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
796796 --name zig1
797797 --zig-lib-dir "${CMAKE_SOURCE_DIR}/lib"
798798 "-femit-bin=${ZIG1_OBJECT}"
799 -fcompiler-rt
799800 "${ZIG1_RELEASE_ARG}"
800801 "${ZIG1_SINGLE_THREADED_ARG}"
801802 -lc
src/Compilation.zig+13-19
......@@ -826,6 +826,9 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
826826 const ofmt = options.object_format orelse options.target.getObjectFormat();
827827
828828 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
829832 if (build_options.omit_stage2)
830833 break :blk true;
831834 if (options.use_llvm) |use_llvm| {
......@@ -833,9 +836,6 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
833836 break :blk false;
834837 }
835838 }
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
840840 break :blk build_options.is_stage1;
841841 };
......@@ -878,9 +878,6 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
878878 if (options.emit_llvm_ir != null or options.emit_llvm_bc != null) {
879879 return error.EmittingLlvmModuleRequiresUsingLlvmBackend;
880880 }
881 if (use_stage1) {
882 return error.@"stage1 only supports LLVM backend";
883 }
884881 }
885882
886883 const tsan = options.want_tsan orelse false;
......@@ -1542,24 +1539,19 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
15421539 }
15431540
15441541 // 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.
15461546 if (comp.bin_file.options.use_stage1) {
15471547 if (comp.bin_file.options.include_compiler_rt) {
15481548 if (is_exe_or_dyn_lib) {
15491549 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.
15511554 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 }
15631555 }
15641556 }
15651557 if (needs_c_symbols) {
......@@ -4002,6 +3994,7 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
40023994 man.hash.add(target.os.getVersionRange());
40033995 man.hash.add(comp.bin_file.options.dll_export_fns);
40043996 man.hash.add(comp.bin_file.options.function_sections);
3997 man.hash.add(comp.bin_file.options.include_compiler_rt);
40053998 man.hash.add(comp.bin_file.options.is_test);
40063999 man.hash.add(comp.bin_file.options.emit != null);
40074000 man.hash.add(mod.emit_h != null);
......@@ -4182,6 +4175,7 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
41824175 .valgrind_enabled = comp.bin_file.options.valgrind,
41834176 .tsan_enabled = comp.bin_file.options.tsan,
41844177 .function_sections = comp.bin_file.options.function_sections,
4178 .include_compiler_rt = comp.bin_file.options.include_compiler_rt,
41854179 .enable_stack_probing = comp.bin_file.options.stack_check,
41864180 .red_zone = comp.bin_file.options.red_zone,
41874181 .enable_time_report = comp.time_report,
src/link/Elf.zig+4
......@@ -1289,6 +1289,10 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
12891289 // TODO: remove when stage2 can build compiler_rt.zig
12901290 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
12921296 if (is_exe_or_dyn_lib) {
12931297 break :blk comp.compiler_rt_static_lib.?.full_object_path;
12941298 } else {
src/link/Wasm.zig+3-1
......@@ -645,7 +645,9 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {
645645 break :blk full_obj_path;
646646 } 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)
649651 comp.compiler_rt_static_lib.?.full_object_path
650652 else
651653 null;
src/main.zig+2-2
......@@ -385,8 +385,8 @@ const usage_build_generic =
385385 \\ --dynamic-linker [path] Set the dynamic interpreter path (usually ld.so)
386386 \\ --sysroot [path] Set the system root directory (usually /)
387387 \\ --version [ver] Dynamic library semver
388 \\ -fsoname[=name] (Linux) Override the default SONAME value
389 \\ -fno-soname (Linux) Disable emitting a SONAME
388 \\ -fsoname[=name] Override the default SONAME value
389 \\ -fno-soname Disable emitting a SONAME
390390 \\ -fLLD Force using LLD as the linker
391391 \\ -fno-LLD Prevent using LLD as the linker
392392 \\ -fcompiler-rt Always include compiler-rt symbols in output
src/stage1.zig+1-1
......@@ -21,7 +21,6 @@ comptime {
2121 assert(build_options.is_stage1);
2222 assert(build_options.have_llvm);
2323 if (!builtin.is_test) {
24 _ = @import("compiler_rt");
2524 @export(main, .{ .name = "main" });
2625 }
2726}
......@@ -126,6 +125,7 @@ pub const Module = extern struct {
126125 valgrind_enabled: bool,
127126 tsan_enabled: bool,
128127 function_sections: bool,
128 include_compiler_rt: bool,
129129 enable_stack_probing: bool,
130130 red_zone: bool,
131131 enable_time_report: bool,
src/stage1/all_types.hpp+1
......@@ -2150,6 +2150,7 @@ struct CodeGen {
21502150 bool have_stack_probing;
21512151 bool red_zone;
21522152 bool function_sections;
2153 bool include_compiler_rt;
21532154 bool test_is_evented;
21542155 bool valgrind_enabled;
21552156 bool tsan_enabled;
src/stage1/codegen.cpp+16
......@@ -9542,6 +9542,22 @@ static void gen_root_source(CodeGen *g) {
95429542 g->panic_fn = panic_fn_val->data.x_ptr.data.fn.fn_entry;
95439543 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
95459561 if (!g->error_during_imports) {
95469562 semantic_analyze(g);
95479563 }
src/stage1/stage1.cpp+1
......@@ -101,6 +101,7 @@ void zig_stage1_build_object(struct ZigStage1 *stage1) {
101101 g->link_libc = stage1->link_libc;
102102 g->link_libcpp = stage1->link_libcpp;
103103 g->function_sections = stage1->function_sections;
104 g->include_compiler_rt = stage1->include_compiler_rt;
104105
105106 g->subsystem = stage1->subsystem;
106107
src/stage1/stage1.h+1
......@@ -196,6 +196,7 @@ struct ZigStage1 {
196196 bool valgrind_enabled;
197197 bool tsan_enabled;
198198 bool function_sections;
199 bool include_compiler_rt;
199200 bool enable_stack_probing;
200201 bool red_zone;
201202 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) {
3939 " --color [auto|off|on] enable or disable colored error messages\n"
4040 " --name [name] override output name\n"
4141 " -femit-bin=[path] Output machine code\n"
42 " -fcompiler-rt Always include compiler-rt symbols in output\n"
4243 " --pkg-begin [name] [path] make pkg available to import and push current pkg\n"
4344 " --pkg-end pop current pkg\n"
4445 " -ODebug build with optimizations off and safety on\n"
......@@ -266,6 +267,7 @@ int main(int argc, char **argv) {
266267 const char *mcpu = nullptr;
267268 bool single_threaded = false;
268269 bool is_test_build = false;
270 bool include_compiler_rt = false;
269271
270272 for (int i = 1; i < argc; i += 1) {
271273 char *arg = argv[i];
......@@ -334,6 +336,8 @@ int main(int argc, char **argv) {
334336 mcpu = arg + strlen("-mcpu=");
335337 } else if (str_starts_with(arg, "-femit-bin=")) {
336338 emit_bin_path = arg + strlen("-femit-bin=");
339 } else if (strcmp(arg, "-fcompiler-rt") == 0) {
340 include_compiler_rt = true;
337341 } else if (i + 1 >= argc) {
338342 fprintf(stderr, "Expected another argument after %s\n", arg);
339343 return print_error_usage(arg0);
......@@ -468,6 +472,7 @@ int main(int argc, char **argv) {
468472 stage1->subsystem = subsystem;
469473 stage1->pic = true;
470474 stage1->is_single_threaded = single_threaded;
475 stage1->include_compiler_rt = include_compiler_rt;
471476
472477 zig_stage1_build_object(stage1);
473478