X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=gas%2Fexpr.c;h=4f4d380ff915b0a3f52fa266366dfbcbad0c5bef;hb=5a918ce730bca784591cef4b3696269510c5f814;hp=f79e31cbe3aff6c8360a9483e899ebdd1b7c6675;hpb=ce34c3732aa131c4796f9bfdd397e6a7bd5aa50b;p=binutils-gdb.git diff --git a/gas/expr.c b/gas/expr.c index f79e31cbe3a..4f4d380ff91 100644 --- a/gas/expr.c +++ b/gas/expr.c @@ -1,13 +1,13 @@ /* expr.c -operands, expressions- Copyright 1987, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, - 1999, 2000, 2001 + 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. This file is part of GAS, the GNU Assembler. GAS is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) + the Free Software Foundation; either version 3, or (at your option) any later version. GAS is distributed in the hope that it will be useful, @@ -17,32 +17,31 @@ You should have received a copy of the GNU General Public License along with GAS; see the file COPYING. If not, write to the Free - Software Foundation, 59 Temple Place - Suite 330, Boston, MA - 02111-1307, USA. */ + Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ /* This is really a branch office of as-read.c. I split it out to clearly distinguish the world of expressions from the world of statements. (It also gives smaller files to re-compile.) Here, "operand"s are of expressions, not instructions. */ -#include -#include #define min(a, b) ((a) < (b) ? (a) : (b)) #include "as.h" +#include "safe-ctype.h" #include "obstack.h" -static void floating_constant PARAMS ((expressionS * expressionP)); -static valueT generic_bignum_to_int32 PARAMS ((void)); +static void floating_constant (expressionS * expressionP); +static valueT generic_bignum_to_int32 (void); #ifdef BFD64 -static valueT generic_bignum_to_int64 PARAMS ((void)); +static valueT generic_bignum_to_int64 (void); #endif -static void integer_constant PARAMS ((int radix, expressionS * expressionP)); -static void mri_char_constant PARAMS ((expressionS *)); -static void current_location PARAMS ((expressionS *)); -static void clean_up_expression PARAMS ((expressionS * expressionP)); -static segT operand PARAMS ((expressionS *)); -static operatorT operator PARAMS ((int *)); +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 *, enum expr_mode); +static operatorT operator (int *); extern const char EXP_CHARS[], FLT_CHARS[]; @@ -63,11 +62,9 @@ static struct expr_symbol_line *expr_symbol_lines; into the fake section expr_section. */ symbolS * -make_expr_symbol (expressionP) - expressionS *expressionP; +make_expr_symbol (expressionS *expressionP) { expressionS zero; - const char *fake; symbolS *symbolP; struct expr_symbol_line *n; @@ -78,12 +75,12 @@ make_expr_symbol (expressionP) if (expressionP->X_op == O_big) { /* This won't work, because the actual value is stored in - generic_floating_point_number or generic_bignum, and we are - going to lose it if we haven't already. */ + generic_floating_point_number or generic_bignum, and we are + going to lose it if we haven't already. */ if (expressionP->X_add_number > 0) - as_bad (_("bignum invalid; zero assumed")); + as_bad (_("bignum invalid")); else - as_bad (_("floating point number invalid; zero assumed")); + as_bad (_("floating point number invalid")); zero.X_op = O_constant; zero.X_add_number = 0; zero.X_unsigned = 0; @@ -91,16 +88,16 @@ make_expr_symbol (expressionP) expressionP = &zero; } - fake = FAKE_LABEL_NAME; - /* Putting constant symbols in absolute_section rather than expr_section is convenient for the old a.out code, for which S_GET_SEGMENT does not always retrieve the value put in by S_SET_SEGMENT. */ - symbolP = symbol_create (fake, + symbolP = symbol_create (FAKE_LABEL_NAME, (expressionP->X_op == O_constant ? absolute_section - : expr_section), + : expressionP->X_op == O_register + ? reg_section + : expr_section), 0, &zero_address_frag); symbol_set_value_expression (symbolP, expressionP); @@ -121,10 +118,7 @@ make_expr_symbol (expressionP) the symbol. */ int -expr_symbol_where (sym, pfile, pline) - symbolS *sym; - char **pfile; - unsigned int *pline; +expr_symbol_where (symbolS *sym, char **pfile, unsigned int *pline) { register struct expr_symbol_line *l; @@ -154,8 +148,7 @@ expr_symbol_where (sym, pfile, pline) but that seems more clumsy. */ symbolS * -expr_build_uconstant (value) - offsetT value; +expr_build_uconstant (offsetT value) { expressionS e; @@ -165,42 +158,10 @@ expr_build_uconstant (value) return make_expr_symbol (&e); } -/* Build an expression for OP s1. */ - -symbolS * -expr_build_unary (op, s1) - operatorT op; - symbolS *s1; -{ - expressionS e; - - e.X_op = op; - e.X_add_symbol = s1; - e.X_add_number = 0; - return make_expr_symbol (&e); -} - -/* Build an expression for s1 OP s2. */ - -symbolS * -expr_build_binary (op, s1, s2) - operatorT op; - symbolS *s1; - symbolS *s2; -{ - expressionS e; - - e.X_op = op; - e.X_add_symbol = s1; - e.X_op_symbol = s2; - e.X_add_number = 0; - return make_expr_symbol (&e); -} - /* Build an expression for the current location ('.'). */ symbolS * -expr_build_dot () +expr_build_dot (void) { expressionS e; @@ -226,12 +187,9 @@ FLONUM_TYPE generic_floating_point_number = { 0 /* sign. */ }; -/* If nonzero, we've been asked to assemble nan, +inf or -inf. */ -int generic_floating_point_magic; static void -floating_constant (expressionP) - expressionS *expressionP; +floating_constant (expressionS *expressionP) { /* input_line_pointer -> floating-point constant. */ int error_code; @@ -243,11 +201,12 @@ floating_constant (expressionP) { if (error_code == ERROR_EXPONENT_OVERFLOW) { - as_bad (_("bad floating-point constant: exponent overflow, probably assembling junk")); + as_bad (_("bad floating-point constant: exponent overflow")); } else { - as_bad (_("bad floating-point constant: unknown error code=%d."), error_code); + as_bad (_("bad floating-point constant: unknown error code=%d"), + error_code); } } expressionP->X_op = O_big; @@ -257,7 +216,7 @@ floating_constant (expressionP) } static valueT -generic_bignum_to_int32 () +generic_bignum_to_int32 (void) { valueT number = ((generic_bignum[1] & LITTLENUM_MASK) << LITTLENUM_NUMBER_OF_BITS) @@ -268,7 +227,7 @@ generic_bignum_to_int32 () #ifdef BFD64 static valueT -generic_bignum_to_int64 () +generic_bignum_to_int64 (void) { valueT number = ((((((((valueT) generic_bignum[3] & LITTLENUM_MASK) @@ -283,9 +242,7 @@ generic_bignum_to_int64 () #endif static void -integer_constant (radix, expressionP) - int radix; - expressionS *expressionP; +integer_constant (int radix, expressionS *expressionP) { char *start; /* Start of number. */ char *suffix = NULL; @@ -328,11 +285,9 @@ integer_constant (radix, expressionP) int flt = 0; /* In MRI mode, the number may have a suffix indicating the - radix. For that matter, it might actually be a floating - point constant. */ - for (suffix = input_line_pointer; - isalnum ((unsigned char) *suffix); - suffix++) + radix. For that matter, it might actually be a floating + point constant. */ + for (suffix = input_line_pointer; ISALNUM (*suffix); suffix++) { if (*suffix == 'e' || *suffix == 'E') flt = 1; @@ -346,9 +301,11 @@ integer_constant (radix, expressionP) else { c = *--suffix; - if (islower ((unsigned char) c)) - c = toupper (c); - if (c == 'B') + c = TOUPPER (c); + /* If we have both NUMBERS_WITH_SUFFIX and LOCAL_LABELS_FB, + we distinguish between 'B' and 'b'. This is the case for + Z80. */ + if ((NUMBERS_WITH_SUFFIX && LOCAL_LABELS_FB ? *suffix : c) == 'B') radix = 2; else if (c == 'D') radix = 10; @@ -403,7 +360,7 @@ integer_constant (radix, expressionP) if (radix == 16 && c == '_') { /* This is literal of the form 0x333_0_12345678_1. - This example is equivalent to 0x00000333000000001234567800000001. */ + This example is equivalent to 0x00000333000000001234567800000001. */ int num_little_digits = 0; int i; @@ -427,7 +384,7 @@ integer_constant (radix, expressionP) /* Check for 8 digit per word max. */ if (ndigit > 8) - as_bad (_("A bignum with underscores may not have more than 8 hex digits in any word.")); + as_bad (_("a bignum with underscores may not have more than 8 hex digits in any word")); /* Add this chunk to the bignum. Shift things down 2 little digits. */ @@ -450,7 +407,7 @@ integer_constant (radix, expressionP) assert (num_little_digits >= 4); if (num_little_digits != 8) - as_bad (_("A bignum with underscores must have exactly 4 words.")); + as_bad (_("a bignum with underscores must have exactly 4 words")); /* We might have some leading zeros. These can be trimmed to give us a change to fit this constant into a small number. */ @@ -573,7 +530,7 @@ integer_constant (radix, expressionP) /* Either not seen or not defined. */ /* @@ Should print out the original string instead of the parsed number. */ - as_bad (_("backw. ref to unknown label \"%d:\", 0 assumed."), + as_bad (_("backward ref to unknown label \"%d:\""), (int) number); expressionP->X_op = O_constant; } @@ -627,10 +584,6 @@ integer_constant (radix, expressionP) else { expressionP->X_op = O_constant; -#ifdef TARGET_WORD_SIZE - /* Sign extend NUMBER. */ - number |= (-(number >> (TARGET_WORD_SIZE - 1))) << (TARGET_WORD_SIZE - 1); -#endif expressionP->X_add_number = number; input_line_pointer--; /* Restore following character. */ } /* Really just a number. */ @@ -647,8 +600,7 @@ integer_constant (radix, expressionP) /* Parse an MRI multi character constant. */ static void -mri_char_constant (expressionP) - expressionS *expressionP; +mri_char_constant (expressionS *expressionP) { int i; @@ -683,8 +635,8 @@ mri_char_constant (expressionP) if (i < SIZE_OF_LARGE_NUMBER - 1) { /* If there is more than one littlenum, left justify the - last one to make it match the earlier ones. If there is - only one, we can just use the value directly. */ + last one to make it match the earlier ones. If there is + only one, we can just use the value directly. */ for (; j < CHARS_PER_LITTLENUM; j++) generic_bignum[i] <<= 8; } @@ -696,7 +648,7 @@ mri_char_constant (expressionP) if (i < 0) { - as_bad (_("Character constant too large")); + as_bad (_("character constant too large")); i = 0; } @@ -737,8 +689,7 @@ mri_char_constant (expressionP) handles the magic symbol `.'. */ static void -current_location (expressionp) - expressionS *expressionp; +current_location (expressionS *expressionp) { if (now_seg == absolute_section) { @@ -747,13 +698,8 @@ current_location (expressionp) } else { - symbolS *symbolp; - - symbolp = symbol_new (FAKE_LABEL_NAME, now_seg, - (valueT) frag_now_fix (), - frag_now); expressionp->X_op = O_symbol; - expressionp->X_add_symbol = symbolp; + expressionp->X_add_symbol = symbol_temp_new_now (); expressionp->X_add_number = 0; } } @@ -761,13 +707,12 @@ current_location (expressionp) /* In: Input_line_pointer points to 1st char of operand, which may be a space. - Out: A expressionS. + Out: An expressionS. The operand may have been empty: in this case X_op == O_absent. Input_line_pointer->(next non-blank) char after operand. */ static segT -operand (expressionP) - expressionS *expressionP; +operand (expressionS *expressionP, enum expr_mode mode) { char c; symbolS *symbolP; /* Points to symbol. */ @@ -805,11 +750,14 @@ operand (expressionP) integer_constant ((NUMBERS_WITH_SUFFIX || flag_m68k_mri) ? 0 : 10, - expressionP); + expressionP); break; #ifdef LITERAL_PREFIXDOLLAR_HEX case '$': + /* $L is the start of a local label, not a hex constant. */ + if (* input_line_pointer == 'L') + goto isname; integer_constant (16, expressionP); break; #endif @@ -827,10 +775,10 @@ operand (expressionP) { char *s; - /* Check for a hex constant. */ + /* Check for a hex or float constant. */ for (s = input_line_pointer; hex_p (*s); s++) ; - if (*s == 'h' || *s == 'H') + if (*s == 'h' || *s == 'H' || *input_line_pointer == '.') { --input_line_pointer; integer_constant (0, expressionP); @@ -858,8 +806,7 @@ operand (expressionP) { input_line_pointer++; floating_constant (expressionP); - expressionP->X_add_number = - - (isupper ((unsigned char) c) ? tolower (c) : c); + expressionP->X_add_number = - TOLOWER (c); } else { @@ -981,8 +928,7 @@ operand (expressionP) case 'G': input_line_pointer++; floating_constant (expressionP); - expressionP->X_add_number = - - (isupper ((unsigned char) c) ? tolower (c) : c); + expressionP->X_add_number = - TOLOWER (c); break; case '$': @@ -1002,16 +948,14 @@ operand (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 != ']')) - { -#ifdef RELAX_PAREN_GROUPING - if (c != '(') -#endif - as_bad (_("Missing '%c' assumed"), c == '(' ? ')' : ']'); - } + as_bad (_("missing '%c'"), c == '(' ? ')' : ']'); else input_line_pointer++; SKIP_WHITESPACE (); @@ -1045,10 +989,6 @@ operand (expressionP) mri_char_constant (expressionP); break; - case '+': - (void) operand (expressionP); - break; - #ifdef TC_M68K case '"': /* Double quote is the bitwise not operator in MRI mode. */ @@ -1062,8 +1002,9 @@ operand (expressionP) goto isname; case '!': case '-': + case '+': { - operand (expressionP); + operand (expressionP, mode); if (expressionP->X_op == O_constant) { /* input_line_pointer -> char after operand. */ @@ -1077,20 +1018,64 @@ operand (expressionP) } else if (c == '~' || c == '"') expressionP->X_add_number = ~ expressionP->X_add_number; - else + else if (c == '!') expressionP->X_add_number = ! expressionP->X_add_number; } + else if (expressionP->X_op == O_big + && expressionP->X_add_number <= 0 + && c == '-' + && (generic_floating_point_number.sign == '+' + || generic_floating_point_number.sign == 'P')) + { + /* Negative flonum (eg, -1.000e0). */ + if (generic_floating_point_number.sign == '+') + generic_floating_point_number.sign = '-'; + else + generic_floating_point_number.sign = 'N'; + } + else if (expressionP->X_op == O_big + && expressionP->X_add_number > 0) + { + int i; + + if (c == '~' || c == '-') + { + for (i = 0; i < expressionP->X_add_number; ++i) + generic_bignum[i] = ~generic_bignum[i]; + if (c == '-') + for (i = 0; i < expressionP->X_add_number; ++i) + { + generic_bignum[i] += 1; + if (generic_bignum[i]) + break; + } + } + else if (c == '!') + { + int nonzero = 0; + for (i = 0; i < expressionP->X_add_number; ++i) + { + if (generic_bignum[i]) + nonzero = 1; + generic_bignum[i] = 0; + } + generic_bignum[0] = nonzero; + } + } else if (expressionP->X_op != O_illegal && expressionP->X_op != O_absent) { - expressionP->X_add_symbol = make_expr_symbol (expressionP); - if (c == '-') - expressionP->X_op = O_uminus; - else if (c == '~' || c == '"') - expressionP->X_op = O_bit_not; - else - expressionP->X_op = O_logical_not; - expressionP->X_add_number = 0; + if (c != '+') + { + expressionP->X_add_symbol = make_expr_symbol (expressionP); + if (c == '-') + expressionP->X_op = O_uminus; + else if (c == '~' || c == '"') + expressionP->X_op = O_bit_not; + else + expressionP->X_op = O_logical_not; + expressionP->X_add_number = 0; + } } else as_warn (_("Unary operator %c ignored because bad operand follows"), @@ -1101,15 +1086,15 @@ operand (expressionP) #if defined (DOLLAR_DOT) || defined (TC_M68K) case '$': /* '$' is the program counter when in MRI mode, or when - DOLLAR_DOT is defined. */ + DOLLAR_DOT is defined. */ #ifndef DOLLAR_DOT if (! flag_m68k_mri) goto de_fault; #endif - if (flag_m68k_mri && hex_p (*input_line_pointer)) + if (DOLLAR_AMBIGU && hex_p (*input_line_pointer)) { - /* In MRI mode, '$' is also used as the prefix for a - hexadecimal constant. */ + /* In MRI mode and on Z80, '$' is also used as the prefix + for a hexadecimal constant. */ integer_constant (16, expressionP); break; } @@ -1200,7 +1185,7 @@ operand (expressionP) goto de_fault; /* In MRI mode, this is a floating point constant represented - using hexadecimal digits. */ + using hexadecimal digits. */ ++input_line_pointer; integer_constant (16, expressionP); @@ -1228,10 +1213,10 @@ operand (expressionP) #ifdef md_parse_name /* This is a hook for the backend to parse certain names - 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)) + 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, mode, &c)) { *input_line_pointer = c; break; @@ -1282,12 +1267,12 @@ operand (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); @@ -1312,7 +1297,7 @@ operand (expressionP) if (expressionP->X_op == O_absent) { ++input_line_pointer; - as_bad (_("Bad expression")); + as_bad (_("bad expression")); expressionP->X_op = O_constant; expressionP->X_add_number = 0; } @@ -1330,6 +1315,9 @@ operand (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: @@ -1343,18 +1331,15 @@ operand (expressionP) /* Internal. Simplify a struct expression for use by expr (). */ -/* In: address of a expressionS. +/* In: address of an expressionS. The X_op field of the expressionS may only take certain values. Elsewise we waste time special-case testing. Sigh. Ditto SEG_ABSENT. Out: expressionS may have been modified: - 'foo-foo' symbol references cancelled to 0, which changes X_op - from O_subtract to O_constant. Unused fields zeroed to help expr (). */ static void -clean_up_expression (expressionP) - expressionS *expressionP; +clean_up_expression (expressionS *expressionP) { switch (expressionP->X_op) { @@ -1372,23 +1357,6 @@ clean_up_expression (expressionP) case O_bit_not: expressionP->X_op_symbol = NULL; break; - case O_subtract: - if (expressionP->X_op_symbol == expressionP->X_add_symbol - || ((symbol_get_frag (expressionP->X_op_symbol) - == symbol_get_frag (expressionP->X_add_symbol)) - && SEG_NORMAL (S_GET_SEGMENT (expressionP->X_add_symbol)) - && (S_GET_VALUE (expressionP->X_op_symbol) - == S_GET_VALUE (expressionP->X_add_symbol)))) - { - addressT diff = (S_GET_VALUE (expressionP->X_add_symbol) - - S_GET_VALUE (expressionP->X_op_symbol)); - - expressionP->X_op = O_constant; - expressionP->X_add_symbol = NULL; - expressionP->X_op_symbol = NULL; - expressionP->X_add_number += diff; - } - break; default: break; } @@ -1400,7 +1368,7 @@ clean_up_expression (expressionP) Unary operators and parenthetical expressions are treated as operands. As usual, Q==quantity==operand, O==operator, X==expression mnemonics. - We used to do a aho/ullman shift-reduce parser, but the logic got so + We used to do an aho/ullman shift-reduce parser, but the logic got so warped that I flushed it and wrote a recursive-descent parser instead. Now things are stable, would anybody like to write a fast parser? Most expressions are either register (which does not even reach here) @@ -1417,6 +1385,9 @@ clean_up_expression (expressionP) #undef __ #define __ O_illegal +#ifndef O_SINGLE_EQ +#define O_SINGLE_EQ O_illegal +#endif /* Maps ASCII -> operators. */ static const operatorT op_encoding[256] = { @@ -1426,7 +1397,7 @@ static const operatorT op_encoding[256] = { __, O_bit_or_not, __, __, __, O_modulus, O_bit_and, __, __, __, O_multiply, O_add, __, O_subtract, __, O_divide, __, __, __, __, __, __, __, __, - __, __, __, __, O_lt, __, O_gt, __, + __, __, __, __, O_lt, O_SINGLE_EQ, O_gt, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, @@ -1456,7 +1427,7 @@ static const operatorT op_encoding[256] = { 0 operand, (expression) 1 || 2 && - 3 = <> < <= >= > + 3 == <> < <= >= > 4 + - 5 used for * / % in MRI mode 6 & ^ ! | @@ -1522,7 +1493,7 @@ static operator_rankT op_rank[] = { #define MRI_MUL_PRECEDENCE 6 void -expr_set_precedence () +expr_set_precedence (void) { if (flag_m68k_mri) { @@ -1541,7 +1512,7 @@ expr_set_precedence () /* Initialize the expression parser. */ void -expr_begin () +expr_begin (void) { expr_set_precedence (); @@ -1558,8 +1529,7 @@ expr_begin () Does not advance INPUT_LINE_POINTER. */ static inline operatorT -operator (num_chars) - int *num_chars; +operator (int *num_chars) { int c; operatorT ret; @@ -1575,6 +1545,10 @@ operator (num_chars) default: return op_encoding[c]; + case '+': + case '-': + return op_encoding[c]; + case '<': switch (input_line_pointer[1]) { @@ -1616,15 +1590,21 @@ operator (num_chars) return ret; case '!': - /* We accept !! as equivalent to ^ for MRI compatibility. */ - if (input_line_pointer[1] != '!') + switch (input_line_pointer[1]) { + case '!': + /* We accept !! as equivalent to ^ for MRI compatibility. */ + *num_chars = 2; + return O_bit_exclusive_or; + case '=': + /* We accept != as equivalent to <>. */ + *num_chars = 2; + return O_ne; + default: if (flag_m68k_mri) return O_bit_inclusive_or; return op_encoding[c]; } - *num_chars = 2; - return O_bit_exclusive_or; case '|': if (input_line_pointer[1] != '|') @@ -1647,9 +1627,9 @@ operator (num_chars) /* Parse an expression. */ segT -expr (rankarg, resultP) - int rankarg; /* Larger # is higher rank. */ - expressionS *resultP; /* Deliver result here. */ +expr (int rankarg, /* Larger # is higher rank. */ + expressionS *resultP, /* Deliver result here. */ + enum expr_mode mode /* Controls behavior. */) { operator_rankT rank = (operator_rankT) rankarg; segT retval; @@ -1658,9 +1638,13 @@ expr (rankarg, resultP) operatorT op_right; int op_chars; - know (rank >= 0); + know (rankarg >= 0); + + /* Save the value of dot for the fixup code. */ + if (rank == 0) + dot_value = frag_now_fix (); - retval = operand (resultP); + retval = operand (resultP, mode); /* operand () gobbles spaces. */ know (*input_line_pointer != ' '); @@ -1669,10 +1653,11 @@ expr (rankarg, resultP) while (op_left != O_illegal && op_rank[(int) op_left] > rank) { segT rightseg; + bfd_vma frag_off; 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")); @@ -1695,27 +1680,12 @@ expr (rankarg, resultP) } } - if (retval == undefined_section) - { - if (SEG_NORMAL (rightseg)) - retval = rightseg; - } - else if (! SEG_NORMAL (retval)) - retval = rightseg; - else if (SEG_NORMAL (rightseg) - && retval != rightseg -#ifdef DIFF_EXPR_OK - && op_left != O_subtract -#endif - ) - as_bad (_("operation combines symbols in different segments")); - op_right = operator (&op_chars); know (op_right == O_illegal || op_rank[(int) op_right] <= op_rank[(int) op_left]); know ((int) op_left >= (int) O_multiply - && (int) op_left <= (int) O_logical_or); + && (int) op_left <= (int) O_index); /* input_line_pointer->after right-hand quantity. */ /* left-hand quantity in resultP. */ @@ -1754,7 +1724,11 @@ expr (rankarg, resultP) } else #endif - if (op_left == O_add && right.X_op == O_constant) +#ifndef md_register_arithmetic +# define md_register_arithmetic 1 +#endif + if (op_left == O_add && right.X_op == O_constant + && (md_register_arithmetic || resultP->X_op != O_register)) { /* X + constant. */ resultP->X_add_number += right.X_add_number; @@ -1763,23 +1737,28 @@ expr (rankarg, resultP) else if (op_left == O_subtract && right.X_op == O_symbol && resultP->X_op == O_symbol - && (symbol_get_frag (right.X_add_symbol) - == symbol_get_frag (resultP->X_add_symbol)) - && SEG_NORMAL (S_GET_SEGMENT (right.X_add_symbol))) - + && retval == rightseg + && (SEG_NORMAL (rightseg) + || right.X_add_symbol == resultP->X_add_symbol) + && frag_offset_fixed_p (symbol_get_frag (resultP->X_add_symbol), + symbol_get_frag (right.X_add_symbol), + &frag_off)) { resultP->X_add_number -= right.X_add_number; + resultP->X_add_number -= frag_off / OCTETS_PER_BYTE; resultP->X_add_number += (S_GET_VALUE (resultP->X_add_symbol) - S_GET_VALUE (right.X_add_symbol)); resultP->X_op = O_constant; resultP->X_add_symbol = 0; } - else if (op_left == O_subtract && right.X_op == O_constant) + else if (op_left == O_subtract && right.X_op == O_constant + && (md_register_arithmetic || resultP->X_op != O_register)) { /* X - constant. */ resultP->X_add_number -= right.X_add_number; } - else if (op_left == O_add && resultP->X_op == O_constant) + else if (op_left == O_add && resultP->X_op == O_constant + && (md_register_arithmetic || right.X_op != O_register)) { /* Constant + X. */ resultP->X_op = right.X_op; @@ -1806,7 +1785,7 @@ expr (rankarg, resultP) case O_left_shift: resultP->X_add_number <<= v; break; case O_right_shift: /* We always use unsigned shifts, to avoid relying on - characteristics of the compiler used to compile gas. */ + characteristics of the compiler used to compile gas. */ resultP->X_add_number = (offsetT) ((valueT) resultP->X_add_number >> (valueT) v); break; @@ -1814,7 +1793,9 @@ expr (rankarg, resultP) case O_bit_or_not: resultP->X_add_number |= ~v; break; case O_bit_exclusive_or: resultP->X_add_number ^= v; break; case O_bit_and: resultP->X_add_number &= v; break; - case O_add: resultP->X_add_number += v; break; + /* Constant + constant (O_add) is handled by the + previous if statement for constant + X, so is omitted + here. */ case O_subtract: resultP->X_add_number -= v; break; case O_eq: resultP->X_add_number = @@ -1861,7 +1842,14 @@ expr (rankarg, resultP) if (op_left == O_add) resultP->X_add_number += right.X_add_number; else if (op_left == O_subtract) - resultP->X_add_number -= right.X_add_number; + { + resultP->X_add_number -= right.X_add_number; + if (retval == rightseg && SEG_NORMAL (retval)) + { + retval = absolute_section; + rightseg = absolute_section; + } + } } else { @@ -1873,6 +1861,21 @@ expr (rankarg, resultP) resultP->X_unsigned = 1; } + if (retval != rightseg) + { + if (! SEG_NORMAL (retval)) + { + if (retval != undefined_section || SEG_NORMAL (rightseg)) + retval = rightseg; + } + else if (SEG_NORMAL (rightseg) +#ifdef DIFF_EXPR_OK + && op_left != O_subtract +#endif + ) + as_bad (_("operation combines symbols in different segments")); + } + op_left = op_right; } /* While next operator is >= this rank. */ @@ -1880,8 +1883,269 @@ expr (rankarg, resultP) 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; + bfd_vma frag_off; + + 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; + add_symbol = 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. + Shifts by constant zero are permitted on anything. + Multiplies, bit-ors, and bit-ands with constant zero are + permitted on anything. + Multiplies and divides by constant one are permitted on + anything. + Binary operations with both operands being the same register + or undefined symbol are permitted if the result doesn't depend + on the input value. + Otherwise, both operands must be absolute. We already handled + the case of addition or subtraction of a constant above. */ + frag_off = 0; + 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_offset_fixed_p (frag_left, frag_right, &frag_off)) + && (seg_left != reg_section || left == right) + && (seg_left != undefined_section || add_symbol == op_symbol))) + { + if ((seg_left == absolute_section && left == 0) + || (seg_right == absolute_section && right == 0)) + { + if (op == O_bit_exclusive_or || op == O_bit_inclusive_or) + { + if (seg_right != absolute_section || right != 0) + { + seg_left = seg_right; + left = right; + add_symbol = op_symbol; + } + op = O_symbol; + break; + } + else if (op == O_left_shift || op == O_right_shift) + { + if (seg_left != absolute_section || left != 0) + { + op = O_symbol; + break; + } + } + else if (op != O_multiply + && op != O_bit_or_not && op != O_bit_and) + return 0; + } + else if (op == O_multiply + && seg_left == absolute_section && left == 1) + { + seg_left = seg_right; + left = right; + add_symbol = op_symbol; + op = O_symbol; + break; + } + else if ((op == O_multiply || op == O_divide) + && seg_right == absolute_section && right == 1) + { + op = O_symbol; + break; + } + else if (left != right + || ((seg_left != reg_section || seg_right != reg_section) + && (seg_left != undefined_section + || seg_right != undefined_section + || add_symbol != op_symbol))) + return 0; + else if (op == O_bit_and || op == O_bit_inclusive_or) + { + op = O_symbol; + break; + } + else if (op != O_bit_exclusive_or && op != O_bit_or_not) + return 0; + } + + right += frag_off / OCTETS_PER_BYTE; + 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 + || 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; + else if (add_symbol != expressionP->X_add_symbol) + final_val += left; + expressionP->X_add_symbol = add_symbol; + } + 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 @@ -1896,7 +2160,7 @@ expr (rankarg, resultP) lines end in end-of-line. */ char -get_symbol_end () +get_symbol_end (void) { char c; @@ -1915,9 +2179,9 @@ get_symbol_end () } unsigned int -get_single_number () +get_single_number (void) { expressionS exp; - operand (&exp); + operand (&exp, expr_normal); return exp.X_add_number; }