diff --git a/src/coreclr/jit/assertionprop.cpp b/src/coreclr/jit/assertionprop.cpp index 033f1af84c5342..327f1a3ca857c0 100644 --- a/src/coreclr/jit/assertionprop.cpp +++ b/src/coreclr/jit/assertionprop.cpp @@ -3835,37 +3835,16 @@ GenTree* Compiler::optAssertionProp_LclFld(ASSERT_VALARG_TP assertions, GenTreeL break; } - // See if the variable is equal to another variable or a constant. + // See if the variable is equal to another variable. // AssertionDsc* const curAssertion = optGetAssertion(assertionIndex); - if (!curAssertion->CanPropLclVar()) - { - continue; - } - - // Copy prop - // - if (curAssertion->op2.kind == O2K_LCLVAR_COPY) + if (curAssertion->CanPropLclVar() && (curAssertion->op2.kind == O2K_LCLVAR_COPY)) { GenTree* const newTree = optCopyAssertionProp(curAssertion, tree, stmt DEBUGARG(assertionIndex)); if (newTree != nullptr) { return newTree; } - - continue; - } - - // Constant prop - // - if (curAssertion->op1.lcl.lclNum == tree->GetLclNum()) - { - GenTree* const newTree = optConstantAssertionProp(curAssertion, tree, stmt DEBUGARG(assertionIndex)); - - if (newTree != nullptr) - { - return newTree; - } } } diff --git a/src/coreclr/jit/codegenarm.cpp b/src/coreclr/jit/codegenarm.cpp index 6ab5c260243931..adbc4147d5e480 100644 --- a/src/coreclr/jit/codegenarm.cpp +++ b/src/coreclr/jit/codegenarm.cpp @@ -1009,9 +1009,6 @@ void CodeGen::genCodeForStoreLclFld(GenTreeLclFld* tree) unsigned varNum = tree->GetLclNum(); LclVarDsc* varDsc = compiler->lvaGetDesc(varNum); - // Ensure that lclVar nodes are typed correctly. - assert(!varDsc->lvNormalizeOnStore() || targetType == genActualType(varDsc->TypeGet())); - GenTree* data = tree->gtOp1; regNumber dataReg = REG_NA; genConsumeRegs(data); diff --git a/src/coreclr/jit/codegenarm64.cpp b/src/coreclr/jit/codegenarm64.cpp index ad76499eb28eff..858fc9b531fb0b 100644 --- a/src/coreclr/jit/codegenarm64.cpp +++ b/src/coreclr/jit/codegenarm64.cpp @@ -2685,9 +2685,6 @@ void CodeGen::genCodeForStoreLclFld(GenTreeLclFld* tree) unsigned varNum = tree->GetLclNum(); LclVarDsc* varDsc = compiler->lvaGetDesc(varNum); - // Ensure that lclVar nodes are typed correctly. - assert(!varDsc->lvNormalizeOnStore() || targetType == genActualType(varDsc->TypeGet())); - GenTree* data = tree->gtOp1; genConsumeRegs(data); diff --git a/src/coreclr/jit/fgdiagnostic.cpp b/src/coreclr/jit/fgdiagnostic.cpp index c5d1aff9aa6414..4b9637df6cf3e2 100644 --- a/src/coreclr/jit/fgdiagnostic.cpp +++ b/src/coreclr/jit/fgdiagnostic.cpp @@ -3032,10 +3032,6 @@ void Compiler::fgDebugCheckFlags(GenTree* tree) expectedFlags |= (GTF_GLOB_REF | GTF_ASG); break; - case GT_LCL_VAR: - assert((tree->gtFlags & GTF_VAR_FOLDED_IND) == 0); - break; - case GT_QMARK: assert(!op1->CanCSE()); assert(op1->OperIsCompare() || op1->IsIntegralConst(0) || op1->IsIntegralConst(1)); diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index 5ce4afae2e4409..6d693102ccee94 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -472,11 +472,6 @@ enum GenTreeFlags : unsigned int GTF_VAR_ITERATOR = 0x01000000, // GT_LCL_VAR -- this is a iterator reference in the loop condition GTF_VAR_CLONED = 0x00800000, // GT_LCL_VAR -- this node has been cloned or is a clone GTF_VAR_CONTEXT = 0x00400000, // GT_LCL_VAR -- this node is part of a runtime lookup - GTF_VAR_FOLDED_IND = 0x00200000, // GT_LCL_VAR -- this node was folded from *(typ*)&lclVar expression tree in fgMorphSmpOp() - // where 'typ' is a small type and 'lclVar' corresponds to a normalized-on-store local variable. - // This flag identifies such nodes in order to make sure that fgDoNormalizeOnStore() is called - // on their parents in post-order morph. - // Relevant for inlining optimizations (see fgInlinePrependStatements) // For additional flags for GT_CALL node see GTF_CALL_M_* diff --git a/src/coreclr/jit/lclmorph.cpp b/src/coreclr/jit/lclmorph.cpp index b344f37c09d878..c03f4fdee1d3b0 100644 --- a/src/coreclr/jit/lclmorph.cpp +++ b/src/coreclr/jit/lclmorph.cpp @@ -293,6 +293,7 @@ class LocalAddressVisitor final : public GenTreeVisitor { None, Nop, + BitCast, LclVar, LclFld }; @@ -702,7 +703,6 @@ class LocalAddressVisitor final : public GenTreeVisitor if (node->OperIs(GT_LCL_VAR, GT_LCL_FLD)) { // If the location is accessed directly then we don't need to do anything. - assert(node->AsLclVarCommon()->GetLclNum() == val.LclNum()); } else @@ -899,9 +899,12 @@ class LocalAddressVisitor final : public GenTreeVisitor { assert(val.IsLocation()); - ClassLayout* indirLayout = nullptr; - IndirTransform transform = SelectLocalIndirTransform(val, user, &indirLayout); - GenTree* indir = val.Node(); + ClassLayout* indirLayout = nullptr; + IndirTransform transform = SelectLocalIndirTransform(val, user, &indirLayout); + GenTree* indir = val.Node(); + unsigned lclNum = val.LclNum(); + LclVarDsc* varDsc = m_compiler->lvaGetDesc(lclNum); + GenTreeLclVarCommon* lclNode = nullptr; switch (transform) { @@ -914,39 +917,56 @@ class LocalAddressVisitor final : public GenTreeVisitor m_stmtModified = true; return; + case IndirTransform::BitCast: + indir->ChangeOper(GT_BITCAST); + indir->gtGetOp1()->ChangeOper(GT_LCL_VAR); + indir->gtGetOp1()->ChangeType(varDsc->TypeGet()); + indir->gtGetOp1()->AsLclVar()->SetLclNum(lclNum); + lclNode = indir->gtGetOp1()->AsLclVarCommon(); + break; + case IndirTransform::LclVar: + if (indir->TypeGet() != varDsc->TypeGet()) + { + assert(genTypeSize(indir) == genTypeSize(varDsc)); // BOOL <-> UBYTE. + indir->ChangeType(varDsc->lvNormalizeOnLoad() ? varDsc->TypeGet() : genActualType(varDsc)); + } indir->ChangeOper(GT_LCL_VAR); - indir->AsLclVar()->SetLclNum(val.LclNum()); + indir->AsLclVar()->SetLclNum(lclNum); + lclNode = indir->AsLclVarCommon(); break; case IndirTransform::LclFld: indir->ChangeOper(GT_LCL_FLD); - indir->AsLclFld()->SetLclNum(val.LclNum()); + indir->AsLclFld()->SetLclNum(lclNum); indir->AsLclFld()->SetLclOffs(val.Offset()); indir->AsLclFld()->SetLayout(indirLayout); + lclNode = indir->AsLclVarCommon(); // Promoted locals aren't currently handled here so partial access can't be // later be transformed into a LCL_VAR and the variable cannot be enregistered. - m_compiler->lvaSetVarDoNotEnregister(val.LclNum() DEBUGARG(DoNotEnregisterReason::LocalField)); + m_compiler->lvaSetVarDoNotEnregister(lclNum DEBUGARG(DoNotEnregisterReason::LocalField)); break; default: unreached(); } - GenTreeLclVarCommon* lclNode = indir->AsLclVarCommon(); - GenTreeFlags lclNodeFlags = GTF_EMPTY; + GenTreeFlags lclNodeFlags = GTF_EMPTY; - if (user->OperIs(GT_ASG) && (user->AsOp()->gtGetOp1() == lclNode)) + if (user->OperIs(GT_ASG) && (user->AsOp()->gtGetOp1() == indir)) { lclNodeFlags |= (GTF_VAR_DEF | GTF_DONT_CSE); - unsigned lhsSize = lclNode->TypeIs(TYP_STRUCT) ? indirLayout->GetSize() : genTypeSize(lclNode); - unsigned lclSize = m_compiler->lvaLclExactSize(val.LclNum()); - if (lhsSize != lclSize) + if (!indir->OperIs(GT_LCL_VAR)) { - assert(lhsSize < lclSize); - lclNodeFlags |= GTF_VAR_USEASG; + unsigned lhsSize = indir->TypeIs(TYP_STRUCT) ? indirLayout->GetSize() : genTypeSize(indir); + unsigned lclSize = m_compiler->lvaLclExactSize(lclNum); + if (lhsSize != lclSize) + { + assert(lhsSize < lclSize); + lclNodeFlags |= GTF_VAR_USEASG; + } } } @@ -981,19 +1001,14 @@ class LocalAddressVisitor final : public GenTreeVisitor LclVarDsc* varDsc = m_compiler->lvaGetDesc(val.LclNum()); - if (varDsc->TypeGet() != TYP_STRUCT) + if (varTypeIsSIMD(varDsc)) { - // TODO-ADDR: Skip integral/floating point variables for now, they're more - // complicated to transform. We can always turn an indirect access of such - // a variable into a LCL_FLD but that blocks enregistration so we need to - // detect those case where we can use LCL_VAR instead, perhaps in conjunction - // with CAST and/or BITCAST. - // Also skip SIMD variables for now, fgMorphFieldAssignToSimdSetElement and + // TODO-ADDR: skip SIMD variables for now, fgMorphFieldAssignToSimdSetElement and // others need to be updated to recognize LCL_FLDs. return IndirTransform::None; } - if (!indir->TypeIs(TYP_STRUCT)) + if (indir->TypeGet() != TYP_STRUCT) { if (varDsc->lvPromoted) { @@ -1002,12 +1017,55 @@ class LocalAddressVisitor final : public GenTreeVisitor return IndirTransform::None; } - // As we are only handling non-promoted STRUCT locals right now, the only - // possible transformation for non-STRUCT indirect uses is LCL_FLD. - assert(varDsc->TypeGet() == TYP_STRUCT); + if (indir->TypeGet() == varDsc->TypeGet()) + { + return IndirTransform::LclVar; + } + + // Locals are not enregistered when optimizations are disabled; there is no point + // in spending time finding LCL_VAR-equivalent trees for them. TODO-ADDR: move + // this check earlier. + if (m_compiler->opts.OptimizationDisabled()) + { + return IndirTransform::LclFld; + } + + // Bool and ubyte are the same type. + if ((indir->TypeIs(TYP_BOOL) && (varDsc->TypeGet() == TYP_UBYTE)) || + (indir->TypeIs(TYP_UBYTE) && (varDsc->TypeGet() == TYP_BOOL))) + { + return IndirTransform::LclVar; + } + + // For small locals on the LHS we can ignore the signed/unsigned diff. + if (user->OperIs(GT_ASG) && (user->gtGetOp1() == indir) && + (varTypeToSigned(indir) == varTypeToSigned(varDsc))) + { + assert(varTypeIsSmall(indir)); + return IndirTransform::LclVar; + } + + // Turn this into a bitcast if we can. + if ((genTypeSize(indir) == genTypeSize(varDsc)) && (varTypeIsFloating(indir) || varTypeIsFloating(varDsc))) + { + // TODO-ADDR: enable this optimization for all users and all targets. + if (user->OperIs(GT_RETURN) && (genTypeSize(indir) <= TARGET_POINTER_SIZE)) + { + return IndirTransform::BitCast; + } + } + return IndirTransform::LclFld; } + if (varDsc->TypeGet() != TYP_STRUCT) + { + // TODO-ADDR: STRUCT uses of primitives require more work: "fgMorphOneAsgBlockOp" + // and init block morphing need to be updated to recognize them. Alternatively, + // we could consider moving some of their functionality here. + return IndirTransform::None; + } + ClassLayout* indirLayout = nullptr; if (indir->OperIs(GT_FIELD)) diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index b99ec4e14797df..d6eaeaba75da7a 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -10807,13 +10807,6 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac, bool* optA { case GT_ASG: - if (op1->OperIs(GT_LCL_VAR) && ((op1->gtFlags & GTF_VAR_FOLDED_IND) != 0)) - { - op1->gtFlags &= ~GTF_VAR_FOLDED_IND; - tree = fgDoNormalizeOnStore(tree); - op2 = tree->gtGetOp2(); - } - lclVarTree = fgIsIndirOfAddrOfLocal(op1); if (lclVarTree != nullptr) { @@ -11230,47 +11223,9 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac, bool* optA } } } - // If the type of the IND (typ) is a "small int", and the type of the local has the - // same width, then we can reduce to just the local variable -- it will be - // correctly normalized. - // - // The below transformation cannot be applied if the local var needs to be normalized on load. - else if (varTypeIsSmall(typ) && (genTypeSize(varDsc) == genTypeSize(typ)) && - !lvaTable[lclNum].lvNormalizeOnLoad()) - { - const bool definitelyLoad = (tree->gtFlags & GTF_DONT_CSE) == 0; - const bool possiblyStore = !definitelyLoad; - - if (possiblyStore || (varTypeIsUnsigned(varDsc) == varTypeIsUnsigned(typ))) - { - typ = temp->TypeGet(); - tree->gtType = typ; - foldAndReturnTemp = true; - - if (possiblyStore) - { - // This node can be on the left-hand-side of an assignment node. - // Mark this node with GTF_VAR_FOLDED_IND to make sure that fgDoNormalizeOnStore() - // is called on its parent in post-order morph. - temp->gtFlags |= GTF_VAR_FOLDED_IND; - } - } - } - // For matching types we can fold - else if (!varTypeIsStruct(typ) && (lvaTable[lclNum].lvType == typ) && - !lvaTable[lclNum].lvNormalizeOnLoad()) - { - tree->gtType = typ = temp->TypeGet(); - foldAndReturnTemp = true; - } // Otherwise will will fold this into a GT_LCL_FLD below // where we check (temp != nullptr) } - else // !temp->OperIsLocal() - { - // We don't try to fold away the GT_IND/GT_ADDR for this case - temp = nullptr; - } } else {