CodeGen support for x86_64 SEH catch handlers in LLVM

This adds handling for ExceptionHandling::MSVC, used by the
x86_64-pc-windows-msvc triple. It assumes that filter functions have
already been outlined in either the frontend or the backend. Filter
functions are used in place of the landingpad catch clause type info
operands. In catch clause order, the first filter to return true will
catch the exception.

The C specific handler table expects the landing pad to be split into
one block per handler, but LLVM IR uses a single landing pad for all
possible unwind actions. This patch papers over the mismatch by
synthesizing single instruction BBs for every catch clause to fill in
the EH selector that the landing pad block expects.

Missing functionality:
- Accessing data in the parent frame from outlined filters
- Cleanups (from __finally) are unsupported, as they will require
  outlining and parent frame access
- Filter clauses are unsupported, as there's no clear analogue in SEH

In other words, this is the minimal set of changes needed to write IR to
catch arbitrary exceptions and resume normal execution.

Reviewers: majnemer

Differential Revision: http://reviews.llvm.org/D6300

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@225904 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Reid Kleckner 2015-01-14 01:05:27 +00:00
parent fbf153aebb
commit 504fa89c8e
12 changed files with 653 additions and 18 deletions

View File

@ -66,6 +66,7 @@ struct LandingPadInfo {
MachineBasicBlock *LandingPadBlock; // Landing pad block.
SmallVector<MCSymbol*, 1> BeginLabels; // Labels prior to invoke.
SmallVector<MCSymbol*, 1> EndLabels; // Labels after invoke.
SmallVector<MCSymbol*, 1> ClauseLabels; // Labels for each clause.
MCSymbol *LandingPadLabel; // Label at beginning of landing pad.
const Function *Personality; // Personality function.
std::vector<int> TypeIds; // List of type ids (filters negative)
@ -330,6 +331,11 @@ public:
///
void addCleanup(MachineBasicBlock *LandingPad);
/// Add a clause for a landing pad. Returns a new label for the clause. This
/// is used by EH schemes that have more than one landing pad. In this case,
/// each clause gets its own basic block.
MCSymbol *addClauseForLandingPad(MachineBasicBlock *LandingPad);
/// getTypeIDFor - Return the type id for the specified typeinfo. This is
/// function wide.
unsigned getTypeIDFor(const GlobalValue *TI);

View File

@ -121,7 +121,8 @@ computeActionsTable(const SmallVectorImpl<const LandingPadInfo*> &LandingPads,
for (unsigned J = NumShared, M = TypeIds.size(); J != M; ++J) {
int TypeID = TypeIds[J];
assert(-1 - TypeID < (int)FilterOffsets.size() && "Unknown filter id!");
int ValueForTypeID = TypeID < 0 ? FilterOffsets[-1 - TypeID] : TypeID;
int ValueForTypeID =
isFilterEHSelector(TypeID) ? FilterOffsets[-1 - TypeID] : TypeID;
unsigned SizeTypeID = getSLEB128Size(ValueForTypeID);
int NextAction = SizeAction ? -(SizeAction + SizeTypeID) : 0;
@ -269,14 +270,14 @@ computeCallSiteTable(SmallVectorImpl<CallSiteEntry> &CallSites,
CallSiteEntry Site = {
BeginLabel,
LastLabel,
LandingPad->LandingPadLabel,
LandingPad,
FirstActions[P.PadIndex]
};
// Try to merge with the previous call-site. SJLJ doesn't do this
if (PreviousIsInvoke && !IsSJLJ) {
CallSiteEntry &Prev = CallSites.back();
if (Site.PadLabel == Prev.PadLabel && Site.Action == Prev.Action) {
if (Site.LPad == Prev.LPad && Site.Action == Prev.Action) {
// Extend the range of the previous entry.
Prev.EndLabel = Site.EndLabel;
continue;
@ -576,15 +577,15 @@ void EHStreamer::emitExceptionTable() {
// Offset of the landing pad, counted in 16-byte bundles relative to the
// @LPStart address.
if (!S.PadLabel) {
if (!S.LPad) {
if (VerboseAsm)
Asm->OutStreamer.AddComment(" has no landing pad");
Asm->OutStreamer.EmitIntValue(0, 4/*size*/);
} else {
if (VerboseAsm)
Asm->OutStreamer.AddComment(Twine(" jumps to ") +
S.PadLabel->getName());
Asm->EmitLabelDifference(S.PadLabel, EHFuncBeginSym, 4);
S.LPad->LandingPadLabel->getName());
Asm->EmitLabelDifference(S.LPad->LandingPadLabel, EHFuncBeginSym, 4);
}
// Offset of the first associated action record, relative to the start of
@ -681,7 +682,7 @@ void EHStreamer::emitTypeInfos(unsigned TTypeEncoding) {
unsigned TypeID = *I;
if (VerboseAsm) {
--Entry;
if (TypeID != 0)
if (isFilterEHSelector(TypeID))
Asm->OutStreamer.AddComment("FilterInfo " + Twine(Entry));
}

View File

@ -23,6 +23,8 @@ class MachineModuleInfo;
class MachineInstr;
class MachineFunction;
class AsmPrinter;
class MCSymbol;
class MCSymbolRefExpr;
template <typename T>
class SmallVectorImpl;
@ -60,11 +62,11 @@ protected:
/// Structure describing an entry in the call-site table.
struct CallSiteEntry {
// The 'try-range' is BeginLabel .. EndLabel.
MCSymbol *BeginLabel; // zero indicates the start of the function.
MCSymbol *EndLabel; // zero indicates the end of the function.
MCSymbol *BeginLabel; // Null indicates the start of the function.
MCSymbol *EndLabel; // Null indicates the end of the function.
// The landing pad starts at PadLabel.
MCSymbol *PadLabel; // zero indicates that there is no landing pad.
// LPad contains the landing pad start labels.
const LandingPadInfo *LPad; // Null indicates that there is no landing pad.
unsigned Action;
};
@ -112,6 +114,13 @@ protected:
virtual void emitTypeInfos(unsigned TTypeEncoding);
// Helpers for for identifying what kind of clause an EH typeid or selector
// corresponds to. Negative selectors are for filter clauses, the zero
// selector is for cleanups, and positive selectors are for catch clauses.
static bool isFilterEHSelector(int Selector) { return Selector < 0; }
static bool isCleanupEHSelector(int Selector) { return Selector == 0; }
static bool isCatchEHSelector(int Selector) { return Selector > 0; }
public:
EHStreamer(AsmPrinter *A);
virtual ~EHStreamer();

View File

@ -99,9 +99,156 @@ void Win64Exception::endFunction(const MachineFunction *) {
if (shouldEmitPersonality) {
Asm->OutStreamer.PushSection();
// Emit an UNWIND_INFO struct describing the prologue.
Asm->OutStreamer.EmitWinEHHandlerData();
emitExceptionTable();
// Emit either MSVC-compatible tables or the usual Itanium-style LSDA after
// the UNWIND_INFO struct.
if (Asm->MAI->getExceptionHandlingType() == ExceptionHandling::MSVC) {
const Function *Per = MMI->getPersonalities()[MMI->getPersonalityIndex()];
if (Per->getName() == "__C_specific_handler")
emitCSpecificHandlerTable();
else
report_fatal_error(Twine("unexpected personality function: ") +
Per->getName());
} else {
emitExceptionTable();
}
Asm->OutStreamer.PopSection();
}
Asm->OutStreamer.EmitWinCFIEndProc();
}
const MCSymbolRefExpr *Win64Exception::createImageRel32(const MCSymbol *Value) {
return MCSymbolRefExpr::Create(Value, MCSymbolRefExpr::VK_COFF_IMGREL32,
Asm->OutContext);
}
/// Emit the language-specific data that __C_specific_handler expects. This
/// handler lives in the x64 Microsoft C runtime and allows catching or cleaning
/// up after faults with __try, __except, and __finally. The typeinfo values
/// are not really RTTI data, but pointers to filter functions that return an
/// integer (1, 0, or -1) indicating how to handle the exception. For __finally
/// blocks and other cleanups, the landing pad label is zero, and the filter
/// function is actually a cleanup handler with the same prototype. A catch-all
/// entry is modeled with a null filter function field and a non-zero landing
/// pad label.
///
/// Possible filter function return values:
/// EXCEPTION_EXECUTE_HANDLER (1):
/// Jump to the landing pad label after cleanups.
/// EXCEPTION_CONTINUE_SEARCH (0):
/// Continue searching this table or continue unwinding.
/// EXCEPTION_CONTINUE_EXECUTION (-1):
/// Resume execution at the trapping PC.
///
/// Inferred table structure:
/// struct Table {
/// int NumEntries;
/// struct Entry {
/// imagerel32 LabelStart;
/// imagerel32 LabelEnd;
/// imagerel32 FilterOrFinally; // Zero means catch-all.
/// imagerel32 LabelLPad; // Zero means __finally.
/// } Entries[NumEntries];
/// };
void Win64Exception::emitCSpecificHandlerTable() {
const std::vector<LandingPadInfo> &PadInfos = MMI->getLandingPads();
// Simplifying assumptions for first implementation:
// - Cleanups are not implemented.
// - Filters are not implemented.
// The Itanium LSDA table sorts similar landing pads together to simplify the
// actions table, but we don't need that.
SmallVector<const LandingPadInfo *, 64> LandingPads;
LandingPads.reserve(PadInfos.size());
for (const auto &LP : PadInfos)
LandingPads.push_back(&LP);
// Compute label ranges for call sites as we would for the Itanium LSDA, but
// use an all zero action table because we aren't using these actions.
SmallVector<unsigned, 64> FirstActions;
FirstActions.resize(LandingPads.size());
SmallVector<CallSiteEntry, 64> CallSites;
computeCallSiteTable(CallSites, LandingPads, FirstActions);
MCSymbol *EHFuncBeginSym =
Asm->GetTempSymbol("eh_func_begin", Asm->getFunctionNumber());
MCSymbol *EHFuncEndSym =
Asm->GetTempSymbol("eh_func_end", Asm->getFunctionNumber());
// Emit the number of table entries.
unsigned NumEntries = 0;
for (const CallSiteEntry &CSE : CallSites) {
if (!CSE.LPad)
continue; // Ignore gaps.
for (int Selector : CSE.LPad->TypeIds) {
// Ignore C++ filter clauses in SEH.
// FIXME: Implement cleanup clauses.
if (isCatchEHSelector(Selector))
++NumEntries;
}
}
Asm->OutStreamer.EmitIntValue(NumEntries, 4);
// Emit the four-label records for each call site entry. The table has to be
// sorted in layout order, and the call sites should already be sorted.
for (const CallSiteEntry &CSE : CallSites) {
// Ignore gaps. Unlike the Itanium model, unwinding through a frame without
// an EH table entry will propagate the exception rather than terminating
// the program.
if (!CSE.LPad)
continue;
const LandingPadInfo *LPad = CSE.LPad;
// Compute the label range. We may reuse the function begin and end labels
// rather than forming new ones.
const MCExpr *Begin =
createImageRel32(CSE.BeginLabel ? CSE.BeginLabel : EHFuncBeginSym);
const MCExpr *End;
if (CSE.EndLabel) {
// The interval is half-open, so we have to add one to include the return
// address of the last invoke in the range.
End = MCBinaryExpr::CreateAdd(createImageRel32(CSE.EndLabel),
MCConstantExpr::Create(1, Asm->OutContext),
Asm->OutContext);
} else {
End = createImageRel32(EHFuncEndSym);
}
// These aren't really type info globals, they are actually pointers to
// filter functions ordered by selector. The zero selector is used for
// cleanups, so slot zero corresponds to selector 1.
const std::vector<const GlobalValue *> &SelectorToFilter = MMI->getTypeInfos();
// Do a parallel iteration across typeids and clause labels, skipping filter
// clauses.
assert(LPad->TypeIds.size() == LPad->ClauseLabels.size());
for (size_t I = 0, E = LPad->TypeIds.size(); I < E; ++I) {
// AddLandingPadInfo stores the clauses in reverse, but there is a FIXME
// to change that.
int Selector = LPad->TypeIds[E - I - 1];
MCSymbol *ClauseLabel = LPad->ClauseLabels[I];
// Ignore C++ filter clauses in SEH.
// FIXME: Implement cleanup clauses.
if (!isCatchEHSelector(Selector))
continue;
Asm->OutStreamer.EmitValue(Begin, 4);
Asm->OutStreamer.EmitValue(End, 4);
if (isCatchEHSelector(Selector)) {
assert(unsigned(Selector - 1) < SelectorToFilter.size());
const GlobalValue *TI = SelectorToFilter[Selector - 1];
if (TI) // Emit the filter function pointer.
Asm->OutStreamer.EmitValue(createImageRel32(Asm->getSymbol(TI)), 4);
else // Otherwise, this is a "catch i8* null", or catch all.
Asm->OutStreamer.EmitIntValue(0, 4);
}
Asm->OutStreamer.EmitValue(createImageRel32(ClauseLabel), 4);
}
}
}

View File

@ -29,6 +29,10 @@ class Win64Exception : public EHStreamer {
/// Per-function flag to indicate if frame moves info should be emitted.
bool shouldEmitMoves;
void emitCSpecificHandlerTable();
const MCSymbolRefExpr *createImageRel32(const MCSymbol *Value);
public:
//===--------------------------------------------------------------------===//
// Main entry points.

View File

@ -452,6 +452,14 @@ void MachineModuleInfo::addCleanup(MachineBasicBlock *LandingPad) {
LP.TypeIds.push_back(0);
}
MCSymbol *
MachineModuleInfo::addClauseForLandingPad(MachineBasicBlock *LandingPad) {
MCSymbol *ClauseLabel = Context.CreateTempSymbol();
LandingPadInfo &LP = getOrCreateLandingPadInfo(LandingPad);
LP.ClauseLabels.push_back(ClauseLabel);
return ClauseLabel;
}
/// TidyLandingPads - Remap landing pad labels and remove any deleted landing
/// pads.
void MachineModuleInfo::TidyLandingPads(DenseMap<MCSymbol*, uintptr_t> *LPMap) {

View File

@ -449,9 +449,9 @@ void TargetPassConfig::addPassesToHandleExceptions() {
case ExceptionHandling::DwarfCFI:
case ExceptionHandling::ARM:
case ExceptionHandling::ItaniumWinEH:
case ExceptionHandling::MSVC: // FIXME: Needs preparation.
addPass(createDwarfEHPass(TM));
break;
case ExceptionHandling::MSVC: // FIXME: Add preparation.
case ExceptionHandling::None:
addPass(createLowerInvokePass());

View File

@ -2071,10 +2071,14 @@ void SelectionDAGBuilder::visitLandingPad(const LandingPadInst &LP) {
// Get the two live-in registers as SDValues. The physregs have already been
// copied into virtual registers.
SDValue Ops[2];
Ops[0] = DAG.getZExtOrTrunc(
DAG.getCopyFromReg(DAG.getEntryNode(), getCurSDLoc(),
FuncInfo.ExceptionPointerVirtReg, TLI.getPointerTy()),
getCurSDLoc(), ValueVTs[0]);
if (FuncInfo.ExceptionPointerVirtReg) {
Ops[0] = DAG.getZExtOrTrunc(
DAG.getCopyFromReg(DAG.getEntryNode(), getCurSDLoc(),
FuncInfo.ExceptionPointerVirtReg, TLI.getPointerTy()),
getCurSDLoc(), ValueVTs[0]);
} else {
Ops[0] = DAG.getConstant(0, TLI.getPointerTy());
}
Ops[1] = DAG.getZExtOrTrunc(
DAG.getCopyFromReg(DAG.getEntryNode(), getCurSDLoc(),
FuncInfo.ExceptionSelectorVirtReg, TLI.getPointerTy()),
@ -2086,6 +2090,27 @@ void SelectionDAGBuilder::visitLandingPad(const LandingPadInst &LP) {
setValue(&LP, Res);
}
unsigned
SelectionDAGBuilder::visitLandingPadClauseBB(GlobalValue *ClauseGV,
MachineBasicBlock *LPadBB) {
SDValue Chain = getControlRoot();
// Get the typeid that we will dispatch on later.
const TargetLowering &TLI = DAG.getTargetLoweringInfo();
const TargetRegisterClass *RC = TLI.getRegClassFor(TLI.getPointerTy());
unsigned VReg = FuncInfo.MF->getRegInfo().createVirtualRegister(RC);
unsigned TypeID = DAG.getMachineFunction().getMMI().getTypeIDFor(ClauseGV);
SDValue Sel = DAG.getConstant(TypeID, TLI.getPointerTy());
Chain = DAG.getCopyToReg(Chain, getCurSDLoc(), VReg, Sel);
// Branch to the main landing pad block.
MachineBasicBlock *ClauseMBB = FuncInfo.MBB;
ClauseMBB->addSuccessor(LPadBB);
DAG.setRoot(DAG.getNode(ISD::BR, getCurSDLoc(), MVT::Other, Chain,
DAG.getBasicBlock(LPadBB)));
return VReg;
}
/// handleSmallSwitchCaseRange - Emit a series of specific tests (suitable for
/// small case ranges).
bool SelectionDAGBuilder::handleSmallSwitchRange(CaseRec& CR,

View File

@ -713,6 +713,8 @@ public:
void visitJumpTable(JumpTable &JT);
void visitJumpTableHeader(JumpTable &JT, JumpTableHeader &JTH,
MachineBasicBlock *SwitchBB);
unsigned visitLandingPadClauseBB(GlobalValue *ClauseGV,
MachineBasicBlock *LPadMBB);
private:
// These all get lowered before this pass.

View File

@ -19,6 +19,7 @@
#include "llvm/Analysis/AliasAnalysis.h"
#include "llvm/Analysis/BranchProbabilityInfo.h"
#include "llvm/Analysis/CFG.h"
#include "llvm/CodeGen/Analysis.h"
#include "llvm/CodeGen/FastISel.h"
#include "llvm/CodeGen/FunctionLoweringInfo.h"
#include "llvm/CodeGen/GCMetadata.h"
@ -40,6 +41,7 @@
#include "llvm/IR/Intrinsics.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/MC/MCAsmInfo.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/ErrorHandling.h"
@ -892,6 +894,8 @@ void SelectionDAGISel::DoInstructionSelection() {
void SelectionDAGISel::PrepareEHLandingPad() {
MachineBasicBlock *MBB = FuncInfo->MBB;
const TargetRegisterClass *PtrRC = TLI->getRegClassFor(TLI->getPointerTy());
// Add a label to mark the beginning of the landing pad. Deletion of the
// landing pad can thus be detected via the MachineModuleInfo.
MCSymbol *Label = MF->getMMI().addLandingPad(MBB);
@ -903,8 +907,66 @@ void SelectionDAGISel::PrepareEHLandingPad() {
BuildMI(*MBB, FuncInfo->InsertPt, SDB->getCurDebugLoc(), II)
.addSym(Label);
if (TM.getMCAsmInfo()->getExceptionHandlingType() ==
ExceptionHandling::MSVC) {
// Make virtual registers and a series of labels that fill in values for the
// clauses.
auto &RI = MF->getRegInfo();
FuncInfo->ExceptionSelectorVirtReg = RI.createVirtualRegister(PtrRC);
// Get all invoke BBs that will unwind into the clause BBs.
SmallVector<MachineBasicBlock *, 4> InvokeBBs(MBB->pred_begin(),
MBB->pred_end());
// Emit separate machine basic blocks with separate labels for each clause
// before the main landing pad block.
const BasicBlock *LLVMBB = MBB->getBasicBlock();
const LandingPadInst *LPadInst = LLVMBB->getLandingPadInst();
MachineInstrBuilder SelectorPHI = BuildMI(
*MBB, MBB->begin(), SDB->getCurDebugLoc(), TII->get(TargetOpcode::PHI),
FuncInfo->ExceptionSelectorVirtReg);
for (unsigned I = 0, E = LPadInst->getNumClauses(); I != E; ++I) {
MachineBasicBlock *ClauseBB = MF->CreateMachineBasicBlock(LLVMBB);
MF->insert(MBB, ClauseBB);
// Add the edge from the invoke to the clause.
for (MachineBasicBlock *InvokeBB : InvokeBBs)
InvokeBB->addSuccessor(ClauseBB);
// Mark the clause as a landing pad or MI passes will delete it.
ClauseBB->setIsLandingPad();
GlobalValue *ClauseGV = ExtractTypeInfo(LPadInst->getClause(I));
// Start the BB with a label.
MCSymbol *ClauseLabel = MF->getMMI().addClauseForLandingPad(MBB);
BuildMI(*ClauseBB, ClauseBB->begin(), SDB->getCurDebugLoc(), II)
.addSym(ClauseLabel);
// Construct a simple BB that defines a register with the typeid constant.
FuncInfo->MBB = ClauseBB;
FuncInfo->InsertPt = ClauseBB->end();
unsigned VReg = SDB->visitLandingPadClauseBB(ClauseGV, MBB);
CurDAG->setRoot(SDB->getRoot());
SDB->clear();
CodeGenAndEmitDAG();
// Add the typeid virtual register to the phi in the main landing pad.
SelectorPHI.addReg(VReg).addMBB(ClauseBB);
}
// Remove the edge from the invoke to the lpad.
for (MachineBasicBlock *InvokeBB : InvokeBBs)
InvokeBB->removeSuccessor(MBB);
// Restore FuncInfo back to its previous state and select the main landing
// pad block.
FuncInfo->MBB = MBB;
FuncInfo->InsertPt = MBB->end();
return;
}
// Mark exception register as live in.
const TargetRegisterClass *PtrRC = TLI->getRegClassFor(TLI->getPointerTy());
if (unsigned Reg = TLI->getExceptionPointerRegister())
FuncInfo->ExceptionPointerVirtReg = MBB->addLiveIn(Reg, PtrRC);

View File

@ -0,0 +1,175 @@
; RUN: llc -mtriple x86_64-pc-windows-msvc < %s | FileCheck %s
define void @two_invoke_merged() {
entry:
invoke void @try_body()
to label %again unwind label %lpad
again:
invoke void @try_body()
to label %done unwind label %lpad
done:
ret void
lpad:
%vals = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__C_specific_handler to i8*)
catch i8* bitcast (i32 (i8*, i8*)* @filt0 to i8*)
catch i8* bitcast (i32 (i8*, i8*)* @filt1 to i8*)
%sel = extractvalue { i8*, i32 } %vals, 1
call void @use_selector(i32 %sel)
ret void
}
; Normal path code
; CHECK-LABEL: {{^}}two_invoke_merged:
; CHECK: .seh_proc two_invoke_merged
; CHECK: .seh_handler __C_specific_handler, @unwind, @except
; CHECK: .Ltmp0:
; CHECK: callq try_body
; CHECK-NEXT: .Ltmp1:
; CHECK: .Ltmp2:
; CHECK: callq try_body
; CHECK-NEXT: .Ltmp3:
; CHECK: retq
; Landing pad code
; CHECK: .Ltmp5:
; CHECK: movl $1, %ecx
; CHECK: jmp
; CHECK: .Ltmp6:
; CHECK: movl $2, %ecx
; CHECK: callq use_selector
; CHECK: .seh_handlerdata
; CHECK-NEXT: .long 2
; CHECK-NEXT: .long .Ltmp0@IMGREL
; CHECK-NEXT: .long .Ltmp3@IMGREL+1
; CHECK-NEXT: .long filt0@IMGREL
; CHECK-NEXT: .long .Ltmp5@IMGREL
; CHECK-NEXT: .long .Ltmp0@IMGREL
; CHECK-NEXT: .long .Ltmp3@IMGREL+1
; CHECK-NEXT: .long filt1@IMGREL
; CHECK-NEXT: .long .Ltmp6@IMGREL
; CHECK: .text
; CHECK: .seh_endproc
define void @two_invoke_gap() {
entry:
invoke void @try_body()
to label %again unwind label %lpad
again:
call void @do_nothing_on_unwind()
invoke void @try_body()
to label %done unwind label %lpad
done:
ret void
lpad:
%vals = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__C_specific_handler to i8*)
catch i8* bitcast (i32 (i8*, i8*)* @filt0 to i8*)
%sel = extractvalue { i8*, i32 } %vals, 1
call void @use_selector(i32 %sel)
ret void
}
; Normal path code
; CHECK-LABEL: {{^}}two_invoke_gap:
; CHECK: .seh_proc two_invoke_gap
; CHECK: .seh_handler __C_specific_handler, @unwind, @except
; CHECK: .Ltmp11:
; CHECK: callq try_body
; CHECK-NEXT: .Ltmp12:
; CHECK: callq do_nothing_on_unwind
; CHECK: .Ltmp13:
; CHECK: callq try_body
; CHECK-NEXT: .Ltmp14:
; CHECK: retq
; Landing pad code
; CHECK: .Ltmp16:
; CHECK: movl $1, %ecx
; CHECK: callq use_selector
; CHECK: .seh_handlerdata
; CHECK-NEXT: .long 2
; CHECK-NEXT: .long .Ltmp11@IMGREL
; CHECK-NEXT: .long .Ltmp12@IMGREL+1
; CHECK-NEXT: .long filt0@IMGREL
; CHECK-NEXT: .long .Ltmp16@IMGREL
; CHECK-NEXT: .long .Ltmp13@IMGREL
; CHECK-NEXT: .long .Ltmp14@IMGREL+1
; CHECK-NEXT: .long filt0@IMGREL
; CHECK-NEXT: .long .Ltmp16@IMGREL
; CHECK: .text
; CHECK: .seh_endproc
define void @two_invoke_nounwind_gap() {
entry:
invoke void @try_body()
to label %again unwind label %lpad
again:
call void @cannot_unwind()
invoke void @try_body()
to label %done unwind label %lpad
done:
ret void
lpad:
%vals = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__C_specific_handler to i8*)
catch i8* bitcast (i32 (i8*, i8*)* @filt0 to i8*)
%sel = extractvalue { i8*, i32 } %vals, 1
call void @use_selector(i32 %sel)
ret void
}
; Normal path code
; CHECK-LABEL: {{^}}two_invoke_nounwind_gap:
; CHECK: .seh_proc two_invoke_nounwind_gap
; CHECK: .seh_handler __C_specific_handler, @unwind, @except
; CHECK: .Ltmp21:
; CHECK: callq try_body
; CHECK-NEXT: .Ltmp22:
; CHECK: callq cannot_unwind
; CHECK: .Ltmp23:
; CHECK: callq try_body
; CHECK-NEXT: .Ltmp24:
; CHECK: retq
; Landing pad code
; CHECK: .Ltmp26:
; CHECK: movl $1, %ecx
; CHECK: callq use_selector
; CHECK: .seh_handlerdata
; CHECK-NEXT: .long 1
; CHECK-NEXT: .long .Ltmp21@IMGREL
; CHECK-NEXT: .long .Ltmp24@IMGREL+1
; CHECK-NEXT: .long filt0@IMGREL
; CHECK-NEXT: .long .Ltmp26@IMGREL
; CHECK: .text
; CHECK: .seh_endproc
declare void @try_body()
declare void @do_nothing_on_unwind()
declare void @cannot_unwind() nounwind
declare void @use_selector(i32)
declare i32 @filt0(i8* %eh_info, i8* %rsp)
declare i32 @filt1(i8* %eh_info, i8* %rsp)
declare void @handler0()
declare void @handler1()
declare i32 @__C_specific_handler(...)
declare i32 @llvm.eh.typeid.for(i8*) readnone nounwind

View File

@ -0,0 +1,196 @@
; RUN: llc -mtriple x86_64-pc-windows-msvc < %s | FileCheck %s
; This test case is also intended to be run manually as a complete functional
; test. It should link, print something, and exit zero rather than crashing.
; It is the hypothetical lowering of a C source program that looks like:
;
; int safe_div(int *n, int *d) {
; int r;
; __try {
; __try {
; r = *n / *d;
; } __except(GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION) {
; puts("EXCEPTION_ACCESS_VIOLATION");
; r = -1;
; }
; } __except(GetExceptionCode() == EXCEPTION_INT_DIVIDE_BY_ZERO) {
; puts("EXCEPTION_INT_DIVIDE_BY_ZERO");
; r = -2;
; }
; return r;
; }
@str1 = internal constant [27 x i8] c"EXCEPTION_ACCESS_VIOLATION\00"
@str2 = internal constant [29 x i8] c"EXCEPTION_INT_DIVIDE_BY_ZERO\00"
define i32 @safe_div(i32* %n, i32* %d) {
entry:
%r = alloca i32, align 4
invoke void @try_body(i32* %r, i32* %n, i32* %d)
to label %__try.cont unwind label %lpad
lpad:
%vals = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__C_specific_handler to i8*)
catch i8* bitcast (i32 (i8*, i8*)* @safe_div_filt0 to i8*)
catch i8* bitcast (i32 (i8*, i8*)* @safe_div_filt1 to i8*)
%ehptr = extractvalue { i8*, i32 } %vals, 0
%sel = extractvalue { i8*, i32 } %vals, 1
%filt0_val = call i32 @llvm.eh.typeid.for(i8* bitcast (i32 (i8*, i8*)* @safe_div_filt0 to i8*))
%is_filt0 = icmp eq i32 %sel, %filt0_val
br i1 %is_filt0, label %handler0, label %eh.dispatch1
eh.dispatch1:
%filt1_val = call i32 @llvm.eh.typeid.for(i8* bitcast (i32 (i8*, i8*)* @safe_div_filt1 to i8*))
%is_filt1 = icmp eq i32 %sel, %filt1_val
br i1 %is_filt1, label %handler1, label %eh.resume
handler0:
call void @puts(i8* getelementptr ([27 x i8]* @str1, i32 0, i32 0))
store i32 -1, i32* %r, align 4
br label %__try.cont
handler1:
call void @puts(i8* getelementptr ([29 x i8]* @str2, i32 0, i32 0))
store i32 -2, i32* %r, align 4
br label %__try.cont
eh.resume:
resume { i8*, i32 } %vals
__try.cont:
%safe_ret = load i32* %r, align 4
ret i32 %safe_ret
}
; Normal path code
; CHECK: {{^}}safe_div:
; CHECK: .seh_proc safe_div
; CHECK: .seh_handler __C_specific_handler, @unwind, @except
; CHECK: .Ltmp0:
; CHECK: leaq [[rloc:.*\(%rsp\)]], %rcx
; CHECK: callq try_body
; CHECK-NEXT: .Ltmp1
; CHECK: .LBB0_7:
; CHECK: movl [[rloc]], %eax
; CHECK: retq
; Landing pad code
; CHECK: .Ltmp3:
; CHECK: movl $1, %[[sel:[a-z]+]]
; CHECK: .Ltmp4
; CHECK: movl $2, %[[sel]]
; CHECK: .L{{.*}}:
; CHECK: cmpl $1, %[[sel]]
; CHECK: # %handler0
; CHECK: callq puts
; CHECK: movl $-1, [[rloc]]
; CHECK: jmp .LBB0_7
; CHECK: cmpl $2, %[[sel]]
; CHECK: # %handler1
; CHECK: callq puts
; CHECK: movl $-2, [[rloc]]
; CHECK: jmp .LBB0_7
; FIXME: EH preparation should not call _Unwind_Resume.
; CHECK: callq _Unwind_Resume
; CHECK: ud2
; CHECK: .seh_handlerdata
; CHECK: .long 2
; CHECK: .long .Ltmp0@IMGREL
; CHECK: .long .Ltmp1@IMGREL+1
; CHECK: .long safe_div_filt0@IMGREL
; CHECK: .long .Ltmp3@IMGREL
; CHECK: .long .Ltmp0@IMGREL
; CHECK: .long .Ltmp1@IMGREL+1
; CHECK: .long safe_div_filt1@IMGREL
; CHECK: .long .Ltmp4@IMGREL
; CHECK: .text
; CHECK: .seh_endproc
define void @try_body(i32* %r, i32* %n, i32* %d) {
entry:
%0 = load i32* %n, align 4
%1 = load i32* %d, align 4
%div = sdiv i32 %0, %1
store i32 %div, i32* %r, align 4
ret void
}
; The prototype of these filter functions is:
; int filter(EXCEPTION_POINTERS *eh_ptrs, void *rbp);
; The definition of EXCEPTION_POINTERS is:
; typedef struct _EXCEPTION_POINTERS {
; EXCEPTION_RECORD *ExceptionRecord;
; CONTEXT *ContextRecord;
; } EXCEPTION_POINTERS;
; The definition of EXCEPTION_RECORD is:
; typedef struct _EXCEPTION_RECORD {
; DWORD ExceptionCode;
; ...
; } EXCEPTION_RECORD;
; The exception code can be retreived with two loads, one for the record
; pointer and one for the code. The values of local variables can be
; accessed via rbp, but that would require additional not yet implemented LLVM
; support.
define i32 @safe_div_filt0(i8* %eh_ptrs, i8* %rbp) {
%eh_ptrs_c = bitcast i8* %eh_ptrs to i32**
%eh_rec = load i32** %eh_ptrs_c
%eh_code = load i32* %eh_rec
; EXCEPTION_ACCESS_VIOLATION = 0xC0000005
%cmp = icmp eq i32 %eh_code, 3221225477
%filt.res = zext i1 %cmp to i32
ret i32 %filt.res
}
define i32 @safe_div_filt1(i8* %eh_ptrs, i8* %rbp) {
%eh_ptrs_c = bitcast i8* %eh_ptrs to i32**
%eh_rec = load i32** %eh_ptrs_c
%eh_code = load i32* %eh_rec
; EXCEPTION_INT_DIVIDE_BY_ZERO = 0xC0000094
%cmp = icmp eq i32 %eh_code, 3221225620
%filt.res = zext i1 %cmp to i32
ret i32 %filt.res
}
@str_result = internal constant [21 x i8] c"safe_div result: %d\0A\00"
define i32 @main() {
%d.addr = alloca i32, align 4
%n.addr = alloca i32, align 4
store i32 10, i32* %n.addr, align 4
store i32 2, i32* %d.addr, align 4
%r1 = call i32 @safe_div(i32* %n.addr, i32* %d.addr)
call void (i8*, ...)* @printf(i8* getelementptr ([21 x i8]* @str_result, i32 0, i32 0), i32 %r1)
store i32 10, i32* %n.addr, align 4
store i32 0, i32* %d.addr, align 4
%r2 = call i32 @safe_div(i32* %n.addr, i32* %d.addr)
call void (i8*, ...)* @printf(i8* getelementptr ([21 x i8]* @str_result, i32 0, i32 0), i32 %r2)
%r3 = call i32 @safe_div(i32* %n.addr, i32* null)
call void (i8*, ...)* @printf(i8* getelementptr ([21 x i8]* @str_result, i32 0, i32 0), i32 %r3)
ret i32 0
}
define void @_Unwind_Resume() {
call void @abort()
unreachable
}
declare i32 @__C_specific_handler(...)
declare i32 @llvm.eh.typeid.for(i8*) readnone nounwind
declare void @puts(i8*)
declare void @printf(i8*, ...)
declare void @abort()