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