| author | |
| committer | |
| log | edb5e493e6d9a4478b3d9c06aa590694757d8c03 |
| tree | 5d5ccca94129e3a0e177845dbb5a9b251271cd07 |
| parent | a5c910adb610ae530db99f10aa77aaed3e85e830 |
* Sema: upgrade operands to array pointers if possible when emitting
AIR.
* Implement safety checks for length mismatch and aliasing.
* AIR: make ptrtoint support slice operands. Implement in LLVM backend.
* C backend: implement new `@memset` semantics. `@memcpy` is not done
yet.12 files changed, 280 insertions(+), 78 deletions(-)
doc/langref.html.in+14-12| ... | ... | @@ -8683,18 +8683,20 @@ test "integer cast panic" { |
| 8683 | 8683 | {#header_open|@memcpy#} |
| 8684 | 8684 | <pre>{#syntax#}@memcpy(noalias dest, noalias source) void{#endsyntax#}</pre> |
| 8685 | 8685 | <p>This function copies bytes from one region of memory to another.</p> |
| 8686 | <p>{#syntax#}dest{#endsyntax#} must be a mutable slice, or a mutable pointer to an array. | |
| 8687 | It may have any alignment, and it may have any element type.</p> | |
| 8688 | <p>{#syntax#}source{#endsyntax#} must be an array, pointer, or a slice | |
| 8689 | with the same element type as {#syntax#}dest{#endsyntax#}. It may have | |
| 8690 | any alignment. Only {#syntax#}const{#endsyntax#} access is required. It | |
| 8691 | is sliced from 0 to the same length as | |
| 8692 | {#syntax#}dest{#endsyntax#}, triggering the same set of safety checks and | |
| 8693 | possible compile errors as | |
| 8694 | {#syntax#}source[0..dest.len]{#endsyntax#}.</p> | |
| 8695 | <p>It is illegal for {#syntax#}dest{#endsyntax#} and | |
| 8696 | {#syntax#}source[0..dest.len]{#endsyntax#} to overlap. If safety | |
| 8697 | checks are enabled, there will be a runtime check for such overlapping.</p> | |
| 8686 | <p>{#syntax#}dest{#endsyntax#} must be a mutable slice, a mutable pointer to an array, or | |
| 8687 | a mutable many-item {#link|pointer|Pointer#}. It may have any | |
| 8688 | alignment, and it may have any element type.</p> | |
| 8689 | <p>Likewise, {#syntax#}source{#endsyntax#} must be a mutable slice, a | |
| 8690 | mutable pointer to an array, or a mutable many-item | |
| 8691 | {#link|pointer|Pointer#}. It may have any alignment, and it may have any | |
| 8692 | element type.</p> | |
| 8693 | <p>The {#syntax#}source{#endsyntax#} element type must support {#link|Type Coercion#} | |
| 8694 | into the {#syntax#}dest{#endsyntax#} element type. The element types may have | |
| 8695 | different ABI size, however, that may incur a performance penalty.</p> | |
| 8696 | <p>Similar to {#link|for#} loops, at least one of {#syntax#}source{#endsyntax#} and | |
| 8697 | {#syntax#}dest{#endsyntax#} must provide a length, and if two lengths are provided, | |
| 8698 | they must be equal.</p> | |
| 8699 | <p>Finally, the two memory regions must not overlap.</p> | |
| 8698 | 8700 | {#header_close#} |
| 8699 | 8701 | |
| 8700 | 8702 | {#header_open|@memset#} |
lib/std/builtin.zig+2| ... | ... | @@ -1002,6 +1002,8 @@ pub const panic_messages = struct { |
| 1002 | 1002 | pub const index_out_of_bounds = "index out of bounds"; |
| 1003 | 1003 | pub const start_index_greater_than_end = "start index is larger than end index"; |
| 1004 | 1004 | pub const for_len_mismatch = "for loop over objects with non-equal lengths"; |
| 1005 | pub const memcpy_len_mismatch = "@memcpy arguments have non-equal lengths"; | |
| 1006 | pub const memcpy_alias = "@memcpy arguments alias"; | |
| 1005 | 1007 | }; |
| 1006 | 1008 | |
| 1007 | 1009 | pub noinline fn returnError(st: *StackTrace) void { |
lib/std/crypto/aegis.zig+2-2| ... | ... | @@ -209,7 +209,7 @@ fn Aegis128LGeneric(comptime tag_bits: u9) type { |
| 209 | 209 | acc |= (computed_tag[j] ^ tag[j]); |
| 210 | 210 | } |
| 211 | 211 | if (acc != 0) { |
| 212 | @memset(m.ptr, undefined, m.len); | |
| 212 | @memset(m, undefined); | |
| 213 | 213 | return error.AuthenticationFailed; |
| 214 | 214 | } |
| 215 | 215 | } |
| ... | ... | @@ -390,7 +390,7 @@ fn Aegis256Generic(comptime tag_bits: u9) type { |
| 390 | 390 | acc |= (computed_tag[j] ^ tag[j]); |
| 391 | 391 | } |
| 392 | 392 | if (acc != 0) { |
| 393 | @memset(m.ptr, undefined, m.len); | |
| 393 | @memset(m, undefined); | |
| 394 | 394 | return error.AuthenticationFailed; |
| 395 | 395 | } |
| 396 | 396 | } |
lib/std/hash/murmur.zig+5-6| ... | ... | @@ -99,9 +99,8 @@ pub const Murmur2_64 = struct { |
| 99 | 99 | |
| 100 | 100 | pub fn hashWithSeed(str: []const u8, seed: u64) u64 { |
| 101 | 101 | const m: u64 = 0xc6a4a7935bd1e995; |
| 102 | const len = @as(u64, str.len); | |
| 103 | var h1: u64 = seed ^ (len *% m); | |
| 104 | for (@ptrCast([*]align(1) const u64, str.ptr)[0..@intCast(usize, len >> 3)]) |v| { | |
| 102 | var h1: u64 = seed ^ (@as(u64, str.len) *% m); | |
| 103 | for (@ptrCast([*]align(1) const u64, str.ptr)[0..str.len / 8]) |v| { | |
| 105 | 104 | var k1: u64 = v; |
| 106 | 105 | if (native_endian == .Big) |
| 107 | 106 | k1 = @byteSwap(k1); |
| ... | ... | @@ -111,11 +110,11 @@ pub const Murmur2_64 = struct { |
| 111 | 110 | h1 ^= k1; |
| 112 | 111 | h1 *%= m; |
| 113 | 112 | } |
| 114 | const rest = len & 7; | |
| 115 | const offset = len - rest; | |
| 113 | const rest = str.len & 7; | |
| 114 | const offset = str.len - rest; | |
| 116 | 115 | if (rest > 0) { |
| 117 | 116 | var k1: u64 = 0; |
| 118 | @memcpy(@ptrCast([*]u8, &k1)[0..@intCast(usize, rest)], @ptrCast([*]const u8, &str[@intCast(usize, offset)])); | |
| 117 | @memcpy(@ptrCast([*]u8, &k1)[0..rest], str[offset..]); | |
| 119 | 118 | if (native_endian == .Big) |
| 120 | 119 | k1 = @byteSwap(k1); |
| 121 | 120 | h1 ^= k1; |
lib/std/mem/Allocator.zig+2-1| ... | ... | @@ -282,7 +282,8 @@ pub fn reallocAdvanced( |
| 282 | 282 | |
| 283 | 283 | const new_mem = self.rawAlloc(byte_count, log2a(Slice.alignment), return_address) orelse |
| 284 | 284 | return error.OutOfMemory; |
| 285 | @memcpy(new_mem[0..@min(byte_count, old_byte_slice.len)], old_byte_slice); | |
| 285 | const copy_len = @min(byte_count, old_byte_slice.len); | |
| 286 | @memcpy(new_mem[0..copy_len], old_byte_slice[0..copy_len]); | |
| 286 | 287 | // TODO https://github.com/ziglang/zig/issues/4298 |
| 287 | 288 | @memset(old_byte_slice, undefined); |
| 288 | 289 | self.rawFree(old_byte_slice, log2a(Slice.alignment), return_address); |
src/Air.zig+1| ... | ... | @@ -462,6 +462,7 @@ pub const Inst = struct { |
| 462 | 462 | /// Uses the `ty_op` field. |
| 463 | 463 | load, |
| 464 | 464 | /// Converts a pointer to its address. Result type is always `usize`. |
| 465 | /// Pointer type size may be any, including slice. | |
| 465 | 466 | /// Uses the `un_op` field. |
| 466 | 467 | ptrtoint, |
| 467 | 468 | /// Given a boolean, returns 0 or 1. |
src/AstGen.zig+1-1| ... | ... | @@ -8455,7 +8455,7 @@ fn builtinCall( |
| 8455 | 8455 | .memcpy => { |
| 8456 | 8456 | _ = try gz.addPlNode(.memcpy, node, Zir.Inst.Bin{ |
| 8457 | 8457 | .lhs = try expr(gz, scope, .{ .rl = .none }, params[0]), |
| 8458 | .rhs = try expr(gz, scope, .{ .rl = .ref }, params[1]), | |
| 8458 | .rhs = try expr(gz, scope, .{ .rl = .none }, params[1]), | |
| 8459 | 8459 | }); |
| 8460 | 8460 | return rvalue(gz, ri, .void_value, node); |
| 8461 | 8461 | }, |
src/Liveness/Verify.zig+2-7| ... | ... | @@ -254,6 +254,8 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void { |
| 254 | 254 | .set_union_tag, |
| 255 | 255 | .min, |
| 256 | 256 | .max, |
| 257 | .memset, | |
| 258 | .memcpy, | |
| 257 | 259 | => { |
| 258 | 260 | const bin_op = data[inst].bin_op; |
| 259 | 261 | try self.verifyInst(inst, .{ bin_op.lhs, bin_op.rhs, .none }); |
| ... | ... | @@ -306,13 +308,6 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void { |
| 306 | 308 | const extra = self.air.extraData(Air.Bin, vector_store_elem.payload).data; |
| 307 | 309 | try self.verifyInst(inst, .{ vector_store_elem.vector_ptr, extra.lhs, extra.rhs }); |
| 308 | 310 | }, |
| 309 | .memset, | |
| 310 | .memcpy, | |
| 311 | => { | |
| 312 | const pl_op = data[inst].pl_op; | |
| 313 | const extra = self.air.extraData(Air.Bin, pl_op.payload).data; | |
| 314 | try self.verifyInst(inst, .{ pl_op.operand, extra.lhs, extra.rhs }); | |
| 315 | }, | |
| 316 | 311 | .cmpxchg_strong, |
| 317 | 312 | .cmpxchg_weak, |
| 318 | 313 | => { |
src/Sema.zig+160-15| ... | ... | @@ -3386,17 +3386,39 @@ fn zirIndexablePtrLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE |
| 3386 | 3386 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; |
| 3387 | 3387 | const src = inst_data.src(); |
| 3388 | 3388 | const object = try sema.resolveInst(inst_data.operand); |
| 3389 | const object_ty = sema.typeOf(object); | |
| 3390 | ||
| 3391 | const is_pointer_to = object_ty.isSinglePointer(); | |
| 3392 | 3389 | |
| 3393 | const array_ty = if (is_pointer_to) | |
| 3394 | object_ty.childType() | |
| 3395 | else | |
| 3396 | object_ty; | |
| 3390 | return indexablePtrLen(sema, block, src, object); | |
| 3391 | } | |
| 3397 | 3392 | |
| 3393 | fn indexablePtrLen( | |
| 3394 | sema: *Sema, | |
| 3395 | block: *Block, | |
| 3396 | src: LazySrcLoc, | |
| 3397 | object: Air.Inst.Ref, | |
| 3398 | ) CompileError!Air.Inst.Ref { | |
| 3399 | const object_ty = sema.typeOf(object); | |
| 3400 | const is_pointer_to = object_ty.isSinglePointer(); | |
| 3401 | const array_ty = if (is_pointer_to) object_ty.childType() else object_ty; | |
| 3398 | 3402 | try checkIndexable(sema, block, src, array_ty); |
| 3403 | return sema.fieldVal(block, src, object, "len", src); | |
| 3404 | } | |
| 3399 | 3405 | |
| 3406 | fn indexablePtrLenOrNone( | |
| 3407 | sema: *Sema, | |
| 3408 | block: *Block, | |
| 3409 | src: LazySrcLoc, | |
| 3410 | object: Air.Inst.Ref, | |
| 3411 | ) CompileError!Air.Inst.Ref { | |
| 3412 | const object_ty = sema.typeOf(object); | |
| 3413 | const array_ty = t: { | |
| 3414 | const ptr_size = object_ty.ptrSizeOrNull() orelse break :t object_ty; | |
| 3415 | break :t switch (ptr_size) { | |
| 3416 | .Many => return .none, | |
| 3417 | .One => object_ty.childType(), | |
| 3418 | else => object_ty, | |
| 3419 | }; | |
| 3420 | }; | |
| 3421 | try checkIndexable(sema, block, src, array_ty); | |
| 3400 | 3422 | return sema.fieldVal(block, src, object, "len", src); |
| 3401 | 3423 | } |
| 3402 | 3424 | |
| ... | ... | @@ -21773,6 +21795,29 @@ fn analyzeMinMax( |
| 21773 | 21795 | return block.addBinOp(air_tag, simd_op.lhs, simd_op.rhs); |
| 21774 | 21796 | } |
| 21775 | 21797 | |
| 21798 | fn upgradeToArrayPtr(sema: *Sema, block: *Block, ptr: Air.Inst.Ref, len: u64) !Air.Inst.Ref { | |
| 21799 | const mod = sema.mod; | |
| 21800 | const info = sema.typeOf(ptr).ptrInfo().data; | |
| 21801 | if (info.size == .One) { | |
| 21802 | // Already an array pointer. | |
| 21803 | return ptr; | |
| 21804 | } | |
| 21805 | const new_ty = try Type.ptr(sema.arena, mod, .{ | |
| 21806 | .pointee_type = try Type.array(sema.arena, len, info.sentinel, info.pointee_type, mod), | |
| 21807 | .sentinel = null, | |
| 21808 | .@"align" = info.@"align", | |
| 21809 | .@"addrspace" = info.@"addrspace", | |
| 21810 | .mutable = info.mutable, | |
| 21811 | .@"allowzero" = info.@"allowzero", | |
| 21812 | .@"volatile" = info.@"volatile", | |
| 21813 | .size = .One, | |
| 21814 | }); | |
| 21815 | if (info.size == .Slice) { | |
| 21816 | return block.addTyOp(.slice_ptr, new_ty, ptr); | |
| 21817 | } | |
| 21818 | return block.addBitCast(new_ty, ptr); | |
| 21819 | } | |
| 21820 | ||
| 21776 | 21821 | fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { |
| 21777 | 21822 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; |
| 21778 | 21823 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; |
| ... | ... | @@ -21780,27 +21825,125 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void |
| 21780 | 21825 | const dest_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; |
| 21781 | 21826 | const src_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; |
| 21782 | 21827 | const dest_ptr = try sema.resolveInst(extra.lhs); |
| 21783 | const src_ptr_ptr = try sema.resolveInst(extra.rhs); | |
| 21784 | const dest_ptr_ty = sema.typeOf(dest_ptr); | |
| 21785 | try checkSliceOrArrayType(sema, block, dest_src, dest_ptr_ty); | |
| 21828 | const src_ptr = try sema.resolveInst(extra.rhs); | |
| 21829 | const dest_len = try indexablePtrLenOrNone(sema, block, dest_src, dest_ptr); | |
| 21830 | const src_len = try indexablePtrLenOrNone(sema, block, src_src, src_ptr); | |
| 21831 | ||
| 21832 | if (dest_len == .none and src_len == .none) { | |
| 21833 | const msg = msg: { | |
| 21834 | const msg = try sema.errMsg(block, src, "unknown @memcpy length", .{}); | |
| 21835 | errdefer msg.destroy(sema.gpa); | |
| 21836 | try sema.errNote(block, dest_src, msg, "destination type {} provides no length", .{ | |
| 21837 | sema.typeOf(dest_ptr).fmt(sema.mod), | |
| 21838 | }); | |
| 21839 | try sema.errNote(block, src_src, msg, "source type {} provides no length", .{ | |
| 21840 | sema.typeOf(src_ptr).fmt(sema.mod), | |
| 21841 | }); | |
| 21842 | break :msg msg; | |
| 21843 | }; | |
| 21844 | return sema.failWithOwnedErrorMsg(msg); | |
| 21845 | } | |
| 21786 | 21846 | |
| 21787 | const dest_len = try sema.fieldVal(block, dest_src, dest_ptr, "len", dest_src); | |
| 21788 | const src_ptr = try sema.analyzeSlice(block, src_src, src_ptr_ptr, .zero_usize, dest_len, .none, .unneeded, src_src, src_src, src_src); | |
| 21847 | var len_val: ?Value = null; | |
| 21848 | ||
| 21849 | if (dest_len != .none and src_len != .none) check: { | |
| 21850 | // If we can check at compile-time, no need for runtime safety. | |
| 21851 | if (try sema.resolveDefinedValue(block, dest_src, dest_len)) |dest_len_val| { | |
| 21852 | len_val = dest_len_val; | |
| 21853 | if (try sema.resolveDefinedValue(block, src_src, src_len)) |src_len_val| { | |
| 21854 | if (!(try sema.valuesEqual(dest_len_val, src_len_val, Type.usize))) { | |
| 21855 | const msg = msg: { | |
| 21856 | const msg = try sema.errMsg(block, src, "non-matching @memcpy lengths", .{}); | |
| 21857 | errdefer msg.destroy(sema.gpa); | |
| 21858 | try sema.errNote(block, dest_src, msg, "length {} here", .{ | |
| 21859 | dest_len_val.fmtValue(Type.usize, sema.mod), | |
| 21860 | }); | |
| 21861 | try sema.errNote(block, src_src, msg, "length {} here", .{ | |
| 21862 | src_len_val.fmtValue(Type.usize, sema.mod), | |
| 21863 | }); | |
| 21864 | break :msg msg; | |
| 21865 | }; | |
| 21866 | return sema.failWithOwnedErrorMsg(msg); | |
| 21867 | } | |
| 21868 | break :check; | |
| 21869 | } | |
| 21870 | } else if (try sema.resolveDefinedValue(block, src_src, src_len)) |src_len_val| { | |
| 21871 | len_val = src_len_val; | |
| 21872 | } | |
| 21873 | ||
| 21874 | if (block.wantSafety()) { | |
| 21875 | const ok = try block.addBinOp(.cmp_eq, dest_len, src_len); | |
| 21876 | try sema.addSafetyCheck(block, ok, .memcpy_len_mismatch); | |
| 21877 | } | |
| 21878 | } | |
| 21789 | 21879 | |
| 21790 | 21880 | const runtime_src = if (try sema.resolveDefinedValue(block, dest_src, dest_ptr)) |dest_ptr_val| rs: { |
| 21791 | 21881 | if (!dest_ptr_val.isComptimeMutablePtr()) break :rs dest_src; |
| 21792 | 21882 | if (try sema.resolveDefinedValue(block, src_src, src_ptr)) |src_ptr_val| { |
| 21793 | if (!src_ptr_val.isComptimeMutablePtr()) break :rs src_src; | |
| 21883 | _ = src_ptr_val; | |
| 21794 | 21884 | return sema.fail(block, src, "TODO: @memcpy at comptime", .{}); |
| 21795 | 21885 | } else break :rs src_src; |
| 21796 | 21886 | } else dest_src; |
| 21797 | 21887 | |
| 21798 | 21888 | try sema.requireRuntimeBlock(block, src, runtime_src); |
| 21889 | ||
| 21890 | const dest_ty = sema.typeOf(dest_ptr); | |
| 21891 | const src_ty = sema.typeOf(src_ptr); | |
| 21892 | ||
| 21893 | // If in-memory coercion is not allowed, explode this memcpy call into a | |
| 21894 | // for loop that copies element-wise. | |
| 21895 | // Likewise if this is an iterable rather than a pointer, do the same | |
| 21896 | // lowering. The AIR instruction requires pointers with element types of | |
| 21897 | // equal ABI size. | |
| 21898 | ||
| 21899 | if (dest_ty.zigTypeTag() != .Pointer or src_ty.zigTypeTag() != .Pointer) { | |
| 21900 | return sema.fail(block, src, "TODO: lower @memcpy to a for loop because the source or destination iterable is a tuple", .{}); | |
| 21901 | } | |
| 21902 | ||
| 21903 | const dest_elem_ty = dest_ty.elemType2(); | |
| 21904 | const src_elem_ty = src_ty.elemType2(); | |
| 21905 | const target = sema.mod.getTarget(); | |
| 21906 | if (.ok != try sema.coerceInMemoryAllowed(block, dest_elem_ty, src_elem_ty, true, target, dest_src, src_src)) { | |
| 21907 | return sema.fail(block, src, "TODO: lower @memcpy to a for loop because the element types have different ABI sizes", .{}); | |
| 21908 | } | |
| 21909 | ||
| 21910 | // If the length is comptime-known, then upgrade src and destination types | |
| 21911 | // into pointer-to-array. At this point we know they are both pointers | |
| 21912 | // already. | |
| 21913 | var new_dest_ptr = dest_ptr; | |
| 21914 | var new_src_ptr = src_ptr; | |
| 21915 | if (len_val) |val| { | |
| 21916 | const len = val.toUnsignedInt(target); | |
| 21917 | new_dest_ptr = try upgradeToArrayPtr(sema, block, dest_ptr, len); | |
| 21918 | new_src_ptr = try upgradeToArrayPtr(sema, block, src_ptr, len); | |
| 21919 | } | |
| 21920 | ||
| 21921 | // Aliasing safety check. | |
| 21922 | if (block.wantSafety()) { | |
| 21923 | const dest_int = try block.addUnOp(.ptrtoint, new_dest_ptr); | |
| 21924 | const src_int = try block.addUnOp(.ptrtoint, new_src_ptr); | |
| 21925 | const len = if (len_val) |v| | |
| 21926 | try sema.addConstant(Type.usize, v) | |
| 21927 | else if (dest_len != .none) | |
| 21928 | dest_len | |
| 21929 | else | |
| 21930 | src_len; | |
| 21931 | ||
| 21932 | // ok1: dest >= src + len | |
| 21933 | // ok2: src >= dest + len | |
| 21934 | const src_plus_len = try block.addBinOp(.add, src_int, len); | |
| 21935 | const dest_plus_len = try block.addBinOp(.add, dest_int, len); | |
| 21936 | const ok1 = try block.addBinOp(.cmp_gte, dest_int, src_plus_len); | |
| 21937 | const ok2 = try block.addBinOp(.cmp_gte, src_int, dest_plus_len); | |
| 21938 | const ok = try block.addBinOp(.bit_or, ok1, ok2); | |
| 21939 | try sema.addSafetyCheck(block, ok, .memcpy_alias); | |
| 21940 | } | |
| 21941 | ||
| 21799 | 21942 | _ = try block.addInst(.{ |
| 21800 | 21943 | .tag = .memcpy, |
| 21801 | 21944 | .data = .{ .bin_op = .{ |
| 21802 | .lhs = dest_ptr, | |
| 21803 | .rhs = src_ptr, | |
| 21945 | .lhs = new_dest_ptr, | |
| 21946 | .rhs = new_src_ptr, | |
| 21804 | 21947 | } }, |
| 21805 | 21948 | }); |
| 21806 | 21949 | } |
| ... | ... | @@ -22949,6 +23092,8 @@ pub const PanicId = enum { |
| 22949 | 23092 | index_out_of_bounds, |
| 22950 | 23093 | start_index_greater_than_end, |
| 22951 | 23094 | for_len_mismatch, |
| 23095 | memcpy_len_mismatch, | |
| 23096 | memcpy_alias, | |
| 22952 | 23097 | }; |
| 22953 | 23098 | |
| 22954 | 23099 | fn addSafetyCheck( |
src/codegen/c.zig+74-24| ... | ... | @@ -6177,18 +6177,43 @@ fn airAtomicStore(f: *Function, inst: Air.Inst.Index, order: [*:0]const u8) !CVa |
| 6177 | 6177 | } |
| 6178 | 6178 | |
| 6179 | 6179 | fn airMemset(f: *Function, inst: Air.Inst.Index) !CValue { |
| 6180 | const pl_op = f.air.instructions.items(.data)[inst].pl_op; | |
| 6181 | const extra = f.air.extraData(Air.Bin, pl_op.payload).data; | |
| 6182 | const dest_ty = f.air.typeOf(pl_op.operand); | |
| 6183 | const dest_ptr = try f.resolveInst(pl_op.operand); | |
| 6184 | const value = try f.resolveInst(extra.lhs); | |
| 6185 | const len = try f.resolveInst(extra.rhs); | |
| 6186 | ||
| 6180 | const bin_op = f.air.instructions.items(.data)[inst].bin_op; | |
| 6181 | const dest_ty = f.air.typeOf(bin_op.lhs); | |
| 6182 | const dest_slice = try f.resolveInst(bin_op.lhs); | |
| 6183 | const value = try f.resolveInst(bin_op.rhs); | |
| 6184 | const elem_ty = f.air.typeOf(bin_op.rhs); | |
| 6185 | const target = f.object.dg.module.getTarget(); | |
| 6186 | const elem_abi_size = elem_ty.abiSize(target); | |
| 6187 | const val_is_undef = if (f.air.value(bin_op.rhs)) |val| val.isUndefDeep() else false; | |
| 6187 | 6188 | const writer = f.object.writer(); |
| 6188 | if (dest_ty.isVolatilePtr()) { | |
| 6189 | var u8_ptr_pl = dest_ty.ptrInfo(); | |
| 6190 | u8_ptr_pl.data.pointee_type = Type.u8; | |
| 6191 | const u8_ptr_ty = Type.initPayload(&u8_ptr_pl.base); | |
| 6189 | ||
| 6190 | if (val_is_undef) { | |
| 6191 | try writer.writeAll("memset("); | |
| 6192 | switch (dest_ty.ptrSize()) { | |
| 6193 | .Slice => { | |
| 6194 | try f.writeCValueMember(writer, dest_slice, .{ .identifier = "ptr" }); | |
| 6195 | try writer.writeAll(", 0xaa, "); | |
| 6196 | try f.writeCValueMember(writer, dest_slice, .{ .identifier = "len" }); | |
| 6197 | if (elem_abi_size > 1) { | |
| 6198 | try writer.print(" * {d});\n", .{elem_abi_size}); | |
| 6199 | } else { | |
| 6200 | try writer.writeAll(");\n"); | |
| 6201 | } | |
| 6202 | }, | |
| 6203 | .One => { | |
| 6204 | const array_ty = dest_ty.childType(); | |
| 6205 | const len = array_ty.arrayLen() * elem_abi_size; | |
| 6206 | ||
| 6207 | try f.writeCValue(writer, dest_slice, .FunctionArgument); | |
| 6208 | try writer.print(", 0xaa, {d});\n", .{len}); | |
| 6209 | }, | |
| 6210 | .Many, .C => unreachable, | |
| 6211 | } | |
| 6212 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); | |
| 6213 | return .none; | |
| 6214 | } | |
| 6215 | ||
| 6216 | if (elem_abi_size > 1 or dest_ty.isVolatilePtr()) { | |
| 6192 | 6217 | const index = try f.allocLocal(inst, Type.usize); |
| 6193 | 6218 | |
| 6194 | 6219 | try writer.writeAll("for ("); |
| ... | ... | @@ -6198,36 +6223,61 @@ fn airMemset(f: *Function, inst: Air.Inst.Index) !CValue { |
| 6198 | 6223 | try writer.writeAll("; "); |
| 6199 | 6224 | try f.writeCValue(writer, index, .Other); |
| 6200 | 6225 | try writer.writeAll(" != "); |
| 6201 | try f.writeCValue(writer, len, .Other); | |
| 6226 | switch (dest_ty.ptrSize()) { | |
| 6227 | .Slice => { | |
| 6228 | try f.writeCValueMember(writer, dest_slice, .{ .identifier = "len" }); | |
| 6229 | }, | |
| 6230 | .One => { | |
| 6231 | const array_ty = dest_ty.childType(); | |
| 6232 | const len = array_ty.arrayLen() * elem_abi_size; | |
| 6233 | try writer.print("{d}", .{len}); | |
| 6234 | }, | |
| 6235 | .Many, .C => unreachable, | |
| 6236 | } | |
| 6202 | 6237 | try writer.writeAll("; "); |
| 6203 | 6238 | try f.writeCValue(writer, index, .Other); |
| 6204 | 6239 | try writer.writeAll(" += "); |
| 6205 | 6240 | try f.object.dg.renderValue(writer, Type.usize, Value.one, .Other); |
| 6206 | try writer.writeAll(") (("); | |
| 6207 | try f.renderType(writer, u8_ptr_ty); | |
| 6208 | try writer.writeByte(')'); | |
| 6209 | try f.writeCValue(writer, dest_ptr, .FunctionArgument); | |
| 6241 | try writer.writeAll(") ("); | |
| 6242 | switch (dest_ty.ptrSize()) { | |
| 6243 | .Slice => try f.writeCValueMember(writer, dest_slice, .{ .identifier = "ptr" }), | |
| 6244 | .One => try f.writeCValue(writer, dest_slice, .FunctionArgument), | |
| 6245 | .Many, .C => unreachable, | |
| 6246 | } | |
| 6210 | 6247 | try writer.writeAll(")["); |
| 6211 | 6248 | try f.writeCValue(writer, index, .Other); |
| 6212 | 6249 | try writer.writeAll("] = "); |
| 6213 | 6250 | try f.writeCValue(writer, value, .FunctionArgument); |
| 6214 | 6251 | try writer.writeAll(";\n"); |
| 6215 | 6252 | |
| 6216 | try reap(f, inst, &.{ pl_op.operand, extra.lhs, extra.rhs }); | |
| 6253 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); | |
| 6217 | 6254 | try freeLocal(f, inst, index.new_local, 0); |
| 6218 | 6255 | |
| 6219 | 6256 | return .none; |
| 6220 | 6257 | } |
| 6221 | 6258 | |
| 6222 | try reap(f, inst, &.{ pl_op.operand, extra.lhs, extra.rhs }); | |
| 6223 | 6259 | try writer.writeAll("memset("); |
| 6224 | try f.writeCValue(writer, dest_ptr, .FunctionArgument); | |
| 6225 | try writer.writeAll(", "); | |
| 6226 | try f.writeCValue(writer, value, .FunctionArgument); | |
| 6227 | try writer.writeAll(", "); | |
| 6228 | try f.writeCValue(writer, len, .FunctionArgument); | |
| 6229 | try writer.writeAll(");\n"); | |
| 6260 | switch (dest_ty.ptrSize()) { | |
| 6261 | .Slice => { | |
| 6262 | try f.writeCValueMember(writer, dest_slice, .{ .identifier = "ptr" }); | |
| 6263 | try writer.writeAll(", "); | |
| 6264 | try f.writeCValue(writer, value, .FunctionArgument); | |
| 6265 | try writer.writeAll(", "); | |
| 6266 | try f.writeCValueMember(writer, dest_slice, .{ .identifier = "len" }); | |
| 6267 | try writer.writeAll(");\n"); | |
| 6268 | }, | |
| 6269 | .One => { | |
| 6270 | const array_ty = dest_ty.childType(); | |
| 6271 | const len = array_ty.arrayLen() * elem_abi_size; | |
| 6230 | 6272 | |
| 6273 | try f.writeCValue(writer, dest_slice, .FunctionArgument); | |
| 6274 | try writer.writeAll(", "); | |
| 6275 | try f.writeCValue(writer, value, .FunctionArgument); | |
| 6276 | try writer.print(", {d});\n", .{len}); | |
| 6277 | }, | |
| 6278 | .Many, .C => unreachable, | |
| 6279 | } | |
| 6280 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); | |
| 6231 | 6281 | return .none; |
| 6232 | 6282 | } |
| 6233 | 6283 |
src/codegen/llvm.zig+7-5| ... | ... | @@ -5777,10 +5777,10 @@ pub const FuncGen = struct { |
| 5777 | 5777 | } |
| 5778 | 5778 | |
| 5779 | 5779 | fn sliceOrArrayPtr(fg: *FuncGen, ptr: *llvm.Value, ty: Type) *llvm.Value { |
| 5780 | switch (ty.ptrSize()) { | |
| 5781 | .Slice => return fg.builder.buildExtractValue(ptr, 0, ""), | |
| 5782 | .One => return ptr, | |
| 5783 | .Many, .C => unreachable, | |
| 5780 | if (ty.isSlice()) { | |
| 5781 | return fg.builder.buildExtractValue(ptr, 0, ""); | |
| 5782 | } else { | |
| 5783 | return ptr; | |
| 5784 | 5784 | } |
| 5785 | 5785 | } |
| 5786 | 5786 | |
| ... | ... | @@ -7917,8 +7917,10 @@ pub const FuncGen = struct { |
| 7917 | 7917 | fn airPtrToInt(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
| 7918 | 7918 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 7919 | 7919 | const operand = try self.resolveInst(un_op); |
| 7920 | const ptr_ty = self.air.typeOf(un_op); | |
| 7921 | const operand_ptr = self.sliceOrArrayPtr(operand, ptr_ty); | |
| 7920 | 7922 | const dest_llvm_ty = try self.dg.lowerType(self.air.typeOfIndex(inst)); |
| 7921 | return self.builder.buildPtrToInt(operand, dest_llvm_ty, ""); | |
| 7923 | return self.builder.buildPtrToInt(operand_ptr, dest_llvm_ty, ""); | |
| 7922 | 7924 | } |
| 7923 | 7925 | |
| 7924 | 7926 | fn airBitCast(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |
src/type.zig+10-5| ... | ... | @@ -3843,9 +3843,14 @@ pub const Type = extern union { |
| 3843 | 3843 | }; |
| 3844 | 3844 | } |
| 3845 | 3845 | |
| 3846 | /// Asserts the `Type` is a pointer. | |
| 3847 | pub fn ptrSize(self: Type) std.builtin.Type.Pointer.Size { | |
| 3848 | return switch (self.tag()) { | |
| 3846 | /// Asserts `ty` is a pointer. | |
| 3847 | pub fn ptrSize(ty: Type) std.builtin.Type.Pointer.Size { | |
| 3848 | return ptrSizeOrNull(ty).?; | |
| 3849 | } | |
| 3850 | ||
| 3851 | /// Returns `null` if `ty` is not a pointer. | |
| 3852 | pub fn ptrSizeOrNull(ty: Type) ?std.builtin.Type.Pointer.Size { | |
| 3853 | return switch (ty.tag()) { | |
| 3849 | 3854 | .const_slice, |
| 3850 | 3855 | .mut_slice, |
| 3851 | 3856 | .const_slice_u8, |
| ... | ... | @@ -3870,9 +3875,9 @@ pub const Type = extern union { |
| 3870 | 3875 | .inferred_alloc_mut, |
| 3871 | 3876 | => .One, |
| 3872 | 3877 | |
| 3873 | .pointer => self.castTag(.pointer).?.data.size, | |
| 3878 | .pointer => ty.castTag(.pointer).?.data.size, | |
| 3874 | 3879 | |
| 3875 | else => unreachable, | |
| 3880 | else => null, | |
| 3876 | 3881 | }; |
| 3877 | 3882 | } |
| 3878 | 3883 |