// Load Store Instructions.
//
+// PC-relative loads need to be matched first as constant pool accesses need to
+// always be PC-relative. We do this using AddedComplexity, as the pattern is
+// simpler than the patterns of the other load instructions.
+let canFoldAsLoad = 1, isReMaterializable = 1, AddedComplexity = 10 in
+def tLDRpci : T1pIs<(outs tGPR:$Rt), (ins t_addrmode_pc:$addr), IIC_iLoad_i,
+ "ldr", "\t$Rt, $addr",
+ [(set tGPR:$Rt, (load (ARMWrapper tconstpool:$addr)))]>,
+ T1Encoding<{0,1,0,0,1,?}> {
+ // A6.2 & A8.6.59
+ bits<3> Rt;
+ bits<8> addr;
+ let Inst{10-8} = Rt;
+ let Inst{7-0} = addr;
+}
+
+// SP-relative loads should be matched before standard immediate-offset loads as
+// it means we avoid having to move SP to another register.
+let canFoldAsLoad = 1 in
+def tLDRspi : T1pIs<(outs tGPR:$Rt), (ins t_addrmode_sp:$addr), IIC_iLoad_i,
+ "ldr", "\t$Rt, $addr",
+ [(set tGPR:$Rt, (load t_addrmode_sp:$addr))]>,
+ T1LdStSP<{1,?,?}> {
+ bits<3> Rt;
+ bits<8> addr;
+ let Inst{10-8} = Rt;
+ let Inst{7-0} = addr;
+}
+
// Loads: reg/reg and reg/imm5
let canFoldAsLoad = 1, isReMaterializable = 1 in
multiclass thumb_ld_rr_ri_enc<bits<3> reg_opc, bits<4> imm_opc,
AddrMode am, InstrItinClass itin_r,
InstrItinClass itin_i, string asm,
PatFrag opnode> {
- def r : // reg/reg
- T1pILdStEncode<reg_opc,
- (outs tGPR:$Rt), (ins AddrMode_r:$addr),
- am, itin_r, asm, "\t$Rt, $addr",
- [(set tGPR:$Rt, (opnode AddrMode_r:$addr))]>;
+ // Immediate-offset loads should be matched before register-offset loads as
+ // when the offset is a constant it's simpler to first check if it fits in the
+ // immediate offset field then fall back to register-offset if it doesn't.
def i : // reg/imm5
T1pILdStEncodeImm<imm_opc, 1 /* Load */,
(outs tGPR:$Rt), (ins AddrMode_i:$addr),
am, itin_i, asm, "\t$Rt, $addr",
[(set tGPR:$Rt, (opnode AddrMode_i:$addr))]>;
+ // Register-offset loads are matched last.
+ def r : // reg/reg
+ T1pILdStEncode<reg_opc,
+ (outs tGPR:$Rt), (ins AddrMode_r:$addr),
+ am, itin_r, asm, "\t$Rt, $addr",
+ [(set tGPR:$Rt, (opnode AddrMode_r:$addr))]>;
}
// Stores: reg/reg and reg/imm5
multiclass thumb_st_rr_ri_enc<bits<3> reg_opc, bits<4> imm_opc,
AddrMode am, InstrItinClass itin_r,
InstrItinClass itin_i, string asm,
PatFrag opnode> {
- def r : // reg/reg
- T1pILdStEncode<reg_opc,
- (outs), (ins tGPR:$Rt, AddrMode_r:$addr),
- am, itin_r, asm, "\t$Rt, $addr",
- [(opnode tGPR:$Rt, AddrMode_r:$addr)]>;
def i : // reg/imm5
T1pILdStEncodeImm<imm_opc, 0 /* Store */,
(outs), (ins tGPR:$Rt, AddrMode_i:$addr),
am, itin_i, asm, "\t$Rt, $addr",
[(opnode tGPR:$Rt, AddrMode_i:$addr)]>;
+ def r : // reg/reg
+ T1pILdStEncode<reg_opc,
+ (outs), (ins tGPR:$Rt, AddrMode_r:$addr),
+ am, itin_r, asm, "\t$Rt, $addr",
+ [(opnode tGPR:$Rt, AddrMode_r:$addr)]>;
}
// A8.6.57 & A8.6.60
-defm tLDR : thumb_ld_rr_ri_enc<0b100, 0b0110, t_addrmode_rrs4,
+defm tLDR : thumb_ld_rr_ri_enc<0b100, 0b0110, t_addrmode_rr,
t_addrmode_is4, AddrModeT1_4,
IIC_iLoad_r, IIC_iLoad_i, "ldr",
UnOpFrag<(load node:$Src)>>;
// A8.6.64 & A8.6.61
-defm tLDRB : thumb_ld_rr_ri_enc<0b110, 0b0111, t_addrmode_rrs1,
+defm tLDRB : thumb_ld_rr_ri_enc<0b110, 0b0111, t_addrmode_rr,
t_addrmode_is1, AddrModeT1_1,
IIC_iLoad_bh_r, IIC_iLoad_bh_i, "ldrb",
UnOpFrag<(zextloadi8 node:$Src)>>;
// A8.6.76 & A8.6.73
-defm tLDRH : thumb_ld_rr_ri_enc<0b101, 0b1000, t_addrmode_rrs2,
+defm tLDRH : thumb_ld_rr_ri_enc<0b101, 0b1000, t_addrmode_rr,
t_addrmode_is2, AddrModeT1_2,
IIC_iLoad_bh_r, IIC_iLoad_bh_i, "ldrh",
UnOpFrag<(zextloadi16 node:$Src)>>;
"ldrsh", "\t$Rt, $addr",
[(set tGPR:$Rt, (sextloadi16 t_addrmode_rr:$addr))]>;
-let canFoldAsLoad = 1 in
-def tLDRspi : T1pIs<(outs tGPR:$Rt), (ins t_addrmode_sp:$addr), IIC_iLoad_i,
- "ldr", "\t$Rt, $addr",
- [(set tGPR:$Rt, (load t_addrmode_sp:$addr))]>,
- T1LdStSP<{1,?,?}> {
- bits<3> Rt;
- bits<8> addr;
- let Inst{10-8} = Rt;
- let Inst{7-0} = addr;
-}
-let canFoldAsLoad = 1, isReMaterializable = 1 in
-def tLDRpci : T1pIs<(outs tGPR:$Rt), (ins t_addrmode_pc:$addr), IIC_iLoad_i,
- "ldr", "\t$Rt, $addr",
- [(set tGPR:$Rt, (load (ARMWrapper tconstpool:$addr)))]>,
- T1Encoding<{0,1,0,0,1,?}> {
- // A6.2 & A8.6.59
+def tSTRspi : T1pIs<(outs), (ins tGPR:$Rt, t_addrmode_sp:$addr), IIC_iStore_i,
+ "str", "\t$Rt, $addr",
+ [(store tGPR:$Rt, t_addrmode_sp:$addr)]>,
+ T1LdStSP<{0,?,?}> {
bits<3> Rt;
bits<8> addr;
let Inst{10-8} = Rt;
- let Inst{7-0} = addr;
+ let Inst{7-0} = addr;
}
// A8.6.194 & A8.6.192
-defm tSTR : thumb_st_rr_ri_enc<0b000, 0b0110, t_addrmode_rrs4,
+defm tSTR : thumb_st_rr_ri_enc<0b000, 0b0110, t_addrmode_rr,
t_addrmode_is4, AddrModeT1_4,
IIC_iStore_r, IIC_iStore_i, "str",
BinOpFrag<(store node:$LHS, node:$RHS)>>;
// A8.6.197 & A8.6.195
-defm tSTRB : thumb_st_rr_ri_enc<0b010, 0b0111, t_addrmode_rrs1,
+defm tSTRB : thumb_st_rr_ri_enc<0b010, 0b0111, t_addrmode_rr,
t_addrmode_is1, AddrModeT1_1,
IIC_iStore_bh_r, IIC_iStore_bh_i, "strb",
BinOpFrag<(truncstorei8 node:$LHS, node:$RHS)>>;
// A8.6.207 & A8.6.205
-defm tSTRH : thumb_st_rr_ri_enc<0b001, 0b1000, t_addrmode_rrs2,
+defm tSTRH : thumb_st_rr_ri_enc<0b001, 0b1000, t_addrmode_rr,
t_addrmode_is2, AddrModeT1_2,
IIC_iStore_bh_r, IIC_iStore_bh_i, "strh",
BinOpFrag<(truncstorei16 node:$LHS, node:$RHS)>>;
-def tSTRspi : T1pIs<(outs), (ins tGPR:$Rt, t_addrmode_sp:$addr), IIC_iStore_i,
- "str", "\t$Rt, $addr",
- [(store tGPR:$Rt, t_addrmode_sp:$addr)]>,
- T1LdStSP<{0,?,?}> {
- bits<3> Rt;
- bits<8> addr;
- let Inst{10-8} = Rt;
- let Inst{7-0} = addr;
-}
-
//===----------------------------------------------------------------------===//
// Load / store multiple Instructions.
//
(tSUBrr tGPR:$lhs, tGPR:$rhs)>;
// Bswap 16 with load/store
-def : T1Pat<(srl (bswap (extloadi16 t_addrmode_rrs2:$addr)), (i32 16)),
- (tREV16 (tLDRHr t_addrmode_rrs2:$addr))>;
def : T1Pat<(srl (bswap (extloadi16 t_addrmode_is2:$addr)), (i32 16)),
(tREV16 (tLDRHi t_addrmode_is2:$addr))>;
-def : T1Pat<(truncstorei16 (srl (bswap tGPR:$Rn), (i32 16)),
- t_addrmode_rrs2:$addr),
- (tSTRHr (tREV16 tGPR:$Rn), t_addrmode_rrs2:$addr)>;
+def : T1Pat<(srl (bswap (extloadi16 t_addrmode_rr:$addr)), (i32 16)),
+ (tREV16 (tLDRHr t_addrmode_rr:$addr))>;
def : T1Pat<(truncstorei16 (srl (bswap tGPR:$Rn), (i32 16)),
t_addrmode_is2:$addr),
(tSTRHi(tREV16 tGPR:$Rn), t_addrmode_is2:$addr)>;
+def : T1Pat<(truncstorei16 (srl (bswap tGPR:$Rn), (i32 16)),
+ t_addrmode_rr:$addr),
+ (tSTRHr (tREV16 tGPR:$Rn), t_addrmode_rr:$addr)>;
// ConstantPool
def : T1Pat<(ARMWrapper tconstpool :$dst), (tLEApcrel tconstpool :$dst)>;
Requires<[IsThumb, HasV5T]>;
// zextload i1 -> zextload i8
-def : T1Pat<(zextloadi1 t_addrmode_rrs1:$addr),
- (tLDRBr t_addrmode_rrs1:$addr)>;
def : T1Pat<(zextloadi1 t_addrmode_is1:$addr),
(tLDRBi t_addrmode_is1:$addr)>;
+def : T1Pat<(zextloadi1 t_addrmode_rr:$addr),
+ (tLDRBr t_addrmode_rr:$addr)>;
// extload from the stack -> word load from the stack, as it avoids having to
// materialize the base in a separate register. This only works when a word
Requires<[IsThumb, IsThumb1Only, IsLE]>;
// extload -> zextload
-def : T1Pat<(extloadi1 t_addrmode_rrs1:$addr), (tLDRBr t_addrmode_rrs1:$addr)>;
-def : T1Pat<(extloadi1 t_addrmode_is1:$addr), (tLDRBi t_addrmode_is1:$addr)>;
-def : T1Pat<(extloadi8 t_addrmode_rrs1:$addr), (tLDRBr t_addrmode_rrs1:$addr)>;
-def : T1Pat<(extloadi8 t_addrmode_is1:$addr), (tLDRBi t_addrmode_is1:$addr)>;
-def : T1Pat<(extloadi16 t_addrmode_rrs2:$addr), (tLDRHr t_addrmode_rrs2:$addr)>;
-def : T1Pat<(extloadi16 t_addrmode_is2:$addr), (tLDRHi t_addrmode_is2:$addr)>;
+def : T1Pat<(extloadi1 t_addrmode_is1:$addr), (tLDRBi t_addrmode_is1:$addr)>;
+def : T1Pat<(extloadi1 t_addrmode_rr:$addr), (tLDRBr t_addrmode_rr:$addr)>;
+def : T1Pat<(extloadi8 t_addrmode_is1:$addr), (tLDRBi t_addrmode_is1:$addr)>;
+def : T1Pat<(extloadi8 t_addrmode_rr:$addr), (tLDRBr t_addrmode_rr:$addr)>;
+def : T1Pat<(extloadi16 t_addrmode_is2:$addr), (tLDRHi t_addrmode_is2:$addr)>;
+def : T1Pat<(extloadi16 t_addrmode_rr:$addr), (tLDRHr t_addrmode_rr:$addr)>;
// If it's impossible to use [r,r] address mode for sextload, select to
// ldr{b|h} + sxt{b|h} instead.
def : T1Pat<(sextloadi8 t_addrmode_is1:$addr),
(tSXTB (tLDRBi t_addrmode_is1:$addr))>,
Requires<[IsThumb, IsThumb1Only, HasV6]>;
-def : T1Pat<(sextloadi8 t_addrmode_rrs1:$addr),
- (tSXTB (tLDRBr t_addrmode_rrs1:$addr))>,
+def : T1Pat<(sextloadi8 t_addrmode_rr:$addr),
+ (tSXTB (tLDRBr t_addrmode_rr:$addr))>,
Requires<[IsThumb, IsThumb1Only, HasV6]>;
def : T1Pat<(sextloadi16 t_addrmode_is2:$addr),
(tSXTH (tLDRHi t_addrmode_is2:$addr))>,
Requires<[IsThumb, IsThumb1Only, HasV6]>;
-def : T1Pat<(sextloadi16 t_addrmode_rrs2:$addr),
- (tSXTH (tLDRHr t_addrmode_rrs2:$addr))>,
+def : T1Pat<(sextloadi16 t_addrmode_rr:$addr),
+ (tSXTH (tLDRHr t_addrmode_rr:$addr))>,
Requires<[IsThumb, IsThumb1Only, HasV6]>;
-def : T1Pat<(sextloadi8 t_addrmode_rrs1:$addr),
- (tASRri (tLSLri (tLDRBr t_addrmode_rrs1:$addr), 24), 24)>;
def : T1Pat<(sextloadi8 t_addrmode_is1:$addr),
(tASRri (tLSLri (tLDRBi t_addrmode_is1:$addr), 24), 24)>;
-def : T1Pat<(sextloadi16 t_addrmode_rrs2:$addr),
- (tASRri (tLSLri (tLDRHr t_addrmode_rrs2:$addr), 16), 16)>;
+def : T1Pat<(sextloadi8 t_addrmode_rr:$addr),
+ (tASRri (tLSLri (tLDRBr t_addrmode_rr:$addr), 24), 24)>;
def : T1Pat<(sextloadi16 t_addrmode_is2:$addr),
(tASRri (tLSLri (tLDRHi t_addrmode_is2:$addr), 16), 16)>;
+def : T1Pat<(sextloadi16 t_addrmode_rr:$addr),
+ (tASRri (tLSLri (tLDRHr t_addrmode_rr:$addr), 16), 16)>;
def : T1Pat<(atomic_load_8 t_addrmode_is1:$src),
(tLDRBi t_addrmode_is1:$src)>;
-def : T1Pat<(atomic_load_8 t_addrmode_rrs1:$src),
- (tLDRBr t_addrmode_rrs1:$src)>;
+def : T1Pat<(atomic_load_8 t_addrmode_rr:$src),
+ (tLDRBr t_addrmode_rr:$src)>;
def : T1Pat<(atomic_load_16 t_addrmode_is2:$src),
(tLDRHi t_addrmode_is2:$src)>;
-def : T1Pat<(atomic_load_16 t_addrmode_rrs2:$src),
- (tLDRHr t_addrmode_rrs2:$src)>;
+def : T1Pat<(atomic_load_16 t_addrmode_rr:$src),
+ (tLDRHr t_addrmode_rr:$src)>;
def : T1Pat<(atomic_load_32 t_addrmode_is4:$src),
(tLDRi t_addrmode_is4:$src)>;
-def : T1Pat<(atomic_load_32 t_addrmode_rrs4:$src),
- (tLDRr t_addrmode_rrs4:$src)>;
+def : T1Pat<(atomic_load_32 t_addrmode_rr:$src),
+ (tLDRr t_addrmode_rr:$src)>;
def : T1Pat<(atomic_store_8 t_addrmode_is1:$ptr, tGPR:$val),
(tSTRBi tGPR:$val, t_addrmode_is1:$ptr)>;
-def : T1Pat<(atomic_store_8 t_addrmode_rrs1:$ptr, tGPR:$val),
- (tSTRBr tGPR:$val, t_addrmode_rrs1:$ptr)>;
+def : T1Pat<(atomic_store_8 t_addrmode_rr:$ptr, tGPR:$val),
+ (tSTRBr tGPR:$val, t_addrmode_rr:$ptr)>;
def : T1Pat<(atomic_store_16 t_addrmode_is2:$ptr, tGPR:$val),
(tSTRHi tGPR:$val, t_addrmode_is2:$ptr)>;
-def : T1Pat<(atomic_store_16 t_addrmode_rrs2:$ptr, tGPR:$val),
- (tSTRHr tGPR:$val, t_addrmode_rrs2:$ptr)>;
+def : T1Pat<(atomic_store_16 t_addrmode_rr:$ptr, tGPR:$val),
+ (tSTRHr tGPR:$val, t_addrmode_rr:$ptr)>;
def : T1Pat<(atomic_store_32 t_addrmode_is4:$ptr, tGPR:$val),
(tSTRi tGPR:$val, t_addrmode_is4:$ptr)>;
-def : T1Pat<(atomic_store_32 t_addrmode_rrs4:$ptr, tGPR:$val),
- (tSTRr tGPR:$val, t_addrmode_rrs4:$ptr)>;
+def : T1Pat<(atomic_store_32 t_addrmode_rr:$ptr, tGPR:$val),
+ (tSTRr tGPR:$val, t_addrmode_rr:$ptr)>;
// Large immediate handling.
-; RUN: llc -mtriple=arm-eabi %s -o - | FileCheck %s
+; RUN: llc -mtriple=thumbv6m-eabi %s -o - | FileCheck %s -check-prefix=CHECK -check-prefix=CHECK-T1
+; RUN: llc -mtriple=thumbv7m-eabi %s -o - | FileCheck %s -check-prefix=CHECK -check-prefix=CHECK-T2
-define i32 @f1(i8* %p) {
+
+; Register offset
+
+; CHECK-LABEL: ldrsb_rr
+; CHECK: ldrsb r0, [r0, r1]
+define i32 @ldrsb_rr(i8* %p, i32 %n) {
+entry:
+ %arrayidx = getelementptr inbounds i8, i8* %p, i32 %n
+ %0 = load i8, i8* %arrayidx, align 1
+ %conv = sext i8 %0 to i32
+ ret i32 %conv
+}
+
+; CHECK-LABEL: ldrsh_rr
+; CHECK-T1: lsls r1, r1, #1
+; CHECK-T1: ldrsh r0, [r0, r1]
+; CHECK-T2: ldrsh.w r0, [r0, r1, lsl #1]
+define i32 @ldrsh_rr(i16* %p, i32 %n) {
+entry:
+ %arrayidx = getelementptr inbounds i16, i16* %p, i32 %n
+ %0 = load i16, i16* %arrayidx, align 2
+ %conv = sext i16 %0 to i32
+ ret i32 %conv
+}
+
+; CHECK-LABEL: ldrb_rr
+; CHECK: ldrb r0, [r0, r1]
+define i32 @ldrb_rr(i8* %p, i32 %n) {
+entry:
+ %arrayidx = getelementptr inbounds i8, i8* %p, i32 %n
+ %0 = load i8, i8* %arrayidx, align 1
+ %conv = zext i8 %0 to i32
+ ret i32 %conv
+}
+
+; CHECK-LABEL: ldrh_rr
+; CHECK-T1: lsls r1, r1, #1
+; CHECK-T1: ldrh r0, [r0, r1]
+; CHECK-T2: ldrh.w r0, [r0, r1, lsl #1]
+define i32 @ldrh_rr(i16* %p, i32 %n) {
+entry:
+ %arrayidx = getelementptr inbounds i16, i16* %p, i32 %n
+ %0 = load i16, i16* %arrayidx, align 2
+ %conv = zext i16 %0 to i32
+ ret i32 %conv
+}
+
+; CHECK-LABEL: ldr_rr
+; CHECK-T1: lsls r1, r1, #2
+; CHECK-T1: ldr r0, [r0, r1]
+; CHECK-T2: ldr.w r0, [r0, r1, lsl #2]
+define i32 @ldr_rr(i32* %p, i32 %n) {
+entry:
+ %arrayidx = getelementptr inbounds i32, i32* %p, i32 %n
+ %0 = load i32, i32* %arrayidx, align 4
+ ret i32 %0
+}
+
+; CHECK-LABEL: strb_rr
+; CHECK: strb r2, [r0, r1]
+define void @strb_rr(i8* %p, i32 %n, i32 %x) {
+entry:
+ %conv = trunc i32 %x to i8
+ %arrayidx = getelementptr inbounds i8, i8* %p, i32 %n
+ store i8 %conv, i8* %arrayidx, align 1
+ ret void
+}
+
+; CHECK-LABEL: strh_rr
+; CHECK-T1: lsls r1, r1, #1
+; CHECK-T1: strh r2, [r0, r1]
+; CHECK-T2: strh.w r2, [r0, r1, lsl #1]
+define void @strh_rr(i16* %p, i32 %n, i32 %x) {
+entry:
+ %conv = trunc i32 %x to i16
+ %arrayidx = getelementptr inbounds i16, i16* %p, i32 %n
+ store i16 %conv, i16* %arrayidx, align 2
+ ret void
+}
+
+; CHECK-LABEL: str_rr
+; CHECK-T1: lsls r1, r1, #2
+; CHECK-T1: str r2, [r0, r1]
+; CHECK-T2: str.w r2, [r0, r1, lsl #2]
+define void @str_rr(i32* %p, i32 %n, i32 %x) {
+entry:
+ %arrayidx = getelementptr inbounds i32, i32* %p, i32 %n
+ store i32 %x, i32* %arrayidx, align 4
+ ret void
+}
+
+
+; Immediate offset of zero
+
+; CHECK-LABEL: ldrsb_ri_zero
+; CHECK-T1: ldrb r0, [r0]
+; CHECK-T1: sxtb r0, r0
+; CHECK-T2: ldrsb.w r0, [r0]
+define i32 @ldrsb_ri_zero(i8* %p) {
+entry:
+ %0 = load i8, i8* %p, align 1
+ %conv = sext i8 %0 to i32
+ ret i32 %conv
+}
+
+; CHECK-LABEL: ldrsh_ri_zero
+; CHECK-T1: ldrh r0, [r0]
+; CHECK-T1: sxth r0, r0
+; CHECK-T2: ldrsh.w r0, [r0]
+define i32 @ldrsh_ri_zero(i16* %p) {
+entry:
+ %0 = load i16, i16* %p, align 2
+ %conv = sext i16 %0 to i32
+ ret i32 %conv
+}
+
+; CHECK-LABEL: ldrb_ri_zero
+; CHECK: ldrb r0, [r0]
+define i32 @ldrb_ri_zero(i8* %p) {
+entry:
+ %0 = load i8, i8* %p, align 1
+ %conv = zext i8 %0 to i32
+ ret i32 %conv
+}
+
+; CHECK-LABEL: ldrh_ri_zero
+; CHECK: ldrh r0, [r0]
+define i32 @ldrh_ri_zero(i16* %p) {
+entry:
+ %0 = load i16, i16* %p, align 2
+ %conv = zext i16 %0 to i32
+ ret i32 %conv
+}
+
+; CHECK-LABEL: ldr_ri_zero
+; CHECK: ldr r0, [r0]
+define i32 @ldr_ri_zero(i32* %p) {
+entry:
+ %0 = load i32, i32* %p, align 4
+ ret i32 %0
+}
+
+; CHECK-LABEL: strb_ri_zero
+; CHECK: strb r1, [r0]
+define void @strb_ri_zero(i8* %p, i32 %x) {
+entry:
+ %conv = trunc i32 %x to i8
+ store i8 %conv, i8* %p, align 1
+ ret void
+}
+
+; CHECK-LABEL: strh_ri_zero
+; CHECK: strh r1, [r0]
+define void @strh_ri_zero(i16* %p, i32 %x) {
+entry:
+ %conv = trunc i32 %x to i16
+ store i16 %conv, i16* %p, align 2
+ ret void
+}
+
+; CHECK-LABEL: str_ri_zero
+; CHECK: str r1, [r0]
+define void @str_ri_zero(i32* %p, i32 %x) {
+entry:
+ store i32 %x, i32* %p, align 4
+ ret void
+}
+
+
+; Maximum Thumb-1 immediate offset
+
+; CHECK-LABEL: ldrsb_ri_t1_max
+; CHECK-T1: movs r1, #31
+; CHECK-T1: ldrsb r0, [r0, r1]
+; CHECK-T2: ldrsb.w r0, [r0, #31]
+define i32 @ldrsb_ri_t1_max(i8* %p) {
+entry:
+ %arrayidx = getelementptr inbounds i8, i8* %p, i32 31
+ %0 = load i8, i8* %arrayidx, align 1
+ %conv = sext i8 %0 to i32
+ ret i32 %conv
+}
+
+; CHECK-LABEL: ldrsh_ri_t1_max
+; CHECK-T1: movs r1, #62
+; CHECK-T1: ldrsh r0, [r0, r1]
+; CHECK-T2: ldrsh.w r0, [r0, #62]
+define i32 @ldrsh_ri_t1_max(i16* %p) {
+entry:
+ %arrayidx = getelementptr inbounds i16, i16* %p, i32 31
+ %0 = load i16, i16* %arrayidx, align 2
+ %conv = sext i16 %0 to i32
+ ret i32 %conv
+}
+
+; CHECK-LABEL: ldrb_ri_t1_max
+; CHECK: ldrb r0, [r0, #31]
+define i32 @ldrb_ri_t1_max(i8* %p) {
+entry:
+ %arrayidx = getelementptr inbounds i8, i8* %p, i32 31
+ %0 = load i8, i8* %arrayidx, align 1
+ %conv = zext i8 %0 to i32
+ ret i32 %conv
+}
+
+; CHECK-LABEL: ldrh_ri_t1_max
+; CHECK: ldrh r0, [r0, #62]
+define i32 @ldrh_ri_t1_max(i16* %p) {
+entry:
+ %arrayidx = getelementptr inbounds i16, i16* %p, i32 31
+ %0 = load i16, i16* %arrayidx, align 2
+ %conv = zext i16 %0 to i32
+ ret i32 %conv
+}
+
+; CHECK-LABEL: ldr_ri_t1_max
+; CHECK: ldr r0, [r0, #124]
+define i32 @ldr_ri_t1_max(i32* %p) {
+entry:
+ %arrayidx = getelementptr inbounds i32, i32* %p, i32 31
+ %0 = load i32, i32* %arrayidx, align 4
+ ret i32 %0
+}
+
+; CHECK-LABEL: strb_ri_t1_max
+; CHECK: strb r1, [r0, #31]
+define void @strb_ri_t1_max(i8* %p, i32 %x) {
+entry:
+ %conv = trunc i32 %x to i8
+ %arrayidx = getelementptr inbounds i8, i8* %p, i32 31
+ store i8 %conv, i8* %arrayidx, align 1
+ ret void
+}
+
+; CHECK-LABEL: strh_ri_t1_max
+; CHECK: strh r1, [r0, #62]
+define void @strh_ri_t1_max(i16* %p, i32 %x) {
+entry:
+ %conv = trunc i32 %x to i16
+ %arrayidx = getelementptr inbounds i16, i16* %p, i32 31
+ store i16 %conv, i16* %arrayidx, align 2
+ ret void
+}
+
+; CHECK-LABEL: str_ri_t1_max
+; CHECK: str r1, [r0, #124]
+define void @str_ri_t1_max(i32* %p, i32 %x) {
+entry:
+ %arrayidx = getelementptr inbounds i32, i32* %p, i32 31
+ store i32 %x, i32* %arrayidx, align 4
+ ret void
+}
+
+
+; One past maximum Thumb-1 immediate offset
+
+; CHECK-LABEL: ldrsb_ri_t1_too_big
+; CHECK-T1: movs r1, #32
+; CHECK-T1: ldrsb r0, [r0, r1]
+; CHECK-T2: ldrsb.w r0, [r0, #32]
+define i32 @ldrsb_ri_t1_too_big(i8* %p) {
+entry:
+ %arrayidx = getelementptr inbounds i8, i8* %p, i32 32
+ %0 = load i8, i8* %arrayidx, align 1
+ %conv = sext i8 %0 to i32
+ ret i32 %conv
+}
+
+; CHECK-LABEL: ldrsh_ri_t1_too_big
+; CHECK-T1: movs r1, #64
+; CHECK-T1: ldrsh r0, [r0, r1]
+; CHECK-T2: ldrsh.w r0, [r0, #64]
+define i32 @ldrsh_ri_t1_too_big(i16* %p) {
+entry:
+ %arrayidx = getelementptr inbounds i16, i16* %p, i32 32
+ %0 = load i16, i16* %arrayidx, align 2
+ %conv = sext i16 %0 to i32
+ ret i32 %conv
+}
+
+; CHECK-LABEL: ldrb_ri_t1_too_big
+; CHECK-T1: movs r1, #32
+; CHECK-T1: ldrb r0, [r0, r1]
+; CHECK-T2: ldrb.w r0, [r0, #32]
+define i32 @ldrb_ri_t1_too_big(i8* %p) {
+entry:
+ %arrayidx = getelementptr inbounds i8, i8* %p, i32 32
+ %0 = load i8, i8* %arrayidx, align 1
+ %conv = zext i8 %0 to i32
+ ret i32 %conv
+}
+
+; CHECK-LABEL: ldrh_ri_t1_too_big
+; CHECK-T1: movs r1, #64
+; CHECK-T1: ldrh r0, [r0, r1]
+; CHECK-T2: ldrh.w r0, [r0, #64]
+define i32 @ldrh_ri_t1_too_big(i16* %p) {
entry:
- %tmp = load i8, i8* %p ; <i8> [#uses=1]
- %tmp1 = sext i8 %tmp to i32 ; <i32> [#uses=1]
- ret i32 %tmp1
+ %arrayidx = getelementptr inbounds i16, i16* %p, i32 32
+ %0 = load i16, i16* %arrayidx, align 2
+ %conv = zext i16 %0 to i32
+ ret i32 %conv
}
-define i32 @f2(i8* %p) {
+; CHECK-LABEL: ldr_ri_t1_too_big
+; CHECK-T1: movs r1, #128
+; CHECK-T1: ldr r0, [r0, r1]
+; CHECK-T2: ldr.w r0, [r0, #128]
+define i32 @ldr_ri_t1_too_big(i32* %p) {
entry:
- %tmp = load i8, i8* %p ; <i8> [#uses=1]
- %tmp2 = zext i8 %tmp to i32 ; <i32> [#uses=1]
- ret i32 %tmp2
+ %arrayidx = getelementptr inbounds i32, i32* %p, i32 32
+ %0 = load i32, i32* %arrayidx, align 4
+ ret i32 %0
}
-define i32 @f3(i16* %p) {
+; CHECK-LABEL: strb_ri_t1_too_big
+; CHECK-T1: movs r2, #32
+; CHECK-T1: strb r1, [r0, r2]
+; CHECK-T2: strb.w r1, [r0, #32]
+define void @strb_ri_t1_too_big(i8* %p, i32 %x) {
entry:
- %tmp = load i16, i16* %p ; <i16> [#uses=1]
- %tmp3 = sext i16 %tmp to i32 ; <i32> [#uses=1]
- ret i32 %tmp3
+ %conv = trunc i32 %x to i8
+ %arrayidx = getelementptr inbounds i8, i8* %p, i32 32
+ store i8 %conv, i8* %arrayidx, align 1
+ ret void
}
-define i32 @f4(i16* %p) {
+; CHECK-LABEL: strh_ri_t1_too_big
+; CHECK-T1: movs r2, #64
+; CHECK-T1: strh r1, [r0, r2]
+; CHECK-T2: strh.w r1, [r0, #64]
+define void @strh_ri_t1_too_big(i16* %p, i32 %x) {
entry:
- %tmp = load i16, i16* %p ; <i16> [#uses=1]
- %tmp4 = zext i16 %tmp to i32 ; <i32> [#uses=1]
- ret i32 %tmp4
+ %conv = trunc i32 %x to i16
+ %arrayidx = getelementptr inbounds i16, i16* %p, i32 32
+ store i16 %conv, i16* %arrayidx, align 2
+ ret void
}
-; CHECK: ldrsb
-; CHECK: ldrb
-; CHECK: ldrsh
-; CHECK: ldrh
+; CHECK-LABEL: str_ri_t1_too_big
+; CHECK-T1: movs r2, #128
+; CHECK-T1: str r1, [r0, r2]
+; CHECK-T2: str.w r1, [r0, #128]
+define void @str_ri_t1_too_big(i32* %p, i32 %x) {
+entry:
+ %arrayidx = getelementptr inbounds i32, i32* %p, i32 32
+ store i32 %x, i32* %arrayidx, align 4
+ ret void
+}
+
+
+; Maximum Thumb-2 immediate offset
+
+; CHECK-LABEL: ldrsb_ri_t2_max
+; CHECK-T1: ldr r1, .LCP
+; CHECK-T1: ldrsb r0, [r0, r1]
+; CHECK-T2: ldrsb.w r0, [r0, #4095]
+define i32 @ldrsb_ri_t2_max(i8* %p) {
+entry:
+ %add.ptr = getelementptr inbounds i8, i8* %p, i32 4095
+ %0 = load i8, i8* %add.ptr, align 1
+ %conv = sext i8 %0 to i32
+ ret i32 %conv
+}
+
+; CHECK-LABEL: ldrsh_ri_t2_max
+; CHECK-T1: ldr r1, .LCP
+; CHECK-T1: ldrsh r0, [r0, r1]
+; CHECK-T2: ldrsh.w r0, [r0, #4095]
+define i32 @ldrsh_ri_t2_max(i8* %p) {
+entry:
+ %add.ptr = getelementptr inbounds i8, i8* %p, i32 4095
+ %0 = bitcast i8* %add.ptr to i16*
+ %1 = load i16, i16* %0, align 2
+ %conv = sext i16 %1 to i32
+ ret i32 %conv
+}
+; CHECK-LABEL: ldrb_ri_t2_max
+; CHECK-T1: ldr r1, .LCP
+; CHECK-T1: ldrb r0, [r0, r1]
+; CHECK-T2: ldrb.w r0, [r0, #4095]
+define i32 @ldrb_ri_t2_max(i8* %p) {
+entry:
+ %add.ptr = getelementptr inbounds i8, i8* %p, i32 4095
+ %0 = load i8, i8* %add.ptr, align 1
+ %conv = zext i8 %0 to i32
+ ret i32 %conv
+}
+
+; CHECK-LABEL: ldrh_ri_t2_max
+; CHECK-T1: ldr r1, .LCP
+; CHECK-T1: ldrh r0, [r0, r1]
+; CHECK-T2: ldrh.w r0, [r0, #4095]
+define i32 @ldrh_ri_t2_max(i8* %p) {
+entry:
+ %add.ptr = getelementptr inbounds i8, i8* %p, i32 4095
+ %0 = bitcast i8* %add.ptr to i16*
+ %1 = load i16, i16* %0, align 2
+ %conv = zext i16 %1 to i32
+ ret i32 %conv
+}
+
+; CHECK-LABEL: ldr_ri_t2_max
+; CHECK-T1: ldr r1, .LCP
+; CHECK-T1: ldr r0, [r0, r1]
+; CHECK-T2: ldr.w r0, [r0, #4095]
+define i32 @ldr_ri_t2_max(i8* %p) {
+entry:
+ %add.ptr = getelementptr inbounds i8, i8* %p, i32 4095
+ %0 = bitcast i8* %add.ptr to i32*
+ %1 = load i32, i32* %0, align 4
+ ret i32 %1
+}
+
+; CHECK-LABEL: strb_ri_t2_max
+; CHECK-T1: ldr r2, .LCP
+; CHECK-T1: strb r1, [r0, r2]
+; CHECK-T2: strb.w r1, [r0, #4095]
+define void @strb_ri_t2_max(i8* %p, i32 %x) {
+entry:
+ %conv = trunc i32 %x to i8
+ %add.ptr = getelementptr inbounds i8, i8* %p, i32 4095
+ store i8 %conv, i8* %add.ptr, align 1
+ ret void
+}
+
+; CHECK-LABEL: strh_ri_t2_max
+; CHECK-T1: ldr r2, .LCP
+; CHECK-T1: strh r1, [r0, r2]
+; CHECK-T2: strh.w r1, [r0, #4095]
+define void @strh_ri_t2_max(i8* %p, i32 %x) {
+entry:
+ %conv = trunc i32 %x to i16
+ %add.ptr = getelementptr inbounds i8, i8* %p, i32 4095
+ %0 = bitcast i8* %add.ptr to i16*
+ store i16 %conv, i16* %0, align 2
+ ret void
+}
+
+; CHECK-LABEL: str_ri_t2_max
+; CHECK-T1: ldr r2, .LCP
+; CHECK-T1: str r1, [r0, r2]
+; CHECK-T2: str.w r1, [r0, #4095]
+define void @str_ri_t2_max(i8* %p, i32 %x) {
+entry:
+ %add.ptr = getelementptr inbounds i8, i8* %p, i32 4095
+ %0 = bitcast i8* %add.ptr to i32*
+ store i32 %x, i32* %0, align 4
+ ret void
+}
+
+
+; One past maximum Thumb-2 immediate offset
+
+; CHECK-LABEL: ldrsb_ri_t2_too_big
+; CHECK-T1: movs r1, #1
+; CHECK-T1: lsls r1, r1, #12
+; CHECK-T2: mov.w r1, #4096
+; CHECK: ldrsb r0, [r0, r1]
+define i32 @ldrsb_ri_t2_too_big(i8* %p) {
+entry:
+ %add.ptr = getelementptr inbounds i8, i8* %p, i32 4096
+ %0 = load i8, i8* %add.ptr, align 1
+ %conv = sext i8 %0 to i32
+ ret i32 %conv
+}
+
+; CHECK-LABEL: ldrsh_ri_t2_too_big
+; CHECK-T1: movs r1, #1
+; CHECK-T1: lsls r1, r1, #12
+; CHECK-T2: mov.w r1, #4096
+; CHECK: ldrsh r0, [r0, r1]
+define i32 @ldrsh_ri_t2_too_big(i8* %p) {
+entry:
+ %add.ptr = getelementptr inbounds i8, i8* %p, i32 4096
+ %0 = bitcast i8* %add.ptr to i16*
+ %1 = load i16, i16* %0, align 2
+ %conv = sext i16 %1 to i32
+ ret i32 %conv
+}
+
+; CHECK-LABEL: ldrb_ri_t2_too_big
+; CHECK-T1: movs r1, #1
+; CHECK-T1: lsls r1, r1, #12
+; CHECK-T2: mov.w r1, #4096
+; CHECK: ldrb r0, [r0, r1]
+define i32 @ldrb_ri_t2_too_big(i8* %p) {
+entry:
+ %add.ptr = getelementptr inbounds i8, i8* %p, i32 4096
+ %0 = load i8, i8* %add.ptr, align 1
+ %conv = zext i8 %0 to i32
+ ret i32 %conv
+}
+
+; CHECK-LABEL: ldrh_ri_t2_too_big
+; CHECK-T1: movs r1, #1
+; CHECK-T1: lsls r1, r1, #12
+; CHECK-T2: mov.w r1, #4096
+; CHECK: ldrh r0, [r0, r1]
+define i32 @ldrh_ri_t2_too_big(i8* %p) {
+entry:
+ %add.ptr = getelementptr inbounds i8, i8* %p, i32 4096
+ %0 = bitcast i8* %add.ptr to i16*
+ %1 = load i16, i16* %0, align 2
+ %conv = zext i16 %1 to i32
+ ret i32 %conv
+}
+
+; CHECK-LABEL: ldr_ri_t2_too_big
+; CHECK-T1: movs r1, #1
+; CHECK-T1: lsls r1, r1, #12
+; CHECK-T2: mov.w r1, #4096
+; CHECK: ldr r0, [r0, r1]
+define i32 @ldr_ri_t2_too_big(i8* %p) {
+entry:
+ %add.ptr = getelementptr inbounds i8, i8* %p, i32 4096
+ %0 = bitcast i8* %add.ptr to i32*
+ %1 = load i32, i32* %0, align 4
+ ret i32 %1
+}
+
+; CHECK-LABEL: strb_ri_t2_too_big
+; CHECK-T1: movs r2, #1
+; CHECK-T1: lsls r2, r2, #12
+; CHECK-T2: mov.w r2, #4096
+; CHECK: strb r1, [r0, r2]
+define void @strb_ri_t2_too_big(i8* %p, i32 %x) {
+entry:
+ %conv = trunc i32 %x to i8
+ %add.ptr = getelementptr inbounds i8, i8* %p, i32 4096
+ store i8 %conv, i8* %add.ptr, align 1
+ ret void
+}
+
+; CHECK-LABEL: strh_ri_t2_too_big
+; CHECK-T1: movs r2, #1
+; CHECK-T1: lsls r2, r2, #12
+; CHECK-T2: mov.w r2, #4096
+; CHECK: strh r1, [r0, r2]
+define void @strh_ri_t2_too_big(i8* %p, i32 %x) {
+entry:
+ %conv = trunc i32 %x to i16
+ %add.ptr = getelementptr inbounds i8, i8* %p, i32 4096
+ %0 = bitcast i8* %add.ptr to i16*
+ store i16 %conv, i16* %0, align 2
+ ret void
+}
+
+; CHECK-LABEL: str_ri_t2_too_big
+; CHECK-T1: movs r2, #1
+; CHECK-T1: lsls r2, r2, #12
+; CHECK-T2: mov.w r2, #4096
+; CHECK: str r1, [r0, r2]
+define void @str_ri_t2_too_big(i8* %p, i32 %x) {
+entry:
+ %add.ptr = getelementptr inbounds i8, i8* %p, i32 4096
+ %0 = bitcast i8* %add.ptr to i32*
+ store i32 %x, i32* %0, align 4
+ ret void
+}