/*
 * Copyright (c) 2005 - 2007, Nils R. Weller
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
#include "icode.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include <sys/mman.h>
#include <unistd.h>
#include "misc.h"
#include "token.h"
#include "control.h"
#include "decl.h"
#include "type.h"
#include "debug.h"
#include "defs.h"
#include "expr.h"
#include "error.h"
#include "subexpr.h"
#include "symlist.h"
#include "reg.h"
#include "functions.h"
#include "backend.h" /* for get_sizeof() */
#include "scope.h"
#include "cc1_main.h" /* flags */
#include "inlineasm.h"
#include "n_libc.h"

struct icode_instr *
alloc_icode_instr(void) {
	struct icode_instr	*ret = n_xmalloc(sizeof *ret);
	static struct icode_instr	nullinstr;
	static unsigned long seq;

	*ret = nullinstr;
	ret->seqno = ++seq;
	return ret;
}

struct icode_list *
alloc_icode_list(void) {
	struct icode_list	*ret = n_xmalloc(sizeof *ret);
	static struct icode_list	nullil;
	*ret = nullil;
	return ret;
}

void
append_icode_list(struct icode_list *list, struct icode_instr *instr) {
	if (list->tail == NULL) {
		list->head = list->tail = instr;
	} else {
		list->tail->next = instr;
		list->tail = instr;
	}
	
	/*
	 * XXX stuff below is a kludge to ensure that pointer pregs are
	 * always recorded properly
	 */
	if (instr->src_vreg && instr->src_ptr_preg == NULL) {
		if (instr->src_vreg->from_ptr) {
			instr->src_ptr_preg
				= instr->src_vreg->from_ptr->pregs[0];
		} else if (instr->src_vreg->parent) {
			struct vreg	*vr2 =
				get_parent_struct(instr->src_vreg);
			if (vr2->from_ptr) {
				instr->src_parent_struct = vr2;
				instr->src_ptr_preg
					= vr2->from_ptr->pregs[0];
			}	
		}
	}
	if (instr->dest_vreg && instr->dest_ptr_preg == NULL) {
		if (instr->dest_vreg->from_ptr) {
			instr->dest_ptr_preg
				= instr->dest_vreg->from_ptr->pregs[0];
		} else if (instr->dest_vreg->parent) {
			struct vreg	*vr2 =
				get_parent_struct(instr->dest_vreg);
			if (vr2->from_ptr) {
				instr->dest_parent_struct = vr2;
				instr->dest_ptr_preg
					= vr2->from_ptr->pregs[0];
			}	
		}
	}
	if (instr->type == INSTR_COPYSTRUCT) {
		struct copystruct	*cs = instr->dat;
		struct vreg		*structtop;

		if (cs->src_vreg && cs->src_vreg->parent) {
			structtop = get_parent_struct(cs->src_vreg);
			if (structtop->from_ptr) {
				instr->src_parent_struct = structtop;
				instr->src_ptr_preg =
					structtop->from_ptr->pregs[0];
			}
		}
		if (cs->dest_vreg && cs->dest_vreg->parent) {
			structtop = get_parent_struct(cs->dest_vreg);
			if (structtop->from_ptr) {
				instr->dest_parent_struct = structtop;
				instr->dest_ptr_preg =
					structtop->from_ptr->pregs[0];
			}	
		}
	}
}

void
merge_icode_lists(struct icode_list *dest, struct icode_list *src) {
	if (src->head == NULL) {
		/* XXX ??? */
		dest->res = src->res;
		return;
	}	
	if (dest->head == NULL) {
		if (dest->tail != NULL) {
			puts("icode list corruption detected");
			exit(EXIT_FAILURE);
		}	
		dest->head = src->head;
		dest->tail = src->tail;
	} else {
		dest->tail->next = src->head;
		if (src->tail != NULL) {
			dest->tail = src->tail;
		}
	}
	dest->res = src->res;
}

struct reg **
make_icode_pregs(struct vreg *vr, struct reg *r) {
	struct reg	**ret;

	if (vr == NULL || !vr->is_multi_reg_obj) {
		ret = n_xmalloc(2 * sizeof *ret);
		ret[0] = vr? vr->pregs[0]: r;
		ret[1] = NULL;
	} else {
		ret = n_xmalloc(vr->is_multi_reg_obj * sizeof *ret);
		if (vr->is_multi_reg_obj != 2) abort();
		/* XXX hardcoded x86 */
		ret[0] = vr->pregs[0];
		ret[1] = vr->pregs[1];
	}

	return ret;
}	
	
struct icode_instr *
generic_icode_make_instr(struct vreg *dest, struct vreg *src, int type) {
	struct icode_instr	*ret = alloc_icode_instr();
	ret->type = type;
	ret->dest_vreg = dest;
	if (dest != NULL) {
		ret->dest_pregs = make_icode_pregs(dest, NULL);
	}	
	ret->src_vreg = src;
	if (src != NULL) {
		ret->src_pregs = make_icode_pregs(src, NULL);
	}	
	return ret;
}

static unsigned long	lnum = 0;

unsigned long
get_label_count(void) {
	return lnum;
}

struct icode_instr *
icode_make_label(const char *name) {
	struct icode_instr	*ret;
	char			buf[1024];

	ret = alloc_icode_instr();
	ret->type = INSTR_LABEL;

	if (name == NULL) {
		sprintf(buf, "L%lu", lnum++);
		ret->dat = n_xstrdup(buf);
	} else {
		ret->dat = n_xstrdup(name);
	}	

	return ret;
}

struct icode_instr *
icode_make_adj_allocated(int bytes) {
	struct icode_instr	*ii = alloc_icode_instr();

	ii->dat = n_xmemdup(&bytes, sizeof bytes);
	ii->type = INSTR_ADJ_ALLOCATED;
	return ii;
}	

struct icode_instr *
icode_make_setreg(struct reg *r, int value) {
	struct icode_instr	*ret = alloc_icode_instr();

	ret->type = INSTR_SETREG;
	ret->src_pregs = make_icode_pregs(NULL, r);
	ret->dat = n_xmemdup(&value, sizeof value); /* XXX :-( */
	return ret;
}	

struct icode_instr *
icode_make_freestack(size_t bytes) {
	struct icode_instr	*ret = alloc_icode_instr();
	ret->type = INSTR_FREESTACK;
	ret->dat = n_xmemdup(&bytes, sizeof bytes);
	return ret;
}


struct icode_instr *
icode_make_branch(struct icode_instr *dest, int btype, struct vreg *vr) {
	struct icode_instr	*ret = alloc_icode_instr();
	ret->type = btype;
	ret->dat = dest;
	ret->dest_vreg = vr;
	return ret;
}

struct icode_instr *
icode_make_call(const char *name) {
	struct icode_instr	*ret = alloc_icode_instr();
	ret->type = INSTR_CALL;
	ret->dat = (char *)name;
	return ret;
}

struct icode_instr *
icode_make_call_indir(struct reg *r) {
	struct icode_instr	*ret = alloc_icode_instr();
	ret->type = INSTR_CALLINDIR;
	ret->dat = r;
	return ret;
}	

void
icode_make_xchg(struct reg *r1, struct reg *r2, struct icode_list *il) {
	struct icode_instr	*ret = alloc_icode_instr();

	ret->type = INSTR_XCHG;
	ret->src_pregs = make_icode_pregs(NULL, r1);
	ret->dest_pregs = make_icode_pregs(NULL, r2);
	append_icode_list(il, ret);
}

static struct icode_instr *
icode_make_load_vla(struct reg *r, struct type *ty) {
	struct icode_instr	*ii = alloc_icode_instr();

	ii->type = INSTR_LOAD_VLA;
	ii->dest_pregs = make_icode_pregs(NULL, r);
	ii->dat = ty;
	return ii;
}

/*
 * XXX pass vreg instead??
 * Note that this really only deals with one register at a time -
 * multi-register objects are dealt with in vreg_faultin()
 */
struct icode_instr *
icode_make_load(struct reg *r, struct vreg *parent_struct) {
	struct icode_instr	*ret = alloc_icode_instr();
	struct vreg		*vr = r->vreg;

	if (vr->type->is_vla && is_immediate_vla_type(vr->type)) {
		return icode_make_load_vla(r, vr->type);
	}

	if (vr->from_const && vr->from_const->data2) {
		/* Too big to be immediate */
		struct ty_llong	*tll;

		tll = vr->from_const->data2;
		tll->loaded = 1;
	}	
	if (vr->type->code == TY_STRUCT && vr->type->tlist == NULL) {
		puts("BUG: Attempt to load struct into register :-(");
		abort();
	}
	if (vr->parent != NULL && parent_struct != NULL) {
		if (parent_struct->from_ptr) {
			ret->src_parent_struct = parent_struct;
			ret->src_ptr_preg =
				parent_struct->from_ptr->pregs[0];
		}
	} else if (vr->from_ptr) {
		ret->src_ptr_preg = vr->from_ptr->pregs[0];
	}	

	ret->type = INSTR_LOAD;
	ret->src_vreg = vr;
	ret->src_pregs = make_icode_pregs(NULL, r);
	if (!vr->var_backed
		&& !vr->from_const
		&& !vr->from_ptr
		&& !vr->parent
		&& !vr->stack_addr) {
		printf("BUG: load from unbacked vreg %p\n", vr);
		printf("calling abort()\n");
		abort();
#if 0
	} else {
		debug_print_vreg_backing(vr);
#endif
	}	
	return ret;
}


/*
 * Store current item to virtual register vr 
 * XXX this is complete messed up
 *    Store current item (INSTR_SETITEM) to vr
 *
 */
void
icode_make_store(struct function *f,
	struct vreg *dest, struct vreg *src, /* XXX order is WRONG!! */
	struct icode_list *il) {

	struct icode_instr	*ret = alloc_icode_instr();


#if 0
if (src->pregs[0] == NULL)   {
	puts ( "   SRC PREG = NULL");
	abort();
}	
#endif

	ret->type = INSTR_STORE;
	ret->dest_vreg = dest;
	ret->dest_pregs = make_icode_pregs(dest, NULL);
	ret->src_vreg = src;
#if 0
	/*
	 * XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
	 * this triggers when compiling evalexpr.c on amd64!!!
	 * with an xmm* register!
	 */
	if (src && src->pregs[0] && src->pregs[0]->size == 4
		&& src->size == 1)  abort();
#endif
	if (src != NULL) {
		ret->src_pregs = make_icode_pregs(src, NULL);
		if (src->type->code == TY_STRUCT
			&& src->type->tlist == NULL) {
			puts("BUG: Attempt to store register to struct :-((((");
			abort();
		}
	}

	if (src != NULL
		&& !src->var_backed
		&& !src->from_ptr
		&& !src->from_const
		&& !src->parent) {
		/*
		 * Need to allocate anonymous storage 
		 * for register
		 */
		if (src->stack_addr == NULL) {
			/* Offset will be patched later */
			/* XXX long long !?!?!!! */
			size_t	size = src->pregs[0]->size;
			
			if (src->is_multi_reg_obj) {
				size += src->pregs[1]->size;
			} else if (backend->arch == ARCH_SPARC) {
				/*
				 * XXX hmm this seems wrong and ugly
				 */
				if (src->pregs[0]->type == REG_FPR) {
					switch (src->type->code) {
					case TY_FLOAT:
						size = 4;
						break;
					case TY_DOUBLE:
						size = 8;
						break;
					case TY_LDOUBLE:
						size = 16;
						break;
					default: unimpl();
					}
				}
			}

			src->stack_addr = make_stack_block(0, size);
			if (f->regs_head == NULL) {
				f->regs_head = f->regs_tail = src->stack_addr;
			} else {
				f->regs_tail->next = src->stack_addr;
				f->regs_tail = f->regs_tail->next;
			}
		}
	} else if (src->from_ptr) {
		vreg_faultin_protected(src, NULL, NULL, src->from_ptr, il, 0);
	}
	append_icode_list(il, ret);

#if 0
	if (src->type->tlist == NULL && IS_FLOATING(src->type->code)) {
		/*
		 * If using x87, we should NEVER cache any values.. so
		 * upon doing ``fstp'', the stack top will be popped -
		 * Invalidate references
		 */
		if (backend->arch == ARCH_X86
			|| (backend->arch == ARCH_AMD64
				&& src->type->code == TY_LDOUBLE)) {
			free_preg(src->pregs[0], il, 1, 0);
		}	
	}
#endif
}

struct icode_instr *
icode_make_store_indir(struct vreg *dest, struct vreg *src) {
	struct icode_instr	*ii = alloc_icode_instr();

if (src->pregs[0] == NULL) abort();	
	ii->type = INSTR_STORE;
	ii->dest_vreg = dest;
	ii->dest_pregs = make_icode_pregs(dest, NULL);
	ii->src_vreg = src;
	ii->src_pregs = make_icode_pregs(src, NULL);
	return ii;
}	


struct icode_instr *
icode_make_sub(struct vreg *dest, struct vreg *src) {
	return generic_icode_make_instr(dest, src, INSTR_SUB);
}

struct icode_instr *
icode_make_neg(struct vreg *vr) {
	return generic_icode_make_instr(NULL, vr, INSTR_NEG);
}
	

struct icode_instr *
icode_make_add(struct vreg *dest, struct vreg *src) {
	return generic_icode_make_instr(dest, src, INSTR_ADD);
}

struct icode_instr *
icode_make_div(struct vreg *dest, struct vreg *src) {
	return generic_icode_make_instr(dest, src, INSTR_DIV);
}

struct icode_instr *
icode_make_mod(struct vreg *dest, struct vreg *src) {
	return generic_icode_make_instr(dest, src, INSTR_MOD);
}

struct icode_instr *
icode_make_mul(struct vreg *dest, struct vreg *src) {
	return generic_icode_make_instr(dest, src, INSTR_MUL);
}

struct icode_instr *
icode_make_shl(struct vreg *dest, struct vreg *src) {
	return generic_icode_make_instr(dest, src, INSTR_SHL);
}

struct icode_instr *
icode_make_shr(struct vreg *dest, struct vreg *src) {
	return generic_icode_make_instr(dest, src, INSTR_SHR);
}

struct icode_instr *
icode_make_and(struct vreg *dest, struct vreg *src) {
	return generic_icode_make_instr(dest, src, INSTR_AND);
}

struct icode_instr *
icode_make_xor(struct vreg *dest, struct vreg *src) {
	struct icode_instr	*ret = alloc_icode_instr();
	ret->type = INSTR_XOR;
	ret->dest_vreg = dest;
	ret->dest_pregs = make_icode_pregs(dest, NULL);
	if (src != NULL) {
		ret->src_pregs = make_icode_pregs(src, NULL);
	}	
	ret->src_vreg = src;
	return ret;
}	

struct icode_instr *
icode_make_not(struct vreg *vr) {
	return generic_icode_make_instr(NULL, vr, INSTR_NOT);
}	

struct icode_instr *
icode_make_or(struct vreg *dest, struct vreg *src) {
	return generic_icode_make_instr(dest, src, INSTR_OR);
}

void
icode_make_preg_or(struct reg *dest, struct reg *src, struct icode_list *il) {
	struct icode_instr	*ret = alloc_icode_instr();
	ret->type = INSTR_PREG_OR;
	ret->dest_pregs = make_icode_pregs(NULL, dest);
	ret->src_pregs = make_icode_pregs(NULL, src);
	append_icode_list(il, ret);
}

struct icode_instr *
icode_make_push(struct vreg *vr, struct icode_list *il) {
	struct icode_instr	*ret = alloc_icode_instr();

	ret->type = INSTR_PUSH;
	if (vr->type && vr->type->is_vla) {
		if (vr->type->tlist->type == TN_ARRAY_OF
			|| vr->type->tlist->type == TN_VARARRAY_OF) {
			struct reg		*r;
			struct icode_instr	*ii;

			if (vr->pregs[0] == NULL
				|| vr->pregs[0]->vreg != vr) {
				r = ALLOC_GPR(curfunc, backend->get_ptr_size(),
					il, 0);	
				vreg_map_preg(vr, r);
				ii  = icode_make_load(r, NULL);
				append_icode_list(il, ii);
				free_preg(r, il, 0, 0);
			}
		}
	}
	ret->src_vreg = vr;
	ret->src_pregs= make_icode_pregs(vr, NULL);

	return ret;
}

struct icode_instr *
icode_make_ret(struct vreg *vr) {
	struct icode_instr	*ret = alloc_icode_instr();
	ret->type = INSTR_RET;
#if 0
	if (vr != NULL) {
		/*ret->src_preg = vr->preg; seems never used*/
		ret->src_vreg = vr;
	}
#endif
	ret->src_vreg = vr;
	return ret;
}

struct icode_instr *
icode_make_indir(struct reg *r) {
	struct icode_instr	*ret = alloc_icode_instr();
	ret->type = INSTR_INDIR;
	ret->dat = r;
	return ret;
}

/*
 * vr = NULL means take address of next argument on stack in
 * variadic function
 */
struct icode_instr *
icode_make_addrof(struct vreg *vr, struct icode_list *il) {
	struct icode_instr	*ret;
	
	ret = alloc_icode_instr();
	ret->type = INSTR_ADDROF;
	ret->src_vreg = vr;
#if 0
	ret->dat = backend->alloc_gpr(curfunc, 4, il, NULL); /* XXX */
#endif
	ret->dat = ALLOC_GPR(curfunc, backend->get_ptr_size(), il, NULL); /* XXX */
	return ret;
}

struct icode_instr *
icode_make_inc(struct vreg *vr) {
	return generic_icode_make_instr(NULL, vr, INSTR_INC);
}

struct icode_instr *
icode_make_dec(struct vreg *vr) {
	return generic_icode_make_instr(NULL, vr, INSTR_DEC);
}

/*
 * =====================================================
 * x86-specific instructions
 * =====================================================
 */
void
icode_make_x86_fxch(struct reg *r, struct reg *r2, struct icode_list *il) {
	struct icode_instr	*ret = alloc_icode_instr();
	struct vreg		*tmp = r2->vreg;

	ret->type = INSTR_X86_FXCH;
	ret->src_pregs = make_icode_pregs(NULL, r);
	ret->dest_pregs = make_icode_pregs(NULL, r2);
	append_icode_list(il, ret);
	if (r->vreg != NULL) {
		r->vreg->pregs[0] = r2;
		r2->vreg = r->vreg;
	}
	if (tmp != NULL) {
		r->vreg = tmp;
		r->vreg->pregs[0] = r;
	}	
}

void
icode_make_asm(struct inline_asm_stmt *inl, struct icode_list *il) {
	struct icode_instr	*ret = alloc_icode_instr();
	ret->type = INSTR_ASM;
	ret->dat = inl;
	append_icode_list(il, ret);
}	

void
icode_make_x86_cdq(struct icode_list *il) {
	struct icode_instr	*ret = alloc_icode_instr();
	ret->type = INSTR_X86_CDQ;
	append_icode_list(il, ret);
}	

void
icode_make_x86_ffree(struct reg *r, struct icode_list *il) {
	struct icode_instr	*ret = alloc_icode_instr();
	ret->type = INSTR_X86_FFREE;
	ret->src_pregs = make_icode_pregs(NULL, r);
	append_icode_list(il, ret);
}

void
icode_make_x86_store_x87cw(struct vreg *vr, struct icode_list *il) {
	struct icode_instr	*ii;
	
	ii = generic_icode_make_instr(NULL, vr, INSTR_X86_FNSTCW);
	append_icode_list(il, ii);
}

void
icode_make_x86_load_x87cw(struct vreg *vr, struct icode_list *il) {
	struct icode_instr	*ii;
	
	ii = generic_icode_make_instr(NULL, vr, INSTR_X86_FLDCW);
	append_icode_list(il, ii);
}

/*
 * =====================================================
 * MIPS-specific instructions
 * =====================================================
 */

void
icode_make_mips_mtc1(
	struct reg *dest,
	struct vreg *src,
	struct icode_list *il) {
	struct icode_instr	*ii;

	ii = generic_icode_make_instr(NULL, src, INSTR_MIPS_MTC1);
	ii->dat = dest;
	append_icode_list(il, ii);
}

void
icode_make_mips_mfc1(
	struct reg *dest,
	struct vreg *src,
	struct icode_list *il) {
	struct icode_instr	*ii;

	ii = generic_icode_make_instr(NULL, src, INSTR_MIPS_MFC1);
	ii->dat = dest;
	append_icode_list(il, ii);
}

void
icode_make_mips_cvt(
	struct vreg *dest,
	struct vreg *src,
	struct icode_list *il) {

	struct icode_instr	*ii;

	ii = generic_icode_make_instr(dest, src, INSTR_MIPS_CVT);
	append_icode_list(il, ii);
}

void
icode_make_mips_trunc(
	struct vreg *dest,
	struct vreg *src,
	struct icode_list *il) {

	struct icode_instr	*ii;

	ii = generic_icode_make_instr(dest, src, INSTR_MIPS_TRUNC);
	append_icode_list(il, ii);
}

void
icode_make_mips_make_32bit_mask(struct reg *r, struct icode_list *il) {
	struct icode_instr	*ii = alloc_icode_instr();

	ii->type = INSTR_MIPS_MAKE_32BIT_MASK;
	ii->dat = r;
	append_icode_list(il, ii);
}

/*
 * =====================================================
 * PowerPC-specific instructions
 * =====================================================
 */
void
icode_make_power_srawi(struct reg *dest, struct reg *src,
	int bits, struct icode_list *il) {

	struct icode_instr	*ii;

	ii = generic_icode_make_instr(NULL, NULL, INSTR_POWER_SRAWI);
	ii->dest_pregs = make_icode_pregs(NULL, dest);
	ii->src_pregs = make_icode_pregs(NULL, src);
	ii->dat = n_xmemdup(&bits, sizeof(int));
	append_icode_list(il, ii);
}

void
icode_make_power_rldicl(struct reg *dest, struct reg *src,
	int bits, struct icode_list *il) {

	struct icode_instr	*ii;

	ii = generic_icode_make_instr(NULL, NULL, INSTR_POWER_RLDICL);
	ii->dest_pregs = make_icode_pregs(NULL, dest);
	ii->src_pregs = make_icode_pregs(NULL, src);
	ii->dat = n_xmemdup(&bits, sizeof(int));
	append_icode_list(il, ii);
}

void
icode_make_power_fcfid(struct reg *dest, struct reg *src,
	struct icode_list *il) {

	struct icode_instr	*ii;

	ii = generic_icode_make_instr(NULL, NULL, INSTR_POWER_FCFID);
	ii->dest_pregs = make_icode_pregs(NULL, dest);
	ii->src_pregs = make_icode_pregs(NULL, src);
	append_icode_list(il, ii);
}

void
icode_make_power_frsp(struct reg *dest, struct icode_list *il) {
	struct icode_instr	*ii;

	ii = generic_icode_make_instr(NULL, NULL, INSTR_POWER_FRSP);
	ii->dat = dest;
	append_icode_list(il, ii);
}


void
icode_make_power_slwi(struct reg *dest, struct reg *src,
	int bits, struct icode_list *il) {
	struct icode_instr	*ii;

	ii = generic_icode_make_instr(NULL, NULL, INSTR_POWER_SLWI);
	ii->dest_pregs = make_icode_pregs(NULL, dest);
	ii->src_pregs = make_icode_pregs(NULL, src);
	ii->dat = n_xmemdup(&bits, sizeof(int));
	append_icode_list(il, ii);
}

void
icode_make_power_rlwinm(struct reg *dest, struct reg *src, int
	value, struct icode_list *il) {

	struct icode_instr	*ii;

	ii = generic_icode_make_instr(NULL, NULL, INSTR_POWER_RLWINM);
	ii->dest_pregs = make_icode_pregs(NULL, dest);
	ii->src_pregs = make_icode_pregs(NULL, src);
	ii->dat = n_xmemdup(&value, sizeof(int));
	append_icode_list(il, ii);
}

void
icode_make_power_extsb(struct reg *r, struct icode_list *il) {
	struct icode_instr	*ii;

	ii = generic_icode_make_instr(NULL, NULL, INSTR_POWER_EXTSB);
	ii->dat = r;
	append_icode_list(il, ii);
}

void
icode_make_power_extsh(struct reg *r, struct icode_list *il) {
	struct icode_instr	*ii;

	ii = generic_icode_make_instr(NULL, NULL, INSTR_POWER_EXTSH);
	ii->dat = r;
	append_icode_list(il, ii);
}

void
icode_make_power_extsw(struct reg *r, struct icode_list *il) {
	struct icode_instr	*ii;

	ii = generic_icode_make_instr(NULL, NULL, INSTR_POWER_EXTSW);
	ii->dat = r;
	append_icode_list(il, ii);
}

 
void
icode_make_power_xoris(struct reg *dest, int val, struct icode_list *il) {
	struct icode_instr	*ii;

	ii = generic_icode_make_instr(NULL, NULL, INSTR_POWER_XORIS);
	ii->dest_pregs = make_icode_pregs(NULL, dest);
	ii->dat = n_xmemdup(&val, sizeof(int));
	append_icode_list(il, ii);
}

void
icode_make_power_lis(struct reg *dest, int val, struct icode_list *il) {
	struct icode_instr	*ii;

	ii = generic_icode_make_instr(NULL, NULL, INSTR_POWER_LIS);
	ii->dest_pregs = make_icode_pregs(NULL, dest);
	ii->dat = n_xmemdup(&val, sizeof(int));
	append_icode_list(il, ii);
}

void
icode_make_power_loadup4(struct reg *dest, struct vreg *src,
	struct icode_list *il) {

	struct icode_instr	*ii;

	ii = generic_icode_make_instr(NULL, src, INSTR_POWER_LOADUP4);
	ii->dest_pregs = make_icode_pregs(NULL, dest);
	append_icode_list(il, ii);
}

void
icode_make_power_fctiwz(struct reg *r, struct icode_list *il) {
	struct icode_instr	*ii;

	ii = generic_icode_make_instr(NULL, NULL, INSTR_POWER_FCTIWZ);
	ii->dat = r;
	append_icode_list(il, ii);
}

/*
 * =====================================================
 * AMD64-specific instructions
 * =====================================================
 */
void
icode_make_amd64_cvtsi2sd(
	struct reg *dest,
	struct vreg *src,
	struct icode_list *il) {

	struct icode_instr	*ii;

	ii = generic_icode_make_instr(NULL, src, INSTR_AMD64_CVTSI2SD);
	ii->dest_pregs = make_icode_pregs(NULL, dest);
	append_icode_list(il, ii);
}

void
icode_make_amd64_cvtsi2ss(
	struct reg *dest,
	struct vreg *src,
	struct icode_list *il) {

	struct icode_instr	*ii;

	ii = generic_icode_make_instr(NULL, src, INSTR_AMD64_CVTSI2SS);
	ii->dest_pregs = make_icode_pregs(NULL, dest);
	append_icode_list(il, ii);
}

void
icode_make_amd64_cvttss2si(
	struct reg *dest,
	struct reg *src,
	struct icode_list *il) {

	struct icode_instr	*ii;

	ii = generic_icode_make_instr(NULL, NULL, INSTR_AMD64_CVTTSS2SI);
	ii->dest_pregs = make_icode_pregs(NULL, dest);
	ii->src_pregs = make_icode_pregs(NULL, src);
	append_icode_list(il, ii);
}

void
icode_make_amd64_cvttsd2si(
	struct reg *dest,
	struct reg *src,
	struct icode_list *il) {

	struct icode_instr	*ii;

	ii = generic_icode_make_instr(NULL, NULL, INSTR_AMD64_CVTTSD2SI);
	ii->dest_pregs = make_icode_pregs(NULL, dest);
	ii->src_pregs = make_icode_pregs(NULL, src);
	append_icode_list(il, ii);
}

void
icode_make_amd64_cvtss2sd(struct reg *r, struct icode_list *il) {
	struct icode_instr	*ii;

	ii = generic_icode_make_instr(NULL, NULL, INSTR_AMD64_CVTSS2SD);
	ii->dat = r;
	append_icode_list(il, ii);
}

void
icode_make_amd64_cvtsd2ss(struct reg *r, struct icode_list *il) {
	struct icode_instr	*ii;

	ii = generic_icode_make_instr(NULL, NULL, INSTR_AMD64_CVTSD2SS);
	ii->dat = r;
	append_icode_list(il, ii);
}

struct icode_instr *
icode_make_seqpoint(struct var_access *stores) {
	struct icode_instr	*ret = alloc_icode_instr();
	ret->type = INSTR_SEQPOINT;
	ret->dat = stores;
	return ret;
}

struct icode_instr *
icode_make_debug(const char *fmt, ...) {
	struct icode_instr	*ret = alloc_icode_instr();
	char			buf[2048];
	va_list			va;

	va_start(va, fmt);
	vsprintf(buf, fmt, va);
	va_end(va);

	ret->type = INSTR_DEBUG;
	ret->dat = n_xstrdup(buf);
	return ret;
}

void
icode_make_dbginfo_line(struct statement *stmt, struct icode_list *il) {
	struct icode_instr	*ii = NULL;
	struct decl		*dec;
	struct expr		*ex;

	if (!gflag) return;

	switch (stmt->type) {
	case ST_CODE:
		ii = alloc_icode_instr();
		ii->type = INSTR_DBGINFO_LINE;
		ex = stmt->data;
		ii->dat = ex->tok;
		break;
	case ST_DECL:
		/* A declaration with initializer has ``code'' */
		dec = stmt->data;
		if (dec->init != NULL) {
			ii = alloc_icode_instr();
			ii->type = INSTR_DBGINFO_LINE;
			ex = dec->init->data;
			ii->dat = ex->tok; 
		}
		break;
	default:
		abort();
	}
	if (ii != NULL) { /* XXX */
		append_icode_list(il, ii);
	}	
}



int	unimpl_instr;

void
icode_make_unimpl(struct icode_list *il) {
	struct icode_instr	*ii = alloc_icode_instr();

	unimpl_instr = 1;
	ii->type = INSTR_UNIMPL;
	append_icode_list(il, ii);
}	

void
icode_make_copyinit(struct decl *d, struct icode_list *il) {
	struct icode_instr	*ii = alloc_icode_instr();

	ii->type = INSTR_COPYINIT;
	ii->dat = d; 
	append_icode_list(il, ii);
}

/*
 * Copy struct src to struct dest. Both may be automatic, static,  
 * indirect, etc. If dest is a null pointer, then it is assumed that
 * the current function returns a structure type, and src will be
 * copied to the hidden pointer for struct returns (hidden_pointer
 * member of ``struct function''). This only works with the MIPS
 * backend right now.
 *
 * If any argument comes from a pointer, that pointer will be loaded
 * into a register as necessary. Note that most backends currently
 * call memcpy() to copy structs, so it is essential to save all GPRs
 * as necessary (backend->invalidate_gprs().) Perhaps it would make
 * sense to add a backend->prepare_copystruct() for register saving.
 *
 * XXX seems dest_preg and src_preg are never used and conceptually
 * totally nonsense because a struct is never gpr-resident
 */
void
icode_make_copystruct(
	struct vreg *dest,
	struct vreg *src,
	struct icode_list *il) {

	struct icode_instr	*ii = alloc_icode_instr();
	struct copystruct		*cs;
	struct vreg			*stop;
	static struct copystruct	nullcs;

	cs = n_xmalloc(sizeof *cs);
	*cs = nullcs;

	/* If source is indirect, do not trash pointer register */
	vreg_set_unallocatable(src);

	cs->dest_vreg = dest;
	if (dest != NULL) {
		vreg_faultin_ptr(dest, il);
		cs->dest_preg = dest->pregs[0];
		if (dest->from_ptr) {
			cs->dest_from_ptr = dest->from_ptr->pregs[0];
		} else if (dest->parent
			&& (stop = get_parent_struct(dest))->from_ptr) {
			cs->dest_from_ptr = NULL;
			cs->dest_from_ptr_struct = stop->from_ptr->pregs[0];
		} else {
			cs->dest_from_ptr = NULL;
		}	
		vreg_set_unallocatable(dest);
	}
	cs->src_vreg = src;
	cs->src_preg = src->pregs[0];

	vreg_faultin_ptr(src, il);
	vreg_set_allocatable(src);
	if (dest != NULL) {
		vreg_set_allocatable(dest);
	}
	if (src->from_ptr) {
		cs->src_from_ptr = src->from_ptr->pregs[0];
	} else if (src->parent
		&& (stop = get_parent_struct(src))->from_ptr) {
		cs->src_from_ptr = NULL;
		cs->src_from_ptr_struct = stop->from_ptr->pregs[0];
	} else {
		cs->src_from_ptr = NULL;
	}

	if (backend->icode_make_structreloc) {
		backend->icode_make_structreloc(cs, il);
	}

	ii->type = INSTR_COPYSTRUCT;
	ii->dat = cs;
	append_icode_list(il, ii);
}

/*
 * Intrinsically copy nbytes of src to dest. Currently dest and src must
 * be pointers or arrays. This is currently only used by __builtin_memcpy,
 * but should eventually replace INSTR_COPYSTRUCT and INSTR_COPYINIT.
 * If may_call_lib is set, the library memcpy() may be called, which
 * makes sense for huge data items (the primary point of __builtin_memcpy
 * is probably to avoid library dependencies.)
 */
void
icode_make_intrinsic_memcpy(struct vreg *dest,
	struct vreg *src,
	struct vreg *nbytes,
	int may_call_lib,
	struct icode_list *il) {

	struct icode_instr	*ii = alloc_icode_instr();
	struct int_memcpy_data	*imdata = n_xmalloc(sizeof *imdata);
	struct reg		*dest_reg;
	struct reg		*src_reg;
	struct reg		*temp_reg;

	ii->type = INSTR_INTRINSIC_MEMCPY;

	if (src->type->tlist == NULL
		|| src->type->tlist == NULL) {
		/*
		 * In the future we should allow copying non-
		 * pointer objects too (take the address here)
		 * for convenient struct and initializer copying
		 */
		unimpl();
	}

	vreg_faultin_ptr(dest, il);
	if (dest->from_ptr) {
		dest_reg = dest->from_ptr->pregs[0];
	} else {
		/* Array or function */
		dest_reg = dest->pregs[0];
	}
	reg_set_unallocatable(dest_reg);

	vreg_faultin_ptr(src, il);
	if (src->from_ptr) {
		src_reg = src->from_ptr->pregs[0];
	} else {
		/* Array or function */
		src_reg = src->pregs[0];
	}
	reg_set_unallocatable(src_reg);

	vreg_faultin(NULL, NULL, nbytes, il, 0);
	reg_set_unallocatable(nbytes->pregs[0]);

	/*
	 * Now allocate temporary register for holding one byte. This
	 * will be used for byte-wise simplistic copying by all
	 * backends until more sophisticated implementations are
	 * written. Then it will be time to have some sort of backend
	 * flag which tells us whether such a temp reg is needed or
	 * not, and what else may have to be done here
	 */
	temp_reg = ALLOC_GPR(curfunc, 1, il, NULL);
	
	reg_set_allocatable(dest_reg);
	reg_set_allocatable(src_reg);
	reg_set_allocatable(nbytes->pregs[0]);

	imdata->dest_addr = dest_reg;
	imdata->src_addr = src_reg;
	imdata->nbytes = nbytes->pregs[0];
	imdata->temp_reg = temp_reg;
	if (may_call_lib /*  && makes_sense_to_call_lib */) {
		imdata->dest_addr->used = 0;
		imdata->src_addr->used = 0;
		imdata->nbytes->used = 0;
		imdata->temp_reg->used = 0;
		backend->invalidate_gprs(il, 1);
	}
	ii->dat = imdata;
	append_icode_list(il, ii);
}

void
icode_make_dealloca(struct stack_block *sb, struct icode_list *il) {
	struct icode_instr	*ii = alloc_icode_instr();
	struct reg		*r;
	struct type		*st;

	st = backend->get_size_t();

	r = backend->get_abi_reg(0, st);
	if (r == NULL) {
		r = ALLOC_GPR(curfunc, backend->get_sizeof_type(st, 0), il, 0); 
	}

	ii->type = INSTR_DEALLOCA;
	ii->src_pregs = make_icode_pregs(NULL, r);
	ii->dat = sb;
	append_icode_list(il, ii);
}

void
icode_make_alloca(struct reg *r, struct vreg *size_vr,
	struct stack_block *sb,
	struct icode_list *il) {

	struct allocadata	*a = n_xmalloc(sizeof *a);
	struct icode_instr	*ii = alloc_icode_instr();

	/*
	 * First we unconditionally free the hidden pointer slot  
	 * for this allocation. Such that
	 *
	 *   for (i = 0; i < 10; ++i) {
	 *      void *p = __builtin_alloca(20);
	 *
	 * ... always performs a free() before overwriting the
	 * saved pointer with a newly allocated one. This is OK
	 * even at the first call because the save area is
	 * initialized to all-null-pointers, and free(NULL) is ok
	 *
	 * XXX 08/09/07: This is completely wrong. Removed the
	 * free, thus repeated allocas leak memory now. But better
	 * than not having it work at all
	 */
	backend->invalidate_gprs(il, 1);
#if 0
	icode_make_dealloca(sb, il);
#endif

	reg_set_unallocatable(r);
	vreg_faultin(NULL, NULL, size_vr, il, 0);
	a->result_reg = r;
	a->size_reg = size_vr->pregs[0];
	a->addr = sb;

	ii->dat = a;
	ii->type = INSTR_ALLOCA;

	/*
	 * Because the current implementation is carried out
	 * using malloc() and free(), we have to save all
	 * registers except for the size argument register!
	 */
	a->result_reg->used = 0;
	a->size_reg->used = 0;
	reg_set_allocatable(r);
	backend->invalidate_gprs(il, 1);
	append_icode_list(il, ii);
#if 0
	store_reg_to_stack_block(a->result_reg, a->addr);
#endif
}

void
icode_make_dealloc_vla(struct stack_block *sb, struct icode_list *il) {
	struct icode_instr	*ii = alloc_icode_instr();
	struct type		*st;

	st = backend->get_size_t();

#if 0
	r = backend->get_abi_reg(0, st);
	if (r == NULL) {
		r = ALLOC_GPR(curfunc, backend->get_sizeof_type(st, 0), il, 0); 
	}
#endif

	ii->type = INSTR_DEALLOC_VLA;
#if 0
	ii->src_pregs = make_icode_pregs(NULL, r);
#endif
	ii->dat = sb;
	append_icode_list(il, ii);
}

void
icode_make_alloc_vla(
	struct stack_block *sb,
	struct icode_list *il) {

	struct allocadata	*a = n_xmalloc(sizeof *a);
	struct icode_instr	*ii = alloc_icode_instr();

	/*
	 * First we unconditionally free the hidden pointer slot  
	 * for this allocation. Such that
	 *
	 *   for (i = 0; i < 10; ++i) {
	 *      void *p = __builtin_alloca(20);
	 *
	 * ... always performs a free() before overwriting the
	 * saved pointer with a newly allocated one. This is OK
	 * even at the first call because the save area is
	 * initialized to all-null-pointers, and free(NULL) is ok
	 */
	backend->invalidate_gprs(il, 1);
	icode_make_dealloc_vla(sb, il);

	ii->dat = sb;
	ii->type = INSTR_ALLOC_VLA;

	/*
	 * Because the current implementation is carried out
	 * using malloc() and free(), we have to save all
	 * registers
	 */
	backend->invalidate_gprs(il, 1);
	append_icode_list(il, ii);
}

void
icode_make_builtin_frame_address(struct reg *r, struct reg *r2,
	size_t *n, struct icode_list *il) {

	struct icode_instr		*ii = alloc_icode_instr();
	struct builtinframeaddressdata	*dat = n_xmalloc(sizeof *dat);

	ii->type = INSTR_BUILTIN_FRAME_ADDRESS;
	dat->result_reg = r;
	dat->temp_reg = r2;
	dat->count = n;
	
	ii->dat = dat;
	append_icode_list(il, ii);
}


void
icode_make_put_vla_whole_size(struct reg *size, struct stack_block *sb,
	struct icode_list *il) {

	struct icode_instr	*ii = alloc_icode_instr();
	struct vlasizedata	*dat = n_xmalloc(sizeof *dat);

	ii->type = INSTR_PUT_VLA_SIZE;
	dat->size = size;
	dat->offset = backend->get_ptr_size();
	dat->blockaddr = sb;
	ii->dat = dat;
	append_icode_list(il, ii);
}


void
icode_make_put_vla_size(struct reg *size, struct stack_block *sb,
	int idx, struct icode_list *il) {

	struct icode_instr	*ii = alloc_icode_instr();
	struct vlasizedata	*dat = n_xmalloc(sizeof *dat);
	int			offset;

	offset = backend->get_ptr_size()
		+ (1 + idx) * backend->get_sizeof_type(
				make_basic_type(TY_ULONG), NULL);
	
	ii->type = INSTR_PUT_VLA_SIZE;
	dat->size = size;
	dat->blockaddr = sb;
	dat->offset = offset;
	ii->dat = dat;
	append_icode_list(il, ii);
}

struct vreg *
icode_make_retr_vla_size(struct reg *size, struct stack_block *sb,
	int idx, struct icode_list *il) {

	struct icode_instr	*ii = alloc_icode_instr();
	struct vlasizedata	*dat = n_xmalloc(sizeof *dat);
	struct vreg		*ret;
	int			offset;

	offset = backend->get_ptr_size()
		+ (1 + idx) * backend->get_sizeof_type(
				make_basic_type(TY_ULONG), NULL);
	
	
	ii->type = INSTR_RETR_VLA_SIZE;
	dat->size = size;
	dat->blockaddr = sb;
	dat->offset = offset;
	ii->dat = dat;
	append_icode_list(il, ii);
	ret = vreg_alloc(NULL, NULL, NULL, NULL);
	vreg_set_new_type(ret, make_basic_type(TY_ULONG));
	vreg_map_preg(ret, size);
	return ret;
}

void
icode_make_allocstack(struct vreg *vr, size_t size, struct icode_list *il) {
	struct icode_instr	*ii = alloc_icode_instr();
	struct allocstack	*as = n_xmalloc(sizeof *as);

	as->nbytes = size;
	as->patchme = vr;

	ii->type = INSTR_ALLOCSTACK;
	ii->dat = as;
	append_icode_list(il, ii);
}

/*
 * XXX should this really deal with multi-gpr objects?!  
 */
struct icode_instr *
icode_make_cmp(struct vreg *dest, struct vreg *src) {
	struct icode_instr	*ret = alloc_icode_instr();

	ret->type = INSTR_CMP;
	ret->dest_vreg = dest;
	ret->dest_pregs = make_icode_pregs(dest, NULL);
	if (src != NULL) {
		ret->src_pregs = make_icode_pregs(src, NULL);
		ret->src_vreg = src;
	}
	return ret;
}

void
icode_make_extend_sign(struct vreg *vr, struct type *to, struct type *from,
	struct icode_list *il) {

	struct icode_instr	*ret = alloc_icode_instr();
	struct extendsign	*data = n_xmalloc(sizeof *data);

	data->dest_preg = vr->pregs[0];
	data->dest_type = to;
	data->src_type = from;
	ret->dat = data;
	ret->type = INSTR_EXTEND_SIGN;
	append_icode_list(il, ret);
}

/* XXX misleading order of to/from :/ */
void
icode_make_conv_fp(struct reg *destr, struct reg *srcr,
	struct type *to, struct type *from,
	struct icode_list *il) {

	struct icode_instr	*ret = alloc_icode_instr();
	struct extendsign	*data = n_xmalloc(sizeof *data);

	data->dest_preg = destr;
	data->src_preg = srcr;
	data->dest_type = to;
	data->src_type = from;
	ret->dat = data;
	ret->type = INSTR_CONV_FP;
	append_icode_list(il, ret);
}

struct icode_instr *
icode_make_jump(struct icode_instr *label) {
	struct icode_instr	*ret = alloc_icode_instr();
	ret->type = INSTR_JUMP;
	ret->dat = label;
	return ret;
}

void
put_label_scope(struct label *l) {
	struct statement	*stmt;

	stmt = alloc_statement();
	stmt->type = ST_LABEL;
	stmt->data = l;
	append_statement(
		&curscope->code, &curscope->code_tail, stmt);
	append_label_list(&curfunc->labels_head, &curfunc->labels_tail, l);
}


void
put_expr_scope(struct expr *e) {
	struct statement	*stmt;

	stmt = alloc_statement();
	stmt->type = ST_CODE;
	stmt->data = e;
	append_statement(&curscope->code, &curscope->code_tail, stmt);
}

void
put_ctrl_scope(struct control *c) {
	struct statement	*stmt;

	stmt = alloc_statement();
	stmt->type = ST_CTRL;
	stmt->data = c;
	append_statement(&curscope->code, &curscope->code_tail, stmt);
}

/*
 * XXX acts as fxch for x86 fp...is this ok?
 */
void
icode_make_copyreg(
        struct reg *dest,
        struct reg *src,
        struct type *desttype,
        struct type *srctype,
        struct icode_list *il) {

        struct icode_instr      *ii = alloc_icode_instr();
        struct copyreg          *cr = n_xmalloc(sizeof *cr);

        cr->src_preg = src;
        cr->dest_preg = dest;
        cr->src_type = srctype;
        cr->dest_type = desttype;

        ii->type = INSTR_MOV;
        ii->dat = cr;

        append_icode_list(il, ii);
}



syntax highlighted by Code2HTML, v. 0.9.1