authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-01-12 23:53:26-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-01-12 23:53:26-07:00
log93b854eb745ab3294054ae71150fe60f134f4d10
treed40e26fcf2524c70c30302f0b503d14a0cdf4e51
parentc4681b4889652d5228a84ac7af5ad5e17ac39055

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 #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`.

19 files changed, 707 insertions(+), 129 deletions(-)

lib/std/testing.zig+5-3
......@@ -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+13-1
......@@ -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+9-3
......@@ -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+26-2
......@@ -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.
3233special: 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 });
src/Sema.zig+249-65
......@@ -379,6 +379,26 @@ pub const Block = struct {
379379 });
380380 }
381381
382 pub fn addVectorInit(
383 block: *Block,
384 vector_ty: Type,
385 elements: []const Air.Inst.Ref,
386 ) !Air.Inst.Ref {
387 const sema = block.sema;
388 const ty_ref = try sema.addType(vector_ty);
389 try sema.air_extra.ensureUnusedCapacity(sema.gpa, elements.len);
390 const extra_index = @intCast(u32, sema.air_extra.items.len);
391 sema.appendRefsAssumeCapacity(elements);
392
393 return block.addInst(.{
394 .tag = .vector_init,
395 .data = .{ .ty_pl = .{
396 .ty = ty_ref,
397 .payload = extra_index,
398 } },
399 });
400 }
401
382402 pub fn addInst(block: *Block, inst: Air.Inst) error{OutOfMemory}!Air.Inst.Ref {
383403 return Air.indexToRef(try block.addInstAsIndex(inst));
384404 }
......@@ -652,8 +672,6 @@ pub fn analyzeBody(
652672 .align_cast => try sema.zirAlignCast(block, inst),
653673 .has_decl => try sema.zirHasDecl(block, inst),
654674 .has_field => try sema.zirHasField(block, inst),
655 .clz => try sema.zirClz(block, inst),
656 .ctz => try sema.zirCtz(block, inst),
657675 .pop_count => try sema.zirPopCount(block, inst),
658676 .byte_swap => try sema.zirByteSwap(block, inst),
659677 .bit_reverse => try sema.zirBitReverse(block, inst),
......@@ -678,6 +696,9 @@ pub fn analyzeBody(
678696 .await_nosuspend => try sema.zirAwait(block, inst, true),
679697 .extended => try sema.zirExtended(block, inst),
680698
699 .clz => try sema.zirClzCtz(block, inst, .clz, Value.clz),
700 .ctz => try sema.zirClzCtz(block, inst, .ctz, Value.ctz),
701
681702 .sqrt => try sema.zirUnaryMath(block, inst),
682703 .sin => try sema.zirUnaryMath(block, inst),
683704 .cos => try sema.zirUnaryMath(block, inst),
......@@ -4643,6 +4664,7 @@ fn zirVectorType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
46434664 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
46444665 const len = try sema.resolveAlreadyCoercedInt(block, len_src, extra.lhs, u32);
46454666 const elem_type = try sema.resolveType(block, elem_type_src, extra.rhs);
4667 try sema.checkVectorElemType(block, elem_type_src, elem_type);
46464668 const vector_type = try Type.Tag.vector.create(sema.arena, .{
46474669 .len = len,
46484670 .elem_type = elem_type,
......@@ -9401,6 +9423,22 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
94019423 }),
94029424 );
94039425 },
9426 .Vector => {
9427 const info = ty.arrayInfo();
9428 const field_values = try sema.arena.alloc(Value, 2);
9429 // len: comptime_int,
9430 field_values[0] = try Value.Tag.int_u64.create(sema.arena, info.len);
9431 // child: type,
9432 field_values[1] = try Value.Tag.ty.create(sema.arena, info.elem_type);
9433
9434 return sema.addConstant(
9435 type_info_ty,
9436 try Value.Tag.@"union".create(sema.arena, .{
9437 .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Vector)),
9438 .val = try Value.Tag.@"struct".create(sema.arena, field_values),
9439 }),
9440 );
9441 },
94049442 .Optional => {
94059443 const field_values = try sema.arena.alloc(Value, 1);
94069444 // child: type,
......@@ -9639,7 +9677,6 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
96399677 .Opaque => return sema.fail(block, src, "TODO: implement zirTypeInfo for Opaque", .{}),
96409678 .Frame => return sema.fail(block, src, "TODO: implement zirTypeInfo for Frame", .{}),
96419679 .AnyFrame => return sema.fail(block, src, "TODO: implement zirTypeInfo for AnyFrame", .{}),
9642 .Vector => return sema.fail(block, src, "TODO: implement zirTypeInfo for Vector", .{}),
96439680 }
96449681}
96459682
......@@ -10945,58 +10982,67 @@ fn zirAlignCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
1094510982 return sema.coerceCompatiblePtrs(block, dest_ty, ptr, ptr_src);
1094610983}
1094710984
10948fn zirClz(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
10985fn zirClzCtz(
10986 sema: *Sema,
10987 block: *Block,
10988 inst: Zir.Inst.Index,
10989 air_tag: Air.Inst.Tag,
10990 comptimeOp: fn (val: Value, ty: Type, target: std.Target) u64,
10991) CompileError!Air.Inst.Ref {
1094910992 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
10950 const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
1095110993 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
1095210994 const operand = sema.resolveInst(inst_data.operand);
1095310995 const operand_ty = sema.typeOf(operand);
10954 // TODO implement support for vectors
10955 if (operand_ty.zigTypeTag() != .Int) {
10956 return sema.fail(block, ty_src, "expected integer type, found '{}'", .{
10957 operand_ty,
10958 });
10959 }
10996 try checkIntOrVector(sema, block, operand, operand_src);
1096010997 const target = sema.mod.getTarget();
1096110998 const bits = operand_ty.intInfo(target).bits;
10962 if (bits == 0) return Air.Inst.Ref.zero;
10963
10964 const result_ty = try Type.smallestUnsignedInt(sema.arena, bits);
10965
10966 const runtime_src = if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |val| {
10967 if (val.isUndef()) return sema.addConstUndef(result_ty);
10968 return sema.addIntUnsigned(result_ty, val.clz(operand_ty, target));
10969 } else operand_src;
10970
10971 try sema.requireRuntimeBlock(block, runtime_src);
10972 return block.addTyOp(.clz, result_ty, operand);
10973}
10974
10975fn zirCtz(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
10976 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
10977 const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
10978 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
10979 const operand = sema.resolveInst(inst_data.operand);
10980 const operand_ty = sema.typeOf(operand);
10981 // TODO implement support for vectors
10982 if (operand_ty.zigTypeTag() != .Int) {
10983 return sema.fail(block, ty_src, "expected integer type, found '{}'", .{
10984 operand_ty,
10985 });
10999 if (bits == 0) {
11000 switch (operand_ty.zigTypeTag()) {
11001 .Vector => return sema.addConstant(
11002 try Type.vector(sema.arena, operand_ty.arrayLen(), Type.comptime_int),
11003 try Value.Tag.repeated.create(sema.arena, Value.zero),
11004 ),
11005 .Int => return Air.Inst.Ref.zero,
11006 else => unreachable,
11007 }
1098611008 }
10987 const target = sema.mod.getTarget();
10988 const bits = operand_ty.intInfo(target).bits;
10989 if (bits == 0) return Air.Inst.Ref.zero;
1099011009
10991 const result_ty = try Type.smallestUnsignedInt(sema.arena, bits);
10992
10993 const runtime_src = if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |val| {
10994 if (val.isUndef()) return sema.addConstUndef(result_ty);
10995 return sema.fail(block, operand_src, "TODO: implement comptime @ctz", .{});
10996 } else operand_src;
10997
10998 try sema.requireRuntimeBlock(block, runtime_src);
10999 return block.addTyOp(.ctz, result_ty, operand);
11010 const result_scalar_ty = try Type.smallestUnsignedInt(sema.arena, bits);
11011 switch (operand_ty.zigTypeTag()) {
11012 .Vector => {
11013 const vec_len = operand_ty.arrayLen();
11014 const result_ty = try Type.vector(sema.arena, vec_len, result_scalar_ty);
11015 if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |val| {
11016 if (val.isUndef()) return sema.addConstUndef(result_ty);
11017
11018 var elem_buf: Value.ElemValueBuffer = undefined;
11019 const elems = try sema.arena.alloc(Value, vec_len);
11020 const scalar_ty = operand_ty.scalarType();
11021 for (elems) |*elem, i| {
11022 const elem_val = val.elemValueBuffer(i, &elem_buf);
11023 const count = comptimeOp(elem_val, scalar_ty, target);
11024 elem.* = try Value.Tag.int_u64.create(sema.arena, count);
11025 }
11026 return sema.addConstant(
11027 result_ty,
11028 try Value.Tag.array.create(sema.arena, elems),
11029 );
11030 } else {
11031 try sema.requireRuntimeBlock(block, operand_src);
11032 return block.addTyOp(air_tag, result_ty, operand);
11033 }
11034 },
11035 .Int => {
11036 if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |val| {
11037 if (val.isUndef()) return sema.addConstUndef(result_scalar_ty);
11038 return sema.addIntUnsigned(result_scalar_ty, comptimeOp(val, operand_ty, target));
11039 } else {
11040 try sema.requireRuntimeBlock(block, operand_src);
11041 return block.addTyOp(air_tag, result_scalar_ty, operand);
11042 }
11043 },
11044 else => unreachable,
11045 }
1100011046}
1100111047
1100211048fn zirPopCount(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
......@@ -11126,6 +11172,19 @@ fn checkPtrType(
1112611172 }
1112711173}
1112811174
11175fn checkVectorElemType(
11176 sema: *Sema,
11177 block: *Block,
11178 ty_src: LazySrcLoc,
11179 ty: Type,
11180) CompileError!void {
11181 switch (ty.zigTypeTag()) {
11182 .Int, .Float, .Bool => return,
11183 else => if (ty.isPtrAtRuntime()) return,
11184 }
11185 return sema.fail(block, ty_src, "expected integer, float, bool, or pointer for the vector element type; found '{}'", .{ty});
11186}
11187
1112911188fn checkFloatType(
1113011189 sema: *Sema,
1113111190 block: *Block,
......@@ -11243,6 +11302,22 @@ fn checkComptimeVarStore(
1124311302 }
1124411303}
1124511304
11305fn checkIntOrVector(
11306 sema: *Sema,
11307 block: *Block,
11308 operand: Air.Inst.Ref,
11309 operand_src: LazySrcLoc,
11310) CompileError!void {
11311 const operand_ty = sema.typeOf(operand);
11312 const operand_zig_ty_tag = try operand_ty.zigTypeTagOrPoison();
11313 switch (operand_zig_ty_tag) {
11314 .Vector, .Int => return,
11315 else => return sema.fail(block, operand_src, "expected integer or vector, found '{}'", .{
11316 operand_ty,
11317 }),
11318 }
11319}
11320
1124611321const SimdBinOp = struct {
1124711322 len: ?usize,
1124811323 /// Coerced to `result_ty`.
......@@ -11464,8 +11539,28 @@ fn zirCmpxchg(
1146411539
1146511540fn zirSplat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
1146611541 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
11467 const src = inst_data.src();
11468 return sema.fail(block, src, "TODO: Sema.zirSplat", .{});
11542 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
11543 const len_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
11544 const scalar_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
11545 const len = @intCast(u32, try sema.resolveInt(block, len_src, extra.lhs, Type.u32));
11546 const scalar = sema.resolveInst(extra.rhs);
11547 const scalar_ty = sema.typeOf(scalar);
11548 try sema.checkVectorElemType(block, scalar_src, scalar_ty);
11549 const vector_ty = try Type.Tag.vector.create(sema.arena, .{
11550 .len = len,
11551 .elem_type = scalar_ty,
11552 });
11553 if (try sema.resolveMaybeUndefVal(block, scalar_src, scalar)) |scalar_val| {
11554 if (scalar_val.isUndef()) return sema.addConstUndef(vector_ty);
11555
11556 return sema.addConstant(
11557 vector_ty,
11558 try Value.Tag.repeated.create(sema.arena, scalar_val),
11559 );
11560 }
11561
11562 try sema.requireRuntimeBlock(block, scalar_src);
11563 return block.addTyOp(.splat, vector_ty, scalar);
1146911564}
1147011565
1147111566fn zirReduce(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
......@@ -13138,6 +13233,8 @@ fn elemVal(
1313813233 return sema.fail(block, src, "array access of non-indexable type '{}'", .{array_ty});
1313913234 }
1314013235
13236 // TODO in case of a vector of pointers, we need to detect whether the element
13237 // index is a scalar or vector instead of unconditionally casting to usize.
1314113238 const elem_index = try sema.coerce(block, Type.usize, elem_index_uncasted, elem_index_src);
1314213239
1314313240 switch (array_ty.zigTypeTag()) {
......@@ -13178,25 +13275,38 @@ fn elemVal(
1317813275 return sema.analyzeLoad(block, array_src, elem_ptr, elem_index_src);
1317913276 },
1318013277 },
13181 .Array => {
13182 if (try sema.resolveMaybeUndefVal(block, array_src, array)) |array_val| {
13183 const elem_ty = array_ty.childType();
13184 if (array_val.isUndef()) return sema.addConstUndef(elem_ty);
13185 const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);
13186 if (maybe_index_val) |index_val| {
13187 const index = @intCast(usize, index_val.toUnsignedInt());
13188 const elem_val = try array_val.elemValue(sema.arena, index);
13189 return sema.addConstant(elem_ty, elem_val);
13190 }
13191 }
13192 try sema.requireRuntimeBlock(block, array_src);
13193 return block.addBinOp(.array_elem_val, array, elem_index);
13278 .Array => return elemValArray(sema, block, array, elem_index, array_src, elem_index_src),
13279 .Vector => {
13280 // TODO: If the index is a vector, the result should be a vector.
13281 return elemValArray(sema, block, array, elem_index, array_src, elem_index_src);
1319413282 },
13195 .Vector => return sema.fail(block, array_src, "TODO implement Sema for elemVal for vector", .{}),
1319613283 else => unreachable,
1319713284 }
1319813285}
1319913286
13287fn elemValArray(
13288 sema: *Sema,
13289 block: *Block,
13290 array: Air.Inst.Ref,
13291 elem_index: Air.Inst.Ref,
13292 array_src: LazySrcLoc,
13293 elem_index_src: LazySrcLoc,
13294) CompileError!Air.Inst.Ref {
13295 const array_ty = sema.typeOf(array);
13296 if (try sema.resolveMaybeUndefVal(block, array_src, array)) |array_val| {
13297 const elem_ty = array_ty.childType();
13298 if (array_val.isUndef()) return sema.addConstUndef(elem_ty);
13299 const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);
13300 if (maybe_index_val) |index_val| {
13301 const index = @intCast(usize, index_val.toUnsignedInt());
13302 const elem_val = try array_val.elemValue(sema.arena, index);
13303 return sema.addConstant(elem_ty, elem_val);
13304 }
13305 }
13306 try sema.requireRuntimeBlock(block, array_src);
13307 return block.addBinOp(.array_elem_val, array, elem_index);
13308}
13309
1320013310fn elemPtrArray(
1320113311 sema: *Sema,
1320213312 block: *Block,
......@@ -13530,6 +13640,7 @@ fn coerce(
1353013640 },
1353113641 .Vector => switch (inst_ty.zigTypeTag()) {
1353213642 .Array => return sema.coerceVectorInMemory(block, dest_ty, dest_ty_src, inst, inst_src),
13643 .Vector => return sema.coerceVectors(block, dest_ty, dest_ty_src, inst, inst_src),
1353313644 else => {},
1353413645 },
1353513646 else => {},
......@@ -14410,8 +14521,9 @@ fn coerceEnumToUnion(
1441014521 return sema.failWithOwnedErrorMsg(msg);
1441114522}
1441214523
14413// Coerces vectors/arrays which have the same in-memory layout. This can be used for
14414// both coercing from and to vectors.
14524/// Coerces vectors/arrays which have the same in-memory layout. This can be used for
14525/// both coercing from and to vectors.
14526/// TODO (affects the lang spec) delete this in favor of always using `coerceVectors`.
1441514527fn coerceVectorInMemory(
1441614528 sema: *Sema,
1441714529 block: *Block,
......@@ -14455,6 +14567,78 @@ fn coerceVectorInMemory(
1445514567 return block.addBitCast(dest_ty, inst);
1445614568}
1445714569
14570/// If the lengths match, coerces element-wise.
14571fn coerceVectors(
14572 sema: *Sema,
14573 block: *Block,
14574 dest_ty: Type,
14575 dest_ty_src: LazySrcLoc,
14576 inst: Air.Inst.Ref,
14577 inst_src: LazySrcLoc,
14578) !Air.Inst.Ref {
14579 const inst_ty = sema.typeOf(inst);
14580 const inst_len = inst_ty.arrayLen();
14581 const dest_len = dest_ty.arrayLen();
14582
14583 if (dest_len != inst_len) {
14584 const msg = msg: {
14585 const msg = try sema.errMsg(block, inst_src, "expected {}, found {}", .{
14586 dest_ty, inst_ty,
14587 });
14588 errdefer msg.destroy(sema.gpa);
14589 try sema.errNote(block, dest_ty_src, msg, "destination has length {d}", .{dest_len});
14590 try sema.errNote(block, inst_src, msg, "source has length {d}", .{inst_len});
14591 break :msg msg;
14592 };
14593 return sema.failWithOwnedErrorMsg(msg);
14594 }
14595
14596 const target = sema.mod.getTarget();
14597 const dest_elem_ty = dest_ty.childType();
14598 const inst_elem_ty = inst_ty.childType();
14599 const in_memory_result = try sema.coerceInMemoryAllowed(block, dest_elem_ty, inst_elem_ty, false, target, dest_ty_src, inst_src);
14600 if (in_memory_result == .ok) {
14601 if (try sema.resolveMaybeUndefVal(block, inst_src, inst)) |inst_val| {
14602 // These types share the same comptime value representation.
14603 return sema.addConstant(dest_ty, inst_val);
14604 }
14605 try sema.requireRuntimeBlock(block, inst_src);
14606 return block.addBitCast(dest_ty, inst);
14607 }
14608
14609 const element_vals = try sema.arena.alloc(Value, dest_len);
14610 const element_refs = try sema.arena.alloc(Air.Inst.Ref, dest_len);
14611 var runtime_src: ?LazySrcLoc = null;
14612
14613 for (element_vals) |*elem, i| {
14614 const index_ref = try sema.addConstant(
14615 Type.usize,
14616 try Value.Tag.int_u64.create(sema.arena, i),
14617 );
14618 const elem_src = inst_src; // TODO better source location
14619 const elem_ref = try elemValArray(sema, block, inst, index_ref, inst_src, elem_src);
14620 const coerced = try sema.coerce(block, dest_elem_ty, elem_ref, elem_src);
14621 element_refs[i] = coerced;
14622 if (runtime_src == null) {
14623 if (try sema.resolveMaybeUndefVal(block, elem_src, coerced)) |elem_val| {
14624 elem.* = elem_val;
14625 } else {
14626 runtime_src = elem_src;
14627 }
14628 }
14629 }
14630
14631 if (runtime_src) |rs| {
14632 try sema.requireRuntimeBlock(block, rs);
14633 return block.addVectorInit(dest_ty, element_refs);
14634 }
14635
14636 return sema.addConstant(
14637 dest_ty,
14638 try Value.Tag.array.create(sema.arena, element_vals),
14639 );
14640}
14641
1445814642fn analyzeDeclVal(
1445914643 sema: *Sema,
1446014644 block: *Block,
src/arch/aarch64/CodeGen.zig+31-1
......@@ -593,6 +593,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
593593 .popcount => try self.airPopcount(inst),
594594 .tag_name => try self.airTagName(inst),
595595 .error_name => try self.airErrorName(inst),
596 .splat => try self.airSplat(inst),
597 .vector_init => try self.airVectorInit(inst),
596598
597599 .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),
598600 .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),
......@@ -1648,7 +1650,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {
16481650 break :result info.return_value;
16491651 };
16501652
1651 if (args.len <= Liveness.bpi - 2) {
1653 if (args.len + 1 <= Liveness.bpi - 1) {
16521654 var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1);
16531655 buf[0] = callee;
16541656 std.mem.copy(Air.Inst.Ref, buf[1..], args);
......@@ -2567,6 +2569,34 @@ fn airErrorName(self: *Self, inst: Air.Inst.Index) !void {
25672569 return self.finishAir(inst, result, .{ un_op, .none, .none });
25682570}
25692571
2572fn airSplat(self: *Self, inst: Air.Inst.Index) !void {
2573 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2574 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airSplat for {}", .{self.target.cpu.arch});
2575 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
2576}
2577
2578fn airVectorInit(self: *Self, inst: Air.Inst.Index) !void {
2579 const vector_ty = self.air.typeOfIndex(inst);
2580 const len = @intCast(u32, vector_ty.arrayLen());
2581 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
2582 const elements = @bitCast([]const Air.Inst.Ref, self.air.extra[ty_pl.payload..][0..len]);
2583 const result: MCValue = res: {
2584 if (self.liveness.isUnused(inst)) break :res MCValue.dead;
2585 return self.fail("TODO implement airVectorInit for {}", .{self.target.cpu.arch});
2586 };
2587
2588 if (elements.len <= Liveness.bpi - 1) {
2589 var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1);
2590 std.mem.copy(Air.Inst.Ref, &buf, elements);
2591 return self.finishAir(inst, result, buf);
2592 }
2593 var bt = try self.iterateBigTomb(inst, elements.len);
2594 for (elements) |elem| {
2595 bt.feed(elem);
2596 }
2597 return bt.finishAir(result);
2598}
2599
25702600fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {
25712601 // First section of indexes correspond to a set number of constant values.
25722602 const ref_int = @enumToInt(inst);
src/arch/arm/CodeGen.zig+30
......@@ -584,6 +584,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
584584 .popcount => try self.airPopcount(inst),
585585 .tag_name => try self.airTagName(inst),
586586 .error_name => try self.airErrorName(inst),
587 .splat => try self.airSplat(inst),
588 .vector_init => try self.airVectorInit(inst),
587589
588590 .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),
589591 .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),
......@@ -3665,6 +3667,34 @@ fn airErrorName(self: *Self, inst: Air.Inst.Index) !void {
36653667 return self.finishAir(inst, result, .{ un_op, .none, .none });
36663668}
36673669
3670fn airSplat(self: *Self, inst: Air.Inst.Index) !void {
3671 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
3672 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airSplat for arm", .{});
3673 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
3674}
3675
3676fn airVectorInit(self: *Self, inst: Air.Inst.Index) !void {
3677 const vector_ty = self.air.typeOfIndex(inst);
3678 const len = @intCast(u32, vector_ty.arrayLen());
3679 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
3680 const elements = @bitCast([]const Air.Inst.Ref, self.air.extra[ty_pl.payload..][0..len]);
3681 const result: MCValue = res: {
3682 if (self.liveness.isUnused(inst)) break :res MCValue.dead;
3683 return self.fail("TODO implement airVectorInit for arm", .{});
3684 };
3685
3686 if (elements.len <= Liveness.bpi - 1) {
3687 var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1);
3688 std.mem.copy(Air.Inst.Ref, &buf, elements);
3689 return self.finishAir(inst, result, buf);
3690 }
3691 var bt = try self.iterateBigTomb(inst, elements.len);
3692 for (elements) |elem| {
3693 bt.feed(elem);
3694 }
3695 return bt.finishAir(result);
3696}
3697
36683698fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {
36693699 // First section of indexes correspond to a set number of constant values.
36703700 const ref_int = @enumToInt(inst);
src/arch/riscv64/CodeGen.zig+30
......@@ -572,6 +572,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
572572 .popcount => try self.airPopcount(inst),
573573 .tag_name => try self.airTagName(inst),
574574 .error_name => try self.airErrorName(inst),
575 .splat => try self.airSplat(inst),
576 .vector_init => try self.airVectorInit(inst),
575577
576578 .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),
577579 .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),
......@@ -2066,6 +2068,34 @@ fn airErrorName(self: *Self, inst: Air.Inst.Index) !void {
20662068 return self.finishAir(inst, result, .{ un_op, .none, .none });
20672069}
20682070
2071fn airSplat(self: *Self, inst: Air.Inst.Index) !void {
2072 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2073 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airSplat for riscv64", .{});
2074 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
2075}
2076
2077fn airVectorInit(self: *Self, inst: Air.Inst.Index) !void {
2078 const vector_ty = self.air.typeOfIndex(inst);
2079 const len = @intCast(u32, vector_ty.arrayLen());
2080 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
2081 const elements = @bitCast([]const Air.Inst.Ref, self.air.extra[ty_pl.payload..][0..len]);
2082 const result: MCValue = res: {
2083 if (self.liveness.isUnused(inst)) break :res MCValue.dead;
2084 return self.fail("TODO implement airVectorInit for riscv64", .{});
2085 };
2086
2087 if (elements.len <= Liveness.bpi - 1) {
2088 var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1);
2089 std.mem.copy(Air.Inst.Ref, &buf, elements);
2090 return self.finishAir(inst, result, buf);
2091 }
2092 var bt = try self.iterateBigTomb(inst, elements.len);
2093 for (elements) |elem| {
2094 bt.feed(elem);
2095 }
2096 return bt.finishAir(result);
2097}
2098
20692099fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {
20702100 // First section of indexes correspond to a set number of constant values.
20712101 const ref_int = @enumToInt(inst);
src/arch/wasm/CodeGen.zig+23
......@@ -3224,6 +3224,29 @@ fn airFloatToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
32243224 return result;
32253225}
32263226
3227fn airSplat(self: *Self, inst: Air.Inst.Index) !void {
3228 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
3229
3230 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
3231 const operand = self.resolveInst(ty_op.operand);
3232
3233 _ = ty_op;
3234 _ = operand;
3235 return self.fail("TODO: Implement wasm airSplat", .{});
3236}
3237
3238fn airVectorInit(self: *Self, inst: Air.Inst.Index) !void {
3239 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
3240
3241 const vector_ty = self.air.typeOfIndex(inst);
3242 const len = @intCast(u32, vector_ty.arrayLen());
3243 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
3244 const elements = @bitCast([]const Air.Inst.Ref, self.air.extra[ty_pl.payload..][0..len]);
3245
3246 _ = elements;
3247 return self.fail("TODO: Wasm backend: implement airVectorInit", .{});
3248}
3249
32273250fn cmpOptionals(self: *Self, lhs: WValue, rhs: WValue, operand_ty: Type, op: std.math.CompareOperator) InnerError!WValue {
32283251 assert(operand_ty.hasCodeGenBits());
32293252 assert(op == .eq or op == .neq);
src/arch/x86_64/CodeGen.zig+31-1
......@@ -635,7 +635,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
635635 .ctz => try self.airCtz(inst),
636636 .popcount => try self.airPopcount(inst),
637637 .tag_name => try self.airTagName(inst),
638 .error_name, => try self.airErrorName(inst),
638 .error_name => try self.airErrorName(inst),
639 .splat => try self.airSplat(inst),
640 .vector_init => try self.airVectorInit(inst),
639641
640642 .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),
641643 .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),
......@@ -3659,6 +3661,34 @@ fn airErrorName(self: *Self, inst: Air.Inst.Index) !void {
36593661 return self.finishAir(inst, result, .{ un_op, .none, .none });
36603662}
36613663
3664fn airSplat(self: *Self, inst: Air.Inst.Index) !void {
3665 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
3666 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airSplat for x86_64", .{});
3667 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
3668}
3669
3670fn airVectorInit(self: *Self, inst: Air.Inst.Index) !void {
3671 const vector_ty = self.air.typeOfIndex(inst);
3672 const len = @intCast(u32, vector_ty.arrayLen());
3673 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
3674 const elements = @bitCast([]const Air.Inst.Ref, self.air.extra[ty_pl.payload..][0..len]);
3675 const result: MCValue = res: {
3676 if (self.liveness.isUnused(inst)) break :res MCValue.dead;
3677 return self.fail("TODO implement airVectorInit for x86_64", .{});
3678 };
3679
3680 if (elements.len <= Liveness.bpi - 1) {
3681 var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1);
3682 std.mem.copy(Air.Inst.Ref, &buf, elements);
3683 return self.finishAir(inst, result, buf);
3684 }
3685 var bt = try self.iterateBigTomb(inst, elements.len);
3686 for (elements) |elem| {
3687 bt.feed(elem);
3688 }
3689 return bt.finishAir(result);
3690}
3691
36623692fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {
36633693 // First section of indexes correspond to a set number of constant values.
36643694 const ref_int = @enumToInt(inst);
src/codegen/c.zig+35
......@@ -1245,6 +1245,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
12451245 .popcount => try airBuiltinCall(f, inst, "popcount"),
12461246 .tag_name => try airTagName(f, inst),
12471247 .error_name => try airErrorName(f, inst),
1248 .splat => try airSplat(f, inst),
1249 .vector_init => try airVectorInit(f, inst),
12481250
12491251 .int_to_float,
12501252 .float_to_int,
......@@ -3015,6 +3017,39 @@ fn airErrorName(f: *Function, inst: Air.Inst.Index) !CValue {
30153017 return f.fail("TODO: C backend: implement airErrorName", .{});
30163018}
30173019
3020fn airSplat(f: *Function, inst: Air.Inst.Index) !CValue {
3021 if (f.liveness.isUnused(inst)) return CValue.none;
3022
3023 const inst_ty = f.air.typeOfIndex(inst);
3024 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
3025 const operand = try f.resolveInst(ty_op.operand);
3026 const writer = f.object.writer();
3027 const local = try f.allocLocal(inst_ty, .Const);
3028 try writer.writeAll(" = ");
3029
3030 _ = operand;
3031 _ = local;
3032 return f.fail("TODO: C backend: implement airSplat", .{});
3033}
3034
3035fn airVectorInit(f: *Function, inst: Air.Inst.Index) !CValue {
3036 if (f.liveness.isUnused(inst)) return CValue.none;
3037
3038 const inst_ty = f.air.typeOfIndex(inst);
3039 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
3040 const vector_ty = f.air.getRefType(ty_pl.ty);
3041 const len = @intCast(u32, vector_ty.arrayLen());
3042 const elements = @bitCast([]const Air.Inst.Ref, f.air.extra[ty_pl.payload..][0..len]);
3043
3044 const writer = f.object.writer();
3045 const local = try f.allocLocal(inst_ty, .Const);
3046 try writer.writeAll(" = ");
3047
3048 _ = elements;
3049 _ = local;
3050 return f.fail("TODO: C backend: implement airVectorInit", .{});
3051}
3052
30183053fn toMemoryOrder(order: std.builtin.AtomicOrder) [:0]const u8 {
30193054 return switch (order) {
30203055 .Unordered => "memory_order_relaxed",
src/codegen/llvm.zig+63-11
......@@ -2089,6 +2089,8 @@ pub const FuncGen = struct {
20892089 .popcount => try self.airPopCount(inst, "ctpop"),
20902090 .tag_name => try self.airTagName(inst),
20912091 .error_name => try self.airErrorName(inst),
2092 .splat => try self.airSplat(inst),
2093 .vector_init => try self.airVectorInit(inst),
20922094
20932095 .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),
20942096 .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),
......@@ -2612,15 +2614,19 @@ pub const FuncGen = struct {
26122614 const array_ty = self.air.typeOf(bin_op.lhs);
26132615 const array_llvm_val = try self.resolveInst(bin_op.lhs);
26142616 const rhs = try self.resolveInst(bin_op.rhs);
2615 assert(isByRef(array_ty));
2616 const indices: [2]*const llvm.Value = .{ self.context.intType(32).constNull(), rhs };
2617 const elem_ptr = self.builder.buildInBoundsGEP(array_llvm_val, &indices, indices.len, "");
2618 const elem_ty = array_ty.childType();
2619 if (isByRef(elem_ty)) {
2620 return elem_ptr;
2621 } else {
2622 return self.builder.buildLoad(elem_ptr, "");
2617 if (isByRef(array_ty)) {
2618 const indices: [2]*const llvm.Value = .{ self.context.intType(32).constNull(), rhs };
2619 const elem_ptr = self.builder.buildInBoundsGEP(array_llvm_val, &indices, indices.len, "");
2620 const elem_ty = array_ty.childType();
2621 if (isByRef(elem_ty)) {
2622 return elem_ptr;
2623 } else {
2624 return self.builder.buildLoad(elem_ptr, "");
2625 }
26232626 }
2627
2628 // This branch can be reached for vectors, which are always by-value.
2629 return self.builder.buildExtractElement(array_llvm_val, rhs, "");
26242630 }
26252631
26262632 fn airPtrElemVal(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
......@@ -4163,11 +4169,20 @@ pub const FuncGen = struct {
41634169 const operand = try self.resolveInst(ty_op.operand);
41644170 const target = self.dg.module.getTarget();
41654171 const bits = operand_ty.intInfo(target).bits;
4172 const vec_len: ?u32 = switch (operand_ty.zigTypeTag()) {
4173 .Vector => @intCast(u32, operand_ty.arrayLen()),
4174 else => null,
4175 };
41664176
41674177 var fn_name_buf: [100]u8 = undefined;
4168 const llvm_fn_name = std.fmt.bufPrintZ(&fn_name_buf, "llvm.{s}.i{d}", .{
4169 prefix, bits,
4170 }) catch unreachable;
4178 const llvm_fn_name = if (vec_len) |len|
4179 std.fmt.bufPrintZ(&fn_name_buf, "llvm.{s}.v{d}i{d}", .{
4180 prefix, len, bits,
4181 }) catch unreachable
4182 else
4183 std.fmt.bufPrintZ(&fn_name_buf, "llvm.{s}.i{d}", .{
4184 prefix, bits,
4185 }) catch unreachable;
41714186 const llvm_i1 = self.context.intType(1);
41724187 const fn_val = self.dg.object.llvm_module.getNamedFunction(llvm_fn_name) orelse blk: {
41734188 const operand_llvm_ty = try self.dg.llvmType(operand_ty);
......@@ -4350,6 +4365,43 @@ pub const FuncGen = struct {
43504365 return self.builder.buildLoad(error_name_ptr, "");
43514366 }
43524367
4368 fn airSplat(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
4369 if (self.liveness.isUnused(inst)) return null;
4370
4371 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
4372 const scalar = try self.resolveInst(ty_op.operand);
4373 const scalar_ty = self.air.typeOf(ty_op.operand);
4374 const vector_ty = self.air.typeOfIndex(inst);
4375 const len = @intCast(u32, vector_ty.arrayLen());
4376 const scalar_llvm_ty = try self.dg.llvmType(scalar_ty);
4377 const op_llvm_ty = scalar_llvm_ty.vectorType(1);
4378 const u32_llvm_ty = self.context.intType(32);
4379 const mask_llvm_ty = u32_llvm_ty.vectorType(len);
4380 const undef_vector = op_llvm_ty.getUndef();
4381 const u32_zero = u32_llvm_ty.constNull();
4382 const op_vector = self.builder.buildInsertElement(undef_vector, scalar, u32_zero, "");
4383 return self.builder.buildShuffleVector(op_vector, undef_vector, mask_llvm_ty.constNull(), "");
4384 }
4385
4386 fn airVectorInit(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
4387 if (self.liveness.isUnused(inst)) return null;
4388
4389 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
4390 const vector_ty = self.air.typeOfIndex(inst);
4391 const len = vector_ty.arrayLen();
4392 const elements = @bitCast([]const Air.Inst.Ref, self.air.extra[ty_pl.payload..][0..len]);
4393 const llvm_vector_ty = try self.dg.llvmType(vector_ty);
4394 const llvm_u32 = self.context.intType(32);
4395
4396 var vector = llvm_vector_ty.getUndef();
4397 for (elements) |elem, i| {
4398 const index_u32 = llvm_u32.constInt(i, .False);
4399 const llvm_elem = try self.resolveInst(elem);
4400 vector = self.builder.buildInsertElement(vector, llvm_elem, index_u32, "");
4401 }
4402 return vector;
4403 }
4404
43534405 fn getErrorNameTable(self: *FuncGen) !*const llvm.Value {
43544406 if (self.dg.object.error_name_table) |table| {
43554407 return table;
src/codegen/llvm/bindings.zig+3
......@@ -820,6 +820,9 @@ pub const Builder = opaque {
820820
821821 pub const setCurrentDebugLocation2 = LLVMSetCurrentDebugLocation2;
822822 extern fn LLVMSetCurrentDebugLocation2(Builder: *const Builder, Loc: *Metadata) void;
823
824 pub const buildShuffleVector = LLVMBuildShuffleVector;
825 extern fn LLVMBuildShuffleVector(*const Builder, V1: *const Value, V2: *const Value, Mask: *const Value, Name: [*:0]const u8) *const Value;
823826};
824827
825828pub const DIScope = opaque {};
src/print_air.zig+16
......@@ -196,6 +196,7 @@ const Writer = struct {
196196 .struct_field_ptr_index_3,
197197 .array_to_slice,
198198 .int_to_float,
199 .splat,
199200 .float_to_int,
200201 .get_union_tag,
201202 .clz,
......@@ -218,6 +219,7 @@ const Writer = struct {
218219 .assembly => try w.writeAssembly(s, inst),
219220 .dbg_stmt => try w.writeDbgStmt(s, inst),
220221 .call => try w.writeCall(s, inst),
222 .vector_init => try w.writeVectorInit(s, inst),
221223 .br => try w.writeBr(s, inst),
222224 .cond_br => try w.writeCondBr(s, inst),
223225 .switch_br => try w.writeSwitchBr(s, inst),
......@@ -290,6 +292,20 @@ const Writer = struct {
290292 try s.writeAll("}");
291293 }
292294
295 fn writeVectorInit(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
296 const ty_pl = w.air.instructions.items(.data)[inst].ty_pl;
297 const vector_ty = w.air.getRefType(ty_pl.ty);
298 const len = @intCast(u32, vector_ty.arrayLen());
299 const elements = @bitCast([]const Air.Inst.Ref, w.air.extra[ty_pl.payload..][0..len]);
300
301 try s.print("{}, [", .{vector_ty});
302 for (elements) |elem, i| {
303 if (i != 0) try s.writeAll(", ");
304 try w.writeOperand(s, inst, i, elem);
305 }
306 try s.writeAll("]");
307 }
308
293309 fn writeStructField(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
294310 const ty_pl = w.air.instructions.items(.data)[inst].ty_pl;
295311 const extra = w.air.extraData(Air.StructField, ty_pl.payload).data;
src/type.zig+4-1
......@@ -3080,7 +3080,7 @@ pub const Type = extern union {
30803080 };
30813081 }
30823082
3083 /// Asserts the type is an integer, enum, or error set.
3083 /// Asserts the type is an integer, enum, error set, or vector of one of them.
30843084 pub fn intInfo(self: Type, target: Target) struct { signedness: std.builtin.Signedness, bits: u16 } {
30853085 var ty = self;
30863086 while (true) switch (ty.tag()) {
......@@ -3128,6 +3128,8 @@ pub const Type = extern union {
31283128 return .{ .signedness = .unsigned, .bits = 16 };
31293129 },
31303130
3131 .vector => ty = ty.castTag(.vector).?.data.elem_type,
3132
31313133 else => unreachable,
31323134 };
31333135 }
......@@ -4501,6 +4503,7 @@ pub const Type = extern union {
45014503 };
45024504
45034505 pub const @"u8" = initTag(.u8);
4506 pub const @"u32" = initTag(.u32);
45044507 pub const @"bool" = initTag(.bool);
45054508 pub const @"usize" = initTag(.usize);
45064509 pub const @"isize" = initTag(.isize);
src/value.zig+65
......@@ -1172,6 +1172,44 @@ pub const Value = extern union {
11721172 }
11731173 }
11741174
1175 pub fn ctz(val: Value, ty: Type, target: Target) u64 {
1176 const ty_bits = ty.intInfo(target).bits;
1177 switch (val.tag()) {
1178 .zero, .bool_false => return ty_bits,
1179 .one, .bool_true => return 0,
1180
1181 .int_u64 => {
1182 const big = @ctz(u64, val.castTag(.int_u64).?.data);
1183 return if (big == 64) ty_bits else big;
1184 },
1185 .int_i64 => {
1186 @panic("TODO implement i64 Value ctz");
1187 },
1188 .int_big_positive => {
1189 // TODO: move this code into std lib big ints
1190 const bigint = val.castTag(.int_big_positive).?.asBigInt();
1191 // Limbs are stored in little-endian order.
1192 var result: u64 = 0;
1193 for (bigint.limbs) |limb| {
1194 const limb_tz = @ctz(std.math.big.Limb, limb);
1195 result += limb_tz;
1196 if (limb_tz != @sizeOf(std.math.big.Limb) * 8) break;
1197 }
1198 return result;
1199 },
1200 .int_big_negative => {
1201 @panic("TODO implement int_big_negative Value ctz");
1202 },
1203
1204 .the_only_possible_value => {
1205 assert(ty_bits == 0);
1206 return ty_bits;
1207 },
1208
1209 else => unreachable,
1210 }
1211 }
1212
11751213 /// Asserts the value is an integer and not undefined.
11761214 /// Returns the number of bits the value requires to represent stored in twos complement form.
11771215 pub fn intBitCountTwosComp(self: Value) usize {
......@@ -1455,6 +1493,20 @@ pub const Value = extern union {
14551493 .field_ptr => @panic("TODO: Implement more pointer eql cases"),
14561494 .eu_payload_ptr => @panic("TODO: Implement more pointer eql cases"),
14571495 .opt_payload_ptr => @panic("TODO: Implement more pointer eql cases"),
1496 .array => {
1497 const a_array = a.castTag(.array).?.data;
1498 const b_array = b.castTag(.array).?.data;
1499
1500 if (a_array.len != b_array.len) return false;
1501
1502 const elem_ty = ty.childType();
1503 for (a_array) |a_elem, i| {
1504 const b_elem = b_array[i];
1505
1506 if (!eql(a_elem, b_elem, elem_ty)) return false;
1507 }
1508 return true;
1509 },
14581510 else => {},
14591511 }
14601512 } else if (a_tag == .null_value or b_tag == .null_value) {
......@@ -1488,6 +1540,19 @@ pub const Value = extern union {
14881540 const int_ty = ty.intTagType(&buf_ty);
14891541 return eql(a_val, b_val, int_ty);
14901542 },
1543 .Array, .Vector => {
1544 const len = ty.arrayLen();
1545 const elem_ty = ty.childType();
1546 var i: usize = 0;
1547 var a_buf: ElemValueBuffer = undefined;
1548 var b_buf: ElemValueBuffer = undefined;
1549 while (i < len) : (i += 1) {
1550 const a_elem = elemValueBuffer(a, i, &a_buf);
1551 const b_elem = elemValueBuffer(b, i, &b_buf);
1552 if (!eql(a_elem, b_elem, elem_ty)) return false;
1553 }
1554 return true;
1555 },
14911556 else => return order(a, b).compare(.eq),
14921557 }
14931558 }
test/behavior/math.zig+73
......@@ -72,6 +72,79 @@ fn testOneClz(comptime T: type, x: T) u32 {
7272 return @clz(T, x);
7373}
7474
75test "@clz vectors" {
76 try testClzVectors();
77 comptime try testClzVectors();
78}
79
80fn testClzVectors() !void {
81 @setEvalBranchQuota(10_000);
82 try testOneClzVector(u8, 64, @splat(64, @as(u8, 0b10001010)), @splat(64, @as(u4, 0)));
83 try testOneClzVector(u8, 64, @splat(64, @as(u8, 0b00001010)), @splat(64, @as(u4, 4)));
84 try testOneClzVector(u8, 64, @splat(64, @as(u8, 0b00011010)), @splat(64, @as(u4, 3)));
85 try testOneClzVector(u8, 64, @splat(64, @as(u8, 0b00000000)), @splat(64, @as(u4, 8)));
86 try testOneClzVector(u128, 64, @splat(64, @as(u128, 0xffffffffffffffff)), @splat(64, @as(u8, 64)));
87 try testOneClzVector(u128, 64, @splat(64, @as(u128, 0x10000000000000000)), @splat(64, @as(u8, 63)));
88}
89
90fn testOneClzVector(
91 comptime T: type,
92 comptime len: u32,
93 x: @Vector(len, T),
94 expected: @Vector(len, u32),
95) !void {
96 try expectVectorsEqual(@clz(T, x), expected);
97}
98
99fn expectVectorsEqual(a: anytype, b: anytype) !void {
100 const len_a = @typeInfo(@TypeOf(a)).Vector.len;
101 const len_b = @typeInfo(@TypeOf(b)).Vector.len;
102 try expect(len_a == len_b);
103
104 var i: usize = 0;
105 while (i < len_a) : (i += 1) {
106 try expect(a[i] == b[i]);
107 }
108}
109
110test "@ctz" {
111 try testCtz();
112 comptime try testCtz();
113}
114
115fn testCtz() !void {
116 try expect(testOneCtz(u8, 0b10100000) == 5);
117 try expect(testOneCtz(u8, 0b10001010) == 1);
118 try expect(testOneCtz(u8, 0b00000000) == 8);
119 try expect(testOneCtz(u16, 0b00000000) == 16);
120}
121
122fn testOneCtz(comptime T: type, x: T) u32 {
123 return @ctz(T, x);
124}
125
126test "@ctz vectors" {
127 try testCtzVectors();
128 comptime try testCtzVectors();
129}
130
131fn testCtzVectors() !void {
132 @setEvalBranchQuota(10_000);
133 try testOneCtzVector(u8, 64, @splat(64, @as(u8, 0b10100000)), @splat(64, @as(u4, 5)));
134 try testOneCtzVector(u8, 64, @splat(64, @as(u8, 0b10001010)), @splat(64, @as(u4, 1)));
135 try testOneCtzVector(u8, 64, @splat(64, @as(u8, 0b00000000)), @splat(64, @as(u4, 8)));
136 try testOneCtzVector(u16, 64, @splat(64, @as(u16, 0b00000000)), @splat(64, @as(u5, 16)));
137}
138
139fn testOneCtzVector(
140 comptime T: type,
141 comptime len: u32,
142 x: @Vector(len, T),
143 expected: @Vector(len, u32),
144) !void {
145 try expectVectorsEqual(@ctz(T, x), expected);
146}
147
75148test "const number literal" {
76149 const one = 1;
77150 const eleven = ten + one;
test/behavior/math_stage1.zig-40
......@@ -6,46 +6,6 @@ const maxInt = std.math.maxInt;
66const minInt = std.math.minInt;
77const mem = std.mem;
88
9test "@clz vectors" {
10 try testClzVectors();
11 comptime try testClzVectors();
12}
13
14fn testClzVectors() !void {
15 @setEvalBranchQuota(10_000);
16 try expectEqual(@clz(u8, @splat(64, @as(u8, 0b10001010))), @splat(64, @as(u4, 0)));
17 try expectEqual(@clz(u8, @splat(64, @as(u8, 0b00001010))), @splat(64, @as(u4, 4)));
18 try expectEqual(@clz(u8, @splat(64, @as(u8, 0b00011010))), @splat(64, @as(u4, 3)));
19 try expectEqual(@clz(u8, @splat(64, @as(u8, 0b00000000))), @splat(64, @as(u4, 8)));
20 try expectEqual(@clz(u128, @splat(64, @as(u128, 0xffffffffffffffff))), @splat(64, @as(u8, 64)));
21 try expectEqual(@clz(u128, @splat(64, @as(u128, 0x10000000000000000))), @splat(64, @as(u8, 63)));
22}
23
24test "@ctz" {
25 try testCtz();
26 comptime try testCtz();
27}
28
29fn testCtz() !void {
30 try expect(@ctz(u8, 0b10100000) == 5);
31 try expect(@ctz(u8, 0b10001010) == 1);
32 try expect(@ctz(u8, 0b00000000) == 8);
33 try expect(@ctz(u16, 0b00000000) == 16);
34}
35
36test "@ctz vectors" {
37 try testClzVectors();
38 comptime try testClzVectors();
39}
40
41fn testCtzVectors() !void {
42 @setEvalBranchQuota(10_000);
43 try expectEqual(@ctz(u8, @splat(64, @as(u8, 0b10100000))), @splat(64, @as(u4, 5)));
44 try expectEqual(@ctz(u8, @splat(64, @as(u8, 0b10001010))), @splat(64, @as(u4, 1)));
45 try expectEqual(@ctz(u8, @splat(64, @as(u8, 0b00000000))), @splat(64, @as(u4, 8)));
46 try expectEqual(@ctz(u16, @splat(64, @as(u16, 0b00000000))), @splat(64, @as(u5, 16)));
47}
48
499test "allow signed integer division/remainder when values are comptime known and positive or exact" {
5010 try expect(5 / 3 == 1);
5111 try expect(-5 / -3 == 1);
test/behavior/popcount.zig+1-1
......@@ -41,6 +41,6 @@ fn testPopCountIntegers() !void {
4141 try expect(@popCount(u8, @bitCast(u8, @as(i8, -120))) == 2);
4242 }
4343 comptime {
44 try expect(@popCount(i128, 0b11111111000110001100010000100001000011000011100101010001) == 24);
44 try expect(@popCount(i128, @as(i128, 0b11111111000110001100010000100001000011000011100101010001)) == 24);
4545 }
4646}