authorgravatar for timonkruiper@gmail.comTimon Kruiper <timonkruiper@gmail.com> 2021-01-05 18:35:42+01:00
committergravatar for timonkruiper@gmail.comTimon Kruiper <timonkruiper@gmail.com> 2021-01-06 10:52:20+01:00
log5d5db833f277518bab152b743d6a4cfaa1e3fd8d
treec7f898a8b063ccb8cf6c249a508ebec14dad0164
parent1149cd593e82b338058037c29bcd0e2caaab0dcb

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 } ```

2 files changed, 44 insertions(+), 4 deletions(-)

src/llvm_backend.zig+35-4
......@@ -148,6 +148,9 @@ pub const LLVMIRModule = struct {
148148 gpa: *Allocator,
149149 err_msg: ?*Compilation.ErrorMsg = null,
150150
151 // TODO: The fields below should really move into a different struct,
152 // because they are only valid when generating a function
153
151154 /// This stores the LLVM values used in a function, such that they can be
152155 /// referred to in other instructions. This table is cleared before every function is generated.
153156 func_inst_table: std.AutoHashMapUnmanaged(*Inst, *const llvm.Value) = .{},
......@@ -156,6 +159,11 @@ pub const LLVMIRModule = struct {
156159 args: []*const llvm.Value = &[_]*const llvm.Value{},
157160 arg_index: usize = 0,
158161
162 entry_block: *const llvm.BasicBlock = undefined,
163 /// This fields stores the last alloca instruction, such that we can append more alloca instructions
164 /// to the top of the function.
165 latest_alloca_inst: ?*const llvm.Value = null,
166
159167 pub fn create(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*LLVMIRModule {
160168 const self = try allocator.create(LLVMIRModule);
161169 errdefer allocator.destroy(self);
......@@ -332,8 +340,9 @@ pub const LLVMIRModule = struct {
332340 bb.deleteBasicBlock();
333341 }
334342
335 const entry_block = llvm_func.appendBasicBlock("Entry");
336 self.builder.positionBuilderAtEnd(entry_block);
343 self.entry_block = llvm_func.appendBasicBlock("Entry");
344 self.builder.positionBuilderAtEnd(self.entry_block);
345 self.latest_alloca_inst = null;
337346
338347 const instructions = func.body.instructions;
339348 for (instructions) |inst| {
......@@ -476,7 +485,7 @@ pub const LLVMIRModule = struct {
476485 const arg_val = self.args[self.arg_index];
477486 self.arg_index += 1;
478487
479 const ptr_val = self.builder.buildAlloca(try self.getLLVMType(inst.base.ty, inst.base.src), "");
488 const ptr_val = self.buildAlloca(try self.getLLVMType(inst.base.ty, inst.base.src));
480489 _ = self.builder.buildStore(arg_val, ptr_val);
481490 return self.builder.buildLoad(ptr_val, "");
482491 }
......@@ -488,7 +497,29 @@ pub const LLVMIRModule = struct {
488497
489498 // TODO: figure out a way to get the name of the var decl.
490499 // TODO: set alignment and volatile
491 return self.builder.buildAlloca(try self.getLLVMType(pointee_type, inst.base.src), "");
500 return self.buildAlloca(try self.getLLVMType(pointee_type, inst.base.src));
501 }
502
503 /// Use this instead of builder.buildAlloca, because this function makes sure to
504 /// put the alloca instruction at the top of the function!
505 fn buildAlloca(self: *LLVMIRModule, t: *const llvm.Type) *const llvm.Value {
506 if (self.latest_alloca_inst) |latest_alloc| {
507 // builder.positionBuilder adds it before the instruction,
508 // but we want to put it after the last alloca instruction.
509 self.builder.positionBuilder(self.entry_block, latest_alloc.getNextInstruction().?);
510 } else {
511 // There might have been other instructions emitted before the
512 // first alloca has been generated. However the alloca should still
513 // be first in the function.
514 if (self.entry_block.getFirstInstruction()) |first_inst| {
515 self.builder.positionBuilder(self.entry_block, first_inst);
516 }
517 }
518 defer self.builder.positionBuilderAtEnd(self.entry_block);
519
520 const val = self.builder.buildAlloca(t, "");
521 self.latest_alloca_inst = val;
522 return val;
492523 }
493524
494525 fn genStore(self: *LLVMIRModule, inst: *Inst.BinOp) !?*const llvm.Value {
src/llvm_bindings.zig+9
......@@ -17,6 +17,9 @@ pub const Value = opaque {
1717 pub const getFirstBasicBlock = LLVMGetFirstBasicBlock;
1818 extern fn LLVMGetFirstBasicBlock(Fn: *const Value) ?*const BasicBlock;
1919
20 pub const getNextInstruction = LLVMGetNextInstruction;
21 extern fn LLVMGetNextInstruction(Inst: *const Value) ?*const Value;
22
2023 // Helper functions
2124 // TODO: Do we want to put these functions here? It allows for convienient function calls
2225 // on Value: llvm_fn.addFnAttr("noreturn")
......@@ -138,6 +141,9 @@ pub const Builder = opaque {
138141 pub const disposeBuilder = LLVMDisposeBuilder;
139142 extern fn LLVMDisposeBuilder(Builder: *const Builder) void;
140143
144 pub const positionBuilder = LLVMPositionBuilder;
145 extern fn LLVMPositionBuilder(Builder: *const Builder, Block: *const BasicBlock, Instr: *const Value) void;
146
141147 pub const positionBuilderAtEnd = LLVMPositionBuilderAtEnd;
142148 extern fn LLVMPositionBuilderAtEnd(Builder: *const Builder, Block: *const BasicBlock) void;
143149
......@@ -196,6 +202,9 @@ pub const Builder = opaque {
196202pub const BasicBlock = opaque {
197203 pub const deleteBasicBlock = LLVMDeleteBasicBlock;
198204 extern fn LLVMDeleteBasicBlock(BB: *const BasicBlock) void;
205
206 pub const getFirstInstruction = LLVMGetFirstInstruction;
207 extern fn LLVMGetFirstInstruction(BB: *const BasicBlock) ?*const Value;
199208};
200209
201210pub const TargetMachine = opaque {