void ScopeStack::popCleanups(CleanupCursor targetScope) { assert(targetScope <= currentCleanupScope()); if (targetScope == currentCleanupScope()) { return; } for (CleanupCursor i = currentCleanupScope(); i-- > targetScope;) { // Any gotos that are still unresolved necessarily leave this scope. // Thus, the cleanup needs to be executed. for (const auto &gotoJump : currentUnresolvedGotos()) { // Make the source resp. last cleanup branch to this one. llvm::BasicBlock *tentative = gotoJump.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], gotoJump.sourceBlock, tentative); } std::vector<GotoJump> &nextUnresolved = (i == 0) ? topLevelUnresolvedGotos : cleanupScopes[i - 1].unresolvedGotos; nextUnresolved.insert(nextUnresolved.end(), currentUnresolvedGotos().begin(), currentUnresolvedGotos().end()); cleanupScopes.pop_back(); } }
TryCatchFinallyScopes::~TryCatchFinallyScopes() { assert(currentCleanupScope() == 0); // If there are still unresolved gotos left, it means that they were either // down or "sideways" (i.e. down another branch) of the tree of all // cleanup scopes, both of which are not allowed in D. if (!currentUnresolvedGotos().empty()) { for (const auto &i : currentUnresolvedGotos()) { error(i.sourceLoc, "`goto` into `try`/`finally` scope is not allowed"); } fatal(); } }
void ScopeStack::jumpToLabel(Loc loc, Identifier *labelName) { // If we have already seen that label, branch to it, executing any cleanups // as necessary. auto 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().emplace_back(loc, irs->scopebb(), target, labelName); }
void TryCatchFinallyScopes::popCleanups(CleanupCursor targetScope) { assert(targetScope <= currentCleanupScope()); if (targetScope == currentCleanupScope()) return; for (CleanupCursor i = currentCleanupScope(); i-- > targetScope;) { // Any gotos that are still unresolved necessarily leave this scope. // Thus, the cleanup needs to be executed. for (const auto &gotoJump : currentUnresolvedGotos()) { // Replace all branches to the tentative target by branches to the cleanup // and continue with the tentative target (we simply reuse it because // there is no reason not to). llvm::BasicBlock *tentative = gotoJump.tentativeTarget; // 1) Replace all branches to the tentative target by branches to a // temporary placeholder BB. llvm::BasicBlock *dummy = irs.insertBB(""); tentative->replaceAllUsesWith(dummy); // 2) We need a cleanup instance which continues execution with the // tentative target. auto startCleanup = cleanupScopes[i].run(irs, gotoJump.sourceBlock, tentative); // 3) Replace all branches to the placeholder BB by branches to the // cleanup. dummy->replaceAllUsesWith(startCleanup); dummy->eraseFromParent(); } Gotos &nextUnresolved = unresolvedGotosPerCleanupScope[i]; nextUnresolved.insert(nextUnresolved.end(), currentUnresolvedGotos().begin(), currentUnresolvedGotos().end()); cleanupScopes.pop_back(); unresolvedGotosPerCleanupScope.pop_back(); landingPadsPerCleanupScope.pop_back(); } }
void TryCatchFinallyScopes::tryResolveGotos(Identifier *labelName, llvm::BasicBlock *targetBlock) { auto &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::addLabelTarget(Identifier *labelName, llvm::BasicBlock *targetBlock) { labelTargets[labelName] = {targetBlock, currentCleanupScope(), nullptr}; // See whether any of the unresolved gotos target this label, and resolve // those that do. 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 TryCatchFinallyScopes::registerUnresolvedGoto(Loc loc, Identifier *labelName) { llvm::BasicBlock *target = irs.insertBB("goto.unresolved"); irs.ir->CreateBr(target); currentUnresolvedGotos().push_back({loc, irs.scopebb(), target, labelName}); }