aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTimon Kruiper <timonkruiper@gmail.com>2020-12-19 11:22:49 +0100
committerTimon Kruiper <timonkruiper@gmail.com>2020-12-28 21:19:40 +0100
commit071417161d5d7ab91c32073693590ef161017dbe (patch)
tree891bfd9fecd90b9b341d95dfb78399788f62c3ed /src
parent4a0d64300bd9ab1a8c35c634856396e3413200f1 (diff)
downloadzig-071417161d5d7ab91c32073693590ef161017dbe.tar.gz
zig-071417161d5d7ab91c32073693590ef161017dbe.zip
stage2: add initial impl of LLVM backend in self-hosted compiler
Diffstat (limited to 'src')
-rw-r--r--src/Compilation.zig2
-rw-r--r--src/codegen/llvm.zig125
-rw-r--r--src/link/Elf.zig45
-rw-r--r--src/llvm_backend.zig410
-rw-r--r--src/llvm_bindings.zig358
5 files changed, 812 insertions, 128 deletions
diff --git a/src/Compilation.zig b/src/Compilation.zig
index 885ecbb95c..26beeccd9e 100644
--- a/src/Compilation.zig
+++ b/src/Compilation.zig
@@ -2106,7 +2106,7 @@ pub fn addCCArgs(
try argv.append("-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS");
}
- const llvm_triple = try @import("codegen/llvm.zig").targetTriple(arena, target);
+ const llvm_triple = try @import("llvm_backend.zig").targetTriple(arena, target);
try argv.appendSlice(&[_][]const u8{ "-target", llvm_triple });
switch (ext) {
diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig
deleted file mode 100644
index f87a7a940c..0000000000
--- a/src/codegen/llvm.zig
+++ /dev/null
@@ -1,125 +0,0 @@
-const std = @import("std");
-const Allocator = std.mem.Allocator;
-
-pub fn targetTriple(allocator: *Allocator, target: std.Target) ![]u8 {
- const llvm_arch = switch (target.cpu.arch) {
- .arm => "arm",
- .armeb => "armeb",
- .aarch64 => "aarch64",
- .aarch64_be => "aarch64_be",
- .aarch64_32 => "aarch64_32",
- .arc => "arc",
- .avr => "avr",
- .bpfel => "bpfel",
- .bpfeb => "bpfeb",
- .hexagon => "hexagon",
- .mips => "mips",
- .mipsel => "mipsel",
- .mips64 => "mips64",
- .mips64el => "mips64el",
- .msp430 => "msp430",
- .powerpc => "powerpc",
- .powerpc64 => "powerpc64",
- .powerpc64le => "powerpc64le",
- .r600 => "r600",
- .amdgcn => "amdgcn",
- .riscv32 => "riscv32",
- .riscv64 => "riscv64",
- .sparc => "sparc",
- .sparcv9 => "sparcv9",
- .sparcel => "sparcel",
- .s390x => "s390x",
- .tce => "tce",
- .tcele => "tcele",
- .thumb => "thumb",
- .thumbeb => "thumbeb",
- .i386 => "i386",
- .x86_64 => "x86_64",
- .xcore => "xcore",
- .nvptx => "nvptx",
- .nvptx64 => "nvptx64",
- .le32 => "le32",
- .le64 => "le64",
- .amdil => "amdil",
- .amdil64 => "amdil64",
- .hsail => "hsail",
- .hsail64 => "hsail64",
- .spir => "spir",
- .spir64 => "spir64",
- .kalimba => "kalimba",
- .shave => "shave",
- .lanai => "lanai",
- .wasm32 => "wasm32",
- .wasm64 => "wasm64",
- .renderscript32 => "renderscript32",
- .renderscript64 => "renderscript64",
- .ve => "ve",
- .spu_2 => return error.LLVMBackendDoesNotSupportSPUMarkII,
- };
- // TODO Add a sub-arch for some architectures depending on CPU features.
-
- const llvm_os = switch (target.os.tag) {
- .freestanding => "unknown",
- .ananas => "ananas",
- .cloudabi => "cloudabi",
- .dragonfly => "dragonfly",
- .freebsd => "freebsd",
- .fuchsia => "fuchsia",
- .ios => "ios",
- .kfreebsd => "kfreebsd",
- .linux => "linux",
- .lv2 => "lv2",
- .macos => "macosx",
- .netbsd => "netbsd",
- .openbsd => "openbsd",
- .solaris => "solaris",
- .windows => "windows",
- .haiku => "haiku",
- .minix => "minix",
- .rtems => "rtems",
- .nacl => "nacl",
- .cnk => "cnk",
- .aix => "aix",
- .cuda => "cuda",
- .nvcl => "nvcl",
- .amdhsa => "amdhsa",
- .ps4 => "ps4",
- .elfiamcu => "elfiamcu",
- .tvos => "tvos",
- .watchos => "watchos",
- .mesa3d => "mesa3d",
- .contiki => "contiki",
- .amdpal => "amdpal",
- .hermit => "hermit",
- .hurd => "hurd",
- .wasi => "wasi",
- .emscripten => "emscripten",
- .uefi => "windows",
- .other => "unknown",
- };
-
- const llvm_abi = switch (target.abi) {
- .none => "unknown",
- .gnu => "gnu",
- .gnuabin32 => "gnuabin32",
- .gnuabi64 => "gnuabi64",
- .gnueabi => "gnueabi",
- .gnueabihf => "gnueabihf",
- .gnux32 => "gnux32",
- .code16 => "code16",
- .eabi => "eabi",
- .eabihf => "eabihf",
- .android => "android",
- .musl => "musl",
- .musleabi => "musleabi",
- .musleabihf => "musleabihf",
- .msvc => "msvc",
- .itanium => "itanium",
- .cygnus => "cygnus",
- .coreclr => "coreclr",
- .simulator => "simulator",
- .macabi => "macabi",
- };
-
- return std.fmt.allocPrint(allocator, "{}-unknown-{}-{}", .{ llvm_arch, llvm_os, llvm_abi });
-}
diff --git a/src/link/Elf.zig b/src/link/Elf.zig
index 6232a64e4a..479dc415e5 100644
--- a/src/link/Elf.zig
+++ b/src/link/Elf.zig
@@ -24,6 +24,7 @@ const build_options = @import("build_options");
const target_util = @import("../target.zig");
const glibc = @import("../glibc.zig");
const Cache = @import("../Cache.zig");
+const llvm_backend = @import("../llvm_backend.zig");
const default_entry_addr = 0x8000000;
@@ -33,6 +34,9 @@ base: File,
ptr_width: PtrWidth,
+/// If this is not null, an object file is created by LLVM and linked with LLD afterwards.
+llvm_ir_module: ?*llvm_backend.LLVMIRModule = null,
+
/// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
/// Same order as in the file.
sections: std.ArrayListUnmanaged(elf.Elf64_Shdr) = std.ArrayListUnmanaged(elf.Elf64_Shdr){},
@@ -224,7 +228,13 @@ pub const SrcFn = struct {
pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*Elf {
assert(options.object_format == .elf);
- if (options.use_llvm) return error.LLVMBackendUnimplementedForELF; // TODO
+ if (options.use_llvm) {
+ const self = try createEmpty(allocator, options);
+ errdefer self.base.destroy();
+
+ self.llvm_ir_module = try llvm_backend.LLVMIRModule.create(allocator, sub_path, options);
+ return self;
+ }
const file = try options.emit.?.directory.handle.createFile(sub_path, .{
.truncate = false,
@@ -288,6 +298,7 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Elf {
}
pub fn deinit(self: *Elf) void {
+ if (self.llvm_ir_module) |ir_module| ir_module.deinit(self.base.allocator);
self.sections.deinit(self.base.allocator);
self.program_headers.deinit(self.base.allocator);
self.shstrtab.deinit(self.base.allocator);
@@ -423,6 +434,8 @@ fn updateString(self: *Elf, old_str_off: u32, new_name: []const u8) !u32 {
}
pub fn populateMissingMetadata(self: *Elf) !void {
+ if (self.llvm_ir_module) |_| return;
+
const small_ptr = switch (self.ptr_width) {
.p32 => true,
.p64 => false,
@@ -727,6 +740,11 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void {
const tracy = trace(@src());
defer tracy.end();
+ if (self.llvm_ir_module) |llvm_ir_module| {
+ try llvm_ir_module.flushModule(comp);
+ return;
+ }
+
// TODO This linker code currently assumes there is only 1 compilation unit and it corresponds to the
// Zig source code.
const module = self.base.options.module orelse return error.LinkingWithoutZigSourceUnimplemented;
@@ -1261,6 +1279,9 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
const stack_size = self.base.options.stack_size_override orelse 16777216;
const allow_shlib_undefined = self.base.options.allow_shlib_undefined orelse !self.base.options.is_native_os;
const compiler_rt_path: ?[]const u8 = if (self.base.options.include_compiler_rt) blk: {
+ // TODO: remove when stage2 can build compiler_rt.zig
+ if (!build_options.is_stage1) break :blk null;
+
if (is_exe_or_dyn_lib) {
break :blk comp.compiler_rt_static_lib.?.full_object_path;
} else {
@@ -1552,7 +1573,12 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
}
// libc
- if (is_exe_or_dyn_lib and !self.base.options.skip_linker_dependencies and !self.base.options.link_libc) {
+ // TODO: enable when stage2 can build c.zig
+ if (is_exe_or_dyn_lib and
+ !self.base.options.skip_linker_dependencies and
+ !self.base.options.link_libc and
+ build_options.is_stage1)
+ {
try argv.append(comp.libc_static_lib.?.full_object_path);
}
@@ -2046,6 +2072,8 @@ fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, al
}
pub fn allocateDeclIndexes(self: *Elf, decl: *Module.Decl) !void {
+ if (self.llvm_ir_module) |_| return;
+
if (decl.link.elf.local_sym_index != 0) return;
try self.local_symbols.ensureCapacity(self.base.allocator, self.local_symbols.items.len + 1);
@@ -2082,6 +2110,8 @@ pub fn allocateDeclIndexes(self: *Elf, decl: *Module.Decl) !void {
}
pub fn freeDecl(self: *Elf, decl: *Module.Decl) void {
+ if (self.llvm_ir_module) |_| return;
+
// Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
self.freeTextBlock(&decl.link.elf);
if (decl.link.elf.local_sym_index != 0) {
@@ -2119,6 +2149,11 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void {
const tracy = trace(@src());
defer tracy.end();
+ if (self.llvm_ir_module) |llvm_ir_module| {
+ try llvm_ir_module.updateDecl(module, decl);
+ return;
+ }
+
var code_buffer = std.ArrayList(u8).init(self.base.allocator);
defer code_buffer.deinit();
@@ -2594,6 +2629,8 @@ pub fn updateDeclExports(
decl: *const Module.Decl,
exports: []const *Module.Export,
) !void {
+ if (self.llvm_ir_module) |_| return;
+
const tracy = trace(@src());
defer tracy.end();
@@ -2667,6 +2704,8 @@ pub fn updateDeclLineNumber(self: *Elf, module: *Module, decl: *const Module.Dec
const tracy = trace(@src());
defer tracy.end();
+ if (self.llvm_ir_module) |_| return;
+
const container_scope = decl.scope.cast(Module.Scope.Container).?;
const tree = container_scope.file_scope.contents.tree;
const file_ast_decls = tree.root_node.decls();
@@ -2685,6 +2724,8 @@ pub fn updateDeclLineNumber(self: *Elf, module: *Module, decl: *const Module.Dec
}
pub fn deleteExport(self: *Elf, exp: Export) void {
+ if (self.llvm_ir_module) |_| return;
+
const sym_index = exp.sym_index orelse return;
self.global_symbol_free_list.append(self.base.allocator, sym_index) catch {};
self.global_symbols.items[sym_index].st_info = 0;
diff --git a/src/llvm_backend.zig b/src/llvm_backend.zig
new file mode 100644
index 0000000000..0745a1fe8b
--- /dev/null
+++ b/src/llvm_backend.zig
@@ -0,0 +1,410 @@
+const std = @import("std");
+const Allocator = std.mem.Allocator;
+const Compilation = @import("Compilation.zig");
+const llvm = @import("llvm_bindings.zig");
+const link = @import("link.zig");
+
+const Module = @import("Module.zig");
+const TypedValue = @import("TypedValue.zig");
+const ir = @import("ir.zig");
+const Inst = ir.Inst;
+
+const Value = @import("value.zig").Value;
+const Type = @import("type.zig").Type;
+
+pub fn targetTriple(allocator: *Allocator, target: std.Target) ![:0]u8 {
+ const llvm_arch = switch (target.cpu.arch) {
+ .arm => "arm",
+ .armeb => "armeb",
+ .aarch64 => "aarch64",
+ .aarch64_be => "aarch64_be",
+ .aarch64_32 => "aarch64_32",
+ .arc => "arc",
+ .avr => "avr",
+ .bpfel => "bpfel",
+ .bpfeb => "bpfeb",
+ .hexagon => "hexagon",
+ .mips => "mips",
+ .mipsel => "mipsel",
+ .mips64 => "mips64",
+ .mips64el => "mips64el",
+ .msp430 => "msp430",
+ .powerpc => "powerpc",
+ .powerpc64 => "powerpc64",
+ .powerpc64le => "powerpc64le",
+ .r600 => "r600",
+ .amdgcn => "amdgcn",
+ .riscv32 => "riscv32",
+ .riscv64 => "riscv64",
+ .sparc => "sparc",
+ .sparcv9 => "sparcv9",
+ .sparcel => "sparcel",
+ .s390x => "s390x",
+ .tce => "tce",
+ .tcele => "tcele",
+ .thumb => "thumb",
+ .thumbeb => "thumbeb",
+ .i386 => "i386",
+ .x86_64 => "x86_64",
+ .xcore => "xcore",
+ .nvptx => "nvptx",
+ .nvptx64 => "nvptx64",
+ .le32 => "le32",
+ .le64 => "le64",
+ .amdil => "amdil",
+ .amdil64 => "amdil64",
+ .hsail => "hsail",
+ .hsail64 => "hsail64",
+ .spir => "spir",
+ .spir64 => "spir64",
+ .kalimba => "kalimba",
+ .shave => "shave",
+ .lanai => "lanai",
+ .wasm32 => "wasm32",
+ .wasm64 => "wasm64",
+ .renderscript32 => "renderscript32",
+ .renderscript64 => "renderscript64",
+ .ve => "ve",
+ .spu_2 => return error.LLVMBackendDoesNotSupportSPUMarkII,
+ };
+ // TODO Add a sub-arch for some architectures depending on CPU features.
+
+ const llvm_os = switch (target.os.tag) {
+ .freestanding => "unknown",
+ .ananas => "ananas",
+ .cloudabi => "cloudabi",
+ .dragonfly => "dragonfly",
+ .freebsd => "freebsd",
+ .fuchsia => "fuchsia",
+ .ios => "ios",
+ .kfreebsd => "kfreebsd",
+ .linux => "linux",
+ .lv2 => "lv2",
+ .macos => "macosx",
+ .netbsd => "netbsd",
+ .openbsd => "openbsd",
+ .solaris => "solaris",
+ .windows => "windows",
+ .haiku => "haiku",
+ .minix => "minix",
+ .rtems => "rtems",
+ .nacl => "nacl",
+ .cnk => "cnk",
+ .aix => "aix",
+ .cuda => "cuda",
+ .nvcl => "nvcl",
+ .amdhsa => "amdhsa",
+ .ps4 => "ps4",
+ .elfiamcu => "elfiamcu",
+ .tvos => "tvos",
+ .watchos => "watchos",
+ .mesa3d => "mesa3d",
+ .contiki => "contiki",
+ .amdpal => "amdpal",
+ .hermit => "hermit",
+ .hurd => "hurd",
+ .wasi => "wasi",
+ .emscripten => "emscripten",
+ .uefi => "windows",
+ .other => "unknown",
+ };
+
+ const llvm_abi = switch (target.abi) {
+ .none => "unknown",
+ .gnu => "gnu",
+ .gnuabin32 => "gnuabin32",
+ .gnuabi64 => "gnuabi64",
+ .gnueabi => "gnueabi",
+ .gnueabihf => "gnueabihf",
+ .gnux32 => "gnux32",
+ .code16 => "code16",
+ .eabi => "eabi",
+ .eabihf => "eabihf",
+ .android => "android",
+ .musl => "musl",
+ .musleabi => "musleabi",
+ .musleabihf => "musleabihf",
+ .msvc => "msvc",
+ .itanium => "itanium",
+ .cygnus => "cygnus",
+ .coreclr => "coreclr",
+ .simulator => "simulator",
+ .macabi => "macabi",
+ };
+
+ return std.fmt.allocPrintZ(allocator, "{}-unknown-{}-{}", .{ llvm_arch, llvm_os, llvm_abi });
+}
+
+pub const LLVMIRModule = struct {
+ llvm_module: *const llvm.ModuleRef,
+ target_machine: *const llvm.TargetMachineRef,
+ output_path: []const u8,
+
+ gpa: *Allocator,
+ err_msg: ?*Compilation.ErrorMsg = null,
+
+ pub fn create(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*LLVMIRModule {
+ const self = try allocator.create(LLVMIRModule);
+ errdefer allocator.destroy(self);
+
+ const gpa = options.module.?.gpa;
+
+ initializeLLVMTargets();
+
+ const root_nameZ = try gpa.dupeZ(u8, options.root_name);
+ defer gpa.free(root_nameZ);
+ const llvm_module = llvm.ModuleRef.createWithName(root_nameZ.ptr);
+ errdefer llvm_module.disposeModule();
+
+ const llvm_target_triple = try targetTriple(gpa, options.target);
+ defer gpa.free(llvm_target_triple);
+
+ var error_message: [*:0]const u8 = undefined;
+ var target_ref: *const llvm.TargetRef = undefined;
+ if (llvm.TargetRef.getTargetFromTriple(llvm_target_triple.ptr, &target_ref, &error_message)) {
+ defer llvm.disposeMessage(error_message);
+
+ const stderr = std.io.getStdErr().outStream();
+ try stderr.print(
+ \\Zig is expecting LLVM to understand this target: '{s}'
+ \\However LLVM responded with: "{s}"
+ \\Zig is unable to continue. This is a bug in Zig:
+ \\https://github.com/ziglang/zig/issues/438
+ \\
+ ,
+ .{
+ llvm_target_triple,
+ error_message,
+ },
+ );
+ return error.InvalidLLVMTriple;
+ }
+
+ const opt_level: llvm.CodeGenOptLevel = if (options.optimize_mode == .Debug) .None else .Aggressive;
+ const target_machine = llvm.TargetMachineRef.createTargetMachine(
+ target_ref,
+ llvm_target_triple.ptr,
+ "",
+ "",
+ opt_level,
+ .Static,
+ .Default,
+ );
+ errdefer target_machine.disposeTargetMachine();
+
+ self.* = .{
+ .llvm_module = llvm_module,
+ .target_machine = target_machine,
+ .output_path = sub_path,
+ .gpa = gpa,
+ };
+ return self;
+ }
+
+ pub fn deinit(self: *LLVMIRModule, allocator: *Allocator) void {
+ self.llvm_module.disposeModule();
+ self.target_machine.disposeTargetMachine();
+ allocator.destroy(self);
+ }
+
+ fn initializeLLVMTargets() void {
+ llvm.initializeAllTargets();
+ llvm.initializeAllTargetInfos();
+ llvm.initializeAllTargetMCs();
+ llvm.initializeAllAsmPrinters();
+ llvm.initializeAllAsmParsers();
+ }
+
+ pub fn flushModule(self: *LLVMIRModule, comp: *Compilation) !void {
+ {
+ var error_message: [*:0]const u8 = undefined;
+ // verifyModule always allocs the error_message even if there is no error
+ defer llvm.disposeMessage(error_message);
+
+ if (self.llvm_module.verifyModule(.ReturnStatus, &error_message)) {
+ const stderr = std.io.getStdErr().outStream();
+ try stderr.print("broken LLVM module found: {s}\nThis is a bug in the Zig compiler.", .{error_message});
+ return error.BrokenLLVMModule;
+ }
+ }
+
+ if (comp.verbose_llvm_ir) {
+ const dump = self.llvm_module.printToString();
+ defer llvm.disposeMessage(dump);
+
+ const stderr = std.io.getStdErr().outStream();
+ try stderr.writeAll(std.mem.spanZ(dump));
+ }
+
+ const output_pathZ = try self.gpa.dupeZ(u8, self.output_path);
+ defer self.gpa.free(output_pathZ);
+
+ var error_message: [*:0]const u8 = undefined;
+ // TODO: where to put the output object, zig-cache something?
+ // TODO: caching?
+ if (self.target_machine.emitToFile(
+ self.llvm_module,
+ output_pathZ.ptr,
+ .ObjectFile,
+ &error_message,
+ )) {
+ defer llvm.disposeMessage(error_message);
+
+ const stderr = std.io.getStdErr().outStream();
+ try stderr.print("LLVM failed to emit file: {s}\n", .{error_message});
+ return error.FailedToEmit;
+ }
+ }
+
+ pub fn updateDecl(self: *LLVMIRModule, module: *Module, decl: *Module.Decl) !void {
+ const typed_value = decl.typed_value.most_recent.typed_value;
+ self.generate(module, typed_value, decl.src()) catch |err| switch (err) {
+ error.CodegenFail => {
+ decl.analysis = .codegen_failure;
+ try module.failed_decls.put(module.gpa, decl, self.err_msg.?);
+ return;
+ },
+ else => |e| return e,
+ };
+ }
+
+ fn generate(self: *LLVMIRModule, module: *Module, typed_value: TypedValue, src: usize) !void {
+ switch (typed_value.ty.zigTypeTag()) {
+ .Fn => {
+ const func = typed_value.val.cast(Value.Payload.Function).?.func;
+
+ var codegen = CodeGen{
+ .module = module,
+ .llvm_module = self.llvm_module,
+ .builder = llvm.BuilderRef.createBuilder(),
+ };
+ defer codegen.builder.disposeBuilder();
+
+ const llvm_func = try codegen.resolveLLVMFunction(func);
+
+ // We remove all the basic blocks of a function to support incremental
+ // compilation!
+ // TODO: remove all basic blocks if functions can have more than one
+ if (llvm_func.getFirstBasicBlock()) |bb| {
+ bb.deleteBasicBlock();
+ }
+
+ const entry_block = llvm_func.appendBasicBlock("Entry");
+ codegen.builder.positionBuilderAtEnd(entry_block);
+
+ const instructions = func.analysis.success.instructions;
+ for (instructions) |inst| {
+ switch (inst.tag) {
+ .breakpoint => try codegen.generateBreakpoint(inst.castTag(.breakpoint).?),
+ .call => try codegen.generateCall(inst.castTag(.call).?),
+ .unreach => codegen.generateUnreach(inst.castTag(.unreach).?),
+ .retvoid => codegen.generateRetVoid(inst.castTag(.retvoid).?),
+ .dbg_stmt => {
+ // TODO: implement debug info
+ },
+ else => |tag| return self.fail(src, "TODO implement LLVM codegen for Zir instruction: {}", .{tag}),
+ }
+ }
+ },
+ else => |ty| return self.fail(src, "TODO implement LLVM codegen for top-level decl type: {}", .{ty}),
+ }
+ }
+
+ pub fn fail(self: *LLVMIRModule, src: usize, comptime format: []const u8, args: anytype) error{ OutOfMemory, CodegenFail } {
+ @setCold(true);
+ std.debug.assert(self.err_msg == null);
+ self.err_msg = try Compilation.ErrorMsg.create(self.gpa, src, format, args);
+ return error.CodegenFail;
+ }
+};
+
+const CodeGen = struct {
+ module: *Module,
+ llvm_module: *const llvm.ModuleRef,
+ builder: *const llvm.BuilderRef,
+
+ fn generateCall(codegen: *CodeGen, inst: *Inst.Call) !void {
+ if (inst.func.cast(Inst.Constant)) |func_inst| {
+ if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
+ const func = func_val.func;
+ const zig_fn_type = func.owner_decl.typed_value.most_recent.typed_value.ty;
+ const llvm_fn = try codegen.resolveLLVMFunction(func);
+
+ // TODO: handle more arguments, inst.args
+
+ // TODO: LLVMBuildCall2 handles opaque function pointers, according to llvm docs
+ // Do we need that?
+ const call = codegen.builder.buildCall(llvm_fn, null, 0, "");
+
+ if (zig_fn_type.fnReturnType().zigTypeTag() == .NoReturn) {
+ _ = codegen.builder.buildUnreachable();
+ }
+ }
+ }
+ }
+
+ fn generateRetVoid(codegen: *CodeGen, inst: *Inst.NoOp) void {
+ _ = codegen.builder.buildRetVoid();
+ }
+
+ fn generateUnreach(codegen: *CodeGen, inst: *Inst.NoOp) void {
+ _ = codegen.builder.buildUnreachable();
+ }
+
+ fn generateBreakpoint(codegen: *CodeGen, 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);
+ const func = codegen.llvm_module.addFunction("llvm.debugtrap", fn_type);
+ // TODO: add assertion: LLVMGetIntrinsicID
+ _ = codegen.builder.buildCall(func, null, 0, "");
+ }
+
+ /// If the llvm function does not exist, create it
+ fn resolveLLVMFunction(codegen: *CodeGen, func: *Module.Fn) !*const llvm.ValueRef {
+ // TODO: do we want to store this in our own datastructure?
+ if (codegen.llvm_module.getNamedFunction(func.owner_decl.name)) |llvm_fn| return llvm_fn;
+
+ const zig_fn_type = func.owner_decl.typed_value.most_recent.typed_value.ty;
+ const return_type = zig_fn_type.fnReturnType();
+
+ const fn_param_len = zig_fn_type.fnParamLen();
+
+ const fn_param_types = try codegen.module.gpa.alloc(Type, fn_param_len);
+ defer codegen.module.gpa.free(fn_param_types);
+ zig_fn_type.fnParamTypes(fn_param_types);
+
+ const llvm_param = try codegen.module.gpa.alloc(*const llvm.TypeRef, fn_param_len);
+ defer codegen.module.gpa.free(llvm_param);
+
+ for (fn_param_types) |fn_param, i| {
+ llvm_param[i] = codegen.getLLVMType(fn_param);
+ }
+
+ const fn_type = llvm.TypeRef.functionType(
+ codegen.getLLVMType(return_type),
+ if (fn_param_len == 0) null else llvm_param.ptr,
+ @intCast(c_uint, fn_param_len),
+ false,
+ );
+ const llvm_fn = codegen.llvm_module.addFunction(func.owner_decl.name, fn_type);
+
+ if (return_type.zigTypeTag() == .NoReturn) {
+ llvm_fn.addFnAttr("noreturn");
+ }
+
+ return llvm_fn;
+ }
+
+ fn getLLVMType(codegen: *CodeGen, t: Type) *const llvm.TypeRef {
+ switch (t.zigTypeTag()) {
+ .Void => return llvm.voidType(),
+ .NoReturn => return llvm.voidType(),
+ .Int => {
+ const info = t.intInfo(codegen.module.getTarget());
+ return llvm.intType(info.bits);
+ },
+ .Bool => return llvm.intType(1),
+ else => unreachable,
+ }
+ }
+};
diff --git a/src/llvm_bindings.zig b/src/llvm_bindings.zig
index 865e989c89..e36d71b312 100644
--- a/src/llvm_bindings.zig
+++ b/src/llvm_bindings.zig
@@ -1,6 +1,364 @@
//! We do this instead of @cImport because the self-hosted compiler is easier
//! to bootstrap if it does not depend on translate-c.
+const std = @import("std");
+const assert = std.debug.assert;
+
+const LLVMBool = bool;
+pub const LLVMAttributeIndex = c_uint;
+
+pub const ValueRef = opaque {
+ pub const addAttributeAtIndex = LLVMAddAttributeAtIndex;
+ extern fn LLVMAddAttributeAtIndex(*const ValueRef, Idx: LLVMAttributeIndex, A: *const AttributeRef) void;
+
+ pub const appendBasicBlock = LLVMAppendBasicBlock;
+ extern fn LLVMAppendBasicBlock(Fn: *const ValueRef, Name: [*:0]const u8) *const BasicBlockRef;
+
+ pub const getFirstBasicBlock = LLVMGetFirstBasicBlock;
+ extern fn LLVMGetFirstBasicBlock(Fn: *const ValueRef) ?*const BasicBlockRef;
+
+ // Helper functions
+ // TODO: Do we want to put these functions here? It allows for convienient function calls
+ // on ValueRef: llvm_fn.addFnAttr("noreturn")
+ fn addAttr(val: *const ValueRef, index: LLVMAttributeIndex, name: []const u8) void {
+ const kind_id = getEnumAttributeKindForName(name.ptr, name.len);
+ assert(kind_id != 0);
+ const llvm_attr = ContextRef.getGlobal().createEnumAttribute(kind_id, 0);
+ val.addAttributeAtIndex(index, llvm_attr);
+ }
+
+ pub fn addFnAttr(val: *const ValueRef, attr_name: []const u8) void {
+ // TODO: improve this API, `addAttr(-1, attr_name)`
+ val.addAttr(std.math.maxInt(LLVMAttributeIndex), attr_name);
+ }
+};
+
+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 ModuleRef = opaque {
+ pub const createWithName = LLVMModuleCreateWithName;
+ extern fn LLVMModuleCreateWithName(ModuleID: [*:0]const u8) *const ModuleRef;
+
+ pub const disposeModule = LLVMDisposeModule;
+ extern fn LLVMDisposeModule(*const ModuleRef) void;
+
+ pub const verifyModule = LLVMVerifyModule;
+ extern fn LLVMVerifyModule(*const ModuleRef, Action: VerifierFailureAction, OutMessage: *[*:0]const u8) LLVMBool;
+
+ pub const addFunction = LLVMAddFunction;
+ extern fn LLVMAddFunction(*const ModuleRef, Name: [*:0]const u8, FunctionTy: *const TypeRef) *const ValueRef;
+
+ pub const getNamedFunction = LLVMGetNamedFunction;
+ extern fn LLVMGetNamedFunction(*const ModuleRef, Name: [*:0]const u8) ?*const ValueRef;
+
+ pub const printToString = LLVMPrintModuleToString;
+ extern fn LLVMPrintModuleToString(*const ModuleRef) [*:0]const u8;
+};
+
+pub const disposeMessage = LLVMDisposeMessage;
+extern fn LLVMDisposeMessage(Message: [*:0]const u8) void;
+
+pub const VerifierFailureAction = extern enum {
+ AbortProcess,
+ PrintMessage,
+ ReturnStatus,
+};
+
+pub const voidType = LLVMVoidType;
+extern fn LLVMVoidType() *const TypeRef;
+
+pub const getEnumAttributeKindForName = LLVMGetEnumAttributeKindForName;
+extern fn LLVMGetEnumAttributeKindForName(Name: [*]const u8, SLen: usize) c_uint;
+
+pub const AttributeRef = opaque {};
+
+pub const ContextRef = opaque {
+ pub const createEnumAttribute = LLVMCreateEnumAttribute;
+ extern fn LLVMCreateEnumAttribute(*const ContextRef, KindID: c_uint, Val: u64) *const AttributeRef;
+
+ pub const getGlobal = LLVMGetGlobalContext;
+ extern fn LLVMGetGlobalContext() *const ContextRef;
+};
+
+pub const intType = LLVMIntType;
+extern fn LLVMIntType(NumBits: c_uint) *const TypeRef;
+
+pub const BuilderRef = opaque {
+ pub const createBuilder = LLVMCreateBuilder;
+ extern fn LLVMCreateBuilder() *const BuilderRef;
+
+ pub const disposeBuilder = LLVMDisposeBuilder;
+ extern fn LLVMDisposeBuilder(Builder: *const BuilderRef) void;
+
+ pub const positionBuilderAtEnd = LLVMPositionBuilderAtEnd;
+ extern fn LLVMPositionBuilderAtEnd(Builder: *const BuilderRef, Block: *const BasicBlockRef) void;
+
+ pub const getInsertBlock = LLVMGetInsertBlock;
+ extern fn LLVMGetInsertBlock(Builder: *const BuilderRef) *const BasicBlockRef;
+
+ pub const buildCall = LLVMBuildCall;
+ extern fn LLVMBuildCall(*const BuilderRef, Fn: *const ValueRef, Args: ?[*]*const ValueRef, NumArgs: c_uint, Name: [*:0]const u8) *const ValueRef;
+
+ pub const buildCall2 = LLVMBuildCall2;
+ extern fn LLVMBuildCall2(*const BuilderRef, *const TypeRef, Fn: *const ValueRef, Args: [*]*const ValueRef, NumArgs: c_uint, Name: [*:0]const u8) *const ValueRef;
+
+ pub const buildRetVoid = LLVMBuildRetVoid;
+ extern fn LLVMBuildRetVoid(*const BuilderRef) *const ValueRef;
+
+ pub const buildUnreachable = LLVMBuildUnreachable;
+ extern fn LLVMBuildUnreachable(*const BuilderRef) *const ValueRef;
+
+ pub const buildAlloca = LLVMBuildAlloca;
+ extern fn LLVMBuildAlloca(*const BuilderRef, Ty: *const TypeRef, Name: [*:0]const u8) *const ValueRef;
+};
+
+pub const BasicBlockRef = opaque {
+ pub const deleteBasicBlock = LLVMDeleteBasicBlock;
+ extern fn LLVMDeleteBasicBlock(BB: *const BasicBlockRef) void;
+};
+
+pub const TargetMachineRef = opaque {
+ pub const createTargetMachine = LLVMCreateTargetMachine;
+ extern fn LLVMCreateTargetMachine(
+ T: *const TargetRef,
+ Triple: [*:0]const u8,
+ CPU: [*:0]const u8,
+ Features: [*:0]const u8,
+ Level: CodeGenOptLevel,
+ Reloc: RelocMode,
+ CodeModel: CodeMode,
+ ) *const TargetMachineRef;
+
+ pub const disposeTargetMachine = LLVMDisposeTargetMachine;
+ extern fn LLVMDisposeTargetMachine(T: *const TargetMachineRef) void;
+
+ pub const emitToFile = LLVMTargetMachineEmitToFile;
+ extern fn LLVMTargetMachineEmitToFile(*const TargetMachineRef, M: *const ModuleRef, Filename: [*:0]const u8, codegen: CodeGenFileType, ErrorMessage: *[*:0]const u8) LLVMBool;
+};
+
+pub const CodeMode = extern enum {
+ Default,
+ JITDefault,
+ Tiny,
+ Small,
+ Kernel,
+ Medium,
+ Large,
+};
+
+pub const CodeGenOptLevel = extern enum {
+ None,
+ Less,
+ Default,
+ Aggressive,
+};
+
+pub const RelocMode = extern enum {
+ Default,
+ Static,
+ PIC,
+ DynamicNoPic,
+ ROPI,
+ RWPI,
+ ROPI_RWPI,
+};
+
+pub const CodeGenFileType = extern enum {
+ AssemblyFile,
+ ObjectFile,
+};
+
+pub const TargetRef = opaque {
+ pub const getTargetFromTriple = LLVMGetTargetFromTriple;
+ extern fn LLVMGetTargetFromTriple(Triple: [*:0]const u8, T: **const TargetRef, ErrorMessage: *[*:0]const u8) LLVMBool;
+};
+
+extern fn LLVMInitializeAArch64TargetInfo() void;
+extern fn LLVMInitializeAMDGPUTargetInfo() void;
+extern fn LLVMInitializeARMTargetInfo() void;
+extern fn LLVMInitializeAVRTargetInfo() void;
+extern fn LLVMInitializeBPFTargetInfo() void;
+extern fn LLVMInitializeHexagonTargetInfo() void;
+extern fn LLVMInitializeLanaiTargetInfo() void;
+extern fn LLVMInitializeMipsTargetInfo() void;
+extern fn LLVMInitializeMSP430TargetInfo() void;
+extern fn LLVMInitializeNVPTXTargetInfo() void;
+extern fn LLVMInitializePowerPCTargetInfo() void;
+extern fn LLVMInitializeRISCVTargetInfo() void;
+extern fn LLVMInitializeSparcTargetInfo() void;
+extern fn LLVMInitializeSystemZTargetInfo() void;
+extern fn LLVMInitializeWebAssemblyTargetInfo() void;
+extern fn LLVMInitializeX86TargetInfo() void;
+extern fn LLVMInitializeXCoreTargetInfo() void;
+extern fn LLVMInitializeAArch64Target() void;
+extern fn LLVMInitializeAMDGPUTarget() void;
+extern fn LLVMInitializeARMTarget() void;
+extern fn LLVMInitializeAVRTarget() void;
+extern fn LLVMInitializeBPFTarget() void;
+extern fn LLVMInitializeHexagonTarget() void;
+extern fn LLVMInitializeLanaiTarget() void;
+extern fn LLVMInitializeMipsTarget() void;
+extern fn LLVMInitializeMSP430Target() void;
+extern fn LLVMInitializeNVPTXTarget() void;
+extern fn LLVMInitializePowerPCTarget() void;
+extern fn LLVMInitializeRISCVTarget() void;
+extern fn LLVMInitializeSparcTarget() void;
+extern fn LLVMInitializeSystemZTarget() void;
+extern fn LLVMInitializeWebAssemblyTarget() void;
+extern fn LLVMInitializeX86Target() void;
+extern fn LLVMInitializeXCoreTarget() void;
+extern fn LLVMInitializeAArch64TargetMC() void;
+extern fn LLVMInitializeAMDGPUTargetMC() void;
+extern fn LLVMInitializeARMTargetMC() void;
+extern fn LLVMInitializeAVRTargetMC() void;
+extern fn LLVMInitializeBPFTargetMC() void;
+extern fn LLVMInitializeHexagonTargetMC() void;
+extern fn LLVMInitializeLanaiTargetMC() void;
+extern fn LLVMInitializeMipsTargetMC() void;
+extern fn LLVMInitializeMSP430TargetMC() void;
+extern fn LLVMInitializeNVPTXTargetMC() void;
+extern fn LLVMInitializePowerPCTargetMC() void;
+extern fn LLVMInitializeRISCVTargetMC() void;
+extern fn LLVMInitializeSparcTargetMC() void;
+extern fn LLVMInitializeSystemZTargetMC() void;
+extern fn LLVMInitializeWebAssemblyTargetMC() void;
+extern fn LLVMInitializeX86TargetMC() void;
+extern fn LLVMInitializeXCoreTargetMC() void;
+extern fn LLVMInitializeAArch64AsmPrinter() void;
+extern fn LLVMInitializeAMDGPUAsmPrinter() void;
+extern fn LLVMInitializeARMAsmPrinter() void;
+extern fn LLVMInitializeAVRAsmPrinter() void;
+extern fn LLVMInitializeBPFAsmPrinter() void;
+extern fn LLVMInitializeHexagonAsmPrinter() void;
+extern fn LLVMInitializeLanaiAsmPrinter() void;
+extern fn LLVMInitializeMipsAsmPrinter() void;
+extern fn LLVMInitializeMSP430AsmPrinter() void;
+extern fn LLVMInitializeNVPTXAsmPrinter() void;
+extern fn LLVMInitializePowerPCAsmPrinter() void;
+extern fn LLVMInitializeRISCVAsmPrinter() void;
+extern fn LLVMInitializeSparcAsmPrinter() void;
+extern fn LLVMInitializeSystemZAsmPrinter() void;
+extern fn LLVMInitializeWebAssemblyAsmPrinter() void;
+extern fn LLVMInitializeX86AsmPrinter() void;
+extern fn LLVMInitializeXCoreAsmPrinter() void;
+extern fn LLVMInitializeAArch64AsmParser() void;
+extern fn LLVMInitializeAMDGPUAsmParser() void;
+extern fn LLVMInitializeARMAsmParser() void;
+extern fn LLVMInitializeAVRAsmParser() void;
+extern fn LLVMInitializeBPFAsmParser() void;
+extern fn LLVMInitializeHexagonAsmParser() void;
+extern fn LLVMInitializeLanaiAsmParser() void;
+extern fn LLVMInitializeMipsAsmParser() void;
+extern fn LLVMInitializeMSP430AsmParser() void;
+extern fn LLVMInitializePowerPCAsmParser() void;
+extern fn LLVMInitializeRISCVAsmParser() void;
+extern fn LLVMInitializeSparcAsmParser() void;
+extern fn LLVMInitializeSystemZAsmParser() void;
+extern fn LLVMInitializeWebAssemblyAsmParser() void;
+extern fn LLVMInitializeX86AsmParser() void;
+
+pub const initializeAllTargetInfos = LLVMInitializeAllTargetInfos;
+fn LLVMInitializeAllTargetInfos() callconv(.C) void {
+ LLVMInitializeAArch64TargetInfo();
+ LLVMInitializeAMDGPUTargetInfo();
+ LLVMInitializeARMTargetInfo();
+ LLVMInitializeAVRTargetInfo();
+ LLVMInitializeBPFTargetInfo();
+ LLVMInitializeHexagonTargetInfo();
+ LLVMInitializeLanaiTargetInfo();
+ LLVMInitializeMipsTargetInfo();
+ LLVMInitializeMSP430TargetInfo();
+ LLVMInitializeNVPTXTargetInfo();
+ LLVMInitializePowerPCTargetInfo();
+ LLVMInitializeRISCVTargetInfo();
+ LLVMInitializeSparcTargetInfo();
+ LLVMInitializeSystemZTargetInfo();
+ LLVMInitializeWebAssemblyTargetInfo();
+ LLVMInitializeX86TargetInfo();
+ LLVMInitializeXCoreTargetInfo();
+}
+pub const initializeAllTargets = LLVMInitializeAllTargets;
+fn LLVMInitializeAllTargets() callconv(.C) void {
+ LLVMInitializeAArch64Target();
+ LLVMInitializeAMDGPUTarget();
+ LLVMInitializeARMTarget();
+ LLVMInitializeAVRTarget();
+ LLVMInitializeBPFTarget();
+ LLVMInitializeHexagonTarget();
+ LLVMInitializeLanaiTarget();
+ LLVMInitializeMipsTarget();
+ LLVMInitializeMSP430Target();
+ LLVMInitializeNVPTXTarget();
+ LLVMInitializePowerPCTarget();
+ LLVMInitializeRISCVTarget();
+ LLVMInitializeSparcTarget();
+ LLVMInitializeSystemZTarget();
+ LLVMInitializeWebAssemblyTarget();
+ LLVMInitializeX86Target();
+ LLVMInitializeXCoreTarget();
+}
+pub const initializeAllTargetMCs = LLVMInitializeAllTargetMCs;
+fn LLVMInitializeAllTargetMCs() callconv(.C) void {
+ LLVMInitializeAArch64TargetMC();
+ LLVMInitializeAMDGPUTargetMC();
+ LLVMInitializeARMTargetMC();
+ LLVMInitializeAVRTargetMC();
+ LLVMInitializeBPFTargetMC();
+ LLVMInitializeHexagonTargetMC();
+ LLVMInitializeLanaiTargetMC();
+ LLVMInitializeMipsTargetMC();
+ LLVMInitializeMSP430TargetMC();
+ LLVMInitializeNVPTXTargetMC();
+ LLVMInitializePowerPCTargetMC();
+ LLVMInitializeRISCVTargetMC();
+ LLVMInitializeSparcTargetMC();
+ LLVMInitializeSystemZTargetMC();
+ LLVMInitializeWebAssemblyTargetMC();
+ LLVMInitializeX86TargetMC();
+ LLVMInitializeXCoreTargetMC();
+}
+pub const initializeAllAsmPrinters = LLVMInitializeAllAsmPrinters;
+fn LLVMInitializeAllAsmPrinters() callconv(.C) void {
+ LLVMInitializeAArch64AsmPrinter();
+ LLVMInitializeAMDGPUAsmPrinter();
+ LLVMInitializeARMAsmPrinter();
+ LLVMInitializeAVRAsmPrinter();
+ LLVMInitializeBPFAsmPrinter();
+ LLVMInitializeHexagonAsmPrinter();
+ LLVMInitializeLanaiAsmPrinter();
+ LLVMInitializeMipsAsmPrinter();
+ LLVMInitializeMSP430AsmPrinter();
+ LLVMInitializeNVPTXAsmPrinter();
+ LLVMInitializePowerPCAsmPrinter();
+ LLVMInitializeRISCVAsmPrinter();
+ LLVMInitializeSparcAsmPrinter();
+ LLVMInitializeSystemZAsmPrinter();
+ LLVMInitializeWebAssemblyAsmPrinter();
+ LLVMInitializeX86AsmPrinter();
+ LLVMInitializeXCoreAsmPrinter();
+}
+pub const initializeAllAsmParsers = LLVMInitializeAllAsmParsers;
+fn LLVMInitializeAllAsmParsers() callconv(.C) void {
+ LLVMInitializeAArch64AsmParser();
+ LLVMInitializeAMDGPUAsmParser();
+ LLVMInitializeARMAsmParser();
+ LLVMInitializeAVRAsmParser();
+ LLVMInitializeBPFAsmParser();
+ LLVMInitializeHexagonAsmParser();
+ LLVMInitializeLanaiAsmParser();
+ LLVMInitializeMipsAsmParser();
+ LLVMInitializeMSP430AsmParser();
+ LLVMInitializePowerPCAsmParser();
+ LLVMInitializeRISCVAsmParser();
+ LLVMInitializeSparcAsmParser();
+ LLVMInitializeSystemZAsmParser();
+ LLVMInitializeWebAssemblyAsmParser();
+ LLVMInitializeX86AsmParser();
+}
+
extern fn ZigLLDLinkCOFF(argc: c_int, argv: [*:null]const ?[*:0]const u8, can_exit_early: bool) c_int;
extern fn ZigLLDLinkELF(argc: c_int, argv: [*:null]const ?[*:0]const u8, can_exit_early: bool) c_int;
extern fn ZigLLDLinkMachO(argc: c_int, argv: [*:null]const ?[*:0]const u8, can_exit_early: bool) c_int;