2005-08-05 Paul Brook <paul@codesourcery.com>
[deliverable/binutils-gdb.git] / gas / config / tc-arm.c
index 12b4addd3a839bfdc55ed6d4c0577695d98d7299..6a587f3c9ce4a2fcb7449596fd60cef233c44123 100644 (file)
@@ -535,6 +535,11 @@ typedef struct literal_pool
 
 /* Pointer to a linked list of literal pools.  */
 literal_pool * list_of_pools = NULL;
+
+/* State variables for IT block handling.  */
+static bfd_boolean current_it_mask = 0;
+static int current_cc;
+
 \f
 /* Pure syntax.         */
 
@@ -3989,14 +3994,14 @@ encode_thumb32_immediate (unsigned int val)
 {
   unsigned int a, i;
 
-  if (val <= 255)
+  if (val <= 0xff)
     return val;
 
-  for (i = 0; i < 32; i++)
+  for (i = 1; i <= 24; i++)
     {
-      a = rotate_left (val, i);
-      if (a >= 128 && a <= 255)
-       return (a & 0x7f) | (i << 7);
+      a = val >> i;
+      if ((val & ~(0xff << i)) == 0)
+       return ((val >> i) & 0x7f) | ((32 - i) << 7);
     }
 
   a = val & 0xff;
@@ -5516,7 +5521,7 @@ static void
 do_iwmmxt_wldstd (void)
 {
   inst.instruction |= inst.operands[0].reg << 12;
-  encode_arm_cp_address (1, TRUE, FALSE, BFD_RELOC_ARM_CP_OFF_IMM_S2);
+  encode_arm_cp_address (1, TRUE, FALSE, 0);
 }
 
 static void
@@ -5637,6 +5642,8 @@ encode_thumb32_shifted_operand (int i)
   unsigned int value = inst.reloc.exp.X_add_number;
   unsigned int shift = inst.operands[i].shift_kind;
 
+  constraint (inst.operands[i].immisreg,
+             _("shift by register not allowed in thumb mode"));
   inst.instruction |= inst.operands[i].reg;
   if (shift == SHIFT_RRX)
     inst.instruction |= SHIFT_ROR << 4;
@@ -5695,9 +5702,10 @@ encode_thumb32_addr_mode (int i, bfd_boolean is_t, bfd_boolean is_d)
        {
          constraint (inst.reloc.exp.X_op != O_constant,
                      _("expression too complex"));
-         constraint (inst.reloc.exp.X_add_number < 0 || inst.reloc.exp.X_add_number > 3,
+         constraint (inst.reloc.exp.X_add_number < 0
+                     || inst.reloc.exp.X_add_number > 3,
                      _("shift out of range"));
-         inst.instruction |= inst.reloc.exp.X_op << 4;
+         inst.instruction |= inst.reloc.exp.X_add_number << 4;
        }
       inst.reloc.type = BFD_RELOC_UNUSED;
     }
@@ -5748,6 +5756,7 @@ encode_thumb32_addr_mode (int i, bfd_boolean is_t, bfd_boolean is_d)
   X(adcs,  4140, eb500000),                    \
   X(add,   1c00, eb000000),                    \
   X(adds,  1c00, eb100000),                    \
+  X(adr,   000f, f20f0000),                    \
   X(and,   4000, ea000000),                    \
   X(ands,  4000, ea100000),                    \
   X(asr,   1000, fa40f000),                    \
@@ -5781,8 +5790,8 @@ encode_thumb32_addr_mode (int i, bfd_boolean is_t, bfd_boolean is_d)
   X(negs,  4240, f1d00000), /* rsbs #0 */      \
   X(orr,   4300, ea400000),                    \
   X(orrs,  4300, ea500000),                    \
-  X(pop,   bc00, e8ad0000), /* ldmia sp!,... */        \
-  X(push,  b400, e8bd0000), /* stmia sp!,... */        \
+  X(pop,   bc00, e8bd0000), /* ldmia sp!,... */        \
+  X(push,  b400, e92d0000), /* stmdb sp!,... */        \
   X(rev,   ba00, fa90f080),                    \
   X(rev16, ba40, fa90f090),                    \
   X(revsh, bac0, fa90f0b0),                    \
@@ -5859,6 +5868,8 @@ do_t_add_sub (void)
     {
       if (!inst.operands[2].isreg)
        {
+         /* ??? Convert large immediates to addw/subw.  */
+         /* ??? 16-bit adds with small immediates.  */
          /* For an immediate, we always generate a 32-bit opcode;
             section relaxation will shrink it later if possible.  */
          inst.instruction = THUMB_OP32 (inst.instruction);
@@ -5873,11 +5884,20 @@ do_t_add_sub (void)
          /* See if we can do this with a 16-bit instruction.  */
          if (!inst.operands[2].shifted && inst.size_req != 4)
            {
-             if (Rd <= 7 && Rn <= 7 && Rn <= 7
-                 && (inst.instruction == T_MNEM_adds
-                     || inst.instruction == T_MNEM_subs))
+             bfd_boolean narrow;
+
+             if (inst.instruction == T_MNEM_adds
+                 || inst.instruction == T_MNEM_subs)
+               narrow = (current_it_mask == 0);
+             else
+               narrow = (current_it_mask != 0);
+             if (Rd > 7 || Rs > 7 || Rn > 7)
+               narrow = FALSE;
+
+             if (narrow)
                {
-                 inst.instruction = (inst.instruction == T_MNEM_adds
+                 inst.instruction = ((inst.instruction == T_MNEM_adds
+                                      || inst.instruction == T_MNEM_add)
                                      ? T_OPCODE_ADD_R3
                                      : T_OPCODE_SUB_R3);
                  inst.instruction |= Rd | (Rs << 3) | (Rn << 6);
@@ -5963,11 +5983,24 @@ do_t_add_sub (void)
 static void
 do_t_adr (void)
 {
-  inst.reloc.type = BFD_RELOC_ARM_THUMB_ADD;
-  inst.reloc.exp.X_add_number -= 4; /* PC relative adjust.  */
-  inst.reloc.pc_rel = 1;
+  if (unified_syntax && inst.size_req != 2)
+    {
+      /* Always generate a 32-bit opcode;
+        section relaxation will shrink it later if possible.  */
+      inst.instruction = THUMB_OP32 (inst.instruction);
+      inst.instruction |= inst.operands[0].reg << 8;
+      inst.reloc.type = BFD_RELOC_ARM_T32_ADD_PC12;
+      inst.reloc.pc_rel = 1;
+    }
+  else
+    {
+      inst.instruction = THUMB_OP16 (inst.instruction);
+      inst.reloc.type = BFD_RELOC_ARM_THUMB_ADD;
+      inst.reloc.exp.X_add_number -= 4; /* PC relative adjust.  */
+      inst.reloc.pc_rel = 1;
 
-  inst.instruction |= inst.operands[0].reg << 4;
+      inst.instruction |= inst.operands[0].reg << 4;
+    }
 }
 
 /* Arithmetic instructions for which there is just one 16-bit
@@ -6001,10 +6034,22 @@ do_t_arit3 (void)
        }
       else
        {
+         bfd_boolean narrow;
+
          /* See if we can do this with a 16-bit instruction.  */
-         if (THUMB_SETS_FLAGS (inst.instruction)
-             && !inst.operands[2].shifted
-             && inst.size_req != 4
+         if (THUMB_SETS_FLAGS (inst.instruction))
+           narrow = current_it_mask == 0;
+         else
+           narrow = current_it_mask != 0;
+
+         if (Rd > 7 || Rn > 7 || Rs > 7)
+           narrow = FALSE;
+         if (inst.operands[2].shifted)
+           narrow = FALSE;
+         if (inst.size_req == 4)
+           narrow = FALSE;
+
+         if (narrow
              && Rd == Rs)
            {
              inst.instruction = THUMB_OP16 (inst.instruction);
@@ -6072,10 +6117,22 @@ do_t_arit3c (void)
        }
       else
        {
+         bfd_boolean narrow;
+
          /* See if we can do this with a 16-bit instruction.  */
-         if (THUMB_SETS_FLAGS (inst.instruction)
-             && !inst.operands[2].shifted
-             && inst.size_req != 4)
+         if (THUMB_SETS_FLAGS (inst.instruction))
+           narrow = current_it_mask == 0;
+         else
+           narrow = current_it_mask != 0;
+
+         if (Rd > 7 || Rn > 7 || Rs > 7)
+           narrow = FALSE;
+         if (inst.operands[2].shifted)
+           narrow = FALSE;
+         if (inst.size_req == 4)
+           narrow = FALSE;
+
+         if (narrow)
            {
              if (Rd == Rs)
                {
@@ -6346,21 +6403,26 @@ static void
 do_t_it (void)
 {
   unsigned int cond = inst.operands[0].imm;
+
+  current_it_mask = (inst.instruction & 0xf) | 0x10;
+  current_cc = cond;
+
+  /* If the condition is a negative condition, invert the mask.  */
   if ((cond & 0x1) == 0x0)
     {
       unsigned int mask = inst.instruction & 0x000f;
-      inst.instruction &= 0xfff0;
 
       if ((mask & 0x7) == 0)
        /* no conversion needed */;
       else if ((mask & 0x3) == 0)
-       mask = (~(mask & 0x8) & 0x8) | 0x4;
-      else if ((mask & 1) == 0)
-       mask = (~(mask & 0xC) & 0xC) | 0x2;
+       mask ^= 0x8;
+      else if ((mask & 0x1) == 0)
+       mask ^= 0xC;
       else
-       mask = (~(mask & 0xE) & 0xE) | 0x1;
+       mask ^= 0xE;
 
-      inst.instruction |= (mask & 0xF);
+      inst.instruction &= 0xfff0;
+      inst.instruction |= mask;
     }
 
   inst.instruction |= cond << 4;
@@ -6948,39 +7010,52 @@ do_t_pld (void)
 static void
 do_t_push_pop (void)
 {
+  unsigned mask;
+  
   constraint (inst.operands[0].writeback,
              _("push/pop do not support {reglist}^"));
   constraint (inst.reloc.type != BFD_RELOC_UNUSED,
              _("expression too complex"));
 
-  if ((inst.operands[0].imm & ~0xff) == 0)
+  mask = inst.operands[0].imm;
+  if ((mask & ~0xff) == 0)
     inst.instruction = THUMB_OP16 (inst.instruction);
   else if ((inst.instruction == T_MNEM_push
-           && (inst.operands[0].imm & ~0xff) == 1 << REG_LR)
+           && (mask & ~0xff) == 1 << REG_LR)
           || (inst.instruction == T_MNEM_pop
-              && (inst.operands[0].imm & ~0xff) == 1 << REG_PC))
+              && (mask & ~0xff) == 1 << REG_PC))
     {
       inst.instruction = THUMB_OP16 (inst.instruction);
       inst.instruction |= THUMB_PP_PC_LR;
-      inst.operands[0].imm &= 0xff;
+      mask &= 0xff;
     }
   else if (unified_syntax)
     {
-      if (inst.operands[1].imm & (1 << 13))
-       as_warn (_("SP should not be in register list"));
+      if (mask & (1 << 13))
+       inst.error =  _("SP not allowed in register list");
       if (inst.instruction == T_MNEM_push)
        {
-         if (inst.operands[1].imm & (1 << 15))
-           as_warn (_("PC should not be in register list"));
+         if (mask & (1 << 15))
+           inst.error = _("PC not allowed in register list");
        }
       else
        {
-         if (inst.operands[1].imm & (1 << 14)
-             && inst.operands[1].imm & (1 << 15))
-           as_warn (_("LR and PC should not both be in register list"));
+         if (mask & (1 << 14)
+             && mask & (1 << 15))
+           inst.error = _("LR and PC should not both be in register list");
        }
-
-      inst.instruction = THUMB_OP32 (inst.instruction);
+      if ((mask & (mask - 1)) == 0)
+       {
+         /* Single register push/pop implemented as str/ldr.  */
+         if (inst.instruction == T_MNEM_push)
+           inst.instruction = 0xf84d0d04; /* str reg, [sp, #-4]! */
+         else
+           inst.instruction = 0xf85d0b04; /* ldr reg, [sp], #4 */
+         mask = ffs(mask) - 1;
+         mask <<= 12;
+       }
+      else
+       inst.instruction = THUMB_OP32 (inst.instruction);
     }
   else
     {
@@ -6988,7 +7063,7 @@ do_t_push_pop (void)
       return;
     }
 
-  inst.instruction |= inst.operands[0].imm;
+  inst.instruction |= mask;
 }
 
 static void
@@ -7657,12 +7732,35 @@ md_assemble (char *str)
          return;
        }
 
+      /* Check conditional suffixes.  */
+      if (current_it_mask)
+       {
+         int cond;
+         cond = current_cc ^ ((current_it_mask >> 4) & 1) ^ 1;
+         if (cond != inst.cond)
+           {
+             as_bad (_("incorrect condition in IT block"));
+             return;
+           }
+         current_it_mask <<= 1;
+         current_it_mask &= 0x1f;
+       }
+      else if (inst.cond != COND_ALWAYS && opcode->tencode != do_t_branch)
+       {
+         as_bad (_("thumb conditional instrunction not in IT block"));
+         return;
+       }
+
       mapping_state (MAP_THUMB);
       inst.instruction = opcode->tvalue;
 
       if (!parse_operands (p, opcode->operands))
        opcode->tencode ();
 
+      /* Clear current_it_mask at the end of an IT block.  */
+      if (current_it_mask == 0x10)
+       current_it_mask = 0;
+
       if (!inst.error)
        {
          assert (inst.instruction < 0xe800 || inst.instruction > 0xffff);
@@ -8205,7 +8303,7 @@ static const struct asm_opcode insns[] =
  TCE(bl,       b000000, f000f800, 1, (EXPr),        branch, t_branch23),
 
   /* Pseudo ops.  */
TCE(adr,      28f0000, 000f,     2, (RR, EXP),     adr,  t_adr),
tCE(adr,      28f0000, adr,      2, (RR, EXP),     adr,  t_adr),
   C3(adrl,     28f0000,           2, (RR, EXP),     adrl),
  tCE(nop,      1a00000, nop,      1, (oI255c),      nop,  t_nop),
 
@@ -8241,11 +8339,11 @@ static const struct asm_opcode insns[] =
  TC3(strt,     4200000, f8400e00, 2, (RR, ADDR),    ldstt, t_ldstt),
  TC3(strbt,    4600000, f8200e00, 2, (RR, ADDR),    ldstt, t_ldstt),
 
- TC3(stmdb,    9000000, e9100000, 2, (RRw, REGLST), ldmstm, t_ldmstm),
- TC3(stmfd,     9000000, e9100000, 2, (RRw, REGLST), ldmstm, t_ldmstm),
+ TC3(stmdb,    9000000, e9000000, 2, (RRw, REGLST), ldmstm, t_ldmstm),
+ TC3(stmfd,     9000000, e9000000, 2, (RRw, REGLST), ldmstm, t_ldmstm),
 
- TC3(ldmdb,    9100000, e9000000, 2, (RRw, REGLST), ldmstm, t_ldmstm),
- TC3(ldmea,    9100000, e9000000, 2, (RRw, REGLST), ldmstm, t_ldmstm),
+ TC3(ldmdb,    9100000, e9100000, 2, (RRw, REGLST), ldmstm, t_ldmstm),
+ TC3(ldmea,    9100000, e9100000, 2, (RRw, REGLST), ldmstm, t_ldmstm),
 
  /* V1 instructions with no Thumb analogue at all.  */
   CE(rsc,      0e00000,           3, (RR, oRR, SH), arit),
@@ -10010,6 +10108,7 @@ md_pcrel_from_section (fixS * fixP, segT seg)
 
     case BFD_RELOC_ARM_THUMB_OFFSET:
     case BFD_RELOC_ARM_T32_OFFSET_IMM:
+    case BFD_RELOC_ARM_T32_ADD_PC12:
       return (base + 4) & ~3;
 
       /* Thumb branches are simply offset by +4.  */
@@ -10532,6 +10631,7 @@ md_apply_fix (fixS *    fixP,
 
     case BFD_RELOC_ARM_T32_IMMEDIATE:
     case BFD_RELOC_ARM_T32_IMM12:
+    case BFD_RELOC_ARM_T32_ADD_PC12:
       /* We claim that this fixup has been processed here,
         even if in fact we generate an error because we do
         not have a reloc for it, so tc_gen_reloc will reject it.  */
@@ -10550,17 +10650,23 @@ md_apply_fix (fixS *  fixP,
       newval <<= 16;
       newval |= md_chars_to_number (buf+2, THUMB_SIZE);
 
-      if (fixP->fx_r_type == BFD_RELOC_ARM_T32_IMM12)
+      /* FUTURE: Implement analogue of negate_data_op for T32.  */
+      if (fixP->fx_r_type == BFD_RELOC_ARM_T32_IMMEDIATE)
+       newimm = encode_thumb32_immediate (value);
+      else
        {
+         /* 12 bit immediate for addw/subw.  */
+         if (value < 0)
+           {
+             value = -value;
+             newval ^= 0x00a00000;
+           }
          if (value > 0xfff)
            newimm = (unsigned int) FAIL;
          else
            newimm = value;
        }
-      else
-       newimm = encode_thumb32_immediate (value);
 
-      /* FUTURE: Implement analogue of negate_data_op for T32.  */
       if (newimm == (unsigned int)FAIL)
        {
          as_bad_where (fixP->fx_file, fixP->fx_line,
This page took 0.030572 seconds and 4 git commands to generate.