refactor ub_checks and contract_checks to share logic
This commit is contained in:
parent
04ff05c9c0
commit
2e5d395f2b
23 changed files with 75 additions and 67 deletions
|
|
@ -1046,8 +1046,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
&Rvalue::NullaryOp(NullOp::ContractChecks, _) => {}
|
||||
&Rvalue::NullaryOp(NullOp::UbChecks, _) => {}
|
||||
&Rvalue::NullaryOp(NullOp::RuntimeChecks(_), _) => {}
|
||||
|
||||
Rvalue::ShallowInitBox(_operand, ty) => {
|
||||
let trait_ref =
|
||||
|
|
|
|||
|
|
@ -841,17 +841,8 @@ fn codegen_stmt<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, cur_block: Block, stmt:
|
|||
fields.iter(),
|
||||
)
|
||||
.bytes(),
|
||||
NullOp::UbChecks => {
|
||||
let val = fx.tcx.sess.ub_checks();
|
||||
let val = CValue::by_val(
|
||||
fx.bcx.ins().iconst(types::I8, i64::from(val)),
|
||||
fx.layout_of(fx.tcx.types.bool),
|
||||
);
|
||||
lval.write_cvalue(fx, val);
|
||||
return;
|
||||
}
|
||||
NullOp::ContractChecks => {
|
||||
let val = fx.tcx.sess.contract_checks();
|
||||
NullOp::RuntimeChecks(kind) => {
|
||||
let val = kind.value(fx.tcx.sess);
|
||||
let val = CValue::by_val(
|
||||
fx.bcx.ins().iconst(types::I8, i64::from(val)),
|
||||
fx.layout_of(fx.tcx.types.bool),
|
||||
|
|
|
|||
|
|
@ -618,12 +618,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
.bytes();
|
||||
bx.cx().const_usize(val)
|
||||
}
|
||||
mir::NullOp::UbChecks => {
|
||||
let val = bx.tcx().sess.ub_checks();
|
||||
bx.cx().const_bool(val)
|
||||
}
|
||||
mir::NullOp::ContractChecks => {
|
||||
let val = bx.tcx().sess.contract_checks();
|
||||
mir::NullOp::RuntimeChecks(kind) => {
|
||||
let val = kind.value(bx.tcx().sess);
|
||||
bx.cx().const_bool(val)
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -646,7 +646,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
|
|||
Rvalue::Cast(_, _, _) => {}
|
||||
|
||||
Rvalue::NullaryOp(
|
||||
NullOp::OffsetOf(_) | NullOp::UbChecks | NullOp::ContractChecks,
|
||||
NullOp::OffsetOf(_) | NullOp::RuntimeChecks(_),
|
||||
_,
|
||||
) => {}
|
||||
Rvalue::ShallowInitBox(_, _) => {}
|
||||
|
|
|
|||
|
|
@ -298,11 +298,11 @@ pub trait Machine<'tcx>: Sized {
|
|||
interp_ok(())
|
||||
}
|
||||
|
||||
/// Determines the result of a `NullaryOp::UbChecks` invocation.
|
||||
fn ub_checks(_ecx: &InterpCx<'tcx, Self>) -> InterpResult<'tcx, bool>;
|
||||
|
||||
/// Determines the result of a `NullaryOp::ContractChecks` invocation.
|
||||
fn contract_checks(_ecx: &InterpCx<'tcx, Self>) -> InterpResult<'tcx, bool>;
|
||||
/// Determines the result of a `NullaryOp::RuntimeChecks` invocation.
|
||||
fn runtime_checks(
|
||||
_ecx: &InterpCx<'tcx, Self>,
|
||||
r: mir::RuntimeChecks,
|
||||
) -> InterpResult<'tcx, bool>;
|
||||
|
||||
/// Called when the interpreter encounters a `StatementKind::ConstEvalCounter` instruction.
|
||||
/// You can use this to detect long or endlessly running programs.
|
||||
|
|
@ -681,14 +681,10 @@ pub macro compile_time_machine(<$tcx: lifetime>) {
|
|||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn ub_checks(_ecx: &InterpCx<$tcx, Self>) -> InterpResult<$tcx, bool> {
|
||||
// We can't look at `tcx.sess` here as that can differ across crates, which can lead to
|
||||
// unsound differences in evaluating the same constant at different instantiation sites.
|
||||
interp_ok(true)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn contract_checks(_ecx: &InterpCx<$tcx, Self>) -> InterpResult<$tcx, bool> {
|
||||
fn runtime_checks(
|
||||
_ecx: &InterpCx<$tcx, Self>,
|
||||
_r: mir::RuntimeChecks,
|
||||
) -> InterpResult<$tcx, bool> {
|
||||
// We can't look at `tcx.sess` here as that can differ across crates, which can lead to
|
||||
// unsound differences in evaluating the same constant at different instantiation sites.
|
||||
interp_ok(true)
|
||||
|
|
|
|||
|
|
@ -522,8 +522,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
self.tcx.offset_of_subfield(self.typing_env, layout, fields.iter()).bytes();
|
||||
ImmTy::from_uint(val, usize_layout())
|
||||
}
|
||||
UbChecks => ImmTy::from_bool(M::ub_checks(self)?, *self.tcx),
|
||||
ContractChecks => ImmTy::from_bool(M::contract_checks(self)?, *self.tcx),
|
||||
RuntimeChecks(r) => ImmTy::from_bool(M::runtime_checks(self, r)?, *self.tcx),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -649,7 +649,9 @@ impl<'tcx> Body<'tcx> {
|
|||
}
|
||||
|
||||
match rvalue {
|
||||
Rvalue::NullaryOp(NullOp::UbChecks, _) => Some((tcx.sess.ub_checks() as u128, targets)),
|
||||
Rvalue::NullaryOp(NullOp::RuntimeChecks(kind), _) => {
|
||||
Some((kind.value(tcx.sess) as u128, targets))
|
||||
}
|
||||
Rvalue::Use(Operand::Constant(constant)) => {
|
||||
let bits = eval_mono_const(constant)?;
|
||||
Some((bits, targets))
|
||||
|
|
|
|||
|
|
@ -1093,8 +1093,10 @@ impl<'tcx> Debug for Rvalue<'tcx> {
|
|||
let t = with_no_trimmed_paths!(format!("{}", t));
|
||||
match op {
|
||||
NullOp::OffsetOf(fields) => write!(fmt, "OffsetOf({t}, {fields:?})"),
|
||||
NullOp::UbChecks => write!(fmt, "UbChecks()"),
|
||||
NullOp::ContractChecks => write!(fmt, "ContractChecks()"),
|
||||
NullOp::RuntimeChecks(RuntimeChecks::UbChecks) => write!(fmt, "UbChecks()"),
|
||||
NullOp::RuntimeChecks(RuntimeChecks::ContractChecks) => {
|
||||
write!(fmt, "ContractChecks()")
|
||||
}
|
||||
}
|
||||
}
|
||||
ThreadLocalRef(did) => ty::tls::with(|tcx| {
|
||||
|
|
|
|||
|
|
@ -795,8 +795,7 @@ impl<'tcx> Rvalue<'tcx> {
|
|||
}
|
||||
Rvalue::Discriminant(ref place) => place.ty(local_decls, tcx).ty.discriminant_ty(tcx),
|
||||
Rvalue::NullaryOp(NullOp::OffsetOf(..), _) => tcx.types.usize,
|
||||
Rvalue::NullaryOp(NullOp::ContractChecks, _)
|
||||
| Rvalue::NullaryOp(NullOp::UbChecks, _) => tcx.types.bool,
|
||||
Rvalue::NullaryOp(NullOp::RuntimeChecks(_), _) => tcx.types.bool,
|
||||
Rvalue::Aggregate(ref ak, ref ops) => match **ak {
|
||||
AggregateKind::Array(ty) => Ty::new_array(tcx, ty, ops.len() as u64),
|
||||
AggregateKind::Tuple => {
|
||||
|
|
@ -864,7 +863,7 @@ impl<'tcx> NullOp<'tcx> {
|
|||
pub fn ty(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
|
||||
match self {
|
||||
NullOp::OffsetOf(_) => tcx.types.usize,
|
||||
NullOp::UbChecks | NullOp::ContractChecks => tcx.types.bool,
|
||||
NullOp::RuntimeChecks(_) => tcx.types.bool,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1565,6 +1565,12 @@ pub enum AggregateKind<'tcx> {
|
|||
pub enum NullOp<'tcx> {
|
||||
/// Returns the offset of a field
|
||||
OffsetOf(&'tcx List<(VariantIdx, FieldIdx)>),
|
||||
/// Returns whether we should perform some checking at runtime.
|
||||
RuntimeChecks(RuntimeChecks),
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable)]
|
||||
pub enum RuntimeChecks {
|
||||
/// Returns whether we should perform some UB-checking at runtime.
|
||||
/// See the `ub_checks` intrinsic docs for details.
|
||||
UbChecks,
|
||||
|
|
@ -1573,6 +1579,15 @@ pub enum NullOp<'tcx> {
|
|||
ContractChecks,
|
||||
}
|
||||
|
||||
impl RuntimeChecks {
|
||||
pub fn value(self, sess: &rustc_session::Session) -> bool {
|
||||
match self {
|
||||
Self::UbChecks => sess.ub_checks(),
|
||||
Self::ContractChecks => sess.contract_checks(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
#[derive(HashStable, TyEncodable, TyDecodable, TypeFoldable, TypeVisitable)]
|
||||
pub enum UnOp {
|
||||
|
|
|
|||
|
|
@ -293,9 +293,9 @@ pub fn reverse_postorder<'a, 'tcx>(
|
|||
/// reachable.
|
||||
///
|
||||
/// Such a traversal is mostly useful because it lets us skip lowering the `false` side
|
||||
/// of `if <T as Trait>::CONST`, as well as [`NullOp::UbChecks`].
|
||||
/// of `if <T as Trait>::CONST`, as well as [`NullOp::RuntimeChecks`].
|
||||
///
|
||||
/// [`NullOp::UbChecks`]: rustc_middle::mir::NullOp::UbChecks
|
||||
/// [`NullOp::RuntimeChecks`]: rustc_middle::mir::NullOp::RuntimeChecks
|
||||
pub fn mono_reachable<'a, 'tcx>(
|
||||
body: &'a Body<'tcx>,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
|
|
|
|||
|
|
@ -452,7 +452,7 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> {
|
|||
| Rvalue::RawPtr(..)
|
||||
| Rvalue::Discriminant(..)
|
||||
| Rvalue::NullaryOp(
|
||||
NullOp::OffsetOf(..) | NullOp::UbChecks | NullOp::ContractChecks,
|
||||
NullOp::OffsetOf(..) | NullOp::RuntimeChecks(_),
|
||||
_,
|
||||
) => {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -75,7 +75,8 @@ impl<'tcx> Visitor<'tcx> for CostChecker<'_, 'tcx> {
|
|||
|
||||
fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, _location: Location) {
|
||||
match rvalue {
|
||||
Rvalue::NullaryOp(NullOp::UbChecks, ..)
|
||||
// FIXME: Should we do the same for `OverflowChecks`?
|
||||
Rvalue::NullaryOp(NullOp::RuntimeChecks(RuntimeChecks::UbChecks), ..)
|
||||
if !self
|
||||
.tcx
|
||||
.sess
|
||||
|
|
|
|||
|
|
@ -675,8 +675,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
|
|||
.tcx
|
||||
.offset_of_subfield(self.typing_env(), arg_layout, fields.iter())
|
||||
.bytes(),
|
||||
NullOp::UbChecks => return None,
|
||||
NullOp::ContractChecks => return None,
|
||||
NullOp::RuntimeChecks(_) => return None,
|
||||
};
|
||||
ImmTy::from_uint(val, ty).into()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -169,7 +169,10 @@ impl<'tcx> InstSimplifyContext<'_, 'tcx> {
|
|||
}
|
||||
|
||||
fn simplify_ub_check(&self, rvalue: &mut Rvalue<'tcx>) {
|
||||
let Rvalue::NullaryOp(NullOp::UbChecks, _) = *rvalue else { return };
|
||||
// FIXME: Should we do the same for overflow checks?
|
||||
let Rvalue::NullaryOp(NullOp::RuntimeChecks(RuntimeChecks::UbChecks), _) = *rvalue else {
|
||||
return;
|
||||
};
|
||||
|
||||
let const_ = Const::from_bool(self.tcx, self.tcx.sess.ub_checks());
|
||||
let constant = ConstOperand { span: DUMMY_SP, const_, user_ty: None };
|
||||
|
|
|
|||
|
|
@ -612,8 +612,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
|
|||
.tcx
|
||||
.offset_of_subfield(self.typing_env, op_layout, fields.iter())
|
||||
.bytes(),
|
||||
NullOp::UbChecks => return None,
|
||||
NullOp::ContractChecks => return None,
|
||||
NullOp::RuntimeChecks(_) => return None,
|
||||
};
|
||||
ImmTy::from_scalar(Scalar::from_target_usize(val, self), layout).into()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,13 +23,18 @@ impl<'tcx> crate::MirPass<'tcx> for LowerIntrinsics {
|
|||
sym::unreachable => {
|
||||
terminator.kind = TerminatorKind::Unreachable;
|
||||
}
|
||||
sym::ub_checks => {
|
||||
sym::ub_checks | sym::contract_checks => {
|
||||
let op = match intrinsic.name {
|
||||
sym::ub_checks => RuntimeChecks::UbChecks,
|
||||
sym::contract_checks => RuntimeChecks::ContractChecks,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let target = target.unwrap();
|
||||
block.statements.push(Statement::new(
|
||||
terminator.source_info,
|
||||
StatementKind::Assign(Box::new((
|
||||
*destination,
|
||||
Rvalue::NullaryOp(NullOp::UbChecks, tcx.types.bool),
|
||||
Rvalue::NullaryOp(NullOp::RuntimeChecks(op), tcx.types.bool),
|
||||
))),
|
||||
));
|
||||
terminator.kind = TerminatorKind::Goto { target };
|
||||
|
|
|
|||
|
|
@ -451,8 +451,7 @@ impl<'tcx> Validator<'_, 'tcx> {
|
|||
|
||||
Rvalue::NullaryOp(op, _) => match op {
|
||||
NullOp::OffsetOf(_) => {}
|
||||
NullOp::UbChecks => {}
|
||||
NullOp::ContractChecks => {}
|
||||
NullOp::RuntimeChecks(_) => {}
|
||||
},
|
||||
|
||||
Rvalue::ShallowInitBox(_, _) => return Err(Unpromotable),
|
||||
|
|
|
|||
|
|
@ -1478,7 +1478,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
|||
Rvalue::Repeat(_, _)
|
||||
| Rvalue::ThreadLocalRef(_)
|
||||
| Rvalue::RawPtr(_, _)
|
||||
| Rvalue::NullaryOp(NullOp::UbChecks | NullOp::ContractChecks, _)
|
||||
| Rvalue::NullaryOp(NullOp::RuntimeChecks(_), _)
|
||||
| Rvalue::Discriminant(_) => {}
|
||||
|
||||
Rvalue::WrapUnsafeBinder(op, ty) => {
|
||||
|
|
|
|||
|
|
@ -642,8 +642,7 @@ impl Rvalue {
|
|||
.ok_or_else(|| error!("Expected a `RigidTy` but found: {place_ty:?}"))
|
||||
}
|
||||
Rvalue::NullaryOp(NullOp::OffsetOf(..), _) => Ok(Ty::usize_ty()),
|
||||
Rvalue::NullaryOp(NullOp::ContractChecks, _)
|
||||
| Rvalue::NullaryOp(NullOp::UbChecks, _) => Ok(Ty::bool_ty()),
|
||||
Rvalue::NullaryOp(NullOp::RuntimeChecks(_), _) => Ok(Ty::bool_ty()),
|
||||
Rvalue::Aggregate(ak, ops) => match *ak {
|
||||
AggregateKind::Array(ty) => Ty::try_new_array(ty, ops.len() as u64),
|
||||
AggregateKind::Tuple => Ok(Ty::new_tuple(
|
||||
|
|
@ -1024,6 +1023,12 @@ pub enum CastKind {
|
|||
pub enum NullOp {
|
||||
/// Returns the offset of a field.
|
||||
OffsetOf(Vec<(VariantIdx, FieldIdx)>),
|
||||
/// Codegen conditions for runtime checks.
|
||||
RuntimeChecks(RuntimeChecks),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize)]
|
||||
pub enum RuntimeChecks {
|
||||
/// cfg!(ub_checks), but at codegen time
|
||||
UbChecks,
|
||||
/// cfg!(contract_checks), but at codegen time
|
||||
|
|
|
|||
|
|
@ -322,12 +322,15 @@ impl<'tcx> Stable<'tcx> for mir::NullOp<'tcx> {
|
|||
cx: &CompilerCtxt<'cx, BridgeTys>,
|
||||
) -> Self::T {
|
||||
use rustc_middle::mir::NullOp::*;
|
||||
use rustc_middle::mir::RuntimeChecks::*;
|
||||
match self {
|
||||
OffsetOf(indices) => crate::mir::NullOp::OffsetOf(
|
||||
indices.iter().map(|idx| idx.stable(tables, cx)).collect(),
|
||||
),
|
||||
UbChecks => crate::mir::NullOp::UbChecks,
|
||||
ContractChecks => crate::mir::NullOp::ContractChecks,
|
||||
RuntimeChecks(op) => crate::mir::NullOp::RuntimeChecks(match op {
|
||||
UbChecks => crate::mir::RuntimeChecks::UbChecks,
|
||||
ContractChecks => crate::mir::RuntimeChecks::ContractChecks,
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -194,7 +194,7 @@ fn check_rvalue<'tcx>(
|
|||
))
|
||||
}
|
||||
},
|
||||
Rvalue::NullaryOp(NullOp::OffsetOf(_) | NullOp::UbChecks | NullOp::ContractChecks, _)
|
||||
Rvalue::NullaryOp(NullOp::OffsetOf(_) | NullOp::RuntimeChecks(_), _)
|
||||
| Rvalue::ShallowInitBox(_, _) => Ok(()),
|
||||
Rvalue::UnaryOp(_, operand) => {
|
||||
let ty = operand.ty(body, cx.tcx);
|
||||
|
|
|
|||
|
|
@ -1329,13 +1329,8 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
|
|||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn ub_checks(ecx: &InterpCx<'tcx, Self>) -> InterpResult<'tcx, bool> {
|
||||
interp_ok(ecx.tcx.sess.ub_checks())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn contract_checks(ecx: &InterpCx<'tcx, Self>) -> InterpResult<'tcx, bool> {
|
||||
interp_ok(ecx.tcx.sess.contract_checks())
|
||||
fn runtime_checks(ecx: &InterpCx<'tcx, Self>, r: mir::RuntimeChecks) -> InterpResult<'tcx, bool> {
|
||||
interp_ok(r.value(&ecx.tcx.sess))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue