/* * 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 #include #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; }