| ... | @@ -1591,10 +1591,28 @@ fn memcpy(cg: *CodeGen, dst: WValue, src: WValue, len: WValue) !void { | ... | @@ -1591,10 +1591,28 @@ 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); |
| | 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); |
| | 1608 | |
| 1594 | try cg.lowerToStack(dst); | 1609 | try cg.lowerToStack(dst); |
| 1595 | try cg.lowerToStack(src); | 1610 | try cg.lowerToStack(src); |
| 1596 | try cg.emitWValue(len); | 1611 | try cg.emitWValue(len); |
| 1597 | try cg.addExtended(.memory_copy); | 1612 | try cg.addExtended(.memory_copy); |
| | 1613 | |
| | 1614 | try cg.endBlock(); |
| | 1615 | |
| 1598 | return; | 1616 | return; |
| 1599 | } | 1617 | } |
| 1600 | | 1618 | |
| ... | @@ -4782,10 +4800,27 @@ fn memset(cg: *CodeGen, elem_ty: Type, ptr: WValue, len: WValue, value: WValue) | ... | @@ -4782,10 +4800,27 @@ fn memset(cg: *CodeGen, elem_ty: Type, ptr: WValue, len: WValue, value: WValue) |
| 4782 | // When bulk_memory is enabled, we lower it to wasm's memset instruction. | 4800 | // When bulk_memory is enabled, we lower it to wasm's memset instruction. |
| 4783 | // If not, we lower it ourselves. | 4801 | // If not, we lower it ourselves. |
| 4784 | if (std.Target.wasm.featureSetHas(cg.target.cpu.features, .bulk_memory) and abi_size == 1) { | 4802 | 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); |
| | 4816 | |
| 4785 | try cg.lowerToStack(ptr); | 4817 | try cg.lowerToStack(ptr); |
| 4786 | try cg.emitWValue(value); | 4818 | try cg.emitWValue(value); |
| 4787 | try cg.emitWValue(len); | 4819 | try cg.emitWValue(len); |
| 4788 | try cg.addExtended(.memory_fill); | 4820 | try cg.addExtended(.memory_fill); |
| | 4821 | |
| | 4822 | try cg.endBlock(); |
| | 4823 | |
| 4789 | return; | 4824 | return; |
| 4790 | } | 4825 | } |
| 4791 | | 4826 | |