| author | |
| committer | |
| log | 86209e1a9259dca40803e56d612beacf5a35855c |
| tree | 21e2b617a7cd424f82e040b94014b1c176dcf63b |
| parent | 914ad1ec2eff4ea9061804ad0da9cde7dd6543b6 |
| parent | ef0f3ba905e992556a60f935cbb7cb30cf1f27db |
| signature |
This is the first 3 commits of #2945, plus my fixups.11 files changed, 626 insertions(+), 68 deletions(-)
doc/langref.html.in+37| ... | ... | @@ -7673,6 +7673,43 @@ test "@setRuntimeSafety" { |
| 7673 | 7673 | {#see_also|@shlExact|@shlWithOverflow#} |
| 7674 | 7674 | {#header_close#} |
| 7675 | 7675 | |
| 7676 | {#header_open|@shuffle#} | |
| 7677 | <pre>{#syntax#}@shuffle(comptime E: type, a: @Vector(a_len, E), b: @Vector(b_len, E), comptime mask: @Vector(mask_len, i32)) @Vector(mask_len, E){#endsyntax#}</pre> | |
| 7678 | <p> | |
| 7679 | Constructs a new {#link|vector|Vectors#} by selecting elements from {#syntax#}a{#endsyntax#} and | |
| 7680 | {#syntax#}b{#endsyntax#} based on {#syntax#}mask{#endsyntax#}. | |
| 7681 | </p> | |
| 7682 | <p> | |
| 7683 | Each element in {#syntax#}mask{#endsyntax#} selects an element from either {#syntax#}a{#endsyntax#} or | |
| 7684 | {#syntax#}b{#endsyntax#}. Positive numbers select from {#syntax#}a{#endsyntax#} starting at 0. | |
| 7685 | Negative values select from {#syntax#}b{#endsyntax#}, starting at {#syntax#}-1{#endsyntax#} and going down. | |
| 7686 | It is recommended to use the {#syntax#}~{#endsyntax#} operator from indexes from {#syntax#}b{#endsyntax#} | |
| 7687 | so that both indexes can start from {#syntax#}0{#endsyntax#} (i.e. {#syntax#}~i32(0){#endsyntax#} is | |
| 7688 | {#syntax#}-1{#endsyntax#}). | |
| 7689 | </p> | |
| 7690 | <p> | |
| 7691 | For each element of {#syntax#}mask{#endsyntax#}, if it or the selected value from | |
| 7692 | {#syntax#}a{#endsyntax#} or {#syntax#}b{#endsyntax#} is {#syntax#}undefined{#endsyntax#}, | |
| 7693 | then the resulting element is {#syntax#}undefined{#endsyntax#}. | |
| 7694 | </p> | |
| 7695 | <p> | |
| 7696 | {#syntax#}a_len{#endsyntax#} and {#syntax#}b_len{#endsyntax#} may differ in length. Out-of-bounds element | |
| 7697 | indexes in {#syntax#}mask{#endsyntax#} result in compile errors. | |
| 7698 | </p> | |
| 7699 | <p> | |
| 7700 | If {#syntax#}a{#endsyntax#} or {#syntax#}b{#endsyntax#} is {#syntax#}undefined{#endsyntax#}, it | |
| 7701 | is equivalent to a vector of all {#syntax#}undefined{#endsyntax#} with the same length as the other vector. | |
| 7702 | If both vectors are {#syntax#}undefined{#endsyntax#}, {#syntax#}@shuffle{#endsyntax#} returns | |
| 7703 | a vector with all elements {#syntax#}undefined{#endsyntax#}. | |
| 7704 | </p> | |
| 7705 | <p> | |
| 7706 | {#syntax#}E{#endsyntax#} must be an {#link|integer|Integers#}, {#link|float|Floats#}, | |
| 7707 | {#link|pointer|Pointers#}, or {#syntax#}bool{#endsyntax#}. The mask may be any vector length, and its | |
| 7708 | length determines the result length. | |
| 7709 | </p> | |
| 7710 | {#see_also|SIMD#} | |
| 7711 | {#header_close#} | |
| 7712 | ||
| 7676 | 7713 | {#header_open|@sizeOf#} |
| 7677 | 7714 | <pre>{#syntax#}@sizeOf(comptime T: type) comptime_int{#endsyntax#}</pre> |
| 7678 | 7715 | <p> |
src/all_types.hpp+12-1| ... | ... | @@ -1351,7 +1351,7 @@ struct ZigTypeBoundFn { |
| 1351 | 1351 | }; |
| 1352 | 1352 | |
| 1353 | 1353 | struct ZigTypeVector { |
| 1354 | // The type must be a pointer, integer, or float | |
| 1354 | // The type must be a pointer, integer, bool, or float | |
| 1355 | 1355 | ZigType *elem_type; |
| 1356 | 1356 | uint32_t len; |
| 1357 | 1357 | }; |
| ... | ... | @@ -1611,6 +1611,7 @@ enum BuiltinFnId { |
| 1611 | 1611 | BuiltinFnIdIntToEnum, |
| 1612 | 1612 | BuiltinFnIdIntType, |
| 1613 | 1613 | BuiltinFnIdVectorType, |
| 1614 | BuiltinFnIdShuffle, | |
| 1614 | 1615 | BuiltinFnIdSetCold, |
| 1615 | 1616 | BuiltinFnIdSetRuntimeSafety, |
| 1616 | 1617 | BuiltinFnIdSetFloatMode, |
| ... | ... | @@ -2428,6 +2429,7 @@ enum IrInstructionId { |
| 2428 | 2429 | IrInstructionIdBoolToInt, |
| 2429 | 2430 | IrInstructionIdIntType, |
| 2430 | 2431 | IrInstructionIdVectorType, |
| 2432 | IrInstructionIdShuffleVector, | |
| 2431 | 2433 | IrInstructionIdBoolNot, |
| 2432 | 2434 | IrInstructionIdMemset, |
| 2433 | 2435 | IrInstructionIdMemcpy, |
| ... | ... | @@ -3669,6 +3671,15 @@ struct IrInstructionVectorToArray { |
| 3669 | 3671 | IrInstruction *result_loc; |
| 3670 | 3672 | }; |
| 3671 | 3673 | |
| 3674 | struct IrInstructionShuffleVector { | |
| 3675 | IrInstruction base; | |
| 3676 | ||
| 3677 | IrInstruction *scalar_type; | |
| 3678 | IrInstruction *a; | |
| 3679 | IrInstruction *b; | |
| 3680 | IrInstruction *mask; // This is in zig-format, not llvm format | |
| 3681 | }; | |
| 3682 | ||
| 3672 | 3683 | struct IrInstructionAssertZero { |
| 3673 | 3684 | IrInstruction base; |
| 3674 | 3685 |
src/analyze.cpp+2-1| ... | ... | @@ -4708,6 +4708,7 @@ ZigType *get_int_type(CodeGen *g, bool is_signed, uint32_t size_in_bits) { |
| 4708 | 4708 | bool is_valid_vector_elem_type(ZigType *elem_type) { |
| 4709 | 4709 | return elem_type->id == ZigTypeIdInt || |
| 4710 | 4710 | elem_type->id == ZigTypeIdFloat || |
| 4711 | elem_type->id == ZigTypeIdBool || | |
| 4711 | 4712 | get_codegen_ptr_type(elem_type) != nullptr; |
| 4712 | 4713 | } |
| 4713 | 4714 | |
| ... | ... | @@ -4727,7 +4728,7 @@ ZigType *get_vector_type(CodeGen *g, uint32_t len, ZigType *elem_type) { |
| 4727 | 4728 | |
| 4728 | 4729 | ZigType *entry = new_type_table_entry(ZigTypeIdVector); |
| 4729 | 4730 | if ((len != 0) && type_has_bits(elem_type)) { |
| 4730 | // Vectors can only be ints, floats, or pointers. ints and floats have trivially resolvable | |
| 4731 | // Vectors can only be ints, floats, bools, or pointers. ints (inc. bools) and floats have trivially resolvable | |
| 4731 | 4732 | // llvm type refs. pointers we will use usize instead. |
| 4732 | 4733 | LLVMTypeRef example_vector_llvm_type; |
| 4733 | 4734 | if (elem_type->id == ZigTypeIdPointer) { |
src/codegen.cpp+84-10| ... | ... | @@ -4581,6 +4581,36 @@ static LLVMValueRef ir_render_ctz(CodeGen *g, IrExecutable *executable, IrInstru |
| 4581 | 4581 | return gen_widen_or_shorten(g, false, int_type, instruction->base.value.type, wrong_size_int); |
| 4582 | 4582 | } |
| 4583 | 4583 | |
| 4584 | static LLVMValueRef ir_render_shuffle_vector(CodeGen *g, IrExecutable *executable, IrInstructionShuffleVector *instruction) { | |
| 4585 | uint64_t len_a = instruction->a->value.type->data.vector.len; | |
| 4586 | uint64_t len_mask = instruction->mask->value.type->data.vector.len; | |
| 4587 | ||
| 4588 | // LLVM uses integers larger than the length of the first array to | |
| 4589 | // index into the second array. This was deemed unnecessarily fragile | |
| 4590 | // when changing code, so Zig uses negative numbers to index the | |
| 4591 | // second vector. These start at -1 and go down, and are easiest to use | |
| 4592 | // with the ~ operator. Here we convert between the two formats. | |
| 4593 | IrInstruction *mask = instruction->mask; | |
| 4594 | LLVMValueRef *values = allocate<LLVMValueRef>(len_mask); | |
| 4595 | for (uint64_t i = 0; i < len_mask; i++) { | |
| 4596 | if (mask->value.data.x_array.data.s_none.elements[i].special == ConstValSpecialUndef) { | |
| 4597 | values[i] = LLVMGetUndef(LLVMInt32Type()); | |
| 4598 | } else { | |
| 4599 | int32_t v = bigint_as_signed(&mask->value.data.x_array.data.s_none.elements[i].data.x_bigint); | |
| 4600 | uint32_t index_val = (v >= 0) ? (uint32_t)v : (uint32_t)~v + (uint32_t)len_a; | |
| 4601 | values[i] = LLVMConstInt(LLVMInt32Type(), index_val, false); | |
| 4602 | } | |
| 4603 | } | |
| 4604 | ||
| 4605 | LLVMValueRef llvm_mask_value = LLVMConstVector(values, len_mask); | |
| 4606 | free(values); | |
| 4607 | ||
| 4608 | return LLVMBuildShuffleVector(g->builder, | |
| 4609 | ir_llvm_value(g, instruction->a), | |
| 4610 | ir_llvm_value(g, instruction->b), | |
| 4611 | llvm_mask_value, ""); | |
| 4612 | } | |
| 4613 | ||
| 4584 | 4614 | static LLVMValueRef ir_render_pop_count(CodeGen *g, IrExecutable *executable, IrInstructionPopCount *instruction) { |
| 4585 | 4615 | ZigType *int_type = instruction->op->value.type; |
| 4586 | 4616 | LLVMValueRef fn_val = get_int_builtin_fn(g, int_type, BuiltinFnIdPopCount); |
| ... | ... | @@ -5549,10 +5579,29 @@ static LLVMValueRef ir_render_vector_to_array(CodeGen *g, IrExecutable *executab |
| 5549 | 5579 | assert(handle_is_ptr(array_type)); |
| 5550 | 5580 | LLVMValueRef result_loc = ir_llvm_value(g, instruction->result_loc); |
| 5551 | 5581 | LLVMValueRef vector = ir_llvm_value(g, instruction->vector); |
| 5552 | LLVMValueRef casted_ptr = LLVMBuildBitCast(g->builder, result_loc, | |
| 5553 | LLVMPointerType(get_llvm_type(g, instruction->vector->value.type), 0), ""); | |
| 5554 | uint32_t alignment = get_ptr_align(g, instruction->result_loc->value.type); | |
| 5555 | gen_store_untyped(g, vector, casted_ptr, alignment, false); | |
| 5582 | ||
| 5583 | ZigType *elem_type = array_type->data.array.child_type; | |
| 5584 | bool bitcast_ok = (elem_type->size_in_bits * 8) == elem_type->abi_size; | |
| 5585 | if (bitcast_ok) { | |
| 5586 | LLVMValueRef casted_ptr = LLVMBuildBitCast(g->builder, result_loc, | |
| 5587 | LLVMPointerType(get_llvm_type(g, instruction->vector->value.type), 0), ""); | |
| 5588 | uint32_t alignment = get_ptr_align(g, instruction->result_loc->value.type); | |
| 5589 | gen_store_untyped(g, vector, casted_ptr, alignment, false); | |
| 5590 | } else { | |
| 5591 | // If the ABI size of the element type is not evenly divisible by size_in_bits, a simple bitcast | |
| 5592 | // will not work, and we fall back to extractelement. | |
| 5593 | LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->llvm_type; | |
| 5594 | LLVMTypeRef u32_type_ref = LLVMInt32Type(); | |
| 5595 | LLVMValueRef zero = LLVMConstInt(usize_type_ref, 0, false); | |
| 5596 | for (uintptr_t i = 0; i < instruction->vector->value.type->data.vector.len; i++) { | |
| 5597 | LLVMValueRef index_usize = LLVMConstInt(usize_type_ref, i, false); | |
| 5598 | LLVMValueRef index_u32 = LLVMConstInt(u32_type_ref, i, false); | |
| 5599 | LLVMValueRef indexes[] = { zero, index_usize }; | |
| 5600 | LLVMValueRef elem_ptr = LLVMBuildInBoundsGEP(g->builder, result_loc, indexes, 2, ""); | |
| 5601 | LLVMValueRef elem = LLVMBuildExtractElement(g->builder, vector, index_u32, ""); | |
| 5602 | LLVMBuildStore(g->builder, elem, elem_ptr); | |
| 5603 | } | |
| 5604 | } | |
| 5556 | 5605 | return result_loc; |
| 5557 | 5606 | } |
| 5558 | 5607 | |
| ... | ... | @@ -5563,12 +5612,34 @@ static LLVMValueRef ir_render_array_to_vector(CodeGen *g, IrExecutable *executab |
| 5563 | 5612 | assert(vector_type->id == ZigTypeIdVector); |
| 5564 | 5613 | assert(!handle_is_ptr(vector_type)); |
| 5565 | 5614 | LLVMValueRef array_ptr = ir_llvm_value(g, instruction->array); |
| 5566 | LLVMValueRef casted_ptr = LLVMBuildBitCast(g->builder, array_ptr, | |
| 5567 | LLVMPointerType(get_llvm_type(g, vector_type), 0), ""); | |
| 5568 | ZigType *array_type = instruction->array->value.type; | |
| 5569 | assert(array_type->id == ZigTypeIdArray); | |
| 5570 | uint32_t alignment = get_abi_alignment(g, array_type->data.array.child_type); | |
| 5571 | return gen_load_untyped(g, casted_ptr, alignment, false, ""); | |
| 5615 | LLVMTypeRef vector_type_ref = get_llvm_type(g, vector_type); | |
| 5616 | ||
| 5617 | ZigType *elem_type = vector_type->data.vector.elem_type; | |
| 5618 | bool bitcast_ok = (elem_type->size_in_bits * 8) == elem_type->abi_size; | |
| 5619 | if (bitcast_ok) { | |
| 5620 | LLVMValueRef casted_ptr = LLVMBuildBitCast(g->builder, array_ptr, | |
| 5621 | LLVMPointerType(vector_type_ref, 0), ""); | |
| 5622 | ZigType *array_type = instruction->array->value.type; | |
| 5623 | assert(array_type->id == ZigTypeIdArray); | |
| 5624 | uint32_t alignment = get_abi_alignment(g, array_type->data.array.child_type); | |
| 5625 | return gen_load_untyped(g, casted_ptr, alignment, false, ""); | |
| 5626 | } else { | |
| 5627 | // If the ABI size of the element type is not evenly divisible by size_in_bits, a simple bitcast | |
| 5628 | // will not work, and we fall back to insertelement. | |
| 5629 | LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->llvm_type; | |
| 5630 | LLVMTypeRef u32_type_ref = LLVMInt32Type(); | |
| 5631 | LLVMValueRef zero = LLVMConstInt(usize_type_ref, 0, false); | |
| 5632 | LLVMValueRef vector = LLVMGetUndef(vector_type_ref); | |
| 5633 | for (uintptr_t i = 0; i < instruction->base.value.type->data.vector.len; i++) { | |
| 5634 | LLVMValueRef index_usize = LLVMConstInt(usize_type_ref, i, false); | |
| 5635 | LLVMValueRef index_u32 = LLVMConstInt(u32_type_ref, i, false); | |
| 5636 | LLVMValueRef indexes[] = { zero, index_usize }; | |
| 5637 | LLVMValueRef elem_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, indexes, 2, ""); | |
| 5638 | LLVMValueRef elem = LLVMBuildLoad(g->builder, elem_ptr, ""); | |
| 5639 | vector = LLVMBuildInsertElement(g->builder, vector, elem, index_u32, ""); | |
| 5640 | } | |
| 5641 | return vector; | |
| 5642 | } | |
| 5572 | 5643 | } |
| 5573 | 5644 | |
| 5574 | 5645 | static LLVMValueRef ir_render_assert_zero(CodeGen *g, IrExecutable *executable, |
| ... | ... | @@ -6054,6 +6125,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, |
| 6054 | 6125 | return ir_render_spill_begin(g, executable, (IrInstructionSpillBegin *)instruction); |
| 6055 | 6126 | case IrInstructionIdSpillEnd: |
| 6056 | 6127 | return ir_render_spill_end(g, executable, (IrInstructionSpillEnd *)instruction); |
| 6128 | case IrInstructionIdShuffleVector: | |
| 6129 | return ir_render_shuffle_vector(g, executable, (IrInstructionShuffleVector *) instruction); | |
| 6057 | 6130 | } |
| 6058 | 6131 | zig_unreachable(); |
| 6059 | 6132 | } |
| ... | ... | @@ -7744,6 +7817,7 @@ static void define_builtin_fns(CodeGen *g) { |
| 7744 | 7817 | create_builtin_fn(g, BuiltinFnIdCompileLog, "compileLog", SIZE_MAX); |
| 7745 | 7818 | create_builtin_fn(g, BuiltinFnIdIntType, "IntType", 2); // TODO rename to Int |
| 7746 | 7819 | create_builtin_fn(g, BuiltinFnIdVectorType, "Vector", 2); |
| 7820 | create_builtin_fn(g, BuiltinFnIdShuffle, "shuffle", 4); | |
| 7747 | 7821 | create_builtin_fn(g, BuiltinFnIdSetCold, "setCold", 1); |
| 7748 | 7822 | create_builtin_fn(g, BuiltinFnIdSetRuntimeSafety, "setRuntimeSafety", 1); |
| 7749 | 7823 | create_builtin_fn(g, BuiltinFnIdSetFloatMode, "setFloatMode", 1); |
src/ir.cpp+338-52| ... | ... | @@ -717,6 +717,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionVectorType *) { |
| 717 | 717 | return IrInstructionIdVectorType; |
| 718 | 718 | } |
| 719 | 719 | |
| 720 | static constexpr IrInstructionId ir_instruction_id(IrInstructionShuffleVector *) { | |
| 721 | return IrInstructionIdShuffleVector; | |
| 722 | } | |
| 723 | ||
| 720 | 724 | static constexpr IrInstructionId ir_instruction_id(IrInstructionBoolNot *) { |
| 721 | 725 | return IrInstructionIdBoolNot; |
| 722 | 726 | } |
| ... | ... | @@ -2277,6 +2281,25 @@ static IrInstruction *ir_build_vector_type(IrBuilder *irb, Scope *scope, AstNode |
| 2277 | 2281 | return &instruction->base; |
| 2278 | 2282 | } |
| 2279 | 2283 | |
| 2284 | static IrInstruction *ir_build_shuffle_vector(IrBuilder *irb, Scope *scope, AstNode *source_node, | |
| 2285 | IrInstruction *scalar_type, IrInstruction *a, IrInstruction *b, IrInstruction *mask) | |
| 2286 | { | |
| 2287 | IrInstructionShuffleVector *instruction = ir_build_instruction<IrInstructionShuffleVector>(irb, scope, source_node); | |
| 2288 | instruction->scalar_type = scalar_type; | |
| 2289 | instruction->a = a; | |
| 2290 | instruction->b = b; | |
| 2291 | instruction->mask = mask; | |
| 2292 | ||
| 2293 | if (scalar_type != nullptr) { | |
| 2294 | ir_ref_instruction(scalar_type, irb->current_basic_block); | |
| 2295 | } | |
| 2296 | ir_ref_instruction(a, irb->current_basic_block); | |
| 2297 | ir_ref_instruction(b, irb->current_basic_block); | |
| 2298 | ir_ref_instruction(mask, irb->current_basic_block); | |
| 2299 | ||
| 2300 | return &instruction->base; | |
| 2301 | } | |
| 2302 | ||
| 2280 | 2303 | static IrInstruction *ir_build_bool_not(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) { |
| 2281 | 2304 | IrInstructionBoolNot *instruction = ir_build_instruction<IrInstructionBoolNot>(irb, scope, source_node); |
| 2282 | 2305 | instruction->value = value; |
| ... | ... | @@ -4936,6 +4959,32 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 4936 | 4959 | IrInstruction *vector_type = ir_build_vector_type(irb, scope, node, arg0_value, arg1_value); |
| 4937 | 4960 | return ir_lval_wrap(irb, scope, vector_type, lval, result_loc); |
| 4938 | 4961 | } |
| 4962 | case BuiltinFnIdShuffle: | |
| 4963 | { | |
| 4964 | AstNode *arg0_node = node->data.fn_call_expr.params.at(0); | |
| 4965 | IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); | |
| 4966 | if (arg0_value == irb->codegen->invalid_instruction) | |
| 4967 | return arg0_value; | |
| 4968 | ||
| 4969 | AstNode *arg1_node = node->data.fn_call_expr.params.at(1); | |
| 4970 | IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); | |
| 4971 | if (arg1_value == irb->codegen->invalid_instruction) | |
| 4972 | return arg1_value; | |
| 4973 | ||
| 4974 | AstNode *arg2_node = node->data.fn_call_expr.params.at(2); | |
| 4975 | IrInstruction *arg2_value = ir_gen_node(irb, arg2_node, scope); | |
| 4976 | if (arg2_value == irb->codegen->invalid_instruction) | |
| 4977 | return arg2_value; | |
| 4978 | ||
| 4979 | AstNode *arg3_node = node->data.fn_call_expr.params.at(3); | |
| 4980 | IrInstruction *arg3_value = ir_gen_node(irb, arg3_node, scope); | |
| 4981 | if (arg3_value == irb->codegen->invalid_instruction) | |
| 4982 | return arg3_value; | |
| 4983 | ||
| 4984 | IrInstruction *shuffle_vector = ir_build_shuffle_vector(irb, scope, node, | |
| 4985 | arg0_value, arg1_value, arg2_value, arg3_value); | |
| 4986 | return ir_lval_wrap(irb, scope, shuffle_vector, lval, result_loc); | |
| 4987 | } | |
| 4939 | 4988 | case BuiltinFnIdMemcpy: |
| 4940 | 4989 | { |
| 4941 | 4990 | AstNode *arg0_node = node->data.fn_call_expr.params.at(0); |
| ... | ... | @@ -11000,6 +11049,19 @@ static ZigType *ir_resolve_type(IrAnalyze *ira, IrInstruction *type_value) { |
| 11000 | 11049 | return ir_resolve_const_type(ira->codegen, ira->new_irb.exec, type_value->source_node, val); |
| 11001 | 11050 | } |
| 11002 | 11051 | |
| 11052 | static ZigType *ir_resolve_vector_elem_type(IrAnalyze *ira, IrInstruction *elem_type_value) { | |
| 11053 | ZigType *elem_type = ir_resolve_type(ira, elem_type_value); | |
| 11054 | if (type_is_invalid(elem_type)) | |
| 11055 | return ira->codegen->builtin_types.entry_invalid; | |
| 11056 | if (!is_valid_vector_elem_type(elem_type)) { | |
| 11057 | ir_add_error(ira, elem_type_value, | |
| 11058 | buf_sprintf("vector element type must be integer, float, bool, or pointer; '%s' is invalid", | |
| 11059 | buf_ptr(&elem_type->name))); | |
| 11060 | return ira->codegen->builtin_types.entry_invalid; | |
| 11061 | } | |
| 11062 | return elem_type; | |
| 11063 | } | |
| 11064 | ||
| 11003 | 11065 | static ZigType *ir_resolve_int_type(IrAnalyze *ira, IrInstruction *type_value) { |
| 11004 | 11066 | ZigType *ty = ir_resolve_type(ira, type_value); |
| 11005 | 11067 | if (type_is_invalid(ty)) |
| ... | ... | @@ -13092,6 +13154,59 @@ static bool optional_value_is_null(ConstExprValue *val) { |
| 13092 | 13154 | } |
| 13093 | 13155 | } |
| 13094 | 13156 | |
| 13157 | static IrInstruction *ir_evaluate_bin_op_cmp(IrAnalyze *ira, ZigType *resolved_type, | |
| 13158 | ConstExprValue *op1_val, ConstExprValue *op2_val, IrInstructionBinOp *bin_op_instruction, IrBinOp op_id, | |
| 13159 | bool one_possible_value) { | |
| 13160 | if (op1_val->special == ConstValSpecialUndef || | |
| 13161 | op2_val->special == ConstValSpecialUndef) | |
| 13162 | return ir_const_undef(ira, &bin_op_instruction->base, resolved_type); | |
| 13163 | if (resolved_type->id == ZigTypeIdComptimeFloat || resolved_type->id == ZigTypeIdFloat) { | |
| 13164 | if (float_is_nan(op1_val) || float_is_nan(op2_val)) { | |
| 13165 | return ir_const_bool(ira, &bin_op_instruction->base, op_id == IrBinOpCmpNotEq); | |
| 13166 | } | |
| 13167 | Cmp cmp_result = float_cmp(op1_val, op2_val); | |
| 13168 | bool answer = resolve_cmp_op_id(op_id, cmp_result); | |
| 13169 | return ir_const_bool(ira, &bin_op_instruction->base, answer); | |
| 13170 | } else if (resolved_type->id == ZigTypeIdComptimeInt || resolved_type->id == ZigTypeIdInt) { | |
| 13171 | Cmp cmp_result = bigint_cmp(&op1_val->data.x_bigint, &op2_val->data.x_bigint); | |
| 13172 | bool answer = resolve_cmp_op_id(op_id, cmp_result); | |
| 13173 | return ir_const_bool(ira, &bin_op_instruction->base, answer); | |
| 13174 | } else if (resolved_type->id == ZigTypeIdPointer && op_id != IrBinOpCmpEq && op_id != IrBinOpCmpNotEq) { | |
| 13175 | if ((op1_val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr || | |
| 13176 | op1_val->data.x_ptr.special == ConstPtrSpecialNull) && | |
| 13177 | (op2_val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr || | |
| 13178 | op2_val->data.x_ptr.special == ConstPtrSpecialNull)) | |
| 13179 | { | |
| 13180 | uint64_t op1_addr = op1_val->data.x_ptr.special == ConstPtrSpecialNull ? | |
| 13181 | 0 : op1_val->data.x_ptr.data.hard_coded_addr.addr; | |
| 13182 | uint64_t op2_addr = op2_val->data.x_ptr.special == ConstPtrSpecialNull ? | |
| 13183 | 0 : op2_val->data.x_ptr.data.hard_coded_addr.addr; | |
| 13184 | Cmp cmp_result; | |
| 13185 | if (op1_addr > op2_addr) { | |
| 13186 | cmp_result = CmpGT; | |
| 13187 | } else if (op1_addr < op2_addr) { | |
| 13188 | cmp_result = CmpLT; | |
| 13189 | } else { | |
| 13190 | cmp_result = CmpEQ; | |
| 13191 | } | |
| 13192 | bool answer = resolve_cmp_op_id(op_id, cmp_result); | |
| 13193 | return ir_const_bool(ira, &bin_op_instruction->base, answer); | |
| 13194 | } | |
| 13195 | } else { | |
| 13196 | bool are_equal = one_possible_value || const_values_equal(ira->codegen, op1_val, op2_val); | |
| 13197 | bool answer; | |
| 13198 | if (op_id == IrBinOpCmpEq) { | |
| 13199 | answer = are_equal; | |
| 13200 | } else if (op_id == IrBinOpCmpNotEq) { | |
| 13201 | answer = !are_equal; | |
| 13202 | } else { | |
| 13203 | zig_unreachable(); | |
| 13204 | } | |
| 13205 | return ir_const_bool(ira, &bin_op_instruction->base, answer); | |
| 13206 | } | |
| 13207 | zig_unreachable(); | |
| 13208 | } | |
| 13209 | ||
| 13095 | 13210 | // Returns ErrorNotLazy when the value cannot be determined |
| 13096 | 13211 | static Error lazy_cmp_zero(AstNode *source_node, ConstExprValue *val, Cmp *result) { |
| 13097 | 13212 | Error err; |
| ... | ... | @@ -13477,51 +13592,22 @@ never_mind_just_calculate_it_normally: |
| 13477 | 13592 | ConstExprValue *op2_val = one_possible_value ? &casted_op2->value : ir_resolve_const(ira, casted_op2, UndefBad); |
| 13478 | 13593 | if (op2_val == nullptr) |
| 13479 | 13594 | return ira->codegen->invalid_instruction; |
| 13480 | ||
| 13481 | if (resolved_type->id == ZigTypeIdComptimeFloat || resolved_type->id == ZigTypeIdFloat) { | |
| 13482 | if (float_is_nan(op1_val) || float_is_nan(op2_val)) { | |
| 13483 | return ir_const_bool(ira, &bin_op_instruction->base, op_id == IrBinOpCmpNotEq); | |
| 13484 | } | |
| 13485 | Cmp cmp_result = float_cmp(op1_val, op2_val); | |
| 13486 | bool answer = resolve_cmp_op_id(op_id, cmp_result); | |
| 13487 | return ir_const_bool(ira, &bin_op_instruction->base, answer); | |
| 13488 | } else if (resolved_type->id == ZigTypeIdComptimeInt || resolved_type->id == ZigTypeIdInt) { | |
| 13489 | Cmp cmp_result = bigint_cmp(&op1_val->data.x_bigint, &op2_val->data.x_bigint); | |
| 13490 | bool answer = resolve_cmp_op_id(op_id, cmp_result); | |
| 13491 | return ir_const_bool(ira, &bin_op_instruction->base, answer); | |
| 13492 | } else if (resolved_type->id == ZigTypeIdPointer && op_id != IrBinOpCmpEq && op_id != IrBinOpCmpNotEq) { | |
| 13493 | if ((op1_val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr || | |
| 13494 | op1_val->data.x_ptr.special == ConstPtrSpecialNull) && | |
| 13495 | (op2_val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr || | |
| 13496 | op2_val->data.x_ptr.special == ConstPtrSpecialNull)) | |
| 13497 | { | |
| 13498 | uint64_t op1_addr = op1_val->data.x_ptr.special == ConstPtrSpecialNull ? | |
| 13499 | 0 : op1_val->data.x_ptr.data.hard_coded_addr.addr; | |
| 13500 | uint64_t op2_addr = op2_val->data.x_ptr.special == ConstPtrSpecialNull ? | |
| 13501 | 0 : op2_val->data.x_ptr.data.hard_coded_addr.addr; | |
| 13502 | Cmp cmp_result; | |
| 13503 | if (op1_addr > op2_addr) { | |
| 13504 | cmp_result = CmpGT; | |
| 13505 | } else if (op1_addr < op2_addr) { | |
| 13506 | cmp_result = CmpLT; | |
| 13507 | } else { | |
| 13508 | cmp_result = CmpEQ; | |
| 13509 | } | |
| 13510 | bool answer = resolve_cmp_op_id(op_id, cmp_result); | |
| 13511 | return ir_const_bool(ira, &bin_op_instruction->base, answer); | |
| 13512 | } | |
| 13513 | } else { | |
| 13514 | bool are_equal = one_possible_value || const_values_equal(ira->codegen, op1_val, op2_val); | |
| 13515 | bool answer; | |
| 13516 | if (op_id == IrBinOpCmpEq) { | |
| 13517 | answer = are_equal; | |
| 13518 | } else if (op_id == IrBinOpCmpNotEq) { | |
| 13519 | answer = !are_equal; | |
| 13520 | } else { | |
| 13521 | zig_unreachable(); | |
| 13522 | } | |
| 13523 | return ir_const_bool(ira, &bin_op_instruction->base, answer); | |
| 13595 | if (resolved_type->id != ZigTypeIdVector) | |
| 13596 | return ir_evaluate_bin_op_cmp(ira, resolved_type, op1_val, op2_val, bin_op_instruction, op_id, one_possible_value); | |
| 13597 | IrInstruction *result = ir_const(ira, &bin_op_instruction->base, | |
| 13598 | get_vector_type(ira->codegen, resolved_type->data.vector.len, ira->codegen->builtin_types.entry_bool)); | |
| 13599 | result->value.data.x_array.data.s_none.elements = | |
| 13600 | create_const_vals(resolved_type->data.vector.len); | |
| 13601 | ||
| 13602 | expand_undef_array(ira->codegen, &result->value); | |
| 13603 | for (size_t i = 0;i < resolved_type->data.vector.len;i++) { | |
| 13604 | IrInstruction *cur_res = ir_evaluate_bin_op_cmp(ira, resolved_type->data.vector.elem_type, | |
| 13605 | &op1_val->data.x_array.data.s_none.elements[i], | |
| 13606 | &op2_val->data.x_array.data.s_none.elements[i], | |
| 13607 | bin_op_instruction, op_id, one_possible_value); | |
| 13608 | copy_const_val(&result->value.data.x_array.data.s_none.elements[i], &cur_res->value, false); | |
| 13524 | 13609 | } |
| 13610 | return result; | |
| 13525 | 13611 | } |
| 13526 | 13612 | |
| 13527 | 13613 | // some comparisons with unsigned numbers can be evaluated |
| ... | ... | @@ -13564,7 +13650,12 @@ never_mind_just_calculate_it_normally: |
| 13564 | 13650 | IrInstruction *result = ir_build_bin_op(&ira->new_irb, |
| 13565 | 13651 | bin_op_instruction->base.scope, bin_op_instruction->base.source_node, |
| 13566 | 13652 | op_id, casted_op1, casted_op2, bin_op_instruction->safety_check_on); |
| 13567 | result->value.type = ira->codegen->builtin_types.entry_bool; | |
| 13653 | if (resolved_type->id == ZigTypeIdVector) { | |
| 13654 | result->value.type = get_vector_type(ira->codegen, resolved_type->data.vector.len, | |
| 13655 | ira->codegen->builtin_types.entry_bool); | |
| 13656 | } else { | |
| 13657 | result->value.type = ira->codegen->builtin_types.entry_bool; | |
| 13658 | } | |
| 13568 | 13659 | return result; |
| 13569 | 13660 | } |
| 13570 | 13661 | |
| ... | ... | @@ -22018,20 +22109,212 @@ static IrInstruction *ir_analyze_instruction_vector_type(IrAnalyze *ira, IrInstr |
| 22018 | 22109 | if (!ir_resolve_unsigned(ira, instruction->len->child, ira->codegen->builtin_types.entry_u32, &len)) |
| 22019 | 22110 | return ira->codegen->invalid_instruction; |
| 22020 | 22111 | |
| 22021 | ZigType *elem_type = ir_resolve_type(ira, instruction->elem_type->child); | |
| 22112 | ZigType *elem_type = ir_resolve_vector_elem_type(ira, instruction->elem_type->child); | |
| 22022 | 22113 | if (type_is_invalid(elem_type)) |
| 22023 | 22114 | return ira->codegen->invalid_instruction; |
| 22024 | 22115 | |
| 22025 | if (!is_valid_vector_elem_type(elem_type)) { | |
| 22026 | ir_add_error(ira, instruction->elem_type, | |
| 22027 | buf_sprintf("vector element type must be integer, float, or pointer; '%s' is invalid", | |
| 22028 | buf_ptr(&elem_type->name))); | |
| 22116 | ZigType *vector_type = get_vector_type(ira->codegen, len, elem_type); | |
| 22117 | ||
| 22118 | return ir_const_type(ira, &instruction->base, vector_type); | |
| 22119 | } | |
| 22120 | ||
| 22121 | static IrInstruction *ir_analyze_shuffle_vector(IrAnalyze *ira, IrInstruction *source_instr, | |
| 22122 | ZigType *scalar_type, IrInstruction *a, IrInstruction *b, IrInstruction *mask) | |
| 22123 | { | |
| 22124 | ir_assert(source_instr && scalar_type && a && b && mask, source_instr); | |
| 22125 | ir_assert(is_valid_vector_elem_type(scalar_type), source_instr); | |
| 22126 | ||
| 22127 | uint32_t len_mask; | |
| 22128 | if (mask->value.type->id == ZigTypeIdVector) { | |
| 22129 | len_mask = mask->value.type->data.vector.len; | |
| 22130 | } else if (mask->value.type->id == ZigTypeIdArray) { | |
| 22131 | len_mask = mask->value.type->data.array.len; | |
| 22132 | } else { | |
| 22133 | ir_add_error(ira, mask, | |
| 22134 | buf_sprintf("expected vector or array, found '%s'", | |
| 22135 | buf_ptr(&mask->value.type->name))); | |
| 22029 | 22136 | return ira->codegen->invalid_instruction; |
| 22030 | 22137 | } |
| 22138 | mask = ir_implicit_cast(ira, mask, get_vector_type(ira->codegen, len_mask, | |
| 22139 | ira->codegen->builtin_types.entry_i32)); | |
| 22140 | if (type_is_invalid(mask->value.type)) | |
| 22141 | return ira->codegen->invalid_instruction; | |
| 22031 | 22142 | |
| 22032 | ZigType *vector_type = get_vector_type(ira->codegen, len, elem_type); | |
| 22143 | uint32_t len_a; | |
| 22144 | if (a->value.type->id == ZigTypeIdVector) { | |
| 22145 | len_a = a->value.type->data.vector.len; | |
| 22146 | } else if (a->value.type->id == ZigTypeIdArray) { | |
| 22147 | len_a = a->value.type->data.array.len; | |
| 22148 | } else if (a->value.type->id == ZigTypeIdUndefined) { | |
| 22149 | len_a = UINT32_MAX; | |
| 22150 | } else { | |
| 22151 | ir_add_error(ira, a, | |
| 22152 | buf_sprintf("expected vector or array with element type '%s', found '%s'", | |
| 22153 | buf_ptr(&scalar_type->name), | |
| 22154 | buf_ptr(&a->value.type->name))); | |
| 22155 | return ira->codegen->invalid_instruction; | |
| 22156 | } | |
| 22033 | 22157 | |
| 22034 | return ir_const_type(ira, &instruction->base, vector_type); | |
| 22158 | uint32_t len_b; | |
| 22159 | if (b->value.type->id == ZigTypeIdVector) { | |
| 22160 | len_b = b->value.type->data.vector.len; | |
| 22161 | } else if (b->value.type->id == ZigTypeIdArray) { | |
| 22162 | len_b = b->value.type->data.array.len; | |
| 22163 | } else if (b->value.type->id == ZigTypeIdUndefined) { | |
| 22164 | len_b = UINT32_MAX; | |
| 22165 | } else { | |
| 22166 | ir_add_error(ira, b, | |
| 22167 | buf_sprintf("expected vector or array with element type '%s', found '%s'", | |
| 22168 | buf_ptr(&scalar_type->name), | |
| 22169 | buf_ptr(&b->value.type->name))); | |
| 22170 | return ira->codegen->invalid_instruction; | |
| 22171 | } | |
| 22172 | ||
| 22173 | if (len_a == UINT32_MAX && len_b == UINT32_MAX) { | |
| 22174 | return ir_const_undef(ira, a, get_vector_type(ira->codegen, len_mask, scalar_type)); | |
| 22175 | } | |
| 22176 | ||
| 22177 | if (len_a == UINT32_MAX) { | |
| 22178 | len_a = len_b; | |
| 22179 | a = ir_const_undef(ira, a, get_vector_type(ira->codegen, len_a, scalar_type)); | |
| 22180 | } else { | |
| 22181 | a = ir_implicit_cast(ira, a, get_vector_type(ira->codegen, len_a, scalar_type)); | |
| 22182 | if (type_is_invalid(a->value.type)) | |
| 22183 | return ira->codegen->invalid_instruction; | |
| 22184 | } | |
| 22185 | ||
| 22186 | if (len_b == UINT32_MAX) { | |
| 22187 | len_b = len_a; | |
| 22188 | b = ir_const_undef(ira, b, get_vector_type(ira->codegen, len_b, scalar_type)); | |
| 22189 | } else { | |
| 22190 | b = ir_implicit_cast(ira, b, get_vector_type(ira->codegen, len_b, scalar_type)); | |
| 22191 | if (type_is_invalid(b->value.type)) | |
| 22192 | return ira->codegen->invalid_instruction; | |
| 22193 | } | |
| 22194 | ||
| 22195 | ConstExprValue *mask_val = ir_resolve_const(ira, mask, UndefOk); | |
| 22196 | if (mask_val == nullptr) | |
| 22197 | return ira->codegen->invalid_instruction; | |
| 22198 | ||
| 22199 | expand_undef_array(ira->codegen, mask_val); | |
| 22200 | ||
| 22201 | for (uint32_t i = 0; i < len_mask; i += 1) { | |
| 22202 | ConstExprValue *mask_elem_val = &mask_val->data.x_array.data.s_none.elements[i]; | |
| 22203 | if (mask_elem_val->special == ConstValSpecialUndef) | |
| 22204 | continue; | |
| 22205 | int32_t v_i32 = bigint_as_signed(&mask_elem_val->data.x_bigint); | |
| 22206 | uint32_t v; | |
| 22207 | IrInstruction *chosen_operand; | |
| 22208 | if (v_i32 >= 0) { | |
| 22209 | v = (uint32_t)v_i32; | |
| 22210 | chosen_operand = a; | |
| 22211 | } else { | |
| 22212 | v = (uint32_t)~v_i32; | |
| 22213 | chosen_operand = b; | |
| 22214 | } | |
| 22215 | if (v >= chosen_operand->value.type->data.vector.len) { | |
| 22216 | ErrorMsg *msg = ir_add_error(ira, mask, | |
| 22217 | buf_sprintf("mask index '%u' has out-of-bounds selection", i)); | |
| 22218 | add_error_note(ira->codegen, msg, chosen_operand->source_node, | |
| 22219 | buf_sprintf("selected index '%u' out of bounds of %s", v, | |
| 22220 | buf_ptr(&chosen_operand->value.type->name))); | |
| 22221 | if (chosen_operand == a && v < len_a + len_b) { | |
| 22222 | add_error_note(ira->codegen, msg, b->source_node, | |
| 22223 | buf_create_from_str("selections from the second vector are specified with negative numbers")); | |
| 22224 | } | |
| 22225 | return ira->codegen->invalid_instruction; | |
| 22226 | } | |
| 22227 | } | |
| 22228 | ||
| 22229 | ZigType *result_type = get_vector_type(ira->codegen, len_mask, scalar_type); | |
| 22230 | if (instr_is_comptime(a) && instr_is_comptime(b)) { | |
| 22231 | ConstExprValue *a_val = ir_resolve_const(ira, a, UndefOk); | |
| 22232 | if (a_val == nullptr) | |
| 22233 | return ira->codegen->invalid_instruction; | |
| 22234 | ||
| 22235 | ConstExprValue *b_val = ir_resolve_const(ira, b, UndefOk); | |
| 22236 | if (b_val == nullptr) | |
| 22237 | return ira->codegen->invalid_instruction; | |
| 22238 | ||
| 22239 | expand_undef_array(ira->codegen, a_val); | |
| 22240 | expand_undef_array(ira->codegen, b_val); | |
| 22241 | ||
| 22242 | IrInstruction *result = ir_const(ira, source_instr, result_type); | |
| 22243 | result->value.data.x_array.data.s_none.elements = create_const_vals(len_mask); | |
| 22244 | for (uint32_t i = 0; i < mask_val->type->data.vector.len; i += 1) { | |
| 22245 | ConstExprValue *mask_elem_val = &mask_val->data.x_array.data.s_none.elements[i]; | |
| 22246 | ConstExprValue *result_elem_val = &result->value.data.x_array.data.s_none.elements[i]; | |
| 22247 | if (mask_elem_val->special == ConstValSpecialUndef) { | |
| 22248 | result_elem_val->special = ConstValSpecialUndef; | |
| 22249 | continue; | |
| 22250 | } | |
| 22251 | int32_t v = bigint_as_signed(&mask_elem_val->data.x_bigint); | |
| 22252 | // We've already checked for and emitted compile errors for index out of bounds here. | |
| 22253 | ConstExprValue *src_elem_val = (v >= 0) ? | |
| 22254 | &a->value.data.x_array.data.s_none.elements[v] : | |
| 22255 | &b->value.data.x_array.data.s_none.elements[~v]; | |
| 22256 | copy_const_val(result_elem_val, src_elem_val, false); | |
| 22257 | ||
| 22258 | ir_assert(result_elem_val->special == ConstValSpecialStatic, source_instr); | |
| 22259 | } | |
| 22260 | result->value.special = ConstValSpecialStatic; | |
| 22261 | return result; | |
| 22262 | } | |
| 22263 | ||
| 22264 | // All static analysis passed, and not comptime. | |
| 22265 | // For runtime codegen, vectors a and b must be the same length. Here we | |
| 22266 | // recursively @shuffle the smaller vector to append undefined elements | |
| 22267 | // to it up to the length of the longer vector. This recursion terminates | |
| 22268 | // in 1 call because these calls to ir_analyze_shuffle_vector guarantee | |
| 22269 | // len_a == len_b. | |
| 22270 | if (len_a != len_b) { | |
| 22271 | uint32_t len_min = min(len_a, len_b); | |
| 22272 | uint32_t len_max = max(len_a, len_b); | |
| 22273 | ||
| 22274 | IrInstruction *expand_mask = ir_const(ira, mask, | |
| 22275 | get_vector_type(ira->codegen, len_max, ira->codegen->builtin_types.entry_i32)); | |
| 22276 | expand_mask->value.data.x_array.data.s_none.elements = create_const_vals(len_max); | |
| 22277 | uint32_t i = 0; | |
| 22278 | for (; i < len_min; i += 1) | |
| 22279 | bigint_init_unsigned(&expand_mask->value.data.x_array.data.s_none.elements[i].data.x_bigint, i); | |
| 22280 | for (; i < len_max; i += 1) | |
| 22281 | bigint_init_signed(&expand_mask->value.data.x_array.data.s_none.elements[i].data.x_bigint, -1); | |
| 22282 | ||
| 22283 | IrInstruction *undef = ir_const_undef(ira, source_instr, | |
| 22284 | get_vector_type(ira->codegen, len_min, scalar_type)); | |
| 22285 | ||
| 22286 | if (len_b < len_a) { | |
| 22287 | b = ir_analyze_shuffle_vector(ira, source_instr, scalar_type, b, undef, expand_mask); | |
| 22288 | } else { | |
| 22289 | a = ir_analyze_shuffle_vector(ira, source_instr, scalar_type, a, undef, expand_mask); | |
| 22290 | } | |
| 22291 | } | |
| 22292 | ||
| 22293 | IrInstruction *result = ir_build_shuffle_vector(&ira->new_irb, | |
| 22294 | source_instr->scope, source_instr->source_node, | |
| 22295 | nullptr, a, b, mask); | |
| 22296 | result->value.type = result_type; | |
| 22297 | return result; | |
| 22298 | } | |
| 22299 | ||
| 22300 | static IrInstruction *ir_analyze_instruction_shuffle_vector(IrAnalyze *ira, IrInstructionShuffleVector *instruction) { | |
| 22301 | ZigType *scalar_type = ir_resolve_vector_elem_type(ira, instruction->scalar_type); | |
| 22302 | if (type_is_invalid(scalar_type)) | |
| 22303 | return ira->codegen->invalid_instruction; | |
| 22304 | ||
| 22305 | IrInstruction *a = instruction->a->child; | |
| 22306 | if (type_is_invalid(a->value.type)) | |
| 22307 | return ira->codegen->invalid_instruction; | |
| 22308 | ||
| 22309 | IrInstruction *b = instruction->b->child; | |
| 22310 | if (type_is_invalid(b->value.type)) | |
| 22311 | return ira->codegen->invalid_instruction; | |
| 22312 | ||
| 22313 | IrInstruction *mask = instruction->mask->child; | |
| 22314 | if (type_is_invalid(mask->value.type)) | |
| 22315 | return ira->codegen->invalid_instruction; | |
| 22316 | ||
| 22317 | return ir_analyze_shuffle_vector(ira, &instruction->base, scalar_type, a, b, mask); | |
| 22035 | 22318 | } |
| 22036 | 22319 | |
| 22037 | 22320 | static IrInstruction *ir_analyze_instruction_bool_not(IrAnalyze *ira, IrInstructionBoolNot *instruction) { |
| ... | ... | @@ -25578,6 +25861,8 @@ static IrInstruction *ir_analyze_instruction_base(IrAnalyze *ira, IrInstruction |
| 25578 | 25861 | return ir_analyze_instruction_int_type(ira, (IrInstructionIntType *)instruction); |
| 25579 | 25862 | case IrInstructionIdVectorType: |
| 25580 | 25863 | return ir_analyze_instruction_vector_type(ira, (IrInstructionVectorType *)instruction); |
| 25864 | case IrInstructionIdShuffleVector: | |
| 25865 | return ir_analyze_instruction_shuffle_vector(ira, (IrInstructionShuffleVector *)instruction); | |
| 25581 | 25866 | case IrInstructionIdBoolNot: |
| 25582 | 25867 | return ir_analyze_instruction_bool_not(ira, (IrInstructionBoolNot *)instruction); |
| 25583 | 25868 | case IrInstructionIdMemset: |
| ... | ... | @@ -25913,6 +26198,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { |
| 25913 | 26198 | case IrInstructionIdTruncate: |
| 25914 | 26199 | case IrInstructionIdIntType: |
| 25915 | 26200 | case IrInstructionIdVectorType: |
| 26201 | case IrInstructionIdShuffleVector: | |
| 25916 | 26202 | case IrInstructionIdBoolNot: |
| 25917 | 26203 | case IrInstructionIdSliceSrc: |
| 25918 | 26204 | case IrInstructionIdMemberCount: |
src/ir_print.cpp+17| ... | ... | @@ -42,6 +42,8 @@ static const char* ir_instruction_type_str(IrInstruction* instruction) { |
| 42 | 42 | switch (instruction->id) { |
| 43 | 43 | case IrInstructionIdInvalid: |
| 44 | 44 | return "Invalid"; |
| 45 | case IrInstructionIdShuffleVector: | |
| 46 | return "Shuffle"; | |
| 45 | 47 | case IrInstructionIdDeclVarSrc: |
| 46 | 48 | return "DeclVarSrc"; |
| 47 | 49 | case IrInstructionIdDeclVarGen: |
| ... | ... | @@ -1208,6 +1210,18 @@ static void ir_print_vector_type(IrPrint *irp, IrInstructionVectorType *instruct |
| 1208 | 1210 | fprintf(irp->f, ")"); |
| 1209 | 1211 | } |
| 1210 | 1212 | |
| 1213 | static void ir_print_shuffle_vector(IrPrint *irp, IrInstructionShuffleVector *instruction) { | |
| 1214 | fprintf(irp->f, "@shuffle("); | |
| 1215 | ir_print_other_instruction(irp, instruction->scalar_type); | |
| 1216 | fprintf(irp->f, ", "); | |
| 1217 | ir_print_other_instruction(irp, instruction->a); | |
| 1218 | fprintf(irp->f, ", "); | |
| 1219 | ir_print_other_instruction(irp, instruction->b); | |
| 1220 | fprintf(irp->f, ", "); | |
| 1221 | ir_print_other_instruction(irp, instruction->mask); | |
| 1222 | fprintf(irp->f, ")"); | |
| 1223 | } | |
| 1224 | ||
| 1211 | 1225 | static void ir_print_bool_not(IrPrint *irp, IrInstructionBoolNot *instruction) { |
| 1212 | 1226 | fprintf(irp->f, "! "); |
| 1213 | 1227 | ir_print_other_instruction(irp, instruction->value); |
| ... | ... | @@ -2143,6 +2157,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction, bool |
| 2143 | 2157 | case IrInstructionIdVectorType: |
| 2144 | 2158 | ir_print_vector_type(irp, (IrInstructionVectorType *)instruction); |
| 2145 | 2159 | break; |
| 2160 | case IrInstructionIdShuffleVector: | |
| 2161 | ir_print_shuffle_vector(irp, (IrInstructionShuffleVector *)instruction); | |
| 2162 | break; | |
| 2146 | 2163 | case IrInstructionIdBoolNot: |
| 2147 | 2164 | ir_print_bool_not(irp, (IrInstructionBoolNot *)instruction); |
| 2148 | 2165 | break; |
std/hash/auto_hash.zig+6-3| ... | ... | @@ -116,7 +116,7 @@ pub fn hash(hasher: var, key: var, comptime strat: HashStrategy) void { |
| 116 | 116 | // Otherwise, hash every element. |
| 117 | 117 | // TODO remove the copy to an array once field access is done. |
| 118 | 118 | const array: [info.len]info.child = key; |
| 119 | comptime var i: u32 = 0; | |
| 119 | comptime var i = 0; | |
| 120 | 120 | inline while (i < info.len) : (i += 1) { |
| 121 | 121 | hash(hasher, array[i], strat); |
| 122 | 122 | } |
| ... | ... | @@ -357,10 +357,13 @@ test "testHash union" { |
| 357 | 357 | test "testHash vector" { |
| 358 | 358 | const a: @Vector(4, u32) = [_]u32{ 1, 2, 3, 4 }; |
| 359 | 359 | const b: @Vector(4, u32) = [_]u32{ 1, 2, 3, 5 }; |
| 360 | const c: @Vector(4, u31) = [_]u31{ 1, 2, 3, 4 }; | |
| 361 | 360 | testing.expect(testHash(a) == testHash(a)); |
| 362 | 361 | testing.expect(testHash(a) != testHash(b)); |
| 363 | testing.expect(testHash(a) != testHash(c)); | |
| 362 | ||
| 363 | const c: @Vector(4, u31) = [_]u31{ 1, 2, 3, 4 }; | |
| 364 | const d: @Vector(4, u31) = [_]u31{ 1, 2, 3, 5 }; | |
| 365 | testing.expect(testHash(c) == testHash(c)); | |
| 366 | testing.expect(testHash(c) != testHash(d)); | |
| 364 | 367 | } |
| 365 | 368 | |
| 366 | 369 | test "testHash error union" { |
test/compile_errors.zig+14-1| ... | ... | @@ -6484,6 +6484,19 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 6484 | 6484 | "tmp.zig:7:23: error: unable to evaluate constant expression", |
| 6485 | 6485 | ); |
| 6486 | 6486 | |
| 6487 | cases.addTest( | |
| 6488 | "@shuffle with selected index past first vector length", | |
| 6489 | \\export fn entry() void { | |
| 6490 | \\ const v: @Vector(4, u32) = [4]u32{ 10, 11, 12, 13 }; | |
| 6491 | \\ const x: @Vector(4, u32) = [4]u32{ 14, 15, 16, 17 }; | |
| 6492 | \\ var z = @shuffle(u32, v, x, [8]i32{ 0, 1, 2, 3, 7, 6, 5, 4 }); | |
| 6493 | \\} | |
| 6494 | , | |
| 6495 | "tmp.zig:4:39: error: mask index '4' has out-of-bounds selection", | |
| 6496 | "tmp.zig:4:27: note: selected index '7' out of bounds of @Vector(4, u32)", | |
| 6497 | "tmp.zig:4:30: note: selections from the second vector are specified with negative numbers", | |
| 6498 | ); | |
| 6499 | ||
| 6487 | 6500 | cases.addTest( |
| 6488 | 6501 | "nested vectors", |
| 6489 | 6502 | \\export fn entry() void { |
| ... | ... | @@ -6491,7 +6504,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 6491 | 6504 | \\ var v: V = undefined; |
| 6492 | 6505 | \\} |
| 6493 | 6506 | , |
| 6494 | "tmp.zig:2:26: error: vector element type must be integer, float, or pointer; '@Vector(4, u8)' is invalid", | |
| 6507 | "tmp.zig:2:26: error: vector element type must be integer, float, bool, or pointer; '@Vector(4, u8)' is invalid", | |
| 6495 | 6508 | ); |
| 6496 | 6509 | |
| 6497 | 6510 | cases.add("compileLog of tagged enum doesn't crash the compiler", |
test/stage1/behavior.zig+1| ... | ... | @@ -80,6 +80,7 @@ comptime { |
| 80 | 80 | _ = @import("behavior/pub_enum.zig"); |
| 81 | 81 | _ = @import("behavior/ref_var_in_if_after_if_2nd_switch_prong.zig"); |
| 82 | 82 | _ = @import("behavior/reflection.zig"); |
| 83 | _ = @import("behavior/shuffle.zig"); | |
| 83 | 84 | _ = @import("behavior/sizeof_and_typeof.zig"); |
| 84 | 85 | _ = @import("behavior/slice.zig"); |
| 85 | 86 | _ = @import("behavior/slicetobytes.zig"); |
test/stage1/behavior/shuffle.zig created+57| ... | ... | @@ -0,0 +1,57 @@ |
| 1 | const std = @import("std"); | |
| 2 | const mem = std.mem; | |
| 3 | const expect = std.testing.expect; | |
| 4 | ||
| 5 | test "@shuffle" { | |
| 6 | const S = struct { | |
| 7 | fn doTheTest() void { | |
| 8 | var v: @Vector(4, i32) = [4]i32{ 2147483647, -2, 30, 40 }; | |
| 9 | var x: @Vector(4, i32) = [4]i32{ 1, 2147483647, 3, 4 }; | |
| 10 | const mask: @Vector(4, i32) = [4]i32{ 0, ~i32(2), 3, ~i32(3) }; | |
| 11 | var res = @shuffle(i32, v, x, mask); | |
| 12 | expect(mem.eql(i32, ([4]i32)(res), [4]i32{ 2147483647, 3, 40, 4 })); | |
| 13 | ||
| 14 | // Implicit cast from array (of mask) | |
| 15 | res = @shuffle(i32, v, x, [4]i32{ 0, ~i32(2), 3, ~i32(3) }); | |
| 16 | expect(mem.eql(i32, ([4]i32)(res), [4]i32{ 2147483647, 3, 40, 4 })); | |
| 17 | ||
| 18 | // Undefined | |
| 19 | const mask2: @Vector(4, i32) = [4]i32{ 3, 1, 2, 0 }; | |
| 20 | res = @shuffle(i32, v, undefined, mask2); | |
| 21 | expect(mem.eql(i32, ([4]i32)(res), [4]i32{ 40, -2, 30, 2147483647 })); | |
| 22 | ||
| 23 | // Upcasting of b | |
| 24 | var v2: @Vector(2, i32) = [2]i32{ 2147483647, undefined }; | |
| 25 | const mask3: @Vector(4, i32) = [4]i32{ ~i32(0), 2, ~i32(0), 3 }; | |
| 26 | res = @shuffle(i32, x, v2, mask3); | |
| 27 | expect(mem.eql(i32, ([4]i32)(res), [4]i32{ 2147483647, 3, 2147483647, 4 })); | |
| 28 | ||
| 29 | // Upcasting of a | |
| 30 | var v3: @Vector(2, i32) = [2]i32{ 2147483647, -2 }; | |
| 31 | const mask4: @Vector(4, i32) = [4]i32{ 0, ~i32(2), 1, ~i32(3) }; | |
| 32 | res = @shuffle(i32, v3, x, mask4); | |
| 33 | expect(mem.eql(i32, ([4]i32)(res), [4]i32{ 2147483647, 3, -2, 4 })); | |
| 34 | ||
| 35 | // bool | |
| 36 | { | |
| 37 | var x2: @Vector(4, bool) = [4]bool{ false, true, false, true }; | |
| 38 | var v4: @Vector(2, bool) = [2]bool{ true, false }; | |
| 39 | const mask5: @Vector(4, i32) = [4]i32{ 0, ~i32(1), 1, 2 }; | |
| 40 | var res2 = @shuffle(bool, x2, v4, mask5); | |
| 41 | expect(mem.eql(bool, ([4]bool)(res2), [4]bool{ false, false, true, false })); | |
| 42 | } | |
| 43 | ||
| 44 | // TODO re-enable when LLVM codegen is fixed | |
| 45 | // https://github.com/ziglang/zig/issues/3246 | |
| 46 | if (false) { | |
| 47 | var x2: @Vector(3, bool) = [3]bool{ false, true, false }; | |
| 48 | var v4: @Vector(2, bool) = [2]bool{ true, false }; | |
| 49 | const mask5: @Vector(4, i32) = [4]i32{ 0, ~i32(1), 1, 2 }; | |
| 50 | var res2 = @shuffle(bool, x2, v4, mask5); | |
| 51 | expect(mem.eql(bool, ([4]bool)(res2), [4]bool{ false, false, true, false })); | |
| 52 | } | |
| 53 | } | |
| 54 | }; | |
| 55 | S.doTheTest(); | |
| 56 | comptime S.doTheTest(); | |
| 57 | } |
test/stage1/behavior/vector.zig+58| ... | ... | @@ -2,6 +2,18 @@ const std = @import("std"); |
| 2 | 2 | const mem = std.mem; |
| 3 | 3 | const expect = std.testing.expect; |
| 4 | 4 | |
| 5 | test "implicit cast vector to array - bool" { | |
| 6 | const S = struct { | |
| 7 | fn doTheTest() void { | |
| 8 | const a: @Vector(4, bool) = [_]bool{ true, false, true, false }; | |
| 9 | const result_array: [4]bool = a; | |
| 10 | expect(mem.eql(bool, result_array, [4]bool{ true, false, true, false })); | |
| 11 | } | |
| 12 | }; | |
| 13 | S.doTheTest(); | |
| 14 | comptime S.doTheTest(); | |
| 15 | } | |
| 16 | ||
| 5 | 17 | test "vector wrap operators" { |
| 6 | 18 | const S = struct { |
| 7 | 19 | fn doTheTest() void { |
| ... | ... | @@ -18,6 +30,23 @@ test "vector wrap operators" { |
| 18 | 30 | comptime S.doTheTest(); |
| 19 | 31 | } |
| 20 | 32 | |
| 33 | test "vector bin compares with mem.eql" { | |
| 34 | const S = struct { | |
| 35 | fn doTheTest() void { | |
| 36 | var v: @Vector(4, i32) = [4]i32{ 2147483647, -2, 30, 40 }; | |
| 37 | var x: @Vector(4, i32) = [4]i32{ 1, 2147483647, 30, 4 }; | |
| 38 | expect(mem.eql(bool, ([4]bool)(v == x), [4]bool{ false, false, true, false})); | |
| 39 | expect(mem.eql(bool, ([4]bool)(v != x), [4]bool{ true, true, false, true})); | |
| 40 | expect(mem.eql(bool, ([4]bool)(v < x), [4]bool{ false, true, false, false})); | |
| 41 | expect(mem.eql(bool, ([4]bool)(v > x), [4]bool{ true, false, false, true})); | |
| 42 | expect(mem.eql(bool, ([4]bool)(v <= x), [4]bool{ false, true, true, false})); | |
| 43 | expect(mem.eql(bool, ([4]bool)(v >= x), [4]bool{ true, false, true, true})); | |
| 44 | } | |
| 45 | }; | |
| 46 | S.doTheTest(); | |
| 47 | comptime S.doTheTest(); | |
| 48 | } | |
| 49 | ||
| 21 | 50 | test "vector int operators" { |
| 22 | 51 | const S = struct { |
| 23 | 52 | fn doTheTest() void { |
| ... | ... | @@ -80,3 +109,32 @@ test "array to vector" { |
| 80 | 109 | var arr = [4]f32{ foo, 1.5, 0.0, 0.0 }; |
| 81 | 110 | var vec: @Vector(4, f32) = arr; |
| 82 | 111 | } |
| 112 | ||
| 113 | test "vector casts of sizes not divisable by 8" { | |
| 114 | const S = struct { | |
| 115 | fn doTheTest() void { | |
| 116 | { | |
| 117 | var v: @Vector(4, u3) = [4]u3{ 5, 2, 3, 0}; | |
| 118 | var x: [4]u3 = v; | |
| 119 | expect(mem.eql(u3, x, ([4]u3)(v))); | |
| 120 | } | |
| 121 | { | |
| 122 | var v: @Vector(4, u2) = [4]u2{ 1, 2, 3, 0}; | |
| 123 | var x: [4]u2 = v; | |
| 124 | expect(mem.eql(u2, x, ([4]u2)(v))); | |
| 125 | } | |
| 126 | { | |
| 127 | var v: @Vector(4, u1) = [4]u1{ 1, 0, 1, 0}; | |
| 128 | var x: [4]u1 = v; | |
| 129 | expect(mem.eql(u1, x, ([4]u1)(v))); | |
| 130 | } | |
| 131 | { | |
| 132 | var v: @Vector(4, bool) = [4]bool{ false, false, true, false}; | |
| 133 | var x: [4]bool = v; | |
| 134 | expect(mem.eql(bool, x, ([4]bool)(v))); | |
| 135 | } | |
| 136 | } | |
| 137 | }; | |
| 138 | S.doTheTest(); | |
| 139 | comptime S.doTheTest(); | |
| 140 | } |