stage2: add initial implementation of func arguments in LLVM backend

The following works:
```
export fn _start() noreturn {
    assert(true);
    exit();
}

fn assert(cond: bool) void {}

fn exit() noreturn {
    unreachable;
}
```
This commit is contained in:
Timon Kruiper 2020-12-24 16:52:14 +01:00
parent 4a32d4f288
commit ec3aedffb1
2 changed files with 47 additions and 2 deletions

View File

@ -301,6 +301,7 @@ pub const LLVMIRModule = struct {
.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 => {
// TODO: implement debug info
},
@ -319,11 +320,23 @@ pub const LLVMIRModule = struct {
const zig_fn_type = func.owner_decl.typed_value.most_recent.typed_value.ty;
const llvm_fn = try self.resolveLLVMFunction(func);
// TODO: handle more arguments, inst.args
const num_args = inst.args.len;
const llvm_param_vals = try self.gpa.alloc(*const llvm.ValueRef, num_args);
defer self.gpa.free(llvm_param_vals);
for (inst.args) |arg, i| {
llvm_param_vals[i] = try self.resolveInst(arg);
}
// TODO: LLVMBuildCall2 handles opaque function pointers, according to llvm docs
// Do we need that?
const call = self.builder.buildCall(llvm_fn, null, 0, "");
const call = self.builder.buildCall(
llvm_fn,
if (num_args == 0) null else llvm_param_vals.ptr,
@intCast(c_uint, num_args),
"",
);
if (zig_fn_type.fnReturnType().zigTypeTag() == .NoReturn) {
_ = self.builder.buildUnreachable();
@ -340,6 +353,10 @@ pub const LLVMIRModule = struct {
_ = self.builder.buildUnreachable();
}
fn genArg(self: *LLVMIRModule, inst: *Inst.Arg) void {
// TODO: implement this
}
fn genBreakpoint(self: *LLVMIRModule, inst: *Inst.NoOp) !void {
// 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);
@ -348,6 +365,25 @@ pub const LLVMIRModule = struct {
_ = self.builder.buildCall(func, null, 0, "");
}
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", .{});
}
fn genTypedValue(self: *LLVMIRModule, src: usize, typed_value: TypedValue) !*const llvm.ValueRef {
const llvm_type = self.getLLVMType(typed_value.ty);
if (typed_value.val.isUndef())
return llvm_type.getUndef();
switch (typed_value.ty.zigTypeTag()) {
.Bool => return if (typed_value.val.toBool()) llvm_type.constAllOnes() else llvm_type.constNull(),
else => return self.fail(src, "TODO implement const of type '{}'", .{typed_value.ty}),
}
}
/// If the llvm function does not exist, create it
fn resolveLLVMFunction(self: *LLVMIRModule, func: *Module.Fn) !*const llvm.ValueRef {
// TODO: do we want to store this in our own datastructure?

View File

@ -36,6 +36,15 @@ pub const ValueRef = opaque {
pub const TypeRef = opaque {
pub const functionType = LLVMFunctionType;
extern fn LLVMFunctionType(ReturnType: *const TypeRef, ParamTypes: ?[*]*const TypeRef, ParamCount: c_uint, IsVarArg: LLVMBool) *const TypeRef;
pub const constNull = LLVMConstNull;
extern fn LLVMConstNull(Ty: *const TypeRef) *const ValueRef;
pub const constAllOnes = LLVMConstAllOnes;
extern fn LLVMConstAllOnes(Ty: *const TypeRef) *const ValueRef;
pub const getUndef = LLVMGetUndef;
extern fn LLVMGetUndef(Ty: *const TypeRef) *const ValueRef;
};
pub const ModuleRef = opaque {