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