| author | |
| committer | |
| log | d1f80fe1063ab9ef28d527e27d2f19b23ea06bc3 |
| tree | 2a4f010ae1533a2f451a8f72895057bbf2cd5ef7 |
| parent | 755d09660d8886f77b089bb97157649d2fe65cf9 |
| parent | bd2db84eede87bf2336a624ff21e798e8b631921 |
Reviewed-on: https://codeberg.org/ziglang/zig/pulls/37349
Reviewed-by: Andrew Kelley <andrew@ziglang.org>8 files changed, 58 insertions(+), 48 deletions(-)
doc/langref.html.in+1-1| ... | ... | @@ -5591,7 +5591,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val |
| 5591 | 5591 | <p> |
| 5592 | 5592 | Produces an array or vector where each element is the value |
| 5593 | 5593 | {#syntax#}element{#endsyntax#}. The return type, including the number of |
| 5594 | elements, is inferred. | |
| 5594 | elements, is inferred. If the result type is a scalar, `element` is returned. | |
| 5595 | 5595 | </p> |
| 5596 | 5596 | {#code|test_splat_builtin.zig#} |
| 5597 | 5597 | {#see_also|Vectors|Arrays|@shuffle#} |
doc/langref/test_splat_builtin.zig+7| ... | ... | @@ -1,5 +1,6 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | 2 | const expectEqualSlices = std.testing.expectEqualSlices; |
| 3 | const expectEqual = std.testing.expectEqual; | |
| 3 | 4 | |
| 4 | 5 | test "vector @splat" { |
| 5 | 6 | const scalar: u32 = 5; |
| ... | ... | @@ -13,4 +14,10 @@ test "array @splat" { |
| 13 | 14 | try expectEqualSlices(u32, &[_]u32{ 5, 5, 5, 5 }, &@as([4]u32, result)); |
| 14 | 15 | } |
| 15 | 16 | |
| 17 | test "scalar @splat" { | |
| 18 | const scalar: u32 = 5; | |
| 19 | const result: u32 = @splat(scalar); | |
| 20 | try expectEqual(5, result); | |
| 21 | } | |
| 22 | ||
| 16 | 23 | // test |
src/Sema.zig+9-10| ... | ... | @@ -7515,13 +7515,12 @@ fn zirSplatOpResultType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Compil |
| 7515 | 7515 | const un_node = sema.code.instructions.items(.data)[@backingInt(inst)].un_node; |
| 7516 | 7516 | |
| 7517 | 7517 | const raw_ty = try sema.resolveTypeOrPoison(block, LazySrcLoc.unneeded, un_node.operand) orelse return .generic_poison_type; |
| 7518 | const vec_ty = raw_ty.optEuBaseType(zcu); | |
| 7518 | const base_ty = raw_ty.optEuBaseType(zcu); | |
| 7519 | 7519 | |
| 7520 | switch (vec_ty.zigTypeTag(zcu)) { | |
| 7521 | .array, .vector => {}, | |
| 7522 | else => return sema.fail(block, block.nodeOffset(un_node.src_node), "expected array or vector type, found '{f}'", .{vec_ty.fmt(zcu)}), | |
| 7520 | switch (base_ty.zigTypeTag(zcu)) { | |
| 7521 | .array, .vector => return Air.internedToRef(base_ty.childType(zcu).toIntern()), | |
| 7522 | else => return Air.internedToRef(base_ty.toIntern()), | |
| 7523 | 7523 | } |
| 7524 | return Air.internedToRef(vec_ty.childType(zcu).toIntern()); | |
| 7525 | 7524 | } |
| 7526 | 7525 | |
| 7527 | 7526 | fn zirVectorType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| ... | ... | @@ -23314,17 +23313,17 @@ fn zirSplat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I |
| 23314 | 23313 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 23315 | 23314 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; |
| 23316 | 23315 | const src = block.nodeOffset(inst_data.src_node); |
| 23317 | const scalar_src = block.builtinCallArgSrc(inst_data.src_node, 0); | |
| 23316 | const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); | |
| 23318 | 23317 | const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@splat"); |
| 23318 | const operand = sema.resolveInst(extra.rhs); | |
| 23319 | 23319 | |
| 23320 | 23320 | switch (dest_ty.zigTypeTag(zcu)) { |
| 23321 | 23321 | .array, .vector => {}, |
| 23322 | else => return sema.fail(block, src, "expected array or vector type, found '{f}'", .{dest_ty.fmt(zcu)}), | |
| 23322 | else => return sema.coerce(block, dest_ty, operand, operand_src), | |
| 23323 | 23323 | } |
| 23324 | 23324 | |
| 23325 | const operand = sema.resolveInst(extra.rhs); | |
| 23326 | 23325 | const scalar_ty = dest_ty.childType(zcu); |
| 23327 | const scalar = try sema.coerce(block, scalar_ty, operand, scalar_src); | |
| 23326 | const scalar = try sema.coerce(block, scalar_ty, operand, operand_src); | |
| 23328 | 23327 | |
| 23329 | 23328 | const len = try sema.usizeCast(block, src, dest_ty.arrayLen(zcu)); |
| 23330 | 23329 | |
| ... | ... | @@ -23348,7 +23347,7 @@ fn zirSplat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I |
| 23348 | 23347 | return .fromValue(try pt.aggregateSplatValue(dest_ty, scalar_val)); |
| 23349 | 23348 | } |
| 23350 | 23349 | |
| 23351 | try sema.requireRuntimeBlock(block, src, scalar_src); | |
| 23350 | try sema.requireRuntimeBlock(block, src, operand_src); | |
| 23352 | 23351 | |
| 23353 | 23352 | switch (dest_ty.zigTypeTag(zcu)) { |
| 23354 | 23353 | .vector, .array => return block.addTyOp(.splat, dest_ty, scalar), |
test/behavior/splat.zig+19| ... | ... | @@ -160,3 +160,22 @@ test "vector @splat" { |
| 160 | 160 | try S.doTheTest(); |
| 161 | 161 | try comptime S.doTheTest(); |
| 162 | 162 | } |
| 163 | ||
| 164 | test "scalar @splat" { | |
| 165 | const S = struct { | |
| 166 | fn testForTypes(R: type, result: R, Op: type, op: Op) !void { | |
| 167 | try expect(@as(R, result) == @as(Op, @splat(op))); | |
| 168 | } | |
| 169 | fn doTheTest() !void { | |
| 170 | try testForTypes(comptime_float, 3.1415, comptime_float, 3.1415); | |
| 171 | try testForTypes(f32, 3.1415, comptime_float, 3.1415); | |
| 172 | try testForTypes(f32, 3.1415, f32, 3.1415); | |
| 173 | try testForTypes(u8, 31, u8, 31); | |
| 174 | try testForTypes(u8, 31, u16, 31); | |
| 175 | try testForTypes(i16, 31, i16, 31); | |
| 176 | try testForTypes(i16, 31, i8, 31); | |
| 177 | } | |
| 178 | }; | |
| 179 | try S.doTheTest(); | |
| 180 | try comptime S.doTheTest(); | |
| 181 | } |
test/behavior/x86_64/binary.zig+9-10| ... | ... | @@ -16,7 +16,6 @@ const nan = math.nan; |
| 16 | 16 | const Scalar = math.Scalar; |
| 17 | 17 | const select = math.select; |
| 18 | 18 | const sign = math.sign; |
| 19 | const splat = math.splat; | |
| 20 | 19 | const Sse = math.Sse; |
| 21 | 20 | const tmin = math.tmin; |
| 22 | 21 | |
| ... | ... | @@ -5250,7 +5249,7 @@ test divTruncUnoptimized { |
| 5250 | 5249 | |
| 5251 | 5250 | inline fn divTruncOptimized(comptime Type: type, lhs: Type, rhs: Type) Type { |
| 5252 | 5251 | @setFloatMode(.optimized); |
| 5253 | return @divTrunc(lhs, select(@abs(rhs) > splat(Type, 0.0), rhs, splat(Type, 1.0))); | |
| 5252 | return @divTrunc(lhs, select(@abs(rhs) > @as(Type, @splat(0.0)), rhs, @as(Type, @splat(1.0)))); | |
| 5254 | 5253 | } |
| 5255 | 5254 | test divTruncOptimized { |
| 5256 | 5255 | const test_div_trunc_optimized = binary(divTruncOptimized, .{ .compare = .approx_int }); |
| ... | ... | @@ -5271,7 +5270,7 @@ test divFloorUnoptimized { |
| 5271 | 5270 | |
| 5272 | 5271 | inline fn divFloorOptimized(comptime Type: type, lhs: Type, rhs: Type) Type { |
| 5273 | 5272 | @setFloatMode(.optimized); |
| 5274 | return @divFloor(lhs, select(@abs(rhs) > splat(Type, 0.0), rhs, splat(Type, 1.0))); | |
| 5273 | return @divFloor(lhs, select(@abs(rhs) > @as(Type, @splat(0.0)), rhs, @as(Type, @splat(1.0)))); | |
| 5275 | 5274 | } |
| 5276 | 5275 | test divFloorOptimized { |
| 5277 | 5276 | const test_div_floor_optimized = binary(divFloorOptimized, .{ .compare = .approx_int }); |
| ... | ... | @@ -5292,7 +5291,7 @@ test divCeilUnoptimized { |
| 5292 | 5291 | |
| 5293 | 5292 | inline fn divCeilOptimized(comptime Type: type, lhs: Type, rhs: Type) Type { |
| 5294 | 5293 | @setFloatMode(.optimized); |
| 5295 | return @divCeil(lhs, select(@abs(rhs) > splat(Type, 0.0), rhs, splat(Type, 1.0))); | |
| 5294 | return @divCeil(lhs, select(@abs(rhs) > @as(Type, @splat(0.0)), rhs, @as(Type, @splat(1.0)))); | |
| 5296 | 5295 | } |
| 5297 | 5296 | test divCeilOptimized { |
| 5298 | 5297 | const test_div_ceil_optimized = binary(divCeilOptimized, .{ .compare = .approx_int }); |
| ... | ... | @@ -5393,7 +5392,7 @@ test mulWithOverflow { |
| 5393 | 5392 | inline fn shlWithOverflow(comptime Type: type, lhs: Type, rhs: Type) struct { Type, ChangeScalar(Type, u1) } { |
| 5394 | 5393 | const bit_cast_rhs: AsSignedness(Type, .unsigned) = @bitCast(rhs); |
| 5395 | 5394 | const truncate_rhs: Log2Int(Type) = @truncate(bit_cast_rhs); |
| 5396 | return @shlWithOverflow(lhs, if (comptime cast(Log2Int(Scalar(Type)), @bitSizeOf(Scalar(Type)))) |bits| truncate_rhs % splat(Log2Int(Type), bits) else truncate_rhs); | |
| 5395 | return @shlWithOverflow(lhs, if (comptime cast(Log2Int(Scalar(Type)), @bitSizeOf(Scalar(Type)))) |bits| truncate_rhs % @as(Log2Int(Type), @splat(bits)) else truncate_rhs); | |
| 5397 | 5396 | } |
| 5398 | 5397 | test shlWithOverflow { |
| 5399 | 5398 | const test_shl_with_overflow = binary(shlWithOverflow, .{}); |
| ... | ... | @@ -5492,7 +5491,7 @@ test bitOr { |
| 5492 | 5491 | inline fn shr(comptime Type: type, lhs: Type, rhs: Type) Type { |
| 5493 | 5492 | const bit_cast_rhs: AsSignedness(Type, .unsigned) = @bitCast(rhs); |
| 5494 | 5493 | const truncate_rhs: Log2Int(Type) = @truncate(bit_cast_rhs); |
| 5495 | return lhs >> if (comptime cast(Log2Int(Scalar(Type)), @bitSizeOf(Scalar(Type)))) |bits| truncate_rhs % splat(Log2Int(Type), bits) else truncate_rhs; | |
| 5494 | return lhs >> if (comptime cast(Log2Int(Scalar(Type)), @bitSizeOf(Scalar(Type)))) |bits| truncate_rhs % @as(Log2Int(Type), @splat(bits)) else truncate_rhs; | |
| 5496 | 5495 | } |
| 5497 | 5496 | test shr { |
| 5498 | 5497 | const test_shr = binary(shr, .{}); |
| ... | ... | @@ -5503,7 +5502,7 @@ test shr { |
| 5503 | 5502 | inline fn shrExact(comptime Type: type, lhs: Type, rhs: Type) Type { |
| 5504 | 5503 | const bit_cast_rhs: AsSignedness(Type, .unsigned) = @bitCast(rhs); |
| 5505 | 5504 | const truncate_rhs: Log2Int(Type) = @truncate(bit_cast_rhs); |
| 5506 | const final_rhs = if (comptime cast(Log2Int(Scalar(Type)), @bitSizeOf(Scalar(Type)))) |bits| truncate_rhs % splat(Log2Int(Type), bits) else truncate_rhs; | |
| 5505 | const final_rhs = if (comptime cast(Log2Int(Scalar(Type)), @bitSizeOf(Scalar(Type)))) |bits| truncate_rhs % @as(Log2Int(Type), @splat(bits)) else truncate_rhs; | |
| 5507 | 5506 | return @shrExact(lhs >> final_rhs << final_rhs, final_rhs); |
| 5508 | 5507 | } |
| 5509 | 5508 | test shrExact { |
| ... | ... | @@ -5515,7 +5514,7 @@ test shrExact { |
| 5515 | 5514 | inline fn shl(comptime Type: type, lhs: Type, rhs: Type) Type { |
| 5516 | 5515 | const bit_cast_rhs: AsSignedness(Type, .unsigned) = @bitCast(rhs); |
| 5517 | 5516 | const truncate_rhs: Log2Int(Type) = @truncate(bit_cast_rhs); |
| 5518 | return lhs << if (comptime cast(Log2Int(Scalar(Type)), @bitSizeOf(Scalar(Type)))) |bits| truncate_rhs % splat(Log2Int(Type), bits) else truncate_rhs; | |
| 5517 | return lhs << if (comptime cast(Log2Int(Scalar(Type)), @bitSizeOf(Scalar(Type)))) |bits| truncate_rhs % @as(Log2Int(Type), @splat(bits)) else truncate_rhs; | |
| 5519 | 5518 | } |
| 5520 | 5519 | test shl { |
| 5521 | 5520 | const test_shl = binary(shl, .{}); |
| ... | ... | @@ -5527,7 +5526,7 @@ inline fn shlExactUnsafe(comptime Type: type, lhs: Type, rhs: Type) Type { |
| 5527 | 5526 | @setRuntimeSafety(false); |
| 5528 | 5527 | const bit_cast_rhs: AsSignedness(Type, .unsigned) = @bitCast(rhs); |
| 5529 | 5528 | const truncate_rhs: Log2Int(Type) = @truncate(bit_cast_rhs); |
| 5530 | const final_rhs = if (comptime cast(Log2Int(Scalar(Type)), @bitSizeOf(Scalar(Type)))) |bits| truncate_rhs % splat(Log2Int(Type), bits) else truncate_rhs; | |
| 5529 | const final_rhs = if (comptime cast(Log2Int(Scalar(Type)), @bitSizeOf(Scalar(Type)))) |bits| truncate_rhs % @as(Log2Int(Type), @splat(bits)) else truncate_rhs; | |
| 5531 | 5530 | return @shlExact(lhs << final_rhs >> final_rhs, final_rhs); |
| 5532 | 5531 | } |
| 5533 | 5532 | test shlExactUnsafe { |
| ... | ... | @@ -5540,7 +5539,7 @@ inline fn shlSaturate(comptime Type: type, lhs: Type, rhs: Type) Type { |
| 5540 | 5539 | // workaround https://github.com/ziglang/zig/issues/23034 |
| 5541 | 5540 | if (@inComptime()) { |
| 5542 | 5541 | // workaround https://github.com/ziglang/zig/issues/23139 |
| 5543 | return lhs <<| @min(@abs(rhs), splat(ChangeScalar(Type, u64), imax(u64))); | |
| 5542 | return lhs <<| @min(@abs(rhs), @as(ChangeScalar(Type, u64), @splat(imax(u64)))); | |
| 5544 | 5543 | } |
| 5545 | 5544 | return lhs <<| @abs(rhs); |
| 5546 | 5545 | } |
test/behavior/x86_64/cast.zig+7-8| ... | ... | @@ -12,7 +12,6 @@ const math = @import("math.zig"); |
| 12 | 12 | const nan = math.nan; |
| 13 | 13 | const next = math.next; |
| 14 | 14 | const Scalar = math.Scalar; |
| 15 | const splat = math.splat; | |
| 16 | 15 | const Sse = math.Sse; |
| 17 | 16 | const tmin = math.tmin; |
| 18 | 17 | |
| ... | ... | @@ -14508,18 +14507,18 @@ inline fn intCast(comptime Result: type, comptime Type: type, rhs: Type, comptim |
| 14508 | 14507 | rhs & imax(Type) >> bits, |
| 14509 | 14508 | .vector => rhs | @select( |
| 14510 | 14509 | Scalar(Type), |
| 14511 | ct_rhs < splat(Type, 0), | |
| 14512 | splat(Type, imin(Scalar(Type)) >> bits), | |
| 14513 | splat(Type, 0), | |
| 14510 | ct_rhs < @as(Type, @splat(0)), | |
| 14511 | @as(Type, @splat(imin(Scalar(Type)) >> bits)), | |
| 14512 | @as(Type, @splat(0)), | |
| 14514 | 14513 | ) & ~@select( |
| 14515 | 14514 | Scalar(Type), |
| 14516 | ct_rhs >= splat(Type, 0), | |
| 14517 | splat(Type, imin(Scalar(Type)) >> bits), | |
| 14518 | splat(Type, 0), | |
| 14515 | ct_rhs >= @as(Type, @splat(0)), | |
| 14516 | @as(Type, @splat(imin(Scalar(Type)) >> bits)), | |
| 14517 | @as(Type, @splat(0)), | |
| 14519 | 14518 | ), |
| 14520 | 14519 | else => comptime unreachable, |
| 14521 | 14520 | }, |
| 14522 | .mask => |bits| if (bits == rhs_info.bits) rhs else rhs & splat(Type, (1 << bits) - 1), | |
| 14521 | .mask => |bits| if (bits == rhs_info.bits) rhs else rhs & @as(Type, @splat((1 << bits) - 1)), | |
| 14523 | 14522 | }); |
| 14524 | 14523 | } |
| 14525 | 14524 | test intCast { |
test/behavior/x86_64/math.zig+6-12| ... | ... | @@ -72,19 +72,13 @@ pub fn Log2Int(comptime Type: type) type { |
| 72 | 72 | pub fn Log2IntCeil(comptime Type: type) type { |
| 73 | 73 | return ChangeScalar(Type, math.Log2IntCeil(Scalar(Type))); |
| 74 | 74 | } |
| 75 | pub fn splat(comptime Type: type, scalar: Scalar(Type)) Type { | |
| 76 | return switch (@typeInfo(Type)) { | |
| 77 | else => scalar, | |
| 78 | .vector => @splat(scalar), | |
| 79 | }; | |
| 80 | } | |
| 81 | 75 | pub fn sign(rhs: anytype) ChangeScalar(@TypeOf(rhs), bool) { |
| 82 | 76 | const Int = ChangeScalar(@TypeOf(rhs), switch (@typeInfo(Scalar(@TypeOf(rhs)))) { |
| 83 | 77 | .int, .comptime_int => Scalar(@TypeOf(rhs)), |
| 84 | 78 | .float => |float| @Int(.signed, float.bits), |
| 85 | 79 | else => @compileError(@typeName(@TypeOf(rhs))), |
| 86 | 80 | }); |
| 87 | return @as(Int, @bitCast(rhs)) < splat(Int, 0); | |
| 81 | return @as(Int, @bitCast(rhs)) < @as(Int, @splat(0)); | |
| 88 | 82 | } |
| 89 | 83 | pub fn select(cond: anytype, lhs: anytype, rhs: @TypeOf(lhs)) @TypeOf(lhs) { |
| 90 | 84 | return switch (@typeInfo(@TypeOf(cond))) { |
| ... | ... | @@ -117,19 +111,19 @@ pub noinline fn checkExpected(expected: anytype, actual: @TypeOf(expected), comp |
| 117 | 111 | .approx_or_overflow => @exp2(@log2(epsilon) * 0.4), |
| 118 | 112 | }; |
| 119 | 113 | const approx_unequal = @abs(expected - actual) > @max( |
| 120 | @abs(expected) * splat(Expected, tolerance), | |
| 121 | splat(Expected, switch (compare) { | |
| 114 | @abs(expected) * @as(Expected, @splat(tolerance)), | |
| 115 | @as(Expected, @splat(switch (compare) { | |
| 122 | 116 | .strict, .relaxed => comptime unreachable, |
| 123 | 117 | .approx, .approx_or_overflow => tolerance, |
| 124 | 118 | .approx_int => 1, |
| 125 | }), | |
| 119 | })), | |
| 126 | 120 | ); |
| 127 | 121 | break :unexpected switch (compare) { |
| 128 | 122 | .strict, .relaxed => comptime unreachable, |
| 129 | 123 | .approx, .approx_int => approx_unequal, |
| 130 | 124 | .approx_or_overflow => approx_unequal & |
| 131 | (((@abs(expected) != splat(Expected, inf(Expected))) & | |
| 132 | (@abs(actual) != splat(Expected, inf(Expected)))) | | |
| 125 | (((@abs(expected) != @as(Expected, @splat(inf(Expected)))) & | |
| 126 | (@abs(actual) != @as(Expected, @splat(inf(Expected))))) | | |
| 133 | 127 | (sign(expected) != sign(actual))), |
| 134 | 128 | }; |
| 135 | 129 | }, |
test/cases/compile_errors/splat_bad_result_type.zig deleted-7| ... | ... | @@ -1,7 +0,0 @@ |
| 1 | export fn f() void { | |
| 2 | _ = @as(u32, @splat(5)); | |
| 3 | } | |
| 4 | ||
| 5 | // error | |
| 6 | // | |
| 7 | // :2:18: error: expected array or vector type, found 'u32' |