authorgravatar for timonkruiper@gmail.comTimon Kruiper <timonkruiper@gmail.com> 2021-01-04 01:08:37+01:00
committergravatar for timonkruiper@gmail.comTimon Kruiper <timonkruiper@gmail.com> 2021-01-06 10:52:07+01:00
log70f6d16ae2d2d9bf8690742c7eba2798b5395174
treecfd702d3a961fc70c4c6b73bb207776cd6338fe2
parent6c4924408b1957d493568271a0158b1190574dd0

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; } ```

3 files changed, 158 insertions(+), 57 deletions(-)

src/llvm_backend.zig+124-57
......@@ -4,6 +4,7 @@ const Allocator = std.mem.Allocator;
44const Compilation = @import("Compilation.zig");
55const llvm = @import("llvm_bindings.zig");
66const link = @import("link.zig");
7const log = std.log.scoped(.codegen);
78
89const Module = @import("Module.zig");
910const TypedValue = @import("TypedValue.zig");
......@@ -288,8 +289,7 @@ pub const LLVMIRModule = struct {
288289 }
289290
290291 pub fn updateDecl(self: *LLVMIRModule, module: *Module, decl: *Module.Decl) !void {
291 const typed_value = decl.typed_value.most_recent.typed_value;
292 self.gen(module, typed_value, decl.src()) catch |err| switch (err) {
292 self.gen(module, decl) catch |err| switch (err) {
293293 error.CodegenFail => {
294294 decl.analysis = .codegen_failure;
295295 try module.failed_decls.put(module.gpa, decl, self.err_msg.?);
......@@ -300,11 +300,16 @@ pub const LLVMIRModule = struct {
300300 };
301301 }
302302
303 fn gen(self: *LLVMIRModule, module: *Module, typed_value: TypedValue, src: usize) !void {
304 if (typed_value.val.castTag(.function)) |func_inst| {
305 const func = func_inst.data;
303 fn gen(self: *LLVMIRModule, module: *Module, decl: *Module.Decl) !void {
304 const typed_value = decl.typed_value.most_recent.typed_value;
305 const src = decl.src();
306
307 log.debug("gen: {s} type: {}, value: {}", .{ decl.name, typed_value.ty, typed_value.val });
308
309 if (typed_value.val.castTag(.function)) |func_payload| {
310 const func = func_payload.data;
306311
307 const llvm_func = try self.resolveLLVMFunction(func, src);
312 const llvm_func = try self.resolveLLVMFunction(func.owner_decl, src);
308313
309314 // This gets the LLVM values from the function and stores them in `self.args`.
310315 const fn_param_len = func.owner_decl.typed_value.most_recent.typed_value.ty.fnParamLen();
......@@ -355,48 +360,55 @@ pub const LLVMIRModule = struct {
355360 };
356361 if (opt_llvm_val) |llvm_val| try self.func_inst_table.putNoClobber(self.gpa, inst, llvm_val);
357362 }
363 } else if (typed_value.val.castTag(.extern_fn)) |extern_fn| {
364 _ = try self.resolveLLVMFunction(extern_fn.data, src);
358365 } else {
359 return self.fail(src, "TODO implement LLVM codegen for top-level decl type: {}", .{typed_value.ty});
366 _ = try self.resolveGlobalDecl(decl, src);
360367 }
361368 }
362369
363370 fn genCall(self: *LLVMIRModule, inst: *Inst.Call) !?*const llvm.ValueRef {
364371 if (inst.func.value()) |func_value| {
365 if (func_value.castTag(.function)) |func_payload| {
366 const func = func_payload.data;
367 const zig_fn_type = func.owner_decl.typed_value.most_recent.typed_value.ty;
368 const llvm_fn = try self.resolveLLVMFunction(func, inst.base.src);
372 const fn_decl = if (func_value.castTag(.extern_fn)) |extern_fn|
373 extern_fn.data
374 else if (func_value.castTag(.function)) |func_payload|
375 func_payload.data.owner_decl
376 else
377 unreachable;
369378
370 const num_args = inst.args.len;
379 const zig_fn_type = fn_decl.typed_value.most_recent.typed_value.ty;
380 const llvm_fn = try self.resolveLLVMFunction(fn_decl, inst.base.src);
371381
372 const llvm_param_vals = try self.gpa.alloc(*const llvm.ValueRef, num_args);
373 defer self.gpa.free(llvm_param_vals);
382 const num_args = inst.args.len;
374383
375 for (inst.args) |arg, i| {
376 llvm_param_vals[i] = try self.resolveInst(arg);
377 }
384 const llvm_param_vals = try self.gpa.alloc(*const llvm.ValueRef, num_args);
385 defer self.gpa.free(llvm_param_vals);
378386
379 // TODO: LLVMBuildCall2 handles opaque function pointers, according to llvm docs
380 // Do we need that?
381 const call = self.builder.buildCall(
382 llvm_fn,
383 if (num_args == 0) null else llvm_param_vals.ptr,
384 @intCast(c_uint, num_args),
385 "",
386 );
387
388 const return_type = zig_fn_type.fnReturnType();
389 if (return_type.tag() == .noreturn) {
390 _ = self.builder.buildUnreachable();
391 }
387 for (inst.args) |arg, i| {
388 llvm_param_vals[i] = try self.resolveInst(arg);
389 }
392390
393 // No need to store the LLVM value if the return type is void or noreturn
394 if (!return_type.hasCodeGenBits()) return null;
391 // TODO: LLVMBuildCall2 handles opaque function pointers, according to llvm docs
392 // Do we need that?
393 const call = self.builder.buildCall(
394 llvm_fn,
395 if (num_args == 0) null else llvm_param_vals.ptr,
396 @intCast(c_uint, num_args),
397 "",
398 );
395399
396 return call;
400 const return_type = zig_fn_type.fnReturnType();
401 if (return_type.tag() == .noreturn) {
402 _ = self.builder.buildUnreachable();
397403 }
404
405 // No need to store the LLVM value if the return type is void or noreturn
406 if (!return_type.hasCodeGenBits()) return null;
407
408 return call;
409 } else {
410 return self.fail(inst.base.src, "TODO implement calling runtime known function pointer LLVM backend", .{});
398411 }
399 return self.fail(inst.base.src, "TODO implement calling runtime known function pointer LLVM backend", .{});
400412 }
401413
402414 fn genRetVoid(self: *LLVMIRModule, inst: *Inst.NoOp) ?*const llvm.ValueRef {
......@@ -515,7 +527,7 @@ pub const LLVMIRModule = struct {
515527 return self.fail(inst.src, "TODO implement global llvm values (or the value is not in the func_inst_table table)", .{});
516528 }
517529
518 fn genTypedValue(self: *LLVMIRModule, src: usize, tv: TypedValue) !*const llvm.ValueRef {
530 fn genTypedValue(self: *LLVMIRModule, src: usize, tv: TypedValue) error{ OutOfMemory, CodegenFail }!*const llvm.ValueRef {
519531 const llvm_type = try self.getLLVMType(tv.ty, src);
520532
521533 if (tv.val.isUndef())
......@@ -538,16 +550,89 @@ pub const LLVMIRModule = struct {
538550 }
539551 return llvm_int;
540552 },
553 .Pointer => switch (tv.val.tag()) {
554 .decl_ref => {
555 const decl = tv.val.castTag(.decl_ref).?.data;
556 const val = try self.resolveGlobalDecl(decl, src);
557
558 const usize_type = try self.getLLVMType(Type.initTag(.usize), src);
559
560 // TODO: second index should be the index into the memory!
561 var indices: [2]*const llvm.ValueRef = .{
562 usize_type.constNull(),
563 usize_type.constNull(),
564 };
565
566 // TODO: consider using buildInBoundsGEP2 for opaque pointers
567 return self.builder.buildInBoundsGEP(val, &indices, 2, "");
568 },
569 else => return self.fail(src, "TODO implement const of pointer type '{}'", .{tv.ty}),
570 },
571 .Array => {
572 if (tv.val.castTag(.bytes)) |payload| {
573 const zero_sentinel = if (tv.ty.sentinel()) |sentinel| blk: {
574 if (sentinel.tag() == .zero) break :blk true;
575 return self.fail(src, "TODO handle other sentinel values", .{});
576 } else false;
577
578 return llvm.constString(payload.data.ptr, @intCast(c_uint, payload.data.len), !zero_sentinel);
579 } else {
580 return self.fail(src, "TODO handle more array values", .{});
581 }
582 },
541583 else => return self.fail(src, "TODO implement const of type '{}'", .{tv.ty}),
542584 }
543585 }
544586
587 fn getLLVMType(self: *LLVMIRModule, t: Type, src: usize) error{ OutOfMemory, CodegenFail }!*const llvm.TypeRef {
588 switch (t.zigTypeTag()) {
589 .Void => return llvm.voidType(),
590 .NoReturn => return llvm.voidType(),
591 .Int => {
592 const info = t.intInfo(self.module.getTarget());
593 return llvm.intType(info.bits);
594 },
595 .Bool => return llvm.intType(1),
596 .Pointer => {
597 if (t.isSlice()) {
598 return self.fail(src, "TODO: LLVM backend: implement slices", .{});
599 } else {
600 const elem_type = try self.getLLVMType(t.elemType(), src);
601 return elem_type.pointerType(0);
602 }
603 },
604 .Array => {
605 const elem_type = try self.getLLVMType(t.elemType(), src);
606 return elem_type.arrayType(@intCast(c_uint, t.abiSize(self.module.getTarget())));
607 },
608 else => return self.fail(src, "TODO implement getLLVMType for type '{}'", .{t}),
609 }
610 }
611
612 fn resolveGlobalDecl(self: *LLVMIRModule, decl: *Module.Decl, src: usize) error{ OutOfMemory, CodegenFail }!*const llvm.ValueRef {
613 // TODO: do we want to store this in our own datastructure?
614 if (self.llvm_module.getNamedGlobal(decl.name)) |val| return val;
615
616 const typed_value = decl.typed_value.most_recent.typed_value;
617
618 // TODO: remove this redundant `getLLVMType`, it is also called in `genTypedValue`.
619 const llvm_type = try self.getLLVMType(typed_value.ty, src);
620 const val = try self.genTypedValue(src, typed_value);
621 const global = self.llvm_module.addGlobal(llvm_type, decl.name);
622 llvm.setInitializer(global, val);
623
624 // TODO ask the Decl if it is const
625 // https://github.com/ziglang/zig/issues/7582
626
627 return global;
628 }
629
545630 /// If the llvm function does not exist, create it
546 fn resolveLLVMFunction(self: *LLVMIRModule, func: *Module.Fn, src: usize) !*const llvm.ValueRef {
631 fn resolveLLVMFunction(self: *LLVMIRModule, func: *Module.Decl, src: usize) !*const llvm.ValueRef {
547632 // TODO: do we want to store this in our own datastructure?
548 if (self.llvm_module.getNamedFunction(func.owner_decl.name)) |llvm_fn| return llvm_fn;
633 if (self.llvm_module.getNamedFunction(func.name)) |llvm_fn| return llvm_fn;
549634
550 const zig_fn_type = func.owner_decl.typed_value.most_recent.typed_value.ty;
635 const zig_fn_type = func.typed_value.most_recent.typed_value.ty;
551636 const return_type = zig_fn_type.fnReturnType();
552637
553638 const fn_param_len = zig_fn_type.fnParamLen();
......@@ -569,7 +654,7 @@ pub const LLVMIRModule = struct {
569654 @intCast(c_uint, fn_param_len),
570655 false,
571656 );
572 const llvm_fn = self.llvm_module.addFunction(func.owner_decl.name, fn_type);
657 const llvm_fn = self.llvm_module.addFunction(func.name, fn_type);
573658
574659 if (return_type.tag() == .noreturn) {
575660 llvm_fn.addFnAttr("noreturn");
......@@ -578,24 +663,6 @@ pub const LLVMIRModule = struct {
578663 return llvm_fn;
579664 }
580665
581 fn getLLVMType(self: *LLVMIRModule, t: Type, src: usize) error{ OutOfMemory, CodegenFail }!*const llvm.TypeRef {
582 switch (t.zigTypeTag()) {
583 .Void => return llvm.voidType(),
584 .NoReturn => return llvm.voidType(),
585 .Int => {
586 const info = t.intInfo(self.module.getTarget());
587 return llvm.intType(info.bits);
588 },
589 .Bool => return llvm.intType(1),
590 .Pointer => {
591 const pointer = t.castPointer().?;
592 const elem_type = try self.getLLVMType(pointer.data, src);
593 return elem_type.pointerType(0);
594 },
595 else => return self.fail(src, "TODO implement getLLVMType for type '{}'", .{t}),
596 }
597 }
598
599666 pub fn fail(self: *LLVMIRModule, src: usize, comptime format: []const u8, args: anytype) error{ OutOfMemory, CodegenFail } {
600667 @setCold(true);
601668 assert(self.err_msg == null);
src/llvm_bindings.zig+21
......@@ -46,11 +46,17 @@ pub const TypeRef = opaque {
4646 pub const constInt = LLVMConstInt;
4747 extern fn LLVMConstInt(IntTy: *const TypeRef, N: c_ulonglong, SignExtend: LLVMBool) *const ValueRef;
4848
49 pub const constArray = LLVMConstArray;
50 extern fn LLVMConstArray(ElementTy: *const TypeRef, ConstantVals: ?[*]*const ValueRef, Length: c_uint) *const ValueRef;
51
4952 pub const getUndef = LLVMGetUndef;
5053 extern fn LLVMGetUndef(Ty: *const TypeRef) *const ValueRef;
5154
5255 pub const pointerType = LLVMPointerType;
5356 extern fn LLVMPointerType(ElementType: *const TypeRef, AddressSpace: c_uint) *const TypeRef;
57
58 pub const arrayType = LLVMArrayType;
59 extern fn LLVMArrayType(ElementType: *const TypeRef, ElementCount: c_uint) *const TypeRef;
5460};
5561
5662pub const ModuleRef = opaque {
......@@ -74,6 +80,12 @@ pub const ModuleRef = opaque {
7480
7581 pub const printToString = LLVMPrintModuleToString;
7682 extern fn LLVMPrintModuleToString(*const ModuleRef) [*:0]const u8;
83
84 pub const addGlobal = LLVMAddGlobal;
85 extern fn LLVMAddGlobal(M: *const ModuleRef, Ty: *const TypeRef, Name: [*:0]const u8) *const ValueRef;
86
87 pub const getNamedGlobal = LLVMGetNamedGlobal;
88 extern fn LLVMGetNamedGlobal(M: *const ModuleRef, Name: [*:0]const u8) ?*const ValueRef;
7789};
7890
7991pub const lookupIntrinsicID = LLVMLookupIntrinsicID;
......@@ -91,6 +103,12 @@ pub const VerifierFailureAction = extern enum {
91103pub const constNeg = LLVMConstNeg;
92104extern fn LLVMConstNeg(ConstantVal: *const ValueRef) *const ValueRef;
93105
106pub const constString = LLVMConstString;
107extern fn LLVMConstString(Str: [*]const u8, Length: c_uint, DontNullTerminate: LLVMBool) *const ValueRef;
108
109pub const setInitializer = LLVMSetInitializer;
110extern fn LLVMSetInitializer(GlobalVar: *const ValueRef, ConstantVal: *const ValueRef) void;
111
94112pub const voidType = LLVMVoidType;
95113extern fn LLVMVoidType() *const TypeRef;
96114
......@@ -170,6 +188,9 @@ pub const BuilderRef = opaque {
170188
171189 pub const buildBitCast = LLVMBuildBitCast;
172190 extern fn LLVMBuildBitCast(*const BuilderRef, Val: *const ValueRef, DestTy: *const TypeRef, Name: [*:0]const u8) *const ValueRef;
191
192 pub const buildInBoundsGEP = LLVMBuildInBoundsGEP;
193 extern fn LLVMBuildInBoundsGEP(B: *const BuilderRef, Pointer: *const ValueRef, Indices: [*]*const ValueRef, NumIndices: c_uint, Name: [*:0]const u8) *const ValueRef;
173194};
174195
175196pub const BasicBlockRef = opaque {
test/stage2/llvm_backend.zig+13
......@@ -27,4 +27,17 @@ pub fn addCases(ctx: *TestContext) !void {
2727 \\}
2828 , "");
2929 }
30
31 {
32 var case = ctx.exeUsingLlvmBackend("hello world", linux_x64);
33
34 case.addCompareOutput(
35 \\extern fn puts(s: [*:0]const u8) c_int;
36 \\
37 \\export fn main() c_int {
38 \\ _ = puts("hello world!");
39 \\ return 0;
40 \\}
41 , "hello world!" ++ std.cstr.line_sep);
42 }
3043}