Skip to content

Commit d916954

Browse files
authored
Merge pull request #22098 from alexrp/wasm-generic-baseline
`std.Target`: Use `lime1` as wasm baseline model and `mvp` as generic model
2 parents f3c29dc + 68c6a88 commit d916954

File tree

8 files changed

+226
-20
lines changed

8 files changed

+226
-20
lines changed

build.zig

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -594,15 +594,14 @@ pub fn build(b: *std.Build) !void {
594594
fn addWasiUpdateStep(b: *std.Build, version: [:0]const u8) !void {
595595
const semver = try std.SemanticVersion.parse(version);
596596

597-
var target_query: std.Target.Query = .{
598-
.cpu_arch = .wasm32,
599-
.os_tag = .wasi,
600-
};
601-
target_query.cpu_features_add.addFeature(@intFromEnum(std.Target.wasm.Feature.bulk_memory));
602-
603597
const exe = addCompilerStep(b, .{
604598
.optimize = .ReleaseSmall,
605-
.target = b.resolveTargetQuery(target_query),
599+
.target = b.resolveTargetQuery(std.Target.Query.parse(.{
600+
.arch_os_abi = "wasm32-wasi",
601+
// * `extended_const` is not supported by the `wasm-opt` version in CI.
602+
// * `nontrapping_bulk_memory_len0` is supported by `wasm2c`.
603+
.cpu_features = "baseline-extended_const+nontrapping_bulk_memory_len0",
604+
}) catch unreachable),
606605
});
607606

608607
const exe_options = b.addOptions();
@@ -644,6 +643,8 @@ fn addWasiUpdateStep(b: *std.Build, version: [:0]const u8) !void {
644643
"wasm-opt",
645644
"-Oz",
646645
"--enable-bulk-memory",
646+
"--enable-mutable-globals",
647+
"--enable-nontrapping-float-to-int",
647648
"--enable-sign-ext",
648649
});
649650
run_opt.addArtifactArg(exe);

lib/std/Target.zig

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1958,7 +1958,7 @@ pub const Cpu = struct {
19581958
.x86_64 => &x86.cpu.x86_64,
19591959
.nvptx, .nvptx64 => &nvptx.cpu.sm_20,
19601960
.ve => &ve.cpu.generic,
1961-
.wasm32, .wasm64 => &wasm.cpu.generic,
1961+
.wasm32, .wasm64 => &wasm.cpu.mvp,
19621962
.xcore => &xcore.cpu.generic,
19631963
.xtensa => &xtensa.cpu.generic,
19641964

@@ -2012,6 +2012,7 @@ pub const Cpu = struct {
20122012
else => generic(arch),
20132013
},
20142014
.xcore => &xcore.cpu.xs1b_generic,
2015+
.wasm32, .wasm64 => &wasm.cpu.lime1,
20152016

20162017
else => generic(arch),
20172018
};

lib/std/Target/wasm.zig

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ pub const Feature = enum {
1313
multimemory,
1414
multivalue,
1515
mutable_globals,
16+
nontrapping_bulk_memory_len0,
1617
nontrapping_fptoint,
1718
reference_types,
1819
relaxed_simd,
@@ -70,6 +71,13 @@ pub const all_features = blk: {
7071
.description = "Enable mutable globals",
7172
.dependencies = featureSet(&[_]Feature{}),
7273
};
74+
result[@intFromEnum(Feature.nontrapping_bulk_memory_len0)] = .{
75+
.llvm_name = null,
76+
.description = "Bulk memory operations with a zero length do not trap",
77+
.dependencies = featureSet(&[_]Feature{
78+
.bulk_memory,
79+
}),
80+
};
7381
result[@intFromEnum(Feature.nontrapping_fptoint)] = .{
7482
.llvm_name = "nontrapping-fptoint",
7583
.description = "Enable non-trapping float-to-int conversion operators",
@@ -139,6 +147,18 @@ pub const cpu = struct {
139147
.sign_ext,
140148
}),
141149
};
150+
pub const lime1: CpuModel = .{
151+
.name = "lime1",
152+
.llvm_name = null,
153+
.features = featureSet(&[_]Feature{
154+
.bulk_memory,
155+
.extended_const,
156+
.multivalue,
157+
.mutable_globals,
158+
.nontrapping_fptoint,
159+
.sign_ext,
160+
}),
161+
};
142162
pub const mvp: CpuModel = .{
143163
.name = "mvp",
144164
.llvm_name = "mvp",

src/Compilation.zig

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4150,14 +4150,9 @@ fn workerDocsWasmFallible(comp: *Compilation, prog_node: std.Progress.Node) anye
41504150
.os_tag = .freestanding,
41514151
.cpu_features_add = std.Target.wasm.featureSet(&.{
41524152
.atomics,
4153-
.bulk_memory,
41544153
// .extended_const, not supported by Safari
4155-
.multivalue,
4156-
.mutable_globals,
4157-
.nontrapping_fptoint,
41584154
.reference_types,
41594155
//.relaxed_simd, not supported by Firefox or Safari
4160-
.sign_ext,
41614156
// observed to cause Error occured during wast conversion :
41624157
// Unknown operator: 0xfd058 in Firefox 117
41634158
//.simd128,

src/arch/wasm/CodeGen.zig

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1591,10 +1591,35 @@ fn memcpy(cg: *CodeGen, dst: WValue, src: WValue, len: WValue) !void {
15911591
// When bulk_memory is enabled, we lower it to wasm's memcpy instruction.
15921592
// If not, we lower it ourselves manually
15931593
if (std.Target.wasm.featureSetHas(cg.target.cpu.features, .bulk_memory)) {
1594+
const len0_ok = std.Target.wasm.featureSetHas(cg.target.cpu.features, .nontrapping_bulk_memory_len0);
1595+
1596+
if (!len0_ok) {
1597+
try cg.startBlock(.block, .empty);
1598+
1599+
// Even if `len` is zero, the spec requires an implementation to trap if `src + len` or
1600+
// `dst + len` are out of memory bounds. This can easily happen in Zig in a case such
1601+
// as:
1602+
//
1603+
// const dst: [*]u8 = undefined;
1604+
// const src: [*]u8 = undefined;
1605+
// var len: usize = runtime_zero();
1606+
// @memcpy(dst[0..len], src[0..len]);
1607+
//
1608+
// So explicitly avoid using `memory.copy` in the `len == 0` case. Lovely design.
1609+
try cg.emitWValue(len);
1610+
try cg.addTag(.i32_eqz);
1611+
try cg.addLabel(.br_if, 0);
1612+
}
1613+
15941614
try cg.lowerToStack(dst);
15951615
try cg.lowerToStack(src);
15961616
try cg.emitWValue(len);
15971617
try cg.addExtended(.memory_copy);
1618+
1619+
if (!len0_ok) {
1620+
try cg.endBlock();
1621+
}
1622+
15981623
return;
15991624
}
16001625

@@ -4782,10 +4807,33 @@ fn memset(cg: *CodeGen, elem_ty: Type, ptr: WValue, len: WValue, value: WValue)
47824807
// When bulk_memory is enabled, we lower it to wasm's memset instruction.
47834808
// If not, we lower it ourselves.
47844809
if (std.Target.wasm.featureSetHas(cg.target.cpu.features, .bulk_memory) and abi_size == 1) {
4810+
const len0_ok = std.Target.wasm.featureSetHas(cg.target.cpu.features, .nontrapping_bulk_memory_len0);
4811+
4812+
if (!len0_ok) {
4813+
try cg.startBlock(.block, .empty);
4814+
4815+
// Even if `len` is zero, the spec requires an implementation to trap if `ptr + len` is
4816+
// out of memory bounds. This can easily happen in Zig in a case such as:
4817+
//
4818+
// const ptr: [*]u8 = undefined;
4819+
// var len: usize = runtime_zero();
4820+
// @memset(ptr[0..len], 42);
4821+
//
4822+
// So explicitly avoid using `memory.fill` in the `len == 0` case. Lovely design.
4823+
try cg.emitWValue(len);
4824+
try cg.addTag(.i32_eqz);
4825+
try cg.addLabel(.br_if, 0);
4826+
}
4827+
47854828
try cg.lowerToStack(ptr);
47864829
try cg.emitWValue(value);
47874830
try cg.emitWValue(len);
47884831
try cg.addExtended(.memory_fill);
4832+
4833+
if (!len0_ok) {
4834+
try cg.endBlock();
4835+
}
4836+
47894837
return;
47904838
}
47914839

src/link/Wasm.zig

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2826,6 +2826,7 @@ pub const Feature = packed struct(u8) {
28262826
multimemory,
28272827
multivalue,
28282828
@"mutable-globals",
2829+
@"nontrapping-bulk-memory-len0",
28292830
@"nontrapping-fptoint",
28302831
@"reference-types",
28312832
@"relaxed-simd",
@@ -2835,14 +2836,44 @@ pub const Feature = packed struct(u8) {
28352836
@"shared-mem",
28362837

28372838
pub fn fromCpuFeature(feature: std.Target.wasm.Feature) Tag {
2838-
return @enumFromInt(@intFromEnum(feature));
2839+
return switch (feature) {
2840+
.atomics => .atomics,
2841+
.bulk_memory => .@"bulk-memory",
2842+
.exception_handling => .@"exception-handling",
2843+
.extended_const => .@"extended-const",
2844+
.half_precision => .@"half-precision",
2845+
.multimemory => .multimemory,
2846+
.multivalue => .multivalue,
2847+
.mutable_globals => .@"mutable-globals",
2848+
.nontrapping_bulk_memory_len0 => .@"nontrapping-bulk-memory-len0", // Zig extension.
2849+
.nontrapping_fptoint => .@"nontrapping-fptoint",
2850+
.reference_types => .@"reference-types",
2851+
.relaxed_simd => .@"relaxed-simd",
2852+
.sign_ext => .@"sign-ext",
2853+
.simd128 => .simd128,
2854+
.tail_call => .@"tail-call",
2855+
};
28392856
}
28402857

28412858
pub fn toCpuFeature(tag: Tag) ?std.Target.wasm.Feature {
2842-
return if (@intFromEnum(tag) < @typeInfo(std.Target.wasm.Feature).@"enum".fields.len)
2843-
@enumFromInt(@intFromEnum(tag))
2844-
else
2845-
null;
2859+
return switch (tag) {
2860+
.atomics => .atomics,
2861+
.@"bulk-memory" => .bulk_memory,
2862+
.@"exception-handling" => .exception_handling,
2863+
.@"extended-const" => .extended_const,
2864+
.@"half-precision" => .half_precision,
2865+
.multimemory => .multimemory,
2866+
.multivalue => .multivalue,
2867+
.@"mutable-globals" => .mutable_globals,
2868+
.@"nontrapping-bulk-memory-len0" => .nontrapping_bulk_memory_len0, // Zig extension.
2869+
.@"nontrapping-fptoint" => .nontrapping_fptoint,
2870+
.@"reference-types" => .reference_types,
2871+
.@"relaxed-simd" => .relaxed_simd,
2872+
.@"sign-ext" => .sign_ext,
2873+
.simd128 => .simd128,
2874+
.@"tail-call" => .tail_call,
2875+
.@"shared-mem" => null, // Linker-only feature.
2876+
};
28462877
}
28472878

28482879
pub const format = @compileError("use @tagName instead");

stage1/wasm2c.c

Lines changed: 91 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,8 @@ int main(int argc, char **argv) {
109109

110110
FILE *out = fopen(argv[2], "wb");
111111
if (out == NULL) panic("unable to open output file");
112-
fputs("#include <math.h>\n"
112+
fputs("#include <float.h>\n"
113+
"#include <math.h>\n"
113114
"#include <stdint.h>\n"
114115
"#include <stdlib.h>\n"
115116
"#include <string.h>\n"
@@ -273,6 +274,47 @@ int main(int argc, char **argv) {
273274
" return dst;\n"
274275
"}\n"
275276
"\n"
277+
"static uint32_t i32_trunc_sat_f32(const float src) {\n"
278+
" if (isnan(src)) return 0;\n"
279+
" if (isinf(src)) return (uint32_t)(signbit(src) == 0 ? INT32_MAX : INT32_MIN);\n"
280+
" return (uint32_t)(int32_t)src;\n"
281+
"}\n"
282+
"static uint32_t u32_trunc_sat_f32(const float src) {\n"
283+
" if (isnan(src)) return 0;\n"
284+
" if (isinf(src)) return signbit(src) == 0 ? UINT32_MAX : 0;\n"
285+
" return (uint32_t)src;\n"
286+
"}\n"
287+
"static uint32_t i32_trunc_sat_f64(const double src) {\n"
288+
" if (isnan(src)) return 0;\n"
289+
" if (isinf(src)) return (uint32_t)(signbit(src) == 0 ? INT32_MAX : INT32_MIN);\n"
290+
" return (uint32_t)(int32_t)src;\n"
291+
"}\n"
292+
"static uint32_t u32_trunc_sat_f64(const double src) {\n"
293+
" if (isnan(src)) return 0;\n"
294+
" if (isinf(src)) return signbit(src) == 0 ? UINT32_MAX : 0;\n"
295+
" return (uint32_t)src;\n"
296+
"}\n"
297+
"static uint64_t i64_trunc_sat_f32(const float src) {\n"
298+
" if (isnan(src)) return 0;\n"
299+
" if (isinf(src)) return (uint64_t)(signbit(src) == 0 ? INT64_MAX : INT64_MIN);\n"
300+
" return (uint64_t)(int64_t)src;\n"
301+
"}\n"
302+
"static uint64_t u64_trunc_sat_f32(const float src) {\n"
303+
" if (isnan(src)) return 0;\n"
304+
" if (isinf(src)) return signbit(src) == 0 ? UINT64_MAX : 0;\n"
305+
" return (uint64_t)src;\n"
306+
"}\n"
307+
"static uint64_t i64_trunc_sat_f64(const double src) {\n"
308+
" if (isnan(src)) return 0;\n"
309+
" if (isinf(src)) return (uint64_t)(signbit(src) == 0 ? INT64_MAX : INT64_MIN);\n"
310+
" return (uint64_t)(int64_t)src;\n"
311+
"}\n"
312+
"static uint64_t u64_trunc_sat_f64(const double src) {\n"
313+
" if (isnan(src)) return 0;\n"
314+
" if (isinf(src)) return signbit(src) == 0 ? UINT64_MAX : 0;\n"
315+
" return (uint64_t)src;\n"
316+
"}\n"
317+
"\n"
276318
"static uint32_t memory_grow(uint8_t **m, uint32_t *p, uint32_t *c, uint32_t n) {\n"
277319
" uint8_t *new_m = *m;\n"
278320
" uint32_t r = *p;\n"
@@ -2074,14 +2116,61 @@ int main(int argc, char **argv) {
20742116
case WasmOpcode_prefixed:
20752117
switch (InputStream_readLeb128_u32(&in)) {
20762118
case WasmPrefixedOpcode_i32_trunc_sat_f32_s:
2119+
if (unreachable_depth == 0) {
2120+
uint32_t lhs = FuncGen_stackPop(&fg);
2121+
FuncGen_stackPush(&fg, out, WasmValType_i32);
2122+
fprintf(out, "i32_trunc_sat_f32(l%" PRIu32 ");\n", lhs);
2123+
}
2124+
break;
20772125
case WasmPrefixedOpcode_i32_trunc_sat_f32_u:
2126+
if (unreachable_depth == 0) {
2127+
uint32_t lhs = FuncGen_stackPop(&fg);
2128+
FuncGen_stackPush(&fg, out, WasmValType_i32);
2129+
fprintf(out, "u32_trunc_sat_f32(l%" PRIu32 ");\n", lhs);
2130+
}
2131+
break;
20782132
case WasmPrefixedOpcode_i32_trunc_sat_f64_s:
2133+
if (unreachable_depth == 0) {
2134+
uint32_t lhs = FuncGen_stackPop(&fg);
2135+
FuncGen_stackPush(&fg, out, WasmValType_i32);
2136+
fprintf(out, "i32_trunc_sat_f64(l%" PRIu32 ");\n", lhs);
2137+
}
2138+
break;
20792139
case WasmPrefixedOpcode_i32_trunc_sat_f64_u:
2140+
if (unreachable_depth == 0) {
2141+
uint32_t lhs = FuncGen_stackPop(&fg);
2142+
FuncGen_stackPush(&fg, out, WasmValType_i32);
2143+
fprintf(out, "u32_trunc_sat_f64(l%" PRIu32 ");\n", lhs);
2144+
}
2145+
break;
20802146
case WasmPrefixedOpcode_i64_trunc_sat_f32_s:
2147+
if (unreachable_depth == 0) {
2148+
uint32_t lhs = FuncGen_stackPop(&fg);
2149+
FuncGen_stackPush(&fg, out, WasmValType_i32);
2150+
fprintf(out, "i64_trunc_sat_f32(l%" PRIu32 ");\n", lhs);
2151+
}
2152+
break;
20812153
case WasmPrefixedOpcode_i64_trunc_sat_f32_u:
2154+
if (unreachable_depth == 0) {
2155+
uint32_t lhs = FuncGen_stackPop(&fg);
2156+
FuncGen_stackPush(&fg, out, WasmValType_i32);
2157+
fprintf(out, "u64_trunc_sat_f32(l%" PRIu32 ");\n", lhs);
2158+
}
2159+
break;
20822160
case WasmPrefixedOpcode_i64_trunc_sat_f64_s:
2161+
if (unreachable_depth == 0) {
2162+
uint32_t lhs = FuncGen_stackPop(&fg);
2163+
FuncGen_stackPush(&fg, out, WasmValType_i32);
2164+
fprintf(out, "i64_trunc_sat_f64(l%" PRIu32 ");\n", lhs);
2165+
}
2166+
break;
20832167
case WasmPrefixedOpcode_i64_trunc_sat_f64_u:
2084-
if (unreachable_depth == 0) panic("unimplemented opcode");
2168+
if (unreachable_depth == 0) {
2169+
uint32_t lhs = FuncGen_stackPop(&fg);
2170+
FuncGen_stackPush(&fg, out, WasmValType_i32);
2171+
fprintf(out, "u64_trunc_sat_f64(l%" PRIu32 ");\n", lhs);
2172+
}
2173+
break;
20852174

20862175
case WasmPrefixedOpcode_memory_init:
20872176
(void)InputStream_readLeb128_u32(&in);

tools/update_cpu_features.zig

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1033,6 +1033,27 @@ const llvm_targets = [_]LlvmTarget{
10331033
.zig_name = "wasm",
10341034
.llvm_name = "WebAssembly",
10351035
.td_name = "WebAssembly.td",
1036+
.extra_features = &.{
1037+
.{
1038+
.zig_name = "nontrapping_bulk_memory_len0",
1039+
.desc = "Bulk memory operations with a zero length do not trap",
1040+
.deps = &.{"bulk_memory"},
1041+
},
1042+
},
1043+
.extra_cpus = &.{
1044+
.{
1045+
.llvm_name = null,
1046+
.zig_name = "lime1",
1047+
.features = &.{
1048+
"bulk_memory",
1049+
"extended_const",
1050+
"multivalue",
1051+
"mutable_globals",
1052+
"nontrapping_fptoint",
1053+
"sign_ext",
1054+
},
1055+
},
1056+
},
10361057
},
10371058
.{
10381059
.zig_name = "x86",

0 commit comments

Comments
 (0)