@@ -23,18 +23,18 @@ extern crate rustc_span;
2323mod log;
2424
2525use std:: env;
26- use std:: num:: NonZero ;
26+ use std:: num:: { NonZero , NonZeroI32 } ;
2727use std:: ops:: Range ;
2828use std:: rc:: Rc ;
2929use std:: str:: FromStr ;
30- use std:: sync:: atomic:: { AtomicI32 , AtomicU32 , Ordering } ;
30+ use std:: sync:: atomic:: { AtomicU32 , Ordering } ;
3131
3232use miri:: {
3333 BacktraceStyle , BorrowTrackerMethod , GenmcConfig , GenmcCtx , MiriConfig , MiriEntryFnType ,
34- ProvenanceMode , RetagFields , TreeBorrowsParams , ValidationMode , run_genmc_mode,
34+ ProvenanceMode , TreeBorrowsParams , ValidationMode , run_genmc_mode,
3535} ;
3636use rustc_abi:: ExternAbi ;
37- use rustc_data_structures:: sync;
37+ use rustc_data_structures:: sync:: { self , DynSync } ;
3838use rustc_driver:: Compilation ;
3939use rustc_hir:: def_id:: LOCAL_CRATE ;
4040use rustc_hir:: { self as hir, Node } ;
@@ -120,15 +120,47 @@ fn entry_fn(tcx: TyCtxt<'_>) -> (DefId, MiriEntryFnType) {
120120 }
121121}
122122
123+ fn run_many_seeds (
124+ many_seeds : ManySeedsConfig ,
125+ eval_entry_once : impl Fn ( u64 ) -> Result < ( ) , NonZeroI32 > + DynSync ,
126+ ) -> Result < ( ) , NonZeroI32 > {
127+ let exit_code =
128+ sync:: IntoDynSyncSend ( AtomicU32 :: new ( rustc_driver:: EXIT_SUCCESS . cast_unsigned ( ) ) ) ;
129+ let num_failed = sync:: IntoDynSyncSend ( AtomicU32 :: new ( 0 ) ) ;
130+ sync:: par_for_each_in ( many_seeds. seeds . clone ( ) , |& seed| {
131+ if let Err ( return_code) = eval_entry_once ( seed. into ( ) ) {
132+ eprintln ! ( "FAILING SEED: {seed}" ) ;
133+ if !many_seeds. keep_going {
134+ // `abort_if_errors` would unwind but would not actually stop miri, since
135+ // `par_for_each` waits for the rest of the threads to finish.
136+ exit ( return_code. get ( ) ) ;
137+ }
138+ // Preserve the "maximum" return code (when interpreted as `u32`), to make
139+ // the result order-independent and to make it 0 only if all executions were 0.
140+ exit_code. fetch_max ( return_code. get ( ) . cast_unsigned ( ) , Ordering :: Relaxed ) ;
141+ num_failed. fetch_add ( 1 , Ordering :: Relaxed ) ;
142+ }
143+ } ) ;
144+ let num_failed = num_failed. 0 . into_inner ( ) ;
145+ let exit_code = exit_code. 0 . into_inner ( ) . cast_signed ( ) ;
146+ if num_failed > 0 {
147+ eprintln ! ( "{num_failed}/{total} SEEDS FAILED" , total = many_seeds. seeds. count( ) ) ;
148+ Err ( NonZeroI32 :: new ( exit_code) . unwrap ( ) )
149+ } else {
150+ assert ! ( exit_code == 0 ) ;
151+ Ok ( ( ) )
152+ }
153+ }
154+
123155impl rustc_driver:: Callbacks for MiriCompilerCalls {
124156 fn after_analysis < ' tcx > (
125157 & mut self ,
126158 _: & rustc_interface:: interface:: Compiler ,
127159 tcx : TyCtxt < ' tcx > ,
128160 ) -> Compilation {
129- if tcx. sess . dcx ( ) . has_errors_or_delayed_bugs ( ) . is_some ( ) {
130- tcx. dcx ( ) . fatal ( "miri cannot be run on programs that fail compilation" ) ;
131- }
161+ tcx. dcx ( ) . abort_if_errors ( ) ;
162+ tcx. dcx ( ) . flush_delayed ( ) ;
163+
132164 if !tcx. crate_types ( ) . contains ( & CrateType :: Executable ) {
133165 tcx. dcx ( ) . fatal ( "miri only makes sense on bin crates" ) ;
134166 }
@@ -161,64 +193,28 @@ impl rustc_driver::Callbacks for MiriCompilerCalls {
161193 optimizations is usually marginal at best.") ;
162194 }
163195
164- // Run in GenMC mode if enabled.
165- if config. genmc_config . is_some ( ) {
166- // Validate GenMC settings.
167- if let Err ( err) = GenmcConfig :: validate ( & mut config, tcx) {
168- fatal_error ! ( "Invalid settings: {err}" ) ;
169- }
170-
171- // This is the entry point used in GenMC mode.
172- // This closure will be called multiple times to explore the concurrent execution space of the program.
173- let eval_entry_once = |genmc_ctx : Rc < GenmcCtx > | {
196+ let res = if config. genmc_config . is_some ( ) {
197+ assert ! ( self . many_seeds. is_none( ) ) ;
198+ run_genmc_mode ( tcx, & config, |genmc_ctx : Rc < GenmcCtx > | {
174199 miri:: eval_entry ( tcx, entry_def_id, entry_type, & config, Some ( genmc_ctx) )
175- } ;
176- let return_code = run_genmc_mode ( & config, eval_entry_once, tcx) . unwrap_or_else ( || {
177- tcx. dcx ( ) . abort_if_errors ( ) ;
178- rustc_driver:: EXIT_FAILURE
179- } ) ;
180- exit ( return_code) ;
181- } ;
182-
183- if let Some ( many_seeds) = self . many_seeds . take ( ) {
200+ } )
201+ } else if let Some ( many_seeds) = self . many_seeds . take ( ) {
184202 assert ! ( config. seed. is_none( ) ) ;
185- let exit_code = sync:: IntoDynSyncSend ( AtomicI32 :: new ( rustc_driver:: EXIT_SUCCESS ) ) ;
186- let num_failed = sync:: IntoDynSyncSend ( AtomicU32 :: new ( 0 ) ) ;
187- sync:: par_for_each_in ( many_seeds. seeds . clone ( ) , |seed| {
203+ run_many_seeds ( many_seeds, |seed| {
188204 let mut config = config. clone ( ) ;
189- config. seed = Some ( ( * seed) . into ( ) ) ;
205+ config. seed = Some ( seed) ;
190206 eprintln ! ( "Trying seed: {seed}" ) ;
191- let return_code = miri:: eval_entry (
192- tcx,
193- entry_def_id,
194- entry_type,
195- & config,
196- /* genmc_ctx */ None ,
197- )
198- . unwrap_or ( rustc_driver:: EXIT_FAILURE ) ;
199- if return_code != rustc_driver:: EXIT_SUCCESS {
200- eprintln ! ( "FAILING SEED: {seed}" ) ;
201- if !many_seeds. keep_going {
202- // `abort_if_errors` would actually not stop, since `par_for_each` waits for the
203- // rest of the to finish, so we just exit immediately.
204- exit ( return_code) ;
205- }
206- exit_code. store ( return_code, Ordering :: Relaxed ) ;
207- num_failed. fetch_add ( 1 , Ordering :: Relaxed ) ;
208- }
209- } ) ;
210- let num_failed = num_failed. 0 . into_inner ( ) ;
211- if num_failed > 0 {
212- eprintln ! ( "{num_failed}/{total} SEEDS FAILED" , total = many_seeds. seeds. count( ) ) ;
213- }
214- exit ( exit_code. 0 . into_inner ( ) ) ;
207+ miri:: eval_entry ( tcx, entry_def_id, entry_type, & config, /* genmc_ctx */ None )
208+ } )
215209 } else {
216- let return_code = miri:: eval_entry ( tcx, entry_def_id, entry_type, & config, None )
217- . unwrap_or_else ( || {
218- tcx. dcx ( ) . abort_if_errors ( ) ;
219- rustc_driver:: EXIT_FAILURE
220- } ) ;
221- exit ( return_code) ;
210+ miri:: eval_entry ( tcx, entry_def_id, entry_type, & config, None )
211+ } ;
212+
213+ if let Err ( return_code) = res {
214+ tcx. dcx ( ) . abort_if_errors ( ) ;
215+ exit ( return_code. get ( ) ) ;
216+ } else {
217+ exit ( rustc_driver:: EXIT_SUCCESS ) ;
222218 }
223219
224220 // Unreachable.
@@ -571,21 +567,17 @@ fn main() {
571567 } else if arg == "-Zmiri-mute-stdout-stderr" {
572568 miri_config. mute_stdout_stderr = true ;
573569 } else if arg == "-Zmiri-retag-fields" {
574- miri_config. retag_fields = RetagFields :: Yes ;
570+ eprintln ! (
571+ "warning: `-Zmiri-retag-fields` is a NOP and will be removed in a future version of Miri.\n \
572+ Field retagging has been on-by-default for a long time."
573+ ) ;
575574 } else if arg == "-Zmiri-fixed-schedule" {
576575 miri_config. fixed_scheduling = true ;
577576 } else if arg == "-Zmiri-deterministic-concurrency" {
578577 miri_config. fixed_scheduling = true ;
579578 miri_config. address_reuse_cross_thread_rate = 0.0 ;
580579 miri_config. cmpxchg_weak_failure_rate = 0.0 ;
581580 miri_config. weak_memory_emulation = false ;
582- } else if let Some ( retag_fields) = arg. strip_prefix ( "-Zmiri-retag-fields=" ) {
583- miri_config. retag_fields = match retag_fields {
584- "all" => RetagFields :: Yes ,
585- "none" => RetagFields :: No ,
586- "scalar" => RetagFields :: OnlyScalar ,
587- _ => fatal_error ! ( "`-Zmiri-retag-fields` can only be `all`, `none`, or `scalar`" ) ,
588- } ;
589581 } else if let Some ( param) = arg. strip_prefix ( "-Zmiri-seed=" ) {
590582 let seed = param. parse :: < u64 > ( ) . unwrap_or_else ( |_| {
591583 fatal_error ! ( "-Zmiri-seed must be an integer that fits into u64" )
@@ -747,6 +739,13 @@ fn main() {
747739 ) ;
748740 } ;
749741
742+ // Validate GenMC settings.
743+ if miri_config. genmc_config . is_some ( )
744+ && let Err ( err) = GenmcConfig :: validate ( & mut miri_config)
745+ {
746+ fatal_error ! ( "Invalid settings: {err}" ) ;
747+ }
748+
750749 debug ! ( "rustc arguments: {:?}" , rustc_args) ;
751750 debug ! ( "crate arguments: {:?}" , miri_config. args) ;
752751 if !miri_config. native_lib . is_empty ( ) && miri_config. native_lib_enable_tracing {
0 commit comments