| author | |
| committer | |
| log | 93b854eb745ab3294054ae71150fe60f134f4d10 |
| tree | d40e26fcf2524c70c30302f0b503d14a0cdf4e51 |
| parent | c4681b4889652d5228a84ac7af5ad5e17ac39055 |
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 { |
| 103 | 103 | ||
| 104 | .Array => |array| try expectEqualSlices(array.child, &expected, &actual), | 104 | .Array => |array| try expectEqualSlices(array.child, &expected, &actual), |
| 105 | 105 | ||
| 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, | ||
| 458 | 463 | ||
| 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, |
| 507 | 512 | ||
| 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), |
| 760 | 771 | ||
| 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 | }, |
| 7061 | 7061 | ||
| 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 the | 31 | /// The main tomb bits are still used and the extra ones are starting with the lsb of the |
| 31 | /// value here. | 32 | /// value here. |
| 32 | special: std.AutoHashMapUnmanaged(Air.Inst.Index, u32), | 33 | special: 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 | } |
| 381 | 381 | ||
| 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), |
| 680 | 698 | ||
| 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 | } |
| 9645 | 9682 | ||
| ... | @@ -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 | } |
| 10947 | 10984 | ||
| 10948 | fn zirClz(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | 10985 | fn 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 vectors | 10996 | 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) { |
| 10963 | 11000 | switch (operand_ty.zigTypeTag()) { | |
| 10964 | const result_ty = try Type.smallestUnsignedInt(sema.arena, bits); | 11001 | .Vector => return sema.addConstant( |
| 10965 | 11002 | 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, |
| 10970 | 11007 | } | |
| 10971 | try sema.requireRuntimeBlock(block, runtime_src); | ||
| 10972 | return block.addTyOp(.clz, result_ty, operand); | ||
| 10973 | } | ||
| 10974 | |||
| 10975 | fn 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; | ||
| 10990 | 11009 | ||
| 10991 | const result_ty = try Type.smallestUnsignedInt(sema.arena, bits); | 11010 | const result_scalar_ty = try Type.smallestUnsignedInt(sema.arena, bits); |
| 10992 | 11011 | 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| { |
| 10997 | 11016 | 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 | } |
| 11001 | 11047 | ||
| 11002 | fn zirPopCount(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | 11048 | fn 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 | } |
| 11128 | 11174 | ||
| 11175 | fn 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 | |||
| 11129 | fn checkFloatType( | 11188 | fn 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 | } |
| 11245 | 11304 | ||
| 11305 | fn 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 | |||
| 11246 | const SimdBinOp = struct { | 11321 | const 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( |
| 11464 | 11539 | ||
| 11465 | fn zirSplat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | 11540 | fn 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 | } |
| 11470 | 11565 | ||
| 11471 | fn zirReduce(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | 11566 | fn 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 | } |
| 13140 | 13235 | ||
| 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); |
| 13142 | 13239 | ||
| 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 | } |
| 13199 | 13286 | ||
| 13287 | fn 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 | |||
| 13200 | fn elemPtrArray( | 13310 | fn 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 | } |
| 14412 | 14523 | ||
| 14413 | // Coerces vectors/arrays which have the same in-memory layout. This can be used for | 14524 | /// 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`. | ||
| 14415 | fn coerceVectorInMemory( | 14527 | fn 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 | } |
| 14457 | 14569 | ||
| 14570 | /// If the lengths match, coerces element-wise. | ||
| 14571 | fn 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 | |||
| 14458 | fn analyzeDeclVal( | 14642 | fn 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), | ||
| 596 | 598 | ||
| 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 | }; |
| 1650 | 1652 | ||
| 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 | } |
| 2569 | 2571 | ||
| 2572 | fn 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 | |||
| 2578 | fn 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 | |||
| 2570 | fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue { | 2600 | fn 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), | ||
| 587 | 589 | ||
| 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 | } |
| 3667 | 3669 | ||
| 3670 | fn 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 | |||
| 3676 | fn 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 | |||
| 3668 | fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue { | 3698 | fn 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), | ||
| 575 | 577 | ||
| 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 | } |
| 2068 | 2070 | ||
| 2071 | fn 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 | |||
| 2077 | fn 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 | |||
| 2069 | fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue { | 2099 | fn 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 | } |
| 3226 | 3226 | ||
| 3227 | fn 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 | |||
| 3238 | fn 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 | |||
| 3227 | fn cmpOptionals(self: *Self, lhs: WValue, rhs: WValue, operand_ty: Type, op: std.math.CompareOperator) InnerError!WValue { | 3250 | fn 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), | ||
| 639 | 641 | ||
| 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 | } |
| 3661 | 3663 | ||
| 3664 | fn 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 | |||
| 3670 | fn 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 | |||
| 3662 | fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue { | 3692 | fn 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), | ||
| 1248 | 1250 | ||
| 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 | } |
| 3017 | 3019 | ||
| 3020 | fn 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 | |||
| 3035 | fn 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 | |||
| 3018 | fn toMemoryOrder(order: std.builtin.AtomicOrder) [:0]const u8 { | 3053 | fn 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), | ||
| 2092 | 2094 | ||
| 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 | } |
| 2625 | 2631 | ||
| 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 | }; | ||
| 4166 | 4176 | ||
| 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 | } |
| 4352 | 4367 | ||
| 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 { |
| 820 | 820 | ||
| 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 | }; |
| 824 | 827 | ||
| 825 | pub const DIScope = opaque {}; | 828 | pub 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 | } |
| 292 | 294 | ||
| 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 | } |
| 3082 | 3082 | ||
| 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 | }, |
| 3130 | 3130 | ||
| 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 | }; |
| 4502 | 4504 | ||
| 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 | } |
| 1174 | 1174 | ||
| 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 | } |
| 74 | 74 | ||
| 75 | test "@clz vectors" { | ||
| 76 | try testClzVectors(); | ||
| 77 | comptime try testClzVectors(); | ||
| 78 | } | ||
| 79 | |||
| 80 | fn 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 | |||
| 90 | fn 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 | |||
| 99 | fn 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 | |||
| 110 | test "@ctz" { | ||
| 111 | try testCtz(); | ||
| 112 | comptime try testCtz(); | ||
| 113 | } | ||
| 114 | |||
| 115 | fn 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 | |||
| 122 | fn testOneCtz(comptime T: type, x: T) u32 { | ||
| 123 | return @ctz(T, x); | ||
| 124 | } | ||
| 125 | |||
| 126 | test "@ctz vectors" { | ||
| 127 | try testCtzVectors(); | ||
| 128 | comptime try testCtzVectors(); | ||
| 129 | } | ||
| 130 | |||
| 131 | fn 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 | |||
| 139 | fn 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 | |||
| 75 | test "const number literal" { | 148 | test "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; |
| 6 | const minInt = std.math.minInt; | 6 | const minInt = std.math.minInt; |
| 7 | const mem = std.mem; | 7 | const mem = std.mem; |
| 8 | 8 | ||
| 9 | test "@clz vectors" { | ||
| 10 | try testClzVectors(); | ||
| 11 | comptime try testClzVectors(); | ||
| 12 | } | ||
| 13 | |||
| 14 | fn 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 | |||
| 24 | test "@ctz" { | ||
| 25 | try testCtz(); | ||
| 26 | comptime try testCtz(); | ||
| 27 | } | ||
| 28 | |||
| 29 | fn 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 | |||
| 36 | test "@ctz vectors" { | ||
| 37 | try testClzVectors(); | ||
| 38 | comptime try testClzVectors(); | ||
| 39 | } | ||
| 40 | |||
| 41 | fn 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 | |||
| 49 | test "allow signed integer division/remainder when values are comptime known and positive or exact" { | 9 | test "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 | } |