@@ -16,9 +16,8 @@ use crate::kani_middle::{
16
16
} ;
17
17
use rustc_middle:: ty:: TyCtxt ;
18
18
use stable_mir:: mir:: {
19
- mono:: Instance ,
20
- visit:: { Location , PlaceContext } ,
21
- BasicBlock , MirVisitor , Operand , Place , Rvalue ,
19
+ mono:: Instance , BasicBlock , CopyNonOverlapping , InlineAsmOperand , NonDivergingIntrinsic ,
20
+ Operand , Place , Rvalue , Statement , StatementKind , Terminator , TerminatorKind ,
22
21
} ;
23
22
use std:: collections:: HashSet ;
24
23
@@ -33,6 +32,13 @@ pub struct InstrumentationVisitor<'a, 'tcx> {
33
32
tcx : TyCtxt < ' tcx > ,
34
33
}
35
34
35
+ enum PlaceOperation {
36
+ Read ,
37
+ Write ,
38
+ Deinit ,
39
+ Noop ,
40
+ }
41
+
36
42
impl < ' a , ' tcx > TargetFinder for InstrumentationVisitor < ' a , ' tcx > {
37
43
fn find_next (
38
44
& mut self ,
@@ -44,14 +50,11 @@ impl<'a, 'tcx> TargetFinder for InstrumentationVisitor<'a, 'tcx> {
44
50
SourceInstruction :: Statement { idx, bb } => {
45
51
let BasicBlock { statements, .. } = & body. blocks ( ) [ bb] ;
46
52
let stmt = & statements[ idx] ;
47
- self . visit_statement ( stmt, self . __location_hack_remove_before_merging ( stmt . span ) )
53
+ self . check_statement ( stmt)
48
54
}
49
55
SourceInstruction :: Terminator { bb } => {
50
56
let BasicBlock { terminator, .. } = & body. blocks ( ) [ bb] ;
51
- self . visit_terminator (
52
- terminator,
53
- self . __location_hack_remove_before_merging ( terminator. span ) ,
54
- )
57
+ self . check_terminator ( terminator)
55
58
}
56
59
}
57
60
self . target . clone ( )
@@ -76,17 +79,152 @@ impl<'a, 'tcx> InstrumentationVisitor<'a, 'tcx> {
76
79
}
77
80
}
78
81
79
- impl < ' a , ' tcx > MirVisitor for InstrumentationVisitor < ' a , ' tcx > {
80
- fn visit_rvalue ( & mut self , rvalue : & Rvalue , location : Location ) {
82
+ impl < ' a , ' tcx > InstrumentationVisitor < ' a , ' tcx > {
83
+ fn check_statement ( & mut self , stmt : & Statement ) {
84
+ let Statement { kind, .. } = stmt;
85
+ match kind {
86
+ StatementKind :: Assign ( place, rvalue) => {
87
+ self . check_place ( place, PlaceOperation :: Write ) ;
88
+ self . check_rvalue ( rvalue) ;
89
+ }
90
+ StatementKind :: FakeRead ( _, place) => {
91
+ // According to the compiler docs, "When executed at runtime this is a nop." For
92
+ // more info, see
93
+ // https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/mir/enum.StatementKind.html#variant.FakeRead,
94
+ self . check_place ( place, PlaceOperation :: Noop ) ;
95
+ }
96
+ StatementKind :: SetDiscriminant { place, .. } => {
97
+ self . check_place ( place, PlaceOperation :: Write ) ;
98
+ }
99
+ StatementKind :: Deinit ( place) => {
100
+ self . check_place ( place, PlaceOperation :: Deinit ) ;
101
+ }
102
+ StatementKind :: Retag ( _, place) => {
103
+ // According to the compiler docs, "These statements are currently only interpreted
104
+ // by miri and only generated when -Z mir-emit-retag is passed." For more info, see
105
+ // https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/mir/enum.StatementKind.html#variant.Retag,
106
+ self . check_place ( place, PlaceOperation :: Noop ) ;
107
+ }
108
+ StatementKind :: PlaceMention ( place) => {
109
+ // According to the compiler docs, "When executed at runtime, this computes the
110
+ // given place, but then discards it without doing a load." For more info, see
111
+ // https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/mir/enum.StatementKind.html#variant.PlaceMention,
112
+ self . check_place ( place, PlaceOperation :: Noop ) ;
113
+ }
114
+ StatementKind :: AscribeUserType { place, .. } => {
115
+ // According to the compiler docs, "When executed at runtime this is a nop." For
116
+ // more info, see
117
+ // https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/mir/enum.StatementKind.html#variant.AscribeUserType,
118
+ self . check_place ( place, PlaceOperation :: Noop ) ;
119
+ }
120
+
121
+ StatementKind :: Intrinsic ( intrisic) => match intrisic {
122
+ NonDivergingIntrinsic :: Assume ( operand) => {
123
+ self . check_operand ( operand) ;
124
+ }
125
+ NonDivergingIntrinsic :: CopyNonOverlapping ( CopyNonOverlapping {
126
+ src,
127
+ dst,
128
+ count,
129
+ } ) => {
130
+ self . check_operand ( src) ;
131
+ self . check_operand ( dst) ;
132
+ self . check_operand ( count) ;
133
+ }
134
+ } ,
135
+ StatementKind :: StorageLive ( _)
136
+ | StatementKind :: StorageDead ( _)
137
+ | StatementKind :: Coverage ( _)
138
+ | StatementKind :: ConstEvalCounter
139
+ | StatementKind :: Nop => { }
140
+ }
141
+ }
142
+
143
+ fn check_terminator ( & mut self , term : & Terminator ) {
144
+ let Terminator { kind, .. } = term;
145
+ match kind {
146
+ TerminatorKind :: Goto { .. }
147
+ | TerminatorKind :: Resume
148
+ | TerminatorKind :: Abort
149
+ | TerminatorKind :: Unreachable
150
+ | TerminatorKind :: Return => { }
151
+ TerminatorKind :: Assert { cond, .. } => {
152
+ self . check_operand ( cond) ;
153
+ }
154
+ TerminatorKind :: Drop { place, .. } => {
155
+ // According to the documentation, "After drop elaboration: Drop terminators are a
156
+ // complete nop for types that have no drop glue. For other types, Drop terminators
157
+ // behave exactly like a call to core::mem::drop_in_place with a pointer to the
158
+ // given place." Since we check arguments when instrumenting function calls, perhaps
159
+ // we need to check that one, too. For more info, see:
160
+ // https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/mir/enum.TerminatorKind.html#variant.Drop
161
+ self . check_place ( place, PlaceOperation :: Read ) ;
162
+ }
163
+ TerminatorKind :: Call { func, args, destination, target : _, unwind : _ } => {
164
+ self . check_operand ( func) ;
165
+ for arg in args {
166
+ self . check_operand ( arg) ;
167
+ }
168
+ self . check_place ( destination, PlaceOperation :: Write ) ;
169
+ }
170
+ TerminatorKind :: InlineAsm { operands, .. } => {
171
+ for op in operands {
172
+ let InlineAsmOperand { in_value, out_place, raw_rpr : _ } = op;
173
+ if let Some ( input) = in_value {
174
+ self . check_operand ( input) ;
175
+ }
176
+ if let Some ( output) = out_place {
177
+ self . check_place ( output, PlaceOperation :: Write ) ;
178
+ }
179
+ }
180
+ }
181
+ TerminatorKind :: SwitchInt { discr, .. } => {
182
+ self . check_operand ( discr) ;
183
+ }
184
+ }
185
+ }
186
+
187
+ fn check_rvalue ( & mut self , rvalue : & Rvalue ) {
81
188
match rvalue {
82
- Rvalue :: AddressOf ( ..) | Rvalue :: Ref ( ..) => {
83
- // These operations are always legitimate for us.
189
+ Rvalue :: AddressOf ( _, place) | Rvalue :: Ref ( _, _, place) => {
190
+ self . check_place ( place, PlaceOperation :: Noop ) ;
191
+ }
192
+ Rvalue :: Aggregate ( _, operands) => {
193
+ for op in operands {
194
+ self . check_operand ( op) ;
195
+ }
196
+ }
197
+ Rvalue :: BinaryOp ( _, lhs, rhs) | Rvalue :: CheckedBinaryOp ( _, lhs, rhs) => {
198
+ self . check_operand ( lhs) ;
199
+ self . check_operand ( rhs) ;
200
+ }
201
+ Rvalue :: Cast ( _, op, _)
202
+ | Rvalue :: Repeat ( op, _)
203
+ | Rvalue :: ShallowInitBox ( op, ..)
204
+ | Rvalue :: UnaryOp ( _, op)
205
+ | Rvalue :: Use ( op) => {
206
+ self . check_operand ( op) ;
207
+ }
208
+ Rvalue :: CopyForDeref ( place) | Rvalue :: Discriminant ( place) | Rvalue :: Len ( place) => {
209
+ self . check_place ( place, PlaceOperation :: Read ) ;
210
+ }
211
+ Rvalue :: ThreadLocalRef ( ..) | Rvalue :: NullaryOp ( ..) => { }
212
+ }
213
+ }
214
+
215
+ fn check_operand ( & mut self , operand : & Operand ) {
216
+ match operand {
217
+ Operand :: Copy ( place) | Operand :: Move ( place) => {
218
+ self . check_place ( place, PlaceOperation :: Read )
219
+ }
220
+ Operand :: Constant ( _) => {
221
+ // Those should be safe to skip, as they are either constants or statics. In the
222
+ // latter case, we handle them in regular uninit visior
84
223
}
85
- _ => self . super_rvalue ( rvalue, location) ,
86
224
}
87
225
}
88
226
89
- fn visit_place ( & mut self , place : & Place , ptx : PlaceContext , location : Location ) {
227
+ fn check_place ( & mut self , place : & Place , place_operation : PlaceOperation ) {
90
228
// Match the place by whatever it is pointing to and find an intersection with the targets.
91
229
if self
92
230
. points_to
@@ -95,18 +233,31 @@ impl<'a, 'tcx> MirVisitor for InstrumentationVisitor<'a, 'tcx> {
95
233
. next ( )
96
234
. is_some ( )
97
235
{
98
- // If we are mutating the place, initialize it.
99
- if ptx. is_mutating ( ) {
100
- self . push_target ( MemoryInitOp :: SetRef {
101
- operand : Operand :: Copy ( place. clone ( ) ) ,
102
- value : true ,
103
- position : InsertPosition :: After ,
104
- } ) ;
105
- } else {
106
- // Otherwise, check its initialization.
107
- self . push_target ( MemoryInitOp :: CheckRef { operand : Operand :: Copy ( place. clone ( ) ) } ) ;
236
+ match place_operation {
237
+ PlaceOperation :: Write => {
238
+ // If we are mutating the place, initialize it.
239
+ self . push_target ( MemoryInitOp :: SetRef {
240
+ operand : Operand :: Copy ( place. clone ( ) ) ,
241
+ value : true ,
242
+ position : InsertPosition :: After ,
243
+ } )
244
+ }
245
+ PlaceOperation :: Deinit => {
246
+ // If we are mutating the place, initialize it.
247
+ self . push_target ( MemoryInitOp :: SetRef {
248
+ operand : Operand :: Copy ( place. clone ( ) ) ,
249
+ value : false ,
250
+ position : InsertPosition :: After ,
251
+ } )
252
+ }
253
+ PlaceOperation :: Read => {
254
+ // Otherwise, check its initialization.
255
+ self . push_target ( MemoryInitOp :: CheckRef {
256
+ operand : Operand :: Copy ( place. clone ( ) ) ,
257
+ } ) ;
258
+ }
259
+ PlaceOperation :: Noop => { }
108
260
}
109
261
}
110
- self . super_place ( place, ptx, location)
111
262
}
112
263
}
0 commit comments