Skip to content

Commit

Permalink
remove test duplication
Browse files Browse the repository at this point in the history
  • Loading branch information
xunilrj committed Jul 13, 2023
1 parent 45ccd44 commit 6af1de7
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 135 deletions.
99 changes: 31 additions & 68 deletions sway-ir/src/optimize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,32 +38,33 @@ mod target_fuel;

#[cfg(test)]
pub mod tests {
use sway_types::SourceEngine;

use crate::{PassGroup, PassManager};
use sway_types::SourceEngine;

// This function parses the IR text representation and run the specified optimizers. After that checks if the IR WAS
// modified and captures all instructions with metadata "!0". These are checked against `expected`.
//
// For example:
//
// ```rust, ignore
// assert_is_optimized(
// &["constcombine"],
// "entry fn main() -> u64 {
// entry():
// l = const u64 1
// r = const u64 2
// result = add l, r, !0
// ret u64 result
// }",
// ["const u64 3"],
// );
// ```
pub(crate) fn assert_is_optimized<'a>(
/// This function parses the IR text representation and run the specified optimizers passes.
/// Then, depending on the `expected` parameter it checks if the IR was optimized or not.
///
/// This comparison is done by capturing all instructions with metadata "!0".
///
/// For example:
///
/// ```rust, ignore
/// assert_optimization(
/// &["constcombine"],
/// "entry fn main() -> u64 {
/// entry():
/// l = const u64 1
/// r = const u64 2
/// result = add l, r, !0
/// ret u64 result
/// }",
/// ["const u64 3"],
/// );
/// ```
pub(crate) fn assert_optimization<'a>(
passes: &[&'static str],
body: &str,
expected: impl IntoIterator<Item = &'a str>,
expected: Option<impl IntoIterator<Item = String>>,
) {
let source_engine = SourceEngine::default();
let mut context = crate::parse(
Expand All @@ -86,8 +87,12 @@ pub mod tests {
group.append_pass(pass);
}

let r = pass_manager.run(&mut context, &group).unwrap();
assert!(r);
let modified = pass_manager.run(&mut context, &group).unwrap();
assert_eq!(expected.is_some(), modified);

let Some(expected) = expected else {
return;
};

let actual = context
.to_string()
Expand All @@ -104,8 +109,9 @@ pub mod tests {
assert!(!actual.is_empty());

let mut expected_matches = actual.len();

for (actual, expected) in actual.iter().zip(expected) {
if !actual.contains(expected) {
if !actual.contains(&expected) {
panic!("error: {actual:?} {expected:?}");
} else {
expected_matches -= 1;
Expand All @@ -114,47 +120,4 @@ pub mod tests {

assert_eq!(expected_matches, 0);
}

// This function parses the IR text representation and run the specified optimizers. After that checks if the IR was
// NOT modified.
//
// For example:
//
// ```rust, ignore
// assert_is_not_optimized(
// &["constcombine"],
// "entry fn main() -> u64 {
// entry():
// l = const u64 0
// r = const u64 1
// result = sub l, r, !0
// ret u64 result
// }"
// );
// ```
pub(crate) fn assert_is_not_optimized(passes: &[&'static str], body: &str) {
let source_engine = SourceEngine::default();
let mut context = crate::parse(
&format!(
"script {{
{body}
}}
!0 = \"a.sw\""
),
&source_engine,
)
.unwrap();

let mut pass_manager = PassManager::default();
crate::register_known_passes(&mut pass_manager);

let mut group = PassGroup::default();
for pass in passes {
group.append_pass(pass);
}

let r = pass_manager.run(&mut context, &group).unwrap();
assert!(!r);
}
}
108 changes: 41 additions & 67 deletions sway-ir/src/optimize/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -284,111 +284,85 @@ fn combine_unary_op(context: &mut Context, function: &Function) -> bool {
mod tests {
use crate::optimize::tests::*;

fn assert_unary_op_is_optimized(opcode: &str, v: &str, result: &str) {
assert_is_optimized(
&["constcombine"],
&format!(
"
entry fn main() -> u64 {{
entry():
v = const u64 {v}
result = {opcode} v, !0
ret u64 result
}}
"
),
[format!("v0 = const u64 {result}").as_str()],
);
}

fn assert_binary_op_is_optimized(opcode: &str, l: &str, r: &str, result: &str) {
assert_is_optimized(
fn assert_operator(opcode: &str, l: &str, r: Option<&str>, result: Option<&str>) {
assert_optimization(
&["constcombine"],
&format!(
"
entry fn main() -> u64 {{
entry():
l = const u64 {l}
r = const u64 {r}
result = {opcode} l, r, !0
{r_inst}
result = {opcode} l, {result_inst} !0
ret u64 result
}}
"
),
[format!("v0 = const u64 {result}").as_str()],
);
}

fn assert_binary_op_is_not_optimized(opcode: &str, l: &str, r: &str) {
assert_is_not_optimized(
&["constcombine"],
&format!(
"
entry fn main() -> u64 {{
entry():
l = const u64 {l}
r = const u64 {r}
result = {opcode} l, r, !0
ret u64 result
}}
"
",
r_inst = match r {
Some(r) => format!("r = const u64 {r}"),
None => format!(""),
},
result_inst = match r.as_ref() {
Some(_) => format!(" r,"),
None => format!(""),
}
),
result.map(|result| [format!("v0 = const u64 {result}")]),
);
}

#[test]
fn unary_op_are_optimized() {
assert_unary_op_is_optimized("not", &u64::MAX.to_string(), "0");
assert_operator("not", &u64::MAX.to_string(), None, Some("0"));
}

#[test]
fn binary_op_are_optimized() {
assert_binary_op_is_optimized("add", "1", "1", "2");
assert_binary_op_is_optimized("sub", "1", "1", "0");
assert_binary_op_is_optimized("mul", "2", "2", "4");
assert_binary_op_is_optimized("div", "10", "5", "2");
assert_binary_op_is_optimized("mod", "12", "5", "2");
assert_binary_op_is_optimized("rsh", "16", "1", "8");
assert_binary_op_is_optimized("lsh", "16", "1", "32");
assert_operator("add", "1", Some("1"), Some("2"));
assert_operator("sub", "1", Some("1"), Some("0"));
assert_operator("mul", "2", Some("2"), Some("4"));
assert_operator("div", "10", Some("5"), Some("2"));
assert_operator("mod", "12", Some("5"), Some("2"));
assert_operator("rsh", "16", Some("1"), Some("8"));
assert_operator("lsh", "16", Some("1"), Some("32"));

assert_binary_op_is_optimized(
assert_operator(
"and",
&0x00FFF.to_string(),
&0xFFF00.to_string(),
&0xF00.to_string(),
Some(&0xFFF00.to_string()),
Some(&0xF00.to_string()),
);
assert_binary_op_is_optimized(
assert_operator(
"or",
&0x00FFF.to_string(),
&0xFFF00.to_string(),
&0xFFFFF.to_string(),
Some(&0xFFF00.to_string()),
Some(&0xFFFFF.to_string()),
);

assert_binary_op_is_optimized(
assert_operator(
"xor",
&0x00FFF.to_string(),
&0xFFF00.to_string(),
&0xFF0FF.to_string(),
Some(&0xFFF00.to_string()),
Some(&0xFF0FF.to_string()),
);
}

#[test]
fn binary_op_are_not_optimized() {
assert_binary_op_is_not_optimized("add", &u64::MAX.to_string(), "1");
assert_binary_op_is_not_optimized("sub", "0", "1");
assert_binary_op_is_not_optimized("mul", &u64::MAX.to_string(), "2");
assert_binary_op_is_not_optimized("div", "1", "0");
assert_operator("add", &u64::MAX.to_string(), Some("1"), None);
assert_operator("sub", "0", Some("1"), None);
assert_operator("mul", &u64::MAX.to_string(), Some("2"), None);
assert_operator("div", "1", Some("0"), None);

assert_binary_op_is_not_optimized("rsh", "1", "64");
assert_binary_op_is_not_optimized("lsh", "1", "64");
assert_operator("rsh", "1", Some("64"), None);
assert_operator("lsh", "1", Some("64"), None);
}

#[test]
fn ok_chain_optimization() {
// Unary operator

// `sub 1` is used to guarantee that the assert string is unique
assert_is_optimized(
assert_optimization(
&["constcombine"],
"
entry fn main() -> u64 {
Expand All @@ -401,11 +375,11 @@ mod tests {
ret u64 result
}
",
["const u64 18446744073709551614"],
Some(["const u64 18446744073709551614".to_string()]),
);

// Binary Operators
assert_is_optimized(
assert_optimization(
&["constcombine"],
"
entry fn main() -> u64 {
Expand All @@ -418,7 +392,7 @@ mod tests {
ret u64 result
}
",
["const u64 6"],
Some(["const u64 6".to_string()]),
);
}
}

0 comments on commit 6af1de7

Please sign in to comment.