Skip to content

Commit

Permalink
stage2: implement register allocation in LLVM self-hosted backend
Browse files Browse the repository at this point in the history
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;
}
```
  • Loading branch information
FireFox317 committed Jan 3, 2021
1 parent a5dab15 commit 19cfd31
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 19 deletions.
80 changes: 61 additions & 19 deletions src/llvm_backend.zig
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,10 @@ pub const LLVMIRModule = struct {
gpa: *Allocator,
err_msg: ?*Compilation.ErrorMsg = null,

/// 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.ValueRef) = .{},

pub fn create(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*LLVMIRModule {
const self = try allocator.create(LLVMIRModule);
errdefer allocator.destroy(self);
Expand Down Expand Up @@ -283,7 +287,10 @@ pub const LLVMIRModule = struct {
.Fn => {
const func = typed_value.val.castTag(.function).?.data;

const llvm_func = try self.resolveLLVMFunction(func);
const llvm_func = try self.resolveLLVMFunction(func, src);

// Make sure no other LLVM values from other functions can be referenced
self.func_inst_table.clearRetainingCapacity();

// We remove all the basic blocks of a function to support incremental
// compilation!
Expand All @@ -297,29 +304,33 @@ pub const LLVMIRModule = struct {

const instructions = func.body.instructions;
for (instructions) |inst| {
switch (inst.tag) {
const opt_llvm_val: ?*const llvm.ValueRef = switch (inst.tag) {
.breakpoint => try self.genBreakpoint(inst.castTag(.breakpoint).?),
.call => try self.genCall(inst.castTag(.call).?),
.unreach => self.genUnreach(inst.castTag(.unreach).?),
.retvoid => self.genRetVoid(inst.castTag(.retvoid).?),
.arg => self.genArg(inst.castTag(.arg).?),
.dbg_stmt => {
.arg => try self.genArg(inst.castTag(.arg).?),
.alloc => try self.genAlloc(inst.castTag(.alloc).?),
.store => try self.genStore(inst.castTag(.store).?),
.dbg_stmt => blk: {
// TODO: implement debug info
break :blk null;
},
else => |tag| return self.fail(src, "TODO implement LLVM codegen for Zir instruction: {}", .{tag}),
}
};
if (opt_llvm_val) |llvm_val| try self.func_inst_table.put(self.gpa, inst, llvm_val);
}
},
else => |ty| return self.fail(src, "TODO implement LLVM codegen for top-level decl type: {}", .{ty}),
}
}

fn genCall(self: *LLVMIRModule, inst: *Inst.Call) !void {
fn genCall(self: *LLVMIRModule, inst: *Inst.Call) !?*const llvm.ValueRef {
if (inst.func.value()) |func_value| {
if (func_value.castTag(.function)) |func_payload| {
const func = func_payload.data;
const zig_fn_type = func.owner_decl.typed_value.most_recent.typed_value.ty;
const llvm_fn = try self.resolveLLVMFunction(func);
const llvm_fn = try self.resolveLLVMFunction(func, inst.base.src);

const num_args = inst.args.len;

Expand All @@ -339,42 +350,73 @@ pub const LLVMIRModule = struct {
"",
);

if (zig_fn_type.fnReturnType().zigTypeTag() == .NoReturn) {
const return_type = zig_fn_type.fnReturnType().zigTypeTag();
if (return_type == .NoReturn) {
_ = self.builder.buildUnreachable();
}

// No need to store the LLVM value if the return type is void or noreturn
if (return_type == .NoReturn or return_type == .Void) return null;

return call;
}
}
return self.fail(inst.base.src, "TODO implement calling runtime known function pointer LLVM backend", .{});
}

fn genRetVoid(self: *LLVMIRModule, inst: *Inst.NoOp) void {
fn genRetVoid(self: *LLVMIRModule, inst: *Inst.NoOp) ?*const llvm.ValueRef {
_ = self.builder.buildRetVoid();
return null;
}

fn genUnreach(self: *LLVMIRModule, inst: *Inst.NoOp) void {
fn genUnreach(self: *LLVMIRModule, inst: *Inst.NoOp) ?*const llvm.ValueRef {
_ = self.builder.buildUnreachable();
return null;
}

fn genArg(self: *LLVMIRModule, inst: *Inst.Arg) void {
fn genArg(self: *LLVMIRModule, inst: *Inst.Arg) !?*const llvm.ValueRef {
// TODO: implement this
return null;
}

fn genBreakpoint(self: *LLVMIRModule, inst: *Inst.NoOp) !void {
fn genAlloc(self: *LLVMIRModule, inst: *Inst.NoOp) !?*const llvm.ValueRef {
// buildAlloca expects the pointee type, not the pointer type, so assert that
// a Payload.PointerSimple is passed to the alloc instruction.
const pointee_type = inst.base.ty.castPointer().?.data;

// 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), "");
}

fn genStore(self: *LLVMIRModule, inst: *Inst.BinOp) !?*const llvm.ValueRef {
const val = try self.resolveInst(inst.rhs);
const ptr = try self.resolveInst(inst.lhs);
_ = self.builder.buildStore(val, ptr);
return null;
}

fn genBreakpoint(self: *LLVMIRModule, inst: *Inst.NoOp) !?*const llvm.ValueRef {
// TODO: Store this function somewhere such that we dont have to add it again
const fn_type = llvm.TypeRef.functionType(llvm.voidType(), null, 0, false);
const func = self.llvm_module.addFunction("llvm.debugtrap", fn_type);

// TODO: add assertion: LLVMGetIntrinsicID
_ = self.builder.buildCall(func, null, 0, "");
return null;
}

fn resolveInst(self: *LLVMIRModule, inst: *ir.Inst) !*const llvm.ValueRef {
if (inst.castTag(.constant)) |const_inst| {
return self.genTypedValue(inst.src, .{ .ty = inst.ty, .val = const_inst.val });
}
return self.fail(inst.src, "TODO implement resolveInst", .{});
if (self.func_inst_table.get(inst)) |value| return value;

return self.fail(inst.src, "TODO implement global llvm values (or the value is not in the func_inst_table table)", .{});
}

fn genTypedValue(self: *LLVMIRModule, src: usize, typed_value: TypedValue) !*const llvm.ValueRef {
const llvm_type = self.getLLVMType(typed_value.ty);
const llvm_type = try self.getLLVMType(typed_value.ty, src);

if (typed_value.val.isUndef())
return llvm_type.getUndef();
Expand All @@ -386,7 +428,7 @@ pub const LLVMIRModule = struct {
}

/// If the llvm function does not exist, create it
fn resolveLLVMFunction(self: *LLVMIRModule, func: *Module.Fn) !*const llvm.ValueRef {
fn resolveLLVMFunction(self: *LLVMIRModule, func: *Module.Fn, src: usize) !*const llvm.ValueRef {
// TODO: do we want to store this in our own datastructure?
if (self.llvm_module.getNamedFunction(func.owner_decl.name)) |llvm_fn| return llvm_fn;

Expand All @@ -403,11 +445,11 @@ pub const LLVMIRModule = struct {
defer self.gpa.free(llvm_param);

for (fn_param_types) |fn_param, i| {
llvm_param[i] = self.getLLVMType(fn_param);
llvm_param[i] = try self.getLLVMType(fn_param, src);
}

const fn_type = llvm.TypeRef.functionType(
self.getLLVMType(return_type),
try self.getLLVMType(return_type, src),
if (fn_param_len == 0) null else llvm_param.ptr,
@intCast(c_uint, fn_param_len),
false,
Expand All @@ -421,7 +463,7 @@ pub const LLVMIRModule = struct {
return llvm_fn;
}

fn getLLVMType(self: *LLVMIRModule, t: Type) *const llvm.TypeRef {
fn getLLVMType(self: *LLVMIRModule, t: Type, src: usize) !*const llvm.TypeRef {
switch (t.zigTypeTag()) {
.Void => return llvm.voidType(),
.NoReturn => return llvm.voidType(),
Expand All @@ -430,7 +472,7 @@ pub const LLVMIRModule = struct {
return llvm.intType(info.bits);
},
.Bool => return llvm.intType(1),
else => unreachable,
else => return self.fail(src, "TODO implement getLLVMType for type '{}'", .{t}),
}
}

Expand Down
3 changes: 3 additions & 0 deletions src/llvm_bindings.zig
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,9 @@ pub const BuilderRef = opaque {

pub const buildAlloca = LLVMBuildAlloca;
extern fn LLVMBuildAlloca(*const BuilderRef, Ty: *const TypeRef, Name: [*:0]const u8) *const ValueRef;

pub const buildStore = LLVMBuildStore;
extern fn LLVMBuildStore(*const BuilderRef, Val: *const ValueRef, Ptr: *const ValueRef) *const ValueRef;
};

pub const BasicBlockRef = opaque {
Expand Down

0 comments on commit 19cfd31

Please sign in to comment.