mirror of
https://github.com/ziglang/zig.git
synced 2026-01-20 22:35:24 +00:00
Merge pull request #11120 from Vexu/stage2
Stage2: make std.rand tests pass
This commit is contained in:
commit
4c1cc4d8d9
@ -7,6 +7,7 @@
|
||||
//! TODO(tiehuis): Benchmark these against other reference implementations.
|
||||
|
||||
const std = @import("std.zig");
|
||||
const builtin = @import("builtin");
|
||||
const assert = std.debug.assert;
|
||||
const expect = std.testing.expect;
|
||||
const expectEqual = std.testing.expectEqual;
|
||||
@ -30,7 +31,10 @@ pub const Sfc64 = @import("rand/Sfc64.zig");
|
||||
|
||||
pub const Random = struct {
|
||||
ptr: *anyopaque,
|
||||
fillFn: fn (ptr: *anyopaque, buf: []u8) void,
|
||||
fillFn: if (builtin.zig_backend == .stage1)
|
||||
fn (ptr: *anyopaque, buf: []u8) void
|
||||
else
|
||||
*const fn (ptr: *anyopaque, buf: []u8) void,
|
||||
|
||||
pub fn init(pointer: anytype, comptime fillFn: fn (ptr: @TypeOf(pointer), buf: []u8) void) Random {
|
||||
const Ptr = @TypeOf(pointer);
|
||||
|
||||
12
src/Air.zig
12
src/Air.zig
@ -520,6 +520,9 @@ pub const Inst = struct {
|
||||
/// equal to the scalar value.
|
||||
/// Uses the `ty_op` field.
|
||||
splat,
|
||||
/// Constructs a vector by selecting elements from `a` and `b` based on `mask`.
|
||||
/// Uses the `ty_pl` field with payload `Shuffle`.
|
||||
shuffle,
|
||||
|
||||
/// Given dest ptr, value, and len, set all elements at dest to value.
|
||||
/// Result type is always void.
|
||||
@ -740,6 +743,14 @@ pub const FieldParentPtr = struct {
|
||||
field_index: u32,
|
||||
};
|
||||
|
||||
pub const Shuffle = struct {
|
||||
a: Inst.Ref,
|
||||
b: Inst.Ref,
|
||||
// index to air_values
|
||||
mask: u32,
|
||||
mask_len: u32,
|
||||
};
|
||||
|
||||
/// Trailing:
|
||||
/// 0. `Inst.Ref` for every outputs_len
|
||||
/// 1. `Inst.Ref` for every inputs_len
|
||||
@ -897,6 +908,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
|
||||
.cmpxchg_weak,
|
||||
.cmpxchg_strong,
|
||||
.slice,
|
||||
.shuffle,
|
||||
.aggregate_init,
|
||||
.union_init,
|
||||
.field_parent_ptr,
|
||||
|
||||
@ -422,6 +422,10 @@ fn analyzeInst(
|
||||
}
|
||||
return extra_tombs.finish();
|
||||
},
|
||||
.shuffle => {
|
||||
const extra = a.air.extraData(Air.Shuffle, inst_datas[inst].ty_pl.payload).data;
|
||||
return trackOperands(a, new_set, inst, main_tomb, .{ extra.a, extra.b, .none });
|
||||
},
|
||||
.aggregate_init => {
|
||||
const ty_pl = inst_datas[inst].ty_pl;
|
||||
const aggregate_ty = a.air.getRefType(ty_pl.ty);
|
||||
|
||||
247
src/Sema.zig
247
src/Sema.zig
@ -4618,7 +4618,7 @@ fn analyzeCall(
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
should_memoize = should_memoize and !arg_val.isComptimeMutablePtr();
|
||||
should_memoize = should_memoize and !arg_val.canMutateComptimeVarState();
|
||||
memoized_call_key.args[arg_i] = .{
|
||||
.ty = param_ty,
|
||||
.val = arg_val,
|
||||
@ -4644,7 +4644,7 @@ fn analyzeCall(
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
should_memoize = should_memoize and !arg_val.isComptimeMutablePtr();
|
||||
should_memoize = should_memoize and !arg_val.canMutateComptimeVarState();
|
||||
memoized_call_key.args[arg_i] = .{
|
||||
.ty = sema.typeOf(uncasted_arg),
|
||||
.val = arg_val,
|
||||
@ -5923,6 +5923,10 @@ fn funcCommon(
|
||||
break :ret_ty ret_ty;
|
||||
} else |err| break :err err;
|
||||
} else |err| break :err err;
|
||||
// Check for generic params.
|
||||
for (block.params.items) |param| {
|
||||
if (param.ty.tag() == .generic_poison) is_generic = true;
|
||||
}
|
||||
};
|
||||
switch (err) {
|
||||
error.GenericPoison => {
|
||||
@ -6111,6 +6115,13 @@ fn zirParam(
|
||||
|
||||
if (sema.resolveBody(block, body, inst)) |param_ty_inst| {
|
||||
if (sema.analyzeAsType(block, src, param_ty_inst)) |param_ty| {
|
||||
if (param_ty.zigTypeTag() == .Fn and param_ty.fnInfo().is_generic) {
|
||||
// zirFunc will not emit error.GenericPoison to build a
|
||||
// partial type for generic functions but we still need to
|
||||
// detect if a function parameter is a generic function
|
||||
// to force the parent function to also be generic.
|
||||
break :err error.GenericPoison;
|
||||
}
|
||||
break :param_ty param_ty;
|
||||
} else |err| break :err err;
|
||||
} else |err| break :err err;
|
||||
@ -8048,7 +8059,6 @@ fn zirBitwise(
|
||||
rhs_ty.arrayLen(),
|
||||
});
|
||||
}
|
||||
return sema.fail(block, src, "TODO implement support for vectors in zirBitwise", .{});
|
||||
} else if (lhs_ty.zigTypeTag() == .Vector or rhs_ty.zigTypeTag() == .Vector) {
|
||||
return sema.fail(block, src, "mixed scalar and vector operands to binary expression: '{}' and '{}'", .{
|
||||
lhs_ty,
|
||||
@ -8064,6 +8074,9 @@ fn zirBitwise(
|
||||
|
||||
if (try sema.resolveMaybeUndefVal(block, lhs_src, casted_lhs)) |lhs_val| {
|
||||
if (try sema.resolveMaybeUndefVal(block, rhs_src, casted_rhs)) |rhs_val| {
|
||||
if (resolved_type.zigTypeTag() == .Vector) {
|
||||
return sema.fail(block, src, "TODO implement zirBitwise for vectors at comptime", .{});
|
||||
}
|
||||
const result_val = switch (air_tag) {
|
||||
.bit_and => try lhs_val.bitwiseAnd(rhs_val, sema.arena),
|
||||
.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
|
||||
|
||||
const operand = try sema.resolveBody(&child_block, body, inst);
|
||||
const operand_ty = sema.typeOf(operand);
|
||||
if (operand_ty.tag() == .generic_poison) return error.GenericPoison;
|
||||
return sema.addType(operand_ty);
|
||||
}
|
||||
|
||||
@ -10973,19 +10987,21 @@ fn zirTypeofLog2IntType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Compil
|
||||
const src = inst_data.src();
|
||||
const operand = sema.resolveInst(inst_data.operand);
|
||||
const operand_ty = sema.typeOf(operand);
|
||||
return sema.log2IntType(block, operand_ty, src);
|
||||
const res_ty = try sema.log2IntType(block, operand_ty, src);
|
||||
return sema.addType(res_ty);
|
||||
}
|
||||
|
||||
fn zirLog2IntType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
|
||||
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
|
||||
const src = inst_data.src();
|
||||
const operand = try sema.resolveType(block, src, inst_data.operand);
|
||||
return sema.log2IntType(block, operand, src);
|
||||
const res_ty = try sema.log2IntType(block, operand, src);
|
||||
return sema.addType(res_ty);
|
||||
}
|
||||
|
||||
fn log2IntType(sema: *Sema, block: *Block, operand: Type, src: LazySrcLoc) CompileError!Air.Inst.Ref {
|
||||
fn log2IntType(sema: *Sema, block: *Block, operand: Type, src: LazySrcLoc) CompileError!Type {
|
||||
switch (operand.zigTypeTag()) {
|
||||
.ComptimeInt => return Air.Inst.Ref.comptime_int_type,
|
||||
.ComptimeInt => return Type.@"comptime_int",
|
||||
.Int => {
|
||||
const bits = operand.bitSize(sema.mod.getTarget());
|
||||
const count = if (bits == 0)
|
||||
@ -10998,16 +11014,24 @@ fn log2IntType(sema: *Sema, block: *Block, operand: Type, src: LazySrcLoc) Compi
|
||||
}
|
||||
break :blk count;
|
||||
};
|
||||
const res = try Module.makeIntType(sema.arena, .unsigned, count);
|
||||
return sema.addType(res);
|
||||
return Module.makeIntType(sema.arena, .unsigned, count);
|
||||
},
|
||||
else => return sema.fail(
|
||||
block,
|
||||
src,
|
||||
"bit shifting operation expected integer type, found '{}'",
|
||||
.{operand},
|
||||
),
|
||||
.Vector => {
|
||||
const elem_ty = operand.elemType2();
|
||||
const log2_elem_ty = try sema.log2IntType(block, elem_ty, src);
|
||||
return Type.Tag.vector.create(sema.arena, .{
|
||||
.len = operand.arrayLen(),
|
||||
.elem_type = log2_elem_ty,
|
||||
});
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
return sema.fail(
|
||||
block,
|
||||
src,
|
||||
"bit shifting operation expected integer type, found '{}'",
|
||||
.{operand},
|
||||
);
|
||||
}
|
||||
|
||||
fn zirTypeofPeer(
|
||||
@ -11044,6 +11068,7 @@ fn zirTypeofPeer(
|
||||
|
||||
for (args) |arg_ref, i| {
|
||||
inst_list[i] = sema.resolveInst(arg_ref);
|
||||
if (sema.typeOf(inst_list[i]).tag() == .generic_poison) return error.GenericPoison;
|
||||
}
|
||||
|
||||
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.
|
||||
|
||||
fn zirShuffle(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
|
||||
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
|
||||
const src = inst_data.src();
|
||||
return sema.fail(block, src, "TODO: Sema.zirShuffle", .{});
|
||||
const extra = sema.code.extraData(Zir.Inst.Shuffle, inst_data.payload_index).data;
|
||||
const elem_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
|
||||
const mask_src: LazySrcLoc = .{ .node_offset_builtin_call_arg3 = inst_data.src_node };
|
||||
|
||||
const elem_ty = try sema.resolveType(block, elem_ty_src, extra.elem_type);
|
||||
try sema.checkVectorElemType(block, elem_ty_src, elem_ty);
|
||||
var a = sema.resolveInst(extra.a);
|
||||
var b = sema.resolveInst(extra.b);
|
||||
var mask = sema.resolveInst(extra.mask);
|
||||
var mask_ty = sema.typeOf(mask);
|
||||
|
||||
const mask_len = switch (sema.typeOf(mask).zigTypeTag()) {
|
||||
.Array, .Vector => sema.typeOf(mask).arrayLen(),
|
||||
else => return sema.fail(block, mask_src, "expected vector or array, found {}", .{sema.typeOf(mask)}),
|
||||
};
|
||||
mask_ty = try Type.Tag.vector.create(sema.arena, .{
|
||||
.len = mask_len,
|
||||
.elem_type = Type.@"i32",
|
||||
});
|
||||
mask = try sema.coerce(block, mask_ty, mask, mask_src);
|
||||
const mask_val = try sema.resolveConstMaybeUndefVal(block, mask_src, mask);
|
||||
return sema.analyzeShuffle(block, inst_data.src_node, elem_ty, a, b, mask_val, @intCast(u32, mask_len));
|
||||
}
|
||||
|
||||
fn analyzeShuffle(
|
||||
sema: *Sema,
|
||||
block: *Block,
|
||||
src_node: i32,
|
||||
elem_ty: Type,
|
||||
a_arg: Air.Inst.Ref,
|
||||
b_arg: Air.Inst.Ref,
|
||||
mask: Value,
|
||||
mask_len: u32,
|
||||
) CompileError!Air.Inst.Ref {
|
||||
const a_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = src_node };
|
||||
const b_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = src_node };
|
||||
const mask_src: LazySrcLoc = .{ .node_offset_builtin_call_arg3 = src_node };
|
||||
var a = a_arg;
|
||||
var b = b_arg;
|
||||
|
||||
const res_ty = try Type.Tag.vector.create(sema.arena, .{
|
||||
.len = mask_len,
|
||||
.elem_type = elem_ty,
|
||||
});
|
||||
|
||||
var maybe_a_len = switch (sema.typeOf(a).zigTypeTag()) {
|
||||
.Array, .Vector => sema.typeOf(a).arrayLen(),
|
||||
.Undefined => null,
|
||||
else => return sema.fail(block, a_src, "expected vector or array with element type {}, found {}", .{
|
||||
elem_ty,
|
||||
sema.typeOf(a),
|
||||
}),
|
||||
};
|
||||
var maybe_b_len = switch (sema.typeOf(b).zigTypeTag()) {
|
||||
.Array, .Vector => sema.typeOf(b).arrayLen(),
|
||||
.Undefined => null,
|
||||
else => return sema.fail(block, b_src, "expected vector or array with element type {}, found {}", .{
|
||||
elem_ty,
|
||||
sema.typeOf(b),
|
||||
}),
|
||||
};
|
||||
if (maybe_a_len == null and maybe_b_len == null) {
|
||||
return sema.addConstUndef(res_ty);
|
||||
}
|
||||
const a_len = maybe_a_len orelse maybe_b_len.?;
|
||||
const b_len = maybe_b_len orelse a_len;
|
||||
|
||||
const a_ty = try Type.Tag.vector.create(sema.arena, .{
|
||||
.len = a_len,
|
||||
.elem_type = elem_ty,
|
||||
});
|
||||
const b_ty = try Type.Tag.vector.create(sema.arena, .{
|
||||
.len = b_len,
|
||||
.elem_type = elem_ty,
|
||||
});
|
||||
|
||||
if (maybe_a_len == null) a = try sema.addConstUndef(a_ty);
|
||||
if (maybe_b_len == null) b = try sema.addConstUndef(b_ty);
|
||||
|
||||
const operand_info = [2]std.meta.Tuple(&.{ u64, LazySrcLoc, Type }){
|
||||
.{ a_len, a_src, a_ty },
|
||||
.{ b_len, b_src, b_ty },
|
||||
};
|
||||
|
||||
var i: usize = 0;
|
||||
while (i < mask_len) : (i += 1) {
|
||||
var buf: Value.ElemValueBuffer = undefined;
|
||||
const elem = mask.elemValueBuffer(i, &buf);
|
||||
if (elem.isUndef()) continue;
|
||||
const int = elem.toSignedInt();
|
||||
var unsigned: u32 = undefined;
|
||||
var chosen: u32 = undefined;
|
||||
if (int >= 0) {
|
||||
unsigned = @intCast(u32, int);
|
||||
chosen = 0;
|
||||
} else {
|
||||
unsigned = @intCast(u32, ~int);
|
||||
chosen = 1;
|
||||
}
|
||||
if (unsigned >= operand_info[chosen][0]) {
|
||||
const msg = msg: {
|
||||
const msg = try sema.errMsg(block, mask_src, "mask index {d} has out-of-bounds selection", .{i});
|
||||
errdefer msg.destroy(sema.gpa);
|
||||
|
||||
try sema.errNote(block, operand_info[chosen][1], msg, "selected index {d} out of bounds of {}", .{
|
||||
unsigned,
|
||||
operand_info[chosen][2],
|
||||
});
|
||||
|
||||
if (chosen == 1) {
|
||||
try sema.errNote(block, b_src, msg, "selections from the second vector are specified with negative numbers", .{});
|
||||
}
|
||||
|
||||
break :msg msg;
|
||||
};
|
||||
return sema.failWithOwnedErrorMsg(block, msg);
|
||||
}
|
||||
}
|
||||
|
||||
if (try sema.resolveMaybeUndefVal(block, a_src, a)) |a_val| {
|
||||
if (try sema.resolveMaybeUndefVal(block, b_src, b)) |b_val| {
|
||||
const values = try sema.arena.alloc(Value, mask_len);
|
||||
|
||||
i = 0;
|
||||
while (i < mask_len) : (i += 1) {
|
||||
var buf: Value.ElemValueBuffer = undefined;
|
||||
const mask_elem_val = mask.elemValueBuffer(i, &buf);
|
||||
if (mask_elem_val.isUndef()) {
|
||||
values[i] = Value.undef;
|
||||
continue;
|
||||
}
|
||||
const int = mask_elem_val.toSignedInt();
|
||||
const unsigned = if (int >= 0) @intCast(u32, int) else @intCast(u32, ~int);
|
||||
if (int >= 0) {
|
||||
values[i] = try a_val.elemValue(sema.arena, unsigned);
|
||||
} else {
|
||||
values[i] = try b_val.elemValue(sema.arena, unsigned);
|
||||
}
|
||||
}
|
||||
const res_val = try Value.Tag.array.create(sema.arena, values);
|
||||
return sema.addConstant(res_ty, res_val);
|
||||
}
|
||||
}
|
||||
|
||||
// All static analysis passed, and not comptime.
|
||||
// For runtime codegen, vectors a and b must be the same length. Here we
|
||||
// recursively @shuffle the smaller vector to append undefined elements
|
||||
// to it up to the length of the longer vector. This recursion terminates
|
||||
// in 1 call because these calls to analyzeShuffle guarantee a_len == b_len.
|
||||
if (a_len != b_len) {
|
||||
const min_len = std.math.min(a_len, b_len);
|
||||
const max_src = if (a_len > b_len) a_src else b_src;
|
||||
const max_len = try sema.usizeCast(block, max_src, std.math.max(a_len, b_len));
|
||||
|
||||
const expand_mask_values = try sema.arena.alloc(Value, max_len);
|
||||
i = 0;
|
||||
while (i < min_len) : (i += 1) {
|
||||
expand_mask_values[i] = try Value.Tag.int_u64.create(sema.arena, i);
|
||||
}
|
||||
while (i < max_len) : (i += 1) {
|
||||
expand_mask_values[i] = Value.negative_one;
|
||||
}
|
||||
const expand_mask = try Value.Tag.array.create(sema.arena, expand_mask_values);
|
||||
|
||||
if (a_len < b_len) {
|
||||
const undef = try sema.addConstUndef(a_ty);
|
||||
a = try sema.analyzeShuffle(block, src_node, elem_ty, a, undef, expand_mask, @intCast(u32, max_len));
|
||||
} else {
|
||||
const undef = try sema.addConstUndef(b_ty);
|
||||
b = try sema.analyzeShuffle(block, src_node, elem_ty, b, undef, expand_mask, @intCast(u32, max_len));
|
||||
}
|
||||
}
|
||||
|
||||
const mask_index = @intCast(u32, sema.air_values.items.len);
|
||||
try sema.air_values.append(sema.gpa, mask);
|
||||
return block.addInst(.{
|
||||
.tag = .shuffle,
|
||||
.data = .{ .ty_pl = .{
|
||||
.ty = try sema.addType(res_ty),
|
||||
.payload = try block.sema.addExtra(Air.Shuffle{
|
||||
.a = a,
|
||||
.b = b,
|
||||
.mask = mask_index,
|
||||
.mask_len = mask_len,
|
||||
}),
|
||||
} },
|
||||
});
|
||||
}
|
||||
|
||||
fn zirSelect(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
|
||||
@ -15663,8 +15873,7 @@ fn elemPtr(
|
||||
},
|
||||
}
|
||||
},
|
||||
.Array => return sema.elemPtrArray(block, array_ptr_src, array_ptr, elem_index, elem_index_src),
|
||||
.Vector => return sema.fail(block, src, "TODO implement Sema for elemPtr for vector", .{}),
|
||||
.Array, .Vector => return sema.elemPtrArray(block, array_ptr_src, array_ptr, elem_index, elem_index_src),
|
||||
.Struct => {
|
||||
// Tuple field access.
|
||||
const index_val = try sema.resolveConstValue(block, elem_index_src, elem_index);
|
||||
|
||||
@ -637,6 +637,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
|
||||
.tag_name => try self.airTagName(inst),
|
||||
.error_name => try self.airErrorName(inst),
|
||||
.splat => try self.airSplat(inst),
|
||||
.shuffle => try self.airShuffle(inst),
|
||||
.aggregate_init => try self.airAggregateInit(inst),
|
||||
.union_init => try self.airUnionInit(inst),
|
||||
.prefetch => try self.airPrefetch(inst),
|
||||
@ -3633,6 +3634,12 @@ fn airSplat(self: *Self, inst: Air.Inst.Index) !void {
|
||||
return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
|
||||
}
|
||||
|
||||
fn airShuffle(self: *Self, inst: Air.Inst.Index) !void {
|
||||
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
|
||||
const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airShuffle for {}", .{self.target.cpu.arch});
|
||||
return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
|
||||
}
|
||||
|
||||
fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void {
|
||||
const vector_ty = self.air.typeOfIndex(inst);
|
||||
const len = vector_ty.vectorLen();
|
||||
|
||||
@ -636,6 +636,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
|
||||
.tag_name => try self.airTagName(inst),
|
||||
.error_name => try self.airErrorName(inst),
|
||||
.splat => try self.airSplat(inst),
|
||||
.shuffle => try self.airShuffle(inst),
|
||||
.aggregate_init => try self.airAggregateInit(inst),
|
||||
.union_init => try self.airUnionInit(inst),
|
||||
.prefetch => try self.airPrefetch(inst),
|
||||
@ -4094,6 +4095,12 @@ fn airSplat(self: *Self, inst: Air.Inst.Index) !void {
|
||||
return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
|
||||
}
|
||||
|
||||
fn airShuffle(self: *Self, inst: Air.Inst.Index) !void {
|
||||
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
|
||||
const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airShuffle for arm", .{});
|
||||
return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
|
||||
}
|
||||
|
||||
fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void {
|
||||
const vector_ty = self.air.typeOfIndex(inst);
|
||||
const len = vector_ty.vectorLen();
|
||||
|
||||
@ -603,6 +603,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
|
||||
.tag_name => try self.airTagName(inst),
|
||||
.error_name => try self.airErrorName(inst),
|
||||
.splat => try self.airSplat(inst),
|
||||
.shuffle => try self.airShuffle(inst),
|
||||
.aggregate_init => try self.airAggregateInit(inst),
|
||||
.union_init => try self.airUnionInit(inst),
|
||||
.prefetch => try self.airPrefetch(inst),
|
||||
@ -2181,6 +2182,12 @@ fn airSplat(self: *Self, inst: Air.Inst.Index) !void {
|
||||
return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
|
||||
}
|
||||
|
||||
fn airShuffle(self: *Self, inst: Air.Inst.Index) !void {
|
||||
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
|
||||
const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airShuffle for riscv64", .{});
|
||||
return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
|
||||
}
|
||||
|
||||
fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void {
|
||||
const vector_ty = self.air.typeOfIndex(inst);
|
||||
const len = vector_ty.vectorLen();
|
||||
|
||||
@ -1255,6 +1255,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {
|
||||
.ret_ptr => self.airRetPtr(inst),
|
||||
.ret_load => self.airRetLoad(inst),
|
||||
.splat => self.airSplat(inst),
|
||||
.shuffle => self.airShuffle(inst),
|
||||
.aggregate_init => self.airAggregateInit(inst),
|
||||
.union_init => self.airUnionInit(inst),
|
||||
.prefetch => self.airPrefetch(inst),
|
||||
@ -2985,6 +2986,17 @@ fn airSplat(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
|
||||
return self.fail("TODO: Implement wasm airSplat", .{});
|
||||
}
|
||||
|
||||
fn airShuffle(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
|
||||
if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
|
||||
|
||||
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
|
||||
const operand = try self.resolveInst(ty_op.operand);
|
||||
|
||||
_ = ty_op;
|
||||
_ = operand;
|
||||
return self.fail("TODO: Implement wasm airShuffle", .{});
|
||||
}
|
||||
|
||||
fn airAggregateInit(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
|
||||
if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
|
||||
|
||||
|
||||
@ -720,6 +720,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
|
||||
.tag_name => try self.airTagName(inst),
|
||||
.error_name => try self.airErrorName(inst),
|
||||
.splat => try self.airSplat(inst),
|
||||
.shuffle => try self.airShuffle(inst),
|
||||
.aggregate_init => try self.airAggregateInit(inst),
|
||||
.union_init => try self.airUnionInit(inst),
|
||||
.prefetch => try self.airPrefetch(inst),
|
||||
@ -5535,6 +5536,12 @@ fn airSplat(self: *Self, inst: Air.Inst.Index) !void {
|
||||
return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
|
||||
}
|
||||
|
||||
fn airShuffle(self: *Self, inst: Air.Inst.Index) !void {
|
||||
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
|
||||
const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airShuffle for x86_64", .{});
|
||||
return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
|
||||
}
|
||||
|
||||
fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void {
|
||||
const vector_ty = self.air.typeOfIndex(inst);
|
||||
const len = vector_ty.vectorLen();
|
||||
|
||||
@ -1716,6 +1716,7 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
|
||||
.tag_name => try airTagName(f, inst),
|
||||
.error_name => try airErrorName(f, inst),
|
||||
.splat => try airSplat(f, inst),
|
||||
.shuffle => try airShuffle(f, inst),
|
||||
.aggregate_init => try airAggregateInit(f, inst),
|
||||
.union_init => try airUnionInit(f, inst),
|
||||
.prefetch => try airPrefetch(f, inst),
|
||||
@ -3557,6 +3558,21 @@ fn airSplat(f: *Function, inst: Air.Inst.Index) !CValue {
|
||||
return f.fail("TODO: C backend: implement airSplat", .{});
|
||||
}
|
||||
|
||||
fn airShuffle(f: *Function, inst: Air.Inst.Index) !CValue {
|
||||
if (f.liveness.isUnused(inst)) return CValue.none;
|
||||
|
||||
const inst_ty = f.air.typeOfIndex(inst);
|
||||
const ty_op = f.air.instructions.items(.data)[inst].ty_op;
|
||||
const operand = try f.resolveInst(ty_op.operand);
|
||||
const writer = f.object.writer();
|
||||
const local = try f.allocLocal(inst_ty, .Const);
|
||||
try writer.writeAll(" = ");
|
||||
|
||||
_ = operand;
|
||||
_ = local;
|
||||
return f.fail("TODO: C backend: implement airShuffle", .{});
|
||||
}
|
||||
|
||||
fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue {
|
||||
if (f.liveness.isUnused(inst)) return CValue.none;
|
||||
|
||||
|
||||
@ -3204,6 +3204,7 @@ pub const FuncGen = struct {
|
||||
.tag_name => try self.airTagName(inst),
|
||||
.error_name => try self.airErrorName(inst),
|
||||
.splat => try self.airSplat(inst),
|
||||
.shuffle => try self.airShuffle(inst),
|
||||
.aggregate_init => try self.airAggregateInit(inst),
|
||||
.union_init => try self.airUnionInit(inst),
|
||||
.prefetch => try self.airPrefetch(inst),
|
||||
@ -5850,6 +5851,43 @@ pub const FuncGen = struct {
|
||||
return self.builder.buildShuffleVector(op_vector, undef_vector, mask_llvm_ty.constNull(), "");
|
||||
}
|
||||
|
||||
fn airShuffle(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
|
||||
if (self.liveness.isUnused(inst)) return null;
|
||||
|
||||
const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
|
||||
const extra = self.air.extraData(Air.Shuffle, ty_pl.payload).data;
|
||||
const a = try self.resolveInst(extra.a);
|
||||
const b = try self.resolveInst(extra.b);
|
||||
const mask = self.air.values[extra.mask];
|
||||
const mask_len = extra.mask_len;
|
||||
const a_len = self.air.typeOf(extra.a).vectorLen();
|
||||
|
||||
// LLVM uses integers larger than the length of the first array to
|
||||
// index into the second array. This was deemed unnecessarily fragile
|
||||
// when changing code, so Zig uses negative numbers to index the
|
||||
// second vector. These start at -1 and go down, and are easiest to use
|
||||
// with the ~ operator. Here we convert between the two formats.
|
||||
const values = try self.gpa.alloc(*const llvm.Value, mask_len);
|
||||
defer self.gpa.free(values);
|
||||
|
||||
const llvm_i32 = self.context.intType(32);
|
||||
|
||||
for (values) |*val, i| {
|
||||
var buf: Value.ElemValueBuffer = undefined;
|
||||
const elem = mask.elemValueBuffer(i, &buf);
|
||||
if (elem.isUndef()) {
|
||||
val.* = llvm_i32.getUndef();
|
||||
} else {
|
||||
const int = elem.toSignedInt();
|
||||
const unsigned = if (int >= 0) @intCast(u32, int) else @intCast(u32, ~int + a_len);
|
||||
val.* = llvm_i32.constInt(unsigned, .False);
|
||||
}
|
||||
}
|
||||
|
||||
const llvm_mask_value = llvm.constVector(values.ptr, mask_len);
|
||||
return self.builder.buildShuffleVector(a, b, llvm_mask_value, "");
|
||||
}
|
||||
|
||||
fn airAggregateInit(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
|
||||
if (self.liveness.isUnused(inst)) return null;
|
||||
|
||||
|
||||
@ -258,6 +258,7 @@ const Writer = struct {
|
||||
.wasm_memory_size => try w.writeWasmMemorySize(s, inst),
|
||||
.wasm_memory_grow => try w.writeWasmMemoryGrow(s, inst),
|
||||
.mul_add => try w.writeMulAdd(s, inst),
|
||||
.shuffle => try w.writeShuffle(s, inst),
|
||||
|
||||
.add_with_overflow,
|
||||
.sub_with_overflow,
|
||||
@ -375,6 +376,16 @@ const Writer = struct {
|
||||
try w.writeOperand(s, inst, 2, pl_op.operand);
|
||||
}
|
||||
|
||||
fn writeShuffle(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
|
||||
const pl_op = w.air.instructions.items(.data)[inst].pl_op;
|
||||
const extra = w.air.extraData(Air.Shuffle, pl_op.payload).data;
|
||||
|
||||
try w.writeOperand(s, inst, 0, extra.a);
|
||||
try s.writeAll(", ");
|
||||
try w.writeOperand(s, inst, 1, extra.b);
|
||||
try s.print(", mask {d}, len {d}", .{ extra.mask, extra.mask_len });
|
||||
}
|
||||
|
||||
fn writeFence(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
|
||||
const atomic_order = w.air.instructions.items(.data)[inst].fence;
|
||||
|
||||
|
||||
@ -1157,6 +1157,7 @@ pub const Value = extern union {
|
||||
) Allocator.Error!Value {
|
||||
switch (ty.zigTypeTag()) {
|
||||
.Int => {
|
||||
if (buffer.len == 0) return Value.zero;
|
||||
const int_info = ty.intInfo(target);
|
||||
const endian = target.cpu.arch.endian();
|
||||
const Limb = std.math.big.Limb;
|
||||
@ -1819,7 +1820,22 @@ pub const Value = extern union {
|
||||
}
|
||||
|
||||
/// Asserts the value is comparable.
|
||||
/// For vectors this is only valid with op == .eq.
|
||||
pub fn compareWithZero(lhs: Value, op: std.math.CompareOperator) bool {
|
||||
switch (lhs.tag()) {
|
||||
.repeated => {
|
||||
assert(op == .eq);
|
||||
return lhs.castTag(.repeated).?.data.compareWithZero(.eq);
|
||||
},
|
||||
.array => {
|
||||
assert(op == .eq);
|
||||
for (lhs.cast(Payload.Array).?.data) |elem_val| {
|
||||
if (!elem_val.compareWithZero(.eq)) return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
return orderAgainstZero(lhs).compare(op);
|
||||
}
|
||||
|
||||
@ -2170,6 +2186,33 @@ pub const Value = extern union {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn canMutateComptimeVarState(val: Value) bool {
|
||||
if (val.isComptimeMutablePtr()) return true;
|
||||
switch (val.tag()) {
|
||||
.repeated => return val.castTag(.repeated).?.data.canMutateComptimeVarState(),
|
||||
.array => {
|
||||
const elems = val.cast(Payload.Array).?.data;
|
||||
for (elems) |elem| {
|
||||
if (elem.canMutateComptimeVarState()) return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
.eu_payload => return val.castTag(.eu_payload).?.data.canMutateComptimeVarState(),
|
||||
.eu_payload_ptr => return val.castTag(.eu_payload_ptr).?.data.canMutateComptimeVarState(),
|
||||
.opt_payload => return val.castTag(.opt_payload).?.data.canMutateComptimeVarState(),
|
||||
.opt_payload_ptr => return val.castTag(.opt_payload_ptr).?.data.canMutateComptimeVarState(),
|
||||
.@"struct" => {
|
||||
const fields = val.cast(Payload.Struct).?.data;
|
||||
for (fields) |field| {
|
||||
if (field.canMutateComptimeVarState()) return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
.@"union" => return val.cast(Payload.Union).?.data.val.canMutateComptimeVarState(),
|
||||
else => return false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the decl referenced by this pointer. If the pointer does not point
|
||||
/// to a decl, or if it points to some part of a decl (like field_ptr or element_ptr),
|
||||
/// this function returns null.
|
||||
|
||||
@ -151,6 +151,7 @@ test {
|
||||
_ = @import("behavior/bugs/2114.zig");
|
||||
_ = @import("behavior/bugs/3779.zig");
|
||||
_ = @import("behavior/bugs/10147.zig");
|
||||
_ = @import("behavior/shuffle.zig");
|
||||
_ = @import("behavior/union_with_members.zig");
|
||||
|
||||
if (builtin.zig_backend == .stage1) {
|
||||
@ -169,7 +170,6 @@ test {
|
||||
_ = @import("behavior/bugs/7027.zig");
|
||||
_ = @import("behavior/const_slice_child.zig");
|
||||
_ = @import("behavior/select.zig");
|
||||
_ = @import("behavior/shuffle.zig");
|
||||
_ = @import("behavior/struct_contains_slice_of_itself.zig");
|
||||
_ = @import("behavior/typename.zig");
|
||||
_ = @import("behavior/vector.zig");
|
||||
|
||||
@ -230,3 +230,16 @@ fn GenNode(comptime T: type) type {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
test "function parameter is generic" {
|
||||
const S = struct {
|
||||
pub fn init(pointer: anytype, comptime fillFn: fn (ptr: *@TypeOf(pointer)) void) void {
|
||||
_ = fillFn;
|
||||
}
|
||||
pub fn fill(self: *u32) void {
|
||||
_ = self;
|
||||
}
|
||||
};
|
||||
var rng: u32 = 2;
|
||||
S.init(rng, S.fill);
|
||||
}
|
||||
|
||||
@ -4,12 +4,12 @@ const mem = std.mem;
|
||||
const expect = std.testing.expect;
|
||||
const Vector = std.meta.Vector;
|
||||
|
||||
test "@shuffle" {
|
||||
test "@shuffle int" {
|
||||
const S = struct {
|
||||
fn doTheTest() !void {
|
||||
var v: Vector(4, i32) = [4]i32{ 2147483647, -2, 30, 40 };
|
||||
var x: Vector(4, i32) = [4]i32{ 1, 2147483647, 3, 4 };
|
||||
const mask: Vector(4, i32) = [4]i32{ 0, ~@as(i32, 2), 3, ~@as(i32, 3) };
|
||||
const mask = [4]i32{ 0, ~@as(i32, 2), 3, ~@as(i32, 3) };
|
||||
var res = @shuffle(i32, v, x, mask);
|
||||
try expect(mem.eql(i32, &@as([4]i32, res), &[4]i32{ 2147483647, 3, 40, 4 }));
|
||||
|
||||
@ -18,40 +18,53 @@ test "@shuffle" {
|
||||
try expect(mem.eql(i32, &@as([4]i32, res), &[4]i32{ 2147483647, 3, 40, 4 }));
|
||||
|
||||
// Undefined
|
||||
const mask2: Vector(4, i32) = [4]i32{ 3, 1, 2, 0 };
|
||||
const mask2 = [4]i32{ 3, 1, 2, 0 };
|
||||
res = @shuffle(i32, v, undefined, mask2);
|
||||
try expect(mem.eql(i32, &@as([4]i32, res), &[4]i32{ 40, -2, 30, 2147483647 }));
|
||||
|
||||
// Upcasting of b
|
||||
var v2: Vector(2, i32) = [2]i32{ 2147483647, undefined };
|
||||
const mask3: Vector(4, i32) = [4]i32{ ~@as(i32, 0), 2, ~@as(i32, 0), 3 };
|
||||
const mask3 = [4]i32{ ~@as(i32, 0), 2, ~@as(i32, 0), 3 };
|
||||
res = @shuffle(i32, x, v2, mask3);
|
||||
try expect(mem.eql(i32, &@as([4]i32, res), &[4]i32{ 2147483647, 3, 2147483647, 4 }));
|
||||
|
||||
// Upcasting of a
|
||||
var v3: Vector(2, i32) = [2]i32{ 2147483647, -2 };
|
||||
const mask4: Vector(4, i32) = [4]i32{ 0, ~@as(i32, 2), 1, ~@as(i32, 3) };
|
||||
const mask4 = [4]i32{ 0, ~@as(i32, 2), 1, ~@as(i32, 3) };
|
||||
res = @shuffle(i32, v3, x, mask4);
|
||||
try expect(mem.eql(i32, &@as([4]i32, res), &[4]i32{ 2147483647, 3, -2, 4 }));
|
||||
|
||||
// bool
|
||||
{
|
||||
var x2: Vector(4, bool) = [4]bool{ false, true, false, true };
|
||||
var v4: Vector(2, bool) = [2]bool{ true, false };
|
||||
const mask5: Vector(4, i32) = [4]i32{ 0, ~@as(i32, 1), 1, 2 };
|
||||
var res2 = @shuffle(bool, x2, v4, mask5);
|
||||
try expect(mem.eql(bool, &@as([4]bool, res2), &[4]bool{ false, false, true, false }));
|
||||
}
|
||||
|
||||
// TODO re-enable when LLVM codegen is fixed
|
||||
// https://github.com/ziglang/zig/issues/3246
|
||||
if (false) {
|
||||
var x2: Vector(3, bool) = [3]bool{ false, true, false };
|
||||
var v4: Vector(2, bool) = [2]bool{ true, false };
|
||||
const mask5: Vector(4, i32) = [4]i32{ 0, ~@as(i32, 1), 1, 2 };
|
||||
var res2 = @shuffle(bool, x2, v4, mask5);
|
||||
try expect(mem.eql(bool, &@as([4]bool, res2), &[4]bool{ false, false, true, false }));
|
||||
}
|
||||
}
|
||||
};
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "@shuffle bool" {
|
||||
const S = struct {
|
||||
fn doTheTest() !void {
|
||||
var x: Vector(4, bool) = [4]bool{ false, true, false, true };
|
||||
var v: Vector(2, bool) = [2]bool{ true, false };
|
||||
const mask = [4]i32{ 0, ~@as(i32, 1), 1, 2 };
|
||||
var res = @shuffle(bool, x, v, mask);
|
||||
try expect(mem.eql(bool, &@as([4]bool, res), &[4]bool{ false, false, true, false }));
|
||||
}
|
||||
};
|
||||
if (builtin.zig_backend == .stage1) try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "@shuffle bool" {
|
||||
// TODO re-enable when LLVM codegen is fixed
|
||||
// https://github.com/ziglang/zig/issues/3246
|
||||
if (true) return error.SkipZigTest;
|
||||
|
||||
const S = struct {
|
||||
fn doTheTest() !void {
|
||||
var x: Vector(3, bool) = [3]bool{ false, true, false };
|
||||
var v: Vector(2, bool) = [2]bool{ true, false };
|
||||
const mask: Vector(4, i32) = [4]i32{ 0, ~@as(i32, 1), 1, 2 };
|
||||
var res = @shuffle(bool, x, v, mask);
|
||||
try expect(mem.eql(bool, &@as([4]bool, res), &[4]bool{ false, false, true, false }));
|
||||
}
|
||||
};
|
||||
try S.doTheTest();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user