* config/tc-mips.c (my_getSmallParser): New function prototype.
authorThiemo Seufer <ths@networkno.de>
Wed, 10 Oct 2001 00:54:53 +0000 (00:54 +0000)
committerThiemo Seufer <ths@networkno.de>
Wed, 10 Oct 2001 00:54:53 +0000 (00:54 +0000)
(small_ex_type): Named this enum, more return values for
my_getSmallExpression.
(mips_ip): Allow SPC and HT between arguments. Handle some NewABI
triple relocations. Protect some parts with ifdef OBJ_ELF.
(percent_op_match): New struct, lookup table for %some_reloc().
(my_getSmallParser): New function, parses nested percent_ops also.
(my_getSmallExpression): Rewite to support nested percent_ops.

gas/ChangeLog
gas/config/tc-mips.c

index 04ea31b1d8190e7a37cab09a0dde63e94916337b..f788b9aaede53099df84081e104967bc0ed0660e 100644 (file)
@@ -1,3 +1,14 @@
+2001-10-10  Thiemo Seufer <seufer@csv.ica.uni-stuttgart.de>
+
+       * config/tc-mips.c (my_getSmallParser): New function prototype.
+       (small_ex_type): Named this enum, more return values for
+       my_getSmallExpression.
+       (mips_ip): Allow SPC and HT between arguments. Handle some NewABI
+       triple relocations. Protect some parts with ifdef OBJ_ELF.
+       (percent_op_match): New struct, lookup table for %some_reloc().
+       (my_getSmallParser): New function, parses nested percent_ops also.
+       (my_getSmallExpression): Rewite to support nested percent_ops.
+
 2001-10-10  Thiemo Seufer <seufer@csv.ica.uni-stuttgart.de>
 
        * config/tc-mips.c
index 712488ae93d79ca442d32b960fb87ed5653866d9..df2e662f5260dc0b4e00a9cfbc3827afdc2809e6 100644 (file)
@@ -675,8 +675,9 @@ static void mips16_ip PARAMS ((char *str, struct mips_cl_insn * ip));
 static void mips16_immed PARAMS ((char *, unsigned int, int, offsetT, boolean,
                                  boolean, boolean, unsigned long *,
                                  boolean *, unsigned short *));
-static int my_getSmallExpression PARAMS ((expressionS * ep, char *str));
-static void my_getExpression PARAMS ((expressionS * ep, char *str));
+static int my_getSmallParser PARAMS ((char **, unsigned int *, int *));
+static int my_getSmallExpression PARAMS ((expressionS *, char *));
+static void my_getExpression PARAMS ((expressionS *, char *));
 static int support_64bit_objects PARAMS((void));
 static symbolS *get_symbol PARAMS ((void));
 static void mips_align PARAMS ((int to, int fill, symbolS *label));
@@ -709,17 +710,30 @@ static const char *mips_cpu_to_str PARAMS ((int));
 static int validate_mips_insn PARAMS ((const struct mips_opcode *));
 static void show PARAMS ((FILE *, char *, int *, int *));
 
-/* Return values of my_getSmallExpression() */
+/* Return values of my_getSmallExpression() */
 
-enum
+enum small_ex_type
 {
   S_EX_NONE = 0,
-  S_EX_LO,
+  S_EX_REGISTER,
+
+  /* Direct relocation creation by %percent_op().  */
+  S_EX_HALF,
   S_EX_HI,
+  S_EX_LO,
+  S_EX_GP_REL,
+  S_EX_GOT,
+  S_EX_CALL16,
+  S_EX_GOT_DISP,
+  S_EX_GOT_PAGE,
+  S_EX_GOT_OFST,
+  S_EX_GOT_HI,
+  S_EX_GOT_LO,
+  S_EX_NEG,
   S_EX_HIGHER,
   S_EX_HIGHEST,
-  S_EX_GPREL,
-  S_EX_NEG
+  S_EX_CALL_HI,
+  S_EX_CALL_LO
 };
 
 /* Table and functions used to map between CPU/ISA names, and
@@ -7426,8 +7440,7 @@ mips_ip (str, ip)
       insn_error = NULL;
       for (args = insn->args;; ++args)
        {
-         if (*s == ' ')
-           ++s;
+         s += strspn (s, " \t");
          switch (*args)
            {
            case '\0':          /* end of args */
@@ -8085,10 +8098,31 @@ mips_ip (str, ip)
                      if (imm_expr.X_op == O_constant)
                        imm_expr.X_add_number =
                          (imm_expr.X_add_number >> 16) & 0xffff;
+#ifdef OBJ_ELF
                      else if (c == S_EX_HIGHEST)
                          *imm_reloc = BFD_RELOC_MIPS_HIGHEST;
                      else if (c == S_EX_HIGHER)
                          *imm_reloc = BFD_RELOC_MIPS_HIGHER;
+                     else if (c == S_EX_GP_REL)
+                       {
+                         /* This occurs in NewABI only.  */
+                         c = my_getSmallExpression (&imm_expr, s);
+                         if (c != S_EX_NEG)
+                           as_bad (_("bad composition of relocations"));
+                         else
+                           {
+                             c = my_getSmallExpression (&imm_expr, s);
+                             if (c != S_EX_LO)
+                               as_bad (_("bad composition of relocations"));
+                             else
+                               {
+                                 imm_reloc[0] = BFD_RELOC_GPREL16;
+                                 imm_reloc[1] = BFD_RELOC_MIPS_SUB;
+                                 imm_reloc[2] = BFD_RELOC_LO16;
+                               }
+                           }
+                       }
+#endif
                      else if (c == S_EX_HI)
                        {
                          *imm_reloc = BFD_RELOC_HI16_S;
@@ -8209,6 +8243,7 @@ mips_ip (str, ip)
                      if (imm_expr.X_op == O_constant)
                        imm_expr.X_add_number =
                          (imm_expr.X_add_number >> 16) & 0xffff;
+#ifdef OBJ_ELF
                      else if (c == S_EX_HIGHEST)
                          *imm_reloc = BFD_RELOC_MIPS_HIGHEST;
                      else if (c == S_EX_HI)
@@ -8216,6 +8251,26 @@ mips_ip (str, ip)
                          *imm_reloc = BFD_RELOC_HI16_S;
                          imm_unmatched_hi = true;
                        }
+                     else if (c == S_EX_GP_REL)
+                       {
+                         /* This occurs in NewABI only.  */
+                         c = my_getSmallExpression (&imm_expr, s);
+                         if (c != S_EX_NEG)
+                           as_bad (_("bad composition of relocations"));
+                         else
+                           {
+                             c = my_getSmallExpression (&imm_expr, s);
+                             if (c != S_EX_HI)
+                               as_bad (_("bad composition of relocations"));
+                             else
+                               {
+                                 imm_reloc[0] = BFD_RELOC_GPREL16;
+                                 imm_reloc[1] = BFD_RELOC_MIPS_SUB;
+                                 imm_reloc[2] = BFD_RELOC_HI16_S;
+                               }
+                           }
+                       }
+#endif
                      else
                        *imm_reloc = BFD_RELOC_HI16;
                    }
@@ -9017,151 +9072,192 @@ mips16_immed (file, line, type, val, warn, small, ext, insn, use_extend,
     }
 }
 \f
+static struct percent_op_match
+{
+   const char *str;
+   const enum small_ex_type type;
+} percent_op[] =
+{
+#ifdef OBJ_ELF
+  {"%half", S_EX_HALF},
+#endif
+  {"%hi", S_EX_HI},
+  {"%lo", S_EX_LO},
+#ifdef OBJ_ELF
+  {"%gp_rel", S_EX_GP_REL},
+  {"%got", S_EX_GOT},
+  {"%call16", S_EX_CALL16},
+  {"%got_disp", S_EX_GOT_DISP},
+  {"%got_page", S_EX_GOT_PAGE},
+  {"%got_ofst", S_EX_GOT_OFST},
+  {"%got_hi", S_EX_GOT_HI},
+  {"%got_lo", S_EX_GOT_LO},
+  {"%neg", S_EX_NEG},
+  {"%higher", S_EX_HIGHER},
+  {"%highest", S_EX_HIGHEST},
+  {"%call_hi", S_EX_CALL_HI},
+  {"%call_lo", S_EX_CALL_LO}
+#endif
+};
+
+/* Parse small expression input.  STR gets adjusted to eat up whitespace.
+   It detects valid "%percent_op(...)" and "($reg)" strings.  Percent_op's
+   can be nested, this is handled by blanking the innermost, parsing the
+   rest by subsequent calls.  */
+
+static int
+my_getSmallParser (str, len, nestlevel)
+     char **str;
+     unsigned int *len;
+     int *nestlevel;
+{
+  int type = S_EX_NONE;
+
+  *len = 0;
+  *str += strspn (*str, " \t");
+  if (**str == '(')
+    {
+      char *b = *str + 1 + strspn (*str + 1, " \t");
+      char *e;
+
+      /* Check for base register.  */
+      if (b[0] == '$')
+       {
+         if (strchr (b, ')')
+             && (e = b + strcspn (b, ") \t"))
+             && e - b > 1 && e - b < 4)
+           {
+              if ((e - b == 3
+                   && ((b[1] == 'f' && b[2] == 'p')
+                       || (b[1] == 's' && b[2] == 'p')
+                       || (b[1] == 'g' && b[2] == 'p')
+                       || (b[1] == 'a' && b[2] == 't')
+                       || (ISDIGIT (b[1])
+                           && ISDIGIT (b[2]))))
+                  || (ISDIGIT (b[1])))
+                {
+                  *len = strcspn (*str, ")") + 1;
+                  return S_EX_REGISTER;
+                }
+           }
+       }
+      else if (b[0] == '%')
+       {
+         *str = b;
+         goto percent_op;
+       }
+      else
+        /* Some other expression in the braces.  */
+        *len = strcspn (*str, ")") + 1;
+    }
+  /* Check for percent_op.  */
+  else if (*str[0] == '%')
+    {
+      char *tmp;
+      unsigned int i;
+
+percent_op:
+      tmp = *str + 1;
+      i = 0;
+
+      while (ISALPHA (*tmp) || *tmp == '_')
+       {
+         *tmp = TOLOWER (*tmp);
+         tmp++;
+       }
+      while (i < (sizeof (percent_op) / sizeof (struct percent_op_match)))
+       {
+         if (strncmp (*str, percent_op[i].str, strlen (percent_op[i].str)))
+             i++;
+         else
+           {
+             type = percent_op[i].type;
+
+             /* Only %hi and %lo are allowed for OldABI.  */
+             if (! HAVE_NEWABI && type != S_EX_HI && type != S_EX_LO)
+               return S_EX_NONE;
+
+             *len = strlen (percent_op[i].str);
+             (*nestlevel)++;
+             return type;
+           }
+       }
+    }
+
+  /* Any other expression.  */
+  return S_EX_NONE;
+}
 
 static int
 my_getSmallExpression (ep, str)
      expressionS *ep;
      char *str;
 {
-  char *sp;
-  char *oldstr = str;
+  static char *oldstr = NULL;
   int c = S_EX_NONE;
+  int oldc;
+  int nest_level = 0;
+  unsigned int len;
 
-  if (*str == ' ')
-    str++;
-  if (*str == '(')
-    c = S_EX_NONE;
-  else if (str[0] == '%'
-          && TOLOWER (str[1]) == 'l'
-          && TOLOWER (str[2]) == 'o'
-          && str[3] == '(')
-    {
-      c = S_EX_LO;
-      str += sizeof ("%lo(") - 2;
-    }
-  else if (str[0] == '%'
-          && TOLOWER (str[1]) == 'h'
-          && TOLOWER (str[2]) == 'i'
-          && str[3] == '(')
-    {
-      c = S_EX_HI;
-      str += sizeof ("%hi(") - 2;
-    }
-  else if (str[0] == '%'
-          && TOLOWER (str[1]) == 'h'
-          && TOLOWER (str[2]) == 'i'
-          && TOLOWER (str[3]) == 'g'
-          && TOLOWER (str[4]) == 'h'
-          && TOLOWER (str[5]) == 'e'
-          && TOLOWER (str[6]) == 'r'
-          && str[7] == '(')
-    {
-      c = S_EX_HIGHER;
-      str += sizeof ("%higher(") - 2;
-    }
-  else if (str[0] == '%'
-          && TOLOWER (str[1]) == 'h'
-          && TOLOWER (str[2]) == 'i'
-          && TOLOWER (str[3]) == 'g'
-          && TOLOWER (str[4]) == 'h'
-          && TOLOWER (str[5]) == 'e'
-          && TOLOWER (str[6]) == 's'
-          && TOLOWER (str[7]) == 't'
-          && str[8] == '(')
-    {
-      c = S_EX_HIGHEST;
-      str += sizeof ("%highest(") - 2;
-    }
-/* currently unsupported */
-#if 0
-  else if (str[0] == '%'
-          && TOLOWER (str[1]) == 'g'
-          && TOLOWER (str[2]) == 'p'
-          && TOLOWER (str[3]) == '_'
-          && TOLOWER (str[4]) == 'r'
-          && TOLOWER (str[5]) == 'e'
-          && TOLOWER (str[6]) == 'l'
-          && str[7] == '(')
-    {
-      c = S_EX_GPREL;
-      str += sizeof ("%gp_rel(") - 2;
-    }
-  else if (str[0] == '%'
-          && TOLOWER (str[1]) == 'n'
-          && TOLOWER (str[2]) == 'e'
-          && TOLOWER (str[3]) == 'g'
-          && str[4] == '(')
-    {
-      c = S_EX_NEG;
-      str += sizeof ("%neg(") - 2;
-    }
-#endif
-  else
+  /* Don't update oldstr if the last call had nested percent_op's.  */
+  if (! oldstr)
+    oldstr = str;
+       
+  do
     {
-      my_getExpression (ep, str);
-      return c;
+      oldc = c;
+      c = my_getSmallParser (&str, &len, &nest_level);
+      if (c != S_EX_NONE && c != S_EX_REGISTER)
+       str += len;
     }
+  while (c != S_EX_NONE && c != S_EX_REGISTER);
 
-  /*
-   * A small expression may be followed by a base register.
-   * Scan to the end of this operand, and then back over a possible
-   * base register.  Then scan the small expression up to that
-   * point.  (Based on code in sparc.c...)
-   */
-  for (sp = str; *sp && *sp != ','; sp++)
-    ;
-  if (sp - 4 >= str && sp[-1] == ')')
+  /* A percent_op was encountered.  */
+  if (nest_level)
     {
-      if (ISDIGIT (sp[-2]))
+      /* Don't try to get a expression if it is already blanked out.  */
+      if (*(str + strspn (str + 1, " )")) != ')')
        {
-         for (sp -= 3; sp >= str && ISDIGIT (*sp); sp--)
-           ;
-         if (*sp == '$' && sp > str && sp[-1] == '(')
-           {
-             sp--;
-             goto do_it;
-           }
+         char save;
+
+         save = *(str + len);
+         *(str + len) = '\0';
+         my_getExpression (ep, str);
+         *(str + len) = save;
        }
-      else if (sp - 5 >= str
-              && sp[-5] == '('
-              && sp[-4] == '$'
-              && ((sp[-3] == 'f' && sp[-2] == 'p')
-                  || (sp[-3] == 's' && sp[-2] == 'p')
-                  || (sp[-3] == 'g' && sp[-2] == 'p')
-                  || (sp[-3] == 'a' && sp[-2] == 't')))
-       {
-         sp -= 5;
-       do_it:
-         if (sp == str)
-           {
-             /* no expression means zero offset */
-             if (c != S_EX_NONE)
-               {
-                 /* %xx(reg) is an error */
-                 ep->X_op = O_absent;
-                 expr_end = oldstr;
-               }
-             else
-               {
-                 ep->X_op = O_constant;
-                 expr_end = sp;
-               }
-             ep->X_add_symbol = NULL;
-             ep->X_op_symbol = NULL;
-             ep->X_add_number = 0;
-           }
-         else
-           {
-             *sp = '\0';
-             my_getExpression (ep, str);
-             *sp = '(';
-           }
-         return c;
+      if (nest_level > 1)
+       {
+         /* blank out including the % sign.  */
+         char *p = strrchr (oldstr, '%');
+         memset (p, ' ', str - p + len);
+         str = oldstr;
+       }
+      else
+       {
+         expr_end = strchr (str, ')') + 1;
        }
+      c = oldc;
     }
-  my_getExpression (ep, str);
+  else if (c == S_EX_NONE)
+    {
+      my_getExpression (ep, str);
+    }
+  else if (c == S_EX_REGISTER)
+    {
+      ep->X_op = O_constant;
+      expr_end = str;
+      ep->X_add_symbol = NULL;
+      ep->X_op_symbol = NULL;
+      ep->X_add_number = 0;
+    }
+  else
+    {
+      as_fatal(_("internal error"));
+    }
+
+  if (nest_level <= 1)
+    oldstr = NULL;
 
-  /* => %highest, %higher, %hi, %lo, %gprel, %neg encountered */
   return c;
 }