/*
* Copyright (c) 2004 - 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.
*
* Expression parser
*/
#include "expr.h"
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include "token.h"
#include "error.h"
#include "misc.h"
#include "defs.h"
#include "decl.h"
#include "builtins.h"
#include "scope.h"
#include "type.h"
#include "icode.h"
#include "debug.h"
#include "cc1_main.h"
#include "subexpr.h"
#include "functions.h"
#include "analyze.h"
#include "symlist.h"
#include "backend.h"
#include "typemap.h"
#include "libnwcc.h"
#include "reg.h"
#include "n_libc.h"
static int
value_is_nonzero(struct tyval *cv) {
int size;
int i;
unsigned char *uc;
size = cross_get_sizeof_type(cv->type);
for (uc = (unsigned char *)cv->value, i = 0; i < size; ++i, ++uc) {
if (*uc != 0) {
return 1;
}
}
return 0;
}
/*
* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
* doesn't handle cross-compilation :(
* address->diff should probably become a ``struct tyval'' and then we
* need a nice set of utility functions to convert host values from/to
* target-independent values. tyval is probably our best bet for
* abstracting these things because exec_op() and such already use it
* in the interface
*/
static void
addr_add_sub(struct tyval *tv, struct expr *ex, struct type *ty, int op) {
long val;
long origval;
int esize = backend->get_sizeof_elem_type(ty);
cross_do_conv(ex->const_value, TY_LONG);
/* XXX what to do here? :( */
ex->const_value->type->code = TY_LONG;
#if 0
val = *(long *)ex->const_value->value * esize;
#endif
val = (long)cross_to_host_size_t(ex->const_value);
origval = val;
val *= esize;
if (tv->address != NULL) {
if (op == TOK_OP_PLUS) {
tv->address->diff += val;
} else {
tv->address->diff -= val;
}
} else {
static struct tyval src;
static struct tyval tmpval;
static struct token optok;
struct type *saved_type;
int opval = TOK_OP_PLUS;
optok.type = TOK_OPERATOR;
optok.data = &opval;
src.value = ex->const_value->value;
/*
* 08/19/07: Store (and later restore!) the value val
* as source. This was missing, so the array element
* calculation above was ignored
*/
cross_from_host_long(src.value, val, TY_LONG);
src.type = make_basic_type(TY_LONG);
/*
* 08/19/07: This wasn't working because, as a comment
* here rightly said :-), we have to use a temporary
* tyval as target instead of using tv as target and
* destination
*/
saved_type = tv->type;
tv->type = src.type;
cross_exec_op(&optok, /*tv*/&tmpval, tv, &src);
cross_from_host_long(src.value, origval, TY_LONG);
tv->value = tmpval.value;
tv->type = saved_type;
}
}
static struct tyval
do_eval_const_expr(struct expr *tree, int extype) {
struct tyval ret;
struct tyval tmp1;
struct tyval tmp2;
struct tyval *tv;
struct tyval *left;
struct tyval *right;
size_t i;
int rc = 0;
int nullok = 1;
static struct tyval nulltv;
struct vreg *builtinvr = NULL;
ret = nulltv;
tmp1 = nulltv;
tmp2 = nulltv;
if (tree->op == 0) {
static struct vreg structvr;
struct vreg *tmpvr;
struct decl *dp;
#if 0
if (extype == EXPR_OPTCONSTINIT
&& (tree->data->operators[0] != NULL
&& *(int *)tree->data->operators[0]->data
!= TOK_OP_ADDR)) {
/*
* Assume this is probably not const
*/
ret.type = NULL;
ret.not_constant = 1;
return ret;
}
#endif
if (tree->data->is_expr) {
ret = do_eval_const_expr(tree->data->is_expr, extype);
if (ret.type == NULL) {
return ret;
}
} else if (tree->data->meat != NULL) {
struct token *t = tree->data->meat;
if (t->type == TOK_STRING_LITERAL) {
ret.str = t->data;
ret.type = ret.str->ty;
} else if (t->type == TOK_IDENTIFIER) {
struct decl *d;
if (tree->data->operators[0] != NULL
&& tree->data->operators[0]->type ==
TOK_PAREN_OPEN) {
struct fcall_data *fd;
fd = tree->data->operators[0]->data;
if (fd->builtin != NULL
&& fd->builtin->type ==
BUILTIN_OFFSETOF) {
builtinvr = fd->builtin->
builtin->
toicode(fd, NULL);
ret.type = builtinvr->type;
ret.value = builtinvr->
from_const->data;
goto builtin_done;
}
}
if ((d = access_symbol(curscope,
t->data, 1)) == NULL) {
/*
* This may be a call to an undeclared
* function if this is a VLA size
* expression or variable initializer
*/
if (extype == EXPR_OPTCONSTINIT
|| extype == EXPR_OPTCONSTARRAYSIZE) {
ret.not_constant = 1;
ret.type = NULL;
} else {
errorfl(t,
"Undeclared identifier `%s'",
t->ascii);
ret.type = ret.value = NULL;
}
return ret;
}
if (d->dtype->storage != TOK_KEY_STATIC
&& d->dtype->storage != TOK_KEY_EXTERN
&& d->dtype->tenum == NULL
&& (d->dtype->tlist == NULL
|| d->dtype->tlist->type
!= TN_FUNCTION)) {
/*
* XXX seems totally bogus.. only
* functions should be allowed at all for
* *values*???
*/
if (extype == EXPR_OPTCONSTINIT
|| extype == EXPR_OPTCONSTARRAYSIZE) {
if (extype == EXPR_OPTCONSTINIT) {
warningfl(t, "Variable "
"initializers are not "
"allowed in ISO C90");
} else {
warningfl(t, "Variable-"
"size arrays are not "
"allowed in ISO C90");
}
ret.not_constant = 1;
} else {
errorfl(t, "Use of non-static "
"identifier `%s' in constant"
" expression", d->dtype->name);
}
ret.type = ret.value = NULL;
return ret;
}
ret.type = d->dtype;
if (d->dtype->tlist == NULL
&& d->dtype->tenum != NULL) {
/*
* The value is freed after use. This is
* OK for tokens, but not for enum
* constants, whose values may be needed
* later - possibly in the same
* expression. So create a copy
*/
ret.value = n_xmemdup(
d->vreg->from_const->data,
sizeof(int));
} else {
int is_addr = 1 /*0*/;
/*
* An identifier used in a constant
* expression may only be used for
* an address constant
*/
#if 0
if (tree->data->operators[0] != NULL
&& tree->data->operators[0]
->type
== TOK_OPERATOR) {
if (*(int *)tree->data->
operators[0]->data
== TOK_OP_ADDR) {
is_addr = 1;
}
}
#endif
if (!is_addr
&& (d->dtype->tlist == NULL
|| d->dtype->tlist->type
== TN_POINTER_TO)) {
unimpl();
} else {
/* Address constant */
#if 0
if (is_addr) {
ret.type =
addrofify_type
(ret.type);
}
#endif
ret.address =
n_xmalloc(sizeof *
ret.address);
ret.address->dec = d;
ret.address->diff = 0;
}
}
} else {
/* Must be constant */
ret.type = make_basic_type(t->type);
ret.type = n_xmemdup(ret.type,
sizeof *ret.type);
if (t->type == TOK_COMP_LITERAL) {
struct comp_literal *lit;
lit = t->data;
if (tree->data->operators[0] != NULL) {
/*
* Don't handle things like
*
* static struct *foo =
* &(struct foo) {...};
*
* just yet
*/
unimpl();
}
ret.static_init = lit->init;
} else if (IS_FLOATING(ret.type->code)) {
struct ty_float *fc;
fc = t->data;
ret.value = fc->num->value;
} else {
ret.value = t->data;
}
ret.is_nullptr_const =
is_nullptr_const(t, ret.type);
}
builtin_done:
ret.alloc = 0;
} else if (tree->data->is_sizeof) {
unsigned long size;
struct vreg *vr;
vr = tree->data->is_sizeof->data;
ret.type = vr->type;
size = vr->size;
ret.value = vr->from_const->data;
} else {
abort();
}
/* Now apply all unary/postfix/prefix operators */
tv = &ret;
if (builtinvr != NULL) {
/*
* Meat of subexpression is builtin function -
* ignore first (function call) operator
*/
i = 1;
} else {
i = 0;
}
for (/*i = 0*/; tree->data->operators[i] != NULL; ++i) {
struct token *op;
struct decl *d;
struct token *ident;
int opval;
op = tree->data->operators[i];
if (ret.value == NULL
&& ret.address == NULL
&& op->type != TOK_OP_CAST
&& (op->type != TOK_OPERATOR
|| *(int *)op->data !=TOK_OP_ADDR)
&& (ret.type->code != TY_STRUCT
|| (op->type != TOK_OP_STRUMEMB
&& op->type != TOK_OP_STRUPMEMB))) {
errorfl(op,
"`%s' operator used with incorrect "
"type", op->ascii);
ret.type = NULL;
return ret;
}
switch (op->type) {
case TOK_OP_CAST:
d = op->data;
if (tv->is_nullptr_const) {
if (d->dtype->tlist != NULL
&& d->dtype->tlist->type ==
TN_POINTER_TO
&& d->dtype->tlist->next
== NULL
&& d->dtype->code == TY_VOID) {
/* Remains nullptr */
;
} else {
tv->is_nullptr_const = 0;
}
}
if (d->dtype->tlist != NULL) {
if (tree->data->meat &&
tree->data->meat->type
== TOK_STRING_LITERAL) {
;
} else {
#if 0
do_conv(tv, TY_ULONG); /* XXX */
#endif
if (ret.type->tlist == NULL) {
cross_do_conv(tv, TY_ULONG); /* XXX */
tv->type = make_basic_type(
TY_ULONG);
tv->type = n_xmemdup(tv->type,
sizeof *tv->type);
}
}
ret.type = d->dtype;
} else {
if (tv->type->tlist != NULL) {
/*
* Very dangerous! A pointer
* cast to an integer as a
* *constant* expression! This
* only makes sense if source
* and target are the same
* size. We have to support
* this for programs like
* xterm
*/
size_t dest_size =
backend->get_sizeof_type(d->dtype,0);
size_t src_size =
backend->get_ptr_size();
if (src_size == dest_size) {
warningfl(op,
"Nonportable bogus cast of pointer to integer in "
"constant expression");
} else {
if (extype == EXPR_OPTCONSTINIT
|| extype == EXPR_OPTCONSTARRAYSIZE) {
if (extype == EXPR_OPTCONSTINIT) {
warningfl(op, "Variable "
"initializers are not "
"allowed in ISO C90");
} else {
warningfl(op, "Variable-"
"size arrays are not "
"allowed in ISO C90");
}
ret.not_constant = 1;
ret.type = NULL;
return ret;
} else {
errorfl(op,
"Invalid bogus cast of pointer to integer in "
"constant expression");
}
}
/*
* BEWARE!!! We keep the type as
* ``pointer'' because the backend
* otherwise gets confused and
* thinks this really is a long. Hm
*
* 08/18/07 If it is an array, we
* change the type to pointer
* because otherwise the backends
* do not understand this kludged
* initializer
*/
if (tv->type->tlist->type ==
TN_ARRAY_OF) {
tv->type =
dup_type(tv->type);
tv->type->tlist->type =
TN_POINTER_TO;
}
tv->type = d->dtype;
} else {
cross_do_conv(tv,
d->dtype->code);
ret.type = d->dtype;
}
}
if (ret.type->code == TY_ENUM
&& ret.type->tlist == NULL) {
/* XXX is this really desirable? */
ret.type = n_xmemdup(make_basic_type(TY_INT),
sizeof(struct type));
}
break;
case TOK_ARRAY_OPEN:
#if 0
if (ret.address != NULL
&& (op=tree->data->operators[i+1])
!= NULL
&& op->type == TOK_OPERATOR
&& *(int *)op->data == TOK_OP_ADDR) {
#endif
{
/* make &foo[x] work */
struct expr *ex
= tree->data->operators[ i /*++ */]->
data;
if (eval_const_expr(ex, extype,
&ret.not_constant) != 0) {
ret.type = NULL;
return ret;
}
addr_add_sub(&ret, ex, tv->type,
TOK_OP_PLUS);
ret.type = dup_type(ret.type);
ret.type->tlist = ret.type->tlist->next;
}
#if 0
do_conv(ex->const_value, TY_ULONG);
ret.address->diff
+= *(unsigned long *)ex->
const_value->value *
backend->get_sizeof_elem_type
(tv->type);
#endif
#if 0
tv->type->tlist->type
= TN_POINTER_TO; /* XXX */
#endif
#if 0
} else {
errorfl(tree->data->operators[i],
"Subscript operator used in"
" constant expression");
ret.type = NULL;
return ret;
}
#endif
break;
case TOK_PAREN_OPEN:
/* Must be function call operator */
if (extype == EXPR_OPTCONSTINIT
|| extype == EXPR_OPTCONSTARRAYSIZE) {
if (extype == EXPR_OPTCONSTINIT) {
warningfl(op, "Variable "
"initializers are not "
"allowed in ISO C90");
} else {
warningfl(op, "Variable-"
"size arrays are not "
"allowed in ISO C90");
}
ret.not_constant = 1;
} else {
errorfl(op, "Invalid function call "
"in constant expression");
}
ret.type = NULL;
return ret;
break;
case TOK_OP_STRUMEMB: /* XXX !??why not TOK_OPERATOR */
case TOK_OP_STRUPMEMB:
if (ret.type == NULL
|| ((ret.type->code != TY_STRUCT
&& ret.type->code != TY_UNION)
/* || ret.type->tlist != NULL */ )) {
errorfl(tree->data->operators[i],
"Incorrect type for "
"operator `%s'",
tree->data->operators[i]->ascii);
ret.type = NULL;
return ret;
}
ident = op->data;
if ((dp = access_symbol(ret.type->tstruc->
scope, ident->data, 0)) == NULL) {
errorfl(tree->data->operators[i],
"Unknown structure member "
"`%s'", ident->data);
ret.type = NULL;
return ret;
}
if (op->type == TOK_OP_STRUMEMB) {
/* . operator */
tmpvr = vreg_alloc(dp, NULL, NULL, NULL);
} else {
/* -> operator */
tmpvr = vreg_alloc(NULL, NULL, NULL,
dp->dtype);
}
#if 0
tmpvr->memberdecl = dp;
tmpvr->parent = structvr;
#endif
/*
* XXX this is quite ad-hocly kludged to make
* nwcc work with the gtar source... it does
* not understand named address constants
* yet and is generally incomplete
*/
if (ret.value) {
static struct vreg parentvr;
parentvr.type = ret.type;
structvr.parent = &parentvr;
structvr.memberdecl = dp;
*(long *)ret.value += calc_offsets(&structvr);
} else if (ret.address != NULL) {
static struct vreg parentvr;
parentvr.type = ret.type;
structvr.parent = &parentvr;
structvr.memberdecl = dp;
ret.address->diff +=
calc_offsets(&structvr);
} else {
unimpl();
}
ret.type = dp->dtype;
break;
case TOK_OPERATOR:
opval = *(int *)op->data;
if (opval == TOK_OP_ADDR) {
if (i == 0) {
; /* already taken care of */
} else {
#if 0
errorfl(op,
"Parse error at `%s'",
op->ascii);
ret.type = NULL;
return ret;
#endif
}
#if 0
if (ret.type->tlist == NULL) {
#endif
/* XXX this sucks */
ret.type =
addrofify_type
(ret.type);
#if 0
}
#endif
#if 0
} else if (opval == TOK_OP_STRUMEMB) {
unimpl();
} else if (opval == TOK_OP_STRUPMEMB) {
unimpl();
#endif
} else if (opval == TOK_OP_LNEG
|| opval == TOK_OP_BNEG
|| opval == TOK_OP_UPLUS
|| opval == TOK_OP_UMINUS) {
int oldtype = op->type; /* XXX needed? */
static struct tyval tytmp;
op->type = opval;
cross_exec_op(op, &tytmp, tv, NULL);
op->type = oldtype;
*tv = tytmp;
} else {
printf("unknown operator %d\n",
opval);
abort();
}
break;
case TOK_KEY_SIZEOF:
case TOK_KEY_ALIGNOF:
; /* taken care of already */
break;
default:
printf("unknown operator %d\n", op->type);
unimpl();
}
}
return ret;
}
tmp1 = do_eval_const_expr(tree->left, extype);
if (tmp1.type == NULL) {
return tmp1;
}
if (tree->op == TOK_OP_LAND) {
int res = 0;
if (value_is_nonzero(&tmp1)) {
tmp2 = do_eval_const_expr(tree->right, extype);
if (value_is_nonzero(&tmp2)) {
res = 1;
}
}
ret.type = make_basic_type(TY_INT);
ret.type = n_xmemdup(ret.type, sizeof *ret.type);
ret.value = n_xmemdup(&res, sizeof res);
goto out;
} else if (tree->op == TOK_OP_LOR) {
int res = 0;
if (!value_is_nonzero(&tmp1)) {
tmp2 = do_eval_const_expr(tree->right, extype);
if (value_is_nonzero(&tmp2)) {
res = 1;
}
} else {
res = 1;
}
ret.type = make_basic_type(TY_INT);
ret.type = n_xmemdup(ret.type, sizeof *ret.type);
ret.value = n_xmemdup(&res, sizeof res);
goto out;
} else if (tree->op != TOK_OP_COND) {
/* XXX broken */
tmp2 = do_eval_const_expr(tree->right, extype);
nullok = 0;
} else {
/* Is TOK_OP_COND */
if (tree->right->op != TOK_OP_COND2) {
errorfl(tree->right->tok,
"Parse error - expected second part "
"of conditional operator");
ret.type = NULL;
return ret;
}
if (value_is_nonzero(&tmp1)) {
tmp2 = do_eval_const_expr(tree->right->left, extype);
} else {
tmp2 = do_eval_const_expr(tree->right->right, extype);
}
if (tmp2.type == NULL) {
return ret;
}
if (tmp2.type->code < TY_INT) {
/*
* XXX we also need some typechecking ... And
* usual arithmetic conversions!!! E.g.
* foo < bar? 0ll: 0;
* ... should convert 0 to 0ll
*/
cross_do_conv(&tmp2, TY_INT);
}
ret.type = tmp2.type;
ret.value = tmp2.value;
goto out;
}
tv = &ret;
left = &tmp1;
right = &tmp2;
if (tmp1.type == NULL
|| (!nullok && tmp2.type == NULL)
|| (!nullok && (rc = cross_convert_tyval(&tmp1, &tmp2)) != 0)) {
ret.type = NULL;
ret.value = NULL;
ret.not_constant = tmp1.not_constant || tmp2.not_constant;
if (rc != 0) {
/*
* XXX mm this is kludged, convert_tyval() shouldn't
* return an error in the first place ;/
*/
if (tmp1.address || tmp2.address) {
if (tree->op == TOK_OP_PLUS
|| tree->op == TOK_OP_MINUS) {
static struct expr dum;
int op = tree->op;
/* Must be addr + integer */
if (tmp1.address == NULL) {
dum.const_value = &tmp1;
addr_add_sub(&tmp2, &dum,
tmp2.type,
op);
return tmp2;
} else if (tmp2.address == NULL) {
dum.const_value = &tmp2;
addr_add_sub(&tmp1, &dum,
tmp1.type,
op);
return tmp1;
}
}
}
errorfl(tree->tok, "Incompatible types in expression");
ret.type = NULL;
return ret;
}
return ret;
}
if ((left->address == NULL && left->value == NULL)
|| (right->address == NULL && right->value == NULL)) {
if (tree->op != TOK_OP_COND) {
errorfl(tree->right->tok, "Incompatible types for "
"operator");
ret.type = NULL;
return ret;
}
}
switch (tree->op) {
case TOK_OP_PLUS:
if (left->address || right->address) {
unimpl();
} else {
cross_exec_op(tree->tok, tv, left, right);
}
break;
case TOK_OP_MINUS:
if (left->address || right->address) {
unimpl();
} else {
cross_exec_op(tree->tok, tv, left, right);
}
break;
case TOK_OP_MULTI:
cross_exec_op(tree->tok, tv, left, right);
break;
case TOK_OP_DIVIDE:
cross_exec_op(tree->tok, tv, left, right);
break;
case TOK_OP_SMALL:
cross_exec_op(tree->tok, tv, left, right);
break;
case TOK_OP_GREAT:
cross_exec_op(tree->tok, tv, left, right);
break;
case TOK_OP_GREATEQ:
cross_exec_op(tree->tok, tv, left, right);
break;
case TOK_OP_SMALLEQ:
cross_exec_op(tree->tok, tv, left, right);
break;
case TOK_OP_LEQU:
cross_exec_op(tree->tok, tv, left, right);
break;
case TOK_OP_LNEQU:
cross_exec_op(tree->tok, tv, left, right);
break;
case TOK_OP_MOD:
cross_exec_op(tree->tok, tv, left, right);
break;
case TOK_OP_BAND:
cross_exec_op(tree->tok, tv, left, right);
break;
case TOK_OP_BOR:
cross_exec_op(tree->tok, tv, left, right);
break;
case TOK_OP_BXOR:
cross_exec_op(tree->tok, tv, left, right);
break;
case TOK_OP_BSHL:
cross_exec_op(tree->tok, tv, left, right);
break;
case TOK_OP_BSHR:
cross_exec_op(tree->tok, tv, left, right);
break;
default:
printf("Badness to the maximum %d\n", tree->op);
abort();
}
if (tree->op != TOK_OP_COND) {
free(left->value);
free(right->value);
}
out:
return ret;
}
int
eval_const_expr(struct expr *ex, int extype, int *not_constant) {
struct tyval tv;
struct tyval *dtv;
if (ex->const_value != NULL) {
/* already been evaluated */
return 0;
}
tv = do_eval_const_expr(ex, extype);
if (tv.type == NULL) {
if (not_constant != NULL) {
*not_constant = tv.not_constant;
}
return -1;
} else {
rv_setrc_print(tv.value, tv.type->code, 0);
dtv = n_xmalloc(sizeof tv);
memcpy(dtv, &tv, sizeof tv);
ex->const_value = dtv;
}
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1