1 use smallvec
::SmallVec
;
3 use super::{BasicBlock, InlineAsmOperand, Operand, SourceInfo, TerminatorKind}
;
4 use rustc_ast
::InlineAsmTemplatePiece
;
5 pub use rustc_ast
::Mutability
;
6 use rustc_macros
::HashStable
;
8 use std
::fmt
::{self, Debug, Formatter, Write}
;
12 pub use super::query
::*;
14 #[derive(Debug, Clone, TyEncodable, TyDecodable, Hash, HashStable, PartialEq)]
15 pub struct SwitchTargets
{
16 /// Possible values. The locations to branch to in each case
17 /// are found in the corresponding indices from the `targets` vector.
18 values
: SmallVec
<[u128
; 1]>,
20 /// Possible branch sites. The last element of this vector is used
21 /// for the otherwise branch, so targets.len() == values.len() + 1
24 // This invariant is quite non-obvious and also could be improved.
25 // One way to make this invariant is to have something like this instead:
27 // branches: Vec<(ConstInt, BasicBlock)>,
28 // otherwise: Option<BasicBlock> // exhaustive if None
30 // However we’ve decided to keep this as-is until we figure a case
31 // where some other approach seems to be strictly better than other.
32 targets
: SmallVec
<[BasicBlock
; 2]>,
36 /// Creates switch targets from an iterator of values and target blocks.
38 /// The iterator may be empty, in which case the `SwitchInt` instruction is equivalent to
39 /// `goto otherwise;`.
40 pub fn new(targets
: impl Iterator
<Item
= (u128
, BasicBlock
)>, otherwise
: BasicBlock
) -> Self {
41 let (values
, mut targets
): (SmallVec
<_
>, SmallVec
<_
>) = targets
.unzip();
42 targets
.push(otherwise
);
43 Self { values, targets }
46 /// Builds a switch targets definition that jumps to `then` if the tested value equals `value`,
47 /// and to `else_` if not.
48 pub fn static_if(value
: u128
, then
: BasicBlock
, else_
: BasicBlock
) -> Self {
49 Self { values: smallvec![value], targets: smallvec![then, else_] }
52 /// Returns the fallback target that is jumped to when none of the values match the operand.
53 pub fn otherwise(&self) -> BasicBlock
{
54 *self.targets
.last().unwrap()
57 /// Returns an iterator over the switch targets.
59 /// The iterator will yield tuples containing the value and corresponding target to jump to, not
60 /// including the `otherwise` fallback target.
62 /// Note that this may yield 0 elements. Only the `otherwise` branch is mandatory.
63 pub fn iter(&self) -> SwitchTargetsIter
<'_
> {
64 SwitchTargetsIter { inner: iter::zip(&self.values, &self.targets) }
67 /// Returns a slice with all possible jump targets (including the fallback target).
68 pub fn all_targets(&self) -> &[BasicBlock
] {
72 pub fn all_targets_mut(&mut self) -> &mut [BasicBlock
] {
76 /// Finds the `BasicBlock` to which this `SwitchInt` will branch given the
77 /// specific value. This cannot fail, as it'll return the `otherwise`
78 /// branch if there's not a specific match for the value.
79 pub fn target_for_value(&self, value
: u128
) -> BasicBlock
{
80 self.iter().find_map(|(v
, t
)| (v
== value
).then_some(t
)).unwrap_or_else(|| self.otherwise())
84 pub struct SwitchTargetsIter
<'a
> {
85 inner
: iter
::Zip
<slice
::Iter
<'a
, u128
>, slice
::Iter
<'a
, BasicBlock
>>,
88 impl<'a
> Iterator
for SwitchTargetsIter
<'a
> {
89 type Item
= (u128
, BasicBlock
);
91 fn next(&mut self) -> Option
<Self::Item
> {
92 self.inner
.next().map(|(val
, bb
)| (*val
, *bb
))
95 fn size_hint(&self) -> (usize, Option
<usize>) {
96 self.inner
.size_hint()
100 impl<'a
> ExactSizeIterator
for SwitchTargetsIter
<'a
> {}
102 #[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)]
103 pub struct Terminator
<'tcx
> {
104 pub source_info
: SourceInfo
,
105 pub kind
: TerminatorKind
<'tcx
>,
108 pub type Successors
<'a
> = impl Iterator
<Item
= BasicBlock
> + 'a
;
109 pub type SuccessorsMut
<'a
> =
110 iter
::Chain
<std
::option
::IntoIter
<&'a
mut BasicBlock
>, slice
::IterMut
<'a
, BasicBlock
>>;
112 impl<'tcx
> Terminator
<'tcx
> {
113 pub fn successors(&self) -> Successors
<'_
> {
114 self.kind
.successors()
117 pub fn successors_mut(&mut self) -> SuccessorsMut
<'_
> {
118 self.kind
.successors_mut()
121 pub fn unwind(&self) -> Option
<&Option
<BasicBlock
>> {
125 pub fn unwind_mut(&mut self) -> Option
<&mut Option
<BasicBlock
>> {
126 self.kind
.unwind_mut()
130 impl<'tcx
> TerminatorKind
<'tcx
> {
131 pub fn if_(cond
: Operand
<'tcx
>, t
: BasicBlock
, f
: BasicBlock
) -> TerminatorKind
<'tcx
> {
132 TerminatorKind
::SwitchInt { discr: cond, targets: SwitchTargets::static_if(0, f, t) }
135 pub fn successors(&self) -> Successors
<'_
> {
136 use self::TerminatorKind
::*;
143 | Call { target: None, cleanup: None, .. }
144 | InlineAsm { destination: None, cleanup: None, .. }
=> {
145 None
.into_iter().chain((&[]).into_iter().copied())
148 | Call { target: None, cleanup: Some(t), .. }
149 | Call { target: Some(t), cleanup: None, .. }
150 | Yield { resume: t, drop: None, .. }
151 | DropAndReplace { target: t, unwind: None, .. }
152 | Drop { target: t, unwind: None, .. }
153 | Assert { target: t, cleanup: None, .. }
154 | FalseUnwind { real_target: t, unwind: None }
155 | InlineAsm { destination: Some(t), cleanup: None, .. }
156 | InlineAsm { destination: None, cleanup: Some(t), .. }
=> {
157 Some(t
).into_iter().chain((&[]).into_iter().copied())
159 Call { target: Some(t), cleanup: Some(ref u), .. }
160 | Yield { resume: t, drop: Some(ref u), .. }
161 | DropAndReplace { target: t, unwind: Some(ref u), .. }
162 | Drop { target: t, unwind: Some(ref u), .. }
163 | Assert { target: t, cleanup: Some(ref u), .. }
164 | FalseUnwind { real_target: t, unwind: Some(ref u) }
165 | InlineAsm { destination: Some(t), cleanup: Some(ref u), .. }
=> {
166 Some(t
).into_iter().chain(slice
::from_ref(u
).into_iter().copied())
168 SwitchInt { ref targets, .. }
=> {
169 None
.into_iter().chain(targets
.targets
.iter().copied())
171 FalseEdge { real_target, ref imaginary_target }
=> Some(real_target
)
173 .chain(slice
::from_ref(imaginary_target
).into_iter().copied()),
177 pub fn successors_mut(&mut self) -> SuccessorsMut
<'_
> {
178 use self::TerminatorKind
::*;
185 | Call { target: None, cleanup: None, .. }
186 | InlineAsm { destination: None, cleanup: None, .. }
=> None
.into_iter().chain(&mut []),
187 Goto { target: ref mut t }
188 | Call { target: None, cleanup: Some(ref mut t), .. }
189 | Call { target: Some(ref mut t), cleanup: None, .. }
190 | Yield { resume: ref mut t, drop: None, .. }
191 | DropAndReplace { target: ref mut t, unwind: None, .. }
192 | Drop { target: ref mut t, unwind: None, .. }
193 | Assert { target: ref mut t, cleanup: None, .. }
194 | FalseUnwind { real_target: ref mut t, unwind: None }
195 | InlineAsm { destination: Some(ref mut t), cleanup: None, .. }
196 | InlineAsm { destination: None, cleanup: Some(ref mut t), .. }
=> {
197 Some(t
).into_iter().chain(&mut [])
199 Call { target: Some(ref mut t), cleanup: Some(ref mut u), .. }
200 | Yield { resume: ref mut t, drop: Some(ref mut u), .. }
201 | DropAndReplace { target: ref mut t, unwind: Some(ref mut u), .. }
202 | Drop { target: ref mut t, unwind: Some(ref mut u), .. }
203 | Assert { target: ref mut t, cleanup: Some(ref mut u), .. }
204 | FalseUnwind { real_target: ref mut t, unwind: Some(ref mut u) }
205 | InlineAsm { destination: Some(ref mut t), cleanup: Some(ref mut u), .. }
=> {
206 Some(t
).into_iter().chain(slice
::from_mut(u
))
208 SwitchInt { ref mut targets, .. }
=> None
.into_iter().chain(&mut targets
.targets
),
209 FalseEdge { ref mut real_target, ref mut imaginary_target }
=> {
210 Some(real_target
).into_iter().chain(slice
::from_mut(imaginary_target
))
215 pub fn unwind(&self) -> Option
<&Option
<BasicBlock
>> {
217 TerminatorKind
::Goto { .. }
218 | TerminatorKind
::Resume
219 | TerminatorKind
::Abort
220 | TerminatorKind
::Return
221 | TerminatorKind
::Unreachable
222 | TerminatorKind
::GeneratorDrop
223 | TerminatorKind
::Yield { .. }
224 | TerminatorKind
::SwitchInt { .. }
225 | TerminatorKind
::FalseEdge { .. }
=> None
,
226 TerminatorKind
::Call { cleanup: ref unwind, .. }
227 | TerminatorKind
::Assert { cleanup: ref unwind, .. }
228 | TerminatorKind
::DropAndReplace { ref unwind, .. }
229 | TerminatorKind
::Drop { ref unwind, .. }
230 | TerminatorKind
::FalseUnwind { ref unwind, .. }
231 | TerminatorKind
::InlineAsm { cleanup: ref unwind, .. }
=> Some(unwind
),
235 pub fn unwind_mut(&mut self) -> Option
<&mut Option
<BasicBlock
>> {
237 TerminatorKind
::Goto { .. }
238 | TerminatorKind
::Resume
239 | TerminatorKind
::Abort
240 | TerminatorKind
::Return
241 | TerminatorKind
::Unreachable
242 | TerminatorKind
::GeneratorDrop
243 | TerminatorKind
::Yield { .. }
244 | TerminatorKind
::SwitchInt { .. }
245 | TerminatorKind
::FalseEdge { .. }
=> None
,
246 TerminatorKind
::Call { cleanup: ref mut unwind, .. }
247 | TerminatorKind
::Assert { cleanup: ref mut unwind, .. }
248 | TerminatorKind
::DropAndReplace { ref mut unwind, .. }
249 | TerminatorKind
::Drop { ref mut unwind, .. }
250 | TerminatorKind
::FalseUnwind { ref mut unwind, .. }
251 | TerminatorKind
::InlineAsm { cleanup: ref mut unwind, .. }
=> Some(unwind
),
255 pub fn as_switch(&self) -> Option
<(&Operand
<'tcx
>, &SwitchTargets
)> {
257 TerminatorKind
::SwitchInt { discr, targets }
=> Some((discr
, targets
)),
262 pub fn as_goto(&self) -> Option
<BasicBlock
> {
264 TerminatorKind
::Goto { target }
=> Some(*target
),
270 impl<'tcx
> Debug
for TerminatorKind
<'tcx
> {
271 fn fmt(&self, fmt
: &mut Formatter
<'_
>) -> fmt
::Result
{
273 let successor_count
= self.successors().count();
274 let labels
= self.fmt_successor_labels();
275 assert_eq
!(successor_count
, labels
.len());
277 match successor_count
{
280 1 => write
!(fmt
, " -> {:?}", self.successors().next().unwrap()),
283 write
!(fmt
, " -> [")?
;
284 for (i
, target
) in self.successors().enumerate() {
288 write
!(fmt
, "{}: {:?}", labels
[i
], target
)?
;
296 impl<'tcx
> TerminatorKind
<'tcx
> {
297 /// Writes the "head" part of the terminator; that is, its name and the data it uses to pick the
298 /// successor basic block, if any. The only information not included is the list of possible
299 /// successors, which may be rendered differently between the text and the graphviz format.
300 pub fn fmt_head
<W
: Write
>(&self, fmt
: &mut W
) -> fmt
::Result
{
301 use self::TerminatorKind
::*;
303 Goto { .. }
=> write
!(fmt
, "goto"),
304 SwitchInt { discr, .. }
=> write
!(fmt
, "switchInt({:?})", discr
),
305 Return
=> write
!(fmt
, "return"),
306 GeneratorDrop
=> write
!(fmt
, "generator_drop"),
307 Resume
=> write
!(fmt
, "resume"),
308 Abort
=> write
!(fmt
, "abort"),
309 Yield { value, resume_arg, .. }
=> write
!(fmt
, "{:?} = yield({:?})", resume_arg
, value
),
310 Unreachable
=> write
!(fmt
, "unreachable"),
311 Drop { place, .. }
=> write
!(fmt
, "drop({:?})", place
),
312 DropAndReplace { place, value, .. }
=> {
313 write
!(fmt
, "replace({:?} <- {:?})", place
, value
)
315 Call { func, args, destination, .. }
=> {
316 write
!(fmt
, "{:?} = ", destination
)?
;
317 write
!(fmt
, "{:?}(", func
)?
;
318 for (index
, arg
) in args
.iter().enumerate() {
322 write
!(fmt
, "{:?}", arg
)?
;
326 Assert { cond, expected, msg, .. }
=> {
327 write
!(fmt
, "assert(")?
;
331 write
!(fmt
, "{:?}, ", cond
)?
;
332 msg
.fmt_assert_args(fmt
)?
;
335 FalseEdge { .. }
=> write
!(fmt
, "falseEdge"),
336 FalseUnwind { .. }
=> write
!(fmt
, "falseUnwind"),
337 InlineAsm { template, ref operands, options, .. }
=> {
338 write
!(fmt
, "asm!(\"{}\"", InlineAsmTemplatePiece
::to_string(template
))?
;
341 let print_late
= |&late
| if late { "late" }
else { "" }
;
343 InlineAsmOperand
::In { reg, value }
=> {
344 write
!(fmt
, "in({}) {:?}", reg
, value
)?
;
346 InlineAsmOperand
::Out { reg, late, place: Some(place) }
=> {
347 write
!(fmt
, "{}out({}) {:?}", print_late(late
), reg
, place
)?
;
349 InlineAsmOperand
::Out { reg, late, place: None }
=> {
350 write
!(fmt
, "{}out({}) _", print_late(late
), reg
)?
;
352 InlineAsmOperand
::InOut
{
356 out_place
: Some(out_place
),
360 "in{}out({}) {:?} => {:?}",
367 InlineAsmOperand
::InOut { reg, late, in_value, out_place: None }
=> {
368 write
!(fmt
, "in{}out({}) {:?} => _", print_late(late
), reg
, in_value
)?
;
370 InlineAsmOperand
::Const { value }
=> {
371 write
!(fmt
, "const {:?}", value
)?
;
373 InlineAsmOperand
::SymFn { value }
=> {
374 write
!(fmt
, "sym_fn {:?}", value
)?
;
376 InlineAsmOperand
::SymStatic { def_id }
=> {
377 write
!(fmt
, "sym_static {:?}", def_id
)?
;
381 write
!(fmt
, ", options({:?}))", options
)
386 /// Returns the list of labels for the edges to the successor basic blocks.
387 pub fn fmt_successor_labels(&self) -> Vec
<Cow
<'
static, str>> {
388 use self::TerminatorKind
::*;
390 Return
| Resume
| Abort
| Unreachable
| GeneratorDrop
=> vec
![],
391 Goto { .. }
=> vec
!["".into()],
392 SwitchInt { ref targets, .. }
=> targets
395 .map(|&u
| Cow
::Owned(u
.to_string()))
396 .chain(iter
::once("otherwise".into()))
398 Call { target: Some(_), cleanup: Some(_), .. }
=> {
399 vec
!["return".into(), "unwind".into()]
401 Call { target: Some(_), cleanup: None, .. }
=> vec
!["return".into()],
402 Call { target: None, cleanup: Some(_), .. }
=> vec
!["unwind".into()],
403 Call { target: None, cleanup: None, .. }
=> vec
![],
404 Yield { drop: Some(_), .. }
=> vec
!["resume".into(), "drop".into()],
405 Yield { drop: None, .. }
=> vec
!["resume".into()],
406 DropAndReplace { unwind: None, .. }
| Drop { unwind: None, .. }
=> {
407 vec
!["return".into()]
409 DropAndReplace { unwind: Some(_), .. }
| Drop { unwind: Some(_), .. }
=> {
410 vec
!["return".into(), "unwind".into()]
412 Assert { cleanup: None, .. }
=> vec
!["".into()],
413 Assert { .. }
=> vec
!["success".into(), "unwind".into()],
414 FalseEdge { .. }
=> vec
!["real".into(), "imaginary".into()],
415 FalseUnwind { unwind: Some(_), .. }
=> vec
!["real".into(), "cleanup".into()],
416 FalseUnwind { unwind: None, .. }
=> vec
!["real".into()],
417 InlineAsm { destination: Some(_), cleanup: Some(_), .. }
=> {
418 vec
!["return".into(), "unwind".into()]
420 InlineAsm { destination: Some(_), cleanup: None, .. }
=> vec
!["return".into()],
421 InlineAsm { destination: None, cleanup: Some(_), .. }
=> vec
!["unwind".into()],
422 InlineAsm { destination: None, cleanup: None, .. }
=> vec
![],