From c6a1fcafc4aa1c47e45639808716a92e93843872 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Wed, 26 Apr 2023 14:43:09 +0200 Subject: [PATCH] Teach constant propagation to propagate GTF_ICON_INITCLASS 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. --- src/coreclr/jit/assertionprop.cpp | 67 +++++++++++++++---------------- src/coreclr/jit/compiler.h | 36 +++++++++++------ src/coreclr/jit/valuenum.cpp | 39 ++++++++++-------- src/coreclr/jit/valuenum.h | 2 +- 4 files changed, 79 insertions(+), 65 deletions(-) diff --git a/src/coreclr/jit/assertionprop.cpp b/src/coreclr/jit/assertionprop.cpp index d7369b3adcce2..1d089b1cf4b5e 100644 --- a/src/coreclr/jit/assertionprop.cpp +++ b/src/coreclr/jit/assertionprop.cpp @@ -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) || @@ -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)); } @@ -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? @@ -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 @@ -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) { @@ -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)) @@ -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 { @@ -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 @@ -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 @@ -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) || @@ -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; @@ -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; @@ -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; @@ -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; @@ -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; @@ -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; @@ -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; @@ -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; @@ -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 @@ -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; } @@ -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 @@ -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; @@ -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) @@ -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) diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index d05f9d1ef246f..01f2114e943a9 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -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); @@ -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); diff --git a/src/coreclr/jit/valuenum.cpp b/src/coreclr/jit/valuenum.cpp index 3d258bdb8263e..902f4bb138121 100644 --- a/src/coreclr/jit/valuenum.cpp +++ b/src/coreclr/jit/valuenum.cpp @@ -2016,7 +2016,7 @@ void ValueNumStore::GetCastOperFromVN(ValueNum vn, var_types* pCastToType, bool* ValueNum ValueNumStore::VNForHandle(ssize_t cnsVal, GenTreeFlags handleFlags) { - assert((handleFlags & ~GTF_ICON_HDL_MASK) == 0); + assert((handleFlags & ~(GTF_ICON_HDL_MASK | GTF_ICON_INITCLASS)) == 0); ValueNum res; VNHandle handle; @@ -2406,7 +2406,7 @@ ValueNum ValueNumStore::VNForFunc(var_types typ, VNFunc func, ValueNum arg0VN) if ((resultVN == NoVN) && GetVNFunc(addressVN, &funcApp) && (funcApp.m_func == VNF_InvariantNonNullLoad)) { ValueNum fieldSeqVN = VNNormalValue(funcApp.m_args[0]); - if (IsVNHandle(fieldSeqVN) && (GetHandleFlags(fieldSeqVN) == GTF_ICON_FIELD_SEQ)) + if (IsVNHandle(fieldSeqVN) && ((GetHandleFlags(fieldSeqVN) & GTF_ICON_HDL_MASK) == GTF_ICON_FIELD_SEQ)) { FieldSeq* fieldSeq = FieldSeqVNToFieldSeq(fieldSeqVN); if (fieldSeq != nullptr) @@ -4151,8 +4151,8 @@ ValueNum ValueNumStore::VNEvalFoldTypeCompare(var_types type, VNFunc func, Value return NoVN; } - assert(GetHandleFlags(handle0) == GTF_ICON_CLASS_HDL); - assert(GetHandleFlags(handle1) == GTF_ICON_CLASS_HDL); + assert((GetHandleFlags(handle0) & GTF_ICON_HDL_MASK) == GTF_ICON_CLASS_HDL); + assert((GetHandleFlags(handle1) & GTF_ICON_HDL_MASK) == GTF_ICON_CLASS_HDL); const ssize_t handleVal0 = ConstantValue(handle0); const ssize_t handleVal1 = ConstantValue(handle1); @@ -5296,7 +5296,7 @@ ValueNum ValueNumStore::VNForFieldSeq(FieldSeq* fieldSeq) // FieldSeq* ValueNumStore::FieldSeqVNToFieldSeq(ValueNum vn) { - assert(IsVNHandle(vn) && (GetHandleFlags(vn) == GTF_ICON_FIELD_SEQ)); + assert(IsVNHandle(vn) && ((GetHandleFlags(vn) & GTF_ICON_HDL_MASK) == GTF_ICON_FIELD_SEQ)); return reinterpret_cast(ConstantValue(vn)); } @@ -5875,10 +5875,10 @@ GenTreeFlags ValueNumStore::GetHandleFlags(ValueNum vn) GenTreeFlags ValueNumStore::GetFoldedArithOpResultHandleFlags(ValueNum vn) { - GenTreeFlags flags = GetHandleFlags(vn); - assert((flags & GTF_ICON_HDL_MASK) == flags); + GenTreeFlags flags = GetHandleFlags(vn); + GenTreeFlags resultFlags = flags & ~GTF_ICON_HDL_MASK; - switch (flags) + switch (flags & GTF_ICON_HDL_MASK) { case GTF_ICON_SCOPE_HDL: case GTF_ICON_CLASS_HDL: @@ -5895,15 +5895,19 @@ GenTreeFlags ValueNumStore::GetFoldedArithOpResultHandleFlags(ValueNum vn) case GTF_ICON_TLS_HDL: case GTF_ICON_STATIC_BOX_PTR: case GTF_ICON_STATIC_ADDR_PTR: - return GTF_ICON_CONST_PTR; + resultFlags |= GTF_ICON_CONST_PTR; + break; case GTF_ICON_STATIC_HDL: case GTF_ICON_GLOBAL_PTR: case GTF_ICON_BBC_PTR: - return GTF_ICON_GLOBAL_PTR; + resultFlags |= GTF_ICON_GLOBAL_PTR; + break; default: assert(!"Unexpected handle type"); return flags; } + + return resultFlags; } bool ValueNumStore::IsVNHandle(ValueNum vn) @@ -5919,7 +5923,7 @@ bool ValueNumStore::IsVNHandle(ValueNum vn) bool ValueNumStore::IsVNObjHandle(ValueNum vn) { - return IsVNHandle(vn) && GetHandleFlags(vn) == GTF_ICON_OBJ_HDL; + return IsVNHandle(vn) && ((GetHandleFlags(vn) & GTF_ICON_HDL_MASK) == GTF_ICON_OBJ_HDL); } //------------------------------------------------------------------------ @@ -8379,7 +8383,7 @@ void ValueNumStore::vnDump(Compiler* comp, ValueNum vn, bool isPtr) { printf("NoVN"); } - else if (IsVNHandle(vn) && (GetHandleFlags(vn) == GTF_ICON_FIELD_SEQ)) + else if (IsVNHandle(vn) && ((GetHandleFlags(vn) & GTF_ICON_HDL_MASK) == GTF_ICON_FIELD_SEQ)) { comp->gtDispFieldSeq(FieldSeqVNToFieldSeq(vn), 0); printf(" "); @@ -9940,9 +9944,9 @@ void Compiler::fgValueNumberTreeConst(GenTree* tree) if (tree->IsIconHandle()) { const GenTreeIntCon* cns = tree->AsIntCon(); - const GenTreeFlags handleFlags = tree->GetIconHandleFlag(); + const GenTreeFlags handleFlags = tree->gtFlags & AssertionDsc::PropagatedIconHandleFlags; tree->gtVNPair.SetBoth(vnStore->VNForHandle(cns->IconValue(), handleFlags)); - if (handleFlags == GTF_ICON_CLASS_HDL) + if ((handleFlags & GTF_ICON_HDL_MASK) == GTF_ICON_CLASS_HDL) { vnStore->AddToEmbeddedHandleMap(cns->IconValue(), cns->gtCompileTimeHandle); } @@ -10034,8 +10038,8 @@ void Compiler::fgValueNumberTreeConst(GenTree* tree) else { assert(doesMethodHaveFrozenObjects()); - tree->gtVNPair.SetBoth( - vnStore->VNForHandle(ssize_t(tree->AsIntConCommon()->IconValue()), tree->GetIconHandleFlag())); + tree->gtVNPair.SetBoth(vnStore->VNForHandle(ssize_t(tree->AsIntConCommon()->IconValue()), + tree->gtFlags & AssertionDsc::PropagatedIconHandleFlags)); } break; @@ -10051,7 +10055,8 @@ void Compiler::fgValueNumberTreeConst(GenTree* tree) if (tree->IsIconHandle()) { tree->gtVNPair.SetBoth( - vnStore->VNForHandle(ssize_t(tree->AsIntConCommon()->IconValue()), tree->GetIconHandleFlag())); + vnStore->VNForHandle(ssize_t(tree->AsIntConCommon()->IconValue()), + tree->gtFlags & AssertionDsc::PropagatedIconHandleFlags)); } else { diff --git a/src/coreclr/jit/valuenum.h b/src/coreclr/jit/valuenum.h index c4f9a2918c0a3..65dacbddd0ee3 100644 --- a/src/coreclr/jit/valuenum.h +++ b/src/coreclr/jit/valuenum.h @@ -985,7 +985,7 @@ class ValueNumStore // If "vn" is checked bound arith, then populate the "info" fields for cmpOp, cmpOper. void GetCompareCheckedBoundArithInfo(ValueNum vn, CompareCheckedBoundArithInfo* info); - // Returns the flags on the current handle. GTF_ICON_SCOPE_HDL for example. + // Returns the flags on the current handle. GTF_ICON_SCOPE_HDL for example. Can also have GTF_ICON_INITCLASS. GenTreeFlags GetHandleFlags(ValueNum vn); // Returns true iff the VN represents a handle constant.