authorgravatar for william@sengir.comWilliam Sengir <william@sengir.com> 2022-03-28 13:08:50-07:00
committergravatar for william@sengir.comWilliam Sengir <william@sengir.com> 2022-04-22 22:08:16-07:00
logfa42fcbc1af8b8713b52a5693c4dfa595ec241b7
tree2682854cec4b0ecf0a456c87459e03aa2e6b460c
parentcf20b97b713d992d84fdd8935ef935f61ed6d747
signaturelock-open Commit is signed but in an unrecognized format.

Sema: combine signed->unsigned and shrinkage runtime checks in intCast


1 files changed, 35 insertions(+), 17 deletions(-)

src/Sema.zig+35-17
...@@ -6971,6 +6971,7 @@ fn intCast(...@@ -6971,6 +6971,7 @@ fn intCast(
6971 operand_src: LazySrcLoc,6971 operand_src: LazySrcLoc,
6972 runtime_safety: bool,6972 runtime_safety: bool,
6973) CompileError!Air.Inst.Ref {6973) CompileError!Air.Inst.Ref {
6974 // TODO: Add support for vectors
6974 const dest_is_comptime_int = try sema.checkIntType(block, dest_ty_src, dest_ty);6975 const dest_is_comptime_int = try sema.checkIntType(block, dest_ty_src, dest_ty);
6975 _ = try sema.checkIntType(block, operand_src, sema.typeOf(operand));6976 _ = try sema.checkIntType(block, operand_src, sema.typeOf(operand));
69766977
...@@ -7006,27 +7007,44 @@ fn intCast(...@@ -7006,27 +7007,44 @@ fn intCast(
7006 const wanted_info = dest_ty.intInfo(target);7007 const wanted_info = dest_ty.intInfo(target);
7007 const actual_bits = actual_info.bits;7008 const actual_bits = actual_info.bits;
7008 const wanted_bits = wanted_info.bits;7009 const wanted_bits = wanted_info.bits;
70097010 const actual_value_bits = actual_bits - @boolToInt(actual_info.signedness == .signed);
7011 const wanted_value_bits = wanted_bits - @boolToInt(wanted_info.signedness == .signed);
7012
7013 // range shrinkage
7014 // requirement: int value fits into target type
7015 if (wanted_value_bits < actual_value_bits) {
7016 const dest_max_val = try dest_ty.maxInt(sema.arena, target);
7017 const dest_max = try sema.addConstant(operand_ty, dest_max_val);
7018 const diff = try block.addBinOp(.subwrap, dest_max, operand);
7019
7020 if (actual_info.signedness == .signed) {
7021 // Reinterpret the sign-bit as part of the value. This will make
7022 // negative differences (`operand` > `dest_max`) appear too big.
7023 const unsigned_operand_ty = try Type.Tag.int_unsigned.create(sema.arena, actual_bits);
7024 const diff_unsigned = try block.addBitCast(unsigned_operand_ty, diff);
7025
7026 // If the destination type is signed, then we need to double its
7027 // range to account for negative values.
7028 const dest_range_val = if (wanted_info.signedness == .signed) range_val: {
7029 const range_minus_one = try dest_max_val.shl(Value.one, unsigned_operand_ty, sema.arena, target);
7030 break :range_val try range_minus_one.intAdd(Value.one, unsigned_operand_ty, sema.arena, target);
7031 } else dest_max_val;
7032 const dest_range = try sema.addConstant(unsigned_operand_ty, dest_range_val);
7033
7034 const is_in_range = try block.addBinOp(.cmp_lte, diff_unsigned, dest_range);
7035 try sema.addSafetyCheck(block, is_in_range, .cast_truncated_data);
7036 } else {
7037 const is_in_range = try block.addBinOp(.cmp_lte, diff, dest_max);
7038 try sema.addSafetyCheck(block, is_in_range, .cast_truncated_data);
7039 }
7040 }
7041 // no shrinkage, yes sign loss
7010 // requirement: signed to unsigned >= 07042 // requirement: signed to unsigned >= 0
7011 if (actual_info.signedness == .signed and7043 else if (actual_info.signedness == .signed and wanted_info.signedness == .unsigned) {
7012 wanted_info.signedness == .unsigned)7044 const zero_inst = try sema.addConstant(operand_ty, Value.zero);
7013 {
7014 const zero_inst = try sema.addConstant(sema.typeOf(operand), Value.zero);
7015 const is_in_range = try block.addBinOp(.cmp_gte, operand, zero_inst);7045 const is_in_range = try block.addBinOp(.cmp_gte, operand, zero_inst);
7016 try sema.addSafetyCheck(block, is_in_range, .cast_truncated_data);7046 try sema.addSafetyCheck(block, is_in_range, .cast_truncated_data);
7017 }7047 }
7018
7019 // requirement: unsigned int value fits into target type
7020 if (actual_bits > wanted_bits or
7021 (actual_bits == wanted_bits and
7022 actual_info.signedness == .unsigned and
7023 wanted_info.signedness == .signed))
7024 {
7025 const max_int = try dest_ty.maxInt(sema.arena, target);
7026 const max_int_inst = try sema.addConstant(operand_ty, max_int);
7027 const is_in_range = try block.addBinOp(.cmp_lte, operand, max_int_inst);
7028 try sema.addSafetyCheck(block, is_in_range, .cast_truncated_data);
7029 }
7030 }7048 }
7031 return block.addTyOp(.intcast, dest_ty, operand);7049 return block.addTyOp(.intcast, dest_ty, operand);
7032}7050}