master
1//! Corresponds to something that Zig source code can `@import`.
2
3/// The root directory of the module. Only files inside this directory can be imported.
4root: Compilation.Path,
5/// Path to the root source file of this module. Relative to `root`. May contain path separators.
6root_src_path: []const u8,
7/// Name used in compile errors. Looks like "root.foo.bar".
8fully_qualified_name: []const u8,
9/// The dependency table of this module. The shared dependencies 'std' and
10/// 'root' are not specified in every module dependency table, but are stored
11/// separately in `Zcu`. 'builtin' is also not stored here, although it is
12/// not necessarily the same between all modules. Handling of `@import` in
13/// the rest of the compiler must detect these special names and use the
14/// correct module instead of consulting `deps`.
15deps: Deps = .{},
16
17resolved_target: ResolvedTarget,
18optimize_mode: std.builtin.OptimizeMode,
19code_model: std.builtin.CodeModel,
20single_threaded: bool,
21error_tracing: bool,
22valgrind: bool,
23pic: bool,
24strip: bool,
25omit_frame_pointer: bool,
26stack_check: bool,
27stack_protector: u32,
28red_zone: bool,
29sanitize_c: std.zig.SanitizeC,
30sanitize_thread: bool,
31fuzz: bool,
32unwind_tables: std.builtin.UnwindTables,
33cc_argv: []const []const u8,
34/// (SPIR-V) whether to generate a structured control flow graph or not
35structured_cfg: bool,
36no_builtin: bool,
37
38pub const Deps = std.StringArrayHashMapUnmanaged(*Module);
39
40pub const Tree = struct {
41 /// Each `Package` exposes a `Module` with build.zig as its root source file.
42 build_module_table: std.AutoArrayHashMapUnmanaged(MultiHashHexDigest, *Module),
43};
44
45pub const CreateOptions = struct {
46 paths: Paths,
47 fully_qualified_name: []const u8,
48
49 cc_argv: []const []const u8,
50 inherited: Inherited,
51 global: Compilation.Config,
52 /// If this is null then `resolved_target` must be non-null.
53 parent: ?*Package.Module,
54
55 pub const Paths = struct {
56 root: Compilation.Path,
57 /// Relative to `root`. May contain path separators.
58 root_src_path: []const u8,
59 };
60
61 pub const Inherited = struct {
62 /// If this is null then `parent` must be non-null.
63 resolved_target: ?ResolvedTarget = null,
64 optimize_mode: ?std.builtin.OptimizeMode = null,
65 code_model: ?std.builtin.CodeModel = null,
66 single_threaded: ?bool = null,
67 error_tracing: ?bool = null,
68 valgrind: ?bool = null,
69 pic: ?bool = null,
70 strip: ?bool = null,
71 omit_frame_pointer: ?bool = null,
72 stack_check: ?bool = null,
73 /// null means default.
74 /// 0 means no stack protector.
75 /// other number means stack protection with that buffer size.
76 stack_protector: ?u32 = null,
77 red_zone: ?bool = null,
78 unwind_tables: ?std.builtin.UnwindTables = null,
79 sanitize_c: ?std.zig.SanitizeC = null,
80 sanitize_thread: ?bool = null,
81 fuzz: ?bool = null,
82 structured_cfg: ?bool = null,
83 no_builtin: ?bool = null,
84 };
85};
86
87pub const ResolvedTarget = struct {
88 result: std.Target,
89 is_native_os: bool,
90 is_native_abi: bool,
91 is_explicit_dynamic_linker: bool,
92 llvm_cpu_features: ?[*:0]const u8 = null,
93};
94
95pub const CreateError = error{
96 OutOfMemory,
97 ValgrindUnsupportedOnTarget,
98 TargetRequiresSingleThreaded,
99 BackendRequiresSingleThreaded,
100 TargetRequiresPic,
101 PieRequiresPic,
102 DynamicLinkingRequiresPic,
103 TargetHasNoRedZone,
104 StackCheckUnsupportedByTarget,
105 StackProtectorUnsupportedByTarget,
106 StackProtectorUnavailableWithoutLibC,
107};
108
109/// At least one of `parent` and `resolved_target` must be non-null.
110pub fn create(arena: Allocator, options: CreateOptions) !*Package.Module {
111 if (options.inherited.sanitize_thread == true) assert(options.global.any_sanitize_thread);
112 if (options.inherited.fuzz == true) assert(options.global.any_fuzz);
113 if (options.inherited.single_threaded == false) assert(options.global.any_non_single_threaded);
114 if (options.inherited.unwind_tables) |uwt| if (uwt != .none) assert(options.global.any_unwind_tables);
115 if (options.inherited.sanitize_c) |sc| if (sc != .off) assert(options.global.any_sanitize_c != .off);
116 if (options.inherited.error_tracing == true) assert(options.global.any_error_tracing);
117
118 const resolved_target = options.inherited.resolved_target orelse options.parent.?.resolved_target;
119 const target = &resolved_target.result;
120
121 const optimize_mode = options.inherited.optimize_mode orelse
122 if (options.parent) |p| p.optimize_mode else options.global.root_optimize_mode;
123
124 const strip = b: {
125 if (options.inherited.strip) |x| break :b x;
126 if (options.parent) |p| break :b p.strip;
127 break :b options.global.root_strip;
128 };
129
130 const zig_backend = target_util.zigBackend(target, options.global.use_llvm);
131
132 const valgrind = b: {
133 if (!target_util.hasValgrindSupport(target, zig_backend)) {
134 if (options.inherited.valgrind == true)
135 return error.ValgrindUnsupportedOnTarget;
136 break :b false;
137 }
138 if (options.inherited.valgrind) |x| break :b x;
139 if (options.parent) |p| break :b p.valgrind;
140 if (strip) break :b false;
141 break :b optimize_mode == .Debug;
142 };
143
144 const single_threaded = b: {
145 if (target_util.alwaysSingleThreaded(target)) {
146 if (options.inherited.single_threaded == false)
147 return error.TargetRequiresSingleThreaded;
148 break :b true;
149 }
150
151 if (options.global.have_zcu) {
152 if (!target_util.supportsThreads(target, zig_backend)) {
153 if (options.inherited.single_threaded == false)
154 return error.BackendRequiresSingleThreaded;
155 break :b true;
156 }
157 }
158
159 if (options.inherited.single_threaded) |x| break :b x;
160 if (options.parent) |p| break :b p.single_threaded;
161 break :b target_util.defaultSingleThreaded(target);
162 };
163
164 const error_tracing = b: {
165 if (options.inherited.error_tracing) |x| break :b x;
166 if (options.parent) |p| break :b p.error_tracing;
167 break :b options.global.root_error_tracing;
168 };
169
170 const pic = b: {
171 if (target_util.requiresPIC(target, options.global.link_libc)) {
172 if (options.inherited.pic == false)
173 return error.TargetRequiresPic;
174 break :b true;
175 }
176 if (options.global.pie) {
177 if (options.inherited.pic == false)
178 return error.PieRequiresPic;
179 break :b true;
180 }
181 if (options.global.link_mode == .dynamic) {
182 if (options.inherited.pic == false)
183 return error.DynamicLinkingRequiresPic;
184 break :b true;
185 }
186 if (options.inherited.pic) |x| break :b x;
187 if (options.parent) |p| break :b p.pic;
188 break :b false;
189 };
190
191 const red_zone = b: {
192 if (!target_util.hasRedZone(target)) {
193 if (options.inherited.red_zone == true)
194 return error.TargetHasNoRedZone;
195 break :b false;
196 }
197 if (options.inherited.red_zone) |x| break :b x;
198 if (options.parent) |p| break :b p.red_zone;
199 break :b true;
200 };
201
202 const omit_frame_pointer = b: {
203 if (options.inherited.omit_frame_pointer) |x| break :b x;
204 if (options.parent) |p| break :b p.omit_frame_pointer;
205 if (optimize_mode == .ReleaseSmall) {
206 // On x86, in most cases, keeping the frame pointer usually results in smaller binary size.
207 // This has to do with how instructions for memory access via the stack base pointer register (when keeping the frame pointer)
208 // are smaller than instructions for memory access via the stack pointer register (when omitting the frame pointer).
209 break :b !target.cpu.arch.isX86();
210 }
211 break :b false;
212 };
213
214 const sanitize_thread = b: {
215 if (options.inherited.sanitize_thread) |x| break :b x;
216 if (options.parent) |p| break :b p.sanitize_thread;
217 break :b false;
218 };
219
220 const unwind_tables = b: {
221 if (options.inherited.unwind_tables) |x| break :b x;
222 if (options.parent) |p| break :b p.unwind_tables;
223
224 break :b target_util.defaultUnwindTables(
225 target,
226 options.global.link_libunwind,
227 sanitize_thread or options.global.any_sanitize_thread,
228 );
229 };
230
231 const fuzz = b: {
232 if (options.inherited.fuzz) |x| break :b x;
233 if (options.parent) |p| break :b p.fuzz;
234 break :b false;
235 };
236
237 const code_model: std.builtin.CodeModel = b: {
238 if (options.inherited.code_model) |x| break :b x;
239 if (options.parent) |p| break :b p.code_model;
240 break :b .default;
241 };
242
243 const is_safe_mode = switch (optimize_mode) {
244 .Debug, .ReleaseSafe => true,
245 .ReleaseFast, .ReleaseSmall => false,
246 };
247
248 const sanitize_c: std.zig.SanitizeC = b: {
249 if (options.inherited.sanitize_c) |x| break :b x;
250 if (options.parent) |p| break :b p.sanitize_c;
251 break :b switch (optimize_mode) {
252 .Debug => .full,
253 // It's recommended to use the minimal runtime in production
254 // environments due to the security implications of the full runtime.
255 // The minimal runtime doesn't provide much benefit over simply
256 // trapping, however, so we do that instead.
257 .ReleaseSafe => .trap,
258 .ReleaseFast, .ReleaseSmall => .off,
259 };
260 };
261
262 const stack_check = b: {
263 if (!target_util.supportsStackProbing(target, zig_backend)) {
264 if (options.inherited.stack_check == true)
265 return error.StackCheckUnsupportedByTarget;
266 break :b false;
267 }
268 if (options.inherited.stack_check) |x| break :b x;
269 if (options.parent) |p| break :b p.stack_check;
270 break :b is_safe_mode;
271 };
272
273 const stack_protector: u32 = sp: {
274 const use_zig_backend = options.global.have_zcu or
275 (options.global.any_c_source_files and options.global.c_frontend == .aro);
276 if (use_zig_backend and !target_util.supportsStackProtector(target, zig_backend)) {
277 if (options.inherited.stack_protector) |x| {
278 if (x > 0) return error.StackProtectorUnsupportedByTarget;
279 }
280 break :sp 0;
281 }
282
283 if (options.global.any_c_source_files and options.global.c_frontend == .clang and
284 !target_util.clangSupportsStackProtector(target))
285 {
286 if (options.inherited.stack_protector) |x| {
287 if (x > 0) return error.StackProtectorUnsupportedByTarget;
288 }
289 break :sp 0;
290 }
291
292 // This logic is checking for linking libc because otherwise our start code
293 // which is trying to set up TLS (i.e. the fs/gs registers) but the stack
294 // protection code depends on fs/gs registers being already set up.
295 // If we were able to annotate start code, or perhaps the entire std lib,
296 // as being exempt from stack protection checks, we could change this logic
297 // to supporting stack protection even when not linking libc.
298 // TODO file issue about this
299 if (!options.global.link_libc) {
300 if (options.inherited.stack_protector) |x| {
301 if (x > 0) return error.StackProtectorUnavailableWithoutLibC;
302 }
303 break :sp 0;
304 }
305
306 if (options.inherited.stack_protector) |x| break :sp x;
307 if (options.parent) |p| break :sp p.stack_protector;
308 if (!is_safe_mode) break :sp 0;
309
310 break :sp target_util.default_stack_protector_buffer_size;
311 };
312
313 const structured_cfg = b: {
314 if (options.inherited.structured_cfg) |x| break :b x;
315 if (options.parent) |p| break :b p.structured_cfg;
316 // We always want a structured control flow in shaders. This option is
317 // only relevant for OpenCL kernels.
318 break :b switch (target.os.tag) {
319 .opencl => false,
320 else => true,
321 };
322 };
323
324 const no_builtin = b: {
325 if (options.inherited.no_builtin) |x| break :b x;
326 if (options.parent) |p| break :b p.no_builtin;
327
328 break :b target.cpu.arch.isBpf();
329 };
330
331 const llvm_cpu_features: ?[*:0]const u8 = b: {
332 if (resolved_target.llvm_cpu_features) |x| break :b x;
333 if (!options.global.use_llvm) break :b null;
334
335 var buf = std.array_list.Managed(u8).init(arena);
336 var disabled_features = std.array_list.Managed(u8).init(arena);
337 defer disabled_features.deinit();
338
339 // Append disabled features after enabled ones, so that their effects aren't overwritten.
340 for (target.cpu.arch.allFeaturesList()) |feature| {
341 if (feature.llvm_name) |llvm_name| {
342 // Ignore these until we figure out how to handle the concept of omitting features.
343 // See https://github.com/ziglang/zig/issues/23539
344 if (target_util.isDynamicAMDGCNFeature(target, feature)) continue;
345
346 var is_enabled = target.cpu.features.isEnabled(feature.index);
347 if (target.cpu.arch == .s390x and @as(std.Target.s390x.Feature, @enumFromInt(feature.index)) == .backchain) {
348 is_enabled = !omit_frame_pointer;
349 }
350
351 if (is_enabled) {
352 try buf.ensureUnusedCapacity(2 + llvm_name.len);
353 buf.appendAssumeCapacity('+');
354 buf.appendSliceAssumeCapacity(llvm_name);
355 buf.appendAssumeCapacity(',');
356 } else {
357 try disabled_features.ensureUnusedCapacity(2 + llvm_name.len);
358 disabled_features.appendAssumeCapacity('-');
359 disabled_features.appendSliceAssumeCapacity(llvm_name);
360 disabled_features.appendAssumeCapacity(',');
361 }
362 }
363 }
364
365 try buf.appendSlice(disabled_features.items);
366 if (buf.items.len == 0) break :b "";
367 assert(std.mem.endsWith(u8, buf.items, ","));
368 buf.items[buf.items.len - 1] = 0;
369 buf.shrinkAndFree(buf.items.len);
370 break :b buf.items[0 .. buf.items.len - 1 :0].ptr;
371 };
372
373 const mod = try arena.create(Module);
374 mod.* = .{
375 .root = options.paths.root,
376 .root_src_path = options.paths.root_src_path,
377 .fully_qualified_name = options.fully_qualified_name,
378 .resolved_target = .{
379 .result = target.*,
380 .is_native_os = resolved_target.is_native_os,
381 .is_native_abi = resolved_target.is_native_abi,
382 .is_explicit_dynamic_linker = resolved_target.is_explicit_dynamic_linker,
383 .llvm_cpu_features = llvm_cpu_features,
384 },
385 .optimize_mode = optimize_mode,
386 .single_threaded = single_threaded,
387 .error_tracing = error_tracing,
388 .valgrind = valgrind,
389 .pic = pic,
390 .strip = strip,
391 .omit_frame_pointer = omit_frame_pointer,
392 .stack_check = stack_check,
393 .stack_protector = stack_protector,
394 .code_model = code_model,
395 .red_zone = red_zone,
396 .sanitize_c = sanitize_c,
397 .sanitize_thread = sanitize_thread,
398 .fuzz = fuzz,
399 .unwind_tables = unwind_tables,
400 .cc_argv = options.cc_argv,
401 .structured_cfg = structured_cfg,
402 .no_builtin = no_builtin,
403 };
404 return mod;
405}
406
407/// All fields correspond to `CreateOptions`.
408pub const LimitedOptions = struct {
409 root: Compilation.Path,
410 root_src_path: []const u8,
411 fully_qualified_name: []const u8,
412};
413
414/// This one can only be used if the Module will only be used for AstGen and earlier in
415/// the pipeline. Illegal behavior occurs if a limited module touches Sema.
416pub fn createLimited(gpa: Allocator, options: LimitedOptions) Allocator.Error!*Package.Module {
417 const mod = try gpa.create(Module);
418 mod.* = .{
419 .root = options.root,
420 .root_src_path = options.root_src_path,
421 .fully_qualified_name = options.fully_qualified_name,
422
423 .resolved_target = undefined,
424 .optimize_mode = undefined,
425 .code_model = undefined,
426 .single_threaded = undefined,
427 .error_tracing = undefined,
428 .valgrind = undefined,
429 .pic = undefined,
430 .strip = undefined,
431 .omit_frame_pointer = undefined,
432 .stack_check = undefined,
433 .stack_protector = undefined,
434 .red_zone = undefined,
435 .sanitize_c = undefined,
436 .sanitize_thread = undefined,
437 .fuzz = undefined,
438 .unwind_tables = undefined,
439 .cc_argv = undefined,
440 .structured_cfg = undefined,
441 .no_builtin = undefined,
442 };
443 return mod;
444}
445
446/// Does not ensure that the module's root directory exists on-disk; see `Builtin.updateFileOnDisk` for that task.
447pub fn createBuiltin(arena: Allocator, opts: Builtin, dirs: Compilation.Directories) Allocator.Error!*Module {
448 const sub_path = "b" ++ std.fs.path.sep_str ++ Cache.binToHex(opts.hash());
449 const new = try arena.create(Module);
450 new.* = .{
451 .root = try .fromRoot(arena, dirs, .global_cache, sub_path),
452 .root_src_path = "builtin.zig",
453 .fully_qualified_name = "builtin",
454 .resolved_target = .{
455 .result = opts.target,
456 // These values are not in `opts`, but do not matter because `builtin.zig` contains no runtime code.
457 .is_native_os = false,
458 .is_native_abi = false,
459 .is_explicit_dynamic_linker = false,
460 .llvm_cpu_features = null,
461 },
462 .optimize_mode = opts.optimize_mode,
463 .single_threaded = opts.single_threaded,
464 .error_tracing = opts.error_tracing,
465 .valgrind = opts.valgrind,
466 .pic = opts.pic,
467 .strip = opts.strip,
468 .omit_frame_pointer = opts.omit_frame_pointer,
469 .code_model = opts.code_model,
470 .sanitize_thread = opts.sanitize_thread,
471 .fuzz = opts.fuzz,
472 .unwind_tables = opts.unwind_tables,
473 .cc_argv = &.{},
474 // These values are not in `opts`, but do not matter because `builtin.zig` contains no runtime code.
475 .stack_check = false,
476 .stack_protector = 0,
477 .red_zone = false,
478 .sanitize_c = .off,
479 .structured_cfg = false,
480 .no_builtin = false,
481 };
482 return new;
483}
484
485/// Returns the `Builtin` which forms the contents of `@import("builtin")` for this module.
486pub fn getBuiltinOptions(m: Module, global: Compilation.Config) Builtin {
487 assert(global.have_zcu);
488 return .{
489 .target = m.resolved_target.result,
490 .zig_backend = target_util.zigBackend(&m.resolved_target.result, global.use_llvm),
491 .output_mode = global.output_mode,
492 .link_mode = global.link_mode,
493 .unwind_tables = m.unwind_tables,
494 .is_test = global.is_test,
495 .single_threaded = m.single_threaded,
496 .link_libc = global.link_libc,
497 .link_libcpp = global.link_libcpp,
498 .optimize_mode = m.optimize_mode,
499 .error_tracing = m.error_tracing,
500 .valgrind = m.valgrind,
501 .sanitize_thread = m.sanitize_thread,
502 .fuzz = m.fuzz,
503 .pic = m.pic,
504 .pie = global.pie,
505 .strip = m.strip,
506 .code_model = m.code_model,
507 .omit_frame_pointer = m.omit_frame_pointer,
508 .wasi_exec_model = global.wasi_exec_model,
509 };
510}
511
512const Module = @This();
513const Package = @import("../Package.zig");
514const std = @import("std");
515const Allocator = std.mem.Allocator;
516const MultiHashHexDigest = Package.Manifest.MultiHashHexDigest;
517const target_util = @import("../target.zig");
518const Cache = std.Build.Cache;
519const Builtin = @import("../Builtin.zig");
520const assert = std.debug.assert;
521const Compilation = @import("../Compilation.zig");
522const File = @import("../Zcu.zig").File;