Commit ea1502974d
Changed files (5)
lib/std/Target/wasm.zig
@@ -13,6 +13,7 @@ pub const Feature = enum {
multimemory,
multivalue,
mutable_globals,
+ nontrapping_bulk_memory_len0,
nontrapping_fptoint,
reference_types,
relaxed_simd,
@@ -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",
src/arch/wasm/CodeGen.zig
@@ -1591,27 +1591,34 @@ 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)) {
- 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);
+ 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);
- try cg.endBlock();
+ if (!len0_ok) {
+ try cg.endBlock();
+ }
return;
}
@@ -4800,26 +4807,32 @@ 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) {
- 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);
+ 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);
- try cg.endBlock();
+ if (!len0_ok) {
+ try cg.endBlock();
+ }
return;
}
src/link/Wasm.zig
@@ -2826,6 +2826,7 @@ pub const Feature = packed struct(u8) {
multimemory,
multivalue,
@"mutable-globals",
+ @"nontrapping-bulk-memory-len0",
@"nontrapping-fptoint",
@"reference-types",
@"relaxed-simd",
tools/update_cpu_features.zig
@@ -1033,6 +1033,13 @@ 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,
build.zig
@@ -598,9 +598,10 @@ fn addWasiUpdateStep(b: *std.Build, version: [:0]const u8) !void {
.optimize = .ReleaseSmall,
.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_fptoint` is not supported by `wasm2c`.
- .cpu_features = "baseline-extended_const-nontrapping_fptoint",
+ // * `extended_const` is not supported by the `wasm-opt` version in CI.
+ // * `nontrapping_fptoint` is not supported by `wasm2c`.
+ // * `nontrapping_bulk_memory_len0` is supported by `wasm2c`.
+ .cpu_features = "baseline-extended_const-nontrapping_fptoint+nontrapping_bulk_memory_len0",
}) catch unreachable),
});