authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-07-16 16:10:11+03:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-07-23 15:40:11+03:00
log55fe34100f8b516480cf530eb58d00ea8b665765
treea687757a7988bd2998304aa0c51fa7243af1f789
parent76d099950aa2e5fee4897c8bc401946f39ed87a4

Sema: exact division safety


4 files changed, 56 insertions(+), 8 deletions(-)

src/Sema.zig+43
...@@ -11917,6 +11917,47 @@ fn analyzeArithmetic(...@@ -11917,6 +11917,47 @@ fn analyzeArithmetic(
11917 },11917 },
11918 else => {},11918 else => {},
11919 }11919 }
11920 if (rs.air_tag == .div_exact) {
11921 const result = try block.addBinOp(.div_exact, casted_lhs, casted_rhs);
11922 const ok = if (scalar_tag == .Float) ok: {
11923 const floored = try block.addUnOp(.floor, result);
11924
11925 if (resolved_type.zigTypeTag() == .Vector) {
11926 const eql = try block.addCmpVector(result, floored, .eq, try sema.addType(resolved_type));
11927 break :ok try block.addInst(.{
11928 .tag = .reduce,
11929 .data = .{ .reduce = .{
11930 .operand = eql,
11931 .operation = .And,
11932 } },
11933 });
11934 } else {
11935 const is_in_range = try block.addBinOp(.cmp_eq, result, floored);
11936 break :ok is_in_range;
11937 }
11938 } else ok: {
11939 const remainder = try block.addBinOp(.rem, casted_lhs, casted_rhs);
11940
11941 if (resolved_type.zigTypeTag() == .Vector) {
11942 const zero_val = try Value.Tag.repeated.create(sema.arena, Value.zero);
11943 const zero = try sema.addConstant(sema.typeOf(casted_rhs), zero_val);
11944 const eql = try block.addCmpVector(remainder, zero, .eq, try sema.addType(resolved_type));
11945 break :ok try block.addInst(.{
11946 .tag = .reduce,
11947 .data = .{ .reduce = .{
11948 .operand = eql,
11949 .operation = .And,
11950 } },
11951 });
11952 } else {
11953 const zero = try sema.addConstant(sema.typeOf(casted_rhs), Value.zero);
11954 const is_in_range = try block.addBinOp(.cmp_eq, remainder, zero);
11955 break :ok is_in_range;
11956 }
11957 };
11958 try sema.addSafetyCheck(block, ok, .exact_division_remainder);
11959 return result;
11960 }
11920 }11961 }
11921 return block.addBinOp(rs.air_tag, casted_lhs, casted_rhs);11962 return block.addBinOp(rs.air_tag, casted_lhs, casted_rhs);
11922}11963}
...@@ -18856,6 +18897,7 @@ pub const PanicId = enum {...@@ -18856,6 +18897,7 @@ pub const PanicId = enum {
18856 shr_overflow,18897 shr_overflow,
18857 divide_by_zero,18898 divide_by_zero,
18858 remainder_division_zero_negative,18899 remainder_division_zero_negative,
18900 exact_division_remainder,
18859};18901};
1886018902
18861fn addSafetyCheck(18903fn addSafetyCheck(
...@@ -19077,6 +19119,7 @@ fn safetyPanic(...@@ -19077,6 +19119,7 @@ fn safetyPanic(
19077 .shr_overflow => "right shift overflowed bits",19119 .shr_overflow => "right shift overflowed bits",
19078 .divide_by_zero => "division by zero",19120 .divide_by_zero => "division by zero",
19079 .remainder_division_zero_negative => "remainder division by zero or negative value",19121 .remainder_division_zero_negative => "remainder division by zero or negative value",
19122 .exact_division_remainder => "exact division produced remainder",
19080 };19123 };
1908119124
19082 const msg_inst = msg_inst: {19125 const msg_inst = msg_inst: {
test/behavior/math.zig+1
...@@ -377,6 +377,7 @@ fn testBinaryNot(x: u16) !void {...@@ -377,6 +377,7 @@ fn testBinaryNot(x: u16) !void {
377}377}
378378
379test "division" {379test "division" {
380 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
380 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO381 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
381 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO382 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
382 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO383 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
test/cases/safety/exact division failure - vectors.zig +6-4
...@@ -1,9 +1,11 @@...@@ -1,9 +1,11 @@
1const std = @import("std");1const std = @import("std");
22
3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
4 _ = message;
5 _ = stack_trace;4 _ = stack_trace;
6 std.process.exit(0);5 if (std.mem.eql(u8, message, "exact division produced remainder")) {
6 std.process.exit(0);
7 }
8 std.process.exit(1);
7}9}
810
9pub fn main() !void {11pub fn main() !void {
...@@ -17,5 +19,5 @@ fn divExact(a: @Vector(4, i32), b: @Vector(4, i32)) @Vector(4, i32) {...@@ -17,5 +19,5 @@ fn divExact(a: @Vector(4, i32), b: @Vector(4, i32)) @Vector(4, i32) {
17 return @divExact(a, b);19 return @divExact(a, b);
18}20}
19// run21// run
20// backend=stage1
21// target=native
\ No newline at end of file
22// backend=llvm
23// target=native
test/cases/safety/exact division failure.zig +6-4
...@@ -1,9 +1,11 @@...@@ -1,9 +1,11 @@
1const std = @import("std");1const std = @import("std");
22
3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
4 _ = message;
5 _ = stack_trace;4 _ = stack_trace;
6 std.process.exit(0);5 if (std.mem.eql(u8, message, "exact division produced remainder")) {
6 std.process.exit(0);
7 }
8 std.process.exit(1);
7}9}
810
9pub fn main() !void {11pub fn main() !void {
...@@ -15,5 +17,5 @@ fn divExact(a: i32, b: i32) i32 {...@@ -15,5 +17,5 @@ fn divExact(a: i32, b: i32) i32 {
15 return @divExact(a, b);17 return @divExact(a, b);
16}18}
17// run19// run
18// backend=stage1
19// target=native
\ No newline at end of file
20// backend=llvm
21// target=native