Repository: aptos-labs/aptos-core (language: python)

Issue:
[compiler-v2][release] Add flag `--optimize=level` to CLI
Add a flag which easily allows to select optimization level, like `none`, `standard`, `full`


Discussion / hints:

Fix patch:
diff --git a/Cargo.lock b/Cargo.lock
index 030595e095040..ae902a3ca33f0 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -320,6 +320,7 @@ dependencies = [
  "move-cli",
  "move-command-line-common",
  "move-compiler",
+ "move-compiler-v2",
  "move-core-types",
  "move-coverage",
  "move-disassembler",
@@ -1830,6 +1831,7 @@ dependencies = [
  "move-cli",
  "move-command-line-common",
  "move-compiler",
+ "move-compiler-v2",
  "move-core-types",
  "move-docgen",
  "move-model",
diff --git a/aptos-move/framework/Cargo.toml b/aptos-move/framework/Cargo.toml
index 3d5bfd0f98501..fb7d4cb4d4495 100644
--- a/aptos-move/framework/Cargo.toml
+++ b/aptos-move/framework/Cargo.toml
@@ -52,6 +52,7 @@ move-binary-format = { workspace = true }
 move-cli = { workspace = true }
 move-command-line-common = { workspace = true }
 move-compiler = { workspace = true }
+move-compiler-v2 = { workspace = true }
 move-core-types = { workspace = true }
 move-docgen = { workspace = true }
 move-model = { workspace = true }
diff --git a/aptos-move/framework/src/aptos.rs b/aptos-move/framework/src/aptos.rs
index 5f1b91ba68eee..6717a00135be4 100644
--- a/aptos-move/framework/src/aptos.rs
+++ b/aptos-move/framework/src/aptos.rs
@@ -4,9 +4,8 @@
 #![forbid(unsafe_code)]
 
 use crate::{
-    docgen::DocgenOptions, extended_checks, path_in_crate,
-    release_builder::RELEASE_BUNDLE_EXTENSION, release_bundle::ReleaseBundle, BuildOptions,
-    ReleaseOptions,
+    docgen::DocgenOptions, path_in_crate, release_builder::RELEASE_BUNDLE_EXTENSION,
+    release_bundle::ReleaseBundle, BuildOptions, ReleaseOptions,
 };
 use clap::ValueEnum;
 use move_command_line_common::address::NumericalAddress;
@@ -100,14 +99,8 @@ impl ReleaseTarget {
             .collect::<Vec<_>>();
         ReleaseOptions {
             build_options: BuildOptions {
-                dev: false,
                 with_srcs,
                 with_abis: true,
-                with_source_maps: false,
-                with_error_map: true,
-                named_addresses: Default::default(),
-                override_std: None,
-                install_dir: None,
                 with_docs: true,
                 docgen_options: Some(DocgenOptions {
                     include_impl: true,
@@ -120,14 +113,7 @@ impl ReleaseTarget {
                     output_format: None,
                 }),
                 skip_fetch_latest_git_deps: true,
-                bytecode_version: None,
-                compiler_version: None,
-                language_version: None,
-                skip_attribute_checks: false,
-                check_test_code: false,
-                known_attributes: extended_checks::get_all_attribute_names().clone(),
-                experiments: vec![],
-                move_2: false,
+                ..BuildOptions::default()
             },
             packages: packages.iter().map(|(path, _)| path.to_owned()).collect(),
             rust_bindings: packages
diff --git a/aptos-move/framework/src/built_package.rs b/aptos-move/framework/src/built_package.rs
index bac96aa568043..0d5c935f958a4 100644
--- a/aptos-move/framework/src/built_package.rs
+++ b/aptos-move/framework/src/built_package.rs
@@ -19,6 +19,7 @@ use itertools::Itertools;
 use move_binary_format::CompiledModule;
 use move_command_line_common::files::MOVE_COMPILED_EXTENSION;
 use move_compiler::compiled_unit::{CompiledUnit, NamedCompiledModule};
+use move_compiler_v2::{options::Options, Experiment};
 use move_core_types::{language_storage::ModuleId, metadata::Metadata};
 use move_model::{
     metadata::{CompilerVersion, LanguageVersion},
@@ -256,6 +257,13 @@ impl BuiltPackage {
 
         // Run extended checks as well derive runtime metadata
         let model = &model_opt.expect("move model");
+
+        if let Some(model_options) = model.get_extension::<Options>() {
+            if model_options.experiment_on(Experiment::STOP_BEFORE_EXTENDED_CHECKS) {
+                std::process::exit(0)
+            }
+        }
+
         let runtime_metadata = extended_checks::run_extended_checks(model);
         if model.diag_count(Severity::Warning) > 0 {
             let mut error_writer = StandardStream::stderr(ColorChoice::Auto);
@@ -265,6 +273,12 @@ impl BuiltPackage {
             }
         }
 
+        if let Some(model_options) = model.get_extension::<Options>() {
+            if model_options.experiment_on(Experiment::STOP_AFTER_EXTENDED_CHECKS) {
+                std::process::exit(0)
+            }
+        }
+
         let compiled_pkg_path = package
             .compiled_package_info
             .build_flags
diff --git a/consensus/src/epoch_manager.rs b/consensus/src/epoch_manager.rs
index 186d86b9312ae..ddf2462fa4492 100644
--- a/consensus/src/epoch_manager.rs
+++ b/consensus/src/epoch_manager.rs
@@ -1809,5 +1809,4 @@ pub enum NoRandomnessReason {
     KeyPairDeserializationError(bcs::Error),
     KeyPairSerializationError(bcs::Error),
     KeyPairPersistError(anyhow::Error),
-    MyPkNotFoundInValidatorSet,
 }
diff --git a/crates/aptos/Cargo.toml b/crates/aptos/Cargo.toml
index 1d0ad2e9d13be..dc27c3d7d037c 100644
--- a/crates/aptos/Cargo.toml
+++ b/crates/aptos/Cargo.toml
@@ -70,6 +70,7 @@ move-bytecode-source-map = { workspace = true }
 move-cli = { workspace = true }
 move-command-line-common = { workspace = true }
 move-compiler = { workspace = true }
+move-compiler-v2 = { workspace = true }
 move-core-types = { workspace = true }
 move-coverage = { workspace = true }
 move-disassembler = { workspace = true }
diff --git a/crates/aptos/src/common/types.rs b/crates/aptos/src/common/types.rs
index b6cf752a68e55..6dfdcc20e0129 100644
--- a/crates/aptos/src/common/types.rs
+++ b/crates/aptos/src/common/types.rs
@@ -18,7 +18,7 @@ use crate::{
     genesis::git::from_yaml,
     move_tool::{ArgWithType, FunctionArgType, MemberId},
 };
-use anyhow::Context;
+use anyhow::{bail, Context};
 use aptos_api_types::ViewFunction;
 use aptos_crypto::{
     ed25519::{Ed25519PrivateKey, Ed25519PublicKey, Ed25519Signature},
@@ -1022,25 +1022,53 @@ impl RestOptions {
     }
 }
 
+/// Options for optimization level
+#[derive(Debug, Clone, Parser)]
+pub enum OptimizationLevel {
+    /// No optimizations
+    None,
+    /// Default optimization level
+    Default,
+    /// Extra optimizations, that may take more time
+    Extra,
+}
+
+impl Default for OptimizationLevel {
+    fn default() -> Self {
+        Self::Default
+    }
+}
+
+impl FromStr for OptimizationLevel {
+    type Err = anyhow::Error;
+
+    /// Parses an optimization level, or default.
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        match s.to_lowercase().as_str() {
+            "none" => Ok(Self::None),
+            "" | "default" => Ok(Self::Default),
+            "extra" => Ok(Self::Extra),
+            _ => bail!(
+                "unrecognized optimization level `{}` (supported versions: `none`, `default`, `aggressive`)",
+                s
+            ),
+        }
+    }
+}
+
 /// Options for compiling a move package dir
 #[derive(Debug, Clone, Parser)]
 pub struct MovePackageDir {
-    /// Enables dev mode, which uses all dev-addresses and dev-dependencies
-    ///
-    /// Dev mode allows for changing dependencies and addresses to the preset [dev-addresses] and
-    /// [dev-dependencies] fields.  This works both inside and out of tests for using preset values.
-    ///
-    /// Currently, it also additionally pulls in all test compilation artifacts
-    #[clap(long)]
-    pub dev: bool,
-    /// Path to a move package (the folder with a Move.toml file)
+    /// Path to a move package (the folder with a Move.toml file).  Defaults to current directory.
     #[clap(long, value_parser)]
     pub package_dir: Option<PathBuf>,
+
     /// Path to save the compiled move package
     ///
     /// Defaults to `<package_dir>/build`
     #[clap(long, value_parser)]
     pub output_dir: Option<PathBuf>,
+
     /// Named addresses for the move binary
     ///
     /// Example: alice=0x1234, bob=0x5678
@@ -1061,42 +1089,83 @@ pub struct MovePackageDir {
     #[clap(long)]
     pub(crate) skip_fetch_latest_git_deps: bool,
 
+    /// Do not complain about unknown attributes in Move code.
+    #[clap(long)]
+    pub skip_attribute_checks: bool,
+
+    /// Enables dev mode, which uses all dev-addresses and dev-dependencies
+    ///
+    /// Dev mode allows for changing dependencies and addresses to the preset [dev-addresses] and
+    /// [dev-dependencies] fields.  This works both inside and out of tests for using preset values.
+    ///
+    /// Currently, it also additionally pulls in all test compilation artifacts
+    #[clap(long)]
+    pub dev: bool,
+
+    /// Do apply extended checks for Aptos (e.g. `#[view]` attribute) also on test code.
+    /// NOTE: this behavior will become the default in the future.
+    /// See <https://github.com/aptos-labs/aptos-core/issues/10335>
+    #[clap(long, env = "APTOS_CHECK_TEST_CODE")]
+    pub check_test_code: bool,
+
+    /// Select optimization level.  Choices are "none", "default", or "extra".
+    /// Level "extra" may spend more time on expensive optimizations in the future.
+    /// Level "none" does no optimizations, possibly leading to use of too many runtime resources.
+    /// Level "default" is the recommended level, and the default if not provided.
+    #[clap(long, alias = "optimization_level", value_parser = clap::value_parser!(OptimizationLevel))]
+    pub optimize: Option<OptimizationLevel>,
+
+    /// Experiments
+    #[clap(long, hide(true))]
+    pub experiments: Vec<String>,
+
+    /// ...or --bytecode `BYTECODE_VERSION`
     /// Specify the version of the bytecode the compiler is going to emit.
-    #[clap(long, default_value_if("move_2", "true", "7"))]
+    /// Defaults to `6`, or `7` if language version 2 is selected
+    /// (through `--move-2` or `--language_version=2`), .
+    #[clap(
+        long,
+        default_value_if("move_2", "true", "7"),
+        alias = "bytecode",
+        verbatim_doc_comment
+    )]
     pub bytecode_version: Option<u32>,
 
+    /// ...or `--compiler COMPILER_VERSION`
     /// Specify the version of the compiler.
-    /// Currently, default to `v1`
+    /// Defaults to `1`, or `2` if `--move-2` is selected.
     #[clap(long, value_parser = clap::value_parser!(CompilerVersion),
-           default_value_if("move_2", "true", "2.0"))]
+           alias = "compiler",
+           default_value_if("move_2", "true", "2.0"),
+           verbatim_doc_comment)]
     pub compiler_version: Option<CompilerVersion>,
 
+    /// ...or `--language LANGUAGE_VERSION`
     /// Specify the language version to be supported.
-    /// Currently, default to `v1`
+    /// Currently, defaults to `1`, unless `--move-2` is selected.
     #[clap(long, value_parser = clap::value_parser!(LanguageVersion),
-           default_value_if("move_2", "true", "2.0"))]
+           alias = "language",
+           default_value_if("move_2", "true", "2.0"),
+           verbatim_doc_comment)]
     pub language_version: Option<LanguageVersion>,
 
-    /// Do not complain about unknown attributes in Move code.
-    #[clap(long)]
-    pub skip_attribute_checks: bool,
-
-    /// Do apply extended checks for Aptos (e.g. `#[view]` attribute) also on test code.
-    /// NOTE: this behavior will become the default in the future.
-    /// See <https://github.com/aptos-labs/aptos-core/issues/10335>
-    #[clap(long, env = "APTOS_CHECK_TEST_CODE")]
-    pub check_test_code: bool,
-
-    /// Select bytecode, language, compiler for Move 2
-    #[clap(long)]
+    /// Select bytecode, language version, and compiler to support Move 2:
+    /// Same as `--bytecode_version=7 --language_version=2.0 --compiler_version=2.0`
+    #[clap(long, verbatim_doc_comment)]
     pub move_2: bool,
 }
 
+impl Default for MovePackageDir {
+    fn default() -> Self {
+        Self::new()
+    }
+}
+
 impl MovePackageDir {
-    pub fn new(package_dir: PathBuf) -> Self {
+    pub fn new() -> Self {
         Self {
             dev: false,
-            package_dir: Some(package_dir),
+            package_dir: None,
             output_dir: None,
             named_addresses: Default::default(),
             override_std: None,
@@ -1107,6 +1176,8 @@ impl MovePackageDir {
             skip_attribute_checks: false,
             check_test_code: false,
             move_2: false,
+            optimize: None,
+            experiments: vec![],
         }
     }
 
diff --git a/crates/aptos/src/governance/mod.rs b/crates/aptos/src/governance/mod.rs
index b091493774af1..79a4e3c2d8d31 100644
--- a/crates/aptos/src/governance/mod.rs
+++ b/crates/aptos/src/governance/mod.rs
@@ -1020,17 +1020,7 @@ impl CliCommand<()> for GenerateUpgradeProposal {
             next_execution_hash,
         } = self;
         let package_path = move_options.get_package_path()?;
-        let options = included_artifacts.build_options(
-            move_options.dev,
-            move_options.skip_fetch_latest_git_deps,
-            move_options.named_addresses(),
-            move_options.override_std,
-            move_options.bytecode_version,
-            move_options.compiler_version,
-            move_options.language_version,
-            move_options.skip_attribute_checks,
-            move_options.check_test_code,
-        );
+        let options = included_artifacts.build_options(&move_options)?;
         let package = BuiltPackage::build(package_path, options)?;
         let release = ReleasePackage::new(package)?;
 
diff --git a/crates/aptos/src/move_tool/lint.rs b/crates/aptos/src/move_tool/lint.rs
new file mode 100644
index 0000000000000..b4fcb7fa73bdd
--- /dev/null
+++ b/crates/aptos/src/move_tool/lint.rs
@@ -0,0 +1,137 @@
+// Copyright © Aptos Foundation
+// SPDX-License-Identifier: Apache-2.0
+
+use crate::{
+    common::types::{AccountAddressWrapper, CliCommand, CliTypedResult, MovePackageDir},
+    move_tool::IncludedArtifacts,
+};
+use aptos_framework::{BuildOptions, BuiltPackage};
+use async_trait::async_trait;
+use clap::Parser;
+use move_compiler_v2::Experiment;
+use move_model::metadata::{CompilerVersion, LanguageVersion};
+use move_package::source_package::std_lib::StdVersion;
+use std::{collections::BTreeMap, path::PathBuf};
+
+/// Run a Lint tool to show additional warnings about the current package, in addition to ordinary
+/// warnings and/or errors generated by the Move 2 compiler.
+#[derive(Debug, Clone, Parser)]
+pub struct LintPackage {
+    /// Path to a move package (the folder with a Move.toml file).  Defaults to current directory.
+    #[clap(long, value_parser)]
+    pub package_dir: Option<PathBuf>,
+
+    /// Specify the path to save the compiled bytecode files which lint generates while
+    /// running checks.
+    /// Defaults to `<package_dir>/build`
+    #[clap(long, value_parser)]
+    pub output_dir: Option<PathBuf>,
+
+    /// ...or `--language LANGUAGE_VERSION`
+    /// Specify the language version to be supported.
+    /// Currently, defaults to `2.0`.
+    #[clap(long, value_parser = clap::value_parser!(LanguageVersion),
+           alias = "language",
+           default_value = "2.0",
+           verbatim_doc_comment)]
+    pub language_version: Option<LanguageVersion>,
+
+    /// Named addresses for the move binary
+    ///
+    /// Example: alice=0x1234, bob=0x5678
+    ///
+    /// Note: This will fail if there are duplicates in the Move.toml file remove those first.
+    #[clap(long, value_parser = crate::common::utils::parse_map::<String, AccountAddressWrapper>, default_value = "")]
+    pub(crate) named_addresses: BTreeMap<String, AccountAddressWrapper>,
+
+    /// Override the standard library version by mainnet/testnet/devnet
+    #[clap(long, value_parser)]
+    pub override_std: Option<StdVersion>,
+
+    /// Skip pulling the latest git dependencies
+    ///
+    /// If you don't have a network connection, the compiler may fail due
+    /// to no ability to pull git dependencies.  This will allow overriding
+    /// this for local development.
+    #[clap(long)]
+    pub(crate) skip_fetch_latest_git_deps: bool,
+
+    /// Do not complain about unknown attributes in Move code.
+    #[clap(long)]
+    pub skip_attribute_checks: bool,
+
+    /// Enables dev mode, which uses all dev-addresses and dev-dependencies
+    ///
+    /// Dev mode allows for changing dependencies and addresses to the preset [dev-addresses] and
+    /// [dev-dependencies] fields.  This works both inside and out of tests for using preset values.
+    ///
+    /// Currently, it also additionally pulls in all test compilation artifacts
+    #[clap(long)]
+    pub dev: bool,
+
+    /// Do apply extended checks for Aptos (e.g. `#[view]` attribute) also on test code.
+    /// NOTE: this behavior will become the default in the future.
+    /// See <https://github.com/aptos-labs/aptos-core/issues/10335>
+    #[clap(long, env = "APTOS_CHECK_TEST_CODE")]
+    pub check_test_code: bool,
+}
+
+impl LintPackage {
+    fn to_move_options(&self) -> MovePackageDir {
+        let LintPackage {
+            dev,
+            package_dir,
+            output_dir,
+            named_addresses,
+            override_std,
+            skip_fetch_latest_git_deps,
+            language_version,
+            skip_attribute_checks,
+            check_test_code,
+        } = self.clone();
+        MovePackageDir {
+            dev,
+            package_dir,
+            output_dir,
+            named_addresses,
+            override_std,
+            skip_fetch_latest_git_deps,
+            language_version,
+            skip_attribute_checks,
+            check_test_code,
+            ..MovePackageDir::new()
+        }
+    }
+}
+
+#[async_trait]
+impl CliCommand<&'static str> for LintPackage {
+    fn command_name(&self) -> &'static str {
+        "LintPackage"
+    }
+
+    async fn execute(self) -> CliTypedResult<&'static str> {
+        let move_options = MovePackageDir {
+            compiler_version: Some(CompilerVersion::V2_0),
+            ..self.to_move_options()
+        };
+        let more_experiments = vec![
+            Experiment::LINT_CHECKS.to_string(),
+            Experiment::SPEC_CHECK.to_string(),
+            Experiment::SEQS_IN_BINOPS_CHECK.to_string(),
+            Experiment::ACCESS_CHECK.to_string(),
+            Experiment::STOP_AFTER_EXTENDED_CHECKS.to_string(),
+        ];
+        let package_path = move_options.get_package_path()?;
+        let included_artifacts = IncludedArtifacts::Sparse;
+        let build_options = BuildOptions {
+            ..included_artifacts.build_options_with_experiments(
+                &move_options,
+                more_experiments,
+                true,
+            )?
+        };
+        BuiltPackage::build(package_path, build_options)?;
+        Ok("succeeded")
+    }
+}
diff --git a/crates/aptos/src/move_tool/mod.rs b/crates/aptos/src/move_tool/mod.rs
index 54bcf6778067c..648b8a3301630 100644
--- a/crates/aptos/src/move_tool/mod.rs
+++ b/crates/aptos/src/move_tool/mod.rs
@@ -8,9 +8,9 @@ use crate::{
         types::{
             load_account_arg, ArgWithTypeJSON, CliConfig, CliError, CliTypedResult,
             ConfigSearchMode, EntryFunctionArguments, EntryFunctionArgumentsJSON,
-            MoveManifestAccountWrapper, MovePackageDir, OverrideSizeCheckOption, ProfileOptions,
-            PromptOptions, RestOptions, SaveFile, ScriptFunctionArguments, TransactionOptions,
-            TransactionSummary,
+            MoveManifestAccountWrapper, MovePackageDir, OptimizationLevel, OverrideSizeCheckOption,
+            ProfileOptions, PromptOptions, RestOptions, SaveFile, ScriptFunctionArguments,
+            TransactionOptions, TransactionSummary,
         },
         utils::{
             check_if_file_exists, create_dir_if_not_exist, dir_default_to_current,
@@ -22,6 +22,7 @@ use crate::{
         bytecode::{Decompile, Disassemble},
         coverage::SummaryCoverage,
         fmt::Fmt,
+        lint::LintPackage,
         manifest::{Dependency, ManifestNamedAddress, MovePackageManifest, PackageInfo},
     },
     CliCommand, CliResult,
@@ -48,13 +49,11 @@ use async_trait::async_trait;
 use clap::{Parser, Subcommand, ValueEnum};
 use itertools::Itertools;
 use move_cli::{self, base::test::UnitTestResult};
-use move_command_line_common::env::MOVE_HOME;
+use move_command_line_common::{address::NumericalAddress, env::MOVE_HOME};
+use move_compiler_v2::Experiment;
 use move_core_types::{identifier::Identifier, language_storage::ModuleId, u256::U256};
 use move_model::metadata::{CompilerVersion, LanguageVersion};
-use move_package::{
-    source_package::{layout::SourcePackageLayout, std_lib::StdVersion},
-    BuildConfig, CompilerConfig,
-};
+use move_package::{source_package::layout::SourcePackageLayout, BuildConfig, CompilerConfig};
 use move_unit_test::UnitTestingConfig;
 pub use package_hooks::*;
 use serde::{Deserialize, Serialize};
@@ -73,6 +72,7 @@ mod aptos_debug_natives;
 mod bytecode;
 pub mod coverage;
 mod fmt;
+mod lint;
 mod manifest;
 pub mod package_hooks;
 mod show;
@@ -108,6 +108,7 @@ pub enum MoveTool {
     Document(DocumentPackage),
     Download(DownloadPackage),
     Init(InitPackage),
+    Lint(LintPackage),
     List(ListPackage),
     Prove(ProvePackage),
     #[clap(alias = "deploy")]
@@ -156,6 +157,7 @@ impl MoveTool {
             MoveTool::View(tool) => tool.execute_serialized().await,
             MoveTool::Replay(tool) => tool.execute_serialized().await,
             MoveTool::Fmt(tool) => tool.execute_serialized().await,
+            MoveTool::Lint(tool) => tool.execute_serialized().await,
         }
     }
 }
@@ -385,17 +387,7 @@ impl CliCommand<Vec<String>> for CompilePackage {
             ..self
                 .included_artifacts_args
                 .included_artifacts
-                .build_options(
-                    self.move_options.dev,
-                    self.move_options.skip_fetch_latest_git_deps,
-                    self.move_options.named_addresses(),
-                    self.move_options.override_std.clone(),
-                    self.move_options.bytecode_version,
-                    self.move_options.compiler_version,
-                    self.move_options.language_version,
-                    self.move_options.skip_attribute_checks,
-                    self.move_options.check_test_code,
-                )
+                .build_options(&self.move_options)?
         };
         let pack = BuiltPackage::build(self.move_options.get_package_path()?, build_options)
             .map_err(|e| CliError::MoveCompilationError(format!("{:#}", e)))?;
@@ -449,17 +441,7 @@ impl CompileScript {
     async fn compile_script(&self) -> CliTypedResult<(Vec<u8>, HashValue)> {
         let build_options = BuildOptions {
             install_dir: self.move_options.output_dir.clone(),
-            ..IncludedArtifacts::None.build_options(
-                self.move_options.dev,
-                self.move_options.skip_fetch_latest_git_deps,
-                self.move_options.named_addresses(),
-                self.move_options.override_std.clone(),
-                self.move_options.bytecode_version,
-                self.move_options.compiler_version,
-                self.move_options.language_version,
-                self.move_options.skip_attribute_checks,
-                self.move_options.check_test_code,
-            )
+            ..IncludedArtifacts::None.build_options(&self.move_options)?
         };
         let package_dir = self.move_options.get_package_path()?;
         let pack = BuiltPackage::build(package_dir, build_options)
@@ -524,6 +506,21 @@ pub struct TestPackage {
     pub dump_state: bool,
 }
 
+fn fix_bytecode_version(
+    bytecode_version_in: Option<u32>,
+    language_version: Option<LanguageVersion>,
+) -> Option<u32> {
+    if bytecode_version_in.is_none() {
+        if let Some(language_version) = language_version {
+            Some(language_version.infer_bytecode_version(bytecode_version_in))
+        } else {
+            bytecode_version_in
+        }
+    } else {
+        bytecode_version_in
+    }
+}
+
 #[async_trait]
 impl CliCommand<&'static str> for TestPackage {
     fn command_name(&self) -> &'static str {
@@ -542,9 +539,13 @@ impl CliCommand<&'static str> for TestPackage {
             compiler_config: CompilerConfig {
                 known_attributes: known_attributes.clone(),
                 skip_attribute_checks: self.move_options.skip_attribute_checks,
+                bytecode_version: fix_bytecode_version(
+                    self.move_options.bytecode_version,
+                    self.move_options.language_version,
+                ),
                 compiler_version: self.move_options.compiler_version,
                 language_version: self.move_options.language_version,
-                ..Default::default()
+                experiments: experiments_from_opt_level(&self.move_options.optimize),
             },
             ..Default::default()
         };
@@ -558,6 +559,17 @@ impl CliCommand<&'static str> for TestPackage {
                 report_stacktrace_on_abort: true,
                 report_storage_on_error: self.dump_state,
                 ignore_compile_warnings: self.ignore_compile_warnings,
+                named_address_values: self
+                    .move_options
+                    .named_addresses
+                    .iter()
+                    .map(|(name, addr_wrap)| {
+                        (
+                            name.clone(),
+                            NumericalAddress::from_account_address(addr_wrap.account_address),
+                        )
+                    })
+                    .collect(),
                 ..UnitTestingConfig::default_with_bound(None)
             },
             // TODO(Gas): we may want to switch to non-zero costs in the future
@@ -624,7 +636,7 @@ impl CliCommand<&'static str> for ProvePackage {
                 move_options.dev,
                 move_options.get_package_path()?.as_path(),
                 move_options.named_addresses(),
-                move_options.bytecode_version,
+                fix_bytecode_version(move_options.bytecode_version, move_options.language_version),
                 move_options.compiler_version,
                 move_options.language_version,
                 move_options.skip_attribute_checks,
@@ -666,24 +678,23 @@ impl CliCommand<&'static str> for DocumentPackage {
         } = self;
         let build_options = BuildOptions {
             dev: move_options.dev,
-            with_srcs: false,
-            with_abis: false,
-            with_source_maps: false,
             with_error_map: false,
             with_docs: true,
-            install_dir: None,
             named_addresses: move_options.named_addresses(),
             override_std: move_options.override_std.clone(),
             docgen_options: Some(docgen_options),
             skip_fetch_latest_git_deps: move_options.skip_fetch_latest_git_deps,
-            bytecode_version: move_options.bytecode_version,
+            bytecode_version: fix_bytecode_version(
+                move_options.bytecode_version,
+                move_options.language_version,
+            ),
             compiler_version: move_options.compiler_version,
             language_version: move_options.language_version,
             skip_attribute_checks: move_options.skip_attribute_checks,
             check_test_code: move_options.check_test_code,
             known_attributes: extended_checks::get_all_attribute_names().clone(),
-            experiments: vec![],
             move_2: move_options.move_2,
+            ..BuildOptions::default()
         };
         BuiltPackage::build(move_options.get_package_path()?, build_options)?;
         Ok("succeeded")
@@ -743,17 +754,7 @@ impl TryInto<PackagePublicationData> for &PublishPackage {
         let options = self
             .included_artifacts_args
             .included_artifacts
-            .build_options(
-                self.move_options.dev,
-                self.move_options.skip_fetch_latest_git_deps,
-                self.move_options.named_addresses(),
-                self.move_options.override_std.clone(),
-                self.move_options.bytecode_version,
-                self.move_options.compiler_version,
-                self.move_options.language_version,
-                self.move_options.skip_attribute_checks,
-                self.move_options.check_test_code,
-            );
+            .build_options(&self.move_options)?;
         let package = BuiltPackage::build(package_path, options)
             .map_err(|e| CliError::MoveCompilationError(format!("{:#}", e)))?;
         let compiled_units = package.extract_code();
@@ -813,20 +814,66 @@ impl FromStr for IncludedArtifacts {
     }
 }
 
+fn experiments_from_opt_level(optlevel: &Option<OptimizationLevel>) -> Vec<String> {
+    match optlevel {
+        None | Some(OptimizationLevel::Default) => {
+            vec![format!("{}=on", Experiment::OPTIMIZE.to_string())]
+        },
+        Some(OptimizationLevel::None) => vec![format!("{}=off", Experiment::OPTIMIZE.to_string())],
+        Some(OptimizationLevel::Extra) => vec![
+            format!("{}=on", Experiment::OPTIMIZE_EXTRA.to_string()),
+            format!("{}=on", Experiment::OPTIMIZE.to_string()),
+        ],
+    }
+}
+
 impl IncludedArtifacts {
     pub(crate) fn build_options(
         self,
-        dev: bool,
-        skip_fetch_latest_git_deps: bool,
-        named_addresses: BTreeMap<String, AccountAddress>,
-        override_std: Option<StdVersion>,
-        bytecode_version: Option<u32>,
-        compiler_version: Option<CompilerVersion>,
-        language_version: Option<LanguageVersion>,
-        skip_attribute_checks: bool,
-        check_test_code: bool,
-    ) -> BuildOptions {
-        use IncludedArtifacts::*;
+        move_options: &MovePackageDir,
+    ) -> CliTypedResult<BuildOptions> {
+        self.build_options_with_experiments(move_options, vec![], false)
+    }
+
+    pub(crate) fn build_options_with_experiments(
+        self,
+        move_options: &MovePackageDir,
+        mut more_experiments: Vec<String>,
+        _skip_codegen: bool, // we currently cannot do this, so ignore it.
+    ) -> CliTypedResult<BuildOptions> {
+        let dev = move_options.dev;
+        let skip_fetch_latest_git_deps = move_options.skip_fetch_latest_git_deps;
+        let named_addresses = move_options.named_addresses();
+        let override_std = move_options.override_std.clone();
+        let bytecode_version =
+            fix_bytecode_version(move_options.bytecode_version, move_options.language_version);
+        let compiler_version = move_options.compiler_version;
+        let language_version = move_options.language_version;
+        let skip_attribute_checks = move_options.skip_attribute_checks;
+        let check_test_code = move_options.check_test_code;
+        let optimize = move_options.optimize.clone();
+        let mut experiments = experiments_from_opt_level(&optimize);
+        experiments.append(&mut move_options.experiments.clone());
+        experiments.append(&mut more_experiments);
+
+        // TODO(#14441): Remove `None |` here when we update default CompilerVersion
+        if matches!(
+            move_options.compiler_version,
+            Option::None | Some(CompilerVersion::V1)
+        ) {
+            if !matches!(optimize, Option::None | Some(OptimizationLevel::Default)) {
+                return Err(CliError::CommandArgumentError(
+                    "`--optimization-level`/`--optimize` flag is not compatible with Move Compiler V1"
+                        .to_string(),
+                ));
+            };
+            if !move_options.experiments.is_empty() {
+                return Err(CliError::CommandArgumentError(
+                    "`--experiments` flag is not compatible with Move Compiler V1".to_string(),
+                ));
+            };
+        }
+
         let base_options = BuildOptions {
             dev,
             // Always enable error map bytecode injection
@@ -839,10 +886,12 @@ impl IncludedArtifacts {
             language_version,
             skip_attribute_checks,
             check_test_code,
+            experiments,
             known_attributes: extended_checks::get_all_attribute_names().clone(),
             ..BuildOptions::default()
         };
-        match self {
+        use IncludedArtifacts::*;
+        Ok(match self {
             None => BuildOptions {
                 with_srcs: false,
                 with_abis: false,
@@ -861,7 +910,7 @@ impl IncludedArtifacts {
                 with_source_maps: true,
                 ..base_options
             },
-        }
+        })
     }
 }
 
@@ -970,17 +1019,7 @@ impl CliCommand<TransactionSummary> for CreateObjectAndPublishPackage {
         let options = self
             .included_artifacts_args
             .included_artifacts
-            .build_options(
-                self.move_options.dev,
-                self.move_options.skip_fetch_latest_git_deps,
-                self.move_options.named_addresses(),
-                self.move_options.override_std.clone(),
-                self.move_options.bytecode_version,
-                self.move_options.compiler_version,
-                self.move_options.language_version,
-                self.move_options.skip_attribute_checks,
-                self.move_options.check_test_code,
-            );
+            .build_options(&self.move_options)?;
         let package = BuiltPackage::build(self.move_options.get_package_path()?, options)?;
         let message = format!(
             "Do you want to publish this package at object address {}",
@@ -1048,17 +1087,7 @@ impl CliCommand<TransactionSummary> for UpgradeObjectPackage {
         let options = self
             .included_artifacts_args
             .included_artifacts
-            .build_options(
-                self.move_options.dev,
-                self.move_options.skip_fetch_latest_git_deps,
-                self.move_options.named_addresses(),
-                self.move_options.override_std.clone(),
-                self.move_options.bytecode_version,
-                self.move_options.compiler_version,
-                self.move_options.language_version,
-                self.move_options.skip_attribute_checks,
-                self.move_options.check_test_code,
-            );
+            .build_options(&self.move_options)?;
         let built_package = BuiltPackage::build(self.move_options.get_package_path()?, options)?;
         let url = self
             .txn_options
@@ -1255,17 +1284,9 @@ fn build_package_options(
     move_options: &MovePackageDir,
     included_artifacts_args: &IncludedArtifactsArgs,
 ) -> anyhow::Result<BuiltPackage> {
-    let options = included_artifacts_args.included_artifacts.build_options(
-        move_options.dev,
-        move_options.skip_fetch_latest_git_deps,
-        move_options.named_addresses(),
-        move_options.override_std.clone(),
-        move_options.bytecode_version,
-        move_options.compiler_version,
-        move_options.language_version,
-        move_options.skip_attribute_checks,
-        move_options.check_test_code,
-    );
+    let options = included_artifacts_args
+        .included_artifacts
+        .build_options(move_options)?;
     BuiltPackage::build(move_options.get_package_path()?, options)
 }
 
@@ -1355,17 +1376,9 @@ impl CliCommand<TransactionSummary> for CreateResourceAccountAndPublishPackage {
         move_options.add_named_address(address_name, resource_address.to_string());
 
         let package_path = move_options.get_package_path()?;
-        let options = included_artifacts_args.included_artifacts.build_options(
-            move_options.dev,
-            move_options.skip_fetch_latest_git_deps,
-            move_options.named_addresses(),
-            move_options.override_std,
-            move_options.bytecode_version,
-            move_options.compiler_version,
-            move_options.language_version,
-            move_options.skip_attribute_checks,
-            move_options.check_test_code,
-        );
+        let options = included_artifacts_args
+            .included_artifacts
+            .build_options(&move_options)?;
         let package = BuiltPackage::build(package_path, options)?;
         let compiled_units = package.extract_code();
 
@@ -1507,18 +1520,11 @@ impl CliCommand<&'static str> for VerifyPackage {
         // First build the package locally to get the package metadata
         let build_options = BuildOptions {
             install_dir: self.move_options.output_dir.clone(),
-            bytecode_version: self.move_options.bytecode_version,
-            ..self.included_artifacts.build_options(
-                self.move_options.dev,
-                self.move_options.skip_fetch_latest_git_deps,
-                self.move_options.named_addresses(),
-                self.move_options.override_std.clone(),
+            bytecode_version: fix_bytecode_version(
                 self.move_options.bytecode_version,
-                self.move_options.compiler_version,
                 self.move_options.language_version,
-                self.move_options.skip_attribute_checks,
-                self.move_options.check_test_code,
-            )
+            ),
+            ..self.included_artifacts.build_options(&self.move_options)?
         };
         let pack = BuiltPackage::build(self.move_options.get_package_path()?, build_options)
             .map_err(|e| CliError::MoveCompilationError(format!("{:#}", e)))?;
diff --git a/crates/aptos/src/move_tool/show.rs b/crates/aptos/src/move_tool/show.rs
index 1a910b322d70f..520edbff14ae1 100644
--- a/crates/aptos/src/move_tool/show.rs
+++ b/crates/aptos/src/move_tool/show.rs
@@ -59,19 +59,8 @@ impl CliCommand<Vec<EntryABI>> for ShowAbi {
             ..self
                 .included_artifacts_args
                 .included_artifacts
-                .build_options(
-                    self.move_options.dev,
-                    self.move_options.skip_fetch_latest_git_deps,
-                    self.move_options.named_addresses(),
-                    self.move_options.override_std.clone(),
-                    self.move_options.bytecode_version,
-                    self.move_options.compiler_version,
-                    self.move_options.language_version,
-                    self.move_options.skip_attribute_checks,
-                    self.move_options.check_test_code,
-                )
+                .build_options(&self.move_options)?
         };
-
         // Build the package.
         let package = BuiltPackage::build(self.move_options.get_package_path()?, build_options)
             .map_err(|e| CliError::MoveCompilationError(format!("{:#}", e)))?;
diff --git a/third_party/move/move-command-line-common/src/address.rs b/third_party/move/move-command-line-common/src/address.rs
index 2121f16addbbf..4e546285ef4c7 100644
--- a/third_party/move/move-command-line-common/src/address.rs
+++ b/third_party/move/move-command-line-common/src/address.rs
@@ -50,6 +50,13 @@ impl NumericalAddress {
         format: NumberFormat::Hex,
     };
 
+    pub fn from_account_address(addr: AccountAddress) -> Self {
+        Self {
+            bytes: addr,
+            format: NumberFormat::Hex,
+        }
+    }
+
     pub const fn new(bytes: [u8; AccountAddress::LENGTH], format: NumberFormat) -> Self {
         Self {
             bytes: AccountAddress::new(bytes),
diff --git a/third_party/move/move-compiler-v2/src/experiments.rs b/third_party/move/move-compiler-v2/src/experiments.rs
index b3c6186a50d36..378f473a408fe 100644
--- a/third_party/move/move-compiler-v2/src/experiments.rs
+++ b/third_party/move/move-compiler-v2/src/experiments.rs
@@ -129,9 +129,14 @@ pub static EXPERIMENTS: Lazy<BTreeMap<String, Experiment>> = Lazy::new(|| {
         },
         Experiment {
             name: Experiment::OPTIMIZE.to_string(),
-            description: "Turns on or off a group of optimizations".to_string(),
+            description: "Turns on standard group of optimizations".to_string(),
             default: Given(true),
         },
+        Experiment {
+            name: Experiment::OPTIMIZE_EXTRA.to_string(),
+            description: "Use extra optimizations".to_string(),
+            default: Given(false),
+        },
         Experiment {
             name: Experiment::COPY_PROPAGATION.to_string(),
             description: "Whether copy propagation is run".to_string(),
@@ -185,7 +190,7 @@ pub static EXPERIMENTS: Lazy<BTreeMap<String, Experiment>> = Lazy::new(|| {
             name: Experiment::AST_SIMPLIFY_FULL.to_string(),
             description: "Whether to run the ast simplifier, including code elimination"
                 .to_string(),
-            default: Given(false),
+            default: Inherited(Experiment::OPTIMIZE_EXTRA.to_string()),
         },
         Experiment {
             name: Experiment::GEN_ACCESS_SPECIFIERS.to_string(),
@@ -209,6 +214,32 @@ pub static EXPERIMENTS: Lazy<BTreeMap<String, Experiment>> = Lazy::new(|| {
             description: "Whether to run flush writes processor and optimization".to_string(),
             default: Inherited(Experiment::OPTIMIZE.to_string()),
         },
+        Experiment {
+            name: Experiment::STOP_BEFORE_STACKLESS_BYTECODE.to_string(),
+            description:
+                "Exit quietly just before converting to stackless bytecode (after AST passes)"
+                    .to_string(),
+            default: Given(false),
+        },
+        Experiment {
+            name: Experiment::STOP_BEFORE_FILE_FORMAT.to_string(),
+            description:
+                "Exit quietly just before generating file format (after stackless bytecode  passes)"
+                    .to_string(),
+            default: Given(false),
+        },
+        Experiment {
+            name: Experiment::STOP_BEFORE_EXTENDED_CHECKS.to_string(),
+            description: "Exit quietly just before extended checks (after file format generation)"
+                .to_string(),
+            default: Given(false),
+        },
+        Experiment {
+            name: Experiment::STOP_AFTER_EXTENDED_CHECKS.to_string(),
+            description: "Exit quietly just after extended checks (after file format generation)"
+                .to_string(),
+            default: Given(false),
+        },
     ];
     experiments
         .into_iter()
@@ -236,6 +267,7 @@ impl Experiment {
     pub const LAMBDA_LIFTING: &'static str = "lambda-lifting";
     pub const LINT_CHECKS: &'static str = "lint-checks";
     pub const OPTIMIZE: &'static str = "optimize";
+    pub const OPTIMIZE_EXTRA: &'static str = "optimize-extra";
     pub const PEEPHOLE_OPTIMIZATION: &'static str = "peephole-optimization";
     pub const RECURSIVE_TYPE_CHECK: &'static str = "recursive-type-check";
     pub const REFERENCE_SAFETY: &'static str = "reference-safety";
@@ -244,6 +276,10 @@ impl Experiment {
     pub const SPEC_CHECK: &'static str = "spec-check";
     pub const SPEC_REWRITE: &'static str = "spec-rewrite";
     pub const SPLIT_CRITICAL_EDGES: &'static str = "split-critical-edges";
+    pub const STOP_AFTER_EXTENDED_CHECKS: &'static str = "stop-after-extended-checks";
+    pub const STOP_BEFORE_EXTENDED_CHECKS: &'static str = "stop-before-extended-checks";
+    pub const STOP_BEFORE_FILE_FORMAT: &'static str = "stop-before-file-format";
+    pub const STOP_BEFORE_STACKLESS_BYTECODE: &'static str = "stop-before-stackless-bytecode";
     pub const UNINITIALIZED_CHECK: &'static str = "uninitialized-check";
     pub const UNUSED_ASSIGNMENT_CHECK: &'static str = "unused-assignment-check";
     pub const UNUSED_STRUCT_PARAMS_CHECK: &'static str = "unused-struct-params-check";
diff --git a/third_party/move/move-compiler-v2/src/lib.rs b/third_party/move/move-compiler-v2/src/lib.rs
index 23e3b65866eeb..4dbf57e059206 100644
--- a/third_party/move/move-compiler-v2/src/lib.rs
+++ b/third_party/move/move-compiler-v2/src/lib.rs
@@ -43,7 +43,7 @@ use codespan_reporting::{
     diagnostic::Severity,
     term::termcolor::{ColorChoice, StandardStream, WriteColor},
 };
-pub use experiments::Experiment;
+pub use experiments::{Experiment, EXPERIMENTS};
 use log::{debug, info, log_enabled, Level};
 use move_binary_format::{binary_views::BinaryIndexedView, errors::VMError};
 use move_bytecode_source_map::source_map::SourceMap;
@@ -95,6 +95,10 @@ where
     let mut env = run_checker_and_rewriters(options.clone())?;
     check_errors(&env, error_writer, "checking errors")?;
 
+    if options.experiment_on(Experiment::STOP_BEFORE_STACKLESS_BYTECODE) {
+        std::process::exit(0)
+    }
+
     // Run code generator
     let mut targets = run_bytecode_gen(&env);
     check_errors(&env, error_writer, "code generation errors")?;
@@ -125,6 +129,10 @@ where
     }
     check_errors(&env, error_writer, "stackless-bytecode analysis errors")?;
 
+    if options.experiment_on(Experiment::STOP_BEFORE_FILE_FORMAT) {
+        std::process::exit(0)
+    }
+
     let modules_and_scripts = run_file_format_gen(&mut env, &targets);
     check_errors(&env, error_writer, "assembling errors")?;
 
diff --git a/third_party/move/move-compiler-v2/src/options.rs b/third_party/move/move-compiler-v2/src/options.rs
index ece7a48ffd1a7..4c342f5769548 100644
--- a/third_party/move/move-compiler-v2/src/options.rs
+++ b/third_party/move/move-compiler-v2/src/options.rs
@@ -62,8 +62,9 @@ pub struct Options {
     pub testing: bool,
 
     /// Active experiments. Experiments alter default behavior of the compiler.
+    /// Each element is `name[=on/off]` to enable/disable experiment `name`.
     /// See `Experiment` struct.
-    #[clap(short)]
+    #[clap(short, hide(true))]
     #[clap(
         long = "experiment",
         num_args = 0..
@@ -88,6 +89,7 @@ pub struct Options {
     #[clap(skip)]
     pub sources_deps: Vec<String>,
 
+    /// Warn about use of deprecated functions, modules, etc.
     #[clap(long = cli::MOVE_COMPILER_WARN_OF_DEPRECATION_USE_FLAG,
            default_value=bool_to_str(move_compiler_warn_of_deprecation_use_env_var()))]
     pub warn_deprecated: bool,
@@ -95,7 +97,7 @@ pub struct Options {
     /// Show warnings about use of deprecated usage in the Aptos libraries,
     /// which we should generally not bother users with.
     /// Note that current value of this constant is "Wdeprecation-aptos"
-    #[clap(long = cli::WARN_OF_DEPRECATION_USE_IN_APTOS_LIBS_FLAG,
+    #[clap(hide(true), long = cli::WARN_OF_DEPRECATION_USE_IN_APTOS_LIBS_FLAG,
            default_value=bool_to_str(warn_of_deprecation_use_in_aptos_libs_env_var()))]
     pub warn_of_deprecation_use_in_aptos_libs: bool,
 
diff --git a/third_party/move/move-examples/diem-framework/crates/crypto/Cargo.toml b/third_party/move/move-examples/diem-framework/crates/crypto/Cargo.toml
index 7c07613b50bdf..bbca086b375f6 100644
--- a/third_party/move/move-examples/diem-framework/crates/crypto/Cargo.toml
+++ b/third_party/move/move-examples/diem-framework/crates/crypto/Cargo.toml
@@ -38,7 +38,7 @@ byteorder = { workspace = true }
 criterion = { workspace = true }
 proptest = { workspace = true }
 proptest-derive = { workspace = true }
-ripemd160 = { workspace = true }
+ripemd = { workspace = true }
 serde_json = { workspace = true }
 sha3 = { workspace = true }
 # TODO: some tests will fail if this is set to 1.0.63
diff --git a/third_party/move/tools/move-package/src/compilation/compiled_package.rs b/third_party/move/tools/move-package/src/compilation/compiled_package.rs
index a803fb3e9743a..f9f1adfececf1 100644
--- a/third_party/move/tools/move-package/src/compilation/compiled_package.rs
+++ b/third_party/move/tools/move-package/src/compilation/compiled_package.rs
@@ -689,11 +689,9 @@ impl CompiledPackage {
                     known_attributes: known_attributes.clone(),
                     language_version: Some(effective_language_version),
                     compile_test_code: flags.keep_testing_functions(),
+                    experiments: config.experiments.clone(),
                     ..Default::default()
                 };
-                for experiment in &config.experiments {
-                    options = options.set_experiment(experiment, true)
-                }
                 options = options.set_experiment(Experiment::ATTACH_COMPILED_MODULE, true);
                 compiler_driver_v2(options)?
             },


Test patch:
diff --git a/crates/aptos/src/test/mod.rs b/crates/aptos/src/test/mod.rs
index 4b01917c969aa..25873452c7339 100644
--- a/crates/aptos/src/test/mod.rs
+++ b/crates/aptos/src/test/mod.rs
@@ -1097,7 +1097,8 @@ impl CliTestFramework {
         MovePackageDir {
             dev: true,
             named_addresses: Self::named_addresses(account_strs),
-            ..MovePackageDir::new(self.move_dir())
+            package_dir: Some(self.move_dir()),
+            ..MovePackageDir::new()
         }
     }