authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-02-09 16:24:41-05:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-02-09 16:24:41-05:00
logca8580ece1ab07215b72394cc4ab3030bf9df139
treeddc9d0174cc7bb6f3681975e6940c882c1b0a283
parenta8a63feba7e40a998b1e1d8d36169e5f1be0d4e1
parent373e21bb564a27c4292812bdfd1673711c2e0fe4
signature Commit is signed but in an unrecognized format.

Merge branch 'vector-int-add-safety'


5 files changed, 153 insertions(+), 84 deletions(-)

src/all_types.hpp+3
......@@ -1538,6 +1538,8 @@ enum ZigLLVMFnId {
15381538 ZigLLVMFnIdBitReverse,
15391539};
15401540
1541// There are a bunch of places in code that rely on these values being in
1542// exactly this order.
15411543enum AddSubMul {
15421544 AddSubMulAdd = 0,
15431545 AddSubMulSub = 1,
......@@ -1563,6 +1565,7 @@ struct ZigLLVMFnKey {
15631565 struct {
15641566 AddSubMul add_sub_mul;
15651567 uint32_t bit_count;
1568 uint32_t vector_len; // 0 means not a vector
15661569 bool is_signed;
15671570 } overflow_arithmetic;
15681571 struct {
src/analyze.cpp+4-2
......@@ -6361,7 +6361,8 @@ uint32_t zig_llvm_fn_key_hash(ZigLLVMFnKey x) {
63616361 case ZigLLVMFnIdOverflowArithmetic:
63626362 return ((uint32_t)(x.data.overflow_arithmetic.bit_count) * 87135777) +
63636363 ((uint32_t)(x.data.overflow_arithmetic.add_sub_mul) * 31640542) +
6364 ((uint32_t)(x.data.overflow_arithmetic.is_signed) ? 1062315172 : 314955820);
6364 ((uint32_t)(x.data.overflow_arithmetic.is_signed) ? 1062315172 : 314955820) +
6365 x.data.overflow_arithmetic.vector_len * 1435156945;
63656366 }
63666367 zig_unreachable();
63676368}
......@@ -6387,7 +6388,8 @@ bool zig_llvm_fn_key_eql(ZigLLVMFnKey a, ZigLLVMFnKey b) {
63876388 case ZigLLVMFnIdOverflowArithmetic:
63886389 return (a.data.overflow_arithmetic.bit_count == b.data.overflow_arithmetic.bit_count) &&
63896390 (a.data.overflow_arithmetic.add_sub_mul == b.data.overflow_arithmetic.add_sub_mul) &&
6390 (a.data.overflow_arithmetic.is_signed == b.data.overflow_arithmetic.is_signed);
6391 (a.data.overflow_arithmetic.is_signed == b.data.overflow_arithmetic.is_signed) &&
6392 (a.data.overflow_arithmetic.vector_len == b.data.overflow_arithmetic.vector_len);
63916393 }
63926394 zig_unreachable();
63936395}
src/codegen.cpp+115-82
......@@ -715,38 +715,59 @@ static void clear_debug_source_node(CodeGen *g) {
715715 ZigLLVMClearCurrentDebugLocation(g->builder);
716716}
717717
718static LLVMValueRef get_arithmetic_overflow_fn(CodeGen *g, ZigType *type_entry,
718static LLVMValueRef get_arithmetic_overflow_fn(CodeGen *g, ZigType *operand_type,
719719 const char *signed_name, const char *unsigned_name)
720720{
721 ZigType *int_type = (operand_type->id == ZigTypeIdVector) ? operand_type->data.vector.elem_type : operand_type;
721722 char fn_name[64];
722723
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;
726726
727 LLVMTypeRef return_elem_types[] = {
728 type_entry->type_ref,
729 LLVMInt1Type(),
730 };
731727 LLVMTypeRef param_types[] = {
732 type_entry->type_ref,
733 type_entry->type_ref,
728 operand_type->type_ref,
729 operand_type->type_ref,
734730 };
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 }
740758}
741759
742static LLVMValueRef get_int_overflow_fn(CodeGen *g, ZigType *type_entry, AddSubMul add_sub_mul) {
743 assert(type_entry->id == ZigTypeIdInt);
760static 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);
744763
745764 ZigLLVMFnKey key = {};
746765 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;
748767 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;
750771
751772 auto existing_entry = g->llvm_fn_table.maybe_get(key);
752773 if (existing_entry)
......@@ -755,13 +776,13 @@ static LLVMValueRef get_int_overflow_fn(CodeGen *g, ZigType *type_entry, AddSubM
755776 LLVMValueRef fn_val;
756777 switch (add_sub_mul) {
757778 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");
759780 break;
760781 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");
762783 break;
763784 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");
765786 break;
766787 }
767788
......@@ -1752,17 +1773,49 @@ static LLVMValueRef gen_widen_or_shorten(CodeGen *g, bool want_runtime_safety, Z
17521773 }
17531774}
17541775
1755static LLVMValueRef gen_overflow_op(CodeGen *g, ZigType *type_entry, AddSubMul op,
1776typedef 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.
1780static const BuildBinOpFunc float_op[3] = { LLVMBuildFAdd, LLVMBuildFSub, LLVMBuildFMul };
1781static const BuildBinOpFunc wrap_op[3] = { LLVMBuildAdd, LLVMBuildSub, LLVMBuildMul };
1782static const BuildBinOpFunc signed_op[3] = { LLVMBuildNSWAdd, LLVMBuildNSWSub, LLVMBuildNSWMul };
1783static const BuildBinOpFunc unsigned_op[3] = { LLVMBuildNUWAdd, LLVMBuildNUWSub, LLVMBuildNUWMul };
1784
1785static LLVMValueRef gen_overflow_op(CodeGen *g, ZigType *operand_type, AddSubMul op,
17561786 LLVMValueRef val1, LLVMValueRef val2)
17571787{
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
17661819 LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "OverflowFail");
17671820 LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "OverflowOk");
17681821 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
25912644
25922645}
25932646
2594typedef LLVMValueRef (*BuildBinOpFunc)(LLVMBuilderRef, LLVMValueRef, LLVMValueRef, const char *);
2595
25962647static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
25972648 IrInstructionBinOp *bin_op_instruction)
25982649{
......@@ -2608,7 +2659,8 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
26082659 (op_id == IrBinOpAdd || op_id == IrBinOpSub) &&
26092660 op1->value.type->data.pointer.ptr_len == PtrLenUnknown)
26102661 );
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;
26122664
26132665 bool want_runtime_safety = bin_op_instruction->safety_check_on &&
26142666 ir_want_runtime_safety(g, &bin_op_instruction->base);
......@@ -2634,17 +2686,17 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
26342686 case IrBinOpCmpGreaterThan:
26352687 case IrBinOpCmpLessOrEq:
26362688 case IrBinOpCmpGreaterOrEq:
2637 if (type_entry->id == ZigTypeIdFloat) {
2689 if (scalar_type->id == ZigTypeIdFloat) {
26382690 ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &bin_op_instruction->base));
26392691 LLVMRealPredicate pred = cmp_op_to_real_predicate(op_id);
26402692 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);
26432695 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)
26482700 {
26492701 LLVMIntPredicate pred = cmp_op_to_int_predicate(op_id, false);
26502702 return LLVMBuildICmp(g->builder, pred, op1_value, op2_value, "");
......@@ -2657,31 +2709,16 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
26572709 case IrBinOpAddWrap:
26582710 case IrBinOpSub:
26592711 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;
26692712 bool is_wrapping = (op_id == IrBinOpSubWrap || op_id == IrBinOpAddWrap || op_id == IrBinOpMultWrap);
26702713 AddSubMul add_sub_mul =
26712714 op_id == IrBinOpAdd || op_id == IrBinOpAddWrap ? AddSubMulAdd :
26722715 op_id == IrBinOpSub || op_id == IrBinOpSubWrap ? AddSubMulSub :
26732716 AddSubMulMul;
26742717
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);
26832720 LLVMValueRef subscript_value;
2684 if (is_vector)
2721 if (operand_type->id == ZigTypeIdVector)
26852722 zig_panic("TODO: Implement vector operations on pointers.");
26862723
26872724 switch (add_sub_mul) {
......@@ -2697,17 +2734,15 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
26972734
26982735 // TODO runtime safety
26992736 return LLVMBuildInBoundsGEP(g->builder, op1_value, &subscript_value, 1, "");
2700 } else if (type_entry->id == ZigTypeIdFloat) {
2737 } else if (scalar_type->id == ZigTypeIdFloat) {
27012738 ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &bin_op_instruction->base));
27022739 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) {
27042741 if (is_wrapping) {
27052742 return wrap_op[add_sub_mul](g->builder, op1_value, op2_value, "");
27062743 } 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) {
27112746 return signed_op[add_sub_mul](g->builder, op1_value, op2_value, "");
27122747 } else {
27132748 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,
27252760 case IrBinOpBitShiftLeftLossy:
27262761 case IrBinOpBitShiftLeftExact:
27272762 {
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);
27312765 bool is_sloppy = (op_id == IrBinOpBitShiftLeftLossy);
27322766 if (is_sloppy) {
27332767 return LLVMBuildShl(g->builder, op1_value, op2_casted, "");
27342768 } 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) {
27372771 return ZigLLVMBuildNSWShl(g->builder, op1_value, op2_casted, "");
27382772 } else {
27392773 return ZigLLVMBuildNUWShl(g->builder, op1_value, op2_casted, "");
......@@ -2742,19 +2776,18 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
27422776 case IrBinOpBitShiftRightLossy:
27432777 case IrBinOpBitShiftRightExact:
27442778 {
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);
27482781 bool is_sloppy = (op_id == IrBinOpBitShiftRightLossy);
27492782 if (is_sloppy) {
2750 if (type_entry->data.integral.is_signed) {
2783 if (scalar_type->data.integral.is_signed) {
27512784 return LLVMBuildAShr(g->builder, op1_value, op2_casted, "");
27522785 } else {
27532786 return LLVMBuildLShr(g->builder, op1_value, op2_casted, "");
27542787 }
27552788 } 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) {
27582791 return ZigLLVMBuildAShrExact(g->builder, op1_value, op2_casted, "");
27592792 } else {
27602793 return ZigLLVMBuildLShrExact(g->builder, op1_value, op2_casted, "");
......@@ -2762,22 +2795,22 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
27622795 }
27632796 case IrBinOpDivUnspecified:
27642797 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);
27662799 case IrBinOpDivExact:
27672800 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);
27692802 case IrBinOpDivTrunc:
27702803 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);
27722805 case IrBinOpDivFloor:
27732806 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);
27752808 case IrBinOpRemRem:
27762809 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);
27782811 case IrBinOpRemMod:
27792812 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);
27812814 }
27822815 zig_unreachable();
27832816}
test/runtime_safety.zig+14
......@@ -94,6 +94,20 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
9494 \\}
9595 );
9696
97 cases.addRuntimeSafety("vector integer addition overflow",
98 \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn {
99 \\ @import("std").os.exit(126);
100 \\}
101 \\pub fn main() void {
102 \\ var a: @Vector(4, i32) = []i32{ 1, 2, 2147483643, 4 };
103 \\ var b: @Vector(4, i32) = []i32{ 5, 6, 7, 8 };
104 \\ const x = add(a, b);
105 \\}
106 \\fn add(a: @Vector(4, i32), b: @Vector(4, i32)) @Vector(4, i32) {
107 \\ return a + b;
108 \\}
109 );
110
97111 cases.addRuntimeSafety("integer subtraction overflow",
98112 \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn {
99113 \\ @import("std").os.exit(126);
test/stage1/behavior/math.zig+17
......@@ -1,5 +1,7 @@
11const std = @import("std");
22const expect = std.testing.expect;
3const expectEqual = std.testing.expectEqual;
4const expectEqualSlices = std.testing.expectEqualSlices;
35const maxInt = std.math.maxInt;
46const minInt = std.math.minInt;
57
......@@ -498,3 +500,18 @@ test "comptime_int param and return" {
498500fn comptimeAdd(comptime a: comptime_int, comptime b: comptime_int) comptime_int {
499501 return a + b;
500502}
503
504test "vector integer addition" {
505 const S = struct {
506 fn doTheTest() void {
507 var a: @Vector(4, i32) = []i32{ 1, 2, 3, 4 };
508 var b: @Vector(4, i32) = []i32{ 5, 6, 7, 8 };
509 var result = a + b;
510 var result_array: [4]i32 = result;
511 const expected = []i32{ 6, 8, 10, 12 };
512 expectEqualSlices(i32, &expected, &result_array);
513 }
514 };
515 S.doTheTest();
516 comptime S.doTheTest();
517}