authorgravatar for takeshi@tetrate.ioTakeshi Yoneda <takeshi@tetrate.io> 2021-07-01 09:02:48+09:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2021-06-30 20:02:48-04:00
logbc7761d8e0bb70adb3c76c588f9f0b62937c6571
tree571b5b93cd6611d458cb728198b9796cff9da3ac
parenta95ba0d10d582020dbd3e6efded53f17287ed211
signature Signed by PGP key 4AEE18F83AFDEB23

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>

7 files changed, 66 insertions(+), 28 deletions(-)

lib/std/builtin.zig+7
......@@ -457,6 +457,13 @@ pub const LinkMode = enum {
457457 Dynamic,
458458};
459459
460/// This data structure is used by the Zig language code generation and
461/// therefore must be kept in sync with the compiler implementation.
462pub const WasiExecModel = enum {
463 command,
464 reactor,
465};
466
460467/// This data structure is used by the Zig language code generation and
461468/// therefore must be kept in sync with the compiler implementation.
462469pub const Version = struct {
lib/std/start.zig+17-11
......@@ -65,8 +65,9 @@ comptime {
6565 }
6666 } else if (native_os == .uefi) {
6767 if (!@hasDecl(root, "EfiMain")) @export(EfiMain, .{ .name = "EfiMain" });
68 } else if (native_arch.isWasm() and native_os == .freestanding) {
69 if (!@hasDecl(root, start_sym_name)) @export(wasm_freestanding_start, .{ .name = start_sym_name });
68 } else if (native_arch.isWasm()) {
69 const wasm_start_sym = if (builtin.wasi_exec_model == .reactor) "_initialize" else "_start";
70 if (!@hasDecl(root, wasm_start_sym)) @export(wasm_start, .{ .name = wasm_start_sym });
7071 } else if (native_os != .other and native_os != .freestanding) {
7172 if (!@hasDecl(root, start_sym_name)) @export(_start, .{ .name = start_sym_name });
7273 }
......@@ -135,10 +136,21 @@ fn _DllMainCRTStartup(
135136 return std.os.windows.TRUE;
136137}
137138
138fn wasm_freestanding_start() callconv(.C) void {
139 // This is marked inline because for some reason LLVM in release mode fails to inline it,
139fn wasm_start() callconv(.C) void {
140 // The entrypoint is marked inline because for some reason LLVM in release mode fails to inline it,
140141 // and we want fewer call frames in stack traces.
141 _ = @call(.{ .modifier = .always_inline }, callMain, .{});
142 switch (native_os) {
143 .freestanding => {
144 _ = @call(.{ .modifier = .always_inline }, callMain, .{});
145 },
146 .wasi => {
147 switch (builtin.wasi_exec_model) {
148 .reactor => _ = @call(.{ .modifier = .always_inline }, callMain, .{}),
149 .command => std.os.wasi.proc_exit(@call(.{ .modifier = .always_inline }, callMain, .{})),
150 }
151 },
152 else => @compileError("unsupported OS"),
153 }
142154}
143155
144156fn 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
164176}
165177
166178fn _start() callconv(.Naked) noreturn {
167 if (native_os == .wasi) {
168 // This is marked inline because for some reason LLVM in release mode fails to inline it,
169 // and we want fewer call frames in stack traces.
170 std.os.wasi.proc_exit(@call(.{ .modifier = .always_inline }, callMain, .{}));
171 }
172
173179 switch (native_arch) {
174180 .x86_64 => {
175181 argc_argv_ptr = asm volatile (
src/Compilation.zig+14-3
......@@ -724,7 +724,7 @@ pub const InitOptions = struct {
724724 test_name_prefix: ?[]const u8 = null,
725725 subsystem: ?std.Target.SubSystem = null,
726726 /// WASI-only. Type of WASI execution model ("command" or "reactor").
727 wasi_exec_model: ?wasi_libc.CRTFile = null,
727 wasi_exec_model: ?std.builtin.WasiExecModel = null,
728728};
729729
730730fn addPackageTableToCacheHash(
......@@ -790,6 +790,9 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
790790
791791 const needs_c_symbols = !options.skip_linker_dependencies and is_exe_or_dyn_lib;
792792
793 // WASI-only. Resolve the optinal exec-model option, defaults to command.
794 const wasi_exec_model = if (options.target.os.tag != .wasi) undefined else options.wasi_exec_model orelse .command;
795
793796 const comp: *Compilation = comp: {
794797 // For allocations that have the same lifetime as Compilation. This arena is used only during this
795798 // initialization and then is freed in deinit().
......@@ -1340,7 +1343,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
13401343 .disable_lld_caching = options.disable_lld_caching,
13411344 .subsystem = options.subsystem,
13421345 .is_test = options.is_test,
1343 .wasi_exec_model = options.wasi_exec_model,
1346 .wasi_exec_model = wasi_exec_model,
13441347 });
13451348 errdefer bin_file.destroy();
13461349 comp.* = .{
......@@ -1442,7 +1445,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
14421445 });
14431446 }
14441447 comp.work_queue.writeAssumeCapacity(&[_]Job{
1445 .{ .wasi_libc_crt_file = comp.bin_file.options.wasi_exec_model orelse .crt1_o },
1448 .{ .wasi_libc_crt_file = wasi_libc.execModelCrtFile(wasi_exec_model) },
14461449 .{ .wasi_libc_crt_file = .libc_a },
14471450 });
14481451 }
......@@ -3650,6 +3653,14 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) Alloc
36503653 std.zig.fmtId(@tagName(comp.bin_file.options.machine_code_model)),
36513654 });
36523655
3656 if (target.os.tag == .wasi) {
3657 const wasi_exec_model_fmt = std.zig.fmtId(@tagName(comp.bin_file.options.wasi_exec_model));
3658 try buffer.writer().print(
3659 \\pub const wasi_exec_model = std.builtin.WasiExecModel.{};
3660 \\
3661 , .{wasi_exec_model_fmt});
3662 }
3663
36533664 if (comp.bin_file.options.is_test) {
36543665 try buffer.appendSlice(
36553666 \\pub var test_functions: []std.builtin.TestFn = undefined; // overwritten later
src/link.zig+1-1
......@@ -116,7 +116,7 @@ pub const Options = struct {
116116 libc_installation: ?*const LibCInstallation,
117117
118118 /// WASI-only. Type of WASI execution model ("command" or "reactor").
119 wasi_exec_model: ?wasi_libc.CRTFile = null,
119 wasi_exec_model: std.builtin.WasiExecModel = undefined,
120120
121121 pub fn effectiveOutputMode(options: Options) std.builtin.OutputMode {
122122 return if (options.use_lld) .Obj else options.output_mode;
src/link/Wasm.zig+9-4
......@@ -687,10 +687,15 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {
687687 // before corrupting globals. See https://github.com/ziglang/zig/issues/4496
688688 try argv.append("--stack-first");
689689
690 // Reactor execution model does not have _start so lld doesn't look for it.
691 if (self.base.options.wasi_exec_model) |exec_model| blk: {
692 if (exec_model != .crt1_reactor_o) break :blk;
690 if (self.base.options.wasi_exec_model == .reactor) {
691 // Reactor execution model does not have _start so lld doesn't look for it.
693692 try argv.append("--no-entry");
693 // Make sure "_initialize" is exported even if this is pure Zig WASI reactor
694 // where WASM_SYMBOL_EXPORTED flag in LLVM is not set on _initialize.
695 try argv.appendSlice(&[_][]const u8{
696 "--export",
697 "_initialize",
698 });
694699 }
695700 } else {
696701 try argv.append("--no-entry"); // So lld doesn't look for _start.
......@@ -717,7 +722,7 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {
717722 if (self.base.options.link_libc) {
718723 try argv.append(try comp.get_libc_crt_file(
719724 arena,
720 wasi_libc.crtFileFullName(self.base.options.wasi_exec_model orelse .crt1_o),
725 wasi_libc.execModelCrtFileFullName(self.base.options.wasi_exec_model),
721726 ));
722727 try argv.append(try comp.get_libc_crt_file(arena, "libc.a"));
723728 }
src/main.zig+9-6
......@@ -321,6 +321,7 @@ const usage_build_generic =
321321 \\ medium|large]
322322 \\ -mred-zone Force-enable the "red-zone"
323323 \\ -mno-red-zone Force-disable the "red-zone"
324 \\ -mexec-model=[value] Execution model (WASI only)
324325 \\ --name [name] Override root name (not a file path)
325326 \\ -O [mode] Choose what to optimize for
326327 \\ Debug (default) Optimizations off, safety on
......@@ -618,7 +619,7 @@ fn buildOutputType(
618619 var subsystem: ?std.Target.SubSystem = null;
619620 var major_subsystem_version: ?u32 = null;
620621 var minor_subsystem_version: ?u32 = null;
621 var wasi_exec_model: ?wasi_libc.CRTFile = null;
622 var wasi_exec_model: ?std.builtin.WasiExecModel = null;
622623
623624 var system_libs = std.ArrayList([]const u8).init(gpa);
624625 defer system_libs.deinit();
......@@ -1071,6 +1072,10 @@ fn buildOutputType(
10711072 mem.startsWith(u8, arg, "-I"))
10721073 {
10731074 try clang_argv.append(arg);
1075 } else if (mem.startsWith(u8, arg, "-mexec-model=")) {
1076 wasi_exec_model = std.meta.stringToEnum(std.builtin.WasiExecModel, arg["-mexec-model=".len..]) orelse {
1077 fatal("expected [command|reactor] for -mexec-mode=[value], found '{s}'", .{arg["-mexec-model=".len..]});
1078 };
10741079 } else {
10751080 fatal("unrecognized parameter: '{s}'", .{arg});
10761081 }
......@@ -1277,11 +1282,9 @@ fn buildOutputType(
12771282 .nostdlibinc => want_native_include_dirs = false,
12781283 .strip => strip = true,
12791284 .exec_model => {
1280 if (std.mem.eql(u8, it.only_arg, "reactor")) {
1281 wasi_exec_model = .crt1_reactor_o;
1282 } else if (std.mem.eql(u8, it.only_arg, "command")) {
1283 wasi_exec_model = .crt1_command_o;
1284 }
1285 wasi_exec_model = std.meta.stringToEnum(std.builtin.WasiExecModel, it.only_arg) orelse {
1286 fatal("expected [command|reactor] for -mexec-mode=[value], found '{s}'", .{it.only_arg});
1287 };
12851288 },
12861289 }
12871290 }
src/wasi_libc.zig+9-3
......@@ -45,9 +45,15 @@ pub fn emulatedLibCRFileLibName(crt_file: CRTFile) []const u8 {
4545 };
4646}
4747
48pub fn crtFileFullName(crt_file: CRTFile) []const u8 {
49 return switch (crt_file) {
50 .crt1_o => "crt1.o",
48pub fn execModelCrtFile(wasi_exec_model: std.builtin.WasiExecModel) CRTFile {
49 return switch (wasi_exec_model) {
50 .reactor => CRTFile.crt1_reactor_o,
51 .command => CRTFile.crt1_command_o,
52 };
53}
54
55pub fn execModelCrtFileFullName(wasi_exec_model: std.builtin.WasiExecModel) []const u8 {
56 return switch (execModelCrtFile(wasi_exec_model)) {
5157 .crt1_reactor_o => "crt1-reactor.o",
5258 .crt1_command_o => "crt1-command.o",
5359 else => unreachable,