Skip to content

Commit

Permalink
Implement the JIT part of the dynamic dictionary expansion feature. (#…
Browse files Browse the repository at this point in the history
…31957)

* Implement the JIT part of the dynamic dictionary expansion feature.

* review response.

* response review
  • Loading branch information
Sergey Andreenko authored Feb 13, 2020
1 parent 134112a commit a06b447
Show file tree
Hide file tree
Showing 6 changed files with 326 additions and 39 deletions.
15 changes: 15 additions & 0 deletions src/coreclr/src/jit/compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9102,6 +9102,21 @@ void cTreeFlags(Compiler* comp, GenTree* tree)
{
chars += printf("[CALL_M_PINVOKE]");
}

if (call->IsFatPointerCandidate())
{
chars += printf("[CALL_FAT_POINTER_CANDIDATE]");
}

if (call->IsGuarded())
{
chars += printf("[CALL_GUARDED]");
}

if (call->IsExpRuntimeLookup())
{
chars += printf("[CALL_EXP_RUNTIME_LOOKUP]");
}
}
break;
default:
Expand Down
36 changes: 27 additions & 9 deletions src/coreclr/src/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -2447,7 +2447,7 @@ class Compiler
// For binary opers.
GenTree* gtNewOperNode(genTreeOps oper, var_types type, GenTree* op1, GenTree* op2);

GenTree* gtNewQmarkNode(var_types type, GenTree* cond, GenTree* colon);
GenTreeQmark* gtNewQmarkNode(var_types type, GenTree* cond, GenTree* colon);

GenTree* gtNewLargeOperNode(genTreeOps oper,
var_types type = TYP_I_IMPL,
Expand Down Expand Up @@ -6326,14 +6326,15 @@ class Compiler
}
};

#define OMF_HAS_NEWARRAY 0x00000001 // Method contains 'new' of an array
#define OMF_HAS_NEWOBJ 0x00000002 // Method contains 'new' of an object type.
#define OMF_HAS_ARRAYREF 0x00000004 // Method contains array element loads or stores.
#define OMF_HAS_VTABLEREF 0x00000008 // Method contains method table reference.
#define OMF_HAS_NULLCHECK 0x00000010 // Method contains null check.
#define OMF_HAS_FATPOINTER 0x00000020 // Method contains call, that needs fat pointer transformation.
#define OMF_HAS_OBJSTACKALLOC 0x00000040 // Method contains an object allocated on the stack.
#define OMF_HAS_GUARDEDDEVIRT 0x00000080 // Method contains guarded devirtualization candidate
#define OMF_HAS_NEWARRAY 0x00000001 // Method contains 'new' of an array
#define OMF_HAS_NEWOBJ 0x00000002 // Method contains 'new' of an object type.
#define OMF_HAS_ARRAYREF 0x00000004 // Method contains array element loads or stores.
#define OMF_HAS_VTABLEREF 0x00000008 // Method contains method table reference.
#define OMF_HAS_NULLCHECK 0x00000010 // Method contains null check.
#define OMF_HAS_FATPOINTER 0x00000020 // Method contains call, that needs fat pointer transformation.
#define OMF_HAS_OBJSTACKALLOC 0x00000040 // Method contains an object allocated on the stack.
#define OMF_HAS_GUARDEDDEVIRT 0x00000080 // Method contains guarded devirtualization candidate
#define OMF_HAS_EXPRUNTIMELOOKUP 0x00000100 // Method contains a runtime lookup to an expandable dictionary.

bool doesMethodHaveFatPointer()
{
Expand Down Expand Up @@ -6373,6 +6374,23 @@ class Compiler
unsigned methodAttr,
unsigned classAttr);

bool doesMethodHaveExpRuntimeLookup()
{
return (optMethodFlags & OMF_HAS_EXPRUNTIMELOOKUP) != 0;
}

void setMethodHasExpRuntimeLookup()
{
optMethodFlags |= OMF_HAS_EXPRUNTIMELOOKUP;
}

void clearMethodHasExpRuntimeLookup()
{
optMethodFlags &= ~OMF_HAS_EXPRUNTIMELOOKUP;
}

void addExpRuntimeLookupCandidate(GenTreeCall* call);

unsigned optMethodFlags;

bool doesMethodHaveNoReturnCalls()
Expand Down
4 changes: 2 additions & 2 deletions src/coreclr/src/jit/gentree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5670,11 +5670,11 @@ GenTree* Compiler::gtNewOperNode(genTreeOps oper, var_types type, GenTree* op1,
return node;
}

GenTree* Compiler::gtNewQmarkNode(var_types type, GenTree* cond, GenTree* colon)
GenTreeQmark* Compiler::gtNewQmarkNode(var_types type, GenTree* cond, GenTree* colon)
{
compQmarkUsed = true;
cond->gtFlags |= GTF_RELOP_QMARK;
GenTree* result = new (this, GT_QMARK) GenTreeQmark(type, cond, colon, this);
GenTreeQmark* result = new (this, GT_QMARK) GenTreeQmark(type, cond, colon, this);
#ifdef DEBUG
if (compQmarkRationalized)
{
Expand Down
16 changes: 16 additions & 0 deletions src/coreclr/src/jit/gentree.h
Original file line number Diff line number Diff line change
Expand Up @@ -3919,6 +3919,7 @@ struct GenTreeCall final : public GenTree
#define GTF_CALL_M_GUARDED 0x00200000 // GT_CALL -- this call was transformed by guarded devirtualization
#define GTF_CALL_M_ALLOC_SIDE_EFFECTS 0x00400000 // GT_CALL -- this is a call to an allocator with side effects
#define GTF_CALL_M_SUPPRESS_GC_TRANSITION 0x00800000 // GT_CALL -- suppress the GC transition (i.e. during a pinvoke) but a separate GC safe point is required.
#define GTF_CALL_M_EXP_RUNTIME_LOOKUP 0x01000000 // GT_CALL -- this call needs to be tranformed into CFG for the dynamic dictionary expansion feature.

// clang-format on

Expand Down Expand Up @@ -4168,6 +4169,21 @@ struct GenTreeCall final : public GenTree
gtCallMoreFlags |= GTF_CALL_M_GUARDED;
}

void SetExpRuntimeLookup()
{
gtFlags |= GTF_CALL_M_EXP_RUNTIME_LOOKUP;
}

void ClearExpRuntimeLookup()
{
gtFlags &= ~GTF_CALL_M_EXP_RUNTIME_LOOKUP;
}

bool IsExpRuntimeLookup() const
{
return (gtFlags & GTF_CALL_M_EXP_RUNTIME_LOOKUP) != 0;
}

unsigned gtCallMoreFlags; // in addition to gtFlags

unsigned char gtCallType : 3; // value from the gtCallTypes enumeration
Expand Down
70 changes: 52 additions & 18 deletions src/coreclr/src/jit/importer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2061,7 +2061,8 @@ GenTree* Compiler::impRuntimeLookupToTree(CORINFO_RESOLVED_TOKEN* pResolvedToken
nullptr DEBUGARG("impRuntimeLookup slot"));
}

GenTree* indOffTree = nullptr;
GenTree* indOffTree = nullptr;
GenTree* lastIndOfTree = nullptr;

// Applied repeated indirections
for (WORD i = 0; i < pRuntimeLookup->indirections; i++)
Expand All @@ -2086,6 +2087,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)
{
lastIndOfTree = impCloneExpr(slotPtrTree, &slotPtrTree, NO_CLASS_HANDLE, (unsigned)CHECK_SPILL_ALL,
nullptr DEBUGARG("impRuntimeLookup indirectOffset"));
}
#endif // 0

slotPtrTree =
gtNewOperNode(GT_ADD, TYP_I_IMPL, slotPtrTree, gtNewIconNode(pRuntimeLookup->offsets[i], TYP_I_IMPL));
}
Expand Down Expand Up @@ -2140,38 +2150,56 @@ GenTree* Compiler::impRuntimeLookupToTree(CORINFO_RESOLVED_TOKEN* pResolvedToken
impSpillSideEffects(true, CHECK_SPILL_ALL DEBUGARG("bubbling QMark1"));

// Extract the handle
GenTree* handle = gtNewOperNode(GT_IND, TYP_I_IMPL, slotPtrTree);
handle->gtFlags |= GTF_IND_NONFAULTING;

GenTree* handleCopy = impCloneExpr(handle, &handle, NO_CLASS_HANDLE, (unsigned)CHECK_SPILL_ALL,
nullptr DEBUGARG("impRuntimeLookup typehandle"));
GenTree* handleForNullCheck = gtNewOperNode(GT_IND, TYP_I_IMPL, slotPtrTree);
handleForNullCheck->gtFlags |= GTF_IND_NONFAULTING;

// Call to helper
GenTree* argNode = gtNewIconEmbHndNode(pRuntimeLookup->signature, nullptr, GTF_ICON_TOKEN_HDL, compileTimeHandle);

GenTreeCall::Use* helperArgs = gtNewCallArgs(ctxTree, argNode);
GenTree* helperCall = gtNewHelperCallNode(pRuntimeLookup->helper, TYP_I_IMPL, helperArgs);
GenTreeCall* helperCall = gtNewHelperCallNode(pRuntimeLookup->helper, TYP_I_IMPL, helperArgs);

// Check for null and possibly call helper
GenTree* relop = gtNewOperNode(GT_NE, TYP_INT, handle, gtNewIconNode(0, TYP_I_IMPL));
GenTree* nullCheck = gtNewOperNode(GT_NE, TYP_INT, handleForNullCheck, gtNewIconNode(0, TYP_I_IMPL));
GenTree* handleForResult = gtCloneExpr(handleForNullCheck);

GenTree* colon = new (this, GT_COLON) GenTreeColon(TYP_I_IMPL,
gtNewNothingNode(), // do nothing if nonnull
helperCall);
GenTree* result = nullptr;

GenTree* qmark = gtNewQmarkNode(TYP_I_IMPL, relop, colon);

unsigned tmp;
if (handleCopy->IsLocal())
#if 0 // Uncomment that block when you add sizeOffset field to pRuntimeLookup.
if (pRuntimeLookup->sizeOffset != 0xFFFF) // dynamic dictionary expansion feature
{
tmp = handleCopy->AsLclVarCommon()->GetLclNum();
assert((lastIndOfTree != nullptr) && (pRuntimeLookup->indirections > 0));

// sizeValue = dictionary[pRuntimeLookup->sizeOffset]
GenTreeIntCon* sizeOffset = gtNewIconNode(pRuntimeLookup->sizeOffset, TYP_I_IMPL);
GenTree* sizeValueOffset = gtNewOperNode(GT_ADD, TYP_I_IMPL, lastIndOfTree, sizeOffset);
GenTree* sizeValue = gtNewOperNode(GT_IND, TYP_I_IMPL, sizeValueOffset);

// sizeCheck fails if sizeValue < pRuntimeLookup->offsets[i]
GenTree* offsetValue = gtNewIconNode(pRuntimeLookup->offsets[pRuntimeLookup->indirections - 1], TYP_I_IMPL);
GenTree* sizeCheck = gtNewOperNode(GT_LE, TYP_INT, sizeValue, offsetValue);

// revert null check condition.
nullCheck->ChangeOperUnchecked(GT_EQ);

// ((sizeCheck fails || nullCheck fails))) ? (helperCall : handle).
// Add checks and the handle as call arguments, indirect call transformer will handle this.
helperCall->gtCallArgs = gtPrependNewCallArg(handleForResult, helperCall->gtCallArgs);
helperCall->gtCallArgs = gtPrependNewCallArg(sizeCheck, helperCall->gtCallArgs);
helperCall->gtCallArgs = gtPrependNewCallArg(nullCheck, helperCall->gtCallArgs);
result = helperCall;
addExpRuntimeLookupCandidate(helperCall);
}
else
#endif // 0
{
tmp = lvaGrabTemp(true DEBUGARG("spilling QMark1"));
GenTreeColon* colonNullCheck = new (this, GT_COLON) GenTreeColon(TYP_I_IMPL, handleForResult, helperCall);
result = gtNewQmarkNode(TYP_I_IMPL, nullCheck, colonNullCheck);
}

impAssignTempGen(tmp, qmark, (unsigned)CHECK_SPILL_NONE);
unsigned tmp = lvaGrabTemp(true DEBUGARG("spilling Runtime Lookup tree"));

impAssignTempGen(tmp, result, (unsigned)CHECK_SPILL_NONE);
return gtNewLclvNode(tmp, TYP_I_IMPL);
}

Expand Down Expand Up @@ -20851,6 +20879,12 @@ void Compiler::addGuardedDevirtualizationCandidate(GenTreeCall* call,
call->gtGuardedDevirtualizationCandidateInfo = pInfo;
}

void Compiler::addExpRuntimeLookupCandidate(GenTreeCall* call)
{
setMethodHasExpRuntimeLookup();
call->SetExpRuntimeLookup();
}

//------------------------------------------------------------------------
// impIsClassExact: check if a class handle can only describe values
// of exactly one class.
Expand Down
Loading

0 comments on commit a06b447

Please sign in to comment.