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 {...@@ -103,11 +103,13 @@ pub fn expectEqual(expected: anytype, actual: @TypeOf(expected)) !void {
103103
104 .Array => |array| try expectEqualSlices(array.child, &expected, &actual),104 .Array => |array| try expectEqualSlices(array.child, &expected, &actual),
105105
106 .Vector => |vectorType| {106 .Vector => |info| {
107 var i: usize = 0;107 var i: usize = 0;
108 while (i < vectorType.len) : (i += 1) {108 while (i < info.len) : (i += 1) {
109 if (!std.meta.eql(expected[i], actual[i])) {109 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 });
111 return error.TestExpectedEqual;113 return error.TestExpectedEqual;
112 }114 }
113 }115 }
src/Air.zig+13-1
...@@ -426,7 +426,8 @@ pub const Inst = struct {...@@ -426,7 +426,8 @@ pub const Inst = struct {
426 /// Given a pointer to a slice, return a pointer to the pointer of the slice.426 /// Given a pointer to a slice, return a pointer to the pointer of the slice.
427 /// Uses the `ty_op` field.427 /// Uses the `ty_op` field.
428 ptr_slice_ptr_ptr,428 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.
430 /// Result type is the element type of the array operand.431 /// Result type is the element type of the array operand.
431 /// Uses the `bin_op` field.432 /// Uses the `bin_op` field.
432 array_elem_val,433 array_elem_val,
...@@ -455,6 +456,10 @@ pub const Inst = struct {...@@ -455,6 +456,10 @@ pub const Inst = struct {
455 /// Given an integer operand, return the float with the closest mathematical meaning.456 /// Given an integer operand, return the float with the closest mathematical meaning.
456 /// Uses the `ty_op` field.457 /// Uses the `ty_op` field.
457 int_to_float,458 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
459 /// Given dest ptr, value, and len, set all elements at dest to value.464 /// Given dest ptr, value, and len, set all elements at dest to value.
460 /// Result type is always void.465 /// Result type is always void.
...@@ -505,6 +510,11 @@ pub const Inst = struct {...@@ -505,6 +510,11 @@ pub const Inst = struct {
505 /// Uses the `un_op` field.510 /// Uses the `un_op` field.
506 error_name,511 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
508 pub fn fromCmpOp(op: std.math.CompareOperator) Tag {518 pub fn fromCmpOp(op: std.math.CompareOperator) Tag {
509 return switch (op) {519 return switch (op) {
510 .lt => .cmp_lt,520 .lt => .cmp_lt,
...@@ -756,6 +766,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {...@@ -756,6 +766,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
756 .cmpxchg_weak,766 .cmpxchg_weak,
757 .cmpxchg_strong,767 .cmpxchg_strong,
758 .slice,768 .slice,
769 .vector_init,
759 => return air.getRefType(datas[inst].ty_pl.ty),770 => return air.getRefType(datas[inst].ty_pl.ty),
760771
761 .not,772 .not,
...@@ -785,6 +796,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {...@@ -785,6 +796,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
785 .array_to_slice,796 .array_to_slice,
786 .float_to_int,797 .float_to_int,
787 .int_to_float,798 .int_to_float,
799 .splat,
788 .get_union_tag,800 .get_union_tag,
789 .clz,801 .clz,
790 .ctz,802 .ctz,
src/AstGen.zig+9-3
...@@ -7060,7 +7060,7 @@ fn builtinCall(...@@ -7060,7 +7060,7 @@ fn builtinCall(
7060 },7060 },
70617061
7062 .splat => {7062 .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]);
7064 const scalar = try expr(gz, scope, .none, params[1]);7064 const scalar = try expr(gz, scope, .none, params[1]);
7065 const result = try gz.addPlNode(.splat, node, Zir.Inst.Bin{7065 const result = try gz.addPlNode(.splat, node, Zir.Inst.Bin{
7066 .lhs = len,7066 .lhs = len,
...@@ -7395,8 +7395,14 @@ fn bitBuiltin(...@@ -7395,8 +7395,14 @@ fn bitBuiltin(
7395 operand_node: Ast.Node.Index,7395 operand_node: Ast.Node.Index,
7396 tag: Zir.Inst.Tag,7396 tag: Zir.Inst.Tag,
7397) InnerError!Zir.Inst.Ref {7397) InnerError!Zir.Inst.Ref {
7398 const int_type = try typeExpr(gz, scope, int_type_node);7398 // The accepted proposal https://github.com/ziglang/zig/issues/6835
7399 const operand = try expr(gz, scope, .{ .ty = int_type }, operand_node);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);
7400 const result = try gz.addUnNode(tag, operand, node);7406 const result = try gz.addUnNode(tag, operand, node);
7401 return rvalue(gz, rl, result, node);7407 return rvalue(gz, rl, result, node);
7402}7408}
src/Liveness.zig+26-2
...@@ -26,7 +26,8 @@ tomb_bits: []usize,...@@ -26,7 +26,8 @@ tomb_bits: []usize,
26/// array. The meaning of the data depends on the AIR tag.26/// array. The meaning of the data depends on the AIR tag.
27/// * `cond_br` - points to a `CondBr` in `extra` at this index.27/// * `cond_br` - points to a `CondBr` in `extra` at this index.
28/// * `switch_br` - points to a `SwitchBr` in `extra` at this index.28/// * `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.
30/// The main tomb bits are still used and the extra ones are starting with the lsb of the31/// The main tomb bits are still used and the extra ones are starting with the lsb of the
31/// value here.32/// value here.
32special: std.AutoHashMapUnmanaged(Air.Inst.Index, u32),33special: std.AutoHashMapUnmanaged(Air.Inst.Index, u32),
...@@ -316,6 +317,7 @@ fn analyzeInst(...@@ -316,6 +317,7 @@ fn analyzeInst(
316 .clz,317 .clz,
317 .ctz,318 .ctz,
318 .popcount,319 .popcount,
320 .splat,
319 => {321 => {
320 const o = inst_datas[inst].ty_op;322 const o = inst_datas[inst].ty_op;
321 return trackOperands(a, new_set, inst, main_tomb, .{ o.operand, .none, .none });323 return trackOperands(a, new_set, inst, main_tomb, .{ o.operand, .none, .none });
...@@ -345,7 +347,7 @@ fn analyzeInst(...@@ -345,7 +347,7 @@ fn analyzeInst(
345 const callee = inst_data.operand;347 const callee = inst_data.operand;
346 const extra = a.air.extraData(Air.Call, inst_data.payload);348 const extra = a.air.extraData(Air.Call, inst_data.payload);
347 const args = @bitCast([]const Air.Inst.Ref, a.air.extra[extra.end..][0..extra.data.args_len]);349 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) {
349 var buf = [1]Air.Inst.Ref{.none} ** (bpi - 1);351 var buf = [1]Air.Inst.Ref{.none} ** (bpi - 1);
350 buf[0] = callee;352 buf[0] = callee;
351 std.mem.copy(Air.Inst.Ref, buf[1..], args);353 std.mem.copy(Air.Inst.Ref, buf[1..], args);
...@@ -363,6 +365,28 @@ fn analyzeInst(...@@ -363,6 +365,28 @@ fn analyzeInst(
363 }365 }
364 return extra_tombs.finish();366 return extra_tombs.finish();
365 },367 },
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 },
366 .struct_field_ptr, .struct_field_val => {390 .struct_field_ptr, .struct_field_val => {
367 const extra = a.air.extraData(Air.StructField, inst_datas[inst].ty_pl.payload).data;391 const extra = a.air.extraData(Air.StructField, inst_datas[inst].ty_pl.payload).data;
368 return trackOperands(a, new_set, inst, main_tomb, .{ extra.struct_operand, .none, .none });392 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 {...@@ -379,6 +379,26 @@ pub const Block = struct {
379 });379 });
380 }380 }
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
382 pub fn addInst(block: *Block, inst: Air.Inst) error{OutOfMemory}!Air.Inst.Ref {402 pub fn addInst(block: *Block, inst: Air.Inst) error{OutOfMemory}!Air.Inst.Ref {
383 return Air.indexToRef(try block.addInstAsIndex(inst));403 return Air.indexToRef(try block.addInstAsIndex(inst));
384 }404 }
...@@ -652,8 +672,6 @@ pub fn analyzeBody(...@@ -652,8 +672,6 @@ pub fn analyzeBody(
652 .align_cast => try sema.zirAlignCast(block, inst),672 .align_cast => try sema.zirAlignCast(block, inst),
653 .has_decl => try sema.zirHasDecl(block, inst),673 .has_decl => try sema.zirHasDecl(block, inst),
654 .has_field => try sema.zirHasField(block, inst),674 .has_field => try sema.zirHasField(block, inst),
655 .clz => try sema.zirClz(block, inst),
656 .ctz => try sema.zirCtz(block, inst),
657 .pop_count => try sema.zirPopCount(block, inst),675 .pop_count => try sema.zirPopCount(block, inst),
658 .byte_swap => try sema.zirByteSwap(block, inst),676 .byte_swap => try sema.zirByteSwap(block, inst),
659 .bit_reverse => try sema.zirBitReverse(block, inst),677 .bit_reverse => try sema.zirBitReverse(block, inst),
...@@ -678,6 +696,9 @@ pub fn analyzeBody(...@@ -678,6 +696,9 @@ pub fn analyzeBody(
678 .await_nosuspend => try sema.zirAwait(block, inst, true),696 .await_nosuspend => try sema.zirAwait(block, inst, true),
679 .extended => try sema.zirExtended(block, inst),697 .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
681 .sqrt => try sema.zirUnaryMath(block, inst),702 .sqrt => try sema.zirUnaryMath(block, inst),
682 .sin => try sema.zirUnaryMath(block, inst),703 .sin => try sema.zirUnaryMath(block, inst),
683 .cos => try sema.zirUnaryMath(block, inst),704 .cos => try sema.zirUnaryMath(block, inst),
...@@ -4643,6 +4664,7 @@ fn zirVectorType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!...@@ -4643,6 +4664,7 @@ fn zirVectorType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
4643 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;4664 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
4644 const len = try sema.resolveAlreadyCoercedInt(block, len_src, extra.lhs, u32);4665 const len = try sema.resolveAlreadyCoercedInt(block, len_src, extra.lhs, u32);
4645 const elem_type = try sema.resolveType(block, elem_type_src, extra.rhs);4666 const elem_type = try sema.resolveType(block, elem_type_src, extra.rhs);
4667 try sema.checkVectorElemType(block, elem_type_src, elem_type);
4646 const vector_type = try Type.Tag.vector.create(sema.arena, .{4668 const vector_type = try Type.Tag.vector.create(sema.arena, .{
4647 .len = len,4669 .len = len,
4648 .elem_type = elem_type,4670 .elem_type = elem_type,
...@@ -9401,6 +9423,22 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai...@@ -9401,6 +9423,22 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
9401 }),9423 }),
9402 );9424 );
9403 },9425 },
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 },
9404 .Optional => {9442 .Optional => {
9405 const field_values = try sema.arena.alloc(Value, 1);9443 const field_values = try sema.arena.alloc(Value, 1);
9406 // child: type,9444 // child: type,
...@@ -9639,7 +9677,6 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai...@@ -9639,7 +9677,6 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
9639 .Opaque => return sema.fail(block, src, "TODO: implement zirTypeInfo for Opaque", .{}),9677 .Opaque => return sema.fail(block, src, "TODO: implement zirTypeInfo for Opaque", .{}),
9640 .Frame => return sema.fail(block, src, "TODO: implement zirTypeInfo for Frame", .{}),9678 .Frame => return sema.fail(block, src, "TODO: implement zirTypeInfo for Frame", .{}),
9641 .AnyFrame => return sema.fail(block, src, "TODO: implement zirTypeInfo for AnyFrame", .{}),9679 .AnyFrame => return sema.fail(block, src, "TODO: implement zirTypeInfo for AnyFrame", .{}),
9642 .Vector => return sema.fail(block, src, "TODO: implement zirTypeInfo for Vector", .{}),
9643 }9680 }
9644}9681}
96459682
...@@ -10945,58 +10982,67 @@ fn zirAlignCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A...@@ -10945,58 +10982,67 @@ fn zirAlignCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
10945 return sema.coerceCompatiblePtrs(block, dest_ty, ptr, ptr_src);10982 return sema.coerceCompatiblePtrs(block, dest_ty, ptr, ptr_src);
10946}10983}
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 {
10949 const inst_data = sema.code.instructions.items(.data)[inst].un_node;10992 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 };
10951 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };10993 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
10952 const operand = sema.resolveInst(inst_data.operand);10994 const operand = sema.resolveInst(inst_data.operand);
10953 const operand_ty = sema.typeOf(operand);10995 const operand_ty = sema.typeOf(operand);
10954 // TODO implement support for vectors10996 try checkIntOrVector(sema, block, operand, operand_src);
10955 if (operand_ty.zigTypeTag() != .Int) {
10956 return sema.fail(block, ty_src, "expected integer type, found '{}'", .{
10957 operand_ty,
10958 });
10959 }
10960 const target = sema.mod.getTarget();10997 const target = sema.mod.getTarget();
10961 const bits = operand_ty.intInfo(target).bits;10998 const bits = operand_ty.intInfo(target).bits;
10962 if (bits == 0) return Air.Inst.Ref.zero;10999 if (bits == 0) {
1096311000 switch (operand_ty.zigTypeTag()) {
10964 const result_ty = try Type.smallestUnsignedInt(sema.arena, bits);11001 .Vector => return sema.addConstant(
1096511002 try Type.vector(sema.arena, operand_ty.arrayLen(), Type.comptime_int),
10966 const runtime_src = if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |val| {11003 try Value.Tag.repeated.create(sema.arena, Value.zero),
10967 if (val.isUndef()) return sema.addConstUndef(result_ty);11004 ),
10968 return sema.addIntUnsigned(result_ty, val.clz(operand_ty, target));11005 .Int => return Air.Inst.Ref.zero,
10969 } else operand_src;11006 else => unreachable,
1097011007 }
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 });
10986 }11008 }
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);11010 const result_scalar_ty = try Type.smallestUnsignedInt(sema.arena, bits);
1099211011 switch (operand_ty.zigTypeTag()) {
10993 const runtime_src = if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |val| {11012 .Vector => {
10994 if (val.isUndef()) return sema.addConstUndef(result_ty);11013 const vec_len = operand_ty.arrayLen();
10995 return sema.fail(block, operand_src, "TODO: implement comptime @ctz", .{});11014 const result_ty = try Type.vector(sema.arena, vec_len, result_scalar_ty);
10996 } else operand_src;11015 if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |val| {
1099711016 if (val.isUndef()) return sema.addConstUndef(result_ty);
10998 try sema.requireRuntimeBlock(block, runtime_src);11017
10999 return block.addTyOp(.ctz, result_ty, operand);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 }
11000}11046}
1100111047
11002fn zirPopCount(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {11048fn zirPopCount(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
...@@ -11126,6 +11172,19 @@ fn checkPtrType(...@@ -11126,6 +11172,19 @@ fn checkPtrType(
11126 }11172 }
11127}11173}
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
11129fn checkFloatType(11188fn checkFloatType(
11130 sema: *Sema,11189 sema: *Sema,
11131 block: *Block,11190 block: *Block,
...@@ -11243,6 +11302,22 @@ fn checkComptimeVarStore(...@@ -11243,6 +11302,22 @@ fn checkComptimeVarStore(
11243 }11302 }
11244}11303}
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
11246const SimdBinOp = struct {11321const SimdBinOp = struct {
11247 len: ?usize,11322 len: ?usize,
11248 /// Coerced to `result_ty`.11323 /// Coerced to `result_ty`.
...@@ -11464,8 +11539,28 @@ fn zirCmpxchg(...@@ -11464,8 +11539,28 @@ fn zirCmpxchg(
1146411539
11465fn zirSplat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {11540fn zirSplat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
11466 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;11541 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
11467 const src = inst_data.src();11542 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
11468 return sema.fail(block, src, "TODO: Sema.zirSplat", .{});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);
11469}11564}
1147011565
11471fn zirReduce(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {11566fn zirReduce(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
...@@ -13138,6 +13233,8 @@ fn elemVal(...@@ -13138,6 +13233,8 @@ fn elemVal(
13138 return sema.fail(block, src, "array access of non-indexable type '{}'", .{array_ty});13233 return sema.fail(block, src, "array access of non-indexable type '{}'", .{array_ty});
13139 }13234 }
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.
13141 const elem_index = try sema.coerce(block, Type.usize, elem_index_uncasted, elem_index_src);13238 const elem_index = try sema.coerce(block, Type.usize, elem_index_uncasted, elem_index_src);
1314213239
13143 switch (array_ty.zigTypeTag()) {13240 switch (array_ty.zigTypeTag()) {
...@@ -13178,25 +13275,38 @@ fn elemVal(...@@ -13178,25 +13275,38 @@ fn elemVal(
13178 return sema.analyzeLoad(block, array_src, elem_ptr, elem_index_src);13275 return sema.analyzeLoad(block, array_src, elem_ptr, elem_index_src);
13179 },13276 },
13180 },13277 },
13181 .Array => {13278 .Array => return elemValArray(sema, block, array, elem_index, array_src, elem_index_src),
13182 if (try sema.resolveMaybeUndefVal(block, array_src, array)) |array_val| {13279 .Vector => {
13183 const elem_ty = array_ty.childType();13280 // TODO: If the index is a vector, the result should be a vector.
13184 if (array_val.isUndef()) return sema.addConstUndef(elem_ty);13281 return elemValArray(sema, block, array, elem_index, array_src, elem_index_src);
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);
13194 },13282 },
13195 .Vector => return sema.fail(block, array_src, "TODO implement Sema for elemVal for vector", .{}),
13196 else => unreachable,13283 else => unreachable,
13197 }13284 }
13198}13285}
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
13200fn elemPtrArray(13310fn elemPtrArray(
13201 sema: *Sema,13311 sema: *Sema,
13202 block: *Block,13312 block: *Block,
...@@ -13530,6 +13640,7 @@ fn coerce(...@@ -13530,6 +13640,7 @@ fn coerce(
13530 },13640 },
13531 .Vector => switch (inst_ty.zigTypeTag()) {13641 .Vector => switch (inst_ty.zigTypeTag()) {
13532 .Array => return sema.coerceVectorInMemory(block, dest_ty, dest_ty_src, inst, inst_src),13642 .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),
13533 else => {},13644 else => {},
13534 },13645 },
13535 else => {},13646 else => {},
...@@ -14410,8 +14521,9 @@ fn coerceEnumToUnion(...@@ -14410,8 +14521,9 @@ fn coerceEnumToUnion(
14410 return sema.failWithOwnedErrorMsg(msg);14521 return sema.failWithOwnedErrorMsg(msg);
14411}14522}
1441214523
14413// Coerces vectors/arrays which have the same in-memory layout. This can be used for14524/// Coerces vectors/arrays which have the same in-memory layout. This can be used for
14414// both coercing from and to vectors.14525/// both coercing from and to vectors.
14526/// TODO (affects the lang spec) delete this in favor of always using `coerceVectors`.
14415fn coerceVectorInMemory(14527fn coerceVectorInMemory(
14416 sema: *Sema,14528 sema: *Sema,
14417 block: *Block,14529 block: *Block,
...@@ -14455,6 +14567,78 @@ fn coerceVectorInMemory(...@@ -14455,6 +14567,78 @@ fn coerceVectorInMemory(
14455 return block.addBitCast(dest_ty, inst);14567 return block.addBitCast(dest_ty, inst);
14456}14568}
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
14458fn analyzeDeclVal(14642fn analyzeDeclVal(
14459 sema: *Sema,14643 sema: *Sema,
14460 block: *Block,14644 block: *Block,
src/arch/aarch64/CodeGen.zig+31-1
...@@ -593,6 +593,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -593,6 +593,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
593 .popcount => try self.airPopcount(inst),593 .popcount => try self.airPopcount(inst),
594 .tag_name => try self.airTagName(inst),594 .tag_name => try self.airTagName(inst),
595 .error_name => try self.airErrorName(inst),595 .error_name => try self.airErrorName(inst),
596 .splat => try self.airSplat(inst),
597 .vector_init => try self.airVectorInit(inst),
596598
597 .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),599 .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),
598 .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),600 .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),
...@@ -1648,7 +1650,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {...@@ -1648,7 +1650,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {
1648 break :result info.return_value;1650 break :result info.return_value;
1649 };1651 };
16501652
1651 if (args.len <= Liveness.bpi - 2) {1653 if (args.len + 1 <= Liveness.bpi - 1) {
1652 var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1);1654 var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1);
1653 buf[0] = callee;1655 buf[0] = callee;
1654 std.mem.copy(Air.Inst.Ref, buf[1..], args);1656 std.mem.copy(Air.Inst.Ref, buf[1..], args);
...@@ -2567,6 +2569,34 @@ fn airErrorName(self: *Self, inst: Air.Inst.Index) !void {...@@ -2567,6 +2569,34 @@ fn airErrorName(self: *Self, inst: Air.Inst.Index) !void {
2567 return self.finishAir(inst, result, .{ un_op, .none, .none });2569 return self.finishAir(inst, result, .{ un_op, .none, .none });
2568}2570}
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
2570fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {2600fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {
2571 // First section of indexes correspond to a set number of constant values.2601 // First section of indexes correspond to a set number of constant values.
2572 const ref_int = @enumToInt(inst);2602 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 {...@@ -584,6 +584,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
584 .popcount => try self.airPopcount(inst),584 .popcount => try self.airPopcount(inst),
585 .tag_name => try self.airTagName(inst),585 .tag_name => try self.airTagName(inst),
586 .error_name => try self.airErrorName(inst),586 .error_name => try self.airErrorName(inst),
587 .splat => try self.airSplat(inst),
588 .vector_init => try self.airVectorInit(inst),
587589
588 .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),590 .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),
589 .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),591 .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),
...@@ -3665,6 +3667,34 @@ fn airErrorName(self: *Self, inst: Air.Inst.Index) !void {...@@ -3665,6 +3667,34 @@ fn airErrorName(self: *Self, inst: Air.Inst.Index) !void {
3665 return self.finishAir(inst, result, .{ un_op, .none, .none });3667 return self.finishAir(inst, result, .{ un_op, .none, .none });
3666}3668}
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
3668fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {3698fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {
3669 // First section of indexes correspond to a set number of constant values.3699 // First section of indexes correspond to a set number of constant values.
3670 const ref_int = @enumToInt(inst);3700 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 {...@@ -572,6 +572,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
572 .popcount => try self.airPopcount(inst),572 .popcount => try self.airPopcount(inst),
573 .tag_name => try self.airTagName(inst),573 .tag_name => try self.airTagName(inst),
574 .error_name => try self.airErrorName(inst),574 .error_name => try self.airErrorName(inst),
575 .splat => try self.airSplat(inst),
576 .vector_init => try self.airVectorInit(inst),
575577
576 .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),578 .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),
577 .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),579 .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),
...@@ -2066,6 +2068,34 @@ fn airErrorName(self: *Self, inst: Air.Inst.Index) !void {...@@ -2066,6 +2068,34 @@ fn airErrorName(self: *Self, inst: Air.Inst.Index) !void {
2066 return self.finishAir(inst, result, .{ un_op, .none, .none });2068 return self.finishAir(inst, result, .{ un_op, .none, .none });
2067}2069}
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
2069fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {2099fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {
2070 // First section of indexes correspond to a set number of constant values.2100 // First section of indexes correspond to a set number of constant values.
2071 const ref_int = @enumToInt(inst);2101 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 {...@@ -3224,6 +3224,29 @@ fn airFloatToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3224 return result;3224 return result;
3225}3225}
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
3227fn cmpOptionals(self: *Self, lhs: WValue, rhs: WValue, operand_ty: Type, op: std.math.CompareOperator) InnerError!WValue {3250fn cmpOptionals(self: *Self, lhs: WValue, rhs: WValue, operand_ty: Type, op: std.math.CompareOperator) InnerError!WValue {
3228 assert(operand_ty.hasCodeGenBits());3251 assert(operand_ty.hasCodeGenBits());
3229 assert(op == .eq or op == .neq);3252 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 {...@@ -635,7 +635,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
635 .ctz => try self.airCtz(inst),635 .ctz => try self.airCtz(inst),
636 .popcount => try self.airPopcount(inst),636 .popcount => try self.airPopcount(inst),
637 .tag_name => try self.airTagName(inst),637 .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
640 .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),642 .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),
641 .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),643 .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),
...@@ -3659,6 +3661,34 @@ fn airErrorName(self: *Self, inst: Air.Inst.Index) !void {...@@ -3659,6 +3661,34 @@ fn airErrorName(self: *Self, inst: Air.Inst.Index) !void {
3659 return self.finishAir(inst, result, .{ un_op, .none, .none });3661 return self.finishAir(inst, result, .{ un_op, .none, .none });
3660}3662}
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
3662fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {3692fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {
3663 // First section of indexes correspond to a set number of constant values.3693 // First section of indexes correspond to a set number of constant values.
3664 const ref_int = @enumToInt(inst);3694 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...@@ -1245,6 +1245,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
1245 .popcount => try airBuiltinCall(f, inst, "popcount"),1245 .popcount => try airBuiltinCall(f, inst, "popcount"),
1246 .tag_name => try airTagName(f, inst),1246 .tag_name => try airTagName(f, inst),
1247 .error_name => try airErrorName(f, inst),1247 .error_name => try airErrorName(f, inst),
1248 .splat => try airSplat(f, inst),
1249 .vector_init => try airVectorInit(f, inst),
12481250
1249 .int_to_float,1251 .int_to_float,
1250 .float_to_int,1252 .float_to_int,
...@@ -3015,6 +3017,39 @@ fn airErrorName(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -3015,6 +3017,39 @@ fn airErrorName(f: *Function, inst: Air.Inst.Index) !CValue {
3015 return f.fail("TODO: C backend: implement airErrorName", .{});3017 return f.fail("TODO: C backend: implement airErrorName", .{});
3016}3018}
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
3018fn toMemoryOrder(order: std.builtin.AtomicOrder) [:0]const u8 {3053fn toMemoryOrder(order: std.builtin.AtomicOrder) [:0]const u8 {
3019 return switch (order) {3054 return switch (order) {
3020 .Unordered => "memory_order_relaxed",3055 .Unordered => "memory_order_relaxed",
src/codegen/llvm.zig+63-11
...@@ -2089,6 +2089,8 @@ pub const FuncGen = struct {...@@ -2089,6 +2089,8 @@ pub const FuncGen = struct {
2089 .popcount => try self.airPopCount(inst, "ctpop"),2089 .popcount => try self.airPopCount(inst, "ctpop"),
2090 .tag_name => try self.airTagName(inst),2090 .tag_name => try self.airTagName(inst),
2091 .error_name => try self.airErrorName(inst),2091 .error_name => try self.airErrorName(inst),
2092 .splat => try self.airSplat(inst),
2093 .vector_init => try self.airVectorInit(inst),
20922094
2093 .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),2095 .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),
2094 .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),2096 .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),
...@@ -2612,15 +2614,19 @@ pub const FuncGen = struct {...@@ -2612,15 +2614,19 @@ pub const FuncGen = struct {
2612 const array_ty = self.air.typeOf(bin_op.lhs);2614 const array_ty = self.air.typeOf(bin_op.lhs);
2613 const array_llvm_val = try self.resolveInst(bin_op.lhs);2615 const array_llvm_val = try self.resolveInst(bin_op.lhs);
2614 const rhs = try self.resolveInst(bin_op.rhs);2616 const rhs = try self.resolveInst(bin_op.rhs);
2615 assert(isByRef(array_ty));2617 if (isByRef(array_ty)) {
2616 const indices: [2]*const llvm.Value = .{ self.context.intType(32).constNull(), rhs };2618 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, "");2619 const elem_ptr = self.builder.buildInBoundsGEP(array_llvm_val, &indices, indices.len, "");
2618 const elem_ty = array_ty.childType();2620 const elem_ty = array_ty.childType();
2619 if (isByRef(elem_ty)) {2621 if (isByRef(elem_ty)) {
2620 return elem_ptr;2622 return elem_ptr;
2621 } else {2623 } else {
2622 return self.builder.buildLoad(elem_ptr, "");2624 return self.builder.buildLoad(elem_ptr, "");
2625 }
2623 }2626 }
2627
2628 // This branch can be reached for vectors, which are always by-value.
2629 return self.builder.buildExtractElement(array_llvm_val, rhs, "");
2624 }2630 }
26252631
2626 fn airPtrElemVal(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {2632 fn airPtrElemVal(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
...@@ -4163,11 +4169,20 @@ pub const FuncGen = struct {...@@ -4163,11 +4169,20 @@ pub const FuncGen = struct {
4163 const operand = try self.resolveInst(ty_op.operand);4169 const operand = try self.resolveInst(ty_op.operand);
4164 const target = self.dg.module.getTarget();4170 const target = self.dg.module.getTarget();
4165 const bits = operand_ty.intInfo(target).bits;4171 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
4167 var fn_name_buf: [100]u8 = undefined;4177 var fn_name_buf: [100]u8 = undefined;
4168 const llvm_fn_name = std.fmt.bufPrintZ(&fn_name_buf, "llvm.{s}.i{d}", .{4178 const llvm_fn_name = if (vec_len) |len|
4169 prefix, bits,4179 std.fmt.bufPrintZ(&fn_name_buf, "llvm.{s}.v{d}i{d}", .{
4170 }) catch unreachable;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;
4171 const llvm_i1 = self.context.intType(1);4186 const llvm_i1 = self.context.intType(1);
4172 const fn_val = self.dg.object.llvm_module.getNamedFunction(llvm_fn_name) orelse blk: {4187 const fn_val = self.dg.object.llvm_module.getNamedFunction(llvm_fn_name) orelse blk: {
4173 const operand_llvm_ty = try self.dg.llvmType(operand_ty);4188 const operand_llvm_ty = try self.dg.llvmType(operand_ty);
...@@ -4350,6 +4365,43 @@ pub const FuncGen = struct {...@@ -4350,6 +4365,43 @@ pub const FuncGen = struct {
4350 return self.builder.buildLoad(error_name_ptr, "");4365 return self.builder.buildLoad(error_name_ptr, "");
4351 }4366 }
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
4353 fn getErrorNameTable(self: *FuncGen) !*const llvm.Value {4405 fn getErrorNameTable(self: *FuncGen) !*const llvm.Value {
4354 if (self.dg.object.error_name_table) |table| {4406 if (self.dg.object.error_name_table) |table| {
4355 return table;4407 return table;
src/codegen/llvm/bindings.zig+3
...@@ -820,6 +820,9 @@ pub const Builder = opaque {...@@ -820,6 +820,9 @@ pub const Builder = opaque {
820820
821 pub const setCurrentDebugLocation2 = LLVMSetCurrentDebugLocation2;821 pub const setCurrentDebugLocation2 = LLVMSetCurrentDebugLocation2;
822 extern fn LLVMSetCurrentDebugLocation2(Builder: *const Builder, Loc: *Metadata) void;822 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;
823};826};
824827
825pub const DIScope = opaque {};828pub const DIScope = opaque {};
src/print_air.zig+16
...@@ -196,6 +196,7 @@ const Writer = struct {...@@ -196,6 +196,7 @@ const Writer = struct {
196 .struct_field_ptr_index_3,196 .struct_field_ptr_index_3,
197 .array_to_slice,197 .array_to_slice,
198 .int_to_float,198 .int_to_float,
199 .splat,
199 .float_to_int,200 .float_to_int,
200 .get_union_tag,201 .get_union_tag,
201 .clz,202 .clz,
...@@ -218,6 +219,7 @@ const Writer = struct {...@@ -218,6 +219,7 @@ const Writer = struct {
218 .assembly => try w.writeAssembly(s, inst),219 .assembly => try w.writeAssembly(s, inst),
219 .dbg_stmt => try w.writeDbgStmt(s, inst),220 .dbg_stmt => try w.writeDbgStmt(s, inst),
220 .call => try w.writeCall(s, inst),221 .call => try w.writeCall(s, inst),
222 .vector_init => try w.writeVectorInit(s, inst),
221 .br => try w.writeBr(s, inst),223 .br => try w.writeBr(s, inst),
222 .cond_br => try w.writeCondBr(s, inst),224 .cond_br => try w.writeCondBr(s, inst),
223 .switch_br => try w.writeSwitchBr(s, inst),225 .switch_br => try w.writeSwitchBr(s, inst),
...@@ -290,6 +292,20 @@ const Writer = struct {...@@ -290,6 +292,20 @@ const Writer = struct {
290 try s.writeAll("}");292 try s.writeAll("}");
291 }293 }
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
293 fn writeStructField(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {309 fn writeStructField(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
294 const ty_pl = w.air.instructions.items(.data)[inst].ty_pl;310 const ty_pl = w.air.instructions.items(.data)[inst].ty_pl;
295 const extra = w.air.extraData(Air.StructField, ty_pl.payload).data;311 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 {...@@ -3080,7 +3080,7 @@ pub const Type = extern union {
3080 };3080 };
3081 }3081 }
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.
3084 pub fn intInfo(self: Type, target: Target) struct { signedness: std.builtin.Signedness, bits: u16 } {3084 pub fn intInfo(self: Type, target: Target) struct { signedness: std.builtin.Signedness, bits: u16 } {
3085 var ty = self;3085 var ty = self;
3086 while (true) switch (ty.tag()) {3086 while (true) switch (ty.tag()) {
...@@ -3128,6 +3128,8 @@ pub const Type = extern union {...@@ -3128,6 +3128,8 @@ pub const Type = extern union {
3128 return .{ .signedness = .unsigned, .bits = 16 };3128 return .{ .signedness = .unsigned, .bits = 16 };
3129 },3129 },
31303130
3131 .vector => ty = ty.castTag(.vector).?.data.elem_type,
3132
3131 else => unreachable,3133 else => unreachable,
3132 };3134 };
3133 }3135 }
...@@ -4501,6 +4503,7 @@ pub const Type = extern union {...@@ -4501,6 +4503,7 @@ pub const Type = extern union {
4501 };4503 };
45024504
4503 pub const @"u8" = initTag(.u8);4505 pub const @"u8" = initTag(.u8);
4506 pub const @"u32" = initTag(.u32);
4504 pub const @"bool" = initTag(.bool);4507 pub const @"bool" = initTag(.bool);
4505 pub const @"usize" = initTag(.usize);4508 pub const @"usize" = initTag(.usize);
4506 pub const @"isize" = initTag(.isize);4509 pub const @"isize" = initTag(.isize);
src/value.zig+65
...@@ -1172,6 +1172,44 @@ pub const Value = extern union {...@@ -1172,6 +1172,44 @@ pub const Value = extern union {
1172 }1172 }
1173 }1173 }
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
1175 /// Asserts the value is an integer and not undefined.1213 /// Asserts the value is an integer and not undefined.
1176 /// Returns the number of bits the value requires to represent stored in twos complement form.1214 /// Returns the number of bits the value requires to represent stored in twos complement form.
1177 pub fn intBitCountTwosComp(self: Value) usize {1215 pub fn intBitCountTwosComp(self: Value) usize {
...@@ -1455,6 +1493,20 @@ pub const Value = extern union {...@@ -1455,6 +1493,20 @@ pub const Value = extern union {
1455 .field_ptr => @panic("TODO: Implement more pointer eql cases"),1493 .field_ptr => @panic("TODO: Implement more pointer eql cases"),
1456 .eu_payload_ptr => @panic("TODO: Implement more pointer eql cases"),1494 .eu_payload_ptr => @panic("TODO: Implement more pointer eql cases"),
1457 .opt_payload_ptr => @panic("TODO: Implement more pointer eql cases"),1495 .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 },
1458 else => {},1510 else => {},
1459 }1511 }
1460 } else if (a_tag == .null_value or b_tag == .null_value) {1512 } else if (a_tag == .null_value or b_tag == .null_value) {
...@@ -1488,6 +1540,19 @@ pub const Value = extern union {...@@ -1488,6 +1540,19 @@ pub const Value = extern union {
1488 const int_ty = ty.intTagType(&buf_ty);1540 const int_ty = ty.intTagType(&buf_ty);
1489 return eql(a_val, b_val, int_ty);1541 return eql(a_val, b_val, int_ty);
1490 },1542 },
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 },
1491 else => return order(a, b).compare(.eq),1556 else => return order(a, b).compare(.eq),
1492 }1557 }
1493 }1558 }
test/behavior/math.zig+73
...@@ -72,6 +72,79 @@ fn testOneClz(comptime T: type, x: T) u32 {...@@ -72,6 +72,79 @@ fn testOneClz(comptime T: type, x: T) u32 {
72 return @clz(T, x);72 return @clz(T, x);
73}73}
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
75test "const number literal" {148test "const number literal" {
76 const one = 1;149 const one = 1;
77 const eleven = ten + one;150 const eleven = ten + one;
test/behavior/math_stage1.zig-40
...@@ -6,46 +6,6 @@ const maxInt = std.math.maxInt;...@@ -6,46 +6,6 @@ const maxInt = std.math.maxInt;
6const minInt = std.math.minInt;6const minInt = std.math.minInt;
7const mem = std.mem;7const 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
49test "allow signed integer division/remainder when values are comptime known and positive or exact" {9test "allow signed integer division/remainder when values are comptime known and positive or exact" {
50 try expect(5 / 3 == 1);10 try expect(5 / 3 == 1);
51 try expect(-5 / -3 == 1);11 try expect(-5 / -3 == 1);
test/behavior/popcount.zig+1-1
...@@ -41,6 +41,6 @@ fn testPopCountIntegers() !void {...@@ -41,6 +41,6 @@ fn testPopCountIntegers() !void {
41 try expect(@popCount(u8, @bitCast(u8, @as(i8, -120))) == 2);41 try expect(@popCount(u8, @bitCast(u8, @as(i8, -120))) == 2);
42 }42 }
43 comptime {43 comptime {
44 try expect(@popCount(i128, 0b11111111000110001100010000100001000011000011100101010001) == 24);44 try expect(@popCount(i128, @as(i128, 0b11111111000110001100010000100001000011000011100101010001)) == 24);
45 }45 }
46}46}