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 {...@@ -598,9 +598,10 @@ fn addWasiUpdateStep(b: *std.Build, version: [:0]const u8) !void {
598 .optimize = .ReleaseSmall,598 .optimize = .ReleaseSmall,
599 .target = b.resolveTargetQuery(std.Target.Query.parse(.{599 .target = b.resolveTargetQuery(std.Target.Query.parse(.{
600 .arch_os_abi = "wasm32-wasi",600 .arch_os_abi = "wasm32-wasi",
601 // `extended_const` is not supported by the `wasm-opt` version in CI.601 // * `extended_const` is not supported by the `wasm-opt` version in CI.
602 // `nontrapping_fptoint` is not supported by `wasm2c`.602 // * `nontrapping_fptoint` is not supported by `wasm2c`.
603 .cpu_features = "baseline-extended_const-nontrapping_fptoint",603 // * `nontrapping_bulk_memory_len0` is supported by `wasm2c`.
604 .cpu_features = "baseline-extended_const-nontrapping_fptoint+nontrapping_bulk_memory_len0",
604 }) catch unreachable),605 }) catch unreachable),
605 });606 });
606607
lib/std/Target/wasm.zig+8
...@@ -13,6 +13,7 @@ pub const Feature = enum {...@@ -13,6 +13,7 @@ pub const Feature = enum {
13 multimemory,13 multimemory,
14 multivalue,14 multivalue,
15 mutable_globals,15 mutable_globals,
16 nontrapping_bulk_memory_len0,
16 nontrapping_fptoint,17 nontrapping_fptoint,
17 reference_types,18 reference_types,
18 relaxed_simd,19 relaxed_simd,
...@@ -70,6 +71,13 @@ pub const all_features = blk: {...@@ -70,6 +71,13 @@ pub const all_features = blk: {
70 .description = "Enable mutable globals",71 .description = "Enable mutable globals",
71 .dependencies = featureSet(&[_]Feature{}),72 .dependencies = featureSet(&[_]Feature{}),
72 };73 };
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 };
73 result[@intFromEnum(Feature.nontrapping_fptoint)] = .{81 result[@intFromEnum(Feature.nontrapping_fptoint)] = .{
74 .llvm_name = "nontrapping-fptoint",82 .llvm_name = "nontrapping-fptoint",
75 .description = "Enable non-trapping float-to-int conversion operators",83 .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 {...@@ -1591,27 +1591,34 @@ fn memcpy(cg: *CodeGen, dst: WValue, src: WValue, len: WValue) !void {
1591 // When bulk_memory is enabled, we lower it to wasm's memcpy instruction.1591 // When bulk_memory is enabled, we lower it to wasm's memcpy instruction.
1592 // If not, we lower it ourselves manually1592 // If not, we lower it ourselves manually
1593 if (std.Target.wasm.featureSetHas(cg.target.cpu.features, .bulk_memory)) {1593 if (std.Target.wasm.featureSetHas(cg.target.cpu.features, .bulk_memory)) {
1594 try cg.startBlock(.block, .empty);1594 const len0_ok = std.Target.wasm.featureSetHas(cg.target.cpu.features, .nontrapping_bulk_memory_len0);
15951595
1596 // Even if `len` is zero, the spec requires an implementation to trap if `src + len` or1596 if (!len0_ok) {
1597 // `dst + len` are out of memory bounds. This can easily happen in Zig in a case such as:1597 try cg.startBlock(.block, .empty);
1598 //1598
1599 // const dst: [*]u8 = undefined;1599 // Even if `len` is zero, the spec requires an implementation to trap if `src + len` or
1600 // const src: [*]u8 = undefined;1600 // `dst + len` are out of memory bounds. This can easily happen in Zig in a case such
1601 // var len: usize = runtime_zero();1601 // as:
1602 // @memcpy(dst[0..len], src[0..len]);1602 //
1603 //1603 // const dst: [*]u8 = undefined;
1604 // So explicitly avoid using `memory.copy` in the `len == 0` case. Lovely design.1604 // const src: [*]u8 = undefined;
1605 try cg.emitWValue(len);1605 // var len: usize = runtime_zero();
1606 try cg.addTag(.i32_eqz);1606 // @memcpy(dst[0..len], src[0..len]);
1607 try cg.addLabel(.br_if, 0);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
1609 try cg.lowerToStack(dst);1614 try cg.lowerToStack(dst);
1610 try cg.lowerToStack(src);1615 try cg.lowerToStack(src);
1611 try cg.emitWValue(len);1616 try cg.emitWValue(len);
1612 try cg.addExtended(.memory_copy);1617 try cg.addExtended(.memory_copy);
16131618
1614 try cg.endBlock();1619 if (!len0_ok) {
1620 try cg.endBlock();
1621 }
16151622
1616 return;1623 return;
1617 }1624 }
...@@ -4800,26 +4807,32 @@ fn memset(cg: *CodeGen, elem_ty: Type, ptr: WValue, len: WValue, value: WValue)...@@ -4800,26 +4807,32 @@ fn memset(cg: *CodeGen, elem_ty: Type, ptr: WValue, len: WValue, value: WValue)
4800 // When bulk_memory is enabled, we lower it to wasm's memset instruction.4807 // When bulk_memory is enabled, we lower it to wasm's memset instruction.
4801 // If not, we lower it ourselves.4808 // If not, we lower it ourselves.
4802 if (std.Target.wasm.featureSetHas(cg.target.cpu.features, .bulk_memory) and abi_size == 1) {4809 if (std.Target.wasm.featureSetHas(cg.target.cpu.features, .bulk_memory) and abi_size == 1) {
4803 try cg.startBlock(.block, .empty);4810 const len0_ok = std.Target.wasm.featureSetHas(cg.target.cpu.features, .nontrapping_bulk_memory_len0);
48044811
4805 // Even if `len` is zero, the spec requires an implementation to trap if `ptr + len` is4812 if (!len0_ok) {
4806 // out of memory bounds. This can easily happen in Zig in a case such as:4813 try cg.startBlock(.block, .empty);
4807 //4814
4808 // const ptr: [*]u8 = undefined;4815 // Even if `len` is zero, the spec requires an implementation to trap if `ptr + len` is
4809 // var len: usize = runtime_zero();4816 // out of memory bounds. This can easily happen in Zig in a case such as:
4810 // @memset(ptr[0..len], 42);4817 //
4811 //4818 // const ptr: [*]u8 = undefined;
4812 // So explicitly avoid using `memory.fill` in the `len == 0` case. Lovely design.4819 // var len: usize = runtime_zero();
4813 try cg.emitWValue(len);4820 // @memset(ptr[0..len], 42);
4814 try cg.addTag(.i32_eqz);4821 //
4815 try cg.addLabel(.br_if, 0);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
4817 try cg.lowerToStack(ptr);4828 try cg.lowerToStack(ptr);
4818 try cg.emitWValue(value);4829 try cg.emitWValue(value);
4819 try cg.emitWValue(len);4830 try cg.emitWValue(len);
4820 try cg.addExtended(.memory_fill);4831 try cg.addExtended(.memory_fill);
48214832
4822 try cg.endBlock();4833 if (!len0_ok) {
4834 try cg.endBlock();
4835 }
48234836
4824 return;4837 return;
4825 }4838 }
src/link/Wasm.zig+1
...@@ -2826,6 +2826,7 @@ pub const Feature = packed struct(u8) {...@@ -2826,6 +2826,7 @@ pub const Feature = packed struct(u8) {
2826 multimemory,2826 multimemory,
2827 multivalue,2827 multivalue,
2828 @"mutable-globals",2828 @"mutable-globals",
2829 @"nontrapping-bulk-memory-len0",
2829 @"nontrapping-fptoint",2830 @"nontrapping-fptoint",
2830 @"reference-types",2831 @"reference-types",
2831 @"relaxed-simd",2832 @"relaxed-simd",
tools/update_cpu_features.zig+7
...@@ -1033,6 +1033,13 @@ const llvm_targets = [_]LlvmTarget{...@@ -1033,6 +1033,13 @@ const llvm_targets = [_]LlvmTarget{
1033 .zig_name = "wasm",1033 .zig_name = "wasm",
1034 .llvm_name = "WebAssembly",1034 .llvm_name = "WebAssembly",
1035 .td_name = "WebAssembly.td",1035 .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 },
1036 .extra_cpus = &.{1043 .extra_cpus = &.{
1037 .{1044 .{
1038 .llvm_name = null,1045 .llvm_name = null,