OSDN Git Service

[ARM][LowOverheadLoops] Correct offset checking
authorSam Parker <sam.parker@arm.com>
Thu, 11 Jul 2019 09:56:15 +0000 (09:56 +0000)
committerSam Parker <sam.parker@arm.com>
Thu, 11 Jul 2019 09:56:15 +0000 (09:56 +0000)
This patch addresses a couple of problems:
1) The maximum supported offset of LE is -4094.
2) The offset of WLS also needs to be checked, this uses a
   maximum positive offset of 4094.

The use of BasicBlockUtils has been changed because the block offsets
weren't being initialised, but the isBBInRange checks both positive
and negative offsets.

ARMISelLowering has been tweaked because the test case presented
another pattern that we weren't supporting.

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

lib/Target/ARM/ARMBasicBlockInfo.h
lib/Target/ARM/ARMISelLowering.cpp
lib/Target/ARM/ARMLowOverheadLoops.cpp
test/CodeGen/Thumb2/LowOverheadLoops/end-positive-offset.mir [new file with mode: 0644]
test/CodeGen/Thumb2/LowOverheadLoops/size-limit.mir
test/CodeGen/Thumb2/LowOverheadLoops/while-negative-offset.mir [new file with mode: 0644]

index d969a6f..400bba3 100644 (file)
@@ -132,6 +132,10 @@ public:
 
   unsigned getOffsetOf(MachineInstr *MI) const;
 
+  unsigned getOffsetOf(MachineBasicBlock *MBB) const {
+    return BBInfo[MBB->getNumber()].Offset;
+  }
+
   void adjustBBOffsetsAfter(MachineBasicBlock *MBB);
 
   void adjustBBSize(MachineBasicBlock *MBB, int Size) {
index 36b669b..dd11ed6 100644 (file)
@@ -12989,22 +12989,26 @@ static SDValue PerformHWLoopCombine(SDNode *N,
                                     const ARMSubtarget *ST) {
   // Look for (brcond (xor test.set.loop.iterations, -1)
   SDValue CC = N->getOperand(1);
+  unsigned Opc = CC->getOpcode();
+  SDValue Int;
 
-  if (CC->getOpcode() != ISD::XOR && CC->getOpcode() != ISD::SETCC)
-    return SDValue();
+  if ((Opc == ISD::XOR || Opc == ISD::SETCC) &&
+      (CC->getOperand(0)->getOpcode() == ISD::INTRINSIC_W_CHAIN)) {
+
+    assert((isa<ConstantSDNode>(CC->getOperand(1)) &&
+            cast<ConstantSDNode>(CC->getOperand(1))->isOne()) &&
+            "Expected to compare against 1");
 
-  if (CC->getOperand(0)->getOpcode() != ISD::INTRINSIC_W_CHAIN)
+    Int = CC->getOperand(0);
+  } else if (CC->getOpcode() == ISD::INTRINSIC_W_CHAIN)
+    Int = CC;
+  else 
     return SDValue();
 
-  SDValue Int = CC->getOperand(0);
   unsigned IntOp = cast<ConstantSDNode>(Int.getOperand(1))->getZExtValue();
   if (IntOp != Intrinsic::test_set_loop_iterations)
     return SDValue();
 
-  assert((isa<ConstantSDNode>(CC->getOperand(1)) &&
-          cast<ConstantSDNode>(CC->getOperand(1))->isOne()) &&
-          "Expected to compare against 1");
-
   SDLoc dl(Int);
   SDValue Chain = N->getOperand(0);
   SDValue Elements = Int.getOperand(2);
index a790ab0..cedf3bd 100644 (file)
@@ -91,6 +91,7 @@ bool ARMLowOverheadLoops::runOnMachineFunction(MachineFunction &MF) {
     MF.getSubtarget().getInstrInfo());
   BBUtils = std::unique_ptr<ARMBasicBlockUtils>(new ARMBasicBlockUtils(MF));
   BBUtils->computeAllBlockSizes();
+  BBUtils->adjustBBOffsetsAfter(&MF.front());
 
   bool Changed = false;
   for (auto ML : MLI) {
@@ -200,9 +201,18 @@ bool ARMLowOverheadLoops::ProcessLoop(MachineLoop *ML) {
       End->getOperand(1).getMBB() != ML->getHeader())
     report_fatal_error("Expected LoopEnd to target Loop Header");
 
-  // The LE instructions has 12-bits for the label offset.
-  if (!BBUtils->isBBInRange(End, ML->getHeader(), 4096)) {
-    LLVM_DEBUG(dbgs() << "ARM Loops: Too large for a low-overhead loop!\n");
+  // The WLS and LE instructions have 12-bits for the label offset. WLS
+  // requires a positive offset, while LE uses negative.
+  if (BBUtils->getOffsetOf(End) < BBUtils->getOffsetOf(ML->getHeader()) ||
+      !BBUtils->isBBInRange(End, ML->getHeader(), 4094)) {
+    LLVM_DEBUG(dbgs() << "ARM Loops: LE offset is out-of-range\n");
+    Revert = true;
+  }
+  if (Start->getOpcode() == ARM::t2WhileLoopStart &&
+      (BBUtils->getOffsetOf(Start) >
+       BBUtils->getOffsetOf(Start->getOperand(1).getMBB()) ||
+       !BBUtils->isBBInRange(Start, Start->getOperand(1).getMBB(), 4094))) {
+    LLVM_DEBUG(dbgs() << "ARM Loops: WLS offset is out-of-range!\n");
     Revert = true;
   }
 
diff --git a/test/CodeGen/Thumb2/LowOverheadLoops/end-positive-offset.mir b/test/CodeGen/Thumb2/LowOverheadLoops/end-positive-offset.mir
new file mode 100644 (file)
index 0000000..2712714
--- /dev/null
@@ -0,0 +1,224 @@
+# RUN: llc -run-pass=arm-low-overhead-loops %s -verify-machineinstrs -o - | FileCheck %s
+
+# CHECK-NOT: DoLoopStart
+# CHECK-NOT: DLS
+# CHECK: bb.2.for.body:
+# CHECK:   t2CMPri $lr, 0, 14, $cpsr, implicit-def $cpsr
+# CHECK:   t2Bcc %bb.4, 1, $cpsr
+# CHECK:   tB %bb.3, 14, $noreg
+# CHECK: bb.3.for.cond.cleanup:
+# CHECK: bb.4.for.header:
+
+--- |
+  target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
+  target triple = "thumbv8.1m.main-unknown-unknown"
+  
+  define void @size_limit(i32* nocapture %a, i32* nocapture readonly %b, i32* nocapture readonly %c, i32 %N) #0 {
+  entry:
+    call void @llvm.set.loop.iterations.i32(i32 %N)
+    br label %for.body.preheader
+  
+  for.body.preheader:                               ; preds = %entry
+    %scevgep = getelementptr i32, i32* %a, i32 -1
+    %scevgep4 = getelementptr i32, i32* %c, i32 -1
+    %scevgep8 = getelementptr i32, i32* %b, i32 -1
+    br label %for.header
+  
+  for.body:                                         ; preds = %for.header
+    %scevgep11 = getelementptr i32, i32* %lsr.iv9, i32 1
+    %ld1 = load i32, i32* %scevgep11, align 4
+    %scevgep7 = getelementptr i32, i32* %lsr.iv5, i32 1
+    %ld2 = load i32, i32* %scevgep7, align 4
+    %mul = mul nsw i32 %ld2, %ld1
+    %scevgep3 = getelementptr i32, i32* %lsr.iv1, i32 1
+    store i32 %mul, i32* %scevgep3, align 4
+    %scevgep2 = getelementptr i32, i32* %lsr.iv1, i32 1
+    %scevgep6 = getelementptr i32, i32* %lsr.iv5, i32 1
+    %scevgep10 = getelementptr i32, i32* %lsr.iv9, i32 1
+    %count.next = call i32 @llvm.loop.decrement.reg.i32.i32.i32(i32 %count, i32 1)
+    %cmp = icmp ne i32 %count.next, 0
+    br i1 %cmp, label %for.cond.cleanup, label %for.header
+  
+  for.cond.cleanup:                                 ; preds = %for.body
+    ret void
+  
+  for.header:                                       ; preds = %for.body, %for.body.preheader
+    %lsr.iv9 = phi i32* [ %scevgep8, %for.body.preheader ], [ %scevgep10, %for.body ]
+    %lsr.iv5 = phi i32* [ %scevgep4, %for.body.preheader ], [ %scevgep6, %for.body ]
+    %lsr.iv1 = phi i32* [ %scevgep, %for.body.preheader ], [ %scevgep2, %for.body ]
+    %count = phi i32 [ %N, %for.body.preheader ], [ %count.next, %for.body ]
+    br label %for.body
+  }
+  
+  ; Function Attrs: nounwind
+  declare i32 @llvm.arm.space(i32, i32) #1
+  
+  ; Function Attrs: noduplicate nounwind
+  declare void @llvm.set.loop.iterations.i32(i32) #2
+  
+  ; Function Attrs: noduplicate nounwind
+  declare i32 @llvm.loop.decrement.reg.i32.i32.i32(i32, i32) #2
+  
+  ; Function Attrs: nounwind
+  declare void @llvm.stackprotector(i8*, i8**) #3
+  
+  attributes #0 = { "target-features"="+lob" }
+  attributes #1 = { nounwind "target-features"="+lob" }
+  attributes #2 = { noduplicate nounwind "target-features"="+lob" }
+  attributes #3 = { nounwind }
+
+...
+---
+name:            size_limit
+alignment:       1
+exposesReturnsTwice: false
+legalized:       false
+regBankSelected: false
+selected:        false
+failedISel:      false
+tracksRegLiveness: false
+hasWinCFI:       false
+registers:       []
+liveins:         
+  - { reg: '$r0', virtual-reg: '' }
+  - { reg: '$r1', virtual-reg: '' }
+  - { reg: '$r2', virtual-reg: '' }
+  - { reg: '$r3', virtual-reg: '' }
+frameInfo:       
+  isFrameAddressTaken: false
+  isReturnAddressTaken: false
+  hasStackMap:     false
+  hasPatchPoint:   false
+  stackSize:       56
+  offsetAdjustment: 0
+  maxAlignment:    4
+  adjustsStack:    false
+  hasCalls:        false
+  stackProtector:  ''
+  maxCallFrameSize: 0
+  cvBytesOfCalleeSavedRegisters: 0
+  hasOpaqueSPAdjustment: false
+  hasVAStart:      false
+  hasMustTailInVarArgFunc: false
+  localFrameSize:  0
+  savePoint:       ''
+  restorePoint:    ''
+fixedStack:      []
+stack:           
+  - { id: 0, name: '', type: spill-slot, offset: -12, size: 4, alignment: 4, 
+      stack-id: default, callee-saved-register: '', callee-saved-restored: true, 
+      debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
+  - { id: 1, name: '', type: spill-slot, offset: -16, size: 4, alignment: 4, 
+      stack-id: default, callee-saved-register: '', callee-saved-restored: true, 
+      debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
+  - { id: 2, name: '', type: spill-slot, offset: -20, size: 4, alignment: 4, 
+      stack-id: default, callee-saved-register: '', callee-saved-restored: true, 
+      debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
+  - { id: 3, name: '', type: spill-slot, offset: -24, size: 4, alignment: 4, 
+      stack-id: default, callee-saved-register: '', callee-saved-restored: true, 
+      debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
+  - { id: 4, name: '', type: spill-slot, offset: -28, size: 4, alignment: 4, 
+      stack-id: default, callee-saved-register: '', callee-saved-restored: true, 
+      debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
+  - { id: 5, name: '', type: spill-slot, offset: -32, size: 4, alignment: 4, 
+      stack-id: default, callee-saved-register: '', callee-saved-restored: true, 
+      debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
+  - { id: 6, name: '', type: spill-slot, offset: -36, size: 4, alignment: 4, 
+      stack-id: default, callee-saved-register: '', callee-saved-restored: true, 
+      debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
+  - { id: 7, name: '', type: spill-slot, offset: -40, size: 4, alignment: 4, 
+      stack-id: default, callee-saved-register: '', callee-saved-restored: true, 
+      debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
+  - { id: 8, name: '', type: spill-slot, offset: -44, size: 4, alignment: 4, 
+      stack-id: default, callee-saved-register: '', callee-saved-restored: true, 
+      debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
+  - { id: 9, name: '', type: spill-slot, offset: -48, size: 4, alignment: 4, 
+      stack-id: default, callee-saved-register: '', callee-saved-restored: true, 
+      debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
+  - { id: 10, name: '', type: spill-slot, offset: -52, size: 4, alignment: 4, 
+      stack-id: default, callee-saved-register: '', callee-saved-restored: true, 
+      debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
+  - { id: 11, name: '', type: spill-slot, offset: -56, size: 4, alignment: 4, 
+      stack-id: default, callee-saved-register: '', callee-saved-restored: true, 
+      debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
+  - { id: 12, name: '', type: spill-slot, offset: -4, size: 4, alignment: 4, 
+      stack-id: default, callee-saved-register: '$lr', callee-saved-restored: false, 
+      debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
+  - { id: 13, name: '', type: spill-slot, offset: -8, size: 4, alignment: 4, 
+      stack-id: default, callee-saved-register: '$r4', callee-saved-restored: true, 
+      debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
+callSites:       []
+constants:       []
+machineFunctionInfo: {}
+body:             |
+  bb.0.entry:
+    successors: %bb.1(0x80000000)
+  
+    frame-setup tPUSH 14, $noreg, killed $r4, killed $lr, implicit-def $sp, implicit $sp
+    frame-setup CFI_INSTRUCTION def_cfa_offset 8
+    frame-setup CFI_INSTRUCTION offset $lr, -4
+    frame-setup CFI_INSTRUCTION offset $r4, -8
+    $sp = frame-setup tSUBspi $sp, 12, 14, $noreg
+    frame-setup CFI_INSTRUCTION def_cfa_offset 56
+    t2DoLoopStart renamable $r3
+    tSTRspi killed $r3, $sp, 11, 14, $noreg :: (store 4 into %stack.0)
+    tSTRspi killed $r2, $sp, 10, 14, $noreg :: (store 4 into %stack.1)
+    tSTRspi killed $r1, $sp, 9, 14, $noreg :: (store 4 into %stack.2)
+    tSTRspi killed $r0, $sp, 8, 14, $noreg :: (store 4 into %stack.3)
+    tB %bb.1, 14, $noreg
+  
+  bb.1.for.body.preheader:
+    successors: %bb.4(0x80000000)
+  
+    $r0 = tLDRspi $sp, 8, 14, $noreg :: (load 4 from %stack.3)
+    renamable $r1, dead $cpsr = tSUBi3 killed renamable $r0, 4, 14, $noreg
+    $r2 = tLDRspi $sp, 10, 14, $noreg :: (load 4 from %stack.1)
+    renamable $r3, dead $cpsr = tSUBi3 killed renamable $r2, 4, 14, $noreg
+    $r12 = t2LDRi12 $sp, 36, 14, $noreg :: (load 4 from %stack.2)
+    renamable $lr = t2SUBri killed renamable $r12, 4, 14, $noreg, $noreg
+    $r4 = tLDRspi $sp, 11, 14, $noreg :: (load 4 from %stack.0)
+    t2STRi12 killed $lr, $sp, 28, 14, $noreg :: (store 4 into %stack.4)
+    tSTRspi killed $r3, $sp, 6, 14, $noreg :: (store 4 into %stack.5)
+    tSTRspi killed $r1, $sp, 5, 14, $noreg :: (store 4 into %stack.6)
+    tSTRspi killed $r4, $sp, 4, 14, $noreg :: (store 4 into %stack.7)
+    tB %bb.4, 14, $noreg
+  
+  bb.2.for.body:
+    successors: %bb.3(0x40000000), %bb.4(0x40000000)
+  
+    $r0 = tLDRspi $sp, 3, 14, $noreg :: (load 4 from %stack.8)
+    renamable $r1, renamable $r0 = t2LDR_PRE renamable $r0, 4, 14, $noreg :: (load 4 from %ir.scevgep11)
+    $r2 = tLDRspi $sp, 2, 14, $noreg :: (load 4 from %stack.9)
+    renamable $r3, renamable $r2 = t2LDR_PRE renamable $r2, 4, 14, $noreg :: (load 4 from %ir.scevgep7)
+    renamable $r1, dead $cpsr = nsw tMUL killed renamable $r3, killed renamable $r1, 14, $noreg
+    $r3 = tLDRspi $sp, 1, 14, $noreg :: (load 4 from %stack.10)
+    early-clobber renamable $r3 = t2STR_PRE killed renamable $r1, renamable $r3, 4, 14, $noreg :: (store 4 into %ir.scevgep3)
+    $r1 = tLDRspi $sp, 0, 14, $noreg :: (load 4 from %stack.11)
+    $lr = tMOVr killed $r1, 14, $noreg
+    renamable $lr = t2LoopDec killed renamable $lr, 1
+    $r12 = tMOVr $lr, 14, $noreg
+    tSTRspi killed $r0, $sp, 7, 14, $noreg :: (store 4 into %stack.4)
+    tSTRspi killed $r2, $sp, 6, 14, $noreg :: (store 4 into %stack.5)
+    tSTRspi killed $r3, $sp, 5, 14, $noreg :: (store 4 into %stack.6)
+    t2STRi12 killed $r12, $sp, 16, 14, $noreg :: (store 4 into %stack.7)
+    t2LoopEnd killed renamable $lr, %bb.4
+    tB %bb.3, 14, $noreg
+  
+  bb.3.for.cond.cleanup:
+    $sp = tADDspi $sp, 12, 14, $noreg
+    tPOP_RET 14, $noreg, def $r4, def $pc
+  
+  bb.4.for.header:
+    successors: %bb.2(0x80000000)
+  
+    $r0 = tLDRspi $sp, 4, 14, $noreg :: (load 4 from %stack.7)
+    $r1 = tLDRspi $sp, 5, 14, $noreg :: (load 4 from %stack.6)
+    $r2 = tLDRspi $sp, 6, 14, $noreg :: (load 4 from %stack.5)
+    $r3 = tLDRspi $sp, 7, 14, $noreg :: (load 4 from %stack.4)
+    tSTRspi killed $r0, $sp, 0, 14, $noreg :: (store 4 into %stack.11)
+    tSTRspi killed $r1, $sp, 1, 14, $noreg :: (store 4 into %stack.10)
+    tSTRspi killed $r2, $sp, 2, 14, $noreg :: (store 4 into %stack.9)
+    tSTRspi killed $r3, $sp, 3, 14, $noreg :: (store 4 into %stack.8)
+    tB %bb.2, 14, $noreg
+
+...
index 1739bda..00255e4 100644 (file)
@@ -29,7 +29,7 @@
     %lsr.iv5 = phi i32* [ %scevgep4, %for.body.preheader ], [ %scevgep6, %for.body ]
     %lsr.iv1 = phi i32* [ %scevgep, %for.body.preheader ], [ %scevgep2, %for.body ]
     %0 = phi i32 [ %N, %for.body.preheader ], [ %3, %for.body ]
-    %size = call i32 @llvm.arm.space(i32 4072, i32 undef)
+    %size = call i32 @llvm.arm.space(i32 4070, i32 undef)
     %scevgep11 = getelementptr i32, i32* %lsr.iv9, i32 1
     %1 = load i32, i32* %scevgep11, align 4, !tbaa !3
     %scevgep7 = getelementptr i32, i32* %lsr.iv5, i32 1
@@ -140,7 +140,7 @@ body:             |
   bb.1.for.body:
     successors: %bb.1(0x7c000000), %bb.2(0x04000000)
   
-    dead renamable $r3 = SPACE 4072, undef renamable $r0
+    dead renamable $r3 = SPACE 4070, undef renamable $r0
     renamable $r12, renamable $r1 = t2LDR_PRE killed renamable $r1, 4, 14, $noreg :: (load 4 from %ir.scevgep11, !tbaa !3)
     renamable $r3, renamable $r2 = t2LDR_PRE killed renamable $r2, 4, 14, $noreg :: (load 4 from %ir.scevgep7, !tbaa !3)
     renamable $r3 = nsw t2MUL killed renamable $r3, killed renamable $r12, 14, $noreg
diff --git a/test/CodeGen/Thumb2/LowOverheadLoops/while-negative-offset.mir b/test/CodeGen/Thumb2/LowOverheadLoops/while-negative-offset.mir
new file mode 100644 (file)
index 0000000..0095f8d
--- /dev/null
@@ -0,0 +1,199 @@
+# RUN: llc -run-pass=arm-low-overhead-loops -o - -verify-machineinstrs %s | FileCheck %s
+# CHECK-NOT: WLS
+# CHECK-NOT: WhileLoopStart
+
+--- |
+  source_filename = "while-size-limit.ll"
+  target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
+  target triple = "thumbv8.1m.main"
+  
+  define void @size_limit(i32* nocapture %a, i32* nocapture readonly %b, i32* nocapture readonly %c, i32 %N) #0 {
+  entry:
+    br label %while
+  
+  for.cond.cleanup:                                 ; preds = %while, %for.body
+    ret void
+  
+  for.body.preheader:                               ; preds = %while
+    %scevgep = getelementptr i32, i32* %a, i32 -1
+    %scevgep4 = getelementptr i32, i32* %c, i32 -1
+    %scevgep8 = getelementptr i32, i32* %b, i32 -1
+    br label %for.body
+  
+  for.body:                                         ; preds = %for.body, %for.body.preheader
+    %lsr.iv9 = phi i32* [ %scevgep8, %for.body.preheader ], [ %scevgep10, %for.body ]
+    %lsr.iv5 = phi i32* [ %scevgep4, %for.body.preheader ], [ %scevgep6, %for.body ]
+    %lsr.iv1 = phi i32* [ %scevgep, %for.body.preheader ], [ %scevgep2, %for.body ]
+    %0 = phi i32 [ %N, %for.body.preheader ], [ %3, %for.body ]
+    %scevgep11 = getelementptr i32, i32* %lsr.iv9, i32 1
+    %1 = load i32, i32* %scevgep11, align 4
+    %scevgep7 = getelementptr i32, i32* %lsr.iv5, i32 1
+    %2 = load i32, i32* %scevgep7, align 4
+    %mul = mul nsw i32 %2, %1
+    %scevgep3 = getelementptr i32, i32* %lsr.iv1, i32 1
+    store i32 %mul, i32* %scevgep3, align 4
+    %scevgep2 = getelementptr i32, i32* %lsr.iv1, i32 1
+    %scevgep6 = getelementptr i32, i32* %lsr.iv5, i32 1
+    %scevgep10 = getelementptr i32, i32* %lsr.iv9, i32 1
+    %3 = call i32 @llvm.loop.decrement.reg.i32.i32.i32(i32 %0, i32 1)
+    %4 = icmp ne i32 %3, 0
+    br i1 %4, label %for.body, label %for.cond.cleanup
+  
+  while:                                            ; preds = %entry
+    %cmp8 = call i1 @llvm.test.set.loop.iterations.i32(i32 %N)
+    br i1 %cmp8, label %for.body.preheader, label %for.cond.cleanup
+  }
+  
+  ; Function Attrs: nounwind
+  declare i32 @llvm.arm.space(i32, i32) #1
+  
+  ; Function Attrs: noduplicate nounwind
+  declare i1 @llvm.test.set.loop.iterations.i32(i32) #2
+  
+  ; Function Attrs: noduplicate nounwind
+  declare i32 @llvm.loop.decrement.reg.i32.i32.i32(i32, i32) #2
+  
+  ; Function Attrs: nounwind
+  declare void @llvm.stackprotector(i8*, i8**) #3
+  
+  attributes #0 = { "target-features"="+lob" }
+  attributes #1 = { nounwind "target-features"="+lob" }
+  attributes #2 = { noduplicate nounwind "target-features"="+lob" }
+  attributes #3 = { nounwind }
+
+...
+---
+name:            size_limit
+alignment:       1
+exposesReturnsTwice: false
+legalized:       false
+regBankSelected: false
+selected:        false
+failedISel:      false
+tracksRegLiveness: false
+hasWinCFI:       false
+registers:       []
+liveins:         
+  - { reg: '$r0', virtual-reg: '' }
+  - { reg: '$r1', virtual-reg: '' }
+  - { reg: '$r2', virtual-reg: '' }
+  - { reg: '$r3', virtual-reg: '' }
+frameInfo:       
+  isFrameAddressTaken: false
+  isReturnAddressTaken: false
+  hasStackMap:     false
+  hasPatchPoint:   false
+  stackSize:       40
+  offsetAdjustment: 0
+  maxAlignment:    4
+  adjustsStack:    false
+  hasCalls:        false
+  stackProtector:  ''
+  maxCallFrameSize: 0
+  cvBytesOfCalleeSavedRegisters: 0
+  hasOpaqueSPAdjustment: false
+  hasVAStart:      false
+  hasMustTailInVarArgFunc: false
+  localFrameSize:  0
+  savePoint:       ''
+  restorePoint:    ''
+fixedStack:      []
+stack:           
+  - { id: 0, name: '', type: spill-slot, offset: -12, size: 4, alignment: 4, 
+      stack-id: default, callee-saved-register: '', callee-saved-restored: true, 
+      debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
+  - { id: 1, name: '', type: spill-slot, offset: -16, size: 4, alignment: 4, 
+      stack-id: default, callee-saved-register: '', callee-saved-restored: true, 
+      debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
+  - { id: 2, name: '', type: spill-slot, offset: -20, size: 4, alignment: 4, 
+      stack-id: default, callee-saved-register: '', callee-saved-restored: true, 
+      debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
+  - { id: 3, name: '', type: spill-slot, offset: -24, size: 4, alignment: 4, 
+      stack-id: default, callee-saved-register: '', callee-saved-restored: true, 
+      debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
+  - { id: 4, name: '', type: spill-slot, offset: -28, size: 4, alignment: 4, 
+      stack-id: default, callee-saved-register: '', callee-saved-restored: true, 
+      debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
+  - { id: 5, name: '', type: spill-slot, offset: -32, size: 4, alignment: 4, 
+      stack-id: default, callee-saved-register: '', callee-saved-restored: true, 
+      debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
+  - { id: 6, name: '', type: spill-slot, offset: -36, size: 4, alignment: 4, 
+      stack-id: default, callee-saved-register: '', callee-saved-restored: true, 
+      debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
+  - { id: 7, name: '', type: spill-slot, offset: -40, size: 4, alignment: 4, 
+      stack-id: default, callee-saved-register: '', callee-saved-restored: true, 
+      debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
+  - { id: 8, name: '', type: spill-slot, offset: -4, size: 4, alignment: 4, 
+      stack-id: default, callee-saved-register: '$lr', callee-saved-restored: false, 
+      debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
+  - { id: 9, name: '', type: spill-slot, offset: -8, size: 4, alignment: 4, 
+      stack-id: default, callee-saved-register: '$r4', callee-saved-restored: true, 
+      debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
+callSites:       []
+constants:       []
+machineFunctionInfo: {}
+body:             |
+  bb.0.entry:
+    successors: %bb.4(0x80000000)
+  
+    frame-setup tPUSH 14, $noreg, killed $r4, killed $lr, implicit-def $sp, implicit $sp
+    frame-setup CFI_INSTRUCTION def_cfa_offset 8
+    frame-setup CFI_INSTRUCTION offset $lr, -4
+    frame-setup CFI_INSTRUCTION offset $r4, -8
+    $sp = frame-setup tSUBspi $sp, 8, 14, $noreg
+    frame-setup CFI_INSTRUCTION def_cfa_offset 40
+    tSTRspi killed $r3, $sp, 7, 14, $noreg :: (store 4 into %stack.0)
+    tSTRspi killed $r2, $sp, 6, 14, $noreg :: (store 4 into %stack.1)
+    tSTRspi killed $r1, $sp, 5, 14, $noreg :: (store 4 into %stack.2)
+    tSTRspi killed $r0, $sp, 4, 14, $noreg :: (store 4 into %stack.3)
+    tB %bb.4, 14, $noreg
+  
+  bb.1.for.cond.cleanup:
+    $sp = tADDspi $sp, 8, 14, $noreg
+    tPOP_RET 14, $noreg, def $r4, def $pc
+  
+  bb.2.for.body.preheader:
+    successors: %bb.3(0x80000000)
+  
+    $r0 = tLDRspi $sp, 4, 14, $noreg :: (load 4 from %stack.3)
+    renamable $r1, dead $cpsr = tSUBi3 killed renamable $r0, 4, 14, $noreg
+    $r2 = tLDRspi $sp, 6, 14, $noreg :: (load 4 from %stack.1)
+    renamable $r3, dead $cpsr = tSUBi3 killed renamable $r2, 4, 14, $noreg
+    $r12 = t2LDRi12 $sp, 20, 14, $noreg :: (load 4 from %stack.2)
+    renamable $lr = t2SUBri killed renamable $r12, 4, 14, $noreg, $noreg
+    $r4 = tLDRspi $sp, 7, 14, $noreg :: (load 4 from %stack.0)
+    t2STRi12 killed $lr, $sp, 12, 14, $noreg :: (store 4 into %stack.4)
+    tSTRspi killed $r3, $sp, 2, 14, $noreg :: (store 4 into %stack.5)
+    tSTRspi killed $r1, $sp, 1, 14, $noreg :: (store 4 into %stack.6)
+    tSTRspi killed $r4, $sp, 0, 14, $noreg :: (store 4 into %stack.7)
+    tB %bb.3, 14, $noreg
+  
+  bb.3.for.body:
+    successors: %bb.3(0x40000000), %bb.1(0x40000000)
+  
+    $r0 = tLDRspi $sp, 0, 14, $noreg :: (load 4 from %stack.7)
+    $r1 = tLDRspi $sp, 1, 14, $noreg :: (load 4 from %stack.6)
+    $r2 = tLDRspi $sp, 2, 14, $noreg :: (load 4 from %stack.5)
+    $r3 = tLDRspi $sp, 3, 14, $noreg :: (load 4 from %stack.4)
+    renamable $r12, renamable $r3 = t2LDR_PRE renamable $r3, 4, 14, $noreg :: (load 4 from %ir.scevgep11)
+    renamable $lr, renamable $r2 = t2LDR_PRE renamable $r2, 4, 14, $noreg :: (load 4 from %ir.scevgep7)
+    renamable $r12 = nsw t2MUL killed renamable $lr, killed renamable $r12, 14, $noreg
+    early-clobber renamable $r1 = t2STR_PRE killed renamable $r12, renamable $r1, 4, 14, $noreg :: (store 4 into %ir.scevgep3)
+    $lr = tMOVr killed $r0, 14, $noreg
+    renamable $lr = t2LoopDec killed renamable $lr, 1
+    $r0 = tMOVr $lr, 14, $noreg
+    tSTRspi killed $r0, $sp, 0, 14, $noreg :: (store 4 into %stack.7)
+    tSTRspi killed $r1, $sp, 1, 14, $noreg :: (store 4 into %stack.6)
+    tSTRspi killed $r2, $sp, 2, 14, $noreg :: (store 4 into %stack.5)
+    tSTRspi killed $r3, $sp, 3, 14, $noreg :: (store 4 into %stack.4)
+    t2LoopEnd killed renamable $lr, %bb.3
+    tB %bb.1, 14, $noreg
+  
+  bb.4.while:
+    successors: %bb.2(0x40000000), %bb.1(0x40000000)
+  
+    $r0 = tLDRspi $sp, 7, 14, $noreg :: (load 4 from %stack.0)
+    t2WhileLoopStart killed renamable $r0, %bb.2
+    tB %bb.1, 14, $noreg
+
+...