Skip to content

Commit 6ba56d2

Browse files
committed
Move trivial_const to a separate module with a doc comment
1 parent 775da71 commit 6ba56d2

File tree

2 files changed

+99
-59
lines changed

2 files changed

+99
-59
lines changed

compiler/rustc_mir_transform/src/lib.rs

Lines changed: 8 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,11 @@ use rustc_hir::def::{CtorKind, DefKind};
2323
use rustc_hir::def_id::LocalDefId;
2424
use rustc_index::IndexVec;
2525
use rustc_middle::mir::{
26-
AnalysisPhase, Body, CallSource, ClearCrossCrate, ConstOperand, ConstQualifs, ConstValue,
27-
LocalDecl, MirPhase, Operand, Place, ProjectionElem, Promoted, RETURN_PLACE, RuntimePhase,
28-
Rvalue, START_BLOCK, SourceInfo, Statement, StatementKind, TerminatorKind,
26+
AnalysisPhase, Body, CallSource, ClearCrossCrate, ConstOperand, ConstQualifs, LocalDecl,
27+
MirPhase, Operand, Place, ProjectionElem, Promoted, RuntimePhase, Rvalue, START_BLOCK,
28+
SourceInfo, Statement, StatementKind, TerminatorKind,
2929
};
30-
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt};
30+
use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt};
3131
use rustc_middle::util::Providers;
3232
use rustc_middle::{bug, query, span_bug};
3333
use rustc_mir_build::builder::build_mir;
@@ -55,6 +55,7 @@ mod liveness;
5555
mod patch;
5656
mod shim;
5757
mod ssa;
58+
mod trivial_const;
5859

5960
/// We import passes via this macro so that we can have a static list of pass names
6061
/// (used to verify CLI arguments). It takes a list of modules, followed by the passes
@@ -226,7 +227,7 @@ pub fn provide(providers: &mut Providers) {
226227
promoted_mir,
227228
deduced_param_attrs: deduce_param_attrs::deduced_param_attrs,
228229
coroutine_by_move_body_def_id: coroutine::coroutine_by_move_body_def_id,
229-
trivial_const: trivial_const_provider,
230+
trivial_const: trivial_const::trivial_const_provider,
230231
..providers.queries
231232
};
232233
}
@@ -377,66 +378,14 @@ fn mir_const_qualif(tcx: TyCtxt<'_>, def: LocalDefId) -> ConstQualifs {
377378
validator.qualifs_in_return_place()
378379
}
379380

380-
fn def_kind_compatible_with_trivial_mir<'tcx>(tcx: TyCtxt<'tcx>, def: LocalDefId) -> bool {
381-
// Static and InlineConst are the obvious additions, but
382-
// * Statics need additional type-checking to taint `static A: _ = 0;`, currently we'd ICE.
383-
// * The MIR for InlineConst is used by the borrow checker, and not easy to skip over.
384-
matches!(tcx.def_kind(def), DefKind::AssocConst | DefKind::Const | DefKind::AnonConst)
385-
}
386-
387-
fn trivial_const_provider<'tcx>(
388-
tcx: TyCtxt<'tcx>,
389-
def: LocalDefId,
390-
) -> Option<(ConstValue, Ty<'tcx>)> {
391-
if def_kind_compatible_with_trivial_mir(tcx, def) {
392-
trivial_const(&tcx.mir_built(def).borrow())
393-
} else {
394-
None
395-
}
396-
}
397-
398-
fn trivial_const<'tcx>(body: &Body<'tcx>) -> Option<(ConstValue, Ty<'tcx>)> {
399-
if body.has_opaque_types() {
400-
return None;
401-
}
402-
403-
if body.basic_blocks.len() != 1 {
404-
return None;
405-
}
406-
407-
let block = &body.basic_blocks[START_BLOCK];
408-
if block.statements.len() != 1 {
409-
return None;
410-
}
411-
412-
if block.terminator().kind != TerminatorKind::Return {
413-
return None;
414-
}
415-
416-
let StatementKind::Assign(box (place, rvalue)) = &block.statements[0].kind else {
417-
return None;
418-
};
419-
420-
if *place != Place::from(RETURN_PLACE) {
421-
return None;
422-
}
423-
424-
if let Rvalue::Use(Operand::Constant(c)) = rvalue {
425-
if let rustc_middle::mir::Const::Val(v, ty) = c.const_ {
426-
return Some((v, ty));
427-
}
428-
}
429-
430-
return None;
431-
}
432-
433381
fn mir_built(tcx: TyCtxt<'_>, def: LocalDefId) -> &Steal<Body<'_>> {
434382
let mut body = build_mir(tcx, def);
435383

436384
// Identifying trivial consts based on their mir_built is easy, but a little wasteful.
437385
// Trying to push this logic earlier in the compiler and never even produce the Body would
438386
// probably improve compile time.
439-
if def_kind_compatible_with_trivial_mir(tcx, def) && trivial_const(&body).is_some() {
387+
if trivial_const::trivial_const(tcx, def, || &body).is_some() {
388+
// Skip all the passes below for trivial consts.
440389
let body = tcx.alloc_steal_mir(body);
441390
pass_manager::dump_mir_for_phase_change(tcx, &body.borrow());
442391
return body;
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
use std::ops::Deref;
2+
3+
use rustc_hir::def::DefKind;
4+
use rustc_hir::def_id::LocalDefId;
5+
use rustc_middle::mir::{
6+
Body, ConstValue, Operand, Place, RETURN_PLACE, Rvalue, START_BLOCK, StatementKind,
7+
TerminatorKind,
8+
};
9+
use rustc_middle::ty::{Ty, TyCtxt, TypeVisitableExt};
10+
11+
/// If the given def is a trivial const, returns the value and type the const evaluates to.
12+
///
13+
/// A "trivial const" is a const which can be easily proven to evaluate successfully, and the value
14+
/// that it evaluates to can be easily found without going through the usual MIR phases for a const.
15+
///
16+
/// Currently the only form of trivial const that is supported is this:
17+
/// ```
18+
/// const A: usize = 0;
19+
/// ```
20+
/// which has this MIR:
21+
/// ```text
22+
/// const A: usize = {
23+
/// let mut _0: usize;
24+
///
25+
/// bb0: {
26+
/// _0 = const 0_usize;
27+
/// return;
28+
/// }
29+
/// }
30+
/// ```
31+
/// Which we recognize by looking for a Body which has a single basic block with a return
32+
/// terminator and a single statement which assigns an `Operand::Constant(Const::Val)` to the
33+
/// return place.
34+
/// This scenario meets the required criteria because:
35+
/// * Control flow cannot panic, we don't have any calls or assert terminators
36+
/// * The value of the const is already computed, so it cannot fail
37+
pub(crate) fn trivial_const<'a, 'tcx: 'a, F, B>(
38+
tcx: TyCtxt<'tcx>,
39+
def: LocalDefId,
40+
body_provider: F,
41+
) -> Option<(ConstValue, Ty<'tcx>)>
42+
where
43+
F: FnOnce() -> B,
44+
B: Deref<Target = Body<'tcx>>,
45+
{
46+
if !matches!(tcx.def_kind(def), DefKind::AssocConst | DefKind::Const | DefKind::AnonConst) {
47+
return None;
48+
}
49+
50+
let body = body_provider();
51+
52+
if body.has_opaque_types() {
53+
return None;
54+
}
55+
56+
if body.basic_blocks.len() != 1 {
57+
return None;
58+
}
59+
60+
let block = &body.basic_blocks[START_BLOCK];
61+
if block.statements.len() != 1 {
62+
return None;
63+
}
64+
65+
if block.terminator().kind != TerminatorKind::Return {
66+
return None;
67+
}
68+
69+
let StatementKind::Assign(box (place, rvalue)) = &block.statements[0].kind else {
70+
return None;
71+
};
72+
73+
if *place != Place::from(RETURN_PLACE) {
74+
return None;
75+
}
76+
77+
if let Rvalue::Use(Operand::Constant(c)) = rvalue {
78+
if let rustc_middle::mir::Const::Val(v, ty) = c.const_ {
79+
return Some((v, ty));
80+
}
81+
}
82+
83+
return None;
84+
}
85+
86+
pub(crate) fn trivial_const_provider<'tcx>(
87+
tcx: TyCtxt<'tcx>,
88+
def: LocalDefId,
89+
) -> Option<(ConstValue, Ty<'tcx>)> {
90+
trivial_const(tcx, def, || tcx.mir_built(def).borrow())
91+
}

0 commit comments

Comments
 (0)