/*
* 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 YASM code from intermediate amd64 code
*/
#include "amd64_emit_yasm.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 "typemap.h"
#include "functions.h"
#include "symlist.h"
#include "dwarf.h"
#include "cc1_main.h"
#include "x86_gen.h"
#include "expr.h"
#include "x86_emit_nasm.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);
/*
* Turns either a byte size into an assembler type string. If a type
* argument is supplied (non-null), it will ensure that ``long long''
* is mapped to ``dword'', as that type is really dealt with as two
* dwords rather than a qword.
*/
static char *
size_to_asmtype(size_t size, struct type *type) {
(void) type;
if (size == /*10*/12
|| size == 10
|| size == 16) return "tword"; /* long double */
else if (size == 8) return "qword"; /* double/long long */
else if (size == 4) return "dword";
else if (size == 2) return "word";
else if (size == 1) return "byte";
else {
printf("bad size for size_to_type(): %lu\n",
(unsigned long)size);
abort();
}
return NULL;
}
extern void
print_nasm_offsets(struct vreg *vr);
static void
emit_extern_decls(void) {
x86_emit_nasm.extern_decls();
}
static void
emit_static_decls(void) {
x86_emit_nasm.static_decls();
}
static void
emit_struct_inits(void) {
x86_emit_nasm.struct_inits();
}
extern void
print_nasm_string_init(size_t howmany, struct ty_string *str);
/*
* XXX Misleading name ;o
*/
static void
emit_strings(void) {
x86_emit_nasm.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, "; inline start\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_nasm(out, stmt->code);
}
x_fprintf(out, "; inline end\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 qword rbp\n"); /* XXX */
x_fprintf(out, "\tmov rbp, rsp\n");
}
static void
emit_func_outro(void) {
x_fprintf(out, "\tret\n");
}
static void
emit_define(const char *name, const char *fmt, ...) {
x86_emit_nasm.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;
char *ascii_type;
(void) f;
/*
* XXX 07/26/07 Hmm ascii_type not used anymore??
*/
if (ii->src_vreg->type
&& ii->src_vreg->type->tlist
&& ii->src_vreg->type->tlist->type == TN_ARRAY_OF) {
ascii_type = size_to_asmtype(8, NULL);
} else {
if (ii->src_vreg->type && ii->src_vreg->type->is_vla) {
ascii_type = size_to_asmtype(8, NULL);
} else {
ascii_type = size_to_asmtype(ii->src_vreg->size,
ii->src_vreg->type);
}
}
if (ii->src_pregs) {
x_fprintf(out, "\tpush %s\n", /*ascii_type*/ 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) {
x_fprintf(out, "\tpush %s [%s",
ascii_type,
vr2->from_ptr->pregs[0]->name);
if (vr->parent->type->code == TY_STRUCT) {
x_fprintf(out, " + %s.%s",
vr2->from_ptr->type->tstruc->tag,
d2->dtype->name);
}
x_fprintf(out, "]\n");
} else if (d2 && d2->stack_addr != NULL) {
x_fprintf(out, "\tpush %s [rbp - %ld",
ascii_type,
d2->stack_addr->offset);
if (vr->parent->type->code == TY_STRUCT) {
x_fputc(' ', out);
print_nasm_offsets(/*d*/vr2);
}
x_fprintf(out, "]\n");
} else if (d2 != NULL) {
/* Must be static */
x_fprintf(out, "\tpush %s [$%s",
ascii_type,
d2->dtype->name);
if (vr->parent->type->code == TY_STRUCT) {
x_fprintf(out, " + ");
print_nasm_offsets(/*d*/vr2);
}
x_fprintf(out, "]\n");
} else {
unimpl();
}
} else {
if (d->stack_addr != NULL) {
/* Stack */
x_fprintf(out, "\tpush %s [rbp - %ld]\n",
ascii_type,
d->stack_addr->offset);
} else {
/* Static or register variable */
if (d->dtype->storage == TOK_KEY_REGISTER) {
unimpl();
} else {
x_fprintf(out, "\tpush %s [$%s]\n",
ascii_type, 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, "\tpush qword _Str%lu\n", s->count);
} else if (IS_INT(t->type)) {
/*
* There are only forms of ``int'' and ``long''
* constants
*/
x_fprintf(out, "\tpush dword ");
cross_print_value_by_type(out,
t->data, t->type, 0);
x_fputc('\n', out);
} else if (IS_LONG(t->type) || IS_LLONG(t->type)) {
x_fprintf(out, "\tpush qword ");
cross_print_value_by_type(out,
t->data, t->type, 0);
x_fputc('\n', out);
} else {
puts("BUG in NASM emit_push()");
exit(EXIT_FAILURE);
}
} else if (vr->from_ptr) {
x_fprintf(out, "\tpush %s [%s]\n", /*ascii_type,*/
ascii_type,
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 rsp, %lu\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 rsp, %lu\n",
(unsigned long)f->total_allocated);
}
x_fprintf(out, "\tpop rbp\n");
} else {
if (*nbytes != 0) {
x_fprintf(out, "\tadd rsp, %lu\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_nasm.struct_defs();
}
static void
emit_setsection(int value) {
x86_emit_nasm.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;
char *p = size_to_asmtype(
backend->get_sizeof_decl(d, NULL),
d->dtype);
if (d->stack_addr) {
x_fprintf(out, "%s [rbp - %ld]",
p, d->stack_addr->offset);
} else {
x_fprintf(out, "%s %s", p, d->dtype->name);
}
} else {
x_fprintf(out, "%s", r->name);
}
}
static void
emit_inc(struct icode_instr *ii) {
x86_emit_nasm.inc(ii);
}
static void
emit_dec(struct icode_instr *ii) {
x86_emit_nasm.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 ", p);
} 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 %s, ", p, r->name);
}
} 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 = "movsxd";
}
} else {
p = "movsx";
}
}
}
}
x_fprintf(out, "\t%s %s, ", p, r->name);
}
if (vr->from_const != NULL && vr->size == 0) {
needsize = 0;
}
if (needsize) {
struct type *ty = vr->type;
size_t size = vr->size;
if (vr->stack_addr != NULL) {
size = r->size;
ty = NULL;
}
x_fprintf(out, "%s ",
size_to_asmtype(size, ty));
}
print_mem_operand(vr, NULL);
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;
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";
}
}
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? */
char *asize = size_to_asmtype(
dest->size, dest->type);
zero_first = 1;
x_fprintf(out, "\tmov %s ", asize);
print_mem_operand(dest, NULL);
x_fprintf(out, ", 0\n");
if (dest->is_multi_reg_obj) {
x_fprintf(out, "\tmov %s ",
asize);
print_mem_operand(dest, NULL);
x_fprintf(out, ", 0\n");
}
p = "mov";
} else {
if (dest->from_ptr
&& src->pregs[0]->size == 4) {
/*
* ``movsxd [r9], eax''
* doesn't work :S
*/
src->pregs[0] =
find_top_reg(
src->pregs[0]);
x_fprintf(out,
"\tmovsxd %s, %s\n",
src->pregs[0]->name,
src->pregs[0]->composed_of[0]->name);
p = "mov";
} else {
if (src->pregs[0]->size == 4) {
p = "movsxd";
} else {
p = "movsx";
}
}
}
} else {
p = "mov";
}
}
}
x_fprintf(out, "\t%s ", p);
if (floating) {
x_fprintf(out, "%s ", size_to_asmtype(dest->size, dest->type));
}
print_mem_operand(dest, NULL);
if (floating) {
/* Already done - floating stores only ever come from st0 */
x_fputc('\n', out);
return;
}
if (src->from_const) {
print_mem_operand(src, NULL);
} else {
/* Must be register */
x_fprintf(out, ", %s\n", src->pregs[0]->name);
}
}
static void
emit_neg(struct reg **dest, struct icode_instr *src) {
x86_emit_nasm.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 %s, ", dest[0]->name);
} else {
x_fprintf(out, "\tsubs%c %s, ",
src->src_vreg->type->code == TY_FLOAT? 's': 'd',
dest[0]->name);
}
} 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, "\tmovsxd %s, %s\n",
r->name,
src->src_pregs[0]->name);
}
x_fprintf(out, "\tsub %s, %s\n",
dest[0]->name,
r->name);
return;
}
}
x_fprintf(out, "\tsub %s, ", dest[0]->name);
}
print_mem_or_reg(src->src_pregs[0], src->src_vreg);
x_fputc('\n', out);
}
/* 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 %s, ", dest[0]->name);
} else {
x_fprintf(out, "\tadds%c %s, ",
src->src_vreg->type->code == TY_FLOAT? 's': 'd',
dest[0]->name);
}
} 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, "\tmovsxd %s, %s\n",
r->name,
src->src_pregs[0]->name);
}
x_fprintf(out, "\tadd %s, %s\n",
dest[0]->name,
r->name);
return;
}
}
x_fprintf(out, "\tadd %s, ", dest[0]->name);
}
print_mem_or_reg(src->src_pregs[0], src->src_vreg);
x_fputc('\n', out);
}
/* 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, "\tcdq\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])) {
x_fprintf(out, "\tfdivp ");
is_x87 = 1;
} else {
x_fprintf(out, "\tdivs%c %s, ",
ty->code == TY_FLOAT? 's': 'd',
dest[0]->name);
}
}
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 %s, edx\n", dest[0]->name);
} else {
x_fprintf(out, "\tmov %s, rdx\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;
int is_x87 = 0;
(void) dest;
if (IS_FLOATING(ty->code)) {
if (STUPID_X87(dest[0])) {
x_fprintf(out, "\tfmulp ");
is_x87 = 1;
} else {
x_fprintf(out, "\tmuls%c %s, ",
ty->code == TY_FLOAT? 's': 'd',
dest[0]->name);
}
} 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, ",
src->src_pregs[0]->size == 4? "eax": "rax");
}
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 sal for signed values!!!!! */
static void
emit_shl(struct reg **dest, struct icode_instr *src) {
x86_emit_nasm.shl(dest, src);
}
/* XXX sar for signed values !!!!!!!! */
static void
emit_shr(struct reg **dest, struct icode_instr *src) {
x86_emit_nasm.shr(dest, src);
}
static void
emit_or(struct reg **dest, struct icode_instr *src) {
x86_emit_nasm.or(dest, src);
}
static void
emit_and(struct reg **dest, struct icode_instr *src) {
x86_emit_nasm.and(dest, src);
}
static void
emit_xor(struct reg **dest, struct icode_instr *src) {
x86_emit_nasm.xor(dest, src);
}
static void
emit_not(struct reg **dest, struct icode_instr *src) {
x86_emit_nasm.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;
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 %s, ", dest[0]->name);
}
if (src->src_pregs == NULL || src->src_vreg == NULL) {
fputc('0', out);
} else {
print_mem_or_reg(src->src_pregs[0], src->src_vreg);
}
x_fputc('\n', out);
}
static void
emit_branch(struct icode_instr *ii) {
x86_emit_nasm.branch(ii);
}
static void
emit_mov(struct copyreg *cr) {
x86_emit_nasm.mov(cr);
}
static void
emit_setreg(struct reg *dest, int *value) {
x86_emit_nasm.setreg(dest, value);
}
static void
emit_xchg(struct reg *r1, struct reg *r2) {
x86_emit_nasm.xchg(r1, r2);
}
static void
emit_addrof(struct reg *dest, struct vreg *src, struct vreg *structtop) {
x86_emit_nasm.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 rdx, %lu\n",
(unsigned long)backend->get_sizeof_type(d->dtype, 0));
x_fprintf(out, "\tmov rsi, %s\n", d->init_name->name);
x_fprintf(out, "\tlea rdi, [rbp - %lu]\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 rcx, %lu\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 %s, %lu\n",
cs->src_from_ptr->name,
calc_offsets(cs->src_vreg)
/*cs->src_vreg->memberdecl->offset*/);
}
/* arg 2 = rsi */
x_fprintf(out, "\tmov rsi, %s\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 %s, %lu\n",
cs->dest_from_ptr->name,
calc_offsets(cs->dest_vreg)
/*cs->dest_vreg->memberdecl->offset*/);
}
/* arg 3 = rdi */
x_fprintf(out, "\tmov rdi, %s\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 al, [rsi]\n");
x_fprintf(out, "\tmov [rdi], al\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_nasm.intrinsic_memcpy(data);
}
static void
emit_zerostack(struct stack_block *sb, size_t nbytes) {
x_fprintf(out, "\tmov rdx, %lu\n",
(unsigned long)nbytes);
x_fprintf(out, "\tmov rsi, 0\n");
x_fprintf(out, "\tlea rdi, [rbp - %lu]\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->size == 4? "edi": "rdi", ad->size_reg->name);
x_fprintf(out, "\tcall malloc\n");
if (ad->result_reg != &amd64_x86_gprs[0]) {
x_fprintf(out, "\tmov %s, rax\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, "\tmov %s, [rbp - %lu]\n",
regname,
(unsigned long)sb->offset);
x_fprintf(out, "\tcall free\n");
}
static void
emit_alloc_vla(struct stack_block *sb) {
x_fprintf(out, "\tmov rdi, [rbp - %lu]\n",
(unsigned long)sb->offset - backend->get_ptr_size());
x_fprintf(out, "\tcall malloc\n");
x_fprintf(out, "\tmov [rbp - %lu], rax\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, "\tmov %s, [rbp - %lu]\n",
regname,
(unsigned long)sb->offset);
x_fprintf(out, "\tcall free\n");
}
static void
emit_put_vla_size(struct vlasizedata *data) {
x_fprintf(out, "\tmov [rbp - %lu], %s\n",
(unsigned long)data->blockaddr->offset - data->offset,
data->size->name);
}
static void
emit_retr_vla_size(struct vlasizedata *data) {
x_fprintf(out, "\tmov %s, [rbp - %lu]\n",
data->size->name,
(unsigned long)data->blockaddr->offset - data->offset);
}
static void
emit_load_vla(struct reg *r, struct stack_block *sb) {
x_fprintf(out, "\tmov %s, [rbp - %lu]\n",
r->name,
(unsigned long)sb->offset);
}
static void
emit_frame_address(struct builtinframeaddressdata *dat) {
x_fprintf(out, "\tmov %s, rbp\n", dat->result_reg->name);
}
static void
emit_save_ret_addr(struct function *f, struct stack_block *sb) {
(void) f;
x_fprintf(out, "\tmov rax, [rbp + 8]\n");
x_fprintf(out, "\tmov [rbp - %lu], rax\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, "\tmov rcx, [rbp - %lu]\n", saved->offset);
x_fprintf(out, "\tcmp rcx, qword [rbp + 8]\n");
x_fprintf(out, "\tje .doret%lu\n", labval);
x_fprintf(out, "\textern __nwcc_stack_corrupt\n");
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, "[rbp %s %lu", 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_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) {
if (d2->stack_addr) {
do_stack(out, vr2->var_backed);
} else {
/* static */
x_fprintf(out, "[$%s", d2->dtype->name);
}
} else if (vr2->from_ptr) {
/* Struct comes from pointer */
x_fprintf(out, "[%s",
vr2->from_ptr->pregs[0]->name);
} else {
printf("BUG: Bad load for %s\n",
vr->type->name? vr->type->name: "structure");
abort();
}
x_fputc(' ', out);
print_nasm_offsets(vr);
} 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;
} else {
x_fputc('[', out);
}
x_fprintf(out, "$%s", d->dtype->name);
}
}
} else if (vr->stack_addr) {
x_fprintf(out, "[rbp - %lu", 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 %s, ", ip->dest_pregs[0]->name);
print_mem_or_reg(ip->src_pregs[0], ip->src_vreg);
x_fputc('\n', out);
}
/* Mem to FPR */
static void
emit_amd64_cvtsi2ss(struct icode_instr *ip) {
x_fprintf(out, "\tcvtsi2ss %s, ", ip->dest_pregs[0]->name);
print_mem_or_reg(ip->src_pregs[0], ip->src_vreg);
x_fputc('\n', out);
}
/* FPR to GPR */
static void
emit_amd64_cvttsd2si(struct icode_instr *ip) {
x_fprintf(out, "\tcvttsd2si %s, %s\n",
ip->dest_pregs[0]->name, ip->src_pregs[0]->name);
}
/* FPR to GPR */
static void
emit_amd64_cvttss2si(struct icode_instr *ip) {
x_fprintf(out, "\tcvttss2si %s, %s\n",
ip->dest_pregs[0]->name, ip->src_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_yasm = {
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_yasm = {
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