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 {...@@ -457,6 +457,13 @@ pub const LinkMode = enum {
457 Dynamic,457 Dynamic,
458};458};
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
460/// This data structure is used by the Zig language code generation and467/// This data structure is used by the Zig language code generation and
461/// therefore must be kept in sync with the compiler implementation.468/// therefore must be kept in sync with the compiler implementation.
462pub const Version = struct {469pub const Version = struct {
lib/std/start.zig+17-11
...@@ -65,8 +65,9 @@ comptime {...@@ -65,8 +65,9 @@ comptime {
65 }65 }
66 } else if (native_os == .uefi) {66 } else if (native_os == .uefi) {
67 if (!@hasDecl(root, "EfiMain")) @export(EfiMain, .{ .name = "EfiMain" });67 if (!@hasDecl(root, "EfiMain")) @export(EfiMain, .{ .name = "EfiMain" });
68 } else if (native_arch.isWasm() and native_os == .freestanding) {68 } else if (native_arch.isWasm()) {
69 if (!@hasDecl(root, start_sym_name)) @export(wasm_freestanding_start, .{ .name = start_sym_name });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 });
70 } else if (native_os != .other and native_os != .freestanding) {71 } else if (native_os != .other and native_os != .freestanding) {
71 if (!@hasDecl(root, start_sym_name)) @export(_start, .{ .name = start_sym_name });72 if (!@hasDecl(root, start_sym_name)) @export(_start, .{ .name = start_sym_name });
72 }73 }
...@@ -135,10 +136,21 @@ fn _DllMainCRTStartup(...@@ -135,10 +136,21 @@ fn _DllMainCRTStartup(
135 return std.os.windows.TRUE;136 return std.os.windows.TRUE;
136}137}
137138
138fn wasm_freestanding_start() callconv(.C) void {139fn wasm_start() callconv(.C) void {
139 // This is marked inline because for some reason LLVM in release mode fails to inline it,140 // The entrypoint is marked inline because for some reason LLVM in release mode fails to inline it,
140 // and we want fewer call frames in stack traces.141 // 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 }
142}154}
143155
144fn EfiMain(handle: uefi.Handle, system_table: *uefi.tables.SystemTable) callconv(.C) usize {156fn 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...@@ -164,12 +176,6 @@ fn EfiMain(handle: uefi.Handle, system_table: *uefi.tables.SystemTable) callconv
164}176}
165177
166fn _start() callconv(.Naked) noreturn {178fn _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
173 switch (native_arch) {179 switch (native_arch) {
174 .x86_64 => {180 .x86_64 => {
175 argc_argv_ptr = asm volatile (181 argc_argv_ptr = asm volatile (
src/Compilation.zig+14-3
...@@ -724,7 +724,7 @@ pub const InitOptions = struct {...@@ -724,7 +724,7 @@ pub const InitOptions = struct {
724 test_name_prefix: ?[]const u8 = null,724 test_name_prefix: ?[]const u8 = null,
725 subsystem: ?std.Target.SubSystem = null,725 subsystem: ?std.Target.SubSystem = null,
726 /// WASI-only. Type of WASI execution model ("command" or "reactor").726 /// 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,
728};728};
729729
730fn addPackageTableToCacheHash(730fn addPackageTableToCacheHash(
...@@ -790,6 +790,9 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {...@@ -790,6 +790,9 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
790790
791 const needs_c_symbols = !options.skip_linker_dependencies and is_exe_or_dyn_lib;791 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
793 const comp: *Compilation = comp: {796 const comp: *Compilation = comp: {
794 // For allocations that have the same lifetime as Compilation. This arena is used only during this797 // For allocations that have the same lifetime as Compilation. This arena is used only during this
795 // initialization and then is freed in deinit().798 // initialization and then is freed in deinit().
...@@ -1340,7 +1343,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {...@@ -1340,7 +1343,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
1340 .disable_lld_caching = options.disable_lld_caching,1343 .disable_lld_caching = options.disable_lld_caching,
1341 .subsystem = options.subsystem,1344 .subsystem = options.subsystem,
1342 .is_test = options.is_test,1345 .is_test = options.is_test,
1343 .wasi_exec_model = options.wasi_exec_model,1346 .wasi_exec_model = wasi_exec_model,
1344 });1347 });
1345 errdefer bin_file.destroy();1348 errdefer bin_file.destroy();
1346 comp.* = .{1349 comp.* = .{
...@@ -1442,7 +1445,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {...@@ -1442,7 +1445,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
1442 });1445 });
1443 }1446 }
1444 comp.work_queue.writeAssumeCapacity(&[_]Job{1447 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) },
1446 .{ .wasi_libc_crt_file = .libc_a },1449 .{ .wasi_libc_crt_file = .libc_a },
1447 });1450 });
1448 }1451 }
...@@ -3650,6 +3653,14 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) Alloc...@@ -3650,6 +3653,14 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) Alloc
3650 std.zig.fmtId(@tagName(comp.bin_file.options.machine_code_model)),3653 std.zig.fmtId(@tagName(comp.bin_file.options.machine_code_model)),
3651 });3654 });
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
3653 if (comp.bin_file.options.is_test) {3664 if (comp.bin_file.options.is_test) {
3654 try buffer.appendSlice(3665 try buffer.appendSlice(
3655 \\pub var test_functions: []std.builtin.TestFn = undefined; // overwritten later3666 \\pub var test_functions: []std.builtin.TestFn = undefined; // overwritten later
src/link.zig+1-1
...@@ -116,7 +116,7 @@ pub const Options = struct {...@@ -116,7 +116,7 @@ pub const Options = struct {
116 libc_installation: ?*const LibCInstallation,116 libc_installation: ?*const LibCInstallation,
117117
118 /// WASI-only. Type of WASI execution model ("command" or "reactor").118 /// 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
121 pub fn effectiveOutputMode(options: Options) std.builtin.OutputMode {121 pub fn effectiveOutputMode(options: Options) std.builtin.OutputMode {
122 return if (options.use_lld) .Obj else options.output_mode;122 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 {...@@ -687,10 +687,15 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {
687 // before corrupting globals. See https://github.com/ziglang/zig/issues/4496687 // before corrupting globals. See https://github.com/ziglang/zig/issues/4496
688 try argv.append("--stack-first");688 try argv.append("--stack-first");
689689
690 // Reactor execution model does not have _start so lld doesn't look for it.690 if (self.base.options.wasi_exec_model == .reactor) {
691 if (self.base.options.wasi_exec_model) |exec_model| blk: {691 // Reactor execution model does not have _start so lld doesn't look for it.
692 if (exec_model != .crt1_reactor_o) break :blk;
693 try argv.append("--no-entry");692 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 });
694 }699 }
695 } else {700 } else {
696 try argv.append("--no-entry"); // So lld doesn't look for _start.701 try argv.append("--no-entry"); // So lld doesn't look for _start.
...@@ -717,7 +722,7 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {...@@ -717,7 +722,7 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {
717 if (self.base.options.link_libc) {722 if (self.base.options.link_libc) {
718 try argv.append(try comp.get_libc_crt_file(723 try argv.append(try comp.get_libc_crt_file(
719 arena,724 arena,
720 wasi_libc.crtFileFullName(self.base.options.wasi_exec_model orelse .crt1_o),725 wasi_libc.execModelCrtFileFullName(self.base.options.wasi_exec_model),
721 ));726 ));
722 try argv.append(try comp.get_libc_crt_file(arena, "libc.a"));727 try argv.append(try comp.get_libc_crt_file(arena, "libc.a"));
723 }728 }
src/main.zig+9-6
...@@ -321,6 +321,7 @@ const usage_build_generic =...@@ -321,6 +321,7 @@ const usage_build_generic =
321 \\ medium|large]321 \\ medium|large]
322 \\ -mred-zone Force-enable the "red-zone"322 \\ -mred-zone Force-enable the "red-zone"
323 \\ -mno-red-zone Force-disable the "red-zone"323 \\ -mno-red-zone Force-disable the "red-zone"
324 \\ -mexec-model=[value] Execution model (WASI only)
324 \\ --name [name] Override root name (not a file path)325 \\ --name [name] Override root name (not a file path)
325 \\ -O [mode] Choose what to optimize for326 \\ -O [mode] Choose what to optimize for
326 \\ Debug (default) Optimizations off, safety on327 \\ Debug (default) Optimizations off, safety on
...@@ -618,7 +619,7 @@ fn buildOutputType(...@@ -618,7 +619,7 @@ fn buildOutputType(
618 var subsystem: ?std.Target.SubSystem = null;619 var subsystem: ?std.Target.SubSystem = null;
619 var major_subsystem_version: ?u32 = null;620 var major_subsystem_version: ?u32 = null;
620 var minor_subsystem_version: ?u32 = null;621 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
623 var system_libs = std.ArrayList([]const u8).init(gpa);624 var system_libs = std.ArrayList([]const u8).init(gpa);
624 defer system_libs.deinit();625 defer system_libs.deinit();
...@@ -1071,6 +1072,10 @@ fn buildOutputType(...@@ -1071,6 +1072,10 @@ fn buildOutputType(
1071 mem.startsWith(u8, arg, "-I"))1072 mem.startsWith(u8, arg, "-I"))
1072 {1073 {
1073 try clang_argv.append(arg);1074 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 };
1074 } else {1079 } else {
1075 fatal("unrecognized parameter: '{s}'", .{arg});1080 fatal("unrecognized parameter: '{s}'", .{arg});
1076 }1081 }
...@@ -1277,11 +1282,9 @@ fn buildOutputType(...@@ -1277,11 +1282,9 @@ fn buildOutputType(
1277 .nostdlibinc => want_native_include_dirs = false,1282 .nostdlibinc => want_native_include_dirs = false,
1278 .strip => strip = true,1283 .strip => strip = true,
1279 .exec_model => {1284 .exec_model => {
1280 if (std.mem.eql(u8, it.only_arg, "reactor")) {1285 wasi_exec_model = std.meta.stringToEnum(std.builtin.WasiExecModel, it.only_arg) orelse {
1281 wasi_exec_model = .crt1_reactor_o;1286 fatal("expected [command|reactor] for -mexec-mode=[value], found '{s}'", .{it.only_arg});
1282 } else if (std.mem.eql(u8, it.only_arg, "command")) {1287 };
1283 wasi_exec_model = .crt1_command_o;
1284 }
1285 },1288 },
1286 }1289 }
1287 }1290 }
src/wasi_libc.zig+9-3
...@@ -45,9 +45,15 @@ pub fn emulatedLibCRFileLibName(crt_file: CRTFile) []const u8 {...@@ -45,9 +45,15 @@ pub fn emulatedLibCRFileLibName(crt_file: CRTFile) []const u8 {
45 };45 };
46}46}
4747
48pub fn crtFileFullName(crt_file: CRTFile) []const u8 {48pub fn execModelCrtFile(wasi_exec_model: std.builtin.WasiExecModel) CRTFile {
49 return switch (crt_file) {49 return switch (wasi_exec_model) {
50 .crt1_o => "crt1.o",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)) {
51 .crt1_reactor_o => "crt1-reactor.o",57 .crt1_reactor_o => "crt1-reactor.o",
52 .crt1_command_o => "crt1-command.o",58 .crt1_command_o => "crt1-command.o",
53 else => unreachable,59 else => unreachable,