Skip to content

Commit 81b864a

Browse files
committed
bms updated
1 parent 9675462 commit 81b864a

File tree

3 files changed

+177
-94
lines changed

3 files changed

+177
-94
lines changed

src/FastExpressionCompiler/FastExpressionCompiler.cs

Lines changed: 140 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,8 @@ public enum CompilerFlags : byte
7777
EnableDelegateDebugInfo = 1 << 1,
7878
/// <summary>When the flag is set then instead of the returning `null` the specific exception is thrown*346</summary>
7979
ThrowOnNotSupportedExpression = 1 << 2,
80-
/// <summary>Will try to evalaute constant, arithmetic, logical, comparison expressions consisting of the former expression types,
81-
/// and emit the result only to the IL instead the whole computation. Minimizes IL and moves optimization to the compilation phase if possible.</summary>
80+
/// <summary>Will try to Interpret arithmetic, logical, comparison expressions for the primitive types,
81+
/// and emit the IL the result only instead of the whole computation.</summary>
8282
DisableInterpreter = 1 << 4
8383
}
8484

@@ -493,7 +493,7 @@ internal static object TryCompileBoundToFirstClosureParam(Type delegateType, Exp
493493
Type[] closurePlusParamTypes, Type returnType, CompilerFlags flags)
494494
{
495495
#endif
496-
// Try to avoid complilation altogether for Func<bool> delegates via Interpreter, see #468
496+
// Try to avoid compilation altogether for Func<bool> delegates via Interpreter, see #468
497497
if ((flags & CompilerFlags.DisableInterpreter) == 0 &
498498
returnType == typeof(bool) & closurePlusParamTypes.Length == 1
499499
&& Interpreter.IsCandidateForInterpretation(bodyExpr)
@@ -521,7 +521,7 @@ internal static object TryCompileBoundToFirstClosureParam(Type delegateType, Exp
521521
closure = new DebugArrayClosure(constantsAndNestedLambdas, debugExpr);
522522
}
523523

524-
// note: @slow this is what System.Compiles does and which makes the compilation significally slower 10x, but the invocation become faster by a single branch instruction
524+
// note: @slow this is what System.Compiles does and which makes the compilation 10x slower, but the invocation become faster by a single branch instruction
525525
// var method = new DynamicMethod(string.Empty, returnType, closurePlusParamTypes, true);
526526
// this is FEC way, significantly faster compilation, but +1 branch instruction in the invocation
527527
var method = new DynamicMethod(string.Empty, returnType, closurePlusParamTypes, typeof(ArrayClosure), true);
@@ -6391,7 +6391,7 @@ private static void EmitLoadArgAddress(ILGenerator il, int paramIndex)
63916391
}
63926392
}
63936393

6394-
/// <summary>Interprter</summary>
6394+
/// <summary>Interpreter</summary>
63956395
public static class Interpreter
63966396
{
63976397
/// <summary>Always returns true</summary>
@@ -6574,7 +6574,7 @@ public static object GetZeroDefaultObject(TypeCode typeCode)
65746574
}
65756575

65766576
/// <summary>Fast, mostly negative check to skip or proceed with interpretation.
6577-
/// Depending on the context you may avoid calling it because you know the interpeted expression beforehand,</summary>
6577+
/// Depending on the context you may avoid calling it because you know the interpreted expression beforehand,</summary>
65786578
[MethodImpl(MethodImplOptions.AggressiveInlining)]
65796579
public static bool IsCandidateForInterpretation(Expression expr)
65806580
{
@@ -6588,7 +6588,7 @@ public static bool IsCandidateForInterpretation(Expression expr)
65886588
expr is BinaryExpression;
65896589
}
65906590

6591-
/// <summary>In case of exception FEC will emit the whole computation to throw expection in the invocation phase</summary>
6591+
/// <summary>In case of exception FEC will emit the whole computation to throw exception in the invocation phase</summary>
65926592
public static bool TryInterpretBoolean(out bool result, Expression expr)
65936593
{
65946594
try
@@ -6607,50 +6607,31 @@ public static bool TryInterpretBoolean(out bool result, Expression expr)
66076607
return false;
66086608
}
66096609

6610-
/// <summary>Tries to interpret the expression of the Primitive type of Constant, Convert, Logical, Comparison, Arithmetic</summary>
6610+
/// <summary>Tries to interpret the expression of the Primitive type of Constant, Convert, Logical, Comparison, Arithmetic.</summary>
66116611
public static bool TryInterpretPrimitive(out object result, Expression expr)
66126612
{
66136613
Debug.Assert(expr.Type.IsPrimitive);
66146614
result = null;
66156615

6616-
var nodeType = expr.NodeType;
6617-
if (nodeType == ExpressionType.Constant)
6618-
{
6619-
#if LIGHT_EXPRESSION
6620-
if (((ConstantExpression)expr).RefField != null)
6621-
return false;
6622-
#endif
6623-
result = ((ConstantExpression)expr).Value;
6624-
if (expr.Type == typeof(bool))
6625-
result = (bool)result ? TrueObject : FalseObject;
6626-
return true;
6627-
}
6628-
6629-
if (nodeType == ExpressionType.Default)
6630-
{
6631-
result = GetZeroDefaultObject(Type.GetTypeCode(expr.Type));
6632-
return true;
6633-
}
6616+
// The order of the checks for the type of the expression is deliberate,
6617+
// because we are starting with complex expressions first.
6618+
// And for the simplest ones like a Constant? the method may not be even called.
6619+
// Instead, the Constant check and interpretation may be done inline.
66346620

6635-
if (nodeType == ExpressionType.Convert)
6621+
var nodeType = expr.NodeType;
6622+
if (nodeType == ExpressionType.Not)
66366623
{
66376624
var unaryExpr = (UnaryExpression)expr;
6638-
var operand = unaryExpr.Operand;
6639-
if (!TryInterpretPrimitive(out result, operand))
6640-
return false;
6641-
var exprType = expr.Type;
6642-
if (operand.Type != exprType && !exprType.IsAssignableFrom(operand.Type))
6625+
var operandExpr = unaryExpr.Operand;
6626+
if (operandExpr is ConstantExpression co)
66436627
{
6644-
var converted = System.Convert.ChangeType(result, exprType);
6645-
result = exprType != typeof(bool) ? converted : (bool)converted ? TrueObject : FalseObject;
6628+
#if LIGHT_EXPRESSION
6629+
if (co.RefField != null) return false;
6630+
#endif
6631+
result = (bool)co.Value ? FalseObject : TrueObject;
6632+
return true;
66466633
}
6647-
return true;
6648-
}
6649-
6650-
if (nodeType == ExpressionType.Not)
6651-
{
6652-
var unaryExpr = (UnaryExpression)expr;
6653-
if (!TryInterpretPrimitive(out var boolVal, unaryExpr.Operand))
6634+
if (!TryInterpretPrimitive(out var boolVal, operandExpr))
66546635
return false;
66556636
result = boolVal == TrueObject ? FalseObject : TrueObject;
66566637
return true;
@@ -6659,71 +6640,152 @@ public static bool TryInterpretPrimitive(out object result, Expression expr)
66596640
if (IsLogical(nodeType))
66606641
{
66616642
var binaryExpr = (BinaryExpression)expr;
6662-
if (!TryInterpretPrimitive(out var leftVal, binaryExpr.Left))
6643+
6644+
// Interpreting the left part as the first candidate for the result
6645+
var left = binaryExpr.Left;
6646+
if (left is ConstantExpression lc)
6647+
{
6648+
#if LIGHT_EXPRESSION
6649+
if (lc.RefField != null) return false;
6650+
#endif
6651+
result = (bool)lc.Value ? TrueObject : FalseObject;
6652+
}
6653+
else if (!TryInterpretPrimitive(out result, left))
66636654
return false;
66646655

6665-
// Short circuit the evalution, because this is an actual logic of these logical operations
6666-
if (leftVal == TrueObject & nodeType == ExpressionType.OrElse ||
6667-
leftVal == FalseObject & nodeType == ExpressionType.AndAlso)
6656+
// Short circuit the interpretation, because this is an actual logic of these logical operations
6657+
if (result == TrueObject & nodeType == ExpressionType.OrElse ||
6658+
result == FalseObject & nodeType == ExpressionType.AndAlso)
6659+
return true;
6660+
6661+
// If the first part is not enough to decide of the expression result, go right
6662+
var right = binaryExpr.Right;
6663+
if (right is ConstantExpression rc)
66686664
{
6669-
result = leftVal;
6665+
#if LIGHT_EXPRESSION
6666+
if (rc.RefField != null) return false;
6667+
#endif
6668+
result = (bool)rc.Value ? TrueObject : FalseObject;
66706669
return true;
66716670
}
6672-
return TryInterpretPrimitive(out result, binaryExpr.Right);
6671+
return TryInterpretPrimitive(out result, right);
66736672
}
66746673

6675-
if (IsComparison(nodeType))
6674+
var isComparison = IsComparison(nodeType);
6675+
if (isComparison || IsArithmetic(nodeType))
66766676
{
66776677
var binaryExpr = (BinaryExpression)expr;
6678-
if (!TryInterpretPrimitive(out var leftVal, binaryExpr.Left) ||
6679-
!TryInterpretPrimitive(out var rightVal, binaryExpr.Right))
6678+
6679+
// Interpreting left part
6680+
var left = binaryExpr.Left;
6681+
object leftVal = null;
6682+
if (left is ConstantExpression lc)
6683+
{
6684+
#if LIGHT_EXPRESSION
6685+
if (lc.RefField != null) return false;
6686+
#endif
6687+
leftVal = lc.Value;
6688+
}
6689+
else if (!TryInterpretPrimitive(out leftVal, left))
66806690
return false;
66816691

6682-
if (nodeType == ExpressionType.Equal | nodeType == ExpressionType.NotEqual)
6692+
// Interpreting right part
6693+
var right = binaryExpr.Right;
6694+
object rightVal = null;
6695+
if (right is ConstantExpression rc)
66836696
{
6684-
var boolVal = leftVal.Equals(rightVal);
6685-
result = nodeType == ExpressionType.Equal
6686-
? (boolVal ? TrueObject : FalseObject)
6687-
: (boolVal ? FalseObject : TrueObject);
6697+
#if LIGHT_EXPRESSION
6698+
if (rc.RefField != null) return false;
6699+
#endif
6700+
rightVal = rc.Value;
66886701
}
6689-
else
6702+
else if (!TryInterpretPrimitive(out rightVal, right))
6703+
return false;
6704+
6705+
// Now do the operation on the left and right
6706+
if (isComparison)
66906707
{
6691-
// Assuming that the both sides are of the same type, we can use only the left one for comparison
6692-
var cmp = leftVal as IComparable;
6693-
if (cmp == null)
6694-
return false;
6695-
var res = cmp.CompareTo(rightVal);
6696-
var boolVal = nodeType switch
6708+
if (nodeType == ExpressionType.Equal | nodeType == ExpressionType.NotEqual)
6709+
{
6710+
var boolVal = leftVal.Equals(rightVal);
6711+
result = nodeType == ExpressionType.Equal
6712+
? (boolVal ? TrueObject : FalseObject)
6713+
: (boolVal ? FalseObject : TrueObject);
6714+
}
6715+
else
66976716
{
6698-
ExpressionType.GreaterThan => res > 0,
6699-
ExpressionType.GreaterThanOrEqual => res >= 0,
6700-
ExpressionType.LessThan => res < 0,
6701-
ExpressionType.LessThanOrEqual => res <= 0,
6702-
_ => UnreachableCase<bool>(),
6703-
};
6704-
result = boolVal ? TrueObject : FalseObject;
6717+
// Assuming that the both sides are of the same type, we can use only the left one for comparison
6718+
var cmp = leftVal as IComparable;
6719+
if (cmp == null)
6720+
return false;
6721+
var res = cmp.CompareTo(rightVal);
6722+
var boolVal = nodeType switch
6723+
{
6724+
ExpressionType.GreaterThan => res > 0,
6725+
ExpressionType.GreaterThanOrEqual => res >= 0,
6726+
ExpressionType.LessThan => res < 0,
6727+
ExpressionType.LessThanOrEqual => res <= 0,
6728+
_ => UnreachableCase<bool>(),
6729+
};
6730+
result = boolVal ? TrueObject : FalseObject;
6731+
}
6732+
return true;
67056733
}
6706-
return true;
6734+
6735+
// For Arithmetic
6736+
result = DoArithmeticOrNull(leftVal, rightVal, nodeType);
6737+
return result != null;
67076738
}
67086739

67096740
if (nodeType == ExpressionType.Negate)
67106741
{
67116742
var unaryExpr = (UnaryExpression)expr;
6712-
if (!TryInterpretPrimitive(out var val, unaryExpr.Operand))
6743+
var operandExpr = unaryExpr.Operand;
6744+
object val = null;
6745+
if (operandExpr is ConstantExpression co)
6746+
{
6747+
#if LIGHT_EXPRESSION
6748+
if (co.RefField != null) return false;
6749+
#endif
6750+
val = co.Value;
6751+
}
6752+
if (!TryInterpretPrimitive(out val, operandExpr))
67136753
return false;
67146754
result = DoNegateOrNull(val);
67156755
return result != null;
67166756
}
67176757

6718-
if (IsArithmetic(nodeType))
6758+
if (expr is ConstantExpression constExpr)
67196759
{
6720-
var binaryExpr = (BinaryExpression)expr;
6721-
if (!TryInterpretPrimitive(out var leftVal, binaryExpr.Left) ||
6722-
!TryInterpretPrimitive(out var rightVal, binaryExpr.Right))
6723-
return false;
6760+
#if LIGHT_EXPRESSION
6761+
if (constExpr.RefField != null) return false;
6762+
#endif
6763+
result = constExpr.Value;
6764+
if (expr.Type == typeof(bool))
6765+
result = (bool)result ? TrueObject : FalseObject;
6766+
return true;
6767+
}
67246768

6725-
result = DoArithmeticOrNull(leftVal, rightVal, nodeType);
6726-
return result != null;
6769+
if (nodeType == ExpressionType.Default)
6770+
{
6771+
result = GetZeroDefaultObject(Type.GetTypeCode(expr.Type));
6772+
return true;
6773+
}
6774+
6775+
if (nodeType == ExpressionType.Convert)
6776+
{
6777+
var unaryExpr = (UnaryExpression)expr;
6778+
var operandExpr = unaryExpr.Operand;
6779+
6780+
if (!TryInterpretPrimitive(out result, operandExpr))
6781+
return false;
6782+
var exprType = expr.Type;
6783+
if (operandExpr.Type != exprType && !exprType.IsAssignableFrom(operandExpr.Type))
6784+
{
6785+
var converted = System.Convert.ChangeType(result, exprType);
6786+
result = exprType != typeof(bool) ? converted : (bool)converted ? TrueObject : FalseObject;
6787+
}
6788+
return true;
67276789
}
67286790

67296791
result = null;

0 commit comments

Comments
 (0)