| ... | ... | @@ -715,38 +715,59 @@ static void clear_debug_source_node(CodeGen *g) { |
| 715 | 715 | ZigLLVMClearCurrentDebugLocation(g->builder); |
| 716 | 716 | } |
| 717 | 717 | |
| 718 | | static LLVMValueRef get_arithmetic_overflow_fn(CodeGen *g, ZigType *type_entry, |
| 718 | static LLVMValueRef get_arithmetic_overflow_fn(CodeGen *g, ZigType *operand_type, |
| 719 | 719 | const char *signed_name, const char *unsigned_name) |
| 720 | 720 | { |
| 721 | ZigType *int_type = (operand_type->id == ZigTypeIdVector) ? operand_type->data.vector.elem_type : operand_type; |
| 721 | 722 | char fn_name[64]; |
| 722 | 723 | |
| 723 | | assert(type_entry->id == ZigTypeIdInt); |
| 724 | | const char *signed_str = type_entry->data.integral.is_signed ? signed_name : unsigned_name; |
| 725 | | sprintf(fn_name, "llvm.%s.with.overflow.i%" PRIu32, signed_str, type_entry->data.integral.bit_count); |
| 724 | assert(int_type->id == ZigTypeIdInt); |
| 725 | const char *signed_str = int_type->data.integral.is_signed ? signed_name : unsigned_name; |
| 726 | 726 | |
| 727 | | LLVMTypeRef return_elem_types[] = { |
| 728 | | type_entry->type_ref, |
| 729 | | LLVMInt1Type(), |
| 730 | | }; |
| 731 | 727 | LLVMTypeRef param_types[] = { |
| 732 | | type_entry->type_ref, |
| 733 | | type_entry->type_ref, |
| 728 | operand_type->type_ref, |
| 729 | operand_type->type_ref, |
| 734 | 730 | }; |
| 735 | | LLVMTypeRef return_struct_type = LLVMStructType(return_elem_types, 2, false); |
| 736 | | LLVMTypeRef fn_type = LLVMFunctionType(return_struct_type, param_types, 2, false); |
| 737 | | LLVMValueRef fn_val = LLVMAddFunction(g->module, fn_name, fn_type); |
| 738 | | assert(LLVMGetIntrinsicID(fn_val)); |
| 739 | | return fn_val; |
| 731 | |
| 732 | if (operand_type->id == ZigTypeIdVector) { |
| 733 | sprintf(fn_name, "llvm.%s.with.overflow.v%" PRIu32 "i%" PRIu32, signed_str, |
| 734 | operand_type->data.vector.len, int_type->data.integral.bit_count); |
| 735 | |
| 736 | LLVMTypeRef return_elem_types[] = { |
| 737 | operand_type->type_ref, |
| 738 | LLVMVectorType(LLVMInt1Type(), operand_type->data.vector.len), |
| 739 | }; |
| 740 | LLVMTypeRef return_struct_type = LLVMStructType(return_elem_types, 2, false); |
| 741 | LLVMTypeRef fn_type = LLVMFunctionType(return_struct_type, param_types, 2, false); |
| 742 | LLVMValueRef fn_val = LLVMAddFunction(g->module, fn_name, fn_type); |
| 743 | assert(LLVMGetIntrinsicID(fn_val)); |
| 744 | return fn_val; |
| 745 | } else { |
| 746 | sprintf(fn_name, "llvm.%s.with.overflow.i%" PRIu32, signed_str, int_type->data.integral.bit_count); |
| 747 | |
| 748 | LLVMTypeRef return_elem_types[] = { |
| 749 | operand_type->type_ref, |
| 750 | LLVMInt1Type(), |
| 751 | }; |
| 752 | LLVMTypeRef return_struct_type = LLVMStructType(return_elem_types, 2, false); |
| 753 | LLVMTypeRef fn_type = LLVMFunctionType(return_struct_type, param_types, 2, false); |
| 754 | LLVMValueRef fn_val = LLVMAddFunction(g->module, fn_name, fn_type); |
| 755 | assert(LLVMGetIntrinsicID(fn_val)); |
| 756 | return fn_val; |
| 757 | } |
| 740 | 758 | } |
| 741 | 759 | |
| 742 | | static LLVMValueRef get_int_overflow_fn(CodeGen *g, ZigType *type_entry, AddSubMul add_sub_mul) { |
| 743 | | assert(type_entry->id == ZigTypeIdInt); |
| 760 | static LLVMValueRef get_int_overflow_fn(CodeGen *g, ZigType *operand_type, AddSubMul add_sub_mul) { |
| 761 | ZigType *int_type = (operand_type->id == ZigTypeIdVector) ? operand_type->data.vector.elem_type : operand_type; |
| 762 | assert(int_type->id == ZigTypeIdInt); |
| 744 | 763 | |
| 745 | 764 | ZigLLVMFnKey key = {}; |
| 746 | 765 | key.id = ZigLLVMFnIdOverflowArithmetic; |
| 747 | | key.data.overflow_arithmetic.is_signed = type_entry->data.integral.is_signed; |
| 766 | key.data.overflow_arithmetic.is_signed = int_type->data.integral.is_signed; |
| 748 | 767 | key.data.overflow_arithmetic.add_sub_mul = add_sub_mul; |
| 749 | | key.data.overflow_arithmetic.bit_count = (uint32_t)type_entry->data.integral.bit_count; |
| 768 | key.data.overflow_arithmetic.bit_count = (uint32_t)int_type->data.integral.bit_count; |
| 769 | key.data.overflow_arithmetic.vector_len = (operand_type->id == ZigTypeIdVector) ? |
| 770 | operand_type->data.vector.len : 0; |
| 750 | 771 | |
| 751 | 772 | auto existing_entry = g->llvm_fn_table.maybe_get(key); |
| 752 | 773 | if (existing_entry) |
| ... | ... | @@ -755,13 +776,13 @@ static LLVMValueRef get_int_overflow_fn(CodeGen *g, ZigType *type_entry, AddSubM |
| 755 | 776 | LLVMValueRef fn_val; |
| 756 | 777 | switch (add_sub_mul) { |
| 757 | 778 | case AddSubMulAdd: |
| 758 | | fn_val = get_arithmetic_overflow_fn(g, type_entry, "sadd", "uadd"); |
| 779 | fn_val = get_arithmetic_overflow_fn(g, operand_type, "sadd", "uadd"); |
| 759 | 780 | break; |
| 760 | 781 | case AddSubMulSub: |
| 761 | | fn_val = get_arithmetic_overflow_fn(g, type_entry, "ssub", "usub"); |
| 782 | fn_val = get_arithmetic_overflow_fn(g, operand_type, "ssub", "usub"); |
| 762 | 783 | break; |
| 763 | 784 | case AddSubMulMul: |
| 764 | | fn_val = get_arithmetic_overflow_fn(g, type_entry, "smul", "umul"); |
| 785 | fn_val = get_arithmetic_overflow_fn(g, operand_type, "smul", "umul"); |
| 765 | 786 | break; |
| 766 | 787 | } |
| 767 | 788 | |
| ... | ... | @@ -1752,17 +1773,49 @@ static LLVMValueRef gen_widen_or_shorten(CodeGen *g, bool want_runtime_safety, Z |
| 1752 | 1773 | } |
| 1753 | 1774 | } |
| 1754 | 1775 | |
| 1755 | | static LLVMValueRef gen_overflow_op(CodeGen *g, ZigType *type_entry, AddSubMul op, |
| 1776 | typedef LLVMValueRef (*BuildBinOpFunc)(LLVMBuilderRef, LLVMValueRef, LLVMValueRef, const char *); |
| 1777 | // These are lookup table using the AddSubMul enum as the lookup. |
| 1778 | // If AddSubMul ever changes, then these tables will be out of |
| 1779 | // date. |
| 1780 | static const BuildBinOpFunc float_op[3] = { LLVMBuildFAdd, LLVMBuildFSub, LLVMBuildFMul }; |
| 1781 | static const BuildBinOpFunc wrap_op[3] = { LLVMBuildAdd, LLVMBuildSub, LLVMBuildMul }; |
| 1782 | static const BuildBinOpFunc signed_op[3] = { LLVMBuildNSWAdd, LLVMBuildNSWSub, LLVMBuildNSWMul }; |
| 1783 | static const BuildBinOpFunc unsigned_op[3] = { LLVMBuildNUWAdd, LLVMBuildNUWSub, LLVMBuildNUWMul }; |
| 1784 | |
| 1785 | static LLVMValueRef gen_overflow_op(CodeGen *g, ZigType *operand_type, AddSubMul op, |
| 1756 | 1786 | LLVMValueRef val1, LLVMValueRef val2) |
| 1757 | 1787 | { |
| 1758 | | LLVMValueRef fn_val = get_int_overflow_fn(g, type_entry, op); |
| 1759 | | LLVMValueRef params[] = { |
| 1760 | | val1, |
| 1761 | | val2, |
| 1762 | | }; |
| 1763 | | LLVMValueRef result_struct = LLVMBuildCall(g->builder, fn_val, params, 2, ""); |
| 1764 | | LLVMValueRef result = LLVMBuildExtractValue(g->builder, result_struct, 0, ""); |
| 1765 | | LLVMValueRef overflow_bit = LLVMBuildExtractValue(g->builder, result_struct, 1, ""); |
| 1788 | LLVMValueRef overflow_bit; |
| 1789 | LLVMValueRef result; |
| 1790 | |
| 1791 | if (operand_type->id == ZigTypeIdVector) { |
| 1792 | ZigType *int_type = operand_type->data.vector.elem_type; |
| 1793 | assert(int_type->id == ZigTypeIdInt); |
| 1794 | LLVMTypeRef one_more_bit_int = LLVMIntType(int_type->data.integral.bit_count + 1); |
| 1795 | LLVMTypeRef one_more_bit_int_vector = LLVMVectorType(one_more_bit_int, operand_type->data.vector.len); |
| 1796 | const auto buildExtFn = int_type->data.integral.is_signed ? LLVMBuildSExt : LLVMBuildZExt; |
| 1797 | LLVMValueRef extended1 = buildExtFn(g->builder, val1, one_more_bit_int_vector, ""); |
| 1798 | LLVMValueRef extended2 = buildExtFn(g->builder, val2, one_more_bit_int_vector, ""); |
| 1799 | LLVMValueRef extended_result = wrap_op[op](g->builder, extended1, extended2, ""); |
| 1800 | result = LLVMBuildTrunc(g->builder, extended_result, operand_type->type_ref, ""); |
| 1801 | |
| 1802 | LLVMValueRef re_extended_result = buildExtFn(g->builder, result, one_more_bit_int_vector, ""); |
| 1803 | LLVMValueRef overflow_vector = LLVMBuildICmp(g->builder, LLVMIntNE, extended_result, re_extended_result, ""); |
| 1804 | LLVMTypeRef bitcast_int_type = LLVMIntType(operand_type->data.vector.len); |
| 1805 | LLVMValueRef bitcasted_overflow = LLVMBuildBitCast(g->builder, overflow_vector, bitcast_int_type, ""); |
| 1806 | LLVMValueRef zero = LLVMConstNull(bitcast_int_type); |
| 1807 | overflow_bit = LLVMBuildICmp(g->builder, LLVMIntNE, bitcasted_overflow, zero, ""); |
| 1808 | } else { |
| 1809 | LLVMValueRef fn_val = get_int_overflow_fn(g, operand_type, op); |
| 1810 | LLVMValueRef params[] = { |
| 1811 | val1, |
| 1812 | val2, |
| 1813 | }; |
| 1814 | LLVMValueRef result_struct = LLVMBuildCall(g->builder, fn_val, params, 2, ""); |
| 1815 | result = LLVMBuildExtractValue(g->builder, result_struct, 0, ""); |
| 1816 | overflow_bit = LLVMBuildExtractValue(g->builder, result_struct, 1, ""); |
| 1817 | } |
| 1818 | |
| 1766 | 1819 | LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "OverflowFail"); |
| 1767 | 1820 | LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "OverflowOk"); |
| 1768 | 1821 | LLVMBuildCondBr(g->builder, overflow_bit, fail_block, ok_block); |
| ... | ... | @@ -2591,8 +2644,6 @@ static LLVMValueRef gen_rem(CodeGen *g, bool want_runtime_safety, bool want_fast |
| 2591 | 2644 | |
| 2592 | 2645 | } |
| 2593 | 2646 | |
| 2594 | | typedef LLVMValueRef (*BuildBinOpFunc)(LLVMBuilderRef, LLVMValueRef, LLVMValueRef, const char *); |
| 2595 | | |
| 2596 | 2647 | static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, |
| 2597 | 2648 | IrInstructionBinOp *bin_op_instruction) |
| 2598 | 2649 | { |
| ... | ... | @@ -2608,7 +2659,8 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, |
| 2608 | 2659 | (op_id == IrBinOpAdd || op_id == IrBinOpSub) && |
| 2609 | 2660 | op1->value.type->data.pointer.ptr_len == PtrLenUnknown) |
| 2610 | 2661 | ); |
| 2611 | | ZigType *type_entry = op1->value.type; |
| 2662 | ZigType *operand_type = op1->value.type; |
| 2663 | ZigType *scalar_type = (operand_type->id == ZigTypeIdVector) ? operand_type->data.vector.elem_type : operand_type; |
| 2612 | 2664 | |
| 2613 | 2665 | bool want_runtime_safety = bin_op_instruction->safety_check_on && |
| 2614 | 2666 | ir_want_runtime_safety(g, &bin_op_instruction->base); |
| ... | ... | @@ -2634,17 +2686,17 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, |
| 2634 | 2686 | case IrBinOpCmpGreaterThan: |
| 2635 | 2687 | case IrBinOpCmpLessOrEq: |
| 2636 | 2688 | case IrBinOpCmpGreaterOrEq: |
| 2637 | | if (type_entry->id == ZigTypeIdFloat) { |
| 2689 | if (scalar_type->id == ZigTypeIdFloat) { |
| 2638 | 2690 | ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &bin_op_instruction->base)); |
| 2639 | 2691 | LLVMRealPredicate pred = cmp_op_to_real_predicate(op_id); |
| 2640 | 2692 | return LLVMBuildFCmp(g->builder, pred, op1_value, op2_value, ""); |
| 2641 | | } else if (type_entry->id == ZigTypeIdInt) { |
| 2642 | | LLVMIntPredicate pred = cmp_op_to_int_predicate(op_id, type_entry->data.integral.is_signed); |
| 2693 | } else if (scalar_type->id == ZigTypeIdInt) { |
| 2694 | LLVMIntPredicate pred = cmp_op_to_int_predicate(op_id, scalar_type->data.integral.is_signed); |
| 2643 | 2695 | return LLVMBuildICmp(g->builder, pred, op1_value, op2_value, ""); |
| 2644 | | } else if (type_entry->id == ZigTypeIdEnum || |
| 2645 | | type_entry->id == ZigTypeIdErrorSet || |
| 2646 | | type_entry->id == ZigTypeIdBool || |
| 2647 | | get_codegen_ptr_type(type_entry) != nullptr) |
| 2696 | } else if (scalar_type->id == ZigTypeIdEnum || |
| 2697 | scalar_type->id == ZigTypeIdErrorSet || |
| 2698 | scalar_type->id == ZigTypeIdBool || |
| 2699 | get_codegen_ptr_type(scalar_type) != nullptr) |
| 2648 | 2700 | { |
| 2649 | 2701 | LLVMIntPredicate pred = cmp_op_to_int_predicate(op_id, false); |
| 2650 | 2702 | return LLVMBuildICmp(g->builder, pred, op1_value, op2_value, ""); |
| ... | ... | @@ -2657,31 +2709,16 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, |
| 2657 | 2709 | case IrBinOpAddWrap: |
| 2658 | 2710 | case IrBinOpSub: |
| 2659 | 2711 | case IrBinOpSubWrap: { |
| 2660 | | // These are lookup table using the AddSubMul enum as the lookup. |
| 2661 | | // If AddSubMul ever changes, then these tables will be out of |
| 2662 | | // date. |
| 2663 | | static const BuildBinOpFunc float_op[3] = { LLVMBuildFAdd, LLVMBuildFSub, LLVMBuildFMul }; |
| 2664 | | static const BuildBinOpFunc wrap_op[3] = { LLVMBuildAdd, LLVMBuildSub, LLVMBuildMul }; |
| 2665 | | static const BuildBinOpFunc signed_op[3] = { LLVMBuildNSWAdd, LLVMBuildNSWSub, LLVMBuildNSWMul }; |
| 2666 | | static const BuildBinOpFunc unsigned_op[3] = { LLVMBuildNUWAdd, LLVMBuildNUWSub, LLVMBuildNUWMul }; |
| 2667 | | |
| 2668 | | bool is_vector = type_entry->id == ZigTypeIdVector; |
| 2669 | 2712 | bool is_wrapping = (op_id == IrBinOpSubWrap || op_id == IrBinOpAddWrap || op_id == IrBinOpMultWrap); |
| 2670 | 2713 | AddSubMul add_sub_mul = |
| 2671 | 2714 | op_id == IrBinOpAdd || op_id == IrBinOpAddWrap ? AddSubMulAdd : |
| 2672 | 2715 | op_id == IrBinOpSub || op_id == IrBinOpSubWrap ? AddSubMulSub : |
| 2673 | 2716 | AddSubMulMul; |
| 2674 | 2717 | |
| 2675 | | // The code that is generated for vectors and scalars are the same, |
| 2676 | | // so we can just set type_entry to the vectors elem_type an avoid |
| 2677 | | // a lot of repeated code. |
| 2678 | | if (is_vector) |
| 2679 | | type_entry = type_entry->data.vector.elem_type; |
| 2680 | | |
| 2681 | | if (type_entry->id == ZigTypeIdPointer) { |
| 2682 | | assert(type_entry->data.pointer.ptr_len == PtrLenUnknown); |
| 2718 | if (scalar_type->id == ZigTypeIdPointer) { |
| 2719 | assert(scalar_type->data.pointer.ptr_len == PtrLenUnknown); |
| 2683 | 2720 | LLVMValueRef subscript_value; |
| 2684 | | if (is_vector) |
| 2721 | if (operand_type->id == ZigTypeIdVector) |
| 2685 | 2722 | zig_panic("TODO: Implement vector operations on pointers."); |
| 2686 | 2723 | |
| 2687 | 2724 | switch (add_sub_mul) { |
| ... | ... | @@ -2697,17 +2734,15 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, |
| 2697 | 2734 | |
| 2698 | 2735 | // TODO runtime safety |
| 2699 | 2736 | return LLVMBuildInBoundsGEP(g->builder, op1_value, &subscript_value, 1, ""); |
| 2700 | | } else if (type_entry->id == ZigTypeIdFloat) { |
| 2737 | } else if (scalar_type->id == ZigTypeIdFloat) { |
| 2701 | 2738 | ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &bin_op_instruction->base)); |
| 2702 | 2739 | return float_op[add_sub_mul](g->builder, op1_value, op2_value, ""); |
| 2703 | | } else if (type_entry->id == ZigTypeIdInt) { |
| 2740 | } else if (scalar_type->id == ZigTypeIdInt) { |
| 2704 | 2741 | if (is_wrapping) { |
| 2705 | 2742 | return wrap_op[add_sub_mul](g->builder, op1_value, op2_value, ""); |
| 2706 | 2743 | } else if (want_runtime_safety) { |
| 2707 | | if (is_vector) |
| 2708 | | zig_panic("TODO: Implement runtime safety vector operations."); |
| 2709 | | return gen_overflow_op(g, type_entry, add_sub_mul, op1_value, op2_value); |
| 2710 | | } else if (type_entry->data.integral.is_signed) { |
| 2744 | return gen_overflow_op(g, operand_type, add_sub_mul, op1_value, op2_value); |
| 2745 | } else if (scalar_type->data.integral.is_signed) { |
| 2711 | 2746 | return signed_op[add_sub_mul](g->builder, op1_value, op2_value, ""); |
| 2712 | 2747 | } else { |
| 2713 | 2748 | return unsigned_op[add_sub_mul](g->builder, op1_value, op2_value, ""); |
| ... | ... | @@ -2725,15 +2760,14 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, |
| 2725 | 2760 | case IrBinOpBitShiftLeftLossy: |
| 2726 | 2761 | case IrBinOpBitShiftLeftExact: |
| 2727 | 2762 | { |
| 2728 | | assert(type_entry->id == ZigTypeIdInt); |
| 2729 | | LLVMValueRef op2_casted = gen_widen_or_shorten(g, false, op2->value.type, |
| 2730 | | type_entry, op2_value); |
| 2763 | assert(scalar_type->id == ZigTypeIdInt); |
| 2764 | LLVMValueRef op2_casted = gen_widen_or_shorten(g, false, op2->value.type, scalar_type, op2_value); |
| 2731 | 2765 | bool is_sloppy = (op_id == IrBinOpBitShiftLeftLossy); |
| 2732 | 2766 | if (is_sloppy) { |
| 2733 | 2767 | return LLVMBuildShl(g->builder, op1_value, op2_casted, ""); |
| 2734 | 2768 | } else if (want_runtime_safety) { |
| 2735 | | return gen_overflow_shl_op(g, type_entry, op1_value, op2_casted); |
| 2736 | | } else if (type_entry->data.integral.is_signed) { |
| 2769 | return gen_overflow_shl_op(g, scalar_type, op1_value, op2_casted); |
| 2770 | } else if (scalar_type->data.integral.is_signed) { |
| 2737 | 2771 | return ZigLLVMBuildNSWShl(g->builder, op1_value, op2_casted, ""); |
| 2738 | 2772 | } else { |
| 2739 | 2773 | return ZigLLVMBuildNUWShl(g->builder, op1_value, op2_casted, ""); |
| ... | ... | @@ -2742,19 +2776,18 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, |
| 2742 | 2776 | case IrBinOpBitShiftRightLossy: |
| 2743 | 2777 | case IrBinOpBitShiftRightExact: |
| 2744 | 2778 | { |
| 2745 | | assert(type_entry->id == ZigTypeIdInt); |
| 2746 | | LLVMValueRef op2_casted = gen_widen_or_shorten(g, false, op2->value.type, |
| 2747 | | type_entry, op2_value); |
| 2779 | assert(scalar_type->id == ZigTypeIdInt); |
| 2780 | LLVMValueRef op2_casted = gen_widen_or_shorten(g, false, op2->value.type, scalar_type, op2_value); |
| 2748 | 2781 | bool is_sloppy = (op_id == IrBinOpBitShiftRightLossy); |
| 2749 | 2782 | if (is_sloppy) { |
| 2750 | | if (type_entry->data.integral.is_signed) { |
| 2783 | if (scalar_type->data.integral.is_signed) { |
| 2751 | 2784 | return LLVMBuildAShr(g->builder, op1_value, op2_casted, ""); |
| 2752 | 2785 | } else { |
| 2753 | 2786 | return LLVMBuildLShr(g->builder, op1_value, op2_casted, ""); |
| 2754 | 2787 | } |
| 2755 | 2788 | } else if (want_runtime_safety) { |
| 2756 | | return gen_overflow_shr_op(g, type_entry, op1_value, op2_casted); |
| 2757 | | } else if (type_entry->data.integral.is_signed) { |
| 2789 | return gen_overflow_shr_op(g, scalar_type, op1_value, op2_casted); |
| 2790 | } else if (scalar_type->data.integral.is_signed) { |
| 2758 | 2791 | return ZigLLVMBuildAShrExact(g->builder, op1_value, op2_casted, ""); |
| 2759 | 2792 | } else { |
| 2760 | 2793 | return ZigLLVMBuildLShrExact(g->builder, op1_value, op2_casted, ""); |
| ... | ... | @@ -2762,22 +2795,22 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, |
| 2762 | 2795 | } |
| 2763 | 2796 | case IrBinOpDivUnspecified: |
| 2764 | 2797 | return gen_div(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base), |
| 2765 | | op1_value, op2_value, type_entry, DivKindFloat); |
| 2798 | op1_value, op2_value, scalar_type, DivKindFloat); |
| 2766 | 2799 | case IrBinOpDivExact: |
| 2767 | 2800 | return gen_div(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base), |
| 2768 | | op1_value, op2_value, type_entry, DivKindExact); |
| 2801 | op1_value, op2_value, scalar_type, DivKindExact); |
| 2769 | 2802 | case IrBinOpDivTrunc: |
| 2770 | 2803 | return gen_div(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base), |
| 2771 | | op1_value, op2_value, type_entry, DivKindTrunc); |
| 2804 | op1_value, op2_value, scalar_type, DivKindTrunc); |
| 2772 | 2805 | case IrBinOpDivFloor: |
| 2773 | 2806 | return gen_div(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base), |
| 2774 | | op1_value, op2_value, type_entry, DivKindFloor); |
| 2807 | op1_value, op2_value, scalar_type, DivKindFloor); |
| 2775 | 2808 | case IrBinOpRemRem: |
| 2776 | 2809 | return gen_rem(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base), |
| 2777 | | op1_value, op2_value, type_entry, RemKindRem); |
| 2810 | op1_value, op2_value, scalar_type, RemKindRem); |
| 2778 | 2811 | case IrBinOpRemMod: |
| 2779 | 2812 | return gen_rem(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base), |
| 2780 | | op1_value, op2_value, type_entry, RemKindMod); |
| 2813 | op1_value, op2_value, scalar_type, RemKindMod); |
| 2781 | 2814 | } |
| 2782 | 2815 | zig_unreachable(); |
| 2783 | 2816 | } |