未验证 提交 68fb7fc6 编写于 作者: S SingleAccretion 提交者: GitHub

ZeroObj assertions (#65257)

It is the case that the IR supports the "zero" node for structs
in two positions: on the RHS of an assignment (InitBlk form) and
under a return, in case the ABI return type is scalar.

Meanwhile, assertion propagation "blindly" replaced structs with
zeroes, and so workarounds had to be applied in order for the IR
to remain valid, in the form of the NO_CSE flag, applied either
explicitly (multi-reg returns) or implicitly (ADDR(LCL) created by
"impNormSturctVal" for call args).

This was:

a) A CQ problem in cases where we forgot to clear the NO_CSE flag
   when the node's parent was updated, say after inlining.
b) A burden for enabling struct LCL_VAR arguments, as one had to
   remembered to mark them NO_CSE in all situation.

This change fixes the problem by deleting propagation of zeroes for
local uses, instead propagating them as part of their parents (ASGs
and RETURNs).

This has the CQ benefits of not being affected by stale NO_CSEs and
the drawback of not participating in the "chained" propagation, where
we first copy-propagated something, and then zero-propagated into the
new local as well. These cases seem rather rare, so I decided not to
spend TP on fixing them by looking at the copy assertions in the new
code.

This change also deletes the zero propagation code for SIMDs. It was
only useful in cases we had a promoted SIMD field that was zero-inited
via an InitBlk on the parent struct. That promotion code was (and is)
creating nodes that look like integral constants, except they are of
TYP_SIMD. We should delete that form and use proper SIMD zero nodes
instead, and design the propagation story for them separately.
上级 ad3847ca
......@@ -985,7 +985,7 @@ void Compiler::optPrintAssertion(AssertionDsc* curAssertion, AssertionIndex asse
printf("Copy ");
}
else if ((curAssertion->op2.kind == O2K_CONST_INT) || (curAssertion->op2.kind == O2K_CONST_LONG) ||
(curAssertion->op2.kind == O2K_CONST_DOUBLE))
(curAssertion->op2.kind == O2K_CONST_DOUBLE) || (curAssertion->op2.kind == O2K_ZEROOBJ))
{
printf("Constant ");
}
......@@ -1171,6 +1171,10 @@ void Compiler::optPrintAssertion(AssertionDsc* curAssertion, AssertionIndex asse
}
break;
case O2K_ZEROOBJ:
printf("ZeroObj");
break;
case O2K_SUBRANGE:
IntegralRange::Print(curAssertion->op2.u2);
break;
......@@ -1496,7 +1500,15 @@ AssertionIndex Compiler::optCreateAssertion(GenTree* op1,
// Constant Assertions
//
case GT_CNS_INT:
op2Kind = O2K_CONST_INT;
if (varTypeIsStruct(op1))
{
assert(op2->IsIntegralConst(0));
op2Kind = O2K_ZEROOBJ;
}
else
{
op2Kind = O2K_CONST_INT;
}
goto CNS_COMMON;
case GT_CNS_LNG:
......@@ -2055,6 +2067,15 @@ void Compiler::optDebugCheckAssertion(AssertionDsc* assertion)
}
break;
case O2K_ZEROOBJ:
{
// We only make these assertion for assignments (not control flow).
assert(assertion->assertionKind == OAK_EQUAL);
// We use "optLocalAssertionIsEqualOrNotEqual" to find these.
assert(assertion->op2.u1.iconVal == 0);
}
break;
default:
// for all other 'assertion->op2.kind' values we don't check anything
break;
......@@ -3178,8 +3199,7 @@ GenTree* Compiler::optConstantAssertionProp(AssertionDsc* curAssertion,
break;
case O2K_CONST_LONG:
if (newTree->gtType == TYP_LONG)
if (newTree->TypeIs(TYP_LONG))
{
newTree->BashToConst(curAssertion->op2.lconVal);
}
......@@ -3206,38 +3226,11 @@ GenTree* Compiler::optConstantAssertionProp(AssertionDsc* curAssertion,
else
{
bool isArrIndex = ((tree->gtFlags & GTF_VAR_ARR_INDEX) != 0);
// If we have done constant propagation of a struct type, it is only valid for zero-init,
// and we have to ensure that we have the right zero for the type.
if (varTypeIsStruct(tree))
{
assert(curAssertion->op2.u1.iconVal == 0);
}
#ifdef FEATURE_SIMD
if (varTypeIsSIMD(tree))
{
LclVarDsc* varDsc = lvaGetDesc(lclNum);
var_types simdType = tree->TypeGet();
assert(varDsc->TypeGet() == simdType);
CorInfoType simdBaseJitType = varDsc->GetSimdBaseJitType();
newTree = gtGetSIMDZero(simdType, simdBaseJitType, varDsc->GetStructHnd());
if (newTree == nullptr)
{
return nullptr;
}
}
else
#endif // FEATURE_SIMD
{
var_types type = tree->TypeGet();
if (type == TYP_STRUCT)
{
// LCL_VAR can be init with a GT_CNS_INT, keep its type INT, not STRUCT.
type = TYP_INT;
}
newTree->BashToConst(curAssertion->op2.u1.iconVal, type);
newTree->ClearIconHandleMask();
}
assert(varTypeIsIntegralOrI(tree));
newTree->BashToConst(curAssertion->op2.u1.iconVal, genActualType(tree));
// If we're doing an array index address, assume any constant propagated contributes to the index.
if (isArrIndex)
{
......@@ -3246,26 +3239,6 @@ GenTree* Compiler::optConstantAssertionProp(AssertionDsc* curAssertion,
}
newTree->gtFlags &= ~GTF_VAR_ARR_INDEX;
}
// Constant ints are of type TYP_INT, not any of the short forms.
if (varTypeIsIntegral(newTree->TypeGet()))
{
#ifdef TARGET_64BIT
var_types newType =
(var_types)((curAssertion->op2.u1.iconFlags & GTF_ASSERTION_PROP_LONG) ? TYP_LONG : TYP_INT);
if (newTree->TypeGet() != newType)
{
noway_assert(newTree->gtType != TYP_REF);
newTree->gtType = newType;
}
#else
if (newTree->TypeGet() != TYP_INT)
{
noway_assert(newTree->gtType != TYP_REF && newTree->gtType != TYP_LONG);
newTree->gtType = TYP_INT;
}
#endif
}
break;
default:
......@@ -3293,6 +3266,55 @@ GenTree* Compiler::optConstantAssertionProp(AssertionDsc* curAssertion,
return optAssertionProp_Update(newTree, tree, stmt);
}
//------------------------------------------------------------------------------
// optZeroObjAssertionProp: Find and propagate a ZEROOBJ assertion for the given tree.
//
// Arguments:
// assertions - set of live assertions
// tree - the tree to possibly replace, in-place, with a zero
//
// Returns:
// Whether propagation took place.
//
// Notes:
// Because not all users of struct nodes support "zero" operands, instead of
// propagating ZEROOBJ on locals, we propagate it on their parents.
//
bool Compiler::optZeroObjAssertionProp(GenTree* tree, ASSERT_VALARG_TP assertions)
{
assert(varTypeIsStruct(tree));
// We only make ZEROOBJ assertions in local propagation.
if (!optLocalAssertionProp)
{
return false;
}
if (!tree->OperIs(GT_LCL_VAR) || lvaGetDesc(tree->AsLclVar())->IsAddressExposed())
{
return false;
}
unsigned lclNum = tree->AsLclVar()->GetLclNum();
AssertionIndex assertionIndex = optLocalAssertionIsEqualOrNotEqual(O1K_LCLVAR, lclNum, O2K_ZEROOBJ, 0, assertions);
if (assertionIndex == NO_ASSERTION_INDEX)
{
return false;
}
AssertionDsc* assertion = optGetAssertion(assertionIndex);
JITDUMP("\nAssertion prop in " FMT_BB ":\n", compCurBB->bbNum);
JITDUMPEXEC(optPrintAssertion(assertion, assertionIndex));
DISPNODE(tree);
tree->BashToZeroConst(TYP_INT);
JITDUMP(" =>\n");
DISPNODE(tree);
return true;
}
//------------------------------------------------------------------------------
// optAssertionProp_LclVarTypeCheck: verify compatible types for copy prop
//
......@@ -3520,6 +3542,13 @@ GenTree* Compiler::optAssertionProp_LclVar(ASSERT_VALARG_TP assertions, GenTreeL
continue;
}
// There are no constant assertions for structs.
//
if (varTypeIsStruct(tree))
{
continue;
}
// Constant prop.
//
// The case where the tree type could be different than the LclVar type is caused by
......@@ -3551,6 +3580,68 @@ GenTree* Compiler::optAssertionProp_LclVar(ASSERT_VALARG_TP assertions, GenTreeL
return nullptr;
}
//------------------------------------------------------------------------
// optAssertionProp_Asg: Try and optimize an assignment via assertions.
//
// Propagates ZEROOBJ for the RHS.
//
// Arguments:
// assertions - set of live assertions
// asg - the store to optimize
// stmt - statement containing "asg"
//
// Returns:
// Updated "asg", or "nullptr"
//
// Notes:
// stmt may be nullptr during local assertion prop
//
GenTree* Compiler::optAssertionProp_Asg(ASSERT_VALARG_TP assertions, GenTreeOp* asg, Statement* stmt)
{
GenTree* rhs = asg->gtGetOp2();
if (asg->OperIsCopyBlkOp() && varTypeIsStruct(rhs))
{
if (optZeroObjAssertionProp(rhs, assertions))
{
return optAssertionProp_Update(asg, asg, stmt);
}
}
return nullptr;
}
//------------------------------------------------------------------------
// optAssertionProp_Return: Try and optimize a GT_RETURN via assertions.
//
// Propagates ZEROOBJ for the return value.
//
// Arguments:
// assertions - set of live assertions
// ret - the return node to optimize
// stmt - statement containing "ret"
//
// Returns:
// Updated "ret", or "nullptr"
//
// Notes:
// stmt may be nullptr during local assertion prop
//
GenTree* Compiler::optAssertionProp_Return(ASSERT_VALARG_TP assertions, GenTreeUnOp* ret, Statement* stmt)
{
GenTree* retValue = ret->gtGetOp1();
// Only propagate zeroes that lowering can deal with.
if (!ret->TypeIs(TYP_VOID) && varTypeIsStruct(retValue) && !varTypeIsStruct(info.compRetNativeType))
{
if (optZeroObjAssertionProp(retValue, assertions))
{
return optAssertionProp_Update(ret, ret, stmt);
}
}
return nullptr;
}
/*****************************************************************************
*
* Given a set of "assertions" to search, find an assertion that matches
......@@ -3561,7 +3652,7 @@ AssertionIndex Compiler::optLocalAssertionIsEqualOrNotEqual(
optOp1Kind op1Kind, unsigned lclNum, optOp2Kind op2Kind, ssize_t cnsVal, ASSERT_VALARG_TP assertions)
{
noway_assert((op1Kind == O1K_LCLVAR) || (op1Kind == O1K_EXACT_TYPE) || (op1Kind == O1K_SUBTYPE));
noway_assert((op2Kind == O2K_CONST_INT) || (op2Kind == O2K_IND_CNS_INT));
noway_assert((op2Kind == O2K_CONST_INT) || (op2Kind == O2K_IND_CNS_INT) || (op2Kind == O2K_ZEROOBJ));
if (!optLocalAssertionProp && BitVecOps::IsEmpty(apTraits, assertions))
{
return NO_ASSERTION_INDEX;
......@@ -4721,6 +4812,12 @@ GenTree* Compiler::optAssertionProp(ASSERT_VALARG_TP assertions, GenTree* tree,
case GT_LCL_VAR:
return optAssertionProp_LclVar(assertions, tree->AsLclVarCommon(), stmt);
case GT_ASG:
return optAssertionProp_Asg(assertions, tree->AsOp(), stmt);
case GT_RETURN:
return optAssertionProp_Return(assertions, tree->AsUnOp(), stmt);
case GT_OBJ:
case GT_BLK:
case GT_IND:
......
......@@ -7696,6 +7696,7 @@ public:
O2K_CONST_INT,
O2K_CONST_LONG,
O2K_CONST_DOUBLE,
O2K_ZEROOBJ,
O2K_SUBRANGE,
O2K_COUNT
};
......@@ -7837,6 +7838,9 @@ public:
// exact match because of positive and negative zero.
return (memcmp(&op2.dconVal, &that->op2.dconVal, sizeof(double)) == 0);
case O2K_ZEROOBJ:
return true;
case O2K_LCLVAR_COPY:
return (op2.lcl.lclNum == that->op2.lcl.lclNum) &&
(!vnBased || op2.lcl.ssaNum == that->op2.lcl.ssaNum) &&
......@@ -7973,7 +7977,6 @@ public:
bool optAssertionIsNonNull(GenTree* op,
ASSERT_VALARG_TP assertions DEBUGARG(bool* pVnBased) DEBUGARG(AssertionIndex* pIndex));
// Used for Relop propagation.
AssertionIndex optGlobalAssertionIsEqualOrNotEqual(ASSERT_VALARG_TP assertions, GenTree* op1, GenTree* op2);
AssertionIndex optGlobalAssertionIsEqualOrNotEqualZero(ASSERT_VALARG_TP assertions, GenTree* op1);
AssertionIndex optLocalAssertionIsEqualOrNotEqual(
......@@ -7987,10 +7990,13 @@ public:
GenTree* optConstantAssertionProp(AssertionDsc* curAssertion,
GenTreeLclVarCommon* tree,
Statement* stmt DEBUGARG(AssertionIndex index));
bool optZeroObjAssertionProp(GenTree* tree, ASSERT_VALARG_TP assertions);
// Assertion propagation functions.
GenTree* optAssertionProp(ASSERT_VALARG_TP assertions, GenTree* tree, Statement* stmt, BasicBlock* block);
GenTree* optAssertionProp_LclVar(ASSERT_VALARG_TP assertions, GenTreeLclVarCommon* tree, Statement* stmt);
GenTree* optAssertionProp_Asg(ASSERT_VALARG_TP assertions, GenTreeOp* asg, Statement* stmt);
GenTree* optAssertionProp_Return(ASSERT_VALARG_TP assertions, GenTreeUnOp* ret, Statement* stmt);
GenTree* optAssertionProp_Ind(ASSERT_VALARG_TP assertions, GenTree* tree, Statement* stmt);
GenTree* optAssertionProp_Cast(ASSERT_VALARG_TP assertions, GenTreeCast* cast, Statement* stmt);
GenTree* optAssertionProp_Call(ASSERT_VALARG_TP assertions, GenTreeCall* call, Statement* stmt);
......
......@@ -1481,6 +1481,8 @@ void GenTree::BashToConst(T value, var_types type /* = TYP_UNDEF */)
type = typeOfValue;
}
assert(type == genActualType(type));
genTreeOps oper = GT_NONE;
if (varTypeIsFloating(type))
{
......@@ -1492,7 +1494,6 @@ void GenTree::BashToConst(T value, var_types type /* = TYP_UNDEF */)
}
SetOperResetFlags(oper);
gtType = type;
switch (oper)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册