Use static allocas for temporaries. (#5734)

Previously we created allocas for temporaries at whatever point in the
output LLVM function we'd reached. This would result in these being
dynamic allocas (performing a dynamic stack allocation), which is
inefficent and can lead to a stack overflow if it happens in a loop.

Switch to putting the allocas in the entry block instead, and instead
generate a lifetime start marker when we reach the point where the
temporary is introduced. We already did this for local variables; this
is just factoring out and reusing that code.
This commit is contained in:
Richard Smith
2025-06-25 21:53:39 +00:00
committed by GitHub
parent 3585b31813
commit a556cf41fc
16 changed files with 286 additions and 176 deletions
+44
View File
@@ -198,6 +198,50 @@ auto FunctionContext::MakeSyntheticBlock() -> llvm::BasicBlock* {
return synthetic_block_;
}
auto FunctionContext::CreateAlloca(llvm::Type* type, const llvm::Twine& name)
-> llvm::AllocaInst* {
// Position the first alloca right before the start of the executable code in
// the function.
llvm::AllocaInst* alloca;
{
llvm::IRBuilderBase::InsertPointGuard guard(builder());
auto debug_loc = builder().getCurrentDebugLocation();
if (after_allocas_) {
builder().SetInsertPoint(after_allocas_);
} else {
builder().SetInsertPointPastAllocas(&llvm_function());
}
// IRBuilder tramples over our debug location when setting the insert point,
// so undo that.
builder().SetCurrentDebugLocation(debug_loc);
// Create an alloca for this variable in the entry block.
alloca = builder().CreateAlloca(type, /*ArraySize=*/nullptr, name);
}
// Create a lifetime start intrinsic here to indicate where its scope really
// begins.
auto size = llvm_module().getDataLayout().getTypeAllocSize(type);
builder().CreateLifetimeStart(
alloca, llvm::ConstantInt::get(llvm_context(), llvm::APInt(64, size)));
// If we just created the first alloca, there is now definitely at least one
// instruction after it -- there is a lifetime start instruction if nothing
// else. Use that instruction as our insert point for all future allocas.
if (!after_allocas_) {
auto loc = alloca->getIterator();
++loc;
after_allocas_ = &*loc;
}
// TODO: Create a matching `@llvm.lifetime.end` intrinsic call when the
// variable goes out of scope.
return alloca;
}
auto FunctionContext::GetDebugLoc(SemIR::InstId inst_id) -> llvm::DebugLoc {
if (!di_subprogram_) {
return llvm::DebugLoc();