Skip to content

Commit

Permalink
Add tests for module analysis
Browse files Browse the repository at this point in the history
  • Loading branch information
Y-Nak committed Dec 6, 2024
1 parent d23040b commit f12ea73
Show file tree
Hide file tree
Showing 12 changed files with 381 additions and 0 deletions.
5 changes: 5 additions & 0 deletions crates/codegen/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,8 @@ sonatina-triple = { path = "../triple", version = "0.0.3-alpha" }
sonatina-macros = { path = "../macros", version = "0.0.3-alpha" }
dashmap = { version = "6.1", features = ["rayon"] }
indexmap = { version = "2.0" }

[dev-dependencies]
sonatina-parser = { path = "../parser", version = "0.0.3-alpha" }
insta = "1.41"
dir-test = "0.4"
4 changes: 4 additions & 0 deletions crates/codegen/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
fn main() {
#[cfg(test)]
println!("cargo:rerun-if-changed=./test_files");
}
22 changes: 22 additions & 0 deletions crates/codegen/test_files/module_analysis/bidirectional.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
source: crates/codegen/tests/module_analysis.rs
input_file: test_files/module_analysis/bidirectional.sntn
---
ModuleDependency: Bidirectional

SCC: [`%fx`]
SCC: [`%f1`, `%f2`]
SCC: [`%f3`]
SCC: [`%f4`]
SCC: [`%f5`]
SCC: [`%f6`]
SCC: [`%f7`]

`%fx` = FuncInfo { is_non_recursive: false, flow: OutgoingOnly, is_leaf: true }
`%f1` = FuncInfo { is_non_recursive: false, flow: Bidirectional, is_leaf: false }
`%f2` = FuncInfo { is_non_recursive: false, flow: Bidirectional, is_leaf: false }
`%f3` = FuncInfo { is_non_recursive: false, flow: Closed, is_leaf: false }
`%f4` = FuncInfo { is_non_recursive: true, flow: OutgoingOnly, is_leaf: false }
`%f5` = FuncInfo { is_non_recursive: false, flow: Bidirectional, is_leaf: false }
`%f6` = FuncInfo { is_non_recursive: false, flow: Bidirectional, is_leaf: false }
`%f7` = FuncInfo { is_non_recursive: true, flow: OutgoingOnly, is_leaf: false }
46 changes: 46 additions & 0 deletions crates/codegen/test_files/module_analysis/bidirectional.sntn
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
target = "evm-ethereum-london"

declare external %fx();

func public %f1() {
block0:
call %f2;
return;
}

func private %f2() {
block0:
call %f1;
call %fx;
return;
}

func private %f3() {
block0:
call %f3;
return;
}

func private %f4() {
block0:
call %f5;
return;
}

func public %f5() {
block0:
call %f6;
return;
}

func private %f6() {
block0:
call %fx;
return;
}

func private %f7() {
block0:
call %fx;
call %f2;
}
18 changes: 18 additions & 0 deletions crates/codegen/test_files/module_analysis/closed.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
source: crates/codegen/tests/module_analysis.rs
input_file: test_files/module_analysis/closed.sntn
---
ModuleDependency: Closed

SCC: [`%f1`, `%f2`]
SCC: [`%f3`]
SCC: [`%f4`]
SCC: [`%f5`]
SCC: [`%f6`]

`%f1` = FuncInfo { is_non_recursive: false, flow: Closed, is_leaf: false }
`%f2` = FuncInfo { is_non_recursive: false, flow: Closed, is_leaf: false }
`%f3` = FuncInfo { is_non_recursive: false, flow: Closed, is_leaf: false }
`%f4` = FuncInfo { is_non_recursive: true, flow: Closed, is_leaf: false }
`%f5` = FuncInfo { is_non_recursive: true, flow: Closed, is_leaf: false }
`%f6` = FuncInfo { is_non_recursive: true, flow: Closed, is_leaf: true }
38 changes: 38 additions & 0 deletions crates/codegen/test_files/module_analysis/closed.sntn
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
target = "evm-ethereum-london"

func private %f1() {
block0:
call %f2;
return;
}

func private %f2() {
block0:
call %f1;
return;
}

func private %f3() {
block0:
call %f3;
return;
}

func private %f4() {
block0:
call %f5;
call %f1;
return;
}

func private %f5() {
block0:
call %f6;
call %f2;
return;
}

func private %f6() {
block0:
return;
}
18 changes: 18 additions & 0 deletions crates/codegen/test_files/module_analysis/incoming.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
source: crates/codegen/tests/module_analysis.rs
input_file: test_files/module_analysis/incoming.sntn
---
ModuleDependency: IncomingOnly

SCC: [`%f1`, `%f2`]
SCC: [`%f3`]
SCC: [`%f4`]
SCC: [`%f5`]
SCC: [`%f6`]

`%f1` = FuncInfo { is_non_recursive: false, flow: IncomingOnly, is_leaf: false }
`%f2` = FuncInfo { is_non_recursive: false, flow: IncomingOnly, is_leaf: false }
`%f3` = FuncInfo { is_non_recursive: false, flow: Closed, is_leaf: false }
`%f4` = FuncInfo { is_non_recursive: true, flow: IncomingOnly, is_leaf: false }
`%f5` = FuncInfo { is_non_recursive: true, flow: IncomingOnly, is_leaf: false }
`%f6` = FuncInfo { is_non_recursive: true, flow: IncomingOnly, is_leaf: true }
36 changes: 36 additions & 0 deletions crates/codegen/test_files/module_analysis/incoming.sntn
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
target = "evm-ethereum-london"

func public %f1() {
block0:
call %f2;
return;
}

func private %f2() {
block0:
call %f1;
return;
}

func private %f3() {
block0:
call %f3;
return;
}

func public %f4() {
block0:
call %f5;
return;
}

func private %f5() {
block0:
call %f6;
return;
}

func private %f6() {
block0:
return;
}
20 changes: 20 additions & 0 deletions crates/codegen/test_files/module_analysis/outgoing.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
source: crates/codegen/tests/module_analysis.rs
input_file: test_files/module_analysis/outgoing.sntn
---
ModuleDependency: OutgoingOnly

SCC: [`%fx`]
SCC: [`%f1`, `%f2`]
SCC: [`%f3`]
SCC: [`%f4`]
SCC: [`%f5`]
SCC: [`%f6`]

`%fx` = FuncInfo { is_non_recursive: false, flow: OutgoingOnly, is_leaf: true }
`%f1` = FuncInfo { is_non_recursive: false, flow: OutgoingOnly, is_leaf: false }
`%f2` = FuncInfo { is_non_recursive: false, flow: OutgoingOnly, is_leaf: false }
`%f3` = FuncInfo { is_non_recursive: false, flow: Closed, is_leaf: false }
`%f4` = FuncInfo { is_non_recursive: true, flow: OutgoingOnly, is_leaf: false }
`%f5` = FuncInfo { is_non_recursive: true, flow: OutgoingOnly, is_leaf: false }
`%f6` = FuncInfo { is_non_recursive: true, flow: OutgoingOnly, is_leaf: false }
40 changes: 40 additions & 0 deletions crates/codegen/test_files/module_analysis/outgoing.sntn
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
target = "evm-ethereum-london"

declare external %fx();

func private %f1() {
block0:
call %f2;
return;
}

func private %f2() {
block0:
call %f1;
call %fx;
return;
}

func private %f3() {
block0:
call %f3;
return;
}

func private %f4() {
block0:
call %f5;
return;
}

func private %f5() {
block0:
call %f6;
return;
}

func private %f6() {
block0:
call %fx;
return;
}
63 changes: 63 additions & 0 deletions crates/codegen/tests/common.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
use std::io::Write;

use sonatina_parser::ParsedModule;

pub fn parse_module(file_path: &str) -> ParsedModule {
let content = std::fs::read_to_string(file_path).unwrap();

match sonatina_parser::parse_module(&content) {
Ok(r) => r,
Err(errs) => {
let mut v: Vec<u8> = Vec::new();
for err in errs {
err.print(&mut v, file_path, &content, false).unwrap();
writeln!(&mut v).unwrap();
}
let err_str = String::from_utf8(v).unwrap();

panic!("{err_str}");
}
}
}

// copied from fe test-utils
/// A macro to assert that a value matches a snapshot.
/// If the snapshot does not exist, it will be created in the same directory as
/// the test file.
#[macro_export]
macro_rules! snap_test {
($value:expr, $fixture_path: expr) => {
snap_test!($value, $fixture_path, None)
};

($value:expr, $fixture_path: expr, $suffix: expr) => {
let mut settings = insta::Settings::new();
let fixture_path = ::std::path::Path::new($fixture_path);
let fixture_dir = fixture_path.parent().unwrap();
let fixture_name = fixture_path.file_stem().unwrap().to_str().unwrap();

settings.set_snapshot_path(fixture_dir);
settings.set_input_file($fixture_path);
settings.set_prepend_module_to_snapshot(false);
settings.set_omit_expression(true);

let suffix: Option<&str> = $suffix;
let name = if let Some(suffix) = suffix {
format!("{fixture_name}.{suffix}")
} else {
fixture_name.into()
};
settings.bind(|| {
insta::_macro_support::assert_snapshot(
(name, $value.as_str()).into(),
std::path::Path::new(env!("CARGO_MANIFEST_DIR")),
fixture_name,
module_path!(),
file!(),
line!(),
&$value,
)
.unwrap()
})
};
}
71 changes: 71 additions & 0 deletions crates/codegen/tests/module_analysis.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
mod common;

use std::fmt::Write;

use dir_test::{dir_test, Fixture};
use indexmap::IndexSet;
use sonatina_codegen::module_analysis::{analyze_module, ModuleInfo, SccRef};
use sonatina_ir::module::ModuleCtx;
use sonatina_parser::ParsedModule;

#[dir_test(
dir: "$CARGO_MANIFEST_DIR/test_files/module_analysis/",
glob: "*.sntn"
loader: common::parse_module,
)]
fn test(fixture: Fixture<ParsedModule>) {
let module = &fixture.content().module;
let module_info = analyze_module(module);
let info_string = dump_module_info(&module.ctx, &module_info);

snap_test!(info_string, fixture.path());
}

fn dump_module_info(ctx: &ModuleCtx, info: &ModuleInfo) -> String {
let mut s = String::new();
// Write access pattern.
writeln!(&mut s, "ModuleDependency: {}\n", info.access_pattern).unwrap();

let mut sccs: IndexSet<SccRef> = IndexSet::default();

// Write SCCs.
for func_ref in info.call_graph.funcs() {
let scc_ref = info.scc.scc_ref(func_ref);
if sccs.insert(info.scc.scc_ref(func_ref)) {
write!(&mut s, "SCC: [").unwrap();
let mut components: Vec<_> = info
.scc
.scc_info(scc_ref)
.components
.iter()
.copied()
.collect();
components.sort_unstable();
let mut components = components.into_iter();

if let Some(func_ref) = components.next() {
ctx.func_sig(func_ref, |sig| {
write!(&mut s, "`%{}`", sig.name()).unwrap();
});
}

for func_ref in components {
ctx.func_sig(func_ref, |sig| {
write!(&mut s, ", `%{}`", sig.name()).unwrap();
});
}

writeln!(&mut s, "]").unwrap();
}
}
writeln!(&mut s).unwrap();

for func_ref in info.call_graph.funcs() {
let func_info = info.func_info.get(&func_ref).unwrap();
ctx.func_sig(func_ref, |sig| {
writeln!(&mut s, "`%{}` = {:?}", sig.name(), func_info).unwrap();
});
}

s
}

0 comments on commit f12ea73

Please sign in to comment.