authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-04-05 18:34:47-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-04-05 18:34:47-04:00
log05b587fcdee91a7c9f170da4a186a512b51b39a8
tree638c7444a2f73fc304b662bf50329ccc18d2c86c
parente2dc63644ab3d8e5cdaec2d58dc57c587295081f
parente84b9b70ff2814d6e50a851dc9f094b15399d2fe
signaturelock-open Commit is signed but in an unrecognized format.

Merge branch 'LemonBoy-vec-div'

closes #4737

5 files changed, 644 insertions(+), 221 deletions(-)

lib/std/target.zig+2-5
......@@ -501,11 +501,8 @@ pub const Target = struct {
501501
502502 /// Removes the specified feature but not its dependents.
503503 pub fn removeFeatureSet(set: *Set, other_set: Set) void {
504 // TODO should be able to use binary not on @Vector type.
505 // https://github.com/ziglang/zig/issues/903
506 for (set.ints) |*int, i| {
507 int.* &= ~other_set.ints[i];
508 }
504 set.ints = @as(@Vector(usize_count, usize), set.ints) &
505 ~@as(@Vector(usize_count, usize), other_set.ints);
509506 }
510507
511508 pub fn populateDependencies(set: *Set, all_features_list: []const Cpu.Feature) void {
src/codegen.cpp+150-59
......@@ -2535,19 +2535,51 @@ static LLVMValueRef ir_render_return(CodeGen *g, IrExecutableGen *executable, Ir
25352535 return nullptr;
25362536}
25372537
2538static LLVMValueRef gen_overflow_shl_op(CodeGen *g, ZigType *type_entry,
2539 LLVMValueRef val1, LLVMValueRef val2)
2538enum class ScalarizePredicate {
2539 // Returns true iff all the elements in the vector are 1.
2540 // Equivalent to folding all the bits with `and`.
2541 All,
2542 // Returns true iff there's at least one element in the vector that is 1.
2543 // Equivalent to folding all the bits with `or`.
2544 Any,
2545};
2546
2547// Collapses a <N x i1> vector into a single i1 according to the given predicate
2548static LLVMValueRef scalarize_cmp_result(CodeGen *g, LLVMValueRef val, ScalarizePredicate predicate) {
2549 assert(LLVMGetTypeKind(LLVMTypeOf(val)) == LLVMVectorTypeKind);
2550 LLVMTypeRef scalar_type = LLVMIntType(LLVMGetVectorSize(LLVMTypeOf(val)));
2551 LLVMValueRef casted = LLVMBuildBitCast(g->builder, val, scalar_type, "");
2552
2553 switch (predicate) {
2554 case ScalarizePredicate::Any: {
2555 LLVMValueRef all_zeros = LLVMConstNull(scalar_type);
2556 return LLVMBuildICmp(g->builder, LLVMIntNE, casted, all_zeros, "");
2557 }
2558 case ScalarizePredicate::All: {
2559 LLVMValueRef all_ones = LLVMConstAllOnes(scalar_type);
2560 return LLVMBuildICmp(g->builder, LLVMIntEQ, casted, all_ones, "");
2561 }
2562 }
2563
2564 zig_unreachable();
2565}
2566
2567
2568static LLVMValueRef gen_overflow_shl_op(CodeGen *g, ZigType *operand_type,
2569 LLVMValueRef val1, LLVMValueRef val2)
25402570{
25412571 // for unsigned left shifting, we do the lossy shift, then logically shift
25422572 // right the same number of bits
25432573 // if the values don't match, we have an overflow
25442574 // for signed left shifting we do the same except arithmetic shift right
2575 ZigType *scalar_type = (operand_type->id == ZigTypeIdVector) ?
2576 operand_type->data.vector.elem_type : operand_type;
25452577
2546 assert(type_entry->id == ZigTypeIdInt);
2578 assert(scalar_type->id == ZigTypeIdInt);
25472579
25482580 LLVMValueRef result = LLVMBuildShl(g->builder, val1, val2, "");
25492581 LLVMValueRef orig_val;
2550 if (type_entry->data.integral.is_signed) {
2582 if (scalar_type->data.integral.is_signed) {
25512583 orig_val = LLVMBuildAShr(g->builder, result, val2, "");
25522584 } else {
25532585 orig_val = LLVMBuildLShr(g->builder, result, val2, "");
......@@ -2556,6 +2588,9 @@ static LLVMValueRef gen_overflow_shl_op(CodeGen *g, ZigType *type_entry,
25562588
25572589 LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "OverflowOk");
25582590 LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "OverflowFail");
2591 if (operand_type->id == ZigTypeIdVector) {
2592 ok_bit = scalarize_cmp_result(g, ok_bit, ScalarizePredicate::All);
2593 }
25592594 LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block);
25602595
25612596 LLVMPositionBuilderAtEnd(g->builder, fail_block);
......@@ -2565,13 +2600,16 @@ static LLVMValueRef gen_overflow_shl_op(CodeGen *g, ZigType *type_entry,
25652600 return result;
25662601}
25672602
2568static LLVMValueRef gen_overflow_shr_op(CodeGen *g, ZigType *type_entry,
2569 LLVMValueRef val1, LLVMValueRef val2)
2603static LLVMValueRef gen_overflow_shr_op(CodeGen *g, ZigType *operand_type,
2604 LLVMValueRef val1, LLVMValueRef val2)
25702605{
2571 assert(type_entry->id == ZigTypeIdInt);
2606 ZigType *scalar_type = (operand_type->id == ZigTypeIdVector) ?
2607 operand_type->data.vector.elem_type : operand_type;
2608
2609 assert(scalar_type->id == ZigTypeIdInt);
25722610
25732611 LLVMValueRef result;
2574 if (type_entry->data.integral.is_signed) {
2612 if (scalar_type->data.integral.is_signed) {
25752613 result = LLVMBuildAShr(g->builder, val1, val2, "");
25762614 } else {
25772615 result = LLVMBuildLShr(g->builder, val1, val2, "");
......@@ -2581,6 +2619,9 @@ static LLVMValueRef gen_overflow_shr_op(CodeGen *g, ZigType *type_entry,
25812619
25822620 LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "OverflowOk");
25832621 LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "OverflowFail");
2622 if (operand_type->id == ZigTypeIdVector) {
2623 ok_bit = scalarize_cmp_result(g, ok_bit, ScalarizePredicate::All);
2624 }
25842625 LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block);
25852626
25862627 LLVMPositionBuilderAtEnd(g->builder, fail_block);
......@@ -2591,12 +2632,7 @@ static LLVMValueRef gen_overflow_shr_op(CodeGen *g, ZigType *type_entry,
25912632}
25922633
25932634static LLVMValueRef gen_float_op(CodeGen *g, LLVMValueRef val, ZigType *type_entry, BuiltinFnId op) {
2594 if ((op == BuiltinFnIdCeil ||
2595 op == BuiltinFnIdFloor) &&
2596 type_entry->id == ZigTypeIdInt)
2597 return val;
2598 assert(type_entry->id == ZigTypeIdFloat);
2599
2635 assert(type_entry->id == ZigTypeIdFloat || type_entry->id == ZigTypeIdVector);
26002636 LLVMValueRef floor_fn = get_float_fn(g, type_entry, ZigLLVMFnIdFloatOp, op);
26012637 return LLVMBuildCall(g->builder, floor_fn, &val, 1, "");
26022638}
......@@ -2612,6 +2648,21 @@ static LLVMValueRef bigint_to_llvm_const(LLVMTypeRef type_ref, BigInt *bigint) {
26122648 if (bigint->digit_count == 0) {
26132649 return LLVMConstNull(type_ref);
26142650 }
2651
2652 if (LLVMGetTypeKind(type_ref) == LLVMVectorTypeKind) {
2653 const unsigned vector_len = LLVMGetVectorSize(type_ref);
2654 LLVMTypeRef elem_type = LLVMGetElementType(type_ref);
2655
2656 LLVMValueRef *values = heap::c_allocator.allocate_nonzero<LLVMValueRef>(vector_len);
2657 // Create a vector with all the elements having the same value
2658 for (unsigned i = 0; i < vector_len; i++) {
2659 values[i] = bigint_to_llvm_const(elem_type, bigint);
2660 }
2661 LLVMValueRef result = LLVMConstVector(values, vector_len);
2662 heap::c_allocator.deallocate(values, vector_len);
2663 return result;
2664 }
2665
26152666 LLVMValueRef unsigned_val;
26162667 if (bigint->digit_count == 1) {
26172668 unsigned_val = LLVMConstInt(type_ref, bigint_ptr(bigint)[0], false);
......@@ -2626,21 +2677,29 @@ static LLVMValueRef bigint_to_llvm_const(LLVMTypeRef type_ref, BigInt *bigint) {
26262677}
26272678
26282679static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast_math,
2629 LLVMValueRef val1, LLVMValueRef val2,
2630 ZigType *type_entry, DivKind div_kind)
2680 LLVMValueRef val1, LLVMValueRef val2, ZigType *operand_type, DivKind div_kind)
26312681{
2682 ZigType *scalar_type = (operand_type->id == ZigTypeIdVector) ?
2683 operand_type->data.vector.elem_type : operand_type;
2684
26322685 ZigLLVMSetFastMath(g->builder, want_fast_math);
26332686
2634 LLVMValueRef zero = LLVMConstNull(get_llvm_type(g, type_entry));
2635 if (want_runtime_safety && (want_fast_math || type_entry->id != ZigTypeIdFloat)) {
2687 LLVMValueRef zero = LLVMConstNull(get_llvm_type(g, operand_type));
2688 if (want_runtime_safety && (want_fast_math || scalar_type->id != ZigTypeIdFloat)) {
2689 // Safety check: divisor != 0
26362690 LLVMValueRef is_zero_bit;
2637 if (type_entry->id == ZigTypeIdInt) {
2691 if (scalar_type->id == ZigTypeIdInt) {
26382692 is_zero_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, val2, zero, "");
2639 } else if (type_entry->id == ZigTypeIdFloat) {
2693 } else if (scalar_type->id == ZigTypeIdFloat) {
26402694 is_zero_bit = LLVMBuildFCmp(g->builder, LLVMRealOEQ, val2, zero, "");
26412695 } else {
26422696 zig_unreachable();
26432697 }
2698
2699 if (operand_type->id == ZigTypeIdVector) {
2700 is_zero_bit = scalarize_cmp_result(g, is_zero_bit, ScalarizePredicate::Any);
2701 }
2702
26442703 LLVMBasicBlockRef div_zero_fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivZeroFail");
26452704 LLVMBasicBlockRef div_zero_ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivZeroOk");
26462705 LLVMBuildCondBr(g->builder, is_zero_bit, div_zero_fail_block, div_zero_ok_block);
......@@ -2650,16 +2709,21 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast
26502709
26512710 LLVMPositionBuilderAtEnd(g->builder, div_zero_ok_block);
26522711
2653 if (type_entry->id == ZigTypeIdInt && type_entry->data.integral.is_signed) {
2654 LLVMValueRef neg_1_value = LLVMConstInt(get_llvm_type(g, type_entry), -1, true);
2712 // Safety check: check for overflow (dividend = minInt and divisor = -1)
2713 if (scalar_type->id == ZigTypeIdInt && scalar_type->data.integral.is_signed) {
2714 LLVMValueRef neg_1_value = LLVMConstAllOnes(get_llvm_type(g, operand_type));
26552715 BigInt int_min_bi = {0};
2656 eval_min_max_value_int(g, type_entry, &int_min_bi, false);
2657 LLVMValueRef int_min_value = bigint_to_llvm_const(get_llvm_type(g, type_entry), &int_min_bi);
2716 eval_min_max_value_int(g, scalar_type, &int_min_bi, false);
2717 LLVMValueRef int_min_value = bigint_to_llvm_const(get_llvm_type(g, operand_type), &int_min_bi);
2718
26582719 LLVMBasicBlockRef overflow_fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivOverflowFail");
26592720 LLVMBasicBlockRef overflow_ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivOverflowOk");
26602721 LLVMValueRef num_is_int_min = LLVMBuildICmp(g->builder, LLVMIntEQ, val1, int_min_value, "");
26612722 LLVMValueRef den_is_neg_1 = LLVMBuildICmp(g->builder, LLVMIntEQ, val2, neg_1_value, "");
26622723 LLVMValueRef overflow_fail_bit = LLVMBuildAnd(g->builder, num_is_int_min, den_is_neg_1, "");
2724 if (operand_type->id == ZigTypeIdVector) {
2725 overflow_fail_bit = scalarize_cmp_result(g, overflow_fail_bit, ScalarizePredicate::Any);
2726 }
26632727 LLVMBuildCondBr(g->builder, overflow_fail_bit, overflow_fail_block, overflow_ok_block);
26642728
26652729 LLVMPositionBuilderAtEnd(g->builder, overflow_fail_block);
......@@ -2669,18 +2733,22 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast
26692733 }
26702734 }
26712735
2672 if (type_entry->id == ZigTypeIdFloat) {
2736 if (scalar_type->id == ZigTypeIdFloat) {
26732737 LLVMValueRef result = LLVMBuildFDiv(g->builder, val1, val2, "");
26742738 switch (div_kind) {
26752739 case DivKindFloat:
26762740 return result;
26772741 case DivKindExact:
26782742 if (want_runtime_safety) {
2679 LLVMValueRef floored = gen_float_op(g, result, type_entry, BuiltinFnIdFloor);
2743 // Safety check: a / b == floor(a / b)
2744 LLVMValueRef floored = gen_float_op(g, result, operand_type, BuiltinFnIdFloor);
2745
26802746 LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactOk");
26812747 LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactFail");
26822748 LLVMValueRef ok_bit = LLVMBuildFCmp(g->builder, LLVMRealOEQ, floored, result, "");
2683
2749 if (operand_type->id == ZigTypeIdVector) {
2750 ok_bit = scalarize_cmp_result(g, ok_bit, ScalarizePredicate::All);
2751 }
26842752 LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block);
26852753
26862754 LLVMPositionBuilderAtEnd(g->builder, fail_block);
......@@ -2695,54 +2763,61 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast
26952763 LLVMBasicBlockRef gez_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivTruncGEZero");
26962764 LLVMBasicBlockRef end_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivTruncEnd");
26972765 LLVMValueRef ltz = LLVMBuildFCmp(g->builder, LLVMRealOLT, val1, zero, "");
2766 if (operand_type->id == ZigTypeIdVector) {
2767 ltz = scalarize_cmp_result(g, ltz, ScalarizePredicate::Any);
2768 }
26982769 LLVMBuildCondBr(g->builder, ltz, ltz_block, gez_block);
26992770
27002771 LLVMPositionBuilderAtEnd(g->builder, ltz_block);
2701 LLVMValueRef ceiled = gen_float_op(g, result, type_entry, BuiltinFnIdCeil);
2772 LLVMValueRef ceiled = gen_float_op(g, result, operand_type, BuiltinFnIdCeil);
27022773 LLVMBasicBlockRef ceiled_end_block = LLVMGetInsertBlock(g->builder);
27032774 LLVMBuildBr(g->builder, end_block);
27042775
27052776 LLVMPositionBuilderAtEnd(g->builder, gez_block);
2706 LLVMValueRef floored = gen_float_op(g, result, type_entry, BuiltinFnIdFloor);
2777 LLVMValueRef floored = gen_float_op(g, result, operand_type, BuiltinFnIdFloor);
27072778 LLVMBasicBlockRef floored_end_block = LLVMGetInsertBlock(g->builder);
27082779 LLVMBuildBr(g->builder, end_block);
27092780
27102781 LLVMPositionBuilderAtEnd(g->builder, end_block);
2711 LLVMValueRef phi = LLVMBuildPhi(g->builder, get_llvm_type(g, type_entry), "");
2782 LLVMValueRef phi = LLVMBuildPhi(g->builder, get_llvm_type(g, operand_type), "");
27122783 LLVMValueRef incoming_values[] = { ceiled, floored };
27132784 LLVMBasicBlockRef incoming_blocks[] = { ceiled_end_block, floored_end_block };
27142785 LLVMAddIncoming(phi, incoming_values, incoming_blocks, 2);
27152786 return phi;
27162787 }
27172788 case DivKindFloor:
2718 return gen_float_op(g, result, type_entry, BuiltinFnIdFloor);
2789 return gen_float_op(g, result, operand_type, BuiltinFnIdFloor);
27192790 }
27202791 zig_unreachable();
27212792 }
27222793
2723 assert(type_entry->id == ZigTypeIdInt);
2794 assert(scalar_type->id == ZigTypeIdInt);
27242795
27252796 switch (div_kind) {
27262797 case DivKindFloat:
27272798 zig_unreachable();
27282799 case DivKindTrunc:
2729 if (type_entry->data.integral.is_signed) {
2800 if (scalar_type->data.integral.is_signed) {
27302801 return LLVMBuildSDiv(g->builder, val1, val2, "");
27312802 } else {
27322803 return LLVMBuildUDiv(g->builder, val1, val2, "");
27332804 }
27342805 case DivKindExact:
27352806 if (want_runtime_safety) {
2807 // Safety check: a % b == 0
27362808 LLVMValueRef remainder_val;
2737 if (type_entry->data.integral.is_signed) {
2809 if (scalar_type->data.integral.is_signed) {
27382810 remainder_val = LLVMBuildSRem(g->builder, val1, val2, "");
27392811 } else {
27402812 remainder_val = LLVMBuildURem(g->builder, val1, val2, "");
27412813 }
2742 LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, remainder_val, zero, "");
27432814
27442815 LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactOk");
27452816 LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactFail");
2817 LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, remainder_val, zero, "");
2818 if (operand_type->id == ZigTypeIdVector) {
2819 ok_bit = scalarize_cmp_result(g, ok_bit, ScalarizePredicate::All);
2820 }
27462821 LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block);
27472822
27482823 LLVMPositionBuilderAtEnd(g->builder, fail_block);
......@@ -2750,14 +2825,14 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast
27502825
27512826 LLVMPositionBuilderAtEnd(g->builder, ok_block);
27522827 }
2753 if (type_entry->data.integral.is_signed) {
2828 if (scalar_type->data.integral.is_signed) {
27542829 return LLVMBuildExactSDiv(g->builder, val1, val2, "");
27552830 } else {
27562831 return LLVMBuildExactUDiv(g->builder, val1, val2, "");
27572832 }
27582833 case DivKindFloor:
27592834 {
2760 if (!type_entry->data.integral.is_signed) {
2835 if (!scalar_type->data.integral.is_signed) {
27612836 return LLVMBuildUDiv(g->builder, val1, val2, "");
27622837 }
27632838 // const d = @divTrunc(a, b);
......@@ -2784,22 +2859,30 @@ enum RemKind {
27842859};
27852860
27862861static LLVMValueRef gen_rem(CodeGen *g, bool want_runtime_safety, bool want_fast_math,
2787 LLVMValueRef val1, LLVMValueRef val2,
2788 ZigType *type_entry, RemKind rem_kind)
2862 LLVMValueRef val1, LLVMValueRef val2, ZigType *operand_type, RemKind rem_kind)
27892863{
2864 ZigType *scalar_type = (operand_type->id == ZigTypeIdVector) ?
2865 operand_type->data.vector.elem_type : operand_type;
2866
27902867 ZigLLVMSetFastMath(g->builder, want_fast_math);
27912868
2792 LLVMValueRef zero = LLVMConstNull(get_llvm_type(g, type_entry));
2869 LLVMValueRef zero = LLVMConstNull(get_llvm_type(g, operand_type));
27932870 if (want_runtime_safety) {
2871 // Safety check: divisor != 0
27942872 LLVMValueRef is_zero_bit;
2795 if (type_entry->id == ZigTypeIdInt) {
2796 LLVMIntPredicate pred = type_entry->data.integral.is_signed ? LLVMIntSLE : LLVMIntEQ;
2873 if (scalar_type->id == ZigTypeIdInt) {
2874 LLVMIntPredicate pred = scalar_type->data.integral.is_signed ? LLVMIntSLE : LLVMIntEQ;
27972875 is_zero_bit = LLVMBuildICmp(g->builder, pred, val2, zero, "");
2798 } else if (type_entry->id == ZigTypeIdFloat) {
2876 } else if (scalar_type->id == ZigTypeIdFloat) {
27992877 is_zero_bit = LLVMBuildFCmp(g->builder, LLVMRealOEQ, val2, zero, "");
28002878 } else {
28012879 zig_unreachable();
28022880 }
2881
2882 if (operand_type->id == ZigTypeIdVector) {
2883 is_zero_bit = scalarize_cmp_result(g, is_zero_bit, ScalarizePredicate::Any);
2884 }
2885
28032886 LLVMBasicBlockRef rem_zero_ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "RemZeroOk");
28042887 LLVMBasicBlockRef rem_zero_fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "RemZeroFail");
28052888 LLVMBuildCondBr(g->builder, is_zero_bit, rem_zero_fail_block, rem_zero_ok_block);
......@@ -2810,7 +2893,7 @@ static LLVMValueRef gen_rem(CodeGen *g, bool want_runtime_safety, bool want_fast
28102893 LLVMPositionBuilderAtEnd(g->builder, rem_zero_ok_block);
28112894 }
28122895
2813 if (type_entry->id == ZigTypeIdFloat) {
2896 if (scalar_type->id == ZigTypeIdFloat) {
28142897 if (rem_kind == RemKindRem) {
28152898 return LLVMBuildFRem(g->builder, val1, val2, "");
28162899 } else {
......@@ -2821,8 +2904,8 @@ static LLVMValueRef gen_rem(CodeGen *g, bool want_runtime_safety, bool want_fast
28212904 return LLVMBuildSelect(g->builder, ltz, c, a, "");
28222905 }
28232906 } else {
2824 assert(type_entry->id == ZigTypeIdInt);
2825 if (type_entry->data.integral.is_signed) {
2907 assert(scalar_type->id == ZigTypeIdInt);
2908 if (scalar_type->data.integral.is_signed) {
28262909 if (rem_kind == RemKindRem) {
28272910 return LLVMBuildSRem(g->builder, val1, val2, "");
28282911 } else {
......@@ -2845,11 +2928,17 @@ static void gen_shift_rhs_check(CodeGen *g, ZigType *lhs_type, ZigType *rhs_type
28452928 // otherwise the check is useful as the allowed values are limited by the
28462929 // operand type itself
28472930 if (!is_power_of_2(lhs_type->data.integral.bit_count)) {
2848 LLVMValueRef bit_count_value = LLVMConstInt(get_llvm_type(g, rhs_type),
2849 lhs_type->data.integral.bit_count, false);
2850 LLVMValueRef less_than_bit = LLVMBuildICmp(g->builder, LLVMIntULT, value, bit_count_value, "");
2931 BigInt bit_count_bi = {0};
2932 bigint_init_unsigned(&bit_count_bi, lhs_type->data.integral.bit_count);
2933 LLVMValueRef bit_count_value = bigint_to_llvm_const(get_llvm_type(g, rhs_type),
2934 &bit_count_bi);
2935
28512936 LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "CheckFail");
28522937 LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "CheckOk");
2938 LLVMValueRef less_than_bit = LLVMBuildICmp(g->builder, LLVMIntULT, value, bit_count_value, "");
2939 if (rhs_type->id == ZigTypeIdVector) {
2940 less_than_bit = scalarize_cmp_result(g, less_than_bit, ScalarizePredicate::Any);
2941 }
28532942 LLVMBuildCondBr(g->builder, less_than_bit, ok_block, fail_block);
28542943
28552944 LLVMPositionBuilderAtEnd(g->builder, fail_block);
......@@ -2966,7 +3055,8 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutableGen *executable,
29663055 case IrBinOpBitShiftLeftExact:
29673056 {
29683057 assert(scalar_type->id == ZigTypeIdInt);
2969 LLVMValueRef op2_casted = gen_widen_or_shorten(g, false, op2->value->type, scalar_type, op2_value);
3058 LLVMValueRef op2_casted = LLVMBuildZExt(g->builder, op2_value,
3059 LLVMTypeOf(op1_value), "");
29703060
29713061 if (want_runtime_safety) {
29723062 gen_shift_rhs_check(g, scalar_type, op2->value->type, op2_value);
......@@ -2976,7 +3066,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutableGen *executable,
29763066 if (is_sloppy) {
29773067 return LLVMBuildShl(g->builder, op1_value, op2_casted, "");
29783068 } else if (want_runtime_safety) {
2979 return gen_overflow_shl_op(g, scalar_type, op1_value, op2_casted);
3069 return gen_overflow_shl_op(g, operand_type, op1_value, op2_casted);
29803070 } else if (scalar_type->data.integral.is_signed) {
29813071 return ZigLLVMBuildNSWShl(g->builder, op1_value, op2_casted, "");
29823072 } else {
......@@ -2987,7 +3077,8 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutableGen *executable,
29873077 case IrBinOpBitShiftRightExact:
29883078 {
29893079 assert(scalar_type->id == ZigTypeIdInt);
2990 LLVMValueRef op2_casted = gen_widen_or_shorten(g, false, op2->value->type, scalar_type, op2_value);
3080 LLVMValueRef op2_casted = LLVMBuildZExt(g->builder, op2_value,
3081 LLVMTypeOf(op1_value), "");
29913082
29923083 if (want_runtime_safety) {
29933084 gen_shift_rhs_check(g, scalar_type, op2->value->type, op2_value);
......@@ -3001,7 +3092,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutableGen *executable,
30013092 return LLVMBuildLShr(g->builder, op1_value, op2_casted, "");
30023093 }
30033094 } else if (want_runtime_safety) {
3004 return gen_overflow_shr_op(g, scalar_type, op1_value, op2_casted);
3095 return gen_overflow_shr_op(g, operand_type, op1_value, op2_casted);
30053096 } else if (scalar_type->data.integral.is_signed) {
30063097 return ZigLLVMBuildAShrExact(g->builder, op1_value, op2_casted, "");
30073098 } else {
......@@ -3010,22 +3101,22 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutableGen *executable,
30103101 }
30113102 case IrBinOpDivUnspecified:
30123103 return gen_div(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base),
3013 op1_value, op2_value, scalar_type, DivKindFloat);
3104 op1_value, op2_value, operand_type, DivKindFloat);
30143105 case IrBinOpDivExact:
30153106 return gen_div(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base),
3016 op1_value, op2_value, scalar_type, DivKindExact);
3107 op1_value, op2_value, operand_type, DivKindExact);
30173108 case IrBinOpDivTrunc:
30183109 return gen_div(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base),
3019 op1_value, op2_value, scalar_type, DivKindTrunc);
3110 op1_value, op2_value, operand_type, DivKindTrunc);
30203111 case IrBinOpDivFloor:
30213112 return gen_div(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base),
3022 op1_value, op2_value, scalar_type, DivKindFloor);
3113 op1_value, op2_value, operand_type, DivKindFloor);
30233114 case IrBinOpRemRem:
30243115 return gen_rem(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base),
3025 op1_value, op2_value, scalar_type, RemKindRem);
3116 op1_value, op2_value, operand_type, RemKindRem);
30263117 case IrBinOpRemMod:
30273118 return gen_rem(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base),
3028 op1_value, op2_value, scalar_type, RemKindMod);
3119 op1_value, op2_value, operand_type, RemKindMod);
30293120 }
30303121 zig_unreachable();
30313122}
src/ir.cpp+254-157
......@@ -283,6 +283,8 @@ static IrInstGen *ir_analyze_union_init(IrAnalyze *ira, IrInst* source_instructi
283283 IrInstGen *result_loc);
284284static IrInstGen *ir_analyze_struct_value_field_value(IrAnalyze *ira, IrInst* source_instr,
285285 IrInstGen *struct_operand, TypeStructField *field);
286static bool value_cmp_numeric_val_any(ZigValue *left, Cmp predicate, ZigValue *right);
287static bool value_cmp_numeric_val_all(ZigValue *left, Cmp predicate, ZigValue *right);
286288
287289static void destroy_instruction_src(IrInstSrc *inst) {
288290 switch (inst->id) {
......@@ -16803,7 +16805,6 @@ static IrInstGen *ir_analyze_math_op(IrAnalyze *ira, IrInst* source_instr,
1680316805 ZigValue *scalar_op2_val = &op2_val->data.x_array.data.s_none.elements[i];
1680416806 ZigValue *scalar_out_val = &out_val->data.x_array.data.s_none.elements[i];
1680516807 assert(scalar_op1_val->type == scalar_type);
16806 assert(scalar_op2_val->type == scalar_type);
1680716808 assert(scalar_out_val->type == scalar_type);
1680816809 ErrorMsg *msg = ir_eval_math_op_scalar(ira, source_instr, scalar_type,
1680916810 scalar_op1_val, op_id, scalar_op2_val, scalar_out_val);
......@@ -16828,27 +16829,49 @@ static IrInstGen *ir_analyze_bit_shift(IrAnalyze *ira, IrInstSrcBinOp *bin_op_in
1682816829 if (type_is_invalid(op1->value->type))
1682916830 return ira->codegen->invalid_inst_gen;
1683016831
16831 if (op1->value->type->id != ZigTypeIdInt && op1->value->type->id != ZigTypeIdComptimeInt) {
16832 IrInstGen *op2 = bin_op_instruction->op2->child;
16833 if (type_is_invalid(op2->value->type))
16834 return ira->codegen->invalid_inst_gen;
16835
16836 ZigType *op1_type = op1->value->type;
16837 ZigType *op2_type = op2->value->type;
16838
16839 if (op1_type->id == ZigTypeIdVector && op2_type->id != ZigTypeIdVector) {
1683216840 ir_add_error(ira, &bin_op_instruction->op1->base,
16833 buf_sprintf("bit shifting operation expected integer type, found '%s'",
16834 buf_ptr(&op1->value->type->name)));
16841 buf_sprintf("bit shifting operation expected vector type, found '%s'",
16842 buf_ptr(&op2_type->name)));
1683516843 return ira->codegen->invalid_inst_gen;
1683616844 }
1683716845
16838 IrInstGen *op2 = bin_op_instruction->op2->child;
16839 if (type_is_invalid(op2->value->type))
16846 if (op1_type->id != ZigTypeIdVector && op2_type->id == ZigTypeIdVector) {
16847 ir_add_error(ira, &bin_op_instruction->op1->base,
16848 buf_sprintf("bit shifting operation expected vector type, found '%s'",
16849 buf_ptr(&op1_type->name)));
16850 return ira->codegen->invalid_inst_gen;
16851 }
16852
16853 ZigType *op1_scalar_type = (op1_type->id == ZigTypeIdVector) ?
16854 op1_type->data.vector.elem_type : op1_type;
16855 ZigType *op2_scalar_type = (op2_type->id == ZigTypeIdVector) ?
16856 op2_type->data.vector.elem_type : op2_type;
16857
16858 if (op1_scalar_type->id != ZigTypeIdInt && op1_scalar_type->id != ZigTypeIdComptimeInt) {
16859 ir_add_error(ira, &bin_op_instruction->op1->base,
16860 buf_sprintf("bit shifting operation expected integer type, found '%s'",
16861 buf_ptr(&op1_scalar_type->name)));
1684016862 return ira->codegen->invalid_inst_gen;
16863 }
1684116864
16842 if (op2->value->type->id != ZigTypeIdInt && op2->value->type->id != ZigTypeIdComptimeInt) {
16865 if (op2_scalar_type->id != ZigTypeIdInt && op2_scalar_type->id != ZigTypeIdComptimeInt) {
1684316866 ir_add_error(ira, &bin_op_instruction->op2->base,
1684416867 buf_sprintf("shift amount has to be an integer type, but found '%s'",
16845 buf_ptr(&op2->value->type->name)));
16868 buf_ptr(&op2_scalar_type->name)));
1684616869 return ira->codegen->invalid_inst_gen;
1684716870 }
1684816871
1684916872 IrInstGen *casted_op2;
1685016873 IrBinOp op_id = bin_op_instruction->op_id;
16851 if (op1->value->type->id == ZigTypeIdComptimeInt) {
16874 if (op1_scalar_type->id == ZigTypeIdComptimeInt) {
1685216875 // comptime_int has no finite bit width
1685316876 casted_op2 = op2;
1685416877
......@@ -16874,10 +16897,15 @@ static IrInstGen *ir_analyze_bit_shift(IrAnalyze *ira, IrInstSrcBinOp *bin_op_in
1687416897 return ira->codegen->invalid_inst_gen;
1687516898 }
1687616899 } else {
16877 const unsigned bit_count = op1->value->type->data.integral.bit_count;
16900 const unsigned bit_count = op1_scalar_type->data.integral.bit_count;
1687816901 ZigType *shift_amt_type = get_smallest_unsigned_int_type(ira->codegen,
1687916902 bit_count > 0 ? bit_count - 1 : 0);
1688016903
16904 if (op1_type->id == ZigTypeIdVector) {
16905 shift_amt_type = get_vector_type(ira->codegen, op1_type->data.vector.len,
16906 shift_amt_type);
16907 }
16908
1688116909 casted_op2 = ir_implicit_cast(ira, op2, shift_amt_type);
1688216910 if (type_is_invalid(casted_op2->value->type))
1688316911 return ira->codegen->invalid_inst_gen;
......@@ -16888,10 +16916,10 @@ static IrInstGen *ir_analyze_bit_shift(IrAnalyze *ira, IrInstSrcBinOp *bin_op_in
1688816916 if (op2_val == nullptr)
1688916917 return ira->codegen->invalid_inst_gen;
1689016918
16891 BigInt bit_count_value = {0};
16892 bigint_init_unsigned(&bit_count_value, bit_count);
16919 ZigValue bit_count_value;
16920 init_const_usize(ira->codegen, &bit_count_value, bit_count);
1689316921
16894 if (bigint_cmp(&op2_val->data.x_bigint, &bit_count_value) != CmpLT) {
16922 if (!value_cmp_numeric_val_all(op2_val, CmpLT, &bit_count_value)) {
1689516923 ErrorMsg* msg = ir_add_error(ira,
1689616924 &bin_op_instruction->base.base,
1689716925 buf_sprintf("RHS of shift is too large for LHS type"));
......@@ -16910,7 +16938,7 @@ static IrInstGen *ir_analyze_bit_shift(IrAnalyze *ira, IrInstSrcBinOp *bin_op_in
1691016938 if (op2_val == nullptr)
1691116939 return ira->codegen->invalid_inst_gen;
1691216940
16913 if (bigint_cmp_zero(&op2_val->data.x_bigint) == CmpEQ)
16941 if (value_cmp_numeric_val_all(op2_val, CmpEQ, nullptr))
1691416942 return ir_analyze_cast(ira, &bin_op_instruction->base.base, op1->value->type, op1);
1691516943 }
1691616944
......@@ -16923,7 +16951,7 @@ static IrInstGen *ir_analyze_bit_shift(IrAnalyze *ira, IrInstSrcBinOp *bin_op_in
1692316951 if (op2_val == nullptr)
1692416952 return ira->codegen->invalid_inst_gen;
1692516953
16926 return ir_analyze_math_op(ira, &bin_op_instruction->base.base, op1->value->type, op1_val, op_id, op2_val);
16954 return ir_analyze_math_op(ira, &bin_op_instruction->base.base, op1_type, op1_val, op_id, op2_val);
1692716955 }
1692816956
1692916957 return ir_build_bin_op_gen(ira, &bin_op_instruction->base.base, op1->value->type,
......@@ -16943,6 +16971,7 @@ static bool ok_float_op(IrBinOp op) {
1694316971 case IrBinOpDivExact:
1694416972 case IrBinOpRemRem:
1694516973 case IrBinOpRemMod:
16974 case IrBinOpRemUnspecified:
1694616975 return true;
1694716976
1694816977 case IrBinOpBoolOr:
......@@ -16963,7 +16992,6 @@ static bool ok_float_op(IrBinOp op) {
1696316992 case IrBinOpAddWrap:
1696416993 case IrBinOpSubWrap:
1696516994 case IrBinOpMultWrap:
16966 case IrBinOpRemUnspecified:
1696716995 case IrBinOpArrayCat:
1696816996 case IrBinOpArrayMult:
1696916997 return false;
......@@ -16991,6 +17019,53 @@ static bool is_pointer_arithmetic_allowed(ZigType *lhs_type, IrBinOp op) {
1699117019 zig_unreachable();
1699217020}
1699317021
17022static bool value_cmp_numeric_val(ZigValue *left, Cmp predicate, ZigValue *right, bool any) {
17023 assert(left->special == ConstValSpecialStatic);
17024 assert(right == nullptr || right->special == ConstValSpecialStatic);
17025
17026 switch (left->type->id) {
17027 case ZigTypeIdComptimeInt:
17028 case ZigTypeIdInt: {
17029 const Cmp result = right ?
17030 bigint_cmp(&left->data.x_bigint, &right->data.x_bigint) :
17031 bigint_cmp_zero(&left->data.x_bigint);
17032 return result == predicate;
17033 }
17034 case ZigTypeIdComptimeFloat:
17035 case ZigTypeIdFloat: {
17036 if (float_is_nan(left))
17037 return false;
17038 if (right != nullptr && float_is_nan(right))
17039 return false;
17040
17041 const Cmp result = right ? float_cmp(left, right) : float_cmp_zero(left);
17042 return result == predicate;
17043 }
17044 case ZigTypeIdVector: {
17045 for (size_t i = 0; i < left->type->data.vector.len; i++) {
17046 ZigValue *scalar_val = &left->data.x_array.data.s_none.elements[i];
17047 const bool result = value_cmp_numeric_val(scalar_val, predicate, right, any);
17048
17049 if (any && result)
17050 return true; // This element satisfies the predicate
17051 else if (!any && !result)
17052 return false; // This element doesn't satisfy the predicate
17053 }
17054 return any ? false : true;
17055 }
17056 default:
17057 zig_unreachable();
17058 }
17059}
17060
17061static bool value_cmp_numeric_val_any(ZigValue *left, Cmp predicate, ZigValue *right) {
17062 return value_cmp_numeric_val(left, predicate, right, true);
17063}
17064
17065static bool value_cmp_numeric_val_all(ZigValue *left, Cmp predicate, ZigValue *right) {
17066 return value_cmp_numeric_val(left, predicate, right, false);
17067}
17068
1699417069static IrInstGen *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstSrcBinOp *instruction) {
1699517070 Error err;
1699617071
......@@ -17096,127 +17171,13 @@ static IrInstGen *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstSrcBinOp *instruc
1709617171 if (type_is_invalid(resolved_type))
1709717172 return ira->codegen->invalid_inst_gen;
1709817173
17099 bool is_int = resolved_type->id == ZigTypeIdInt || resolved_type->id == ZigTypeIdComptimeInt;
17100 bool is_float = resolved_type->id == ZigTypeIdFloat || resolved_type->id == ZigTypeIdComptimeFloat;
17101 bool is_signed_div = (
17102 (resolved_type->id == ZigTypeIdInt && resolved_type->data.integral.is_signed) ||
17103 resolved_type->id == ZigTypeIdFloat ||
17104 (resolved_type->id == ZigTypeIdComptimeFloat &&
17105 ((bigfloat_cmp_zero(&op1->value->data.x_bigfloat) != CmpGT) !=
17106 (bigfloat_cmp_zero(&op2->value->data.x_bigfloat) != CmpGT))) ||
17107 (resolved_type->id == ZigTypeIdComptimeInt &&
17108 ((bigint_cmp_zero(&op1->value->data.x_bigint) != CmpGT) !=
17109 (bigint_cmp_zero(&op2->value->data.x_bigint) != CmpGT)))
17110 );
17111 if (op_id == IrBinOpDivUnspecified && is_int) {
17112 if (is_signed_div) {
17113 bool ok = false;
17114 if (instr_is_comptime(op1) && instr_is_comptime(op2)) {
17115 ZigValue *op1_val = ir_resolve_const(ira, op1, UndefBad);
17116 if (op1_val == nullptr)
17117 return ira->codegen->invalid_inst_gen;
17118
17119 ZigValue *op2_val = ir_resolve_const(ira, op2, UndefBad);
17120 if (op2_val == nullptr)
17121 return ira->codegen->invalid_inst_gen;
17122
17123 if (bigint_cmp_zero(&op2_val->data.x_bigint) == CmpEQ) {
17124 // the division by zero error will be caught later, but we don't have a
17125 // division function ambiguity problem.
17126 op_id = IrBinOpDivTrunc;
17127 ok = true;
17128 } else {
17129 BigInt trunc_result;
17130 BigInt floor_result;
17131 bigint_div_trunc(&trunc_result, &op1_val->data.x_bigint, &op2_val->data.x_bigint);
17132 bigint_div_floor(&floor_result, &op1_val->data.x_bigint, &op2_val->data.x_bigint);
17133 if (bigint_cmp(&trunc_result, &floor_result) == CmpEQ) {
17134 ok = true;
17135 op_id = IrBinOpDivTrunc;
17136 }
17137 }
17138 }
17139 if (!ok) {
17140 ir_add_error(ira, &instruction->base.base,
17141 buf_sprintf("division with '%s' and '%s': signed integers must use @divTrunc, @divFloor, or @divExact",
17142 buf_ptr(&op1->value->type->name),
17143 buf_ptr(&op2->value->type->name)));
17144 return ira->codegen->invalid_inst_gen;
17145 }
17146 } else {
17147 op_id = IrBinOpDivTrunc;
17148 }
17149 } else if (op_id == IrBinOpRemUnspecified) {
17150 if (is_signed_div && (is_int || is_float)) {
17151 bool ok = false;
17152 if (instr_is_comptime(op1) && instr_is_comptime(op2)) {
17153 ZigValue *op1_val = ir_resolve_const(ira, op1, UndefBad);
17154 if (op1_val == nullptr)
17155 return ira->codegen->invalid_inst_gen;
17174 ZigType *scalar_type = (resolved_type->id == ZigTypeIdVector) ?
17175 resolved_type->data.vector.elem_type : resolved_type;
1715617176
17157 if (is_int) {
17158 ZigValue *op2_val = ir_resolve_const(ira, op2, UndefBad);
17159 if (op2_val == nullptr)
17160 return ira->codegen->invalid_inst_gen;
17177 bool is_int = scalar_type->id == ZigTypeIdInt || scalar_type->id == ZigTypeIdComptimeInt;
17178 bool is_float = scalar_type->id == ZigTypeIdFloat || scalar_type->id == ZigTypeIdComptimeFloat;
1716117179
17162 if (bigint_cmp_zero(&op2->value->data.x_bigint) == CmpEQ) {
17163 // the division by zero error will be caught later, but we don't
17164 // have a remainder function ambiguity problem
17165 ok = true;
17166 } else {
17167 BigInt rem_result;
17168 BigInt mod_result;
17169 bigint_rem(&rem_result, &op1_val->data.x_bigint, &op2_val->data.x_bigint);
17170 bigint_mod(&mod_result, &op1_val->data.x_bigint, &op2_val->data.x_bigint);
17171 ok = bigint_cmp(&rem_result, &mod_result) == CmpEQ;
17172 }
17173 } else {
17174 IrInstGen *casted_op2 = ir_implicit_cast(ira, op2, resolved_type);
17175 if (type_is_invalid(casted_op2->value->type))
17176 return ira->codegen->invalid_inst_gen;
17177
17178 ZigValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad);
17179 if (op2_val == nullptr)
17180 return ira->codegen->invalid_inst_gen;
17181
17182 if (float_cmp_zero(casted_op2->value) == CmpEQ) {
17183 // the division by zero error will be caught later, but we don't
17184 // have a remainder function ambiguity problem
17185 ok = true;
17186 } else {
17187 ZigValue rem_result = {};
17188 ZigValue mod_result = {};
17189 float_rem(&rem_result, op1_val, op2_val);
17190 float_mod(&mod_result, op1_val, op2_val);
17191 ok = float_cmp(&rem_result, &mod_result) == CmpEQ;
17192 }
17193 }
17194 }
17195 if (!ok) {
17196 ir_add_error(ira, &instruction->base.base,
17197 buf_sprintf("remainder division with '%s' and '%s': signed integers and floats must use @rem or @mod",
17198 buf_ptr(&op1->value->type->name),
17199 buf_ptr(&op2->value->type->name)));
17200 return ira->codegen->invalid_inst_gen;
17201 }
17202 }
17203 op_id = IrBinOpRemRem;
17204 }
17205
17206 bool ok = false;
17207 if (is_int) {
17208 ok = true;
17209 } else if (is_float && ok_float_op(op_id)) {
17210 ok = true;
17211 } else if (resolved_type->id == ZigTypeIdVector) {
17212 ZigType *elem_type = resolved_type->data.vector.elem_type;
17213 if (elem_type->id == ZigTypeIdInt || elem_type->id == ZigTypeIdComptimeInt) {
17214 ok = true;
17215 } else if ((elem_type->id == ZigTypeIdFloat || elem_type->id == ZigTypeIdComptimeFloat) && ok_float_op(op_id)) {
17216 ok = true;
17217 }
17218 }
17219 if (!ok) {
17180 if (!is_int && !(is_float && ok_float_op(op_id))) {
1722017181 AstNode *source_node = instruction->base.base.source_node;
1722117182 ir_add_error_node(ira, source_node,
1722217183 buf_sprintf("invalid operands to binary expression: '%s' and '%s'",
......@@ -17225,7 +17186,16 @@ static IrInstGen *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstSrcBinOp *instruc
1722517186 return ira->codegen->invalid_inst_gen;
1722617187 }
1722717188
17228 if (resolved_type->id == ZigTypeIdComptimeInt) {
17189 IrInstGen *casted_op1 = ir_implicit_cast(ira, op1, resolved_type);
17190 if (type_is_invalid(casted_op1->value->type))
17191 return ira->codegen->invalid_inst_gen;
17192
17193 IrInstGen *casted_op2 = ir_implicit_cast(ira, op2, resolved_type);
17194 if (type_is_invalid(casted_op2->value->type))
17195 return ira->codegen->invalid_inst_gen;
17196
17197 // Comptime integers have no fixed size
17198 if (scalar_type->id == ZigTypeIdComptimeInt) {
1722917199 if (op_id == IrBinOpAddWrap) {
1723017200 op_id = IrBinOpAdd;
1723117201 } else if (op_id == IrBinOpSubWrap) {
......@@ -17235,25 +17205,131 @@ static IrInstGen *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstSrcBinOp *instruc
1723517205 }
1723617206 }
1723717207
17238 IrInstGen *casted_op1 = ir_implicit_cast(ira, op1, resolved_type);
17239 if (type_is_invalid(casted_op1->value->type))
17240 return ira->codegen->invalid_inst_gen;
17241
17242 IrInstGen *casted_op2 = ir_implicit_cast(ira, op2, resolved_type);
17243 if (type_is_invalid(casted_op2->value->type))
17244 return ira->codegen->invalid_inst_gen;
17245
1724617208 if (instr_is_comptime(casted_op1) && instr_is_comptime(casted_op2)) {
1724717209 ZigValue *op1_val = ir_resolve_const(ira, casted_op1, UndefBad);
1724817210 if (op1_val == nullptr)
1724917211 return ira->codegen->invalid_inst_gen;
17212
1725017213 ZigValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad);
1725117214 if (op2_val == nullptr)
1725217215 return ira->codegen->invalid_inst_gen;
1725317216
17217 // Promote division with negative numbers to signed
17218 bool is_signed_div = value_cmp_numeric_val_any(op1_val, CmpLT, nullptr) ||
17219 value_cmp_numeric_val_any(op2_val, CmpLT, nullptr);
17220
17221 if (op_id == IrBinOpDivUnspecified && is_int) {
17222 // Default to truncating division and check if it's valid for the
17223 // given operands if signed
17224 op_id = IrBinOpDivTrunc;
17225
17226 if (is_signed_div) {
17227 bool ok = false;
17228
17229 if (value_cmp_numeric_val_any(op2_val, CmpEQ, nullptr)) {
17230 // the division by zero error will be caught later, but we don't have a
17231 // division function ambiguity problem.
17232 ok = true;
17233 } else {
17234 IrInstGen *trunc_val = ir_analyze_math_op(ira, &instruction->base.base, resolved_type,
17235 op1_val, IrBinOpDivTrunc, op2_val);
17236 if (type_is_invalid(trunc_val->value->type))
17237 return ira->codegen->invalid_inst_gen;
17238
17239 IrInstGen *floor_val = ir_analyze_math_op(ira, &instruction->base.base, resolved_type,
17240 op1_val, IrBinOpDivFloor, op2_val);
17241 if (type_is_invalid(floor_val->value->type))
17242 return ira->codegen->invalid_inst_gen;
17243
17244 IrInstGen *cmp_val = ir_analyze_bin_op_cmp_numeric(ira, &instruction->base.base,
17245 trunc_val, floor_val, IrBinOpCmpEq);
17246 if (type_is_invalid(cmp_val->value->type))
17247 return ira->codegen->invalid_inst_gen;
17248
17249 // We can "upgrade" the operator only if trunc(a/b) == floor(a/b)
17250 if (!ir_resolve_bool(ira, cmp_val, &ok))
17251 return ira->codegen->invalid_inst_gen;
17252 }
17253
17254 if (!ok) {
17255 ir_add_error(ira, &instruction->base.base,
17256 buf_sprintf("division with '%s' and '%s': signed integers must use @divTrunc, @divFloor, or @divExact",
17257 buf_ptr(&op1->value->type->name),
17258 buf_ptr(&op2->value->type->name)));
17259 return ira->codegen->invalid_inst_gen;
17260 }
17261 }
17262 } else if (op_id == IrBinOpRemUnspecified) {
17263 op_id = IrBinOpRemRem;
17264
17265 if (is_signed_div) {
17266 bool ok = false;
17267
17268 if (value_cmp_numeric_val_any(op2_val, CmpEQ, nullptr)) {
17269 // the division by zero error will be caught later, but we don't have a
17270 // division function ambiguity problem.
17271 ok = true;
17272 } else {
17273 IrInstGen *rem_val = ir_analyze_math_op(ira, &instruction->base.base, resolved_type,
17274 op1_val, IrBinOpRemRem, op2_val);
17275 if (type_is_invalid(rem_val->value->type))
17276 return ira->codegen->invalid_inst_gen;
17277
17278 IrInstGen *mod_val = ir_analyze_math_op(ira, &instruction->base.base, resolved_type,
17279 op1_val, IrBinOpRemMod, op2_val);
17280 if (type_is_invalid(mod_val->value->type))
17281 return ira->codegen->invalid_inst_gen;
17282
17283 IrInstGen *cmp_val = ir_analyze_bin_op_cmp_numeric(ira, &instruction->base.base,
17284 rem_val, mod_val, IrBinOpCmpEq);
17285 if (type_is_invalid(cmp_val->value->type))
17286 return ira->codegen->invalid_inst_gen;
17287
17288 // We can "upgrade" the operator only if mod(a,b) == rem(a,b)
17289 if (!ir_resolve_bool(ira, cmp_val, &ok))
17290 return ira->codegen->invalid_inst_gen;
17291 }
17292
17293 if (!ok) {
17294 ir_add_error(ira, &instruction->base.base,
17295 buf_sprintf("remainder division with '%s' and '%s': signed integers and floats must use @rem or @mod",
17296 buf_ptr(&op1->value->type->name),
17297 buf_ptr(&op2->value->type->name)));
17298 return ira->codegen->invalid_inst_gen;
17299 }
17300 }
17301 }
17302
1725417303 return ir_analyze_math_op(ira, &instruction->base.base, resolved_type, op1_val, op_id, op2_val);
1725517304 }
1725617305
17306 const bool is_signed_div =
17307 (scalar_type->id == ZigTypeIdInt && scalar_type->data.integral.is_signed) ||
17308 scalar_type->id == ZigTypeIdFloat;
17309
17310 // Warn the user to use the proper operators here
17311 if (op_id == IrBinOpDivUnspecified && is_int) {
17312 op_id = IrBinOpDivTrunc;
17313
17314 if (is_signed_div) {
17315 ir_add_error(ira, &instruction->base.base,
17316 buf_sprintf("division with '%s' and '%s': signed integers must use @divTrunc, @divFloor, or @divExact",
17317 buf_ptr(&op1->value->type->name),
17318 buf_ptr(&op2->value->type->name)));
17319 return ira->codegen->invalid_inst_gen;
17320 }
17321 } else if (op_id == IrBinOpRemUnspecified) {
17322 op_id = IrBinOpRemRem;
17323
17324 if (is_signed_div) {
17325 ir_add_error(ira, &instruction->base.base,
17326 buf_sprintf("remainder division with '%s' and '%s': signed integers and floats must use @rem or @mod",
17327 buf_ptr(&op1->value->type->name),
17328 buf_ptr(&op2->value->type->name)));
17329 return ira->codegen->invalid_inst_gen;
17330 }
17331 }
17332
1725717333 return ir_build_bin_op_gen(ira, &instruction->base.base, resolved_type,
1725817334 op_id, casted_op1, casted_op2, instruction->safety_check_on);
1725917335}
......@@ -20337,24 +20413,45 @@ static IrInstGen *ir_analyze_bin_not(IrAnalyze *ira, IrInstSrcUnOp *instruction)
2033720413 if (type_is_invalid(expr_type))
2033820414 return ira->codegen->invalid_inst_gen;
2033920415
20340 if (expr_type->id == ZigTypeIdInt) {
20341 if (instr_is_comptime(value)) {
20342 ZigValue *target_const_val = ir_resolve_const(ira, value, UndefBad);
20343 if (target_const_val == nullptr)
20344 return ira->codegen->invalid_inst_gen;
20416 ZigType *scalar_type = (expr_type->id == ZigTypeIdVector) ?
20417 expr_type->data.vector.elem_type : expr_type;
2034520418
20346 IrInstGen *result = ir_const(ira, &instruction->base.base, expr_type);
20347 bigint_not(&result->value->data.x_bigint, &target_const_val->data.x_bigint,
20348 expr_type->data.integral.bit_count, expr_type->data.integral.is_signed);
20349 return result;
20419 if (scalar_type->id != ZigTypeIdInt) {
20420 ir_add_error(ira, &instruction->base.base,
20421 buf_sprintf("unable to perform binary not operation on type '%s'", buf_ptr(&expr_type->name)));
20422 return ira->codegen->invalid_inst_gen;
20423 }
20424
20425 if (instr_is_comptime(value)) {
20426 ZigValue *expr_val = ir_resolve_const(ira, value, UndefBad);
20427 if (expr_val == nullptr)
20428 return ira->codegen->invalid_inst_gen;
20429
20430 IrInstGen *result = ir_const(ira, &instruction->base.base, expr_type);
20431
20432 if (expr_type->id == ZigTypeIdVector) {
20433 expand_undef_array(ira->codegen, expr_val);
20434 result->value->special = ConstValSpecialUndef;
20435 expand_undef_array(ira->codegen, result->value);
20436
20437 for (size_t i = 0; i < expr_type->data.vector.len; i++) {
20438 ZigValue *src_val = &expr_val->data.x_array.data.s_none.elements[i];
20439 ZigValue *dst_val = &result->value->data.x_array.data.s_none.elements[i];
20440
20441 dst_val->type = scalar_type;
20442 dst_val->special = ConstValSpecialStatic;
20443 bigint_not(&dst_val->data.x_bigint, &src_val->data.x_bigint,
20444 scalar_type->data.integral.bit_count, scalar_type->data.integral.is_signed);
20445 }
20446 } else {
20447 bigint_not(&result->value->data.x_bigint, &expr_val->data.x_bigint,
20448 scalar_type->data.integral.bit_count, scalar_type->data.integral.is_signed);
2035020449 }
2035120450
20352 return ir_build_binary_not(ira, &instruction->base.base, value, expr_type);
20451 return result;
2035320452 }
2035420453
20355 ir_add_error(ira, &instruction->base.base,
20356 buf_sprintf("unable to perform binary not operation on type '%s'", buf_ptr(&expr_type->name)));
20357 return ira->codegen->invalid_inst_gen;
20454 return ir_build_binary_not(ira, &instruction->base.base, value, expr_type);
2035820455}
2035920456
2036020457static IrInstGen *ir_analyze_instruction_un_op(IrAnalyze *ira, IrInstSrcUnOp *instruction) {
test/runtime_safety.zig+43
......@@ -505,6 +505,21 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
505505 \\}
506506 );
507507
508 cases.addRuntimeSafety("signed integer division overflow - vectors",
509 \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn {
510 \\ @import("std").os.exit(126);
511 \\}
512 \\pub fn main() !void {
513 \\ var a: @Vector(4, i16) = [_]i16{ 1, 2, -32768, 4 };
514 \\ var b: @Vector(4, i16) = [_]i16{ 1, 2, -1, 4 };
515 \\ const x = div(a, b);
516 \\ if (x[2] == 32767) return error.Whatever;
517 \\}
518 \\fn div(a: @Vector(4, i16), b: @Vector(4, i16)) @Vector(4, i16) {
519 \\ return @divTrunc(a, b);
520 \\}
521 );
522
508523 cases.addRuntimeSafety("signed shift left overflow",
509524 \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn {
510525 \\ @import("std").os.exit(126);
......@@ -569,6 +584,20 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
569584 \\}
570585 );
571586
587 cases.addRuntimeSafety("integer division by zero - vectors",
588 \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn {
589 \\ @import("std").os.exit(126);
590 \\}
591 \\pub fn main() void {
592 \\ var a: @Vector(4, i32) = [4]i32{111, 222, 333, 444};
593 \\ var b: @Vector(4, i32) = [4]i32{111, 0, 333, 444};
594 \\ const x = div0(a, b);
595 \\}
596 \\fn div0(a: @Vector(4, i32), b: @Vector(4, i32)) @Vector(4, i32) {
597 \\ return @divTrunc(a, b);
598 \\}
599 );
600
572601 cases.addRuntimeSafety("exact division failure",
573602 \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn {
574603 \\ @import("std").os.exit(126);
......@@ -582,6 +611,20 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
582611 \\}
583612 );
584613
614 cases.addRuntimeSafety("exact division failure - vectors",
615 \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn {
616 \\ @import("std").os.exit(126);
617 \\}
618 \\pub fn main() !void {
619 \\ var a: @Vector(4, i32) = [4]i32{111, 222, 333, 444};
620 \\ var b: @Vector(4, i32) = [4]i32{111, 222, 333, 441};
621 \\ const x = divExact(a, b);
622 \\}
623 \\fn divExact(a: @Vector(4, i32), b: @Vector(4, i32)) @Vector(4, i32) {
624 \\ return @divExact(a, b);
625 \\}
626 );
627
585628 cases.addRuntimeSafety("cast []u8 to bigger slice of wrong size",
586629 \\const std = @import("std");
587630 \\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
test/stage1/behavior/vector.zig+195
......@@ -1,5 +1,6 @@
11const std = @import("std");
22const mem = std.mem;
3const math = std.math;
34const expect = std.testing.expect;
45const expectEqual = std.testing.expectEqual;
56
......@@ -276,3 +277,197 @@ test "vector comparison operators" {
276277 S.doTheTest();
277278 comptime S.doTheTest();
278279}
280
281test "vector division operators" {
282 const S = struct {
283 fn doTheTestDiv(comptime T: type, x: @Vector(4, T), y: @Vector(4, T)) void {
284 if (!comptime std.meta.trait.isSignedInt(T)) {
285 const d0 = x / y;
286 for (@as([4]T, d0)) |v, i| {
287 expectEqual(x[i] / y[i], v);
288 }
289 }
290 const d1 = @divExact(x, y);
291 for (@as([4]T, d1)) |v, i| {
292 expectEqual(@divExact(x[i], y[i]), v);
293 }
294 const d2 = @divFloor(x, y);
295 for (@as([4]T, d2)) |v, i| {
296 expectEqual(@divFloor(x[i], y[i]), v);
297 }
298 const d3 = @divTrunc(x, y);
299 for (@as([4]T, d3)) |v, i| {
300 expectEqual(@divTrunc(x[i], y[i]), v);
301 }
302 }
303
304 fn doTheTestMod(comptime T: type, x: @Vector(4, T), y: @Vector(4, T)) void {
305 if ((!comptime std.meta.trait.isSignedInt(T)) and @typeInfo(T) != .Float) {
306 const r0 = x % y;
307 for (@as([4]T, r0)) |v, i| {
308 expectEqual(x[i] % y[i], v);
309 }
310 }
311 const r1 = @mod(x, y);
312 for (@as([4]T, r1)) |v, i| {
313 expectEqual(@mod(x[i], y[i]), v);
314 }
315 const r2 = @rem(x, y);
316 for (@as([4]T, r2)) |v, i| {
317 expectEqual(@rem(x[i], y[i]), v);
318 }
319 }
320
321 fn doTheTest() void {
322 // https://github.com/ziglang/zig/issues/4952
323 if (std.builtin.os.tag != .windows) {
324 doTheTestDiv(f16, [4]f16{ 4.0, -4.0, 4.0, -4.0 }, [4]f16{ 1.0, 2.0, -1.0, -2.0 });
325 }
326
327 doTheTestDiv(f32, [4]f32{ 4.0, -4.0, 4.0, -4.0 }, [4]f32{ 1.0, 2.0, -1.0, -2.0 });
328 doTheTestDiv(f64, [4]f64{ 4.0, -4.0, 4.0, -4.0 }, [4]f64{ 1.0, 2.0, -1.0, -2.0 });
329
330 // https://github.com/ziglang/zig/issues/4952
331 if (std.builtin.os.tag != .windows) {
332 doTheTestMod(f16, [4]f16{ 4.0, -4.0, 4.0, -4.0 }, [4]f16{ 1.0, 2.0, 0.5, 3.0 });
333 }
334 doTheTestMod(f32, [4]f32{ 4.0, -4.0, 4.0, -4.0 }, [4]f32{ 1.0, 2.0, 0.5, 3.0 });
335 doTheTestMod(f64, [4]f64{ 4.0, -4.0, 4.0, -4.0 }, [4]f64{ 1.0, 2.0, 0.5, 3.0 });
336
337 doTheTestDiv(i8, [4]i8{ 4, -4, 4, -4 }, [4]i8{ 1, 2, -1, -2 });
338 doTheTestDiv(i16, [4]i16{ 4, -4, 4, -4 }, [4]i16{ 1, 2, -1, -2 });
339 doTheTestDiv(i32, [4]i32{ 4, -4, 4, -4 }, [4]i32{ 1, 2, -1, -2 });
340 doTheTestDiv(i64, [4]i64{ 4, -4, 4, -4 }, [4]i64{ 1, 2, -1, -2 });
341
342 doTheTestMod(i8, [4]i8{ 4, -4, 4, -4 }, [4]i8{ 1, 2, 4, 8 });
343 doTheTestMod(i16, [4]i16{ 4, -4, 4, -4 }, [4]i16{ 1, 2, 4, 8 });
344 doTheTestMod(i32, [4]i32{ 4, -4, 4, -4 }, [4]i32{ 1, 2, 4, 8 });
345 doTheTestMod(i64, [4]i64{ 4, -4, 4, -4 }, [4]i64{ 1, 2, 4, 8 });
346
347 doTheTestDiv(u8, [4]u8{ 1, 2, 4, 8 }, [4]u8{ 1, 1, 2, 4 });
348 doTheTestDiv(u16, [4]u16{ 1, 2, 4, 8 }, [4]u16{ 1, 1, 2, 4 });
349 doTheTestDiv(u32, [4]u32{ 1, 2, 4, 8 }, [4]u32{ 1, 1, 2, 4 });
350 doTheTestDiv(u64, [4]u64{ 1, 2, 4, 8 }, [4]u64{ 1, 1, 2, 4 });
351
352 doTheTestMod(u8, [4]u8{ 1, 2, 4, 8 }, [4]u8{ 1, 1, 2, 4 });
353 doTheTestMod(u16, [4]u16{ 1, 2, 4, 8 }, [4]u16{ 1, 1, 2, 4 });
354 doTheTestMod(u32, [4]u32{ 1, 2, 4, 8 }, [4]u32{ 1, 1, 2, 4 });
355 doTheTestMod(u64, [4]u64{ 1, 2, 4, 8 }, [4]u64{ 1, 1, 2, 4 });
356 }
357 };
358
359 S.doTheTest();
360 comptime S.doTheTest();
361}
362
363test "vector bitwise not operator" {
364 const S = struct {
365 fn doTheTestNot(comptime T: type, x: @Vector(4, T)) void {
366 var y = ~x;
367 for (@as([4]T, y)) |v, i| {
368 expectEqual(~x[i], v);
369 }
370 }
371 fn doTheTest() void {
372 doTheTestNot(u8, [_]u8{ 0, 2, 4, 255 });
373 doTheTestNot(u16, [_]u16{ 0, 2, 4, 255 });
374 doTheTestNot(u32, [_]u32{ 0, 2, 4, 255 });
375 doTheTestNot(u64, [_]u64{ 0, 2, 4, 255 });
376
377 doTheTestNot(u8, [_]u8{ 0, 2, 4, 255 });
378 doTheTestNot(u16, [_]u16{ 0, 2, 4, 255 });
379 doTheTestNot(u32, [_]u32{ 0, 2, 4, 255 });
380 doTheTestNot(u64, [_]u64{ 0, 2, 4, 255 });
381 }
382 };
383
384 S.doTheTest();
385 comptime S.doTheTest();
386}
387
388test "vector shift operators" {
389 const S = struct {
390 fn doTheTestShift(x: var, y: var) void {
391 const N = @typeInfo(@TypeOf(x)).Array.len;
392 const TX = @typeInfo(@TypeOf(x)).Array.child;
393 const TY = @typeInfo(@TypeOf(y)).Array.child;
394
395 var xv = @as(@Vector(N, TX), x);
396 var yv = @as(@Vector(N, TY), y);
397
398 var z0 = xv >> yv;
399 for (@as([N]TX, z0)) |v, i| {
400 expectEqual(x[i] >> y[i], v);
401 }
402 var z1 = xv << yv;
403 for (@as([N]TX, z1)) |v, i| {
404 expectEqual(x[i] << y[i], v);
405 }
406 }
407 fn doTheTestShiftExact(x: var, y: var, dir: enum { Left, Right }) void {
408 const N = @typeInfo(@TypeOf(x)).Array.len;
409 const TX = @typeInfo(@TypeOf(x)).Array.child;
410 const TY = @typeInfo(@TypeOf(y)).Array.child;
411
412 var xv = @as(@Vector(N, TX), x);
413 var yv = @as(@Vector(N, TY), y);
414
415 var z = if (dir == .Left) @shlExact(xv, yv) else @shrExact(xv, yv);
416 for (@as([N]TX, z)) |v, i| {
417 const check = if (dir == .Left) x[i] << y[i] else x[i] >> y[i];
418 expectEqual(check, v);
419 }
420 }
421 fn doTheTest() void {
422 doTheTestShift([_]u8{ 0, 2, 4, math.maxInt(u8) }, [_]u3{ 2, 0, 2, 7 });
423 doTheTestShift([_]u16{ 0, 2, 4, math.maxInt(u16) }, [_]u4{ 2, 0, 2, 15 });
424 doTheTestShift([_]u24{ 0, 2, 4, math.maxInt(u24) }, [_]u5{ 2, 0, 2, 23 });
425 doTheTestShift([_]u32{ 0, 2, 4, math.maxInt(u32) }, [_]u5{ 2, 0, 2, 31 });
426 doTheTestShift([_]u64{ 0xfe, math.maxInt(u64) }, [_]u6{ 0, 63 });
427
428 doTheTestShift([_]i8{ 0, 2, 4, math.maxInt(i8) }, [_]u3{ 2, 0, 2, 7 });
429 doTheTestShift([_]i16{ 0, 2, 4, math.maxInt(i16) }, [_]u4{ 2, 0, 2, 7 });
430 doTheTestShift([_]i24{ 0, 2, 4, math.maxInt(i24) }, [_]u5{ 2, 0, 2, 7 });
431 doTheTestShift([_]i32{ 0, 2, 4, math.maxInt(i32) }, [_]u5{ 2, 0, 2, 7 });
432 doTheTestShift([_]i64{ 0xfe, math.maxInt(i64) }, [_]u6{ 0, 63 });
433
434 doTheTestShiftExact([_]u8{ 0, 1, 1 << 7, math.maxInt(u8) ^ 1 }, [_]u3{ 4, 0, 7, 1 }, .Right);
435 doTheTestShiftExact([_]u16{ 0, 1, 1 << 15, math.maxInt(u16) ^ 1 }, [_]u4{ 4, 0, 15, 1 }, .Right);
436 doTheTestShiftExact([_]u24{ 0, 1, 1 << 23, math.maxInt(u24) ^ 1 }, [_]u5{ 4, 0, 23, 1 }, .Right);
437 doTheTestShiftExact([_]u32{ 0, 1, 1 << 31, math.maxInt(u32) ^ 1 }, [_]u5{ 4, 0, 31, 1 }, .Right);
438 doTheTestShiftExact([_]u64{ 1 << 63, 1 }, [_]u6{ 63, 0 }, .Right);
439
440 doTheTestShiftExact([_]u8{ 0, 1, 1, math.maxInt(u8) ^ (1 << 7) }, [_]u3{ 4, 0, 7, 1 }, .Left);
441 doTheTestShiftExact([_]u16{ 0, 1, 1, math.maxInt(u16) ^ (1 << 15) }, [_]u4{ 4, 0, 15, 1 }, .Left);
442 doTheTestShiftExact([_]u24{ 0, 1, 1, math.maxInt(u24) ^ (1 << 23) }, [_]u5{ 4, 0, 23, 1 }, .Left);
443 doTheTestShiftExact([_]u32{ 0, 1, 1, math.maxInt(u32) ^ (1 << 31) }, [_]u5{ 4, 0, 31, 1 }, .Left);
444 doTheTestShiftExact([_]u64{ 1 << 63, 1 }, [_]u6{ 0, 63 }, .Left);
445 }
446 };
447
448 switch (std.builtin.arch) {
449 .i386,
450 .aarch64,
451 .aarch64_be,
452 .aarch64_32,
453 .arm,
454 .armeb,
455 .thumb,
456 .thumbeb,
457 .mips,
458 .mipsel,
459 .mips64,
460 .mips64el,
461 .riscv64,
462 .sparcv9,
463 => {
464 // LLVM miscompiles on this architecture
465 // https://github.com/ziglang/zig/issues/4951
466 return error.SkipZigTest;
467 },
468 else => {},
469 }
470
471 S.doTheTest();
472 comptime S.doTheTest();
473}