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 {
146146 gpa: *Allocator,
147147 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
149153 pub fn create(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*LLVMIRModule {
150154 const self = try allocator.create(LLVMIRModule);
151155 errdefer allocator.destroy(self);
......@@ -283,7 +287,10 @@ pub const LLVMIRModule = struct {
283287 .Fn => {
284288 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
288295 // We remove all the basic blocks of a function to support incremental
289296 // compilation!
......@@ -297,29 +304,33 @@ pub const LLVMIRModule = struct {
297304
298305 const instructions = func.body.instructions;
299306 for (instructions) |inst| {
300 switch (inst.tag) {
307 const opt_llvm_val: ?*const llvm.ValueRef = switch (inst.tag) {
301308 .breakpoint => try self.genBreakpoint(inst.castTag(.breakpoint).?),
302309 .call => try self.genCall(inst.castTag(.call).?),
303310 .unreach => self.genUnreach(inst.castTag(.unreach).?),
304311 .retvoid => self.genRetVoid(inst.castTag(.retvoid).?),
305 .arg => self.genArg(inst.castTag(.arg).?),
306 .dbg_stmt => {
312 .arg => try self.genArg(inst.castTag(.arg).?),
313 .alloc => try self.genAlloc(inst.castTag(.alloc).?),
314 .store => try self.genStore(inst.castTag(.store).?),
315 .dbg_stmt => blk: {
307316 // TODO: implement debug info
317 break :blk null;
308318 },
309319 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);
311322 }
312323 },
313324 else => |ty| return self.fail(src, "TODO implement LLVM codegen for top-level decl type: {}", .{ty}),
314325 }
315326 }
316327
317 fn genCall(self: *LLVMIRModule, inst: *Inst.Call) !void {
328 fn genCall(self: *LLVMIRModule, inst: *Inst.Call) !?*const llvm.ValueRef {
318329 if (inst.func.value()) |func_value| {
319330 if (func_value.castTag(.function)) |func_payload| {
320331 const func = func_payload.data;
321332 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
324335 const num_args = inst.args.len;
325336
......@@ -339,42 +350,73 @@ pub const LLVMIRModule = struct {
339350 "",
340351 );
341352
342 if (zig_fn_type.fnReturnType().zigTypeTag() == .NoReturn) {
353 const return_type = zig_fn_type.fnReturnType().zigTypeTag();
354 if (return_type == .NoReturn) {
343355 _ = self.builder.buildUnreachable();
344356 }
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;
345362 }
346363 }
364 return self.fail(inst.base.src, "TODO implement calling runtime known function pointer LLVM backend", .{});
347365 }
348366
349 fn genRetVoid(self: *LLVMIRModule, inst: *Inst.NoOp) void {
367 fn genRetVoid(self: *LLVMIRModule, inst: *Inst.NoOp) ?*const llvm.ValueRef {
350368 _ = self.builder.buildRetVoid();
369 return null;
351370 }
352371
353 fn genUnreach(self: *LLVMIRModule, inst: *Inst.NoOp) void {
372 fn genUnreach(self: *LLVMIRModule, inst: *Inst.NoOp) ?*const llvm.ValueRef {
354373 _ = self.builder.buildUnreachable();
374 return null;
355375 }
356376
357 fn genArg(self: *LLVMIRModule, inst: *Inst.Arg) void {
377 fn genArg(self: *LLVMIRModule, inst: *Inst.Arg) !?*const llvm.ValueRef {
358378 // TODO: implement this
379 return null;
359380 }
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 {
362400 // TODO: Store this function somewhere such that we dont have to add it again
363401 const fn_type = llvm.TypeRef.functionType(llvm.voidType(), null, 0, false);
364402 const func = self.llvm_module.addFunction("llvm.debugtrap", fn_type);
403
365404 // TODO: add assertion: LLVMGetIntrinsicID
366405 _ = self.builder.buildCall(func, null, 0, "");
406 return null;
367407 }
368408
369409 fn resolveInst(self: *LLVMIRModule, inst: *ir.Inst) !*const llvm.ValueRef {
370410 if (inst.castTag(.constant)) |const_inst| {
371411 return self.genTypedValue(inst.src, .{ .ty = inst.ty, .val = const_inst.val });
372412 }
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)", .{});
374416 }
375417
376418 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
379421 if (typed_value.val.isUndef())
380422 return llvm_type.getUndef();
......@@ -386,7 +428,7 @@ pub const LLVMIRModule = struct {
386428 }
387429
388430 /// 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 {
390432 // TODO: do we want to store this in our own datastructure?
391433 if (self.llvm_module.getNamedFunction(func.owner_decl.name)) |llvm_fn| return llvm_fn;
392434
......@@ -403,11 +445,11 @@ pub const LLVMIRModule = struct {
403445 defer self.gpa.free(llvm_param);
404446
405447 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);
407449 }
408450
409451 const fn_type = llvm.TypeRef.functionType(
410 self.getLLVMType(return_type),
452 try self.getLLVMType(return_type, src),
411453 if (fn_param_len == 0) null else llvm_param.ptr,
412454 @intCast(c_uint, fn_param_len),
413455 false,
......@@ -421,7 +463,7 @@ pub const LLVMIRModule = struct {
421463 return llvm_fn;
422464 }
423465
424 fn getLLVMType(self: *LLVMIRModule, t: Type) *const llvm.TypeRef {
466 fn getLLVMType(self: *LLVMIRModule, t: Type, src: usize) !*const llvm.TypeRef {
425467 switch (t.zigTypeTag()) {
426468 .Void => return llvm.voidType(),
427469 .NoReturn => return llvm.voidType(),
......@@ -430,7 +472,7 @@ pub const LLVMIRModule = struct {
430472 return llvm.intType(info.bits);
431473 },
432474 .Bool => return llvm.intType(1),
433 else => unreachable,
475 else => return self.fail(src, "TODO implement getLLVMType for type '{}'", .{t}),
434476 }
435477 }
436478
src/llvm_bindings.zig+3
......@@ -122,6 +122,9 @@ pub const BuilderRef = opaque {
122122
123123 pub const buildAlloca = LLVMBuildAlloca;
124124 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;
125128};
126129
127130pub const BasicBlockRef = opaque {