aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2022-03-11 13:48:28 -0500
committerGitHub <noreply@github.com>2022-03-11 13:48:28 -0500
commit4c1cc4d8d91af459e5ac1e20b744c7dc9b5b8da3 (patch)
tree8ee7c3b7f2b4c66e9ed8599b9c5164b78d1cc43c /src
parent5fbae9cd6fe7a53d85ad6dd20cab75f7b835f0ad (diff)
parent03b8206f27485f871fc489f884ffbc276d61877c (diff)
downloadzig-4c1cc4d8d91af459e5ac1e20b744c7dc9b5b8da3.tar.gz
zig-4c1cc4d8d91af459e5ac1e20b744c7dc9b5b8da3.zip
Merge pull request #11120 from Vexu/stage2
Stage2: make std.rand tests pass
Diffstat (limited to 'src')
-rw-r--r--src/Air.zig12
-rw-r--r--src/Liveness.zig4
-rw-r--r--src/Sema.zig247
-rw-r--r--src/arch/aarch64/CodeGen.zig7
-rw-r--r--src/arch/arm/CodeGen.zig7
-rw-r--r--src/arch/riscv64/CodeGen.zig7
-rw-r--r--src/arch/wasm/CodeGen.zig12
-rw-r--r--src/arch/x86_64/CodeGen.zig7
-rw-r--r--src/codegen/c.zig16
-rw-r--r--src/codegen/llvm.zig38
-rw-r--r--src/print_air.zig11
-rw-r--r--src/value.zig43
12 files changed, 392 insertions, 19 deletions
diff --git a/src/Air.zig b/src/Air.zig
index cc15113653..a1d5e1e8d9 100644
--- a/src/Air.zig
+++ b/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,
diff --git a/src/Liveness.zig b/src/Liveness.zig
index 077d2f2888..c91288f354 100644
--- a/src/Liveness.zig
+++ b/src/Liveness.zig
@@ -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);
diff --git a/src/Sema.zig b/src/Sema.zig
index 2a3236e8ad..a33861c74c 100644
--- a/src/Sema.zig
+++ b/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);
diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig
index dcac2c0a60..10e93cf23c 100644
--- a/src/arch/aarch64/CodeGen.zig
+++ b/src/arch/aarch64/CodeGen.zig
@@ -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();
diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig
index 2248daf1d0..e1eed9a941 100644
--- a/src/arch/arm/CodeGen.zig
+++ b/src/arch/arm/CodeGen.zig
@@ -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();
diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig
index 07903cebdc..f6a4b9c08e 100644
--- a/src/arch/riscv64/CodeGen.zig
+++ b/src/arch/riscv64/CodeGen.zig
@@ -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();
diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig
index 3f28b87b55..6c54699831 100644
--- a/src/arch/wasm/CodeGen.zig
+++ b/src/arch/wasm/CodeGen.zig
@@ -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 = {} };
diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig
index 4adc59e662..68aa770ffc 100644
--- a/src/arch/x86_64/CodeGen.zig
+++ b/src/arch/x86_64/CodeGen.zig
@@ -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();
diff --git a/src/codegen/c.zig b/src/codegen/c.zig
index 4a30bf023b..c179b65046 100644
--- a/src/codegen/c.zig
+++ b/src/codegen/c.zig
@@ -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;
diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig
index d7df161b00..190fcd0ee6 100644
--- a/src/codegen/llvm.zig
+++ b/src/codegen/llvm.zig
@@ -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;
diff --git a/src/print_air.zig b/src/print_air.zig
index 56d0cb9623..749e4751fb 100644
--- a/src/print_air.zig
+++ b/src/print_air.zig
@@ -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;
diff --git a/src/value.zig b/src/value.zig
index 7c5401cd75..9117ef78df 100644
--- a/src/value.zig
+++ b/src/value.zig
@@ -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.