authorgravatar for timonkruiper@gmail.comTimon Kruiper <timonkruiper@gmail.com> 2020-12-29 20:09:08+01:00
committergravatar for timonkruiper@gmail.comTimon Kruiper <timonkruiper@gmail.com> 2021-01-03 17:23:30+01:00
log19cfd310b0d5ba7d9542d50db281035b15daad35
tree21420f447be61d69549f283b055ae0641696711e
parenta5dab15edea5a82a43481504637ed51655c51680

stage2: implement register allocation in LLVM self-hosted backend

A HashMap has been added which store the LLVM values used in a function. Together with the alloc and store instructions the following now works: ``` export fn _start() noreturn { var x: bool = true; exit(); } fn exit() noreturn { unreachable; } ```

2 files changed, 64 insertions(+), 19 deletions(-)

src/llvm_backend.zig+61-19
...@@ -146,6 +146,10 @@ pub const LLVMIRModule = struct {...@@ -146,6 +146,10 @@ pub const LLVMIRModule = struct {
146 gpa: *Allocator,146 gpa: *Allocator,
147 err_msg: ?*Compilation.ErrorMsg = null,147 err_msg: ?*Compilation.ErrorMsg = null,
148148
149 /// This stores the LLVM values used in a function, such that they can be
150 /// referred to in other instructions. This table is cleared before every function is generated.
151 func_inst_table: std.AutoHashMapUnmanaged(*Inst, *const llvm.ValueRef) = .{},
152
149 pub fn create(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*LLVMIRModule {153 pub fn create(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*LLVMIRModule {
150 const self = try allocator.create(LLVMIRModule);154 const self = try allocator.create(LLVMIRModule);
151 errdefer allocator.destroy(self);155 errdefer allocator.destroy(self);
...@@ -283,7 +287,10 @@ pub const LLVMIRModule = struct {...@@ -283,7 +287,10 @@ pub const LLVMIRModule = struct {
283 .Fn => {287 .Fn => {
284 const func = typed_value.val.castTag(.function).?.data;288 const func = typed_value.val.castTag(.function).?.data;
285289
286 const llvm_func = try self.resolveLLVMFunction(func);290 const llvm_func = try self.resolveLLVMFunction(func, src);
291
292 // Make sure no other LLVM values from other functions can be referenced
293 self.func_inst_table.clearRetainingCapacity();
287294
288 // We remove all the basic blocks of a function to support incremental295 // We remove all the basic blocks of a function to support incremental
289 // compilation!296 // compilation!
...@@ -297,29 +304,33 @@ pub const LLVMIRModule = struct {...@@ -297,29 +304,33 @@ pub const LLVMIRModule = struct {
297304
298 const instructions = func.body.instructions;305 const instructions = func.body.instructions;
299 for (instructions) |inst| {306 for (instructions) |inst| {
300 switch (inst.tag) {307 const opt_llvm_val: ?*const llvm.ValueRef = switch (inst.tag) {
301 .breakpoint => try self.genBreakpoint(inst.castTag(.breakpoint).?),308 .breakpoint => try self.genBreakpoint(inst.castTag(.breakpoint).?),
302 .call => try self.genCall(inst.castTag(.call).?),309 .call => try self.genCall(inst.castTag(.call).?),
303 .unreach => self.genUnreach(inst.castTag(.unreach).?),310 .unreach => self.genUnreach(inst.castTag(.unreach).?),
304 .retvoid => self.genRetVoid(inst.castTag(.retvoid).?),311 .retvoid => self.genRetVoid(inst.castTag(.retvoid).?),
305 .arg => self.genArg(inst.castTag(.arg).?),312 .arg => try self.genArg(inst.castTag(.arg).?),
306 .dbg_stmt => {313 .alloc => try self.genAlloc(inst.castTag(.alloc).?),
314 .store => try self.genStore(inst.castTag(.store).?),
315 .dbg_stmt => blk: {
307 // TODO: implement debug info316 // TODO: implement debug info
317 break :blk null;
308 },318 },
309 else => |tag| return self.fail(src, "TODO implement LLVM codegen for Zir instruction: {}", .{tag}),319 else => |tag| return self.fail(src, "TODO implement LLVM codegen for Zir instruction: {}", .{tag}),
310 }320 };
321 if (opt_llvm_val) |llvm_val| try self.func_inst_table.put(self.gpa, inst, llvm_val);
311 }322 }
312 },323 },
313 else => |ty| return self.fail(src, "TODO implement LLVM codegen for top-level decl type: {}", .{ty}),324 else => |ty| return self.fail(src, "TODO implement LLVM codegen for top-level decl type: {}", .{ty}),
314 }325 }
315 }326 }
316327
317 fn genCall(self: *LLVMIRModule, inst: *Inst.Call) !void {328 fn genCall(self: *LLVMIRModule, inst: *Inst.Call) !?*const llvm.ValueRef {
318 if (inst.func.value()) |func_value| {329 if (inst.func.value()) |func_value| {
319 if (func_value.castTag(.function)) |func_payload| {330 if (func_value.castTag(.function)) |func_payload| {
320 const func = func_payload.data;331 const func = func_payload.data;
321 const zig_fn_type = func.owner_decl.typed_value.most_recent.typed_value.ty;332 const zig_fn_type = func.owner_decl.typed_value.most_recent.typed_value.ty;
322 const llvm_fn = try self.resolveLLVMFunction(func);333 const llvm_fn = try self.resolveLLVMFunction(func, inst.base.src);
323334
324 const num_args = inst.args.len;335 const num_args = inst.args.len;
325336
...@@ -339,42 +350,73 @@ pub const LLVMIRModule = struct {...@@ -339,42 +350,73 @@ pub const LLVMIRModule = struct {
339 "",350 "",
340 );351 );
341352
342 if (zig_fn_type.fnReturnType().zigTypeTag() == .NoReturn) {353 const return_type = zig_fn_type.fnReturnType().zigTypeTag();
354 if (return_type == .NoReturn) {
343 _ = self.builder.buildUnreachable();355 _ = self.builder.buildUnreachable();
344 }356 }
357
358 // No need to store the LLVM value if the return type is void or noreturn
359 if (return_type == .NoReturn or return_type == .Void) return null;
360
361 return call;
345 }362 }
346 }363 }
364 return self.fail(inst.base.src, "TODO implement calling runtime known function pointer LLVM backend", .{});
347 }365 }
348366
349 fn genRetVoid(self: *LLVMIRModule, inst: *Inst.NoOp) void {367 fn genRetVoid(self: *LLVMIRModule, inst: *Inst.NoOp) ?*const llvm.ValueRef {
350 _ = self.builder.buildRetVoid();368 _ = self.builder.buildRetVoid();
369 return null;
351 }370 }
352371
353 fn genUnreach(self: *LLVMIRModule, inst: *Inst.NoOp) void {372 fn genUnreach(self: *LLVMIRModule, inst: *Inst.NoOp) ?*const llvm.ValueRef {
354 _ = self.builder.buildUnreachable();373 _ = self.builder.buildUnreachable();
374 return null;
355 }375 }
356376
357 fn genArg(self: *LLVMIRModule, inst: *Inst.Arg) void {377 fn genArg(self: *LLVMIRModule, inst: *Inst.Arg) !?*const llvm.ValueRef {
358 // TODO: implement this378 // TODO: implement this
379 return null;
359 }380 }
360381
361 fn genBreakpoint(self: *LLVMIRModule, inst: *Inst.NoOp) !void {382 fn genAlloc(self: *LLVMIRModule, inst: *Inst.NoOp) !?*const llvm.ValueRef {
383 // buildAlloca expects the pointee type, not the pointer type, so assert that
384 // a Payload.PointerSimple is passed to the alloc instruction.
385 const pointee_type = inst.base.ty.castPointer().?.data;
386
387 // TODO: figure out a way to get the name of the var decl.
388 // TODO: set alignment and volatile
389 return self.builder.buildAlloca(try self.getLLVMType(pointee_type, inst.base.src), "");
390 }
391
392 fn genStore(self: *LLVMIRModule, inst: *Inst.BinOp) !?*const llvm.ValueRef {
393 const val = try self.resolveInst(inst.rhs);
394 const ptr = try self.resolveInst(inst.lhs);
395 _ = self.builder.buildStore(val, ptr);
396 return null;
397 }
398
399 fn genBreakpoint(self: *LLVMIRModule, inst: *Inst.NoOp) !?*const llvm.ValueRef {
362 // TODO: Store this function somewhere such that we dont have to add it again400 // TODO: Store this function somewhere such that we dont have to add it again
363 const fn_type = llvm.TypeRef.functionType(llvm.voidType(), null, 0, false);401 const fn_type = llvm.TypeRef.functionType(llvm.voidType(), null, 0, false);
364 const func = self.llvm_module.addFunction("llvm.debugtrap", fn_type);402 const func = self.llvm_module.addFunction("llvm.debugtrap", fn_type);
403
365 // TODO: add assertion: LLVMGetIntrinsicID404 // TODO: add assertion: LLVMGetIntrinsicID
366 _ = self.builder.buildCall(func, null, 0, "");405 _ = self.builder.buildCall(func, null, 0, "");
406 return null;
367 }407 }
368408
369 fn resolveInst(self: *LLVMIRModule, inst: *ir.Inst) !*const llvm.ValueRef {409 fn resolveInst(self: *LLVMIRModule, inst: *ir.Inst) !*const llvm.ValueRef {
370 if (inst.castTag(.constant)) |const_inst| {410 if (inst.castTag(.constant)) |const_inst| {
371 return self.genTypedValue(inst.src, .{ .ty = inst.ty, .val = const_inst.val });411 return self.genTypedValue(inst.src, .{ .ty = inst.ty, .val = const_inst.val });
372 }412 }
373 return self.fail(inst.src, "TODO implement resolveInst", .{});413 if (self.func_inst_table.get(inst)) |value| return value;
414
415 return self.fail(inst.src, "TODO implement global llvm values (or the value is not in the func_inst_table table)", .{});
374 }416 }
375417
376 fn genTypedValue(self: *LLVMIRModule, src: usize, typed_value: TypedValue) !*const llvm.ValueRef {418 fn genTypedValue(self: *LLVMIRModule, src: usize, typed_value: TypedValue) !*const llvm.ValueRef {
377 const llvm_type = self.getLLVMType(typed_value.ty);419 const llvm_type = try self.getLLVMType(typed_value.ty, src);
378420
379 if (typed_value.val.isUndef())421 if (typed_value.val.isUndef())
380 return llvm_type.getUndef();422 return llvm_type.getUndef();
...@@ -386,7 +428,7 @@ pub const LLVMIRModule = struct {...@@ -386,7 +428,7 @@ pub const LLVMIRModule = struct {
386 }428 }
387429
388 /// If the llvm function does not exist, create it430 /// If the llvm function does not exist, create it
389 fn resolveLLVMFunction(self: *LLVMIRModule, func: *Module.Fn) !*const llvm.ValueRef {431 fn resolveLLVMFunction(self: *LLVMIRModule, func: *Module.Fn, src: usize) !*const llvm.ValueRef {
390 // TODO: do we want to store this in our own datastructure?432 // TODO: do we want to store this in our own datastructure?
391 if (self.llvm_module.getNamedFunction(func.owner_decl.name)) |llvm_fn| return llvm_fn;433 if (self.llvm_module.getNamedFunction(func.owner_decl.name)) |llvm_fn| return llvm_fn;
392434
...@@ -403,11 +445,11 @@ pub const LLVMIRModule = struct {...@@ -403,11 +445,11 @@ pub const LLVMIRModule = struct {
403 defer self.gpa.free(llvm_param);445 defer self.gpa.free(llvm_param);
404446
405 for (fn_param_types) |fn_param, i| {447 for (fn_param_types) |fn_param, i| {
406 llvm_param[i] = self.getLLVMType(fn_param);448 llvm_param[i] = try self.getLLVMType(fn_param, src);
407 }449 }
408450
409 const fn_type = llvm.TypeRef.functionType(451 const fn_type = llvm.TypeRef.functionType(
410 self.getLLVMType(return_type),452 try self.getLLVMType(return_type, src),
411 if (fn_param_len == 0) null else llvm_param.ptr,453 if (fn_param_len == 0) null else llvm_param.ptr,
412 @intCast(c_uint, fn_param_len),454 @intCast(c_uint, fn_param_len),
413 false,455 false,
...@@ -421,7 +463,7 @@ pub const LLVMIRModule = struct {...@@ -421,7 +463,7 @@ pub const LLVMIRModule = struct {
421 return llvm_fn;463 return llvm_fn;
422 }464 }
423465
424 fn getLLVMType(self: *LLVMIRModule, t: Type) *const llvm.TypeRef {466 fn getLLVMType(self: *LLVMIRModule, t: Type, src: usize) !*const llvm.TypeRef {
425 switch (t.zigTypeTag()) {467 switch (t.zigTypeTag()) {
426 .Void => return llvm.voidType(),468 .Void => return llvm.voidType(),
427 .NoReturn => return llvm.voidType(),469 .NoReturn => return llvm.voidType(),
...@@ -430,7 +472,7 @@ pub const LLVMIRModule = struct {...@@ -430,7 +472,7 @@ pub const LLVMIRModule = struct {
430 return llvm.intType(info.bits);472 return llvm.intType(info.bits);
431 },473 },
432 .Bool => return llvm.intType(1),474 .Bool => return llvm.intType(1),
433 else => unreachable,475 else => return self.fail(src, "TODO implement getLLVMType for type '{}'", .{t}),
434 }476 }
435 }477 }
436478
src/llvm_bindings.zig+3
...@@ -122,6 +122,9 @@ pub const BuilderRef = opaque {...@@ -122,6 +122,9 @@ pub const BuilderRef = opaque {
122122
123 pub const buildAlloca = LLVMBuildAlloca;123 pub const buildAlloca = LLVMBuildAlloca;
124 extern fn LLVMBuildAlloca(*const BuilderRef, Ty: *const TypeRef, Name: [*:0]const u8) *const ValueRef;124 extern fn LLVMBuildAlloca(*const BuilderRef, Ty: *const TypeRef, Name: [*:0]const u8) *const ValueRef;
125
126 pub const buildStore = LLVMBuildStore;
127 extern fn LLVMBuildStore(*const BuilderRef, Val: *const ValueRef, Ptr: *const ValueRef) *const ValueRef;
125};128};
126129
127pub const BasicBlockRef = opaque {130pub const BasicBlockRef = opaque {