-
Notifications
You must be signed in to change notification settings - Fork 13.4k
Let MonoReachable traversal evaluate BinOps #129287
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -51,7 +51,107 @@ pub fn provide(providers: &mut Providers) { | |
}; | ||
} | ||
|
||
use rustc_middle::mir::{ | ||
BasicBlockData, ConstOperand, NullOp, Operand, Rvalue, StatementKind, SwitchTargets, | ||
TerminatorKind, | ||
}; | ||
use rustc_middle::ty::{Instance, TyCtxt}; | ||
|
||
/// `rustc_driver::main` installs a handler that will set this to `true` if | ||
/// the compiler has been sent a request to shut down, such as by a Ctrl-C. | ||
/// This static lives here because it is only read by the interpreter. | ||
pub static CTRL_C_RECEIVED: AtomicBool = AtomicBool::new(false); | ||
|
||
/// If this basic block ends with a [`TerminatorKind::SwitchInt`] for which we can evaluate the | ||
/// dimscriminant in monomorphization, we return the discriminant bits and the | ||
/// [`SwitchTargets`], just so the caller doesn't also have to match on the terminator. | ||
pub fn try_const_mono_switchint<'a, 'tcx>( | ||
tcx: TyCtxt<'tcx>, | ||
instance: Instance<'tcx>, | ||
block: &'a BasicBlockData<'tcx>, | ||
) -> Option<(u128, &'a SwitchTargets)> { | ||
// There are two places here we need to evaluate a constant. | ||
let eval_mono_const = |constant: &ConstOperand<'tcx>| { | ||
let env = ty::ParamEnv::reveal_all(); | ||
let mono_literal = instance.instantiate_mir_and_normalize_erasing_regions( | ||
tcx, | ||
env, | ||
crate::ty::EarlyBinder::bind(constant.const_), | ||
); | ||
mono_literal.try_eval_bits(tcx, env) | ||
}; | ||
|
||
let TerminatorKind::SwitchInt { discr, targets } = &block.terminator().kind else { | ||
return None; | ||
}; | ||
|
||
// If this is a SwitchInt(const _), then we can just evaluate the constant and return. | ||
let discr = match discr { | ||
Operand::Constant(constant) => { | ||
let bits = eval_mono_const(constant)?; | ||
return Some((bits, targets)); | ||
} | ||
Operand::Move(place) | Operand::Copy(place) => place, | ||
}; | ||
|
||
// MIR for `if false` actually looks like this: | ||
// _1 = const _ | ||
// SwitchInt(_1) | ||
// | ||
// And MIR for if intrinsics::ub_checks() looks like this: | ||
// _1 = UbChecks() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we just make this a variant for mir::Const and avoid having this logic? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, that would be really odd. We could consider making |
||
// SwitchInt(_1) | ||
// | ||
// So we're going to try to recognize this pattern. | ||
// | ||
// If we have a SwitchInt on a non-const place, we find the most recent statement that | ||
// isn't a storage marker. If that statement is an assignment of a const to our | ||
// discriminant place, we evaluate and return the const, as if we've const-propagated it | ||
// into the SwitchInt. | ||
|
||
let last_stmt = block.statements.iter().rev().find(|stmt| { | ||
!matches!(stmt.kind, StatementKind::StorageDead(_) | StatementKind::StorageLive(_)) | ||
})?; | ||
|
||
let (place, rvalue) = last_stmt.kind.as_assign()?; | ||
|
||
if discr != place { | ||
return None; | ||
} | ||
|
||
use rustc_span::DUMMY_SP; | ||
|
||
use crate::const_eval::DummyMachine; | ||
use crate::interpret::InterpCx; | ||
match rvalue { | ||
Rvalue::NullaryOp(NullOp::UbChecks, _) => Some((tcx.sess.ub_checks() as u128, targets)), | ||
Rvalue::Use(Operand::Constant(constant)) => { | ||
let bits = eval_mono_const(constant)?; | ||
Some((bits, targets)) | ||
} | ||
Rvalue::BinaryOp(binop, box (Operand::Constant(lhs), Operand::Constant(rhs))) => { | ||
let env = ty::ParamEnv::reveal_all(); | ||
let lhs = instance.instantiate_mir_and_normalize_erasing_regions( | ||
tcx, | ||
env, | ||
crate::ty::EarlyBinder::bind(lhs.const_), | ||
); | ||
let ecx = InterpCx::new(tcx, DUMMY_SP, env, DummyMachine); | ||
let lhs = ecx.eval_mir_constant(&lhs, DUMMY_SP, None).ok()?; | ||
let lhs = ecx.read_immediate(&lhs).ok()?; | ||
|
||
let rhs = instance.instantiate_mir_and_normalize_erasing_regions( | ||
tcx, | ||
env, | ||
crate::ty::EarlyBinder::bind(rhs.const_), | ||
); | ||
let rhs = ecx.eval_mir_constant(&rhs, DUMMY_SP, None).ok()?; | ||
let rhs = ecx.read_immediate(&rhs).ok()?; | ||
|
||
let res = ecx.binary_op(*binop, &lhs, &rhs).ok()?; | ||
let res = res.to_scalar_int().unwrap().to_bits_unchecked(); | ||
Some((res, targets)) | ||
} | ||
_ => None, | ||
} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.