diff --git a/README.md b/README.md index 80fb122..0abd16b 100644 --- a/README.md +++ b/README.md @@ -21,14 +21,6 @@ workflows and other language constructs. * `StringFormatExpression`: An expression that creates a string using a supplied format string and parameters. * `DebugExpression`: An expression that helps when debugging expression trees. -* **Expression Optimizers** - * `OptimizerBuilder`: An composite optimizer builder. - * `ConstantFoldingOptimizer`: An optimizer that precomputes constant expressions. - * `OperatorReductionOptimizer`: An optimizer that simplifies arithmetic and logical expressions. - * `StructuralReductionOptimizer`: An optimizer that removes unreachable code and consolidates nested or redundant constructs. - * `ValueBindingOptimizer`: An optimizer that simplifies and inlines, variable, constant, and member access operations. - * `SubExpressionCachingOptimizer`: An optimizer that identifies and caches repeated subexpressions within a tree. - * Supports Fast Expression Compiler (FEC) for improved performance. ## Examples diff --git a/docs/docs.projitems b/docs/docs.projitems index c377965..fa849f7 100644 --- a/docs/docs.projitems +++ b/docs/docs.projitems @@ -9,14 +9,6 @@ docs - - - - - - - - diff --git a/docs/index.md b/docs/index.md index 5645aa4..38ce917 100644 --- a/docs/index.md +++ b/docs/index.md @@ -26,14 +26,6 @@ trees to handle asynchronous workflows and other language constructs. * `StringFormatExpression`: An expression that creates a string using a supplied format string and parameters. * `DebugExpression`: An expression that helps when debugging expression trees. -* **Expression Optimizers** - * `OptimizerBuilder`: An composite optimizer builder. - * `ConstantFoldingOptimizer`: An optimizer that precomputes constant expressions. - * `OperatorReductionOptimizer`: An optimizer that simplifies arithmetic and logical expressions. - * `StructuralReductionOptimizer`: An optimizer that removes unreachable code and consolidates nested or redundant constructs. - * `ValueBindingOptimizer`: An optimizer that simplifies and inlines, variable, constant, and member access operations. - * `SubExpressionCachingOptimizer`: An optimizer that identifies and caches repeated subexpressions within a tree. - * Supports Fast Expression Compiler (FEC) for improved performance. ## Examples diff --git a/docs/optimizers/constant-folding-optimizer.md b/docs/optimizers/constant-folding-optimizer.md deleted file mode 100644 index 78344cb..0000000 --- a/docs/optimizers/constant-folding-optimizer.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -layout: default -title: Constant Folding Optimizer -parent: optimizers -nav_order: 3 ---- -# Constant Folding Optimizer -The **Constant Folding Optimizer** precomputes constant expressions. - -## **Purpose** -- **Issue**: Expressions involving only constants can unnecessarily consume runtime resources for evaluation. -- **Solution**: Replaces constant expressions with their precomputed values. -- **Result**: Simplifies trees and improves runtime performance by removing redundant evaluations. - -## **Usage Example** -### **Before Optimization** -```csharp -.Lambda #Lambda1> { - .Add(.Constant(3), .Constant(5)) -} -``` - -### **After Optimization** -```csharp -.Lambda #Lambda1> { - .Constant(8) -} -``` \ No newline at end of file diff --git a/docs/optimizers/operator-reduction-optimizer.md b/docs/optimizers/operator-reduction-optimizer.md deleted file mode 100644 index d25f048..0000000 --- a/docs/optimizers/operator-reduction-optimizer.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -layout: default -title: Operator Reduction Optimizer -parent: optimizers -nav_order: 4 ---- -# Operator Reduction Optimizer -The **Operator Reduction Optimizer** simplifies arithmetic and logical expressions. - -## **Purpose** -- **Issue**: Expression trees often contain redundant operations, such as adding zero or multiplying by one. -- **Solution**: By removing or simplifying these expressions, the optimizer reduces tree size and computation complexity. -- **Result**: Improved runtime performance through reduced instruction execution and simplified interpretation. - -## **Usage Example** -### **Before Optimization** -```csharp -.Lambda #Lambda1> { - .Add(.Parameter(x), .Constant(0)) -} -``` - -### **After Optimization** -```csharp -.Lambda #Lambda1> { - .Parameter(x) -} -``` \ No newline at end of file diff --git a/docs/optimizers/optimizer-builder.md b/docs/optimizers/optimizer-builder.md deleted file mode 100644 index 6816d6d..0000000 --- a/docs/optimizers/optimizer-builder.md +++ /dev/null @@ -1,40 +0,0 @@ ---- -layout: default -title: Optimizer Builder -parent: optimizers -nav_order: 2 ---- -# Optimizer Builder -The `OptimizerBuilder` constructs a composite optimizer, and orders optimizers based on their dependencies and priorities, ensuring proper execution. - -## **Dependency Ordering** -The builder: -1. Constructs a dependency graph based on declared dependencies. -2. Topologically sorts the graph to resolve dependency order. -3. Weaves standalone optimizers into the sequence based on priority. - -## **Workflow** -- **Input Optimizers**: - - `OperatorReductionOptimizer`: Depends on `ConstantFoldingVisitor` and `OperatorReductionVisitor`. - - `StructuralReductionOptimizer`: Depends on `StructuralReductionVisitor`. - -- **Dependency Graph**: - - Nodes: `{ConstantFoldingVisitor, OperatorReductionVisitor, StructuralReductionVisitor}` - - Edges: `{ConstantFoldingVisitor -> OperatorReductionVisitor}` - -- **Sorted Execution Order**: - 1. `ConstantFoldingVisitor` - 2. `OperatorReductionVisitor` - 3. `StructuralReductionVisitor` - -## **Usage Example** -### **Builder Setup** -```csharp -var optimizer = new OptimizerBuilder() - .With() - .With() - .With() - .Build(); - -var optimizedExpression = optimizer.Optimize(expression); -``` diff --git a/docs/optimizers/optimizers.md b/docs/optimizers/optimizers.md deleted file mode 100644 index 512b9cd..0000000 --- a/docs/optimizers/optimizers.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -layout: default -title: Optimizers -has_children: true -nav_order: 1 ---- -# Optimizers - -Expression optimization enhances the execution of expression trees by reducing redundancies, simplifying constructs, -and eliminating unnecessary computations. By restructuring and minimizing expression complexity, optimizations -improve runtime performance and reduce memory usage. diff --git a/docs/optimizers/overview.md b/docs/optimizers/overview.md deleted file mode 100644 index e0360fe..0000000 --- a/docs/optimizers/overview.md +++ /dev/null @@ -1,43 +0,0 @@ ---- -layout: default -title: Overview -parent: optimizers -nav_order: 1 ---- -# Overview of Expression Optimization - -Expression optimization enhances the execution of expression trees by reducing redundancies, simplifying constructs, -and eliminating unnecessary computations. By restructuring and minimizing expression complexity, optimizations -improve runtime performance and reduce memory usage. - -## Optimization Categories - -Optimizations generally address one fo four categories of expression tree elements: - -### Structural Optimization - -Simplifies control flow structures like loops, conditionals, and blocks. - - - **How**: Removes unreachable code and consolidates nested blocks. - - **Why**: Flattening nested blocks into a single sequence reduces interpreter overhead. - -### Expression-Level Optimization - -Optimizes arithmetic, logical, and constant expressions. - - - **How**: Eliminates trivial operations, such as adding zero or multiplying by one, and precomputes constant results. - - **Why**: Folding constants reduces evaluation costs at runtime. - -### Inlining and Binding - -Inlines variables, constants, and member accesses to reduce overhead and memory usage. - - - **How**: Eliminates temporary variables and simplifies bindings. - - **Why**: Replacing a single-use variable with its value directly reduces allocation and access overhead. - -### Subexpression Caching - -Identifies and caches reusable subexpressions to minimize repeated computations. - - - **How**: Prevents redundant evaluation of identical subexpressions in complex trees. - - **Why**: Storing the result of a reused subexpression in a temporary variable minimizes redundant computation. diff --git a/docs/optimizers/structural-reduction-optimizer.md b/docs/optimizers/structural-reduction-optimizer.md deleted file mode 100644 index 450ad05..0000000 --- a/docs/optimizers/structural-reduction-optimizer.md +++ /dev/null @@ -1,32 +0,0 @@ ---- -layout: default -title: Structural Reduction Optimizer -parent: optimizers -nav_order: 5 ---- -# Structural Reduction Optimizer -The **Structural Reduction Optimizer** removes unreachable code and consolidates nested or redundant constructs. - -## **Purpose** -- **Issue**: Control flow structures can introduce unnecessary branching and redundant constructs, increasing overhead. -- **Solution**: Simplifies control flow by removing dead branches, collapsing nested blocks, and optimizing loops. -- **Result**: Streamlined execution paths with reduced branching and improved readability. - -## **Usage Example** -### **Before Optimization** -```csharp -.Lambda #Lambda1> { - .IfThenElse( - .Constant(true), - .Constant(42), - .Constant(0) - ) -} -``` - -### **After Optimization** -```csharp -.Lambda #Lambda1> { - .Constant(42) -} -``` \ No newline at end of file diff --git a/docs/optimizers/subexpression-caching-optimizer.md b/docs/optimizers/subexpression-caching-optimizer.md deleted file mode 100644 index 7010398..0000000 --- a/docs/optimizers/subexpression-caching-optimizer.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -layout: default -title: Subexpression Caching Optimizer -parent: optimizers -nav_order: 7 ---- -# Subexpression Caching Optimizer -The **Subexpression Caching Optimizer** identifies and caches repeated subexpressions within a tree. - -## **Purpose** -- **Issue**: Identical subexpressions evaluated multiple times can lead to redundant computations. -- **Solution**: Creates temporary variables for reusable subexpressions and updates the tree to use these variables. -- **Result**: Reduced computation time by minimizing repeated evaluation. - -## **Usage Example** -### **Before Optimization** -```csharp -.Lambda #Lambda1> { - .Add(.Multiply(5, (3 + 2)), .Multiply(5, (3 + 2))) -} -``` - -### **After Optimization** -```csharp -.Lambda #Lambda1> { - .Block(System.Int32 $cacheVar) { - $cacheVar = .Multiply(5, (3 + 2)); - .Add($cacheVar, $cacheVar) - } -} -``` diff --git a/docs/optimizers/value-binding-optimizer.md b/docs/optimizers/value-binding-optimizer.md deleted file mode 100644 index f6cf764..0000000 --- a/docs/optimizers/value-binding-optimizer.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -layout: default -title: Value Binding Optimizer -parent: optimizers -nav_order: 6 ---- -# Value Binding Optimizer -The **Value Binding Optimizer** simplifies and inlines, variable, constant, and member access operations. - -## **Purpose** -- **Issue**: Expression trees often include temporary variables or member accesses that can be resolved to constants or direct values. -- **Solution**: Inlines single-use variables, evaluates constant member accesses, and simplifies bindings. -- **Result**: Reduced memory usage and improved evaluation speed. - -## **Usage Example** -### **Before Optimization** -```csharp -.Lambda #Lambda1> { - .Block( - .Assign(.Parameter(x), .Constant(5)), - .Parameter(x) - ) -} -``` - -### **After Optimization** -```csharp -.Lambda #Lambda1> { - .Constant(5) -} -``` \ No newline at end of file diff --git a/src/Hyperbee.Expressions/Optimizers/ConstantFoldingOptimizer.cs b/src/Hyperbee.Expressions/Optimizers/ConstantFoldingOptimizer.cs deleted file mode 100644 index 952e5a4..0000000 --- a/src/Hyperbee.Expressions/Optimizers/ConstantFoldingOptimizer.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Hyperbee.Expressions.Optimizers.Visitors; - -namespace Hyperbee.Expressions.Optimizers; - -// ConstantFoldingOptimizer: Constant Evaluation and Simplification -// -// This optimizer evaluates constant expressions and simplifies them to their simplest form. - -public class ConstantFoldingOptimizer : ExpressionOptimizer -{ - public override IExpressionTransformer[] Dependencies => - [ - new ConstantFoldingVisitor() - ]; -} diff --git a/src/Hyperbee.Expressions/Optimizers/ExpressionOptimizer.cs b/src/Hyperbee.Expressions/Optimizers/ExpressionOptimizer.cs deleted file mode 100644 index 91fa66e..0000000 --- a/src/Hyperbee.Expressions/Optimizers/ExpressionOptimizer.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System.Linq.Expressions; - -namespace Hyperbee.Expressions.Optimizers; - -public abstract class ExpressionOptimizer //: IExpressionOptimizer -{ - public abstract IExpressionTransformer[] Dependencies { get; } - - public virtual Expression Optimize( Expression expression ) - { - foreach ( var dependency in Dependencies ) - { - expression = dependency.Transform( expression ); - } - - return expression; - } - - public TExpr Optimize( TExpr expression ) where TExpr : LambdaExpression - { - foreach ( var dependency in Dependencies ) - { - expression = (TExpr) dependency.Transform( expression ); - } - - return expression; - } -} diff --git a/src/Hyperbee.Expressions/Optimizers/IExpressionTransformer.cs b/src/Hyperbee.Expressions/Optimizers/IExpressionTransformer.cs deleted file mode 100644 index 7106d48..0000000 --- a/src/Hyperbee.Expressions/Optimizers/IExpressionTransformer.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.Linq.Expressions; - -namespace Hyperbee.Expressions.Optimizers; - -public interface IExpressionTransformer -{ - int Priority { get; } - - Expression Transform( Expression expression ); -} - -public static class PriorityGroup -{ - public const int ConstantEvaluationAndSimplification = 0x0100; - public const int ControlFlowAndVariableSimplification = 0x0200; - public const int StructuralReductionAndConsolidation = 0x0400; - public const int ExpressionReductionAndCaching = 0x0800; -} diff --git a/src/Hyperbee.Expressions/Optimizers/InliningOptimizer.cs b/src/Hyperbee.Expressions/Optimizers/InliningOptimizer.cs deleted file mode 100644 index f2fc366..0000000 --- a/src/Hyperbee.Expressions/Optimizers/InliningOptimizer.cs +++ /dev/null @@ -1,33 +0,0 @@ -using Hyperbee.Expressions.Optimizers.Visitors; - -namespace Hyperbee.Expressions.Optimizers; - -// InliningOptimizer: Inlining Functions, Constants, and Conditional Values -// -// This optimizer inlines constants and simplifies expressions by propagating known values. -// It focuses on inlining lambda expressions, boolean short-circuiting, and simplifying conditional -// expressions based on constant values. -// -// Before: -// .Invoke(lambda, .Constant(5)) -// -// After: -// .LambdaExpression( -// .Parameter(x), -// .Add(.Constant(5), .Constant(5)) -// ) -// -// Before: -// .IfThenElse(.Constant(true), .Constant("True Branch"), .Constant("False Branch")) -// -// After: -// .Constant("True Branch") -// -public class InliningOptimizer : ExpressionOptimizer -{ - public override IExpressionTransformer[] Dependencies => - [ - new InliningVisitor() - ]; -} - diff --git a/src/Hyperbee.Expressions/Optimizers/OperatorReductionOptimizer.cs b/src/Hyperbee.Expressions/Optimizers/OperatorReductionOptimizer.cs deleted file mode 100644 index b965b3d..0000000 --- a/src/Hyperbee.Expressions/Optimizers/OperatorReductionOptimizer.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Hyperbee.Expressions.Optimizers.Visitors; - -namespace Hyperbee.Expressions.Optimizers; - -// ExpressionReduction: Arithmetic and Logical Reduction -// -// This optimizer removes trivial arithmetic and logical expressions that -// have no effect, simplifies nested expressions, and combines sequential -// expressions where possible. -// -// Examples: -// Before: -// .Add(.Parameter(x), .Constant(0)) -// -// After: -// .Parameter(x) -// -// Before: -// .Multiply(.Parameter(x), .Constant(1)) -// -// After: -// .Parameter(x) -// -public class OperatorReductionOptimizer : ExpressionOptimizer -{ - public override IExpressionTransformer[] Dependencies => - [ - new ConstantFoldingVisitor(), - new OperatorReductionVisitor() - ]; -} - diff --git a/src/Hyperbee.Expressions/Optimizers/OptimizerBuilder.cs b/src/Hyperbee.Expressions/Optimizers/OptimizerBuilder.cs deleted file mode 100644 index 7d0c03f..0000000 --- a/src/Hyperbee.Expressions/Optimizers/OptimizerBuilder.cs +++ /dev/null @@ -1,180 +0,0 @@ -namespace Hyperbee.Expressions.Optimizers; - -public class OptimizerBuilder -{ - private readonly List _optimizers = []; - - public OptimizerBuilder With() where T : ExpressionOptimizer, new() - { - _optimizers.Add( new T() ); - return this; - } - - public ExpressionOptimizer Build() - { - // Separate optimizers based on dependency count - var multiDependencyOptimizers = _optimizers.Where( opt => opt.Dependencies.Length > 1 ).ToList(); - var standaloneTransformers = _optimizers - .Where( opt => opt.Dependencies.Length == 1 ) - .SelectMany( opt => opt.Dependencies ) - .OrderBy( tran => tran.Priority ) - .ToList(); - - // Build and sort the dependency graph - var dependencyGraph = BuildDependencyGraph( multiDependencyOptimizers ); - var sortedDependencies = TopologicalSort( dependencyGraph ); - - // Weave standalone transformers into sorted dependencies to achieve a unified execution order - var wovenTransformers = WeaveStandaloneTransformers( sortedDependencies, standaloneTransformers ); - - return new CompositeOptimizer( wovenTransformers.ToArray() ); - } - - // Construct a dependency graph based on relative order within each optimizer's dependencies. - // Each visitor type becomes a node, and edges are added to maintain the internal order of each optimizer’s dependencies. - // This process ensures that dependencies are combined into a consistent order across optimizers without duplication. - // - // Example: - // Optimizer 1: [A, B, C, D] - // Optimizer 2: [S, C, E, F, D] - // - // Resulting Graph: - // Nodes: [A, B, C, D, S, E, F] - // Edges: [A → B, B → C, C → D, S → C, C → E, E → F, F → D] - // - // This ensures all relative dependencies are respected, creating a coherent dependency graph. - - private static Dictionary> BuildDependencyGraph( IEnumerable optimizers ) - { - var graph = new Dictionary>(); - - foreach ( var optimizer in optimizers ) - { - var dependencies = optimizer.Dependencies; - for ( var i = 0; i < dependencies.Length - 1; i++ ) - { - var current = dependencies[i].GetType(); - var next = dependencies[i + 1].GetType(); - - if ( !graph.ContainsKey( current ) ) - graph[current] = []; - - if ( !graph[current].Contains( next ) ) - graph[current].Add( next ); - } - - // Ensure each dependency type is represented as a node in the graph - foreach ( var dep in dependencies.Select( d => d.GetType() ) ) - { - if ( !graph.ContainsKey( dep ) ) - graph[dep] = []; - } - } - - return graph; - } - - // Perform a topological sort on the dependency graph to ensure a globally consistent order that satisfies - // all relative constraints from each optimizer's dependency list. - // - // Example: - // Given a graph with edges: [A → B, B → C, C → D, S → C, C → E, E → F, F → D] - // - // Calling TopologicalSort on this graph would yield the sorted order: [A, S, B, C, E, F, D]. - // This order preserves dependencies across optimizers and avoids duplication. - // - // Before Sort: - // Graph Edges: [A → B, B → C, C → D, S → C, C → E, E → F, F → D] - // - // After Sort: - // Sorted Order: [A, S, B, C, E, F, D] - - private List TopologicalSort( Dictionary> graph ) - { - var sorted = new List(); - var visited = new HashSet(); - var visiting = new HashSet(); - - foreach ( var node in graph.Keys ) - { - if ( !Visit( node ) ) - throw new InvalidOperationException( $"Cycle detected in dependencies involving {node}." ); - } - - return sorted.Select( type => - _optimizers.SelectMany( opt => opt.Dependencies ) - .First( dep => dep.GetType() == type ) - ).ToList(); - - bool Visit( Type node ) - { - if ( visited.Contains( node ) ) - return true; - - if ( !visiting.Add( node ) ) - return false; // Cycle detected - - foreach ( var neighbor in graph[node] ) - { - if ( !Visit( neighbor ) ) - return false; - } - - visiting.Remove( node ); - visited.Add( node ); - sorted.Add( node ); - - return true; - } - } - - // Weave standalone transformers into the sorted list of multi-dependency transformers, based on priority. - // This ensures that standalone transformers ordered according to priority, while respecting the sorted - // execution order of multi-dependency transformers. - // - // Example: - // Sorted Transformers: [A, B, C] - // Standalone Transformers: [X (Priority: 1), Y (Priority: 2)] - // - // Resulting Order: - // [X, A, B, Y, C] - // - // This interleaving ensures that the final list maintains dependency order while honoring standalone transformer priorities. - - private static List WeaveStandaloneTransformers( List sortedTransformers, List standaloneTransformers ) - { - var finalTransformers = new List(); - int standaloneIndex = 0; - - for ( var index = 0; index < sortedTransformers.Count; index++ ) - { - var transformer = sortedTransformers[index]; - while ( standaloneIndex < standaloneTransformers.Count && standaloneTransformers[standaloneIndex].Priority <= transformer.Priority ) - { - finalTransformers.Add( standaloneTransformers[standaloneIndex] ); - standaloneIndex++; - } - - finalTransformers.Add( transformer ); - } - - // Append any remaining standalone transformers - while ( standaloneIndex < standaloneTransformers.Count ) - { - finalTransformers.Add( standaloneTransformers[standaloneIndex] ); - standaloneIndex++; - } - - return finalTransformers; - } - - public class CompositeOptimizer : ExpressionOptimizer - { - public override IExpressionTransformer[] Dependencies { get; } - - internal CompositeOptimizer( IExpressionTransformer[] dependencies ) - { - Dependencies = dependencies; - } - } -} diff --git a/src/Hyperbee.Expressions/Optimizers/StructuralReductionOptimizer.cs b/src/Hyperbee.Expressions/Optimizers/StructuralReductionOptimizer.cs deleted file mode 100644 index 2257e2e..0000000 --- a/src/Hyperbee.Expressions/Optimizers/StructuralReductionOptimizer.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Hyperbee.Expressions.Optimizers.Visitors; - -namespace Hyperbee.Expressions.Optimizers; - -// StructuralReductionOptimizer: Control Flow Simplification -// -// This optimizer manages blocks, labels, loops, and conditionals, removing unreachable -// code and dead branches based on control flow and constant conditions. - -public class StructuralReductionOptimizer : ExpressionOptimizer -{ - public override IExpressionTransformer[] Dependencies => - [ - new StructuralReductionVisitor() - ]; -} - diff --git a/src/Hyperbee.Expressions/Optimizers/SubexpressionCachingOptimizer.cs b/src/Hyperbee.Expressions/Optimizers/SubexpressionCachingOptimizer.cs deleted file mode 100644 index 8d1a28c..0000000 --- a/src/Hyperbee.Expressions/Optimizers/SubexpressionCachingOptimizer.cs +++ /dev/null @@ -1,40 +0,0 @@ -using Hyperbee.Expressions.Optimizers.Visitors; - -namespace Hyperbee.Expressions.Optimizers; - -// ExpressionCachingOptimizer: Expression Subexpression Caching -// -// This optimizer performs subexpression caching to reduce repeated computation in complex expressions. -// By identifying and reusing common subexpressions, it improves execution efficiency, especially in cases -// where identical subexpressions are evaluated multiple times within an expression tree. -// -// Example: -// -// Before Optimization: -// -// .Lambda #Lambda1 { -// 5 * (3 + 2) + 5 * (3 + 2) -// } -// -// After Optimization: -// -// .Lambda #Lambda1 { -// .Block(System.Int32 $cacheVar) { -// $cacheVar = 5 * (3 + 2); -// $cacheVar + $cacheVar -// } -// } -// -// In this example, the optimizer identifies the subexpression `5 * (3 + 2)` as a repeated, cacheable part. -// It creates a variable `$cacheVar` to hold the computed value of `5 * (3 + 2)`, and replaces occurrences -// of this subexpression with `$cacheVar` in the resulting `BlockExpression`. This optimization reduces -// redundant calculations, resulting in a more efficient expression execution. - -public class SubexpressionCachingOptimizer : ExpressionOptimizer -{ - public override IExpressionTransformer[] Dependencies => - [ - new SubexpressionCachingVisitor() - ]; -} - diff --git a/src/Hyperbee.Expressions/Optimizers/ValueBindingOptimizer.cs b/src/Hyperbee.Expressions/Optimizers/ValueBindingOptimizer.cs deleted file mode 100644 index 74a1e63..0000000 --- a/src/Hyperbee.Expressions/Optimizers/ValueBindingOptimizer.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Hyperbee.Expressions.Optimizers.Visitors; - -namespace Hyperbee.Expressions.Optimizers; - -// VariableBindingOptimizer: Variable, Constant, and Member Access Optimization -// -// This optimizer handles optimizations related to variables, constants, and member accesses, -// performing constant folding, variable inlining, and simplifying member access. - -public class ValueBindingOptimizer : ExpressionOptimizer -{ - public override IExpressionTransformer[] Dependencies => - [ - new VariableReducerVisitor(), - new ConstantFoldingVisitor(), - new MemberAccessVisitor() - ]; -} diff --git a/src/Hyperbee.Expressions/Optimizers/Visitors/ConstantFoldingVisitor.cs b/src/Hyperbee.Expressions/Optimizers/Visitors/ConstantFoldingVisitor.cs deleted file mode 100644 index 311f5f5..0000000 --- a/src/Hyperbee.Expressions/Optimizers/Visitors/ConstantFoldingVisitor.cs +++ /dev/null @@ -1,329 +0,0 @@ -using System.Linq.Expressions; -using System.Numerics; - -namespace Hyperbee.Expressions.Optimizers.Visitors; - -// ConstantFoldingVisitor: Arithmetic, Logical, and Conditional Constant Folding -// -// This visitor evaluates constant expressions at compile-time, replacing them with their computed result. -// It simplifies arithmetic, boolean, string, and conditional expressions when all operands are constants. -// -// Examples: -// -// Before: -// .Add(.Constant(3), .Constant(5)) -// -// After: -// .Constant(8) -// -// Before: -// .IfThenElse(.Constant(true), .Constant("True Branch"), .Constant("False Branch")) -// -// After: -// .Constant("True Branch") -// -// Before: -// .Negate(.Constant(5)) -// -// After: -// .Constant(-5) -// -public class ConstantFoldingVisitor : ExpressionVisitor, IExpressionTransformer -{ - public int Priority => PriorityGroup.ConstantEvaluationAndSimplification + 10; - - public Expression Transform( Expression expression ) - { - return Visit( expression ); - } - - protected override Expression VisitBinary( BinaryExpression node ) - { - if ( node.Left is not ConstantExpression leftConst || node.Right is not ConstantExpression rightConst ) - { - return base.VisitBinary( node ); - } - - var folded = FoldConstants( node.NodeType, leftConst, rightConst ); - return folded ?? base.VisitBinary( node ); - } - - protected override Expression VisitUnary( UnaryExpression node ) - { - if ( node.Operand is ConstantExpression constOperand ) - { - return FoldUnaryOperation( node.NodeType, constOperand ) ?? base.VisitUnary( node ); - } - - return base.VisitUnary( node ); - } - - protected override Expression VisitConditional( ConditionalExpression node ) - { - var test = Visit( node.Test ); - - if ( test is not ConstantExpression testConst ) - { - return base.VisitConditional( node ); - } - - var condition = (bool) testConst.Value!; - var result = condition ? node.IfTrue : node.IfFalse; - return Visit( result ); - - } - - private static ConstantExpression FoldUnaryOperation( ExpressionType nodeType, ConstantExpression operand ) - { - // Unary folding: Supports negation, logical NOT, and bitwise NOT for constants. - // - // Examples: - // - // Before: - // .Negate(.Constant(5)) - // - // After: - // .Constant(-5) - // - return nodeType switch - { - ExpressionType.Negate when operand.Value is int intValue => Expression.Constant( -intValue ), - ExpressionType.Not when operand.Value is bool boolValue => Expression.Constant( !boolValue ), - ExpressionType.OnesComplement when operand.Value is int intValue => Expression.Constant( ~intValue ), - _ => null - }; - } - - private static ConstantExpression FoldConstants( ExpressionType nodeType, ConstantExpression leftConst, ConstantExpression rightConst ) - { - if ( !typeof( IConvertible ).IsAssignableFrom( leftConst.Type ) || !typeof( IConvertible ).IsAssignableFrom( rightConst.Type ) ) - return null; - - // Arithmetic and logical operations on constants - if ( IsNumericType( leftConst.Type ) && IsNumericType( rightConst.Type ) ) - { - return FoldNumericOperation( nodeType, leftConst, rightConst ); - } - - if ( leftConst.Type != rightConst.Type ) - { - return null; - } - - var type = leftConst.Type; - - return type switch - { - not null when type == typeof( bool ) => FoldBooleanOperation( nodeType, (bool) leftConst.Value!, (bool) rightConst.Value! ), - not null when type == typeof( string ) => FoldStringOperation( nodeType, (string) leftConst.Value!, (string) rightConst.Value! ), - not null when type == typeof( char ) => FoldCharOperation( nodeType, (char) leftConst.Value!, (char) rightConst.Value! ), - not null when type == typeof( DateTime ) => FoldDateTimeOperation( nodeType, (DateTime) leftConst.Value!, rightConst.Value ), - _ => null - }; - } - - private static ConstantExpression FoldBooleanOperation( ExpressionType nodeType, bool left, bool right ) - { - // Boolean folding: Simplifies operations like AND, OR, and comparisons. - // - // Examples: - // - // Before: - // .And(.Constant(true), .Constant(false)) - // - // After: - // .Constant(false) - // - return nodeType switch - { - ExpressionType.And => Expression.Constant( left && right ), - ExpressionType.Or => Expression.Constant( left || right ), - ExpressionType.Equal => Expression.Constant( left == right ), - ExpressionType.NotEqual => Expression.Constant( left != right ), - _ => null - }; - } - - private static ConstantExpression FoldCharOperation( ExpressionType nodeType, char left, char right ) - { - // Char folding: Supports addition and subtraction on char constants. - // - // Examples: - // - // Before: - // .Add(.Constant('A'), .Constant(1)) - // - // After: - // .Constant('B') - // - int leftInt = left; - int rightInt = right; - - return nodeType switch - { - ExpressionType.Add => Expression.Constant( (char) (leftInt + rightInt) ), - ExpressionType.Subtract => Expression.Constant( (char) (leftInt - rightInt) ), - _ => null - }; - } - - private static ConstantExpression FoldDateTimeOperation( ExpressionType nodeType, DateTime left, object rightValue ) - { - // DateTime folding: Supports addition and subtraction of TimeSpan to DateTime. - // - // Examples: - // - // Before: - // .Add(.Constant(new DateTime(2024, 1, 1)), .Constant(TimeSpan.FromDays(1))) - // - // After: - // .Constant(new DateTime(2024, 1, 2)) - // - if ( rightValue is not TimeSpan rightTimeSpan ) - return null; - - return nodeType switch - { - ExpressionType.Add => Expression.Constant( left + rightTimeSpan ), - ExpressionType.Subtract => Expression.Constant( left - rightTimeSpan ), - _ => null - }; - } - - private static ConstantExpression FoldStringOperation( ExpressionType nodeType, string left, string right ) - { - // String folding: Concatenates strings for `Add` operation. - // - // Examples: - // - // Before: - // .Add(.Constant("Hello, "), .Constant("World!")) - // - // After: - // .Constant("Hello, World!") - // - return nodeType != ExpressionType.Add ? null : Expression.Constant( left + right ); - } - - private static ConstantExpression FoldNumericOperation( ExpressionType nodeType, ConstantExpression leftConst, ConstantExpression rightConst ) - { - Type commonType; - object leftValue; - object rightValue; - - if ( leftConst.Type == rightConst.Type ) - { - commonType = leftConst.Type; - leftValue = leftConst.Value!; - rightValue = rightConst.Value!; - } - else - { - commonType = GetNumericType( leftConst.Type, rightConst.Type ); - - if ( commonType == null ) - throw new InvalidOperationException( "Incompatible numeric types." ); - - leftValue = Convert.ChangeType( leftConst.Value, commonType )!; - rightValue = Convert.ChangeType( rightConst.Value, commonType )!; - } - - return commonType switch - { - not null when commonType == typeof( byte ) => ApplyOperation( nodeType, (byte) leftValue, (byte) rightValue ), - not null when commonType == typeof( sbyte ) => ApplyOperation( nodeType, (sbyte) leftValue, (sbyte) rightValue ), - not null when commonType == typeof( short ) => ApplyOperation( nodeType, (short) leftValue, (short) rightValue ), - not null when commonType == typeof( ushort ) => ApplyOperation( nodeType, (ushort) leftValue, (ushort) rightValue ), - not null when commonType == typeof( int ) => ApplyOperation( nodeType, (int) leftValue, (int) rightValue ), - not null when commonType == typeof( uint ) => ApplyOperation( nodeType, (uint) leftValue, (uint) rightValue ), - not null when commonType == typeof( long ) => ApplyOperation( nodeType, (long) leftValue, (long) rightValue ), - not null when commonType == typeof( ulong ) => ApplyOperation( nodeType, (ulong) leftValue, (ulong) rightValue ), - not null when commonType == typeof( float ) => ApplyOperation( nodeType, (float) leftValue, (float) rightValue ), - not null when commonType == typeof( double ) => ApplyOperation( nodeType, (double) leftValue, (double) rightValue ), - not null when commonType == typeof( decimal ) => ApplyOperation( nodeType, (decimal) leftValue, (decimal) rightValue ), - _ => throw new InvalidOperationException( "Unsupported type for promoted operation." ) - }; - - static ConstantExpression ApplyOperation( ExpressionType nodeType, T left, T right ) where T : INumber - { - var constant = nodeType switch - { - ExpressionType.Add => left + right, - ExpressionType.Subtract => left - right, - ExpressionType.Multiply => left * right, - ExpressionType.Divide => Divide( left, right ), - _ => throw new InvalidOperationException( $"Unsupported operation {nodeType} for type {typeof( T )}." ) - }; - - return Expression.Constant( constant ); - - static T Divide( T left, T right ) - { - if ( right == T.Zero ) - throw new DivideByZeroException(); - - return left / right; - } - } - } - - private static bool IsNumericType( Type type ) - { - if ( type == null ) - return false; - - return Type.GetTypeCode( type ) switch - { - TypeCode.Byte or - TypeCode.SByte or - TypeCode.Int16 or - TypeCode.UInt16 or - TypeCode.Int32 or - TypeCode.UInt32 or - TypeCode.Int64 or - TypeCode.UInt64 or - TypeCode.Single or - TypeCode.Double or - TypeCode.Decimal => true, - _ => false - }; - } - - private static readonly TypeCode[] TypeCodePromotionOrder = - [ - TypeCode.Byte, - TypeCode.SByte, - TypeCode.Int16, - TypeCode.UInt16, - TypeCode.Int32, - TypeCode.UInt32, - TypeCode.Int64, - TypeCode.UInt64, - TypeCode.Single, - TypeCode.Double, - TypeCode.Decimal - ]; - - private static Type GetNumericType( Type leftType, Type rightType ) - { - var leftCode = Type.GetTypeCode( leftType ); - var rightCode = Type.GetTypeCode( rightType ); - - if ( leftCode == rightCode ) - return leftType; - - var leftIndex = Array.IndexOf( TypeCodePromotionOrder, leftCode ); - var rightIndex = Array.IndexOf( TypeCodePromotionOrder, rightCode ); - - if ( leftIndex < 0 || rightIndex < 0 ) - return null; - - var commonTypeCode = TypeCodePromotionOrder[Math.Max( leftIndex, rightIndex )]; - - return Type.GetType( "System." + commonTypeCode ); - } -} - - - - diff --git a/src/Hyperbee.Expressions/Optimizers/Visitors/InliningVisitor.cs b/src/Hyperbee.Expressions/Optimizers/Visitors/InliningVisitor.cs deleted file mode 100644 index 3f26f56..0000000 --- a/src/Hyperbee.Expressions/Optimizers/Visitors/InliningVisitor.cs +++ /dev/null @@ -1,117 +0,0 @@ -using System.Linq.Expressions; - -namespace Hyperbee.Expressions.Optimizers.Visitors; - -// InliningOptimizer: Inlining Functions, Constants, and Conditional Values -// -// This optimizer inlines constants and simplifies expressions by propagating known values. -// It focuses on inlining lambda expressions, boolean short-circuiting, and simplifying conditional -// expressions based on constant values. -// -// Before: -// .Invoke(lambda, .Constant(5)) -// -// After: -// .LambdaExpression( -// .Parameter(x), -// .Add(.Constant(5), .Constant(5)) -// ) -// -// Before: -// .IfThenElse(.Constant(true), .Constant("True Branch"), .Constant("False Branch")) -// -// After: -// .Constant("True Branch") -// -public class InliningVisitor : ExpressionVisitor, IExpressionTransformer -{ - private ConstantFoldingVisitor ConstantFoldingVisitor { get; } = new(); - - public int Priority => PriorityGroup.ControlFlowAndVariableSimplification + 30; - - public Expression Transform( Expression expression ) - { - return Visit( expression ); - } - - protected override Expression VisitBinary( BinaryExpression node ) - { - // Visit left and right nodes to apply inlining or other optimizations - var left = Visit( node.Left ); - var right = Visit( node.Right ); - - // Handle short-circuiting for `AndAlso` and `OrElse` - if ( node.NodeType == ExpressionType.AndAlso || node.NodeType == ExpressionType.OrElse ) - { - if ( left is ConstantExpression leftConst && leftConst.Value is bool leftBool ) - { - // Short-circuit based on left value - return (node.NodeType, leftBool) switch - { - (ExpressionType.AndAlso, true ) => ConstantFoldingVisitor.Visit( right ), - (ExpressionType.AndAlso, false ) => Expression.Constant( false ), - (ExpressionType.OrElse, true ) => Expression.Constant( true ), - (ExpressionType.OrElse, false ) => ConstantFoldingVisitor.Visit( right ), - _ => node.Update( left, node.Conversion, right ) - }; - } - } - - // If both sides are constants, and it's not a logical short-circuit case, try folding directly - if ( left is ConstantExpression && right is ConstantExpression ) - { - return ConstantFoldingVisitor.Visit( Expression.MakeBinary( node.NodeType, left, right ) ); - } - - return node.Update( left, node.Conversion, right ); - } - - protected override Expression VisitInvocation( InvocationExpression node ) - { - if ( node.Expression is LambdaExpression lambda ) - { - // Inline lambda expressions by replacing parameters with arguments - var argumentMap = lambda.Parameters - .Zip( node.Arguments, ( parameter, argument ) => (parameter, argument) ) - .ToDictionary( pair => pair.parameter, pair => pair.argument ); - - var inlinedBody = new ParameterReplacer( argumentMap ).Visit( lambda.Body ); - return ConstantFoldingVisitor.Visit( inlinedBody ); // Apply folding after inlining - } - - return base.VisitInvocation( node ); - } - - protected override Expression VisitConditional( ConditionalExpression node ) - { - // Evaluate conditional expressions with constant test conditions - var test = Visit( node.Test ); - var ifTrue = Visit( node.IfTrue ); - var ifFalse = Visit( node.IfFalse ); - - if ( test is ConstantExpression constTest && constTest.Value is bool condition ) - { - var result = condition ? ifTrue : ifFalse; - return ConstantFoldingVisitor.Visit( result ); // Apply folding after resolving the condition - } - - return node.Update( test, ifTrue, ifFalse ); - } - - // Helper class for inlining: replaces parameters with arguments - private class ParameterReplacer : ExpressionVisitor - { - private readonly Dictionary _replacements; - - public ParameterReplacer( Dictionary replacements ) - { - _replacements = replacements; - } - - protected override Expression VisitParameter( ParameterExpression node ) - { - return _replacements.TryGetValue( node, out var replacement ) ? replacement : base.VisitParameter( node ); - } - } -} - diff --git a/src/Hyperbee.Expressions/Optimizers/Visitors/MemberAccessVisitor.cs b/src/Hyperbee.Expressions/Optimizers/Visitors/MemberAccessVisitor.cs deleted file mode 100644 index 12cf05b..0000000 --- a/src/Hyperbee.Expressions/Optimizers/Visitors/MemberAccessVisitor.cs +++ /dev/null @@ -1,73 +0,0 @@ -using System.Linq.Expressions; -using System.Reflection; - -namespace Hyperbee.Expressions.Optimizers.Visitors; - -// MemberAccessVisitor: Constant Field and Property Access Simplification -// -// This visitor simplifies member access by precomputing values for constant fields or -// properties. When a field or property is accessed through a constant object, it evaluates -// the expression and replaces it with the value. -// -// Before: -// .MemberAccess(.Constant(obj), Property) -// -// After: -// .Constant(value) -// -public class MemberAccessVisitor : ExpressionVisitor, IExpressionTransformer -{ - public int Priority => PriorityGroup.ConstantEvaluationAndSimplification + 20; - - public Expression Transform( Expression expression ) - { - return Visit( expression ); - } - - protected override Expression VisitMember( MemberExpression node ) - { - node = (MemberExpression) base.VisitMember( node ); - - if ( node.Expression is not ConstantExpression constExpr ) - { - return node; - } - - switch ( node.Member ) - { - case FieldInfo fieldInfo: - var fieldValue = fieldInfo.GetValue( constExpr.Value ); - return Expression.Constant( fieldValue, node.Type ); - - case PropertyInfo propertyInfo when propertyInfo.CanRead: - var propertyValue = propertyInfo.GetMethod!.IsStatic - ? propertyInfo.GetValue( null ) - : propertyInfo.GetValue( constExpr.Value ); - return Expression.Constant( propertyValue, node.Type ); - - default: - return node; - } - } - - protected override Expression VisitBinary( BinaryExpression node ) - { - if ( node.NodeType != ExpressionType.Coalesce || node.Left is not ConstantExpression leftConst || leftConst.Value != null ) - { - return base.VisitBinary( node ); - } - - return Visit( node.Right ); - } - - protected override Expression VisitIndex( IndexExpression node ) - { - if ( node.Object is not ConstantExpression constantArray || constantArray.Value is not Array array || node.Arguments[0] is not ConstantExpression indexExpr ) - { - return base.VisitIndex( node ); - } - - var index = (int) indexExpr.Value!; - return Expression.Constant( array.GetValue( index ), node.Type ); - } -} diff --git a/src/Hyperbee.Expressions/Optimizers/Visitors/OperatorReductionVisitor.cs b/src/Hyperbee.Expressions/Optimizers/Visitors/OperatorReductionVisitor.cs deleted file mode 100644 index 5b57370..0000000 --- a/src/Hyperbee.Expressions/Optimizers/Visitors/OperatorReductionVisitor.cs +++ /dev/null @@ -1,176 +0,0 @@ -using System.Linq.Expressions; - -namespace Hyperbee.Expressions.Optimizers.Visitors; - -// OperatorReductionVisitor: Arithmetic and Logical Operator Reduction -// -// This visitor eliminates trivial arithmetic expressions (like adding zero, multiplying by one), -// simplifies logical identities (like x && true or x || false), and flattens nested expressions -// where applicable. -// -// Examples: -// -// Before: -// .Add(.Parameter(x), .Constant(0)) -// -// After: -// .Parameter(x) -// -// Before: -// .Multiply(.Parameter(x), .Constant(1)) -// -// After: -// .Parameter(x) -// -// Before: -// .Add(.Add(.Parameter(x), .Parameter(y)), .Parameter(z)) -// -// After: -// .Add(.Parameter(x), .Parameter(y), .Parameter(z)) -// -public class OperatorReductionVisitor : ExpressionVisitor, IExpressionTransformer -{ - public int Priority => PriorityGroup.ExpressionReductionAndCaching + 60; - - public Expression Transform( Expression expression ) - { - return Visit( expression ); - } - - protected override Expression VisitBinary( BinaryExpression node ) - { - // Recursively visit left and right nodes to ensure optimizations - // are applied in subexpressions first - - var left = Visit( node.Left ); - var right = Visit( node.Right ); - - // Apply trivial arithmetic and logical simplifications - - switch ( node.NodeType ) - { - // Arithmetic simplifications - case ExpressionType.Add: - case ExpressionType.Subtract: - { - if ( right is ConstantExpression rightConst && rightConst.Value is int rightVal ) - { - // x + 0 or x - 0 - if ( rightVal == 0 ) - return left; - } - - if ( node.NodeType == ExpressionType.Add && left is ConstantExpression leftConst && leftConst.Value is int leftValue ) - { - // 0 + x - if ( leftValue == 0 ) - return right; - } - - break; - } - - case ExpressionType.Multiply: - { - if ( right is ConstantExpression rightConst && rightConst.Value is int rightValue ) - { - // x * 1 - if ( rightValue == 1 ) - return left; - - // x * 0 - if ( rightValue == 0 ) - return Expression.Constant( 0 ); - } - - if ( left is ConstantExpression leftConst && leftConst.Value is int leftValue ) - { - // 1 * x - if ( leftValue == 1 ) - return right; - - // 0 * x - if ( leftValue == 0 ) - return Expression.Constant( 0 ); - } - - break; - } - - // Logical short-circuiting for `&& true`, `|| false`, etc. - case ExpressionType.AndAlso: - { - // x && true or true && x - if ( right is ConstantExpression rightConst && rightConst.Value is bool rightValue && rightValue ) - return left; - - if ( left is ConstantExpression leftConst && leftConst.Value is bool leftValue && leftValue ) - return right; - - // false && x or x && false - if ( left is ConstantExpression lConstAndFalse && lConstAndFalse.Value is bool leftValAndFalse && !leftValAndFalse || - right is ConstantExpression rConstAndFalse && rConstAndFalse.Value is bool rightValAndFalse && !rightValAndFalse ) - return Expression.Constant( false ); - - break; - } - - case ExpressionType.OrElse: - { - // x || false or false || x - if ( right is ConstantExpression rightConst && rightConst.Value is bool rightValue && !rightValue ) - return left; - - if ( left is ConstantExpression leftConst && leftConst.Value is bool leftValue && !leftValue ) - return right; - - // true || x or x || true - if ( left is ConstantExpression lConstOrTrue && lConstOrTrue.Value is bool leftValOrTrue && leftValOrTrue || - right is ConstantExpression rConstOrTrue && rConstOrTrue.Value is bool rightValOrTrue && rightValOrTrue ) - return Expression.Constant( true ); - - break; - } - } - - if ( node.NodeType != ExpressionType.Add && node.NodeType != ExpressionType.Multiply ) - { - return node.Update( left, node.Conversion, right ); - } - - // Handle nested expression flattening specifically for `Add` and `Multiply` - - // Collect terms for the node and recursively flatten if necessary - var allTerms = FlattenBinaryExpression( node, node.NodeType ).ToList(); - - // Aggregate constants in the list (e.g., sum all constants for `Add`) - var constants = allTerms.OfType().ToList(); - var otherTerms = allTerms.Except( constants ).ToList(); - - if ( constants.Count <= 0 ) - { - return otherTerms.Aggregate( ( acc, term ) => Expression.MakeBinary( node.NodeType, acc, term ) ); - } - - // Sum or multiply the constant values based on node type - var constantResult = node.NodeType switch - { - ExpressionType.Add => constants.Sum( c => (int) c.Value! ), - _ => constants.Aggregate( 1, ( acc, c ) => acc * (int) c.Value! ) - }; - - otherTerms.Insert( 0, Expression.Constant( constantResult ) ); - - return otherTerms.Aggregate( ( acc, term ) => Expression.MakeBinary( node.NodeType, acc, term ) ); - } - - private static IEnumerable FlattenBinaryExpression( Expression expr, ExpressionType type ) - { - if ( expr is BinaryExpression binary && binary.NodeType == type ) - { - return FlattenBinaryExpression( binary.Left, type ).Concat( FlattenBinaryExpression( binary.Right, type ) ); - } - - return [expr]; - } -} diff --git a/src/Hyperbee.Expressions/Optimizers/Visitors/StructuralReductionVisitor.cs b/src/Hyperbee.Expressions/Optimizers/Visitors/StructuralReductionVisitor.cs deleted file mode 100644 index f970b47..0000000 --- a/src/Hyperbee.Expressions/Optimizers/Visitors/StructuralReductionVisitor.cs +++ /dev/null @@ -1,127 +0,0 @@ -using System.Linq.Expressions; - -namespace Hyperbee.Expressions.Optimizers.Visitors; - -public class StructuralReductionVisitor : ExpressionVisitor, IExpressionTransformer -{ - public int Priority => PriorityGroup.StructuralReductionAndConsolidation + 50; - - public Expression Transform( Expression expression ) - { - return Visit( expression ); - } - - // Visit BlockExpression, handling block flattening and reduction - protected override Expression VisitBlock( BlockExpression node ) - { - var variables = new List(); - var expressions = new List(); - - foreach ( var expr in node.Expressions ) - { - var visitedExpr = Visit( expr ); - - // Skip over no-op expressions and empty blocks - if ( IsNoOpExpression( visitedExpr ) ) - continue; - - // Flatten nested blocks - if ( visitedExpr is BlockExpression nestedBlock ) - { - variables.AddRange( nestedBlock.Variables ); - expressions.AddRange( nestedBlock.Expressions ); - } - else - { - expressions.Add( visitedExpr ); - } - } - - // Remove single-use blocks - return expressions.Count == 1 ? expressions[0] : Expression.Block( variables, expressions ); - } - - // Visit GotoExpression, eliminating redundant Goto expressions - protected override Expression VisitGoto( GotoExpression node ) - { - if ( IsNoOpExpression( node.Value ) ) - { - return Expression.Empty(); // Skip no-op Gotos - } - - return base.VisitGoto( node ); - } - - // Visit LoopExpression, optimizing based on loop content - protected override Expression VisitLoop( LoopExpression node ) - { - var visitedBody = Visit( node.Body ); - - if ( IsFalsyConstant( visitedBody ) || IsNoOpExpression( visitedBody ) || IsEffectivelyInfiniteNoOpLoop( visitedBody ) ) - { - return node.Type == typeof( void ) ? Expression.Empty() : Expression.Default( node.Type ); - } - - return ReferenceEquals( node.Body, visitedBody ) - ? node - : Expression.Loop( visitedBody, node.BreakLabel, node.ContinueLabel ); - } - - // Visit ConditionalExpression, simplifying based on constant conditions - protected override Expression VisitConditional( ConditionalExpression node ) - { - var test = Visit( node.Test ); - - if ( test is ConstantExpression constantTest && constantTest.Value is bool boolTest ) - { - // Replace based on constant true or false - return boolTest ? Visit( node.IfTrue ) : Visit( node.IfFalse ); - } - - return node.Update( test, Visit( node.IfTrue ), Visit( node.IfFalse ) ); - } - - // Visit TryCatchExpression, simplifying empty or no-op TryCatch blocks - protected override Expression VisitTry( TryExpression node ) - { - var body = Visit( node.Body ); - - // Remove Try if catches are no-ops or finally has no effect - if ( node.Handlers.All( handler => IsNoOpExpression( handler.Body ) ) && (node.Finally == null || IsNoOpExpression( node.Finally )) ) - { - return body; - } - - return node.Update( body, node.Handlers, node.Finally, node.Fault ); - } - - // Helper methods for loop reduction and simplification - private static bool IsFalsyConstant( Expression expression ) - { - return expression is ConstantExpression constant && constant.Value switch - { - null => true, - bool boolValue => !boolValue, - int intValue => intValue == 0, - double doubleValue => doubleValue == 0.0, - float floatValue => floatValue == 0.0f, - _ => false - }; - } - - private static bool IsNoOpExpression( Expression expression ) - { - return expression is DefaultExpression || - (expression is BlockExpression block && block.Expressions.All( IsNoOpExpression )); - } - - private static bool IsEffectivelyInfiniteNoOpLoop( Expression expression ) - { - return expression is ConstantExpression && !HasControlFlowOrSideEffects( expression ); - } - - private static bool HasControlFlowOrSideEffects( Expression expression ) - { - return expression is not ConstantExpression; - } -} diff --git a/src/Hyperbee.Expressions/Optimizers/Visitors/SubexpressionCachingVisitor.cs b/src/Hyperbee.Expressions/Optimizers/Visitors/SubexpressionCachingVisitor.cs deleted file mode 100644 index 74b1a49..0000000 --- a/src/Hyperbee.Expressions/Optimizers/Visitors/SubexpressionCachingVisitor.cs +++ /dev/null @@ -1,181 +0,0 @@ -using System.Linq.Expressions; -using Hyperbee.Expressions.Visitors; - -namespace Hyperbee.Expressions.Optimizers.Visitors; - -// SubexpressionCachingVisitor: Subexpression Caching -// -// This optimizer performs subexpression caching to reduce repeated computation in complex expressions. -// By identifying and reusing common subexpressions, it improves execution efficiency, especially in cases -// where identical subexpressions are evaluated multiple times within an expression tree. -// -// This optimizer works in multiple phases: -// * Fingerprinting: Each expression is traversed to create a unique "fingerprint" for subexpressions. -// * Deferred Caching: Subexpressions deemed "complex enough" are queued for caching. This ensures -// that only the necessary subexpressions are cached and that simple expressions remain unchanged. -// * Block Wrapping: Cached expressions are assigned to variables in a `BlockExpression`, which groups -// these assignments and ensures variables are reused wherever possible. -// -// Example: -// -// Before Optimization: -// -// .Lambda #Lambda1 { -// 5 * (3 + 2) + 5 * (3 + 2) -// } -// -// After Optimization: -// -// .Lambda #Lambda1 { -// .Block(System.Int32 $cacheVar) { -// $cacheVar = 5 * (3 + 2); -// $cacheVar + $cacheVar -// } -// } -// -// In this example, the optimizer identifies the subexpression `5 * (3 + 2)` as a repeated, cacheable part. -// It creates a variable `$cacheVar` to hold the computed value of `5 * (3 + 2)`, and replaces occurrences -// of this subexpression with `$cacheVar` in the resulting `BlockExpression`. This optimization reduces -// redundant calculations, resulting in a more efficient expression execution. - -public class SubexpressionCachingVisitor : ExpressionVisitor, IExpressionTransformer -{ - private readonly Dictionary _fingerprintCache = new( new ByteArrayComparer() ); - private readonly Dictionary _cacheVariables = new( new ByteArrayComparer() ); - private readonly Queue<(Expression Original, ParameterExpression Variable)> _deferredReplacements = new(); - - private readonly ExpressionFingerprinter _fingerprinter = new(); - - public int Priority => PriorityGroup.ExpressionReductionAndCaching + 70; - - public Expression Transform( Expression expression ) - { - _deferredReplacements.Clear(); - var visitedExpression = Visit( expression ); - - if ( _deferredReplacements.Count == 0 ) - { - return visitedExpression; - } - - var blockExpressions = new List(); - var variables = new List(); - - while ( _deferredReplacements.Count > 0 ) - { - var (original, cacheVariable) = _deferredReplacements.Dequeue(); - - if ( variables.Contains( cacheVariable ) ) - { - continue; - } - - variables.Add( cacheVariable ); - blockExpressions.Add( Expression.Assign( cacheVariable, original ) ); - } - - if ( visitedExpression is LambdaExpression lambda ) - { - blockExpressions.Add( lambda.Body ); - var lambdaResult = Expression.Lambda( lambda.Type, Expression.Block( variables, blockExpressions ), lambda.Parameters ); - return lambdaResult; - } - - blockExpressions.Add( visitedExpression ); - return Expression.Block( variables, blockExpressions ); - } - - public override Expression Visit( Expression node ) - { - var visited = base.Visit( node ); - return ResolveExpression( node, visited ); - } - - private Expression ResolveExpression( Expression original, Expression visitedNode ) - { - if ( !ReferenceEquals( original, visitedNode ) || !IsComplexEnoughToCache( visitedNode ) ) - { - return visitedNode; - } - - var fingerprint = _fingerprinter.ComputeFingerprint( visitedNode ); - - if ( !_fingerprintCache.TryGetValue( fingerprint, out var cachedNode ) ) - { - _fingerprintCache[fingerprint] = visitedNode; - cachedNode = visitedNode; - } - - if ( _cacheVariables.TryGetValue( fingerprint, out var cacheVariable ) ) - { - return cacheVariable; - } - - cacheVariable = Expression.Variable( cachedNode.Type, "cacheVar" ); - _cacheVariables[fingerprint] = cacheVariable; - _deferredReplacements.Enqueue( (visitedNode, cacheVariable) ); - - return cacheVariable; - } - - private static bool IsComplexEnoughToCache( Expression node ) - { - return node switch - { - BinaryExpression binary => - (binary.Left is not ConstantExpression || binary.Right is not ConstantExpression) && - (IsComplexEnoughToCache( binary.Left ) || - IsComplexEnoughToCache( binary.Right ) || - binary.NodeType is - ExpressionType.Add or - ExpressionType.Multiply or - ExpressionType.Divide or - ExpressionType.Subtract - ), - - MethodCallExpression or MemberExpression or InvocationExpression => true, - UnaryExpression unary => unary.Operand is not ConstantExpression && IsComplexEnoughToCache( unary.Operand ), - BlockExpression block => block.Expressions.Count > 1, - ConditionalExpression or NewExpression or NewArrayExpression => true, - LambdaExpression lambda => lambda.Parameters.Count > 0 || IsComplexEnoughToCache( lambda.Body ), - MemberInitExpression or ListInitExpression or IndexExpression => true, - ConstantExpression => false, - _ => false - }; - } - - private class ByteArrayComparer : IEqualityComparer - { - public bool Equals( byte[] x, byte[] y ) - { - if ( ReferenceEquals( x, y ) ) - return true; - - if ( x == null || y == null ) - return false; - - if ( x.Length != y.Length ) - return false; - - for ( var i = 0; i < x.Length; i++ ) - { - if ( x[i] != y[i] ) - return false; - } - - return true; - } - - public int GetHashCode( byte[] bytes ) - { - var hash = 17; - - foreach ( var x in bytes ) - { - hash = hash * 31 + x; - } - - return hash; - } - } -} diff --git a/src/Hyperbee.Expressions/Optimizers/Visitors/VariableReducerVisitor.cs b/src/Hyperbee.Expressions/Optimizers/Visitors/VariableReducerVisitor.cs deleted file mode 100644 index cf3727a..0000000 --- a/src/Hyperbee.Expressions/Optimizers/Visitors/VariableReducerVisitor.cs +++ /dev/null @@ -1,126 +0,0 @@ -using System.Linq.Expressions; - -namespace Hyperbee.Expressions.Optimizers.Visitors; - -// VariableReducerVisitor: Inlining and Memory Optimization -// -// This visitor combines inlining and memory optimization by inlining single-use variables, removing unused -// variables, and reusing parameters where possible. This helps in reducing both memory usage and -// unnecessary assignments, making the code more efficient. -// -// Example Transformations: -// Before: -// .Block( -// .Assign(.Parameter(x), .Constant(5)), -// .Add(.Parameter(x), .Parameter(x)) -// ) -// -// After: -// .Add(.Constant(5), .Constant(5)) -// -// Before: -// .Block( -// .Assign(.Parameter(temp), .Constant(42)), -// .Parameter(temp) -// ) -// -// After: -// .Constant(42) - -public class VariableReducerVisitor : ExpressionVisitor, IExpressionTransformer -{ - private readonly Dictionary _replacements = new(); - private readonly Dictionary _reusedParameters = new(); - private readonly HashSet _uniqueVariables = []; - - public int Priority => PriorityGroup.ControlFlowAndVariableSimplification + 40; - - public Expression Transform( Expression expression ) - { - _replacements.Clear(); - _reusedParameters.Clear(); - _uniqueVariables.Clear(); - - return Visit( expression ); - } - - protected override Expression VisitBlock( BlockExpression node ) - { - //var variables = new List(); - var expressions = new List(); - var variableUsageCount = new Dictionary(); - - // Count usages and track replacements for inlining - foreach ( var expr in node.Expressions ) - { - if ( expr is BinaryExpression binaryExpr && binaryExpr.NodeType == ExpressionType.Assign && - binaryExpr.Left is ParameterExpression variable ) - { - variableUsageCount.TryAdd( variable, 0 ); - variableUsageCount[variable]++; - _replacements[variable] = binaryExpr.Right; - } - } - - // Inline or retain variables based on usage - foreach ( var expr in node.Expressions ) - { - switch ( expr ) - { - case BinaryExpression binaryExpr when binaryExpr.NodeType == ExpressionType.Assign && - binaryExpr.Left is ParameterExpression variable: - if ( variableUsageCount[variable] == 1 && _replacements[variable] is ConstantExpression ) - { - // Inline single-use constant assignments - expressions.Add( Visit( _replacements[variable] ) ); - } - else - { - _uniqueVariables.Add( variable ); - //variables.Add(variable); - expressions.Add( Visit( expr ) ); - } - break; - default: - expressions.Add( Visit( expr ) ); - break; - } - } - - // Remove unused variables and consolidate variable declarations - var finalVariables = new List( _uniqueVariables ); - - return finalVariables.Count == 0 ? expressions.Last() : Expression.Block( finalVariables, expressions ); - } - - protected override Expression VisitParameter( ParameterExpression node ) - { - // Reuse parameters if already processed - if ( _reusedParameters.TryGetValue( node, out var reusedParam ) ) - { - return reusedParam; - } - - if ( _replacements.TryGetValue( node, out var replacement ) ) - { - return Visit( replacement ); - } - - _reusedParameters[node] = node; - return base.VisitParameter( node ); - } - - protected override Expression VisitLoop( LoopExpression node ) - { - var visitedBody = Visit( node.Body ); - - if ( ReferenceEquals( visitedBody, node.Body ) ) - return node; - - if ( visitedBody is ConstantExpression constantBody ) - return constantBody; - - return visitedBody; - } -} - diff --git a/src/Hyperbee.Expressions/Transformation/NodeExpression.cs b/src/Hyperbee.Expressions/Transformation/NodeExpression.cs index d5b38b3..dfd62ec 100644 --- a/src/Hyperbee.Expressions/Transformation/NodeExpression.cs +++ b/src/Hyperbee.Expressions/Transformation/NodeExpression.cs @@ -39,33 +39,19 @@ public NodeExpression( int stateId, int scopeId, int groupId ) public bool IsNoOp => Expressions.Count == 0 && ResultVariable == null; - protected override Expression VisitChildren( ExpressionVisitor visitor ) - { - return Update( - Expressions.Select( visitor.Visit ).ToList(), - visitor.Visit( ResultValue ), - visitor.Visit( ResultVariable ), - (Transition) visitor.Visit( Transition ) - ); - } - - private Expression Update( List expressions, Expression resultValue, Expression resultVariable, Transition transition ) - { - Expressions = expressions; - ResultValue = resultValue; - ResultVariable = resultVariable; - Transition = transition; - - return this; - } - public override Expression Reduce() { if ( StateMachineSource == null ) throw new InvalidOperationException( $"Reduce requires an {nameof( Transformation.StateMachineSource )} instance." ); - Transition.Parent = this; - return Transition.Reduce(); + var expressions = new List( 8 ) { Label( NodeLabel ) }; + expressions.AddRange( Expressions ); + + Transition.AddExpressions( this, expressions ); + + return expressions.Count == 1 + ? expressions[0] + : Block( expressions ); } internal static List Merge( List nodes ) //BF ME - not sure if this is the right place or not diff --git a/src/Hyperbee.Expressions/Transformation/StateMachineBuilder.cs b/src/Hyperbee.Expressions/Transformation/StateMachineBuilder.cs index edc9068..29b9b8e 100644 --- a/src/Hyperbee.Expressions/Transformation/StateMachineBuilder.cs +++ b/src/Hyperbee.Expressions/Transformation/StateMachineBuilder.cs @@ -331,16 +331,6 @@ FieldInfo[] fields var exitLabel = Label( "ST_EXIT" ); - // Optimize source nodes - - StateMachineOptimizer.Optimize( source ); - - // Variable Hoisting - - HoistVariables( source, fields, stateMachine ); - - // Assign state-machine source to nodes - var stateMachineSource = new StateMachineSource( stateMachine, exitLabel, @@ -350,14 +340,9 @@ FieldInfo[] fields source.ReturnValue ); - foreach ( var node in source.Nodes ) - { - node.StateMachineSource = stateMachineSource; // required for node reducers - } - - // Add the state-nodes + // Create the body expressions - var bodyExpressions = CreateBody( stateField, source ); + var bodyExpressions = CreateBody( fields, source, stateMachineSource ); // Add the final builder result assignment @@ -374,70 +359,86 @@ FieldInfo[] fields ) ] ); - // Create a try-catch block to handle exceptions + // Create final lambda with try-catch block var exceptionParam = Parameter( typeof( Exception ), "ex" ); - var tryCatchBlock = TryCatch( - Block( - typeof( void ), - source.ReturnValue != null - ? [source.ReturnValue] - : [], - bodyExpressions - ), - Catch( - exceptionParam, - Block( - Assign( stateField, Constant( -2 ) ), - Call( - builderField, - nameof( AsyncTaskMethodBuilder.SetException ), - null, - exceptionParam - ) - ) - ) - ); - - // Create the final lambda expression - return Lambda( typeof( MoveNextDelegate<> ).MakeGenericType( stateMachineType ), Block( - tryCatchBlock, + TryCatch( + Block( + typeof( void ), + source.ReturnValue != null + ? [source.ReturnValue] + : [], + bodyExpressions + ), + Catch( + exceptionParam, + Block( + Assign( stateField, Constant( -2 ) ), + Call( + builderField, + nameof( AsyncTaskMethodBuilder.SetException ), + null, + exceptionParam + ) + ) + ) + ), Label( exitLabel ) ), stateMachine ); } - private static List CreateBody( MemberExpression stateField, LoweringResult source ) + private static List CreateBody( FieldInfo[] fields, LoweringResult source, StateMachineSource stateMachineSource ) { + // Optimize source nodes + + StateMachineOptimizer.Optimize( source ); + + // Assign state-machine source to nodes (required for node reducers) + + foreach ( var node in source.Nodes ) + { + node.StateMachineSource = stateMachineSource; + } + + // Create the body expressions + var firstScope = source.Scopes.First(); var jumpTable = JumpTableBuilder.Build( firstScope, source.Scopes, - stateField + stateMachineSource.StateIdField ); - return [jumpTable, Block( NodeExpression.Merge( firstScope.Nodes ) )]; //BF ME + var bodyBlock = Block( NodeExpression.Merge( firstScope.Nodes ) ); + + // hoist variables + + var bodyExpressions = HoistVariables( + [jumpTable, bodyBlock], + fields, + stateMachineSource.StateMachine + ); + + return bodyExpressions; } - [MethodImpl( MethodImplOptions.AggressiveInlining )] - private static void HoistVariables( LoweringResult source, FieldInfo[] fields, ParameterExpression stateMachine ) + private static List HoistVariables( List expressions, FieldInfo[] fields, ParameterExpression stateMachine ) { var fieldMembers = fields .Select( field => Field( stateMachine, field ) ) .ToDictionary( x => x.Member.Name ); var hoistingVisitor = new HoistingVisitor( fieldMembers ); + var hoisted = expressions.Select( hoistingVisitor.Visit ).ToList(); - foreach ( var node in source.Nodes ) - { - hoistingVisitor.Visit( node ); - } + return hoisted; } private sealed class HoistingVisitor( IDictionary memberExpressions ) : ExpressionVisitor diff --git a/src/Hyperbee.Expressions/Transformation/Transitions/AwaitResultTransition.cs b/src/Hyperbee.Expressions/Transformation/Transitions/AwaitResultTransition.cs index 7753ddb..d5b4f7f 100644 --- a/src/Hyperbee.Expressions/Transformation/Transitions/AwaitResultTransition.cs +++ b/src/Hyperbee.Expressions/Transformation/Transitions/AwaitResultTransition.cs @@ -1,4 +1,5 @@ using System.Linq.Expressions; +using static System.Linq.Expressions.Expression; namespace Hyperbee.Expressions.Transformation.Transitions; @@ -11,33 +12,12 @@ internal class AwaitResultTransition : Transition internal override NodeExpression FallThroughNode => TargetNode; - protected override Expression VisitChildren( ExpressionVisitor visitor ) + protected override void SetBody( List expressions, NodeExpression parent ) { - return Update( - visitor.Visit( AwaiterVariable ), - visitor.Visit( ResultVariable ) - ); - } - - internal AwaitResultTransition Update( Expression awaiterVariable, Expression resultVariable ) - { - if ( awaiterVariable == AwaiterVariable && resultVariable == ResultVariable ) - return this; - - return new AwaitResultTransition - { - AwaiterVariable = awaiterVariable, - ResultVariable = resultVariable, - TargetNode = TargetNode, - AwaitBinder = AwaitBinder - }; - } - - protected override List GetBody() - { - return GetExpressions(); + expressions.AddRange( Expressions() ); + return; - List GetExpressions() + List Expressions() { var getResultMethod = AwaitBinder.GetResultMethod; @@ -47,20 +27,49 @@ List GetExpressions() if ( ResultVariable == null ) { - var transition = GotoOrFallThrough( Parent.StateOrder, TargetNode ); + var transition = GotoOrFallThrough( parent.StateOrder, TargetNode ); return transition == Empty() ? [getResultCall] : [getResultCall, transition]; } - return [ + return + [ Assign( ResultVariable, getResultCall ), - GotoOrFallThrough( Parent.StateOrder, TargetNode ) + GotoOrFallThrough( parent.StateOrder, TargetNode ) ]; } } + //protected override List GetBody(NodeExpression parent ) + //{ + // return GetExpressions(); + + // List GetExpressions() + // { + // var getResultMethod = AwaitBinder.GetResultMethod; + + // var getResultCall = getResultMethod.IsStatic + // ? Call( getResultMethod, AwaiterVariable ) + // : Call( Constant( AwaitBinder ), getResultMethod, AwaiterVariable ); + + // if ( ResultVariable == null ) + // { + // var transition = GotoOrFallThrough( parent.StateOrder, TargetNode ); + + // return transition == Empty() + // ? [getResultCall] + // : [getResultCall, transition]; + // } + + // return [ + // Assign( ResultVariable, getResultCall ), + // GotoOrFallThrough( parent.StateOrder, TargetNode ) + // ]; + // } + //} + internal override void Optimize( HashSet references ) { TargetNode = OptimizeGotos( TargetNode ); diff --git a/src/Hyperbee.Expressions/Transformation/Transitions/AwaitTransition.cs b/src/Hyperbee.Expressions/Transformation/Transitions/AwaitTransition.cs index 6a0b2bf..6e88943 100644 --- a/src/Hyperbee.Expressions/Transformation/Transitions/AwaitTransition.cs +++ b/src/Hyperbee.Expressions/Transformation/Transitions/AwaitTransition.cs @@ -1,4 +1,5 @@ using System.Linq.Expressions; +using static System.Linq.Expressions.Expression; namespace Hyperbee.Expressions.Transformation.Transitions; @@ -14,37 +15,14 @@ internal class AwaitTransition : Transition internal override NodeExpression FallThroughNode => CompletionNode; - protected override Expression VisitChildren( ExpressionVisitor visitor ) + protected override void SetBody( List expressions, NodeExpression parent ) { - return Update( - visitor.Visit( Target ), - visitor.Visit( AwaiterVariable ) - ); - } - - internal AwaitTransition Update( Expression target, Expression awaiterVariable ) - { - if ( target == Target && awaiterVariable == AwaiterVariable ) - return this; - - return new AwaitTransition - { - StateId = StateId, - Target = target, - AwaiterVariable = awaiterVariable, - CompletionNode = CompletionNode, - AwaitBinder = AwaitBinder, - ConfigureAwait = ConfigureAwait - }; - } - - protected override List GetBody() - { - return GetExpressions(); + expressions.AddRange( Expressions() ); + return; - List GetExpressions() + List Expressions() { - var resolverSource = Parent.StateMachineSource; + var resolverSource = parent.StateMachineSource; var getAwaiterMethod = AwaitBinder.GetAwaiterMethod; var getAwaiterCall = getAwaiterMethod.IsStatic @@ -78,7 +56,7 @@ List GetExpressions() ) }; - var fallThrough = GotoOrFallThrough( Parent.StateOrder, CompletionNode, true ); + var fallThrough = GotoOrFallThrough( parent.StateOrder, CompletionNode, true ); if ( fallThrough != null ) body.Add( fallThrough ); diff --git a/src/Hyperbee.Expressions/Transformation/Transitions/ConditionalTransition.cs b/src/Hyperbee.Expressions/Transformation/Transitions/ConditionalTransition.cs index 954c14e..fd92a64 100644 --- a/src/Hyperbee.Expressions/Transformation/Transitions/ConditionalTransition.cs +++ b/src/Hyperbee.Expressions/Transformation/Transitions/ConditionalTransition.cs @@ -1,4 +1,5 @@ using System.Linq.Expressions; +using static System.Linq.Expressions.Expression; namespace Hyperbee.Expressions.Transformation.Transitions; @@ -10,31 +11,14 @@ internal class ConditionalTransition : Transition internal override NodeExpression FallThroughNode => IfFalse; - protected override Expression VisitChildren( ExpressionVisitor visitor ) + protected override void SetBody( List expressions, NodeExpression parent ) { - return Update( visitor.Visit( Test ) ); - } - - internal ConditionalTransition Update( Expression test ) - { - if ( test == Test ) - return this; - - return new ConditionalTransition - { - Test = test, - IfTrue = IfTrue, - IfFalse = IfFalse - }; - } - - protected override List GetBody() - { - return [GetExpression()]; + expressions.Add( Expression() ); + return; - Expression GetExpression() + Expression Expression() { - var fallThrough = GotoOrFallThrough( Parent.StateOrder, IfFalse, true ); + var fallThrough = GotoOrFallThrough( parent.StateOrder, IfFalse, true ); if ( fallThrough == null ) return IfThen( Test, Goto( IfTrue.NodeLabel ) ); diff --git a/src/Hyperbee.Expressions/Transformation/Transitions/FinalTransition.cs b/src/Hyperbee.Expressions/Transformation/Transitions/FinalTransition.cs index 202a98d..0edce5a 100644 --- a/src/Hyperbee.Expressions/Transformation/Transitions/FinalTransition.cs +++ b/src/Hyperbee.Expressions/Transformation/Transitions/FinalTransition.cs @@ -1,4 +1,5 @@ using System.Linq.Expressions; +using static System.Linq.Expressions.Expression; namespace Hyperbee.Expressions.Transformation.Transitions; @@ -10,15 +11,14 @@ internal override void Optimize( HashSet references ) { } - protected override List GetBody() + protected override void SetBody( List expressions, NodeExpression parent ) { - return EmptyBody; } - protected override void AssignResult( List expressions ) + protected override void SetResult( List expressions, NodeExpression parent ) { - var resultField = Parent.StateMachineSource.ResultField; - var returnValue = Parent.StateMachineSource.ReturnValue; + var resultField = parent.StateMachineSource.ResultField; + var returnValue = parent.StateMachineSource.ReturnValue; if ( returnValue != null ) { @@ -46,7 +46,7 @@ protected override void AssignResult( List expressions ) } expressions.Add( - Assign( resultField, Parent.ResultValue ?? Constant( null, typeof( IVoidResult ) ) ) + Assign( resultField, parent.ResultValue ?? Constant( null, typeof( IVoidResult ) ) ) ); } } diff --git a/src/Hyperbee.Expressions/Transformation/Transitions/GotoTransition.cs b/src/Hyperbee.Expressions/Transformation/Transitions/GotoTransition.cs index f7351f1..f978ee3 100644 --- a/src/Hyperbee.Expressions/Transformation/Transitions/GotoTransition.cs +++ b/src/Hyperbee.Expressions/Transformation/Transitions/GotoTransition.cs @@ -7,9 +7,9 @@ internal class GotoTransition : Transition public NodeExpression TargetNode { get; set; } internal override NodeExpression FallThroughNode => TargetNode; - protected override List GetBody() + protected override void SetBody( List expressions, NodeExpression parent ) { - return [GotoOrFallThrough( Parent.StateOrder, TargetNode )]; + expressions.Add( GotoOrFallThrough( parent.StateOrder, TargetNode ) ); } internal override void Optimize( HashSet references ) diff --git a/src/Hyperbee.Expressions/Transformation/Transitions/LoopTransition.cs b/src/Hyperbee.Expressions/Transformation/Transitions/LoopTransition.cs index 00388b4..14d44e6 100644 --- a/src/Hyperbee.Expressions/Transformation/Transitions/LoopTransition.cs +++ b/src/Hyperbee.Expressions/Transformation/Transitions/LoopTransition.cs @@ -10,9 +10,8 @@ internal class LoopTransition : Transition internal override NodeExpression FallThroughNode => BodyNode; - protected override List GetBody() + protected override void SetBody( List expressions, NodeExpression parent ) { - return EmptyBody; } internal override void Optimize( HashSet references ) diff --git a/src/Hyperbee.Expressions/Transformation/Transitions/SwitchTransition.cs b/src/Hyperbee.Expressions/Transformation/Transitions/SwitchTransition.cs index 1df3ff7..a9badcc 100644 --- a/src/Hyperbee.Expressions/Transformation/Transitions/SwitchTransition.cs +++ b/src/Hyperbee.Expressions/Transformation/Transitions/SwitchTransition.cs @@ -1,4 +1,5 @@ using System.Linq.Expressions; +using static System.Linq.Expressions.Expression; namespace Hyperbee.Expressions.Transformation.Transitions; @@ -10,39 +11,19 @@ internal class SwitchTransition : Transition internal override NodeExpression FallThroughNode => DefaultNode; - protected override Expression VisitChildren( ExpressionVisitor visitor ) + protected override void SetBody( List expressions, NodeExpression parent ) { - return Update( - visitor.Visit( SwitchValue ), - CaseNodes.Select( x => x.Update( x.TestValues.Select( visitor.Visit ).ToList() ) ).ToList() - ); - } - - internal SwitchTransition Update( Expression switchValue, List caseNodes ) - { - if ( switchValue == SwitchValue ) - return this; - - return new SwitchTransition - { - DefaultNode = DefaultNode, - SwitchValue = switchValue, - CaseNodes = caseNodes - }; - } - - protected override List GetBody() - { - return [GetExpression()]; + expressions.Add( Expression() ); + return; - Expression GetExpression() + Expression Expression() { Expression defaultBody; if ( DefaultNode != null ) { defaultBody = GotoOrFallThrough( - Parent.StateOrder, + parent.StateOrder, DefaultNode, allowNull: true ); @@ -53,7 +34,7 @@ Expression GetExpression() } var cases = CaseNodes - .Select( switchCase => switchCase.Reduce( Parent.StateOrder ) ) + .Select( switchCase => switchCase.Reduce( parent.StateOrder ) ) .ToArray(); return Switch( SwitchValue, defaultBody, cases ); diff --git a/src/Hyperbee.Expressions/Transformation/Transitions/Transition.cs b/src/Hyperbee.Expressions/Transformation/Transitions/Transition.cs index d256b46..c47c52a 100644 --- a/src/Hyperbee.Expressions/Transformation/Transitions/Transition.cs +++ b/src/Hyperbee.Expressions/Transformation/Transitions/Transition.cs @@ -1,63 +1,28 @@ using System.Diagnostics; using System.Linq.Expressions; +using static System.Linq.Expressions.Expression; + namespace Hyperbee.Expressions.Transformation.Transitions; [DebuggerDisplay( "Transition = {GetType().Name,nq}" )] -internal abstract class Transition : Expression +internal abstract class Transition { - protected static readonly List EmptyBody = [Empty()]; - - public override ExpressionType NodeType => ExpressionType.Extension; - public override Type Type => typeof( void ); - public override bool CanReduce => true; - - protected override Expression VisitChildren( ExpressionVisitor visitor ) => this; - internal abstract NodeExpression FallThroughNode { get; } - internal abstract void Optimize( HashSet references ); - - internal NodeExpression Parent { get; set; } - - public override Expression Reduce() + public void AddExpressions( NodeExpression parent, List expressions ) { - if ( Parent == null ) - throw new InvalidOperationException( $"Transition Reduce requires a {nameof( Parent )} instance." ); - - var reduced = Reduce( Parent ); + if ( parent == null ) + throw new InvalidOperationException( $"Transition {nameof( AddExpressions )} requires a {nameof( parent )} instance." ); - return (reduced.Count == 1) - ? reduced[0] - : Block( reduced ); + SetResult( expressions, parent ); + SetBody( expressions, parent ); } - private List Reduce( NodeExpression node ) + protected virtual void SetResult( List expressions, NodeExpression parent ) { - var expressions = new List( 8 ) // Label, Expressions, AssignResult, Transition - { - Label( node.NodeLabel ) - }; - - expressions.AddRange( node.Expressions ); - - // add result assignment - - AssignResult( expressions ); - - // add transition body - - expressions.AddRange( GetBody() ); - - return expressions; - } - - protected abstract List GetBody(); - - protected virtual void AssignResult( List expressions ) - { - var resultValue = Parent.ResultValue; - var resultVariable = Parent.ResultVariable; + var resultValue = parent.ResultValue; + var resultVariable = parent.ResultVariable; if ( resultValue != null && resultVariable != null && resultValue.Type == resultVariable.Type ) { @@ -69,12 +34,9 @@ protected virtual void AssignResult( List expressions ) } } - protected static Expression GotoOrFallThrough( int order, NodeExpression node, bool allowNull = false ) - { - return order + 1 == node.StateOrder - ? allowNull ? null : Empty() - : Goto( node.NodeLabel ); - } + protected abstract void SetBody( List expressions, NodeExpression parent ); + + internal abstract void Optimize( HashSet references ); protected static NodeExpression OptimizeGotos( NodeExpression node ) { @@ -85,5 +47,15 @@ protected static NodeExpression OptimizeGotos( NodeExpression node ) return node; } + + protected static Expression GotoOrFallThrough( int order, NodeExpression node, bool allowNull = false ) + { + if ( order + 1 == node.StateOrder ) + { + return allowNull ? null : Empty(); + } + + return Goto( node.NodeLabel ); + } } diff --git a/src/Hyperbee.Expressions/Transformation/Transitions/TryCatchTransition.cs b/src/Hyperbee.Expressions/Transformation/Transitions/TryCatchTransition.cs index f2b8ae7..e9d0e38 100644 --- a/src/Hyperbee.Expressions/Transformation/Transitions/TryCatchTransition.cs +++ b/src/Hyperbee.Expressions/Transformation/Transitions/TryCatchTransition.cs @@ -1,4 +1,5 @@ using System.Linq.Expressions; +using static System.Linq.Expressions.Expression; namespace Hyperbee.Expressions.Transformation.Transitions; @@ -16,54 +17,26 @@ internal class TryCatchTransition : Transition internal override NodeExpression FallThroughNode => TryNode; - protected override Transition VisitChildren( ExpressionVisitor visitor ) + protected override void SetBody( List expressions, NodeExpression parent ) { - return Update( - CatchBlocks.Select( c => new CatchBlockDefinition( c.Handler, visitor.Visit( c.UpdateBody ), c.CatchState ) ).ToList(), - visitor.Visit( TryStateVariable ), - visitor.Visit( ExceptionVariable ) - ); - } - - internal TryCatchTransition Update( - List catchBlocks, - Expression tryStateVariable, - Expression exceptionVariable ) - { - if ( catchBlocks == CatchBlocks && tryStateVariable == TryStateVariable && exceptionVariable == ExceptionVariable ) - return this; - - return new TryCatchTransition - { - TryNode = TryNode, - FinallyNode = FinallyNode, - CatchBlocks = catchBlocks, - TryStateVariable = tryStateVariable, - ExceptionVariable = exceptionVariable, - StateScope = StateScope, - Scopes = Scopes - }; - } - - protected override List GetBody() - { - return GetExpressions(); + expressions.AddRange( Expressions() ); + return; - List GetExpressions() + List Expressions() { var body = new List { JumpTableBuilder.Build( StateScope, Scopes, - Parent.StateMachineSource.StateIdField + parent.StateMachineSource.StateIdField ) }; //body.AddRange( StateScope.Nodes ); //BF ME - merge nodes here body.AddRange( NodeExpression.Merge( StateScope.Nodes ) ); - MapCatchBlock( Parent.StateOrder, out var catches, out var switchCases ); + MapCatchBlock( parent.StateOrder, out var catches, out var switchCases ); return [ TryCatch( diff --git a/test/Hyperbee.Expressions.Tests/Optimizers/InliningOptimizerTests.cs b/test/Hyperbee.Expressions.Tests/Optimizers/InliningOptimizerTests.cs deleted file mode 100644 index ac78248..0000000 --- a/test/Hyperbee.Expressions.Tests/Optimizers/InliningOptimizerTests.cs +++ /dev/null @@ -1,110 +0,0 @@ -using System.Linq.Expressions; -using Hyperbee.Expressions.Optimizers; - -namespace Hyperbee.Expressions.Tests.Optimizers; - -[TestClass] -public class InliningOptimizerTests -{ - [TestMethod] - public void Inlining_ShouldInlineSimpleConstant() - { - // Before: .Add(.Constant(10), .Constant(5)) - // After: .Constant(15) - - // Arrange - var expression = Expression.Add( Expression.Constant( 10 ), Expression.Constant( 5 ) ); - var optimizer = new InliningOptimizer(); - - // Act - var result = optimizer.Optimize( expression ); - var value = ((ConstantExpression) result).Value; - - // Assert - Assert.AreEqual( 15, value ); - } - - [TestMethod] - public void Inlining_ShouldInlineLambdaExpression() - { - // Before: .Invoke((x) => x + 5, .Constant(3)) - // After: .Constant(8) - - // Arrange - var parameter = Expression.Parameter( typeof( int ), "x" ); - var lambda = Expression.Lambda( Expression.Add( parameter, Expression.Constant( 5 ) ), parameter ); - var invocation = Expression.Invoke( lambda, Expression.Constant( 3 ) ); - var optimizer = new InliningOptimizer(); - - // Act - var result = optimizer.Optimize( invocation ); - var value = ((ConstantExpression) result).Value; - - // Assert - Assert.AreEqual( 8, value ); - } - - [TestMethod] - public void Inlining_ShouldShortCircuitBoolean() - { - // Before: .AndAlso(.Constant(true), .Constant(false)) - // After: .Constant(false) - - // Arrange - var expression = Expression.AndAlso( Expression.Constant( true ), Expression.Constant( false ) ); - var optimizer = new InliningOptimizer(); - - // Act - var result = optimizer.Optimize( expression ); - var value = (bool) ((ConstantExpression) result).Value!; - - // Assert - Assert.IsFalse( value ); - } - - [TestMethod] - public void Inlining_ShouldInlineConditionalExpression() - { - // Before: .Conditional(.Constant(true), .Constant("True"), .Constant("False")) - // After: .Constant("True") - - // Arrange - var condition = Expression.Constant( true ); - var conditional = Expression.Condition( condition, Expression.Constant( "True" ), Expression.Constant( "False" ) ); - var optimizer = new InliningOptimizer(); - - // Act - var result = optimizer.Optimize( conditional ); - var value = ((ConstantExpression) result).Value; - - // Assert - Assert.AreEqual( "True", value ); - } - - [TestMethod] - public void Inlining_ShouldSimplifyNestedConditionalExpression() - { - // Before: .Conditional(.Constant(true), .Conditional(.Constant(false), .Constant("A"), .Constant("B")), .Constant("C")) - // After: .Constant("B") - - // Arrange - var innerCondition = Expression.Condition( - Expression.Constant( false ), - Expression.Constant( "A" ), - Expression.Constant( "B" ) - ); - var outerCondition = Expression.Condition( - Expression.Constant( true ), - innerCondition, - Expression.Constant( "C" ) - ); - var optimizer = new InliningOptimizer(); - - // Act - var result = optimizer.Optimize( outerCondition ); - var value = ((ConstantExpression) result).Value; - - // Assert - Assert.AreEqual( "B", value ); - } -} diff --git a/test/Hyperbee.Expressions.Tests/Optimizers/OperatorReductionOptimizerTests.cs b/test/Hyperbee.Expressions.Tests/Optimizers/OperatorReductionOptimizerTests.cs deleted file mode 100644 index 87c7f1d..0000000 --- a/test/Hyperbee.Expressions.Tests/Optimizers/OperatorReductionOptimizerTests.cs +++ /dev/null @@ -1,118 +0,0 @@ -using System.Linq.Expressions; -using Hyperbee.Expressions.Optimizers; - -namespace Hyperbee.Expressions.Tests.Optimizers; - -[TestClass] -public class OperatorReductionOptimizerTests -{ - [TestMethod] - public void OperatorReduction_ShouldRemoveAddZero() - { - // Before: .Add(.Parameter(x), .Constant(0)) - // After: .Parameter(x) - - // Arrange - var expression = Expression.Add( Expression.Parameter( typeof( int ), "x" ), Expression.Constant( 0 ) ); - var optimizer = new OperatorReductionOptimizer(); - - // Act - var result = optimizer.Optimize( expression ); - - // Assert - Assert.IsInstanceOfType( result, typeof( ParameterExpression ) ); - } - - [TestMethod] - public void OperatorReduction_ShouldRemoveMultiplyByOne() - { - // Before: .Multiply(.Parameter(x), .Constant(1)) - // After: .Parameter(x) - - // Arrange - var expression = Expression.Multiply( Expression.Parameter( typeof( int ), "x" ), Expression.Constant( 1 ) ); - var optimizer = new OperatorReductionOptimizer(); - - // Act - var result = optimizer.Optimize( expression ); - - // Assert - Assert.IsInstanceOfType( result, typeof( ParameterExpression ) ); - } - - [TestMethod] - public void OperatorReduction_ShouldSimplifyLogicalIdentity() - { - // Before: .AndAlso(.Constant(true), .Parameter(x)) - // After: .Parameter(x) - - // Arrange - var andExpression = Expression.AndAlso( Expression.Constant( true ), Expression.Parameter( typeof( bool ), "x" ) ); - var optimizer = new OperatorReductionOptimizer(); - - // Act - var result = optimizer.Optimize( andExpression ); - - // Assert - Assert.IsInstanceOfType( result, typeof( ParameterExpression ) ); - } - - [TestMethod] - public void OperatorReduction_ShouldFlattenNestedExpressions() - { - // Before: .Add(.Add(.Constant(1), .Constant(2)), .Constant(3)) - // After: .Constant(6) - - // Arrange - var nestedExpression = Expression.Add( Expression.Add( Expression.Constant( 1 ), Expression.Constant( 2 ) ), Expression.Constant( 3 ) ); - var optimizer = new OperatorReductionOptimizer(); - - // Act - var result = optimizer.Optimize( nestedExpression ); - var constant = result as ConstantExpression; - var value = constant?.Value; - - // Assert - Assert.IsInstanceOfType( result, typeof( ConstantExpression ), "Expected a ConstantExpression after optimization." ); - Assert.IsNotNull( constant, "Expected a non-null ConstantExpression." ); - Assert.AreEqual( 6, value ); - } - - [TestMethod] - public void OperatorReduction_ShouldSimplifyMultiOperationReduction() - { - // Before: .Multiply(.Add(.Parameter(x), .Constant(0)), .Constant(1)) - // After: .Parameter(x) - - // Arrange - var parameter = Expression.Parameter( typeof( int ), "x" ); - var addZero = Expression.Add( parameter, Expression.Constant( 0 ) ); - var multiplyByOne = Expression.Multiply( addZero, Expression.Constant( 1 ) ); - var optimizer = new OperatorReductionOptimizer(); - - // Act - var result = optimizer.Optimize( multiplyByOne ); - - // Assert - Assert.IsInstanceOfType( result, typeof( ParameterExpression ) ); - } - - [TestMethod] - public void OperatorReduction_ShouldSimplifyComplexBooleanReduction() - { - // Before: .AndAlso(.OrElse(.Constant(true), .Constant(false)), .Constant(true)) - // After: .Constant(true) - - // Arrange - var orExpression = Expression.OrElse( Expression.Constant( true ), Expression.Constant( false ) ); - var andExpression = Expression.AndAlso( orExpression, Expression.Constant( true ) ); - var optimizer = new OperatorReductionOptimizer(); - - // Act - var result = optimizer.Optimize( andExpression ); - var value = (bool) ((ConstantExpression) result).Value!; - - // Assert - Assert.IsTrue( value ); - } -} diff --git a/test/Hyperbee.Expressions.Tests/Optimizers/StructuralReductionOptimizerTests.cs b/test/Hyperbee.Expressions.Tests/Optimizers/StructuralReductionOptimizerTests.cs deleted file mode 100644 index 1e180ce..0000000 --- a/test/Hyperbee.Expressions.Tests/Optimizers/StructuralReductionOptimizerTests.cs +++ /dev/null @@ -1,113 +0,0 @@ -using System.Linq.Expressions; -using Hyperbee.Expressions.Optimizers; - -namespace Hyperbee.Expressions.Tests.Optimizers; - -[TestClass] -public class StructuralReductionOptimizerTests -{ - [TestMethod] - public void StructuralReduction_ShouldRemoveUnreachableCode() - { - // Before: .Block(.Constant(1), .Constant(2)) - // After: .Constant(1) - - // Arrange - var block = Expression.Block( Expression.Constant( 1 ), Expression.Constant( 2 ) ); - var optimizer = new StructuralReductionOptimizer(); - - // Act - var result = optimizer.Optimize( block ); - var value = ((ConstantExpression) ((BlockExpression) result).Expressions[0]).Value; - - // Assert - Assert.AreEqual( 1, value ); - } - - [TestMethod] - public void StructuralReduction_ShouldSimplifyEmptyTryCatch() - { - // Before: .TryCatch(.Empty(), .Catch(...)) - // After: .Empty() - - // Arrange - var tryCatch = Expression.TryCatch( - Expression.Empty(), - Expression.Catch( Expression.Parameter( typeof( Exception ) ), Expression.Empty() ) - ); - var optimizer = new StructuralReductionOptimizer(); - - // Act - var result = optimizer.Optimize( tryCatch ); - - // Assert - Assert.IsInstanceOfType( result, typeof( DefaultExpression ) ); - } - - [TestMethod] - public void StructuralReduction_ShouldRemoveInfiniteLoop() - { - // Before: .Loop(.Constant(1)) - // After: .Empty() - - // Arrange - var loop = Expression.Loop( Expression.Constant( 1 ) ); - var optimizer = new StructuralReductionOptimizer(); - - // Act - var result = optimizer.Optimize( loop ); - - // Assert - Assert.IsInstanceOfType( result, typeof( DefaultExpression ) ); - } - - [TestMethod] - public void StructuralReduction_ShouldSimplifyNestedConditionalExpression() - { - // Before: .Block(.IfThenElse(.Constant(true), .IfThenElse(.Constant(false), .Break(), .Constant("B")))) - // After: .Constant("B") - - // Arrange - var innerCondition = Expression.IfThenElse( - Expression.Constant( false ), - Expression.Break( Expression.Label() ), - Expression.Constant( "B" ) - ); - var outerCondition = Expression.IfThenElse( - Expression.Constant( true ), - innerCondition, - Expression.Constant( "C" ) - ); - var block = Expression.Block( outerCondition ); - var optimizer = new StructuralReductionOptimizer(); - - // Act - var result = optimizer.Optimize( block ); - var value = ((ConstantExpression) result).Value; - - // Assert - Assert.AreEqual( "B", value ); - } - - [TestMethod] - public void StructuralReduction_ShouldSimplifyLoopWithComplexCondition() - { - // Before: .Loop(.IfThenElse(.Constant(false), .Break(), .Constant(1))) - // After: .Empty() - - // Arrange - var loopCondition = Expression.IfThenElse( - Expression.Constant( false ), - Expression.Break( Expression.Label() ), - Expression.Constant( 1 ) - ); - var loop = Expression.Loop( loopCondition ); - var optimizer = new StructuralReductionOptimizer(); - - // Act - var result = optimizer.Optimize( loop ); - - // Assert - Assert.IsInstanceOfType( result, typeof( DefaultExpression ) ); - } -} diff --git a/test/Hyperbee.Expressions.Tests/Optimizers/SubexpressionCachingOptimizerTests.cs b/test/Hyperbee.Expressions.Tests/Optimizers/SubexpressionCachingOptimizerTests.cs deleted file mode 100644 index f9ebb63..0000000 --- a/test/Hyperbee.Expressions.Tests/Optimizers/SubexpressionCachingOptimizerTests.cs +++ /dev/null @@ -1,146 +0,0 @@ -using System.Linq.Expressions; -using Hyperbee.Expressions.Optimizers; -using Hyperbee.Expressions.Tests.TestSupport; - -namespace Hyperbee.Expressions.Tests.Optimizers; - -[TestClass] -public class SubexpressionCachingOptimizerTests -{ - public class TestClass - { - public string Method() => "Result"; - } - - [TestMethod] - public void SubexpressionCaching_ShouldNotCacheSimpleExpressions() - { - // Before: .Add(.Constant(2), .Constant(3)) + .Add(.Constant(2), .Constant(3)) - // After: .Add(.Constant(2), .Constant(3)) + .Add(.Constant(2), .Constant(3)) - - // Arrange - var simpleExpr = Expression.Add( Expression.Constant( 2 ), Expression.Constant( 3 ) ); - var referenceExpr = Expression.Lambda>( - Expression.Add( simpleExpr, simpleExpr ) - ); - - var optimizer = new SubexpressionCachingOptimizer(); - - // Act - var optimizedExpr = optimizer.Optimize( referenceExpr ); - var reference = referenceExpr.Compile(); - var result = reference(); - var optimized = optimizedExpr.Compile(); - var comparand = optimized(); - - // Assert - Assert.AreEqual( result, comparand ); - Assert.IsTrue( optimizedExpr.GetDebugView().Contains( "2 + 3 + 2 + 3" ), "Simple expression should not be cached." ); - } - - [TestMethod] - public void SubexpressionCaching_ShouldCacheComplexExpressions() - { - // Before: .Add(.Multiply(.Constant(5), .Add(.Constant(3), .Constant(2))), .Multiply(.Constant(5), .Add(.Constant(3), .Constant(2)))) - // After: .Block(.Assign(variable, .Multiply(.Constant(5), .Add(.Constant(3), .Constant(2)))), .Add(variable, variable)) - - // Arrange - var complexExpr = Expression.Multiply( Expression.Constant( 5 ), Expression.Add( Expression.Constant( 3 ), Expression.Constant( 2 ) ) ); - var referenceExpr = Expression.Lambda>( - Expression.Add( complexExpr, complexExpr ) - ); - - var optimizer = new SubexpressionCachingOptimizer(); - - // Act - var optimizedExpr = optimizer.Optimize( referenceExpr ); - var reference = referenceExpr.Compile(); - var result = reference(); - var optimized = optimizedExpr.Compile(); - var comparand = optimized(); - - // Assert - Assert.AreEqual( result, comparand ); - Assert.IsTrue( optimizedExpr.GetDebugView().Contains( "Block" ), "Complex expression should be cached with a block." ); - } - - [TestMethod] - public void SubexpressionCaching_ShouldCacheRepeatedMethodCalls() - { - // Before: .Add(.Call(Method), .Call(Method)) - // After: .Block(.Assign(variable, .Call(Method)), .Add(variable, variable)) - - // Arrange - Expression> referenceExpr = () => Math.Abs( -5 ) + Math.Abs( -5 ); - var optimizer = new SubexpressionCachingOptimizer(); - - // Act - var optimizedExpr = optimizer.Optimize( referenceExpr ); - var reference = referenceExpr.Compile(); - var result = reference(); - var optimized = optimizedExpr.Compile(); - var comparand = optimized(); - - // Assert - Assert.AreEqual( result, comparand ); - Assert.IsTrue( optimizedExpr.GetDebugView().Contains( "Block" ), "Method call should be cached with a block." ); - } - - [TestMethod] - public void SubexpressionCaching_ShouldCacheRepeatedExpressions() - { - // Before: .Add(.Add(x, .Constant(5)), .Add(x, .Constant(5))) - // After: .Block(.Assign(variable, .Add(x, .Constant(5))), .Add(variable, variable)) - - // Arrange - var x = Expression.Parameter( typeof( int ), "x" ); - var repeatedExpr = Expression.Add( x, Expression.Constant( 5 ) ); - var complexExpression = Expression.Add( repeatedExpr, repeatedExpr ); - var optimizer = new SubexpressionCachingOptimizer(); - - // Act - var result = optimizer.Optimize( complexExpression ); - - // Assert - Assert.IsInstanceOfType( result, typeof( BlockExpression ) ); - Assert.AreEqual( 2, ((BlockExpression) result).Expressions.Count ); - } - - [TestMethod] - public void SubexpressionCaching_ShouldCacheCrossScopeExpression() - { - // Before: .Block(.Call(obj.Method), .Block(.Call(obj.Method))) - // After: .Block(.Assign(variable, .Call(obj.Method)), .Variable(variable), .Block(.Variable(variable))) - - // Arrange - var methodCall = Expression.Call( Expression.Constant( new TestClass() ), "Method", null ); - var innerBlock = Expression.Block( methodCall ); - var outerBlock = Expression.Block( methodCall, innerBlock ); - var optimizer = new SubexpressionCachingOptimizer(); - - // Act - var result = optimizer.Optimize( outerBlock ); - - // Assert - Assert.IsInstanceOfType( result, typeof( BlockExpression ) ); - } - - [TestMethod] - public void SubexpressionCaching_ShouldCacheParameterizedSubexpression() - { - // Before: .Block(.Add(.Parameter(x), .Constant(5)), .Add(.Parameter(x), .Constant(5))) - // After: .Block(.Assign(variable, .Add(.Parameter(x), .Constant(5))), .Variable(variable), .Variable(variable)) - - // Arrange - var parameter = Expression.Parameter( typeof( int ), "x" ); - var addExpression = Expression.Add( parameter, Expression.Constant( 5 ) ); - var block = Expression.Block( addExpression, addExpression ); - var optimizer = new SubexpressionCachingOptimizer(); - - // Act - var result = optimizer.Optimize( block ); - - // Assert - Assert.IsInstanceOfType( result, typeof( BlockExpression ) ); - } -} diff --git a/test/Hyperbee.Expressions.Tests/Optimizers/ValueBindingOptimizerTests.cs b/test/Hyperbee.Expressions.Tests/Optimizers/ValueBindingOptimizerTests.cs deleted file mode 100644 index 7ff0ae5..0000000 --- a/test/Hyperbee.Expressions.Tests/Optimizers/ValueBindingOptimizerTests.cs +++ /dev/null @@ -1,174 +0,0 @@ -using System.Linq.Expressions; -using Hyperbee.Expressions.Optimizers; - -namespace Hyperbee.Expressions.Tests.Optimizers; - -[TestClass] -public class ValueBindingOptimizerTests -{ - public class Container - { - public NestedClass Nested { get; } = new(); - } - - public class NestedClass - { - public string Value => "ExpectedValue"; - } - - [TestMethod] - public void ValueBinding_ShouldFoldConstants() - { - // Before: .Add(.Constant(2), .Constant(3)) - // After: .Constant(5) - - // Arrange - var expression = Expression.Add( Expression.Constant( 2 ), Expression.Constant( 3 ) ); - var optimizer = new ValueBindingOptimizer(); - - // Act - var result = optimizer.Optimize( expression ); - var value = ((ConstantExpression) result).Value; - - // Assert - Assert.AreEqual( 5, value ); - } - - [TestMethod] - public void ValueBinding_ShouldInlineSingleUseVariable() - { - // Before: .Block(.Assign(.Parameter(x), .Constant(10)), .Parameter(x)) - // After: .Constant(10) - - // Arrange - var parameter = Expression.Parameter( typeof( int ), "x" ); - var block = Expression.Block( [parameter], Expression.Assign( parameter, Expression.Constant( 10 ) ), parameter ); - var optimizer = new ValueBindingOptimizer(); - - // Act - var result = optimizer.Optimize( block ); - var value = ((ConstantExpression) result).Value; - - // Assert - Assert.AreEqual( 10, value ); - } - - [TestMethod] - public void ValueBinding_ShouldSimplifyConstantMemberAccess() - { - // Before: .Property(.Constant(new DateTime(2024, 1, 1)), "Year") - // After: .Constant(2024) - - // Arrange - var date = Expression.Constant( new DateTime( 2024, 1, 1 ) ); - var memberAccess = Expression.Property( date, "Year" ); - var optimizer = new ValueBindingOptimizer(); - - // Act - var result = optimizer.Optimize( memberAccess ); - var value = ((ConstantExpression) result).Value; - - // Assert - Assert.AreEqual( 2024, value ); - } - - [TestMethod] - public void ValueBinding_ShouldInlineVariableInNestedScope() - { - // Before: .Block(.Assign(.Parameter(x), .Constant(10)), .Block(.Parameter(x))) - // After: .Constant(10) - - // Arrange - var parameter = Expression.Parameter( typeof( int ), "x" ); - var nestedBlock = Expression.Block( parameter ); - var outerBlock = Expression.Block( [parameter], Expression.Assign( parameter, Expression.Constant( 10 ) ), nestedBlock ); - var optimizer = new ValueBindingOptimizer(); - - // Act - var result = optimizer.Optimize( outerBlock ); - var value = ((ConstantExpression) result).Value; - - // Assert - Assert.AreEqual( 10, value ); - } - - [TestMethod] - public void ValueBinding_ShouldSimplifyComplexMemberAccess() - { - // Before: .Property(.Property(.Constant(new Container()), "Nested"), "Value") - // After: .Constant("ExpectedValue") - - // Arrange - var container = Expression.Constant( new Container() ); - var nestedMember = Expression.Property( container, nameof( Container.Nested ) ); - var memberAccess = Expression.Property( nestedMember, nameof( Container.Nested.Value ) ); - var optimizer = new ValueBindingOptimizer(); - - // Act - var result = optimizer.Optimize( memberAccess ); - var value = ((ConstantExpression) result).Value; - - // Assert - Assert.AreEqual( "ExpectedValue", value ); - } - - [TestMethod] - public void ValueBinding_ShouldReduceRedundantAllocations() - { - // Before: .Block(.Assign(.Parameter(x), .Constant(1)), .Parameter(x)) - // After: .Constant(1) - - // Arrange - var param = Expression.Parameter( typeof( int ), "x" ); - var redundantAlloc = Expression.Block( [param], Expression.Assign( param, Expression.Constant( 1 ) ), param ); - var optimizer = new ValueBindingOptimizer(); - - // Act - var result = optimizer.Optimize( redundantAlloc ); - var constant = (ConstantExpression) result; - var value = constant.Value; - - // Assert - Assert.AreEqual( 1, value ); - } - - [TestMethod] - public void ValueBinding_ShouldReuseTemporaryVariableInLoop() - { - // Before: .Loop(.Block(.Assign(.Parameter(temp), .Constant(42)), .Parameter(temp))) - // After: .Constant(42) - - // Arrange - var tempVar = Expression.Parameter( typeof( int ), "temp" ); - var loop = Expression.Loop( Expression.Block( [tempVar], Expression.Assign( tempVar, Expression.Constant( 42 ) ), tempVar ) ); - var optimizer = new ValueBindingOptimizer(); - - // Act - var result = optimizer.Optimize( loop ); - - // Assert - var resultValue = (result as ConstantExpression)?.Value; - - Assert.IsInstanceOfType( result, typeof( ConstantExpression ) ); - Assert.AreEqual( 42, resultValue, "Expected the loop body constant to have value 42." ); - } - - [TestMethod] - public void ValueBinding_ShouldReduceAllocationsInNestedBlocks() - { - // Before: .Block(.Block(.Assign(.Parameter(temp), .Constant(10)), .Parameter(temp)), .Parameter(temp)) - // After: .Constant(10) - - // Arrange - var tempVar = Expression.Parameter( typeof( int ), "temp" ); - var innerBlock = Expression.Block( [tempVar], Expression.Assign( tempVar, Expression.Constant( 10 ) ), tempVar ); - var outerBlock = Expression.Block( innerBlock, tempVar ); - var optimizer = new ValueBindingOptimizer(); - - // Act - var result = optimizer.Optimize( outerBlock ); - - // Assert - Assert.IsInstanceOfType( result, typeof( ConstantExpression ) ); - } -}