Auto merge of #96862 - oli-obk:enum_cast_mir, r=RalfJung
Change enum->int casts to not go through MIR casts. follow-up to https://github.com/rust-lang/rust/pull/96814 this simplifies all backends and even gives LLVM more information about the return value of `Rvalue::Discriminant`, enabling optimizations in more cases.
This commit is contained in:
commit
53792b9c5c
@ -635,29 +635,6 @@ fn is_fat_ptr<'tcx>(fx: &FunctionCx<'_, '_, 'tcx>, ty: Ty<'tcx>) -> bool {
|
|||||||
let (ptr, _extra) = operand.load_scalar_pair(fx);
|
let (ptr, _extra) = operand.load_scalar_pair(fx);
|
||||||
lval.write_cvalue(fx, CValue::by_val(ptr, dest_layout))
|
lval.write_cvalue(fx, CValue::by_val(ptr, dest_layout))
|
||||||
}
|
}
|
||||||
} else if let ty::Adt(adt_def, _substs) = from_ty.kind() {
|
|
||||||
// enum -> discriminant value
|
|
||||||
assert!(adt_def.is_enum());
|
|
||||||
match to_ty.kind() {
|
|
||||||
ty::Uint(_) | ty::Int(_) => {}
|
|
||||||
_ => unreachable!("cast adt {} -> {}", from_ty, to_ty),
|
|
||||||
}
|
|
||||||
let to_clif_ty = fx.clif_type(to_ty).unwrap();
|
|
||||||
|
|
||||||
let discriminant = crate::discriminant::codegen_get_discriminant(
|
|
||||||
fx,
|
|
||||||
operand,
|
|
||||||
fx.layout_of(operand.layout().ty.discriminant_ty(fx.tcx)),
|
|
||||||
)
|
|
||||||
.load_scalar(fx);
|
|
||||||
|
|
||||||
let res = crate::cast::clif_intcast(
|
|
||||||
fx,
|
|
||||||
discriminant,
|
|
||||||
to_clif_ty,
|
|
||||||
to_ty.is_signed(),
|
|
||||||
);
|
|
||||||
lval.write_cvalue(fx, CValue::by_val(res, dest_layout));
|
|
||||||
} else {
|
} else {
|
||||||
let to_clif_ty = fx.clif_type(to_ty).unwrap();
|
let to_clif_ty = fx.clif_type(to_ty).unwrap();
|
||||||
let from = operand.load_scalar(fx);
|
let from = operand.load_scalar(fx);
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
use std::iter;
|
use std::iter;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
use tracing::debug;
|
use tracing::{debug, instrument};
|
||||||
|
|
||||||
// All Builders must have an llfn associated with them
|
// All Builders must have an llfn associated with them
|
||||||
#[must_use]
|
#[must_use]
|
||||||
@ -464,15 +464,15 @@ fn atomic_load(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument(level = "trace", skip(self))]
|
||||||
fn load_operand(&mut self, place: PlaceRef<'tcx, &'ll Value>) -> OperandRef<'tcx, &'ll Value> {
|
fn load_operand(&mut self, place: PlaceRef<'tcx, &'ll Value>) -> OperandRef<'tcx, &'ll Value> {
|
||||||
debug!("PlaceRef::load: {:?}", place);
|
|
||||||
|
|
||||||
assert_eq!(place.llextra.is_some(), place.layout.is_unsized());
|
assert_eq!(place.llextra.is_some(), place.layout.is_unsized());
|
||||||
|
|
||||||
if place.layout.is_zst() {
|
if place.layout.is_zst() {
|
||||||
return OperandRef::new_zst(self, place.layout);
|
return OperandRef::new_zst(self, place.layout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument(level = "trace", skip(bx))]
|
||||||
fn scalar_load_metadata<'a, 'll, 'tcx>(
|
fn scalar_load_metadata<'a, 'll, 'tcx>(
|
||||||
bx: &mut Builder<'a, 'll, 'tcx>,
|
bx: &mut Builder<'a, 'll, 'tcx>,
|
||||||
load: &'ll Value,
|
load: &'ll Value,
|
||||||
|
@ -1619,7 +1619,7 @@ pub fn LLVMRustBuildIntCast<'a>(
|
|||||||
B: &Builder<'a>,
|
B: &Builder<'a>,
|
||||||
Val: &'a Value,
|
Val: &'a Value,
|
||||||
DestTy: &'a Type,
|
DestTy: &'a Type,
|
||||||
IsSized: bool,
|
IsSigned: bool,
|
||||||
) -> &'a Value;
|
) -> &'a Value;
|
||||||
|
|
||||||
// Comparisons
|
// Comparisons
|
||||||
|
@ -204,6 +204,7 @@ pub fn project_field<Bx: BuilderMethods<'a, 'tcx, Value = V>>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Obtain the actual discriminant of a value.
|
/// Obtain the actual discriminant of a value.
|
||||||
|
#[instrument(level = "trace", skip(bx))]
|
||||||
pub fn codegen_get_discr<Bx: BuilderMethods<'a, 'tcx, Value = V>>(
|
pub fn codegen_get_discr<Bx: BuilderMethods<'a, 'tcx, Value = V>>(
|
||||||
self,
|
self,
|
||||||
bx: &mut Bx,
|
bx: &mut Bx,
|
||||||
@ -420,12 +421,12 @@ pub fn storage_dead<Bx: BuilderMethods<'a, 'tcx, Value = V>>(&self, bx: &mut Bx)
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||||
|
#[instrument(level = "trace", skip(self, bx))]
|
||||||
pub fn codegen_place(
|
pub fn codegen_place(
|
||||||
&mut self,
|
&mut self,
|
||||||
bx: &mut Bx,
|
bx: &mut Bx,
|
||||||
place_ref: mir::PlaceRef<'tcx>,
|
place_ref: mir::PlaceRef<'tcx>,
|
||||||
) -> PlaceRef<'tcx, Bx::Value> {
|
) -> PlaceRef<'tcx, Bx::Value> {
|
||||||
debug!("codegen_place(place_ref={:?})", place_ref);
|
|
||||||
let cx = self.cx;
|
let cx = self.cx;
|
||||||
let tcx = self.cx.tcx();
|
let tcx = self.cx.tcx();
|
||||||
|
|
||||||
|
@ -12,17 +12,15 @@
|
|||||||
use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf};
|
use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf};
|
||||||
use rustc_middle::ty::{self, adjustment::PointerCast, Instance, Ty, TyCtxt};
|
use rustc_middle::ty::{self, adjustment::PointerCast, Instance, Ty, TyCtxt};
|
||||||
use rustc_span::source_map::{Span, DUMMY_SP};
|
use rustc_span::source_map::{Span, DUMMY_SP};
|
||||||
use rustc_target::abi::{Abi, Int, Variants};
|
|
||||||
|
|
||||||
impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||||
|
#[instrument(level = "trace", skip(self, bx))]
|
||||||
pub fn codegen_rvalue(
|
pub fn codegen_rvalue(
|
||||||
&mut self,
|
&mut self,
|
||||||
mut bx: Bx,
|
mut bx: Bx,
|
||||||
dest: PlaceRef<'tcx, Bx::Value>,
|
dest: PlaceRef<'tcx, Bx::Value>,
|
||||||
rvalue: &mir::Rvalue<'tcx>,
|
rvalue: &mir::Rvalue<'tcx>,
|
||||||
) -> Bx {
|
) -> Bx {
|
||||||
debug!("codegen_rvalue(dest.llval={:?}, rvalue={:?})", dest.llval, rvalue);
|
|
||||||
|
|
||||||
match *rvalue {
|
match *rvalue {
|
||||||
mir::Rvalue::Use(ref operand) => {
|
mir::Rvalue::Use(ref operand) => {
|
||||||
let cg_operand = self.codegen_operand(&mut bx, operand);
|
let cg_operand = self.codegen_operand(&mut bx, operand);
|
||||||
@ -285,74 +283,12 @@ pub fn codegen_rvalue_operand(
|
|||||||
CastTy::from_ty(operand.layout.ty).expect("bad input type for cast");
|
CastTy::from_ty(operand.layout.ty).expect("bad input type for cast");
|
||||||
let r_t_out = CastTy::from_ty(cast.ty).expect("bad output type for cast");
|
let r_t_out = CastTy::from_ty(cast.ty).expect("bad output type for cast");
|
||||||
let ll_t_in = bx.cx().immediate_backend_type(operand.layout);
|
let ll_t_in = bx.cx().immediate_backend_type(operand.layout);
|
||||||
match operand.layout.variants {
|
|
||||||
Variants::Single { index } => {
|
|
||||||
if let Some(discr) =
|
|
||||||
operand.layout.ty.discriminant_for_variant(bx.tcx(), index)
|
|
||||||
{
|
|
||||||
let discr_layout = bx.cx().layout_of(discr.ty);
|
|
||||||
let discr_t = bx.cx().immediate_backend_type(discr_layout);
|
|
||||||
let discr_val = bx.cx().const_uint_big(discr_t, discr.val);
|
|
||||||
let discr_val =
|
|
||||||
bx.intcast(discr_val, ll_t_out, discr.ty.is_signed());
|
|
||||||
|
|
||||||
return (
|
|
||||||
bx,
|
|
||||||
OperandRef {
|
|
||||||
val: OperandValue::Immediate(discr_val),
|
|
||||||
layout: cast,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Variants::Multiple { .. } => {}
|
|
||||||
}
|
|
||||||
let llval = operand.immediate();
|
let llval = operand.immediate();
|
||||||
|
|
||||||
let mut signed = false;
|
|
||||||
if let Abi::Scalar(scalar) = operand.layout.abi {
|
|
||||||
if let Int(_, s) = scalar.primitive() {
|
|
||||||
// We use `i1` for bytes that are always `0` or `1`,
|
|
||||||
// e.g., `#[repr(i8)] enum E { A, B }`, but we can't
|
|
||||||
// let LLVM interpret the `i1` as signed, because
|
|
||||||
// then `i1 1` (i.e., E::B) is effectively `i8 -1`.
|
|
||||||
signed = !scalar.is_bool() && s;
|
|
||||||
|
|
||||||
if !scalar.is_always_valid(bx.cx())
|
|
||||||
&& scalar.valid_range(bx.cx()).end
|
|
||||||
>= scalar.valid_range(bx.cx()).start
|
|
||||||
{
|
|
||||||
// We want `table[e as usize ± k]` to not
|
|
||||||
// have bound checks, and this is the most
|
|
||||||
// convenient place to put the `assume`s.
|
|
||||||
if scalar.valid_range(bx.cx()).start > 0 {
|
|
||||||
let enum_value_lower_bound = bx.cx().const_uint_big(
|
|
||||||
ll_t_in,
|
|
||||||
scalar.valid_range(bx.cx()).start,
|
|
||||||
);
|
|
||||||
let cmp_start = bx.icmp(
|
|
||||||
IntPredicate::IntUGE,
|
|
||||||
llval,
|
|
||||||
enum_value_lower_bound,
|
|
||||||
);
|
|
||||||
bx.assume(cmp_start);
|
|
||||||
}
|
|
||||||
|
|
||||||
let enum_value_upper_bound = bx
|
|
||||||
.cx()
|
|
||||||
.const_uint_big(ll_t_in, scalar.valid_range(bx.cx()).end);
|
|
||||||
let cmp_end = bx.icmp(
|
|
||||||
IntPredicate::IntULE,
|
|
||||||
llval,
|
|
||||||
enum_value_upper_bound,
|
|
||||||
);
|
|
||||||
bx.assume(cmp_end);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let newval = match (r_t_in, r_t_out) {
|
let newval = match (r_t_in, r_t_out) {
|
||||||
(CastTy::Int(_), CastTy::Int(_)) => bx.intcast(llval, ll_t_out, signed),
|
(CastTy::Int(i), CastTy::Int(_)) => {
|
||||||
|
bx.intcast(llval, ll_t_out, i.is_signed())
|
||||||
|
}
|
||||||
(CastTy::Float, CastTy::Float) => {
|
(CastTy::Float, CastTy::Float) => {
|
||||||
let srcsz = bx.cx().float_width(ll_t_in);
|
let srcsz = bx.cx().float_width(ll_t_in);
|
||||||
let dstsz = bx.cx().float_width(ll_t_out);
|
let dstsz = bx.cx().float_width(ll_t_out);
|
||||||
@ -364,8 +300,8 @@ pub fn codegen_rvalue_operand(
|
|||||||
llval
|
llval
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(CastTy::Int(_), CastTy::Float) => {
|
(CastTy::Int(i), CastTy::Float) => {
|
||||||
if signed {
|
if i.is_signed() {
|
||||||
bx.sitofp(llval, ll_t_out)
|
bx.sitofp(llval, ll_t_out)
|
||||||
} else {
|
} else {
|
||||||
bx.uitofp(llval, ll_t_out)
|
bx.uitofp(llval, ll_t_out)
|
||||||
@ -374,8 +310,9 @@ pub fn codegen_rvalue_operand(
|
|||||||
(CastTy::Ptr(_) | CastTy::FnPtr, CastTy::Ptr(_)) => {
|
(CastTy::Ptr(_) | CastTy::FnPtr, CastTy::Ptr(_)) => {
|
||||||
bx.pointercast(llval, ll_t_out)
|
bx.pointercast(llval, ll_t_out)
|
||||||
}
|
}
|
||||||
(CastTy::Int(_), CastTy::Ptr(_)) => {
|
(CastTy::Int(i), CastTy::Ptr(_)) => {
|
||||||
let usize_llval = bx.intcast(llval, bx.cx().type_isize(), signed);
|
let usize_llval =
|
||||||
|
bx.intcast(llval, bx.cx().type_isize(), i.is_signed());
|
||||||
bx.inttoptr(usize_llval, ll_t_out)
|
bx.inttoptr(usize_llval, ll_t_out)
|
||||||
}
|
}
|
||||||
(CastTy::Float, CastTy::Int(IntTy::I)) => {
|
(CastTy::Float, CastTy::Int(IntTy::I)) => {
|
||||||
|
@ -6,9 +6,8 @@
|
|||||||
use crate::traits::*;
|
use crate::traits::*;
|
||||||
|
|
||||||
impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||||
|
#[instrument(level = "debug", skip(self, bx))]
|
||||||
pub fn codegen_statement(&mut self, mut bx: Bx, statement: &mir::Statement<'tcx>) -> Bx {
|
pub fn codegen_statement(&mut self, mut bx: Bx, statement: &mir::Statement<'tcx>) -> Bx {
|
||||||
debug!("codegen_statement(statement={:?})", statement);
|
|
||||||
|
|
||||||
self.set_debug_loc(&mut bx, statement.source_info);
|
self.set_debug_loc(&mut bx, statement.source_info);
|
||||||
match statement.kind {
|
match statement.kind {
|
||||||
mir::StatementKind::Assign(box (ref place, ref rvalue)) => {
|
mir::StatementKind::Assign(box (ref place, ref rvalue)) => {
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
use rustc_middle::ty::adjustment::PointerCast;
|
use rustc_middle::ty::adjustment::PointerCast;
|
||||||
use rustc_middle::ty::layout::{IntegerExt, LayoutOf, TyAndLayout};
|
use rustc_middle::ty::layout::{IntegerExt, LayoutOf, TyAndLayout};
|
||||||
use rustc_middle::ty::{self, FloatTy, Ty, TypeAndMut};
|
use rustc_middle::ty::{self, FloatTy, Ty, TypeAndMut};
|
||||||
use rustc_target::abi::{Integer, Variants};
|
use rustc_target::abi::Integer;
|
||||||
use rustc_type_ir::sty::TyKind::*;
|
use rustc_type_ir::sty::TyKind::*;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
@ -128,12 +128,10 @@ pub fn misc_cast(
|
|||||||
Float(FloatTy::F64) => {
|
Float(FloatTy::F64) => {
|
||||||
return Ok(self.cast_from_float(src.to_scalar()?.to_f64()?, cast_ty).into());
|
return Ok(self.cast_from_float(src.to_scalar()?.to_f64()?, cast_ty).into());
|
||||||
}
|
}
|
||||||
// The rest is integer/pointer-"like", including fn ptr casts and casts from enums that
|
// The rest is integer/pointer-"like", including fn ptr casts
|
||||||
// are represented as integers.
|
|
||||||
_ => assert!(
|
_ => assert!(
|
||||||
src.layout.ty.is_bool()
|
src.layout.ty.is_bool()
|
||||||
|| src.layout.ty.is_char()
|
|| src.layout.ty.is_char()
|
||||||
|| src.layout.ty.is_enum()
|
|
||||||
|| src.layout.ty.is_integral()
|
|| src.layout.ty.is_integral()
|
||||||
|| src.layout.ty.is_any_ptr(),
|
|| src.layout.ty.is_any_ptr(),
|
||||||
"Unexpected cast from type {:?}",
|
"Unexpected cast from type {:?}",
|
||||||
@ -143,25 +141,6 @@ pub fn misc_cast(
|
|||||||
|
|
||||||
// # First handle non-scalar source values.
|
// # First handle non-scalar source values.
|
||||||
|
|
||||||
// Handle cast from a ZST enum (0 or 1 variants).
|
|
||||||
match src.layout.variants {
|
|
||||||
Variants::Single { index } => {
|
|
||||||
if src.layout.abi.is_uninhabited() {
|
|
||||||
// This is dead code, because an uninhabited enum is UB to
|
|
||||||
// instantiate.
|
|
||||||
throw_ub!(Unreachable);
|
|
||||||
}
|
|
||||||
if let Some(discr) = src.layout.ty.discriminant_for_variant(*self.tcx, index) {
|
|
||||||
assert!(src.layout.is_zst());
|
|
||||||
let discr_layout = self.layout_of(discr.ty)?;
|
|
||||||
|
|
||||||
let scalar = Scalar::from_uint(discr.val, discr_layout.layout.size());
|
|
||||||
return Ok(self.cast_from_int_like(scalar, discr_layout, cast_ty)?.into());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Variants::Multiple { .. } => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle casting any ptr to raw ptr (might be a fat ptr).
|
// Handle casting any ptr to raw ptr (might be a fat ptr).
|
||||||
if src.layout.ty.is_any_ptr() && cast_ty.is_unsafe_ptr() {
|
if src.layout.ty.is_any_ptr() && cast_ty.is_unsafe_ptr() {
|
||||||
let dest_layout = self.layout_of(cast_ty)?;
|
let dest_layout = self.layout_of(cast_ty)?;
|
||||||
|
@ -7,9 +7,9 @@
|
|||||||
use rustc_middle::mir::visit::NonUseContext::VarDebugInfo;
|
use rustc_middle::mir::visit::NonUseContext::VarDebugInfo;
|
||||||
use rustc_middle::mir::visit::{PlaceContext, Visitor};
|
use rustc_middle::mir::visit::{PlaceContext, Visitor};
|
||||||
use rustc_middle::mir::{
|
use rustc_middle::mir::{
|
||||||
traversal, AggregateKind, BasicBlock, BinOp, Body, BorrowKind, Local, Location, MirPass,
|
traversal, AggregateKind, BasicBlock, BinOp, Body, BorrowKind, CastKind, Local, Location,
|
||||||
MirPhase, Operand, Place, PlaceElem, PlaceRef, ProjectionElem, Rvalue, SourceScope, Statement,
|
MirPass, MirPhase, Operand, Place, PlaceElem, PlaceRef, ProjectionElem, Rvalue, SourceScope,
|
||||||
StatementKind, Terminator, TerminatorKind, UnOp, START_BLOCK,
|
Statement, StatementKind, Terminator, TerminatorKind, UnOp, START_BLOCK,
|
||||||
};
|
};
|
||||||
use rustc_middle::ty::fold::BottomUpFolder;
|
use rustc_middle::ty::fold::BottomUpFolder;
|
||||||
use rustc_middle::ty::{self, InstanceDef, ParamEnv, Ty, TyCtxt, TypeFoldable};
|
use rustc_middle::ty::{self, InstanceDef, ParamEnv, Ty, TyCtxt, TypeFoldable};
|
||||||
@ -361,6 +361,7 @@ macro_rules! check_kinds {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Rvalue::Ref(..) => {}
|
||||||
Rvalue::Len(p) => {
|
Rvalue::Len(p) => {
|
||||||
let pty = p.ty(&self.body.local_decls, self.tcx).ty;
|
let pty = p.ty(&self.body.local_decls, self.tcx).ty;
|
||||||
check_kinds!(
|
check_kinds!(
|
||||||
@ -503,7 +504,30 @@ macro_rules! check_kinds {
|
|||||||
let a = operand.ty(&self.body.local_decls, self.tcx);
|
let a = operand.ty(&self.body.local_decls, self.tcx);
|
||||||
check_kinds!(a, "Cannot shallow init type {:?}", ty::RawPtr(..));
|
check_kinds!(a, "Cannot shallow init type {:?}", ty::RawPtr(..));
|
||||||
}
|
}
|
||||||
_ => {}
|
Rvalue::Cast(kind, operand, target_type) => {
|
||||||
|
match kind {
|
||||||
|
CastKind::Misc => {
|
||||||
|
let op_ty = operand.ty(self.body, self.tcx);
|
||||||
|
if op_ty.is_enum() {
|
||||||
|
self.fail(
|
||||||
|
location,
|
||||||
|
format!(
|
||||||
|
"enum -> int casts should go through `Rvalue::Discriminant`: {operand:?}:{op_ty} as {target_type}",
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Nothing to check here
|
||||||
|
CastKind::PointerFromExposedAddress
|
||||||
|
| CastKind::PointerExposeAddress
|
||||||
|
| CastKind::Pointer(_) => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Rvalue::Repeat(_, _)
|
||||||
|
| Rvalue::ThreadLocalRef(_)
|
||||||
|
| Rvalue::AddressOf(_, _)
|
||||||
|
| Rvalue::NullaryOp(_, _)
|
||||||
|
| Rvalue::Discriminant(_) => {}
|
||||||
}
|
}
|
||||||
self.super_rvalue(rvalue, location);
|
self.super_rvalue(rvalue, location);
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,12 @@ pub enum IntTy {
|
|||||||
Char,
|
Char,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl IntTy {
|
||||||
|
pub fn is_signed(self) -> bool {
|
||||||
|
matches!(self, Self::I)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Valid types for the result of a non-coercion cast
|
// Valid types for the result of a non-coercion cast
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
pub enum CastTy<'tcx> {
|
pub enum CastTy<'tcx> {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
//! See docs in `build/expr/mod.rs`.
|
//! See docs in `build/expr/mod.rs`.
|
||||||
|
|
||||||
use rustc_index::vec::Idx;
|
use rustc_index::vec::Idx;
|
||||||
|
use rustc_middle::ty::util::IntTypeExt;
|
||||||
|
|
||||||
use crate::build::expr::as_place::PlaceBase;
|
use crate::build::expr::as_place::PlaceBase;
|
||||||
use crate::build::expr::category::{Category, RvalueFunc};
|
use crate::build::expr::category::{Category, RvalueFunc};
|
||||||
@ -190,7 +191,30 @@ pub(crate) fn as_rvalue(
|
|||||||
}
|
}
|
||||||
ExprKind::Cast { source } => {
|
ExprKind::Cast { source } => {
|
||||||
let source = &this.thir[source];
|
let source = &this.thir[source];
|
||||||
let from_ty = CastTy::from_ty(source.ty);
|
|
||||||
|
// Casting an enum to an integer is equivalent to computing the discriminant and casting the
|
||||||
|
// discriminant. Previously every backend had to repeat the logic for this operation. Now we
|
||||||
|
// create all the steps directly in MIR with operations all backends need to support anyway.
|
||||||
|
let (source, ty) = if let ty::Adt(adt_def, ..) = source.ty.kind() && adt_def.is_enum() {
|
||||||
|
let discr_ty = adt_def.repr().discr_type().to_ty(this.tcx);
|
||||||
|
let place = unpack!(block = this.as_place(block, source));
|
||||||
|
let discr = this.temp(discr_ty, source.span);
|
||||||
|
this.cfg.push_assign(
|
||||||
|
block,
|
||||||
|
source_info,
|
||||||
|
discr,
|
||||||
|
Rvalue::Discriminant(place),
|
||||||
|
);
|
||||||
|
|
||||||
|
(Operand::Move(discr), discr_ty)
|
||||||
|
} else {
|
||||||
|
let ty = source.ty;
|
||||||
|
let source = unpack!(
|
||||||
|
block = this.as_operand(block, scope, source, None, NeedsTemporary::No)
|
||||||
|
);
|
||||||
|
(source, ty)
|
||||||
|
};
|
||||||
|
let from_ty = CastTy::from_ty(ty);
|
||||||
let cast_ty = CastTy::from_ty(expr.ty);
|
let cast_ty = CastTy::from_ty(expr.ty);
|
||||||
let cast_kind = match (from_ty, cast_ty) {
|
let cast_kind = match (from_ty, cast_ty) {
|
||||||
(Some(CastTy::Ptr(_) | CastTy::FnPtr), Some(CastTy::Int(_))) => {
|
(Some(CastTy::Ptr(_) | CastTy::FnPtr), Some(CastTy::Int(_))) => {
|
||||||
@ -201,9 +225,6 @@ pub(crate) fn as_rvalue(
|
|||||||
}
|
}
|
||||||
(_, _) => CastKind::Misc,
|
(_, _) => CastKind::Misc,
|
||||||
};
|
};
|
||||||
let source = unpack!(
|
|
||||||
block = this.as_operand(block, scope, source, None, NeedsTemporary::No)
|
|
||||||
);
|
|
||||||
block.and(Rvalue::Cast(cast_kind, source, expr.ty))
|
block.and(Rvalue::Cast(cast_kind, source, expr.ty))
|
||||||
}
|
}
|
||||||
ExprKind::Pointer { cast, source } => {
|
ExprKind::Pointer { cast, source } => {
|
||||||
|
@ -32,13 +32,14 @@ pub(crate) fn mirror_exprs(&mut self, exprs: &'tcx [hir::Expr<'tcx>]) -> Box<[Ex
|
|||||||
exprs.iter().map(|expr| self.mirror_expr_inner(expr)).collect()
|
exprs.iter().map(|expr| self.mirror_expr_inner(expr)).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument(level = "trace", skip(self, hir_expr))]
|
||||||
pub(super) fn mirror_expr_inner(&mut self, hir_expr: &'tcx hir::Expr<'tcx>) -> ExprId {
|
pub(super) fn mirror_expr_inner(&mut self, hir_expr: &'tcx hir::Expr<'tcx>) -> ExprId {
|
||||||
let temp_lifetime =
|
let temp_lifetime =
|
||||||
self.rvalue_scopes.temporary_scope(self.region_scope_tree, hir_expr.hir_id.local_id);
|
self.rvalue_scopes.temporary_scope(self.region_scope_tree, hir_expr.hir_id.local_id);
|
||||||
let expr_scope =
|
let expr_scope =
|
||||||
region::Scope { id: hir_expr.hir_id.local_id, data: region::ScopeData::Node };
|
region::Scope { id: hir_expr.hir_id.local_id, data: region::ScopeData::Node };
|
||||||
|
|
||||||
debug!("Expr::make_mirror(): id={}, span={:?}", hir_expr.hir_id, hir_expr.span);
|
trace!(?hir_expr.hir_id, ?hir_expr.span);
|
||||||
|
|
||||||
let mut expr = self.make_mirror_unadjusted(hir_expr);
|
let mut expr = self.make_mirror_unadjusted(hir_expr);
|
||||||
|
|
||||||
@ -49,7 +50,7 @@ pub(super) fn mirror_expr_inner(&mut self, hir_expr: &'tcx hir::Expr<'tcx>) -> E
|
|||||||
|
|
||||||
// Now apply adjustments, if any.
|
// Now apply adjustments, if any.
|
||||||
for adjustment in self.typeck_results.expr_adjustments(hir_expr) {
|
for adjustment in self.typeck_results.expr_adjustments(hir_expr) {
|
||||||
debug!("make_mirror: expr={:?} applying adjustment={:?}", expr, adjustment);
|
trace!(?expr, ?adjustment);
|
||||||
let span = expr.span;
|
let span = expr.span;
|
||||||
expr =
|
expr =
|
||||||
self.apply_adjustment(hir_expr, expr, adjustment, adjustment_span.unwrap_or(span));
|
self.apply_adjustment(hir_expr, expr, adjustment, adjustment_span.unwrap_or(span));
|
||||||
|
@ -12,13 +12,15 @@ pub enum Bar {
|
|||||||
// CHECK-LABEL: @lookup_inc
|
// CHECK-LABEL: @lookup_inc
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub fn lookup_inc(buf: &[u8; 5], f: Bar) -> u8 {
|
pub fn lookup_inc(buf: &[u8; 5], f: Bar) -> u8 {
|
||||||
// CHECK-NOT: panic_bounds_check
|
// FIXME: panic check can be removed by adding the assumes back after https://github.com/rust-lang/rust/pull/98332
|
||||||
|
// CHECK: panic_bounds_check
|
||||||
buf[f as usize + 1]
|
buf[f as usize + 1]
|
||||||
}
|
}
|
||||||
|
|
||||||
// CHECK-LABEL: @lookup_dec
|
// CHECK-LABEL: @lookup_dec
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub fn lookup_dec(buf: &[u8; 5], f: Bar) -> u8 {
|
pub fn lookup_dec(buf: &[u8; 5], f: Bar) -> u8 {
|
||||||
// CHECK-NOT: panic_bounds_check
|
// FIXME: panic check can be removed by adding the assumes back after https://github.com/rust-lang/rust/pull/98332
|
||||||
|
// CHECK: panic_bounds_check
|
||||||
buf[f as usize - 1]
|
buf[f as usize - 1]
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ pub enum Exception {
|
|||||||
// CHECK-LABEL: @access
|
// CHECK-LABEL: @access
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub fn access(array: &[usize; 12], exc: Exception) -> usize {
|
pub fn access(array: &[usize; 12], exc: Exception) -> usize {
|
||||||
// CHECK-NOT: panic_bounds_check
|
// FIXME: panic check can be removed by adding the assumes back after https://github.com/rust-lang/rust/pull/98332
|
||||||
|
// CHECK: panic_bounds_check
|
||||||
array[(exc as u8 - 4) as usize]
|
array[(exc as u8 - 4) as usize]
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// compile-flags: -O
|
// compile-flags: -C opt-level=0
|
||||||
|
|
||||||
#![crate_type = "lib"]
|
#![crate_type = "lib"]
|
||||||
|
|
||||||
@ -9,7 +9,10 @@ pub enum E {
|
|||||||
|
|
||||||
// CHECK-LABEL: @index
|
// CHECK-LABEL: @index
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub fn index(x: &[u32; 3], ind: E) -> u32{
|
pub fn index(x: &[u32; 3], ind: E) -> u32 {
|
||||||
// CHECK-NOT: panic_bounds_check
|
// Canary: we should be able to optimize out the bounds check, but we need
|
||||||
|
// to track the range of the discriminant result in order to be able to do that.
|
||||||
|
// oli-obk tried to add that, but that caused miscompilations all over the place.
|
||||||
|
// CHECK: panic_bounds_check
|
||||||
x[ind as usize]
|
x[ind as usize]
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@ pub enum Bar {
|
|||||||
// CHECK-LABEL: @lookup_unmodified
|
// CHECK-LABEL: @lookup_unmodified
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub fn lookup_unmodified(buf: &[u8; 5], f: Bar) -> u8 {
|
pub fn lookup_unmodified(buf: &[u8; 5], f: Bar) -> u8 {
|
||||||
// CHECK-NOT: panic_bounds_check
|
// FIXME: panic check can be removed by adding the assumes back after https://github.com/rust-lang/rust/pull/98332
|
||||||
|
// CHECK: panic_bounds_check
|
||||||
buf[f as usize]
|
buf[f as usize]
|
||||||
}
|
}
|
||||||
|
13
src/test/mir-opt/enum_cast.bar.mir_map.0.mir
Normal file
13
src/test/mir-opt/enum_cast.bar.mir_map.0.mir
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
// MIR for `bar` 0 mir_map
|
||||||
|
|
||||||
|
fn bar(_1: Bar) -> usize {
|
||||||
|
debug bar => _1; // in scope 0 at $DIR/enum_cast.rs:22:8: 22:11
|
||||||
|
let mut _0: usize; // return place in scope 0 at $DIR/enum_cast.rs:22:21: 22:26
|
||||||
|
let mut _2: isize; // in scope 0 at $DIR/enum_cast.rs:23:5: 23:8
|
||||||
|
|
||||||
|
bb0: {
|
||||||
|
_2 = discriminant(_1); // scope 0 at $DIR/enum_cast.rs:23:5: 23:17
|
||||||
|
_0 = move _2 as usize (Misc); // scope 0 at $DIR/enum_cast.rs:23:5: 23:17
|
||||||
|
return; // scope 0 at $DIR/enum_cast.rs:24:2: 24:2
|
||||||
|
}
|
||||||
|
}
|
13
src/test/mir-opt/enum_cast.boo.mir_map.0.mir
Normal file
13
src/test/mir-opt/enum_cast.boo.mir_map.0.mir
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
// MIR for `boo` 0 mir_map
|
||||||
|
|
||||||
|
fn boo(_1: Boo) -> usize {
|
||||||
|
debug boo => _1; // in scope 0 at $DIR/enum_cast.rs:26:8: 26:11
|
||||||
|
let mut _0: usize; // return place in scope 0 at $DIR/enum_cast.rs:26:21: 26:26
|
||||||
|
let mut _2: u8; // in scope 0 at $DIR/enum_cast.rs:27:5: 27:8
|
||||||
|
|
||||||
|
bb0: {
|
||||||
|
_2 = discriminant(_1); // scope 0 at $DIR/enum_cast.rs:27:5: 27:17
|
||||||
|
_0 = move _2 as usize (Misc); // scope 0 at $DIR/enum_cast.rs:27:5: 27:17
|
||||||
|
return; // scope 0 at $DIR/enum_cast.rs:28:2: 28:2
|
||||||
|
}
|
||||||
|
}
|
54
src/test/mir-opt/enum_cast.droppy.mir_map.0.mir
Normal file
54
src/test/mir-opt/enum_cast.droppy.mir_map.0.mir
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
// MIR for `droppy` 0 mir_map
|
||||||
|
|
||||||
|
fn droppy() -> () {
|
||||||
|
let mut _0: (); // return place in scope 0 at $DIR/enum_cast.rs:39:13: 39:13
|
||||||
|
let _1: (); // in scope 0 at $DIR/enum_cast.rs:40:5: 45:6
|
||||||
|
let _2: Droppy; // in scope 0 at $DIR/enum_cast.rs:41:13: 41:14
|
||||||
|
let mut _4: isize; // in scope 0 at $DIR/enum_cast.rs:44:17: 44:18
|
||||||
|
let _5: Droppy; // in scope 0 at $DIR/enum_cast.rs:46:9: 46:10
|
||||||
|
scope 1 {
|
||||||
|
debug x => _2; // in scope 1 at $DIR/enum_cast.rs:41:13: 41:14
|
||||||
|
scope 2 {
|
||||||
|
debug y => _3; // in scope 2 at $DIR/enum_cast.rs:44:13: 44:14
|
||||||
|
}
|
||||||
|
scope 3 {
|
||||||
|
let _3: usize; // in scope 3 at $DIR/enum_cast.rs:44:13: 44:14
|
||||||
|
}
|
||||||
|
}
|
||||||
|
scope 4 {
|
||||||
|
debug z => _5; // in scope 4 at $DIR/enum_cast.rs:46:9: 46:10
|
||||||
|
}
|
||||||
|
|
||||||
|
bb0: {
|
||||||
|
StorageLive(_1); // scope 0 at $DIR/enum_cast.rs:40:5: 45:6
|
||||||
|
StorageLive(_2); // scope 0 at $DIR/enum_cast.rs:41:13: 41:14
|
||||||
|
_2 = Droppy::C; // scope 0 at $DIR/enum_cast.rs:41:17: 41:26
|
||||||
|
FakeRead(ForLet(None), _2); // scope 0 at $DIR/enum_cast.rs:41:13: 41:14
|
||||||
|
StorageLive(_3); // scope 3 at $DIR/enum_cast.rs:44:13: 44:14
|
||||||
|
_4 = discriminant(_2); // scope 3 at $DIR/enum_cast.rs:44:17: 44:27
|
||||||
|
_3 = move _4 as usize (Misc); // scope 3 at $DIR/enum_cast.rs:44:17: 44:27
|
||||||
|
FakeRead(ForLet(None), _3); // scope 3 at $DIR/enum_cast.rs:44:13: 44:14
|
||||||
|
_1 = const (); // scope 0 at $DIR/enum_cast.rs:40:5: 45:6
|
||||||
|
StorageDead(_3); // scope 1 at $DIR/enum_cast.rs:45:5: 45:6
|
||||||
|
drop(_2) -> [return: bb1, unwind: bb3]; // scope 0 at $DIR/enum_cast.rs:45:5: 45:6
|
||||||
|
}
|
||||||
|
|
||||||
|
bb1: {
|
||||||
|
StorageDead(_2); // scope 0 at $DIR/enum_cast.rs:45:5: 45:6
|
||||||
|
StorageDead(_1); // scope 0 at $DIR/enum_cast.rs:45:5: 45:6
|
||||||
|
StorageLive(_5); // scope 0 at $DIR/enum_cast.rs:46:9: 46:10
|
||||||
|
_5 = Droppy::B; // scope 0 at $DIR/enum_cast.rs:46:13: 46:22
|
||||||
|
FakeRead(ForLet(None), _5); // scope 0 at $DIR/enum_cast.rs:46:9: 46:10
|
||||||
|
_0 = const (); // scope 0 at $DIR/enum_cast.rs:39:13: 47:2
|
||||||
|
drop(_5) -> [return: bb2, unwind: bb3]; // scope 0 at $DIR/enum_cast.rs:47:1: 47:2
|
||||||
|
}
|
||||||
|
|
||||||
|
bb2: {
|
||||||
|
StorageDead(_5); // scope 0 at $DIR/enum_cast.rs:47:1: 47:2
|
||||||
|
return; // scope 0 at $DIR/enum_cast.rs:47:2: 47:2
|
||||||
|
}
|
||||||
|
|
||||||
|
bb3 (cleanup): {
|
||||||
|
resume; // scope 0 at $DIR/enum_cast.rs:39:1: 47:2
|
||||||
|
}
|
||||||
|
}
|
13
src/test/mir-opt/enum_cast.foo.mir_map.0.mir
Normal file
13
src/test/mir-opt/enum_cast.foo.mir_map.0.mir
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
// MIR for `foo` 0 mir_map
|
||||||
|
|
||||||
|
fn foo(_1: Foo) -> usize {
|
||||||
|
debug foo => _1; // in scope 0 at $DIR/enum_cast.rs:18:8: 18:11
|
||||||
|
let mut _0: usize; // return place in scope 0 at $DIR/enum_cast.rs:18:21: 18:26
|
||||||
|
let mut _2: isize; // in scope 0 at $DIR/enum_cast.rs:19:5: 19:8
|
||||||
|
|
||||||
|
bb0: {
|
||||||
|
_2 = discriminant(_1); // scope 0 at $DIR/enum_cast.rs:19:5: 19:17
|
||||||
|
_0 = move _2 as usize (Misc); // scope 0 at $DIR/enum_cast.rs:19:5: 19:17
|
||||||
|
return; // scope 0 at $DIR/enum_cast.rs:20:2: 20:2
|
||||||
|
}
|
||||||
|
}
|
50
src/test/mir-opt/enum_cast.rs
Normal file
50
src/test/mir-opt/enum_cast.rs
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
// EMIT_MIR enum_cast.foo.mir_map.0.mir
|
||||||
|
// EMIT_MIR enum_cast.bar.mir_map.0.mir
|
||||||
|
// EMIT_MIR enum_cast.boo.mir_map.0.mir
|
||||||
|
|
||||||
|
enum Foo {
|
||||||
|
A
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Bar {
|
||||||
|
A, B
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(u8)]
|
||||||
|
enum Boo {
|
||||||
|
A, B
|
||||||
|
}
|
||||||
|
|
||||||
|
fn foo(foo: Foo) -> usize {
|
||||||
|
foo as usize
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bar(bar: Bar) -> usize {
|
||||||
|
bar as usize
|
||||||
|
}
|
||||||
|
|
||||||
|
fn boo(boo: Boo) -> usize {
|
||||||
|
boo as usize
|
||||||
|
}
|
||||||
|
|
||||||
|
// EMIT_MIR enum_cast.droppy.mir_map.0.mir
|
||||||
|
enum Droppy {
|
||||||
|
A, B, C
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Droppy {
|
||||||
|
fn drop(&mut self) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn droppy() {
|
||||||
|
{
|
||||||
|
let x = Droppy::C;
|
||||||
|
// remove this entire test once `cenum_impl_drop_cast` becomes a hard error
|
||||||
|
#[allow(cenum_impl_drop_cast)]
|
||||||
|
let y = x as usize;
|
||||||
|
}
|
||||||
|
let z = Droppy::B;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
}
|
@ -30,5 +30,5 @@ fn main() {
|
|||||||
assert_eq!(e as u32, 2);
|
assert_eq!(e as u32, 2);
|
||||||
assert_eq!(FLAG.load(Ordering::SeqCst), 0);
|
assert_eq!(FLAG.load(Ordering::SeqCst), 0);
|
||||||
}
|
}
|
||||||
assert_eq!(FLAG.load(Ordering::SeqCst), 0);
|
assert_eq!(FLAG.load(Ordering::SeqCst), 1);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user