Skip to content

Commit

Permalink
WIP: move many global settings to become per-Module
Browse files Browse the repository at this point in the history
Much of the logic from Compilation.create() is extracted into
Compilation.Config.resolve() which accepts many optional settings and
produces concrete settings. This separate step is needed by API users of
Compilation so that they can pass the resolved global settings to the
Module creation function, which itself needs to resolve per-Module
settings.

Since the target and other things are no longer global settings, I did
not want them stored in link.File (in the `options` field). That options
field was already a kludge; those options should be resolved into
concrete settings. This commit also starts to work on that, deleting
link.Options, moving the fields into Compilation and
ObjectFormat-specific structs instead. Some fields were ephemeral and
should not have been stored at all, such as symbol_size_hint.

The link.File object of Compilation is now a `?*link.File` and `null`
when -fno-emit-bin is passed. It is now arena-allocated along with
Compilation itself, avoiding some messy cleanup code that was there
before.

On the command line, it is now possible to configure the standard
library itself by using `--mod std` just like any other module. This
meant that the CLI needed to create the standard library module rather
than having Compilation create it.

There are a lot of changes in this commit and it's still not done. I
didn't realize how quickly this changeset was going to balloon out of
control, and there are still many lines that need to be changed before
it even compiles successfully.

* introduce std.Build.Cache.HashHelper.oneShot
* add error_tracing to std.Build.Module
* extract build.zig file generation into src/Builtin.zig
* each CSourceFile and RcSourceFile now has a Module owner, which
  determines some of the C compiler flags.
  • Loading branch information
andrewrk committed Dec 10, 2023
1 parent 25fe8c7 commit e785d4f
Show file tree
Hide file tree
Showing 16 changed files with 3,250 additions and 2,711 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,7 @@ set(ZIG_STAGE2_SOURCES
"${CMAKE_SOURCE_DIR}/src/Air.zig"
"${CMAKE_SOURCE_DIR}/src/AstGen.zig"
"${CMAKE_SOURCE_DIR}/src/Compilation.zig"
"${CMAKE_SOURCE_DIR}/src/Compilation/Config.zig"
"${CMAKE_SOURCE_DIR}/src/Liveness.zig"
"${CMAKE_SOURCE_DIR}/src/Module.zig"
"${CMAKE_SOURCE_DIR}/src/Package.zig"
Expand Down
14 changes: 14 additions & 0 deletions lib/std/Build/Cache.zig
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,20 @@ pub const HashHelper = struct {
) catch unreachable;
return out_digest;
}

pub fn oneShot(bytes: []const u8) [hex_digest_len]u8 {
var hasher: Hasher = hasher_init;
hasher.update(bytes);
var bin_digest: BinDigest = undefined;
hasher.final(&bin_digest);
var out_digest: [hex_digest_len]u8 = undefined;
_ = fmt.bufPrint(
&out_digest,
"{s}",
.{fmt.fmtSliceHexLower(&bin_digest)},
) catch unreachable;
return out_digest;
}
};

pub const Lock = struct {
Expand Down
4 changes: 4 additions & 0 deletions lib/std/Build/Module.zig
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ valgrind: ?bool,
pic: ?bool,
red_zone: ?bool,
omit_frame_pointer: ?bool,
error_tracing: ?bool,
link_libc: ?bool,
link_libcpp: ?bool,

Expand Down Expand Up @@ -177,6 +178,7 @@ pub const CreateOptions = struct {
/// Whether to omit the stack frame pointer. Frees up a register and makes it
/// more difficult to obtain stack traces. Has target-dependent effects.
omit_frame_pointer: ?bool = null,
error_tracing: ?bool = null,
};

pub const Import = struct {
Expand Down Expand Up @@ -216,6 +218,7 @@ pub fn init(m: *Module, owner: *std.Build, options: CreateOptions, compile: ?*St
.pic = options.pic,
.red_zone = options.red_zone,
.omit_frame_pointer = options.omit_frame_pointer,
.error_tracing = options.error_tracing,
.export_symbol_names = &.{},
};

Expand Down Expand Up @@ -601,6 +604,7 @@ pub fn appendZigProcessFlags(
try addFlag(zig_args, m.stack_check, "-fstack-check", "-fno-stack-check");
try addFlag(zig_args, m.stack_protector, "-fstack-protector", "-fno-stack-protector");
try addFlag(zig_args, m.omit_frame_pointer, "-fomit-frame-pointer", "-fno-omit-frame-pointer");
try addFlag(zig_args, m.error_tracing, "-ferror-tracing", "-fno-error-tracing");
try addFlag(zig_args, m.sanitize_c, "-fsanitize-c", "-fno-sanitize-c");
try addFlag(zig_args, m.sanitize_thread, "-fsanitize-thread", "-fno-sanitize-thread");
try addFlag(zig_args, m.valgrind, "-fvalgrind", "-fno-valgrind");
Expand Down
240 changes: 240 additions & 0 deletions src/Builtin.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
target: std.Target,
zig_backend: std.builtin.CompilerBackend,
output_mode: std.builtin.OutputMode,
link_mode: std.builtin.LinkMode,
is_test: bool,
test_evented_io: bool,
single_threaded: bool,
link_libc: bool,
link_libcpp: bool,
optimize_mode: std.builtin.OptimizeMode,
error_tracing: bool,
valgrind: bool,
sanitize_thread: bool,
pic: bool,
pie: bool,
strip: bool,
code_model: std.builtin.CodeModel,
omit_frame_pointer: bool,
wasi_exec_model: std.builtin.WasiExecModel,

pub fn generate(opts: @This(), allocator: Allocator) Allocator.Error![:0]u8 {
var buffer = std.ArrayList(u8).init(allocator);
defer buffer.deinit();

const target = opts.target;
const generic_arch_name = target.cpu.arch.genericName();
const zig_backend = opts.zig_backend;

@setEvalBranchQuota(4000);
try buffer.writer().print(
\\const std = @import("std");
\\/// Zig version. When writing code that supports multiple versions of Zig, prefer
\\/// feature detection (i.e. with `@hasDecl` or `@hasField`) over version checks.
\\pub const zig_version = std.SemanticVersion.parse(zig_version_string) catch unreachable;
\\pub const zig_version_string = "{s}";
\\pub const zig_backend = std.builtin.CompilerBackend.{};
\\
\\pub const output_mode = std.builtin.OutputMode.{};
\\pub const link_mode = std.builtin.LinkMode.{};
\\pub const is_test = {};
\\pub const single_threaded = {};
\\pub const abi = std.Target.Abi.{};
\\pub const cpu: std.Target.Cpu = .{{
\\ .arch = .{},
\\ .model = &std.Target.{}.cpu.{},
\\ .features = std.Target.{}.featureSet(&[_]std.Target.{}.Feature{{
\\
, .{
build_options.version,
std.zig.fmtId(@tagName(zig_backend)),
std.zig.fmtId(@tagName(opts.output_mode)),
std.zig.fmtId(@tagName(opts.link_mode)),
opts.is_test,
opts.single_threaded,
std.zig.fmtId(@tagName(target.abi)),
std.zig.fmtId(@tagName(target.cpu.arch)),
std.zig.fmtId(generic_arch_name),
std.zig.fmtId(target.cpu.model.name),
std.zig.fmtId(generic_arch_name),
std.zig.fmtId(generic_arch_name),
});

for (target.cpu.arch.allFeaturesList(), 0..) |feature, index_usize| {
const index = @as(std.Target.Cpu.Feature.Set.Index, @intCast(index_usize));
const is_enabled = target.cpu.features.isEnabled(index);
if (is_enabled) {
try buffer.writer().print(" .{},\n", .{std.zig.fmtId(feature.name)});
}
}
try buffer.writer().print(
\\ }}),
\\}};
\\pub const os = std.Target.Os{{
\\ .tag = .{},
\\ .version_range = .{{
,
.{std.zig.fmtId(@tagName(target.os.tag))},
);

switch (target.os.getVersionRange()) {
.none => try buffer.appendSlice(" .none = {} },\n"),
.semver => |semver| try buffer.writer().print(
\\ .semver = .{{
\\ .min = .{{
\\ .major = {},
\\ .minor = {},
\\ .patch = {},
\\ }},
\\ .max = .{{
\\ .major = {},
\\ .minor = {},
\\ .patch = {},
\\ }},
\\ }}}},
\\
, .{
semver.min.major,
semver.min.minor,
semver.min.patch,

semver.max.major,
semver.max.minor,
semver.max.patch,
}),
.linux => |linux| try buffer.writer().print(
\\ .linux = .{{
\\ .range = .{{
\\ .min = .{{
\\ .major = {},
\\ .minor = {},
\\ .patch = {},
\\ }},
\\ .max = .{{
\\ .major = {},
\\ .minor = {},
\\ .patch = {},
\\ }},
\\ }},
\\ .glibc = .{{
\\ .major = {},
\\ .minor = {},
\\ .patch = {},
\\ }},
\\ }}}},
\\
, .{
linux.range.min.major,
linux.range.min.minor,
linux.range.min.patch,

linux.range.max.major,
linux.range.max.minor,
linux.range.max.patch,

linux.glibc.major,
linux.glibc.minor,
linux.glibc.patch,
}),
.windows => |windows| try buffer.writer().print(
\\ .windows = .{{
\\ .min = {s},
\\ .max = {s},
\\ }}}},
\\
,
.{ windows.min, windows.max },
),
}
try buffer.appendSlice(
\\};
\\pub const target: std.Target = .{
\\ .cpu = cpu,
\\ .os = os,
\\ .abi = abi,
\\ .ofmt = object_format,
\\
);

if (target.dynamic_linker.get()) |dl| {
try buffer.writer().print(
\\ .dynamic_linker = std.Target.DynamicLinker.init("{s}"),
\\}};
\\
, .{dl});
} else {
try buffer.appendSlice(
\\ .dynamic_linker = std.Target.DynamicLinker.none,
\\};
\\
);
}

// This is so that compiler_rt and libc.zig libraries know whether they
// will eventually be linked with libc. They make different decisions
// about what to export depending on whether another libc will be linked
// in. For example, compiler_rt will not export the __chkstk symbol if it
// knows libc will provide it, and likewise c.zig will not export memcpy.
const link_libc = opts.link_libc;

try buffer.writer().print(
\\pub const object_format = std.Target.ObjectFormat.{};
\\pub const mode = std.builtin.OptimizeMode.{};
\\pub const link_libc = {};
\\pub const link_libcpp = {};
\\pub const have_error_return_tracing = {};
\\pub const valgrind_support = {};
\\pub const sanitize_thread = {};
\\pub const position_independent_code = {};
\\pub const position_independent_executable = {};
\\pub const strip_debug_info = {};
\\pub const code_model = std.builtin.CodeModel.{};
\\pub const omit_frame_pointer = {};
\\
, .{
std.zig.fmtId(@tagName(target.ofmt)),
std.zig.fmtId(@tagName(opts.optimize_mode)),
link_libc,
opts.link_libcpp,
opts.error_tracing,
opts.valgrind,
opts.sanitize_thread,
opts.pic,
opts.pie,
opts.strip,
std.zig.fmtId(@tagName(opts.code_model)),
opts.omit_frame_pointer,
});

if (target.os.tag == .wasi) {
const wasi_exec_model_fmt = std.zig.fmtId(@tagName(opts.wasi_exec_model));
try buffer.writer().print(
\\pub const wasi_exec_model = std.builtin.WasiExecModel.{};
\\
, .{wasi_exec_model_fmt});
}

if (opts.is_test) {
try buffer.appendSlice(
\\pub var test_functions: []const std.builtin.TestFn = undefined; // overwritten later
\\
);
if (opts.test_evented_io) {
try buffer.appendSlice(
\\pub const test_io_mode = .evented;
\\
);
} else {
try buffer.appendSlice(
\\pub const test_io_mode = .blocking;
\\
);
}
}

return buffer.toOwnedSliceSentinel(0);
}

const std = @import("std");
const Allocator = std.mem.Allocator;
const build_options = @import("build_options");
Loading

0 comments on commit e785d4f

Please sign in to comment.