| ... | ... | @@ -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,28 @@ 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 | static LLVMValueRef gen_overflow_op(CodeGen *g, ZigType *operand_type, AddSubMul op, |
| 1756 | 1777 | LLVMValueRef val1, LLVMValueRef val2) |
| 1757 | 1778 | { |
| 1758 | | LLVMValueRef fn_val = get_int_overflow_fn(g, type_entry, op); |
| 1779 | LLVMValueRef fn_val = get_int_overflow_fn(g, operand_type, op); |
| 1759 | 1780 | LLVMValueRef params[] = { |
| 1760 | 1781 | val1, |
| 1761 | 1782 | val2, |
| 1762 | 1783 | }; |
| 1763 | 1784 | LLVMValueRef result_struct = LLVMBuildCall(g->builder, fn_val, params, 2, ""); |
| 1764 | 1785 | LLVMValueRef result = LLVMBuildExtractValue(g->builder, result_struct, 0, ""); |
| 1765 | | LLVMValueRef overflow_bit = LLVMBuildExtractValue(g->builder, result_struct, 1, ""); |
| 1786 | |
| 1787 | LLVMValueRef overflow_bit; |
| 1788 | if (operand_type->id == ZigTypeIdVector) { |
| 1789 | LLVMValueRef overflow_vector = LLVMBuildExtractValue(g->builder, result_struct, 1, ""); |
| 1790 | LLVMTypeRef bigger_int_type_ref = LLVMIntType(operand_type->data.vector.len); |
| 1791 | LLVMValueRef bitcasted_overflow = LLVMBuildBitCast(g->builder, overflow_vector, bigger_int_type_ref, ""); |
| 1792 | LLVMValueRef zero = LLVMConstNull(bigger_int_type_ref); |
| 1793 | overflow_bit = LLVMBuildICmp(g->builder, LLVMIntNE, bitcasted_overflow, zero, ""); |
| 1794 | } else { |
| 1795 | overflow_bit = LLVMBuildExtractValue(g->builder, result_struct, 1, ""); |
| 1796 | } |
| 1797 | |
| 1766 | 1798 | LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "OverflowFail"); |
| 1767 | 1799 | LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "OverflowOk"); |
| 1768 | 1800 | LLVMBuildCondBr(g->builder, overflow_bit, fail_block, ok_block); |
| ... | ... | @@ -2608,7 +2640,8 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, |
| 2608 | 2640 | (op_id == IrBinOpAdd || op_id == IrBinOpSub) && |
| 2609 | 2641 | op1->value.type->data.pointer.ptr_len == PtrLenUnknown) |
| 2610 | 2642 | ); |
| 2611 | | ZigType *type_entry = op1->value.type; |
| 2643 | ZigType *operand_type = op1->value.type; |
| 2644 | ZigType *scalar_type = (operand_type->id == ZigTypeIdVector) ? operand_type->data.vector.elem_type : operand_type; |
| 2612 | 2645 | |
| 2613 | 2646 | bool want_runtime_safety = bin_op_instruction->safety_check_on && |
| 2614 | 2647 | ir_want_runtime_safety(g, &bin_op_instruction->base); |
| ... | ... | @@ -2634,17 +2667,17 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, |
| 2634 | 2667 | case IrBinOpCmpGreaterThan: |
| 2635 | 2668 | case IrBinOpCmpLessOrEq: |
| 2636 | 2669 | case IrBinOpCmpGreaterOrEq: |
| 2637 | | if (type_entry->id == ZigTypeIdFloat) { |
| 2670 | if (scalar_type->id == ZigTypeIdFloat) { |
| 2638 | 2671 | ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &bin_op_instruction->base)); |
| 2639 | 2672 | LLVMRealPredicate pred = cmp_op_to_real_predicate(op_id); |
| 2640 | 2673 | 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); |
| 2674 | } else if (scalar_type->id == ZigTypeIdInt) { |
| 2675 | LLVMIntPredicate pred = cmp_op_to_int_predicate(op_id, scalar_type->data.integral.is_signed); |
| 2643 | 2676 | 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) |
| 2677 | } else if (scalar_type->id == ZigTypeIdEnum || |
| 2678 | scalar_type->id == ZigTypeIdErrorSet || |
| 2679 | scalar_type->id == ZigTypeIdBool || |
| 2680 | get_codegen_ptr_type(scalar_type) != nullptr) |
| 2648 | 2681 | { |
| 2649 | 2682 | LLVMIntPredicate pred = cmp_op_to_int_predicate(op_id, false); |
| 2650 | 2683 | return LLVMBuildICmp(g->builder, pred, op1_value, op2_value, ""); |
| ... | ... | @@ -2665,23 +2698,16 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, |
| 2665 | 2698 | static const BuildBinOpFunc signed_op[3] = { LLVMBuildNSWAdd, LLVMBuildNSWSub, LLVMBuildNSWMul }; |
| 2666 | 2699 | static const BuildBinOpFunc unsigned_op[3] = { LLVMBuildNUWAdd, LLVMBuildNUWSub, LLVMBuildNUWMul }; |
| 2667 | 2700 | |
| 2668 | | bool is_vector = type_entry->id == ZigTypeIdVector; |
| 2669 | 2701 | bool is_wrapping = (op_id == IrBinOpSubWrap || op_id == IrBinOpAddWrap || op_id == IrBinOpMultWrap); |
| 2670 | 2702 | AddSubMul add_sub_mul = |
| 2671 | 2703 | op_id == IrBinOpAdd || op_id == IrBinOpAddWrap ? AddSubMulAdd : |
| 2672 | 2704 | op_id == IrBinOpSub || op_id == IrBinOpSubWrap ? AddSubMulSub : |
| 2673 | 2705 | AddSubMulMul; |
| 2674 | 2706 | |
| 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); |
| 2707 | if (scalar_type->id == ZigTypeIdPointer) { |
| 2708 | assert(scalar_type->data.pointer.ptr_len == PtrLenUnknown); |
| 2683 | 2709 | LLVMValueRef subscript_value; |
| 2684 | | if (is_vector) |
| 2710 | if (operand_type->id == ZigTypeIdVector) |
| 2685 | 2711 | zig_panic("TODO: Implement vector operations on pointers."); |
| 2686 | 2712 | |
| 2687 | 2713 | switch (add_sub_mul) { |
| ... | ... | @@ -2697,17 +2723,15 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, |
| 2697 | 2723 | |
| 2698 | 2724 | // TODO runtime safety |
| 2699 | 2725 | return LLVMBuildInBoundsGEP(g->builder, op1_value, &subscript_value, 1, ""); |
| 2700 | | } else if (type_entry->id == ZigTypeIdFloat) { |
| 2726 | } else if (scalar_type->id == ZigTypeIdFloat) { |
| 2701 | 2727 | ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &bin_op_instruction->base)); |
| 2702 | 2728 | return float_op[add_sub_mul](g->builder, op1_value, op2_value, ""); |
| 2703 | | } else if (type_entry->id == ZigTypeIdInt) { |
| 2729 | } else if (scalar_type->id == ZigTypeIdInt) { |
| 2704 | 2730 | if (is_wrapping) { |
| 2705 | 2731 | return wrap_op[add_sub_mul](g->builder, op1_value, op2_value, ""); |
| 2706 | 2732 | } 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) { |
| 2733 | return gen_overflow_op(g, operand_type, add_sub_mul, op1_value, op2_value); |
| 2734 | } else if (scalar_type->data.integral.is_signed) { |
| 2711 | 2735 | return signed_op[add_sub_mul](g->builder, op1_value, op2_value, ""); |
| 2712 | 2736 | } else { |
| 2713 | 2737 | return unsigned_op[add_sub_mul](g->builder, op1_value, op2_value, ""); |
| ... | ... | @@ -2725,15 +2749,14 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, |
| 2725 | 2749 | case IrBinOpBitShiftLeftLossy: |
| 2726 | 2750 | case IrBinOpBitShiftLeftExact: |
| 2727 | 2751 | { |
| 2728 | | assert(type_entry->id == ZigTypeIdInt); |
| 2729 | | LLVMValueRef op2_casted = gen_widen_or_shorten(g, false, op2->value.type, |
| 2730 | | type_entry, op2_value); |
| 2752 | assert(scalar_type->id == ZigTypeIdInt); |
| 2753 | LLVMValueRef op2_casted = gen_widen_or_shorten(g, false, op2->value.type, scalar_type, op2_value); |
| 2731 | 2754 | bool is_sloppy = (op_id == IrBinOpBitShiftLeftLossy); |
| 2732 | 2755 | if (is_sloppy) { |
| 2733 | 2756 | return LLVMBuildShl(g->builder, op1_value, op2_casted, ""); |
| 2734 | 2757 | } 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) { |
| 2758 | return gen_overflow_shl_op(g, scalar_type, op1_value, op2_casted); |
| 2759 | } else if (scalar_type->data.integral.is_signed) { |
| 2737 | 2760 | return ZigLLVMBuildNSWShl(g->builder, op1_value, op2_casted, ""); |
| 2738 | 2761 | } else { |
| 2739 | 2762 | return ZigLLVMBuildNUWShl(g->builder, op1_value, op2_casted, ""); |
| ... | ... | @@ -2742,19 +2765,18 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, |
| 2742 | 2765 | case IrBinOpBitShiftRightLossy: |
| 2743 | 2766 | case IrBinOpBitShiftRightExact: |
| 2744 | 2767 | { |
| 2745 | | assert(type_entry->id == ZigTypeIdInt); |
| 2746 | | LLVMValueRef op2_casted = gen_widen_or_shorten(g, false, op2->value.type, |
| 2747 | | type_entry, op2_value); |
| 2768 | assert(scalar_type->id == ZigTypeIdInt); |
| 2769 | LLVMValueRef op2_casted = gen_widen_or_shorten(g, false, op2->value.type, scalar_type, op2_value); |
| 2748 | 2770 | bool is_sloppy = (op_id == IrBinOpBitShiftRightLossy); |
| 2749 | 2771 | if (is_sloppy) { |
| 2750 | | if (type_entry->data.integral.is_signed) { |
| 2772 | if (scalar_type->data.integral.is_signed) { |
| 2751 | 2773 | return LLVMBuildAShr(g->builder, op1_value, op2_casted, ""); |
| 2752 | 2774 | } else { |
| 2753 | 2775 | return LLVMBuildLShr(g->builder, op1_value, op2_casted, ""); |
| 2754 | 2776 | } |
| 2755 | 2777 | } 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) { |
| 2778 | return gen_overflow_shr_op(g, scalar_type, op1_value, op2_casted); |
| 2779 | } else if (scalar_type->data.integral.is_signed) { |
| 2758 | 2780 | return ZigLLVMBuildAShrExact(g->builder, op1_value, op2_casted, ""); |
| 2759 | 2781 | } else { |
| 2760 | 2782 | return ZigLLVMBuildLShrExact(g->builder, op1_value, op2_casted, ""); |
| ... | ... | @@ -2762,22 +2784,22 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, |
| 2762 | 2784 | } |
| 2763 | 2785 | case IrBinOpDivUnspecified: |
| 2764 | 2786 | return gen_div(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base), |
| 2765 | | op1_value, op2_value, type_entry, DivKindFloat); |
| 2787 | op1_value, op2_value, scalar_type, DivKindFloat); |
| 2766 | 2788 | case IrBinOpDivExact: |
| 2767 | 2789 | return gen_div(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base), |
| 2768 | | op1_value, op2_value, type_entry, DivKindExact); |
| 2790 | op1_value, op2_value, scalar_type, DivKindExact); |
| 2769 | 2791 | case IrBinOpDivTrunc: |
| 2770 | 2792 | return gen_div(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base), |
| 2771 | | op1_value, op2_value, type_entry, DivKindTrunc); |
| 2793 | op1_value, op2_value, scalar_type, DivKindTrunc); |
| 2772 | 2794 | case IrBinOpDivFloor: |
| 2773 | 2795 | return gen_div(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base), |
| 2774 | | op1_value, op2_value, type_entry, DivKindFloor); |
| 2796 | op1_value, op2_value, scalar_type, DivKindFloor); |
| 2775 | 2797 | case IrBinOpRemRem: |
| 2776 | 2798 | return gen_rem(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base), |
| 2777 | | op1_value, op2_value, type_entry, RemKindRem); |
| 2799 | op1_value, op2_value, scalar_type, RemKindRem); |
| 2778 | 2800 | case IrBinOpRemMod: |
| 2779 | 2801 | return gen_rem(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base), |
| 2780 | | op1_value, op2_value, type_entry, RemKindMod); |
| 2802 | op1_value, op2_value, scalar_type, RemKindMod); |
| 2781 | 2803 | } |
| 2782 | 2804 | zig_unreachable(); |
| 2783 | 2805 | } |