Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement the JIT part of the dynamic dictionary expansion feature. #31957

Merged

Conversation

sandreenko
Copy link
Contributor

We would like to merge JIT changes first to allow @fadimounir pass tests when he adds VM changes in his PR.

The feature is described here.

The feature uses the pre-existing mechanism of introducing CFG in IndirectCallTransformer phase after importation.

The jit changes were tested with Fadi's VM changes and tests.

PTAL @dotnet/jit-contrib, @fadimounir

@sandreenko sandreenko added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Feb 8, 2020
@fadimounir
Copy link
Contributor

Looks generally good to me, but I don't know much about this codebase to give any meaningful feedback. Thanks @sandreenko for implementing this!

src/coreclr/src/jit/importer.cpp Outdated Show resolved Hide resolved
DEBUG_DESTROY_NODE(colonNullCheck);

// ((sizeCheck fails || nullCheck fails))) ? (helperCall : handle).
// Add checks and the handle as call arguments, indirect call transformer will handle this.
Copy link
Member

Choose a reason for hiding this comment

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

Seems a little odd to me to model this as a call and not some new IR node type/oper that has the call and other nodes as children.

A new node/oper would be more obvious to check for later if it didn't get transformed for some reason; the call you produce here still looks like legal IR.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In the first prototype, I used double QMARK to get a valid IR from importer, but it required more changes in indirect call transformer (for example rename it to smth not call dependent).
And also, I had to destroy these QMARK in that phase, which was not free.

I am not a fan of adding new GenTree opcodes with such a short lifetime, but we can discuss it.

Copy link
Member

Choose a reason for hiding this comment

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

I think we're going to have more and more of these early-jit-phase only opcodes going forward, and may well want to defer their expansion.

It is often easier to reason about a runtime construct abstractly than reason about what its expansion does (say we want to CSE these lookups -- it will be tricky to do once they've introduced control flow).

I don't want to hold up this PR over this point, we can always come back later and change this aspect, when we've got more motivation to do so.

@@ -8,8 +8,8 @@
#endif

// The IndirectCallTransformer transforms indirect calls that involve fat function
// pointers or guarded devirtualization candidates. These transformations introduce
// control flow and so can't easily be done in the importer.
// pointers, guarded devirtualization candidates, or runtime lookup with dynamic dictionary expansion feature.
Copy link
Member

Choose a reason for hiding this comment

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

We should rename this base class and file, but maybe not just yet.

I have some work in progress that extends this too. Plenty of time to bikeshed on a new name.

@@ -116,6 +116,13 @@ class IndirectCallTransformer
transformer.Run();
count++;
}

if (ContainsExpRuntimeLookup(stmt))
Copy link
Member

Choose a reason for hiding this comment

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

Presumably only one of these Contains... can be true since we require the victim to be at or near the root of the tree. Would it make sense to early out when one fires?

Do we know for sure statement iteration is not messed up by these transformations?

We can exclude drilling into cases here by first looking at the various method flags again, right? Might save a bit of time. That is, if OMF_HAS_FATPOINTER is not set, no need to call ContainsFatCalli, and so on.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Would it make sense to early out when one fires?

It won't hurt, added.

I think if we are planning to add more and want to optimize than we probably should keep lists of statements that need transformations in Compiler class.

@@ -2084,6 +2085,15 @@ GenTree* Compiler::impRuntimeLookupToTree(CORINFO_RESOLVED_TOKEN* pResolvedToken

if (pRuntimeLookup->offsets[i] != 0)
{
// The last indirection could be subject to a size check (dynamic dictionary expansion feature)
#if 0 // Uncomment that block when you add sizeOffset field to pRuntimeLookup.
if (i == pRuntimeLookup->indirections - 1 && pRuntimeLookup->sizeOffset != 0xFFFF)
Copy link
Member

Choose a reason for hiding this comment

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

Should the 0xFFFF be a #define in CorInfo.h? (We don't need to add that now, but it might be good to put something like CORINFO_LOOKUP_DIRECT_OFFSET here instead of the 0xFFFF so that it forces the addition when sizeOffset is added and this block is uncommented.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yep. I can do that with my portion of the changes.

@sandreenko
Copy link
Contributor Author

PR was updated, please take another look.

Copy link
Member

@AndyAyersMS AndyAyersMS left a comment

Choose a reason for hiding this comment

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

Looks good; left one small suggestion.

{
GuardedDevirtualizationTransformer transformer(compiler, block, stmt);
transformer.Run();
count++;
}

if (ContainsExpRuntimeLookup(stmt))
else if (ContainsExpRuntimeLookup(stmt))
Copy link
Member

Choose a reason for hiding this comment

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

I was thinking you'd do something like this:

if (doesMethodHaveExpRuntimeLookup() && ContainsExpRuntimeLookup(stmt))

(and similarly for the other clauses)

DEBUG_DESTROY_NODE(colonNullCheck);

// ((sizeCheck fails || nullCheck fails))) ? (helperCall : handle).
// Add checks and the handle as call arguments, indirect call transformer will handle this.
Copy link
Member

Choose a reason for hiding this comment

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

I think we're going to have more and more of these early-jit-phase only opcodes going forward, and may well want to defer their expansion.

It is often easier to reason about a runtime construct abstractly than reason about what its expansion does (say we want to CSE these lookups -- it will be tricky to do once they've introduced control flow).

I don't want to hold up this PR over this point, we can always come back later and change this aspect, when we've got more motivation to do so.

@sandreenko sandreenko merged commit a06b447 into dotnet:master Feb 13, 2020
@sandreenko sandreenko deleted the MakeDectionaryLayoutDynamicJitPartOnly branch February 13, 2020 00:24
@ghost ghost locked as resolved and limited conversation to collaborators Dec 10, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants