X-Git-Url: http://drtracing.org/?a=blobdiff_plain;f=gas%2Fexpr.c;h=b1cdb38bab58add5550d1affbbbfcd363b298279;hb=73b090a922a5f43931f0ec10b1b1b9507c819ebf;hp=dabc1058b7bbf3a6686f5e13927f4e068a32389b;hpb=85825401dc523061be3cb0df3a281a2be31e3402;p=deliverable%2Fbinutils-gdb.git diff --git a/gas/expr.c b/gas/expr.c index dabc1058b7..b1cdb38bab 100644 --- a/gas/expr.c +++ b/gas/expr.c @@ -1,11 +1,11 @@ /* expr.c -operands, expressions- - Copyright (C) 1987, 1990, 1991, 1992 Free Software Foundation, Inc. + Copyright (C) 1987-2016 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, @@ -14,170 +14,455 @@ GNU General Public License for more details. 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, 675 Mass Ave, Cambridge, MA 02139, USA. */ + along with GAS; see the file COPYING. If not, write to the Free + 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. - */ +/* 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" +#ifdef HAVE_LIMITS_H +#include +#endif +#ifndef CHAR_BIT +#define CHAR_BIT 8 +#endif -#if __STDC__ == 1 +static void floating_constant (expressionS * expressionP); +static valueT generic_bignum_to_int32 (void); +#ifdef BFD64 +static valueT generic_bignum_to_int64 (void); +#endif +static void integer_constant (int radix, expressionS * expressionP); +static void mri_char_constant (expressionS *); static void clean_up_expression (expressionS * expressionP); -#else /* __STDC__ */ -static void clean_up_expression (); /* Internal. */ -#endif /* not __STDC__ */ -extern const char EXP_CHARS[]; /* JF hide MD floating pt stuff all the same place */ -extern const char FLT_CHARS[]; - -/* - * Build any floating-point literal here. - * Also build any bignum literal here. - */ - -/* LITTLENUM_TYPE generic_buffer [6]; *//* JF this is a hack */ +static segT operand (expressionS *, enum expr_mode); +static operatorT operatorf (int *); + +/* We keep a mapping of expression symbols to file positions, so that + we can provide better error messages. */ + +struct expr_symbol_line { + struct expr_symbol_line *next; + symbolS *sym; + const char *file; + unsigned int line; +}; + +static struct expr_symbol_line *expr_symbol_lines; + +/* Build a dummy symbol to hold a complex expression. This is how we + build expressions up out of other expressions. The symbol is put + into the fake section expr_section. */ + +symbolS * +make_expr_symbol (expressionS *expressionP) +{ + expressionS zero; + symbolS *symbolP; + struct expr_symbol_line *n; + + if (expressionP->X_op == O_symbol + && expressionP->X_add_number == 0) + return expressionP->X_add_symbol; + + 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. */ + if (expressionP->X_add_number > 0) + as_bad (_("bignum invalid")); + else + as_bad (_("floating point number invalid")); + zero.X_op = O_constant; + zero.X_add_number = 0; + zero.X_unsigned = 0; + zero.X_extrabit = 0; + clean_up_expression (&zero); + expressionP = &zero; + } + + /* 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_LABEL_NAME, + (expressionP->X_op == O_constant + ? absolute_section + : expressionP->X_op == O_register + ? reg_section + : expr_section), + 0, &zero_address_frag); + symbol_set_value_expression (symbolP, expressionP); + + if (expressionP->X_op == O_constant) + resolve_symbol_value (symbolP); + + n = XNEW (struct expr_symbol_line); + n->sym = symbolP; + n->file = as_where (&n->line); + n->next = expr_symbol_lines; + expr_symbol_lines = n; + + return symbolP; +} + +/* Return the file and line number for an expr symbol. Return + non-zero if something was found, 0 if no information is known for + the symbol. */ + +int +expr_symbol_where (symbolS *sym, const char **pfile, unsigned int *pline) +{ + struct expr_symbol_line *l; + + for (l = expr_symbol_lines; l != NULL; l = l->next) + { + if (l->sym == sym) + { + *pfile = l->file; + *pline = l->line; + return 1; + } + } + + return 0; +} + +/* Utilities for building expressions. + Since complex expressions are recorded as symbols for use in other + expressions these return a symbolS * and not an expressionS *. + These explicitly do not take an "add_number" argument. */ +/* ??? For completeness' sake one might want expr_build_symbol. + It would just return its argument. */ + +/* Build an expression for an unsigned constant. + The corresponding one for signed constants is missing because + there's currently no need for it. One could add an unsigned_p flag + but that seems more clumsy. */ + +symbolS * +expr_build_uconstant (offsetT value) +{ + expressionS e; + + e.X_op = O_constant; + e.X_add_number = value; + e.X_unsigned = 1; + e.X_extrabit = 0; + return make_expr_symbol (&e); +} + +/* Build an expression for the current location ('.'). */ + +symbolS * +expr_build_dot (void) +{ + expressionS e; + + current_location (&e); + return symbol_clone_if_forward_ref (make_expr_symbol (&e)); +} + +/* Build any floating-point literal here. + Also build any bignum literal here. */ + /* Seems atof_machine can backscan through generic_bignum and hit whatever happens to be loaded before it in memory. And its way too complicated for me to fix right. Thus a hack. JF: Just make generic_bignum bigger, and never write into the early words, thus they'll always be zero. - I hate Dean's floating-point code. Bleh. - */ + I hate Dean's floating-point code. Bleh. */ LITTLENUM_TYPE generic_bignum[SIZE_OF_LARGE_NUMBER + 6]; -FLONUM_TYPE generic_floating_point_number = -{ - &generic_bignum[6], /* low (JF: Was 0) */ - &generic_bignum[SIZE_OF_LARGE_NUMBER + 6 - 1], /* high JF: (added +6) */ - 0, /* leader */ - 0, /* exponent */ - 0 /* sign */ + +FLONUM_TYPE generic_floating_point_number = { + &generic_bignum[6], /* low. (JF: Was 0) */ + &generic_bignum[SIZE_OF_LARGE_NUMBER + 6 - 1], /* high. JF: (added +6) */ + 0, /* leader. */ + 0, /* exponent. */ + 0 /* sign. */ }; -/* If nonzero, we've been asked to assemble nan, +inf or -inf */ -int generic_floating_point_magic; + -floating_constant (expressionP) - expressionS *expressionP; +static void +floating_constant (expressionS *expressionP) { - /* input_line_pointer->*/ - /* floating-point constant. */ + /* input_line_pointer -> floating-point constant. */ int error_code; - error_code = atof_generic - (&input_line_pointer, ".", EXP_CHARS, - &generic_floating_point_number); + error_code = atof_generic (&input_line_pointer, ".", EXP_CHARS, + &generic_floating_point_number); if (error_code) { 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_seg = SEG_BIG; - /* input_line_pointer->just after constant, */ - /* which may point to whitespace. */ + expressionP->X_op = O_big; + /* input_line_pointer -> just after constant, which may point to + whitespace. */ expressionP->X_add_number = -1; } +static valueT +generic_bignum_to_int32 (void) +{ + valueT number = + ((generic_bignum[1] & LITTLENUM_MASK) << LITTLENUM_NUMBER_OF_BITS) + | (generic_bignum[0] & LITTLENUM_MASK); + number &= 0xffffffff; + return number; +} +#ifdef BFD64 +static valueT +generic_bignum_to_int64 (void) +{ + valueT number = + ((((((((valueT) generic_bignum[3] & LITTLENUM_MASK) + << LITTLENUM_NUMBER_OF_BITS) + | ((valueT) generic_bignum[2] & LITTLENUM_MASK)) + << LITTLENUM_NUMBER_OF_BITS) + | ((valueT) generic_bignum[1] & LITTLENUM_MASK)) + << LITTLENUM_NUMBER_OF_BITS) + | ((valueT) generic_bignum[0] & LITTLENUM_MASK)); + return number; +} +#endif -integer_constant (radix, expressionP) - int radix; - expressionS *expressionP; +static void +integer_constant (int radix, expressionS *expressionP) { - register char *digit_2; /*->2nd digit of number. */ + char *start; /* Start of number. */ + char *suffix = NULL; char c; + valueT number; /* Offset or (absolute) value. */ + short int digit; /* Value of next digit in current radix. */ + short int maxdig = 0; /* Highest permitted digit value. */ + int too_many_digits = 0; /* If we see >= this number of. */ + char *name; /* Points to name of symbol. */ + symbolS *symbolP; /* Points to symbol. */ + + int small; /* True if fits in 32 bits. */ + + /* May be bignum, or may fit in 32 bits. */ + /* Most numbers fit into 32 bits, and we want this case to be fast. + so we pretend it will fit into 32 bits. If, after making up a 32 + bit number, we realise that we have scanned more digits than + comfortably fit into 32 bits, we re-scan the digits coding them + into a bignum. For decimal and octal numbers we are + conservative: Some numbers may be assumed bignums when in fact + they do fit into 32 bits. Numbers of any radix can have excess + leading zeros: We strive to recognise this and cast them back + into 32 bits. We must check that the bignum really is more than + 32 bits, and change it back to a 32-bit number if it fits. The + number we are looking for is expected to be positive, but if it + fits into 32 bits as an unsigned number, we let it be a 32-bit + number. The cavalier approach is for speed in ordinary cases. */ + /* This has been extended for 64 bits. We blindly assume that if + you're compiling in 64-bit mode, the target is a 64-bit machine. + This should be cleaned up. */ + +#ifdef BFD64 +#define valuesize 64 +#else /* includes non-bfd case, mostly */ +#define valuesize 32 +#endif - register valueT number; /* offset or (absolute) value */ - register short int digit; /* value of next digit in current radix */ - register short int maxdig = 0;/* highest permitted digit value. */ - register int too_many_digits = 0; /* if we see >= this number of */ - register char *name; /* points to name of symbol */ - register symbolS *symbolP; /* points to symbol */ - - int small; /* true if fits in 32 bits. */ - extern char hex_value[]; /* in hex_value.c */ - - /* may be bignum, or may fit in 32 bits. */ - /* - * most numbers fit into 32 bits, and we want this case to be fast. - * so we pretend it will fit into 32 bits. if, after making up a 32 - * bit number, we realise that we have scanned more digits than - * comfortably fit into 32 bits, we re-scan the digits coding - * them into a bignum. for decimal and octal numbers we are conservative: some - * numbers may be assumed bignums when in fact they do fit into 32 bits. - * numbers of any radix can have excess leading zeros: we strive - * to recognise this and cast them back into 32 bits. - * we must check that the bignum really is more than 32 - * bits, and change it back to a 32-bit number if it fits. - * the number we are looking for is expected to be positive, but - * if it fits into 32 bits as an unsigned number, we let it be a 32-bit - * number. the cavalier approach is for speed in ordinary cases. - */ + if (is_end_of_line[(unsigned char) *input_line_pointer]) + { + expressionP->X_op = O_absent; + return; + } - switch (radix) + if ((NUMBERS_WITH_SUFFIX || flag_m68k_mri) && radix == 0) { + 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 (*suffix); suffix++) + { + if (*suffix == 'e' || *suffix == 'E') + flt = 1; + } + + if (suffix == input_line_pointer) + { + radix = 10; + suffix = NULL; + } + else + { + c = *--suffix; + 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; + else if (c == 'O' || c == 'Q') + radix = 8; + else if (c == 'H') + radix = 16; + else if (suffix[1] == '.' || c == 'E' || flt) + { + floating_constant (expressionP); + return; + } + else + { + radix = 10; + suffix = NULL; + } + } + } + switch (radix) + { case 2: maxdig = 2; - too_many_digits = 33; + too_many_digits = valuesize + 1; break; case 8: maxdig = radix = 8; - too_many_digits = 11; + too_many_digits = (valuesize + 2) / 3 + 1; break; case 16: - - maxdig = radix = 16; - too_many_digits = 9; + too_many_digits = (valuesize + 3) / 4 + 1; break; case 10: maxdig = radix = 10; - too_many_digits = 11; + too_many_digits = (valuesize + 11) / 4; /* Very rough. */ } - c = *input_line_pointer; - input_line_pointer++; - digit_2 = input_line_pointer; - for (number = 0; (digit = hex_value[c]) < maxdig; c = *input_line_pointer++) +#undef valuesize + start = input_line_pointer; + c = *input_line_pointer++; + for (number = 0; + (digit = hex_value (c)) < maxdig; + c = *input_line_pointer++) { number = number * radix + digit; } - /* c contains character after number. */ - /* input_line_pointer->char after c. */ - small = input_line_pointer - digit_2 < too_many_digits; - if (!small) + /* c contains character after number. */ + /* input_line_pointer->char after c. */ + small = (input_line_pointer - start - 1) < too_many_digits; + + if (radix == 16 && c == '_') + { + /* This is literal of the form 0x333_0_12345678_1. + This example is equivalent to 0x00000333000000001234567800000001. */ + + int num_little_digits = 0; + int i; + input_line_pointer = start; /* -> 1st digit. */ + + know (LITTLENUM_NUMBER_OF_BITS == 16); + + for (c = '_'; c == '_'; num_little_digits += 2) + { + + /* Convert one 64-bit word. */ + int ndigit = 0; + number = 0; + for (c = *input_line_pointer++; + (digit = hex_value (c)) < maxdig; + c = *(input_line_pointer++)) + { + number = number * radix + digit; + ndigit++; + } + + /* 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")); + + /* Add this chunk to the bignum. + Shift things down 2 little digits. */ + know (LITTLENUM_NUMBER_OF_BITS == 16); + for (i = min (num_little_digits + 1, SIZE_OF_LARGE_NUMBER - 1); + i >= 2; + i--) + generic_bignum[i] = generic_bignum[i - 2]; + + /* Add the new digits as the least significant new ones. */ + generic_bignum[0] = number & 0xffffffff; + generic_bignum[1] = number >> 16; + } + + /* Again, c is char after number, input_line_pointer->after c. */ + + if (num_little_digits > SIZE_OF_LARGE_NUMBER - 1) + num_little_digits = SIZE_OF_LARGE_NUMBER - 1; + + gas_assert (num_little_digits >= 4); + + if (num_little_digits != 8) + 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. */ + while (generic_bignum[num_little_digits - 1] == 0 + && num_little_digits > 1) + num_little_digits--; + + if (num_little_digits <= 2) + { + /* will fit into 32 bits. */ + number = generic_bignum_to_int32 (); + small = 1; + } +#ifdef BFD64 + else if (num_little_digits <= 4) + { + /* Will fit into 64 bits. */ + number = generic_bignum_to_int64 (); + small = 1; + } +#endif + else + { + small = 0; + + /* Number of littlenums in the bignum. */ + number = num_little_digits; + } + } + else if (!small) { - /* - * we saw a lot of digits. manufacture a bignum the hard way. - */ - LITTLENUM_TYPE *leader; /*->high order littlenum of the bignum. */ - LITTLENUM_TYPE *pointer; /*->littlenum we are frobbing now. */ + /* We saw a lot of digits. manufacture a bignum the hard way. */ + LITTLENUM_TYPE *leader; /* -> high order littlenum of the bignum. */ + LITTLENUM_TYPE *pointer; /* -> littlenum we are frobbing now. */ long carry; leader = generic_bignum; generic_bignum[0] = 0; generic_bignum[1] = 0; - /* we could just use digit_2, but lets be mnemonic. */ - input_line_pointer = --digit_2; /*->1st digit. */ + generic_bignum[2] = 0; + generic_bignum[3] = 0; + input_line_pointer = start; /* -> 1st digit. */ c = *input_line_pointer++; - for (; (carry = hex_value[c]) < maxdig; c = *input_line_pointer++) + for (; (carry = hex_value (c)) < maxdig; c = *input_line_pointer++) { - for (pointer = generic_bignum; - pointer <= leader; - pointer++) + for (pointer = generic_bignum; pointer <= leader; pointer++) { long work; @@ -188,202 +473,287 @@ integer_constant (radix, expressionP) if (carry) { if (leader < generic_bignum + SIZE_OF_LARGE_NUMBER - 1) - { /* room to grow a longer bignum. */ + { + /* Room to grow a longer bignum. */ *++leader = carry; } } } - /* again, c is char after number, */ - /* input_line_pointer->after c. */ - know (sizeof (int) * 8 == 32); + /* Again, c is char after number. */ + /* input_line_pointer -> after c. */ know (LITTLENUM_NUMBER_OF_BITS == 16); - /* hence the constant "2" in the next line. */ if (leader < generic_bignum + 2) - { /* will fit into 32 bits. */ - number = - ((generic_bignum[1] & LITTLENUM_MASK) << LITTLENUM_NUMBER_OF_BITS) - | (generic_bignum[0] & LITTLENUM_MASK); + { + /* Will fit into 32 bits. */ + number = generic_bignum_to_int32 (); small = 1; } +#ifdef BFD64 + else if (leader < generic_bignum + 4) + { + /* Will fit into 64 bits. */ + number = generic_bignum_to_int64 (); + small = 1; + } +#endif else { - number = leader - generic_bignum + 1; /* number of littlenums in the bignum. */ + /* Number of littlenums in the bignum. */ + number = leader - generic_bignum + 1; } } + + if ((NUMBERS_WITH_SUFFIX || flag_m68k_mri) + && suffix != NULL + && input_line_pointer - 1 == suffix) + c = *input_line_pointer++; + +#ifndef tc_allow_U_suffix +#define tc_allow_U_suffix 1 +#endif + /* PR 19910: Look for, and ignore, a U suffix to the number. */ + if (tc_allow_U_suffix && (c == 'U' || c == 'u')) + c = * input_line_pointer++; + if (small) { - /* - * here with number, in correct radix. c is the next char. - * note that unlike un*x, we allow "011f" "0x9f" to - * both mean the same as the (conventional) "9f". this is simply easier - * than checking for strict canonical form. syntax sux! - */ + /* Here with number, in correct radix. c is the next char. + Note that unlike un*x, we allow "011f" "0x9f" to both mean + the same as the (conventional) "9f". + This is simply easier than checking for strict canonical + form. Syntax sux! */ - switch (c) + if (LOCAL_LABELS_FB && c == 'b') { + /* Backward ref to local label. + Because it is backward, expect it to be defined. */ + /* Construct a local label. */ + name = fb_label_name ((int) number, 0); + + /* Seen before, or symbol is defined: OK. */ + symbolP = symbol_find (name); + if ((symbolP != NULL) && (S_IS_DEFINED (symbolP))) + { + /* Local labels are never absolute. Don't waste time + checking absoluteness. */ + know (SEG_NORMAL (S_GET_SEGMENT (symbolP))); -#ifdef LOCAL_LABELS_FB - case 'b': - { - /* - * backward ref to local label. - * because it is backward, expect it to be defined. - */ - /* Construct a local label. */ - name = fb_label_name ((int) number, 0); - - /* seen before, or symbol is defined: ok */ - symbolP = symbol_find (name); - if ((symbolP != NULL) && (S_IS_DEFINED (symbolP))) - { - - /* local labels are never absolute. don't waste time - checking absoluteness. */ - know (SEG_NORMAL (S_GET_SEGMENT (symbolP))); + expressionP->X_op = O_symbol; + expressionP->X_add_symbol = symbolP; + } + else + { + /* Either not seen or not defined. */ + /* @@ Should print out the original string instead of + the parsed number. */ + as_bad (_("backward ref to unknown label \"%d:\""), + (int) number); + expressionP->X_op = O_constant; + } - expressionP->X_add_symbol = symbolP; - expressionP->X_seg = S_GET_SEGMENT (symbolP); + expressionP->X_add_number = 0; + } /* case 'b' */ + else if (LOCAL_LABELS_FB && c == 'f') + { + /* Forward reference. Expect symbol to be undefined or + unknown. undefined: seen it before. unknown: never seen + it before. + + Construct a local label name, then an undefined symbol. + Don't create a xseg frag for it: caller may do that. + Just return it as never seen before. */ + name = fb_label_name ((int) number, 1); + symbolP = symbol_find_or_make (name); + /* We have no need to check symbol properties. */ +#ifndef many_segments + /* Since "know" puts its arg into a "string", we + can't have newlines in the argument. */ + know (S_GET_SEGMENT (symbolP) == undefined_section || S_GET_SEGMENT (symbolP) == text_section || S_GET_SEGMENT (symbolP) == data_section); +#endif + expressionP->X_op = O_symbol; + expressionP->X_add_symbol = symbolP; + expressionP->X_add_number = 0; + } /* case 'f' */ + else if (LOCAL_LABELS_DOLLAR && c == '$') + { + /* If the dollar label is *currently* defined, then this is just + another reference to it. If it is not *currently* defined, + then this is a fresh instantiation of that number, so create + it. */ - } - else - { /* either not seen or not defined. */ - as_bad ("backw. ref to unknown label \"%d:\", 0 assumed.", number); - expressionP->X_seg = SEG_ABSOLUTE; - } + if (dollar_label_defined ((long) number)) + { + name = dollar_label_name ((long) number, 0); + symbolP = symbol_find (name); + know (symbolP != NULL); + } + else + { + name = dollar_label_name ((long) number, 1); + symbolP = symbol_find_or_make (name); + } - expressionP->X_add_number = 0; - break; - } /* case 'b' */ + expressionP->X_op = O_symbol; + expressionP->X_add_symbol = symbolP; + expressionP->X_add_number = 0; + } /* case '$' */ + else + { + expressionP->X_op = O_constant; + expressionP->X_add_number = number; + input_line_pointer--; /* Restore following character. */ + } /* Really just a number. */ + } + else + { + /* Not a small number. */ + expressionP->X_op = O_big; + expressionP->X_add_number = number; /* Number of littlenums. */ + input_line_pointer--; /* -> char following number. */ + } +} - case 'f': - { - /* - * forward reference. expect symbol to be undefined or - * unknown. undefined: seen it before. unknown: never seen - * it before. - * construct a local label name, then an undefined symbol. - * don't create a xseg frag for it: caller may do that. - * just return it as never seen before. - */ - name = fb_label_name ((int) number, 1); - symbolP = symbol_find_or_make (name); - /* we have no need to check symbol properties. */ -#ifndef many_segments - /* since "know" puts its arg into a "string", we - can't have newlines in the argument. */ - know (S_GET_SEGMENT (symbolP) == SEG_UNKNOWN || S_GET_SEGMENT (symbolP) == SEG_TEXT || S_GET_SEGMENT (symbolP) == SEG_DATA); -#endif - expressionP->X_add_symbol = symbolP; - expressionP->X_seg = SEG_UNKNOWN; - expressionP->X_subtract_symbol = NULL; - expressionP->X_add_number = 0; +/* Parse an MRI multi character constant. */ - break; - } /* case 'f' */ +static void +mri_char_constant (expressionS *expressionP) +{ + int i; -#endif /* LOCAL_LABELS_FB */ + if (*input_line_pointer == '\'' + && input_line_pointer[1] != '\'') + { + expressionP->X_op = O_constant; + expressionP->X_add_number = 0; + return; + } -#ifdef LOCAL_LABELS_DOLLAR + /* In order to get the correct byte ordering, we must build the + number in reverse. */ + for (i = SIZE_OF_LARGE_NUMBER - 1; i >= 0; i--) + { + int j; - case '$': - { + generic_bignum[i] = 0; + for (j = 0; j < CHARS_PER_LITTLENUM; j++) + { + if (*input_line_pointer == '\'') + { + if (input_line_pointer[1] != '\'') + break; + ++input_line_pointer; + } + generic_bignum[i] <<= 8; + generic_bignum[i] += *input_line_pointer; + ++input_line_pointer; + } - /* If the dollar label is *currently* defined, then this is just - another reference to it. If it is not *currently* defined, - then this is a fresh instantiation of that number, so create - it. */ + 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. */ + for (; j < CHARS_PER_LITTLENUM; j++) + generic_bignum[i] <<= 8; + } - if (dollar_label_defined (number)) - { - name = dollar_label_name (number, 0); - symbolP = symbol_find (name); - know (symbolP != NULL); - } - else - { - name = dollar_label_name (number, 1); - symbolP = symbol_find_or_make (name); - } + if (*input_line_pointer == '\'' + && input_line_pointer[1] != '\'') + break; + } - expressionP->X_add_symbol = symbolP; - expressionP->X_add_number = 0; - expressionP->X_seg = S_GET_SEGMENT (symbolP); + if (i < 0) + { + as_bad (_("character constant too large")); + i = 0; + } - break; - } /* case '$' */ + if (i > 0) + { + int c; + int j; -#endif /* LOCAL_LABELS_DOLLAR */ + c = SIZE_OF_LARGE_NUMBER - i; + for (j = 0; j < c; j++) + generic_bignum[j] = generic_bignum[i + j]; + i = c; + } - default: - { - expressionP->X_add_number = number; - expressionP->X_seg = SEG_ABSOLUTE; - input_line_pointer--; /* restore following character. */ - break; - } /* really just a number */ + know (LITTLENUM_NUMBER_OF_BITS == 16); + if (i > 2) + { + expressionP->X_op = O_big; + expressionP->X_add_number = i; + } + else + { + expressionP->X_op = O_constant; + if (i < 2) + expressionP->X_add_number = generic_bignum[0] & LITTLENUM_MASK; + else + expressionP->X_add_number = + (((generic_bignum[1] & LITTLENUM_MASK) + << LITTLENUM_NUMBER_OF_BITS) + | (generic_bignum[0] & LITTLENUM_MASK)); + } - } /* switch on char following the number */ + /* Skip the final closing quote. */ + ++input_line_pointer; +} +/* Return an expression representing the current location. This + handles the magic symbol `.'. */ +void +current_location (expressionS *expressionp) +{ + if (now_seg == absolute_section) + { + expressionp->X_op = O_constant; + expressionp->X_add_number = abs_section_offset; } else - { /* not a small number */ - expressionP->X_add_number = number; - expressionP->X_seg = SEG_BIG; - input_line_pointer--; /*->char following number. */ - } /* if (small) */ -} /* integer_constant() */ - - -/* - * Summary of operand(). - * - * in: Input_line_pointer points to 1st char of operand, which may - * be a space. - * - * out: A expressionS. X_seg determines how to understand the rest of the - * expressionS. - * The operand may have been empty: in this case X_seg == SEG_ABSENT. - * Input_line_pointer->(next non-blank) char after operand. - * - */ - + { + expressionp->X_op = O_symbol; + expressionp->X_add_symbol = &dot_symbol; + expressionp->X_add_number = 0; + } +} +/* In: Input_line_pointer points to 1st char of operand, which may + be a space. + + 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) - register expressionS *expressionP; +operand (expressionS *expressionP, enum expr_mode mode) { - register char c; - register symbolS *symbolP; /* points to symbol */ - register char *name; /* points to name of symbol */ - /* invented for humans only, hope */ - /* optimising compiler flushes it! */ - register short int radix; /* 2, 8, 10 or 16, 0 when floating */ - /* 0 means we saw start of a floating- */ - /* point constant. */ - - /* digits, assume it is a bignum. */ + char c; + symbolS *symbolP; /* Points to symbol. */ + char *name; /* Points to name of symbol. */ + segT segment; + /* All integers are regarded as unsigned unless they are negated. + This is because the only thing which cares whether a number is + unsigned is the code in emit_expr which extends constants into + bignums. It should only sign extend negative numbers, so that + something like ``.quad 0x80000000'' is not sign extended even + though it appears negative if valueT is 32 bits. */ + expressionP->X_unsigned = 1; + expressionP->X_extrabit = 0; + /* Digits, assume it is a bignum. */ + SKIP_WHITESPACE (); /* Leading whitespace is part of operand. */ + c = *input_line_pointer++; /* input_line_pointer -> past char in c. */ - SKIP_WHITESPACE (); /* leading whitespace is part of operand. */ - c = *input_line_pointer++; /* input_line_pointer->past char in c. */ + if (is_end_of_line[(unsigned char) c]) + goto eol; switch (c) { -#ifdef MRI - case '%': - integer_constant (2, expressionP); - break; - case '@': - integer_constant (8, expressionP); - break; - case '$': - integer_constant (16, expressionP); - break; -#endif case '1': case '2': case '3': @@ -395,54 +765,106 @@ operand (expressionP) case '9': input_line_pointer--; - integer_constant (10, expressionP); + integer_constant ((NUMBERS_WITH_SUFFIX || flag_m68k_mri) + ? 0 : 10, + 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 + +#ifdef LITERAL_PREFIXPERCENT_BIN + case '%': + integer_constant (2, expressionP); break; +#endif case '0': - /* non-decimal radix */ + /* Non-decimal radix. */ + if (NUMBERS_WITH_SUFFIX || flag_m68k_mri) + { + char *s; + /* Check for a hex or float constant. */ + for (s = input_line_pointer; hex_p (*s); s++) + ; + if (*s == 'h' || *s == 'H' || *input_line_pointer == '.') + { + --input_line_pointer; + integer_constant (0, expressionP); + break; + } + } c = *input_line_pointer; switch (c) { - + case 'o': + case 'O': + case 'q': + case 'Q': + case '8': + case '9': + if (NUMBERS_WITH_SUFFIX || flag_m68k_mri) + { + integer_constant (0, expressionP); + break; + } + /* Fall through. */ default: + default_case: if (c && strchr (FLT_CHARS, c)) { input_line_pointer++; floating_constant (expressionP); + expressionP->X_add_number = - TOLOWER (c); } else { - /* The string was only zero */ - expressionP->X_add_symbol = 0; + /* The string was only zero. */ + expressionP->X_op = O_constant; expressionP->X_add_number = 0; - expressionP->X_seg = SEG_ABSOLUTE; } break; case 'x': case 'X': + if (flag_m68k_mri) + goto default_case; input_line_pointer++; integer_constant (16, expressionP); break; case 'b': -#ifdef LOCAL_LABELS_FB - if (!*input_line_pointer - || (!strchr ("+-.0123456789", *input_line_pointer) - && !strchr (EXP_CHARS, *input_line_pointer))) + if (LOCAL_LABELS_FB && !flag_m68k_mri + && input_line_pointer[1] != '0' + && input_line_pointer[1] != '1') { + /* Parse this as a back reference to label 0. */ input_line_pointer--; integer_constant (10, expressionP); break; } -#endif + /* Otherwise, parse this as a binary number. */ + /* Fall through. */ case 'B': - input_line_pointer++; - integer_constant (2, expressionP); - break; + if (input_line_pointer[1] == '0' + || input_line_pointer[1] == '1') + { + input_line_pointer++; + integer_constant (2, expressionP); + break; + } + if (flag_m68k_mri || NUMBERS_WITH_SUFFIX) + input_line_pointer++; + goto default_case; case '0': case '1': @@ -452,420 +874,599 @@ operand (expressionP) case '5': case '6': case '7': - integer_constant (8, expressionP); + integer_constant ((flag_m68k_mri || NUMBERS_WITH_SUFFIX) + ? 0 : 8, + expressionP); break; case 'f': -#ifdef LOCAL_LABELS_FB - /* if it says '0f' and the line ends or it doesn't look like - a floating point #, its a local label ref. dtrt */ - /* likewise for the b's. xoxorich. */ - if (c == 'f' - && (!*input_line_pointer || - (!strchr ("+-.0123456789", *input_line_pointer) && - !strchr (EXP_CHARS, *input_line_pointer)))) + if (LOCAL_LABELS_FB) { - input_line_pointer -= 1; - integer_constant (10, expressionP); - break; + int is_label = 1; + + /* If it says "0f" and it could possibly be a floating point + number, make it one. Otherwise, make it a local label, + and try to deal with parsing the rest later. */ + if (!is_end_of_line[(unsigned char) input_line_pointer[1]] + && strchr (FLT_CHARS, 'f') != NULL) + { + char *cp = input_line_pointer + 1; + + atof_generic (&cp, ".", EXP_CHARS, + &generic_floating_point_number); + + /* Was nothing parsed, or does it look like an + expression? */ + is_label = (cp == input_line_pointer + 1 + || (cp == input_line_pointer + 2 + && (cp[-1] == '-' || cp[-1] == '+')) + || *cp == 'f' + || *cp == 'b'); + } + if (is_label) + { + input_line_pointer--; + integer_constant (10, expressionP); + break; + } } -#endif + /* Fall through. */ case 'd': case 'D': + if (flag_m68k_mri || NUMBERS_WITH_SUFFIX) + { + integer_constant (0, expressionP); + break; + } + /* Fall through. */ case 'F': case 'r': case 'e': case 'E': case 'g': case 'G': - input_line_pointer++; floating_constant (expressionP); + expressionP->X_add_number = - TOLOWER (c); break; -#ifdef LOCAL_LABELS_DOLLAR case '$': - integer_constant (10, expressionP); - break; -#endif + if (LOCAL_LABELS_DOLLAR) + { + integer_constant (10, expressionP); + break; + } + else + goto default_case; } break; - case '(': - /* didn't begin with digit & not a name */ - { - (void) expression (expressionP); - /* Expression() will pass trailing whitespace */ - if (*input_line_pointer++ != ')') - { - as_bad ("Missing ')' assumed"); - input_line_pointer--; - } - /* here with input_line_pointer->char after "(...)" */ - } - return expressionP->X_seg; - +#ifndef NEED_INDEX_OPERATOR + case '[': +# ifdef md_need_index_operator + if (md_need_index_operator()) + goto de_fault; +# endif + /* FALLTHROUGH */ +#endif + case '(': + /* Didn't begin with digit & not a name. */ + segment = expr (0, expressionP, mode); + /* expression () will pass trailing whitespace. */ + if ((c == '(' && *input_line_pointer != ')') + || (c == '[' && *input_line_pointer != ']')) + { + if (* input_line_pointer) + as_bad (_("found '%c', expected: '%c'"), + * input_line_pointer, c == '(' ? ')' : ']'); + else + as_bad (_("missing '%c'"), c == '(' ? ')' : ']'); + } + else + input_line_pointer++; + SKIP_WHITESPACE (); + /* Here with input_line_pointer -> char after "(...)". */ + return segment; + +#ifdef TC_M68K + case 'E': + if (! flag_m68k_mri || *input_line_pointer != '\'') + goto de_fault; + as_bad (_("EBCDIC constants are not supported")); + /* Fall through. */ + case 'A': + if (! flag_m68k_mri || *input_line_pointer != '\'') + goto de_fault; + ++input_line_pointer; + /* Fall through. */ +#endif case '\'': - /* - * Warning: to conform to other people's assemblers NO ESCAPEMENT is permitted - * for a single quote. The next character, parity errors and all, is taken - * as the value of the operand. VERY KINKY. - */ - expressionP->X_add_number = *input_line_pointer++; - expressionP->X_seg = SEG_ABSOLUTE; + if (! flag_m68k_mri) + { + /* Warning: to conform to other people's assemblers NO + ESCAPEMENT is permitted for a single quote. The next + character, parity errors and all, is taken as the value + of the operand. VERY KINKY. */ + expressionP->X_op = O_constant; + expressionP->X_add_number = *input_line_pointer++; + break; + } + + mri_char_constant (expressionP); break; +#ifdef TC_M68K + case '"': + /* Double quote is the bitwise not operator in MRI mode. */ + if (! flag_m68k_mri) + goto de_fault; + /* Fall through. */ +#endif case '~': + /* '~' is permitted to start a label on the Delta. */ + if (is_name_beginner (c)) + goto isname; + case '!': case '-': case '+': - { - /* unary operator: hope for SEG_ABSOLUTE */ - switch (operand (expressionP)) +#ifdef md_operator + unary: +#endif + operand (expressionP, mode); + if (expressionP->X_op == O_constant) { - case SEG_ABSOLUTE: - /* input_line_pointer -> char after operand */ + /* input_line_pointer -> char after operand. */ if (c == '-') { - expressionP->X_add_number = -expressionP->X_add_number; - /* - * Notice: '-' may overflow: no warning is given. This is compatible - * with other people's assemblers. Sigh. - */ + expressionP->X_add_number + = - (addressT) expressionP->X_add_number; + /* Notice: '-' may overflow: no warning is given. + This is compatible with other people's + assemblers. Sigh. */ + expressionP->X_unsigned = 0; + if (expressionP->X_add_number) + expressionP->X_extrabit ^= 1; } + else if (c == '~' || c == '"') + expressionP->X_add_number = ~ expressionP->X_add_number; + 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 == '-') { - expressionP->X_add_number = ~expressionP->X_add_number; + for (i = 0; i < expressionP->X_add_number; ++i) + generic_bignum[i] = ~generic_bignum[i]; + + /* Extend the bignum to at least the size of .octa. */ + if (expressionP->X_add_number < SIZE_OF_LARGE_NUMBER) + { + expressionP->X_add_number = SIZE_OF_LARGE_NUMBER; + for (; i < expressionP->X_add_number; ++i) + generic_bignum[i] = ~(LITTLENUM_TYPE) 0; + } + + if (c == '-') + for (i = 0; i < expressionP->X_add_number; ++i) + { + generic_bignum[i] += 1; + if (generic_bignum[i]) + break; + } } - break; - - case SEG_TEXT: - case SEG_DATA: - case SEG_BSS: - case SEG_PASS1: - case SEG_UNKNOWN: - if (c == '-') - { /* JF I hope this hack works */ - expressionP->X_subtract_symbol = expressionP->X_add_symbol; - expressionP->X_add_symbol = 0; - expressionP->X_seg = SEG_DIFFERENCE; - break; + else if (c == '!') + { + for (i = 0; i < expressionP->X_add_number; ++i) + if (generic_bignum[i] != 0) + break; + expressionP->X_add_number = i >= expressionP->X_add_number; + expressionP->X_op = O_constant; + expressionP->X_unsigned = 1; + expressionP->X_extrabit = 0; + } + } + else if (expressionP->X_op != O_illegal + && expressionP->X_op != O_absent) + { + 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; } - default: /* unary on non-absolute is unsuported */ - as_warn ("Unary operator %c ignored because bad operand follows", c); - break; - /* Expression undisturbed from operand(). */ } + else + as_warn (_("Unary operator %c ignored because bad operand follows"), + c); } + break; +#if defined (DOLLAR_DOT) || defined (TC_M68K) + case '$': + /* '$' is the program counter when in MRI mode, or when + DOLLAR_DOT is defined. */ +#ifndef DOLLAR_DOT + if (! flag_m68k_mri) + goto de_fault; +#endif + if (DOLLAR_AMBIGU && hex_p (*input_line_pointer)) + { + /* In MRI mode and on Z80, '$' is also used as the prefix + for a hexadecimal constant. */ + integer_constant (16, expressionP); + break; + } + if (is_part_of_name (*input_line_pointer)) + goto isname; + current_location (expressionP); break; +#endif case '.': if (!is_part_of_name (*input_line_pointer)) { - char *fake; - extern struct obstack frags; - - /* JF: '.' is pseudo symbol with value of current location - in current segment. */ -#ifdef DOT_LABEL_PREFIX - fake = ".L0\001"; -#else - fake = "L0\001"; -#endif - symbolP = symbol_new (fake, - now_seg, - (valueT) (obstack_next_free (&frags) - frag_now->fr_literal), - frag_now); - - expressionP->X_add_number = 0; - expressionP->X_add_symbol = symbolP; - expressionP->X_seg = now_seg; + current_location (expressionP); break; - } - else + else if ((strncasecmp (input_line_pointer, "startof.", 8) == 0 + && ! is_part_of_name (input_line_pointer[8])) + || (strncasecmp (input_line_pointer, "sizeof.", 7) == 0 + && ! is_part_of_name (input_line_pointer[7]))) { - goto isname; + int start; + + start = (input_line_pointer[1] == 't' + || input_line_pointer[1] == 'T'); + input_line_pointer += start ? 8 : 7; + SKIP_WHITESPACE (); + if (*input_line_pointer != '(') + as_bad (_("syntax error in .startof. or .sizeof.")); + else + { + char *buf; + ++input_line_pointer; + SKIP_WHITESPACE (); + c = get_symbol_name (& name); - } - case ',': - case '\n': - /* can't imagine any other kind of operand */ - expressionP->X_seg = SEG_ABSENT; - input_line_pointer--; - md_operand (expressionP); - break; - /* Fall through */ - default: - if (is_name_beginner (c)) /* here if did not begin with a digit */ - { - /* - * Identifier begins here. - * This is kludged for speed, so code is repeated. - */ - isname: - name = --input_line_pointer; - c = get_symbol_end (); - symbolP = symbol_find_or_make (name); - /* - * If we have an absolute symbol or a reg, then we know its value now. - */ - expressionP->X_seg = S_GET_SEGMENT (symbolP); - switch (expressionP->X_seg) - { - case SEG_ABSOLUTE: - case SEG_REGISTER: - expressionP->X_add_number = S_GET_VALUE (symbolP); - break; + buf = concat (start ? ".startof." : ".sizeof.", name, + (char *) NULL); + symbolP = symbol_make (buf); + free (buf); - default: - expressionP->X_add_number = 0; + expressionP->X_op = O_symbol; expressionP->X_add_symbol = symbolP; + expressionP->X_add_number = 0; + + *input_line_pointer = c; + SKIP_WHITESPACE_AFTER_NAME (); + if (*input_line_pointer != ')') + as_bad (_("syntax error in .startof. or .sizeof.")); + else + ++input_line_pointer; } - *input_line_pointer = c; - expressionP->X_subtract_symbol = NULL; + break; } else { - as_bad ("Bad expression"); - expressionP->X_add_number = 0; - expressionP->X_seg = SEG_ABSOLUTE; - + goto isname; } - } + case ',': + eol: + /* Can't imagine any other kind of operand. */ + expressionP->X_op = O_absent; + input_line_pointer--; + break; +#ifdef TC_M68K + case '%': + if (! flag_m68k_mri) + goto de_fault; + integer_constant (2, expressionP); + break; + case '@': + if (! flag_m68k_mri) + goto de_fault; + integer_constant (8, expressionP); + break; + case ':': + if (! flag_m68k_mri) + goto de_fault; + /* In MRI mode, this is a floating point constant represented + using hexadecimal digits. */ + ++input_line_pointer; + integer_constant (16, expressionP); + break; + case '*': + if (! flag_m68k_mri || is_part_of_name (*input_line_pointer)) + goto de_fault; - /* - * It is more 'efficient' to clean up the expressionS when they are created. - * Doing it here saves lines of code. - */ - clean_up_expression (expressionP); - SKIP_WHITESPACE (); /*->1st char after operand. */ - know (*input_line_pointer != ' '); - return (expressionP->X_seg); -} /* operand() */ - + current_location (expressionP); + break; +#endif -/* Internal. Simplify a struct expression for use by expr() */ + default: +#if defined(md_need_index_operator) || defined(TC_M68K) + de_fault: +#endif + if (is_name_beginner (c) || c == '"') /* Here if did not begin with a digit. */ + { + /* Identifier begins here. + This is kludged for speed, so code is repeated. */ + isname: + -- input_line_pointer; + c = get_symbol_name (&name); -/* - * In: address of a expressionS. - * The X_seg field of the expressionS may only take certain values. - * Now, we permit SEG_PASS1 to make code smaller & faster. - * 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_seg from SEG_DIFFERENCE to SEG_ABSOLUTE; - * Unused fields zeroed to help expr(). - */ +#ifdef md_operator + { + operatorT op = md_operator (name, 1, &c); -static void -clean_up_expression (expressionP) - register expressionS *expressionP; -{ - switch (expressionP->X_seg) - { - case SEG_ABSENT: - case SEG_PASS1: - expressionP->X_add_symbol = NULL; - expressionP->X_subtract_symbol = NULL; - expressionP->X_add_number = 0; - break; + switch (op) + { + case O_uminus: + restore_line_pointer (c); + c = '-'; + goto unary; + case O_bit_not: + restore_line_pointer (c); + c = '~'; + goto unary; + case O_logical_not: + restore_line_pointer (c); + c = '!'; + goto unary; + case O_illegal: + as_bad (_("invalid use of operator \"%s\""), name); + break; + default: + break; + } - case SEG_BIG: - case SEG_ABSOLUTE: - expressionP->X_subtract_symbol = NULL; - expressionP->X_add_symbol = NULL; - break; + if (op != O_absent && op != O_illegal) + { + restore_line_pointer (c); + expr (9, expressionP, mode); + expressionP->X_add_symbol = make_expr_symbol (expressionP); + expressionP->X_op_symbol = NULL; + expressionP->X_add_number = 0; + expressionP->X_op = op; + break; + } + } +#endif - case SEG_UNKNOWN: - expressionP->X_subtract_symbol = NULL; - break; +#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, mode, &c)) + { + restore_line_pointer (c); + break; + } +#endif - case SEG_DIFFERENCE: - /* - * It does not hurt to 'cancel' NULL==NULL - * when comparing symbols for 'eq'ness. - * It is faster to re-cancel them to NULL - * than to check for this special case. - */ - if (expressionP->X_subtract_symbol == expressionP->X_add_symbol - || (expressionP->X_subtract_symbol - && expressionP->X_add_symbol - && expressionP->X_subtract_symbol->sy_frag == expressionP->X_add_symbol->sy_frag - && S_GET_VALUE (expressionP->X_subtract_symbol) == S_GET_VALUE (expressionP->X_add_symbol))) - { - expressionP->X_subtract_symbol = NULL; - expressionP->X_add_symbol = NULL; - expressionP->X_seg = SEG_ABSOLUTE; - } - break; +#ifdef TC_I960 + /* The MRI i960 assembler permits + lda sizeof code,g13 + FIXME: This should use md_parse_name. */ + if (flag_mri + && (strcasecmp (name, "sizeof") == 0 + || strcasecmp (name, "startof") == 0)) + { + int start; + char *buf; - case SEG_REGISTER: - expressionP->X_add_symbol = NULL; - expressionP->X_subtract_symbol = NULL; - break; + start = (name[1] == 't' + || name[1] == 'T'); - default: - if (SEG_NORMAL (expressionP->X_seg)) - { - expressionP->X_subtract_symbol = NULL; - } - else - { - BAD_CASE (expressionP->X_seg); - } - break; - } -} /* clean_up_expression() */ - -/* - * expr_part () - * - * Internal. Made a function because this code is used in 2 places. - * Generate error or correct X_?????_symbol of expressionS. - */ + *input_line_pointer = c; + SKIP_WHITESPACE_AFTER_NAME (); -/* - * symbol_1 += symbol_2 ... well ... sort of. - */ + c = get_symbol_name (& name); -static segT -expr_part (symbol_1_PP, symbol_2_P) - symbolS **symbol_1_PP; - symbolS *symbol_2_P; -{ - segT return_value; -#ifndef MANY_SEGMENTS - know ((*symbol_1_PP) == NULL || (S_GET_SEGMENT (*symbol_1_PP) == SEG_TEXT) || (S_GET_SEGMENT (*symbol_1_PP) == SEG_DATA) || (S_GET_SEGMENT (*symbol_1_PP) == SEG_BSS) || (!S_IS_DEFINED (*symbol_1_PP))); - know (symbol_2_P == NULL || (S_GET_SEGMENT (symbol_2_P) == SEG_TEXT) || (S_GET_SEGMENT (symbol_2_P) == SEG_DATA) || (S_GET_SEGMENT (symbol_2_P) == SEG_BSS) || (!S_IS_DEFINED (symbol_2_P))); + buf = concat (start ? ".startof." : ".sizeof.", name, + (char *) NULL); + symbolP = symbol_make (buf); + free (buf); + + expressionP->X_op = O_symbol; + expressionP->X_add_symbol = symbolP; + expressionP->X_add_number = 0; + + *input_line_pointer = c; + SKIP_WHITESPACE_AFTER_NAME (); + break; + } #endif - if (*symbol_1_PP) - { - if (!S_IS_DEFINED (*symbol_1_PP)) - { - if (symbol_2_P) + + symbolP = symbol_find_or_make (name); + + /* If we have an absolute symbol or a reg, then we know its + value now. */ + segment = S_GET_SEGMENT (symbolP); + if (mode != expr_defer + && segment == absolute_section + && !S_FORCE_RELOC (symbolP, 0)) { - return_value = SEG_PASS1; - *symbol_1_PP = NULL; + expressionP->X_op = O_constant; + expressionP->X_add_number = S_GET_VALUE (symbolP); + } + else if (mode != expr_defer && segment == reg_section) + { + expressionP->X_op = O_register; + expressionP->X_add_number = S_GET_VALUE (symbolP); } else { - know (!S_IS_DEFINED (*symbol_1_PP)); - return_value = SEG_UNKNOWN; + expressionP->X_op = O_symbol; + expressionP->X_add_symbol = symbolP; + expressionP->X_add_number = 0; } + + restore_line_pointer (c); } else { - if (symbol_2_P) + /* Let the target try to parse it. Success is indicated by changing + the X_op field to something other than O_absent and pointing + input_line_pointer past the expression. If it can't parse the + expression, X_op and input_line_pointer should be unchanged. */ + expressionP->X_op = O_absent; + --input_line_pointer; + md_operand (expressionP); + if (expressionP->X_op == O_absent) { - if (!S_IS_DEFINED (symbol_2_P)) - { - *symbol_1_PP = NULL; - return_value = SEG_PASS1; - } - else - { - /* {seg1} - {seg2} */ - as_bad ("Expression too complex, 2 symbolS forgotten: \"%s\" \"%s\"", - S_GET_NAME (*symbol_1_PP), S_GET_NAME (symbol_2_P)); - *symbol_1_PP = NULL; - return_value = SEG_ABSOLUTE; - } - } - else - { - return_value = S_GET_SEGMENT (*symbol_1_PP); + ++input_line_pointer; + as_bad (_("bad expression")); + expressionP->X_op = O_constant; + expressionP->X_add_number = 0; } } + break; } - else - { /* (* symbol_1_PP) == NULL */ - if (symbol_2_P) - { - *symbol_1_PP = symbol_2_P; - return_value = S_GET_SEGMENT (symbol_2_P); - } - else - { - *symbol_1_PP = NULL; - return_value = SEG_ABSOLUTE; - } + + /* It is more 'efficient' to clean up the expressionS when they are + created. Doing it here saves lines of code. */ + clean_up_expression (expressionP); + SKIP_WHITESPACE (); /* -> 1st char after operand. */ + know (*input_line_pointer != ' '); + + /* The PA port needs this information. */ + if (expressionP->X_add_symbol) + symbol_mark_used (expressionP->X_add_symbol); + + if (mode != expr_defer) + { + 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); } -#ifndef MANY_SEGMENTS - know (return_value == SEG_ABSOLUTE || return_value == SEG_TEXT || return_value == SEG_DATA || return_value == SEG_BSS || return_value == SEG_UNKNOWN || return_value == SEG_PASS1); -#endif - know ((*symbol_1_PP) == NULL || (S_GET_SEGMENT (*symbol_1_PP) == return_value)); - return (return_value); -} /* expr_part() */ + + switch (expressionP->X_op) + { + default: + return absolute_section; + case O_symbol: + return S_GET_SEGMENT (expressionP->X_add_symbol); + case O_register: + return reg_section; + } +} -/* Expression parser. */ - -/* - * We allow an empty expression, and just assume (absolute,0) silently. - * 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 - * 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) - * or 1 symbol. Then "symbol+constant" and "symbol-symbol" are common. - * So I guess it doesn't really matter how inefficient more complex expressions - * are parsed. - * - * After expr(RANK,resultP) input_line_pointer->operator of rank <= RANK. - * Also, we have consumed any leading or trailing spaces (operand does that) - * and done all intervening operators. - */ - -typedef enum +/* Internal. Simplify a struct expression for use by expr (). */ + +/* 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: + Unused fields zeroed to help expr (). */ + +static void +clean_up_expression (expressionS *expressionP) { - O_illegal, /* (0) what we get for illegal op */ - - O_multiply, /* (1) * */ - O_divide, /* (2) / */ - O_modulus, /* (3) % */ - O_left_shift, /* (4) < */ - O_right_shift, /* (5) > */ - O_bit_inclusive_or, /* (6) | */ - O_bit_or_not, /* (7) ! */ - O_bit_exclusive_or, /* (8) ^ */ - O_bit_and, /* (9) & */ - O_add, /* (10) + */ - O_subtract /* (11) - */ + switch (expressionP->X_op) + { + case O_illegal: + case O_absent: + expressionP->X_add_number = 0; + /* Fall through. */ + case O_big: + case O_constant: + case O_register: + expressionP->X_add_symbol = NULL; + /* Fall through. */ + case O_symbol: + case O_uminus: + case O_bit_not: + expressionP->X_op_symbol = NULL; + break; + default: + break; + } } + +/* Expression parser. */ -operatorT; +/* We allow an empty expression, and just assume (absolute,0) silently. + Unary operators and parenthetical expressions are treated as operands. + As usual, Q==quantity==operand, O==operator, X==expression mnemonics. -#define __ O_illegal + 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) + or 1 symbol. Then "symbol+constant" and "symbol-symbol" are common. + So I guess it doesn't really matter how inefficient more complex expressions + are parsed. -static const operatorT op_encoding[256] = -{ /* maps ASCII->operators */ + After expr(RANK,resultP) input_line_pointer->operator of rank <= RANK. + Also, we have consumed any leading or trailing spaces (operand does that) + and done all intervening operators. + This returns the segment of the result, which will be + absolute_section or the segment of a symbol. */ + +#undef __ +#define __ O_illegal +#ifndef O_SINGLE_EQ +#define O_SINGLE_EQ O_illegal +#endif + +/* Maps ASCII -> operators. */ +static const operatorT op_encoding[256] = { __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, O_bit_or_not, __, __, __, O_modulus, O_bit_and, __, __, __, O_multiply, O_add, __, O_subtract, __, O_divide, __, __, __, __, __, __, __, __, - __, __, __, __, O_left_shift, __, O_right_shift, __, + __, __, __, __, O_lt, O_SINGLE_EQ, O_gt, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, - __, __, __, __, __, __, O_bit_exclusive_or, __, + __, __, __, +#ifdef NEED_INDEX_OPERATOR + O_index, +#else + __, +#endif + __, __, O_bit_exclusive_or, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, @@ -881,284 +1482,908 @@ static const operatorT op_encoding[256] = __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __ }; +/* Rank Examples + 0 operand, (expression) + 1 || + 2 && + 3 == <> < <= >= > + 4 + - + 5 used for * / % in MRI mode + 6 & ^ ! | + 7 * / % << >> + 8 unary - unary ~ +*/ +static operator_rankT op_rank[O_max] = { + 0, /* O_illegal */ + 0, /* O_absent */ + 0, /* O_constant */ + 0, /* O_symbol */ + 0, /* O_symbol_rva */ + 0, /* O_register */ + 0, /* O_big */ + 9, /* O_uminus */ + 9, /* O_bit_not */ + 9, /* O_logical_not */ + 8, /* O_multiply */ + 8, /* O_divide */ + 8, /* O_modulus */ + 8, /* O_left_shift */ + 8, /* O_right_shift */ + 7, /* O_bit_inclusive_or */ + 7, /* O_bit_or_not */ + 7, /* O_bit_exclusive_or */ + 7, /* O_bit_and */ + 5, /* O_add */ + 5, /* O_subtract */ + 4, /* O_eq */ + 4, /* O_ne */ + 4, /* O_lt */ + 4, /* O_le */ + 4, /* O_ge */ + 4, /* O_gt */ + 3, /* O_logical_and */ + 2, /* O_logical_or */ + 1, /* O_index */ +}; -/* - * Rank Examples - * 0 operand, (expression) - * 1 + - - * 2 & ^ ! | - * 3 * / % << >> - */ -static const operator_rankT - op_rank[] = -{0, 3, 3, 3, 3, 3, 2, 2, 2, 2, 1, 1}; +/* Unfortunately, in MRI mode for the m68k, multiplication and + division have lower precedence than the bit wise operators. This + function sets the operator precedences correctly for the current + mode. Also, MRI uses a different bit_not operator, and this fixes + that as well. */ + +#define STANDARD_MUL_PRECEDENCE 8 +#define MRI_MUL_PRECEDENCE 6 + +void +expr_set_precedence (void) +{ + if (flag_m68k_mri) + { + op_rank[O_multiply] = MRI_MUL_PRECEDENCE; + op_rank[O_divide] = MRI_MUL_PRECEDENCE; + op_rank[O_modulus] = MRI_MUL_PRECEDENCE; + } + else + { + op_rank[O_multiply] = STANDARD_MUL_PRECEDENCE; + op_rank[O_divide] = STANDARD_MUL_PRECEDENCE; + op_rank[O_modulus] = STANDARD_MUL_PRECEDENCE; + } +} + +void +expr_set_rank (operatorT op, operator_rankT rank) +{ + gas_assert (op >= O_md1 && op < ARRAY_SIZE (op_rank)); + op_rank[op] = rank; +} + +/* Initialize the expression parser. */ + +void +expr_begin (void) +{ + expr_set_precedence (); + + /* Verify that X_op field is wide enough. */ + { + expressionS e; + e.X_op = O_max; + gas_assert (e.X_op == O_max); + } +} -/* Return resultP->X_seg. */ -segT -expr (rank, resultP) - register operator_rankT rank; /* Larger # is higher rank. */ - register expressionS *resultP; /* Deliver result here. */ +/* Return the encoding for the operator at INPUT_LINE_POINTER, and + sets NUM_CHARS to the number of characters in the operator. + Does not advance INPUT_LINE_POINTER. */ + +static inline operatorT +operatorf (int *num_chars) +{ + int c; + operatorT ret; + + c = *input_line_pointer & 0xff; + *num_chars = 1; + + if (is_end_of_line[c]) + return O_illegal; + +#ifdef md_operator + if (is_name_beginner (c)) + { + char *name; + char ec = get_symbol_name (& name); + + ret = md_operator (name, 2, &ec); + switch (ret) + { + case O_absent: + *input_line_pointer = ec; + input_line_pointer = name; + break; + case O_uminus: + case O_bit_not: + case O_logical_not: + as_bad (_("invalid use of operator \"%s\""), name); + ret = O_illegal; + /* FALLTHROUGH */ + default: + *input_line_pointer = ec; + *num_chars = input_line_pointer - name; + input_line_pointer = name; + return ret; + } + } +#endif + + switch (c) + { + default: + ret = op_encoding[c]; +#ifdef md_operator + if (ret == O_illegal) + { + char *start = input_line_pointer; + + ret = md_operator (NULL, 2, NULL); + if (ret != O_illegal) + *num_chars = input_line_pointer - start; + input_line_pointer = start; + } +#endif + return ret; + + case '+': + case '-': + return op_encoding[c]; + + case '<': + switch (input_line_pointer[1]) + { + default: + return op_encoding[c]; + case '<': + ret = O_left_shift; + break; + case '>': + ret = O_ne; + break; + case '=': + ret = O_le; + break; + } + *num_chars = 2; + return ret; + + case '=': + if (input_line_pointer[1] != '=') + return op_encoding[c]; + + *num_chars = 2; + return O_eq; + + case '>': + switch (input_line_pointer[1]) + { + default: + return op_encoding[c]; + case '>': + ret = O_right_shift; + break; + case '=': + ret = O_ge; + break; + } + *num_chars = 2; + return ret; + + case '!': + 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]; + } + + case '|': + if (input_line_pointer[1] != '|') + return op_encoding[c]; + + *num_chars = 2; + return O_logical_or; + + case '&': + if (input_line_pointer[1] != '&') + return op_encoding[c]; + + *num_chars = 2; + return O_logical_and; + } + + /* NOTREACHED */ +} + +/* Implement "word-size + 1 bit" addition for + {resultP->X_extrabit:resultP->X_add_number} + {rhs_highbit:amount}. This + is used so that the full range of unsigned word values and the full range of + signed word values can be represented in an O_constant expression, which is + useful e.g. for .sleb128 directives. */ + +void +add_to_result (expressionS *resultP, offsetT amount, int rhs_highbit) +{ + valueT ures = resultP->X_add_number; + valueT uamount = amount; + + resultP->X_add_number += amount; + + resultP->X_extrabit ^= rhs_highbit; + + if (ures + uamount < ures) + resultP->X_extrabit ^= 1; +} + +/* Similarly, for subtraction. */ + +void +subtract_from_result (expressionS *resultP, offsetT amount, int rhs_highbit) +{ + valueT ures = resultP->X_add_number; + valueT uamount = amount; + + resultP->X_add_number -= amount; + + resultP->X_extrabit ^= rhs_highbit; + + if (ures < uamount) + resultP->X_extrabit ^= 1; +} + +/* Parse an expression. */ + +segT +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; expressionS right; - register operatorT op_left; - register char c_left; /* 1st operator character. */ - register operatorT op_right; - register char c_right; - - know (rank >= 0); - (void) operand (resultP); - know (*input_line_pointer != ' '); /* Operand() gobbles spaces. */ - c_left = *input_line_pointer; /* Potential operator character. */ - op_left = op_encoding[c_left]; + operatorT op_left; + operatorT op_right; + int op_chars; + + know (rankarg >= 0); + + /* Save the value of dot for the fixup code. */ + if (rank == 0) + { + dot_value = frag_now_fix (); + dot_frag = frag_now; + } + + retval = operand (resultP, mode); + + /* operand () gobbles spaces. */ + know (*input_line_pointer != ' '); + + op_left = operatorf (&op_chars); while (op_left != O_illegal && op_rank[(int) op_left] > rank) { - input_line_pointer++; /*->after 1st character of operator. */ - /* Operators "<<" and ">>" have 2 characters. */ - if (*input_line_pointer == c_left && (c_left == '<' || c_left == '>')) + segT rightseg; + offsetT frag_off; + + input_line_pointer += op_chars; /* -> after operator. */ + + right.X_md = 0; + rightseg = expr (op_rank[(int) op_left], &right, mode); + if (right.X_op == O_absent) { - input_line_pointer++; - } /*->after operator. */ - if (SEG_ABSENT == expr (op_rank[(int) op_left], &right)) + as_warn (_("missing operand; zero assumed")); + right.X_op = O_constant; + right.X_add_number = 0; + right.X_add_symbol = NULL; + right.X_op_symbol = NULL; + } + + know (*input_line_pointer != ' '); + + if (op_left == O_index) + { + if (*input_line_pointer != ']') + as_bad ("missing right bracket"); + else + { + ++input_line_pointer; + SKIP_WHITESPACE (); + } + } + + op_right = operatorf (&op_chars); + + know (op_right == O_illegal || op_left == O_index + || op_rank[(int) op_right] <= op_rank[(int) op_left]); + know ((int) op_left >= (int) O_multiply); +#ifndef md_operator + know ((int) op_left <= (int) O_index); +#else + know ((int) op_left < (int) O_max); +#endif + + /* input_line_pointer->after right-hand quantity. */ + /* left-hand quantity in resultP. */ + /* right-hand quantity in right. */ + /* operator in op_left. */ + + if (resultP->X_op == O_big) { - as_warn ("Missing operand value assumed absolute 0."); + if (resultP->X_add_number > 0) + as_warn (_("left operand is a bignum; integer 0 assumed")); + else + as_warn (_("left operand is a float; integer 0 assumed")); + resultP->X_op = O_constant; resultP->X_add_number = 0; - resultP->X_subtract_symbol = NULL; resultP->X_add_symbol = NULL; - resultP->X_seg = SEG_ABSOLUTE; + resultP->X_op_symbol = NULL; } - know (*input_line_pointer != ' '); - c_right = *input_line_pointer; - op_right = op_encoding[c_right]; - if (*input_line_pointer == c_right && (c_right == '<' || c_right == '>')) + if (right.X_op == O_big) { - input_line_pointer++; - } /*->after operator. */ - know ((int) op_right == 0 || op_rank[(int) op_right] <= op_rank[(int) op_left]); - /* input_line_pointer->after right-hand quantity. */ - /* left-hand quantity in resultP */ - /* right-hand quantity in right. */ - /* operator in op_left. */ - if (resultP->X_seg == SEG_PASS1 || right.X_seg == SEG_PASS1) + if (right.X_add_number > 0) + as_warn (_("right operand is a bignum; integer 0 assumed")); + else + as_warn (_("right operand is a float; integer 0 assumed")); + right.X_op = O_constant; + right.X_add_number = 0; + right.X_add_symbol = NULL; + right.X_op_symbol = NULL; + } + + /* Optimize common cases. */ +#ifdef md_optimize_expr + if (md_optimize_expr (resultP, op_left, &right)) { - resultP->X_seg = SEG_PASS1; + /* Skip. */ + ; } else +#endif +#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. */ + add_to_result (resultP, right.X_add_number, right.X_extrabit); + } + /* This case comes up in PIC code. */ + else if (op_left == O_subtract + && right.X_op == O_symbol + && resultP->X_op == O_symbol + && retval == rightseg +#ifdef md_allow_local_subtract + && md_allow_local_subtract (resultP, & right, rightseg) +#endif + && ((SEG_NORMAL (rightseg) + && !S_FORCE_RELOC (resultP->X_add_symbol, 0) + && !S_FORCE_RELOC (right.X_add_symbol, 0)) + || 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)) + { + offsetT symval_diff = S_GET_VALUE (resultP->X_add_symbol) + - S_GET_VALUE (right.X_add_symbol); + subtract_from_result (resultP, right.X_add_number, right.X_extrabit); + subtract_from_result (resultP, frag_off / OCTETS_PER_BYTE, 0); + add_to_result (resultP, symval_diff, symval_diff < 0); + resultP->X_op = O_constant; + resultP->X_add_symbol = 0; + } + else if (op_left == O_subtract && right.X_op == O_constant + && (md_register_arithmetic || resultP->X_op != O_register)) { - if (resultP->X_seg == SEG_BIG) + /* X - constant. */ + subtract_from_result (resultP, right.X_add_number, right.X_extrabit); + } + 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; + resultP->X_add_symbol = right.X_add_symbol; + resultP->X_op_symbol = right.X_op_symbol; + add_to_result (resultP, right.X_add_number, right.X_extrabit); + retval = rightseg; + } + else if (resultP->X_op == O_constant && right.X_op == O_constant) + { + /* Constant OP constant. */ + offsetT v = right.X_add_number; + if (v == 0 && (op_left == O_divide || op_left == O_modulus)) { - as_warn ("Left operand of %c is a %s. Integer 0 assumed.", - c_left, resultP->X_add_number > 0 ? "bignum" : "float"); - resultP->X_seg = SEG_ABSOLUTE; - resultP->X_add_symbol = 0; - resultP->X_subtract_symbol = 0; - resultP->X_add_number = 0; + as_warn (_("division by zero")); + v = 1; } - if (right.X_seg == SEG_BIG) + if ((valueT) v >= sizeof(valueT) * CHAR_BIT + && (op_left == O_left_shift || op_left == O_right_shift)) { - as_warn ("Right operand of %c is a %s. Integer 0 assumed.", - c_left, right.X_add_number > 0 ? "bignum" : "float"); - right.X_seg = SEG_ABSOLUTE; - right.X_add_symbol = 0; - right.X_subtract_symbol = 0; - right.X_add_number = 0; + as_warn_value_out_of_range (_("shift count"), v, 0, + sizeof(valueT) * CHAR_BIT - 1, + NULL, 0); + resultP->X_add_number = v = 0; } - if (op_left == O_subtract) + switch (op_left) { - /* - * Convert - into + by exchanging symbolS and negating number. - * I know -infinity can't be negated in 2's complement: - * but then it can't be subtracted either. This trick - * does not cause any further inaccuracy. - */ - - register symbolS *symbolP; - - right.X_add_number = -right.X_add_number; - symbolP = right.X_add_symbol; - right.X_add_symbol = right.X_subtract_symbol; - right.X_subtract_symbol = symbolP; - if (symbolP) - { - right.X_seg = SEG_DIFFERENCE; - } - op_left = O_add; + default: goto general; + case O_multiply: resultP->X_add_number *= v; break; + case O_divide: resultP->X_add_number /= v; break; + case O_modulus: resultP->X_add_number %= v; break; + 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. */ + resultP->X_add_number = + (offsetT) ((valueT) resultP->X_add_number >> (valueT) v); + break; + case O_bit_inclusive_or: resultP->X_add_number |= v; break; + 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; + /* Constant + constant (O_add) is handled by the + previous if statement for constant + X, so is omitted + here. */ + case O_subtract: + subtract_from_result (resultP, v, 0); + break; + case O_eq: + resultP->X_add_number = + resultP->X_add_number == v ? ~ (offsetT) 0 : 0; + break; + case O_ne: + resultP->X_add_number = + resultP->X_add_number != v ? ~ (offsetT) 0 : 0; + break; + case O_lt: + resultP->X_add_number = + resultP->X_add_number < v ? ~ (offsetT) 0 : 0; + break; + case O_le: + resultP->X_add_number = + resultP->X_add_number <= v ? ~ (offsetT) 0 : 0; + break; + case O_ge: + resultP->X_add_number = + resultP->X_add_number >= v ? ~ (offsetT) 0 : 0; + break; + case O_gt: + resultP->X_add_number = + resultP->X_add_number > v ? ~ (offsetT) 0 : 0; + break; + case O_logical_and: + resultP->X_add_number = resultP->X_add_number && v; + break; + case O_logical_or: + resultP->X_add_number = resultP->X_add_number || v; + break; } - + } + else if (resultP->X_op == O_symbol + && right.X_op == O_symbol + && (op_left == O_add + || op_left == O_subtract + || (resultP->X_add_number == 0 + && right.X_add_number == 0))) + { + /* Symbol OP symbol. */ + resultP->X_op = op_left; + resultP->X_op_symbol = right.X_add_symbol; if (op_left == O_add) + add_to_result (resultP, right.X_add_number, right.X_extrabit); + else if (op_left == O_subtract) { - segT seg1; - segT seg2; -#ifndef MANY_SEGMENTS - - know (resultP->X_seg == SEG_DATA || resultP->X_seg == SEG_TEXT || resultP->X_seg == SEG_BSS || resultP->X_seg == SEG_UNKNOWN || resultP->X_seg == SEG_DIFFERENCE || resultP->X_seg == SEG_ABSOLUTE || resultP->X_seg == SEG_PASS1 || resultP->X_seg == SEG_REGISTER); - - know (right.X_seg == SEG_DATA || right.X_seg == SEG_TEXT || right.X_seg == SEG_BSS || right.X_seg == SEG_UNKNOWN || right.X_seg == SEG_DIFFERENCE || right.X_seg == SEG_ABSOLUTE || right.X_seg == SEG_PASS1); -#endif - clean_up_expression (&right); - clean_up_expression (resultP); - - seg1 = expr_part (&resultP->X_add_symbol, right.X_add_symbol); - seg2 = expr_part (&resultP->X_subtract_symbol, right.X_subtract_symbol); - if (seg1 == SEG_PASS1 || seg2 == SEG_PASS1) + subtract_from_result (resultP, right.X_add_number, + right.X_extrabit); + if (retval == rightseg + && SEG_NORMAL (retval) + && !S_FORCE_RELOC (resultP->X_add_symbol, 0) + && !S_FORCE_RELOC (right.X_add_symbol, 0)) { - need_pass_2 = 1; - resultP->X_seg = SEG_PASS1; + retval = absolute_section; + rightseg = absolute_section; } - else if (seg2 == SEG_ABSOLUTE) - resultP->X_seg = seg1; - else if (seg1 != SEG_UNKNOWN - && seg1 != SEG_ABSOLUTE - && seg2 != SEG_UNKNOWN - && seg1 != seg2) - { - know (seg2 != SEG_ABSOLUTE); - know (resultP->X_subtract_symbol); -#ifndef MANY_SEGMENTS - know (seg1 == SEG_TEXT || seg1 == SEG_DATA || seg1 == SEG_BSS); - know (seg2 == SEG_TEXT || seg2 == SEG_DATA || seg2 == SEG_BSS); + } + } + else + { + general: + /* The general case. */ + resultP->X_add_symbol = make_expr_symbol (resultP); + resultP->X_op_symbol = make_expr_symbol (&right); + resultP->X_op = op_left; + resultP->X_add_number = 0; + resultP->X_unsigned = 1; + resultP->X_extrabit = 0; + } + + if (retval != rightseg) + { + if (retval == undefined_section) + ; + else if (rightseg == undefined_section) + retval = rightseg; + else if (retval == expr_section) + ; + else if (rightseg == expr_section) + retval = rightseg; + else if (retval == reg_section) + ; + else if (rightseg == reg_section) + retval = rightseg; + else if (rightseg == absolute_section) + ; + else if (retval == absolute_section) + retval = rightseg; +#ifdef DIFF_EXPR_OK + else if (op_left == O_subtract) + ; #endif - know (resultP->X_add_symbol); - know (resultP->X_subtract_symbol); - as_bad ("Expression too complex: forgetting %s - %s", - S_GET_NAME (resultP->X_add_symbol), - S_GET_NAME (resultP->X_subtract_symbol)); - resultP->X_seg = SEG_ABSOLUTE; - /* Clean_up_expression() will do the rest. */ - } - else - resultP->X_seg = SEG_DIFFERENCE; + else + as_bad (_("operation combines symbols in different segments")); + } + + op_left = op_right; + } /* While next operator is >= this rank. */ + + /* The PA port needs this information. */ + if (resultP->X_add_symbol) + symbol_mark_used (resultP->X_add_symbol); - resultP->X_add_number += right.X_add_number; - clean_up_expression (resultP); + 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 *orig_add_symbol = 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; + offsetT 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 - { /* Not +. */ - if (resultP->X_seg == SEG_UNKNOWN || right.X_seg == SEG_UNKNOWN) - { - resultP->X_seg = SEG_PASS1; - need_pass_2 = 1; - } - else + else if (seg_left == absolute_section) + { + final_val += left; + left = right; + seg_left = seg_right; + add_symbol = op_symbol; + orig_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. + 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) { - resultP->X_subtract_symbol = NULL; - resultP->X_add_symbol = NULL; - /* Will be SEG_ABSOLUTE. */ - if (resultP->X_seg != SEG_ABSOLUTE || right.X_seg != SEG_ABSOLUTE) + if (!(seg_right == absolute_section && right == 0)) { - as_bad ("Relocation error. Absolute 0 assumed."); - resultP->X_seg = SEG_ABSOLUTE; - resultP->X_add_number = 0; + seg_left = seg_right; + left = right; + add_symbol = op_symbol; + orig_add_symbol = expressionP->X_op_symbol; } - else + op = O_symbol; + break; + } + else if (op == O_left_shift || op == O_right_shift) + { + if (!(seg_left == absolute_section && left == 0)) { - switch (op_left) - { - case O_bit_inclusive_or: - resultP->X_add_number |= right.X_add_number; - break; - - case O_modulus: - if (right.X_add_number) - { - resultP->X_add_number %= right.X_add_number; - } - else - { - as_warn ("Division by 0. 0 assumed."); - resultP->X_add_number = 0; - } - break; - - case O_bit_and: - resultP->X_add_number &= right.X_add_number; - break; - - case O_multiply: - resultP->X_add_number *= right.X_add_number; - break; - - case O_divide: - if (right.X_add_number) - { - resultP->X_add_number /= right.X_add_number; - } - else - { - as_warn ("Division by 0. 0 assumed."); - resultP->X_add_number = 0; - } - break; - - case O_left_shift: - resultP->X_add_number <<= right.X_add_number; - break; - - case O_right_shift: - resultP->X_add_number >>= right.X_add_number; - break; - - case O_bit_exclusive_or: - resultP->X_add_number ^= right.X_add_number; - break; - - case O_bit_or_not: - resultP->X_add_number |= ~right.X_add_number; - break; - - default: - BAD_CASE (op_left); - break; - } /* switch(operator) */ + op = O_symbol; + break; } - } /* If we have to force need_pass_2. */ - } /* If operator was +. */ - } /* If we didn't set need_pass_2. */ - op_left = op_right; - } /* While next operator is >= this rank. */ - return (resultP->X_seg); + } + 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; + orig_add_symbol = expressionP->X_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 (!symbol_same_p (add_symbol, orig_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; } -/* - * get_symbol_end() - * - * 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 - * here lessens the crowd at read.c. - * - * Assume input_line_pointer is at start of symbol name. - * Advance input_line_pointer past symbol name. - * Turn that character into a '\0', returning its former value. - * This allows a string compare (RMS wants symbol names to be strings) - * of the symbol name. - * There will always be a char following symbol name, because all good - * lines end in end-of-line. - */ +/* 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 + here lessens the crowd at read.c. + + Assume input_line_pointer is at start of symbol name, or the + start of a double quote enclosed symbol name. + Advance input_line_pointer past symbol name. + Turn that character into a '\0', returning its former value, + which may be the closing double quote. + This allows a string compare (RMS wants symbol names to be strings) + of the symbol name. + There will always be a char following symbol name, because all good + lines end in end-of-line. */ + char -get_symbol_end () +get_symbol_name (char ** ilp_return) { - register char c; + char c; - while (is_part_of_name (c = *input_line_pointer++)) - ; + * ilp_return = input_line_pointer; + /* We accept \001 in a name in case this is being called with a + constructed string. */ + if (is_name_beginner (c = *input_line_pointer++) || c == '\001') + { + while (is_part_of_name (c = *input_line_pointer++) + || c == '\001') + ; + if (is_name_ender (c)) + c = *input_line_pointer++; + } + else if (c == '"') + { + bfd_boolean backslash_seen; + + * ilp_return = input_line_pointer; + do + { + backslash_seen = c == '\\'; + c = * input_line_pointer ++; + } + while (c != 0 && (c != '"' || backslash_seen)); + + if (c == 0) + as_warn (_("missing closing '\"'")); + } *--input_line_pointer = 0; - return (c); + return c; } +/* Replace the NUL character pointed to by input_line_pointer + with C. If C is \" then advance past it. Return the character + now pointed to by input_line_pointer. */ -unsigned int -get_single_number () +char +restore_line_pointer (char c) +{ + * input_line_pointer = c; + if (c == '"') + c = * ++ input_line_pointer; + return c; +} + +unsigned int +get_single_number (void) { expressionS exp; - operand (&exp); + operand (&exp, expr_normal); return exp.X_add_number; - } - -/* - * Local Variables: - * comment-column: 0 - * fill-column: 131 - * End: - */ - -/* end of expr.c */