diff options
Diffstat (limited to 'src/jit/rationalize.cpp')
-rw-r--r-- | src/jit/rationalize.cpp | 250 |
1 files changed, 124 insertions, 126 deletions
diff --git a/src/jit/rationalize.cpp b/src/jit/rationalize.cpp index 03e0c9a27e..7f5a26fa1f 100644 --- a/src/jit/rationalize.cpp +++ b/src/jit/rationalize.cpp @@ -16,44 +16,6 @@ struct SplitData Rationalizer* thisPhase; }; -//------------------------------------------------------------------------------ -// isNodeCallArg - given a context (stack of parent nodes), determine if the TOS is an arg to a call -//------------------------------------------------------------------------------ - -GenTree* isNodeCallArg(ArrayStack<GenTree*>* parentStack) -{ - for (int i = 1; // 0 is current node, so start at 1 - i < parentStack->Height(); i++) - { - GenTree* node = parentStack->Index(i); - switch (node->OperGet()) - { - case GT_LIST: - case GT_ARGPLACE: - break; - case GT_NOP: - // Currently there's an issue when the rationalizer performs - // the fixup of a call argument: the case is when we remove an - // inserted NOP as a parent of a call introduced by fgMorph; - // when then the rationalizer removes it, the tree stack in the - // walk is not consistent with the node it was just deleted, so the - // solution is just to go 1 level deeper. - // TODO-Cleanup: This has to be fixed in a proper way: make the rationalizer - // correctly modify the evaluation stack when removing treenodes. - if (node->gtOp.gtOp1->gtOper == GT_CALL) - { - return node->gtOp.gtOp1; - } - break; - case GT_CALL: - return node; - default: - return nullptr; - } - } - return nullptr; -} - // return op that is the store equivalent of the given load opcode genTreeOps storeForm(genTreeOps loadForm) { @@ -109,54 +71,6 @@ void copyFlags(GenTree* dst, GenTree* src, unsigned mask) dst->gtFlags |= (src->gtFlags & mask); } -// call args have other pointers to them which must be fixed up if -// they are replaced -void Compiler::fgFixupIfCallArg(ArrayStack<GenTree*>* parentStack, GenTree* oldChild, GenTree* newChild) -{ - GenTree* parentCall = isNodeCallArg(parentStack); - if (!parentCall) - { - return; - } - - // we have replaced an arg, so update pointers in argtable - fgFixupArgTabEntryPtr(parentCall, oldChild, newChild); -} - -//------------------------------------------------------------------------ -// fgFixupArgTabEntryPtr: Fixup the fgArgTabEntryPtr of parentCall after -// replacing oldArg with newArg -// -// Arguments: -// parentCall - a pointer to the parent call node -// oldArg - the original argument node -// newArg - the replacement argument node -// - -void Compiler::fgFixupArgTabEntryPtr(GenTreePtr parentCall, GenTreePtr oldArg, GenTreePtr newArg) -{ - assert(parentCall != nullptr); - assert(oldArg != nullptr); - assert(newArg != nullptr); - - JITDUMP("parent call was :\n"); - DISPNODE(parentCall); - - JITDUMP("old child was :\n"); - DISPNODE(oldArg); - - if (oldArg->gtFlags & GTF_LATE_ARG) - { - newArg->gtFlags |= GTF_LATE_ARG; - } - else - { - fgArgTabEntryPtr fp = Compiler::gtArgEntryByNode(parentCall, oldArg); - assert(fp->node == oldArg); - fp->node = newArg; - } -} - // Rewrite a SIMD indirection as GT_IND(GT_LEA(obj.op1)), or as a simple // lclVar if possible. // @@ -191,8 +105,8 @@ void Rationalizer::RewriteSIMDOperand(LIR::Use& use, bool keepBlk) return; } - // If the operand of is a GT_ADDR(GT_LCL_VAR) and LclVar is known to be of simdType, - // replace obj by GT_LCL_VAR. + // If we have GT_IND(GT_LCL_VAR_ADDR) and the GT_LCL_VAR_ADDR is TYP_BYREF/TYP_I_IMPL, + // and the var is a SIMD type, replace the expression by GT_LCL_VAR. GenTree* addr = tree->AsIndir()->Addr(); if (addr->OperIsLocalAddr() && comp->isAddrOfSIMDType(addr)) { @@ -202,6 +116,17 @@ void Rationalizer::RewriteSIMDOperand(LIR::Use& use, bool keepBlk) addr->gtType = simdType; use.ReplaceWith(comp, addr); } +#if defined(_TARGET_X86_) + // For x86, if we have GT_IND(GT_ADDR(GT_SIMD)), remove the GT_IND(GT_ADDR()), leaving just + // the GT_SIMD. + else if ((addr->OperGet() == GT_ADDR) && (addr->gtGetOp1()->OperGet() == GT_SIMD)) + { + BlockRange().Remove(tree); + BlockRange().Remove(addr); + + use.ReplaceWith(comp, addr->gtGetOp1()); + } +#endif // defined(_TARGET_X86_) else if (!keepBlk) { tree->SetOper(GT_IND); @@ -242,13 +167,32 @@ void Rationalizer::RewriteNodeAsCall(GenTree** use, // Create the call node GenTreeCall* call = comp->gtNewCallNode(CT_USER_FUNC, callHnd, tree->gtType, args); - call = comp->fgMorphArgs(call); + +#if DEBUG + CORINFO_SIG_INFO sig; + comp->eeGetMethodSig(callHnd, &sig); + assert(JITtype2varType(sig.retType) == tree->gtType); +#endif // DEBUG + + call = comp->fgMorphArgs(call); + // Determine if this call has changed any codegen requirements. + comp->fgCheckArgCnt(); + #ifdef FEATURE_READYTORUN_COMPILER call->gtCall.setEntryPoint(entryPoint); #endif // Replace "tree" with "call" - *use = call; + if (data->parentStack->Height() > 1) + { + data->parentStack->Index(1)->ReplaceOperand(use, call); + } + else + { + // If there's no parent, the tree being replaced is the root of the + // statement (and no special handling is necessary). + *use = call; + } // Rebuild the evaluation order. comp->gtSetStmtInfo(root); @@ -278,8 +222,6 @@ void Rationalizer::RewriteNodeAsCall(GenTree** use, treeNextNode->gtPrev = treeLastNode; } - comp->fgFixupIfCallArg(data->parentStack, tree, call); - // Propagate flags of "call" to its parents. // 0 is current node, so start at 1 for (int i = 1; i < data->parentStack->Height(); i++) @@ -510,33 +452,77 @@ void Rationalizer::RewriteAssignment(LIR::Use& use) genTreeOps locationOp = location->OperGet(); -#ifdef FEATURE_SIMD - if (varTypeIsSIMD(location) && assignment->OperIsInitBlkOp()) + if (assignment->OperIsBlkOp()) { - if (location->OperGet() == GT_LCL_VAR) +#ifdef FEATURE_SIMD + if (varTypeIsSIMD(location) && assignment->OperIsInitBlkOp()) { - var_types simdType = location->TypeGet(); - GenTree* initVal = assignment->gtOp.gtOp2; - var_types baseType = comp->getBaseTypeOfSIMDLocal(location); - if (baseType != TYP_UNKNOWN) + if (location->OperGet() == GT_LCL_VAR) { - GenTreeSIMD* simdTree = new (comp, GT_SIMD) - GenTreeSIMD(simdType, initVal, SIMDIntrinsicInit, baseType, genTypeSize(simdType)); - assignment->gtOp.gtOp2 = simdTree; - value = simdTree; - initVal->gtNext = simdTree; - simdTree->gtPrev = initVal; - - simdTree->gtNext = location; - location->gtPrev = simdTree; + var_types simdType = location->TypeGet(); + GenTree* initVal = assignment->gtOp.gtOp2; + var_types baseType = comp->getBaseTypeOfSIMDLocal(location); + if (baseType != TYP_UNKNOWN) + { + GenTreeSIMD* simdTree = new (comp, GT_SIMD) + GenTreeSIMD(simdType, initVal, SIMDIntrinsicInit, baseType, genTypeSize(simdType)); + assignment->gtOp.gtOp2 = simdTree; + value = simdTree; + initVal->gtNext = simdTree; + simdTree->gtPrev = initVal; + + simdTree->gtNext = location; + location->gtPrev = simdTree; + } } } - else +#endif // FEATURE_SIMD + if ((location->TypeGet() == TYP_STRUCT) && !assignment->IsPhiDefn() && !value->IsMultiRegCall()) { - assert(location->OperIsBlk()); + if ((location->OperGet() == GT_LCL_VAR)) + { + // We need to construct a block node for the location. + // Modify lcl to be the address form. + location->SetOper(addrForm(locationOp)); + LclVarDsc* varDsc = &(comp->lvaTable[location->AsLclVarCommon()->gtLclNum]); + location->gtType = TYP_BYREF; + GenTreeBlk* storeBlk = nullptr; + unsigned int size = varDsc->lvExactSize; + + if (varDsc->lvStructGcCount != 0) + { + CORINFO_CLASS_HANDLE structHnd = varDsc->lvVerTypeInfo.GetClassHandle(); + GenTreeObj* objNode = comp->gtNewObjNode(structHnd, location)->AsObj(); + unsigned int slots = (unsigned)(roundUp(size, TARGET_POINTER_SIZE) / TARGET_POINTER_SIZE); + + objNode->SetGCInfo(varDsc->lvGcLayout, varDsc->lvStructGcCount, slots); + objNode->ChangeOper(GT_STORE_OBJ); + objNode->SetData(value); + comp->fgMorphUnsafeBlk(objNode); + storeBlk = objNode; + } + else + { + storeBlk = new (comp, GT_STORE_BLK) GenTreeBlk(GT_STORE_BLK, TYP_STRUCT, location, value, size); + } + storeBlk->gtFlags |= (GTF_REVERSE_OPS | GTF_ASG); + storeBlk->gtFlags |= ((location->gtFlags | value->gtFlags) & GTF_ALL_EFFECT); + + GenTree* insertionPoint = location->gtNext; + BlockRange().InsertBefore(insertionPoint, storeBlk); + use.ReplaceWith(comp, storeBlk); + BlockRange().Remove(assignment); + JITDUMP("After transforming local struct assignment into a block op:\n"); + DISPTREERANGE(BlockRange(), use.Def()); + JITDUMP("\n"); + return; + } + else + { + assert(location->OperIsBlk()); + } } } -#endif // FEATURE_SIMD switch (locationOp) { @@ -605,10 +591,10 @@ void Rationalizer::RewriteAssignment(LIR::Use& use) } JITDUMP("Rewriting GT_ASG(%s(X), Y) to %s(X,Y):\n", GenTree::NodeName(location->gtOper), GenTree::NodeName(storeOper)); - storeBlk->gtOper = storeOper; + storeBlk->SetOperRaw(storeOper); storeBlk->gtFlags &= ~GTF_DONT_CSE; storeBlk->gtFlags |= (assignment->gtFlags & (GTF_ALL_EFFECT | GTF_REVERSE_OPS | GTF_BLK_VOLATILE | - GTF_BLK_UNALIGNED | GTF_BLK_INIT | GTF_DONT_CSE)); + GTF_BLK_UNALIGNED | GTF_DONT_CSE)); storeBlk->gtBlk.Data() = value; // Replace the assignment node with the store @@ -693,21 +679,20 @@ Compiler::fgWalkResult Rationalizer::RewriteNode(GenTree** useEdge, ArrayStack<G const bool isLateArg = (node->gtFlags & GTF_LATE_ARG) != 0; #endif - // First, remove any preceeding GT_LIST nodes, which are not otherwise visited by the tree walk. + // First, remove any preceeding list nodes, which are not otherwise visited by the tree walk. // - // NOTE: GT_LIST nodes that are used as aggregates, by block ops, and by phi nodes will in fact be visited. - for (GenTree* prev = node->gtPrev; - prev != nullptr && prev->OperGet() == GT_LIST && !(prev->AsArgList()->IsAggregate()); - prev = node->gtPrev) + // NOTE: GT_FIELD_LIST head nodes, and GT_LIST nodes used by phi nodes will in fact be visited. + for (GenTree* prev = node->gtPrev; prev != nullptr && prev->OperIsAnyList() && !(prev->OperIsFieldListHead()); + prev = node->gtPrev) { BlockRange().Remove(prev); } // In addition, remove the current node if it is a GT_LIST node that is not an aggregate. - if (node->OperGet() == GT_LIST) + if (node->OperIsAnyList()) { GenTreeArgList* list = node->AsArgList(); - if (!list->IsAggregate()) + if (!list->OperIsFieldListHead()) { BlockRange().Remove(list); } @@ -741,6 +726,11 @@ Compiler::fgWalkResult Rationalizer::RewriteNode(GenTree** useEdge, ArrayStack<G RewriteAddress(use); break; + case GT_IND: + // Clear the `GTF_IND_ASG_LHS` flag, which overlaps with `GTF_IND_REQ_ADDR_IN_REG`. + node->gtFlags &= ~GTF_IND_ASG_LHS; + break; + case GT_NOP: // fgMorph sometimes inserts NOP nodes between defs and uses // supposedly 'to prevent constant folding'. In this case, remove the @@ -931,19 +921,27 @@ Compiler::fgWalkResult Rationalizer::RewriteNode(GenTree** useEdge, ArrayStack<G #endif // FEATURE_SIMD default: + // JCC nodes should not be present in HIR. + assert(node->OperGet() != GT_JCC); break; } // Do some extra processing on top-level nodes to remove unused local reads. - if (use.IsDummyUse() && node->OperIsLocalRead()) + if (node->OperIsLocalRead()) { - assert((node->gtFlags & GTF_ALL_EFFECT) == 0); - - comp->lvaDecRefCnts(node); - BlockRange().Remove(node); + if (use.IsDummyUse()) + { + comp->lvaDecRefCnts(node); + BlockRange().Remove(node); + } + else + { + // Local reads are side-effect-free; clear any flags leftover from frontend transformations. + node->gtFlags &= ~GTF_ALL_EFFECT; + } } - assert(isLateArg == ((node->gtFlags & GTF_LATE_ARG) != 0)); + assert(isLateArg == ((use.Def()->gtFlags & GTF_LATE_ARG) != 0)); return Compiler::WALK_CONTINUE; } |