| author | |
| committer | |
| log | 7c25390c957273ff43927608a45e257c4ed73549 |
| tree | 411809c36b20d2575816bcf726330413c3624398 |
| parent | a5fb28070f37c2cad92ac8805bcc704e872fc538 |
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.11 files changed, 48 insertions(+), 23 deletions(-)
CMakeLists.txt+1| ... | @@ -796,6 +796,7 @@ set(BUILD_ZIG1_ARGS | ... | @@ -796,6 +796,7 @@ set(BUILD_ZIG1_ARGS |
| 796 | --name zig1 | 796 | --name zig1 |
| 797 | --zig-lib-dir "${CMAKE_SOURCE_DIR}/lib" | 797 | --zig-lib-dir "${CMAKE_SOURCE_DIR}/lib" |
| 798 | "-femit-bin=${ZIG1_OBJECT}" | 798 | "-femit-bin=${ZIG1_OBJECT}" |
| 799 | -fcompiler-rt | ||
| 799 | "${ZIG1_RELEASE_ARG}" | 800 | "${ZIG1_RELEASE_ARG}" |
| 800 | "${ZIG1_SINGLE_THREADED_ARG}" | 801 | "${ZIG1_SINGLE_THREADED_ARG}" |
| 801 | -lc | 802 | -lc |
src/Compilation.zig+13-19| ... | @@ -826,6 +826,9 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { | ... | @@ -826,6 +826,9 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { |
| 826 | const ofmt = options.object_format orelse options.target.getObjectFormat(); | 826 | const ofmt = options.object_format orelse options.target.getObjectFormat(); |
| 827 | 827 | ||
| 828 | const use_stage1 = options.use_stage1 orelse blk: { | 828 | const use_stage1 = options.use_stage1 orelse blk: { |
| 829 | // Even though we may have no Zig code to compile (depending on `options.root_pkg`), | ||
| 830 | // we may need to use stage1 for building compiler-rt and other dependencies. | ||
| 831 | |||
| 829 | if (build_options.omit_stage2) | 832 | if (build_options.omit_stage2) |
| 830 | break :blk true; | 833 | break :blk true; |
| 831 | if (options.use_llvm) |use_llvm| { | 834 | if (options.use_llvm) |use_llvm| { |
| ... | @@ -833,9 +836,6 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { | ... | @@ -833,9 +836,6 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { |
| 833 | break :blk false; | 836 | break :blk false; |
| 834 | } | 837 | } |
| 835 | } | 838 | } |
| 836 | // If we have no zig code to compile, no need for stage1 backend. | ||
| 837 | if (options.root_pkg == null) | ||
| 838 | break :blk false; | ||
| 839 | 839 | ||
| 840 | break :blk build_options.is_stage1; | 840 | break :blk build_options.is_stage1; |
| 841 | }; | 841 | }; |
| ... | @@ -878,9 +878,6 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { | ... | @@ -878,9 +878,6 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { |
| 878 | if (options.emit_llvm_ir != null or options.emit_llvm_bc != null) { | 878 | if (options.emit_llvm_ir != null or options.emit_llvm_bc != null) { |
| 879 | return error.EmittingLlvmModuleRequiresUsingLlvmBackend; | 879 | return error.EmittingLlvmModuleRequiresUsingLlvmBackend; |
| 880 | } | 880 | } |
| 881 | if (use_stage1) { | ||
| 882 | return error.@"stage1 only supports LLVM backend"; | ||
| 883 | } | ||
| 884 | } | 881 | } |
| 885 | 882 | ||
| 886 | const tsan = options.want_tsan orelse false; | 883 | const tsan = options.want_tsan orelse false; |
| ... | @@ -1542,24 +1539,19 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { | ... | @@ -1542,24 +1539,19 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { |
| 1542 | } | 1539 | } |
| 1543 | 1540 | ||
| 1544 | // The `use_stage1` condition is here only because stage2 cannot yet build compiler-rt. | 1541 | // The `use_stage1` condition is here only because stage2 cannot yet build compiler-rt. |
| 1545 | // Once it is capable this condition should be removed. | 1542 | // Once it is capable this condition should be removed. When removing this condition, |
| 1543 | // also test the use case of `build-obj -fcompiler-rt` with the self-hosted compiler | ||
| 1544 | // and make sure the compiler-rt symbols are emitted. Currently this is hooked up for | ||
| 1545 | // stage1 but not stage2. | ||
| 1546 | if (comp.bin_file.options.use_stage1) { | 1546 | if (comp.bin_file.options.use_stage1) { |
| 1547 | if (comp.bin_file.options.include_compiler_rt) { | 1547 | if (comp.bin_file.options.include_compiler_rt) { |
| 1548 | if (is_exe_or_dyn_lib) { | 1548 | if (is_exe_or_dyn_lib) { |
| 1549 | try comp.work_queue.writeItem(.{ .compiler_rt_lib = {} }); | 1549 | try comp.work_queue.writeItem(.{ .compiler_rt_lib = {} }); |
| 1550 | } else { | 1550 | } else if (options.output_mode != .Obj) { |
| 1551 | // If build-obj with -fcompiler-rt is requested, that is handled specially | ||
| 1552 | // elsewhere. In this case we are making a static library, so we ask | ||
| 1553 | // for a compiler-rt object to put in it. | ||
| 1551 | try comp.work_queue.writeItem(.{ .compiler_rt_obj = {} }); | 1554 | try comp.work_queue.writeItem(.{ .compiler_rt_obj = {} }); |
| 1552 | if (comp.bin_file.options.object_format != .elf and | ||
| 1553 | comp.bin_file.options.output_mode == .Obj) | ||
| 1554 | { | ||
| 1555 | // For ELF we can rely on using -r to link multiple objects together into one, | ||
| 1556 | // but to truly support `build-obj -fcompiler-rt` will require virtually | ||
| 1557 | // injecting `_ = @import("compiler_rt.zig")` into the root source file of | ||
| 1558 | // the compilation. | ||
| 1559 | fatal("Embedding compiler-rt into {s} objects is not yet implemented.", .{ | ||
| 1560 | @tagName(comp.bin_file.options.object_format), | ||
| 1561 | }); | ||
| 1562 | } | ||
| 1563 | } | 1555 | } |
| 1564 | } | 1556 | } |
| 1565 | if (needs_c_symbols) { | 1557 | if (needs_c_symbols) { |
| ... | @@ -4002,6 +3994,7 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node | ... | @@ -4002,6 +3994,7 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node |
| 4002 | man.hash.add(target.os.getVersionRange()); | 3994 | man.hash.add(target.os.getVersionRange()); |
| 4003 | man.hash.add(comp.bin_file.options.dll_export_fns); | 3995 | man.hash.add(comp.bin_file.options.dll_export_fns); |
| 4004 | man.hash.add(comp.bin_file.options.function_sections); | 3996 | man.hash.add(comp.bin_file.options.function_sections); |
| 3997 | man.hash.add(comp.bin_file.options.include_compiler_rt); | ||
| 4005 | man.hash.add(comp.bin_file.options.is_test); | 3998 | man.hash.add(comp.bin_file.options.is_test); |
| 4006 | man.hash.add(comp.bin_file.options.emit != null); | 3999 | man.hash.add(comp.bin_file.options.emit != null); |
| 4007 | man.hash.add(mod.emit_h != null); | 4000 | man.hash.add(mod.emit_h != null); |
| ... | @@ -4182,6 +4175,7 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node | ... | @@ -4182,6 +4175,7 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node |
| 4182 | .valgrind_enabled = comp.bin_file.options.valgrind, | 4175 | .valgrind_enabled = comp.bin_file.options.valgrind, |
| 4183 | .tsan_enabled = comp.bin_file.options.tsan, | 4176 | .tsan_enabled = comp.bin_file.options.tsan, |
| 4184 | .function_sections = comp.bin_file.options.function_sections, | 4177 | .function_sections = comp.bin_file.options.function_sections, |
| 4178 | .include_compiler_rt = comp.bin_file.options.include_compiler_rt, | ||
| 4185 | .enable_stack_probing = comp.bin_file.options.stack_check, | 4179 | .enable_stack_probing = comp.bin_file.options.stack_check, |
| 4186 | .red_zone = comp.bin_file.options.red_zone, | 4180 | .red_zone = comp.bin_file.options.red_zone, |
| 4187 | .enable_time_report = comp.time_report, | 4181 | .enable_time_report = comp.time_report, |
src/link/Elf.zig+4| ... | @@ -1289,6 +1289,10 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { | ... | @@ -1289,6 +1289,10 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { |
| 1289 | // TODO: remove when stage2 can build compiler_rt.zig | 1289 | // TODO: remove when stage2 can build compiler_rt.zig |
| 1290 | if (!build_options.is_stage1) break :blk null; | 1290 | if (!build_options.is_stage1) break :blk null; |
| 1291 | 1291 | ||
| 1292 | // In the case of build-obj we include the compiler-rt symbols directly alongside | ||
| 1293 | // the symbols of the root source file, in the same compilation unit. | ||
| 1294 | if (is_obj) break :blk null; | ||
| 1295 | |||
| 1292 | if (is_exe_or_dyn_lib) { | 1296 | if (is_exe_or_dyn_lib) { |
| 1293 | break :blk comp.compiler_rt_static_lib.?.full_object_path; | 1297 | break :blk comp.compiler_rt_static_lib.?.full_object_path; |
| 1294 | } else { | 1298 | } else { |
src/link/Wasm.zig+3-1| ... | @@ -645,7 +645,9 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void { | ... | @@ -645,7 +645,9 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void { |
| 645 | break :blk full_obj_path; | 645 | break :blk full_obj_path; |
| 646 | } else null; | 646 | } else null; |
| 647 | 647 | ||
| 648 | const compiler_rt_path: ?[]const u8 = if (self.base.options.include_compiler_rt) | 648 | const is_obj = self.base.options.output_mode == .Obj; |
| 649 | |||
| 650 | const compiler_rt_path: ?[]const u8 = if (self.base.options.include_compiler_rt and !is_obj) | ||
| 649 | comp.compiler_rt_static_lib.?.full_object_path | 651 | comp.compiler_rt_static_lib.?.full_object_path |
| 650 | else | 652 | else |
| 651 | null; | 653 | null; |
src/main.zig+2-2| ... | @@ -385,8 +385,8 @@ const usage_build_generic = | ... | @@ -385,8 +385,8 @@ const usage_build_generic = |
| 385 | \\ --dynamic-linker [path] Set the dynamic interpreter path (usually ld.so) | 385 | \\ --dynamic-linker [path] Set the dynamic interpreter path (usually ld.so) |
| 386 | \\ --sysroot [path] Set the system root directory (usually /) | 386 | \\ --sysroot [path] Set the system root directory (usually /) |
| 387 | \\ --version [ver] Dynamic library semver | 387 | \\ --version [ver] Dynamic library semver |
| 388 | \\ -fsoname[=name] (Linux) Override the default SONAME value | 388 | \\ -fsoname[=name] Override the default SONAME value |
| 389 | \\ -fno-soname (Linux) Disable emitting a SONAME | 389 | \\ -fno-soname Disable emitting a SONAME |
| 390 | \\ -fLLD Force using LLD as the linker | 390 | \\ -fLLD Force using LLD as the linker |
| 391 | \\ -fno-LLD Prevent using LLD as the linker | 391 | \\ -fno-LLD Prevent using LLD as the linker |
| 392 | \\ -fcompiler-rt Always include compiler-rt symbols in output | 392 | \\ -fcompiler-rt Always include compiler-rt symbols in output |
src/stage1.zig+1-1| ... | @@ -21,7 +21,6 @@ comptime { | ... | @@ -21,7 +21,6 @@ comptime { |
| 21 | assert(build_options.is_stage1); | 21 | assert(build_options.is_stage1); |
| 22 | assert(build_options.have_llvm); | 22 | assert(build_options.have_llvm); |
| 23 | if (!builtin.is_test) { | 23 | if (!builtin.is_test) { |
| 24 | _ = @import("compiler_rt"); | ||
| 25 | @export(main, .{ .name = "main" }); | 24 | @export(main, .{ .name = "main" }); |
| 26 | } | 25 | } |
| 27 | } | 26 | } |
| ... | @@ -126,6 +125,7 @@ pub const Module = extern struct { | ... | @@ -126,6 +125,7 @@ pub const Module = extern struct { |
| 126 | valgrind_enabled: bool, | 125 | valgrind_enabled: bool, |
| 127 | tsan_enabled: bool, | 126 | tsan_enabled: bool, |
| 128 | function_sections: bool, | 127 | function_sections: bool, |
| 128 | include_compiler_rt: bool, | ||
| 129 | enable_stack_probing: bool, | 129 | enable_stack_probing: bool, |
| 130 | red_zone: bool, | 130 | red_zone: bool, |
| 131 | enable_time_report: bool, | 131 | enable_time_report: bool, |
src/stage1/all_types.hpp+1| ... | @@ -2150,6 +2150,7 @@ struct CodeGen { | ... | @@ -2150,6 +2150,7 @@ struct CodeGen { |
| 2150 | bool have_stack_probing; | 2150 | bool have_stack_probing; |
| 2151 | bool red_zone; | 2151 | bool red_zone; |
| 2152 | bool function_sections; | 2152 | bool function_sections; |
| 2153 | bool include_compiler_rt; | ||
| 2153 | bool test_is_evented; | 2154 | bool test_is_evented; |
| 2154 | bool valgrind_enabled; | 2155 | bool valgrind_enabled; |
| 2155 | bool tsan_enabled; | 2156 | bool tsan_enabled; |
src/stage1/codegen.cpp+16| ... | @@ -9542,6 +9542,22 @@ static void gen_root_source(CodeGen *g) { | ... | @@ -9542,6 +9542,22 @@ static void gen_root_source(CodeGen *g) { |
| 9542 | g->panic_fn = panic_fn_val->data.x_ptr.data.fn.fn_entry; | 9542 | g->panic_fn = panic_fn_val->data.x_ptr.data.fn.fn_entry; |
| 9543 | assert(g->panic_fn != nullptr); | 9543 | assert(g->panic_fn != nullptr); |
| 9544 | 9544 | ||
| 9545 | if (g->include_compiler_rt) { | ||
| 9546 | Buf *import_target_path; | ||
| 9547 | Buf full_path = BUF_INIT; | ||
| 9548 | ZigType *compiler_rt_import; | ||
| 9549 | if ((err = analyze_import(g, std_import, buf_create_from_str("./special/compiler_rt.zig"), | ||
| 9550 | &compiler_rt_import, &import_target_path, &full_path))) | ||
| 9551 | { | ||
| 9552 | if (err == ErrorFileNotFound) { | ||
| 9553 | fprintf(stderr, "unable to find '%s'", buf_ptr(import_target_path)); | ||
| 9554 | } else { | ||
| 9555 | fprintf(stderr, "unable to open '%s': %s\n", buf_ptr(&full_path), err_str(err)); | ||
| 9556 | } | ||
| 9557 | exit(1); | ||
| 9558 | } | ||
| 9559 | } | ||
| 9560 | |||
| 9545 | if (!g->error_during_imports) { | 9561 | if (!g->error_during_imports) { |
| 9546 | semantic_analyze(g); | 9562 | semantic_analyze(g); |
| 9547 | } | 9563 | } |
src/stage1/stage1.cpp+1| ... | @@ -101,6 +101,7 @@ void zig_stage1_build_object(struct ZigStage1 *stage1) { | ... | @@ -101,6 +101,7 @@ void zig_stage1_build_object(struct ZigStage1 *stage1) { |
| 101 | g->link_libc = stage1->link_libc; | 101 | g->link_libc = stage1->link_libc; |
| 102 | g->link_libcpp = stage1->link_libcpp; | 102 | g->link_libcpp = stage1->link_libcpp; |
| 103 | g->function_sections = stage1->function_sections; | 103 | g->function_sections = stage1->function_sections; |
| 104 | g->include_compiler_rt = stage1->include_compiler_rt; | ||
| 104 | 105 | ||
| 105 | g->subsystem = stage1->subsystem; | 106 | g->subsystem = stage1->subsystem; |
| 106 | 107 |
src/stage1/stage1.h+1| ... | @@ -196,6 +196,7 @@ struct ZigStage1 { | ... | @@ -196,6 +196,7 @@ struct ZigStage1 { |
| 196 | bool valgrind_enabled; | 196 | bool valgrind_enabled; |
| 197 | bool tsan_enabled; | 197 | bool tsan_enabled; |
| 198 | bool function_sections; | 198 | bool function_sections; |
| 199 | bool include_compiler_rt; | ||
| 199 | bool enable_stack_probing; | 200 | bool enable_stack_probing; |
| 200 | bool red_zone; | 201 | bool red_zone; |
| 201 | bool enable_time_report; | 202 | bool enable_time_report; |
src/stage1/zig0.cpp+5| ... | @@ -39,6 +39,7 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) { | ... | @@ -39,6 +39,7 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) { |
| 39 | " --color [auto|off|on] enable or disable colored error messages\n" | 39 | " --color [auto|off|on] enable or disable colored error messages\n" |
| 40 | " --name [name] override output name\n" | 40 | " --name [name] override output name\n" |
| 41 | " -femit-bin=[path] Output machine code\n" | 41 | " -femit-bin=[path] Output machine code\n" |
| 42 | " -fcompiler-rt Always include compiler-rt symbols in output\n" | ||
| 42 | " --pkg-begin [name] [path] make pkg available to import and push current pkg\n" | 43 | " --pkg-begin [name] [path] make pkg available to import and push current pkg\n" |
| 43 | " --pkg-end pop current pkg\n" | 44 | " --pkg-end pop current pkg\n" |
| 44 | " -ODebug build with optimizations off and safety on\n" | 45 | " -ODebug build with optimizations off and safety on\n" |
| ... | @@ -266,6 +267,7 @@ int main(int argc, char **argv) { | ... | @@ -266,6 +267,7 @@ int main(int argc, char **argv) { |
| 266 | const char *mcpu = nullptr; | 267 | const char *mcpu = nullptr; |
| 267 | bool single_threaded = false; | 268 | bool single_threaded = false; |
| 268 | bool is_test_build = false; | 269 | bool is_test_build = false; |
| 270 | bool include_compiler_rt = false; | ||
| 269 | 271 | ||
| 270 | for (int i = 1; i < argc; i += 1) { | 272 | for (int i = 1; i < argc; i += 1) { |
| 271 | char *arg = argv[i]; | 273 | char *arg = argv[i]; |
| ... | @@ -334,6 +336,8 @@ int main(int argc, char **argv) { | ... | @@ -334,6 +336,8 @@ int main(int argc, char **argv) { |
| 334 | mcpu = arg + strlen("-mcpu="); | 336 | mcpu = arg + strlen("-mcpu="); |
| 335 | } else if (str_starts_with(arg, "-femit-bin=")) { | 337 | } else if (str_starts_with(arg, "-femit-bin=")) { |
| 336 | emit_bin_path = arg + strlen("-femit-bin="); | 338 | emit_bin_path = arg + strlen("-femit-bin="); |
| 339 | } else if (strcmp(arg, "-fcompiler-rt") == 0) { | ||
| 340 | include_compiler_rt = true; | ||
| 337 | } else if (i + 1 >= argc) { | 341 | } else if (i + 1 >= argc) { |
| 338 | fprintf(stderr, "Expected another argument after %s\n", arg); | 342 | fprintf(stderr, "Expected another argument after %s\n", arg); |
| 339 | return print_error_usage(arg0); | 343 | return print_error_usage(arg0); |
| ... | @@ -468,6 +472,7 @@ int main(int argc, char **argv) { | ... | @@ -468,6 +472,7 @@ int main(int argc, char **argv) { |
| 468 | stage1->subsystem = subsystem; | 472 | stage1->subsystem = subsystem; |
| 469 | stage1->pic = true; | 473 | stage1->pic = true; |
| 470 | stage1->is_single_threaded = single_threaded; | 474 | stage1->is_single_threaded = single_threaded; |
| 475 | stage1->include_compiler_rt = include_compiler_rt; | ||
| 471 | 476 | ||
| 472 | zig_stage1_build_object(stage1); | 477 | zig_stage1_build_object(stage1); |
| 473 | 478 |