Skip to content

Commit 3336088

Browse files
committed
partially inline eval_rvalue_into_place for const prop lint
1 parent 5dcf303 commit 3336088

File tree

2 files changed

+187
-8
lines changed

2 files changed

+187
-8
lines changed

compiler/rustc_const_eval/src/util/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ pub use self::type_name::type_name;
1414
/// Classify whether an operator is "left-homogeneous", i.e., the LHS has the
1515
/// same type as the result.
1616
#[inline]
17-
pub(crate) fn binop_left_homogeneous(op: mir::BinOp) -> bool {
17+
pub fn binop_left_homogeneous(op: mir::BinOp) -> bool {
1818
use rustc_middle::mir::BinOp::*;
1919
match op {
2020
Add | AddUnchecked | Sub | SubUnchecked | Mul | MulUnchecked | Div | Rem | BitXor
@@ -26,7 +26,7 @@ pub(crate) fn binop_left_homogeneous(op: mir::BinOp) -> bool {
2626
/// Classify whether an operator is "right-homogeneous", i.e., the RHS has the
2727
/// same type as the LHS.
2828
#[inline]
29-
pub(crate) fn binop_right_homogeneous(op: mir::BinOp) -> bool {
29+
pub fn binop_right_homogeneous(op: mir::BinOp) -> bool {
3030
use rustc_middle::mir::BinOp::*;
3131
match op {
3232
Add | AddUnchecked | Sub | SubUnchecked | Mul | MulUnchecked | Div | Rem | BitXor

compiler/rustc_mir_transform/src/const_prop_lint.rs

Lines changed: 185 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use std::fmt::Debug;
55

66
use either::Left;
77

8-
use rustc_const_eval::interpret::Immediate;
8+
use rustc_const_eval::interpret::{ImmTy, Immediate, Projectable};
99
use rustc_const_eval::interpret::{
1010
InterpCx, InterpResult, MemoryKind, OpTy, Scalar, StackPopCleanup,
1111
};
@@ -21,7 +21,7 @@ use rustc_middle::ty::{
2121
self, ConstInt, Instance, ParamEnv, ScalarInt, Ty, TyCtxt, TypeVisitableExt,
2222
};
2323
use rustc_span::Span;
24-
use rustc_target::abi::{HasDataLayout, Size, TargetDataLayout};
24+
use rustc_target::abi::{self, Abi, HasDataLayout, Size, TargetDataLayout};
2525

2626
use crate::const_prop::CanConstProp;
2727
use crate::const_prop::ConstPropMachine;
@@ -540,6 +540,188 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
540540
)
541541
}
542542
}
543+
544+
#[instrument(level = "trace", skip(self), ret)]
545+
fn eval_rvalue(
546+
&mut self,
547+
rvalue: &Rvalue<'tcx>,
548+
location: Location,
549+
dest: &Place<'tcx>,
550+
) -> Option<()> {
551+
if !dest.projection.is_empty() {
552+
return None;
553+
}
554+
use rustc_middle::mir::Rvalue::*;
555+
let dest = self.use_ecx(location, |this| this.ecx.eval_place(*dest))?;
556+
trace!(?dest);
557+
558+
let val = match *rvalue {
559+
ThreadLocalRef(_) => return None,
560+
561+
Use(ref operand) => self.eval_operand(operand, location, Some(dest.layout))?,
562+
563+
CopyForDeref(place) => self.eval_place(place, location, Some(dest.layout))?,
564+
565+
BinaryOp(bin_op, box (ref left, ref right)) => {
566+
let layout =
567+
rustc_const_eval::util::binop_left_homogeneous(bin_op).then_some(dest.layout);
568+
let left = self.eval_operand(left, location, layout)?;
569+
let left = self.use_ecx(location, |this| this.ecx.read_immediate(&left))?;
570+
571+
let layout =
572+
rustc_const_eval::util::binop_right_homogeneous(bin_op).then_some(left.layout);
573+
let right = self.eval_operand(right, location, layout)?;
574+
let right = self.use_ecx(location, |this| this.ecx.read_immediate(&right))?;
575+
576+
let val = self
577+
.use_ecx(location, |this| this.ecx.wrapping_binary_op(bin_op, &left, &right))?;
578+
val.into()
579+
}
580+
581+
CheckedBinaryOp(bin_op, box (ref left, ref right)) => {
582+
let left = self.eval_operand(left, location, None)?;
583+
let left = self.use_ecx(location, |this| this.ecx.read_immediate(&left))?;
584+
585+
let layout =
586+
rustc_const_eval::util::binop_right_homogeneous(bin_op).then_some(left.layout);
587+
let right = self.eval_operand(right, location, layout)?;
588+
let right = self.use_ecx(location, |this| this.ecx.read_immediate(&right))?;
589+
590+
let (val, overflowed) = self.use_ecx(location, |this| {
591+
this.ecx.overflowing_binary_op(bin_op, &left, &right)
592+
})?;
593+
let tuple = Ty::new_tup_from_iter(
594+
self.tcx,
595+
[val.layout.ty, self.tcx.types.bool].into_iter(),
596+
);
597+
let tuple = self.ecx.layout_of(tuple).ok()?;
598+
let val =
599+
ImmTy::from_scalar_pair(val.to_scalar(), Scalar::from_bool(overflowed), tuple);
600+
val.into()
601+
}
602+
603+
UnaryOp(un_op, ref operand) => {
604+
let operand = self.eval_operand(operand, location, Some(dest.layout))?;
605+
let val = self.use_ecx(location, |this| this.ecx.read_immediate(&operand))?;
606+
607+
let val = self.use_ecx(location, |this| this.ecx.wrapping_unary_op(un_op, &val))?;
608+
val.into()
609+
}
610+
611+
Aggregate(ref kind, ref fields) => {
612+
trace!(?kind);
613+
trace!(?dest.layout);
614+
if dest.layout.is_zst() {
615+
ImmTy::uninit(dest.layout).into()
616+
} else if let Abi::Scalar(abi::Scalar::Initialized { .. }) = dest.layout.abi {
617+
let fields = fields
618+
.iter()
619+
.map(|field| self.eval_operand(field, location, None))
620+
.collect::<Option<Vec<_>>>()?;
621+
trace!(?fields);
622+
let mut field =
623+
fields.into_iter().find(|field| field.layout.abi.is_scalar())?;
624+
field.layout = dest.layout;
625+
field
626+
} else if let Abi::ScalarPair(
627+
abi::Scalar::Initialized { .. },
628+
abi::Scalar::Initialized { .. },
629+
) = dest.layout.abi
630+
{
631+
let fields = fields
632+
.iter()
633+
.map(|field| self.eval_operand(field, location, None))
634+
.collect::<Option<Vec<_>>>()?;
635+
trace!(?fields);
636+
let pair =
637+
fields.iter().find(|field| matches!(field.layout.abi, Abi::ScalarPair(..)));
638+
if let Some(pair) = pair {
639+
let mut pair = pair.clone();
640+
pair.layout = dest.layout;
641+
pair
642+
} else {
643+
// TODO: build a pair from two scalars
644+
return None;
645+
}
646+
} else {
647+
return None;
648+
}
649+
}
650+
651+
Repeat(ref op, n) => {
652+
trace!(?op, ?n);
653+
return None;
654+
}
655+
656+
Len(place) => {
657+
let src = self.eval_place(place, location, None)?;
658+
let len = src.len(&self.ecx).ok()?;
659+
ImmTy::from_scalar(Scalar::from_target_usize(len, self), dest.layout).into()
660+
}
661+
662+
Ref(..) | AddressOf(..) => return None,
663+
664+
NullaryOp(ref null_op, ty) => {
665+
let layout = self.use_ecx(location, |this| this.ecx.layout_of(ty))?;
666+
let val = match null_op {
667+
NullOp::SizeOf => layout.size.bytes(),
668+
NullOp::AlignOf => layout.align.abi.bytes(),
669+
NullOp::OffsetOf(fields) => {
670+
layout.offset_of_subfield(self, fields.iter()).bytes()
671+
}
672+
};
673+
ImmTy::from_scalar(Scalar::from_target_usize(val, self), dest.layout).into()
674+
}
675+
676+
ShallowInitBox(..) => return None,
677+
678+
Cast(ref kind, ref value, to) => match kind {
679+
CastKind::IntToInt | CastKind::IntToFloat => {
680+
let value = self.eval_operand(value, location, None)?;
681+
let value = self.ecx.read_immediate(&value).ok()?;
682+
let to = self.ecx.layout_of(to).ok()?;
683+
let res = self.ecx.int_to_int_or_float(&value, to).ok()?;
684+
res.into()
685+
}
686+
CastKind::FloatToFloat | CastKind::FloatToInt => {
687+
let value = self.eval_operand(value, location, None)?;
688+
let value = self.ecx.read_immediate(&value).ok()?;
689+
let to = self.ecx.layout_of(to).ok()?;
690+
let res = self.ecx.float_to_float_or_int(&value, to).ok()?;
691+
res.into()
692+
}
693+
CastKind::Transmute => {
694+
let value = self.eval_operand(value, location, None)?;
695+
let to = self.ecx.layout_of(to).ok()?;
696+
// `offset` for immediates only supports scalar/scalar-pair ABIs,
697+
// so bail out if the target is not one.
698+
if value.as_mplace_or_imm().is_right() {
699+
match (value.layout.abi, to.abi) {
700+
(Abi::Scalar(..), Abi::Scalar(..)) => {}
701+
(Abi::ScalarPair(..), Abi::ScalarPair(..)) => {}
702+
_ => return None,
703+
}
704+
}
705+
value.offset(Size::ZERO, to, &self.ecx).ok()?
706+
}
707+
_ => return None,
708+
},
709+
710+
Discriminant(place) => {
711+
let op = self.eval_place(place, location, None)?;
712+
let variant = self.use_ecx(location, |this| this.ecx.read_discriminant(&op))?;
713+
let imm = self.use_ecx(location, |this| {
714+
this.ecx.discriminant_for_variant(op.layout.ty, variant)
715+
})?;
716+
imm.into()
717+
}
718+
};
719+
trace!(?val);
720+
721+
self.use_ecx(location, |this| this.ecx.copy_op(&val, &dest, true))?;
722+
723+
Some(())
724+
}
543725
}
544726

545727
impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> {
@@ -574,10 +756,7 @@ impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> {
574756
_ if place.is_indirect() => {}
575757
ConstPropMode::NoPropagation => self.ensure_not_propagated(place.local),
576758
ConstPropMode::OnlyInsideOwnBlock | ConstPropMode::FullConstProp => {
577-
if self
578-
.use_ecx(location, |this| this.ecx.eval_rvalue_into_place(rvalue, *place))
579-
.is_none()
580-
{
759+
if self.eval_rvalue(rvalue, location, place).is_none() {
581760
// Const prop failed, so erase the destination, ensuring that whatever happens
582761
// from here on, does not know about the previous value.
583762
// This is important in case we have

0 commit comments

Comments
 (0)