@@ -33,7 +33,7 @@ use rustc_interface::Config;
33
33
use rustc_middle:: ty:: TyCtxt ;
34
34
use rustc_session:: config:: { ErrorOutputType , OutputType } ;
35
35
use rustc_span:: ErrorGuaranteed ;
36
- use std:: collections:: HashMap ;
36
+ use std:: collections:: { BTreeMap , HashMap } ;
37
37
use std:: fs:: File ;
38
38
use std:: io:: BufWriter ;
39
39
use std:: mem;
@@ -67,7 +67,7 @@ fn backend(queries: Arc<Mutex<QueryDb>>) -> Box<CodegenBackend> {
67
67
type HarnessId = DefPathHash ;
68
68
69
69
/// A set of stubs.
70
- type Stubs = HashMap < DefPathHash , DefPathHash > ;
70
+ type Stubs = BTreeMap < DefPathHash , DefPathHash > ;
71
71
72
72
#[ derive( Debug ) ]
73
73
struct HarnessInfo {
@@ -109,14 +109,15 @@ enum CompilationStage {
109
109
/// Stage where the compiler will perform codegen of all harnesses that don't use stub.
110
110
CodegenNoStubs {
111
111
target_harnesses : Vec < HarnessId > ,
112
- next_harnesses : Vec < HarnessId > ,
112
+ next_harnesses : Vec < Vec < HarnessId > > ,
113
113
all_harnesses : HashMap < HarnessId , HarnessInfo > ,
114
114
} ,
115
- /// Stage where the compiler will codegen harnesses that use stub, one at a time.
116
- /// Note: We could potentially codegen all harnesses that have the same list of stubs.
115
+ /// Stage where the compiler will codegen harnesses that use stub, one group at a time.
116
+ /// The harnesses at this stage are grouped according to the stubs they are using. For now,
117
+ /// we ensure they have the exact same set of stubs.
117
118
CodegenWithStubs {
118
- target_harness : HarnessId ,
119
- next_harnesses : Vec < HarnessId > ,
119
+ target_harnesses : Vec < HarnessId > ,
120
+ next_harnesses : Vec < Vec < HarnessId > > ,
120
121
all_harnesses : HashMap < HarnessId , HarnessInfo > ,
121
122
} ,
122
123
Done ,
@@ -166,7 +167,9 @@ impl KaniCompiler {
166
167
CompilationStage :: CodegenNoStubs { .. } => {
167
168
unreachable ! ( "This stage should always run in the same session as Init" ) ;
168
169
}
169
- CompilationStage :: CodegenWithStubs { target_harness, all_harnesses, .. } => {
170
+ CompilationStage :: CodegenWithStubs { target_harnesses, all_harnesses, .. } => {
171
+ assert ! ( !target_harnesses. is_empty( ) , "expected at least one target harness" ) ;
172
+ let target_harness = & target_harnesses[ 0 ] ;
170
173
let extra_arg =
171
174
stubbing:: mk_rustc_arg ( & all_harnesses[ & target_harness] . stub_map ) ;
172
175
let mut args = orig_args. clone ( ) ;
@@ -192,9 +195,10 @@ impl KaniCompiler {
192
195
}
193
196
CompilationStage :: CodegenNoStubs { next_harnesses, all_harnesses, .. }
194
197
| CompilationStage :: CodegenWithStubs { next_harnesses, all_harnesses, .. } => {
195
- if let Some ( target_harness) = next_harnesses. pop ( ) {
198
+ if let Some ( target_harnesses) = next_harnesses. pop ( ) {
199
+ assert ! ( !target_harnesses. is_empty( ) , "expected at least one target harness" ) ;
196
200
CompilationStage :: CodegenWithStubs {
197
- target_harness ,
201
+ target_harnesses ,
198
202
next_harnesses : mem:: take ( next_harnesses) ,
199
203
all_harnesses : mem:: take ( all_harnesses) ,
200
204
}
@@ -210,6 +214,7 @@ impl KaniCompiler {
210
214
211
215
/// Run the Rust compiler with the given arguments and pass `&mut self` to handle callbacks.
212
216
fn run_compilation_session ( & mut self , args : & [ String ] ) -> Result < ( ) , ErrorGuaranteed > {
217
+ debug ! ( ?args, "run_compilation_session" ) ;
213
218
let queries = self . queries . clone ( ) ;
214
219
let mut compiler = RunCompiler :: new ( args, self ) ;
215
220
compiler. set_make_codegen_backend ( Some ( Box :: new ( move |_cfg| backend ( queries) ) ) ) ;
@@ -249,7 +254,7 @@ impl KaniCompiler {
249
254
// Even if no_stubs is empty we still need to store metadata.
250
255
CompilationStage :: CodegenNoStubs {
251
256
target_harnesses : no_stubs,
252
- next_harnesses : with_stubs,
257
+ next_harnesses : group_by_stubs ( with_stubs, & all_harnesses ) ,
253
258
all_harnesses,
254
259
}
255
260
} else {
@@ -266,7 +271,15 @@ impl KaniCompiler {
266
271
fn prepare_codegen ( & mut self ) -> Compilation {
267
272
debug ! ( stage=?self . stage, "prepare_codegen" ) ;
268
273
match & self . stage {
269
- CompilationStage :: CodegenNoStubs { target_harnesses, all_harnesses, .. } => {
274
+ CompilationStage :: CodegenNoStubs { target_harnesses, all_harnesses, .. }
275
+ | CompilationStage :: CodegenWithStubs { target_harnesses, all_harnesses, .. } => {
276
+ debug ! (
277
+ harnesses=?target_harnesses
278
+ . iter( )
279
+ . map( |h| & all_harnesses[ h] . metadata. pretty_name)
280
+ . collect:: <Vec <_>>( ) ,
281
+ "prepare_codegen"
282
+ ) ;
270
283
let queries = & mut ( * self . queries . lock ( ) . unwrap ( ) ) ;
271
284
queries. harnesses_info = target_harnesses
272
285
. iter ( )
@@ -276,14 +289,6 @@ impl KaniCompiler {
276
289
. collect ( ) ;
277
290
Compilation :: Continue
278
291
}
279
- CompilationStage :: CodegenWithStubs { target_harness, all_harnesses, .. } => {
280
- let queries = & mut ( * self . queries . lock ( ) . unwrap ( ) ) ;
281
- queries. harnesses_info = HashMap :: from ( [ (
282
- * target_harness,
283
- all_harnesses[ & target_harness] . metadata . goto_file . clone ( ) . unwrap ( ) ,
284
- ) ] ) ;
285
- Compilation :: Continue
286
- }
287
292
CompilationStage :: Init | CompilationStage :: Done => unreachable ! ( ) ,
288
293
}
289
294
}
@@ -314,6 +319,18 @@ impl KaniCompiler {
314
319
}
315
320
}
316
321
322
+ /// Group the harnesses by their stubs.
323
+ fn group_by_stubs (
324
+ harnesses : Vec < HarnessId > ,
325
+ all_harnesses : & HashMap < HarnessId , HarnessInfo > ,
326
+ ) -> Vec < Vec < HarnessId > > {
327
+ let mut per_stubs: BTreeMap < & Stubs , Vec < HarnessId > > = BTreeMap :: default ( ) ;
328
+ for harness in harnesses {
329
+ per_stubs. entry ( & all_harnesses[ & harness] . stub_map ) . or_default ( ) . push ( harness)
330
+ }
331
+ per_stubs. into_values ( ) . collect ( )
332
+ }
333
+
317
334
/// Use default function implementations.
318
335
impl Callbacks for KaniCompiler {
319
336
/// Configure the [KaniCompiler] `self` object during the [CompilationStage::Init].
@@ -326,7 +343,6 @@ impl Callbacks for KaniCompiler {
326
343
& matches,
327
344
matches ! ( config. opts. error_format, ErrorOutputType :: Json { .. } ) ,
328
345
) ;
329
-
330
346
// Configure queries.
331
347
let queries = & mut ( * self . queries . lock ( ) . unwrap ( ) ) ;
332
348
queries. emit_vtable_restrictions = matches. get_flag ( parser:: RESTRICT_FN_PTRS ) ;
@@ -364,3 +380,75 @@ impl Callbacks for KaniCompiler {
364
380
self . prepare_codegen ( )
365
381
}
366
382
}
383
+
384
+ #[ cfg( test) ]
385
+ mod tests {
386
+ use super :: { HarnessInfo , Stubs } ;
387
+ use crate :: kani_compiler:: { group_by_stubs, HarnessId } ;
388
+ use kani_metadata:: { HarnessAttributes , HarnessMetadata } ;
389
+ use rustc_data_structures:: fingerprint:: Fingerprint ;
390
+ use rustc_hir:: definitions:: DefPathHash ;
391
+ use std:: collections:: HashMap ;
392
+
393
+ fn mock_next_id ( ) -> HarnessId {
394
+ static mut COUNTER : u64 = 0 ;
395
+ unsafe { COUNTER += 1 } ;
396
+ let id = unsafe { COUNTER } ;
397
+ DefPathHash ( Fingerprint :: new ( id, 0 ) )
398
+ }
399
+
400
+ fn mock_metadata ( ) -> HarnessMetadata {
401
+ HarnessMetadata {
402
+ pretty_name : String :: from ( "dummy" ) ,
403
+ mangled_name : String :: from ( "dummy" ) ,
404
+ crate_name : String :: from ( "dummy" ) ,
405
+ original_file : String :: from ( "dummy" ) ,
406
+ original_start_line : 10 ,
407
+ original_end_line : 20 ,
408
+ goto_file : None ,
409
+ attributes : HarnessAttributes :: default ( ) ,
410
+ }
411
+ }
412
+
413
+ fn mock_info_with_stubs ( stub_map : Stubs ) -> HarnessInfo {
414
+ HarnessInfo { metadata : mock_metadata ( ) , stub_map }
415
+ }
416
+
417
+ #[ test]
418
+ fn test_group_by_stubs_works ( ) {
419
+ // Set up the inputs
420
+ let harness_1 = mock_next_id ( ) ;
421
+ let harness_2 = mock_next_id ( ) ;
422
+ let harness_3 = mock_next_id ( ) ;
423
+ let harnesses = vec ! [ harness_1, harness_2, harness_3] ;
424
+
425
+ let stub_1 = ( mock_next_id ( ) , mock_next_id ( ) ) ;
426
+ let stub_2 = ( mock_next_id ( ) , mock_next_id ( ) ) ;
427
+ let stub_3 = ( mock_next_id ( ) , mock_next_id ( ) ) ;
428
+ let stub_4 = ( stub_3. 0 , mock_next_id ( ) ) ;
429
+
430
+ let set_1 = Stubs :: from ( [ stub_1, stub_2, stub_3] ) ;
431
+ let set_2 = Stubs :: from ( [ stub_1, stub_2, stub_4] ) ;
432
+ let set_3 = Stubs :: from ( [ stub_1, stub_3, stub_2] ) ;
433
+ assert_eq ! ( set_1, set_3) ;
434
+ assert_ne ! ( set_1, set_2) ;
435
+
436
+ let harnesses_info = HashMap :: from ( [
437
+ ( harness_1, mock_info_with_stubs ( set_1) ) ,
438
+ ( harness_2, mock_info_with_stubs ( set_2) ) ,
439
+ ( harness_3, mock_info_with_stubs ( set_3) ) ,
440
+ ] ) ;
441
+ assert_eq ! ( harnesses_info. len( ) , 3 ) ;
442
+
443
+ // Run the function under test.
444
+ let grouped = group_by_stubs ( harnesses, & harnesses_info) ;
445
+
446
+ // Verify output.
447
+ assert_eq ! ( grouped. len( ) , 2 ) ;
448
+ assert ! (
449
+ grouped. contains( & vec![ harness_1, harness_3] )
450
+ || grouped. contains( & vec![ harness_3, harness_1] )
451
+ ) ;
452
+ assert ! ( grouped. contains( & vec![ harness_2] ) ) ;
453
+ }
454
+ }
0 commit comments