From 0782586b15302654501ebbcab3a2c63755a6fadb Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sat, 16 Jul 2022 02:55:08 +0300 Subject: [PATCH] Sema: divide by zero safety --- src/Sema.zig | 43 +++++++++++++++++++ .../integer division by zero - vectors.zig | 10 +++-- .../cases/safety/integer division by zero.zig | 10 +++-- .../remainder division by negative number.zig | 20 +++++++++ 4 files changed, 75 insertions(+), 8 deletions(-) create mode 100644 test/cases/safety/remainder division by negative number.zig diff --git a/src/Sema.zig b/src/Sema.zig index 64a740c6b1..9fdb98f3c2 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -11877,6 +11877,45 @@ fn analyzeArithmetic( return sema.tupleFieldValByIndex(block, src, op_ov, 0, op_ov_tuple_ty); } } + switch (rs.air_tag) { + .div_float, .div_exact, .div_trunc, .div_floor => { + const ok = if (resolved_type.zigTypeTag() == .Vector) ok: { + const zero_val = try Value.Tag.repeated.create(sema.arena, Value.zero); + const zero = try sema.addConstant(sema.typeOf(casted_rhs), zero_val); + const ok = try block.addCmpVector(casted_rhs, zero, .neq, try sema.addType(resolved_type)); + break :ok try block.addInst(.{ + .tag = .reduce, + .data = .{ .reduce = .{ + .operand = ok, + .operation = .And, + } }, + }); + } else ok: { + const zero = try sema.addConstant(sema.typeOf(casted_rhs), Value.zero); + break :ok try block.addBinOp(.cmp_neq, casted_rhs, zero); + }; + try sema.addSafetyCheck(block, ok, .divide_by_zero); + }, + .rem, .mod => { + const ok = if (resolved_type.zigTypeTag() == .Vector) ok: { + const zero_val = try Value.Tag.repeated.create(sema.arena, Value.zero); + const zero = try sema.addConstant(sema.typeOf(casted_rhs), zero_val); + const ok = try block.addCmpVector(casted_rhs, zero, if (scalar_tag == .Int) .gt else .neq, try sema.addType(resolved_type)); + break :ok try block.addInst(.{ + .tag = .reduce, + .data = .{ .reduce = .{ + .operand = ok, + .operation = .And, + } }, + }); + } else ok: { + const zero = try sema.addConstant(sema.typeOf(casted_rhs), Value.zero); + break :ok try block.addBinOp(if (scalar_tag == .Int) .cmp_gt else .cmp_neq, casted_rhs, zero); + }; + try sema.addSafetyCheck(block, ok, .remainder_division_zero_negative); + }, + else => {}, + } } return block.addBinOp(rs.air_tag, casted_lhs, casted_rhs); } @@ -18813,6 +18852,8 @@ pub const PanicId = enum { integer_overflow, shl_overflow, shr_overflow, + divide_by_zero, + remainder_division_zero_negative, }; fn addSafetyCheck( @@ -19031,6 +19072,8 @@ fn safetyPanic( .integer_overflow => "integer overflow", .shl_overflow => "left shift overflowed bits", .shr_overflow => "right shift overflowed bits", + .divide_by_zero => "division by zero", + .remainder_division_zero_negative => "remainder division by zero or negative value", }; const msg_inst = msg_inst: { diff --git a/test/cases/safety/integer division by zero - vectors.zig b/test/cases/safety/integer division by zero - vectors.zig index 136f179935..0789f7f088 100644 --- a/test/cases/safety/integer division by zero - vectors.zig +++ b/test/cases/safety/integer division by zero - vectors.zig @@ -1,9 +1,11 @@ const std = @import("std"); pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { - _ = message; _ = stack_trace; - std.process.exit(0); + if (std.mem.eql(u8, message, "division by zero")) { + std.process.exit(0); + } + std.process.exit(1); } pub fn main() !void { var a: @Vector(4, i32) = [4]i32{111, 222, 333, 444}; @@ -16,5 +18,5 @@ fn div0(a: @Vector(4, i32), b: @Vector(4, i32)) @Vector(4, i32) { return @divTrunc(a, b); } // run -// backend=stage1 -// target=native \ No newline at end of file +// backend=llvm +// target=native diff --git a/test/cases/safety/integer division by zero.zig b/test/cases/safety/integer division by zero.zig index 8d80a7c848..46f57c1117 100644 --- a/test/cases/safety/integer division by zero.zig +++ b/test/cases/safety/integer division by zero.zig @@ -1,9 +1,11 @@ const std = @import("std"); pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { - _ = message; _ = stack_trace; - std.process.exit(0); + if (std.mem.eql(u8, message, "division by zero")) { + std.process.exit(0); + } + std.process.exit(1); } pub fn main() !void { const x = div0(999, 0); @@ -14,5 +16,5 @@ fn div0(a: i32, b: i32) i32 { return @divTrunc(a, b); } // run -// backend=stage1 -// target=native \ No newline at end of file +// backend=llvm +// target=native diff --git a/test/cases/safety/remainder division by negative number.zig b/test/cases/safety/remainder division by negative number.zig new file mode 100644 index 0000000000..2edbf4509c --- /dev/null +++ b/test/cases/safety/remainder division by negative number.zig @@ -0,0 +1,20 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { + _ = stack_trace; + if (std.mem.eql(u8, message, "remainder division by zero or negative value")) { + std.process.exit(0); + } + std.process.exit(1); +} +pub fn main() !void { + const x = div0(999, -1); + _ = x; + return error.TestFailed; +} +fn div0(a: i32, b: i32) i32 { + return @rem(a, b); +} +// run +// backend=llvm +// target=native