Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
15 changes: 8 additions & 7 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -594,15 +594,14 @@ pub fn build(b: *std.Build) !void {
fn addWasiUpdateStep(b: *std.Build, version: [:0]const u8) !void {
const semver = try std.SemanticVersion.parse(version);

var target_query: std.Target.Query = .{
.cpu_arch = .wasm32,
.os_tag = .wasi,
};
target_query.cpu_features_add.addFeature(@intFromEnum(std.Target.wasm.Feature.bulk_memory));

const exe = addCompilerStep(b, .{
.optimize = .ReleaseSmall,
.target = b.resolveTargetQuery(target_query),
.target = b.resolveTargetQuery(std.Target.Query.parse(.{
.arch_os_abi = "wasm32-wasi",
// * `extended_const` is not supported by the `wasm-opt` version in CI.
// * `nontrapping_bulk_memory_len0` is supported by `wasm2c`.
.cpu_features = "baseline-extended_const+nontrapping_bulk_memory_len0",
}) catch unreachable),
});

const exe_options = b.addOptions();
Expand Down Expand Up @@ -644,6 +643,8 @@ fn addWasiUpdateStep(b: *std.Build, version: [:0]const u8) !void {
"wasm-opt",
"-Oz",
"--enable-bulk-memory",
"--enable-mutable-globals",
"--enable-nontrapping-float-to-int",
"--enable-sign-ext",
});
run_opt.addArtifactArg(exe);
Expand Down
3 changes: 2 additions & 1 deletion lib/std/Target.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1958,7 +1958,7 @@ pub const Cpu = struct {
.x86_64 => &x86.cpu.x86_64,
.nvptx, .nvptx64 => &nvptx.cpu.sm_20,
.ve => &ve.cpu.generic,
.wasm32, .wasm64 => &wasm.cpu.generic,
.wasm32, .wasm64 => &wasm.cpu.mvp,
.xcore => &xcore.cpu.generic,
.xtensa => &xtensa.cpu.generic,

Expand Down Expand Up @@ -2012,6 +2012,7 @@ pub const Cpu = struct {
else => generic(arch),
},
.xcore => &xcore.cpu.xs1b_generic,
.wasm32, .wasm64 => &wasm.cpu.lime1,

else => generic(arch),
};
Expand Down
20 changes: 20 additions & 0 deletions lib/std/Target/wasm.zig
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ pub const Feature = enum {
multimemory,
multivalue,
mutable_globals,
nontrapping_bulk_memory_len0,
nontrapping_fptoint,
reference_types,
relaxed_simd,
Expand Down Expand Up @@ -70,6 +71,13 @@ pub const all_features = blk: {
.description = "Enable mutable globals",
.dependencies = featureSet(&[_]Feature{}),
};
result[@intFromEnum(Feature.nontrapping_bulk_memory_len0)] = .{
.llvm_name = null,
.description = "Bulk memory operations with a zero length do not trap",
.dependencies = featureSet(&[_]Feature{
.bulk_memory,
}),
};
result[@intFromEnum(Feature.nontrapping_fptoint)] = .{
.llvm_name = "nontrapping-fptoint",
.description = "Enable non-trapping float-to-int conversion operators",
Expand Down Expand Up @@ -139,6 +147,18 @@ pub const cpu = struct {
.sign_ext,
}),
};
pub const lime1: CpuModel = .{
.name = "lime1",
.llvm_name = null,
.features = featureSet(&[_]Feature{
.bulk_memory,
.extended_const,
.multivalue,
.mutable_globals,
.nontrapping_fptoint,
.sign_ext,
}),
};
pub const mvp: CpuModel = .{
.name = "mvp",
.llvm_name = "mvp",
Expand Down
5 changes: 0 additions & 5 deletions src/Compilation.zig
Original file line number Diff line number Diff line change
Expand Up @@ -4150,14 +4150,9 @@ fn workerDocsWasmFallible(comp: *Compilation, prog_node: std.Progress.Node) anye
.os_tag = .freestanding,
.cpu_features_add = std.Target.wasm.featureSet(&.{
.atomics,
.bulk_memory,
// .extended_const, not supported by Safari
.multivalue,
.mutable_globals,
.nontrapping_fptoint,
.reference_types,
//.relaxed_simd, not supported by Firefox or Safari
.sign_ext,
// observed to cause Error occured during wast conversion :
// Unknown operator: 0xfd058 in Firefox 117
//.simd128,
Expand Down
48 changes: 48 additions & 0 deletions src/arch/wasm/CodeGen.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1591,10 +1591,35 @@ fn memcpy(cg: *CodeGen, dst: WValue, src: WValue, len: WValue) !void {
// When bulk_memory is enabled, we lower it to wasm's memcpy instruction.
// If not, we lower it ourselves manually
if (std.Target.wasm.featureSetHas(cg.target.cpu.features, .bulk_memory)) {
const len0_ok = std.Target.wasm.featureSetHas(cg.target.cpu.features, .nontrapping_bulk_memory_len0);

if (!len0_ok) {
try cg.startBlock(.block, .empty);

// Even if `len` is zero, the spec requires an implementation to trap if `src + len` or
// `dst + len` are out of memory bounds. This can easily happen in Zig in a case such
// as:
//
// const dst: [*]u8 = undefined;
// const src: [*]u8 = undefined;
// var len: usize = runtime_zero();
// @memcpy(dst[0..len], src[0..len]);
//
// So explicitly avoid using `memory.copy` in the `len == 0` case. Lovely design.
try cg.emitWValue(len);
try cg.addTag(.i32_eqz);
try cg.addLabel(.br_if, 0);
}

try cg.lowerToStack(dst);
try cg.lowerToStack(src);
try cg.emitWValue(len);
try cg.addExtended(.memory_copy);

if (!len0_ok) {
try cg.endBlock();
}

return;
}

Expand Down Expand Up @@ -4782,10 +4807,33 @@ fn memset(cg: *CodeGen, elem_ty: Type, ptr: WValue, len: WValue, value: WValue)
// When bulk_memory is enabled, we lower it to wasm's memset instruction.
// If not, we lower it ourselves.
if (std.Target.wasm.featureSetHas(cg.target.cpu.features, .bulk_memory) and abi_size == 1) {
const len0_ok = std.Target.wasm.featureSetHas(cg.target.cpu.features, .nontrapping_bulk_memory_len0);

if (!len0_ok) {
try cg.startBlock(.block, .empty);

// Even if `len` is zero, the spec requires an implementation to trap if `ptr + len` is
// out of memory bounds. This can easily happen in Zig in a case such as:
//
// const ptr: [*]u8 = undefined;
// var len: usize = runtime_zero();
// @memset(ptr[0..len], 42);
//
// So explicitly avoid using `memory.fill` in the `len == 0` case. Lovely design.
try cg.emitWValue(len);
try cg.addTag(.i32_eqz);
try cg.addLabel(.br_if, 0);
}

try cg.lowerToStack(ptr);
try cg.emitWValue(value);
try cg.emitWValue(len);
try cg.addExtended(.memory_fill);

if (!len0_ok) {
try cg.endBlock();
}

return;
}

Expand Down
41 changes: 36 additions & 5 deletions src/link/Wasm.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2826,6 +2826,7 @@ pub const Feature = packed struct(u8) {
multimemory,
multivalue,
@"mutable-globals",
@"nontrapping-bulk-memory-len0",
@"nontrapping-fptoint",
@"reference-types",
@"relaxed-simd",
Expand All @@ -2835,14 +2836,44 @@ pub const Feature = packed struct(u8) {
@"shared-mem",

pub fn fromCpuFeature(feature: std.Target.wasm.Feature) Tag {
return @enumFromInt(@intFromEnum(feature));
return switch (feature) {
Copy link
Member

Choose a reason for hiding this comment

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

Good move, this looks a lot better. Sorry about the trouble, I should have done this from the beginning.

.atomics => .atomics,
.bulk_memory => .@"bulk-memory",
.exception_handling => .@"exception-handling",
.extended_const => .@"extended-const",
.half_precision => .@"half-precision",
.multimemory => .multimemory,
.multivalue => .multivalue,
.mutable_globals => .@"mutable-globals",
.nontrapping_bulk_memory_len0 => .@"nontrapping-bulk-memory-len0", // Zig extension.
.nontrapping_fptoint => .@"nontrapping-fptoint",
.reference_types => .@"reference-types",
.relaxed_simd => .@"relaxed-simd",
.sign_ext => .@"sign-ext",
.simd128 => .simd128,
.tail_call => .@"tail-call",
};
}

pub fn toCpuFeature(tag: Tag) ?std.Target.wasm.Feature {
return if (@intFromEnum(tag) < @typeInfo(std.Target.wasm.Feature).@"enum".fields.len)
@enumFromInt(@intFromEnum(tag))
else
null;
return switch (tag) {
.atomics => .atomics,
.@"bulk-memory" => .bulk_memory,
.@"exception-handling" => .exception_handling,
.@"extended-const" => .extended_const,
.@"half-precision" => .half_precision,
.multimemory => .multimemory,
.multivalue => .multivalue,
.@"mutable-globals" => .mutable_globals,
.@"nontrapping-bulk-memory-len0" => .nontrapping_bulk_memory_len0, // Zig extension.
.@"nontrapping-fptoint" => .nontrapping_fptoint,
.@"reference-types" => .reference_types,
.@"relaxed-simd" => .relaxed_simd,
.@"sign-ext" => .sign_ext,
.simd128 => .simd128,
.@"tail-call" => .tail_call,
.@"shared-mem" => null, // Linker-only feature.
};
}

pub const format = @compileError("use @tagName instead");
Expand Down
93 changes: 91 additions & 2 deletions stage1/wasm2c.c
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,8 @@ int main(int argc, char **argv) {

FILE *out = fopen(argv[2], "wb");
if (out == NULL) panic("unable to open output file");
fputs("#include <math.h>\n"
fputs("#include <float.h>\n"
"#include <math.h>\n"
"#include <stdint.h>\n"
"#include <stdlib.h>\n"
"#include <string.h>\n"
Expand Down Expand Up @@ -273,6 +274,47 @@ int main(int argc, char **argv) {
" return dst;\n"
"}\n"
"\n"
"static uint32_t i32_trunc_sat_f32(const float src) {\n"
" if (isnan(src)) return 0;\n"
" if (isinf(src)) return (uint32_t)(signbit(src) == 0 ? INT32_MAX : INT32_MIN);\n"
" return (uint32_t)(int32_t)src;\n"
"}\n"
"static uint32_t u32_trunc_sat_f32(const float src) {\n"
" if (isnan(src)) return 0;\n"
" if (isinf(src)) return signbit(src) == 0 ? UINT32_MAX : 0;\n"
" return (uint32_t)src;\n"
"}\n"
"static uint32_t i32_trunc_sat_f64(const double src) {\n"
" if (isnan(src)) return 0;\n"
" if (isinf(src)) return (uint32_t)(signbit(src) == 0 ? INT32_MAX : INT32_MIN);\n"
" return (uint32_t)(int32_t)src;\n"
"}\n"
"static uint32_t u32_trunc_sat_f64(const double src) {\n"
" if (isnan(src)) return 0;\n"
" if (isinf(src)) return signbit(src) == 0 ? UINT32_MAX : 0;\n"
" return (uint32_t)src;\n"
"}\n"
"static uint64_t i64_trunc_sat_f32(const float src) {\n"
" if (isnan(src)) return 0;\n"
" if (isinf(src)) return (uint64_t)(signbit(src) == 0 ? INT64_MAX : INT64_MIN);\n"
" return (uint64_t)(int64_t)src;\n"
"}\n"
"static uint64_t u64_trunc_sat_f32(const float src) {\n"
" if (isnan(src)) return 0;\n"
" if (isinf(src)) return signbit(src) == 0 ? UINT64_MAX : 0;\n"
" return (uint64_t)src;\n"
"}\n"
"static uint64_t i64_trunc_sat_f64(const double src) {\n"
" if (isnan(src)) return 0;\n"
" if (isinf(src)) return (uint64_t)(signbit(src) == 0 ? INT64_MAX : INT64_MIN);\n"
" return (uint64_t)(int64_t)src;\n"
"}\n"
"static uint64_t u64_trunc_sat_f64(const double src) {\n"
" if (isnan(src)) return 0;\n"
" if (isinf(src)) return signbit(src) == 0 ? UINT64_MAX : 0;\n"
" return (uint64_t)src;\n"
"}\n"
"\n"
"static uint32_t memory_grow(uint8_t **m, uint32_t *p, uint32_t *c, uint32_t n) {\n"
" uint8_t *new_m = *m;\n"
" uint32_t r = *p;\n"
Expand Down Expand Up @@ -2074,14 +2116,61 @@ int main(int argc, char **argv) {
case WasmOpcode_prefixed:
switch (InputStream_readLeb128_u32(&in)) {
case WasmPrefixedOpcode_i32_trunc_sat_f32_s:
if (unreachable_depth == 0) {
uint32_t lhs = FuncGen_stackPop(&fg);
FuncGen_stackPush(&fg, out, WasmValType_i32);
fprintf(out, "i32_trunc_sat_f32(l%" PRIu32 ");\n", lhs);
}
break;
case WasmPrefixedOpcode_i32_trunc_sat_f32_u:
if (unreachable_depth == 0) {
uint32_t lhs = FuncGen_stackPop(&fg);
FuncGen_stackPush(&fg, out, WasmValType_i32);
fprintf(out, "u32_trunc_sat_f32(l%" PRIu32 ");\n", lhs);
}
break;
case WasmPrefixedOpcode_i32_trunc_sat_f64_s:
if (unreachable_depth == 0) {
uint32_t lhs = FuncGen_stackPop(&fg);
FuncGen_stackPush(&fg, out, WasmValType_i32);
fprintf(out, "i32_trunc_sat_f64(l%" PRIu32 ");\n", lhs);
}
break;
case WasmPrefixedOpcode_i32_trunc_sat_f64_u:
if (unreachable_depth == 0) {
uint32_t lhs = FuncGen_stackPop(&fg);
FuncGen_stackPush(&fg, out, WasmValType_i32);
fprintf(out, "u32_trunc_sat_f64(l%" PRIu32 ");\n", lhs);
}
break;
case WasmPrefixedOpcode_i64_trunc_sat_f32_s:
if (unreachable_depth == 0) {
uint32_t lhs = FuncGen_stackPop(&fg);
FuncGen_stackPush(&fg, out, WasmValType_i32);
fprintf(out, "i64_trunc_sat_f32(l%" PRIu32 ");\n", lhs);
}
break;
case WasmPrefixedOpcode_i64_trunc_sat_f32_u:
if (unreachable_depth == 0) {
uint32_t lhs = FuncGen_stackPop(&fg);
FuncGen_stackPush(&fg, out, WasmValType_i32);
fprintf(out, "u64_trunc_sat_f32(l%" PRIu32 ");\n", lhs);
}
break;
case WasmPrefixedOpcode_i64_trunc_sat_f64_s:
if (unreachable_depth == 0) {
uint32_t lhs = FuncGen_stackPop(&fg);
FuncGen_stackPush(&fg, out, WasmValType_i32);
fprintf(out, "i64_trunc_sat_f64(l%" PRIu32 ");\n", lhs);
}
break;
case WasmPrefixedOpcode_i64_trunc_sat_f64_u:
if (unreachable_depth == 0) panic("unimplemented opcode");
if (unreachable_depth == 0) {
uint32_t lhs = FuncGen_stackPop(&fg);
FuncGen_stackPush(&fg, out, WasmValType_i32);
fprintf(out, "u64_trunc_sat_f64(l%" PRIu32 ");\n", lhs);
}
break;

case WasmPrefixedOpcode_memory_init:
(void)InputStream_readLeb128_u32(&in);
Expand Down
21 changes: 21 additions & 0 deletions tools/update_cpu_features.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1033,6 +1033,27 @@ const llvm_targets = [_]LlvmTarget{
.zig_name = "wasm",
.llvm_name = "WebAssembly",
.td_name = "WebAssembly.td",
.extra_features = &.{
.{
.zig_name = "nontrapping_bulk_memory_len0",
.desc = "Bulk memory operations with a zero length do not trap",
.deps = &.{"bulk_memory"},
},
},
.extra_cpus = &.{
.{
.llvm_name = null,
.zig_name = "lime1",
.features = &.{
"bulk_memory",
"extended_const",
"multivalue",
"mutable_globals",
"nontrapping_fptoint",
"sign_ext",
},
},
},
},
.{
.zig_name = "x86",
Expand Down
Loading