| ... | @@ -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 manually | 1592 | // 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); |
| 1595 | | 1595 | |
| 1596 | // Even if `len` is zero, the spec requires an implementation to trap if `src + len` or | 1596 | 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 | } |
| 1608 | | 1613 | |
| 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); |
| 1613 | | 1618 | |
| 1614 | try cg.endBlock(); | 1619 | if (!len0_ok) { |
| | 1620 | try cg.endBlock(); |
| | 1621 | } |
| 1615 | | 1622 | |
| 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); |
| 4804 | | 4811 | |
| 4805 | // Even if `len` is zero, the spec requires an implementation to trap if `ptr + len` is | 4812 | 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 | } |
| 4816 | | 4827 | |
| 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); |
| 4821 | | 4832 | |
| 4822 | try cg.endBlock(); | 4833 | if (!len0_ok) { |
| | 4834 | try cg.endBlock(); |
| | 4835 | } |
| 4823 | | 4836 | |
| 4824 | return; | 4837 | return; |
| 4825 | } | 4838 | } |