From c81ae52ee0b2e952f8ed9c5c6517af8182bb09c1 Mon Sep 17 00:00:00 2001 From: Timon Kruiper Date: Mon, 14 Dec 2020 20:30:24 +0100 Subject: [PATCH 1/8] stage2: fix compilation of self-hosted compiler with -Denable-llvm --- build.zig | 33 ++++++++++++++++++++------------- src/zig_clang.h | 20 +++++++++++++++++++- 2 files changed, 39 insertions(+), 14 deletions(-) diff --git a/build.zig b/build.zig index bef3b3b58f..65e0840d82 100644 --- a/build.zig +++ b/build.zig @@ -91,6 +91,20 @@ pub fn build(b: *Builder) !void { exe.addBuildOption(bool, "have_llvm", enable_llvm); if (enable_llvm) { const cmake_cfg = if (static_llvm) null else findAndParseConfigH(b, config_h_path_option); + + const exe_cflags = [_][]const u8{ + "-std=c++14", + "-D__STDC_CONSTANT_MACROS", + "-D__STDC_FORMAT_MACROS", + "-D__STDC_LIMIT_MACROS", + "-D_GNU_SOURCE", + "-fvisibility-inlines-hidden", + "-fno-exceptions", + "-fno-rtti", + "-Werror=type-limits", + "-Wno-missing-braces", + "-Wno-comment", + }; if (is_stage1) { exe.addIncludeDir("src"); exe.addIncludeDir("deps/SoftFloat-3e/source/include"); @@ -109,19 +123,6 @@ pub fn build(b: *Builder) !void { softfloat.addCSourceFiles(&softfloat_sources, &[_][]const u8{ "-std=c99", "-O3" }); exe.linkLibrary(softfloat); - const exe_cflags = [_][]const u8{ - "-std=c++14", - "-D__STDC_CONSTANT_MACROS", - "-D__STDC_FORMAT_MACROS", - "-D__STDC_LIMIT_MACROS", - "-D_GNU_SOURCE", - "-fvisibility-inlines-hidden", - "-fno-exceptions", - "-fno-rtti", - "-Werror=type-limits", - "-Wno-missing-braces", - "-Wno-comment", - }; exe.addCSourceFiles(&stage1_sources, &exe_cflags); exe.addCSourceFiles(&optimized_c_sources, &[_][]const u8{ "-std=c99", "-O3" }); if (cmake_cfg == null) { @@ -205,6 +206,12 @@ pub fn build(b: *Builder) !void { exe.linkSystemLibrary(lib_name); } + // We need this because otherwise zig_clang_cc1_main.cpp ends up pulling + // in a dependency on llvm::cfg::Update::dump() which is + // unavailable when LLVM is compiled in Release mode. + const zig_cpp_cflags = exe_cflags ++ [_][]const u8{"-DNDEBUG=1"}; + exe.addCSourceFiles(&zig_cpp_sources, &zig_cpp_cflags); + // This means we rely on clang-or-zig-built LLVM, Clang, LLD libraries. exe.linkSystemLibrary("c++"); diff --git a/src/zig_clang.h b/src/zig_clang.h index 32879062ae..169fbcedfb 100644 --- a/src/zig_clang.h +++ b/src/zig_clang.h @@ -8,14 +8,32 @@ #ifndef ZIG_ZIG_CLANG_H #define ZIG_ZIG_CLANG_H -#include "stage1/stage2.h" #include #include +#include + +#ifdef __cplusplus +#define ZIG_EXTERN_C extern "C" +#else +#define ZIG_EXTERN_C +#endif // ATTENTION: If you modify this file, be sure to update the corresponding // extern function declarations in the self-hosted compiler file // src/clang.zig. +// ABI warning +struct Stage2ErrorMsg { + const char *filename_ptr; // can be null + size_t filename_len; + const char *msg_ptr; + size_t msg_len; + const char *source; // valid until the ASTUnit is freed. can be null + unsigned line; // 0 based + unsigned column; // 0 based + unsigned offset; // byte offset into source +}; + struct ZigClangSourceLocation { unsigned ID; }; From 4a0d64300bd9ab1a8c35c634856396e3413200f1 Mon Sep 17 00:00:00 2001 From: Timon Kruiper Date: Mon, 14 Dec 2020 20:46:36 +0100 Subject: [PATCH 2/8] stage2: rename llvm.zig to llvm_bindings.zig Putting functions in this file will create functions like `llvm.function`, and the compiler thinks these are llvm intrinsics. --- CMakeLists.txt | 2 +- src/link.zig | 2 +- src/{llvm.zig => llvm_bindings.zig} | 0 src/main.zig | 6 +++--- src/mingw.zig | 2 +- src/target.zig | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) rename src/{llvm.zig => llvm_bindings.zig} (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index d51f753ac0..3e002ca5c1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -549,7 +549,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.zig" + "${CMAKE_SOURCE_DIR}/src/llvm_bindings.zig" "${CMAKE_SOURCE_DIR}/src/main.zig" "${CMAKE_SOURCE_DIR}/src/mingw.zig" "${CMAKE_SOURCE_DIR}/src/musl.zig" diff --git a/src/link.zig b/src/link.zig index 530bcd0b25..468f23bffd 100644 --- a/src/link.zig +++ b/src/link.zig @@ -567,7 +567,7 @@ pub const File = struct { std.debug.print("\n", .{}); } - const llvm = @import("llvm.zig"); + const llvm = @import("llvm_bindings.zig"); const os_type = @import("target.zig").osToLLVM(base.options.target.os.tag); const bad = llvm.WriteArchive(full_out_path_z, object_files.items.ptr, object_files.items.len, os_type); if (bad) return error.UnableToWriteArchive; diff --git a/src/llvm.zig b/src/llvm_bindings.zig similarity index 100% rename from src/llvm.zig rename to src/llvm_bindings.zig diff --git a/src/main.zig b/src/main.zig index c23cba98d8..15bb3be602 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1676,7 +1676,7 @@ fn buildOutputType( if (build_options.have_llvm and emit_asm != .no) { // LLVM has no way to set this non-globally. const argv = [_][*:0]const u8{ "zig (LLVM option parsing)", "--x86-asm-syntax=intel" }; - @import("llvm.zig").ParseCommandLineOptions(argv.len, &argv); + @import("llvm_bindings.zig").ParseCommandLineOptions(argv.len, &argv); } gimmeMoreOfThoseSweetSweetFileDescriptors(); @@ -2839,7 +2839,7 @@ pub fn punt_to_lld(arena: *Allocator, args: []const []const u8) error{OutOfMemor argv[i] = try arena.dupeZ(u8, arg); // TODO If there was an argsAllocZ we could avoid this allocation. } const exit_code = rc: { - const llvm = @import("llvm.zig"); + const llvm = @import("llvm_bindings.zig"); const argc = @intCast(c_int, argv.len); if (mem.eql(u8, args[1], "ld.lld")) { break :rc llvm.LinkELF(argc, argv.ptr, true); @@ -3224,7 +3224,7 @@ fn detectNativeTargetInfo(gpa: *Allocator, cross_target: std.zig.CrossTarget) !s if (!build_options.have_llvm) fatal("CPU features detection is not yet available for {} without LLVM extensions", .{@tagName(arch)}); - const llvm = @import("llvm.zig"); + const llvm = @import("llvm_bindings.zig"); const llvm_cpu_name = llvm.GetHostCPUName(); const llvm_cpu_features = llvm.GetNativeFeatures(); info.target.cpu = try detectNativeCpuWithLLVM(arch, llvm_cpu_name, llvm_cpu_features); diff --git a/src/mingw.zig b/src/mingw.zig index a9e275434f..246b0f33dc 100644 --- a/src/mingw.zig +++ b/src/mingw.zig @@ -405,7 +405,7 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void { }); errdefer comp.gpa.free(lib_final_path); - const llvm = @import("llvm.zig"); + const llvm = @import("llvm_bindings.zig"); const arch_type = @import("target.zig").archToLLVM(target.cpu.arch); const def_final_path_z = try arena.dupeZ(u8, def_final_path); const lib_final_path_z = try arena.dupeZ(u8, lib_final_path); diff --git a/src/target.zig b/src/target.zig index b3c83c76d8..cfe16f3fe6 100644 --- a/src/target.zig +++ b/src/target.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const llvm = @import("llvm.zig"); +const llvm = @import("llvm_bindings.zig"); pub const ArchOsAbi = struct { arch: std.Target.Cpu.Arch, From 071417161d5d7ab91c32073693590ef161017dbe Mon Sep 17 00:00:00 2001 From: Timon Kruiper Date: Sat, 19 Dec 2020 11:22:49 +0100 Subject: [PATCH 3/8] stage2: add initial impl of LLVM backend in self-hosted compiler --- CMakeLists.txt | 2 +- src/Compilation.zig | 2 +- src/codegen/llvm.zig | 125 ------------- src/link/Elf.zig | 45 ++++- src/llvm_backend.zig | 410 ++++++++++++++++++++++++++++++++++++++++++ src/llvm_bindings.zig | 358 ++++++++++++++++++++++++++++++++++++ 6 files changed, 813 insertions(+), 129 deletions(-) delete mode 100644 src/codegen/llvm.zig create mode 100644 src/llvm_backend.zig diff --git a/CMakeLists.txt b/CMakeLists.txt index 3e002ca5c1..a9dc38b40b 100644 --- a/CMakeLists.txt +++ b/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" 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; From b059bb84b85ca7914d617cae7c4960a798412ea5 Mon Sep 17 00:00:00 2001 From: Timon Kruiper Date: Sat, 19 Dec 2020 12:14:17 +0100 Subject: [PATCH 4/8] stage2: add LLVM codegen windows support to the self-hosted compiler The following example generates a valid `main.exe`: `zig build-exe main.zig -fLLVM -target x86_64-windows-gnu --subsystem console` ``` export fn wWinMainCRTStartup() noreturn { foo(); exit(); } fn foo() void {} fn exit() noreturn { unreachable; } ``` --- src/link/Coff.zig | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/src/link/Coff.zig b/src/link/Coff.zig index bea3033f88..161a4092f4 100644 --- a/src/link/Coff.zig +++ b/src/link/Coff.zig @@ -16,6 +16,7 @@ const link = @import("../link.zig"); const build_options = @import("build_options"); const Cache = @import("../Cache.zig"); const mingw = @import("../mingw.zig"); +const llvm_backend = @import("../llvm_backend.zig"); const allocation_padding = 4 / 3; const minimum_text_block_size = 64 * allocation_padding; @@ -32,6 +33,9 @@ pub const base_tag: link.File.Tag = .coff; const msdos_stub = @embedFile("msdos-stub.bin"); +/// If this is not null, an object file is created by LLVM and linked with LLD afterwards. +llvm_ir_module: ?*llvm_backend.LLVMIRModule = null, + base: link.File, ptr_width: PtrWidth, error_flags: link.File.ErrorFlags = .{}, @@ -121,8 +125,13 @@ pub const SrcFn = void; pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*Coff { assert(options.object_format == .coff); - if (options.use_llvm) return error.LLVM_BackendIsTODO_ForCoff; // TODO - if (options.use_lld) return error.LLD_LinkingIsTODO_ForCoff; // 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, @@ -648,6 +657,11 @@ pub fn updateDecl(self: *Coff, 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(); @@ -704,6 +718,8 @@ pub fn freeDecl(self: *Coff, decl: *Module.Decl) void { } pub fn updateDeclExports(self: *Coff, module: *Module, decl: *const Module.Decl, exports: []const *Module.Export) !void { + if (self.llvm_ir_module) |_| return; + for (exports) |exp| { if (exp.options.section) |section_name| { if (!mem.eql(u8, section_name, ".text")) { @@ -744,6 +760,11 @@ pub fn flushModule(self: *Coff, 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; + } + if (self.text_section_size_dirty) { // Write the new raw size in the .text header var buf: [4]u8 = undefined; @@ -1124,8 +1145,9 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void { try argv.append(comp.libunwind_static_lib.?.full_object_path); } + // TODO: remove when stage2 can build compiler_rt.zig, c.zig and ssp.zig // compiler-rt, libc and libssp - if (is_exe_or_dyn_lib and !self.base.options.skip_linker_dependencies) { + if (is_exe_or_dyn_lib and !self.base.options.skip_linker_dependencies and build_options.is_stage1) { if (!self.base.options.link_libc) { try argv.append(comp.libc_static_lib.?.full_object_path); } @@ -1235,6 +1257,7 @@ pub fn updateDeclLineNumber(self: *Coff, module: *Module, decl: *Module.Decl) !v } pub fn deinit(self: *Coff) void { + if (self.llvm_ir_module) |ir_module| ir_module.deinit(self.base.allocator); self.text_block_free_list.deinit(self.base.allocator); self.offset_table.deinit(self.base.allocator); self.offset_table_free_list.deinit(self.base.allocator); From 6b8d28312ccca56d652af32bad360e9a5394fb8e Mon Sep 17 00:00:00 2001 From: Timon Kruiper Date: Mon, 21 Dec 2020 10:21:30 +0100 Subject: [PATCH 5/8] stage2: fix building self-hosted without llvm-backend enabled. --- src/link/Coff.zig | 18 ++++++++---------- src/link/Elf.zig | 19 +++++++++---------- 2 files changed, 17 insertions(+), 20 deletions(-) diff --git a/src/link/Coff.zig b/src/link/Coff.zig index 161a4092f4..8c60086010 100644 --- a/src/link/Coff.zig +++ b/src/link/Coff.zig @@ -125,7 +125,7 @@ pub const SrcFn = void; pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*Coff { assert(options.object_format == .coff); - if (options.use_llvm) { + if (build_options.have_llvm and options.use_llvm) { const self = try createEmpty(allocator, options); errdefer self.base.destroy(); @@ -657,10 +657,8 @@ pub fn updateDecl(self: *Coff, 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; - } + if (build_options.have_llvm) + if (self.llvm_ir_module) |llvm_ir_module| return try llvm_ir_module.updateDecl(module, decl); var code_buffer = std.ArrayList(u8).init(self.base.allocator); defer code_buffer.deinit(); @@ -760,10 +758,8 @@ pub fn flushModule(self: *Coff, 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; - } + if (build_options.have_llvm) + if (self.llvm_ir_module) |llvm_ir_module| return try llvm_ir_module.flushModule(comp); if (self.text_section_size_dirty) { // Write the new raw size in the .text header @@ -1257,7 +1253,9 @@ pub fn updateDeclLineNumber(self: *Coff, module: *Module, decl: *Module.Decl) !v } pub fn deinit(self: *Coff) void { - if (self.llvm_ir_module) |ir_module| ir_module.deinit(self.base.allocator); + if (build_options.have_llvm) + if (self.llvm_ir_module) |ir_module| ir_module.deinit(self.base.allocator); + self.text_block_free_list.deinit(self.base.allocator); self.offset_table.deinit(self.base.allocator); self.offset_table_free_list.deinit(self.base.allocator); diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 479dc415e5..9472f8fca7 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -228,7 +228,7 @@ 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) { + if (build_options.have_llvm and options.use_llvm) { const self = try createEmpty(allocator, options); errdefer self.base.destroy(); @@ -298,7 +298,10 @@ 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); + if (build_options.have_llvm) + 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); @@ -740,10 +743,8 @@ 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; - } + if (build_options.have_llvm) + if (self.llvm_ir_module) |llvm_ir_module| return try llvm_ir_module.flushModule(comp); // TODO This linker code currently assumes there is only 1 compilation unit and it corresponds to the // Zig source code. @@ -2149,10 +2150,8 @@ 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; - } + if (build_options.have_llvm) + if (self.llvm_ir_module) |llvm_ir_module| return try llvm_ir_module.updateDecl(module, decl); var code_buffer = std.ArrayList(u8).init(self.base.allocator); defer code_buffer.deinit(); From 09cf043efd5966e328e4454bc30bc756f439938b Mon Sep 17 00:00:00 2001 From: Timon Kruiper Date: Thu, 24 Dec 2020 11:40:54 +0100 Subject: [PATCH 6/8] stage2: add missing asserts and early returns for LLVM backend --- src/link/Coff.zig | 5 +++++ src/link/Elf.zig | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/link/Coff.zig b/src/link/Coff.zig index 8c60086010..096fa2cd0b 100644 --- a/src/link/Coff.zig +++ b/src/link/Coff.zig @@ -413,6 +413,8 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Coff { } pub fn allocateDeclIndexes(self: *Coff, decl: *Module.Decl) !void { + if (self.llvm_ir_module) |_| return; + try self.offset_table.ensureCapacity(self.base.allocator, self.offset_table.items.len + 1); if (self.offset_table_free_list.popOrNull()) |i| { @@ -710,6 +712,8 @@ pub fn updateDecl(self: *Coff, module: *Module, decl: *Module.Decl) !void { } pub fn freeDecl(self: *Coff, 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.coff); self.offset_table_free_list.append(self.base.allocator, decl.link.coff.offset_table_index) catch {}; @@ -1245,6 +1249,7 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void { } pub fn getDeclVAddr(self: *Coff, decl: *const Module.Decl) u64 { + assert(self.llvm_ir_module == null); return self.text_section_virtual_address + decl.link.coff.text_offset; } diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 9472f8fca7..25b883f8c6 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -318,6 +318,7 @@ pub fn deinit(self: *Elf) void { } pub fn getDeclVAddr(self: *Elf, decl: *const Module.Decl) u64 { + assert(self.llvm_ir_module == null); assert(decl.link.elf.local_sym_index != 0); return self.local_symbols.items[decl.link.elf.local_sym_index].st_value; } @@ -437,7 +438,7 @@ 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; + assert(self.llvm_ir_module == null); const small_ptr = switch (self.ptr_width) { .p32 => true, From 4a32d4f288737a0d67484c1f05817ec468ec41f1 Mon Sep 17 00:00:00 2001 From: Timon Kruiper Date: Thu, 24 Dec 2020 16:47:09 +0100 Subject: [PATCH 7/8] stage2: refactor (simplify) code structure of `llvm_backend.zig` --- src/llvm_backend.zig | 114 +++++++++++++++++++++---------------------- 1 file changed, 55 insertions(+), 59 deletions(-) diff --git a/src/llvm_backend.zig b/src/llvm_backend.zig index 0745a1fe8b..5dce808eed 100644 --- a/src/llvm_backend.zig +++ b/src/llvm_backend.zig @@ -136,8 +136,11 @@ pub fn targetTriple(allocator: *Allocator, target: std.Target) ![:0]u8 { } pub const LLVMIRModule = struct { + module: *Module, llvm_module: *const llvm.ModuleRef, target_machine: *const llvm.TargetMachineRef, + builder: *const llvm.BuilderRef, + output_path: []const u8, gpa: *Allocator, @@ -192,9 +195,14 @@ pub const LLVMIRModule = struct { ); errdefer target_machine.disposeTargetMachine(); + const builder = llvm.BuilderRef.createBuilder(); + errdefer builder.disposeBuilder(); + self.* = .{ + .module = options.module.?, .llvm_module = llvm_module, .target_machine = target_machine, + .builder = builder, .output_path = sub_path, .gpa = gpa, }; @@ -202,8 +210,9 @@ pub const LLVMIRModule = struct { } pub fn deinit(self: *LLVMIRModule, allocator: *Allocator) void { - self.llvm_module.disposeModule(); + self.builder.disposeBuilder(); self.target_machine.disposeTargetMachine(); + self.llvm_module.disposeModule(); allocator.destroy(self); } @@ -216,6 +225,14 @@ pub const LLVMIRModule = struct { } pub fn flushModule(self: *LLVMIRModule, comp: *Compilation) !void { + 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)); + } + { var error_message: [*:0]const u8 = undefined; // verifyModule always allocs the error_message even if there is no error @@ -228,14 +245,6 @@ pub const LLVMIRModule = struct { } } - 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); @@ -258,7 +267,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.generate(module, typed_value, decl.src()) catch |err| switch (err) { + self.gen(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.?); @@ -268,19 +277,12 @@ pub const LLVMIRModule = struct { }; } - fn generate(self: *LLVMIRModule, module: *Module, typed_value: TypedValue, src: usize) !void { + fn gen(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); + const llvm_func = try self.resolveLLVMFunction(func); // We remove all the basic blocks of a function to support incremental // compilation! @@ -290,15 +292,15 @@ pub const LLVMIRModule = struct { } const entry_block = llvm_func.appendBasicBlock("Entry"); - codegen.builder.positionBuilderAtEnd(entry_block); + self.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).?), + .breakpoint => try self.genBreakpoint(inst.castTag(.breakpoint).?), + .call => try self.genCall(inst.castTag(.call).?), + .unreach => self.genUnreach(inst.castTag(.unreach).?), + .retvoid => self.genRetVoid(inst.castTag(.retvoid).?), .dbg_stmt => { // TODO: implement debug info }, @@ -310,83 +312,70 @@ pub const LLVMIRModule = struct { } } - 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 { + fn genCall(self: *LLVMIRModule, 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); + const llvm_fn = try self.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, ""); + const call = self.builder.buildCall(llvm_fn, null, 0, ""); if (zig_fn_type.fnReturnType().zigTypeTag() == .NoReturn) { - _ = codegen.builder.buildUnreachable(); + _ = self.builder.buildUnreachable(); } } } } - fn generateRetVoid(codegen: *CodeGen, inst: *Inst.NoOp) void { - _ = codegen.builder.buildRetVoid(); + fn genRetVoid(self: *LLVMIRModule, inst: *Inst.NoOp) void { + _ = self.builder.buildRetVoid(); } - fn generateUnreach(codegen: *CodeGen, inst: *Inst.NoOp) void { - _ = codegen.builder.buildUnreachable(); + fn genUnreach(self: *LLVMIRModule, inst: *Inst.NoOp) void { + _ = self.builder.buildUnreachable(); } - fn generateBreakpoint(codegen: *CodeGen, inst: *Inst.NoOp) !void { + fn genBreakpoint(self: *LLVMIRModule, inst: *Inst.NoOp) !void { // TODO: Store this function somewhere such that we dont have to add it again const fn_type = llvm.TypeRef.functionType(llvm.voidType(), null, 0, false); - const func = codegen.llvm_module.addFunction("llvm.debugtrap", fn_type); + const func = self.llvm_module.addFunction("llvm.debugtrap", fn_type); // TODO: add assertion: LLVMGetIntrinsicID - _ = codegen.builder.buildCall(func, null, 0, ""); + _ = self.builder.buildCall(func, null, 0, ""); } /// If the llvm function does not exist, create it - fn resolveLLVMFunction(codegen: *CodeGen, func: *Module.Fn) !*const llvm.ValueRef { + fn resolveLLVMFunction(self: *LLVMIRModule, 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; + if (self.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); + const fn_param_types = try self.gpa.alloc(Type, fn_param_len); + defer self.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); + const llvm_param = try self.gpa.alloc(*const llvm.TypeRef, fn_param_len); + defer self.gpa.free(llvm_param); for (fn_param_types) |fn_param, i| { - llvm_param[i] = codegen.getLLVMType(fn_param); + llvm_param[i] = self.getLLVMType(fn_param); } const fn_type = llvm.TypeRef.functionType( - codegen.getLLVMType(return_type), + self.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); + const llvm_fn = self.llvm_module.addFunction(func.owner_decl.name, fn_type); if (return_type.zigTypeTag() == .NoReturn) { llvm_fn.addFnAttr("noreturn"); @@ -395,16 +384,23 @@ const CodeGen = struct { return llvm_fn; } - fn getLLVMType(codegen: *CodeGen, t: Type) *const llvm.TypeRef { + fn getLLVMType(self: *LLVMIRModule, t: Type) *const llvm.TypeRef { switch (t.zigTypeTag()) { .Void => return llvm.voidType(), .NoReturn => return llvm.voidType(), .Int => { - const info = t.intInfo(codegen.module.getTarget()); + const info = t.intInfo(self.module.getTarget()); return llvm.intType(info.bits); }, .Bool => return llvm.intType(1), else => unreachable, } } + + 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; + } }; From ec3aedffb173845b3fe6f7559e06e1bc3cfde6f7 Mon Sep 17 00:00:00 2001 From: Timon Kruiper Date: Thu, 24 Dec 2020 16:52:14 +0100 Subject: [PATCH 8/8] stage2: add initial implementation of func arguments in LLVM backend The following works: ``` export fn _start() noreturn { assert(true); exit(); } fn assert(cond: bool) void {} fn exit() noreturn { unreachable; } ``` --- src/llvm_backend.zig | 40 ++++++++++++++++++++++++++++++++++++++-- src/llvm_bindings.zig | 9 +++++++++ 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/src/llvm_backend.zig b/src/llvm_backend.zig index 5dce808eed..77aa4d3bd5 100644 --- a/src/llvm_backend.zig +++ b/src/llvm_backend.zig @@ -301,6 +301,7 @@ pub const LLVMIRModule = struct { .call => try self.genCall(inst.castTag(.call).?), .unreach => self.genUnreach(inst.castTag(.unreach).?), .retvoid => self.genRetVoid(inst.castTag(.retvoid).?), + .arg => self.genArg(inst.castTag(.arg).?), .dbg_stmt => { // TODO: implement debug info }, @@ -319,11 +320,23 @@ pub const LLVMIRModule = struct { const zig_fn_type = func.owner_decl.typed_value.most_recent.typed_value.ty; const llvm_fn = try self.resolveLLVMFunction(func); - // TODO: handle more arguments, inst.args + const num_args = inst.args.len; + + const llvm_param_vals = try self.gpa.alloc(*const llvm.ValueRef, num_args); + defer self.gpa.free(llvm_param_vals); + + for (inst.args) |arg, i| { + llvm_param_vals[i] = try self.resolveInst(arg); + } // TODO: LLVMBuildCall2 handles opaque function pointers, according to llvm docs // Do we need that? - const call = self.builder.buildCall(llvm_fn, null, 0, ""); + const call = self.builder.buildCall( + llvm_fn, + if (num_args == 0) null else llvm_param_vals.ptr, + @intCast(c_uint, num_args), + "", + ); if (zig_fn_type.fnReturnType().zigTypeTag() == .NoReturn) { _ = self.builder.buildUnreachable(); @@ -340,6 +353,10 @@ pub const LLVMIRModule = struct { _ = self.builder.buildUnreachable(); } + fn genArg(self: *LLVMIRModule, inst: *Inst.Arg) void { + // TODO: implement this + } + fn genBreakpoint(self: *LLVMIRModule, inst: *Inst.NoOp) !void { // TODO: Store this function somewhere such that we dont have to add it again const fn_type = llvm.TypeRef.functionType(llvm.voidType(), null, 0, false); @@ -348,6 +365,25 @@ pub const LLVMIRModule = struct { _ = self.builder.buildCall(func, null, 0, ""); } + fn resolveInst(self: *LLVMIRModule, inst: *ir.Inst) !*const llvm.ValueRef { + if (inst.castTag(.constant)) |const_inst| { + return self.genTypedValue(inst.src, .{ .ty = inst.ty, .val = const_inst.val }); + } + return self.fail(inst.src, "TODO implement resolveInst", .{}); + } + + fn genTypedValue(self: *LLVMIRModule, src: usize, typed_value: TypedValue) !*const llvm.ValueRef { + const llvm_type = self.getLLVMType(typed_value.ty); + + if (typed_value.val.isUndef()) + return llvm_type.getUndef(); + + switch (typed_value.ty.zigTypeTag()) { + .Bool => return if (typed_value.val.toBool()) llvm_type.constAllOnes() else llvm_type.constNull(), + else => return self.fail(src, "TODO implement const of type '{}'", .{typed_value.ty}), + } + } + /// If the llvm function does not exist, create it fn resolveLLVMFunction(self: *LLVMIRModule, func: *Module.Fn) !*const llvm.ValueRef { // TODO: do we want to store this in our own datastructure? diff --git a/src/llvm_bindings.zig b/src/llvm_bindings.zig index e36d71b312..388acc4ba2 100644 --- a/src/llvm_bindings.zig +++ b/src/llvm_bindings.zig @@ -36,6 +36,15 @@ pub const ValueRef = opaque { pub const TypeRef = opaque { pub const functionType = LLVMFunctionType; extern fn LLVMFunctionType(ReturnType: *const TypeRef, ParamTypes: ?[*]*const TypeRef, ParamCount: c_uint, IsVarArg: LLVMBool) *const TypeRef; + + pub const constNull = LLVMConstNull; + extern fn LLVMConstNull(Ty: *const TypeRef) *const ValueRef; + + pub const constAllOnes = LLVMConstAllOnes; + extern fn LLVMConstAllOnes(Ty: *const TypeRef) *const ValueRef; + + pub const getUndef = LLVMGetUndef; + extern fn LLVMGetUndef(Ty: *const TypeRef) *const ValueRef; }; pub const ModuleRef = opaque {