authorgravatar for david@vortan.devDavid Rubin <david@vortan.dev> 2024-12-26 05:30:56-08:00
committergravatar for david@vortan.devDavid Rubin <david@vortan.dev> 2025-02-25 11:22:33-08:00
log2d4574aafbf95f357ad6306b98d9c8ebebbafe2e
treecbe4a1fb8e16545bfe49e542f8d8d1f02de9aa36
parenta468929519157cf7a2070a0368357cb73ad80b61

Compilation: always import ubsan if a ZCU exists

Unlike `compiler-rt`, `ubsan` uses the standard library quite a lot. Using a similar approach to how `compiler-rt` is handled today, where it's compiled into its own object and then linked would be sub-optimal as we'd be introducing a lot of code bloat. This approach always "imports" `ubsan` if the ZCU, if it exists. If it doesn't such as the case where we're compiling only C code, then we have no choice other than to compile it down to an object and link. There's still a tiny optimization we can do in that case, which is when compiling to a static library, there's no need to construct an archive with a single object. We'd only go back and parse out ubsan from the archive later in the pipeline. So we compile it to an object instead and link that to the static library. TLDR; - `zig build-exe foo.c` -> build `libubsan.a` and links - `zig build-obj foo.c` -> doesn't build anything, just emits references to ubsan runtime - `zig build-lib foo.c -static` -> build `ubsan.o` and link it - `zig build-exe foo.zig bar.c` -> import `ubsan-rt` into the ZCU - `zig build-obj foo.zig bar.c` -> import `ubsan-rt` into the ZCU - `zig build-lib foo.zig bar.c` -> import `ubsan-rt` into the ZCU

2 files changed, 6 insertions(+), 3 deletions(-)

src/Compilation.zig+5-2
...@@ -1309,7 +1309,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil...@@ -1309,7 +1309,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
1309 (!options.skip_linker_dependencies and is_exe_or_dyn_lib);1309 (!options.skip_linker_dependencies and is_exe_or_dyn_lib);
13101310
1311 const include_ubsan_rt = options.want_ubsan_rt orelse1311 const include_ubsan_rt = options.want_ubsan_rt orelse
1312 (!options.skip_linker_dependencies and is_exe_or_dyn_lib);1312 (!options.skip_linker_dependencies and is_exe_or_dyn_lib and !have_zcu);
13131313
1314 if (include_compiler_rt and output_mode == .Obj) {1314 if (include_compiler_rt and output_mode == .Obj) {
1315 // For objects, this mechanism relies on essentially `_ = @import("compiler-rt");`1315 // For objects, this mechanism relies on essentially `_ = @import("compiler-rt");`
...@@ -1337,7 +1337,10 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil...@@ -1337,7 +1337,10 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
1337 try options.root_mod.deps.putNoClobber(arena, "compiler_rt", compiler_rt_mod);1337 try options.root_mod.deps.putNoClobber(arena, "compiler_rt", compiler_rt_mod);
1338 }1338 }
13391339
1340 if (include_ubsan_rt and output_mode == .Obj) {1340 // unlike compiler_rt, we always want to go through the `_ = @import("ubsan-rt")`
1341 // approach, since the ubsan runtime uses quite a lot of the standard library
1342 // and this reduces unnecessary bloat.
1343 if (!options.skip_linker_dependencies and have_zcu) {
1341 const ubsan_rt_mod = try Package.Module.create(arena, .{1344 const ubsan_rt_mod = try Package.Module.create(arena, .{
1342 .global_cache_directory = options.global_cache_directory,1345 .global_cache_directory = options.global_cache_directory,
1343 .paths = .{1346 .paths = .{
src/Zcu.zig+1-1
...@@ -175,7 +175,7 @@ nav_val_analysis_queued: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, voi...@@ -175,7 +175,7 @@ nav_val_analysis_queued: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, voi
175175
176/// These are the modules which we initially queue for analysis in `Compilation.update`.176/// These are the modules which we initially queue for analysis in `Compilation.update`.
177/// `resolveReferences` will use these as the root of its reachability traversal.177/// `resolveReferences` will use these as the root of its reachability traversal.
178analysis_roots: std.BoundedArray(*Package.Module, 3) = .{},178analysis_roots: std.BoundedArray(*Package.Module, 4) = .{},
179/// This is the cached result of `Zcu.resolveReferences`. It is computed on-demand, and179/// This is the cached result of `Zcu.resolveReferences`. It is computed on-demand, and
180/// reset to `null` when any semantic analysis occurs (since this invalidates the data).180/// reset to `null` when any semantic analysis occurs (since this invalidates the data).
181/// Allocated into `gpa`.181/// Allocated into `gpa`.