Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 18 additions & 12 deletions lib/std/start.zig
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const native_os = builtin.os.tag;

var argc_argv_ptr: [*]usize = undefined;

const start_sym_name = if (native_arch.isMIPS()) "__start" else "_start";
const start_sym_name = if (native_arch.isMIPS()) "__start" else if (builtin.is_wasi_reactor_exec_model) "_initialize" else "_start";

comptime {
// No matter what, we import the root file, so that any export, test, comptime
Expand Down Expand Up @@ -64,8 +64,8 @@ comptime {
}
} else if (native_os == .uefi) {
if (!@hasDecl(root, "EfiMain")) @export(EfiMain, .{ .name = "EfiMain" });
} else if (native_arch.isWasm() and native_os == .freestanding) {
if (!@hasDecl(root, start_sym_name)) @export(wasm_freestanding_start, .{ .name = start_sym_name });
} else if (native_arch.isWasm()) {
if (!@hasDecl(root, start_sym_name)) @export(wasm_start, .{ .name = start_sym_name });
} else if (native_os != .other and native_os != .freestanding) {
if (!@hasDecl(root, start_sym_name)) @export(_start, .{ .name = start_sym_name });
}
Expand Down Expand Up @@ -134,10 +134,22 @@ fn _DllMainCRTStartup(
return std.os.windows.TRUE;
}

fn wasm_freestanding_start() callconv(.C) void {
// This is marked inline because for some reason LLVM in release mode fails to inline it,
fn wasm_start() callconv(.C) void {
// The entrypoint is marked inline because for some reason LLVM in release mode fails to inline it,
// and we want fewer call frames in stack traces.
_ = @call(.{ .modifier = .always_inline }, callMain, .{});
switch (native_os) {
.freestanding => {
_ = @call(.{ .modifier = .always_inline }, callMain, .{});
},
.wasi => {
if (builtin.is_wasi_reactor_exec_model) {
_ = @call(.{ .modifier = .always_inline }, callMain, .{});
} else {
std.os.wasi.proc_exit(@call(.{ .modifier = .always_inline }, callMain, .{}));
}
},
else => @compileError("unsupported OS"),
}
}

fn EfiMain(handle: uefi.Handle, system_table: *uefi.tables.SystemTable) callconv(.C) usize {
Expand All @@ -163,12 +175,6 @@ fn EfiMain(handle: uefi.Handle, system_table: *uefi.tables.SystemTable) callconv
}

fn _start() callconv(.Naked) noreturn {
if (native_os == .wasi) {
// This is marked inline because for some reason LLVM in release mode fails to inline it,
// and we want fewer call frames in stack traces.
std.os.wasi.proc_exit(@call(.{ .modifier = .always_inline }, callMain, .{}));
}

switch (native_arch) {
.x86_64 => {
argc_argv_ptr = asm volatile (
Expand Down
9 changes: 7 additions & 2 deletions src/Compilation.zig
Original file line number Diff line number Diff line change
Expand Up @@ -724,7 +724,7 @@ pub const InitOptions = struct {
test_name_prefix: ?[]const u8 = null,
subsystem: ?std.Target.SubSystem = null,
/// WASI-only. Type of WASI execution model ("command" or "reactor").
wasi_exec_model: ?wasi_libc.CRTFile = null,
wasi_exec_model: ?[]const u8 = null,
};

fn addPackageTableToCacheHash(
Expand Down Expand Up @@ -1446,7 +1446,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
});
}
comp.work_queue.writeAssumeCapacity(&[_]Job{
.{ .wasi_libc_crt_file = comp.bin_file.options.wasi_exec_model orelse .crt1_o },
.{ .wasi_libc_crt_file = wasi_libc.getExecModelCRTFIle(options.wasi_exec_model) },
.{ .wasi_libc_crt_file = .libc_a },
});
}
Expand Down Expand Up @@ -3638,6 +3638,11 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) Alloc
std.zig.fmtId(@tagName(comp.bin_file.options.machine_code_model)),
});

try buffer.writer().print(
\\pub const is_wasi_reactor_exec_model = {};
\\
, .{if (comp.bin_file.options.wasi_exec_model) |model| std.mem.eql(u8, model, "reactor") else false});

if (comp.bin_file.options.is_test) {
try buffer.appendSlice(
\\pub var test_functions: []std.builtin.TestFn = undefined; // overwritten later
Expand Down
2 changes: 1 addition & 1 deletion src/link.zig
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ pub const Options = struct {
libc_installation: ?*const LibCInstallation,

/// WASI-only. Type of WASI execution model ("command" or "reactor").
wasi_exec_model: ?wasi_libc.CRTFile = null,
wasi_exec_model: ?[]const u8,

pub fn effectiveOutputMode(options: Options) std.builtin.OutputMode {
return if (options.use_lld) .Obj else options.output_mode;
Expand Down
17 changes: 12 additions & 5 deletions src/link/Wasm.zig
Original file line number Diff line number Diff line change
Expand Up @@ -682,10 +682,17 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {
// before corrupting globals. See https://github.com/ziglang/zig/issues/4496
try argv.append("--stack-first");

// Reactor execution model does not have _start so lld doesn't look for it.
if (self.base.options.wasi_exec_model) |exec_model| blk: {
if (exec_model != .crt1_reactor_o) break :blk;
try argv.append("--no-entry");
if (self.base.options.wasi_exec_model) |model| {
if (std.mem.eql(u8, model, "reactor")) {
// Reactor execution model does not have _start so lld doesn't look for it.
try argv.append("--no-entry");
// Make sure "_initialize" is exported even if this is pure Zig WASI reactor
// where WASM_SYMBOL_EXPORTED flag in LLVM is not set on _initialize.
try argv.appendSlice(&[_][]const u8{
"--export",
"_initialize",
});
}
}
} else {
try argv.append("--no-entry"); // So lld doesn't look for _start.
Expand All @@ -712,7 +719,7 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {
if (self.base.options.link_libc) {
try argv.append(try comp.get_libc_crt_file(
arena,
wasi_libc.crtFileFullName(self.base.options.wasi_exec_model orelse .crt1_o),
wasi_libc.execModelCRTFileFullName(self.base.options.wasi_exec_model),
));
try argv.append(try comp.get_libc_crt_file(arena, "libc.a"));
}
Expand Down
13 changes: 5 additions & 8 deletions src/main.zig
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,7 @@ const usage_build_generic =
\\ medium|large]
\\ -mred-zone Force-enable the "red-zone"
\\ -mno-red-zone Force-disable the "red-zone"
\\ -mexec-model=[value] Execution model (WASI only)
\\ --name [name] Override root name (not a file path)
\\ -O [mode] Choose what to optimize for
\\ Debug (default) Optimizations off, safety on
Expand Down Expand Up @@ -616,7 +617,7 @@ fn buildOutputType(
var subsystem: ?std.Target.SubSystem = null;
var major_subsystem_version: ?u32 = null;
var minor_subsystem_version: ?u32 = null;
var wasi_exec_model: ?wasi_libc.CRTFile = null;
var wasi_exec_model: ?[]const u8 = null;

var system_libs = std.ArrayList([]const u8).init(gpa);
defer system_libs.deinit();
Expand Down Expand Up @@ -1065,6 +1066,8 @@ fn buildOutputType(
mem.startsWith(u8, arg, "-I"))
{
try clang_argv.append(arg);
} else if (mem.startsWith(u8, arg, "-mexec-model=")) {
wasi_exec_model = arg["-mexec-model=".len..];
} else {
fatal("unrecognized parameter: '{s}'", .{arg});
}
Expand Down Expand Up @@ -1270,13 +1273,7 @@ fn buildOutputType(
.framework => try frameworks.append(it.only_arg),
.nostdlibinc => want_native_include_dirs = false,
.strip => strip = true,
.exec_model => {
if (std.mem.eql(u8, it.only_arg, "reactor")) {
wasi_exec_model = .crt1_reactor_o;
} else if (std.mem.eql(u8, it.only_arg, "command")) {
wasi_exec_model = .crt1_command_o;
}
},
.exec_model => wasi_exec_model = it.only_arg,
}
}
// Parse linker args.
Expand Down
16 changes: 14 additions & 2 deletions src/wasi_libc.zig
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,20 @@ pub fn emulatedLibCRFileLibName(crt_file: CRTFile) []const u8 {
};
}

pub fn crtFileFullName(crt_file: CRTFile) []const u8 {
return switch (crt_file) {
pub fn getExecModelCRTFIle(wasi_exec_model: ?[]const u8) CRTFile {
return if (wasi_exec_model) |model|
if (std.mem.eql(u8, model, "reactor"))
CRTFile.crt1_reactor_o
else if (std.mem.eql(u8, model, "command"))
CRTFile.crt1_command_o
else
unreachable
else
CRTFile.crt1_o;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on my research it looks like every WASI program is either a command or a reactor. Is it meaningful for a program to be neither? Why is crt1_o even an option?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah we should remove crt1_o in favor of crt1_command_o, where the latter lets wasm-ld call ctors rather than explicitly calling in crt.c. Also I believe this reduces the complexity here about null vs default stuff.

so I removed the support for crt1_o. cc @kubkon wdyt?

}

pub fn execModelCRTFileFullName(wasi_exec_model: ?[]const u8) []const u8 {
return switch (getExecModelCRTFIle(wasi_exec_model)) {
.crt1_o => "crt1.o",
.crt1_reactor_o => "crt1-reactor.o",
.crt1_command_o => "crt1-command.o",
Expand Down