https://github.com/trungnt2910 updated 
https://github.com/llvm/llvm-project/pull/184953

>From 8b861e1a97d83f6c897e6b0b32444f52456faae0 Mon Sep 17 00:00:00 2001
From: Trung Nguyen <[email protected]>
Date: Sun, 15 Mar 2026 02:01:56 +1100
Subject: [PATCH 1/2] [ARM] Add support for Windows SEH

This commit implements Windows Structured Exception Handling (SEH)
support for ARM `clang` in MSVC mode.

This includes enabling the relevant language constructs in the
Clang frontend and adding new ARM-specific code lowering logic.
---
 clang/include/clang/Basic/TargetInfo.h       |  3 +-
 clang/lib/CodeGen/CGException.cpp            | 72 +++++++++++++++-
 llvm/include/llvm/IR/Intrinsics.td           |  2 +-
 llvm/lib/CodeGen/AsmPrinter/WinException.cpp |  7 +-
 llvm/lib/Target/ARM/ARMAsmPrinter.cpp        |  9 ++
 llvm/lib/Target/ARM/ARMBaseRegisterInfo.cpp  | 17 ++++
 llvm/lib/Target/ARM/ARMBaseRegisterInfo.h    |  1 +
 llvm/lib/Target/ARM/ARMExpandPseudoInsts.cpp | 87 ++++++++++++++++++++
 llvm/lib/Target/ARM/ARMFrameLowering.cpp     | 29 +++++++
 llvm/lib/Target/ARM/ARMFrameLowering.h       |  2 +
 llvm/lib/Target/ARM/ARMISelLowering.cpp      | 29 +++++++
 llvm/lib/Target/ARM/ARMInstrInfo.td          | 23 ++++++
 llvm/lib/Target/ARM/ARMInstrThumb2.td        |  3 +
 llvm/lib/Target/ARM/ARMMCInstLower.cpp       |  3 +
 llvm/lib/Target/ARM/ARMMachineFunctionInfo.h |  7 ++
 15 files changed, 285 insertions(+), 9 deletions(-)

diff --git a/clang/include/clang/Basic/TargetInfo.h 
b/clang/include/clang/Basic/TargetInfo.h
index f3a85737f6571..1405164ac9a33 100644
--- a/clang/include/clang/Basic/TargetInfo.h
+++ b/clang/include/clang/Basic/TargetInfo.h
@@ -1668,7 +1668,8 @@ class TargetInfo : public TransferrableTargetInfo,
   bool isSEHTrySupported() const {
     return getTriple().isOSWindows() &&
            (getTriple().isX86() ||
-            getTriple().getArch() == llvm::Triple::aarch64);
+            getTriple().getArch() == llvm::Triple::aarch64 ||
+            getTriple().isThumb());
   }
 
   /// Return true if {|} are normal characters in the asm string.
diff --git a/clang/lib/CodeGen/CGException.cpp 
b/clang/lib/CodeGen/CGException.cpp
index 1437bc2d32159..93e49503fd64d 100644
--- a/clang/lib/CodeGen/CGException.cpp
+++ b/clang/lib/CodeGen/CGException.cpp
@@ -22,6 +22,7 @@
 #include "clang/AST/StmtObjC.h"
 #include "clang/AST/StmtVisitor.h"
 #include "clang/Basic/DiagnosticSema.h"
+#include "llvm/IR/InlineAsm.h"
 #include "llvm/IR/IntrinsicInst.h"
 #include "llvm/IR/Intrinsics.h"
 #include "llvm/IR/IntrinsicsWebAssembly.h"
@@ -1764,6 +1765,43 @@ struct PerformSEHFinally final : EHScopeStack::Cleanup {
     Args.add(RValue::get(IsForEH), ArgTys[0]);
     Args.add(RValue::get(FP), ArgTys[1]);
 
+    // For 32-bit ARM, we rely on the extra parameter passed in r3 to
+    // correctly restore the frame pointer.
+    // When this funclet is called by the OS runtime, R1 contains the
+    // stack pointer at the parent's own call site, while R11 has been
+    // clobbered by the funclet's prologue.
+    if (CGM.getTriple().isThumb()) {
+      if (F.isForEHCleanup()) {
+        // If this is a EH funclet, ensure that R2 and R3 are forwarded
+        // properly to the handler function.
+        for (auto Reg : {"r2", "r3"}) {
+          llvm::FunctionType *AsmFTy =
+              llvm::FunctionType::get(CGF.Int32Ty, false);
+          llvm::InlineAsm *IA =
+              llvm::InlineAsm::get(AsmFTy, "", std::string("={") + Reg + "}",
+                                   /*HasSideEffects=*/false);
+          llvm::Value *RegVal = CGF.Builder.CreateCall(IA);
+
+          Args.add(
+              RValue::get(CGF.Builder.CreateIntToPtr(RegVal, CGF.VoidPtrTy)),
+              Context.VoidPtrTy);
+        }
+      } else if (CGF.IsOutlinedSEHHelper) {
+        // If the parent itself is a finally block, pass the arguments down.
+        for (auto Index : {2, 3}) {
+          Args.add(RValue::get(&CGF.CurFn->arg_begin()[Index]),
+                   Context.VoidPtrTy);
+        }
+      } else {
+        // We are calling our own finally block.
+        // Set the context to null, and eh_recoverfp will fall back to reading
+        // r1.
+        llvm::Value *Null = llvm::Constant::getNullValue(CGM.VoidPtrTy);
+        Args.add(RValue::get(Null), Context.VoidPtrTy);
+        Args.add(RValue::get(Null), Context.VoidPtrTy);
+      }
+    }
+
     // Arrange a two-arg function info and type.
     const CGFunctionInfo &FnInfo =
         CGM.getTypes().arrangeBuiltinFunctionCall(Context.VoidTy, Args);
@@ -1924,13 +1962,27 @@ void 
CodeGenFunction::EmitCapturedLocals(CodeGenFunction &ParentCGF,
   }
 
   llvm::Value *ParentFP = EntryFP;
-  if (IsFilter) {
+  if (IsFilter || CGM.getTriple().isThumb()) {
     // Given whatever FP the runtime provided us in EntryFP, recover the true
     // frame pointer of the parent function. We only need to do this in 
filters,
     // since finally funclets recover the parent FP for us.
     llvm::Function *RecoverFPIntrin =
         CGM.getIntrinsic(llvm::Intrinsic::eh_recoverfp);
-    ParentFP = Builder.CreateCall(RecoverFPIntrin, {ParentCGF.CurFn, EntryFP});
+    if (!CGM.getTriple().isThumb()) {
+      ParentFP = Builder.CreateCall(
+          RecoverFPIntrin, {ParentCGF.CurFn, EntryFP,
+                            llvm::Constant::getNullValue(CGM.VoidPtrTy)});
+    } else {
+      // On 32-bit ARM, the EntryFP is always the parent's original SP.
+      // We use that along with the full context passed in argument 3 to
+      // restore the frame pointer.
+      // This also applies to finally blocks. For these blocks, the ARM
+      // version of eh_recoverfp will use EntryFP if the blocks were called
+      // directly from its parent, or EntryContext if called by the OS runtime.
+      llvm::Value *EntryContext = &CurFn->arg_begin()[3];
+      ParentFP = Builder.CreateCall(RecoverFPIntrin,
+                                    {ParentCGF.CurFn, EntryFP, EntryContext});
+    }
 
     // if the parent is a _finally, the passed-in ParentFP is the FP
     // of parent _finally, not Establisher's FP (FP of outermost function).
@@ -1946,8 +1998,8 @@ void CodeGenFunction::EmitCapturedLocals(CodeGenFunction 
&ParentCGF,
       for (auto &I : ParentCGF.LocalDeclMap) {
         const VarDecl *D = cast<VarDecl>(I.first);
         if (isa<ImplicitParamDecl>(D) &&
-            D->getType() == getContext().VoidPtrTy) {
-          assert(D->getName().starts_with("frame_pointer"));
+            D->getType() == getContext().VoidPtrTy &&
+            D->getName().starts_with("frame_pointer")) {
           FramePtrAddrAlloca =
               cast<llvm::AllocaInst>(I.second.getBasePointer());
           break;
@@ -2070,6 +2122,18 @@ void 
CodeGenFunction::startOutlinedSEHHelper(CodeGenFunction &ParentCGF,
         getContext(), /*DC=*/nullptr, StartLoc,
         &getContext().Idents.get("frame_pointer"), getContext().VoidPtrTy,
         ImplicitParamKind::Other));
+    if (CGM.getTarget().getTriple().isThumb()) {
+      // ARM (and ARM64 as well) Windows provides two extra parameters to SEH
+      // handlers. On ARM, nonvolatile_regs point to a saved copy of R4 - R11.
+      Args.push_back(ImplicitParamDecl::Create(
+          getContext(), /*DC=*/nullptr, StartLoc,
+          &getContext().Idents.get("callback"), getContext().VoidPtrTy,
+          ImplicitParamKind::Other));
+      Args.push_back(ImplicitParamDecl::Create(
+          getContext(), /*DC=*/nullptr, StartLoc,
+          &getContext().Idents.get("nonvolatile_regs"), getContext().VoidPtrTy,
+          ImplicitParamKind::Other));
+    }
   }
 
   QualType RetTy = IsFilter ? getContext().LongTy : getContext().VoidTy;
diff --git a/llvm/include/llvm/IR/Intrinsics.td 
b/llvm/include/llvm/IR/Intrinsics.td
index 4469ff155b854..a146da3417ba3 100644
--- a/llvm/include/llvm/IR/Intrinsics.td
+++ b/llvm/include/llvm/IR/Intrinsics.td
@@ -916,7 +916,7 @@ def int_localrecover : DefaultAttrsIntrinsic<[llvm_ptr_ty],
 // Given the frame pointer passed into an SEH filter function, returns a
 // pointer to the local variable area suitable for use with llvm.localrecover.
 def int_eh_recoverfp : DefaultAttrsIntrinsic<[llvm_ptr_ty],
-                                 [llvm_ptr_ty, llvm_ptr_ty],
+                                 [llvm_ptr_ty, llvm_ptr_ty, llvm_ptr_ty],
                                  [IntrNoMem]>;
 
 // To mark the beginning/end of a try-scope for Windows SEH -EHa
diff --git a/llvm/lib/CodeGen/AsmPrinter/WinException.cpp 
b/llvm/lib/CodeGen/AsmPrinter/WinException.cpp
index 90d8196ffb82a..ca579c22f66be 100644
--- a/llvm/lib/CodeGen/AsmPrinter/WinException.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/WinException.cpp
@@ -33,9 +33,10 @@
 using namespace llvm;
 
 WinException::WinException(AsmPrinter *A) : EHStreamer(A) {
-  // MSVC's EH tables are always composed of 32-bit words.  All known 64-bit
-  // platforms use an imagerel32 relocation to refer to symbols.
-  useImageRel32 = (A->getDataLayout().getPointerSizeInBits() == 64);
+  // MSVC's EH tables are always composed of 32-bit words. All known
+  // architectures use an imagerel32 relocation to refer to symbols, except
+  // 32-bit x86.
+  useImageRel32 = A->TM.getTargetTriple().getArch() != Triple::x86;
   isAArch64 = Asm->TM.getTargetTriple().isAArch64();
   isThumb = Asm->TM.getTargetTriple().isThumb();
 }
diff --git a/llvm/lib/Target/ARM/ARMAsmPrinter.cpp 
b/llvm/lib/Target/ARM/ARMAsmPrinter.cpp
index fb2bbb5680156..8e1b792ee6540 100644
--- a/llvm/lib/Target/ARM/ARMAsmPrinter.cpp
+++ b/llvm/lib/Target/ARM/ARMAsmPrinter.cpp
@@ -200,6 +200,15 @@ bool ARMAsmPrinter::runOnMachineFunction(MachineFunction 
&MF) {
   // Emit the XRay table for this function.
   emitXRayTable();
 
+  // Emit the offset of the frame pointer for SEH handlers.
+  if (TM.getTargetTriple().isOSWindows()) {
+    MCSymbol *ParentFrameOffsetSym = OutContext.getOrCreateSymbol(
+        Twine("$ssehpfo$") + CurrentFnSym->getName());
+    OutStreamer->emitAssignment(
+        ParentFrameOffsetSym,
+        MCConstantExpr::create(AFI->getSEHParentFrameOffset(), OutContext));
+  }
+
   // If we need V4T thumb mode Register Indirect Jump pads, emit them.
   // These are created per function, rather than per TU, since it's
   // relatively easy to exceed the thumb branch range within a TU.
diff --git a/llvm/lib/Target/ARM/ARMBaseRegisterInfo.cpp 
b/llvm/lib/Target/ARM/ARMBaseRegisterInfo.cpp
index 80921ce4fb4dd..1bc1452022096 100644
--- a/llvm/lib/Target/ARM/ARMBaseRegisterInfo.cpp
+++ b/llvm/lib/Target/ARM/ARMBaseRegisterInfo.cpp
@@ -520,6 +520,16 @@ ARMBaseRegisterInfo::getFrameRegister(const 
MachineFunction &MF) const {
   return ARM::SP;
 }
 
+unsigned
+ARMBaseRegisterInfo::getLocalAddressRegister(const MachineFunction &MF) const {
+  const auto &MFI = MF.getFrameInfo();
+  if (!MF.hasEHFunclets() && !MFI.hasVarSizedObjects())
+    return ARM::SP;
+  else if (hasStackRealignment(MF))
+    return getBaseRegister();
+  return getFrameRegister(MF);
+}
+
 /// emitLoadConstPool - Emits a load from constpool to materialize the
 /// specified immediate.
 void ARMBaseRegisterInfo::emitLoadConstPool(
@@ -833,6 +843,13 @@ 
ARMBaseRegisterInfo::eliminateFrameIndex(MachineBasicBlock::iterator II,
 
   int Offset = TFI->ResolveFrameIndexReference(MF, FrameIndex, FrameReg, 
SPAdj);
 
+  if (MI.getOpcode() == TargetOpcode::LOCAL_ESCAPE) {
+    MachineOperand &FI = MI.getOperand(FIOperandNum);
+    StackOffset Offset = TFI->getNonLocalFrameIndexReference(MF, FrameIndex);
+    FI.ChangeToImmediate(Offset.getFixed());
+    return false;
+  }
+
   // PEI::scavengeFrameVirtualRegs() cannot accurately track SPAdj because the
   // call frame setup/destroy instructions have already been eliminated.  That
   // means the stack pointer cannot be used to access the emergency spill slot
diff --git a/llvm/lib/Target/ARM/ARMBaseRegisterInfo.h 
b/llvm/lib/Target/ARM/ARMBaseRegisterInfo.h
index 03b0fa0d1ee08..1f0681bf2ee3e 100644
--- a/llvm/lib/Target/ARM/ARMBaseRegisterInfo.h
+++ b/llvm/lib/Target/ARM/ARMBaseRegisterInfo.h
@@ -127,6 +127,7 @@ class ARMBaseRegisterInfo : public ARMGenRegisterInfo {
 
   // Debug information queries.
   Register getFrameRegister(const MachineFunction &MF) const override;
+  unsigned getLocalAddressRegister(const MachineFunction &MF) const;
   Register getBaseRegister() const { return BasePtr; }
 
   /// emitLoadConstPool - Emits a load from constpool to materialize the
diff --git a/llvm/lib/Target/ARM/ARMExpandPseudoInsts.cpp 
b/llvm/lib/Target/ARM/ARMExpandPseudoInsts.cpp
index 276736333fb5d..8564caa94acf0 100644
--- a/llvm/lib/Target/ARM/ARMExpandPseudoInsts.cpp
+++ b/llvm/lib/Target/ARM/ARMExpandPseudoInsts.cpp
@@ -985,6 +985,8 @@ static MachineOperand getMovOperand(const MachineOperand 
&MO,
   }
   case MachineOperand::MO_ExternalSymbol:
     return MachineOperand::CreateES(MO.getSymbolName(), TF);
+  case MachineOperand::MO_MCSymbol:
+    return MachineOperand::CreateMCSymbol(MO.getMCSymbol(), TF);
   case MachineOperand::MO_JumpTableIndex:
     return MachineOperand::CreateJTI(MO.getIndex(), TF);
   default:
@@ -2242,6 +2244,14 @@ bool ARMExpandPseudo::ExpandMI(MachineBasicBlock &MBB,
       return true;
     }
 
+    case ARM::CLEANUPRET:
+    case ARM::CATCHRET: {
+      unsigned RetOpcode = STI->isThumb() ? ARM::tBX_RET : ARM::BX_RET;
+      BuildMI(MBB, MBBI, MI.getDebugLoc(), TII->get(RetOpcode))
+          .add(predOps(ARMCC::AL));
+      MI.eraseFromParent();
+      return true;
+    }
     case ARM::TCRETURNdi:
     case ARM::TCRETURNri:
     case ARM::TCRETURNrinotr12: {
@@ -3282,6 +3292,83 @@ bool ARMExpandPseudo::ExpandMI(MachineBasicBlock &MBB,
       MI.eraseFromParent();
       return true;
     }
+    case ARM::WIN_LOCAL_ADDRESS: {
+      MachineBasicBlock &MBB = *MI.getParent();
+      MachineFunction &MF = *MBB.getParent();
+      MachineBasicBlock::iterator MBBI = MI.getIterator();
+
+      DebugLoc dl = MI.getDebugLoc();
+      Register DstReg = MI.getOperand(0).getReg();
+      const auto *RegInfo = STI->getRegisterInfo();
+      unsigned LocalAddrReg = RegInfo->getLocalAddressRegister(MF);
+
+      // For normal functions, store the register used to offset local 
addresses
+      // into the destination register (usually R1).
+      // For EH funclets, this is a no-op to prevent corrupting parameters
+      // provided by the OS runtime.
+      if (!MBB.isEHFuncletEntry()) {
+        if (AFI->isThumb2Function())
+          emitT2RegPlusImmediate(MBB, MBBI, dl, DstReg, LocalAddrReg, 0,
+                                 ARMCC::AL, Register(), *TII);
+        else
+          emitARMRegPlusImmediate(MBB, MBBI, dl, DstReg, LocalAddrReg, 0,
+                                  ARMCC::AL, Register(), *TII);
+      }
+
+      MI.eraseFromParent();
+      return true;
+    }
+    case ARM::WIN_RECOVER_FP: {
+      MachineBasicBlock &MBB = *MI.getParent();
+      MachineBasicBlock::iterator MBBI = MI.getIterator();
+
+      DebugLoc dl = MI.getDebugLoc();
+      Register DstReg = MI.getOperand(0).getReg();
+      Register FpReg = MI.getOperand(1).getReg();
+      Register CtxReg = MI.getOperand(2).getReg();
+      const MachineOperand &SymbolOp = MI.getOperand(3);
+
+      // This symbol tells where in the context we should look for the address
+      // pointer. Usually it is R6 (offset 0x8) or R11 (offset 0x1C).
+      MachineOperand MOLo = SymbolOp;
+      MOLo.setTargetFlags(ARMII::MO_LO16);
+      BuildMI(MBB, MBBI, dl, TII->get(ARM::t2MOVi16), DstReg)
+          .add(MOLo)
+          .add(predOps(ARMCC::AL));
+
+      MachineOperand MOHi = SymbolOp;
+      MOHi.setTargetFlags(ARMII::MO_HI16);
+      BuildMI(MBB, MBBI, dl, TII->get(ARM::t2MOVTi16), DstReg)
+          .addReg(DstReg)
+          .add(MOHi)
+          .add(predOps(ARMCC::AL));
+
+      // If a null context is passed, we are likely to be called directly from
+      // the parent. In this case, we don't have the full register context, but
+      // we can trust that the first parameter is the actual frame used to
+      // restore variables.
+      BuildMI(MBB, MBBI, dl, TII->get(ARM::t2CMPri))
+          .addReg(CtxReg)
+          .addImm(0)
+          .add(predOps(ARMCC::AL));
+
+      BuildMI(MBB, MBBI, dl, TII->get(ARM::t2IT))
+          .addImm(ARMCC::NE)
+          .addImm(ARMVCC::Else);
+
+      BuildMI(MBB, MBBI, dl, TII->get(ARM::t2LDRs), DstReg)
+          .addReg(CtxReg)
+          .addReg(DstReg)
+          .addImm(0)
+          .add(predOps(ARMCC::NE));
+
+      BuildMI(MBB, MBBI, dl, TII->get(ARM::tMOVr), DstReg)
+          .addReg(FpReg)
+          .add(predOps(ARMCC::EQ));
+
+      MI.eraseFromParent();
+      return true;
+    }
   }
 }
 
diff --git a/llvm/lib/Target/ARM/ARMFrameLowering.cpp 
b/llvm/lib/Target/ARM/ARMFrameLowering.cpp
index 3abace4f6a958..ddbce7a6e1020 100644
--- a/llvm/lib/Target/ARM/ARMFrameLowering.cpp
+++ b/llvm/lib/Target/ARM/ARMFrameLowering.cpp
@@ -624,6 +624,8 @@ static MachineBasicBlock::iterator 
insertSEH(MachineBasicBlock::iterator MBBI,
 
   case ARM::tBX_RET:
   case ARM::t2BXAUT_RET:
+  case ARM::CLEANUPRET:
+  case ARM::CATCHRET:
   case ARM::TCRETURNri:
   case ARM::TCRETURNrinotr12:
     MIB = BuildMI(MF, DL, TII.get(ARM::SEH_Nop_Ret))
@@ -1395,6 +1397,15 @@ void ARMFrameLowering::emitPrologue(MachineFunction &MF,
   // checks for hasVarSizedObjects.
   if (MFI.hasVarSizedObjects())
     AFI->setShouldRestoreSPFromFP(true);
+
+  // Set these values so SEH handlers know how to access parent frame 
variables.
+  if (STI.isTargetWindows()) {
+    unsigned LocalAddrReg = RegInfo->getLocalAddressRegister(MF);
+    if (LocalAddrReg == RegInfo->getBaseRegister())
+      AFI->setSEHParentFrameOffset(0x08); // R6
+    else if (LocalAddrReg == RegInfo->getFrameRegister(MF))
+      AFI->setSEHParentFrameOffset(0x1C); // R11
+  }
 }
 
 void ARMFrameLowering::emitEpilogue(MachineFunction &MF,
@@ -1582,6 +1593,22 @@ StackOffset 
ARMFrameLowering::getFrameIndexReference(const MachineFunction &MF,
   return StackOffset::getFixed(ResolveFrameIndexReference(MF, FI, FrameReg, 
0));
 }
 
+StackOffset
+ARMFrameLowering::getNonLocalFrameIndexReference(const MachineFunction &MF,
+                                                 int FI) const {
+  const MachineFrameInfo &MFI = MF.getFrameInfo();
+  const ARMFunctionInfo *AFI = MF.getInfo<ARMFunctionInfo>();
+  const ARMBaseRegisterInfo *RegInfo =
+      MF.getSubtarget<ARMSubtarget>().getRegisterInfo();
+
+  Register LocalAddrReg = RegInfo->getLocalAddressRegister(MF);
+  int Offset = MFI.getObjectOffset(FI) + MFI.getStackSize();
+  int FPOffset = Offset - AFI->getFramePtrSpillOffset();
+
+  return StackOffset::getFixed(
+      (LocalAddrReg == RegInfo->getFrameRegister(MF)) ? FPOffset : Offset);
+}
+
 int ARMFrameLowering::ResolveFrameIndexReference(const MachineFunction &MF,
                                                  int FI, Register &FrameReg,
                                                  int SPAdj) const {
@@ -2365,6 +2392,8 @@ static unsigned estimateRSStackSizeLimit(MachineFunction 
&MF,
     for (auto &MI : MBB) {
       if (MI.isDebugInstr())
         continue;
+      if (MI.getOpcode() == TargetOpcode::LOCAL_ESCAPE)
+        continue;
       for (unsigned i = 0, e = MI.getNumOperands(); i != e; ++i) {
         if (!MI.getOperand(i).isFI())
           continue;
diff --git a/llvm/lib/Target/ARM/ARMFrameLowering.h 
b/llvm/lib/Target/ARM/ARMFrameLowering.h
index 9dc88d4671c38..7021af0d9fbdd 100644
--- a/llvm/lib/Target/ARM/ARMFrameLowering.h
+++ b/llvm/lib/Target/ARM/ARMFrameLowering.h
@@ -51,6 +51,8 @@ class ARMFrameLowering : public TargetFrameLowering {
   bool canSimplifyCallFramePseudos(const MachineFunction &MF) const override;
   StackOffset getFrameIndexReference(const MachineFunction &MF, int FI,
                                      Register &FrameReg) const override;
+  StackOffset getNonLocalFrameIndexReference(const MachineFunction &MF,
+                                             int FI) const override;
   int ResolveFrameIndexReference(const MachineFunction &MF, int FI,
                                  Register &FrameReg, int SPAdj) const;
 
diff --git a/llvm/lib/Target/ARM/ARMISelLowering.cpp 
b/llvm/lib/Target/ARM/ARMISelLowering.cpp
index 228ed68e386b4..7e056ad5bd503 100644
--- a/llvm/lib/Target/ARM/ARMISelLowering.cpp
+++ b/llvm/lib/Target/ARM/ARMISelLowering.cpp
@@ -85,6 +85,7 @@
 #include "llvm/IR/Type.h"
 #include "llvm/IR/User.h"
 #include "llvm/IR/Value.h"
+#include "llvm/MC/MCAsmInfo.h"
 #include "llvm/MC/MCInstrDesc.h"
 #include "llvm/MC/MCInstrItineraries.h"
 #include "llvm/MC/MCSchedule.h"
@@ -3837,6 +3838,32 @@ ARMTargetLowering::LowerINTRINSIC_WO_CHAIN(SDValue Op, 
SelectionDAG &DAG,
   SDLoc dl(Op);
   switch (IntNo) {
   default: return SDValue();    // Don't custom lower most intrinsics.
+  case Intrinsic::localaddress: {
+    return SDValue(
+        DAG.getMachineNode(ARM::WIN_LOCAL_ADDRESS, dl, Op.getValueType()), 0);
+  }
+  case Intrinsic::eh_recoverfp: {
+    SDValue FnOp = Op.getOperand(1);
+    GlobalAddressSDNode *GSD = dyn_cast<GlobalAddressSDNode>(FnOp);
+    auto *Fn = dyn_cast_or_null<Function>(GSD ? GSD->getGlobal() : nullptr);
+    if (!Fn)
+      report_fatal_error(
+          "llvm.eh.recoverfp must take a function as the first argument");
+
+    SDValue IncomingFpOp = Op.getOperand(2);
+    SDValue IncomingCtxOp = Op.getOperand(3);
+
+    EVT PtrVT = getPointerTy(DAG.getDataLayout());
+    std::string SymName = ("$ssehpfo$" + Fn->getName()).str();
+    const char *PersistentName =
+        DAG.getMachineFunction().createExternalSymbolName(SymName);
+
+    SDValue Offset = DAG.getTargetExternalSymbol(PersistentName, PtrVT);
+
+    return SDValue(DAG.getMachineNode(ARM::WIN_RECOVER_FP, dl, PtrVT,
+                                      IncomingFpOp, IncomingCtxOp, Offset),
+                   0);
+  }
   case Intrinsic::thread_pointer: {
     EVT PtrVT = getPointerTy(DAG.getDataLayout());
     return DAG.getNode(ARMISD::THREAD_POINTER, dl, PtrVT);
@@ -5870,6 +5897,8 @@ Register ARMTargetLowering::getRegisterByName(const char* 
RegName, LLT VT,
                                               const MachineFunction &MF) const 
{
   return StringSwitch<Register>(RegName)
       .Case("sp", ARM::SP)
+      .Case("r2", ARM::R2)
+      .Case("r3", ARM::R3)
       .Default(Register());
 }
 
diff --git a/llvm/lib/Target/ARM/ARMInstrInfo.td 
b/llvm/lib/Target/ARM/ARMInstrInfo.td
index b72aa4b28736e..2ede4275e00fb 100644
--- a/llvm/lib/Target/ARM/ARMInstrInfo.td
+++ b/llvm/lib/Target/ARM/ARMInstrInfo.td
@@ -6220,6 +6220,17 @@ def : ARMPat<(ARMWrapperJT tjumptable:$dst),
 
 // TODO: add,sub,and, 3-instr forms?
 
+
+// Exception Handling
+def ARMLocalRecover : SDNode<"ISD::LOCAL_RECOVER",
+                             SDTypeProfile<1, 1, [SDTCisSameAs<0, 1>,
+                                                  SDTCisInt<1>]>>;
+def : ARMPat<(ARMLocalRecover mcsym:$sym), (MOVi32imm mcsym:$sym)>,
+            Requires<[IsARM, UseMovt]>;
+def : ARMPat<(ARMLocalRecover mcsym:$sym), (LDRLIT_ga_abs mcsym:$sym)>,
+            Requires<[IsARM, DontUseMovt]>;
+
+
 // Tail calls. These patterns also apply to Thumb mode.
 // Regular indirect tail call
 def : Pat<(ARMtcret tcGPR:$dst, (i32 timm:$SPDiff)),
@@ -6725,3 +6736,15 @@ let isPseudo = 1 in {
   let isTerminator = 1 in
   def SEH_EpilogEnd : PseudoInst<(outs), (ins), NoItinerary, []>, Sched<[]>;
 }
+
+// C++ Exception / SEH Pseudo Instructions
+let isTerminator = 1, isReturn = 1, isBarrier = 1, isCodeGenOnly = 1, 
hasNoSchedulingInfo = 1 in {
+  def CLEANUPRET : PseudoInst<(outs), (ins), NoItinerary, [(cleanupret bb)]>, 
Sched<[]>;
+  def CATCHRET : PseudoInst<(outs), (ins arm_br_target:$dst, 
arm_br_target:$src), NoItinerary, [(catchret bb:$dst, bb:$src)]>, Sched<[]>;
+}
+
+// Pseudo Instruction for setting local address pointer on Windows.
+let isPseudo = 1, isBarrier = 1, isCodeGenOnly = 1, hasNoSchedulingInfo = 1 in 
{
+  def WIN_LOCAL_ADDRESS : PseudoInst<(outs GPR:$dst), (ins), NoItinerary, []>;
+  def WIN_RECOVER_FP : PseudoInst<(outs GPR:$dst), (ins GPR:$fp, GPR:$ctx, 
i32imm:$parent), NoItinerary, []>;
+}
diff --git a/llvm/lib/Target/ARM/ARMInstrThumb2.td 
b/llvm/lib/Target/ARM/ARMInstrThumb2.td
index b17c76baec4ef..795b155a68a20 100644
--- a/llvm/lib/Target/ARM/ARMInstrThumb2.td
+++ b/llvm/lib/Target/ARM/ARMInstrThumb2.td
@@ -4393,6 +4393,9 @@ def t2LDRLIT_ga_pcrel : PseudoInst<(outs rGPR:$dst), (ins 
i32imm:$addr),
                        Requires<[IsThumb, HasV8MBaseline, DontUseMovtInPic]>;
 }
 
+// Exception Handling
+def : T2Pat<(ARMLocalRecover mcsym:$sym), (t2MOVi32imm mcsym:$sym)>;
+
 // TLS globals
 def : Pat<(ARMWrapperPIC tglobaltlsaddr:$addr),
           (t2LDRLIT_ga_pcrel tglobaltlsaddr:$addr)>,
diff --git a/llvm/lib/Target/ARM/ARMMCInstLower.cpp 
b/llvm/lib/Target/ARM/ARMMCInstLower.cpp
index c040904a82b71..79ee0f22f3c1e 100644
--- a/llvm/lib/Target/ARM/ARMMCInstLower.cpp
+++ b/llvm/lib/Target/ARM/ARMMCInstLower.cpp
@@ -100,6 +100,9 @@ bool ARMAsmPrinter::lowerOperand(const MachineOperand &MO,
     MCOp = MCOperand::createExpr(MCSymbolRefExpr::create(
         MO.getMBB()->getSymbol(), OutContext));
     break;
+  case MachineOperand::MO_MCSymbol:
+    MCOp = GetSymbolRef(MO, MO.getMCSymbol());
+    break;
   case MachineOperand::MO_GlobalAddress:
     MCOp = GetSymbolRef(MO,
                         GetARMGVSymbol(MO.getGlobal(), MO.getTargetFlags()));
diff --git a/llvm/lib/Target/ARM/ARMMachineFunctionInfo.h 
b/llvm/lib/Target/ARM/ARMMachineFunctionInfo.h
index b6897608a952c..8644a48e1c14d 100644
--- a/llvm/lib/Target/ARM/ARMMachineFunctionInfo.h
+++ b/llvm/lib/Target/ARM/ARMMachineFunctionInfo.h
@@ -155,6 +155,10 @@ class ARMFunctionInfo : public MachineFunctionInfo {
   /// destinations.
   bool BranchTargetEnforcement = false;
 
+  /// SEHParentFrameOffset - Offset of the register used for SEH frame recovery
+  /// within the saved nonvolatile registers area. -1 if this area is not used.
+  int SEHParentFrameOffset = -1;
+
 public:
   ARMFunctionInfo() = default;
 
@@ -286,6 +290,9 @@ class ARMFunctionInfo : public MachineFunctionInfo {
   void setPreservesR0() { PreservesR0 = true; }
   bool getPreservesR0() const { return PreservesR0; }
 
+  int getSEHParentFrameOffset() const { return SEHParentFrameOffset; }
+  void setSEHParentFrameOffset(int Offset) { SEHParentFrameOffset = Offset; }
+
   bool shouldSignReturnAddress() const {
     return shouldSignReturnAddress(LRSpilled);
   }

>From c1df7784ee59ba585de21856c8927a93bebdfd52 Mon Sep 17 00:00:00 2001
From: Trung Nguyen <[email protected]>
Date: Sun, 15 Mar 2026 02:04:53 +1100
Subject: [PATCH 2/2] [ARM] Port SEH codegen tests to thumbv7-windows

---
 clang/test/CodeGen/exceptions-seh-finally.c | 111 ++++----
 clang/test/CodeGen/exceptions-seh.c         |  84 +++---
 llvm/test/CodeGen/ARM/seh-finally.ll        | 301 ++++++++++++++++++++
 3 files changed, 405 insertions(+), 91 deletions(-)
 create mode 100644 llvm/test/CodeGen/ARM/seh-finally.ll

diff --git a/clang/test/CodeGen/exceptions-seh-finally.c 
b/clang/test/CodeGen/exceptions-seh-finally.c
index 0cdf230311250..480e2e9c2696b 100644
--- a/clang/test/CodeGen/exceptions-seh-finally.c
+++ b/clang/test/CodeGen/exceptions-seh-finally.c
@@ -1,6 +1,7 @@
 // RUN: %clang_cc1 %s -triple x86_64-pc-win32 -fms-extensions -emit-llvm -O1 
-disable-llvm-passes -o - | FileCheck %s
 // RUN: %clang_cc1 %s -triple i686-pc-win32 -fms-extensions -emit-llvm -O1 
-disable-llvm-passes -o - | FileCheck %s
 // RUN: %clang_cc1 %s -triple aarch64-windows -fms-extensions -emit-llvm -O1 
-disable-llvm-passes -o - | FileCheck %s
+// RUN: %clang_cc1 %s -triple thumbv7-windows -fms-extensions -emit-llvm -O1 
-disable-llvm-passes -o - | FileCheck %s
 // NOTE: we're passing "-O1 -disable-llvm-passes" to avoid adding optnone and 
noinline everywhere.
 
 void abort(void) __attribute__((noreturn));
@@ -15,24 +16,24 @@ void basic_finally(void) {
   }
 }
 
-// CHECK-LABEL: define dso_local void @basic_finally()
-// CHECK: invoke void @might_crash()
+// CHECK-LABEL: define dso_local {{.*}}void @basic_finally()
+// CHECK: invoke {{.*}}void @might_crash()
 // CHECK:     to label %[[invoke_cont:[^ ]*]] unwind label %[[lpad:[^ ]*]]
 //
 // CHECK: [[invoke_cont]]
 // CHECK: %[[fp:[^ ]*]] = call ptr @llvm.localaddress()
-// CHECK: call void @"?fin$0@0@basic_finally@@"({{i8 noundef( zeroext)?}} 0, 
ptr noundef %[[fp]])
+// CHECK: call {{.*}}void @"?fin$0@0@basic_finally@@"({{i8 noundef( 
zeroext)?}} 0, ptr noundef %[[fp]]{{.*}})
 // CHECK-NEXT: ret void
 //
 // CHECK: [[lpad]]
 // CHECK-NEXT: %[[pad:[^ ]*]] = cleanuppad
 // CHECK: %[[fp:[^ ]*]] = call ptr @llvm.localaddress()
-// CHECK: call void @"?fin$0@0@basic_finally@@"({{i8 noundef( zeroext)?}} 1, 
ptr noundef %[[fp]])
+// CHECK: call {{.*}}void @"?fin$0@0@basic_finally@@"({{i8 noundef( 
zeroext)?}} 1, ptr noundef %[[fp]]{{.*}})
 // CHECK-NEXT: cleanupret from %[[pad]] unwind to caller
 
-// CHECK: define internal void @"?fin$0@0@basic_finally@@"({{.*}})
+// CHECK: define internal {{.*}}void @"?fin$0@0@basic_finally@@"({{[^)]*}})
 // CHECK-SAME: [[finally_attrs:#[0-9]+]]
-// CHECK: call void @cleanup()
+// CHECK: call {{.*}}void @cleanup()
 
 // Mostly check that we don't double emit 'r' which would crash.
 void decl_in_finally(void) {
@@ -55,22 +56,22 @@ void label_in_finally(void) {
   }
 }
 
-// CHECK-LABEL: define dso_local void @label_in_finally()
-// CHECK: invoke void @might_crash()
+// CHECK-LABEL: define dso_local {{.*}}void @label_in_finally()
+// CHECK: invoke {{.*}}void @might_crash()
 // CHECK:     to label %[[invoke_cont:[^ ]*]] unwind label %[[lpad:[^ ]*]]
 //
 // CHECK: [[invoke_cont]]
 // CHECK: %[[fp:[^ ]*]] = call ptr @llvm.localaddress()
-// CHECK: call void @"?fin$0@0@label_in_finally@@"({{i8 noundef( zeroext)?}} 
0, ptr noundef %[[fp]])
+// CHECK: call {{.*}}void @"?fin$0@0@label_in_finally@@"({{i8 noundef( 
zeroext)?}} 0, ptr noundef %[[fp]]{{.*}})
 // CHECK: ret void
 
-// CHECK: define internal void @"?fin$0@0@label_in_finally@@"({{.*}})
+// CHECK: define internal {{.*}}void @"?fin$0@0@label_in_finally@@"({{[^)]*}})
 // CHECK-SAME: [[finally_attrs]]
 // CHECK: br label %[[l:[^ ]*]]
 //
 // CHECK: [[l]]
-// CHECK: call void @cleanup()
-// CHECK: call i32 @check_condition()
+// CHECK: call {{.*}}void @cleanup()
+// CHECK: call {{.*}}i32 @check_condition()
 // CHECK: br i1 {{.*}}, label
 // CHECK: br label %[[l]]
 
@@ -83,22 +84,22 @@ void use_abnormal_termination(void) {
   }
 }
 
-// CHECK-LABEL: define dso_local void @use_abnormal_termination()
-// CHECK: invoke void @might_crash()
+// CHECK-LABEL: define dso_local {{.*}}void @use_abnormal_termination()
+// CHECK: invoke {{.*}}void @might_crash()
 // CHECK:     to label %[[invoke_cont:[^ ]*]] unwind label %[[lpad:[^ ]*]]
 //
 // CHECK: [[invoke_cont]]
 // CHECK: %[[fp:[^ ]*]] = call ptr @llvm.localaddress()
-// CHECK: call void @"?fin$0@0@use_abnormal_termination@@"({{i8 noundef( 
zeroext)?}} 0, ptr noundef %[[fp]])
+// CHECK: call {{.*}}void @"?fin$0@0@use_abnormal_termination@@"({{i8 noundef( 
zeroext)?}} 0, ptr noundef %[[fp]]{{.*}})
 // CHECK: ret void
 //
 // CHECK: [[lpad]]
 // CHECK-NEXT: %[[pad:[^ ]*]] = cleanuppad
 // CHECK: %[[fp:[^ ]*]] = call ptr @llvm.localaddress()
-// CHECK: call void @"?fin$0@0@use_abnormal_termination@@"({{i8 noundef( 
zeroext)?}} 1, ptr noundef %[[fp]])
+// CHECK: call {{.*}}void @"?fin$0@0@use_abnormal_termination@@"({{i8 noundef( 
zeroext)?}} 1, ptr noundef %[[fp]]{{.*}})
 // CHECK-NEXT: cleanupret from %[[pad]] unwind to caller
 
-// CHECK: define internal void @"?fin$0@0@use_abnormal_termination@@"({{i8 
noundef( zeroext)?}} %[[abnormal:abnormal_termination]], ptr noundef 
%frame_pointer)
+// CHECK: define internal {{.*}}void 
@"?fin$0@0@use_abnormal_termination@@"({{i8 noundef( zeroext)?}} 
%[[abnormal:abnormal_termination]], ptr noundef %frame_pointer{{[^)]*}})
 // CHECK-SAME: [[finally_attrs]]
 // CHECK: %[[abnormal_zext:[^ ]*]] = zext i8 %[[abnormal]] to i32
 // CHECK: store i32 %[[abnormal_zext]], ptr @crashed
@@ -112,13 +113,13 @@ void noreturn_noop_finally(void) {
   }
 }
 
-// CHECK-LABEL: define dso_local void @noreturn_noop_finally()
-// CHECK: call void @"?fin$0@0@noreturn_noop_finally@@"({{.*}})
+// CHECK-LABEL: define dso_local {{.*}}void @noreturn_noop_finally()
+// CHECK: call {{.*}}void @"?fin$0@0@noreturn_noop_finally@@"({{.*}})
 // CHECK: ret void
 
-// CHECK: define internal void @"?fin$0@0@noreturn_noop_finally@@"({{.*}})
+// CHECK: define internal {{.*}}void 
@"?fin$0@0@noreturn_noop_finally@@"({{[^)]*}})
 // CHECK-SAME: [[finally_attrs]]
-// CHECK: call void @abort()
+// CHECK: call {{.*}}void @abort()
 // CHECK: unreachable
 
 void noreturn_finally(void) {
@@ -129,22 +130,22 @@ void noreturn_finally(void) {
   }
 }
 
-// CHECK-LABEL: define dso_local void @noreturn_finally()
-// CHECK: invoke void @might_crash()
+// CHECK-LABEL: define dso_local {{.*}}void @noreturn_finally()
+// CHECK: invoke {{.*}}void @might_crash()
 // CHECK:     to label %[[cont:[^ ]*]] unwind label %[[lpad:[^ ]*]]
 //
 // CHECK: [[cont]]
-// CHECK: call void @"?fin$0@0@noreturn_finally@@"({{.*}})
+// CHECK: call {{.*}}void @"?fin$0@0@noreturn_finally@@"({{.*}})
 // CHECK: ret void
 //
 // CHECK: [[lpad]]
 // CHECK-NEXT: %[[pad:[^ ]*]] = cleanuppad
-// CHECK: call void @"?fin$0@0@noreturn_finally@@"({{.*}})
+// CHECK: call {{.*}}void @"?fin$0@0@noreturn_finally@@"({{.*}})
 // CHECK-NEXT: cleanupret from %[[pad]] unwind to caller
 
-// CHECK: define internal void @"?fin$0@0@noreturn_finally@@"({{.*}})
+// CHECK: define internal {{.*}}void @"?fin$0@0@noreturn_finally@@"({{[^)]*}})
 // CHECK-SAME: [[finally_attrs]]
-// CHECK: call void @abort()
+// CHECK: call {{.*}}void @abort()
 // CHECK: unreachable
 
 int finally_with_return(void) {
@@ -153,14 +154,14 @@ int finally_with_return(void) {
   } __finally {
   }
 }
-// CHECK-LABEL: define dso_local i32 @finally_with_return()
+// CHECK-LABEL: define dso_local {{.*}}i32 @finally_with_return()
 // CHECK: store i32 1, ptr %cleanup.dest.slot
 // CHECK: %cleanup.dest = load i32, ptr %cleanup.dest.slot
 // CHECK: icmp ne i32 %cleanup.dest
-// CHECK: call void @"?fin$0@0@finally_with_return@@"({{.*}})
+// CHECK: call {{.*}}void @"?fin$0@0@finally_with_return@@"({{.*}})
 // CHECK: ret i32 42
 
-// CHECK: define internal void @"?fin$0@0@finally_with_return@@"({{.*}})
+// CHECK: define internal {{.*}}void 
@"?fin$0@0@finally_with_return@@"({{[^)]*}})
 // CHECK-SAME: [[finally_attrs]]
 // CHECK-NOT: br i1
 // CHECK-NOT: br label
@@ -178,24 +179,24 @@ int nested___finally___finally(void) {
   return 0;
 }
 
-// CHECK-LABEL: define dso_local i32 @nested___finally___finally
-// CHECK: invoke void @"?fin$1@0@nested___finally___finally@@"({{.*}})
+// CHECK-LABEL: define dso_local {{.*}}i32 @nested___finally___finally
+// CHECK: invoke {{.*}}void @"?fin$1@0@nested___finally___finally@@"({{.*}})
 // CHECK:          to label %[[outercont:[^ ]*]] unwind label %[[lpad:[^ ]*]]
 //
 // CHECK: [[outercont]]
-// CHECK: call void @"?fin$0@0@nested___finally___finally@@"({{.*}})
+// CHECK: call {{.*}}void @"?fin$0@0@nested___finally___finally@@"({{.*}})
 // CHECK-NEXT: ret i32 0
 //
 // CHECK: [[lpad]]
 // CHECK-NEXT: %[[pad:[^ ]*]] = cleanuppad
-// CHECK: call void @"?fin$0@0@nested___finally___finally@@"({{.*}})
+// CHECK: call {{.*}}void @"?fin$0@0@nested___finally___finally@@"({{.*}})
 // CHECK-NEXT: cleanupret from %[[pad]] unwind to caller
 
-// CHECK-LABEL: define internal void 
@"?fin$0@0@nested___finally___finally@@"({{.*}})
+// CHECK-LABEL: define internal {{.*}}void 
@"?fin$0@0@nested___finally___finally@@"({{[^)]*}})
 // CHECK-SAME: [[finally_attrs]]
 // CHECK: ret void
 
-// CHECK-LABEL: define internal void 
@"?fin$1@0@nested___finally___finally@@"({{.*}})
+// CHECK-LABEL: define internal {{.*}}void 
@"?fin$1@0@nested___finally___finally@@"({{[^)]*}})
 // CHECK-SAME: [[finally_attrs]]
 // CHECK: unreachable
 
@@ -213,21 +214,21 @@ int nested___finally___finally_with_eh_edge(void) {
   }
   return 912;
 }
-// CHECK-LABEL: define dso_local i32 @nested___finally___finally_with_eh_edge
-// CHECK: invoke void @might_crash()
+// CHECK-LABEL: define dso_local {{.*}}i32 
@nested___finally___finally_with_eh_edge
+// CHECK: invoke {{.*}}void @might_crash()
 // CHECK-NEXT: to label %[[invokecont:[^ ]*]] unwind label %[[lpad1:[^ ]*]]
 //
 // [[invokecont]]
-// CHECK: invoke void 
@"?fin$1@0@nested___finally___finally_with_eh_edge@@"({{.*}})
+// CHECK: invoke {{.*}}void 
@"?fin$1@0@nested___finally___finally_with_eh_edge@@"({{.*}})
 // CHECK-NEXT:       to label %[[outercont:[^ ]*]] unwind label %[[lpad2:[^ 
]*]]
 //
 // CHECK: [[outercont]]
-// CHECK: call void 
@"?fin$0@0@nested___finally___finally_with_eh_edge@@"({{.*}})
+// CHECK: call {{.*}}void 
@"?fin$0@0@nested___finally___finally_with_eh_edge@@"({{.*}})
 // CHECK-NEXT: ret i32 912
 //
 // CHECK: [[lpad1]]
 // CHECK-NEXT: %[[innerpad:[^ ]*]] = cleanuppad
-// CHECK: invoke void 
@"?fin$1@0@nested___finally___finally_with_eh_edge@@"({{.*}})
+// CHECK: invoke {{.*}}void 
@"?fin$1@0@nested___finally___finally_with_eh_edge@@"({{.*}})
 // CHECK-NEXT:    label %[[innercleanupretbb:[^ ]*]] unwind label %[[lpad2:[^ 
]*]]
 //
 // CHECK: [[innercleanupretbb]]
@@ -235,14 +236,14 @@ int nested___finally___finally_with_eh_edge(void) {
 //
 // CHECK: [[lpad2]]
 // CHECK-NEXT: %[[outerpad:[^ ]*]] = cleanuppad
-// CHECK: call void 
@"?fin$0@0@nested___finally___finally_with_eh_edge@@"({{.*}})
+// CHECK: call {{.*}}void 
@"?fin$0@0@nested___finally___finally_with_eh_edge@@"({{.*}})
 // CHECK-NEXT: cleanupret from %[[outerpad]] unwind to caller
 
-// CHECK-LABEL: define internal void 
@"?fin$0@0@nested___finally___finally_with_eh_edge@@"({{.*}})
+// CHECK-LABEL: define internal {{.*}}void 
@"?fin$0@0@nested___finally___finally_with_eh_edge@@"({{[^)]*}})
 // CHECK-SAME: [[finally_attrs]]
 // CHECK: ret void
 
-// CHECK-LABEL: define internal void 
@"?fin$1@0@nested___finally___finally_with_eh_edge@@"({{.*}})
+// CHECK-LABEL: define internal {{.*}}void 
@"?fin$1@0@nested___finally___finally_with_eh_edge@@"({{[^)]*}})
 // CHECK-SAME: [[finally_attrs]]
 // CHECK: unreachable
 
@@ -257,20 +258,20 @@ void finally_within_finally(void) {
   }
 }
 
-// CHECK-LABEL: define dso_local void @finally_within_finally(
-// CHECK: invoke void @might_crash(
+// CHECK-LABEL: define dso_local {{.*}}void @finally_within_finally(
+// CHECK: invoke {{.*}}void @might_crash(
 
-// CHECK: call void @"?fin$0@0@finally_within_finally@@"(
-// CHECK: call void @"?fin$0@0@finally_within_finally@@"({{.*}}) [ "funclet"(
+// CHECK: call {{.*}}void @"?fin$0@0@finally_within_finally@@"(
+// CHECK: call {{.*}}void @"?fin$0@0@finally_within_finally@@"({{.*}}) [ 
"funclet"(
 
-// CHECK-LABEL: define internal void 
@"?fin$0@0@finally_within_finally@@"({{[^)]*}})
+// CHECK-LABEL: define internal {{.*}}void 
@"?fin$0@0@finally_within_finally@@"({{[^)]*}})
 // CHECK-SAME: [[finally_attrs]]
-// CHECK: invoke void @might_crash(
+// CHECK: invoke {{.*}}void @might_crash(
 
-// CHECK: call void @"?fin$1@0@finally_within_finally@@"(
-// CHECK: call void @"?fin$1@0@finally_within_finally@@"({{.*}}) [ "funclet"(
+// CHECK: call {{.*}}void @"?fin$1@0@finally_within_finally@@"(
+// CHECK: call {{.*}}void @"?fin$1@0@finally_within_finally@@"({{.*}}) [ 
"funclet"(
 
-// CHECK-LABEL: define internal void 
@"?fin$1@0@finally_within_finally@@"({{[^)]*}})
+// CHECK-LABEL: define internal {{.*}}void 
@"?fin$1@0@finally_within_finally@@"({{[^)]*}})
 // CHECK-SAME: [[finally_attrs]]
 
 void cleanup_with_func(const char *);
@@ -282,8 +283,8 @@ void finally_with_func(void) {
   }
 }
 
-// CHECK-LABEL: define internal void @"?fin$0@0@finally_with_func@@"({{[^)]*}})
-// CHECK: call void @cleanup_with_func(ptr noundef 
@"??_C@_0BC@COAGBPGM@finally_with_func?$AA@")
+// CHECK-LABEL: define internal {{.*}}void 
@"?fin$0@0@finally_with_func@@"({{[^)]*}})
+// CHECK: call {{.*}}void @cleanup_with_func(ptr noundef 
@"??_C@_0BC@COAGBPGM@finally_with_func?$AA@")
 
 // Look for the absence of noinline.  nounwind is expected; any further
 // attributes should be string attributes.
diff --git a/clang/test/CodeGen/exceptions-seh.c 
b/clang/test/CodeGen/exceptions-seh.c
index 25d622419b09c..9869ac308da94 100644
--- a/clang/test/CodeGen/exceptions-seh.c
+++ b/clang/test/CodeGen/exceptions-seh.c
@@ -4,6 +4,8 @@
 // RUN:         | FileCheck %s --check-prefix=CHECK --check-prefix=X86
 // RUN: %clang_cc1 %s -triple aarch64-windows -fms-extensions -emit-llvm -o - \
 // RUN:         | FileCheck %s --check-prefixes=CHECK,ARM64
+// RUN: %clang_cc1 %s -triple thumbv7-windows -fms-extensions -emit-llvm -o - \
+// RUN:         | FileCheck %s --check-prefixes=CHECK,ARM
 // RUN: %clang_cc1 %s -triple i686-pc-windows-gnu -fms-extensions -emit-llvm 
-o - \
 // RUN:         | FileCheck %s --check-prefix=X86-GNU
 // RUN: %clang_cc1 %s -triple x86_64-pc-windows-gnu -fms-extensions -emit-llvm 
-o - \
@@ -12,7 +14,7 @@
 void try_body(int numerator, int denominator, int *myres) {
   *myres = numerator / denominator;
 }
-// CHECK-LABEL: define dso_local void @try_body(i32 noundef %numerator, i32 
noundef %denominator, ptr noundef %myres)
+// CHECK-LABEL: define dso_local {{.*}}void @try_body(i32 noundef %numerator, 
i32 noundef %denominator, ptr noundef %myres)
 // CHECK: sdiv i32
 // CHECK: store i32 %{{.*}}, ptr
 // CHECK: ret void
@@ -29,16 +31,18 @@ int safe_div(int numerator, int denominator, int *res) {
   return success;
 }
 
-// CHECK-LABEL: define dso_local i32 @safe_div(i32 noundef %numerator, i32 
noundef %denominator, ptr noundef %res)
+// CHECK-LABEL: define dso_local {{.*}}i32 @safe_div(i32 noundef %numerator, 
i32 noundef %denominator, ptr noundef %res)
 // X64-SAME:      personality ptr @__C_specific_handler
 // ARM64-SAME:    personality ptr @__C_specific_handler
+// ARM-SAME:      personality ptr @__C_specific_handler
 // X86-SAME:      personality ptr @_except_handler3
-// CHECK: invoke void @try_body(i32 noundef %{{.*}}, i32 noundef %{{.*}}, ptr 
noundef %{{.*}}) #[[NOINLINE:[0-9]+]]
+// CHECK: invoke {{.*}}void @try_body(i32 noundef %{{.*}}, i32 noundef 
%{{.*}}, ptr noundef %{{.*}}) #[[NOINLINE:[0-9]+]]
 // CHECK:       to label %{{.*}} unwind label %[[catchpad:[^ ]*]]
 //
 // CHECK: [[catchpad]]
 // X64: %[[padtoken:[^ ]*]] = catchpad within %{{[^ ]*}} [ptr null]
 // ARM64: %[[padtoken:[^ ]*]] = catchpad within %{{[^ ]*}} [ptr null]
+// ARM: %[[padtoken:[^ ]*]] = catchpad within %{{[^ ]*}} [ptr null]
 // X86: %[[padtoken:[^ ]*]] = catchpad within %{{[^ ]*}} [ptr 
@"?filt$0@0@safe_div@@"]
 // CHECK-NEXT: catchret from %[[padtoken]] to label %[[except:[^ ]*]]
 //
@@ -50,9 +54,9 @@ int safe_div(int numerator, int denominator, int *res) {
 
 // 32-bit SEH needs this filter to save the exception code.
 //
-// X86-LABEL: define internal i32 @"?filt$0@0@safe_div@@"()
+// X86-LABEL: define internal {{.*}}i32 @"?filt$0@0@safe_div@@"()
 // X86: %[[ebp:[^ ]*]] = call ptr @llvm.frameaddress.p0(i32 1)
-// X86: %[[fp:[^ ]*]] = call ptr @llvm.eh.recoverfp(ptr @safe_div, ptr 
%[[ebp]])
+// X86: %[[fp:[^ ]*]] = call ptr @llvm.eh.recoverfp(ptr @safe_div, ptr 
%[[ebp]], ptr {{.*}})
 // X86: call ptr @llvm.localrecover(ptr @safe_div, ptr %[[fp]], i32 0)
 // X86: load ptr, ptr
 // X86: load ptr, ptr
@@ -61,9 +65,9 @@ int safe_div(int numerator, int denominator, int *res) {
 // X86: ret i32 1
 
 // Mingw uses msvcrt, so it can also use _except_handler3.
-// X86-GNU-LABEL: define dso_local i32 @safe_div(i32 noundef %numerator, i32 
noundef %denominator, ptr noundef %res)
+// X86-GNU-LABEL: define dso_local {{.*}}i32 @safe_div(i32 noundef %numerator, 
i32 noundef %denominator, ptr noundef %res)
 // X86-GNU-SAME:      personality ptr @_except_handler3
-// X64-GNU-LABEL: define dso_local i32 @safe_div(i32 noundef %numerator, i32 
noundef %denominator, ptr noundef %res)
+// X64-GNU-LABEL: define dso_local {{.*}}i32 @safe_div(i32 noundef %numerator, 
i32 noundef %denominator, ptr noundef %res)
 // X64-GNU-SAME:      personality ptr @__C_specific_handler
 
 void j(void);
@@ -78,15 +82,17 @@ int filter_expr_capture(void) {
   return r;
 }
 
-// CHECK-LABEL: define dso_local i32 @filter_expr_capture()
+// CHECK-LABEL: define dso_local {{.*}}i32 @filter_expr_capture()
 // X64-SAME: personality ptr @__C_specific_handler
 // ARM64-SAME: personality ptr @__C_specific_handler
+// ARM-SAME: personality ptr @__C_specific_handler
 // X86-SAME: personality ptr @_except_handler3
 // X64: call void (...) @llvm.localescape(ptr %[[r:[^ ,]*]])
 // ARM64: call void (...) @llvm.localescape(ptr %[[r:[^ ,]*]])
+// ARM: call void (...) @llvm.localescape(ptr %[[r:[^ ,]*]])
 // X86: call void (...) @llvm.localescape(ptr %[[r:[^ ,]*]], ptr %[[code:[^ 
,]*]])
 // CHECK: store i32 42, ptr %[[r]]
-// CHECK: invoke void @j() #[[NOINLINE]]
+// CHECK: invoke {{.*}}void @j() #[[NOINLINE]]
 //
 // CHECK: catchpad within %{{[^ ]*}} [ptr @"?filt$0@0@filter_expr_capture@@"]
 // CHECK: store i32 13, ptr %[[r]]
@@ -94,17 +100,21 @@ int filter_expr_capture(void) {
 // CHECK: %[[rv:[^ ]*]] = load i32, ptr %[[r]]
 // CHECK: ret i32 %[[rv]]
 
-// X64-LABEL: define internal i32 @"?filt$0@0@filter_expr_capture@@"(ptr 
noundef %exception_pointers, ptr noundef %frame_pointer)
-// X64: %[[fp:[^ ]*]] = call ptr @llvm.eh.recoverfp(ptr @filter_expr_capture, 
ptr %frame_pointer)
+// X64-LABEL: define internal {{.*}}i32 @"?filt$0@0@filter_expr_capture@@"(ptr 
noundef %exception_pointers, ptr noundef %frame_pointer)
+// X64: %[[fp:[^ ]*]] = call ptr @llvm.eh.recoverfp(ptr @filter_expr_capture, 
ptr %frame_pointer, ptr {{.*}})
 // X64: call ptr @llvm.localrecover(ptr @filter_expr_capture, ptr %[[fp]], i32 
0)
 //
-// ARM64-LABEL: define internal i32 @"?filt$0@0@filter_expr_capture@@"(ptr 
noundef %exception_pointers, ptr noundef %frame_pointer)
-// ARM64: %[[fp:[^ ]*]] = call ptr @llvm.eh.recoverfp(ptr 
@filter_expr_capture, ptr %frame_pointer)
+// ARM64-LABEL: define internal {{.*}}i32 
@"?filt$0@0@filter_expr_capture@@"(ptr noundef %exception_pointers, ptr noundef 
%frame_pointer)
+// ARM64: %[[fp:[^ ]*]] = call ptr @llvm.eh.recoverfp(ptr 
@filter_expr_capture, ptr %frame_pointer, ptr {{.*}})
 // ARM64: call ptr @llvm.localrecover(ptr @filter_expr_capture, ptr %[[fp]], 
i32 0)
 //
-// X86-LABEL: define internal i32 @"?filt$0@0@filter_expr_capture@@"()
+// ARM-LABEL: define internal {{.*}}i32 @"?filt$0@0@filter_expr_capture@@"(ptr 
noundef %exception_pointers, ptr noundef %frame_pointer{{.*}})
+// ARM: %[[fp:[^ ]*]] = call ptr @llvm.eh.recoverfp(ptr @filter_expr_capture, 
ptr %frame_pointer, ptr %{{.*}})
+// ARM: call ptr @llvm.localrecover(ptr @filter_expr_capture, ptr %[[fp]], i32 
0)
+//
+// X86-LABEL: define internal {{.*}}i32 @"?filt$0@0@filter_expr_capture@@"()
 // X86: %[[ebp:[^ ]*]] = call ptr @llvm.frameaddress.p0(i32 1)
-// X86: %[[fp:[^ ]*]] = call ptr @llvm.eh.recoverfp(ptr @filter_expr_capture, 
ptr %[[ebp]])
+// X86: %[[fp:[^ ]*]] = call ptr @llvm.eh.recoverfp(ptr @filter_expr_capture, 
ptr %[[ebp]], ptr {{.*}})
 // X86: call ptr @llvm.localrecover(ptr @filter_expr_capture, ptr %[[fp]], i32 
0)
 //
 // CHECK: store i32 -1, ptr %{{.*}}
@@ -124,12 +134,13 @@ int nested_try(void) {
   }
   return r;
 }
-// CHECK-LABEL: define dso_local i32 @nested_try()
+// CHECK-LABEL: define dso_local {{.*}}i32 @nested_try()
 // X64-SAME: personality ptr @__C_specific_handler
 // ARM64-SAME: personality ptr @__C_specific_handler
+// ARM-SAME: personality ptr @__C_specific_handler
 // X86-SAME: personality ptr @_except_handler3
 // CHECK: store i32 42, ptr %[[r:[^ ,]*]]
-// CHECK: invoke void @j() #[[NOINLINE]]
+// CHECK: invoke {{.*}}void @j() #[[NOINLINE]]
 // CHECK:       to label %[[cont:[^ ]*]] unwind label %[[cswitch_inner:[^ ]*]]
 //
 // CHECK: [[cswitch_inner]]
@@ -165,13 +176,13 @@ int nested_try(void) {
 // CHECK: store i32 0, ptr %[[r]]
 // CHECK: br label %[[inner_try_cont]]
 //
-// CHECK-LABEL: define internal i32 @"?filt$0@0@nested_try@@"({{.*}})
+// CHECK-LABEL: define internal {{.*}}i32 @"?filt$0@0@nested_try@@"({{[^)]*}})
 // X86: call ptr @llvm.eh.recoverfp({{.*}})
 // CHECK: load ptr, ptr
 // CHECK: load i32, ptr
 // CHECK: icmp eq i32 %{{.*}}, 456
 //
-// CHECK-LABEL: define internal i32 @"?filt$1@0@nested_try@@"({{.*}})
+// CHECK-LABEL: define internal {{.*}}i32 @"?filt$1@0@nested_try@@"({{[^)]*}})
 // X86: call ptr @llvm.eh.recoverfp({{.*}})
 // CHECK: load ptr, ptr
 // CHECK: load i32, ptr
@@ -185,31 +196,32 @@ int basic_finally(int g) {
   }
   return g;
 }
-// CHECK-LABEL: define dso_local i32 @basic_finally(i32 noundef %g)
+// CHECK-LABEL: define dso_local {{.*}}i32 @basic_finally(i32 noundef %g)
 // X64-SAME: personality ptr @__C_specific_handler
 // ARM64-SAME: personality ptr @__C_specific_handler
+// ARM-SAME: personality ptr @__C_specific_handler
 // X86-SAME: personality ptr @_except_handler3
 // CHECK: %[[g_addr:[^ ]*]] = alloca i32, align 4
 // CHECK: call void (...) @llvm.localescape(ptr %[[g_addr]])
 // CHECK: store i32 %g, ptr %[[g_addr]]
 //
-// CHECK: invoke void @j()
+// CHECK: invoke {{.*}}void @j()
 // CHECK:       to label %[[cont:[^ ]*]] unwind label %[[cleanuppad:[^ ]*]]
 //
 // CHECK: [[cont]]
 // CHECK: %[[fp:[^ ]*]] = call ptr @llvm.localaddress()
-// CHECK: call void @"?fin$0@0@basic_finally@@"({{i8 noundef( zeroext)?}} 0, 
ptr noundef %[[fp]])
+// CHECK: call {{.*}}void @"?fin$0@0@basic_finally@@"({{i8 noundef( 
zeroext)?}} 0, ptr noundef %[[fp]]{{.*}})
 // CHECK: load i32, ptr %[[g_addr]], align 4
 // CHECK: ret i32
 //
 // CHECK: [[cleanuppad]]
 // CHECK: %[[padtoken:[^ ]*]] = cleanuppad within none []
 // CHECK: %[[fp:[^ ]*]] = call ptr @llvm.localaddress()
-// CHECK: call void @"?fin$0@0@basic_finally@@"({{i8 noundef( zeroext)?}} 1, 
ptr noundef %[[fp]])
+// CHECK: call {{.*}}void @"?fin$0@0@basic_finally@@"({{i8 noundef( 
zeroext)?}} 1, ptr noundef %[[fp]]{{.*}})
 // CHECK: cleanupret from %[[padtoken]] unwind to caller
 
-// CHECK: define internal void @"?fin$0@0@basic_finally@@"({{i8 noundef( 
zeroext)?}} %abnormal_termination, ptr noundef %frame_pointer)
-// CHECK:   call ptr @llvm.localrecover(ptr @basic_finally, ptr 
%frame_pointer, i32 0)
+// CHECK: define internal {{.*}}void @"?fin$0@0@basic_finally@@"({{[^)]*}})
+// CHECK:   call ptr @llvm.localrecover(ptr @basic_finally, ptr %{{.*}}, i32 0)
 // CHECK:   load i32, ptr %{{.*}}, align 4
 // CHECK:   add nsw i32 %{{.*}}, 1
 // CHECK:   store i32 %{{.*}}, ptr %{{.*}}, align 4
@@ -223,8 +235,8 @@ int except_return(void) {
     return 42;
   }
 }
-// CHECK-LABEL: define dso_local i32 @except_return()
-// CHECK: %[[tmp:[^ ]*]] = invoke i32 @returns_int()
+// CHECK-LABEL: define dso_local {{.*}}i32 @except_return()
+// CHECK: %[[tmp:[^ ]*]] = invoke {{.*}}i32 @returns_int()
 // CHECK:       to label %[[cont:[^ ]*]] unwind label %[[catchpad:[^ ]*]]
 //
 // CHECK: [[catchpad]]
@@ -252,18 +264,18 @@ void finally_capture_twice(int x) {
   }
 }
 //
-// CHECK-LABEL: define dso_local void @finally_capture_twice(
+// CHECK-LABEL: define dso_local {{.*}}void @finally_capture_twice(
 // CHECK:         [[X:%.*]] = alloca i32, align 4
 // CHECK:         call void (...) @llvm.localescape(ptr [[X]])
 // CHECK-NEXT:    store i32 {{.*}}, ptr [[X]], align 4
 // CHECK-NEXT:    [[LOCAL:%.*]] = call ptr @llvm.localaddress()
-// CHECK-NEXT:    call void [[FINALLY:@.*]](i8 noundef{{ zeroext | }}0, ptr 
noundef [[LOCAL]])
-// CHECK:       define internal void [[FINALLY]](
+// CHECK-NEXT:    call {{.*}}void [[FINALLY:@.*]](i8 noundef{{ zeroext | }}0, 
ptr noundef [[LOCAL]]{{.*}})
+// CHECK:       define internal {{.*}}void [[FINALLY]]({{[^)]*}})
 // CHECK:         [[LOCAL:%.*]] = call ptr @llvm.localrecover(
 // CHECK-NEXT:    [[Y:%.*]] = alloca i32, align 4
 // CHECK-NEXT:    [[Z:%.*]] = alloca i32, align 4
-// CHECK-NEXT:    store ptr
-// CHECK-NEXT:    store i8
+// CHECK: store ptr
+// CHECK: store i8
 // CHECK-NEXT:    [[T0:%.*]] = load i32, ptr [[LOCAL]], align 4
 // CHECK-NEXT:    store i32 [[T0]], ptr [[Y]], align 4
 // CHECK-NEXT:    [[T0:%.*]] = load i32, ptr [[LOCAL]], align 4
@@ -279,15 +291,15 @@ int exception_code_in_except(void) {
   return 0;
 }
 
-// CHECK-LABEL: define dso_local i32 @exception_code_in_except()
+// CHECK-LABEL: define dso_local {{.*}}i32 @exception_code_in_except()
 // CHECK: %[[ret_slot:[^ ]*]] = alloca i32
 // CHECK: %[[code_slot:[^ ]*]] = alloca i32
-// CHECK: invoke void @try_body(i32 noundef 0, i32 noundef 0, ptr noundef null)
+// CHECK: invoke {{.*}}void @try_body(i32 noundef 0, i32 noundef 0, ptr 
noundef null)
 // CHECK: %[[pad:[^ ]*]] = catchpad
 // CHECK: catchret from %[[pad]]
-// X64: %[[code:[^ ]*]] = call i32 @llvm.eh.exceptioncode(token %[[pad]])
+// X64: %[[code:[^ ]*]] = call {{.*}}i32 @llvm.eh.exceptioncode(token %[[pad]])
 // X64: store i32 %[[code]], ptr %[[code_slot]]
-// ARM64: %[[code:[^ ]*]] = call i32 @llvm.eh.exceptioncode(token %[[pad]])
+// ARM64: %[[code:[^ ]*]] = call {{.*}}i32 @llvm.eh.exceptioncode(token 
%[[pad]])
 // ARM64: store i32 %[[code]], ptr %[[code_slot]]
 // CHECK: %[[ret1:[^ ]*]] = load i32, ptr %[[code_slot]]
 // CHECK: store i32 %[[ret1]], ptr %[[ret_slot]]
diff --git a/llvm/test/CodeGen/ARM/seh-finally.ll 
b/llvm/test/CodeGen/ARM/seh-finally.ll
new file mode 100644
index 0000000000000..53e4b993ef1fe
--- /dev/null
+++ b/llvm/test/CodeGen/ARM/seh-finally.ll
@@ -0,0 +1,301 @@
+; RUN: llc -mtriple thumbv7-windows-msvc -o - %s | FileCheck %s
+
+; struct S { int x; };
+; void foo(int n);
+; void foo_s(struct S o);
+; void simple_seh() {
+;   struct S o;
+; 
+;   __try { foo(o.x); }
+;   __finally { foo(o.x); }
+; }
+; void stack_realign() {
+;   struct S __declspec(align(32)) o;
+; 
+;   __try { foo(o.x); }
+;   __finally { foo(o.x); }
+; }
+; void vla_present(int n) {
+;   int vla[n];
+; 
+;   __try { foo(n); }
+;   __finally { foo(n); }
+; }
+; void vla_and_realign(int n) {
+;   struct S __declspec(align(32)) o;
+;   int vla[n];
+; 
+;   __try { foo(o.x); }
+;   __finally { foo(o.x); }
+; }
+
+%struct.S = type { i32 }
+
+define dso_local arm_aapcs_vfpcc void @simple_seh() #0 personality ptr 
@__C_specific_handler {
+entry:
+; CHECK-LABEL: simple_seh:
+; CHECK: push.w {r11, lr}
+; CHECK: $Msimple_seh$frame_escape_0 = [[OFFSET:[0-9]+]]
+; CHECK: bl foo
+; CHECK: mov r1, sp
+; CHECK: movs r2, #0
+; CHECK: movs r3, #0
+; CHECK: bl "?fin$0@0@simple_seh@@"
+
+  %o = alloca %struct.S, align 4
+  call void (...) @llvm.localescape(ptr %o)
+  %x = getelementptr inbounds nuw %struct.S, ptr %o, i32 0, i32 0
+  %0 = load i32, ptr %x, align 4
+  invoke arm_aapcs_vfpcc void @foo(i32 noundef %0) #5
+          to label %invoke.cont unwind label %ehcleanup
+
+invoke.cont:                                      ; preds = %entry
+  %1 = call ptr @llvm.localaddress()
+  call arm_aapcs_vfpcc void @"?fin$0@0@simple_seh@@"(i8 noundef zeroext 0, ptr 
noundef %1, ptr noundef null, ptr noundef null)
+  ret void
+
+ehcleanup:                                        ; preds = %entry
+  %2 = cleanuppad within none []
+  %3 = call ptr @llvm.localaddress()
+  %4 = call i32 asm "", "={r2}"()
+  %5 = inttoptr i32 %4 to ptr
+  %6 = call i32 asm "", "={r3}"()
+  %7 = inttoptr i32 %6 to ptr
+  call arm_aapcs_vfpcc void @"?fin$0@0@simple_seh@@"(i8 noundef zeroext 1, ptr 
noundef %3, ptr noundef %5, ptr noundef %7) [ "funclet"(token %2) ]
+  cleanupret from %2 unwind to caller
+}
+
+define internal arm_aapcs_vfpcc void @"?fin$0@0@simple_seh@@"(i8 noundef 
zeroext %abnormal_termination, ptr noundef %frame_pointer, ptr noundef 
%callback, ptr noundef %nonvolatile_regs) #1 {
+entry:
+; CHECK-LABEL: "?fin$0@0@simple_seh@@":
+; CHECK: movw [[R_ESC:r[0-9]+]], :lower16:$Msimple_seh$frame_escape_0
+; CHECK: movt [[R_ESC]], :upper16:$Msimple_seh$frame_escape_0
+; CHECK: ldr [[R_VAL:r[0-9]+]], [{{r[0-9]+}}, [[R_ESC]]]
+; CHECK: bl foo
+
+  %nonvolatile_regs.addr = alloca ptr, align 4
+  %callback.addr = alloca ptr, align 4
+  %frame_pointer.addr = alloca ptr, align 4
+  %abnormal_termination.addr = alloca i8, align 1
+  %0 = call ptr @llvm.eh.recoverfp(ptr @simple_seh, ptr %frame_pointer, ptr 
%nonvolatile_regs)
+  %o = call ptr @llvm.localrecover(ptr @simple_seh, ptr %0, i32 0)
+  store ptr %nonvolatile_regs, ptr %nonvolatile_regs.addr, align 4
+  store ptr %callback, ptr %callback.addr, align 4
+  store ptr %frame_pointer, ptr %frame_pointer.addr, align 4
+  store i8 %abnormal_termination, ptr %abnormal_termination.addr, align 1
+  %x = getelementptr inbounds nuw %struct.S, ptr %o, i32 0, i32 0
+  %1 = load i32, ptr %x, align 4
+  call arm_aapcs_vfpcc void @foo(i32 noundef %1)
+  ret void
+}
+
+define dso_local arm_aapcs_vfpcc void @stack_realign() #0 personality ptr 
@__C_specific_handler {
+entry:
+; CHECK-LABEL: stack_realign:
+; CHECK: push {r4, r7}
+; CHECK: push.w {r11, lr}
+; CHECK: mov r11, sp
+; CHECK: bfc r4, #0, #5
+; CHECK: $Mstack_realign$frame_escape_0 = 0
+; CHECK: bl foo
+; CHECK: mov r1, r6
+; CHECK: movs r2, #0
+; CHECK: movs r3, #0
+; CHECK: bl "?fin$0@0@stack_realign@@"
+
+  %o = alloca %struct.S, align 32
+  call void (...) @llvm.localescape(ptr %o)
+  %x = getelementptr inbounds nuw %struct.S, ptr %o, i32 0, i32 0
+  %0 = load i32, ptr %x, align 32
+  invoke arm_aapcs_vfpcc void @foo(i32 noundef %0) #5
+          to label %invoke.cont unwind label %ehcleanup
+
+invoke.cont:                                      ; preds = %entry
+  %1 = call ptr @llvm.localaddress()
+  call arm_aapcs_vfpcc void @"?fin$0@0@stack_realign@@"(i8 noundef zeroext 0, 
ptr noundef %1, ptr noundef null, ptr noundef null)
+  ret void
+
+ehcleanup:                                        ; preds = %entry
+  %2 = cleanuppad within none []
+  %3 = call ptr @llvm.localaddress()
+  %4 = call i32 asm "", "={r2}"()
+  %5 = inttoptr i32 %4 to ptr
+  %6 = call i32 asm "", "={r3}"()
+  %7 = inttoptr i32 %6 to ptr
+  call arm_aapcs_vfpcc void @"?fin$0@0@stack_realign@@"(i8 noundef zeroext 1, 
ptr noundef %3, ptr noundef %5, ptr noundef %7) [ "funclet"(token %2) ]
+  cleanupret from %2 unwind to caller
+}
+
+define internal arm_aapcs_vfpcc void @"?fin$0@0@stack_realign@@"(i8 noundef 
zeroext %abnormal_termination, ptr noundef %frame_pointer, ptr noundef 
%callback, ptr noundef %nonvolatile_regs) #1 {
+entry:
+; CHECK-LABEL: "?fin$0@0@stack_realign@@":
+; CHECK: movw [[R_ESC:r[0-9]+]], :lower16:$Mstack_realign$frame_escape_0
+; CHECK: movt [[R_ESC]], :upper16:$Mstack_realign$frame_escape_0
+; CHECK: ldr [[R_VAL:r[0-9]+]], [{{r[0-9]+}}, [[R_ESC]]]
+; CHECK: bl foo
+
+  %nonvolatile_regs.addr = alloca ptr, align 4
+  %callback.addr = alloca ptr, align 4
+  %frame_pointer.addr = alloca ptr, align 4
+  %abnormal_termination.addr = alloca i8, align 1
+  %0 = call ptr @llvm.eh.recoverfp(ptr @stack_realign, ptr %frame_pointer, ptr 
%nonvolatile_regs)
+  %o = call ptr @llvm.localrecover(ptr @stack_realign, ptr %0, i32 0)
+  store ptr %nonvolatile_regs, ptr %nonvolatile_regs.addr, align 4
+  store ptr %callback, ptr %callback.addr, align 4
+  store ptr %frame_pointer, ptr %frame_pointer.addr, align 4
+  store i8 %abnormal_termination, ptr %abnormal_termination.addr, align 1
+  %x = getelementptr inbounds nuw %struct.S, ptr %o, i32 0, i32 0
+  %1 = load i32, ptr %x, align 32
+  call arm_aapcs_vfpcc void @foo(i32 noundef %1)
+  ret void
+}
+
+define dso_local arm_aapcs_vfpcc void @vla_present(i32 noundef %n) #0 
personality ptr @__C_specific_handler {
+entry:
+; CHECK-LABEL: vla_present:
+; CHECK: push.w {r11, lr}
+; CHECK: $Mvla_present$frame_escape_0 = [[OFFSET:[0-9]+]]
+; CHECK: bl foo
+; CHECK: mov r1, sp
+; CHECK: movs r2, #0
+; CHECK: movs r3, #0
+; CHECK: bl "?fin$0@0@vla_present@@"
+
+  %n.addr = alloca i32, align 4
+  %saved_stack = alloca ptr, align 4
+  %__vla_expr0 = alloca i32, align 4
+  call void (...) @llvm.localescape(ptr %n.addr)
+  store i32 %n, ptr %n.addr, align 4
+  %0 = load i32, ptr %n.addr, align 4
+  %1 = call ptr @llvm.stacksave.p0()
+  store ptr %1, ptr %saved_stack, align 4
+  %vla = alloca i32, i32 %0, align 4
+  store i32 %0, ptr %__vla_expr0, align 4
+  %2 = load i32, ptr %n.addr, align 4
+  invoke arm_aapcs_vfpcc void @foo(i32 noundef %2) #5
+          to label %invoke.cont unwind label %ehcleanup
+
+invoke.cont:                                      ; preds = %entry
+  %3 = call ptr @llvm.localaddress()
+  call arm_aapcs_vfpcc void @"?fin$0@0@vla_present@@"(i8 noundef zeroext 0, 
ptr noundef %3, ptr noundef null, ptr noundef null)
+  %4 = load ptr, ptr %saved_stack, align 4
+  call void @llvm.stackrestore.p0(ptr %4)
+  ret void
+
+ehcleanup:                                        ; preds = %entry
+  %5 = cleanuppad within none []
+  %6 = call ptr @llvm.localaddress()
+  %7 = call i32 asm "", "={r2}"()
+  %8 = inttoptr i32 %7 to ptr
+  %9 = call i32 asm "", "={r3}"()
+  %10 = inttoptr i32 %9 to ptr
+  call arm_aapcs_vfpcc void @"?fin$0@0@vla_present@@"(i8 noundef zeroext 1, 
ptr noundef %6, ptr noundef %8, ptr noundef %10) [ "funclet"(token %5) ]
+  cleanupret from %5 unwind to caller
+}
+
+define internal arm_aapcs_vfpcc void @"?fin$0@0@vla_present@@"(i8 noundef 
zeroext %abnormal_termination, ptr noundef %frame_pointer, ptr noundef 
%callback, ptr noundef %nonvolatile_regs) #1 {
+entry:
+; CHECK-LABEL: "?fin$0@0@vla_present@@":
+; CHECK: movw [[R_ESC:r[0-9]+]], :lower16:$Mvla_present$frame_escape_0
+; CHECK: movt [[R_ESC]], :upper16:$Mvla_present$frame_escape_0
+; CHECK: ldr [[R_VAL:r[0-9]+]], [{{r[0-9]+}}, [[R_ESC]]]
+; CHECK: bl foo
+
+  %nonvolatile_regs.addr = alloca ptr, align 4
+  %callback.addr = alloca ptr, align 4
+  %frame_pointer.addr = alloca ptr, align 4
+  %abnormal_termination.addr = alloca i8, align 1
+  %0 = call ptr @llvm.eh.recoverfp(ptr @vla_present, ptr %frame_pointer, ptr 
%nonvolatile_regs)
+  %n.addr = call ptr @llvm.localrecover(ptr @vla_present, ptr %0, i32 0)
+  store ptr %nonvolatile_regs, ptr %nonvolatile_regs.addr, align 4
+  store ptr %callback, ptr %callback.addr, align 4
+  store ptr %frame_pointer, ptr %frame_pointer.addr, align 4
+  store i8 %abnormal_termination, ptr %abnormal_termination.addr, align 1
+  %1 = load i32, ptr %n.addr, align 4
+  call arm_aapcs_vfpcc void @foo(i32 noundef %1)
+  ret void
+}
+
+define dso_local arm_aapcs_vfpcc void @vla_and_realign(i32 noundef %n) #0 
personality ptr @__C_specific_handler {
+entry:
+; CHECK-LABEL: vla_and_realign:
+; CHECK: push {r4, r7}
+; CHECK: push.w {r11, lr}
+; CHECK: mov r11, sp
+; CHECK: bfc r4, #0, #5
+; CHECK: $Mvla_and_realign$frame_escape_0 = [[OFFSET:[0-9]+]]
+; CHECK: bl foo
+; CHECK: mov r1, r6
+; CHECK: movs r2, #0
+; CHECK: movs r3, #0
+; CHECK: bl "?fin$0@0@vla_and_realign@@"
+
+  %n.addr = alloca i32, align 4
+  %o = alloca %struct.S, align 32
+  %saved_stack = alloca ptr, align 4
+  %__vla_expr0 = alloca i32, align 4
+  call void (...) @llvm.localescape(ptr %o)
+  store i32 %n, ptr %n.addr, align 4
+  %0 = load i32, ptr %n.addr, align 4
+  %1 = call ptr @llvm.stacksave.p0()
+  store ptr %1, ptr %saved_stack, align 4
+  %vla = alloca i32, i32 %0, align 4
+  store i32 %0, ptr %__vla_expr0, align 4
+  %x = getelementptr inbounds nuw %struct.S, ptr %o, i32 0, i32 0
+  %2 = load i32, ptr %x, align 32
+  invoke arm_aapcs_vfpcc void @foo(i32 noundef %2) #5
+          to label %invoke.cont unwind label %ehcleanup
+
+invoke.cont:                                      ; preds = %entry
+  %3 = call ptr @llvm.localaddress()
+  call arm_aapcs_vfpcc void @"?fin$0@0@vla_and_realign@@"(i8 noundef zeroext 
0, ptr noundef %3, ptr noundef null, ptr noundef null)
+  %4 = load ptr, ptr %saved_stack, align 4
+  call void @llvm.stackrestore.p0(ptr %4)
+  ret void
+
+ehcleanup:                                        ; preds = %entry
+  %5 = cleanuppad within none []
+  %6 = call ptr @llvm.localaddress()
+  %7 = call i32 asm "", "={r2}"()
+  %8 = inttoptr i32 %7 to ptr
+  %9 = call i32 asm "", "={r3}"()
+  %10 = inttoptr i32 %9 to ptr
+  call arm_aapcs_vfpcc void @"?fin$0@0@vla_and_realign@@"(i8 noundef zeroext 
1, ptr noundef %6, ptr noundef %8, ptr noundef %10) [ "funclet"(token %5) ]
+  cleanupret from %5 unwind to caller
+}
+
+define internal arm_aapcs_vfpcc void @"?fin$0@0@vla_and_realign@@"(i8 noundef 
zeroext %abnormal_termination, ptr noundef %frame_pointer, ptr noundef 
%callback, ptr noundef %nonvolatile_regs) #1 {
+entry:
+; CHECK-LABEL: "?fin$0@0@vla_and_realign@@":
+; CHECK: movw [[R_ESC:r[0-9]+]], :lower16:$Mvla_and_realign$frame_escape_0
+; CHECK: movt [[R_ESC]], :upper16:$Mvla_and_realign$frame_escape_0
+; CHECK: ldr [[R_VAL:r[0-9]+]], [{{r[0-9]+}}, [[R_ESC]]]
+; CHECK: bl foo
+
+  %nonvolatile_regs.addr = alloca ptr, align 4
+  %callback.addr = alloca ptr, align 4
+  %frame_pointer.addr = alloca ptr, align 4
+  %abnormal_termination.addr = alloca i8, align 1
+  %0 = call ptr @llvm.eh.recoverfp(ptr @vla_and_realign, ptr %frame_pointer, 
ptr %nonvolatile_regs)
+  %o = call ptr @llvm.localrecover(ptr @vla_and_realign, ptr %0, i32 0)
+  store ptr %nonvolatile_regs, ptr %nonvolatile_regs.addr, align 4
+  store ptr %callback, ptr %callback.addr, align 4
+  store ptr %frame_pointer, ptr %frame_pointer.addr, align 4
+  store i8 %abnormal_termination, ptr %abnormal_termination.addr, align 1
+  %x = getelementptr inbounds nuw %struct.S, ptr %o, i32 0, i32 0
+  %1 = load i32, ptr %x, align 32
+  call arm_aapcs_vfpcc void @foo(i32 noundef %1)
+  ret void
+}
+
+declare arm_aapcs_vfpcc void @foo(i32 noundef)
+declare void @llvm.stackrestore.p0(ptr)
+declare ptr @llvm.stacksave.p0()
+declare ptr @llvm.eh.recoverfp(ptr, ptr, ptr)
+declare ptr @llvm.localrecover(ptr, ptr, i32 immarg)
+declare ptr @llvm.localaddress()
+declare void @llvm.localescape(...)
+declare i32 @__C_specific_handler(...)
+
+attributes #0 = { noinline optnone }
+attributes #1 = { noinline }

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to