mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2024-11-01 15:11:24 +00:00
Adding support for llvm.eh.begincatch and llvm.eh.endcatch intrinsics and beginning the documentation of native Windows exception handling.
Differential Revision: http://reviews.llvm.org/D7398 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@228733 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
b613f8842e
commit
7741851819
@ -64,6 +64,21 @@ handling at the expense of slower execution when no exceptions are thrown. As
|
||||
exceptions are, by their nature, intended for uncommon code paths, DWARF
|
||||
exception handling is generally preferred to SJLJ.
|
||||
|
||||
Windows Runtime Exception Handling
|
||||
-----------------------------------
|
||||
|
||||
Windows runtime based exception handling uses the same basic IR structure as
|
||||
Itanium ABI based exception handling, but it relies on the personality
|
||||
functions provided by the native Windows runtime library, ``__CxxFrameHandler3``
|
||||
for C++ exceptions: ``__C_specific_handler`` for 64-bit SEH or
|
||||
``_frame_handler3/4`` for 32-bit SEH. This results in a very different
|
||||
execution model and requires some minor modifications to the initial IR
|
||||
representation and a significant restructuring just before code generation.
|
||||
|
||||
General information about the Windows x64 exception handling mechanism can be
|
||||
found at `MSDN Exception Handling (x64)
|
||||
<https://msdn.microsoft.com/en-us/library/1eyas8tf(v=vs.80).aspx>_`.
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
@ -306,6 +321,97 @@ the selector results they understand and then resume exception propagation with
|
||||
the `resume instruction <LangRef.html#i_resume>`_ if none of the conditions
|
||||
match.
|
||||
|
||||
C++ Exception Handling using the Windows Runtime
|
||||
=================================================
|
||||
|
||||
(Note: Windows C++ exception handling support is a work in progress and is
|
||||
not yet fully implemented. The text below describes how it will work
|
||||
when completed.)
|
||||
|
||||
The Windows runtime function for C++ exception handling uses a mutli-phase
|
||||
approach. When an exception occurs it searches the current callstack for a
|
||||
frame that has a handler for the exception. If a handler is found, it then
|
||||
calls the cleanup handler for each frame above the handler which has a
|
||||
cleanup handler before calling the catch handler. These calls are all made
|
||||
from a stack context different from the original frame in which the handler
|
||||
is defined. Therefore, it is necessary to outline these handlers from their
|
||||
original context before code generation.
|
||||
|
||||
Catch handlers are called with a pointer to the handler itself as the first
|
||||
argument and a pointer to the parent function's stack frame as the second
|
||||
argument. The catch handler uses the `llvm.recoverframe
|
||||
<LangRef.html#llvm-frameallocate-and-llvm-framerecover-intrinsics>`_ to get a
|
||||
pointer to a frame allocation block that is created in the parent frame using
|
||||
the `llvm.allocateframe
|
||||
<LangRef.html#llvm-frameallocate-and-llvm-framerecover-intrinsics>`_ intrinsic.
|
||||
The ``WinEHPrepare`` pass will have created a structure definition for the
|
||||
contents of this block. The first two members of the structure will always be
|
||||
(1) a 32-bit integer that the runtime uses to track the exception state of the
|
||||
parent frame for the purposes of handling chained exceptions and (2) a pointer
|
||||
to the object associated with the exception (roughly, the parameter of the
|
||||
catch clause). These two members will be followed by any frame variables from
|
||||
the parent function which must be accessed in any of the functions unwind or
|
||||
catch handlers. The catch handler returns the address at which execution
|
||||
should continue.
|
||||
|
||||
Cleanup handlers perform any cleanup necessary as the frame goes out of scope,
|
||||
such as calling object destructors. The runtime handles the actual unwinding
|
||||
of the stack. If an exception occurs in a cleanup handler the runtime manages
|
||||
termination of the process. Cleanup handlers are called with the same arguments
|
||||
as catch handlers (a pointer to the handler and a pointer to the parent stack
|
||||
frame) and use the same mechanism described above to access frame variables
|
||||
in the parent function. Cleanup handlers do not return a value.
|
||||
|
||||
The IR generated for Windows runtime based C++ exception handling is initially
|
||||
very similar to the ``landingpad`` mechanism described above. Calls to
|
||||
libc++abi functions (such as ``__cxa_begin_catch``/``__cxa_end_catch`` and
|
||||
``__cxa_throw_exception`` are replaced with calls to intrinsics or Windows
|
||||
runtime functions (such as ``llvm.eh.begincatch``/``llvm.eh.endcatch`` and
|
||||
``__CxxThrowException``).
|
||||
|
||||
During the WinEHPrepare pass, the handler functions are outlined into handler
|
||||
functions and the original landing pad code is replaced with a call to the
|
||||
``llvm.eh.actions`` intrinsic that describes the order in which handlers will
|
||||
be processed from the logical location of the landing pad and an indirect
|
||||
branch to the return value of the ``llvm.eh.actions`` intrinsic. The
|
||||
``llvm.eh.actions`` intrinsic is defined as returning the address at which
|
||||
execution will continue. This is a temporary construct which will be removed
|
||||
before code generation, but it allows for the accurate tracking of control
|
||||
flow until then.
|
||||
|
||||
A typical landing pad will look like this after outlining:
|
||||
|
||||
.. code-block:: llvm
|
||||
|
||||
lpad:
|
||||
%vals = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*)
|
||||
cleanup
|
||||
catch i8* bitcast (i8** @_ZTIi to i8*)
|
||||
catch i8* bitcast (i8** @_ZTIf to i8*)
|
||||
%recover = call i8* (...)* @llvm.eh.actions(
|
||||
i32 3, i8* bitcast (i8** @_ZTIi to i8*), i8* (i8*, i8*)* @_Z4testb.catch.1)
|
||||
i32 2, i8* null, void (i8*, i8*)* @_Z4testb.cleanup.1)
|
||||
i32 1, i8* bitcast (i8** @_ZTIf to i8*), i8* (i8*, i8*)* @_Z4testb.catch.0)
|
||||
i32 0, i8* null, void (i8*, i8*)* @_Z4testb.cleanup.0)
|
||||
indirectbr i8* %recover, [label %try.cont1, label %try.cont2]
|
||||
|
||||
In this example, the landing pad represents an exception handling context with
|
||||
two catch handlers and a cleanup handler that have been outlined. If an
|
||||
exception is thrown with a type that matches ``_ZTIi``, the ``_Z4testb.catch.1``
|
||||
handler will be called an no clean-up is needed. If an exception is thrown
|
||||
with a type that matches ``_ZTIf``, first the ``_Z4testb.cleanup.1`` handler
|
||||
will be called to perform unwind-related cleanup, then the ``_Z4testb.catch.1``
|
||||
handler will be called. If an exception is throw which does not match either
|
||||
of these types and the exception is handled by another frame further up the
|
||||
call stack, first the ``_Z4testb.cleanup.1`` handler will be called, then the
|
||||
``_Z4testb.cleanup.0`` handler (which corresponds to a different scope) will be
|
||||
called, and exception handling will continue at the next frame in the call
|
||||
stack will be called. One of the catch handlers will return the address of
|
||||
``%try.cont1`` in the parent function and the other will return the address of
|
||||
``%try.cont2``, meaning that execution continues at one of those blocks after
|
||||
an exception is caught.
|
||||
|
||||
|
||||
Exception Handling Intrinsics
|
||||
=============================
|
||||
|
||||
@ -329,6 +435,70 @@ function. This value can be used to compare against the result of
|
||||
|
||||
Uses of this intrinsic are generated by the C++ front-end.
|
||||
|
||||
.. _llvm.eh.begincatch:
|
||||
|
||||
``llvm.eh.begincatch``
|
||||
----------------------
|
||||
|
||||
.. code-block:: llvm
|
||||
|
||||
i8* @llvm.eh.begincatch(i8* %exn)
|
||||
|
||||
|
||||
This intrinsic marks the beginning of catch handling code within the blocks
|
||||
following a ``landingpad`` instruction. The exact behavior of this function
|
||||
depends on the compilation target and the personality function associated
|
||||
with the ``landingpad`` instruction.
|
||||
|
||||
The argument to this intrinsic is a pointer that was previously extracted from
|
||||
the aggregate return value of the ``landingpad`` instruction. The return
|
||||
value of the intrinsic is a pointer to the exception object to be used by the
|
||||
catch code. This pointer is returned as an ``i8*`` value, but the actual type
|
||||
of the object will depend on the exception that was thrown.
|
||||
|
||||
Uses of this intrinsic are generated by the C++ front-end. Many targets will
|
||||
use implementation-specific functions (such as ``__cxa_begin_catch``) instead
|
||||
of this intrinsic. The intrinsic is provided for targets that require a more
|
||||
abstract interface.
|
||||
|
||||
When used in the native Windows C++ exception handling implementation, this
|
||||
intrinsic serves as a placeholder to delimit code before a catch handler is
|
||||
outlined. When the handler is is outlined, this intrinsic will be replaced
|
||||
by instructions that retrieve the exception object pointer from the frame
|
||||
allocation block.
|
||||
|
||||
|
||||
.. _llvm.eh.endcatch:
|
||||
|
||||
``llvm.eh.endcatch``
|
||||
----------------------
|
||||
|
||||
.. code-block:: llvm
|
||||
|
||||
void @llvm.eh.endcatch()
|
||||
|
||||
|
||||
This intrinsic marks the end of catch handling code within the current block,
|
||||
which will be a successor of a block which called ``llvm.eh.begincatch''.
|
||||
The exact behavior of this function depends on the compilation target and the
|
||||
personality function associated with the corresponding ``landingpad``
|
||||
instruction.
|
||||
|
||||
There may be more than one call to ``llvm.eh.endcatch`` for any given call to
|
||||
``llvm.eh.begincatch`` with each ``llvm.eh.endcatch`` call corresponding to the
|
||||
end of a different control path. All control paths following a call to
|
||||
``llvm.eh.begincatch`` must reach a call to ``llvm.eh.endcatch``.
|
||||
|
||||
Uses of this intrinsic are generated by the C++ front-end. Many targets will
|
||||
use implementation-specific functions (such as ``__cxa_begin_catch``) instead
|
||||
of this intrinsic. The intrinsic is provided for targets that require a more
|
||||
abstract interface.
|
||||
|
||||
When used in the native Windows C++ exception handling implementation, this
|
||||
intrinsic serves as a placeholder to delimit code before a catch handler is
|
||||
outlined. After the handler is outlined, this intrinsic is simply removed.
|
||||
|
||||
|
||||
SJLJ Intrinsics
|
||||
---------------
|
||||
|
||||
|
@ -411,6 +411,11 @@ def int_eh_typeid_for : Intrinsic<[llvm_i32_ty], [llvm_ptr_ty], [IntrNoMem]>;
|
||||
def int_eh_return_i32 : Intrinsic<[], [llvm_i32_ty, llvm_ptr_ty]>;
|
||||
def int_eh_return_i64 : Intrinsic<[], [llvm_i64_ty, llvm_ptr_ty]>;
|
||||
|
||||
// eh.begincatch takes a pointer returned by a landingpad instruction and
|
||||
// returns the exception object pointer for the exception to be handled.
|
||||
def int_eh_begincatch : Intrinsic<[llvm_ptr_ty], [llvm_ptr_ty]>;
|
||||
def int_eh_endcatch : Intrinsic<[], []>;
|
||||
|
||||
// __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.
|
||||
|
@ -35,6 +35,7 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/Analysis/Lint.h"
|
||||
#include "llvm/ADT/SmallSet.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/Analysis/AliasAnalysis.h"
|
||||
#include "llvm/Analysis/AssumptionCache.h"
|
||||
@ -73,6 +74,8 @@ namespace {
|
||||
void visitMemoryReference(Instruction &I, Value *Ptr,
|
||||
uint64_t Size, unsigned Align,
|
||||
Type *Ty, unsigned Flags);
|
||||
void visitEHBeginCatch(IntrinsicInst *II);
|
||||
void visitEHEndCatch(IntrinsicInst *II);
|
||||
|
||||
void visitCallInst(CallInst &I);
|
||||
void visitInvokeInst(InvokeInst &I);
|
||||
@ -346,6 +349,13 @@ void Lint::visitCallSite(CallSite CS) {
|
||||
visitMemoryReference(I, CS.getArgument(0), AliasAnalysis::UnknownSize,
|
||||
0, nullptr, MemRef::Read | MemRef::Write);
|
||||
break;
|
||||
|
||||
case Intrinsic::eh_begincatch:
|
||||
visitEHBeginCatch(II);
|
||||
break;
|
||||
case Intrinsic::eh_endcatch:
|
||||
visitEHEndCatch(II);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -509,6 +519,188 @@ void Lint::visitShl(BinaryOperator &I) {
|
||||
"Undefined result: Shift count out of range", &I);
|
||||
}
|
||||
|
||||
static bool
|
||||
allPredsCameFromLandingPad(BasicBlock *BB,
|
||||
SmallSet<BasicBlock *, 4> &VisitedBlocks) {
|
||||
VisitedBlocks.insert(BB);
|
||||
if (BB->isLandingPad())
|
||||
return true;
|
||||
// If we find a block with no predecessors, the search failed.
|
||||
if (pred_empty(BB))
|
||||
return false;
|
||||
for (BasicBlock *Pred : predecessors(BB)) {
|
||||
if (VisitedBlocks.count(Pred))
|
||||
continue;
|
||||
if (!allPredsCameFromLandingPad(Pred, VisitedBlocks))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
allSuccessorsReachEndCatch(BasicBlock *BB, BasicBlock::iterator InstBegin,
|
||||
IntrinsicInst **SecondBeginCatch,
|
||||
SmallSet<BasicBlock *, 4> &VisitedBlocks) {
|
||||
VisitedBlocks.insert(BB);
|
||||
for (BasicBlock::iterator I = InstBegin, E = BB->end(); I != E; ++I) {
|
||||
IntrinsicInst *IC = dyn_cast<IntrinsicInst>(I);
|
||||
if (IC && IC->getIntrinsicID() == Intrinsic::eh_endcatch)
|
||||
return true;
|
||||
// If we find another begincatch while looking for an endcatch,
|
||||
// that's also an error.
|
||||
if (IC && IC->getIntrinsicID() == Intrinsic::eh_begincatch) {
|
||||
*SecondBeginCatch = IC;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// If we reach a block with no successors while searching, the
|
||||
// search has failed.
|
||||
if (succ_empty(BB))
|
||||
return false;
|
||||
// Otherwise, search all of the successors.
|
||||
for (BasicBlock *Succ : successors(BB)) {
|
||||
if (VisitedBlocks.count(Succ))
|
||||
continue;
|
||||
if (!allSuccessorsReachEndCatch(Succ, Succ->begin(), SecondBeginCatch,
|
||||
VisitedBlocks))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Lint::visitEHBeginCatch(IntrinsicInst *II) {
|
||||
// The checks in this function make a potentially dubious assumption about
|
||||
// the CFG, namely that any block involved in a catch is only used for the
|
||||
// catch. This will very likely be true of IR generated by a front end,
|
||||
// but it may cease to be true, for example, if the IR is run through a
|
||||
// pass which combines similar blocks.
|
||||
//
|
||||
// In general, if we encounter a block the isn't dominated by the catch
|
||||
// block while we are searching the catch block's successors for a call
|
||||
// to end catch intrinsic, then it is possible that it will be legal for
|
||||
// a path through this block to never reach a call to llvm.eh.endcatch.
|
||||
// An analogous statement could be made about our search for a landing
|
||||
// pad among the catch block's predecessors.
|
||||
//
|
||||
// What is actually required is that no path is possible at runtime that
|
||||
// reaches a call to llvm.eh.begincatch without having previously visited
|
||||
// a landingpad instruction and that no path is possible at runtime that
|
||||
// calls llvm.eh.begincatch and does not subsequently call llvm.eh.endcatch
|
||||
// (mentally adjusting for the fact that in reality these calls will be
|
||||
// removed before code generation).
|
||||
//
|
||||
// Because this is a lint check, we take a pessimistic approach and warn if
|
||||
// the control flow is potentially incorrect.
|
||||
|
||||
SmallSet<BasicBlock *, 4> VisitedBlocks;
|
||||
BasicBlock *CatchBB = II->getParent();
|
||||
|
||||
// The begin catch must occur in a landing pad block or all paths
|
||||
// to it must have come from a landing pad.
|
||||
Assert1(allPredsCameFromLandingPad(CatchBB, VisitedBlocks),
|
||||
"llvm.eh.begincatch may be reachable without passing a landingpad",
|
||||
II);
|
||||
|
||||
// Reset the visited block list.
|
||||
VisitedBlocks.clear();
|
||||
|
||||
IntrinsicInst *SecondBeginCatch = nullptr;
|
||||
|
||||
// This has to be called before it is asserted. Otherwise, the first assert
|
||||
// below can never be hit.
|
||||
bool EndCatchFound = allSuccessorsReachEndCatch(
|
||||
CatchBB, std::next(static_cast<BasicBlock::iterator>(II)),
|
||||
&SecondBeginCatch, VisitedBlocks);
|
||||
Assert2(
|
||||
SecondBeginCatch == nullptr,
|
||||
"llvm.eh.begincatch may be called a second time before llvm.eh.endcatch",
|
||||
II, SecondBeginCatch);
|
||||
Assert1(EndCatchFound,
|
||||
"Some paths from llvm.eh.begincatch may not reach llvm.eh.endcatch",
|
||||
II);
|
||||
}
|
||||
|
||||
static bool allPredCameFromBeginCatch(
|
||||
BasicBlock *BB, BasicBlock::reverse_iterator InstRbegin,
|
||||
IntrinsicInst **SecondEndCatch, SmallSet<BasicBlock *, 4> &VisitedBlocks) {
|
||||
VisitedBlocks.insert(BB);
|
||||
// Look for a begincatch in this block.
|
||||
for (BasicBlock::reverse_iterator RI = InstRbegin, RE = BB->rend(); RI != RE;
|
||||
++RI) {
|
||||
IntrinsicInst *IC = dyn_cast<IntrinsicInst>(&*RI);
|
||||
if (IC && IC->getIntrinsicID() == Intrinsic::eh_begincatch)
|
||||
return true;
|
||||
// If we find another end catch before we find a begin catch, that's
|
||||
// an error.
|
||||
if (IC && IC->getIntrinsicID() == Intrinsic::eh_endcatch) {
|
||||
*SecondEndCatch = IC;
|
||||
return false;
|
||||
}
|
||||
// If we encounter a landingpad instruction, the search failed.
|
||||
if (isa<LandingPadInst>(*RI))
|
||||
return false;
|
||||
}
|
||||
// If while searching we find a block with no predeccesors,
|
||||
// the search failed.
|
||||
if (pred_empty(BB))
|
||||
return false;
|
||||
// Search any predecessors we haven't seen before.
|
||||
for (BasicBlock *Pred : predecessors(BB)) {
|
||||
if (VisitedBlocks.count(Pred))
|
||||
continue;
|
||||
if (!allPredCameFromBeginCatch(Pred, Pred->rbegin(), SecondEndCatch,
|
||||
VisitedBlocks))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Lint::visitEHEndCatch(IntrinsicInst *II) {
|
||||
// The check in this function makes a potentially dubious assumption about
|
||||
// the CFG, namely that any block involved in a catch is only used for the
|
||||
// catch. This will very likely be true of IR generated by a front end,
|
||||
// but it may cease to be true, for example, if the IR is run through a
|
||||
// pass which combines similar blocks.
|
||||
//
|
||||
// In general, if we encounter a block the isn't post-dominated by the
|
||||
// end catch block while we are searching the end catch block's predecessors
|
||||
// for a call to the begin catch intrinsic, then it is possible that it will
|
||||
// be legal for a path to reach the end catch block without ever having
|
||||
// called llvm.eh.begincatch.
|
||||
//
|
||||
// What is actually required is that no path is possible at runtime that
|
||||
// reaches a call to llvm.eh.endcatch without having previously visited
|
||||
// a call to llvm.eh.begincatch (mentally adjusting for the fact that in
|
||||
// reality these calls will be removed before code generation).
|
||||
//
|
||||
// Because this is a lint check, we take a pessimistic approach and warn if
|
||||
// the control flow is potentially incorrect.
|
||||
|
||||
BasicBlock *EndCatchBB = II->getParent();
|
||||
|
||||
// Alls paths to the end catch call must pass through a begin catch call.
|
||||
|
||||
// If llvm.eh.begincatch wasn't called in the current block, we'll use this
|
||||
// lambda to recursively look for it in predecessors.
|
||||
SmallSet<BasicBlock *, 4> VisitedBlocks;
|
||||
IntrinsicInst *SecondEndCatch = nullptr;
|
||||
|
||||
// This has to be called before it is asserted. Otherwise, the first assert
|
||||
// below can never be hit.
|
||||
bool BeginCatchFound =
|
||||
allPredCameFromBeginCatch(EndCatchBB, BasicBlock::reverse_iterator(II),
|
||||
&SecondEndCatch, VisitedBlocks);
|
||||
Assert2(
|
||||
SecondEndCatch == nullptr,
|
||||
"llvm.eh.endcatch may be called a second time after llvm.eh.begincatch",
|
||||
II, SecondEndCatch);
|
||||
Assert1(
|
||||
BeginCatchFound,
|
||||
"llvm.eh.endcatch may be reachable without passing llvm.eh.begincatch",
|
||||
II);
|
||||
}
|
||||
|
||||
static bool isZero(Value *V, const DataLayout *DL, DominatorTree *DT,
|
||||
AssumptionCache *AC) {
|
||||
// Assume undef could be zero.
|
||||
|
@ -5670,6 +5670,9 @@ SelectionDAGBuilder::visitIntrinsicCall(const CallInst &I, unsigned Intrinsic) {
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
case Intrinsic::eh_begincatch:
|
||||
case Intrinsic::eh_endcatch:
|
||||
llvm_unreachable("begin/end catch intrinsics not lowered in codegen");
|
||||
}
|
||||
}
|
||||
|
||||
|
109
test/Analysis/Lint/cppeh-catch-intrinsics-clean.ll
Normal file
109
test/Analysis/Lint/cppeh-catch-intrinsics-clean.ll
Normal file
@ -0,0 +1,109 @@
|
||||
; RUN: opt -lint -disable-output < %s
|
||||
|
||||
; This test is meant to prove that the verifier does not report errors for correct
|
||||
; use of the llvm.eh.begincatch and llvm.eh.endcatch intrinsics.
|
||||
|
||||
target triple = "x86_64-pc-windows-msvc"
|
||||
|
||||
declare i8* @llvm.eh.begincatch(i8*)
|
||||
|
||||
declare void @llvm.eh.endcatch()
|
||||
|
||||
@_ZTIi = external constant i8*
|
||||
|
||||
; Function Attrs: uwtable
|
||||
define void @test_ref_clean() {
|
||||
entry:
|
||||
invoke void @_Z9may_throwv()
|
||||
to label %try.cont unwind label %lpad
|
||||
|
||||
lpad: ; preds = %entry
|
||||
%0 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*)
|
||||
catch i8* bitcast (i8** @_ZTIi to i8*)
|
||||
%exn = extractvalue { i8*, i32 } %0, 0
|
||||
%sel = extractvalue { i8*, i32 } %0, 1
|
||||
%1 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*))
|
||||
%matches = icmp eq i32 %sel, %1
|
||||
br i1 %matches, label %catch, label %eh.resume
|
||||
|
||||
catch: ; preds = %lpad
|
||||
%2 = call i8* @llvm.eh.begincatch(i8* %exn)
|
||||
call void @_Z10handle_intv()
|
||||
br label %invoke.cont2
|
||||
|
||||
invoke.cont2: ; preds = %catch
|
||||
call void @llvm.eh.endcatch()
|
||||
br label %try.cont
|
||||
|
||||
try.cont: ; preds = %invoke.cont2, %entry
|
||||
ret void
|
||||
|
||||
eh.resume: ; preds = %catch.dispatch
|
||||
resume { i8*, i32 } %0
|
||||
}
|
||||
|
||||
; Function Attrs: uwtable
|
||||
define void @test_ref_clean_multibranch() {
|
||||
entry:
|
||||
invoke void @_Z9may_throwv()
|
||||
to label %invoke.cont unwind label %lpad
|
||||
|
||||
invoke.cont:
|
||||
invoke void @_Z9may_throwv()
|
||||
to label %invoke.cont unwind label %lpad1
|
||||
|
||||
lpad: ; preds = %entry
|
||||
%0 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*)
|
||||
catch i8* bitcast (i8** @_ZTIi to i8*)
|
||||
%exn = extractvalue { i8*, i32 } %0, 0
|
||||
%sel = extractvalue { i8*, i32 } %0, 1
|
||||
%1 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*))
|
||||
%matches = icmp eq i32 %sel, %1
|
||||
br i1 %matches, label %catch, label %eh.resume
|
||||
|
||||
invoke void @_Z9may_throwv()
|
||||
to label %try.cont unwind label %lpad
|
||||
|
||||
lpad1: ; preds = %entry
|
||||
%l1.0 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*)
|
||||
cleanup
|
||||
catch i8* bitcast (i8** @_ZTIi to i8*)
|
||||
%exn1 = extractvalue { i8*, i32 } %l1.0, 0
|
||||
%sel1 = extractvalue { i8*, i32 } %l1.0, 1
|
||||
%l1.1 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*))
|
||||
%matchesl1 = icmp eq i32 %sel1, %l1.1
|
||||
br i1 %matchesl1, label %catch, label %eh.resume
|
||||
|
||||
catch: ; preds = %lpad, %lpad1
|
||||
%exn2 = phi i8* [%exn, %lpad], [%exn1, %lpad1]
|
||||
%sel2 = phi i32 [%sel, %lpad], [%sel1, %lpad1]
|
||||
%3 = call i8* @llvm.eh.begincatch(i8* %exn2)
|
||||
call void @_Z10handle_intv()
|
||||
%matches1 = icmp eq i32 %sel2, 0
|
||||
br i1 %matches1, label %invoke.cont2, label %invoke.cont3
|
||||
|
||||
invoke.cont2: ; preds = %catch
|
||||
call void @llvm.eh.endcatch()
|
||||
br label %try.cont
|
||||
|
||||
invoke.cont3: ; preds = %catch
|
||||
call void @llvm.eh.endcatch()
|
||||
br label %eh.resume
|
||||
|
||||
try.cont: ; preds = %invoke.cont2, %entry
|
||||
ret void
|
||||
|
||||
eh.resume: ; preds = %catch.dispatch
|
||||
%lpad.val = insertvalue { i8*, i32 } undef, i32 0, 1
|
||||
resume { i8*, i32 } %lpad.val
|
||||
}
|
||||
|
||||
declare void @_Z9may_throwv()
|
||||
|
||||
declare i32 @__CxxFrameHandler3(...)
|
||||
|
||||
; Function Attrs: nounwind readnone
|
||||
declare i32 @llvm.eh.typeid.for(i8*)
|
||||
|
||||
declare void @_Z10handle_intv()
|
||||
|
278
test/Analysis/Lint/cppeh-catch-intrinsics.ll
Normal file
278
test/Analysis/Lint/cppeh-catch-intrinsics.ll
Normal file
@ -0,0 +1,278 @@
|
||||
; RUN: opt -lint -disable-output < %s 2>&1 | FileCheck %s
|
||||
|
||||
; This test is meant to prove that the Verifier is able to identify a variety
|
||||
; of errors with the llvm.eh.begincatch and llvm.eh.endcatch intrinsics.
|
||||
; See cppeh-catch-intrinsics-clean for correct uses.
|
||||
|
||||
target triple = "x86_64-pc-windows-msvc"
|
||||
|
||||
declare i8* @llvm.eh.begincatch(i8*)
|
||||
|
||||
declare void @llvm.eh.endcatch()
|
||||
|
||||
@_ZTIi = external constant i8*
|
||||
|
||||
; Function Attrs: uwtable
|
||||
define void @test_missing_endcatch() {
|
||||
; CHECK: Some paths from llvm.eh.begincatch may not reach llvm.eh.endcatch
|
||||
; CHECK-NEXT: %2 = call i8* @llvm.eh.begincatch(i8* %exn)
|
||||
entry:
|
||||
invoke void @_Z9may_throwv()
|
||||
to label %try.cont unwind label %lpad
|
||||
|
||||
lpad: ; preds = %entry
|
||||
%0 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*)
|
||||
catch i8* bitcast (i8** @_ZTIi to i8*)
|
||||
%exn = extractvalue { i8*, i32 } %0, 0
|
||||
%sel = extractvalue { i8*, i32 } %0, 1
|
||||
%1 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*))
|
||||
%matches = icmp eq i32 %sel, %1
|
||||
br i1 %matches, label %catch, label %eh.resume
|
||||
|
||||
catch: ; preds = %lpad
|
||||
%2 = call i8* @llvm.eh.begincatch(i8* %exn)
|
||||
call void @_Z10handle_intv()
|
||||
br label %invoke.cont2
|
||||
|
||||
invoke.cont2: ; preds = %catch
|
||||
br label %try.cont
|
||||
|
||||
try.cont: ; preds = %invoke.cont2, %entry
|
||||
ret void
|
||||
|
||||
eh.resume: ; preds = %catch.dispatch
|
||||
resume { i8*, i32 } %0
|
||||
}
|
||||
|
||||
; Function Attrs: uwtable
|
||||
define void @test_missing_begincatch() {
|
||||
; CHECK: llvm.eh.endcatch may be reachable without passing llvm.eh.begincatch
|
||||
; CHECK-NEXT: call void @llvm.eh.endcatch()
|
||||
entry:
|
||||
invoke void @_Z9may_throwv()
|
||||
to label %try.cont unwind label %lpad
|
||||
|
||||
lpad: ; preds = %entry
|
||||
%0 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*)
|
||||
catch i8* bitcast (i8** @_ZTIi to i8*)
|
||||
%exn = extractvalue { i8*, i32 } %0, 0
|
||||
%sel = extractvalue { i8*, i32 } %0, 1
|
||||
%1 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*))
|
||||
%matches = icmp eq i32 %sel, %1
|
||||
br i1 %matches, label %catch, label %eh.resume
|
||||
|
||||
catch: ; preds = %lpad
|
||||
call void @_Z10handle_intv()
|
||||
br label %invoke.cont2
|
||||
|
||||
invoke.cont2: ; preds = %catch
|
||||
call void @llvm.eh.endcatch()
|
||||
br label %try.cont
|
||||
|
||||
try.cont: ; preds = %invoke.cont2, %entry
|
||||
ret void
|
||||
|
||||
eh.resume: ; preds = %catch.dispatch
|
||||
resume { i8*, i32 } %0
|
||||
}
|
||||
|
||||
; Function Attrs: uwtable
|
||||
define void @test_multiple_begin() {
|
||||
; CHECK: llvm.eh.begincatch may be called a second time before llvm.eh.endcatch
|
||||
; CHECK-NEXT: %2 = call i8* @llvm.eh.begincatch(i8* %exn)
|
||||
; CHECK-NEXT: %3 = call i8* @llvm.eh.begincatch(i8* %exn)
|
||||
entry:
|
||||
invoke void @_Z9may_throwv()
|
||||
to label %try.cont unwind label %lpad
|
||||
|
||||
lpad: ; preds = %entry
|
||||
%0 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*)
|
||||
catch i8* bitcast (i8** @_ZTIi to i8*)
|
||||
%exn = extractvalue { i8*, i32 } %0, 0
|
||||
%sel = extractvalue { i8*, i32 } %0, 1
|
||||
%1 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*))
|
||||
%matches = icmp eq i32 %sel, %1
|
||||
br i1 %matches, label %catch, label %eh.resume
|
||||
|
||||
catch: ; preds = %lpad
|
||||
%2 = call i8* @llvm.eh.begincatch(i8* %exn)
|
||||
call void @_Z10handle_intv()
|
||||
br label %invoke.cont2
|
||||
|
||||
invoke.cont2: ; preds = %catch
|
||||
%3 = call i8* @llvm.eh.begincatch(i8* %exn)
|
||||
call void @llvm.eh.endcatch()
|
||||
br label %try.cont
|
||||
|
||||
try.cont: ; preds = %invoke.cont2, %entry
|
||||
ret void
|
||||
|
||||
eh.resume: ; preds = %catch.dispatch
|
||||
resume { i8*, i32 } %0
|
||||
}
|
||||
|
||||
; Function Attrs: uwtable
|
||||
define void @test_multiple_end() {
|
||||
; CHECK: llvm.eh.endcatch may be called a second time after llvm.eh.begincatch
|
||||
; CHECK-NEXT: call void @llvm.eh.endcatch()
|
||||
; CHECK-NEXT: call void @llvm.eh.endcatch()
|
||||
entry:
|
||||
invoke void @_Z9may_throwv()
|
||||
to label %try.cont unwind label %lpad
|
||||
|
||||
lpad: ; preds = %entry
|
||||
%0 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*)
|
||||
catch i8* bitcast (i8** @_ZTIi to i8*)
|
||||
%exn = extractvalue { i8*, i32 } %0, 0
|
||||
%sel = extractvalue { i8*, i32 } %0, 1
|
||||
%1 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*))
|
||||
%matches = icmp eq i32 %sel, %1
|
||||
br i1 %matches, label %catch, label %eh.resume
|
||||
|
||||
catch: ; preds = %lpad
|
||||
%2 = call i8* @llvm.eh.begincatch(i8* %exn)
|
||||
call void @_Z10handle_intv()
|
||||
call void @llvm.eh.endcatch()
|
||||
br label %invoke.cont2
|
||||
|
||||
invoke.cont2: ; preds = %catch
|
||||
call void @llvm.eh.endcatch()
|
||||
br label %try.cont
|
||||
|
||||
try.cont: ; preds = %invoke.cont2, %entry
|
||||
ret void
|
||||
|
||||
eh.resume: ; preds = %catch.dispatch
|
||||
resume { i8*, i32 } %0
|
||||
}
|
||||
|
||||
|
||||
; Function Attrs: uwtable
|
||||
define void @test_begincatch_without_lpad() {
|
||||
; CHECK: llvm.eh.begincatch may be reachable without passing a landingpad
|
||||
; CHECK-NEXT: %0 = call i8* @llvm.eh.begincatch(i8* %exn)
|
||||
entry:
|
||||
%exn = alloca i8
|
||||
%0 = call i8* @llvm.eh.begincatch(i8* %exn)
|
||||
call void @_Z10handle_intv()
|
||||
br label %invoke.cont2
|
||||
|
||||
invoke.cont2: ; preds = %catch
|
||||
call void @llvm.eh.endcatch()
|
||||
br label %try.cont
|
||||
|
||||
try.cont: ; preds = %invoke.cont2, %entry
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: uwtable
|
||||
define void @test_branch_to_begincatch_with_no_lpad(i32 %fake.sel) {
|
||||
; CHECK: llvm.eh.begincatch may be reachable without passing a landingpad
|
||||
; CHECK-NEXT: %3 = call i8* @llvm.eh.begincatch(i8* %exn2)
|
||||
entry:
|
||||
%fake.exn = alloca i8
|
||||
invoke void @_Z9may_throwv()
|
||||
to label %catch unwind label %lpad
|
||||
|
||||
lpad: ; preds = %entry
|
||||
%0 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*)
|
||||
catch i8* bitcast (i8** @_ZTIi to i8*)
|
||||
%exn = extractvalue { i8*, i32 } %0, 0
|
||||
%sel = extractvalue { i8*, i32 } %0, 1
|
||||
%1 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*))
|
||||
%matches = icmp eq i32 %sel, %1
|
||||
br i1 %matches, label %catch, label %eh.resume
|
||||
|
||||
invoke void @_Z9may_throwv()
|
||||
to label %try.cont unwind label %lpad
|
||||
|
||||
catch: ; preds = %lpad, %entry
|
||||
%exn2 = phi i8* [%exn, %lpad], [%fake.exn, %entry]
|
||||
%sel2 = phi i32 [%sel, %lpad], [%fake.sel, %entry]
|
||||
%3 = call i8* @llvm.eh.begincatch(i8* %exn2)
|
||||
call void @_Z10handle_intv()
|
||||
%matches1 = icmp eq i32 %sel2, 0
|
||||
br i1 %matches1, label %invoke.cont2, label %invoke.cont3
|
||||
|
||||
invoke.cont2: ; preds = %catch
|
||||
call void @llvm.eh.endcatch()
|
||||
br label %try.cont
|
||||
|
||||
invoke.cont3: ; preds = %catch
|
||||
call void @llvm.eh.endcatch()
|
||||
br label %eh.resume
|
||||
|
||||
try.cont: ; preds = %invoke.cont2
|
||||
ret void
|
||||
|
||||
eh.resume: ; preds = %catch.dispatch
|
||||
%lpad.val = insertvalue { i8*, i32 } undef, i32 0, 1
|
||||
resume { i8*, i32 } %lpad.val
|
||||
}
|
||||
|
||||
; Function Attrs: uwtable
|
||||
define void @test_branch_missing_endcatch() {
|
||||
; CHECK: Some paths from llvm.eh.begincatch may not reach llvm.eh.endcatch
|
||||
; CHECK-NEXT: %3 = call i8* @llvm.eh.begincatch(i8* %exn2)
|
||||
entry:
|
||||
invoke void @_Z9may_throwv()
|
||||
to label %invoke.cont unwind label %lpad
|
||||
|
||||
invoke.cont:
|
||||
invoke void @_Z9may_throwv()
|
||||
to label %invoke.cont unwind label %lpad1
|
||||
|
||||
lpad: ; preds = %entry
|
||||
%0 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*)
|
||||
catch i8* bitcast (i8** @_ZTIi to i8*)
|
||||
%exn = extractvalue { i8*, i32 } %0, 0
|
||||
%sel = extractvalue { i8*, i32 } %0, 1
|
||||
%1 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*))
|
||||
%matches = icmp eq i32 %sel, %1
|
||||
br i1 %matches, label %catch, label %eh.resume
|
||||
|
||||
invoke void @_Z9may_throwv()
|
||||
to label %try.cont unwind label %lpad
|
||||
|
||||
lpad1: ; preds = %entry
|
||||
%l1.0 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*)
|
||||
cleanup
|
||||
catch i8* bitcast (i8** @_ZTIi to i8*)
|
||||
%exn1 = extractvalue { i8*, i32 } %l1.0, 0
|
||||
%sel1 = extractvalue { i8*, i32 } %l1.0, 1
|
||||
%l1.1 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*))
|
||||
%matchesl1 = icmp eq i32 %sel1, %l1.1
|
||||
br i1 %matchesl1, label %catch, label %eh.resume
|
||||
|
||||
catch: ; preds = %lpad, %lpad1
|
||||
%exn2 = phi i8* [%exn, %lpad], [%exn1, %lpad1]
|
||||
%sel2 = phi i32 [%sel, %lpad], [%sel1, %lpad1]
|
||||
%3 = call i8* @llvm.eh.begincatch(i8* %exn2)
|
||||
call void @_Z10handle_intv()
|
||||
%matches1 = icmp eq i32 %sel2, 0
|
||||
br i1 %matches1, label %invoke.cont2, label %invoke.cont3
|
||||
|
||||
invoke.cont2: ; preds = %catch
|
||||
call void @llvm.eh.endcatch()
|
||||
br label %try.cont
|
||||
|
||||
invoke.cont3: ; preds = %catch
|
||||
br label %eh.resume
|
||||
|
||||
try.cont: ; preds = %invoke.cont2, %entry
|
||||
ret void
|
||||
|
||||
eh.resume: ; preds = %catch.dispatch
|
||||
%lpad.val = insertvalue { i8*, i32 } undef, i32 0, 1
|
||||
resume { i8*, i32 } %lpad.val
|
||||
}
|
||||
|
||||
declare void @_Z9may_throwv()
|
||||
|
||||
declare i32 @__CxxFrameHandler3(...)
|
||||
|
||||
; Function Attrs: nounwind readnone
|
||||
declare i32 @llvm.eh.typeid.for(i8*)
|
||||
|
||||
declare void @_Z10handle_intv()
|
||||
|
Loading…
Reference in New Issue
Block a user