| author | |
| committer | |
| log | 069a009fbc6e34cc5a14cea037a2129cf4642434 |
| tree | 581889431796bef24de22e1c12ce8c6a6fa472b1 |
| parent | 3c46da14db296beec186c3df4fe3669f6c99b715 |
Closes: #3652112 files changed, 131 insertions(+), 42 deletions(-)
src/Air.zig+2-2| ... | @@ -780,8 +780,8 @@ pub const Inst = struct { | ... | @@ -780,8 +780,8 @@ pub const Inst = struct { |
| 780 | reduce, | 780 | reduce, |
| 781 | /// Same as `reduce` with optimized float mode. | 781 | /// Same as `reduce` with optimized float mode. |
| 782 | reduce_optimized, | 782 | reduce_optimized, |
| 783 | /// Given an integer, bool, float, or pointer operand, return a vector with all elements | 783 | /// Given an operand, return a vector or array with all elements equal to the operand. |
| 784 | /// equal to the scalar value. | 784 | /// For a sentinel-terminated array, the sentinel is derived from the result type. |
| 785 | /// Uses the `ty_op` field. | 785 | /// Uses the `ty_op` field. |
| 786 | splat, | 786 | splat, |
| 787 | /// Constructs a vector by selecting elements from a single vector based on a mask. Each | 787 | /// Constructs a vector by selecting elements from a single vector based on a mask. Each |
src/Air/Legalize.zig+22-8| ... | @@ -200,6 +200,8 @@ pub const Feature = enum { | ... | @@ -200,6 +200,8 @@ pub const Feature = enum { |
| 200 | expand_packed_agg_field_val, | 200 | expand_packed_agg_field_val, |
| 201 | /// Replace `aggregate_init` of a packed struct with a sequence of `shl_exact`, `bit_cast`, `int_cast`, and `bit_or`. | 201 | /// Replace `aggregate_init` of a packed struct with a sequence of `shl_exact`, `bit_cast`, `int_cast`, and `bit_or`. |
| 202 | expand_packed_aggregate_init, | 202 | expand_packed_aggregate_init, |
| 203 | /// Replace `splat` of an array with an `aggregate_init`. | ||
| 204 | expand_array_splat, | ||
| 203 | /// Replace `array_to_vector` with an `array_elem_val` per element followed by an `aggregate_init`. | 205 | /// Replace `array_to_vector` with an `array_elem_val` per element followed by an `aggregate_init`. |
| 204 | expand_array_to_vector, | 206 | expand_array_to_vector, |
| 205 | 207 | ||
| ... | @@ -891,15 +893,27 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void { | ... | @@ -891,15 +893,27 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void { |
| 891 | .soft_float => unreachable, // the operand is not a scalar | 893 | .soft_float => unreachable, // the operand is not a scalar |
| 892 | } | 894 | } |
| 893 | }, | 895 | }, |
| 894 | .splat => if (l.features.has(.splat_one_elem_to_bit_cast)) { | 896 | .splat => { |
| 895 | const ty_op = l.air_instructions.items(.data)[@backingInt(inst)].ty_op; | 897 | const ty_op = l.air_instructions.items(.data)[@backingInt(inst)].ty_op; |
| 896 | switch (ty_op.ty.vectorLen(zcu)) { | 898 | switch (ty_op.ty.zigTypeTag(zcu)) { |
| 897 | 0 => unreachable, | 899 | .vector => switch (ty_op.ty.vectorLen(zcu)) { |
| 898 | 1 => continue :inst l.replaceInst(inst, .bit_cast, .{ .ty_op = .{ | 900 | 0 => unreachable, |
| 899 | .ty = ty_op.ty, | 901 | 1 => continue :inst l.replaceInst(inst, .bit_cast, .{ .ty_op = .{ |
| 900 | .operand = ty_op.operand, | 902 | .ty = ty_op.ty, |
| 901 | } }), | 903 | .operand = ty_op.operand, |
| 902 | else => {}, | 904 | } }), |
| 905 | else => {}, | ||
| 906 | }, | ||
| 907 | .array => if (l.features.has(.expand_array_splat)) { | ||
| 908 | const len: usize = @intCast(ty_op.ty.arrayLen(zcu)); | ||
| 909 | const elems_start: u32 = @intCast(l.air_extra.items.len); | ||
| 910 | try l.air_extra.appendNTimes(l.pt.zcu.gpa, @backingInt(ty_op.operand), len); | ||
| 911 | continue :inst l.replaceInst(inst, .aggregate_init, .{ .ty_pl = .{ | ||
| 912 | .ty = ty_op.ty, | ||
| 913 | .payload = elems_start, | ||
| 914 | } }); | ||
| 915 | }, | ||
| 916 | else => unreachable, | ||
| 903 | } | 917 | } |
| 904 | }, | 918 | }, |
| 905 | .shuffle_one => { | 919 | .shuffle_one => { |
src/Sema.zig+1-7| ... | @@ -23271,13 +23271,7 @@ fn zirSplat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I | ... | @@ -23271,13 +23271,7 @@ fn zirSplat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I |
| 23271 | try sema.requireRuntimeBlock(block, src, scalar_src); | 23271 | try sema.requireRuntimeBlock(block, src, scalar_src); |
| 23272 | 23272 | ||
| 23273 | switch (dest_ty.zigTypeTag(zcu)) { | 23273 | switch (dest_ty.zigTypeTag(zcu)) { |
| 23274 | .array => { | 23274 | .vector, .array => return block.addTyOp(.splat, dest_ty, scalar), |
| 23275 | const elems = try sema.arena.alloc(Air.Inst.Ref, len + @intFromBool(maybe_sentinel != null)); | ||
| 23276 | @memset(elems[0..len], scalar); | ||
| 23277 | if (maybe_sentinel) |s| elems[len] = Air.internedToRef(s.toIntern()); | ||
| 23278 | return block.addAggregateInit(dest_ty, elems); | ||
| 23279 | }, | ||
| 23280 | .vector => return block.addTyOp(.splat, dest_ty, scalar), | ||
| 23281 | else => unreachable, | 23275 | else => unreachable, |
| 23282 | } | 23276 | } |
| 23283 | } | 23277 | } |
src/codegen/aarch64.zig+1| ... | @@ -8,6 +8,7 @@ pub const Select = @import("aarch64/Select.zig"); | ... | @@ -8,6 +8,7 @@ pub const Select = @import("aarch64/Select.zig"); |
| 8 | pub fn legalizeFeatures(_: *const std.Target) *const Air.Legalize.Features { | 8 | pub fn legalizeFeatures(_: *const std.Target) *const Air.Legalize.Features { |
| 9 | return comptime &.initMany(&.{ | 9 | return comptime &.initMany(&.{ |
| 10 | .expand_bit_cast_safe, | 10 | .expand_bit_cast_safe, |
| 11 | .expand_array_splat, | ||
| 11 | .expand_array_to_vector, | 12 | .expand_array_to_vector, |
| 12 | }); | 13 | }); |
| 13 | } | 14 | } |
src/codegen/c.zig+1| ... | @@ -39,6 +39,7 @@ pub fn legalizeFeatures(_: *const std.Target) ?*const Air.Legalize.Features { | ... | @@ -39,6 +39,7 @@ pub fn legalizeFeatures(_: *const std.Target) ?*const Air.Legalize.Features { |
| 39 | .expand_packed_store = true, | 39 | .expand_packed_store = true, |
| 40 | .expand_packed_agg_field_val = true, | 40 | .expand_packed_agg_field_val = true, |
| 41 | .expand_packed_aggregate_init = true, | 41 | .expand_packed_aggregate_init = true, |
| 42 | .expand_array_splat = true, | ||
| 42 | .expand_array_to_vector = true, | 43 | .expand_array_to_vector = true, |
| 43 | 44 | ||
| 44 | .scalarize_bit_cast_array = true, | 45 | .scalarize_bit_cast_array = true, |
src/codegen/llvm/FuncGen.zig+77-25| ... | @@ -5590,8 +5590,36 @@ fn airMemset(self: *FuncGen, inst: Air.Inst.Index, safety: bool) Allocator.Error | ... | @@ -5590,8 +5590,36 @@ fn airMemset(self: *FuncGen, inst: Air.Inst.Index, safety: bool) Allocator.Error |
| 5590 | .slice => null, | 5590 | .slice => null, |
| 5591 | .many, .c => unreachable, | 5591 | .many, .c => unreachable, |
| 5592 | }); | 5592 | }); |
| 5593 | const len_bytes = try self.sliceOrArrayLenInBytes(dest_slice, ptr_ty); | ||
| 5593 | 5594 | ||
| 5594 | if (allow_byte_memset) if (bin_op.rhs.toInterned()) |elem_ip_index| { | 5595 | try self.lowerMemset( |
| 5596 | dest_ptr, | ||
| 5597 | dest_ptr_align, | ||
| 5598 | bin_op.rhs, | ||
| 5599 | elem_ty, | ||
| 5600 | len_bytes, | ||
| 5601 | access_kind, | ||
| 5602 | safety, | ||
| 5603 | allow_byte_memset, | ||
| 5604 | ); | ||
| 5605 | return .none; | ||
| 5606 | } | ||
| 5607 | |||
| 5608 | fn lowerMemset( | ||
| 5609 | self: *FuncGen, | ||
| 5610 | dest_ptr: Builder.Value, | ||
| 5611 | dest_ptr_align: InternPool.Alignment, | ||
| 5612 | elem_ref: Air.Inst.Ref, | ||
| 5613 | elem_ty: Type, | ||
| 5614 | len_bytes: Builder.Value, | ||
| 5615 | access_kind: Builder.MemoryAccessKind, | ||
| 5616 | safety: bool, | ||
| 5617 | allow_byte_memset: bool, | ||
| 5618 | ) Allocator.Error!void { | ||
| 5619 | const o = self.object; | ||
| 5620 | const zcu = o.zcu; | ||
| 5621 | |||
| 5622 | if (allow_byte_memset) if (elem_ref.toInterned()) |elem_ip_index| { | ||
| 5595 | const elem_val: Value = .fromInterned(elem_ip_index); | 5623 | const elem_val: Value = .fromInterned(elem_ip_index); |
| 5596 | if (elem_val.isUndef(zcu)) { | 5624 | if (elem_val.isUndef(zcu)) { |
| 5597 | // Even if safety is disabled, we still emit a memset to undefined since it conveys | 5625 | // Even if safety is disabled, we still emit a memset to undefined since it conveys |
| ... | @@ -5601,20 +5629,19 @@ fn airMemset(self: *FuncGen, inst: Air.Inst.Index, safety: bool) Allocator.Error | ... | @@ -5601,20 +5629,19 @@ fn airMemset(self: *FuncGen, inst: Air.Inst.Index, safety: bool) Allocator.Error |
| 5601 | try o.builder.intValue(.i8, 0xaa) | 5629 | try o.builder.intValue(.i8, 0xaa) |
| 5602 | else | 5630 | else |
| 5603 | try o.builder.undefValue(.i8); | 5631 | try o.builder.undefValue(.i8); |
| 5604 | const len = try self.sliceOrArrayLenInBytes(dest_slice, ptr_ty); | ||
| 5605 | _ = try self.wip.callMemSet( | 5632 | _ = try self.wip.callMemSet( |
| 5606 | dest_ptr, | 5633 | dest_ptr, |
| 5607 | dest_ptr_align.toLlvm(), | 5634 | dest_ptr_align.toLlvm(), |
| 5608 | fill_byte, | 5635 | fill_byte, |
| 5609 | len, | 5636 | len_bytes, |
| 5610 | access_kind, | 5637 | access_kind, |
| 5611 | self.disable_intrinsics, | 5638 | self.disable_intrinsics, |
| 5612 | ); | 5639 | ); |
| 5613 | const owner_mod = self.ownerModule(); | 5640 | const owner_mod = self.ownerModule(); |
| 5614 | if (safety and owner_mod.valgrind) { | 5641 | if (safety and owner_mod.valgrind) { |
| 5615 | try self.valgrindMarkUndef(dest_ptr, len); | 5642 | try self.valgrindMarkUndef(dest_ptr, len_bytes); |
| 5616 | } | 5643 | } |
| 5617 | return .none; | 5644 | return; |
| 5618 | } | 5645 | } |
| 5619 | 5646 | ||
| 5620 | // Test if the element value is compile-time known to be a | 5647 | // Test if the element value is compile-time known to be a |
| ... | @@ -5623,20 +5650,19 @@ fn airMemset(self: *FuncGen, inst: Air.Inst.Index, safety: bool) Allocator.Error | ... | @@ -5623,20 +5650,19 @@ fn airMemset(self: *FuncGen, inst: Air.Inst.Index, safety: bool) Allocator.Error |
| 5623 | // intrinsic can be used. | 5650 | // intrinsic can be used. |
| 5624 | if (try elem_val.hasRepeatedByteRepr(zcu)) |byte_val| { | 5651 | if (try elem_val.hasRepeatedByteRepr(zcu)) |byte_val| { |
| 5625 | const fill_byte = try o.builder.intValue(.i8, byte_val); | 5652 | const fill_byte = try o.builder.intValue(.i8, byte_val); |
| 5626 | const len = try self.sliceOrArrayLenInBytes(dest_slice, ptr_ty); | ||
| 5627 | _ = try self.wip.callMemSet( | 5653 | _ = try self.wip.callMemSet( |
| 5628 | dest_ptr, | 5654 | dest_ptr, |
| 5629 | dest_ptr_align.toLlvm(), | 5655 | dest_ptr_align.toLlvm(), |
| 5630 | fill_byte, | 5656 | fill_byte, |
| 5631 | len, | 5657 | len_bytes, |
| 5632 | access_kind, | 5658 | access_kind, |
| 5633 | self.disable_intrinsics, | 5659 | self.disable_intrinsics, |
| 5634 | ); | 5660 | ); |
| 5635 | return .none; | 5661 | return; |
| 5636 | } | 5662 | } |
| 5637 | }; | 5663 | }; |
| 5638 | 5664 | ||
| 5639 | const value = try self.resolveInst(bin_op.rhs); | 5665 | const value = try self.resolveInst(elem_ref); |
| 5640 | const elem_abi_size = elem_ty.abiSize(zcu); | 5666 | const elem_abi_size = elem_ty.abiSize(zcu); |
| 5641 | 5667 | ||
| 5642 | intrinsic: { | 5668 | intrinsic: { |
| ... | @@ -5660,16 +5686,15 @@ fn airMemset(self: *FuncGen, inst: Air.Inst.Index, safety: bool) Allocator.Error | ... | @@ -5660,16 +5686,15 @@ fn airMemset(self: *FuncGen, inst: Air.Inst.Index, safety: bool) Allocator.Error |
| 5660 | break :intrinsic; | 5686 | break :intrinsic; |
| 5661 | }; | 5687 | }; |
| 5662 | // Great, we can use the intrinsic! | 5688 | // Great, we can use the intrinsic! |
| 5663 | const len = try self.sliceOrArrayLenInBytes(dest_slice, ptr_ty); | ||
| 5664 | _ = try self.wip.callMemSet( | 5689 | _ = try self.wip.callMemSet( |
| 5665 | dest_ptr, | 5690 | dest_ptr, |
| 5666 | dest_ptr_align.toLlvm(), | 5691 | dest_ptr_align.toLlvm(), |
| 5667 | fill_byte, | 5692 | fill_byte, |
| 5668 | len, | 5693 | len_bytes, |
| 5669 | access_kind, | 5694 | access_kind, |
| 5670 | self.disable_intrinsics, | 5695 | self.disable_intrinsics, |
| 5671 | ); | 5696 | ); |
| 5672 | return .none; | 5697 | return; |
| 5673 | } | 5698 | } |
| 5674 | 5699 | ||
| 5675 | // non-byte-sized element. lower with a loop. something like this: | 5700 | // non-byte-sized element. lower with a loop. something like this: |
| ... | @@ -5693,15 +5718,7 @@ fn airMemset(self: *FuncGen, inst: Air.Inst.Index, safety: bool) Allocator.Error | ... | @@ -5693,15 +5718,7 @@ fn airMemset(self: *FuncGen, inst: Air.Inst.Index, safety: bool) Allocator.Error |
| 5693 | const body_block = try self.wip.block(1, "InlineMemsetBody"); | 5718 | const body_block = try self.wip.block(1, "InlineMemsetBody"); |
| 5694 | const end_block = try self.wip.block(1, "InlineMemsetEnd"); | 5719 | const end_block = try self.wip.block(1, "InlineMemsetEnd"); |
| 5695 | 5720 | ||
| 5696 | const end_ptr = switch (ptr_ty.ptrSize(zcu)) { | 5721 | const end_ptr = try self.ptraddScaled(dest_ptr, len_bytes, 1); |
| 5697 | .slice => try self.ptraddScaled( | ||
| 5698 | dest_ptr, | ||
| 5699 | try self.wip.extractValue(dest_slice, &.{1}, ""), | ||
| 5700 | elem_abi_size, | ||
| 5701 | ), | ||
| 5702 | .one => try self.ptraddConst(dest_ptr, ptr_ty.childType(zcu).abiSize(zcu)), | ||
| 5703 | .many, .c => unreachable, | ||
| 5704 | }; | ||
| 5705 | _ = try self.wip.br(loop_block); | 5722 | _ = try self.wip.br(loop_block); |
| 5706 | 5723 | ||
| 5707 | self.wip.cursor = .{ .block = loop_block }; | 5724 | self.wip.cursor = .{ .block = loop_block }; |
| ... | @@ -5718,7 +5735,7 @@ fn airMemset(self: *FuncGen, inst: Air.Inst.Index, safety: bool) Allocator.Error | ... | @@ -5718,7 +5735,7 @@ fn airMemset(self: *FuncGen, inst: Air.Inst.Index, safety: bool) Allocator.Error |
| 5718 | 5735 | ||
| 5719 | self.wip.cursor = .{ .block = end_block }; | 5736 | self.wip.cursor = .{ .block = end_block }; |
| 5720 | it_ptr.finish(&.{ next_ptr, dest_ptr }, &.{ body_block, entry_block }, &self.wip); | 5737 | it_ptr.finish(&.{ next_ptr, dest_ptr }, &.{ body_block, entry_block }, &self.wip); |
| 5721 | return .none; | 5738 | return; |
| 5722 | } | 5739 | } |
| 5723 | 5740 | ||
| 5724 | fn airMemcpy(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.Value { | 5741 | fn airMemcpy(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.Value { |
| ... | @@ -5980,10 +5997,45 @@ fn airErrorName(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.Va | ... | @@ -5980,10 +5997,45 @@ fn airErrorName(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.Va |
| 5980 | } | 5997 | } |
| 5981 | 5998 | ||
| 5982 | fn airSplat(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.Value { | 5999 | fn airSplat(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.Value { |
| 6000 | const o = self.object; | ||
| 6001 | const zcu = o.zcu; | ||
| 5983 | const ty_op = self.air.instructions.items(.data)[@backingInt(inst)].ty_op; | 6002 | const ty_op = self.air.instructions.items(.data)[@backingInt(inst)].ty_op; |
| 5984 | const scalar = try self.resolveInst(ty_op.operand); | 6003 | const result_ty = self.typeOfIndex(inst); |
| 5985 | const vector_ty = self.typeOfIndex(inst); | 6004 | switch (result_ty.zigTypeTag(zcu)) { |
| 5986 | return self.wip.splatVector(try self.object.lowerType(vector_ty, .as_value), scalar, ""); | 6005 | .vector => { |
| 6006 | const scalar = try self.resolveInst(ty_op.operand); | ||
| 6007 | return self.wip.splatVector(try o.lowerType(result_ty, .as_value), scalar, ""); | ||
| 6008 | }, | ||
| 6009 | .array => { | ||
| 6010 | assert(isByRef(result_ty, zcu)); | ||
| 6011 | |||
| 6012 | const result_ptr = try self.buildZigAlloca(result_ty, .none); | ||
| 6013 | const array_info = result_ty.arrayInfo(zcu); | ||
| 6014 | const elem_size = array_info.elem_type.abiSize(zcu); | ||
| 6015 | const len_bytes = array_info.len * elem_size; | ||
| 6016 | const len_bytes_llvm = try o.builder.intValue(try o.lowerType(.usize, .as_value), len_bytes); | ||
| 6017 | |||
| 6018 | try self.lowerMemset( | ||
| 6019 | result_ptr, | ||
| 6020 | result_ty.abiAlignment(zcu), | ||
| 6021 | ty_op.operand, | ||
| 6022 | array_info.elem_type, | ||
| 6023 | len_bytes_llvm, | ||
| 6024 | .normal, | ||
| 6025 | false, | ||
| 6026 | !self.needMemsetWorkaround(len_bytes), | ||
| 6027 | ); | ||
| 6028 | |||
| 6029 | if (array_info.sentinel) |sent_val| { | ||
| 6030 | const sent_ptr = try self.ptraddConst(result_ptr, len_bytes); | ||
| 6031 | const sent_elem = try self.resolveValue(sent_val); | ||
| 6032 | try self.store(sent_ptr, .none, sent_elem.toValue(), array_info.elem_type, .normal); | ||
| 6033 | } | ||
| 6034 | |||
| 6035 | return result_ptr; | ||
| 6036 | }, | ||
| 6037 | else => unreachable, | ||
| 6038 | } | ||
| 5987 | } | 6039 | } |
| 5988 | 6040 | ||
| 5989 | fn airSelect(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.Value { | 6041 | fn airSelect(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.Value { |
src/codegen/riscv64/CodeGen.zig+1| ... | @@ -59,6 +59,7 @@ pub fn legalizeFeatures(_: *const std.Target) *const Air.Legalize.Features { | ... | @@ -59,6 +59,7 @@ pub fn legalizeFeatures(_: *const std.Target) *const Air.Legalize.Features { |
| 59 | .expand_sub_safe, | 59 | .expand_sub_safe, |
| 60 | .expand_mul_safe, | 60 | .expand_mul_safe, |
| 61 | 61 | ||
| 62 | .expand_array_splat, | ||
| 62 | .expand_array_to_vector, | 63 | .expand_array_to_vector, |
| 63 | }); | 64 | }); |
| 64 | } | 65 | } |
src/codegen/sparc64/CodeGen.zig+1| ... | @@ -42,6 +42,7 @@ const InnerError = codegen.Error || error{OutOfRegisters}; | ... | @@ -42,6 +42,7 @@ const InnerError = codegen.Error || error{OutOfRegisters}; |
| 42 | 42 | ||
| 43 | pub fn legalizeFeatures(_: *const std.Target) ?*const Air.Legalize.Features { | 43 | pub fn legalizeFeatures(_: *const std.Target) ?*const Air.Legalize.Features { |
| 44 | return comptime &.initMany(&.{ | 44 | return comptime &.initMany(&.{ |
| 45 | .expand_array_splat, | ||
| 45 | .expand_array_to_vector, | 46 | .expand_array_to_vector, |
| 46 | }); | 47 | }); |
| 47 | } | 48 | } |
src/codegen/spirv/CodeGen.zig+1| ... | @@ -129,6 +129,7 @@ pub fn legalizeFeatures(_: *const std.Target) *const Air.Legalize.Features { | ... | @@ -129,6 +129,7 @@ pub fn legalizeFeatures(_: *const std.Target) *const Air.Legalize.Features { |
| 129 | .expand_sub_safe, | 129 | .expand_sub_safe, |
| 130 | .expand_mul_safe, | 130 | .expand_mul_safe, |
| 131 | 131 | ||
| 132 | .expand_array_splat, | ||
| 132 | .expand_array_to_vector, | 133 | .expand_array_to_vector, |
| 133 | }); | 134 | }); |
| 134 | } | 135 | } |
src/codegen/wasm/CodeGen.zig+1| ... | @@ -38,6 +38,7 @@ pub fn legalizeFeatures(_: *const std.Target) *const Air.Legalize.Features { | ... | @@ -38,6 +38,7 @@ pub fn legalizeFeatures(_: *const std.Target) *const Air.Legalize.Features { |
| 38 | .expand_packed_store, | 38 | .expand_packed_store, |
| 39 | .expand_packed_agg_field_val, | 39 | .expand_packed_agg_field_val, |
| 40 | .expand_packed_aggregate_init, | 40 | .expand_packed_aggregate_init, |
| 41 | .expand_array_splat, | ||
| 41 | .expand_array_to_vector, | 42 | .expand_array_to_vector, |
| 42 | 43 | ||
| 43 | .scalarize_add, | 44 | .scalarize_add, |
src/codegen/x86_64/CodeGen.zig+1| ... | @@ -78,6 +78,7 @@ pub fn legalizeFeatures(_: *const std.Target) *const Air.Legalize.Features { | ... | @@ -78,6 +78,7 @@ pub fn legalizeFeatures(_: *const std.Target) *const Air.Legalize.Features { |
| 78 | .expand_packed_store, | 78 | .expand_packed_store, |
| 79 | .expand_packed_agg_field_val, | 79 | .expand_packed_agg_field_val, |
| 80 | .expand_packed_aggregate_init, | 80 | .expand_packed_aggregate_init, |
| 81 | .expand_array_splat, | ||
| 81 | .expand_array_to_vector, | 82 | .expand_array_to_vector, |
| 82 | }); | 83 | }); |
| 83 | } | 84 | } |
test/behavior/memset.zig+22| ... | @@ -30,6 +30,28 @@ fn testMemsetArray() !void { | ... | @@ -30,6 +30,28 @@ fn testMemsetArray() !void { |
| 30 | } | 30 | } |
| 31 | } | 31 | } |
| 32 | 32 | ||
| 33 | test "@memset preserves array sentinel" { | ||
| 34 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | ||
| 35 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; | ||
| 36 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; | ||
| 37 | |||
| 38 | try testMemsetArraySentinel(); | ||
| 39 | try comptime testMemsetArraySentinel(); | ||
| 40 | } | ||
| 41 | |||
| 42 | fn testMemsetArraySentinel() !void { | ||
| 43 | var value: u32 = 42; | ||
| 44 | _ = &value; | ||
| 45 | var array: [3:0]u32 = .{ 1, 2, 3 }; | ||
| 46 | |||
| 47 | @memset(&array, value); | ||
| 48 | |||
| 49 | try expect(array[0] == value); | ||
| 50 | try expect(array[1] == value); | ||
| 51 | try expect(array[2] == value); | ||
| 52 | try expect(array[3] == 0); | ||
| 53 | } | ||
| 54 | |||
| 33 | test "@memset on slices" { | 55 | test "@memset on slices" { |
| 34 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | 56 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 35 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; | 57 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; |