Skip to content

Commit b6f7b90

Browse files
committed
stage2: implement @ctz and @clz including SIMD
AIR: * `array_elem_val` is now allowed to be used with a vector as the array type. * New instructions: splat, vector_init AstGen: * The splat ZIR instruction uses coerced_ty for the ResultLoc, avoiding an unnecessary `as` instruction, since the coercion will be performed in Sema. * Builtins that accept vectors now ignore the type parameter. Comment from this commit reproduced here: The accepted proposal ziglang#6835 tells us to remove the type parameter from these builtins. To stay source-compatible with stage1, we still observe the parameter here, but we do not encode it into the ZIR. To implement this proposal in stage2, only AstGen code will need to be changed. Sema: * `clz` and `ctz` ZIR instructions are now handled by the same function which accept AIR tag and comptime eval function pointer to differentiate. * `@typeInfo` for vectors is implemented. * `@splat` is implemented. It takes advantage of `Value.Tag.repeated` 😎 * `elemValue` is implemented for vectors, when the index is a scalar. Handling a vector index is still TODO. * Element-wise coercion is implemented for vectors. It could probably be optimized a bit, but it is at least complete & correct. * `Type.intInfo` supports vectors, returning int info for the element. * `Value.ctz` initial implementation. Needs work. * `Value.eql` is implemented for arrays and vectors. LLVM backend: * Implement vector support when lowering `array_elem_val`. * Implement vector support when lowering `ctz` and `clz`. * Implement `splat` and `vector_init`.
1 parent f24f7e7 commit b6f7b90

19 files changed

+707
-129
lines changed

lib/std/testing.zig

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,11 +103,13 @@ pub fn expectEqual(expected: anytype, actual: @TypeOf(expected)) !void {
103103

104104
.Array => |array| try expectEqualSlices(array.child, &expected, &actual),
105105

106-
.Vector => |vectorType| {
106+
.Vector => |info| {
107107
var i: usize = 0;
108-
while (i < vectorType.len) : (i += 1) {
108+
while (i < info.len) : (i += 1) {
109109
if (!std.meta.eql(expected[i], actual[i])) {
110-
std.debug.print("index {} incorrect. expected {}, found {}\n", .{ i, expected[i], actual[i] });
110+
std.debug.print("index {} incorrect. expected {}, found {}\n", .{
111+
i, expected[i], actual[i],
112+
});
111113
return error.TestExpectedEqual;
112114
}
113115
}

src/Air.zig

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -426,7 +426,8 @@ pub const Inst = struct {
426426
/// Given a pointer to a slice, return a pointer to the pointer of the slice.
427427
/// Uses the `ty_op` field.
428428
ptr_slice_ptr_ptr,
429-
/// Given an array value and element index, return the element value at that index.
429+
/// Given an (array value or vector value) and element index,
430+
/// return the element value at that index.
430431
/// Result type is the element type of the array operand.
431432
/// Uses the `bin_op` field.
432433
array_elem_val,
@@ -455,6 +456,10 @@ pub const Inst = struct {
455456
/// Given an integer operand, return the float with the closest mathematical meaning.
456457
/// Uses the `ty_op` field.
457458
int_to_float,
459+
/// Given an integer, bool, float, or pointer operand, return a vector with all elements
460+
/// equal to the scalar value.
461+
/// Uses the `ty_op` field.
462+
splat,
458463

459464
/// Given dest ptr, value, and len, set all elements at dest to value.
460465
/// Result type is always void.
@@ -505,6 +510,11 @@ pub const Inst = struct {
505510
/// Uses the `un_op` field.
506511
error_name,
507512

513+
/// Constructs a vector value out of runtime-known elements.
514+
/// Uses the `ty_pl` field, payload is index of an array of elements, each of which
515+
/// is a `Ref`. Length of the array is given by the vector type.
516+
vector_init,
517+
508518
pub fn fromCmpOp(op: std.math.CompareOperator) Tag {
509519
return switch (op) {
510520
.lt => .cmp_lt,
@@ -756,6 +766,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
756766
.cmpxchg_weak,
757767
.cmpxchg_strong,
758768
.slice,
769+
.vector_init,
759770
=> return air.getRefType(datas[inst].ty_pl.ty),
760771

761772
.not,
@@ -785,6 +796,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
785796
.array_to_slice,
786797
.float_to_int,
787798
.int_to_float,
799+
.splat,
788800
.get_union_tag,
789801
.clz,
790802
.ctz,

src/AstGen.zig

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7060,7 +7060,7 @@ fn builtinCall(
70607060
},
70617061

70627062
.splat => {
7063-
const len = try expr(gz, scope, .{ .ty = .u32_type }, params[0]);
7063+
const len = try expr(gz, scope, .{ .coerced_ty = .u32_type }, params[0]);
70647064
const scalar = try expr(gz, scope, .none, params[1]);
70657065
const result = try gz.addPlNode(.splat, node, Zir.Inst.Bin{
70667066
.lhs = len,
@@ -7395,8 +7395,14 @@ fn bitBuiltin(
73957395
operand_node: Ast.Node.Index,
73967396
tag: Zir.Inst.Tag,
73977397
) InnerError!Zir.Inst.Ref {
7398-
const int_type = try typeExpr(gz, scope, int_type_node);
7399-
const operand = try expr(gz, scope, .{ .ty = int_type }, operand_node);
7398+
// The accepted proposal https://github.com/ziglang/zig/issues/6835
7399+
// tells us to remove the type parameter from these builtins. To stay
7400+
// source-compatible with stage1, we still observe the parameter here,
7401+
// but we do not encode it into the ZIR. To implement this proposal in
7402+
// stage2, only AstGen code will need to be changed.
7403+
_ = try typeExpr(gz, scope, int_type_node);
7404+
7405+
const operand = try expr(gz, scope, .none, operand_node);
74007406
const result = try gz.addUnNode(tag, operand, node);
74017407
return rvalue(gz, rl, result, node);
74027408
}

src/Liveness.zig

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ tomb_bits: []usize,
2626
/// array. The meaning of the data depends on the AIR tag.
2727
/// * `cond_br` - points to a `CondBr` in `extra` at this index.
2828
/// * `switch_br` - points to a `SwitchBr` in `extra` at this index.
29-
/// * `asm`, `call` - the value is a set of bits which are the extra tomb bits of operands.
29+
/// * `asm`, `call`, `vector_init` - the value is a set of bits which are the extra tomb
30+
/// bits of operands.
3031
/// The main tomb bits are still used and the extra ones are starting with the lsb of the
3132
/// value here.
3233
special: std.AutoHashMapUnmanaged(Air.Inst.Index, u32),
@@ -316,6 +317,7 @@ fn analyzeInst(
316317
.clz,
317318
.ctz,
318319
.popcount,
320+
.splat,
319321
=> {
320322
const o = inst_datas[inst].ty_op;
321323
return trackOperands(a, new_set, inst, main_tomb, .{ o.operand, .none, .none });
@@ -345,7 +347,7 @@ fn analyzeInst(
345347
const callee = inst_data.operand;
346348
const extra = a.air.extraData(Air.Call, inst_data.payload);
347349
const args = @bitCast([]const Air.Inst.Ref, a.air.extra[extra.end..][0..extra.data.args_len]);
348-
if (args.len <= bpi - 2) {
350+
if (args.len + 1 <= bpi - 1) {
349351
var buf = [1]Air.Inst.Ref{.none} ** (bpi - 1);
350352
buf[0] = callee;
351353
std.mem.copy(Air.Inst.Ref, buf[1..], args);
@@ -363,6 +365,28 @@ fn analyzeInst(
363365
}
364366
return extra_tombs.finish();
365367
},
368+
.vector_init => {
369+
const ty_pl = inst_datas[inst].ty_pl;
370+
const vector_ty = a.air.getRefType(ty_pl.ty);
371+
const len = @intCast(u32, vector_ty.arrayLen());
372+
const elements = @bitCast([]const Air.Inst.Ref, a.air.extra[ty_pl.payload..][0..len]);
373+
374+
if (elements.len <= bpi - 1) {
375+
var buf = [1]Air.Inst.Ref{.none} ** (bpi - 1);
376+
std.mem.copy(Air.Inst.Ref, &buf, elements);
377+
return trackOperands(a, new_set, inst, main_tomb, buf);
378+
}
379+
var extra_tombs: ExtraTombs = .{
380+
.analysis = a,
381+
.new_set = new_set,
382+
.inst = inst,
383+
.main_tomb = main_tomb,
384+
};
385+
for (elements) |elem| {
386+
try extra_tombs.feed(elem);
387+
}
388+
return extra_tombs.finish();
389+
},
366390
.struct_field_ptr, .struct_field_val => {
367391
const extra = a.air.extraData(Air.StructField, inst_datas[inst].ty_pl.payload).data;
368392
return trackOperands(a, new_set, inst, main_tomb, .{ extra.struct_operand, .none, .none });

0 commit comments

Comments
 (0)