Add support for WASI reactor in pure Zig-exe. (#9178)

* Add command line help for "-mexec-model"
* Define WasmExecModel enum in std.builtin.
* Drop the support for the old crt1.o in favor of crt1-command.o

Signed-off-by: Takeshi Yoneda <takeshi@tetrate.io>
This commit is contained in:
Takeshi Yoneda 2021-07-01 09:02:48 +09:00 committed by GitHub
parent a95ba0d10d
commit bc7761d8e0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 66 additions and 28 deletions

View File

@ -457,6 +457,13 @@ pub const LinkMode = enum {
Dynamic,
};
/// This data structure is used by the Zig language code generation and
/// therefore must be kept in sync with the compiler implementation.
pub const WasiExecModel = enum {
command,
reactor,
};
/// This data structure is used by the Zig language code generation and
/// therefore must be kept in sync with the compiler implementation.
pub const Version = struct {

View File

@ -65,8 +65,9 @@ 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()) {
const wasm_start_sym = if (builtin.wasi_exec_model == .reactor) "_initialize" else "_start";
if (!@hasDecl(root, wasm_start_sym)) @export(wasm_start, .{ .name = wasm_start_sym });
} else if (native_os != .other and native_os != .freestanding) {
if (!@hasDecl(root, start_sym_name)) @export(_start, .{ .name = start_sym_name });
}
@ -135,10 +136,21 @@ 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 => {
switch (builtin.wasi_exec_model) {
.reactor => _ = @call(.{ .modifier = .always_inline }, callMain, .{}),
.command => 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 {
@ -164,12 +176,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 (

View File

@ -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: ?std.builtin.WasiExecModel = null,
};
fn addPackageTableToCacheHash(
@ -790,6 +790,9 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
const needs_c_symbols = !options.skip_linker_dependencies and is_exe_or_dyn_lib;
// WASI-only. Resolve the optinal exec-model option, defaults to command.
const wasi_exec_model = if (options.target.os.tag != .wasi) undefined else options.wasi_exec_model orelse .command;
const comp: *Compilation = comp: {
// For allocations that have the same lifetime as Compilation. This arena is used only during this
// initialization and then is freed in deinit().
@ -1340,7 +1343,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
.disable_lld_caching = options.disable_lld_caching,
.subsystem = options.subsystem,
.is_test = options.is_test,
.wasi_exec_model = options.wasi_exec_model,
.wasi_exec_model = wasi_exec_model,
});
errdefer bin_file.destroy();
comp.* = .{
@ -1442,7 +1445,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.execModelCrtFile(wasi_exec_model) },
.{ .wasi_libc_crt_file = .libc_a },
});
}
@ -3650,6 +3653,14 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) Alloc
std.zig.fmtId(@tagName(comp.bin_file.options.machine_code_model)),
});
if (target.os.tag == .wasi) {
const wasi_exec_model_fmt = std.zig.fmtId(@tagName(comp.bin_file.options.wasi_exec_model));
try buffer.writer().print(
\\pub const wasi_exec_model = std.builtin.WasiExecModel.{};
\\
, .{wasi_exec_model_fmt});
}
if (comp.bin_file.options.is_test) {
try buffer.appendSlice(
\\pub var test_functions: []std.builtin.TestFn = undefined; // overwritten later

View File

@ -116,7 +116,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: std.builtin.WasiExecModel = undefined,
pub fn effectiveOutputMode(options: Options) std.builtin.OutputMode {
return if (options.use_lld) .Obj else options.output_mode;

View File

@ -687,10 +687,15 @@ 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;
if (self.base.options.wasi_exec_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.
@ -717,7 +722,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"));
}

View File

@ -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
@ -618,7 +619,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: ?std.builtin.WasiExecModel = null;
var system_libs = std.ArrayList([]const u8).init(gpa);
defer system_libs.deinit();
@ -1071,6 +1072,10 @@ fn buildOutputType(
mem.startsWith(u8, arg, "-I"))
{
try clang_argv.append(arg);
} else if (mem.startsWith(u8, arg, "-mexec-model=")) {
wasi_exec_model = std.meta.stringToEnum(std.builtin.WasiExecModel, arg["-mexec-model=".len..]) orelse {
fatal("expected [command|reactor] for -mexec-mode=[value], found '{s}'", .{arg["-mexec-model=".len..]});
};
} else {
fatal("unrecognized parameter: '{s}'", .{arg});
}
@ -1277,11 +1282,9 @@ fn buildOutputType(
.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;
}
wasi_exec_model = std.meta.stringToEnum(std.builtin.WasiExecModel, it.only_arg) orelse {
fatal("expected [command|reactor] for -mexec-mode=[value], found '{s}'", .{it.only_arg});
};
},
}
}

View File

@ -45,9 +45,15 @@ pub fn emulatedLibCRFileLibName(crt_file: CRTFile) []const u8 {
};
}
pub fn crtFileFullName(crt_file: CRTFile) []const u8 {
return switch (crt_file) {
.crt1_o => "crt1.o",
pub fn execModelCrtFile(wasi_exec_model: std.builtin.WasiExecModel) CRTFile {
return switch (wasi_exec_model) {
.reactor => CRTFile.crt1_reactor_o,
.command => CRTFile.crt1_command_o,
};
}
pub fn execModelCrtFileFullName(wasi_exec_model: std.builtin.WasiExecModel) []const u8 {
return switch (execModelCrtFile(wasi_exec_model)) {
.crt1_reactor_o => "crt1-reactor.o",
.crt1_command_o => "crt1-command.o",
else => unreachable,