| ... | ... | @@ -8511,6 +8511,14 @@ pub const FuncGen = struct { |
| 8511 | 8511 | const dest_ptr = self.sliceOrArrayPtr(dest_slice, ptr_ty); |
| 8512 | 8512 | const is_volatile = ptr_ty.isVolatilePtr(mod); |
| 8513 | 8513 | |
| 8514 | // Any WebAssembly runtime will trap when the destination pointer is out-of-bounds, regardless |
| 8515 | // of the length. This means we need to emit a check where we skip the memset when the length |
| 8516 | // is 0 as we allow for undefined pointers in 0-sized slices. |
| 8517 | // This logic can be removed once https://github.com/ziglang/zig/issues/16360 is done. |
| 8518 | const intrinsic_len0_traps = o.target.isWasm() and |
| 8519 | ptr_ty.isSlice(mod) and |
| 8520 | std.Target.wasm.featureSetHas(o.target.cpu.features, .bulk_memory); |
| 8521 | |
| 8514 | 8522 | if (try self.air.value(bin_op.rhs, mod)) |elem_val| { |
| 8515 | 8523 | if (elem_val.isUndefDeep(mod)) { |
| 8516 | 8524 | // Even if safety is disabled, we still emit a memset to undefined since it conveys |
| ... | ... | @@ -8521,7 +8529,11 @@ pub const FuncGen = struct { |
| 8521 | 8529 | else |
| 8522 | 8530 | u8_llvm_ty.getUndef(); |
| 8523 | 8531 | const len = self.sliceOrArrayLenInBytes(dest_slice, ptr_ty); |
| 8524 | | _ = self.builder.buildMemSet(dest_ptr, fill_byte, len, dest_ptr_align, is_volatile); |
| 8532 | if (intrinsic_len0_traps) { |
| 8533 | try self.safeWasmMemset(dest_ptr, fill_byte, len, dest_ptr_align, is_volatile); |
| 8534 | } else { |
| 8535 | _ = self.builder.buildMemSet(dest_ptr, fill_byte, len, dest_ptr_align, is_volatile); |
| 8536 | } |
| 8525 | 8537 | |
| 8526 | 8538 | if (safety and mod.comp.bin_file.options.valgrind) { |
| 8527 | 8539 | self.valgrindMarkUndef(dest_ptr, len); |
| ... | ... | @@ -8539,7 +8551,12 @@ pub const FuncGen = struct { |
| 8539 | 8551 | .val = byte_val, |
| 8540 | 8552 | }); |
| 8541 | 8553 | const len = self.sliceOrArrayLenInBytes(dest_slice, ptr_ty); |
| 8542 | | _ = self.builder.buildMemSet(dest_ptr, fill_byte, len, dest_ptr_align, is_volatile); |
| 8554 | |
| 8555 | if (intrinsic_len0_traps) { |
| 8556 | try self.safeWasmMemset(dest_ptr, fill_byte, len, dest_ptr_align, is_volatile); |
| 8557 | } else { |
| 8558 | _ = self.builder.buildMemSet(dest_ptr, fill_byte, len, dest_ptr_align, is_volatile); |
| 8559 | } |
| 8543 | 8560 | return null; |
| 8544 | 8561 | } |
| 8545 | 8562 | } |
| ... | ... | @@ -8551,7 +8568,12 @@ pub const FuncGen = struct { |
| 8551 | 8568 | // In this case we can take advantage of LLVM's intrinsic. |
| 8552 | 8569 | const fill_byte = try self.bitCast(value, elem_ty, Type.u8); |
| 8553 | 8570 | const len = self.sliceOrArrayLenInBytes(dest_slice, ptr_ty); |
| 8554 | | _ = self.builder.buildMemSet(dest_ptr, fill_byte, len, dest_ptr_align, is_volatile); |
| 8571 | |
| 8572 | if (intrinsic_len0_traps) { |
| 8573 | try self.safeWasmMemset(dest_ptr, fill_byte, len, dest_ptr_align, is_volatile); |
| 8574 | } else { |
| 8575 | _ = self.builder.buildMemSet(dest_ptr, fill_byte, len, dest_ptr_align, is_volatile); |
| 8576 | } |
| 8555 | 8577 | return null; |
| 8556 | 8578 | } |
| 8557 | 8579 | |
| ... | ... | @@ -8622,6 +8644,25 @@ pub const FuncGen = struct { |
| 8622 | 8644 | return null; |
| 8623 | 8645 | } |
| 8624 | 8646 | |
| 8647 | fn safeWasmMemset( |
| 8648 | self: *FuncGen, |
| 8649 | dest_ptr: *llvm.Value, |
| 8650 | fill_byte: *llvm.Value, |
| 8651 | len: *llvm.Value, |
| 8652 | dest_ptr_align: u32, |
| 8653 | is_volatile: bool, |
| 8654 | ) !void { |
| 8655 | const llvm_usize_ty = self.context.intType(self.dg.object.target.ptrBitWidth()); |
| 8656 | const cond = try self.cmp(len, llvm_usize_ty.constInt(0, .False), Type.usize, .neq); |
| 8657 | const memset_block = self.context.appendBasicBlock(self.llvm_func, "MemsetTrapSkip"); |
| 8658 | const end_block = self.context.appendBasicBlock(self.llvm_func, "MemsetTrapEnd"); |
| 8659 | _ = self.builder.buildCondBr(cond, memset_block, end_block); |
| 8660 | self.builder.positionBuilderAtEnd(memset_block); |
| 8661 | _ = self.builder.buildMemSet(dest_ptr, fill_byte, len, dest_ptr_align, is_volatile); |
| 8662 | _ = self.builder.buildBr(end_block); |
| 8663 | self.builder.positionBuilderAtEnd(end_block); |
| 8664 | } |
| 8665 | |
| 8625 | 8666 | fn airMemcpy(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 8626 | 8667 | const o = self.dg.object; |
| 8627 | 8668 | const mod = o.module; |
| ... | ... | @@ -8634,6 +8675,35 @@ pub const FuncGen = struct { |
| 8634 | 8675 | const len = self.sliceOrArrayLenInBytes(dest_slice, dest_ptr_ty); |
| 8635 | 8676 | const dest_ptr = self.sliceOrArrayPtr(dest_slice, dest_ptr_ty); |
| 8636 | 8677 | const is_volatile = src_ptr_ty.isVolatilePtr(mod) or dest_ptr_ty.isVolatilePtr(mod); |
| 8678 | |
| 8679 | // When bulk-memory is enabled, this will be lowered to WebAssembly's memory.copy instruction. |
| 8680 | // This instruction will trap on an invalid address, regardless of the length. |
| 8681 | // For this reason we must add a check for 0-sized slices as its pointer field can be undefined. |
| 8682 | // We only have to do this for slices as arrays will have a valid pointer. |
| 8683 | // This logic can be removed once https://github.com/ziglang/zig/issues/16360 is done. |
| 8684 | if (o.target.isWasm() and |
| 8685 | std.Target.wasm.featureSetHas(o.target.cpu.features, .bulk_memory) and |
| 8686 | dest_ptr_ty.isSlice(mod)) |
| 8687 | { |
| 8688 | const llvm_usize_ty = self.context.intType(self.dg.object.target.ptrBitWidth()); |
| 8689 | const cond = try self.cmp(len, llvm_usize_ty.constInt(0, .False), Type.usize, .neq); |
| 8690 | const memcpy_block = self.context.appendBasicBlock(self.llvm_func, "MemcpyTrapSkip"); |
| 8691 | const end_block = self.context.appendBasicBlock(self.llvm_func, "MemcpyTrapEnd"); |
| 8692 | _ = self.builder.buildCondBr(cond, memcpy_block, end_block); |
| 8693 | self.builder.positionBuilderAtEnd(memcpy_block); |
| 8694 | _ = self.builder.buildMemCpy( |
| 8695 | dest_ptr, |
| 8696 | dest_ptr_ty.ptrAlignment(mod), |
| 8697 | src_ptr, |
| 8698 | src_ptr_ty.ptrAlignment(mod), |
| 8699 | len, |
| 8700 | is_volatile, |
| 8701 | ); |
| 8702 | _ = self.builder.buildBr(end_block); |
| 8703 | self.builder.positionBuilderAtEnd(end_block); |
| 8704 | return null; |
| 8705 | } |
| 8706 | |
| 8637 | 8707 | _ = self.builder.buildMemCpy( |
| 8638 | 8708 | dest_ptr, |
| 8639 | 8709 | dest_ptr_ty.ptrAlignment(mod), |