authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-02-09 15:23:29-05:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-02-09 16:24:29-05:00
log373e21bb564a27c4292812bdfd1673711c2e0fe4
treeddc9d0174cc7bb6f3681975e6940c882c1b0a283
parent0a7bdc00771dbad1dfe5eb93a7cade89059d227a
signature Commit is signed but in an unrecognized format.

implement vector math safety with ext and trunc


3 files changed, 64 insertions(+), 22 deletions(-)

src/codegen.cpp+33-22
...@@ -1773,25 +1773,46 @@ static LLVMValueRef gen_widen_or_shorten(CodeGen *g, bool want_runtime_safety, Z...@@ -1773,25 +1773,46 @@ static LLVMValueRef gen_widen_or_shorten(CodeGen *g, bool want_runtime_safety, Z
1773 }1773 }
1774}1774}
17751775
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
1776static LLVMValueRef gen_overflow_op(CodeGen *g, ZigType *operand_type, AddSubMul op,1785static LLVMValueRef gen_overflow_op(CodeGen *g, ZigType *operand_type, AddSubMul op,
1777 LLVMValueRef val1, LLVMValueRef val2)1786 LLVMValueRef val1, LLVMValueRef val2)
1778{1787{
1779 LLVMValueRef fn_val = get_int_overflow_fn(g, operand_type, op);
1780 LLVMValueRef params[] = {
1781 val1,
1782 val2,
1783 };
1784 LLVMValueRef result_struct = LLVMBuildCall(g->builder, fn_val, params, 2, "");
1785 LLVMValueRef result = LLVMBuildExtractValue(g->builder, result_struct, 0, "");
1786
1787 LLVMValueRef overflow_bit;1788 LLVMValueRef overflow_bit;
1789 LLVMValueRef result;
1790
1788 if (operand_type->id == ZigTypeIdVector) {1791 if (operand_type->id == ZigTypeIdVector) {
1789 LLVMValueRef overflow_vector = LLVMBuildExtractValue(g->builder, result_struct, 1, "");1792 ZigType *int_type = operand_type->data.vector.elem_type;
1790 LLVMTypeRef bigger_int_type_ref = LLVMIntType(operand_type->data.vector.len);1793 assert(int_type->id == ZigTypeIdInt);
1791 LLVMValueRef bitcasted_overflow = LLVMBuildBitCast(g->builder, overflow_vector, bigger_int_type_ref, "");1794 LLVMTypeRef one_more_bit_int = LLVMIntType(int_type->data.integral.bit_count + 1);
1792 LLVMValueRef zero = LLVMConstNull(bigger_int_type_ref);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);
1793 overflow_bit = LLVMBuildICmp(g->builder, LLVMIntNE, bitcasted_overflow, zero, "");1807 overflow_bit = LLVMBuildICmp(g->builder, LLVMIntNE, bitcasted_overflow, zero, "");
1794 } else {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, "");
1795 overflow_bit = LLVMBuildExtractValue(g->builder, result_struct, 1, "");1816 overflow_bit = LLVMBuildExtractValue(g->builder, result_struct, 1, "");
1796 }1817 }
17971818
...@@ -2623,8 +2644,6 @@ static LLVMValueRef gen_rem(CodeGen *g, bool want_runtime_safety, bool want_fast...@@ -2623,8 +2644,6 @@ static LLVMValueRef gen_rem(CodeGen *g, bool want_runtime_safety, bool want_fast
26232644
2624}2645}
26252646
2626typedef LLVMValueRef (*BuildBinOpFunc)(LLVMBuilderRef, LLVMValueRef, LLVMValueRef, const char *);
2627
2628static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,2647static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
2629 IrInstructionBinOp *bin_op_instruction)2648 IrInstructionBinOp *bin_op_instruction)
2630{2649{
...@@ -2690,14 +2709,6 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,...@@ -2690,14 +2709,6 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
2690 case IrBinOpAddWrap:2709 case IrBinOpAddWrap:
2691 case IrBinOpSub:2710 case IrBinOpSub:
2692 case IrBinOpSubWrap: {2711 case IrBinOpSubWrap: {
2693 // These are lookup table using the AddSubMul enum as the lookup.
2694 // If AddSubMul ever changes, then these tables will be out of
2695 // date.
2696 static const BuildBinOpFunc float_op[3] = { LLVMBuildFAdd, LLVMBuildFSub, LLVMBuildFMul };
2697 static const BuildBinOpFunc wrap_op[3] = { LLVMBuildAdd, LLVMBuildSub, LLVMBuildMul };
2698 static const BuildBinOpFunc signed_op[3] = { LLVMBuildNSWAdd, LLVMBuildNSWSub, LLVMBuildNSWMul };
2699 static const BuildBinOpFunc unsigned_op[3] = { LLVMBuildNUWAdd, LLVMBuildNUWSub, LLVMBuildNUWMul };
2700
2701 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);
2702 AddSubMul add_sub_mul =2713 AddSubMul add_sub_mul =
2703 op_id == IrBinOpAdd || op_id == IrBinOpAddWrap ? AddSubMulAdd :2714 op_id == IrBinOpAdd || op_id == IrBinOpAddWrap ? AddSubMulAdd :
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}