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
signaturelock-open 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 {...@@ -1538,6 +1538,8 @@ enum ZigLLVMFnId {
1538 ZigLLVMFnIdBitReverse,1538 ZigLLVMFnIdBitReverse,
1539};1539};
15401540
1541// There are a bunch of places in code that rely on these values being in
1542// exactly this order.
1541enum AddSubMul {1543enum AddSubMul {
1542 AddSubMulAdd = 0,1544 AddSubMulAdd = 0,
1543 AddSubMulSub = 1,1545 AddSubMulSub = 1,
...@@ -1563,6 +1565,7 @@ struct ZigLLVMFnKey {...@@ -1563,6 +1565,7 @@ struct ZigLLVMFnKey {
1563 struct {1565 struct {
1564 AddSubMul add_sub_mul;1566 AddSubMul add_sub_mul;
1565 uint32_t bit_count;1567 uint32_t bit_count;
1568 uint32_t vector_len; // 0 means not a vector
1566 bool is_signed;1569 bool is_signed;
1567 } overflow_arithmetic;1570 } overflow_arithmetic;
1568 struct {1571 struct {
src/analyze.cpp+4-2
...@@ -6361,7 +6361,8 @@ uint32_t zig_llvm_fn_key_hash(ZigLLVMFnKey x) {...@@ -6361,7 +6361,8 @@ uint32_t zig_llvm_fn_key_hash(ZigLLVMFnKey x) {
6361 case ZigLLVMFnIdOverflowArithmetic:6361 case ZigLLVMFnIdOverflowArithmetic:
6362 return ((uint32_t)(x.data.overflow_arithmetic.bit_count) * 87135777) +6362 return ((uint32_t)(x.data.overflow_arithmetic.bit_count) * 87135777) +
6363 ((uint32_t)(x.data.overflow_arithmetic.add_sub_mul) * 31640542) +6363 ((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;
6365 }6366 }
6366 zig_unreachable();6367 zig_unreachable();
6367}6368}
...@@ -6387,7 +6388,8 @@ bool zig_llvm_fn_key_eql(ZigLLVMFnKey a, ZigLLVMFnKey b) {...@@ -6387,7 +6388,8 @@ bool zig_llvm_fn_key_eql(ZigLLVMFnKey a, ZigLLVMFnKey b) {
6387 case ZigLLVMFnIdOverflowArithmetic:6388 case ZigLLVMFnIdOverflowArithmetic:
6388 return (a.data.overflow_arithmetic.bit_count == b.data.overflow_arithmetic.bit_count) &&6389 return (a.data.overflow_arithmetic.bit_count == b.data.overflow_arithmetic.bit_count) &&
6389 (a.data.overflow_arithmetic.add_sub_mul == b.data.overflow_arithmetic.add_sub_mul) &&6390 (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);
6391 }6393 }
6392 zig_unreachable();6394 zig_unreachable();
6393}6395}
src/codegen.cpp+115-82
...@@ -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}
717717
718static LLVMValueRef get_arithmetic_overflow_fn(CodeGen *g, ZigType *type_entry,718static 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];
722723
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);
726726
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}
741759
742static LLVMValueRef get_int_overflow_fn(CodeGen *g, ZigType *type_entry, AddSubMul add_sub_mul) {760static 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);
744763
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;
750771
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 }
767788
...@@ -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}
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,
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
25912644
2592}2645}
25932646
2594typedef LLVMValueRef (*BuildBinOpFunc)(LLVMBuilderRef, LLVMValueRef, LLVMValueRef, const char *);
2595
2596static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,2647static 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;
26122664
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;
26742717
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 avoid2719 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.");
26862723
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,
26972734
2698 // TODO runtime safety2735 // 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}
test/runtime_safety.zig+14
...@@ -94,6 +94,20 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {...@@ -94,6 +94,20 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
94 \\}94 \\}
95 );95 );
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
97 cases.addRuntimeSafety("integer subtraction overflow",111 cases.addRuntimeSafety("integer subtraction overflow",
98 \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn {112 \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn {
99 \\ @import("std").os.exit(126);113 \\ @import("std").os.exit(126);
test/stage1/behavior/math.zig+17
...@@ -1,5 +1,7 @@...@@ -1,5 +1,7 @@
1const std = @import("std");1const std = @import("std");
2const expect = std.testing.expect;2const expect = std.testing.expect;
3const expectEqual = std.testing.expectEqual;
4const expectEqualSlices = std.testing.expectEqualSlices;
3const maxInt = std.math.maxInt;5const maxInt = std.math.maxInt;
4const minInt = std.math.minInt;6const minInt = std.math.minInt;
57
...@@ -498,3 +500,18 @@ test "comptime_int param and return" {...@@ -498,3 +500,18 @@ test "comptime_int param and return" {
498fn comptimeAdd(comptime a: comptime_int, comptime b: comptime_int) comptime_int {500fn comptimeAdd(comptime a: comptime_int, comptime b: comptime_int) comptime_int {
499 return a + b;501 return a + b;
500}502}
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}