authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-07-22 16:37:38-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-07-22 19:51:32-07:00
log7c25390c957273ff43927608a45e257c4ed73549
tree411809c36b20d2575816bcf726330413c3624398
parenta5fb28070f37c2cad92ac8805bcc704e872fc538

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.

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 zig1796 --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 -lc802 -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();
827827
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;
839839
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 }
885882
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 }
15431540
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.zig1289 // 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;
12911291
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;
647647
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_path651 comp.compiler_rt_static_lib.?.full_object_path
650 else652 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 semver387 \\ --version [ver] Dynamic library semver
388 \\ -fsoname[=name] (Linux) Override the default SONAME value388 \\ -fsoname[=name] Override the default SONAME value
389 \\ -fno-soname (Linux) Disable emitting a SONAME389 \\ -fno-soname Disable emitting a SONAME
390 \\ -fLLD Force using LLD as the linker390 \\ -fLLD Force using LLD as the linker
391 \\ -fno-LLD Prevent using LLD as the linker391 \\ -fno-LLD Prevent using LLD as the linker
392 \\ -fcompiler-rt Always include compiler-rt symbols in output392 \\ -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);
95449544
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;
104105
105 g->subsystem = stage1->subsystem;106 g->subsystem = stage1->subsystem;
106107
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;
269271
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;
471476
472 zig_stage1_build_object(stage1);477 zig_stage1_build_object(stage1);
473478