223 lines
6.6 KiB
Rust
223 lines
6.6 KiB
Rust
//! Integration tests for the Slang compiler with optimizer
|
|
//!
|
|
//! These tests compile Slang source code and verify both the compilation
|
|
//! and optimization passes work correctly together using snapshot testing.
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use compiler::Compiler;
|
|
use indoc::indoc;
|
|
use parser::Parser;
|
|
use tokenizer::Tokenizer;
|
|
|
|
/// Compile Slang source code and return both unoptimized and optimized output
|
|
fn compile_with_and_without_optimization(source: &str) -> String {
|
|
// Compile for unoptimized output
|
|
let tokenizer = Tokenizer::from(source);
|
|
let parser = Parser::new(tokenizer);
|
|
let compiler = Compiler::new(parser, None);
|
|
let result = compiler.compile();
|
|
|
|
// Get unoptimized output
|
|
let mut unoptimized_writer = std::io::BufWriter::new(Vec::new());
|
|
result
|
|
.instructions
|
|
.write(&mut unoptimized_writer)
|
|
.expect("Failed to write unoptimized output");
|
|
let unoptimized_bytes = unoptimized_writer
|
|
.into_inner()
|
|
.expect("Failed to get bytes");
|
|
let unoptimized = String::from_utf8(unoptimized_bytes).expect("Invalid UTF-8");
|
|
|
|
// Compile again for optimized output
|
|
let tokenizer2 = Tokenizer::from(source);
|
|
let parser2 = Parser::new(tokenizer2);
|
|
let compiler2 = Compiler::new(parser2, None);
|
|
let result2 = compiler2.compile();
|
|
|
|
// Apply optimizations
|
|
let optimized_instructions = optimizer::optimize(result2.instructions);
|
|
|
|
// Get optimized output
|
|
let mut optimized_writer = std::io::BufWriter::new(Vec::new());
|
|
optimized_instructions
|
|
.write(&mut optimized_writer)
|
|
.expect("Failed to write optimized output");
|
|
let optimized_bytes = optimized_writer.into_inner().expect("Failed to get bytes");
|
|
let optimized = String::from_utf8(optimized_bytes).expect("Invalid UTF-8");
|
|
|
|
// Combine both outputs with clear separators
|
|
format!(
|
|
"## Unoptimized Output\n\n{}\n## Optimized Output\n\n{}",
|
|
unoptimized, optimized
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn test_simple_leaf_function() {
|
|
let source = "fn test() { let x = 10; }";
|
|
let output = compile_with_and_without_optimization(source);
|
|
insta::assert_snapshot!(output);
|
|
}
|
|
|
|
#[test]
|
|
fn test_function_with_call() {
|
|
let source = indoc! {"
|
|
fn add(a, b) { return a + b; }
|
|
fn main() { let x = add(5, 10); }
|
|
"};
|
|
let output = compile_with_and_without_optimization(source);
|
|
insta::assert_snapshot!(output);
|
|
}
|
|
|
|
#[test]
|
|
fn test_constant_folding() {
|
|
let source = "let x = 5 + 10;";
|
|
let output = compile_with_and_without_optimization(source);
|
|
insta::assert_snapshot!(output);
|
|
}
|
|
|
|
#[test]
|
|
fn test_algebraic_simplification() {
|
|
let source = "let x = 5; let y = x * 1;";
|
|
let output = compile_with_and_without_optimization(source);
|
|
insta::assert_snapshot!(output);
|
|
}
|
|
|
|
#[test]
|
|
fn test_strength_reduction() {
|
|
let source = "fn double(x) { return x * 2; }";
|
|
let output = compile_with_and_without_optimization(source);
|
|
insta::assert_snapshot!(output);
|
|
}
|
|
|
|
#[test]
|
|
fn test_dead_code_elimination() {
|
|
let source = indoc! {"
|
|
fn compute(x) {
|
|
let unused = 20;
|
|
return x + 1;
|
|
}
|
|
"};
|
|
let output = compile_with_and_without_optimization(source);
|
|
insta::assert_snapshot!(output);
|
|
}
|
|
|
|
#[test]
|
|
fn test_peephole_comparison_fusion() {
|
|
let source = indoc! {"
|
|
fn compare(x, y) {
|
|
if (x > y) {
|
|
let z = 1;
|
|
}
|
|
}
|
|
"};
|
|
let output = compile_with_and_without_optimization(source);
|
|
insta::assert_snapshot!(output);
|
|
}
|
|
|
|
#[test]
|
|
fn test_select_optimization() {
|
|
let source = indoc! {"
|
|
fn ternary(cond) {
|
|
let result = 0;
|
|
if (cond) {
|
|
result = 10;
|
|
} else {
|
|
result = 20;
|
|
}
|
|
return result;
|
|
}
|
|
"};
|
|
let output = compile_with_and_without_optimization(source);
|
|
insta::assert_snapshot!(output);
|
|
}
|
|
|
|
#[test]
|
|
fn test_leaf_function_no_stack_frame() {
|
|
let source = indoc! {"
|
|
fn increment(x) {
|
|
x = x + 1;
|
|
}
|
|
"};
|
|
let output = compile_with_and_without_optimization(source);
|
|
insta::assert_snapshot!(output);
|
|
}
|
|
|
|
#[test]
|
|
fn test_complex_arithmetic() {
|
|
let source = indoc! {"
|
|
fn compute(a, b, c) {
|
|
let x = a * 2;
|
|
let y = b + 0;
|
|
let z = c * 1;
|
|
return x + y + z;
|
|
}
|
|
"};
|
|
let output = compile_with_and_without_optimization(source);
|
|
insta::assert_snapshot!(output);
|
|
}
|
|
|
|
#[test]
|
|
fn test_nested_function_calls() {
|
|
let source = indoc! {"
|
|
fn add(a, b) { return a + b; }
|
|
fn multiply(x, y) { return x * 2; }
|
|
fn complex(a, b) {
|
|
let sum = add(a, b);
|
|
let doubled = multiply(sum, 2);
|
|
return doubled;
|
|
}
|
|
"};
|
|
let output = compile_with_and_without_optimization(source);
|
|
insta::assert_snapshot!(output);
|
|
}
|
|
|
|
#[test]
|
|
fn test_tuples() {
|
|
let source = indoc! {r#"
|
|
device self = "db";
|
|
device day = "d0";
|
|
|
|
fn getSomethingElse(input) {
|
|
return input + 1;
|
|
}
|
|
|
|
fn getSensorData() {
|
|
return (
|
|
day.Vertical,
|
|
day.Horizontal,
|
|
getSomethingElse(3)
|
|
);
|
|
}
|
|
|
|
loop {
|
|
yield();
|
|
|
|
let (vertical, horizontal, _) = getSensorData();
|
|
|
|
(horizontal, _, _) = getSensorData();
|
|
|
|
self.Setting = horizontal;
|
|
}
|
|
"#};
|
|
|
|
let output = compile_with_and_without_optimization(source);
|
|
insta::assert_snapshot!(output);
|
|
}
|
|
|
|
#[test]
|
|
fn test_larre_script() {
|
|
let source = include_str!("./test_files/test_larre_script.stlg");
|
|
let output = compile_with_and_without_optimization(source);
|
|
insta::assert_snapshot!(output);
|
|
}
|
|
|
|
#[test]
|
|
fn test_reagent_processing() {
|
|
let source = include_str!("./test_files/reagent_processing.stlg");
|
|
let output = compile_with_and_without_optimization(source);
|
|
insta::assert_snapshot!(output);
|
|
}
|
|
}
|