/*
 * 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.
 *
 * Parsing of, and icode generation for, builtin functions.
 * This stuff is mostly kinda ad-hoc and ugly :-(
 *
 * Maybe parsing and code generation should be separated as this
 * file keeps growing.
 *
 * Implemented builtins:
 *
 *      __builtin_va_start
 *      __builtin_va_stdarg_start
 *      __builtin_va_arg
 *      __builtin_va_next_arg
 *      __builtin_va_end
 *      __builtin_memcpy
 *      __builtin_va_expect
 *            NOTE: This one is just ignored
 *      __builtin_va_alloca
 *            NOTE: This one uses malloc()/free() instead of the stack. Also,
 *                  it does not honor block scope, so every allocated block
 *                  is kept until the current function is left
 *      __builtin_va_copy
 *            NOTE: No typechecking
 */
#include "builtins.h"
#include <string.h>
#include <stdlib.h>
#include "icode.h"
#include "token.h"
#include "error.h"
#include "subexpr.h"
#include "icode.h"
#include "functions.h"
#include "backend.h"
#include "expr.h"
#include "type.h"
#include "symlist.h"
#include "scope.h"
#include "typemap.h"
#include "amd64_gen.h"
#include "token.h"
#include "backend.h"
#include "decl.h"
#include "reg.h"
#include "x87_nonsense.h"
#include "n_libc.h"

struct type	*builtin_va_list_type;

/*
 * 08/22/07: This stuff was nonsense for va_arg()s first argument, but
 * seems to still make some sense for va_start()
 */
static char *
get_ident(struct expr *ex) {
	struct token	*idtok;

	if (ex->op != 0) {
		return NULL;
	}
	if (ex->data->meat != NULL) {
		idtok = ex->data->meat;
	} else if (ex->data->is_expr) {
		if (ex->data->is_expr->op != 0) {
			return NULL;
		} else if (ex->data->is_expr->data->meat != NULL) {
			idtok = ex->data->is_expr->data->meat;
		} else {
			return NULL;
		}	
	} else {
		return NULL;
	}	
	return idtok->ascii;
}	


static int
builtin_parse_va_start(struct token **tok, struct fcall_data *fdat) {
	struct token		*t = *tok;
	struct decl		*dec;
	struct expr		*ex;
	struct sym_entry	*se;
	char			*ident;
	int			i;

	if ((ex = parse_expr(&t, TOK_OP_COMMA, 0, 0, 1)) == NULL) {
		return -1;
	}	
	fdat->builtin->args[0] = ex;
	fdat->builtin->args[1] = NULL;
	if (next_token(&t) != 0) {
		return -1;
	}	

	if ((ex = parse_expr(&t, TOK_PAREN_CLOSE, 0, 0, 1)) == NULL) {
		return -1;
	}	
#if 0
	if (ex->op != 0 || ex->data->meat->type != TOK_IDENTIFIER) {
		errorfl(t, "Bad second argument to va_start() - "
			"Identifier expected");
		return -1;
	}
#endif
	if ((ident = get_ident(ex)) == NULL) {
		errorfl(t, "Bad second argument to va_start() - "
			"Identifier expected");
		return -1;
	}	

	if ((dec = lookup_symbol(curfunc->scope, ident, 0)) == NULL) {
		errorfl(t, "Undeclared identifier `%s'", ident);
		return -1;
	}	

	for (i = 0, se = curfunc->scope->slist; se != NULL; se = se->next) {
		++i;
		if (i == curfunc->proto->dtype->tlist->tfunc->nargs) {
			break;
		}	
	}	
	if (se == NULL || se->dec != dec) {
		errorfl(t, "Second argument to va_start() is not %s "
			"parameter of `%s'",
			se == NULL? "a": "last",
			curfunc->proto->dtype->name);
		return -1;
	}	

	if (t->type != TOK_PAREN_CLOSE) {
		errorfl(t, "Syntax error at `%s'", t->ascii);
		return -1;
	}	

	*tok = t;
	return 0;
}

static int
builtin_parse_next_arg(struct token **tok, struct fcall_data *fdat) {
	struct expr	*ex;
	struct token	*t = *tok;

	(void) fdat;
	if ((ex = parse_expr(&t, TOK_PAREN_CLOSE, 0, 0, 1)) == NULL) {
		return -1;
	}
	if (ex->op != 0 || ex->data->meat->type != TOK_IDENTIFIER) {
		errorfl(t, "Bad second argument to va_start() - "
			"Identifier expected");
		return -1;
	}
	*tok = t;
	return 0;
}	

static int
builtin_parse_va_end(struct token **tok, struct fcall_data *fdat) {
	struct token	*t = *tok;
	struct expr	*ex;

	(void) fdat;

	if ((ex = parse_expr(&t, TOK_PAREN_CLOSE, 0, 0 ,1)) == NULL) {
		return -1;
	}
	if (ex->op != 0 || ex->data->meat->type != TOK_IDENTIFIER) {
		errorfl(t, "Bad second argument to va_start() - "
			"Identifier expected");
		return -1;
	}
	*tok = t;
	return 0;
}

static int
builtin_parse_va_arg(struct token **tok, struct fcall_data *fdat) {
	struct decl	**typedec;
	struct token	*starttok;
	struct token	*t = *tok;
	struct expr	*ex;
	struct type	*type;

	if ((ex = parse_expr(&t, TOK_OP_COMMA, 0, 0, 1)) == NULL) {
		return -1;
	}	

#if 0
	if ((ident = get_ident(ex)) == NULL) {
		errorfl(t, "Bad second argument to va_arg() - "
			"Identifier expected");
		return -1;
	}	
	if ((dec = lookup_symbol(curscope, ident, 1)) == NULL) {
		errorfl(t, "Undeclared identifier `%s'", ident);
		return -1;
	}	
	
	fdat->builtin->args[0] = dec;
#endif
	fdat->builtin->args[0] = ex;

	if (next_token(&t) != 0) {	
		return -1;
	}

	starttok = t;
	if ((typedec = parse_decl(&t, DECL_CAST)) == NULL) {
		return -1;
	}	
	type = typedec[0]->dtype;
	free(typedec);

	fdat->builtin->args[1] = type;
	fdat->builtin->args[2] = NULL;

#if 0
	if (is_basic_agg_type(type)) {
		unimpl();
	}
#endif

	*tok = t;
	return 0;
}

static int
builtin_parse_expect(struct token **tok, struct fcall_data *fdat) {
	struct expr	*ex;
	struct expr	*ex2;
	struct token	*t = *tok;
	struct token	*second_start;

	if ((ex = parse_expr(&t, TOK_OP_COMMA, 0, 0, 1)) == NULL) {
		return -1;
	}
	/* hmm - gcc warns about first argument of pointer type */
	if (next_token(&t) != 0) {
		return -1;
	}		
	second_start = t;
	if ((ex2 = parse_expr(&t, TOK_PAREN_CLOSE, 0, EXPR_CONST, 1)) == NULL) {
		return -1;
	}
#if 0
	/* const_value is always null!?!??!?!?!?!?!??? :-( :-( :-( */
	if (!is_integral_type(ex2->const_value->type)) {
		/* hmm - gcc accepts stuff like 0.0f */
		errorfl(second_start,
			"Second argument of __builtin_expect not integer constant");
		return -1;
	}
#endif
	fdat->builtin->args[0] = ex;
	fdat->builtin->args[1] = ex2;
	fdat->builtin->type = BUILTIN_EXPECT;
	*tok = t;
	return 0;
}

static int
generic_builtin_parse_alloca(struct token **tok, struct fcall_data *fdat) {
	struct expr	*ex;

	if ((ex = parse_expr(tok, TOK_PAREN_CLOSE, 0, 0, 1)) == NULL) {
		return -1;
	}
	fdat->builtin->args[0] = ex;
	return 0;
}

static int
generic_builtin_parse_va_copy(struct token **tok, struct fcall_data *fdat) {
	struct expr	*ex;
	struct expr	*ex2;

	if ((ex = parse_expr(tok, TOK_OP_COMMA, 0, 0, 1)) == NULL) {
		return -1;
	}
	if (next_token(tok) != 0) {
		return -1;
	}
	if ((ex2 = parse_expr(tok, TOK_PAREN_CLOSE, 0, 0, 1)) == NULL) {
		return -1;
	}
	fdat->builtin->args[0] = ex;
	fdat->builtin->args[1] = ex2;
	return 0;
}

static int
generic_builtin_parse_memcpy(struct token **tok, struct fcall_data *fdat) {
	struct expr	*ex;
	struct expr	*ex2;
	struct expr	*ex3;

	if ((ex = parse_expr(tok, TOK_OP_COMMA, 0, 0, 1)) == NULL) {
		return -1;
	}
	if (next_token(tok) != 0) {
		return -1;
	}
	if ((ex2 = parse_expr(tok, TOK_OP_COMMA, 0, 0, 1)) == NULL) {
		return -1;
	}
	if (next_token(tok) != 0) {
		return -1;
	}
	if ((ex3 = parse_expr(tok, TOK_PAREN_CLOSE, 0, 0, 1)) == NULL) {
		return -1;
	}
	fdat->builtin->args[0] = ex;
	fdat->builtin->args[1] = ex2;
	fdat->builtin->args[2] = ex3;
	return 0;
}

static int
generic_builtin_parse_offsetof(struct token **tok, struct fcall_data *fdat) {
	struct token	*t = *tok;
	struct token	*starttok = t;
	struct token	*ident;
	struct decl	**typedec;
	struct decl	*d;

	if ((typedec = parse_decl(&t, DECL_FUNCARG)) == NULL) {
		return -1;
	}
	if (typedec[0]->dtype->code != TY_STRUCT
		|| typedec[0]->dtype->tlist != NULL) {
		errorfl(starttok, "First operand of __builtin_offsetof() "
			"is not structure type");
		return -1;
	}
	if (t->type != TOK_OPERATOR
		|| *(int *)t->data != TOK_OP_COMMA) {
		errorfl(t, "Syntax error at `%s'", t->ascii);
		return -1;
	}
	if (next_token(&t) != 0) {
		return -1;
	}
	if (t->type != TOK_IDENTIFIER) {
		errorfl(t, "Second operand of __builtin_offsetof() must be "
			"identifier");	
		return -1;
	}
	ident = t;
	if (next_token(&t) != 0) {
		return -1;
	}
	if (t->type != TOK_PAREN_CLOSE) {
		errorfl(t, "Syntax error at `%s' - expected closing parentheses",
			t->ascii);
		return -1;
	}
	if ((d = access_symbol(typedec[0]->dtype->tstruc->scope,
		ident->data, 0)) == NULL) {
		return -1;
	}
	*tok = t;
	fdat->builtin->args[0] = d;
	fdat->builtin->type = BUILTIN_OFFSETOF;
	return 0;
}

static int
generic_builtin_parse_frame_address(struct token **tok, struct fcall_data *fdat) {
	struct expr	*ex;
	struct token	*t;
	size_t		frameno;

	if ((ex = parse_expr(tok, TOK_PAREN_CLOSE, 0, EXPR_CONST, 1)) == NULL) {
		return -1;
	}
	if (ex->const_value == NULL) {
		errorfl(t, "Argument to __builtin_frame_address() not "
			"constant");
		return -1;
	}
	if (!is_integral_type(ex->const_value->type)) {
		errorfl(t, "Argument to __builtin_frame_address() not "
			"integral");
		return -1;
	}
	cross_do_conv(ex->const_value, TY_INT);
	frameno = cross_to_host_size_t(ex->const_value); /* XXXX CROSS??? */

	if (frameno > 0) {
		errorfl(t, "__builtin_frame_address() currently only "
			"works with the current stack frame (argument 0)!");
		return -1;
	}
	fdat->builtin->args[0] = n_xmemdup(&frameno, sizeof frameno);
	return 0;
}


static struct vreg *
generic_builtin_va_copy_to_icode(struct fcall_data *fdat,
	struct icode_list *il) {
	struct vreg		*vr = vreg_alloc(NULL, NULL, NULL, NULL);
	struct vreg		*arg0;
	struct vreg		*arg1;
	struct vreg		*tmpvr;
	struct expr		*ex0 = fdat->builtin->args[0];
	struct expr		*ex1 = fdat->builtin->args[1];
	struct icode_instr	*ii;
	struct token		*tok;
	int			size;
	int			va_list_is_array = 0;

	arg0 = expr_to_icode(ex0, NULL, il, 0, 1);
	if (arg0 == NULL) {
		return NULL;
	}
	arg1 = expr_to_icode(ex1, NULL, il, 0, 1);
	if (arg1 == NULL) {
		return NULL;
	}

	/*
	 * XXX this is just a quick kludge... we will later want to do
	 * full type-checking! Ideally use unary_to_icode() from
	 * subexpr.c for this to determine whether the address can be
	 * taken
	 */
	size = backend->get_sizeof_type(arg0->type, NULL);

	if (backend->arch == ARCH_AMD64) {
		/*
		 * XXX use backend->va_list_is_sysv_like?!?!
		 * This must be done on Linux/PPC too
		 */ 
		va_list_is_array = 1;
		vreg_faultin(NULL, NULL, arg0, il, 0);
		size = 24;  /* XXX because the source really is an array! */
	} else {
		ii = icode_make_addrof(arg0, il);
		append_icode_list(il, ii);
		arg0 = vreg_disconnect(arg0);
		arg0->type = addrofify_type(arg0->type);
		arg0->size = backend->get_sizeof_type(arg0->type, NULL);
		vreg_map_preg(arg0, ii->dat);
	}
	reg_set_unallocatable(arg0->pregs[0]);

	if (!va_list_is_array) {
		ii = icode_make_addrof(arg1, il);
		append_icode_list(il, ii);
		arg1 = vreg_disconnect(arg1);
		arg1->type = addrofify_type(arg1->type);
		arg1->size = backend->get_sizeof_type(arg1->type, NULL);
		vreg_map_preg(arg1, ii->dat);
	} else {	
		vreg_faultin(NULL, NULL, arg1, il, 0);
	}	

	reg_set_unallocatable(arg1->pregs[0]);

	tok = const_from_value(&size, make_basic_type(TY_INT));
	tmpvr = vreg_alloc(NULL, tok, NULL, make_basic_type(TY_INT));
	vreg_faultin(NULL, NULL, tmpvr, il, 0);
	
	reg_set_allocatable(arg0->pregs[0]);
	reg_set_allocatable(arg1->pregs[0]);
	icode_make_intrinsic_memcpy(arg0, arg1, tmpvr, 0, il);
	
	vr->type = make_basic_type(TY_VOID);
	return vr;
}

static struct vreg *
generic_builtin_memcpy_to_icode(struct fcall_data *fdat,
	struct icode_list *il) {
	struct vreg	*resvr = vreg_alloc(NULL, NULL, NULL, NULL);
	struct vreg	*arg0;
	struct vreg	*arg1;
	struct vreg	*arg2;
	struct expr	*ex0 = fdat->builtin->args[0];
	struct expr	*ex1 = fdat->builtin->args[1];
	struct expr	*ex2 = fdat->builtin->args[2];

	resvr->type = n_xmemdup(make_basic_type(TY_VOID), sizeof(struct type));
	append_typelist(resvr->type, TN_POINTER_TO, 0, NULL, NULL);
	resvr->size = backend->get_sizeof_type(resvr->type, NULL);

	arg0 = expr_to_icode(ex0, NULL, il, 0, 1);
	if (arg0 == NULL) {
		return NULL;
	}
	if (arg0->type->tlist == NULL) {
		errorfl(ex0->tok, "First argument to __builtin_memcpy "
			"must be pointer");
		return NULL;
	}

	/*
	 * Now save target address to stack (this is the return value
	 * of __builtin_memcpy() and memcpy() generally.)
	 */
	vreg_faultin(NULL, NULL, arg0, il, 0);	
	vreg_map_preg(resvr, arg0->pregs[0]);
	free_preg(resvr->pregs[0], il, 0, 1); /* save, but don't invalidate */
	vreg_map_preg(arg0, resvr->pregs[0]);
	
	arg1 = expr_to_icode(ex1, NULL, il, 0, 1);
	if (arg1 == NULL) {
		return NULL;
	}
	if (arg1->type->tlist == NULL) {
		errorfl(ex1->tok, "Second argument to __builtin_memcpy "
			"must be pointer");
		return NULL;
	}
	arg2 = expr_to_icode(ex2, NULL, il, 0, 1);
	if (arg2 == NULL) {
		return NULL;
	}
	if (!is_integral_type(arg2->type)) {
		errorfl(ex2->tok, "Third argument to __builtin_memcpy "
			"must be integral type");
		return NULL;
	}
	icode_make_intrinsic_memcpy(arg0, arg1, arg2, 0, il);
	return resvr;
}


static struct vreg *
generic_builtin_alloca_to_icode(struct fcall_data *fdat,
	struct icode_list *il) {

	struct vreg		*vr = vreg_alloc(NULL, NULL, NULL, NULL);
	struct vreg		*size_vr;
	struct type		*ty;
	struct stack_block	*sb;
	struct reg		*r;
	struct expr		*ex = fdat->builtin->args[0];
	
	size_vr = expr_to_icode(ex, NULL, il, 0, 1);
	if (size_vr == NULL) {
		return NULL;
	}
	if (!is_integral_type(size_vr->type)) {
		errorfl(ex->tok, "Argument not of integral type");
		return NULL;
	}	

	/* Result type is ``void *'' */
	ty = n_xmemdup(make_basic_type(TY_VOID), sizeof *ty);
	append_typelist(ty, TN_POINTER_TO, 0, NULL, NULL);
	vr->type = ty;
	vr->size = backend->get_sizeof_type(ty, NULL);

	/*
	 * Now allocate a stack block in which to store the returned
	 * pointer, so that we can free it upon return from the function.
	 * As usual the offset is patched in gen_function()
	 */
	sb = make_stack_block(0, vr->size);
	if (curfunc->alloca_head == NULL) {
		curfunc->alloca_head = curfunc->alloca_tail = sb;
	} else {
		curfunc->alloca_tail->next = sb;
		curfunc->alloca_tail = sb;
	}

	/* Get register to store result pointer */
	vreg_set_unallocatable(size_vr);

	/*
	 * 11/12/07: ALLOC_GPR() is wrong here! We have to use the ABI's
	 * malloc() return address register. This stuff is kludgedx XXX
	 * XXXXXXXXXXXXXX
	 */
	r = backend->get_abi_ret_reg(make_void_ptr_type());
	free_preg(r, il, 1, 1);
#if 0
	r = ALLOC_GPR(curfunc, vr->size, il, 0);
#endif

	icode_make_alloca(r, size_vr, sb, il);
	vreg_map_preg(vr, r);

	return vr;
}

static struct vreg *
generic_builtin_offsetof_to_icode(struct fcall_data *fdat,
	struct icode_list *il) {
	
	struct vreg	*vr;
	long		val; /* XXX because const_from_type() is broken */
	struct token	*tok;
	struct decl	*d = fdat->builtin->args[0];

	val = d->offset; 
	(void) il;

#if 0
	tok = const_from_value(&val, NULL);
	vr = vreg_alloc(NULL, tok, NULL, backend->get_size_t());
#endif
	tok = alloc_token();
	tok->data = n_xmalloc(16);  /* XXX */
	tok->type = backend->get_size_t()->code;
	cross_from_host_long(tok->data, val, tok->type);
	vr = vreg_alloc(NULL, tok, NULL, NULL);
	
	return vr;
}

static struct vreg *
generic_builtin_frame_address_to_icode(struct fcall_data *fdat,
	struct icode_list *il) {

	struct vreg	*vr = vreg_alloc(NULL,NULL,NULL,NULL);
	struct type	*ty;
	struct reg	*r;
	struct reg	*r2;

	ty = n_xmemdup(make_basic_type(TY_VOID), sizeof *ty);
	append_typelist(ty, TN_POINTER_TO, 0, NULL, NULL);
	vreg_set_new_type(vr, ty);

	/*
	 * Allocate two GPRs to make walking up the stack possible
	 */
	r = ALLOC_GPR(curfunc, vr->size, il, NULL);
	r2 = ALLOC_GPR(curfunc, vr->size, il, NULL);
	icode_make_builtin_frame_address(r, r2, fdat->builtin->args[0], il);
	free_preg(r2, il, 1, 0);
	vreg_map_preg(vr, r);
	return vr;
}

static struct vreg * 
x86_builtin_va_start_to_icode(
	struct fcall_data *fdat,
	struct icode_list *il) {

	struct type		*ty;
	struct vreg		*vr = vreg_alloc(NULL, NULL, NULL, NULL);
	struct vreg		*valist_vr;
	struct icode_instr	*ii;
	
	ii = icode_make_addrof(NULL, il);
	append_icode_list(il, ii);

	valist_vr = expr_to_icode(fdat->builtin->args[0], NULL, il, 0, 1);
	if (valist_vr == NULL) {
		return NULL;
	}
	vreg_map_preg(valist_vr, ii->dat);
	icode_make_store(curfunc, valist_vr, valist_vr, il);

	free_preg(valist_vr->pregs[0], il, 0, 0);

	ty = make_basic_type(TY_VOID);
	vr->type = ty;
	vr->size = 0;
	return vr;
}

static struct vreg * 
x86_builtin_next_arg_to_icode(
	struct fcall_data *fdat,
	struct icode_list *il) {

	struct type		*ty;
	struct vreg		*vr = vreg_alloc(NULL, NULL, NULL, NULL);
	struct icode_instr	*ii;
	
	(void) fdat;
	ii = icode_make_addrof(NULL, il);
	append_icode_list(il, ii);
	
	vreg_map_preg(vr, ii->dat);

	ty = n_xmemdup(make_basic_type(TY_CHAR), sizeof *ty);
	ty->tlist = alloc_type_node();
	ty->tlist->type = TN_POINTER_TO;
	vr->type = ty;
	vr->size = backend->get_sizeof_type(ty, NULL);
	return vr;
}

static struct vreg * 
x86_builtin_va_end_to_icode(
	struct fcall_data *fdat,
	struct icode_list *il) {

	struct type	*ty;
	struct vreg	*vr = vreg_alloc(NULL, NULL, NULL, NULL);

	(void) fdat; (void) il;
	
	ty = make_basic_type(TY_VOID);
	vr->type = ty;
	vr->size = 0;
	return vr;
}

static struct vreg *
x86_builtin_va_arg_to_icode(
	struct fcall_data *fdat,
	struct icode_list *il) {

	struct vreg		*valist_vr;
	struct vreg		*vr;
	struct vreg		*tmpvr;
	struct token		*c;
	struct icode_instr	*ii;
	struct type		*argtype = fdat->builtin->args[1];
	int			size;
	int			intsize;
	
	/*
	 * 08/22/07: Wow, this stuff used get_ident() and did not
	 * generate icode for the first argument at all! Thus
	 *
	 *   va_list *foo;
	 *   va_arg(*foo, type);
	 *
	 * ... didn't work because *foo was not loaded
	 */
	valist_vr = expr_to_icode(fdat->builtin->args[0], NULL, il, 0, 1);
	if (valist_vr == NULL) {
		return NULL;
	 } /* else {
	      check type...
	 } */     
	vreg_faultin(NULL, NULL, /*valist->vreg*/valist_vr, il, 0);
	if ((argtype->code == TY_STRUCT || argtype->code == TY_UNION)
		&& argtype->tlist == NULL) {
		/* Struct/union passed by value */
		vr = vreg_alloc(NULL, NULL, /*valist->vreg*/valist_vr, NULL);
		vr->type = fdat->builtin->args[1];
		vr->size = size = backend->get_sizeof_type(vr->type, NULL);
		if (fdat->lvalue != NULL) {
			/* Result is assigned - copy */
			vreg_faultin_ptr(/*valist->vreg*/ valist_vr, il);
			vreg_faultin_ptr(fdat->lvalue, il);
			icode_make_copystruct(fdat->lvalue, vr, il);
			vr->struct_ret = 1;
		}
	} else {
		/* XXX from_ptr?? check for indirection chain!!??! */
		vr = vreg_alloc(NULL, NULL, /*valist->vreg*/ valist_vr, NULL);
		vr->type = fdat->builtin->args[1];
		vr->size = size = backend->get_sizeof_type(vr->type, NULL);

		if (is_x87_trash(vr)) {
			vr = x87_anonymify(vr, il);
		} else {
			vreg_faultin(NULL, NULL, vr, il, 0);
			vreg_anonymify(&vr, NULL, NULL, il);
		}
	}

	intsize = backend->get_sizeof_type(
		make_basic_type(backend->arch == ARCH_X86? TY_INT: TY_LONG),
		NULL);
	if (size < intsize) {
		size = intsize;
	}	
		
	c = const_from_value(&size, NULL);
	tmpvr = vreg_alloc(NULL, c, NULL, NULL);
	vreg_faultin(NULL, NULL, tmpvr, il, 0);
	vreg_faultin_protected(tmpvr, NULL, NULL, /*valist->vreg*/valist_vr, il, 0);
	ii = icode_make_add(  /*valist->vreg*/valist_vr, tmpvr);
	append_icode_list(il, ii);
	icode_make_store(curfunc, /*valist->vreg*/valist_vr, /*valist->vreg*/valist_vr, il);

	return vr;
}



static struct builtin	builtins[] = { 
	{ "va_start", sizeof "va_start" - 1,
		builtin_parse_va_start, /*x86_builtin_va_start_to_icode*/0 },
	/* va_start and stdarg_start are equivalent */	
	{ "stdarg_start", sizeof "stdarg_start" - 1,
		builtin_parse_va_start, x86_builtin_va_start_to_icode },
	{ "next_arg", sizeof "next_arg" - 1,
		builtin_parse_next_arg, x86_builtin_next_arg_to_icode },
	{ "va_end", sizeof "va_end" - 1,
		builtin_parse_va_end, x86_builtin_va_end_to_icode },
	{ "va_arg", sizeof "va_arg" - 1,
		builtin_parse_va_arg, x86_builtin_va_arg_to_icode },
	{ "expect", sizeof "expect" - 1,
		builtin_parse_expect, NULL },
	{ "alloca", sizeof "alloca" - 1,
		generic_builtin_parse_alloca, generic_builtin_alloca_to_icode },
	{ "va_copy", sizeof "va_copy" - 1,
		generic_builtin_parse_va_copy, generic_builtin_va_copy_to_icode },
	{ "memcpy", sizeof "memcpy" - 1,
		generic_builtin_parse_memcpy, generic_builtin_memcpy_to_icode },
	{ "frame_address", sizeof "frame_address" - 1,
		generic_builtin_parse_frame_address,
		generic_builtin_frame_address_to_icode },
	{ "offsetof", sizeof "offsetof" - 1,
		generic_builtin_parse_offsetof,
		generic_builtin_offsetof_to_icode },
	{ NULL, 0, NULL, NULL } 
};


static struct vreg * 
mips_builtin_next_arg_to_icode(
	struct fcall_data *fdat,
	struct icode_list *il) {
	(void) fdat; (void) il;
	unimpl();
	return NULL;
}

/*
 * XXXXXXXXXX misses struct/union arguments!
 */
static struct vreg * 
mips_builtin_va_arg_to_icode(
	struct fcall_data *fdat,
	struct icode_list *il) {

	struct vreg		*valist_vr;
	struct vreg		*vr;
	struct vreg		*tmpvr;
	struct token		*c;
	struct type		*argtype = fdat->builtin->args[1];
	struct icode_instr	*ii;
	int			size;
	int			intsize;
	
	/*
	 * 08/22/07: Wow, this stuff used get_ident() and did not
	 * generate icode for the first argument at all! Thus
	 *
	 *   va_list *foo;
	 *   va_arg(*foo, type);
	 *
	 * ... didn't work because *foo was not loaded
	 */
	valist_vr = expr_to_icode(fdat->builtin->args[0], NULL, il, 0, 1);
	if (valist_vr == NULL) {
		return NULL;
	 } /* else {
	      check type...
	 } */     
	vreg_faultin(NULL, NULL, /*valist->vreg*/valist_vr, il, 0);
	if ((argtype->code == TY_STRUCT || argtype->code == TY_UNION)
		&& argtype->tlist == NULL) {
		unimpl();
	}
	
	/* XXX from_ptr?? check for indirection chain!!??! */
	vr = vreg_alloc(NULL, NULL, /*valist->vreg*/valist_vr, NULL);
#if 0
	vr->type = fdat->builtin->args[1];
	vr->size = size = backend->get_sizeof_type(vr->type, NULL);
	vreg_faultin(NULL, NULL, vr, il, 0);
#endif

	if ((argtype->code == TY_STRUCT || argtype->code == TY_UNION)
		&& argtype->tlist == NULL) {
		/* Struct/union passed by value */
		vr = vreg_alloc(NULL, NULL, /*valist->vreg*/valist_vr, NULL);
		vr->type = fdat->builtin->args[1];
		vr->size = size = backend->get_sizeof_type(vr->type, NULL);
		if (fdat->lvalue != NULL) {
			/* Result is assigned - copy */
			vreg_faultin_ptr(/*valist->vreg*/valist_vr, il);
			vreg_faultin_ptr(fdat->lvalue, il);
			icode_make_copystruct(fdat->lvalue, vr, il);
			vr->struct_ret = 1;
		}
	} else {
		/*
		 * Small arguments are passed as dwords.
		 * XXX take fp into account!!
		 * ... so let us load the dword, then just treat it as
		 * the destination type
		 */
		tmpvr = n_xmemdup(vr, sizeof *vr);
		tmpvr->type = make_basic_type(TY_ULLONG);
		tmpvr->size = 8;

		vreg_faultin(NULL, NULL, tmpvr, il, 0);
		vr->type = fdat->builtin->args[1];
		vr->size = size = backend->get_sizeof_type(vr->type, NULL);
		vreg_map_preg(vr, tmpvr->pregs[0]);
		vreg_anonymify(&vr, NULL, NULL, il);

	}
	intsize = backend->
			get_sizeof_type(make_basic_type(TY_INT), NULL);
	if (size < intsize) {
		size = intsize;
	}	

	if (size < 8) {
		/* Even small types are stored as dwords */
		size = 8;
	}

	c = const_from_value(&size, NULL);
	tmpvr = vreg_alloc(NULL, c, NULL, NULL);
	vreg_faultin(NULL, NULL, tmpvr, il, 0);
	vreg_faultin_protected(tmpvr, NULL, NULL, /*valist->vreg*/valist_vr, il, 0);
	ii = icode_make_add(/*valist->vreg*/valist_vr, tmpvr);
	append_icode_list(il, ii);
	icode_make_store(curfunc, /*valist->vreg*/valist_vr, /*valist->vreg*/valist_vr, il);

	return vr;
}


static void
get_member_vreg(struct decl *d, struct vreg **dest, struct vreg *parent) {
	struct vreg	*vr;

	vr = vreg_alloc(NULL, NULL, NULL, d->dtype);
	vr->parent = parent;
	vr->memberdecl = d;
	vr->from_ptr = parent;
	*dest = vr;
}



static struct type	*voidptr_type;

/*
 * typedef struct {
 *    unsigned gp_offset;
 *    unsigned fp_offset;
 *    void *overflow_arg_area;
 *    void *reg_save_area;
 * } va_list[1];
 *
 * -  reg_save_area is recorded by fty->lastarg if some variadic args are
 *    passed in registers
 * -  if all variadic stuff is passed on the stack, lastarg points to the
 *    beginning of the stack arguments
 * -  if registers are used for arguments, fty->lastarg->stack_addr->from_reg
 *    is non-null, and
 *     (struct reg **)fty->lastarg->stack_addr->from_reg - amd64_argregs
 *    is the number of gprs used, which, multiplied by 8, gives the gp_offset.
 * -  XXX fp is UNIMPLEMENTED
 * phew ..
 */
static struct vreg * 
amd64_builtin_va_start_to_icode(
	struct fcall_data *fdat,
	struct icode_list *il) {

	struct type		*ty;
	struct vreg		*vr = vreg_alloc(NULL, NULL, NULL, NULL);
	struct vreg		*valist_vr;
	struct sym_entry	*se;
	struct icode_instr	*ii;
	struct ty_func		*fty;
	struct reg		*r;
	struct decl		*srcdec;
	struct vreg		*srcvr;
	struct vreg		*tempvr;
	struct amd64_va_patches	*patches;
	static struct amd64_va_patches	nullp;

	patches = n_xmalloc(sizeof *patches);
	*patches = nullp;
	curfunc->patchme = patches;

	fty = curfunc->fty; 

	
	valist_vr = expr_to_icode(fdat->builtin->args[0], NULL, il, 0, 1);
	if (valist_vr == NULL) {
		return NULL;
	}
	vreg_faultin(NULL, NULL, valist_vr, il, 0);
	if (valist_vr->type->tlist == NULL
		|| valist_vr->type->tlist->type != TN_POINTER_TO
		|| valist_vr->type->tlist->next != NULL
		|| valist_vr->type->code != TY_STRUCT
		|| valist_vr->type->tstruc != builtin_va_list_type->tstruc) {
		errorfl(NULL /* XXX */, "Argument to va_start() has wrong "
			"type");
		return NULL;
	} 

	r = ALLOC_GPR(curfunc, 0, il, 0);

	se = valist_vr->type->tstruc->scope->slist;

	/* se = gp_offset */
	get_member_vreg(se->dec, &tempvr, valist_vr);
	ii = icode_make_setreg(r->composed_of[0], /*n*/0);
	append_icode_list(il, ii);
	patches->gp_offset = ii->dat; 

	vreg_map_preg(tempvr, r->composed_of[0]);
	icode_make_store(curfunc, tempvr, tempvr, il);

	se = se->next;

	/* se = fp_offset */
	get_member_vreg(se->dec, &tempvr, valist_vr);
	ii = icode_make_setreg(r->composed_of[0], 48); /* begins after gprs */
	append_icode_list(il, ii);
	patches->fp_offset = ii->dat;
	vreg_map_preg(tempvr, r->composed_of[0]);
	icode_make_store(curfunc, tempvr, tempvr, il);

	se = se->next;
	/* se = overflow_arg_area; */
	get_member_vreg(se->dec, &tempvr, valist_vr);

	srcdec = alloc_decl();

	/* XXX typing stuff is totally bogus?! untyped addres suffices?? */
	srcdec->dtype = voidptr_type;
	srcdec->stack_addr = make_stack_block(0, 8); 
	patches->overflow_arg_area = srcdec->stack_addr;
	srcvr = vreg_alloc(srcdec, NULL, NULL, NULL); 
	ii = icode_make_addrof(srcvr, il);
	append_icode_list(il, ii);
	vreg_map_preg(srcvr, ii->dat);
	icode_make_store(curfunc, srcvr, tempvr, il);
	free_preg(ii->dat, il, 0, 0);

	se = se->next;
	/* se = reg_save_area */
	get_member_vreg(se->dec, &tempvr, valist_vr);
	srcdec = alloc_decl();
	srcdec->dtype = voidptr_type;
	srcdec->stack_addr = make_stack_block(0, 8); 
	patches->reg_save_area = srcdec->stack_addr;
	srcvr = vreg_alloc(srcdec, NULL, NULL, NULL); 
	ii = icode_make_addrof(srcvr, il);
	append_icode_list(il, ii);
	vreg_map_preg(srcvr, ii->dat);
	icode_make_store(curfunc, srcvr, tempvr, il);
	free_preg(ii->dat, il, 0, 0);
#if 0
	se->dec->stack_addr = make_stack_block(0, 8);
	patches->reg_save_area = se->dec->stack_addr;
#endif

	ty = make_basic_type(TY_VOID);
	vr->type = ty;
	vr->size = 0;
	free_preg(r, il, 0, 0);
	return vr;
}

static struct vreg * 
amd64_builtin_next_arg_to_icode(
	struct fcall_data *fdat,
	struct icode_list *il) {

	struct type		*ty;
	struct vreg		*vr = vreg_alloc(NULL, NULL, NULL, NULL);
	struct icode_instr	*ii;
	
	(void) fdat;
	ii = icode_make_addrof(NULL, il);
	append_icode_list(il, ii);
	
	vreg_map_preg(vr, ii->dat);

	ty = n_xmemdup(make_basic_type(TY_CHAR), sizeof *ty);
	ty->tlist = alloc_type_node();
	ty->tlist->type = TN_POINTER_TO;
	vr->type = ty;
	vr->size = backend->get_sizeof_type(ty, NULL);
	return vr;
}


static struct vreg *
amd64_builtin_va_arg_to_icode(
	struct fcall_data *fdat,
	struct icode_list *il) {

	struct vreg		*valist_vr;
	struct vreg		*vr;
	struct vreg		*tmpvr;
	struct vreg		*tempmembvr;
	struct vreg		*pointer;
	struct vreg		*limit_vreg;
	struct vreg		*offset_vreg = NULL;
	struct token		*c;
	struct icode_instr	*ii;
	struct type		*argtype = fdat->builtin->args[1];
	struct sym_entry	*se;
	struct icode_instr	*label;
	struct icode_instr	*label2;
	struct token		*fe_const;
	struct reg		*r;
	long			long_size;
	int			size;
	int			is_floating = 0;

#define gp_offset se->dec
#define fp_offset se->next->dec
#define overflow_arg_area se->next->next->dec
#define reg_save_area se->next->next->next->dec
	/*
	 * 08/22/07: Wow, this stuff used get_ident() and did not
	 * generate icode for the first argument at all! Thus
	 *
	 *   va_list *foo;
	 *   va_arg(*foo, type);
	 *
	 * ... didn't work because *foo was not loaded
	 */
	valist_vr = expr_to_icode(fdat->builtin->args[0], NULL, il, 0, 1);
	if (valist_vr == NULL) {
		return NULL;
	 } /* else {
	      check type...
	 } */     
	vreg_faultin(NULL, NULL, /*valist->vreg*/valist_vr, il, 0);

#if 0
	/* Load va_list pointer into register */
	vreg_faultin(NULL, NULL, valist->vreg, il, 0);
	reg_set_unallocatable(valist->vreg->pregs[0]);
#endif	
	se = valist_vr->type->tstruc->scope->slist;

	/* All va_list members come from the va_list pointer */
	get_member_vreg(gp_offset, &tempmembvr, valist_vr);
	gp_offset->vreg = tempmembvr;
	get_member_vreg(fp_offset, &tempmembvr, valist_vr);
	fp_offset->vreg = tempmembvr;
	get_member_vreg(overflow_arg_area, &tempmembvr, valist_vr);
	overflow_arg_area->vreg = tempmembvr;
	get_member_vreg(reg_save_area, &tempmembvr, valist_vr);
	reg_save_area->vreg = tempmembvr;

	if ((argtype->code == TY_STRUCT
		|| argtype->code == TY_UNION
		|| argtype->code == TY_LDOUBLE)
		&& argtype->tlist == NULL) {
		/* Struct/union/long double passed by value */
		struct vreg	*oarea;

		vreg_faultin(NULL, NULL, overflow_arg_area->vreg, il, 0);
		oarea = vreg_disconnect(overflow_arg_area->vreg);
		oarea->type = n_xmemdup(argtype, sizeof *argtype);
		append_typelist(oarea->type, TN_POINTER_TO, 0, NULL, NULL);

		vr = vreg_alloc(NULL, NULL, NULL, NULL);
		vr->from_ptr = oarea;
		vr->type = argtype;
		vr->size = size = backend->get_sizeof_type(vr->type, NULL);
		if (fdat->lvalue != NULL) {
			/* Result is assigned - copy */

			vreg_faultin_ptr(/*valist->vreg*/valist_vr, il);
			vreg_faultin_ptr(fdat->lvalue, il);
			if (argtype->code == TY_LDOUBLE) {
				vreg_faultin_x87(NULL, NULL, vr, il, 0);
				vreg_anonymify(&vr, NULL, NULL, il);
			} else {
				/* Struct/union result */ 
				icode_make_copystruct(fdat->lvalue, vr, il);
				vr->struct_ret = 1;
			}
		}
		while (size % 8) ++size;
		long_size = size;
		fe_const =
			const_from_value(&long_size, make_basic_type(TY_LONG));
		limit_vreg = vreg_alloc(NULL, fe_const, NULL, NULL);
		vreg_faultin_protected(oarea,
			 NULL, NULL, limit_vreg, il, 0);
		ii = icode_make_add(oarea, limit_vreg);
		append_icode_list(il, ii);
		vreg_map_preg(overflow_arg_area->vreg, oarea->pregs[0]);
		icode_make_store(curfunc, overflow_arg_area->vreg,
			overflow_arg_area->vreg, il);
		return vr;
	} else if (argtype->tlist == NULL && IS_FLOATING(argtype->code)) {
		if (argtype->code == TY_LDOUBLE) {
			/* Passed on stack */
		} else {
			is_floating = 1;
			offset_vreg = fp_offset->vreg;
			long_size = 112; /* 48 gpr- + 64 fpr-bytes */
		}
	} else {
		offset_vreg = gp_offset->vreg;
		long_size = 48; /* 48 gpr bytes */
	}

	vreg_faultin(NULL, NULL, /*gp_offset->vreg*/offset_vreg, il, 0);
	fe_const = const_from_value(&long_size, make_basic_type(TY_LONG));
	limit_vreg = vreg_alloc(NULL, fe_const, NULL, NULL);
	vreg_faultin(NULL, NULL, limit_vreg, il, 0);

	ii = icode_make_cmp(/*gp_offset->vreg*/offset_vreg, limit_vreg);
	append_icode_list(il, ii);

	r = limit_vreg->pregs[0]; /* reuse register */
	pointer = vreg_alloc(NULL, NULL, NULL, voidptr_type);

	label = icode_make_label(NULL);
	label2 = icode_make_label(NULL);
	ii = icode_make_branch(label, INSTR_BR_EQUAL, offset_vreg);
	append_icode_list(il, ii);

	/* offset is below 48 - use register save area */ 
	vreg_faultin(r, NULL, reg_save_area->vreg, il, 0);
	ii = icode_make_add(reg_save_area->vreg, offset_vreg);
	append_icode_list(il, ii);
	append_icode_list(il, icode_make_jump(label2));

	append_icode_list(il, label);

	/* offset is 48 - use overflow area */

	vreg_faultin(r, NULL, overflow_arg_area->vreg, il, 0);
	append_icode_list(il, label2);
	vreg_map_preg(pointer, r);
	reg_set_unallocatable(r);

	/* XXX from_ptr?? check for indirection chain!!??! */
	vr = vreg_alloc(NULL, NULL, /*pointer,*/NULL, NULL);
	vr->from_ptr = pointer;
	vr->type = fdat->builtin->args[1];
	vr->size = size = backend->get_sizeof_type(vr->type, NULL);
	vreg_faultin(NULL, NULL, vr, il, 0);
	vreg_anonymify(&vr, NULL, NULL, il);

	if (size < 8) {
		size = 8;
	}	
	c = const_from_value(&size, NULL);
	tmpvr = vreg_alloc(NULL, c, NULL, NULL);
	vreg_faultin(NULL, NULL, tmpvr, il, 0);

	vreg_faultin(NULL, NULL, offset_vreg, il, 0);
	limit_vreg = vreg_alloc(NULL, fe_const, NULL, NULL);
	vreg_faultin(NULL, NULL, limit_vreg, il, 0);
	ii = icode_make_cmp(offset_vreg, limit_vreg);
	append_icode_list(il, ii);
	label = icode_make_label(NULL);
	label2 = icode_make_label(NULL);
	ii = icode_make_branch(label, INSTR_BR_EQUAL, offset_vreg);
	append_icode_list(il, ii);

	/* offset is below 48/112 - use register save area */ 
	vreg_faultin_protected(offset_vreg, NULL, NULL, tmpvr, il, 0);
	ii = icode_make_add(offset_vreg, tmpvr);
	append_icode_list(il, ii);
	icode_make_store(curfunc, offset_vreg, offset_vreg, il);
	append_icode_list(il, icode_make_jump(label2));
	append_icode_list(il, label);

	/* offset is 48/112 - use overflow area */
	vreg_faultin(NULL, NULL, overflow_arg_area->vreg, il, 0);
	vreg_faultin(NULL, NULL, tmpvr, il, 0);
	ii = icode_make_add(overflow_arg_area->vreg, tmpvr);
	append_icode_list(il, ii);
	icode_make_store(curfunc, overflow_arg_area->vreg,
		overflow_arg_area->vreg, il);
	append_icode_list(il, label2);

	free_preg(/*valist->vreg*/ valist_vr->pregs[0], il, 0, 0);
	free_preg(offset_vreg->pregs[0], il, 0, 0);
	free_preg(overflow_arg_area->vreg->pregs[0], il, 0, 0);
	free_preg(reg_save_area->vreg->pregs[0], il, 0, 0);
	free_preg(r, il, 0, 0);
#undef gp_offset
#undef fp_offset
#undef overflow_arg_area
#undef reg_save_area
	return vr;
}

static builtin_to_icode_func_t	x86_icode_funcs[] = {
	x86_builtin_va_start_to_icode,
	x86_builtin_va_start_to_icode,
	x86_builtin_next_arg_to_icode,
	x86_builtin_va_end_to_icode,
	x86_builtin_va_arg_to_icode,
	NULL, /* expect */
	generic_builtin_alloca_to_icode,
	generic_builtin_va_copy_to_icode,
	generic_builtin_memcpy_to_icode,
	generic_builtin_frame_address_to_icode,
	generic_builtin_offsetof_to_icode
};

static builtin_to_icode_func_t	mips_icode_funcs[] = {
	x86_builtin_va_start_to_icode,
	x86_builtin_va_start_to_icode,
	mips_builtin_next_arg_to_icode,
	x86_builtin_va_end_to_icode,
	mips_builtin_va_arg_to_icode,
	NULL, /* expect */
	generic_builtin_alloca_to_icode,	
	generic_builtin_va_copy_to_icode,
	generic_builtin_memcpy_to_icode,
	generic_builtin_frame_address_to_icode,
	generic_builtin_offsetof_to_icode
};

static builtin_to_icode_func_t	sparc_icode_funcs[] = {
	x86_builtin_va_start_to_icode,
	x86_builtin_va_start_to_icode,
	mips_builtin_next_arg_to_icode,
	x86_builtin_va_end_to_icode,
	mips_builtin_va_arg_to_icode,
	NULL, /* expect */
	generic_builtin_alloca_to_icode,
	generic_builtin_va_copy_to_icode,
	generic_builtin_memcpy_to_icode,
	generic_builtin_frame_address_to_icode,
	generic_builtin_offsetof_to_icode
};

static builtin_to_icode_func_t	power_icode_funcs[] = {
	x86_builtin_va_start_to_icode,
	x86_builtin_va_start_to_icode,
	/*mips*/ x86_builtin_next_arg_to_icode,
	x86_builtin_va_end_to_icode,
	/*mips*/ x86_builtin_va_arg_to_icode,
	NULL, /* expect */
	generic_builtin_alloca_to_icode,	
	generic_builtin_va_copy_to_icode,
	generic_builtin_memcpy_to_icode,
	generic_builtin_frame_address_to_icode,
	generic_builtin_offsetof_to_icode
};
	
static builtin_to_icode_func_t	amd64_icode_funcs[] = {
	amd64_builtin_va_start_to_icode,
	amd64_builtin_va_start_to_icode,
	amd64_builtin_next_arg_to_icode,
	x86_builtin_va_end_to_icode,
	amd64_builtin_va_arg_to_icode,
	NULL, /* expect */
	generic_builtin_alloca_to_icode,	
	generic_builtin_va_copy_to_icode,
	generic_builtin_memcpy_to_icode,
	generic_builtin_frame_address_to_icode,
	generic_builtin_offsetof_to_icode
};

/*
 * XXX the initialization below means that the ``builtins'' and
 * ``*_icode_funcs'' tables have to have the same ordering
 */
static void
init_builtin_functions(void) {
	builtin_to_icode_func_t	*p = NULL;
	unsigned int		i;

	if (backend->arch == ARCH_X86) {
		p = x86_icode_funcs;
	} else if (backend->arch == ARCH_AMD64) {
		p = amd64_icode_funcs;
	} else if (backend->arch == ARCH_MIPS) {
		p = mips_icode_funcs;
	} else if (backend->arch == ARCH_POWER) {
		p = power_icode_funcs;
	} else if (backend->arch == ARCH_SPARC) {
		p = sparc_icode_funcs;
	} else {
		unimpl();
	}
	for (i = 0; i < sizeof x86_icode_funcs /
		sizeof x86_icode_funcs[0]; ++i) {
		builtins[i].toicode = p[i]; 
	}
}

static struct builtin *
lookup_builtin(const char *name) {
	size_t	namelen = strlen(name);
	int	i;

	/*
	 * XXX test below is for alloca()
	 */
	if (name[0] == '_') {
		name += sizeof "__builtin_" - 1;
		namelen -= sizeof "__builtin_" - 1;
	}	

	for (i = 0; builtins[i].name != NULL; ++i) {
		if (builtins[i].namelen != namelen) {
			continue;
		}
		if (strcmp(builtins[i].name, name) == 0) {
			return &builtins[i];
		}
	}
	return NULL;
}	


struct fcall_data *
get_builtin(struct token **tok, struct token *nametok) {
	struct builtin		*b;
	struct fcall_data	*ret;
	static int		inited;

	if (!inited) {
		/* XXX ... */
		voidptr_type = n_xmemdup(make_basic_type(TY_VOID),
			sizeof *voidptr_type);
		voidptr_type->tlist = alloc_type_node();
		voidptr_type->tlist->type = TN_POINTER_TO;
		init_builtin_functions();
		inited = 1;
	}

	if ((b = lookup_builtin(nametok->data)) == NULL) {
		errorfl(nametok, "Unknown builtin `%s'", nametok->ascii);
		return NULL;
	}	

	if (b->parse == builtin_parse_va_start) {
		/* Seems nonsense because of va_list arguments */
		if (!curfunc->proto->dtype->tlist->tfunc->variadic) {
			errorfl(nametok,
				"`%s' used in non-variadic function `%s'",
				nametok->ascii, curfunc->proto->dtype->name);
			return NULL;
		}	
	}	

	ret = alloc_fcall_data();
	ret->builtin = n_xmalloc(sizeof *ret->builtin);
	memset(ret->builtin, 0, sizeof *ret->builtin);
	ret->builtin->builtin = b;
	if (b->parse(tok, ret) != 0) {
		free(ret);
		return NULL;
	}
	return ret;
}



syntax highlighted by Code2HTML, v. 0.9.1