Allow symbols in MEMORY region specification
[deliverable/binutils-gdb.git] / gas / config / tc-s390.c
index 36d59748033752498c8e2f958aaf4268b07c73c3..9102c609b4a5a9c160c28dab449b5f64c8d03255 100644 (file)
@@ -1,6 +1,5 @@
 /* tc-s390.c -- Assemble for the S390
-   Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008,
-   2009, 2010  Free Software Foundation, Inc.
+   Copyright (C) 2000-2015 Free Software Foundation, Inc.
    Contributed by Martin Schwidefsky (schwidefsky@de.ibm.com).
 
    This file is part of GAS, the GNU Assembler.
@@ -44,6 +43,10 @@ static int s390_arch_size = 0;
 static unsigned int current_cpu = S390_OPCODE_MAXCPU - 1;
 static unsigned int current_mode_mask = 0;
 
+/* Set to TRUE if the highgprs flag in the ELF header needs to be set
+   for the output file.  */
+static bfd_boolean set_highgprs_p = FALSE;
+
 /* Whether to use user friendly register names. Default is TRUE.  */
 #ifndef TARGET_REG_NAMES_P
 #define TARGET_REG_NAMES_P TRUE
@@ -86,156 +89,55 @@ static void s390_bss (int);
 static void s390_insn (int);
 static void s390_literals (int);
 static void s390_machine (int);
+static void s390_machinemode (int);
 
 const pseudo_typeS md_pseudo_table[] =
 {
-  { "align", s_align_bytes, 0 },
+  { "align",        s_align_bytes,      0 },
   /* Pseudo-ops which must be defined.  */
-  { "bss",      s390_bss,       0 },
-  { "insn",     s390_insn,      0 },
+  { "bss",          s390_bss,           0 },
+  { "insn",         s390_insn,          0 },
   /* Pseudo-ops which must be overridden.  */
-  { "byte",    s390_byte,      0 },
-  { "short",    s390_elf_cons,  2 },
-  { "long",    s390_elf_cons,  4 },
-  { "quad",     s390_elf_cons,  8 },
-  { "ltorg",    s390_literals,  0 },
-  { "string",   stringer,       8 + 1 },
-  { "machine",  s390_machine,   0 },
-  { NULL,      NULL,           0 }
-};
-
-
-/* Structure to hold information about predefined registers.  */
-struct pd_reg
-  {
-    char *name;
-    int value;
-  };
-
-/* List of registers that are pre-defined:
-
-   Each access register has a predefined name of the form:
-     a<reg_num> which has the value <reg_num>.
-
-   Each control register has a predefined name of the form:
-     c<reg_num> which has the value <reg_num>.
-
-   Each general register has a predefined name of the form:
-     r<reg_num> which has the value <reg_num>.
-
-   Each floating point register a has predefined name of the form:
-     f<reg_num> which has the value <reg_num>.
-
-   There are individual registers as well:
-     sp     has the value 15
-     lit    has the value 12
-
-   The table is sorted. Suitable for searching by a binary search.  */
-
-static const struct pd_reg pre_defined_registers[] =
-{
-  { "a0", 0 },     /* Access registers */
-  { "a1", 1 },
-  { "a10", 10 },
-  { "a11", 11 },
-  { "a12", 12 },
-  { "a13", 13 },
-  { "a14", 14 },
-  { "a15", 15 },
-  { "a2", 2 },
-  { "a3", 3 },
-  { "a4", 4 },
-  { "a5", 5 },
-  { "a6", 6 },
-  { "a7", 7 },
-  { "a8", 8 },
-  { "a9", 9 },
-
-  { "c0", 0 },     /* Control registers */
-  { "c1", 1 },
-  { "c10", 10 },
-  { "c11", 11 },
-  { "c12", 12 },
-  { "c13", 13 },
-  { "c14", 14 },
-  { "c15", 15 },
-  { "c2", 2 },
-  { "c3", 3 },
-  { "c4", 4 },
-  { "c5", 5 },
-  { "c6", 6 },
-  { "c7", 7 },
-  { "c8", 8 },
-  { "c9", 9 },
-
-  { "f0", 0 },     /* Floating point registers */
-  { "f1", 1 },
-  { "f10", 10 },
-  { "f11", 11 },
-  { "f12", 12 },
-  { "f13", 13 },
-  { "f14", 14 },
-  { "f15", 15 },
-  { "f2", 2 },
-  { "f3", 3 },
-  { "f4", 4 },
-  { "f5", 5 },
-  { "f6", 6 },
-  { "f7", 7 },
-  { "f8", 8 },
-  { "f9", 9 },
-
-  { "lit", 13 },   /* Pointer to literal pool */
-
-  { "r0", 0 },     /* General purpose registers */
-  { "r1", 1 },
-  { "r10", 10 },
-  { "r11", 11 },
-  { "r12", 12 },
-  { "r13", 13 },
-  { "r14", 14 },
-  { "r15", 15 },
-  { "r2", 2 },
-  { "r3", 3 },
-  { "r4", 4 },
-  { "r5", 5 },
-  { "r6", 6 },
-  { "r7", 7 },
-  { "r8", 8 },
-  { "r9", 9 },
-
-  { "sp", 15 },   /* Stack pointer */
-
+  { "byte",        s390_byte,          0 },
+  { "short",        s390_elf_cons,      2 },
+  { "long",        s390_elf_cons,      4 },
+  { "quad",         s390_elf_cons,      8 },
+  { "ltorg",        s390_literals,      0 },
+  { "string",       stringer,           8 + 1 },
+  { "machine",      s390_machine,       0 },
+  { "machinemode",  s390_machinemode,   0 },
+  { NULL,          NULL,               0 }
 };
 
-#define REG_NAME_CNT (sizeof (pre_defined_registers) / sizeof (struct pd_reg))
-
 /* Given NAME, find the register number associated with that name, return
    the integer value associated with the given name or -1 on failure.  */
 
 static int
-reg_name_search (const struct pd_reg *regs, int regcount, const char *name)
+reg_name_search (const char *name)
 {
-  int middle, low, high;
-  int cmp;
+  int val = -1;
 
-  low = 0;
-  high = regcount - 1;
+  if (strcasecmp (name, "lit") == 0)
+    return 13;
 
-  do
+  if (strcasecmp (name, "sp") == 0)
+    return 15;
+
+  if (name[0] != 'a' && name[0] != 'c' && name[0] != 'f'
+      && name[0] != 'r' && name[0] != 'v')
+    return -1;
+
+  if (ISDIGIT (name[1]))
     {
-      middle = (low + high) / 2;
-      cmp = strcasecmp (name, regs[middle].name);
-      if (cmp < 0)
-       high = middle - 1;
-      else if (cmp > 0)
-       low = middle + 1;
-      else
-       return regs[middle].value;
+      val = name[1] - '0';
+      if (ISDIGIT (name[2]))
+       val = val * 10 + name[2] - '0';
     }
-  while (low <= high);
 
-  return -1;
+  if ((name[0] != 'v' && val > 15) || val > 31)
+    val = -1;
+
+  return val;
 }
 
 
@@ -267,7 +169,7 @@ register_name (expressionS *expressionP)
     return FALSE;
 
   c = get_symbol_end ();
-  reg_number = reg_name_search (pre_defined_registers, REG_NAME_CNT, name);
+  reg_number = reg_name_search (name);
 
   /* Put back the delimiting char.  */
   *input_line_pointer = c;
@@ -375,6 +277,10 @@ s390_parse_cpu (char *arg)
     return S390_OPCODE_Z10;
   else if (strcmp (arg, "z196") == 0)
     return S390_OPCODE_Z196;
+  else if (strcmp (arg, "zEC12") == 0)
+    return S390_OPCODE_ZEC12;
+  else if (strcmp (arg, "z13") == 0)
+    return S390_OPCODE_Z13;
   else if (strcmp (arg, "all") == 0)
     return S390_OPCODE_MAXCPU - 1;
   else
@@ -409,7 +315,11 @@ md_parse_option (int c, char *arg)
        current_mode_mask = 1 << S390_OPCODE_ESA;
 
       else if (arg != NULL && strcmp (arg, "zarch") == 0)
-       current_mode_mask = 1 << S390_OPCODE_ZARCH;
+       {
+         if (s390_arch_size == 32)
+           set_highgprs_p = TRUE;
+         current_mode_mask = 1 << S390_OPCODE_ZARCH;
+       }
 
       else if (arg != NULL && strncmp (arg, "arch=", 5) == 0)
        {
@@ -478,7 +388,7 @@ md_show_usage (FILE *stream)
 static void
 s390_setup_opcodes (void)
 {
-  register const struct s390_opcode *op;
+  const struct s390_opcode *op;
   const struct s390_opcode *op_end;
   bfd_boolean dup_insn = FALSE;
   const char *retval;
@@ -525,9 +435,8 @@ s390_setup_opcodes (void)
 void
 md_begin (void)
 {
-  register const struct s390_opcode *op;
+  const struct s390_opcode *op;
   const struct s390_opcode *op_end;
-  bfd_boolean dup_insn = FALSE;
   const char *retval;
 
   /* Give a warning if the combination -m64-bit and -Aesa is used.  */
@@ -548,11 +457,8 @@ md_begin (void)
     {
       retval = hash_insert (s390_opformat_hash, op->name, (void *) op);
       if (retval != (const char *) NULL)
-       {
-         as_bad (_("Internal assembler error for instruction format %s"),
-                 op->name);
-         dup_insn = TRUE;
-       }
+       as_bad (_("Internal assembler error for instruction format %s"),
+               op->name);
     }
 
   s390_setup_opcodes ();
@@ -626,6 +532,12 @@ s390_insert_operand (unsigned char *insn,
       max = (((addressT) 1 << (operand->bits - 1)) << 1) - 1;
       min = (offsetT) 0;
       uval = (addressT) val;
+
+      /* Vector register operands have an additional bit in the RXB
+        field.  */
+      if (operand->flags & S390_OPERAND_VR)
+       max = (max << 1) | 1;
+
       /* Length x in an instructions has real length x+1.  */
       if (operand->flags & S390_OPERAND_LENGTH)
        uval--;
@@ -645,6 +557,43 @@ s390_insert_operand (unsigned char *insn,
        }
     }
 
+  if (operand->flags & S390_OPERAND_VR)
+    {
+      /* Insert the extra bit into the RXB field.  */
+      switch (operand->shift)
+       {
+       case 8:
+         insn[4] |= (uval & 0x10) >> 1;
+         break;
+       case 12:
+         insn[4] |= (uval & 0x10) >> 2;
+         break;
+       case 16:
+         insn[4] |= (uval & 0x10) >> 3;
+         break;
+       case 32:
+         insn[4] |= (uval & 0x10) >> 4;
+         break;
+       }
+      uval &= 0xf;
+    }
+
+  if (operand->flags & S390_OPERAND_OR1)
+    uval |= 1;
+  if (operand->flags & S390_OPERAND_OR2)
+    uval |= 2;
+  if (operand->flags & S390_OPERAND_OR8)
+    uval |= 8;
+
+  /* Duplicate the operand at bit pos 12 to 16.  */
+  if (operand->flags & S390_OPERAND_CP16)
+    {
+      /* Copy VR operand at bit pos 12 to bit pos 16.  */
+      insn[2] |= uval << 4;
+      /* Copy the flag in the RXB field.  */
+      insn[4] |= (insn[4] & 4) >> 1;
+    }
+
   /* Insert fragments of the operand byte for byte.  */
   offset = operand->shift + operand->bits;
   uval <<= (-offset) & 7;
@@ -1199,6 +1148,14 @@ md_gather_operands (char *str,
 
       operand = s390_operands + *opindex_ptr;
 
+      if ((opcode->flags & S390_INSTR_FLAG_OPTPARM) && *str == '\0')
+       {
+         /* Optional parameters might need to be ORed with a
+            value so calling s390_insert_operand is needed.  */
+         s390_insert_operand (insn, operand, 0, NULL, 0);
+         break;
+       }
+
       if (skip_optional && (operand->flags & S390_OPERAND_INDEX))
        {
          /* We do an early skip. For D(X,B) constructions the index
@@ -1267,9 +1224,20 @@ md_gather_operands (char *str,
                  && ex.X_add_number == 0
                  && warn_areg_zero)
                as_warn (_("base register specified but zero"));
-             if ((operand->flags & S390_OPERAND_REG_EVEN)
+             if ((operand->flags & S390_OPERAND_GPR)
+                 && (operand->flags & S390_OPERAND_REG_PAIR)
                  && (ex.X_add_number & 1))
-               as_fatal (_("odd numbered register specified as register pair"));
+               as_fatal (_("odd numbered general purpose register specified as "
+                           "register pair"));
+             if ((operand->flags & S390_OPERAND_FPR)
+                 && (operand->flags & S390_OPERAND_REG_PAIR)
+                 && ex.X_add_number != 0 && ex.X_add_number != 1
+                 && ex.X_add_number != 4 && ex.X_add_number != 5
+                 && ex.X_add_number != 8 && ex.X_add_number != 9
+                 && ex.X_add_number != 12 && ex.X_add_number != 13)
+               as_fatal (_("invalid floating point register pair.  Valid fp "
+                           "register pair operands are 0, 1, 4, 5, 8, 9, "
+                           "12 or 13."));
              s390_insert_operand (insn, operand, ex.X_add_number, NULL, 0);
            }
        }
@@ -1297,8 +1265,14 @@ md_gather_operands (char *str,
          else if (suffix == ELF_SUFFIX_PLT)
            {
              if ((operand->flags & S390_OPERAND_PCREL)
-                 && (operand->bits == 16))
+                 && (operand->bits == 12))
+               reloc = BFD_RELOC_390_PLT12DBL;
+             else if ((operand->flags & S390_OPERAND_PCREL)
+                      && (operand->bits == 16))
                reloc = BFD_RELOC_390_PLT16DBL;
+             else if ((operand->flags & S390_OPERAND_PCREL)
+                      && (operand->bits == 24))
+               reloc = BFD_RELOC_390_PLT24DBL;
              else if ((operand->flags & S390_OPERAND_PCREL)
                       && (operand->bits == 32))
                reloc = BFD_RELOC_390_PLT32DBL;
@@ -1453,6 +1427,10 @@ md_gather_operands (char *str,
                as_bad (_("syntax error; ')' not allowed here"));
              str++;
            }
+
+         if ((opcode->flags & S390_INSTR_FLAG_OPTPARM) && *str == '\0')
+           continue;
+
          /* If there is a next operand it must be separated by a comma.  */
          if (opindex_ptr[1] != '\0')
            {
@@ -1535,7 +1513,7 @@ md_gather_operands (char *str,
          if (!reloc_howto)
            abort ();
 
-         size = bfd_get_reloc_size (reloc_howto);
+         size = ((reloc_howto->bitsize - 1) / 8) + 1;
 
          if (size < 1 || size > 4)
            abort ();
@@ -1792,7 +1770,7 @@ s390_literals (int ignore ATTRIBUTE_UNUSED)
 
 /* The .machine pseudo op allows to switch to a different CPU level in
    the asm listing.  The current CPU setting can be stored on a stack
-   with .machine push and restored with .machined pop.  */
+   with .machine push and restored with .machine pop.  */
 
 static void
 s390_machine (int ignore ATTRIBUTE_UNUSED)
@@ -1822,10 +1800,6 @@ s390_machine (int ignore ATTRIBUTE_UNUSED)
     {
       unsigned int old_cpu = current_cpu;
       unsigned int new_cpu;
-      char *p;
-
-      for (p = cpu_string; *p != 0; p++)
-       *p = TOLOWER (*p);
 
       if (strcmp (cpu_string, "push") == 0)
        {
@@ -1856,6 +1830,83 @@ s390_machine (int ignore ATTRIBUTE_UNUSED)
   demand_empty_rest_of_line ();
 }
 
+/* The .machinemode pseudo op allows to switch to a different
+   architecture mode in the asm listing.  The current architecture
+   mode setting can be stored on a stack with .machinemode push and
+   restored with .machinemode pop.  */
+
+static void
+s390_machinemode (int ignore ATTRIBUTE_UNUSED)
+{
+  char *mode_string;
+#define MAX_HISTORY 100
+  static unsigned int *mode_history;
+  static int curr_hist;
+
+  SKIP_WHITESPACE ();
+
+  if (*input_line_pointer == '"')
+    {
+      int len;
+      mode_string = demand_copy_C_string (&len);
+    }
+  else
+    {
+      char c;
+      mode_string = input_line_pointer;
+      c = get_symbol_end ();
+      mode_string = xstrdup (mode_string);
+      *input_line_pointer = c;
+    }
+
+  if (mode_string != NULL)
+    {
+      unsigned int old_mode_mask = current_mode_mask;
+      char *p;
+
+      for (p = mode_string; *p != 0; p++)
+       *p = TOLOWER (*p);
+
+      if (strcmp (mode_string, "push") == 0)
+       {
+         if (mode_history == NULL)
+           mode_history = xmalloc (MAX_HISTORY * sizeof (*mode_history));
+
+         if (curr_hist >= MAX_HISTORY)
+           as_bad (_(".machinemode stack overflow"));
+         else
+           mode_history[curr_hist++] = current_mode_mask;
+       }
+      else if (strcmp (mode_string, "pop") == 0)
+       {
+         if (curr_hist <= 0)
+           as_bad (_(".machinemode stack underflow"));
+         else
+           current_mode_mask = mode_history[--curr_hist];
+       }
+      else
+       {
+         if (strcmp (mode_string, "esa") == 0)
+           current_mode_mask = 1 << S390_OPCODE_ESA;
+         else if (strcmp (mode_string, "zarch") == 0)
+           {
+             if (s390_arch_size == 32)
+               set_highgprs_p = TRUE;
+             current_mode_mask = 1 << S390_OPCODE_ZARCH;
+           }
+         else if (strcmp (mode_string, "zarch_nohighgprs") == 0)
+           current_mode_mask = 1 << S390_OPCODE_ZARCH;
+         else
+           as_bad (_("invalid machine `%s'"), mode_string);
+       }
+
+      if (current_mode_mask != old_mode_mask)
+       s390_setup_opcodes ();
+    }
+
+  demand_empty_rest_of_line ();
+}
+
 char *
 md_atof (int type, char *litp, int *sizep)
 {
@@ -1938,7 +1989,9 @@ tc_s390_fix_adjustable (fixS *fixP)
       || fixP->fx_r_type == BFD_RELOC_390_PLTOFF16
       || fixP->fx_r_type == BFD_RELOC_390_PLTOFF32
       || fixP->fx_r_type == BFD_RELOC_390_PLTOFF64
+      || fixP->fx_r_type == BFD_RELOC_390_PLT12DBL
       || fixP->fx_r_type == BFD_RELOC_390_PLT16DBL
+      || fixP->fx_r_type == BFD_RELOC_390_PLT24DBL
       || fixP->fx_r_type == BFD_RELOC_390_PLT32
       || fixP->fx_r_type == BFD_RELOC_390_PLT32DBL
       || fixP->fx_r_type == BFD_RELOC_390_PLT64
@@ -2004,7 +2057,9 @@ tc_s390_force_relocation (struct fix *fixp)
     case BFD_RELOC_390_GOT64:
     case BFD_RELOC_390_GOTENT:
     case BFD_RELOC_390_PLT32:
+    case BFD_RELOC_390_PLT12DBL:
     case BFD_RELOC_390_PLT16DBL:
+    case BFD_RELOC_390_PLT24DBL:
     case BFD_RELOC_390_PLT32DBL:
     case BFD_RELOC_390_PLT64:
     case BFD_RELOC_390_GOTPLT12:
@@ -2015,7 +2070,7 @@ tc_s390_force_relocation (struct fix *fixp)
     case BFD_RELOC_390_GOTPLTENT:
       return 1;
     default:
-      break;;
+      break;
     }
 
   return generic_force_reloc (fixp);
@@ -2096,6 +2151,14 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
          fixP->fx_where += 1;
          fixP->fx_r_type = BFD_RELOC_8;
        }
+      else if (operand->bits == 12 && operand->shift == 12
+              && (operand->flags & S390_OPERAND_PCREL))
+       {
+         fixP->fx_size = 2;
+         fixP->fx_where += 1;
+         fixP->fx_offset += 1;
+         fixP->fx_r_type = BFD_RELOC_390_PC12DBL;
+       }
       else if (operand->bits == 16 && operand->shift == 16)
        {
          fixP->fx_size = 2;
@@ -2108,6 +2171,14 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
          else
            fixP->fx_r_type = BFD_RELOC_16;
        }
+      else if (operand->bits == 24 && operand->shift == 24
+              && (operand->flags & S390_OPERAND_PCREL))
+       {
+         fixP->fx_size = 3;
+         fixP->fx_where += 3;
+         fixP->fx_offset += 3;
+         fixP->fx_r_type = BFD_RELOC_390_PC24DBL;
+       }
       else if (operand->bits == 32 && operand->shift == 16
               && (operand->flags & S390_OPERAND_PCREL))
        {
@@ -2146,10 +2217,18 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
        case BFD_RELOC_390_12:
        case BFD_RELOC_390_GOT12:
        case BFD_RELOC_390_GOTPLT12:
+       case BFD_RELOC_390_PC12DBL:
+       case BFD_RELOC_390_PLT12DBL:
+         if (fixP->fx_pcrel)
+           value++;
+
          if (fixP->fx_done)
            {
              unsigned short mop;
 
+             if (fixP->fx_pcrel)
+               value >>= 1;
+
              mop = bfd_getb16 ((unsigned char *) where);
              mop |= (unsigned short) (value & 0xfff);
              bfd_putb16 ((bfd_vma) mop, (unsigned char *) where);
@@ -2197,6 +2276,20 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
            md_number_to_chars (where, (offsetT) value >> 1, 2);
          break;
 
+       case BFD_RELOC_390_PC24DBL:
+       case BFD_RELOC_390_PLT24DBL:
+         value += 3;
+         if (fixP->fx_done)
+           {
+             unsigned int mop;
+             value >>= 1;
+
+             mop = bfd_getb32 ((unsigned char *) where - 1);
+             mop |= (unsigned int) (value & 0xffffff);
+             bfd_putb32 ((bfd_vma) mop, (unsigned char *) where - 1);
+           }
+         break;
+
        case BFD_RELOC_32:
          if (fixP->fx_pcrel)
            fixP->fx_r_type = BFD_RELOC_32_PCREL;
@@ -2360,7 +2453,7 @@ tc_s390_regname_to_dw2regnum (char *regname)
 
   if (regname[0] != 'c' && regname[0] != 'a')
     {
-      regnum = reg_name_search (pre_defined_registers, REG_NAME_CNT, regname);
+      regnum = reg_name_search (regname);
       if (regname[0] == 'f' && regnum != -1)
         regnum += 16;
     }
@@ -2374,6 +2467,6 @@ tc_s390_regname_to_dw2regnum (char *regname)
 void
 s390_elf_final_processing (void)
 {
-  if (s390_arch_size == 32 && (current_mode_mask & (1 << S390_OPCODE_ZARCH)))
+  if (set_highgprs_p)
     elf_elfheader (stdoutput)->e_flags |= EF_S390_HIGH_GPRS;
 }
This page took 0.029689 seconds and 4 git commands to generate.