[llvm] [ARM] Do not emit unwind tables when saving LR around outlined call (PR #69611)

via llvm-commits llvm-commits at lists.llvm.org
Thu Oct 19 09:16:24 PDT 2023


https://github.com/ostannard created https://github.com/llvm/llvm-project/pull/69611

In some cases, the machine outliner needs to preserve LR across an
outlined call by pushing it onto the stack. Previously, this also
generated unwind table instructions, which is incorrect because EHABI
unwind tables cannot represent different stack frames a different points
in the function, so the extra unwind info applied to the entire
function.

The outliner code already avoided generating CFI instructions, but EHABI
unwind data is generated later from the actual instructions, so we need
to avoid using the FrameSetup and FrameDestroy flags to prevent unwind
data being generated.

This is my first multi-commit change done through github, it it OK to do this as one PR, or should I raise separate PRs for the two preparatory commits?

>From 7f6225708599d4cbf8bab432874cbbf7e96c0cd2 Mon Sep 17 00:00:00 2001
From: Oliver Stannard <oliver.stannard at arm.com>
Date: Thu, 19 Oct 2023 16:25:11 +0100
Subject: [PATCH 1/3] [ARM] Serialise isLRSpilled in MIR

Change-Id: Ib9cd2467dd262738652ffe1dbf0de579938f10b5
---
 .../lib/Target/ARM/ARMMachineFunctionInfo.cpp | 12 +++++++++
 llvm/lib/Target/ARM/ARMMachineFunctionInfo.h  | 26 +++++++++++++++++++
 llvm/lib/Target/ARM/ARMTargetMachine.cpp      | 21 +++++++++++++++
 llvm/lib/Target/ARM/ARMTargetMachine.h        |  8 ++++++
 4 files changed, 67 insertions(+)

diff --git a/llvm/lib/Target/ARM/ARMMachineFunctionInfo.cpp b/llvm/lib/Target/ARM/ARMMachineFunctionInfo.cpp
index aa9d8b54d963607..e842715445124c5 100644
--- a/llvm/lib/Target/ARM/ARMMachineFunctionInfo.cpp
+++ b/llvm/lib/Target/ARM/ARMMachineFunctionInfo.cpp
@@ -13,6 +13,18 @@ using namespace llvm;
 
 void ARMFunctionInfo::anchor() {}
 
+yaml::ARMFunctionInfo::ARMFunctionInfo(const llvm::ARMFunctionInfo &MFI)
+    : LRSpilled(MFI.isLRSpilled()) {}
+
+void yaml::ARMFunctionInfo::mappingImpl(yaml::IO &YamlIO) {
+  MappingTraits<ARMFunctionInfo>::mapping(YamlIO, *this);
+}
+
+void ARMFunctionInfo::initializeBaseYamlFields(
+    const yaml::ARMFunctionInfo &YamlMFI) {
+  LRSpilled = YamlMFI.LRSpilled;
+}
+
 static bool GetBranchTargetEnforcement(const Function &F,
                                        const ARMSubtarget *Subtarget) {
   if (!Subtarget->isMClass() || !Subtarget->hasV7Ops())
diff --git a/llvm/lib/Target/ARM/ARMMachineFunctionInfo.h b/llvm/lib/Target/ARM/ARMMachineFunctionInfo.h
index f7531ce78ccae4e..eea12a7d3f56874 100644
--- a/llvm/lib/Target/ARM/ARMMachineFunctionInfo.h
+++ b/llvm/lib/Target/ARM/ARMMachineFunctionInfo.h
@@ -16,12 +16,17 @@
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/SmallPtrSet.h"
 #include "llvm/CodeGen/MachineFunction.h"
+#include "llvm/CodeGen/MIRYamlMapping.h"
 #include "llvm/IR/GlobalVariable.h"
 #include "llvm/Support/ErrorHandling.h"
 #include <utility>
 
 namespace llvm {
 
+namespace yaml {
+struct ARMFunctionInfo;
+} // end namespace yaml
+
 class ARMSubtarget;
 
 /// ARMFunctionInfo - This class is derived from MachineFunctionInfo and
@@ -293,8 +298,29 @@ class ARMFunctionInfo : public MachineFunctionInfo {
   }
 
   bool branchTargetEnforcement() const { return BranchTargetEnforcement; }
+
+  void initializeBaseYamlFields(const yaml::ARMFunctionInfo &YamlMFI);
 };
 
+namespace yaml {
+struct ARMFunctionInfo final : public yaml::MachineFunctionInfo {
+  bool LRSpilled;
+
+  ARMFunctionInfo() = default;
+  ARMFunctionInfo(const llvm::ARMFunctionInfo &MFI);
+
+  void mappingImpl(yaml::IO &YamlIO) override;
+  ~ARMFunctionInfo() = default;
+};
+
+template <> struct MappingTraits<ARMFunctionInfo> {
+  static void mapping(IO &YamlIO, ARMFunctionInfo &MFI) {
+    YamlIO.mapOptional("isLRSpilled", MFI.LRSpilled);
+  }
+};
+
+} // end namespace yaml
+
 } // end namespace llvm
 
 #endif // LLVM_LIB_TARGET_ARM_ARMMACHINEFUNCTIONINFO_H
diff --git a/llvm/lib/Target/ARM/ARMTargetMachine.cpp b/llvm/lib/Target/ARM/ARMTargetMachine.cpp
index a80d485e750beba..0348ae20fbf1eb5 100644
--- a/llvm/lib/Target/ARM/ARMTargetMachine.cpp
+++ b/llvm/lib/Target/ARM/ARMTargetMachine.cpp
@@ -30,6 +30,7 @@
 #include "llvm/CodeGen/GlobalISel/Legalizer.h"
 #include "llvm/CodeGen/GlobalISel/LegalizerInfo.h"
 #include "llvm/CodeGen/GlobalISel/RegBankSelect.h"
+#include "llvm/CodeGen/MIRParser/MIParser.h"
 #include "llvm/CodeGen/MachineFunction.h"
 #include "llvm/CodeGen/MachineScheduler.h"
 #include "llvm/CodeGen/Passes.h"
@@ -619,3 +620,23 @@ void ARMPassConfig::addPreEmitPass2() {
     addPass(createEHContGuardCatchretPass());
   }
 }
+
+yaml::MachineFunctionInfo *
+ARMBaseTargetMachine::createDefaultFuncInfoYAML() const {
+  return new yaml::ARMFunctionInfo();
+}
+
+yaml::MachineFunctionInfo *
+ARMBaseTargetMachine::convertFuncInfoToYAML(const MachineFunction &MF) const {
+  const auto *MFI = MF.getInfo<ARMFunctionInfo>();
+  return new yaml::ARMFunctionInfo(*MFI);
+}
+
+bool ARMBaseTargetMachine::parseMachineFunctionInfo(
+    const yaml::MachineFunctionInfo &MFI, PerFunctionMIParsingState &PFS,
+    SMDiagnostic &Error, SMRange &SourceRange) const {
+  const auto &YamlMFI = static_cast<const yaml::ARMFunctionInfo &>(MFI);
+  MachineFunction &MF = PFS.MF;
+  MF.getInfo<ARMFunctionInfo>()->initializeBaseYamlFields(YamlMFI);
+  return false;
+}
diff --git a/llvm/lib/Target/ARM/ARMTargetMachine.h b/llvm/lib/Target/ARM/ARMTargetMachine.h
index 1754382692baadb..69d8fa8ada64983 100644
--- a/llvm/lib/Target/ARM/ARMTargetMachine.h
+++ b/llvm/lib/Target/ARM/ARMTargetMachine.h
@@ -83,6 +83,14 @@ class ARMBaseTargetMachine : public LLVMTargetMachine {
     // Addrspacecasts are always noops.
     return true;
   }
+
+  yaml::MachineFunctionInfo *createDefaultFuncInfoYAML() const override;
+  yaml::MachineFunctionInfo *
+  convertFuncInfoToYAML(const MachineFunction &MF) const override;
+  bool parseMachineFunctionInfo(const yaml::MachineFunctionInfo &,
+                                PerFunctionMIParsingState &PFS,
+                                SMDiagnostic &Error,
+                                SMRange &SourceRange) const override;
 };
 
 /// ARM/Thumb little endian target machine.

>From 899589476b5fe07f7d2eb3420fd22846fbb09fc4 Mon Sep 17 00:00:00 2001
From: Oliver Stannard <oliver.stannard at arm.com>
Date: Thu, 19 Oct 2023 16:31:01 +0100
Subject: [PATCH 2/3] Pre-commit test

Change-Id: Ib9695cae262e7858de620329428e8496c2a0ec8c
---
 .../CodeGen/ARM/machine-outliner-noreturn.mir | 645 ++++++++++++++++++
 1 file changed, 645 insertions(+)
 create mode 100644 llvm/test/CodeGen/ARM/machine-outliner-noreturn.mir

diff --git a/llvm/test/CodeGen/ARM/machine-outliner-noreturn.mir b/llvm/test/CodeGen/ARM/machine-outliner-noreturn.mir
new file mode 100644
index 000000000000000..852ef9d139d621c
--- /dev/null
+++ b/llvm/test/CodeGen/ARM/machine-outliner-noreturn.mir
@@ -0,0 +1,645 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
+# RUN: llc -run-pass=machine-outliner %s -o - | FileCheck %s
+
+--- |
+  source_filename = "test.cpp"
+  target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
+  target triple = "thumbv8.1m.main-arm-none-eabi"
+
+  @__stack_chk_guard = external dso_local global ptr
+
+  define hidden void @_Z2F3v() #0 {
+  entry:
+    %StackGuardSlot = alloca ptr, align 4
+    %i1 = alloca i8, i32 undef, align 8
+    ret void
+  }
+
+  declare void @llvm.memset.p0.i32(ptr nocapture writeonly, i8, i32, i1 immarg) #1
+
+  define hidden void @_Z3F38i(i32 noundef %P0) #0 {
+  entry:
+    %StackGuardSlot = alloca ptr, align 4
+    unreachable
+
+  cond.false:                                       ; preds = %entry
+    ret void
+
+  cond.end:                                         ; preds = %entry
+    ret void
+  }
+
+  declare void @__aeabi_assert(ptr, ptr, i32)
+
+  define hidden void @_Z3F63v(ptr %agg.result) #0 {
+  entry:
+    %StackGuardSlot = alloca ptr, align 4
+    ret void
+  }
+
+  define hidden void @_Z3F94iiz(i32 %P0, i32 %P1, ...) #0 {
+  entry:
+    %StackGuardSlot = alloca ptr, align 4
+    unreachable
+
+  cond.false:                                       ; preds = %entry
+    unreachable
+
+  cond.end:                                         ; preds = %cond.false, %entry
+    unreachable
+
+  cond.false3:                                      ; preds = %cond.end
+    unreachable
+
+  cond.end4:                                        ; preds = %cond.false3, %cond.end
+    unreachable
+  }
+
+  declare ptr @llvm.returnaddress(i32 immarg) #2
+
+  declare void @llvm.va_start(ptr) #3
+
+  declare void @__cxa_throw(ptr, ptr, ptr)
+
+  declare ptr @llvm.stackguard() #3
+
+  declare void @llvm.stackprotector(ptr, ptr) #3
+
+  attributes #0 = { minsize sspreq }
+  attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: write) }
+  attributes #2 = { nocallback nofree nosync nounwind willreturn memory(none) }
+  attributes #3 = { nocallback nofree nosync nounwind willreturn }
+  attributes #4 = { noreturn }
+
+...
+---
+name:            _Z2F3v
+alignment:       4
+exposesReturnsTwice: false
+legalized:       false
+regBankSelected: false
+selected:        false
+failedISel:      false
+tracksRegLiveness: true
+hasWinCFI:       false
+callsEHReturn:   false
+callsUnwindInit: false
+hasEHCatchret:   false
+hasEHScopes:     false
+hasEHFunclets:   false
+isOutlined:      false
+debugInstrRef:   false
+failsVerification: false
+tracksDebugUserValues: true
+registers:       []
+liveins:         []
+frameInfo:
+  isFrameAddressTaken: false
+  isReturnAddressTaken: false
+  hasStackMap:     false
+  hasPatchPoint:   false
+  stackSize:       24
+  offsetAdjustment: -16
+  maxAlignment:    4
+  adjustsStack:    true
+  hasCalls:        true
+  stackProtector:  '%stack.0.StackGuardSlot'
+  functionContext: ''
+  maxCallFrameSize: 0
+  cvBytesOfCalleeSavedRegisters: 0
+  hasOpaqueSPAdjustment: false
+  hasVAStart:      false
+  hasMustTailInVarArgFunc: false
+  hasTailCall:     false
+  localFrameSize:  4
+  savePoint:       ''
+  restorePoint:    ''
+fixedStack:      []
+stack:
+  - { id: 0, name: StackGuardSlot, type: default, offset: -20, size: 4,
+      alignment: 4, stack-id: default, callee-saved-register: '', callee-saved-restored: true,
+      local-offset: -4, debug-info-variable: '', debug-info-expression: '',
+      debug-info-location: '' }
+  - { id: 1, name: i1, type: variable-sized, offset: -20, alignment: 1,
+      stack-id: default, callee-saved-register: '', callee-saved-restored: true,
+      local-offset: -4, debug-info-variable: '', debug-info-expression: '',
+      debug-info-location: '' }
+  - { id: 2, 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: 3, name: '', type: spill-slot, offset: -8, size: 4, alignment: 4,
+      stack-id: default, callee-saved-register: '$r7', callee-saved-restored: true,
+      debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
+  - { id: 4, name: '', type: spill-slot, offset: -12, size: 4, alignment: 4,
+      stack-id: default, callee-saved-register: '$r6', callee-saved-restored: true,
+      debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
+  - { id: 5, name: '', type: spill-slot, offset: -16, size: 4, alignment: 4,
+      stack-id: default, callee-saved-register: '$r4', callee-saved-restored: true,
+      debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
+entry_values:    []
+callSites:       []
+debugValueSubstitutions: []
+constants:
+  - id:              0
+    value:           i32 -23388
+    alignment:       4
+    isTargetSpecific: false
+machineFunctionInfo:
+  isLRSpilled:     true
+body:             |
+  ; CHECK-LABEL: name: _Z2F3v
+  ; CHECK: bb.0.entry:
+  ; CHECK-NEXT:   successors: %bb.1(0x80000000)
+  ; CHECK-NEXT:   liveins: $r4, $r6, $lr
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT:   frame-setup tPUSH 14 /* CC::al */, $noreg, undef $r2, undef $r3, killed $r4, killed $r6, $r7, killed $lr, implicit-def $sp, implicit $sp
+  ; CHECK-NEXT:   frame-setup CFI_INSTRUCTION def_cfa_offset 24
+  ; CHECK-NEXT:   frame-setup CFI_INSTRUCTION offset $lr, -4
+  ; CHECK-NEXT:   frame-setup CFI_INSTRUCTION offset $r7, -8
+  ; CHECK-NEXT:   frame-setup CFI_INSTRUCTION offset $r6, -12
+  ; CHECK-NEXT:   frame-setup CFI_INSTRUCTION offset $r4, -16
+  ; CHECK-NEXT:   $r7 = frame-setup tADDrSPi $sp, 4, 14 /* CC::al */, $noreg
+  ; CHECK-NEXT:   frame-setup CFI_INSTRUCTION def_cfa $r7, 8
+  ; CHECK-NEXT:   $r0 = t2MOVi16 target-flags(arm-lo16) @__stack_chk_guard, 14 /* CC::al */, $noreg
+  ; CHECK-NEXT:   renamable $r1 = t2MVNi 91, 14 /* CC::al */, $noreg, $noreg
+  ; CHECK-NEXT:   $r0 = t2MOVTi16 killed $r0, target-flags(arm-hi16) @__stack_chk_guard, 14 /* CC::al */, $noreg
+  ; CHECK-NEXT:   $r0 = tLDRi killed $r0, 0, 14 /* CC::al */, $noreg :: (dereferenceable invariant load (s32) from @__stack_chk_guard)
+  ; CHECK-NEXT:   t2STRi8 killed renamable $r0, $r7, -12, 14 /* CC::al */, $noreg :: (volatile store (s32) into %stack.0.StackGuardSlot)
+  ; CHECK-NEXT:   $r0 = tMOVr $sp, 14 /* CC::al */, $noreg
+  ; CHECK-NEXT:   tSTRBi killed renamable $r1, renamable $r0, 2, 14 /* CC::al */, $noreg :: (store (s8) into %ir.i1 + 2, align 2, basealign 8)
+  ; CHECK-NEXT:   renamable $r1 = tLDRpci %const.0, 14 /* CC::al */, $noreg :: (load (s32) from constant-pool)
+  ; CHECK-NEXT:   tSTRHi killed renamable $r1, killed renamable $r0, 0, 14 /* CC::al */, $noreg :: (store (s16) into %ir.i1, align 8)
+  ; CHECK-NEXT:   tBL 14 /* CC::al */, $noreg, @OUTLINED_FUNCTION_1, implicit-def $lr, implicit $sp, implicit-def $r1, implicit-def $lr, implicit $noreg, implicit $sp
+  ; CHECK-NEXT:   renamable $r0 = t2LDRi8 $r7, -12, 14 /* CC::al */, $noreg :: (volatile load (s32) from %stack.0.StackGuardSlot)
+  ; CHECK-NEXT:   $r1 = tLDRi killed $r1, 0, 14 /* CC::al */, $noreg :: (dereferenceable invariant load (s32) from @__stack_chk_guard)
+  ; CHECK-NEXT:   tCMPr killed renamable $r1, killed renamable $r0, 14 /* CC::al */, $noreg, implicit-def $cpsr
+  ; CHECK-NEXT:   t2IT 0, 2, implicit-def $itstate
+  ; CHECK-NEXT:   $r4 = frame-destroy t2SUBri $r7, 8, 0 /* CC::eq */, $cpsr, $noreg, implicit $itstate
+  ; CHECK-NEXT:   $sp = frame-destroy tMOVr $r4, 0 /* CC::eq */, $cpsr, implicit $itstate
+  ; CHECK-NEXT:   frame-destroy tPOP_RET 0 /* CC::eq */, killed $cpsr, def $r4, def $r6, def $r7, def $pc, implicit killed $r4, implicit killed $itstate
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT: bb.1.entry:
+  ; CHECK-NEXT:   tBL 14 /* CC::al */, $noreg, &__stack_chk_fail, csr_aapcs, implicit-def dead $lr, implicit $sp, implicit-def $sp
+  bb.0.entry:
+    successors: %bb.1(0x00000800)
+    liveins: $r4, $r6, $lr
+
+    frame-setup tPUSH 14 /* CC::al */, $noreg, undef $r2, undef $r3, killed $r4, killed $r6, $r7, killed $lr, implicit-def $sp, implicit $sp
+    frame-setup CFI_INSTRUCTION def_cfa_offset 24
+    frame-setup CFI_INSTRUCTION offset $lr, -4
+    frame-setup CFI_INSTRUCTION offset $r7, -8
+    frame-setup CFI_INSTRUCTION offset $r6, -12
+    frame-setup CFI_INSTRUCTION offset $r4, -16
+    $r7 = frame-setup tADDrSPi $sp, 4, 14 /* CC::al */, $noreg
+    frame-setup CFI_INSTRUCTION def_cfa $r7, 8
+    $r0 = t2MOVi16 target-flags(arm-lo16) @__stack_chk_guard, 14 /* CC::al */, $noreg
+    renamable $r1 = t2MVNi 91, 14 /* CC::al */, $noreg, $noreg
+    $r0 = t2MOVTi16 killed $r0, target-flags(arm-hi16) @__stack_chk_guard, 14 /* CC::al */, $noreg
+    $r0 = tLDRi killed $r0, 0, 14 /* CC::al */, $noreg :: (dereferenceable invariant load (s32) from @__stack_chk_guard)
+    t2STRi8 killed renamable $r0, $r7, -12, 14 /* CC::al */, $noreg :: (volatile store (s32) into %stack.0.StackGuardSlot)
+    $r0 = tMOVr $sp, 14 /* CC::al */, $noreg
+    tSTRBi killed renamable $r1, renamable $r0, 2, 14 /* CC::al */, $noreg :: (store (s8) into %ir.i1 + 2, align 2, basealign 8)
+    renamable $r1 = tLDRpci %const.0, 14 /* CC::al */, $noreg :: (load (s32) from constant-pool)
+    tSTRHi killed renamable $r1, killed renamable $r0, 0, 14 /* CC::al */, $noreg :: (store (s16) into %ir.i1, align 8)
+    $r1 = t2MOVi16 target-flags(arm-lo16) @__stack_chk_guard, 14 /* CC::al */, $noreg
+    $r1 = t2MOVTi16 killed $r1, target-flags(arm-hi16) @__stack_chk_guard, 14 /* CC::al */, $noreg
+    renamable $r0 = t2LDRi8 $r7, -12, 14 /* CC::al */, $noreg :: (volatile load (s32) from %stack.0.StackGuardSlot)
+    $r1 = tLDRi killed $r1, 0, 14 /* CC::al */, $noreg :: (dereferenceable invariant load (s32) from @__stack_chk_guard)
+    tCMPr killed renamable $r1, killed renamable $r0, 14 /* CC::al */, $noreg, implicit-def $cpsr
+    t2IT 0, 2, implicit-def $itstate
+    $r4 = frame-destroy t2SUBri $r7, 8, 0 /* CC::eq */, $cpsr, $noreg, implicit $itstate
+    $sp = frame-destroy tMOVr $r4, 0 /* CC::eq */, $cpsr, implicit $itstate
+    frame-destroy tPOP_RET 0 /* CC::eq */, killed $cpsr, def $r4, def $r6, def $r7, def $pc, implicit killed $r4, implicit killed $itstate
+
+  bb.1.entry:
+    tBL 14 /* CC::al */, $noreg, &__stack_chk_fail, csr_aapcs, implicit-def dead $lr, implicit $sp, implicit-def $sp
+
+...
+---
+name:            _Z3F38i
+alignment:       4
+exposesReturnsTwice: false
+legalized:       false
+regBankSelected: false
+selected:        false
+failedISel:      false
+tracksRegLiveness: true
+hasWinCFI:       false
+callsEHReturn:   false
+callsUnwindInit: false
+hasEHCatchret:   false
+hasEHScopes:     false
+hasEHFunclets:   false
+isOutlined:      false
+debugInstrRef:   false
+failsVerification: false
+tracksDebugUserValues: true
+registers:       []
+liveins:
+  - { reg: '$r0', virtual-reg: '' }
+frameInfo:
+  isFrameAddressTaken: false
+  isReturnAddressTaken: false
+  hasStackMap:     false
+  hasPatchPoint:   false
+  stackSize:       16
+  offsetAdjustment: 0
+  maxAlignment:    4
+  adjustsStack:    true
+  hasCalls:        true
+  stackProtector:  '%stack.0.StackGuardSlot'
+  functionContext: ''
+  maxCallFrameSize: 0
+  cvBytesOfCalleeSavedRegisters: 0
+  hasOpaqueSPAdjustment: false
+  hasVAStart:      false
+  hasMustTailInVarArgFunc: false
+  hasTailCall:     true
+  localFrameSize:  4
+  savePoint:       ''
+  restorePoint:    ''
+fixedStack:      []
+stack:
+  - { id: 0, name: StackGuardSlot, type: default, offset: -12, size: 4,
+      alignment: 4, stack-id: default, callee-saved-register: '', callee-saved-restored: true,
+      local-offset: -4, debug-info-variable: '', debug-info-expression: '',
+      debug-info-location: '' }
+  - { id: 1, 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: 2, name: '', type: spill-slot, offset: -8, size: 4, alignment: 4,
+      stack-id: default, callee-saved-register: '$r7', callee-saved-restored: true,
+      debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
+entry_values:    []
+callSites:       []
+debugValueSubstitutions: []
+constants:       []
+machineFunctionInfo:
+  isLRSpilled:     true
+body:             |
+  ; CHECK-LABEL: name: _Z3F38i
+  ; CHECK: bb.0.entry:
+  ; CHECK-NEXT:   successors: %bb.1(0x40000000), %bb.3(0x40000000)
+  ; CHECK-NEXT:   liveins: $r0, $r7, $lr
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT:   frame-setup tPUSH 14 /* CC::al */, $noreg, undef $r5, undef $r6, killed $r7, killed $lr, implicit-def $sp, implicit $sp
+  ; CHECK-NEXT:   frame-setup CFI_INSTRUCTION def_cfa_offset 16
+  ; CHECK-NEXT:   frame-setup CFI_INSTRUCTION offset $lr, -4
+  ; CHECK-NEXT:   frame-setup CFI_INSTRUCTION offset $r7, -8
+  ; CHECK-NEXT:   tBL 14 /* CC::al */, $noreg, @OUTLINED_FUNCTION_1, implicit-def $lr, implicit $sp, implicit-def $r1, implicit-def $lr, implicit $noreg, implicit $sp
+  ; CHECK-NEXT:   $r1 = tLDRi killed $r1, 0, 14 /* CC::al */, $noreg :: (dereferenceable invariant load (s32) from @__stack_chk_guard)
+  ; CHECK-NEXT:   tSTRspi killed renamable $r1, $sp, 1, 14 /* CC::al */, $noreg :: (volatile store (s32) into %stack.0.StackGuardSlot)
+  ; CHECK-NEXT:   renamable $r1 = t2MOVi16 5480, 14 /* CC::al */, $noreg
+  ; CHECK-NEXT:   tCMPr killed renamable $r0, killed renamable $r1, 14 /* CC::al */, $noreg, implicit-def $cpsr
+  ; CHECK-NEXT:   t2Bcc %bb.3, 1 /* CC::ne */, killed $cpsr
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT: bb.1.cond.end:
+  ; CHECK-NEXT:   successors: %bb.2(0x80000000)
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT:   tBL 14 /* CC::al */, $noreg, @OUTLINED_FUNCTION_0, implicit-def $lr, implicit $sp, implicit-def $cpsr, implicit-def $lr, implicit-def $r0, implicit-def $r1, implicit $noreg, implicit $sp
+  ; CHECK-NEXT:   t2IT 0, 8, implicit-def $itstate
+  ; CHECK-NEXT:   frame-destroy tPOP_RET 0 /* CC::eq */, killed $cpsr, def dead $r2, def dead $r3, def $r7, def $pc, implicit killed $itstate
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT: bb.2.cond.false:
+  ; CHECK-NEXT:   successors:{{  $}}
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT:   tBL 14 /* CC::al */, $noreg, &__stack_chk_fail, csr_aapcs, implicit-def dead $lr, implicit $sp, implicit-def $sp
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT: bb.3.cond.false:
+  ; CHECK-NEXT:   successors: %bb.2(0x80000000)
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT:   tBL 14 /* CC::al */, $noreg, @OUTLINED_FUNCTION_0, implicit-def $lr, implicit $sp, implicit-def $cpsr, implicit-def $lr, implicit-def $r0, implicit-def $r1, implicit $noreg, implicit $sp
+  ; CHECK-NEXT:   t2IT 0, 2, implicit-def $itstate
+  ; CHECK-NEXT:   $r2, dead $noreg = tMOVi8 18, 0 /* CC::eq */, $cpsr, implicit $itstate
+  ; CHECK-NEXT:   $sp = frame-destroy t2LDMIA_UPD $sp, 0 /* CC::eq */, $cpsr, def dead $r1, def dead $r3, def dead $r7, def dead $lr, implicit $itstate
+  ; CHECK-NEXT:   tTAILJMPdND @__aeabi_assert, 0 /* CC::eq */, killed $cpsr, csr_aapcs, implicit $sp, implicit $sp, implicit undef $r0, implicit undef $r1, implicit killed $r2, implicit killed $r2, implicit-def dead $r2, implicit $sp, implicit-def $sp, implicit killed $itstate
+  ; CHECK-NEXT:   t2B %bb.2, 14 /* CC::al */, $noreg
+  bb.0.entry:
+    successors: %bb.1(0x40000000), %bb.3(0x40000000)
+    liveins: $r0, $r7, $lr
+
+    frame-setup tPUSH 14 /* CC::al */, $noreg, undef $r5, undef $r6, killed $r7, killed $lr, implicit-def $sp, implicit $sp
+    frame-setup CFI_INSTRUCTION def_cfa_offset 16
+    frame-setup CFI_INSTRUCTION offset $lr, -4
+    frame-setup CFI_INSTRUCTION offset $r7, -8
+    $r1 = t2MOVi16 target-flags(arm-lo16) @__stack_chk_guard, 14 /* CC::al */, $noreg
+    $r1 = t2MOVTi16 killed $r1, target-flags(arm-hi16) @__stack_chk_guard, 14 /* CC::al */, $noreg
+    $r1 = tLDRi killed $r1, 0, 14 /* CC::al */, $noreg :: (dereferenceable invariant load (s32) from @__stack_chk_guard)
+    tSTRspi killed renamable $r1, $sp, 1, 14 /* CC::al */, $noreg :: (volatile store (s32) into %stack.0.StackGuardSlot)
+    renamable $r1 = t2MOVi16 5480, 14 /* CC::al */, $noreg
+    tCMPr killed renamable $r0, killed renamable $r1, 14 /* CC::al */, $noreg, implicit-def $cpsr
+    t2Bcc %bb.3, 1 /* CC::ne */, killed $cpsr
+
+  bb.1.cond.end:
+    successors: %bb.2(0x00000800)
+
+    $r1 = t2MOVi16 target-flags(arm-lo16) @__stack_chk_guard, 14 /* CC::al */, $noreg
+    renamable $r0 = tLDRspi $sp, 1, 14 /* CC::al */, $noreg :: (volatile load (s32) from %stack.0.StackGuardSlot)
+    $r1 = t2MOVTi16 killed $r1, target-flags(arm-hi16) @__stack_chk_guard, 14 /* CC::al */, $noreg
+    $r1 = tLDRi killed $r1, 0, 14 /* CC::al */, $noreg :: (dereferenceable invariant load (s32) from @__stack_chk_guard)
+    tCMPr killed renamable $r1, killed renamable $r0, 14 /* CC::al */, $noreg, implicit-def $cpsr
+    t2IT 0, 8, implicit-def $itstate
+    frame-destroy tPOP_RET 0 /* CC::eq */, killed $cpsr, def dead $r2, def dead $r3, def $r7, def $pc, implicit killed $itstate
+
+  bb.2.cond.false:
+    successors:
+
+    tBL 14 /* CC::al */, $noreg, &__stack_chk_fail, csr_aapcs, implicit-def dead $lr, implicit $sp, implicit-def $sp
+
+  bb.3.cond.false:
+    successors: %bb.2(0x00000800)
+
+    $r1 = t2MOVi16 target-flags(arm-lo16) @__stack_chk_guard, 14 /* CC::al */, $noreg
+    renamable $r0 = tLDRspi $sp, 1, 14 /* CC::al */, $noreg :: (volatile load (s32) from %stack.0.StackGuardSlot)
+    $r1 = t2MOVTi16 killed $r1, target-flags(arm-hi16) @__stack_chk_guard, 14 /* CC::al */, $noreg
+    $r1 = tLDRi killed $r1, 0, 14 /* CC::al */, $noreg :: (dereferenceable invariant load (s32) from @__stack_chk_guard)
+    tCMPr killed renamable $r1, killed renamable $r0, 14 /* CC::al */, $noreg, implicit-def $cpsr
+    t2IT 0, 2, implicit-def $itstate
+    $r2, dead $noreg = tMOVi8 18, 0 /* CC::eq */, $cpsr, implicit $itstate
+    $sp = frame-destroy t2LDMIA_UPD $sp, 0 /* CC::eq */, $cpsr, def dead $r1, def dead $r3, def dead $r7, def dead $lr, implicit $itstate
+    tTAILJMPdND @__aeabi_assert, 0 /* CC::eq */, killed $cpsr, csr_aapcs, implicit $sp, implicit $sp, implicit undef $r0, implicit undef $r1, implicit killed $r2, implicit killed $r2, implicit-def dead $r2, implicit $sp, implicit-def $sp, implicit killed $itstate
+    t2B %bb.2, 14 /* CC::al */, $noreg
+
+...
+---
+name:            _Z3F63v
+alignment:       4
+exposesReturnsTwice: false
+legalized:       false
+regBankSelected: false
+selected:        false
+failedISel:      false
+tracksRegLiveness: true
+hasWinCFI:       false
+callsEHReturn:   false
+callsUnwindInit: false
+hasEHCatchret:   false
+hasEHScopes:     false
+hasEHFunclets:   false
+isOutlined:      false
+debugInstrRef:   false
+failsVerification: false
+tracksDebugUserValues: true
+registers:       []
+liveins:
+  - { reg: '$r0', virtual-reg: '' }
+frameInfo:
+  isFrameAddressTaken: false
+  isReturnAddressTaken: false
+  hasStackMap:     false
+  hasPatchPoint:   false
+  stackSize:       16
+  offsetAdjustment: 0
+  maxAlignment:    4
+  adjustsStack:    true
+  hasCalls:        true
+  stackProtector:  '%stack.0.StackGuardSlot'
+  functionContext: ''
+  maxCallFrameSize: 0
+  cvBytesOfCalleeSavedRegisters: 0
+  hasOpaqueSPAdjustment: false
+  hasVAStart:      false
+  hasMustTailInVarArgFunc: false
+  hasTailCall:     false
+  localFrameSize:  4
+  savePoint:       ''
+  restorePoint:    ''
+fixedStack:      []
+stack:
+  - { id: 0, name: StackGuardSlot, type: default, offset: -12, size: 4,
+      alignment: 4, stack-id: default, callee-saved-register: '', callee-saved-restored: true,
+      local-offset: -4, debug-info-variable: '', debug-info-expression: '',
+      debug-info-location: '' }
+  - { id: 1, 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: 2, name: '', type: spill-slot, offset: -8, size: 4, alignment: 4,
+      stack-id: default, callee-saved-register: '$r7', callee-saved-restored: true,
+      debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
+entry_values:    []
+callSites:       []
+debugValueSubstitutions: []
+constants:
+  - id:              0
+    value:           i32 1072693248
+    alignment:       4
+    isTargetSpecific: false
+machineFunctionInfo:
+  isLRSpilled:     true
+body:             |
+  ; CHECK-LABEL: name: _Z3F63v
+  ; CHECK: bb.0.entry:
+  ; CHECK-NEXT:   successors: %bb.1(0x80000000)
+  ; CHECK-NEXT:   liveins: $r0, $r7, $lr
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT:   frame-setup tPUSH 14 /* CC::al */, $noreg, undef $r5, undef $r6, killed $r7, killed $lr, implicit-def $sp, implicit $sp
+  ; CHECK-NEXT:   frame-setup CFI_INSTRUCTION def_cfa_offset 16
+  ; CHECK-NEXT:   frame-setup CFI_INSTRUCTION offset $lr, -4
+  ; CHECK-NEXT:   frame-setup CFI_INSTRUCTION offset $r7, -8
+  ; CHECK-NEXT:   $r1 = t2MOVi16 target-flags(arm-lo16) @__stack_chk_guard, 14 /* CC::al */, $noreg
+  ; CHECK-NEXT:   renamable $r2, dead $cpsr = tMOVi8 0, 14 /* CC::al */, $noreg
+  ; CHECK-NEXT:   $r1 = t2MOVTi16 killed $r1, target-flags(arm-hi16) @__stack_chk_guard, 14 /* CC::al */, $noreg
+  ; CHECK-NEXT:   $r1 = tLDRi killed $r1, 0, 14 /* CC::al */, $noreg :: (dereferenceable invariant load (s32) from @__stack_chk_guard)
+  ; CHECK-NEXT:   tSTRspi killed renamable $r1, $sp, 1, 14 /* CC::al */, $noreg :: (volatile store (s32) into %stack.0.StackGuardSlot)
+  ; CHECK-NEXT:   renamable $r1 = tLDRpci %const.0, 14 /* CC::al */, $noreg :: (load (s32) from constant-pool)
+  ; CHECK-NEXT:   t2STRDi8 killed $r2, killed $r1, killed $r0, 0, 14 /* CC::al */, $noreg :: (store (s32) into %ir.agg.result, align 8), (store (s32) into %ir.agg.result + 4, basealign 8)
+  ; CHECK-NEXT:   tBL 14 /* CC::al */, $noreg, @OUTLINED_FUNCTION_1, implicit-def $lr, implicit $sp, implicit-def $r1, implicit-def $lr, implicit $noreg, implicit $sp
+  ; CHECK-NEXT:   renamable $r0 = tLDRspi $sp, 1, 14 /* CC::al */, $noreg :: (volatile load (s32) from %stack.0.StackGuardSlot)
+  ; CHECK-NEXT:   $r1 = tLDRi killed $r1, 0, 14 /* CC::al */, $noreg :: (dereferenceable invariant load (s32) from @__stack_chk_guard)
+  ; CHECK-NEXT:   tCMPr killed renamable $r1, killed renamable $r0, 14 /* CC::al */, $noreg, implicit-def $cpsr
+  ; CHECK-NEXT:   t2IT 0, 8, implicit-def $itstate
+  ; CHECK-NEXT:   frame-destroy tPOP_RET 0 /* CC::eq */, killed $cpsr, def dead $r2, def dead $r3, def $r7, def $pc, implicit killed $itstate
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT: bb.1.entry:
+  ; CHECK-NEXT:   tBL 14 /* CC::al */, $noreg, &__stack_chk_fail, csr_aapcs, implicit-def dead $lr, implicit $sp, implicit-def $sp
+  bb.0.entry:
+    successors: %bb.1(0x00000800)
+    liveins: $r0, $r7, $lr
+
+    frame-setup tPUSH 14 /* CC::al */, $noreg, undef $r5, undef $r6, killed $r7, killed $lr, implicit-def $sp, implicit $sp
+    frame-setup CFI_INSTRUCTION def_cfa_offset 16
+    frame-setup CFI_INSTRUCTION offset $lr, -4
+    frame-setup CFI_INSTRUCTION offset $r7, -8
+    $r1 = t2MOVi16 target-flags(arm-lo16) @__stack_chk_guard, 14 /* CC::al */, $noreg
+    renamable $r2, dead $cpsr = tMOVi8 0, 14 /* CC::al */, $noreg
+    $r1 = t2MOVTi16 killed $r1, target-flags(arm-hi16) @__stack_chk_guard, 14 /* CC::al */, $noreg
+    $r1 = tLDRi killed $r1, 0, 14 /* CC::al */, $noreg :: (dereferenceable invariant load (s32) from @__stack_chk_guard)
+    tSTRspi killed renamable $r1, $sp, 1, 14 /* CC::al */, $noreg :: (volatile store (s32) into %stack.0.StackGuardSlot)
+    renamable $r1 = tLDRpci %const.0, 14 /* CC::al */, $noreg :: (load (s32) from constant-pool)
+    t2STRDi8 killed $r2, killed $r1, killed $r0, 0, 14 /* CC::al */, $noreg :: (store (s32) into %ir.agg.result, align 8), (store (s32) into %ir.agg.result + 4, basealign 8)
+    $r1 = t2MOVi16 target-flags(arm-lo16) @__stack_chk_guard, 14 /* CC::al */, $noreg
+    $r1 = t2MOVTi16 killed $r1, target-flags(arm-hi16) @__stack_chk_guard, 14 /* CC::al */, $noreg
+    renamable $r0 = tLDRspi $sp, 1, 14 /* CC::al */, $noreg :: (volatile load (s32) from %stack.0.StackGuardSlot)
+    $r1 = tLDRi killed $r1, 0, 14 /* CC::al */, $noreg :: (dereferenceable invariant load (s32) from @__stack_chk_guard)
+    tCMPr killed renamable $r1, killed renamable $r0, 14 /* CC::al */, $noreg, implicit-def $cpsr
+    t2IT 0, 8, implicit-def $itstate
+    frame-destroy tPOP_RET 0 /* CC::eq */, killed $cpsr, def dead $r2, def dead $r3, def $r7, def $pc, implicit killed $itstate
+
+  bb.1.entry:
+    tBL 14 /* CC::al */, $noreg, &__stack_chk_fail, csr_aapcs, implicit-def dead $lr, implicit $sp, implicit-def $sp
+
+...
+---
+name:            _Z3F94iiz
+alignment:       4
+exposesReturnsTwice: false
+legalized:       false
+regBankSelected: false
+selected:        false
+failedISel:      false
+tracksRegLiveness: true
+hasWinCFI:       false
+callsEHReturn:   false
+callsUnwindInit: false
+hasEHCatchret:   false
+hasEHScopes:     false
+hasEHFunclets:   false
+isOutlined:      false
+debugInstrRef:   false
+failsVerification: false
+tracksDebugUserValues: true
+registers:       []
+liveins:
+  - { reg: '$r0', virtual-reg: '' }
+  - { reg: '$r1', virtual-reg: '' }
+  - { reg: '$r2', virtual-reg: '' }
+  - { reg: '$r3', virtual-reg: '' }
+  - { reg: '$lr', virtual-reg: '' }
+frameInfo:
+  isFrameAddressTaken: false
+  isReturnAddressTaken: true
+  hasStackMap:     false
+  hasPatchPoint:   false
+  stackSize:       24
+  offsetAdjustment: 0
+  maxAlignment:    4
+  adjustsStack:    true
+  hasCalls:        true
+  stackProtector:  '%stack.0.StackGuardSlot'
+  functionContext: ''
+  maxCallFrameSize: 0
+  cvBytesOfCalleeSavedRegisters: 0
+  hasOpaqueSPAdjustment: false
+  hasVAStart:      true
+  hasMustTailInVarArgFunc: false
+  hasTailCall:     false
+  localFrameSize:  4
+  savePoint:       ''
+  restorePoint:    ''
+fixedStack:
+  - { id: 0, type: default, offset: -8, size: 8, alignment: 8, stack-id: default,
+      isImmutable: false, isAliased: false, callee-saved-register: '',
+      callee-saved-restored: true, debug-info-variable: '', debug-info-expression: '',
+      debug-info-location: '' }
+stack:
+  - { id: 0, name: StackGuardSlot, type: default, offset: -20, size: 4,
+      alignment: 4, stack-id: default, callee-saved-register: '', callee-saved-restored: true,
+      local-offset: -4, debug-info-variable: '', debug-info-expression: '',
+      debug-info-location: '' }
+  - { id: 1, name: '', type: spill-slot, offset: -12, size: 4, alignment: 4,
+      stack-id: default, callee-saved-register: '$lr', callee-saved-restored: true,
+      debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
+  - { id: 2, name: '', type: spill-slot, offset: -16, size: 4, alignment: 4,
+      stack-id: default, callee-saved-register: '$r4', callee-saved-restored: true,
+      debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
+entry_values:    []
+callSites:       []
+debugValueSubstitutions: []
+constants:       []
+machineFunctionInfo:
+  isLRSpilled:     true
+body:             |
+  ; CHECK-LABEL: name: _Z3F94iiz
+  ; CHECK: bb.0.entry:
+  ; CHECK-NEXT:   successors: %bb.2(0x40000000), %bb.1(0x40000000)
+  ; CHECK-NEXT:   liveins: $lr, $r0, $r1, $r2, $r3, $r4
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT:   $sp = frame-setup tSUBspi $sp, 2, 14 /* CC::al */, $noreg
+  ; CHECK-NEXT:   frame-setup CFI_INSTRUCTION def_cfa_offset 8
+  ; CHECK-NEXT:   frame-setup tPUSH 14 /* CC::al */, $noreg, undef $r2, undef $r3, killed $r4, $lr, implicit-def $sp, implicit $sp
+  ; CHECK-NEXT:   frame-setup CFI_INSTRUCTION def_cfa_offset 24
+  ; CHECK-NEXT:   frame-setup CFI_INSTRUCTION offset $lr, -12
+  ; CHECK-NEXT:   frame-setup CFI_INSTRUCTION offset $r4, -16
+  ; CHECK-NEXT:   $r4 = tMOVr killed $r1, 14 /* CC::al */, $noreg
+  ; CHECK-NEXT:   early-clobber $sp = frame-setup t2STR_PRE killed $lr, $sp, -8, 14 /* CC::al */, $noreg
+  ; CHECK-NEXT:   tBL 14 /* CC::al */, $noreg, @OUTLINED_FUNCTION_1, implicit-def $lr, implicit $sp, implicit-def $lr, implicit-def $sp, implicit-def $r1, implicit $noreg, implicit $sp
+  ; CHECK-NEXT:   $lr, $sp = frame-destroy t2LDR_POST $sp, 8, 14 /* CC::al */, $noreg
+  ; CHECK-NEXT:   tCMPi8 killed renamable $r0, 0, 14 /* CC::al */, $noreg, implicit-def $cpsr
+  ; CHECK-NEXT:   $r1 = tLDRi killed $r1, 0, 14 /* CC::al */, $noreg :: (dereferenceable invariant load (s32) from @__stack_chk_guard)
+  ; CHECK-NEXT:   t2STRDi8 killed $r2, killed $r3, $sp, 16, 14 /* CC::al */, $noreg :: (store (s32) into %fixed-stack.0, align 8), (store (s32) into %fixed-stack.0 + 4)
+  ; CHECK-NEXT:   tSTRspi killed renamable $r1, $sp, 1, 14 /* CC::al */, $noreg :: (volatile store (s32) into %stack.0.StackGuardSlot)
+  ; CHECK-NEXT:   renamable $r1 = tADDrSPi $sp, 4, 14 /* CC::al */, $noreg
+  ; CHECK-NEXT:   tSTRi killed renamable $r1, undef renamable $r0, 0, 14 /* CC::al */, $noreg :: (store (s32) into `ptr undef`)
+  ; CHECK-NEXT:   INLINEASM &"", 1 /* sideeffect attdialect */, 327689 /* reguse:GPR */, killed renamable $lr
+  ; CHECK-NEXT:   t2Bcc %bb.2, 0 /* CC::eq */, killed $cpsr
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT: bb.1.cond.false:
+  ; CHECK-NEXT:   successors: %bb.2(0x80000000)
+  ; CHECK-NEXT:   liveins: $r4
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT:   $r2, dead $cpsr = tMOVi8 29, 14 /* CC::al */, $noreg
+  ; CHECK-NEXT:   tBL 14 /* CC::al */, $noreg, @__aeabi_assert, csr_aapcs, implicit-def dead $lr, implicit $sp, implicit undef $r0, implicit undef $r1, implicit killed $r2, implicit-def $sp
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT: bb.2.cond.end:
+  ; CHECK-NEXT:   successors: %bb.4(0x40000000), %bb.3(0x40000000)
+  ; CHECK-NEXT:   liveins: $r4
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT:   tCMPi8 killed renamable $r4, 0, 14 /* CC::al */, $noreg, implicit-def $cpsr
+  ; CHECK-NEXT:   t2Bcc %bb.4, 0 /* CC::eq */, killed $cpsr
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT: bb.3.cond.false3:
+  ; CHECK-NEXT:   successors: %bb.4(0x80000000)
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT:   $r2, dead $cpsr = tMOVi8 30, 14 /* CC::al */, $noreg
+  ; CHECK-NEXT:   tBL 14 /* CC::al */, $noreg, @__aeabi_assert, csr_aapcs, implicit-def dead $lr, implicit $sp, implicit undef $r0, implicit undef $r1, implicit killed $r2, implicit-def $sp
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT: bb.4.cond.end4:
+  ; CHECK-NEXT:   $r2, dead $cpsr = tMOVi8 0, 14 /* CC::al */, $noreg
+  ; CHECK-NEXT:   tBL 14 /* CC::al */, $noreg, @__cxa_throw, csr_aapcs, implicit-def dead $lr, implicit $sp, implicit undef $r0, implicit undef $r1, implicit killed $r2, implicit-def $sp
+  bb.0.entry:
+    successors: %bb.2(0x40000000), %bb.1(0x40000000)
+    liveins: $lr, $r0, $r1, $r2, $r3, $r4
+
+    $sp = frame-setup tSUBspi $sp, 2, 14 /* CC::al */, $noreg
+    frame-setup CFI_INSTRUCTION def_cfa_offset 8
+    frame-setup tPUSH 14 /* CC::al */, $noreg, undef $r2, undef $r3, killed $r4, $lr, implicit-def $sp, implicit $sp
+    frame-setup CFI_INSTRUCTION def_cfa_offset 24
+    frame-setup CFI_INSTRUCTION offset $lr, -12
+    frame-setup CFI_INSTRUCTION offset $r4, -16
+    $r4 = tMOVr killed $r1, 14 /* CC::al */, $noreg
+    $r1 = t2MOVi16 target-flags(arm-lo16) @__stack_chk_guard, 14 /* CC::al */, $noreg
+    $r1 = t2MOVTi16 killed $r1, target-flags(arm-hi16) @__stack_chk_guard, 14 /* CC::al */, $noreg
+    tCMPi8 killed renamable $r0, 0, 14 /* CC::al */, $noreg, implicit-def $cpsr
+    $r1 = tLDRi killed $r1, 0, 14 /* CC::al */, $noreg :: (dereferenceable invariant load (s32) from @__stack_chk_guard)
+    t2STRDi8 killed $r2, killed $r3, $sp, 16, 14 /* CC::al */, $noreg :: (store (s32) into %fixed-stack.0, align 8), (store (s32) into %fixed-stack.0 + 4)
+    tSTRspi killed renamable $r1, $sp, 1, 14 /* CC::al */, $noreg :: (volatile store (s32) into %stack.0.StackGuardSlot)
+    renamable $r1 = tADDrSPi $sp, 4, 14 /* CC::al */, $noreg
+    tSTRi killed renamable $r1, undef renamable $r0, 0, 14 /* CC::al */, $noreg :: (store (s32) into `ptr undef`)
+    INLINEASM &"", 1 /* sideeffect attdialect */, 327689 /* reguse:GPR */, killed renamable $lr
+    t2Bcc %bb.2, 0 /* CC::eq */, killed $cpsr
+
+  bb.1.cond.false:
+    successors: %bb.2(0x80000000)
+    liveins: $r4
+
+    $r2, dead $cpsr = tMOVi8 29, 14 /* CC::al */, $noreg
+    tBL 14 /* CC::al */, $noreg, @__aeabi_assert, csr_aapcs, implicit-def dead $lr, implicit $sp, implicit undef $r0, implicit undef $r1, implicit killed $r2, implicit-def $sp
+
+  bb.2.cond.end:
+    successors: %bb.4(0x40000000), %bb.3(0x40000000)
+    liveins: $r4
+
+    tCMPi8 killed renamable $r4, 0, 14 /* CC::al */, $noreg, implicit-def $cpsr
+    t2Bcc %bb.4, 0 /* CC::eq */, killed $cpsr
+
+  bb.3.cond.false3:
+    successors: %bb.4(0x80000000)
+
+    $r2, dead $cpsr = tMOVi8 30, 14 /* CC::al */, $noreg
+    tBL 14 /* CC::al */, $noreg, @__aeabi_assert, csr_aapcs, implicit-def dead $lr, implicit $sp, implicit undef $r0, implicit undef $r1, implicit killed $r2, implicit-def $sp
+
+  bb.4.cond.end4:
+    $r2, dead $cpsr = tMOVi8 0, 14 /* CC::al */, $noreg
+    tBL 14 /* CC::al */, $noreg, @__cxa_throw, csr_aapcs, implicit-def dead $lr, implicit $sp, implicit undef $r0, implicit undef $r1, implicit killed $r2, implicit-def $sp
+
+...

>From 69a5dc1086b358641ee34922014a1042a4c63034 Mon Sep 17 00:00:00 2001
From: Oliver Stannard <oliver.stannard at arm.com>
Date: Thu, 19 Oct 2023 16:35:31 +0100
Subject: [PATCH 3/3] [ARM] Do not emit unwind tables when saving LR around
 outlined call

In some cases, the machine outliner needs to preserve LR across an
outlined call by pushing it onto the stack. Previously, this also
generated unwind table instructions, which is incorrect because EHABI
unwind tables cannot represent different stack frames a different points
in the function, so the extra unwind info applied to the entire
function.

The outliner code already avoided generating CFI instructions, but EHABI
unwind data is generated later from the actual instructions, so we need
to avoid using the FrameSetup and FrameDestroy flags to prevent unwind
data being generated.

Change-Id: I2fa574f0d1b2138818c1ebd8997c7dcf51d274b7
---
 llvm/lib/Target/ARM/ARMBaseInstrInfo.cpp            | 12 +++++++-----
 llvm/test/CodeGen/ARM/machine-outliner-noreturn.mir |  4 ++--
 2 files changed, 9 insertions(+), 7 deletions(-)

diff --git a/llvm/lib/Target/ARM/ARMBaseInstrInfo.cpp b/llvm/lib/Target/ARM/ARMBaseInstrInfo.cpp
index 1ffdde0360cf623..bc94b4cd2079caa 100644
--- a/llvm/lib/Target/ARM/ARMBaseInstrInfo.cpp
+++ b/llvm/lib/Target/ARM/ARMBaseInstrInfo.cpp
@@ -6450,20 +6450,21 @@ void ARMBaseInstrInfo::saveLROnStack(MachineBasicBlock &MBB,
                                      MachineBasicBlock::iterator It, bool CFI,
                                      bool Auth) const {
   int Align = std::max(Subtarget.getStackAlignment().value(), uint64_t(8));
+  unsigned MIFlags = CFI ? MachineInstr::FrameSetup : 0;
   assert(Align >= 8 && Align <= 256);
   if (Auth) {
     assert(Subtarget.isThumb2());
     // Compute PAC in R12. Outlining ensures R12 is dead across the outlined
     // sequence.
     BuildMI(MBB, It, DebugLoc(), get(ARM::t2PAC))
-        .setMIFlags(MachineInstr::FrameSetup);
+        .setMIFlags(MIFlags);
     BuildMI(MBB, It, DebugLoc(), get(ARM::t2STRD_PRE), ARM::SP)
         .addReg(ARM::R12, RegState::Kill)
         .addReg(ARM::LR, RegState::Kill)
         .addReg(ARM::SP)
         .addImm(-Align)
         .add(predOps(ARMCC::AL))
-        .setMIFlags(MachineInstr::FrameSetup);
+        .setMIFlags(MIFlags);
   } else {
     unsigned Opc = Subtarget.isThumb() ? ARM::t2STR_PRE : ARM::STR_PRE_IMM;
     BuildMI(MBB, It, DebugLoc(), get(Opc), ARM::SP)
@@ -6471,7 +6472,7 @@ void ARMBaseInstrInfo::saveLROnStack(MachineBasicBlock &MBB,
         .addReg(ARM::SP)
         .addImm(-Align)
         .add(predOps(ARMCC::AL))
-        .setMIFlags(MachineInstr::FrameSetup);
+        .setMIFlags(MIFlags);
   }
 
   if (!CFI)
@@ -6526,6 +6527,7 @@ void ARMBaseInstrInfo::restoreLRFromStack(MachineBasicBlock &MBB,
                                           MachineBasicBlock::iterator It,
                                           bool CFI, bool Auth) const {
   int Align = Subtarget.getStackAlignment().value();
+  unsigned MIFlags = CFI ? MachineInstr::FrameDestroy : 0;
   if (Auth) {
     assert(Subtarget.isThumb2());
     // Restore return address PAC and LR.
@@ -6536,7 +6538,7 @@ void ARMBaseInstrInfo::restoreLRFromStack(MachineBasicBlock &MBB,
         .addReg(ARM::SP)
         .addImm(Align)
         .add(predOps(ARMCC::AL))
-        .setMIFlags(MachineInstr::FrameDestroy);
+        .setMIFlags(MIFlags);
     // LR authentication is after the CFI instructions, below.
   } else {
     unsigned Opc = Subtarget.isThumb() ? ARM::t2LDR_POST : ARM::LDR_POST_IMM;
@@ -6547,7 +6549,7 @@ void ARMBaseInstrInfo::restoreLRFromStack(MachineBasicBlock &MBB,
       MIB.addReg(0);
     MIB.addImm(Subtarget.getStackAlignment().value())
         .add(predOps(ARMCC::AL))
-        .setMIFlags(MachineInstr::FrameDestroy);
+        .setMIFlags(MIFlags);
   }
 
   if (CFI) {
diff --git a/llvm/test/CodeGen/ARM/machine-outliner-noreturn.mir b/llvm/test/CodeGen/ARM/machine-outliner-noreturn.mir
index 852ef9d139d621c..922b3e77b42a785 100644
--- a/llvm/test/CodeGen/ARM/machine-outliner-noreturn.mir
+++ b/llvm/test/CodeGen/ARM/machine-outliner-noreturn.mir
@@ -561,9 +561,9 @@ body:             |
   ; CHECK-NEXT:   frame-setup CFI_INSTRUCTION offset $lr, -12
   ; CHECK-NEXT:   frame-setup CFI_INSTRUCTION offset $r4, -16
   ; CHECK-NEXT:   $r4 = tMOVr killed $r1, 14 /* CC::al */, $noreg
-  ; CHECK-NEXT:   early-clobber $sp = frame-setup t2STR_PRE killed $lr, $sp, -8, 14 /* CC::al */, $noreg
+  ; CHECK-NEXT:   early-clobber $sp = t2STR_PRE killed $lr, $sp, -8, 14 /* CC::al */, $noreg
   ; CHECK-NEXT:   tBL 14 /* CC::al */, $noreg, @OUTLINED_FUNCTION_1, implicit-def $lr, implicit $sp, implicit-def $lr, implicit-def $sp, implicit-def $r1, implicit $noreg, implicit $sp
-  ; CHECK-NEXT:   $lr, $sp = frame-destroy t2LDR_POST $sp, 8, 14 /* CC::al */, $noreg
+  ; CHECK-NEXT:   $lr, $sp = t2LDR_POST $sp, 8, 14 /* CC::al */, $noreg
   ; CHECK-NEXT:   tCMPi8 killed renamable $r0, 0, 14 /* CC::al */, $noreg, implicit-def $cpsr
   ; CHECK-NEXT:   $r1 = tLDRi killed $r1, 0, 14 /* CC::al */, $noreg :: (dereferenceable invariant load (s32) from @__stack_chk_guard)
   ; CHECK-NEXT:   t2STRDi8 killed $r2, killed $r3, $sp, 16, 14 /* CC::al */, $noreg :: (store (s32) into %fixed-stack.0, align 8), (store (s32) into %fixed-stack.0 + 4)



More information about the llvm-commits mailing list