| ... | @@ -9766,14 +9766,6 @@ pub const FuncGen = struct { | ... | @@ -9766,14 +9766,6 @@ pub const FuncGen = struct { |
| 9766 | const access_kind: Builder.MemoryAccessKind = | 9766 | const access_kind: Builder.MemoryAccessKind = |
| 9767 | if (ptr_ty.isVolatilePtr(zcu)) .@"volatile" else .normal; | 9767 | if (ptr_ty.isVolatilePtr(zcu)) .@"volatile" else .normal; |
| 9768 | | 9768 | |
| 9769 | // Any WebAssembly runtime will trap when the destination pointer is out-of-bounds, regardless | | |
| 9770 | // of the length. This means we need to emit a check where we skip the memset when the length | | |
| 9771 | // is 0 as we allow for undefined pointers in 0-sized slices. | | |
| 9772 | // This logic can be removed once https://github.com/ziglang/zig/issues/16360 is done. | | |
| 9773 | const intrinsic_len0_traps = o.target.cpu.arch.isWasm() and | | |
| 9774 | ptr_ty.isSlice(zcu) and | | |
| 9775 | std.Target.wasm.featureSetHas(o.target.cpu.features, .bulk_memory); | | |
| 9776 | | | |
| 9777 | if (try self.air.value(bin_op.rhs, pt)) |elem_val| { | 9769 | if (try self.air.value(bin_op.rhs, pt)) |elem_val| { |
| 9778 | if (elem_val.isUndefDeep(zcu)) { | 9770 | if (elem_val.isUndefDeep(zcu)) { |
| 9779 | // Even if safety is disabled, we still emit a memset to undefined since it conveys | 9771 | // Even if safety is disabled, we still emit a memset to undefined since it conveys |
| ... | @@ -9784,24 +9776,14 @@ pub const FuncGen = struct { | ... | @@ -9784,24 +9776,14 @@ pub const FuncGen = struct { |
| 9784 | else | 9776 | else |
| 9785 | try o.builder.undefValue(.i8); | 9777 | try o.builder.undefValue(.i8); |
| 9786 | const len = try self.sliceOrArrayLenInBytes(dest_slice, ptr_ty); | 9778 | const len = try self.sliceOrArrayLenInBytes(dest_slice, ptr_ty); |
| 9787 | if (intrinsic_len0_traps) { | 9779 | _ = try self.wip.callMemSet( |
| 9788 | try self.safeWasmMemset( | 9780 | dest_ptr, |
| 9789 | dest_ptr, | 9781 | dest_ptr_align, |
| 9790 | fill_byte, | 9782 | fill_byte, |
| 9791 | len, | 9783 | len, |
| 9792 | dest_ptr_align, | 9784 | access_kind, |
| 9793 | access_kind, | 9785 | self.disable_intrinsics, |
| 9794 | ); | 9786 | ); |
| 9795 | } else { | | |
| 9796 | _ = try self.wip.callMemSet( | | |
| 9797 | dest_ptr, | | |
| 9798 | dest_ptr_align, | | |
| 9799 | fill_byte, | | |
| 9800 | len, | | |
| 9801 | access_kind, | | |
| 9802 | self.disable_intrinsics, | | |
| 9803 | ); | | |
| 9804 | } | | |
| 9805 | const owner_mod = self.ng.ownerModule(); | 9787 | const owner_mod = self.ng.ownerModule(); |
| 9806 | if (safety and owner_mod.valgrind) { | 9788 | if (safety and owner_mod.valgrind) { |
| 9807 | try self.valgrindMarkUndef(dest_ptr, len); | 9789 | try self.valgrindMarkUndef(dest_ptr, len); |
| ... | @@ -9816,24 +9798,14 @@ pub const FuncGen = struct { | ... | @@ -9816,24 +9798,14 @@ pub const FuncGen = struct { |
| 9816 | if (try elem_val.hasRepeatedByteRepr(pt)) |byte_val| { | 9798 | if (try elem_val.hasRepeatedByteRepr(pt)) |byte_val| { |
| 9817 | const fill_byte = try o.builder.intValue(.i8, byte_val); | 9799 | const fill_byte = try o.builder.intValue(.i8, byte_val); |
| 9818 | const len = try self.sliceOrArrayLenInBytes(dest_slice, ptr_ty); | 9800 | const len = try self.sliceOrArrayLenInBytes(dest_slice, ptr_ty); |
| 9819 | if (intrinsic_len0_traps) { | 9801 | _ = try self.wip.callMemSet( |
| 9820 | try self.safeWasmMemset( | 9802 | dest_ptr, |
| 9821 | dest_ptr, | 9803 | dest_ptr_align, |
| 9822 | fill_byte, | 9804 | fill_byte, |
| 9823 | len, | 9805 | len, |
| 9824 | dest_ptr_align, | 9806 | access_kind, |
| 9825 | access_kind, | 9807 | self.disable_intrinsics, |
| 9826 | ); | 9808 | ); |
| 9827 | } else { | | |
| 9828 | _ = try self.wip.callMemSet( | | |
| 9829 | dest_ptr, | | |
| 9830 | dest_ptr_align, | | |
| 9831 | fill_byte, | | |
| 9832 | len, | | |
| 9833 | access_kind, | | |
| 9834 | self.disable_intrinsics, | | |
| 9835 | ); | | |
| 9836 | } | | |
| 9837 | return .none; | 9809 | return .none; |
| 9838 | } | 9810 | } |
| 9839 | } | 9811 | } |
| ... | @@ -9846,24 +9818,14 @@ pub const FuncGen = struct { | ... | @@ -9846,24 +9818,14 @@ pub const FuncGen = struct { |
| 9846 | const fill_byte = try self.bitCast(value, elem_ty, Type.u8); | 9818 | const fill_byte = try self.bitCast(value, elem_ty, Type.u8); |
| 9847 | const len = try self.sliceOrArrayLenInBytes(dest_slice, ptr_ty); | 9819 | const len = try self.sliceOrArrayLenInBytes(dest_slice, ptr_ty); |
| 9848 | | 9820 | |
| 9849 | if (intrinsic_len0_traps) { | 9821 | _ = try self.wip.callMemSet( |
| 9850 | try self.safeWasmMemset( | 9822 | dest_ptr, |
| 9851 | dest_ptr, | 9823 | dest_ptr_align, |
| 9852 | fill_byte, | 9824 | fill_byte, |
| 9853 | len, | 9825 | len, |
| 9854 | dest_ptr_align, | 9826 | access_kind, |
| 9855 | access_kind, | 9827 | self.disable_intrinsics, |
| 9856 | ); | 9828 | ); |
| 9857 | } else { | | |
| 9858 | _ = try self.wip.callMemSet( | | |
| 9859 | dest_ptr, | | |
| 9860 | dest_ptr_align, | | |
| 9861 | fill_byte, | | |
| 9862 | len, | | |
| 9863 | access_kind, | | |
| 9864 | self.disable_intrinsics, | | |
| 9865 | ); | | |
| 9866 | } | | |
| 9867 | return .none; | 9829 | return .none; |
| 9868 | } | 9830 | } |
| 9869 | | 9831 | |
| ... | @@ -9927,33 +9889,6 @@ pub const FuncGen = struct { | ... | @@ -9927,33 +9889,6 @@ pub const FuncGen = struct { |
| 9927 | return .none; | 9889 | return .none; |
| 9928 | } | 9890 | } |
| 9929 | | 9891 | |
| 9930 | fn safeWasmMemset( | | |
| 9931 | self: *FuncGen, | | |
| 9932 | dest_ptr: Builder.Value, | | |
| 9933 | fill_byte: Builder.Value, | | |
| 9934 | len: Builder.Value, | | |
| 9935 | dest_ptr_align: Builder.Alignment, | | |
| 9936 | access_kind: Builder.MemoryAccessKind, | | |
| 9937 | ) !void { | | |
| 9938 | const o = self.ng.object; | | |
| 9939 | const usize_zero = try o.builder.intValue(try o.lowerType(Type.usize), 0); | | |
| 9940 | const cond = try self.cmp(.normal, .neq, Type.usize, len, usize_zero); | | |
| 9941 | const memset_block = try self.wip.block(1, "MemsetTrapSkip"); | | |
| 9942 | const end_block = try self.wip.block(2, "MemsetTrapEnd"); | | |
| 9943 | _ = try self.wip.brCond(cond, memset_block, end_block, .none); | | |
| 9944 | self.wip.cursor = .{ .block = memset_block }; | | |
| 9945 | _ = try self.wip.callMemSet( | | |
| 9946 | dest_ptr, | | |
| 9947 | dest_ptr_align, | | |
| 9948 | fill_byte, | | |
| 9949 | len, | | |
| 9950 | access_kind, | | |
| 9951 | self.disable_intrinsics, | | |
| 9952 | ); | | |
| 9953 | _ = try self.wip.br(end_block); | | |
| 9954 | self.wip.cursor = .{ .block = end_block }; | | |
| 9955 | } | | |
| 9956 | | | |
| 9957 | fn airMemcpy(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | 9892 | fn airMemcpy(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 9958 | const o = self.ng.object; | 9893 | const o = self.ng.object; |
| 9959 | const pt = o.pt; | 9894 | const pt = o.pt; |
| ... | @@ -9969,35 +9904,6 @@ pub const FuncGen = struct { | ... | @@ -9969,35 +9904,6 @@ pub const FuncGen = struct { |
| 9969 | const access_kind: Builder.MemoryAccessKind = if (src_ptr_ty.isVolatilePtr(zcu) or | 9904 | const access_kind: Builder.MemoryAccessKind = if (src_ptr_ty.isVolatilePtr(zcu) or |
| 9970 | dest_ptr_ty.isVolatilePtr(zcu)) .@"volatile" else .normal; | 9905 | dest_ptr_ty.isVolatilePtr(zcu)) .@"volatile" else .normal; |
| 9971 | | 9906 | |
| 9972 | // When bulk-memory is enabled, this will be lowered to WebAssembly's memory.copy instruction. | | |
| 9973 | // This instruction will trap on an invalid address, regardless of the length. | | |
| 9974 | // For this reason we must add a check for 0-sized slices as its pointer field can be undefined. | | |
| 9975 | // We only have to do this for slices as arrays will have a valid pointer. | | |
| 9976 | // This logic can be removed once https://github.com/ziglang/zig/issues/16360 is done. | | |
| 9977 | if (o.target.cpu.arch.isWasm() and | | |
| 9978 | std.Target.wasm.featureSetHas(o.target.cpu.features, .bulk_memory) and | | |
| 9979 | dest_ptr_ty.isSlice(zcu)) | | |
| 9980 | { | | |
| 9981 | const usize_zero = try o.builder.intValue(try o.lowerType(Type.usize), 0); | | |
| 9982 | const cond = try self.cmp(.normal, .neq, Type.usize, len, usize_zero); | | |
| 9983 | const memcpy_block = try self.wip.block(1, "MemcpyTrapSkip"); | | |
| 9984 | const end_block = try self.wip.block(2, "MemcpyTrapEnd"); | | |
| 9985 | _ = try self.wip.brCond(cond, memcpy_block, end_block, .none); | | |
| 9986 | self.wip.cursor = .{ .block = memcpy_block }; | | |
| 9987 | _ = try self.wip.callMemCpy( | | |
| 9988 | dest_ptr, | | |
| 9989 | dest_ptr_ty.ptrAlignment(zcu).toLlvm(), | | |
| 9990 | src_ptr, | | |
| 9991 | src_ptr_ty.ptrAlignment(zcu).toLlvm(), | | |
| 9992 | len, | | |
| 9993 | access_kind, | | |
| 9994 | self.disable_intrinsics, | | |
| 9995 | ); | | |
| 9996 | _ = try self.wip.br(end_block); | | |
| 9997 | self.wip.cursor = .{ .block = end_block }; | | |
| 9998 | return .none; | | |
| 9999 | } | | |
| 10000 | | | |
| 10001 | _ = try self.wip.callMemCpy( | 9907 | _ = try self.wip.callMemCpy( |
| 10002 | dest_ptr, | 9908 | dest_ptr, |
| 10003 | dest_ptr_ty.ptrAlignment(zcu).toLlvm(), | 9909 | dest_ptr_ty.ptrAlignment(zcu).toLlvm(), |