* subsegs.c (subseg_set_rest): Always set seginfp->frchainP if NULL.
[deliverable/binutils-gdb.git] / gas / expr.c
index 7e37010949d9f2bc277465ff48665560974a8c94..69f0aaccdb64b42da016bf5ad54aa08b35c3b22f 100644 (file)
@@ -1,6 +1,6 @@
 /* expr.c -operands, expressions-
    Copyright 1987, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
-   1999, 2000, 2001, 2002, 2003, 2004, 2005
+   1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006
    Free Software Foundation, Inc.
 
    This file is part of GAS, the GNU Assembler.
@@ -41,7 +41,7 @@ static void integer_constant (int radix, expressionS * expressionP);
 static void mri_char_constant (expressionS *);
 static void current_location (expressionS *);
 static void clean_up_expression (expressionS * expressionP);
-static segT operand (expressionS *);
+static segT operand (expressionS *, enum expr_mode);
 static operatorT operator (int *);
 
 extern const char EXP_CHARS[], FLT_CHARS[];
@@ -301,7 +301,10 @@ integer_constant (int radix, expressionS *expressionP)
        {
          c = *--suffix;
          c = TOUPPER (c);
-         if (c == 'B')
+         /* 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;
@@ -708,7 +711,7 @@ current_location (expressionS *expressionp)
        Input_line_pointer->(next non-blank) char after operand.  */
 
 static segT
-operand (expressionS *expressionP)
+operand (expressionS *expressionP, enum expr_mode mode)
 {
   char c;
   symbolS *symbolP;    /* Points to symbol.  */
@@ -944,7 +947,10 @@ operand (expressionS *expressionP)
     case '[':
 #endif
       /* Didn't begin with digit & not a name.  */
-      segment = expression (expressionP);
+      if (mode != expr_defer)
+       segment = expression (expressionP);
+      else
+       segment = deferred_expression (expressionP);
       /* expression () will pass trailing whitespace.  */
       if ((c == '(' && *input_line_pointer != ')')
          || (c == '[' && *input_line_pointer != ']'))
@@ -1002,7 +1008,7 @@ operand (expressionS *expressionP)
        if (0 && (c == '-' || c == '+') && *input_line_pointer == c)
          goto target_op;
        
-       operand (expressionP);
+       operand (expressionP, mode);
        if (expressionP->X_op == O_constant)
          {
            /* input_line_pointer -> char after operand.  */
@@ -1089,10 +1095,10 @@ operand (expressionS *expressionP)
       if (! flag_m68k_mri)
        goto de_fault;
 #endif
-      if (flag_m68k_mri && hex_p (*input_line_pointer))
+      if (DOLLAR_AMBIGU && hex_p (*input_line_pointer))
        {
-         /* In MRI mode, '$' is also used as the prefix for a
-            hexadecimal constant.  */
+         /* In MRI mode and on Z80, '$' is also used as the prefix
+            for a hexadecimal constant.  */
          integer_constant (16, expressionP);
          break;
        }
@@ -1214,7 +1220,7 @@ operand (expressionS *expressionP)
             specially in certain contexts.  If a name always has a
             specific value, it can often be handled by simply
             entering it in the symbol table.  */
-         if (md_parse_name (name, expressionP, &c))
+         if (md_parse_name (name, expressionP, mode, &c))
            {
              *input_line_pointer = c;
              break;
@@ -1265,12 +1271,12 @@ operand (expressionS *expressionP)
          /* If we have an absolute symbol or a reg, then we know its
             value now.  */
          segment = S_GET_SEGMENT (symbolP);
-         if (segment == absolute_section)
+         if (mode != expr_defer && segment == absolute_section)
            {
              expressionP->X_op = O_constant;
              expressionP->X_add_number = S_GET_VALUE (symbolP);
            }
-         else if (segment == reg_section)
+         else if (mode != expr_defer && segment == reg_section)
            {
              expressionP->X_op = O_register;
              expressionP->X_add_number = S_GET_VALUE (symbolP);
@@ -1314,6 +1320,9 @@ operand (expressionS *expressionP)
   if (expressionP->X_add_symbol)
     symbol_mark_used (expressionP->X_add_symbol);
 
+  expressionP->X_add_symbol = symbol_clone_if_forward_ref (expressionP->X_add_symbol);
+  expressionP->X_op_symbol = symbol_clone_if_forward_ref (expressionP->X_op_symbol);
+
   switch (expressionP->X_op)
     {
     default:
@@ -1381,6 +1390,9 @@ clean_up_expression (expressionS *expressionP)
 
 #undef __
 #define __ O_illegal
+#ifndef O_SINGLE_EQ
+#define O_SINGLE_EQ O_illegal
+#endif
 
 /* Maps ASCII -> operators.  */
 static const operatorT op_encoding[256] = {
@@ -1390,7 +1402,7 @@ static const operatorT op_encoding[256] = {
   __, O_bit_or_not, __, __, __, O_modulus, O_bit_and, __,
   __, __, O_multiply, O_add, __, O_subtract, __, O_divide,
   __, __, __, __, __, __, __, __,
-  __, __, __, __, O_lt, __, O_gt, __,
+  __, __, __, __, O_lt, O_SINGLE_EQ, O_gt, __,
   __, __, __, __, __, __, __, __,
   __, __, __, __, __, __, __, __,
   __, __, __, __, __, __, __, __,
@@ -1587,15 +1599,21 @@ operator (int *num_chars)
       return ret;
 
     case '!':
-      /* We accept !! as equivalent to ^ for MRI compatibility.  */
-      if (input_line_pointer[1] != '!')
+      switch (input_line_pointer[1])
        {
+       case '!':
+         /* We accept !! as equivalent to ^ for MRI compatibility. */
+         *num_chars = 2;
+         return O_bit_exclusive_or;
+       case '=':
+         /* We accept != as equivalent to <>.  */
+         *num_chars = 2;
+         return O_ne;
+       default:
          if (flag_m68k_mri)
            return O_bit_inclusive_or;
          return op_encoding[c];
        }
-      *num_chars = 2;
-      return O_bit_exclusive_or;
 
     case '|':
       if (input_line_pointer[1] != '|')
@@ -1619,7 +1637,8 @@ operator (int *num_chars)
 
 segT
 expr (int rankarg,             /* Larger # is higher rank.  */
-      expressionS *resultP     /* Deliver result here.  */)
+      expressionS *resultP,    /* Deliver result here.  */
+      enum expr_mode mode      /* Controls behavior.  */)
 {
   operator_rankT rank = (operator_rankT) rankarg;
   segT retval;
@@ -1634,7 +1653,7 @@ expr (int rankarg,                /* Larger # is higher rank.  */
   if (rank == 0)
     dot_value = frag_now_fix ();
 
-  retval = operand (resultP);
+  retval = operand (resultP, mode);
 
   /* operand () gobbles spaces.  */
   know (*input_line_pointer != ' ');
@@ -1643,10 +1662,11 @@ expr (int rankarg,              /* Larger # is higher rank.  */
   while (op_left != O_illegal && op_rank[(int) op_left] > rank)
     {
       segT rightseg;
+      bfd_vma frag_off;
 
       input_line_pointer += op_chars;  /* -> after operator.  */
 
-      rightseg = expr (op_rank[(int) op_left], &right);
+      rightseg = expr (op_rank[(int) op_left], &right, mode);
       if (right.X_op == O_absent)
        {
          as_warn (_("missing operand; zero assumed"));
@@ -1722,12 +1742,15 @@ expr (int rankarg,              /* Larger # is higher rank.  */
       else if (op_left == O_subtract
               && right.X_op == O_symbol
               && resultP->X_op == O_symbol
-              && (symbol_get_frag (right.X_add_symbol)
-                  == symbol_get_frag (resultP->X_add_symbol))
+              && retval == rightseg
               && (SEG_NORMAL (rightseg)
-                  || right.X_add_symbol == resultP->X_add_symbol))
+                  || right.X_add_symbol == resultP->X_add_symbol)
+              && frag_offset_fixed_p (symbol_get_frag (resultP->X_add_symbol),
+                                      symbol_get_frag (right.X_add_symbol),
+                                      &frag_off))
        {
          resultP->X_add_number -= right.X_add_number;
+         resultP->X_add_number -= frag_off / OCTETS_PER_BYTE;
          resultP->X_add_number += (S_GET_VALUE (resultP->X_add_symbol)
                                    - S_GET_VALUE (right.X_add_symbol));
          resultP->X_op = O_constant;
@@ -1861,8 +1884,269 @@ expr (int rankarg,              /* Larger # is higher rank.  */
   if (resultP->X_add_symbol)
     symbol_mark_used (resultP->X_add_symbol);
 
+  if (rank == 0 && mode == expr_evaluate)
+    resolve_expression (resultP);
+
   return resultP->X_op == O_constant ? absolute_section : retval;
 }
+
+/* Resolve an expression without changing any symbols/sub-expressions
+   used.  */
+
+int
+resolve_expression (expressionS *expressionP)
+{
+  /* Help out with CSE.  */
+  valueT final_val = expressionP->X_add_number;
+  symbolS *add_symbol = expressionP->X_add_symbol;
+  symbolS *op_symbol = expressionP->X_op_symbol;
+  operatorT op = expressionP->X_op;
+  valueT left, right;
+  segT seg_left, seg_right;
+  fragS *frag_left, *frag_right;
+  bfd_vma frag_off;
+
+  switch (op)
+    {
+    default:
+      return 0;
+
+    case O_constant:
+    case O_register:
+      left = 0;
+      break;
+
+    case O_symbol:
+    case O_symbol_rva:
+      if (!snapshot_symbol (&add_symbol, &left, &seg_left, &frag_left))
+       return 0;
+
+      break;
+
+    case O_uminus:
+    case O_bit_not:
+    case O_logical_not:
+      if (!snapshot_symbol (&add_symbol, &left, &seg_left, &frag_left))
+       return 0;
+
+      if (seg_left != absolute_section)
+       return 0;
+
+      if (op == O_logical_not)
+       left = !left;
+      else if (op == O_uminus)
+       left = -left;
+      else
+       left = ~left;
+      op = O_constant;
+      break;
+
+    case O_multiply:
+    case O_divide:
+    case O_modulus:
+    case O_left_shift:
+    case O_right_shift:
+    case O_bit_inclusive_or:
+    case O_bit_or_not:
+    case O_bit_exclusive_or:
+    case O_bit_and:
+    case O_add:
+    case O_subtract:
+    case O_eq:
+    case O_ne:
+    case O_lt:
+    case O_le:
+    case O_ge:
+    case O_gt:
+    case O_logical_and:
+    case O_logical_or:
+      if (!snapshot_symbol (&add_symbol, &left, &seg_left, &frag_left)
+         || !snapshot_symbol (&op_symbol, &right, &seg_right, &frag_right))
+       return 0;
+
+      /* Simplify addition or subtraction of a constant by folding the
+        constant into X_add_number.  */
+      if (op == O_add)
+       {
+         if (seg_right == absolute_section)
+           {
+             final_val += right;
+             op = O_symbol;
+             break;
+           }
+         else if (seg_left == absolute_section)
+           {
+             final_val += left;
+             left = right;
+             seg_left = seg_right;
+             add_symbol = op_symbol;
+             op = O_symbol;
+             break;
+           }
+       }
+      else if (op == O_subtract)
+       {
+         if (seg_right == absolute_section)
+           {
+             final_val -= right;
+             op = O_symbol;
+             break;
+           }
+       }
+
+      /* Equality and non-equality tests are permitted on anything.
+        Subtraction, and other comparison operators are permitted if
+        both operands are in the same section.
+        Shifts by constant zero are permitted on anything.
+        Multiplies, bit-ors, and bit-ands with constant zero are
+        permitted on anything.
+        Multiplies and divides by constant one are permitted on
+        anything.
+        Binary operations with both operands being the same register
+        or undefined symbol are permitted if the result doesn't depend
+        on the input value.
+        Otherwise, both operands must be absolute.  We already handled
+        the case of addition or subtraction of a constant above.  */
+      frag_off = 0;
+      if (!(seg_left == absolute_section
+              && seg_right == absolute_section)
+         && !(op == O_eq || op == O_ne)
+         && !((op == O_subtract
+               || op == O_lt || op == O_le || op == O_ge || op == O_gt)
+              && seg_left == seg_right
+              && (finalize_syms
+                  || frag_offset_fixed_p (frag_left, frag_right, &frag_off))
+              && (seg_left != reg_section || left == right)
+              && (seg_left != undefined_section || add_symbol == op_symbol)))
+       {
+         if ((seg_left == absolute_section && left == 0)
+             || (seg_right == absolute_section && right == 0))
+           {
+             if (op == O_bit_exclusive_or || op == O_bit_inclusive_or)
+               {
+                 if (seg_right != absolute_section || right != 0)
+                   {
+                     seg_left = seg_right;
+                     left = right;
+                     add_symbol = op_symbol;
+                   }
+                 op = O_symbol;
+                 break;
+               }
+             else if (op == O_left_shift || op == O_right_shift)
+               {
+                 if (seg_left != absolute_section || left != 0)
+                   {
+                     op = O_symbol;
+                     break;
+                   }
+               }
+             else if (op != O_multiply
+                      && op != O_bit_or_not && op != O_bit_and)
+               return 0;
+           }
+         else if (op == O_multiply
+                  && seg_left == absolute_section && left == 1)
+           {
+             seg_left = seg_right;
+             left = right;
+             add_symbol = op_symbol;
+             op = O_symbol;
+             break;
+           }
+         else if ((op == O_multiply || op == O_divide)
+                  && seg_right == absolute_section && right == 1)
+           {
+             op = O_symbol;
+             break;
+           }
+         else if (left != right
+                  || ((seg_left != reg_section || seg_right != reg_section)
+                      && (seg_left != undefined_section
+                          || seg_right != undefined_section
+                          || add_symbol != op_symbol)))
+           return 0;
+         else if (op == O_bit_and || op == O_bit_inclusive_or)
+           {
+             op = O_symbol;
+             break;
+           }
+         else if (op != O_bit_exclusive_or && op != O_bit_or_not)
+           return 0;
+       }
+
+      right += frag_off / OCTETS_PER_BYTE;
+      switch (op)
+       {
+       case O_add:                     left += right; break;
+       case O_subtract:                left -= right; break;
+       case O_multiply:                left *= right; break;
+       case O_divide:
+         if (right == 0)
+           return 0;
+         left = (offsetT) left / (offsetT) right;
+         break;
+       case O_modulus:
+         if (right == 0)
+           return 0;
+         left = (offsetT) left % (offsetT) right;
+         break;
+       case O_left_shift:              left <<= right; break;
+       case O_right_shift:             left >>= right; break;
+       case O_bit_inclusive_or:        left |= right; break;
+       case O_bit_or_not:              left |= ~right; break;
+       case O_bit_exclusive_or:        left ^= right; break;
+       case O_bit_and:                 left &= right; break;
+       case O_eq:
+       case O_ne:
+         left = (left == right
+                 && seg_left == seg_right
+                 && (finalize_syms || frag_left == frag_right)
+                 && (seg_left != undefined_section
+                     || add_symbol == op_symbol)
+                 ? ~ (valueT) 0 : 0);
+         if (op == O_ne)
+           left = ~left;
+         break;
+       case O_lt:
+         left = (offsetT) left <  (offsetT) right ? ~ (valueT) 0 : 0;
+         break;
+       case O_le:
+         left = (offsetT) left <= (offsetT) right ? ~ (valueT) 0 : 0;
+         break;
+       case O_ge:
+         left = (offsetT) left >= (offsetT) right ? ~ (valueT) 0 : 0;
+         break;
+       case O_gt:
+         left = (offsetT) left >  (offsetT) right ? ~ (valueT) 0 : 0;
+         break;
+       case O_logical_and:     left = left && right; break;
+       case O_logical_or:      left = left || right; break;
+       default:                abort ();
+       }
+
+      op = O_constant;
+      break;
+    }
+
+  if (op == O_symbol)
+    {
+      if (seg_left == absolute_section)
+       op = O_constant;
+      else if (seg_left == reg_section && final_val == 0)
+       op = O_register;
+      else if (add_symbol != expressionP->X_add_symbol)
+       final_val += left;
+      expressionP->X_add_symbol = add_symbol;
+    }
+  expressionP->X_op = op;
+
+  if (op == O_constant || op == O_register)
+    final_val += left;
+  expressionP->X_add_number = final_val;
+
+  return 1;
+}
 \f
 /* 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
@@ -1899,6 +2183,6 @@ unsigned int
 get_single_number (void)
 {
   expressionS exp;
-  operand (&exp);
+  operand (&exp, expr_normal);
   return exp.X_add_number;
 }
This page took 0.046288 seconds and 4 git commands to generate.