summaryrefslogtreecommitdiff
path: root/gas/expr.c
diff options
context:
space:
mode:
authorNick Clifton <nickc@redhat.com>2005-10-11 11:16:17 +0000
committerNick Clifton <nickc@redhat.com>2005-10-11 11:16:17 +0000
commit9497f5ac6bc10bdd65ea471787619bde1edca77d (patch)
tree7f36b3cb6f0d84b058dfba51242bd900edde9503 /gas/expr.c
parent1334d4d50c52bc295dace4982442369838f478b3 (diff)
downloadbinutils-9497f5ac6bc10bdd65ea471787619bde1edca77d.tar.gz
binutils-9497f5ac6bc10bdd65ea471787619bde1edca77d.tar.bz2
binutils-9497f5ac6bc10bdd65ea471787619bde1edca77d.zip
This adjusts equate handling by
- allowing true forward references (which will always assume the referenced symbols have at the point of use) through the new .eqv pseudo-op and the new == operator - disallowing changing .equiv-generated equates (so that the protection this provides is both forward and backward) - snapshotting equates when their value gets changed so that previous uses don't get affected by the new value. - allowing expressions in places where absolute expressions (or register names) are needed which were not completely resolvable at the point of their definition but which are fully resolvable at the point of use In addition it fixes PR/288.
Diffstat (limited to 'gas/expr.c')
-rw-r--r--gas/expr.c222
1 files changed, 211 insertions, 11 deletions
diff --git a/gas/expr.c b/gas/expr.c
index fff0fde7075..32de0f7394b 100644
--- a/gas/expr.c
+++ b/gas/expr.c
@@ -41,7 +41,7 @@ static void integer_constant (int radix, expressionS * expressionP);
static void mri_char_constant (expressionS *);
static void current_location (expressionS *);
static void clean_up_expression (expressionS * expressionP);
-static segT operand (expressionS *);
+static segT operand (expressionS *, enum expr_mode);
static operatorT operator (int *);
extern const char EXP_CHARS[], FLT_CHARS[];
@@ -708,7 +708,7 @@ current_location (expressionS *expressionp)
Input_line_pointer->(next non-blank) char after operand. */
static segT
-operand (expressionS *expressionP)
+operand (expressionS *expressionP, enum expr_mode mode)
{
char c;
symbolS *symbolP; /* Points to symbol. */
@@ -944,7 +944,10 @@ operand (expressionS *expressionP)
case '[':
#endif
/* Didn't begin with digit & not a name. */
- segment = expression (expressionP);
+ if (mode != expr_defer)
+ segment = expression (expressionP);
+ else
+ segment = deferred_expression (expressionP);
/* expression () will pass trailing whitespace. */
if ((c == '(' && *input_line_pointer != ')')
|| (c == '[' && *input_line_pointer != ']'))
@@ -1002,7 +1005,7 @@ operand (expressionS *expressionP)
if (0 && (c == '-' || c == '+') && *input_line_pointer == c)
goto target_op;
- operand (expressionP);
+ operand (expressionP, mode);
if (expressionP->X_op == O_constant)
{
/* input_line_pointer -> char after operand. */
@@ -1214,7 +1217,7 @@ operand (expressionS *expressionP)
specially in certain contexts. If a name always has a
specific value, it can often be handled by simply
entering it in the symbol table. */
- if (md_parse_name (name, expressionP, &c))
+ if (md_parse_name (name, expressionP, mode, &c))
{
*input_line_pointer = c;
break;
@@ -1265,12 +1268,12 @@ operand (expressionS *expressionP)
/* If we have an absolute symbol or a reg, then we know its
value now. */
segment = S_GET_SEGMENT (symbolP);
- if (segment == absolute_section)
+ if (mode != expr_defer && segment == absolute_section)
{
expressionP->X_op = O_constant;
expressionP->X_add_number = S_GET_VALUE (symbolP);
}
- else if (segment == reg_section)
+ else if (mode != expr_defer && segment == reg_section)
{
expressionP->X_op = O_register;
expressionP->X_add_number = S_GET_VALUE (symbolP);
@@ -1314,6 +1317,9 @@ operand (expressionS *expressionP)
if (expressionP->X_add_symbol)
symbol_mark_used (expressionP->X_add_symbol);
+ expressionP->X_add_symbol = symbol_clone_if_forward_ref (expressionP->X_add_symbol);
+ expressionP->X_op_symbol = symbol_clone_if_forward_ref (expressionP->X_op_symbol);
+
switch (expressionP->X_op)
{
default:
@@ -1625,7 +1631,8 @@ operator (int *num_chars)
segT
expr (int rankarg, /* Larger # is higher rank. */
- expressionS *resultP /* Deliver result here. */)
+ expressionS *resultP, /* Deliver result here. */
+ enum expr_mode mode /* Controls behavior. */)
{
operator_rankT rank = (operator_rankT) rankarg;
segT retval;
@@ -1640,7 +1647,7 @@ expr (int rankarg, /* Larger # is higher rank. */
if (rank == 0)
dot_value = frag_now_fix ();
- retval = operand (resultP);
+ retval = operand (resultP, mode);
/* operand () gobbles spaces. */
know (*input_line_pointer != ' ');
@@ -1652,7 +1659,7 @@ expr (int rankarg, /* Larger # is higher rank. */
input_line_pointer += op_chars; /* -> after operator. */
- rightseg = expr (op_rank[(int) op_left], &right);
+ rightseg = expr (op_rank[(int) op_left], &right, mode);
if (right.X_op == O_absent)
{
as_warn (_("missing operand; zero assumed"));
@@ -1867,8 +1874,201 @@ expr (int rankarg, /* Larger # is higher rank. */
if (resultP->X_add_symbol)
symbol_mark_used (resultP->X_add_symbol);
+ if (rank == 0 && mode == expr_evaluate)
+ resolve_expression (resultP);
+
return resultP->X_op == O_constant ? absolute_section : retval;
}
+
+/* Resolve an expression without changing any symbols/sub-expressions
+ used. */
+
+int
+resolve_expression (expressionS *expressionP)
+{
+ /* Help out with CSE. */
+ valueT final_val = expressionP->X_add_number;
+ symbolS *add_symbol = expressionP->X_add_symbol;
+ symbolS *op_symbol = expressionP->X_op_symbol;
+ operatorT op = expressionP->X_op;
+ valueT left, right;
+ segT seg_left, seg_right;
+ fragS *frag_left, *frag_right;
+
+ switch (op)
+ {
+ default:
+ return 0;
+
+ case O_constant:
+ case O_register:
+ left = 0;
+ break;
+
+ case O_symbol:
+ case O_symbol_rva:
+ if (!snapshot_symbol (add_symbol, &left, &seg_left, &frag_left))
+ return 0;
+
+ break;
+
+ case O_uminus:
+ case O_bit_not:
+ case O_logical_not:
+ if (!snapshot_symbol (add_symbol, &left, &seg_left, &frag_left))
+ return 0;
+
+ if (seg_left != absolute_section)
+ return 0;
+
+ if (op == O_logical_not)
+ left = !left;
+ else if (op == O_uminus)
+ left = -left;
+ else
+ left = ~left;
+ op = O_constant;
+ break;
+
+ case O_multiply:
+ case O_divide:
+ case O_modulus:
+ case O_left_shift:
+ case O_right_shift:
+ case O_bit_inclusive_or:
+ case O_bit_or_not:
+ case O_bit_exclusive_or:
+ case O_bit_and:
+ case O_add:
+ case O_subtract:
+ case O_eq:
+ case O_ne:
+ case O_lt:
+ case O_le:
+ case O_ge:
+ case O_gt:
+ case O_logical_and:
+ case O_logical_or:
+ if (!snapshot_symbol (add_symbol, &left, &seg_left, &frag_left)
+ || !snapshot_symbol (op_symbol, &right, &seg_right, &frag_right))
+ return 0;
+
+ /* Simplify addition or subtraction of a constant by folding the
+ constant into X_add_number. */
+ if (op == O_add)
+ {
+ if (seg_right == absolute_section)
+ {
+ final_val += right;
+ op = O_symbol;
+ break;
+ }
+ else if (seg_left == absolute_section)
+ {
+ final_val += left;
+ left = right;
+ seg_left = seg_right;
+ expressionP->X_add_symbol = expressionP->X_op_symbol;
+ op = O_symbol;
+ break;
+ }
+ }
+ else if (op == O_subtract)
+ {
+ if (seg_right == absolute_section)
+ {
+ final_val -= right;
+ op = O_symbol;
+ break;
+ }
+ }
+
+ /* Equality and non-equality tests are permitted on anything.
+ Subtraction, and other comparison operators are permitted if
+ both operands are in the same section. Otherwise, both
+ operands must be absolute. We already handled the case of
+ addition or subtraction of a constant above. */
+ if (!(seg_left == absolute_section
+ && seg_right == absolute_section)
+ && !(op == O_eq || op == O_ne)
+ && !((op == O_subtract
+ || op == O_lt || op == O_le || op == O_ge || op == O_gt)
+ && seg_left == seg_right
+ && (finalize_syms || frag_left == frag_right)
+ && ((seg_left != undefined_section
+ && seg_left != reg_section)
+ || add_symbol == op_symbol)))
+ return 0;
+
+ switch (op)
+ {
+ case O_add: left += right; break;
+ case O_subtract: left -= right; break;
+ case O_multiply: left *= right; break;
+ case O_divide:
+ if (right == 0)
+ return 0;
+ left = (offsetT) left / (offsetT) right;
+ break;
+ case O_modulus:
+ if (right == 0)
+ return 0;
+ left = (offsetT) left % (offsetT) right;
+ break;
+ case O_left_shift: left <<= right; break;
+ case O_right_shift: left >>= right; break;
+ case O_bit_inclusive_or: left |= right; break;
+ case O_bit_or_not: left |= ~right; break;
+ case O_bit_exclusive_or: left ^= right; break;
+ case O_bit_and: left &= right; break;
+ case O_eq:
+ case O_ne:
+ left = (left == right
+ && seg_left == seg_right
+ && (finalize_syms || frag_left == frag_right)
+ && ((seg_left != undefined_section
+ && seg_left != reg_section)
+ || add_symbol == op_symbol)
+ ? ~ (valueT) 0 : 0);
+ if (op == O_ne)
+ left = ~left;
+ break;
+ case O_lt:
+ left = (offsetT) left < (offsetT) right ? ~ (valueT) 0 : 0;
+ break;
+ case O_le:
+ left = (offsetT) left <= (offsetT) right ? ~ (valueT) 0 : 0;
+ break;
+ case O_ge:
+ left = (offsetT) left >= (offsetT) right ? ~ (valueT) 0 : 0;
+ break;
+ case O_gt:
+ left = (offsetT) left > (offsetT) right ? ~ (valueT) 0 : 0;
+ break;
+ case O_logical_and: left = left && right; break;
+ case O_logical_or: left = left || right; break;
+ default: abort ();
+ }
+
+ op = O_constant;
+ break;
+ }
+
+ if (op == O_symbol)
+ {
+ if (seg_left == absolute_section)
+ op = O_constant;
+ else if (seg_left == reg_section && final_val == 0)
+ op = O_register;
+ }
+ expressionP->X_op = op;
+
+ if (op == O_constant || op == O_register)
+ final_val += left;
+ expressionP->X_add_number = final_val;
+
+ return 1;
+}
/* This lives here because it belongs equally in expr.c & read.c.
expr.c is just a branch office read.c anyway, and putting it
@@ -1905,6 +2105,6 @@ unsigned int
get_single_number (void)
{
expressionS exp;
- operand (&exp);
+ operand (&exp, expr_normal);
return exp.X_add_number;
}