Skip to content

Also assume wrap-around discriminants in as MIR building #111579

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Merged
merged 1 commit into from
May 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 58 additions & 43 deletions compiler/rustc_mir_build/src/build/expr/as_rvalue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use rustc_middle::mir::Place;
use rustc_middle::mir::*;
use rustc_middle::thir::*;
use rustc_middle::ty::cast::{mir_cast_kind, CastTy};
use rustc_middle::ty::layout::IntegerExt;
use rustc_middle::ty::{self, Ty, UpvarSubsts};
use rustc_span::Span;

Expand Down Expand Up @@ -225,49 +226,63 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
);
let (op,ty) = (Operand::Move(discr), discr_ty);

if let Abi::Scalar(scalar) = layout.unwrap().abi{
if let Primitive::Int(_, signed) = scalar.primitive() {
let range = scalar.valid_range(&this.tcx);
// FIXME: Handle wraparound cases too.
if range.end >= range.start {
let mut assumer = |range: u128, bin_op: BinOp| {
// We will be overwriting this val if our scalar is signed value
// because sign extension on unsigned types might cause unintended things
let mut range_val =
ConstantKind::from_bits(this.tcx, range, ty::ParamEnv::empty().and(discr_ty));
let bool_ty = this.tcx.types.bool;
if signed {
let scalar_size_extend = scalar.size(&this.tcx).sign_extend(range);
let discr_layout = this.tcx.layout_of(this.param_env.and(discr_ty));
let truncated_val = discr_layout.unwrap().size.truncate(scalar_size_extend);
range_val = ConstantKind::from_bits(
this.tcx,
truncated_val,
ty::ParamEnv::empty().and(discr_ty),
);
}
let lit_op = this.literal_operand(expr.span, range_val);
let is_bin_op = this.temp(bool_ty, expr_span);
this.cfg.push_assign(
block,
source_info,
is_bin_op,
Rvalue::BinaryOp(bin_op, Box::new(((lit_op), (Operand::Copy(discr))))),
);
this.cfg.push(
block,
Statement {
source_info,
kind: StatementKind::Intrinsic(Box::new(NonDivergingIntrinsic::Assume(
Operand::Copy(is_bin_op),
))),
},
)
};
assumer(range.end, BinOp::Ge);
assumer(range.start, BinOp::Le);
}
}
if let Abi::Scalar(scalar) = layout.unwrap().abi
&& !scalar.is_always_valid(&this.tcx)
&& let Primitive::Int(int_width, _signed) = scalar.primitive()
{
let unsigned_ty = int_width.to_ty(this.tcx, false);
let unsigned_place = this.temp(unsigned_ty, expr_span);
this.cfg.push_assign(
block,
source_info,
unsigned_place,
Rvalue::Cast(CastKind::IntToInt, Operand::Copy(discr), unsigned_ty));

let bool_ty = this.tcx.types.bool;
let range = scalar.valid_range(&this.tcx);
let merge_op =
if range.start <= range.end {
BinOp::BitAnd
} else {
BinOp::BitOr
};

let mut comparer = |range: u128, bin_op: BinOp| -> Place<'tcx> {
let range_val =
ConstantKind::from_bits(this.tcx, range, ty::ParamEnv::empty().and(unsigned_ty));
let lit_op = this.literal_operand(expr.span, range_val);
let is_bin_op = this.temp(bool_ty, expr_span);
this.cfg.push_assign(
block,
source_info,
is_bin_op,
Rvalue::BinaryOp(bin_op, Box::new((Operand::Copy(unsigned_place), lit_op))),
);
is_bin_op
};
let assert_place = if range.start == 0 {
comparer(range.end, BinOp::Le)
} else {
let start_place = comparer(range.start, BinOp::Ge);
let end_place = comparer(range.end, BinOp::Le);
let merge_place = this.temp(bool_ty, expr_span);
this.cfg.push_assign(
block,
source_info,
merge_place,
Rvalue::BinaryOp(merge_op, Box::new((Operand::Move(start_place), Operand::Move(end_place)))),
);
merge_place
};
this.cfg.push(
block,
Statement {
source_info,
kind: StatementKind::Intrinsic(Box::new(NonDivergingIntrinsic::Assume(
Operand::Move(assert_place),
))),
},
);
}

(op,ty)
Expand Down
28 changes: 28 additions & 0 deletions tests/assembly/option-nonzero-eq.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// revisions: WIN LIN
// [WIN] only-windows
// [LIN] only-linux
// assembly-output: emit-asm
// compile-flags: --crate-type=lib -O -C llvm-args=-x86-asm-syntax=intel
// only-x86_64
// ignore-sgx
// ignore-debug

use std::cmp::Ordering;

// CHECK-lABEL: ordering_eq:
#[no_mangle]
pub fn ordering_eq(l: Option<Ordering>, r: Option<Ordering>) -> bool {
// Linux (System V): first two arguments are rdi then rsi
// Windows: first two arguments are rcx then rdx
// Both use rax for the return value.

// CHECK-NOT: mov
// CHECK-NOT: test
// CHECK-NOT: cmp

// LIN: cmp dil, sil
// WIN: cmp cl, dl
// CHECK-NEXT: sete al
// CHECK-NEXT: ret
l == r
}
12 changes: 3 additions & 9 deletions tests/codegen/option-nonzero-eq.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ use core::cmp::Ordering;
use core::num::{NonZeroU32, NonZeroI64};
use core::ptr::NonNull;

// See also tests/assembly/option-nonzero-eq.rs, for cases with `assume`s in the
// LLVM and thus don't optimize down clearly here, but do in assembly.

// CHECK-lABEL: @non_zero_eq
#[no_mangle]
pub fn non_zero_eq(l: Option<NonZeroU32>, r: Option<NonZeroU32>) -> bool {
Expand All @@ -33,12 +36,3 @@ pub fn non_null_eq(l: Option<NonNull<u8>>, r: Option<NonNull<u8>>) -> bool {
// CHECK-NEXT: ret i1
l == r
}

// CHECK-lABEL: @ordering_eq
#[no_mangle]
pub fn ordering_eq(l: Option<Ordering>, r: Option<Ordering>) -> bool {
// CHECK: start:
// CHECK-NEXT: icmp eq i8
// CHECK-NEXT: ret i1
l == r
}
9 changes: 4 additions & 5 deletions tests/mir-opt/building/enum_cast.bar.built.after.mir
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,16 @@ fn bar(_1: Bar) -> usize {
let mut _0: usize; // return place in scope 0 at $DIR/enum_cast.rs:+0:21: +0:26
let _2: Bar; // in scope 0 at $DIR/enum_cast.rs:+1:5: +1:8
let mut _3: isize; // in scope 0 at $DIR/enum_cast.rs:+1:5: +1:8
let mut _4: bool; // in scope 0 at $DIR/enum_cast.rs:+1:5: +1:17
let mut _4: u8; // in scope 0 at $DIR/enum_cast.rs:+1:5: +1:17
let mut _5: bool; // in scope 0 at $DIR/enum_cast.rs:+1:5: +1:17

bb0: {
StorageLive(_2); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:8
_2 = move _1; // scope 0 at $DIR/enum_cast.rs:+1:5: +1:8
_3 = discriminant(_2); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:17
_4 = Ge(const 1_isize, _3); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:17
assume(_4); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:17
_5 = Le(const 0_isize, _3); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:17
assume(_5); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:17
_4 = _3 as u8 (IntToInt); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:17
_5 = Le(_4, const 1_u8); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:17
assume(move _5); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:17
_0 = move _3 as usize (IntToInt); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:17
StorageDead(_2); // scope 0 at $DIR/enum_cast.rs:+1:16: +1:17
return; // scope 0 at $DIR/enum_cast.rs:+2:2: +2:2
Expand Down
9 changes: 4 additions & 5 deletions tests/mir-opt/building/enum_cast.boo.built.after.mir
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,16 @@ fn boo(_1: Boo) -> usize {
let mut _0: usize; // return place in scope 0 at $DIR/enum_cast.rs:+0:21: +0:26
let _2: Boo; // in scope 0 at $DIR/enum_cast.rs:+1:5: +1:8
let mut _3: u8; // in scope 0 at $DIR/enum_cast.rs:+1:5: +1:8
let mut _4: bool; // in scope 0 at $DIR/enum_cast.rs:+1:5: +1:17
let mut _4: u8; // in scope 0 at $DIR/enum_cast.rs:+1:5: +1:17
let mut _5: bool; // in scope 0 at $DIR/enum_cast.rs:+1:5: +1:17

bb0: {
StorageLive(_2); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:8
_2 = move _1; // scope 0 at $DIR/enum_cast.rs:+1:5: +1:8
_3 = discriminant(_2); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:17
_4 = Ge(const 1_u8, _3); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:17
assume(_4); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:17
_5 = Le(const 0_u8, _3); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:17
assume(_5); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:17
_4 = _3 as u8 (IntToInt); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:17
_5 = Le(_4, const 1_u8); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:17
assume(move _5); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:17
_0 = move _3 as usize (IntToInt); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:17
StorageDead(_2); // scope 0 at $DIR/enum_cast.rs:+1:16: +1:17
return; // scope 0 at $DIR/enum_cast.rs:+2:2: +2:2
Expand Down
9 changes: 4 additions & 5 deletions tests/mir-opt/building/enum_cast.droppy.built.after.mir
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ fn droppy() -> () {
let _2: Droppy; // in scope 0 at $DIR/enum_cast.rs:+2:13: +2:14
let _4: Droppy; // in scope 0 at $DIR/enum_cast.rs:+5:17: +5:18
let mut _5: isize; // in scope 0 at $DIR/enum_cast.rs:+5:17: +5:18
let mut _6: bool; // in scope 0 at $DIR/enum_cast.rs:+5:17: +5:27
let mut _6: u8; // in scope 0 at $DIR/enum_cast.rs:+5:17: +5:27
let mut _7: bool; // in scope 0 at $DIR/enum_cast.rs:+5:17: +5:27
let _8: Droppy; // in scope 0 at $DIR/enum_cast.rs:+7:9: +7:10
scope 1 {
Expand All @@ -31,10 +31,9 @@ fn droppy() -> () {
StorageLive(_4); // scope 3 at $DIR/enum_cast.rs:+5:17: +5:18
_4 = move _2; // scope 3 at $DIR/enum_cast.rs:+5:17: +5:18
_5 = discriminant(_4); // scope 3 at $DIR/enum_cast.rs:+5:17: +5:27
_6 = Ge(const 2_isize, _5); // scope 3 at $DIR/enum_cast.rs:+5:17: +5:27
assume(_6); // scope 3 at $DIR/enum_cast.rs:+5:17: +5:27
_7 = Le(const 0_isize, _5); // scope 3 at $DIR/enum_cast.rs:+5:17: +5:27
assume(_7); // scope 3 at $DIR/enum_cast.rs:+5:17: +5:27
_6 = _5 as u8 (IntToInt); // scope 3 at $DIR/enum_cast.rs:+5:17: +5:27
_7 = Le(_6, const 2_u8); // scope 3 at $DIR/enum_cast.rs:+5:17: +5:27
assume(move _7); // scope 3 at $DIR/enum_cast.rs:+5:17: +5:27
_3 = move _5 as usize (IntToInt); // scope 3 at $DIR/enum_cast.rs:+5:17: +5:27
drop(_4) -> [return: bb1, unwind: bb4]; // scope 3 at $DIR/enum_cast.rs:+5:26: +5:27
}
Expand Down
22 changes: 22 additions & 0 deletions tests/mir-opt/building/enum_cast.far.built.after.mir
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// MIR for `far` after built

fn far(_1: Far) -> isize {
debug far => _1; // in scope 0 at $DIR/enum_cast.rs:+0:8: +0:11
let mut _0: isize; // return place in scope 0 at $DIR/enum_cast.rs:+0:21: +0:26
let _2: Far; // in scope 0 at $DIR/enum_cast.rs:+1:5: +1:8
let mut _3: i16; // in scope 0 at $DIR/enum_cast.rs:+1:5: +1:8
let mut _4: u16; // in scope 0 at $DIR/enum_cast.rs:+1:5: +1:17
let mut _5: bool; // in scope 0 at $DIR/enum_cast.rs:+1:5: +1:17

bb0: {
StorageLive(_2); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:8
_2 = move _1; // scope 0 at $DIR/enum_cast.rs:+1:5: +1:8
_3 = discriminant(_2); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:17
_4 = _3 as u16 (IntToInt); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:17
_5 = Le(_4, const 1_u16); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:17
assume(move _5); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:17
_0 = move _3 as isize (IntToInt); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:17
StorageDead(_2); // scope 0 at $DIR/enum_cast.rs:+1:16: +1:17
return; // scope 0 at $DIR/enum_cast.rs:+2:2: +2:2
}
}
26 changes: 26 additions & 0 deletions tests/mir-opt/building/enum_cast.offsetty.built.after.mir
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// MIR for `offsetty` after built

fn offsetty(_1: NotStartingAtZero) -> u32 {
debug x => _1; // in scope 0 at $DIR/enum_cast.rs:+0:13: +0:14
let mut _0: u32; // return place in scope 0 at $DIR/enum_cast.rs:+0:38: +0:41
let _2: NotStartingAtZero; // in scope 0 at $DIR/enum_cast.rs:+1:5: +1:6
let mut _3: isize; // in scope 0 at $DIR/enum_cast.rs:+1:5: +1:6
let mut _4: u8; // in scope 0 at $DIR/enum_cast.rs:+1:5: +1:13
let mut _5: bool; // in scope 0 at $DIR/enum_cast.rs:+1:5: +1:13
let mut _6: bool; // in scope 0 at $DIR/enum_cast.rs:+1:5: +1:13
let mut _7: bool; // in scope 0 at $DIR/enum_cast.rs:+1:5: +1:13

bb0: {
StorageLive(_2); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:6
_2 = move _1; // scope 0 at $DIR/enum_cast.rs:+1:5: +1:6
_3 = discriminant(_2); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:13
_4 = _3 as u8 (IntToInt); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:13
_5 = Ge(_4, const 4_u8); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:13
_6 = Le(_4, const 8_u8); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:13
_7 = BitAnd(move _5, move _6); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:13
assume(move _7); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:13
_0 = move _3 as u32 (IntToInt); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:13
StorageDead(_2); // scope 0 at $DIR/enum_cast.rs:+1:12: +1:13
return; // scope 0 at $DIR/enum_cast.rs:+2:2: +2:2
}
}
42 changes: 42 additions & 0 deletions tests/mir-opt/building/enum_cast.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// EMIT_MIR enum_cast.foo.built.after.mir
// EMIT_MIR enum_cast.bar.built.after.mir
// EMIT_MIR enum_cast.boo.built.after.mir
// EMIT_MIR enum_cast.far.built.after.mir

enum Foo {
A
Expand All @@ -15,6 +16,11 @@ enum Boo {
A, B
}

#[repr(i16)]
enum Far {
A, B
}

fn foo(foo: Foo) -> usize {
foo as usize
}
Expand All @@ -27,6 +33,10 @@ fn boo(boo: Boo) -> usize {
boo as usize
}

fn far(far: Far) -> isize {
far as isize
}

// EMIT_MIR enum_cast.droppy.built.after.mir
enum Droppy {
A, B, C
Expand All @@ -46,5 +56,37 @@ fn droppy() {
let z = Droppy::B;
}

#[repr(i16)]
enum SignedAroundZero {
A = -2,
B = 0,
C = 2,
}

#[repr(u16)]
enum UnsignedAroundZero {
A = 65535,
B = 0,
C = 1,
}

// EMIT_MIR enum_cast.signy.built.after.mir
fn signy(x: SignedAroundZero) -> i16 {
x as i16
}

// EMIT_MIR enum_cast.unsigny.built.after.mir
fn unsigny(x: UnsignedAroundZero) -> u16 {
// FIXME: This doesn't get an around-the-end range today, sadly.
x as u16
}

enum NotStartingAtZero { A = 4, B = 6, C = 8 }

// EMIT_MIR enum_cast.offsetty.built.after.mir
fn offsetty(x: NotStartingAtZero) -> u32 {
x as u32
}

fn main() {
}
26 changes: 26 additions & 0 deletions tests/mir-opt/building/enum_cast.signy.built.after.mir
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// MIR for `signy` after built

fn signy(_1: SignedAroundZero) -> i16 {
debug x => _1; // in scope 0 at $DIR/enum_cast.rs:+0:10: +0:11
let mut _0: i16; // return place in scope 0 at $DIR/enum_cast.rs:+0:34: +0:37
let _2: SignedAroundZero; // in scope 0 at $DIR/enum_cast.rs:+1:5: +1:6
let mut _3: i16; // in scope 0 at $DIR/enum_cast.rs:+1:5: +1:6
let mut _4: u16; // in scope 0 at $DIR/enum_cast.rs:+1:5: +1:13
let mut _5: bool; // in scope 0 at $DIR/enum_cast.rs:+1:5: +1:13
let mut _6: bool; // in scope 0 at $DIR/enum_cast.rs:+1:5: +1:13
let mut _7: bool; // in scope 0 at $DIR/enum_cast.rs:+1:5: +1:13

bb0: {
StorageLive(_2); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:6
_2 = move _1; // scope 0 at $DIR/enum_cast.rs:+1:5: +1:6
_3 = discriminant(_2); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:13
_4 = _3 as u16 (IntToInt); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:13
_5 = Ge(_4, const 65534_u16); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:13
_6 = Le(_4, const 2_u16); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:13
_7 = BitOr(move _5, move _6); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:13
assume(move _7); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:13
_0 = move _3 as i16 (IntToInt); // scope 0 at $DIR/enum_cast.rs:+1:5: +1:13
StorageDead(_2); // scope 0 at $DIR/enum_cast.rs:+1:12: +1:13
return; // scope 0 at $DIR/enum_cast.rs:+2:2: +2:2
}
}
17 changes: 17 additions & 0 deletions tests/mir-opt/building/enum_cast.unsigny.built.after.mir
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// MIR for `unsigny` after built

fn unsigny(_1: UnsignedAroundZero) -> u16 {
debug x => _1; // in scope 0 at $DIR/enum_cast.rs:+0:12: +0:13
let mut _0: u16; // return place in scope 0 at $DIR/enum_cast.rs:+0:38: +0:41
let _2: UnsignedAroundZero; // in scope 0 at $DIR/enum_cast.rs:+2:5: +2:6
let mut _3: u16; // in scope 0 at $DIR/enum_cast.rs:+2:5: +2:6

bb0: {
StorageLive(_2); // scope 0 at $DIR/enum_cast.rs:+2:5: +2:6
_2 = move _1; // scope 0 at $DIR/enum_cast.rs:+2:5: +2:6
_3 = discriminant(_2); // scope 0 at $DIR/enum_cast.rs:+2:5: +2:13
_0 = move _3 as u16 (IntToInt); // scope 0 at $DIR/enum_cast.rs:+2:5: +2:13
StorageDead(_2); // scope 0 at $DIR/enum_cast.rs:+2:12: +2:13
return; // scope 0 at $DIR/enum_cast.rs:+3:2: +3:2
}
}