authorgravatar for gfischman@gmail.comGuy Fischman <gfischman@gmail.com> 2026-08-11 10:03:55+02:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-08-15 10:38:52+02:00
loga72236ae8e9f11f42351ae42b1242f6091216c45
tree5b8f47bb6dd51bdf65ff619af9eca52040188179
parent4987b62a90d1dea50e7c4cd13a02c7dfe2f9c2e3

compiler: lower array-to-vector coercion in the backend


18 files changed, 352 insertions(+), 1 deletions(-)

src/Air.zig+12
...@@ -748,6 +748,16 @@ pub const Inst = struct {...@@ -748,6 +748,16 @@ pub const Inst = struct {
748 /// Given a pointer to an array, return a slice.748 /// Given a pointer to an array, return a slice.
749 /// Uses the `ty_op` field.749 /// Uses the `ty_op` field.
750 array_to_slice,750 array_to_slice,
751 /// Given an array, return a vector with the same element type and length. A sentinel on
752 /// the operand type is not included in the result.
753 ///
754 /// Vectors have no well-defined in-memory layout, so only the backend can know whether
755 /// the array representation may be reinterpreted rather than copied element-by-element.
756 /// Backends which do not lower this directly can enable
757 /// `Air.Legalize.Feature.expand_array_to_vector`.
758 ///
759 /// Uses the `ty_op` field.
760 array_to_vector,
751 /// Given a float operand, return the integer with the closest mathematical meaning.761 /// Given a float operand, return the integer with the closest mathematical meaning.
752 /// Uses the `ty_op` field.762 /// Uses the `ty_op` field.
753 int_from_float,763 int_from_float,
...@@ -1754,6 +1764,7 @@ pub fn typeOfIndex(air: *const Air, inst: Air.Inst.Index, ip: *const InternPool)...@@ -1754,6 +1764,7 @@ pub fn typeOfIndex(air: *const Air, inst: Air.Inst.Index, ip: *const InternPool)
1754 .struct_field_ptr_index_2,1764 .struct_field_ptr_index_2,
1755 .struct_field_ptr_index_3,1765 .struct_field_ptr_index_3,
1756 .array_to_slice,1766 .array_to_slice,
1767 .array_to_vector,
1757 .int_from_float,1768 .int_from_float,
1758 .int_from_float_optimized,1769 .int_from_float_optimized,
1759 .int_from_float_safe,1770 .int_from_float_safe,
...@@ -2106,6 +2117,7 @@ pub fn mustLower(air: Air, inst: Air.Inst.Index, ip: *const InternPool) bool {...@@ -2106,6 +2117,7 @@ pub fn mustLower(air: Air, inst: Air.Inst.Index, ip: *const InternPool) bool {
2106 .slice_elem_ptr,2117 .slice_elem_ptr,
2107 .ptr_elem_ptr,2118 .ptr_elem_ptr,
2108 .array_to_slice,2119 .array_to_slice,
2120 .array_to_vector,
2109 .int_from_float,2121 .int_from_float,
2110 .int_from_float_optimized,2122 .int_from_float_optimized,
2111 .float_from_int,2123 .float_from_int,
src/Air/Legalize.zig+47
...@@ -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 `array_to_vector` with an `array_elem_val` per element followed by an `aggregate_init`.
204 expand_array_to_vector,
203205
204 /// Replace all arithmetic operations on 16-bit floating-point types with calls to soft-float206 /// Replace all arithmetic operations on 16-bit floating-point types with calls to soft-float
205 /// routines in compiler_rt, including `fptrunc`/`fpext`/`float_from_int`/`int_from_float`207 /// routines in compiler_rt, including `fptrunc`/`fpext`/`float_from_int`/`int_from_float`
...@@ -863,6 +865,9 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void {...@@ -863,6 +865,9 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void {
863 .ptr_elem_ptr,865 .ptr_elem_ptr,
864 .array_to_slice,866 .array_to_slice,
865 => {},867 => {},
868 .array_to_vector => if (l.features.has(.expand_array_to_vector)) {
869 continue :inst l.replaceInst(inst, .block, try l.arrayToVectorBlockPayload(inst));
870 },
866 inline .reduce, .reduce_optimized => |air_tag| {871 inline .reduce, .reduce_optimized => |air_tag| {
867 const reduce = l.air_instructions.items(.data)[@backingInt(inst)].reduce;872 const reduce = l.air_instructions.items(.data)[@backingInt(inst)].reduce;
868 const vector_ty = l.typeOf(reduce.operand);873 const vector_ty = l.typeOf(reduce.operand);
...@@ -2954,6 +2959,48 @@ fn packedAggregateInitBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Erro...@@ -2954,6 +2959,48 @@ fn packedAggregateInitBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Erro
2954 } };2959 } };
2955}2960}
29562961
2962fn arrayToVectorBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error!Air.Inst.Data {
2963 const pt = l.pt;
2964 const zcu = pt.zcu;
2965 const gpa = zcu.gpa;
2966
2967 const orig_ty_op = l.air_instructions.items(.data)[@backingInt(orig_inst)].ty_op;
2968 const vec_ty = orig_ty_op.ty.toType();
2969 const len: usize = @intCast(vec_ty.vectorLen(zcu));
2970
2971 var bfa_buf: [64 + 2]Air.Inst.Index = undefined;
2972 var bfa_state: std.heap.BufferFirstAllocator = .init(@ptrCast(&bfa_buf), gpa);
2973 const bfa = bfa_state.allocator();
2974
2975 const inst_buf = try bfa.alloc(Air.Inst.Index, len + 2);
2976 defer bfa.free(inst_buf);
2977
2978 var main_block: Block = .init(inst_buf);
2979 try l.air_instructions.ensureUnusedCapacity(gpa, inst_buf.len);
2980 try l.air_extra.ensureUnusedCapacity(gpa, len);
2981
2982 const elems_start: u32 = @intCast(l.air_extra.items.len);
2983 for (0..len) |elem_index| {
2984 const index_ref: Air.Inst.Ref = .fromValue(try pt.intValue(.usize, elem_index));
2985 const elem = main_block.addBinOp(l, .array_elem_val, orig_ty_op.operand, index_ref).toRef();
2986 l.air_extra.appendAssumeCapacity(@backingInt(elem));
2987 }
2988
2989 const result = main_block.add(l, .{
2990 .tag = .aggregate_init,
2991 .data = .{ .ty_pl = .{
2992 .ty = .fromType(vec_ty),
2993 .payload = elems_start,
2994 } },
2995 }).toRef();
2996 main_block.addBr(l, orig_inst, result);
2997
2998 return .{ .ty_pl = .{
2999 .ty = .fromType(vec_ty),
3000 .payload = try l.addBlockBody(main_block.body()),
3001 } };
3002}
3003
2957/// Given a `std.math.big.int.Const`, converts it to a `Value` which is a float of type `float_ty`3004/// Given a `std.math.big.int.Const`, converts it to a `Value` which is a float of type `float_ty`
2958/// representing the same numeric value. If the integer cannot be exactly represented, `round`3005/// representing the same numeric value. If the integer cannot be exactly represented, `round`
2959/// decides whether the value should be rounded up or down. If `is_vector`, then `float_ty` is3006/// decides whether the value should be rounded up or down. If `is_vector`, then `float_ty` is
src/Air/Liveness.zig+1
...@@ -525,6 +525,7 @@ fn analyzeInst(...@@ -525,6 +525,7 @@ fn analyzeInst(
525 .struct_field_ptr_index_2,525 .struct_field_ptr_index_2,
526 .struct_field_ptr_index_3,526 .struct_field_ptr_index_3,
527 .array_to_slice,527 .array_to_slice,
528 .array_to_vector,
528 .int_from_float,529 .int_from_float,
529 .int_from_float_optimized,530 .int_from_float_optimized,
530 .int_from_float_safe,531 .int_from_float_safe,
src/Air/Liveness/Verify.zig+1
...@@ -113,6 +113,7 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void {...@@ -113,6 +113,7 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void {
113 .struct_field_ptr_index_2,113 .struct_field_ptr_index_2,
114 .struct_field_ptr_index_3,114 .struct_field_ptr_index_3,
115 .array_to_slice,115 .array_to_slice,
116 .array_to_vector,
116 .int_from_float,117 .int_from_float,
117 .int_from_float_optimized,118 .int_from_float_optimized,
118 .int_from_float_safe,119 .int_from_float_safe,
src/Air/Verify.zig+1
...@@ -380,6 +380,7 @@ fn body(verify: *Verify, body_insts: []const Air.Inst.Index) Error!void {...@@ -380,6 +380,7 @@ fn body(verify: *Verify, body_insts: []const Air.Inst.Index) Error!void {
380 .slice_elem_ptr,380 .slice_elem_ptr,
381 .ptr_elem_val,381 .ptr_elem_val,
382 .array_to_slice,382 .array_to_slice,
383 .array_to_vector,
383 .int_from_float,384 .int_from_float,
384 .int_from_float_optimized,385 .int_from_float_optimized,
385 .int_from_float_safe,386 .int_from_float_safe,
src/Air/print.zig+1
...@@ -265,6 +265,7 @@ const Writer = struct {...@@ -265,6 +265,7 @@ const Writer = struct {
265 .struct_field_ptr_index_2,265 .struct_field_ptr_index_2,
266 .struct_field_ptr_index_3,266 .struct_field_ptr_index_3,
267 .array_to_slice,267 .array_to_slice,
268 .array_to_vector,
268 .float_from_int,269 .float_from_int,
269 .splat,270 .splat,
270 .int_from_float,271 .int_from_float,
src/Sema.zig+10
...@@ -30504,6 +30504,16 @@ fn coerceArrayLike(...@@ -30504,6 +30504,16 @@ fn coerceArrayLike(
30504 }30504 }
30505 }30505 }
3050630506
30507 // Matching element types means no per-element work, so let the backend lower the conversion.
30508 if (dest_ty.isVector(zcu) and
30509 inst_ty.zigTypeTag(zcu) == .array and
30510 inst_ty.childType(zcu).toIntern() == dest_elem_ty.toIntern() and
30511 sema.resolveValue(inst) == null)
30512 {
30513 try sema.requireRuntimeBlock(block, inst_src, null);
30514 return block.addTyOp(.array_to_vector, dest_ty, inst);
30515 }
30516
30507 const element_vals = try sema.arena.alloc(InternPool.Index, dest_len);30517 const element_vals = try sema.arena.alloc(InternPool.Index, dest_len);
30508 const element_refs = try sema.arena.alloc(Air.Inst.Ref, dest_len);30518 const element_refs = try sema.arena.alloc(Air.Inst.Ref, dest_len);
30509 var runtime_src: ?LazySrcLoc = null;30519 var runtime_src: ?LazySrcLoc = null;
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");
8pub fn legalizeFeatures(_: *const std.Target) *const Air.Legalize.Features {8pub 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_to_vector,
11 });12 });
12}13}
1314
src/codegen/aarch64/Select.zig+2
...@@ -258,6 +258,7 @@ pub fn analyze(isel: *Select, air_body: []const Air.Inst.Index) !void {...@@ -258,6 +258,7 @@ pub fn analyze(isel: *Select, air_body: []const Air.Inst.Index) !void {
258 .work_group_size,258 .work_group_size,
259 .work_group_id,259 .work_group_id,
260 .spirv_runtime_array_len,260 .spirv_runtime_array_len,
261 .array_to_vector,
261 => unreachable,262 => unreachable,
262 .ret_ptr => {263 .ret_ptr => {
263 const ty = air_data[@backingInt(air_inst_index)].ty;264 const ty = air_data[@backingInt(air_inst_index)].ty;
...@@ -6295,6 +6296,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,...@@ -6295,6 +6296,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
6295 }6296 }
6296 if (air.next()) |next_air_tag| continue :air_tag next_air_tag;6297 if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
6297 },6298 },
6299 .array_to_vector => unreachable, // legalize .expand_array_to_vector
6298 .array_to_slice => {6300 .array_to_slice => {
6299 if (isel.live_values.fetchRemove(air.inst_index)) |slice_vi| {6301 if (isel.live_values.fetchRemove(air.inst_index)) |slice_vi| {
6300 defer slice_vi.value.deref(isel);6302 defer slice_vi.value.deref(isel);
src/codegen/c.zig+2
...@@ -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_to_vector = true,
4243
43 .scalarize_bit_cast_array = true,44 .scalarize_bit_cast_array = true,
44 .scalarize_bit_cast_vector_non_elementwise = true,45 .scalarize_bit_cast_vector_non_elementwise = true,
...@@ -2870,6 +2871,7 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) Error!void {...@@ -2870,6 +2871,7 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) Error!void {
2870 .store_safe => try airStore(f, inst, true),2871 .store_safe => try airStore(f, inst, true),
2871 .struct_field_ptr => try airStructFieldPtr(f, inst),2872 .struct_field_ptr => try airStructFieldPtr(f, inst),
2872 .array_to_slice => try airArrayToSlice(f, inst),2873 .array_to_slice => try airArrayToSlice(f, inst),
2874 .array_to_vector => unreachable, // legalize .expand_array_to_vector
2873 .cmpxchg_weak => try airCmpxchg(f, inst, "weak"),2875 .cmpxchg_weak => try airCmpxchg(f, inst, "weak"),
2874 .cmpxchg_strong => try airCmpxchg(f, inst, "strong"),2876 .cmpxchg_strong => try airCmpxchg(f, inst, "strong"),
2875 .atomic_rmw => try airAtomicRmw(f, inst),2877 .atomic_rmw => try airAtomicRmw(f, inst),
src/codegen/llvm/FuncGen.zig+43
...@@ -504,6 +504,7 @@ fn genBody(self: *FuncGen, body: []const Air.Inst.Index, coverage_point: Air.Cov...@@ -504,6 +504,7 @@ fn genBody(self: *FuncGen, body: []const Air.Inst.Index, coverage_point: Air.Cov
504 .int_from_float_optimized_safe => unreachable, // handled by `legalizeFeatures`504 .int_from_float_optimized_safe => unreachable, // handled by `legalizeFeatures`
505505
506 .array_to_slice => try self.airArrayToSlice(inst),506 .array_to_slice => try self.airArrayToSlice(inst),
507 .array_to_vector => try self.airArrayToVector(inst),
507 .float_from_int => try self.airFloatFromInt(inst),508 .float_from_int => try self.airFloatFromInt(inst),
508 .cmpxchg_weak => try self.airCmpxchg(inst, .weak),509 .cmpxchg_weak => try self.airCmpxchg(inst, .weak),
509 .cmpxchg_strong => try self.airCmpxchg(inst, .strong),510 .cmpxchg_strong => try self.airCmpxchg(inst, .strong),
...@@ -2027,6 +2028,48 @@ fn airArrayToSlice(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder...@@ -2027,6 +2028,48 @@ fn airArrayToSlice(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder
2027 return self.wip.buildAggregate(slice_llvm_ty, &.{ operand, len }, "");2028 return self.wip.buildAggregate(slice_llvm_ty, &.{ operand, len }, "");
2028}2029}
20292030
2031fn airArrayToVector(fg: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.Value {
2032 const o = fg.object;
2033 const zcu = o.zcu;
2034 const ty_op = fg.air.instructions.items(.data)[@backingInt(inst)].ty_op;
2035 const array_ty = fg.typeOf(ty_op.operand);
2036 const vector_ty = fg.typeOfIndex(inst);
2037 const elem_ty = vector_ty.childType(zcu);
2038 const operand = try fg.resolveInst(ty_op.operand);
2039
2040 assert(array_ty.arrayLen(zcu) == vector_ty.vectorLen(zcu));
2041 assert(array_ty.childType(zcu).toIntern() == elem_ty.toIntern());
2042 assert(isByRef(array_ty, zcu)); // the operand is runtime-known, so the array has runtime bits
2043
2044 // A by-ref vector is lowered as `[n x T]` with the same element representation as the array,
2045 // so the operand is already the result.
2046 if (isByRef(vector_ty, zcu)) return operand;
2047
2048 // LLVM lays `<n x T>` out as `n` consecutive `T`s, just like `[n]T`, so long as `T` is
2049 // accessed as the same type it is used as; then this is one load.
2050 if ((try o.lowerType(elem_ty, .memory_access)) == (try o.lowerType(elem_ty, .as_value)) and
2051 // f80 has an unusual in-memory representation with padding bytes, so is
2052 // not eligible for this optimization
2053 !(elem_ty.isRuntimeFloat() and elem_ty.floatBits(zcu.getTarget()) == 80))
2054 {
2055 return fg.load(operand, array_ty.abiAlignment(zcu), vector_ty, .normal);
2056 }
2057
2058 const llvm_usize = try o.lowerType(.usize, .as_value);
2059 const elem_size = elem_ty.abiSize(zcu);
2060 var vector = try o.builder.poisonValue(try o.lowerType(vector_ty, .as_value));
2061 for (0..@intCast(vector_ty.vectorLen(zcu))) |elem_index| {
2062 const elem_ptr = try fg.ptraddScaled(
2063 operand,
2064 try o.builder.intValue(llvm_usize, elem_index),
2065 elem_size,
2066 );
2067 const elem = try fg.load(elem_ptr, .none, elem_ty, .normal);
2068 vector = try fg.wip.insertElement(vector, elem, try o.builder.intValue(.i32, elem_index), "");
2069 }
2070 return vector;
2071}
2072
2030fn airFloatFromInt(fg: *FuncGen, inst: Air.Inst.Index) TodoError!Builder.Value {2073fn airFloatFromInt(fg: *FuncGen, inst: Air.Inst.Index) TodoError!Builder.Value {
2031 const o = fg.object;2074 const o = fg.object;
2032 const zcu = o.zcu;2075 const zcu = o.zcu;
src/codegen/riscv64/CodeGen.zig+3
...@@ -58,6 +58,8 @@ pub fn legalizeFeatures(_: *const std.Target) *const Air.Legalize.Features {...@@ -58,6 +58,8 @@ pub fn legalizeFeatures(_: *const std.Target) *const Air.Legalize.Features {
58 .expand_add_safe,58 .expand_add_safe,
59 .expand_sub_safe,59 .expand_sub_safe,
60 .expand_mul_safe,60 .expand_mul_safe,
61
62 .expand_array_to_vector,
61 });63 });
62}64}
6365
...@@ -1474,6 +1476,7 @@ fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void {...@@ -1474,6 +1476,7 @@ fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void {
14741476
1475 .slice => try func.airSlice(inst),1477 .slice => try func.airSlice(inst),
1476 .array_to_slice => try func.airArrayToSlice(inst),1478 .array_to_slice => try func.airArrayToSlice(inst),
1479 .array_to_vector => unreachable, // legalize .expand_array_to_vector
14771480
1478 .slice_ptr => try func.airSlicePtr(inst),1481 .slice_ptr => try func.airSlicePtr(inst),
1479 .slice_len => try func.airSliceLen(inst),1482 .slice_len => try func.airSliceLen(inst),
src/codegen/sparc64/CodeGen.zig+4-1
...@@ -41,7 +41,9 @@ const Self = @This();...@@ -41,7 +41,9 @@ const Self = @This();
41const InnerError = codegen.Error || error{OutOfRegisters};41const InnerError = codegen.Error || error{OutOfRegisters};
4242
43pub fn legalizeFeatures(_: *const std.Target) ?*const Air.Legalize.Features {43pub fn legalizeFeatures(_: *const std.Target) ?*const Air.Legalize.Features {
44 return null;44 return comptime &.initMany(&.{
45 .expand_array_to_vector,
46 });
45}47}
4648
47const RegisterView = enum(u1) {49const RegisterView = enum(u1) {
...@@ -578,6 +580,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -578,6 +580,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
578 .struct_field_ptr=> try self.airStructFieldPtr(inst),580 .struct_field_ptr=> try self.airStructFieldPtr(inst),
579 .agg_field_val => try self.airAggFieldVal(inst),581 .agg_field_val => try self.airAggFieldVal(inst),
580 .array_to_slice => try self.airArrayToSlice(inst),582 .array_to_slice => try self.airArrayToSlice(inst),
583 .array_to_vector => unreachable, // legalize .expand_array_to_vector
581 .float_from_int => try self.airFloatFromInt(inst),584 .float_from_int => try self.airFloatFromInt(inst),
582 .int_from_float => try self.airIntFromFloat(inst),585 .int_from_float => try self.airIntFromFloat(inst),
583 .cmpxchg_strong,586 .cmpxchg_strong,
src/codegen/spirv/CodeGen.zig+3
...@@ -128,6 +128,8 @@ pub fn legalizeFeatures(_: *const std.Target) *const Air.Legalize.Features {...@@ -128,6 +128,8 @@ pub fn legalizeFeatures(_: *const std.Target) *const Air.Legalize.Features {
128 .expand_add_safe,128 .expand_add_safe,
129 .expand_sub_safe,129 .expand_sub_safe,
130 .expand_mul_safe,130 .expand_mul_safe,
131
132 .expand_array_to_vector,
131 });133 });
132}134}
133135
...@@ -4429,6 +4431,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) Error!void {...@@ -4429,6 +4431,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) Error!void {
4429 .not => try cg.airNot(inst),4431 .not => try cg.airNot(inst),
44304432
4431 .array_to_slice => try cg.airArrayToSlice(inst),4433 .array_to_slice => try cg.airArrayToSlice(inst),
4434 .array_to_vector => unreachable, // legalize .expand_array_to_vector
4432 .slice => try cg.airSlice(inst),4435 .slice => try cg.airSlice(inst),
4433 .aggregate_init => try cg.airAggregateInit(inst),4436 .aggregate_init => try cg.airAggregateInit(inst),
4434 .memcpy => return cg.airMemcpy(inst),4437 .memcpy => return cg.airMemcpy(inst),
src/codegen/wasm/CodeGen.zig+2
...@@ -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_to_vector,
4142
42 .scalarize_add,43 .scalarize_add,
43 .scalarize_add_optimized,44 .scalarize_add_optimized,
...@@ -1753,6 +1754,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -1753,6 +1754,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
17531754
1754 .array_elem_val => cg.airArrayElemVal(inst),1755 .array_elem_val => cg.airArrayElemVal(inst),
1755 .array_to_slice => cg.airArrayToSlice(inst),1756 .array_to_slice => cg.airArrayToSlice(inst),
1757 .array_to_vector => unreachable, // legalize .expand_array_to_vector
1756 .alloc => cg.airAlloc(inst),1758 .alloc => cg.airAlloc(inst),
1757 .arg => cg.airArg(inst),1759 .arg => cg.airArg(inst),
1758 .block => cg.airBlock(inst),1760 .block => cg.airBlock(inst),
src/codegen/x86_64/CodeGen.zig+2
...@@ -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_to_vector,
81 });82 });
82}83}
8384
...@@ -104405,6 +104406,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {...@@ -104405,6 +104406,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
104405 }104406 }
104406 try ops[0].finish(inst, &.{ bin_op.lhs, bin_op.rhs }, &ops, cg);104407 try ops[0].finish(inst, &.{ bin_op.lhs, bin_op.rhs }, &ops, cg);
104407 },104408 },
104409 .array_to_vector => unreachable, // legalize .expand_array_to_vector
104408 .array_to_slice => {104410 .array_to_slice => {
104409 const ty_op = air_datas[@backingInt(inst)].ty_op;104411 const ty_op = air_datas[@backingInt(inst)].ty_op;
104410 var ops = try cg.tempsFromOperands(inst, .{ty_op.operand});104412 var ops = try cg.tempsFromOperands(inst, .{ty_op.operand});
test/behavior/vector.zig+198
...@@ -229,6 +229,204 @@ test "array to vector" {...@@ -229,6 +229,204 @@ test "array to vector" {
229 try comptime S.doTheTest();229 try comptime S.doTheTest();
230}230}
231231
232test "array of abi-sized integer to vector of same type" {
233 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
234 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
235 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
236 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
237
238 const S = struct {
239 const one: u32 = 1;
240 const two: u32 = 2;
241
242 fn doTheTest() !void {
243 {
244 var arr: [8]u8 = .{ 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF };
245 const vec: @Vector(8, u8) = arr;
246 arr[0] = 0; // should not affect `vec`
247 try expect(vec[0] == 0x01);
248 try expect(vec[1] == 0x23);
249 try expect(vec[2] == 0x45);
250 try expect(vec[3] == 0x67);
251 try expect(vec[4] == 0x89);
252 try expect(vec[5] == 0xAB);
253 try expect(vec[6] == 0xCD);
254 try expect(vec[7] == 0xEF);
255 }
256
257 {
258 var arr: [4]u16 = .{ 0x0123, 0x4567, 0x89AB, 0xCDEF };
259 const vec: @Vector(4, u16) = arr;
260 arr[0] = 0; // should not affect `vec`
261 try expect(vec[0] == 0x0123);
262 try expect(vec[1] == 0x4567);
263 try expect(vec[2] == 0x89AB);
264 try expect(vec[3] == 0xCDEF);
265 }
266
267 {
268 var arr: [2]u32 = .{ 0x01234567, 0x89ABCDEF };
269 const vec: @Vector(2, u32) = arr;
270 arr[0] = 0; // should not affect `vec`
271 try expect(vec[0] == 0x01234567);
272 try expect(vec[1] == 0x89ABCDEF);
273 }
274
275 {
276 var arr: [1]u64 = .{0x0123456789ABCDEF};
277 const vec: @Vector(1, u64) = arr;
278 arr[0] = 0; // should not affect `vec`
279 try expect(vec[0] == 0x0123456789ABCDEF);
280 }
281
282 // Like the u16 case, but a non-power-of-two size.
283 {
284 var arr: [5]u16 = .{ 0x0123, 0x4567, 0x89AB, 0xCDEF, 0xDEAD };
285 const vec: @Vector(5, u16) = arr;
286 arr[0] = 0; // should not affect `vec`
287 try expect(vec[0] == 0x0123);
288 try expect(vec[1] == 0x4567);
289 try expect(vec[2] == 0x89AB);
290 try expect(vec[3] == 0xCDEF);
291 try expect(vec[4] == 0xDEAD);
292 }
293
294 // Like the u32 case, but a non-power-of-two size.
295 {
296 var arr: [3]u32 = .{ 0x01234567, 0x89ABCDEF, 0xDEADBEEF };
297 const vec: @Vector(3, u32) = arr;
298 arr[0] = 0; // should not affect `vec`
299 try expect(vec[0] == 0x01234567);
300 try expect(vec[1] == 0x89ABCDEF);
301 try expect(vec[2] == 0xDEADBEEF);
302 }
303
304 {
305 var arr: [2]*const u32 = .{ &one, &two };
306 const vec: @Vector(2, *const u32) = arr;
307 arr[0] = &two; // should not affect `vec`
308 try expect(vec[0] == &one);
309 try expect(vec[1] == &two);
310 }
311 }
312 };
313 try S.doTheTest();
314 try comptime S.doTheTest();
315}
316
317test "array of float to vector of same type" {
318 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
319 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
320 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
321 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
322 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
323
324 const S = struct {
325 fn doTheTest() !void {
326 {
327 var arr: [4]f16 = .{ 1.5, 2.5, 3.5, 4.5 };
328 const vec: @Vector(4, f16) = arr;
329 arr[0] = 0; // should not affect `vec`
330 try expect(vec[0] == 1.5);
331 try expect(vec[1] == 2.5);
332 try expect(vec[2] == 3.5);
333 try expect(vec[3] == 4.5);
334 }
335
336 {
337 var arr: [4]f32 = .{ 1.5, 2.5, 3.5, 4.5 };
338 const vec: @Vector(4, f32) = arr;
339 arr[0] = 0; // should not affect `vec`
340 try expect(vec[0] == 1.5);
341 try expect(vec[1] == 2.5);
342 try expect(vec[2] == 3.5);
343 try expect(vec[3] == 4.5);
344 }
345
346 {
347 var arr: [4]f64 = .{ 1.5, 2.5, 3.5, 4.5 };
348 const vec: @Vector(4, f64) = arr;
349 arr[0] = 0; // should not affect `vec`
350 try expect(vec[0] == 1.5);
351 try expect(vec[1] == 2.5);
352 try expect(vec[2] == 3.5);
353 try expect(vec[3] == 4.5);
354 }
355
356 {
357 var arr: [2]f80 = .{ 1.5, 2.5 };
358 const vec: @Vector(2, f80) = arr;
359 arr[0] = 0; // should not affect `vec`
360 try expect(vec[0] == 1.5);
361 try expect(vec[1] == 2.5);
362 }
363
364 {
365 var arr: [2]f128 = .{ 3.5, 4.5 };
366 const vec: @Vector(2, f128) = arr;
367 arr[0] = 0; // should not affect `vec`
368 try expect(vec[0] == 3.5);
369 try expect(vec[1] == 4.5);
370 }
371 }
372 };
373 try S.doTheTest();
374 try comptime S.doTheTest();
375}
376
377test "array of non-abi-sized integer to vector of same type" {
378 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
379 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
380 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
381 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
382
383 const S = struct {
384 fn doTheTest() !void {
385 {
386 var arr: [5]bool = .{ false, true, false, true, true };
387 const vec: @Vector(5, bool) = arr;
388 arr[0] = true; // should not affect `vec`
389 try expect(vec[0] == false);
390 try expect(vec[1] == true);
391 try expect(vec[2] == false);
392 try expect(vec[3] == true);
393 try expect(vec[4] == true);
394 }
395
396 {
397 var arr: [4]u1 = .{ 1, 0, 1, 1 };
398 const vec: @Vector(4, u1) = arr;
399 arr[0] = 0; // should not affect `vec`
400 try expect(vec[0] == 1);
401 try expect(vec[1] == 0);
402 try expect(vec[2] == 1);
403 try expect(vec[3] == 1);
404 }
405
406 {
407 var arr: [4]u3 = .{ 0, 3, 5, 7 };
408 const vec: @Vector(4, u3) = arr;
409 arr[0] = 1; // should not affect `vec`
410 try expect(vec[0] == 0);
411 try expect(vec[1] == 3);
412 try expect(vec[2] == 5);
413 try expect(vec[3] == 7);
414 }
415
416 {
417 var arr: [3]u24 = .{ 0x010203, 0x040506, 0xFF0000 };
418 const vec: @Vector(3, u24) = arr;
419 arr[0] = 0; // should not affect `vec`
420 try expect(vec[0] == 0x010203);
421 try expect(vec[1] == 0x040506);
422 try expect(vec[2] == 0xFF0000);
423 }
424 }
425 };
426 try S.doTheTest();
427 try comptime S.doTheTest();
428}
429
232test "array vector coercion - odd sizes" {430test "array vector coercion - odd sizes" {
233 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;431 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
234 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO432 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
test/llvm_ir.zig+19
...@@ -136,6 +136,25 @@ pub fn addCases(cases: *tests.LlvmIrContext) void {...@@ -136,6 +136,25 @@ pub fn addCases(cases: *tests.LlvmIrContext) void {
136 " %8 = zext i1 %4 to i8",136 " %8 = zext i1 %4 to i8",
137 " store i8 %8, ptr %1, align 2",137 " store i8 %8, ptr %1, align 2",
138 }, .{ .strip = true });138 }, .{ .strip = true });
139
140 cases.addMatches("array to vector coercion is one vector load",
141 \\export fn entry(arr: *const [16]u8) @Vector(16, u8) {
142 \\ return arr.*;
143 \\}
144 , &.{
145 "load <16 x i8>, ptr",
146 }, .{});
147
148 cases.addMatches("array to vector coercion feeding overlapping shuffles",
149 \\export fn entry(a: *@Vector(8, u8), b: *@Vector(8, u8), src: [*]const u8) void {
150 \\ const q: @Vector(16, u8) = src[0..16].*;
151 \\ a.* = @shuffle(u8, q, undefined, @Vector(8, i32){ 0, 1, 2, 3, 4, 5, 6, 7 });
152 \\ b.* = @shuffle(u8, q, undefined, @Vector(8, i32){ 7, 8, 9, 10, 11, 12, 13, 14 });
153 \\}
154 , &.{
155 "load <16 x i8>, ptr",
156 "shufflevector <16 x i8>",
157 }, .{});
139}158}
140159
141const std = @import("std");160const std = @import("std");