[SEH] Implement GetExceptionCode in __except blocks

This introduces an intrinsic called llvm.eh.exceptioncode. It is lowered
by copying the EAX value live into whatever basic block it is called
from. Obviously, this only works if you insert it late during codegen,
because otherwise mid-level passes might reschedule it.

llvm-svn: 235768
This commit is contained in:
Reid Kleckner 2015-04-24 20:25:05 +00:00
parent 75ef0c09d0
commit cfbfe6f29c
8 changed files with 108 additions and 33 deletions

View File

@ -221,7 +221,7 @@ public:
int getArgumentFrameIndex(const Argument *A);
private:
void addSEHHandlersForLPads();
void addSEHHandlersForLPads(ArrayRef<const LandingPadInst *> LPads);
/// LiveOutRegInfo - Information about live out vregs.
IndexedMap<LiveOutInfo, VirtReg2IndexFunctor> LiveOutRegInfo;

View File

@ -421,6 +421,8 @@ def int_eh_endcatch : Intrinsic<[], []>;
// Represents the list of actions to take when an exception is thrown.
def int_eh_actions : Intrinsic<[llvm_ptr_ty], [llvm_vararg_ty], []>;
def int_eh_exceptioncode : Intrinsic<[llvm_i32_ty], []>;
// __builtin_unwind_init is an undocumented GCC intrinsic that causes all
// callee-saved registers to be saved and restored (regardless of whether they
// are used) in the calling function. It is used by libgcc_eh.

View File

@ -271,40 +271,49 @@ void FunctionLoweringInfo::set(const Function &fn, MachineFunction &mf,
}
// Mark landing pad blocks.
const LandingPadInst *LP = nullptr;
SmallVector<const LandingPadInst *, 4> LPads;
for (BB = Fn->begin(); BB != EB; ++BB) {
if (const auto *Invoke = dyn_cast<InvokeInst>(BB->getTerminator()))
MBBMap[Invoke->getSuccessor(1)]->setIsLandingPad();
if (BB->isLandingPad())
LP = BB->getLandingPadInst();
LPads.push_back(BB->getLandingPadInst());
}
// Calculate EH numbers for MSVC C++ EH and save SEH handlers if necessary.
// If this is an MSVC EH personality, we need to do a bit more work.
EHPersonality Personality = EHPersonality::Unknown;
if (LP)
Personality = classifyEHPersonality(LP->getPersonalityFn());
if (!LPads.empty())
Personality = classifyEHPersonality(LPads.back()->getPersonalityFn());
if (!isMSVCEHPersonality(Personality))
return;
WinEHFuncInfo *EHInfo = nullptr;
if (Personality == EHPersonality::MSVC_Win64SEH) {
addSEHHandlersForLPads();
addSEHHandlersForLPads(LPads);
} else if (Personality == EHPersonality::MSVC_CXX) {
const Function *WinEHParentFn = MMI.getWinEHParent(&fn);
WinEHFuncInfo &FI = MMI.getWinEHFuncInfo(WinEHParentFn);
if (FI.LandingPadStateMap.empty()) {
WinEHNumbering Num(FI);
EHInfo = &MMI.getWinEHFuncInfo(WinEHParentFn);
if (EHInfo->LandingPadStateMap.empty()) {
WinEHNumbering Num(*EHInfo);
Num.calculateStateNumbers(*WinEHParentFn);
// Pop everything on the handler stack.
Num.processCallSite(None, ImmutableCallSite());
}
// Copy the state numbers to LandingPadInfo for the current function, which
// could be a handler or the parent.
for (const LandingPadInst *LP : LPads) {
MachineBasicBlock *LPadMBB = MBBMap[LP->getParent()];
MMI.addWinEHState(LPadMBB, EHInfo->LandingPadStateMap[LP]);
}
}
}
void FunctionLoweringInfo::addSEHHandlersForLPads() {
void FunctionLoweringInfo::addSEHHandlersForLPads(
ArrayRef<const LandingPadInst *> LPads) {
MachineModuleInfo &MMI = MF->getMMI();
// Iterate over all landing pads with llvm.eh.actions calls.
for (const BasicBlock &BB : *Fn) {
const LandingPadInst *LP = BB.getLandingPadInst();
if (!LP)
continue;
for (const LandingPadInst *LP : LPads) {
const IntrinsicInst *ActionsCall =
dyn_cast<IntrinsicInst>(LP->getNextNode());
if (!ActionsCall ||

View File

@ -4817,6 +4817,18 @@ SelectionDAGBuilder::visitIntrinsicCall(const CallInst &I, unsigned Intrinsic) {
case Intrinsic::eh_begincatch:
case Intrinsic::eh_endcatch:
llvm_unreachable("begin/end catch intrinsics not lowered in codegen");
case Intrinsic::eh_exceptioncode: {
unsigned Reg = TLI.getExceptionPointerRegister();
assert(Reg && "cannot get exception code on this platform");
MVT PtrVT = TLI.getPointerTy();
const TargetRegisterClass *PtrRC = TLI.getRegClassFor(PtrVT);
unsigned VReg = FuncInfo.MBB->addLiveIn(Reg, PtrRC);
SDValue N =
DAG.getCopyFromReg(DAG.getEntryNode(), getCurSDLoc(), VReg, PtrVT);
N = DAG.getZExtOrTrunc(N, getCurSDLoc(), MVT::i32);
setValue(&I, N);
return nullptr;
}
}
}

View File

@ -961,12 +961,6 @@ bool SelectionDAGISel::PrepareEHLandingPad() {
for (MachineBasicBlock *InvokeBB : InvokeBBs)
InvokeBB->removeSuccessor(MBB);
// Transfer EH state number assigned to the IR block to the MBB.
if (Personality == EHPersonality::MSVC_CXX) {
WinEHFuncInfo &FI = MF->getMMI().getWinEHFuncInfo(MF->getFunction());
MF->getMMI().addWinEHState(MBB, FI.LandingPadStateMap[LPadInst]);
}
// Don't select instructions for the landingpad.
return false;
}

View File

@ -71,7 +71,7 @@ class WinEHPrepare : public FunctionPass {
public:
static char ID; // Pass identification, replacement for typeid.
WinEHPrepare(const TargetMachine *TM = nullptr)
: FunctionPass(ID), DT(nullptr) {}
: FunctionPass(ID), DT(nullptr), SEHExceptionCodeSlot(nullptr) {}
bool runOnFunction(Function &Fn) override;
@ -133,6 +133,8 @@ private:
// outlined into a handler. This is done after all handlers have been
// outlined but before the outlined code is pruned from the parent function.
DenseMap<const BasicBlock *, BasicBlock *> LPadTargetBlocks;
AllocaInst *SEHExceptionCodeSlot;
};
class WinEHFrameVariableMaterializer : public ValueMaterializer {
@ -628,6 +630,13 @@ bool WinEHPrepare::prepareExceptionHandlers(
Type *Int32Type = Type::getInt32Ty(Context);
Function *ActionIntrin = Intrinsic::getDeclaration(M, Intrinsic::eh_actions);
if (isAsynchronousEHPersonality(Personality)) {
// FIXME: Switch the ehptr type to i32 and then switch this.
SEHExceptionCodeSlot =
new AllocaInst(Int8PtrType, nullptr, "seh_exception_code",
F.getEntryBlock().getFirstInsertionPt());
}
for (LandingPadInst *LPad : LPads) {
// Look for evidence that this landingpad has already been processed.
bool LPadHasActionList = false;
@ -680,23 +689,48 @@ bool WinEHPrepare::prepareExceptionHandlers(
// Replace all extracted values with undef and ultimately replace the
// landingpad with undef.
// FIXME: This doesn't handle SEH GetExceptionCode(). For now, we just give
// out undef until we figure out the codegen support.
SmallVector<Instruction *, 4> Extracts;
SmallVector<Instruction *, 4> SEHCodeUses;
SmallVector<Instruction *, 4> EHUndefs;
for (User *U : LPad->users()) {
auto *E = dyn_cast<ExtractValueInst>(U);
if (!E)
continue;
assert(E->getNumIndices() == 1 &&
"Unexpected operation: extracting both landing pad values");
Extracts.push_back(E);
unsigned Idx = *E->idx_begin();
assert((Idx == 0 || Idx == 1) && "unexpected index");
if (Idx == 0 && isAsynchronousEHPersonality(Personality))
SEHCodeUses.push_back(E);
else
EHUndefs.push_back(E);
}
for (Instruction *E : Extracts) {
for (Instruction *E : EHUndefs) {
E->replaceAllUsesWith(UndefValue::get(E->getType()));
E->eraseFromParent();
}
LPad->replaceAllUsesWith(UndefValue::get(LPad->getType()));
// Rewrite uses of the exception pointer to loads of an alloca.
for (Instruction *E : SEHCodeUses) {
SmallVector<Use *, 4> Uses;
for (Use &U : E->uses())
Uses.push_back(&U);
for (Use *U : Uses) {
auto *I = cast<Instruction>(U->getUser());
if (isa<ResumeInst>(I))
continue;
LoadInst *LI;
if (auto *Phi = dyn_cast<PHINode>(I))
LI = new LoadInst(SEHExceptionCodeSlot, "sehcode", false,
Phi->getIncomingBlock(*U));
else
LI = new LoadInst(SEHExceptionCodeSlot, "sehcode", false, I);
U->set(LI);
}
E->replaceAllUsesWith(UndefValue::get(E->getType()));
E->eraseFromParent();
}
// Add a call to describe the actions for this landing pad.
std::vector<Value *> ActionArgs;
for (ActionHandler *Action : Actions) {
@ -820,6 +854,13 @@ bool WinEHPrepare::prepareExceptionHandlers(
Builder.SetInsertPoint(&F.getEntryBlock().back());
Builder.CreateCall(FrameEscapeFn, AllocasToEscape);
if (SEHExceptionCodeSlot) {
if (SEHExceptionCodeSlot->hasNUses(0))
SEHExceptionCodeSlot->eraseFromParent();
else
PromoteMemToReg(SEHExceptionCodeSlot, *DT);
}
// Clean up the handler action maps we created for this function
DeleteContainerSeconds(CatchHandlerMap);
CatchHandlerMap.clear();
@ -1193,6 +1234,7 @@ bool WinEHPrepare::outlineHandler(ActionHandler *Action, Function *SrcFn,
/// target.
void WinEHPrepare::processSEHCatchHandler(CatchHandler *CatchAction,
BasicBlock *StartBB) {
LLVMContext &Context = StartBB->getContext();
BasicBlock *HandlerBB;
BasicBlock *NextBB;
Constant *Selector;
@ -1210,6 +1252,12 @@ void WinEHPrepare::processSEHCatchHandler(CatchHandler *CatchAction,
HandlerBB =
StartBB->splitBasicBlock(StartBB->getFirstInsertionPt(), "catch.all");
}
IRBuilder<> Builder(HandlerBB->getFirstInsertionPt());
Function *EHCodeFn = Intrinsic::getDeclaration(
StartBB->getParent()->getParent(), Intrinsic::eh_exceptioncode);
Value *Code = Builder.CreateCall(EHCodeFn, "sehcode");
Code = Builder.CreateIntToPtr(Code, SEHExceptionCodeSlot->getAllocatedType());
Builder.CreateStore(Code, SEHExceptionCodeSlot);
CatchAction->setHandlerBlockOrFunc(BlockAddress::get(HandlerBB));
TinyPtrVector<BasicBlock *> Targets(HandlerBB);
CatchAction->setReturnTargets(Targets);

View File

@ -55,9 +55,8 @@ eh.resume:
; CHECK-NEXT: indirectbr {{.*}} [label %__except]
;
; CHECK: __except:
; FIXME: This should not be undef, it should be the new landingpad value, which
; should ultimately lower down to eax.
; CHECK: invoke void @might_crash(i8* undef)
; CHECK: call i32 @llvm.eh.exceptioncode()
; CHECK: invoke void @might_crash(i8* %{{.*}})
; CHECK: landingpad { i8*, i32 }
; CHECK-NEXT: cleanup
; CHECK-NEXT: call i8* (...) @llvm.eh.actions(i32 0, void (i8*, i8*)* @resume_phi.cleanup)

View File

@ -1,10 +1,10 @@
; RUN: llc -mtriple=x86_64-windows-msvc < %s | FileCheck %s
@str = internal unnamed_addr constant [10 x i8] c"recovered\00", align 1
@str = linkonce_odr unnamed_addr constant [27 x i8] c"GetExceptionCode(): 0x%lx\0A\00", align 1
declare i32 @__C_specific_handler(...)
declare void @crash()
declare i32 @puts(i8*)
declare i32 @printf(i8* nocapture readonly, ...) nounwind
define i32 @main() {
entry:
@ -14,7 +14,10 @@ entry:
lpad:
%0 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__C_specific_handler to i8*)
catch i8* null
call i32 @puts(i8* getelementptr inbounds ([10 x i8], [10 x i8]* @str, i64 0, i64 0))
%1 = extractvalue { i8*, i32 } %0, 0
%2 = ptrtoint i8* %1 to i64
%3 = trunc i64 %2 to i32
call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([27 x i8], [27 x i8]* @str, i64 0, i64 0), i32 %3)
br label %__try.cont
__try.cont:
@ -24,7 +27,15 @@ eh.resume:
resume { i8*, i32 } %0
}
; Check that we can get the exception code from eax to the printf.
; CHECK-LABEL: main:
; CHECK: retq
; CHECK: # Block address taken
; CHECK: leaq str(%rip), %rcx
; CHECK: movl %eax, %edx
; CHECK: callq printf
; CHECK: .seh_handlerdata
; CHECK-NEXT: .long 1
; CHECK-NEXT: .Ltmp{{[0-9]+}}@IMGREL