/*
* 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <ctype.h>
#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
};
syntax highlighted by Code2HTML, v. 0.9.1