/* * 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. * * Emit gas code from intermediate amd64 code */ #include "amd64_emit_gas.h" #include #include #include #include #include #include "scope.h" #include "type.h" #include "decl.h" #include "icode.h" #include "subexpr.h" #include "token.h" #include "functions.h" #include "symlist.h" #include "dwarf.h" #include "cc1_main.h" #include "x86_gen.h" #include "typemap.h" #include "expr.h" #include "x86_emit_gas.h" #include "inlineasm.h" #include "error.h" #include "n_libc.h" static FILE *out; static int init(FILE *fd, struct scope *s) { (void) s; out = fd; return 0; } static void print_mem_operand(struct vreg *vr, struct token *constant); extern int size_to_gaspostfix(size_t size, struct type *type); static void emit_extern_decls(void) { if (x86_emit_gas.extern_decls != NULL) { x86_emit_gas.extern_decls(); } } static void emit_static_decls(void) { x86_emit_gas.static_decls(); } static void emit_struct_inits(void) { x86_emit_gas.struct_inits(); } /* * XXX Misleading name ;o */ static void emit_strings(void) { x86_emit_gas.strings(); } static void emit_comment(const char *fmt, ...) { int rc; va_list va; va_start(va, fmt); x_fprintf(out, "# "); rc = vfprintf(out, fmt, va); va_end(va); x_fputc('\n', out); if (rc == EOF || fflush(out) == EOF) { perror("vfprintf"); exit(EXIT_FAILURE); } } static void emit_dwarf2_line(struct token *tok) { x_fprintf(out, "\t[loc %d %d 0]\n", tok->fileid, tok->line); } static void emit_dwarf2_files(void) { struct dwarf_in_file *inf; x_fprintf(out, "[file \"%s\"]\n", input_file); for (inf = dwarf_files; inf != NULL; inf = inf->next) { x_fprintf(out, "[file %d \"%s\"]\n", inf->id, inf->name); } } static void emit_inlineasm(struct inline_asm_stmt *stmt) { x_fprintf(out, "#APP\n"); /* * There may be an empty body for statements where only the side * effect is desired; * __asm__("" ::: "memory"); */ if (stmt->code != NULL) { inline_instr_to_gas(out, stmt->code); } x_fprintf(out, "#NO_APP\n"); } static void emit_unimpl(void) { unimpl(); } static void emit_empty(void) { x_fputc('\n', out); } static void emit_label(const char *name, int is_func) { if (is_func) { x_fprintf(out, "%s:\n", name); } else { x_fprintf(out, ".%s:\n", name); } } static void emit_call(const char *name) { x_fprintf(out, "\tcall %s\n", name); } static void emit_callindir(struct reg *r) { x_fprintf(out, "\tcall *%%%s\n", r->name); } static void emit_func_intro(struct function *f) { (void) f; x_fprintf(out, "\tpush %%rbp\n"); /* XXX */ x_fprintf(out, "\tmov %%rsp, %%rbp\n"); } static void emit_func_outro(void) { x_fprintf(out, "\tret\n"); } static void emit_define(const char *name, const char *fmt, ...) { x86_emit_gas.define(name, fmt); } static void emit_push(struct function *f, struct icode_instr *ii) { struct vreg *vr = ii->src_vreg; struct reg *r = ii->src_pregs? (void *)ii->src_pregs[0]: (void *)NULL; int ch; if (vr->type->is_vla) { ch = 'q'; } else { if (backend->get_sizeof_type(vr->type, NULL) == 8) { ch = 'q'; } else { ch = 'l'; } } if (ii->src_pregs) { x_fprintf(out, "\tpush %%%s\n", r->name); } else if (vr->var_backed) { struct decl *d = vr->var_backed; if (vr->parent != NULL) { /* Structure or union member */ struct decl *d2/* = d*/; struct vreg *vr2; vr2 = get_parent_struct(vr); d2 = vr2->var_backed; if (vr2->from_ptr) { if (vr->parent->type->code == TY_STRUCT) { x_fprintf(out, "\tpush%c %lu(%%%s)\n", ch, calc_offsets(vr2), vr2->from_ptr->pregs[0]->name); } else { x_fprintf(out, "\tpush%c (%%%s)\n", ch, vr2->from_ptr->pregs[0]->name); } } else if (d2 && d2->stack_addr != NULL) { long soff = 0; if (vr->parent->type->code == TY_STRUCT) { soff = calc_offsets(vr2); } x_fprintf(out, "\tpush%c %ld(%%rbp)\n", ch, -d2->stack_addr->offset+soff); } else if (d2 != NULL) { /* Must be static */ x_fprintf(out, "\tpush%c %s", ch, d2->dtype->name); if (vr->parent->type->code == TY_STRUCT) { x_fprintf(out, "+%lu", calc_offsets(vr2)); } x_fputc('\n', out); } else { unimpl(); } } else { if (d->stack_addr != NULL) { /* Stack */ x_fprintf(out, "\tpush%c -%ld(%%rbp)\n", ch, d->stack_addr->offset); } else { /* Static or register variable */ if (d->dtype->storage == TOK_KEY_REGISTER) { unimpl(); } else { x_fprintf(out, "\tpush%c %s\n", ch, d->dtype->name); } } } } else if (vr->from_const) { struct token *t = vr->from_const; if (t->type == TOK_STRING_LITERAL) { struct ty_string *s = t->data; x_fprintf(out, "\tpushq $._Str%lu\n", s->count); } else if (IS_INT(t->type)) { /* * There are only forms of ``int'' and ``long'' * constants */ #if 0 if (t->type == TY_INT) { x_fprintf(out, "\tpushl $%d\n", *(int *) t->data); } else { /* UINT */ x_fprintf(out, "\tpushl $%u\n", *(unsigned *) t->data); } #endif x_fprintf(out, "\tpushl $"); cross_print_value_by_type(out, t->data, t->type, 0); } else if (IS_LONG(t->type) || IS_LLONG(t->type)) { #if 0 if (t->type == TY_LONG || t->type == TY_LLONG) { x_fprintf(out, "\tpushq $%ld\n", *(long *) t->data); } else { /* unsigned */ x_fprintf(out, "\tpushq $%lu\n", *(unsigned long *) t->data); } #endif x_fprintf(out, "\tpushq $"); cross_print_value_by_type(out, t->data, t->type, 0); } else { puts("BUG in NASM emit_push()"); exit(EXIT_FAILURE); } } else if (vr->from_ptr) { x_fprintf(out, "\tpush%c (%%%s)\n", /*ascii_type,*/ ch, vr->from_ptr->pregs[0]->name); } else { unimpl(); } f->total_allocated += 4; } static void emit_allocstack(struct function *f, size_t nbytes) { (void) f; x_fprintf(out, "\tsub $%lu, %%rsp\n", (unsigned long)nbytes); } static void emit_freestack(struct function *f, size_t *nbytes) { if (nbytes == NULL) { /* Procedure outro */ if (f->total_allocated != 0) { x_fprintf(out, "\tadd $%lu, %%rsp\n", (unsigned long)f->total_allocated); } x_fprintf(out, "\tpop %%rbp\n"); } else { if (*nbytes != 0) { x_fprintf(out, "\tadd $%lu, %%rsp\n", (unsigned long)*nbytes); f->total_allocated -= *nbytes; } } } static void emit_adj_allocated(struct function *f, int *nbytes) { f->total_allocated += *nbytes; } static void emit_struct_defs(void) { x86_emit_gas.struct_defs(); } static void emit_setsection(int value) { x86_emit_gas.setsection(value); } static void emit_alloc(size_t nbytes) { unimpl(); (void) nbytes; } static void print_mem_or_reg(struct reg *r, struct vreg *vr) { if (vr->on_var) { struct decl *d = vr->var_backed; if (d->stack_addr) { x_fprintf(out, "-%ld(%%rbp)", d->stack_addr->offset); } else { x_fprintf(out, "%s", d->dtype->name); } } else { x_fprintf(out, "%%%s", r->name); } } static void emit_inc(struct icode_instr *ii) { x86_emit_gas.inc(ii); } static void emit_dec(struct icode_instr *ii) { x86_emit_gas.dec(ii); } static void emit_load(struct reg *r, struct vreg *vr) { char *p; int needsize = 1; if (r->type == REG_FPR) { if (STUPID_X87(r)) { if (!IS_FLOATING(vr->type->code)) { p = "fild"; } else { p = "fld"; } x_fprintf(out, "\t%s%c ", p, size_to_gaspostfix(vr->size, vr->type)); } else { /* SSE */ if (!IS_FLOATING(vr->type->code)) { p = "cvtsi2ss"; } else { if (vr->type->code == TY_FLOAT) { p = "movss"; } else { /* Must be double */ p = "movsd"; } } needsize = 0; x_fprintf(out, "\t%s ", p); } } else { if (vr->stack_addr != NULL) { p = "mov"; } else if (vr->type && vr->type->tlist != NULL && vr->type->tlist->type == TN_ARRAY_OF) { if (vr->from_const == NULL) { p = "lea"; } else { p = "mov"; } needsize = 0; } else { if (r->size == vr->size || vr->size == 8) { /* == 8 for long long, SA for anonymous */ p = "mov"; } else { if (vr->type == NULL || vr->type->sign == TOK_KEY_UNSIGNED) { /* XXX 32->64bit? ... */ p = "movzx"; } else { if (r->size == 8 && vr->size == 4) { if (vr->from_const) { needsize = 0; p = "mov"; } else { p = "movslq"; } } else { p = "movsx"; } } } } x_fprintf(out, "\t%s ", p); } print_mem_operand(vr, NULL); if (r->type != REG_FPR || !STUPID_X87(r)) { x_fprintf(out, ", %%%s\n", r->name); } x_fputc('\n', out); } /* * Takes vreg source arg - not preg - so that it can be either a preg * or immediate (where that makes sense!) */ static void emit_store(struct vreg *dest, struct vreg *src) { char *p = NULL; int floating = 0; int zero_first = 0; int post = 0; if (dest->size) { post = size_to_gaspostfix(dest->size, dest->type); } if (dest == &floatbuf) { /* * XXX this is a botch to ignore the problem of the messed * up floatbuf typing for now. This makes code compile, but * not necessarily work right :-) */ if (src->pregs[0]->size == 4 && post == 'q') { post = 'l'; } } if (src->pregs[0] && src->pregs[0]->type == REG_FPR) { if (STUPID_X87(src->pregs[0])) { if (!IS_FLOATING(dest->type->code)) { p = "fistp"; } else { p = "fstp"; } floating = 1; } else { if (!IS_FLOATING(dest->type->code)) { unimpl(); } else { if (dest->type->code == TY_FLOAT) { p = "movss"; } else { p = "movsd"; } } post = 0; floating = 0; /* because SSE is non-x87-like */ } } else { if (dest->stack_addr != NULL || dest->from_const != NULL) { p = "mov"; } else { if (src->pregs[0] && dest->size < src->pregs[0]->size) { if (src->type == NULL || src->type->sign == TOK_KEY_UNSIGNED) { /* XXX wow this should never be executed? */ zero_first = 1; x_fprintf(out, "\tmov%c ", post?post:' '); x_fprintf(out, "$0, "); print_mem_operand(dest, NULL); p = "mov"; } else { if (dest->from_ptr && src->pregs[0]->size == 4) { /* * ``movslq %eax, (%r9)'' * doesn't work :S */ src->pregs[0] = find_top_reg( src->pregs[0]); x_fprintf(out, "\tmovslq %%%s, %%%s\n", src->pregs[0]->composed_of[0]->name, src->pregs[0]->name); p = "mov"; } else { if (src->pregs[0]->size == 4) { p = "movslq"; } else { p = "movsx"; } } } } else { p = "mov"; } } } if (post) { x_fprintf(out, "\t%s%c ", p, post); } else { x_fprintf(out, "\t%s ", p); } if (floating) { /* Already done - floating stores only ever come from st0 */ print_mem_operand(dest, NULL); x_fputc('\n', out); return; } if (src->from_const) { print_mem_operand(src, NULL); } else { /* Must be register */ x_fprintf(out, "%%%s, ", src->pregs[0]->name); } print_mem_operand(dest, NULL); x_fputc('\n', out); } static void emit_neg(struct reg **dest, struct icode_instr *src) { x86_emit_gas.neg(dest, src); } /* XXX 64bit */ static void emit_sub(struct reg **dest, struct icode_instr *src) { if (dest[0]->type == REG_FPR) { if (STUPID_X87(dest[0])) { x_fprintf(out, "\tfsubp "); } else { x_fprintf(out, "\tsubs%c ", src->src_vreg->type->code == TY_FLOAT? 's': 'd'); } } else { if (src->dest_vreg->type->tlist) { /* ptr arit ... sub rax, edx doesn't work */ if (src->src_vreg->size == 4) { /* XXX frontend stuff?! */ struct reg *r; r = find_top_reg(src->src_pregs[0]); if (src->src_vreg->type->sign != TOK_KEY_UNSIGNED) { x_fprintf(out, "\tmovslq %%%s, %%%s\n", src->src_pregs[0]->name, r->name); } x_fprintf(out, "\tsub %%%s, %%%s\n", r->name, dest[0]->name); return; } } x_fprintf(out, "\tsub "); } print_mem_or_reg(src->src_pregs[0], src->src_vreg); x_fprintf(out, ", %%%s\n", dest[0]->name); } /* XXX 64bit */ static void emit_add(struct reg **dest, struct icode_instr *src) { if (dest[0]->type == REG_FPR) { if (STUPID_X87(dest[0])) { x_fprintf(out, "\tfaddp "); } else { x_fprintf(out, "\tadds%c ", src->src_vreg->type->code == TY_FLOAT? 's': 'd'); } } else { if (src->dest_vreg->type->tlist) { /* ptr arit ... add rax, edx doesn't work */ if (src->src_vreg->size == 4) { /* XXX frontend stuff?! */ struct reg *r; r = find_top_reg(src->src_pregs[0]); if (src->src_vreg->type->sign != TOK_KEY_UNSIGNED) { x_fprintf(out, "\tmovslq %%%s, %%%s\n", src->src_pregs[0]->name, r->name); } x_fprintf(out, "\tadd %%%s, %%%s\n", r->name, dest[0]->name); return; } } x_fprintf(out, "\tadd "); } print_mem_or_reg(src->src_pregs[0], src->src_vreg); x_fprintf(out, ", %%%s\n", dest[0]->name); } /* XXX 64bit */ static void emit_div(struct reg **dest, struct icode_instr *src, int formod) { struct type *ty = src->src_vreg->type; int is_x87 = 0; (void) dest; (void) formod; if (!IS_FLOATING(ty->code)) { int is_64bit = IS_LONG(ty->code) || IS_LLONG(ty->code); if (ty->sign != TOK_KEY_UNSIGNED) { if (is_64bit) { x_fprintf(out, "\txor %%rdx, %%rdx\n"); } else { /* sign-extend eax to edx:eax */ x_fprintf(out, "\tcltd\n"); } x_fprintf(out, "\tidiv "); } else { if (is_64bit) { x_fprintf(out, "\txor %%rdx, %%rdx\n"); } else { x_fprintf(out, "\txor %%edx, %%edx\n"); } x_fprintf(out, "\tdiv "); } } else { if (STUPID_X87(src->src_pregs[0])) { /* XXX why is the fxch needed???? */ x_fprintf(out, "\tfxch %%st(1)\n"); x_fprintf(out, "\tfdivp "); is_x87 = 1; } else { x_fprintf(out, "\tdivs%c %%%s, %%%s\n", ty->code == TY_FLOAT? 's': 'd', src->src_pregs[0]->name, dest[0]->name); return; } } if (is_x87) { print_mem_or_reg(src->dest_pregs[0], src->dest_vreg); } else { print_mem_or_reg(src->src_pregs[0], src->src_vreg); } x_fputc('\n', out); } /* XXX 64bit ... */ static void emit_mod(struct reg **dest, struct icode_instr *src) { emit_div(dest, src, 1); if (!IS_LLONG(src->dest_vreg->type->code) && !IS_LONG(src->dest_vreg->type->code)) { x_fprintf(out, "\tmov %%edx, %%%s\n", dest[0]->name); } else { x_fprintf(out, "\tmov %%rdx, %%%s\n", dest[0]->name); } } /* XXX ouch..how does it work with 64bit? */ static void emit_mul(struct reg **dest, struct icode_instr *src) { struct type *ty = src->src_vreg->type; (void) dest; if (IS_FLOATING(ty->code)) { if (STUPID_X87(dest[0])) { x_fprintf(out, "\tfmulp "); } else { x_fprintf(out, "\tmuls%c ", ty->code == TY_FLOAT? 's': 'd'); } } else if (ty->sign == TOK_KEY_UNSIGNED) { x_fprintf(out, "\tmul "); } else { /* signed integer multiplication */ /* XXX should use mul for pointer arithmetic :( */ x_fprintf(out, "\timul %%%s, %%%s\n", src->src_pregs[0]->name, src->src_pregs[0]->size == 4? "eax": "rax"); return; } print_mem_or_reg(src->src_pregs[0], src->src_vreg); if (IS_FLOATING(ty->code)) { x_fprintf(out, ", %%%s", src->dest_pregs[0]->name); } x_fputc('\n', out); } /* XXX sal for signed values!!!!! */ static void emit_shl(struct reg **dest, struct icode_instr *src) { x86_emit_gas.shl(dest, src); } /* XXX sar for signed values !!!!!!!! */ static void emit_shr(struct reg **dest, struct icode_instr *src) { x86_emit_gas.shr(dest, src); } static void emit_or(struct reg **dest, struct icode_instr *src) { x86_emit_gas.or(dest, src); } static void emit_and(struct reg **dest, struct icode_instr *src) { x86_emit_gas.and(dest, src); } static void emit_xor(struct reg **dest, struct icode_instr *src) { x86_emit_gas.xor(dest, src); } static void emit_not(struct reg **dest, struct icode_instr *src) { x86_emit_gas.not(dest, src); } static void emit_ret(struct icode_instr *ii) { (void) ii; x_fprintf(out, "\tret\n"); /* XXX */ } extern struct icode_instr *last_x87_cmp; extern struct icode_instr *last_sse_cmp; /* * XXX compltee bogus code for token.c ... * call n_xmalloc * movl %rax, -40(rbp) * mov $0, %eax * cmp $0, %eax */ static void emit_cmp(struct reg **dest, struct icode_instr *src) { if (dest[0]->size == 8 && src->src_pregs && src->src_pregs[0]->size == 4) { src->src_pregs[0] = find_top_reg(src->src_pregs[0]); } else if (src->src_pregs && src->src_pregs[0]->size == 8 && dest[0]->size == 4) { dest[0] = find_top_reg(dest[0]); } if (dest[0]->type == REG_FPR) { if (STUPID_X87(dest[0])) { last_x87_cmp = src; } else { last_sse_cmp = src; } return; } else { fprintf(out, "\tcmp "); } if (src->src_pregs == NULL || src->src_vreg == NULL) { x_fprintf(out, "$0"); } else { print_mem_or_reg(src->src_pregs[0], src->src_vreg); } x_fprintf(out, ", %%%s\n", dest[0]->name); } static void emit_branch(struct icode_instr *ii) { x86_emit_gas.branch(ii); } static void emit_mov(struct copyreg *cr) { x86_emit_gas.mov(cr); } static void emit_setreg(struct reg *dest, int *value) { x86_emit_gas.setreg(dest, value); } static void emit_xchg(struct reg *r1, struct reg *r2) { x86_emit_gas.xchg(r1, r2); } static void emit_addrof(struct reg *dest, struct vreg *src, struct vreg *structtop) { x86_emit_gas.addrof(dest, src, structtop); } /* * Copy initializer to automatic variable of aggregate type */ static void emit_copyinit(struct decl *d) { #if 0 (unsigned long)d->vreg->size); #endif x_fprintf(out, "\tmov $%lu, %%rdx\n", (unsigned long)backend->get_sizeof_type(d->dtype, 0)); x_fprintf(out, "\tmov $.%s, %%rsi\n", d->init_name->name); /* XXX */ x_fprintf(out, "\tlea -%lu(%%rbp), %%rdi\n", d->stack_addr->offset); x_fprintf(out, "\tcall memcpy\n"); } /* * Assign one struct to another (may be any of automatic or static or * addressed thru pointer) * * This stuff now copies intrinsically - i.e. doesn't use memcpy() - * because that way we can guarantee that no registers are trashed. * This simplifies things like passing structs to functions on the * stack, but is currently very simplistic and thus not very fast * for big structs * * (Note that the frontend cannot assume that all registers remain * intact because other backends do still call memcpy()) */ static void emit_copystruct(struct copystruct *cs) { struct vreg *stop; static unsigned long lcount; x_fprintf(out, "\tpush %%rdx\n"); x_fprintf(out, "\tpush %%rdi\n"); x_fprintf(out, "\tpush %%rsi\n"); x_fprintf(out, "\tpush %%rax\n"); /* arg 3 = rdx */ #if 0 x_fprintf(out, "\tmov rdx, %lu\n", (unsigned long)cs->src_vreg->size); #endif x_fprintf(out, "\tmov $%lu, %%rcx\n", (unsigned long)cs->src_vreg->size); if (cs->src_from_ptr == NULL) { if (cs->src_vreg->parent) { stop = get_parent_struct(cs->src_vreg); } else { stop = NULL; } /* arg 2 = rsi */ emit_addrof(&amd64_x86_gprs[4], cs->src_vreg, stop); } else { if (cs->src_vreg->parent) { x_fprintf(out, "\tadd $%lu, %%%s\n", calc_offsets(cs->src_vreg), /*cs->src_vreg->memberdecl->offset,*/ cs->src_from_ptr->name); } /* arg 2 = rsi */ x_fprintf(out, "\tmov %%%s, %%rsi\n", cs->src_from_ptr->name); } if (cs->dest_vreg == NULL) { /* copy to hidden pointer */ emit_load(&amd64_x86_gprs[5], curfunc->hidden_pointer); } else if (cs->dest_from_ptr == NULL) { if (cs->dest_vreg->parent) { stop = get_parent_struct(cs->dest_vreg); } else { stop = NULL; } /* arg 3 = rdi */ emit_addrof(&amd64_x86_gprs[5], cs->dest_vreg, stop); } else { if (cs->dest_vreg->parent) { x_fprintf(out, "\tadd $%lu, %%%s\n", calc_offsets(cs->dest_vreg), /*cs->dest_vreg->memberdecl->offset,*/ cs->dest_from_ptr->name); } /* arg 3 = rdi */ x_fprintf(out, "\tmov %%%s, %%rdi\n", cs->dest_from_ptr->name); } #if 0 x_fprintf(out, "\tcall memcpy\n"); #endif x_fprintf(out, "\t.copystruct%lu:\n", lcount); x_fprintf(out, "\tmov (%%rsi), %%al\n"); x_fprintf(out, "\tmov %%al, (%%rdi)\n"); x_fprintf(out, "\tinc %%rdi\n"); x_fprintf(out, "\tinc %%rsi\n"); x_fprintf(out, "\tloop .copystruct%lu\n", lcount++); x_fprintf(out, "\tpop %%rax\n"); x_fprintf(out, "\tpop %%rsi\n"); x_fprintf(out, "\tpop %%rdi\n"); x_fprintf(out, "\tpop %%rdx\n"); } static void emit_intrinsic_memcpy(struct int_memcpy_data *data) { x86_emit_gas.intrinsic_memcpy(data); } static void emit_zerostack(struct stack_block *sb, size_t nbytes) { x_fprintf(out, "\tmov $%lu, %%rdx\n", (unsigned long)nbytes); x_fprintf(out, "\tmov $0, %%rsi\n"); x_fprintf(out, "\tlea -%lu(%%rbp), %%rdi\n", (unsigned long)sb->offset); x_fprintf(out, "\tcall memset\n"); } static void emit_alloca(struct allocadata *ad) { x_fprintf(out, "\tmov %%%s, %%%s\n", ad->size_reg->name, ad->size_reg->size == 4? "edi": "rdi"); x_fprintf(out, "\tcall malloc\n"); if (ad->result_reg != &amd64_x86_gprs[0]) { x_fprintf(out, "\tmov %%rax, %%%s\n", ad->result_reg->name); } } static void emit_dealloca(struct stack_block *sb, struct reg *r) { char *regname = r? r->name: "rdi"; x_fprintf(out, "\tmovq -%lu(%%rbp), %%%s\n", (unsigned long)sb->offset, regname); x_fprintf(out, "\tcall free\n"); } static void emit_alloc_vla(struct stack_block *sb) { x_fprintf(out, "\tmov -%lu(%%rbp), %%rdi\n", (unsigned long)sb->offset - backend->get_ptr_size()); x_fprintf(out, "\tcall malloc\n"); x_fprintf(out, "\tmov %%rax, -%lu(%%rbp)\n", (unsigned long)sb->offset); } static void emit_dealloc_vla(struct stack_block *sb, struct reg *r) { char *regname = r? r->name: "rdi"; x_fprintf(out, "\tmovq -%lu(%%rbp), %%%s\n", (unsigned long)sb->offset, regname); x_fprintf(out, "\tcall free\n"); } static void emit_put_vla_size(struct vlasizedata *data) { x_fprintf(out, "\tmovq %%%s, -%lu(%%rbp)\n", data->size->name, (unsigned long)data->blockaddr->offset - data->offset); } static void emit_retr_vla_size(struct vlasizedata *data) { x_fprintf(out, "\tmovq -%lu(%%rbp), %%%s\n", (unsigned long)data->blockaddr->offset - data->offset, data->size->name); } static void emit_load_vla(struct reg *r, struct stack_block *sb) { x_fprintf(out, "\tmovq -%lu(%%rbp), %%%s\n", (unsigned long)sb->offset, r->name); } static void emit_frame_address(struct builtinframeaddressdata *dat) { x_fprintf(out, "\tmovq %%rbp, %%%s\n", dat->result_reg->name); } static void emit_save_ret_addr(struct function *f, struct stack_block *sb) { (void) f; x_fprintf(out, "\tmov 8(%%rbp), %%rax\n"); x_fprintf(out, "\tmov %%rax, -%lu(%%rbp)\n", sb->offset); } static void emit_check_ret_addr(struct function *f, struct stack_block *saved) { static unsigned long labval = 0; (void) f; x_fprintf(out, "\tmovq -%lu(%%rbp), %%rcx\n", saved->offset); x_fprintf(out, "\tcmpq 8(%%rbp), %%rcx\n"); x_fprintf(out, "\tje .doret%lu\n", labval); x_fprintf(out, "\tcall __nwcc_stack_corrupt\n"); x_fprintf(out, ".doret%lu:\n", labval++); } static void do_stack(FILE *out, struct decl *d) { char *sign; if (d->stack_addr->is_func_arg) { sign = ""; } else { sign = "-"; } x_fprintf(out, "%s%lu(%%rbp", sign, d->stack_addr->offset); } static void print_mem_operand(struct vreg *vr, struct token *constant) { int needbracket = 1; if (vr && vr->from_const != NULL) { constant = vr->from_const; } if (constant != NULL) { struct token *t = vr->from_const; if (!IS_FLOATING(t->type)) { /* * XXX hmm sometimes we use $.Str and sometimes * .Str?? */ x_fputc('$', out); } /* if (t->type == TY_INT) { x_fprintf(out, "%d", *(int *)t->data); } else if (t->type == TY_UINT) { x_fprintf(out, "%u", *(unsigned *)t->data); } else if (t->type == TY_LONG || t->type == TY_LLONG) { x_fprintf(out, "%ld", *(long *)t->data); } else if (t->type == TY_ULONG || t->type == TY_ULLONG) { x_fprintf(out, "%lu", *(unsigned long *)t->data); }*/ if (IS_INT(t->type) || IS_LONG(t->type) || IS_LLONG(t->type)) { cross_print_value_by_type(out, t->data, t->type, 0); } else if (t->type == TOK_STRING_LITERAL) { struct ty_string *ts = t->data; x_fprintf(out, "._Str%ld", ts->count); } else if (t->type == TY_FLOAT || t->type == TY_DOUBLE || t->type == TY_LDOUBLE) { struct ty_float *tf = t->data; x_fprintf(out, "(_Float%lu)", tf->count); } else { printf("loadimm: Bad data type %d\n", t->type); exit(EXIT_FAILURE); } } else if (vr->parent != NULL) { struct vreg *vr2; struct decl *d2; vr2 = get_parent_struct(vr); if ((d2 = vr2->var_backed) != NULL) { unsigned long off = calc_offsets(vr); #if 0 hm continue here, upwards #endif if (d2->stack_addr) { /* XXX wow this sux */ if (vr2->var_backed->stack_addr->is_func_arg) { vr2->var_backed->stack_addr->offset += /*vr->memberdecl->offset*/ off; } else { vr2->var_backed->stack_addr->offset -= /*vr->memberdecl->offset*/ off; } do_stack(out, vr2->var_backed); if (vr2->var_backed->stack_addr->is_func_arg) { vr2->var_backed->stack_addr->offset -= /*vr->memberdecl->offset*/ off; } else { vr2->var_backed->stack_addr->offset += /*vr->memberdecl->offset*/ off; } } else { /* static */ x_fprintf(out, "%s+%lu", d2->dtype->name, calc_offsets(vr)); needbracket = 0; } } else if (vr2->from_ptr) { /* Struct comes from pointer */ x_fprintf(out, "%lu(%%%s", calc_offsets(vr) , vr2->from_ptr->pregs[0]->name); } else { printf("BUG: Bad load for %s\n", vr->type->name? vr->type->name: "structure"); abort(); } } else if (vr->var_backed) { struct decl *d = vr->var_backed; if (d->stack_addr != NULL) { do_stack(out, d); } else { /* * Static or register variable */ if (d->dtype->storage == TOK_KEY_REGISTER) { unimpl(); } else { if (d->dtype->tlist != NULL && d->dtype->tlist->type == TN_FUNCTION) { needbracket = 0; x_fprintf(out, "$%s", d->dtype->name); } else { x_fprintf(out, "%s", d->dtype->name); } needbracket = 0; } } } else if (vr->stack_addr) { x_fprintf(out, "-%lu(%%rbp", vr->stack_addr->offset); } else if (vr->from_ptr) { x_fprintf(out, "(%%%s", vr->from_ptr->pregs[0]->name); } else { abort(); } if (constant == NULL) { if (needbracket) { x_fputc(')', out); } } } /* Mem to FPR */ static void emit_amd64_cvtsi2sd(struct icode_instr *ip) { x_fprintf(out, "\tcvtsi2sd "); print_mem_or_reg(ip->src_pregs[0], ip->src_vreg); x_fprintf(out, ", %%%s\n", ip->dest_pregs[0]->name); } /* Mem to FPR */ static void emit_amd64_cvtsi2ss(struct icode_instr *ip) { x_fprintf(out, "\tcvtsi2ss "); print_mem_or_reg(ip->src_pregs[0], ip->src_vreg); x_fprintf(out, ", %%%s\n", ip->dest_pregs[0]->name); } /* FPR to GPR */ static void emit_amd64_cvttsd2si(struct icode_instr *ip) { x_fprintf(out, "\tcvttsd2si %%%s, %%%s\n", ip->src_pregs[0]->name, ip->dest_pregs[0]->name); } /* FPR to GPR */ static void emit_amd64_cvttss2si(struct icode_instr *ip) { x_fprintf(out, "\tcvttss2si %%%s, %%%s\n", ip->src_pregs[0]->name, ip->dest_pregs[0]->name); } static void emit_amd64_cvtsd2ss(struct icode_instr *ii) { struct reg *r = ii->dat; x_fprintf(out, "\tcvtsd2ss %%%s, %%%s\n", r->name, r->name); } static void emit_amd64_cvtss2sd(struct icode_instr *ii) { struct reg *r = ii->dat; x_fprintf(out, "\tcvtss2sd %%%s, %%%s\n", r->name, r->name); } struct emitter amd64_emit_gas = { init, emit_strings, emit_extern_decls, emit_static_decls, emit_struct_defs, emit_comment, emit_dwarf2_line, emit_dwarf2_files, emit_inlineasm, emit_unimpl, emit_empty, emit_label, emit_call, emit_callindir, emit_func_intro, emit_func_outro, emit_define, emit_push, emit_allocstack, emit_freestack, emit_adj_allocated, emit_inc, emit_dec, emit_load, emit_store, emit_setsection, emit_alloc, emit_neg, emit_sub, emit_add, emit_div, emit_mod, emit_mul, emit_shl, emit_shr, emit_or, NULL, /* emit_preg_or */ emit_and, emit_xor, emit_not, emit_ret, emit_cmp, NULL, /* extend_sign */ NULL, /* conv_fp */ emit_branch, emit_mov, emit_setreg, emit_xchg, emit_addrof, emit_copyinit, emit_copystruct, emit_intrinsic_memcpy, emit_zerostack, emit_alloca, emit_dealloca, emit_alloc_vla, emit_dealloc_vla, emit_put_vla_size, emit_retr_vla_size, emit_load_vla, emit_frame_address, emit_struct_inits, emit_save_ret_addr, emit_check_ret_addr, print_mem_operand }; struct emitter_amd64 emit_amd64_gas = { emit_amd64_cvtsi2sd, emit_amd64_cvtsi2ss, emit_amd64_cvttsd2si, emit_amd64_cvttss2si, emit_amd64_cvtsd2ss, emit_amd64_cvtss2sd };