| ... | @@ -715,38 +715,59 @@ static void clear_debug_source_node(CodeGen *g) { | ... | @@ -715,38 +715,59 @@ static void clear_debug_source_node(CodeGen *g) { |
| 715 | ZigLLVMClearCurrentDebugLocation(g->builder); | 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 | const char *signed_name, const char *unsigned_name) | 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 | char fn_name[64]; | 722 | char fn_name[64]; |
| 722 | | 723 | |
| 723 | assert(type_entry->id == ZigTypeIdInt); | 724 | assert(int_type->id == ZigTypeIdInt); |
| 724 | const char *signed_str = type_entry->data.integral.is_signed ? signed_name : unsigned_name; | 725 | const char *signed_str = int_type->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); | | |
| 726 | | 726 | |
| 727 | LLVMTypeRef return_elem_types[] = { | | |
| 728 | type_entry->type_ref, | | |
| 729 | LLVMInt1Type(), | | |
| 730 | }; | | |
| 731 | LLVMTypeRef param_types[] = { | 727 | LLVMTypeRef param_types[] = { |
| 732 | type_entry->type_ref, | 728 | operand_type->type_ref, |
| 733 | type_entry->type_ref, | 729 | operand_type->type_ref, |
| 734 | }; | 730 | }; |
| 735 | LLVMTypeRef return_struct_type = LLVMStructType(return_elem_types, 2, false); | 731 | |
| 736 | LLVMTypeRef fn_type = LLVMFunctionType(return_struct_type, param_types, 2, false); | 732 | if (operand_type->id == ZigTypeIdVector) { |
| 737 | LLVMValueRef fn_val = LLVMAddFunction(g->module, fn_name, fn_type); | 733 | sprintf(fn_name, "llvm.%s.with.overflow.v%" PRIu32 "i%" PRIu32, signed_str, |
| 738 | assert(LLVMGetIntrinsicID(fn_val)); | 734 | operand_type->data.vector.len, int_type->data.integral.bit_count); |
| 739 | return fn_val; | 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) { | 760 | static LLVMValueRef get_int_overflow_fn(CodeGen *g, ZigType *operand_type, AddSubMul add_sub_mul) { |
| 743 | assert(type_entry->id == ZigTypeIdInt); | 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 | ZigLLVMFnKey key = {}; | 764 | ZigLLVMFnKey key = {}; |
| 746 | key.id = ZigLLVMFnIdOverflowArithmetic; | 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 | key.data.overflow_arithmetic.add_sub_mul = add_sub_mul; | 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 | auto existing_entry = g->llvm_fn_table.maybe_get(key); | 772 | auto existing_entry = g->llvm_fn_table.maybe_get(key); |
| 752 | if (existing_entry) | 773 | if (existing_entry) |
| ... | @@ -755,13 +776,13 @@ static LLVMValueRef get_int_overflow_fn(CodeGen *g, ZigType *type_entry, AddSubM | ... | @@ -755,13 +776,13 @@ static LLVMValueRef get_int_overflow_fn(CodeGen *g, ZigType *type_entry, AddSubM |
| 755 | LLVMValueRef fn_val; | 776 | LLVMValueRef fn_val; |
| 756 | switch (add_sub_mul) { | 777 | switch (add_sub_mul) { |
| 757 | case AddSubMulAdd: | 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 | break; | 780 | break; |
| 760 | case AddSubMulSub: | 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 | break; | 783 | break; |
| 763 | case AddSubMulMul: | 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 | break; | 786 | break; |
| 766 | } | 787 | } |
| 767 | | 788 | |
| ... | @@ -1752,17 +1773,49 @@ static LLVMValueRef gen_widen_or_shorten(CodeGen *g, bool want_runtime_safety, Z | ... | @@ -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 | LLVMValueRef val1, LLVMValueRef val2) | 1786 | LLVMValueRef val1, LLVMValueRef val2) |
| 1757 | { | 1787 | { |
| 1758 | LLVMValueRef fn_val = get_int_overflow_fn(g, type_entry, op); | 1788 | LLVMValueRef overflow_bit; |
| 1759 | LLVMValueRef params[] = { | 1789 | LLVMValueRef result; |
| 1760 | val1, | 1790 | |
| 1761 | val2, | 1791 | if (operand_type->id == ZigTypeIdVector) { |
| 1762 | }; | 1792 | ZigType *int_type = operand_type->data.vector.elem_type; |
| 1763 | LLVMValueRef result_struct = LLVMBuildCall(g->builder, fn_val, params, 2, ""); | 1793 | assert(int_type->id == ZigTypeIdInt); |
| 1764 | LLVMValueRef result = LLVMBuildExtractValue(g->builder, result_struct, 0, ""); | 1794 | LLVMTypeRef one_more_bit_int = LLVMIntType(int_type->data.integral.bit_count + 1); |
| 1765 | LLVMValueRef overflow_bit = LLVMBuildExtractValue(g->builder, result_struct, 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 | LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "OverflowFail"); | 1819 | LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "OverflowFail"); |
| 1767 | LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "OverflowOk"); | 1820 | LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "OverflowOk"); |
| 1768 | LLVMBuildCondBr(g->builder, overflow_bit, fail_block, ok_block); | 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,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 | static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, | 2647 | static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, |
| 2597 | IrInstructionBinOp *bin_op_instruction) | 2648 | IrInstructionBinOp *bin_op_instruction) |
| 2598 | { | 2649 | { |
| ... | @@ -2608,7 +2659,8 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, | ... | @@ -2608,7 +2659,8 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, |
| 2608 | (op_id == IrBinOpAdd || op_id == IrBinOpSub) && | 2659 | (op_id == IrBinOpAdd || op_id == IrBinOpSub) && |
| 2609 | op1->value.type->data.pointer.ptr_len == PtrLenUnknown) | 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 | bool want_runtime_safety = bin_op_instruction->safety_check_on && | 2665 | bool want_runtime_safety = bin_op_instruction->safety_check_on && |
| 2614 | ir_want_runtime_safety(g, &bin_op_instruction->base); | 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,17 +2686,17 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, |
| 2634 | case IrBinOpCmpGreaterThan: | 2686 | case IrBinOpCmpGreaterThan: |
| 2635 | case IrBinOpCmpLessOrEq: | 2687 | case IrBinOpCmpLessOrEq: |
| 2636 | case IrBinOpCmpGreaterOrEq: | 2688 | case IrBinOpCmpGreaterOrEq: |
| 2637 | if (type_entry->id == ZigTypeIdFloat) { | 2689 | if (scalar_type->id == ZigTypeIdFloat) { |
| 2638 | ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &bin_op_instruction->base)); | 2690 | ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &bin_op_instruction->base)); |
| 2639 | LLVMRealPredicate pred = cmp_op_to_real_predicate(op_id); | 2691 | LLVMRealPredicate pred = cmp_op_to_real_predicate(op_id); |
| 2640 | return LLVMBuildFCmp(g->builder, pred, op1_value, op2_value, ""); | 2692 | return LLVMBuildFCmp(g->builder, pred, op1_value, op2_value, ""); |
| 2641 | } else if (type_entry->id == ZigTypeIdInt) { | 2693 | } else if (scalar_type->id == ZigTypeIdInt) { |
| 2642 | LLVMIntPredicate pred = cmp_op_to_int_predicate(op_id, type_entry->data.integral.is_signed); | 2694 | LLVMIntPredicate pred = cmp_op_to_int_predicate(op_id, scalar_type->data.integral.is_signed); |
| 2643 | return LLVMBuildICmp(g->builder, pred, op1_value, op2_value, ""); | 2695 | return LLVMBuildICmp(g->builder, pred, op1_value, op2_value, ""); |
| 2644 | } else if (type_entry->id == ZigTypeIdEnum || | 2696 | } else if (scalar_type->id == ZigTypeIdEnum || |
| 2645 | type_entry->id == ZigTypeIdErrorSet || | 2697 | scalar_type->id == ZigTypeIdErrorSet || |
| 2646 | type_entry->id == ZigTypeIdBool || | 2698 | scalar_type->id == ZigTypeIdBool || |
| 2647 | get_codegen_ptr_type(type_entry) != nullptr) | 2699 | get_codegen_ptr_type(scalar_type) != nullptr) |
| 2648 | { | 2700 | { |
| 2649 | LLVMIntPredicate pred = cmp_op_to_int_predicate(op_id, false); | 2701 | LLVMIntPredicate pred = cmp_op_to_int_predicate(op_id, false); |
| 2650 | return LLVMBuildICmp(g->builder, pred, op1_value, op2_value, ""); | 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,31 +2709,16 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, |
| 2657 | case IrBinOpAddWrap: | 2709 | case IrBinOpAddWrap: |
| 2658 | case IrBinOpSub: | 2710 | case IrBinOpSub: |
| 2659 | case IrBinOpSubWrap: { | 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 | bool is_wrapping = (op_id == IrBinOpSubWrap || op_id == IrBinOpAddWrap || op_id == IrBinOpMultWrap); | 2712 | bool is_wrapping = (op_id == IrBinOpSubWrap || op_id == IrBinOpAddWrap || op_id == IrBinOpMultWrap); |
| 2670 | AddSubMul add_sub_mul = | 2713 | AddSubMul add_sub_mul = |
| 2671 | op_id == IrBinOpAdd || op_id == IrBinOpAddWrap ? AddSubMulAdd : | 2714 | op_id == IrBinOpAdd || op_id == IrBinOpAddWrap ? AddSubMulAdd : |
| 2672 | op_id == IrBinOpSub || op_id == IrBinOpSubWrap ? AddSubMulSub : | 2715 | op_id == IrBinOpSub || op_id == IrBinOpSubWrap ? AddSubMulSub : |
| 2673 | AddSubMulMul; | 2716 | AddSubMulMul; |
| 2674 | | 2717 | |
| 2675 | // The code that is generated for vectors and scalars are the same, | 2718 | if (scalar_type->id == ZigTypeIdPointer) { |
| 2676 | // so we can just set type_entry to the vectors elem_type an avoid | 2719 | assert(scalar_type->data.pointer.ptr_len == PtrLenUnknown); |
| 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); | | |
| 2683 | LLVMValueRef subscript_value; | 2720 | LLVMValueRef subscript_value; |
| 2684 | if (is_vector) | 2721 | if (operand_type->id == ZigTypeIdVector) |
| 2685 | zig_panic("TODO: Implement vector operations on pointers."); | 2722 | zig_panic("TODO: Implement vector operations on pointers."); |
| 2686 | | 2723 | |
| 2687 | switch (add_sub_mul) { | 2724 | switch (add_sub_mul) { |
| ... | @@ -2697,17 +2734,15 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, | ... | @@ -2697,17 +2734,15 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, |
| 2697 | | 2734 | |
| 2698 | // TODO runtime safety | 2735 | // TODO runtime safety |
| 2699 | return LLVMBuildInBoundsGEP(g->builder, op1_value, &subscript_value, 1, ""); | 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 | ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &bin_op_instruction->base)); | 2738 | ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &bin_op_instruction->base)); |
| 2702 | return float_op[add_sub_mul](g->builder, op1_value, op2_value, ""); | 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 | if (is_wrapping) { | 2741 | if (is_wrapping) { |
| 2705 | return wrap_op[add_sub_mul](g->builder, op1_value, op2_value, ""); | 2742 | return wrap_op[add_sub_mul](g->builder, op1_value, op2_value, ""); |
| 2706 | } else if (want_runtime_safety) { | 2743 | } else if (want_runtime_safety) { |
| 2707 | if (is_vector) | 2744 | return gen_overflow_op(g, operand_type, add_sub_mul, op1_value, op2_value); |
| 2708 | zig_panic("TODO: Implement runtime safety vector operations."); | 2745 | } else if (scalar_type->data.integral.is_signed) { |
| 2709 | return gen_overflow_op(g, type_entry, add_sub_mul, op1_value, op2_value); | | |
| 2710 | } else if (type_entry->data.integral.is_signed) { | | |
| 2711 | return signed_op[add_sub_mul](g->builder, op1_value, op2_value, ""); | 2746 | return signed_op[add_sub_mul](g->builder, op1_value, op2_value, ""); |
| 2712 | } else { | 2747 | } else { |
| 2713 | return unsigned_op[add_sub_mul](g->builder, op1_value, op2_value, ""); | 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,15 +2760,14 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, |
| 2725 | case IrBinOpBitShiftLeftLossy: | 2760 | case IrBinOpBitShiftLeftLossy: |
| 2726 | case IrBinOpBitShiftLeftExact: | 2761 | case IrBinOpBitShiftLeftExact: |
| 2727 | { | 2762 | { |
| 2728 | assert(type_entry->id == ZigTypeIdInt); | 2763 | assert(scalar_type->id == ZigTypeIdInt); |
| 2729 | LLVMValueRef op2_casted = gen_widen_or_shorten(g, false, op2->value.type, | 2764 | LLVMValueRef op2_casted = gen_widen_or_shorten(g, false, op2->value.type, scalar_type, op2_value); |
| 2730 | type_entry, op2_value); | | |
| 2731 | bool is_sloppy = (op_id == IrBinOpBitShiftLeftLossy); | 2765 | bool is_sloppy = (op_id == IrBinOpBitShiftLeftLossy); |
| 2732 | if (is_sloppy) { | 2766 | if (is_sloppy) { |
| 2733 | return LLVMBuildShl(g->builder, op1_value, op2_casted, ""); | 2767 | return LLVMBuildShl(g->builder, op1_value, op2_casted, ""); |
| 2734 | } else if (want_runtime_safety) { | 2768 | } else if (want_runtime_safety) { |
| 2735 | return gen_overflow_shl_op(g, type_entry, op1_value, op2_casted); | 2769 | return gen_overflow_shl_op(g, scalar_type, op1_value, op2_casted); |
| 2736 | } else if (type_entry->data.integral.is_signed) { | 2770 | } else if (scalar_type->data.integral.is_signed) { |
| 2737 | return ZigLLVMBuildNSWShl(g->builder, op1_value, op2_casted, ""); | 2771 | return ZigLLVMBuildNSWShl(g->builder, op1_value, op2_casted, ""); |
| 2738 | } else { | 2772 | } else { |
| 2739 | return ZigLLVMBuildNUWShl(g->builder, op1_value, op2_casted, ""); | 2773 | return ZigLLVMBuildNUWShl(g->builder, op1_value, op2_casted, ""); |
| ... | @@ -2742,19 +2776,18 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, | ... | @@ -2742,19 +2776,18 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, |
| 2742 | case IrBinOpBitShiftRightLossy: | 2776 | case IrBinOpBitShiftRightLossy: |
| 2743 | case IrBinOpBitShiftRightExact: | 2777 | case IrBinOpBitShiftRightExact: |
| 2744 | { | 2778 | { |
| 2745 | assert(type_entry->id == ZigTypeIdInt); | 2779 | assert(scalar_type->id == ZigTypeIdInt); |
| 2746 | LLVMValueRef op2_casted = gen_widen_or_shorten(g, false, op2->value.type, | 2780 | LLVMValueRef op2_casted = gen_widen_or_shorten(g, false, op2->value.type, scalar_type, op2_value); |
| 2747 | type_entry, op2_value); | | |
| 2748 | bool is_sloppy = (op_id == IrBinOpBitShiftRightLossy); | 2781 | bool is_sloppy = (op_id == IrBinOpBitShiftRightLossy); |
| 2749 | if (is_sloppy) { | 2782 | if (is_sloppy) { |
| 2750 | if (type_entry->data.integral.is_signed) { | 2783 | if (scalar_type->data.integral.is_signed) { |
| 2751 | return LLVMBuildAShr(g->builder, op1_value, op2_casted, ""); | 2784 | return LLVMBuildAShr(g->builder, op1_value, op2_casted, ""); |
| 2752 | } else { | 2785 | } else { |
| 2753 | return LLVMBuildLShr(g->builder, op1_value, op2_casted, ""); | 2786 | return LLVMBuildLShr(g->builder, op1_value, op2_casted, ""); |
| 2754 | } | 2787 | } |
| 2755 | } else if (want_runtime_safety) { | 2788 | } else if (want_runtime_safety) { |
| 2756 | return gen_overflow_shr_op(g, type_entry, op1_value, op2_casted); | 2789 | return gen_overflow_shr_op(g, scalar_type, op1_value, op2_casted); |
| 2757 | } else if (type_entry->data.integral.is_signed) { | 2790 | } else if (scalar_type->data.integral.is_signed) { |
| 2758 | return ZigLLVMBuildAShrExact(g->builder, op1_value, op2_casted, ""); | 2791 | return ZigLLVMBuildAShrExact(g->builder, op1_value, op2_casted, ""); |
| 2759 | } else { | 2792 | } else { |
| 2760 | return ZigLLVMBuildLShrExact(g->builder, op1_value, op2_casted, ""); | 2793 | return ZigLLVMBuildLShrExact(g->builder, op1_value, op2_casted, ""); |
| ... | @@ -2762,22 +2795,22 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, | ... | @@ -2762,22 +2795,22 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, |
| 2762 | } | 2795 | } |
| 2763 | case IrBinOpDivUnspecified: | 2796 | case IrBinOpDivUnspecified: |
| 2764 | return gen_div(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base), | 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 | case IrBinOpDivExact: | 2799 | case IrBinOpDivExact: |
| 2767 | return gen_div(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base), | 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 | case IrBinOpDivTrunc: | 2802 | case IrBinOpDivTrunc: |
| 2770 | return gen_div(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base), | 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 | case IrBinOpDivFloor: | 2805 | case IrBinOpDivFloor: |
| 2773 | return gen_div(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base), | 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 | case IrBinOpRemRem: | 2808 | case IrBinOpRemRem: |
| 2776 | return gen_rem(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base), | 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 | case IrBinOpRemMod: | 2811 | case IrBinOpRemMod: |
| 2779 | return gen_rem(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base), | 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 | zig_unreachable(); | 2815 | zig_unreachable(); |
| 2783 | } | 2816 | } |