Index: lib/Target/AArch64/AArch64FrameLowering.h =================================================================== --- lib/Target/AArch64/AArch64FrameLowering.h +++ lib/Target/AArch64/AArch64FrameLowering.h @@ -69,6 +69,17 @@ bool enableStackSlotScavenging(const MachineFunction &MF) const override; + void processFunctionBeforeFrameFinalized(MachineFunction &MF, + RegScavenger *RS) const override; + + unsigned getWinEHParentFrameOffset(const MachineFunction &MF) const override; + + unsigned getWinEHFuncletFrameSize(const MachineFunction &MF) const; + + int getFrameIndexReferencePreferSP(const MachineFunction &MF, int FI, + unsigned &FrameReg, + bool IgnoreSPUpdates) const override; + private: bool shouldCombineCSRLocalStackBump(MachineFunction &MF, unsigned StackBumpBytes) const; Index: lib/Target/AArch64/AArch64FrameLowering.cpp =================================================================== --- lib/Target/AArch64/AArch64FrameLowering.cpp +++ lib/Target/AArch64/AArch64FrameLowering.cpp @@ -204,6 +204,11 @@ bool AArch64FrameLowering::hasFP(const MachineFunction &MF) const { const MachineFrameInfo &MFI = MF.getFrameInfo(); const TargetRegisterInfo *RegInfo = MF.getSubtarget().getRegisterInfo(); + // Win64 EH requires a frame pointer if funclets are present, as the locals + // are accessed off the frame pointer in both the parent function and the + // funclets. + if (MF.hasEHFunclets()) + return true; // Retain behavior of always omitting the FP for leaf functions when possible. if (MFI.hasCalls() && MF.getTarget().Options.DisableFramePointerElim(MF)) return true; @@ -774,6 +779,12 @@ return Key.equals_lower("a_key"); } +static bool needsWinCFI(const MachineFunction &MF) { + const Function &F = MF.getFunction(); + return MF.getTarget().getMCAsmInfo()->usesWindowsCFI() && + F.needsUnwindTableEntry(); +} + void AArch64FrameLowering::emitPrologue(MachineFunction &MF, MachineBasicBlock &MBB) const { MachineBasicBlock::iterator MBBI = MBB.begin(); @@ -787,9 +798,10 @@ bool needsFrameMoves = (MMI.hasDebugInfo() || F.needsUnwindTableEntry()) && !MF.getTarget().getMCAsmInfo()->usesWindowsCFI(); bool HasFP = hasFP(MF); - bool NeedsWinCFI = MF.getTarget().getMCAsmInfo()->usesWindowsCFI() && - F.needsUnwindTableEntry(); + bool NeedsWinCFI = needsWinCFI(MF); MF.setHasWinCFI(NeedsWinCFI); + bool IsFunclet = MBB.isEHFuncletEntry(); + // At this point, we're going to decide whether or not the function uses a // redzone. In most cases, the function doesn't have a redzone so let's // assume that's false and set it to true in the case that there's a redzone. @@ -811,7 +823,14 @@ if (MF.getFunction().getCallingConv() == CallingConv::GHC) return; - int NumBytes = (int)MFI.getStackSize(); + // getStackSize() includes all the locals in its size calculation. We don't + // include these locals when computing the stack size of a funclet, as they + // are allocated in the parent's stack frame and accessed via the frame + // pointer from the funclet. We only save the callee saved registers in the + // funclet, which are really the callee saved registers of the parent + // function, including the funclet. + int NumBytes = IsFunclet ? (int)getWinEHFuncletFrameSize(MF) + : (int)MFI.getStackSize(); if (!AFI->hasStackFrame() && !windowsRequiresStackProbe(MF, NumBytes)) { assert(!HasFP && "unexpected function without stack frame but with FP"); // All of the stack allocation is for locals. @@ -847,7 +866,10 @@ bool IsWin64 = Subtarget.isCallingConvWin64(MF.getFunction().getCallingConv()); - unsigned FixedObject = IsWin64 ? alignTo(AFI->getVarArgsGPRSize(), 16) : 0; + // Var args are accounted for in the containing function, so don't + // include them for funclets. + unsigned FixedObject = (IsWin64 && !IsFunclet) ? + alignTo(AFI->getVarArgsGPRSize(), 16) : 0; auto PrologueSaveSize = AFI->getCalleeSavedStackSize() + FixedObject; // All of the remaining stack allocations are for locals. @@ -875,6 +897,16 @@ ++MBBI; } + // The code below is not applicable to funclets. We have emitted all the SEH + // opcodes that we needed to emit. The FP and BP belong to the containing + // function. + if (IsFunclet) { + if (NeedsWinCFI) + BuildMI(MBB, MBBI, DL, TII->get(AArch64::SEH_PrologEnd)) + .setMIFlag(MachineInstr::FrameSetup); + return; + } + if (HasFP) { // Only set up FP if we actually need to. Frame pointer is fp = // sp - fixedobject - 16. @@ -1165,6 +1197,16 @@ } } +static bool isFuncletReturnInstr(const MachineInstr &MI) { + switch (MI.getOpcode()) { + default: + return false; + case AArch64::CATCHRET: + case AArch64::CLEANUPRET: + return true; + } +} + void AArch64FrameLowering::emitEpilogue(MachineFunction &MF, MachineBasicBlock &MBB) const { MachineBasicBlock::iterator MBBI = MBB.getLastNonDebugInstr(); @@ -1173,8 +1215,8 @@ const TargetInstrInfo *TII = Subtarget.getInstrInfo(); DebugLoc DL; bool IsTailCallReturn = false; - bool NeedsWinCFI = MF.getTarget().getMCAsmInfo()->usesWindowsCFI() && - MF.getFunction().needsUnwindTableEntry(); + bool NeedsWinCFI = needsWinCFI(MF); + bool IsFunclet = isFuncletReturnInstr(*MBBI); if (MBB.end() != MBBI) { DL = MBBI->getDebugLoc(); @@ -1184,7 +1226,8 @@ RetOpcode == AArch64::TCRETURNriBTI; } - int NumBytes = MFI.getStackSize(); + int NumBytes = IsFunclet ? (int)getWinEHFuncletFrameSize(MF) + : MFI.getStackSize(); AArch64FunctionInfo *AFI = MF.getInfo(); // All calls are tail calls in GHC calling conv, and functions have no @@ -1242,9 +1285,12 @@ bool IsWin64 = Subtarget.isCallingConvWin64(MF.getFunction().getCallingConv()); unsigned FixedObject = IsWin64 ? alignTo(AFI->getVarArgsGPRSize(), 16) : 0; - uint64_t AfterCSRPopSize = ArgumentPopSize; auto PrologueSaveSize = AFI->getCalleeSavedStackSize() + FixedObject; + // Var args are accounted for in the containting function, so don't + // include them for funclets. + if (MF.hasEHFunclets()) + AFI->setLocalStackSize(NumBytes - PrologueSaveSize); bool CombineSPBump = shouldCombineCSRLocalStackBump(MF, NumBytes); // Assume we can't combine the last pop with the sp restore. @@ -1445,6 +1491,14 @@ // being in range for direct access. If the FPOffset is positive, // that'll always be best, as the SP will be even further away. UseFP = true; + } else if (MF.hasEHFunclets()) { + // Funclets access the locals contained in the in the parent's stack + // frame via the frame pointer, so we have to use the FP in the parent + // function. + assert( + Subtarget.isCallingConvWin64(MF.getFunction().getCallingConv()) && + "Funclets should only be present on Win64"); + UseFP = true; } else { // We have the choice between FP and (SP or BP). if (FPOffsetFits && PreferFP) // If FP is the best fit, use it. @@ -1538,8 +1592,7 @@ if (CSI.empty()) return; - bool NeedsWinCFI = MF.getTarget().getMCAsmInfo()->usesWindowsCFI() && - MF.getFunction().needsUnwindTableEntry(); + bool NeedsWinCFI = needsWinCFI(MF); AArch64FunctionInfo *AFI = MF.getInfo(); MachineFrameInfo &MFI = MF.getFrameInfo(); CallingConv::ID CC = MF.getFunction().getCallingConv(); @@ -1652,8 +1705,7 @@ const TargetRegisterInfo *TRI) const { MachineFunction &MF = *MBB.getParent(); const TargetInstrInfo &TII = *MF.getSubtarget().getInstrInfo(); - bool NeedsWinCFI = MF.getTarget().getMCAsmInfo()->usesWindowsCFI() && - MF.getFunction().needsUnwindTableEntry(); + bool NeedsWinCFI = needsWinCFI(MF); DebugLoc DL; SmallVector RegPairs; @@ -1765,8 +1817,7 @@ const TargetInstrInfo &TII = *MF.getSubtarget().getInstrInfo(); DebugLoc DL; SmallVector RegPairs; - bool NeedsWinCFI = MF.getTarget().getMCAsmInfo()->usesWindowsCFI() && - MF.getFunction().needsUnwindTableEntry(); + bool NeedsWinCFI = needsWinCFI(MF); if (MI != MBB.end()) DL = MI->getDebugLoc(); @@ -1998,3 +2049,73 @@ const AArch64FunctionInfo *AFI = MF.getInfo(); return AFI->hasCalleeSaveStackFreeSpace(); } + +void AArch64FrameLowering::processFunctionBeforeFrameFinalized( + MachineFunction &MF, RegScavenger *RS) const { + // Mark the function as not having WinCFI. We will set it back to true in + // emitPrologue if unwinding info is needed. + MF.setHasWinCFI(false); + + // If this function isn't doing Win64-style C++ EH, we don't need to do + // anything. + if (!MF.hasEHFunclets()) + return; + const TargetInstrInfo &TII = *MF.getSubtarget().getInstrInfo(); + MachineFrameInfo &MFI = MF.getFrameInfo(); + WinEHFuncInfo &EHInfo = *MF.getWinEHFuncInfo(); + + MachineBasicBlock &MBB = MF.front(); + auto MBBI = MBB.begin(); + while (MBBI != MBB.end() && MBBI->getFlag(MachineInstr::FrameSetup)) + ++MBBI; + + if (MBBI->isTerminator()) + return; + + // Create an UnwindHelp object. + int UnwindHelpFI = + MFI.CreateStackObject(/*size*/8, /*alignment*/16, false); + EHInfo.UnwindHelpFrameIdx = UnwindHelpFI; + // We need to store -2 into the UnwindHelp object at the start of the + // function. + DebugLoc DL; + RS->enterBasicBlock(MBB); + unsigned DstReg = RS->scavengeRegister(&AArch64::GPR64RegClass, MBBI, 0); + BuildMI(MBB, MBBI, DL, TII.get(AArch64::MOVi64imm), DstReg).addImm(-2); + BuildMI(MBB, MBBI, DL, TII.get(AArch64::STURXi)) + .addReg(DstReg, getKillRegState(true)) + .addFrameIndex(UnwindHelpFI) + .addImm(0); +} + +/// For Win64 AArch64 EH, the offset to the Unwind object is from the SP before +/// the update. This is easily retrieved as it is exactly the offset that is set +/// in processFunctionBeforeFrameFinalized. +int AArch64FrameLowering::getFrameIndexReferencePreferSP( + const MachineFunction &MF, int FI, unsigned &FrameReg, + bool IgnoreSPUpdates) const { + const MachineFrameInfo &MFI = MF.getFrameInfo(); + LLVM_DEBUG(dbgs() << "Offset from the SP for " << FI << " is " + << MFI.getObjectOffset(FI) << "\n"); + FrameReg = AArch64::SP; + return MFI.getObjectOffset(FI); +} + +/// The parent frame offset (aka dispFrame) is only used on X86_64 to retrieve +/// the parent's frame pointer +unsigned AArch64FrameLowering::getWinEHParentFrameOffset( + const MachineFunction &MF) const { + return 0; +} + +/// Funclets only need to account for space for the callee saved registers, +/// as the locals are account for in the paren't stack frame. +unsigned AArch64FrameLowering::getWinEHFuncletFrameSize( + const MachineFunction &MF) const { + // This is the size of the pushed CSRs. + unsigned CSSize = + MF.getInfo()->getCalleeSavedStackSize(); + // This is the amount of stack a funclet needs to allocate. + return alignTo(CSSize + MF.getFrameInfo().getMaxCallFrameSize(), + getStackAlignment()); +} Index: lib/Target/AArch64/AArch64ISelLowering.h =================================================================== --- lib/Target/AArch64/AArch64ISelLowering.h +++ lib/Target/AArch64/AArch64ISelLowering.h @@ -302,6 +302,12 @@ MachineBasicBlock *EmitF128CSEL(MachineInstr &MI, MachineBasicBlock *BB) const; + MachineBasicBlock *EmitLoweredCatchRet(MachineInstr &MI, + MachineBasicBlock *BB) const; + + MachineBasicBlock *EmitLoweredCatchPad(MachineInstr &MI, + MachineBasicBlock *BB) const; + MachineBasicBlock * EmitInstrWithCustomInserter(MachineInstr &MI, MachineBasicBlock *MBB) const override; @@ -517,6 +523,8 @@ bool functionArgumentNeedsConsecutiveRegisters(Type *Ty, CallingConv::ID CallConv, bool isVarArg) const override; + /// Used for exception handling on Win64. + bool needsFixedCatchObjects() const override; private: /// Keep a pointer to the AArch64Subtarget around so that we can /// make the right decision when generating code for different targets. Index: lib/Target/AArch64/AArch64ISelLowering.cpp =================================================================== --- lib/Target/AArch64/AArch64ISelLowering.cpp +++ lib/Target/AArch64/AArch64ISelLowering.cpp @@ -1273,6 +1273,20 @@ return EndBB; } +MachineBasicBlock *AArch64TargetLowering::EmitLoweredCatchRet( + MachineInstr &MI, MachineBasicBlock *BB) const { + assert(!isAsynchronousEHPersonality(classifyEHPersonality( + BB->getParent()->getFunction().getPersonalityFn())) && + "SEH does not use catchret!"); + return BB; +} + +MachineBasicBlock *AArch64TargetLowering::EmitLoweredCatchPad( + MachineInstr &MI, MachineBasicBlock *BB) const { + MI.eraseFromParent(); + return BB; +} + MachineBasicBlock *AArch64TargetLowering::EmitInstrWithCustomInserter( MachineInstr &MI, MachineBasicBlock *BB) const { switch (MI.getOpcode()) { @@ -1288,6 +1302,11 @@ case TargetOpcode::STACKMAP: case TargetOpcode::PATCHPOINT: return emitPatchPoint(MI, BB); + + case AArch64::CATCHRET: + return EmitLoweredCatchRet(MI, BB); + case AArch64::CATCHPAD: + return EmitLoweredCatchPad(MI, BB); } } @@ -11743,3 +11762,8 @@ MF.getFrameInfo().computeMaxCallFrameSize(MF); TargetLoweringBase::finalizeLowering(MF); } + +// Unlike X86, we let frame lowering assign offsets to all catch objects. +bool AArch64TargetLowering::needsFixedCatchObjects() const { + return false; +} Index: lib/Target/AArch64/AArch64InstrInfo.cpp =================================================================== --- lib/Target/AArch64/AArch64InstrInfo.cpp +++ lib/Target/AArch64/AArch64InstrInfo.cpp @@ -66,7 +66,8 @@ cl::desc("Restrict range of Bcc instructions (DEBUG)")); AArch64InstrInfo::AArch64InstrInfo(const AArch64Subtarget &STI) - : AArch64GenInstrInfo(AArch64::ADJCALLSTACKDOWN, AArch64::ADJCALLSTACKUP), + : AArch64GenInstrInfo(AArch64::ADJCALLSTACKDOWN, AArch64::ADJCALLSTACKUP, + AArch64::CATCHRET), RI(STI.getTargetTriple()), Subtarget(STI) {} /// GetInstSize - Return the number of bytes of code the specified @@ -1657,11 +1658,36 @@ } bool AArch64InstrInfo::expandPostRAPseudo(MachineInstr &MI) const { - if (MI.getOpcode() != TargetOpcode::LOAD_STACK_GUARD) + if (MI.getOpcode() != TargetOpcode::LOAD_STACK_GUARD && + MI.getOpcode() != AArch64::CATCHRET) return false; MachineBasicBlock &MBB = *MI.getParent(); DebugLoc DL = MI.getDebugLoc(); + + if (MI.getOpcode() == AArch64::CATCHRET) { + // Skip to the first instruction before the epilog. + const TargetInstrInfo *TII = + MBB.getParent()->getSubtarget().getInstrInfo(); + MachineBasicBlock *TargetMBB = MI.getOperand(0).getMBB(); + auto MBBI = MachineBasicBlock::iterator(MI); + MachineBasicBlock::iterator FirstEpilogSEH = std::prev(MBBI); + while (FirstEpilogSEH->getFlag(MachineInstr::FrameDestroy) && + FirstEpilogSEH != MBB.begin()) + FirstEpilogSEH = std::prev(FirstEpilogSEH); + if (FirstEpilogSEH != MBB.begin()) + FirstEpilogSEH = std::next(FirstEpilogSEH); + BuildMI(MBB, FirstEpilogSEH, DL, TII->get(AArch64::ADRP)) + .addReg(AArch64::X0) + .addMBB(TargetMBB); + BuildMI(MBB, FirstEpilogSEH, DL, TII->get(AArch64::ADDXri)) + .addReg(AArch64::X0) + .addReg(AArch64::X0) + .addMBB(TargetMBB) + .addImm(0); + return true; + } + unsigned Reg = MI.getOperand(0).getReg(); const GlobalValue *GV = cast((*MI.memoperands_begin())->getValue()); Index: lib/Target/AArch64/AArch64InstrInfo.td =================================================================== --- lib/Target/AArch64/AArch64InstrInfo.td +++ lib/Target/AArch64/AArch64InstrInfo.td @@ -3175,6 +3175,20 @@ def SEH_EpilogEnd : Pseudo<(outs), (ins), []>, Sched<[]>; } +// Pseudo instructions for Windows EH +//===----------------------------------------------------------------------===// +let isTerminator = 1, hasSideEffects = 1, isBarrier = 1, hasCtrlDep = 1, + isCodeGenOnly = 1, isReturn = 1, isEHScopeReturn = 1, isPseudo = 1 in { + def CLEANUPRET : Pseudo<(outs), (ins), [(cleanupret)]>, Sched<[]>; + let usesCustomInserter = 1 in + def CATCHRET : Pseudo<(outs), (ins am_brcond:$dst, am_brcond:$src), [(catchret bb:$dst, bb:$src)]>, + Sched<[]>; +} + +let hasSideEffects = 1, hasCtrlDep = 1, isCodeGenOnly = 1, + usesCustomInserter = 1 in +def CATCHPAD : Pseudo<(outs), (ins), [(catchpad)]>, Sched<[]>; + //===----------------------------------------------------------------------===// // Floating point immediate move. //===----------------------------------------------------------------------===// Index: lib/Target/AArch64/AArch64MCInstLower.cpp =================================================================== --- lib/Target/AArch64/AArch64MCInstLower.cpp +++ lib/Target/AArch64/AArch64MCInstLower.cpp @@ -269,4 +269,17 @@ if (lowerOperand(MO, MCOp)) OutMI.addOperand(MCOp); } + + switch (OutMI.getOpcode()) { + case AArch64::CATCHRET: + OutMI = MCInst(); + OutMI.setOpcode(AArch64::RET); + OutMI.addOperand(MCOperand::createReg(AArch64::LR)); + break; + case AArch64::CLEANUPRET: + OutMI = MCInst(); + OutMI.setOpcode(AArch64::RET); + OutMI.addOperand(MCOperand::createReg(AArch64::LR)); + break; + } } Index: lib/Target/AArch64/AArch64RegisterInfo.h =================================================================== --- lib/Target/AArch64/AArch64RegisterInfo.h +++ lib/Target/AArch64/AArch64RegisterInfo.h @@ -65,6 +65,9 @@ // normal calls, so they need a different mask to represent this. const uint32_t *getTLSCallPreservedMask() const; + // Funclets on ARM64 Windows don't preserve any registers. + const uint32_t *getNoPreservedMask() const override; + /// getThisReturnPreservedMask - Returns a call preserved mask specific to the /// case that 'returned' is on an i64 first argument if the calling convention /// is one that can (partially) model this attribute with a preserved mask Index: lib/Target/AArch64/AArch64RegisterInfo.cpp =================================================================== --- lib/Target/AArch64/AArch64RegisterInfo.cpp +++ lib/Target/AArch64/AArch64RegisterInfo.cpp @@ -161,6 +161,10 @@ *Mask = UpdatedMask; } +const uint32_t *AArch64RegisterInfo::getNoPreservedMask() const { + return CSR_AArch64_NoRegs_RegMask; +} + const uint32_t * AArch64RegisterInfo::getThisReturnPreservedMask(const MachineFunction &MF, CallingConv::ID CC) const { Index: test/CodeGen/AArch64/wineh-try-catch.ll =================================================================== --- /dev/null +++ test/CodeGen/AArch64/wineh-try-catch.ll @@ -0,0 +1,177 @@ +; RUN: llc -o - %s -mtriple=aarch64-windows | FileCheck %s + +; We test the following +; 1) That the unwind help object is created and that its offset from the stack +; pointer on entry is patched into the table fed to __CxxFrameHandler3 +; 2) That the stack update for the catch funclet only includes the callee saved +; registers +; 3) That the locals are accessed using the frame pointer in both the funclet +; and the parent function. + +; The following checks that the unwind help object has -2 stored into it at +; fp - 400 - 256 = fp - 656, which is on-entry sp - 48 + 32 - 656 = +; on-entry sp - 672. We check this offset in the table later on. + +; CHECK-LABEL: "?func@@YAHXZ": +; CHECK: str x28, [sp, #-48]! +; CHECK: stp x19, x20, [sp, #16] +; CHECK: stp x29, x30, [sp, #32] +; CHECK: add x29, sp, #32 +; CHECK: sub sp, sp, #624 +; CHECK: orr x1, xzr, #0xfffffffffffffffe +; CHECK: sub x8, x29, #400 +; CHECK: stur x1, [x8, #-256] + +; Now check that x is stored at fp - 20. We check that this is the same +; location accessed from the funclet to retrieve x. +; CHECK: orr w8, wzr, #0x1 +; CHECK: stur w8, [x29, [[X_OFFSET:#-[1-9][0-9]+]] + +; Check the offset off the frame pointer at which B is located. +; Check the same offset is used to pass the address of B to init2 in the +; funclet. +; CHECK: sub x0, x29, [[B_OFFSET:#[1-9][0-9]+]] +; CHECK: bl "?init@@YAXPEAH@Z" + +; This is the label for the throw that is encoded in the ip2state. +; We are inside the try block, where we make a call to func2 +; CHECK-LABEL: .Ltmp0: +; CHECK: bl "?func2@@YAHXZ + +; CHECK: [[CATCHRETDEST:.LBB0_[0-9]+]]: ; %catchret.dest + +; Check the catch funclet. +; CHECK-LABEL: "?catch$2@?0??func@@YAHXZ@4HA": + +; Check that the stack space is allocated only for the callee saved registers. +; CHECK: str x28, [sp, #-48]! +; CHECK: stp x19, x20, [sp, #16] +; CHECK: stp x29, x30, [sp, #32] +; CHECK: sub x19, x29, #636 + +; Check that there are no further stack updates. +; CHECK-NOT: sub sp, sp + +; Check that the stack address passed to init2 is off the frame pointer, and +; that it matches the address of B in the parent function. +; CHECK: sub x0, x29, [[B_OFFSET]] +; CHECK: bl "?init2@@YAXPEAH@Z" + +; Check that are storing x back to the same location off the frame pointer as in +; the parent function. +; CHECK: stur w8, [x29, [[X_OFFSET]]] + +; Check that the funclet branches back to the catchret destination +; CHECK: adrp x0, .LBB0_3 +; CHECK-NEXT: add x0, x0, [[CATCHRETDEST]] + + +; Now check that the offset of the unwind help object from the stack pointer on +; entry to func is encoded in cppxdata that is passed to __CxxFrameHandler3. As +; computed above, this comes to -672. +; CHECK-LABEL: "$cppxdata$?func@@YAHXZ": +; CHECK-NEXT: .word 429065506 ; MagicNumber +; CHECK-NEXT: .word 2 ; MaxState +; CHECK-NEXT: .word ("$stateUnwindMap$?func@@YAHXZ")@IMGREL ; UnwindMap +; CHECK-NEXT: .word 1 ; NumTryBlocks +; CHECK-NEXT: .word ("$tryMap$?func@@YAHXZ")@IMGREL ; TryBlockMap +; CHECK-NEXT: .word 4 ; IPMapEntries +; CHECK-NEXT: .word ("$ip2state$?func@@YAHXZ")@IMGREL ; IPToStateXData +; CHECK-NEXT: .word -672 ; UnwindHelp + + + +target datalayout = "e-m:w-p:64:64-i32:32-i64:64-i128:128-n32:64-S128" +target triple = "aarch64-unknown-windows-msvc19.11.0" + +%rtti.TypeDescriptor2 = type { i8**, i8*, [3 x i8] } +%eh.CatchableType = type { i32, i32, i32, i32, i32, i32, i32 } +%eh.CatchableTypeArray.1 = type { i32, [1 x i32] } +%eh.ThrowInfo = type { i32, i32, i32, i32 } + +$"??_R0H@8" = comdat any + +$"_CT??_R0H@84" = comdat any + +$_CTA1H = comdat any + +$_TI1H = comdat any + +@"??_7type_info@@6B@" = external constant i8* +@"??_R0H@8" = linkonce_odr global %rtti.TypeDescriptor2 { i8** @"??_7type_info@@6B@", i8* null, [3 x i8] c".H\00" }, comdat +@__ImageBase = external dso_local constant i8 +@"_CT??_R0H@84" = linkonce_odr unnamed_addr constant %eh.CatchableType { i32 1, i32 trunc (i64 sub nuw nsw (i64 ptrtoint (%rtti.TypeDescriptor2* @"??_R0H@8" to i64), i64 ptrtoint (i8* @__ImageBase to i64)) to i32), i32 0, i32 -1, i32 0, i32 4, i32 0 }, section ".xdata", comdat +@_CTA1H = linkonce_odr unnamed_addr constant %eh.CatchableTypeArray.1 { i32 1, [1 x i32] [i32 trunc (i64 sub nuw nsw (i64 ptrtoint (%eh.CatchableType* @"_CT??_R0H@84" to i64), i64 ptrtoint (i8* @__ImageBase to i64)) to i32)] }, section ".xdata", comdat +@_TI1H = linkonce_odr unnamed_addr constant %eh.ThrowInfo { i32 0, i32 0, i32 0, i32 trunc (i64 sub nuw nsw (i64 ptrtoint (%eh.CatchableTypeArray.1* @_CTA1H to i64), i64 ptrtoint (i8* @__ImageBase to i64)) to i32) }, section ".xdata", comdat + +; Function Attrs: noinline optnone +define dso_local i32 @"?func@@YAHXZ"() #0 personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) { +entry: + %B = alloca [50 x i32], align 4 + %x = alloca i32, align 4 + %tmp = alloca i32, align 4 + %i = alloca i32, align 4 + %C = alloca [100 x i32], align 4 + store i32 1, i32* %x, align 4 + %arraydecay = getelementptr inbounds [50 x i32], [50 x i32]* %B, i32 0, i32 0 + call void @"?init@@YAXPEAH@Z"(i32* %arraydecay) + %call = invoke i32 @"?func2@@YAHXZ"() + to label %invoke.cont unwind label %catch.dispatch + +invoke.cont: ; preds = %entry + store i32 %call, i32* %tmp, align 4 + %0 = bitcast i32* %tmp to i8* + invoke void @_CxxThrowException(i8* %0, %eh.ThrowInfo* @_TI1H) #2 + to label %unreachable unwind label %catch.dispatch + +catch.dispatch: ; preds = %invoke.cont, %entry + %1 = catchswitch within none [label %catch] unwind to caller + +catch: ; preds = %catch.dispatch + %2 = catchpad within %1 [%rtti.TypeDescriptor2* @"??_R0H@8", i32 0, i32* %i] + %arraydecay1 = getelementptr inbounds [100 x i32], [100 x i32]* %C, i32 0, i32 0 + call void @"?init@@YAXPEAH@Z"(i32* %arraydecay1) [ "funclet"(token %2) ] + %arraydecay2 = getelementptr inbounds [50 x i32], [50 x i32]* %B, i32 0, i32 0 + call void @"?init2@@YAXPEAH@Z"(i32* %arraydecay2) [ "funclet"(token %2) ] + %3 = load i32, i32* %i, align 4 + %idxprom = sext i32 %3 to i64 + %arrayidx = getelementptr inbounds [50 x i32], [50 x i32]* %B, i64 0, i64 %idxprom + %4 = load i32, i32* %arrayidx, align 4 + %5 = load i32, i32* %i, align 4 + %idxprom3 = sext i32 %5 to i64 + %arrayidx4 = getelementptr inbounds [100 x i32], [100 x i32]* %C, i64 0, i64 %idxprom3 + %6 = load i32, i32* %arrayidx4, align 4 + %add = add nsw i32 %4, %6 + %7 = load i32, i32* %i, align 4 + %8 = load i32, i32* %i, align 4 + %mul = mul nsw i32 %7, %8 + %add5 = add nsw i32 %add, %mul + store i32 %add5, i32* %x, align 4 + catchret from %2 to label %catchret.dest + +catchret.dest: ; preds = %catch + br label %try.cont + +try.cont: ; preds = %catchret.dest + %arrayidx6 = getelementptr inbounds [50 x i32], [50 x i32]* %B, i64 0, i64 2 + %9 = load i32, i32* %arrayidx6, align 4 + %10 = load i32, i32* %x, align 4 + %add7 = add nsw i32 %9, %10 + ret i32 %add7 + +unreachable: ; preds = %invoke.cont + unreachable +} + +declare dso_local void @"?init@@YAXPEAH@Z"(i32*) + +declare dso_local i32 @"?func2@@YAHXZ"() + +declare dso_local i32 @__CxxFrameHandler3(...) + +declare dllimport void @_CxxThrowException(i8*, %eh.ThrowInfo*) + +declare dso_local void @"?init2@@YAXPEAH@Z"(i32*) + +attributes #0 = { noinline optnone } +attributes #2 = { noreturn }