/*
* 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.
*
* SPARC 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 "sparc_emit_as.h"
#include "cc1_main.h"
#include "n_libc.h"
static FILE *out;
struct emitter_sparc *emit_sparc;
#define N_GPRS 32
#define N_FPRS 64
struct reg sparc_gprs[N_GPRS];
struct reg *g_regs;
struct reg *l_regs;
struct reg *o_regs;
struct reg *i_regs;
static struct reg sparc_fprs[N_FPRS];
static struct vreg saved_gprs[N_GPRS];
static struct stack_block *saved_gprs_sb[N_GPRS];
/*#define STACK_ARG_START (24 + 8*4) */
/* XXX This SUCKS!! it is actually the save area start...
* stack arguments are really at STACK_ARG_START+6*sparc_gprs[0].size
*/
#define STACK_ARG_START ( \
16 * sparc_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;
char *p;
static char fpr_names[1024];
static char *names[] = {
"g0", "g1", "g2", "g3", "g4", "g5", "g6", "g7",
"o0", "o1", "o2", "o3", "o4", "o5", "o6", "o7",
"l0", "l1", "l2", "l3", "l4", "l5", "l6", "l7",
"i0", "i1", "i2", "i3", "i4", "i5", "i6", "i7",
};
g_regs = &sparc_gprs[0];
o_regs = &sparc_gprs[8];
l_regs = &sparc_gprs[16];
i_regs = &sparc_gprs[24];
/* Registers are just named after their number */
for (i = 0; i < N_GPRS; ++i) {
sparc_gprs[i].type = REG_GPR;
sparc_gprs[i].allocatable = 1;
if (backend->abi == ABI_SPARC64) {
sparc_gprs[i].size = 8;
} else {
sparc_gprs[i].size = 4;
}
sparc_gprs[i].name = names[i];
}
p = fpr_names;
for (i = 0; i < N_FPRS; ++i) {
sparc_fprs[i].type = REG_FPR;
sparc_fprs[i].allocatable = 1;
sparc_fprs[i].size = 8;
sparc_fprs[i].name = p;
p += sprintf(p, "f%d", i)+1;
}
/* Some registers with predefined meaning should not be allocated */
for (i = 0; i < 8; ++i) {
g_regs[i].allocatable = 0; /* Global registers */
}
o_regs[6].allocatable = 0; /* stack pointer o6 */
i_regs[6].allocatable = 0; /* frame pointer i6 */
tmpgpr = &g_regs[1];
tmpgpr->allocatable = 0;
/*
* tmpgpr2 is only used for 64bit address calculations
*/
if (backend->abi == ABI_SPARC64) {
tmpgpr2 = &l_regs[7];
tmpgpr2->allocatable = 0;
}
tmpfpr = &sparc_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;
/* Save all gprs except g0-g7 */
/*do_invalidate(&g_regs[1], il, saveregs) */
for (i = 8; i < N_GPRS; ++i) {
if (&sparc_gprs[i] == tmpgpr2) {
continue;
}
do_invalidate(&sparc_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(&sparc_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 (&sparc_gprs[i] == except[j]) {
break;
}
}
if (except[j] != NULL) {
continue;
}
do_invalidate(&sparc_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_SPARC64
&& 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,sparc_gprs,N_GPRS,
callee_save_map, line);
}
/*
* SPARC floating point register allocator. This is a little tricky
* because we have 64 32bit FPRs which can be combined into pairs and
* quadruples to form 64bit and 128bit double and long double sets.
*
* Register numbers have to be aligned, such that double may only
* occupy a first register whose number is divisible by 2, and long
* double one that is divisible by 4:
*
* [ f0 ][ f1 ][ f2 ][ f3 ][ f4 ]
* |_________|_________|_________|_________|____ float slots
* |___________________|___________________|____ double slots
* | |
* |_______________________________________|____ long double slots
*
* We just always mark the corresponding slot allocated. This means
* that if we're e.g. looking at f3 because we want to allocate a 32bit
* float there, we also have to consider whether f0 is allocated to a
* 4-fpr long double, in which case f3 is implicitly already in use!
* Additionally we also have to look at f2 to check for the same
* condition with double!
*
* XXX hmm this might have been a job for composed_of in struct reg..
* but maybe not.
*/
#define GET_DOUBLE_BASE(regno) \
(regno & ~1u)
#define GET_LDOUBLE_BASE(regno) \
(regno & ~3u)
#define IS_DOUBLE_SLOT(base) \
(sparc_fprs[base].vreg != NULL \
&& sparc_fprs[base].vreg->type->code == TY_DOUBLE)
#define IS_LDOUBLE_SLOT(base) \
(sparc_fprs[base].vreg != NULL \
&& sparc_fprs[base].vreg->type->code == TY_LDOUBLE)
static struct reg *
alloc_fpr(struct function *f, int size, struct icode_list *il,
struct reg *dontwipe) {
int i;
struct reg *ret = NULL;
int limit;
int stepsize;
int base;
static int lastalloc = 0;
if (size == 4) {
/* Only lower 32 FPRs can be used for floats */
limit = 32;
stepsize = 1; /* Can use any fpr */
} else {
limit = 64;
if (size == 8) {
stepsize = 2; /* 2-reg-alignment */;
} else { /* long double */
stepsize = 4; /* 4-reg-alignment */
}
}
(void) f; (void) size; (void) il; (void) dontwipe;
for (i = 0; i < limit; i += stepsize) {
#define REGFREE(r) (!r.used && r.allocatable)
if (REGFREE(sparc_fprs[i])) {
/*
* For double and long double, we actually need
* multiple free 32bit FPRs
*/
if (stepsize > 1) {
/* Need 2 or 4 regs */
if (!REGFREE(sparc_fprs[i+1])) {
continue;
}
if (stepsize == 4) {
/* Need 4 regs */
if (!REGFREE(sparc_fprs[i+2])
|| !REGFREE(sparc_fprs[i+3])) {
continue;
}
} else {
/*
* Must be double - check if we are
* in an allocated long double set
*/
base = GET_LDOUBLE_BASE(i);
if (base != i
&& IS_DOUBLE_SLOT(base)) {
if (!REGFREE(sparc_fprs[base])) {
continue;
}
}
}
} else {
/*
* This is a float - check if we are
* in an allocated double or long double
* register set
*/
base = GET_DOUBLE_BASE(i);
if (base != i
&& IS_DOUBLE_SLOT(base)) {
if (!REGFREE(sparc_fprs[base])) {
/* Already taken! */
continue;
}
}
base = GET_LDOUBLE_BASE(i);
if (base != i
&& IS_LDOUBLE_SLOT(base)) {
if (!REGFREE(sparc_fprs[base])) {
/* Already taken! */
continue;
}
}
}
ret = &sparc_fprs[i];
lastalloc = i;
break;
}
}
if (ret == NULL) {
#if 0
puts("uh-huh your floating point code is too heavy");
puts("sorry.");
abort();
#endif
/*
* We have to free a register that is already in use.
* In case of double and long double, we need an entire
* set of 32bit registers. We need to inspect the vreg
* corresponding to our registers to determine whether
* we need to save multiple small items or jus a big
* one.
*
* Note that lastalloc may have been aligned for a
* different type!
*/
lastalloc &= ~(unsigned)(stepsize - 1); /* Align */
lastalloc += stepsize;
lastalloc %= limit;
ret = &sparc_fprs[lastalloc];
if (stepsize == 1) {
if (ret->vreg && ret->vreg->size == 4 && ret->used) {
/* Free a float for new one! */
free_preg(ret, il, 1, 1);
} else {
base = GET_DOUBLE_BASE(lastalloc);
if (IS_DOUBLE_SLOT(base)
&& !REGFREE(sparc_fprs[base])) {
/* Using double slot */
free_preg(&sparc_fprs[base], il, 1, 1);
} else {
/* Using long double slot */
base = GET_LDOUBLE_BASE(lastalloc);
free_preg(&sparc_fprs[base], il, 1, 1);
}
}
} else if (stepsize == 2) {
if (ret->vreg && ret->vreg->size == 8 && ret->used) {
/* Free a double for a new one! */
free_preg(ret, il, 1, 1);
} else {
base = GET_LDOUBLE_BASE(lastalloc);
if (IS_LDOUBLE_SLOT(base)
&& !REGFREE(sparc_fprs[base])) {
free_preg(&sparc_fprs[base], il, 1, 1);
} else {
/*
* There must be one or two floats
* occupying our desired double slot
*/
if (!REGFREE(sparc_fprs[lastalloc])) {
free_preg(&sparc_fprs[lastalloc],
il, 1, 1);
}
if (!REGFREE(sparc_fprs[lastalloc+1])) {
free_preg(
&sparc_fprs[lastalloc+1],
il, 1, 1);
}
}
}
} else /* if (stepsize == 4) */ {
if (ret->vreg && ret->vreg->size == 16 && ret->used) {
/* Free a long double for a new one! */
free_preg(ret, il, 1, 1);
} else {
/*
* There may be up to 2 doubles and or up to
* 4 floats occupying our slot
*/
for (i = 0; i < 4; ++i) {
if (!REGFREE(sparc_fprs[lastalloc+i])) {
free_preg(&sparc_fprs[
lastalloc+i], il, 1, 1);
}
}
}
}
}
ret->used = 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 SPARC assembler `%s'\n",
asmflag);
exit(EXIT_FAILURE);
}
emit = &sparc_emit_as;
emit_sparc = &sparc_emit_sparc_as;
backend->emit = emit;
return emit->init(out, s);
}
static int
get_ptr_size(void) {
if (backend->abi != ABI_SPARC64) {
return 4;
} else {
return 8;
}
}
static struct type *
get_size_t(void) {
if (backend->abi != ABI_SPARC64) {
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_SPARC64) {
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 = sparc_gprs[0].size;
vreg_map_preg(&rvr, &i_regs[0]);
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, &i_regs[1]);
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, &i_regs[0]);
emit->load(&i_regs[0], &rvr);
if (ip && ip->src_vreg && ip->src_vreg->is_multi_reg_obj) {
rvr.stack_addr = f->alloca_regs->next;
vreg_map_preg(&rvr, &i_regs[1]);
emit->load(&i_regs[1], &rvr);
}
}
if (f->vla_head != NULL) {
struct stack_block *sb;
static struct vreg rvr;
rvr.stack_addr = f->alloca_regs;
rvr.size = sparc_gprs[0].size;
vreg_map_preg(&rvr, &i_regs[0]);
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, &i_regs[1]);
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, &i_regs[0]);
emit->load(&i_regs[0], &rvr);
if (ip && ip->src_vreg && ip->src_vreg->is_multi_reg_obj) {
rvr.stack_addr = f->alloca_regs->next;
vreg_map_preg(&rvr, &i_regs[1]);
emit->load(&i_regs[1], &rvr);
}
}
for (i = 8; i < N_GPRS; ++i) {
if (saved_gprs[i].stack_addr != NULL) {
emit->load(&sparc_gprs[i], &saved_gprs[i]);
}
}
emit->freestack(f, NULL);
emit->ret(ip);
}
/* 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->code == TY_STRUCT
|| se->dec->dtype->code == TY_UNION
|| se->dec->dtype->tlist != NULL) {
if (*gprs_used < 6) {
if (se->dec->dtype->tlist == NULL
&& (se->dec->dtype->code == TY_STRUCT
|| se->dec->dtype->code == TY_UNION)) {
/*
* Allocate storage for saving the struct -
* remember the caller only passed a
* pointer
*/
se->dec->stack_addr =
stack_malloc(curfunc, size);
stack_align(curfunc, 8);
} else {
/*
* The variable can be saved in the
* caller provided register save area
*/
se->dec->stack_addr = make_stack_block(0, size);
se->dec->stack_addr->offset = STACK_ARG_START +
*gprs_used * sparc_gprs[0].size;
}
se->dec->stack_addr->from_reg =
&i_regs[*gprs_used];
++*gprs_used;
} else {
/* Passed on stack */
se->dec->stack_addr = make_stack_block(
STACK_ARG_START+ *stack_bytes_used +
6 * sparc_gprs[0].size +
(sparc_gprs[0].size - size), size);
#if 0
se->dec->stack_addr = stack_malloc(
curfunc, size);
stack_align(curfunc, 16);
/**stack_bytes_used +=4;*/ /* XXX */
#endif
*stack_bytes_used += sparc_gprs[0].size;
}
} else if (IS_FLOATING(se->dec->dtype->code)) {
if (*fprs_used < 12) {
se->dec->stack_addr = make_stack_block(0, size);
se->dec->stack_addr->from_reg =
&sparc_fprs[/*12*/0 + *fprs_used];
switch (se->dec->dtype->code) {
case TY_FLOAT:
++*fprs_used;
break;
case TY_DOUBLE:
*fprs_used += 2;
break;
case TY_LDOUBLE:
*fprs_used += 4;
break;
}
} else {
/* XXX broken */
se->dec->stack_addr = make_stack_block(
STACK_ARG_START+ *stack_bytes_used, size);
*stack_bytes_used += 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 * sparc_gprs[0].size)
f->fty->lastarg->stack_addr =
make_stack_block(/*REG_SAVE_AREA_OFFSET*/
STACK_ARG_START, 0);
f->fty->lastarg->stack_addr->is_func_arg = 1;
}
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->type = f->hidden_pointer->var_backed->dtype;
f->hidden_pointer->var_backed->dtype->tlist->type =
TN_POINTER_TO;
/* Pointer goes to register save area */
f->hidden_pointer->var_backed->stack_addr =
make_stack_block(0, ptrsize);
f->hidden_pointer->var_backed->stack_addr->offset =
STACK_ARG_START;
f->hidden_pointer->var_backed->stack_addr->from_reg =
&i_regs[0];
f->hidden_pointer->var_backed->stack_addr->is_func_arg = 1;
++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 >= 6) {
/* Wow, all variadic stuff passed on stack */
#if 0
f->fty->lastarg->stack_addr->offset += /* XXX was = */
stack_bytes_used;
#endif
} else {
/*
* (64) 32 bytes were allocated, skip as many as
* necessary
*/
#if 0
f->fty->lastarg->stack_addr->offset +=
gprs_used * sparc_gprs[0].size;
#endif
f->fty->lastarg->stack_addr->from_reg =
&i_regs[gprs_used];
}
f->fty->lastarg->stack_addr->offset +=
gprs_used * sparc_gprs[0].size + stack_bytes_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;
}
size = backend->
get_sizeof_decl(dec[i], NULL);
#if 0
if (i+1 < scope->automatic_decls.ndecls
&& !dec[i+1]->dtype->is_vla) {
/* 11/07/07: This used dec[i] instead of i+1 */
#if 0
align = backend->get_align_type(dec[i+1]->dtype);
while ( /*(*/ f->total_allocated /*+ size)*/ % align) {
++f->total_allocated;
}
#endif
align = calc_align_bytes(f->total_allocated,
dec[i]->dtype,
dec[i+1]->dtype);
} else {
align = 0;
}
#endif
align = align_for_cur_auto_var(dec[i]->dtype, f->total_allocated);
if (align) {
(void)stack_malloc(f, align);
}
sb = stack_malloc(f, size);
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 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.section \".text\"\n");
/* 4 = 4 bytes, not bits?! */
x_fprintf(out, "\t.align 4\n");
if (f->proto->dtype->storage != TOK_KEY_STATIC) {
x_fprintf(out, "\t.global %s\n", f->proto->dtype->name);
}
x_fprintf(out, "\t.type %s, #function\n", f->proto->dtype->name);
emit->label(f->proto->dtype->name, 1);
funcname = f->proto->dtype->name;
proto = f->proto->dtype->tlist->tfunc;
/* Create space for saving frame pointer */
f->total_allocated += sparc_gprs[0].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, sparc_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 += sparc_gprs[0].size;
saved_gprs[i].stack_addr = saved_gprs_sb[i];
saved_gprs[i].size = sparc_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, sparc_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;
}
/*
* Allocate storage for saving VLA data, and initialize
* it to zero (must be patched like temporary register storage)
*/
for (sb = f->vla_head; sb != NULL; sb = sb->next) {
f->total_allocated += sb->nbytes;
vla_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, sparc_gprs[0].size);
f->total_allocated += sparc_gprs[0].size;
f->alloca_regs->offset = f->total_allocated;
f->alloca_regs->next = make_stack_block(0, sparc_gprs[0].size);
f->total_allocated += sparc_gprs[0].size;
f->alloca_regs->next->offset = f->total_allocated;
}
stack_align(f, 16);
/*
* The SPARC ABI requires the caller to allocate storage
* for saving argument registers and passing stack arguments,
* the latter of which is 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.
* 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 = 6 * sparc_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 += sparc_gprs[1].size; *//* saved sp */
/* register window save area and hidden parameter - 16+1 */
f->total_allocated += 17 * sparc_gprs[0].size;
/* 16 byte alignment */
while (f->total_allocated % 16) ++f->total_allocated;
if (f->total_allocated > 0) {
emit->allocstack(f, f->total_allocated);
}
/*
* Patch parameter offsets and save corresponding argument
* registers to stack, if necessary
*/
se = proto->scope->slist;
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 */
#if 0
se->dec->stack_addr->offset =
f->total_allocated +
se->dec->stack_addr->offset;
#endif
copy_struct_regstack(se->dec);
} else {
/*
* Passed in register, but backed by
* register save area
*/
#if 0
se->dec->stack_addr->offset =
f->total_allocated +
se->dec->stack_addr->offset+
REG_SAVE_AREA_OFFSET;
#endif
store_preg_to_var(se->dec,
se->dec->stack_addr->nbytes,
se->dec->stack_addr->from_reg);
}
} else {
#if 0
/*
* Argument passed on stack
*/
unimpl();
se->dec->stack_addr->offset =
f->total_allocated +
se->dec->stack_addr->offset;
#endif
}
}
if (f->hidden_pointer) {
struct decl *d = f->hidden_pointer->var_backed;
#if 0
d->stack_addr->offset = f->total_allocated + d->stack_addr->offset;
#endif
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
unimpl();
} else {
struct reg *r =
f->fty->lastarg->stack_addr->from_reg;
saved_offset = f->fty->lastarg->stack_addr->offset;
for (; r != &i_regs[6]; ++r) {
store_preg_to_var(f->fty->lastarg,
sparc_gprs[0].size, r);
f->fty->lastarg->stack_addr->offset +=
sparc_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;
emit->static_decls();
emit->struct_inits();
emit->empty();
emit->strings(); /* XXX bad */
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;
int is_struct = 0;
(void) bytes_left;
size = backend->get_sizeof_type(vr->type, NULL);
if (vr->type->tlist
&& vr->type->tlist->type ==
TN_ARRAY_OF) {
size = /*4*/sparc_gprs[0].size;
} else if (vr->type->tlist == NULL
&& (vr->type->code == TY_STRUCT
|| vr->type->code == TY_UNION)) {
/* Struct/union-by-value is passed by address */
size = sparc_gprs[0].size;
is_struct = 1;
}
if (/*4*/sparc_gprs[0].size - size > 0) {
/* Need to right-adjust */
*stack_bytes_used += /*4*/sparc_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+ 22 * sparc_gprs[0].size
/*STACK_ARG_START*/ /*-size*/, dest->size);
dest->stack_addr->use_frame_pointer = 0;
*stack_bytes_used += size;
if (is_struct) {
vr = n_xmemdup(vr, sizeof *vr);
vr->type = n_xmemdup(vr->type, sizeof *vr->type);
append_typelist(vr->type, TN_POINTER_TO, NULL, NULL, 0);
icode_make_copyreg(tmpgpr, vr->pregs[0], NULL, NULL, il);
} else {
vreg_faultin(tmpgpr, NULL, vr, il, 0);
}
vreg_map_preg(dest, tmpgpr);
icode_make_store(curfunc, dest, dest, il);
/*
* We have to ensure that the frontend never permanently uses
* tmpgpr
*/
tmpgpr->vreg = NULL;
tmpgpr->used = 0;
}
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 ... */
#if 0
/*
* 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 = &o_regs[i];
struct vreg *newvr;
newvr = vreg_alloc(0,0,0,0);
newvr->size = sparc_gprs[0].size;
newvr->type = make_basic_type(TY_ULONG);
newvr->stack_addr =
make_stack_block(reg_offset + sparc_gprs[0].size * i,
8);
newvr->stack_addr->use_frame_pointer = 0;
vreg_map_preg(newvr, &o_regs[i]);
icode_make_store(NULL, newvr, newvr, il);
reg_set_unused(&o_regs[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(&o_regs[i], NULL,
reg_vrs[i], il, 0);
reg_set_unallocatable(&o_regs[i]);
}
return 0;
}
#endif
static size_t
sparc_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 % sparc_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(&o_regs[0], il, 1, 1);
icode_make_copyreg(&o_regs[0], ii->dat,
NULL, NULL, il);
++gprs_used;
}
if (fcall->functype->nargs == -1
|| ty->implicit) {
/* Need default argument promotions */
need_dap = 1;
}
allpushed += sparc_calc_stack_bytes(vrs, nvrs, &takes_struct);
allpushed *= 2;
if (takes_struct) {
/* memcpy() will be called to pass argument(s) */
backend->invalidate_gprs(il, 1);
}
if (/*allpushed > 0*/ 1) {
if ((int)allpushed > curfunc->max_bytes_pushed) {
curfunc->max_bytes_pushed = allpushed;
}
}
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 < 6) {
put_arg_into_reg(o_regs,
&gprs_used, 0, 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) {
#if 0
pass_struct_union(vrs[i],
&gprs_used, &fprs_used, &stack_bytes_used, il);
#endif
/*
* Structs/unions passed by value are really
* passed by address in the SPARC ABI
*/
ii = icode_make_addrof(vrs[i], il);
append_icode_list(il, ii);
vreg_map_preg(vrs[i], ii->dat);
if (gprs_used < 6) {
icode_make_copyreg(&o_regs[gprs_used++],
ii->dat, NULL, NULL, il);
} else {
pass_arg_stack(vrs[i], 0,
&stack_bytes_used, il);
}
free_preg(ii->dat, il, 1, 0);
} 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) {
if (vrs[i]->type->code == TY_FLOAT) {
vrs[i] = backend->
icode_make_cast(vrs[i],
make_basic_type(TY_DOUBLE), il);
} else if (vrs[i]->type->code == TY_LDOUBLE) {
/*
* Long double is tricky because
* it 1) is passed in two gprs, and
* 2) those gprs need to be aligned
*/
if (gprs_used < 6) {
if (gprs_used & 1) {
/* Not aligned! */
++gprs_used;
}
}
}
vreg_faultin(NULL, NULL, vrs[i], il, 0);
if (gprs_used < 6) {
struct type *oty = vrs[i]->type;
vreg_anonymify(&vrs[i], NULL, NULL, il);
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;
vreg_faultin(&o_regs[gprs_used],
NULL,
vrs[i], il, 0);
++gprs_used;
if (oty->code == TY_LDOUBLE) {
if (gprs_used < 6) {
struct vreg *tmp;
tmp = n_xmemdup(vrs[i],
sizeof *vrs[i]);
tmp->pregs[0] = NULL;
vreg_faultin(
&o_regs[gprs_used],
NULL, tmp, il, 0);
++gprs_used;
} else {
unimpl();
}
}
#if 0
put_arg_into_reg(sparc_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(sparc_fprs,
&fprs_used, 0, 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(&o_regs[0], 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(&o_regs[i]);
o_regs[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) {
o_regs[i].allocatable = 0;
}
vreg_faultin(tmpgpr, NULL, tmpvr, il, 0);
for (i = 0; i < gprs_used; ++i) {
o_regs[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] = &o_regs[0];
} else {
struct type_node *tnsav = ty->tlist;
ty->tlist = NULL;
if (backend->abi != ABI_SPARC64
&& IS_LLONG(ty->code)) {
is_multi_reg_obj = 2;
}
if (is_integral_type(ty)) {
ret->pregs[0] = &o_regs[0];
if (is_multi_reg_obj) {
ret->pregs[1] = &o_regs[1];
}
} else if (ty->code == TY_FLOAT
|| ty->code == TY_DOUBLE
|| ty->code == TY_LDOUBLE) {
ret->pregs[0] = &sparc_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 = 0; i < 6; ++i) {
reg_set_allocatable(&o_regs[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(&i_regs[0],
&i_regs[1], vr, il, 0);
} else {
vreg_faultin(&i_regs[0], NULL,
vr, il, 0);
}
} else if (rtype->code == TY_FLOAT
|| rtype->code == TY_DOUBLE
|| rtype->code == TY_LDOUBLE) {
/* XXX ldouble ... */
vreg_faultin(&sparc_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 0
if (op == TOK_OP_DIVIDE
|| op == TOK_OP_MULTI
|| op == TOK_OP_MOD
|| (backend->abi != ABI_SPARC64
&& IS_LLONG(dest[0]->type->code)
&& dest[0]->type->tlist == NULL
&& (op == TOK_OP_PLUS
|| op == TOK_OP_MINUS
|| op == TOK_OP_BSHL
|| op == TOK_OP_BSHR))) {
/*
* Div/mul and 64bit add/sub are done in software -
* prepare for function call
*/
backend->invalidate_gprs(il, 1);
}
#endif
vreg_faultin(NULL, NULL, *dest, il, 0);
vreg_faultin(NULL, NULL, *src, il, 0);
(void) op;
}
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_SPARC64
&& IS_LONG(to->code))) {
to_is_64bit = 1;
}
}
if (from->tlist == NULL) {
if (IS_LLONG(from->code)
|| (backend->abi == ABI_SPARC64
&& 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) {
unimpl();
}
} else if (to_is_64bit) {
if (!from_is_64bit && !from->tlist) {
if (backend->abi != ABI_SPARC64) {
ret->pregs[1] = ret->pregs[0];
ret->pregs[0] = ALLOC_GPR(curfunc
, 0, il, NULL);
if (from->sign != TOK_KEY_UNSIGNED) {
unimpl();
} else {
ii = icode_make_setreg(
ret->pregs[0], 0);
append_icode_list(il, ii);
}
} else {
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();
}
unimpl();
} else {
/* from signed */
if (from->code == TY_SCHAR) {
} else if (from->code == TY_SHORT) {
} else if (from->code == TY_INT) {
} else {
}
unimpl();
}
}
}
} else if (from_is_64bit) {
struct reg *r = ret->pregs[0];
if (backend->abi != ABI_SPARC64) {
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
*/
sparc_gprs[0].allocatable = sparc_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(tmpgpr, needand);
append_icode_list(il, ii);
ii = icode_make_and(ret, NULL);
append_icode_list(il, ii);
}
if (needlwi) {
unimpl();
}
if (needextsh) {
unimpl();
}
}
sparc_gprs[0].allocatable = sparc_gprs[0].used = 0;
}
/*
* 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;
struct icode_instr *ii;
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 (backend->abi == ABI_SPARC64
&& (IS_LONG(from->code) ||
IS_LLONG(from->code))) {
/*
* Load value as integer, then conv_fp it. That's
* it!
*/
struct vreg *double_vreg;
struct vreg *tmpret;
struct reg *fpreg;
int to_size;
to_size = backend->get_sizeof_type(to, NULL);
/* 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);
/* Now load as double */
double_vreg = n_xmemdup(tmpret, sizeof *tmpret);
double_vreg->type = make_basic_type(TY_DOUBLE);
if (to_size == 4) {
to_size = 8;
}
fpreg = backend->alloc_fpr(curfunc, to_size, il, NULL);
vreg_faultin(fpreg, NULL, double_vreg,
il, 0);
vreg_map_preg(ret, fpreg);
icode_make_conv_fp(ret->pregs[0], ret->pregs[0],
to, from, il);
} else if (!IS_FLOATING(from->code)) {
struct vreg *fp_vreg;
/*
* We have to load the integer value into a
* floating point register and then perform
* a fitos/fitod on it
*/
fp_vreg = ret;
if (IS_CHAR(from->code) || IS_SHORT(from->code)) {
struct type *ity = make_basic_type(TY_INT);
if (from->code == TY_SCHAR
|| from->code == TY_SHORT) {
/* Have to sign-extend */
icode_make_extend_sign(ret, ity,
from, il);
}
from = ity;
}
vreg_reinterpret_as(&fp_vreg, from, to, il);
icode_make_conv_fp(fp_vreg->pregs[0],
fp_vreg->pregs[0], to, from, il);
vreg_map_preg(ret, fp_vreg->pregs[0]);
} else if (to->code != from->code) {
/*
* From fp to fp.
* Double to float means we can cut the 2-reg set
* Float to double means we may have to allocate a
* new 2-reg set (currently always done - the
* icode_make_conv_fp interface sucks)
*/
/* XXXXXXXXX need to aligned alloc reg !!!! */
struct reg *r;
reg_set_unallocatable(ret->pregs[0]);
r = backend->alloc_fpr(curfunc,
backend->get_sizeof_type(to, NULL), il, 0);
icode_make_conv_fp(r, ret->pregs[0], to, from, il);
ret->pregs[0] = r;
}
} 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 load as float/double (depending on
* whether the source integer is 32bit or 64bit)
* and then fitos/fitod it
*/
r = ALLOC_GPR(curfunc, 0, il, NULL);
double_vreg = vreg_alloc(NULL, NULL, NULL, NULL);
vreg_map_preg(double_vreg, ret->pregs[0]);
double_vreg->type = from;
double_vreg->size = backend->get_sizeof_type(from, NULL);
icode_make_conv_fp(ret->pregs[0], ret->pregs[0], to, from, il);
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 = to;
int_vreg->size = backend->get_sizeof_type(to, NULL);
int_vreg->stack_addr = double_vreg->stack_addr;
vreg_faultin(r, NULL, int_vreg, il, 0);
ret->pregs[0] = r;
} else {
#if 0
change_preg_size(ret, to, from, il);
#endif
int to_size = backend->get_sizeof_type(to, NULL);
int from_size = backend->get_sizeof_type(from, NULL);
unsigned needand = 0;
if (to_size == from_size) {
;
} else if (to->tlist != NULL) {
; /* XXX What 2 do */
} else if (to_size < from_size) {
/* Truncate */
if (to->tlist != NULL) {
/* XXX hmm */
} else if (IS_CHAR(to->code)) {
needand = 0xff;
} else if (IS_SHORT(to->code)) {
needand = 0xffff;
} else if (IS_INT(to->code)
|| IS_LONG(to->code)) {
/* Must be from 64bit long or long long */
needand = 0xffffffff;
} else {
unimpl();
}
} else {
/* to_size > from_size - sign- or zero-extend */
if (from->sign == TOK_KEY_UNSIGNED) {
needand = 0xffffffff;
} else {
/* sign-extend */
icode_make_extend_sign(ret, to, from, il);
}
}
if (needand) {
ii = icode_make_setreg(tmpgpr, needand);
append_icode_list(il, ii);
ii = icode_make_and(ret, NULL);
append_icode_list(il, ii);
}
}
vreg_set_new_type(ret, orig_to); /* because of uintptr_t stuff */
vreg_map_preg(ret, ret->pregs[0]);
if (backend->abi != ABI_SPARC64
&& 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, &o_regs[0], &o_regs[1],
&o_regs[2], 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(&sparc_gprs[i]);
if (((i+1) % 3) == 0) {
putchar('\n');
}
}
}
static int
is_multi_reg_obj(struct type *t) {
(void) t;
if (backend->abi != ABI_SPARC64
&& 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;
}
static struct reg *
get_abi_reg(int index, struct type *ty) {
if (index == 0
&& (is_integral_type(ty)
|| ty->tlist != NULL)) {
return &o_regs[0];
} else {
unimpl();
}
return NULL;
}
static struct reg *
get_abi_ret_reg(struct type *ty) {
if (is_integral_type(ty) || ty->tlist != NULL) {
return &o_regs[0];
} else {
unimpl();
}
/* NOTREACHED */
return NULL;
}
/*
* Calculates the amount of stack bytes needed to pass vrs to
* a function, if any. This is because it's more convenient
* to pre-allocated any needed space in advance, rather than
* doing it when needed. Special care must be taken to ensure
* that this stuff is always in sync with the code that
* actually pushes the arguments onto the stack.
*
* It is assumed that the stack starts out being word aligned.
*
*/
static size_t
sparc_calc_stack_bytes(struct vreg **vrs, int nvrs, int *takes_struct) {
size_t nbytes = 0;
int i;
int stackstruct = 0;
for (i = 0; i < nvrs; ++i) {
nbytes += sparc_gprs[0].size * 2;
if (vrs[i]->type->code == TY_STRUCT
|| vrs[i]->type->code == TY_UNION) {
nbytes += sparc_gprs[0].size;
stackstruct = 1;
}
}
while (nbytes % 16) {
++nbytes;
}
/* Allocate space for saving registers */
if (stackstruct) {
/* XXXXXXXX wow reg_offset seems broken!??! */
reg_offset = nbytes;
nbytes += 8 * 16;
*takes_struct = 1;
} else {
*takes_struct = 0;
}
return nbytes;
}
struct backend sparc_backend = {
ARCH_SPARC,
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