22// SPDX-License-Identifier: Apache-2.0
33
44use crate :: build_model;
5+ use anyhow:: bail;
56use codespan_reporting:: {
67 diagnostic:: Severity ,
78 term:: termcolor:: { ColorChoice , StandardStream } ,
89} ;
9- use log:: LevelFilter ;
10+ use log:: { info , LevelFilter } ;
1011use move_core_types:: account_address:: AccountAddress ;
11- use move_model:: metadata:: { CompilerVersion , LanguageVersion } ;
12+ use move_model:: {
13+ metadata:: { CompilerVersion , LanguageVersion } ,
14+ model:: { GlobalEnv , VerificationScope } ,
15+ } ;
16+ use move_prover:: cli:: Options ;
1217use std:: {
1318 collections:: { BTreeMap , BTreeSet } ,
19+ fs,
1420 path:: Path ,
1521 time:: Instant ,
1622} ;
@@ -27,6 +33,12 @@ pub struct ProverOptions {
2733 #[ clap( long, short) ]
2834 pub filter : Option < String > ,
2935
36+ /// Scopes verification to the specified function. This can either be a name of the
37+ /// form "mod::func" or simply "func", in the later case every matching function is
38+ /// taken.
39+ #[ clap( long, short) ]
40+ pub only : Option < String > ,
41+
3042 /// Whether to display additional information in error reports. This may help
3143 /// debugging but also can make verification slower.
3244 #[ clap( long, short) ]
@@ -84,6 +96,24 @@ pub struct ProverOptions {
8496 #[ clap( long) ]
8597 pub dump : bool ,
8698
99+ /// Whether to benchmark verification. If selected, each verification target in the
100+ /// current package will be verified independently with timing recorded. This attempts
101+ /// to detect timeouts. A benchmark report will be written to `prover_benchmark.fun_data` in the
102+ /// package directory. The command also writes a `prover_benchmark.svg` graphic, which
103+ /// is build from the data in the file above, comparing with any other `*.fun_data` files
104+ /// in the package directory. Thus, you can rename the data file to something like
105+ /// `prover_benchmark_v1.fun_data` and in the next run, compare benchmarks in the `.svg`
106+ /// file from multiple runs.
107+ #[ clap( long = "benchmark" ) ]
108+ pub benchmark : bool ,
109+
110+ /// Whether to skip verification of type instantiations of functions. This may miss
111+ /// some verification conditions if different type instantiations can create
112+ /// different behavior via type reflection or storage access, but can speed up
113+ /// verification.
114+ #[ clap( long = "skip-instance-check" ) ]
115+ pub skip_instance_check : bool ,
116+
87117 #[ clap( skip) ]
88118 pub for_test : bool ,
89119}
@@ -93,6 +123,7 @@ impl Default for ProverOptions {
93123 Self {
94124 verbosity : None ,
95125 filter : None ,
126+ only : None ,
96127 trace : false ,
97128 cvc5 : false ,
98129 stratification_depth : 6 ,
@@ -106,7 +137,9 @@ impl Default for ProverOptions {
106137 loop_unroll : None ,
107138 stable_test_output : false ,
108139 dump : false ,
140+ benchmark : false ,
109141 for_test : false ,
142+ skip_instance_check : false ,
110143 }
111144 }
112145}
@@ -127,6 +160,7 @@ impl ProverOptions {
127160 ) -> anyhow:: Result < ( ) > {
128161 let now = Instant :: now ( ) ;
129162 let for_test = self . for_test ;
163+ let benchmark = self . benchmark ;
130164 let mut model = build_model (
131165 dev_mode,
132166 package_path,
@@ -140,6 +174,15 @@ impl ProverOptions {
140174 experiments. to_vec ( ) ,
141175 ) ?;
142176 let mut options = self . convert_options ( ) ;
177+ options. language_version = language_version;
178+ options. model_builder . language_version = language_version. unwrap_or_default ( ) ;
179+ if compiler_version. unwrap_or_default ( ) >= CompilerVersion :: V2_0
180+ || language_version
181+ . unwrap_or_default ( )
182+ . is_at_least ( LanguageVersion :: V2_0 )
183+ {
184+ options. compiler_v2 = true ;
185+ }
143186 // Need to ensure a distinct output.bpl file for concurrent execution. In non-test
144187 // mode, we actually want to use the static output.bpl for debugging purposes
145188 let _temp_holder = if for_test {
@@ -163,11 +206,21 @@ impl ProverOptions {
163206 template_bytes : include_bytes ! ( "aptos-natives.bpl" ) . to_vec ( ) ,
164207 module_instance_names : move_prover_boogie_backend:: options:: custom_native_options ( ) ,
165208 } ) ;
166- let mut writer = StandardStream :: stderr ( ColorChoice :: Auto ) ;
167- if compiler_version . unwrap_or_default ( ) == CompilerVersion :: V1 {
168- move_prover :: run_move_prover_with_model ( & mut model , & mut writer , options, Some ( now ) ) ?;
209+ if benchmark {
210+ // Special mode of benchmarking
211+ run_prover_benchmark ( package_path , & mut model , options) ?;
169212 } else {
170- move_prover:: run_move_prover_with_model_v2 ( & mut model, & mut writer, options, now) ?;
213+ let mut writer = StandardStream :: stderr ( ColorChoice :: Auto ) ;
214+ if compiler_version. unwrap_or_default ( ) == CompilerVersion :: V1 {
215+ move_prover:: run_move_prover_with_model (
216+ & mut model,
217+ & mut writer,
218+ options,
219+ Some ( now) ,
220+ ) ?;
221+ } else {
222+ move_prover:: run_move_prover_with_model_v2 ( & mut model, & mut writer, options, now) ?;
223+ }
171224 }
172225 Ok ( ( ) )
173226 }
@@ -184,6 +237,11 @@ impl ProverOptions {
184237 output_path : "" . to_string ( ) ,
185238 verbosity_level,
186239 prover : move_prover_bytecode_pipeline:: options:: ProverOptions {
240+ verify_scope : if let Some ( name) = self . only {
241+ VerificationScope :: Only ( name)
242+ } else {
243+ VerificationScope :: All
244+ } ,
187245 stable_test_output : self . stable_test_output ,
188246 auto_trace_level : if self . trace {
189247 move_prover_bytecode_pipeline:: options:: AutoTraceLevel :: VerifiedFunction
@@ -215,6 +273,7 @@ impl ProverOptions {
215273 } ,
216274 custom_natives : None ,
217275 loop_unroll : self . loop_unroll ,
276+ skip_instance_check : self . skip_instance_check ,
218277 ..Default :: default ( )
219278 } ,
220279 ..Default :: default ( )
@@ -234,3 +293,106 @@ impl ProverOptions {
234293 }
235294 }
236295}
296+
297+ fn run_prover_benchmark (
298+ package_path : & Path ,
299+ env : & mut GlobalEnv ,
300+ mut options : Options ,
301+ ) -> anyhow:: Result < ( ) > {
302+ info ! ( "starting prover benchmark" ) ;
303+ // Determine sources and dependencies from the env
304+ let mut sources = BTreeSet :: new ( ) ;
305+ let mut deps: Vec < String > = vec ! [ ] ;
306+ for module in env. get_modules ( ) {
307+ let file_name = module. get_source_path ( ) . to_string_lossy ( ) . to_string ( ) ;
308+ if module. is_primary_target ( ) {
309+ sources. insert ( module. get_source_path ( ) . to_string_lossy ( ) . to_string ( ) ) ;
310+ } else if let Some ( p) = Path :: new ( & file_name)
311+ . parent ( )
312+ . and_then ( |p| p. canonicalize ( ) . ok ( ) )
313+ {
314+ // The prover doesn't like to have `p` and `p/s` as dep paths, filter those out
315+ let p = p. to_string_lossy ( ) . to_string ( ) ;
316+ let mut done = false ;
317+ for d in & mut deps {
318+ if p. starts_with ( & * d) {
319+ // p is subsumed
320+ done = true ;
321+ break ;
322+ } else if d. starts_with ( & p) {
323+ // p is more general or equal to d, swap it out
324+ * d = p. to_string ( ) ;
325+ done = true ;
326+ break ;
327+ }
328+ }
329+ if !done {
330+ deps. push ( p)
331+ }
332+ } else {
333+ bail ! ( "invalid file path `{}`" , file_name)
334+ }
335+ }
336+
337+ // Enrich the prover options by the aliases in the env
338+ for ( alias, address) in env. get_address_alias_map ( ) {
339+ options. move_named_address_values . push ( format ! (
340+ "{}={}" ,
341+ alias. display( env. symbol_pool( ) ) ,
342+ address. to_hex_literal( )
343+ ) )
344+ }
345+
346+ // Create or override a prover_benchmark.toml in the package dir, reflection `options`
347+ let config_file = package_path. join ( "prover_benchmark.toml" ) ;
348+ let toml = toml:: to_string ( & options) ?;
349+ std:: fs:: write ( & config_file, toml) ?;
350+
351+ // Args for the benchmark API
352+ let mut args = vec ! [
353+ // Command name
354+ "bench" . to_string( ) ,
355+ // Benchmark by function not module
356+ "--func" . to_string( ) ,
357+ // Use as the config the file we derived from `options`
358+ "--config" . to_string( ) ,
359+ config_file. to_string_lossy( ) . to_string( ) ,
360+ ] ;
361+
362+ // Add deps and sources to args and run the tool
363+ for dep in deps {
364+ args. push ( "-d" . to_string ( ) ) ;
365+ args. push ( dep)
366+ }
367+ args. extend ( sources) ;
368+ move_prover_lab:: benchmark:: benchmark ( & args) ;
369+
370+ // The benchmark stores the result in `<config_file>.fun_data`, now plot it.
371+ // If there are any other `*.fun_data` files, add them to the plot.
372+ let mut args = vec ! [
373+ "plot" . to_string( ) ,
374+ format!(
375+ "--out={}" ,
376+ config_file
377+ . as_path( )
378+ . with_extension( "svg" )
379+ . to_string_lossy( )
380+ ) ,
381+ "--sort" . to_string( ) ,
382+ ] ;
383+ let main_data_file = config_file
384+ . as_path ( )
385+ . with_extension ( "fun_data" )
386+ . to_string_lossy ( )
387+ . to_string ( ) ;
388+ args. push ( main_data_file. clone ( ) ) ;
389+ let paths = fs:: read_dir ( package_path) ?;
390+ for p in paths. flatten ( ) {
391+ let p = p. path ( ) . as_path ( ) . to_string_lossy ( ) . to_string ( ) ;
392+ // Only use this if its is not the main data file we already added
393+ if p. ends_with ( ".fun_data" ) && !p. ends_with ( "/prover_benchmark.fun_data" ) {
394+ args. push ( p)
395+ }
396+ }
397+ move_prover_lab:: plot:: plot_svg ( & args)
398+ }
0 commit comments