Index: include/llvm/CodeGen/MachineBasicBlock.h =================================================================== --- include/llvm/CodeGen/MachineBasicBlock.h +++ include/llvm/CodeGen/MachineBasicBlock.h @@ -376,6 +376,12 @@ return make_range(livein_begin(), livein_end()); } + /// Get the clobber mask for the basic block begin and ends. Funclets use this + /// to prevent register allocation across funclet transitions. A null return + /// means no mask. + const uint32_t *getBeginClobberMask(const TargetRegisterInfo *TRI) const; + const uint32_t *getEndClobberMask(const TargetRegisterInfo *TRI) const; + /// Return alignment of the basic block. The alignment is specified as /// log2(bytes). unsigned getAlignment() const { return Alignment; } Index: include/llvm/Target/TargetRegisterInfo.h =================================================================== --- include/llvm/Target/TargetRegisterInfo.h +++ include/llvm/Target/TargetRegisterInfo.h @@ -475,6 +475,11 @@ return nullptr; } + // Return a register mask that clobbers everything. + virtual const uint32_t *getNoPreservedMask() const { + return nullptr; + } + /// Return all the call-preserved register masks defined for this target. virtual ArrayRef getRegMasks() const = 0; virtual ArrayRef getRegMaskNames() const = 0; Index: lib/CodeGen/LiveIntervalAnalysis.cpp =================================================================== --- lib/CodeGen/LiveIntervalAnalysis.cpp +++ lib/CodeGen/LiveIntervalAnalysis.cpp @@ -225,6 +225,13 @@ for (MachineBasicBlock &MBB : *MF) { std::pair &RMB = RegMaskBlocks[MBB.getNumber()]; RMB.first = RegMaskSlots.size(); + + // Some block starts, such as EH funclets, create masks. + if (const uint32_t *Mask = MBB.getBeginClobberMask(TRI)) { + RegMaskSlots.push_back(Indexes->getMBBStartIdx(&MBB)); + RegMaskBits.push_back(Mask); + } + for (MachineInstr &MI : MBB) { for (const MachineOperand &MO : MI.operands()) { if (!MO.isRegMask()) @@ -233,6 +240,13 @@ RegMaskBits.push_back(MO.getRegMask()); } } + + // Some block ends, such as funclet returns, create masks. + if (const uint32_t *Mask = MBB.getEndClobberMask(TRI)) { + RegMaskSlots.push_back(Indexes->getMBBEndIdx(&MBB)); + RegMaskBits.push_back(Mask); + } + // Compute the number of register mask instructions in this block. RMB.second = RegMaskSlots.size() - RMB.first; } Index: lib/CodeGen/MachineBasicBlock.cpp =================================================================== --- lib/CodeGen/MachineBasicBlock.cpp +++ lib/CodeGen/MachineBasicBlock.cpp @@ -1301,3 +1301,27 @@ // At this point we have no idea of the liveness of the register. return LQR_Unknown; } + +const uint32_t * +MachineBasicBlock::getBeginClobberMask(const TargetRegisterInfo *TRI) const { + const uint32_t *Mask = nullptr; + // EH funclet entry does not preserve any registers. + if (isEHFuncletEntry()) { + Mask = TRI->getNoPreservedMask(); + assert(Mask && "target does not expose no preserved mask"); + } + return Mask; +} + +const uint32_t * +MachineBasicBlock::getEndClobberMask(const TargetRegisterInfo *TRI) const { + const uint32_t *Mask = nullptr; + // If we see a return block with successors, this must be a funclet return, + // which does not preserve any registers. If there are no successors, we don't + // care what kind of return it is, putting a mask after it is a no-op. + if (isEHFuncletEntry()) { + Mask = TRI->getNoPreservedMask(); + assert(Mask && "target does not expose no preserved mask"); + } + return Mask; +} Index: lib/Target/ARM/ARMBaseRegisterInfo.h =================================================================== --- lib/Target/ARM/ARMBaseRegisterInfo.h +++ lib/Target/ARM/ARMBaseRegisterInfo.h @@ -94,7 +94,7 @@ const MCPhysReg *getCalleeSavedRegs(const MachineFunction *MF) const override; const uint32_t *getCallPreservedMask(const MachineFunction &MF, CallingConv::ID) const override; - const uint32_t *getNoPreservedMask() const; + const uint32_t *getNoPreservedMask() const override; /// getThisReturnPreservedMask - Returns a call preserved mask specific to the /// case that 'returned' is on an i32 first argument if the calling convention Index: lib/Target/PowerPC/PPCRegisterInfo.h =================================================================== --- lib/Target/PowerPC/PPCRegisterInfo.h +++ lib/Target/PowerPC/PPCRegisterInfo.h @@ -77,7 +77,7 @@ const MCPhysReg *getCalleeSavedRegs(const MachineFunction *MF) const override; const uint32_t *getCallPreservedMask(const MachineFunction &MF, CallingConv::ID CC) const override; - const uint32_t *getNoPreservedMask() const; + const uint32_t *getNoPreservedMask() const override; void adjustStackMapLiveOutMask(uint32_t *Mask) const override; Index: lib/Target/X86/X86RegisterInfo.h =================================================================== --- lib/Target/X86/X86RegisterInfo.h +++ lib/Target/X86/X86RegisterInfo.h @@ -96,7 +96,7 @@ getCalleeSavedRegs(const MachineFunction* MF) const override; const uint32_t *getCallPreservedMask(const MachineFunction &MF, CallingConv::ID) const override; - const uint32_t *getNoPreservedMask() const; + const uint32_t *getNoPreservedMask() const override; /// getReservedRegs - Returns a bitset indexed by physical register number /// indicating if a register is a special register that has particular uses and Index: test/CodeGen/X86/catchpad-regmask.ll =================================================================== --- /dev/null +++ test/CodeGen/X86/catchpad-regmask.ll @@ -0,0 +1,150 @@ +; RUN: llc < %s | FileCheck %s + +; Based on this code: +; +; extern "C" int array[4]; +; extern "C" void global_array(int idx1, int idx2, int idx3) { +; try { +; array[idx1] = 111; +; throw; +; } catch (...) { +; array[idx2] = 222; +; } +; array[idx3] = 333; +; } +; extern "C" __declspec(dllimport) int imported; +; extern "C" void access_imported() { +; try { +; imported = 111; +; throw; +; } catch (...) { +; imported = 222; +; } +; imported = 333; +; } + +target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc18.0.0" + +%eh.ThrowInfo = type { i32, i32, i32, i32 } + +@array = external global [4 x i32], align 16 +@imported = external dllimport global i32, align 4 + +; Function Attrs: uwtable +define void @global_array(i32 %idx1, i32 %idx2, i32 %idx3) #0 personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) { +entry: + %idxprom = sext i32 %idx1 to i64 + %arrayidx = getelementptr inbounds [4 x i32], [4 x i32]* @array, i64 0, i64 %idxprom + store i32 111, i32* %arrayidx, align 4, !tbaa !2 + invoke void @_CxxThrowException(i8* null, %eh.ThrowInfo* null) #1 + to label %unreachable unwind label %catch.dispatch + +catch.dispatch: ; preds = %entry + %0 = catchpad [i8* null, i32 64, i8* null] + to label %catch unwind label %catchendblock + +catch: ; preds = %catch.dispatch + %idxprom1 = sext i32 %idx2 to i64 + %arrayidx2 = getelementptr inbounds [4 x i32], [4 x i32]* @array, i64 0, i64 %idxprom1 + store i32 222, i32* %arrayidx2, align 4, !tbaa !2 + catchret %0 to label %try.cont + +try.cont: ; preds = %catch + %idxprom3 = sext i32 %idx3 to i64 + %arrayidx4 = getelementptr inbounds [4 x i32], [4 x i32]* @array, i64 0, i64 %idxprom3 + store i32 333, i32* %arrayidx4, align 4, !tbaa !2 + ret void + +catchendblock: ; preds = %catch.dispatch + catchendpad unwind to caller + +unreachable: ; preds = %entry + unreachable +} + +; CHECK-LABEL: global_array: # @global_array +; CHECK: pushq %rbp +; First array access +; CHECK: movslq %ecx, %[[idx:[^ ]*]] +; CHECK: leaq array(%rip), %[[base:[^ ]*]] +; CHECK: movl $111, (%[[base]],%[[idx]],4) +; Might throw an exception and return to below... +; CHECK: callq _CxxThrowException +; Third array access must remat the address of array +; CHECK: movslq {{.*}}, %[[idx:[^ ]*]] +; CHECK: leaq array(%rip), %[[base:[^ ]*]] +; CHECK: movl $333, (%[[base]],%[[idx]],4) +; CHECK: popq %rbp +; CHECK: retq + +; CHECK: "?catch$2@?0?global_array@4HA": +; CHECK: pushq %rbp +; CHECK: movslq {{.*}}, %[[idx:[^ ]*]] +; CHECK: leaq array(%rip), %[[base:[^ ]*]] +; CHECK: movl $222, (%[[base]],%[[idx]],4) +; CHECK: popq %rbp +; CHECK: retq # CATCHRET + +declare void @_CxxThrowException(i8*, %eh.ThrowInfo*) + +declare i32 @__CxxFrameHandler3(...) + +; Function Attrs: uwtable +define void @access_imported() #0 personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) { +entry: + store i32 111, i32* @imported, align 4, !tbaa !2 + invoke void @_CxxThrowException(i8* null, %eh.ThrowInfo* null) #1 + to label %unreachable unwind label %catch.dispatch + +catch.dispatch: ; preds = %entry + %0 = catchpad [i8* null, i32 64, i8* null] + to label %catch unwind label %catchendblock + +catch: ; preds = %catch.dispatch + store i32 222, i32* @imported, align 4, !tbaa !2 + catchret %0 to label %try.cont + +try.cont: ; preds = %catch + store i32 333, i32* @imported, align 4, !tbaa !2 + ret void + +catchendblock: ; preds = %catch.dispatch + catchendpad unwind to caller + +unreachable: ; preds = %entry + unreachable +} + +; CHECK-LABEL: access_imported: # @access_imported +; CHECK: pushq %rbp +; CHECK: movq __imp_imported(%rip), %[[base:[^ ]*]] +; CHECK: movl $111, (%[[base]]) +; Might throw an exception and return to below... +; CHECK: callq _CxxThrowException +; Third access must reload the address of imported +; CHECK: movq __imp_imported(%rip), %[[base:[^ ]*]] +; CHECK: movl $333, (%[[base]]) +; CHECK: popq %rbp +; CHECK: retq + +; CHECK: "?catch$2@?0?access_imported@4HA": +; CHECK: pushq %rbp +; CHECK: movq __imp_imported(%rip), %[[base:[^ ]*]] +; CHECK: movl $222, (%[[base]]) +; CHECK: popq %rbp +; CHECK: retq # CATCHRET + + +attributes #0 = { uwtable "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { noreturn } + +!llvm.module.flags = !{!0} +!llvm.ident = !{!1} + +!0 = !{i32 1, !"PIC Level", i32 2} +!1 = !{!"clang version 3.8.0 "} +!2 = !{!3, !3, i64 0} +!3 = !{!"int", !4, i64 0} +!4 = !{!"omnipotent char", !5, i64 0} +!5 = !{!"Simple C/C++ TBAA"} Index: test/CodeGen/X86/cleanuppad-large-codemodel.ll =================================================================== --- /dev/null +++ test/CodeGen/X86/cleanuppad-large-codemodel.ll @@ -0,0 +1,27 @@ +; RUN: llc -mtriple=x86_64-pc-windows-msvc -code-model=large -o - < %s | FileCheck %s + +declare i32 @__CxxFrameHandler3(...) + +declare void @bar() + +define void @foo() personality i32 (...)* @__CxxFrameHandler3 { +entry: + invoke void @bar() + to label %exit unwind label %cleanup +cleanup: + %c = cleanuppad [] + call void @bar() + cleanupret %c unwind to caller +exit: + ret void +} + +; CHECK: foo: # @foo +; CHECK: movabsq $bar, %[[reg:[^ ]*]] +; CHECK: callq *%[[reg]] +; CHECK: retq + +; CHECK: "?dtor$2@?0?foo@4HA": +; CHECK: movabsq $bar, %[[reg:[^ ]*]] +; CHECK: callq *%[[reg]] +; CHECK: retq # CLEANUPRET