Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
43 changes: 13 additions & 30 deletions crates/opy-rs/src/compiler/lowering/rules.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use super::*;
use crate::enums;

mod tail_negation;

impl<'a> Lowering<'a> {
pub(in crate::compiler) fn lower_rules(&mut self) -> Result<(), IntegrationError> {
for entry in &self.hir.rules {
Expand Down Expand Up @@ -456,37 +458,18 @@ impl<'a> Lowering<'a> {
..
} = statement
&& branches.len() == 1
&& let Some(operand) = tail_negation::operand(self, &branches[0].condition)?
{
let branch = &branches[0];
let is_not_condition = matches!(
&*branch.condition,
Expr::Unary { op, .. } if op == "not"
);
let is_comparison = matches!(
&*branch.condition,
Expr::Binary { op, .. }
if matches!(op.as_str(), "==" | "!=" | "<" | "<=" | ">" | ">=")
);
if (is_not_condition || is_comparison) && !branch.body.is_empty() {
let body = self.lower_actions(&branch.body, break_target)?;
if !body.is_empty() && (is_not_condition || branch.body.len() == 1) {
let condition = if is_not_condition {
let Expr::Unary { operand, .. } = &*branch.condition else {
unreachable!()
};
self.lower_value(operand)?
} else {
let condition = self.lower_value(&branch.condition)?;
self.push_call("not", vec![condition])
};
let distance = self.canonical_action_width(&body, *span)?;
let distance = self.push_number(distance as f64);
let skip = self.push_call_action("skipIf", &[condition, distance]);
self.mark_action_origins(std::slice::from_ref(&skip), *span);
actions.push(skip);
actions.extend(body);
continue;
}
let body = self.lower_actions(&branches[0].body, break_target)?;
if !body.is_empty() {
let condition = self.lower_value(operand)?;
let distance = self.canonical_action_width(&body, *span)?;
let distance = self.push_number(distance as f64);
let skip = self.push_call_action("skipIf", &[condition, distance]);
self.mark_action_origins(std::slice::from_ref(&skip), *span);
actions.push(skip);
actions.extend(body);
continue;
}
}
if self.lower_conditional_label_jump(statement, &mut actions)? {
Expand Down
19 changes: 19 additions & 0 deletions crates/opy-rs/src/compiler/lowering/rules/tail_negation.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
use super::*;

pub(super) fn operand<'expr>(
lowering: &mut Lowering<'_>,
condition: &'expr Expr,
) -> Result<Option<&'expr Expr>, IntegrationError> {
let Expr::Unary { op, operand, .. } = condition else {
return Ok(None);
};
if op != "not" {
return Ok(None);
}
let condition = lowering.lower_value(condition)?;
Ok(matches!(
lowering.materialize_value(condition),
workshop_rs::Value::Call { name, .. } if name == "not"
)
.then_some(operand.as_ref()))
}
29 changes: 2 additions & 27 deletions crates/opy-rs/src/compiler/tests/control_flow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,33 +119,8 @@ fn control_flow_debug_lowers_to_a_native_hud_action() {
}

#[test]
fn aggressive_size_optimization_lowers_a_tail_comparison_to_skip_if() {
let source = "#!optimizeForSize\n#!optimizeForSizeAggressive\nglobalvar g\nrule \"r\":\n @Event global\n if g == 1:\n g = 2\n";
let hir = crate::compile(source, "source.opy", Path::new(".")).unwrap();
let artifact = Compiler::new().unwrap().compile_hir(&hir).unwrap();

assert!(
artifact
.emitted
.contains("Skip If(Not(Compare(Global.g, ==, 1)), True);")
);
assert!(!artifact.emitted.contains("If(Compare(Global.g, ==, 1));"));
}

#[test]
fn aggressive_size_optimization_respects_directive_boundaries() {
let source = "globalvar g\nrule \"before\":\n @Event global\n if g == 1:\n g = 2\n#!optimizeForSize\n#!optimizeForSizeAggressive\nrule \"after\":\n @Event global\n if g == 1:\n g = 2\n#!disableOptimizations\nrule \"disabled\":\n @Event global\n if g == 1:\n g = 2\n";
let hir = crate::compile(source, "source.opy", Path::new(".")).unwrap();
let artifact = Compiler::new().unwrap().compile_hir(&hir).unwrap();

assert_eq!(artifact.emitted.matches("Skip If(").count(), 1);
assert_eq!(
artifact
.emitted
.matches("If(Compare(Global.g, ==, 1));")
.count(),
2
);
fn aggressive_size_control_flow_matches_the_pinned_oracle() {
assert_native_wir_equivalent("structural-size-aggressive-control-flow");
}

#[test]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"schemaVersion": 1,
"id": "synthetic/structural-size-aggressive-control-flow",
"category": "compilation",
"features": [
"structural convergence",
"control flow",
"optimizeForSize",
"optimizeForSizeAggressive"
],
"source": "source.opy",
"tests": {
"source": {
"nativeStatus": "success",
"relationship": "match",
"ruleNames": true
},
"compiler": {
"nativeStatus": "success",
"relationship": "match",
"comparison": "semantic-wir",
"semanticEquivalent": true
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"compile": {
"diagnostics": [],
"exitCode": 0,
"status": "success",
"stdout": "",
"workshop": "variables {\n global:\n 0: g\n}\n\nrule (\"default comparison\") {\n event {\n Ongoing - Global;\n }\n actions {\n If(Compare(Global.g, ==, 1));\n Set Global Variable(g, 2);\n }\n}\n\nrule (\"aggressive alone comparison\") {\n event {\n Ongoing - Global;\n }\n actions {\n If(Compare(Global.g, ==, 1));\n Set Global Variable(g, 2);\n }\n}\n\n//Optimize for size enabled\nrule (\"tail comparison\") {\n event {\n Ongoing - Global;\n }\n actions {\n If(Compare(Global.g, ==, 1));\n Set Global Variable(g, 2);\n }\n}\n\nrule (\"tail negation\") {\n event {\n Ongoing - Global;\n }\n actions {\n Skip If(Global.g, True);\n Set Global Variable(g, 2);\n //__label_if_2__:\n }\n}\n\nrule (\"tail negated equality\") {\n event {\n Ongoing - Global;\n }\n actions {\n If(Compare(Global.g, !=, 1));\n Set Global Variable(g, 2);\n }\n}\n\nrule (\"tail negated inequality\") {\n event {\n Ongoing - Global;\n }\n actions {\n If(Compare(Global.g, ==, 1));\n Set Global Variable(g, 2);\n }\n}\n\nrule (\"tail negated less than\") {\n event {\n Ongoing - Global;\n }\n actions {\n If(Compare(Global.g, >=, 1));\n Set Global Variable(g, 2);\n }\n}\n\nrule (\"tail negated less than or equal\") {\n event {\n Ongoing - Global;\n }\n actions {\n If(Compare(Global.g, >, 1));\n Set Global Variable(g, 2);\n }\n}\n\nrule (\"tail negated greater than\") {\n event {\n Ongoing - Global;\n }\n actions {\n If(Compare(Global.g, <=, 1));\n Set Global Variable(g, 2);\n }\n}\n\nrule (\"tail negated greater than or equal\") {\n event {\n Ongoing - Global;\n }\n actions {\n If(Compare(Global.g, <, 1));\n Set Global Variable(g, 2);\n }\n}\n\nrule (\"tail double negated equality\") {\n event {\n Ongoing - Global;\n }\n actions {\n If(Compare(Global.g, ==, 1));\n Set Global Variable(g, 2);\n }\n}\n\nrule (\"tail double negated inequality\") {\n event {\n Ongoing - Global;\n }\n actions {\n If(Compare(Global.g, !=, 1));\n Set Global Variable(g, 2);\n }\n}\n\nrule (\"tail double negated less than\") {\n event {\n Ongoing - Global;\n }\n actions {\n If(Compare(Global.g, <, 1));\n Set Global Variable(g, 2);\n }\n}\n\nrule (\"tail double negated less than or equal\") {\n event {\n Ongoing - Global;\n }\n actions {\n If(Compare(Global.g, <=, 1));\n Set Global Variable(g, 2);\n }\n}\n\nrule (\"tail double negated greater than\") {\n event {\n Ongoing - Global;\n }\n actions {\n If(Compare(Global.g, >, 1));\n Set Global Variable(g, 2);\n }\n}\n\nrule (\"tail double negated greater than or equal\") {\n event {\n Ongoing - Global;\n }\n actions {\n If(Compare(Global.g, >=, 1));\n Set Global Variable(g, 2);\n }\n}\n",
"workshopExact": "variables {\n global:\n 0: g\n}\n\nrule (\"default comparison\") {\n event {\n Ongoing - Global;\n }\n actions {\n If(Compare(Global.g, ==, 1));\n Set Global Variable(g, 2);\n }\n}\n\nrule (\"aggressive alone comparison\") {\n event {\n Ongoing - Global;\n }\n actions {\n If(Compare(Global.g, ==, 1));\n Set Global Variable(g, 2);\n }\n}\n\n//Optimize for size enabled\nrule (\"tail comparison\") {\n event {\n Ongoing - Global;\n }\n actions {\n If(Compare(Global.g, ==, 1));\n Set Global Variable(g, 2);\n }\n}\n\nrule (\"tail negation\") {\n event {\n Ongoing - Global;\n }\n actions {\n Skip If(Global.g, True);\n Set Global Variable(g, 2);\n //__label_if_2__:\n }\n}\n\nrule (\"tail negated equality\") {\n event {\n Ongoing - Global;\n }\n actions {\n If(Compare(Global.g, !=, 1));\n Set Global Variable(g, 2);\n }\n}\n\nrule (\"tail negated inequality\") {\n event {\n Ongoing - Global;\n }\n actions {\n If(Compare(Global.g, ==, 1));\n Set Global Variable(g, 2);\n }\n}\n\nrule (\"tail negated less than\") {\n event {\n Ongoing - Global;\n }\n actions {\n If(Compare(Global.g, >=, 1));\n Set Global Variable(g, 2);\n }\n}\n\nrule (\"tail negated less than or equal\") {\n event {\n Ongoing - Global;\n }\n actions {\n If(Compare(Global.g, >, 1));\n Set Global Variable(g, 2);\n }\n}\n\nrule (\"tail negated greater than\") {\n event {\n Ongoing - Global;\n }\n actions {\n If(Compare(Global.g, <=, 1));\n Set Global Variable(g, 2);\n }\n}\n\nrule (\"tail negated greater than or equal\") {\n event {\n Ongoing - Global;\n }\n actions {\n If(Compare(Global.g, <, 1));\n Set Global Variable(g, 2);\n }\n}\n\nrule (\"tail double negated equality\") {\n event {\n Ongoing - Global;\n }\n actions {\n If(Compare(Global.g, ==, 1));\n Set Global Variable(g, 2);\n }\n}\n\nrule (\"tail double negated inequality\") {\n event {\n Ongoing - Global;\n }\n actions {\n If(Compare(Global.g, !=, 1));\n Set Global Variable(g, 2);\n }\n}\n\nrule (\"tail double negated less than\") {\n event {\n Ongoing - Global;\n }\n actions {\n If(Compare(Global.g, <, 1));\n Set Global Variable(g, 2);\n }\n}\n\nrule (\"tail double negated less than or equal\") {\n event {\n Ongoing - Global;\n }\n actions {\n If(Compare(Global.g, <=, 1));\n Set Global Variable(g, 2);\n }\n}\n\nrule (\"tail double negated greater than\") {\n event {\n Ongoing - Global;\n }\n actions {\n If(Compare(Global.g, >, 1));\n Set Global Variable(g, 2);\n }\n}\n\nrule (\"tail double negated greater than or equal\") {\n event {\n Ongoing - Global;\n }\n actions {\n If(Compare(Global.g, >=, 1));\n Set Global Variable(g, 2);\n }\n}\n\n",
"workshopSha256": "31be06ac043a30f8ac3b00ac5df598ca7477c89ff06453d880727d2dec45bc56"
},
"fixture": "synthetic/structural-size-aggressive-control-flow",
"input": {
"files": [
{
"path": "source.opy",
"sha256": "df747c12bdc3e40e4536ada503116ee01db93126c374fb4a13f522e70ac4a638"
}
],
"sha256": "bdac5cd906b8da4a24f36ec794f7bdd0dd279a4a967fc410a806185b3c18efd6",
"source": "source.opy"
},
"oracle": {
"contentCommit": "5a7d0e294b8cad73b9701987bb584d0551d7fa4d",
"gitHead": "ec763c1d245aa5485ef61af5b2bda4975bce4877",
"integrity": "sha512-C5F6btT5yn19BvHuE0bjMuSd/2esebBabmpL4SYLQFuJ4IdouVOKEoMLLuJFAyNc0Phwrolblj5GDkDGYR0ofg==",
"language": "en-US",
"license": "GPL-3.0-only",
"name": "overpy",
"registryTarball": "https://registry.npmjs.org/overpy/-/overpy-9.7.17.tgz",
"repository": "https://github.com/Zezombye/overpy",
"version": "9.7.17"
},
"schemaVersion": 1
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
globalvar g

rule "default comparison":
@Event global
if g == 1:
g = 2

#!optimizeForSizeAggressive

rule "aggressive alone comparison":
@Event global
if g == 1:
g = 2

#!optimizeForSize

rule "tail comparison":
@Event global
if g == 1:
g = 2

rule "tail negation":
@Event global
if not g:
g = 2

rule "tail negated equality":
@Event global
if not (g == 1):
g = 2

rule "tail negated inequality":
@Event global
if not (g != 1):
g = 2

rule "tail negated less than":
@Event global
if not (g < 1):
g = 2

rule "tail negated less than or equal":
@Event global
if not (g <= 1):
g = 2

rule "tail negated greater than":
@Event global
if not (g > 1):
g = 2

rule "tail negated greater than or equal":
@Event global
if not (g >= 1):
g = 2

rule "tail double negated equality":
@Event global
if not not (g == 1):
g = 2

rule "tail double negated inequality":
@Event global
if not not (g != 1):
g = 2

rule "tail double negated less than":
@Event global
if not not (g < 1):
g = 2

rule "tail double negated less than or equal":
@Event global
if not not (g <= 1):
g = 2

rule "tail double negated greater than":
@Event global
if not not (g > 1):
g = 2

rule "tail double negated greater than or equal":
@Event global
if not not (g >= 1):
g = 2
Loading