| author | |
| committer | |
| log | 4c1cc4d8d91af459e5ac1e20b744c7dc9b5b8da3 |
| tree | 8ee7c3b7f2b4c66e9ed8599b9c5164b78d1cc43c |
| parent | 5fbae9cd6fe7a53d85ad6dd20cab75f7b835f0ad |
| parent | 03b8206f27485f871fc489f884ffbc276d61877c |
| signature |
Stage2: make std.rand tests pass16 files changed, 447 insertions(+), 44 deletions(-)
lib/std/rand.zig+5-1| ... | ... | @@ -7,6 +7,7 @@ |
| 7 | 7 | //! TODO(tiehuis): Benchmark these against other reference implementations. |
| 8 | 8 | |
| 9 | 9 | const std = @import("std.zig"); |
| 10 | const builtin = @import("builtin"); | |
| 10 | 11 | const assert = std.debug.assert; |
| 11 | 12 | const expect = std.testing.expect; |
| 12 | 13 | const expectEqual = std.testing.expectEqual; |
| ... | ... | @@ -30,7 +31,10 @@ pub const Sfc64 = @import("rand/Sfc64.zig"); |
| 30 | 31 | |
| 31 | 32 | pub const Random = struct { |
| 32 | 33 | ptr: *anyopaque, |
| 33 | fillFn: fn (ptr: *anyopaque, buf: []u8) void, | |
| 34 | fillFn: if (builtin.zig_backend == .stage1) | |
| 35 | fn (ptr: *anyopaque, buf: []u8) void | |
| 36 | else | |
| 37 | *const fn (ptr: *anyopaque, buf: []u8) void, | |
| 34 | 38 | |
| 35 | 39 | pub fn init(pointer: anytype, comptime fillFn: fn (ptr: @TypeOf(pointer), buf: []u8) void) Random { |
| 36 | 40 | const Ptr = @TypeOf(pointer); |
src/Air.zig+12| ... | ... | @@ -520,6 +520,9 @@ pub const Inst = struct { |
| 520 | 520 | /// equal to the scalar value. |
| 521 | 521 | /// Uses the `ty_op` field. |
| 522 | 522 | splat, |
| 523 | /// Constructs a vector by selecting elements from `a` and `b` based on `mask`. | |
| 524 | /// Uses the `ty_pl` field with payload `Shuffle`. | |
| 525 | shuffle, | |
| 523 | 526 | |
| 524 | 527 | /// Given dest ptr, value, and len, set all elements at dest to value. |
| 525 | 528 | /// Result type is always void. |
| ... | ... | @@ -740,6 +743,14 @@ pub const FieldParentPtr = struct { |
| 740 | 743 | field_index: u32, |
| 741 | 744 | }; |
| 742 | 745 | |
| 746 | pub const Shuffle = struct { | |
| 747 | a: Inst.Ref, | |
| 748 | b: Inst.Ref, | |
| 749 | // index to air_values | |
| 750 | mask: u32, | |
| 751 | mask_len: u32, | |
| 752 | }; | |
| 753 | ||
| 743 | 754 | /// Trailing: |
| 744 | 755 | /// 0. `Inst.Ref` for every outputs_len |
| 745 | 756 | /// 1. `Inst.Ref` for every inputs_len |
| ... | ... | @@ -897,6 +908,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { |
| 897 | 908 | .cmpxchg_weak, |
| 898 | 909 | .cmpxchg_strong, |
| 899 | 910 | .slice, |
| 911 | .shuffle, | |
| 900 | 912 | .aggregate_init, |
| 901 | 913 | .union_init, |
| 902 | 914 | .field_parent_ptr, |
src/Liveness.zig+4| ... | ... | @@ -422,6 +422,10 @@ fn analyzeInst( |
| 422 | 422 | } |
| 423 | 423 | return extra_tombs.finish(); |
| 424 | 424 | }, |
| 425 | .shuffle => { | |
| 426 | const extra = a.air.extraData(Air.Shuffle, inst_datas[inst].ty_pl.payload).data; | |
| 427 | return trackOperands(a, new_set, inst, main_tomb, .{ extra.a, extra.b, .none }); | |
| 428 | }, | |
| 425 | 429 | .aggregate_init => { |
| 426 | 430 | const ty_pl = inst_datas[inst].ty_pl; |
| 427 | 431 | const aggregate_ty = a.air.getRefType(ty_pl.ty); |
src/Sema.zig+228-19| ... | ... | @@ -4618,7 +4618,7 @@ fn analyzeCall( |
| 4618 | 4618 | }, |
| 4619 | 4619 | else => {}, |
| 4620 | 4620 | } |
| 4621 | should_memoize = should_memoize and !arg_val.isComptimeMutablePtr(); | |
| 4621 | should_memoize = should_memoize and !arg_val.canMutateComptimeVarState(); | |
| 4622 | 4622 | memoized_call_key.args[arg_i] = .{ |
| 4623 | 4623 | .ty = param_ty, |
| 4624 | 4624 | .val = arg_val, |
| ... | ... | @@ -4644,7 +4644,7 @@ fn analyzeCall( |
| 4644 | 4644 | }, |
| 4645 | 4645 | else => {}, |
| 4646 | 4646 | } |
| 4647 | should_memoize = should_memoize and !arg_val.isComptimeMutablePtr(); | |
| 4647 | should_memoize = should_memoize and !arg_val.canMutateComptimeVarState(); | |
| 4648 | 4648 | memoized_call_key.args[arg_i] = .{ |
| 4649 | 4649 | .ty = sema.typeOf(uncasted_arg), |
| 4650 | 4650 | .val = arg_val, |
| ... | ... | @@ -5923,6 +5923,10 @@ fn funcCommon( |
| 5923 | 5923 | break :ret_ty ret_ty; |
| 5924 | 5924 | } else |err| break :err err; |
| 5925 | 5925 | } else |err| break :err err; |
| 5926 | // Check for generic params. | |
| 5927 | for (block.params.items) |param| { | |
| 5928 | if (param.ty.tag() == .generic_poison) is_generic = true; | |
| 5929 | } | |
| 5926 | 5930 | }; |
| 5927 | 5931 | switch (err) { |
| 5928 | 5932 | error.GenericPoison => { |
| ... | ... | @@ -6111,6 +6115,13 @@ fn zirParam( |
| 6111 | 6115 | |
| 6112 | 6116 | if (sema.resolveBody(block, body, inst)) |param_ty_inst| { |
| 6113 | 6117 | if (sema.analyzeAsType(block, src, param_ty_inst)) |param_ty| { |
| 6118 | if (param_ty.zigTypeTag() == .Fn and param_ty.fnInfo().is_generic) { | |
| 6119 | // zirFunc will not emit error.GenericPoison to build a | |
| 6120 | // partial type for generic functions but we still need to | |
| 6121 | // detect if a function parameter is a generic function | |
| 6122 | // to force the parent function to also be generic. | |
| 6123 | break :err error.GenericPoison; | |
| 6124 | } | |
| 6114 | 6125 | break :param_ty param_ty; |
| 6115 | 6126 | } else |err| break :err err; |
| 6116 | 6127 | } else |err| break :err err; |
| ... | ... | @@ -8048,7 +8059,6 @@ fn zirBitwise( |
| 8048 | 8059 | rhs_ty.arrayLen(), |
| 8049 | 8060 | }); |
| 8050 | 8061 | } |
| 8051 | return sema.fail(block, src, "TODO implement support for vectors in zirBitwise", .{}); | |
| 8052 | 8062 | } else if (lhs_ty.zigTypeTag() == .Vector or rhs_ty.zigTypeTag() == .Vector) { |
| 8053 | 8063 | return sema.fail(block, src, "mixed scalar and vector operands to binary expression: '{}' and '{}'", .{ |
| 8054 | 8064 | lhs_ty, |
| ... | ... | @@ -8064,6 +8074,9 @@ fn zirBitwise( |
| 8064 | 8074 | |
| 8065 | 8075 | if (try sema.resolveMaybeUndefVal(block, lhs_src, casted_lhs)) |lhs_val| { |
| 8066 | 8076 | if (try sema.resolveMaybeUndefVal(block, rhs_src, casted_rhs)) |rhs_val| { |
| 8077 | if (resolved_type.zigTypeTag() == .Vector) { | |
| 8078 | return sema.fail(block, src, "TODO implement zirBitwise for vectors at comptime", .{}); | |
| 8079 | } | |
| 8067 | 8080 | const result_val = switch (air_tag) { |
| 8068 | 8081 | .bit_and => try lhs_val.bitwiseAnd(rhs_val, sema.arena), |
| 8069 | 8082 | .bit_or => try lhs_val.bitwiseOr(rhs_val, sema.arena), |
| ... | ... | @@ -10965,6 +10978,7 @@ fn zirTypeofBuiltin(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr |
| 10965 | 10978 | |
| 10966 | 10979 | const operand = try sema.resolveBody(&child_block, body, inst); |
| 10967 | 10980 | const operand_ty = sema.typeOf(operand); |
| 10981 | if (operand_ty.tag() == .generic_poison) return error.GenericPoison; | |
| 10968 | 10982 | return sema.addType(operand_ty); |
| 10969 | 10983 | } |
| 10970 | 10984 | |
| ... | ... | @@ -10973,19 +10987,21 @@ fn zirTypeofLog2IntType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Compil |
| 10973 | 10987 | const src = inst_data.src(); |
| 10974 | 10988 | const operand = sema.resolveInst(inst_data.operand); |
| 10975 | 10989 | const operand_ty = sema.typeOf(operand); |
| 10976 | return sema.log2IntType(block, operand_ty, src); | |
| 10990 | const res_ty = try sema.log2IntType(block, operand_ty, src); | |
| 10991 | return sema.addType(res_ty); | |
| 10977 | 10992 | } |
| 10978 | 10993 | |
| 10979 | 10994 | fn zirLog2IntType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 10980 | 10995 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; |
| 10981 | 10996 | const src = inst_data.src(); |
| 10982 | 10997 | const operand = try sema.resolveType(block, src, inst_data.operand); |
| 10983 | return sema.log2IntType(block, operand, src); | |
| 10998 | const res_ty = try sema.log2IntType(block, operand, src); | |
| 10999 | return sema.addType(res_ty); | |
| 10984 | 11000 | } |
| 10985 | 11001 | |
| 10986 | fn log2IntType(sema: *Sema, block: *Block, operand: Type, src: LazySrcLoc) CompileError!Air.Inst.Ref { | |
| 11002 | fn log2IntType(sema: *Sema, block: *Block, operand: Type, src: LazySrcLoc) CompileError!Type { | |
| 10987 | 11003 | switch (operand.zigTypeTag()) { |
| 10988 | .ComptimeInt => return Air.Inst.Ref.comptime_int_type, | |
| 11004 | .ComptimeInt => return Type.@"comptime_int", | |
| 10989 | 11005 | .Int => { |
| 10990 | 11006 | const bits = operand.bitSize(sema.mod.getTarget()); |
| 10991 | 11007 | const count = if (bits == 0) |
| ... | ... | @@ -10998,16 +11014,24 @@ fn log2IntType(sema: *Sema, block: *Block, operand: Type, src: LazySrcLoc) Compi |
| 10998 | 11014 | } |
| 10999 | 11015 | break :blk count; |
| 11000 | 11016 | }; |
| 11001 | const res = try Module.makeIntType(sema.arena, .unsigned, count); | |
| 11002 | return sema.addType(res); | |
| 11017 | return Module.makeIntType(sema.arena, .unsigned, count); | |
| 11003 | 11018 | }, |
| 11004 | else => return sema.fail( | |
| 11005 | block, | |
| 11006 | src, | |
| 11007 | "bit shifting operation expected integer type, found '{}'", | |
| 11008 | .{operand}, | |
| 11009 | ), | |
| 11019 | .Vector => { | |
| 11020 | const elem_ty = operand.elemType2(); | |
| 11021 | const log2_elem_ty = try sema.log2IntType(block, elem_ty, src); | |
| 11022 | return Type.Tag.vector.create(sema.arena, .{ | |
| 11023 | .len = operand.arrayLen(), | |
| 11024 | .elem_type = log2_elem_ty, | |
| 11025 | }); | |
| 11026 | }, | |
| 11027 | else => {}, | |
| 11010 | 11028 | } |
| 11029 | return sema.fail( | |
| 11030 | block, | |
| 11031 | src, | |
| 11032 | "bit shifting operation expected integer type, found '{}'", | |
| 11033 | .{operand}, | |
| 11034 | ); | |
| 11011 | 11035 | } |
| 11012 | 11036 | |
| 11013 | 11037 | fn zirTypeofPeer( |
| ... | ... | @@ -11044,6 +11068,7 @@ fn zirTypeofPeer( |
| 11044 | 11068 | |
| 11045 | 11069 | for (args) |arg_ref, i| { |
| 11046 | 11070 | inst_list[i] = sema.resolveInst(arg_ref); |
| 11071 | if (sema.typeOf(inst_list[i]).tag() == .generic_poison) return error.GenericPoison; | |
| 11047 | 11072 | } |
| 11048 | 11073 | |
| 11049 | 11074 | const result_type = try sema.resolvePeerTypes(block, src, inst_list, .{ .typeof_builtin_call_node_offset = extra.data.src_node }); |
| ... | ... | @@ -13427,8 +13452,193 @@ fn zirReduce(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. |
| 13427 | 13452 | |
| 13428 | 13453 | fn zirShuffle(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 13429 | 13454 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; |
| 13430 | const src = inst_data.src(); | |
| 13431 | return sema.fail(block, src, "TODO: Sema.zirShuffle", .{}); | |
| 13455 | const extra = sema.code.extraData(Zir.Inst.Shuffle, inst_data.payload_index).data; | |
| 13456 | const elem_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; | |
| 13457 | const mask_src: LazySrcLoc = .{ .node_offset_builtin_call_arg3 = inst_data.src_node }; | |
| 13458 | ||
| 13459 | const elem_ty = try sema.resolveType(block, elem_ty_src, extra.elem_type); | |
| 13460 | try sema.checkVectorElemType(block, elem_ty_src, elem_ty); | |
| 13461 | var a = sema.resolveInst(extra.a); | |
| 13462 | var b = sema.resolveInst(extra.b); | |
| 13463 | var mask = sema.resolveInst(extra.mask); | |
| 13464 | var mask_ty = sema.typeOf(mask); | |
| 13465 | ||
| 13466 | const mask_len = switch (sema.typeOf(mask).zigTypeTag()) { | |
| 13467 | .Array, .Vector => sema.typeOf(mask).arrayLen(), | |
| 13468 | else => return sema.fail(block, mask_src, "expected vector or array, found {}", .{sema.typeOf(mask)}), | |
| 13469 | }; | |
| 13470 | mask_ty = try Type.Tag.vector.create(sema.arena, .{ | |
| 13471 | .len = mask_len, | |
| 13472 | .elem_type = Type.@"i32", | |
| 13473 | }); | |
| 13474 | mask = try sema.coerce(block, mask_ty, mask, mask_src); | |
| 13475 | const mask_val = try sema.resolveConstMaybeUndefVal(block, mask_src, mask); | |
| 13476 | return sema.analyzeShuffle(block, inst_data.src_node, elem_ty, a, b, mask_val, @intCast(u32, mask_len)); | |
| 13477 | } | |
| 13478 | ||
| 13479 | fn analyzeShuffle( | |
| 13480 | sema: *Sema, | |
| 13481 | block: *Block, | |
| 13482 | src_node: i32, | |
| 13483 | elem_ty: Type, | |
| 13484 | a_arg: Air.Inst.Ref, | |
| 13485 | b_arg: Air.Inst.Ref, | |
| 13486 | mask: Value, | |
| 13487 | mask_len: u32, | |
| 13488 | ) CompileError!Air.Inst.Ref { | |
| 13489 | const a_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = src_node }; | |
| 13490 | const b_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = src_node }; | |
| 13491 | const mask_src: LazySrcLoc = .{ .node_offset_builtin_call_arg3 = src_node }; | |
| 13492 | var a = a_arg; | |
| 13493 | var b = b_arg; | |
| 13494 | ||
| 13495 | const res_ty = try Type.Tag.vector.create(sema.arena, .{ | |
| 13496 | .len = mask_len, | |
| 13497 | .elem_type = elem_ty, | |
| 13498 | }); | |
| 13499 | ||
| 13500 | var maybe_a_len = switch (sema.typeOf(a).zigTypeTag()) { | |
| 13501 | .Array, .Vector => sema.typeOf(a).arrayLen(), | |
| 13502 | .Undefined => null, | |
| 13503 | else => return sema.fail(block, a_src, "expected vector or array with element type {}, found {}", .{ | |
| 13504 | elem_ty, | |
| 13505 | sema.typeOf(a), | |
| 13506 | }), | |
| 13507 | }; | |
| 13508 | var maybe_b_len = switch (sema.typeOf(b).zigTypeTag()) { | |
| 13509 | .Array, .Vector => sema.typeOf(b).arrayLen(), | |
| 13510 | .Undefined => null, | |
| 13511 | else => return sema.fail(block, b_src, "expected vector or array with element type {}, found {}", .{ | |
| 13512 | elem_ty, | |
| 13513 | sema.typeOf(b), | |
| 13514 | }), | |
| 13515 | }; | |
| 13516 | if (maybe_a_len == null and maybe_b_len == null) { | |
| 13517 | return sema.addConstUndef(res_ty); | |
| 13518 | } | |
| 13519 | const a_len = maybe_a_len orelse maybe_b_len.?; | |
| 13520 | const b_len = maybe_b_len orelse a_len; | |
| 13521 | ||
| 13522 | const a_ty = try Type.Tag.vector.create(sema.arena, .{ | |
| 13523 | .len = a_len, | |
| 13524 | .elem_type = elem_ty, | |
| 13525 | }); | |
| 13526 | const b_ty = try Type.Tag.vector.create(sema.arena, .{ | |
| 13527 | .len = b_len, | |
| 13528 | .elem_type = elem_ty, | |
| 13529 | }); | |
| 13530 | ||
| 13531 | if (maybe_a_len == null) a = try sema.addConstUndef(a_ty); | |
| 13532 | if (maybe_b_len == null) b = try sema.addConstUndef(b_ty); | |
| 13533 | ||
| 13534 | const operand_info = [2]std.meta.Tuple(&.{ u64, LazySrcLoc, Type }){ | |
| 13535 | .{ a_len, a_src, a_ty }, | |
| 13536 | .{ b_len, b_src, b_ty }, | |
| 13537 | }; | |
| 13538 | ||
| 13539 | var i: usize = 0; | |
| 13540 | while (i < mask_len) : (i += 1) { | |
| 13541 | var buf: Value.ElemValueBuffer = undefined; | |
| 13542 | const elem = mask.elemValueBuffer(i, &buf); | |
| 13543 | if (elem.isUndef()) continue; | |
| 13544 | const int = elem.toSignedInt(); | |
| 13545 | var unsigned: u32 = undefined; | |
| 13546 | var chosen: u32 = undefined; | |
| 13547 | if (int >= 0) { | |
| 13548 | unsigned = @intCast(u32, int); | |
| 13549 | chosen = 0; | |
| 13550 | } else { | |
| 13551 | unsigned = @intCast(u32, ~int); | |
| 13552 | chosen = 1; | |
| 13553 | } | |
| 13554 | if (unsigned >= operand_info[chosen][0]) { | |
| 13555 | const msg = msg: { | |
| 13556 | const msg = try sema.errMsg(block, mask_src, "mask index {d} has out-of-bounds selection", .{i}); | |
| 13557 | errdefer msg.destroy(sema.gpa); | |
| 13558 | ||
| 13559 | try sema.errNote(block, operand_info[chosen][1], msg, "selected index {d} out of bounds of {}", .{ | |
| 13560 | unsigned, | |
| 13561 | operand_info[chosen][2], | |
| 13562 | }); | |
| 13563 | ||
| 13564 | if (chosen == 1) { | |
| 13565 | try sema.errNote(block, b_src, msg, "selections from the second vector are specified with negative numbers", .{}); | |
| 13566 | } | |
| 13567 | ||
| 13568 | break :msg msg; | |
| 13569 | }; | |
| 13570 | return sema.failWithOwnedErrorMsg(block, msg); | |
| 13571 | } | |
| 13572 | } | |
| 13573 | ||
| 13574 | if (try sema.resolveMaybeUndefVal(block, a_src, a)) |a_val| { | |
| 13575 | if (try sema.resolveMaybeUndefVal(block, b_src, b)) |b_val| { | |
| 13576 | const values = try sema.arena.alloc(Value, mask_len); | |
| 13577 | ||
| 13578 | i = 0; | |
| 13579 | while (i < mask_len) : (i += 1) { | |
| 13580 | var buf: Value.ElemValueBuffer = undefined; | |
| 13581 | const mask_elem_val = mask.elemValueBuffer(i, &buf); | |
| 13582 | if (mask_elem_val.isUndef()) { | |
| 13583 | values[i] = Value.undef; | |
| 13584 | continue; | |
| 13585 | } | |
| 13586 | const int = mask_elem_val.toSignedInt(); | |
| 13587 | const unsigned = if (int >= 0) @intCast(u32, int) else @intCast(u32, ~int); | |
| 13588 | if (int >= 0) { | |
| 13589 | values[i] = try a_val.elemValue(sema.arena, unsigned); | |
| 13590 | } else { | |
| 13591 | values[i] = try b_val.elemValue(sema.arena, unsigned); | |
| 13592 | } | |
| 13593 | } | |
| 13594 | const res_val = try Value.Tag.array.create(sema.arena, values); | |
| 13595 | return sema.addConstant(res_ty, res_val); | |
| 13596 | } | |
| 13597 | } | |
| 13598 | ||
| 13599 | // All static analysis passed, and not comptime. | |
| 13600 | // For runtime codegen, vectors a and b must be the same length. Here we | |
| 13601 | // recursively @shuffle the smaller vector to append undefined elements | |
| 13602 | // to it up to the length of the longer vector. This recursion terminates | |
| 13603 | // in 1 call because these calls to analyzeShuffle guarantee a_len == b_len. | |
| 13604 | if (a_len != b_len) { | |
| 13605 | const min_len = std.math.min(a_len, b_len); | |
| 13606 | const max_src = if (a_len > b_len) a_src else b_src; | |
| 13607 | const max_len = try sema.usizeCast(block, max_src, std.math.max(a_len, b_len)); | |
| 13608 | ||
| 13609 | const expand_mask_values = try sema.arena.alloc(Value, max_len); | |
| 13610 | i = 0; | |
| 13611 | while (i < min_len) : (i += 1) { | |
| 13612 | expand_mask_values[i] = try Value.Tag.int_u64.create(sema.arena, i); | |
| 13613 | } | |
| 13614 | while (i < max_len) : (i += 1) { | |
| 13615 | expand_mask_values[i] = Value.negative_one; | |
| 13616 | } | |
| 13617 | const expand_mask = try Value.Tag.array.create(sema.arena, expand_mask_values); | |
| 13618 | ||
| 13619 | if (a_len < b_len) { | |
| 13620 | const undef = try sema.addConstUndef(a_ty); | |
| 13621 | a = try sema.analyzeShuffle(block, src_node, elem_ty, a, undef, expand_mask, @intCast(u32, max_len)); | |
| 13622 | } else { | |
| 13623 | const undef = try sema.addConstUndef(b_ty); | |
| 13624 | b = try sema.analyzeShuffle(block, src_node, elem_ty, b, undef, expand_mask, @intCast(u32, max_len)); | |
| 13625 | } | |
| 13626 | } | |
| 13627 | ||
| 13628 | const mask_index = @intCast(u32, sema.air_values.items.len); | |
| 13629 | try sema.air_values.append(sema.gpa, mask); | |
| 13630 | return block.addInst(.{ | |
| 13631 | .tag = .shuffle, | |
| 13632 | .data = .{ .ty_pl = .{ | |
| 13633 | .ty = try sema.addType(res_ty), | |
| 13634 | .payload = try block.sema.addExtra(Air.Shuffle{ | |
| 13635 | .a = a, | |
| 13636 | .b = b, | |
| 13637 | .mask = mask_index, | |
| 13638 | .mask_len = mask_len, | |
| 13639 | }), | |
| 13640 | } }, | |
| 13641 | }); | |
| 13432 | 13642 | } |
| 13433 | 13643 | |
| 13434 | 13644 | fn zirSelect(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| ... | ... | @@ -15663,8 +15873,7 @@ fn elemPtr( |
| 15663 | 15873 | }, |
| 15664 | 15874 | } |
| 15665 | 15875 | }, |
| 15666 | .Array => return sema.elemPtrArray(block, array_ptr_src, array_ptr, elem_index, elem_index_src), | |
| 15667 | .Vector => return sema.fail(block, src, "TODO implement Sema for elemPtr for vector", .{}), | |
| 15876 | .Array, .Vector => return sema.elemPtrArray(block, array_ptr_src, array_ptr, elem_index, elem_index_src), | |
| 15668 | 15877 | .Struct => { |
| 15669 | 15878 | // Tuple field access. |
| 15670 | 15879 | const index_val = try sema.resolveConstValue(block, elem_index_src, elem_index); |
src/arch/aarch64/CodeGen.zig+7| ... | ... | @@ -637,6 +637,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 637 | 637 | .tag_name => try self.airTagName(inst), |
| 638 | 638 | .error_name => try self.airErrorName(inst), |
| 639 | 639 | .splat => try self.airSplat(inst), |
| 640 | .shuffle => try self.airShuffle(inst), | |
| 640 | 641 | .aggregate_init => try self.airAggregateInit(inst), |
| 641 | 642 | .union_init => try self.airUnionInit(inst), |
| 642 | 643 | .prefetch => try self.airPrefetch(inst), |
| ... | ... | @@ -3633,6 +3634,12 @@ fn airSplat(self: *Self, inst: Air.Inst.Index) !void { |
| 3633 | 3634 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 3634 | 3635 | } |
| 3635 | 3636 | |
| 3637 | fn airShuffle(self: *Self, inst: Air.Inst.Index) !void { | |
| 3638 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | |
| 3639 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airShuffle for {}", .{self.target.cpu.arch}); | |
| 3640 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | |
| 3641 | } | |
| 3642 | ||
| 3636 | 3643 | fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void { |
| 3637 | 3644 | const vector_ty = self.air.typeOfIndex(inst); |
| 3638 | 3645 | const len = vector_ty.vectorLen(); |
src/arch/arm/CodeGen.zig+7| ... | ... | @@ -636,6 +636,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 636 | 636 | .tag_name => try self.airTagName(inst), |
| 637 | 637 | .error_name => try self.airErrorName(inst), |
| 638 | 638 | .splat => try self.airSplat(inst), |
| 639 | .shuffle => try self.airShuffle(inst), | |
| 639 | 640 | .aggregate_init => try self.airAggregateInit(inst), |
| 640 | 641 | .union_init => try self.airUnionInit(inst), |
| 641 | 642 | .prefetch => try self.airPrefetch(inst), |
| ... | ... | @@ -4094,6 +4095,12 @@ fn airSplat(self: *Self, inst: Air.Inst.Index) !void { |
| 4094 | 4095 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 4095 | 4096 | } |
| 4096 | 4097 | |
| 4098 | fn airShuffle(self: *Self, inst: Air.Inst.Index) !void { | |
| 4099 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | |
| 4100 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airShuffle for arm", .{}); | |
| 4101 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | |
| 4102 | } | |
| 4103 | ||
| 4097 | 4104 | fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void { |
| 4098 | 4105 | const vector_ty = self.air.typeOfIndex(inst); |
| 4099 | 4106 | const len = vector_ty.vectorLen(); |
src/arch/riscv64/CodeGen.zig+7| ... | ... | @@ -603,6 +603,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 603 | 603 | .tag_name => try self.airTagName(inst), |
| 604 | 604 | .error_name => try self.airErrorName(inst), |
| 605 | 605 | .splat => try self.airSplat(inst), |
| 606 | .shuffle => try self.airShuffle(inst), | |
| 606 | 607 | .aggregate_init => try self.airAggregateInit(inst), |
| 607 | 608 | .union_init => try self.airUnionInit(inst), |
| 608 | 609 | .prefetch => try self.airPrefetch(inst), |
| ... | ... | @@ -2181,6 +2182,12 @@ fn airSplat(self: *Self, inst: Air.Inst.Index) !void { |
| 2181 | 2182 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 2182 | 2183 | } |
| 2183 | 2184 | |
| 2185 | fn airShuffle(self: *Self, inst: Air.Inst.Index) !void { | |
| 2186 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | |
| 2187 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airShuffle for riscv64", .{}); | |
| 2188 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | |
| 2189 | } | |
| 2190 | ||
| 2184 | 2191 | fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void { |
| 2185 | 2192 | const vector_ty = self.air.typeOfIndex(inst); |
| 2186 | 2193 | const len = vector_ty.vectorLen(); |
src/arch/wasm/CodeGen.zig+12| ... | ... | @@ -1255,6 +1255,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { |
| 1255 | 1255 | .ret_ptr => self.airRetPtr(inst), |
| 1256 | 1256 | .ret_load => self.airRetLoad(inst), |
| 1257 | 1257 | .splat => self.airSplat(inst), |
| 1258 | .shuffle => self.airShuffle(inst), | |
| 1258 | 1259 | .aggregate_init => self.airAggregateInit(inst), |
| 1259 | 1260 | .union_init => self.airUnionInit(inst), |
| 1260 | 1261 | .prefetch => self.airPrefetch(inst), |
| ... | ... | @@ -2985,6 +2986,17 @@ fn airSplat(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2985 | 2986 | return self.fail("TODO: Implement wasm airSplat", .{}); |
| 2986 | 2987 | } |
| 2987 | 2988 | |
| 2989 | fn airShuffle(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | |
| 2990 | if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; | |
| 2991 | ||
| 2992 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | |
| 2993 | const operand = try self.resolveInst(ty_op.operand); | |
| 2994 | ||
| 2995 | _ = ty_op; | |
| 2996 | _ = operand; | |
| 2997 | return self.fail("TODO: Implement wasm airShuffle", .{}); | |
| 2998 | } | |
| 2999 | ||
| 2988 | 3000 | fn airAggregateInit(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2989 | 3001 | if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; |
| 2990 | 3002 |
src/arch/x86_64/CodeGen.zig+7| ... | ... | @@ -720,6 +720,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 720 | 720 | .tag_name => try self.airTagName(inst), |
| 721 | 721 | .error_name => try self.airErrorName(inst), |
| 722 | 722 | .splat => try self.airSplat(inst), |
| 723 | .shuffle => try self.airShuffle(inst), | |
| 723 | 724 | .aggregate_init => try self.airAggregateInit(inst), |
| 724 | 725 | .union_init => try self.airUnionInit(inst), |
| 725 | 726 | .prefetch => try self.airPrefetch(inst), |
| ... | ... | @@ -5535,6 +5536,12 @@ fn airSplat(self: *Self, inst: Air.Inst.Index) !void { |
| 5535 | 5536 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 5536 | 5537 | } |
| 5537 | 5538 | |
| 5539 | fn airShuffle(self: *Self, inst: Air.Inst.Index) !void { | |
| 5540 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | |
| 5541 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airShuffle for x86_64", .{}); | |
| 5542 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | |
| 5543 | } | |
| 5544 | ||
| 5538 | 5545 | fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void { |
| 5539 | 5546 | const vector_ty = self.air.typeOfIndex(inst); |
| 5540 | 5547 | const len = vector_ty.vectorLen(); |
src/codegen/c.zig+16| ... | ... | @@ -1716,6 +1716,7 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO |
| 1716 | 1716 | .tag_name => try airTagName(f, inst), |
| 1717 | 1717 | .error_name => try airErrorName(f, inst), |
| 1718 | 1718 | .splat => try airSplat(f, inst), |
| 1719 | .shuffle => try airShuffle(f, inst), | |
| 1719 | 1720 | .aggregate_init => try airAggregateInit(f, inst), |
| 1720 | 1721 | .union_init => try airUnionInit(f, inst), |
| 1721 | 1722 | .prefetch => try airPrefetch(f, inst), |
| ... | ... | @@ -3557,6 +3558,21 @@ fn airSplat(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3557 | 3558 | return f.fail("TODO: C backend: implement airSplat", .{}); |
| 3558 | 3559 | } |
| 3559 | 3560 | |
| 3561 | fn airShuffle(f: *Function, inst: Air.Inst.Index) !CValue { | |
| 3562 | if (f.liveness.isUnused(inst)) return CValue.none; | |
| 3563 | ||
| 3564 | const inst_ty = f.air.typeOfIndex(inst); | |
| 3565 | const ty_op = f.air.instructions.items(.data)[inst].ty_op; | |
| 3566 | const operand = try f.resolveInst(ty_op.operand); | |
| 3567 | const writer = f.object.writer(); | |
| 3568 | const local = try f.allocLocal(inst_ty, .Const); | |
| 3569 | try writer.writeAll(" = "); | |
| 3570 | ||
| 3571 | _ = operand; | |
| 3572 | _ = local; | |
| 3573 | return f.fail("TODO: C backend: implement airShuffle", .{}); | |
| 3574 | } | |
| 3575 | ||
| 3560 | 3576 | fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3561 | 3577 | if (f.liveness.isUnused(inst)) return CValue.none; |
| 3562 | 3578 |
src/codegen/llvm.zig+38| ... | ... | @@ -3204,6 +3204,7 @@ pub const FuncGen = struct { |
| 3204 | 3204 | .tag_name => try self.airTagName(inst), |
| 3205 | 3205 | .error_name => try self.airErrorName(inst), |
| 3206 | 3206 | .splat => try self.airSplat(inst), |
| 3207 | .shuffle => try self.airShuffle(inst), | |
| 3207 | 3208 | .aggregate_init => try self.airAggregateInit(inst), |
| 3208 | 3209 | .union_init => try self.airUnionInit(inst), |
| 3209 | 3210 | .prefetch => try self.airPrefetch(inst), |
| ... | ... | @@ -5850,6 +5851,43 @@ pub const FuncGen = struct { |
| 5850 | 5851 | return self.builder.buildShuffleVector(op_vector, undef_vector, mask_llvm_ty.constNull(), ""); |
| 5851 | 5852 | } |
| 5852 | 5853 | |
| 5854 | fn airShuffle(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { | |
| 5855 | if (self.liveness.isUnused(inst)) return null; | |
| 5856 | ||
| 5857 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; | |
| 5858 | const extra = self.air.extraData(Air.Shuffle, ty_pl.payload).data; | |
| 5859 | const a = try self.resolveInst(extra.a); | |
| 5860 | const b = try self.resolveInst(extra.b); | |
| 5861 | const mask = self.air.values[extra.mask]; | |
| 5862 | const mask_len = extra.mask_len; | |
| 5863 | const a_len = self.air.typeOf(extra.a).vectorLen(); | |
| 5864 | ||
| 5865 | // LLVM uses integers larger than the length of the first array to | |
| 5866 | // index into the second array. This was deemed unnecessarily fragile | |
| 5867 | // when changing code, so Zig uses negative numbers to index the | |
| 5868 | // second vector. These start at -1 and go down, and are easiest to use | |
| 5869 | // with the ~ operator. Here we convert between the two formats. | |
| 5870 | const values = try self.gpa.alloc(*const llvm.Value, mask_len); | |
| 5871 | defer self.gpa.free(values); | |
| 5872 | ||
| 5873 | const llvm_i32 = self.context.intType(32); | |
| 5874 | ||
| 5875 | for (values) |*val, i| { | |
| 5876 | var buf: Value.ElemValueBuffer = undefined; | |
| 5877 | const elem = mask.elemValueBuffer(i, &buf); | |
| 5878 | if (elem.isUndef()) { | |
| 5879 | val.* = llvm_i32.getUndef(); | |
| 5880 | } else { | |
| 5881 | const int = elem.toSignedInt(); | |
| 5882 | const unsigned = if (int >= 0) @intCast(u32, int) else @intCast(u32, ~int + a_len); | |
| 5883 | val.* = llvm_i32.constInt(unsigned, .False); | |
| 5884 | } | |
| 5885 | } | |
| 5886 | ||
| 5887 | const llvm_mask_value = llvm.constVector(values.ptr, mask_len); | |
| 5888 | return self.builder.buildShuffleVector(a, b, llvm_mask_value, ""); | |
| 5889 | } | |
| 5890 | ||
| 5853 | 5891 | fn airAggregateInit(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { |
| 5854 | 5892 | if (self.liveness.isUnused(inst)) return null; |
| 5855 | 5893 |
src/print_air.zig+11| ... | ... | @@ -258,6 +258,7 @@ const Writer = struct { |
| 258 | 258 | .wasm_memory_size => try w.writeWasmMemorySize(s, inst), |
| 259 | 259 | .wasm_memory_grow => try w.writeWasmMemoryGrow(s, inst), |
| 260 | 260 | .mul_add => try w.writeMulAdd(s, inst), |
| 261 | .shuffle => try w.writeShuffle(s, inst), | |
| 261 | 262 | |
| 262 | 263 | .add_with_overflow, |
| 263 | 264 | .sub_with_overflow, |
| ... | ... | @@ -375,6 +376,16 @@ const Writer = struct { |
| 375 | 376 | try w.writeOperand(s, inst, 2, pl_op.operand); |
| 376 | 377 | } |
| 377 | 378 | |
| 379 | fn writeShuffle(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { | |
| 380 | const pl_op = w.air.instructions.items(.data)[inst].pl_op; | |
| 381 | const extra = w.air.extraData(Air.Shuffle, pl_op.payload).data; | |
| 382 | ||
| 383 | try w.writeOperand(s, inst, 0, extra.a); | |
| 384 | try s.writeAll(", "); | |
| 385 | try w.writeOperand(s, inst, 1, extra.b); | |
| 386 | try s.print(", mask {d}, len {d}", .{ extra.mask, extra.mask_len }); | |
| 387 | } | |
| 388 | ||
| 378 | 389 | fn writeFence(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { |
| 379 | 390 | const atomic_order = w.air.instructions.items(.data)[inst].fence; |
| 380 | 391 |
src/value.zig+43| ... | ... | @@ -1157,6 +1157,7 @@ pub const Value = extern union { |
| 1157 | 1157 | ) Allocator.Error!Value { |
| 1158 | 1158 | switch (ty.zigTypeTag()) { |
| 1159 | 1159 | .Int => { |
| 1160 | if (buffer.len == 0) return Value.zero; | |
| 1160 | 1161 | const int_info = ty.intInfo(target); |
| 1161 | 1162 | const endian = target.cpu.arch.endian(); |
| 1162 | 1163 | const Limb = std.math.big.Limb; |
| ... | ... | @@ -1819,7 +1820,22 @@ pub const Value = extern union { |
| 1819 | 1820 | } |
| 1820 | 1821 | |
| 1821 | 1822 | /// Asserts the value is comparable. |
| 1823 | /// For vectors this is only valid with op == .eq. | |
| 1822 | 1824 | pub fn compareWithZero(lhs: Value, op: std.math.CompareOperator) bool { |
| 1825 | switch (lhs.tag()) { | |
| 1826 | .repeated => { | |
| 1827 | assert(op == .eq); | |
| 1828 | return lhs.castTag(.repeated).?.data.compareWithZero(.eq); | |
| 1829 | }, | |
| 1830 | .array => { | |
| 1831 | assert(op == .eq); | |
| 1832 | for (lhs.cast(Payload.Array).?.data) |elem_val| { | |
| 1833 | if (!elem_val.compareWithZero(.eq)) return false; | |
| 1834 | } | |
| 1835 | return true; | |
| 1836 | }, | |
| 1837 | else => {}, | |
| 1838 | } | |
| 1823 | 1839 | return orderAgainstZero(lhs).compare(op); |
| 1824 | 1840 | } |
| 1825 | 1841 | |
| ... | ... | @@ -2170,6 +2186,33 @@ pub const Value = extern union { |
| 2170 | 2186 | }; |
| 2171 | 2187 | } |
| 2172 | 2188 | |
| 2189 | pub fn canMutateComptimeVarState(val: Value) bool { | |
| 2190 | if (val.isComptimeMutablePtr()) return true; | |
| 2191 | switch (val.tag()) { | |
| 2192 | .repeated => return val.castTag(.repeated).?.data.canMutateComptimeVarState(), | |
| 2193 | .array => { | |
| 2194 | const elems = val.cast(Payload.Array).?.data; | |
| 2195 | for (elems) |elem| { | |
| 2196 | if (elem.canMutateComptimeVarState()) return true; | |
| 2197 | } | |
| 2198 | return false; | |
| 2199 | }, | |
| 2200 | .eu_payload => return val.castTag(.eu_payload).?.data.canMutateComptimeVarState(), | |
| 2201 | .eu_payload_ptr => return val.castTag(.eu_payload_ptr).?.data.canMutateComptimeVarState(), | |
| 2202 | .opt_payload => return val.castTag(.opt_payload).?.data.canMutateComptimeVarState(), | |
| 2203 | .opt_payload_ptr => return val.castTag(.opt_payload_ptr).?.data.canMutateComptimeVarState(), | |
| 2204 | .@"struct" => { | |
| 2205 | const fields = val.cast(Payload.Struct).?.data; | |
| 2206 | for (fields) |field| { | |
| 2207 | if (field.canMutateComptimeVarState()) return true; | |
| 2208 | } | |
| 2209 | return false; | |
| 2210 | }, | |
| 2211 | .@"union" => return val.cast(Payload.Union).?.data.val.canMutateComptimeVarState(), | |
| 2212 | else => return false, | |
| 2213 | } | |
| 2214 | } | |
| 2215 | ||
| 2173 | 2216 | /// Gets the decl referenced by this pointer. If the pointer does not point |
| 2174 | 2217 | /// to a decl, or if it points to some part of a decl (like field_ptr or element_ptr), |
| 2175 | 2218 | /// this function returns null. |
test/behavior.zig+1-1| ... | ... | @@ -151,6 +151,7 @@ test { |
| 151 | 151 | _ = @import("behavior/bugs/2114.zig"); |
| 152 | 152 | _ = @import("behavior/bugs/3779.zig"); |
| 153 | 153 | _ = @import("behavior/bugs/10147.zig"); |
| 154 | _ = @import("behavior/shuffle.zig"); | |
| 154 | 155 | _ = @import("behavior/union_with_members.zig"); |
| 155 | 156 | |
| 156 | 157 | if (builtin.zig_backend == .stage1) { |
| ... | ... | @@ -169,7 +170,6 @@ test { |
| 169 | 170 | _ = @import("behavior/bugs/7027.zig"); |
| 170 | 171 | _ = @import("behavior/const_slice_child.zig"); |
| 171 | 172 | _ = @import("behavior/select.zig"); |
| 172 | _ = @import("behavior/shuffle.zig"); | |
| 173 | 173 | _ = @import("behavior/struct_contains_slice_of_itself.zig"); |
| 174 | 174 | _ = @import("behavior/typename.zig"); |
| 175 | 175 | _ = @import("behavior/vector.zig"); |
test/behavior/generics.zig+13| ... | ... | @@ -230,3 +230,16 @@ fn GenNode(comptime T: type) type { |
| 230 | 230 | } |
| 231 | 231 | }; |
| 232 | 232 | } |
| 233 | ||
| 234 | test "function parameter is generic" { | |
| 235 | const S = struct { | |
| 236 | pub fn init(pointer: anytype, comptime fillFn: fn (ptr: *@TypeOf(pointer)) void) void { | |
| 237 | _ = fillFn; | |
| 238 | } | |
| 239 | pub fn fill(self: *u32) void { | |
| 240 | _ = self; | |
| 241 | } | |
| 242 | }; | |
| 243 | var rng: u32 = 2; | |
| 244 | S.init(rng, S.fill); | |
| 245 | } |
test/behavior/shuffle.zig+36-23| ... | ... | @@ -4,12 +4,12 @@ const mem = std.mem; |
| 4 | 4 | const expect = std.testing.expect; |
| 5 | 5 | const Vector = std.meta.Vector; |
| 6 | 6 | |
| 7 | test "@shuffle" { | |
| 7 | test "@shuffle int" { | |
| 8 | 8 | const S = struct { |
| 9 | 9 | fn doTheTest() !void { |
| 10 | 10 | var v: Vector(4, i32) = [4]i32{ 2147483647, -2, 30, 40 }; |
| 11 | 11 | var x: Vector(4, i32) = [4]i32{ 1, 2147483647, 3, 4 }; |
| 12 | const mask: Vector(4, i32) = [4]i32{ 0, ~@as(i32, 2), 3, ~@as(i32, 3) }; | |
| 12 | const mask = [4]i32{ 0, ~@as(i32, 2), 3, ~@as(i32, 3) }; | |
| 13 | 13 | var res = @shuffle(i32, v, x, mask); |
| 14 | 14 | try expect(mem.eql(i32, &@as([4]i32, res), &[4]i32{ 2147483647, 3, 40, 4 })); |
| 15 | 15 | |
| ... | ... | @@ -18,40 +18,53 @@ test "@shuffle" { |
| 18 | 18 | try expect(mem.eql(i32, &@as([4]i32, res), &[4]i32{ 2147483647, 3, 40, 4 })); |
| 19 | 19 | |
| 20 | 20 | // Undefined |
| 21 | const mask2: Vector(4, i32) = [4]i32{ 3, 1, 2, 0 }; | |
| 21 | const mask2 = [4]i32{ 3, 1, 2, 0 }; | |
| 22 | 22 | res = @shuffle(i32, v, undefined, mask2); |
| 23 | 23 | try expect(mem.eql(i32, &@as([4]i32, res), &[4]i32{ 40, -2, 30, 2147483647 })); |
| 24 | 24 | |
| 25 | 25 | // Upcasting of b |
| 26 | 26 | var v2: Vector(2, i32) = [2]i32{ 2147483647, undefined }; |
| 27 | const mask3: Vector(4, i32) = [4]i32{ ~@as(i32, 0), 2, ~@as(i32, 0), 3 }; | |
| 27 | const mask3 = [4]i32{ ~@as(i32, 0), 2, ~@as(i32, 0), 3 }; | |
| 28 | 28 | res = @shuffle(i32, x, v2, mask3); |
| 29 | 29 | try expect(mem.eql(i32, &@as([4]i32, res), &[4]i32{ 2147483647, 3, 2147483647, 4 })); |
| 30 | 30 | |
| 31 | 31 | // Upcasting of a |
| 32 | 32 | var v3: Vector(2, i32) = [2]i32{ 2147483647, -2 }; |
| 33 | const mask4: Vector(4, i32) = [4]i32{ 0, ~@as(i32, 2), 1, ~@as(i32, 3) }; | |
| 33 | const mask4 = [4]i32{ 0, ~@as(i32, 2), 1, ~@as(i32, 3) }; | |
| 34 | 34 | res = @shuffle(i32, v3, x, mask4); |
| 35 | 35 | try expect(mem.eql(i32, &@as([4]i32, res), &[4]i32{ 2147483647, 3, -2, 4 })); |
| 36 | } | |
| 37 | }; | |
| 38 | try S.doTheTest(); | |
| 39 | comptime try S.doTheTest(); | |
| 40 | } | |
| 36 | 41 | |
| 37 | // bool | |
| 38 | { | |
| 39 | var x2: Vector(4, bool) = [4]bool{ false, true, false, true }; | |
| 40 | var v4: Vector(2, bool) = [2]bool{ true, false }; | |
| 41 | const mask5: Vector(4, i32) = [4]i32{ 0, ~@as(i32, 1), 1, 2 }; | |
| 42 | var res2 = @shuffle(bool, x2, v4, mask5); | |
| 43 | try expect(mem.eql(bool, &@as([4]bool, res2), &[4]bool{ false, false, true, false })); | |
| 44 | } | |
| 45 | ||
| 46 | // TODO re-enable when LLVM codegen is fixed | |
| 47 | // https://github.com/ziglang/zig/issues/3246 | |
| 48 | if (false) { | |
| 49 | var x2: Vector(3, bool) = [3]bool{ false, true, false }; | |
| 50 | var v4: Vector(2, bool) = [2]bool{ true, false }; | |
| 51 | const mask5: Vector(4, i32) = [4]i32{ 0, ~@as(i32, 1), 1, 2 }; | |
| 52 | var res2 = @shuffle(bool, x2, v4, mask5); | |
| 53 | try expect(mem.eql(bool, &@as([4]bool, res2), &[4]bool{ false, false, true, false })); | |
| 54 | } | |
| 42 | test "@shuffle bool" { | |
| 43 | const S = struct { | |
| 44 | fn doTheTest() !void { | |
| 45 | var x: Vector(4, bool) = [4]bool{ false, true, false, true }; | |
| 46 | var v: Vector(2, bool) = [2]bool{ true, false }; | |
| 47 | const mask = [4]i32{ 0, ~@as(i32, 1), 1, 2 }; | |
| 48 | var res = @shuffle(bool, x, v, mask); | |
| 49 | try expect(mem.eql(bool, &@as([4]bool, res), &[4]bool{ false, false, true, false })); | |
| 50 | } | |
| 51 | }; | |
| 52 | if (builtin.zig_backend == .stage1) try S.doTheTest(); | |
| 53 | comptime try S.doTheTest(); | |
| 54 | } | |
| 55 | ||
| 56 | test "@shuffle bool" { | |
| 57 | // TODO re-enable when LLVM codegen is fixed | |
| 58 | // https://github.com/ziglang/zig/issues/3246 | |
| 59 | if (true) return error.SkipZigTest; | |
| 60 | ||
| 61 | const S = struct { | |
| 62 | fn doTheTest() !void { | |
| 63 | var x: Vector(3, bool) = [3]bool{ false, true, false }; | |
| 64 | var v: Vector(2, bool) = [2]bool{ true, false }; | |
| 65 | const mask: Vector(4, i32) = [4]i32{ 0, ~@as(i32, 1), 1, 2 }; | |
| 66 | var res = @shuffle(bool, x, v, mask); | |
| 67 | try expect(mem.eql(bool, &@as([4]bool, res), &[4]bool{ false, false, true, false })); | |
| 55 | 68 | } |
| 56 | 69 | }; |
| 57 | 70 | try S.doTheTest(); |