ldc/gen/funcgenstate.cpp
Johan Engelen ef0719f36b
Implement -femit-local-var-lifetime which adds local (stack) variable… (#4395)
Implement -femit-local-var-lifetime which adds local (stack) variable lifetime annotation to LLVM IR, which enables sharing stack space for variables whose lifetimes do not overlap.
Resolves issue #2227

This is not enabled by default yet, to prevent miscompilation due to bugs (should be enabled in future for optimization levels > 0, and when sanitizers are enabled).
2023-06-01 22:45:56 +00:00

152 lines
5.3 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//===-- funcgenstate.cpp --------------------------------------------------===//
//
// LDC the LLVM D compiler
//
// This file is distributed under the BSD-style LDC license. See the LICENSE
// file for details.
//
//===----------------------------------------------------------------------===//
#include "gen/funcgenstate.h"
#include "dmd/identifier.h"
#include "gen/llvm.h"
#include "gen/llvmhelpers.h"
#include "gen/ms-cxx-helper.h"
#include "gen/runtime.h"
#include "ir/irfunction.h"
JumpTarget::JumpTarget(llvm::BasicBlock *targetBlock,
CleanupCursor cleanupScope, Statement *targetStatement)
: targetBlock(targetBlock), cleanupScope(cleanupScope),
targetStatement(targetStatement) {}
JumpTargets::JumpTargets(TryCatchFinallyScopes &scopes) : scopes(scopes) {}
void JumpTargets::pushLoopTarget(Statement *loopStatement,
llvm::BasicBlock *continueTarget,
llvm::BasicBlock *breakTarget) {
continueTargets.emplace_back(continueTarget, scopes.currentCleanupScope(),
loopStatement);
breakTargets.emplace_back(breakTarget, scopes.currentCleanupScope(),
loopStatement);
}
void JumpTargets::popLoopTarget() {
continueTargets.pop_back();
breakTargets.pop_back();
}
void JumpTargets::pushBreakTarget(Statement *switchStatement,
llvm::BasicBlock *targetBlock) {
breakTargets.push_back(
{targetBlock, scopes.currentCleanupScope(), switchStatement});
}
void JumpTargets::popBreakTarget() { breakTargets.pop_back(); }
void JumpTargets::addLabelTarget(Identifier *labelName,
llvm::BasicBlock *targetBlock) {
labelTargets[labelName] = {targetBlock, scopes.currentCleanupScope(),
nullptr};
// See whether any of the unresolved gotos target this label, and resolve
// those that do.
scopes.tryResolveGotos(labelName, targetBlock);
}
void JumpTargets::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()) {
scopes.runCleanups(it->second.cleanupScope, it->second.targetBlock);
} else {
scopes.registerUnresolvedGoto(loc, labelName);
}
}
void JumpTargets::jumpToStatement(std::vector<JumpTarget> &targets,
Statement *loopOrSwitchStatement) {
for (auto it = targets.rbegin(), end = targets.rend(); it != end; ++it) {
if (it->targetStatement == loopOrSwitchStatement) {
scopes.runCleanups(it->cleanupScope, it->targetBlock);
return;
}
}
assert(false && "Target for labeled break not found.");
}
void JumpTargets::jumpToClosest(std::vector<JumpTarget> &targets) {
assert(!targets.empty() &&
"Encountered break/continue but no loop in scope.");
JumpTarget &t = targets.back();
scopes.runCleanups(t.cleanupScope, t.targetBlock);
}
llvm::BasicBlock *SwitchCaseTargets::get(Statement *stmt) {
auto it = targetBBs.find(stmt);
assert(it != targetBBs.end());
return it->second;
}
llvm::BasicBlock *SwitchCaseTargets::getOrCreate(Statement *stmt,
const llvm::Twine &name,
IRState &irs) {
auto &bb = targetBBs[stmt];
if (!bb)
bb = irs.insertBB(name);
return bb;
}
FuncGenState::FuncGenState(IrFunction &irFunc, IRState &irs)
: irFunc(irFunc), scopes(irs), localVariableLifetimeAnnotator(irs),
jumpTargets(scopes), switchTargets(), irs(irs) {}
LLCallBasePtr FuncGenState::callOrInvoke(llvm::Value *callee,
llvm::FunctionType *calleeType,
llvm::ArrayRef<llvm::Value *> args,
const char *name, bool isNothrow) {
// If this is a direct call, we might be able to use the callee attributes
// to our advantage.
llvm::Function *calleeFn = llvm::dyn_cast<llvm::Function>(callee);
// Ignore 'nothrow' if there are active catch blocks handling non-Exception
// Throwables.
if (isNothrow && scopes.isCatchingNonExceptions())
isNothrow = false;
// Intrinsics don't support invoking and 'nounwind' functions don't need it.
const bool doesNotThrow =
isNothrow || global.params.betterC ||
(calleeFn && (calleeFn->isIntrinsic() || calleeFn->doesNotThrow()));
// calls inside a funclet must be annotated with its value
llvm::SmallVector<llvm::OperandBundleDef, 2> BundleList;
#if LDC_LLVM_VER >= 1100
llvm::FunctionCallee calleeArg(calleeType, callee);
#else
auto calleeArg = callee;
#endif
if (doesNotThrow || scopes.empty()) {
auto call = irs.ir->CreateCall(calleeArg, args, BundleList, name);
if (calleeFn) {
call->setAttributes(calleeFn->getAttributes());
}
return call;
}
llvm::BasicBlock *landingPad = scopes.getLandingPad();
llvm::BasicBlock *postinvoke = irs.insertBB("postinvoke");
auto invoke = irs.ir->CreateInvoke(calleeArg, postinvoke, landingPad, args,
BundleList, name);
if (calleeFn) {
invoke->setAttributes(calleeFn->getAttributes());
}
irs.ir->SetInsertPoint(postinvoke);
return invoke;
}