Add support for armv5 architecture
authorNick Clifton <nickc@redhat.com>
Mon, 5 Jul 1999 07:39:01 +0000 (07:39 +0000)
committerNick Clifton <nickc@redhat.com>
Mon, 5 Jul 1999 07:39:01 +0000 (07:39 +0000)
Add ADRL pseudo op.

gas/ChangeLog
gas/config/tc-arm.c
gas/doc/c-arm.texi

index e90d867..d7db6e6 100644 (file)
@@ -1,3 +1,26 @@
+1999-07-05  Nick Clifton  <nickc@cygnus.com>
+
+       * config/tc-arm.c (ARM_EXT_V5): Define.
+       (ARM_ARCH_V5, ARM_ARCH_V5T): Define.
+       (md_begin): Detect ARM v5 architectures.
+       (md_parse_option): Accept arm v5 specification.
+       (md_show_usage): Documment -marmv5 switch.
+
+       * doc/c-arm.texi: Document -marmv5 command line option.
+       
+       * config/tc-arm.c (do_adrl): New function.  Implement ADRL pseudo
+       op.
+       (validate_immediate_twopart): New function.  Determine if a
+       constant can be computed by two ADD instructions.
+       (output_inst): Remove its command line parameter - it was never
+       used.
+       (md_apply_fix3): Support BFD_RELOC_ARM_ADRL_IMMEDIATE, used to
+       implememt the ADRL pseudo op.
+       (tc_gen_reloc): Generate a suitable error message if an ADRL
+       instruction tries to generate a real reloc.
+
+       * doc/c-arm.texi: Document NOP, ADR and ADRL pseudo ops.
+
 Thu Jul  1 15:33:10 1999  Jeffrey A Law  (law@cygnus.com)
 
        * config/tc-hppa.c (pa_ip): Convert the opcode and all completers
index 9900b31..93c6d82 100644 (file)
 #define ARM_LONGMUL    0x00000010      /* allow long multiplies */
 #define ARM_HALFWORD    0x00000020     /* allow half word loads */
 #define ARM_THUMB       0x00000040     /* allow BX instruction  */
+#define ARM_EXT_V5     0x00000080      /* allow CLZ etc         */
 
-#define ARM_ARCHv4     (ARM_7 | ARM_LONGMUL | ARM_HALFWORD)
+/* Architectures are the sum of the base and extensions */
+#define ARM_ARCH_V4    (ARM_7 | ARM_LONGMUL | ARM_HALFWORD)
+#define ARM_ARCH_V4T   (ARM_ARCH_V4 | ARM_THUMB)
+#define ARM_ARCH_V5    (ARM_ARCH_V4 | ARM_EXT_V5)
+#define ARM_ARCH_V5T   (ARM_ARCH_V5 | ARM_THUMB)
 
 /* Some useful combinations:  */
 #define ARM_ANY                0x00ffffff
-#define ARM_2UP                0x00fffffe
+#define ARM_2UP                (ARM_ANY - ARM_1)
 #define ARM_ALL                ARM_2UP         /* Not arm1 only */
 #define ARM_3UP                0x00fffffc
 #define ARM_6UP                0x00fffff8      /* Includes ARM7 */
@@ -73,7 +78,7 @@
      
 #ifndef CPU_DEFAULT
 #if defined __thumb__
-#define CPU_DEFAULT (ARM_ARCHv4 | ARM_THUMB)
+#define CPU_DEFAULT (ARM_ARCH_V4 | ARM_THUMB)
 #else
 #define CPU_DEFAULT ARM_ALL
 #endif
@@ -419,6 +424,7 @@ static void do_branch               PARAMS ((char *operands, unsigned long flags));
 static void do_swi             PARAMS ((char *operands, unsigned long flags));
 /* Pseudo Op codes */
 static void do_adr             PARAMS ((char *operands, unsigned long flags));
+static void do_adrl            PARAMS ((char * operands, unsigned long flags));
 static void do_nop             PARAMS ((char *operands, unsigned long flags));
 /* ARM 2 */
 static void do_mul             PARAMS ((char *operands, unsigned long flags));
@@ -455,6 +461,7 @@ static void symbol_locate   PARAMS ((symbolS *, CONST char *, segT,
                                         valueT, fragS *));
 static int add_to_lit_pool     PARAMS ((void));
 static unsigned validate_immediate     PARAMS ((unsigned));
+static unsigned validate_immediate_twopart     PARAMS ((unsigned int, unsigned int *));
 static int validate_offset_imm PARAMS ((int, int));
 static void opcode_select      PARAMS ((int));
 static void end_of_line                PARAMS ((char *));
@@ -484,7 +491,7 @@ static void thumb_mov_compare       PARAMS ((char *, int));
 static void set_constant_flonums       PARAMS ((void));
 static valueT md_chars_to_number       PARAMS ((char *, int));
 static void insert_reg_alias   PARAMS ((char *, int));
-static void output_inst                PARAMS ((char *));
+static void output_inst                PARAMS ((void));
 #ifdef OBJ_ELF
 static bfd_reloc_code_real_type        arm_parse_reloc PARAMS ((void));
 #endif
@@ -540,6 +547,7 @@ static CONST struct asm_opcode insns[] =
 
 /* Pseudo ops */
   {"adr",   0x028f0000, NULL,   NULL,        ARM_ANY,      do_adr},
+  {"adrl",  0x028f0000, NULL,   NULL,        ARM_ANY,      do_adrl},
   {"nop",   0x01a00000, NULL,   NULL,        ARM_ANY,      do_nop},
 
 /* ARM 2 multiplies */
@@ -870,8 +878,8 @@ static CONST struct reg_entry reg_table[] =
   {NULL, 0}
 };
 
-#define bad_args _("Bad arguments to instruction");
-#define bad_pc _("r15 not allowed here");
+#define bad_args       _("Bad arguments to instruction");
+#define bad_pc                 _("r15 not allowed here");
 
 static struct hash_control * arm_ops_hsh = NULL;
 static struct hash_control * arm_tops_hsh = NULL;
@@ -1079,6 +1087,47 @@ validate_immediate (val)
   return FAIL;
 }
 
+/* Check to see if an immediate can be computed as two seperate immediate
+   values, added together.  We already know that this value cannot be
+   computed by just one ARM instruction.  */
+
+static unsigned int
+validate_immediate_twopart (val, highpart)
+     unsigned int val;
+     unsigned int * highpart;
+{
+  unsigned int a;
+  unsigned int i;
+  
+  for (i = 0; i < 32; i += 2)
+    if (((a = rotate_left (val, i)) & 0xff) != 0)
+      {
+       if (a & 0xff00)
+         {
+           if (a & ~ 0xffff)
+             continue;
+           * highpart = (a  >> 8) | ((i + 24) << 7);
+         }
+       else if (a & 0xff0000)
+         {
+           if (a & 0xff000000)
+             continue;
+
+           * highpart = (a >> 16) | ((i + 16) << 7);
+         }
+       else
+         {
+           assert (a & 0xff000000);
+
+           * highpart = (a >> 24) | ((i + 8) << 7);
+         }
+
+       return (a & 0xff) | (i << 7);
+      }
+  
+  return FAIL;
+}
+
 static int
 validate_offset_imm (val, hwse)
      int val;
@@ -2607,6 +2656,41 @@ do_adr (str, flags)
 }
 
 static void
+do_adrl (str, flags)
+     char *        str;
+     unsigned long flags;
+{
+  /* This is a pseudo-op of the form "adrl rd, label" to be converted
+     into a relative address of the form:
+       add rd, pc, #low(label-.-8)"
+       add rd, rd, #high(label-.-8)"   */
+
+  while (* str == ' ')
+    str ++;
+
+  if (reg_required_here (& str, 12) == FAIL
+      || skip_past_comma (& str) == FAIL
+      || my_get_expression (& inst.reloc.exp, & str))
+    {
+      if (!inst.error)
+       inst.error = bad_args;
+      return;
+    }
+  
+  end_of_line (str);
+  
+  /* Frag hacking will turn this into a sub instruction if the offset turns
+     out to be negative.  */
+  inst.reloc.type              = BFD_RELOC_ARM_ADRL_IMMEDIATE;
+  inst.reloc.exp.X_add_number -= 8; /* PC relative adjust */
+  inst.reloc.pc_rel            = 1;
+  inst.instruction            |= flags;
+  inst.size                    = INSN_SIZE * 2;
+  
+  return;
+}
+
+static void
 do_cmp (str, flags)
      char *        str;
      unsigned long flags;
@@ -4984,9 +5068,13 @@ md_begin ()
     /* Catch special cases */
     if (cpu_variant != (FPU_DEFAULT | CPU_DEFAULT))
       {
-       if (cpu_variant & ARM_THUMB)
+       if (cpu_variant & (ARM_EXT_V5 & ARM_THUMB))
+         mach = bfd_mach_arm_5T;
+       else if (cpu_variant & ARM_EXT_V5)
+         mach = bfd_mach_arm_5;
+       else if (cpu_variant & ARM_THUMB)
          mach = bfd_mach_arm_4T;
-       else if ((cpu_variant & ARM_ARCHv4) == ARM_ARCHv4)
+       else if ((cpu_variant & ARM_ARCH_V4) == ARM_ARCH_V4)
          mach = bfd_mach_arm_4;
        else if (cpu_variant & ARM_LONGMUL)
          mach = bfd_mach_arm_3M;
@@ -5326,7 +5414,51 @@ md_apply_fix3 (fixP, val, seg)
       md_number_to_chars (buf, (valueT) newimm, INSN_SIZE);
       break;
 
-     case BFD_RELOC_ARM_OFFSET_IMM:
+    case BFD_RELOC_ARM_ADRL_IMMEDIATE:
+      {
+       unsigned int highpart = 0;
+       unsigned int newinsn  = 0xe1a00000; /* nop */
+       newimm = validate_immediate (value);
+       temp = md_chars_to_number (buf, INSN_SIZE);
+
+       /* If the instruction will fail, see if we can fix things up by
+          changing the opcode.  */
+       if (newimm == (unsigned int) FAIL
+           && (newimm = negate_data_op (& temp, value)) == (unsigned int) FAIL)
+         {
+           /* No ?  OK - try using two ADD instructions to generate the value.  */
+           newimm = validate_immediate_twopart (value, & highpart);
+
+           /* Yes - then make sure that the second instruction is also an add.  */
+           if (newimm != (unsigned int) FAIL)
+             newinsn = temp;
+           /* Still No ?  Try using a negated value.  */
+           else if (validate_immediate_twopart (- value, & highpart) != (unsigned int) FAIL)
+               temp = newinsn = (temp & OPCODE_MASK) | OPCODE_SUB << DATA_OP_SHIFT;
+           /* Otherwise - give up.  */
+           else
+             {
+               as_bad_where (fixP->fx_file, fixP->fx_line,
+                             _("Unable to compute ADRL instructions for PC offset of 0x%x\n"), value);
+               break;
+             }
+
+           /* Replace the first operand in the 2nd instruction (which is the PC)
+              with the destination register.  We have already added in the PC in the
+              first instruction and we do not want to do it again.  */
+           newinsn &= ~ 0xf0000;
+           newinsn |= ((newinsn & 0x0f000) << 4);
+         }
+
+       newimm |= (temp & 0xfffff000);
+       md_number_to_chars (buf, (valueT) newimm, INSN_SIZE);
+
+       highpart |= (newinsn & 0xfffff000);
+       md_number_to_chars (buf + INSN_SIZE, (valueT) highpart, INSN_SIZE);
+      }
+      break;
+
+    case BFD_RELOC_ARM_OFFSET_IMM:
       sign = value >= 0;
       if ((value = validate_offset_imm (value, 0)) == FAIL)
         {
@@ -5816,6 +5948,12 @@ tc_gen_reloc (section, fixp)
                    fixp->fx_r_type);
       return NULL;
 
+    case BFD_RELOC_ARM_ADRL_IMMEDIATE:
+      as_bad_where (fixp->fx_file, fixp->fx_line,
+                   _("ADRL used for a symbol not defined in the same file"),
+                   fixp->fx_r_type);
+      return NULL;
+
     case BFD_RELOC_ARM_OFFSET_IMM:
       as_bad_where (fixp->fx_file, fixp->fx_line,
                    _("Internal_relocation (type %d) not fixed up (OFFSET_IMM)"),
@@ -5880,8 +6018,7 @@ md_estimate_size_before_relax (fragP, segtype)
 }
 
 static void
-output_inst (str)
-     char * str;
+output_inst PARAMS ((void))
 {
   char * to = NULL;
     
@@ -5896,7 +6033,13 @@ output_inst (str)
     {
       assert (inst.size == (2 * THUMB_SIZE));
       md_number_to_chars (to, inst.instruction >> 16, THUMB_SIZE);
-      md_number_to_chars (to + 2, inst.instruction, THUMB_SIZE);
+      md_number_to_chars (to + THUMB_SIZE, inst.instruction, THUMB_SIZE);
+    }
+  else if (inst.size > INSN_SIZE)
+    {
+      assert (inst.size == (2 * INSN_SIZE));
+      md_number_to_chars (to, inst.instruction, INSN_SIZE);
+      md_number_to_chars (to + INSN_SIZE, inst.instruction, INSN_SIZE);
     }
   else
     md_number_to_chars (to, inst.instruction, inst.size);
@@ -5936,7 +6079,7 @@ md_assemble (str)
 
   if (*str == ' ')
     str++;                     /* Skip leading white space */
-    
+  
   /* Scan up to the end of the op-code, which must end in white space or
      end of string.  */
   for (start = p = str; *p != '\0'; p++)
@@ -5951,24 +6094,25 @@ md_assemble (str)
 
   if (thumb_mode)
     {
-      CONST struct thumb_opcode *opcode;
+      CONST struct thumb_opcode * opcode;
 
       c = *p;
       *p = '\0';
       opcode = (CONST struct thumb_opcode *) hash_find (arm_tops_hsh, str);
       *p = c;
+      
       if (opcode)
        {
          inst.instruction = opcode->value;
          inst.size = opcode->size;
          (*opcode->parms)(p);
-         output_inst (start);
+         output_inst ();
          return;
        }
     }
   else
     {
-      CONST struct asm_opcode *opcode;
+      CONST struct asm_opcode * opcode;
 
       inst.size = INSN_SIZE;
       /* p now points to the end of the opcode, probably white space, but we
@@ -5982,10 +6126,11 @@ md_assemble (str)
          *q = '\0';
          opcode = (CONST struct asm_opcode *) hash_find (arm_ops_hsh, str);
          *q = c;
+         
          if (opcode && opcode->template)
            {
              unsigned long flag_bits = 0;
-             char *r;
+             char * r;
 
              /* Check that this instruction is supported for this CPU */
              if ((opcode->variants & cpu_variant) == 0)
@@ -6002,7 +6147,7 @@ md_assemble (str)
                      inst.instruction |= COND_ALWAYS;
                      (*opcode->parms)(q, 0);
                    }
-                 output_inst (start);
+                 output_inst ();
                  return;
                }
 
@@ -6087,7 +6232,7 @@ _("Warning: Use of the 'nv' conditional is deprecated\n"));
                }
 
              (*opcode->parms) (p, flag_bits);
-             output_inst (start);
+             output_inst ();
              return;
            }
 
@@ -6174,9 +6319,10 @@ _("Warning: Use of the 'nv' conditional is deprecated\n"));
  *            -m[arm]3                Arm 3 processor
  *            -m[arm]6[xx],           Arm 6 processors
  *            -m[arm]7[xx][t][[d]m]   Arm 7 processors
- *            -m8[10]                 Arm 8 processors
- *            -m9[20][tdmi]           Arm 9 processors
+ *            -m[arm]8[10]            Arm 8 processors
+ *            -m[arm]9[20][tdmi]      Arm 9 processors
  *            -mstrongarm[110[0]]     StrongARM processors
+ *            -m[arm]v[2345]         Arm architecures
  *            -mall                   All (except the ARM1)
  *    FP variants:
  *            -mfpa10, -mfpa11        FPA10 and 11 co-processor instructions
@@ -6265,7 +6411,7 @@ md_parse_option (c, arg)
             }
           else if (streq (str, "thumb-interwork"))
             {
-              cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_THUMB | ARM_ARCHv4;
+              cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_THUMB | ARM_ARCH_V4;
 #if defined OBJ_COFF || defined OBJ_ELF
               support_interwork = true;
 #endif
@@ -6399,7 +6545,7 @@ md_parse_option (c, arg)
                 switch (* str)
                   {
                   case 't':
-                    cpu_variant |= (ARM_THUMB | ARM_ARCHv4);
+                    cpu_variant |= (ARM_THUMB | ARM_ARCH_V4);
                     break;
 
                   case 'm':
@@ -6426,20 +6572,20 @@ md_parse_option (c, arg)
 
            case '8':
              if (streq (str, "8") || streq (str, "810"))
-               cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_8 | ARM_ARCHv4 | ARM_LONGMUL;
+               cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_8 | ARM_ARCH_V4 | ARM_LONGMUL;
              else
                goto bad;
              break;
              
            case '9':
              if (streq (str, "9"))
-               cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_9 | ARM_ARCHv4 | ARM_LONGMUL | ARM_THUMB;
+               cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_9 | ARM_ARCH_V4 | ARM_LONGMUL | ARM_THUMB;
              else if (streq (str, "920"))
-               cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_9 | ARM_ARCHv4 | ARM_LONGMUL;
+               cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_9 | ARM_ARCH_V4 | ARM_LONGMUL;
              else if (streq (str, "920t"))
-               cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_9 | ARM_ARCHv4 | ARM_LONGMUL | ARM_THUMB;
+               cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_9 | ARM_ARCH_V4 | ARM_LONGMUL | ARM_THUMB;
              else if (streq (str, "9tdmi"))
-               cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_9 | ARM_ARCHv4 | ARM_LONGMUL | ARM_THUMB;
+               cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_9 | ARM_ARCH_V4 | ARM_LONGMUL | ARM_THUMB;
              else
                goto bad;
              break;
@@ -6448,7 +6594,7 @@ md_parse_option (c, arg)
              if (streq (str, "strongarm")
                  || streq (str, "strongarm110")
                  || streq (str, "strongarm1100"))
-               cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_8 | ARM_ARCHv4 | ARM_LONGMUL;
+               cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_8 | ARM_ARCH_V4 | ARM_LONGMUL;
              else
                goto bad;
              break;
@@ -6478,7 +6624,18 @@ md_parse_option (c, arg)
                  break;
                  
                case '4':
-                 cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_ARCHv4;
+                 cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_ARCH_V4;
+                 
+                 switch (*++str)
+                   {
+                   case 't': cpu_variant |= ARM_THUMB; break;
+                   case 0:   break;
+                   default:  as_bad (_("Invalid architecture variant -m%s"), arg); break;
+                   }
+                 break;
+                 
+               case '5':
+                 cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_ARCH_V5;
                  
                  switch (*++str)
                    {
@@ -6521,7 +6678,7 @@ md_show_usage (fp)
 _("\
  ARM Specific Assembler Options:\n\
   -m[arm][<processor name>] select processor variant\n\
-  -m[arm]v[2|2a|3|3m|4|4t]  select architecture variant\n\
+  -m[arm]v[2|2a|3|3m|4|4t|5]select architecture variant\n\
   -mthumb                   only allow Thumb instructions\n\
   -mthumb-interwork         mark the assembled code as supporting interworking\n\
   -mall                     allow any instruction\n\
index b94fb2a..1538ac9 100644 (file)
 @cindex options for ARM (none)
 @table @code
 @cindex @code{-marm} command line option, ARM
-@item -marm @var{[2|250|3|6|60|600|610|620|7|7m|7d|7dm|7di|7dmi|70|700|700i|710|710c|7100|7500|7500fe|7tdmi|8|810|9|9tdmistrongarm|strongarm110|strongarm1100]}
+@item -marm @var{[2|250|3|6|60|600|610|620|7|7m|7d|7dm|7di|7dmi|70|700|700i|710|710c|7100|7500|7500fe|7tdmi|8|810|9|9tdmi|920||strongarm|strongarm110|strongarm1100]}
 This option specifies the target processor.  The assembler will issue an
 error message if an attempt is made to assemble an instruction which
 will not execute on the target processor.
 @cindex @code{-marmv} command line option, ARM
-@item -marmv @var{[2|2a|3|3m|4|4t]}
+@item -marmv @var{[2|2a|3|3m|4|4t|5|5t]}
 This option specifies the target architecture.  The assembler will issue
 an error message if an attempt is made to assemble an instruction which
 will not execute on the target architecture.
@@ -184,13 +184,23 @@ This is a synonym for .ltorg.
 
 @cindex ARM opcodes
 @cindex opcodes for ARM
-@code{@value{AS}} implements all the standard ARM opcodes.
+@code{@value{AS}} implements all the standard ARM opcodes.  It also
+implements several pseudo opcodes, including several synthetic load
+instructions. 
 
-*TODO* Document the pseudo-ops (adr, nop)
+@table @code
+
+@cindex @code{NOP} pseudo op, ARM
+@item NOP
+@smallexample
+  nop
+@end smallexample
 
-GAS for the ARM supports a synthetic register load instruction whoes
-syntax is: 
+This pseudo op will always evaluate to a legal ARM instruction that does
+nothing.  Currently it will evaluate to MOV r0, r0.
 
+@cindex @code{LDR reg,=<label>} pseudo op, ARM
+@item LDR 
 @smallexample
   ldr <register> , = <expression>
 @end smallexample
@@ -201,6 +211,37 @@ constant can be generated by either of these instructions.  Otherwise
 the constant will be placed into the nearest literal pool (if it not
 already there) and a PC relative LDR instruction will be generated.
 
+@cindex @code{ADR reg,<label>} pseudo op, ARM
+@item ADR
+@smallexample
+  adr <register> <label>
+@end smallexample
+
+This instruction will load the address of @var{label} into the indicated
+register.  The instruction will evaluate to a PC relative ADD or SUB
+instruction depending upon where the label is located.  If the label is
+out of range, or if it is not defined in the same file (and section) as
+the ADR instruction, then an error will be generated.  This instruction
+will not make use of the literal pool.
+
+@cindex @code{ADRL reg,<label>} pseudo op, ARM
+@item ADRL 
+@smallexample
+  adrl <register> <label>
+@end smallexample
+
+This instruction will load the address of @var{label} into the indicated
+register.  The instruction will evaluate to one or two a PC relative ADD
+or SUB instructions depending upon where the label is located.  If a
+second instruction is not needed a NOP instruction will be generated in
+its place, so that this instruction is always 8 bytes long.
+
+If the label is out of range, or if it is not defined in the same file
+(and section) as the ADRL instruction, then an error will be generated.
+This instruction will not make use of the literal pool.
+
+@end table
+
 For information on the ARM or Thumb instruction sets, see @cite{ARM
 Software Development Toolkit Reference Manual}, Advanced RISC Machines
 Ltd.