authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-02-09 14:44:33-05:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-02-09 14:44:33-05:00
log0a7bdc00771dbad1dfe5eb93a7cade89059d227a
tree8cb52e6e9ce0e52a63bccd5974c1a7c6150dfdce
parenta8a63feba7e40a998b1e1d8d36169e5f1be0d4e1
signature Commit is signed but in an unrecognized format.

implement vector addition with safety checking

this would work if @llvm.sadd.with.overflow supported vectors, which it does in trunk. but it does not support them in llvm 7 or even in llvm 8 release branch. so the next commit after this will have to do a different strategy, but when llvm 9 comes out it may be worth coming back to this one.

3 files changed, 95 insertions(+), 68 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+88-66
......@@ -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,28 @@ 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,
1776static LLVMValueRef gen_overflow_op(CodeGen *g, ZigType *operand_type, AddSubMul op,
17561777 LLVMValueRef val1, LLVMValueRef val2)
17571778{
1758 LLVMValueRef fn_val = get_int_overflow_fn(g, type_entry, op);
1779 LLVMValueRef fn_val = get_int_overflow_fn(g, operand_type, op);
17591780 LLVMValueRef params[] = {
17601781 val1,
17611782 val2,
17621783 };
17631784 LLVMValueRef result_struct = LLVMBuildCall(g->builder, fn_val, params, 2, "");
17641785 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
17661798 LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "OverflowFail");
17671799 LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "OverflowOk");
17681800 LLVMBuildCondBr(g->builder, overflow_bit, fail_block, ok_block);
......@@ -2608,7 +2640,8 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
26082640 (op_id == IrBinOpAdd || op_id == IrBinOpSub) &&
26092641 op1->value.type->data.pointer.ptr_len == PtrLenUnknown)
26102642 );
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;
26122645
26132646 bool want_runtime_safety = bin_op_instruction->safety_check_on &&
26142647 ir_want_runtime_safety(g, &bin_op_instruction->base);
......@@ -2634,17 +2667,17 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
26342667 case IrBinOpCmpGreaterThan:
26352668 case IrBinOpCmpLessOrEq:
26362669 case IrBinOpCmpGreaterOrEq:
2637 if (type_entry->id == ZigTypeIdFloat) {
2670 if (scalar_type->id == ZigTypeIdFloat) {
26382671 ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &bin_op_instruction->base));
26392672 LLVMRealPredicate pred = cmp_op_to_real_predicate(op_id);
26402673 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);
26432676 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)
26482681 {
26492682 LLVMIntPredicate pred = cmp_op_to_int_predicate(op_id, false);
26502683 return LLVMBuildICmp(g->builder, pred, op1_value, op2_value, "");
......@@ -2665,23 +2698,16 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
26652698 static const BuildBinOpFunc signed_op[3] = { LLVMBuildNSWAdd, LLVMBuildNSWSub, LLVMBuildNSWMul };
26662699 static const BuildBinOpFunc unsigned_op[3] = { LLVMBuildNUWAdd, LLVMBuildNUWSub, LLVMBuildNUWMul };
26672700
2668 bool is_vector = type_entry->id == ZigTypeIdVector;
26692701 bool is_wrapping = (op_id == IrBinOpSubWrap || op_id == IrBinOpAddWrap || op_id == IrBinOpMultWrap);
26702702 AddSubMul add_sub_mul =
26712703 op_id == IrBinOpAdd || op_id == IrBinOpAddWrap ? AddSubMulAdd :
26722704 op_id == IrBinOpSub || op_id == IrBinOpSubWrap ? AddSubMulSub :
26732705 AddSubMulMul;
26742706
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);
26832709 LLVMValueRef subscript_value;
2684 if (is_vector)
2710 if (operand_type->id == ZigTypeIdVector)
26852711 zig_panic("TODO: Implement vector operations on pointers.");
26862712
26872713 switch (add_sub_mul) {
......@@ -2697,17 +2723,15 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
26972723
26982724 // TODO runtime safety
26992725 return LLVMBuildInBoundsGEP(g->builder, op1_value, &subscript_value, 1, "");
2700 } else if (type_entry->id == ZigTypeIdFloat) {
2726 } else if (scalar_type->id == ZigTypeIdFloat) {
27012727 ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &bin_op_instruction->base));
27022728 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) {
27042730 if (is_wrapping) {
27052731 return wrap_op[add_sub_mul](g->builder, op1_value, op2_value, "");
27062732 } 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) {
27112735 return signed_op[add_sub_mul](g->builder, op1_value, op2_value, "");
27122736 } else {
27132737 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,
27252749 case IrBinOpBitShiftLeftLossy:
27262750 case IrBinOpBitShiftLeftExact:
27272751 {
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);
27312754 bool is_sloppy = (op_id == IrBinOpBitShiftLeftLossy);
27322755 if (is_sloppy) {
27332756 return LLVMBuildShl(g->builder, op1_value, op2_casted, "");
27342757 } 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) {
27372760 return ZigLLVMBuildNSWShl(g->builder, op1_value, op2_casted, "");
27382761 } else {
27392762 return ZigLLVMBuildNUWShl(g->builder, op1_value, op2_casted, "");
......@@ -2742,19 +2765,18 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
27422765 case IrBinOpBitShiftRightLossy:
27432766 case IrBinOpBitShiftRightExact:
27442767 {
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);
27482770 bool is_sloppy = (op_id == IrBinOpBitShiftRightLossy);
27492771 if (is_sloppy) {
2750 if (type_entry->data.integral.is_signed) {
2772 if (scalar_type->data.integral.is_signed) {
27512773 return LLVMBuildAShr(g->builder, op1_value, op2_casted, "");
27522774 } else {
27532775 return LLVMBuildLShr(g->builder, op1_value, op2_casted, "");
27542776 }
27552777 } 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) {
27582780 return ZigLLVMBuildAShrExact(g->builder, op1_value, op2_casted, "");
27592781 } else {
27602782 return ZigLLVMBuildLShrExact(g->builder, op1_value, op2_casted, "");
......@@ -2762,22 +2784,22 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
27622784 }
27632785 case IrBinOpDivUnspecified:
27642786 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);
27662788 case IrBinOpDivExact:
27672789 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);
27692791 case IrBinOpDivTrunc:
27702792 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);
27722794 case IrBinOpDivFloor:
27732795 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);
27752797 case IrBinOpRemRem:
27762798 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);
27782800 case IrBinOpRemMod:
27792801 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);
27812803 }
27822804 zig_unreachable();
27832805}