-
Notifications
You must be signed in to change notification settings - Fork 5.5k
Keep precise argument sizes between import and morph. #43130
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -22743,7 +22743,11 @@ void Compiler::fgAttachStructInlineeToAsg(GenTree* tree, GenTree* child, CORINFO | |
| // If the return type is a struct type and we're on a platform | ||
| // where structs can be returned in multiple registers, ensure the | ||
| // call has a suitable parent. | ||
|
|
||
| // | ||
| // If the original call type and the substitution type are different | ||
| // the functions makes necessary updates. It could happen if there was | ||
| // an implicit conversion in the inlinee body. | ||
| // | ||
| Compiler::fgWalkResult Compiler::fgUpdateInlineReturnExpressionPlaceHolder(GenTree** pTree, fgWalkData* data) | ||
| { | ||
| // All the operations here and in the corresponding postorder | ||
|
|
@@ -22798,6 +22802,29 @@ Compiler::fgWalkResult Compiler::fgUpdateInlineReturnExpressionPlaceHolder(GenTr | |
| } | ||
| #endif // DEBUG | ||
|
|
||
| var_types newType = inlineCandidate->TypeGet(); | ||
|
|
||
| // If we end up swapping type we may need to retype the tree: | ||
|
sandreenko marked this conversation as resolved.
Outdated
|
||
| if (retType != newType) | ||
| { | ||
| if ((retType == TYP_BYREF) && (tree->OperGet() == GT_IND)) | ||
| { | ||
| // - in an RVA static if we've reinterpreted it as a byref; | ||
| assert(newType == TYP_I_IMPL); | ||
| JITDUMP("Updating type of the return GT_IND expression to TYP_BYREF\n"); | ||
| inlineCandidate->gtType = TYP_BYREF; | ||
| } | ||
| else | ||
| { | ||
| // - under a call if we changed size of the argument. | ||
| GenTree* putArgType = comp->fgCheckCallArgUpdate(data->parent, inlineCandidate, retType); | ||
| if (putArgType != nullptr) | ||
| { | ||
| inlineCandidate = putArgType; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| tree->ReplaceWith(inlineCandidate, comp); | ||
| comp->compCurBB->bbFlags |= (bbFlags & BBF_SPLIT_GAINED); | ||
|
|
||
|
|
@@ -22809,17 +22836,6 @@ Compiler::fgWalkResult Compiler::fgUpdateInlineReturnExpressionPlaceHolder(GenTr | |
| printf("\n"); | ||
| } | ||
| #endif // DEBUG | ||
|
|
||
| var_types newType = tree->TypeGet(); | ||
|
|
||
| // If we end up swapping in an RVA static we may need to retype it here, | ||
| // if we've reinterpreted it as a byref. | ||
| if ((retType != newType) && (retType == TYP_BYREF) && (tree->OperGet() == GT_IND)) | ||
| { | ||
| assert(newType == TYP_I_IMPL); | ||
| JITDUMP("Updating type of the return GT_IND expression to TYP_BYREF\n"); | ||
| tree->gtType = TYP_BYREF; | ||
| } | ||
| } | ||
|
|
||
| // If an inline was rejected and the call returns a struct, we may | ||
|
|
@@ -23101,8 +23117,16 @@ Compiler::fgWalkResult Compiler::fgLateDevirtualization(GenTree** pTree, fgWalkD | |
| } | ||
| else | ||
| { | ||
| GenTree* foldedTree = comp->gtFoldExpr(tree); | ||
| *pTree = foldedTree; | ||
| const var_types retType = tree->TypeGet(); | ||
| GenTree* foldedTree = comp->gtFoldExpr(tree); | ||
| const var_types newType = foldedTree->TypeGet(); | ||
|
|
||
| GenTree* putArgType = comp->fgCheckCallArgUpdate(data->parent, foldedTree, retType); | ||
| if (putArgType != nullptr) | ||
| { | ||
| foldedTree = putArgType; | ||
| } | ||
| *pTree = foldedTree; | ||
| } | ||
|
|
||
| return WALK_CONTINUE; | ||
|
|
@@ -23799,8 +23823,12 @@ Statement* Compiler::fgInlinePrependStatements(InlineInfo* inlineInfo) | |
| { | ||
| const InlArgInfo& argInfo = inlArgInfo[argNum]; | ||
| const bool argIsSingleDef = !argInfo.argHasLdargaOp && !argInfo.argHasStargOp; | ||
| GenTree* const argNode = inlArgInfo[argNum].argNode; | ||
| unsigned __int64 bbFlags = inlArgInfo[argNum].bbFlags; | ||
| GenTree* argNode = inlArgInfo[argNum].argNode; | ||
| const bool argHasPutArg = argNode->OperIs(GT_PUTARG_TYPE); | ||
|
|
||
| unsigned __int64 bbFlags = 0; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See note above; I don't believe you can set these to zero like this and pass jit stress. |
||
| argNode = argNode->gtSkipPutArgType(); | ||
| argNode = argNode->gtRetExprVal(&bbFlags); | ||
|
AndyAyersMS marked this conversation as resolved.
Outdated
|
||
|
|
||
| if (argInfo.argHasTmp) | ||
| { | ||
|
|
@@ -23820,7 +23848,11 @@ Statement* Compiler::fgInlinePrependStatements(InlineInfo* inlineInfo) | |
|
|
||
| GenTree* argSingleUseNode = argInfo.argBashTmpNode; | ||
|
|
||
| if ((argSingleUseNode != nullptr) && !(argSingleUseNode->gtFlags & GTF_VAR_CLONED) && argIsSingleDef) | ||
| // argHasPutArg disqualifies the arg from a direct substitution because we don't have information about | ||
| // its user. For example: replace `LCL_VAR short` with `PUTARG_TYPE short->LCL_VAR int`, | ||
| // we should keep `PUTARG_TYPE` iff the user is a call that needs `short` and delete it otherwise. | ||
| if ((argSingleUseNode != nullptr) && !(argSingleUseNode->gtFlags & GTF_VAR_CLONED) && argIsSingleDef && | ||
| !argHasPutArg) | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This new condition allows us to always the argument as a temp with the correct type, we need it because for a correct direct substitution we need to know if the parent is a call or not but we don't have this information. , etc. all lower than 0.04%. What happens there is:
I have not found a simple way to avoid it or compensate for it.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would be good to have a comment in the code here explaining why |
||
| { | ||
| // Change the temp in-place to the actual argument. | ||
| // We currently do not support this for struct arguments, so it must not be a GT_OBJ. | ||
|
|
@@ -26501,6 +26533,44 @@ void Compiler::fgTailMergeThrowsJumpToHelper(BasicBlock* predBlock, | |
| canonicalBlock->bbFlags |= (BBF_JMP_TARGET | BBF_HAS_LABEL); | ||
| } | ||
|
|
||
| //------------------------------------------------------------------------ | ||
| // fgCheckCallArgUpdate: check if we are replacing a call argument and add GT_PUTARG_TYPE if necessary. | ||
| // | ||
| // Arguments: | ||
| // parent - the parent that could be a call; | ||
| // child - the new child node; | ||
| // origType - the original child type; | ||
| // | ||
| // Returns: | ||
| // PUT_ARG_TYPE node if it is needed, nullptr otherwise. | ||
| // | ||
| GenTree* Compiler::fgCheckCallArgUpdate(GenTree* parent, GenTree* child, var_types origType) | ||
| { | ||
| if ((parent == nullptr) || !parent->IsCall()) | ||
| { | ||
| return nullptr; | ||
| } | ||
| const var_types newType = child->TypeGet(); | ||
| if (newType == origType) | ||
| { | ||
| return nullptr; | ||
| } | ||
| if (varTypeIsStruct(origType) || (genTypeSize(origType) == genTypeSize(newType))) | ||
| { | ||
| assert(!varTypeIsStruct(newType)); | ||
| return nullptr; | ||
| } | ||
| GenTree* putArgType = gtNewOperNode(GT_PUTARG_TYPE, origType, child); | ||
| #if defined(DEBUG) | ||
| if (verbose) | ||
| { | ||
| printf("For call [%06d] the new argument's type [%06d]", dspTreeID(parent), dspTreeID(child)); | ||
| printf(" does not match the original type size, add a GT_PUTARG_TYPE [%06d]\n", dspTreeID(parent)); | ||
| } | ||
| #endif | ||
| return putArgType; | ||
| } | ||
|
|
||
| #ifdef DEBUG | ||
|
|
||
| //------------------------------------------------------------------------ | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1736,7 +1736,9 @@ struct GenTree | |
| inline GenTree* gtEffectiveVal(bool commaOnly = false); | ||
|
|
||
| // Tunnel through any GT_RET_EXPRs | ||
| inline GenTree* gtRetExprVal(unsigned __int64* pbbFlags); | ||
| inline GenTree* gtRetExprVal(unsigned __int64* pbbFlags = nullptr); | ||
|
|
||
| inline GenTree* gtSkipPutArgType(); | ||
|
|
||
| // Return the child of this node if it is a GT_RELOAD or GT_COPY; otherwise simply return the node itself | ||
| inline GenTree* gtSkipReloadOrCopy(); | ||
|
|
@@ -7111,14 +7113,15 @@ inline GenTree* GenTree::gtGetOp2IfPresent() const | |
| return op2; | ||
| } | ||
|
|
||
| inline GenTree* GenTree::gtEffectiveVal(bool commaOnly) | ||
| inline GenTree* GenTree::gtEffectiveVal(bool commaOnly /* = false */) | ||
| { | ||
| GenTree* effectiveVal = this; | ||
| for (;;) | ||
| { | ||
| assert(!effectiveVal->OperIs(GT_PUTARG_TYPE)); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm curious why you added this here - that is, I can see why we wouldn't expect to encounter these under a
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We have several similar functions like |
||
| if (effectiveVal->gtOper == GT_COMMA) | ||
| { | ||
| effectiveVal = effectiveVal->AsOp()->gtOp2; | ||
| effectiveVal = effectiveVal->AsOp()->gtGetOp2(); | ||
| } | ||
| else if (!commaOnly && (effectiveVal->gtOper == GT_NOP) && (effectiveVal->AsOp()->gtOp1 != nullptr)) | ||
| { | ||
|
|
@@ -7147,23 +7150,49 @@ inline GenTree* GenTree::gtEffectiveVal(bool commaOnly) | |
| // Multi-level inlines can form chains of GT_RET_EXPRs. | ||
| // This method walks back to the root of the chain. | ||
|
|
||
| inline GenTree* GenTree::gtRetExprVal(unsigned __int64* pbbFlags) | ||
| inline GenTree* GenTree::gtRetExprVal(unsigned __int64* pbbFlags /* = nullptr */) | ||
| { | ||
| GenTree* retExprVal = this; | ||
| unsigned __int64 bbFlags = 0; | ||
|
|
||
| assert(pbbFlags != nullptr); | ||
| assert(!retExprVal->OperIs(GT_PUTARG_TYPE)); | ||
|
|
||
| for (; retExprVal->gtOper == GT_RET_EXPR; retExprVal = retExprVal->AsRetExpr()->gtInlineCandidate) | ||
| while (retExprVal->OperIs(GT_RET_EXPR)) | ||
| { | ||
| bbFlags = retExprVal->AsRetExpr()->bbFlags; | ||
| const GenTreeRetExpr* retExpr = retExprVal->AsRetExpr(); | ||
| bbFlags = retExpr->bbFlags; | ||
| retExprVal = retExpr->gtInlineCandidate; | ||
| } | ||
|
|
||
| *pbbFlags = bbFlags; | ||
| if (pbbFlags != nullptr) | ||
| { | ||
| *pbbFlags = bbFlags; | ||
| } | ||
|
|
||
| return retExprVal; | ||
| } | ||
|
|
||
| //------------------------------------------------------------------------- | ||
| // gtSkipPutArgType - skip PUTARG_TYPE if it is presented. | ||
| // | ||
| // Returns: | ||
| // the original tree or its child if it was a PUTARG_TYPE. | ||
| // | ||
| // Notes: | ||
| // PUTARG_TYPE should be skipped when we are doing transformations | ||
| // that are not affected by ABI, for example: inlining, implicit byref morphing. | ||
| // | ||
| inline GenTree* GenTree::gtSkipPutArgType() | ||
| { | ||
| if (OperIs(GT_PUTARG_TYPE)) | ||
| { | ||
| GenTree* res = AsUnOp()->gtGetOp1(); | ||
| assert(!res->OperIs(GT_PUTARG_TYPE)); | ||
| return res; | ||
| } | ||
| return this; | ||
| } | ||
|
|
||
| inline GenTree* GenTree::gtSkipReloadOrCopy() | ||
| { | ||
| // There can be only one reload or copy (we can't have a reload/copy of a reload/copy) | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.