Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MassaLabs: Implement a Intermediate Representation to improve the compilation process #359

Open
wants to merge 67 commits into
base: next
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
24e687a
feat - move AlgebraicGraph from ir to codegen/air
Leo-Besancon Sep 23, 2024
92952bd
Base IR graph, to improve
Leo-Besancon Sep 26, 2024
f4880ab
Make base IR structure (passes / translation from AST..)
Leo-Besancon Sep 27, 2024
33da4a3
Additional structure (MIR > AIR lowering), added TODO MIR comments
Leo-Besancon Sep 27, 2024
c036336
Add comments to TODOs
Leo-Besancon Sep 30, 2024
d018fd6
Add typed nodes in the Mir
Leo-Besancon Oct 9, 2024
5047968
Update MirValues to account for all case of binding in the AST
Leo-Besancon Oct 11, 2024
f34d16c
Add type for new elements
Leo-Besancon Oct 11, 2024
a615a8b
Add function variable node type
Leo-Besancon Oct 11, 2024
1af3508
Update value.rs
Leo-Besancon Oct 11, 2024
e4ce487
Implement some elements for lowering from AST to MIR
Leo-Besancon Oct 15, 2024
7b554ba
Inlining unit test structure
Soulthym Oct 11, 2024
18c1870
MirType:
Soulthym Oct 11, 2024
21dc897
Add human readable serialization to MirGraph
Soulthym Oct 15, 2024
7c180c9
Make IR test pass
Leo-Besancon Oct 16, 2024
6e58c2a
Merge remote-tracking branch 'origin/thy-ir-inlining' into leo_loweri…
Leo-Besancon Oct 16, 2024
e8fa708
Fix conflict Option<>
Leo-Besancon Oct 16, 2024
b0df63d
fix Migraph pretty printer counters not being shared between recursions
Soulthym Oct 17, 2024
6e1ddc4
Merge branch 'thy-ir-inlining' into feat-implement-ir
Soulthym Oct 17, 2024
6ca0349
Reworked use_list, reworked placeholder mechanic + fmt
Leo-Besancon Oct 17, 2024
ee7f04d
Add placeholder type to avoid issues with Constant(0)
Leo-Besancon Oct 17, 2024
9a2f4f8
MirGraph pretty printer: add call support + track func indexes
Soulthym Oct 17, 2024
1d7ca53
Inlining test: complete Input
Soulthym Oct 17, 2024
33e022d
cargo fmt + clippy
Leo-Besancon Oct 17, 2024
7cadc6f
Fix double Enf in cas of Bindary::Eq
Leo-Besancon Oct 18, 2024
65db04c
improve MirGraph pretty printer
Soulthym Oct 18, 2024
b7834cb
Add root management to differentiate between dead nodes and root nodes
Leo-Besancon Oct 22, 2024
65e0bc6
Add functions tests in MIR
Leo-Besancon Oct 22, 2024
04ff191
Inlining 1/2: replace call(func, args) site by func.body
Soulthym Oct 17, 2024
2876cd8
Inlining 2/2: swap variables for arg values in inlined body
Soulthym Oct 22, 2024
a57a3b0
cargo fmt + clippy
Soulthym Oct 22, 2024
4804834
Add helpers to insert nodes for each operation
Leo-Besancon Nov 5, 2024
35dd907
Generic visitor
Soulthym Nov 6, 2024
03744ad
expose roots and graph to visitor
Soulthym Nov 6, 2024
64cf047
inlining with generic visitor
Soulthym Nov 6, 2024
a999882
implement post order visitor
Soulthym Nov 7, 2024
9176471
fix missing visit of stored nodes
Soulthym Nov 8, 2024
ac51b88
Rename VisitOrder::DepthFirst to Manual
Soulthym Nov 8, 2024
f60e664
Add unrolling and lowering
Leo-Besancon Nov 14, 2024
e9f1bd7
rework Operation: base structure
Soulthym Oct 31, 2024
4910417
fix ir2 module structure
Soulthym Nov 4, 2024
9e43103
implement Sub and Mul
Soulthym Nov 4, 2024
78ab06d
make Operands dyn Value
Soulthym Nov 5, 2024
f066d6a
blanket migration for degree and trace
Soulthym Nov 6, 2024
103f453
added Eq, Clone and Debug support to Value and Operations
Soulthym Nov 13, 2024
d130f97
convert MirValue to the new structure
Soulthym Nov 13, 2024
987aa35
ir2: new graph structure based on enums
Soulthym Nov 19, 2024
a76e357
Split graph module, and several improvements to the graph datastructure
Soulthym Nov 21, 2024
cbd6620
fix builder pattern duplication and split+rename NodeType
Soulthym Nov 26, 2024
858d012
replace asserts by expects
Soulthym Nov 27, 2024
750ecf6
Scope with unique insertions
Soulthym Nov 27, 2024
345cbe7
IsNode derive macro
Soulthym Nov 29, 2024
002dfa5
fix module name
Soulthym Nov 29, 2024
2f1bc62
Merge pull request #3 from massalabs/thy-rework-ir
Soulthym Nov 29, 2024
ced1630
swap-ir-impl starting point (#4)
Soulthym Feb 5, 2025
40c478b
refactor: Remove test file duplication for codegen
Leo-Besancon Feb 5, 2025
bc49b4f
refactor: Remove test file duplication for air
Leo-Besancon Feb 5, 2025
9c8bbfd
remove unused Leaf
Soulthym Feb 5, 2025
b19b1c6
refactor: cleanup MIR module of useless structs
Leo-Besancon Feb 5, 2025
7711db5
refactor: Improve constant prop & value numbering placeholders
Leo-Besancon Feb 5, 2025
0bd41a1
docs: Add documentation to visitor, clean up mir passes mod.rs
Leo-Besancon Feb 5, 2025
4617ad8
refactor: Update root modules and MIR / AIR README.md
Leo-Besancon Feb 6, 2025
2f2149f
chore: fix docs format
Leo-Besancon Feb 6, 2025
a7c918d
docs: document all MIR Op variants
Leo-Besancon Feb 6, 2025
7d7f069
chore: cargo fmt fix + add Mir struct documentation
Leo-Besancon Feb 6, 2025
deec91a
refactor: Sanity check in graph, and add documentation for Mir graph …
Leo-Besancon Feb 6, 2025
471b15c
Add various MIR documentation (#9)
Leo-Besancon Feb 6, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Generated by Cargo
# will have compiled files and executables
/target/
**/target/

# Remove system config files generated by mac os
**/.DS_Store
Expand All @@ -17,4 +17,4 @@ Cargo.lock

# Ignore examples codegen generated files
**/examples/*.rs
**/examples/*.masm
**/examples/*.masm
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ members = [
"air-script",
"parser",
"pass",
"ir",
"mir",
"air",
"codegen/masm",
"codegen/winterfell",
]
Expand Down
3 changes: 2 additions & 1 deletion air-script/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ name = "airc"
path = "src/main.rs"

[dependencies]
air-ir = { package = "air-ir", path = "../ir", version = "0.4" }
mir = { package = "mir", path = "../mir", version = "0.4" }
air-ir = { package = "air-ir", path = "../air", version = "0.4" }
air-parser = { package = "air-parser", path = "../parser", version = "0.4" }
air-pass = { package = "air-pass", path = "../pass", version = "0.1" }
air-codegen-masm = { package = "air-codegen-masm", path = "../codegen/masm", version = "0.4" }
Expand Down
50 changes: 41 additions & 9 deletions air-script/src/cli/transpile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ impl Target {
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
pub enum Pipeline {
WithMIR,
WithoutMIR,
}

#[derive(Args)]
pub struct Transpile {
Expand All @@ -40,28 +45,55 @@ pub struct Transpile {
help = "Defines the target language, defaults to Winterfell"
)]
target: Option<Target>,

#[arg(
short,
long,
help = "Defines the compilation pipeline (WithMIR or WithoutMIR), defaults to WithMIR"
)]
pipeline: Option<Pipeline>,
}

impl Transpile {
pub fn execute(&self) -> Result<(), String> {
println!("============================================================");
println!("Transpiling...");

let input_path = &self.input;

let codemap = Arc::new(CodeMap::new());
let emitter = Arc::new(DefaultEmitter::new(ColorChoice::Auto));
let diagnostics = DiagnosticsHandler::new(Default::default(), codemap.clone(), emitter);

let pipeline = self.pipeline.unwrap_or(Pipeline::WithMIR);
// Parse from file to internal representation
let air = air_parser::parse_file(&diagnostics, codemap, input_path)
.map_err(CompileError::Parse)
.and_then(|ast| {
let mut pipeline = air_parser::transforms::ConstantPropagation::new(&diagnostics)
.chain(air_parser::transforms::Inlining::new(&diagnostics))
.chain(air_ir::passes::AstToAir::new(&diagnostics));
pipeline.run(ast)
});
let air = match pipeline {
Pipeline::WithMIR => {
println!("Transpiling with Mir pipeline...");
air_parser::parse_file(&diagnostics, codemap, input_path)
.map_err(CompileError::Parse)
.and_then(|ast| {
let mut pipeline =
air_parser::transforms::ConstantPropagation::new(&diagnostics)
.chain(mir::passes::AstToMir::new(&diagnostics))
.chain(mir::passes::Inlining::new(&diagnostics))
.chain(mir::passes::Unrolling::new(&diagnostics))
.chain(air_ir::passes::MirToAir::new(&diagnostics));
pipeline.run(ast)
})
}
Pipeline::WithoutMIR => {
println!("Transpiling without Mir pipeline...");
air_parser::parse_file(&diagnostics, codemap, input_path)
.map_err(CompileError::Parse)
.and_then(|ast| {
let mut pipeline =
air_parser::transforms::ConstantPropagation::new(&diagnostics)
.chain(air_parser::transforms::Inlining::new(&diagnostics))
.chain(air_ir::passes::AstToAir::new(&diagnostics));
pipeline.run(ast)
})
}
};

match air {
Ok(air) => {
Expand Down
36 changes: 27 additions & 9 deletions air-script/tests/codegen/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ pub enum Target {
Winterfell,
Masm,
}
pub enum Pipeline {
WithMIR,
WithoutMIR,
}

pub struct Test {
input_path: String,
Expand All @@ -19,20 +23,34 @@ impl Test {
Test { input_path }
}

pub fn transpile(&self, target: Target) -> Result<String, CompileError> {
pub fn transpile(&self, target: Target, pipeline: Pipeline) -> Result<String, CompileError> {
let codemap = Arc::new(CodeMap::new());
let emitter = Arc::new(DefaultEmitter::new(ColorChoice::Auto));
let diagnostics = DiagnosticsHandler::new(Default::default(), codemap.clone(), emitter);

// Parse from file to internal representation
let air = air_parser::parse_file(&diagnostics, codemap, &self.input_path)
.map_err(CompileError::Parse)
.and_then(|ast| {
let mut pipeline = air_parser::transforms::ConstantPropagation::new(&diagnostics)
.chain(air_parser::transforms::Inlining::new(&diagnostics))
.chain(air_ir::passes::AstToAir::new(&diagnostics));
pipeline.run(ast)
})?;
let air = match pipeline {
Pipeline::WithMIR => air_parser::parse_file(&diagnostics, codemap, &self.input_path)
.map_err(CompileError::Parse)
.and_then(|ast| {
let mut pipeline =
air_parser::transforms::ConstantPropagation::new(&diagnostics)
.chain(mir::passes::AstToMir::new(&diagnostics))
.chain(mir::passes::Inlining::new(&diagnostics))
.chain(mir::passes::Unrolling::new(&diagnostics))
.chain(air_ir::passes::MirToAir::new(&diagnostics));
pipeline.run(ast)
})?,
Pipeline::WithoutMIR => air_parser::parse_file(&diagnostics, codemap, &self.input_path)
.map_err(CompileError::Parse)
.and_then(|ast| {
let mut pipeline =
air_parser::transforms::ConstantPropagation::new(&diagnostics)
.chain(air_parser::transforms::Inlining::new(&diagnostics))
.chain(air_ir::passes::AstToAir::new(&diagnostics));
pipeline.run(ast)
})?,
};

let backend: Box<dyn CodeGenerator<Output = String>> = match target {
Target::Winterfell => Box::new(air_codegen_winter::CodeGenerator),
Expand Down
228 changes: 228 additions & 0 deletions air-script/tests/codegen/masm_with_mir.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
use super::helpers::{Pipeline, Target, Test};
use expect_test::expect_file;

// tests_wo_mir
// ================================================================================================

#[test]
fn aux_trace() {
let generated_masm = Test::new("tests/aux_trace/aux_trace.air".to_string())
.transpile(Target::Masm, Pipeline::WithMIR)
.unwrap();

let expected = expect_file!["../aux_trace/aux_trace.masm"];
expected.assert_eq(&generated_masm);
}

#[test]
fn binary() {
let generated_masm = Test::new("tests/binary/binary.air".to_string())
.transpile(Target::Masm, Pipeline::WithMIR)
.unwrap();

let expected = expect_file!["../binary/binary.masm"];
expected.assert_eq(&generated_masm);
}

#[test]
fn periodic_columns() {
let generated_masm = Test::new("tests/periodic_columns/periodic_columns.air".to_string())
.transpile(Target::Masm, Pipeline::WithMIR)
.unwrap();

let expected = expect_file!["../periodic_columns/periodic_columns.masm"];
expected.assert_eq(&generated_masm);
}

#[test]
fn pub_inputs() {
let generated_masm = Test::new("tests/pub_inputs/pub_inputs.air".to_string())
.transpile(Target::Masm, Pipeline::WithMIR)
.unwrap();

let expected = expect_file!["../pub_inputs/pub_inputs.masm"];
expected.assert_eq(&generated_masm);
}

#[test]
fn system() {
let generated_masm = Test::new("tests/system/system.air".to_string())
.transpile(Target::Masm, Pipeline::WithMIR)
.unwrap();

let expected = expect_file!["../system/system.masm"];
expected.assert_eq(&generated_masm);
}

#[test]
fn bitwise() {
let generated_masm = Test::new("tests/bitwise/bitwise.air".to_string())
.transpile(Target::Masm, Pipeline::WithMIR)
.unwrap();

let expected = expect_file!["../bitwise/bitwise.masm"];
expected.assert_eq(&generated_masm);
}

#[test]
fn constants() {
let generated_masm = Test::new("tests/constants/constants.air".to_string())
.transpile(Target::Masm, Pipeline::WithMIR)
.unwrap();

let expected = expect_file!["../constants/constants.masm"];
expected.assert_eq(&generated_masm);
}

#[test]
fn constant_in_range() {
let generated_masm = Test::new("tests/constant_in_range/constant_in_range.air".to_string())
.transpile(Target::Masm, Pipeline::WithMIR)
.unwrap();

let expected = expect_file!["../constant_in_range/constant_in_range.masm"];
expected.assert_eq(&generated_masm);
}

#[test]
fn evaluators() {
let generated_masm = Test::new("tests/evaluators/evaluators.air".to_string())
.transpile(Target::Masm, Pipeline::WithMIR)
.unwrap();

let expected = expect_file!["../evaluators/evaluators.masm"];
expected.assert_eq(&generated_masm);
}

#[test]
fn functions_simple() {
let generated_masm = Test::new("tests/functions/functions_simple.air".to_string())
.transpile(Target::Masm, Pipeline::WithMIR)
.unwrap();

let expected = expect_file!["../functions/functions_simple.masm"];
expected.assert_eq(&generated_masm);
}

#[test]
fn functions_simple_inlined() {
// make sure that the constraints generated using inlined functions are the same as the ones
// generated using regular functions
let generated_masm = Test::new("tests/functions/inlined_functions_simple.air".to_string())
.transpile(Target::Masm, Pipeline::WithMIR)
.unwrap();
let expected = expect_file!["../functions/functions_simple.masm"];
expected.assert_eq(&generated_masm);
}

#[test]
fn functions_complex() {
let generated_masm = Test::new("tests/functions/functions_complex.air".to_string())
.transpile(Target::Masm, Pipeline::WithMIR)
.unwrap();

let expected = expect_file!["../functions/functions_complex_with_mir.masm"];
expected.assert_eq(&generated_masm);
}

#[test]
fn variables() {
let generated_masm = Test::new("tests/variables/variables.air".to_string())
.transpile(Target::Masm, Pipeline::WithMIR)
.unwrap();

let expected = expect_file!["../variables/variables.masm"];
expected.assert_eq(&generated_masm);
}

#[test]
fn trace_col_groups() {
let generated_masm = Test::new("tests/trace_col_groups/trace_col_groups.air".to_string())
.transpile(Target::Masm, Pipeline::WithMIR)
.unwrap();

let expected = expect_file!["../trace_col_groups/trace_col_groups.masm"];
expected.assert_eq(&generated_masm);
}

#[test]
fn indexed_trace_access() {
let generated_masm =
Test::new("tests/indexed_trace_access/indexed_trace_access.air".to_string())
.transpile(Target::Masm, Pipeline::WithMIR)
.unwrap();

let expected = expect_file!["../indexed_trace_access/indexed_trace_access.masm"];
expected.assert_eq(&generated_masm);
}

#[test]
#[ignore] // TODO: There is some non-determinism in the IR creation, unskip this test once it is fixed
fn random_values() {
let generated_masm = Test::new("tests/random_values/random_values_simple.air".to_string())
.transpile(Target::Masm, Pipeline::WithMIR)
.unwrap();
let expected = expect_file!["../random_values/random_values.masm"];
expected.assert_eq(&generated_masm);

let generated_masm = Test::new("tests/random_values/random_values_bindings.air".to_string())
.transpile(Target::Masm, Pipeline::WithMIR)
.unwrap();
let expected = expect_file!["../random_values/random_values.masm"];
expected.assert_eq(&generated_masm);
}

#[test]
fn list_comprehension() {
let generated_masm = Test::new("tests/list_comprehension/list_comprehension.air".to_string())
.transpile(Target::Masm, Pipeline::WithMIR)
.unwrap();

let expected = expect_file!["../list_comprehension/list_comprehension_with_mir.masm"];
expected.assert_eq(&generated_masm);
}

#[test]
fn list_folding() {
let generated_masm = Test::new("tests/list_folding/list_folding.air".to_string())
.transpile(Target::Masm, Pipeline::WithMIR)
.unwrap();

let expected = expect_file!["../list_folding/list_folding_with_mir.masm"];
expected.assert_eq(&generated_masm);
}

#[test]
#[ignore] // TODO: There is some non-determinism in the IR creation, unskip this test once it is fixed
fn selectors() {
let generated_masm = Test::new("tests/selectors/selectors.air".to_string())
.transpile(Target::Masm, Pipeline::WithMIR)
.unwrap();
let expected = expect_file!["../selectors/selectors.masm"];
expected.assert_eq(&generated_masm);

let generated_masm = Test::new("tests/selectors/selectors_with_evaluators.air".to_string())
.transpile(Target::Masm, Pipeline::WithMIR)
.unwrap();
let expected = expect_file!["../selectors/selectors.masm"];
expected.assert_eq(&generated_masm);
}

#[test]
fn constraint_comprehension() {
let generated_masm =
Test::new("tests/constraint_comprehension/constraint_comprehension.air".to_string())
.transpile(Target::Masm, Pipeline::WithMIR)
.unwrap();

let expected = expect_file!["../constraint_comprehension/constraint_comprehension.masm"];
expected.assert_eq(&generated_masm);

let generated_masm =
Test::new("tests/constraint_comprehension/cc_with_evaluators.air".to_string())
.transpile(Target::Masm, Pipeline::WithMIR)
.unwrap();

let expected = expect_file!["../constraint_comprehension/constraint_comprehension.masm"];
expected.assert_eq(&generated_masm);
}
Loading