From a5fb28070f37c2cad92ac8805bcc704e872fc538 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 22 Jul 2021 14:44:06 -0700 Subject: add -femit-llvm-bc CLI option and implement it * Added doc comments for `std.Target.ObjectFormat` enum * `std.Target.oFileExt` is removed because it is incorrect for Plan-9 targets. Instead, use `std.Target.ObjectFormat.fileExt` and pass a CPU architecture. * Added `Compilation.Directory.joinZ` for when a null byte is desired. * Improvements to `Compilation.create` logic for computing `use_llvm` and reporting errors in contradictory flags. `-femit-llvm-ir` and `-femit-llvm-bc` will now imply `-fLLVM`. * Fix compilation when passing `.bc` files on the command line. * Improvements to the stage2 LLVM backend: - cleaned up error messages and error reporting. Properly bubble up some errors rather than dumping to stderr; others turn into panics. - properly call ZigLLVMCreateTargetMachine and ZigLLVMTargetMachineEmitToFile and implement calculation of the respective parameters (cpu features, code model, abi name, lto, tsan, etc). - LLVM module verification only runs in debug builds of the compiler - use LLVMDumpModule rather than printToString because in the case that we incorrectly pass a null pointer to LLVM it may crash during dumping the module and having it partially printed is helpful in this case. - support -femit-asm, -fno-emit-bin, -femit-llvm-ir, -femit-llvm-bc - Support LLVM backend when used with Mach-O and WASM linkers. --- src/stage1/codegen.cpp | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) (limited to 'src/stage1/codegen.cpp') diff --git a/src/stage1/codegen.cpp b/src/stage1/codegen.cpp index 9ab0e269a2..582625c7ff 100644 --- a/src/stage1/codegen.cpp +++ b/src/stage1/codegen.cpp @@ -8506,19 +8506,22 @@ static void zig_llvm_emit_output(CodeGen *g) { const char *asm_filename = nullptr; const char *bin_filename = nullptr; const char *llvm_ir_filename = nullptr; + const char *bitcode_filename = nullptr; if (buf_len(&g->o_file_output_path) != 0) bin_filename = buf_ptr(&g->o_file_output_path); if (buf_len(&g->asm_file_output_path) != 0) asm_filename = buf_ptr(&g->asm_file_output_path); if (buf_len(&g->llvm_ir_file_output_path) != 0) llvm_ir_filename = buf_ptr(&g->llvm_ir_file_output_path); + if (buf_len(&g->bitcode_file_output_path) != 0) bitcode_filename = buf_ptr(&g->bitcode_file_output_path); - // Unfortunately, LLVM shits the bed when we ask for both binary and assembly. So we call the entire - // pipeline multiple times if this is requested. + // Unfortunately, LLVM shits the bed when we ask for both binary and assembly. + // So we call the entire pipeline multiple times if this is requested. if (asm_filename != nullptr && bin_filename != nullptr) { if (ZigLLVMTargetMachineEmitToFile(g->target_machine, g->module, &err_msg, g->build_mode == BuildModeDebug, is_small, g->enable_time_report, g->tsan_enabled, - g->have_lto, nullptr, bin_filename, llvm_ir_filename)) + g->have_lto, nullptr, bin_filename, llvm_ir_filename, nullptr)) { - fprintf(stderr, "LLVM failed to emit file: %s\n", err_msg); + fprintf(stderr, "LLVM failed to emit bin=%s, ir=%s: %s\n", + bin_filename, llvm_ir_filename, err_msg); exit(1); } bin_filename = nullptr; @@ -8527,9 +8530,11 @@ static void zig_llvm_emit_output(CodeGen *g) { if (ZigLLVMTargetMachineEmitToFile(g->target_machine, g->module, &err_msg, g->build_mode == BuildModeDebug, is_small, g->enable_time_report, g->tsan_enabled, - g->have_lto, asm_filename, bin_filename, llvm_ir_filename)) + g->have_lto, asm_filename, bin_filename, llvm_ir_filename, bitcode_filename)) { - fprintf(stderr, "LLVM failed to emit file: %s\n", err_msg); + fprintf(stderr, "LLVM failed to emit asm=%s, bin=%s, ir=%s, bc=%s: %s\n", + asm_filename, bin_filename, llvm_ir_filename, bitcode_filename, + err_msg); exit(1); } -- cgit v1.2.3 From 7c25390c957273ff43927608a45e257c4ed73549 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 22 Jul 2021 16:37:38 -0700 Subject: support -fcompiler-rt in conjunction with build-obj When using `build-exe` or `build-lib -dynamic`, `-fcompiler-rt` means building compiler-rt into a static library and then linking it into the executable. When using `build-lib`, `-fcompiler-rt` means building compiler-rt into an object file and then adding it into the static archive. Before this commit, when using `build-obj`, zig would build compiler-rt into an object file, and then on ELF, use `lld -r` to merge it into the main object file. Other linker backends of LLD do not support `-r` to merge objects, so this failed with error messages for those targets. Now, `-fcompiler-rt` when used with `build-obj` acts as if the user puts `_ = @import("compiler_rt");` inside their root source file. The symbols of compiler-rt go into the same compilation unit as the root source file. This is hooked up for stage1 only for now. Once stage2 is capable of building compiler-rt, it should be hooked up there as well. --- CMakeLists.txt | 1 + src/Compilation.zig | 32 +++++++++++++------------------- src/link/Elf.zig | 4 ++++ src/link/Wasm.zig | 4 +++- src/main.zig | 4 ++-- src/stage1.zig | 2 +- src/stage1/all_types.hpp | 1 + src/stage1/codegen.cpp | 16 ++++++++++++++++ src/stage1/stage1.cpp | 1 + src/stage1/stage1.h | 1 + src/stage1/zig0.cpp | 5 +++++ 11 files changed, 48 insertions(+), 23 deletions(-) (limited to 'src/stage1/codegen.cpp') diff --git a/CMakeLists.txt b/CMakeLists.txt index 0a8da2dd49..2714fa6d6b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -796,6 +796,7 @@ set(BUILD_ZIG1_ARGS --name zig1 --zig-lib-dir "${CMAKE_SOURCE_DIR}/lib" "-femit-bin=${ZIG1_OBJECT}" + -fcompiler-rt "${ZIG1_RELEASE_ARG}" "${ZIG1_SINGLE_THREADED_ARG}" -lc diff --git a/src/Compilation.zig b/src/Compilation.zig index fb35f5153f..8fb86916e6 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -826,6 +826,9 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { const ofmt = options.object_format orelse options.target.getObjectFormat(); const use_stage1 = options.use_stage1 orelse blk: { + // Even though we may have no Zig code to compile (depending on `options.root_pkg`), + // we may need to use stage1 for building compiler-rt and other dependencies. + if (build_options.omit_stage2) break :blk true; if (options.use_llvm) |use_llvm| { @@ -833,9 +836,6 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { break :blk false; } } - // If we have no zig code to compile, no need for stage1 backend. - if (options.root_pkg == null) - break :blk false; break :blk build_options.is_stage1; }; @@ -878,9 +878,6 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { if (options.emit_llvm_ir != null or options.emit_llvm_bc != null) { return error.EmittingLlvmModuleRequiresUsingLlvmBackend; } - if (use_stage1) { - return error.@"stage1 only supports LLVM backend"; - } } const tsan = options.want_tsan orelse false; @@ -1542,24 +1539,19 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { } // The `use_stage1` condition is here only because stage2 cannot yet build compiler-rt. - // Once it is capable this condition should be removed. + // Once it is capable this condition should be removed. When removing this condition, + // also test the use case of `build-obj -fcompiler-rt` with the self-hosted compiler + // and make sure the compiler-rt symbols are emitted. Currently this is hooked up for + // stage1 but not stage2. if (comp.bin_file.options.use_stage1) { if (comp.bin_file.options.include_compiler_rt) { if (is_exe_or_dyn_lib) { try comp.work_queue.writeItem(.{ .compiler_rt_lib = {} }); - } else { + } else if (options.output_mode != .Obj) { + // If build-obj with -fcompiler-rt is requested, that is handled specially + // elsewhere. In this case we are making a static library, so we ask + // for a compiler-rt object to put in it. try comp.work_queue.writeItem(.{ .compiler_rt_obj = {} }); - if (comp.bin_file.options.object_format != .elf and - comp.bin_file.options.output_mode == .Obj) - { - // For ELF we can rely on using -r to link multiple objects together into one, - // but to truly support `build-obj -fcompiler-rt` will require virtually - // injecting `_ = @import("compiler_rt.zig")` into the root source file of - // the compilation. - fatal("Embedding compiler-rt into {s} objects is not yet implemented.", .{ - @tagName(comp.bin_file.options.object_format), - }); - } } } if (needs_c_symbols) { @@ -4002,6 +3994,7 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node man.hash.add(target.os.getVersionRange()); man.hash.add(comp.bin_file.options.dll_export_fns); man.hash.add(comp.bin_file.options.function_sections); + man.hash.add(comp.bin_file.options.include_compiler_rt); man.hash.add(comp.bin_file.options.is_test); man.hash.add(comp.bin_file.options.emit != null); man.hash.add(mod.emit_h != null); @@ -4182,6 +4175,7 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node .valgrind_enabled = comp.bin_file.options.valgrind, .tsan_enabled = comp.bin_file.options.tsan, .function_sections = comp.bin_file.options.function_sections, + .include_compiler_rt = comp.bin_file.options.include_compiler_rt, .enable_stack_probing = comp.bin_file.options.stack_check, .red_zone = comp.bin_file.options.red_zone, .enable_time_report = comp.time_report, diff --git a/src/link/Elf.zig b/src/link/Elf.zig index b1eb219cf6..c95af23026 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1289,6 +1289,10 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { // TODO: remove when stage2 can build compiler_rt.zig if (!build_options.is_stage1) break :blk null; + // In the case of build-obj we include the compiler-rt symbols directly alongside + // the symbols of the root source file, in the same compilation unit. + if (is_obj) break :blk null; + if (is_exe_or_dyn_lib) { break :blk comp.compiler_rt_static_lib.?.full_object_path; } else { diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index f8803dfcb7..2ed6576033 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -645,7 +645,9 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void { break :blk full_obj_path; } else null; - const compiler_rt_path: ?[]const u8 = if (self.base.options.include_compiler_rt) + const is_obj = self.base.options.output_mode == .Obj; + + const compiler_rt_path: ?[]const u8 = if (self.base.options.include_compiler_rt and !is_obj) comp.compiler_rt_static_lib.?.full_object_path else null; diff --git a/src/main.zig b/src/main.zig index db7fc17df0..d3c7d024a1 100644 --- a/src/main.zig +++ b/src/main.zig @@ -385,8 +385,8 @@ const usage_build_generic = \\ --dynamic-linker [path] Set the dynamic interpreter path (usually ld.so) \\ --sysroot [path] Set the system root directory (usually /) \\ --version [ver] Dynamic library semver - \\ -fsoname[=name] (Linux) Override the default SONAME value - \\ -fno-soname (Linux) Disable emitting a SONAME + \\ -fsoname[=name] Override the default SONAME value + \\ -fno-soname Disable emitting a SONAME \\ -fLLD Force using LLD as the linker \\ -fno-LLD Prevent using LLD as the linker \\ -fcompiler-rt Always include compiler-rt symbols in output diff --git a/src/stage1.zig b/src/stage1.zig index 0108e9fc64..a00e036964 100644 --- a/src/stage1.zig +++ b/src/stage1.zig @@ -21,7 +21,6 @@ comptime { assert(build_options.is_stage1); assert(build_options.have_llvm); if (!builtin.is_test) { - _ = @import("compiler_rt"); @export(main, .{ .name = "main" }); } } @@ -126,6 +125,7 @@ pub const Module = extern struct { valgrind_enabled: bool, tsan_enabled: bool, function_sections: bool, + include_compiler_rt: bool, enable_stack_probing: bool, red_zone: bool, enable_time_report: bool, diff --git a/src/stage1/all_types.hpp b/src/stage1/all_types.hpp index ccdbf6b155..8dfc8de6f9 100644 --- a/src/stage1/all_types.hpp +++ b/src/stage1/all_types.hpp @@ -2150,6 +2150,7 @@ struct CodeGen { bool have_stack_probing; bool red_zone; bool function_sections; + bool include_compiler_rt; bool test_is_evented; bool valgrind_enabled; bool tsan_enabled; diff --git a/src/stage1/codegen.cpp b/src/stage1/codegen.cpp index 582625c7ff..67d787427f 100644 --- a/src/stage1/codegen.cpp +++ b/src/stage1/codegen.cpp @@ -9542,6 +9542,22 @@ static void gen_root_source(CodeGen *g) { g->panic_fn = panic_fn_val->data.x_ptr.data.fn.fn_entry; assert(g->panic_fn != nullptr); + if (g->include_compiler_rt) { + Buf *import_target_path; + Buf full_path = BUF_INIT; + ZigType *compiler_rt_import; + if ((err = analyze_import(g, std_import, buf_create_from_str("./special/compiler_rt.zig"), + &compiler_rt_import, &import_target_path, &full_path))) + { + if (err == ErrorFileNotFound) { + fprintf(stderr, "unable to find '%s'", buf_ptr(import_target_path)); + } else { + fprintf(stderr, "unable to open '%s': %s\n", buf_ptr(&full_path), err_str(err)); + } + exit(1); + } + } + if (!g->error_during_imports) { semantic_analyze(g); } diff --git a/src/stage1/stage1.cpp b/src/stage1/stage1.cpp index 38d236c560..2fbab192a7 100644 --- a/src/stage1/stage1.cpp +++ b/src/stage1/stage1.cpp @@ -101,6 +101,7 @@ void zig_stage1_build_object(struct ZigStage1 *stage1) { g->link_libc = stage1->link_libc; g->link_libcpp = stage1->link_libcpp; g->function_sections = stage1->function_sections; + g->include_compiler_rt = stage1->include_compiler_rt; g->subsystem = stage1->subsystem; diff --git a/src/stage1/stage1.h b/src/stage1/stage1.h index 44f3c625c0..1e8eef5937 100644 --- a/src/stage1/stage1.h +++ b/src/stage1/stage1.h @@ -196,6 +196,7 @@ struct ZigStage1 { bool valgrind_enabled; bool tsan_enabled; bool function_sections; + bool include_compiler_rt; bool enable_stack_probing; bool red_zone; bool enable_time_report; diff --git a/src/stage1/zig0.cpp b/src/stage1/zig0.cpp index f2412a74ef..d07d4f9e37 100644 --- a/src/stage1/zig0.cpp +++ b/src/stage1/zig0.cpp @@ -39,6 +39,7 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) { " --color [auto|off|on] enable or disable colored error messages\n" " --name [name] override output name\n" " -femit-bin=[path] Output machine code\n" + " -fcompiler-rt Always include compiler-rt symbols in output\n" " --pkg-begin [name] [path] make pkg available to import and push current pkg\n" " --pkg-end pop current pkg\n" " -ODebug build with optimizations off and safety on\n" @@ -266,6 +267,7 @@ int main(int argc, char **argv) { const char *mcpu = nullptr; bool single_threaded = false; bool is_test_build = false; + bool include_compiler_rt = false; for (int i = 1; i < argc; i += 1) { char *arg = argv[i]; @@ -334,6 +336,8 @@ int main(int argc, char **argv) { mcpu = arg + strlen("-mcpu="); } else if (str_starts_with(arg, "-femit-bin=")) { emit_bin_path = arg + strlen("-femit-bin="); + } else if (strcmp(arg, "-fcompiler-rt") == 0) { + include_compiler_rt = true; } else if (i + 1 >= argc) { fprintf(stderr, "Expected another argument after %s\n", arg); return print_error_usage(arg0); @@ -468,6 +472,7 @@ int main(int argc, char **argv) { stage1->subsystem = subsystem; stage1->pic = true; stage1->is_single_threaded = single_threaded; + stage1->include_compiler_rt = include_compiler_rt; zig_stage1_build_object(stage1); -- cgit v1.2.3