mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-23 19:23:23 +01:00
Inlining often produces landingpad instructions with repeated
catch or repeated filter clauses. Teach instcombine a bunch of tricks for simplifying landingpad clauses. Currently the code only recognizes the GNU C++ and Ada personality functions, but that doesn't stop it doing a bunch of "generic" transforms which are hopefully fine for any real-world personality function. If these "generic" transforms turn out not to be generic, they can always be conditioned on the personality function. Probably someone should add the ObjC++ personality function. I didn't as I don't know anything about it. llvm-svn: 140852
This commit is contained in:
parent
774d60816b
commit
b4c8b2d9fa
@ -193,6 +193,7 @@ public:
|
||||
Instruction *visitExtractElementInst(ExtractElementInst &EI);
|
||||
Instruction *visitShuffleVectorInst(ShuffleVectorInst &SVI);
|
||||
Instruction *visitExtractValueInst(ExtractValueInst &EV);
|
||||
Instruction *visitLandingPadInst(LandingPadInst &LI);
|
||||
|
||||
// visitInstruction - Specify what to return for unhandled instructions...
|
||||
Instruction *visitInstruction(Instruction &I) { return 0; }
|
||||
|
@ -49,6 +49,7 @@
|
||||
#include "llvm/Support/ValueHandle.h"
|
||||
#include "llvm/ADT/SmallPtrSet.h"
|
||||
#include "llvm/ADT/Statistic.h"
|
||||
#include "llvm/ADT/StringSwitch.h"
|
||||
#include "llvm-c/Initialization.h"
|
||||
#include <algorithm>
|
||||
#include <climits>
|
||||
@ -1413,6 +1414,342 @@ Instruction *InstCombiner::visitExtractValueInst(ExtractValueInst &EV) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
enum Personality_Type {
|
||||
Unknown_Personality,
|
||||
GNU_Ada_Personality,
|
||||
GNU_CXX_Personality
|
||||
};
|
||||
|
||||
/// RecognizePersonality - See if the given exception handling personality
|
||||
/// function is one that we understand. If so, return a description of it;
|
||||
/// otherwise return Unknown_Personality.
|
||||
static Personality_Type RecognizePersonality(Value *Pers) {
|
||||
Function *F = dyn_cast<Function>(Pers->stripPointerCasts());
|
||||
if (!F)
|
||||
return Unknown_Personality;
|
||||
return StringSwitch<Personality_Type>(F->getName())
|
||||
.Case("__gnat_eh_personality", GNU_Ada_Personality)
|
||||
.Case("__gxx_personality_v0", GNU_CXX_Personality)
|
||||
.Default(Unknown_Personality);
|
||||
}
|
||||
|
||||
/// isCatchAll - Return 'true' if the given typeinfo will match anything.
|
||||
static bool isCatchAll(Personality_Type Personality, Constant *TypeInfo) {
|
||||
switch (Personality) {
|
||||
case Unknown_Personality:
|
||||
return false;
|
||||
case GNU_Ada_Personality:
|
||||
// While __gnat_all_others_value will match any Ada exception, it doesn't
|
||||
// match foreign exceptions (or didn't, before gcc-4.7).
|
||||
return false;
|
||||
case GNU_CXX_Personality:
|
||||
return TypeInfo->isNullValue();
|
||||
}
|
||||
llvm_unreachable("Unknown personality!");
|
||||
}
|
||||
|
||||
static bool shorter_filter(const Value *LHS, const Value *RHS) {
|
||||
return
|
||||
cast<ArrayType>(LHS->getType())->getNumElements()
|
||||
<
|
||||
cast<ArrayType>(RHS->getType())->getNumElements();
|
||||
}
|
||||
|
||||
Instruction *InstCombiner::visitLandingPadInst(LandingPadInst &LI) {
|
||||
// The logic here should be correct for any real-world personality function.
|
||||
// However if that turns out not to be true, the offending logic can always
|
||||
// be conditioned on the personality function, like the catch-all logic is.
|
||||
Personality_Type Personality = RecognizePersonality(LI.getPersonalityFn());
|
||||
|
||||
// Simplify the list of clauses, eg by removing repeated catch clauses
|
||||
// (these are often created by inlining).
|
||||
bool MakeNewInstruction = false; // If true, recreate using the following:
|
||||
SmallVector<Value *, 16> NewClauses; // - Clauses for the new instruction;
|
||||
bool CleanupFlag = LI.isCleanup(); // - The new instruction is a cleanup.
|
||||
|
||||
SmallPtrSet<Value *, 16> AlreadyCaught; // Typeinfos known caught already.
|
||||
for (unsigned i = 0, e = LI.getNumClauses(); i != e; ++i) {
|
||||
bool isLastClause = i + 1 == e;
|
||||
if (LI.isCatch(i)) {
|
||||
// A catch clause.
|
||||
Value *CatchClause = LI.getClause(i);
|
||||
Constant *TypeInfo = cast<Constant>(CatchClause->stripPointerCasts());
|
||||
|
||||
// If we already saw this clause, there is no point in having a second
|
||||
// copy of it.
|
||||
if (AlreadyCaught.insert(TypeInfo)) {
|
||||
// This catch clause was not already seen.
|
||||
NewClauses.push_back(CatchClause);
|
||||
} else {
|
||||
// Repeated catch clause - drop the redundant copy.
|
||||
MakeNewInstruction = true;
|
||||
}
|
||||
|
||||
// If this is a catch-all then there is no point in keeping any following
|
||||
// clauses or marking the landingpad as having a cleanup.
|
||||
if (isCatchAll(Personality, TypeInfo)) {
|
||||
if (!isLastClause)
|
||||
MakeNewInstruction = true;
|
||||
CleanupFlag = false;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// A filter clause. If any of the filter elements were already caught
|
||||
// then they can be dropped from the filter. It is tempting to try to
|
||||
// exploit the filter further by saying that any typeinfo that does not
|
||||
// occur in the filter can't be caught later (and thus can be dropped).
|
||||
// However this would be wrong, since typeinfos can match without being
|
||||
// equal (for example if one represents a C++ class, and the other some
|
||||
// class derived from it).
|
||||
assert(LI.isFilter(i) && "Unsupported landingpad clause!");
|
||||
Value *FilterClause = LI.getClause(i);
|
||||
ArrayType *FilterType = cast<ArrayType>(FilterClause->getType());
|
||||
unsigned NumTypeInfos = FilterType->getNumElements();
|
||||
|
||||
// An empty filter catches everything, so there is no point in keeping any
|
||||
// following clauses or marking the landingpad as having a cleanup. By
|
||||
// dealing with this case here the following code is made a bit simpler.
|
||||
if (!NumTypeInfos) {
|
||||
NewClauses.push_back(FilterClause);
|
||||
if (!isLastClause)
|
||||
MakeNewInstruction = true;
|
||||
CleanupFlag = false;
|
||||
break;
|
||||
}
|
||||
|
||||
bool MakeNewFilter = false; // If true, make a new filter.
|
||||
SmallVector<Constant *, 16> NewFilterElts; // New elements.
|
||||
if (isa<ConstantAggregateZero>(FilterClause)) {
|
||||
// Not an empty filter - it contains at least one null typeinfo.
|
||||
assert(NumTypeInfos > 0 && "Should have handled empty filter already!");
|
||||
Constant *TypeInfo =
|
||||
Constant::getNullValue(FilterType->getElementType());
|
||||
// If this typeinfo is a catch-all then the filter can never match.
|
||||
if (isCatchAll(Personality, TypeInfo)) {
|
||||
// Throw the filter away.
|
||||
MakeNewInstruction = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// There is no point in having multiple copies of this typeinfo, so
|
||||
// discard all but the first copy if there is more than one.
|
||||
NewFilterElts.push_back(TypeInfo);
|
||||
if (NumTypeInfos > 1)
|
||||
MakeNewFilter = true;
|
||||
} else {
|
||||
ConstantArray *Filter = cast<ConstantArray>(FilterClause);
|
||||
SmallPtrSet<Value *, 16> SeenInFilter; // For uniquing the elements.
|
||||
NewFilterElts.reserve(NumTypeInfos);
|
||||
|
||||
// Remove any filter elements that were already caught or that already
|
||||
// occurred in the filter. While there, see if any of the elements are
|
||||
// catch-alls. If so, the filter can be discarded.
|
||||
bool SawCatchAll = false;
|
||||
for (unsigned j = 0; j != NumTypeInfos; ++j) {
|
||||
Value *Elt = Filter->getOperand(j);
|
||||
Constant *TypeInfo = cast<Constant>(Elt->stripPointerCasts());
|
||||
if (isCatchAll(Personality, TypeInfo)) {
|
||||
// This element is a catch-all. Bail out, noting this fact.
|
||||
SawCatchAll = true;
|
||||
break;
|
||||
}
|
||||
if (AlreadyCaught.count(TypeInfo))
|
||||
// Already caught by an earlier clause, so having it in the filter
|
||||
// is pointless.
|
||||
continue;
|
||||
// There is no point in having multiple copies of the same typeinfo in
|
||||
// a filter, so only add it if we didn't already.
|
||||
if (SeenInFilter.insert(TypeInfo))
|
||||
NewFilterElts.push_back(cast<Constant>(Elt));
|
||||
}
|
||||
// A filter containing a catch-all cannot match anything by definition.
|
||||
if (SawCatchAll) {
|
||||
// Throw the filter away.
|
||||
MakeNewInstruction = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we dropped something from the filter, make a new one.
|
||||
if (NewFilterElts.size() < NumTypeInfos)
|
||||
MakeNewFilter = true;
|
||||
}
|
||||
if (MakeNewFilter) {
|
||||
FilterType = ArrayType::get(FilterType->getElementType(),
|
||||
NewFilterElts.size());
|
||||
FilterClause = ConstantArray::get(FilterType, NewFilterElts);
|
||||
MakeNewInstruction = true;
|
||||
}
|
||||
|
||||
NewClauses.push_back(FilterClause);
|
||||
|
||||
// If the new filter is empty then it will catch everything so there is
|
||||
// no point in keeping any following clauses or marking the landingpad
|
||||
// as having a cleanup. The case of the original filter being empty was
|
||||
// already handled above.
|
||||
if (MakeNewFilter && !NewFilterElts.size()) {
|
||||
assert(MakeNewInstruction && "New filter but not a new instruction!");
|
||||
CleanupFlag = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If several filters occur in a row then reorder them so that the shortest
|
||||
// filters come first (those with the smallest number of elements). This is
|
||||
// advantageous because shorter filters are more likely to match, speeding up
|
||||
// unwinding, but mostly because it increases the effectiveness of the other
|
||||
// filter optimizations below.
|
||||
for (unsigned i = 0, e = NewClauses.size(); i + 1 < e; ) {
|
||||
unsigned j;
|
||||
// Find the maximal 'j' s.t. the range [i, j) consists entirely of filters.
|
||||
for (j = i; j != e; ++j)
|
||||
if (!isa<ArrayType>(NewClauses[j]->getType()))
|
||||
break;
|
||||
|
||||
// Check whether the filters are already sorted by length. We need to know
|
||||
// if sorting them is actually going to do anything so that we only make a
|
||||
// new landingpad instruction if it does.
|
||||
for (unsigned k = i; k + 1 < j; ++k)
|
||||
if (shorter_filter(NewClauses[k+1], NewClauses[k])) {
|
||||
// Not sorted, so sort the filters now. Doing an unstable sort would be
|
||||
// correct too but reordering filters pointlessly might confuse users.
|
||||
std::stable_sort(NewClauses.begin() + i, NewClauses.begin() + j,
|
||||
shorter_filter);
|
||||
MakeNewInstruction = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// Look for the next batch of filters.
|
||||
i = j + 1;
|
||||
}
|
||||
|
||||
// If typeinfos matched if and only if equal, then the elements of a filter L
|
||||
// that occurs later than a filter F could be replaced by the intersection of
|
||||
// the elements of F and L. In reality two typeinfos can match without being
|
||||
// equal (for example if one represents a C++ class, and the other some class
|
||||
// derived from it) so it would be wrong to perform this transform in general.
|
||||
// However the transform is correct and useful if F is a subset of L. In that
|
||||
// case L can be replaced by F, and thus removed altogether since repeating a
|
||||
// filter is pointless. So here we look at all pairs of filters F and L where
|
||||
// L follows F in the list of clauses, and remove L if every element of F is
|
||||
// an element of L. This can occur when inlining C++ functions with exception
|
||||
// specifications.
|
||||
for (unsigned i = 0; i + 1 < NewClauses.size(); ++i) {
|
||||
// Examine each filter in turn.
|
||||
Value *Filter = NewClauses[i];
|
||||
ArrayType *FTy = dyn_cast<ArrayType>(Filter->getType());
|
||||
if (!FTy)
|
||||
// Not a filter - skip it.
|
||||
continue;
|
||||
unsigned FElts = FTy->getNumElements();
|
||||
// Examine each filter following this one. Doing this backwards means that
|
||||
// we don't have to worry about filters disappearing under us when removed.
|
||||
for (unsigned j = NewClauses.size() - 1; j != i; --j) {
|
||||
Value *LFilter = NewClauses[j];
|
||||
ArrayType *LTy = dyn_cast<ArrayType>(LFilter->getType());
|
||||
if (!LTy)
|
||||
// Not a filter - skip it.
|
||||
continue;
|
||||
// If Filter is a subset of LFilter, i.e. every element of Filter is also
|
||||
// an element of LFilter, then discard LFilter.
|
||||
SmallVector<Value *, 16>::iterator J = NewClauses.begin() + j;
|
||||
// If Filter is empty then it is a subset of LFilter.
|
||||
if (!FElts) {
|
||||
// Discard LFilter.
|
||||
NewClauses.erase(J);
|
||||
MakeNewInstruction = true;
|
||||
// Move on to the next filter.
|
||||
continue;
|
||||
}
|
||||
unsigned LElts = LTy->getNumElements();
|
||||
// If Filter is longer than LFilter then it cannot be a subset of it.
|
||||
if (FElts > LElts)
|
||||
// Move on to the next filter.
|
||||
continue;
|
||||
// At this point we know that LFilter has at least one element.
|
||||
if (isa<ConstantAggregateZero>(LFilter)) { // LFilter only contains zeros.
|
||||
// Filter is a subset of LFilter iff Filter contains only zeros (as we
|
||||
// already know that Filter is not longer than LFilter).
|
||||
if (isa<ConstantAggregateZero>(Filter)) {
|
||||
assert(FElts <= LElts && "Should have handled this case earlier!");
|
||||
// Discard LFilter.
|
||||
NewClauses.erase(J);
|
||||
MakeNewInstruction = true;
|
||||
}
|
||||
// Move on to the next filter.
|
||||
continue;
|
||||
}
|
||||
ConstantArray *LArray = cast<ConstantArray>(LFilter);
|
||||
if (isa<ConstantAggregateZero>(Filter)) { // Filter only contains zeros.
|
||||
// Since Filter is non-empty and contains only zeros, it is a subset of
|
||||
// LFilter iff LFilter contains a zero.
|
||||
assert(FElts > 0 && "Should have eliminated the empty filter earlier!");
|
||||
for (unsigned l = 0; l != LElts; ++l)
|
||||
if (LArray->getOperand(l)->isNullValue()) {
|
||||
// LFilter contains a zero - discard it.
|
||||
NewClauses.erase(J);
|
||||
MakeNewInstruction = true;
|
||||
break;
|
||||
}
|
||||
// Move on to the next filter.
|
||||
continue;
|
||||
}
|
||||
// At this point we know that both filters are ConstantArrays. Loop over
|
||||
// operands to see whether every element of Filter is also an element of
|
||||
// LFilter. Since filters tend to be short this is probably faster than
|
||||
// using a method that scales nicely.
|
||||
ConstantArray *FArray = cast<ConstantArray>(Filter);
|
||||
bool AllFound = true;
|
||||
for (unsigned f = 0; f != FElts; ++f) {
|
||||
Value *FTypeInfo = FArray->getOperand(f)->stripPointerCasts();
|
||||
AllFound = false;
|
||||
for (unsigned l = 0; l != LElts; ++l) {
|
||||
Value *LTypeInfo = LArray->getOperand(l)->stripPointerCasts();
|
||||
if (LTypeInfo == FTypeInfo) {
|
||||
AllFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!AllFound)
|
||||
break;
|
||||
}
|
||||
if (AllFound) {
|
||||
// Discard LFilter.
|
||||
NewClauses.erase(J);
|
||||
MakeNewInstruction = true;
|
||||
}
|
||||
// Move on to the next filter.
|
||||
}
|
||||
}
|
||||
|
||||
// If we changed any of the clauses, replace the old landingpad instruction
|
||||
// with a new one.
|
||||
if (MakeNewInstruction) {
|
||||
LandingPadInst *NLI = LandingPadInst::Create(LI.getType(),
|
||||
LI.getPersonalityFn(),
|
||||
NewClauses.size());
|
||||
for (unsigned i = 0, e = NewClauses.size(); i != e; ++i)
|
||||
NLI->addClause(NewClauses[i]);
|
||||
// A landing pad with no clauses must have the cleanup flag set. It is
|
||||
// theoretically possible, though highly unlikely, that we eliminated all
|
||||
// clauses. If so, force the cleanup flag to true.
|
||||
if (NewClauses.empty())
|
||||
CleanupFlag = true;
|
||||
NLI->setCleanup(CleanupFlag);
|
||||
return NLI;
|
||||
}
|
||||
|
||||
// Even if none of the clauses changed, we may nonetheless have understood
|
||||
// that the cleanup flag is pointless. Clear it if so.
|
||||
if (LI.isCleanup() != CleanupFlag) {
|
||||
assert(!CleanupFlag && "Adding a cleanup, not removing one?!");
|
||||
LI.setCleanup(CleanupFlag);
|
||||
return &LI;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
157
test/Transforms/InstCombine/LandingPadClauses.ll
Normal file
157
test/Transforms/InstCombine/LandingPadClauses.ll
Normal file
@ -0,0 +1,157 @@
|
||||
; RUN: opt < %s -instcombine -S | FileCheck %s
|
||||
|
||||
@T1 = external constant i32
|
||||
@T2 = external constant i32
|
||||
@T3 = external constant i32
|
||||
|
||||
declare i32 @generic_personality(i32, i64, i8*, i8*)
|
||||
declare i32 @__gxx_personality_v0(i32, i64, i8*, i8*)
|
||||
|
||||
declare void @bar()
|
||||
|
||||
define void @foo_generic() {
|
||||
; CHECK: @foo_generic
|
||||
invoke void @bar()
|
||||
to label %cont.a unwind label %lpad.a
|
||||
cont.a:
|
||||
invoke void @bar()
|
||||
to label %cont.b unwind label %lpad.b
|
||||
cont.b:
|
||||
invoke void @bar()
|
||||
to label %cont.c unwind label %lpad.c
|
||||
cont.c:
|
||||
invoke void @bar()
|
||||
to label %cont.d unwind label %lpad.d
|
||||
cont.d:
|
||||
invoke void @bar()
|
||||
to label %cont.e unwind label %lpad.e
|
||||
cont.e:
|
||||
invoke void @bar()
|
||||
to label %cont.f unwind label %lpad.f
|
||||
cont.f:
|
||||
invoke void @bar()
|
||||
to label %cont.g unwind label %lpad.g
|
||||
cont.g:
|
||||
invoke void @bar()
|
||||
to label %cont.h unwind label %lpad.h
|
||||
cont.h:
|
||||
ret void
|
||||
|
||||
lpad.a:
|
||||
%a = landingpad { i8*, i32 } personality i32 (i32, i64, i8*, i8*)* @generic_personality
|
||||
catch i32* @T1
|
||||
catch i32* @T2
|
||||
catch i32* @T1
|
||||
catch i32* @T2
|
||||
unreachable
|
||||
; CHECK: %a = landingpad
|
||||
; CHECK-NEXT: @T1
|
||||
; CHECK-NEXT: @T2
|
||||
; CHECK-NEXT: unreachable
|
||||
|
||||
lpad.b:
|
||||
%b = landingpad { i8*, i32 } personality i32 (i32, i64, i8*, i8*)* @generic_personality
|
||||
filter [0 x i32*] zeroinitializer
|
||||
catch i32* @T1
|
||||
unreachable
|
||||
; CHECK: %b = landingpad
|
||||
; CHECK-NEXT: filter
|
||||
; CHECK-NEXT: unreachable
|
||||
|
||||
lpad.c:
|
||||
%c = landingpad { i8*, i32 } personality i32 (i32, i64, i8*, i8*)* @generic_personality
|
||||
catch i32* @T1
|
||||
filter [1 x i32*] [i32* @T1]
|
||||
catch i32* @T2
|
||||
unreachable
|
||||
; CHECK: %c = landingpad
|
||||
; CHECK-NEXT: @T1
|
||||
; CHECK-NEXT: filter [0 x i32*]
|
||||
; CHECK-NEXT: unreachable
|
||||
|
||||
lpad.d:
|
||||
%d = landingpad { i8*, i32 } personality i32 (i32, i64, i8*, i8*)* @generic_personality
|
||||
filter [3 x i32*] zeroinitializer
|
||||
unreachable
|
||||
; CHECK: %d = landingpad
|
||||
; CHECK-NEXT: filter [1 x i32*] zeroinitializer
|
||||
; CHECK-NEXT: unreachable
|
||||
|
||||
lpad.e:
|
||||
%e = landingpad { i8*, i32 } personality i32 (i32, i64, i8*, i8*)* @generic_personality
|
||||
catch i32* @T1
|
||||
filter [3 x i32*] [i32* @T1, i32* @T2, i32* @T2]
|
||||
unreachable
|
||||
; CHECK: %e = landingpad
|
||||
; CHECK-NEXT: @T1
|
||||
; CHECK-NEXT: filter [1 x i32*] [i32* @T2]
|
||||
; CHECK-NEXT: unreachable
|
||||
|
||||
lpad.f:
|
||||
%f = landingpad { i8*, i32 } personality i32 (i32, i64, i8*, i8*)* @generic_personality
|
||||
filter [2 x i32*] [i32* @T2, i32* @T1]
|
||||
filter [1 x i32*] [i32* @T1]
|
||||
unreachable
|
||||
; CHECK: %f = landingpad
|
||||
; CHECK-NEXT: filter [1 x i32*] [i32* @T1]
|
||||
; CHECK-NEXT: unreachable
|
||||
|
||||
lpad.g:
|
||||
%g = landingpad { i8*, i32 } personality i32 (i32, i64, i8*, i8*)* @generic_personality
|
||||
filter [1 x i32*] [i32* @T1]
|
||||
catch i32* @T3
|
||||
filter [2 x i32*] [i32* @T2, i32* @T1]
|
||||
unreachable
|
||||
; CHECK: %g = landingpad
|
||||
; CHECK-NEXT: filter [1 x i32*] [i32* @T1]
|
||||
; CHECK-NEXT: catch i32* @T3
|
||||
; CHECK-NEXT: unreachable
|
||||
|
||||
lpad.h:
|
||||
%h = landingpad { i8*, i32 } personality i32 (i32, i64, i8*, i8*)* @generic_personality
|
||||
filter [2 x i32*] [i32* @T1, i32* null]
|
||||
filter [1 x i32*] zeroinitializer
|
||||
unreachable
|
||||
; CHECK: %h = landingpad
|
||||
; CHECK-NEXT: filter [1 x i32*] zeroinitializer
|
||||
; CHECK-NEXT: unreachable
|
||||
}
|
||||
|
||||
define void @foo_cxx() {
|
||||
; CHECK: @foo_cxx
|
||||
invoke void @bar()
|
||||
to label %cont.a unwind label %lpad.a
|
||||
cont.a:
|
||||
invoke void @bar()
|
||||
to label %cont.b unwind label %lpad.b
|
||||
cont.b:
|
||||
invoke void @bar()
|
||||
to label %cont.c unwind label %lpad.c
|
||||
cont.c:
|
||||
ret void
|
||||
|
||||
lpad.a:
|
||||
%a = landingpad { i8*, i32 } personality i32 (i32, i64, i8*, i8*)* @__gxx_personality_v0
|
||||
catch i32* null
|
||||
catch i32* @T1
|
||||
unreachable
|
||||
; CHECK: %a = landingpad
|
||||
; CHECK-NEXT: null
|
||||
; CHECK-NEXT: unreachable
|
||||
|
||||
lpad.b:
|
||||
%b = landingpad { i8*, i32 } personality i32 (i32, i64, i8*, i8*)* @__gxx_personality_v0
|
||||
filter [1 x i32*] zeroinitializer
|
||||
unreachable
|
||||
; CHECK: %b = landingpad
|
||||
; CHECK-NEXT: cleanup
|
||||
; CHECK-NEXT: unreachable
|
||||
|
||||
lpad.c:
|
||||
%c = landingpad { i8*, i32 } personality i32 (i32, i64, i8*, i8*)* @__gxx_personality_v0
|
||||
filter [2 x i32*] [i32* @T1, i32* null]
|
||||
unreachable
|
||||
; CHECK: %c = landingpad
|
||||
; CHECK-NEXT: cleanup
|
||||
; CHECK-NEXT: unreachable
|
||||
}
|
Loading…
Reference in New Issue
Block a user