Skip to content

JIT: Introduce VNWalkPhis and use in VNNeverNegative as an example #105197

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
Jul 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
228 changes: 124 additions & 104 deletions src/coreclr/jit/valuenum.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1635,7 +1635,20 @@ bool ValueNumStore::IsKnownNonNull(ValueNum vn)
}

VNFuncApp funcAttr;
return GetVNFunc(vn, &funcAttr) && (s_vnfOpAttribs[funcAttr.m_func] & VNFOA_KnownNonNull) != 0;
if (!GetVNFunc(vn, &funcAttr))
{
return false;
}

if ((s_vnfOpAttribs[funcAttr.m_func] & VNFOA_KnownNonNull) != 0)
{
return true;
}

// TODO: we can recognize more non-null idioms here, e.g.
// ADD(IsKnownNonNull(op1), smallCns), etc.

return false;
}

bool ValueNumStore::IsSharedStatic(ValueNum vn)
Expand Down Expand Up @@ -3191,81 +3204,81 @@ ValueNum ValueNumStore::VNForMapPhysicalSelect(
return result;
}

typedef JitHashTable<ValueNum, JitSmallPrimitiveKeyFuncs<ValueNum>, bool> ValueNumSet;

class SmallValueNumSet
bool ValueNumStore::SmallValueNumSet::Lookup(ValueNum vn)
{
union
// O(N) lookup for inline elements
if (m_numElements <= ArrLen(m_inlineElements))
{
ValueNum m_inlineElements[4];
ValueNumSet* m_set;
};
unsigned m_numElements = 0;

public:
unsigned Count()
{
return m_numElements;
}

template <typename Func>
void ForEach(Func func)
{
if (m_numElements <= ArrLen(m_inlineElements))
for (unsigned i = 0; i < m_numElements; i++)
{
for (unsigned i = 0; i < m_numElements; i++)
if (m_inlineElements[i] == vn)
{
func(m_inlineElements[i]);
}
}
else
{
for (ValueNum vn : ValueNumSet::KeyIteration(m_set))
{
func(vn);
return true;
}
}

// Not found
return false;
}

void Add(Compiler* comp, ValueNum vn)
return m_set->Lookup(vn);
}

// Returns false if the value already exists
bool ValueNumStore::SmallValueNumSet::Add(Compiler* comp, ValueNum vn)
{
if (m_numElements <= ArrLen(m_inlineElements))
{
if (m_numElements <= ArrLen(m_inlineElements))
for (unsigned i = 0; i < m_numElements; i++)
{
for (unsigned i = 0; i < m_numElements; i++)
if (m_inlineElements[i] == vn)
{
if (m_inlineElements[i] == vn)
{
return;
}
// Already exists
return false;
}
}

if (m_numElements < ArrLen(m_inlineElements))
if (m_numElements < ArrLen(m_inlineElements))
{
m_inlineElements[m_numElements] = vn;
m_numElements++;
}
else
{
ValueNumSet* set = new (comp, CMK_ValueNumber) ValueNumSet(comp->getAllocator(CMK_ValueNumber));
for (ValueNum oldVn : m_inlineElements)
{
m_inlineElements[m_numElements] = vn;
m_numElements++;
set->Set(oldVn, true);
}
else
{
ValueNumSet* set = new (comp, CMK_ValueNumber) ValueNumSet(comp->getAllocator(CMK_ValueNumber));
for (ValueNum oldVn : m_inlineElements)
{
set->Set(oldVn, true);
}

set->Set(vn, true);
set->Set(vn, true);

m_set = set;
m_numElements++;
assert(m_numElements == set->GetCount());
}
}
else
{
m_set->Set(vn, true, ValueNumSet::SetKind::Overwrite);
m_numElements = m_set->GetCount();
m_set = set;
m_numElements++;
assert(m_numElements == set->GetCount());
}
return true;
}
};

bool exists = m_set->Set(vn, true, ValueNumSet::SetKind::Overwrite);
m_numElements = m_set->GetCount();
return !exists;
}

//------------------------------------------------------------------------------
// VNPhiDefToVN: Extracts the VN for a specific argument of a phi definition.
//
// Arguments:
// phiDef - The phi definition
// ssaArgNum - The argument number to extract
//
// Return Value:
// The VN for the specified argument of the phi definition.
//
ValueNum ValueNumStore::VNPhiDefToVN(const VNPhiDef& phiDef, unsigned ssaArgNum)
{
return m_pComp->lvaGetDesc(phiDef.LclNum)->GetPerSsaData(phiDef.SsaArgs[ssaArgNum])->m_vnPair.Get(VNK_Conservative);
}

//------------------------------------------------------------------------------
// VNForMapSelectInner: Select value from a map and record loop memory dependencies.
Expand Down Expand Up @@ -6513,68 +6526,75 @@ bool ValueNumStore::IsVNInt32Constant(ValueNum vn)

bool ValueNumStore::IsVNNeverNegative(ValueNum vn)
{
assert(varTypeIsIntegral(TypeOfVN(vn)));

if (IsVNConstant(vn))
{
var_types vnTy = TypeOfVN(vn);
if (vnTy == TYP_INT)
auto vnVisitor = [this](ValueNum vn) -> VNVisit {
if ((vn == NoVN) || !varTypeIsIntegral(TypeOfVN(vn)))
{
return GetConstantInt32(vn) >= 0;
return VNVisit::Abort;
}
else if (vnTy == TYP_LONG)

if (IsVNConstant(vn))
{
return GetConstantInt64(vn) >= 0;
var_types vnTy = TypeOfVN(vn);
if (vnTy == TYP_INT)
{
return GetConstantInt32(vn) >= 0 ? VNVisit::Continue : VNVisit::Abort;
}
if (vnTy == TYP_LONG)
{
return GetConstantInt64(vn) >= 0 ? VNVisit::Continue : VNVisit::Abort;
}
return VNVisit::Abort;
}

return false;
}
// Array length can never be negative.
if (IsVNArrLen(vn))
{
return VNVisit::Continue;
}

// Array length can never be negative.
if (IsVNArrLen(vn))
{
return true;
}
// TODO-VN: Recognize Span.Length
// Handle more intrinsics such as Math.Max(neverNegative1, neverNegative2)

VNFuncApp funcApp;
if (GetVNFunc(vn, &funcApp))
{
switch (funcApp.m_func)
VNFuncApp funcApp;
if (GetVNFunc(vn, &funcApp))
{
case VNF_GE_UN:
case VNF_GT_UN:
case VNF_LE_UN:
case VNF_LT_UN:
case VNF_COUNT:
case VNF_ADD_UN_OVF:
case VNF_SUB_UN_OVF:
case VNF_MUL_UN_OVF:
switch (funcApp.m_func)
{
case VNF_GE_UN:
case VNF_GT_UN:
case VNF_LE_UN:
case VNF_LT_UN:
case VNF_COUNT:
case VNF_ADD_UN_OVF:
case VNF_SUB_UN_OVF:
case VNF_MUL_UN_OVF:
#ifdef FEATURE_HW_INTRINSICS
#ifdef TARGET_XARCH
case VNF_HWI_POPCNT_PopCount:
case VNF_HWI_POPCNT_X64_PopCount:
case VNF_HWI_LZCNT_LeadingZeroCount:
case VNF_HWI_LZCNT_X64_LeadingZeroCount:
case VNF_HWI_BMI1_TrailingZeroCount:
case VNF_HWI_BMI1_X64_TrailingZeroCount:
return true;
case VNF_HWI_POPCNT_PopCount:
case VNF_HWI_POPCNT_X64_PopCount:
case VNF_HWI_LZCNT_LeadingZeroCount:
case VNF_HWI_LZCNT_X64_LeadingZeroCount:
case VNF_HWI_BMI1_TrailingZeroCount:
case VNF_HWI_BMI1_X64_TrailingZeroCount:
return VNVisit::Continue;
#elif defined(TARGET_ARM64)
case VNF_HWI_AdvSimd_PopCount:
case VNF_HWI_AdvSimd_LeadingZeroCount:
case VNF_HWI_AdvSimd_LeadingSignCount:
case VNF_HWI_ArmBase_LeadingZeroCount:
case VNF_HWI_ArmBase_Arm64_LeadingZeroCount:
case VNF_HWI_ArmBase_Arm64_LeadingSignCount:
return true;
case VNF_HWI_AdvSimd_PopCount:
case VNF_HWI_AdvSimd_LeadingZeroCount:
case VNF_HWI_AdvSimd_LeadingSignCount:
case VNF_HWI_ArmBase_LeadingZeroCount:
case VNF_HWI_ArmBase_Arm64_LeadingZeroCount:
case VNF_HWI_ArmBase_Arm64_LeadingSignCount:
return VNVisit::Continue;
#endif
#endif // FEATURE_HW_INTRINSICS

default:
break;
default:
break;
}
}
}

return false;
return VNVisit::Abort;
};
return VNVisitReachingVNs(vn, vnVisitor) == VNVisit::Continue;
}

GenTreeFlags ValueNumStore::GetHandleFlags(ValueNum vn)
Expand Down
101 changes: 101 additions & 0 deletions src/coreclr/jit/valuenum.h
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,107 @@ class ValueNumStore

void PeelOffsets(ValueNum* vn, target_ssize_t* offset);

typedef JitHashTable<ValueNum, JitSmallPrimitiveKeyFuncs<ValueNum>, bool> ValueNumSet;

class SmallValueNumSet
{
union
{
ValueNum m_inlineElements[4];
ValueNumSet* m_set;
};
unsigned m_numElements = 0;

public:
unsigned Count()
{
return m_numElements;
}

template <typename Func>
void ForEach(Func func)
{
if (m_numElements <= ArrLen(m_inlineElements))
{
for (unsigned i = 0; i < m_numElements; i++)
{
func(m_inlineElements[i]);
}
}
else
{
for (ValueNum vn : ValueNumSet::KeyIteration(m_set))
{
func(vn);
}
}
}

// Returns false if the value wasn't found
bool Lookup(ValueNum vn);

// Returns false if the value already exists
bool Add(Compiler* comp, ValueNum vn);
};

enum class VNVisit
{
Continue,
Abort,
};

ValueNum VNPhiDefToVN(const VNPhiDef& phiDef, unsigned ssaArgNum);

//--------------------------------------------------------------------------------
// VNVisitReachingVNs: given a VN, call the specified callback function on it and all the VNs that reach it
// via PHI definitions if any.
//
// Arguments:
// vn - The VN to visit all the reaching VNs for
// argVisitor - The callback function to call on the vn and its PHI arguments if any
//
// Return Value:
// VNVisit::Aborted - an argVisitor returned VNVisit::Abort, we stop the walk and return
// VNVisit::Continue - all argVisitor returned VNVisit::Continue
//
template <typename TArgVisitor>
VNVisit VNVisitReachingVNs(ValueNum vn, TArgVisitor argVisitor)
{
ArrayStack<ValueNum> toVisit(m_alloc);
toVisit.Push(vn);

SmallValueNumSet visited;
visited.Add(m_pComp, vn);
while (toVisit.Height() > 0)
{
ValueNum vnToVisit = toVisit.Pop();

// We need to handle nested (and, potentially, recursive) phi definitions.
// For now, we ignore memory phi definitions.
VNPhiDef phiDef;
if (GetPhiDef(vnToVisit, &phiDef))
{
for (unsigned ssaArgNum = 0; ssaArgNum < phiDef.NumArgs; ssaArgNum++)
{
ValueNum childVN = VNPhiDefToVN(phiDef, ssaArgNum);
if (visited.Add(m_pComp, childVN))
{
toVisit.Push(childVN);
}
}
}
else
{
if (argVisitor(vnToVisit) == VNVisit::Abort)
{
// The visitor wants to abort the walk.
return VNVisit::Abort;
}
}
}
return VNVisit::Continue;
}

// And the single constant for an object reference type.
static ValueNum VNForNull()
{
Expand Down
Loading