This is the mail archive of the
binutils@sourceware.org
mailing list for the binutils project.
[Patch, ARM, GAS] Fix single-reg LDM/STM to LDR/STR translation.
- From: Tejas Belagod <tejas dot belagod at arm dot com>
- To: binutils at sourceware dot org
- Date: Fri, 17 Sep 2010 11:06:24 +0100
- Subject: [Patch, ARM, GAS] Fix single-reg LDM/STM to LDR/STR translation.
Hi,
The attached patch fixes the translation of single-register LDM/STM. For
instruction like:
STMIA sp, {r1}
this patch chooses to encode this as
STR r1, [sp, #0]
which can fit into a 16-bit thumb encoding. This is in line with what
the ARM ARM has to say about choosing encodings for STM:
"Encoding T2 does not support a list containing only one register. If an
STM instruction with just one register <Rt> in the list is assembled to
Thumb and encoding T1 is not available, it is assembled to the
equivalent STR<c><q> <Rt>,[<Rn>]{,#4} instruction"
For LDM/STM with single-register list that can't fit into 16-bit
encoding after translation to STR/LDR, 32-bit encoding is used. Care
also has been taken to record UNPREDICTABLE errors where necessary.
Tested with arm-none-eabi. OK?
--
Tejas Belagod
ARM.
Changelog:
gas/
2010-09-17 Tejas Belagod <tejas.belagod@arm.com>
* config/tc-arm.c (do_t_ldmstm): Add logic to handle
single-register list for ldm/stm.
gas/testsuite
2010-09-17 Tejas Belagod <tejas.belagod@arm.com>
* gas/arm/thumb2_ldmstm.d: Change single-register stmia to use
16-bit str encoding instead of str.w. Likewise for ldmia.
* gas/arm/thumb2_ldmstm.s: Change stmia comment. Add tests for
T1 ldmia-to-ldr.
diff --git a/gas/config/tc-arm.c b/gas/config/tc-arm.c
index f1ffe9c..285affe 100644
--- a/gas/config/tc-arm.c
+++ b/gas/config/tc-arm.c
@@ -9941,30 +9941,68 @@ do_t_ldmstm (void)
{
mask = 1 << inst.operands[0].reg;
- if (inst.operands[0].reg <= 7
- && (inst.instruction == T_MNEM_stmia
- ? inst.operands[0].writeback
- : (inst.operands[0].writeback
- == !(inst.operands[1].imm & mask))))
+ if (inst.operands[0].reg <= 7)
{
if (inst.instruction == T_MNEM_stmia
- && (inst.operands[1].imm & mask)
- && (inst.operands[1].imm & (mask - 1)))
- as_warn (_("value stored for r%d is UNKNOWN"),
- inst.operands[0].reg);
+ ? inst.operands[0].writeback
+ : (inst.operands[0].writeback
+ == !(inst.operands[1].imm & mask)))
+ {
+ if (inst.instruction == T_MNEM_stmia
+ && (inst.operands[1].imm & mask)
+ && (inst.operands[1].imm & (mask - 1)))
+ as_warn (_("value stored for r%d is UNKNOWN"),
+ inst.operands[0].reg);
- inst.instruction = THUMB_OP16 (inst.instruction);
- inst.instruction |= inst.operands[0].reg << 8;
- inst.instruction |= inst.operands[1].imm;
- narrow = TRUE;
+ inst.instruction = THUMB_OP16 (inst.instruction);
+ inst.instruction |= inst.operands[0].reg << 8;
+ inst.instruction |= inst.operands[1].imm;
+ narrow = TRUE;
+ }
+ else if ((inst.operands[1].imm & (inst.operands[1].imm-1)) == 0)
+ {
+ /* This means 1 register in reg list one of 3 situations:
+ 1. Instruction is stmia, but without writeback.
+ 2. lmdia without writeback, but with Rn not in
+ reglist.
+ 3. ldmia with writeback, but with Rn in reglist.
+ Case 3 is UNPREDICTABLE behaviour, so we handle
+ case 1 and 2 which can be converted into a 16-bit
+ str or ldr. The SP cases are handled below. */
+ unsigned long opcode;
+ /* First, record an error for Case 3. */
+ if (inst.operands[1].imm & mask
+ && inst.operands[0].writeback)
+ inst.error =
+ _("having the base register in the register list when "
+ "using write back is UNPREDICTABLE");
+
+ opcode = (inst.instruction == T_MNEM_stmia ? T_MNEM_str
+ : T_MNEM_ldr);
+ inst.instruction = THUMB_OP16 (opcode);
+ inst.instruction |= inst.operands[0].reg << 3;
+ inst.instruction |= (ffs (inst.operands[1].imm)-1);
+ narrow = TRUE;
+ }
}
- else if (inst.operands[0] .reg == REG_SP
- && inst.operands[0].writeback)
+ else if (inst.operands[0] .reg == REG_SP)
{
- inst.instruction = THUMB_OP16 (inst.instruction == T_MNEM_stmia
- ? T_MNEM_push : T_MNEM_pop);
- inst.instruction |= inst.operands[1].imm;
- narrow = TRUE;
+ if (inst.operands[0].writeback)
+ {
+ inst.instruction =
+ THUMB_OP16 (inst.instruction == T_MNEM_stmia
+ ? T_MNEM_push : T_MNEM_pop);
+ inst.instruction |= inst.operands[1].imm;
+ narrow = TRUE;
+ }
+ else if ((inst.operands[1].imm & (inst.operands[1].imm-1)) == 0)
+ {
+ inst.instruction =
+ THUMB_OP16 (inst.instruction == T_MNEM_stmia
+ ? T_MNEM_str_sp : T_MNEM_ldr_sp);
+ inst.instruction |= ((ffs (inst.operands[1].imm)-1) << 8);
+ narrow = TRUE;
+ }
}
}
diff --git a/gas/testsuite/gas/arm/thumb2_ldmstm.d b/gas/testsuite/gas/arm/thumb2_ldmstm.d
index 50d3ee6..f89bf16 100644
--- a/gas/testsuite/gas/arm/thumb2_ldmstm.d
+++ b/gas/testsuite/gas/arm/thumb2_ldmstm.d
@@ -48,6 +48,12 @@ Disassembly of section .text:
0[0-9a-f]+ <[^>]+> f858 9b04 ldr.w r9, \[r8\], #4
0[0-9a-f]+ <[^>]+> f8d8 9000 ldr.w r9, \[r8\]
0[0-9a-f]+ <[^>]+> f840 1b04 str.w r1, \[r0\], #4
-0[0-9a-f]+ <[^>]+> f8c0 1000 str.w r1, \[r0\]
+0[0-9a-f]+ <[^>]+> 6001 str r1, \[r0, #0\]
+0[0-9a-f]+ <[^>]+> 680a ldr r2, \[r1, #0\]
+0[0-9a-f]+ <[^>]+> 6807 ldr r7, \[r0, #0\]
+0[0-9a-f]+ <[^>]+> 9700 str r7, \[sp, #0\]
+0[0-9a-f]+ <[^>]+> 9000 str r0, \[sp, #0\]
+0[0-9a-f]+ <[^>]+> 9f00 ldr r7, \[sp, #0\]
+0[0-9a-f]+ <[^>]+> 9800 ldr r0, \[sp, #0\]
0[0-9a-f]+ <[^>]+> f848 9b04 str.w r9, \[r8\], #4
0[0-9a-f]+ <[^>]+> f8c8 9000 str.w r9, \[r8\]
diff --git a/gas/testsuite/gas/arm/thumb2_ldmstm.s b/gas/testsuite/gas/arm/thumb2_ldmstm.s
index 6cbcc17..ab7701c 100644
--- a/gas/testsuite/gas/arm/thumb2_ldmstm.s
+++ b/gas/testsuite/gas/arm/thumb2_ldmstm.s
@@ -52,6 +52,12 @@ ldmstm:
ldmia r8!, {r9} @ ldr.w r9, [r8], #4
ldmia r8, {r9} @ ldr.w r9, [r8]
stmia.w r0!, {r1} @ str.w r1, [r0], #4
- stmia r0, {r1} @ str.w r1, [r0]
+ stmia r0, {r1} @ T1 str r1, [r0]
+ ldmia r1, {r2} @ T1 ldr r2, [r1]
+ ldmia r0, {r7} @ T1 ldr r7, [r0]
+ stmia sp, {r7} @ T1 str r7, [sp]
+ stmia sp, {r0} @ T1 str r0, [sp]
+ ldmia sp, {r7} @ T1 ldr r7, [sp]
+ ldmia sp, {r0} @ T1 ldr r0, [sp]
stmia r8!, {r9} @ str.w r9, [r8], #4
stmia r8, {r9} @ str.w r9, [r8]