Skip to content

Commit d6acef0

Browse files
authored
[move-compiler-v2] Implement AST Simplification to solve aptos-labs#10850 (aptos-labs#11553)
This is an expression simplifier which works at the AST level in Compiler V2, doing some quick optimizations that reduce the amount of code the stackless bytecode passes have to deal with. Notably, it eliminates some of the overhead of inlining, as seen in the test case deep_exp.move. * Fixes aptos-labs#10850: Compiler V2 needs expression simplification * Fixes aptos-labs#11535: Constant folding should not allow full-bitsize shifts * Fixes aptos-labs#11536: Constant folding displays redundant errors * Fixes aptos-labs#11549: Model Dump for a Cast operation doesn't show the type * Fixes aptos-labs#11805: Load constants the right way * Fixes aptos-labs#11720: Process got killed when running unit tests in aptos-gas-schedule-updator using V2
1 parent 42d905a commit d6acef0

File tree

258 files changed

+11311
-2372
lines changed

Some content is hidden

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

258 files changed

+11311
-2372
lines changed

third_party/move/move-compiler-v2/src/ast_simplifier.rs

Lines changed: 1187 additions & 0 deletions
Large diffs are not rendered by default.

third_party/move/move-compiler-v2/src/bytecode_generator.rs

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -254,15 +254,21 @@ impl<'env> Generator<'env> {
254254
self.label_counter += 1;
255255
Label::new(n as usize)
256256
} else {
257-
self.internal_error(id, "too many labels");
257+
self.internal_error(id, format!("too many labels: {}", self.label_counter));
258258
Label::new(0)
259259
}
260260
}
261261

262262
/// Require unary target.
263263
fn require_unary_target(&mut self, id: NodeId, target: Vec<TempIndex>) -> TempIndex {
264264
if target.len() != 1 {
265-
self.internal_error(id, "inconsistent expression target arity");
265+
self.internal_error(
266+
id,
267+
format!(
268+
"inconsistent expression target arity: {} and 1",
269+
target.len()
270+
),
271+
);
266272
0
267273
} else {
268274
target[0]
@@ -273,7 +279,13 @@ impl<'env> Generator<'env> {
273279
/// interning.
274280
fn require_unary_arg(&self, id: NodeId, args: &[Exp]) -> Exp {
275281
if args.len() != 1 {
276-
self.internal_error(id, "inconsistent expression argument arity");
282+
self.internal_error(
283+
id,
284+
format!(
285+
"inconsistent expression argument arity: {} and 1",
286+
args.len()
287+
),
288+
);
277289
ExpData::Invalid(self.env().new_node_id()).into_exp()
278290
} else {
279291
args[0].to_owned()
@@ -501,6 +513,18 @@ impl<'env> Generator<'env> {
501513
Value::Bool(x) => Constant::Bool(*x),
502514
Value::ByteArray(x) => Constant::ByteArray(x.clone()),
503515
Value::AddressArray(x) => Constant::AddressArray(x.clone()),
516+
Value::Tuple(x) => {
517+
if let Some(inner_ty) = ty.get_vector_element_type() {
518+
Constant::Vector(
519+
x.iter()
520+
.map(|v| self.to_constant(id, inner_ty.clone(), v))
521+
.collect(),
522+
)
523+
} else {
524+
self.internal_error(id, format!("inconsistent tuple type: {:?}", ty));
525+
Constant::Bool(false)
526+
}
527+
},
504528
Value::Vector(x) => {
505529
if let Some(inner_ty) = ty.get_vector_element_type() {
506530
Constant::Vector(
@@ -546,7 +570,14 @@ impl<'env> Generator<'env> {
546570
Operation::Freeze => self.gen_op_call(targets, id, BytecodeOperation::FreezeRef, args),
547571
Operation::Tuple => {
548572
if targets.len() != args.len() {
549-
self.internal_error(id, "inconsistent tuple arity")
573+
self.internal_error(
574+
id,
575+
format!(
576+
"inconsistent tuple arity: {} and {}",
577+
targets.len(),
578+
args.len()
579+
),
580+
)
550581
} else {
551582
for (target, arg) in targets.into_iter().zip(args.iter()) {
552583
self.gen(vec![target], arg)
@@ -1216,6 +1247,7 @@ impl<'env> Generator<'env> {
12161247
match pat {
12171248
Pattern::Wildcard(_) => {
12181249
// Nothing to do
1250+
// TODO(#12475) Should we copy to a temp here?
12191251
},
12201252
Pattern::Var(var_id, sym) => {
12211253
let local = self.find_local_for_pattern(*var_id, *sym, next_scope);

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -223,12 +223,16 @@ impl<'a> ExpRewriterFunctions for LambdaLifter<'a> {
223223
}
224224
}
225225

226-
fn rewrite_enter_scope<'b>(&mut self, vars: impl Iterator<Item = &'b (NodeId, Symbol)>) {
226+
fn rewrite_enter_scope<'b>(
227+
&mut self,
228+
_id: NodeId,
229+
vars: impl Iterator<Item = &'b (NodeId, Symbol)>,
230+
) {
227231
self.scopes
228232
.push(vars.map(|(_, sym)| sym).cloned().collect())
229233
}
230234

231-
fn rewrite_exit_scope(&mut self) {
235+
fn rewrite_exit_scope(&mut self, _id: NodeId) {
232236
let exiting = self.scopes.pop().expect("stack balanced");
233237
// Remove all locals which are bound in the scope we are exiting.
234238
self.free_locals.retain(|name, _| !exiting.contains(name));

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
use log::debug;
1010
use move_model::model::GlobalEnv;
11-
use std::fmt::Write;
11+
use std::io::Write;
1212

1313
pub mod lambda_lifter;
1414

third_party/move/move-compiler-v2/src/flow_insensitive_checkers.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ impl<'env, 'params> SymbolVisitor<'env, 'params> {
112112
}
113113
self.seen_uses.exit_scope();
114114
},
115-
Pre | MidMutate | BeforeThen | BeforeElse => {},
115+
Pre | MidMutate | BeforeThen | BeforeElse | PreSequenceValue => {},
116116
};
117117
},
118118
Lambda(_, pat, _) => {
@@ -129,7 +129,7 @@ impl<'env, 'params> SymbolVisitor<'env, 'params> {
129129
}
130130
self.seen_uses.exit_scope();
131131
},
132-
BeforeBody | MidMutate | BeforeThen | BeforeElse => {},
132+
BeforeBody | MidMutate | BeforeThen | BeforeElse | PreSequenceValue => {},
133133
};
134134
},
135135
Quant(_, _, ranges, ..) => {
@@ -141,7 +141,7 @@ impl<'env, 'params> SymbolVisitor<'env, 'params> {
141141
}
142142
self.seen_uses.exit_scope();
143143
},
144-
BeforeBody | MidMutate | BeforeThen | BeforeElse => {},
144+
BeforeBody | MidMutate | BeforeThen | BeforeElse | PreSequenceValue => {},
145145
};
146146
},
147147
Assign(_, pat, _) => {

third_party/move/move-compiler-v2/src/inliner.rs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,9 @@
3636
///
3737
/// - TODO(10858): add an anchor AST node so we can implement `Return` for inline functions and
3838
/// `Lambda`.
39-
/// - TODO(10850): add a simplifier that simplifies certain code constructs.
4039
use codespan_reporting::diagnostic::Severity;
4140
use itertools::chain;
42-
use log::{info, trace};
41+
use log::{debug, trace};
4342
use move_model::{
4443
ast::{Exp, ExpData, Operation, Pattern, TempIndex},
4544
exp_rewriter::ExpRewriterFunctions,
@@ -64,7 +63,7 @@ type CallSiteLocations = BTreeMap<(QualifiedFunId, QualifiedFunId), BTreeSet<Nod
6463
/// Run inlining on current program's AST. For each function which is target of the compilation,
6564
/// visit that function body and inline any calls to functions marked as "inline".
6665
pub fn run_inlining(env: &mut GlobalEnv) {
67-
info!("Inlining");
66+
debug!("Inlining");
6867
// Get non-inline function roots for running inlining.
6968
// Also generate an error for any target inline functions lacking a body to inline.
7069
let mut todo = get_targets(env);
@@ -1026,15 +1025,19 @@ impl<'env, 'rewriter> ExpRewriterFunctions for InlinedRewriter<'env, 'rewriter>
10261025

10271026
/// Record that the provided symbols have local definitions, so renaming should be done.
10281027
/// Note that incoming vars are from a Pattern *after* renaming, so these are shadowed symbols.
1029-
fn rewrite_enter_scope<'a>(&mut self, vars: impl Iterator<Item = &'a (NodeId, Symbol)>) {
1028+
fn rewrite_enter_scope<'a>(
1029+
&mut self,
1030+
_id: NodeId,
1031+
vars: impl Iterator<Item = &'a (NodeId, Symbol)>,
1032+
) {
10301033
self.shadow_stack
10311034
.enter_scope_after_renaming(vars.map(|(_, sym)| sym));
10321035
}
10331036

10341037
/// On exiting a scope defining some symbols shadowing lambda free vars, record that we have
10351038
/// exited the scope so any occurrences of those free vars should be left alone (if there are
10361039
/// not further shadowing scopes further out).
1037-
fn rewrite_exit_scope(&mut self) {
1040+
fn rewrite_exit_scope(&mut self, _id: NodeId) {
10381041
self.shadow_stack.exit_scope();
10391042
}
10401043

third_party/move/move-compiler-v2/src/lib.rs

Lines changed: 81 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,36 @@
22
// Parts of the project are originally copyright © Meta Platforms, Inc.
33
// SPDX-License-Identifier: Apache-2.0
44

5-
use crate::pipeline::{
6-
ability_processor::AbilityProcessor, avail_copies_analysis::AvailCopiesAnalysisProcessor,
7-
copy_propagation::CopyPropagation, dead_store_elimination::DeadStoreElimination,
8-
exit_state_analysis::ExitStateAnalysisProcessor,
9-
livevar_analysis_processor::LiveVarAnalysisProcessor,
10-
reference_safety_processor::ReferenceSafetyProcessor,
11-
split_critical_edges_processor::SplitCriticalEdgesProcessor,
12-
uninitialized_use_checker::UninitializedUseChecker,
13-
unreachable_code_analysis::UnreachableCodeProcessor,
14-
unreachable_code_remover::UnreachableCodeRemover, variable_coalescing::VariableCoalescing,
5+
pub mod ast_simplifier;
6+
mod bytecode_generator;
7+
pub mod env_pipeline;
8+
mod experiments;
9+
mod file_format_generator;
10+
pub mod flow_insensitive_checkers;
11+
pub mod function_checker;
12+
pub mod inliner;
13+
pub mod logging;
14+
pub mod options;
15+
pub mod pipeline;
16+
17+
use crate::{
18+
env_pipeline::EnvProcessorPipeline,
19+
pipeline::{
20+
ability_processor::AbilityProcessor, avail_copies_analysis::AvailCopiesAnalysisProcessor,
21+
copy_propagation::CopyPropagation, dead_store_elimination::DeadStoreElimination,
22+
exit_state_analysis::ExitStateAnalysisProcessor,
23+
livevar_analysis_processor::LiveVarAnalysisProcessor,
24+
reference_safety_processor::ReferenceSafetyProcessor,
25+
split_critical_edges_processor::SplitCriticalEdgesProcessor,
26+
uninitialized_use_checker::UninitializedUseChecker,
27+
unreachable_code_analysis::UnreachableCodeProcessor,
28+
unreachable_code_remover::UnreachableCodeRemover, variable_coalescing::VariableCoalescing,
29+
},
1530
};
1631
use anyhow::bail;
1732
use codespan_reporting::term::termcolor::{ColorChoice, StandardStream, WriteColor};
1833
pub use experiments::*;
19-
use log::{debug, info, log_enabled, trace, Level};
34+
use log::{debug, info, log_enabled, Level};
2035
use move_binary_format::binary_views::BinaryIndexedView;
2136
use move_command_line_common::files::FileHash;
2237
use move_compiler::{
@@ -35,18 +50,7 @@ use move_stackless_bytecode::function_target_pipeline::{
3550
};
3651
use move_symbol_pool::Symbol;
3752
pub use options::*;
38-
use std::{collections::BTreeSet, path::Path};
39-
40-
mod bytecode_generator;
41-
pub mod env_pipeline;
42-
mod experiments;
43-
mod file_format_generator;
44-
pub mod flow_insensitive_checkers;
45-
pub mod function_checker;
46-
pub mod inliner;
47-
pub mod logging;
48-
pub mod options;
49-
pub mod pipeline;
53+
use std::{collections::BTreeSet, io::Write, path::Path};
5054

5155
/// Run Move compiler and print errors to stderr.
5256
pub fn run_move_compiler_to_stderr(
@@ -57,41 +61,38 @@ pub fn run_move_compiler_to_stderr(
5761
}
5862

5963
/// Run move compiler and print errors to given writer.
60-
pub fn run_move_compiler(
61-
error_writer: &mut impl WriteColor,
64+
pub fn run_move_compiler<W>(
65+
error_writer: &mut W,
6266
options: Options,
63-
) -> anyhow::Result<(GlobalEnv, Vec<AnnotatedCompiledUnit>)> {
67+
) -> anyhow::Result<(GlobalEnv, Vec<AnnotatedCompiledUnit>)>
68+
where
69+
W: WriteColor + Write,
70+
{
6471
logging::setup_logging();
6572
info!("Move Compiler v2");
6673
// Run context check.
6774
let mut env = run_checker(options.clone())?;
6875
check_errors(&env, error_writer, "checking errors")?;
6976

70-
trace!("After context check, GlobalEnv=\n{}", env.dump_env());
77+
debug!("After context check, GlobalEnv=\n{}", env.dump_env());
7178

72-
// Flow-insensitive checks on AST
73-
flow_insensitive_checkers::check_for_unused_vars_and_params(&mut env);
74-
function_checker::check_for_function_typed_parameters(&mut env);
75-
function_checker::check_access_and_use(&mut env, true);
79+
let env_pipeline = create_env_processor_pipeline(&env);
80+
if log_enabled!(Level::Debug) {
81+
env_pipeline.run_and_record(&mut env, error_writer)?;
82+
} else {
83+
env_pipeline.run(&mut env);
84+
}
7685
check_errors(&env, error_writer, "checking errors")?;
7786

78-
trace!(
87+
debug!(
7988
"After flow-insensitive checks, GlobalEnv=\n{}",
8089
env.dump_env()
8190
);
8291

83-
// Run inlining.
84-
inliner::run_inlining(&mut env);
85-
check_errors(&env, error_writer, "inlining")?;
86-
87-
debug!("After inlining, GlobalEnv=\n{}", env.dump_env());
88-
89-
function_checker::check_access_and_use(&mut env, false);
90-
check_errors(&env, error_writer, "post-inlining access checks")?;
91-
9292
// Run code generator
9393
let mut targets = run_bytecode_gen(&env);
9494
check_errors(&env, error_writer, "code generation errors")?;
95+
debug!("After bytecode_gen, GlobalEnv={}", env.dump_env());
9596

9697
// Run transformation pipeline
9798
let pipeline = bytecode_pipeline(&env);
@@ -204,6 +205,40 @@ pub fn run_file_format_gen(env: &GlobalEnv, targets: &FunctionTargetsHolder) ->
204205
file_format_generator::generate_file_format(env, targets)
205206
}
206207

208+
/// Returns the standard env_processor_pipeline
209+
pub fn create_env_processor_pipeline<'b>(env: &GlobalEnv) -> EnvProcessorPipeline<'b> {
210+
let options = env.get_extension::<Options>().expect("options");
211+
let optimize_on = options.experiment_on(Experiment::OPTIMIZE);
212+
213+
let mut env_pipeline = EnvProcessorPipeline::default();
214+
env_pipeline.add(
215+
"unused vars and params checks",
216+
flow_insensitive_checkers::check_for_unused_vars_and_params,
217+
);
218+
env_pipeline.add(
219+
"function typed parameter check",
220+
function_checker::check_for_function_typed_parameters,
221+
);
222+
env_pipeline.add(
223+
"access and use check before inlining",
224+
|env: &mut GlobalEnv| function_checker::check_access_and_use(env, true),
225+
);
226+
env_pipeline.add("inlining", inliner::run_inlining);
227+
env_pipeline.add(
228+
"access and use check after inlining",
229+
|env: &mut GlobalEnv| function_checker::check_access_and_use(env, false),
230+
);
231+
env_pipeline.add("simplifier", {
232+
move |env: &mut GlobalEnv| {
233+
ast_simplifier::run_simplifier(
234+
env,
235+
optimize_on, // eliminate code only if optimize is on
236+
)
237+
}
238+
});
239+
env_pipeline
240+
}
241+
207242
/// Returns the bytecode processing pipeline.
208243
pub fn bytecode_pipeline(env: &GlobalEnv) -> FunctionTargetPipeline {
209244
let options = env.get_extension::<Options>().expect("options");
@@ -274,11 +309,14 @@ pub fn run_bytecode_verifier(units: &[AnnotatedCompiledUnit], env: &mut GlobalEn
274309
}
275310

276311
/// Report any diags in the env to the writer and fail if there are errors.
277-
pub fn check_errors<W: WriteColor>(
312+
pub fn check_errors<W>(
278313
env: &GlobalEnv,
279314
error_writer: &mut W,
280315
msg: &'static str,
281-
) -> anyhow::Result<()> {
316+
) -> anyhow::Result<()>
317+
where
318+
W: WriteColor + Write,
319+
{
282320
let options = env.get_extension::<Options>().unwrap_or_default();
283321
env.report_diag(error_writer, options.report_severity());
284322
if env.has_errors() {

third_party/move/move-compiler-v2/src/pipeline/reference_safety_processor.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1322,8 +1322,8 @@ impl<'env, 'state> LifetimeAnalysisStep<'env, 'state> {
13221322
infos.push((
13231323
loc.clone(),
13241324
format!(
1325-
"conflicting reference {}used here",
1326-
self.display_name_or_empty("", temp)
1325+
"conflicting reference{} used here",
1326+
self.display_name_or_empty(" ", temp)
13271327
),
13281328
))
13291329
}

third_party/move/move-compiler-v2/tests/bytecode-generator/assign.exp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ module 0x42::assign {
1616
Tuple()
1717
}
1818
private fun assign_pattern(s: assign::S,f: u64,h: u64): u64 {
19-
assign::S{ f: f: u64, g: assign::T{ h: h: u64 } } = s;
19+
assign::S{ f: f: u64, g: assign::T{ h: h: u64 } }: assign::S = s;
2020
Add<u64>(f, h)
2121
}
2222
private fun assign_struct(s: &mut assign::S) {

0 commit comments

Comments
 (0)