Commit 071417161d

Timon Kruiper <timonkruiper@gmail.com>
2020-12-19 11:22:49
stage2: add initial impl of LLVM backend in self-hosted compiler
1 parent 4a0d643
src/codegen/llvm.zig
@@ -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 });
-}
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;
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) {
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,
+        }
+    }
+};
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;
CMakeLists.txt
@@ -527,7 +527,6 @@ set(ZIG_STAGE2_SOURCES
     "${CMAKE_SOURCE_DIR}/src/codegen/aarch64.zig"
     "${CMAKE_SOURCE_DIR}/src/codegen/arm.zig"
     "${CMAKE_SOURCE_DIR}/src/codegen/c.zig"
-    "${CMAKE_SOURCE_DIR}/src/codegen/llvm.zig"
     "${CMAKE_SOURCE_DIR}/src/codegen/riscv64.zig"
     "${CMAKE_SOURCE_DIR}/src/codegen/spu-mk2.zig"
     "${CMAKE_SOURCE_DIR}/src/codegen/wasm.zig"
@@ -549,6 +548,7 @@ set(ZIG_STAGE2_SOURCES
     "${CMAKE_SOURCE_DIR}/src/link/cbe.h"
     "${CMAKE_SOURCE_DIR}/src/link/msdos-stub.bin"
     "${CMAKE_SOURCE_DIR}/src/liveness.zig"
+    "${CMAKE_SOURCE_DIR}/src/llvm_backend.zig"
     "${CMAKE_SOURCE_DIR}/src/llvm_bindings.zig"
     "${CMAKE_SOURCE_DIR}/src/main.zig"
     "${CMAKE_SOURCE_DIR}/src/mingw.zig"