| author | |
| committer | |
| log | 54ffcf95a8596aa3adf57cddbe079a24f849f408 |
| tree | 7e189bd8ef3f39ae28fe31791877eab372f671e8 |
| parent | e2dc63644ab3d8e5cdaec2d58dc57c587295081f |
| signature |
Closes #40503 files changed, 326 insertions(+), 173 deletions(-)
src/codegen.cpp+96-44| ... | ... | @@ -2591,12 +2591,7 @@ static LLVMValueRef gen_overflow_shr_op(CodeGen *g, ZigType *type_entry, |
| 2591 | 2591 | } |
| 2592 | 2592 | |
| 2593 | 2593 | static 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 | ||
| 2594 | assert(type_entry->id == ZigTypeIdFloat || type_entry->id == ZigTypeIdVector); | |
| 2600 | 2595 | LLVMValueRef floor_fn = get_float_fn(g, type_entry, ZigLLVMFnIdFloatOp, op); |
| 2601 | 2596 | return LLVMBuildCall(g->builder, floor_fn, &val, 1, ""); |
| 2602 | 2597 | } |
| ... | ... | @@ -2612,6 +2607,21 @@ static LLVMValueRef bigint_to_llvm_const(LLVMTypeRef type_ref, BigInt *bigint) { |
| 2612 | 2607 | if (bigint->digit_count == 0) { |
| 2613 | 2608 | return LLVMConstNull(type_ref); |
| 2614 | 2609 | } |
| 2610 | ||
| 2611 | if (LLVMGetTypeKind(type_ref) == LLVMVectorTypeKind) { | |
| 2612 | const unsigned vector_len = LLVMGetVectorSize(type_ref); | |
| 2613 | LLVMTypeRef elem_type = LLVMGetElementType(type_ref); | |
| 2614 | ||
| 2615 | LLVMValueRef *values = heap::c_allocator.allocate_nonzero<LLVMValueRef>(vector_len); | |
| 2616 | // Create a vector with all the elements having the same value | |
| 2617 | for (unsigned i = 0; i < vector_len; i++) { | |
| 2618 | values[i] = bigint_to_llvm_const(elem_type, bigint); | |
| 2619 | } | |
| 2620 | LLVMValueRef result = LLVMConstVector(values, vector_len); | |
| 2621 | heap::c_allocator.deallocate(values, vector_len); | |
| 2622 | return result; | |
| 2623 | } | |
| 2624 | ||
| 2615 | 2625 | LLVMValueRef unsigned_val; |
| 2616 | 2626 | if (bigint->digit_count == 1) { |
| 2617 | 2627 | unsigned_val = LLVMConstInt(type_ref, bigint_ptr(bigint)[0], false); |
| ... | ... | @@ -2625,22 +2635,40 @@ static LLVMValueRef bigint_to_llvm_const(LLVMTypeRef type_ref, BigInt *bigint) { |
| 2625 | 2635 | } |
| 2626 | 2636 | } |
| 2627 | 2637 | |
| 2638 | // Collapses a <N x i1> vector into a single i1 whose value is 1 iff all the | |
| 2639 | // vector elements are 1 | |
| 2640 | static LLVMValueRef scalarize_cmp_result(CodeGen *g, LLVMValueRef val) { | |
| 2641 | assert(LLVMGetTypeKind(LLVMTypeOf(val)) == LLVMVectorTypeKind); | |
| 2642 | LLVMTypeRef scalar_type = LLVMIntType(LLVMGetVectorSize(LLVMTypeOf(val))); | |
| 2643 | LLVMValueRef all_ones = LLVMConstAllOnes(scalar_type); | |
| 2644 | LLVMValueRef casted = LLVMBuildBitCast(g->builder, val, scalar_type, ""); | |
| 2645 | return LLVMBuildICmp(g->builder, LLVMIntEQ, casted, all_ones, ""); | |
| 2646 | } | |
| 2647 | ||
| 2628 | 2648 | static 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) | |
| 2649 | LLVMValueRef val1, LLVMValueRef val2, ZigType *operand_type, DivKind div_kind) | |
| 2631 | 2650 | { |
| 2651 | ZigType *scalar_type = (operand_type->id == ZigTypeIdVector) ? | |
| 2652 | operand_type->data.vector.elem_type : operand_type; | |
| 2653 | ||
| 2632 | 2654 | ZigLLVMSetFastMath(g->builder, want_fast_math); |
| 2633 | 2655 | |
| 2634 | LLVMValueRef zero = LLVMConstNull(get_llvm_type(g, type_entry)); | |
| 2635 | if (want_runtime_safety && (want_fast_math || type_entry->id != ZigTypeIdFloat)) { | |
| 2656 | LLVMValueRef zero = LLVMConstNull(get_llvm_type(g, operand_type)); | |
| 2657 | if (want_runtime_safety && (want_fast_math || scalar_type->id != ZigTypeIdFloat)) { | |
| 2658 | // Safety check: divisor != 0 | |
| 2636 | 2659 | LLVMValueRef is_zero_bit; |
| 2637 | if (type_entry->id == ZigTypeIdInt) { | |
| 2660 | if (scalar_type->id == ZigTypeIdInt) { | |
| 2638 | 2661 | is_zero_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, val2, zero, ""); |
| 2639 | } else if (type_entry->id == ZigTypeIdFloat) { | |
| 2662 | } else if (scalar_type->id == ZigTypeIdFloat) { | |
| 2640 | 2663 | is_zero_bit = LLVMBuildFCmp(g->builder, LLVMRealOEQ, val2, zero, ""); |
| 2641 | 2664 | } else { |
| 2642 | 2665 | zig_unreachable(); |
| 2643 | 2666 | } |
| 2667 | ||
| 2668 | if (operand_type->id == ZigTypeIdVector) { | |
| 2669 | is_zero_bit = scalarize_cmp_result(g, is_zero_bit); | |
| 2670 | } | |
| 2671 | ||
| 2644 | 2672 | LLVMBasicBlockRef div_zero_fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivZeroFail"); |
| 2645 | 2673 | LLVMBasicBlockRef div_zero_ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivZeroOk"); |
| 2646 | 2674 | LLVMBuildCondBr(g->builder, is_zero_bit, div_zero_fail_block, div_zero_ok_block); |
| ... | ... | @@ -2650,16 +2678,21 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast |
| 2650 | 2678 | |
| 2651 | 2679 | LLVMPositionBuilderAtEnd(g->builder, div_zero_ok_block); |
| 2652 | 2680 | |
| 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); | |
| 2681 | // Safety check: check for overflow (dividend = minInt and divisor = -1) | |
| 2682 | if (scalar_type->id == ZigTypeIdInt && scalar_type->data.integral.is_signed) { | |
| 2683 | LLVMValueRef neg_1_value = LLVMConstAllOnes(get_llvm_type(g, operand_type)); | |
| 2655 | 2684 | 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); | |
| 2685 | eval_min_max_value_int(g, scalar_type, &int_min_bi, false); | |
| 2686 | LLVMValueRef int_min_value = bigint_to_llvm_const(get_llvm_type(g, operand_type), &int_min_bi); | |
| 2687 | ||
| 2658 | 2688 | LLVMBasicBlockRef overflow_fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivOverflowFail"); |
| 2659 | 2689 | LLVMBasicBlockRef overflow_ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivOverflowOk"); |
| 2660 | 2690 | LLVMValueRef num_is_int_min = LLVMBuildICmp(g->builder, LLVMIntEQ, val1, int_min_value, ""); |
| 2661 | 2691 | LLVMValueRef den_is_neg_1 = LLVMBuildICmp(g->builder, LLVMIntEQ, val2, neg_1_value, ""); |
| 2662 | 2692 | LLVMValueRef overflow_fail_bit = LLVMBuildAnd(g->builder, num_is_int_min, den_is_neg_1, ""); |
| 2693 | if (operand_type->id == ZigTypeIdVector) { | |
| 2694 | overflow_fail_bit = scalarize_cmp_result(g, overflow_fail_bit); | |
| 2695 | } | |
| 2663 | 2696 | LLVMBuildCondBr(g->builder, overflow_fail_bit, overflow_fail_block, overflow_ok_block); |
| 2664 | 2697 | |
| 2665 | 2698 | LLVMPositionBuilderAtEnd(g->builder, overflow_fail_block); |
| ... | ... | @@ -2669,18 +2702,22 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast |
| 2669 | 2702 | } |
| 2670 | 2703 | } |
| 2671 | 2704 | |
| 2672 | if (type_entry->id == ZigTypeIdFloat) { | |
| 2705 | if (scalar_type->id == ZigTypeIdFloat) { | |
| 2673 | 2706 | LLVMValueRef result = LLVMBuildFDiv(g->builder, val1, val2, ""); |
| 2674 | 2707 | switch (div_kind) { |
| 2675 | 2708 | case DivKindFloat: |
| 2676 | 2709 | return result; |
| 2677 | 2710 | case DivKindExact: |
| 2678 | 2711 | if (want_runtime_safety) { |
| 2679 | LLVMValueRef floored = gen_float_op(g, result, type_entry, BuiltinFnIdFloor); | |
| 2712 | // Safety check: a / b == floor(a / b) | |
| 2713 | LLVMValueRef floored = gen_float_op(g, result, operand_type, BuiltinFnIdFloor); | |
| 2714 | ||
| 2680 | 2715 | LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactOk"); |
| 2681 | 2716 | LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactFail"); |
| 2682 | 2717 | LLVMValueRef ok_bit = LLVMBuildFCmp(g->builder, LLVMRealOEQ, floored, result, ""); |
| 2683 | ||
| 2718 | if (operand_type->id == ZigTypeIdVector) { | |
| 2719 | ok_bit = scalarize_cmp_result(g, ok_bit); | |
| 2720 | } | |
| 2684 | 2721 | LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); |
| 2685 | 2722 | |
| 2686 | 2723 | LLVMPositionBuilderAtEnd(g->builder, fail_block); |
| ... | ... | @@ -2695,54 +2732,61 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast |
| 2695 | 2732 | LLVMBasicBlockRef gez_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivTruncGEZero"); |
| 2696 | 2733 | LLVMBasicBlockRef end_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivTruncEnd"); |
| 2697 | 2734 | LLVMValueRef ltz = LLVMBuildFCmp(g->builder, LLVMRealOLT, val1, zero, ""); |
| 2735 | if (operand_type->id == ZigTypeIdVector) { | |
| 2736 | ltz = scalarize_cmp_result(g, ltz); | |
| 2737 | } | |
| 2698 | 2738 | LLVMBuildCondBr(g->builder, ltz, ltz_block, gez_block); |
| 2699 | 2739 | |
| 2700 | 2740 | LLVMPositionBuilderAtEnd(g->builder, ltz_block); |
| 2701 | LLVMValueRef ceiled = gen_float_op(g, result, type_entry, BuiltinFnIdCeil); | |
| 2741 | LLVMValueRef ceiled = gen_float_op(g, result, operand_type, BuiltinFnIdCeil); | |
| 2702 | 2742 | LLVMBasicBlockRef ceiled_end_block = LLVMGetInsertBlock(g->builder); |
| 2703 | 2743 | LLVMBuildBr(g->builder, end_block); |
| 2704 | 2744 | |
| 2705 | 2745 | LLVMPositionBuilderAtEnd(g->builder, gez_block); |
| 2706 | LLVMValueRef floored = gen_float_op(g, result, type_entry, BuiltinFnIdFloor); | |
| 2746 | LLVMValueRef floored = gen_float_op(g, result, operand_type, BuiltinFnIdFloor); | |
| 2707 | 2747 | LLVMBasicBlockRef floored_end_block = LLVMGetInsertBlock(g->builder); |
| 2708 | 2748 | LLVMBuildBr(g->builder, end_block); |
| 2709 | 2749 | |
| 2710 | 2750 | LLVMPositionBuilderAtEnd(g->builder, end_block); |
| 2711 | LLVMValueRef phi = LLVMBuildPhi(g->builder, get_llvm_type(g, type_entry), ""); | |
| 2751 | LLVMValueRef phi = LLVMBuildPhi(g->builder, get_llvm_type(g, operand_type), ""); | |
| 2712 | 2752 | LLVMValueRef incoming_values[] = { ceiled, floored }; |
| 2713 | 2753 | LLVMBasicBlockRef incoming_blocks[] = { ceiled_end_block, floored_end_block }; |
| 2714 | 2754 | LLVMAddIncoming(phi, incoming_values, incoming_blocks, 2); |
| 2715 | 2755 | return phi; |
| 2716 | 2756 | } |
| 2717 | 2757 | case DivKindFloor: |
| 2718 | return gen_float_op(g, result, type_entry, BuiltinFnIdFloor); | |
| 2758 | return gen_float_op(g, result, operand_type, BuiltinFnIdFloor); | |
| 2719 | 2759 | } |
| 2720 | 2760 | zig_unreachable(); |
| 2721 | 2761 | } |
| 2722 | 2762 | |
| 2723 | assert(type_entry->id == ZigTypeIdInt); | |
| 2763 | assert(scalar_type->id == ZigTypeIdInt); | |
| 2724 | 2764 | |
| 2725 | 2765 | switch (div_kind) { |
| 2726 | 2766 | case DivKindFloat: |
| 2727 | 2767 | zig_unreachable(); |
| 2728 | 2768 | case DivKindTrunc: |
| 2729 | if (type_entry->data.integral.is_signed) { | |
| 2769 | if (scalar_type->data.integral.is_signed) { | |
| 2730 | 2770 | return LLVMBuildSDiv(g->builder, val1, val2, ""); |
| 2731 | 2771 | } else { |
| 2732 | 2772 | return LLVMBuildUDiv(g->builder, val1, val2, ""); |
| 2733 | 2773 | } |
| 2734 | 2774 | case DivKindExact: |
| 2735 | 2775 | if (want_runtime_safety) { |
| 2776 | // Safety check: a % b == 0 | |
| 2736 | 2777 | LLVMValueRef remainder_val; |
| 2737 | if (type_entry->data.integral.is_signed) { | |
| 2778 | if (scalar_type->data.integral.is_signed) { | |
| 2738 | 2779 | remainder_val = LLVMBuildSRem(g->builder, val1, val2, ""); |
| 2739 | 2780 | } else { |
| 2740 | 2781 | remainder_val = LLVMBuildURem(g->builder, val1, val2, ""); |
| 2741 | 2782 | } |
| 2742 | LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, remainder_val, zero, ""); | |
| 2743 | 2783 | |
| 2744 | 2784 | LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactOk"); |
| 2745 | 2785 | LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactFail"); |
| 2786 | LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, remainder_val, zero, ""); | |
| 2787 | if (operand_type->id == ZigTypeIdVector) { | |
| 2788 | ok_bit = scalarize_cmp_result(g, ok_bit); | |
| 2789 | } | |
| 2746 | 2790 | LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); |
| 2747 | 2791 | |
| 2748 | 2792 | LLVMPositionBuilderAtEnd(g->builder, fail_block); |
| ... | ... | @@ -2750,14 +2794,14 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast |
| 2750 | 2794 | |
| 2751 | 2795 | LLVMPositionBuilderAtEnd(g->builder, ok_block); |
| 2752 | 2796 | } |
| 2753 | if (type_entry->data.integral.is_signed) { | |
| 2797 | if (scalar_type->data.integral.is_signed) { | |
| 2754 | 2798 | return LLVMBuildExactSDiv(g->builder, val1, val2, ""); |
| 2755 | 2799 | } else { |
| 2756 | 2800 | return LLVMBuildExactUDiv(g->builder, val1, val2, ""); |
| 2757 | 2801 | } |
| 2758 | 2802 | case DivKindFloor: |
| 2759 | 2803 | { |
| 2760 | if (!type_entry->data.integral.is_signed) { | |
| 2804 | if (!scalar_type->data.integral.is_signed) { | |
| 2761 | 2805 | return LLVMBuildUDiv(g->builder, val1, val2, ""); |
| 2762 | 2806 | } |
| 2763 | 2807 | // const d = @divTrunc(a, b); |
| ... | ... | @@ -2784,22 +2828,30 @@ enum RemKind { |
| 2784 | 2828 | }; |
| 2785 | 2829 | |
| 2786 | 2830 | static 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) | |
| 2831 | LLVMValueRef val1, LLVMValueRef val2, ZigType *operand_type, RemKind rem_kind) | |
| 2789 | 2832 | { |
| 2833 | ZigType *scalar_type = (operand_type->id == ZigTypeIdVector) ? | |
| 2834 | operand_type->data.vector.elem_type : operand_type; | |
| 2835 | ||
| 2790 | 2836 | ZigLLVMSetFastMath(g->builder, want_fast_math); |
| 2791 | 2837 | |
| 2792 | LLVMValueRef zero = LLVMConstNull(get_llvm_type(g, type_entry)); | |
| 2838 | LLVMValueRef zero = LLVMConstNull(get_llvm_type(g, operand_type)); | |
| 2793 | 2839 | if (want_runtime_safety) { |
| 2840 | // Safety check: divisor != 0 | |
| 2794 | 2841 | LLVMValueRef is_zero_bit; |
| 2795 | if (type_entry->id == ZigTypeIdInt) { | |
| 2796 | LLVMIntPredicate pred = type_entry->data.integral.is_signed ? LLVMIntSLE : LLVMIntEQ; | |
| 2842 | if (scalar_type->id == ZigTypeIdInt) { | |
| 2843 | LLVMIntPredicate pred = scalar_type->data.integral.is_signed ? LLVMIntSLE : LLVMIntEQ; | |
| 2797 | 2844 | is_zero_bit = LLVMBuildICmp(g->builder, pred, val2, zero, ""); |
| 2798 | } else if (type_entry->id == ZigTypeIdFloat) { | |
| 2845 | } else if (scalar_type->id == ZigTypeIdFloat) { | |
| 2799 | 2846 | is_zero_bit = LLVMBuildFCmp(g->builder, LLVMRealOEQ, val2, zero, ""); |
| 2800 | 2847 | } else { |
| 2801 | 2848 | zig_unreachable(); |
| 2802 | 2849 | } |
| 2850 | ||
| 2851 | if (operand_type->id == ZigTypeIdVector) { | |
| 2852 | is_zero_bit = scalarize_cmp_result(g, is_zero_bit); | |
| 2853 | } | |
| 2854 | ||
| 2803 | 2855 | LLVMBasicBlockRef rem_zero_ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "RemZeroOk"); |
| 2804 | 2856 | LLVMBasicBlockRef rem_zero_fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "RemZeroFail"); |
| 2805 | 2857 | LLVMBuildCondBr(g->builder, is_zero_bit, rem_zero_fail_block, rem_zero_ok_block); |
| ... | ... | @@ -2810,7 +2862,7 @@ static LLVMValueRef gen_rem(CodeGen *g, bool want_runtime_safety, bool want_fast |
| 2810 | 2862 | LLVMPositionBuilderAtEnd(g->builder, rem_zero_ok_block); |
| 2811 | 2863 | } |
| 2812 | 2864 | |
| 2813 | if (type_entry->id == ZigTypeIdFloat) { | |
| 2865 | if (scalar_type->id == ZigTypeIdFloat) { | |
| 2814 | 2866 | if (rem_kind == RemKindRem) { |
| 2815 | 2867 | return LLVMBuildFRem(g->builder, val1, val2, ""); |
| 2816 | 2868 | } else { |
| ... | ... | @@ -2821,8 +2873,8 @@ static LLVMValueRef gen_rem(CodeGen *g, bool want_runtime_safety, bool want_fast |
| 2821 | 2873 | return LLVMBuildSelect(g->builder, ltz, c, a, ""); |
| 2822 | 2874 | } |
| 2823 | 2875 | } else { |
| 2824 | assert(type_entry->id == ZigTypeIdInt); | |
| 2825 | if (type_entry->data.integral.is_signed) { | |
| 2876 | assert(scalar_type->id == ZigTypeIdInt); | |
| 2877 | if (scalar_type->data.integral.is_signed) { | |
| 2826 | 2878 | if (rem_kind == RemKindRem) { |
| 2827 | 2879 | return LLVMBuildSRem(g->builder, val1, val2, ""); |
| 2828 | 2880 | } else { |
| ... | ... | @@ -3010,22 +3062,22 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutableGen *executable, |
| 3010 | 3062 | } |
| 3011 | 3063 | case IrBinOpDivUnspecified: |
| 3012 | 3064 | return gen_div(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base), |
| 3013 | op1_value, op2_value, scalar_type, DivKindFloat); | |
| 3065 | op1_value, op2_value, operand_type, DivKindFloat); | |
| 3014 | 3066 | case IrBinOpDivExact: |
| 3015 | 3067 | return gen_div(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base), |
| 3016 | op1_value, op2_value, scalar_type, DivKindExact); | |
| 3068 | op1_value, op2_value, operand_type, DivKindExact); | |
| 3017 | 3069 | case IrBinOpDivTrunc: |
| 3018 | 3070 | return gen_div(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base), |
| 3019 | op1_value, op2_value, scalar_type, DivKindTrunc); | |
| 3071 | op1_value, op2_value, operand_type, DivKindTrunc); | |
| 3020 | 3072 | case IrBinOpDivFloor: |
| 3021 | 3073 | return gen_div(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base), |
| 3022 | op1_value, op2_value, scalar_type, DivKindFloor); | |
| 3074 | op1_value, op2_value, operand_type, DivKindFloor); | |
| 3023 | 3075 | case IrBinOpRemRem: |
| 3024 | 3076 | return gen_rem(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base), |
| 3025 | op1_value, op2_value, scalar_type, RemKindRem); | |
| 3077 | op1_value, op2_value, operand_type, RemKindRem); | |
| 3026 | 3078 | case IrBinOpRemMod: |
| 3027 | 3079 | return gen_rem(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base), |
| 3028 | op1_value, op2_value, scalar_type, RemKindMod); | |
| 3080 | op1_value, op2_value, operand_type, RemKindMod); | |
| 3029 | 3081 | } |
| 3030 | 3082 | zig_unreachable(); |
| 3031 | 3083 | } |
src/ir.cpp+155-129| ... | ... | @@ -16943,6 +16943,7 @@ static bool ok_float_op(IrBinOp op) { |
| 16943 | 16943 | case IrBinOpDivExact: |
| 16944 | 16944 | case IrBinOpRemRem: |
| 16945 | 16945 | case IrBinOpRemMod: |
| 16946 | case IrBinOpRemUnspecified: | |
| 16946 | 16947 | return true; |
| 16947 | 16948 | |
| 16948 | 16949 | case IrBinOpBoolOr: |
| ... | ... | @@ -16963,7 +16964,6 @@ static bool ok_float_op(IrBinOp op) { |
| 16963 | 16964 | case IrBinOpAddWrap: |
| 16964 | 16965 | case IrBinOpSubWrap: |
| 16965 | 16966 | case IrBinOpMultWrap: |
| 16966 | case IrBinOpRemUnspecified: | |
| 16967 | 16967 | case IrBinOpArrayCat: |
| 16968 | 16968 | case IrBinOpArrayMult: |
| 16969 | 16969 | return false; |
| ... | ... | @@ -16991,6 +16991,31 @@ static bool is_pointer_arithmetic_allowed(ZigType *lhs_type, IrBinOp op) { |
| 16991 | 16991 | zig_unreachable(); |
| 16992 | 16992 | } |
| 16993 | 16993 | |
| 16994 | static bool value_cmp_zero_any(ZigValue *value, Cmp predicate) { | |
| 16995 | assert(value->special == ConstValSpecialStatic); | |
| 16996 | ||
| 16997 | switch (value->type->id) { | |
| 16998 | case ZigTypeIdComptimeInt: | |
| 16999 | case ZigTypeIdInt: | |
| 17000 | return bigint_cmp_zero(&value->data.x_bigint) == predicate; | |
| 17001 | case ZigTypeIdComptimeFloat: | |
| 17002 | case ZigTypeIdFloat: | |
| 17003 | if (float_is_nan(value)) | |
| 17004 | return false; | |
| 17005 | return float_cmp_zero(value) == predicate; | |
| 17006 | case ZigTypeIdVector: { | |
| 17007 | for (size_t i = 0; i < value->type->data.vector.len; i++) { | |
| 17008 | ZigValue *scalar_val = &value->data.x_array.data.s_none.elements[i]; | |
| 17009 | if (!value_cmp_zero_any(scalar_val, predicate)) | |
| 17010 | return true; | |
| 17011 | } | |
| 17012 | return false; | |
| 17013 | } | |
| 17014 | default: | |
| 17015 | zig_unreachable(); | |
| 17016 | } | |
| 17017 | } | |
| 17018 | ||
| 16994 | 17019 | static IrInstGen *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstSrcBinOp *instruction) { |
| 16995 | 17020 | Error err; |
| 16996 | 17021 | |
| ... | ... | @@ -17096,127 +17121,13 @@ static IrInstGen *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstSrcBinOp *instruc |
| 17096 | 17121 | if (type_is_invalid(resolved_type)) |
| 17097 | 17122 | return ira->codegen->invalid_inst_gen; |
| 17098 | 17123 | |
| 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; | |
| 17124 | ZigType *scalar_type = (resolved_type->id == ZigTypeIdVector) ? | |
| 17125 | resolved_type->data.vector.elem_type : resolved_type; | |
| 17118 | 17126 | |
| 17119 | ZigValue *op2_val = ir_resolve_const(ira, op2, UndefBad); | |
| 17120 | if (op2_val == nullptr) | |
| 17121 | return ira->codegen->invalid_inst_gen; | |
| 17127 | bool is_int = scalar_type->id == ZigTypeIdInt || scalar_type->id == ZigTypeIdComptimeInt; | |
| 17128 | bool is_float = scalar_type->id == ZigTypeIdFloat || scalar_type->id == ZigTypeIdComptimeFloat; | |
| 17122 | 17129 | |
| 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; | |
| 17156 | ||
| 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; | |
| 17161 | ||
| 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) { | |
| 17130 | if (!is_int && !(is_float && ok_float_op(op_id))) { | |
| 17220 | 17131 | AstNode *source_node = instruction->base.base.source_node; |
| 17221 | 17132 | ir_add_error_node(ira, source_node, |
| 17222 | 17133 | buf_sprintf("invalid operands to binary expression: '%s' and '%s'", |
| ... | ... | @@ -17225,7 +17136,16 @@ static IrInstGen *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstSrcBinOp *instruc |
| 17225 | 17136 | return ira->codegen->invalid_inst_gen; |
| 17226 | 17137 | } |
| 17227 | 17138 | |
| 17228 | if (resolved_type->id == ZigTypeIdComptimeInt) { | |
| 17139 | IrInstGen *casted_op1 = ir_implicit_cast(ira, op1, resolved_type); | |
| 17140 | if (type_is_invalid(casted_op1->value->type)) | |
| 17141 | return ira->codegen->invalid_inst_gen; | |
| 17142 | ||
| 17143 | IrInstGen *casted_op2 = ir_implicit_cast(ira, op2, resolved_type); | |
| 17144 | if (type_is_invalid(casted_op2->value->type)) | |
| 17145 | return ira->codegen->invalid_inst_gen; | |
| 17146 | ||
| 17147 | // Comptime integers have no fixed size | |
| 17148 | if (scalar_type->id == ZigTypeIdComptimeInt) { | |
| 17229 | 17149 | if (op_id == IrBinOpAddWrap) { |
| 17230 | 17150 | op_id = IrBinOpAdd; |
| 17231 | 17151 | } else if (op_id == IrBinOpSubWrap) { |
| ... | ... | @@ -17235,25 +17155,131 @@ static IrInstGen *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstSrcBinOp *instruc |
| 17235 | 17155 | } |
| 17236 | 17156 | } |
| 17237 | 17157 | |
| 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 | ||
| 17246 | 17158 | if (instr_is_comptime(casted_op1) && instr_is_comptime(casted_op2)) { |
| 17247 | 17159 | ZigValue *op1_val = ir_resolve_const(ira, casted_op1, UndefBad); |
| 17248 | 17160 | if (op1_val == nullptr) |
| 17249 | 17161 | return ira->codegen->invalid_inst_gen; |
| 17162 | ||
| 17250 | 17163 | ZigValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad); |
| 17251 | 17164 | if (op2_val == nullptr) |
| 17252 | 17165 | return ira->codegen->invalid_inst_gen; |
| 17253 | 17166 | |
| 17167 | // Promote division with negative numbers to signed | |
| 17168 | bool is_signed_div = value_cmp_zero_any(op1_val, CmpLT) || | |
| 17169 | value_cmp_zero_any(op2_val, CmpLT); | |
| 17170 | ||
| 17171 | if (op_id == IrBinOpDivUnspecified && is_int) { | |
| 17172 | // Default to truncating division and check if it's valid for the | |
| 17173 | // given operands if signed | |
| 17174 | op_id = IrBinOpDivTrunc; | |
| 17175 | ||
| 17176 | if (is_signed_div) { | |
| 17177 | bool ok = false; | |
| 17178 | ||
| 17179 | if (value_cmp_zero_any(op2_val, CmpEQ)) { | |
| 17180 | // the division by zero error will be caught later, but we don't have a | |
| 17181 | // division function ambiguity problem. | |
| 17182 | ok = true; | |
| 17183 | } else { | |
| 17184 | IrInstGen *trunc_val = ir_analyze_math_op(ira, &instruction->base.base, resolved_type, | |
| 17185 | op1_val, IrBinOpDivTrunc, op2_val); | |
| 17186 | if (type_is_invalid(trunc_val->value->type)) | |
| 17187 | return ira->codegen->invalid_inst_gen; | |
| 17188 | ||
| 17189 | IrInstGen *floor_val = ir_analyze_math_op(ira, &instruction->base.base, resolved_type, | |
| 17190 | op1_val, IrBinOpDivFloor, op2_val); | |
| 17191 | if (type_is_invalid(floor_val->value->type)) | |
| 17192 | return ira->codegen->invalid_inst_gen; | |
| 17193 | ||
| 17194 | IrInstGen *cmp_val = ir_analyze_bin_op_cmp_numeric(ira, &instruction->base.base, | |
| 17195 | trunc_val, floor_val, IrBinOpCmpEq); | |
| 17196 | if (type_is_invalid(cmp_val->value->type)) | |
| 17197 | return ira->codegen->invalid_inst_gen; | |
| 17198 | ||
| 17199 | // We can "upgrade" the operator only if trunc(a/b) == floor(a/b) | |
| 17200 | if (!ir_resolve_bool(ira, cmp_val, &ok)) | |
| 17201 | return ira->codegen->invalid_inst_gen; | |
| 17202 | } | |
| 17203 | ||
| 17204 | if (!ok) { | |
| 17205 | ir_add_error(ira, &instruction->base.base, | |
| 17206 | buf_sprintf("division with '%s' and '%s': signed integers must use @divTrunc, @divFloor, or @divExact", | |
| 17207 | buf_ptr(&op1->value->type->name), | |
| 17208 | buf_ptr(&op2->value->type->name))); | |
| 17209 | return ira->codegen->invalid_inst_gen; | |
| 17210 | } | |
| 17211 | } | |
| 17212 | } else if (op_id == IrBinOpRemUnspecified) { | |
| 17213 | op_id = IrBinOpRemRem; | |
| 17214 | ||
| 17215 | if (is_signed_div) { | |
| 17216 | bool ok = false; | |
| 17217 | ||
| 17218 | if (value_cmp_zero_any(op2_val, CmpEQ)) { | |
| 17219 | // the division by zero error will be caught later, but we don't have a | |
| 17220 | // division function ambiguity problem. | |
| 17221 | ok = true; | |
| 17222 | } else { | |
| 17223 | IrInstGen *rem_val = ir_analyze_math_op(ira, &instruction->base.base, resolved_type, | |
| 17224 | op1_val, IrBinOpRemRem, op2_val); | |
| 17225 | if (type_is_invalid(rem_val->value->type)) | |
| 17226 | return ira->codegen->invalid_inst_gen; | |
| 17227 | ||
| 17228 | IrInstGen *mod_val = ir_analyze_math_op(ira, &instruction->base.base, resolved_type, | |
| 17229 | op1_val, IrBinOpRemMod, op2_val); | |
| 17230 | if (type_is_invalid(mod_val->value->type)) | |
| 17231 | return ira->codegen->invalid_inst_gen; | |
| 17232 | ||
| 17233 | IrInstGen *cmp_val = ir_analyze_bin_op_cmp_numeric(ira, &instruction->base.base, | |
| 17234 | rem_val, mod_val, IrBinOpCmpEq); | |
| 17235 | if (type_is_invalid(cmp_val->value->type)) | |
| 17236 | return ira->codegen->invalid_inst_gen; | |
| 17237 | ||
| 17238 | // We can "upgrade" the operator only if mod(a,b) == rem(a,b) | |
| 17239 | if (!ir_resolve_bool(ira, cmp_val, &ok)) | |
| 17240 | return ira->codegen->invalid_inst_gen; | |
| 17241 | } | |
| 17242 | ||
| 17243 | if (!ok) { | |
| 17244 | ir_add_error(ira, &instruction->base.base, | |
| 17245 | buf_sprintf("remainder division with '%s' and '%s': signed integers and floats must use @rem or @mod", | |
| 17246 | buf_ptr(&op1->value->type->name), | |
| 17247 | buf_ptr(&op2->value->type->name))); | |
| 17248 | return ira->codegen->invalid_inst_gen; | |
| 17249 | } | |
| 17250 | } | |
| 17251 | } | |
| 17252 | ||
| 17254 | 17253 | return ir_analyze_math_op(ira, &instruction->base.base, resolved_type, op1_val, op_id, op2_val); |
| 17255 | 17254 | } |
| 17256 | 17255 | |
| 17256 | const bool is_signed_div = | |
| 17257 | (scalar_type->id == ZigTypeIdInt && scalar_type->data.integral.is_signed) || | |
| 17258 | scalar_type->id == ZigTypeIdFloat; | |
| 17259 | ||
| 17260 | // Warn the user to use the proper operators here | |
| 17261 | if (op_id == IrBinOpDivUnspecified && is_int) { | |
| 17262 | op_id = IrBinOpDivTrunc; | |
| 17263 | ||
| 17264 | if (is_signed_div) { | |
| 17265 | ir_add_error(ira, &instruction->base.base, | |
| 17266 | buf_sprintf("division with '%s' and '%s': signed integers must use @divTrunc, @divFloor, or @divExact", | |
| 17267 | buf_ptr(&op1->value->type->name), | |
| 17268 | buf_ptr(&op2->value->type->name))); | |
| 17269 | return ira->codegen->invalid_inst_gen; | |
| 17270 | } | |
| 17271 | } else if (op_id == IrBinOpRemUnspecified) { | |
| 17272 | op_id = IrBinOpRemRem; | |
| 17273 | ||
| 17274 | if (is_signed_div) { | |
| 17275 | ir_add_error(ira, &instruction->base.base, | |
| 17276 | buf_sprintf("remainder division with '%s' and '%s': signed integers and floats must use @rem or @mod", | |
| 17277 | buf_ptr(&op1->value->type->name), | |
| 17278 | buf_ptr(&op2->value->type->name))); | |
| 17279 | return ira->codegen->invalid_inst_gen; | |
| 17280 | } | |
| 17281 | } | |
| 17282 | ||
| 17257 | 17283 | return ir_build_bin_op_gen(ira, &instruction->base.base, resolved_type, |
| 17258 | 17284 | op_id, casted_op1, casted_op2, instruction->safety_check_on); |
| 17259 | 17285 | } |
test/stage1/behavior/vector.zig+75| ... | ... | @@ -276,3 +276,78 @@ test "vector comparison operators" { |
| 276 | 276 | S.doTheTest(); |
| 277 | 277 | comptime S.doTheTest(); |
| 278 | 278 | } |
| 279 | ||
| 280 | test "vector division operators" { | |
| 281 | const S = struct { | |
| 282 | fn doTheTestDiv(comptime T: type, x: @Vector(4, T), y: @Vector(4, T)) void { | |
| 283 | if (!comptime std.meta.trait.isSignedInt(T)) { | |
| 284 | const d0 = x / y; | |
| 285 | for (@as([4]T, d0)) |v, i| { | |
| 286 | expectEqual(x[i] / y[i], v); | |
| 287 | } | |
| 288 | } | |
| 289 | const d1 = @divExact(x, y); | |
| 290 | for (@as([4]T, d1)) |v, i| { | |
| 291 | expectEqual(@divExact(x[i], y[i]), v); | |
| 292 | } | |
| 293 | const d2 = @divFloor(x, y); | |
| 294 | for (@as([4]T, d2)) |v, i| { | |
| 295 | expectEqual(@divFloor(x[i], y[i]), v); | |
| 296 | } | |
| 297 | const d3 = @divTrunc(x, y); | |
| 298 | for (@as([4]T, d3)) |v, i| { | |
| 299 | expectEqual(@divTrunc(x[i], y[i]), v); | |
| 300 | } | |
| 301 | } | |
| 302 | ||
| 303 | fn doTheTestMod(comptime T: type, x: @Vector(4, T), y: @Vector(4, T)) void { | |
| 304 | if ((!comptime std.meta.trait.isSignedInt(T)) and @typeInfo(T) != .Float) { | |
| 305 | const r0 = x % y; | |
| 306 | for (@as([4]T, r0)) |v, i| { | |
| 307 | expectEqual(x[i] % y[i], v); | |
| 308 | } | |
| 309 | } | |
| 310 | const r1 = @mod(x, y); | |
| 311 | for (@as([4]T, r1)) |v, i| { | |
| 312 | expectEqual(@mod(x[i], y[i]), v); | |
| 313 | } | |
| 314 | const r2 = @rem(x, y); | |
| 315 | for (@as([4]T, r2)) |v, i| { | |
| 316 | expectEqual(@rem(x[i], y[i]), v); | |
| 317 | } | |
| 318 | } | |
| 319 | ||
| 320 | fn doTheTest() void { | |
| 321 | doTheTestDiv(f16, [4]f16{ 4.0, -4.0, 4.0, -4.0 }, [4]f16{ 1.0, 2.0, -1.0, -2.0 }); | |
| 322 | doTheTestDiv(f32, [4]f32{ 4.0, -4.0, 4.0, -4.0 }, [4]f32{ 1.0, 2.0, -1.0, -2.0 }); | |
| 323 | doTheTestDiv(f64, [4]f64{ 4.0, -4.0, 4.0, -4.0 }, [4]f64{ 1.0, 2.0, -1.0, -2.0 }); | |
| 324 | ||
| 325 | doTheTestMod(f16, [4]f16{ 4.0, -4.0, 4.0, -4.0 }, [4]f16{ 1.0, 2.0, 0.5, 3.0 }); | |
| 326 | doTheTestMod(f32, [4]f32{ 4.0, -4.0, 4.0, -4.0 }, [4]f32{ 1.0, 2.0, 0.5, 3.0 }); | |
| 327 | doTheTestMod(f64, [4]f64{ 4.0, -4.0, 4.0, -4.0 }, [4]f64{ 1.0, 2.0, 0.5, 3.0 }); | |
| 328 | ||
| 329 | doTheTestDiv(i8, [4]i8{ 4, -4, 4, -4 }, [4]i8{ 1, 2, -1, -2 }); | |
| 330 | doTheTestDiv(i16, [4]i16{ 4, -4, 4, -4 }, [4]i16{ 1, 2, -1, -2 }); | |
| 331 | doTheTestDiv(i32, [4]i32{ 4, -4, 4, -4 }, [4]i32{ 1, 2, -1, -2 }); | |
| 332 | doTheTestDiv(i64, [4]i64{ 4, -4, 4, -4 }, [4]i64{ 1, 2, -1, -2 }); | |
| 333 | ||
| 334 | doTheTestMod(i8, [4]i8{ 4, -4, 4, -4 }, [4]i8{ 1, 2, 4, 8 }); | |
| 335 | doTheTestMod(i16, [4]i16{ 4, -4, 4, -4 }, [4]i16{ 1, 2, 4, 8 }); | |
| 336 | doTheTestMod(i32, [4]i32{ 4, -4, 4, -4 }, [4]i32{ 1, 2, 4, 8 }); | |
| 337 | doTheTestMod(i64, [4]i64{ 4, -4, 4, -4 }, [4]i64{ 1, 2, 4, 8 }); | |
| 338 | ||
| 339 | doTheTestDiv(u8, [4]u8{ 1, 2, 4, 8 }, [4]u8{ 1, 1, 2, 4 }); | |
| 340 | doTheTestDiv(u16, [4]u16{ 1, 2, 4, 8 }, [4]u16{ 1, 1, 2, 4 }); | |
| 341 | doTheTestDiv(u32, [4]u32{ 1, 2, 4, 8 }, [4]u32{ 1, 1, 2, 4 }); | |
| 342 | doTheTestDiv(u64, [4]u64{ 1, 2, 4, 8 }, [4]u64{ 1, 1, 2, 4 }); | |
| 343 | ||
| 344 | doTheTestMod(u8, [4]u8{ 1, 2, 4, 8 }, [4]u8{ 1, 1, 2, 4 }); | |
| 345 | doTheTestMod(u16, [4]u16{ 1, 2, 4, 8 }, [4]u16{ 1, 1, 2, 4 }); | |
| 346 | doTheTestMod(u32, [4]u32{ 1, 2, 4, 8 }, [4]u32{ 1, 1, 2, 4 }); | |
| 347 | doTheTestMod(u64, [4]u64{ 1, 2, 4, 8 }, [4]u64{ 1, 1, 2, 4 }); | |
| 348 | } | |
| 349 | }; | |
| 350 | ||
| 351 | S.doTheTest(); | |
| 352 | comptime S.doTheTest(); | |
| 353 | } |