authorgravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2025-01-22 02:56:53+01:00
committergravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2025-01-22 20:56:28+01:00
logea1502974dd991c0ed4c58dd54fb96cf7080f293
tree00d3b226ba78e1c2864df26e038512c03708668b
parent280ced66eb712f5c74835b10fa6ba0bd915ee96b
signaturebadge-check Signed by SSH key SHA256:7B/LJ7bpR1eX8aCXSr4mtd5M45VMPKcx9zY8e95b5QM

wasm: Add a nontrapping_bulk_memory_len0 feature.

This will mainly be used when targeting our wasm2c implementation which has no problem with zero-length bulk memory operations, as a non-standard extension.

5 files changed, 62 insertions(+), 32 deletions(-)

build.zig+4-3
......@@ -598,9 +598,10 @@ fn addWasiUpdateStep(b: *std.Build, version: [:0]const u8) !void {
598598 .optimize = .ReleaseSmall,
599599 .target = b.resolveTargetQuery(std.Target.Query.parse(.{
600600 .arch_os_abi = "wasm32-wasi",
601 // `extended_const` is not supported by the `wasm-opt` version in CI.
602 // `nontrapping_fptoint` is not supported by `wasm2c`.
603 .cpu_features = "baseline-extended_const-nontrapping_fptoint",
601 // * `extended_const` is not supported by the `wasm-opt` version in CI.
602 // * `nontrapping_fptoint` is not supported by `wasm2c`.
603 // * `nontrapping_bulk_memory_len0` is supported by `wasm2c`.
604 .cpu_features = "baseline-extended_const-nontrapping_fptoint+nontrapping_bulk_memory_len0",
604605 }) catch unreachable),
605606 });
606607
lib/std/Target/wasm.zig+8
......@@ -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",
src/arch/wasm/CodeGen.zig+42-29
......@@ -1591,27 +1591,34 @@ 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 try cg.startBlock(.block, .empty);
1595
1596 // Even if `len` is zero, the spec requires an implementation to trap if `src + len` or
1597 // `dst + len` are out of memory bounds. This can easily happen in Zig in a case such as:
1598 //
1599 // const dst: [*]u8 = undefined;
1600 // const src: [*]u8 = undefined;
1601 // var len: usize = runtime_zero();
1602 // @memcpy(dst[0..len], src[0..len]);
1603 //
1604 // So explicitly avoid using `memory.copy` in the `len == 0` case. Lovely design.
1605 try cg.emitWValue(len);
1606 try cg.addTag(.i32_eqz);
1607 try cg.addLabel(.br_if, 0);
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 }
16081613
16091614 try cg.lowerToStack(dst);
16101615 try cg.lowerToStack(src);
16111616 try cg.emitWValue(len);
16121617 try cg.addExtended(.memory_copy);
16131618
1614 try cg.endBlock();
1619 if (!len0_ok) {
1620 try cg.endBlock();
1621 }
16151622
16161623 return;
16171624 }
......@@ -4800,26 +4807,32 @@ fn memset(cg: *CodeGen, elem_ty: Type, ptr: WValue, len: WValue, value: WValue)
48004807 // When bulk_memory is enabled, we lower it to wasm's memset instruction.
48014808 // If not, we lower it ourselves.
48024809 if (std.Target.wasm.featureSetHas(cg.target.cpu.features, .bulk_memory) and abi_size == 1) {
4803 try cg.startBlock(.block, .empty);
4804
4805 // Even if `len` is zero, the spec requires an implementation to trap if `ptr + len` is
4806 // out of memory bounds. This can easily happen in Zig in a case such as:
4807 //
4808 // const ptr: [*]u8 = undefined;
4809 // var len: usize = runtime_zero();
4810 // @memset(ptr[0..len], 42);
4811 //
4812 // So explicitly avoid using `memory.fill` in the `len == 0` case. Lovely design.
4813 try cg.emitWValue(len);
4814 try cg.addTag(.i32_eqz);
4815 try cg.addLabel(.br_if, 0);
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 }
48164827
48174828 try cg.lowerToStack(ptr);
48184829 try cg.emitWValue(value);
48194830 try cg.emitWValue(len);
48204831 try cg.addExtended(.memory_fill);
48214832
4822 try cg.endBlock();
4833 if (!len0_ok) {
4834 try cg.endBlock();
4835 }
48234836
48244837 return;
48254838 }
src/link/Wasm.zig+1
......@@ -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",
tools/update_cpu_features.zig+7
......@@ -1033,6 +1033,13 @@ 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 },
10361043 .extra_cpus = &.{
10371044 .{
10381045 .llvm_name = null,