[IR] Reformulate LLVM's EH funclet IR

While we have successfully implemented a funclet-oriented EH scheme on
top of LLVM IR, our scheme has some notable deficiencies:
- catchendpad and cleanupendpad are necessary in the current design
  but they are difficult to explain to others, even to seasoned LLVM
  experts.
- catchendpad and cleanupendpad are optimization barriers.  They cannot
  be split and force all potentially throwing call-sites to be invokes.
  This has a noticable effect on the quality of our code generation.
- catchpad, while similar in some aspects to invoke, is fairly awkward.
  It is unsplittable, starts a funclet, and has control flow to other
  funclets.
- The nesting relationship between funclets is currently a property of
  control flow edges.  Because of this, we are forced to carefully
  analyze the flow graph to see if there might potentially exist illegal
  nesting among funclets.  While we have logic to clone funclets when
  they are illegally nested, it would be nicer if we had a
  representation which forbade them upfront.

Let's clean this up a bit by doing the following:
- Instead, make catchpad more like cleanuppad and landingpad: no control
  flow, just a bunch of simple operands;  catchpad would be splittable.
- Introduce catchswitch, a control flow instruction designed to model
  the constraints of funclet oriented EH.
- Make funclet scoping explicit by having funclet instructions consume
  the token produced by the funclet which contains them.
- Remove catchendpad and cleanupendpad.  Their presence can be inferred
  implicitly using coloring information.

N.B.  The state numbering code for the CLR has been updated but the
veracity of it's output cannot be spoken for.  An expert should take a
look to make sure the results are reasonable.

Reviewers: rnk, JosephTremoulet, andrew.w.kaylor

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

llvm-svn: 255422
This commit is contained in:
David Majnemer
2015-12-12 05:38:55 +00:00
parent a38312a9a4
commit 8a1c45d6e8
106 changed files with 3135 additions and 6143 deletions

View File

@@ -2315,7 +2315,7 @@ bool LLParser::PerFunctionState::FinishFunction() {
/// forward reference record if needed. This can return null if the value
/// exists but does not have the right type.
Value *LLParser::PerFunctionState::GetVal(const std::string &Name, Type *Ty,
LocTy Loc, OperatorConstraint OC) {
LocTy Loc) {
// Look this name up in the normal function symbol table.
Value *Val = F.getValueSymbolTable().lookup(Name);
@@ -2329,24 +2329,6 @@ Value *LLParser::PerFunctionState::GetVal(const std::string &Name, Type *Ty,
// If we have the value in the symbol table or fwd-ref table, return it.
if (Val) {
// Check operator constraints.
switch (OC) {
case OC_None:
// no constraint
break;
case OC_CatchPad:
if (!isa<CatchPadInst>(Val)) {
P.Error(Loc, "'%" + Name + "' is not a catchpad");
return nullptr;
}
break;
case OC_CleanupPad:
if (!isa<CleanupPadInst>(Val)) {
P.Error(Loc, "'%" + Name + "' is not a cleanuppad");
return nullptr;
}
break;
}
if (Val->getType() == Ty) return Val;
if (Ty->isLabelTy())
P.Error(Loc, "'%" + Name + "' is not a basic block");
@@ -2365,30 +2347,16 @@ Value *LLParser::PerFunctionState::GetVal(const std::string &Name, Type *Ty,
// Otherwise, create a new forward reference for this value and remember it.
Value *FwdVal;
if (Ty->isLabelTy()) {
assert(!OC);
FwdVal = BasicBlock::Create(F.getContext(), Name, &F);
} else if (!OC) {
FwdVal = new Argument(Ty, Name);
} else {
switch (OC) {
case OC_CatchPad:
FwdVal = CatchPadInst::Create(&F.getEntryBlock(), &F.getEntryBlock(), {},
Name);
break;
case OC_CleanupPad:
FwdVal = CleanupPadInst::Create(F.getContext(), {}, Name);
break;
default:
llvm_unreachable("unexpected constraint");
}
FwdVal = new Argument(Ty, Name);
}
ForwardRefVals[Name] = std::make_pair(FwdVal, Loc);
return FwdVal;
}
Value *LLParser::PerFunctionState::GetVal(unsigned ID, Type *Ty, LocTy Loc,
OperatorConstraint OC) {
Value *LLParser::PerFunctionState::GetVal(unsigned ID, Type *Ty, LocTy Loc) {
// Look this name up in the normal function symbol table.
Value *Val = ID < NumberedVals.size() ? NumberedVals[ID] : nullptr;
@@ -2402,24 +2370,6 @@ Value *LLParser::PerFunctionState::GetVal(unsigned ID, Type *Ty, LocTy Loc,
// If we have the value in the symbol table or fwd-ref table, return it.
if (Val) {
// Check operator constraint.
switch (OC) {
case OC_None:
// no constraint
break;
case OC_CatchPad:
if (!isa<CatchPadInst>(Val)) {
P.Error(Loc, "'%" + Twine(ID) + "' is not a catchpad");
return nullptr;
}
break;
case OC_CleanupPad:
if (!isa<CleanupPadInst>(Val)) {
P.Error(Loc, "'%" + Twine(ID) + "' is not a cleanuppad");
return nullptr;
}
break;
}
if (Val->getType() == Ty) return Val;
if (Ty->isLabelTy())
P.Error(Loc, "'%" + Twine(ID) + "' is not a basic block");
@@ -2437,21 +2387,9 @@ Value *LLParser::PerFunctionState::GetVal(unsigned ID, Type *Ty, LocTy Loc,
// Otherwise, create a new forward reference for this value and remember it.
Value *FwdVal;
if (Ty->isLabelTy()) {
assert(!OC);
FwdVal = BasicBlock::Create(F.getContext(), "", &F);
} else if (!OC) {
FwdVal = new Argument(Ty);
} else {
switch (OC) {
case OC_CatchPad:
FwdVal = CatchPadInst::Create(&F.getEntryBlock(), &F.getEntryBlock(), {});
break;
case OC_CleanupPad:
FwdVal = CleanupPadInst::Create(F.getContext(), {});
break;
default:
llvm_unreachable("unexpected constraint");
}
FwdVal = new Argument(Ty);
}
ForwardRefValIDs[ID] = std::make_pair(FwdVal, Loc);
@@ -2487,17 +2425,6 @@ bool LLParser::PerFunctionState::SetInstName(int NameID,
if (Sentinel->getType() != Inst->getType())
return P.Error(NameLoc, "instruction forward referenced with type '" +
getTypeString(FI->second.first->getType()) + "'");
// Check operator constraints. We only put cleanuppads or catchpads in
// the forward value map if the value is constrained to match.
if (isa<CatchPadInst>(Sentinel)) {
if (!isa<CatchPadInst>(Inst))
return P.Error(FI->second.second,
"'%" + Twine(NameID) + "' is not a catchpad");
} else if (isa<CleanupPadInst>(Sentinel)) {
if (!isa<CleanupPadInst>(Inst))
return P.Error(FI->second.second,
"'%" + Twine(NameID) + "' is not a cleanuppad");
}
Sentinel->replaceAllUsesWith(Inst);
delete Sentinel;
@@ -2515,17 +2442,6 @@ bool LLParser::PerFunctionState::SetInstName(int NameID,
if (Sentinel->getType() != Inst->getType())
return P.Error(NameLoc, "instruction forward referenced with type '" +
getTypeString(FI->second.first->getType()) + "'");
// Check operator constraints. We only put cleanuppads or catchpads in
// the forward value map if the value is constrained to match.
if (isa<CatchPadInst>(Sentinel)) {
if (!isa<CatchPadInst>(Inst))
return P.Error(FI->second.second,
"'%" + NameStr + "' is not a catchpad");
} else if (isa<CleanupPadInst>(Sentinel)) {
if (!isa<CleanupPadInst>(Inst))
return P.Error(FI->second.second,
"'%" + NameStr + "' is not a cleanuppad");
}
Sentinel->replaceAllUsesWith(Inst);
delete Sentinel;
@@ -4235,30 +4151,18 @@ bool LLParser::ParseMetadata(Metadata *&MD, PerFunctionState *PFS) {
//===----------------------------------------------------------------------===//
bool LLParser::ConvertValIDToValue(Type *Ty, ValID &ID, Value *&V,
PerFunctionState *PFS,
OperatorConstraint OC) {
PerFunctionState *PFS) {
if (Ty->isFunctionTy())
return Error(ID.Loc, "functions are not values, refer to them as pointers");
if (OC && ID.Kind != ValID::t_LocalID && ID.Kind != ValID::t_LocalName) {
switch (OC) {
case OC_CatchPad:
return Error(ID.Loc, "Catchpad value required in this position");
case OC_CleanupPad:
return Error(ID.Loc, "Cleanuppad value required in this position");
default:
llvm_unreachable("Unexpected constraint kind");
}
}
switch (ID.Kind) {
case ValID::t_LocalID:
if (!PFS) return Error(ID.Loc, "invalid use of function-local name");
V = PFS->GetVal(ID.UIntVal, Ty, ID.Loc, OC);
V = PFS->GetVal(ID.UIntVal, Ty, ID.Loc);
return V == nullptr;
case ValID::t_LocalName:
if (!PFS) return Error(ID.Loc, "invalid use of function-local name");
V = PFS->GetVal(ID.StrVal, Ty, ID.Loc, OC);
V = PFS->GetVal(ID.StrVal, Ty, ID.Loc);
return V == nullptr;
case ValID::t_InlineAsm: {
if (!ID.FTy || !InlineAsm::Verify(ID.FTy, ID.StrVal2))
@@ -4385,11 +4289,10 @@ bool LLParser::parseConstantValue(Type *Ty, Constant *&C) {
}
}
bool LLParser::ParseValue(Type *Ty, Value *&V, PerFunctionState *PFS,
OperatorConstraint OC) {
bool LLParser::ParseValue(Type *Ty, Value *&V, PerFunctionState *PFS) {
V = nullptr;
ValID ID;
return ParseValID(ID, PFS) || ConvertValIDToValue(Ty, ID, V, PFS, OC);
return ParseValID(ID, PFS) || ConvertValIDToValue(Ty, ID, V, PFS);
}
bool LLParser::ParseTypeAndValue(Value *&V, PerFunctionState *PFS) {
@@ -4818,11 +4721,10 @@ int LLParser::ParseInstruction(Instruction *&Inst, BasicBlock *BB,
case lltok::kw_resume: return ParseResume(Inst, PFS);
case lltok::kw_cleanupret: return ParseCleanupRet(Inst, PFS);
case lltok::kw_catchret: return ParseCatchRet(Inst, PFS);
case lltok::kw_catchpad: return ParseCatchPad(Inst, PFS);
case lltok::kw_terminatepad: return ParseTerminatePad(Inst, PFS);
case lltok::kw_cleanuppad: return ParseCleanupPad(Inst, PFS);
case lltok::kw_catchendpad: return ParseCatchEndPad(Inst, PFS);
case lltok::kw_cleanupendpad: return ParseCleanupEndPad(Inst, PFS);
case lltok::kw_catchswitch: return ParseCatchSwitch(Inst, PFS);
case lltok::kw_catchpad: return ParseCatchPad(Inst, PFS);
case lltok::kw_terminatepad:return ParseTerminatePad(Inst, PFS);
case lltok::kw_cleanuppad: return ParseCleanupPad(Inst, PFS);
// Binary Operators.
case lltok::kw_add:
case lltok::kw_sub:
@@ -5262,11 +5164,14 @@ bool LLParser::ParseExceptionArgs(SmallVectorImpl<Value *> &Args,
}
/// ParseCleanupRet
/// ::= 'cleanupret' Value unwind ('to' 'caller' | TypeAndValue)
/// ::= 'cleanupret' from Value unwind ('to' 'caller' | TypeAndValue)
bool LLParser::ParseCleanupRet(Instruction *&Inst, PerFunctionState &PFS) {
Value *CleanupPad = nullptr;
if (ParseValue(Type::getTokenTy(Context), CleanupPad, PFS, OC_CleanupPad))
if (ParseToken(lltok::kw_from, "expected 'from' after cleanupret"))
return true;
if (ParseValue(Type::getTokenTy(Context), CleanupPad, PFS))
return true;
if (ParseToken(lltok::kw_unwind, "expected 'unwind' in cleanupret"))
@@ -5283,16 +5188,19 @@ bool LLParser::ParseCleanupRet(Instruction *&Inst, PerFunctionState &PFS) {
}
}
Inst = CleanupReturnInst::Create(cast<CleanupPadInst>(CleanupPad), UnwindBB);
Inst = CleanupReturnInst::Create(CleanupPad, UnwindBB);
return false;
}
/// ParseCatchRet
/// ::= 'catchret' Value 'to' TypeAndValue
/// ::= 'catchret' from Parent Value 'to' TypeAndValue
bool LLParser::ParseCatchRet(Instruction *&Inst, PerFunctionState &PFS) {
Value *CatchPad = nullptr;
if (ParseValue(Type::getTokenTy(Context), CatchPad, PFS, OC_CatchPad))
if (ParseToken(lltok::kw_from, "expected 'from' after catchret"))
return true;
if (ParseValue(Type::getTokenTy(Context), CatchPad, PFS))
return true;
BasicBlock *BB;
@@ -5300,31 +5208,98 @@ bool LLParser::ParseCatchRet(Instruction *&Inst, PerFunctionState &PFS) {
ParseTypeAndBasicBlock(BB, PFS))
return true;
Inst = CatchReturnInst::Create(cast<CatchPadInst>(CatchPad), BB);
Inst = CatchReturnInst::Create(CatchPad, BB);
return false;
}
/// ParseCatchSwitch
/// ::= 'catchswitch' within Parent
bool LLParser::ParseCatchSwitch(Instruction *&Inst, PerFunctionState &PFS) {
Value *ParentPad;
LocTy BBLoc;
if (ParseToken(lltok::kw_within, "expected 'within' after catchswitch"))
return true;
if (Lex.getKind() != lltok::kw_none && Lex.getKind() != lltok::LocalVar &&
Lex.getKind() != lltok::LocalVarID)
return TokError("expected scope value for catchswitch");
if (ParseValue(Type::getTokenTy(Context), ParentPad, PFS))
return true;
if (ParseToken(lltok::lsquare, "expected '[' with catchswitch labels"))
return true;
SmallVector<BasicBlock *, 32> Table;
do {
BasicBlock *DestBB;
if (ParseTypeAndBasicBlock(DestBB, PFS))
return true;
Table.push_back(DestBB);
} while (EatIfPresent(lltok::comma));
if (ParseToken(lltok::rsquare, "expected ']' after catchswitch labels"))
return true;
if (ParseToken(lltok::kw_unwind,
"expected 'unwind' after catchswitch scope"))
return true;
BasicBlock *UnwindBB = nullptr;
if (EatIfPresent(lltok::kw_to)) {
if (ParseToken(lltok::kw_caller, "expected 'caller' in catchswitch"))
return true;
} else {
if (ParseTypeAndBasicBlock(UnwindBB, PFS))
return true;
}
auto *CatchSwitch =
CatchSwitchInst::Create(ParentPad, UnwindBB, Table.size());
for (BasicBlock *DestBB : Table)
CatchSwitch->addHandler(DestBB);
Inst = CatchSwitch;
return false;
}
/// ParseCatchPad
/// ::= 'catchpad' ParamList 'to' TypeAndValue 'unwind' TypeAndValue
bool LLParser::ParseCatchPad(Instruction *&Inst, PerFunctionState &PFS) {
Value *CatchSwitch = nullptr;
if (ParseToken(lltok::kw_within, "expected 'within' after catchpad"))
return true;
if (Lex.getKind() != lltok::LocalVar && Lex.getKind() != lltok::LocalVarID)
return TokError("expected scope value for catchpad");
if (ParseValue(Type::getTokenTy(Context), CatchSwitch, PFS))
return true;
SmallVector<Value *, 8> Args;
if (ParseExceptionArgs(Args, PFS))
return true;
BasicBlock *NormalBB, *UnwindBB;
if (ParseToken(lltok::kw_to, "expected 'to' in catchpad") ||
ParseTypeAndBasicBlock(NormalBB, PFS) ||
ParseToken(lltok::kw_unwind, "expected 'unwind' in catchpad") ||
ParseTypeAndBasicBlock(UnwindBB, PFS))
return true;
Inst = CatchPadInst::Create(NormalBB, UnwindBB, Args);
Inst = CatchPadInst::Create(CatchSwitch, Args);
return false;
}
/// ParseTerminatePad
/// ::= 'terminatepad' ParamList 'to' TypeAndValue
/// ::= 'terminatepad' within Parent ParamList 'to' TypeAndValue
bool LLParser::ParseTerminatePad(Instruction *&Inst, PerFunctionState &PFS) {
Value *ParentPad = nullptr;
if (ParseToken(lltok::kw_within, "expected 'within' after terminatepad"))
return true;
if (Lex.getKind() != lltok::kw_none && Lex.getKind() != lltok::LocalVar &&
Lex.getKind() != lltok::LocalVarID)
return TokError("expected scope value for terminatepad");
if (ParseValue(Type::getTokenTy(Context), ParentPad, PFS))
return true;
SmallVector<Value *, 8> Args;
if (ParseExceptionArgs(Args, PFS))
return true;
@@ -5343,71 +5318,30 @@ bool LLParser::ParseTerminatePad(Instruction *&Inst, PerFunctionState &PFS) {
}
}
Inst = TerminatePadInst::Create(Context, UnwindBB, Args);
Inst = TerminatePadInst::Create(ParentPad, UnwindBB, Args);
return false;
}
/// ParseCleanupPad
/// ::= 'cleanuppad' ParamList
/// ::= 'cleanuppad' within Parent ParamList
bool LLParser::ParseCleanupPad(Instruction *&Inst, PerFunctionState &PFS) {
Value *ParentPad = nullptr;
if (ParseToken(lltok::kw_within, "expected 'within' after cleanuppad"))
return true;
if (Lex.getKind() != lltok::kw_none && Lex.getKind() != lltok::LocalVar &&
Lex.getKind() != lltok::LocalVarID)
return TokError("expected scope value for cleanuppad");
if (ParseValue(Type::getTokenTy(Context), ParentPad, PFS))
return true;
SmallVector<Value *, 8> Args;
if (ParseExceptionArgs(Args, PFS))
return true;
Inst = CleanupPadInst::Create(Context, Args);
return false;
}
/// ParseCatchEndPad
/// ::= 'catchendpad' unwind ('to' 'caller' | TypeAndValue)
bool LLParser::ParseCatchEndPad(Instruction *&Inst, PerFunctionState &PFS) {
if (ParseToken(lltok::kw_unwind, "expected 'unwind' in catchendpad"))
return true;
BasicBlock *UnwindBB = nullptr;
if (Lex.getKind() == lltok::kw_to) {
Lex.Lex();
if (Lex.getKind() == lltok::kw_caller) {
Lex.Lex();
} else {
return true;
}
} else {
if (ParseTypeAndBasicBlock(UnwindBB, PFS)) {
return true;
}
}
Inst = CatchEndPadInst::Create(Context, UnwindBB);
return false;
}
/// ParseCatchEndPad
/// ::= 'cleanupendpad' Value unwind ('to' 'caller' | TypeAndValue)
bool LLParser::ParseCleanupEndPad(Instruction *&Inst, PerFunctionState &PFS) {
Value *CleanupPad = nullptr;
if (ParseValue(Type::getTokenTy(Context), CleanupPad, PFS, OC_CleanupPad))
return true;
if (ParseToken(lltok::kw_unwind, "expected 'unwind' in catchendpad"))
return true;
BasicBlock *UnwindBB = nullptr;
if (Lex.getKind() == lltok::kw_to) {
Lex.Lex();
if (Lex.getKind() == lltok::kw_caller) {
Lex.Lex();
} else {
return true;
}
} else {
if (ParseTypeAndBasicBlock(UnwindBB, PFS)) {
return true;
}
}
Inst = CleanupEndPadInst::Create(cast<CleanupPadInst>(CleanupPad), UnwindBB);
Inst = CleanupPadInst::Create(ParentPad, Args);
return false;
}