daily update
[external/binutils.git] / gas / expr.c
index 3ae1bcc..4517bc6 100644 (file)
@@ -1,13 +1,13 @@
 /* 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, 2007, 2009
    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,
    (It also gives smaller files to re-compile.)
    Here, "operand"s are of expressions, not instructions.  */
 
-#include <string.h>
 #define min(a, b)       ((a) < (b) ? (a) : (b))
 
 #include "as.h"
 #include "safe-ctype.h"
 #include "obstack.h"
 
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#ifndef CHAR_BIT
+#define CHAR_BIT 8
+#endif
+
 static void floating_constant (expressionS * expressionP);
 static valueT generic_bignum_to_int32 (void);
 #ifdef BFD64
@@ -42,7 +48,7 @@ static void mri_char_constant (expressionS *);
 static void current_location (expressionS *);
 static void clean_up_expression (expressionS * expressionP);
 static segT operand (expressionS *, enum expr_mode);
-static operatorT operator (int *);
+static operatorT operatorf (int *);
 
 extern const char EXP_CHARS[], FLT_CHARS[];
 
@@ -96,7 +102,9 @@ make_expr_symbol (expressionS *expressionP)
   symbolP = symbol_create (FAKE_LABEL_NAME,
                           (expressionP->X_op == O_constant
                            ? absolute_section
-                           : expr_section),
+                           : expressionP->X_op == O_register
+                             ? reg_section
+                             : expr_section),
                           0, &zero_address_frag);
   symbol_set_value_expression (symbolP, expressionP);
 
@@ -403,7 +411,7 @@ integer_constant (int radix, expressionS *expressionP)
       if (num_little_digits > SIZE_OF_LARGE_NUMBER - 1)
        num_little_digits = SIZE_OF_LARGE_NUMBER - 1;
 
-      assert (num_little_digits >= 4);
+      gas_assert (num_little_digits >= 4);
 
       if (num_little_digits != 8)
        as_bad (_("a bignum with underscores must have exactly 4 words"));
@@ -942,10 +950,15 @@ operand (expressionS *expressionP, enum expr_mode mode)
 
       break;
 
-    case '(':
 #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.  */
       if (mode != expr_defer)
        segment = expression (expressionP);
@@ -1003,11 +1016,9 @@ operand (expressionS *expressionP, enum expr_mode mode)
     case '-':
     case '+':
       {
-       /* Do not accept ++e or --e as +(+e) or -(-e)
-          Disabled, since the preprocessor removes whitespace.  */
-       if (0 && (c == '-' || c == '+') && *input_line_pointer == c)
-         goto target_op;
-       
+#ifdef md_operator
+      unary:
+#endif
        operand (expressionP, mode);
        if (expressionP->X_op == O_constant)
          {
@@ -1204,7 +1215,7 @@ operand (expressionS *expressionP, enum expr_mode mode)
 #endif
 
     default:
-#ifdef TC_M68K
+#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.  */
@@ -1215,6 +1226,43 @@ operand (expressionS *expressionP, enum expr_mode mode)
          name = --input_line_pointer;
          c = get_symbol_end ();
 
+#ifdef md_operator
+         {
+           operatorT op = md_operator (name, 1, &c);
+
+           switch (op)
+             {
+             case O_uminus:
+               *input_line_pointer = c;
+               c = '-';
+               goto unary;
+             case O_bit_not:
+               *input_line_pointer = c;
+               c = '~';
+               goto unary;
+             case O_logical_not:
+               *input_line_pointer = c;
+               c = '!';
+               goto unary;
+             case O_illegal:
+               as_bad (_("invalid use of operator \"%s\""), name);
+               break;
+             default:
+               break;
+             }
+           if (op != O_absent && op != O_illegal)
+             {
+               *input_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
+
 #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
@@ -1291,7 +1339,6 @@ operand (expressionS *expressionP, enum expr_mode mode)
        }
       else
        {
-       target_op:
          /* 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
@@ -1439,7 +1486,7 @@ static const operatorT op_encoding[256] = {
    7   * / % << >>
    8   unary - unary ~
 */
-static operator_rankT op_rank[] = {
+static operator_rankT op_rank[O_max] = {
   0,   /* O_illegal */
   0,   /* O_absent */
   0,   /* O_constant */
@@ -1470,22 +1517,6 @@ static operator_rankT op_rank[] = {
   3,   /* O_logical_and */
   2,   /* O_logical_or */
   1,   /* O_index */
-  0,   /* O_md1 */
-  0,   /* O_md2 */
-  0,   /* O_md3 */
-  0,   /* O_md4 */
-  0,   /* O_md5 */
-  0,   /* O_md6 */
-  0,   /* O_md7 */
-  0,   /* O_md8 */
-  0,   /* O_md9 */
-  0,   /* O_md10 */
-  0,   /* O_md11 */
-  0,   /* O_md12 */
-  0,   /* O_md13 */
-  0,   /* O_md14 */
-  0,   /* O_md15 */
-  0,   /* O_md16 */
 };
 
 /* Unfortunately, in MRI mode for the m68k, multiplication and
@@ -1514,6 +1545,13 @@ expr_set_precedence (void)
     }
 }
 
+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
@@ -1525,7 +1563,7 @@ expr_begin (void)
   {
     expressionS e;
     e.X_op = O_max;
-    assert (e.X_op == O_max);
+    gas_assert (e.X_op == O_max);
   }
 }
 \f
@@ -1534,7 +1572,7 @@ expr_begin (void)
    Does not advance INPUT_LINE_POINTER.  */
 
 static inline operatorT
-operator (int *num_chars)
+operatorf (int *num_chars)
 {
   int c;
   operatorT ret;
@@ -1545,18 +1583,54 @@ operator (int *num_chars)
   if (is_end_of_line[c])
     return O_illegal;
 
+#ifdef md_operator
+  if (is_name_beginner (c))
+    {
+      char *name = input_line_pointer;
+      char ec = get_symbol_end ();
+
+      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:
-      return op_encoding[c];
+      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 '-':
-      /* Do not allow a++b and a--b to be a + (+b) and a - (-b)
-        Disabled, since the preprocessor removes whitespace.  */
-      if (1 || input_line_pointer[1] != c)
-       return op_encoding[c];
-      return O_illegal;
+      return op_encoding[c];
 
     case '<':
       switch (input_line_pointer[1])
@@ -1647,7 +1721,7 @@ expr (int rankarg,                /* Larger # is higher rank.  */
   operatorT op_right;
   int op_chars;
 
-  know (rank >= 0);
+  know (rankarg >= 0);
 
   /* Save the value of dot for the fixup code.  */
   if (rank == 0)
@@ -1658,13 +1732,15 @@ expr (int rankarg,              /* Larger # is higher rank.  */
   /* operand () gobbles spaces.  */
   know (*input_line_pointer != ' ');
 
-  op_left = operator (&op_chars);
+  op_left = operatorf (&op_chars);
   while (op_left != O_illegal && op_rank[(int) op_left] > rank)
     {
       segT rightseg;
+      bfd_vma 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)
        {
@@ -1688,12 +1764,16 @@ expr (int rankarg,              /* Larger # is higher rank.  */
            }
        }
 
-      op_right = operator (&op_chars);
+      op_right = operatorf (&op_chars);
 
-      know (op_right == O_illegal
+      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
-           && (int) op_left <= (int) O_index);
+      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.  */
@@ -1732,7 +1812,11 @@ expr (int rankarg,               /* Larger # is higher rank.  */
        }
       else
 #endif
-      if (op_left == O_add && right.X_op == O_constant)
+#ifndef md_register_arithmetic
+# define md_register_arithmetic 1
+#endif
+      if (op_left == O_add && right.X_op == O_constant
+         && (md_register_arithmetic || resultP->X_op != O_register))
        {
          /* X + constant.  */
          resultP->X_add_number += right.X_add_number;
@@ -1741,23 +1825,31 @@ 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
+#ifdef md_allow_local_subtract
+              && md_allow_local_subtract (resultP, & right, rightseg)
+#endif
               && (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;
          resultP->X_add_symbol = 0;
        }
-      else if (op_left == O_subtract && right.X_op == O_constant)
+      else if (op_left == O_subtract && right.X_op == O_constant
+              && (md_register_arithmetic || resultP->X_op != O_register))
        {
          /* X - constant.  */
          resultP->X_add_number -= right.X_add_number;
        }
-      else if (op_left == O_add && resultP->X_op == O_constant)
+      else if (op_left == O_add && resultP->X_op == O_constant
+              && (md_register_arithmetic || right.X_op != O_register))
        {
          /* Constant + X.  */
          resultP->X_op = right.X_op;
@@ -1775,9 +1867,17 @@ expr (int rankarg,               /* Larger # is higher rank.  */
              as_warn (_("division by zero"));
              v = 1;
            }
+         if ((valueT) v >= sizeof(valueT) * CHAR_BIT
+             && (op_left == O_left_shift || op_left == O_right_shift))
+           {
+             as_warn_value_out_of_range (_("shift count"), v, 0,
+                                         sizeof(valueT) * CHAR_BIT - 1,
+                                         NULL, 0);
+             resultP->X_add_number = v = 0;
+           }
          switch (op_left)
            {
-           default:                    abort ();
+           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;
@@ -1792,7 +1892,9 @@ expr (int rankarg,                /* Larger # is higher rank.  */
            case O_bit_or_not:          resultP->X_add_number |= ~v; break;
            case O_bit_exclusive_or:    resultP->X_add_number ^= v; break;
            case O_bit_and:             resultP->X_add_number &= v; break;
-           case O_add:                 resultP->X_add_number += v; break;
+             /* Constant + constant (O_add) is handled by the
+                previous if statement for constant + X, so is omitted
+                here.  */
            case O_subtract:            resultP->X_add_number -= v; break;
            case O_eq:
              resultP->X_add_number =
@@ -1850,6 +1952,7 @@ expr (int rankarg,                /* Larger # is higher rank.  */
        }
       else
        {
+        general:
          /* The general case.  */
          resultP->X_add_symbol = make_expr_symbol (resultP);
          resultP->X_op_symbol = make_expr_symbol (&right);
@@ -1895,11 +1998,13 @@ 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;
+  bfd_vma frag_off;
 
   switch (op)
     {
@@ -1975,6 +2080,7 @@ resolve_expression (expressionS *expressionP)
              left = right;
              seg_left = seg_right;
              add_symbol = op_symbol;
+             orig_add_symbol = expressionP->X_op_symbol;
              op = O_symbol;
              break;
            }
@@ -2002,13 +2108,15 @@ resolve_expression (expressionS *expressionP)
         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_left == frag_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)))
        {
@@ -2017,18 +2125,19 @@ resolve_expression (expressionS *expressionP)
            {
              if (op == O_bit_exclusive_or || op == O_bit_inclusive_or)
                {
-                 if (seg_right != absolute_section || right != 0)
+                 if (!(seg_right == absolute_section && right == 0))
                    {
                      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_left_shift || op == O_right_shift)
                {
-                 if (seg_left != absolute_section || left != 0)
+                 if (!(seg_left == absolute_section && left == 0))
                    {
                      op = O_symbol;
                      break;
@@ -2044,6 +2153,7 @@ resolve_expression (expressionS *expressionP)
              seg_left = seg_right;
              left = right;
              add_symbol = op_symbol;
+             orig_add_symbol = expressionP->X_op_symbol;
              op = O_symbol;
              break;
            }
@@ -2053,11 +2163,11 @@ resolve_expression (expressionS *expressionP)
              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)))
+         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)
            {
@@ -2068,6 +2178,7 @@ resolve_expression (expressionS *expressionP)
            return 0;
        }
 
+      right += frag_off / OCTETS_PER_BYTE;
       switch (op)
        {
        case O_add:                     left += right; break;
@@ -2127,7 +2238,7 @@ resolve_expression (expressionS *expressionP)
        op = O_constant;
       else if (seg_left == reg_section && final_val == 0)
        op = O_register;
-      else if (add_symbol != expressionP->X_add_symbol)
+      else if (!symbol_same_p (add_symbol, orig_add_symbol))
        final_val += left;
       expressionP->X_add_symbol = add_symbol;
     }