/*
 * 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