switch()-statements on ARM (-mthumb -fpic) [was: How to take the address of a label_ref?]
Adrian von Bidder
avbidder@acter.ch
Tue Nov 13 15:03:00 GMT 2001
Philip Blundell wrote:
> I think what he's actually trying to do is generate "add rN, pc, #(.Lb - . -
> 8)".
Bingo. I'm using this in my thumb/pic switch statement. Please have a
look at my patch - I've tested it successfully with some trivial
hello-switch programs, but I just hope I did not disturb anything else.
A switch now gets translated to (with -mthumb -fpic, others shouldn't
change):
sub r0, r0, <lower-bound>
cmp r0, <upper-bound>
bhi <default case>
adr r1, .L18
lsl r2, r0, #2
ldr r3, [r2, r1]
add r0, r1, r3
mov pc, r0
.align 2
.align 2
.L18:
.word .L3-.L18
.word .L10-.L18
.word .L4-.L18
.word .L5-.L18
.word .L5-.L18
.word .L5-.L18
To generate the adr instruction, I did (set (match_dup)(label_ref ...))
and defined an insn which would interpret this. However, I do not really
feel comfortable with this since it only works if the label_ref is close
enough.
Funny thing is that when I move the lsl in front of the adr (changing
the arm.md), I get an ICE. But this here works fine. Also, I'm quite
sure the *thumb_subsi3imm_insn I created is not necessary if I would
change my template.
Patch is against current cvs. I'll probably try it on 3.0.2, too.
[attached, as netscape kills diffs sent inline]
greets from Zürich
-- vbi
-------------- next part --------------
2001-11-22 Adrian von Bidder <avbidder@acter.ch>
* arm.h (CASE_VECTOR_PC_RELATIVE, ASM_OUTPUT_ADDR_DIFF_ELT): define
* arm.md (thumbpic_casesi): new pattern for switch() statements
(*thumb_subsi3imm_insn, *thumb_mulsi3_power2, *thumb_adr): referred by
thumbpic_casesi
diff -bprudN --exclude=*CVS* latest/gcc/config/arm/arm.h latest-uclinux/gcc/config/arm/arm.h
--- latest/gcc/config/arm/arm.h Tue Oct 30 15:18:33 2001
+++ latest-uclinux/gcc/config/arm/arm.h Tue Nov 20 15:23:29 2001
@@ -2334,7 +2348,7 @@ typedef struct
instruction expects the table to contain offsets from the address of the
table.
Do not define this if the table should contain absolute addresses. */
-/* #define CASE_VECTOR_PC_RELATIVE 1 */
+#define CASE_VECTOR_PC_RELATIVE flag_pic
/* Specify the tree operation to be used to convert reals to integers. */
#define IMPLICIT_FIX_EXPR FIX_ROUND_EXPR
@@ -2594,6 +2608,14 @@ extern int making_const_table;
} \
while (0)
+/* for pc relative jump tables when using pic code: */
+#undef ASM_OUTPUT_ADDR_DIFF_ELT
+#define ASM_OUTPUT_ADDR_DIFF_ELT(STREAM, BODY, VALUE, REL) \
+ if (TARGET_ARM) \
+ fprintf (STREAM, "\tb\t.L%d\n", VALUE); \
+ else \
+ fprintf (STREAM, "\t.word\t.L%d-.L%d\n", VALUE, REL) \
+
#define ARM_DECLARE_FUNCTION_NAME(STREAM, NAME, DECL) \
do \
{ \
diff -bprudN --exclude=*CVS* latest/gcc/config/arm/arm.md latest-uclinux/gcc/config/arm/arm.md
--- latest/gcc/config/arm/arm.md Wed Sep 12 19:17:53 2001
+++ latest-uclinux/gcc/config/arm/arm.md Thu Nov 22 09:02:54 2001
@@ -991,6 +991,17 @@
[(set_attr "length" "2")]
)
+(define_insn "*thumb_subsi3imm_insn"
+ [(set (match_operand:SI 0 "register_operand" "=l,l")
+ (minus:SI (match_operand:SI 1 "register_operand" "l,0")
+ (match_operand:SI 2 "const_int_operand" "L,I")))]
+ "TARGET_THUMB && ((unsigned int)INTVAL(operands[2]) < 256)"
+ "@
+ sub\\t%0, %1, %2
+ sub\\t%0, %2"
+ [(set_attr "length" "2")]
+)
+
(define_insn_and_split "*arm_subsi3_insn"
[(set (match_operand:SI 0 "s_register_operand" "=r,r")
(minus:SI (match_operand:SI 1 "reg_or_int_operand" "rI,?n")
@@ -1165,6 +1176,16 @@
(set_attr "type" "mult")]
)
+; multiply by power of 2
+(define_insn "*thumb_mulsi3_power2"
+ [(set (match_operand:SI 0 "s_register_operand" "=l")
+ (mult:SI (match_operand:SI 1 "s_register_operand" "0")
+ (match_operand:SI 2 "const_int_operand" "")))]
+ "TARGET_THUMB && (((INTVAL(operands[2]))&((INTVAL(operands[2]) - 1))) == 0)"
+ "lsl\\t%0, %1, %2"
+ [(set_attr "length" "2")]
+)
+
(define_insn "*mulsi3_compare0"
[(set (reg:CC_NOOV CC_REGNUM)
(compare:CC_NOOV (mult:SI
@@ -6829,10 +6850,17 @@
(match_operand:SI 2 "const_int_operand" "") ; total range
(match_operand:SI 3 "" "") ; table label
(match_operand:SI 4 "" "")] ; Out of range label
- "TARGET_ARM"
+ "TARGET_ARM || (TARGET_THUMB && flag_pic)"
"
{
rtx reg;
+
+ if (TARGET_THUMB && flag_pic) {
+ emit_insn (gen_thumbpic_casesi (operands[0], operands[1],
+ operands[2], operands[3], operands[4]));
+ DONE;
+ }
+
if (operands[1] != const0_rtx)
{
reg = gen_reg_rtx (SImode);
@@ -6845,15 +6873,15 @@
if (!const_ok_for_arm (INTVAL (operands[2])))
operands[2] = force_reg (SImode, operands[2]);
- emit_jump_insn (gen_casesi_internal (operands[0], operands[2], operands[3],
- operands[4]));
+ emit_jump_insn (gen_arm_casesi_internal (operands[0], operands[2],
+ operands[3], operands[4]));
DONE;
}"
)
;; The USE in this pattern is needed to tell flow analysis that this is
;; a CASESI insn. It has no other purpose.
-(define_insn "casesi_internal"
+(define_insn "arm_casesi_internal"
[(parallel [(set (pc)
(if_then_else
(leu (match_operand:SI 0 "s_register_operand" "r")
@@ -6873,6 +6901,53 @@
(set_attr "length" "12")]
)
+;; -fpic -mthumb is wrong with just tablejump.
+(define_expand "thumbpic_casesi"
+ [(set (match_dup 5)
+ (minus:SI (match_operand:SI 0 "" "")
+ (match_operand:SI 1 "" "")))
+ (set (pc)
+ (if_then_else (gtu (match_dup 5)
+ (match_operand 2 "" ""))
+ (label_ref (match_operand 4 "" ""))
+ (pc)))
+ (set (match_dup 7)
+ (label_ref (match_operand 3 "" "")))
+ (set (match_dup 6)
+ (mult:SI (match_dup 5)
+ (const_int 4)))
+ (set (match_dup 8)
+ (mem:SI (plus:SI (match_dup 6)
+ (match_dup 7))))
+ (set (match_dup 9)
+ (plus:SI (match_dup 7)
+ (match_dup 8)))
+ (parallel [
+ (set (pc)
+ (match_dup 9))
+ (use (label_ref (match_dup 3)))])]
+ "TARGET_THUMB && flag_pic"
+ "{
+ operands[5] = gen_reg_rtx (SImode);
+ operands[6] = gen_reg_rtx (SImode);
+ operands[7] = gen_reg_rtx (SImode);
+ operands[8] = gen_reg_rtx (SImode);
+ operands[9] = gen_reg_rtx (SImode);
+
+ operands[1] = force_reg(SImode, operands[1]);
+ }"
+)
+
+; this works only if the label_ref is close enough (10 bit word aligned)
+; but it should only be called from thumbpic_casesi anyway.
+(define_insn "*thumb_adr"
+ [(set (match_operand:SI 0 "s_register_operand" "=l")
+ (label_ref (match_operand 1 "" "")))]
+ "TARGET_THUMB"
+ "adr\\t%0, %a1"
+ [(set_attr "length" "2")]
+)
+
(define_expand "indirect_jump"
[(set (pc)
(match_operand:SI 0 "s_register_operand" ""))]
@@ -9132,12 +9207,11 @@
)
;; Miscellaneous Thumb patterns
-
(define_insn "tablejump"
[(set (pc) (match_operand:SI 0 "register_operand" "l*r"))
(use (label_ref (match_operand 1 "" "")))]
"TARGET_THUMB"
- "mov pc, %0"
+ "mov\\tpc, %0"
[(set_attr "length" "2")]
)
More information about the Gcc-patches
mailing list