mirror of
https://github.com/ldc-developers/ldc.git
synced 2025-05-01 23:50:43 +03:00
The big catch/finally rework, part 1
Never generates any landing pads or invoke instructions right now for simplicity. The code for emitting them will be added back in the next step. The "after..." blocks without any precedessors remain for now, as we need a clean way to suppress any codegen for that block (but not new blocks, which might resolve labels) before tackling that one. Builds druntime/Phobos on OS X x86_64 (albeit without EH, of course).
This commit is contained in:
parent
bfc20df4c8
commit
4236ae9ce5
12 changed files with 861 additions and 1315 deletions
|
@ -746,7 +746,7 @@ void CompoundAsmStatement_toIR(CompoundAsmStatement *stmt, IRState* p)
|
||||||
sw->addCase(LLConstantInt::get(llvm::IntegerType::get(gIR->context(), 32), it->second), casebb);
|
sw->addCase(LLConstantInt::get(llvm::IntegerType::get(gIR->context(), 32), it->second), casebb);
|
||||||
|
|
||||||
p->scope() = IRScope(casebb);
|
p->scope() = IRScope(casebb);
|
||||||
DtoGoto(stmt->loc, it->first, stmt->enclosingFinally);
|
DtoGoto(stmt->loc, it->first);
|
||||||
}
|
}
|
||||||
|
|
||||||
p->scope() = IRScope(bb);
|
p->scope() = IRScope(bb);
|
||||||
|
|
|
@ -884,14 +884,13 @@ void DtoDefineFunction(FuncDeclaration* fd)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FuncGen fg;
|
{
|
||||||
irFunc->gen = &fg;
|
ScopeStack scopeStack(gIR);
|
||||||
|
irFunc->scopes = &scopeStack;
|
||||||
|
|
||||||
DtoCreateNestedContext(fd);
|
DtoCreateNestedContext(fd);
|
||||||
|
|
||||||
if (fd->vresult && !
|
if (fd->vresult && !fd->vresult->nestedrefs.dim) // FIXME: not sure here :/
|
||||||
fd->vresult->nestedrefs.dim // FIXME: not sure here :/
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
DtoVarDeclaration(fd->vresult);
|
DtoVarDeclaration(fd->vresult);
|
||||||
}
|
}
|
||||||
|
@ -915,8 +914,10 @@ void DtoDefineFunction(FuncDeclaration* fd)
|
||||||
}
|
}
|
||||||
|
|
||||||
// output function body
|
// output function body
|
||||||
codegenFunction(fd->fbody, gIR);
|
Statement_toIR(fd->fbody, gIR);
|
||||||
irFunc->gen = 0;
|
|
||||||
|
irFunc->scopes = 0;
|
||||||
|
}
|
||||||
|
|
||||||
llvm::BasicBlock* bb = gIR->scopebb();
|
llvm::BasicBlock* bb = gIR->scopebb();
|
||||||
if (pred_begin(bb) == pred_end(bb) && bb != &bb->getParent()->getEntryBlock()) {
|
if (pred_begin(bb) == pred_end(bb) && bb != &bb->getParent()->getEntryBlock()) {
|
||||||
|
|
|
@ -41,23 +41,6 @@ const IRScope& IRScope::operator=(const IRScope& rhs)
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
IRTargetScope::IRTargetScope(
|
|
||||||
Statement* s,
|
|
||||||
EnclosingTryFinally* enclosinghandler,
|
|
||||||
llvm::BasicBlock* continueTarget,
|
|
||||||
llvm::BasicBlock* breakTarget,
|
|
||||||
bool onlyLabeledBreak
|
|
||||||
)
|
|
||||||
{
|
|
||||||
this->s = s;
|
|
||||||
this->enclosinghandler = enclosinghandler;
|
|
||||||
this->breakTarget = breakTarget;
|
|
||||||
this->continueTarget = continueTarget;
|
|
||||||
this->onlyLabeledBreak = onlyLabeledBreak;
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
IRState::IRState(const char *name, llvm::LLVMContext &context)
|
IRState::IRState(const char *name, llvm::LLVMContext &context)
|
||||||
: module(name, context), DBuilder(this)
|
: module(name, context), DBuilder(this)
|
||||||
|
|
|
@ -201,20 +201,20 @@ struct IRState
|
||||||
template <typename T>
|
template <typename T>
|
||||||
llvm::CallSite IRState::CreateCallOrInvoke(LLValue* Callee, const T &args, const char* Name)
|
llvm::CallSite IRState::CreateCallOrInvoke(LLValue* Callee, const T &args, const char* Name)
|
||||||
{
|
{
|
||||||
FuncGen& funcGen = *func()->gen;
|
//ScopeStack& funcGen = *func()->scopes;
|
||||||
LLFunction* fn = llvm::dyn_cast<LLFunction>(Callee);
|
LLFunction* fn = llvm::dyn_cast<LLFunction>(Callee);
|
||||||
|
|
||||||
const bool hasTemporaries = funcGen.hasTemporariesToDestruct();
|
/*const bool hasTemporaries = funcGen.hasTemporariesToDestruct();
|
||||||
// intrinsics don't support invoking and 'nounwind' functions don't need it.
|
// intrinsics don't support invoking and 'nounwind' functions don't need it.
|
||||||
const bool doesNotThrow = (fn && (fn->isIntrinsic() || fn->doesNotThrow()));
|
const bool doesNotThrow = (fn && (fn->isIntrinsic() || fn->doesNotThrow()));
|
||||||
|
|
||||||
if (doesNotThrow || (!hasTemporaries && funcGen.landingPad == NULL))
|
if (doesNotThrow || (!hasTemporaries && funcGen.landingPad == NULL))
|
||||||
{
|
{*/
|
||||||
llvm::CallInst* call = ir->CreateCall(Callee, args, Name);
|
llvm::CallInst* call = ir->CreateCall(Callee, args, Name);
|
||||||
if (fn)
|
if (fn)
|
||||||
call->setAttributes(fn->getAttributes());
|
call->setAttributes(fn->getAttributes());
|
||||||
return call;
|
return call;
|
||||||
}
|
/*}
|
||||||
|
|
||||||
if (hasTemporaries)
|
if (hasTemporaries)
|
||||||
funcGen.prepareToDestructAllTemporariesOnThrow(this);
|
funcGen.prepareToDestructAllTemporariesOnThrow(this);
|
||||||
|
@ -229,10 +229,9 @@ llvm::CallSite IRState::CreateCallOrInvoke(LLValue* Callee, const T &args, const
|
||||||
funcGen.landingPadInfo.pop();
|
funcGen.landingPadInfo.pop();
|
||||||
|
|
||||||
scope() = IRScope(postinvoke);
|
scope() = IRScope(postinvoke);
|
||||||
return invoke;
|
return invoke;*/
|
||||||
}
|
}
|
||||||
|
|
||||||
void codegenFunction(Statement *s, IRState *irs);
|
|
||||||
void Statement_toIR(Statement *s, IRState *irs);
|
void Statement_toIR(Statement *s, IRState *irs);
|
||||||
|
|
||||||
#endif // LDC_GEN_IRSTATE_H
|
#endif // LDC_GEN_IRSTATE_H
|
||||||
|
|
|
@ -227,7 +227,7 @@ LLValue *DtoModuleFileName(Module* M, const Loc& loc)
|
||||||
/*////////////////////////////////////////////////////////////////////////////////////////
|
/*////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// GOTO HELPER
|
// GOTO HELPER
|
||||||
////////////////////////////////////////////////////////////////////////////////////////*/
|
////////////////////////////////////////////////////////////////////////////////////////*/
|
||||||
void DtoGoto(Loc &loc, LabelDsymbol *target, TryFinallyStatement *sourceFinally)
|
void DtoGoto(Loc &loc, LabelDsymbol *target)
|
||||||
{
|
{
|
||||||
assert(!gIR->scopereturned());
|
assert(!gIR->scopereturned());
|
||||||
|
|
||||||
|
@ -238,90 +238,9 @@ void DtoGoto(Loc &loc, LabelDsymbol *target, TryFinallyStatement *sourceFinally)
|
||||||
fatal();
|
fatal();
|
||||||
}
|
}
|
||||||
|
|
||||||
// find target basic block
|
gIR->func()->scopes->jumpToLabel(loc, target->ident);
|
||||||
std::string labelname = gIR->func()->gen->getScopedLabelName(target->ident->toChars());
|
|
||||||
llvm::BasicBlock* &targetBB = gIR->func()->gen->labelToBB[labelname];
|
|
||||||
if (targetBB == NULL)
|
|
||||||
targetBB = llvm::BasicBlock::Create(gIR->context(), "label_" + labelname, gIR->topfunc());
|
|
||||||
|
|
||||||
// emit code for finallys between goto and label
|
|
||||||
DtoEnclosingHandlers(loc, lblstmt);
|
|
||||||
|
|
||||||
// goto into finally blocks is forbidden by the spec
|
|
||||||
// but should work fine
|
|
||||||
if (lblstmt->tf != sourceFinally)
|
|
||||||
{
|
|
||||||
error(loc, "spec disallows goto into or out of finally block");
|
|
||||||
fatal();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
llvm::BranchInst::Create(targetBB, gIR->scopebb());
|
|
||||||
}
|
|
||||||
|
|
||||||
/****************************************************************************************/
|
|
||||||
/*////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// TRY-FINALLY
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////*/
|
|
||||||
|
|
||||||
void EnclosingTryFinally::emitCode(IRState * p)
|
|
||||||
{
|
|
||||||
if (tf->finalbody)
|
|
||||||
{
|
|
||||||
llvm::BasicBlock* oldpad = p->func()->gen->landingPad;
|
|
||||||
p->func()->gen->landingPad = landingPad;
|
|
||||||
Statement_toIR(tf->finalbody, p);
|
|
||||||
p->func()->gen->landingPad = oldpad;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
void DtoEnclosingHandlers(Loc& loc, Statement* target)
|
|
||||||
{
|
|
||||||
// labels are a special case: they are not required to enclose the current scope
|
|
||||||
// for them we use the enclosing scope handler as a reference point
|
|
||||||
LabelStatement* lblstmt = target ? target->isLabelStatement() : 0;
|
|
||||||
if (lblstmt)
|
|
||||||
target = lblstmt->enclosingScopeExit;
|
|
||||||
|
|
||||||
// Figure out up until what handler we need to emit. Note that we need to
|
|
||||||
// use use indices instead of iterators in the loop where we actually emit
|
|
||||||
// them, as emitCode() might itself push/pop from the vector if it contains
|
|
||||||
// control flow and and thus invalidate the latter.
|
|
||||||
FuncGen::TargetScopeVec& scopes = gIR->func()->gen->targetScopes;
|
|
||||||
size_t remainingScopes = scopes.size();
|
|
||||||
while (remainingScopes != 0) {
|
|
||||||
if (scopes[remainingScopes - 1].s == target) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
--remainingScopes;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (target && !remainingScopes) {
|
|
||||||
if (lblstmt)
|
|
||||||
error(loc, "cannot goto into try, volatile or synchronized statement at %s", target->loc.toChars());
|
|
||||||
else
|
|
||||||
error(loc, "internal error, cannot find jump path to statement at %s", target->loc.toChars());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// emit code for enclosing handlers
|
|
||||||
//
|
|
||||||
|
|
||||||
// since the labelstatements possibly inside are private
|
|
||||||
// and might already exist push a label scope
|
|
||||||
gIR->func()->gen->pushUniqueLabelScope("enclosing");
|
|
||||||
|
|
||||||
for (size_t i = scopes.size(); i > remainingScopes; --i) {
|
|
||||||
EnclosingTryFinally *tf = scopes[i - 1].enclosinghandler;
|
|
||||||
if (tf) tf->emitCode(gIR);
|
|
||||||
}
|
|
||||||
|
|
||||||
gIR->func()->gen->popLabelScope();
|
|
||||||
}
|
|
||||||
|
|
||||||
/****************************************************************************************/
|
|
||||||
/*////////////////////////////////////////////////////////////////////////////////////////
|
/*////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// ASSIGNMENT HELPER (store this in that)
|
// ASSIGNMENT HELPER (store this in that)
|
||||||
////////////////////////////////////////////////////////////////////////////////////////*/
|
////////////////////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
|
@ -22,16 +22,6 @@
|
||||||
#include "gen/llvm.h"
|
#include "gen/llvm.h"
|
||||||
#include "ir/irfuncty.h"
|
#include "ir/irfuncty.h"
|
||||||
|
|
||||||
// this is used for tracking try-finally scopes
|
|
||||||
struct EnclosingTryFinally
|
|
||||||
{
|
|
||||||
TryFinallyStatement* tf;
|
|
||||||
llvm::BasicBlock* landingPad;
|
|
||||||
void emitCode(IRState* p);
|
|
||||||
EnclosingTryFinally(TryFinallyStatement* _tf, llvm::BasicBlock* _pad)
|
|
||||||
: tf(_tf), landingPad(_pad) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
// dynamic memory helpers
|
// dynamic memory helpers
|
||||||
LLValue* DtoNew(Loc& loc, Type* newtype);
|
LLValue* DtoNew(Loc& loc, Type* newtype);
|
||||||
LLValue* DtoNewStruct(Loc& loc, TypeStruct* newtype);
|
LLValue* DtoNewStruct(Loc& loc, TypeStruct* newtype);
|
||||||
|
@ -54,12 +44,7 @@ void DtoAssert(Module* M, Loc& loc, DValue* msg);
|
||||||
LLValue* DtoModuleFileName(Module* M, const Loc& loc);
|
LLValue* DtoModuleFileName(Module* M, const Loc& loc);
|
||||||
|
|
||||||
/// emits goto to LabelStatement with the target identifier
|
/// emits goto to LabelStatement with the target identifier
|
||||||
/// the sourceFinally is only used for error checking
|
void DtoGoto(Loc &loc, LabelDsymbol *target);
|
||||||
void DtoGoto(Loc &loc, LabelDsymbol *target, TryFinallyStatement *sourceFinally);
|
|
||||||
|
|
||||||
// Generates IR for enclosing handlers between the current state and
|
|
||||||
// the scope created by the 'target' statement.
|
|
||||||
void DtoEnclosingHandlers(Loc& loc, Statement* target);
|
|
||||||
|
|
||||||
/// Enters a critical section.
|
/// Enters a critical section.
|
||||||
void DtoEnterCritical(Loc& loc, LLValue* g);
|
void DtoEnterCritical(Loc& loc, LLValue* g);
|
||||||
|
|
File diff suppressed because it is too large
Load diff
52
gen/toir.cpp
52
gen/toir.cpp
|
@ -39,7 +39,6 @@
|
||||||
#include "gen/warnings.h"
|
#include "gen/warnings.h"
|
||||||
#include "ir/irtypeclass.h"
|
#include "ir/irtypeclass.h"
|
||||||
#include "ir/irtypestruct.h"
|
#include "ir/irtypestruct.h"
|
||||||
#include "ir/irlandingpad.h"
|
|
||||||
#include "llvm/Support/CommandLine.h"
|
#include "llvm/Support/CommandLine.h"
|
||||||
#include "llvm/Support/ManagedStatic.h"
|
#include "llvm/Support/ManagedStatic.h"
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
@ -267,15 +266,26 @@ class ToElemVisitor : public Visitor
|
||||||
{
|
{
|
||||||
IRState *p;
|
IRState *p;
|
||||||
bool destructTemporaries;
|
bool destructTemporaries;
|
||||||
|
CleanupCursor initialCleanupScope;
|
||||||
DValue *result;
|
DValue *result;
|
||||||
public:
|
public:
|
||||||
ToElemVisitor(IRState *p_, bool destructTemporaries_)
|
ToElemVisitor(IRState *p_, bool destructTemporaries_)
|
||||||
: p(p_), destructTemporaries(destructTemporaries_), result(NULL)
|
: p(p_), destructTemporaries(destructTemporaries_), result(NULL)
|
||||||
{
|
{
|
||||||
p->func()->gen->pushToElemScope();
|
initialCleanupScope = p->func()->scopes->currentCleanupScope();
|
||||||
}
|
}
|
||||||
|
|
||||||
~ToElemVisitor() { p->func()->gen->popToElemScope(destructTemporaries); }
|
~ToElemVisitor()
|
||||||
|
{
|
||||||
|
if (destructTemporaries && p->func()->scopes->currentCleanupScope() != initialCleanupScope)
|
||||||
|
{
|
||||||
|
llvm::BasicBlock* endbb = llvm::BasicBlock::Create(
|
||||||
|
p->context(), "toElem.success", p->topfunc());
|
||||||
|
p->func()->scopes->runCleanups(initialCleanupScope, endbb);
|
||||||
|
p->func()->scopes->popCleanups(initialCleanupScope);
|
||||||
|
p->scope() = IRScope(endbb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
DValue *getResult() { return result; }
|
DValue *getResult() { return result; }
|
||||||
|
|
||||||
|
@ -300,7 +310,17 @@ public:
|
||||||
{
|
{
|
||||||
VarDeclaration* vd = varValue->var;
|
VarDeclaration* vd = varValue->var;
|
||||||
if (!vd->isDataseg() && vd->edtor && !vd->noscope)
|
if (!vd->isDataseg() && vd->edtor && !vd->noscope)
|
||||||
p->func()->gen->pushTemporaryToDestruct(vd);
|
{
|
||||||
|
llvm::BasicBlock* cleanupbb = llvm::BasicBlock::Create(
|
||||||
|
p->context(), llvm::Twine("dtor.") + vd->toChars(), p->topfunc());
|
||||||
|
|
||||||
|
// TODO: Clean this up with push/pop insertion point methods.
|
||||||
|
IRScope oldScope = p->scope();
|
||||||
|
p->scope() = IRScope(cleanupbb);
|
||||||
|
toElemDtor(vd->edtor);
|
||||||
|
p->func()->scopes->pushCleanup(cleanupbb, p->scopebb());
|
||||||
|
p->scope() = oldScope;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1148,13 +1168,13 @@ public:
|
||||||
if (result)
|
if (result)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
VarDeclarations& temporaries = gIR->func()->gen->getTemporariesToDestruct();
|
#if 0
|
||||||
|
|
||||||
// check if we are about to construct a just declared temporary:
|
// check if we are about to construct a just declared temporary:
|
||||||
// MyStruct(myArgs) => (MyStruct tmp; tmp).this(myArgs)
|
// MyStruct(myArgs) => (MyStruct tmp; tmp).this(myArgs)
|
||||||
|
const CleanupCursor temporaryScope = p->func()->scopes->currentCleanupScope();
|
||||||
bool constructingTemporary = false;
|
bool constructingTemporary = false;
|
||||||
if (!temporaries.empty() &&
|
if (temporaryScope != 0 && dfnval && dfnval->func &&
|
||||||
dfnval && dfnval->func && dfnval->func->isCtorDeclaration())
|
dfnval->func->isCtorDeclaration())
|
||||||
{
|
{
|
||||||
DotVarExp* dve = static_cast<DotVarExp*>(e->e1);
|
DotVarExp* dve = static_cast<DotVarExp*>(e->e1);
|
||||||
if (dve->e1->op == TOKcomma)
|
if (dve->e1->op == TOKcomma)
|
||||||
|
@ -1175,17 +1195,19 @@ public:
|
||||||
// i.e., don't destruct the temporary if its constructor throws
|
// i.e., don't destruct the temporary if its constructor throws
|
||||||
// (DMD issue 13095)
|
// (DMD issue 13095)
|
||||||
// => remember position in stack and pop temporarily
|
// => remember position in stack and pop temporarily
|
||||||
int indexOfTemporary = (!constructingTemporary ? -1
|
if (constructingTemporary) {
|
||||||
: static_cast<int>(temporaries.size()) - 1);
|
p->func()->scopes->suspendCleanup(temporaryScope);
|
||||||
VarDeclaration* temporary = (!constructingTemporary ? NULL
|
}
|
||||||
: temporaries.pop());
|
#endif
|
||||||
|
|
||||||
result = DtoCallFunction(e->loc, e->type, fnval, e->arguments);
|
result = DtoCallFunction(e->loc, e->type, fnval, e->arguments);
|
||||||
|
|
||||||
|
#if 0
|
||||||
// insert the now fully constructed temporary at the original index;
|
// insert the now fully constructed temporary at the original index;
|
||||||
// i.e., before any new temporaries pushed by DtoCallFunction()
|
// i.e., before any new temporaries pushed by DtoCallFunction()
|
||||||
if (constructingTemporary)
|
if (constructingTemporary) {
|
||||||
temporaries.insert(indexOfTemporary, temporary);
|
p->func()->scopes->resumeCleanup(temporaryScope);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
@ -15,117 +15,251 @@
|
||||||
#include "ir/irfunction.h"
|
#include "ir/irfunction.h"
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
FuncGen::FuncGen()
|
namespace {
|
||||||
{
|
void executeCleanup(IRState *irs, CleanupScope& scope,
|
||||||
landingPad = NULL;
|
llvm::BasicBlock *sourceBlock, llvm::BasicBlock* continueWith
|
||||||
nextUnique.push(0);
|
) {
|
||||||
|
if (scope.exitTargets.empty() || (
|
||||||
|
scope.exitTargets.size() == 1 &&
|
||||||
|
scope.exitTargets[0].branchTarget == continueWith
|
||||||
|
)) {
|
||||||
|
// We didn't need a branch selector before and still don't need one.
|
||||||
|
assert(!scope.branchSelector);
|
||||||
|
|
||||||
|
// Set up the unconditional branch at the end of the cleanup if we have
|
||||||
|
// not done so already.
|
||||||
|
if (scope.exitTargets.empty()) {
|
||||||
|
scope.exitTargets.push_back(CleanupExitTarget(continueWith));
|
||||||
|
llvm::BranchInst::Create(continueWith, scope.endBlock);
|
||||||
|
}
|
||||||
|
scope.exitTargets.front().sourceBlocks.push_back(sourceBlock);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string FuncGen::getScopedLabelName(const char* ident)
|
// We need a branch selector if we are here...
|
||||||
{
|
if (!scope.branchSelector) {
|
||||||
if(labelScopes.empty())
|
// ... and have not created one yet, so do so now.
|
||||||
return std::string(ident);
|
scope.branchSelector = new llvm::AllocaInst(
|
||||||
|
llvm::Type::getInt32Ty(gIR->context()),
|
||||||
|
llvm::Twine("branchsel.") + scope.beginBlock->getName(),
|
||||||
|
irs->topallocapoint()
|
||||||
|
);
|
||||||
|
|
||||||
std::string result = "__";
|
// Now we also need to store 0 to it to keep the paths that go to the
|
||||||
for(unsigned int i = 0; i < labelScopes.size(); ++i)
|
// only existing branch target the same.
|
||||||
result += labelScopes[i] + "_";
|
std::vector<llvm::BasicBlock*>& v = scope.exitTargets.front().sourceBlocks;
|
||||||
return result + ident;
|
for (std::vector<llvm::BasicBlock*>::iterator it = v.begin(), end = v.end();
|
||||||
|
it != end; ++it
|
||||||
|
) {
|
||||||
|
new llvm::StoreInst(DtoConstUint(0), scope.branchSelector,
|
||||||
|
(*it)->getTerminator());
|
||||||
}
|
}
|
||||||
|
|
||||||
void FuncGen::pushUniqueLabelScope(const char* name)
|
// And convert the BranchInst to the existing branch target to a
|
||||||
{
|
// SelectInst so we can append the other cases to it.
|
||||||
std::ostringstream uniquename;
|
scope.endBlock->getTerminator()->eraseFromParent();
|
||||||
uniquename << name << nextUnique.top()++;
|
llvm::Value *sel = new llvm::LoadInst(scope.branchSelector, "",
|
||||||
nextUnique.push(0);
|
scope.endBlock);
|
||||||
labelScopes.push_back(uniquename.str());
|
llvm::SwitchInst::Create(
|
||||||
|
sel,
|
||||||
|
scope.exitTargets[0].branchTarget,
|
||||||
|
1, // Expected number of branches, only for pre-allocating.
|
||||||
|
scope.endBlock
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FuncGen::popLabelScope()
|
// If we already know this branch target, figure out the branch selector
|
||||||
{
|
// value and simply insert the store into the source block (prior to the
|
||||||
labelScopes.pop_back();
|
// last instruction, which is the branch to the first cleanup).
|
||||||
nextUnique.pop();
|
for (unsigned i = 0; i < scope.exitTargets.size(); ++i) {
|
||||||
}
|
CleanupExitTarget& t = scope.exitTargets[i];
|
||||||
|
if (t.branchTarget == continueWith) {
|
||||||
|
new llvm::StoreInst(DtoConstUint(i), scope.branchSelector,
|
||||||
|
sourceBlock->getTerminator());
|
||||||
|
|
||||||
void FuncGen::pushToElemScope()
|
// Note: Strictly speaking, keeping this up to date would not be
|
||||||
{
|
// needed right now, because we never to any optimizations that
|
||||||
toElemScopes.push(static_cast<unsigned>(temporariesToDestruct.size()));
|
// require changes to the source blocks after the initial conversion
|
||||||
}
|
// from one to two branch targets. Keeping this around for now to
|
||||||
|
// ease future development, but may be removed to save some work.
|
||||||
|
t.sourceBlocks.push_back(sourceBlock);
|
||||||
|
|
||||||
void FuncGen::popToElemScope(bool destructTemporaries)
|
return;
|
||||||
{
|
|
||||||
assert(!toElemScopes.empty());
|
|
||||||
|
|
||||||
const bool isOuterMost = (toElemScopes.size() == 1);
|
|
||||||
if (destructTemporaries || isOuterMost)
|
|
||||||
{
|
|
||||||
int numInitialTemporaries = toElemScopes.back();
|
|
||||||
assert(!isOuterMost || numInitialTemporaries == 0);
|
|
||||||
this->destructTemporaries(numInitialTemporaries);
|
|
||||||
}
|
|
||||||
|
|
||||||
toElemScopes.pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
void FuncGen::pushTemporaryToDestruct(VarDeclaration* vd)
|
|
||||||
{
|
|
||||||
temporariesToDestruct.push(vd);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FuncGen::hasTemporariesToDestruct()
|
|
||||||
{
|
|
||||||
return !temporariesToDestruct.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
VarDeclarations& FuncGen::getTemporariesToDestruct()
|
|
||||||
{
|
|
||||||
return temporariesToDestruct;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FuncGen::destructTemporaries(unsigned numToKeep)
|
|
||||||
{
|
|
||||||
// pop one temporary after the other from the temporariesToDestruct stack
|
|
||||||
// and evaluate its destructor expression
|
|
||||||
// so when an exception occurs in a destructor expression, all older
|
|
||||||
// temporaries (excl. the one which threw in its destructor) will be
|
|
||||||
// destructed in a landing pad
|
|
||||||
while (temporariesToDestruct.size() > numToKeep)
|
|
||||||
{
|
|
||||||
VarDeclaration* vd = temporariesToDestruct.pop();
|
|
||||||
toElemDtor(vd->edtor);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FuncGen::destructAllTemporariesAndRestoreStack()
|
// We don't know this branch target yet, so add it to the SwitchInst...
|
||||||
{
|
llvm::ConstantInt * const selectorVal = DtoConstUint(scope.exitTargets.size());
|
||||||
VarDeclarations original = temporariesToDestruct;
|
llvm::cast<llvm::SwitchInst>(scope.endBlock->getTerminator())->addCase(
|
||||||
destructTemporaries(0);
|
selectorVal, continueWith);
|
||||||
temporariesToDestruct = original;
|
|
||||||
|
// ... insert the store into the source block...
|
||||||
|
new llvm::StoreInst(selectorVal, scope.branchSelector,
|
||||||
|
sourceBlock->getTerminator());
|
||||||
|
|
||||||
|
// ... and keep track of it (again, this is unnecessary right now as
|
||||||
|
// discussed in the above note).
|
||||||
|
scope.exitTargets.push_back(CleanupExitTarget(continueWith));
|
||||||
|
scope.exitTargets.back().sourceBlocks.push_back(sourceBlock);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FuncGen::prepareToDestructAllTemporariesOnThrow(IRState* irState)
|
ScopeStack::~ScopeStack() {
|
||||||
{
|
if (!topLevelUnresolvedGotos.empty()) {
|
||||||
class CallDestructors : public IRLandingPadCatchFinallyInfo
|
for (std::vector<GotoJump>::iterator it = topLevelUnresolvedGotos.begin(),
|
||||||
{
|
end = topLevelUnresolvedGotos.end();
|
||||||
public:
|
it != end; ++it
|
||||||
FuncGen& funcGen;
|
) {
|
||||||
CallDestructors(FuncGen& funcGen) : funcGen(funcGen) {}
|
error(it->sourceLoc, "goto into try/finally scope is not allowed");
|
||||||
void toIR(LLValue*)
|
}
|
||||||
{
|
fatal();
|
||||||
funcGen.destructAllTemporariesAndRestoreStack();
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
CallDestructors* callDestructors = new CallDestructors(*this);
|
|
||||||
|
|
||||||
// create landing pad
|
|
||||||
llvm::BasicBlock* landingpadbb = llvm::BasicBlock::Create(irState->context(),
|
|
||||||
"temporariesLandingPad", irState->topfunc());
|
|
||||||
|
|
||||||
// set up the landing pad
|
|
||||||
landingPadInfo.addFinally(callDestructors, /* deleteOnPop = */ true);
|
|
||||||
landingPadInfo.push(landingpadbb);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ScopeStack::pushCleanup(llvm::BasicBlock* beginBlock, llvm::BasicBlock* endBlock) {
|
||||||
|
cleanupScopes.push_back(CleanupScope(beginBlock, endBlock));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScopeStack::runCleanups(CleanupCursor targetScope,
|
||||||
|
llvm::BasicBlock* continueWith
|
||||||
|
) {
|
||||||
|
assert(targetScope <= currentCleanupScope());
|
||||||
|
|
||||||
|
if (targetScope == currentCleanupScope()) {
|
||||||
|
// No cleanups to run, just branch to the next block.
|
||||||
|
llvm::BranchInst::Create(continueWith, irs->scopebb());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert the unconditional branch to the first cleanup block.
|
||||||
|
irs->ir->CreateBr(cleanupScopes.back().beginBlock);
|
||||||
|
|
||||||
|
// Update all the control flow in the cleanups to make sure we end up where
|
||||||
|
// we want.
|
||||||
|
for (CleanupCursor i = currentCleanupScope(); i-- > targetScope;) {
|
||||||
|
llvm::BasicBlock *nextBlock = (i > targetScope) ?
|
||||||
|
cleanupScopes[i - 1].beginBlock : continueWith;
|
||||||
|
executeCleanup(irs, cleanupScopes[i], irs->scopebb(), nextBlock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScopeStack::runAllCleanups(llvm::BasicBlock* continueWith) {
|
||||||
|
runCleanups(0, continueWith);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScopeStack::popCleanups(CleanupCursor targetScope) {
|
||||||
|
if (targetScope == currentCleanupScope()) return;
|
||||||
|
|
||||||
|
for (CleanupCursor i = currentCleanupScope(); i-- > targetScope;) {
|
||||||
|
for (std::vector<GotoJump>::iterator it = currentUnresolvedGotos().begin(),
|
||||||
|
end = currentUnresolvedGotos().end();
|
||||||
|
it != end; ++it
|
||||||
|
) {
|
||||||
|
// Make the source resp. last cleanup branch to this one.
|
||||||
|
llvm::BasicBlock *tentative = it->tentativeTarget;
|
||||||
|
tentative->replaceAllUsesWith(cleanupScopes[i].beginBlock);
|
||||||
|
|
||||||
|
// And continue execution with the tentative target (we simply reuse
|
||||||
|
// it because there is no reason not to).
|
||||||
|
executeCleanup(irs, cleanupScopes[i], it->sourceBlock, tentative);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::vector<GotoJump>& nextUnresolved = (i == 0) ?
|
||||||
|
topLevelUnresolvedGotos : cleanupScopes[i - 1].unresolvedGotos;
|
||||||
|
nextUnresolved.insert(
|
||||||
|
nextUnresolved.end(),
|
||||||
|
currentUnresolvedGotos().begin(),
|
||||||
|
currentUnresolvedGotos().end()
|
||||||
|
);
|
||||||
|
|
||||||
|
cleanupScopes.pop_back();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScopeStack::pushLoopTarget(Statement* loopStatement, llvm::BasicBlock* continueTarget,
|
||||||
|
llvm::BasicBlock* breakTarget
|
||||||
|
) {
|
||||||
|
continueTargets.push_back({continueTarget, currentCleanupScope(), loopStatement});
|
||||||
|
breakTargets.push_back({breakTarget, currentCleanupScope(), loopStatement});
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScopeStack::popLoopTarget() {
|
||||||
|
continueTargets.pop_back();
|
||||||
|
breakTargets.pop_back();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScopeStack::pushBreakTarget(Statement* switchStatement,
|
||||||
|
llvm::BasicBlock* targetBlock
|
||||||
|
) {
|
||||||
|
breakTargets.push_back({targetBlock, currentCleanupScope(), switchStatement});
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScopeStack::popBreakTarget() {
|
||||||
|
breakTargets.pop_back();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScopeStack::addLabelTarget(Identifier* labelName,
|
||||||
|
llvm::BasicBlock* targetBlock
|
||||||
|
) {
|
||||||
|
labelTargets[labelName] = {targetBlock, currentCleanupScope(), 0};
|
||||||
|
|
||||||
|
std::vector<GotoJump>& unresolved = currentUnresolvedGotos();
|
||||||
|
size_t i = 0;
|
||||||
|
while (i < unresolved.size()) {
|
||||||
|
if (unresolved[i].targetLabel != labelName) {
|
||||||
|
++i;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
unresolved[i].tentativeTarget->replaceAllUsesWith(targetBlock);
|
||||||
|
unresolved[i].tentativeTarget->eraseFromParent();
|
||||||
|
unresolved.erase(unresolved.begin() + i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScopeStack::jumpToLabel(Loc loc, Identifier* labelName) {
|
||||||
|
// If we have already seen that label, branch to it, executing any cleanups
|
||||||
|
// as necessary.
|
||||||
|
LabelTargetMap::iterator it = labelTargets.find(labelName);
|
||||||
|
if (it != labelTargets.end()) {
|
||||||
|
runCleanups(it->second.cleanupScope, it->second.targetBlock);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
llvm::BasicBlock *target =
|
||||||
|
llvm::BasicBlock::Create(irs->context(), "goto.unresolved", irs->topfunc());
|
||||||
|
irs->ir->CreateBr(target);
|
||||||
|
currentUnresolvedGotos().push_back({loc, irs->scopebb(), target, labelName});
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScopeStack::jumpToStatement(std::vector<JumpTarget>& targets,
|
||||||
|
Statement* loopOrSwitchStatement
|
||||||
|
) {
|
||||||
|
for (std::vector<JumpTarget>::reverse_iterator it = targets.rbegin(),
|
||||||
|
end = targets.rend();
|
||||||
|
it != end; ++it
|
||||||
|
) {
|
||||||
|
if (it->targetStatement == loopOrSwitchStatement) {
|
||||||
|
runCleanups(it->cleanupScope, it->targetBlock);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert(false && "Target for labeled break not found.");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScopeStack::jumpToClosest(std::vector<JumpTarget>& targets) {
|
||||||
|
assert(!targets.empty() &&
|
||||||
|
"Encountered break/continue but no loop in scope.");
|
||||||
|
JumpTarget &t = targets.back();
|
||||||
|
runCleanups(t.cleanupScope, t.targetBlock);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<GotoJump>& ScopeStack::currentUnresolvedGotos() {
|
||||||
|
return cleanupScopes.empty() ?
|
||||||
|
topLevelUnresolvedGotos :
|
||||||
|
cleanupScopes.back().unresolvedGotos;
|
||||||
|
}
|
||||||
|
|
||||||
IrFunction::IrFunction(FuncDeclaration* fd)
|
IrFunction::IrFunction(FuncDeclaration* fd)
|
||||||
{
|
{
|
||||||
|
@ -151,6 +285,9 @@ IrFunction::IrFunction(FuncDeclaration* fd)
|
||||||
|
|
||||||
_arguments = NULL;
|
_arguments = NULL;
|
||||||
_argptr = NULL;
|
_argptr = NULL;
|
||||||
|
|
||||||
|
retValSlot = NULL;
|
||||||
|
retBlock = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void IrFunction::setNeverInline()
|
void IrFunction::setNeverInline()
|
||||||
|
|
274
ir/irfunction.h
274
ir/irfunction.h
|
@ -19,97 +19,240 @@
|
||||||
#include "llvm/ADT/DenseMap.h"
|
#include "llvm/ADT/DenseMap.h"
|
||||||
#include "llvm/ADT/DenseMapInfo.h"
|
#include "llvm/ADT/DenseMapInfo.h"
|
||||||
#include "gen/llvm.h"
|
#include "gen/llvm.h"
|
||||||
#include "ir/irlandingpad.h"
|
|
||||||
#include "ir/irfuncty.h"
|
#include "ir/irfuncty.h"
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <stack>
|
#include <stack>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
class Identifier;
|
||||||
class Statement;
|
class Statement;
|
||||||
struct EnclosingTryFinally;
|
|
||||||
struct IRState;
|
struct IRState;
|
||||||
|
|
||||||
// scope statements that can be target of jumps
|
/// Represents a position on the stack of currently active cleanup scopes.
|
||||||
// includes loops, switch, case, labels
|
///
|
||||||
struct IRTargetScope
|
/// Since we always need to run a contiguous part of the stack (or all) in
|
||||||
{
|
/// order, this is enough to uniquely identify the location of a given target.
|
||||||
// generating statement
|
typedef size_t CleanupCursor;
|
||||||
Statement* s;
|
|
||||||
|
|
||||||
// the try-finally block that encloses the statement
|
/// Stores information needed to correctly jump to a given label or loop
|
||||||
EnclosingTryFinally* enclosinghandler;
|
/// statement (break/continue).
|
||||||
|
struct JumpTarget {
|
||||||
|
/// The basic block to ultimately branch to.
|
||||||
|
llvm::BasicBlock* targetBlock;
|
||||||
|
|
||||||
llvm::BasicBlock* breakTarget;
|
/// The index of the label target in the stack of active cleanup scopes.
|
||||||
llvm::BasicBlock* continueTarget;
|
///
|
||||||
|
/// When generating code for a jump to this label, the cleanups between
|
||||||
|
/// the current depth and that of the level will be emitted. Note that
|
||||||
|
/// we need to handle only one direction (towards the root of the stack)
|
||||||
|
/// because D forbids gotos into try or finally blocks.
|
||||||
|
// TODO: We might not be able to detect illegal jumps across try-finally
|
||||||
|
// blocks by only storing the index.
|
||||||
|
CleanupCursor cleanupScope;
|
||||||
|
|
||||||
/// If true, the breakTarget is only considered if it is explicitly
|
/// Keeps target of the associated loop or switch statement so we can
|
||||||
/// specified (via s), and not for unqualified "break;" statements.
|
/// handle both unlabeled and labeled jumps.
|
||||||
bool onlyLabeledBreak;
|
Statement* targetStatement;
|
||||||
|
|
||||||
IRTargetScope(
|
|
||||||
Statement* s,
|
|
||||||
EnclosingTryFinally* enclosinghandler,
|
|
||||||
llvm::BasicBlock* continueTarget,
|
|
||||||
llvm::BasicBlock* breakTarget,
|
|
||||||
bool onlyLabeledBreak = false
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FuncGen
|
/// Defines source and target label of a goto (used if we cannot immediately
|
||||||
{
|
/// figure out the target basic block).
|
||||||
FuncGen();
|
struct GotoJump {
|
||||||
|
// The location of the jump instruction, for error reporting.
|
||||||
|
Loc sourceLoc;
|
||||||
|
|
||||||
// pushes a unique label scope of the given name
|
/// The basic block which contains the goto as its terminator.
|
||||||
void pushUniqueLabelScope(const char* name);
|
llvm::BasicBlock* sourceBlock;
|
||||||
// pops a label scope
|
|
||||||
void popLabelScope();
|
|
||||||
|
|
||||||
// gets the string under which the label's BB
|
/// While we have not found the actual branch target, we might need to
|
||||||
// is stored in the labelToBB map.
|
/// create a "fake" basic block in order to be able to execute the cleanups
|
||||||
// essentially prefixes ident by the strings in labelScopes
|
/// (we do not keep branching information around after leaving the scope).
|
||||||
std::string getScopedLabelName(const char* ident);
|
llvm::BasicBlock* tentativeTarget;
|
||||||
|
|
||||||
// label to basic block lookup
|
/// The label to target with the goto.
|
||||||
typedef std::map<std::string, llvm::BasicBlock*> LabelToBBMap;
|
Identifier* targetLabel;
|
||||||
LabelToBBMap labelToBB;
|
};
|
||||||
|
|
||||||
// loop blocks
|
/// Describes a particular way to leave a certain scope and continue execution
|
||||||
typedef std::vector<IRTargetScope> TargetScopeVec;
|
/// at another one (return, break/continue, exception handling, etc.).
|
||||||
TargetScopeVec targetScopes;
|
struct CleanupExitTarget {
|
||||||
|
explicit CleanupExitTarget(llvm::BasicBlock* t) : branchTarget(t) {}
|
||||||
|
|
||||||
// landing pads for try statements
|
/// The target basic block to branch to after running the cleanup.
|
||||||
IRLandingPad landingPadInfo;
|
llvm::BasicBlock* branchTarget;
|
||||||
llvm::BasicBlock* landingPad;
|
|
||||||
|
|
||||||
void pushToElemScope();
|
/// The basic blocks that want to continue with this target after running
|
||||||
void popToElemScope(bool destructTemporaries);
|
/// the cleanup. We need to keep this information around so we can insert
|
||||||
|
/// stores to the branch selector variable when converting from one to two
|
||||||
|
/// targets.
|
||||||
|
std::vector<llvm::BasicBlock*> sourceBlocks;
|
||||||
|
};
|
||||||
|
|
||||||
void pushTemporaryToDestruct(VarDeclaration* vd);
|
/// Represents a scope (in abstract terms, not curly braces) that requires a
|
||||||
bool hasTemporariesToDestruct();
|
/// piece of cleanup code to be run whenever it is left, whether as part of
|
||||||
VarDeclarations& getTemporariesToDestruct();
|
/// normal control flow or exception unwinding.
|
||||||
|
///
|
||||||
|
/// This includes finally blocks (which are also generated by the frontend for
|
||||||
|
/// running the destructors of non-temporary variables) and the destructors of
|
||||||
|
/// temporaries (which are unfortunately not lowered by the frontend).
|
||||||
|
///
|
||||||
|
/// Our goal is to only emit each cleanup once such as to avoid generating an
|
||||||
|
/// exponential number of basic blocks/landing pads for handling all the
|
||||||
|
/// different ways of exiting a deeply nested scope (exception/no exception/...).
|
||||||
|
class CleanupScope {
|
||||||
|
public:
|
||||||
|
CleanupScope(llvm::BasicBlock* beginBlock, llvm::BasicBlock* endBlock) :
|
||||||
|
beginBlock(beginBlock), endBlock(endBlock), branchSelector(0) {}
|
||||||
|
|
||||||
void destructAllTemporariesAndRestoreStack();
|
/// The basic block to branch to for running the cleanup.
|
||||||
// pushes a landing pad which needs to be popped after the
|
llvm::BasicBlock* beginBlock;
|
||||||
// following invoke instruction
|
|
||||||
void prepareToDestructAllTemporariesOnThrow(IRState* irState);
|
/// The basic block that contains the end of the cleanuip code (is different
|
||||||
|
/// from beginBlock if the cleanup contains control flow).
|
||||||
|
llvm::BasicBlock* endBlock;
|
||||||
|
|
||||||
|
/// The branch selector variable, or null if not created yet.
|
||||||
|
llvm::AllocaInst* branchSelector;
|
||||||
|
|
||||||
|
/// Stores all possible targets blocks after running this cleanup, along
|
||||||
|
/// with what predecessors want to continue at that target. The index in
|
||||||
|
/// the vector corresponds to the branch selector value for that target.
|
||||||
|
std::vector<CleanupExitTarget> exitTargets;
|
||||||
|
|
||||||
|
/// Keeps track of all the gotos somewhere inside this scope for which we
|
||||||
|
/// have not found the label yet (because it occurs lexically later in the
|
||||||
|
/// function).
|
||||||
|
std::vector<GotoJump> unresolvedGotos;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Contains transitory information about the current scope, etc. while
|
||||||
|
/// traversing the function for codegen purposes.
|
||||||
|
class ScopeStack {
|
||||||
|
public:
|
||||||
|
ScopeStack(IRState *irs) : irs(irs) {}
|
||||||
|
~ScopeStack();
|
||||||
|
|
||||||
|
/// Registers a piece of cleanup code to be run.
|
||||||
|
///
|
||||||
|
/// The basic block is expected not to contain a terminator yet. It will be
|
||||||
|
/// added by ScopeStack as needed based on what followup blocks there will be
|
||||||
|
/// registered.
|
||||||
|
void pushCleanup(llvm::BasicBlock* beginBlock, llvm::BasicBlock* endBlock);
|
||||||
|
|
||||||
|
/// Terminates the current IRScope with a branch to the cleanups needed for
|
||||||
|
/// leaving the given scope and continuing execution at the target scope
|
||||||
|
/// stack level.
|
||||||
|
///
|
||||||
|
/// After running them, execution will branch to the given basic block.
|
||||||
|
void runCleanups(CleanupCursor targetScope, llvm::BasicBlock* continueWith);
|
||||||
|
|
||||||
|
/// Like #runCleanups(), but runs all of them.
|
||||||
|
void runAllCleanups(llvm::BasicBlock* continueWith);
|
||||||
|
|
||||||
|
/// Pops all the cleanups between the current scope and the target cursor.
|
||||||
|
void popCleanups(CleanupCursor targetScope);
|
||||||
|
|
||||||
|
/// Returns a cursor that identifies the curernt cleanup scope, to be later
|
||||||
|
/// userd with #popCleanups() et al.
|
||||||
|
///
|
||||||
|
/// Note that this cursor is only valid as long as the current scope is not
|
||||||
|
/// popped.
|
||||||
|
CleanupCursor currentCleanupScope() { return cleanupScopes.size(); }
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
void suspendCleanup(CleanupCursor target);
|
||||||
|
|
||||||
|
void resumeCleanup(CleanupCursor target);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
///
|
||||||
|
void pushCatch(llvm::Value* classInfoPtr, llvm::Value* exceptionVar,
|
||||||
|
llvm::BasicBlock* bodyBlock) {}
|
||||||
|
|
||||||
|
///
|
||||||
|
void popCatch() {}
|
||||||
|
|
||||||
|
///
|
||||||
|
void pushLoopTarget(Statement* loopStatement, llvm::BasicBlock* continueTarget,
|
||||||
|
llvm::BasicBlock* breakTarget);
|
||||||
|
|
||||||
|
/// Pops the last pushed loop target, so it is no longer taken into
|
||||||
|
/// consideration for resolving breaks/continues.
|
||||||
|
void popLoopTarget();
|
||||||
|
|
||||||
|
///
|
||||||
|
void pushBreakTarget(Statement* switchStatement, llvm::BasicBlock* targetBlock);
|
||||||
|
|
||||||
|
///
|
||||||
|
void popBreakTarget();
|
||||||
|
|
||||||
|
///
|
||||||
|
void addLabelTarget(Identifier* labelName, llvm::BasicBlock* targetBlock);
|
||||||
|
|
||||||
|
///
|
||||||
|
void jumpToLabel(Loc loc, Identifier* labelName);
|
||||||
|
|
||||||
|
///
|
||||||
|
void continueWithLoop(Statement* loopStatement) {
|
||||||
|
jumpToStatement(continueTargets, loopStatement);
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
void continueWithClosest() {
|
||||||
|
jumpToClosest(continueTargets);
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
void breakToStatement(Statement* loopOrSwitchStatement) {
|
||||||
|
jumpToStatement(breakTargets, loopOrSwitchStatement);
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
void breakToClosest() {
|
||||||
|
jumpToClosest(breakTargets);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// prefix for labels and gotos
|
std::vector<GotoJump>& currentUnresolvedGotos();
|
||||||
// used for allowing labels to be emitted twice
|
|
||||||
std::vector<std::string> labelScopes;
|
|
||||||
|
|
||||||
// next unique id stack
|
void jumpToStatement(std::vector<JumpTarget>& targets, Statement* loopOrSwitchStatement);
|
||||||
std::stack<int> nextUnique;
|
|
||||||
|
|
||||||
Array<unsigned> toElemScopes; // number of initial temporaries
|
void jumpToClosest(std::vector<JumpTarget>& targets);
|
||||||
VarDeclarations temporariesToDestruct;
|
|
||||||
|
|
||||||
void destructTemporaries(unsigned numToKeep);
|
IRState *irs;
|
||||||
|
|
||||||
|
typedef llvm::DenseMap<Identifier*, JumpTarget> LabelTargetMap;
|
||||||
|
/// The labels we have encountered in this function so far, accessed by
|
||||||
|
/// their associated identifier (i.e. the name of the label).
|
||||||
|
LabelTargetMap labelTargets;
|
||||||
|
|
||||||
|
///
|
||||||
|
std::vector<JumpTarget> breakTargets;
|
||||||
|
|
||||||
|
///
|
||||||
|
std::vector<JumpTarget> continueTargets;
|
||||||
|
|
||||||
|
///
|
||||||
|
std::vector<CleanupScope> cleanupScopes;
|
||||||
|
|
||||||
|
/// Gotos which we were not able to resolve to any cleanup scope, but which
|
||||||
|
/// might still be defined later in the function at top level. If there are
|
||||||
|
/// any left on function exit, it is an error (e.g. because the user tried
|
||||||
|
/// to goto into a finally block, etc.).
|
||||||
|
std::vector<GotoJump> topLevelUnresolvedGotos;
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
/// To be able to handle the broken AST the frontend produces in some cases
|
||||||
|
/// for temporary constructor calls, we need to be able to temporarily
|
||||||
|
/// suspend a cleanup from being run without removing it from the stack.
|
||||||
|
// FIXME: Make sure this does not break our usual assumption that all
|
||||||
|
// temporaries are run all the time.
|
||||||
|
std::vector<CleanupCursor> suspendedCleanups;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
// represents a function
|
// represents a function
|
||||||
struct IrFunction
|
struct IrFunction {
|
||||||
{
|
|
||||||
// constructor
|
// constructor
|
||||||
IrFunction(FuncDeclaration* fd);
|
IrFunction(FuncDeclaration* fd);
|
||||||
|
|
||||||
|
@ -122,7 +265,7 @@ struct IrFunction
|
||||||
FuncDeclaration* decl;
|
FuncDeclaration* decl;
|
||||||
TypeFunction* type;
|
TypeFunction* type;
|
||||||
|
|
||||||
FuncGen* gen;
|
ScopeStack* scopes;
|
||||||
|
|
||||||
bool queued;
|
bool queued;
|
||||||
bool defined;
|
bool defined;
|
||||||
|
@ -141,6 +284,11 @@ struct IrFunction
|
||||||
llvm::Value* _arguments;
|
llvm::Value* _arguments;
|
||||||
llvm::Value* _argptr;
|
llvm::Value* _argptr;
|
||||||
|
|
||||||
|
// A stack slot containing the return value, for functions that return by value.
|
||||||
|
llvm::AllocaInst* retValSlot;
|
||||||
|
// The basic block with the return instruction.
|
||||||
|
llvm::BasicBlock* retBlock;
|
||||||
|
|
||||||
#if LDC_LLVM_VER >= 307
|
#if LDC_LLVM_VER >= 307
|
||||||
llvm::DISubprogram* diSubprogram = nullptr;
|
llvm::DISubprogram* diSubprogram = nullptr;
|
||||||
std::stack<llvm::DILexicalBlock*> diLexicalBlocks;
|
std::stack<llvm::DILexicalBlock*> diLexicalBlocks;
|
||||||
|
|
|
@ -1,253 +0,0 @@
|
||||||
//===-- irlandingpad.cpp --------------------------------------------------===//
|
|
||||||
//
|
|
||||||
// LDC – the LLVM D compiler
|
|
||||||
//
|
|
||||||
// This file is distributed under the BSD-style LDC license. See the LICENSE
|
|
||||||
// file for details.
|
|
||||||
//
|
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
|
|
||||||
#include "gen/llvm.h"
|
|
||||||
#include "gen/classes.h"
|
|
||||||
#include "gen/irstate.h"
|
|
||||||
#include "gen/llvmhelpers.h"
|
|
||||||
#include "gen/logger.h"
|
|
||||||
#include "gen/runtime.h"
|
|
||||||
#include "gen/tollvm.h"
|
|
||||||
#include "ir/irlandingpad.h"
|
|
||||||
|
|
||||||
// creates new landing pad
|
|
||||||
static llvm::LandingPadInst *createLandingPadInst()
|
|
||||||
{
|
|
||||||
LLType* retType = LLStructType::get(LLType::getInt8PtrTy(gIR->context()),
|
|
||||||
LLType::getInt32Ty(gIR->context()),
|
|
||||||
NULL);
|
|
||||||
#if LDC_LLVM_VER >= 307
|
|
||||||
LLFunction* currentFunction = gIR->func()->func;
|
|
||||||
if (!currentFunction->hasPersonalityFn()) {
|
|
||||||
LLFunction* personalityFn = LLVM_D_GetRuntimeFunction(Loc(), gIR->module, "_d_eh_personality");
|
|
||||||
currentFunction->setPersonalityFn(personalityFn);
|
|
||||||
}
|
|
||||||
return gIR->ir->CreateLandingPad(retType, 0, "landing_pad");
|
|
||||||
#else
|
|
||||||
LLFunction* personalityFn = LLVM_D_GetRuntimeFunction(Loc(), gIR->module, "_d_eh_personality");
|
|
||||||
return gIR->ir->CreateLandingPad(retType, personalityFn, 0, "landing_pad");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
IRLandingPadCatchInfo::IRLandingPadCatchInfo(Catch* catchstmt_, llvm::BasicBlock* end_) :
|
|
||||||
end(end_), catchStmt(catchstmt_)
|
|
||||||
{
|
|
||||||
target = llvm::BasicBlock::Create(gIR->context(), "catch", gIR->topfunc(), end);
|
|
||||||
|
|
||||||
assert(catchStmt->type);
|
|
||||||
catchType = catchStmt->type->toBasetype()->isClassHandle();
|
|
||||||
assert(catchType);
|
|
||||||
DtoResolveClass(catchType);
|
|
||||||
|
|
||||||
if (catchStmt->var) {
|
|
||||||
if (!catchStmt->var->nestedrefs.dim) {
|
|
||||||
gIR->func()->gen->landingPadInfo.getExceptionStorage();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void IRLandingPadCatchInfo::toIR()
|
|
||||||
{
|
|
||||||
if (!catchStmt)
|
|
||||||
return;
|
|
||||||
|
|
||||||
gIR->scope() = IRScope(target);
|
|
||||||
gIR->DBuilder.EmitBlockStart(catchStmt->loc);
|
|
||||||
|
|
||||||
LLFunction* enterCatchFn =
|
|
||||||
LLVM_D_GetRuntimeFunction(Loc(), gIR->module, "_d_eh_enter_catch");
|
|
||||||
gIR->ir->CreateCall(enterCatchFn);
|
|
||||||
|
|
||||||
// assign storage to catch var
|
|
||||||
if (catchStmt->var) {
|
|
||||||
LLType* llCatchVarType = DtoType(catchStmt->var->type); // e.g., Throwable*
|
|
||||||
LLValue* catch_var = gIR->func()->gen->landingPadInfo.getExceptionStorage();
|
|
||||||
|
|
||||||
// use the same storage for all exceptions that are not accessed in
|
|
||||||
// nested functions
|
|
||||||
if (!catchStmt->var->nestedrefs.dim) {
|
|
||||||
assert(!isIrLocalCreated(catchStmt->var));
|
|
||||||
IrLocal* irLocal = getIrLocal(catchStmt->var, true);
|
|
||||||
irLocal->value = DtoBitCast(catch_var, getPtrToType(llCatchVarType));
|
|
||||||
} else {
|
|
||||||
// this will alloca if we haven't already and take care of nested refs
|
|
||||||
DtoDeclarationExp(catchStmt->var);
|
|
||||||
IrLocal* irLocal = getIrLocal(catchStmt->var);
|
|
||||||
|
|
||||||
// the exception is stored in catch_var. copy it over if necessary
|
|
||||||
LLValue* exc = DtoBitCast(DtoLoad(catch_var), llCatchVarType);
|
|
||||||
DtoStore(exc, irLocal->value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// emit handler, if there is one
|
|
||||||
// handler is zero for instance for 'catch { debug foo(); }'
|
|
||||||
if (catchStmt->handler)
|
|
||||||
Statement_toIR(catchStmt->handler, gIR);
|
|
||||||
|
|
||||||
if (!gIR->scopereturned())
|
|
||||||
gIR->ir->CreateBr(end);
|
|
||||||
|
|
||||||
gIR->DBuilder.EmitBlockEnd();
|
|
||||||
}
|
|
||||||
|
|
||||||
IRLandingPadFinallyStatementInfo::IRLandingPadFinallyStatementInfo(Statement *finallyBody_) :
|
|
||||||
finallyBody(finallyBody_)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void IRLandingPadFinallyStatementInfo::toIR(LLValue *eh_ptr)
|
|
||||||
{
|
|
||||||
IRLandingPad &padInfo = gIR->func()->gen->landingPadInfo;
|
|
||||||
llvm::BasicBlock* &pad = gIR->func()->gen->landingPad;
|
|
||||||
|
|
||||||
gIR->DBuilder.EmitBlockStart(finallyBody->loc);
|
|
||||||
Statement_toIR(finallyBody, gIR);
|
|
||||||
gIR->DBuilder.EmitBlockEnd();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void IRLandingPad::addCatch(Catch* catchstmt, llvm::BasicBlock* end)
|
|
||||||
{
|
|
||||||
unpushedScope.catches.push_back(IRLandingPadCatchInfo(catchstmt, end));
|
|
||||||
}
|
|
||||||
|
|
||||||
void IRLandingPad::addFinally(Statement* finallyStmt)
|
|
||||||
{
|
|
||||||
addFinally(new IRLandingPadFinallyStatementInfo(finallyStmt), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void IRLandingPad::addFinally(IRLandingPadCatchFinallyInfo *finallyInfo,
|
|
||||||
bool deleteOnPop)
|
|
||||||
{
|
|
||||||
assert(unpushedScope.finally == NULL && "only one finally per try-finally block");
|
|
||||||
unpushedScope.finally = finallyInfo;
|
|
||||||
unpushedScope.isFinallyCreatedInternally = deleteOnPop;
|
|
||||||
}
|
|
||||||
|
|
||||||
void IRLandingPad::push(llvm::BasicBlock* inBB)
|
|
||||||
{
|
|
||||||
unpushedScope.target = inBB;
|
|
||||||
scopeStack.push(unpushedScope);
|
|
||||||
unpushedScope = IRLandingPadScope();
|
|
||||||
gIR->func()->gen->landingPad = get();
|
|
||||||
}
|
|
||||||
|
|
||||||
void IRLandingPad::pop()
|
|
||||||
{
|
|
||||||
IRLandingPadScope scope = scopeStack.top();
|
|
||||||
scopeStack.pop();
|
|
||||||
gIR->func()->gen->landingPad = get();
|
|
||||||
|
|
||||||
std::deque<IRLandingPadCatchInfo>::iterator itr, end = scope.catches.end();
|
|
||||||
for (itr = scope.catches.begin(); itr != end; ++itr)
|
|
||||||
itr->toIR();
|
|
||||||
constructLandingPad(scope);
|
|
||||||
if (scope.finally && scope.isFinallyCreatedInternally)
|
|
||||||
delete scope.finally;
|
|
||||||
}
|
|
||||||
|
|
||||||
llvm::BasicBlock* IRLandingPad::get()
|
|
||||||
{
|
|
||||||
if (scopeStack.size() == 0)
|
|
||||||
return NULL;
|
|
||||||
else
|
|
||||||
return scopeStack.top().target;
|
|
||||||
}
|
|
||||||
|
|
||||||
void IRLandingPad::constructLandingPad(IRLandingPadScope scope)
|
|
||||||
{
|
|
||||||
// save and rewrite scope
|
|
||||||
IRScope savedIRScope = gIR->scope();
|
|
||||||
gIR->scope() = IRScope(scope.target);
|
|
||||||
|
|
||||||
// create landingpad
|
|
||||||
llvm::LandingPadInst *landingPad = createLandingPadInst();
|
|
||||||
LLValue* eh_ptr = DtoExtractValue(landingPad, 0);
|
|
||||||
LLValue* eh_sel = DtoExtractValue(landingPad, 1);
|
|
||||||
|
|
||||||
// add landingpad clauses, emit finallys and 'if' chain to catch the exception
|
|
||||||
llvm::Function* eh_typeid_for_fn = GET_INTRINSIC_DECL(eh_typeid_for);
|
|
||||||
|
|
||||||
bool isFirstCatch = true;
|
|
||||||
std::stack<IRLandingPadScope> savedScopeStack = scopeStack;
|
|
||||||
std::deque<IRLandingPadCatchInfo>::iterator catchItr, catchItrEnd;
|
|
||||||
while (true) {
|
|
||||||
catchItr = scope.catches.begin();
|
|
||||||
catchItrEnd = scope.catches.end();
|
|
||||||
for (; catchItr != catchItrEnd; ++catchItr) {
|
|
||||||
// if it is a first catch and some catch allocated storage, store exception object
|
|
||||||
if (isFirstCatch && catch_var) {
|
|
||||||
#if LDC_LLVM_VER >= 305
|
|
||||||
if (global.params.targetTriple.isWindowsMSVCEnvironment())
|
|
||||||
{
|
|
||||||
// eh_ptr is a pointer to the Throwable object.
|
|
||||||
LLType *objectTy = DtoType(ClassDeclaration::object->type);
|
|
||||||
LLValue *object = gIR->ir->CreateBitCast(eh_ptr, objectTy);
|
|
||||||
gIR->ir->CreateStore(object, catch_var);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
// eh_ptr is a pointer to _d_exception, which has a reference
|
|
||||||
// to the Throwable object at offset 0.
|
|
||||||
LLType *objectPtrTy = DtoType(ClassDeclaration::object->type->pointerTo());
|
|
||||||
LLValue *objectPtr = gIR->ir->CreateBitCast(eh_ptr, objectPtrTy);
|
|
||||||
gIR->ir->CreateStore(gIR->ir->CreateLoad(objectPtr), catch_var);
|
|
||||||
}
|
|
||||||
isFirstCatch = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// create next block
|
|
||||||
llvm::BasicBlock *next = llvm::BasicBlock::Create(gIR->context(), "eh.next", gIR->topfunc());
|
|
||||||
// get class info symbol
|
|
||||||
LLValue *classInfo = getIrAggr(catchItr->catchType)->getClassInfoSymbol();
|
|
||||||
// add that symbol as landing pad clause
|
|
||||||
landingPad->addClause(llvm::cast<llvm::Constant>(classInfo));
|
|
||||||
// call llvm.eh.typeid.for to get class info index in the exception table
|
|
||||||
classInfo = DtoBitCast(classInfo, getPtrToType(DtoType(Type::tint8)));
|
|
||||||
LLValue *eh_id = gIR->ir->CreateCall(eh_typeid_for_fn, classInfo);
|
|
||||||
// check exception selector (eh_sel) against the class info index
|
|
||||||
gIR->ir->CreateCondBr(gIR->ir->CreateICmpEQ(eh_sel, eh_id), catchItr->target, next);
|
|
||||||
gIR->scope() = IRScope(next);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (scope.finally) {
|
|
||||||
scope.finally->toIR(eh_ptr);
|
|
||||||
landingPad->setCleanup(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (scopeStack.empty())
|
|
||||||
break;
|
|
||||||
scope = scopeStack.top();
|
|
||||||
scopeStack.pop();
|
|
||||||
gIR->func()->gen->landingPad = get();
|
|
||||||
}
|
|
||||||
|
|
||||||
// restore landing pad infos
|
|
||||||
scopeStack = savedScopeStack;
|
|
||||||
gIR->func()->gen->landingPad = get();
|
|
||||||
|
|
||||||
// no catch matched and all finallys executed - resume unwind
|
|
||||||
llvm::Function* unwind_resume_fn = LLVM_D_GetRuntimeFunction(Loc(), gIR->module, "_d_eh_resume_unwind");
|
|
||||||
gIR->ir->CreateCall(unwind_resume_fn, eh_ptr);
|
|
||||||
gIR->ir->CreateUnreachable();
|
|
||||||
|
|
||||||
// restore scope
|
|
||||||
gIR->scope() = savedIRScope;
|
|
||||||
}
|
|
||||||
|
|
||||||
LLValue* IRLandingPad::getExceptionStorage()
|
|
||||||
{
|
|
||||||
if (!catch_var) {
|
|
||||||
Logger::println("Making new catch var");
|
|
||||||
catch_var = DtoAlloca(ClassDeclaration::object->type, "catchvar");
|
|
||||||
}
|
|
||||||
return catch_var;
|
|
||||||
}
|
|
|
@ -1,125 +0,0 @@
|
||||||
//===-- ir/irlandingpad.h - Codegen state for EH blocks ---------*- C++ -*-===//
|
|
||||||
//
|
|
||||||
// LDC – the LLVM D compiler
|
|
||||||
//
|
|
||||||
// This file is distributed under the BSD-style LDC license. See the LICENSE
|
|
||||||
// file for details.
|
|
||||||
//
|
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
//
|
|
||||||
// State kept while doing codegen for a single "EH block" consisting of
|
|
||||||
// of several catch/finally/cleanup clauses. Handles nesting of these blocks.
|
|
||||||
//
|
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
|
|
||||||
#ifndef LDC_IR_IRLANDINGPADINFO_H
|
|
||||||
#define LDC_IR_IRLANDINGPADINFO_H
|
|
||||||
|
|
||||||
#include "statement.h"
|
|
||||||
#include <deque>
|
|
||||||
#include <stack>
|
|
||||||
|
|
||||||
namespace llvm {
|
|
||||||
class Type;
|
|
||||||
class Value;
|
|
||||||
class BasicBlock;
|
|
||||||
class Function;
|
|
||||||
}
|
|
||||||
|
|
||||||
// holds information about a single catch
|
|
||||||
struct IRLandingPadCatchInfo
|
|
||||||
{
|
|
||||||
// default constructor for being able to store in a vector
|
|
||||||
IRLandingPadCatchInfo() :
|
|
||||||
target(NULL), end(0), catchStmt(NULL), catchType(0)
|
|
||||||
{}
|
|
||||||
|
|
||||||
IRLandingPadCatchInfo(Catch* catchStmt, llvm::BasicBlock* end);
|
|
||||||
|
|
||||||
// codegen the catch block
|
|
||||||
void toIR();
|
|
||||||
|
|
||||||
llvm::BasicBlock *target;
|
|
||||||
llvm::BasicBlock *end;
|
|
||||||
Catch *catchStmt;
|
|
||||||
ClassDeclaration *catchType;
|
|
||||||
};
|
|
||||||
|
|
||||||
// holds information about a single finally
|
|
||||||
class IRLandingPadCatchFinallyInfo
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual ~IRLandingPadCatchFinallyInfo() {}
|
|
||||||
virtual void toIR(LLValue *eh_ptr) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
class IRLandingPadFinallyStatementInfo : public IRLandingPadCatchFinallyInfo
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
IRLandingPadFinallyStatementInfo(Statement *finallyBody);
|
|
||||||
// codegen the finally block
|
|
||||||
void toIR(LLValue *eh_ptr);
|
|
||||||
private:
|
|
||||||
// the body of finally
|
|
||||||
Statement *finallyBody;
|
|
||||||
};
|
|
||||||
|
|
||||||
// holds information about a single try-catch-finally block
|
|
||||||
struct IRLandingPadScope
|
|
||||||
{
|
|
||||||
explicit IRLandingPadScope(llvm::BasicBlock *target_ = NULL) :
|
|
||||||
target(target_), finally(0), isFinallyCreatedInternally(false) {}
|
|
||||||
|
|
||||||
// the target for invokes
|
|
||||||
llvm::BasicBlock *target;
|
|
||||||
// information about catch blocks
|
|
||||||
std::deque<IRLandingPadCatchInfo> catches;
|
|
||||||
// information about a finally block
|
|
||||||
IRLandingPadCatchFinallyInfo *finally;
|
|
||||||
bool isFinallyCreatedInternally;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// holds information about all possible catch and finally actions
|
|
||||||
// and can emit landing pads to be called from the unwind runtime
|
|
||||||
struct IRLandingPad
|
|
||||||
{
|
|
||||||
IRLandingPad() : catch_var(NULL) {}
|
|
||||||
|
|
||||||
// creates a new landing pad according to given infos
|
|
||||||
// and the ones on the stack. also stores it as invoke target
|
|
||||||
void push(llvm::BasicBlock* inBB);
|
|
||||||
|
|
||||||
// add catch information, will be used in next call to push
|
|
||||||
void addCatch(Catch* catchstmt, llvm::BasicBlock* end);
|
|
||||||
// add finally statement, will be used in next call to push
|
|
||||||
void addFinally(Statement* finallyStmt);
|
|
||||||
// add finally information, will be used in next call to push
|
|
||||||
void addFinally(IRLandingPadCatchFinallyInfo *finallyInfo,
|
|
||||||
bool deleteOnPop = false);
|
|
||||||
|
|
||||||
// builds the most recently constructed landing pad
|
|
||||||
// and the catch blocks, then pops the landing pad bb
|
|
||||||
// and its infos
|
|
||||||
void pop();
|
|
||||||
|
|
||||||
// creates or gets storage for exception object
|
|
||||||
llvm::Value* getExceptionStorage();
|
|
||||||
|
|
||||||
private:
|
|
||||||
friend class IRLandingPadFinallyStatementInfo;
|
|
||||||
// gets the current landing pad
|
|
||||||
llvm::BasicBlock* get();
|
|
||||||
|
|
||||||
// constructs the landing pad
|
|
||||||
void constructLandingPad(IRLandingPadScope scope);
|
|
||||||
|
|
||||||
// information about try-catch-finally blocks
|
|
||||||
std::stack<IRLandingPadScope> scopeStack;
|
|
||||||
IRLandingPadScope unpushedScope;
|
|
||||||
|
|
||||||
// storage for the catch variable
|
|
||||||
llvm::Value* catch_var;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
Loading…
Add table
Add a link
Reference in a new issue