authorgravatar for thatlemon@gmail.comLemonBoy <thatlemon@gmail.com> 2020-03-14 13:23:41+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-04-05 18:34:31-04:00
log54ffcf95a8596aa3adf57cddbe079a24f849f408
tree7e189bd8ef3f39ae28fe31791877eab372f671e8
parente2dc63644ab3d8e5cdaec2d58dc57c587295081f
signaturelock-open Commit is signed but in an unrecognized format.

ir: Support div/mod/rem on vector types

Closes #4050

3 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,12 +2591,7 @@ static LLVMValueRef gen_overflow_shr_op(CodeGen *g, ZigType *type_entry,
2591}2591}
25922592
2593static LLVMValueRef gen_float_op(CodeGen *g, LLVMValueRef val, ZigType *type_entry, BuiltinFnId op) {2593static LLVMValueRef gen_float_op(CodeGen *g, LLVMValueRef val, ZigType *type_entry, BuiltinFnId op) {
2594 if ((op == BuiltinFnIdCeil ||2594 assert(type_entry->id == ZigTypeIdFloat || type_entry->id == ZigTypeIdVector);
2595 op == BuiltinFnIdFloor) &&
2596 type_entry->id == ZigTypeIdInt)
2597 return val;
2598 assert(type_entry->id == ZigTypeIdFloat);
2599
2600 LLVMValueRef floor_fn = get_float_fn(g, type_entry, ZigLLVMFnIdFloatOp, op);2595 LLVMValueRef floor_fn = get_float_fn(g, type_entry, ZigLLVMFnIdFloatOp, op);
2601 return LLVMBuildCall(g->builder, floor_fn, &val, 1, "");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,6 +2607,21 @@ static LLVMValueRef bigint_to_llvm_const(LLVMTypeRef type_ref, BigInt *bigint) {
2612 if (bigint->digit_count == 0) {2607 if (bigint->digit_count == 0) {
2613 return LLVMConstNull(type_ref);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 LLVMValueRef unsigned_val;2625 LLVMValueRef unsigned_val;
2616 if (bigint->digit_count == 1) {2626 if (bigint->digit_count == 1) {
2617 unsigned_val = LLVMConstInt(type_ref, bigint_ptr(bigint)[0], false);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,22 +2635,40 @@ static LLVMValueRef bigint_to_llvm_const(LLVMTypeRef type_ref, BigInt *bigint) {
2625 }2635 }
2626}2636}
26272637
2638// Collapses a <N x i1> vector into a single i1 whose value is 1 iff all the
2639// vector elements are 1
2640static 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
2628static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast_math,2648static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast_math,
2629 LLVMValueRef val1, LLVMValueRef val2,2649 LLVMValueRef val1, LLVMValueRef val2, ZigType *operand_type, DivKind div_kind)
2630 ZigType *type_entry, DivKind div_kind)
2631{2650{
2651 ZigType *scalar_type = (operand_type->id == ZigTypeIdVector) ?
2652 operand_type->data.vector.elem_type : operand_type;
2653
2632 ZigLLVMSetFastMath(g->builder, want_fast_math);2654 ZigLLVMSetFastMath(g->builder, want_fast_math);
26332655
2634 LLVMValueRef zero = LLVMConstNull(get_llvm_type(g, type_entry));2656 LLVMValueRef zero = LLVMConstNull(get_llvm_type(g, operand_type));
2635 if (want_runtime_safety && (want_fast_math || type_entry->id != ZigTypeIdFloat)) {2657 if (want_runtime_safety && (want_fast_math || scalar_type->id != ZigTypeIdFloat)) {
2658 // Safety check: divisor != 0
2636 LLVMValueRef is_zero_bit;2659 LLVMValueRef is_zero_bit;
2637 if (type_entry->id == ZigTypeIdInt) {2660 if (scalar_type->id == ZigTypeIdInt) {
2638 is_zero_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, val2, zero, "");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 is_zero_bit = LLVMBuildFCmp(g->builder, LLVMRealOEQ, val2, zero, "");2663 is_zero_bit = LLVMBuildFCmp(g->builder, LLVMRealOEQ, val2, zero, "");
2641 } else {2664 } else {
2642 zig_unreachable();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 LLVMBasicBlockRef div_zero_fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivZeroFail");2672 LLVMBasicBlockRef div_zero_fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivZeroFail");
2645 LLVMBasicBlockRef div_zero_ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivZeroOk");2673 LLVMBasicBlockRef div_zero_ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivZeroOk");
2646 LLVMBuildCondBr(g->builder, is_zero_bit, div_zero_fail_block, div_zero_ok_block);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,16 +2678,21 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast
26502678
2651 LLVMPositionBuilderAtEnd(g->builder, div_zero_ok_block);2679 LLVMPositionBuilderAtEnd(g->builder, div_zero_ok_block);
26522680
2653 if (type_entry->id == ZigTypeIdInt && type_entry->data.integral.is_signed) {2681 // Safety check: check for overflow (dividend = minInt and divisor = -1)
2654 LLVMValueRef neg_1_value = LLVMConstInt(get_llvm_type(g, type_entry), -1, true);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 BigInt int_min_bi = {0};2684 BigInt int_min_bi = {0};
2656 eval_min_max_value_int(g, type_entry, &int_min_bi, false);2685 eval_min_max_value_int(g, scalar_type, &int_min_bi, false);
2657 LLVMValueRef int_min_value = bigint_to_llvm_const(get_llvm_type(g, type_entry), &int_min_bi);2686 LLVMValueRef int_min_value = bigint_to_llvm_const(get_llvm_type(g, operand_type), &int_min_bi);
2687
2658 LLVMBasicBlockRef overflow_fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivOverflowFail");2688 LLVMBasicBlockRef overflow_fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivOverflowFail");
2659 LLVMBasicBlockRef overflow_ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivOverflowOk");2689 LLVMBasicBlockRef overflow_ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivOverflowOk");
2660 LLVMValueRef num_is_int_min = LLVMBuildICmp(g->builder, LLVMIntEQ, val1, int_min_value, "");2690 LLVMValueRef num_is_int_min = LLVMBuildICmp(g->builder, LLVMIntEQ, val1, int_min_value, "");
2661 LLVMValueRef den_is_neg_1 = LLVMBuildICmp(g->builder, LLVMIntEQ, val2, neg_1_value, "");2691 LLVMValueRef den_is_neg_1 = LLVMBuildICmp(g->builder, LLVMIntEQ, val2, neg_1_value, "");
2662 LLVMValueRef overflow_fail_bit = LLVMBuildAnd(g->builder, num_is_int_min, den_is_neg_1, "");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 LLVMBuildCondBr(g->builder, overflow_fail_bit, overflow_fail_block, overflow_ok_block);2696 LLVMBuildCondBr(g->builder, overflow_fail_bit, overflow_fail_block, overflow_ok_block);
26642697
2665 LLVMPositionBuilderAtEnd(g->builder, overflow_fail_block);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,18 +2702,22 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast
2669 }2702 }
2670 }2703 }
26712704
2672 if (type_entry->id == ZigTypeIdFloat) {2705 if (scalar_type->id == ZigTypeIdFloat) {
2673 LLVMValueRef result = LLVMBuildFDiv(g->builder, val1, val2, "");2706 LLVMValueRef result = LLVMBuildFDiv(g->builder, val1, val2, "");
2674 switch (div_kind) {2707 switch (div_kind) {
2675 case DivKindFloat:2708 case DivKindFloat:
2676 return result;2709 return result;
2677 case DivKindExact:2710 case DivKindExact:
2678 if (want_runtime_safety) {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 LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactOk");2715 LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactOk");
2681 LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactFail");2716 LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactFail");
2682 LLVMValueRef ok_bit = LLVMBuildFCmp(g->builder, LLVMRealOEQ, floored, result, "");2717 LLVMValueRef ok_bit = LLVMBuildFCmp(g->builder, LLVMRealOEQ, floored, result, "");
26832718 if (operand_type->id == ZigTypeIdVector) {
2719 ok_bit = scalarize_cmp_result(g, ok_bit);
2720 }
2684 LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block);2721 LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block);
26852722
2686 LLVMPositionBuilderAtEnd(g->builder, fail_block);2723 LLVMPositionBuilderAtEnd(g->builder, fail_block);
...@@ -2695,54 +2732,61 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast...@@ -2695,54 +2732,61 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast
2695 LLVMBasicBlockRef gez_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivTruncGEZero");2732 LLVMBasicBlockRef gez_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivTruncGEZero");
2696 LLVMBasicBlockRef end_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivTruncEnd");2733 LLVMBasicBlockRef end_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivTruncEnd");
2697 LLVMValueRef ltz = LLVMBuildFCmp(g->builder, LLVMRealOLT, val1, zero, "");2734 LLVMValueRef ltz = LLVMBuildFCmp(g->builder, LLVMRealOLT, val1, zero, "");
2735 if (operand_type->id == ZigTypeIdVector) {
2736 ltz = scalarize_cmp_result(g, ltz);
2737 }
2698 LLVMBuildCondBr(g->builder, ltz, ltz_block, gez_block);2738 LLVMBuildCondBr(g->builder, ltz, ltz_block, gez_block);
26992739
2700 LLVMPositionBuilderAtEnd(g->builder, ltz_block);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 LLVMBasicBlockRef ceiled_end_block = LLVMGetInsertBlock(g->builder);2742 LLVMBasicBlockRef ceiled_end_block = LLVMGetInsertBlock(g->builder);
2703 LLVMBuildBr(g->builder, end_block);2743 LLVMBuildBr(g->builder, end_block);
27042744
2705 LLVMPositionBuilderAtEnd(g->builder, gez_block);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 LLVMBasicBlockRef floored_end_block = LLVMGetInsertBlock(g->builder);2747 LLVMBasicBlockRef floored_end_block = LLVMGetInsertBlock(g->builder);
2708 LLVMBuildBr(g->builder, end_block);2748 LLVMBuildBr(g->builder, end_block);
27092749
2710 LLVMPositionBuilderAtEnd(g->builder, end_block);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 LLVMValueRef incoming_values[] = { ceiled, floored };2752 LLVMValueRef incoming_values[] = { ceiled, floored };
2713 LLVMBasicBlockRef incoming_blocks[] = { ceiled_end_block, floored_end_block };2753 LLVMBasicBlockRef incoming_blocks[] = { ceiled_end_block, floored_end_block };
2714 LLVMAddIncoming(phi, incoming_values, incoming_blocks, 2);2754 LLVMAddIncoming(phi, incoming_values, incoming_blocks, 2);
2715 return phi;2755 return phi;
2716 }2756 }
2717 case DivKindFloor: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 zig_unreachable();2760 zig_unreachable();
2721 }2761 }
27222762
2723 assert(type_entry->id == ZigTypeIdInt);2763 assert(scalar_type->id == ZigTypeIdInt);
27242764
2725 switch (div_kind) {2765 switch (div_kind) {
2726 case DivKindFloat:2766 case DivKindFloat:
2727 zig_unreachable();2767 zig_unreachable();
2728 case DivKindTrunc:2768 case DivKindTrunc:
2729 if (type_entry->data.integral.is_signed) {2769 if (scalar_type->data.integral.is_signed) {
2730 return LLVMBuildSDiv(g->builder, val1, val2, "");2770 return LLVMBuildSDiv(g->builder, val1, val2, "");
2731 } else {2771 } else {
2732 return LLVMBuildUDiv(g->builder, val1, val2, "");2772 return LLVMBuildUDiv(g->builder, val1, val2, "");
2733 }2773 }
2734 case DivKindExact:2774 case DivKindExact:
2735 if (want_runtime_safety) {2775 if (want_runtime_safety) {
2776 // Safety check: a % b == 0
2736 LLVMValueRef remainder_val;2777 LLVMValueRef remainder_val;
2737 if (type_entry->data.integral.is_signed) {2778 if (scalar_type->data.integral.is_signed) {
2738 remainder_val = LLVMBuildSRem(g->builder, val1, val2, "");2779 remainder_val = LLVMBuildSRem(g->builder, val1, val2, "");
2739 } else {2780 } else {
2740 remainder_val = LLVMBuildURem(g->builder, val1, val2, "");2781 remainder_val = LLVMBuildURem(g->builder, val1, val2, "");
2741 }2782 }
2742 LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, remainder_val, zero, "");
27432783
2744 LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactOk");2784 LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactOk");
2745 LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactFail");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 LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block);2790 LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block);
27472791
2748 LLVMPositionBuilderAtEnd(g->builder, fail_block);2792 LLVMPositionBuilderAtEnd(g->builder, fail_block);
...@@ -2750,14 +2794,14 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast...@@ -2750,14 +2794,14 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast
27502794
2751 LLVMPositionBuilderAtEnd(g->builder, ok_block);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 return LLVMBuildExactSDiv(g->builder, val1, val2, "");2798 return LLVMBuildExactSDiv(g->builder, val1, val2, "");
2755 } else {2799 } else {
2756 return LLVMBuildExactUDiv(g->builder, val1, val2, "");2800 return LLVMBuildExactUDiv(g->builder, val1, val2, "");
2757 }2801 }
2758 case DivKindFloor:2802 case DivKindFloor:
2759 {2803 {
2760 if (!type_entry->data.integral.is_signed) {2804 if (!scalar_type->data.integral.is_signed) {
2761 return LLVMBuildUDiv(g->builder, val1, val2, "");2805 return LLVMBuildUDiv(g->builder, val1, val2, "");
2762 }2806 }
2763 // const d = @divTrunc(a, b);2807 // const d = @divTrunc(a, b);
...@@ -2784,22 +2828,30 @@ enum RemKind {...@@ -2784,22 +2828,30 @@ enum RemKind {
2784};2828};
27852829
2786static LLVMValueRef gen_rem(CodeGen *g, bool want_runtime_safety, bool want_fast_math,2830static LLVMValueRef gen_rem(CodeGen *g, bool want_runtime_safety, bool want_fast_math,
2787 LLVMValueRef val1, LLVMValueRef val2,2831 LLVMValueRef val1, LLVMValueRef val2, ZigType *operand_type, RemKind rem_kind)
2788 ZigType *type_entry, RemKind rem_kind)
2789{2832{
2833 ZigType *scalar_type = (operand_type->id == ZigTypeIdVector) ?
2834 operand_type->data.vector.elem_type : operand_type;
2835
2790 ZigLLVMSetFastMath(g->builder, want_fast_math);2836 ZigLLVMSetFastMath(g->builder, want_fast_math);
27912837
2792 LLVMValueRef zero = LLVMConstNull(get_llvm_type(g, type_entry));2838 LLVMValueRef zero = LLVMConstNull(get_llvm_type(g, operand_type));
2793 if (want_runtime_safety) {2839 if (want_runtime_safety) {
2840 // Safety check: divisor != 0
2794 LLVMValueRef is_zero_bit;2841 LLVMValueRef is_zero_bit;
2795 if (type_entry->id == ZigTypeIdInt) {2842 if (scalar_type->id == ZigTypeIdInt) {
2796 LLVMIntPredicate pred = type_entry->data.integral.is_signed ? LLVMIntSLE : LLVMIntEQ;2843 LLVMIntPredicate pred = scalar_type->data.integral.is_signed ? LLVMIntSLE : LLVMIntEQ;
2797 is_zero_bit = LLVMBuildICmp(g->builder, pred, val2, zero, "");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 is_zero_bit = LLVMBuildFCmp(g->builder, LLVMRealOEQ, val2, zero, "");2846 is_zero_bit = LLVMBuildFCmp(g->builder, LLVMRealOEQ, val2, zero, "");
2800 } else {2847 } else {
2801 zig_unreachable();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 LLVMBasicBlockRef rem_zero_ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "RemZeroOk");2855 LLVMBasicBlockRef rem_zero_ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "RemZeroOk");
2804 LLVMBasicBlockRef rem_zero_fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "RemZeroFail");2856 LLVMBasicBlockRef rem_zero_fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "RemZeroFail");
2805 LLVMBuildCondBr(g->builder, is_zero_bit, rem_zero_fail_block, rem_zero_ok_block);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,7 +2862,7 @@ static LLVMValueRef gen_rem(CodeGen *g, bool want_runtime_safety, bool want_fast
2810 LLVMPositionBuilderAtEnd(g->builder, rem_zero_ok_block);2862 LLVMPositionBuilderAtEnd(g->builder, rem_zero_ok_block);
2811 }2863 }
28122864
2813 if (type_entry->id == ZigTypeIdFloat) {2865 if (scalar_type->id == ZigTypeIdFloat) {
2814 if (rem_kind == RemKindRem) {2866 if (rem_kind == RemKindRem) {
2815 return LLVMBuildFRem(g->builder, val1, val2, "");2867 return LLVMBuildFRem(g->builder, val1, val2, "");
2816 } else {2868 } else {
...@@ -2821,8 +2873,8 @@ static LLVMValueRef gen_rem(CodeGen *g, bool want_runtime_safety, bool want_fast...@@ -2821,8 +2873,8 @@ static LLVMValueRef gen_rem(CodeGen *g, bool want_runtime_safety, bool want_fast
2821 return LLVMBuildSelect(g->builder, ltz, c, a, "");2873 return LLVMBuildSelect(g->builder, ltz, c, a, "");
2822 }2874 }
2823 } else {2875 } else {
2824 assert(type_entry->id == ZigTypeIdInt);2876 assert(scalar_type->id == ZigTypeIdInt);
2825 if (type_entry->data.integral.is_signed) {2877 if (scalar_type->data.integral.is_signed) {
2826 if (rem_kind == RemKindRem) {2878 if (rem_kind == RemKindRem) {
2827 return LLVMBuildSRem(g->builder, val1, val2, "");2879 return LLVMBuildSRem(g->builder, val1, val2, "");
2828 } else {2880 } else {
...@@ -3010,22 +3062,22 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutableGen *executable,...@@ -3010,22 +3062,22 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutableGen *executable,
3010 }3062 }
3011 case IrBinOpDivUnspecified:3063 case IrBinOpDivUnspecified:
3012 return gen_div(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base),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 case IrBinOpDivExact:3066 case IrBinOpDivExact:
3015 return gen_div(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base),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 case IrBinOpDivTrunc:3069 case IrBinOpDivTrunc:
3018 return gen_div(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base),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 case IrBinOpDivFloor:3072 case IrBinOpDivFloor:
3021 return gen_div(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base),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 case IrBinOpRemRem:3075 case IrBinOpRemRem:
3024 return gen_rem(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base),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 case IrBinOpRemMod:3078 case IrBinOpRemMod:
3027 return gen_rem(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base),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 zig_unreachable();3082 zig_unreachable();
3031}3083}
src/ir.cpp+155-129
...@@ -16943,6 +16943,7 @@ static bool ok_float_op(IrBinOp op) {...@@ -16943,6 +16943,7 @@ static bool ok_float_op(IrBinOp op) {
16943 case IrBinOpDivExact:16943 case IrBinOpDivExact:
16944 case IrBinOpRemRem:16944 case IrBinOpRemRem:
16945 case IrBinOpRemMod:16945 case IrBinOpRemMod:
16946 case IrBinOpRemUnspecified:
16946 return true;16947 return true;
1694716948
16948 case IrBinOpBoolOr:16949 case IrBinOpBoolOr:
...@@ -16963,7 +16964,6 @@ static bool ok_float_op(IrBinOp op) {...@@ -16963,7 +16964,6 @@ static bool ok_float_op(IrBinOp op) {
16963 case IrBinOpAddWrap:16964 case IrBinOpAddWrap:
16964 case IrBinOpSubWrap:16965 case IrBinOpSubWrap:
16965 case IrBinOpMultWrap:16966 case IrBinOpMultWrap:
16966 case IrBinOpRemUnspecified:
16967 case IrBinOpArrayCat:16967 case IrBinOpArrayCat:
16968 case IrBinOpArrayMult:16968 case IrBinOpArrayMult:
16969 return false;16969 return false;
...@@ -16991,6 +16991,31 @@ static bool is_pointer_arithmetic_allowed(ZigType *lhs_type, IrBinOp op) {...@@ -16991,6 +16991,31 @@ static bool is_pointer_arithmetic_allowed(ZigType *lhs_type, IrBinOp op) {
16991 zig_unreachable();16991 zig_unreachable();
16992}16992}
1699316993
16994static 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
16994static IrInstGen *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstSrcBinOp *instruction) {17019static IrInstGen *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstSrcBinOp *instruction) {
16995 Error err;17020 Error err;
1699617021
...@@ -17096,127 +17121,13 @@ static IrInstGen *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstSrcBinOp *instruc...@@ -17096,127 +17121,13 @@ static IrInstGen *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstSrcBinOp *instruc
17096 if (type_is_invalid(resolved_type))17121 if (type_is_invalid(resolved_type))
17097 return ira->codegen->invalid_inst_gen;17122 return ira->codegen->invalid_inst_gen;
1709817123
17099 bool is_int = resolved_type->id == ZigTypeIdInt || resolved_type->id == ZigTypeIdComptimeInt;17124 ZigType *scalar_type = (resolved_type->id == ZigTypeIdVector) ?
17100 bool is_float = resolved_type->id == ZigTypeIdFloat || resolved_type->id == ZigTypeIdComptimeFloat;17125 resolved_type->data.vector.elem_type : resolved_type;
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;
1711817126
17119 ZigValue *op2_val = ir_resolve_const(ira, op2, UndefBad);17127 bool is_int = scalar_type->id == ZigTypeIdInt || scalar_type->id == ZigTypeIdComptimeInt;
17120 if (op2_val == nullptr)17128 bool is_float = scalar_type->id == ZigTypeIdFloat || scalar_type->id == ZigTypeIdComptimeFloat;
17121 return ira->codegen->invalid_inst_gen;
1712217129
17123 if (bigint_cmp_zero(&op2_val->data.x_bigint) == CmpEQ) {17130 if (!is_int && !(is_float && ok_float_op(op_id))) {
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) {
17220 AstNode *source_node = instruction->base.base.source_node;17131 AstNode *source_node = instruction->base.base.source_node;
17221 ir_add_error_node(ira, source_node,17132 ir_add_error_node(ira, source_node,
17222 buf_sprintf("invalid operands to binary expression: '%s' and '%s'",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,7 +17136,16 @@ static IrInstGen *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstSrcBinOp *instruc
17225 return ira->codegen->invalid_inst_gen;17136 return ira->codegen->invalid_inst_gen;
17226 }17137 }
1722717138
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 if (op_id == IrBinOpAddWrap) {17149 if (op_id == IrBinOpAddWrap) {
17230 op_id = IrBinOpAdd;17150 op_id = IrBinOpAdd;
17231 } else if (op_id == IrBinOpSubWrap) {17151 } else if (op_id == IrBinOpSubWrap) {
...@@ -17235,25 +17155,131 @@ static IrInstGen *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstSrcBinOp *instruc...@@ -17235,25 +17155,131 @@ static IrInstGen *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstSrcBinOp *instruc
17235 }17155 }
17236 }17156 }
1723717157
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 if (instr_is_comptime(casted_op1) && instr_is_comptime(casted_op2)) {17158 if (instr_is_comptime(casted_op1) && instr_is_comptime(casted_op2)) {
17247 ZigValue *op1_val = ir_resolve_const(ira, casted_op1, UndefBad);17159 ZigValue *op1_val = ir_resolve_const(ira, casted_op1, UndefBad);
17248 if (op1_val == nullptr)17160 if (op1_val == nullptr)
17249 return ira->codegen->invalid_inst_gen;17161 return ira->codegen->invalid_inst_gen;
17162
17250 ZigValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad);17163 ZigValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad);
17251 if (op2_val == nullptr)17164 if (op2_val == nullptr)
17252 return ira->codegen->invalid_inst_gen;17165 return ira->codegen->invalid_inst_gen;
1725317166
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 return ir_analyze_math_op(ira, &instruction->base.base, resolved_type, op1_val, op_id, op2_val);17253 return ir_analyze_math_op(ira, &instruction->base.base, resolved_type, op1_val, op_id, op2_val);
17255 }17254 }
1725617255
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 return ir_build_bin_op_gen(ira, &instruction->base.base, resolved_type,17283 return ir_build_bin_op_gen(ira, &instruction->base.base, resolved_type,
17258 op_id, casted_op1, casted_op2, instruction->safety_check_on);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,3 +276,78 @@ test "vector comparison operators" {
276 S.doTheTest();276 S.doTheTest();
277 comptime S.doTheTest();277 comptime S.doTheTest();
278}278}
279
280test "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}