| author | |
| committer | |
| log | a72236ae8e9f11f42351ae42b1242f6091216c45 |
| tree | 5b8f47bb6dd51bdf65ff619af9eca52040188179 |
| parent | 4987b62a90d1dea50e7c4cd13a02c7dfe2f9c2e3 |
18 files changed, 352 insertions(+), 1 deletions(-)
src/Air.zig+12| ... | ... | @@ -748,6 +748,16 @@ pub const Inst = struct { |
| 748 | 748 | /// Given a pointer to an array, return a slice. |
| 749 | 749 | /// Uses the `ty_op` field. |
| 750 | 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 | 761 | /// Given a float operand, return the integer with the closest mathematical meaning. |
| 752 | 762 | /// Uses the `ty_op` field. |
| 753 | 763 | int_from_float, |
| ... | ... | @@ -1754,6 +1764,7 @@ pub fn typeOfIndex(air: *const Air, inst: Air.Inst.Index, ip: *const InternPool) |
| 1754 | 1764 | .struct_field_ptr_index_2, |
| 1755 | 1765 | .struct_field_ptr_index_3, |
| 1756 | 1766 | .array_to_slice, |
| 1767 | .array_to_vector, | |
| 1757 | 1768 | .int_from_float, |
| 1758 | 1769 | .int_from_float_optimized, |
| 1759 | 1770 | .int_from_float_safe, |
| ... | ... | @@ -2106,6 +2117,7 @@ pub fn mustLower(air: Air, inst: Air.Inst.Index, ip: *const InternPool) bool { |
| 2106 | 2117 | .slice_elem_ptr, |
| 2107 | 2118 | .ptr_elem_ptr, |
| 2108 | 2119 | .array_to_slice, |
| 2120 | .array_to_vector, | |
| 2109 | 2121 | .int_from_float, |
| 2110 | 2122 | .int_from_float_optimized, |
| 2111 | 2123 | .float_from_int, |
src/Air/Legalize.zig+47| ... | ... | @@ -200,6 +200,8 @@ pub const Feature = enum { |
| 200 | 200 | expand_packed_agg_field_val, |
| 201 | 201 | /// Replace `aggregate_init` of a packed struct with a sequence of `shl_exact`, `bit_cast`, `int_cast`, and `bit_or`. |
| 202 | 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, | |
| 203 | 205 | |
| 204 | 206 | /// Replace all arithmetic operations on 16-bit floating-point types with calls to soft-float |
| 205 | 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 | 865 | .ptr_elem_ptr, |
| 864 | 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 | 871 | inline .reduce, .reduce_optimized => |air_tag| { |
| 867 | 872 | const reduce = l.air_instructions.items(.data)[@backingInt(inst)].reduce; |
| 868 | 873 | const vector_ty = l.typeOf(reduce.operand); |
| ... | ... | @@ -2954,6 +2959,48 @@ fn packedAggregateInitBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Erro |
| 2954 | 2959 | } }; |
| 2955 | 2960 | } |
| 2956 | 2961 | |
| 2962 | fn 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 | 3004 | /// Given a `std.math.big.int.Const`, converts it to a `Value` which is a float of type `float_ty` |
| 2958 | 3005 | /// representing the same numeric value. If the integer cannot be exactly represented, `round` |
| 2959 | 3006 | /// 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 | 525 | .struct_field_ptr_index_2, |
| 526 | 526 | .struct_field_ptr_index_3, |
| 527 | 527 | .array_to_slice, |
| 528 | .array_to_vector, | |
| 528 | 529 | .int_from_float, |
| 529 | 530 | .int_from_float_optimized, |
| 530 | 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 | 113 | .struct_field_ptr_index_2, |
| 114 | 114 | .struct_field_ptr_index_3, |
| 115 | 115 | .array_to_slice, |
| 116 | .array_to_vector, | |
| 116 | 117 | .int_from_float, |
| 117 | 118 | .int_from_float_optimized, |
| 118 | 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 | 380 | .slice_elem_ptr, |
| 381 | 381 | .ptr_elem_val, |
| 382 | 382 | .array_to_slice, |
| 383 | .array_to_vector, | |
| 383 | 384 | .int_from_float, |
| 384 | 385 | .int_from_float_optimized, |
| 385 | 386 | .int_from_float_safe, |
src/Air/print.zig+1| ... | ... | @@ -265,6 +265,7 @@ const Writer = struct { |
| 265 | 265 | .struct_field_ptr_index_2, |
| 266 | 266 | .struct_field_ptr_index_3, |
| 267 | 267 | .array_to_slice, |
| 268 | .array_to_vector, | |
| 268 | 269 | .float_from_int, |
| 269 | 270 | .splat, |
| 270 | 271 | .int_from_float, |
src/Sema.zig+10| ... | ... | @@ -30504,6 +30504,16 @@ fn coerceArrayLike( |
| 30504 | 30504 | } |
| 30505 | 30505 | } |
| 30506 | 30506 | |
| 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 | 30517 | const element_vals = try sema.arena.alloc(InternPool.Index, dest_len); |
| 30508 | 30518 | const element_refs = try sema.arena.alloc(Air.Inst.Ref, dest_len); |
| 30509 | 30519 | var runtime_src: ?LazySrcLoc = null; |
src/codegen/aarch64.zig+1| ... | ... | @@ -8,6 +8,7 @@ pub const Select = @import("aarch64/Select.zig"); |
| 8 | 8 | pub fn legalizeFeatures(_: *const std.Target) *const Air.Legalize.Features { |
| 9 | 9 | return comptime &.initMany(&.{ |
| 10 | 10 | .expand_bit_cast_safe, |
| 11 | .expand_array_to_vector, | |
| 11 | 12 | }); |
| 12 | 13 | } |
| 13 | 14 |
src/codegen/aarch64/Select.zig+2| ... | ... | @@ -258,6 +258,7 @@ pub fn analyze(isel: *Select, air_body: []const Air.Inst.Index) !void { |
| 258 | 258 | .work_group_size, |
| 259 | 259 | .work_group_id, |
| 260 | 260 | .spirv_runtime_array_len, |
| 261 | .array_to_vector, | |
| 261 | 262 | => unreachable, |
| 262 | 263 | .ret_ptr => { |
| 263 | 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 | 6296 | } |
| 6296 | 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 | 6300 | .array_to_slice => { |
| 6299 | 6301 | if (isel.live_values.fetchRemove(air.inst_index)) |slice_vi| { |
| 6300 | 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 | 39 | .expand_packed_store = true, |
| 40 | 40 | .expand_packed_agg_field_val = true, |
| 41 | 41 | .expand_packed_aggregate_init = true, |
| 42 | .expand_array_to_vector = true, | |
| 42 | 43 | |
| 43 | 44 | .scalarize_bit_cast_array = true, |
| 44 | 45 | .scalarize_bit_cast_vector_non_elementwise = true, |
| ... | ... | @@ -2870,6 +2871,7 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) Error!void { |
| 2870 | 2871 | .store_safe => try airStore(f, inst, true), |
| 2871 | 2872 | .struct_field_ptr => try airStructFieldPtr(f, inst), |
| 2872 | 2873 | .array_to_slice => try airArrayToSlice(f, inst), |
| 2874 | .array_to_vector => unreachable, // legalize .expand_array_to_vector | |
| 2873 | 2875 | .cmpxchg_weak => try airCmpxchg(f, inst, "weak"), |
| 2874 | 2876 | .cmpxchg_strong => try airCmpxchg(f, inst, "strong"), |
| 2875 | 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 | 504 | .int_from_float_optimized_safe => unreachable, // handled by `legalizeFeatures` |
| 505 | 505 | |
| 506 | 506 | .array_to_slice => try self.airArrayToSlice(inst), |
| 507 | .array_to_vector => try self.airArrayToVector(inst), | |
| 507 | 508 | .float_from_int => try self.airFloatFromInt(inst), |
| 508 | 509 | .cmpxchg_weak => try self.airCmpxchg(inst, .weak), |
| 509 | 510 | .cmpxchg_strong => try self.airCmpxchg(inst, .strong), |
| ... | ... | @@ -2027,6 +2028,48 @@ fn airArrayToSlice(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder |
| 2027 | 2028 | return self.wip.buildAggregate(slice_llvm_ty, &.{ operand, len }, ""); |
| 2028 | 2029 | } |
| 2029 | 2030 | |
| 2031 | fn 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 | ||
| 2030 | 2073 | fn airFloatFromInt(fg: *FuncGen, inst: Air.Inst.Index) TodoError!Builder.Value { |
| 2031 | 2074 | const o = fg.object; |
| 2032 | 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 | 58 | .expand_add_safe, |
| 59 | 59 | .expand_sub_safe, |
| 60 | 60 | .expand_mul_safe, |
| 61 | ||
| 62 | .expand_array_to_vector, | |
| 61 | 63 | }); |
| 62 | 64 | } |
| 63 | 65 | |
| ... | ... | @@ -1474,6 +1476,7 @@ fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void { |
| 1474 | 1476 | |
| 1475 | 1477 | .slice => try func.airSlice(inst), |
| 1476 | 1478 | .array_to_slice => try func.airArrayToSlice(inst), |
| 1479 | .array_to_vector => unreachable, // legalize .expand_array_to_vector | |
| 1477 | 1480 | |
| 1478 | 1481 | .slice_ptr => try func.airSlicePtr(inst), |
| 1479 | 1482 | .slice_len => try func.airSliceLen(inst), |
src/codegen/sparc64/CodeGen.zig+4-1| ... | ... | @@ -41,7 +41,9 @@ const Self = @This(); |
| 41 | 41 | const InnerError = codegen.Error || error{OutOfRegisters}; |
| 42 | 42 | |
| 43 | 43 | pub fn legalizeFeatures(_: *const std.Target) ?*const Air.Legalize.Features { |
| 44 | return null; | |
| 44 | return comptime &.initMany(&.{ | |
| 45 | .expand_array_to_vector, | |
| 46 | }); | |
| 45 | 47 | } |
| 46 | 48 | |
| 47 | 49 | const RegisterView = enum(u1) { |
| ... | ... | @@ -578,6 +580,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 578 | 580 | .struct_field_ptr=> try self.airStructFieldPtr(inst), |
| 579 | 581 | .agg_field_val => try self.airAggFieldVal(inst), |
| 580 | 582 | .array_to_slice => try self.airArrayToSlice(inst), |
| 583 | .array_to_vector => unreachable, // legalize .expand_array_to_vector | |
| 581 | 584 | .float_from_int => try self.airFloatFromInt(inst), |
| 582 | 585 | .int_from_float => try self.airIntFromFloat(inst), |
| 583 | 586 | .cmpxchg_strong, |
src/codegen/spirv/CodeGen.zig+3| ... | ... | @@ -128,6 +128,8 @@ pub fn legalizeFeatures(_: *const std.Target) *const Air.Legalize.Features { |
| 128 | 128 | .expand_add_safe, |
| 129 | 129 | .expand_sub_safe, |
| 130 | 130 | .expand_mul_safe, |
| 131 | ||
| 132 | .expand_array_to_vector, | |
| 131 | 133 | }); |
| 132 | 134 | } |
| 133 | 135 | |
| ... | ... | @@ -4429,6 +4431,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) Error!void { |
| 4429 | 4431 | .not => try cg.airNot(inst), |
| 4430 | 4432 | |
| 4431 | 4433 | .array_to_slice => try cg.airArrayToSlice(inst), |
| 4434 | .array_to_vector => unreachable, // legalize .expand_array_to_vector | |
| 4432 | 4435 | .slice => try cg.airSlice(inst), |
| 4433 | 4436 | .aggregate_init => try cg.airAggregateInit(inst), |
| 4434 | 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 | 38 | .expand_packed_store, |
| 39 | 39 | .expand_packed_agg_field_val, |
| 40 | 40 | .expand_packed_aggregate_init, |
| 41 | .expand_array_to_vector, | |
| 41 | 42 | |
| 42 | 43 | .scalarize_add, |
| 43 | 44 | .scalarize_add_optimized, |
| ... | ... | @@ -1753,6 +1754,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 1753 | 1754 | |
| 1754 | 1755 | .array_elem_val => cg.airArrayElemVal(inst), |
| 1755 | 1756 | .array_to_slice => cg.airArrayToSlice(inst), |
| 1757 | .array_to_vector => unreachable, // legalize .expand_array_to_vector | |
| 1756 | 1758 | .alloc => cg.airAlloc(inst), |
| 1757 | 1759 | .arg => cg.airArg(inst), |
| 1758 | 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 | 78 | .expand_packed_store, |
| 79 | 79 | .expand_packed_agg_field_val, |
| 80 | 80 | .expand_packed_aggregate_init, |
| 81 | .expand_array_to_vector, | |
| 81 | 82 | }); |
| 82 | 83 | } |
| 83 | 84 | |
| ... | ... | @@ -104405,6 +104406,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { |
| 104405 | 104406 | } |
| 104406 | 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 | 104410 | .array_to_slice => { |
| 104409 | 104411 | const ty_op = air_datas[@backingInt(inst)].ty_op; |
| 104410 | 104412 | var ops = try cg.tempsFromOperands(inst, .{ty_op.operand}); |
test/behavior/vector.zig+198| ... | ... | @@ -229,6 +229,204 @@ test "array to vector" { |
| 229 | 229 | try comptime S.doTheTest(); |
| 230 | 230 | } |
| 231 | 231 | |
| 232 | test "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 | ||
| 317 | test "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 | ||
| 377 | test "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 | ||
| 232 | 430 | test "array vector coercion - odd sizes" { |
| 233 | 431 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 234 | 432 | 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 | 136 | " %8 = zext i1 %4 to i8", |
| 137 | 137 | " store i8 %8, ptr %1, align 2", |
| 138 | 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 | } |
| 140 | 159 | |
| 141 | 160 | const std = @import("std"); |