/* 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. */
{
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;
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
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;
{
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;
}
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), \
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), \
{
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);
/* 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);
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
}
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);
}
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)
{
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;
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
{
return;
}
- inst.instruction |= inst.operands[0].imm;
+ inst.instruction |= mask;
}
static void
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);
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),
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),
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. */
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. */
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,