Skip to content

Commit 415fcc5

Browse files
committed
Improve transforms
Rename CfgGuidedOrdering to InitialOrdering
1 parent 84cf216 commit 415fcc5

17 files changed

+1258
-201
lines changed

smithy-rules-engine/src/main/java/software/amazon/smithy/rulesengine/logic/bdd/BddEquivalenceChecker.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ private BddEquivalenceChecker(Cfg cfg, Bdd bdd, List<Condition> conditions, List
6060
this.bdd = bdd;
6161
this.conditions = conditions;
6262
this.results = results;
63-
this.parameters = new ArrayList<>(cfg.getRuleSet().getParameters().toList());
63+
this.parameters = new ArrayList<>(cfg.getParameters().toList());
6464

6565
for (int i = 0; i < conditions.size(); i++) {
6666
conditionToIndex.put(conditions.get(i), i);

smithy-rules-engine/src/main/java/software/amazon/smithy/rulesengine/logic/bdd/BddTrait.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ public static BddTrait from(Cfg cfg) {
7373
}
7474

7575
return builder()
76-
.parameters(cfg.getRuleSet().getParameters())
76+
.parameters(cfg.getParameters())
7777
.conditions(compiler.getOrderedConditions())
7878
.results(compiler.getIndexedResults())
7979
.bdd(bdd)
Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,15 @@
2323
* together in the BDD, enabling better node sharing. This ordering implementation flattens the tree structure while
2424
* respecting data dependencies.
2525
*/
26-
final class CfgGuidedOrdering implements OrderingStrategy {
27-
private static final Logger LOGGER = Logger.getLogger(CfgGuidedOrdering.class.getName());
26+
final class InitialOrdering implements OrderingStrategy {
27+
private static final Logger LOGGER = Logger.getLogger(InitialOrdering.class.getName());
2828

2929
/** How many distinct consumers make an isSet() a "gate". */
3030
private static final int GATE_SUCCESSOR_THRESHOLD = 2;
3131

3232
private final Cfg cfg;
3333

34-
CfgGuidedOrdering(Cfg cfg) {
34+
InitialOrdering(Cfg cfg) {
3535
this.cfg = cfg;
3636
}
3737

@@ -51,6 +51,7 @@ public List<Condition> orderConditions(Condition[] conditions) {
5151

5252
long elapsed = System.currentTimeMillis() - startTime;
5353
LOGGER.info(() -> String.format("Initial ordering: %d conditions in %dms", conditions.length, elapsed));
54+
result.forEach(System.out::println);
5455
return result;
5556
}
5657

smithy-rules-engine/src/main/java/software/amazon/smithy/rulesengine/logic/bdd/OrderingStrategy.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ interface OrderingStrategy {
2828
* @return the initial ordering strategy.
2929
*/
3030
static OrderingStrategy initialOrdering(Cfg cfg) {
31-
return new CfgGuidedOrdering(cfg);
31+
return new InitialOrdering(cfg);
3232
}
3333

3434
/**

smithy-rules-engine/src/main/java/software/amazon/smithy/rulesengine/logic/cfg/Cfg.java

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import java.util.NoSuchElementException;
1818
import java.util.Set;
1919
import software.amazon.smithy.rulesengine.language.EndpointRuleSet;
20+
import software.amazon.smithy.rulesengine.language.syntax.parameters.Parameters;
2021
import software.amazon.smithy.rulesengine.language.syntax.rule.Condition;
2122
import software.amazon.smithy.rulesengine.language.syntax.rule.EndpointRule;
2223
import software.amazon.smithy.rulesengine.language.syntax.rule.ErrorRule;
@@ -38,16 +39,20 @@
3839
*/
3940
public final class Cfg implements Iterable<CfgNode> {
4041

41-
private final EndpointRuleSet ruleSet;
42+
private final Parameters parameters;
4243
private final CfgNode root;
4344

4445
// Lazily computed condition data
4546
private Condition[] conditions;
4647
private Map<Condition, Integer> conditionToIndex;
4748

4849
Cfg(EndpointRuleSet ruleSet, CfgNode root) {
49-
this.ruleSet = ruleSet;
50+
this(ruleSet == null ? Parameters.builder().build() : ruleSet.getParameters(), root);
51+
}
52+
53+
Cfg(Parameters parameters, CfgNode root) {
5054
this.root = SmithyBuilder.requiredState("root", root);
55+
this.parameters = parameters;
5156
}
5257

5358
/**
@@ -125,8 +130,8 @@ private synchronized void extractConditions() {
125130
this.conditionToIndex = indexMap;
126131
}
127132

128-
public EndpointRuleSet getRuleSet() {
129-
return ruleSet;
133+
public Parameters getParameters() {
134+
return parameters;
130135
}
131136

132137
@Override

smithy-rules-engine/src/main/java/software/amazon/smithy/rulesengine/logic/cfg/CoalesceTransform.java

Lines changed: 34 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -45,21 +45,17 @@ static EndpointRuleSet transform(EndpointRuleSet ruleSet) {
4545
CoalesceTransform transform = new CoalesceTransform();
4646

4747
List<Rule> transformedRules = new ArrayList<>();
48-
for (Rule rule : ruleSet.getRules()) {
49-
transformedRules.add(transform.transformRule(rule));
48+
for (int i = 0; i < ruleSet.getRules().size(); i++) {
49+
transformedRules.add(transform.transformRule(ruleSet.getRules().get(i), "root/rule[" + i + "]"));
5050
}
5151

5252
if (LOGGER.isLoggable(Level.INFO)) {
53-
StringBuilder msg = new StringBuilder();
54-
msg.append("\n=== Coalescing Transform Complete ===\n");
55-
msg.append("Total: ").append(transform.coalesceCount).append(" coalesced, ");
56-
msg.append(transform.cacheHits).append(" cache hits, ");
57-
msg.append(transform.skippedNoZeroValue).append(" skipped (no zero value), ");
58-
msg.append(transform.skippedMultipleUses).append(" skipped (multiple uses)");
59-
if (!transform.skippedRecordTypes.isEmpty()) {
60-
msg.append("\nSkipped record-returning functions: ").append(transform.skippedRecordTypes);
61-
}
62-
LOGGER.info(msg.toString());
53+
LOGGER.info(String.format(
54+
"Coalescing: %d coalesced, %d cache hits, %d skipped (no zero), %d skipped (multiple uses)",
55+
transform.coalesceCount,
56+
transform.cacheHits,
57+
transform.skippedNoZeroValue,
58+
transform.skippedMultipleUses));
6359
}
6460

6561
return EndpointRuleSet.builder()
@@ -69,50 +65,31 @@ static EndpointRuleSet transform(EndpointRuleSet ruleSet) {
6965
.build();
7066
}
7167

72-
private Rule transformRule(Rule rule) {
68+
private Rule transformRule(Rule rule, String rulePath) {
7369
Set<Condition> eliminatedConditions = new HashSet<>();
74-
List<Condition> conditions = rule.getConditions();
75-
Map<String, Integer> localVarUsage = countLocalVariableUsage(conditions);
76-
List<Condition> transformedConditions = transformConditions(conditions, eliminatedConditions, localVarUsage);
7770

78-
if (rule instanceof TreeRule) {
79-
TreeRule treeRule = (TreeRule) rule;
80-
List<Rule> transformedNestedRules = new ArrayList<>();
81-
boolean nestedChanged = false;
82-
83-
for (Rule nestedRule : treeRule.getRules()) {
84-
Rule transformedNested = transformRule(nestedRule);
85-
transformedNestedRules.add(transformedNested);
86-
if (transformedNested != nestedRule) {
87-
nestedChanged = true;
88-
}
89-
}
90-
91-
if (!transformedConditions.equals(conditions) || nestedChanged) {
92-
return TreeRule.builder()
93-
.description(rule.getDocumentation().orElse(null))
94-
.conditions(transformedConditions)
95-
.treeRule(transformedNestedRules);
71+
// Count local usage for THIS rule's conditions
72+
Map<String, Integer> localVarUsage = new HashMap<>();
73+
for (Condition condition : rule.getConditions()) {
74+
for (String ref : condition.getFunction().getReferences()) {
75+
localVarUsage.merge(ref, 1, Integer::sum);
9676
}
97-
} else if (!transformedConditions.equals(conditions)) {
98-
// For other rule types, just update conditions
99-
return rule.withConditions(transformedConditions);
10077
}
10178

102-
return rule;
103-
}
104-
105-
private Map<String, Integer> countLocalVariableUsage(List<Condition> conditions) {
106-
Map<String, Integer> usage = new HashMap<>();
79+
List<Condition> transformedConditions = transformConditions(
80+
rule.getConditions(),
81+
eliminatedConditions,
82+
localVarUsage);
10783

108-
// Count how many times each variable is used within this specific rule
109-
for (Condition condition : conditions) {
110-
for (String ref : condition.getFunction().getReferences()) {
111-
usage.merge(ref, 1, Integer::sum);
112-
}
84+
if (rule instanceof TreeRule) {
85+
TreeRule treeRule = (TreeRule) rule;
86+
return TreeRule.builder()
87+
.description(rule.getDocumentation().orElse(null))
88+
.conditions(transformedConditions)
89+
.treeRule(TreeRewriter.transformNestedRules(treeRule, rulePath, this::transformRule));
11390
}
11491

115-
return usage;
92+
return rule.withConditions(transformedConditions);
11693
}
11794

11895
private List<Condition> transformConditions(
@@ -128,25 +105,19 @@ private List<Condition> transformConditions(
128105
continue;
129106
}
130107

131-
// Check if this is a bind that can be coalesced with the next condition
132108
if (i + 1 < conditions.size() && current.getResult().isPresent()) {
133109
String var = current.getResult().get().toString();
134110
Condition next = conditions.get(i + 1);
135111

136112
if (canCoalesce(var, current, next, localVarUsage)) {
137-
// Create coalesced condition
138-
Condition coalesced = createCoalescedCondition(current, next, var);
139-
result.add(coalesced);
140-
// Mark both conditions as eliminated
113+
result.add(createCoalescedCondition(current, next, var));
141114
eliminatedConditions.add(current);
142115
eliminatedConditions.add(next);
143-
// Skip the next condition
144-
i++;
116+
i++; // Skip next
145117
continue;
146118
}
147119
}
148120

149-
// No coalescing possible, keep the condition as-is
150121
result.add(current);
151122
}
152123

@@ -155,28 +126,22 @@ private List<Condition> transformConditions(
155126

156127
private boolean canCoalesce(String var, Condition bind, Condition use, Map<String, Integer> localVarUsage) {
157128
if (!use.getFunction().getReferences().contains(var)) {
158-
// The use condition must reference the variable
159129
return false;
160-
} else if (use.getFunction().getFunctionDefinition() == IsSet.getDefinition()) {
161-
// Never coalesce into presence checks (isSet)
130+
}
131+
132+
if (use.getFunction().getFunctionDefinition() == IsSet.getDefinition()) {
162133
return false;
163134
}
164135

165-
// Check if variable is only used once in this local rule context (even if it appears multiple times globally)
136+
// Check local usage
166137
Integer localUses = localVarUsage.get(var);
167138
if (localUses == null || localUses > 1) {
168139
skippedMultipleUses++;
169140
return false;
170141
}
171142

172-
// Get the actual return type (could be Optional<T> or T)
173143
Type type = bind.getFunction().getFunctionDefinition().getReturnType();
174-
175-
// Check if we can get a zero value for this type. For OptionalType, we use the inner type's zero value
176-
Type innerType = type;
177-
if (type instanceof OptionalType) {
178-
innerType = ((OptionalType) type).inner();
179-
}
144+
Type innerType = type instanceof OptionalType ? ((OptionalType) type).inner() : type;
180145

181146
if (innerType instanceof RecordType) {
182147
skippedNoZeroValue++;
@@ -196,16 +161,11 @@ private Condition createCoalescedCondition(Condition bind, Condition use, String
196161
LibraryFunction bindExpr = bind.getFunction();
197162
LibraryFunction useExpr = use.getFunction();
198163

199-
// Get the type and its zero value
200164
Type type = bindExpr.getFunctionDefinition().getReturnType();
201-
Type innerType = type;
202-
if (type instanceof OptionalType) {
203-
innerType = ((OptionalType) type).inner();
204-
}
165+
Type innerType = type instanceof OptionalType ? ((OptionalType) type).inner() : type;
205166

206167
Literal zero = innerType.getZeroValue().get();
207168

208-
// Create cache key based on canonical representations
209169
String bindCanonical = bindExpr.canonicalize().toString();
210170
String zeroCanonical = zero.toString();
211171
String useCanonical = useExpr.canonicalize().toString();
@@ -220,10 +180,9 @@ private Condition createCoalescedCondition(Condition bind, Condition use, String
220180

221181
Expression coalesced = Coalesce.ofExpressions(bindExpr, zero);
222182

223-
// Replace the variable reference in the use expression
224183
Map<String, Expression> replacements = new HashMap<>();
225184
replacements.put(var, coalesced);
226-
ReferenceRewriter rewriter = ReferenceRewriter.forReplacements(replacements);
185+
TreeRewriter rewriter = TreeRewriter.forReplacements(replacements);
227186
Expression replaced = rewriter.rewrite(useExpr);
228187
LibraryFunction canonicalized = ((LibraryFunction) replaced).canonicalize();
229188

0 commit comments

Comments
 (0)