stage2: hoist alloca instructions to top of function in LLVM backend
This way the generated code only has to setup the stack size at the beginning of a function and this improves codegen. Fixes #7689 ``` fn foo() void {} export fn hello(z: i8) void { var x: i16 = undefined; foo(); var y: i32 = 1; y += z; } ``` llvm-ir: ``` define void @hello(i8 %0) { Entry: %1 = alloca i8, align 1 %2 = alloca i16, align 2 %3 = alloca i32, align 4 store i8 %0, i8* %1, align 1 %4 = load i8, i8* %1, align 1 store i16 undef, i16* %2, align 2 call void @foo() store i32 1, i32* %3, align 4 %5 = load i32, i32* %3, align 4 %6 = sext i8 %4 to i32 %7 = add nsw i32 %5, %6 store i32 %7, i32* %3, align 4 ret void } ```
This commit is contained in:
@@ -148,6 +148,9 @@ pub const LLVMIRModule = struct {
|
||||
gpa: *Allocator,
|
||||
err_msg: ?*Compilation.ErrorMsg = null,
|
||||
|
||||
// TODO: The fields below should really move into a different struct,
|
||||
// because they are only valid when generating a function
|
||||
|
||||
/// This stores the LLVM values used in a function, such that they can be
|
||||
/// referred to in other instructions. This table is cleared before every function is generated.
|
||||
func_inst_table: std.AutoHashMapUnmanaged(*Inst, *const llvm.Value) = .{},
|
||||
@@ -156,6 +159,11 @@ pub const LLVMIRModule = struct {
|
||||
args: []*const llvm.Value = &[_]*const llvm.Value{},
|
||||
arg_index: usize = 0,
|
||||
|
||||
entry_block: *const llvm.BasicBlock = undefined,
|
||||
/// This fields stores the last alloca instruction, such that we can append more alloca instructions
|
||||
/// to the top of the function.
|
||||
latest_alloca_inst: ?*const llvm.Value = null,
|
||||
|
||||
pub fn create(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*LLVMIRModule {
|
||||
const self = try allocator.create(LLVMIRModule);
|
||||
errdefer allocator.destroy(self);
|
||||
@@ -332,8 +340,9 @@ pub const LLVMIRModule = struct {
|
||||
bb.deleteBasicBlock();
|
||||
}
|
||||
|
||||
const entry_block = llvm_func.appendBasicBlock("Entry");
|
||||
self.builder.positionBuilderAtEnd(entry_block);
|
||||
self.entry_block = llvm_func.appendBasicBlock("Entry");
|
||||
self.builder.positionBuilderAtEnd(self.entry_block);
|
||||
self.latest_alloca_inst = null;
|
||||
|
||||
const instructions = func.body.instructions;
|
||||
for (instructions) |inst| {
|
||||
@@ -476,7 +485,7 @@ pub const LLVMIRModule = struct {
|
||||
const arg_val = self.args[self.arg_index];
|
||||
self.arg_index += 1;
|
||||
|
||||
const ptr_val = self.builder.buildAlloca(try self.getLLVMType(inst.base.ty, inst.base.src), "");
|
||||
const ptr_val = self.buildAlloca(try self.getLLVMType(inst.base.ty, inst.base.src));
|
||||
_ = self.builder.buildStore(arg_val, ptr_val);
|
||||
return self.builder.buildLoad(ptr_val, "");
|
||||
}
|
||||
@@ -488,7 +497,29 @@ pub const LLVMIRModule = struct {
|
||||
|
||||
// TODO: figure out a way to get the name of the var decl.
|
||||
// TODO: set alignment and volatile
|
||||
return self.builder.buildAlloca(try self.getLLVMType(pointee_type, inst.base.src), "");
|
||||
return self.buildAlloca(try self.getLLVMType(pointee_type, inst.base.src));
|
||||
}
|
||||
|
||||
/// Use this instead of builder.buildAlloca, because this function makes sure to
|
||||
/// put the alloca instruction at the top of the function!
|
||||
fn buildAlloca(self: *LLVMIRModule, t: *const llvm.Type) *const llvm.Value {
|
||||
if (self.latest_alloca_inst) |latest_alloc| {
|
||||
// builder.positionBuilder adds it before the instruction,
|
||||
// but we want to put it after the last alloca instruction.
|
||||
self.builder.positionBuilder(self.entry_block, latest_alloc.getNextInstruction().?);
|
||||
} else {
|
||||
// There might have been other instructions emitted before the
|
||||
// first alloca has been generated. However the alloca should still
|
||||
// be first in the function.
|
||||
if (self.entry_block.getFirstInstruction()) |first_inst| {
|
||||
self.builder.positionBuilder(self.entry_block, first_inst);
|
||||
}
|
||||
}
|
||||
defer self.builder.positionBuilderAtEnd(self.entry_block);
|
||||
|
||||
const val = self.builder.buildAlloca(t, "");
|
||||
self.latest_alloca_inst = val;
|
||||
return val;
|
||||
}
|
||||
|
||||
fn genStore(self: *LLVMIRModule, inst: *Inst.BinOp) !?*const llvm.Value {
|
||||
|
||||
@@ -17,6 +17,9 @@ pub const Value = opaque {
|
||||
pub const getFirstBasicBlock = LLVMGetFirstBasicBlock;
|
||||
extern fn LLVMGetFirstBasicBlock(Fn: *const Value) ?*const BasicBlock;
|
||||
|
||||
pub const getNextInstruction = LLVMGetNextInstruction;
|
||||
extern fn LLVMGetNextInstruction(Inst: *const Value) ?*const Value;
|
||||
|
||||
// Helper functions
|
||||
// TODO: Do we want to put these functions here? It allows for convienient function calls
|
||||
// on Value: llvm_fn.addFnAttr("noreturn")
|
||||
@@ -138,6 +141,9 @@ pub const Builder = opaque {
|
||||
pub const disposeBuilder = LLVMDisposeBuilder;
|
||||
extern fn LLVMDisposeBuilder(Builder: *const Builder) void;
|
||||
|
||||
pub const positionBuilder = LLVMPositionBuilder;
|
||||
extern fn LLVMPositionBuilder(Builder: *const Builder, Block: *const BasicBlock, Instr: *const Value) void;
|
||||
|
||||
pub const positionBuilderAtEnd = LLVMPositionBuilderAtEnd;
|
||||
extern fn LLVMPositionBuilderAtEnd(Builder: *const Builder, Block: *const BasicBlock) void;
|
||||
|
||||
@@ -196,6 +202,9 @@ pub const Builder = opaque {
|
||||
pub const BasicBlock = opaque {
|
||||
pub const deleteBasicBlock = LLVMDeleteBasicBlock;
|
||||
extern fn LLVMDeleteBasicBlock(BB: *const BasicBlock) void;
|
||||
|
||||
pub const getFirstInstruction = LLVMGetFirstInstruction;
|
||||
extern fn LLVMGetFirstInstruction(BB: *const BasicBlock) ?*const Value;
|
||||
};
|
||||
|
||||
pub const TargetMachine = opaque {
|
||||
|
||||
Reference in New Issue
Block a user