sync with obs
[external/binutils.git] / gas / config / tc-sparc.c
index a9d633d..77fda56 100644 (file)
@@ -1,6 +1,7 @@
 /* tc-sparc.c -- Assemble for the SPARC
    Copyright 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
-   1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
+   1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
+   2011
    Free Software Foundation, Inc.
    This file is part of GAS, the GNU Assembler.
 
@@ -75,8 +76,16 @@ static int default_arch_size;
 /* The currently selected v9 memory model.  Currently only used for
    ELF.  */
 static enum { MM_TSO, MM_PSO, MM_RMO } sparc_memory_model = MM_RMO;
+
+#ifndef TE_SOLARIS
+/* Bitmask of instruction types seen so far, used to populate the
+   GNU attributes section with hwcap information.  */
+static int hwcap_seen;
+#endif
 #endif
 
+static int hwcap_allowed;
+
 static int architecture_requested;
 static int warn_on_bump;
 
@@ -224,23 +233,38 @@ static struct sparc_arch {
   int default_arch_size;
   /* Allowable arg to -A?  */
   int user_option_p;
+  int hwcap_allowed;
 } sparc_arch_table[] = {
-  { "v6", "v6", v6, 0, 1 },
-  { "v7", "v7", v7, 0, 1 },
-  { "v8", "v8", v8, 32, 1 },
-  { "sparclet", "sparclet", sparclet, 32, 1 },
-  { "sparclite", "sparclite", sparclite, 32, 1 },
-  { "sparc86x", "sparclite", sparc86x, 32, 1 },
-  { "v8plus", "v9", v9, 0, 1 },
-  { "v8plusa", "v9a", v9, 0, 1 },
-  { "v8plusb", "v9b", v9, 0, 1 },
-  { "v9", "v9", v9, 0, 1 },
-  { "v9a", "v9a", v9, 0, 1 },
-  { "v9b", "v9b", v9, 0, 1 },
+  { "v6", "v6", v6, 0, 1, 0 },
+  { "v7", "v7", v7, 0, 1, 0 },
+  { "v8", "v8", v8, 32, 1, F_MUL32|F_DIV32|F_FSMULD },
+  { "v8a", "v8", v8, 32, 1, F_MUL32|F_DIV32|F_FSMULD },
+  { "sparc", "v9", v9, 0, 1, F_MUL32|F_DIV32|F_FSMULD|F_POPC|F_V8PLUS },
+  { "sparcvis", "v9a", v9, 0, 1, F_MUL32|F_DIV32|F_FSMULD|F_POPC|F_VIS },
+  { "sparcvis2", "v9b", v9, 0, 1, F_MUL32|F_DIV32|F_FSMULD|F_POPC|F_VIS|F_VIS2 },
+  { "sparcfmaf", "v9b", v9, 0, 1, F_MUL32|F_DIV32|F_FSMULD|F_POPC|F_VIS|F_VIS2|F_FMAF },
+  { "sparcima", "v9b", v9, 0, 1, F_MUL32|F_DIV32|F_FSMULD|F_POPC|F_VIS|F_VIS2|F_FMAF|F_IMA },
+  { "sparcvis3", "v9b", v9, 0, 1, F_MUL32|F_DIV32|F_FSMULD|F_POPC|F_VIS|F_VIS2|F_FMAF|F_VIS3|F_HPC },
+  { "sparcvis3r", "v9b", v9, 0, 1, F_MUL32|F_DIV32|F_FSMULD|F_POPC|F_VIS|F_VIS2|F_FMAF|F_VIS3|F_HPC|F_RANDOM|F_TRANS|F_FJFMAU },
+  { "sparclet", "sparclet", sparclet, 32, 1, F_MUL32|F_DIV32|F_FSMULD },
+  { "sparclite", "sparclite", sparclite, 32, 1, F_MUL32|F_DIV32|F_FSMULD },
+  { "sparc86x", "sparclite", sparc86x, 32, 1, F_MUL32|F_DIV32|F_FSMULD },
+  { "v8plus", "v9", v9, 0, 1, F_MUL32|F_DIV32|F_FSMULD|F_POPC|F_V8PLUS },
+  { "v8plusa", "v9a", v9, 0, 1, F_MUL32|F_DIV32|F_FSMULD|F_POPC|F_V8PLUS|F_VIS },
+  { "v8plusb", "v9b", v9, 0, 1, F_MUL32|F_DIV32|F_FSMULD|F_POPC|F_V8PLUS|F_VIS|F_VIS2 },
+  { "v8plusc", "v9b", v9, 0, 1, F_MUL32|F_DIV32|F_FSMULD|F_POPC|F_V8PLUS|F_VIS|F_VIS2|F_ASI_BLK_INIT },
+  { "v8plusd", "v9b", v9, 0, 1, F_MUL32|F_DIV32|F_FSMULD|F_POPC|F_V8PLUS|F_VIS|F_VIS2|F_ASI_BLK_INIT|F_FMAF|F_VIS3|F_HPC },
+  { "v8plusv", "v9b", v9, 0, 1, F_MUL32|F_DIV32|F_FSMULD|F_POPC|F_V8PLUS|F_VIS|F_VIS2|F_ASI_BLK_INIT|F_FMAF|F_VIS3|F_HPC|F_RANDOM|F_TRANS|F_FJFMAU|F_IMA|F_ASI_CACHE_SPARING },
+  { "v9", "v9", v9, 0, 1, F_MUL32|F_DIV32|F_FSMULD|F_POPC },
+  { "v9a", "v9a", v9, 0, 1, F_MUL32|F_DIV32|F_FSMULD|F_POPC|F_VIS },
+  { "v9b", "v9b", v9, 0, 1, F_MUL32|F_DIV32|F_FSMULD|F_POPC|F_VIS|F_VIS2 },
+  { "v9c", "v9b", v9, 0, 1, F_MUL32|F_DIV32|F_FSMULD|F_POPC|F_VIS|F_VIS2|F_ASI_BLK_INIT },
+  { "v9d", "v9b", v9, 0, 1, F_MUL32|F_DIV32|F_FSMULD|F_POPC|F_VIS|F_VIS2|F_ASI_BLK_INIT|F_FMAF|F_VIS3|F_HPC },
+  { "v9v", "v9b", v9, 0, 1, F_MUL32|F_DIV32|F_FSMULD|F_POPC|F_VIS|F_VIS2|F_ASI_BLK_INIT|F_FMAF|F_VIS3|F_HPC|F_RANDOM|F_TRANS|F_FJFMAU|F_IMA|F_ASI_CACHE_SPARING },
   /* This exists to allow configure.in/Makefile.in to pass one
      value to specify both the default machine and default word size.  */
-  { "v9-64", "v9", v9, 64, 0 },
-  { NULL, NULL, v8, 0, 0 }
+  { "v9-64", "v9", v9, 64, 0, F_MUL32|F_DIV32|F_FSMULD|F_POPC },
+  { NULL, NULL, v8, 0, 0, 0 }
 };
 
 /* Variant of default_arch */
@@ -480,7 +504,10 @@ md_parse_option (int c, char *arg)
        if (opcode_arch == SPARC_OPCODE_ARCH_BAD)
          as_fatal (_("Bad opcode table, broken assembler."));
 
-       max_architecture = opcode_arch;
+       if (!architecture_requested
+           || opcode_arch > max_architecture)
+         max_architecture = opcode_arch;
+       hwcap_allowed |= sa->hwcap_allowed;
        architecture_requested = 1;
       }
       break;
@@ -773,6 +800,7 @@ struct priv_reg_entry v9a_asr_table[] =
   {"pcr", 16},
   {"gsr", 19},
   {"dcr", 18},
+  {"cps", 28},
   {"clear_softint", 21},
   {"", -1},                    /* End marker.  */
 };
@@ -914,6 +942,48 @@ sparc_md_end (void)
       default: break;
       }
   bfd_set_arch_mach (stdoutput, bfd_arch_sparc, mach);
+
+#if defined(OBJ_ELF) && !defined(TE_SOLARIS)
+  if (hwcap_seen)
+    {
+      int bits = 0;
+
+      if (hwcap_seen & F_MUL32)
+       bits |= ELF_SPARC_HWCAP_MUL32;
+      if (hwcap_seen & F_DIV32)
+       bits |= ELF_SPARC_HWCAP_DIV32;
+      if (hwcap_seen & F_FSMULD)
+       bits |= ELF_SPARC_HWCAP_FSMULD;
+      if (hwcap_seen & F_V8PLUS)
+       bits |= ELF_SPARC_HWCAP_V8PLUS;
+      if (hwcap_seen & F_POPC)
+       bits |= ELF_SPARC_HWCAP_POPC;
+      if (hwcap_seen & F_VIS)
+       bits |= ELF_SPARC_HWCAP_VIS;
+      if (hwcap_seen & F_VIS2)
+       bits |= ELF_SPARC_HWCAP_VIS2;
+      if (hwcap_seen & F_ASI_BLK_INIT)
+       bits |= ELF_SPARC_HWCAP_ASI_BLK_INIT;
+      if (hwcap_seen & F_FMAF)
+       bits |= ELF_SPARC_HWCAP_FMAF;
+      if (hwcap_seen & F_VIS3)
+       bits |= ELF_SPARC_HWCAP_VIS3;
+      if (hwcap_seen & F_HPC)
+       bits |= ELF_SPARC_HWCAP_HPC;
+      if (hwcap_seen & F_RANDOM)
+       bits |= ELF_SPARC_HWCAP_RANDOM;
+      if (hwcap_seen & F_TRANS)
+       bits |= ELF_SPARC_HWCAP_TRANS;
+      if (hwcap_seen & F_FJFMAU)
+       bits |= ELF_SPARC_HWCAP_FJFMAU;
+      if (hwcap_seen & F_IMA)
+       bits |= ELF_SPARC_HWCAP_IMA;
+      if (hwcap_seen & F_ASI_CACHE_SPARING)
+       bits |= ELF_SPARC_HWCAP_ASI_CACHE_SPARING;
+
+      bfd_elf_add_obj_attr_int (stdoutput, OBJ_ATTR_GNU, Tag_GNU_Sparc_HWCAPS, bits);
+    }
+#endif
 }
 \f
 /* Return non-zero if VAL is in the range -(MAX+1) to MAX.  */
@@ -1367,6 +1437,44 @@ md_assemble (char *str)
     }
 }
 
+static const char *
+get_hwcap_name (int mask)
+{
+  if (mask & F_MUL32)
+    return "mul32";
+  if (mask & F_DIV32)
+    return "div32";
+  if (mask & F_FSMULD)
+    return "fsmuld";
+  if (mask & F_V8PLUS)
+    return "v8plus";
+  if (mask & F_POPC)
+    return "popc";
+  if (mask & F_VIS)
+    return "vis";
+  if (mask & F_VIS2)
+    return "vis2";
+  if (mask & F_ASI_BLK_INIT)
+    return "ASIBlkInit";
+  if (mask & F_FMAF)
+    return "fmaf";
+  if (mask & F_VIS3)
+    return "vis3";
+  if (mask & F_HPC)
+    return "hpc";
+  if (mask & F_RANDOM)
+    return "random";
+  if (mask & F_TRANS)
+    return "trans";
+  if (mask & F_FJFMAU)
+    return "fjfmau";
+  if (mask & F_IMA)
+    return "ima";
+  if (mask & F_ASI_CACHE_SPARING)
+    return "cspare";
+  return "UNKNOWN";
+}
+
 /* Subroutine of md_assemble to do the actual parsing.  */
 
 static int
@@ -2136,6 +2244,9 @@ sparc_ip (char *str, const struct sparc_opcode **pinsn)
            case 'B':
            case 'R':
 
+           case '4':
+           case '5':
+
            case 'g':
            case 'H':
            case 'J':
@@ -2153,6 +2264,7 @@ sparc_ip (char *str, const struct sparc_opcode **pinsn)
 
                    if ((*args == 'v'
                         || *args == 'B'
+                        || *args == '5'
                         || *args == 'H')
                        && (mask & 1))
                      {
@@ -2214,6 +2326,11 @@ sparc_ip (char *str, const struct sparc_opcode **pinsn)
                    opcode |= RS2 (mask);
                    continue;
 
+                 case '4':
+                 case '5':
+                   opcode |= RS3 (mask);
+                   continue;
+
                  case 'g':
                  case 'H':
                  case 'J':
@@ -2233,6 +2350,14 @@ sparc_ip (char *str, const struct sparc_opcode **pinsn)
                }
              break;
 
+           case '(':
+             if (strncmp (s, "%efsr", 5) == 0)
+               {
+                 s += 5;
+                 continue;
+               }
+             break;
+
            case '0':           /* 64 bit immediate (set, setsw, setx insn)  */
              the_insn.reloc = BFD_RELOC_NONE; /* reloc handled elsewhere  */
              goto immediate;
@@ -2390,8 +2515,10 @@ sparc_ip (char *str, const struct sparc_opcode **pinsn)
                  {
                    if (s1[-2] == '%' && s1[-3] == '+')
                      s1 -= 3;
-                   else if (strchr ("goli0123456789", s1[-2]) && s1[-3] == '%' && s1[-4] == '+')
+                   else if (strchr ("golir0123456789", s1[-2]) && s1[-3] == '%' && s1[-4] == '+')
                      s1 -= 4;
+                   else if (s1[-3] == 'r' && s1[-4] == '%' && s1[-5] == '+')
+                     s1 -= 5;
                    else
                      s1 = NULL;
                    if (s1)
@@ -2723,7 +2850,12 @@ sparc_ip (char *str, const struct sparc_opcode **pinsn)
        {
          /* We have a match.  Now see if the architecture is OK.  */
          int needed_arch_mask = insn->architecture;
+         int hwcaps = insn->flags & F_HWCAP_MASK;
 
+#if defined(OBJ_ELF) && !defined(TE_SOLARIS)
+         if (hwcaps)
+                 hwcap_seen |= hwcaps;
+#endif
          if (v9_arg_p)
            {
              needed_arch_mask &=
@@ -2791,6 +2923,17 @@ sparc_ip (char *str, const struct sparc_opcode **pinsn)
                         sparc_opcode_archs[max_architecture].name);
              return special_case;
            }
+
+         /* Make sure the the hwcaps used by the instruction are
+            currently enabled.  */
+         if (hwcaps & ~hwcap_allowed)
+           {
+             const char *hwcap_name = get_hwcap_name(hwcaps & ~hwcap_allowed);
+
+             as_bad (_("Hardware capability \"%s\" not enabled for \"%s\"."),
+                     hwcap_name, str);
+             return special_case;
+           }
        } /* If no match.  */
 
       break;