diff --git a/optimizer/src/main/java/dev/cel/optimizer/optimizers/BUILD.bazel b/optimizer/src/main/java/dev/cel/optimizer/optimizers/BUILD.bazel index 02fc857df..50984fe37 100644 --- a/optimizer/src/main/java/dev/cel/optimizer/optimizers/BUILD.bazel +++ b/optimizer/src/main/java/dev/cel/optimizer/optimizers/BUILD.bazel @@ -60,11 +60,13 @@ java_library( ":default_optimizer_constants", "//:auto_value", "//bundle:cel", + "//checker:standard_decl", "//common:cel_ast", "//common:cel_source", "//common:compiler_common", "//common:mutable_ast", "//common:mutable_source", + "//common:operator", "//common/ast", "//common/ast:cel_block", "//common/ast:mutable_expr", diff --git a/optimizer/src/main/java/dev/cel/optimizer/optimizers/SubexpressionOptimizer.java b/optimizer/src/main/java/dev/cel/optimizer/optimizers/SubexpressionOptimizer.java index 74794cda5..8756908a5 100644 --- a/optimizer/src/main/java/dev/cel/optimizer/optimizers/SubexpressionOptimizer.java +++ b/optimizer/src/main/java/dev/cel/optimizer/optimizers/SubexpressionOptimizer.java @@ -28,6 +28,7 @@ import com.google.common.base.Verify; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableListMultimap; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Multimaps; import com.google.common.collect.Sets; @@ -35,6 +36,10 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue; import dev.cel.bundle.Cel; import dev.cel.bundle.CelBuilder; +import dev.cel.checker.CelStandardDeclarations.StandardFunction.Overload.Arithmetic; +import dev.cel.checker.CelStandardDeclarations.StandardFunction.Overload.BooleanOperator; +import dev.cel.checker.CelStandardDeclarations.StandardFunction.Overload.Comparison; +import dev.cel.checker.CelStandardDeclarations.StandardFunction.Overload.Relation; import dev.cel.common.CelAbstractSyntaxTree; import dev.cel.common.CelMutableAst; import dev.cel.common.CelMutableSource; @@ -44,6 +49,7 @@ import dev.cel.common.CelSource.Extension.Version; import dev.cel.common.CelValidationException; import dev.cel.common.CelVarDecl; +import dev.cel.common.Operator; import dev.cel.common.ast.CelBlock; import dev.cel.common.ast.CelExpr; import dev.cel.common.ast.CelExpr.CelCall; @@ -51,8 +57,15 @@ import dev.cel.common.ast.CelExpr.CelList; import dev.cel.common.ast.CelExpr.ExprKind.Kind; import dev.cel.common.ast.CelMutableExpr; +import dev.cel.common.ast.CelMutableExpr.CelMutableCall; +import dev.cel.common.ast.CelMutableExpr.CelMutableComprehension; import dev.cel.common.ast.CelMutableExpr.CelMutableIdent; +import dev.cel.common.ast.CelMutableExpr.CelMutableList; +import dev.cel.common.ast.CelMutableExpr.CelMutableMap; +import dev.cel.common.ast.CelMutableExpr.CelMutableSelect; +import dev.cel.common.ast.CelMutableExpr.CelMutableStruct; import dev.cel.common.ast.CelMutableExprConverter; +import dev.cel.common.ast.CelReference; import dev.cel.common.navigation.CelNavigableExpr; import dev.cel.common.navigation.CelNavigableExprUtil; import dev.cel.common.navigation.CelNavigableMutableAst; @@ -65,8 +78,10 @@ import dev.cel.optimizer.CelAstOptimizer; import java.util.ArrayList; import java.util.Arrays; +import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; +import java.util.IdentityHashMap; import java.util.List; import java.util.Map; import java.util.Set; @@ -173,7 +188,9 @@ private OptimizationResult optimizeUsingCelBlock(CelAbstractSyntaxTree ast, Cel for (iterCount = 0; iterCount < cseOptions.iterationLimit(); iterCount++) { CelNavigableMutableAst navAst = CelNavigableMutableAst.fromAst(astToModify); - ImmutableList cseCandidates = getCseCandidates(navAst); + ValueNumberer valueNumberer = + new ValueNumberer(navAst.getRoot().expr(), ast.getReferenceMap()); + ImmutableList cseCandidates = getCseCandidates(navAst, valueNumberer); if (cseCandidates.isEmpty()) { break; } @@ -390,25 +407,25 @@ private static void replaceWithBlockIdent( expr.setIdent(CelMutableIdent.create(blockIdentifier)); } - private ImmutableList getCseCandidates(CelNavigableMutableAst navAst) { + private ImmutableList getCseCandidates( + CelNavigableMutableAst navAst, ValueNumberer valueNumberer) { Set ineligibleExprs = getIneligibleExprsFromComprehensionBranches(navAst); ImmutableList allNodes = navAst.getRoot().allNodes(TraversalOrder.PRE_ORDER).collect(toImmutableList()); ImmutableListMultimap nodesByHeight = Multimaps.index(allNodes, CelNavigableMutableExpr::height); - HashSet semanticallyEqualNodes = new HashSet<>(); + Set seenVns = new HashSet<>(); for (CelNavigableMutableExpr node : allNodes) { ImmutableList sameHeightNodes = nodesByHeight.get(node.height()); if (sameHeightNodes.size() < 2 || !canEliminate(node, ineligibleExprs)) { continue; } - // Normalize the expr to test semantic equivalence. - CelMutableExpr normalizedExpr = normalizeForEquality(node.expr()); - if (!semanticallyEqualNodes.add(normalizedExpr)) { + int vn = valueNumberer.vnOf(node.expr()); + if (!seenVns.add(vn)) { return sameHeightNodes.stream() .filter(candidate -> canEliminate(candidate, ineligibleExprs)) .map(CelNavigableMutableExpr::expr) - .filter(expr -> normalizeForEquality(expr).equals(normalizedExpr)) + .filter(expr -> valueNumberer.vnOf(expr) == vn) .collect(toImmutableList()); } } @@ -488,17 +505,146 @@ private boolean containsEliminableFunctionOnly(CelNavigableMutableExpr navigable }); } - /** - * Converts the {@link CelMutableExpr} to make it suitable for performing a semantically equals - * check. - * - *

Specifically, this will deep copy the mutable expr then set all expr IDs in the expression - * tree to 0. - */ - private CelMutableExpr normalizeForEquality(CelMutableExpr mutableExpr) { - CelMutableExpr copiedExpr = CelMutableExpr.newInstance(mutableExpr); + // Assigns equal value numbers to equivalent subexpressions, including commuted and converse calls + // with only standard overloads. If both operands fail, either error may be reported, which CEL's + // unspecified evaluation order permits. + private static final class ValueNumberer { + private static final ImmutableMap SWAPPED_FUNCTIONS = + ImmutableMap.builder() + .put(Operator.ADD.getFunction(), Operator.ADD.getFunction()) + .put(Operator.MULTIPLY.getFunction(), Operator.MULTIPLY.getFunction()) + .put(Operator.EQUALS.getFunction(), Operator.EQUALS.getFunction()) + .put(Operator.NOT_EQUALS.getFunction(), Operator.NOT_EQUALS.getFunction()) + .put(Operator.LOGICAL_AND.getFunction(), Operator.LOGICAL_AND.getFunction()) + .put(Operator.LOGICAL_OR.getFunction(), Operator.LOGICAL_OR.getFunction()) + .put(Operator.GREATER.getFunction(), Operator.LESS.getFunction()) + .put(Operator.GREATER_EQUALS.getFunction(), Operator.LESS_EQUALS.getFunction()) + .buildOrThrow(); + private static final ImmutableSet SWAPPABLE_OVERLOAD_IDS = + Stream.concat( + Stream.of( + Arithmetic.ADD_INT64, + Arithmetic.ADD_UINT64, + Arithmetic.ADD_DOUBLE, + Arithmetic.ADD_DURATION_DURATION, + Arithmetic.ADD_TIMESTAMP_DURATION, + Arithmetic.ADD_DURATION_TIMESTAMP, + Arithmetic.MULTIPLY_INT64, + Arithmetic.MULTIPLY_UINT64, + Arithmetic.MULTIPLY_DOUBLE, + Relation.EQUALS, + Relation.NOT_EQUALS, + BooleanOperator.LOGICAL_AND, + BooleanOperator.LOGICAL_OR), + EnumSet.allOf(Comparison.class).stream()) + .map(overload -> overload.celOverloadDecl().overloadId()) + .collect(toImmutableSet()); + + private final ImmutableMap references; + private final Map, Integer> keyToVn; + private final IdentityHashMap exprToVn; + + private int vnOf(CelMutableExpr expr) { + return Verify.verifyNotNull(exprToVn.get(expr)); + } + + private int computeVn(CelMutableExpr expr) { + ImmutableList key = buildKey(expr); + int vn = keyToVn.computeIfAbsent(key, k -> keyToVn.size()); + exprToVn.put(expr, vn); + return vn; + } - return astMutator.clearExprIds(copiedExpr); + private ImmutableList computeVns(List exprs) { + ImmutableList.Builder vns = ImmutableList.builderWithExpectedSize(exprs.size()); + for (CelMutableExpr expr : exprs) { + vns.add(computeVn(expr)); + } + return vns.build(); + } + + private ImmutableList buildKey(CelMutableExpr expr) { + switch (expr.getKind()) { + case CONSTANT: + return ImmutableList.of(Kind.CONSTANT, expr.constant()); + case IDENT: + // Runs after identifiers are mangled. + return ImmutableList.of(Kind.IDENT, expr.ident().name()); + case SELECT: + CelMutableSelect select = expr.select(); + return ImmutableList.of( + Kind.SELECT, computeVn(select.operand()), select.field(), select.testOnly()); + case CALL: + CelMutableCall call = expr.call(); + String function = call.function(); + // VNs are non-negative, so -1 marks a global call. + int targetVn = call.target().map(this::computeVn).orElse(-1); + ImmutableList argVns = computeVns(call.args()); + // Keyed even when no swap applies (e.g. _<_), so a call that also resolves to custom + // overloads (x < 5 on dyn) never merges with a canonicalized standard-only one (5 > x). + boolean swappable = hasOnlySwappableOverloads(expr.id()); + String swapped = SWAPPED_FUNCTIONS.get(function); + if (swappable + && swapped != null + && (!swapped.equals(function) || argVns.get(0) > argVns.get(1))) { + function = swapped; + argVns = argVns.reverse(); + } + return ImmutableList.of(Kind.CALL, targetVn, function, argVns, swappable); + case LIST: + CelMutableList list = expr.list(); + return ImmutableList.of( + Kind.LIST, computeVns(list.elements()), ImmutableList.copyOf(list.optionalIndices())); + case MAP: + CelMutableMap map = expr.map(); + ImmutableList.Builder entryVns = + ImmutableList.builderWithExpectedSize(map.entries().size()); + for (CelMutableMap.Entry entry : map.entries()) { + entryVns.add( + ImmutableList.of( + computeVn(entry.key()), computeVn(entry.value()), entry.optionalEntry())); + } + return ImmutableList.of(Kind.MAP, entryVns.build()); + case STRUCT: + CelMutableStruct struct = expr.struct(); + ImmutableList.Builder fieldVns = + ImmutableList.builderWithExpectedSize(struct.entries().size()); + for (CelMutableStruct.Entry entry : struct.entries()) { + fieldVns.add( + ImmutableList.of( + entry.fieldKey(), computeVn(entry.value()), entry.optionalEntry())); + } + return ImmutableList.of(Kind.STRUCT, struct.messageName(), fieldVns.build()); + case COMPREHENSION: + CelMutableComprehension comp = expr.comprehension(); + return ImmutableList.of( + Kind.COMPREHENSION, + comp.iterVar(), + comp.iterVar2(), + comp.accuVar(), + computeVn(comp.iterRange()), + computeVn(comp.accuInit()), + computeVn(comp.loopCondition()), + computeVn(comp.loopStep()), + computeVn(comp.result())); + default: + throw new IllegalArgumentException("Unexpected ExprKind: " + expr.getKind()); + } + } + + private boolean hasOnlySwappableOverloads(long id) { + ImmutableList overloadIds = references.get(id).overloadIds(); + // Fail closed: containsAll is vacuously true for an empty list. + return !overloadIds.isEmpty() && SWAPPABLE_OVERLOAD_IDS.containsAll(overloadIds); + } + + @SuppressWarnings("ValueClassIdentity") // Integer values are only unboxed, never compared. + private ValueNumberer(CelMutableExpr root, ImmutableMap references) { + this.references = references; + this.keyToVn = new HashMap<>(); + this.exprToVn = new IdentityHashMap<>(); + computeVn(root); + } } /** Options to configure how Common Subexpression Elimination behave. */ diff --git a/optimizer/src/test/java/dev/cel/optimizer/optimizers/SubexpressionOptimizerBaselineTest.java b/optimizer/src/test/java/dev/cel/optimizer/optimizers/SubexpressionOptimizerBaselineTest.java index ffe79faf1..fed21e99a 100644 --- a/optimizer/src/test/java/dev/cel/optimizer/optimizers/SubexpressionOptimizerBaselineTest.java +++ b/optimizer/src/test/java/dev/cel/optimizer/optimizers/SubexpressionOptimizerBaselineTest.java @@ -403,7 +403,17 @@ private enum CseTestCase { "pure_custom_func(msg.oneof_type.payload.single_int64) +" + " pure_custom_func(msg.oneof_type.payload.single_int32) +" + " pure_custom_func(msg.oneof_type.payload.single_int64) +" - + " pure_custom_func(msg.single_int64)"); + + " pure_custom_func(msg.single_int64)"), + VALUE_NUMBERING_COMMUTATIVE_ADD_CONVERSE_RELATION_LOGICAL_AND( + "(msg.single_int64 + msg.single_int32 > 5 && msg.oneof_type.payload.single_int64 <" + + " msg.single_int64 + msg.single_int32) == (msg.single_int32 + msg.single_int64 >" + + " msg.oneof_type.payload.single_int64 && 5 < msg.single_int32 + msg.single_int64)"), + VALUE_NUMBERING_COMMUTATIVE_MULTIPLY_EQUALITY_LOGICAL_OR( + "(msg.oneof_type.payload.single_int32 * msg.single_int64 == 24 || msg.single_int32 !=" + + " msg.oneof_type.payload.single_int64) && (msg.oneof_type.payload.single_int64 !=" + + " msg.single_int32 || 24 == msg.single_int64 * msg.oneof_type.payload.single_int32)"), + ; + private final String source; CseTestCase(String source) { diff --git a/optimizer/src/test/java/dev/cel/optimizer/optimizers/SubexpressionOptimizerTest.java b/optimizer/src/test/java/dev/cel/optimizer/optimizers/SubexpressionOptimizerTest.java index cba16b6df..a206e26e2 100644 --- a/optimizer/src/test/java/dev/cel/optimizer/optimizers/SubexpressionOptimizerTest.java +++ b/optimizer/src/test/java/dev/cel/optimizer/optimizers/SubexpressionOptimizerTest.java @@ -79,8 +79,9 @@ private static Cel setupCelEnv(CelBuilder celBuilder) { .populateMacroCalls(true) .enableHeterogeneousNumericComparisons(true) .build()) - .addCompilerLibraries(CelExtensions.bindings(), CelExtensions.strings()) - .addRuntimeLibraries(CelExtensions.strings()) + .addCompilerLibraries( + CelExtensions.bindings(), CelExtensions.strings(), CelExtensions.optional()) + .addRuntimeLibraries(CelExtensions.strings(), CelExtensions.optional()) .addFunctionDeclarations( CelFunctionDecl.newFunctionDeclaration( "non_pure_custom_func", @@ -822,6 +823,175 @@ public void optimize_afterSelectOptimizer_throwsException( .contains("SubexpressionOptimizer must run before SelectOptimizer."); } + private enum ValueNumberingTestCase { + COMMUTATIVE_INT_ADD( + "(msg.single_int64 + msg.single_int32) * (msg.single_int32 + msg.single_int64)", + "cel.@block([msg.single_int64 + msg.single_int32], @index0 * @index0)"), + COMMUTATIVE_UINT_MULTIPLY( + "(msg.single_uint64 * msg.single_uint32) + (msg.single_uint32 * msg.single_uint64)", + "cel.@block([msg.single_uint64 * msg.single_uint32], @index0 + @index0)"), + COMMUTATIVE_DOUBLE_ADD( + "(msg.single_double + 1.5) * (1.5 + msg.single_double)", + "cel.@block([msg.single_double + 1.5], @index0 * @index0)"), + COMMUTATIVE_DURATION_TIMESTAMP_ADD( + "(msg.single_duration + msg.single_timestamp) == (msg.single_timestamp +" + + " msg.single_duration)", + "cel.@block([msg.single_duration + msg.single_timestamp], @index0 == @index0)"), + NON_COMMUTATIVE_INT_SUBTRACT_PRESERVED( + "(msg.single_int64 - msg.single_int32) == (msg.single_int32 - msg.single_int64)", + "cel.@block([msg.single_int32, msg.single_int64], @index1 - @index0 == @index0 - @index1)"), + NON_COMMUTATIVE_STRING_CONCAT_PRESERVED( + "(msg.single_string + \"a\") == (\"a\" + msg.single_string)", + "cel.@block([msg.single_string], @index0 + \"a\" == \"a\" + @index0)"), + NON_COMMUTATIVE_DYN_ADD_PRESERVED( + "(dyn(msg.single_string) + dyn(\"a\")) == (dyn(\"a\") + dyn(msg.single_string))", + "cel.@block([dyn(\"a\"), dyn(msg.single_string)], @index1 + @index0 == @index0 +" + + " @index1)"), + COMMUTATIVE_EQUALS( + "(msg.single_int64 == 1) && (1 == msg.single_int64)", + "cel.@block([msg.single_int64 == 1], @index0 && @index0)"), + COMMUTATIVE_NOT_EQUALS( + "(msg.single_int64 != 1) || (1 != msg.single_int64)", + "cel.@block([msg.single_int64 != 1], @index0 || @index0)"), + COMMUTATIVE_LOGICAL_AND( + "((msg.single_int64 == 1) && (msg.single_int32 == 2)) || ((msg.single_int32 == 2) &&" + + " (msg.single_int64 == 1))", + "cel.@block([msg.single_int64 == 1 && msg.single_int32 == 2], @index0 || @index0)"), + COMMUTATIVE_LOGICAL_OR( + "((msg.single_int64 == 1) || (msg.single_int32 == 2)) && ((msg.single_int32 == 2) ||" + + " (msg.single_int64 == 1))", + "cel.@block([msg.single_int64 == 1 || msg.single_int32 == 2], @index0 && @index0)"), + RELATIONAL_CONVERSE_LESS_GREATER( + "(msg.single_int64 < msg.single_int32) && (msg.single_int32 > msg.single_int64)", + "cel.@block([msg.single_int64 < msg.single_int32], @index0 && @index0)"), + RELATIONAL_CONVERSE_LESS_EQUALS_GREATER_EQUALS( + "(msg.single_int64 <= msg.single_int32) && (msg.single_int32 >= msg.single_int64)", + "cel.@block([msg.single_int64 <= msg.single_int32], @index0 && @index0)"), + NOT_EQUALS_WITHOUT_SHARED_EQUALS_PRESERVED( + "[msg.single_int64 != 1, msg.single_int64 != 1, msg.single_int64 == 1]", + "cel.@block([msg.single_int64 != 1], [@index0, @index0, msg.single_int64 == 1])"), + OPTIONAL_LIST_AND_MAP_DISTINCT_VALUE_NUMBERS( + "([?optional.of(msg.single_int64)].size() == [optional.of(msg.single_int64)].size()) &&" + + " ({?\"k\": optional.of(msg.single_int64)}.size() == {\"k\":" + + " optional.of(msg.single_int64)}.size())", + "cel.@block([optional.of(msg.single_int64)], [?@index0].size() == [@index0].size() &&" + + " {?\"k\": @index0}.size() == {\"k\": @index0}.size())"), + ; + + private final String source; + private final String expectedUnparsed; + + ValueNumberingTestCase(String source, String expectedUnparsed) { + this.source = source; + this.expectedUnparsed = expectedUnparsed; + } + } + + @Test + public void cse_valueNumbering(@TestParameter ValueNumberingTestCase testCase) throws Exception { + CelAbstractSyntaxTree ast = cel.compile(testCase.source).getAst(); + CelOptimizer optimizer = + newCseOptimizer( + SubexpressionOptimizerOptions.newBuilder().populateMacroCalls(true).build()); + ImmutableMap input = + ImmutableMap.of( + "msg", + TestAllTypes.newBuilder() + .setSingleInt64(1L) + .setSingleInt32(2) + .setSingleUint64(3L) + .setSingleUint32(4) + .setSingleDouble(2.5) + .setSingleString("hello") + .build()); + + CelAbstractSyntaxTree optimizedAst = optimizer.optimize(ast); + + assertThat(CEL_UNPARSER.unparse(optimizedAst)).isEqualTo(testCase.expectedUnparsed); + assertThat(cel.createProgram(optimizedAst).eval(input)) + .isEqualTo(cel.createProgram(ast).eval(input)); + } + + @Test + public void cse_customOperatorOverloads_preservesSemantics( + @TestParameter({ + "(msg.single_string + msg.single_int64) == (msg.single_int64 + msg.single_string)", + "(msg.single_string > msg.single_int64) == (msg.single_int64 < msg.single_string)" + }) + String source) + throws Exception { + Cel cel = + this.cel + .toCelBuilder() + .addFunctionDeclarations( + CelFunctionDecl.newFunctionDeclaration( + "_+_", + newGlobalOverload( + "add_string_int", SimpleType.INT, SimpleType.STRING, SimpleType.INT), + newGlobalOverload( + "add_int_string", SimpleType.INT, SimpleType.INT, SimpleType.STRING)), + CelFunctionDecl.newFunctionDeclaration( + "_>_", + newGlobalOverload( + "greater_string_int", SimpleType.BOOL, SimpleType.STRING, SimpleType.INT)), + CelFunctionDecl.newFunctionDeclaration( + "_<_", + newGlobalOverload( + "less_int_string", SimpleType.BOOL, SimpleType.INT, SimpleType.STRING))) + .addFunctionBindings( + CelFunctionBinding.from( + "add_string_int", String.class, Long.class, (s, i) -> s.length() + i), + CelFunctionBinding.from( + "add_int_string", Long.class, String.class, (i, s) -> i * 100 + s.length()), + CelFunctionBinding.from( + "greater_string_int", String.class, Long.class, (s, i) -> s.length() > i), + CelFunctionBinding.from( + "less_int_string", Long.class, String.class, (i, s) -> i >= 0)) + .build(); + CelOptimizer optimizer = + CelOptimizerFactory.standardCelOptimizerBuilder(cel) + .addAstOptimizers(SubexpressionOptimizer.getInstance()) + .build(); + CelAbstractSyntaxTree ast = cel.compile(source).getAst(); + ImmutableMap input = + ImmutableMap.of( + "msg", TestAllTypes.newBuilder().setSingleString("a").setSingleInt64(1L).build()); + + CelAbstractSyntaxTree optimizedAst = optimizer.optimize(ast); + + assertThat(cel.createProgram(optimizedAst).eval(input)) + .isEqualTo(cel.createProgram(ast).eval(input)); + } + + @Test + public void cse_oneSidedCustomOverloadOnDyn_notMerged( + @TestParameter({"(x < 5) == (5 > x)", "(x + 1) == (1 + x)"}) String source) throws Exception { + // With x: dyn, only the left operand also resolves to the custom overload, so the right one is + // standard-only and swappable. The two must not share a value number. + Cel cel = + this.cel + .toCelBuilder() + .addFunctionDeclarations( + CelFunctionDecl.newFunctionDeclaration( + "_<_", + newGlobalOverload( + "less_string_int", SimpleType.BOOL, SimpleType.STRING, SimpleType.INT)), + CelFunctionDecl.newFunctionDeclaration( + "_+_", + newGlobalOverload( + "add_string_int", SimpleType.INT, SimpleType.STRING, SimpleType.INT))) + .build(); + CelOptimizer optimizer = + CelOptimizerFactory.standardCelOptimizerBuilder(cel) + .addAstOptimizers(SubexpressionOptimizer.getInstance()) + .build(); + CelAbstractSyntaxTree ast = cel.compile(source).getAst(); + + CelAbstractSyntaxTree optimizedAst = optimizer.optimize(ast); + + assertThat(CEL_UNPARSER.unparse(optimizedAst)).isEqualTo(CEL_UNPARSER.unparse(ast)); + } + /** * Converts AST containing cel.block related test functions to internal functions (e.g: cel.block * -> cel.@block) diff --git a/optimizer/src/test/resources/constfold_before_subexpression_unparsed.baseline b/optimizer/src/test/resources/constfold_before_subexpression_unparsed.baseline index 1cceb0c60..9fde9e160 100644 --- a/optimizer/src/test/resources/constfold_before_subexpression_unparsed.baseline +++ b/optimizer/src/test/resources/constfold_before_subexpression_unparsed.baseline @@ -314,4 +314,16 @@ Test case: CUSTOM_FUNCTION_ELIMINABLE Source: pure_custom_func(msg.oneof_type.payload.single_int64) + pure_custom_func(msg.oneof_type.payload.single_int32) + pure_custom_func(msg.oneof_type.payload.single_int64) + pure_custom_func(msg.single_int64) =====> Result: 31 -Unparsed: cel.@block([msg.oneof_type.payload, pure_custom_func(@index0.single_int64)], @index1 + pure_custom_func(@index0.single_int32) + @index1 + pure_custom_func(msg.single_int64)) \ No newline at end of file +Unparsed: cel.@block([msg.oneof_type.payload, pure_custom_func(@index0.single_int64)], @index1 + pure_custom_func(@index0.single_int32) + @index1 + pure_custom_func(msg.single_int64)) + +Test case: VALUE_NUMBERING_COMMUTATIVE_ADD_CONVERSE_RELATION_LOGICAL_AND +Source: (msg.single_int64 + msg.single_int32 > 5 && msg.oneof_type.payload.single_int64 < msg.single_int64 + msg.single_int32) == (msg.single_int32 + msg.single_int64 > msg.oneof_type.payload.single_int64 && 5 < msg.single_int32 + msg.single_int64) +=====> +Result: true +Unparsed: cel.@block([msg.single_int64 + msg.single_int32, @index0 > 5 && msg.oneof_type.payload.single_int64 < @index0], @index1 == @index1) + +Test case: VALUE_NUMBERING_COMMUTATIVE_MULTIPLY_EQUALITY_LOGICAL_OR +Source: (msg.oneof_type.payload.single_int32 * msg.single_int64 == 24 || msg.single_int32 != msg.oneof_type.payload.single_int64) && (msg.oneof_type.payload.single_int64 != msg.single_int32 || 24 == msg.single_int64 * msg.oneof_type.payload.single_int32) +=====> +Result: true +Unparsed: cel.@block([msg.oneof_type.payload, @index0.single_int32 * msg.single_int64 == 24 || msg.single_int32 != @index0.single_int64], @index1 && @index1) \ No newline at end of file diff --git a/optimizer/src/test/resources/subexpression_ast_block_common_subexpr_only.baseline b/optimizer/src/test/resources/subexpression_ast_block_common_subexpr_only.baseline index 9782b5dc2..a3fe457e5 100644 --- a/optimizer/src/test/resources/subexpression_ast_block_common_subexpr_only.baseline +++ b/optimizer/src/test/resources/subexpression_ast_block_common_subexpr_only.baseline @@ -4755,4 +4755,144 @@ CALL [1] { } } } +} +Test case: VALUE_NUMBERING_COMMUTATIVE_ADD_CONVERSE_RELATION_LOGICAL_AND +Source: (msg.single_int64 + msg.single_int32 > 5 && msg.oneof_type.payload.single_int64 < msg.single_int64 + msg.single_int32) == (msg.single_int32 + msg.single_int64 > msg.oneof_type.payload.single_int64 && 5 < msg.single_int32 + msg.single_int64) +=====> +CALL [1] { + function: cel.@block + args: { + LIST [2] { + elements: { + CALL [3] { + function: _+_ + args: { + SELECT [4] { + IDENT [5] { + name: msg + }.single_int64 + } + SELECT [6] { + IDENT [7] { + name: msg + }.single_int32 + } + } + } + CALL [8] { + function: _&&_ + args: { + CALL [9] { + function: _>_ + args: { + IDENT [10] { + name: @index0 + } + CONSTANT [11] { value: 5 } + } + } + CALL [12] { + function: _<_ + args: { + SELECT [13] { + SELECT [14] { + SELECT [15] { + IDENT [16] { + name: msg + }.oneof_type + }.payload + }.single_int64 + } + IDENT [17] { + name: @index0 + } + } + } + } + } + } + } + CALL [18] { + function: _==_ + args: { + IDENT [19] { + name: @index1 + } + IDENT [20] { + name: @index1 + } + } + } + } +} +Test case: VALUE_NUMBERING_COMMUTATIVE_MULTIPLY_EQUALITY_LOGICAL_OR +Source: (msg.oneof_type.payload.single_int32 * msg.single_int64 == 24 || msg.single_int32 != msg.oneof_type.payload.single_int64) && (msg.oneof_type.payload.single_int64 != msg.single_int32 || 24 == msg.single_int64 * msg.oneof_type.payload.single_int32) +=====> +CALL [1] { + function: cel.@block + args: { + LIST [2] { + elements: { + SELECT [3] { + SELECT [4] { + IDENT [5] { + name: msg + }.oneof_type + }.payload + } + CALL [6] { + function: _||_ + args: { + CALL [7] { + function: _==_ + args: { + CALL [8] { + function: _*_ + args: { + SELECT [9] { + IDENT [10] { + name: @index0 + }.single_int32 + } + SELECT [11] { + IDENT [12] { + name: msg + }.single_int64 + } + } + } + CONSTANT [13] { value: 24 } + } + } + CALL [14] { + function: _!=_ + args: { + SELECT [15] { + IDENT [16] { + name: msg + }.single_int32 + } + SELECT [17] { + IDENT [18] { + name: @index0 + }.single_int64 + } + } + } + } + } + } + } + CALL [19] { + function: _&&_ + args: { + IDENT [20] { + name: @index1 + } + IDENT [21] { + name: @index1 + } + } + } + } } \ No newline at end of file diff --git a/optimizer/src/test/resources/subexpression_unparsed.baseline b/optimizer/src/test/resources/subexpression_unparsed.baseline index 411d74828..b8913047b 100644 --- a/optimizer/src/test/resources/subexpression_unparsed.baseline +++ b/optimizer/src/test/resources/subexpression_unparsed.baseline @@ -314,4 +314,16 @@ Test case: CUSTOM_FUNCTION_ELIMINABLE Source: pure_custom_func(msg.oneof_type.payload.single_int64) + pure_custom_func(msg.oneof_type.payload.single_int32) + pure_custom_func(msg.oneof_type.payload.single_int64) + pure_custom_func(msg.single_int64) =====> Result: 31 -Unparsed: cel.@block([msg.oneof_type.payload, pure_custom_func(@index0.single_int64)], @index1 + pure_custom_func(@index0.single_int32) + @index1 + pure_custom_func(msg.single_int64)) \ No newline at end of file +Unparsed: cel.@block([msg.oneof_type.payload, pure_custom_func(@index0.single_int64)], @index1 + pure_custom_func(@index0.single_int32) + @index1 + pure_custom_func(msg.single_int64)) + +Test case: VALUE_NUMBERING_COMMUTATIVE_ADD_CONVERSE_RELATION_LOGICAL_AND +Source: (msg.single_int64 + msg.single_int32 > 5 && msg.oneof_type.payload.single_int64 < msg.single_int64 + msg.single_int32) == (msg.single_int32 + msg.single_int64 > msg.oneof_type.payload.single_int64 && 5 < msg.single_int32 + msg.single_int64) +=====> +Result: true +Unparsed: cel.@block([msg.single_int64 + msg.single_int32, @index0 > 5 && msg.oneof_type.payload.single_int64 < @index0], @index1 == @index1) + +Test case: VALUE_NUMBERING_COMMUTATIVE_MULTIPLY_EQUALITY_LOGICAL_OR +Source: (msg.oneof_type.payload.single_int32 * msg.single_int64 == 24 || msg.single_int32 != msg.oneof_type.payload.single_int64) && (msg.oneof_type.payload.single_int64 != msg.single_int32 || 24 == msg.single_int64 * msg.oneof_type.payload.single_int32) +=====> +Result: true +Unparsed: cel.@block([msg.oneof_type.payload, @index0.single_int32 * msg.single_int64 == 24 || msg.single_int32 != @index0.single_int64], @index1 && @index1) \ No newline at end of file