Skip to content

Commit 114beea

Browse files
Better explanations and extra functionality in MutableBody (rust-lang#3382)
`MutableBody` is the core data structure for MIR manipulation in instrumentation passes. However, its public methods have limited documentation and unclear semantics. This slowed down the development of instrumentation passes. This PR aims to fix that by: - Clarifying how source instruction shifts when the methods are called; - Adding functionality for inserting basic blocks; - Expanding the support for different terminator types. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses.
1 parent abcb54e commit 114beea

File tree

5 files changed

+146
-93
lines changed

5 files changed

+146
-93
lines changed

kani-compiler/src/kani_middle/transform/body.rs

Lines changed: 103 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -88,16 +88,20 @@ impl MutableBody {
8888

8989
pub fn new_str_operand(&mut self, msg: &str, span: Span) -> Operand {
9090
let literal = MirConst::from_str(msg);
91-
Operand::Constant(ConstOperand { span, user_ty: None, const_: literal })
91+
self.new_const_operand(literal, span)
9292
}
9393

94-
pub fn new_const_operand(&mut self, val: u128, uint_ty: UintTy, span: Span) -> Operand {
94+
pub fn new_uint_operand(&mut self, val: u128, uint_ty: UintTy, span: Span) -> Operand {
9595
let literal = MirConst::try_from_uint(val, uint_ty).unwrap();
96+
self.new_const_operand(literal, span)
97+
}
98+
99+
fn new_const_operand(&mut self, literal: MirConst, span: Span) -> Operand {
96100
Operand::Constant(ConstOperand { span, user_ty: None, const_: literal })
97101
}
98102

99103
/// Create a raw pointer of `*mut type` and return a new local where that value is stored.
100-
pub fn new_cast_ptr(
104+
pub fn insert_ptr_cast(
101105
&mut self,
102106
from: Operand,
103107
pointee_ty: Ty,
@@ -108,13 +112,13 @@ impl MutableBody {
108112
assert!(from.ty(self.locals()).unwrap().kind().is_raw_ptr());
109113
let target_ty = Ty::new_ptr(pointee_ty, mutability);
110114
let rvalue = Rvalue::Cast(CastKind::PtrToPtr, from, target_ty);
111-
self.new_assignment(rvalue, source, position)
115+
self.insert_assignment(rvalue, source, position)
112116
}
113117

114118
/// Add a new assignment for the given binary operation.
115119
///
116120
/// Return the local where the result is saved.
117-
pub fn new_binary_op(
121+
pub fn insert_binary_op(
118122
&mut self,
119123
bin_op: BinOp,
120124
lhs: Operand,
@@ -123,13 +127,13 @@ impl MutableBody {
123127
position: InsertPosition,
124128
) -> Local {
125129
let rvalue = Rvalue::BinaryOp(bin_op, lhs, rhs);
126-
self.new_assignment(rvalue, source, position)
130+
self.insert_assignment(rvalue, source, position)
127131
}
128132

129133
/// Add a new assignment.
130134
///
131-
/// Return local where the result is saved.
132-
pub fn new_assignment(
135+
/// Return the local where the result is saved.
136+
pub fn insert_assignment(
133137
&mut self,
134138
rvalue: Rvalue,
135139
source: &mut SourceInstruction,
@@ -146,9 +150,10 @@ impl MutableBody {
146150
/// Add a new assert to the basic block indicated by the given index.
147151
///
148152
/// The new assertion will have the same span as the source instruction, and the basic block
149-
/// will be split. The source instruction will be adjusted to point to the first instruction in
150-
/// the new basic block.
151-
pub fn add_check(
153+
/// will be split. If `InsertPosition` is `InsertPosition::Before`, `source` will point to the
154+
/// same instruction as before. If `InsertPosition` is `InsertPosition::After`, `source` will
155+
/// point to the new terminator.
156+
pub fn insert_check(
152157
&mut self,
153158
tcx: TyCtxt,
154159
check_type: &CheckType,
@@ -183,7 +188,7 @@ impl MutableBody {
183188
unwind: UnwindAction::Terminate,
184189
};
185190
let terminator = Terminator { kind, span };
186-
self.split_bb(source, position, terminator);
191+
self.insert_terminator(source, position, terminator);
187192
}
188193
CheckType::Panic | CheckType::NoCore => {
189194
tcx.sess
@@ -199,10 +204,11 @@ impl MutableBody {
199204

200205
/// Add a new call to the basic block indicated by the given index.
201206
///
202-
/// The new call will have the same span as the source instruction, and the basic block
203-
/// will be split. The source instruction will be adjusted to point to the first instruction in
204-
/// the new basic block.
205-
pub fn add_call(
207+
/// The new call will have the same span as the source instruction, and the basic block will be
208+
/// split. If `InsertPosition` is `InsertPosition::Before`, `source` will point to the same
209+
/// instruction as before. If `InsertPosition` is `InsertPosition::After`, `source` will point
210+
/// to the new terminator.
211+
pub fn insert_call(
206212
&mut self,
207213
callee: &Instance,
208214
source: &mut SourceInstruction,
@@ -222,13 +228,14 @@ impl MutableBody {
222228
unwind: UnwindAction::Terminate,
223229
};
224230
let terminator = Terminator { kind, span };
225-
self.split_bb(source, position, terminator);
231+
self.insert_terminator(source, position, terminator);
226232
}
227233

228-
/// Split a basic block and use the new terminator in the basic block that was split.
229-
///
230-
/// The source is updated to point to the same instruction which is now in the new basic block.
231-
pub fn split_bb(
234+
/// Split a basic block and use the new terminator in the basic block that was split. If
235+
/// `InsertPosition` is `InsertPosition::Before`, `source` will point to the same instruction as
236+
/// before. If `InsertPosition` is `InsertPosition::After`, `source` will point to the new
237+
/// terminator.
238+
fn split_bb(
232239
&mut self,
233240
source: &mut SourceInstruction,
234241
position: InsertPosition,
@@ -245,6 +252,7 @@ impl MutableBody {
245252
}
246253

247254
/// Split a basic block right before the source location.
255+
/// `source` will point to the same instruction as before after the function is done.
248256
fn split_bb_before(&mut self, source: &mut SourceInstruction, new_term: Terminator) {
249257
let new_bb_idx = self.blocks.len();
250258
let (idx, bb) = match source {
@@ -268,46 +276,77 @@ impl MutableBody {
268276
}
269277

270278
/// Split a basic block right after the source location.
279+
/// `source` will point to the new terminator after the function is done.
271280
fn split_bb_after(&mut self, source: &mut SourceInstruction, mut new_term: Terminator) {
272281
let new_bb_idx = self.blocks.len();
273282
match source {
274283
// Split the current block after the statement located at `source`
275284
// and move the remaining statements into the new one.
276285
SourceInstruction::Statement { idx, bb } => {
277286
let (orig_idx, orig_bb) = (*idx, *bb);
278-
*idx = 0;
279-
*bb = new_bb_idx;
280287
let old_term = mem::replace(&mut self.blocks[orig_bb].terminator, new_term);
281288
let bb_stmts = &mut self.blocks[orig_bb].statements;
282289
let remaining = bb_stmts.split_off(orig_idx + 1);
283290
let new_bb = BasicBlock { statements: remaining, terminator: old_term };
284291
self.blocks.push(new_bb);
292+
// Update the source to point at the terminator.
293+
*source = SourceInstruction::Terminator { bb: orig_bb };
285294
}
286295
// Make the terminator at `source` point at the new block,
287296
// the terminator of which is a simple Goto instruction.
288297
SourceInstruction::Terminator { bb } => {
289-
let current_terminator = &mut self.blocks.get_mut(*bb).unwrap().terminator;
290-
// Kani can only instrument function calls like this.
291-
match (&mut current_terminator.kind, &mut new_term.kind) {
292-
(
293-
TerminatorKind::Call { target: Some(target_bb), .. },
294-
TerminatorKind::Call { target: Some(new_target_bb), .. },
295-
) => {
296-
// Set the new terminator to point where previous terminator pointed.
297-
*new_target_bb = *target_bb;
298-
// Point the current terminator to the new terminator's basic block.
299-
*target_bb = new_bb_idx;
300-
// Update the current poisition.
301-
*bb = new_bb_idx;
302-
self.blocks.push(BasicBlock { statements: vec![], terminator: new_term });
303-
}
304-
_ => unimplemented!("Kani can only split blocks after calls."),
305-
};
298+
let current_term = &mut self.blocks.get_mut(*bb).unwrap().terminator;
299+
let target_bb = get_mut_target_ref(current_term);
300+
let new_target_bb = get_mut_target_ref(&mut new_term);
301+
// Set the new terminator to point where previous terminator pointed.
302+
*new_target_bb = *target_bb;
303+
// Point the current terminator to the new terminator's basic block.
304+
*target_bb = new_bb_idx;
305+
// Update the source to point at the terminator.
306+
*bb = new_bb_idx;
307+
self.blocks.push(BasicBlock { statements: vec![], terminator: new_term });
306308
}
307309
};
308310
}
309311

310-
/// Insert statement before or after the source instruction and update the source as needed.
312+
/// Insert basic block before or after the source instruction and update `source` accordingly. If
313+
/// `InsertPosition` is `InsertPosition::Before`, `source` will point to the same instruction as
314+
/// before. If `InsertPosition` is `InsertPosition::After`, `source` will point to the
315+
/// terminator of the newly inserted basic block.
316+
pub fn insert_bb(
317+
&mut self,
318+
mut bb: BasicBlock,
319+
source: &mut SourceInstruction,
320+
position: InsertPosition,
321+
) {
322+
// Splitting adds 1 block, so the added block index is len + 1;
323+
let split_bb_idx = self.blocks().len();
324+
let inserted_bb_idx = self.blocks().len() + 1;
325+
// Update the terminator of the basic block to point at the remaining part of the split
326+
// basic block.
327+
let target = get_mut_target_ref(&mut bb.terminator);
328+
*target = split_bb_idx;
329+
let new_term = Terminator {
330+
kind: TerminatorKind::Goto { target: inserted_bb_idx },
331+
span: source.span(&self.blocks),
332+
};
333+
self.split_bb(source, position, new_term);
334+
self.blocks.push(bb);
335+
}
336+
337+
pub fn insert_terminator(
338+
&mut self,
339+
source: &mut SourceInstruction,
340+
position: InsertPosition,
341+
terminator: Terminator,
342+
) {
343+
self.split_bb(source, position, terminator);
344+
}
345+
346+
/// Insert statement before or after the source instruction and update the source as needed. If
347+
/// `InsertPosition` is `InsertPosition::Before`, `source` will point to the same instruction as
348+
/// before. If `InsertPosition` is `InsertPosition::After`, `source` will point to the
349+
/// newly inserted statement.
311350
pub fn insert_stmt(
312351
&mut self,
313352
new_stmt: Statement,
@@ -338,22 +377,18 @@ impl MutableBody {
338377
SourceInstruction::Terminator { bb } => {
339378
// Create a new basic block, as we need to append a statement after the terminator.
340379
let current_terminator = &mut self.blocks.get_mut(*bb).unwrap().terminator;
341-
// Kani can only instrument function calls in this way.
342-
match &mut current_terminator.kind {
343-
TerminatorKind::Call { target: Some(target_bb), .. } => {
344-
*source = SourceInstruction::Statement { idx: 0, bb: new_bb_idx };
345-
let new_bb = BasicBlock {
346-
statements: vec![new_stmt],
347-
terminator: Terminator {
348-
kind: TerminatorKind::Goto { target: *target_bb },
349-
span,
350-
},
351-
};
352-
*target_bb = new_bb_idx;
353-
self.blocks.push(new_bb);
354-
}
355-
_ => unimplemented!("Kani can only insert statements after calls."),
380+
// Update target of the terminator.
381+
let target_bb = get_mut_target_ref(current_terminator);
382+
*source = SourceInstruction::Statement { idx: 0, bb: new_bb_idx };
383+
let new_bb = BasicBlock {
384+
statements: vec![new_stmt],
385+
terminator: Terminator {
386+
kind: TerminatorKind::Goto { target: *target_bb },
387+
span,
388+
},
356389
};
390+
*target_bb = new_bb_idx;
391+
self.blocks.push(new_bb);
357392
}
358393
}
359394
}
@@ -574,3 +609,15 @@ pub trait MutMirVisitor {
574609
}
575610
}
576611
}
612+
613+
fn get_mut_target_ref(terminator: &mut Terminator) -> &mut BasicBlockIdx {
614+
match &mut terminator.kind {
615+
TerminatorKind::Assert { target, .. }
616+
| TerminatorKind::Drop { target, .. }
617+
| TerminatorKind::Goto { target }
618+
| TerminatorKind::Call { target: Some(target), .. } => target,
619+
_ => unimplemented!(
620+
"Kani can only insert instructions after terminators that have a `target` field."
621+
),
622+
}
623+
}

kani-compiler/src/kani_middle/transform/check_uninit/mod.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ impl UninitPass {
230230
*pointee_info.ty(),
231231
);
232232
collect_skipped(&operation, body, skip_first);
233-
body.add_call(
233+
body.insert_call(
234234
&is_ptr_initialized_instance,
235235
source,
236236
operation.position(),
@@ -257,7 +257,7 @@ impl UninitPass {
257257
let layout_operand =
258258
mk_layout_operand(body, source, operation.position(), &element_layout);
259259
collect_skipped(&operation, body, skip_first);
260-
body.add_call(
260+
body.insert_call(
261261
&is_ptr_initialized_instance,
262262
source,
263263
operation.position(),
@@ -276,7 +276,7 @@ impl UninitPass {
276276
// Make sure all non-padding bytes are initialized.
277277
collect_skipped(&operation, body, skip_first);
278278
let ptr_operand_ty = ptr_operand.ty(body.locals()).unwrap();
279-
body.add_check(
279+
body.insert_check(
280280
tcx,
281281
&self.check_type,
282282
source,
@@ -345,7 +345,7 @@ impl UninitPass {
345345
*pointee_info.ty(),
346346
);
347347
collect_skipped(&operation, body, skip_first);
348-
body.add_call(
348+
body.insert_call(
349349
&set_ptr_initialized_instance,
350350
source,
351351
operation.position(),
@@ -372,7 +372,7 @@ impl UninitPass {
372372
let layout_operand =
373373
mk_layout_operand(body, source, operation.position(), &element_layout);
374374
collect_skipped(&operation, body, skip_first);
375-
body.add_call(
375+
body.insert_call(
376376
&set_ptr_initialized_instance,
377377
source,
378378
operation.position(),
@@ -408,8 +408,8 @@ impl UninitPass {
408408
span,
409409
user_ty: None,
410410
}));
411-
let result = body.new_assignment(rvalue, source, position);
412-
body.add_check(tcx, &self.check_type, source, position, result, reason);
411+
let result = body.insert_assignment(rvalue, source, position);
412+
body.insert_check(tcx, &self.check_type, source, position, result, reason);
413413
}
414414
}
415415

@@ -432,7 +432,7 @@ pub fn mk_layout_operand(
432432
layout_byte_mask: &[bool],
433433
) -> Operand {
434434
Operand::Move(Place {
435-
local: body.new_assignment(
435+
local: body.insert_assignment(
436436
Rvalue::Aggregate(
437437
AggregateKind::Array(Ty::bool_ty()),
438438
layout_byte_mask
@@ -531,7 +531,7 @@ fn inject_memory_init_setup(
531531
)
532532
.unwrap();
533533

534-
new_body.add_call(
534+
new_body.insert_call(
535535
&memory_initialization_init,
536536
&mut source,
537537
InsertPosition::Before,

kani-compiler/src/kani_middle/transform/check_uninit/uninit_visitor.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ impl MemoryInitOp {
5757
Operand::Copy(place) | Operand::Move(place) => place,
5858
Operand::Constant(_) => unreachable!(),
5959
};
60-
body.new_assignment(
60+
body.insert_assignment(
6161
Rvalue::AddressOf(Mutability::Not, place.clone()),
6262
source,
6363
self.position(),

0 commit comments

Comments
 (0)