11use crate :: mir:: interpret:: Scalar ;
22use crate :: ty:: { self , Ty , TyCtxt } ;
33use rustc_ast:: { InlineAsmOptions , InlineAsmTemplatePiece } ;
4+ use smallvec:: { smallvec, SmallVec } ;
45
56use super :: {
67 AssertMessage , BasicBlock , InlineAsmOperand , Operand , Place , SourceInfo , Successors ,
@@ -16,6 +17,87 @@ use std::slice;
1617
1718pub use super :: query:: * ;
1819
20+ #[ derive( Debug , Clone , TyEncodable , TyDecodable , HashStable , PartialEq ) ]
21+ pub struct SwitchTargets {
22+ /// Possible values. The locations to branch to in each case
23+ /// are found in the corresponding indices from the `targets` vector.
24+ values : SmallVec < [ u128 ; 1 ] > ,
25+
26+ /// Possible branch sites. The last element of this vector is used
27+ /// for the otherwise branch, so targets.len() == values.len() + 1
28+ /// should hold.
29+ //
30+ // This invariant is quite non-obvious and also could be improved.
31+ // One way to make this invariant is to have something like this instead:
32+ //
33+ // branches: Vec<(ConstInt, BasicBlock)>,
34+ // otherwise: Option<BasicBlock> // exhaustive if None
35+ //
36+ // However we’ve decided to keep this as-is until we figure a case
37+ // where some other approach seems to be strictly better than other.
38+ targets : SmallVec < [ BasicBlock ; 2 ] > ,
39+ }
40+
41+ impl SwitchTargets {
42+ /// Creates switch targets from an iterator of values and target blocks.
43+ ///
44+ /// The iterator may be empty, in which case the `SwitchInt` instruction is equivalent to
45+ /// `goto otherwise;`.
46+ pub fn new ( targets : impl Iterator < Item = ( u128 , BasicBlock ) > , otherwise : BasicBlock ) -> Self {
47+ let ( values, mut targets) : ( SmallVec < _ > , SmallVec < _ > ) = targets. unzip ( ) ;
48+ targets. push ( otherwise) ;
49+ Self { values : values. into ( ) , targets }
50+ }
51+
52+ /// Builds a switch targets definition that jumps to `then` if the tested value equals `value`,
53+ /// and to `else_` if not.
54+ pub fn static_if ( value : u128 , then : BasicBlock , else_ : BasicBlock ) -> Self {
55+ Self { values : smallvec ! [ value] , targets : smallvec ! [ then, else_] }
56+ }
57+
58+ /// Returns the fallback target that is jumped to when none of the values match the operand.
59+ pub fn otherwise ( & self ) -> BasicBlock {
60+ * self . targets . last ( ) . unwrap ( )
61+ }
62+
63+ /// Returns an iterator over the switch targets.
64+ ///
65+ /// The iterator will yield tuples containing the value and corresponding target to jump to, not
66+ /// including the `otherwise` fallback target.
67+ ///
68+ /// Note that this may yield 0 elements. Only the `otherwise` branch is mandatory.
69+ pub fn iter ( & self ) -> SwitchTargetsIter < ' _ > {
70+ SwitchTargetsIter { inner : self . values . iter ( ) . zip ( self . targets . iter ( ) ) }
71+ }
72+
73+ /// Returns a slice with all possible jump targets (including the fallback target).
74+ pub fn all_targets ( & self ) -> & [ BasicBlock ] {
75+ & self . targets
76+ }
77+
78+ pub fn all_targets_mut ( & mut self ) -> & mut [ BasicBlock ] {
79+ & mut self . targets
80+ }
81+ }
82+
83+ pub struct SwitchTargetsIter < ' a > {
84+ inner : iter:: Zip < slice:: Iter < ' a , u128 > , slice:: Iter < ' a , BasicBlock > > ,
85+ }
86+
87+ impl < ' a > Iterator for SwitchTargetsIter < ' a > {
88+ type Item = ( u128 , BasicBlock ) ;
89+
90+ fn next ( & mut self ) -> Option < Self :: Item > {
91+ self . inner . next ( ) . map ( |( val, bb) | ( * val, * bb) )
92+ }
93+
94+ fn size_hint ( & self ) -> ( usize , Option < usize > ) {
95+ self . inner . size_hint ( )
96+ }
97+ }
98+
99+ impl < ' a > ExactSizeIterator for SwitchTargetsIter < ' a > { }
100+
19101#[ derive( Clone , TyEncodable , TyDecodable , HashStable , PartialEq ) ]
20102pub enum TerminatorKind < ' tcx > {
21103 /// Block should have one successor in the graph; we jump there.
@@ -32,23 +114,7 @@ pub enum TerminatorKind<'tcx> {
32114 /// FIXME: remove this redundant information. Currently, it is relied on by pretty-printing.
33115 switch_ty : Ty < ' tcx > ,
34116
35- /// Possible values. The locations to branch to in each case
36- /// are found in the corresponding indices from the `targets` vector.
37- values : Cow < ' tcx , [ u128 ] > ,
38-
39- /// Possible branch sites. The last element of this vector is used
40- /// for the otherwise branch, so targets.len() == values.len() + 1
41- /// should hold.
42- //
43- // This invariant is quite non-obvious and also could be improved.
44- // One way to make this invariant is to have something like this instead:
45- //
46- // branches: Vec<(ConstInt, BasicBlock)>,
47- // otherwise: Option<BasicBlock> // exhaustive if None
48- //
49- // However we’ve decided to keep this as-is until we figure a case
50- // where some other approach seems to be strictly better than other.
51- targets : Vec < BasicBlock > ,
117+ targets : SwitchTargets ,
52118 } ,
53119
54120 /// Indicates that the landing pad is finished and unwinding should
@@ -227,12 +293,10 @@ impl<'tcx> TerminatorKind<'tcx> {
227293 t : BasicBlock ,
228294 f : BasicBlock ,
229295 ) -> TerminatorKind < ' tcx > {
230- static BOOL_SWITCH_FALSE : & [ u128 ] = & [ 0 ] ;
231296 TerminatorKind :: SwitchInt {
232297 discr : cond,
233298 switch_ty : tcx. types . bool ,
234- values : From :: from ( BOOL_SWITCH_FALSE ) ,
235- targets : vec ! [ f, t] ,
299+ targets : SwitchTargets :: static_if ( 0 , f, t) ,
236300 }
237301 }
238302
@@ -263,7 +327,7 @@ impl<'tcx> TerminatorKind<'tcx> {
263327 | FalseUnwind { real_target : ref t, unwind : Some ( ref u) } => {
264328 Some ( t) . into_iter ( ) . chain ( slice:: from_ref ( u) )
265329 }
266- SwitchInt { ref targets, .. } => None . into_iter ( ) . chain ( & targets[ ..] ) ,
330+ SwitchInt { ref targets, .. } => None . into_iter ( ) . chain ( & targets. targets [ ..] ) ,
267331 FalseEdge { ref real_target, ref imaginary_target } => {
268332 Some ( real_target) . into_iter ( ) . chain ( slice:: from_ref ( imaginary_target) )
269333 }
@@ -297,7 +361,7 @@ impl<'tcx> TerminatorKind<'tcx> {
297361 | FalseUnwind { real_target : ref mut t, unwind : Some ( ref mut u) } => {
298362 Some ( t) . into_iter ( ) . chain ( slice:: from_mut ( u) )
299363 }
300- SwitchInt { ref mut targets, .. } => None . into_iter ( ) . chain ( & mut targets[ ..] ) ,
364+ SwitchInt { ref mut targets, .. } => None . into_iter ( ) . chain ( & mut targets. targets [ ..] ) ,
301365 FalseEdge { ref mut real_target, ref mut imaginary_target } => {
302366 Some ( real_target) . into_iter ( ) . chain ( slice:: from_mut ( imaginary_target) )
303367 }
@@ -469,11 +533,12 @@ impl<'tcx> TerminatorKind<'tcx> {
469533 match * self {
470534 Return | Resume | Abort | Unreachable | GeneratorDrop => vec ! [ ] ,
471535 Goto { .. } => vec ! [ "" . into( ) ] ,
472- SwitchInt { ref values , switch_ty, .. } => ty:: tls:: with ( |tcx| {
536+ SwitchInt { ref targets , switch_ty, .. } => ty:: tls:: with ( |tcx| {
473537 let param_env = ty:: ParamEnv :: empty ( ) ;
474538 let switch_ty = tcx. lift ( & switch_ty) . unwrap ( ) ;
475539 let size = tcx. layout_of ( param_env. and ( switch_ty) ) . unwrap ( ) . size ;
476- values
540+ targets
541+ . values
477542 . iter ( )
478543 . map ( |& u| {
479544 ty:: Const :: from_scalar ( tcx, Scalar :: from_uint ( u, size) , switch_ty)
0 commit comments