diff options
author | Carol Eidt <carol.eidt@microsoft.com> | 2017-10-04 17:42:07 -0700 |
---|---|---|
committer | Carol Eidt <carol.eidt@microsoft.com> | 2017-10-11 11:13:15 -0700 |
commit | 3da20a179409f6232d9844f1525f45d01c14aaa1 (patch) | |
tree | 934ec47fe75eb6dbd046ef2b9438a2a3b6c439c7 /src | |
parent | 65738239e7a50615d0fc50dcbe5b1b6086f96ef2 (diff) | |
download | coreclr-3da20a179409f6232d9844f1525f45d01c14aaa1.tar.gz coreclr-3da20a179409f6232d9844f1525f45d01c14aaa1.tar.bz2 coreclr-3da20a179409f6232d9844f1525f45d01c14aaa1.zip |
Cleanup of Lowering & LsraInfo
These are preparatory changes for eliminating gtLsraInfo.
Register requirements should never be set on contained nodes. This includes setting isDelayFree and restricting to byte registers for x86.
- This results in net positive diffs for the framework (eliminating incorrect setting of hasDelayFreeSrc), though a net regression for the tests on x86 (including many instances of effectively the same code).
- The regressions are largely related to issue #11274.
Improve consistency of IsValue():
- Any node that can be contained should produce a value, and have a type (e.g. GT_FIELD_LIST).
- Some value nodes (GTK_NOVALUE isn't set) are allowed to have TYP_VOID, in which case IsValue() should return false.
- This simplifies IsValue().
- Any node that can be assigned a register should return true for IsValue() (e.g. GT_LOCKADD).
- PUTARG_STK doesn't produce a value; get type from its operand.
- This requires some fixing up of SIMD12 operands.
- Unused GT_LONG nodes shouldn't define any registers
Eliminate isNoRegCompare, by setting type of JTRUE operand to TYP_VOID
- Set GTF_SET_FLAGS on the operand to ensure it is not eliminated as dead code.
Diffstat (limited to 'src')
-rw-r--r-- | src/jit/codegenarmarch.cpp | 2 | ||||
-rw-r--r-- | src/jit/codegenlinear.cpp | 13 | ||||
-rw-r--r-- | src/jit/codegenxarch.cpp | 92 | ||||
-rw-r--r-- | src/jit/decomposelongs.cpp | 69 | ||||
-rw-r--r-- | src/jit/decomposelongs.h | 1 | ||||
-rw-r--r-- | src/jit/flowgraph.cpp | 4 | ||||
-rw-r--r-- | src/jit/gentree.cpp | 7 | ||||
-rw-r--r-- | src/jit/gentree.h | 37 | ||||
-rw-r--r-- | src/jit/gtlist.h | 2 | ||||
-rw-r--r-- | src/jit/lir.cpp | 13 | ||||
-rw-r--r-- | src/jit/lower.cpp | 37 | ||||
-rw-r--r-- | src/jit/lowerxarch.cpp | 10 | ||||
-rw-r--r-- | src/jit/lsra.cpp | 2 | ||||
-rw-r--r-- | src/jit/lsra.h | 2 | ||||
-rw-r--r-- | src/jit/lsraarm.cpp | 19 | ||||
-rw-r--r-- | src/jit/lsraarm64.cpp | 15 | ||||
-rw-r--r-- | src/jit/lsraarmarch.cpp | 9 | ||||
-rw-r--r-- | src/jit/lsraxarch.cpp | 202 | ||||
-rw-r--r-- | src/jit/nodeinfo.h | 4 |
19 files changed, 313 insertions, 227 deletions
diff --git a/src/jit/codegenarmarch.cpp b/src/jit/codegenarmarch.cpp index 22aed1056d..7130ec28fd 100644 --- a/src/jit/codegenarmarch.cpp +++ b/src/jit/codegenarmarch.cpp @@ -538,8 +538,8 @@ void CodeGen::genIntrinsic(GenTreePtr treeNode) void CodeGen::genPutArgStk(GenTreePutArgStk* treeNode) { assert(treeNode->OperIs(GT_PUTARG_STK)); - var_types targetType = treeNode->TypeGet(); GenTreePtr source = treeNode->gtOp1; + var_types targetType = source->TypeGet(); emitter* emit = getEmitter(); // This is the varNum for our store operations, diff --git a/src/jit/codegenlinear.cpp b/src/jit/codegenlinear.cpp index 1ee288b505..830183ed5a 100644 --- a/src/jit/codegenlinear.cpp +++ b/src/jit/codegenlinear.cpp @@ -398,11 +398,8 @@ void CodeGen::genCodeForBBlist() // performed at the end of each block. // TODO: could these checks be performed more frequently? E.g., at each location where // the register allocator says there are no live non-variable registers. Perhaps this could - // be done by (a) keeping a running count of live non-variable registers by using - // gtLsraInfo.srcCount and gtLsraInfo.dstCount to decrement and increment the count, respectively, - // and running the checks when the count is zero. Or, (b) use the map maintained by LSRA - // (operandToLocationInfoMap) to mark a node somehow when, after the execution of that node, - // there will be no live non-variable registers. + // be done by using the map maintained by LSRA (operandToLocationInfoMap) to mark a node + // somehow when, after the execution of that node, there will be no live non-variable registers. regSet.rsSpillChk(); @@ -1375,15 +1372,12 @@ void CodeGen::genConsumePutStructArgStk(GenTreePutArgStk* putArgNode, regNumber srcReg, regNumber sizeReg) { - assert(varTypeIsStruct(putArgNode)); - // The putArgNode children are always contained. We should not consume any registers. assert(putArgNode->gtGetOp1()->isContained()); - GenTree* dstAddr = putArgNode; - // Get the source address. GenTree* src = putArgNode->gtGetOp1(); + assert(varTypeIsStruct(src)); assert((src->gtOper == GT_OBJ) || ((src->gtOper == GT_IND && varTypeIsSIMD(src)))); GenTree* srcAddr = src->gtGetOp1(); @@ -1406,6 +1400,7 @@ void CodeGen::genConsumePutStructArgStk(GenTreePutArgStk* putArgNode, assert(dstReg != REG_SPBASE); inst_RV_RV(INS_mov, dstReg, REG_SPBASE); #else // !_TARGET_X86_ + GenTree* dstAddr = putArgNode; if (dstAddr->gtRegNum != dstReg) { // Generate LEA instruction to load the stack of the outgoing var + SlotNum offset (or the incoming arg area diff --git a/src/jit/codegenxarch.cpp b/src/jit/codegenxarch.cpp index a51243626b..7ba3f46666 100644 --- a/src/jit/codegenxarch.cpp +++ b/src/jit/codegenxarch.cpp @@ -3207,13 +3207,10 @@ unsigned CodeGen::genMove1IfNeeded(unsigned size, regNumber intTmpReg, GenTree* // void CodeGen::genStructPutArgUnroll(GenTreePutArgStk* putArgNode) { + GenTreePtr src = putArgNode->gtOp.gtOp1; // We will never call this method for SIMD types, which are stored directly // in genPutStructArgStk(). - noway_assert(putArgNode->TypeGet() == TYP_STRUCT); - - // Make sure we got the arguments of the cpblk operation in the right registers - GenTreePtr dstAddr = putArgNode; - GenTreePtr src = putArgNode->gtOp.gtOp1; + noway_assert(src->TypeGet() == TYP_STRUCT); unsigned size = putArgNode->getArgSize(); assert(size <= CPBLK_UNROLL_LIMIT); @@ -3331,14 +3328,12 @@ void CodeGen::genStructPutArgUnroll(GenTreePutArgStk* putArgNode) // void CodeGen::genStructPutArgRepMovs(GenTreePutArgStk* putArgNode) { - assert(putArgNode->TypeGet() == TYP_STRUCT); - assert(putArgNode->getArgSize() > CPBLK_UNROLL_LIMIT); - - // Make sure we got the arguments of the cpblk operation in the right registers - GenTreePtr dstAddr = putArgNode; GenTreePtr srcAddr = putArgNode->gtGetOp1(); + assert(srcAddr->TypeGet() == TYP_STRUCT); + assert(putArgNode->getArgSize() > CPBLK_UNROLL_LIMIT); - // Validate state. + // Make sure we got the arguments of the cpblk operation in the right registers, and that + // 'srcAddr' is contained as expected. assert(putArgNode->gtRsvdRegs == (RBM_RDI | RBM_RCX | RBM_RSI)); assert(srcAddr->isContained()); @@ -3424,7 +3419,7 @@ void CodeGen::genCodeForCpObj(GenTreeObj* cpObjNode) sourceIsLocal = true; } - bool dstOnStack = dstAddr->OperIsLocalAddr(); + bool dstOnStack = dstAddr->gtSkipReloadOrCopy()->OperIsLocalAddr(); #ifdef DEBUG @@ -5216,34 +5211,24 @@ void CodeGen::genCallInstruction(GenTreeCall* call) GenTreePtr arg = args->gtOp.gtOp1; if (arg->OperGet() != GT_ARGPLACE && !(arg->gtFlags & GTF_LATE_ARG)) { -#if defined(_TARGET_X86_) - if ((arg->OperGet() == GT_PUTARG_STK) && (arg->gtGetOp1()->OperGet() == GT_FIELD_LIST)) + if (arg->OperGet() == GT_PUTARG_STK) { + GenTree* source = arg->gtOp.gtOp1; + ssize_t size = arg->AsPutArgStk()->getArgSize(); + stackArgBytes += size; +#ifdef DEBUG fgArgTabEntryPtr curArgTabEntry = compiler->gtArgEntryByNode(call, arg); assert(curArgTabEntry); - stackArgBytes += curArgTabEntry->numSlots * TARGET_POINTER_SIZE; - } - else -#endif // defined(_TARGET_X86_) - + assert(size == (curArgTabEntry->numSlots * TARGET_POINTER_SIZE)); #ifdef FEATURE_PUT_STRUCT_ARG_STK - if (genActualType(arg->TypeGet()) == TYP_STRUCT) - { - assert(arg->OperGet() == GT_PUTARG_STK); - - GenTreeObj* obj = arg->gtGetOp1()->AsObj(); - unsigned argBytes = (unsigned)roundUp(obj->gtBlkSize, TARGET_POINTER_SIZE); -#ifdef DEBUG - fgArgTabEntryPtr curArgTabEntry = compiler->gtArgEntryByNode(call, arg); - assert((curArgTabEntry->numSlots * TARGET_POINTER_SIZE) == argBytes); -#endif // DEBUG - stackArgBytes += argBytes; - } - else + if (source->TypeGet() == TYP_STRUCT) + { + GenTreeObj* obj = source->AsObj(); + unsigned argBytes = (unsigned)roundUp(obj->gtBlkSize, TARGET_POINTER_SIZE); + assert((curArgTabEntry->numSlots * TARGET_POINTER_SIZE) == argBytes); + } #endif // FEATURE_PUT_STRUCT_ARG_STK - - { - stackArgBytes += genTypeSize(genActualType(arg->TypeGet())); +#endif // DEBUG } } args = args->gtOp.gtOp2; @@ -7596,10 +7581,12 @@ void CodeGen::genRemoveAlignmentAfterCall(GenTreeCall* call, unsigned bias) // bool CodeGen::genAdjustStackForPutArgStk(GenTreePutArgStk* putArgStk) { + const unsigned argSize = putArgStk->getArgSize(); + GenTree* source = putArgStk->gtGetOp1(); + #ifdef FEATURE_SIMD - if (varTypeIsSIMD(putArgStk)) + if (!source->OperIs(GT_FIELD_LIST) && varTypeIsSIMD(source)) { - const unsigned argSize = genTypeSize(putArgStk); inst_RV_IV(INS_sub, REG_SPBASE, argSize, EA_PTRSIZE); AddStackLevel(argSize); m_pushStkArg = false; @@ -7607,8 +7594,6 @@ bool CodeGen::genAdjustStackForPutArgStk(GenTreePutArgStk* putArgStk) } #endif // FEATURE_SIMD - const unsigned argSize = putArgStk->getArgSize(); - // If the gtPutArgStkKind is one of the push types, we do not pre-adjust the stack. // This is set in Lowering, and is true if and only if: // - This argument contains any GC pointers OR @@ -7621,13 +7606,11 @@ bool CodeGen::genAdjustStackForPutArgStk(GenTreePutArgStk* putArgStk) { case GenTreePutArgStk::Kind::RepInstr: case GenTreePutArgStk::Kind::Unroll: - assert((putArgStk->gtNumberReferenceSlots == 0) && (putArgStk->gtGetOp1()->OperGet() != GT_FIELD_LIST) && - (argSize >= 16)); + assert((putArgStk->gtNumberReferenceSlots == 0) && (source->OperGet() != GT_FIELD_LIST) && (argSize >= 16)); break; case GenTreePutArgStk::Kind::Push: case GenTreePutArgStk::Kind::PushAllSlots: - assert((putArgStk->gtNumberReferenceSlots != 0) || (putArgStk->gtGetOp1()->OperGet() == GT_FIELD_LIST) || - (argSize < 16)); + assert((putArgStk->gtNumberReferenceSlots != 0) || (source->OperGet() == GT_FIELD_LIST) || (argSize < 16)); break; case GenTreePutArgStk::Kind::Invalid: default: @@ -7871,26 +7854,21 @@ void CodeGen::genPutArgStkFieldList(GenTreePutArgStk* putArgStk) // void CodeGen::genPutArgStk(GenTreePutArgStk* putArgStk) { - var_types targetType = putArgStk->TypeGet(); + GenTreePtr data = putArgStk->gtOp1; + var_types targetType = genActualType(data->TypeGet()); #ifdef _TARGET_X86_ genAlignStackBeforeCall(putArgStk); - if (varTypeIsStruct(targetType)) + if ((data->OperGet() != GT_FIELD_LIST) && varTypeIsStruct(targetType)) { (void)genAdjustStackForPutArgStk(putArgStk); genPutStructArgStk(putArgStk); return; } - // The following logic is applicable for x86 arch. - assert(!varTypeIsFloating(targetType) || (targetType == putArgStk->gtOp1->TypeGet())); - - GenTreePtr data = putArgStk->gtOp1; - - // On a 32-bit target, all of the long arguments are handled with GT_FIELD_LIST, - // and the type of the putArgStk is TYP_VOID. + // On a 32-bit target, all of the long arguments are handled with GT_FIELD_LISTs of TYP_INT. assert(targetType != TYP_LONG); const unsigned argSize = putArgStk->getArgSize(); @@ -7936,7 +7914,6 @@ void CodeGen::genPutArgStk(GenTreePutArgStk* putArgStk) #endif // FEATURE_UNIX_AMD64_STRUCT_PASSING noway_assert(targetType != TYP_STRUCT); - assert(!varTypeIsFloating(targetType) || (targetType == putArgStk->gtOp1->TypeGet())); // Get argument offset on stack. // Here we cross check that argument offset hasn't changed from lowering to codegen since @@ -7949,8 +7926,6 @@ void CodeGen::genPutArgStk(GenTreePutArgStk* putArgStk) assert(argOffset == (int)curArgTabEntry->slotNum * TARGET_POINTER_SIZE); #endif - GenTreePtr data = putArgStk->gtOp1; - if (data->isContainedIntOrIImmed()) { getEmitter()->emitIns_S_I(ins_Store(targetType), emitTypeSize(targetType), baseVarNum, argOffset, @@ -8134,7 +8109,8 @@ void CodeGen::genStoreRegToStackArg(var_types type, regNumber srcReg, int offset // For non tail calls this is the outgoingArgSpace. void CodeGen::genPutStructArgStk(GenTreePutArgStk* putArgStk) { - var_types targetType = putArgStk->TypeGet(); + GenTree* source = putArgStk->gtGetOp1(); + var_types targetType = source->TypeGet(); #if defined(_TARGET_X86_) && defined(FEATURE_SIMD) if (targetType == TYP_SIMD12) @@ -8146,7 +8122,7 @@ void CodeGen::genPutStructArgStk(GenTreePutArgStk* putArgStk) if (varTypeIsSIMD(targetType)) { - regNumber srcReg = genConsumeReg(putArgStk->gtGetOp1()); + regNumber srcReg = genConsumeReg(source); assert((srcReg != REG_NA) && (genIsValidFloatReg(srcReg))); genStoreRegToStackArg(targetType, srcReg, 0); return; @@ -8185,7 +8161,7 @@ void CodeGen::genPutStructArgStk(GenTreePutArgStk* putArgStk) // future. assert(m_pushStkArg); - GenTree* srcAddr = putArgStk->gtGetOp1()->gtGetOp1(); + GenTree* srcAddr = source->gtGetOp1(); BYTE* gcPtrs = putArgStk->gtGcPtrs; const unsigned numSlots = putArgStk->gtNumSlots; diff --git a/src/jit/decomposelongs.cpp b/src/jit/decomposelongs.cpp index 174bba2a56..7637726ff2 100644 --- a/src/jit/decomposelongs.cpp +++ b/src/jit/decomposelongs.cpp @@ -274,31 +274,7 @@ GenTree* DecomposeLongs::DecomposeNode(GenTree* tree) // element into two elements: one for each half of the GT_LONG. if ((use.Def()->OperGet() == GT_LONG) && !use.IsDummyUse() && (use.User()->OperGet() == GT_FIELD_LIST)) { - GenTreeOp* value = use.Def()->AsOp(); - Range().Remove(value); - - // The node returned by `use.User()` is the head of the field list. We need to find the actual node that uses - // the `GT_LONG` so that we can split it. - GenTreeFieldList* listNode = use.User()->AsFieldList(); - for (; listNode != nullptr; listNode = listNode->Rest()) - { - if (listNode->Current() == value) - { - break; - } - } - - assert(listNode != nullptr); - GenTree* rest = listNode->gtOp2; - - GenTreeFieldList* loNode = listNode; - loNode->gtOp1 = value->gtOp1; - loNode->gtFieldType = TYP_INT; - - GenTreeFieldList* hiNode = - new (m_compiler, GT_FIELD_LIST) GenTreeFieldList(value->gtOp2, loNode->gtFieldOffset + 4, TYP_INT, loNode); - - hiNode->gtOp2 = rest; + DecomposeFieldList(use.User()->AsFieldList(), use.Def()->AsOp()); } #ifdef DEBUG @@ -725,6 +701,49 @@ GenTree* DecomposeLongs::DecomposeCnsLng(LIR::Use& use) } //------------------------------------------------------------------------ +// DecomposeFieldList: Decompose GT_FIELD_LIST. +// +// Arguments: +// listNode - the head of the FIELD_LIST that contains the given GT_LONG. +// longNode - the node to decompose +// +// Return Value: +// The next node to process. +// +// Notes: +// Split a LONG field list element into two elements: one for each half of the GT_LONG. +// +GenTree* DecomposeLongs::DecomposeFieldList(GenTreeFieldList* listNode, GenTreeOp* longNode) +{ + assert(longNode->OperGet() == GT_LONG); + // We are given the head of the field list. We need to find the actual node that uses + // the `GT_LONG` so that we can split it. + for (; listNode != nullptr; listNode = listNode->Rest()) + { + if (listNode->Current() == longNode) + { + break; + } + } + assert(listNode != nullptr); + + Range().Remove(longNode); + + GenTree* rest = listNode->gtOp2; + + GenTreeFieldList* loNode = listNode; + loNode->gtType = TYP_INT; + loNode->gtOp1 = longNode->gtOp1; + loNode->gtFieldType = TYP_INT; + + GenTreeFieldList* hiNode = + new (m_compiler, GT_FIELD_LIST) GenTreeFieldList(longNode->gtOp2, loNode->gtFieldOffset + 4, TYP_INT, loNode); + hiNode->gtOp2 = rest; + + return listNode->gtNext; +} + +//------------------------------------------------------------------------ // DecomposeCall: Decompose GT_CALL. // // Arguments: diff --git a/src/jit/decomposelongs.h b/src/jit/decomposelongs.h index 7a0d6ff5ba..c008fa255c 100644 --- a/src/jit/decomposelongs.h +++ b/src/jit/decomposelongs.h @@ -45,6 +45,7 @@ private: GenTree* DecomposeStoreLclFld(LIR::Use& use); GenTree* DecomposeCast(LIR::Use& use); GenTree* DecomposeCnsLng(LIR::Use& use); + GenTree* DecomposeFieldList(GenTreeFieldList* listNode, GenTreeOp* longNode); GenTree* DecomposeCall(LIR::Use& use); GenTree* DecomposeInd(LIR::Use& use); GenTree* DecomposeStoreInd(LIR::Use& use); diff --git a/src/jit/flowgraph.cpp b/src/jit/flowgraph.cpp index 06e0d3f712..01b59d4b97 100644 --- a/src/jit/flowgraph.cpp +++ b/src/jit/flowgraph.cpp @@ -14465,6 +14465,10 @@ bool Compiler::fgOptimizeBranchToNext(BasicBlock* block, BasicBlock* bNext, Basi LIR::Range& blockRange = LIR::AsRange(block); GenTree* jmp = blockRange.LastNode(); assert(jmp->OperIsConditionalJump()); + if (jmp->OperGet() == GT_JTRUE) + { + jmp->gtOp.gtOp1->gtFlags &= ~GTF_SET_FLAGS; + } bool isClosed; unsigned sideEffects; diff --git a/src/jit/gentree.cpp b/src/jit/gentree.cpp index 540d1686fc..1fdcc0cf4a 100644 --- a/src/jit/gentree.cpp +++ b/src/jit/gentree.cpp @@ -1089,6 +1089,13 @@ bool GenTreeCall::AreArgsComplete() const return false; } +#if !defined(FEATURE_PUT_STRUCT_ARG_STK) && !defined(LEGACY_BACKEND) +unsigned GenTreePutArgStk::getArgSize() +{ + return genTypeSize(genActualType(gtOp1->gtType)); +} +#endif // !defined(FEATURE_PUT_STRUCT_ARG_STK) && !defined(LEGACY_BACKEND) + /***************************************************************************** * * Returns non-zero if the two trees are identical. diff --git a/src/jit/gentree.h b/src/jit/gentree.h index e8803e0e1d..2ef1486481 100644 --- a/src/jit/gentree.h +++ b/src/jit/gentree.h @@ -1122,9 +1122,12 @@ public: return false; } - if (gtOper == GT_NOP || gtOper == GT_CALL) + if (gtType == TYP_VOID) { - return gtType != TYP_VOID; + // These are the only operators which can produce either VOID or non-VOID results. + assert(OperIs(GT_NOP, GT_CALL, GT_LOCKADD, GT_FIELD_LIST, GT_COMMA) || OperIsCompare() || OperIsLong() || + OperIsSIMD()); + return false; } if (gtOper == GT_FIELD_LIST) @@ -1177,13 +1180,13 @@ public: inline void ClearUnusedValue(); inline bool IsUnusedValue() const; - bool OperIs(genTreeOps oper) + bool OperIs(genTreeOps oper) const { return OperGet() == oper; } template <typename... T> - bool OperIs(genTreeOps oper, T... rest) + bool OperIs(genTreeOps oper, T... rest) const { return OperIs(oper) || OperIs(rest...); } @@ -1613,7 +1616,7 @@ public: return OperIsAtomicOp(gtOper); } - // This is basically here for cleaner FEATURE_SIMD #ifdefs. + // This is here for cleaner FEATURE_SIMD #ifdefs. static bool OperIsSIMD(genTreeOps gtOper) { #ifdef FEATURE_SIMD @@ -1623,11 +1626,26 @@ public: #endif // !FEATURE_SIMD } - bool OperIsSIMD() + bool OperIsSIMD() const { return OperIsSIMD(gtOper); } + // This is here for cleaner GT_LONG #ifdefs. + static bool OperIsLong(genTreeOps gtOper) + { +#if defined(_TARGET_64BIT_) || defined(LEGACY_BACKEND) + return false; +#else + return gtOper == GT_LONG; +#endif + } + + bool OperIsLong() const + { + return OperIsLong(gtOper); + } + bool OperIsFieldListHead() { return (gtOper == GT_FIELD_LIST) && ((gtFlags & GTF_FIELD_LIST_HEAD) != 0); @@ -3160,6 +3178,7 @@ struct GenTreeFieldList : public GenTreeArgList assert(!arg->OperIsAnyList()); gtFieldOffset = fieldOffset; gtFieldType = fieldType; + gtType = fieldType; if (prevList == nullptr) { gtFlags |= GTF_FIELD_LIST_HEAD; @@ -3973,7 +3992,7 @@ struct GenTreeMultiRegOp : public GenTreeOp { regNumber gtOtherReg; - // GTF_SPILL or GTF_SPILLED flag on a multi-reg call node indicates that one or + // GTF_SPILL or GTF_SPILLED flag on a multi-reg node indicates that one or // more of its result regs are in that state. The spill flag of each of the // return register is stored here. We only need 2 bits per returned register, // so this is treated as a 2-bit array. No architecture needs more than 8 bits. @@ -5287,7 +5306,9 @@ struct GenTreePutArgStk : public GenTreeUnOp unsigned gtNumberReferenceSlots; // Number of reference slots. BYTE* gtGcPtrs; // gcPointers -#endif // FEATURE_PUT_STRUCT_ARG_STK +#elif !defined(LEGACY_BACKEND) + unsigned getArgSize(); +#endif // !LEGACY_BACKEND #if defined(DEBUG) || defined(UNIX_X86_ABI) GenTreeCall* gtCall; // the call node to which this argument belongs diff --git a/src/jit/gtlist.h b/src/jit/gtlist.h index 939f00c334..110fe3b29c 100644 --- a/src/jit/gtlist.h +++ b/src/jit/gtlist.h @@ -57,7 +57,7 @@ GTNODE(ARR_LENGTH , GenTreeArrLen ,0,GTK_UNOP|GTK_EXOP) // array GTNODE(INTRINSIC , GenTreeIntrinsic ,0,GTK_BINOP|GTK_EXOP) // intrinsics -GTNODE(LOCKADD , GenTreeOp ,0,GTK_BINOP|GTK_NOVALUE) +GTNODE(LOCKADD , GenTreeOp ,0,GTK_BINOP) GTNODE(XADD , GenTreeOp ,0,GTK_BINOP) GTNODE(XCHG , GenTreeOp ,0,GTK_BINOP) GTNODE(CMPXCHG , GenTreeCmpXchg ,0,GTK_SPECIAL) diff --git a/src/jit/lir.cpp b/src/jit/lir.cpp index a2343ad313..80b9c34f2c 100644 --- a/src/jit/lir.cpp +++ b/src/jit/lir.cpp @@ -981,7 +981,11 @@ void LIR::Range::Remove(GenTree* node, bool markOperandsUnused) if (markOperandsUnused) { node->VisitOperands([](GenTree* operand) -> GenTree::VisitResult { - operand->SetUnusedValue(); + // The operand of JTRUE does not produce a value (just sets the flags). + if (operand->IsValue()) + { + operand->SetUnusedValue(); + } return GenTree::VisitResult::Continue; }); } @@ -1608,8 +1612,11 @@ bool LIR::Range::CheckLIR(Compiler* compiler, bool checkUnusedValues) const // The GT_NOP case is because sometimes we eliminate stack argument stores as dead, but // instead of removing them we replace with a NOP. // ARGPLACE nodes are not represented in the LIR sequence. Ignore them. - assert((node->OperGet() == GT_CALL) && - (def->OperIsStore() || def->OperIs(GT_PUTARG_STK, GT_NOP, GT_ARGPLACE))); + // The argument of a JTRUE doesn't produce a value (just sets a flag). + assert(((node->OperGet() == GT_CALL) && + (def->OperIsStore() || def->OperIs(GT_PUTARG_STK, GT_NOP, GT_ARGPLACE))) || + ((node->OperGet() == GT_JTRUE) && (def->TypeGet() == TYP_VOID) && + ((def->gtFlags & GTF_SET_FLAGS) != 0))); continue; } diff --git a/src/jit/lower.cpp b/src/jit/lower.cpp index c2c405fad6..70d95bd31c 100644 --- a/src/jit/lower.cpp +++ b/src/jit/lower.cpp @@ -1072,11 +1072,27 @@ GenTreePtr Lowering::NewPutArg(GenTreeCall* call, GenTreePtr arg, fgArgTabEntryP // This provides the info to put this argument in in-coming arg area slot // instead of in out-going arg area slot. - PUT_STRUCT_ARG_STK_ONLY(assert(info->isStruct == varTypeIsStruct(type))); // Make sure state is correct + // Make sure state is correct. The PUTARG_STK has TYP_VOID, as it doesn't produce + // a result. So the type of its operand must be the correct type to push on the stack. + // For a FIELD_LIST, this will be the type of the field (not the type of the arg), + // but otherwise it is generally the type of the operand. However, in the case of + // TYP_SIMD12 the type of the operand may need to be modified. + PUT_STRUCT_ARG_STK_ONLY(assert(info->isStruct == varTypeIsStruct(type))); +#if defined(FEATURE_SIMD) && defined(FEATURE_PUT_STRUCT_ARG_STK) + if (type == TYP_SIMD12) + { + arg->gtType = type; + } + else +#endif // defined(FEATURE_SIMD) && defined(FEATURE_PUT_STRUCT_ARG_STK) + { + assert((genActualType(arg->TypeGet()) == type) || (arg->OperGet() == GT_FIELD_LIST)); + } - putArg = new (comp, GT_PUTARG_STK) - GenTreePutArgStk(GT_PUTARG_STK, type, arg, info->slotNum PUT_STRUCT_ARG_STK_ONLY_ARG(info->numSlots), - call->IsFastTailCall(), call); + putArg = + new (comp, GT_PUTARG_STK) GenTreePutArgStk(GT_PUTARG_STK, TYP_VOID, arg, + info->slotNum PUT_STRUCT_ARG_STK_ONLY_ARG(info->numSlots), + call->IsFastTailCall(), call); #ifdef FEATURE_PUT_STRUCT_ARG_STK // If the ArgTabEntry indicates that this arg is a struct @@ -1259,7 +1275,7 @@ void Lowering::LowerArg(GenTreeCall* call, GenTreePtr* ppArg) GenTreeFieldList* fieldList = new (comp, GT_FIELD_LIST) GenTreeFieldList(argLo, 0, TYP_INT, nullptr); // Only the first fieldList node (GTF_FIELD_LIST_HEAD) is in the instruction sequence. (void)new (comp, GT_FIELD_LIST) GenTreeFieldList(argHi, 4, TYP_INT, fieldList); - putArg = NewPutArg(call, fieldList, info, TYP_VOID); + putArg = NewPutArg(call, fieldList, info, type); BlockRange().InsertBefore(arg, putArg); BlockRange().Remove(arg); @@ -1268,17 +1284,17 @@ void Lowering::LowerArg(GenTreeCall* call, GenTreePtr* ppArg) } else { + assert(arg->OperGet() == GT_LONG); // For longs, we will replace the GT_LONG with a GT_FIELD_LIST, and put that under a PUTARG_STK. // Although the hi argument needs to be pushed first, that will be handled by the general case, // in which the fields will be reversed. - noway_assert(arg->OperGet() == GT_LONG); assert(info->numSlots == 2); GenTreePtr argLo = arg->gtGetOp1(); GenTreePtr argHi = arg->gtGetOp2(); GenTreeFieldList* fieldList = new (comp, GT_FIELD_LIST) GenTreeFieldList(argLo, 0, TYP_INT, nullptr); // Only the first fieldList node (GTF_FIELD_LIST_HEAD) is in the instruction sequence. (void)new (comp, GT_FIELD_LIST) GenTreeFieldList(argHi, 4, TYP_INT, fieldList); - putArg = NewPutArg(call, fieldList, info, TYP_VOID); + putArg = NewPutArg(call, fieldList, info, type); putArg->gtRegNum = info->regNum; // We can't call ReplaceArgWithPutArgOrCopy here because it presumes that we are keeping the original arg. @@ -2742,7 +2758,7 @@ GenTree* Lowering::LowerJTrue(GenTreeOp* jtrue) relop->SetOper(GT_JCMP); relop->gtFlags &= ~(GTF_JCMP_TST | GTF_JCMP_EQ); relop->gtFlags |= flags; - relop->gtLsraInfo.isNoRegCompare = true; + relop->gtType = TYP_VOID; relopOp2->SetContained(); @@ -5721,8 +5737,9 @@ void Lowering::ContainCheckRet(GenTreeOp* ret) void Lowering::ContainCheckJTrue(GenTreeOp* node) { // The compare does not need to be generated into a register. - GenTree* cmp = node->gtGetOp1(); - cmp->gtLsraInfo.isNoRegCompare = true; + GenTree* cmp = node->gtGetOp1(); + cmp->gtType = TYP_VOID; + cmp->gtFlags |= GTF_SET_FLAGS; } #endif // !LEGACY_BACKEND diff --git a/src/jit/lowerxarch.cpp b/src/jit/lowerxarch.cpp index 415c10619b..f509475fe1 100644 --- a/src/jit/lowerxarch.cpp +++ b/src/jit/lowerxarch.cpp @@ -474,8 +474,9 @@ void Lowering::LowerPutArgStk(GenTreePutArgStk* putArgStk) BlockRange().InsertAfter(fieldList, head); BlockRange().Remove(fieldList); - fieldList = head; - putArgStk->gtOp1 = fieldList; + fieldList = head; + putArgStk->gtOp1 = fieldList; + putArgStk->gtType = fieldList->gtType; } // Now that the fields have been sorted, the kind of code we will generate. @@ -557,7 +558,7 @@ void Lowering::LowerPutArgStk(GenTreePutArgStk* putArgStk) GenTreePtr src = putArgStk->gtOp1; #ifdef FEATURE_PUT_STRUCT_ARG_STK - if (putArgStk->TypeGet() != TYP_STRUCT) + if (src->TypeGet() != TYP_STRUCT) #endif // FEATURE_PUT_STRUCT_ARG_STK { // If the child of GT_PUTARG_STK is a constant, we don't need a register to @@ -858,8 +859,7 @@ void Lowering::LowerSIMD(GenTreeSIMD* simdNode) } simdNode->gtFlags |= GTF_SET_FLAGS; - simdNode->SetUnusedValue(); - simdNode->gtLsraInfo.isNoRegCompare = true; + simdNode->gtType = TYP_VOID; } #endif ContainCheckSIMD(simdNode); diff --git a/src/jit/lsra.cpp b/src/jit/lsra.cpp index a59af282e6..4f178b68dd 100644 --- a/src/jit/lsra.cpp +++ b/src/jit/lsra.cpp @@ -4839,7 +4839,7 @@ void LinearScan::buildIntervals() TreeNodeInfoInit(node); // If the node produces an unused value, mark it as a local def-use - if (node->IsValue() && node->IsUnusedValue() && !node->gtLsraInfo.isNoRegCompare) + if (node->IsValue() && node->IsUnusedValue()) { node->gtLsraInfo.isLocalDefUse = true; node->gtLsraInfo.dstCount = 0; diff --git a/src/jit/lsra.h b/src/jit/lsra.h index d149b3207a..4215038488 100644 --- a/src/jit/lsra.h +++ b/src/jit/lsra.h @@ -1225,7 +1225,7 @@ private: void TreeNodeInfoInitCheckByteable(GenTree* tree); - void SetDelayFree(GenTree* delayUseSrc); + bool SetDelayFree(GenTree* delayUseSrc); void TreeNodeInfoInitSimple(GenTree* tree); int GetOperandSourceCount(GenTree* node); diff --git a/src/jit/lsraarm.cpp b/src/jit/lsraarm.cpp index f35a23a72b..908f74b7de 100644 --- a/src/jit/lsraarm.cpp +++ b/src/jit/lsraarm.cpp @@ -214,7 +214,18 @@ void LinearScan::TreeNodeInfoInit(GenTree* tree) } // Set the default dstCount. This may be modified below. - info->dstCount = tree->IsValue() ? 1 : 0; + if (tree->IsValue()) + { + info->dstCount = 1; + if (tree->IsUnusedValue()) + { + info->isLocalDefUse = true; + } + } + else + { + info->dstCount = 0; + } switch (tree->OperGet()) { @@ -442,6 +453,10 @@ void LinearScan::TreeNodeInfoInit(GenTree* tree) case GT_LONG: assert(tree->IsUnusedValue()); // Contained nodes are already processed, only unused GT_LONG can reach here. + // An unused GT_LONG doesn't produce any registers. + tree->gtType = TYP_VOID; + tree->ClearUnusedValue(); + info->isLocalDefUse = false; // An unused GT_LONG node needs to consume its sources. info->srcCount = 2; @@ -785,6 +800,8 @@ void LinearScan::TreeNodeInfoInit(GenTree* tree) } // We need to be sure that we've set info->srcCount and info->dstCount appropriately assert((info->dstCount < 2) || tree->IsMultiRegNode()); + assert(info->isLocalDefUse == (tree->IsValue() && tree->IsUnusedValue())); + assert(!tree->IsUnusedValue() || (info->dstCount != 0)); } #endif // _TARGET_ARM_ diff --git a/src/jit/lsraarm64.cpp b/src/jit/lsraarm64.cpp index 83f6d64983..b774699e92 100644 --- a/src/jit/lsraarm64.cpp +++ b/src/jit/lsraarm64.cpp @@ -58,7 +58,18 @@ void LinearScan::TreeNodeInfoInit(GenTree* tree) } // Set the default dstCount. This may be modified below. - info->dstCount = tree->IsValue() ? 1 : 0; + if (tree->IsValue()) + { + info->dstCount = 1; + if (tree->IsUnusedValue()) + { + info->isLocalDefUse = true; + } + } + else + { + info->dstCount = 0; + } switch (tree->OperGet()) { @@ -667,6 +678,8 @@ void LinearScan::TreeNodeInfoInit(GenTree* tree) } // We need to be sure that we've set info->srcCount and info->dstCount appropriately assert((info->dstCount < 2) || tree->IsMultiRegCall()); + assert(info->isLocalDefUse == (tree->IsValue() && tree->IsUnusedValue())); + assert(!tree->IsUnusedValue() || (info->dstCount != 0)); } //------------------------------------------------------------------------ diff --git a/src/jit/lsraarmarch.cpp b/src/jit/lsraarmarch.cpp index 53e223dab6..9351ede35c 100644 --- a/src/jit/lsraarmarch.cpp +++ b/src/jit/lsraarmarch.cpp @@ -92,15 +92,8 @@ void LinearScan::TreeNodeInfoInitCmp(GenTreePtr tree) { TreeNodeInfo* info = &(tree->gtLsraInfo); + assert((info->dstCount == 1) || (tree->TypeGet() == TYP_VOID)); info->srcCount = tree->gtOp.gtOp2->isContained() ? 1 : 2; - if (info->isNoRegCompare) - { - info->dstCount = 0; - } - else - { - assert((info->dstCount == 1) || tree->OperIs(GT_CMP, GT_TEST_EQ, GT_TEST_NE)); - } } void LinearScan::TreeNodeInfoInitGCWriteBarrier(GenTree* tree) diff --git a/src/jit/lsraxarch.cpp b/src/jit/lsraxarch.cpp index 4fff111506..5eba59e6db 100644 --- a/src/jit/lsraxarch.cpp +++ b/src/jit/lsraxarch.cpp @@ -116,12 +116,22 @@ void LinearScan::TreeNodeInfoInit(GenTree* tree) { info->dstCount = 0; assert(info->srcCount == 0); - TreeNodeInfoInitCheckByteable(tree); return; } // Set the default dstCount. This may be modified below. - info->dstCount = tree->IsValue() ? 1 : 0; + if (tree->IsValue()) + { + info->dstCount = 1; + if (tree->IsUnusedValue()) + { + info->isLocalDefUse = true; + } + } + else + { + info->dstCount = 0; + } // floating type generates AVX instruction (vmovss etc.), set the flag SetContainsAVXFlags(varTypeIsFloating(tree->TypeGet())); @@ -150,6 +160,7 @@ void LinearScan::TreeNodeInfoInit(GenTree* tree) info->regOptional = false; tree->SetContained(); info->dstCount = 0; + return; } } __fallthrough; @@ -194,6 +205,10 @@ void LinearScan::TreeNodeInfoInit(GenTree* tree) case GT_LONG: assert(tree->IsUnusedValue()); // Contained nodes are already processed, only unused GT_LONG can reach here. + // An unused GT_LONG doesn't produce any registers. + tree->gtType = TYP_VOID; + tree->ClearUnusedValue(); + info->isLocalDefUse = false; // An unused GT_LONG node needs to consume its sources. info->srcCount = 2; @@ -439,7 +454,7 @@ void LinearScan::TreeNodeInfoInit(GenTree* tree) tree->gtCmpXchg.gtOpComparand->gtLsraInfo.setSrcCandidates(this, RBM_RAX); tree->gtCmpXchg.gtOpLocation->gtLsraInfo.setSrcCandidates(this, allRegs(TYP_INT) & ~RBM_RAX); tree->gtCmpXchg.gtOpValue->gtLsraInfo.setSrcCandidates(this, allRegs(TYP_INT) & ~RBM_RAX); - tree->gtLsraInfo.setDstCandidates(this, RBM_RAX); + info->setDstCandidates(this, RBM_RAX); break; case GT_LOCKADD: @@ -632,16 +647,18 @@ void LinearScan::TreeNodeInfoInit(GenTree* tree) // Commutative opers like add/mul/and/or/xor could reverse the order of // operands if it is safe to do so. In such a case we would like op2 to be // target preferenced instead of op1. - if (tree->OperIsCommutative() && op1->gtLsraInfo.dstCount == 0 && op2 != nullptr) + if (tree->OperIsCommutative() && op1->isContained() && op2 != nullptr) { op1 = op2; op2 = tree->gtOp.gtOp1; } - // If we have a read-modify-write operation, we want to preference op1 to the target. - // If op1 is contained, we don't want to preference it, but it won't - // show up as a source in that case, so it will be ignored. - op1->gtLsraInfo.isTgtPref = true; + // If we have a read-modify-write operation, we want to preference op1 to the target, + // if it is not contained. + if (!op1->isContained()) + { + op1->gtLsraInfo.isTgtPref = true; + } // Is this a non-commutative operator, or is op2 a contained memory op? // In either case, we need to make op2 remain live until the op is complete, by marking @@ -672,29 +689,28 @@ void LinearScan::TreeNodeInfoInit(GenTree* tree) // which allows its second operand to be a contained // immediate wheres xadd instruction requires its // second operand to be in a register. - assert(tree->gtLsraInfo.dstCount == 0); - - // Give it an artificial type and mark it isLocalDefUse = true. - // This would result in a Def position created but not considered - // consumed by its parent node. - tree->gtType = TYP_INT; - tree->gtLsraInfo.isLocalDefUse = true; + assert(info->dstCount == 0); + + // Give it an artificial type and mark it as an unused value. + // This results in a Def position created but not considered consumed by its parent node. + tree->gtType = TYP_INT; + info->dstCount = 1; + info->isLocalDefUse = true; + tree->SetUnusedValue(); } else { - assert(tree->gtLsraInfo.dstCount != 0); + assert(info->dstCount != 0); } delayUseSrc = op1; } - else if ((op2 != nullptr) && - (!tree->OperIsCommutative() || (isContainableMemoryOp(op2) && (op2->gtLsraInfo.srcCount == 0)))) + else if ((op2 != nullptr) && (!tree->OperIsCommutative() || (op2->isContained() && !op2->IsCnsIntOrI()))) { delayUseSrc = op2; } - if (delayUseSrc != nullptr) + if ((delayUseSrc != nullptr) && SetDelayFree(delayUseSrc)) { - SetDelayFree(delayUseSrc); info->hasDelayFreeSrc = true; } } @@ -702,36 +718,43 @@ void LinearScan::TreeNodeInfoInit(GenTree* tree) TreeNodeInfoInitCheckByteable(tree); - if (tree->IsUnusedValue() && (info->dstCount != 0)) - { - info->isLocalDefUse = true; - } // We need to be sure that we've set info->srcCount and info->dstCount appropriately assert((info->dstCount < 2) || (tree->IsMultiRegCall() && info->dstCount == MAX_RET_REG_COUNT)); + assert(info->isLocalDefUse == (tree->IsValue() && tree->IsUnusedValue())); + assert(!tree->IsUnusedValue() || (info->dstCount != 0)); } -void LinearScan::SetDelayFree(GenTree* delayUseSrc) +bool LinearScan::SetDelayFree(GenTree* delayUseSrc) { // If delayUseSrc is an indirection and it doesn't produce a result, then we need to set "delayFree' // on the base & index, if any. // Otherwise, we set it on delayUseSrc itself. - if (delayUseSrc->isIndir() && (delayUseSrc->gtLsraInfo.dstCount == 0)) + bool returnValue = false; + if (delayUseSrc->isContained()) { - GenTree* base = delayUseSrc->AsIndir()->Base(); - GenTree* index = delayUseSrc->AsIndir()->Index(); - if (base != nullptr) - { - base->gtLsraInfo.isDelayFree = true; - } - if (index != nullptr) + // If delayUseSrc is a non-Indir contained node (e.g. a local) there's no register use to delay. + if (delayUseSrc->isIndir()) { - index->gtLsraInfo.isDelayFree = true; + GenTree* base = delayUseSrc->AsIndir()->Base(); + GenTree* index = delayUseSrc->AsIndir()->Index(); + if (base != nullptr) + { + base->gtLsraInfo.isDelayFree = true; + returnValue = true; + } + if (index != nullptr) + { + index->gtLsraInfo.isDelayFree = true; + returnValue = true; + } } } else { delayUseSrc->gtLsraInfo.isDelayFree = true; + returnValue = true; } + return returnValue; } //------------------------------------------------------------------------ @@ -886,7 +909,8 @@ void LinearScan::TreeNodeInfoInitSimple(GenTree* tree) void LinearScan::TreeNodeInfoInitReturn(GenTree* tree) { TreeNodeInfo* info = &(tree->gtLsraInfo); - GenTree* op1 = tree->gtGetOp1(); + assert(info->dstCount == 0); + GenTree* op1 = tree->gtGetOp1(); #if !defined(_TARGET_64BIT_) if (tree->TypeGet() == TYP_LONG) @@ -897,15 +921,14 @@ void LinearScan::TreeNodeInfoInitReturn(GenTree* tree) info->srcCount = 2; loVal->gtLsraInfo.setSrcCandidates(this, RBM_LNGRET_LO); hiVal->gtLsraInfo.setSrcCandidates(this, RBM_LNGRET_HI); - assert(info->dstCount == 0); } else #endif // !defined(_TARGET_64BIT_) + if ((tree->TypeGet() != TYP_VOID) && !op1->isContained()) { regMaskTP useCandidates = RBM_NONE; - info->srcCount = ((tree->TypeGet() == TYP_VOID) || op1->isContained()) ? 0 : 1; - assert(info->dstCount == 0); + info->srcCount = 1; #ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING if (varTypeIsStruct(tree)) @@ -926,8 +949,7 @@ void LinearScan::TreeNodeInfoInitReturn(GenTree* tree) switch (tree->TypeGet()) { case TYP_VOID: - useCandidates = RBM_NONE; - break; + unreached(); case TYP_FLOAT: useCandidates = RBM_FLOATRET; break; @@ -975,56 +997,60 @@ void LinearScan::TreeNodeInfoInitShiftRotate(GenTree* tree) // We will allow whatever can be encoded - hope you know what you are doing. if (!shiftBy->isContained()) { - source->gtLsraInfo.setSrcCandidates(this, allRegs(TYP_INT) & ~RBM_RCX); shiftBy->gtLsraInfo.setSrcCandidates(this, RBM_RCX); - info->setDstCandidates(this, allRegs(TYP_INT) & ~RBM_RCX); - if (!tree->isContained()) + if (!source->isContained()) { - info->srcCount = 2; - } - } - else - { - // Note that Rotate Left/Right instructions don't set ZF and SF flags. - // - // If the operand being shifted is 32-bits then upper three bits are masked - // by hardware to get actual shift count. Similarly for 64-bit operands - // shift count is narrowed to [0..63]. If the resulting shift count is zero, - // then shift operation won't modify flags. - // - // TODO-CQ-XARCH: We can optimize generating 'test' instruction for GT_EQ/NE(shift, 0) - // if the shift count is known to be non-zero and in the range depending on the - // operand size. - if (!tree->isContained()) - { - info->srcCount = 1; + source->gtLsraInfo.setSrcCandidates(this, allRegs(TYP_INT) & ~RBM_RCX); } + info->setDstCandidates(this, allRegs(TYP_INT) & ~RBM_RCX); } -#ifdef _TARGET_X86_ - // The first operand of a GT_LSH_HI and GT_RSH_LO oper is a GT_LONG so that - // we can have a three operand form. Increment the srcCount. - if (tree->OperGet() == GT_LSH_HI || tree->OperGet() == GT_RSH_LO) + // Note that Rotate Left/Right instructions don't set ZF and SF flags. + // + // If the operand being shifted is 32-bits then upper three bits are masked + // by hardware to get actual shift count. Similarly for 64-bit operands + // shift count is narrowed to [0..63]. If the resulting shift count is zero, + // then shift operation won't modify flags. + // + // TODO-CQ-XARCH: We can optimize generating 'test' instruction for GT_EQ/NE(shift, 0) + // if the shift count is known to be non-zero and in the range depending on the + // operand size. + + if (!tree->isContained()) { - assert((source->OperGet() == GT_LONG) && source->isContained()); +#ifdef _TARGET_X86_ + // The first operand of a GT_LSH_HI and GT_RSH_LO oper is a GT_LONG so that + // we can have a three operand form. Increment the srcCount. + if (tree->OperGet() == GT_LSH_HI || tree->OperGet() == GT_RSH_LO) + { + assert((source->OperGet() == GT_LONG) && source->isContained()); - info->srcCount++; + if (tree->OperGet() == GT_LSH_HI) + { + GenTreePtr sourceLo = source->gtOp.gtOp1; + sourceLo->gtLsraInfo.isDelayFree = true; + } + else + { + GenTreePtr sourceHi = source->gtOp.gtOp2; + sourceHi->gtLsraInfo.isDelayFree = true; + } - if (tree->OperGet() == GT_LSH_HI) - { - GenTreePtr sourceLo = source->gtOp.gtOp1; - sourceLo->gtLsraInfo.isDelayFree = true; + source->gtLsraInfo.hasDelayFreeSrc = true; + info->hasDelayFreeSrc = true; + info->srcCount += 2; } else +#endif + if (!source->isContained()) { - GenTreePtr sourceHi = source->gtOp.gtOp2; - sourceHi->gtLsraInfo.isDelayFree = true; + info->srcCount++; + } + if (!shiftBy->isContained()) + { + info->srcCount++; } - - source->gtLsraInfo.hasDelayFreeSrc = true; - info->hasDelayFreeSrc = true; } -#endif } //------------------------------------------------------------------------ @@ -1649,9 +1675,12 @@ void LinearScan::TreeNodeInfoInitPutArgStk(GenTreePutArgStk* putArgStk) #endif // _TARGET_X86_ } + GenTreePtr src = putArgStk->gtOp1; + var_types type = src->TypeGet(); + #if defined(FEATURE_SIMD) && defined(_TARGET_X86_) // For PutArgStk of a TYP_SIMD12, we need an extra register. - if (putArgStk->TypeGet() == TYP_SIMD12) + if (type == TYP_SIMD12) { info->srcCount = putArgStk->gtOp1->gtLsraInfo.dstCount; info->internalFloatCount = 1; @@ -1660,14 +1689,13 @@ void LinearScan::TreeNodeInfoInitPutArgStk(GenTreePutArgStk* putArgStk) } #endif // defined(FEATURE_SIMD) && defined(_TARGET_X86_) - if (putArgStk->TypeGet() != TYP_STRUCT) + if (type != TYP_STRUCT) { TreeNodeInfoInitSimple(putArgStk); return; } GenTreePtr dst = putArgStk; - GenTreePtr src = putArgStk->gtOp1; GenTreePtr srcAddr = nullptr; info->srcCount = GetOperandSourceCount(src); @@ -2607,14 +2635,7 @@ void LinearScan::TreeNodeInfoInitCmp(GenTreePtr tree) TreeNodeInfo* info = &(tree->gtLsraInfo); info->srcCount = 0; - if (info->isNoRegCompare) - { - info->dstCount = 0; - } - else - { - assert((info->dstCount == 1) || tree->OperIs(GT_CMP)); - } + assert((info->dstCount == 1) || (tree->TypeGet() == TYP_VOID)); #ifdef _TARGET_X86_ // If the compare is used by a jump, we just need to set the condition codes. If not, then we need @@ -2630,7 +2651,7 @@ void LinearScan::TreeNodeInfoInitCmp(GenTreePtr tree) var_types op1Type = op1->TypeGet(); var_types op2Type = op2->TypeGet(); - if (!op1->gtLsraInfo.isNoRegCompare) + if (op1->TypeGet() != TYP_VOID) { info->srcCount += GetOperandSourceCount(op1); } @@ -2718,9 +2739,8 @@ void LinearScan::TreeNodeInfoInitMul(GenTreePtr tree) { containedMemOp = op2; } - if (containedMemOp != nullptr) + if ((containedMemOp != nullptr) && SetDelayFree(containedMemOp)) { - SetDelayFree(containedMemOp); info->hasDelayFreeSrc = true; } } diff --git a/src/jit/nodeinfo.h b/src/jit/nodeinfo.h index 3f8532bd37..5f03da2776 100644 --- a/src/jit/nodeinfo.h +++ b/src/jit/nodeinfo.h @@ -32,7 +32,6 @@ public: regOptional = false; definesAnyRegisters = false; isInternalRegDelayFree = false; - isNoRegCompare = false; #ifdef DEBUG isInitialized = false; #endif @@ -145,9 +144,6 @@ public: // in which result is produced. unsigned char isInternalRegDelayFree : 1; - // True if this is a compare feeding a JTRUE that doesn't need to be generated into a register. - unsigned char isNoRegCompare : 1; - #ifdef DEBUG // isInitialized is set when the tree node is handled. unsigned char isInitialized : 1; |