authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-04-26 13:20:19-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-04-26 13:20:19-04:00
log50f1856476038e57f5d2f47c751f608b0b360662
treeb4ced145c76f6fbb2cb5038d0403586ab444d9e3
parent0324975d1d6111b2631aa4c49831afb105f8e33d
parenteac09ac3506cd1bbd7cde92adb6c4a7ef424f717
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #11336 from wsengir/stage2-optimize-intcast

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

2 files changed, 36 insertions(+), 17 deletions(-)

src/Sema.zig+35-17
......@@ -6975,6 +6975,7 @@ fn intCast(
69756975 operand_src: LazySrcLoc,
69766976 runtime_safety: bool,
69776977) CompileError!Air.Inst.Ref {
6978 // TODO: Add support for vectors
69786979 const dest_is_comptime_int = try sema.checkIntType(block, dest_ty_src, dest_ty);
69796980 _ = try sema.checkIntType(block, operand_src, sema.typeOf(operand));
69806981
......@@ -7010,27 +7011,44 @@ fn intCast(
70107011 const wanted_info = dest_ty.intInfo(target);
70117012 const actual_bits = actual_info.bits;
70127013 const wanted_bits = wanted_info.bits;
7013
7014 const actual_value_bits = actual_bits - @boolToInt(actual_info.signedness == .signed);
7015 const wanted_value_bits = wanted_bits - @boolToInt(wanted_info.signedness == .signed);
7016
7017 // range shrinkage
7018 // requirement: int value fits into target type
7019 if (wanted_value_bits < actual_value_bits) {
7020 const dest_max_val = try dest_ty.maxInt(sema.arena, target);
7021 const dest_max = try sema.addConstant(operand_ty, dest_max_val);
7022 const diff = try block.addBinOp(.subwrap, dest_max, operand);
7023
7024 if (actual_info.signedness == .signed) {
7025 // Reinterpret the sign-bit as part of the value. This will make
7026 // negative differences (`operand` > `dest_max`) appear too big.
7027 const unsigned_operand_ty = try Type.Tag.int_unsigned.create(sema.arena, actual_bits);
7028 const diff_unsigned = try block.addBitCast(unsigned_operand_ty, diff);
7029
7030 // If the destination type is signed, then we need to double its
7031 // range to account for negative values.
7032 const dest_range_val = if (wanted_info.signedness == .signed) range_val: {
7033 const range_minus_one = try dest_max_val.shl(Value.one, unsigned_operand_ty, sema.arena, target);
7034 break :range_val try range_minus_one.intAdd(Value.one, unsigned_operand_ty, sema.arena, target);
7035 } else dest_max_val;
7036 const dest_range = try sema.addConstant(unsigned_operand_ty, dest_range_val);
7037
7038 const is_in_range = try block.addBinOp(.cmp_lte, diff_unsigned, dest_range);
7039 try sema.addSafetyCheck(block, is_in_range, .cast_truncated_data);
7040 } else {
7041 const is_in_range = try block.addBinOp(.cmp_lte, diff, dest_max);
7042 try sema.addSafetyCheck(block, is_in_range, .cast_truncated_data);
7043 }
7044 }
7045 // no shrinkage, yes sign loss
70147046 // requirement: signed to unsigned >= 0
7015 if (actual_info.signedness == .signed and
7016 wanted_info.signedness == .unsigned)
7017 {
7018 const zero_inst = try sema.addConstant(sema.typeOf(operand), Value.zero);
7047 else if (actual_info.signedness == .signed and wanted_info.signedness == .unsigned) {
7048 const zero_inst = try sema.addConstant(operand_ty, Value.zero);
70197049 const is_in_range = try block.addBinOp(.cmp_gte, operand, zero_inst);
70207050 try sema.addSafetyCheck(block, is_in_range, .cast_truncated_data);
70217051 }
7022
7023 // requirement: unsigned int value fits into target type
7024 if (actual_bits > wanted_bits or
7025 (actual_bits == wanted_bits and
7026 actual_info.signedness == .unsigned and
7027 wanted_info.signedness == .signed))
7028 {
7029 const max_int = try dest_ty.maxInt(sema.arena, target);
7030 const max_int_inst = try sema.addConstant(operand_ty, max_int);
7031 const is_in_range = try block.addBinOp(.cmp_lte, operand, max_int_inst);
7032 try sema.addSafetyCheck(block, is_in_range, .cast_truncated_data);
7033 }
70347052 }
70357053 return block.addTyOp(.intcast, dest_ty, operand);
70367054}
test/behavior/int128.zig+1
......@@ -46,6 +46,7 @@ test "int128" {
4646 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
4747 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
4848 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
49 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
4950
5051 var buff: i128 = -1;
5152 try expect(buff < 0 and (buff + 1) == 0);