From 407f45b07d8d30a32f8c18e1b5007c174aa2aa03 Mon Sep 17 00:00:00 2001 From: Alexey Prokhin Date: Wed, 10 Jul 2013 11:51:24 +0400 Subject: [PATCH 1/5] Extend IRLandingPad to support arbitrary finally blocks. Just laying down the ground work before fixing issue 426. --- ir/irlandingpad.cpp | 93 ++++++++++++++++++++++++++++----------------- ir/irlandingpad.h | 32 ++++++++++++++-- 2 files changed, 86 insertions(+), 39 deletions(-) diff --git a/ir/irlandingpad.cpp b/ir/irlandingpad.cpp index a3036f59..684b7be1 100644 --- a/ir/irlandingpad.cpp +++ b/ir/irlandingpad.cpp @@ -17,6 +17,16 @@ #include "gen/tollvm.h" #include "ir/irlandingpad.h" +// creates new landing pad +static llvm::LandingPadInst *createLandingPadInst() +{ + llvm::Function* personality_fn = LLVM_D_GetRuntimeFunction(gIR->module, "_d_eh_personality"); + LLType *retType = LLStructType::get(LLType::getInt8PtrTy(gIR->context()), + LLType::getInt32Ty(gIR->context()), + NULL); + return gIR->ir->CreateLandingPad(retType, personality_fn, 0, "landing_pad"); +} + IRLandingPadCatchInfo::IRLandingPadCatchInfo(Catch* catchstmt_, llvm::BasicBlock* end_) : catchStmt(catchstmt_), end(end_) { @@ -74,15 +84,54 @@ void IRLandingPadCatchInfo::toIR() DtoDwarfBlockEnd(); } +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; + + // create collision landing pad that handles exceptions thrown inside the finally block + llvm::BasicBlock *collision = llvm::BasicBlock::Create(gIR->context(), "eh.collision", gIR->topfunc(), gIR->scopeend()); + llvm::BasicBlock *bb = gIR->scopebb(); + gIR->scope() = IRScope(collision, gIR->scopeend()); + llvm::LandingPadInst *collisionLandingPad = createLandingPadInst(); + LLValue* collision_eh_ptr = DtoExtractValue(collisionLandingPad, 0); + collisionLandingPad->setCleanup(true); + llvm::Function* collision_fn = LLVM_D_GetRuntimeFunction(gIR->module, "_d_eh_handle_collision"); + gIR->CreateCallOrInvoke2(collision_fn, collision_eh_ptr, eh_ptr); + gIR->ir->CreateUnreachable(); + gIR->scope() = IRScope(bb, gIR->scopeend()); + + // set collision landing pad as unwind target and emit the body of the finally + DtoDwarfBlockStart(finallyBody->loc); + padInfo.scopeStack.push(IRLandingPadScope(collision)); + pad = collision; + finallyBody->toIR(gIR); + padInfo.scopeStack.pop(); + pad = padInfo.get(); + DtoDwarfBlockEnd(); +} + void IRLandingPad::addCatch(Catch* catchstmt, llvm::BasicBlock* end) { unpushedScope.catches.push_back(IRLandingPadCatchInfo(catchstmt, end)); } -void IRLandingPad::addFinally(Statement* finallystmt) +void IRLandingPad::addFinally(Statement* finallyStmt) { - assert(unpushedScope.finallyBody == NULL && "only one finally per try-finally block"); - unpushedScope.finallyBody = finallystmt; + assert(unpushedScope.finally == NULL && "only one finally per try-finally block"); + unpushedScope.finally = new IRLandingPadFinallyStatementInfo(finallyStmt); + unpushedScope.isFinallyCreatedInternally = true; +} + +void IRLandingPad::addFinally(IRLandingPadCatchFinallyInfo *finallyInfo) +{ + assert(unpushedScope.finally == NULL && "only one finally per try-finally block"); + unpushedScope.finally = finallyInfo; } void IRLandingPad::push(llvm::BasicBlock* inBB) @@ -103,6 +152,8 @@ void IRLandingPad::pop() for (itr = scope.catches.begin(); itr != end; ++itr) itr->toIR(); constructLandingPad(scope); + if (scope.finally && scope.isFinallyCreatedInternally) + delete scope.finally; } llvm::BasicBlock* IRLandingPad::get() @@ -113,16 +164,6 @@ llvm::BasicBlock* IRLandingPad::get() return scopeStack.top().target; } -// creates new landing pad -static llvm::LandingPadInst *createLandingPadInst() -{ - llvm::Function* personality_fn = LLVM_D_GetRuntimeFunction(gIR->module, "_d_eh_personality"); - LLType *retType = LLStructType::get(LLType::getInt8PtrTy(gIR->context()), - LLType::getInt32Ty(gIR->context()), - NULL); - return gIR->ir->CreateLandingPad(retType, personality_fn, 0, "landing_pad"); -} - void IRLandingPad::constructLandingPad(IRLandingPadScope scope) { // save and rewrite scope @@ -152,6 +193,7 @@ void IRLandingPad::constructLandingPad(IRLandingPadScope scope) 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(), gIR->scopeend()); // get class info symbol @@ -166,28 +208,9 @@ void IRLandingPad::constructLandingPad(IRLandingPadScope scope) gIR->scope() = IRScope(next, gIR->scopeend()); } - if (scope.finallyBody) { - // create collision landing pad that handles exceptions thrown inside the finally block - llvm::BasicBlock *collision = llvm::BasicBlock::Create(gIR->context(), "eh.collision", gIR->topfunc(), gIR->scopeend()); - llvm::BasicBlock *bb = gIR->scopebb(); - gIR->scope() = IRScope(collision, gIR->scopeend()); - llvm::LandingPadInst *collisionLandingPad = createLandingPadInst(); - LLValue* collision_eh_ptr = DtoExtractValue(collisionLandingPad, 0); - collisionLandingPad->setCleanup(true); - llvm::Function* collision_fn = LLVM_D_GetRuntimeFunction(gIR->module, "_d_eh_handle_collision"); - gIR->CreateCallOrInvoke2(collision_fn, collision_eh_ptr, eh_ptr); - gIR->ir->CreateUnreachable(); - gIR->scope() = IRScope(bb, gIR->scopeend()); - - // set collision landing pad as unwind target and emit the body of the finally - DtoDwarfBlockStart(scope.finallyBody->loc); - scopeStack.push(IRLandingPadScope(collision)); - gIR->func()->gen->landingPad = collision; - scope.finallyBody->toIR(gIR); - scopeStack.pop(); - gIR->func()->gen->landingPad = get(); - DtoDwarfBlockEnd(); - landingPad->setCleanup(true); + if (scope.finally) { + scope.finally->toIR(eh_ptr); + landingPad->setCleanup(true); } if (scopeStack.empty()) diff --git a/ir/irlandingpad.h b/ir/irlandingpad.h index 708bb256..c70a1ded 100644 --- a/ir/irlandingpad.h +++ b/ir/irlandingpad.h @@ -47,17 +47,38 @@ struct IRLandingPadCatchInfo 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-inally block struct IRLandingPadScope { - explicit IRLandingPadScope(llvm::BasicBlock *target_ = NULL) : target(target_), finallyBody(0) {} + 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 catches; - // the body of finally - Statement *finallyBody; + // information about a finally block + IRLandingPadCatchFinallyInfo *finally; + bool isFinallyCreatedInternally; }; @@ -73,8 +94,10 @@ struct IRLandingPad // 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(Statement* finallystmt); + void addFinally(IRLandingPadCatchFinallyInfo *finallyInfo); // builds the most recently constructed landing pad // and the catch blocks, then pops the landing pad bb @@ -85,6 +108,7 @@ struct IRLandingPad llvm::Value* getExceptionStorage(); private: + friend class IRLandingPadFinallyStatementInfo; // gets the current landing pad llvm::BasicBlock* get(); From 71023952d43b570ef7ebd9586a5a12527b56a990 Mon Sep 17 00:00:00 2001 From: Alexey Prokhin Date: Wed, 10 Jul 2013 12:38:15 +0400 Subject: [PATCH 2/5] Issue #426 part 1. Wrap destructor calls of temporary variables in a try/finally expression. --- gen/toir.cpp | 50 ++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 42 insertions(+), 8 deletions(-) diff --git a/gen/toir.cpp b/gen/toir.cpp index bb3fc730..adba8ff6 100644 --- a/gen/toir.cpp +++ b/gen/toir.cpp @@ -38,6 +38,7 @@ #include "gen/warnings.h" #include "ir/irtypeclass.h" #include "ir/irtypestruct.h" +#include "ir/irlandingpad.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/ManagedStatic.h" #include @@ -64,22 +65,55 @@ void Expression::cacheLvalue(IRState* irs) * Evaluate Expression, then call destructors on any temporaries in it. */ -DValue *Expression::toElemDtor(IRState *irs) +DValue *Expression::toElemDtor(IRState *p) { Logger::println("Expression::toElemDtor(): %s", toChars()); LOG_SCOPE - size_t starti = irs->varsInScope().size(); - DValue *val = toElem(irs); - size_t endi = irs->varsInScope().size(); + class CallDestructors : public IRLandingPadCatchFinallyInfo { + public: + std::vector edtors; - // Add destructors - while (endi-- > starti) - { + void toIR(LLValue */*eh_ptr*/ = 0) + { + std::vector::const_reverse_iterator itr, end = edtors.rend(); + for (itr = edtors.rbegin(); itr != end; ++itr) + (*itr)->toElem(gIR); + } + }; + + // create finally block that calls destructors on temporaries + CallDestructors *callDestructors = new CallDestructors; + + // create landing pad + llvm::BasicBlock *oldend = p->scopeend(); + llvm::BasicBlock *landingpadbb = llvm::BasicBlock::Create(gIR->context(), "landingpad", p->topfunc(), oldend); + + // set up the landing pad + IRLandingPad& pad = gIR->func()->gen->landingPadInfo; + pad.addFinally(callDestructors); + pad.push(landingpadbb); + + // evaluate expression + size_t starti = p->varsInScope().size(); + DValue *val = toElem(p); + size_t endi = p->varsInScope().size(); + + // prepare list of the destructors + while (endi-- > starti) { VarDeclaration *vd = gIR->varsInScope().back(); gIR->varsInScope().pop_back(); - vd->edtor->toElem(gIR); + callDestructors->edtors.push_back(vd->edtor); } + + // build the landing pad + llvm::BasicBlock *oldbb = p->scopebb(); + pad.pop(); + + // call the destructors + gIR->scope() = IRScope(oldbb, oldend); + callDestructors->toIR(); + delete callDestructors; return val; } From 19997494154088be518cf9a95708fb50792db4b4 Mon Sep 17 00:00:00 2001 From: Alexey Prokhin Date: Wed, 10 Jul 2013 12:45:41 +0400 Subject: [PATCH 3/5] Issue #426 part 2. Generate a try-finally block only if it is required (i.e. there are actually some destructor calls that are needed to be put into finally) --- gen/irstate.h | 4 --- gen/llvmhelpers.cpp | 11 +----- gen/toir.cpp | 85 +++++++++++++++++++++++++++++---------------- 3 files changed, 57 insertions(+), 43 deletions(-) diff --git a/gen/irstate.h b/gen/irstate.h index 45659988..8ac8efe8 100644 --- a/gen/irstate.h +++ b/gen/irstate.h @@ -72,9 +72,6 @@ struct IRScope IRScope(llvm::BasicBlock* b, llvm::BasicBlock* e); const IRScope& operator=(const IRScope& rhs); - - // list of variables needing destruction - std::vector varsInScope; }; struct IRBuilderHelper @@ -157,7 +154,6 @@ struct IRState // basic block scopes std::vector scopes; IRScope& scope(); - std::vector &varsInScope() { return scope().varsInScope; } llvm::BasicBlock* scopebb(); llvm::BasicBlock* scopeend(); bool scopereturned(); diff --git a/gen/llvmhelpers.cpp b/gen/llvmhelpers.cpp index 8c01cd7b..9f9cf7a3 100644 --- a/gen/llvmhelpers.cpp +++ b/gen/llvmhelpers.cpp @@ -1055,7 +1055,7 @@ void DtoVarDeclaration(VarDeclaration* vd) { vd->ir.irLocal->value = val; } - goto Lexit; + return; } } } @@ -1087,15 +1087,6 @@ void DtoVarDeclaration(VarDeclaration* vd) ex->exp->toElem(gIR); } } - -Lexit: - /* Mark the point of construction of a variable that needs to be destructed. - */ - if (vd->edtor && !vd->noscope) - { - // Put vd on list of things needing destruction - gIR->varsInScope().push_back(vd); - } } DValue* DtoDeclarationExp(Dsymbol* declaration) diff --git a/gen/toir.cpp b/gen/toir.cpp index adba8ff6..4abf9c0e 100644 --- a/gen/toir.cpp +++ b/gen/toir.cpp @@ -72,7 +72,11 @@ DValue *Expression::toElemDtor(IRState *p) class CallDestructors : public IRLandingPadCatchFinallyInfo { public: - std::vector edtors; + CallDestructors(const std::vector &edtors_) + : edtors(edtors_) + {} + + const std::vector &edtors; void toIR(LLValue */*eh_ptr*/ = 0) { @@ -80,41 +84,64 @@ DValue *Expression::toElemDtor(IRState *p) for (itr = edtors.rbegin(); itr != end; ++itr) (*itr)->toElem(gIR); } + + static int searchVarsWithDesctructors(Expression *exp, void *edtors) + { + if (exp->op == TOKdeclaration) { + DeclarationExp *de = (DeclarationExp*)exp; + if (VarDeclaration *vd = de->declaration->isVarDeclaration()) { + while (vd->aliassym) { + vd = vd->aliassym->isVarDeclaration(); + if (!vd) + return 0; + } + + if (vd->init) { + if (ExpInitializer *ex = vd->init->isExpInitializer()) + ex->exp->apply(&searchVarsWithDesctructors, edtors); + } + + if (!vd->isDataseg() && vd->edtor && !vd->noscope) + static_cast*>(edtors)->push_back(vd->edtor); + } + } + return 0; + } }; - // create finally block that calls destructors on temporaries - CallDestructors *callDestructors = new CallDestructors; - // create landing pad - llvm::BasicBlock *oldend = p->scopeend(); - llvm::BasicBlock *landingpadbb = llvm::BasicBlock::Create(gIR->context(), "landingpad", p->topfunc(), oldend); + // find destructors that must be called + std::vector edtors; + apply(&CallDestructors::searchVarsWithDesctructors, &edtors); - // set up the landing pad - IRLandingPad& pad = gIR->func()->gen->landingPadInfo; - pad.addFinally(callDestructors); - pad.push(landingpadbb); + if (!edtors.empty()) { + // create finally block that calls destructors on temporaries + CallDestructors *callDestructors = new CallDestructors(edtors); - // evaluate expression - size_t starti = p->varsInScope().size(); - DValue *val = toElem(p); - size_t endi = p->varsInScope().size(); + // create landing pad + llvm::BasicBlock *oldend = p->scopeend(); + llvm::BasicBlock *landingpadbb = llvm::BasicBlock::Create(gIR->context(), "landingpad", p->topfunc(), oldend); - // prepare list of the destructors - while (endi-- > starti) { - VarDeclaration *vd = gIR->varsInScope().back(); - gIR->varsInScope().pop_back(); - callDestructors->edtors.push_back(vd->edtor); + // set up the landing pad + IRLandingPad& pad = gIR->func()->gen->landingPadInfo; + pad.addFinally(callDestructors); + pad.push(landingpadbb); + + // evaluate the expression + DValue *val = toElem(p); + + // build the landing pad + llvm::BasicBlock *oldbb = p->scopebb(); + pad.pop(); + + // call the destructors + gIR->scope() = IRScope(oldbb, oldend); + callDestructors->toIR(); + delete callDestructors; + return val; + } else { + return toElem(p); } - - // build the landing pad - llvm::BasicBlock *oldbb = p->scopebb(); - pad.pop(); - - // call the destructors - gIR->scope() = IRScope(oldbb, oldend); - callDestructors->toIR(); - delete callDestructors; - return val; } ////////////////////////////////////////////////////////////////////////////////////////// From cf32ced6a9be4356d3869a8b44a98970fc2a3ab6 Mon Sep 17 00:00:00 2001 From: Alexey Prokhin Date: Wed, 10 Jul 2013 13:20:35 +0400 Subject: [PATCH 4/5] =?UTF-8?q?Fixed=20issue=20#426=20=E2=80=94=20dtor=20/?= =?UTF-8?q?=20destructor=20not=20called=20for=20(rvalue)=20struct=20used?= =?UTF-8?q?=20in=20opApply?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/d2/dmd-testsuite | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/d2/dmd-testsuite b/tests/d2/dmd-testsuite index 0510d182..48e8b097 160000 --- a/tests/d2/dmd-testsuite +++ b/tests/d2/dmd-testsuite @@ -1 +1 @@ -Subproject commit 0510d182bfd08fc7bde95d9880a95d82f9b2e2cd +Subproject commit 48e8b097f3660df83bd01f79e6e0cda4cee48764 From 8b783da5234291f0bda83faaf8334f477eb2a43e Mon Sep 17 00:00:00 2001 From: Alexey Prokhin Date: Wed, 10 Jul 2013 18:15:05 +0400 Subject: [PATCH 5/5] Fixed cases where the destructor is called on an uninitialized temporary --- gen/toir.cpp | 49 ++++++++++++++++++++++++------------------ tests/d2/dmd-testsuite | 2 +- 2 files changed, 29 insertions(+), 22 deletions(-) diff --git a/gen/toir.cpp b/gen/toir.cpp index 4abf9c0e..99247fe8 100644 --- a/gen/toir.cpp +++ b/gen/toir.cpp @@ -115,33 +115,40 @@ DValue *Expression::toElemDtor(IRState *p) apply(&CallDestructors::searchVarsWithDesctructors, &edtors); if (!edtors.empty()) { - // create finally block that calls destructors on temporaries - CallDestructors *callDestructors = new CallDestructors(edtors); + if (op == TOKcall) { + // create finally block that calls destructors on temporaries + CallDestructors *callDestructors = new CallDestructors(edtors); - // create landing pad - llvm::BasicBlock *oldend = p->scopeend(); - llvm::BasicBlock *landingpadbb = llvm::BasicBlock::Create(gIR->context(), "landingpad", p->topfunc(), oldend); + // create landing pad + llvm::BasicBlock *oldend = p->scopeend(); + llvm::BasicBlock *landingpadbb = llvm::BasicBlock::Create(gIR->context(), "landingpad", p->topfunc(), oldend); - // set up the landing pad - IRLandingPad& pad = gIR->func()->gen->landingPadInfo; - pad.addFinally(callDestructors); - pad.push(landingpadbb); + // set up the landing pad + IRLandingPad &pad = gIR->func()->gen->landingPadInfo; + pad.addFinally(callDestructors); + pad.push(landingpadbb); - // evaluate the expression - DValue *val = toElem(p); + // evaluate the expression + DValue *val = toElem(p); - // build the landing pad - llvm::BasicBlock *oldbb = p->scopebb(); - pad.pop(); + // build the landing pad + llvm::BasicBlock *oldbb = p->scopebb(); + pad.pop(); - // call the destructors - gIR->scope() = IRScope(oldbb, oldend); - callDestructors->toIR(); - delete callDestructors; - return val; - } else { - return toElem(p); + // call the destructors + gIR->scope() = IRScope(oldbb, oldend); + callDestructors->toIR(); + delete callDestructors; + return val; + } else { + DValue *val = toElem(p); + CallDestructors(edtors).toIR(); + return val; + } } + + return toElem(p); + } ////////////////////////////////////////////////////////////////////////////////////////// diff --git a/tests/d2/dmd-testsuite b/tests/d2/dmd-testsuite index 48e8b097..e8c14b05 160000 --- a/tests/d2/dmd-testsuite +++ b/tests/d2/dmd-testsuite @@ -1 +1 @@ -Subproject commit 48e8b097f3660df83bd01f79e6e0cda4cee48764 +Subproject commit e8c14b05ca3e3642c2fe343a912cd2e800864414