/*
* Copyright (c) 2006 - 2007, Nils R. Weller
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* PowerPC backend
*/
#include "backend.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>
#include "scope.h"
#include "decl.h"
#include "type.h"
#include "decl.h"
#include "icode.h"
#include "functions.h"
#include "control.h"
#include "debug.h"
#include "token.h"
#include "error.h"
#include "functions.h"
#include "symlist.h"
#include "icode.h"
#include "stack.h"
#include "reg.h"
#include "subexpr.h"
#include "expr.h"
/* #include "x86_emit_gas.h" */
#include "inlineasm.h"
#include "power_emit_as.h"
#include "cc1_main.h"
#include "n_libc.h"
static FILE *out;
struct emitter_power *emit_power;
#define N_GPRS 32
#define N_FPRS 32
struct reg power_gprs[N_GPRS];
static struct reg power_fprs[N_FPRS];
static struct vreg saved_gprs[N_GPRS];
static struct stack_block *saved_gprs_sb[N_GPRS];
struct vreg float_conv_mask;
/*#define STACK_ARG_START (24 + 8*4) */
#define STACK_ARG_START ((6 + 8) * power_gprs[0].size)
static int callee_save_map[] = {
/* 0-3 */ 0, 0, 0, 0,
/* 4-7 */ 0, 0, 0, 0,
/* 8-11 */ 0, 0, 0, 0,
/* 12-15 */ 0, 0, 0, 0,
/* 16-19 */ 1, 1, 1, 1, /* 16 - 23 = callee save temp */
/* 20-23 */ 1, 1, 1, 1,
/* 24-27 */ 0, 0, 0, 0,
/* 28-31 */ 0, 0, 1<<8, 0 /* 30 = callee save temp */
};
static int floating_callee_save_map[] = {
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 1, 1,
1, 1, 1, 1,
1, 1, 1, 1,
1, 1, 1, 1,
1, 1, 1, 1
};
static void
init_regs(void) {
int i;
static char *names[] = {
"0", "1", "2", "3", "4", "5", "6", "7",
"8", "9", "10", "11", "12", "13", "14", "15",
"16", "17", "18", "19", "20", "21", "22", "23",
"24", "25", "26", "27", "28", "29", "30", "31"
};
/* Registers are just named after their number */
for (i = 0; i < N_GPRS; ++i) {
power_gprs[i].type = REG_GPR;
power_gprs[i].allocatable = 1;
if (backend->abi == ABI_POWER64) {
power_gprs[i].size = 8;
} else {
power_gprs[i].size = 4;
}
power_gprs[i].name = names[i];
}
for (i = 0; i < N_FPRS; ++i) {
power_fprs[i].type = REG_FPR;
power_fprs[i].allocatable = 1;
power_fprs[i].size = 8;
power_fprs[i].name = names[i];
}
/* Some registers with predefined meaning should not be allocated */
power_gprs[0].allocatable = 0; /* zero register */
power_gprs[1].allocatable = 0; /* stack pointer */
power_gprs[2].allocatable = 0; /* table of contents pointer */
#if 0
tmpgpr = &power_gprs[0];
#endif
/*
* XXX gpr0 is a bad choice as temp register because it
* behaves differtly in different contexts. For example,
* lwz 0, addr
* stw 123, 0(0)
* ... crashes. I'm not sure which gpr is best to use yet,
* so I'm using gpr13 for now, which is the first non-
* volatile register
*/
tmpgpr = &power_gprs[13];
tmpgpr->allocatable = 0;
tmpfpr = &power_fprs[13];
tmpfpr->allocatable = 0;
}
static void
do_invalidate(struct reg *r, struct icode_list *il, int save) {
/* Neither allocatable nor used means dedicated register */
if (!r->allocatable && !r->used) {
return;
}
free_preg(r, il, 1, save);
}
/*
* XXX Hm should distinguish between function calls and other
* invalidations
*/
static void
invalidate_gprs(struct icode_list *il, int saveregs) {
int i;
for (i = 0; i < N_GPRS; ++i) {
do_invalidate(&power_gprs[i], il, saveregs);
}
for (i = 0; i < N_FPRS; ++i) {
/*
* XXX this belongs into invalidate_fprs() or
* else rename this to invalidate_regs() ;-)
*/
do_invalidate(&power_fprs[i], il, saveregs);
}
(void) floating_callee_save_map;
}
static void
invalidate_except(struct icode_list *il, int save, ...) {
int i;
struct reg *except[8];
struct reg *arg;
va_list va;
va_start(va, save);
for (i = 0; (arg = va_arg(va, struct reg *)) != NULL; ++i) {
except[i] = arg;
}
va_end(va);
except[i] = NULL;
for (i = 0; i < N_GPRS; ++i) {
int j;
for (j = 0; except[j] != NULL; ++j) {
if (&power_gprs[i] == except[j]) {
break;
}
}
if (except[j] != NULL) {
continue;
}
do_invalidate(&power_gprs[i], il, save);
}
}
static struct reg *
alloc_gpr(
struct function *f,
int size,
struct icode_list *il,
struct reg *dontwipe,
int line) {
if (backend->abi != ABI_POWER64
&& size == 8) {
if (backend->multi_gpr_object) {
backend->multi_gpr_object = 0;
} else {
backend->multi_gpr_object = 1;
}
}
return generic_alloc_gpr(f,size,il,dontwipe,power_gprs,N_GPRS,
callee_save_map, line);
}
static struct reg *
alloc_fpr(struct function *f, int size, struct icode_list *il,
struct reg *dontwipe) {
int i;
struct reg *ret = NULL;
(void) f; (void) size; (void) il; (void) dontwipe;
for (i = 0; i < N_FPRS; ++i) {
if (!power_fprs[i].used && power_fprs[i].allocatable) {
ret = &power_fprs[i];
break;
}
#if 0
/* XXX this does not work, I wonder why!!!! */
ret = generic_alloc_gpr(f, size, il, NULL, power_fprs+1,
N_FPRS - 1,
NULL, 0);
#endif
}
if (ret == NULL) {
#if 0
puts("uh-huh your floating point code is too heavy");
puts("sorry.");
abort();
#endif
ret = &power_fprs[13];
free_preg(ret, il, 1, 1);
}
return ret;
}
static int
init(FILE *fd, struct scope *s) {
out = fd;
init_regs();
if (asmflag && strcmp(asmflag, "as") != 0) {
(void) fprintf(stderr, "Unknown PowerPC assembler `%s'\n",
asmflag);
exit(EXIT_FAILURE);
}
emit = &power_emit_as;
emit_power = &power_emit_power_as;
backend->emit = emit;
return emit->init(out, s);
}
static int
get_ptr_size(void) {
if (backend->abi != ABI_POWER64) {
return 4;
} else {
return 8;
}
}
static struct type *
get_size_t(void) {
if (backend->abi != ABI_POWER64) {
return make_basic_type(TY_UINT);
} else {
return make_basic_type(TY_ULONG);
}
}
static struct type *
get_uintptr_t(void) {
return make_basic_type(TY_ULONG);
}
static size_t
get_sizeof_basic(int type) {
switch (type) {
case TY_ENUM:
return 4; /* XXX */
case TY_INT:
case TY_UINT:
case TY_LONG:
case TY_ULONG:
if (backend->abi == ABI_POWER64) {
if (IS_LONG(type)) {
return 8;
}
}
return 4;
case TY_LLONG:
case TY_ULLONG:
return 8;
case TY_CHAR:
case TY_UCHAR:
case TY_SCHAR:
case TY_BOOL:
return 1;
case TY_SHORT:
case TY_USHORT:
return 2;
case TY_FLOAT:
return 4;
case TY_DOUBLE:
return 8;
case TY_LDOUBLE:
return 16;
default:
printf("err sizeof cannot cope w/ it, wuz %d\n", type);
abort();
return 1; /* XXX */
}
}
static void
do_ret(struct function *f, struct icode_instr *ip) {
int i;
if (f->alloca_head != NULL) {
struct stack_block *sb;
static struct vreg rvr;
rvr.stack_addr = f->alloca_regs;
rvr.size = power_gprs[0].size;
vreg_map_preg(&rvr, &power_gprs[3]);
emit->store(&rvr, &rvr);
if (ip && ip->src_vreg && ip->src_vreg->is_multi_reg_obj) {
rvr.stack_addr = f->alloca_regs->next;
vreg_map_preg(&rvr, &power_gprs[4]);
emit->store(&rvr, &rvr);
}
for (sb = f->alloca_head; sb != NULL; sb = sb->next) {
emit->dealloca(sb, NULL);
}
rvr.stack_addr = f->alloca_regs;
vreg_map_preg(&rvr, &power_gprs[3]);
emit->load(&power_gprs[3], &rvr);
if (ip && ip->src_vreg && ip->src_vreg->is_multi_reg_obj) {
rvr.stack_addr = f->alloca_regs->next;
vreg_map_preg(&rvr, &power_gprs[4]);
emit->load(&power_gprs[4], &rvr);
}
}
if (f->vla_head != NULL) {
struct stack_block *sb;
static struct vreg rvr;
rvr.stack_addr = f->alloca_regs;
rvr.size = power_gprs[0].size;
vreg_map_preg(&rvr, &power_gprs[3]);
emit->store(&rvr, &rvr);
if (ip && ip->src_vreg && ip->src_vreg->is_multi_reg_obj) {
rvr.stack_addr = f->alloca_regs->next;
vreg_map_preg(&rvr, &power_gprs[4]);
emit->store(&rvr, &rvr);
}
for (sb = f->vla_head; sb != NULL; sb = sb->next) {
emit->dealloc_vla(sb, NULL);
}
rvr.stack_addr = f->alloca_regs;
vreg_map_preg(&rvr, &power_gprs[3]);
emit->load(&power_gprs[3], &rvr);
if (ip && ip->src_vreg && ip->src_vreg->is_multi_reg_obj) {
rvr.stack_addr = f->alloca_regs->next;
vreg_map_preg(&rvr, &power_gprs[4]);
emit->load(&power_gprs[4], &rvr);
}
}
for (i = 0; i < N_GPRS; ++i) {
if (saved_gprs[i].stack_addr != NULL) {
emit->load(&power_gprs[i], &saved_gprs[i]);
}
}
emit->freestack(f, NULL);
if (backend->abi == ABI_POWER64) {
x_fprintf(out, "\tld 31, -8(1)\n");
} else {
x_fprintf(out, "\tlwz 31, -4(1)\n");
}
emit->ret(ip);
}
static struct reg *
get_abi_reg(int index, struct type *ty) {
if (index == 0
&& (is_integral_type(ty)
|| ty->tlist != NULL)) {
return &power_gprs[3];
} else {
unimpl();
}
return NULL;
}
static struct reg *
get_abi_ret_reg(struct type *ty) {
if (is_integral_type(ty)
|| ty->tlist != NULL) {
return &power_gprs[3];
} else {
unimpl();
}
/* NOTREACHED */
return NULL;
}
/* XXX platform-independent?!?! used by amd64 */
void
store_preg_to_var(struct decl *d, size_t size, struct reg *r);
#define IS_UNION_OR_ARRAY(ty) \
((ty->code == TY_UNION && ty->tlist == NULL) \
|| (ty->tlist && ty->tlist->type == TN_ARRAY_OF))
/* Get total size for struct field se, including alignment (except last) */
#define TOTAL_BYTES(se) \
(se->next? se->next->dec->offset - se->dec->offset: \
backend->get_sizeof_type(se->dec->dtype, NULL))
#if 0
static void
align_stack(struct function *f, struct type *t, size_t size) {
size_t align;
align = backend->get_align_type(t);
while ((f->total_allocated + size) % align) {
++f->total_allocated;
}
}
#endif
static void
do_map_parameter(
struct function *f,
int *gprs_used,
int *fprs_used,
size_t *stack_bytes_used,
struct sym_entry *se) {
size_t size;
struct vreg tmpvr;
static struct vreg nullvr;
(void) f;
tmpvr = nullvr;
size = backend->get_sizeof_type(se->dec->dtype,0);
if (is_integral_type(se->dec->dtype)
|| se->dec->dtype->tlist != NULL) {
if (*gprs_used < 8) {
se->dec->stack_addr = make_stack_block(0, size);
se->dec->stack_addr->offset =
*gprs_used * /*4*/power_gprs[0].size;
se->dec->stack_addr->from_reg =
&power_gprs[3 + *gprs_used];
++*gprs_used;
} else {
se->dec->stack_addr = make_stack_block(
STACK_ARG_START+ *stack_bytes_used +
(power_gprs[0].size - size), size);
/**stack_bytes_used +=4;*/ /* XXX */
*stack_bytes_used += power_gprs[0].size;
}
} else if (IS_FLOATING(se->dec->dtype->code)) {
if (*fprs_used < 8) {
se->dec->stack_addr = make_stack_block(0, size);
/* WHAT???????????? THIS WAS power_fprs[12+ fprs_used?!?!?!?!? */
se->dec->stack_addr->from_reg =
&power_fprs[ 1 /*12*/ + *fprs_used];
++*fprs_used;
} else {
/* XXX broken */
se->dec->stack_addr = make_stack_block(
STACK_ARG_START+ *stack_bytes_used, size);
*stack_bytes_used += size;
}
} else if (se->dec->dtype->code == TY_STRUCT
|| se->dec->dtype->code == TY_UNION) {
se->dec->stack_addr = make_stack_block(
STACK_ARG_START + *stack_bytes_used,
se->dec->dtype->tstruc->size);
*stack_bytes_used += se->dec->dtype->tstruc->size;
} else {
unimpl();
}
se->dec->stack_addr->is_func_arg = 1;
}
static void
map_parameters(struct function *f, struct ty_func *proto) {
int i;
int gprs_used = 0;
int fprs_used = 0;
size_t stack_bytes_used = 0;
struct sym_entry *se;
if (f->fty->variadic) {
/*
* Allocate enough storage for all registers. If none
* of the unprototyped variadic arguments are passed
* in registers (quite unlikely), then the allocation
* below is redundant
*/
f->fty->lastarg = alloc_decl();
/* Register save area starts at 24 on 32bit ppc */
#if 0
f->fty->lastarg->stack_addr = make_stack_block(24, 0);
#endif
/* XXX is 24 for 32bit and 48 for 64bit right?? */
#define REG_SAVE_AREA_OFFSET (6 * power_gprs[0].size)
f->fty->lastarg->stack_addr =
make_stack_block(REG_SAVE_AREA_OFFSET, 0);
}
se = proto->scope->slist;
if (f->proto->dtype->tlist->next == NULL
&& (f->proto->dtype->code == TY_STRUCT
|| f->proto->dtype->code == TY_UNION)) {
/*
* Function returns struct/union - accomodate for
* hidden pointer (passed as first argument)
*/
size_t ptrsize = backend->get_ptr_size();
f->hidden_pointer = vreg_alloc(NULL, NULL, NULL, NULL);
f->hidden_pointer->size = ptrsize;
f->hidden_pointer->var_backed = alloc_decl();
f->hidden_pointer->var_backed->dtype =
n_xmemdup(f->proto->dtype, sizeof *f->proto->dtype);
f->hidden_pointer->var_backed->dtype->tlist =
alloc_type_node();
f->hidden_pointer->var_backed->dtype->tlist->type =
TN_POINTER_TO;
f->hidden_pointer->type = f->hidden_pointer->var_backed->dtype;
f->hidden_pointer->var_backed->stack_addr =
make_stack_block(/*24*/REG_SAVE_AREA_OFFSET, ptrsize);
f->hidden_pointer->var_backed->stack_addr->from_reg =
&power_gprs[3];
++gprs_used;
}
/*
* Allocate stack space for those arguments that were
* passed in registers, set addresses to existing
* space for those that were passed on the stack
*/
for (i = 0; i < proto->nargs; ++i, se = se->next) {
do_map_parameter(f, &gprs_used, &fprs_used,
&stack_bytes_used, se);
}
if (f->fty->variadic) {
if (gprs_used >= 8) {
/* Wow, all variadic stuff passed on stack */
f->fty->lastarg->stack_addr->offset += /* XXX was = */
stack_bytes_used;
} else {
/*
* (64) 32 bytes were allocated, skip as many as
* necessary
*/
#if 0
f->fty->lastarg->stack_addr->offset -=
gprs_used * power_gprs[0].size;
#endif
f->fty->lastarg->stack_addr->offset +=
gprs_used * power_gprs[0].size;
f->fty->lastarg->stack_addr->from_reg =
&power_gprs[3 + gprs_used];
}
}
}
static void
make_local_variables(struct function *f) {
struct scope *scope;
int i;
size_t size;
for (scope = f->scope; scope != NULL; scope = scope->next) {
struct stack_block *sb;
struct scope *tmp;
struct decl **dec;
size_t align;
for (tmp = scope; tmp != NULL; tmp = tmp->parent) {
if (tmp == f->scope) {
break;
}
}
if (tmp == NULL) {
/* End of function reached */
break;
}
if (scope->type != SCOPE_CODE) continue;
dec = scope->automatic_decls.data;
for (i = 0; i < scope->automatic_decls.ndecls; ++i) {
if (dec[i]->stack_addr != NULL) { /* XXX sucks */
continue;
} else if (dec[i]->dtype->is_vla) {
/* XXX doesn't work for ptr to
* VLA */
continue;
}
if (i+1 < scope->automatic_decls.ndecls
&& !dec[i+1]->dtype->is_vla) {
/* 11/07/07: This used dec[i] instead of i+1 */
align = backend->get_align_type(dec[i]->dtype);
while ((f->total_allocated + size) % align) {
++f->total_allocated;
}
}
size = backend->
get_sizeof_decl(dec[i], NULL);
sb = stack_malloc(f, size/*+align*/);
sb->nbytes = size;
dec[i]->stack_addr = sb;
}
}
}
static int
gen_function(struct function *f) {
unsigned int mask;
int i;
int nsaved;
char *funcname;
int min_bytes_pushed;
struct ty_func *proto;
struct scope *scope;
struct icode_instr *lastret = NULL;
struct stack_block *sb;
struct sym_entry *se;
size_t alloca_bytes = 0;
size_t vla_bytes = 0;
/* XXX use emit->intro() ??? */
x_fprintf(out, "\t.csect .text[PR]\n");
/* Instructions are 4 bytes in PPC64 too! */
x_fprintf(out, "\t.align 2\n");
if (f->proto->dtype->storage != TOK_KEY_STATIC) {
x_fprintf(out, "\t.globl %s\n", f->proto->dtype->name);
x_fprintf(out, "\t.globl .%s\n", f->proto->dtype->name);
}
x_fprintf(out, "\t.csect %s[DS]\n", f->proto->dtype->name);
emit->label(f->proto->dtype->name, 1);
if (backend->abi == ABI_POWER64) {
x_fprintf(out, "\t.llong ");
} else {
x_fprintf(out, "\t.long ");
}
x_fprintf(out, ".%s, TOC[tc0], 0\n", f->proto->dtype->name);
x_fprintf(out, "\t.csect .text[PR]\n");
/*emit->label(f->proto->dtype->name, 1);*/
x_fprintf(out, ".%s:\n", f->proto->dtype->name);
funcname = f->proto->dtype->name;
proto = f->proto->dtype->tlist->tfunc;
/* Create space for saving frame pointer */
f->total_allocated += power_gprs[31].size;
map_parameters(f, proto);
stack_align(f, 16);
make_local_variables(f);
stack_align(f, 16);
/*
* Allocate storage for saving callee-saved registers
* (but defer saving them until sp has been updated)
*/
nsaved = 0;
for (mask = 1, i = 0; i < N_GPRS; ++i, mask <<= 1) {
if (f->callee_save_used & mask) {
if (saved_gprs_sb[i] == NULL) {
saved_gprs_sb[i] =
make_stack_block(0, power_gprs[0].size);
/*
* The frame pointer cannot be used yet
* because it needs to be saved as well
*/
saved_gprs_sb[i]->use_frame_pointer = 0;
}
f->total_allocated += power_gprs[0].size;
saved_gprs[i].stack_addr = saved_gprs_sb[i];
saved_gprs[i].size = power_gprs[0].size;
saved_gprs[i].stack_addr->offset =
f->total_allocated;
++nsaved;
} else {
saved_gprs[i].stack_addr = NULL;
}
}
f->callee_save_offset = f->total_allocated;
/*
* Allocate storage for temporarily saving GPRs. Offsets need to
* be patched once more when the size of the entire stack frame
* is known :-(
*/
for (sb = f->regs_head; sb != NULL; sb = sb->next) {
stack_align(f, sb->nbytes);
f->total_allocated += sb->nbytes;
sb->offset = f->total_allocated;
}
/*
* Allocate storage for saving alloca() pointers, and initialize
* it to zero (must be patched like temporary register storage)
*/
stack_align(f, power_gprs[0].size);
for (sb = f->alloca_head; sb != NULL; sb = sb->next) {
f->total_allocated += sb->nbytes;
alloca_bytes += sb->nbytes;
sb->offset = f->total_allocated;
}
if (f->alloca_head != NULL || f->vla_head != NULL) {
/*
* Get stack for saving return value registers before
* performing free() on alloca()ted blocks
*/
f->alloca_regs = make_stack_block(0, power_gprs[0].size);
f->total_allocated += power_gprs[0].size;
f->alloca_regs->offset = f->total_allocated;
f->alloca_regs->next = make_stack_block(0, power_gprs[0].size);
f->total_allocated += power_gprs[0].size;
f->alloca_regs->next->offset = f->total_allocated;
}
/*
* Allocate storage for saving VLA data, and initialize it to zero
*/
for (sb = f->vla_head; sb != NULL; sb = sb->next) {
f->total_allocated += sb->nbytes;
vla_bytes += sb->nbytes;
sb->offset = f->total_allocated;
}
stack_align(f, 8);
/*
* The PowerOpen ABI requires the caller to allocate storage
* for saving registers and passing stack arguments, recorded
* by max_bytes_pushed. Unfortunately, it is possible that
* unrequested function calls must be generated, e.g. to
* perform software arithmetic with the __nwcc*() functions,
* or to copy a structure initializer using memcpy().
* Therefore, we always allocate a minimum save area to ensure
* that no surprises with hidden calls can happen.
* XXX obviously it may be beneficial to omit this if no
* calls actually happen
*/
min_bytes_pushed =
power_gprs[0].size * 2 /* linkage bytes */
+ 18 * power_gprs[0].size; /* reg save area */
if (f->max_bytes_pushed < min_bytes_pushed) {
f->max_bytes_pushed = min_bytes_pushed;
while (f->max_bytes_pushed % 8) ++f->max_bytes_pushed;
}
f->total_allocated += f->max_bytes_pushed; /* Parameter area */
f->total_allocated += power_gprs[1].size; /* saved sp */
x_fprintf(out, "\tmflr 0\n");
if (backend->abi == ABI_POWER64) {
x_fprintf(out, "\tstd 0, 16(1)\n");
x_fprintf(out, "\tstd 31, -8(1)\n");
} else {
x_fprintf(out, "\tstw 0, 8(1)\n");
x_fprintf(out, "\tstw 31, -4(1)\n");
}
if (f->total_allocated > 0) {
emit->allocstack(f, f->total_allocated);
}
/*
* Local variable offsets can only now be patched because the
* PowerPC frame pointer points to the bottom of the frame, not
* the top, which is terrible.
*/
for (scope = f->scope; scope != NULL; scope = scope->next) {
struct scope *tmp;
struct decl **dec;
for (tmp = scope; tmp != NULL; tmp = tmp->parent) {
if (tmp == f->scope) {
break;
}
}
if (tmp == NULL) {
/* End of function reached */
break;
}
if (scope->type != SCOPE_CODE) continue;
dec = scope->automatic_decls.data;
for (i = 0; i < scope->automatic_decls.ndecls; ++i) {
if (dec[i]->stack_addr->is_func_arg
/*&& !dec[i]->stack_addr->from_reg*/) {
/*
* Argument passed on stack - needs different
* kind of patchery later
*/
continue;
} else if (dec[i]->dtype->is_vla) {
continue;
}
dec[i]->stack_addr->offset =
f->total_allocated - dec[i]->stack_addr->offset;
}
#if 0
if (f->hidden_pointer) {
f->hidden_pointer->var_backed->stack_addr->offset =
f->total_allocated -
f->hidden_pointer->var_backed->stack_addr->offset;
}
#endif
}
if (f->fty->variadic) {
f->fty->lastarg->stack_addr->offset =
f->total_allocated /*-*/ +
f->fty->lastarg->stack_addr->offset;
}
se = proto->scope->slist;
for (sb = f->regs_head; sb != NULL; sb = sb->next) {
sb->offset = f->total_allocated - sb->offset;
}
for (sb = f->alloca_head; sb != NULL; sb = sb->next) {
sb->offset = f->total_allocated - sb->offset;
}
for (sb = f->vla_head; sb != NULL; sb = sb->next) {
sb->offset = f->total_allocated - sb->offset;
}
if (f->alloca_regs != NULL) {
f->alloca_regs->offset = f->total_allocated
- f->alloca_regs->offset;
f->alloca_regs->next->offset = f->total_allocated
- f->alloca_regs->next->offset;
}
if (nsaved > 0) {
for (i = 0; i < N_GPRS; ++i) {
if (saved_gprs[i].stack_addr != NULL) {
saved_gprs[i].stack_addr->offset =
f->total_allocated -
saved_gprs[i].stack_addr->offset;
vreg_map_preg(&saved_gprs[i],
&power_gprs[i]);
emit->store(&saved_gprs[i],
&saved_gprs[i]);
reg_set_unused(&power_gprs[i]);
}
}
}
/*
* Patch parameter offsets and save corresponding argument
* registers to stack, if necessary
*/
for (i = 0; i < proto->nargs; ++i, se = se->next) {
/*
* There are two cases where the stack address must
* be patched:
*
* - The argument is passed on the stack
* - The argument is passed in a register, but
* backed by a register save area slot
*
* In either case, we need an offset into the stack
* frame of the caller, which can only now be
* computed.
*
* (this also applies to hidden_pointer for struct
* returns, which is passed in gpr3.)
*
* fp . . . . . . [frame start] [arguments ...]
* ^ lowest address highest address ^
* So the offset (with fp) is the size of the entire
* stack frame plus the offset in the stack arg area
*/
if (se->dec->stack_addr->from_reg != NULL) {
/* Write argument register contents to stack */
if ((se->dec->dtype->code == TY_STRUCT
|| se->dec->dtype->code == TY_UNION)
&& se->dec->dtype->tlist == NULL) {
/* Passed on stack */
se->dec->stack_addr->offset =
f->total_allocated +
se->dec->stack_addr->offset;
} else {
/*
* Passed in register, but backed by
* register save area
*/
se->dec->stack_addr->offset =
f->total_allocated +
se->dec->stack_addr->offset+
REG_SAVE_AREA_OFFSET;
store_preg_to_var(se->dec,
se->dec->stack_addr->nbytes,
se->dec->stack_addr->from_reg);
}
} else {
/*
* Argument passed on stack
*/
se->dec->stack_addr->offset =
f->total_allocated +
se->dec->stack_addr->offset;
}
}
if (f->hidden_pointer) {
struct decl *d = f->hidden_pointer->var_backed;
d->stack_addr->offset = f->total_allocated + d->stack_addr->offset;
store_preg_to_var(d,
d->stack_addr->nbytes,
d->stack_addr->from_reg);
}
if (f->fty->variadic) {
size_t saved_offset;
if (f->fty->lastarg->stack_addr->from_reg == NULL) {
/* Entirely on stack */
#if 0
f->fty->lastarg->stack_addr->offset =
f->total_allocated +
f->fty->lastarg->stack_addr->offset;
#endif
} else {
struct reg *r =
f->fty->lastarg->stack_addr->from_reg;
saved_offset = f->fty->lastarg->stack_addr->offset;
for (; r != &power_gprs[3+8]; ++r) {
store_preg_to_var(f->fty->lastarg,
power_gprs[0].size, r);
f->fty->lastarg->stack_addr->offset +=
power_gprs[0].size;
}
f->fty->lastarg->stack_addr->offset = saved_offset;
}
}
if (f->alloca_head != NULL) {
emit->zerostack(f->alloca_tail, alloca_bytes);
}
if (f->vla_head != NULL) {
emit->zerostack(f->vla_tail, vla_bytes);
}
if (xlate_icode(f, f->icode, &lastret) != 0) {
return -1;
}
if (lastret != NULL) {
struct icode_instr *tmp;
for (tmp = lastret->next; tmp != NULL; tmp = tmp->next) {
if (tmp->type != INSTR_SETITEM) {
lastret = NULL;
break;
}
}
}
if (lastret == NULL) {
do_ret(f, NULL);
}
return 0;
}
static int
gen_program(void) {
struct function *func;
x_fprintf(out, "\t.toc\n");
emit->extern_decls();
x_fprintf(out, "\t.csect .text[PR]\n");
emit->static_decls();
emit->struct_inits();
emit->empty();
emit->strings(); /* XXX bad */
x_fprintf(out, "\t.csect .text[PR]\n");
for (func = funclist; func != NULL; func = func->next) {
curfunc = func;
if (gen_function(func) != 0) {
return -1;
}
emit->empty();
emit->empty();
}
x_fflush(out);
return 0;
}
static void
pass_arg_stack(
struct vreg *vr,
size_t bytes_left,
size_t *stack_bytes_used,
struct icode_list *il) {
struct vreg *dest;
/*
* All types are passed as double words
* (right-justified)
*/
size_t size;
(void) bytes_left;
size = backend->get_sizeof_type(vr->type, NULL);
if (vr->type->tlist
&& vr->type->tlist->type ==
TN_ARRAY_OF) {
size = /*4*/power_gprs[0].size;
}
if (/*4*/power_gprs[0].size - size > 0) {
/* Need to right-adjust */
*stack_bytes_used += /*4*/power_gprs[0].size - size;
}
dest = vreg_alloc(NULL, NULL, NULL, NULL);
dest->type = vr->type;
dest->size = vr->size;
dest->stack_addr = make_stack_block(
*stack_bytes_used+STACK_ARG_START /*-size*/, dest->size);
dest->stack_addr->use_frame_pointer = 0;
*stack_bytes_used += size;
vreg_faultin(tmpgpr, NULL, vr, il, 0);
vreg_map_preg(dest, tmpgpr);
icode_make_store(curfunc, dest, dest, il);
}
void
put_arg_into_reg(
struct reg *regset,
int *index, int startat,
struct vreg *vr,
struct icode_list *il);
extern unsigned long reg_offset; /* XXX mips ... */
/*
* Pass a struct/union to a function. The first members of a
* struct are passed in GPRs/FPRs, the rest on the stack. The
* first members of unions are passed only in GPRs, the rest
* on the stack. ...at least that's what the n32/n64 ABIs say!
*
* nwcc passes all structs/unions on the stack, just like on
* x86. This avoids a lot of complexity. It also means that
* nwcc is not link compatible with MIPSpro and gcc in this
* regard. This breaks support for some library functions, so
* I may end up supporting n32 fully eventually.
*
* XXX this doesn't seem to take stack alignment into account
* at all
*/
static int
pass_struct_union(
struct vreg *vr, int *gprs_used, int *fprs_used,
size_t *stack_bytes_used,
struct icode_list *il) {
struct vreg *destvr;
struct vreg *reg_vrs[8];
int i;
(void) fprs_used;
destvr = vreg_alloc(NULL, NULL, NULL, NULL);
destvr->type = vr->type;
destvr->size = vr->size;
destvr->var_backed = alloc_decl();
destvr->var_backed->stack_addr =
make_stack_block(*stack_bytes_used+
STACK_ARG_START, vr->size);
destvr->var_backed->stack_addr->
use_frame_pointer = 0;
*stack_bytes_used += vr->size;
/*
* Save occupied argument registers before call to memcpy() (in
*/
for (i = 0; i < *gprs_used; ++i) {
struct reg *r = &power_gprs[3+i];
struct vreg *newvr;
newvr = vreg_alloc(0,0,0,0);
newvr->size = power_gprs[0].size;
newvr->type = make_basic_type(TY_ULONG);
newvr->stack_addr =
make_stack_block(reg_offset + power_gprs[0].size * i,
8);
newvr->stack_addr->use_frame_pointer = 0;
vreg_map_preg(newvr, &power_gprs[3+i]);
icode_make_store(NULL, newvr, newvr, il);
reg_set_unused(&power_gprs[3+i]);
reg_vrs[i] = newvr;
reg_set_unused(r);
r->vreg = NULL;
}
vreg_faultin_ptr(vr, il);
icode_make_copystruct(destvr, vr, il);
/* Restore argument registers */
for (i = 0; i < *gprs_used; ++i) {
vreg_faultin(&power_gprs[3+i], NULL,
reg_vrs[i], il, 0);
reg_set_unallocatable(&power_gprs[3+i]);
}
return 0;
}
size_t
calc_stack_bytes(struct vreg **vrs, int nvrs, int *takes_struct);
static struct vreg *
icode_make_fcall(struct fcall_data *fcall, struct vreg **vrs, int nvrs,
struct icode_list *il)
{
unsigned long allpushed = 0;
struct vreg *tmpvr;
struct vreg *ret = NULL;
struct vreg *vr2;
struct type *ty;
struct icode_instr *ii;
struct type_node *tn;
struct vreg *struct_lvalue;
size_t stack_bytes_used = 0;
size_t saved_total_allocated;
int i;
int takes_struct = 0;
int need_dap = 0;
int struct_return = 0;
int gprs_used = 0;
int fprs_used = 0;
int is_multi_reg_obj = 0;
int ret_is_anon_struct = 0;
saved_total_allocated = curfunc->total_allocated;
ty = fcall->calltovr->type;
tmpvr = fcall->calltovr;
tn = ty->tlist;
if (tn->type == TN_POINTER_TO) {
/* Called thru function pointer */
tn = tn->next;
}
struct_lvalue = fcall->lvalue;
if ((ty->code == TY_STRUCT
|| ty->code == TY_UNION)
&& tn->next == NULL) {
struct_return = 1;
/*
* This is a big struct that doesn't fit into
* regsiters, so it is necessary to pass a pointer
* to some space to store the result into in r4
*/
if (struct_lvalue == NULL || fcall->need_anon) {
struct type_node *tnsav;
/*
* Result of function is not assigned so we need
* to allocate storage for the callee to store
* its result into
*/
/* XXX ARGH This is bogus on ppc..need to preallocate */
tnsav = ty->tlist;
ty->tlist = NULL;
/* XXX hm */
struct_lvalue = vreg_stack_alloc(ty, il, 1 /*0*/,NULL);
ty->tlist = tnsav;
allpushed += struct_lvalue->size;
/*while (allpushed % 4*/ /* XXX *//*) ++allpushed;*/
while (allpushed % power_gprs[0].size) ++allpushed;
ret_is_anon_struct = 1;
}
/* Hidden pointer is passed in first GPR! */
ii = icode_make_addrof(struct_lvalue, il);
append_icode_list(il, ii);
free_preg(&power_gprs[3], il, 1, 1);
icode_make_copyreg(&power_gprs[3], ii->dat,
NULL, NULL, il);
++gprs_used;
}
if (fcall->functype->nargs == -1
|| ty->implicit) {
/* Need default argument promotions */
need_dap = 1;
}
allpushed += calc_stack_bytes(vrs, nvrs, &takes_struct);
if (takes_struct) {
/* memcpy() will be called to pass argument(s) */
backend->invalidate_gprs(il, 1);
}
if (/*allpushed > 0*/ 1) {
int linkage_bytes = backend->abi == ABI_POWER64? 16: 8;
/*icode_make_allocstack(NULL, allpushed, il);*/
allpushed += linkage_bytes;
allpushed += 18 * power_gprs[0].size; /* XXX 18 comes from 64bit abi */
if ((int)allpushed > curfunc->max_bytes_pushed) {
curfunc->max_bytes_pushed = allpushed;
}
allpushed -= linkage_bytes;
}
for (i = 0; i < nvrs; ++i) {
if ((fcall->functype->variadic
&& i >= fcall->functype->nargs)
|| fcall->calltovr->type->implicit) {
need_dap = 1;
}
if (vrs[i]->parent) {
vr2 = get_parent_struct(vrs[i]);
} else {
vr2 = NULL;
}
if (is_integral_type(vrs[i]->type)
|| vrs[i]->type->tlist
/*|| vrs[i]->from_const*/ ) {
if (vrs[i]->type->tlist == NULL
&& (IS_CHAR(vrs[i]->type->code)
|| IS_SHORT(vrs[i]->type->code))) {
vrs[i] = backend->
icode_make_cast(vrs[i],
make_basic_type(TY_INT), il);
}
if (gprs_used < 8) {
put_arg_into_reg(power_gprs,
&gprs_used, 3, vrs[i], il);
} else {
pass_arg_stack(vrs[i], 0,
&stack_bytes_used, il);
}
} else if (vrs[i]->type->code == TY_STRUCT
|| vrs[i]->type->code == TY_UNION) {
pass_struct_union(vrs[i],
&gprs_used, &fprs_used, &stack_bytes_used, il);
} else if (IS_FLOATING(vrs[i]->type->code)) {
/*
* For variadic functions, floating point values
* go into gprs (floats are promoted to double)
*/
if (need_dap) {
/*
* 10/31/07: Added cast for double too
* even though it already has that type.
* This just has the effect of anonymi-
* fying the double. Otheriwse the
* reinterpretation as a long long in
* 32bit mode below will break if we
* have an unanoymified constant here,
* since the fp constant is wrongly
* reinterpreted as integer constant
*/
if (vrs[i]->type->code == TY_FLOAT
|| vrs[i]->type->code ==
TY_DOUBLE) {
vrs[i] = backend->
icode_make_cast(vrs[i],
make_basic_type(TY_DOUBLE), il);
} else if (vrs[i]->type->code == TY_LDOUBLE) {
unimpl();
}
/*
* XXXXXXXXXX we have to put this stuff into
* both fp and gp regs to satisfy both
* accidently undeclared functions and
* variadic ones!
*/
if (gprs_used < 7) { /* need at least two */
free_preg(vrs[i]->pregs[0], il, 1, 1);
vrs[i] = vreg_disconnect(vrs[i]);
vrs[i]->pregs[0] = NULL;
vrs[i]->type = make_basic_type(TY_ULONG);
vrs[i]->size = 8;
if (backend->abi != ABI_POWER64) {
vrs[i]->is_multi_reg_obj = 2;
vreg_faultin(&power_gprs[3+gprs_used],
&power_gprs[3+gprs_used+1],
vrs[i], il, 0);
gprs_used += 2;
} else {
vreg_faultin(&power_gprs[3+
gprs_used],
NULL, vrs[i], il, 0);
++gprs_used;
}
#if 0
put_arg_into_reg(power_gprs,
&gprs_used, 3, vrs[i], il);
#endif
} else {
pass_arg_stack(vrs[i], 0,
&stack_bytes_used, il);
}
} else {
if (fprs_used < 13) {
put_arg_into_reg(power_fprs,
&fprs_used, 1, vrs[i], il);
} else {
pass_arg_stack(vrs[i], 0,
&stack_bytes_used, il);
}
}
} else {
unimpl();
}
/* We can free the register(s) - marked unallocatable */
/*free_pregs_vreg(vrs[i], il, 0, 0);*/
if (vr2 && vr2->from_ptr && vr2->from_ptr->pregs[0]
&& vr2->from_ptr->pregs[0]->vreg == vr2->from_ptr) {
free_preg(vr2->from_ptr->pregs[0], il, 0, 0);
}
}
if (struct_return) {
/* XXX uh-huh what's going on here?!!?! why this AGAIN??? */
ii = icode_make_addrof(struct_lvalue, il);
append_icode_list(il, ii);
icode_make_copyreg(&power_gprs[3], ii->dat, NULL, NULL, il);
free_preg(ii->dat, il, 0, 0);
}
for (i = 0; i < gprs_used; ++i) {
/* Don't save argument registers */
reg_set_unused(&power_gprs[3+i]);
power_gprs[3+i].allocatable = 1;
}
if (!takes_struct) {
backend->invalidate_gprs(il, 1);
}
if (ty->tlist->type == TN_POINTER_TO) {
/*
* Need to indirect through function pointer. It is
* necessary to mark argument registers unallocatable
* because the function pointer itself may be loaded
* indirectly, e.g. through a struct pointer, and
* that pointer then too needs to be faulted in
*/
for (i = 0; i < gprs_used; ++i) {
power_gprs[3+i].allocatable = 0;
}
vreg_faultin(tmpgpr, NULL, tmpvr, il, 0);
for (i = 0; i < gprs_used; ++i) {
power_gprs[3+i].allocatable = 1;
}
ii = icode_make_call_indir(tmpvr->pregs[0]);
tmpvr->pregs[0]->used = 0;
tmpvr->pregs[0]->vreg = NULL;
} else {
ii = icode_make_call(ty->name);
}
append_icode_list(il, ii);
ret = vreg_alloc(NULL, NULL, NULL, NULL);
ret->type = ty;
#if 0
if (ty->tlist->next != NULL) {
#endif
if ((ty->tlist->type == TN_POINTER_TO
&& ty->tlist->next->next != NULL)
|| (ty->tlist->type == TN_FUNCTION
&& ty->tlist->next != NULL)) {
/* Must be pointer */
ret->pregs[0] = &power_gprs[3];
} else {
struct type_node *tnsav = ty->tlist;
ty->tlist = NULL;
if (backend->abi != ABI_POWER64
&& IS_LLONG(ty->code)) {
is_multi_reg_obj = 2;
}
if (is_integral_type(ty)) {
ret->pregs[0] = &power_gprs[3];
if (is_multi_reg_obj) {
ret->pregs[1] = &power_gprs[4];
}
} else if (ty->code == TY_FLOAT
|| ty->code == TY_DOUBLE
|| ty->code == TY_LDOUBLE) {
ret->pregs[0] = &power_fprs[0];
} else if (ty->code == TY_STRUCT
|| ty->code == TY_UNION) {
if (ret_is_anon_struct) {
/*
* 08/16/07: Added this
*/
ret = struct_lvalue;
}
ret->struct_ret = 1;
ret->pregs[0] = NULL;
} else if (ty->code == TY_VOID) {
; /* Nothing! */
}
ty->tlist = tnsav;
}
for (i = 3; i < 11; ++i) {
reg_set_allocatable(&power_gprs[i]);
}
if (ret->pregs[0] != NULL) {
vreg_map_preg(ret, ret->pregs[0]);
}
if (is_multi_reg_obj) {
vreg_map_preg2(ret, ret->pregs[1]);
ret->is_multi_reg_obj = 2;
}
ret->type = n_xmemdup(ret->type, sizeof *ret->type);
if (ret->type->tlist->type == TN_POINTER_TO) {
copy_tlist(&ret->type->tlist, ret->type->tlist->next->next);
} else {
copy_tlist(&ret->type->tlist, ret->type->tlist->next);
}
if (ret->type->code != TY_VOID || ret->type->tlist) {
ret->size = backend->get_sizeof_type(ret->type, NULL);
}
return ret;
}
static int
icode_make_return(struct vreg *vr, struct icode_list *il) {
struct icode_instr *ii;
struct type_node *oldtn;
struct type *rtype = curfunc->proto->dtype;
oldtn = rtype->tlist;
rtype->tlist = rtype->tlist->next;
if (vr != NULL) {
if (is_integral_type(rtype)
|| rtype->tlist != NULL) {
/* XXX long long ?!?!!?!??!?? */
if (vr->is_multi_reg_obj) {
vreg_faultin(&power_gprs[3],
&power_gprs[4], vr, il, 0);
} else {
vreg_faultin(&power_gprs[3], NULL,
vr, il, 0);
}
} else if (rtype->code == TY_FLOAT
|| rtype->code == TY_DOUBLE
|| rtype->code == TY_LDOUBLE) {
/* XXX ldouble ... */
vreg_faultin(&power_fprs[0], NULL, vr, il, 0);
} else if (rtype->code == TY_STRUCT
|| rtype->code == TY_UNION) {
/* vr may come from pointer */
vreg_faultin_ptr(vr, il);
icode_make_copystruct(NULL, vr, il);
}
}
ii = icode_make_ret(vr);
append_icode_list(il, ii);
rtype->tlist = oldtn;
return 0;
}
static void
icode_prepare_op(
struct vreg **dest,
struct vreg **src,
int op,
struct icode_list *il) {
if ((op == TOK_OP_DIVIDE
|| op == TOK_OP_MULTI
|| op == TOK_OP_MOD
|| op == TOK_OP_PLUS
|| op == TOK_OP_MINUS
|| op == TOK_OP_BSHL
|| op == TOK_OP_BSHR)
&& (backend->abi != ABI_POWER64
&& IS_LLONG(dest[0]->type->code)
&& dest[0]->type->tlist == NULL)) {
/*
* Div/mul and 64bit add/sub are done in software -
* prepare for function call
*
* XXX the invalidate may trash stuff which is still
* needed! e.g. a pointer thru which we assign the
* result of the operation (sparc typemap.c bug)
*/
/* XXX woah that invalidate is kinda heavy... */
backend->invalidate_gprs(il, 1);
}
vreg_faultin(NULL, NULL, *dest, il, 0);
vreg_faultin(NULL, NULL, *src, il, 0);
(void) op;
}
#define TO_ZERO() icode_make_copyreg(&power_gprs[0], ret->pregs[0], NULL, NULL, il)
#define FROM_ZERO() icode_make_copyreg(ret->pregs[0], &power_gprs[0], NULL, NULL, il)
static void
change_preg_size(struct vreg *ret, struct type *to, struct type *from,
struct icode_list *il) {
struct icode_instr *ii;
int to_is_64bit = 0;
int from_is_64bit = 0;
if (to->tlist == NULL) {
if (IS_LLONG(to->code)
|| (backend->abi == ABI_POWER64
&& IS_LONG(to->code))) {
to_is_64bit = 1;
}
}
if (from->tlist == NULL) {
if (IS_LLONG(from->code)
|| (backend->abi == ABI_POWER64
&& IS_LONG(from->code))) {
from_is_64bit = 1;
}
}
if (IS_CHAR(from->code) && IS_CHAR(to->code)) {
if (from->sign == TOK_KEY_UNSIGNED
&& to->code == TY_SCHAR) {
TO_ZERO();
icode_make_power_slwi(&power_gprs[0],
&power_gprs[0], 24, il);
icode_make_power_srawi(&power_gprs[0],
&power_gprs[0], 24, il);
FROM_ZERO();
}
} else if (to_is_64bit) {
if (!from_is_64bit && !from->tlist) {
if (backend->abi != ABI_POWER64) {
ret->pregs[1] = ret->pregs[0];
ret->pregs[0] = ALLOC_GPR(curfunc
, 0, il, NULL);
if (from->sign != TOK_KEY_UNSIGNED) {
icode_make_power_srawi(ret->pregs[0],
ret->pregs[1], 31, il);
} else {
ii = icode_make_setreg(
ret->pregs[0], 0);
append_icode_list(il, ii);
}
} else {
TO_ZERO();
if (from->sign == TOK_KEY_UNSIGNED
|| from->code == TY_CHAR) {
int bits = 0;
if (IS_CHAR(from->code)) {
bits = 56;
} else if (from->code == TY_USHORT) {
bits = 48;
} else if (from->code == TY_UINT) {
bits = 32;
} else {
unimpl();
}
icode_make_power_rldicl(&power_gprs[0],
&power_gprs[0], bits, il);
} else {
/* from signed */
if (from->code == TY_SCHAR) {
icode_make_power_extsb(
&power_gprs[0], il);
} else if (from->code == TY_SHORT) {
icode_make_power_extsh(
&power_gprs[0], il);
} else if (from->code == TY_INT) {
icode_make_power_extsw(
&power_gprs[0], il);
} else {
unimpl();
}
}
FROM_ZERO();
}
}
} else if (from_is_64bit) {
struct reg *r = ret->pregs[0];
if (backend->abi != ABI_POWER64) {
ret->pregs[0] = ret->pregs[1];
free_preg(r, il, 1, 0);
}
/*
* Seems r can become gpr0. I do not know why
* this is happening but it is terrible
*/
power_gprs[0].allocatable = power_gprs[0].used = 0;
goto truncate_further;
} else {
/* Neither is long long, but they are different */
int needand;
int needextsh;
int needlwi;
truncate_further:
needand = needextsh = needlwi = 0;
if (to->code == TY_SCHAR) {
needand = 0xff;
needlwi = 24;
} else if (to->code == TY_CHAR || to->code == TY_UCHAR) {
needand = 0xff;
} else if (to->code == TY_SHORT) {
needand = 0xffff;
needextsh = 1;
} else if (to->code == TY_USHORT) {
needand = 0xffff;
} else if (to->code == TY_INT) {
} else if (to->code == TY_UINT) {
/*needand = 0xffffffff;*/
} else if (to->code == TY_LONG) {
} else if (to->code == TY_ULONG) {
/*needand = 0xffffffff;*/
}
if (needand) {
ii = icode_make_setreg(&power_gprs[0], needand);
append_icode_list(il, ii);
ii = icode_make_and(ret, NULL);
append_icode_list(il, ii);
}
if (needlwi) {
icode_make_copyreg(&power_gprs[0], ret->pregs[0],
NULL, NULL, il);
icode_make_power_slwi(&power_gprs[0],
&power_gprs[0], needlwi, il);
icode_make_power_srawi(&power_gprs[0],
&power_gprs[0], needlwi, il);
icode_make_copyreg(ret->pregs[0], &power_gprs[0],
NULL, NULL, il);
}
if (needextsh) {
icode_make_power_extsh(ret->pregs[0], il);
}
}
power_gprs[0].allocatable = power_gprs[0].used = 0;
}
static struct vreg *
conv_int_to_fp_32(struct vreg *ret, struct icode_list *il,
struct type *from, struct type *to) {
struct reg *fpreg;
struct reg *lower_word;
struct vreg *llong_vreg = NULL;
struct vreg *double_vreg = NULL;
struct icode_instr *ii;
#if 0
if (!IS_INT(from->code)
&& !IS_LONG(from->code)
&& !IS_LLONG(from->code)) {
#endif
if (from->code < TY_INT) {
int intcode;
if (from->sign == TOK_KEY_UNSIGNED) {
intcode = TY_UINT;
} else {
intcode = TY_INT;
}
ret = vreg_disconnect(ret);
change_preg_size(ret,
make_basic_type(intcode), from, il);
ret = vreg_disconnect(ret);
from = make_basic_type(intcode);
ret->is_multi_reg_obj = 0;
ret->type = to;
ret->size = 4;
}
if (float_conv_mask.var_backed == NULL) {
float_conv_mask.var_backed =
alloc_decl();
float_conv_mask.size = 8;
float_conv_mask.type =
n_xmemdup(make_basic_type(TY_DOUBLE),
sizeof(struct type));;
float_conv_mask.type->name =
"_Float_conv_mask";
float_conv_mask.var_backed->dtype =
float_conv_mask.type;
}
/*
* XXX This stuff, particularly long long,
* is quite 32bit-specific :-(
*/
fpreg = backend->alloc_fpr(curfunc, 0, il, NULL);
lower_word = ALLOC_GPR(curfunc, 0, il, NULL);
icode_make_power_lis(lower_word, 0x4330, il);
TO_ZERO();
icode_make_power_xoris(&power_gprs[0],
0x8000, il);
llong_vreg = vreg_alloc(NULL,NULL,NULL,NULL);
llong_vreg->type = make_basic_type(TY_LLONG);
llong_vreg->size = 8;
if (backend->abi != ABI_POWER64) {
llong_vreg->is_multi_reg_obj = 2;
vreg_map_preg(llong_vreg, lower_word);
vreg_map_preg2(llong_vreg, &power_gprs[0]);
} else {
llong_vreg->is_multi_reg_obj = 0;
/*
* Move upper word to upper word in register
*/
icode_make_power_slwi(&power_gprs[0],
&power_gprs[0], 32, il);
/* OR lower word into it as well */
icode_make_preg_or(&power_gprs[0],
lower_word, il);
vreg_map_preg(llong_vreg, &power_gprs[0]);
}
icode_make_store(curfunc, llong_vreg, llong_vreg, il);
free_preg(ret->pregs[0], il, 1, 0);
/*
* The vreg_map_preg() above sets gpr0 to
* allocatable, which is no good
*/
power_gprs[0].allocatable
= power_gprs[0].used = 0;
/*
* XXX this should be complete nonsense, like all
* other such stack_addr assignemnts! that is
* because the address is (always?) a null pointer
* which is only later patched up on behalf of
* icode_make_allocstack()
*/
ret->stack_addr = llong_vreg->stack_addr;
if (to->code == TY_FLOAT) {
/* We have to load as double ... */
double_vreg = n_xmemdup(llong_vreg, sizeof *llong_vreg);
double_vreg->is_multi_reg_obj = 0;
double_vreg->type = make_basic_type(TY_DOUBLE);
}
power_gprs[0].allocatable = power_gprs[0].used = 0;
vreg_faultin(fpreg, NULL, double_vreg? double_vreg: ret, il, 0);
reg_set_unallocatable(fpreg);
vreg_faultin(NULL, NULL, &float_conv_mask, il, 0);
ret->pregs[0] = fpreg;
ii = icode_make_sub(ret, &float_conv_mask);
append_icode_list(il, ii);
if (to->code == TY_FLOAT) {
/* Truncate to float */
icode_make_power_frsp(fpreg, il);
}
reg_set_allocatable(fpreg);
return ret;
}
static struct vreg *
conv_int_to_fp_64(struct vreg *ret, struct icode_list *il,
struct type *from, struct type *to) {
if (from->code < TY_LONG) {
int longcode;
if (from->sign == TOK_KEY_UNSIGNED) {
longcode = TY_ULONG;
} else {
longcode = TY_LONG;
}
ret = vreg_disconnect(ret);
change_preg_size(ret, make_basic_type(longcode), from, il);
ret = vreg_disconnect(ret);
from = make_basic_type(longcode);
ret->type = to;
ret->size = backend->get_sizeof_type(to, NULL);
}
if (IS_LONG(from->code) ||
IS_LLONG(from->code)) {
/*
* Load value as double, then fcfid it. That's
* it!
* XXX this only works for signed 64bit to
* double :-/ The other stuff just sucks too
* much
*/
struct vreg *double_vreg;
struct vreg *tmpret;
struct reg *fpreg;
/* Save integer to stack */
tmpret = n_xmemdup(ret, sizeof *ret); /* XXX :-( */
tmpret->type = from;
tmpret->size = backend->get_sizeof_type(from, NULL);
vreg_map_preg(tmpret, tmpret->pregs[0]);
free_preg(tmpret->pregs[0], il, 1, 1);
double_vreg = n_xmemdup(tmpret, sizeof *tmpret);
double_vreg->type = make_basic_type(TY_DOUBLE);
/* Now load it as double */
fpreg = backend->alloc_fpr(curfunc, 0, il, NULL);
vreg_faultin(fpreg, NULL, double_vreg,
il, 0);
icode_make_power_fcfid(fpreg, fpreg, il);
vreg_map_preg(ret, fpreg);
} else {
unimpl();
}
return ret;
}
/*
* Most of the time, instructions give meaning to data. This function
* generates code required to convert virtual register ``src'' to type
* ``to'' where necessary
*/
static struct vreg *
icode_make_cast(struct vreg *src, struct type *to, struct icode_list *il) {
struct vreg *ret;
struct type *from = src->type;
struct type *orig_to = to;
ret = src;
if (ret->type->tlist != NULL
|| (ret->type->code != TY_STRUCT
&& ret->type->code != TY_UNION)) {
vreg_anonymify(&ret, NULL, NULL /*r*/, il);
}
/* XXX anonymify.. */
if (ret == src) {
ret = vreg_disconnect(src);
}
ret->type = to;
if (to->code == TY_VOID) {
if (to->tlist == NULL) {
ret->size = 0;
free_pregs_vreg(ret, il, 0, 0);
return ret;
}
} else {
ret->is_nullptr_const = 0;
}
ret->size = backend->get_sizeof_type(to, NULL);
if (from->tlist != NULL && to->tlist != NULL) {
/*
* Pointers are always of same size
* and use same registers
*/
return ret;
} else if (to->tlist != NULL) {
/*
* Integral type to pointer type - cast to
* uintptr_t to get it to the same size
*/
to = backend->get_uintptr_t();
}
if (to->code != from->code) {
/*
* XXX source type may be trashed. This is perhaps a
* bug in vreg_anonymify()!!? this kludge fixes it
* for now ...
*/
src = n_xmemdup(src, sizeof *src);
src->type = from;
src->size = backend->get_sizeof_type(from, 0);
}
/*
* We may have to move the item to a different
* register as a result of the conversion
*/
if (to->code == from->code) {
return ret; /* Nothing to do */
} else if (to->tlist || from->tlist) {
; /* XXX hmm */
} else if (IS_FLOATING(to->code)) {
if (!IS_FLOATING(from->code)) {
if (backend->abi == ABI_POWER64) {
ret = conv_int_to_fp_64(ret, il, from, to);
} else {
ret = conv_int_to_fp_32(ret, il, from, to);
}
} else if (to->code != from->code) {
/* From fp to fp */
#if 0
if (from->code == TY_FLOAT && to->code == TY_DOUBLE) {
icode_make_power_set_load_double(il);
free_preg(ret->pregs[0], il, 1, 1);
vreg_faultin(NULL, NULL, ret, il, 0);
icode_make_power_unset_load_double(il);
} else {
/*unimpl();*/
}
#endif
if (from->code != TY_FLOAT && to->code == TY_FLOAT) {
icode_make_power_frsp(ret->pregs[0], il);
}
}
} else if (IS_FLOATING(from->code)) {
struct reg *r;
struct vreg *double_vreg;
struct vreg *int_vreg;
/*
* ``to'' has already been found to be non-fp.
* We have to round to float, then fctiwz the
* result (the backend does both in one go),
* save the result FPR as double, and then
* read the upper 4 bytes as integer word
*/
r = ALLOC_GPR(curfunc, 0, il, NULL);
icode_make_power_fctiwz(ret->pregs[0], il);
double_vreg = vreg_alloc(NULL, NULL, NULL, NULL);
vreg_map_preg(double_vreg, ret->pregs[0]);
double_vreg->type = make_basic_type(TY_DOUBLE);
double_vreg->size = 8;
icode_make_store(curfunc, double_vreg, double_vreg, il);
free_preg(ret->pregs[0], il, 1, 0);
int_vreg = vreg_alloc(NULL, NULL, NULL, NULL);
int_vreg->type = make_basic_type(TY_INT);
int_vreg->size = 4;
int_vreg->stack_addr = double_vreg->stack_addr;
icode_make_power_loadup4(r, int_vreg, il);
ret->pregs[0] = r;
if (IS_LLONG(to->code)) {
/* XXX */
ret->pregs[1] = ALLOC_GPR(curfunc, 0, il, NULL);
}
} else {
change_preg_size(ret, to, from, il);
}
vreg_set_new_type(ret, orig_to); /* because of uintptr_t stuff */
vreg_map_preg(ret, ret->pregs[0]);
if (backend->abi != ABI_POWER64
&& IS_LLONG(to->code)
&& to->tlist == NULL) {
ret->is_multi_reg_obj = 2;
vreg_map_preg2(ret, ret->pregs[1]);
} else {
ret->is_multi_reg_obj = 0;
ret->pregs[1] = NULL;
}
if (ret->type->code == TY_BOOL && ret->type->tlist == NULL) {
boolify_result(ret, il);
}
return ret;
}
static void
icode_make_structreloc(struct copystruct *cs, struct icode_list *il) {
relocate_struct_regs(cs, &power_gprs[3], &power_gprs[4],
&power_gprs[5], il);
}
static void
do_print_gpr(struct reg *r) {
printf("%s=%d ", r->name, r->used);
if (r->vreg && r->vreg->pregs[0] == r) {
printf("<-> %p", r->vreg);
}
}
static void
debug_print_gprs(void) {
int i;
for (i = 0; i < N_GPRS; ++i) {
if ((i % 3) == 0) {
printf("\t\t");
} else {
putchar('\t');
}
do_print_gpr(&power_gprs[i]);
if (((i+1) % 3) == 0) {
putchar('\n');
}
}
}
static int
is_multi_reg_obj(struct type *t) {
(void) t;
if (backend->abi != ABI_POWER64
&& IS_LLONG(t->code)
&& t->tlist == NULL) {
return 2;
}
return 0;
}
static struct reg *
name_to_reg(const char *name) {
(void) name;
return NULL;
}
static struct reg *
asmvreg_to_reg(
struct vreg **vr0,
int ch,
struct inline_asm_io *io,
struct icode_list *il,
int faultin) {
struct vreg *vr = *vr0;
(void) ch; (void) io; (void) il; (void)faultin;
if ((vr->type->code == TY_STRUCT || vr->type->code == TY_UNION)
&& vr->type->tlist == NULL) {
errorfl(io->expr->tok,
"Cannot load struct/union into register");
}
return NULL;
}
static char *
get_inlineasm_label(const char *tmpl) {
char *ret = n_xmalloc(strlen(tmpl) + sizeof "inlasm");
sprintf(ret, "inlasm%s", tmpl);
return ret;
}
struct backend power_backend = {
ARCH_POWER,
0, /* ABI */
0, /* multi_gpr_object */
8, /* structure alignment (set by init()) */
init,
is_multi_reg_obj,
get_ptr_size,
get_size_t,
get_uintptr_t,
get_sizeof_basic,
get_sizeof_type,
get_sizeof_elem_type,
get_sizeof_decl,
get_sizeof_const,
get_sizeof_vla_type,
get_align_type,
gen_program,
NULL,
NULL,
invalidate_gprs,
invalidate_except,
/*generic_*/ alloc_gpr,
NULL,
alloc_fpr,
NULL,
icode_make_fcall,
icode_make_return,
NULL,
icode_prepare_op,
icode_make_cast,
icode_make_structreloc,
make_null_block,
make_init_name,
debug_print_gprs,
name_to_reg,
asmvreg_to_reg,
get_inlineasm_label,
do_ret,
get_abi_reg,
get_abi_ret_reg,
generic_same_representation
};
syntax highlighted by Code2HTML, v. 0.9.1