diff options
author | Andy Ayers <andya@microsoft.com> | 2018-12-07 00:12:04 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-12-07 00:12:04 -0800 |
commit | 08be98cd4d13102958b21d8ad4b067f6fee1abb4 (patch) | |
tree | 8368d6569c113e71bf37e307af5e67d5d2053616 /src/jit/gentree.cpp | |
parent | 1c18b3290b825e66e973e147eda8c7cca3e539c6 (diff) | |
download | coreclr-08be98cd4d13102958b21d8ad4b067f6fee1abb4.tar.gz coreclr-08be98cd4d13102958b21d8ad4b067f6fee1abb4.tar.bz2 coreclr-08be98cd4d13102958b21d8ad4b067f6fee1abb4.zip |
JIT: block general cloning of candidate calls (#21418)
Follow-up from #21270 and #21414.
Block general cloning from inadvertently cloning a candidate call.
Add a separate path for cloning calls that are inline and guarded
devirtualization candidates for use by guarded devirtualization.
Callers need to take extra steps after cloning one of these calls
to properly fix everything up.
Also fix up some issues in the large comment for the fat calli
transformation.
Diffstat (limited to 'src/jit/gentree.cpp')
-rw-r--r-- | src/jit/gentree.cpp | 205 |
1 files changed, 131 insertions, 74 deletions
diff --git a/src/jit/gentree.cpp b/src/jit/gentree.cpp index 542a701d65..6d36b0bb75 100644 --- a/src/jit/gentree.cpp +++ b/src/jit/gentree.cpp @@ -7273,82 +7273,14 @@ GenTree* Compiler::gtCloneExpr( case GT_CALL: - copy = new (this, GT_CALL) GenTreeCall(tree->TypeGet()); - - copy->gtCall.gtCallObjp = tree->gtCall.gtCallObjp - ? gtCloneExpr(tree->gtCall.gtCallObjp, addFlags, deepVarNum, deepVarVal) - : nullptr; - copy->gtCall.gtCallArgs = - tree->gtCall.gtCallArgs - ? gtCloneExpr(tree->gtCall.gtCallArgs, addFlags, deepVarNum, deepVarVal)->AsArgList() - : nullptr; - copy->gtCall.gtCallMoreFlags = tree->gtCall.gtCallMoreFlags; - copy->gtCall.gtCallLateArgs = - tree->gtCall.gtCallLateArgs - ? gtCloneExpr(tree->gtCall.gtCallLateArgs, addFlags, deepVarNum, deepVarVal)->AsArgList() - : nullptr; - -#if !FEATURE_FIXED_OUT_ARGS - copy->gtCall.regArgList = tree->gtCall.regArgList; - copy->gtCall.regArgListCount = tree->gtCall.regArgListCount; -#endif - - // The call sig comes from the EE and doesn't change throughout the compilation process, meaning - // we only really need one physical copy of it. Therefore a shallow pointer copy will suffice. - // (Note that this still holds even if the tree we are cloning was created by an inlinee compiler, - // because the inlinee still uses the inliner's memory allocator anyway.) - copy->gtCall.callSig = tree->gtCall.callSig; - - copy->gtCall.gtCallType = tree->gtCall.gtCallType; - copy->gtCall.gtReturnType = tree->gtCall.gtReturnType; - copy->gtCall.gtControlExpr = tree->gtCall.gtControlExpr; - - /* Copy the union */ - if (tree->gtCall.gtCallType == CT_INDIRECT) - { - copy->gtCall.gtCallCookie = - tree->gtCall.gtCallCookie ? gtCloneExpr(tree->gtCall.gtCallCookie, addFlags, deepVarNum, deepVarVal) - : nullptr; - copy->gtCall.gtCallAddr = tree->gtCall.gtCallAddr - ? gtCloneExpr(tree->gtCall.gtCallAddr, addFlags, deepVarNum, deepVarVal) - : nullptr; - } - else if (tree->gtCall.IsVirtualStub()) - { - copy->gtCall.gtCallMethHnd = tree->gtCall.gtCallMethHnd; - copy->gtCall.gtStubCallStubAddr = tree->gtCall.gtStubCallStubAddr; - } - else + // We can't safely clone calls that have GT_RET_EXPRs via gtCloneExpr. + // You must use gtCloneCandidateCall for these calls (and then do appropriate other fixup) + if (tree->gtCall.IsInlineCandidate() || tree->gtCall.IsGuardedDevirtualizationCandidate()) { - copy->gtCall.gtCallMethHnd = tree->gtCall.gtCallMethHnd; - copy->gtCall.gtInlineCandidateInfo = tree->gtCall.gtInlineCandidateInfo; + NO_WAY("Cloning of calls with associated GT_RET_EXPR nodes is not supported"); } - if (tree->gtCall.fgArgInfo) - { - // Create and initialize the fgArgInfo for our copy of the call tree - copy->gtCall.fgArgInfo = new (this, CMK_Unknown) fgArgInfo(copy->AsCall(), tree->AsCall()); - } - else - { - copy->gtCall.fgArgInfo = nullptr; - } - copy->gtCall.gtRetClsHnd = tree->gtCall.gtRetClsHnd; - -#if FEATURE_MULTIREG_RET - copy->gtCall.gtReturnTypeDesc = tree->gtCall.gtReturnTypeDesc; -#endif - -#ifdef FEATURE_READYTORUN_COMPILER - copy->gtCall.setEntryPoint(tree->gtCall.gtEntryPoint); -#endif - -#if defined(DEBUG) || defined(INLINE_DATA) - copy->gtCall.gtInlineObservation = tree->gtCall.gtInlineObservation; - copy->gtCall.gtRawILOffset = tree->gtCall.gtRawILOffset; -#endif - - copy->AsCall()->CopyOtherRegFlags(tree->AsCall()); + copy = gtCloneExprCallHelper(tree->AsCall(), addFlags, deepVarNum, deepVarVal); break; case GT_FIELD: @@ -7487,6 +7419,131 @@ DONE: } //------------------------------------------------------------------------ +// gtCloneExprCallHelper: clone a call tree +// +// Notes: +// Do not invoke this method directly, instead call either gtCloneExpr +// or gtCloneCandidateCall, as appropriate. +// +// Arguments: +// tree - the call to clone +// addFlags - GTF_* flags to add to the copied tree nodes +// deepVarNum - lclNum to replace uses of beyond the root, or BAD_VAR_NUM for no replacement +// deepVarVal - If replacing beyond root, replace `deepVarNum` with IntCns `deepVarVal` +// +// Returns: +// Cloned copy of call and all subtrees. + +GenTreeCall* Compiler::gtCloneExprCallHelper(GenTreeCall* tree, unsigned addFlags, unsigned deepVarNum, int deepVarVal) +{ + GenTreeCall* copy = new (this, GT_CALL) GenTreeCall(tree->TypeGet()); + + copy->gtCallObjp = tree->gtCallObjp ? gtCloneExpr(tree->gtCallObjp, addFlags, deepVarNum, deepVarVal) : nullptr; + copy->gtCallArgs = + tree->gtCallArgs ? gtCloneExpr(tree->gtCallArgs, addFlags, deepVarNum, deepVarVal)->AsArgList() : nullptr; + copy->gtCallMoreFlags = tree->gtCallMoreFlags; + copy->gtCallLateArgs = tree->gtCallLateArgs + ? gtCloneExpr(tree->gtCallLateArgs, addFlags, deepVarNum, deepVarVal)->AsArgList() + : nullptr; + +#if !FEATURE_FIXED_OUT_ARGS + copy->regArgList = tree->regArgList; + copy->regArgListCount = tree->regArgListCount; +#endif + + // The call sig comes from the EE and doesn't change throughout the compilation process, meaning + // we only really need one physical copy of it. Therefore a shallow pointer copy will suffice. + // (Note that this still holds even if the tree we are cloning was created by an inlinee compiler, + // because the inlinee still uses the inliner's memory allocator anyway.) + copy->callSig = tree->callSig; + + copy->gtCallType = tree->gtCallType; + copy->gtReturnType = tree->gtReturnType; + copy->gtControlExpr = tree->gtControlExpr; + + /* Copy the union */ + if (tree->gtCallType == CT_INDIRECT) + { + copy->gtCallCookie = + tree->gtCallCookie ? gtCloneExpr(tree->gtCallCookie, addFlags, deepVarNum, deepVarVal) : nullptr; + copy->gtCallAddr = tree->gtCallAddr ? gtCloneExpr(tree->gtCallAddr, addFlags, deepVarNum, deepVarVal) : nullptr; + } + else if (tree->IsVirtualStub()) + { + copy->gtCallMethHnd = tree->gtCallMethHnd; + copy->gtStubCallStubAddr = tree->gtStubCallStubAddr; + } + else + { + copy->gtCallMethHnd = tree->gtCallMethHnd; + copy->gtInlineCandidateInfo = nullptr; + } + + if (tree->fgArgInfo) + { + // Create and initialize the fgArgInfo for our copy of the call tree + copy->fgArgInfo = new (this, CMK_Unknown) fgArgInfo(copy, tree); + } + else + { + copy->fgArgInfo = nullptr; + } + + copy->gtRetClsHnd = tree->gtRetClsHnd; + +#if FEATURE_MULTIREG_RET + copy->gtReturnTypeDesc = tree->gtReturnTypeDesc; +#endif + +#ifdef FEATURE_READYTORUN_COMPILER + copy->setEntryPoint(tree->gtEntryPoint); +#endif + +#if defined(DEBUG) || defined(INLINE_DATA) + copy->gtInlineObservation = tree->gtInlineObservation; + copy->gtRawILOffset = tree->gtCall.gtRawILOffset; +#endif + + copy->CopyOtherRegFlags(tree); + + return copy; +} + +//------------------------------------------------------------------------ +// gtCloneCandidateCall: clone a call that is an inline or guarded +// devirtualization candidate (~ any call that can have a GT_RET_EXPR) +// +// Notes: +// If the call really is a candidate, the caller must take additional steps +// after cloning to re-establish candidate info and the relationship between +// the candidate and any associated GT_RET_EXPR. +// +// Arguments: +// call - the call to clone +// +// Returns: +// Cloned copy of call and all subtrees. + +GenTreeCall* Compiler::gtCloneCandidateCall(GenTreeCall* call) +{ + assert(call->IsInlineCandidate() || call->IsGuardedDevirtualizationCandidate()); + + GenTreeCall* result = gtCloneExprCallHelper(call); + + // There is some common post-processing in gtCloneExpr that we reproduce + // here, for the fields that make sense for candidate calls. + result->gtFlags |= call->gtFlags; + +#if defined(DEBUG) + result->gtDebugFlags |= (call->gtDebugFlags & ~GTF_DEBUG_NODE_MASK); +#endif + + result->CopyReg(call); + + return result; +} + +//------------------------------------------------------------------------ // gtReplaceTree: Replace a tree with a new tree. // // Arguments: @@ -12079,7 +12136,7 @@ GenTree* Compiler::gtFoldTypeCompare(GenTree* tree) // Compare the two method tables GenTree* const compare = gtCreateHandleCompare(oper, objMT, knownMT, typeCheckInliningResult); - // Drop any any now irrelevant flags + // Drop any now irrelevant flags compare->gtFlags |= tree->gtFlags & (GTF_RELOP_JMP_USED | GTF_RELOP_QMARK | GTF_DONT_CSE); // And we're done |