Skip to content

Commit 2141444

Browse files
authored
[compiler-v2] Making v2 the basis of the prover (step #1) (aptos-labs#12462)
* [compiler-v2] Making v2 the basis of the prover (step #1) This adds the missing parts to let compiler v2 fully support the specification language, and switches the prover to use v2 as the basis for verification of v1 bytecode. There is one further step needed to run the prover also on the code generated by v2 but that one is smaller than here. Notice that with this, we are dogfooding the v2 compiler frontend in production with the Move prover. There is no switching back and forth, code for the v1 prover integration has been removed. In more detail this does the following: - There are two new env processors, the spec_checker and the spec_rewriter: - `spec_checker` checks the correct use of Move functions in the specification language. Those functions must be 'pure' and not depend on state or use certain other constructs. The checker is to be run as part of the regular compiler chain. - `spec_rewriter` rewrites specification expressions by converting used Move functions into specification functions, and doing other transformations to lift a Move expression into the specification language. This is only run by the prover itself. - Inlining has been extended to deal with specification constructs. - To support the inlining refactoring and the new processors, a new module `rewrite_target` is introduced which allows to collect functions and specification elements in a program in a unified fashion, rewriting them, and writing back to the environment. This new data structure has been inspired by the current design of the inliner and naturally extends it. - A lot of ugliness has been ripped out of the model builder infrastructure (e.g. `TryImplAsSpec` mode is gone, as this is now handled by the `spec_rewriter`). More should come in step #2. - Multiple test cases have been added. - The prover driver has been adapted to use the new components. * Fixing some unit tests * Making hopefully all tests pass: - Adding tuple support to the specification language as they are created by the inliner. - Fixing an issue in memory usage calculation - Adding a flag `--aptos` to the prover command line for easier debugging, avoiding the CLI. * Disabling a condition for CI because of timeout. * Rebasing
1 parent 0c7a0ae commit 2141444

File tree

299 files changed

+5125
-6557
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

299 files changed

+5125
-6557
lines changed

Cargo.lock

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

aptos-move/framework/aptos-framework/doc/reconfiguration_with_dkg.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,8 +164,10 @@ Abort if no DKG is in progress.
164164
<b>requires</b> <a href="chain_status.md#0x1_chain_status_is_operating">chain_status::is_operating</a>();
165165
<b>include</b> <a href="stake.md#0x1_stake_ResourceRequirement">stake::ResourceRequirement</a>;
166166
<b>include</b> <a href="stake.md#0x1_stake_GetReconfigStartTimeRequirement">stake::GetReconfigStartTimeRequirement</a>;
167-
<b>include</b> <a href="../../aptos-stdlib/../move-stdlib/doc/features.md#0x1_features_spec_periodical_reward_rate_decrease_enabled">features::spec_periodical_reward_rate_decrease_enabled</a>() ==&gt; <a href="staking_config.md#0x1_staking_config_StakingRewardsConfigEnabledRequirement">staking_config::StakingRewardsConfigEnabledRequirement</a>;
167+
<b>include</b> <a href="../../aptos-stdlib/../move-stdlib/doc/features.md#0x1_features_spec_periodical_reward_rate_decrease_enabled">features::spec_periodical_reward_rate_decrease_enabled</a>(
168+
) ==&gt; <a href="staking_config.md#0x1_staking_config_StakingRewardsConfigEnabledRequirement">staking_config::StakingRewardsConfigEnabledRequirement</a>;
168169
<b>aborts_if</b> <b>false</b>;
170+
<b>pragma</b> verify_duration_estimate = 600;
169171
</code></pre>
170172

171173

aptos-move/framework/aptos-framework/sources/reconfiguration_with_dkg.spec.move

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,10 @@ spec aptos_framework::reconfiguration_with_dkg {
1212
requires chain_status::is_operating();
1313
include stake::ResourceRequirement;
1414
include stake::GetReconfigStartTimeRequirement;
15-
include features::spec_periodical_reward_rate_decrease_enabled() ==> staking_config::StakingRewardsConfigEnabledRequirement;
15+
include features::spec_periodical_reward_rate_decrease_enabled(
16+
) ==> staking_config::StakingRewardsConfigEnabledRequirement;
1617
aborts_if false;
18+
pragma verify_duration_estimate = 600; // TODO: set because of timeout (property proved).
1719
}
1820

1921
spec finish(account: &signer) {
@@ -59,5 +61,4 @@ spec aptos_framework::reconfiguration_with_dkg {
5961
requires dkg::has_incomplete_session();
6062
aborts_if false;
6163
}
62-
6364
}

aptos-move/framework/src/prover.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ impl ProverOptions {
123123
) -> anyhow::Result<()> {
124124
let now = Instant::now();
125125
let for_test = self.for_test;
126-
let model = build_model(
126+
let mut model = build_model(
127127
dev_mode,
128128
package_path,
129129
named_addresses,
@@ -162,7 +162,7 @@ impl ProverOptions {
162162
)],
163163
});
164164
let mut writer = StandardStream::stderr(ColorChoice::Auto);
165-
move_prover::run_move_prover_with_model(&model, &mut writer, options, Some(now))?;
165+
move_prover::run_move_prover_with_model(&mut model, &mut writer, options, Some(now))?;
166166
Ok(())
167167
}
168168

third_party/move/evm/move-to-yul/tests/TestABIStructs.exp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2446,7 +2446,7 @@ object "test_A2_M_test_abi_String" {
24462446
case 2 {
24472447
// label L5
24482448
// assert Le($t1, $t4)
2449-
// assert forall j: num: Range(0, $t1): ascii::$is_valid_char(Index($t0, j))
2449+
// assert forall j: num: Range(0, $t1): ascii::is_valid_char(Index($t0, j))
24502450
// $t6 := <($t1, $t4)
24512451
$t6 := $Lt(i, $t4)
24522452
// if ($t6) goto L1 else goto L0
@@ -2474,7 +2474,7 @@ object "test_A2_M_test_abi_String" {
24742474
case 5 {
24752475
// label L0
24762476
// assert Eq<u64>($t1, $t4)
2477-
// assert forall j: num: Range(0, $t4): ascii::$is_valid_char(Index($t0, j))
2477+
// assert forall j: num: Range(0, $t4): ascii::is_valid_char(Index($t0, j))
24782478
// $t14 := move($t0)
24792479
$t14 := mload($locals)
24802480
// $t15 := pack ascii::String($t14)

third_party/move/move-compiler-v2/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ log = { version = "0.4.14", features = ["serde"] }
3737
num = "0.4.0"
3838
once_cell = "1.7.2"
3939
#paste = "1.0.5"
40-
#petgraph = "0.5.1"
40+
petgraph = "0.6.4"
4141
serde = { version = "1.0.124", features = ["derive"] }
4242

4343
[dev-dependencies]

third_party/move/move-compiler-v2/src/env_pipeline/mod.rs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,14 @@
66
77
// TODO: move all other `&mut GlobalEnv` processors into this module.
88

9-
use log::debug;
9+
use log::trace;
1010
use move_model::model::GlobalEnv;
1111
use std::io::Write;
1212

1313
pub mod lambda_lifter;
14+
pub mod rewrite_target;
15+
pub mod spec_checker;
16+
pub mod spec_rewriter;
1417

1518
/// Represents a pipeline of processors working on the global environment.
1619
#[derive(Default)]
@@ -32,10 +35,10 @@ impl<'a> EnvProcessorPipeline<'a> {
3235
/// Runs the pipeline. Running will be ended if any of the steps produces an error.
3336
/// The function returns true if all steps succeeded without errors.
3437
pub fn run(&self, env: &mut GlobalEnv) -> bool {
35-
debug!("before env processor pipeline: {}", env.dump_env());
38+
trace!("before env processor pipeline: {}", env.dump_env());
3639
for (name, proc) in &self.processors {
3740
proc(env);
38-
debug!("after env processor {}", name);
41+
trace!("after env processor {}", name);
3942
if env.has_errors() {
4043
return false;
4144
}
@@ -47,13 +50,13 @@ impl<'a> EnvProcessorPipeline<'a> {
4750
/// only.
4851
pub fn run_and_record(&self, env: &mut GlobalEnv, w: &mut impl Write) -> anyhow::Result<bool> {
4952
let msg = format!("before env processor pipeline:\n{}\n", env.dump_env());
50-
debug!("{}", msg);
53+
trace!("{}", msg);
5154
writeln!(w, "// -- Model dump {}", msg)?;
5255
for (name, proc) in &self.processors {
5356
proc(env);
5457
if !env.has_errors() {
5558
let msg = format!("after env processor {}:\n{}\n", name, env.dump_env());
56-
debug!("{}", msg);
59+
trace!("{}", msg);
5760
writeln!(w, "// -- Model dump {}", msg)?;
5861
} else {
5962
return Ok(false);
Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
// Copyright © Aptos Foundation
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
use move_model::{
5+
ast::{Exp, Spec, SpecBlockTarget},
6+
model::{FunId, GlobalEnv, NodeId, QualifiedId, SpecFunId},
7+
};
8+
use std::{
9+
collections::{BTreeMap, BTreeSet},
10+
mem,
11+
};
12+
13+
/// Represents a target for rewriting.
14+
#[derive(Debug, PartialOrd, Ord, PartialEq, Eq, Clone)]
15+
pub enum RewriteTarget {
16+
/// A Move function
17+
MoveFun(QualifiedId<FunId>),
18+
/// A specification function
19+
SpecFun(QualifiedId<SpecFunId>),
20+
/// A specification block, which can be attached to a Move function, struct, or module.
21+
SpecBlock(SpecBlockTarget),
22+
}
23+
24+
/// Represents the state of a rewriting target.
25+
#[derive(Debug, Clone, Eq, PartialEq)]
26+
pub enum RewriteState {
27+
/// The target has not been changed
28+
Unchanged,
29+
/// The definition of a Move or spec function has changed
30+
Def(Exp),
31+
/// A specification block has changed.
32+
Spec(Spec),
33+
/// The target is 'abstract', i.e. does have a definition.
34+
Abstract,
35+
}
36+
37+
/// Scope for collecting targets.
38+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd)]
39+
pub enum RewritingScope {
40+
/// Including only targets for which `module.is_target()` is true.
41+
CompilationTarget,
42+
/// Include everything.
43+
Everything,
44+
}
45+
46+
/// Represents a set of rewriting targets in the given state.
47+
#[derive(Clone)]
48+
pub struct RewriteTargets {
49+
pub targets: BTreeMap<RewriteTarget, RewriteState>,
50+
}
51+
52+
impl RewriteTargets {
53+
/// Create a new set of rewrite targets, collecting them as specified by `scope`.
54+
/// Those targets are initially associated with `Unchanged` state.
55+
pub fn create(env: &GlobalEnv, scope: RewritingScope) -> Self {
56+
let mut targets = vec![];
57+
let add_spec =
58+
|targets: &mut Vec<RewriteTarget>, sb_target: SpecBlockTarget, spec: &Spec| {
59+
if !spec.is_empty() {
60+
targets.push(RewriteTarget::SpecBlock(sb_target))
61+
}
62+
};
63+
for module in env.get_modules() {
64+
if scope == RewritingScope::Everything || module.is_target() {
65+
for func in module.get_functions() {
66+
let id = func.get_qualified_id();
67+
targets.push(RewriteTarget::MoveFun(id));
68+
add_spec(
69+
&mut targets,
70+
SpecBlockTarget::Function(id.module_id, id.id),
71+
&func.get_spec(),
72+
);
73+
}
74+
for (spec_fun_id, _) in module.get_spec_funs() {
75+
targets.push(RewriteTarget::SpecFun(
76+
module.get_id().qualified(*spec_fun_id),
77+
));
78+
}
79+
for struct_env in module.get_structs() {
80+
add_spec(
81+
&mut targets,
82+
SpecBlockTarget::Struct(module.get_id(), struct_env.get_id()),
83+
&struct_env.get_spec(),
84+
)
85+
}
86+
if !module.get_spec().is_empty() {
87+
add_spec(
88+
&mut targets,
89+
SpecBlockTarget::Module(module.get_id()),
90+
&module.get_spec(),
91+
);
92+
}
93+
}
94+
}
95+
Self {
96+
targets: targets
97+
.into_iter()
98+
.map(|target| (target, RewriteState::Unchanged))
99+
.collect(),
100+
}
101+
}
102+
103+
/// Filters the targets according to the predicate.
104+
pub fn filter(&mut self, pred: impl Fn(&RewriteTarget, &RewriteState) -> bool) {
105+
self.targets = mem::take(&mut self.targets)
106+
.into_iter()
107+
.filter(|(t, s)| pred(t, s))
108+
.collect();
109+
}
110+
111+
/// Iterates all targets.
112+
pub fn iter(&self) -> impl Iterator<Item = (&RewriteTarget, &RewriteState)> + '_ {
113+
self.targets.iter()
114+
}
115+
116+
/// Returns an iteration of the target keys.
117+
pub fn keys(&self) -> impl Iterator<Item = RewriteTarget> + '_ {
118+
self.targets.keys().cloned()
119+
}
120+
121+
/// Adds a new rewrite target in state `Unchanged` if it doesn't exist yet. Returns
122+
/// a boolean whether the entry is new and a mutable reference to the state.
123+
pub fn entry(&mut self, target: RewriteTarget) -> (bool, &mut RewriteState) {
124+
let mut is_new = false;
125+
let state = self.targets.entry(target).or_insert_with(|| {
126+
is_new = true;
127+
RewriteState::Unchanged
128+
});
129+
(is_new, state)
130+
}
131+
132+
/// Gets the current state of the target.
133+
pub fn state(&self, target: &RewriteTarget) -> &RewriteState {
134+
self.targets.get(target).expect("state defined")
135+
}
136+
137+
/// Gets the mutable current state of the target.
138+
pub fn state_mut(&mut self, target: &RewriteTarget) -> &mut RewriteState {
139+
self.targets.get_mut(target).expect("state defined")
140+
}
141+
142+
/// Updates the global env based on the current state. This consumes
143+
/// the rewrite targets.
144+
pub fn write_to_env(self, env: &mut GlobalEnv) {
145+
for (target, state) in self.targets {
146+
use RewriteState::*;
147+
use RewriteTarget::*;
148+
match (target, state) {
149+
(_, Unchanged) => {},
150+
(MoveFun(fnid), Def(def)) => env.set_function_def(fnid, def),
151+
(SpecFun(fnid), Def(def)) => env.get_spec_fun_mut(fnid).body = Some(def),
152+
(SpecBlock(sb_target), Spec(spec)) => {
153+
*env.get_spec_block_mut(&sb_target) = spec;
154+
},
155+
_ => panic!("unexpected rewrite target and result combination"),
156+
}
157+
}
158+
}
159+
}
160+
161+
impl RewriteTarget {
162+
/// Gets the call sites for the target.
163+
pub fn called_funs_with_call_sites(
164+
&self,
165+
env: &GlobalEnv,
166+
) -> BTreeMap<QualifiedId<FunId>, BTreeSet<NodeId>> {
167+
use RewriteTarget::*;
168+
match self {
169+
MoveFun(id) => env
170+
.get_function(*id)
171+
.get_def()
172+
.map(|e| e.called_funs_with_callsites())
173+
.unwrap_or_default(),
174+
SpecFun(id) => env
175+
.get_spec_fun(*id)
176+
.body
177+
.as_ref()
178+
.map(|e| e.called_funs_with_callsites())
179+
.unwrap_or_default(),
180+
SpecBlock(target) => {
181+
let spec = env.get_spec_block(target);
182+
spec.called_funs_with_callsites()
183+
},
184+
}
185+
}
186+
187+
/// Get the environment state of this target in form of a RewriteState.
188+
pub fn get_env_state(&self, env: &GlobalEnv) -> RewriteState {
189+
use RewriteState::*;
190+
use RewriteTarget::*;
191+
match self {
192+
MoveFun(fid) => env
193+
.get_function(*fid)
194+
.get_def()
195+
.map(|e| Def(e.clone()))
196+
.unwrap_or(Abstract),
197+
SpecFun(fid) => env
198+
.get_spec_fun(*fid)
199+
.body
200+
.clone()
201+
.map(Def)
202+
.unwrap_or(Abstract),
203+
SpecBlock(sb_target) => Spec(env.get_spec_block(sb_target).clone()),
204+
}
205+
}
206+
}

0 commit comments

Comments
 (0)