Skip to content

Commit

Permalink
[compiler-v2] Use livevar analysis to optimize file format generation (
Browse files Browse the repository at this point in the history
…aptos-labs#9361)

* [compiler-v2] Use livevar analysis to optimize file format generation

This connects the live-variable analysis which is already present in the Move prover to the v2 bytecode pipeline. This information is then used in the file-format generator to make better decisions when to move or copy values.

Livevar is a standard compiler construction data flow analysis. It computes the set of variables which are alive (being used in subsequent reachable code) before and after each program point. The implementation from the prover uses our dataflow analysis framework and is [here](https://github.com/aptos-labs/aptos-core/blob/206f529c0c9d8488e27d2e50297178f0caf429a5/third_party/move/move-prover/bytecode/src/livevar_analysis.rs#L381). In this PR we create the first processing step in the bytecode pipeline with the new module `pipeline/livevar_analysis_step.rs` which forwards logical work to the existing analysis.

* Addressing reviewer comments.
  • Loading branch information
wrwg authored Aug 11, 2023
1 parent 42aa399 commit 21b7429
Show file tree
Hide file tree
Showing 19 changed files with 1,631 additions and 185 deletions.

Large diffs are not rendered by default.

8 changes: 5 additions & 3 deletions third_party/move/move-compiler-v2/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ mod bytecode_generator;
mod experiments;
mod file_format_generator;
mod options;
pub mod pipeline;

use crate::pipeline::livevar_analysis_processor::LiveVarAnalysisProcessor;
use anyhow::anyhow;
use codespan_reporting::term::termcolor::{ColorChoice, StandardStream, WriteColor};
pub use experiments::*;
Expand Down Expand Up @@ -121,9 +123,9 @@ pub fn run_file_format_gen(

/// Returns the bytecode processing pipeline.
pub fn bytecode_pipeline(_env: &GlobalEnv) -> FunctionTargetPipeline {
// TODO: insert processors here as we proceed.
// Use `env.get_extension::<Options>()` to access compiler options
FunctionTargetPipeline::default()
let mut pipeline = FunctionTargetPipeline::default();
pipeline.add_processor(Box::new(LiveVarAnalysisProcessor()));
pipeline
}

/// Report any diags in the env to the writer and fail if there are errors.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Copyright © Aptos Foundation
// Parts of the project are originally copyright © Meta Platforms, Inc.
// SPDX-License-Identifier: Apache-2.0

//! Implements a live-variable analysis processor, annotating lifetime information about locals.
//! See also https://en.wikipedia.org/wiki/Live-variable_analysis
use move_model::model::FunctionEnv;
use move_stackless_bytecode::{
function_target::{FunctionData, FunctionTarget},
function_target_pipeline::{FunctionTargetProcessor, FunctionTargetsHolder},
livevar_analysis,
};

pub struct LiveVarAnalysisProcessor();

impl FunctionTargetProcessor for LiveVarAnalysisProcessor {
fn process(
&self,
_targets: &mut FunctionTargetsHolder,
fun_env: &FunctionEnv,
mut data: FunctionData,
_scc_opt: Option<&[FunctionEnv]>,
) -> FunctionData {
// Call the existing live-var analysis from the move-prover.
let target = FunctionTarget::new(fun_env, &data);
let offset_to_live_refs = livevar_analysis::LiveVarAnnotation::from_map(
livevar_analysis::run_livevar_analysis(&target, &data.code),
);
// Annotate the result on the function data.
data.annotations.set(offset_to_live_refs, true);
data
}

fn name(&self) -> String {
"LiveVarAnalysisProcessor".to_owned()
}
}

impl LiveVarAnalysisProcessor {
/// Registers annotation formatter at the given function target. This is for debugging and
/// testing.
pub fn register_formatters(target: &FunctionTarget) {
target.register_annotation_formatter(Box::new(livevar_analysis::format_livevar_annotation))
}
}
4 changes: 4 additions & 0 deletions third_party/move/move-compiler-v2/src/pipeline/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// Copyright © Aptos Foundation
// Parts of the project are originally copyright © Meta Platforms, Inc.
// SPDX-License-Identifier: Apache-2.0
pub mod livevar_analysis_processor;
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,67 @@ fun assign::assign_struct($t0: &mut assign::S) {
5: return ()
}

============ after LiveVarAnalysisProcessor: ================

[variant baseline]
fun assign::assign_field($t0: &mut assign::S, $t1: u64) {
var $t2: &mut u64
# live vars: $t0, $t1
0: $t2 := borrow_field<assign::S>.f($t0)
# live vars: $t1, $t2
1: write_ref($t2, $t1)
# live vars:
2: return ()
}


[variant baseline]
fun assign::assign_int($t0: &mut u64) {
var $t1: u64
# live vars: $t0
0: $t1 := 42
# live vars: $t0, $t1
1: write_ref($t0, $t1)
# live vars:
2: return ()
}


[variant baseline]
fun assign::assign_pattern($t0: assign::S, $t1: u64, $t2: u64): u64 {
var $t3: u64
var $t4: assign::T
# live vars: $t0
0: ($t1, $t4) := unpack assign::S($t0)
# live vars: $t1, $t4
1: $t2 := unpack assign::T($t4)
# live vars: $t1, $t2
2: $t3 := +($t1, $t2)
# live vars: $t3
3: return $t3
}


[variant baseline]
fun assign::assign_struct($t0: &mut assign::S) {
var $t1: assign::S
var $t2: u64
var $t3: assign::T
var $t4: u64
# live vars: $t0
0: $t2 := 42
# live vars: $t0, $t2
1: $t4 := 42
# live vars: $t0, $t2, $t4
2: $t3 := pack assign::T($t4)
# live vars: $t0, $t2, $t3
3: $t1 := pack assign::S($t2, $t3)
# live vars: $t0, $t1
4: write_ref($t0, $t1)
# live vars:
5: return ()
}


============ disassembled file-format ==================
// Move bytecode v6
Expand All @@ -57,18 +118,18 @@ struct S {

assign_field(Arg0: &mut S, Arg1: u64) {
B0:
0: CopyLoc[0](Arg0: &mut S)
0: MoveLoc[0](Arg0: &mut S)
1: MutBorrowField[0](S.f: u64)
2: StLoc[2](loc0: &mut u64)
3: CopyLoc[1](Arg1: u64)
4: CopyLoc[2](loc0: &mut u64)
3: MoveLoc[1](Arg1: u64)
4: MoveLoc[2](loc0: &mut u64)
5: WriteRef
6: Ret
}
assign_int(Arg0: &mut u64) {
B0:
0: LdConst[0](U64: [42, 0, 0, 0, 0, 0, 0, 0])
1: CopyLoc[0](Arg0: &mut u64)
1: MoveLoc[0](Arg0: &mut u64)
2: WriteRef
3: Ret
}
Expand All @@ -86,7 +147,7 @@ B0:
1: LdConst[0](U64: [42, 0, 0, 0, 0, 0, 0, 0])
2: Pack[0](T)
3: Pack[1](S)
4: CopyLoc[0](Arg0: &mut S)
4: MoveLoc[0](Arg0: &mut S)
5: WriteRef
6: Ret
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,130 @@ fun borrow::mut_param($t0: u64): u64 {
5: return $t1
}

============ after LiveVarAnalysisProcessor: ================

[variant baseline]
fun borrow::field($t0: &borrow::S): u64 {
var $t1: u64
var $t2: &u64
var $t3: &u64
# live vars: $t0
0: $t3 := borrow_field<borrow::S>.f($t0)
# live vars: $t3
1: $t2 := move($t3)
# live vars: $t2
2: $t1 := read_ref($t2)
# live vars: $t1
3: return $t1
}


[variant baseline]
fun borrow::local($t0: u64): u64 {
var $t1: u64
var $t2: u64
var $t3: u64
var $t4: &u64
var $t5: &u64
# live vars:
0: $t3 := 33
# live vars: $t3
1: $t2 := move($t3)
# live vars: $t2
2: $t5 := borrow_local($t2)
# live vars: $t5
3: $t4 := move($t5)
# live vars: $t4
4: $t1 := read_ref($t4)
# live vars: $t1
5: return $t1
}


[variant baseline]
fun borrow::param($t0: u64): u64 {
var $t1: u64
var $t2: &u64
var $t3: &u64
# live vars: $t0
0: $t3 := borrow_local($t0)
# live vars: $t3
1: $t2 := move($t3)
# live vars: $t2
2: $t1 := read_ref($t2)
# live vars: $t1
3: return $t1
}


[variant baseline]
fun borrow::mut_field($t0: &mut borrow::S): u64 {
var $t1: u64
var $t2: &mut u64
var $t3: &mut u64
var $t4: u64
# live vars: $t0
0: $t3 := borrow_field<borrow::S>.f($t0)
# live vars: $t3
1: $t2 := move($t3)
# live vars: $t2
2: $t4 := 22
# live vars: $t2, $t4
3: write_ref($t2, $t4)
# live vars: $t2
4: $t1 := read_ref($t2)
# live vars: $t1
5: return $t1
}


[variant baseline]
fun borrow::mut_local($t0: u64): u64 {
var $t1: u64
var $t2: u64
var $t3: u64
var $t4: &mut u64
var $t5: &mut u64
var $t6: u64
# live vars:
0: $t3 := 33
# live vars: $t3
1: $t2 := move($t3)
# live vars: $t2
2: $t5 := borrow_local($t2)
# live vars: $t5
3: $t4 := move($t5)
# live vars: $t4
4: $t6 := 22
# live vars: $t4, $t6
5: write_ref($t4, $t6)
# live vars: $t4
6: $t1 := read_ref($t4)
# live vars: $t1
7: return $t1
}


[variant baseline]
fun borrow::mut_param($t0: u64): u64 {
var $t1: u64
var $t2: &mut u64
var $t3: &mut u64
var $t4: u64
# live vars: $t0
0: $t3 := borrow_local($t0)
# live vars: $t3
1: $t2 := move($t3)
# live vars: $t2
2: $t4 := 22
# live vars: $t2, $t4
3: write_ref($t2, $t4)
# live vars: $t2
4: $t1 := read_ref($t2)
# live vars: $t1
5: return $t1
}


============ disassembled file-format ==================
// Move bytecode v6
Expand All @@ -98,10 +222,10 @@ struct S {

field(Arg0: &S): u64 {
B0:
0: CopyLoc[0](Arg0: &S)
0: MoveLoc[0](Arg0: &S)
1: ImmBorrowField[0](S.f: u64)
2: StLoc[1](loc0: &u64)
3: CopyLoc[1](loc0: &u64)
3: MoveLoc[1](loc0: &u64)
4: ReadRef
5: Ret
}
Expand All @@ -112,27 +236,27 @@ B0:
1: StLoc[1](loc0: u64)
2: ImmBorrowLoc[1](loc0: u64)
3: StLoc[2](loc1: &u64)
4: CopyLoc[2](loc1: &u64)
4: MoveLoc[2](loc1: &u64)
5: ReadRef
6: Ret
}
param(Arg0: u64): u64 {
B0:
0: ImmBorrowLoc[0](Arg0: u64)
1: StLoc[1](loc0: &u64)
2: CopyLoc[1](loc0: &u64)
2: MoveLoc[1](loc0: &u64)
3: ReadRef
4: Ret
}
mut_field(Arg0: &mut S): u64 {
B0:
0: CopyLoc[0](Arg0: &mut S)
0: MoveLoc[0](Arg0: &mut S)
1: MutBorrowField[0](S.f: u64)
2: StLoc[1](loc0: &mut u64)
3: LdConst[1](U64: [22, 0, 0, 0, 0, 0, 0, 0])
4: CopyLoc[1](loc0: &mut u64)
5: WriteRef
6: CopyLoc[1](loc0: &mut u64)
6: MoveLoc[1](loc0: &mut u64)
7: ReadRef
8: Ret
}
Expand All @@ -146,7 +270,7 @@ B0:
4: LdConst[1](U64: [22, 0, 0, 0, 0, 0, 0, 0])
5: CopyLoc[2](loc1: &mut u64)
6: WriteRef
7: CopyLoc[2](loc1: &mut u64)
7: MoveLoc[2](loc1: &mut u64)
8: ReadRef
9: Ret
}
Expand All @@ -157,7 +281,7 @@ B0:
2: LdConst[1](U64: [22, 0, 0, 0, 0, 0, 0, 0])
3: CopyLoc[1](loc0: &mut u64)
4: WriteRef
5: CopyLoc[1](loc0: &mut u64)
5: MoveLoc[1](loc0: &mut u64)
6: ReadRef
7: Ret
}
Expand Down
Loading

0 comments on commit 21b7429

Please sign in to comment.