/*
* Copyright (c) 2005 - 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.
*
* A grabbag of function for managing the backend, and also for backend stuff
* that is generic across two or more platforms
*/
#include "backend.h"
#include <stdlib.h>
#include <limits.h>
#include "error.h"
#include "icode.h"
#include "decl.h"
#include "functions.h"
#include "expr.h"
#include "attribute.h"
#include "debug.h"
#include "token.h"
#include "scope.h"
#include "type.h"
#include "features.h"
#include "typemap.h"
#include "symlist.h"
#include "n_libc.h"
struct backend *backend;
struct emitter *emit;
struct reg *tmpgpr;
struct reg *tmpgpr2;
struct reg *tmpfpr;
int host_endianness;
char *tunit_name;
size_t tunit_size;
struct init_with_name *init_list_head;
struct init_with_name *init_list_tail;
extern int archflag;
extern int abiflag;
int backend_warn_inv;
int
init_backend(FILE *fd, struct scope *s) {
unsigned int foo = 123;
if (*(unsigned char *)&foo == 123) {
host_endianness = ENDIAN_LITTLE;
} else {
/* Naive - doesn't work with PDP endianness */
host_endianness = ENDIAN_BIG;
}
/*backend = &x86_backend;*/
#if 0
#ifdef __i386__
backend = &x86_backend;
#elif defined __amd64__
backend = &amd64_backend;
#elif defined __sgi
backend = &mips_backend;
#elif defined _AIX
backend = &power_backend;
#else
#error "Unsupported target architecture"
#endif
#endif
switch (archflag) {
case ARCH_X86:
backend = &x86_backend;
break;
case ARCH_AMD64:
backend = &amd64_backend;
break;
case ARCH_POWER:
backend = &power_backend;
break;
case ARCH_MIPS:
backend = &mips_backend;
break;
case ARCH_SPARC:
backend = &sparc_backend;
break;
default:
puts("UNKNOWN ARCHITECTURE!!!!!!!!!!");
abort();
}
backend->abi = abiflag;
/*backend = &power_backend;*/
return backend->init(fd, s);
}
struct vreg *
get_parent_struct(struct vreg *vr) {
struct vreg *vr2 = vr;
while (vr2->parent != NULL) {
if (vr2->from_ptr) break;
vr2 = vr2->parent;
}
return vr2;
}
size_t
calc_align_bytes(size_t offset, struct type *curtype, struct type *nexttype) {
size_t cursize;
size_t alignto;
size_t oldoffset;
cursize = backend->get_sizeof_type(curtype, NULL);
offset += cursize;
alignto = backend->get_align_type(nexttype);
oldoffset = offset;
while (offset % alignto) {
++offset;
}
return offset - oldoffset;
}
static void
map_pregs(struct vreg *vr, struct reg **pregs) {
if (vr != NULL) {
if (pregs && pregs[0]) {
vreg_map_preg(vr, pregs[0]);
if (vr->is_multi_reg_obj && pregs[1]) {
vreg_map_preg2(vr, pregs[1]);
} else {
vr->pregs[1] = NULL;
}
} else {
vr->pregs[0] = NULL;
}
}
}
int
do_xlate(
struct function *f,
struct icode_instr **ipp) {
struct icode_instr *ip = *ipp;
struct stack_block *sb;
struct allocstack *as;
int found;
map_pregs(ip->src_vreg, ip->src_pregs);
map_pregs(ip->dest_vreg, ip->dest_pregs);
if (ip->src_parent_struct) {
if (ip->src_ptr_preg) {
/*
* 08/05/07: UNBELIEVABLE! This used to map the
* register to the parent struct vreg, but not to
* the parent struct from_ptr vreg, whree it would
* have belonged! That one is used by the emitters,
* so stuff broke
*/
vreg_map_preg(ip->src_parent_struct->from_ptr,
ip->src_ptr_preg);
}
} else if (ip->src_ptr_preg) {
vreg_map_preg(ip->src_vreg->from_ptr, ip->src_ptr_preg);
}
if (ip->dest_parent_struct) {
if (ip->dest_ptr_preg) {
/*
* 08/05/07: See comment above about source parent
* pointer
*/
vreg_map_preg(ip->dest_parent_struct->from_ptr,
ip->dest_ptr_preg);
}
} else if (ip->dest_ptr_preg) {
vreg_map_preg(ip->dest_vreg->from_ptr, ip->dest_ptr_preg);
}
switch (ip->type) {
case INSTR_CALL:
emit->call(ip->dat);
break;
case INSTR_CALLINDIR:
emit->callindir(ip->dat);
break;
case INSTR_PUSH:
emit->push(f, ip);
break;
case INSTR_ALLOCSTACK:
/*
* XXX freestack updates total_allocated but allocstack
* doesn't. Is this good?
*/
as = ip->dat;
emit->allocstack(f, as->nbytes);
f->total_allocated += as->nbytes;
if (as->patchme != NULL) {
/* The space is actually used! */
sb = make_stack_block(f->total_allocated, as->nbytes);
as->patchme->var_backed->stack_addr = sb;
}
break;
case INSTR_FREESTACK:
emit->freestack(f, ip->dat);
break;
case INSTR_ADJ_ALLOCATED:
emit->adj_allocated(f, ip->dat);
break;
case INSTR_INDIR:
break;
case INSTR_MOV:
emit->mov(ip->dat);
break;
case INSTR_SETREG:
emit->setreg(ip->src_pregs[0], (int *)ip->dat);
break;
case INSTR_ADDROF: {
struct vreg *vr = NULL;
if (ip->src_vreg != NULL
&& ip->src_vreg->parent != NULL) {
vr = get_parent_struct(ip->src_vreg);
}
emit->addrof(ip->dat, ip->src_vreg, vr);
}
break;
case INSTR_INC:
emit->inc(ip);
break;
case INSTR_DEC:
emit->dec(ip);
break;
case INSTR_LABEL:
/* generated labels are always local */
emit->label(ip->dat, 0);
break;
case INSTR_NEG:
emit->neg(ip->src_pregs, ip);
break;
case INSTR_SUB:
emit->sub(ip->dest_pregs, ip);
break;
case INSTR_ADD:
emit->add(ip->dest_pregs, ip);
break;
case INSTR_MUL:
emit->mul(ip->dest_pregs, ip);
break;
case INSTR_DIV:
emit->div(ip->dest_pregs, ip, 0);
break;
case INSTR_MOD:
emit->mod(ip->dest_pregs, ip);
break;
case INSTR_SHL:
emit->shl(ip->dest_pregs, ip);
break;
case INSTR_SHR:
emit->shr(ip->dest_pregs, ip);
break;
case INSTR_OR:
emit->or(ip->dest_pregs, ip);
break;
case INSTR_PREG_OR:
emit->preg_or(ip->dest_pregs, ip);
break;
case INSTR_AND:
emit->and(ip->dest_pregs, ip);
break;
case INSTR_XOR:
emit->xor(ip->dest_pregs, ip);
break;
case INSTR_NOT:
emit->not(ip->src_pregs, ip);
break;
case INSTR_RET:
backend->do_ret(f, ip);
break;
case INSTR_SEQPOINT:
break;
case INSTR_STORE:
/* XXX confusingly messed up order of args */
#if FEAT_DEBUG_DUMP_BOGUS_STORES
if (ip->dest_vreg->stack_addr != NULL) {
struct icode_instr *tmp;
struct stack_block *sb = ip->dest_vreg->stack_addr;
for (tmp = ip->next; tmp != NULL; tmp = tmp->next) {
if (tmp->src_vreg
&& tmp->src_vreg->stack_addr == sb) {
if (tmp->type == INSTR_LOAD) {
break;
} else {
emit->comment("used by ? as src");
}
} else if (tmp->dest_vreg
&& tmp->dest_vreg->stack_addr == sb) {
emit->comment("used by ? as dest");
}
}
if (tmp == NULL) {
emit->comment("seems unneeded");
}
}
#endif
emit->store(ip->src_vreg, ip->dest_vreg);
if (ip->src_vreg->is_multi_reg_obj) {
emit->store(ip->src_vreg, ip->dest_vreg);
}
break;
case INSTR_WRITEBACK:
emit->store(ip->src_vreg, ip->src_vreg);
break;
case INSTR_LOAD:
emit->load(ip->src_pregs[0], ip->src_vreg);
break;
case INSTR_DEBUG:
emit->comment(ip->dat);
break;
case INSTR_DBGINFO_LINE:
emit->dwarf2_line(ip->dat);
break;
case INSTR_UNIMPL:
emit->genunimpl();
break;
case INSTR_CMP:
emit->cmp(ip->dest_pregs, ip);
break;
case INSTR_EXTEND_SIGN:
emit->extend_sign(ip);
break;
case INSTR_CONV_FP:
emit->conv_fp(ip);
break;
case INSTR_COPYINIT:
emit->copyinit(ip->dat);
break;
case INSTR_COPYSTRUCT:
emit->copystruct(ip->dat);
break;
case INSTR_INTRINSIC_MEMCPY:
emit->intrinsic_memcpy(ip->dat);
break;
case INSTR_ALLOCA: {
struct allocadata *ad = ip->dat;
static struct vreg vr;
/*
* XXXXXXX 08/27/07 This sucks:
* - alloca_ should be renamed alloca
* - The store below saves some code
* duplication (which is why it's here),
* but makes it difficult to find!!! This
* should go into icode_make_alloca()
*
* Was wrong because:
*
* - Making the register anonymous and
* type-less breaks on systems where the
* ABI uses pointers of different size
* than GPRs; e.g. MIPS/N32. Now we use
* make_void_ptr_type() instead
*/
emit->alloca_(ad);
/* Now save the result pointer */
vr.stack_addr = ad->addr;
vr.type = make_void_ptr_type();
vr.size = backend->get_sizeof_type(vr.type, NULL);
vreg_map_preg(&vr, ad->result_reg);
emit->store(&vr, &vr);
}
break;
case INSTR_DEALLOCA:
emit->dealloca(ip->dat, ip->src_pregs[0]);
break;
case INSTR_ALLOC_VLA:
emit->alloc_vla(ip->dat);
break;
case INSTR_DEALLOC_VLA:
emit->dealloc_vla(ip->dat, NULL);
break;
case INSTR_PUT_VLA_SIZE:
emit->put_vla_size(ip->dat);
break;
case INSTR_RETR_VLA_SIZE:
emit->retr_vla_size(ip->dat);
break;
case INSTR_LOAD_VLA:
emit->load_vla(ip->dest_pregs[0],
((struct type *)ip->dat)->vla_addr);
break;
case INSTR_BUILTIN_FRAME_ADDRESS:
emit->frame_address(ip->dat);
break;
case INSTR_ASM:
emit->inlineasm(ip->dat);
break;
case INSTR_BR_EQUAL:
case INSTR_BR_NEQUAL:
case INSTR_BR_GREATER:
case INSTR_BR_SMALLER:
case INSTR_BR_GREATEREQ:
case INSTR_BR_SMALLEREQ:
case INSTR_JUMP:
emit->branch(ip);
break;
case INSTR_XCHG:
emit->xchg(ip->src_pregs[0], ip->dest_pregs[0]);
break;
default:
/* Must be machine specific */
found = 1; /* XXX uh-huh should this not be 0? :( */
if (backend->arch == ARCH_X86
|| backend->arch == ARCH_AMD64) {
switch (ip->type) {
case INSTR_X86_FXCH:
emit_x86->fxch(ip->dest_pregs[0],
ip->src_pregs[0]);
break;
case INSTR_X86_FFREE:
emit_x86->ffree(ip->src_pregs[0]);
break;
case INSTR_X86_FNSTCW:
emit_x86->fnstcw(ip->src_vreg);
break;
case INSTR_X86_FLDCW:
emit_x86->fldcw(ip->src_vreg);
break;
case INSTR_X86_CDQ:
emit_x86->cdq();
break;
case INSTR_AMD64_CVTTSS2SI:
emit_amd64->cvttss2si(ip);
break;
case INSTR_AMD64_CVTTSD2SI:
emit_amd64->cvttsd2si(ip);
break;
case INSTR_AMD64_CVTSI2SD:
emit_amd64->cvtsi2sd(ip);
break;
case INSTR_AMD64_CVTSI2SS:
emit_amd64->cvtsi2ss(ip);
break;
case INSTR_AMD64_CVTSD2SS:
emit_amd64->cvtsd2ss(ip);
break;
case INSTR_AMD64_CVTSS2SD:
emit_amd64->cvtss2sd(ip);
break;
default:
found = 0;
}
} else if (backend->arch == ARCH_POWER) {
switch (ip->type) {
case INSTR_POWER_SRAWI:
emit_power->srawi(ip);
break;
case INSTR_POWER_RLDICL:
emit_power->rldicl(ip);
break;
case INSTR_POWER_FCFID:
emit_power->fcfid(ip);
break;
case INSTR_POWER_FRSP:
emit_power->frsp(ip);
break;
case INSTR_POWER_RLWINM:
emit_power->rlwinm(ip);
break;
case INSTR_POWER_SLWI:
emit_power->slwi(ip);
break;
case INSTR_POWER_EXTSB:
emit_power->extsb(ip);
break;
case INSTR_POWER_EXTSH:
emit_power->extsh(ip);
break;
case INSTR_POWER_EXTSW:
emit_power->extsw(ip);
break;
case INSTR_POWER_XORIS:
emit_power->xoris(ip);
break;
case INSTR_POWER_LIS:
emit_power->lis(ip);
break;
case INSTR_POWER_LOADUP4:
emit_power->loadup4(ip);
break;
case INSTR_POWER_FCTIWZ:
emit_power->fctiwz(ip);
break;
default:
found = 0;
}
} else if (backend->arch == ARCH_MIPS) {
switch (ip->type) {
case INSTR_MIPS_MFC1:
emit_mips->mfc1(ip);
break;
case INSTR_MIPS_MTC1:
emit_mips->mtc1(ip);
break;
case INSTR_MIPS_CVT:
emit_mips->cvt(ip);
break;
case INSTR_MIPS_TRUNC:
emit_mips->trunc(ip);
break;
case INSTR_MIPS_MAKE_32BIT_MASK:
emit_mips->make_32bit_mask(ip);
break;
default:
found = 0;
}
}
if (found) {
break;
}
printf("Unknown instruction - %d\n", ip->type);
return -1;
}
return 0;
}
int
xlate_icode(
struct function *f,
struct icode_list *ilp,
struct icode_instr **lastret) {
struct icode_instr *ip;
if (ilp == NULL) {
/* Empty function */
return 0;
}
for (ip = ilp->head; ip != NULL; ip = ip->next) {
if (do_xlate(f, &ip) != 0) {
return -1;
}
if (ip->type == INSTR_RET) {
*lastret = ip;
}
}
return 0;
}
/*
* For designated initializers: Allocate static designated initializer
* and initialize it with init
*/
struct vreg *
vreg_static_alloc(struct type *ty, struct initializer *init) {
struct vreg *ret;
static struct decl dec;
struct decl *decp;
size_t size;
struct decl *dummy[2];
size = backend->get_sizeof_type(ty, NULL);
dec.dtype = ty;
decp = n_xmemdup(&dec, sizeof dec);
decp->init = init;
ret = vreg_alloc(decp, NULL, NULL, NULL);
decp->dtype = n_xmemdup(decp->dtype, sizeof *decp->dtype);
decp->dtype->is_func = 0;
decp->dtype->storage = 0;
decp->dtype->name = NULL;
dummy[0] = decp;
dummy[1] = NULL;
store_decl_scope(curscope, dummy);
return ret;
}
struct vreg *
vreg_stack_alloc(struct type *ty, struct icode_list *il, int on_frame,
struct initializer *init) {
struct vreg *ret;
static struct decl dec;
struct decl *decp;
size_t size;
size = backend->get_sizeof_type(ty, NULL);
dec.dtype = ty;
decp = n_xmemdup(&dec, sizeof dec);
ret = vreg_alloc(decp, NULL, NULL, NULL);
/*
* 08/18/07: The old icode_make_allocstack() solution was
* only suitable for allocating stack at the current stack
* pointer position. This is inadequate for allocating
* temporary storage like anonymous structs
*/
if (!on_frame) {
/*
* Allocate at current stack pointer position
*/
icode_make_allocstack(ret, size, il);
} else {
/*
* Allocate when the stack frame is created. We can
* easily do this by linking the declaration on the
* scope list of declarations!
*/
struct decl *dummy[2];
decp->dtype = n_xmemdup(decp->dtype, sizeof *decp->dtype);
decp->dtype->is_func = 0;
decp->dtype->storage = 0;
decp->dtype->name = NULL;
dummy[0] = decp;
dummy[1] = NULL;
store_decl_scope(curscope, dummy);
}
/*
* Careful now if we have an initializer. Since this function is
* only called with initializer for compound literals, we have
* to generate the initializer exactly here! The slightest
* reordering can cause garbage results because the literal is
* probably part of a larger expression such as:
*
* printf("%d\n", (struct foo){ .bar = rand() }.bar);
*
* ... here reordering may cause the call to be performed before
* initialization. Likewise doing the initialization too too early
* may cause non-constant initializers to misbehave
*/
if (init != NULL) {
/*
* Only set init member to initilizer for call to
* init_to_icode(). We have to remove it again afterwards
* because otherwise it will be initialized again when
* the variable is created
*/
decp->init = init;
backend->invalidate_gprs(il, 1);
/*
* 09/14/07: Forgot to create a vreg for the declaration!
* This must be done here because the declaration is not
* linked on the declaration list, where it would be done
* automatically
*/
decp->vreg = vreg_alloc(decp, NULL, NULL, NULL);
vreg_set_new_type(decp->vreg, decp->dtype);
init_to_icode(decp, il);
decp->init = NULL;
}
return ret;
}
struct initializer *
make_null_block(struct sym_entry *se,
struct type *ty,
struct type *struct_ty,
int remaining) {
struct initializer *ret;
size_t size = 0;
size_t msize;
size_t align;
struct sym_entry *startse = se;
int struct_align = 1 ;
int start_offset = 0;
if (struct_ty != NULL) {
struct_align = backend->get_align_type(struct_ty);
if (se != NULL) {
start_offset = se->dec->offset;
}
}
if (se != NULL) {
for (; se != NULL; se = se->next) {
int orig_align;
/*
* 08/07/07: Changed this stuff, hope it's correct
* now
*/
align = backend->get_align_type(se->dec->dtype);
orig_align = align;
while ((struct_align+start_offset+size) % align) ++align;
size += align - orig_align;
size += backend->get_sizeof_type(se->dec->dtype, NULL);
}
} else {
align = backend->get_align_type(ty);
msize = backend->get_sizeof_type(ty, NULL);
if (align > msize) {
size = align * remaining;
} else {
size = msize * remaining;
}
}
/* XXX this is BORKORORENORK */
/* 08/07/07: Removed line below, should not be necessary anymore */
#if 0
while (size % struct_align /*backend->struct_align*/) ++size;
#endif
ret = alloc_initializer();
ret->type = INIT_NULL;
ret->data = n_xmemdup(&size, sizeof size);
if (struct_ty != NULL) {
ret->left_alignment = struct_align;
ret->left_type = startse->dec->dtype;
} else {
ret->left_alignment = align;
}
return ret;
}
struct init_with_name *
make_init_name(struct initializer *init) {
static unsigned long count;
char name[128];
struct init_with_name *ret;
sprintf(name, "_Agginit%lu", count++);
ret = n_xmalloc(sizeof *ret);
ret->name = n_xstrdup(name);
ret->init = init;
ret->next = NULL;
if (init_list_head == NULL) {
init_list_head = init_list_tail = ret;
} else {
init_list_tail->next = ret;
init_list_tail = init_list_tail->next;
}
return ret;
}
size_t
get_sizeof_const(struct token *constant) {
if (constant->type == TOK_STRING_LITERAL) {
struct ty_string *str;
str = constant->data;
return str->size;
} else {
return backend->get_sizeof_basic(constant->type);
}
}
size_t
get_sizeof_elem_type(struct type *t) {
struct type_node *head = t->tlist;
size_t ret;
t->tlist = t->tlist->next;
ret = backend->get_sizeof_type(t, NULL);
t->tlist = head;
return ret;
}
size_t
get_sizeof_decl(struct decl *d, struct token *tok) {
if (d->size == 0) {
d->size = backend->get_sizeof_type(d->dtype, tok);
}
return d->size;
}
static size_t
get_union_align(struct type *ty) {
struct sym_entry *se;
size_t maxalign = 0;
for (se = ty->tstruc->scope->slist;
se != NULL;
se = se->next) {
size_t align;
align = backend->
get_align_type(se->dec->dtype);
if (align > maxalign) {
maxalign = align;
}
}
return maxalign;
}
/*
* XXX this isn't as platform-independent and invariable as the author of
* this stuff would have you believe. For starters, consider ``long double''
* on x86 and x86-64.
*/
size_t
get_align_type(struct type *ty) {
if (ty->fastattr & CATTR_ALIGNED) {
return lookup_attr(ty->attributes, ATTRS_ALIGNED)->iarg;
}
if (ty->tlist == NULL
|| ty->tlist->type != TN_ARRAY_OF) {
int ret;
#define X86IFY(val) if (backend->arch == ARCH_X86 && val > 4) val = 4
if (ty->tlist == NULL) {
if (ty->code == TY_UNION) {
ret = get_union_align(ty);
X86IFY(ret);
return ret;
} else if (ty->code == TY_STRUCT) {
struct attrib *a;
a = lookup_attr(ty->tstruc->attrib, ATTRS_ALIGNED);
if (ty->tstruc->alignment && a == NULL) {
ret = ty->tstruc->alignment;
X86IFY(ret);
return ret;
}
if (a != NULL) {
ty->tstruc->alignment = a->iarg;
} else if (ty->tstruc->scope->slist->next == NULL) {
/* Only one member */
ty->tstruc->alignment = backend->
get_align_type(
ty->tstruc->scope->slist->dec->dtype);
} else {
ty->tstruc->alignment =
get_union_align(ty);
}
ret = ty->tstruc->alignment;
if (a == NULL) {
X86IFY(ret);
}
return ret;
} else if (ty->code == TY_LDOUBLE) {
if (backend->arch == ARCH_AMD64) {
return 16;
} else if (backend->arch == ARCH_X86) {
return 4;
}
}
}
ret = backend->get_sizeof_type(ty, NULL);
/* XXX */
X86IFY(ret);
return ret;
} else {
struct type tmp = *ty;
tmp.tlist = tmp.tlist->next;
return get_align_type(&tmp);
}
}
unsigned long
calc_offsets(struct vreg *vr) {
size_t ret = 0;
do {
if (vr->parent->type->code == TY_STRUCT) {
ret += vr->memberdecl->offset;
}
if (vr->from_ptr) {
break;
}
vr = vr->parent;
} while (vr->parent != NULL);
return ret;
}
void
as_align_for_type(FILE *out, struct type *ty) {
unsigned long align;
unsigned long alignbits;
align = backend->get_align_type(ty);
if (backend->arch == ARCH_SPARC) {
alignbits = align;
} else {
alignbits = 0;
while (align >>= 1) {
++alignbits;
}
}
/* Make low-order bits of location counter zero */
x_fprintf(out, "\t.align %lu\n", alignbits);
}
void
as_print_string_init(FILE *o, size_t howmany, struct ty_string *str) {
char *p;
size_t i;
x_fprintf(o, ".byte\t");
for (i = 0, p = str->str; i < str->size-1; ++p, ++i) {
x_fprintf(o, "0x%x", (unsigned char)*p);
if (i+1 < str->size-1) {
if (i > 0 && (i % 10) == 0) {
x_fprintf(o, "\n.byte\t");
} else {
x_fputc(',', o);
}
}
}
if (howmany >= str->size) {
if (str->size > 1) {
(void) fprintf(o, ", ");
}
(void) fprintf(o, "0");
}
x_fputc('\n', o);
}
struct reg *
generic_alloc_gpr(
struct function *f,
int size,
struct icode_list *il,
struct reg *dontwipe,
struct reg *regset,
int nregs,
int *csave_map,
int line) {
int i;
int save = 0;
int least_idx = -1;
int regno;
static int last_alloc;
struct reg *ret = NULL;
(void) size;
(void) line;
(void) dontwipe;
(void) f;
for (i = 0; i < nregs; ++i) {
if (reg_unused(®set[i])
&& reg_allocatable(®set[i])) {
ret = &/*mips_gprs*/regset[i];
last_alloc = i;
break;
} else {
if (!optimizing /* || !reg_allocatable(...)*/) {
continue;
}
}
}
if (ret == NULL) {
/*
* Save and hand out register with least
* references
*/
save = 1;
if (!optimizing) {
static int cur;
int iterations = 0;
if (cur == last_alloc) {
/*
* Ensure two successive allocs always
* use different registers
*/
cur = (cur + 1) % nregs;
}
do {
if (cur == nregs) cur = 0;
ret = ®set[cur++];
if (++iterations == nregs) {
/*
* Ouch, no register can be allocated.
* This will probably only ever happen
* with inline asm statements using too
* many registers .... HOPEFULLY!!
*/
return NULL;
}
/*
* 10/18/07: Wow, the check below didn't
* use reg_allocatable(). Thus the AMD64
* sub-registers for r8-r15 were not
* considered when determining
* allocatability
*/
} while ((dontwipe != NULL && ret == dontwipe)
/*|| !ret->allocatable*/
|| !reg_allocatable(ret));
last_alloc = cur - 1;
} else {
int idx;
idx = least_idx == -1? 0: least_idx;
if (idx == last_alloc) {
idx = (idx + 1) % nregs;
}
ret = &/*mips_gprs*/regset[idx];
last_alloc = idx;
}
}
regno = ret - /*mips_gprs*/regset;
if (csave_map != NULL) {
f->callee_save_used |= csave_map[regno] << regno;
}
if (save) {
struct reg *top_reg = NULL;
/*
* IMPORTANT: It is assumed that an allocatable register
* has a vreg, hence no ret->vreg != NULL check here.
* Reusing a preg without a vreg is obviously a bug
* because without a vreg, it cannot be saved anywhere.
* See reg_set_unallocatable()/vreg_faultin_protected()
*
* 10/30/07: This didn't work for AMD64 sub-registers.
* Example: We are allocating r10, but only r10d is used.
* In that case we can't insist on ret being mapped to a
* vreg. That's a bug. Another thing is that smaller-
* than-GPR size requests weren't honored, which is not
* used on AMD64 anyway, but could be used at some point.
* This is also implemented (but untested) now.
*
* XXX This currently only works with one sub-register
* per register (e.g. ah/al for ax wouldn't work)
*/
if (!ret->used) {
/*
* Find register to free (there must be one because
* the ``save'' flag is set)
*/
top_reg = ret;
do {
ret = ret->composed_of[0];
} while (!ret->used);
}
if (ret->vreg->from_const == NULL
&& ret->vreg->var_backed == NULL
&& ret->vreg->from_ptr == NULL
&& ret->vreg->parent == NULL) {
/* Anonymous register - must be saved */
free_preg(ret, il, 1, 1);
}
ret->vreg = NULL;
if (size == 0) {
/* Request to allocate top register */
if (top_reg != NULL) {
/* Change ret back to top */
ret = top_reg;
}
} else if (ret->size != (unsigned)size) {
/*
* Freed sub-register does not match
* the desired size
*/
if (top_reg != NULL) {
ret = top_reg;
}
/*
* Check for composed_of != NULL because some
* callers supply size info even when there is
* no need to distinguish between sizes. E.g.
* FPRs are usually 8 bytes and have no sub
* registers, but generic_alloc_gpr() may be
* called with a 4 byte size argument for a
* float
*/
while (ret->composed_of && ret->size != (unsigned)size) {
ret = ret->composed_of[0];
}
}
}
ret->used = ret->allocatable = 1;
if (ret == NULL) {
debug_log_regstuff(ret, NULL, DEBUG_LOG_FAILEDALLOC);
} else {
debug_log_regstuff(ret, NULL, DEBUG_LOG_ALLOCGPR);
}
#ifdef DEBUG6
if (ret != NULL) {
ret->line = line;
++ret->nallocs;
}
#endif
return ret;
}
/*
* This function is used to tell us whether a tlist really does include
* a VLA component. This is sometimes necessary to know when we modify
* typelists
*/
int
vla_type_has_constant_size(struct type_node *tn) {
for (; tn != NULL; tn = tn->next) {
if (tn->type == TN_VARARRAY_OF) {
/* Not constant */
return 0;
}
}
/* Constant! */
return 1;
}
int
is_immediate_vla_type(struct type *ty) {
if (!ty->is_vla) {
return 0;
}
if (ty->tlist == NULL) {
return 0;
}
if (ty->tlist->type == TN_POINTER_TO) {
return 0;
}
/* XXX */
return 1;
}
size_t
get_sizeof_type(struct type *t, struct token *tok) {
if (t->is_vla && !vla_type_has_constant_size(t->tlist)) {
puts("BUG: get_sizeof_type() applied to VLA, should use "
"get_sizeof_vla_type() instead!!!!");
abort();
}
if (t->tlist != NULL) {
/*
* May not be called with function argument,
* so this has to be a pointer or an array
*/
if (t->tlist->type == TN_ARRAY_OF) {
size_t elem_size;
struct type_node *tmp;
if (t->tlist->arrarg
&& t->tlist->arrarg->const_value == NULL) {
/*
* An unspecified array size is ok if this
* function was called internally because
* then it came from vreg_alloc() or the
* likes. This is to permit something like
* extern struct foo bar[];
* bar;
* XXX probably should never be called in
* contexts where the above is ok
*/
if (tok != NULL) {
/* Not called internally */
errorfl(tok,
"Cannot take size of incomplete type");
}
return 0;
}
if (t->tlist->arrarg_const == 0) {
t->tlist->arrarg_const =
cross_to_host_size_t(
t->tlist->arrarg->const_value);
}
tmp = t->tlist;
t->tlist = t->tlist->next;
elem_size = backend->get_sizeof_type(t, tok);
t->tlist = tmp;
return t->tlist->arrarg_const * elem_size;
} else if (t->tlist->type == TN_POINTER_TO) {
return backend->get_ptr_size();
} else {
/* TN_FUNCTION */
return backend->get_ptr_size(); /* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */
if (tok != NULL) {
/* User used ``sizeof'' explicitly */
errorfl(tok,
"`sizeof' operator cannot be applied"
" to functions");
} else {
/* Function was called internally */
puts("BUG: sizeof() applied to function");
abort();
}
return 0;
}
return backend->get_ptr_size();
}
if (t->code == TY_STRUCT) {
return t->tstruc->size;
} else if (t->code == TY_UNION) {
return t->tstruc->size;
} else {
/* return backend->get_sizeof_basic(t->code);*/
return cross_get_sizeof_type(t);
}
}
/*
* 07/24/07: NEW! A sizeof for VLAs. This generates code to fetch
* the hidden VLA sizes at runtimes, and to multiply them. Consequently
* it returns a vreg with the result
*/
struct vreg *
get_sizeof_vla_type(struct type *ty, struct icode_list *il) {
size_t ulong_size = backend->get_sizeof_type(
make_basic_type(TY_ULONG), NULL);
int base_size;
struct reg *res_reg;
struct type_node *tn;
struct type_node *saved_tlist;
struct token *tok;
struct vreg *ret;
struct stack_block *block_addr = ty->vla_addr;
res_reg = ALLOC_GPR(curfunc, ulong_size, il, NULL);
if (ty->tlist->type == TN_POINTER_TO) {
/* Not much to be done */
base_size = backend->get_ptr_size();
} else {
/*
* Get base type size, e.g. sizeot(int), etc., if this is a
* (possibly multi-dimensional) array.
* ATTENTION: If this is an array of pointers, the base
* type size is sizeof(ptr) instead of the genuine base
* type size
*/
base_size = 0;
for (tn = ty->tlist; tn != NULL; tn = tn->next) {
if (tn->type == TN_POINTER_TO) {
/* Yes, base is pointer */
base_size = backend->get_ptr_size();
break;
}
}
if (base_size == 0) {
/* This is an array of non-pointer types */
saved_tlist = ty->tlist;
ty->tlist = NULL;
ty->is_vla = 0;
base_size = backend->get_sizeof_type(ty, NULL);
ty->is_vla = 1;
ty->tlist = saved_tlist;
}
}
tok = const_from_value(&base_size, make_basic_type(TY_INT));
ret = vreg_alloc(NULL, tok, NULL, make_basic_type(TY_INT));
/*
* Anonymify base size and make it a size_t
* XXX const_from_value should be fixed instead!
*/
ret = backend->icode_make_cast(ret,
backend->get_size_t(), il);
if (ty->tlist->type != TN_POINTER_TO) {
/*
* Must be an array of VLAs, or of pointers to VLAs,
* or a plain VLA. We don't attempt to fold constant
* dimensions yet. Instead we just calculate it all
* dynamically. (XXX)
*/
struct vreg *factor_vreg;
struct token *factor_tok;
int factor_size;
int vla_idx = 0;
struct icode_instr *ii;
for (tn = ty->tlist; tn != NULL; tn = tn->next) {
if (tn->type == TN_POINTER_TO) {
/*
* This already went into the ``base size''
* of this array - nothing left to do
*/
break;
} else if (tn->type == TN_ARRAY_OF) {
if (tn->arrarg_const == 0) {
tn->arrarg_const =
cross_to_host_size_t(
tn->arrarg->const_value);
}
factor_size = tn->arrarg_const;
factor_tok = const_from_value(&factor_size,
make_basic_type(TY_INT));
factor_vreg = vreg_alloc(NULL, factor_tok,
NULL, make_basic_type(TY_INT));
reg_set_unallocatable(ret->pregs[0]);
factor_vreg = backend->icode_make_cast(
factor_vreg,
make_basic_type(TY_ULONG),
il);
reg_set_allocatable(ret->pregs[0]);
} else {
/* Must be VLA */
struct reg *dimsize;
reg_set_unallocatable(ret->pregs[0]);
dimsize = ALLOC_GPR(curfunc, ulong_size, il, 0);
reg_set_allocatable(ret->pregs[0]);
factor_vreg = icode_make_retr_vla_size(
dimsize, block_addr, vla_idx, il);
++vla_idx;
}
backend->icode_prepare_op(&ret, &factor_vreg,
TOK_OP_MULTI, il);
ii = icode_make_mul(ret, factor_vreg);
append_icode_list(il, ii);
}
}
return ret;
}
/* XXX platform-independent?!?! used by amd64 */
void
store_preg_to_var(struct decl *d, size_t size, struct reg *r) {
static struct vreg vr;
vr.type = d->dtype;
vr.size = size;
vr.var_backed = d;
vreg_map_preg(&vr, r);
emit->store(&vr, &vr);
r->used = 0;
}
/* XXX generic? */
void
put_arg_into_reg(
struct reg *regset,
int *index, int startat,
struct vreg *vr,
struct icode_list *il) {
struct reg *r;
struct reg *r2 = NULL;
r = ®set[startat + *index];
if (vr->is_multi_reg_obj) {
r2 = ®set[startat + *index + 1];
}
if (vr->pregs[0] == NULL
|| vr->pregs[0] != r
|| r->vreg != vr
|| r2) {
free_preg(r, il, 1, 1);
if (r2) free_preg(r2, il, 1, 1);
vreg_faultin(r, r2, vr, il, 0);
} else {
vreg_map_preg(vr, r);
}
reg_set_unallocatable(r);
if (r2) {
reg_set_unallocatable(r2);
++*index;
}
++*index;
}
/*
* XXX the alignment stuff is totally botched.. it should be done in
* this routine and not in print_init_expR()..
* also, this could probably be unified with gas/nasm print_init_list()
*/
void
generic_print_init_list(FILE *out, struct decl *dec, struct initializer *init,
void (*print_init_expr)(struct type *, struct expr *)) {
struct sym_entry *se = NULL;
int is_struct = 0;
if (dec
&& (dec->dtype->code == TY_STRUCT
|| dec->dtype->code == TY_UNION)
&& dec->dtype->tlist == NULL) {
se = dec->dtype->tstruc->scope->slist;
is_struct = 1;
}
for (; init != NULL; init = init->next) {
if (init->type == INIT_NESTED) {
struct decl *nested_dec = NULL;
struct type_node *saved_tlist = NULL;
if (se == NULL) {
/*
* May be an array of structs, in
* which case the struct declaration
* is needed for alignment
*/
if (dec && dec->dtype->code == TY_STRUCT) {
nested_dec = alloc_decl();
nested_dec->dtype = dec->dtype;
saved_tlist = dec->dtype->tlist;
dec->dtype->tlist = NULL;
}
} else {
nested_dec = se->dec;
}
generic_print_init_list(out, nested_dec, init->data,
print_init_expr);
if (saved_tlist != NULL) {
dec->dtype->tlist = saved_tlist;
free(nested_dec);
}
} else if (init->type == INIT_EXPR) {
struct expr *ex;
ex = init->data;
print_init_expr(ex->const_value->type, ex);
} else if (init->type == INIT_NULL) {
x_fprintf(out, "\t.%s %lu\n",
backend->arch == ARCH_SPARC? "skip": "space",
(unsigned long)*(size_t *)init->data);
for (; se != NULL && se->next != NULL; se = se->next) {
;
}
}
if (se != NULL) {
se = se->next;
}
}
if (is_struct) {
int align = backend->get_align_type(dec->dtype);
if (backend->arch != ARCH_SPARC) {
if (align == 2) align = 1;
else if (align == 4) align = 2;
else if (align == 8) align = 3;
}
/* XXX or use .space?! */
x_fprintf(out, "\t.align %d\n", align);
}
}
/*
* This function relocates structure pointer and size values to
* different registers if necessary, in preparation for a structure
* assignment. The point is that on most architectures, like MIPS
* and PPC, the memcpy() arguments go into GPRs, and we have to
* ensure that moving one of these values does not trash another
* value because it is resident in the destination GPR
*/
void
relocate_struct_regs(struct copystruct *cs,
struct reg *r0, struct reg *r1, struct reg *r2,
struct icode_list *il) {
struct reg *curregs[4];
struct reg *tmp;
int i;
curregs[0] = cs->src_from_ptr;
curregs[1] = cs->dest_from_ptr;
curregs[2] = cs->src_from_ptr_struct;
curregs[3] = cs->dest_from_ptr_struct;
reg_set_unallocatable(r0);
reg_set_unallocatable(r1);
reg_set_unallocatable(r2);
/*
* 11/01/07: This was missing
*/
for (i = 0; i < 4; ++i) {
if (curregs[i] != NULL) {
reg_set_unallocatable(curregs[i]);
}
}
for (i = 0; i < 4; ++i) {
/*
* 11/01/07: This was missing
*/
if (curregs[i] == NULL) {
continue;
}
if (curregs[i] == r0
|| curregs[i] == r1
|| curregs[i] == r2) {
/* Move elsewhere */
tmp = ALLOC_GPR(curfunc, curregs[i]->size, il, NULL);
icode_make_copyreg(tmp, curregs[i],
curregs[i]->vreg->type, /* XXX ok? */
curregs[i]->vreg->type, /* XXX ok? */
il);
/*
* 11/01/07: This was missing!!!!!!!! The
* registers were relocated, but the register
* information was not updated. Terrible!
*/
switch (i) {
case 0:
cs->src_from_ptr = tmp;
break;
case 1:
cs->dest_from_ptr = tmp;
break;
case 2:
cs->src_from_ptr_struct = tmp;
break;
case 3:
cs->dest_from_ptr_struct = tmp;
break;
}
}
}
free_preg(r0, il, 0, 0);
free_preg(r1, il, 0, 0);
free_preg(r2, il, 0, 0);
for (i = 0; i < 4; ++i) {
if (curregs[i] != NULL) {
free_preg(curregs[i], il, 0, 0);
}
}
}
/*
* This function copies the structure pointed to by dec->stack_addr->
* from_reg to the stack block designated by dec->stack_addr, by
* calling emit->copystruct().
* The purpose is just to set up the data structures required by
* copystruct()
*/
void
copy_struct_regstack(struct decl *dec) {
static struct copystruct cs;
static struct vreg src_vreg;
static struct vreg dest_vreg;
src_vreg.type = dest_vreg.type = dec->dtype;
src_vreg.size = backend->get_sizeof_type(dec->dtype, NULL);
dest_vreg.size = src_vreg.size;
dest_vreg.var_backed = dec;
cs.src_from_ptr = dec->stack_addr->from_reg;
cs.src_vreg = &src_vreg;
cs.dest_vreg = &dest_vreg;
emit->copystruct(&cs);
}
/*
* This is a new attempt at generic_print_init_list(), which uses gas/UNIX
* as style syntax, but performs alignment in the same way as the x86 one
*/
void
new_generic_print_init_list(FILE *out, struct decl *dec, struct initializer *init,
void (*print_init_expr)(struct type *, struct expr *)) {
struct sym_entry *se = NULL;
struct sym_entry *startse = NULL;
if (dec
&& (dec->dtype->code == TY_STRUCT
|| dec->dtype->code == TY_UNION)
&& dec->dtype->tlist == NULL) {
se = dec->dtype->tstruc->scope->slist;
}
for (; init != NULL; init = init->next) {
if (init->type == INIT_NESTED) {
struct decl *nested_dec = NULL;
struct type_node *saved_tlist = NULL;
if (se == NULL) {
/*
* May be an array of structs, in
* which case the struct declaration
* is needed for alignment
*/
if (dec && dec->dtype->code == TY_STRUCT) {
nested_dec = alloc_decl();
nested_dec->dtype = dec->dtype;
saved_tlist = dec->dtype->tlist;
dec->dtype->tlist = NULL;
}
} else {
nested_dec = se->dec;
}
new_generic_print_init_list(out, nested_dec, init->data,
print_init_expr);
if (saved_tlist != NULL) {
dec->dtype->tlist = saved_tlist;
free(nested_dec);
}
} else if (init->type == INIT_EXPR) {
struct expr *ex;
ex = init->data;
print_init_expr(ex->const_value->type, ex);
} else if (init->type == INIT_NULL) {
x_fprintf(out, "\t.%s %lu\n",
backend->arch == ARCH_SPARC? "skip": "space",
(unsigned long)*(size_t *)init->data);
startse = se;
for (; se != NULL && se->next != NULL; se = se->next) {
;
}
}
if (se != NULL) {
/* May need alignment */
struct decl *d = NULL;
struct type *ty = NULL;
size_t nbytes;
if (se->next != NULL) {
/* We may have to align for the next member */
d = se->next->dec;
ty = d->dtype;
} else if (dec->dtype->tstruc->scope->slist->next) {
/*
* We've reached the end of the struct and
* may have to pad the struct, such that if
* we have an array of structs, every element
* is properly aligned.
*
* Note that we have to use the whole struct
* alignment, not just first member alignment
*/
ty = dec->dtype;
if (init->type == INIT_NULL) {
/*
* 08/08/07: Same fix as in x86 struct
* init functions
*/
size_t curoff = startse->dec->offset +
*(size_t *)init->data;
size_t alignto = backend->get_align_type(ty);
size_t tmp = 0;
while ((curoff + tmp) % alignto) {
++tmp;
}
if (tmp > 0) {
x_fprintf(out, "\t.%s %lu\n",
backend->arch == ARCH_SPARC?
"skip": "space",
tmp);
}
} else {
d = dec->dtype->tstruc->scope->slist->dec;
}
}
if (d != NULL) {
nbytes = calc_align_bytes(se->dec->offset,
se->dec->dtype, ty);
if (nbytes) {
x_fprintf(out, "\t.%s %lu\n",
backend->arch == ARCH_SPARC?
"skip": "space",
nbytes);
}
}
se = se->next;
}
}
}
size_t
generic_print_init_var(FILE *out, struct decl *d, size_t segoff,
void (*print_init_expr)(struct type *, struct expr *),
int skip_is_space) {
struct type *dt = d->dtype;
size_t size;
size_t ret = 0;
if (DECL_UNUSED(d)) {
return 0;
}
/* Constant initializer expression */
x_fprintf(out, "%s:\n", dt->name);
new_generic_print_init_list(out, d, d->init, print_init_expr);
ret = size = backend->get_sizeof_decl(d, NULL);
if (d->next != NULL) {
unsigned long align;
struct decl *tmpd;
/*
* Now we have to check which of the next variables
* is actually used. Because if it's not used, it's
* not printed, and then we'd get wrong alignment
*/
for (tmpd = d->next; tmpd != NULL; tmpd = tmpd->next) {
if (!DECL_UNUSED(tmpd)) {
break;
}
}
if (tmpd != NULL) {
align = calc_align_bytes(segoff,
d->dtype, tmpd->dtype);
if (align) {
/*
* XXX is this really needed?!?! doesn't the
* SPARC assembler have "space" or why was
* skip usedh ere???
*/
x_fprintf(out, "\t.%s %lu\n",
skip_is_space? "space": "skip", align);
ret += align;
}
}
}
return ret;
}
int
generic_same_representation(struct type *dest, struct type *src) {
size_t dest_size = backend->get_sizeof_type(dest, NULL);
size_t src_size = backend->get_sizeof_type(src, NULL);
if ((is_integral_type(dest)
|| dest->tlist != NULL)
&& (is_integral_type(src)
|| src->tlist != NULL)
&& dest_size == src_size) {
return 1;
} else {
return 0;
}
}
void
store_reg_to_stack_block(struct reg *r, struct stack_block *sb) {
static struct vreg vr;
vr.type = make_void_ptr_type();
vr.size = backend->get_sizeof_type(vr.type, NULL);
vr.stack_addr = sb;
vreg_map_preg(&vr, r);
emit->store(&vr, &vr);
}
unsigned long
align_for_cur_auto_var(struct type *ty, unsigned long curoff) {
unsigned long align = backend->get_align_type(ty);
unsigned long origoff = curoff;
while (curoff % align) {
++curoff;
}
return curoff - origoff;
}
syntax highlighted by Code2HTML, v. 0.9.1