@@ -33,7 +33,7 @@ use rustc_interface::Config;
3333use rustc_middle:: ty:: TyCtxt ;
3434use rustc_session:: config:: { ErrorOutputType , OutputType } ;
3535use rustc_span:: ErrorGuaranteed ;
36- use std:: collections:: HashMap ;
36+ use std:: collections:: { BTreeMap , HashMap } ;
3737use std:: fs:: File ;
3838use std:: io:: BufWriter ;
3939use std:: mem;
@@ -67,7 +67,7 @@ fn backend(queries: Arc<Mutex<QueryDb>>) -> Box<CodegenBackend> {
6767type HarnessId = DefPathHash ;
6868
6969/// A set of stubs.
70- type Stubs = HashMap < DefPathHash , DefPathHash > ;
70+ type Stubs = BTreeMap < DefPathHash , DefPathHash > ;
7171
7272#[ derive( Debug ) ]
7373struct HarnessInfo {
@@ -109,14 +109,15 @@ enum CompilationStage {
109109 /// Stage where the compiler will perform codegen of all harnesses that don't use stub.
110110 CodegenNoStubs {
111111 target_harnesses : Vec < HarnessId > ,
112- next_harnesses : Vec < HarnessId > ,
112+ next_harnesses : Vec < Vec < HarnessId > > ,
113113 all_harnesses : HashMap < HarnessId , HarnessInfo > ,
114114 } ,
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.
117118 CodegenWithStubs {
118- target_harness : HarnessId ,
119- next_harnesses : Vec < HarnessId > ,
119+ target_harnesses : Vec < HarnessId > ,
120+ next_harnesses : Vec < Vec < HarnessId > > ,
120121 all_harnesses : HashMap < HarnessId , HarnessInfo > ,
121122 } ,
122123 Done ,
@@ -166,7 +167,9 @@ impl KaniCompiler {
166167 CompilationStage :: CodegenNoStubs { .. } => {
167168 unreachable ! ( "This stage should always run in the same session as Init" ) ;
168169 }
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 ] ;
170173 let extra_arg =
171174 stubbing:: mk_rustc_arg ( & all_harnesses[ & target_harness] . stub_map ) ;
172175 let mut args = orig_args. clone ( ) ;
@@ -192,9 +195,10 @@ impl KaniCompiler {
192195 }
193196 CompilationStage :: CodegenNoStubs { next_harnesses, all_harnesses, .. }
194197 | 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" ) ;
196200 CompilationStage :: CodegenWithStubs {
197- target_harness ,
201+ target_harnesses ,
198202 next_harnesses : mem:: take ( next_harnesses) ,
199203 all_harnesses : mem:: take ( all_harnesses) ,
200204 }
@@ -210,6 +214,7 @@ impl KaniCompiler {
210214
211215 /// Run the Rust compiler with the given arguments and pass `&mut self` to handle callbacks.
212216 fn run_compilation_session ( & mut self , args : & [ String ] ) -> Result < ( ) , ErrorGuaranteed > {
217+ debug ! ( ?args, "run_compilation_session" ) ;
213218 let queries = self . queries . clone ( ) ;
214219 let mut compiler = RunCompiler :: new ( args, self ) ;
215220 compiler. set_make_codegen_backend ( Some ( Box :: new ( move |_cfg| backend ( queries) ) ) ) ;
@@ -249,7 +254,7 @@ impl KaniCompiler {
249254 // Even if no_stubs is empty we still need to store metadata.
250255 CompilationStage :: CodegenNoStubs {
251256 target_harnesses : no_stubs,
252- next_harnesses : with_stubs,
257+ next_harnesses : group_by_stubs ( with_stubs, & all_harnesses ) ,
253258 all_harnesses,
254259 }
255260 } else {
@@ -266,7 +271,15 @@ impl KaniCompiler {
266271 fn prepare_codegen ( & mut self ) -> Compilation {
267272 debug ! ( stage=?self . stage, "prepare_codegen" ) ;
268273 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+ ) ;
270283 let queries = & mut ( * self . queries . lock ( ) . unwrap ( ) ) ;
271284 queries. harnesses_info = target_harnesses
272285 . iter ( )
@@ -276,14 +289,6 @@ impl KaniCompiler {
276289 . collect ( ) ;
277290 Compilation :: Continue
278291 }
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- }
287292 CompilationStage :: Init | CompilationStage :: Done => unreachable ! ( ) ,
288293 }
289294 }
@@ -314,6 +319,18 @@ impl KaniCompiler {
314319 }
315320}
316321
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+
317334/// Use default function implementations.
318335impl Callbacks for KaniCompiler {
319336 /// Configure the [KaniCompiler] `self` object during the [CompilationStage::Init].
@@ -326,7 +343,6 @@ impl Callbacks for KaniCompiler {
326343 & matches,
327344 matches ! ( config. opts. error_format, ErrorOutputType :: Json { .. } ) ,
328345 ) ;
329-
330346 // Configure queries.
331347 let queries = & mut ( * self . queries . lock ( ) . unwrap ( ) ) ;
332348 queries. emit_vtable_restrictions = matches. get_flag ( parser:: RESTRICT_FN_PTRS ) ;
@@ -364,3 +380,75 @@ impl Callbacks for KaniCompiler {
364380 self . prepare_codegen ( )
365381 }
366382}
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