From a72236ae8e9f11f42351ae42b1242f6091216c45 Mon Sep 17 00:00:00 2001 From: Guy Fischman Date: Tue, 11 Aug 2026 10:03:55 +0200 Subject: [PATCH] compiler: lower array-to-vector coercion in the backend --- src/Air.zig | 12 ++ src/Air/Legalize.zig | 47 ++++++++ src/Air/Liveness.zig | 1 + src/Air/Liveness/Verify.zig | 1 + src/Air/Verify.zig | 1 + src/Air/print.zig | 1 + src/Sema.zig | 10 ++ src/codegen/aarch64.zig | 1 + src/codegen/aarch64/Select.zig | 2 + src/codegen/c.zig | 2 + src/codegen/llvm/FuncGen.zig | 43 +++++++ src/codegen/riscv64/CodeGen.zig | 3 + src/codegen/sparc64/CodeGen.zig | 5 +- src/codegen/spirv/CodeGen.zig | 3 + src/codegen/wasm/CodeGen.zig | 2 + src/codegen/x86_64/CodeGen.zig | 2 + test/behavior/vector.zig | 198 ++++++++++++++++++++++++++++++++ test/llvm_ir.zig | 19 +++ 18 files changed, 352 insertions(+), 1 deletion(-) diff --git a/src/Air.zig b/src/Air.zig index ad4526f0760417f5b90819864a73c62e7f6c00a3..e86637581c6a41fd4eac9deb0c550e21d01d1552 100644 --- a/src/Air.zig +++ b/src/Air.zig @@ -748,6 +748,16 @@ pub const Inst = struct { /// Given a pointer to an array, return a slice. /// Uses the `ty_op` field. array_to_slice, + /// Given an array, return a vector with the same element type and length. A sentinel on + /// the operand type is not included in the result. + /// + /// Vectors have no well-defined in-memory layout, so only the backend can know whether + /// the array representation may be reinterpreted rather than copied element-by-element. + /// Backends which do not lower this directly can enable + /// `Air.Legalize.Feature.expand_array_to_vector`. + /// + /// Uses the `ty_op` field. + array_to_vector, /// Given a float operand, return the integer with the closest mathematical meaning. /// Uses the `ty_op` field. int_from_float, @@ -1754,6 +1764,7 @@ pub fn typeOfIndex(air: *const Air, inst: Air.Inst.Index, ip: *const InternPool) .struct_field_ptr_index_2, .struct_field_ptr_index_3, .array_to_slice, + .array_to_vector, .int_from_float, .int_from_float_optimized, .int_from_float_safe, @@ -2106,6 +2117,7 @@ pub fn mustLower(air: Air, inst: Air.Inst.Index, ip: *const InternPool) bool { .slice_elem_ptr, .ptr_elem_ptr, .array_to_slice, + .array_to_vector, .int_from_float, .int_from_float_optimized, .float_from_int, diff --git a/src/Air/Legalize.zig b/src/Air/Legalize.zig index 64fd462b7bd8e041a65c3ec89616db830776349b..3c426ec57a195cf211bcbc2a75261030ea563402 100644 --- a/src/Air/Legalize.zig +++ b/src/Air/Legalize.zig @@ -200,6 +200,8 @@ pub const Feature = enum { expand_packed_agg_field_val, /// Replace `aggregate_init` of a packed struct with a sequence of `shl_exact`, `bit_cast`, `int_cast`, and `bit_or`. expand_packed_aggregate_init, + /// Replace `array_to_vector` with an `array_elem_val` per element followed by an `aggregate_init`. + expand_array_to_vector, /// Replace all arithmetic operations on 16-bit floating-point types with calls to soft-float /// 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 { .ptr_elem_ptr, .array_to_slice, => {}, + .array_to_vector => if (l.features.has(.expand_array_to_vector)) { + continue :inst l.replaceInst(inst, .block, try l.arrayToVectorBlockPayload(inst)); + }, inline .reduce, .reduce_optimized => |air_tag| { const reduce = l.air_instructions.items(.data)[@backingInt(inst)].reduce; const vector_ty = l.typeOf(reduce.operand); @@ -2954,6 +2959,48 @@ fn packedAggregateInitBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Erro } }; } +fn arrayToVectorBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error!Air.Inst.Data { + const pt = l.pt; + const zcu = pt.zcu; + const gpa = zcu.gpa; + + const orig_ty_op = l.air_instructions.items(.data)[@backingInt(orig_inst)].ty_op; + const vec_ty = orig_ty_op.ty.toType(); + const len: usize = @intCast(vec_ty.vectorLen(zcu)); + + var bfa_buf: [64 + 2]Air.Inst.Index = undefined; + var bfa_state: std.heap.BufferFirstAllocator = .init(@ptrCast(&bfa_buf), gpa); + const bfa = bfa_state.allocator(); + + const inst_buf = try bfa.alloc(Air.Inst.Index, len + 2); + defer bfa.free(inst_buf); + + var main_block: Block = .init(inst_buf); + try l.air_instructions.ensureUnusedCapacity(gpa, inst_buf.len); + try l.air_extra.ensureUnusedCapacity(gpa, len); + + const elems_start: u32 = @intCast(l.air_extra.items.len); + for (0..len) |elem_index| { + const index_ref: Air.Inst.Ref = .fromValue(try pt.intValue(.usize, elem_index)); + const elem = main_block.addBinOp(l, .array_elem_val, orig_ty_op.operand, index_ref).toRef(); + l.air_extra.appendAssumeCapacity(@backingInt(elem)); + } + + const result = main_block.add(l, .{ + .tag = .aggregate_init, + .data = .{ .ty_pl = .{ + .ty = .fromType(vec_ty), + .payload = elems_start, + } }, + }).toRef(); + main_block.addBr(l, orig_inst, result); + + return .{ .ty_pl = .{ + .ty = .fromType(vec_ty), + .payload = try l.addBlockBody(main_block.body()), + } }; +} + /// Given a `std.math.big.int.Const`, converts it to a `Value` which is a float of type `float_ty` /// representing the same numeric value. If the integer cannot be exactly represented, `round` /// decides whether the value should be rounded up or down. If `is_vector`, then `float_ty` is diff --git a/src/Air/Liveness.zig b/src/Air/Liveness.zig index 3940ae27b8446d71c053e938dbbb9344b56daa7e..6089d2c7bc2f5092e9dad7f7dec174a4af1a0b2c 100644 --- a/src/Air/Liveness.zig +++ b/src/Air/Liveness.zig @@ -525,6 +525,7 @@ fn analyzeInst( .struct_field_ptr_index_2, .struct_field_ptr_index_3, .array_to_slice, + .array_to_vector, .int_from_float, .int_from_float_optimized, .int_from_float_safe, diff --git a/src/Air/Liveness/Verify.zig b/src/Air/Liveness/Verify.zig index c136509de9156318907cf45c0473c544cca04bc5..832728c1c660156cbec6008cf4c369ae6b6f0dc1 100644 --- a/src/Air/Liveness/Verify.zig +++ b/src/Air/Liveness/Verify.zig @@ -113,6 +113,7 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void { .struct_field_ptr_index_2, .struct_field_ptr_index_3, .array_to_slice, + .array_to_vector, .int_from_float, .int_from_float_optimized, .int_from_float_safe, diff --git a/src/Air/Verify.zig b/src/Air/Verify.zig index 438de7d1b43471f36535a6bc1ea3af7359d8b1f3..4b6e6a29118187877c621a8a92dcabe09dd5feaa 100644 --- a/src/Air/Verify.zig +++ b/src/Air/Verify.zig @@ -380,6 +380,7 @@ fn body(verify: *Verify, body_insts: []const Air.Inst.Index) Error!void { .slice_elem_ptr, .ptr_elem_val, .array_to_slice, + .array_to_vector, .int_from_float, .int_from_float_optimized, .int_from_float_safe, diff --git a/src/Air/print.zig b/src/Air/print.zig index 1712607a673187a351788aeda3907f7e614a2044..d8fe54ed7f33e0d9190a1b23bcfda9b7420b93bb 100644 --- a/src/Air/print.zig +++ b/src/Air/print.zig @@ -265,6 +265,7 @@ const Writer = struct { .struct_field_ptr_index_2, .struct_field_ptr_index_3, .array_to_slice, + .array_to_vector, .float_from_int, .splat, .int_from_float, diff --git a/src/Sema.zig b/src/Sema.zig index 920bf997512c7c59bcca6715c7cf30c4f02e9bb1..e78041fc91819a65787410250222a1da6fd649de 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -30504,6 +30504,16 @@ fn coerceArrayLike( } } + // Matching element types means no per-element work, so let the backend lower the conversion. + if (dest_ty.isVector(zcu) and + inst_ty.zigTypeTag(zcu) == .array and + inst_ty.childType(zcu).toIntern() == dest_elem_ty.toIntern() and + sema.resolveValue(inst) == null) + { + try sema.requireRuntimeBlock(block, inst_src, null); + return block.addTyOp(.array_to_vector, dest_ty, inst); + } + const element_vals = try sema.arena.alloc(InternPool.Index, dest_len); const element_refs = try sema.arena.alloc(Air.Inst.Ref, dest_len); var runtime_src: ?LazySrcLoc = null; diff --git a/src/codegen/aarch64.zig b/src/codegen/aarch64.zig index a6eaac253ffd654ef418d6fbcff4577de9b0c4e9..0b53fdb79a1cb1e53848725991521601f2fa3647 100644 --- a/src/codegen/aarch64.zig +++ b/src/codegen/aarch64.zig @@ -8,6 +8,7 @@ pub const Select = @import("aarch64/Select.zig"); pub fn legalizeFeatures(_: *const std.Target) *const Air.Legalize.Features { return comptime &.initMany(&.{ .expand_bit_cast_safe, + .expand_array_to_vector, }); } diff --git a/src/codegen/aarch64/Select.zig b/src/codegen/aarch64/Select.zig index 1e5f1cbb6e7eee1fc176bff68fa0511dd8ec1b7a..e5bbeb4f5fed1256e23b30ea73e19a994ba504a8 100644 --- a/src/codegen/aarch64/Select.zig +++ b/src/codegen/aarch64/Select.zig @@ -258,6 +258,7 @@ pub fn analyze(isel: *Select, air_body: []const Air.Inst.Index) !void { .work_group_size, .work_group_id, .spirv_runtime_array_len, + .array_to_vector, => unreachable, .ret_ptr => { 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, } if (air.next()) |next_air_tag| continue :air_tag next_air_tag; }, + .array_to_vector => unreachable, // legalize .expand_array_to_vector .array_to_slice => { if (isel.live_values.fetchRemove(air.inst_index)) |slice_vi| { defer slice_vi.value.deref(isel); diff --git a/src/codegen/c.zig b/src/codegen/c.zig index f2c8431dcab682067b18a9c785e1ac2edda4ade5..977295ad3013414ebee2739b251da717e735a358 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -39,6 +39,7 @@ pub fn legalizeFeatures(_: *const std.Target) ?*const Air.Legalize.Features { .expand_packed_store = true, .expand_packed_agg_field_val = true, .expand_packed_aggregate_init = true, + .expand_array_to_vector = true, .scalarize_bit_cast_array = true, .scalarize_bit_cast_vector_non_elementwise = true, @@ -2870,6 +2871,7 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) Error!void { .store_safe => try airStore(f, inst, true), .struct_field_ptr => try airStructFieldPtr(f, inst), .array_to_slice => try airArrayToSlice(f, inst), + .array_to_vector => unreachable, // legalize .expand_array_to_vector .cmpxchg_weak => try airCmpxchg(f, inst, "weak"), .cmpxchg_strong => try airCmpxchg(f, inst, "strong"), .atomic_rmw => try airAtomicRmw(f, inst), diff --git a/src/codegen/llvm/FuncGen.zig b/src/codegen/llvm/FuncGen.zig index 7f3e4beb41b29b046bfa1ae30b38c51f4a32df51..8cc966db96eac388e480c231c409b24af5b657db 100644 --- a/src/codegen/llvm/FuncGen.zig +++ b/src/codegen/llvm/FuncGen.zig @@ -504,6 +504,7 @@ fn genBody(self: *FuncGen, body: []const Air.Inst.Index, coverage_point: Air.Cov .int_from_float_optimized_safe => unreachable, // handled by `legalizeFeatures` .array_to_slice => try self.airArrayToSlice(inst), + .array_to_vector => try self.airArrayToVector(inst), .float_from_int => try self.airFloatFromInt(inst), .cmpxchg_weak => try self.airCmpxchg(inst, .weak), .cmpxchg_strong => try self.airCmpxchg(inst, .strong), @@ -2027,6 +2028,48 @@ fn airArrayToSlice(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder return self.wip.buildAggregate(slice_llvm_ty, &.{ operand, len }, ""); } +fn airArrayToVector(fg: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.Value { + const o = fg.object; + const zcu = o.zcu; + const ty_op = fg.air.instructions.items(.data)[@backingInt(inst)].ty_op; + const array_ty = fg.typeOf(ty_op.operand); + const vector_ty = fg.typeOfIndex(inst); + const elem_ty = vector_ty.childType(zcu); + const operand = try fg.resolveInst(ty_op.operand); + + assert(array_ty.arrayLen(zcu) == vector_ty.vectorLen(zcu)); + assert(array_ty.childType(zcu).toIntern() == elem_ty.toIntern()); + assert(isByRef(array_ty, zcu)); // the operand is runtime-known, so the array has runtime bits + + // A by-ref vector is lowered as `[n x T]` with the same element representation as the array, + // so the operand is already the result. + if (isByRef(vector_ty, zcu)) return operand; + + // LLVM lays `` out as `n` consecutive `T`s, just like `[n]T`, so long as `T` is + // accessed as the same type it is used as; then this is one load. + if ((try o.lowerType(elem_ty, .memory_access)) == (try o.lowerType(elem_ty, .as_value)) and + // f80 has an unusual in-memory representation with padding bytes, so is + // not eligible for this optimization + !(elem_ty.isRuntimeFloat() and elem_ty.floatBits(zcu.getTarget()) == 80)) + { + return fg.load(operand, array_ty.abiAlignment(zcu), vector_ty, .normal); + } + + const llvm_usize = try o.lowerType(.usize, .as_value); + const elem_size = elem_ty.abiSize(zcu); + var vector = try o.builder.poisonValue(try o.lowerType(vector_ty, .as_value)); + for (0..@intCast(vector_ty.vectorLen(zcu))) |elem_index| { + const elem_ptr = try fg.ptraddScaled( + operand, + try o.builder.intValue(llvm_usize, elem_index), + elem_size, + ); + const elem = try fg.load(elem_ptr, .none, elem_ty, .normal); + vector = try fg.wip.insertElement(vector, elem, try o.builder.intValue(.i32, elem_index), ""); + } + return vector; +} + fn airFloatFromInt(fg: *FuncGen, inst: Air.Inst.Index) TodoError!Builder.Value { const o = fg.object; const zcu = o.zcu; diff --git a/src/codegen/riscv64/CodeGen.zig b/src/codegen/riscv64/CodeGen.zig index 1a6ae84190bf394992f5e30b305de583be9b5829..17561ce3a902510c50446d4a202724cd156039e5 100644 --- a/src/codegen/riscv64/CodeGen.zig +++ b/src/codegen/riscv64/CodeGen.zig @@ -58,6 +58,8 @@ pub fn legalizeFeatures(_: *const std.Target) *const Air.Legalize.Features { .expand_add_safe, .expand_sub_safe, .expand_mul_safe, + + .expand_array_to_vector, }); } @@ -1474,6 +1476,7 @@ fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void { .slice => try func.airSlice(inst), .array_to_slice => try func.airArrayToSlice(inst), + .array_to_vector => unreachable, // legalize .expand_array_to_vector .slice_ptr => try func.airSlicePtr(inst), .slice_len => try func.airSliceLen(inst), diff --git a/src/codegen/sparc64/CodeGen.zig b/src/codegen/sparc64/CodeGen.zig index be9102f81ec684c11afe4cce59742823704b3175..c0ccd968fe60f3ee0fd2a8bd3f637d521734e418 100644 --- a/src/codegen/sparc64/CodeGen.zig +++ b/src/codegen/sparc64/CodeGen.zig @@ -41,7 +41,9 @@ const Self = @This(); const InnerError = codegen.Error || error{OutOfRegisters}; pub fn legalizeFeatures(_: *const std.Target) ?*const Air.Legalize.Features { - return null; + return comptime &.initMany(&.{ + .expand_array_to_vector, + }); } const RegisterView = enum(u1) { @@ -578,6 +580,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .struct_field_ptr=> try self.airStructFieldPtr(inst), .agg_field_val => try self.airAggFieldVal(inst), .array_to_slice => try self.airArrayToSlice(inst), + .array_to_vector => unreachable, // legalize .expand_array_to_vector .float_from_int => try self.airFloatFromInt(inst), .int_from_float => try self.airIntFromFloat(inst), .cmpxchg_strong, diff --git a/src/codegen/spirv/CodeGen.zig b/src/codegen/spirv/CodeGen.zig index 1b46cfbbc1fcb105d85bdb6a0bf7b05a480cbcea..f89fbdb4e40e957e6f180f053d2d19524c174824 100644 --- a/src/codegen/spirv/CodeGen.zig +++ b/src/codegen/spirv/CodeGen.zig @@ -128,6 +128,8 @@ pub fn legalizeFeatures(_: *const std.Target) *const Air.Legalize.Features { .expand_add_safe, .expand_sub_safe, .expand_mul_safe, + + .expand_array_to_vector, }); } @@ -4429,6 +4431,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) Error!void { .not => try cg.airNot(inst), .array_to_slice => try cg.airArrayToSlice(inst), + .array_to_vector => unreachable, // legalize .expand_array_to_vector .slice => try cg.airSlice(inst), .aggregate_init => try cg.airAggregateInit(inst), .memcpy => return cg.airMemcpy(inst), diff --git a/src/codegen/wasm/CodeGen.zig b/src/codegen/wasm/CodeGen.zig index df15671e3379fabd3fbd3e2a75a015367b0e77a0..7c6733259b4b2f8f591534836ffadbe18868fe31 100644 --- a/src/codegen/wasm/CodeGen.zig +++ b/src/codegen/wasm/CodeGen.zig @@ -38,6 +38,7 @@ pub fn legalizeFeatures(_: *const std.Target) *const Air.Legalize.Features { .expand_packed_store, .expand_packed_agg_field_val, .expand_packed_aggregate_init, + .expand_array_to_vector, .scalarize_add, .scalarize_add_optimized, @@ -1753,6 +1754,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { .array_elem_val => cg.airArrayElemVal(inst), .array_to_slice => cg.airArrayToSlice(inst), + .array_to_vector => unreachable, // legalize .expand_array_to_vector .alloc => cg.airAlloc(inst), .arg => cg.airArg(inst), .block => cg.airBlock(inst), diff --git a/src/codegen/x86_64/CodeGen.zig b/src/codegen/x86_64/CodeGen.zig index 063a8c158f3ff592ded65497dc14640098588e31..b073b4923ca63483a352a383ac33996f7aac882b 100644 --- a/src/codegen/x86_64/CodeGen.zig +++ b/src/codegen/x86_64/CodeGen.zig @@ -78,6 +78,7 @@ pub fn legalizeFeatures(_: *const std.Target) *const Air.Legalize.Features { .expand_packed_store, .expand_packed_agg_field_val, .expand_packed_aggregate_init, + .expand_array_to_vector, }); } @@ -104405,6 +104406,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { } try ops[0].finish(inst, &.{ bin_op.lhs, bin_op.rhs }, &ops, cg); }, + .array_to_vector => unreachable, // legalize .expand_array_to_vector .array_to_slice => { const ty_op = air_datas[@backingInt(inst)].ty_op; var ops = try cg.tempsFromOperands(inst, .{ty_op.operand}); diff --git a/test/behavior/vector.zig b/test/behavior/vector.zig index 1b7d354e19c92996c8b6f30dde1acc2ad5914a4a..9b77994848b2bfbfab21b015c437ad2b6f5a6f54 100644 --- a/test/behavior/vector.zig +++ b/test/behavior/vector.zig @@ -229,6 +229,204 @@ test "array to vector" { try comptime S.doTheTest(); } +test "array of abi-sized integer to vector of same type" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; + + const S = struct { + const one: u32 = 1; + const two: u32 = 2; + + fn doTheTest() !void { + { + var arr: [8]u8 = .{ 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF }; + const vec: @Vector(8, u8) = arr; + arr[0] = 0; // should not affect `vec` + try expect(vec[0] == 0x01); + try expect(vec[1] == 0x23); + try expect(vec[2] == 0x45); + try expect(vec[3] == 0x67); + try expect(vec[4] == 0x89); + try expect(vec[5] == 0xAB); + try expect(vec[6] == 0xCD); + try expect(vec[7] == 0xEF); + } + + { + var arr: [4]u16 = .{ 0x0123, 0x4567, 0x89AB, 0xCDEF }; + const vec: @Vector(4, u16) = arr; + arr[0] = 0; // should not affect `vec` + try expect(vec[0] == 0x0123); + try expect(vec[1] == 0x4567); + try expect(vec[2] == 0x89AB); + try expect(vec[3] == 0xCDEF); + } + + { + var arr: [2]u32 = .{ 0x01234567, 0x89ABCDEF }; + const vec: @Vector(2, u32) = arr; + arr[0] = 0; // should not affect `vec` + try expect(vec[0] == 0x01234567); + try expect(vec[1] == 0x89ABCDEF); + } + + { + var arr: [1]u64 = .{0x0123456789ABCDEF}; + const vec: @Vector(1, u64) = arr; + arr[0] = 0; // should not affect `vec` + try expect(vec[0] == 0x0123456789ABCDEF); + } + + // Like the u16 case, but a non-power-of-two size. + { + var arr: [5]u16 = .{ 0x0123, 0x4567, 0x89AB, 0xCDEF, 0xDEAD }; + const vec: @Vector(5, u16) = arr; + arr[0] = 0; // should not affect `vec` + try expect(vec[0] == 0x0123); + try expect(vec[1] == 0x4567); + try expect(vec[2] == 0x89AB); + try expect(vec[3] == 0xCDEF); + try expect(vec[4] == 0xDEAD); + } + + // Like the u32 case, but a non-power-of-two size. + { + var arr: [3]u32 = .{ 0x01234567, 0x89ABCDEF, 0xDEADBEEF }; + const vec: @Vector(3, u32) = arr; + arr[0] = 0; // should not affect `vec` + try expect(vec[0] == 0x01234567); + try expect(vec[1] == 0x89ABCDEF); + try expect(vec[2] == 0xDEADBEEF); + } + + { + var arr: [2]*const u32 = .{ &one, &two }; + const vec: @Vector(2, *const u32) = arr; + arr[0] = &two; // should not affect `vec` + try expect(vec[0] == &one); + try expect(vec[1] == &two); + } + } + }; + try S.doTheTest(); + try comptime S.doTheTest(); +} + +test "array of float to vector of same type" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO + + const S = struct { + fn doTheTest() !void { + { + var arr: [4]f16 = .{ 1.5, 2.5, 3.5, 4.5 }; + const vec: @Vector(4, f16) = arr; + arr[0] = 0; // should not affect `vec` + try expect(vec[0] == 1.5); + try expect(vec[1] == 2.5); + try expect(vec[2] == 3.5); + try expect(vec[3] == 4.5); + } + + { + var arr: [4]f32 = .{ 1.5, 2.5, 3.5, 4.5 }; + const vec: @Vector(4, f32) = arr; + arr[0] = 0; // should not affect `vec` + try expect(vec[0] == 1.5); + try expect(vec[1] == 2.5); + try expect(vec[2] == 3.5); + try expect(vec[3] == 4.5); + } + + { + var arr: [4]f64 = .{ 1.5, 2.5, 3.5, 4.5 }; + const vec: @Vector(4, f64) = arr; + arr[0] = 0; // should not affect `vec` + try expect(vec[0] == 1.5); + try expect(vec[1] == 2.5); + try expect(vec[2] == 3.5); + try expect(vec[3] == 4.5); + } + + { + var arr: [2]f80 = .{ 1.5, 2.5 }; + const vec: @Vector(2, f80) = arr; + arr[0] = 0; // should not affect `vec` + try expect(vec[0] == 1.5); + try expect(vec[1] == 2.5); + } + + { + var arr: [2]f128 = .{ 3.5, 4.5 }; + const vec: @Vector(2, f128) = arr; + arr[0] = 0; // should not affect `vec` + try expect(vec[0] == 3.5); + try expect(vec[1] == 4.5); + } + } + }; + try S.doTheTest(); + try comptime S.doTheTest(); +} + +test "array of non-abi-sized integer to vector of same type" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; + + const S = struct { + fn doTheTest() !void { + { + var arr: [5]bool = .{ false, true, false, true, true }; + const vec: @Vector(5, bool) = arr; + arr[0] = true; // should not affect `vec` + try expect(vec[0] == false); + try expect(vec[1] == true); + try expect(vec[2] == false); + try expect(vec[3] == true); + try expect(vec[4] == true); + } + + { + var arr: [4]u1 = .{ 1, 0, 1, 1 }; + const vec: @Vector(4, u1) = arr; + arr[0] = 0; // should not affect `vec` + try expect(vec[0] == 1); + try expect(vec[1] == 0); + try expect(vec[2] == 1); + try expect(vec[3] == 1); + } + + { + var arr: [4]u3 = .{ 0, 3, 5, 7 }; + const vec: @Vector(4, u3) = arr; + arr[0] = 1; // should not affect `vec` + try expect(vec[0] == 0); + try expect(vec[1] == 3); + try expect(vec[2] == 5); + try expect(vec[3] == 7); + } + + { + var arr: [3]u24 = .{ 0x010203, 0x040506, 0xFF0000 }; + const vec: @Vector(3, u24) = arr; + arr[0] = 0; // should not affect `vec` + try expect(vec[0] == 0x010203); + try expect(vec[1] == 0x040506); + try expect(vec[2] == 0xFF0000); + } + } + }; + try S.doTheTest(); + try comptime S.doTheTest(); +} + test "array vector coercion - odd sizes" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO diff --git a/test/llvm_ir.zig b/test/llvm_ir.zig index 7949ddab0d19abb763440cdca0d5112cef974da9..d7ceb45d637142bfd5ca605ac74af271a18fe794 100644 --- a/test/llvm_ir.zig +++ b/test/llvm_ir.zig @@ -136,6 +136,25 @@ pub fn addCases(cases: *tests.LlvmIrContext) void { " %8 = zext i1 %4 to i8", " store i8 %8, ptr %1, align 2", }, .{ .strip = true }); + + cases.addMatches("array to vector coercion is one vector load", + \\export fn entry(arr: *const [16]u8) @Vector(16, u8) { + \\ return arr.*; + \\} + , &.{ + "load <16 x i8>, ptr", + }, .{}); + + cases.addMatches("array to vector coercion feeding overlapping shuffles", + \\export fn entry(a: *@Vector(8, u8), b: *@Vector(8, u8), src: [*]const u8) void { + \\ const q: @Vector(16, u8) = src[0..16].*; + \\ a.* = @shuffle(u8, q, undefined, @Vector(8, i32){ 0, 1, 2, 3, 4, 5, 6, 7 }); + \\ b.* = @shuffle(u8, q, undefined, @Vector(8, i32){ 7, 8, 9, 10, 11, 12, 13, 14 }); + \\} + , &.{ + "load <16 x i8>, ptr", + "shufflevector <16 x i8>", + }, .{}); } const std = @import("std"); -- 2.54.0