Skip to content

Commit

Permalink
support -fcompiler-rt in conjunction with build-obj
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
andrewrk committed Jul 23, 2021
1 parent 3a5678a commit 2a8b83c
Show file tree
Hide file tree
Showing 11 changed files with 48 additions and 23 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
32 changes: 13 additions & 19 deletions src/Compilation.zig
Original file line number Diff line number Diff line change
Expand Up @@ -826,16 +826,16 @@ 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| {
if (!use_llvm) {
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;
};
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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,
Expand Down
4 changes: 4 additions & 0 deletions src/link/Elf.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
4 changes: 3 additions & 1 deletion src/link/Wasm.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
4 changes: 2 additions & 2 deletions src/main.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/stage1.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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" });
}
}
Expand Down Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions src/stage1/all_types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
16 changes: 16 additions & 0 deletions src/stage1/codegen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
1 change: 1 addition & 0 deletions src/stage1/stage1.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
1 change: 1 addition & 0 deletions src/stage1/stage1.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
5 changes: 5 additions & 0 deletions src/stage1/zig0.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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];
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);

Expand Down

0 comments on commit 2a8b83c

Please sign in to comment.