/* expr.c -operands, expressions-
- Copyright 1987, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
- 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2009
- Free Software Foundation, Inc.
+ Copyright (C) 1987-2020 Free Software Foundation, Inc.
This file is part of GAS, the GNU Assembler.
#include "as.h"
#include "safe-ctype.h"
-#include "obstack.h"
#ifdef HAVE_LIMITS_H
#include <limits.h>
#define CHAR_BIT 8
#endif
+bfd_boolean literal_prefix_dollar_hex = FALSE;
+
static void floating_constant (expressionS * expressionP);
static valueT generic_bignum_to_int32 (void);
#ifdef BFD64
static segT operand (expressionS *, enum expr_mode);
static operatorT operatorf (int *);
-extern const char EXP_CHARS[], FLT_CHARS[];
-
/* 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;
- char *file;
+ const char *file;
unsigned int line;
};
zero.X_op = O_constant;
zero.X_add_number = 0;
zero.X_unsigned = 0;
+ zero.X_extrabit = 0;
clean_up_expression (&zero);
expressionP = &zero;
}
if (expressionP->X_op == O_constant)
resolve_symbol_value (symbolP);
- n = (struct expr_symbol_line *) xmalloc (sizeof *n);
+ n = XNEW (struct expr_symbol_line);
n->sym = symbolP;
- as_where (&n->file, &n->line);
+ n->file = as_where (&n->line);
n->next = expr_symbol_lines;
expr_symbol_lines = n;
the symbol. */
int
-expr_symbol_where (symbolS *sym, char **pfile, unsigned int *pline)
+expr_symbol_where (symbolS *sym, const char **pfile, unsigned int *pline)
{
- register struct expr_symbol_line *l;
+ struct expr_symbol_line *l;
for (l = expr_symbol_lines; l != NULL; l = l->next)
{
e.X_op = O_constant;
e.X_add_number = value;
e.X_unsigned = 1;
+ e.X_extrabit = 0;
return make_expr_symbol (&e);
}
#define valuesize 32
#endif
+ if (is_end_of_line[(unsigned char) *input_line_pointer])
+ {
+ expressionP->X_op = O_absent;
+ return;
+ }
+
if ((NUMBERS_WITH_SUFFIX || flag_m68k_mri) && radix == 0)
{
int flt = 0;
&& 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++;
+
+#ifndef tc_allow_L_suffix
+#define tc_allow_L_suffix 1
+#endif
+ /* PR 20732: Look for, and ignore, a L or LL suffix to the number. */
+ if (tc_allow_L_suffix)
+ while (c == 'L' || c == 'l')
+ c = * input_line_pointer++;
+
if (small)
{
/* Here with number, in correct radix. c is the next char.
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. */
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;
case 'b':
- if (LOCAL_LABELS_FB && ! (flag_m68k_mri || NUMBERS_WITH_SUFFIX))
+ if (LOCAL_LABELS_FB && !flag_m68k_mri
+ && input_line_pointer[1] != '0'
+ && input_line_pointer[1] != '1')
{
- /* This code used to check for '+' and '-' here, and, in
- some conditions, fall through to call
- integer_constant. However, that didn't make sense,
- as integer_constant only accepts digits. */
- /* Some of our code elsewhere does permit digits greater
- than the expected base; for consistency, do the same
- here. */
- if (input_line_pointer[1] < '0'
- || input_line_pointer[1] > '9')
- {
- /* Parse this as a back reference to label 0. */
- input_line_pointer--;
- integer_constant (10, expressionP);
- break;
- }
- /* Otherwise, parse this as a binary number. */
+ /* Parse this as a back reference to label 0. */
+ input_line_pointer--;
+ integer_constant (10, expressionP);
+ break;
}
+ /* Otherwise, parse this as a binary number. */
/* Fall through. */
case 'B':
- input_line_pointer++;
+ 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)
- goto default_case;
- integer_constant (2, expressionP);
- break;
+ input_line_pointer++;
+ goto default_case;
case '0':
case '1':
case 'f':
if (LOCAL_LABELS_FB)
{
+ 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 (!input_line_pointer[1]
- || (is_end_of_line[0xff & input_line_pointer[1]])
- || strchr (FLT_CHARS, 'f') == NULL)
- goto is_0f_label;
- {
- char *cp = input_line_pointer + 1;
- int r = atof_generic (&cp, ".", EXP_CHARS,
- &generic_floating_point_number);
- switch (r)
- {
- case 0:
- case ERROR_EXPONENT_OVERFLOW:
- if (*cp == 'f' || *cp == 'b')
- /* Looks like a difference expression. */
- goto is_0f_label;
- else if (cp == input_line_pointer + 1)
- /* No characters has been accepted -- looks like
- end of operand. */
- goto is_0f_label;
- else
- goto is_0f_float;
- default:
- as_fatal (_("expr.c(operand): bad atof_generic return val %d"),
- r);
- }
- }
-
- /* Okay, now we've sorted it out. We resume at one of these
- two labels, depending on what we've decided we're probably
- looking at. */
- is_0f_label:
- input_line_pointer--;
- integer_constant (10, expressionP);
- break;
-
- is_0f_float:
- /* Fall through. */
- ;
+ 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;
+ }
}
+ /* Fall through. */
case 'd':
case 'D':
if (md_need_index_operator())
goto de_fault;
# endif
- /* FALLTHROUGH */
#endif
+ /* Fall through. */
case '(':
/* Didn't begin with digit & not a name. */
- if (mode != expr_defer)
- segment = expression (expressionP);
- else
- segment = deferred_expression (expressionP);
+ segment = expr (0, expressionP, mode);
/* expression () will pass trailing whitespace. */
if ((c == '(' && *input_line_pointer != ')')
|| (c == '[' && *input_line_pointer != ']'))
- as_bad (_("missing '%c'"), c == '(' ? ')' : ']');
+ {
+ 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 ();
if (! flag_m68k_mri || *input_line_pointer != '\'')
goto de_fault;
++input_line_pointer;
- /* Fall through. */
#endif
+ /* Fall through. */
case '\'':
if (! flag_m68k_mri)
{
/* Double quote is the bitwise not operator in MRI mode. */
if (! flag_m68k_mri)
goto de_fault;
- /* Fall through. */
#endif
+ /* Fall through. */
case '~':
/* '~' is permitted to start a label on the Delta. */
if (is_name_beginner (c))
goto isname;
+ /* Fall through. */
case '!':
case '-':
case '+':
/* input_line_pointer -> char after operand. */
if (c == '-')
{
- expressionP->X_add_number = - expressionP->X_add_number;
+ 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;
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
}
break;
-#if defined (DOLLAR_DOT) || defined (TC_M68K)
+#if !defined (DOLLAR_DOT) && !defined (TC_M68K)
+ case '$':
+ if (literal_prefix_dollar_hex)
+ {
+ /* $L is the start of a local label, not a hex constant. */
+ if (* input_line_pointer == 'L')
+ goto isname;
+ integer_constant (16, expressionP);
+ }
+ else
+ {
+ goto isname;
+ }
+ break;
+#else
case '$':
/* '$' is the program counter when in MRI mode, or when
DOLLAR_DOT is defined. */
|| input_line_pointer[1] == 'T');
input_line_pointer += start ? 8 : 7;
SKIP_WHITESPACE ();
+
+ /* Cover for the as_bad () invocations below. */
+ expressionP->X_op = O_absent;
+
if (*input_line_pointer != '(')
as_bad (_("syntax error in .startof. or .sizeof."));
else
++input_line_pointer;
SKIP_WHITESPACE ();
- name = input_line_pointer;
- c = get_symbol_end ();
+ c = get_symbol_name (& name);
+ if (! *name)
+ {
+ as_bad (_("expected symbol name"));
+ (void) restore_line_pointer (c);
+ if (c != ')')
+ ignore_rest_of_line ();
+ else
+ ++input_line_pointer;
+ break;
+ }
- buf = (char *) xmalloc (strlen (name) + 10);
- if (start)
- sprintf (buf, ".startof.%s", name);
- else
- sprintf (buf, ".sizeof.%s", name);
+ buf = concat (start ? ".startof." : ".sizeof.", name,
+ (char *) NULL);
symbolP = symbol_make (buf);
free (buf);
expressionP->X_add_number = 0;
*input_line_pointer = c;
- SKIP_WHITESPACE ();
+ SKIP_WHITESPACE_AFTER_NAME ();
if (*input_line_pointer != ')')
as_bad (_("syntax error in .startof. or .sizeof."));
else
#if defined(md_need_index_operator) || defined(TC_M68K)
de_fault:
#endif
- if (is_name_beginner (c)) /* Here if did not begin with a digit. */
+ 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:
- name = --input_line_pointer;
- c = get_symbol_end ();
+ -- input_line_pointer;
+ c = get_symbol_name (&name);
#ifdef md_operator
{
switch (op)
{
case O_uminus:
- *input_line_pointer = c;
+ restore_line_pointer (c);
c = '-';
goto unary;
case O_bit_not:
- *input_line_pointer = c;
+ restore_line_pointer (c);
c = '~';
goto unary;
case O_logical_not:
- *input_line_pointer = c;
+ restore_line_pointer (c);
c = '!';
goto unary;
case O_illegal:
default:
break;
}
+
if (op != O_absent && op != O_illegal)
{
- *input_line_pointer = c;
+ restore_line_pointer (c);
expr (9, expressionP, mode);
expressionP->X_add_symbol = make_expr_symbol (expressionP);
expressionP->X_op_symbol = NULL;
entering it in the symbol table. */
if (md_parse_name (name, expressionP, mode, &c))
{
- *input_line_pointer = c;
- break;
- }
-#endif
-
-#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;
-
- start = (name[1] == 't'
- || name[1] == 'T');
-
- *input_line_pointer = c;
- SKIP_WHITESPACE ();
-
- name = input_line_pointer;
- c = get_symbol_end ();
-
- buf = (char *) xmalloc (strlen (name) + 10);
- if (start)
- sprintf (buf, ".startof.%s", name);
- else
- sprintf (buf, ".sizeof.%s", name);
- 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 ();
-
+ restore_line_pointer (c);
break;
}
#endif
expressionP->X_add_symbol = symbolP;
expressionP->X_add_number = 0;
}
- *input_line_pointer = c;
+
+ restore_line_pointer (c);
}
else
{
/* 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. */
+ SKIP_ALL_WHITESPACE (); /* -> 1st char after operand. */
know (*input_line_pointer != ' ');
/* The PA port needs this information. */
#ifdef md_operator
if (is_name_beginner (c))
{
- char *name = input_line_pointer;
- char ec = get_symbol_end ();
+ char *name;
+ char ec = get_symbol_name (& name);
ret = md_operator (name, 2, &ec);
switch (ret)
/* 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
/* Save the value of dot for the fixup code. */
if (rank == 0)
- dot_value = frag_now_fix ();
+ {
+ dot_value = frag_now_fix ();
+ dot_frag = frag_now;
+ }
retval = operand (resultP, mode);
while (op_left != O_illegal && op_rank[(int) op_left] > rank)
{
segT rightseg;
- bfd_vma frag_off;
+ offsetT frag_off;
input_line_pointer += op_chars; /* -> after operator. */
right.X_op_symbol = NULL;
}
+ if (mode == expr_defer
+ && ((resultP->X_add_symbol != NULL
+ && S_IS_FORWARD_REF (resultP->X_add_symbol))
+ || (right.X_add_symbol != NULL
+ && S_IS_FORWARD_REF (right.X_add_symbol))))
+ goto general;
+
/* Optimize common cases. */
#ifdef md_optimize_expr
if (md_optimize_expr (resultP, op_left, &right))
&& (md_register_arithmetic || resultP->X_op != O_register))
{
/* X + constant. */
- resultP->X_add_number += right.X_add_number;
+ add_to_result (resultP, right.X_add_number, right.X_extrabit);
}
/* This case comes up in PIC code. */
else if (op_left == O_subtract
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));
+ 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;
}
&& (md_register_arithmetic || resultP->X_op != O_register))
{
/* X - constant. */
- resultP->X_add_number -= right.X_add_number;
+ 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))
resultP->X_op = right.X_op;
resultP->X_add_symbol = right.X_add_symbol;
resultP->X_op_symbol = right.X_op_symbol;
- resultP->X_add_number += right.X_add_number;
+ 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 + 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_subtract:
+ subtract_from_result (resultP, v, 0);
+ break;
case O_eq:
resultP->X_add_number =
resultP->X_add_number == v ? ~ (offsetT) 0 : 0;
resultP->X_op = op_left;
resultP->X_op_symbol = right.X_add_symbol;
if (op_left == O_add)
- resultP->X_add_number += right.X_add_number;
+ add_to_result (resultP, right.X_add_number, right.X_extrabit);
else if (op_left == O_subtract)
{
- resultP->X_add_number -= right.X_add_number;
+ 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)
resultP->X_op = op_left;
resultP->X_add_number = 0;
resultP->X_unsigned = 1;
+ resultP->X_extrabit = 0;
}
if (retval != rightseg)
valueT left, right;
segT seg_left, seg_right;
fragS *frag_left, *frag_right;
- bfd_vma frag_off;
+ offsetT frag_off;
switch (op)
{
|| 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))
+ || frag_offset_fixed_p (frag_left, frag_right, &frag_off)
+ || (op == O_gt
+ && frag_gtoffset_p (left, frag_left,
+ right, frag_right, &frag_off)))
&& (seg_left != reg_section || left == right)
&& (seg_left != undefined_section || add_symbol == op_symbol)))
{
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.
+ 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.
+ 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.
+ 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 (void)
+get_symbol_name (char ** ilp_return)
{
char c;
- /* We accept \001 in a name in case this is being called with a
+ * ilp_return = input_line_pointer;
+ /* We accept FAKE_LABEL_CHAR in a name in case this is being called with a
constructed string. */
- if (is_name_beginner (c = *input_line_pointer++) || c == '\001')
+ if (is_name_beginner (c = *input_line_pointer++)
+ || (input_from_string && c == FAKE_LABEL_CHAR))
{
while (is_part_of_name (c = *input_line_pointer++)
- || c == '\001')
+ || (input_from_string && c == FAKE_LABEL_CHAR))
;
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. */
+
+char
+restore_line_pointer (char c)
+{
+ * input_line_pointer = c;
+ if (c == '"')
+ c = * ++ input_line_pointer;
+ return c;
}
unsigned int