ARM: tell LLVM about zext properties of ldrexb/ldrexh
authorTim Northover <tnorthover@apple.com>
Thu, 3 Apr 2014 15:10:35 +0000 (15:10 +0000)
committerTim Northover <tnorthover@apple.com>
Thu, 3 Apr 2014 15:10:35 +0000 (15:10 +0000)
Implementing this via ComputeMaskedBits has two advantages:
  + It actually works. DAGISel doesn't deal with the chains properly
    in the previous pattern-based solution, so they never trigger.
  + The information can be used in other DAG combines, as well as the
    trivial "get rid of truncs". For example if the trunc is in a
    different basic block.

rdar://problem/16227836

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@205540 91177308-0d34-0410-b5e6-96231b3b80d8

lib/Target/ARM/ARMISelLowering.cpp
lib/Target/ARM/ARMInstrInfo.td
test/CodeGen/ARM/atomic-ops-v8.ll
test/CodeGen/ARM/ldaex-stlex.ll
test/CodeGen/ARM/ldstrex.ll

index 310d845db11e0c9d36b49adca34ba5f234b5050f..2ebad8e0e43264e17a49efd1bb520ae27bbb2438 100644 (file)
@@ -9964,6 +9964,20 @@ void ARMTargetLowering::computeMaskedBitsForTargetNode(const SDValue Op,
     KnownOne  &= KnownOneRHS;
     return;
   }
+  case ISD::INTRINSIC_W_CHAIN: {
+    ConstantSDNode *CN = cast<ConstantSDNode>(Op->getOperand(1));
+    Intrinsic::ID IntID = static_cast<Intrinsic::ID>(CN->getZExtValue());
+    switch (IntID) {
+    default: return;
+    case Intrinsic::arm_ldaex:
+    case Intrinsic::arm_ldrex: {
+      EVT VT = cast<MemIntrinsicSDNode>(Op)->getMemoryVT();
+      unsigned MemBits = VT.getScalarType().getSizeInBits();
+      KnownZero |= APInt::getHighBitsSet(BitWidth, BitWidth - MemBits);
+      return;
+    }
+    }
+  }
   }
 }
 
index 1f09c9f07d2094478e7e90f01b79c6b9787dddf6..75a109e2c3cb92868486f38673a1db0b1bf05d6f 100644 (file)
@@ -4476,19 +4476,11 @@ def CLREX : AXI<(outs), (ins), MiscFrm, NoItinerary, "clrex",
   let Inst{31-0} = 0b11110101011111111111000000011111;
 }
 
-def : ARMPat<(and (ldrex_1 addr_offset_none:$addr), 0xff),
-             (LDREXB addr_offset_none:$addr)>;
-def : ARMPat<(and (ldrex_2 addr_offset_none:$addr), 0xffff),
-             (LDREXH addr_offset_none:$addr)>;
 def : ARMPat<(strex_1 (and GPR:$Rt, 0xff), addr_offset_none:$addr),
              (STREXB GPR:$Rt, addr_offset_none:$addr)>;
 def : ARMPat<(strex_2 (and GPR:$Rt, 0xffff), addr_offset_none:$addr),
              (STREXH GPR:$Rt, addr_offset_none:$addr)>;
 
-def : ARMPat<(and (ldaex_1 addr_offset_none:$addr), 0xff),
-             (LDAEXB addr_offset_none:$addr)>;
-def : ARMPat<(and (ldaex_2 addr_offset_none:$addr), 0xffff),
-             (LDAEXH addr_offset_none:$addr)>;
 def : ARMPat<(stlex_1 (and GPR:$Rt, 0xff), addr_offset_none:$addr),
              (STLEXB GPR:$Rt, addr_offset_none:$addr)>;
 def : ARMPat<(stlex_2 (and GPR:$Rt, 0xffff), addr_offset_none:$addr),
index 00f9006c1555be232696ee8f38bba6d7cc42fad9..7922e2220309f340b6a93ec2af7e412a99c74a2f 100644 (file)
@@ -805,8 +805,8 @@ define i8 @test_atomic_load_umin_i8(i8 zeroext %offset) nounwind {
 ; CHECK: ldrexb r[[OLD:[0-9]+]], {{.*}}[[ADDR]]
   ; r0 below is a reasonable guess but could change: it certainly comes into the
   ;  function there.
-; CHECK-NEXT: uxtb r[[OLDX]], r[[OLD]]
-; CHECK-NEXT: cmp r[[OLDX]], r0
+; CHECK-NEXT: mov r[[NEW:[0-9]+]], r0
+; CHECK-NEXT: cmp r[[OLD]], r0
 ; Thumb mode: it ls
 ; CHECK:      movls r[[NEW]], r[[OLD]]
 ; CHECK-NEXT: strexb [[STATUS:r[0-9]+]], r[[NEW]], {{.*}}[[ADDR]]
@@ -831,8 +831,8 @@ define i16 @test_atomic_load_umin_i16(i16 zeroext %offset) nounwind {
 ; CHECK: ldaexh r[[OLD:[0-9]+]], {{.*}}[[ADDR]]
   ; r0 below is a reasonable guess but could change: it certainly comes into the
   ;  function there.
-; CHECK-NEXT: uxth r[[OLDX]], r[[OLD]]
-; CHECK-NEXT: cmp r[[OLDX]], r0
+; CHECK-NEXT: mov r[[NEW:[0-9]+]], r0
+; CHECK-NEXT: cmp r[[OLD]], r0
 ; Thumb mode: it ls
 ; CHECK:      movls r[[NEW]], r[[OLD]]
 ; CHECK-NEXT: strexh [[STATUS:r[0-9]+]], r[[NEW]], {{.*}}[[ADDR]]
@@ -919,8 +919,8 @@ define i8 @test_atomic_load_umax_i8(i8 zeroext %offset) nounwind {
 ; CHECK: ldaexb r[[OLD:[0-9]+]], {{.*}}[[ADDR]]
   ; r0 below is a reasonable guess but could change: it certainly comes into the
   ;  function there.
-; CHECK-NEXT: uxtb r[[OLDX:[0-9]+]], r[[OLD]]
-; CHECK-NEXT: cmp r[[OLDX]], r0
+; CHECK-NEXT: mov r[[NEW:[0-9]+]], r0
+; CHECK-NEXT: cmp r[[OLD]], r0
 ; Thumb mode: it hi
 ; CHECK:      movhi r[[NEW]], r[[OLD]]
 ; CHECK-NEXT: stlexb [[STATUS:r[0-9]+]], r[[NEW]], {{.*}}[[ADDR]]
@@ -945,8 +945,8 @@ define i16 @test_atomic_load_umax_i16(i16 zeroext %offset) nounwind {
 ; CHECK: ldrexh r[[OLD:[0-9]+]], {{.*}}[[ADDR]]
   ; r0 below is a reasonable guess but could change: it certainly comes into the
   ;  function there.
-; CHECK-NEXT: uxth r[[OLDX:[0-9]+]], r[[OLD]]
-; CHECK-NEXT: cmp r[[OLDX]], r0
+; CHECK-NEXT: mov r[[NEW:[0-9]+]], r0
+; CHECK-NEXT: cmp r[[OLD]], r0
 ; Thumb mode: it hi
 ; CHECK:      movhi r[[NEW]], r[[OLD]]
 ; CHECK-NEXT: strexh [[STATUS:r[0-9]+]], r[[NEW]], {{.*}}[[ADDR]]
@@ -1033,8 +1033,7 @@ define i8 @test_atomic_cmpxchg_i8(i8 zeroext %wanted, i8 zeroext %new) nounwind
 ; CHECK: ldaexb r[[OLD:[0-9]+]], [r[[ADDR]]]
   ; r0 below is a reasonable guess but could change: it certainly comes into the
   ;  function there.
-; CHECK-NEXT: uxtb r[[OLDX:[0-9]+]], r[[OLD]]
-; CHECK-NEXT: cmp r[[OLDX]], r0
+; CHECK-NEXT: cmp r[[OLD]], r0
 ; CHECK-NEXT: bne .LBB{{[0-9]+}}_3
 ; CHECK-NEXT: BB#2:
   ; As above, r1 is a reasonable guess.
@@ -1060,8 +1059,7 @@ define i16 @test_atomic_cmpxchg_i16(i16 zeroext %wanted, i16 zeroext %new) nounw
 ; CHECK: ldaexh r[[OLD:[0-9]+]], [r[[ADDR]]]
   ; r0 below is a reasonable guess but could change: it certainly comes into the
   ;  function there.
-; CHECK-NEXT: uxth r[[OLDX:[0-9]+]], r[[OLD]]
-; CHECK-NEXT: cmp r[[OLDX]], r0
+; CHECK-NEXT: cmp r[[OLD]], r0
 ; CHECK-NEXT: bne .LBB{{[0-9]+}}_3
 ; CHECK-NEXT: BB#2:
   ; As above, r1 is a reasonable guess.
index f309f77fd5fe279ae57cf016427a0832ae5ae3a7..bfdfea3315630cb5fbced50f21f69bc9aa4ed44b 100644 (file)
@@ -34,17 +34,21 @@ declare i32 @llvm.arm.stlexd(i32, i32, i8*) nounwind
 ; CHECK-LABEL: test_load_i8:
 ; CHECK: ldaexb r0, [r0]
 ; CHECK-NOT: uxtb
-define i32 @test_load_i8(i8* %addr) {
+; CHECK-NOT: and
+define zeroext i8 @test_load_i8(i8* %addr) {
   %val = call i32 @llvm.arm.ldaex.p0i8(i8* %addr)
-  ret i32 %val
+  %val8 = trunc i32 %val to i8
+  ret i8 %val8
 }
 
 ; CHECK-LABEL: test_load_i16:
 ; CHECK: ldaexh r0, [r0]
 ; CHECK-NOT: uxth
-define i32 @test_load_i16(i16* %addr) {
+; CHECK-NOT: and
+define zeroext i16 @test_load_i16(i16* %addr) {
   %val = call i32 @llvm.arm.ldaex.p0i16(i16* %addr)
-  ret i32 %val
+  %val16 = trunc i32 %val to i16
+  ret i16 %val16
 }
 
 ; CHECK-LABEL: test_load_i32:
index 5eaae53da994abcdbd739ee15541e5e64a48e202..a40e255e83eabdeca723ad0a5e8a90b091f8776c 100644 (file)
@@ -36,17 +36,21 @@ declare i32 @llvm.arm.strexd(i32, i32, i8*) nounwind
 ; CHECK-LABEL: test_load_i8:
 ; CHECK: ldrexb r0, [r0]
 ; CHECK-NOT: uxtb
-define i32 @test_load_i8(i8* %addr) {
+; CHECK-NOT: and
+define zeroext i8 @test_load_i8(i8* %addr) {
   %val = call i32 @llvm.arm.ldrex.p0i8(i8* %addr)
-  ret i32 %val
+  %val8 = trunc i32 %val to i8
+  ret i8 %val8
 }
 
 ; CHECK-LABEL: test_load_i16:
 ; CHECK: ldrexh r0, [r0]
 ; CHECK-NOT: uxth
-define i32 @test_load_i16(i16* %addr) {
+; CHECK-NOT: and
+define zeroext i16 @test_load_i16(i16* %addr) {
   %val = call i32 @llvm.arm.ldrex.p0i16(i16* %addr)
-  ret i32 %val
+  %val16 = trunc i32 %val to i16
+  ret i16 %val16
 }
 
 ; CHECK-LABEL: test_load_i32:
@@ -137,3 +141,19 @@ define void @excl_addrmode() {
 
   ret void
 }
+
+; LLVM should know, even across basic blocks, that ldrex is setting the high
+; bits of its i32 to 0. There should be no zero-extend operation.
+define zeroext i8 @test_cross_block_zext_i8(i1 %tst, i8* %addr) {
+; CHECK: test_cross_block_zext_i8:
+; CHECK-NOT: uxtb
+; CHECK-NOT: and
+; CHECK: bx lr
+  %val = call i32 @llvm.arm.ldrex.p0i8(i8* %addr)
+  br i1 %tst, label %end, label %mid
+mid:
+  ret i8 42
+end:
+  %val8 = trunc i32 %val to i8
+  ret i8 %val8
+}