yasm/libyasm/expr.c
Peter Johnson 1e77ce6c40 Add core TASM syntax support.
Contributed by: Samuel Thibault <samuel.thibault@ens-lyon.org>

It is built on top of the NASM parser and preproc, with the following
notable extensions for TASM syntax:
 - case insensitive symbols and filenames,
 - support for segment and size of labels, which permits to avoid giving
   them on each memory dereference,
 - support for data reservation (i.e. e.g. "var dd ?"),
 - support for multiples (i.e. e.g. "var dd 1 dup 10"),
 - little endian string integer constants,
 - additional expression operators: shl, shr, and, or, low, high,
 - additional offset keyword,
 - additional fword and df,
 - support for doubled quotes within quotes,
 - support for array-like and structure-like notations: t[eax] and
   [var].field,
 - support for tasm directives: macro, rept, irp, locals, proc, struc,
   segment, assume.

Notes:

 - Almost all extensions are only effective when tasm_compatible_mode is
   set, so we should have very reduced possible breakage.

 - Because the "and" keyword can be an expression operator and an
   instruction name, the data pseudo-instructions explicitly switch the
   lexer state to INSTRUCTION state to fix the ambiguity.

 - In gen_x86_insn.py, several instructions (namely lds and lea) now take
   relaxed memory sizes.  The reason is that in the case of tasm, the size
   of the actual pointed data is passed up to there, and thus any type of
   data should be accepted.

With all of this, loadlin can be compiled by yasm with quite reduced
modifications.

A new TASM-like frontend is also included.

svn path=/trunk/yasm/; revision=2130
2008-10-07 05:38:11 +00:00

1516 lines
47 KiB
C

/*
* Expression handling
*
* Copyright (C) 2001-2007 Michael Urman, Peter Johnson
*
* 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 AUTHOR AND OTHER 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 AUTHOR OR OTHER CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "util.h"
/*@unused@*/ RCSID("$Id$");
#include "libyasm-stdint.h"
#include "coretype.h"
#include "bitvect.h"
#include "errwarn.h"
#include "intnum.h"
#include "floatnum.h"
#include "expr.h"
#include "symrec.h"
#include "bytecode.h"
#include "section.h"
#include "arch.h"
static /*@only@*/ yasm_expr *expr_level_op
(/*@returned@*/ /*@only@*/ yasm_expr *e, int fold_const,
int simplify_ident, int simplify_reg_mul);
static int expr_traverse_nodes_post(/*@null@*/ yasm_expr *e,
/*@null@*/ void *d,
int (*func) (/*@null@*/ yasm_expr *e,
/*@null@*/ void *d));
static void expr_delete_term(yasm_expr__item *term, int recurse);
/* Bitmap of used items. We should really never need more than 2 at a time,
* so 31 is pretty much overkill.
*/
static unsigned long itempool_used = 0;
static yasm_expr__item itempool[31];
/* allocate a new expression node, with children as defined.
* If it's a unary operator, put the element in left and set right=NULL. */
/*@-compmempass@*/
yasm_expr *
yasm_expr_create(yasm_expr_op op, yasm_expr__item *left,
yasm_expr__item *right, unsigned long line)
{
yasm_expr *ptr, *sube;
unsigned long z;
ptr = yasm_xmalloc(sizeof(yasm_expr));
ptr->op = op;
ptr->numterms = 0;
ptr->terms[0].type = YASM_EXPR_NONE;
ptr->terms[1].type = YASM_EXPR_NONE;
if (left) {
ptr->terms[0] = *left; /* structure copy */
z = (unsigned long)(left-itempool);
if (z>=31)
yasm_internal_error(N_("could not find expritem in pool"));
itempool_used &= ~(1<<z);
ptr->numterms++;
/* Search downward until we find something *other* than an
* IDENT, then bring it up to the current level.
*/
while (ptr->terms[0].type == YASM_EXPR_EXPR &&
ptr->terms[0].data.expn->op == YASM_EXPR_IDENT) {
sube = ptr->terms[0].data.expn;
ptr->terms[0] = sube->terms[0]; /* structure copy */
/*@-usereleased@*/
yasm_xfree(sube);
/*@=usereleased@*/
}
} else {
yasm_internal_error(N_("Right side of expression must exist"));
}
if (right) {
ptr->terms[1] = *right; /* structure copy */
z = (unsigned long)(right-itempool);
if (z>=31)
yasm_internal_error(N_("could not find expritem in pool"));
itempool_used &= ~(1<<z);
ptr->numterms++;
/* Search downward until we find something *other* than an
* IDENT, then bring it up to the current level.
*/
while (ptr->terms[1].type == YASM_EXPR_EXPR &&
ptr->terms[1].data.expn->op == YASM_EXPR_IDENT) {
sube = ptr->terms[1].data.expn;
ptr->terms[1] = sube->terms[0]; /* structure copy */
/*@-usereleased@*/
yasm_xfree(sube);
/*@=usereleased@*/
}
}
ptr->line = line;
return expr_level_op(ptr, 1, 1, 0);
}
/*@=compmempass@*/
/* helpers */
static yasm_expr__item *
expr_get_item(void)
{
int z = 0;
unsigned long v = itempool_used & 0x7fffffff;
while (v & 1) {
v >>= 1;
z++;
}
if (z>=31)
yasm_internal_error(N_("too many expritems"));
itempool_used |= 1<<z;
return &itempool[z];
}
yasm_expr__item *
yasm_expr_precbc(yasm_bytecode *precbc)
{
yasm_expr__item *e = expr_get_item();
e->type = YASM_EXPR_PRECBC;
e->data.precbc = precbc;
return e;
}
yasm_expr__item *
yasm_expr_sym(yasm_symrec *s)
{
yasm_expr__item *e = expr_get_item();
e->type = YASM_EXPR_SYM;
e->data.sym = s;
return e;
}
yasm_expr__item *
yasm_expr_expr(yasm_expr *x)
{
yasm_expr__item *e = expr_get_item();
e->type = YASM_EXPR_EXPR;
e->data.expn = x;
return e;
}
yasm_expr__item *
yasm_expr_int(yasm_intnum *i)
{
yasm_expr__item *e = expr_get_item();
e->type = YASM_EXPR_INT;
e->data.intn = i;
return e;
}
yasm_expr__item *
yasm_expr_float(yasm_floatnum *f)
{
yasm_expr__item *e = expr_get_item();
e->type = YASM_EXPR_FLOAT;
e->data.flt = f;
return e;
}
yasm_expr__item *
yasm_expr_reg(uintptr_t reg)
{
yasm_expr__item *e = expr_get_item();
e->type = YASM_EXPR_REG;
e->data.reg = reg;
return e;
}
/* Transforms instances of symrec-symrec [symrec+(-1*symrec)] into single
* expritems if possible. Uses a simple n^2 algorithm because n is usually
* quite small. Also works for precbc-precbc (or symrec-precbc,
* precbc-symrec).
*/
static /*@only@*/ yasm_expr *
expr_xform_bc_dist_base(/*@returned@*/ /*@only@*/ yasm_expr *e,
/*@null@*/ void *cbd,
int (*callback) (yasm_expr__item *ei,
yasm_bytecode *precbc,
yasm_bytecode *precbc2,
void *cbd))
{
int i;
/*@dependent@*/ yasm_section *sect;
/*@dependent@*/ /*@null@*/ yasm_bytecode *precbc;
int numterms;
/* Handle symrec-symrec in ADD exprs by looking for (-1*symrec) and
* symrec term pairs (where both symrecs are in the same segment).
*/
if (e->op != YASM_EXPR_ADD)
return e;
for (i=0; i<e->numterms; i++) {
int j;
yasm_expr *sube;
yasm_intnum *intn;
yasm_symrec *sym = NULL;
/*@dependent@*/ yasm_section *sect2;
/*@dependent@*/ /*@null@*/ yasm_bytecode *precbc2;
/* First look for an (-1*symrec) term */
if (e->terms[i].type != YASM_EXPR_EXPR)
continue;
sube = e->terms[i].data.expn;
if (sube->op != YASM_EXPR_MUL || sube->numterms != 2)
continue;
if (sube->terms[0].type == YASM_EXPR_INT &&
(sube->terms[1].type == YASM_EXPR_SYM ||
sube->terms[1].type == YASM_EXPR_PRECBC)) {
intn = sube->terms[0].data.intn;
if (sube->terms[1].type == YASM_EXPR_PRECBC)
precbc = sube->terms[1].data.precbc;
else
sym = sube->terms[1].data.sym;
} else if ((sube->terms[0].type == YASM_EXPR_SYM ||
sube->terms[0].type == YASM_EXPR_PRECBC) &&
sube->terms[1].type == YASM_EXPR_INT) {
if (sube->terms[0].type == YASM_EXPR_PRECBC)
precbc = sube->terms[0].data.precbc;
else
sym = sube->terms[0].data.sym;
intn = sube->terms[1].data.intn;
} else
continue;
if (!yasm_intnum_is_neg1(intn))
continue;
if (sym && !yasm_symrec_get_label(sym, &precbc))
continue;
sect2 = yasm_bc_get_section(precbc);
/* Now look for a symrec term in the same segment */
for (j=0; j<e->numterms; j++) {
if (((e->terms[j].type == YASM_EXPR_SYM &&
yasm_symrec_get_label(e->terms[j].data.sym, &precbc2)) ||
(e->terms[j].type == YASM_EXPR_PRECBC &&
(precbc2 = e->terms[j].data.precbc))) &&
(sect = yasm_bc_get_section(precbc2)) &&
sect == sect2 &&
callback(&e->terms[j], precbc, precbc2, cbd)) {
/* Delete the matching (-1*symrec) term */
yasm_expr_destroy(sube);
e->terms[i].type = YASM_EXPR_NONE;
break; /* stop looking for matching symrec term */
}
}
}
/* Clean up any deleted (EXPR_NONE) terms */
numterms = 0;
for (i=0; i<e->numterms; i++) {
if (e->terms[i].type != YASM_EXPR_NONE)
e->terms[numterms++] = e->terms[i]; /* structure copy */
}
if (e->numterms != numterms) {
e->numterms = numterms;
e = yasm_xrealloc(e, sizeof(yasm_expr)+((numterms<2) ? 0 :
sizeof(yasm_expr__item)*(numterms-2)));
if (numterms == 1)
e->op = YASM_EXPR_IDENT;
}
return e;
}
static int
expr_xform_bc_dist_cb(yasm_expr__item *ei, yasm_bytecode *precbc,
yasm_bytecode *precbc2, /*@null@*/ void *d)
{
yasm_intnum *dist = yasm_calc_bc_dist(precbc, precbc2);
if (!dist)
return 0;
/* Change the term to an integer */
ei->type = YASM_EXPR_INT;
ei->data.intn = dist;
return 1;
}
/* Transforms instances of symrec-symrec [symrec+(-1*symrec)] into integers if
* possible.
*/
static /*@only@*/ yasm_expr *
expr_xform_bc_dist(/*@returned@*/ /*@only@*/ yasm_expr *e)
{
return expr_xform_bc_dist_base(e, NULL, expr_xform_bc_dist_cb);
}
typedef struct bc_dist_subst_cbd {
void (*callback) (unsigned int subst, yasm_bytecode *precbc,
yasm_bytecode *precbc2, void *cbd);
void *cbd;
unsigned int subst;
} bc_dist_subst_cbd;
static int
expr_bc_dist_subst_cb(yasm_expr__item *ei, yasm_bytecode *precbc,
yasm_bytecode *precbc2, /*@null@*/ void *d)
{
bc_dist_subst_cbd *my_cbd = d;
assert(my_cbd != NULL);
/* Call higher-level callback */
my_cbd->callback(my_cbd->subst, precbc, precbc2, my_cbd->cbd);
/* Change the term to an subst */
ei->type = YASM_EXPR_SUBST;
ei->data.subst = my_cbd->subst;
my_cbd->subst++;
return 1;
}
static yasm_expr *
expr_xform_bc_dist_subst(yasm_expr *e, void *d)
{
return expr_xform_bc_dist_base(e, d, expr_bc_dist_subst_cb);
}
int
yasm_expr__bc_dist_subst(yasm_expr **ep, void *cbd,
void (*callback) (unsigned int subst,
yasm_bytecode *precbc,
yasm_bytecode *precbc2,
void *cbd))
{
bc_dist_subst_cbd my_cbd; /* callback info for low-level callback */
my_cbd.callback = callback;
my_cbd.cbd = cbd;
my_cbd.subst = 0;
*ep = yasm_expr__level_tree(*ep, 1, 1, 1, 0, &expr_xform_bc_dist_subst,
&my_cbd);
return my_cbd.subst;
}
/* Negate just a single ExprItem by building a -1*ei subexpression */
static void
expr_xform_neg_item(yasm_expr *e, yasm_expr__item *ei)
{
yasm_expr *sube = yasm_xmalloc(sizeof(yasm_expr));
/* Build -1*ei subexpression */
sube->op = YASM_EXPR_MUL;
sube->line = e->line;
sube->numterms = 2;
sube->terms[0].type = YASM_EXPR_INT;
sube->terms[0].data.intn = yasm_intnum_create_int(-1);
sube->terms[1] = *ei; /* structure copy */
/* Replace original ExprItem with subexp */
ei->type = YASM_EXPR_EXPR;
ei->data.expn = sube;
}
/* Negates e by multiplying by -1, with distribution over lower-precedence
* operators (eg ADD) and special handling to simplify result w/ADD, NEG, and
* others.
*
* Returns a possibly reallocated e.
*/
static /*@only@*/ yasm_expr *
expr_xform_neg_helper(/*@returned@*/ /*@only@*/ yasm_expr *e)
{
yasm_expr *ne;
int i;
switch (e->op) {
case YASM_EXPR_ADD:
/* distribute (recursively if expr) over terms */
for (i=0; i<e->numterms; i++) {
if (e->terms[i].type == YASM_EXPR_EXPR)
e->terms[i].data.expn =
expr_xform_neg_helper(e->terms[i].data.expn);
else
expr_xform_neg_item(e, &e->terms[i]);
}
break;
case YASM_EXPR_SUB:
/* change op to ADD, and recursively negate left side (if expr) */
e->op = YASM_EXPR_ADD;
if (e->terms[0].type == YASM_EXPR_EXPR)
e->terms[0].data.expn =
expr_xform_neg_helper(e->terms[0].data.expn);
else
expr_xform_neg_item(e, &e->terms[0]);
break;
case YASM_EXPR_NEG:
/* Negating a negated value? Make it an IDENT. */
e->op = YASM_EXPR_IDENT;
break;
case YASM_EXPR_IDENT:
/* Negating an ident? Change it into a MUL w/ -1 if there's no
* floatnums present below; if there ARE floatnums, recurse.
*/
if (e->terms[0].type == YASM_EXPR_FLOAT)
yasm_floatnum_calc(e->terms[0].data.flt, YASM_EXPR_NEG, NULL);
else if (e->terms[0].type == YASM_EXPR_INT)
yasm_intnum_calc(e->terms[0].data.intn, YASM_EXPR_NEG, NULL);
else if (e->terms[0].type == YASM_EXPR_EXPR &&
yasm_expr__contains(e->terms[0].data.expn, YASM_EXPR_FLOAT))
expr_xform_neg_helper(e->terms[0].data.expn);
else {
e->op = YASM_EXPR_MUL;
e->numterms = 2;
e->terms[1].type = YASM_EXPR_INT;
e->terms[1].data.intn = yasm_intnum_create_int(-1);
}
break;
default:
/* Everything else. MUL will be combined when it's leveled.
* Make a new expr (to replace e) with -1*e.
*/
ne = yasm_xmalloc(sizeof(yasm_expr));
ne->op = YASM_EXPR_MUL;
ne->line = e->line;
ne->numterms = 2;
ne->terms[0].type = YASM_EXPR_INT;
ne->terms[0].data.intn = yasm_intnum_create_int(-1);
ne->terms[1].type = YASM_EXPR_EXPR;
ne->terms[1].data.expn = e;
return ne;
}
return e;
}
/* Transforms negatives into expressions that are easier to combine:
* -x -> -1*x
* a-b -> a+(-1*b)
*
* Call post-order on an expression tree to transform the entire tree.
*
* Returns a possibly reallocated e.
*/
static /*@only@*/ yasm_expr *
expr_xform_neg(/*@returned@*/ /*@only@*/ yasm_expr *e)
{
switch (e->op) {
case YASM_EXPR_NEG:
/* Turn -x into -1*x */
e->op = YASM_EXPR_IDENT;
return expr_xform_neg_helper(e);
case YASM_EXPR_SUB:
/* Turn a-b into a+(-1*b) */
/* change op to ADD, and recursively negate right side (if expr) */
e->op = YASM_EXPR_ADD;
if (e->terms[1].type == YASM_EXPR_EXPR)
e->terms[1].data.expn =
expr_xform_neg_helper(e->terms[1].data.expn);
else
expr_xform_neg_item(e, &e->terms[1]);
break;
default:
break;
}
return e;
}
/* Look for simple identities that make the entire result constant:
* 0*&x, -1|x, etc.
*/
static int
expr_is_constant(yasm_expr_op op, yasm_intnum *intn)
{
int iszero = yasm_intnum_is_zero(intn);
return ((iszero && op == YASM_EXPR_MUL) ||
(iszero && op == YASM_EXPR_AND) ||
(iszero && op == YASM_EXPR_LAND) ||
(yasm_intnum_is_neg1(intn) && op == YASM_EXPR_OR));
}
/* Look for simple "left" identities like 0+x, 1*x, etc. */
static int
expr_can_destroy_int_left(yasm_expr_op op, yasm_intnum *intn)
{
int iszero = yasm_intnum_is_zero(intn);
return ((yasm_intnum_is_pos1(intn) && op == YASM_EXPR_MUL) ||
(iszero && op == YASM_EXPR_ADD) ||
(yasm_intnum_is_neg1(intn) && op == YASM_EXPR_AND) ||
(!iszero && op == YASM_EXPR_LAND) ||
(iszero && op == YASM_EXPR_OR) ||
(iszero && op == YASM_EXPR_LOR));
}
/* Look for simple "right" identities like x+|-0, x*&/1 */
static int
expr_can_destroy_int_right(yasm_expr_op op, yasm_intnum *intn)
{
int iszero = yasm_intnum_is_zero(intn);
int ispos1 = yasm_intnum_is_pos1(intn);
return ((ispos1 && op == YASM_EXPR_MUL) ||
(ispos1 && op == YASM_EXPR_DIV) ||
(iszero && op == YASM_EXPR_ADD) ||
(iszero && op == YASM_EXPR_SUB) ||
(yasm_intnum_is_neg1(intn) && op == YASM_EXPR_AND) ||
(!iszero && op == YASM_EXPR_LAND) ||
(iszero && op == YASM_EXPR_OR) ||
(iszero && op == YASM_EXPR_LOR) ||
(iszero && op == YASM_EXPR_SHL) ||
(iszero && op == YASM_EXPR_SHR));
}
/* Check for and simplify identities. Returns new number of expr terms.
* Sets e->op = EXPR_IDENT if numterms ends up being 1.
* Uses numterms parameter instead of e->numterms for basis of "new" number
* of terms.
* Assumes int_term is *only* integer term in e.
* NOTE: Really designed to only be used by expr_level_op().
*/
static int
expr_simplify_identity(yasm_expr *e, int numterms, int int_term,
int simplify_reg_mul)
{
int i;
int save_numterms;
/* Don't do this step if it's 1*REG. Save and restore numterms so
* yasm_expr__contains() works correctly.
*/
save_numterms = e->numterms;
e->numterms = numterms;
if (simplify_reg_mul || e->op != YASM_EXPR_MUL
|| !yasm_intnum_is_pos1(e->terms[int_term].data.intn)
|| !yasm_expr__contains(e, YASM_EXPR_REG)) {
/* Check for simple identities that delete the intnum.
* Don't delete if the intnum is the only thing in the expn.
*/
if ((int_term == 0 && numterms > 1 &&
expr_can_destroy_int_left(e->op, e->terms[0].data.intn)) ||
(int_term > 0 &&
expr_can_destroy_int_right(e->op, e->terms[int_term].data.intn))) {
/* Delete the intnum */
yasm_intnum_destroy(e->terms[int_term].data.intn);
/* Slide everything to its right over by 1 */
if (int_term != numterms-1) /* if it wasn't last.. */
memmove(&e->terms[int_term], &e->terms[int_term+1],
(numterms-1-int_term)*sizeof(yasm_expr__item));
/* Update numterms */
numterms--;
int_term = -1; /* no longer an int term */
}
}
e->numterms = save_numterms;
/* Check for simple identites that delete everything BUT the intnum.
* Don't bother if the intnum is the only thing in the expn.
*/
if (numterms > 1 && int_term != -1 &&
expr_is_constant(e->op, e->terms[int_term].data.intn)) {
/* Loop through, deleting everything but the integer term */
for (i=0; i<e->numterms; i++)
if (i != int_term)
expr_delete_term(&e->terms[i], 1);
/* Move integer term to the first term (if not already there) */
if (int_term != 0)
e->terms[0] = e->terms[int_term]; /* structure copy */
/* Set numterms to 1 */
numterms = 1;
}
/* Compute NOT, NEG, and LNOT on single intnum. */
if (numterms == 1 && int_term == 0 &&
(e->op == YASM_EXPR_NOT || e->op == YASM_EXPR_NEG ||
e->op == YASM_EXPR_LNOT))
yasm_intnum_calc(e->terms[0].data.intn, e->op, NULL);
/* Change expression to IDENT if possible. */
if (numterms == 1)
e->op = YASM_EXPR_IDENT;
/* Return the updated numterms */
return numterms;
}
/* Levels the expression tree starting at e. Eg:
* a+(b+c) -> a+b+c
* (a+b)+(c+d) -> a+b+c+d
* Naturally, only levels operators that allow more than two operand terms.
* NOTE: only does *one* level of leveling (no recursion). Should be called
* post-order on a tree to combine deeper levels.
* Also brings up any IDENT values into the current level (for ALL operators).
* Folds (combines by evaluation) *integer* constant values if fold_const != 0.
*
* Returns a possibly reallocated e.
*/
/*@-mustfree@*/
static /*@only@*/ yasm_expr *
expr_level_op(/*@returned@*/ /*@only@*/ yasm_expr *e, int fold_const,
int simplify_ident, int simplify_reg_mul)
{
int i, j, o, fold_numterms, level_numterms, level_fold_numterms;
int first_int_term = -1;
/* Determine how many operands will need to be brought up (for leveling).
* Go ahead and bring up any IDENT'ed values.
*/
while (e->op == YASM_EXPR_IDENT && e->terms[0].type == YASM_EXPR_EXPR) {
yasm_expr *sube = e->terms[0].data.expn;
yasm_xfree(e);
e = sube;
}
/* If non-numeric expression, don't fold constants. */
if (e->op > YASM_EXPR_NONNUM)
fold_const = 0;
level_numterms = e->numterms;
level_fold_numterms = 0;
for (i=0; i<e->numterms; i++) {
/* Search downward until we find something *other* than an
* IDENT, then bring it up to the current level.
*/
while (e->terms[i].type == YASM_EXPR_EXPR &&
e->terms[i].data.expn->op == YASM_EXPR_IDENT) {
yasm_expr *sube = e->terms[i].data.expn;
e->terms[i] = sube->terms[0];
yasm_xfree(sube);
}
if (e->terms[i].type == YASM_EXPR_EXPR &&
e->terms[i].data.expn->op == e->op) {
/* It's an expression w/the same operator, add in its numterms.
* But don't forget to subtract one for the expr itself!
*/
level_numterms += e->terms[i].data.expn->numterms - 1;
/* If we're folding constants, count up the number of constants
* that will be merged in.
*/
if (fold_const)
for (j=0; j<e->terms[i].data.expn->numterms; j++)
if (e->terms[i].data.expn->terms[j].type ==
YASM_EXPR_INT)
level_fold_numterms++;
}
/* Find the first integer term (if one is present) if we're folding
* constants.
*/
if (fold_const && first_int_term == -1 &&
e->terms[i].type == YASM_EXPR_INT)
first_int_term = i;
}
/* Look for other integer terms if there's one and combine.
* Also eliminate empty spaces when combining and adjust numterms
* variables.
*/
fold_numterms = e->numterms;
if (first_int_term != -1) {
for (i=first_int_term+1, o=first_int_term+1; i<e->numterms; i++) {
if (e->terms[i].type == YASM_EXPR_INT) {
yasm_intnum_calc(e->terms[first_int_term].data.intn, e->op,
e->terms[i].data.intn);
fold_numterms--;
level_numterms--;
/* make sure to delete folded intnum */
yasm_intnum_destroy(e->terms[i].data.intn);
} else if (o != i) {
/* copy term if it changed places */
e->terms[o++] = e->terms[i];
} else
o++;
}
if (simplify_ident) {
int new_fold_numterms;
/* Simplify identities and make IDENT if possible. */
new_fold_numterms =
expr_simplify_identity(e, fold_numterms, first_int_term,
simplify_reg_mul);
level_numterms -= fold_numterms-new_fold_numterms;
fold_numterms = new_fold_numterms;
}
if (fold_numterms == 1)
e->op = YASM_EXPR_IDENT;
}
/* Only level operators that allow more than two operand terms.
* Also don't bother leveling if it's not necessary to bring up any terms.
*/
if ((e->op != YASM_EXPR_ADD && e->op != YASM_EXPR_MUL &&
e->op != YASM_EXPR_OR && e->op != YASM_EXPR_AND &&
e->op != YASM_EXPR_LOR && e->op != YASM_EXPR_LAND &&
e->op != YASM_EXPR_LXOR && e->op != YASM_EXPR_XOR) ||
level_numterms <= fold_numterms) {
/* Downsize e if necessary */
if (fold_numterms < e->numterms && e->numterms > 2)
e = yasm_xrealloc(e, sizeof(yasm_expr)+((fold_numterms<2) ? 0 :
sizeof(yasm_expr__item)*(fold_numterms-2)));
/* Update numterms */
e->numterms = fold_numterms;
return e;
}
/* Adjust numterms for constant folding from terms being "pulled up".
* Careful: if there's no integer term in e, then save space for it.
*/
if (fold_const) {
level_numterms -= level_fold_numterms;
if (first_int_term == -1 && level_fold_numterms != 0)
level_numterms++;
}
/* Alloc more (or conceivably less, but not usually) space for e */
e = yasm_xrealloc(e, sizeof(yasm_expr)+((level_numterms<2) ? 0 :
sizeof(yasm_expr__item)*(level_numterms-2)));
/* Copy up ExprItem's. Iterate from right to left to keep the same
* ordering as was present originally.
* Combine integer terms as necessary.
*/
for (i=fold_numterms-1, o=level_numterms-1; i>=0; i--) {
if (e->terms[i].type == YASM_EXPR_EXPR &&
e->terms[i].data.expn->op == e->op) {
/* bring up subexpression */
yasm_expr *sube = e->terms[i].data.expn;
/* copy terms right to left */
for (j=sube->numterms-1; j>=0; j--) {
if (fold_const && sube->terms[j].type == YASM_EXPR_INT) {
/* Need to fold it in.. but if there's no int term already,
* just copy into a new one.
*/
if (first_int_term == -1) {
first_int_term = o--;
e->terms[first_int_term] = sube->terms[j]; /* struc */
} else {
yasm_intnum_calc(e->terms[first_int_term].data.intn,
e->op, sube->terms[j].data.intn);
/* make sure to delete folded intnum */
yasm_intnum_destroy(sube->terms[j].data.intn);
}
} else {
if (o == first_int_term)
o--;
e->terms[o--] = sube->terms[j]; /* structure copy */
}
}
/* delete subexpression, but *don't delete nodes* (as we've just
* copied them!)
*/
yasm_xfree(sube);
} else if (o != i) {
/* copy operand if it changed places */
if (o == first_int_term)
o--;
e->terms[o] = e->terms[i];
/* If we moved the first_int_term, change first_int_num too */
if (i == first_int_term)
first_int_term = o;
o--;
} else
o--;
}
/* Simplify identities, make IDENT if possible, and save to e->numterms. */
if (simplify_ident && first_int_term != -1) {
e->numterms = expr_simplify_identity(e, level_numterms,
first_int_term, simplify_reg_mul);
} else {
e->numterms = level_numterms;
if (level_numterms == 1)
e->op = YASM_EXPR_IDENT;
}
return e;
}
/*@=mustfree@*/
typedef SLIST_HEAD(yasm__exprhead, yasm__exprentry) yasm__exprhead;
typedef struct yasm__exprentry {
/*@reldef@*/ SLIST_ENTRY(yasm__exprentry) next;
/*@null@*/ const yasm_expr *e;
} yasm__exprentry;
static yasm_expr *
expr_expand_equ(yasm_expr *e, yasm__exprhead *eh)
{
int i;
yasm__exprentry ee;
/* traverse terms */
for (i=0; i<e->numterms; i++) {
const yasm_expr *equ_expr;
/* Expand equ's. */
if (e->terms[i].type == YASM_EXPR_SYM &&
(equ_expr = yasm_symrec_get_equ(e->terms[i].data.sym))) {
yasm__exprentry *np;
/* Check for circular reference */
SLIST_FOREACH(np, eh, next) {
if (np->e == equ_expr) {
yasm_error_set(YASM_ERROR_TOO_COMPLEX,
N_("circular reference detected"));
return e;
}
}
e->terms[i].type = YASM_EXPR_EXPR;
e->terms[i].data.expn = yasm_expr_copy(equ_expr);
/* Remember we saw this equ and recurse */
ee.e = equ_expr;
SLIST_INSERT_HEAD(eh, &ee, next);
e->terms[i].data.expn = expr_expand_equ(e->terms[i].data.expn, eh);
SLIST_REMOVE_HEAD(eh, next);
} else if (e->terms[i].type == YASM_EXPR_EXPR)
/* Recurse */
e->terms[i].data.expn = expr_expand_equ(e->terms[i].data.expn, eh);
}
return e;
}
static yasm_expr *
expr_level_tree(yasm_expr *e, int fold_const, int simplify_ident,
int simplify_reg_mul, int calc_bc_dist,
yasm_expr_xform_func expr_xform_extra,
void *expr_xform_extra_data)
{
int i;
e = expr_xform_neg(e);
/* traverse terms */
for (i=0; i<e->numterms; i++) {
/* Recurse */
if (e->terms[i].type == YASM_EXPR_EXPR)
e->terms[i].data.expn =
expr_level_tree(e->terms[i].data.expn, fold_const,
simplify_ident, simplify_reg_mul, calc_bc_dist,
expr_xform_extra, expr_xform_extra_data);
}
/* Check for SEG of SEG:OFF, if we match, simplify to just the segment */
if (e->op == YASM_EXPR_SEG && e->terms[0].type == YASM_EXPR_EXPR &&
e->terms[0].data.expn->op == YASM_EXPR_SEGOFF) {
e->op = YASM_EXPR_IDENT;
e->terms[0].data.expn->op = YASM_EXPR_IDENT;
/* Destroy the second (offset) term */
e->terms[0].data.expn->numterms = 1;
expr_delete_term(&e->terms[0].data.expn->terms[1], 1);
}
/* do callback */
e = expr_level_op(e, fold_const, simplify_ident, simplify_reg_mul);
if (calc_bc_dist || expr_xform_extra) {
if (calc_bc_dist)
e = expr_xform_bc_dist(e);
if (expr_xform_extra)
e = expr_xform_extra(e, expr_xform_extra_data);
e = expr_level_tree(e, fold_const, simplify_ident, simplify_reg_mul,
0, NULL, NULL);
}
return e;
}
/* Level an entire expn tree, expanding equ's as we go */
yasm_expr *
yasm_expr__level_tree(yasm_expr *e, int fold_const, int simplify_ident,
int simplify_reg_mul, int calc_bc_dist,
yasm_expr_xform_func expr_xform_extra,
void *expr_xform_extra_data)
{
yasm__exprhead eh;
SLIST_INIT(&eh);
if (!e)
return 0;
e = expr_expand_equ(e, &eh);
e = expr_level_tree(e, fold_const, simplify_ident, simplify_reg_mul,
calc_bc_dist, expr_xform_extra, expr_xform_extra_data);
return e;
}
/* Comparison function for expr_order_terms().
* Assumes ExprType enum is in canonical order.
*/
static int
expr_order_terms_compare(const void *va, const void *vb)
{
const yasm_expr__item *a = va, *b = vb;
return (a->type - b->type);
}
/* Reorder terms of e into canonical order. Only reorders if reordering
* doesn't change meaning of expression. (eg, doesn't reorder SUB).
* Canonical order: REG, INT, FLOAT, SYM, EXPR.
* Multiple terms of a single type are kept in the same order as in
* the original expression.
* NOTE: Only performs reordering on *one* level (no recursion).
*/
void
yasm_expr__order_terms(yasm_expr *e)
{
/* don't bother reordering if only one element */
if (e->numterms == 1)
return;
/* only reorder some types of operations */
switch (e->op) {
case YASM_EXPR_ADD:
case YASM_EXPR_MUL:
case YASM_EXPR_OR:
case YASM_EXPR_AND:
case YASM_EXPR_XOR:
case YASM_EXPR_LOR:
case YASM_EXPR_LAND:
case YASM_EXPR_LXOR:
/* Use mergesort to sort. It's fast on already sorted values and a
* stable sort (multiple terms of same type are kept in the same
* order).
*/
yasm__mergesort(e->terms, (size_t)e->numterms,
sizeof(yasm_expr__item), expr_order_terms_compare);
break;
default:
break;
}
}
static void
expr_item_copy(yasm_expr__item *dest, const yasm_expr__item *src)
{
dest->type = src->type;
switch (src->type) {
case YASM_EXPR_SYM:
/* Symbols don't need to be copied */
dest->data.sym = src->data.sym;
break;
case YASM_EXPR_PRECBC:
/* Nor do direct bytecode references */
dest->data.precbc = src->data.precbc;
break;
case YASM_EXPR_EXPR:
dest->data.expn = yasm_expr__copy_except(src->data.expn, -1);
break;
case YASM_EXPR_INT:
dest->data.intn = yasm_intnum_copy(src->data.intn);
break;
case YASM_EXPR_FLOAT:
dest->data.flt = yasm_floatnum_copy(src->data.flt);
break;
case YASM_EXPR_REG:
dest->data.reg = src->data.reg;
break;
case YASM_EXPR_SUBST:
dest->data.subst = src->data.subst;
break;
default:
break;
}
}
/* Copy entire expression EXCEPT for index "except" at *top level only*. */
yasm_expr *
yasm_expr__copy_except(const yasm_expr *e, int except)
{
yasm_expr *n;
int i;
n = yasm_xmalloc(sizeof(yasm_expr) +
sizeof(yasm_expr__item)*(e->numterms<2?0:e->numterms-2));
n->op = e->op;
n->line = e->line;
n->numterms = e->numterms;
for (i=0; i<e->numterms; i++) {
if (i != except)
expr_item_copy(&n->terms[i], &e->terms[i]);
}
return n;
}
static void
expr_delete_term(yasm_expr__item *term, int recurse)
{
switch (term->type) {
case YASM_EXPR_INT:
yasm_intnum_destroy(term->data.intn);
break;
case YASM_EXPR_FLOAT:
yasm_floatnum_destroy(term->data.flt);
break;
case YASM_EXPR_EXPR:
if (recurse)
yasm_expr_destroy(term->data.expn);
break;
default:
break;
}
}
static int
expr_destroy_each(/*@only@*/ yasm_expr *e, /*@unused@*/ void *d)
{
int i;
for (i=0; i<e->numterms; i++)
expr_delete_term(&e->terms[i], 0);
yasm_xfree(e); /* free ourselves */
return 0; /* don't stop recursion */
}
/*@-mustfree@*/
void
yasm_expr_destroy(yasm_expr *e)
{
expr_traverse_nodes_post(e, NULL, expr_destroy_each);
}
/*@=mustfree@*/
int
yasm_expr_is_op(const yasm_expr *e, yasm_expr_op op)
{
return (e->op == op);
}
static int
expr_contains_callback(const yasm_expr__item *ei, void *d)
{
yasm_expr__type *t = d;
return (ei->type & *t);
}
int
yasm_expr__contains(const yasm_expr *e, yasm_expr__type t)
{
return yasm_expr__traverse_leaves_in_const(e, &t, expr_contains_callback);
}
typedef struct subst_cbd {
unsigned int num_items;
const yasm_expr__item *items;
} subst_cbd;
static int
expr_subst_callback(yasm_expr__item *ei, void *d)
{
subst_cbd *cbd = d;
if (ei->type != YASM_EXPR_SUBST)
return 0;
if (ei->data.subst >= cbd->num_items)
return 1; /* error */
expr_item_copy(ei, &cbd->items[ei->data.subst]);
return 0;
}
int
yasm_expr__subst(yasm_expr *e, unsigned int num_items,
const yasm_expr__item *items)
{
subst_cbd cbd;
cbd.num_items = num_items;
cbd.items = items;
return yasm_expr__traverse_leaves_in(e, &cbd, expr_subst_callback);
}
/* Traverse over expression tree, calling func for each operation AFTER the
* branches (if expressions) have been traversed (eg, postorder
* traversal). The data pointer d is passed to each func call.
*
* Stops early (and returns 1) if func returns 1. Otherwise returns 0.
*/
static int
expr_traverse_nodes_post(yasm_expr *e, void *d,
int (*func) (/*@null@*/ yasm_expr *e,
/*@null@*/ void *d))
{
int i;
if (!e)
return 0;
/* traverse terms */
for (i=0; i<e->numterms; i++) {
if (e->terms[i].type == YASM_EXPR_EXPR &&
expr_traverse_nodes_post(e->terms[i].data.expn, d, func))
return 1;
}
/* do callback */
return func(e, d);
}
/* Traverse over expression tree in order, calling func for each leaf
* (non-operation). The data pointer d is passed to each func call.
*
* Stops early (and returns 1) if func returns 1. Otherwise returns 0.
*/
int
yasm_expr__traverse_leaves_in_const(const yasm_expr *e, void *d,
int (*func) (/*@null@*/ const yasm_expr__item *ei, /*@null@*/ void *d))
{
int i;
if (!e)
return 0;
for (i=0; i<e->numterms; i++) {
if (e->terms[i].type == YASM_EXPR_EXPR) {
if (yasm_expr__traverse_leaves_in_const(e->terms[i].data.expn, d,
func))
return 1;
} else {
if (func(&e->terms[i], d))
return 1;
}
}
return 0;
}
/* Traverse over expression tree in order, calling func for each leaf
* (non-operation). The data pointer d is passed to each func call.
*
* Stops early (and returns 1) if func returns 1. Otherwise returns 0.
*/
int
yasm_expr__traverse_leaves_in(yasm_expr *e, void *d,
int (*func) (/*@null@*/ yasm_expr__item *ei, /*@null@*/ void *d))
{
int i;
if (!e)
return 0;
for (i=0; i<e->numterms; i++) {
if (e->terms[i].type == YASM_EXPR_EXPR) {
if (yasm_expr__traverse_leaves_in(e->terms[i].data.expn, d, func))
return 1;
} else {
if (func(&e->terms[i], d))
return 1;
}
}
return 0;
}
yasm_expr *
yasm_expr_extract_deep_segoff(yasm_expr **ep)
{
yasm_expr *retval;
yasm_expr *e = *ep;
int i;
/* Try to extract at this level */
retval = yasm_expr_extract_segoff(ep);
if (retval)
return retval;
/* Not at this level? Search any expr children. */
for (i=0; i<e->numterms; i++) {
if (e->terms[i].type == YASM_EXPR_EXPR) {
retval = yasm_expr_extract_deep_segoff(&e->terms[i].data.expn);
if (retval)
return retval;
}
}
/* Didn't find one */
return NULL;
}
yasm_expr *
yasm_expr_extract_segoff(yasm_expr **ep)
{
yasm_expr *retval;
yasm_expr *e = *ep;
/* If not SEG:OFF, we can't do this transformation */
if (e->op != YASM_EXPR_SEGOFF)
return NULL;
/* Extract the SEG portion out to its own expression */
if (e->terms[0].type == YASM_EXPR_EXPR)
retval = e->terms[0].data.expn;
else {
/* Need to build IDENT expression to hold non-expression contents */
retval = yasm_xmalloc(sizeof(yasm_expr));
retval->op = YASM_EXPR_IDENT;
retval->numterms = 1;
retval->terms[0] = e->terms[0]; /* structure copy */
}
/* Delete the SEG: portion by changing the expression into an IDENT */
e->op = YASM_EXPR_IDENT;
e->numterms = 1;
e->terms[0] = e->terms[1]; /* structure copy */
return retval;
}
yasm_expr *
yasm_expr_extract_wrt(yasm_expr **ep)
{
yasm_expr *retval;
yasm_expr *e = *ep;
/* If not WRT, we can't do this transformation */
if (e->op != YASM_EXPR_WRT)
return NULL;
/* Extract the right side portion out to its own expression */
if (e->terms[1].type == YASM_EXPR_EXPR)
retval = e->terms[1].data.expn;
else {
/* Need to build IDENT expression to hold non-expression contents */
retval = yasm_xmalloc(sizeof(yasm_expr));
retval->op = YASM_EXPR_IDENT;
retval->numterms = 1;
retval->terms[0] = e->terms[1]; /* structure copy */
}
/* Delete the right side portion by changing the expr into an IDENT */
e->op = YASM_EXPR_IDENT;
e->numterms = 1;
return retval;
}
/*@-unqualifiedtrans -nullderef -nullstate -onlytrans@*/
yasm_intnum *
yasm_expr_get_intnum(yasm_expr **ep, int calc_bc_dist)
{
*ep = yasm_expr_simplify(*ep, calc_bc_dist);
if ((*ep)->op == YASM_EXPR_IDENT && (*ep)->terms[0].type == YASM_EXPR_INT)
return (*ep)->terms[0].data.intn;
else
return (yasm_intnum *)NULL;
}
/*@=unqualifiedtrans =nullderef -nullstate -onlytrans@*/
/*@-unqualifiedtrans -nullderef -nullstate -onlytrans@*/
const yasm_symrec *
yasm_expr_get_symrec(yasm_expr **ep, int simplify)
{
if (simplify)
*ep = yasm_expr_simplify(*ep, 0);
if ((*ep)->op == YASM_EXPR_IDENT && (*ep)->terms[0].type == YASM_EXPR_SYM)
return (*ep)->terms[0].data.sym;
else
return (yasm_symrec *)NULL;
}
/*@=unqualifiedtrans =nullderef -nullstate -onlytrans@*/
/*@-unqualifiedtrans -nullderef -nullstate -onlytrans@*/
const uintptr_t *
yasm_expr_get_reg(yasm_expr **ep, int simplify)
{
if (simplify)
*ep = yasm_expr_simplify(*ep, 0);
if ((*ep)->op == YASM_EXPR_IDENT && (*ep)->terms[0].type == YASM_EXPR_REG)
return &((*ep)->terms[0].data.reg);
else
return NULL;
}
/*@=unqualifiedtrans =nullderef -nullstate -onlytrans@*/
void
yasm_expr_print(const yasm_expr *e, FILE *f)
{
char opstr[8];
int i;
if (!e) {
fprintf(f, "(nil)");
return;
}
switch (e->op) {
case YASM_EXPR_ADD:
strcpy(opstr, "+");
break;
case YASM_EXPR_SUB:
strcpy(opstr, "-");
break;
case YASM_EXPR_MUL:
strcpy(opstr, "*");
break;
case YASM_EXPR_DIV:
strcpy(opstr, "/");
break;
case YASM_EXPR_SIGNDIV:
strcpy(opstr, "//");
break;
case YASM_EXPR_MOD:
strcpy(opstr, "%");
break;
case YASM_EXPR_SIGNMOD:
strcpy(opstr, "%%");
break;
case YASM_EXPR_NEG:
fprintf(f, "-");
opstr[0] = 0;
break;
case YASM_EXPR_NOT:
fprintf(f, "~");
opstr[0] = 0;
break;
case YASM_EXPR_OR:
strcpy(opstr, "|");
break;
case YASM_EXPR_AND:
strcpy(opstr, "&");
break;
case YASM_EXPR_XOR:
strcpy(opstr, "^");
break;
case YASM_EXPR_XNOR:
strcpy(opstr, "XNOR");
break;
case YASM_EXPR_NOR:
strcpy(opstr, "NOR");
break;
case YASM_EXPR_SHL:
strcpy(opstr, "<<");
break;
case YASM_EXPR_SHR:
strcpy(opstr, ">>");
break;
case YASM_EXPR_LOR:
strcpy(opstr, "||");
break;
case YASM_EXPR_LAND:
strcpy(opstr, "&&");
break;
case YASM_EXPR_LNOT:
strcpy(opstr, "!");
break;
case YASM_EXPR_LXOR:
strcpy(opstr, "^^");
break;
case YASM_EXPR_LXNOR:
strcpy(opstr, "LXNOR");
break;
case YASM_EXPR_LNOR:
strcpy(opstr, "LNOR");
break;
case YASM_EXPR_LT:
strcpy(opstr, "<");
break;
case YASM_EXPR_GT:
strcpy(opstr, ">");
break;
case YASM_EXPR_LE:
strcpy(opstr, "<=");
break;
case YASM_EXPR_GE:
strcpy(opstr, ">=");
break;
case YASM_EXPR_NE:
strcpy(opstr, "!=");
break;
case YASM_EXPR_EQ:
strcpy(opstr, "==");
break;
case YASM_EXPR_SEG:
fprintf(f, "SEG ");
opstr[0] = 0;
break;
case YASM_EXPR_WRT:
strcpy(opstr, " WRT ");
break;
case YASM_EXPR_SEGOFF:
strcpy(opstr, ":");
break;
case YASM_EXPR_IDENT:
opstr[0] = 0;
break;
default:
strcpy(opstr, " !UNK! ");
break;
}
for (i=0; i<e->numterms; i++) {
switch (e->terms[i].type) {
case YASM_EXPR_PRECBC:
fprintf(f, "{%lx}",
yasm_bc_next_offset(e->terms[i].data.precbc));
break;
case YASM_EXPR_SYM:
fprintf(f, "%s", yasm_symrec_get_name(e->terms[i].data.sym));
break;
case YASM_EXPR_EXPR:
fprintf(f, "(");
yasm_expr_print(e->terms[i].data.expn, f);
fprintf(f, ")");
break;
case YASM_EXPR_INT:
yasm_intnum_print(e->terms[i].data.intn, f);
break;
case YASM_EXPR_FLOAT:
yasm_floatnum_print(e->terms[i].data.flt, f);
break;
case YASM_EXPR_REG:
/* FIXME */
/*yasm_arch_reg_print(arch, e->terms[i].data.reg, f);*/
break;
case YASM_EXPR_SUBST:
fprintf(f, "[%u]", e->terms[i].data.subst);
break;
case YASM_EXPR_NONE:
break;
}
if (i < e->numterms-1)
fprintf(f, "%s", opstr);
}
}
unsigned int
yasm_expr_size(const yasm_expr *e)
{
int i;
int seen = 0;
unsigned int size = 0, newsize;
if (e->op == YASM_EXPR_IDENT) {
if (e->terms[0].type == YASM_EXPR_SYM)
return yasm_symrec_get_size(e->terms[0].data.sym);
return 0;
}
if (e->op != YASM_EXPR_ADD && e->op != YASM_EXPR_SUB)
return 0;
for (i=0; i<e->numterms; i++) {
newsize = 0;
switch (e->terms[i].type) {
case YASM_EXPR_EXPR:
newsize = yasm_expr_size(e->terms[i].data.expn);
break;
case YASM_EXPR_SYM:
newsize = yasm_symrec_get_size(e->terms[i].data.sym);
break;
default:
break;
}
if (newsize) {
size = newsize;
if (seen)
/* either sum of idents (?!) or substract of idents */
return 0;
seen = 1;
}
}
/* exactly one offset */
return size;
}
const char *
yasm_expr_segment(const yasm_expr *e)
{
int i;
int seen = 0;
const char *segment = NULL;
if (e->op == YASM_EXPR_IDENT) {
if (e->terms[0].type == YASM_EXPR_SYM)
return yasm_symrec_get_segment(e->terms[0].data.sym);
return NULL;
}
if (e->op != YASM_EXPR_ADD && e->op != YASM_EXPR_SUB)
return NULL;
for (i=0; i<e->numterms; i++) {
if ((e->op == YASM_EXPR_ADD || !i) &&
e->terms[i].type == YASM_EXPR_EXPR) {
if ((segment = yasm_expr_segment(e->terms[i].data.expn))) {
if (seen) {
/* either sum of idents (?!) or substract of idents */
return NULL;
}
seen = 1;
}
}
}
/* exactly one offset */
return segment;
}