1 use rustc_index
::vec
::{Idx, IndexVec}
;
2 use rustc_middle
::mir
::*;
3 use rustc_middle
::ty
::Ty
;
6 /// This struct represents a patch to MIR, which can add
7 /// new statements and basic blocks and patch over block
9 pub struct MirPatch
<'tcx
> {
10 patch_map
: IndexVec
<BasicBlock
, Option
<TerminatorKind
<'tcx
>>>,
11 new_blocks
: Vec
<BasicBlockData
<'tcx
>>,
12 new_statements
: Vec
<(Location
, StatementKind
<'tcx
>)>,
13 new_locals
: Vec
<LocalDecl
<'tcx
>>,
14 resume_block
: Option
<BasicBlock
>,
19 impl<'tcx
> MirPatch
<'tcx
> {
20 pub fn new(body
: &Body
<'tcx
>) -> Self {
21 let mut result
= MirPatch
{
22 patch_map
: IndexVec
::from_elem(None
, body
.basic_blocks()),
24 new_statements
: vec
![],
26 next_local
: body
.local_decls
.len(),
31 // Check if we already have a resume block
32 for (bb
, block
) in body
.basic_blocks().iter_enumerated() {
33 if let TerminatorKind
::Resume
= block
.terminator().kind
&& block
.statements
.is_empty() {
34 result
.resume_block
= Some(bb
);
42 pub fn resume_block(&mut self) -> BasicBlock
{
43 if let Some(bb
) = self.resume_block
{
47 let bb
= self.new_block(BasicBlockData
{
49 terminator
: Some(Terminator
{
50 source_info
: SourceInfo
::outermost(self.body_span
),
51 kind
: TerminatorKind
::Resume
,
55 self.resume_block
= Some(bb
);
59 pub fn is_patched(&self, bb
: BasicBlock
) -> bool
{
60 self.patch_map
[bb
].is_some()
63 pub fn terminator_loc(&self, body
: &Body
<'tcx
>, bb
: BasicBlock
) -> Location
{
64 let offset
= match bb
.index().checked_sub(body
.basic_blocks().len()) {
65 Some(index
) => self.new_blocks
[index
].statements
.len(),
66 None
=> body
[bb
].statements
.len(),
68 Location { block: bb, statement_index: offset }
71 pub fn new_local_with_info(
75 local_info
: Option
<Box
<LocalInfo
<'tcx
>>>,
77 let index
= self.next_local
;
79 let mut new_decl
= LocalDecl
::new(ty
, span
);
80 new_decl
.local_info
= local_info
;
81 self.new_locals
.push(new_decl
);
82 Local
::new(index
as usize)
85 pub fn new_temp(&mut self, ty
: Ty
<'tcx
>, span
: Span
) -> Local
{
86 self.new_local_with_info(ty
, span
, None
)
89 pub fn new_internal(&mut self, ty
: Ty
<'tcx
>, span
: Span
) -> Local
{
90 let index
= self.next_local
;
92 self.new_locals
.push(LocalDecl
::new(ty
, span
).internal());
93 Local
::new(index
as usize)
96 pub fn new_block(&mut self, data
: BasicBlockData
<'tcx
>) -> BasicBlock
{
97 let block
= BasicBlock
::new(self.patch_map
.len());
98 debug
!("MirPatch: new_block: {:?}: {:?}", block
, data
);
99 self.new_blocks
.push(data
);
100 self.patch_map
.push(None
);
104 pub fn patch_terminator(&mut self, block
: BasicBlock
, new
: TerminatorKind
<'tcx
>) {
105 assert
!(self.patch_map
[block
].is_none());
106 debug
!("MirPatch: patch_terminator({:?}, {:?})", block
, new
);
107 self.patch_map
[block
] = Some(new
);
110 pub fn add_statement(&mut self, loc
: Location
, stmt
: StatementKind
<'tcx
>) {
111 debug
!("MirPatch: add_statement({:?}, {:?})", loc
, stmt
);
112 self.new_statements
.push((loc
, stmt
));
115 pub fn add_assign(&mut self, loc
: Location
, place
: Place
<'tcx
>, rv
: Rvalue
<'tcx
>) {
116 self.add_statement(loc
, StatementKind
::Assign(Box
::new((place
, rv
))));
119 pub fn apply(self, body
: &mut Body
<'tcx
>) {
121 "MirPatch: {:?} new temps, starting from index {}: {:?}",
122 self.new_locals
.len(),
123 body
.local_decls
.len(),
127 "MirPatch: {} new blocks, starting from index {}",
128 self.new_blocks
.len(),
129 body
.basic_blocks().len()
131 let bbs
= if self.patch_map
.is_empty() && self.new_blocks
.is_empty() {
132 body
.basic_blocks
.as_mut_preserves_cfg()
134 body
.basic_blocks
.as_mut()
136 bbs
.extend(self.new_blocks
);
137 body
.local_decls
.extend(self.new_locals
);
138 for (src
, patch
) in self.patch_map
.into_iter_enumerated() {
139 if let Some(patch
) = patch
{
140 debug
!("MirPatch: patching block {:?}", src
);
141 bbs
[src
].terminator_mut().kind
= patch
;
145 let mut new_statements
= self.new_statements
;
146 new_statements
.sort_by_key(|s
| s
.0);
149 let mut last_bb
= START_BLOCK
;
150 let mut stmts_and_targets
: Vec
<(Statement
<'_
>, BasicBlock
)> = Vec
::new();
151 for (mut loc
, stmt
) in new_statements
{
152 if loc
.block
!= last_bb
{
156 debug
!("MirPatch: adding statement {:?} at loc {:?}+{}", stmt
, loc
, delta
);
157 loc
.statement_index
+= delta
;
158 let source_info
= Self::source_info_for_index(&body
[loc
.block
], loc
);
160 // For mir-opt `Derefer` to work in all cases we need to
161 // get terminator's targets and apply the statement to all of them.
162 if loc
.statement_index
> body
[loc
.block
].statements
.len() {
163 let term
= body
[loc
.block
].terminator();
164 for i
in term
.successors() {
165 stmts_and_targets
.push((Statement { source_info, kind: stmt.clone() }
, i
));
173 .insert(loc
.statement_index
, Statement { source_info, kind: stmt }
);
177 for (stmt
, target
) in stmts_and_targets
.into_iter().rev() {
178 body
[target
].statements
.insert(0, stmt
);
182 pub fn source_info_for_index(data
: &BasicBlockData
<'_
>, loc
: Location
) -> SourceInfo
{
183 match data
.statements
.get(loc
.statement_index
) {
184 Some(stmt
) => stmt
.source_info
,
185 None
=> data
.terminator().source_info
,
189 pub fn source_info_for_location(&self, body
: &Body
<'tcx
>, loc
: Location
) -> SourceInfo
{
190 let data
= match loc
.block
.index().checked_sub(body
.basic_blocks().len()) {
191 Some(new
) => &self.new_blocks
[new
],
192 None
=> &body
[loc
.block
],
194 Self::source_info_for_index(data
, loc
)