diff --git a/crates/opy-rs/src/compiler/lowering/action_calls.rs b/crates/opy-rs/src/compiler/lowering/action_calls.rs index 93d00bd3..f0bf7fa7 100644 --- a/crates/opy-rs/src/compiler/lowering/action_calls.rs +++ b/crates/opy-rs/src/compiler/lowering/action_calls.rs @@ -1,6 +1,7 @@ use super::*; use super::super::blizzard_global; +use super::super::size_optimization::{Slot, slot}; fn is_cased_color_tag(text: &[char], index: usize) -> Option { let remaining = text[index..].iter().collect::(); @@ -608,7 +609,16 @@ impl<'a> Lowering<'a> { // Team.1) but never the argument-only ones (0, the empty string). let operands = rule_condition_root && matches!(call_id, "==" | "!=" | "<" | "<=" | ">" | ">="); - for value in args { + for (index, value) in args.iter_mut().enumerate() { + if matches!(self.value(*value), Value::Number(0.0)) + && (matches!(call_id, "==" | "!=" | "<" | "<=" | ">" | ">=") + || matches!( + slot(call_id, index), + Some(Slot::Boolean | Slot::FalseOnly | Slot::ZeroAsNull) + )) + { + continue; + } *value = self.apply_replacement(*value, call_id, span, operands); } } diff --git a/crates/opy-rs/src/compiler/size_optimization.rs b/crates/opy-rs/src/compiler/size_optimization.rs index 8fcd74e3..fb7178cc 100644 --- a/crates/opy-rs/src/compiler/size_optimization.rs +++ b/crates/opy-rs/src/compiler/size_optimization.rs @@ -1,7 +1,7 @@ use workshop_rs::catalog::{Kind, ParamCoercions}; use workshop_rs::{Action, ModifyOp, Value}; -use self::literal_slots::{Slot, slot}; +pub(super) use self::literal_slots::{Slot, slot}; use super::Compiler; use super::lowering::{EMPTY_STRING_NAME, Lowering}; use super::operator_optimization::{falsy, literal_text, number_components}; @@ -94,6 +94,19 @@ impl<'a> SizeOptimizer<'a> { } fn modified(&self, op: ModifyOp, value: &mut Value) { + if matches!(op, ModifyOp::Max | ModifyOp::Min) && matches!(value, Value::Number(0.0)) { + let replacement = [ + "getCapturePercentage", + "getPayloadProgressPercentage", + "isMatchComplete", + ] + .into_iter() + .find(|name| self.replacement(name)); + if let Some(name) = replacement { + *value = Value::call(name, []); + return; + } + } if is_empty_string(value) { *value = self.empty_string(false); return; diff --git a/crates/opy-rs/src/compiler/size_optimization/literal_slots.rs b/crates/opy-rs/src/compiler/size_optimization/literal_slots.rs index b8487ffb..5ce2875c 100644 --- a/crates/opy-rs/src/compiler/size_optimization/literal_slots.rs +++ b/crates/opy-rs/src/compiler/size_optimization/literal_slots.rs @@ -1,5 +1,5 @@ #[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub(super) enum Slot { +pub(in crate::compiler) enum Slot { Boolean, TrueOnly, FalseOnly, @@ -7,7 +7,7 @@ pub(super) enum Slot { ZeroVectorAsNull, } -pub(super) fn slot(name: &str, index: usize) -> Option { +pub(in crate::compiler) fn slot(name: &str, index: usize) -> Option { Some(match (name, index) { ("startForcingSpawn", 1) => Slot::FalseOnly, ("add", 0 | 1) | ("divide", 0) | ("modulo", 0 | 1) | ("subtract", 1) => Slot::TrueOnly, diff --git a/crates/opy-rs/src/compiler/tests/structural_convergence.rs b/crates/opy-rs/src/compiler/tests/structural_convergence.rs index 8fb3b038..fe18a7f9 100644 --- a/crates/opy-rs/src/compiler/tests/structural_convergence.rs +++ b/crates/opy-rs/src/compiler/tests/structural_convergence.rs @@ -179,6 +179,11 @@ fn size_optimization_action_values_match_the_pinned_oracle() { assert_converges("structural-size-action-values"); } +#[test] +fn literal_slots_take_precedence_over_the_capture_replacement() { + assert_converges("structural-size-capture-slots"); +} + #[test] fn condition_operands_skip_the_argument_replacements() { assert_converges("structural-size-condition-replacements"); diff --git a/crates/opy-rs/tests/fixtures/corpus/synthetic/structural-size-capture-slots/fixture.json b/crates/opy-rs/tests/fixtures/corpus/synthetic/structural-size-capture-slots/fixture.json new file mode 100644 index 00000000..2118d0aa --- /dev/null +++ b/crates/opy-rs/tests/fixtures/corpus/synthetic/structural-size-capture-slots/fixture.json @@ -0,0 +1,23 @@ +{ + "schemaVersion": 1, + "id": "synthetic/structural-size-capture-slots", + "category": "compiler", + "features": [ + "optimizeForSize", + "preprocessing/replacements" + ], + "source": "source.opy", + "tests": { + "source": { + "nativeStatus": "success", + "relationship": "match", + "ruleNames": true + }, + "compiler": { + "nativeStatus": "success", + "relationship": "match", + "comparison": "semantic-wir", + "semanticEquivalent": true + } + } +} diff --git a/crates/opy-rs/tests/fixtures/corpus/synthetic/structural-size-capture-slots/oracle.json b/crates/opy-rs/tests/fixtures/corpus/synthetic/structural-size-capture-slots/oracle.json new file mode 100644 index 00000000..8463bc8c --- /dev/null +++ b/crates/opy-rs/tests/fixtures/corpus/synthetic/structural-size-capture-slots/oracle.json @@ -0,0 +1,34 @@ +{ + "compile": { + "diagnostics": [], + "exitCode": 0, + "status": "success", + "stdout": "", + "workshop": "variables {\n global:\n 0: arr\n 1: result\n}\n\nrule (\"capture directive without size optimization\") {\n event {\n Ongoing - Global;\n }\n actions {\n Set Global Variable(result, Index Of Array Value(Global.arr, 0));\n Modify Global Variable(result, Max, 0);\n Modify Global Variable(result, Min, 0);\n Set Global Variable(result, Compare(Global.result, ==, 0));\n Set Global Variable(result, Index Of Array Value(Global.arr, Compare(Global.result, ==, 0)));\n }\n}\n\n//Optimize for size enabled\nrule (\"literal slots precede capture replacement\") {\n event {\n Ongoing - Global;\n }\n actions {\n Set Global Variable(result, Index Of Array Value(Global.arr, Null));\n Set Global Variable(result, Index Of Array Value(Global.arr, 1));\n Set Global Variable(result, Array Contains(Global.arr, Null));\n Set Global Variable(result, Index Of Array Value(Global.arr, Index Of Array Value(Global.arr, Null)));\n Wait(False, Ignore Condition);\n Create HUD Text(All Players(All Teams), Point Capture Percentage, Custom String(\"\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003{0}\", Custom String(\"\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\")), Null, Left, -9999, Color(Orange), Null, Null, Visible To and String, Default Visibility);\n Modify Global Variable(result, Max, Point Capture Percentage);\n Modify Global Variable(result, Min, Point Capture Percentage);\n Set Global Variable(result, Max(Global.arr, False));\n Set Global Variable(result, Min(Global.arr, False));\n Set Global Variable(result, Compare(Global.result, ==, Null));\n Set Global Variable(result, Index Of Array Value(Global.arr, Compare(Global.result, ==, Null)));\n }\n}\n\nrule (\"capture comparison condition\") {\n event {\n Ongoing - Global;\n }\n conditions {\n Global.result == Null;\n }\n actions {\n Set Global Variable(result, 1);\n }\n}\n\n//Optimizations disabled\nrule (\"capture directive with optimizations disabled\") {\n event {\n Ongoing - Global;\n }\n actions {\n Set Global Variable(result, Max(Global.result, 0));\n Set Global Variable(result, Min(Global.result, 0));\n Set Global Variable(result, Compare(Global.result, ==, 0));\n Set Global Variable(result, Index Of Array Value(Global.arr, Compare(Global.result, ==, 0)));\n }\n}\n", + "workshopExact": "variables {\n global:\n 0: arr\n 1: result\n}\n\nrule (\"capture directive without size optimization\") {\n event {\n Ongoing - Global;\n }\n actions {\n Set Global Variable(result, Index Of Array Value(Global.arr, 0));\n Modify Global Variable(result, Max, 0);\n Modify Global Variable(result, Min, 0);\n Set Global Variable(result, Compare(Global.result, ==, 0));\n Set Global Variable(result, Index Of Array Value(Global.arr, Compare(Global.result, ==, 0)));\n }\n}\n\n//Optimize for size enabled\nrule (\"literal slots precede capture replacement\") {\n event {\n Ongoing - Global;\n }\n actions {\n Set Global Variable(result, Index Of Array Value(Global.arr, Null));\n Set Global Variable(result, Index Of Array Value(Global.arr, 1));\n Set Global Variable(result, Array Contains(Global.arr, Null));\n Set Global Variable(result, Index Of Array Value(Global.arr, Index Of Array Value(Global.arr, Null)));\n Wait(False, Ignore Condition);\n Create HUD Text(All Players(All Teams), Point Capture Percentage, Custom String(\"\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003{0}\", Custom String(\"\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\u2003\")), Null, Left, -9999, Color(Orange), Null, Null, Visible To and String, Default Visibility);\n Modify Global Variable(result, Max, Point Capture Percentage);\n Modify Global Variable(result, Min, Point Capture Percentage);\n Set Global Variable(result, Max(Global.arr, False));\n Set Global Variable(result, Min(Global.arr, False));\n Set Global Variable(result, Compare(Global.result, ==, Null));\n Set Global Variable(result, Index Of Array Value(Global.arr, Compare(Global.result, ==, Null)));\n }\n}\n\nrule (\"capture comparison condition\") {\n event {\n Ongoing - Global;\n }\n conditions {\n Global.result == Null;\n }\n actions {\n Set Global Variable(result, 1);\n }\n}\n\n//Optimizations disabled\nrule (\"capture directive with optimizations disabled\") {\n event {\n Ongoing - Global;\n }\n actions {\n Set Global Variable(result, Max(Global.result, 0));\n Set Global Variable(result, Min(Global.result, 0));\n Set Global Variable(result, Compare(Global.result, ==, 0));\n Set Global Variable(result, Index Of Array Value(Global.arr, Compare(Global.result, ==, 0)));\n }\n}\n\n", + "workshopSha256": "e4a7ed467e5831ca205d30cac2d2e2227fd36b8c119fa6cd938338b829eea815" + }, + "fixture": "synthetic/structural-size-capture-slots", + "input": { + "files": [ + { + "path": "source.opy", + "sha256": "b87390e38852bf1268adb1ba414a988046810705d69ddd8e6b2a62b710d0e1b2" + } + ], + "sha256": "3a9dcb53aa184fe9d0ec387e6b32dcd237a638a3acfb75e54ca28020c5ff6659", + "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 +} diff --git a/crates/opy-rs/tests/fixtures/corpus/synthetic/structural-size-capture-slots/source.opy b/crates/opy-rs/tests/fixtures/corpus/synthetic/structural-size-capture-slots/source.opy new file mode 100644 index 00000000..e182ef72 --- /dev/null +++ b/crates/opy-rs/tests/fixtures/corpus/synthetic/structural-size-capture-slots/source.opy @@ -0,0 +1,40 @@ +#!replace0ByCapturePercentage +globalvar arr +globalvar result + +rule "capture directive without size optimization": + @Event global + result = arr.index(0) + result = max(result, 0) + result = min(result, 0) + result = (result == 0) + result = arr.index(result == 0) + +#!optimizeForSize +rule "literal slots precede capture replacement": + @Event global + result = arr.index(0) + result = arr.index(1) + result = 0 in arr + result = arr.index(arr.index(0)) + wait(0) + print(0) + result = max(result, 0) + result = min(result, 0) + result = max(arr, 0) + result = min(arr, 0) + result = (result == 0) + result = arr.index(result == 0) + +rule "capture comparison condition": + @Event global + @Condition result == 0 + result = 1 + +#!disableOptimizations +rule "capture directive with optimizations disabled": + @Event global + result = max(result, 0) + result = min(result, 0) + result = (result == 0) + result = arr.index(result == 0)