Skip to content

Commit 76b7c91

Browse files
jogibear9988dadhi
authored andcommitted
Support xxxAssign, and Tests for 91 and 95 (dadhi#96)
* SUpport for AddAssign, xxxAssign ... * Tests for Issues dadhi#91 and dadhi#95 * bugfix always used add * bugfix forgot else * bugfix for dadhi#93 * bugfix enum issue in linq2db tests * bugfix for convert in issue dadhi#83 * bugfix one more test of dadhi#83 * bugfix for failing tests * bugfix disable not working test again * fixed by ref add (probably others) * reenable Tests - they work now * feature refactor code a little bit * bugfix add missing multiply checked * test for dadhi#100 * refactoring of fastexpressioncompiler changes
1 parent cc94d68 commit 76b7c91

File tree

5 files changed

+773
-438
lines changed

5 files changed

+773
-438
lines changed

src/FastExpressionCompiler/FastExpressionCompiler.cs

Lines changed: 163 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -887,7 +887,7 @@ internal static class CurryClosureActions
887887

888888
#region Collect Bound Constants
889889

890-
private static bool IsClosureBoundConstant(object value, TypeInfo type) =>
890+
private static bool IsClosureBoundConstant(object value, TypeInfo type) =>
891891
value is Delegate ||
892892
!type.IsPrimitive && !type.IsEnum && !(value is string) && !(value is Type);
893893

@@ -1310,13 +1310,7 @@ public static bool TryEmit(object exprObj, ExpressionType exprNodeType, Type exp
13101310
case ExpressionType.NotEqual:
13111311
return TryEmitComparison(exprObj, exprNodeType, paramExprs, il, ref closure);
13121312

1313-
case ExpressionType.Add:
1314-
case ExpressionType.AddChecked:
1315-
case ExpressionType.Subtract:
1316-
case ExpressionType.SubtractChecked:
1317-
case ExpressionType.Multiply:
1318-
case ExpressionType.MultiplyChecked:
1319-
case ExpressionType.Divide:
1313+
case ExpressionType arithmetic when Tools.IsArithmetic(arithmetic):
13201314
return TryEmitArithmeticOperation(exprObj, exprType, exprNodeType, paramExprs, il, ref closure);
13211315

13221316
case ExpressionType.AndAlso:
@@ -1329,7 +1323,8 @@ public static bool TryEmit(object exprObj, ExpressionType exprNodeType, Type exp
13291323
case ExpressionType.Conditional:
13301324
return TryEmitConditional((ConditionalExpression)exprObj, paramExprs, il, ref closure);
13311325

1332-
case ExpressionType.Assign:
1326+
case ExpressionType arithmeticAssign when Tools.GetArithmeticAssignOrSelf(arithmeticAssign) != arithmeticAssign:
1327+
case ExpressionType.Assign:
13331328
return TryEmitAssign(exprObj, exprType, paramExprs, il, ref closure);
13341329

13351330
case ExpressionType.Block:
@@ -1655,9 +1650,12 @@ private static bool TryEmitParameter(object paramExprObj, Type paramType,
16551650
{
16561651
if (closure.HasClosure)
16571652
paramIndex += 1; // shift parameter indices by one, because the first one will be closure
1658-
1659-
var asAddress = parent == ExpressionType.Call && paramType.GetTypeInfo().IsValueType && !Tools.IsByRefParameter(paramExprObj);
1653+
var isByRef = Tools.IsByRefParameter(paramExprObj);
1654+
var asAddress = parent == ExpressionType.Call && paramType.GetTypeInfo().IsValueType && !isByRef;
16601655
EmitLoadParamArg(il, paramIndex, asAddress);
1656+
if (isByRef && Tools.IsArithmetic(parent))
1657+
EmitDereference(il, paramType);
1658+
16611659
return true;
16621660
}
16631661

@@ -1692,6 +1690,35 @@ private static bool TryEmitParameter(object paramExprObj, Type paramType,
16921690
return true;
16931691
}
16941692

1693+
private static void EmitDereference(ILGenerator il, Type type)
1694+
{
1695+
if (type == typeof(Int32))
1696+
il.Emit(OpCodes.Ldind_I4);
1697+
else if (type == typeof(Int64))
1698+
il.Emit(OpCodes.Ldind_I8);
1699+
else if (type == typeof(Int16))
1700+
il.Emit(OpCodes.Ldind_I2);
1701+
else if (type == typeof(SByte))
1702+
il.Emit(OpCodes.Ldind_I1);
1703+
else if (type == typeof(Single))
1704+
il.Emit(OpCodes.Ldind_R4);
1705+
else if (type == typeof(Double))
1706+
il.Emit(OpCodes.Ldind_R8);
1707+
else if (type == typeof(IntPtr))
1708+
il.Emit(OpCodes.Ldind_I);
1709+
else if (type == typeof(UIntPtr))
1710+
il.Emit(OpCodes.Ldind_I);
1711+
else if (type == typeof(Byte))
1712+
il.Emit(OpCodes.Ldind_U1);
1713+
else if (type == typeof(UInt16))
1714+
il.Emit(OpCodes.Ldind_U2);
1715+
else if (type == typeof(UInt32))
1716+
il.Emit(OpCodes.Ldind_U4);
1717+
else
1718+
il.Emit(OpCodes.Ldobj, type);
1719+
//TODO: UInt64 as there is no OpCodes? Ldind_Ref?
1720+
}
1721+
16951722
// loads argument at paramIndex onto evaluation stack
16961723
private static void EmitLoadParamArg(ILGenerator il, int paramIndex, bool asAddress)
16971724
{
@@ -1774,6 +1801,11 @@ private static bool TryEmitConvert(object exprObj, Type targetType,
17741801
object[] paramExprs, ILGenerator il, ref ClosureInfo closure)
17751802
{
17761803
var e = exprObj.GetOperandExprInfo();
1804+
1805+
var m = exprObj is UnaryExpression ue ? ue.Method : ((UnaryExpressionInfo)exprObj).Method;
1806+
if (m != null && m.Name[2] != '_' && m.Name != "op_Implicit" && m.Name != "op_Explicit")
1807+
return TryEmit(e.Expr, e.NodeType, e.Type, paramExprs, il, ref closure, ExpressionType.Call, 0) && EmitMethodCall(il, m);
1808+
17771809
if (!TryEmit(e.Expr, e.NodeType, e.Type, paramExprs, il, ref closure, ExpressionType.Convert))
17781810
return false;
17791811

@@ -1812,30 +1844,36 @@ private static bool TryEmitConvert(object exprObj, Type targetType,
18121844
// Conversion to Nullable: new Nullable<T>(T val);
18131845
else if (targetTypeInfo.IsGenericType && targetTypeInfo.GetGenericTypeDefinition() == typeof(Nullable<>))
18141846
il.Emit(OpCodes.Newobj, targetType.GetConstructorByArgs(targetTypeInfo.GenericTypeArguments[0]));
1815-
1816-
else if (targetType == typeof(int))
1817-
il.Emit(OpCodes.Conv_I4);
1818-
else if (targetType == typeof(float))
1819-
il.Emit(OpCodes.Conv_R4);
1820-
else if (targetType == typeof(uint))
1821-
il.Emit(OpCodes.Conv_U4);
1822-
else if (targetType == typeof(sbyte))
1823-
il.Emit(OpCodes.Conv_I1);
1824-
else if (targetType == typeof(byte))
1825-
il.Emit(OpCodes.Conv_U1);
1826-
else if (targetType == typeof(short))
1827-
il.Emit(OpCodes.Conv_I2);
1828-
else if (targetType == typeof(ushort))
1829-
il.Emit(OpCodes.Conv_U2);
1830-
else if (targetType == typeof(long))
1831-
il.Emit(OpCodes.Conv_I8);
1832-
else if (targetType == typeof(ulong))
1833-
il.Emit(OpCodes.Conv_U8);
1834-
else if (targetType == typeof(double))
1835-
il.Emit(OpCodes.Conv_R8);
1847+
else
1848+
{
1849+
if (targetType.GetTypeInfo().IsEnum)
1850+
targetType = Enum.GetUnderlyingType(targetType);
1851+
1852+
if (targetType == typeof(int))
1853+
il.Emit(OpCodes.Conv_I4);
1854+
else if (targetType == typeof(float))
1855+
il.Emit(OpCodes.Conv_R4);
1856+
else if (targetType == typeof(uint))
1857+
il.Emit(OpCodes.Conv_U4);
1858+
else if (targetType == typeof(sbyte))
1859+
il.Emit(OpCodes.Conv_I1);
1860+
else if (targetType == typeof(byte))
1861+
il.Emit(OpCodes.Conv_U1);
1862+
else if (targetType == typeof(short))
1863+
il.Emit(OpCodes.Conv_I2);
1864+
else if (targetType == typeof(ushort))
1865+
il.Emit(OpCodes.Conv_U2);
1866+
else if (targetType == typeof(long))
1867+
il.Emit(OpCodes.Conv_I8);
1868+
else if (targetType == typeof(ulong))
1869+
il.Emit(OpCodes.Conv_U8);
1870+
else if (targetType == typeof(double))
1871+
il.Emit(OpCodes.Conv_R8);
1872+
1873+
else // cast as the last resort and let's it fail if unlucky
1874+
il.Emit(OpCodes.Castclass, targetType);
1875+
}
18361876

1837-
else // cast as the last resort and let's it fail if unlucky
1838-
il.Emit(OpCodes.Castclass, targetType);
18391877
return true;
18401878
}
18411879

@@ -2234,7 +2272,7 @@ private static bool TryEmitAssign(object exprObj, Type exprType,
22342272
object[] paramExprs, ILGenerator il, ref ClosureInfo closure)
22352273
{
22362274
object left, right;
2237-
ExpressionType leftNodeType, rightNodeType;
2275+
ExpressionType leftNodeType, rightNodeType, nodeType;
22382276

22392277
var expr = exprObj as BinaryExpression;
22402278
if (expr != null)
@@ -2243,6 +2281,7 @@ private static bool TryEmitAssign(object exprObj, Type exprType,
22432281
right = expr.Right;
22442282
leftNodeType = expr.Left.NodeType;
22452283
rightNodeType = expr.Right.NodeType;
2284+
nodeType = expr.NodeType;
22462285
}
22472286
else
22482287
{
@@ -2251,6 +2290,7 @@ private static bool TryEmitAssign(object exprObj, Type exprType,
22512290
right = info.Right;
22522291
leftNodeType = left.GetNodeType();
22532292
rightNodeType = right.GetNodeType();
2293+
nodeType = info.NodeType;
22542294
}
22552295

22562296
// if this assignment is part of a single body-less expression or the result of a block
@@ -2275,8 +2315,16 @@ private static bool TryEmitAssign(object exprObj, Type exprType,
22752315
if (Tools.IsByRefParameter(left))
22762316
{
22772317
EmitLoadParamArg(il, paramIndex, false);
2278-
if (!TryEmit(right, rightNodeType, exprType, paramExprs, il, ref closure, ExpressionType.Assign))
2318+
2319+
var newExprType = Tools.GetArithmeticAssignOrSelf(nodeType);
2320+
if (newExprType != nodeType)
2321+
{
2322+
if (!TryEmit(exprObj, newExprType, exprType, paramExprs, il, ref closure, ExpressionType.Assign))
2323+
return false;
2324+
}
2325+
else if (!TryEmit(right, rightNodeType, exprType, paramExprs, il, ref closure, ExpressionType.Assign))
22792326
return false;
2327+
22802328
EmitByRefStore(il, left.ToExpression().Type);
22812329
}
22822330
else
@@ -2466,8 +2514,8 @@ private static void EmitByRefStore(ILGenerator il, Type type)
24662514
il.Emit(OpCodes.Stind_Ref);
24672515
else if (type == typeof(IntPtr) || type == typeof(UIntPtr))
24682516
il.Emit(OpCodes.Stind_I);
2469-
else
2470-
throw new NotImplementedException();
2517+
else
2518+
il.Emit(OpCodes.Stobj, type);
24712519
}
24722520

24732521
private static bool TryEmitIndexAssign(IndexExpression indexExpr, Type instType, Type elementType, ILGenerator il)
@@ -2539,7 +2587,6 @@ private static bool TryEmitMethodCall(object exprObj, object[] paramExprs, ILGen
25392587
return EmitMethodCall(il, method);
25402588
}
25412589

2542-
25432590
// if call is done into byref method parameters there is no indicators in tree, so grab that from method
25442591
// current approach is to copy into new list only if there are by ref with by ref parameters,
25452592
// possible approach to store hit map of small size (possible 256 bit #89) to check if parameter is by ref
@@ -2845,6 +2892,9 @@ private static bool TryEmitComparison(object exprObj, ExpressionType exprNodeTyp
28452892
{
28462893
leftOpType = expr.Left.Type;
28472894
rightOpType = expr.Right.Type;
2895+
2896+
if (expr.Right is ConstantExpression c && c.Value == null && expr.Right.Type == typeof(object))
2897+
rightOpType = leftOpType;
28482898
}
28492899
else
28502900
{
@@ -2930,7 +2980,7 @@ var methodName
29302980
private static bool TryEmitArithmeticOperation(object exprObj, Type exprType, ExpressionType exprNodeType,
29312981
object[] paramExprs, ILGenerator il, ref ClosureInfo closure)
29322982
{
2933-
if (!EmitBinary(exprObj, paramExprs, il, ref closure, ExpressionType.Default))
2983+
if (!EmitBinary(exprObj, paramExprs, il, ref closure, exprNodeType))
29342984
return false;
29352985

29362986
var exprTypeInfo = exprType.GetTypeInfo();
@@ -2942,7 +2992,9 @@ var methodName
29422992
: exprNodeType == ExpressionType.Subtract ? "op_Subtraction"
29432993
: exprNodeType == ExpressionType.SubtractChecked ? "op_Subtraction"
29442994
: exprNodeType == ExpressionType.Multiply ? "op_Multiply"
2995+
: exprNodeType == ExpressionType.MultiplyChecked ? "op_Multiply"
29452996
: exprNodeType == ExpressionType.Divide ? "op_Division"
2997+
: exprNodeType == ExpressionType.Modulo ? "op_Modulus"
29462998
: null;
29472999

29483000
var method = methodName != null ? exprTypeInfo.GetDeclaredMethod(methodName) : null;
@@ -2952,30 +3004,37 @@ var methodName
29523004
switch (exprNodeType)
29533005
{
29543006
case ExpressionType.Add:
3007+
case ExpressionType.AddAssign:
29553008
il.Emit(OpCodes.Add);
29563009
return true;
29573010

29583011
case ExpressionType.AddChecked:
3012+
case ExpressionType.AddAssignChecked:
29593013
il.Emit(IsUnsigned(exprType) ? OpCodes.Add_Ovf_Un : OpCodes.Add_Ovf);
29603014
return true;
29613015

29623016
case ExpressionType.Subtract:
3017+
case ExpressionType.SubtractAssign:
29633018
il.Emit(OpCodes.Sub);
29643019
return true;
29653020

29663021
case ExpressionType.SubtractChecked:
3022+
case ExpressionType.SubtractAssignChecked:
29673023
il.Emit(IsUnsigned(exprType) ? OpCodes.Sub_Ovf_Un : OpCodes.Sub_Ovf);
29683024
return true;
29693025

29703026
case ExpressionType.Multiply:
3027+
case ExpressionType.MultiplyAssign:
29713028
il.Emit(OpCodes.Mul);
29723029
return true;
29733030

29743031
case ExpressionType.MultiplyChecked:
3032+
case ExpressionType.MultiplyAssignChecked:
29753033
il.Emit(IsUnsigned(exprType) ? OpCodes.Mul_Ovf_Un : OpCodes.Mul_Ovf);
29763034
return true;
29773035

29783036
case ExpressionType.Divide:
3037+
case ExpressionType.DivideAssign:
29793038
il.Emit(OpCodes.Div);
29803039
return true;
29813040
}
@@ -3128,16 +3187,64 @@ internal static object GetLastBlockExpression(object exprObj)
31283187
return null;
31293188
}
31303189

3131-
internal static bool IsByRefAssign(object exprObj)
3190+
//TODO: test what is faster? Copy and inline switch? Switch in method? Ors in method?
3191+
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
3192+
internal static bool IsArithmetic(ExpressionType arithmetic) =>
3193+
arithmetic == ExpressionType.Add
3194+
|| arithmetic == ExpressionType.AddChecked
3195+
|| arithmetic == ExpressionType.Subtract
3196+
|| arithmetic == ExpressionType.SubtractChecked
3197+
|| arithmetic == ExpressionType.Multiply
3198+
|| arithmetic == ExpressionType.MultiplyChecked
3199+
|| arithmetic == ExpressionType.Divide
3200+
|| arithmetic == ExpressionType.Modulo;
3201+
3202+
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
3203+
internal static ExpressionType GetArithmeticAssignOrSelf(ExpressionType arithmetic)
31323204
{
3133-
var exprInfo = exprObj as BinaryExpressionInfo;
3134-
if (exprInfo != null && exprInfo.NodeType == ExpressionType.Assign)
3135-
return IsByRefParameter(exprInfo.Left);
3205+
switch (arithmetic)
3206+
{
3207+
case ExpressionType.AddAssign: return ExpressionType.Add;
3208+
case ExpressionType.AddAssignChecked: return ExpressionType.AddChecked;
3209+
case ExpressionType.SubtractAssign: return ExpressionType.Subtract;
3210+
case ExpressionType.SubtractAssignChecked: return ExpressionType.SubtractChecked;
3211+
case ExpressionType.MultiplyAssign: return ExpressionType.Multiply;
3212+
case ExpressionType.MultiplyAssignChecked: return ExpressionType.MultiplyChecked;
3213+
case ExpressionType.DivideAssign: return ExpressionType.Divide;
3214+
case ExpressionType.ModuloAssign: return ExpressionType.Modulo;
3215+
}
31363216

3137-
var expr = exprObj as BinaryExpression;
3138-
if (expr != null && expr.NodeType == ExpressionType.Assign)
3139-
return IsByRefParameter(expr.Left);
3217+
return arithmetic;
3218+
}
3219+
3220+
internal static bool IsByRefAssign(object exprObj)
3221+
{
3222+
ExpressionType? nodeType = null;
3223+
object left = null;
3224+
if (exprObj is BinaryExpression expr)
3225+
{
3226+
nodeType = expr.NodeType;
3227+
left = expr.Left;
3228+
}
3229+
else if (exprObj is BinaryExpressionInfo exprInfo)
3230+
{
3231+
nodeType = exprInfo.NodeType;
3232+
left = exprInfo.Left;
3233+
}
31403234

3235+
if (nodeType.HasValue && (nodeType == ExpressionType.Assign
3236+
|| (GetArithmeticAssignOrSelf(nodeType.Value) != nodeType.Value)
3237+
|| nodeType == ExpressionType.AndAssign
3238+
|| nodeType == ExpressionType.ExclusiveOrAssign
3239+
|| nodeType == ExpressionType.LeftShiftAssign
3240+
|| nodeType == ExpressionType.OrAssign
3241+
|| nodeType == ExpressionType.PostDecrementAssign
3242+
|| nodeType == ExpressionType.PostIncrementAssign
3243+
|| nodeType == ExpressionType.PowerAssign
3244+
|| nodeType == ExpressionType.PreDecrementAssign
3245+
|| nodeType == ExpressionType.PreIncrementAssign
3246+
|| nodeType == ExpressionType.RightShiftAssign))
3247+
return IsByRefParameter(left);
31413248
return false;
31423249
}
31433250

@@ -3503,6 +3610,8 @@ public class UnaryExpressionInfo : ExpressionInfo
35033610

35043611
public readonly ExpressionInfo Operand;
35053612

3613+
public readonly MethodInfo Method;
3614+
35063615
public override Expression ToExpression()
35073616
{
35083617
if (NodeType == ExpressionType.Convert)
@@ -3516,6 +3625,13 @@ public UnaryExpressionInfo(ExpressionType nodeType, ExpressionInfo operand, Type
35163625
Operand = operand;
35173626
Type = type;
35183627
}
3628+
3629+
public UnaryExpressionInfo(ExpressionType nodeType, ExpressionInfo operand, MethodInfo method)
3630+
{
3631+
NodeType = nodeType;
3632+
Operand = operand;
3633+
Method = method;
3634+
}
35193635
}
35203636

35213637
public abstract class BinaryExpressionInfo : ExpressionInfo

0 commit comments

Comments
 (0)