Skip to content

Conversation

jogibear9988
Copy link
Contributor

No description provided.

@dadhi
Copy link
Owner

dadhi commented Sep 8, 2018

Great idea!


// Check TryEmitInvertedNullComparison is used
var il = ILReaderFactory.Create(convert1.Method).ToList();
Assert.AreEqual(il[0].OpCode, OpCodes.Ldarg_0);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May be even simplified by CollectionAssert.AreEqual()

@jogibear9988
Copy link
Contributor Author

I'm still workin on one more linq2db test.

But there is see, MsCompiler can also line parameterd lambdas

@jogibear9988
Copy link
Contributor Author

I've now a Problem with ldarga.s

In the Lambda a Method is Called "ConvertString" wich gets a Int as a Parameter wich is a argument.

Our Code now emits ldarga.s but Microsoft code emits ldarg.s

                 var asAddress = (parent == ExpressionType.Call || parent == ExpressionType.MemberAccess) && paramExpr.Type.IsValueType() && !paramExpr.IsByRef;

This now returns true but should return false, any idea how to fix?

@jogibear9988
Copy link
Contributor Author

I've added a test

@jogibear9988
Copy link
Contributor Author

I've added a fix, don't know if it's correct ;-(

And if it's correct I think we need to hand over this parameter through every try emit call (like the others)

@dadhi
Copy link
Owner

dadhi commented Sep 8, 2018

And if it's correct I think we need to hand over this parameter through every try emit call (like the others)

I have started the work on combining all this parameters into enum flags. So far, so good. Much easy to trace the code and find the patterns - and simplify some parts.

So this new parameter will be just an another flag, no need to change parameters through all calls.

Plus I have started to inline things were possible. e.g. TryEmitMany and TryEmitBinary. It localize the code checks, in some places they even not required anymore.

Will see how it goes.

@jogibear9988
Copy link
Contributor Author

do not yet merge, I'm workin on an issue with struct types when ldflda instead of lddld has tu be used. But I need to find out when

@dadhi
Copy link
Owner

dadhi commented Sep 8, 2018

Won't do. May be will ask you for rebase when I done with my changes.

@jogibear9988
Copy link
Contributor Author

Info when to use Ldloc vs LdlocA https://stackoverflow.com/questions/4273331/ldloc-or-ldloca

@jogibear9988
Copy link
Contributor Author

Only one Linq2db test atm now fails on my system.

But 3 times I thought now I had the problem and it's still there. (It's a huge Expression, so the 3 problems were errors we had, but there are still some left)

@jogibear9988
Copy link
Contributor Author

@dadhi how far did you get with your refactoring?

@dadhi
Copy link
Owner

dadhi commented Sep 10, 2018

@dadhi how far did you get with your refactoring?

I have finished mechanically, but a big amount of test fails. Will try to fix, and optimize my refactoring tomorrow.

@dadhi
Copy link
Owner

dadhi commented Sep 11, 2018

@jogibear9988
I have finished my refactoring.
Summing it up:

  • I have introduced extensible ParentFlags enum and corresponding parent parameter in TryEmit.. replacing old parent, ignoreResult, isMemberAccess parameters.
  • TryEmit.. methods where inlined where possible to minimize call-stack, as a result, TryEmitMany and TryEmitBinary disappeared. Other small methods were inlined too.
  • Type expr parameter was removed from TryEmit.. as easily acquired from expression.

The problem will be to merge this PR, cause some methods were inlined.
Maybe just go change by change commit, re-adding them together with tests.

@jogibear9988
Copy link
Contributor Author

i look to fix tonight

var byRefIndex = argExprs[i].Type.IsByRef ? i : -1;
if (!TryEmit(argExprs[i], paramExprs, il, ref closure, parent & ~ParentFlags.IgnoreResult, byRefIndex))
return false;
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dzmitry-lahoda do you think this is a correct fix ?

IgnoreResult = 1 << 1,
Call = 1 << 2,
MemberAccess = 1 << 3,
MemberAccess = 1 << 3, // Any Parent Expression is a MemberExpression
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Regarding your comment here, parent flags currently are propagated through nested expressions until changed. That means it is more of an ancestor than a parent.

But we may reset the parent in TryEmit when we go deeper. It was my initial variant. Then I removed the reset and nothing changed. So if you found test where it does not work, we may need return reset back:

internal static ParentFlags Reset(ParentFlags p) =>
    p & (ParentFlags.IgnoreResult);

Should we preserve IsInstanceAccess as well?


public bool HasClosure => ClosureType != null;

public bool LastEmitIsAddress;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we make it into ParentFlags?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it's used from the Caller

var notExpr = (UnaryExpression)expr;
if (!TryEmit(notExpr.Operand, paramExprs, il, ref closure, parent))
return false;
if ((parent & ParentFlags.IgnoreResult) > 0)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This IL emit may be moved to a method.


var asAddress =
paramType.IsValueType() && !paramExpr.IsByRef &&
(parent & (ParentFlags.Call | ParentFlags.MemberAccess)) != 0;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, my fail :(
Thanks for fixing.

var locT = il.DeclareLocal(targetType);
il.Emit(OpCodes.Stloc_S, loc);
il.Emit(OpCodes.Ldloca_S, loc);
var hasValueMethod = sourceTypeInfo.GetDeclaredMethod("get_HasValue");
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a method for this in Tools.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Name?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FindNullableHasValueGetterMethod or something

return false;
il.Emit(OpCodes.Brfalse, labelFalse);
il.Emit(OpCodes.Ldloca_S, loc);
var mthValue = sourceTypeInfo.GetDeclaredMethods("GetValueOrDefault")
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

..the same, there is method in Tools.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Name?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FindNullableGetValueOrDefaultMethod or something

@jogibear9988
Copy link
Contributor Author

I still have a bug in the Linq2db Tests, wich I need to find.

So do not merge yet

@dadhi
Copy link
Owner

dadhi commented Sep 12, 2018

ok

var idx = argExprs[i].Type.IsByRef ? i : -1;
if (!TryEmit(argExprs[i], paramExprs, il, ref closure, parent, idx))
return false;
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dzmitry-lahoda again, is this correct fix for new with byref?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep. Originally I missed that in .NET there is more then one way to call method (usual, and via delegate and via ctor).

@dadhi
Copy link
Owner

dadhi commented Sep 12, 2018

the following 2 Il commands are missing in FEC

image

The code is required because of System.Compile represents Closure as object[] in this case.
To load value type DateTime from the object array, it loads the index of an array, then loads the reference to an element in the array by index, and because the element is an object, it unboxes it into DateTime.

FEC up to some number of items represents closure as typed Closure<T1, ... Tn> object. Therefore the field load is enough.

But after some amount of items, FEC starts to use ArrayClosure which is the same object[] wrapped in the class. So we may need to check this case.

Here the code in constructing closure which decides what to use:

var createMethods = ExpressionCompiler.Closure.CreateMethods;
if (totalItemCount > createMethods.Length)
{
ClosureType = typeof(ArrayClosure);
if (constructTypeOnly)
return null;
var items = new object[totalItemCount];
if (constants.Length != 0)
for (var i = 0; i < constants.Length; i++)
items[i] = constants[i].Value;
// skip non passed parameters as it is only for nested lambdas
if (nestedLambdas.Length != 0)
for (var i = 0; i < nestedLambdas.Length; i++)
items[constPlusParamCount + i] = nestedLambdas[i].Lambda;
return new ArrayClosure(items);
}

You may comment the methods here to enforce ArrayClosure usage and check the tests.

@jogibear9988 jogibear9988 changed the title add the possibility to check the IL in tests [do not merge - yet] add the possibility to check the IL in tests Sep 13, 2018
@dadhi
Copy link
Owner

dadhi commented Sep 14, 2018

@jogibear9988 Hi, it is an amazing thread of work.
Can we merge it now, cause it becomes harder to check and review all things here?

@jogibear9988
Copy link
Contributor Author

You can merge.

linq2db Tests now work except 3, this 3 did also not work before your refactoring. I#m still looking to get them working, but it's a huge expression tree wich I need to pack into a test!

@jogibear9988 jogibear9988 changed the title [do not merge - yet] add the possibility to check the IL in tests add the possibility to check the IL in tests Sep 14, 2018
@jogibear9988
Copy link
Contributor Author

You also then can close the other 2 pull req. I've included them in mine and fixed the issues

@dadhi dadhi merged commit 11191a4 into dadhi:master Sep 14, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants