Skip to content

Commit

Permalink
Teach constant propagation to propagate GTF_ICON_INITCLASS
Browse files Browse the repository at this point in the history
This flag represents that dereferences off of the handle are dependent
on a cctor and cannot be hoisted out unless all cctors also are. It must
be propagated together with the constant for correctness.
  • Loading branch information
jakobbotsch committed Apr 26, 2023
1 parent ee80bdb commit c6a1fca
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 65 deletions.
67 changes: 32 additions & 35 deletions src/coreclr/jit/assertionprop.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -699,12 +699,12 @@ void Compiler::optPrintAssertion(AssertionDsc* curAssertion, AssertionIndex asse
if (curAssertion->op1.kind == O1K_EXACT_TYPE)
{
printf("Exact Type MT(%08X)", dspPtr(curAssertion->op2.u1.iconVal));
assert(curAssertion->op2.HasIconFlag());
assert(curAssertion->op2.IsHandle());
}
else if (curAssertion->op1.kind == O1K_SUBTYPE)
{
printf("MT(%08X)", dspPtr(curAssertion->op2.u1.iconVal));
assert(curAssertion->op2.HasIconFlag());
assert(curAssertion->op2.IsHandle());
}
else if ((curAssertion->op1.kind == O1K_BOUND_OPER_BND) ||
(curAssertion->op1.kind == O1K_BOUND_LOOP_BND) ||
Expand Down Expand Up @@ -741,7 +741,7 @@ void Compiler::optPrintAssertion(AssertionDsc* curAssertion, AssertionIndex asse
}
else
{
if (curAssertion->op2.HasIconFlag())
if (curAssertion->op2.IsHandle())
{
printf("[%08p]", dspPtr(curAssertion->op2.u1.iconVal));
}
Expand Down Expand Up @@ -1058,7 +1058,7 @@ AssertionIndex Compiler::optCreateAssertion(GenTree* op1,
assertion.op2.kind = O2K_CONST_INT;
assertion.op2.vn = ValueNumStore::VNForNull();
assertion.op2.u1.iconVal = 0;
assertion.op2.SetIconFlag(GTF_EMPTY);
assertion.op2.SetIconFlags(GTF_EMPTY);
}
//
// Are we making an assertion about a local variable?
Expand Down Expand Up @@ -1112,7 +1112,7 @@ AssertionIndex Compiler::optCreateAssertion(GenTree* op1,
assertion.op1.lcl.ssaNum = op1->AsLclVarCommon()->GetSsaNum();
assertion.op2.u1.iconVal = op2->AsIntCon()->gtIconVal;
assertion.op2.vn = optConservativeNormalVN(op2);
assertion.op2.SetIconFlag(op2->GetIconHandleFlag());
assertion.op2.SetIconFlags(op2->gtFlags & AssertionDsc::PropagatedIconHandleFlags);

//
// Ok everything has been set and the assertion looks good
Expand Down Expand Up @@ -1201,7 +1201,8 @@ AssertionIndex Compiler::optCreateAssertion(GenTree* op1,
#endif // TARGET_ARM

assertion.op2.u1.iconVal = iconVal;
assertion.op2.SetIconFlag(op2->GetIconHandleFlag(), op2->AsIntCon()->gtFieldSeq);
assertion.op2.SetIconFlags(op2->gtFlags & AssertionDsc::PropagatedIconHandleFlags,
op2->AsIntCon()->gtFieldSeq);
}
else if (op2->gtOper == GT_CNS_LNG)
{
Expand Down Expand Up @@ -1363,10 +1364,7 @@ AssertionIndex Compiler::optCreateAssertion(GenTree* op1,
assertion.op2.kind = O2K_IND_CNS_INT;
assertion.op2.u1.iconVal = cnsValue;
assertion.op2.vn = optConservativeNormalVN(op2->AsOp()->gtOp1);

/* iconFlags should only contain bits in GTF_ICON_HDL_MASK */
assert((iconFlags & ~GTF_ICON_HDL_MASK) == 0);
assertion.op2.SetIconFlag(iconFlags);
assertion.op2.SetIconFlags(iconFlags);
}
// JIT case
else if (optIsTreeKnownIntValue(!optLocalAssertionProp, op2, &cnsValue, &iconFlags))
Expand All @@ -1375,10 +1373,7 @@ AssertionIndex Compiler::optCreateAssertion(GenTree* op1,
assertion.op2.kind = O2K_CONST_INT;
assertion.op2.u1.iconVal = cnsValue;
assertion.op2.vn = optConservativeNormalVN(op2);

/* iconFlags should only contain bits in GTF_ICON_HDL_MASK */
assert((iconFlags & ~GTF_ICON_HDL_MASK) == 0);
assertion.op2.SetIconFlag(iconFlags);
assertion.op2.SetIconFlags(iconFlags);
}
else
{
Expand Down Expand Up @@ -1447,7 +1442,7 @@ bool Compiler::optIsTreeKnownIntValue(bool vnBased, GenTree* tree, ssize_t* pCon
if (tree->OperGet() == GT_CNS_INT)
{
*pConstant = tree->AsIntCon()->IconValue();
*pFlags = tree->GetIconHandleFlag();
*pFlags = tree->gtFlags & AssertionDsc::PropagatedIconHandleFlags;
return true;
}
#ifdef TARGET_64BIT
Expand All @@ -1456,7 +1451,7 @@ bool Compiler::optIsTreeKnownIntValue(bool vnBased, GenTree* tree, ssize_t* pCon
else if (tree->OperGet() == GT_CNS_LNG)
{
*pConstant = tree->AsLngCon()->gtLconVal;
*pFlags = tree->GetIconHandleFlag();
*pFlags = tree->gtFlags & AssertionDsc::PropagatedIconHandleFlags;
return true;
}
#endif
Expand Down Expand Up @@ -1676,12 +1671,11 @@ void Compiler::optDebugCheckAssertion(AssertionDsc* assertion)
case O2K_IND_CNS_INT:
case O2K_CONST_INT:
{
// The only flags that can be set are those in the GTF_ICON_HDL_MASK.
switch (assertion->op1.kind)
{
case O1K_EXACT_TYPE:
case O1K_SUBTYPE:
assert(assertion->op2.HasIconFlag());
assert(assertion->op2.IsHandle());
break;
case O1K_LCLVAR:
assert((lvaGetDesc(assertion->op1.lcl.lclNum)->lvType != TYP_REF) ||
Expand All @@ -1700,7 +1694,7 @@ void Compiler::optDebugCheckAssertion(AssertionDsc* assertion)
{
// All handles should be represented by O2K_CONST_INT,
// so no handle bits should be set here.
assert(!assertion->op2.HasIconFlag());
assert(!assertion->op2.IsHandle());
}
break;

Expand Down Expand Up @@ -1912,7 +1906,7 @@ AssertionInfo Compiler::optCreateJTrueBoundsAssertion(GenTree* tree)
dsc.op2.kind = O2K_CONST_INT;
dsc.op2.vn = vnStore->VNZeroForType(op2->TypeGet());
dsc.op2.u1.iconVal = 0;
dsc.op2.SetIconFlag(GTF_EMPTY);
dsc.op2.SetIconFlags(GTF_EMPTY);
AssertionIndex index = optAddAssertion(&dsc);
optCreateComplementaryAssertion(index, nullptr, nullptr);
return index;
Expand All @@ -1929,7 +1923,7 @@ AssertionInfo Compiler::optCreateJTrueBoundsAssertion(GenTree* tree)
dsc.op2.kind = O2K_CONST_INT;
dsc.op2.vn = vnStore->VNZeroForType(op2->TypeGet());
dsc.op2.u1.iconVal = 0;
dsc.op2.SetIconFlag(GTF_EMPTY);
dsc.op2.SetIconFlags(GTF_EMPTY);
AssertionIndex index = optAddAssertion(&dsc);
optCreateComplementaryAssertion(index, nullptr, nullptr);
return index;
Expand All @@ -1946,7 +1940,7 @@ AssertionInfo Compiler::optCreateJTrueBoundsAssertion(GenTree* tree)
dsc.op2.kind = O2K_CONST_INT;
dsc.op2.vn = vnStore->VNZeroForType(op2->TypeGet());
dsc.op2.u1.iconVal = 0;
dsc.op2.SetIconFlag(GTF_EMPTY);
dsc.op2.SetIconFlags(GTF_EMPTY);
AssertionIndex index = optAddAssertion(&dsc);
optCreateComplementaryAssertion(index, nullptr, nullptr);
return index;
Expand All @@ -1963,7 +1957,7 @@ AssertionInfo Compiler::optCreateJTrueBoundsAssertion(GenTree* tree)
dsc.op2.kind = O2K_CONST_INT;
dsc.op2.vn = vnStore->VNZeroForType(TYP_INT);
dsc.op2.u1.iconVal = 0;
dsc.op2.SetIconFlag(GTF_EMPTY);
dsc.op2.SetIconFlags(GTF_EMPTY);
AssertionIndex index = optAddAssertion(&dsc);
optCreateComplementaryAssertion(index, nullptr, nullptr);
return index;
Expand Down Expand Up @@ -2006,7 +2000,7 @@ AssertionInfo Compiler::optCreateJTrueBoundsAssertion(GenTree* tree)
dsc.op2.kind = O2K_CONST_INT;
dsc.op2.vn = vnStore->VNZeroForType(op2->TypeGet());
dsc.op2.u1.iconVal = 0;
dsc.op2.SetIconFlag(GTF_EMPTY);
dsc.op2.SetIconFlags(GTF_EMPTY);
AssertionIndex index = optAddAssertion(&dsc);
optCreateComplementaryAssertion(index, nullptr, nullptr);
return index;
Expand All @@ -2023,7 +2017,7 @@ AssertionInfo Compiler::optCreateJTrueBoundsAssertion(GenTree* tree)
dsc.op2.kind = O2K_CONST_INT;
dsc.op2.vn = vnStore->VNZeroForType(TYP_INT);
dsc.op2.u1.iconVal = 0;
dsc.op2.SetIconFlag(GTF_EMPTY);
dsc.op2.SetIconFlags(GTF_EMPTY);
AssertionIndex index = optAddAssertion(&dsc);
optCreateComplementaryAssertion(index, nullptr, nullptr);
return index;
Expand All @@ -2037,7 +2031,7 @@ AssertionInfo Compiler::optCreateJTrueBoundsAssertion(GenTree* tree)
dsc.op2.kind = O2K_CONST_INT;
dsc.op2.vn = vnStore->VNZeroForType(TYP_INT);
dsc.op2.u1.iconVal = 0;
dsc.op2.SetIconFlag(GTF_EMPTY);
dsc.op2.SetIconFlags(GTF_EMPTY);
AssertionIndex index = optAddAssertion(&dsc);
optCreateComplementaryAssertion(index, nullptr, nullptr);
return index;
Expand Down Expand Up @@ -2133,7 +2127,7 @@ AssertionInfo Compiler::optAssertionGenJtrue(GenTree* tree)
dsc.op2.vn = vnStore->VNConservativeNormalValue(op2->gtVNPair);
dsc.op2.kind = O2K_CONST_INT;
dsc.op2.u1.iconVal = 0;
dsc.op2.SetIconFlag(GTF_EMPTY);
dsc.op2.SetIconFlags(GTF_EMPTY);

// when con is not zero, create an assertion on the arr.Length == con edge
// when con is zero, create an assertion on the arr.Length != 0 edge
Expand Down Expand Up @@ -2952,11 +2946,13 @@ GenTree* Compiler::optConstantAssertionProp(AssertionDsc* curAssertion,
break;

case O2K_CONST_INT:

{
GenTreeFlags iconFlags = curAssertion->op2.GetIconFlags();
GenTreeFlags iconHandle = iconFlags & GTF_ICON_HDL_MASK;
// Don't propagate handles if we need to report relocs.
if (opts.compReloc && curAssertion->op2.HasIconFlag() && curAssertion->op2.u1.iconVal != 0)
if (opts.compReloc && (iconHandle != 0) && curAssertion->op2.u1.iconVal != 0)
{
if (curAssertion->op2.GetIconFlag() == GTF_ICON_STATIC_HDL)
if (iconHandle == GTF_ICON_STATIC_HDL)
{
propagateType = true;
}
Expand All @@ -2976,11 +2972,10 @@ GenTree* Compiler::optConstantAssertionProp(AssertionDsc* curAssertion,
assert(!varTypeIsSmall(tree) || (curAssertion->op2.u1.iconVal ==
optCastConstantSmall(curAssertion->op2.u1.iconVal, tree->TypeGet())));

if (curAssertion->op2.HasIconFlag())
if (iconHandle != 0)
{
// Here we have to allocate a new 'large' node to replace the old one
newTree = gtNewIconHandleNode(curAssertion->op2.u1.iconVal, curAssertion->op2.GetIconFlag(),
curAssertion->op2.u1.fieldSeq);
newTree = gtNewIconHandleNode(curAssertion->op2.u1.iconVal, iconFlags, curAssertion->op2.u1.fieldSeq);

// Make sure we don't retype const gc handles to TYP_I_IMPL
// Although, it's possible for e.g. GTF_ICON_STATIC_HDL
Expand All @@ -3003,8 +2998,10 @@ GenTree* Compiler::optConstantAssertionProp(AssertionDsc* curAssertion,
{
assert(varTypeIsIntegralOrI(tree));
newTree->BashToConst(curAssertion->op2.u1.iconVal, genActualType(tree));
newTree->gtFlags |= iconFlags;
}
break;
}

default:
return nullptr;
Expand Down Expand Up @@ -3884,7 +3881,7 @@ GenTree* Compiler::optAssertionPropGlobal_RelOp(ASSERT_VALARG_TP assertions, Gen

if (vnStore->IsVNHandle(vnCns))
{
op1->gtFlags |= (vnStore->GetHandleFlags(vnCns) & GTF_ICON_HDL_MASK);
op1->gtFlags |= (vnStore->GetHandleFlags(vnCns) & AssertionDsc::PropagatedIconHandleFlags);
}
}
else if (op1->TypeGet() == TYP_LONG)
Expand All @@ -3893,7 +3890,7 @@ GenTree* Compiler::optAssertionPropGlobal_RelOp(ASSERT_VALARG_TP assertions, Gen

if (vnStore->IsVNHandle(vnCns))
{
op1->gtFlags |= (vnStore->GetHandleFlags(vnCns) & GTF_ICON_HDL_MASK);
op1->gtFlags |= (vnStore->GetHandleFlags(vnCns) & AssertionDsc::PropagatedIconHandleFlags);
}
}
else if (op1->TypeGet() == TYP_DOUBLE)
Expand Down
36 changes: 24 additions & 12 deletions src/coreclr/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -7318,30 +7318,41 @@ class Compiler
IntegralRange u2;
};

bool HasIconFlag()
{
assert(m_encodedIconFlags <= 0xFF);
return m_encodedIconFlags != 0;
}
GenTreeFlags GetIconFlag()
GenTreeFlags GetIconFlags()
{
// number of trailing zeros in GTF_ICON_HDL_MASK
const uint16_t iconMaskTzc = 24;
static_assert_no_msg((0xFF000000 == GTF_ICON_HDL_MASK) && (GTF_ICON_HDL_MASK >> iconMaskTzc) == 0xFF);

GenTreeFlags flags = (GenTreeFlags)(m_encodedIconFlags << iconMaskTzc);
assert((flags & ~GTF_ICON_HDL_MASK) == 0);
GenTreeFlags flags = (GenTreeFlags)((m_encodedIconFlags & 0xFF) << iconMaskTzc);
if ((m_encodedIconFlags & 0x100) != 0)
{
flags |= GTF_ICON_INITCLASS;
}

return flags;
}
void SetIconFlag(GenTreeFlags flags, FieldSeq* fieldSeq = nullptr)
void SetIconFlags(GenTreeFlags flags, FieldSeq* fieldSeq = nullptr)
{
const uint16_t iconMaskTzc = 24;
assert((flags & ~GTF_ICON_HDL_MASK) == 0);
assert((flags & ~PropagatedIconHandleFlags) == 0);
m_encodedIconFlags = flags >> iconMaskTzc;
u1.fieldSeq = fieldSeq;
if ((flags & GTF_ICON_INITCLASS) != 0)
{
m_encodedIconFlags |= 0x100;
}

u1.fieldSeq = fieldSeq;
}

bool IsHandle()
{
return (GetIconFlags() & GTF_ICON_HDL_MASK) != 0;
}
} op2;

static const GenTreeFlags PropagatedIconHandleFlags = GTF_ICON_HDL_MASK | GTF_ICON_INITCLASS;

bool IsCheckedBoundArithBound()
{
return ((assertionKind == OAK_EQUAL || assertionKind == OAK_NOT_EQUAL) && op1.kind == O1K_BOUND_OPER_BND);
Expand Down Expand Up @@ -7448,7 +7459,8 @@ class Compiler
{
case O2K_IND_CNS_INT:
case O2K_CONST_INT:
return ((op2.u1.iconVal == that->op2.u1.iconVal) && (op2.GetIconFlag() == that->op2.GetIconFlag()));
return ((op2.u1.iconVal == that->op2.u1.iconVal) &&
(op2.GetIconFlags() == that->op2.GetIconFlags()));

case O2K_CONST_LONG:
return (op2.lconVal == that->op2.lconVal);
Expand Down
Loading

0 comments on commit c6a1fca

Please sign in to comment.