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:
Timon Kruiper
2021-01-05 18:35:42 +01:00
parent 1149cd593e
commit 5d5db833f2
2 changed files with 44 additions and 4 deletions

View File

@@ -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 {

View File

@@ -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 {