aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTimon Kruiper <timonkruiper@gmail.com>2021-01-04 01:08:37 +0100
committerTimon Kruiper <timonkruiper@gmail.com>2021-01-06 10:52:07 +0100
commit70f6d16ae2d2d9bf8690742c7eba2798b5395174 (patch)
treecfd702d3a961fc70c4c6b73bb207776cd6338fe2 /src
parent6c4924408b1957d493568271a0158b1190574dd0 (diff)
downloadzig-70f6d16ae2d2d9bf8690742c7eba2798b5395174.tar.gz
zig-70f6d16ae2d2d9bf8690742c7eba2798b5395174.zip
stage2: add initial impl for generating global decls in LLVM backend
Also adds support for extern functions, simple pointer and simple array types and values. A simple hello world now compiles: `zig build-exe example.zig -fLLVM -lc` ``` extern fn puts(s: [*:0]const u8) c_int; export fn main() c_int { _ = puts("hello world!"); return 0; } ```
Diffstat (limited to 'src')
-rw-r--r--src/llvm_backend.zig181
-rw-r--r--src/llvm_bindings.zig21
2 files changed, 145 insertions, 57 deletions
diff --git a/src/llvm_backend.zig b/src/llvm_backend.zig
index b0e59e3ffa..20d821f1da 100644
--- a/src/llvm_backend.zig
+++ b/src/llvm_backend.zig
@@ -4,6 +4,7 @@ const Allocator = std.mem.Allocator;
const Compilation = @import("Compilation.zig");
const llvm = @import("llvm_bindings.zig");
const link = @import("link.zig");
+const log = std.log.scoped(.codegen);
const Module = @import("Module.zig");
const TypedValue = @import("TypedValue.zig");
@@ -288,8 +289,7 @@ pub const LLVMIRModule = struct {
}
pub fn updateDecl(self: *LLVMIRModule, module: *Module, decl: *Module.Decl) !void {
- const typed_value = decl.typed_value.most_recent.typed_value;
- self.gen(module, typed_value, decl.src()) catch |err| switch (err) {
+ self.gen(module, decl) catch |err| switch (err) {
error.CodegenFail => {
decl.analysis = .codegen_failure;
try module.failed_decls.put(module.gpa, decl, self.err_msg.?);
@@ -300,11 +300,16 @@ pub const LLVMIRModule = struct {
};
}
- fn gen(self: *LLVMIRModule, module: *Module, typed_value: TypedValue, src: usize) !void {
- if (typed_value.val.castTag(.function)) |func_inst| {
- const func = func_inst.data;
+ fn gen(self: *LLVMIRModule, module: *Module, decl: *Module.Decl) !void {
+ const typed_value = decl.typed_value.most_recent.typed_value;
+ const src = decl.src();
+
+ log.debug("gen: {s} type: {}, value: {}", .{ decl.name, typed_value.ty, typed_value.val });
+
+ if (typed_value.val.castTag(.function)) |func_payload| {
+ const func = func_payload.data;
- const llvm_func = try self.resolveLLVMFunction(func, src);
+ const llvm_func = try self.resolveLLVMFunction(func.owner_decl, src);
// This gets the LLVM values from the function and stores them in `self.args`.
const fn_param_len = func.owner_decl.typed_value.most_recent.typed_value.ty.fnParamLen();
@@ -355,48 +360,55 @@ pub const LLVMIRModule = struct {
};
if (opt_llvm_val) |llvm_val| try self.func_inst_table.putNoClobber(self.gpa, inst, llvm_val);
}
+ } else if (typed_value.val.castTag(.extern_fn)) |extern_fn| {
+ _ = try self.resolveLLVMFunction(extern_fn.data, src);
} else {
- return self.fail(src, "TODO implement LLVM codegen for top-level decl type: {}", .{typed_value.ty});
+ _ = try self.resolveGlobalDecl(decl, src);
}
}
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, inst.base.src);
+ const fn_decl = if (func_value.castTag(.extern_fn)) |extern_fn|
+ extern_fn.data
+ else if (func_value.castTag(.function)) |func_payload|
+ func_payload.data.owner_decl
+ else
+ unreachable;
- const num_args = inst.args.len;
+ const zig_fn_type = fn_decl.typed_value.most_recent.typed_value.ty;
+ const llvm_fn = try self.resolveLLVMFunction(fn_decl, inst.base.src);
- const llvm_param_vals = try self.gpa.alloc(*const llvm.ValueRef, num_args);
- defer self.gpa.free(llvm_param_vals);
+ const num_args = inst.args.len;
- for (inst.args) |arg, i| {
- llvm_param_vals[i] = try self.resolveInst(arg);
- }
+ const llvm_param_vals = try self.gpa.alloc(*const llvm.ValueRef, num_args);
+ defer self.gpa.free(llvm_param_vals);
- // TODO: LLVMBuildCall2 handles opaque function pointers, according to llvm docs
- // Do we need that?
- const call = self.builder.buildCall(
- llvm_fn,
- if (num_args == 0) null else llvm_param_vals.ptr,
- @intCast(c_uint, num_args),
- "",
- );
-
- const return_type = zig_fn_type.fnReturnType();
- if (return_type.tag() == .noreturn) {
- _ = self.builder.buildUnreachable();
- }
+ for (inst.args) |arg, i| {
+ llvm_param_vals[i] = try self.resolveInst(arg);
+ }
- // No need to store the LLVM value if the return type is void or noreturn
- if (!return_type.hasCodeGenBits()) return null;
+ // TODO: LLVMBuildCall2 handles opaque function pointers, according to llvm docs
+ // Do we need that?
+ const call = self.builder.buildCall(
+ llvm_fn,
+ if (num_args == 0) null else llvm_param_vals.ptr,
+ @intCast(c_uint, num_args),
+ "",
+ );
- return call;
+ const return_type = zig_fn_type.fnReturnType();
+ if (return_type.tag() == .noreturn) {
+ _ = self.builder.buildUnreachable();
}
+
+ // No need to store the LLVM value if the return type is void or noreturn
+ if (!return_type.hasCodeGenBits()) return null;
+
+ return call;
+ } else {
+ return self.fail(inst.base.src, "TODO implement calling runtime known function pointer LLVM backend", .{});
}
- return self.fail(inst.base.src, "TODO implement calling runtime known function pointer LLVM backend", .{});
}
fn genRetVoid(self: *LLVMIRModule, inst: *Inst.NoOp) ?*const llvm.ValueRef {
@@ -515,7 +527,7 @@ pub const LLVMIRModule = struct {
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, tv: TypedValue) !*const llvm.ValueRef {
+ fn genTypedValue(self: *LLVMIRModule, src: usize, tv: TypedValue) error{ OutOfMemory, CodegenFail }!*const llvm.ValueRef {
const llvm_type = try self.getLLVMType(tv.ty, src);
if (tv.val.isUndef())
@@ -538,16 +550,89 @@ pub const LLVMIRModule = struct {
}
return llvm_int;
},
+ .Pointer => switch (tv.val.tag()) {
+ .decl_ref => {
+ const decl = tv.val.castTag(.decl_ref).?.data;
+ const val = try self.resolveGlobalDecl(decl, src);
+
+ const usize_type = try self.getLLVMType(Type.initTag(.usize), src);
+
+ // TODO: second index should be the index into the memory!
+ var indices: [2]*const llvm.ValueRef = .{
+ usize_type.constNull(),
+ usize_type.constNull(),
+ };
+
+ // TODO: consider using buildInBoundsGEP2 for opaque pointers
+ return self.builder.buildInBoundsGEP(val, &indices, 2, "");
+ },
+ else => return self.fail(src, "TODO implement const of pointer type '{}'", .{tv.ty}),
+ },
+ .Array => {
+ if (tv.val.castTag(.bytes)) |payload| {
+ const zero_sentinel = if (tv.ty.sentinel()) |sentinel| blk: {
+ if (sentinel.tag() == .zero) break :blk true;
+ return self.fail(src, "TODO handle other sentinel values", .{});
+ } else false;
+
+ return llvm.constString(payload.data.ptr, @intCast(c_uint, payload.data.len), !zero_sentinel);
+ } else {
+ return self.fail(src, "TODO handle more array values", .{});
+ }
+ },
else => return self.fail(src, "TODO implement const of type '{}'", .{tv.ty}),
}
}
+ fn getLLVMType(self: *LLVMIRModule, t: Type, src: usize) error{ OutOfMemory, CodegenFail }!*const llvm.TypeRef {
+ switch (t.zigTypeTag()) {
+ .Void => return llvm.voidType(),
+ .NoReturn => return llvm.voidType(),
+ .Int => {
+ const info = t.intInfo(self.module.getTarget());
+ return llvm.intType(info.bits);
+ },
+ .Bool => return llvm.intType(1),
+ .Pointer => {
+ if (t.isSlice()) {
+ return self.fail(src, "TODO: LLVM backend: implement slices", .{});
+ } else {
+ const elem_type = try self.getLLVMType(t.elemType(), src);
+ return elem_type.pointerType(0);
+ }
+ },
+ .Array => {
+ const elem_type = try self.getLLVMType(t.elemType(), src);
+ return elem_type.arrayType(@intCast(c_uint, t.abiSize(self.module.getTarget())));
+ },
+ else => return self.fail(src, "TODO implement getLLVMType for type '{}'", .{t}),
+ }
+ }
+
+ fn resolveGlobalDecl(self: *LLVMIRModule, decl: *Module.Decl, src: usize) error{ OutOfMemory, CodegenFail }!*const llvm.ValueRef {
+ // TODO: do we want to store this in our own datastructure?
+ if (self.llvm_module.getNamedGlobal(decl.name)) |val| return val;
+
+ const typed_value = decl.typed_value.most_recent.typed_value;
+
+ // TODO: remove this redundant `getLLVMType`, it is also called in `genTypedValue`.
+ const llvm_type = try self.getLLVMType(typed_value.ty, src);
+ const val = try self.genTypedValue(src, typed_value);
+ const global = self.llvm_module.addGlobal(llvm_type, decl.name);
+ llvm.setInitializer(global, val);
+
+ // TODO ask the Decl if it is const
+ // https://github.com/ziglang/zig/issues/7582
+
+ return global;
+ }
+
/// If the llvm function does not exist, create it
- fn resolveLLVMFunction(self: *LLVMIRModule, func: *Module.Fn, src: usize) !*const llvm.ValueRef {
+ fn resolveLLVMFunction(self: *LLVMIRModule, func: *Module.Decl, 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;
+ if (self.llvm_module.getNamedFunction(func.name)) |llvm_fn| return llvm_fn;
- const zig_fn_type = func.owner_decl.typed_value.most_recent.typed_value.ty;
+ const zig_fn_type = func.typed_value.most_recent.typed_value.ty;
const return_type = zig_fn_type.fnReturnType();
const fn_param_len = zig_fn_type.fnParamLen();
@@ -569,7 +654,7 @@ pub const LLVMIRModule = struct {
@intCast(c_uint, fn_param_len),
false,
);
- const llvm_fn = self.llvm_module.addFunction(func.owner_decl.name, fn_type);
+ const llvm_fn = self.llvm_module.addFunction(func.name, fn_type);
if (return_type.tag() == .noreturn) {
llvm_fn.addFnAttr("noreturn");
@@ -578,24 +663,6 @@ pub const LLVMIRModule = struct {
return llvm_fn;
}
- fn getLLVMType(self: *LLVMIRModule, t: Type, src: usize) error{ OutOfMemory, CodegenFail }!*const llvm.TypeRef {
- switch (t.zigTypeTag()) {
- .Void => return llvm.voidType(),
- .NoReturn => return llvm.voidType(),
- .Int => {
- const info = t.intInfo(self.module.getTarget());
- return llvm.intType(info.bits);
- },
- .Bool => return llvm.intType(1),
- .Pointer => {
- const pointer = t.castPointer().?;
- const elem_type = try self.getLLVMType(pointer.data, src);
- return elem_type.pointerType(0);
- },
- else => return self.fail(src, "TODO implement getLLVMType for type '{}'", .{t}),
- }
- }
-
pub fn fail(self: *LLVMIRModule, src: usize, comptime format: []const u8, args: anytype) error{ OutOfMemory, CodegenFail } {
@setCold(true);
assert(self.err_msg == null);
diff --git a/src/llvm_bindings.zig b/src/llvm_bindings.zig
index aa95537328..369772d4c1 100644
--- a/src/llvm_bindings.zig
+++ b/src/llvm_bindings.zig
@@ -46,11 +46,17 @@ pub const TypeRef = opaque {
pub const constInt = LLVMConstInt;
extern fn LLVMConstInt(IntTy: *const TypeRef, N: c_ulonglong, SignExtend: LLVMBool) *const ValueRef;
+ pub const constArray = LLVMConstArray;
+ extern fn LLVMConstArray(ElementTy: *const TypeRef, ConstantVals: ?[*]*const ValueRef, Length: c_uint) *const ValueRef;
+
pub const getUndef = LLVMGetUndef;
extern fn LLVMGetUndef(Ty: *const TypeRef) *const ValueRef;
pub const pointerType = LLVMPointerType;
extern fn LLVMPointerType(ElementType: *const TypeRef, AddressSpace: c_uint) *const TypeRef;
+
+ pub const arrayType = LLVMArrayType;
+ extern fn LLVMArrayType(ElementType: *const TypeRef, ElementCount: c_uint) *const TypeRef;
};
pub const ModuleRef = opaque {
@@ -74,6 +80,12 @@ pub const ModuleRef = opaque {
pub const printToString = LLVMPrintModuleToString;
extern fn LLVMPrintModuleToString(*const ModuleRef) [*:0]const u8;
+
+ pub const addGlobal = LLVMAddGlobal;
+ extern fn LLVMAddGlobal(M: *const ModuleRef, Ty: *const TypeRef, Name: [*:0]const u8) *const ValueRef;
+
+ pub const getNamedGlobal = LLVMGetNamedGlobal;
+ extern fn LLVMGetNamedGlobal(M: *const ModuleRef, Name: [*:0]const u8) ?*const ValueRef;
};
pub const lookupIntrinsicID = LLVMLookupIntrinsicID;
@@ -91,6 +103,12 @@ pub const VerifierFailureAction = extern enum {
pub const constNeg = LLVMConstNeg;
extern fn LLVMConstNeg(ConstantVal: *const ValueRef) *const ValueRef;
+pub const constString = LLVMConstString;
+extern fn LLVMConstString(Str: [*]const u8, Length: c_uint, DontNullTerminate: LLVMBool) *const ValueRef;
+
+pub const setInitializer = LLVMSetInitializer;
+extern fn LLVMSetInitializer(GlobalVar: *const ValueRef, ConstantVal: *const ValueRef) void;
+
pub const voidType = LLVMVoidType;
extern fn LLVMVoidType() *const TypeRef;
@@ -170,6 +188,9 @@ pub const BuilderRef = opaque {
pub const buildBitCast = LLVMBuildBitCast;
extern fn LLVMBuildBitCast(*const BuilderRef, Val: *const ValueRef, DestTy: *const TypeRef, Name: [*:0]const u8) *const ValueRef;
+
+ pub const buildInBoundsGEP = LLVMBuildInBoundsGEP;
+ extern fn LLVMBuildInBoundsGEP(B: *const BuilderRef, Pointer: *const ValueRef, Indices: [*]*const ValueRef, NumIndices: c_uint, Name: [*:0]const u8) *const ValueRef;
};
pub const BasicBlockRef = opaque {