authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-08-05 17:47:01+03:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-08-05 22:13:58+03:00
log19d5ffc710faa23cd07a6dff23d4afc43c0c7f63
tree515867fdb18082dbe7388a14f18ee6210adb1bc8
parent9116e26c1ffb49cf68bebf3af2a019af08474d12

Sema: add safety check for non-power-of-two shift amounts


5 files changed, 102 insertions(+), 50 deletions(-)

src/Sema.zig+88-40
...@@ -10227,34 +10227,57 @@ fn zirShl(...@@ -10227,34 +10227,57 @@ fn zirShl(
10227 } else rhs;10227 } else rhs;
1022810228
10229 try sema.requireRuntimeBlock(block, src, runtime_src);10229 try sema.requireRuntimeBlock(block, src, runtime_src);
10230 if (block.wantSafety() and air_tag == .shl_exact) {10230 if (block.wantSafety()) {
10231 const op_ov_tuple_ty = try sema.overflowArithmeticTupleType(lhs_ty);10231 const bit_count = scalar_ty.intInfo(target).bits;
10232 const op_ov = try block.addInst(.{10232 if (!std.math.isPowerOfTwo(bit_count)) {
10233 .tag = .shl_with_overflow,10233 const bit_count_val = try Value.Tag.int_u64.create(sema.arena, bit_count);
10234 .data = .{ .ty_pl = .{10234
10235 .ty = try sema.addType(op_ov_tuple_ty),10235 const ok = if (rhs_ty.zigTypeTag() == .Vector) ok: {
10236 .payload = try sema.addExtra(Air.Bin{10236 const bit_count_inst = try sema.addConstant(rhs_ty, try Value.Tag.repeated.create(sema.arena, bit_count_val));
10237 .lhs = lhs,10237 const lt = try block.addCmpVector(rhs, bit_count_inst, .lt, try sema.addType(rhs_ty));
10238 .rhs = rhs,10238 break :ok try block.addInst(.{
10239 }),10239 .tag = .reduce,
10240 } },10240 .data = .{ .reduce = .{
10241 });10241 .operand = lt,
10242 const ov_bit = try sema.tupleFieldValByIndex(block, src, op_ov, 1, op_ov_tuple_ty);10242 .operation = .And,
10243 const any_ov_bit = if (lhs_ty.zigTypeTag() == .Vector)10243 } },
10244 try block.addInst(.{10244 });
10245 .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce,10245 } else ok: {
10246 .data = .{ .reduce = .{10246 const bit_count_inst = try sema.addConstant(rhs_ty, bit_count_val);
10247 .operand = ov_bit,10247 break :ok try block.addBinOp(.cmp_lt, rhs, bit_count_inst);
10248 .operation = .Or,10248 };
10249 try sema.addSafetyCheck(block, ok, .shift_rhs_too_big);
10250 }
10251
10252 if (air_tag == .shl_exact) {
10253 const op_ov_tuple_ty = try sema.overflowArithmeticTupleType(lhs_ty);
10254 const op_ov = try block.addInst(.{
10255 .tag = .shl_with_overflow,
10256 .data = .{ .ty_pl = .{
10257 .ty = try sema.addType(op_ov_tuple_ty),
10258 .payload = try sema.addExtra(Air.Bin{
10259 .lhs = lhs,
10260 .rhs = rhs,
10261 }),
10249 } },10262 } },
10250 })10263 });
10251 else10264 const ov_bit = try sema.tupleFieldValByIndex(block, src, op_ov, 1, op_ov_tuple_ty);
10252 ov_bit;10265 const any_ov_bit = if (lhs_ty.zigTypeTag() == .Vector)
10253 const zero_ov = try sema.addConstant(Type.@"u1", Value.zero);10266 try block.addInst(.{
10254 const no_ov = try block.addBinOp(.cmp_eq, any_ov_bit, zero_ov);10267 .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce,
10268 .data = .{ .reduce = .{
10269 .operand = ov_bit,
10270 .operation = .Or,
10271 } },
10272 })
10273 else
10274 ov_bit;
10275 const zero_ov = try sema.addConstant(Type.@"u1", Value.zero);
10276 const no_ov = try block.addBinOp(.cmp_eq, any_ov_bit, zero_ov);
1025510277
10256 try sema.addSafetyCheck(block, no_ov, .shl_overflow);10278 try sema.addSafetyCheck(block, no_ov, .shl_overflow);
10257 return sema.tupleFieldValByIndex(block, src, op_ov, 0, op_ov_tuple_ty);10279 return sema.tupleFieldValByIndex(block, src, op_ov, 0, op_ov_tuple_ty);
10280 }
10258 }10281 }
10259 return block.addBinOp(air_tag, lhs, new_rhs);10282 return block.addBinOp(air_tag, lhs, new_rhs);
10260}10283}
...@@ -10333,20 +10356,43 @@ fn zirShr(...@@ -10333,20 +10356,43 @@ fn zirShr(
1033310356
10334 try sema.requireRuntimeBlock(block, src, runtime_src);10357 try sema.requireRuntimeBlock(block, src, runtime_src);
10335 const result = try block.addBinOp(air_tag, lhs, rhs);10358 const result = try block.addBinOp(air_tag, lhs, rhs);
10336 if (block.wantSafety() and air_tag == .shr_exact) {10359 if (block.wantSafety()) {
10337 const back = try block.addBinOp(.shl, result, rhs);10360 const bit_count = scalar_ty.intInfo(target).bits;
1033810361 if (!std.math.isPowerOfTwo(bit_count)) {
10339 const ok = if (rhs_ty.zigTypeTag() == .Vector) ok: {10362 const bit_count_val = try Value.Tag.int_u64.create(sema.arena, bit_count);
10340 const eql = try block.addCmpVector(lhs, back, .eq, try sema.addType(rhs_ty));10363
10341 break :ok try block.addInst(.{10364 const ok = if (rhs_ty.zigTypeTag() == .Vector) ok: {
10342 .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce,10365 const bit_count_inst = try sema.addConstant(rhs_ty, try Value.Tag.repeated.create(sema.arena, bit_count_val));
10343 .data = .{ .reduce = .{10366 const lt = try block.addCmpVector(rhs, bit_count_inst, .lt, try sema.addType(rhs_ty));
10344 .operand = eql,10367 break :ok try block.addInst(.{
10345 .operation = .And,10368 .tag = .reduce,
10346 } },10369 .data = .{ .reduce = .{
10347 });10370 .operand = lt,
10348 } else try block.addBinOp(.cmp_eq, lhs, back);10371 .operation = .And,
10349 try sema.addSafetyCheck(block, ok, .shr_overflow);10372 } },
10373 });
10374 } else ok: {
10375 const bit_count_inst = try sema.addConstant(rhs_ty, bit_count_val);
10376 break :ok try block.addBinOp(.cmp_lt, rhs, bit_count_inst);
10377 };
10378 try sema.addSafetyCheck(block, ok, .shift_rhs_too_big);
10379 }
10380
10381 if (air_tag == .shr_exact) {
10382 const back = try block.addBinOp(.shl, result, rhs);
10383
10384 const ok = if (rhs_ty.zigTypeTag() == .Vector) ok: {
10385 const eql = try block.addCmpVector(lhs, back, .eq, try sema.addType(rhs_ty));
10386 break :ok try block.addInst(.{
10387 .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce,
10388 .data = .{ .reduce = .{
10389 .operand = eql,
10390 .operation = .And,
10391 } },
10392 });
10393 } else try block.addBinOp(.cmp_eq, lhs, back);
10394 try sema.addSafetyCheck(block, ok, .shr_overflow);
10395 }
10350 }10396 }
10351 return result;10397 return result;
10352}10398}
...@@ -19972,6 +20018,7 @@ pub const PanicId = enum {...@@ -19972,6 +20018,7 @@ pub const PanicId = enum {
19972 inactive_union_field,20018 inactive_union_field,
19973 integer_part_out_of_bounds,20019 integer_part_out_of_bounds,
19974 corrupt_switch,20020 corrupt_switch,
20021 shift_rhs_too_big,
19975};20022};
1997620023
19977fn addSafetyCheck(20024fn addSafetyCheck(
...@@ -20268,6 +20315,7 @@ fn safetyPanic(...@@ -20268,6 +20315,7 @@ fn safetyPanic(
20268 .inactive_union_field => "access of inactive union field",20315 .inactive_union_field => "access of inactive union field",
20269 .integer_part_out_of_bounds => "integer part of floating point value out of bounds",20316 .integer_part_out_of_bounds => "integer part of floating point value out of bounds",
20270 .corrupt_switch => "switch on corrupt value",20317 .corrupt_switch => "switch on corrupt value",
20318 .shift_rhs_too_big => "shift amount is greater than the type size",
20271 };20319 };
2027220320
20273 const msg_inst = msg_inst: {20321 const msg_inst = msg_inst: {
test/cases/safety/shift left by huge amount.zig +1-1
...@@ -17,5 +17,5 @@ pub fn main() !void {...@@ -17,5 +17,5 @@ pub fn main() !void {
17}17}
1818
19// run19// run
20// backend=stage120// backend=llvm
21// target=native21// target=native
test/cases/safety/shift right by huge amount.zig +1-1
...@@ -17,5 +17,5 @@ pub fn main() !void {...@@ -17,5 +17,5 @@ pub fn main() !void {
17}17}
1818
19// run19// run
20// backend=stage120// backend=llvm
21// target=native21// target=native
test/cases/safety/signed integer division overflow - 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, "integer overflow")) {
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 div(a: @Vector(4, i16), b: @Vector(4, i16)) @Vector(4, i16) {...@@ -17,5 +19,5 @@ fn div(a: @Vector(4, i16), b: @Vector(4, i16)) @Vector(4, i16) {
17 return @divTrunc(a, b);19 return @divTrunc(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/signed integer division overflow.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, "integer overflow")) {
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 div(a: i16, b: i16) i16 {...@@ -15,5 +17,5 @@ fn div(a: i16, b: i16) i16 {
15 return @divTrunc(a, b);17 return @divTrunc(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