1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
5 /*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
6 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
10 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
11 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
20 #include "ssaconfig.h"
22 // Windows x86 and Windows ARM/ARM64 may not define _isnanf() but they do define _isnan().
23 // We will redirect the macros to these other functions if the macro is not defined for the
24 // platform. This has the side effect of a possible implicit upcasting for arguments passed.
25 #if (defined(_TARGET_X86_) || defined(_TARGET_ARM_) || defined(_TARGET_ARM64_)) && !defined(FEATURE_PAL)
28 #define _isnanf _isnan
31 #endif // (defined(_TARGET_X86_) || defined(_TARGET_ARM_) || defined(_TARGET_ARM64_)) && !defined(FEATURE_PAL)
33 // We need to use target-specific NaN values when statically compute expressions.
34 // Otherwise, cross crossgen (e.g. x86_arm) would have different binary outputs
35 // from native crossgen (i.e. arm_arm) when the NaN got "embedded" into code.
37 // For example, when placing NaN value in r3 register
38 // x86_arm crossgen would emit
41 // while arm_arm crossgen (and JIT) output is
47 //------------------------------------------------------------------------
48 // NaN: Return target-specific float NaN value
51 // "Default" NaN value returned by expression 0.0f / 0.0f on x86/x64 has
52 // different binary representation (0xffc00000) than NaN on
53 // ARM32/ARM64 (0x7fc00000).
57 #if defined(_TARGET_XARCH_)
58 unsigned bits = 0xFFC00000u;
59 #elif defined(_TARGET_ARMARCH_)
60 unsigned bits = 0x7FC00000u;
62 #error Unsupported or unset target architecture
65 static_assert(sizeof(bits) == sizeof(result), "sizeof(unsigned) must equal sizeof(float)");
66 memcpy(&result, &bits, sizeof(result));
73 //------------------------------------------------------------------------
74 // NaN: Return target-specific double NaN value
77 // "Default" NaN value returned by expression 0.0 / 0.0 on x86/x64 has
78 // different binary representation (0xfff8000000000000) than NaN on
79 // ARM32/ARM64 (0x7ff8000000000000).
83 #if defined(_TARGET_XARCH_)
84 unsigned long long bits = 0xFFF8000000000000ull;
85 #elif defined(_TARGET_ARMARCH_)
86 unsigned long long bits = 0x7FF8000000000000ull;
88 #error Unsupported or unset target architecture
91 static_assert(sizeof(bits) == sizeof(result), "sizeof(unsigned long long) must equal sizeof(double)");
92 memcpy(&result, &bits, sizeof(result));
97 //------------------------------------------------------------------------
98 // FpAdd: Computes value1 + value2
101 // TFpTraits::NaN() - If target ARM32/ARM64 and result value is NaN
102 // value1 + value2 - Otherwise
105 // See FloatTraits::NaN() and DoubleTraits::NaN() notes.
107 template <typename TFp, typename TFpTraits>
108 TFp FpAdd(TFp value1, TFp value2)
110 #ifdef _TARGET_ARMARCH_
111 // If [value1] is negative infinity and [value2] is positive infinity
112 // the result is NaN.
113 // If [value1] is positive infinity and [value2] is negative infinity
114 // the result is NaN.
116 if (!_finite(value1) && !_finite(value2))
118 if (value1 < 0 && value2 > 0)
120 return TFpTraits::NaN();
123 if (value1 > 0 && value2 < 0)
125 return TFpTraits::NaN();
128 #endif // _TARGET_ARMARCH_
130 return value1 + value2;
133 //------------------------------------------------------------------------
134 // FpSub: Computes value1 - value2
137 // TFpTraits::NaN() - If target ARM32/ARM64 and result value is NaN
138 // value1 - value2 - Otherwise
141 // See FloatTraits::NaN() and DoubleTraits::NaN() notes.
143 template <typename TFp, typename TFpTraits>
144 TFp FpSub(TFp value1, TFp value2)
146 #ifdef _TARGET_ARMARCH_
147 // If [value1] is positive infinity and [value2] is positive infinity
148 // the result is NaN.
149 // If [value1] is negative infinity and [value2] is negative infinity
150 // the result is NaN.
152 if (!_finite(value1) && !_finite(value2))
154 if (value1 > 0 && value2 > 0)
156 return TFpTraits::NaN();
159 if (value1 < 0 && value2 < 0)
161 return TFpTraits::NaN();
164 #endif // _TARGET_ARMARCH_
166 return value1 - value2;
169 //------------------------------------------------------------------------
170 // FpMul: Computes value1 * value2
173 // TFpTraits::NaN() - If target ARM32/ARM64 and result value is NaN
174 // value1 * value2 - Otherwise
177 // See FloatTraits::NaN() and DoubleTraits::NaN() notes.
179 template <typename TFp, typename TFpTraits>
180 TFp FpMul(TFp value1, TFp value2)
182 #ifdef _TARGET_ARMARCH_
183 // From the ECMA standard:
185 // If [value1] is zero and [value2] is infinity
186 // the result is NaN.
187 // If [value1] is infinity and [value2] is zero
188 // the result is NaN.
190 if (value1 == 0 && !_finite(value2) && !_isnan(value2))
192 return TFpTraits::NaN();
194 if (!_finite(value1) && !_isnan(value1) && value2 == 0)
196 return TFpTraits::NaN();
198 #endif // _TARGET_ARMARCH_
200 return value1 * value2;
203 //------------------------------------------------------------------------
204 // FpDiv: Computes value1 / value2
207 // TFpTraits::NaN() - If target ARM32/ARM64 and result value is NaN
208 // value1 / value2 - Otherwise
211 // See FloatTraits::NaN() and DoubleTraits::NaN() notes.
213 template <typename TFp, typename TFpTraits>
214 TFp FpDiv(TFp dividend, TFp divisor)
216 #ifdef _TARGET_ARMARCH_
217 // From the ECMA standard:
219 // If [dividend] is zero and [divisor] is zero
220 // the result is NaN.
221 // If [dividend] is infinity and [divisor] is infinity
222 // the result is NaN.
224 if (dividend == 0 && divisor == 0)
226 return TFpTraits::NaN();
228 else if (!_finite(dividend) && !_isnan(dividend) && !_finite(divisor) && !_isnan(divisor))
230 return TFpTraits::NaN();
232 #endif // _TARGET_ARMARCH_
234 return dividend / divisor;
237 template <typename TFp, typename TFpTraits>
238 TFp FpRem(TFp dividend, TFp divisor)
240 // From the ECMA standard:
242 // If [divisor] is zero or [dividend] is infinity
243 // the result is NaN.
244 // If [divisor] is infinity,
245 // the result is [dividend]
247 if (divisor == 0 || !_finite(dividend))
249 return TFpTraits::NaN();
251 else if (!_finite(divisor) && !_isnan(divisor))
256 return (TFp)fmod((double)dividend, (double)divisor);
259 //--------------------------------------------------------------------------------
260 // VNGetOperKind: - Given two bools: isUnsigned and overFlowCheck
261 // return the correct VNOperKind for them.
264 // isUnsigned - The operKind returned should have the unsigned property
265 // overflowCheck - The operKind returned should have the overflow check property
268 // - The VNOperKind to use for this pair of (isUnsigned, overflowCheck)
270 VNOperKind VNGetOperKind(bool isUnsigned, bool overflowCheck)
280 return VOK_OverflowCheck;
291 return VOK_Unsigned_OverflowCheck;
296 //--------------------------------------------------------------------------------
297 // GetVNFuncForOper: - Given a genTreeOper this function Returns the correct
298 // VNFunc to use for ValueNumbering
301 // oper - The gtOper value from the GenTree node
302 // operKind - An enum that supports Normal, Unsigned, OverflowCheck,
303 // and Unsigned_OverflowCheck,
306 // - The VNFunc to use for this pair of (oper, operKind)
308 // Notes: - An assert will fire when the oper does not support
309 // the operKInd that is supplied.
311 VNFunc GetVNFuncForOper(genTreeOps oper, VNOperKind operKind)
313 VNFunc result = VNF_COUNT; // An illegal value
314 bool invalid = false;
316 // For most genTreeOpers we just use the VNFunc with the same enum value as the oper
318 if (operKind == VOK_Default)
320 // We can directly use the enum value of oper
321 result = VNFunc(oper);
323 else if ((oper == GT_EQ) || (oper == GT_NE))
325 if (operKind == VOK_Unsigned)
327 // We will permit unsignedOper to be used with GT_EQ and GT_NE (as it is a no-op)
329 // Again we directly use the enum value of oper
330 result = VNFunc(oper);
337 else // We will need to use a VNF_ function
342 if (operKind == VOK_Unsigned)
353 if (operKind == VOK_Unsigned)
364 if (operKind == VOK_Unsigned)
375 if (operKind == VOK_Unsigned)
386 if (operKind == VOK_OverflowCheck)
388 result = VNF_ADD_OVF;
390 else if (operKind == VOK_Unsigned_OverflowCheck)
392 result = VNF_ADD_UN_OVF;
401 if (operKind == VOK_OverflowCheck)
403 result = VNF_SUB_OVF;
405 else if (operKind == VOK_Unsigned_OverflowCheck)
407 result = VNF_SUB_UN_OVF;
416 if (operKind == VOK_OverflowCheck)
418 result = VNF_MUL_OVF;
420 else if (operKind == VOK_Unsigned_OverflowCheck)
422 result = VNF_MUL_UN_OVF;
424 #ifndef _TARGET_64BIT_
425 else if (operKind == VOK_Unsigned)
427 // This is the special 64-bit unsigned multiply used on 32-bit targets
428 result = VNF_MUL64_UN;
438 // Will trigger the noway_assert below.
442 noway_assert(!invalid && (result != VNF_COUNT));
447 //--------------------------------------------------------------------------------
448 // GetVNFuncForNode: - Given a GenTree node, this returns the proper
449 // VNFunc to use for ValueNumbering
452 // node - The GenTree node that we need the VNFunc for.
455 // - The VNFunc to use for this GenTree node
457 // Notes: - The gtFlags from the node are used to set operKind
458 // to one of Normal, Unsigned, OverflowCheck,
459 // or Unsigned_OverflowCheck. Also see GetVNFuncForOper()
461 VNFunc GetVNFuncForNode(GenTree* node)
463 bool isUnsignedOper = ((node->gtFlags & GTF_UNSIGNED) != 0);
464 bool hasOverflowCheck = node->gtOverflowEx();
465 VNOperKind operKind = VNGetOperKind(isUnsignedOper, hasOverflowCheck);
466 VNFunc result = GetVNFuncForOper(node->gtOper, operKind);
471 unsigned ValueNumStore::VNFuncArity(VNFunc vnf)
473 // Read the bit field out of the table...
474 return (s_vnfOpAttribs[vnf] & VNFOA_ArityMask) >> VNFOA_ArityShift;
478 bool ValueNumStore::IsOverflowIntDiv(int v0, int v1)
480 return (v1 == -1) && (v0 == INT32_MIN);
484 bool ValueNumStore::IsOverflowIntDiv(INT64 v0, INT64 v1)
486 return (v1 == -1) && (v0 == INT64_MIN);
489 template <typename T>
490 bool ValueNumStore::IsOverflowIntDiv(T v0, T v1)
496 bool ValueNumStore::IsIntZero(int v)
501 bool ValueNumStore::IsIntZero(unsigned v)
506 bool ValueNumStore::IsIntZero(INT64 v)
511 bool ValueNumStore::IsIntZero(UINT64 v)
515 template <typename T>
516 bool ValueNumStore::IsIntZero(T v)
521 ValueNumStore::ValueNumStore(Compiler* comp, CompAllocator alloc)
525 , m_fixedPointMapSels(alloc, 8)
526 , m_checkedBoundVNs(alloc)
528 , m_intCnsMap(nullptr)
529 , m_longCnsMap(nullptr)
530 , m_handleMap(nullptr)
531 , m_floatCnsMap(nullptr)
532 , m_doubleCnsMap(nullptr)
533 , m_byrefCnsMap(nullptr)
534 , m_VNFunc0Map(nullptr)
535 , m_VNFunc1Map(nullptr)
536 , m_VNFunc2Map(nullptr)
537 , m_VNFunc3Map(nullptr)
538 , m_VNFunc4Map(nullptr)
543 // We have no current allocation chunks.
544 for (unsigned i = 0; i < TYP_COUNT; i++)
546 for (unsigned j = CEA_None; j <= CEA_Count + MAX_LOOP_NUM; j++)
548 m_curAllocChunk[i][j] = NoChunk;
552 for (unsigned i = 0; i < SmallIntConstNum; i++)
554 m_VNsForSmallIntConsts[i] = NoVN;
556 // We will reserve chunk 0 to hold some special constants, like the constant NULL, the "exception" value, and the
558 Chunk* specialConstChunk = new (m_alloc) Chunk(m_alloc, &m_nextChunkBase, TYP_REF, CEA_Const, MAX_LOOP_NUM);
559 specialConstChunk->m_numUsed +=
560 SRC_NumSpecialRefConsts; // Implicitly allocate 0 ==> NULL, and 1 ==> Exception, 2 ==> ZeroMap.
561 ChunkNum cn = m_chunks.Push(specialConstChunk);
564 m_mapSelectBudget = (int)JitConfig.JitVNMapSelBudget(); // We cast the unsigned DWORD to a signed int.
566 // This value must be non-negative and non-zero, reset the value to DEFAULT_MAP_SELECT_BUDGET if it isn't.
567 if (m_mapSelectBudget <= 0)
569 m_mapSelectBudget = DEFAULT_MAP_SELECT_BUDGET;
577 template <typename T>
578 T ValueNumStore::EvalOp(VNFunc vnf, T v0)
580 genTreeOps oper = genTreeOps(vnf);
582 // Here we handle unary ops that are the same for all types.
586 // Note that GT_NEG is the only valid unary floating point operation
593 // Otherwise must be handled by the type specific method
594 return EvalOpSpecialized(vnf, v0);
598 double ValueNumStore::EvalOpSpecialized<double>(VNFunc vnf, double v0)
600 // Here we handle specialized double unary ops.
601 noway_assert(!"EvalOpSpecialized<double> - unary");
606 float ValueNumStore::EvalOpSpecialized<float>(VNFunc vnf, float v0)
608 // Here we handle specialized float unary ops.
609 noway_assert(!"EvalOpSpecialized<float> - unary");
613 template <typename T>
614 T ValueNumStore::EvalOpSpecialized(VNFunc vnf, T v0)
616 if (vnf < VNF_Boundary)
618 genTreeOps oper = genTreeOps(vnf);
630 UINT16 v0_unsigned = UINT16(v0);
632 v0_unsigned = ((v0_unsigned >> 8) & 0xFF) | ((v0_unsigned << 8) & 0xFF00);
633 return T(v0_unsigned);
639 UINT32 v0_unsigned = UINT32(v0);
641 v0_unsigned = ((v0_unsigned >> 24) & 0xFF) | ((v0_unsigned >> 8) & 0xFF00) |
642 ((v0_unsigned << 8) & 0xFF0000) | ((v0_unsigned << 24) & 0xFF000000);
643 return T(v0_unsigned);
645 else if (sizeof(T) == 8)
647 UINT64 v0_unsigned = UINT64(v0);
649 v0_unsigned = ((v0_unsigned >> 56) & 0xFF) | ((v0_unsigned >> 40) & 0xFF00) |
650 ((v0_unsigned >> 24) & 0xFF0000) | ((v0_unsigned >> 8) & 0xFF000000) |
651 ((v0_unsigned << 8) & 0xFF00000000) | ((v0_unsigned << 24) & 0xFF0000000000) |
652 ((v0_unsigned << 40) & 0xFF000000000000) | ((v0_unsigned << 56) & 0xFF00000000000000);
653 return T(v0_unsigned);
657 break; // unknown primitive
665 noway_assert(!"Unhandled operation in EvalOpSpecialized<T> - unary");
673 template <typename T>
674 T ValueNumStore::EvalOp(VNFunc vnf, T v0, T v1)
676 // Here we handle the binary ops that are the same for all types.
678 // Currently there are none (due to floating point NaN representations)
680 // Otherwise must be handled by the type specific method
681 return EvalOpSpecialized(vnf, v0, v1);
685 double ValueNumStore::EvalOpSpecialized<double>(VNFunc vnf, double v0, double v1)
687 // Here we handle specialized double binary ops.
688 if (vnf < VNF_Boundary)
690 genTreeOps oper = genTreeOps(vnf);
696 return FpAdd<double, DoubleTraits>(v0, v1);
698 return FpSub<double, DoubleTraits>(v0, v1);
700 return FpMul<double, DoubleTraits>(v0, v1);
702 return FpDiv<double, DoubleTraits>(v0, v1);
704 return FpRem<double, DoubleTraits>(v0, v1);
707 // For any other value of 'oper', we will assert below
712 noway_assert(!"EvalOpSpecialized<double> - binary");
717 float ValueNumStore::EvalOpSpecialized<float>(VNFunc vnf, float v0, float v1)
719 // Here we handle specialized float binary ops.
720 if (vnf < VNF_Boundary)
722 genTreeOps oper = genTreeOps(vnf);
728 return FpAdd<float, FloatTraits>(v0, v1);
730 return FpSub<float, FloatTraits>(v0, v1);
732 return FpMul<float, FloatTraits>(v0, v1);
734 return FpDiv<float, FloatTraits>(v0, v1);
736 return FpRem<float, FloatTraits>(v0, v1);
739 // For any other value of 'oper', we will assert below
743 assert(!"EvalOpSpecialized<float> - binary");
747 template <typename T>
748 T ValueNumStore::EvalOpSpecialized(VNFunc vnf, T v0, T v1)
750 typedef typename jitstd::make_unsigned<T>::type UT;
752 assert((sizeof(T) == 4) || (sizeof(T) == 8));
754 // Here we handle binary ops that are the same for all integer types
755 if (vnf < VNF_Boundary)
757 genTreeOps oper = genTreeOps(vnf);
769 assert(IsIntZero(v1) == false);
770 assert(IsOverflowIntDiv(v0, v1) == false);
774 assert(IsIntZero(v1) == false);
775 assert(IsOverflowIntDiv(v0, v1) == false);
779 assert(IsIntZero(v1) == false);
780 return T(UT(v0) / UT(v1));
783 assert(IsIntZero(v1) == false);
784 return T(UT(v0) % UT(v1));
796 return v0 << (v1 & 0x3F);
805 return v0 >> (v1 & 0x3F);
814 return UINT64(v0) >> (v1 & 0x3F);
818 return UINT32(v0) >> v1;
823 return (v0 << v1) | (UINT64(v0) >> (64 - v1));
827 return (v0 << v1) | (UINT32(v0) >> (32 - v1));
833 return (v0 << (64 - v1)) | (UINT64(v0) >> v1);
837 return (v0 << (32 - v1)) | (UINT32(v0) >> v1);
841 // For any other value of 'oper', we will assert below
845 else // must be a VNF_ function
849 // Here we handle those that are the same for all integer types.
852 // For any other value of 'vnf', we will assert below
857 noway_assert(!"Unhandled operation in EvalOpSpecialized<T> - binary");
862 int ValueNumStore::EvalComparison<double>(VNFunc vnf, double v0, double v1)
864 // Here we handle specialized double comparisons.
866 // We must check for a NaN argument as they they need special handling
867 bool hasNanArg = (_isnan(v0) || _isnan(v1));
869 if (vnf < VNF_Boundary)
871 genTreeOps oper = genTreeOps(vnf);
875 // return false in all cases except for GT_NE;
876 return (oper == GT_NE);
894 // For any other value of 'oper', we will assert below
898 noway_assert(!"Unhandled operation in EvalComparison<double>");
903 int ValueNumStore::EvalComparison<float>(VNFunc vnf, float v0, float v1)
905 // Here we handle specialized float comparisons.
907 // We must check for a NaN argument as they they need special handling
908 bool hasNanArg = (_isnanf(v0) || _isnanf(v1));
910 if (vnf < VNF_Boundary)
912 genTreeOps oper = genTreeOps(vnf);
916 // return false in all cases except for GT_NE;
917 return (oper == GT_NE);
935 // For any other value of 'oper', we will assert below
939 else // must be a VNF_ function
943 // always returns true
958 // For any other value of 'vnf', we will assert below
962 noway_assert(!"Unhandled operation in EvalComparison<float>");
966 template <typename T>
967 int ValueNumStore::EvalComparison(VNFunc vnf, T v0, T v1)
969 typedef typename jitstd::make_unsigned<T>::type UT;
971 // Here we handle the compare ops that are the same for all integer types.
972 if (vnf < VNF_Boundary)
974 genTreeOps oper = genTreeOps(vnf);
990 // For any other value of 'oper', we will assert below
994 else // must be a VNF_ function
999 return T(UT(v0) > UT(v1));
1001 return T(UT(v0) >= UT(v1));
1003 return T(UT(v0) < UT(v1));
1005 return T(UT(v0) <= UT(v1));
1007 // For any other value of 'vnf', we will assert below
1011 noway_assert(!"Unhandled operation in EvalComparison<T>");
1015 // Create a ValueNum for an exception set singleton for 'x'
1017 ValueNum ValueNumStore::VNExcSetSingleton(ValueNum x)
1019 return VNForFunc(TYP_REF, VNF_ExcSetCons, x, VNForEmptyExcSet());
1021 // Create a ValueNumPair for an exception set singleton for 'xp'
1023 ValueNumPair ValueNumStore::VNPExcSetSingleton(ValueNumPair xp)
1025 return ValueNumPair(VNExcSetSingleton(xp.GetLiberal()), VNExcSetSingleton(xp.GetConservative()));
1028 //-------------------------------------------------------------------------------------------
1029 // VNCheckAscending: - Helper method used to verify that elements in an exception set list
1030 // are sorted in ascending order. This method only checks that the
1031 // next value in the list has a greater value number than 'item'.
1034 // item - The previous item visited in the exception set that we are iterating
1035 // xs1 - The tail portion of the exception set that we are iterating.
1038 // - Returns true when the next value is greater than 'item'
1039 // - or whne we have an empty list remaining.
1041 // Note: - Duplicates items aren't allowed in an exception set
1042 // Used to verify that exception sets are in ascending order when processing them.
1044 bool ValueNumStore::VNCheckAscending(ValueNum item, ValueNum xs1)
1046 if (xs1 == VNForEmptyExcSet())
1053 bool b1 = GetVNFunc(xs1, &funcXs1);
1054 assert(b1 && funcXs1.m_func == VNF_ExcSetCons); // Precondition: xs1 is an exception set.
1056 return (item < funcXs1.m_args[0]);
1060 //-------------------------------------------------------------------------------------------
1061 // VNExcSetUnion: - Given two exception sets, performs a set Union operation
1062 // and returns the value number for the combined exception set.
1064 // Arguments: - The arguments must be applications of VNF_ExcSetCons or the empty set
1065 // xs0 - The value number of the first exception set
1066 // xs1 - The value number of the second exception set
1068 // Return Value: - The value number of the combined exception set
1070 // Note: - Checks and relies upon the invariant that exceptions sets
1071 // 1. Have no duplicate values
1072 // 2. all elements in an exception set are in sorted order.
1074 ValueNum ValueNumStore::VNExcSetUnion(ValueNum xs0, ValueNum xs1)
1076 if (xs0 == VNForEmptyExcSet())
1080 else if (xs1 == VNForEmptyExcSet())
1087 bool b0 = GetVNFunc(xs0, &funcXs0);
1088 assert(b0 && funcXs0.m_func == VNF_ExcSetCons); // Precondition: xs0 is an exception set.
1090 bool b1 = GetVNFunc(xs1, &funcXs1);
1091 assert(b1 && funcXs1.m_func == VNF_ExcSetCons); // Precondition: xs1 is an exception set.
1092 ValueNum res = NoVN;
1093 if (funcXs0.m_args[0] < funcXs1.m_args[0])
1095 assert(VNCheckAscending(funcXs0.m_args[0], funcXs0.m_args[1]));
1097 // add the lower one (from xs0) to the result, advance xs0
1098 res = VNForFunc(TYP_REF, VNF_ExcSetCons, funcXs0.m_args[0], VNExcSetUnion(funcXs0.m_args[1], xs1));
1100 else if (funcXs0.m_args[0] == funcXs1.m_args[0])
1102 assert(VNCheckAscending(funcXs0.m_args[0], funcXs0.m_args[1]));
1103 assert(VNCheckAscending(funcXs1.m_args[0], funcXs1.m_args[1]));
1105 // Equal elements; add one (from xs0) to the result, advance both sets
1106 res = VNForFunc(TYP_REF, VNF_ExcSetCons, funcXs0.m_args[0],
1107 VNExcSetUnion(funcXs0.m_args[1], funcXs1.m_args[1]));
1111 assert(funcXs0.m_args[0] > funcXs1.m_args[0]);
1112 assert(VNCheckAscending(funcXs1.m_args[0], funcXs1.m_args[1]));
1114 // add the lower one (from xs1) to the result, advance xs1
1115 res = VNForFunc(TYP_REF, VNF_ExcSetCons, funcXs1.m_args[0], VNExcSetUnion(xs0, funcXs1.m_args[1]));
1122 //--------------------------------------------------------------------------------
1123 // VNPExcSetUnion: - Returns a Value Number Pair that represents the set union
1125 // (see VNExcSetUnion for more details)
1127 // Notes: - This method is used to form a Value Number Pair when we
1128 // want both the Liberal and Conservative Value Numbers
1130 ValueNumPair ValueNumStore::VNPExcSetUnion(ValueNumPair xs0vnp, ValueNumPair xs1vnp)
1132 return ValueNumPair(VNExcSetUnion(xs0vnp.GetLiberal(), xs1vnp.GetLiberal()),
1133 VNExcSetUnion(xs0vnp.GetConservative(), xs1vnp.GetConservative()));
1136 //-------------------------------------------------------------------------------------------
1137 // VNExcSetIntersection: - Given two exception sets, performs a set Intersection operation
1138 // and returns the value number for this exception set.
1140 // Arguments: - The arguments must be applications of VNF_ExcSetCons or the empty set
1141 // xs0 - The value number of the first exception set
1142 // xs1 - The value number of the second exception set
1144 // Return Value: - The value number of the new exception set.
1145 // if the e are no values in common then VNForEmptyExcSet() is returned.
1147 // Note: - Checks and relies upon the invariant that exceptions sets
1148 // 1. Have no duplicate values
1149 // 2. all elements in an exception set are in sorted order.
1151 ValueNum ValueNumStore::VNExcSetIntersection(ValueNum xs0, ValueNum xs1)
1153 if ((xs0 == VNForEmptyExcSet()) || (xs1 == VNForEmptyExcSet()))
1155 return VNForEmptyExcSet();
1160 bool b0 = GetVNFunc(xs0, &funcXs0);
1161 assert(b0 && funcXs0.m_func == VNF_ExcSetCons); // Precondition: xs0 is an exception set.
1163 bool b1 = GetVNFunc(xs1, &funcXs1);
1164 assert(b1 && funcXs1.m_func == VNF_ExcSetCons); // Precondition: xs1 is an exception set.
1165 ValueNum res = NoVN;
1167 if (funcXs0.m_args[0] < funcXs1.m_args[0])
1169 assert(VNCheckAscending(funcXs0.m_args[0], funcXs0.m_args[1]));
1170 res = VNExcSetIntersection(funcXs0.m_args[1], xs1);
1172 else if (funcXs0.m_args[0] == funcXs1.m_args[0])
1174 assert(VNCheckAscending(funcXs0.m_args[0], funcXs0.m_args[1]));
1175 assert(VNCheckAscending(funcXs1.m_args[0], funcXs1.m_args[1]));
1177 // Equal elements; Add it to the result.
1178 res = VNForFunc(TYP_REF, VNF_ExcSetCons, funcXs0.m_args[0],
1179 VNExcSetIntersection(funcXs0.m_args[1], funcXs1.m_args[1]));
1183 assert(funcXs0.m_args[0] > funcXs1.m_args[0]);
1184 assert(VNCheckAscending(funcXs1.m_args[0], funcXs1.m_args[1]));
1185 res = VNExcSetIntersection(xs0, funcXs1.m_args[1]);
1192 //--------------------------------------------------------------------------------
1193 // VNPExcSetIntersection: - Returns a Value Number Pair that represents the set
1194 // intersection for both parts.
1195 // (see VNExcSetIntersection for more details)
1197 // Notes: - This method is used to form a Value Number Pair when we
1198 // want both the Liberal and Conservative Value Numbers
1200 ValueNumPair ValueNumStore::VNPExcSetIntersection(ValueNumPair xs0vnp, ValueNumPair xs1vnp)
1202 return ValueNumPair(VNExcSetIntersection(xs0vnp.GetLiberal(), xs1vnp.GetLiberal()),
1203 VNExcSetIntersection(xs0vnp.GetConservative(), xs1vnp.GetConservative()));
1206 //----------------------------------------------------------------------------------------
1207 // VNExcIsSubset - Given two exception sets, returns true when vnCandidateSet is a
1208 // subset of vnFullSet
1210 // Arguments: - The arguments must be applications of VNF_ExcSetCons or the empty set
1211 // vnFullSet - The value number of the 'full' exception set
1212 // vnCandidateSet - The value number of the 'candidate' exception set
1214 // Return Value: - Returns true if every singleton ExcSet value in the vnCandidateSet
1215 // is also present in the vnFullSet.
1217 // Note: - Checks and relies upon the invariant that exceptions sets
1218 // 1. Have no duplicate values
1219 // 2. all elements in an exception set are in sorted order.
1221 bool ValueNumStore::VNExcIsSubset(ValueNum vnFullSet, ValueNum vnCandidateSet)
1223 if (vnCandidateSet == VNForEmptyExcSet())
1227 else if ((vnFullSet == VNForEmptyExcSet()) || (vnFullSet == ValueNumStore::NoVN))
1232 VNFuncApp funcXsFull;
1233 bool b0 = GetVNFunc(vnFullSet, &funcXsFull);
1234 assert(b0 && funcXsFull.m_func == VNF_ExcSetCons); // Precondition: vnFullSet is an exception set.
1235 VNFuncApp funcXsCand;
1236 bool b1 = GetVNFunc(vnCandidateSet, &funcXsCand);
1237 assert(b1 && funcXsCand.m_func == VNF_ExcSetCons); // Precondition: vnCandidateSet is an exception set.
1239 ValueNum vnFullSetPrev = VNForNull();
1240 ValueNum vnCandSetPrev = VNForNull();
1242 ValueNum vnFullSetRemainder = funcXsFull.m_args[1];
1243 ValueNum vnCandSetRemainder = funcXsCand.m_args[1];
1247 ValueNum vnFullSetItem = funcXsFull.m_args[0];
1248 ValueNum vnCandSetItem = funcXsCand.m_args[0];
1250 // Enforce that both sets are sorted by increasing ValueNumbers
1252 assert(vnFullSetItem > vnFullSetPrev);
1253 assert(vnCandSetItem >= vnCandSetPrev); // equal when we didn't advance the candidate set
1255 if (vnFullSetItem > vnCandSetItem)
1257 // The Full set does not contain the vnCandSetItem
1260 // now we must have (vnFullSetItem <= vnCandSetItem)
1262 // When we have a matching value we advance the candidate set
1264 if (vnFullSetItem == vnCandSetItem)
1266 // Have we finished matching?
1268 if (vnCandSetRemainder == VNForEmptyExcSet())
1270 // We matched every item in the candidate set'
1275 // Advance the candidate set
1277 b1 = GetVNFunc(vnCandSetRemainder, &funcXsCand);
1278 assert(b1 && funcXsCand.m_func == VNF_ExcSetCons); // Precondition: vnCandSetRemainder is an exception set.
1279 vnCandSetRemainder = funcXsCand.m_args[1];
1282 if (vnFullSetRemainder == VNForEmptyExcSet())
1284 // No more items are left in the full exception set
1289 // We will advance the full set
1291 b0 = GetVNFunc(vnFullSetRemainder, &funcXsFull);
1292 assert(b0 && funcXsFull.m_func == VNF_ExcSetCons); // Precondition: vnFullSetRemainder is an exception set.
1293 vnFullSetRemainder = funcXsFull.m_args[1];
1295 vnFullSetPrev = vnFullSetItem;
1296 vnCandSetPrev = vnCandSetItem;
1300 //-------------------------------------------------------------------------------------
1301 // VNUnpackExc: - Given a ValueNum 'vnWx, return via write back parameters both
1302 // the normal and the exception set components.
1305 // vnWx - A value number, it may have an exception set
1306 // pvn - a write back pointer to the normal value portion of 'vnWx'
1307 // pvnx - a write back pointer for the exception set portion of 'vnWx'
1309 // Return Values: - This method signature is void but returns two values using
1310 // the write back parameters.
1312 // Note: When 'vnWx' does not have an exception set, the orginal value is the
1313 // normal value and is written to 'pvn' and VNForEmptyExcSet() is
1314 // written to 'pvnx'.
1315 // When we have an exception set 'vnWx' will be a VN func with m_func
1316 // equal to VNF_ValWithExc.
1318 void ValueNumStore::VNUnpackExc(ValueNum vnWx, ValueNum* pvn, ValueNum* pvnx)
1320 assert(vnWx != NoVN);
1322 if (GetVNFunc(vnWx, &funcApp) && funcApp.m_func == VNF_ValWithExc)
1324 *pvn = funcApp.m_args[0];
1325 *pvnx = funcApp.m_args[1];
1330 *pvnx = VNForEmptyExcSet();
1334 //-------------------------------------------------------------------------------------
1335 // VNPUnpackExc: - Given a ValueNumPair 'vnpWx, return via write back parameters
1336 // both the normal and the exception set components.
1337 // (see VNUnpackExc for more details)
1339 // Notes: - This method is used to form a Value Number Pair when we
1340 // want both the Liberal and Conservative Value Numbers
1342 void ValueNumStore::VNPUnpackExc(ValueNumPair vnpWx, ValueNumPair* pvnp, ValueNumPair* pvnpx)
1344 VNUnpackExc(vnpWx.GetLiberal(), pvnp->GetLiberalAddr(), pvnpx->GetLiberalAddr());
1345 VNUnpackExc(vnpWx.GetConservative(), pvnp->GetConservativeAddr(), pvnpx->GetConservativeAddr());
1348 //-------------------------------------------------------------------------------------
1349 // VNUnionExcSet: - Given a ValueNum 'vnWx' and a current 'vnExcSet', return an
1350 // exception set of the Union of both exception sets.
1353 // vnWx - A value number, it may have an exception set
1354 // vnExcSet - The value number for the current exception set
1356 // Return Values: - The value number of the Union of the exception set of 'vnWx'
1357 // with the current 'vnExcSet'.
1359 // Note: When 'vnWx' does not have an exception set, 'vnExcSet' is returned.
1361 ValueNum ValueNumStore::VNUnionExcSet(ValueNum vnWx, ValueNum vnExcSet)
1363 assert(vnWx != NoVN);
1365 if (GetVNFunc(vnWx, &funcApp) && funcApp.m_func == VNF_ValWithExc)
1367 vnExcSet = VNExcSetUnion(funcApp.m_args[1], vnExcSet);
1372 //-------------------------------------------------------------------------------------
1373 // VNPUnionExcSet: - Given a ValueNum 'vnWx' and a current 'excSet', return an
1374 // exception set of the Union of both exception sets.
1375 // (see VNUnionExcSet for more details)
1377 // Notes: - This method is used to form a Value Number Pair when we
1378 // want both the Liberal and Conservative Value Numbers
1380 ValueNumPair ValueNumStore::VNPUnionExcSet(ValueNumPair vnpWx, ValueNumPair vnpExcSet)
1382 return ValueNumPair(VNUnionExcSet(vnpWx.GetLiberal(), vnpExcSet.GetLiberal()),
1383 VNUnionExcSet(vnpWx.GetConservative(), vnpExcSet.GetConservative()));
1386 //--------------------------------------------------------------------------------
1387 // VNNormalValue: - Returns a Value Number that represents the result for the
1388 // normal (non-exceptional) evaluation for the expression.
1391 // vn - The Value Number for the expression, including any excSet.
1392 // This excSet is an optional item and represents the set of
1393 // possible exceptions for the expression.
1396 // - The Value Number for the expression without the exception set.
1397 // This can be the orginal 'vn', when there are no exceptions.
1399 // Notes: - Whenever we have an exception set the Value Number will be
1400 // a VN func with VNF_ValWithExc.
1401 // This VN func has the normal value as m_args[0]
1403 ValueNum ValueNumStore::VNNormalValue(ValueNum vn)
1406 if (GetVNFunc(vn, &funcApp) && funcApp.m_func == VNF_ValWithExc)
1408 return funcApp.m_args[0];
1416 //------------------------------------------------------------------------------------
1417 // VNMakeNormalUnique:
1420 // vn - The current Value Number for the expression, including any excSet.
1421 // This excSet is an optional item and represents the set of
1422 // possible exceptions for the expression.
1425 // - The normal value is set to a new unique VN, while keeping
1426 // the excSet (if any)
1428 ValueNum ValueNumStore::VNMakeNormalUnique(ValueNum orig)
1430 // First Unpack the existing Norm,Exc for 'elem'
1431 ValueNum vnOrigNorm;
1432 ValueNum vnOrigExcSet;
1433 VNUnpackExc(orig, &vnOrigNorm, &vnOrigExcSet);
1435 // Replace the normal value with a unique ValueNum
1436 ValueNum vnUnique = VNForExpr(m_pComp->compCurBB, TypeOfVN(vnOrigNorm));
1438 // Keep any ExcSet from 'elem'
1439 return VNWithExc(vnUnique, vnOrigExcSet);
1442 //--------------------------------------------------------------------------------
1443 // VNPMakeNormalUniquePair:
1446 // vnp - The Value Number Pair for the expression, including any excSet.
1449 // - The normal values are set to a new unique VNs, while keeping
1450 // the excSets (if any)
1452 ValueNumPair ValueNumStore::VNPMakeNormalUniquePair(ValueNumPair vnp)
1454 return ValueNumPair(VNMakeNormalUnique(vnp.GetLiberal()), VNMakeNormalUnique(vnp.GetConservative()));
1457 //--------------------------------------------------------------------------------
1458 // VNNormalValue: - Returns a Value Number that represents the result for the
1459 // normal (non-exceptional) evaluation for the expression.
1462 // vnp - The Value Number Pair for the expression, including any excSet.
1463 // This excSet is an optional item and represents the set of
1464 // possible exceptions for the expression.
1465 // vnk - The ValueNumKind either liberal or conservative
1468 // - The Value Number for the expression without the exception set.
1469 // This can be the orginal 'vn', when there are no exceptions.
1471 // Notes: - Whenever we have an exception set the Value Number will be
1472 // a VN func with VNF_ValWithExc.
1473 // This VN func has the normal value as m_args[0]
1475 ValueNum ValueNumStore::VNNormalValue(ValueNumPair vnp, ValueNumKind vnk)
1477 return VNNormalValue(vnp.Get(vnk));
1480 //--------------------------------------------------------------------------------
1481 // VNPNormalPair: - Returns a Value Number Pair that represents the result for the
1482 // normal (non-exceptional) evaluation for the expression.
1483 // (see VNNormalValue for more details)
1485 // vnp - The Value Number Pair for the expression, including any excSet.
1487 // Notes: - This method is used to form a Value Number Pair using both
1488 // the Liberal and Conservative Value Numbers normal (non-exceptional)
1490 ValueNumPair ValueNumStore::VNPNormalPair(ValueNumPair vnp)
1492 return ValueNumPair(VNNormalValue(vnp.GetLiberal()), VNNormalValue(vnp.GetConservative()));
1495 //---------------------------------------------------------------------------
1496 // VNExceptionSet: - Returns a Value Number that represents the set of possible
1497 // exceptions that could be encountered for the expression.
1500 // vn - The Value Number for the expression, including any excSet.
1501 // This excSet is an optional item and represents the set of
1502 // possible exceptions for the expression.
1505 // - The Value Number for the set of exceptions of the expression.
1506 // If the 'vn' has no exception set then a special Value Number
1507 // representing the empty exception set is returned.
1509 // Notes: - Whenever we have an exception set the Value Number will be
1510 // a VN func with VNF_ValWithExc.
1511 // This VN func has the exception set as m_args[1]
1513 ValueNum ValueNumStore::VNExceptionSet(ValueNum vn)
1516 if (GetVNFunc(vn, &funcApp) && funcApp.m_func == VNF_ValWithExc)
1518 return funcApp.m_args[1];
1522 return VNForEmptyExcSet();
1526 //--------------------------------------------------------------------------------
1527 // VNPExceptionSet: - Returns a Value Number Pair that represents the set of possible
1528 // exceptions that could be encountered for the expression.
1529 // (see VNExceptionSet for more details)
1531 // Notes: - This method is used to form a Value Number Pair when we
1532 // want both the Liberal and Conservative Value Numbers
1534 ValueNumPair ValueNumStore::VNPExceptionSet(ValueNumPair vnp)
1536 return ValueNumPair(VNExceptionSet(vnp.GetLiberal()), VNExceptionSet(vnp.GetConservative()));
1539 //---------------------------------------------------------------------------
1540 // VNWithExc: - Returns a Value Number that also can have both a normal value
1541 // as well as am exception set.
1544 // vn - The current Value Number for the expression, it may include
1545 // an exception set.
1546 // excSet - The Value Number representing the new exception set that
1547 // is to be added to any exceptions already present in 'vn'
1550 // - The new Value Number for the combination the two inputs.
1551 // If the 'excSet' is the special Value Number representing
1552 // the empty exception set then 'vn' is returned.
1554 // Notes: - We use a Set Union operation, 'VNExcSetUnion', to add any
1555 // new exception items from 'excSet' to the existing set.
1557 ValueNum ValueNumStore::VNWithExc(ValueNum vn, ValueNum excSet)
1559 if (excSet == VNForEmptyExcSet())
1567 VNUnpackExc(vn, &vnNorm, &vnX);
1568 return VNForFunc(TypeOfVN(vnNorm), VNF_ValWithExc, vnNorm, VNExcSetUnion(vnX, excSet));
1572 //--------------------------------------------------------------------------------
1573 // VNPWithExc: - Returns a Value Number Pair that also can have both a normal value
1574 // as well as am exception set.
1575 // (see VNWithExc for more details)
1577 // Notes: = This method is used to form a Value Number Pair when we
1578 // want both the Liberal and Conservative Value Numbers
1580 ValueNumPair ValueNumStore::VNPWithExc(ValueNumPair vnp, ValueNumPair excSetVNP)
1582 return ValueNumPair(VNWithExc(vnp.GetLiberal(), excSetVNP.GetLiberal()),
1583 VNWithExc(vnp.GetConservative(), excSetVNP.GetConservative()));
1586 bool ValueNumStore::IsKnownNonNull(ValueNum vn)
1593 return GetVNFunc(vn, &funcAttr) && (s_vnfOpAttribs[funcAttr.m_func] & VNFOA_KnownNonNull) != 0;
1596 bool ValueNumStore::IsSharedStatic(ValueNum vn)
1603 return GetVNFunc(vn, &funcAttr) && (s_vnfOpAttribs[funcAttr.m_func] & VNFOA_SharedStatic) != 0;
1606 ValueNumStore::Chunk::Chunk(CompAllocator alloc,
1607 ValueNum* pNextBaseVN,
1609 ChunkExtraAttribs attribs,
1610 BasicBlock::loopNumber loopNum)
1611 : m_defs(nullptr), m_numUsed(0), m_baseVN(*pNextBaseVN), m_typ(typ), m_attribs(attribs), m_loopNum(loopNum)
1613 // Allocate "m_defs" here, according to the typ/attribs pair.
1618 break; // Nothing to do.
1623 m_defs = new (alloc) Alloc<TYP_INT>::Type[ChunkSize];
1626 m_defs = new (alloc) Alloc<TYP_FLOAT>::Type[ChunkSize];
1629 m_defs = new (alloc) Alloc<TYP_LONG>::Type[ChunkSize];
1632 m_defs = new (alloc) Alloc<TYP_DOUBLE>::Type[ChunkSize];
1635 m_defs = new (alloc) Alloc<TYP_BYREF>::Type[ChunkSize];
1638 // We allocate space for a single REF constant, NULL, so we can access these values uniformly.
1639 // Since this value is always the same, we represent it as a static.
1640 m_defs = &s_specialRefConsts[0];
1641 break; // Nothing to do.
1643 assert(false); // Should not reach here.
1648 m_defs = new (alloc) VNHandle[ChunkSize];
1652 m_defs = new (alloc) VNFunc[ChunkSize];
1656 m_defs = new (alloc) VNDefFunc1Arg[ChunkSize];
1659 m_defs = new (alloc) VNDefFunc2Arg[ChunkSize];
1662 m_defs = new (alloc) VNDefFunc3Arg[ChunkSize];
1665 m_defs = new (alloc) VNDefFunc4Arg[ChunkSize];
1670 *pNextBaseVN += ChunkSize;
1673 ValueNumStore::Chunk* ValueNumStore::GetAllocChunk(var_types typ,
1674 ChunkExtraAttribs attribs,
1675 BasicBlock::loopNumber loopNum)
1679 if (loopNum == MAX_LOOP_NUM)
1681 // Loop nest is unknown/irrelevant for this VN.
1686 // Loop nest is interesting. Since we know this is only true for unique VNs, we know attribs will
1687 // be CEA_None and can just index based on loop number.
1688 noway_assert(attribs == CEA_None);
1689 // Map NOT_IN_LOOP -> MAX_LOOP_NUM to make the index range contiguous [0..MAX_LOOP_NUM]
1690 index = CEA_Count + (loopNum == BasicBlock::NOT_IN_LOOP ? MAX_LOOP_NUM : loopNum);
1692 ChunkNum cn = m_curAllocChunk[typ][index];
1695 res = m_chunks.Get(cn);
1696 if (res->m_numUsed < ChunkSize)
1701 // Otherwise, must allocate a new one.
1702 res = new (m_alloc) Chunk(m_alloc, &m_nextChunkBase, typ, attribs, loopNum);
1703 cn = m_chunks.Push(res);
1704 m_curAllocChunk[typ][index] = cn;
1708 ValueNum ValueNumStore::VNForIntCon(INT32 cnsVal)
1710 if (IsSmallIntConst(cnsVal))
1712 unsigned ind = cnsVal - SmallIntConstMin;
1713 ValueNum vn = m_VNsForSmallIntConsts[ind];
1718 vn = GetVNForIntCon(cnsVal);
1719 m_VNsForSmallIntConsts[ind] = vn;
1724 return GetVNForIntCon(cnsVal);
1728 ValueNum ValueNumStore::VNForLongCon(INT64 cnsVal)
1731 if (GetLongCnsMap()->Lookup(cnsVal, &res))
1737 Chunk* c = GetAllocChunk(TYP_LONG, CEA_Const);
1738 unsigned offsetWithinChunk = c->AllocVN();
1739 res = c->m_baseVN + offsetWithinChunk;
1740 reinterpret_cast<INT64*>(c->m_defs)[offsetWithinChunk] = cnsVal;
1741 GetLongCnsMap()->Set(cnsVal, res);
1746 ValueNum ValueNumStore::VNForFloatCon(float cnsVal)
1749 if (GetFloatCnsMap()->Lookup(cnsVal, &res))
1755 Chunk* c = GetAllocChunk(TYP_FLOAT, CEA_Const);
1756 unsigned offsetWithinChunk = c->AllocVN();
1757 res = c->m_baseVN + offsetWithinChunk;
1758 reinterpret_cast<float*>(c->m_defs)[offsetWithinChunk] = cnsVal;
1759 GetFloatCnsMap()->Set(cnsVal, res);
1764 ValueNum ValueNumStore::VNForDoubleCon(double cnsVal)
1767 if (GetDoubleCnsMap()->Lookup(cnsVal, &res))
1773 Chunk* c = GetAllocChunk(TYP_DOUBLE, CEA_Const);
1774 unsigned offsetWithinChunk = c->AllocVN();
1775 res = c->m_baseVN + offsetWithinChunk;
1776 reinterpret_cast<double*>(c->m_defs)[offsetWithinChunk] = cnsVal;
1777 GetDoubleCnsMap()->Set(cnsVal, res);
1782 ValueNum ValueNumStore::VNForByrefCon(INT64 cnsVal)
1785 if (GetByrefCnsMap()->Lookup(cnsVal, &res))
1791 Chunk* c = GetAllocChunk(TYP_BYREF, CEA_Const);
1792 unsigned offsetWithinChunk = c->AllocVN();
1793 res = c->m_baseVN + offsetWithinChunk;
1794 reinterpret_cast<INT64*>(c->m_defs)[offsetWithinChunk] = cnsVal;
1795 GetByrefCnsMap()->Set(cnsVal, res);
1800 ValueNum ValueNumStore::VNForCastOper(var_types castToType, bool srcIsUnsigned /*=false*/)
1802 assert(castToType != TYP_STRUCT);
1803 INT32 cnsVal = INT32(castToType) << INT32(VCA_BitCount);
1804 assert((cnsVal & INT32(VCA_ReservedBits)) == 0);
1808 // We record the srcIsUnsigned by or-ing a 0x01
1809 cnsVal |= INT32(VCA_UnsignedSrc);
1811 ValueNum result = VNForIntCon(cnsVal);
1814 if (m_pComp->verbose)
1816 printf(" VNForCastOper(%s%s) is " FMT_VN "\n", varTypeName(castToType), srcIsUnsigned ? ", unsignedSrc" : "",
1824 ValueNum ValueNumStore::VNForHandle(ssize_t cnsVal, unsigned handleFlags)
1826 assert((handleFlags & ~GTF_ICON_HDL_MASK) == 0);
1830 VNHandle::Initialize(&handle, cnsVal, handleFlags);
1831 if (GetHandleMap()->Lookup(handle, &res))
1837 Chunk* c = GetAllocChunk(TYP_I_IMPL, CEA_Handle);
1838 unsigned offsetWithinChunk = c->AllocVN();
1839 res = c->m_baseVN + offsetWithinChunk;
1840 reinterpret_cast<VNHandle*>(c->m_defs)[offsetWithinChunk] = handle;
1841 GetHandleMap()->Set(handle, res);
1846 // Returns the value number for zero of the given "typ".
1847 // It has an unreached() for a "typ" that has no zero value, such as TYP_VOID.
1848 ValueNum ValueNumStore::VNZeroForType(var_types typ)
1859 return VNForIntCon(0);
1862 return VNForLongCon(0);
1864 return VNForFloatCon(0.0f);
1866 return VNForDoubleCon(0.0);
1870 return VNForByrefCon(0);
1873 // TODO-CQ: Improve value numbering for SIMD types.
1878 #endif // FEATURE_SIMD
1879 return VNForZeroMap(); // Recursion!
1881 // These should be unreached.
1883 unreached(); // Should handle all types.
1887 // Returns the value number for one of the given "typ".
1888 // It returns NoVN for a "typ" that has no one value, such as TYP_REF.
1889 ValueNum ValueNumStore::VNOneForType(var_types typ)
1900 return VNForIntCon(1);
1903 return VNForLongCon(1);
1905 return VNForFloatCon(1.0f);
1907 return VNForDoubleCon(1.0);
1914 class Object* ValueNumStore::s_specialRefConsts[] = {nullptr, nullptr, nullptr};
1916 //----------------------------------------------------------------------------------------
1917 // VNForFunc - Returns the ValueNum associated with 'func'
1918 // There is a one-to-one relationship between the ValueNum and 'func'
1921 // typ - The type of the resulting ValueNum produced by 'func'
1922 // func - Any nullary VNFunc
1924 // Return Value: - Returns the ValueNum associated with 'func'
1926 // Note: - This method only handles Nullary operators (i.e., symbolic constants).
1928 ValueNum ValueNumStore::VNForFunc(var_types typ, VNFunc func)
1930 assert(VNFuncArity(func) == 0);
1931 assert(func != VNF_NotAField);
1935 // Have we already assigned a ValueNum for 'func' ?
1937 if (!GetVNFunc0Map()->Lookup(func, &resultVN))
1939 // Allocate a new ValueNum for 'func'
1940 Chunk* c = GetAllocChunk(typ, CEA_Func0);
1941 unsigned offsetWithinChunk = c->AllocVN();
1942 resultVN = c->m_baseVN + offsetWithinChunk;
1943 reinterpret_cast<VNFunc*>(c->m_defs)[offsetWithinChunk] = func;
1944 GetVNFunc0Map()->Set(func, resultVN);
1949 //----------------------------------------------------------------------------------------
1950 // VNForFunc - Returns the ValueNum associated with 'func'('arg0VN')
1951 // There is a one-to-one relationship between the ValueNum
1952 // and 'func'('arg0VN')
1955 // typ - The type of the resulting ValueNum produced by 'func'
1956 // func - Any unary VNFunc
1957 // arg0VN - The ValueNum of the argument to 'func'
1959 // Return Value: - Returns the ValueNum associated with 'func'('arg0VN')
1961 // Note: - This method only handles Unary operators
1963 ValueNum ValueNumStore::VNForFunc(var_types typ, VNFunc func, ValueNum arg0VN)
1965 assert(arg0VN == VNNormalValue(arg0VN)); // Arguments don't carry exceptions.
1967 // Try to perform constant-folding.
1968 if (CanEvalForConstantArgs(func) && IsVNConstant(arg0VN))
1970 return EvalFuncForConstantArgs(typ, func, arg0VN);
1975 // Have we already assigned a ValueNum for 'func'('arg0VN') ?
1977 VNDefFunc1Arg fstruct(func, arg0VN);
1978 if (!GetVNFunc1Map()->Lookup(fstruct, &resultVN))
1980 // Otherwise, Allocate a new ValueNum for 'func'('arg0VN')
1982 Chunk* c = GetAllocChunk(typ, CEA_Func1);
1983 unsigned offsetWithinChunk = c->AllocVN();
1984 resultVN = c->m_baseVN + offsetWithinChunk;
1985 reinterpret_cast<VNDefFunc1Arg*>(c->m_defs)[offsetWithinChunk] = fstruct;
1986 // Record 'resultVN' in the Func1Map
1987 GetVNFunc1Map()->Set(fstruct, resultVN);
1992 //----------------------------------------------------------------------------------------
1993 // VNForFunc - Returns the ValueNum associated with 'func'('arg0VN','arg1VN')
1994 // There is a one-to-one relationship between the ValueNum
1995 // and 'func'('arg0VN','arg1VN')
1998 // typ - The type of the resulting ValueNum produced by 'func'
1999 // func - Any binary VNFunc
2000 // arg0VN - The ValueNum of the first argument to 'func'
2001 // arg1VN - The ValueNum of the second argument to 'func'
2003 // Return Value: - Returns the ValueNum associated with 'func'('arg0VN','arg1VN')
2005 // Note: - This method only handles Binary operators
2007 ValueNum ValueNumStore::VNForFunc(var_types typ, VNFunc func, ValueNum arg0VN, ValueNum arg1VN)
2009 assert(arg0VN != NoVN && arg1VN != NoVN);
2010 assert(arg0VN == VNNormalValue(arg0VN)); // Arguments carry no exceptions.
2011 assert(arg1VN == VNNormalValue(arg1VN)); // Arguments carry no exceptions.
2012 assert(VNFuncArity(func) == 2);
2013 assert(func != VNF_MapSelect); // Precondition: use the special function VNForMapSelect defined for that.
2017 // When both operands are constants we can usually perform constant-folding.
2019 if (CanEvalForConstantArgs(func) && IsVNConstant(arg0VN) && IsVNConstant(arg1VN))
2021 bool canFold = true; // Normally we will be able to fold this 'func'
2023 // Special case for VNF_Cast of constant handles
2024 // Don't allow an eval/fold of a GT_CAST(non-I_IMPL, Handle)
2026 if ((func == VNF_Cast) && (typ != TYP_I_IMPL) && IsVNHandle(arg0VN))
2031 // Currently CanEvalForConstantArgs() returns false for VNF_CastOvf
2032 // In the future we could handle this case in folding.
2033 assert(func != VNF_CastOvf);
2035 // It is possible for us to have mismatched types (see Bug 750863)
2036 // We don't try to fold a binary operation when one of the constant operands
2037 // is a floating-point constant and the other is not.
2039 var_types arg0VNtyp = TypeOfVN(arg0VN);
2040 bool arg0IsFloating = varTypeIsFloating(arg0VNtyp);
2042 var_types arg1VNtyp = TypeOfVN(arg1VN);
2043 bool arg1IsFloating = varTypeIsFloating(arg1VNtyp);
2045 if (arg0IsFloating != arg1IsFloating)
2050 // NaNs are unordered wrt to other floats. While an ordered
2051 // comparison would return false, an unordered comparison
2052 // will return true if any operands are a NaN. We only perform
2053 // ordered NaN comparison in EvalComparison.
2054 if ((arg0IsFloating && (((arg0VNtyp == TYP_FLOAT) && _isnanf(GetConstantSingle(arg0VN))) ||
2055 ((arg0VNtyp == TYP_DOUBLE) && _isnan(GetConstantDouble(arg0VN))))) ||
2056 (arg1IsFloating && (((arg1VNtyp == TYP_FLOAT) && _isnanf(GetConstantSingle(arg1VN))) ||
2057 ((arg1VNtyp == TYP_DOUBLE) && _isnan(GetConstantDouble(arg1VN))))))
2062 if (typ == TYP_BYREF)
2064 // We don't want to fold expressions that produce TYP_BYREF
2068 bool shouldFold = canFold;
2072 // We can fold the expression, but we don't want to fold
2073 // when the expression will always throw an exception
2074 shouldFold = VNEvalShouldFold(typ, func, arg0VN, arg1VN);
2079 return EvalFuncForConstantArgs(typ, func, arg0VN, arg1VN);
2082 // We canonicalize commutative operations.
2083 // (Perhaps should eventually handle associative/commutative [AC] ops -- but that gets complicated...)
2084 if (VNFuncIsCommutative(func))
2086 // Order arg0 arg1 by numerical VN value.
2087 if (arg0VN > arg1VN)
2089 jitstd::swap(arg0VN, arg1VN);
2093 // Have we already assigned a ValueNum for 'func'('arg0VN','arg1VN') ?
2095 VNDefFunc2Arg fstruct(func, arg0VN, arg1VN);
2096 if (!GetVNFunc2Map()->Lookup(fstruct, &resultVN))
2098 if (func == VNF_CastClass)
2100 // In terms of values, a castclass always returns its second argument, the object being cast.
2101 // The operation may also throw an exception
2102 ValueNum vnExcSet = VNExcSetSingleton(VNForFunc(TYP_REF, VNF_InvalidCastExc, arg1VN, arg0VN));
2103 resultVN = VNWithExc(arg1VN, vnExcSet);
2107 resultVN = EvalUsingMathIdentity(typ, func, arg0VN, arg1VN);
2109 // Do we have a valid resultVN?
2110 if ((resultVN == NoVN) || (TypeOfVN(resultVN) != typ))
2112 // Otherwise, Allocate a new ValueNum for 'func'('arg0VN','arg1VN')
2114 Chunk* c = GetAllocChunk(typ, CEA_Func2);
2115 unsigned offsetWithinChunk = c->AllocVN();
2116 resultVN = c->m_baseVN + offsetWithinChunk;
2117 reinterpret_cast<VNDefFunc2Arg*>(c->m_defs)[offsetWithinChunk] = fstruct;
2118 // Record 'resultVN' in the Func2Map
2119 GetVNFunc2Map()->Set(fstruct, resultVN);
2126 //----------------------------------------------------------------------------------------
2127 // VNForFunc - Returns the ValueNum associated with 'func'('arg0VN','arg1VN','arg2VN')
2128 // There is a one-to-one relationship between the ValueNum
2129 // and 'func'('arg0VN','arg1VN','arg2VN')
2132 // typ - The type of the resulting ValueNum produced by 'func'
2133 // func - Any binary VNFunc
2134 // arg0VN - The ValueNum of the first argument to 'func'
2135 // arg1VN - The ValueNum of the second argument to 'func'
2136 // arg2VN - The ValueNum of the third argument to 'func'
2138 // Return Value: - Returns the ValueNum associated with 'func'('arg0VN','arg1VN','arg1VN)
2140 // Note: - This method only handles Trinary operations
2141 // We have to special case VNF_PhiDef, as it's first two arguments are not ValueNums
2143 ValueNum ValueNumStore::VNForFunc(var_types typ, VNFunc func, ValueNum arg0VN, ValueNum arg1VN, ValueNum arg2VN)
2145 assert(arg0VN != NoVN);
2146 assert(arg1VN != NoVN);
2147 assert(arg2VN != NoVN);
2148 assert(VNFuncArity(func) == 3);
2151 // Function arguments carry no exceptions.
2153 if (func != VNF_PhiDef)
2155 // For a phi definition first and second argument are "plain" local/ssa numbers.
2156 // (I don't know if having such non-VN arguments to a VN function is a good idea -- if we wanted to declare
2157 // ValueNum to be "short" it would be a problem, for example. But we'll leave it for now, with these explicit
2159 assert(arg0VN == VNNormalValue(arg0VN));
2160 assert(arg1VN == VNNormalValue(arg1VN));
2162 assert(arg2VN == VNNormalValue(arg2VN));
2164 assert(VNFuncArity(func) == 3);
2168 // Have we already assigned a ValueNum for 'func'('arg0VN','arg1VN','arg2VN') ?
2170 VNDefFunc3Arg fstruct(func, arg0VN, arg1VN, arg2VN);
2171 if (!GetVNFunc3Map()->Lookup(fstruct, &resultVN))
2173 // Otherwise, Allocate a new ValueNum for 'func'('arg0VN','arg1VN','arg2VN')
2175 Chunk* c = GetAllocChunk(typ, CEA_Func3);
2176 unsigned offsetWithinChunk = c->AllocVN();
2177 resultVN = c->m_baseVN + offsetWithinChunk;
2178 reinterpret_cast<VNDefFunc3Arg*>(c->m_defs)[offsetWithinChunk] = fstruct;
2179 // Record 'resultVN' in the Func3Map
2180 GetVNFunc3Map()->Set(fstruct, resultVN);
2185 // ----------------------------------------------------------------------------------------
2186 // VNForFunc - Returns the ValueNum associated with 'func'('arg0VN','arg1VN','arg2VN','arg3VN')
2187 // There is a one-to-one relationship between the ValueNum
2188 // and 'func'('arg0VN','arg1VN','arg2VN','arg3VN')
2191 // typ - The type of the resulting ValueNum produced by 'func'
2192 // func - Any binary VNFunc
2193 // arg0VN - The ValueNum of the first argument to 'func'
2194 // arg1VN - The ValueNum of the second argument to 'func'
2195 // arg2VN - The ValueNum of the third argument to 'func'
2196 // arg3VN - The ValueNum of the fourth argument to 'func'
2198 // Return Value: - Returns the ValueNum associated with 'func'('arg0VN','arg1VN','arg2VN','arg3VN')
2200 // Note: Currently the only four operand func is the VNF_PtrToArrElem operation
2202 ValueNum ValueNumStore::VNForFunc(
2203 var_types typ, VNFunc func, ValueNum arg0VN, ValueNum arg1VN, ValueNum arg2VN, ValueNum arg3VN)
2205 assert(arg0VN != NoVN && arg1VN != NoVN && arg2VN != NoVN && arg3VN != NoVN);
2207 // Function arguments carry no exceptions.
2208 assert(arg0VN == VNNormalValue(arg0VN));
2209 assert(arg1VN == VNNormalValue(arg1VN));
2210 assert(arg2VN == VNNormalValue(arg2VN));
2211 assert(arg3VN == VNNormalValue(arg3VN));
2212 assert(VNFuncArity(func) == 4);
2216 // Have we already assigned a ValueNum for 'func'('arg0VN','arg1VN','arg2VN','arg3VN') ?
2218 VNDefFunc4Arg fstruct(func, arg0VN, arg1VN, arg2VN, arg3VN);
2219 if (!GetVNFunc4Map()->Lookup(fstruct, &resultVN))
2221 // Otherwise, Allocate a new ValueNum for 'func'('arg0VN','arg1VN','arg2VN','arg3VN')
2223 Chunk* c = GetAllocChunk(typ, CEA_Func4);
2224 unsigned offsetWithinChunk = c->AllocVN();
2225 resultVN = c->m_baseVN + offsetWithinChunk;
2226 reinterpret_cast<VNDefFunc4Arg*>(c->m_defs)[offsetWithinChunk] = fstruct;
2227 // Record 'resultVN' in the Func4Map
2228 GetVNFunc4Map()->Set(fstruct, resultVN);
2233 //------------------------------------------------------------------------------
2234 // VNForMapStore : Evaluate VNF_MapStore with the given arguments.
2239 // arg0VN - Map value number
2240 // arg1VN - Index value number
2241 // arg2VN - New value for map[index]
2244 // Value number for the result of the evaluation.
2246 ValueNum ValueNumStore::VNForMapStore(var_types typ, ValueNum arg0VN, ValueNum arg1VN, ValueNum arg2VN)
2248 ValueNum result = VNForFunc(typ, VNF_MapStore, arg0VN, arg1VN, arg2VN);
2250 if (m_pComp->verbose)
2252 printf(" VNForMapStore(" FMT_VN ", " FMT_VN ", " FMT_VN "):%s returns ", arg0VN, arg1VN, arg2VN,
2254 m_pComp->vnPrint(result, 1);
2261 //------------------------------------------------------------------------------
2262 // VNForMapSelect : Evaluate VNF_MapSelect with the given arguments.
2266 // vnk - Value number kind
2268 // arg0VN - Map value number
2269 // arg1VN - Index value number
2272 // Value number for the result of the evaluation.
2275 // This requires a "ValueNumKind" because it will attempt, given "select(phi(m1, ..., mk), ind)", to evaluate
2276 // "select(m1, ind)", ..., "select(mk, ind)" to see if they agree. It needs to know which kind of value number
2277 // (liberal/conservative) to read from the SSA def referenced in the phi argument.
2279 ValueNum ValueNumStore::VNForMapSelect(ValueNumKind vnk, var_types typ, ValueNum arg0VN, ValueNum arg1VN)
2281 int budget = m_mapSelectBudget;
2282 bool usedRecursiveVN = false;
2283 ValueNum result = VNForMapSelectWork(vnk, typ, arg0VN, arg1VN, &budget, &usedRecursiveVN);
2285 // The remaining budget should always be between [0..m_mapSelectBudget]
2286 assert((budget >= 0) && (budget <= m_mapSelectBudget));
2289 if (m_pComp->verbose)
2291 printf(" VNForMapSelect(" FMT_VN ", " FMT_VN "):%s returns ", arg0VN, arg1VN, varTypeName(typ));
2292 m_pComp->vnPrint(result, 1);
2299 //------------------------------------------------------------------------------
2300 // VNForMapSelectWork : A method that does the work for VNForMapSelect and may call itself recursively.
2304 // vnk - Value number kind
2306 // arg0VN - Zeroth argument
2307 // arg1VN - First argument
2308 // pBudget - Remaining budget for the outer evaluation
2309 // pUsedRecursiveVN - Out-parameter that is set to true iff RecursiveVN was returned from this method
2310 // or from a method called during one of recursive invocations.
2313 // Value number for the result of the evaluation.
2316 // This requires a "ValueNumKind" because it will attempt, given "select(phi(m1, ..., mk), ind)", to evaluate
2317 // "select(m1, ind)", ..., "select(mk, ind)" to see if they agree. It needs to know which kind of value number
2318 // (liberal/conservative) to read from the SSA def referenced in the phi argument.
2320 ValueNum ValueNumStore::VNForMapSelectWork(
2321 ValueNumKind vnk, var_types typ, ValueNum arg0VN, ValueNum arg1VN, int* pBudget, bool* pUsedRecursiveVN)
2324 // This label allows us to directly implement a tail call by setting up the arguments, and doing a goto to here.
2325 assert(arg0VN != NoVN && arg1VN != NoVN);
2326 assert(arg0VN == VNNormalValue(arg0VN)); // Arguments carry no exceptions.
2327 assert(arg1VN == VNNormalValue(arg1VN)); // Arguments carry no exceptions.
2329 *pUsedRecursiveVN = false;
2332 // Provide a mechanism for writing tests that ensure we don't call this ridiculously often.
2335 // This printing is sometimes useful in debugging.
2336 // if ((m_numMapSels % 1000) == 0) printf("%d VNF_MapSelect applications.\n", m_numMapSels);
2338 unsigned selLim = JitConfig.JitVNMapSelLimit();
2339 assert(selLim == 0 || m_numMapSels < selLim);
2343 VNDefFunc2Arg fstruct(VNF_MapSelect, arg0VN, arg1VN);
2344 if (GetVNFunc2Map()->Lookup(fstruct, &res))
2350 // Give up if we've run out of budget.
2351 if (--(*pBudget) <= 0)
2353 // We have to use 'nullptr' for the basic block here, because subsequent expressions
2354 // in different blocks may find this result in the VNFunc2Map -- other expressions in
2355 // the IR may "evaluate" to this same VNForExpr, so it is not "unique" in the sense
2356 // that permits the BasicBlock attribution.
2357 res = VNForExpr(nullptr, typ);
2358 GetVNFunc2Map()->Set(fstruct, res);
2362 // If it's recursive, stop the recursion.
2363 if (SelectIsBeingEvaluatedRecursively(arg0VN, arg1VN))
2365 *pUsedRecursiveVN = true;
2369 if (arg0VN == VNForZeroMap())
2371 return VNZeroForType(typ);
2373 else if (IsVNFunc(arg0VN))
2376 GetVNFunc(arg0VN, &funcApp);
2377 if (funcApp.m_func == VNF_MapStore)
2379 // select(store(m, i, v), i) == v
2380 if (funcApp.m_args[1] == arg1VN)
2382 #if FEATURE_VN_TRACE_APPLY_SELECTORS
2383 JITDUMP(" AX1: select([" FMT_VN "]store(" FMT_VN ", " FMT_VN ", " FMT_VN "), " FMT_VN
2384 ") ==> " FMT_VN ".\n",
2385 funcApp.m_args[0], arg0VN, funcApp.m_args[1], funcApp.m_args[2], arg1VN, funcApp.m_args[2]);
2387 return funcApp.m_args[2];
2389 // i # j ==> select(store(m, i, v), j) == select(m, j)
2390 // Currently the only source of distinctions is when both indices are constants.
2391 else if (IsVNConstant(arg1VN) && IsVNConstant(funcApp.m_args[1]))
2393 assert(funcApp.m_args[1] != arg1VN); // we already checked this above.
2394 #if FEATURE_VN_TRACE_APPLY_SELECTORS
2395 JITDUMP(" AX2: " FMT_VN " != " FMT_VN " ==> select([" FMT_VN "]store(" FMT_VN ", " FMT_VN
2396 ", " FMT_VN "), " FMT_VN ") ==> select(" FMT_VN ", " FMT_VN ").\n",
2397 arg1VN, funcApp.m_args[1], arg0VN, funcApp.m_args[0], funcApp.m_args[1], funcApp.m_args[2],
2398 arg1VN, funcApp.m_args[0], arg1VN);
2400 // This is the equivalent of the recursive tail call:
2401 // return VNForMapSelect(vnk, typ, funcApp.m_args[0], arg1VN);
2402 // Make sure we capture any exceptions from the "i" and "v" of the store...
2403 arg0VN = funcApp.m_args[0];
2407 else if (funcApp.m_func == VNF_PhiDef || funcApp.m_func == VNF_PhiMemoryDef)
2409 unsigned lclNum = BAD_VAR_NUM;
2410 bool isMemory = false;
2411 VNFuncApp phiFuncApp;
2412 bool defArgIsFunc = false;
2413 if (funcApp.m_func == VNF_PhiDef)
2415 lclNum = unsigned(funcApp.m_args[0]);
2416 defArgIsFunc = GetVNFunc(funcApp.m_args[2], &phiFuncApp);
2420 assert(funcApp.m_func == VNF_PhiMemoryDef);
2422 defArgIsFunc = GetVNFunc(funcApp.m_args[1], &phiFuncApp);
2424 if (defArgIsFunc && phiFuncApp.m_func == VNF_Phi)
2426 // select(phi(m1, m2), x): if select(m1, x) == select(m2, x), return that, else new fresh.
2427 // Get the first argument of the phi.
2429 // We need to be careful about breaking infinite recursion. Record the outer select.
2430 m_fixedPointMapSels.Push(VNDefFunc2Arg(VNF_MapSelect, arg0VN, arg1VN));
2432 assert(IsVNConstant(phiFuncApp.m_args[0]));
2433 unsigned phiArgSsaNum = ConstantValue<unsigned>(phiFuncApp.m_args[0]);
2437 phiArgVN = m_pComp->GetMemoryPerSsaData(phiArgSsaNum)->m_vnPair.Get(vnk);
2441 phiArgVN = m_pComp->lvaTable[lclNum].GetPerSsaData(phiArgSsaNum)->m_vnPair.Get(vnk);
2443 if (phiArgVN != ValueNumStore::NoVN)
2445 bool allSame = true;
2446 ValueNum argRest = phiFuncApp.m_args[1];
2447 ValueNum sameSelResult =
2448 VNForMapSelectWork(vnk, typ, phiArgVN, arg1VN, pBudget, pUsedRecursiveVN);
2450 // It is possible that we just now exceeded our budget, if so we need to force an early exit
2451 // and stop calling VNForMapSelectWork
2454 // We don't have any budget remaining to verify that all phiArgs are the same
2455 // so setup the default failure case now.
2459 while (allSame && argRest != ValueNumStore::NoVN)
2461 ValueNum cur = argRest;
2462 VNFuncApp phiArgFuncApp;
2463 if (GetVNFunc(argRest, &phiArgFuncApp) && phiArgFuncApp.m_func == VNF_Phi)
2465 cur = phiArgFuncApp.m_args[0];
2466 argRest = phiArgFuncApp.m_args[1];
2470 argRest = ValueNumStore::NoVN; // Cause the loop to terminate.
2472 assert(IsVNConstant(cur));
2473 phiArgSsaNum = ConstantValue<unsigned>(cur);
2476 phiArgVN = m_pComp->GetMemoryPerSsaData(phiArgSsaNum)->m_vnPair.Get(vnk);
2480 phiArgVN = m_pComp->lvaTable[lclNum].GetPerSsaData(phiArgSsaNum)->m_vnPair.Get(vnk);
2482 if (phiArgVN == ValueNumStore::NoVN)
2488 bool usedRecursiveVN = false;
2489 ValueNum curResult =
2490 VNForMapSelectWork(vnk, typ, phiArgVN, arg1VN, pBudget, &usedRecursiveVN);
2491 *pUsedRecursiveVN |= usedRecursiveVN;
2492 if (sameSelResult == ValueNumStore::RecursiveVN)
2494 sameSelResult = curResult;
2496 if (curResult != ValueNumStore::RecursiveVN && curResult != sameSelResult)
2502 if (allSame && sameSelResult != ValueNumStore::RecursiveVN)
2504 // Make sure we're popping what we pushed.
2505 assert(FixedPointMapSelsTopHasValue(arg0VN, arg1VN));
2506 m_fixedPointMapSels.Pop();
2508 // To avoid exponential searches, we make sure that this result is memo-ized.
2509 // The result is always valid for memoization if we didn't rely on RecursiveVN to get it.
2510 // If RecursiveVN was used, we are processing a loop and we can't memo-ize this intermediate
2511 // result if, e.g., this block is in a multi-entry loop.
2512 if (!*pUsedRecursiveVN)
2514 GetVNFunc2Map()->Set(fstruct, sameSelResult);
2517 return sameSelResult;
2519 // Otherwise, fall through to creating the select(phi(m1, m2), x) function application.
2521 // Make sure we're popping what we pushed.
2522 assert(FixedPointMapSelsTopHasValue(arg0VN, arg1VN));
2523 m_fixedPointMapSels.Pop();
2528 // We may have run out of budget and already assigned a result
2529 if (!GetVNFunc2Map()->Lookup(fstruct, &res))
2531 // Otherwise, assign a new VN for the function application.
2532 Chunk* c = GetAllocChunk(typ, CEA_Func2);
2533 unsigned offsetWithinChunk = c->AllocVN();
2534 res = c->m_baseVN + offsetWithinChunk;
2535 reinterpret_cast<VNDefFunc2Arg*>(c->m_defs)[offsetWithinChunk] = fstruct;
2536 GetVNFunc2Map()->Set(fstruct, res);
2542 ValueNum ValueNumStore::EvalFuncForConstantArgs(var_types typ, VNFunc func, ValueNum arg0VN)
2544 assert(CanEvalForConstantArgs(func));
2545 assert(IsVNConstant(arg0VN));
2546 switch (TypeOfVN(arg0VN))
2550 int resVal = EvalOp<int>(func, ConstantValue<int>(arg0VN));
2551 // Unary op on a handle results in a handle.
2552 return IsVNHandle(arg0VN) ? VNForHandle(ssize_t(resVal), GetHandleFlags(arg0VN)) : VNForIntCon(resVal);
2556 INT64 resVal = EvalOp<INT64>(func, ConstantValue<INT64>(arg0VN));
2557 // Unary op on a handle results in a handle.
2558 return IsVNHandle(arg0VN) ? VNForHandle(ssize_t(resVal), GetHandleFlags(arg0VN)) : VNForLongCon(resVal);
2562 float resVal = EvalOp<float>(func, ConstantValue<float>(arg0VN));
2563 return VNForFloatCon(resVal);
2567 double resVal = EvalOp<double>(func, ConstantValue<double>(arg0VN));
2568 return VNForDoubleCon(resVal);
2572 // If arg0 has a possible exception, it wouldn't have been constant.
2573 assert(!VNHasExc(arg0VN));
2575 assert(arg0VN == VNForNull()); // Only other REF constant.
2576 assert(func == VNFunc(GT_ARR_LENGTH)); // Only function we can apply to a REF constant!
2577 return VNWithExc(VNForVoid(), VNExcSetSingleton(VNForFunc(TYP_REF, VNF_NullPtrExc, VNForNull())));
2580 // We will assert below
2583 noway_assert(!"Unhandled operation in EvalFuncForConstantArgs");
2587 bool ValueNumStore::SelectIsBeingEvaluatedRecursively(ValueNum map, ValueNum ind)
2589 for (unsigned i = 0; i < m_fixedPointMapSels.Size(); i++)
2591 VNDefFunc2Arg& elem = m_fixedPointMapSels.GetRef(i);
2592 assert(elem.m_func == VNF_MapSelect);
2593 if (elem.m_arg0 == map && elem.m_arg1 == ind)
2602 bool ValueNumStore::FixedPointMapSelsTopHasValue(ValueNum map, ValueNum index)
2604 if (m_fixedPointMapSels.Size() == 0)
2608 VNDefFunc2Arg& top = m_fixedPointMapSels.TopRef();
2609 return top.m_func == VNF_MapSelect && top.m_arg0 == map && top.m_arg1 == index;
2613 // Given an integer constant value number return its value as an int.
2615 int ValueNumStore::GetConstantInt32(ValueNum argVN)
2617 assert(IsVNConstant(argVN));
2618 var_types argVNtyp = TypeOfVN(argVN);
2625 result = ConstantValue<int>(argVN);
2627 #ifndef _TARGET_64BIT_
2630 result = (int)ConstantValue<size_t>(argVN);
2639 // Given an integer constant value number return its value as an INT64.
2641 INT64 ValueNumStore::GetConstantInt64(ValueNum argVN)
2643 assert(IsVNConstant(argVN));
2644 var_types argVNtyp = TypeOfVN(argVN);
2651 result = (INT64)ConstantValue<int>(argVN);
2654 result = ConstantValue<INT64>(argVN);
2658 result = (INT64)ConstantValue<size_t>(argVN);
2666 // Given a double constant value number return its value as a double.
2668 double ValueNumStore::GetConstantDouble(ValueNum argVN)
2670 assert(IsVNConstant(argVN));
2671 assert(TypeOfVN(argVN) == TYP_DOUBLE);
2673 return ConstantValue<double>(argVN);
2676 // Given a float constant value number return its value as a float.
2678 float ValueNumStore::GetConstantSingle(ValueNum argVN)
2680 assert(IsVNConstant(argVN));
2681 assert(TypeOfVN(argVN) == TYP_FLOAT);
2683 return ConstantValue<float>(argVN);
2686 // Compute the proper value number when the VNFunc has all constant arguments
2687 // This essentially performs constant folding at value numbering time
2689 ValueNum ValueNumStore::EvalFuncForConstantArgs(var_types typ, VNFunc func, ValueNum arg0VN, ValueNum arg1VN)
2691 assert(CanEvalForConstantArgs(func));
2692 assert(IsVNConstant(arg0VN) && IsVNConstant(arg1VN));
2693 assert(!VNHasExc(arg0VN) && !VNHasExc(arg1VN)); // Otherwise, would not be constant.
2695 // if our func is the VNF_Cast operation we handle it first
2696 if (func == VNF_Cast)
2698 return EvalCastForConstantArgs(typ, func, arg0VN, arg1VN);
2701 var_types arg0VNtyp = TypeOfVN(arg0VN);
2702 var_types arg1VNtyp = TypeOfVN(arg1VN);
2704 // When both arguments are floating point types
2705 // We defer to the EvalFuncForConstantFPArgs()
2706 if (varTypeIsFloating(arg0VNtyp) && varTypeIsFloating(arg1VNtyp))
2708 return EvalFuncForConstantFPArgs(typ, func, arg0VN, arg1VN);
2711 // after this we shouldn't have to deal with floating point types for arg0VN or arg1VN
2712 assert(!varTypeIsFloating(arg0VNtyp));
2713 assert(!varTypeIsFloating(arg1VNtyp));
2715 // Stack-normalize the result type.
2716 if (varTypeIsSmall(typ))
2721 ValueNum result; // left uninitialized, we are required to initialize it on all paths below.
2723 // Are both args of the same type?
2724 if (arg0VNtyp == arg1VNtyp)
2726 if (arg0VNtyp == TYP_INT)
2728 int arg0Val = ConstantValue<int>(arg0VN);
2729 int arg1Val = ConstantValue<int>(arg1VN);
2731 if (VNFuncIsComparison(func))
2733 assert(typ == TYP_INT);
2734 result = VNForIntCon(EvalComparison(func, arg0Val, arg1Val));
2738 assert(typ == TYP_INT);
2739 int resultVal = EvalOp<int>(func, arg0Val, arg1Val);
2740 // Bin op on a handle results in a handle.
2741 ValueNum handleVN = IsVNHandle(arg0VN) ? arg0VN : IsVNHandle(arg1VN) ? arg1VN : NoVN;
2742 if (handleVN != NoVN)
2744 result = VNForHandle(ssize_t(resultVal), GetHandleFlags(handleVN)); // Use VN for Handle
2748 result = VNForIntCon(resultVal);
2752 else if (arg0VNtyp == TYP_LONG)
2754 INT64 arg0Val = ConstantValue<INT64>(arg0VN);
2755 INT64 arg1Val = ConstantValue<INT64>(arg1VN);
2757 if (VNFuncIsComparison(func))
2759 assert(typ == TYP_INT);
2760 result = VNForIntCon(EvalComparison(func, arg0Val, arg1Val));
2764 assert(typ == TYP_LONG);
2765 INT64 resultVal = EvalOp<INT64>(func, arg0Val, arg1Val);
2766 ValueNum handleVN = IsVNHandle(arg0VN) ? arg0VN : IsVNHandle(arg1VN) ? arg1VN : NoVN;
2768 if (handleVN != NoVN)
2770 result = VNForHandle(ssize_t(resultVal), GetHandleFlags(handleVN)); // Use VN for Handle
2774 result = VNForLongCon(resultVal);
2778 else // both args are TYP_REF or both args are TYP_BYREF
2780 INT64 arg0Val = ConstantValue<size_t>(arg0VN); // We represent ref/byref constants as size_t's.
2781 INT64 arg1Val = ConstantValue<size_t>(arg1VN); // Also we consider null to be zero.
2783 if (VNFuncIsComparison(func))
2785 assert(typ == TYP_INT);
2786 result = VNForIntCon(EvalComparison(func, arg0Val, arg1Val));
2788 else if (typ == TYP_INT) // We could see GT_OR of a constant ByRef and Null
2790 int resultVal = (int)EvalOp<INT64>(func, arg0Val, arg1Val);
2791 result = VNForIntCon(resultVal);
2793 else // We could see GT_OR of a constant ByRef and Null
2795 assert((typ == TYP_BYREF) || (typ == TYP_LONG));
2796 INT64 resultVal = EvalOp<INT64>(func, arg0Val, arg1Val);
2797 result = VNForByrefCon(resultVal);
2801 else // We have args of different types
2803 // We represent ref/byref constants as size_t's.
2804 // Also we consider null to be zero.
2806 INT64 arg0Val = GetConstantInt64(arg0VN);
2807 INT64 arg1Val = GetConstantInt64(arg1VN);
2809 if (VNFuncIsComparison(func))
2811 assert(typ == TYP_INT);
2812 result = VNForIntCon(EvalComparison(func, arg0Val, arg1Val));
2814 else if (typ == TYP_INT) // We could see GT_OR of an int and constant ByRef or Null
2816 int resultVal = (int)EvalOp<INT64>(func, arg0Val, arg1Val);
2817 result = VNForIntCon(resultVal);
2821 assert(typ != TYP_INT);
2822 INT64 resultVal = EvalOp<INT64>(func, arg0Val, arg1Val);
2827 result = VNForByrefCon(resultVal);
2830 result = VNForLongCon(resultVal);
2833 assert(resultVal == 0); // Only valid REF constant
2834 result = VNForNull();
2845 // Compute the proper value number when the VNFunc has all constant floating-point arguments
2846 // This essentially must perform constant folding at value numbering time
2848 ValueNum ValueNumStore::EvalFuncForConstantFPArgs(var_types typ, VNFunc func, ValueNum arg0VN, ValueNum arg1VN)
2850 assert(CanEvalForConstantArgs(func));
2851 assert(IsVNConstant(arg0VN) && IsVNConstant(arg1VN));
2853 // We expect both argument types to be floating-point types
2854 var_types arg0VNtyp = TypeOfVN(arg0VN);
2855 var_types arg1VNtyp = TypeOfVN(arg1VN);
2857 assert(varTypeIsFloating(arg0VNtyp));
2858 assert(varTypeIsFloating(arg1VNtyp));
2860 // We also expect both arguments to be of the same floating-point type
2861 assert(arg0VNtyp == arg1VNtyp);
2863 ValueNum result; // left uninitialized, we are required to initialize it on all paths below.
2865 if (VNFuncIsComparison(func))
2867 assert(genActualType(typ) == TYP_INT);
2869 if (arg0VNtyp == TYP_FLOAT)
2871 result = VNForIntCon(EvalComparison<float>(func, GetConstantSingle(arg0VN), GetConstantSingle(arg1VN)));
2875 assert(arg0VNtyp == TYP_DOUBLE);
2876 result = VNForIntCon(EvalComparison<double>(func, GetConstantDouble(arg0VN), GetConstantDouble(arg1VN)));
2881 // We expect the return type to be the same as the argument type
2882 assert(varTypeIsFloating(typ));
2883 assert(arg0VNtyp == typ);
2885 if (typ == TYP_FLOAT)
2887 float floatResultVal = EvalOp<float>(func, GetConstantSingle(arg0VN), GetConstantSingle(arg1VN));
2888 result = VNForFloatCon(floatResultVal);
2892 assert(typ == TYP_DOUBLE);
2894 double doubleResultVal = EvalOp<double>(func, GetConstantDouble(arg0VN), GetConstantDouble(arg1VN));
2895 result = VNForDoubleCon(doubleResultVal);
2902 // Compute the proper value number for a VNF_Cast with constant arguments
2903 // This essentially must perform constant folding at value numbering time
2905 ValueNum ValueNumStore::EvalCastForConstantArgs(var_types typ, VNFunc func, ValueNum arg0VN, ValueNum arg1VN)
2907 assert(func == VNF_Cast);
2908 assert(IsVNConstant(arg0VN) && IsVNConstant(arg1VN));
2910 // Stack-normalize the result type.
2911 if (varTypeIsSmall(typ))
2916 var_types arg0VNtyp = TypeOfVN(arg0VN);
2917 var_types arg1VNtyp = TypeOfVN(arg1VN);
2919 // arg1VN is really the gtCastType that we are casting to
2920 assert(arg1VNtyp == TYP_INT);
2921 int arg1Val = ConstantValue<int>(arg1VN);
2922 assert(arg1Val >= 0);
2924 if (IsVNHandle(arg0VN))
2926 // We don't allow handles to be cast to random var_types.
2927 assert(typ == TYP_I_IMPL);
2930 // We previously encoded the castToType operation using vnForCastOper()
2932 bool srcIsUnsigned = ((arg1Val & INT32(VCA_UnsignedSrc)) != 0);
2933 var_types castToType = var_types(arg1Val >> INT32(VCA_BitCount));
2935 var_types castFromType = arg0VNtyp;
2937 switch (castFromType) // GT_CAST source type
2939 #ifndef _TARGET_64BIT_
2945 int arg0Val = GetConstantInt32(arg0VN);
2950 assert(typ == TYP_INT);
2951 return VNForIntCon(INT8(arg0Val));
2954 assert(typ == TYP_INT);
2955 return VNForIntCon(UINT8(arg0Val));
2957 assert(typ == TYP_INT);
2958 return VNForIntCon(INT16(arg0Val));
2960 assert(typ == TYP_INT);
2961 return VNForIntCon(UINT16(arg0Val));
2964 assert(typ == TYP_INT);
2968 assert(!IsVNHandle(arg0VN));
2969 #ifdef _TARGET_64BIT_
2970 if (typ == TYP_LONG)
2974 return VNForLongCon(INT64(unsigned(arg0Val)));
2978 return VNForLongCon(INT64(arg0Val));
2983 assert(typ == TYP_BYREF);
2986 return VNForByrefCon(INT64(unsigned(arg0Val)));
2990 return VNForByrefCon(INT64(arg0Val));
2993 #else // TARGET_32BIT
2995 return VNForLongCon(INT64(unsigned(arg0Val)));
2997 return VNForLongCon(INT64(arg0Val));
3000 assert(typ == TYP_BYREF);
3001 return VNForByrefCon((INT64)arg0Val);
3003 assert(typ == TYP_FLOAT);
3006 return VNForFloatCon(float(unsigned(arg0Val)));
3010 return VNForFloatCon(float(arg0Val));
3013 assert(typ == TYP_DOUBLE);
3016 return VNForDoubleCon(double(unsigned(arg0Val)));
3020 return VNForDoubleCon(double(arg0Val));
3028 #ifdef _TARGET_64BIT_
3033 INT64 arg0Val = GetConstantInt64(arg0VN);
3038 assert(typ == TYP_INT);
3039 return VNForIntCon(INT8(arg0Val));
3042 assert(typ == TYP_INT);
3043 return VNForIntCon(UINT8(arg0Val));
3045 assert(typ == TYP_INT);
3046 return VNForIntCon(INT16(arg0Val));
3048 assert(typ == TYP_INT);
3049 return VNForIntCon(UINT16(arg0Val));
3051 assert(typ == TYP_INT);
3052 return VNForIntCon(INT32(arg0Val));
3054 assert(typ == TYP_INT);
3055 return VNForIntCon(UINT32(arg0Val));
3058 assert(typ == TYP_LONG);
3061 assert(typ == TYP_BYREF);
3062 return VNForByrefCon((INT64)arg0Val);
3064 assert(typ == TYP_FLOAT);
3067 return VNForFloatCon(FloatingPointUtils::convertUInt64ToFloat(UINT64(arg0Val)));
3071 return VNForFloatCon(float(arg0Val));
3074 assert(typ == TYP_DOUBLE);
3077 return VNForDoubleCon(FloatingPointUtils::convertUInt64ToDouble(UINT64(arg0Val)));
3081 return VNForDoubleCon(double(arg0Val));
3089 float arg0Val = GetConstantSingle(arg0VN);
3094 assert(typ == TYP_INT);
3095 return VNForIntCon(INT8(arg0Val));
3098 assert(typ == TYP_INT);
3099 return VNForIntCon(UINT8(arg0Val));
3101 assert(typ == TYP_INT);
3102 return VNForIntCon(INT16(arg0Val));
3104 assert(typ == TYP_INT);
3105 return VNForIntCon(UINT16(arg0Val));
3107 assert(typ == TYP_INT);
3108 return VNForIntCon(INT32(arg0Val));
3110 assert(typ == TYP_INT);
3111 return VNForIntCon(UINT32(arg0Val));
3113 assert(typ == TYP_LONG);
3114 return VNForLongCon(INT64(arg0Val));
3116 assert(typ == TYP_LONG);
3117 return VNForLongCon(UINT64(arg0Val));
3119 assert(typ == TYP_FLOAT);
3120 return VNForFloatCon(arg0Val);
3122 assert(typ == TYP_DOUBLE);
3123 return VNForDoubleCon(double(arg0Val));
3130 double arg0Val = GetConstantDouble(arg0VN);
3135 assert(typ == TYP_INT);
3136 return VNForIntCon(INT8(arg0Val));
3139 assert(typ == TYP_INT);
3140 return VNForIntCon(UINT8(arg0Val));
3142 assert(typ == TYP_INT);
3143 return VNForIntCon(INT16(arg0Val));
3145 assert(typ == TYP_INT);
3146 return VNForIntCon(UINT16(arg0Val));
3148 assert(typ == TYP_INT);
3149 return VNForIntCon(INT32(arg0Val));
3151 assert(typ == TYP_INT);
3152 return VNForIntCon(UINT32(arg0Val));
3154 assert(typ == TYP_LONG);
3155 return VNForLongCon(INT64(arg0Val));
3157 assert(typ == TYP_LONG);
3158 return VNForLongCon(UINT64(arg0Val));
3160 assert(typ == TYP_FLOAT);
3161 return VNForFloatCon(float(arg0Val));
3163 assert(typ == TYP_DOUBLE);
3164 return VNForDoubleCon(arg0Val);
3174 //-----------------------------------------------------------------------------------
3175 // CanEvalForConstantArgs: - Given a VNFunc value return true when we can perform
3176 // compile-time constant folding for the operation.
3179 // vnf - The VNFunc that we are inquiring about
3182 // - Returns true if we can always compute a constant result
3183 // when given all constant args.
3185 // Notes: - When this method returns true, the logic to compute the
3186 // compile-time result must also be added to EvalOP,
3187 // EvalOpspecialized or EvalComparison
3189 bool ValueNumStore::CanEvalForConstantArgs(VNFunc vnf)
3191 if (vnf < VNF_Boundary)
3193 genTreeOps oper = genTreeOps(vnf);
3197 // Only return true for the node kinds that have code that supports
3198 // them in EvalOP, EvalOpspecialized or EvalComparison
3234 // We can evaluate these.
3238 // We can not evaluate these.
3244 // some VNF_ that we can evaluate
3255 // We can evaluate these.
3259 // We can not evaluate these.
3265 //----------------------------------------------------------------------------------------
3266 // VNEvalShouldFold - Returns true if we should perform the folding operation.
3267 // It returns false if we don't want to fold the expression,
3268 // because it will always throw an exception.
3271 // typ - The type of the resulting ValueNum produced by 'func'
3272 // func - Any binary VNFunc
3273 // arg0VN - The ValueNum of the first argument to 'func'
3274 // arg1VN - The ValueNum of the second argument to 'func'
3276 // Return Value: - Returns true if we should perform a folding operation.
3278 bool ValueNumStore::VNEvalShouldFold(var_types typ, VNFunc func, ValueNum arg0VN, ValueNum arg1VN)
3280 bool shouldFold = true;
3282 // We have some arithmetic operations that will always throw
3283 // an exception given particular constant argument(s).
3284 // (i.e. integer division by zero)
3286 // We will avoid performing any constant folding on them
3287 // since they won't actually produce any result.
3288 // Instead they always will throw an exception.
3290 if (func < VNF_Boundary)
3292 genTreeOps oper = genTreeOps(func);
3294 // Floating point operations do not throw exceptions
3296 if (!varTypeIsFloating(typ))
3298 // Is this an integer divide/modulo that will always throw an exception?
3300 if ((oper == GT_DIV) || (oper == GT_UDIV) || (oper == GT_MOD) || (oper == GT_UMOD))
3302 if ((TypeOfVN(arg0VN) != typ) || (TypeOfVN(arg1VN) != typ))
3304 // Just in case we have mismatched types
3309 bool isUnsigned = (oper == GT_UDIV) || (oper == GT_UMOD);
3310 if (typ == TYP_LONG)
3312 INT64 kArg0 = ConstantValue<INT64>(arg0VN);
3313 INT64 kArg1 = ConstantValue<INT64>(arg1VN);
3315 if (IsIntZero(kArg1))
3317 // Don't fold, we have a divide by zero
3320 else if (!isUnsigned || IsOverflowIntDiv(kArg0, kArg1))
3322 // Don't fold, we have a divide of INT64_MIN/-1
3326 else if (typ == TYP_INT)
3328 int kArg0 = ConstantValue<int>(arg0VN);
3329 int kArg1 = ConstantValue<int>(arg1VN);
3331 if (IsIntZero(kArg1))
3333 // Don't fold, we have a divide by zero
3336 else if (!isUnsigned && IsOverflowIntDiv(kArg0, kArg1))
3338 // Don't fold, we have a divide of INT32_MIN/-1
3342 else // strange value for 'typ'
3344 assert(!"unexpected 'typ' in VNForFunc constant folding");
3351 else // (func > VNF_Boundary)
3354 // Add checks in the future if we support folding of VNF_ADD_OVF, etc...
3360 //----------------------------------------------------------------------------------------
3361 // EvalUsingMathIdentity
3362 // - Attempts to evaluate 'func' by using mathimatical identities
3363 // that can be applied to 'func'.
3366 // typ - The type of the resulting ValueNum produced by 'func'
3367 // func - Any binary VNFunc
3368 // arg0VN - The ValueNum of the first argument to 'func'
3369 // arg1VN - The ValueNum of the second argument to 'func'
3371 // Return Value: - When successful a ValueNum for the expression is returned.
3372 // When unsuccessful NoVN is returned.
3374 ValueNum ValueNumStore::EvalUsingMathIdentity(var_types typ, VNFunc func, ValueNum arg0VN, ValueNum arg1VN)
3376 ValueNum resultVN = NoVN; // set default result to unsuccessful
3378 if (typ == TYP_BYREF) // We don't want/need to optimize a zero byref
3380 return resultVN; // return the unsuccessful value
3383 // We have ways of evaluating some binary functions.
3384 if (func < VNF_Boundary)
3386 switch (genTreeOps(func))
3394 // This identity does not apply for floating point (when x == -0.0)
3396 if (!varTypeIsFloating(typ))
3398 ZeroVN = VNZeroForType(typ);
3399 if (VNIsEqual(arg0VN, ZeroVN))
3403 else if (VNIsEqual(arg1VN, ZeroVN))
3413 // This identity does not apply for floating point (when x == -0.0)
3415 if (!varTypeIsFloating(typ))
3417 ZeroVN = VNZeroForType(typ);
3418 if (VNIsEqual(arg1VN, ZeroVN))
3422 else if (VNIsEqual(arg0VN, arg1VN))
3430 // These identities do not apply for floating point
3432 if (!varTypeIsFloating(typ))
3436 ZeroVN = VNZeroForType(typ);
3437 if (arg0VN == ZeroVN)
3441 else if (arg1VN == ZeroVN)
3448 OneVN = VNOneForType(typ);
3449 if (arg0VN == OneVN)
3453 else if (arg1VN == OneVN)
3463 // This identity does not apply for floating point
3465 if (!varTypeIsFloating(typ))
3467 OneVN = VNOneForType(typ);
3468 if (arg1VN == OneVN)
3477 // (0 | x) == x, (0 ^ x) == x
3478 // (x | 0) == x, (x ^ 0) == x
3479 ZeroVN = VNZeroForType(typ);
3480 if (arg0VN == ZeroVN)
3484 else if (arg1VN == ZeroVN)
3493 ZeroVN = VNZeroForType(typ);
3494 if (arg0VN == ZeroVN)
3498 else if (arg1VN == ZeroVN)
3513 ZeroVN = VNZeroForType(typ);
3514 if (arg1VN == ZeroVN)
3522 if (arg0VN == ZeroVN)
3531 // (x == x) == true, (null == non-null) == false, (non-null == null) == false
3532 // (x <= x) == true, (null <= non-null) == false, (non-null <= null) == false
3533 // (x >= x) == true, (null >= non-null) == false, (non-null >= null) == false
3535 // This identity does not apply for floating point (when x == NaN)
3537 if (!varTypeIsFloating(typ))
3539 if (VNIsEqual(arg0VN, arg1VN))
3541 resultVN = VNOneForType(typ);
3543 if ((arg0VN == VNForNull()) && IsKnownNonNull(arg1VN))
3545 resultVN = VNZeroForType(typ);
3547 if (IsKnownNonNull(arg0VN) && (arg1VN == VNForNull()))
3549 resultVN = VNZeroForType(typ);
3557 // (x != x) == false, (null != non-null) == true, (non-null != null) == true
3558 // (x > x) == false, (null == non-null) == true, (non-null == null) == true
3559 // (x < x) == false, (null == non-null) == true, (non-null == null) == true
3561 // This identity does not apply for floating point (when x == NaN)
3563 if (!varTypeIsFloating(typ))
3565 if (VNIsEqual(arg0VN, arg1VN))
3567 resultVN = VNZeroForType(typ);
3569 if ((arg0VN == VNForNull()) && IsKnownNonNull(arg1VN))
3571 resultVN = VNOneForType(typ);
3573 if (IsKnownNonNull(arg0VN) && (arg1VN == VNForNull()))
3575 resultVN = VNOneForType(typ);
3584 else // must be a VNF_ function
3586 // These identities do not apply for floating point (when x == NaN)
3588 if (VNIsEqual(arg0VN, arg1VN))
3592 if ((func == VNF_LE_UN) || (func == VNF_GE_UN))
3594 resultVN = VNOneForType(typ);
3598 else if ((func == VNF_LT_UN) || (func == VNF_GT_UN))
3600 resultVN = VNZeroForType(typ);
3607 //------------------------------------------------------------------------
3608 // VNForExpr: Opaque value number that is equivalent to itself but unique
3609 // from all other value numbers.
3612 // block - BasicBlock where the expression that produces this value occurs.
3613 // May be nullptr to force conservative "could be anywhere" interpretation.
3614 // typ - Type of the expression in the IR
3617 // A new value number distinct from any previously generated, that compares as equal
3618 // to itself, but not any other value number, and is annotated with the given
3621 ValueNum ValueNumStore::VNForExpr(BasicBlock* block, var_types typ)
3623 BasicBlock::loopNumber loopNum;
3624 if (block == nullptr)
3626 loopNum = MAX_LOOP_NUM;
3630 loopNum = block->bbNatLoopNum;
3633 // We always allocate a new, unique VN in this call.
3634 // The 'typ' is used to partition the allocation of VNs into different chunks.
3635 Chunk* c = GetAllocChunk(typ, CEA_None, loopNum);
3636 unsigned offsetWithinChunk = c->AllocVN();
3637 ValueNum result = c->m_baseVN + offsetWithinChunk;
3641 ValueNum ValueNumStore::VNApplySelectors(ValueNumKind vnk,
3643 FieldSeqNode* fieldSeq,
3644 size_t* wbFinalStructSize)
3646 if (fieldSeq == nullptr)
3652 assert(fieldSeq != FieldSeqStore::NotAField());
3654 // Skip any "FirstElem" pseudo-fields or any "ConstantIndex" pseudo-fields
3655 if (fieldSeq->IsPseudoField())
3657 return VNApplySelectors(vnk, map, fieldSeq->m_next, wbFinalStructSize);
3660 // Otherwise, is a real field handle.
3661 CORINFO_FIELD_HANDLE fldHnd = fieldSeq->m_fieldHnd;
3662 CORINFO_CLASS_HANDLE structHnd = NO_CLASS_HANDLE;
3663 ValueNum fldHndVN = VNForHandle(ssize_t(fldHnd), GTF_ICON_FIELD_HDL);
3664 noway_assert(fldHnd != nullptr);
3665 CorInfoType fieldCit = m_pComp->info.compCompHnd->getFieldType(fldHnd, &structHnd);
3666 var_types fieldType = JITtype2varType(fieldCit);
3668 size_t structSize = 0;
3669 if (varTypeIsStruct(fieldType))
3671 structSize = m_pComp->info.compCompHnd->getClassSize(structHnd);
3672 // We do not normalize the type field accesses during importation unless they
3673 // are used in a call, return or assignment.
3674 if ((fieldType == TYP_STRUCT) && (structSize <= m_pComp->largestEnregisterableStructSize()))
3676 fieldType = m_pComp->impNormStructType(structHnd);
3679 if (wbFinalStructSize != nullptr)
3681 *wbFinalStructSize = structSize;
3685 if (m_pComp->verbose)
3687 printf(" VNApplySelectors:\n");
3688 const char* modName;
3689 const char* fldName = m_pComp->eeGetFieldName(fldHnd, &modName);
3690 printf(" VNForHandle(%s) is " FMT_VN ", fieldType is %s", fldName, fldHndVN, varTypeName(fieldType));
3691 if (varTypeIsStruct(fieldType))
3693 printf(", size = %d", structSize);
3699 if (fieldSeq->m_next != nullptr)
3701 ValueNum newMap = VNForMapSelect(vnk, fieldType, map, fldHndVN);
3702 return VNApplySelectors(vnk, newMap, fieldSeq->m_next, wbFinalStructSize);
3704 else // end of fieldSeq
3706 return VNForMapSelect(vnk, fieldType, map, fldHndVN);
3711 ValueNum ValueNumStore::VNApplySelectorsTypeCheck(ValueNum elem, var_types indType, size_t elemStructSize)
3713 var_types elemTyp = TypeOfVN(elem);
3715 // Check if the elemTyp is matching/compatible
3717 if (indType != elemTyp)
3719 // We are trying to read from an 'elem' of type 'elemType' using 'indType' read
3721 size_t elemTypSize = (elemTyp == TYP_STRUCT) ? elemStructSize : genTypeSize(elemTyp);
3722 size_t indTypeSize = genTypeSize(indType);
3724 if ((indType == TYP_REF) && (varTypeIsStruct(elemTyp)))
3726 // indType is TYP_REF and elemTyp is TYP_STRUCT
3728 // We have a pointer to a static that is a Boxed Struct
3732 else if (indTypeSize > elemTypSize)
3734 // Reading beyong the end of 'elem'
3736 // return a new unique value number
3737 elem = VNMakeNormalUnique(elem);
3739 JITDUMP(" *** Mismatched types in VNApplySelectorsTypeCheck (reading beyond the end)\n");
3741 else if (varTypeIsStruct(indType))
3743 // return a new unique value number
3744 elem = VNMakeNormalUnique(elem);
3746 JITDUMP(" *** Mismatched types in VNApplySelectorsTypeCheck (indType is TYP_STRUCT)\n");
3750 // We are trying to read an 'elem' of type 'elemType' using 'indType' read
3752 // insert a cast of elem to 'indType'
3753 elem = VNForCast(elem, indType, elemTyp);
3760 ValueNum ValueNumStore::VNApplySelectorsAssignTypeCoerce(ValueNum elem, var_types indType, BasicBlock* block)
3762 var_types elemTyp = TypeOfVN(elem);
3764 // Check if the elemTyp is matching/compatible
3766 if (indType != elemTyp)
3768 bool isConstant = IsVNConstant(elem);
3769 if (isConstant && (elemTyp == genActualType(indType)))
3771 // (i.e. We recorded a constant of TYP_INT for a TYP_BYTE field)
3775 // We are trying to write an 'elem' of type 'elemType' using 'indType' store
3777 if (varTypeIsStruct(indType))
3779 // return a new unique value number
3780 elem = VNMakeNormalUnique(elem);
3782 JITDUMP(" *** Mismatched types in VNApplySelectorsAssignTypeCoerce (indType is TYP_STRUCT)\n");
3786 // We are trying to write an 'elem' of type 'elemType' using 'indType' store
3788 // insert a cast of elem to 'indType'
3789 elem = VNForCast(elem, indType, elemTyp);
3791 JITDUMP(" Cast to %s inserted in VNApplySelectorsAssignTypeCoerce (elemTyp is %s)\n",
3792 varTypeName(indType), varTypeName(elemTyp));
3799 //------------------------------------------------------------------------
3800 // VNApplySelectorsAssign: Compute the value number corresponding to "map" but with
3801 // the element at "fieldSeq" updated to have type "elem"; this is the new memory
3802 // value for an assignment of value "elem" into the memory at location "fieldSeq"
3803 // that occurs in block "block" and has type "indType" (so long as the selectors
3804 // into that memory occupy disjoint locations, which is true for GcHeap).
3807 // vnk - Identifies whether to recurse to Conservative or Liberal value numbers
3808 // when recursing through phis
3809 // map - Value number for the field map before the assignment
3810 // elem - Value number for the value being stored (to the given field)
3811 // indType - Type of the indirection storing the value to the field
3812 // block - Block where the assignment occurs
3815 // The value number corresponding to memory after the assignment.
3817 ValueNum ValueNumStore::VNApplySelectorsAssign(
3818 ValueNumKind vnk, ValueNum map, FieldSeqNode* fieldSeq, ValueNum elem, var_types indType, BasicBlock* block)
3820 if (fieldSeq == nullptr)
3822 return VNApplySelectorsAssignTypeCoerce(elem, indType, block);
3826 assert(fieldSeq != FieldSeqStore::NotAField());
3828 // Skip any "FirstElem" pseudo-fields or any "ConstantIndex" pseudo-fields
3829 // These will occur, at least, in struct static expressions, for method table offsets.
3830 if (fieldSeq->IsPseudoField())
3832 return VNApplySelectorsAssign(vnk, map, fieldSeq->m_next, elem, indType, block);
3835 // Otherwise, fldHnd is a real field handle.
3836 CORINFO_FIELD_HANDLE fldHnd = fieldSeq->m_fieldHnd;
3837 ValueNum fldHndVN = VNForHandle(ssize_t(fldHnd), GTF_ICON_FIELD_HDL);
3838 noway_assert(fldHnd != nullptr);
3839 CorInfoType fieldCit = m_pComp->info.compCompHnd->getFieldType(fldHnd);
3840 var_types fieldType = JITtype2varType(fieldCit);
3843 if (fieldSeq->m_next)
3846 if (m_pComp->verbose)
3848 const char* modName;
3849 const char* fldName = m_pComp->eeGetFieldName(fldHnd, &modName);
3850 printf(" VNForHandle(%s) is " FMT_VN ", fieldType is %s\n", fldName, fldHndVN,
3851 varTypeName(fieldType));
3854 ValueNum fseqMap = VNForMapSelect(vnk, fieldType, map, fldHndVN);
3855 elemAfter = VNApplySelectorsAssign(vnk, fseqMap, fieldSeq->m_next, elem, indType, block);
3860 if (m_pComp->verbose)
3862 if (fieldSeq->m_next == nullptr)
3864 printf(" VNApplySelectorsAssign:\n");
3866 const char* modName;
3867 const char* fldName = m_pComp->eeGetFieldName(fldHnd, &modName);
3868 printf(" VNForHandle(%s) is " FMT_VN ", fieldType is %s\n", fldName, fldHndVN,
3869 varTypeName(fieldType));
3872 elemAfter = VNApplySelectorsAssignTypeCoerce(elem, indType, block);
3875 ValueNum newMap = VNForMapStore(fieldType, map, fldHndVN, elemAfter);
3880 ValueNumPair ValueNumStore::VNPairApplySelectors(ValueNumPair map, FieldSeqNode* fieldSeq, var_types indType)
3882 size_t structSize = 0;
3883 ValueNum liberalVN = VNApplySelectors(VNK_Liberal, map.GetLiberal(), fieldSeq, &structSize);
3884 liberalVN = VNApplySelectorsTypeCheck(liberalVN, indType, structSize);
3887 ValueNum conservVN = VNApplySelectors(VNK_Conservative, map.GetConservative(), fieldSeq, &structSize);
3888 conservVN = VNApplySelectorsTypeCheck(conservVN, indType, structSize);
3890 return ValueNumPair(liberalVN, conservVN);
3893 bool ValueNumStore::IsVNNotAField(ValueNum vn)
3895 return m_chunks.GetNoExpand(GetChunkNum(vn))->m_attribs == CEA_NotAField;
3898 ValueNum ValueNumStore::VNForFieldSeq(FieldSeqNode* fieldSeq)
3900 if (fieldSeq == nullptr)
3904 else if (fieldSeq == FieldSeqStore::NotAField())
3906 // We always allocate a new, unique VN in this call.
3907 Chunk* c = GetAllocChunk(TYP_REF, CEA_NotAField);
3908 unsigned offsetWithinChunk = c->AllocVN();
3909 ValueNum result = c->m_baseVN + offsetWithinChunk;
3914 ssize_t fieldHndVal = ssize_t(fieldSeq->m_fieldHnd);
3915 ValueNum fieldHndVN = VNForHandle(fieldHndVal, GTF_ICON_FIELD_HDL);
3916 ValueNum seqNextVN = VNForFieldSeq(fieldSeq->m_next);
3917 ValueNum fieldSeqVN = VNForFunc(TYP_REF, VNF_FieldSeq, fieldHndVN, seqNextVN);
3920 if (m_pComp->verbose)
3922 printf(" FieldSeq");
3923 vnDump(m_pComp, fieldSeqVN);
3924 printf(" is " FMT_VN "\n", fieldSeqVN);
3932 FieldSeqNode* ValueNumStore::FieldSeqVNToFieldSeq(ValueNum vn)
3934 if (vn == VNForNull())
3939 assert(IsVNFunc(vn));
3942 GetVNFunc(vn, &funcApp);
3943 if (funcApp.m_func == VNF_NotAField)
3945 return FieldSeqStore::NotAField();
3948 assert(funcApp.m_func == VNF_FieldSeq);
3949 const ssize_t fieldHndVal = ConstantValue<ssize_t>(funcApp.m_args[0]);
3950 FieldSeqNode* head =
3951 m_pComp->GetFieldSeqStore()->CreateSingleton(reinterpret_cast<CORINFO_FIELD_HANDLE>(fieldHndVal));
3952 FieldSeqNode* tail = FieldSeqVNToFieldSeq(funcApp.m_args[1]);
3953 return m_pComp->GetFieldSeqStore()->Append(head, tail);
3956 ValueNum ValueNumStore::FieldSeqVNAppend(ValueNum fsVN1, ValueNum fsVN2)
3958 if (fsVN1 == VNForNull())
3963 assert(IsVNFunc(fsVN1));
3966 GetVNFunc(fsVN1, &funcApp1);
3968 if ((funcApp1.m_func == VNF_NotAField) || IsVNNotAField(fsVN2))
3970 return VNForFieldSeq(FieldSeqStore::NotAField());
3973 assert(funcApp1.m_func == VNF_FieldSeq);
3974 ValueNum tailRes = FieldSeqVNAppend(funcApp1.m_args[1], fsVN2);
3975 ValueNum fieldSeqVN = VNForFunc(TYP_REF, VNF_FieldSeq, funcApp1.m_args[0], tailRes);
3978 if (m_pComp->verbose)
3980 printf(" fieldSeq " FMT_VN " is ", fieldSeqVN);
3981 vnDump(m_pComp, fieldSeqVN);
3989 ValueNum ValueNumStore::ExtendPtrVN(GenTree* opA, GenTree* opB)
3991 if (opB->OperGet() == GT_CNS_INT)
3993 FieldSeqNode* fldSeq = opB->gtIntCon.gtFieldSeq;
3994 if (fldSeq != nullptr)
3996 return ExtendPtrVN(opA, fldSeq);
4002 ValueNum ValueNumStore::ExtendPtrVN(GenTree* opA, FieldSeqNode* fldSeq)
4004 assert(fldSeq != nullptr);
4006 ValueNum res = NoVN;
4008 ValueNum opAvnWx = opA->gtVNPair.GetLiberal();
4009 assert(VNIsValid(opAvnWx));
4012 VNUnpackExc(opAvnWx, &opAvn, &opAvnx);
4013 assert(VNIsValid(opAvn) && VNIsValid(opAvnx));
4016 if (!GetVNFunc(opAvn, &funcApp))
4021 if (funcApp.m_func == VNF_PtrToLoc)
4024 // For PtrToLoc, lib == cons.
4025 VNFuncApp consFuncApp;
4026 assert(GetVNFunc(VNConservativeNormalValue(opA->gtVNPair), &consFuncApp) && consFuncApp.Equals(funcApp));
4028 ValueNum fldSeqVN = VNForFieldSeq(fldSeq);
4029 res = VNForFunc(TYP_BYREF, VNF_PtrToLoc, funcApp.m_args[0], FieldSeqVNAppend(funcApp.m_args[1], fldSeqVN));
4031 else if (funcApp.m_func == VNF_PtrToStatic)
4033 ValueNum fldSeqVN = VNForFieldSeq(fldSeq);
4034 res = VNForFunc(TYP_BYREF, VNF_PtrToStatic, FieldSeqVNAppend(funcApp.m_args[0], fldSeqVN));
4036 else if (funcApp.m_func == VNF_PtrToArrElem)
4038 ValueNum fldSeqVN = VNForFieldSeq(fldSeq);
4039 res = VNForFunc(TYP_BYREF, VNF_PtrToArrElem, funcApp.m_args[0], funcApp.m_args[1], funcApp.m_args[2],
4040 FieldSeqVNAppend(funcApp.m_args[3], fldSeqVN));
4044 res = VNWithExc(res, opAvnx);
4049 ValueNum Compiler::fgValueNumberArrIndexAssign(CORINFO_CLASS_HANDLE elemTypeEq,
4052 FieldSeqNode* fldSeq,
4056 bool invalidateArray = false;
4057 ValueNum elemTypeEqVN = vnStore->VNForHandle(ssize_t(elemTypeEq), GTF_ICON_CLASS_HDL);
4058 var_types arrElemType = DecodeElemType(elemTypeEq);
4059 ValueNum hAtArrType = vnStore->VNForMapSelect(VNK_Liberal, TYP_REF, fgCurMemoryVN[GcHeap], elemTypeEqVN);
4060 ValueNum hAtArrTypeAtArr = vnStore->VNForMapSelect(VNK_Liberal, TYP_REF, hAtArrType, arrVN);
4061 ValueNum hAtArrTypeAtArrAtInx = vnStore->VNForMapSelect(VNK_Liberal, arrElemType, hAtArrTypeAtArr, inxVN);
4063 ValueNum newValAtInx = ValueNumStore::NoVN;
4064 ValueNum newValAtArr = ValueNumStore::NoVN;
4065 ValueNum newValAtArrType = ValueNumStore::NoVN;
4067 if (fldSeq == FieldSeqStore::NotAField())
4069 // This doesn't represent a proper array access
4070 JITDUMP(" *** NotAField sequence encountered in fgValueNumberArrIndexAssign\n");
4072 // Store a new unique value for newValAtArrType
4073 newValAtArrType = vnStore->VNForExpr(compCurBB, TYP_REF);
4074 invalidateArray = true;
4078 // Note that this does the right thing if "fldSeq" is null -- returns last "rhs" argument.
4079 // This is the value that should be stored at "arr[inx]".
4081 vnStore->VNApplySelectorsAssign(VNK_Liberal, hAtArrTypeAtArrAtInx, fldSeq, rhsVN, indType, compCurBB);
4083 var_types arrElemFldType = arrElemType; // Uses arrElemType unless we has a non-null fldSeq
4084 if (vnStore->IsVNFunc(newValAtInx))
4087 vnStore->GetVNFunc(newValAtInx, &funcApp);
4088 if (funcApp.m_func == VNF_MapStore)
4090 arrElemFldType = vnStore->TypeOfVN(newValAtInx);
4094 if (indType != arrElemFldType)
4096 // Mismatched types: Store between different types (indType into array of arrElemFldType)
4099 JITDUMP(" *** Mismatched types in fgValueNumberArrIndexAssign\n");
4101 // Store a new unique value for newValAtArrType
4102 newValAtArrType = vnStore->VNForExpr(compCurBB, TYP_REF);
4103 invalidateArray = true;
4107 if (!invalidateArray)
4109 newValAtArr = vnStore->VNForMapStore(indType, hAtArrTypeAtArr, inxVN, newValAtInx);
4110 newValAtArrType = vnStore->VNForMapStore(TYP_REF, hAtArrType, arrVN, newValAtArr);
4116 printf(" hAtArrType " FMT_VN " is MapSelect(curGcHeap(" FMT_VN "), ", hAtArrType, fgCurMemoryVN[GcHeap]);
4118 if (arrElemType == TYP_STRUCT)
4120 printf("%s[]).\n", eeGetClassName(elemTypeEq));
4124 printf("%s[]).\n", varTypeName(arrElemType));
4126 printf(" hAtArrTypeAtArr " FMT_VN " is MapSelect(hAtArrType(" FMT_VN "), arr=" FMT_VN ")\n", hAtArrTypeAtArr,
4128 printf(" hAtArrTypeAtArrAtInx " FMT_VN " is MapSelect(hAtArrTypeAtArr(" FMT_VN "), inx=" FMT_VN "):%s\n",
4129 hAtArrTypeAtArrAtInx, hAtArrTypeAtArr, inxVN, varTypeName(arrElemType));
4131 if (!invalidateArray)
4133 printf(" newValAtInd " FMT_VN " is ", newValAtInx);
4134 vnStore->vnDump(this, newValAtInx);
4137 printf(" newValAtArr " FMT_VN " is ", newValAtArr);
4138 vnStore->vnDump(this, newValAtArr);
4142 printf(" newValAtArrType " FMT_VN " is ", newValAtArrType);
4143 vnStore->vnDump(this, newValAtArrType);
4148 return vnStore->VNForMapStore(TYP_REF, fgCurMemoryVN[GcHeap], elemTypeEqVN, newValAtArrType);
4151 ValueNum Compiler::fgValueNumberArrIndexVal(GenTree* tree, VNFuncApp* pFuncApp, ValueNum addrXvn)
4153 assert(vnStore->IsVNHandle(pFuncApp->m_args[0]));
4154 CORINFO_CLASS_HANDLE arrElemTypeEQ = CORINFO_CLASS_HANDLE(vnStore->ConstantValue<ssize_t>(pFuncApp->m_args[0]));
4155 ValueNum arrVN = pFuncApp->m_args[1];
4156 ValueNum inxVN = pFuncApp->m_args[2];
4157 FieldSeqNode* fldSeq = vnStore->FieldSeqVNToFieldSeq(pFuncApp->m_args[3]);
4158 return fgValueNumberArrIndexVal(tree, arrElemTypeEQ, arrVN, inxVN, addrXvn, fldSeq);
4161 ValueNum Compiler::fgValueNumberArrIndexVal(GenTree* tree,
4162 CORINFO_CLASS_HANDLE elemTypeEq,
4166 FieldSeqNode* fldSeq)
4168 assert(tree == nullptr || tree->OperIsIndir());
4170 // The VN inputs are required to be non-exceptional values.
4171 assert(arrVN == vnStore->VNNormalValue(arrVN));
4172 assert(inxVN == vnStore->VNNormalValue(inxVN));
4174 var_types elemTyp = DecodeElemType(elemTypeEq);
4175 var_types indType = (tree == nullptr) ? elemTyp : tree->TypeGet();
4176 ValueNum selectedElem;
4178 if (fldSeq == FieldSeqStore::NotAField())
4180 // This doesn't represent a proper array access
4181 JITDUMP(" *** NotAField sequence encountered in fgValueNumberArrIndexVal\n");
4183 // a new unique value number
4184 selectedElem = vnStore->VNForExpr(compCurBB, elemTyp);
4189 printf(" IND of PtrToArrElem is unique VN " FMT_VN ".\n", selectedElem);
4193 if (tree != nullptr)
4195 tree->gtVNPair.SetBoth(selectedElem);
4200 ValueNum elemTypeEqVN = vnStore->VNForHandle(ssize_t(elemTypeEq), GTF_ICON_CLASS_HDL);
4201 ValueNum hAtArrType = vnStore->VNForMapSelect(VNK_Liberal, TYP_REF, fgCurMemoryVN[GcHeap], elemTypeEqVN);
4202 ValueNum hAtArrTypeAtArr = vnStore->VNForMapSelect(VNK_Liberal, TYP_REF, hAtArrType, arrVN);
4203 ValueNum wholeElem = vnStore->VNForMapSelect(VNK_Liberal, elemTyp, hAtArrTypeAtArr, inxVN);
4208 printf(" hAtArrType " FMT_VN " is MapSelect(curGcHeap(" FMT_VN "), ", hAtArrType, fgCurMemoryVN[GcHeap]);
4209 if (elemTyp == TYP_STRUCT)
4211 printf("%s[]).\n", eeGetClassName(elemTypeEq));
4215 printf("%s[]).\n", varTypeName(elemTyp));
4218 printf(" hAtArrTypeAtArr " FMT_VN " is MapSelect(hAtArrType(" FMT_VN "), arr=" FMT_VN ").\n",
4219 hAtArrTypeAtArr, hAtArrType, arrVN);
4221 printf(" wholeElem " FMT_VN " is MapSelect(hAtArrTypeAtArr(" FMT_VN "), ind=" FMT_VN ").\n", wholeElem,
4222 hAtArrTypeAtArr, inxVN);
4226 selectedElem = wholeElem;
4227 size_t elemStructSize = 0;
4230 selectedElem = vnStore->VNApplySelectors(VNK_Liberal, wholeElem, fldSeq, &elemStructSize);
4231 elemTyp = vnStore->TypeOfVN(selectedElem);
4233 selectedElem = vnStore->VNApplySelectorsTypeCheck(selectedElem, indType, elemStructSize);
4234 selectedElem = vnStore->VNWithExc(selectedElem, excVN);
4237 if (verbose && (selectedElem != wholeElem))
4239 printf(" selectedElem is " FMT_VN " after applying selectors.\n", selectedElem);
4243 if (tree != nullptr)
4245 tree->gtVNPair.SetLiberal(selectedElem);
4247 // TODO-CQ: what to do here about exceptions? We don't have the array and ind conservative
4248 // values, so we don't have their exceptions. Maybe we should.
4249 tree->gtVNPair.SetConservative(vnStore->VNForExpr(compCurBB, tree->TypeGet()));
4253 return selectedElem;
4256 ValueNum Compiler::fgValueNumberByrefExposedLoad(var_types type, ValueNum pointerVN)
4258 ValueNum memoryVN = fgCurMemoryVN[ByrefExposed];
4259 // The memoization for VNFunc applications does not factor in the result type, so
4260 // VNF_ByrefExposedLoad takes the loaded type as an explicit parameter.
4261 ValueNum typeVN = vnStore->VNForIntCon(type);
4263 vnStore->VNForFunc(type, VNF_ByrefExposedLoad, typeVN, vnStore->VNNormalValue(pointerVN), memoryVN);
4268 var_types ValueNumStore::TypeOfVN(ValueNum vn)
4275 Chunk* c = m_chunks.GetNoExpand(GetChunkNum(vn));
4279 //------------------------------------------------------------------------
4280 // LoopOfVN: If the given value number is an opaque one associated with a particular
4281 // expression in the IR, give the loop number where the expression occurs; otherwise,
4282 // returns MAX_LOOP_NUM.
4285 // vn - Value number to query
4288 // The correspondingblock's bbNatLoopNum, which may be BasicBlock::NOT_IN_LOOP.
4289 // Returns MAX_LOOP_NUM if this VN is not an opaque value number associated with
4290 // a particular expression/location in the IR.
4292 BasicBlock::loopNumber ValueNumStore::LoopOfVN(ValueNum vn)
4296 return MAX_LOOP_NUM;
4299 Chunk* c = m_chunks.GetNoExpand(GetChunkNum(vn));
4300 return c->m_loopNum;
4303 bool ValueNumStore::IsVNConstant(ValueNum vn)
4309 Chunk* c = m_chunks.GetNoExpand(GetChunkNum(vn));
4310 if (c->m_attribs == CEA_Const)
4312 return vn != VNForVoid(); // Void is not a "real" constant -- in the sense that it represents no value.
4316 return c->m_attribs == CEA_Handle;
4320 bool ValueNumStore::IsVNInt32Constant(ValueNum vn)
4322 if (!IsVNConstant(vn))
4327 return TypeOfVN(vn) == TYP_INT;
4330 unsigned ValueNumStore::GetHandleFlags(ValueNum vn)
4332 assert(IsVNHandle(vn));
4333 Chunk* c = m_chunks.GetNoExpand(GetChunkNum(vn));
4334 unsigned offset = ChunkOffset(vn);
4335 VNHandle* handle = &reinterpret_cast<VNHandle*>(c->m_defs)[offset];
4336 return handle->m_flags;
4339 bool ValueNumStore::IsVNHandle(ValueNum vn)
4346 Chunk* c = m_chunks.GetNoExpand(GetChunkNum(vn));
4347 return c->m_attribs == CEA_Handle;
4350 bool ValueNumStore::IsVNConstantBound(ValueNum vn)
4352 // Do we have "var < 100"?
4359 if (!GetVNFunc(vn, &funcAttr))
4363 if (funcAttr.m_func != (VNFunc)GT_LE && funcAttr.m_func != (VNFunc)GT_GE && funcAttr.m_func != (VNFunc)GT_LT &&
4364 funcAttr.m_func != (VNFunc)GT_GT)
4369 return IsVNInt32Constant(funcAttr.m_args[0]) != IsVNInt32Constant(funcAttr.m_args[1]);
4372 void ValueNumStore::GetConstantBoundInfo(ValueNum vn, ConstantBoundInfo* info)
4374 assert(IsVNConstantBound(vn));
4377 // Do we have var < 100?
4379 GetVNFunc(vn, &funcAttr);
4381 bool isOp1Const = IsVNInt32Constant(funcAttr.m_args[1]);
4385 info->cmpOper = funcAttr.m_func;
4386 info->cmpOpVN = funcAttr.m_args[0];
4387 info->constVal = GetConstantInt32(funcAttr.m_args[1]);
4391 info->cmpOper = GenTree::SwapRelop((genTreeOps)funcAttr.m_func);
4392 info->cmpOpVN = funcAttr.m_args[1];
4393 info->constVal = GetConstantInt32(funcAttr.m_args[0]);
4397 //------------------------------------------------------------------------
4398 // IsVNArrLenUnsignedBound: Checks if the specified vn represents an expression
4399 // such as "(uint)i < (uint)len" that implies that the index is valid
4400 // (0 <= i && i < a.len).
4403 // vn - Value number to query
4404 // info - Pointer to an UnsignedCompareCheckedBoundInfo object to return information about
4405 // the expression. Not populated if the vn expression isn't suitable (e.g. i <= len).
4406 // This enables optCreateJTrueBoundAssertion to immediatly create an OAK_NO_THROW
4407 // assertion instead of the OAK_EQUAL/NOT_EQUAL assertions created by signed compares
4408 // (IsVNCompareCheckedBound, IsVNCompareCheckedBoundArith) that require further processing.
4410 bool ValueNumStore::IsVNUnsignedCompareCheckedBound(ValueNum vn, UnsignedCompareCheckedBoundInfo* info)
4414 if (GetVNFunc(vn, &funcApp))
4416 if ((funcApp.m_func == VNF_LT_UN) || (funcApp.m_func == VNF_GE_UN))
4418 // We only care about "(uint)i < (uint)len" and its negation "(uint)i >= (uint)len"
4419 if (IsVNCheckedBound(funcApp.m_args[1]))
4421 info->vnIdx = funcApp.m_args[0];
4422 info->cmpOper = funcApp.m_func;
4423 info->vnBound = funcApp.m_args[1];
4427 else if ((funcApp.m_func == VNF_GT_UN) || (funcApp.m_func == VNF_LE_UN))
4429 // We only care about "(uint)a.len > (uint)i" and its negation "(uint)a.len <= (uint)i"
4430 if (IsVNCheckedBound(funcApp.m_args[0]))
4432 info->vnIdx = funcApp.m_args[1];
4433 // Let's keep a consistent operand order - it's always i < len, never len > i
4434 info->cmpOper = (funcApp.m_func == VNF_GT_UN) ? VNF_LT_UN : VNF_GE_UN;
4435 info->vnBound = funcApp.m_args[0];
4444 bool ValueNumStore::IsVNCompareCheckedBound(ValueNum vn)
4446 // Do we have "var < len"?
4453 if (!GetVNFunc(vn, &funcAttr))
4457 if (funcAttr.m_func != (VNFunc)GT_LE && funcAttr.m_func != (VNFunc)GT_GE && funcAttr.m_func != (VNFunc)GT_LT &&
4458 funcAttr.m_func != (VNFunc)GT_GT)
4462 if (!IsVNCheckedBound(funcAttr.m_args[0]) && !IsVNCheckedBound(funcAttr.m_args[1]))
4470 void ValueNumStore::GetCompareCheckedBound(ValueNum vn, CompareCheckedBoundArithInfo* info)
4472 assert(IsVNCompareCheckedBound(vn));
4474 // Do we have var < a.len?
4476 GetVNFunc(vn, &funcAttr);
4478 bool isOp1CheckedBound = IsVNCheckedBound(funcAttr.m_args[1]);
4479 if (isOp1CheckedBound)
4481 info->cmpOper = funcAttr.m_func;
4482 info->cmpOp = funcAttr.m_args[0];
4483 info->vnBound = funcAttr.m_args[1];
4487 info->cmpOper = GenTree::SwapRelop((genTreeOps)funcAttr.m_func);
4488 info->cmpOp = funcAttr.m_args[1];
4489 info->vnBound = funcAttr.m_args[0];
4493 bool ValueNumStore::IsVNCheckedBoundArith(ValueNum vn)
4495 // Do we have "a.len +or- var"
4503 return GetVNFunc(vn, &funcAttr) && // vn is a func.
4504 (funcAttr.m_func == (VNFunc)GT_ADD || funcAttr.m_func == (VNFunc)GT_SUB) && // the func is +/-
4505 (IsVNCheckedBound(funcAttr.m_args[0]) || IsVNCheckedBound(funcAttr.m_args[1])); // either op1 or op2 is a.len
4508 void ValueNumStore::GetCheckedBoundArithInfo(ValueNum vn, CompareCheckedBoundArithInfo* info)
4510 // Do we have a.len +/- var?
4511 assert(IsVNCheckedBoundArith(vn));
4512 VNFuncApp funcArith;
4513 GetVNFunc(vn, &funcArith);
4515 bool isOp1CheckedBound = IsVNCheckedBound(funcArith.m_args[1]);
4516 if (isOp1CheckedBound)
4518 info->arrOper = funcArith.m_func;
4519 info->arrOp = funcArith.m_args[0];
4520 info->vnBound = funcArith.m_args[1];
4524 info->arrOper = funcArith.m_func;
4525 info->arrOp = funcArith.m_args[1];
4526 info->vnBound = funcArith.m_args[0];
4530 bool ValueNumStore::IsVNCompareCheckedBoundArith(ValueNum vn)
4532 // Do we have: "var < a.len - var"
4539 if (!GetVNFunc(vn, &funcAttr))
4544 // Suitable comparator.
4545 if (funcAttr.m_func != (VNFunc)GT_LE && funcAttr.m_func != (VNFunc)GT_GE && funcAttr.m_func != (VNFunc)GT_LT &&
4546 funcAttr.m_func != (VNFunc)GT_GT)
4551 // Either the op0 or op1 is arr len arithmetic.
4552 if (!IsVNCheckedBoundArith(funcAttr.m_args[0]) && !IsVNCheckedBoundArith(funcAttr.m_args[1]))
4560 void ValueNumStore::GetCompareCheckedBoundArithInfo(ValueNum vn, CompareCheckedBoundArithInfo* info)
4562 assert(IsVNCompareCheckedBoundArith(vn));
4565 GetVNFunc(vn, &funcAttr);
4567 // Check whether op0 or op1 is checked bound arithmetic.
4568 bool isOp1CheckedBoundArith = IsVNCheckedBoundArith(funcAttr.m_args[1]);
4569 if (isOp1CheckedBoundArith)
4571 info->cmpOper = funcAttr.m_func;
4572 info->cmpOp = funcAttr.m_args[0];
4573 GetCheckedBoundArithInfo(funcAttr.m_args[1], info);
4577 info->cmpOper = GenTree::SwapRelop((genTreeOps)funcAttr.m_func);
4578 info->cmpOp = funcAttr.m_args[1];
4579 GetCheckedBoundArithInfo(funcAttr.m_args[0], info);
4583 ValueNum ValueNumStore::GetArrForLenVn(ValueNum vn)
4591 if (GetVNFunc(vn, &funcAttr) && funcAttr.m_func == (VNFunc)GT_ARR_LENGTH)
4593 return funcAttr.m_args[0];
4598 bool ValueNumStore::IsVNNewArr(ValueNum vn, VNFuncApp* funcApp)
4604 bool result = false;
4605 if (GetVNFunc(vn, funcApp))
4607 result = (funcApp->m_func == VNF_JitNewArr) || (funcApp->m_func == VNF_JitReadyToRunNewArr);
4612 int ValueNumStore::GetNewArrSize(ValueNum vn)
4615 if (IsVNNewArr(vn, &funcApp))
4617 ValueNum arg1VN = funcApp.m_args[1];
4618 if (IsVNConstant(arg1VN) && TypeOfVN(arg1VN) == TYP_INT)
4620 return ConstantValue<int>(arg1VN);
4626 bool ValueNumStore::IsVNArrLen(ValueNum vn)
4633 return (GetVNFunc(vn, &funcAttr) && funcAttr.m_func == (VNFunc)GT_ARR_LENGTH);
4636 bool ValueNumStore::IsVNCheckedBound(ValueNum vn)
4639 if (m_checkedBoundVNs.TryGetValue(vn, &dummy))
4641 // This VN appeared as the conservative VN of the length argument of some
4642 // GT_ARR_BOUND node.
4647 // Even if we haven't seen this VN in a bounds check, if it is an array length
4648 // VN then consider it a checked bound VN. This facilitates better bounds check
4649 // removal by ensuring that compares against array lengths get put in the
4650 // optCseCheckedBoundMap; such an array length might get CSEd with one that was
4651 // directly used in a bounds check, and having the map entry will let us update
4652 // the compare's VN so that OptimizeRangeChecks can recognize such compares.
4659 void ValueNumStore::SetVNIsCheckedBound(ValueNum vn)
4661 // This is meant to flag VNs for lengths that aren't known at compile time, so we can
4662 // form and propagate assertions about them. Ensure that callers filter out constant
4663 // VNs since they're not what we're looking to flag, and assertion prop can reason
4664 // directly about constants.
4665 assert(!IsVNConstant(vn));
4666 m_checkedBoundVNs.AddOrUpdate(vn, true);
4669 ValueNum ValueNumStore::EvalMathFuncUnary(var_types typ, CorInfoIntrinsics gtMathFN, ValueNum arg0VN)
4671 assert(arg0VN == VNNormalValue(arg0VN));
4673 // If the math intrinsic is not implemented by target-specific instructions, such as implemented
4674 // by user calls, then don't do constant folding on it. This minimizes precision loss.
4676 if (IsVNConstant(arg0VN) && m_pComp->IsTargetIntrinsic(gtMathFN))
4678 assert(varTypeIsFloating(TypeOfVN(arg0VN)));
4680 if (typ == TYP_DOUBLE)
4682 // Both operand and its result must be of the same floating point type.
4683 assert(typ == TypeOfVN(arg0VN));
4684 double arg0Val = GetConstantDouble(arg0VN);
4689 case CORINFO_INTRINSIC_Sin:
4692 case CORINFO_INTRINSIC_Cos:
4695 case CORINFO_INTRINSIC_Sqrt:
4696 res = sqrt(arg0Val);
4698 case CORINFO_INTRINSIC_Abs:
4699 res = fabs(arg0Val);
4701 case CORINFO_INTRINSIC_Ceiling:
4702 res = ceil(arg0Val);
4704 case CORINFO_INTRINSIC_Floor:
4705 res = floor(arg0Val);
4707 case CORINFO_INTRINSIC_Round:
4708 res = FloatingPointUtils::round(arg0Val);
4711 unreached(); // the above are the only math intrinsics at the time of this writing.
4714 return VNForDoubleCon(res);
4716 else if (typ == TYP_FLOAT)
4718 // Both operand and its result must be of the same floating point type.
4719 assert(typ == TypeOfVN(arg0VN));
4720 float arg0Val = GetConstantSingle(arg0VN);
4725 case CORINFO_INTRINSIC_Sin:
4726 res = sinf(arg0Val);
4728 case CORINFO_INTRINSIC_Cos:
4729 res = cosf(arg0Val);
4731 case CORINFO_INTRINSIC_Sqrt:
4732 res = sqrtf(arg0Val);
4734 case CORINFO_INTRINSIC_Abs:
4735 res = fabsf(arg0Val);
4737 case CORINFO_INTRINSIC_Ceiling:
4738 res = ceilf(arg0Val);
4740 case CORINFO_INTRINSIC_Floor:
4741 res = floorf(arg0Val);
4743 case CORINFO_INTRINSIC_Round:
4744 res = FloatingPointUtils::round(arg0Val);
4747 unreached(); // the above are the only math intrinsics at the time of this writing.
4750 return VNForFloatCon(res);
4754 // CORINFO_INTRINSIC_Round is currently the only intrinsic that takes floating-point arguments
4755 // and that returns a non floating-point result.
4757 assert(typ == TYP_INT);
4758 assert(gtMathFN == CORINFO_INTRINSIC_Round);
4762 switch (TypeOfVN(arg0VN))
4766 double arg0Val = GetConstantDouble(arg0VN);
4767 res = int(FloatingPointUtils::round(arg0Val));
4772 float arg0Val = GetConstantSingle(arg0VN);
4773 res = int(FloatingPointUtils::round(arg0Val));
4780 return VNForIntCon(res);
4785 assert(typ == TYP_DOUBLE || typ == TYP_FLOAT || (typ == TYP_INT && gtMathFN == CORINFO_INTRINSIC_Round));
4787 VNFunc vnf = VNF_Boundary;
4790 case CORINFO_INTRINSIC_Sin:
4793 case CORINFO_INTRINSIC_Cos:
4796 case CORINFO_INTRINSIC_Cbrt:
4799 case CORINFO_INTRINSIC_Sqrt:
4802 case CORINFO_INTRINSIC_Abs:
4805 case CORINFO_INTRINSIC_Round:
4806 if (typ == TYP_DOUBLE)
4808 vnf = VNF_RoundDouble;
4810 else if (typ == TYP_FLOAT)
4812 vnf = VNF_RoundFloat;
4814 else if (typ == TYP_INT)
4820 noway_assert(!"Invalid INTRINSIC_Round");
4823 case CORINFO_INTRINSIC_Cosh:
4826 case CORINFO_INTRINSIC_Sinh:
4829 case CORINFO_INTRINSIC_Tan:
4832 case CORINFO_INTRINSIC_Tanh:
4835 case CORINFO_INTRINSIC_Asin:
4838 case CORINFO_INTRINSIC_Asinh:
4841 case CORINFO_INTRINSIC_Acos:
4844 case CORINFO_INTRINSIC_Acosh:
4847 case CORINFO_INTRINSIC_Atan:
4850 case CORINFO_INTRINSIC_Atanh:
4853 case CORINFO_INTRINSIC_Log10:
4856 case CORINFO_INTRINSIC_Exp:
4859 case CORINFO_INTRINSIC_Ceiling:
4862 case CORINFO_INTRINSIC_Floor:
4866 unreached(); // the above are the only math intrinsics at the time of this writing.
4869 return VNForFunc(typ, vnf, arg0VN);
4873 ValueNum ValueNumStore::EvalMathFuncBinary(var_types typ, CorInfoIntrinsics gtMathFN, ValueNum arg0VN, ValueNum arg1VN)
4875 assert(varTypeIsFloating(typ));
4876 assert(arg0VN == VNNormalValue(arg0VN));
4877 assert(arg1VN == VNNormalValue(arg1VN));
4879 VNFunc vnf = VNF_Boundary;
4881 // Currently, none of the binary math intrinsic are implemented by target-specific instructions.
4882 // To minimize precision loss, do not do constant folding on them.
4886 case CORINFO_INTRINSIC_Atan2:
4890 case CORINFO_INTRINSIC_Pow:
4895 unreached(); // the above are the only binary math intrinsics at the time of this writing.
4898 return VNForFunc(typ, vnf, arg0VN, arg1VN);
4901 bool ValueNumStore::IsVNFunc(ValueNum vn)
4907 Chunk* c = m_chunks.GetNoExpand(GetChunkNum(vn));
4908 switch (c->m_attribs)
4922 bool ValueNumStore::GetVNFunc(ValueNum vn, VNFuncApp* funcApp)
4929 Chunk* c = m_chunks.GetNoExpand(GetChunkNum(vn));
4930 unsigned offset = ChunkOffset(vn);
4931 assert(offset < c->m_numUsed);
4932 switch (c->m_attribs)
4936 VNDefFunc4Arg* farg4 = &reinterpret_cast<VNDefFunc4Arg*>(c->m_defs)[offset];
4937 funcApp->m_func = farg4->m_func;
4938 funcApp->m_arity = 4;
4939 funcApp->m_args[0] = farg4->m_arg0;
4940 funcApp->m_args[1] = farg4->m_arg1;
4941 funcApp->m_args[2] = farg4->m_arg2;
4942 funcApp->m_args[3] = farg4->m_arg3;
4947 VNDefFunc3Arg* farg3 = &reinterpret_cast<VNDefFunc3Arg*>(c->m_defs)[offset];
4948 funcApp->m_func = farg3->m_func;
4949 funcApp->m_arity = 3;
4950 funcApp->m_args[0] = farg3->m_arg0;
4951 funcApp->m_args[1] = farg3->m_arg1;
4952 funcApp->m_args[2] = farg3->m_arg2;
4957 VNDefFunc2Arg* farg2 = &reinterpret_cast<VNDefFunc2Arg*>(c->m_defs)[offset];
4958 funcApp->m_func = farg2->m_func;
4959 funcApp->m_arity = 2;
4960 funcApp->m_args[0] = farg2->m_arg0;
4961 funcApp->m_args[1] = farg2->m_arg1;
4966 VNDefFunc1Arg* farg1 = &reinterpret_cast<VNDefFunc1Arg*>(c->m_defs)[offset];
4967 funcApp->m_func = farg1->m_func;
4968 funcApp->m_arity = 1;
4969 funcApp->m_args[0] = farg1->m_arg0;
4974 VNDefFunc0Arg* farg0 = &reinterpret_cast<VNDefFunc0Arg*>(c->m_defs)[offset];
4975 funcApp->m_func = farg0->m_func;
4976 funcApp->m_arity = 0;
4981 funcApp->m_func = VNF_NotAField;
4982 funcApp->m_arity = 0;
4990 ValueNum ValueNumStore::VNForRefInAddr(ValueNum vn)
4992 var_types vnType = TypeOfVN(vn);
4993 if (vnType == TYP_REF)
4998 assert(vnType == TYP_BYREF);
5000 if (GetVNFunc(vn, &funcApp))
5002 assert(funcApp.m_arity == 2 && (funcApp.m_func == VNFunc(GT_ADD) || funcApp.m_func == VNFunc(GT_SUB)));
5003 var_types vnArg0Type = TypeOfVN(funcApp.m_args[0]);
5004 if (vnArg0Type == TYP_REF || vnArg0Type == TYP_BYREF)
5006 return VNForRefInAddr(funcApp.m_args[0]);
5010 assert(funcApp.m_func == VNFunc(GT_ADD) &&
5011 (TypeOfVN(funcApp.m_args[1]) == TYP_REF || TypeOfVN(funcApp.m_args[1]) == TYP_BYREF));
5012 return VNForRefInAddr(funcApp.m_args[1]);
5017 assert(IsVNConstant(vn));
5022 bool ValueNumStore::VNIsValid(ValueNum vn)
5024 ChunkNum cn = GetChunkNum(vn);
5025 if (cn >= m_chunks.Size())
5030 Chunk* c = m_chunks.GetNoExpand(cn);
5031 return ChunkOffset(vn) < c->m_numUsed;
5036 void ValueNumStore::vnDump(Compiler* comp, ValueNum vn, bool isPtr)
5043 else if (IsVNHandle(vn))
5045 ssize_t val = ConstantValue<ssize_t>(vn);
5046 printf("Hnd const: 0x%p", dspPtr(val));
5048 else if (IsVNConstant(vn))
5050 var_types vnt = TypeOfVN(vn);
5061 int val = ConstantValue<int>(vn);
5064 printf("PtrCns[%p]", dspPtr(val));
5069 if ((val > -1000) && (val < 1000))
5071 printf(" %ld", val);
5075 printf(" 0x%X", val);
5083 INT64 val = ConstantValue<INT64>(vn);
5086 printf("LngPtrCns: 0x%p", dspPtr(val));
5091 if ((val > -1000) && (val < 1000))
5093 printf(" %ld", val);
5095 else if ((val & 0xFFFFFFFF00000000LL) == 0)
5097 printf(" 0x%X", val);
5101 printf(" 0x%llx", val);
5107 printf("FltCns[%f]", ConstantValue<float>(vn));
5110 printf("DblCns[%f]", ConstantValue<double>(vn));
5113 if (vn == VNForNull())
5117 else if (vn == VNForVoid())
5123 assert(vn == VNForZeroMap());
5136 #endif // FEATURE_SIMD
5137 printf("structVal");
5140 // These should be unreached.
5145 else if (IsVNCompareCheckedBound(vn))
5147 CompareCheckedBoundArithInfo info;
5148 GetCompareCheckedBound(vn, &info);
5151 else if (IsVNCompareCheckedBoundArith(vn))
5153 CompareCheckedBoundArithInfo info;
5154 GetCompareCheckedBoundArithInfo(vn, &info);
5157 else if (IsVNFunc(vn))
5160 GetVNFunc(vn, &funcApp);
5161 // A few special cases...
5162 switch (funcApp.m_func)
5165 vnDumpFieldSeq(comp, &funcApp, true);
5168 vnDumpMapSelect(comp, &funcApp);
5171 vnDumpMapStore(comp, &funcApp);
5173 case VNF_ValWithExc:
5174 vnDumpValWithExc(comp, &funcApp);
5177 printf("%s(", VNFuncName(funcApp.m_func));
5178 for (unsigned i = 0; i < funcApp.m_arity; i++)
5185 printf(FMT_VN, funcApp.m_args[i]);
5187 #if FEATURE_VN_DUMP_FUNC_ARGS
5189 vnDump(comp, funcApp.m_args[i]);
5197 // Otherwise, just a VN with no structure; print just the VN.
5203 // Requires "valWithExc" to be a value with an exeception set VNFuncApp.
5204 // Prints a representation of the exeception set on standard out.
5205 void ValueNumStore::vnDumpValWithExc(Compiler* comp, VNFuncApp* valWithExc)
5207 assert(valWithExc->m_func == VNF_ValWithExc); // Precondition.
5209 ValueNum normVN = valWithExc->m_args[0]; // First arg is the VN from normal execution
5210 ValueNum excVN = valWithExc->m_args[1]; // Second arg is the set of possible exceptions
5212 assert(IsVNFunc(excVN));
5214 GetVNFunc(excVN, &excSeq);
5217 printf(FMT_VN, normVN);
5218 vnDump(comp, normVN);
5220 printf(FMT_VN, excVN);
5221 vnDumpExcSeq(comp, &excSeq, true);
5224 // Requires "excSeq" to be a ExcSetCons sequence.
5225 // Prints a representation of the set of exceptions on standard out.
5226 void ValueNumStore::vnDumpExcSeq(Compiler* comp, VNFuncApp* excSeq, bool isHead)
5228 assert(excSeq->m_func == VNF_ExcSetCons); // Precondition.
5230 ValueNum curExc = excSeq->m_args[0];
5231 bool hasTail = (excSeq->m_args[1] != VNForEmptyExcSet());
5233 if (isHead && hasTail)
5238 vnDump(comp, curExc);
5243 assert(IsVNFunc(excSeq->m_args[1]));
5245 GetVNFunc(excSeq->m_args[1], &tail);
5246 vnDumpExcSeq(comp, &tail, false);
5249 if (isHead && hasTail)
5255 void ValueNumStore::vnDumpFieldSeq(Compiler* comp, VNFuncApp* fieldSeq, bool isHead)
5257 assert(fieldSeq->m_func == VNF_FieldSeq); // Precondition.
5258 // First arg is the field handle VN.
5259 assert(IsVNConstant(fieldSeq->m_args[0]) && TypeOfVN(fieldSeq->m_args[0]) == TYP_I_IMPL);
5260 ssize_t fieldHndVal = ConstantValue<ssize_t>(fieldSeq->m_args[0]);
5261 bool hasTail = (fieldSeq->m_args[1] != VNForNull());
5263 if (isHead && hasTail)
5268 CORINFO_FIELD_HANDLE fldHnd = CORINFO_FIELD_HANDLE(fieldHndVal);
5269 if (fldHnd == FieldSeqStore::FirstElemPseudoField)
5271 printf("#FirstElem");
5273 else if (fldHnd == FieldSeqStore::ConstantIndexPseudoField)
5275 printf("#ConstantIndex");
5279 const char* modName;
5280 const char* fldName = m_pComp->eeGetFieldName(fldHnd, &modName);
5281 printf("%s", fldName);
5287 assert(IsVNFunc(fieldSeq->m_args[1]));
5289 GetVNFunc(fieldSeq->m_args[1], &tail);
5290 vnDumpFieldSeq(comp, &tail, false);
5293 if (isHead && hasTail)
5299 void ValueNumStore::vnDumpMapSelect(Compiler* comp, VNFuncApp* mapSelect)
5301 assert(mapSelect->m_func == VNF_MapSelect); // Precondition.
5303 ValueNum mapVN = mapSelect->m_args[0]; // First arg is the map id
5304 ValueNum indexVN = mapSelect->m_args[1]; // Second arg is the index
5306 comp->vnPrint(mapVN, 0);
5308 comp->vnPrint(indexVN, 0);
5312 void ValueNumStore::vnDumpMapStore(Compiler* comp, VNFuncApp* mapStore)
5314 assert(mapStore->m_func == VNF_MapStore); // Precondition.
5316 ValueNum mapVN = mapStore->m_args[0]; // First arg is the map id
5317 ValueNum indexVN = mapStore->m_args[1]; // Second arg is the index
5318 ValueNum newValVN = mapStore->m_args[2]; // Third arg is the new value
5320 comp->vnPrint(mapVN, 0);
5322 comp->vnPrint(indexVN, 0);
5324 comp->vnPrint(newValVN, 0);
5329 // Static fields, methods.
5330 static UINT8 vnfOpAttribs[VNF_COUNT];
5331 static genTreeOps genTreeOpsIllegalAsVNFunc[] = {GT_IND, // When we do heap memory.
5332 GT_NULLCHECK, GT_QMARK, GT_COLON, GT_LOCKADD, GT_XADD, GT_XCHG,
5333 GT_CMPXCHG, GT_LCLHEAP, GT_BOX,
5335 // These need special semantics:
5336 GT_COMMA, // == second argument (but with exception(s) from first).
5337 GT_ADDR, GT_ARR_BOUNDS_CHECK,
5338 GT_OBJ, // May reference heap memory.
5339 GT_BLK, // May reference heap memory.
5340 GT_INIT_VAL, // Not strictly a pass-through.
5342 // These control-flow operations need no values.
5343 GT_JTRUE, GT_RETURN, GT_SWITCH, GT_RETFILT, GT_CKFINITE};
5345 UINT8* ValueNumStore::s_vnfOpAttribs = nullptr;
5347 void ValueNumStore::InitValueNumStoreStatics()
5349 // Make sure we've gotten constants right...
5350 assert(unsigned(VNFOA_Arity) == (1 << VNFOA_ArityShift));
5351 assert(unsigned(VNFOA_AfterArity) == (unsigned(VNFOA_Arity) << VNFOA_ArityBits));
5353 s_vnfOpAttribs = &vnfOpAttribs[0];
5354 for (unsigned i = 0; i < GT_COUNT; i++)
5356 genTreeOps gtOper = static_cast<genTreeOps>(i);
5358 if (GenTree::OperIsUnary(gtOper))
5362 else if (GenTree::OperIsBinary(gtOper))
5366 // Since GT_ARR_BOUNDS_CHECK is not currently GTK_BINOP
5367 else if (gtOper == GT_ARR_BOUNDS_CHECK)
5371 vnfOpAttribs[i] |= (arity << VNFOA_ArityShift);
5373 if (GenTree::OperIsCommutative(gtOper))
5375 vnfOpAttribs[i] |= VNFOA_Commutative;
5379 // I so wish this wasn't the best way to do this...
5381 int vnfNum = VNF_Boundary + 1; // The macro definition below will update this after using it.
5383 #define ValueNumFuncDef(vnf, arity, commute, knownNonNull, sharedStatic) \
5385 vnfOpAttribs[vnfNum] |= VNFOA_Commutative; \
5387 vnfOpAttribs[vnfNum] |= VNFOA_KnownNonNull; \
5389 vnfOpAttribs[vnfNum] |= VNFOA_SharedStatic; \
5390 vnfOpAttribs[vnfNum] |= (arity << VNFOA_ArityShift); \
5393 #include "valuenumfuncs.h"
5394 #undef ValueNumFuncDef
5396 for (unsigned i = 0; i < _countof(genTreeOpsIllegalAsVNFunc); i++)
5398 vnfOpAttribs[genTreeOpsIllegalAsVNFunc[i]] |= VNFOA_IllegalGenTreeOp;
5403 // Define the name array.
5404 #define ValueNumFuncDef(vnf, arity, commute, knownNonNull, sharedStatic) #vnf,
5406 const char* ValueNumStore::VNFuncNameArr[] = {
5407 #include "valuenumfuncs.h"
5408 #undef ValueNumFuncDef
5412 const char* ValueNumStore::VNFuncName(VNFunc vnf)
5414 if (vnf < VNF_Boundary)
5416 return GenTree::OpName(genTreeOps(vnf));
5420 return VNFuncNameArr[vnf - (VNF_Boundary + 1)];
5424 static const char* s_reservedNameArr[] = {
5425 "$VN.Recursive", // -2 RecursiveVN
5426 "$VN.No", // -1 NoVN
5427 "$VN.Null", // 0 VNForNull()
5428 "$VN.ZeroMap", // 1 VNForZeroMap()
5429 "$VN.ReadOnlyHeap", // 2 VNForROH()
5430 "$VN.Void", // 3 VNForVoid()
5431 "$VN.EmptyExcSet" // 4 VNForEmptyExcSet()
5434 // Returns the string name of "vn" when it is a reserved value number, nullptr otherwise
5436 const char* ValueNumStore::reservedName(ValueNum vn)
5438 int val = vn - ValueNumStore::RecursiveVN; // Add two, making 'RecursiveVN' equal to zero
5439 int max = ValueNumStore::SRC_NumSpecialRefConsts - ValueNumStore::RecursiveVN;
5441 if ((val >= 0) && (val < max))
5443 return s_reservedNameArr[val];
5450 // Returns true if "vn" is a reserved value number
5453 bool ValueNumStore::isReservedVN(ValueNum vn)
5455 int val = vn - ValueNumStore::RecursiveVN; // Adding two, making 'RecursiveVN' equal to zero
5456 int max = ValueNumStore::SRC_NumSpecialRefConsts - ValueNumStore::RecursiveVN;
5458 if ((val >= 0) && (val < max))
5466 void ValueNumStore::RunTests(Compiler* comp)
5468 VNFunc VNF_Add = GenTreeOpToVNFunc(GT_ADD);
5470 ValueNumStore* vns = new (comp->getAllocatorDebugOnly()) ValueNumStore(comp, comp->getAllocatorDebugOnly());
5471 ValueNum vnNull = VNForNull();
5472 assert(vnNull == VNForNull());
5474 ValueNum vnFor1 = vns->VNForIntCon(1);
5475 assert(vnFor1 == vns->VNForIntCon(1));
5476 assert(vns->TypeOfVN(vnFor1) == TYP_INT);
5477 assert(vns->IsVNConstant(vnFor1));
5478 assert(vns->ConstantValue<int>(vnFor1) == 1);
5480 ValueNum vnFor100 = vns->VNForIntCon(100);
5481 assert(vnFor100 == vns->VNForIntCon(100));
5482 assert(vnFor100 != vnFor1);
5483 assert(vns->TypeOfVN(vnFor100) == TYP_INT);
5484 assert(vns->IsVNConstant(vnFor100));
5485 assert(vns->ConstantValue<int>(vnFor100) == 100);
5487 ValueNum vnFor1F = vns->VNForFloatCon(1.0f);
5488 assert(vnFor1F == vns->VNForFloatCon(1.0f));
5489 assert(vnFor1F != vnFor1 && vnFor1F != vnFor100);
5490 assert(vns->TypeOfVN(vnFor1F) == TYP_FLOAT);
5491 assert(vns->IsVNConstant(vnFor1F));
5492 assert(vns->ConstantValue<float>(vnFor1F) == 1.0f);
5494 ValueNum vnFor1D = vns->VNForDoubleCon(1.0);
5495 assert(vnFor1D == vns->VNForDoubleCon(1.0));
5496 assert(vnFor1D != vnFor1F && vnFor1D != vnFor1 && vnFor1D != vnFor100);
5497 assert(vns->TypeOfVN(vnFor1D) == TYP_DOUBLE);
5498 assert(vns->IsVNConstant(vnFor1D));
5499 assert(vns->ConstantValue<double>(vnFor1D) == 1.0);
5501 ValueNum vnRandom1 = vns->VNForExpr(nullptr, TYP_INT);
5502 ValueNum vnForFunc2a = vns->VNForFunc(TYP_INT, VNF_Add, vnFor1, vnRandom1);
5503 assert(vnForFunc2a == vns->VNForFunc(TYP_INT, VNF_Add, vnFor1, vnRandom1));
5504 assert(vnForFunc2a != vnFor1D && vnForFunc2a != vnFor1F && vnForFunc2a != vnFor1 && vnForFunc2a != vnRandom1);
5505 assert(vns->TypeOfVN(vnForFunc2a) == TYP_INT);
5506 assert(!vns->IsVNConstant(vnForFunc2a));
5507 assert(vns->IsVNFunc(vnForFunc2a));
5509 bool b = vns->GetVNFunc(vnForFunc2a, &fa2a);
5511 assert(fa2a.m_func == VNF_Add && fa2a.m_arity == 2 && fa2a.m_args[0] == vnFor1 && fa2a.m_args[1] == vnRandom1);
5513 ValueNum vnForFunc2b = vns->VNForFunc(TYP_INT, VNF_Add, vnFor1, vnFor100);
5514 assert(vnForFunc2b == vns->VNForFunc(TYP_INT, VNF_Add, vnFor1, vnFor100));
5515 assert(vnForFunc2b != vnFor1D && vnForFunc2b != vnFor1F && vnForFunc2b != vnFor1 && vnForFunc2b != vnFor100);
5516 assert(vns->TypeOfVN(vnForFunc2b) == TYP_INT);
5517 assert(vns->IsVNConstant(vnForFunc2b));
5518 assert(vns->ConstantValue<int>(vnForFunc2b) == 101);
5520 // printf("Did ValueNumStore::RunTests.\n");
5524 typedef JitExpandArrayStack<BasicBlock*> BlockStack;
5526 // This represents the "to do" state of the value number computation.
5527 struct ValueNumberState
5529 // These two stacks collectively represent the set of blocks that are candidates for
5530 // processing, because at least one predecessor has been processed. Blocks on "m_toDoAllPredsDone"
5531 // have had *all* predecessors processed, and thus are candidates for some extra optimizations.
5532 // Blocks on "m_toDoNotAllPredsDone" have at least one predecessor that has not been processed.
5533 // Blocks are initially on "m_toDoNotAllPredsDone" may be moved to "m_toDoAllPredsDone" when their last
5534 // unprocessed predecessor is processed, thus maintaining the invariants.
5535 BlockStack m_toDoAllPredsDone;
5536 BlockStack m_toDoNotAllPredsDone;
5540 // TBD: This should really be a bitset...
5542 // first bit indicates completed,
5543 // second bit indicates that it's been pushed on all-done stack,
5544 // third bit indicates that it's been pushed on not-all-done stack.
5550 BVB_onAllDone = 0x2,
5551 BVB_onNotAllDone = 0x4,
5554 bool GetVisitBit(unsigned bbNum, BlockVisitBits bvb)
5556 return (m_visited[bbNum] & bvb) != 0;
5558 void SetVisitBit(unsigned bbNum, BlockVisitBits bvb)
5560 m_visited[bbNum] |= bvb;
5563 ValueNumberState(Compiler* comp)
5564 : m_toDoAllPredsDone(comp->getAllocator(), /*minSize*/ 4)
5565 , m_toDoNotAllPredsDone(comp->getAllocator(), /*minSize*/ 4)
5567 , m_visited(new (comp, CMK_ValueNumber) BYTE[comp->fgBBNumMax + 1]())
5571 BasicBlock* ChooseFromNotAllPredsDone()
5573 assert(m_toDoAllPredsDone.Size() == 0);
5574 // If we have no blocks with all preds done, then (ideally, if all cycles have been captured by loops)
5575 // we must have at least one block within a loop. We want to do the loops first. Doing a loop entry block
5576 // should break the cycle, making the rest of the body of the loop (unless there's a nested loop) doable by the
5577 // all-preds-done rule. If several loop entry blocks are available, at least one should have all non-loop preds
5578 // done -- we choose that.
5579 for (unsigned i = 0; i < m_toDoNotAllPredsDone.Size(); i++)
5581 BasicBlock* cand = m_toDoNotAllPredsDone.Get(i);
5583 // Skip any already-completed blocks (a block may have all its preds finished, get added to the
5584 // all-preds-done todo set, and get processed there). Do this by moving the last one down, to
5585 // keep the array compact.
5586 while (GetVisitBit(cand->bbNum, BVB_complete))
5588 if (i + 1 < m_toDoNotAllPredsDone.Size())
5590 cand = m_toDoNotAllPredsDone.Pop();
5591 m_toDoNotAllPredsDone.Set(i, cand);
5595 // "cand" is the last element; delete it.
5596 (void)m_toDoNotAllPredsDone.Pop();
5600 // We may have run out of non-complete candidates above. If so, we're done.
5601 if (i == m_toDoNotAllPredsDone.Size())
5606 // See if "cand" is a loop entry.
5608 if (m_comp->optBlockIsLoopEntry(cand, &lnum))
5610 // "lnum" is the innermost loop of which "cand" is the entry; find the outermost.
5611 unsigned lnumPar = m_comp->optLoopTable[lnum].lpParent;
5612 while (lnumPar != BasicBlock::NOT_IN_LOOP)
5614 if (m_comp->optLoopTable[lnumPar].lpEntry == cand)
5622 lnumPar = m_comp->optLoopTable[lnumPar].lpParent;
5625 bool allNonLoopPredsDone = true;
5626 for (flowList* pred = m_comp->BlockPredsWithEH(cand); pred != nullptr; pred = pred->flNext)
5628 BasicBlock* predBlock = pred->flBlock;
5629 if (!m_comp->optLoopTable[lnum].lpContains(predBlock))
5631 if (!GetVisitBit(predBlock->bbNum, BVB_complete))
5633 allNonLoopPredsDone = false;
5637 if (allNonLoopPredsDone)
5644 // If we didn't find a loop entry block with all non-loop preds done above, then return a random member (if
5646 if (m_toDoNotAllPredsDone.Size() == 0)
5652 return m_toDoNotAllPredsDone.Pop();
5656 // Debugging output that is too detailed for a normal JIT dump...
5657 #define DEBUG_VN_VISIT 0
5659 // Record that "blk" has been visited, and add any unvisited successors of "blk" to the appropriate todo set.
5660 void FinishVisit(BasicBlock* blk)
5662 #ifdef DEBUG_VN_VISIT
5663 JITDUMP("finish(" FMT_BB ").\n", blk->bbNum);
5664 #endif // DEBUG_VN_VISIT
5666 SetVisitBit(blk->bbNum, BVB_complete);
5668 for (BasicBlock* succ : blk->GetAllSuccs(m_comp))
5670 #ifdef DEBUG_VN_VISIT
5671 JITDUMP(" Succ(" FMT_BB ").\n", succ->bbNum);
5672 #endif // DEBUG_VN_VISIT
5674 if (GetVisitBit(succ->bbNum, BVB_complete))
5678 #ifdef DEBUG_VN_VISIT
5679 JITDUMP(" Not yet completed.\n");
5680 #endif // DEBUG_VN_VISIT
5682 bool allPredsVisited = true;
5683 for (flowList* pred = m_comp->BlockPredsWithEH(succ); pred != nullptr; pred = pred->flNext)
5685 BasicBlock* predBlock = pred->flBlock;
5686 if (!GetVisitBit(predBlock->bbNum, BVB_complete))
5688 allPredsVisited = false;
5693 if (allPredsVisited)
5695 #ifdef DEBUG_VN_VISIT
5696 JITDUMP(" All preds complete, adding to allDone.\n");
5697 #endif // DEBUG_VN_VISIT
5699 assert(!GetVisitBit(succ->bbNum, BVB_onAllDone)); // Only last completion of last succ should add to
5701 m_toDoAllPredsDone.Push(succ);
5702 SetVisitBit(succ->bbNum, BVB_onAllDone);
5706 #ifdef DEBUG_VN_VISIT
5707 JITDUMP(" Not all preds complete Adding to notallDone, if necessary...\n");
5708 #endif // DEBUG_VN_VISIT
5710 if (!GetVisitBit(succ->bbNum, BVB_onNotAllDone))
5712 #ifdef DEBUG_VN_VISIT
5713 JITDUMP(" Was necessary.\n");
5714 #endif // DEBUG_VN_VISIT
5715 m_toDoNotAllPredsDone.Push(succ);
5716 SetVisitBit(succ->bbNum, BVB_onNotAllDone);
5724 return m_toDoAllPredsDone.Size() > 0 || m_toDoNotAllPredsDone.Size() > 0;
5728 void Compiler::fgValueNumber()
5731 // This could be a JITDUMP, but some people find it convenient to set a breakpoint on the printf.
5734 printf("\n*************** In fgValueNumber()\n");
5738 // If we skipped SSA, skip VN as well.
5739 if (fgSsaPassesCompleted == 0)
5744 // Allocate the value number store.
5745 assert(fgVNPassesCompleted > 0 || vnStore == nullptr);
5746 if (fgVNPassesCompleted == 0)
5748 CompAllocator allocator(getAllocator(CMK_ValueNumber));
5749 vnStore = new (allocator) ValueNumStore(this, allocator);
5754 // Make sure the memory SSA names have no value numbers.
5755 for (unsigned i = 0; i < lvMemoryPerSsaData.GetCount(); i++)
5757 lvMemoryPerSsaData.GetSsaDefByIndex(i)->m_vnPair = noVnp;
5759 for (BasicBlock* blk = fgFirstBB; blk != nullptr; blk = blk->bbNext)
5761 // Now iterate over the block's statements, and their trees.
5762 for (GenTree* stmts = blk->FirstNonPhiDef(); stmts != nullptr; stmts = stmts->gtNext)
5764 assert(stmts->IsStatement());
5765 for (GenTree* tree = stmts->gtStmt.gtStmtList; tree; tree = tree->gtNext)
5767 tree->gtVNPair.SetBoth(ValueNumStore::NoVN);
5773 // Compute the side effects of loops.
5774 optComputeLoopSideEffects();
5776 // At the block level, we will use a modified worklist algorithm. We will have two
5777 // "todo" sets of unvisited blocks. Blocks (other than the entry block) are put in a
5778 // todo set only when some predecessor has been visited, so all blocks have at least one
5779 // predecessor visited. The distinction between the two sets is whether *all* predecessors have
5780 // already been visited. We visit such blocks preferentially if they exist, since phi definitions
5781 // in such blocks will have all arguments defined, enabling a simplification in the case that all
5782 // arguments to the phi have the same VN. If no such blocks exist, we pick a block with at least
5783 // one unvisited predecessor. In this case, we assign a new VN for phi definitions.
5785 // Start by giving incoming arguments value numbers.
5786 // Also give must-init vars a zero of their type.
5787 for (unsigned lclNum = 0; lclNum < lvaCount; lclNum++)
5789 if (!lvaInSsa(lclNum))
5794 LclVarDsc* varDsc = &lvaTable[lclNum];
5795 assert(varDsc->lvTracked);
5797 if (varDsc->lvIsParam)
5799 // We assume that code equivalent to this variable initialization loop
5800 // has been performed when doing SSA naming, so that all the variables we give
5801 // initial VNs to here have been given initial SSA definitions there.
5802 // SSA numbers always start from FIRST_SSA_NUM, and we give the value number to SSA name FIRST_SSA_NUM.
5803 // We use the VNF_InitVal(i) from here so we know that this value is loop-invariant
5805 ValueNum initVal = vnStore->VNForFunc(varDsc->TypeGet(), VNF_InitVal, vnStore->VNForIntCon(lclNum));
5806 LclSsaVarDsc* ssaDef = varDsc->GetPerSsaData(SsaConfig::FIRST_SSA_NUM);
5807 ssaDef->m_vnPair.SetBoth(initVal);
5808 ssaDef->m_defLoc.m_blk = fgFirstBB;
5810 else if (info.compInitMem || varDsc->lvMustInit ||
5811 VarSetOps::IsMember(this, fgFirstBB->bbLiveIn, varDsc->lvVarIndex))
5813 // The last clause covers the use-before-def variables (the ones that are live-in to the the first block),
5814 // these are variables that are read before being initialized (at least on some control flow paths)
5815 // if they are not must-init, then they get VNF_InitVal(i), as with the param case.)
5817 bool isZeroed = (info.compInitMem || varDsc->lvMustInit);
5818 ValueNum initVal = ValueNumStore::NoVN; // We must assign a new value to initVal
5819 var_types typ = varDsc->TypeGet();
5823 case TYP_LCLBLK: // The outgoing args area for arm and x64
5824 case TYP_BLK: // A blob of memory
5825 // TYP_BLK is used for the EHSlots LclVar on x86 (aka shadowSPslotsVar)
5826 // and for the lvaInlinedPInvokeFrameVar on x64, arm and x86
5827 // The stack associated with these LclVars are not zero initialized
5828 // thus we set 'initVN' to a new, unique VN.
5830 initVal = vnStore->VNForExpr(fgFirstBB);
5836 // LclVars of TYP_BYREF can be zero-inited.
5837 initVal = vnStore->VNForByrefCon(0);
5841 // Here we have uninitialized TYP_BYREF
5842 initVal = vnStore->VNForFunc(typ, VNF_InitVal, vnStore->VNForIntCon(lclNum));
5849 // By default we will zero init these LclVars
5850 initVal = vnStore->VNZeroForType(typ);
5854 initVal = vnStore->VNForFunc(typ, VNF_InitVal, vnStore->VNForIntCon(lclNum));
5859 bool isVarargParam = (lclNum == lvaVarargsBaseOfStkArgs || lclNum == lvaVarargsHandleArg);
5861 initVal = vnStore->VNForExpr(fgFirstBB); // a new, unique VN.
5863 assert(initVal != ValueNumStore::NoVN);
5865 LclSsaVarDsc* ssaDef = varDsc->GetPerSsaData(SsaConfig::FIRST_SSA_NUM);
5866 ssaDef->m_vnPair.SetBoth(initVal);
5867 ssaDef->m_defLoc.m_blk = fgFirstBB;
5870 // Give memory an initial value number (about which we know nothing).
5871 ValueNum memoryInitVal = vnStore->VNForFunc(TYP_REF, VNF_InitVal, vnStore->VNForIntCon(-1)); // Use -1 for memory.
5872 GetMemoryPerSsaData(SsaConfig::FIRST_SSA_NUM)->m_vnPair.SetBoth(memoryInitVal);
5876 printf("Memory Initial Value in BB01 is: " FMT_VN "\n", memoryInitVal);
5880 ValueNumberState vs(this);
5882 // Push the first block. This has no preds.
5883 vs.m_toDoAllPredsDone.Push(fgFirstBB);
5885 while (vs.ToDoExists())
5887 while (vs.m_toDoAllPredsDone.Size() > 0)
5889 BasicBlock* toDo = vs.m_toDoAllPredsDone.Pop();
5890 fgValueNumberBlock(toDo);
5891 // Record that we've visited "toDo", and add successors to the right sets.
5892 vs.FinishVisit(toDo);
5894 // OK, we've run out of blocks whose predecessors are done. Pick one whose predecessors are not all done,
5895 // process that. This may make more "all-done" blocks, so we'll go around the outer loop again --
5896 // note that this is an "if", not a "while" loop.
5897 if (vs.m_toDoNotAllPredsDone.Size() > 0)
5899 BasicBlock* toDo = vs.ChooseFromNotAllPredsDone();
5900 if (toDo == nullptr)
5902 continue; // We may have run out, because of completed blocks on the not-all-preds done list.
5905 fgValueNumberBlock(toDo);
5906 // Record that we've visited "toDo", and add successors to the right sest.
5907 vs.FinishVisit(toDo);
5915 fgVNPassesCompleted++;
5918 void Compiler::fgValueNumberBlock(BasicBlock* blk)
5923 compCurStmtNum = blk->bbStmtNum - 1; // Set compCurStmtNum
5926 // First: visit phi's. If "newVNForPhis", give them new VN's. If not,
5927 // first check to see if all phi args have the same value.
5928 GenTree* firstNonPhi = blk->FirstNonPhiDef();
5929 for (GenTree* phiDefs = blk->bbTreeList; phiDefs != firstNonPhi; phiDefs = phiDefs->gtNext)
5931 // TODO-Cleanup: It has been proposed that we should have an IsPhiDef predicate. We would use it
5932 // in Block::FirstNonPhiDef as well.
5933 GenTree* phiDef = phiDefs->gtStmt.gtStmtExpr;
5934 assert(phiDef->OperGet() == GT_ASG);
5935 GenTreeLclVarCommon* newSsaVar = phiDef->gtOp.gtOp1->AsLclVarCommon();
5937 ValueNumPair phiAppVNP;
5938 ValueNumPair sameVNPair;
5940 GenTree* phiFunc = phiDef->gtOp.gtOp2;
5942 // At this point a GT_PHI node should never have a nullptr for gtOp1
5943 // and the gtOp1 should always be a GT_LIST node.
5944 GenTree* phiOp1 = phiFunc->gtOp.gtOp1;
5945 noway_assert(phiOp1 != nullptr);
5946 noway_assert(phiOp1->OperGet() == GT_LIST);
5948 GenTreeArgList* phiArgs = phiFunc->gtOp.gtOp1->AsArgList();
5950 // A GT_PHI node should have more than one argument.
5951 noway_assert(phiArgs->Rest() != nullptr);
5953 GenTreeLclVarCommon* phiArg = phiArgs->Current()->AsLclVarCommon();
5954 phiArgs = phiArgs->Rest();
5956 phiAppVNP.SetBoth(vnStore->VNForIntCon(phiArg->gtSsaNum));
5957 bool allSameLib = true;
5958 bool allSameCons = true;
5959 sameVNPair = lvaTable[phiArg->gtLclNum].GetPerSsaData(phiArg->gtSsaNum)->m_vnPair;
5960 if (!sameVNPair.BothDefined())
5963 allSameCons = false;
5965 while (phiArgs != nullptr)
5967 phiArg = phiArgs->Current()->AsLclVarCommon();
5968 // Set the VN of the phi arg.
5969 phiArg->gtVNPair = lvaTable[phiArg->gtLclNum].GetPerSsaData(phiArg->gtSsaNum)->m_vnPair;
5970 if (phiArg->gtVNPair.BothDefined())
5972 if (phiArg->gtVNPair.GetLiberal() != sameVNPair.GetLiberal())
5976 if (phiArg->gtVNPair.GetConservative() != sameVNPair.GetConservative())
5978 allSameCons = false;
5984 allSameCons = false;
5986 ValueNumPair phiArgSsaVNP;
5987 phiArgSsaVNP.SetBoth(vnStore->VNForIntCon(phiArg->gtSsaNum));
5988 phiAppVNP = vnStore->VNPairForFunc(newSsaVar->TypeGet(), VNF_Phi, phiArgSsaVNP, phiAppVNP);
5989 phiArgs = phiArgs->Rest();
5992 ValueNumPair newVNPair;
5995 newVNPair.SetLiberal(sameVNPair.GetLiberal());
5999 newVNPair.SetLiberal(phiAppVNP.GetLiberal());
6003 newVNPair.SetConservative(sameVNPair.GetConservative());
6007 newVNPair.SetConservative(phiAppVNP.GetConservative());
6010 LclSsaVarDsc* newSsaVarDsc = lvaTable[newSsaVar->gtLclNum].GetPerSsaData(newSsaVar->GetSsaNum());
6011 // If all the args of the phi had the same value(s, liberal and conservative), then there wasn't really
6012 // a reason to have the phi -- just pass on that value.
6013 if (allSameLib && allSameCons)
6015 newSsaVarDsc->m_vnPair = newVNPair;
6019 printf("In SSA definition, incoming phi args all same, set VN of local %d/%d to ",
6020 newSsaVar->GetLclNum(), newSsaVar->GetSsaNum());
6021 vnpPrint(newVNPair, 1);
6028 // They were not the same; we need to create a phi definition.
6029 ValueNumPair lclNumVNP;
6030 lclNumVNP.SetBoth(ValueNum(newSsaVar->GetLclNum()));
6031 ValueNumPair ssaNumVNP;
6032 ssaNumVNP.SetBoth(ValueNum(newSsaVar->GetSsaNum()));
6033 ValueNumPair vnPhiDef =
6034 vnStore->VNPairForFunc(newSsaVar->TypeGet(), VNF_PhiDef, lclNumVNP, ssaNumVNP, phiAppVNP);
6035 newSsaVarDsc->m_vnPair = vnPhiDef;
6039 printf("SSA definition: set VN of local %d/%d to ", newSsaVar->GetLclNum(), newSsaVar->GetSsaNum());
6040 vnpPrint(vnPhiDef, 1);
6047 // Now do the same for each MemoryKind.
6048 for (MemoryKind memoryKind : allMemoryKinds())
6050 // Is there a phi for this block?
6051 if (blk->bbMemorySsaPhiFunc[memoryKind] == nullptr)
6053 fgCurMemoryVN[memoryKind] = GetMemoryPerSsaData(blk->bbMemorySsaNumIn[memoryKind])->m_vnPair.GetLiberal();
6054 assert(fgCurMemoryVN[memoryKind] != ValueNumStore::NoVN);
6058 if ((memoryKind == ByrefExposed) && byrefStatesMatchGcHeapStates)
6060 // The update for GcHeap will copy its result to ByrefExposed.
6061 assert(memoryKind < GcHeap);
6062 assert(blk->bbMemorySsaPhiFunc[memoryKind] == blk->bbMemorySsaPhiFunc[GcHeap]);
6067 ValueNum newMemoryVN;
6068 if (optBlockIsLoopEntry(blk, &loopNum))
6070 newMemoryVN = fgMemoryVNForLoopSideEffects(memoryKind, blk, loopNum);
6074 // Are all the VN's the same?
6075 BasicBlock::MemoryPhiArg* phiArgs = blk->bbMemorySsaPhiFunc[memoryKind];
6076 assert(phiArgs != BasicBlock::EmptyMemoryPhiDef);
6077 // There should be > 1 args to a phi.
6078 assert(phiArgs->m_nextArg != nullptr);
6079 ValueNum phiAppVN = vnStore->VNForIntCon(phiArgs->GetSsaNum());
6080 JITDUMP(" Building phi application: $%x = SSA# %d.\n", phiAppVN, phiArgs->GetSsaNum());
6081 bool allSame = true;
6082 ValueNum sameVN = GetMemoryPerSsaData(phiArgs->GetSsaNum())->m_vnPair.GetLiberal();
6083 if (sameVN == ValueNumStore::NoVN)
6087 phiArgs = phiArgs->m_nextArg;
6088 while (phiArgs != nullptr)
6090 ValueNum phiArgVN = GetMemoryPerSsaData(phiArgs->GetSsaNum())->m_vnPair.GetLiberal();
6091 if (phiArgVN == ValueNumStore::NoVN || phiArgVN != sameVN)
6096 ValueNum oldPhiAppVN = phiAppVN;
6098 unsigned phiArgSSANum = phiArgs->GetSsaNum();
6099 ValueNum phiArgSSANumVN = vnStore->VNForIntCon(phiArgSSANum);
6100 JITDUMP(" Building phi application: $%x = SSA# %d.\n", phiArgSSANumVN, phiArgSSANum);
6101 phiAppVN = vnStore->VNForFunc(TYP_REF, VNF_Phi, phiArgSSANumVN, phiAppVN);
6102 JITDUMP(" Building phi application: $%x = phi($%x, $%x).\n", phiAppVN, phiArgSSANumVN,
6104 phiArgs = phiArgs->m_nextArg;
6108 newMemoryVN = sameVN;
6113 vnStore->VNForFunc(TYP_REF, VNF_PhiMemoryDef, vnStore->VNForHandle(ssize_t(blk), 0), phiAppVN);
6116 GetMemoryPerSsaData(blk->bbMemorySsaNumIn[memoryKind])->m_vnPair.SetLiberal(newMemoryVN);
6117 fgCurMemoryVN[memoryKind] = newMemoryVN;
6118 if ((memoryKind == GcHeap) && byrefStatesMatchGcHeapStates)
6120 // Keep the CurMemoryVNs in sync
6121 fgCurMemoryVN[ByrefExposed] = newMemoryVN;
6127 printf("The SSA definition for %s (#%d) at start of " FMT_BB " is ", memoryKindNames[memoryKind],
6128 blk->bbMemorySsaNumIn[memoryKind], blk->bbNum);
6129 vnPrint(fgCurMemoryVN[memoryKind], 1);
6135 // Now iterate over the remaining statements, and their trees.
6136 for (GenTree* stmt = firstNonPhi; stmt != nullptr; stmt = stmt->gtNext)
6138 assert(stmt->IsStatement());
6144 printf("\n***** " FMT_BB ", stmt %d (before)\n", blk->bbNum, compCurStmtNum);
6145 gtDispTree(stmt->gtStmt.gtStmtExpr);
6150 for (GenTree* tree = stmt->gtStmt.gtStmtList; tree; tree = tree->gtNext)
6152 fgValueNumberTree(tree);
6158 printf("\n***** " FMT_BB ", stmt %d (after)\n", blk->bbNum, compCurStmtNum);
6159 gtDispTree(stmt->gtStmt.gtStmtExpr);
6163 printf("---------\n");
6169 for (MemoryKind memoryKind : allMemoryKinds())
6171 if ((memoryKind == GcHeap) && byrefStatesMatchGcHeapStates)
6173 // The update to the shared SSA data will have already happened for ByrefExposed.
6174 assert(memoryKind > ByrefExposed);
6175 assert(blk->bbMemorySsaNumOut[memoryKind] == blk->bbMemorySsaNumOut[ByrefExposed]);
6176 assert(GetMemoryPerSsaData(blk->bbMemorySsaNumOut[memoryKind])->m_vnPair.GetLiberal() ==
6177 fgCurMemoryVN[memoryKind]);
6181 if (blk->bbMemorySsaNumOut[memoryKind] != blk->bbMemorySsaNumIn[memoryKind])
6183 GetMemoryPerSsaData(blk->bbMemorySsaNumOut[memoryKind])->m_vnPair.SetLiberal(fgCurMemoryVN[memoryKind]);
6187 compCurBB = nullptr;
6190 ValueNum Compiler::fgMemoryVNForLoopSideEffects(MemoryKind memoryKind,
6191 BasicBlock* entryBlock,
6192 unsigned innermostLoopNum)
6194 // "loopNum" is the innermost loop for which "blk" is the entry; find the outermost one.
6195 assert(innermostLoopNum != BasicBlock::NOT_IN_LOOP);
6196 unsigned loopsInNest = innermostLoopNum;
6197 unsigned loopNum = innermostLoopNum;
6198 while (loopsInNest != BasicBlock::NOT_IN_LOOP)
6200 if (optLoopTable[loopsInNest].lpEntry != entryBlock)
6204 loopNum = loopsInNest;
6205 loopsInNest = optLoopTable[loopsInNest].lpParent;
6211 printf("Computing %s state for block " FMT_BB ", entry block for loops %d to %d:\n",
6212 memoryKindNames[memoryKind], entryBlock->bbNum, innermostLoopNum, loopNum);
6216 // If this loop has memory havoc effects, just use a new, unique VN.
6217 if (optLoopTable[loopNum].lpLoopHasMemoryHavoc[memoryKind])
6219 ValueNum res = vnStore->VNForExpr(entryBlock, TYP_REF);
6223 printf(" Loop %d has memory havoc effect; heap state is new unique $%x.\n", loopNum, res);
6229 // Otherwise, find the predecessors of the entry block that are not in the loop.
6230 // If there is only one such, use its memory value as the "base." If more than one,
6231 // use a new unique VN.
6232 BasicBlock* nonLoopPred = nullptr;
6233 bool multipleNonLoopPreds = false;
6234 for (flowList* pred = BlockPredsWithEH(entryBlock); pred != nullptr; pred = pred->flNext)
6236 BasicBlock* predBlock = pred->flBlock;
6237 if (!optLoopTable[loopNum].lpContains(predBlock))
6239 if (nonLoopPred == nullptr)
6241 nonLoopPred = predBlock;
6248 printf(" Entry block has >1 non-loop preds: (at least) " FMT_BB " and " FMT_BB ".\n",
6249 nonLoopPred->bbNum, predBlock->bbNum);
6252 multipleNonLoopPreds = true;
6257 if (multipleNonLoopPreds)
6259 ValueNum res = vnStore->VNForExpr(entryBlock, TYP_REF);
6263 printf(" Therefore, memory state is new, fresh $%x.\n", res);
6268 // Otherwise, there is a single non-loop pred.
6269 assert(nonLoopPred != nullptr);
6270 // What is its memory post-state?
6271 ValueNum newMemoryVN = GetMemoryPerSsaData(nonLoopPred->bbMemorySsaNumOut[memoryKind])->m_vnPair.GetLiberal();
6272 assert(newMemoryVN != ValueNumStore::NoVN); // We must have processed the single non-loop pred before reaching the
6278 printf(" Init %s state is $%x, with new, fresh VN at:\n", memoryKindNames[memoryKind], newMemoryVN);
6281 // Modify "base" by setting all the modified fields/field maps/array maps to unknown values.
6282 // These annotations apply specifically to the GcHeap, where we disambiguate across such stores.
6283 if (memoryKind == GcHeap)
6285 // First the fields/field maps.
6286 Compiler::LoopDsc::FieldHandleSet* fieldsMod = optLoopTable[loopNum].lpFieldsModified;
6287 if (fieldsMod != nullptr)
6289 for (Compiler::LoopDsc::FieldHandleSet::KeyIterator ki = fieldsMod->Begin(); !ki.Equal(fieldsMod->End());
6292 CORINFO_FIELD_HANDLE fldHnd = ki.Get();
6293 ValueNum fldHndVN = vnStore->VNForHandle(ssize_t(fldHnd), GTF_ICON_FIELD_HDL);
6298 const char* modName;
6299 const char* fldName = eeGetFieldName(fldHnd, &modName);
6300 printf(" VNForHandle(%s) is " FMT_VN "\n", fldName, fldHndVN);
6305 vnStore->VNForMapStore(TYP_REF, newMemoryVN, fldHndVN, vnStore->VNForExpr(entryBlock, TYP_REF));
6308 // Now do the array maps.
6309 Compiler::LoopDsc::ClassHandleSet* elemTypesMod = optLoopTable[loopNum].lpArrayElemTypesModified;
6310 if (elemTypesMod != nullptr)
6312 for (Compiler::LoopDsc::ClassHandleSet::KeyIterator ki = elemTypesMod->Begin();
6313 !ki.Equal(elemTypesMod->End()); ++ki)
6315 CORINFO_CLASS_HANDLE elemClsHnd = ki.Get();
6320 var_types elemTyp = DecodeElemType(elemClsHnd);
6321 // If a valid class handle is given when the ElemType is set, DecodeElemType will
6322 // return TYP_STRUCT, and elemClsHnd is that handle.
6323 // Otherwise, elemClsHnd is NOT a valid class handle, and is the encoded var_types value.
6324 if (elemTyp == TYP_STRUCT)
6326 printf(" Array map %s[]\n", eeGetClassName(elemClsHnd));
6330 printf(" Array map %s[]\n", varTypeName(elemTyp));
6335 ValueNum elemTypeVN = vnStore->VNForHandle(ssize_t(elemClsHnd), GTF_ICON_CLASS_HDL);
6336 ValueNum uniqueVN = vnStore->VNForExpr(entryBlock, TYP_REF);
6337 newMemoryVN = vnStore->VNForMapStore(TYP_REF, newMemoryVN, elemTypeVN, uniqueVN);
6343 // If there were any fields/elements modified, this should have been recorded as havoc
6344 // for ByrefExposed.
6345 assert(memoryKind == ByrefExposed);
6346 assert((optLoopTable[loopNum].lpFieldsModified == nullptr) ||
6347 optLoopTable[loopNum].lpLoopHasMemoryHavoc[memoryKind]);
6348 assert((optLoopTable[loopNum].lpArrayElemTypesModified == nullptr) ||
6349 optLoopTable[loopNum].lpLoopHasMemoryHavoc[memoryKind]);
6355 printf(" Final %s state is $%x.\n", memoryKindNames[memoryKind], newMemoryVN);
6361 void Compiler::fgMutateGcHeap(GenTree* tree DEBUGARG(const char* msg))
6363 // Update the current memory VN, and if we're tracking the heap SSA # caused by this node, record it.
6364 recordGcHeapStore(tree, vnStore->VNForExpr(compCurBB, TYP_REF) DEBUGARG(msg));
6367 void Compiler::fgMutateAddressExposedLocal(GenTree* tree DEBUGARG(const char* msg))
6369 // Update the current ByrefExposed VN, and if we're tracking the heap SSA # caused by this node, record it.
6370 recordAddressExposedLocalStore(tree, vnStore->VNForExpr(compCurBB) DEBUGARG(msg));
6373 void Compiler::recordGcHeapStore(GenTree* curTree, ValueNum gcHeapVN DEBUGARG(const char* msg))
6375 // bbMemoryDef must include GcHeap for any block that mutates the GC Heap
6376 // and GC Heap mutations are also ByrefExposed mutations
6377 assert((compCurBB->bbMemoryDef & memoryKindSet(GcHeap, ByrefExposed)) == memoryKindSet(GcHeap, ByrefExposed));
6378 fgCurMemoryVN[GcHeap] = gcHeapVN;
6380 if (byrefStatesMatchGcHeapStates)
6382 // Since GcHeap and ByrefExposed share SSA nodes, they need to share
6383 // value numbers too.
6384 fgCurMemoryVN[ByrefExposed] = gcHeapVN;
6388 // GcHeap and ByrefExposed have different defnums and VNs. We conservatively
6389 // assume that this GcHeap store may alias any byref load/store, so don't
6390 // bother trying to record the map/select stuff, and instead just an opaque VN
6392 fgCurMemoryVN[ByrefExposed] = vnStore->VNForExpr(compCurBB);
6398 printf(" fgCurMemoryVN[GcHeap] assigned for %s at ", msg);
6399 Compiler::printTreeID(curTree);
6400 printf(" to VN: " FMT_VN ".\n", gcHeapVN);
6404 // If byrefStatesMatchGcHeapStates is true, then since GcHeap and ByrefExposed share
6405 // their SSA map entries, the below will effectively update both.
6406 fgValueNumberRecordMemorySsa(GcHeap, curTree);
6409 void Compiler::recordAddressExposedLocalStore(GenTree* curTree, ValueNum memoryVN DEBUGARG(const char* msg))
6411 // This should only happen if GcHeap and ByrefExposed are being tracked separately;
6412 // otherwise we'd go through recordGcHeapStore.
6413 assert(!byrefStatesMatchGcHeapStates);
6415 // bbMemoryDef must include ByrefExposed for any block that mutates an address-exposed local
6416 assert((compCurBB->bbMemoryDef & memoryKindSet(ByrefExposed)) != 0);
6417 fgCurMemoryVN[ByrefExposed] = memoryVN;
6422 printf(" fgCurMemoryVN[ByrefExposed] assigned for %s at ", msg);
6423 Compiler::printTreeID(curTree);
6424 printf(" to VN: " FMT_VN ".\n", memoryVN);
6428 fgValueNumberRecordMemorySsa(ByrefExposed, curTree);
6431 void Compiler::fgValueNumberRecordMemorySsa(MemoryKind memoryKind, GenTree* tree)
6434 if (GetMemorySsaMap(memoryKind)->Lookup(tree, &ssaNum))
6436 GetMemoryPerSsaData(ssaNum)->m_vnPair.SetLiberal(fgCurMemoryVN[memoryKind]);
6441 Compiler::printTreeID(tree);
6442 printf(" sets %s SSA # %d to VN $%x: ", memoryKindNames[memoryKind], ssaNum, fgCurMemoryVN[memoryKind]);
6443 vnStore->vnDump(this, fgCurMemoryVN[memoryKind]);
6450 // The input 'tree' is a leaf node that is a constant
6451 // Assign the proper value number to the tree
6452 void Compiler::fgValueNumberTreeConst(GenTree* tree)
6454 genTreeOps oper = tree->OperGet();
6455 var_types typ = tree->TypeGet();
6456 assert(GenTree::OperIsConst(oper));
6469 if (tree->IsCnsIntOrI() && tree->IsIconHandle())
6471 tree->gtVNPair.SetBoth(
6472 vnStore->VNForHandle(ssize_t(tree->gtIntConCommon.IconValue()), tree->GetIconHandleFlag()));
6474 else if ((typ == TYP_LONG) || (typ == TYP_ULONG))
6476 tree->gtVNPair.SetBoth(vnStore->VNForLongCon(INT64(tree->gtIntConCommon.LngValue())));
6480 tree->gtVNPair.SetBoth(vnStore->VNForIntCon(int(tree->gtIntConCommon.IconValue())));
6485 tree->gtVNPair.SetBoth(vnStore->VNForFloatCon((float)tree->gtDblCon.gtDconVal));
6488 tree->gtVNPair.SetBoth(vnStore->VNForDoubleCon(tree->gtDblCon.gtDconVal));
6491 if (tree->gtIntConCommon.IconValue() == 0)
6493 tree->gtVNPair.SetBoth(ValueNumStore::VNForNull());
6497 assert(tree->gtFlags == GTF_ICON_STR_HDL); // Constant object can be only frozen string.
6498 tree->gtVNPair.SetBoth(
6499 vnStore->VNForHandle(ssize_t(tree->gtIntConCommon.IconValue()), tree->GetIconHandleFlag()));
6504 if (tree->gtIntConCommon.IconValue() == 0)
6506 tree->gtVNPair.SetBoth(ValueNumStore::VNForNull());
6510 assert(tree->IsCnsIntOrI());
6512 if (tree->IsIconHandle())
6514 tree->gtVNPair.SetBoth(
6515 vnStore->VNForHandle(ssize_t(tree->gtIntConCommon.IconValue()), tree->GetIconHandleFlag()));
6519 tree->gtVNPair.SetBoth(vnStore->VNForByrefCon(tree->gtIntConCommon.IconValue()));
6529 //------------------------------------------------------------------------
6530 // fgValueNumberBlockAssignment: Perform value numbering for block assignments.
6533 // tree - the block assignment to be value numbered.
6539 // 'tree' must be a block assignment (GT_INITBLK, GT_COPYBLK, GT_COPYOBJ).
6541 void Compiler::fgValueNumberBlockAssignment(GenTree* tree)
6543 GenTree* lhs = tree->gtGetOp1();
6544 GenTree* rhs = tree->gtGetOp2();
6546 if (tree->OperIsInitBlkOp())
6548 GenTreeLclVarCommon* lclVarTree;
6551 if (tree->DefinesLocal(this, &lclVarTree, &isEntire))
6553 assert(lclVarTree->gtFlags & GTF_VAR_DEF);
6554 // Should not have been recorded as updating the GC heap.
6555 assert(!GetMemorySsaMap(GcHeap)->Lookup(tree));
6557 unsigned lclNum = lclVarTree->GetLclNum();
6559 // Ignore vars that we excluded from SSA (for example, because they're address-exposed). They don't have
6560 // SSA names in which to store VN's on defs. We'll yield unique VN's when we read from them.
6561 if (lvaInSsa(lclNum))
6563 // Should not have been recorded as updating ByrefExposed.
6564 assert(!GetMemorySsaMap(ByrefExposed)->Lookup(tree));
6566 unsigned lclDefSsaNum = GetSsaNumForLocalVarDef(lclVarTree);
6568 ValueNum initBlkVN = ValueNumStore::NoVN;
6569 GenTree* initConst = rhs;
6570 if (isEntire && initConst->OperGet() == GT_CNS_INT)
6572 unsigned initVal = 0xFF & (unsigned)initConst->AsIntConCommon()->IconValue();
6575 initBlkVN = vnStore->VNZeroForType(lclVarTree->TypeGet());
6578 ValueNum lclVarVN = (initBlkVN != ValueNumStore::NoVN)
6580 : vnStore->VNForExpr(compCurBB, var_types(lvaTable[lclNum].lvType));
6582 lvaTable[lclNum].GetPerSsaData(lclDefSsaNum)->m_vnPair.SetBoth(lclVarVN);
6586 printf("N%03u ", tree->gtSeqNum);
6587 Compiler::printTreeID(tree);
6589 gtDispNodeName(tree);
6590 printf(" V%02u/%d => ", lclNum, lclDefSsaNum);
6591 vnPrint(lclVarVN, 1);
6596 else if (lvaVarAddrExposed(lclVarTree->gtLclNum))
6598 fgMutateAddressExposedLocal(tree DEBUGARG("INITBLK - address-exposed local"));
6603 // For now, arbitrary side effect on GcHeap/ByrefExposed.
6604 // TODO-CQ: Why not be complete, and get this case right?
6605 fgMutateGcHeap(tree DEBUGARG("INITBLK - non local"));
6607 // Initblock's are of type void. Give them the void "value" -- they may occur in argument lists, which we
6608 // want to be able to give VN's to.
6609 tree->gtVNPair.SetBoth(ValueNumStore::VNForVoid());
6613 assert(tree->OperIsCopyBlkOp());
6614 // TODO-Cleanup: We should factor things so that we uniformly rely on "PtrTo" VN's, and
6615 // the memory cases can be shared with assignments.
6616 GenTreeLclVarCommon* lclVarTree = nullptr;
6617 bool isEntire = false;
6618 // Note that we don't care about exceptions here, since we're only using the values
6619 // to perform an assignment (which happens after any exceptions are raised...)
6621 if (tree->DefinesLocal(this, &lclVarTree, &isEntire))
6623 // Should not have been recorded as updating the GC heap.
6624 assert(!GetMemorySsaMap(GcHeap)->Lookup(tree));
6626 unsigned lhsLclNum = lclVarTree->GetLclNum();
6627 FieldSeqNode* lhsFldSeq = nullptr;
6628 // If it's excluded from SSA, don't need to do anything.
6629 if (lvaInSsa(lhsLclNum))
6631 // Should not have been recorded as updating ByrefExposed.
6632 assert(!GetMemorySsaMap(ByrefExposed)->Lookup(tree));
6634 unsigned lclDefSsaNum = GetSsaNumForLocalVarDef(lclVarTree);
6636 if (lhs->IsLocalExpr(this, &lclVarTree, &lhsFldSeq))
6638 noway_assert(lclVarTree->gtLclNum == lhsLclNum);
6643 if (lhs->OperIsBlk())
6645 lhsAddr = lhs->AsBlk()->Addr();
6649 assert(lhs->OperGet() == GT_IND);
6650 lhsAddr = lhs->gtOp.gtOp1;
6653 // For addr-of-local expressions, lib/cons shouldn't matter.
6654 assert(lhsAddr->gtVNPair.BothEqual());
6655 ValueNum lhsAddrVN = lhsAddr->GetVN(VNK_Liberal);
6657 // Unpack the PtrToLoc value number of the address.
6658 assert(vnStore->IsVNFunc(lhsAddrVN));
6660 VNFuncApp lhsAddrFuncApp;
6661 vnStore->GetVNFunc(lhsAddrVN, &lhsAddrFuncApp);
6663 assert(lhsAddrFuncApp.m_func == VNF_PtrToLoc);
6664 assert(vnStore->IsVNConstant(lhsAddrFuncApp.m_args[0]) &&
6665 vnStore->ConstantValue<unsigned>(lhsAddrFuncApp.m_args[0]) == lhsLclNum);
6667 lhsFldSeq = vnStore->FieldSeqVNToFieldSeq(lhsAddrFuncApp.m_args[1]);
6670 // Now we need to get the proper RHS.
6671 GenTreeLclVarCommon* rhsLclVarTree = nullptr;
6672 LclVarDsc* rhsVarDsc = nullptr;
6673 FieldSeqNode* rhsFldSeq = nullptr;
6674 ValueNumPair rhsVNPair;
6675 bool isNewUniq = false;
6676 if (!rhs->OperIsIndir())
6678 if (rhs->IsLocalExpr(this, &rhsLclVarTree, &rhsFldSeq))
6680 unsigned rhsLclNum = rhsLclVarTree->GetLclNum();
6681 rhsVarDsc = &lvaTable[rhsLclNum];
6682 if (!lvaInSsa(rhsLclNum) || rhsFldSeq == FieldSeqStore::NotAField())
6684 rhsVNPair.SetBoth(vnStore->VNForExpr(compCurBB, rhsLclVarTree->TypeGet()));
6689 rhsVNPair = lvaTable[rhsLclVarTree->GetLclNum()]
6690 .GetPerSsaData(rhsLclVarTree->GetSsaNum())
6692 var_types indType = rhsLclVarTree->TypeGet();
6694 rhsVNPair = vnStore->VNPairApplySelectors(rhsVNPair, rhsFldSeq, indType);
6699 rhsVNPair.SetBoth(vnStore->VNForExpr(compCurBB, rhs->TypeGet()));
6705 GenTree* srcAddr = rhs->AsIndir()->Addr();
6706 VNFuncApp srcAddrFuncApp;
6707 if (srcAddr->IsLocalAddrExpr(this, &rhsLclVarTree, &rhsFldSeq))
6709 unsigned rhsLclNum = rhsLclVarTree->GetLclNum();
6710 rhsVarDsc = &lvaTable[rhsLclNum];
6711 if (!lvaInSsa(rhsLclNum) || rhsFldSeq == FieldSeqStore::NotAField())
6717 rhsVNPair = lvaTable[rhsLclVarTree->GetLclNum()]
6718 .GetPerSsaData(rhsLclVarTree->GetSsaNum())
6720 var_types indType = rhsLclVarTree->TypeGet();
6722 rhsVNPair = vnStore->VNPairApplySelectors(rhsVNPair, rhsFldSeq, indType);
6725 else if (vnStore->GetVNFunc(vnStore->VNLiberalNormalValue(srcAddr->gtVNPair), &srcAddrFuncApp))
6727 if (srcAddrFuncApp.m_func == VNF_PtrToStatic)
6729 var_types indType = lclVarTree->TypeGet();
6730 ValueNum fieldSeqVN = srcAddrFuncApp.m_args[0];
6732 FieldSeqNode* zeroOffsetFldSeq = nullptr;
6733 if (GetZeroOffsetFieldMap()->Lookup(srcAddr, &zeroOffsetFldSeq))
6736 vnStore->FieldSeqVNAppend(fieldSeqVN, vnStore->VNForFieldSeq(zeroOffsetFldSeq));
6739 FieldSeqNode* fldSeqForStaticVar = vnStore->FieldSeqVNToFieldSeq(fieldSeqVN);
6741 if (fldSeqForStaticVar != FieldSeqStore::NotAField())
6743 // We model statics as indices into GcHeap (which is a subset of ByrefExposed).
6744 ValueNum selectedStaticVar;
6745 size_t structSize = 0;
6746 selectedStaticVar = vnStore->VNApplySelectors(VNK_Liberal, fgCurMemoryVN[GcHeap],
6747 fldSeqForStaticVar, &structSize);
6749 vnStore->VNApplySelectorsTypeCheck(selectedStaticVar, indType, structSize);
6751 rhsVNPair.SetLiberal(selectedStaticVar);
6752 rhsVNPair.SetConservative(vnStore->VNForExpr(compCurBB, indType));
6756 JITDUMP(" *** Missing field sequence info for Src/RHS of COPYBLK\n");
6760 else if (srcAddrFuncApp.m_func == VNF_PtrToArrElem)
6763 fgValueNumberArrIndexVal(nullptr, &srcAddrFuncApp, vnStore->VNForEmptyExcSet());
6764 rhsVNPair.SetLiberal(elemLib);
6765 rhsVNPair.SetConservative(vnStore->VNForExpr(compCurBB, lclVarTree->TypeGet()));
6778 if (lhsFldSeq == FieldSeqStore::NotAField())
6780 // We don't have proper field sequence information for the lhs
6782 JITDUMP(" *** Missing field sequence info for Dst/LHS of COPYBLK\n");
6788 rhsVNPair.SetBoth(vnStore->VNForExpr(compCurBB, lclVarTree->TypeGet()));
6790 else // We will assign rhsVNPair into a map[lhsFldSeq]
6792 if (lhsFldSeq != nullptr && isEntire)
6794 // This can occur for structs with one field, itself of a struct type.
6795 // We are assigning the one field and it is also the entire enclosing struct.
6797 // Use an unique value number for the old map, as this is an an entire assignment
6798 // and we won't have any other values in the map
6799 ValueNumPair uniqueMap;
6800 uniqueMap.SetBoth(vnStore->VNForExpr(compCurBB, lclVarTree->TypeGet()));
6801 rhsVNPair = vnStore->VNPairApplySelectorsAssign(uniqueMap, lhsFldSeq, rhsVNPair,
6802 lclVarTree->TypeGet(), compCurBB);
6806 ValueNumPair oldLhsVNPair =
6807 lvaTable[lhsLclNum].GetPerSsaData(lclVarTree->GetSsaNum())->m_vnPair;
6808 rhsVNPair = vnStore->VNPairApplySelectorsAssign(oldLhsVNPair, lhsFldSeq, rhsVNPair,
6809 lclVarTree->TypeGet(), compCurBB);
6813 lvaTable[lhsLclNum].GetPerSsaData(lclDefSsaNum)->m_vnPair = vnStore->VNPNormalPair(rhsVNPair);
6819 Compiler::printTreeID(tree);
6820 printf(" assigned VN to local var V%02u/%d: ", lhsLclNum, lclDefSsaNum);
6823 printf("new uniq ");
6825 vnpPrint(rhsVNPair, 1);
6830 else if (lvaVarAddrExposed(lhsLclNum))
6832 fgMutateAddressExposedLocal(tree DEBUGARG("COPYBLK - address-exposed local"));
6837 // For now, arbitrary side effect on GcHeap/ByrefExposed.
6838 // TODO-CQ: Why not be complete, and get this case right?
6839 fgMutateGcHeap(tree DEBUGARG("COPYBLK - non local"));
6841 // Copyblock's are of type void. Give them the void "value" -- they may occur in argument lists, which we want
6842 // to be able to give VN's to.
6843 tree->gtVNPair.SetBoth(ValueNumStore::VNForVoid());
6847 void Compiler::fgValueNumberTree(GenTree* tree)
6849 genTreeOps oper = tree->OperGet();
6852 // TODO-CQ: For now TYP_SIMD values are not handled by value numbering to be amenable for CSE'ing.
6853 if (oper == GT_SIMD)
6855 tree->gtVNPair.SetBoth(vnStore->VNForExpr(compCurBB, TYP_UNKNOWN));
6860 #ifdef FEATURE_HW_INTRINSICS
6861 if (oper == GT_HWIntrinsic)
6863 // TODO-CQ: For now hardware intrinsics are not handled by value numbering to be amenable for CSE'ing.
6864 tree->gtVNPair.SetBoth(vnStore->VNForExpr(compCurBB, TYP_UNKNOWN));
6866 GenTreeHWIntrinsic* hwIntrinsicNode = tree->AsHWIntrinsic();
6867 assert(hwIntrinsicNode != nullptr);
6869 // For safety/correctness we must mutate the global heap valuenumber
6870 // for any HW intrinsic that performs a memory store operation
6871 if (hwIntrinsicNode->OperIsMemoryStore())
6873 fgMutateGcHeap(tree DEBUGARG("HWIntrinsic - MemoryStore"));
6878 #endif // FEATURE_HW_INTRINSICS
6880 var_types typ = tree->TypeGet();
6881 if (GenTree::OperIsConst(oper))
6883 // If this is a struct assignment, with a constant rhs, it is an initBlk, and it is not
6884 // really useful to value number the constant.
6885 if (!varTypeIsStruct(tree))
6887 fgValueNumberTreeConst(tree);
6890 else if (GenTree::OperIsLeaf(oper))
6896 GenTreeLclVarCommon* lcl = tree->AsLclVarCommon();
6897 unsigned lclNum = lcl->gtLclNum;
6898 LclVarDsc* varDsc = &lvaTable[lclNum];
6900 // Do we have a Use (read) of the LclVar?
6902 if ((lcl->gtFlags & GTF_VAR_DEF) == 0 ||
6903 (lcl->gtFlags & GTF_VAR_USEASG)) // If it is a "pure" def, will handled as part of the assignment.
6905 bool generateUniqueVN = false;
6906 FieldSeqNode* zeroOffsetFldSeq = nullptr;
6908 // When we have a TYP_BYREF LclVar it can have a zero offset field sequence that needs to be added
6909 if (typ == TYP_BYREF)
6911 GetZeroOffsetFieldMap()->Lookup(tree, &zeroOffsetFldSeq);
6914 if (varDsc->lvPromoted && varDsc->lvFieldCnt == 1)
6916 // If the promoted var has only one field var, treat like a use of the field var.
6917 lclNum = varDsc->lvFieldLclStart;
6920 if (lcl->gtSsaNum == SsaConfig::RESERVED_SSA_NUM)
6922 // Not an SSA variable.
6924 if (lvaVarAddrExposed(lclNum))
6926 // Address-exposed locals are part of ByrefExposed.
6927 ValueNum addrVN = vnStore->VNForFunc(TYP_BYREF, VNF_PtrToLoc, vnStore->VNForIntCon(lclNum),
6928 vnStore->VNForFieldSeq(nullptr));
6929 ValueNum loadVN = fgValueNumberByrefExposedLoad(typ, addrVN);
6931 lcl->gtVNPair.SetBoth(loadVN);
6935 // Assign odd cases a new, unique, VN.
6936 generateUniqueVN = true;
6941 ValueNumPair wholeLclVarVNP = varDsc->GetPerSsaData(lcl->gtSsaNum)->m_vnPair;
6943 // Check for mismatched LclVar size
6945 unsigned typSize = genTypeSize(genActualType(typ));
6946 unsigned varSize = genTypeSize(genActualType(varDsc->TypeGet()));
6948 if (typSize == varSize)
6950 lcl->gtVNPair = wholeLclVarVNP;
6952 else // mismatched LclVar definition and LclVar use size
6954 if (typSize < varSize)
6956 // the indirection is reading less that the whole LclVar
6957 // create a new VN that represent the partial value
6959 ValueNumPair partialLclVarVNP =
6960 vnStore->VNPairForCast(wholeLclVarVNP, typ, varDsc->TypeGet());
6961 lcl->gtVNPair = partialLclVarVNP;
6965 assert(typSize > varSize);
6966 // the indirection is reading beyond the end of the field
6968 generateUniqueVN = true;
6973 if (!generateUniqueVN)
6975 // There are a couple of cases where we haven't assigned a valid value number to 'lcl'
6977 if (lcl->gtVNPair.GetLiberal() == ValueNumStore::NoVN)
6979 // So far, we know about two of these cases:
6980 // Case 1) We have a local var who has never been defined but it's seen as a use.
6981 // This is the case of storeIndir(addr(lclvar)) = expr. In this case since we only
6982 // take the address of the variable, this doesn't mean it's a use nor we have to
6983 // initialize it, so in this very rare case, we fabricate a value number.
6984 // Case 2) Local variables that represent structs which are assigned using CpBlk.
6986 // Make sure we have either case 1 or case 2
6988 GenTree* nextNode = lcl->gtNext;
6989 assert((nextNode->gtOper == GT_ADDR && nextNode->gtOp.gtOp1 == lcl) ||
6990 varTypeIsStruct(lcl->TypeGet()));
6992 // We will assign a unique value number for these
6994 generateUniqueVN = true;
6998 if (!generateUniqueVN && (zeroOffsetFldSeq != nullptr))
7000 ValueNum addrExtended = vnStore->ExtendPtrVN(lcl, zeroOffsetFldSeq);
7001 if (addrExtended != ValueNumStore::NoVN)
7003 lcl->gtVNPair.SetBoth(addrExtended);
7007 if (generateUniqueVN)
7009 ValueNum uniqVN = vnStore->VNForExpr(compCurBB, lcl->TypeGet());
7010 lcl->gtVNPair.SetBoth(uniqVN);
7013 else if ((lcl->gtFlags & GTF_VAR_DEF) != 0)
7015 // We have a Def (write) of the LclVar
7017 // TODO-Review: For the short term, we have a workaround for copyblk/initblk. Those that use
7018 // addrSpillTemp will have a statement like "addrSpillTemp = addr(local)." If we previously decided
7019 // that this block operation defines the local, we will have labeled the "local" node as a DEF
7020 // This flag propagates to the "local" on the RHS. So we'll assume that this is correct,
7021 // and treat it as a def (to a new, unique VN).
7023 if (lcl->gtSsaNum != SsaConfig::RESERVED_SSA_NUM)
7025 ValueNum uniqVN = vnStore->VNForExpr(compCurBB, lcl->TypeGet());
7026 varDsc->GetPerSsaData(lcl->gtSsaNum)->m_vnPair.SetBoth(uniqVN);
7029 lcl->gtVNPair = ValueNumPair(); // Avoid confusion -- we don't set the VN of a lcl being defined.
7035 // Use the value of the function pointer (actually, a method handle.)
7036 tree->gtVNPair.SetBoth(
7037 vnStore->VNForHandle(ssize_t(tree->gtFptrVal.gtFptrMethod), GTF_ICON_METHOD_HDL));
7040 // This group passes through a value from a child node.
7042 tree->SetVNsFromNode(tree->gtRetExpr.gtInlineCandidate);
7047 GenTreeLclFld* lclFld = tree->AsLclFld();
7048 assert(!lvaInSsa(lclFld->GetLclNum()) || lclFld->gtFieldSeq != nullptr);
7049 // If this is a (full) def, then the variable will be labeled with the new SSA number,
7050 // which will not have a value. We skip; it will be handled by one of the assignment-like
7051 // forms (assignment, or initBlk or copyBlk).
7052 if (((lclFld->gtFlags & GTF_VAR_DEF) == 0) || (lclFld->gtFlags & GTF_VAR_USEASG))
7054 unsigned lclNum = lclFld->GetLclNum();
7055 unsigned ssaNum = lclFld->GetSsaNum();
7056 LclVarDsc* varDsc = &lvaTable[lclNum];
7058 var_types indType = tree->TypeGet();
7059 if (lclFld->gtFieldSeq == FieldSeqStore::NotAField() || !lvaInSsa(lclFld->GetLclNum()))
7061 // This doesn't represent a proper field access or it's a struct
7062 // with overlapping fields that is hard to reason about; return a new unique VN.
7063 tree->gtVNPair.SetBoth(vnStore->VNForExpr(compCurBB, indType));
7067 ValueNumPair lclVNPair = varDsc->GetPerSsaData(ssaNum)->m_vnPair;
7068 tree->gtVNPair = vnStore->VNPairApplySelectors(lclVNPair, lclFld->gtFieldSeq, indType);
7074 // The ones below here all get a new unique VN -- but for various reasons, explained after each.
7076 // We know nothing about the value of a caught expression.
7077 tree->gtVNPair.SetBoth(vnStore->VNForExpr(compCurBB, tree->TypeGet()));
7081 // Skip GT_CLS_VAR nodes that are the LHS of an assignment. (We labeled these earlier.)
7082 // We will "evaluate" this as part of the assignment.
7084 if ((tree->gtFlags & GTF_CLS_VAR_ASG_LHS) == 0)
7086 bool isVolatile = (tree->gtFlags & GTF_FLD_VOLATILE) != 0;
7090 // For Volatile indirection, first mutate GcHeap/ByrefExposed
7091 fgMutateGcHeap(tree DEBUGARG("GTF_FLD_VOLATILE - read"));
7094 // We just mutate GcHeap/ByrefExposed if isVolatile is true, and then do the read as normal.
7098 // 2: volatile read s;
7101 // We should never assume that the values read by 1 and 2 are the same (because the heap was mutated
7102 // in between them)... but we *should* be able to prove that the values read in 2 and 3 are the
7106 ValueNumPair clsVarVNPair;
7108 // If the static field handle is for a struct type field, then the value of the static
7109 // is a "ref" to the boxed struct -- treat it as the address of the static (we assume that a
7110 // first element offset will be added to get to the actual struct...)
7111 GenTreeClsVar* clsVar = tree->AsClsVar();
7112 FieldSeqNode* fldSeq = clsVar->gtFieldSeq;
7113 assert(fldSeq != nullptr); // We need to have one.
7114 ValueNum selectedStaticVar = ValueNumStore::NoVN;
7115 if (gtIsStaticFieldPtrToBoxedStruct(clsVar->TypeGet(), fldSeq->m_fieldHnd))
7117 clsVarVNPair.SetBoth(
7118 vnStore->VNForFunc(TYP_BYREF, VNF_PtrToStatic, vnStore->VNForFieldSeq(fldSeq)));
7122 // This is a reference to heap memory.
7123 // We model statics as indices into GcHeap (which is a subset of ByrefExposed).
7125 FieldSeqNode* fldSeqForStaticVar =
7126 GetFieldSeqStore()->CreateSingleton(tree->gtClsVar.gtClsVarHnd);
7127 size_t structSize = 0;
7128 selectedStaticVar = vnStore->VNApplySelectors(VNK_Liberal, fgCurMemoryVN[GcHeap],
7129 fldSeqForStaticVar, &structSize);
7131 vnStore->VNApplySelectorsTypeCheck(selectedStaticVar, tree->TypeGet(), structSize);
7133 clsVarVNPair.SetLiberal(selectedStaticVar);
7134 // The conservative interpretation always gets a new, unique VN.
7135 clsVarVNPair.SetConservative(vnStore->VNForExpr(compCurBB, tree->TypeGet()));
7138 // The ValueNum returned must represent the full-sized IL-Stack value
7139 // If we need to widen this value then we need to introduce a VNF_Cast here to represent
7140 // the widened value. This is necessary since the CSE package can replace all occurances
7141 // of a given ValueNum with a LclVar that is a full-sized IL-Stack value
7143 if (varTypeIsSmall(tree->TypeGet()))
7145 var_types castToType = tree->TypeGet();
7146 clsVarVNPair = vnStore->VNPairForCast(clsVarVNPair, castToType, castToType);
7148 tree->gtVNPair = clsVarVNPair;
7152 case GT_MEMORYBARRIER: // Leaf
7153 // For MEMORYBARRIER add an arbitrary side effect on GcHeap/ByrefExposed.
7154 fgMutateGcHeap(tree DEBUGARG("MEMORYBARRIER"));
7157 // These do not represent values.
7159 case GT_JMP: // Control flow
7160 case GT_LABEL: // Control flow
7161 #if !FEATURE_EH_FUNCLETS
7162 case GT_END_LFIN: // Control flow
7165 // This node is a standin for an argument whose value will be computed later. (Perhaps it's
7166 // a register argument, and we don't want to preclude use of the register in arg evaluation yet.)
7167 // We give this a "fake" value number now; if the call in which it occurs cares about the
7168 // value (e.g., it's a helper call whose result is a function of argument values) we'll reset
7169 // this later, when the later args have been assigned VNs.
7170 tree->gtVNPair.SetBoth(vnStore->VNForExpr(compCurBB, tree->TypeGet()));
7174 // This one is special because we should never process it in this method: it should
7175 // always be taken care of, when needed, during pre-processing of a blocks phi definitions.
7183 else if (GenTree::OperIsSimple(oper))
7186 // Sometimes we query the memory ssa map in an assertion, and need a dummy location for the ignored result.
7187 unsigned memorySsaNum;
7190 if ((oper == GT_ASG) && !varTypeIsStruct(tree))
7192 GenTree* lhs = tree->gtOp.gtOp1;
7193 GenTree* rhs = tree->gtOp.gtOp2;
7195 ValueNumPair rhsVNPair = rhs->gtVNPair;
7197 // Is the type being stored different from the type computed by the rhs?
7198 if (rhs->TypeGet() != lhs->TypeGet())
7200 // This means that there is an implicit cast on the rhs value
7202 // We will add a cast function to reflect the possible narrowing of the rhs value
7204 var_types castToType = lhs->TypeGet();
7205 var_types castFromType = rhs->TypeGet();
7206 bool isUnsigned = varTypeIsUnsigned(castFromType);
7208 rhsVNPair = vnStore->VNPairForCast(rhsVNPair, castToType, castFromType, isUnsigned);
7211 if (tree->TypeGet() != TYP_VOID)
7213 // Assignment operators, as expressions, return the value of the RHS.
7214 tree->gtVNPair = rhsVNPair;
7217 // Now that we've labeled the assignment as a whole, we don't care about exceptions.
7218 rhsVNPair = vnStore->VNPNormalPair(rhsVNPair);
7220 // Record the exeception set for this 'tree' in vnExcSet.
7221 // First we'll record the exeception set for the rhs and
7222 // later we will union in the exeception set for the lhs
7226 // Unpack, Norm,Exc for 'rhsVNPair'
7227 ValueNum vnRhsLibNorm;
7228 vnStore->VNUnpackExc(rhsVNPair.GetLiberal(), &vnRhsLibNorm, &vnExcSet);
7230 // Now that we've saved the rhs exeception set, we we will use the normal values.
7231 rhsVNPair = ValueNumPair(vnRhsLibNorm, vnStore->VNNormalValue(rhsVNPair.GetConservative()));
7233 // If the types of the rhs and lhs are different then we
7234 // may want to change the ValueNumber assigned to the lhs.
7236 if (rhs->TypeGet() != lhs->TypeGet())
7238 if (rhs->TypeGet() == TYP_REF)
7240 // If we have an unsafe IL assignment of a TYP_REF to a non-ref (typically a TYP_BYREF)
7241 // then don't propagate this ValueNumber to the lhs, instead create a new unique VN
7243 rhsVNPair.SetBoth(vnStore->VNForExpr(compCurBB, lhs->TypeGet()));
7247 // We have to handle the case where the LHS is a comma. In that case, we don't evaluate the comma,
7248 // so we give it VNForVoid, and we're really interested in the effective value.
7249 GenTree* lhsCommaIter = lhs;
7250 while (lhsCommaIter->OperGet() == GT_COMMA)
7252 lhsCommaIter->gtVNPair.SetBoth(vnStore->VNForVoid());
7253 lhsCommaIter = lhsCommaIter->gtOp.gtOp2;
7255 lhs = lhs->gtEffectiveVal();
7257 // Now, record the new VN for an assignment (performing the indicated "state update").
7258 // It's safe to use gtEffectiveVal here, because the non-last elements of a comma list on the
7259 // LHS will come before the assignment in evaluation order.
7260 switch (lhs->OperGet())
7264 GenTreeLclVarCommon* lcl = lhs->AsLclVarCommon();
7265 unsigned lclDefSsaNum = GetSsaNumForLocalVarDef(lcl);
7267 // Should not have been recorded as updating the GC heap.
7268 assert(!GetMemorySsaMap(GcHeap)->Lookup(tree, &memorySsaNum));
7270 if (lclDefSsaNum != SsaConfig::RESERVED_SSA_NUM)
7272 // Should not have been recorded as updating ByrefExposed mem.
7273 assert(!GetMemorySsaMap(ByrefExposed)->Lookup(tree, &memorySsaNum));
7275 assert(rhsVNPair.GetLiberal() != ValueNumStore::NoVN);
7277 lhs->gtVNPair = rhsVNPair;
7278 lvaTable[lcl->gtLclNum].GetPerSsaData(lclDefSsaNum)->m_vnPair = rhsVNPair;
7283 printf("N%03u ", lhs->gtSeqNum);
7284 Compiler::printTreeID(lhs);
7286 gtDispNodeName(lhs);
7287 gtDispLeaf(lhs, nullptr);
7289 vnpPrint(lhs->gtVNPair, 1);
7294 else if (lvaVarAddrExposed(lcl->gtLclNum))
7296 // We could use MapStore here and MapSelect on reads of address-exposed locals
7297 // (using the local nums as selectors) to get e.g. propagation of values
7298 // through address-taken locals in regions of code with no calls or byref
7300 // For now, just use a new opaque VN.
7301 ValueNum heapVN = vnStore->VNForExpr(compCurBB);
7302 recordAddressExposedLocalStore(tree, heapVN DEBUGARG("local assign"));
7310 Compiler::printTreeID(tree);
7311 printf(" assigns to non-address-taken local var V%02u; excluded from SSA, so value not "
7321 GenTreeLclFld* lclFld = lhs->AsLclFld();
7322 unsigned lclDefSsaNum = GetSsaNumForLocalVarDef(lclFld);
7324 // Should not have been recorded as updating the GC heap.
7325 assert(!GetMemorySsaMap(GcHeap)->Lookup(tree, &memorySsaNum));
7327 if (lclDefSsaNum != SsaConfig::RESERVED_SSA_NUM)
7329 ValueNumPair newLhsVNPair;
7330 // Is this a full definition?
7331 if ((lclFld->gtFlags & GTF_VAR_USEASG) == 0)
7333 assert(!lclFld->IsPartialLclFld(this));
7334 assert(rhsVNPair.GetLiberal() != ValueNumStore::NoVN);
7335 newLhsVNPair = rhsVNPair;
7339 // We should never have a null field sequence here.
7340 assert(lclFld->gtFieldSeq != nullptr);
7341 if (lclFld->gtFieldSeq == FieldSeqStore::NotAField())
7343 // We don't know what field this represents. Assign a new VN to the whole variable
7344 // (since we may be writing to an unknown portion of it.)
7345 newLhsVNPair.SetBoth(vnStore->VNForExpr(compCurBB, lvaGetActualType(lclFld->gtLclNum)));
7349 // We do know the field sequence.
7350 // The "lclFld" node will be labeled with the SSA number of its "use" identity
7351 // (we looked in a side table above for its "def" identity). Look up that value.
7352 ValueNumPair oldLhsVNPair =
7353 lvaTable[lclFld->GetLclNum()].GetPerSsaData(lclFld->GetSsaNum())->m_vnPair;
7354 newLhsVNPair = vnStore->VNPairApplySelectorsAssign(oldLhsVNPair, lclFld->gtFieldSeq,
7355 rhsVNPair, // Pre-value.
7356 lclFld->TypeGet(), compCurBB);
7359 lvaTable[lclFld->GetLclNum()].GetPerSsaData(lclDefSsaNum)->m_vnPair = newLhsVNPair;
7360 lhs->gtVNPair = newLhsVNPair;
7364 if (lhs->gtVNPair.GetLiberal() != ValueNumStore::NoVN)
7366 printf("N%03u ", lhs->gtSeqNum);
7367 Compiler::printTreeID(lhs);
7369 gtDispNodeName(lhs);
7370 gtDispLeaf(lhs, nullptr);
7372 vnpPrint(lhs->gtVNPair, 1);
7378 else if (lvaVarAddrExposed(lclFld->gtLclNum))
7380 // This side-effects ByrefExposed. Just use a new opaque VN.
7381 // As with GT_LCL_VAR, we could probably use MapStore here and MapSelect at corresponding
7382 // loads, but to do so would have to identify the subset of address-exposed locals
7383 // whose fields can be disambiguated.
7384 ValueNum heapVN = vnStore->VNForExpr(compCurBB);
7385 recordAddressExposedLocalStore(tree, heapVN DEBUGARG("local field assign"));
7391 noway_assert(!"Phi arg cannot be LHS.");
7396 noway_assert(!"GT_BLK/GT_OBJ can not be LHS when !varTypeIsStruct(tree) is true!");
7401 bool isVolatile = (lhs->gtFlags & GTF_IND_VOLATILE) != 0;
7405 // For Volatile store indirection, first mutate GcHeap/ByrefExposed
7406 fgMutateGcHeap(lhs DEBUGARG("GTF_IND_VOLATILE - store"));
7407 tree->gtVNPair.SetBoth(vnStore->VNForExpr(compCurBB, lhs->TypeGet()));
7410 GenTree* arg = lhs->gtOp.gtOp1;
7412 // Indicates whether the argument of the IND is the address of a local.
7413 bool wasLocal = false;
7415 lhs->gtVNPair = rhsVNPair;
7418 ValueNum argVN = arg->gtVNPair.GetLiberal();
7420 bool argIsVNFunc = vnStore->GetVNFunc(vnStore->VNNormalValue(argVN), &funcApp);
7422 // Is this an assignment to a (field of, perhaps) a local?
7423 // If it is a PtrToLoc, lib and cons VNs will be the same.
7426 if (funcApp.m_func == VNF_PtrToLoc)
7428 assert(arg->gtVNPair.BothEqual()); // If it's a PtrToLoc, lib/cons shouldn't differ.
7429 assert(vnStore->IsVNConstant(funcApp.m_args[0]));
7430 unsigned lclNum = vnStore->ConstantValue<unsigned>(funcApp.m_args[0]);
7434 if (lvaInSsa(lclNum))
7436 FieldSeqNode* fieldSeq = vnStore->FieldSeqVNToFieldSeq(funcApp.m_args[1]);
7438 // Either "arg" is the address of (part of) a local itself, or else we have
7439 // a "rogue" PtrToLoc, one that should have made the local in question
7440 // address-exposed. Assert on that.
7441 GenTreeLclVarCommon* lclVarTree = nullptr;
7442 bool isEntire = false;
7443 unsigned lclDefSsaNum = SsaConfig::RESERVED_SSA_NUM;
7444 ValueNumPair newLhsVNPair;
7446 if (arg->DefinesLocalAddr(this, genTypeSize(lhs->TypeGet()), &lclVarTree, &isEntire))
7448 // The local #'s should agree.
7449 assert(lclNum == lclVarTree->GetLclNum());
7451 if (fieldSeq == FieldSeqStore::NotAField())
7453 // We don't know where we're storing, so give the local a new, unique VN.
7454 // Do this by considering it an "entire" assignment, with an unknown RHS.
7456 rhsVNPair.SetBoth(vnStore->VNForExpr(compCurBB, lclVarTree->TypeGet()));
7461 newLhsVNPair = rhsVNPair;
7462 lclDefSsaNum = lclVarTree->GetSsaNum();
7466 // Don't use the lclVarTree's VN: if it's a local field, it will
7467 // already be dereferenced by it's field sequence.
7468 ValueNumPair oldLhsVNPair = lvaTable[lclVarTree->GetLclNum()]
7469 .GetPerSsaData(lclVarTree->GetSsaNum())
7471 lclDefSsaNum = GetSsaNumForLocalVarDef(lclVarTree);
7473 vnStore->VNPairApplySelectorsAssign(oldLhsVNPair, fieldSeq, rhsVNPair,
7474 lhs->TypeGet(), compCurBB);
7476 lvaTable[lclNum].GetPerSsaData(lclDefSsaNum)->m_vnPair = newLhsVNPair;
7480 unreached(); // "Rogue" PtrToLoc, as discussed above.
7486 Compiler::printTreeID(tree);
7487 printf(" assigned VN to local var V%02u/%d: VN ", lclNum, lclDefSsaNum);
7488 vnpPrint(newLhsVNPair, 1);
7493 else if (lvaVarAddrExposed(lclNum))
7495 // Need to record the effect on ByrefExposed.
7496 // We could use MapStore here and MapSelect on reads of address-exposed locals
7497 // (using the local nums as selectors) to get e.g. propagation of values
7498 // through address-taken locals in regions of code with no calls or byref
7500 // For now, just use a new opaque VN.
7501 ValueNum heapVN = vnStore->VNForExpr(compCurBB);
7502 recordAddressExposedLocalStore(tree, heapVN DEBUGARG("PtrToLoc indir"));
7507 // Was the argument of the GT_IND the address of a local, handled above?
7510 GenTree* obj = nullptr;
7511 GenTree* staticOffset = nullptr;
7512 FieldSeqNode* fldSeq = nullptr;
7514 // Is the LHS an array index expression?
7515 if (argIsVNFunc && funcApp.m_func == VNF_PtrToArrElem)
7517 CORINFO_CLASS_HANDLE elemTypeEq =
7518 CORINFO_CLASS_HANDLE(vnStore->ConstantValue<ssize_t>(funcApp.m_args[0]));
7519 ValueNum arrVN = funcApp.m_args[1];
7520 ValueNum inxVN = funcApp.m_args[2];
7521 FieldSeqNode* fldSeq = vnStore->FieldSeqVNToFieldSeq(funcApp.m_args[3]);
7523 if (arg->gtOper != GT_LCL_VAR)
7525 // Does the child of the GT_IND 'arg' have an associated zero-offset field sequence?
7526 FieldSeqNode* addrFieldSeq = nullptr;
7527 if (GetZeroOffsetFieldMap()->Lookup(arg, &addrFieldSeq))
7529 fldSeq = GetFieldSeqStore()->Append(addrFieldSeq, fldSeq);
7537 Compiler::printTreeID(tree);
7538 printf(" assigns to an array element:\n");
7542 ValueNum heapVN = fgValueNumberArrIndexAssign(elemTypeEq, arrVN, inxVN, fldSeq,
7543 rhsVNPair.GetLiberal(), lhs->TypeGet());
7544 recordGcHeapStore(tree, heapVN DEBUGARG("ArrIndexAssign (case 1)"));
7546 // It may be that we haven't parsed it yet. Try.
7547 else if (lhs->gtFlags & GTF_IND_ARR_INDEX)
7550 bool b = GetArrayInfoMap()->Lookup(lhs, &arrInfo);
7552 ValueNum arrVN = ValueNumStore::NoVN;
7553 ValueNum inxVN = ValueNumStore::NoVN;
7554 FieldSeqNode* fldSeq = nullptr;
7557 GenTree* arr = nullptr;
7558 arg->ParseArrayAddress(this, &arrInfo, &arr, &inxVN, &fldSeq);
7561 fgMutateGcHeap(tree DEBUGARG("assignment to unparseable array expression"));
7564 // Otherwise, parsing succeeded.
7566 // Need to form H[arrType][arr][ind][fldSeq] = rhsVNPair.GetLiberal()
7568 // Get the element type equivalence class representative.
7569 CORINFO_CLASS_HANDLE elemTypeEq =
7570 EncodeElemType(arrInfo.m_elemType, arrInfo.m_elemStructType);
7571 arrVN = arr->gtVNPair.GetLiberal();
7573 FieldSeqNode* zeroOffsetFldSeq = nullptr;
7574 if (GetZeroOffsetFieldMap()->Lookup(arg, &zeroOffsetFldSeq))
7576 fldSeq = GetFieldSeqStore()->Append(fldSeq, zeroOffsetFldSeq);
7579 ValueNum heapVN = fgValueNumberArrIndexAssign(elemTypeEq, arrVN, inxVN, fldSeq,
7580 rhsVNPair.GetLiberal(), lhs->TypeGet());
7581 recordGcHeapStore(tree, heapVN DEBUGARG("ArrIndexAssign (case 2)"));
7583 else if (arg->IsFieldAddr(this, &obj, &staticOffset, &fldSeq))
7585 if (fldSeq == FieldSeqStore::NotAField())
7587 fgMutateGcHeap(tree DEBUGARG("NotAField"));
7591 assert(fldSeq != nullptr);
7593 CORINFO_CLASS_HANDLE fldCls = info.compCompHnd->getFieldClass(fldSeq->m_fieldHnd);
7596 // Make sure that the class containing it is not a value class (as we are expecting
7597 // an instance field)
7598 assert((info.compCompHnd->getClassAttribs(fldCls) & CORINFO_FLG_VALUECLASS) == 0);
7599 assert(staticOffset == nullptr);
7603 // Get the first (instance or static) field from field seq. GcHeap[field] will yield
7605 if (fldSeq->IsFirstElemFieldSeq())
7607 fldSeq = fldSeq->m_next;
7608 assert(fldSeq != nullptr);
7611 // Get a field sequence for just the first field in the sequence
7613 FieldSeqNode* firstFieldOnly = GetFieldSeqStore()->CreateSingleton(fldSeq->m_fieldHnd);
7615 // The final field in the sequence will need to match the 'indType'
7616 var_types indType = lhs->TypeGet();
7618 vnStore->VNApplySelectors(VNK_Liberal, fgCurMemoryVN[GcHeap], firstFieldOnly);
7620 // The type of the field is "struct" if there are more fields in the sequence,
7621 // otherwise it is the type returned from VNApplySelectors above.
7622 var_types firstFieldType = vnStore->TypeOfVN(fldMapVN);
7624 // The value number from the rhs of the assignment
7625 ValueNum storeVal = rhsVNPair.GetLiberal();
7626 ValueNum newFldMapVN = ValueNumStore::NoVN;
7628 // when (obj != nullptr) we have an instance field, otherwise a static field
7629 // when (staticOffset != nullptr) it represents a offset into a static or the call to
7630 // Shared Static Base
7631 if ((obj != nullptr) || (staticOffset != nullptr))
7633 ValueNum valAtAddr = fldMapVN;
7634 ValueNum normVal = ValueNumStore::NoVN;
7638 // Unpack, Norm,Exc for 'obj'
7639 ValueNum vnObjExcSet;
7640 vnStore->VNUnpackExc(obj->gtVNPair.GetLiberal(), &normVal, &vnObjExcSet);
7641 vnExcSet = vnStore->VNExcSetUnion(vnExcSet, vnObjExcSet);
7643 // construct the ValueNumber for 'fldMap at obj'
7645 vnStore->VNForMapSelect(VNK_Liberal, firstFieldType, fldMapVN, normVal);
7647 else // (staticOffset != nullptr)
7649 // construct the ValueNumber for 'fldMap at staticOffset'
7650 normVal = vnStore->VNLiberalNormalValue(staticOffset->gtVNPair);
7652 vnStore->VNForMapSelect(VNK_Liberal, firstFieldType, fldMapVN, normVal);
7654 // Now get rid of any remaining struct field dereferences. (if they exist)
7658 vnStore->VNApplySelectorsAssign(VNK_Liberal, valAtAddr, fldSeq->m_next,
7659 storeVal, indType, compCurBB);
7662 // From which we can construct the new ValueNumber for 'fldMap at normVal'
7663 newFldMapVN = vnStore->VNForMapStore(vnStore->TypeOfVN(fldMapVN), fldMapVN, normVal,
7668 // plain static field
7670 // Now get rid of any remaining struct field dereferences. (if they exist)
7674 vnStore->VNApplySelectorsAssign(VNK_Liberal, fldMapVN, fldSeq->m_next,
7675 storeVal, indType, compCurBB);
7678 newFldMapVN = vnStore->VNApplySelectorsAssign(VNK_Liberal, fgCurMemoryVN[GcHeap],
7679 fldSeq, storeVal, indType, compCurBB);
7682 // It is not strictly necessary to set the lhs value number,
7683 // but the dumps read better with it set to the 'storeVal' that we just computed
7684 lhs->gtVNPair.SetBoth(storeVal);
7686 // Update the field map for firstField in GcHeap to this new value.
7688 vnStore->VNApplySelectorsAssign(VNK_Liberal, fgCurMemoryVN[GcHeap], firstFieldOnly,
7689 newFldMapVN, indType, compCurBB);
7691 recordGcHeapStore(tree, heapVN DEBUGARG("StoreField"));
7696 GenTreeLclVarCommon* lclVarTree = nullptr;
7697 bool isLocal = tree->DefinesLocal(this, &lclVarTree);
7699 if (isLocal && lvaVarAddrExposed(lclVarTree->gtLclNum))
7701 // Store to address-exposed local; need to record the effect on ByrefExposed.
7702 // We could use MapStore here and MapSelect on reads of address-exposed locals
7703 // (using the local nums as selectors) to get e.g. propagation of values
7704 // through address-taken locals in regions of code with no calls or byref
7706 // For now, just use a new opaque VN.
7707 ValueNum memoryVN = vnStore->VNForExpr(compCurBB);
7708 recordAddressExposedLocalStore(tree, memoryVN DEBUGARG("PtrToLoc indir"));
7712 // If it doesn't define a local, then it might update GcHeap/ByrefExposed.
7713 // For the new ByrefExposed VN, we could use an operator here like
7714 // VNF_ByrefExposedStore that carries the VNs of the pointer and RHS, then
7715 // at byref loads if the current ByrefExposed VN happens to be
7716 // VNF_ByrefExposedStore with the same pointer VN, we could propagate the
7717 // VN from the RHS to the VN for the load. This would e.g. allow tracking
7718 // values through assignments to out params. For now, just model this
7719 // as an opaque GcHeap/ByrefExposed mutation.
7720 fgMutateGcHeap(tree DEBUGARG("assign-of-IND"));
7725 // We don't actually evaluate an IND on the LHS, so give it the Void value.
7726 tree->gtVNPair.SetBoth(vnStore->VNForVoid());
7732 bool isVolatile = (lhs->gtFlags & GTF_FLD_VOLATILE) != 0;
7736 // For Volatile store indirection, first mutate GcHeap/ByrefExposed
7737 fgMutateGcHeap(lhs DEBUGARG("GTF_CLS_VAR - store")); // always change fgCurMemoryVN
7740 // We model statics as indices into GcHeap (which is a subset of ByrefExposed).
7741 FieldSeqNode* fldSeqForStaticVar = GetFieldSeqStore()->CreateSingleton(lhs->gtClsVar.gtClsVarHnd);
7742 assert(fldSeqForStaticVar != FieldSeqStore::NotAField());
7744 ValueNum storeVal = rhsVNPair.GetLiberal(); // The value number from the rhs of the assignment
7745 storeVal = vnStore->VNApplySelectorsAssign(VNK_Liberal, fgCurMemoryVN[GcHeap], fldSeqForStaticVar,
7746 storeVal, lhs->TypeGet(), compCurBB);
7748 // It is not strictly necessary to set the lhs value number,
7749 // but the dumps read better with it set to the 'storeVal' that we just computed
7750 lhs->gtVNPair.SetBoth(storeVal);
7752 // bbMemoryDef must include GcHeap for any block that mutates the GC heap
7753 assert((compCurBB->bbMemoryDef & memoryKindSet(GcHeap)) != 0);
7755 // Update the field map for the fgCurMemoryVN and SSA for the tree
7756 recordGcHeapStore(tree, storeVal DEBUGARG("Static Field store"));
7761 assert(!"Unknown node for lhs of assignment!");
7763 // For Unknown stores, mutate GcHeap/ByrefExposed
7764 fgMutateGcHeap(lhs DEBUGARG("Unkwown Assignment - store")); // always change fgCurMemoryVN
7768 // Other kinds of assignment: initblk and copyblk.
7769 else if (oper == GT_ASG && varTypeIsStruct(tree))
7771 fgValueNumberBlockAssignment(tree);
7773 else if (oper == GT_ADDR)
7775 // We have special representations for byrefs to lvalues.
7776 GenTree* arg = tree->gtOp.gtOp1;
7777 if (arg->OperIsLocal())
7779 FieldSeqNode* fieldSeq = nullptr;
7780 ValueNum newVN = ValueNumStore::NoVN;
7781 if (!lvaInSsa(arg->gtLclVarCommon.GetLclNum()))
7783 newVN = vnStore->VNForExpr(compCurBB, TYP_BYREF);
7785 else if (arg->OperGet() == GT_LCL_FLD)
7787 fieldSeq = arg->AsLclFld()->gtFieldSeq;
7788 if (fieldSeq == nullptr)
7790 // Local field with unknown field seq -- not a precise pointer.
7791 newVN = vnStore->VNForExpr(compCurBB, TYP_BYREF);
7794 if (newVN == ValueNumStore::NoVN)
7796 assert(arg->gtLclVarCommon.GetSsaNum() != ValueNumStore::NoVN);
7797 newVN = vnStore->VNForFunc(TYP_BYREF, VNF_PtrToLoc,
7798 vnStore->VNForIntCon(arg->gtLclVarCommon.GetLclNum()),
7799 vnStore->VNForFieldSeq(fieldSeq));
7801 tree->gtVNPair.SetBoth(newVN);
7803 else if ((arg->gtOper == GT_IND) || arg->OperIsBlk())
7805 // Usually the ADDR and IND just cancel out...
7806 // except when this GT_ADDR has a valid zero-offset field sequence
7808 FieldSeqNode* zeroOffsetFieldSeq = nullptr;
7809 if (GetZeroOffsetFieldMap()->Lookup(tree, &zeroOffsetFieldSeq) &&
7810 (zeroOffsetFieldSeq != FieldSeqStore::NotAField()))
7812 ValueNum addrExtended = vnStore->ExtendPtrVN(arg->gtOp.gtOp1, zeroOffsetFieldSeq);
7813 if (addrExtended != ValueNumStore::NoVN)
7815 tree->gtVNPair.SetBoth(addrExtended); // We don't care about lib/cons differences for addresses.
7819 // ExtendPtrVN returned a failure result
7820 // So give this address a new unique value
7821 tree->gtVNPair.SetBoth(vnStore->VNForExpr(compCurBB, TYP_BYREF));
7826 // They just cancel, so fetch the ValueNumber from the op1 of the GT_IND node.
7828 GenTree* addr = arg->AsIndir()->Addr();
7829 tree->gtVNPair = addr->gtVNPair;
7831 // For the CSE phase mark the address as GTF_DONT_CSE
7832 // because it will end up with the same value number as tree (the GT_ADDR).
7833 addr->gtFlags |= GTF_DONT_CSE;
7838 // May be more cases to do here! But we'll punt for now.
7839 tree->gtVNPair.SetBoth(vnStore->VNForExpr(compCurBB, TYP_BYREF));
7842 else if ((oper == GT_IND) || GenTree::OperIsBlk(oper))
7844 // So far, we handle cases in which the address is a ptr-to-local, or if it's
7845 // a pointer to an object field or array element. Other cases become uses of
7846 // the current ByrefExposed value and the pointer value, so that at least we
7847 // can recognize redundant loads with no stores between them.
7848 GenTree* addr = tree->AsIndir()->Addr();
7849 GenTreeLclVarCommon* lclVarTree = nullptr;
7850 FieldSeqNode* fldSeq2 = nullptr;
7851 GenTree* obj = nullptr;
7852 GenTree* staticOffset = nullptr;
7853 bool isVolatile = (tree->gtFlags & GTF_IND_VOLATILE) != 0;
7855 // See if the addr has any exceptional part.
7856 ValueNumPair addrNvnp;
7857 ValueNumPair addrXvnp;
7858 vnStore->VNPUnpackExc(addr->gtVNPair, &addrNvnp, &addrXvnp);
7860 // Is the dereference immutable? If so, model it as referencing the read-only heap.
7861 if (tree->gtFlags & GTF_IND_INVARIANT)
7863 assert(!isVolatile); // We don't expect both volatile and invariant
7865 ValueNumPair(vnStore->VNForMapSelect(VNK_Liberal, TYP_REF, ValueNumStore::VNForROH(),
7866 addrNvnp.GetLiberal()),
7867 vnStore->VNForMapSelect(VNK_Conservative, TYP_REF, ValueNumStore::VNForROH(),
7868 addrNvnp.GetConservative()));
7869 tree->gtVNPair = vnStore->VNPWithExc(tree->gtVNPair, addrXvnp);
7871 else if (isVolatile)
7873 // For Volatile indirection, mutate GcHeap/ByrefExposed
7874 fgMutateGcHeap(tree DEBUGARG("GTF_IND_VOLATILE - read"));
7876 // The value read by the GT_IND can immediately change
7877 ValueNum newUniq = vnStore->VNForExpr(compCurBB, tree->TypeGet());
7878 tree->gtVNPair = vnStore->VNPWithExc(ValueNumPair(newUniq, newUniq), addrXvnp);
7880 // We always want to evaluate the LHS when the GT_IND node is marked with GTF_IND_ARR_INDEX
7881 // as this will relabel the GT_IND child correctly using the VNF_PtrToArrElem
7882 else if ((tree->gtFlags & GTF_IND_ARR_INDEX) != 0)
7885 bool b = GetArrayInfoMap()->Lookup(tree, &arrInfo);
7888 ValueNum inxVN = ValueNumStore::NoVN;
7889 FieldSeqNode* fldSeq = nullptr;
7892 GenTree* arr = nullptr;
7893 addr->ParseArrayAddress(this, &arrInfo, &arr, &inxVN, &fldSeq);
7896 tree->gtVNPair.SetBoth(vnStore->VNForExpr(compCurBB, tree->TypeGet()));
7899 assert(fldSeq != FieldSeqStore::NotAField());
7902 // Need to form H[arrType][arr][ind][fldSeq]
7903 // Get the array element type equivalence class rep.
7904 CORINFO_CLASS_HANDLE elemTypeEq = EncodeElemType(arrInfo.m_elemType, arrInfo.m_elemStructType);
7905 ValueNum elemTypeEqVN = vnStore->VNForHandle(ssize_t(elemTypeEq), GTF_ICON_CLASS_HDL);
7906 JITDUMP(" VNForHandle(arrElemType: %s) is " FMT_VN "\n",
7907 (arrInfo.m_elemType == TYP_STRUCT) ? eeGetClassName(arrInfo.m_elemStructType)
7908 : varTypeName(arrInfo.m_elemType),
7911 // We take the "VNNormalValue"s here, because if either has exceptional outcomes, they will be captured
7912 // as part of the value of the composite "addr" operation...
7913 ValueNum arrVN = vnStore->VNLiberalNormalValue(arr->gtVNPair);
7914 inxVN = vnStore->VNNormalValue(inxVN);
7916 // Additionally, relabel the address with a PtrToArrElem value number.
7917 ValueNum fldSeqVN = vnStore->VNForFieldSeq(fldSeq);
7919 vnStore->VNForFunc(TYP_BYREF, VNF_PtrToArrElem, elemTypeEqVN, arrVN, inxVN, fldSeqVN);
7921 // The aggregate "addr" VN should have had all the exceptions bubble up...
7922 elemAddr = vnStore->VNWithExc(elemAddr, addrXvnp.GetLiberal());
7923 addr->gtVNPair.SetBoth(elemAddr);
7927 printf(" Relabeled IND_ARR_INDEX address node ");
7928 Compiler::printTreeID(addr);
7929 printf(" with l:" FMT_VN ": ", elemAddr);
7930 vnStore->vnDump(this, elemAddr);
7932 if (vnStore->VNNormalValue(elemAddr) != elemAddr)
7934 printf(" [" FMT_VN " is: ", vnStore->VNNormalValue(elemAddr));
7935 vnStore->vnDump(this, vnStore->VNNormalValue(elemAddr));
7941 // We now need to retrieve the value number for the array element value
7942 // and give this value number to the GT_IND node 'tree'
7943 // We do this whenever we have an rvalue, but we don't do it for a
7944 // normal LHS assignment into an array element.
7946 if ((tree->gtFlags & GTF_IND_ASG_LHS) == 0)
7948 fgValueNumberArrIndexVal(tree, elemTypeEq, arrVN, inxVN, addrXvnp.GetLiberal(), fldSeq);
7951 // In general we skip GT_IND nodes on that are the LHS of an assignment. (We labeled these earlier.)
7952 // We will "evaluate" this as part of the assignment.
7953 else if ((tree->gtFlags & GTF_IND_ASG_LHS) == 0)
7955 FieldSeqNode* localFldSeq = nullptr;
7958 // Is it a local or a heap address?
7959 if (addr->IsLocalAddrExpr(this, &lclVarTree, &localFldSeq) && lvaInSsa(lclVarTree->GetLclNum()))
7961 unsigned lclNum = lclVarTree->GetLclNum();
7962 unsigned ssaNum = lclVarTree->GetSsaNum();
7963 LclVarDsc* varDsc = &lvaTable[lclNum];
7965 if ((localFldSeq == FieldSeqStore::NotAField()) || (localFldSeq == nullptr))
7967 tree->gtVNPair.SetBoth(vnStore->VNForExpr(compCurBB, tree->TypeGet()));
7971 var_types indType = tree->TypeGet();
7972 ValueNumPair lclVNPair = varDsc->GetPerSsaData(ssaNum)->m_vnPair;
7973 tree->gtVNPair = vnStore->VNPairApplySelectors(lclVNPair, localFldSeq, indType);
7976 tree->gtVNPair = vnStore->VNPWithExc(tree->gtVNPair, addrXvnp);
7978 else if (vnStore->GetVNFunc(addrNvnp.GetLiberal(), &funcApp) && funcApp.m_func == VNF_PtrToStatic)
7980 var_types indType = tree->TypeGet();
7981 ValueNum fieldSeqVN = funcApp.m_args[0];
7983 FieldSeqNode* fldSeqForStaticVar = vnStore->FieldSeqVNToFieldSeq(fieldSeqVN);
7985 if (fldSeqForStaticVar != FieldSeqStore::NotAField())
7987 ValueNum selectedStaticVar;
7988 // We model statics as indices into the GcHeap (which is a subset of ByrefExposed).
7989 size_t structSize = 0;
7990 selectedStaticVar = vnStore->VNApplySelectors(VNK_Liberal, fgCurMemoryVN[GcHeap],
7991 fldSeqForStaticVar, &structSize);
7992 selectedStaticVar = vnStore->VNApplySelectorsTypeCheck(selectedStaticVar, indType, structSize);
7994 tree->gtVNPair.SetLiberal(selectedStaticVar);
7995 tree->gtVNPair.SetConservative(vnStore->VNForExpr(compCurBB, indType));
7999 JITDUMP(" *** Missing field sequence info for VNF_PtrToStatic value GT_IND\n");
8000 tree->gtVNPair.SetBoth(vnStore->VNForExpr(compCurBB, indType)); // a new unique value number
8002 tree->gtVNPair = vnStore->VNPWithExc(tree->gtVNPair, addrXvnp);
8004 else if (vnStore->GetVNFunc(addrNvnp.GetLiberal(), &funcApp) && (funcApp.m_func == VNF_PtrToArrElem))
8006 fgValueNumberArrIndexVal(tree, &funcApp, addrXvnp.GetLiberal());
8008 else if (addr->IsFieldAddr(this, &obj, &staticOffset, &fldSeq2))
8010 if (fldSeq2 == FieldSeqStore::NotAField())
8012 tree->gtVNPair.SetBoth(vnStore->VNForExpr(compCurBB, tree->TypeGet()));
8014 else if (fldSeq2 != nullptr)
8016 // Get the first (instance or static) field from field seq. GcHeap[field] will yield the "field
8018 CLANG_FORMAT_COMMENT_ANCHOR;
8021 CORINFO_CLASS_HANDLE fldCls = info.compCompHnd->getFieldClass(fldSeq2->m_fieldHnd);
8024 // Make sure that the class containing it is not a value class (as we are expecting an
8026 assert((info.compCompHnd->getClassAttribs(fldCls) & CORINFO_FLG_VALUECLASS) == 0);
8027 assert(staticOffset == nullptr);
8031 // Get a field sequence for just the first field in the sequence
8033 FieldSeqNode* firstFieldOnly = GetFieldSeqStore()->CreateSingleton(fldSeq2->m_fieldHnd);
8034 size_t structSize = 0;
8036 vnStore->VNApplySelectors(VNK_Liberal, fgCurMemoryVN[GcHeap], firstFieldOnly, &structSize);
8038 // The final field in the sequence will need to match the 'indType'
8039 var_types indType = tree->TypeGet();
8041 // The type of the field is "struct" if there are more fields in the sequence,
8042 // otherwise it is the type returned from VNApplySelectors above.
8043 var_types firstFieldType = vnStore->TypeOfVN(fldMapVN);
8045 ValueNum valAtAddr = fldMapVN;
8048 // construct the ValueNumber for 'fldMap at obj'
8049 ValueNum objNormVal = vnStore->VNLiberalNormalValue(obj->gtVNPair);
8050 valAtAddr = vnStore->VNForMapSelect(VNK_Liberal, firstFieldType, fldMapVN, objNormVal);
8052 else if (staticOffset != nullptr)
8054 // construct the ValueNumber for 'fldMap at staticOffset'
8055 ValueNum offsetNormVal = vnStore->VNLiberalNormalValue(staticOffset->gtVNPair);
8056 valAtAddr = vnStore->VNForMapSelect(VNK_Liberal, firstFieldType, fldMapVN, offsetNormVal);
8059 // Now get rid of any remaining struct field dereferences.
8060 if (fldSeq2->m_next)
8062 valAtAddr = vnStore->VNApplySelectors(VNK_Liberal, valAtAddr, fldSeq2->m_next, &structSize);
8064 valAtAddr = vnStore->VNApplySelectorsTypeCheck(valAtAddr, indType, structSize);
8066 tree->gtVNPair.SetLiberal(valAtAddr);
8068 // The conservative value is a new, unique VN.
8069 tree->gtVNPair.SetConservative(vnStore->VNForExpr(compCurBB, tree->TypeGet()));
8070 tree->gtVNPair = vnStore->VNPWithExc(tree->gtVNPair, addrXvnp);
8074 // Occasionally we do an explicit null test on a REF, so we just dereference it with no
8075 // field sequence. The result is probably unused.
8076 tree->gtVNPair.SetBoth(vnStore->VNForExpr(compCurBB, tree->TypeGet()));
8077 tree->gtVNPair = vnStore->VNPWithExc(tree->gtVNPair, addrXvnp);
8080 else // We don't know where the address points, so it is an ByrefExposed load.
8082 ValueNum addrVN = addr->gtVNPair.GetLiberal();
8083 ValueNum loadVN = fgValueNumberByrefExposedLoad(typ, addrVN);
8084 tree->gtVNPair.SetLiberal(loadVN);
8085 tree->gtVNPair.SetConservative(vnStore->VNForExpr(compCurBB, tree->TypeGet()));
8086 tree->gtVNPair = vnStore->VNPWithExc(tree->gtVNPair, addrXvnp);
8090 else if (tree->OperGet() == GT_CAST)
8092 fgValueNumberCastTree(tree);
8094 else if (tree->OperGet() == GT_INTRINSIC)
8096 fgValueNumberIntrinsic(tree);
8098 else // Look up the VNFunc for the node
8100 VNFunc vnf = GetVNFuncForNode(tree);
8102 if (ValueNumStore::VNFuncIsLegal(vnf))
8104 if (GenTree::OperIsUnary(oper))
8106 if (tree->gtOp.gtOp1 != nullptr)
8108 if (tree->OperGet() == GT_NOP)
8110 // Pass through arg vn.
8111 tree->gtVNPair = tree->gtOp.gtOp1->gtVNPair;
8115 ValueNumPair op1VNP;
8116 ValueNumPair op1VNPx;
8117 vnStore->VNPUnpackExc(tree->gtOp.gtOp1->gtVNPair, &op1VNP, &op1VNPx);
8119 // If we are fetching the array length for an array ref that came from global memory
8120 // then for CSE safety we must use the conservative value number for both
8122 if ((tree->OperGet() == GT_ARR_LENGTH) && ((tree->gtOp.gtOp1->gtFlags & GTF_GLOB_REF) != 0))
8124 // use the conservative value number for both when computing the VN for the ARR_LENGTH
8125 op1VNP.SetBoth(op1VNP.GetConservative());
8129 vnStore->VNPWithExc(vnStore->VNPairForFunc(tree->TypeGet(), vnf, op1VNP), op1VNPx);
8132 else // Is actually nullary.
8134 // Mostly we'll leave these without a value number, assuming we'll detect these as VN failures
8135 // if they actually need to have values. With the exception of NOPs, which can sometimes have
8137 if (tree->OperGet() == GT_NOP)
8139 tree->gtVNPair.SetBoth(vnStore->VNForExpr(compCurBB, tree->TypeGet()));
8143 else // we have a binary oper
8145 assert(oper != GT_ASG); // We handled assignments earlier.
8146 assert(GenTree::OperIsBinary(oper));
8147 // Standard binary operator.
8148 ValueNumPair op2VNPair;
8149 if (tree->gtOp.gtOp2 == nullptr)
8151 // Handle any GT_LIST nodes as they can have a nullptr for op2.
8152 op2VNPair.SetBoth(ValueNumStore::VNForNull());
8156 op2VNPair = tree->gtOp.gtOp2->gtVNPair;
8159 // Handle a few special cases: if we add a field offset constant to a PtrToXXX, we will get back a
8163 ValueNumPair op1vnp;
8164 ValueNumPair op1Xvnp;
8165 vnStore->VNPUnpackExc(tree->gtOp.gtOp1->gtVNPair, &op1vnp, &op1Xvnp);
8167 ValueNumPair op2vnp;
8168 ValueNumPair op2Xvnp;
8169 vnStore->VNPUnpackExc(op2VNPair, &op2vnp, &op2Xvnp);
8170 ValueNumPair excSet = vnStore->VNPExcSetUnion(op1Xvnp, op2Xvnp);
8172 ValueNum newVN = ValueNumStore::NoVN;
8174 // Check for the addition of a field offset constant
8176 if ((oper == GT_ADD) && (!tree->gtOverflowEx()))
8178 newVN = vnStore->ExtendPtrVN(tree->gtOp.gtOp1, tree->gtOp.gtOp2);
8181 if (newVN != ValueNumStore::NoVN)
8183 // We don't care about differences between liberal and conservative for pointer values.
8184 newVN = vnStore->VNWithExc(newVN, excSet.GetLiberal());
8185 tree->gtVNPair.SetBoth(newVN);
8189 VNFunc vnf = GetVNFuncForNode(tree);
8190 ValueNumPair normalPair = vnStore->VNPairForFunc(tree->TypeGet(), vnf, op1vnp, op2vnp);
8191 tree->gtVNPair = vnStore->VNPWithExc(normalPair, excSet);
8192 // For overflow checking operations the VNF_OverflowExc will be added below
8193 // by fgValueNumberAddExceptionSet
8197 else // ValueNumStore::VNFuncIsLegal returns false
8199 // Some of the genTreeOps that aren't legal VNFuncs so they get special handling.
8204 ValueNumPair op1vnp;
8205 ValueNumPair op1Xvnp;
8206 vnStore->VNPUnpackExc(tree->gtOp.gtOp1->gtVNPair, &op1vnp, &op1Xvnp);
8207 ValueNumPair op2vnp;
8208 ValueNumPair op2Xvnp = ValueNumStore::VNPForEmptyExcSet();
8209 GenTree* op2 = tree->gtGetOp2();
8211 if (op2->OperIsIndir() && ((op2->gtFlags & GTF_IND_ASG_LHS) != 0))
8213 // If op2 represents the lhs of an assignment then we give a VNForVoid for the lhs
8214 op2vnp = ValueNumPair(ValueNumStore::VNForVoid(), ValueNumStore::VNForVoid());
8216 else if ((op2->OperGet() == GT_CLS_VAR) && (op2->gtFlags & GTF_CLS_VAR_ASG_LHS))
8218 // If op2 represents the lhs of an assignment then we give a VNForVoid for the lhs
8219 op2vnp = ValueNumPair(ValueNumStore::VNForVoid(), ValueNumStore::VNForVoid());
8223 vnStore->VNPUnpackExc(op2->gtVNPair, &op2vnp, &op2Xvnp);
8225 tree->gtVNPair = vnStore->VNPWithExc(op2vnp, vnStore->VNPExcSetUnion(op1Xvnp, op2Xvnp));
8231 // An Explicit null check, produces no value
8232 // But we do persist any execeptions produced by op1
8234 tree->gtVNPair = vnStore->VNPWithExc(vnStore->VNPForVoid(),
8235 vnStore->VNPExceptionSet(tree->gtOp.gtOp1->gtVNPair));
8236 // The exception set with VNF_NullPtrExc will be added below
8237 // by fgValueNumberAddExceptionSet
8241 case GT_LOCKADD: // Binop
8242 noway_assert("LOCKADD should not appear before lowering");
8245 case GT_XADD: // Binop
8246 case GT_XCHG: // Binop
8248 // For XADD and XCHG other intrinsics add an arbitrary side effect on GcHeap/ByrefExposed.
8249 fgMutateGcHeap(tree DEBUGARG("Interlocked intrinsic"));
8251 assert(tree->OperIsImplicitIndir()); // special node with an implicit indirections
8253 GenTree* addr = tree->gtOp.gtOp1; // op1
8254 GenTree* data = tree->gtOp.gtOp2; // op2
8256 ValueNumPair vnpExcSet = ValueNumStore::VNPForEmptyExcSet();
8258 vnpExcSet = vnStore->VNPUnionExcSet(data->gtVNPair, vnpExcSet);
8259 vnpExcSet = vnStore->VNPUnionExcSet(addr->gtVNPair, vnpExcSet);
8261 // The normal value is a new unique VN.
8262 ValueNumPair normalPair;
8263 normalPair.SetBoth(vnStore->VNForExpr(compCurBB, tree->TypeGet()));
8265 // Attach the combined exception set
8266 tree->gtVNPair = vnStore->VNPWithExc(normalPair, vnpExcSet);
8268 // add the null check exception for 'addr' to the tree's value number
8269 fgValueNumberAddExceptionSetForIndirection(tree, addr);
8275 // These nodes never need to have a ValueNumber
8276 tree->gtVNPair.SetBoth(ValueNumStore::NoVN);
8280 // BOX doesn't do anything at this point, the actual object allocation
8281 // and initialization happens separately (and not numbering BOX correctly
8282 // prevents seeing allocation related assertions through it)
8283 tree->gtVNPair = tree->gtGetOp1()->gtVNPair;
8287 // The default action is to give the node a new, unique VN.
8288 tree->gtVNPair.SetBoth(vnStore->VNForExpr(compCurBB, tree->TypeGet()));
8294 // next we add any exception sets for the current tree node
8295 fgValueNumberAddExceptionSet(tree);
8299 assert(GenTree::OperIsSpecial(oper));
8301 // TBD: We must handle these individually. For now:
8305 fgValueNumberCall(tree->AsCall());
8308 case GT_ARR_BOUNDS_CHECK:
8311 #endif // FEATURE_SIMD
8312 #ifdef FEATURE_HW_INTRINSICS
8313 case GT_HW_INTRINSIC_CHK:
8314 #endif // FEATURE_HW_INTRINSICS
8316 ValueNumPair vnpIndex = tree->AsBoundsChk()->gtIndex->gtVNPair;
8317 ValueNumPair vnpArrLen = tree->AsBoundsChk()->gtArrLen->gtVNPair;
8319 // Construct the exception set for bounds check
8320 ValueNumPair vnpExcSet = vnStore->VNPExcSetSingleton(
8321 vnStore->VNPairForFunc(TYP_REF, VNF_IndexOutOfRangeExc, vnStore->VNPNormalPair(vnpIndex),
8322 vnStore->VNPNormalPair(vnpArrLen)));
8324 // And collect the exceptions from Index and ArrLen
8325 vnpExcSet = vnStore->VNPUnionExcSet(vnpIndex, vnpExcSet);
8326 vnpExcSet = vnStore->VNPUnionExcSet(vnpArrLen, vnpExcSet);
8328 // A bounds check node has no value, but may throw exceptions.
8329 tree->gtVNPair = vnStore->VNPWithExc(vnStore->VNPForVoid(), vnpExcSet);
8331 // Record non-constant value numbers that are used as the length argument to bounds checks, so that
8332 // assertion prop will know that comparisons against them are worth analyzing.
8333 ValueNum lengthVN = tree->AsBoundsChk()->gtArrLen->gtVNPair.GetConservative();
8334 if ((lengthVN != ValueNumStore::NoVN) && !vnStore->IsVNConstant(lengthVN))
8336 vnStore->SetVNIsCheckedBound(lengthVN);
8341 case GT_CMPXCHG: // Specialop
8343 // For CMPXCHG and other intrinsics add an arbitrary side effect on GcHeap/ByrefExposed.
8344 fgMutateGcHeap(tree DEBUGARG("Interlocked intrinsic"));
8346 GenTreeCmpXchg* const cmpXchg = tree->AsCmpXchg();
8348 assert(tree->OperIsImplicitIndir()); // special node with an implicit indirections
8350 GenTree* location = cmpXchg->gtOpLocation; // arg1
8351 GenTree* value = cmpXchg->gtOpValue; // arg2
8352 GenTree* comparand = cmpXchg->gtOpComparand; // arg3
8354 ValueNumPair vnpExcSet = ValueNumStore::VNPForEmptyExcSet();
8356 // Collect the exception sets from our operands
8357 vnpExcSet = vnStore->VNPUnionExcSet(location->gtVNPair, vnpExcSet);
8358 vnpExcSet = vnStore->VNPUnionExcSet(value->gtVNPair, vnpExcSet);
8359 vnpExcSet = vnStore->VNPUnionExcSet(comparand->gtVNPair, vnpExcSet);
8361 // The normal value is a new unique VN.
8362 ValueNumPair normalPair;
8363 normalPair.SetBoth(vnStore->VNForExpr(compCurBB, tree->TypeGet()));
8365 // Attach the combined exception set
8366 tree->gtVNPair = vnStore->VNPWithExc(normalPair, vnpExcSet);
8368 // add the null check exception for 'location' to the tree's value number
8369 fgValueNumberAddExceptionSetForIndirection(tree, location);
8370 // add the null check exception for 'comparand' to the tree's value number
8371 fgValueNumberAddExceptionSetForIndirection(tree, comparand);
8376 tree->gtVNPair.SetBoth(vnStore->VNForExpr(compCurBB, tree->TypeGet()));
8382 if (tree->gtVNPair.GetLiberal() != ValueNumStore::NoVN)
8384 printf("N%03u ", tree->gtSeqNum);
8387 gtDispNodeName(tree);
8388 if (tree->OperIsLeaf() || tree->OperIsLocalStore()) // local stores used to be leaves
8390 gtDispLeaf(tree, nullptr);
8393 vnpPrint(tree->gtVNPair, 1);
8400 void Compiler::fgValueNumberIntrinsic(GenTree* tree)
8402 assert(tree->OperGet() == GT_INTRINSIC);
8403 GenTreeIntrinsic* intrinsic = tree->AsIntrinsic();
8404 ValueNumPair arg0VNP, arg1VNP;
8405 ValueNumPair arg0VNPx = ValueNumStore::VNPForEmptyExcSet();
8406 ValueNumPair arg1VNPx = ValueNumStore::VNPForEmptyExcSet();
8408 vnStore->VNPUnpackExc(intrinsic->gtOp.gtOp1->gtVNPair, &arg0VNP, &arg0VNPx);
8410 if (intrinsic->gtOp.gtOp2 != nullptr)
8412 vnStore->VNPUnpackExc(intrinsic->gtOp.gtOp2->gtVNPair, &arg1VNP, &arg1VNPx);
8415 if (IsMathIntrinsic(intrinsic->gtIntrinsicId))
8417 // GT_INTRINSIC is a currently a subtype of binary operators. But most of
8418 // the math intrinsics are actually unary operations.
8420 if (intrinsic->gtOp.gtOp2 == nullptr)
8422 intrinsic->gtVNPair =
8423 vnStore->VNPWithExc(vnStore->EvalMathFuncUnary(tree->TypeGet(), intrinsic->gtIntrinsicId, arg0VNP),
8428 ValueNumPair newVNP =
8429 vnStore->EvalMathFuncBinary(tree->TypeGet(), intrinsic->gtIntrinsicId, arg0VNP, arg1VNP);
8430 ValueNumPair excSet = vnStore->VNPExcSetUnion(arg0VNPx, arg1VNPx);
8431 intrinsic->gtVNPair = vnStore->VNPWithExc(newVNP, excSet);
8436 switch (intrinsic->gtIntrinsicId)
8438 case CORINFO_INTRINSIC_Object_GetType:
8439 intrinsic->gtVNPair =
8440 vnStore->VNPWithExc(vnStore->VNPairForFunc(intrinsic->TypeGet(), VNF_ObjGetType, arg0VNP),
8450 void Compiler::fgValueNumberCastTree(GenTree* tree)
8452 assert(tree->OperGet() == GT_CAST);
8454 ValueNumPair srcVNPair = tree->gtOp.gtOp1->gtVNPair;
8455 var_types castToType = tree->CastToType();
8456 var_types castFromType = tree->CastFromType();
8457 bool srcIsUnsigned = ((tree->gtFlags & GTF_UNSIGNED) != 0);
8458 bool hasOverflowCheck = tree->gtOverflowEx();
8460 assert(genActualType(castToType) == genActualType(tree->TypeGet())); // Insure that the resultType is correct
8462 tree->gtVNPair = vnStore->VNPairForCast(srcVNPair, castToType, castFromType, srcIsUnsigned, hasOverflowCheck);
8465 // Compute the normal ValueNumber for a cast operation with no exceptions
8466 ValueNum ValueNumStore::VNForCast(ValueNum srcVN,
8467 var_types castToType,
8468 var_types castFromType,
8469 bool srcIsUnsigned /* = false */)
8471 // The resulting type after performingthe cast is always widened to a supported IL stack size
8472 var_types resultType = genActualType(castToType);
8474 // When we're considering actual value returned by a non-checking cast whether or not the source is
8475 // unsigned does *not* matter for non-widening casts. That is, if we cast an int or a uint to short,
8476 // we just extract the first two bytes from the source bit pattern, not worrying about the interpretation.
8477 // The same is true in casting between signed/unsigned types of the same width. Only when we're doing
8478 // a widening cast do we care about whether the source was unsigned,so we know whether to sign or zero extend it.
8480 bool srcIsUnsignedNorm = srcIsUnsigned;
8481 if (genTypeSize(castToType) <= genTypeSize(castFromType))
8483 srcIsUnsignedNorm = false;
8486 ValueNum castTypeVN = VNForCastOper(castToType, srcIsUnsigned);
8487 ValueNum resultVN = VNForFunc(resultType, VNF_Cast, srcVN, castTypeVN);
8490 if (m_pComp->verbose)
8492 printf(" VNForCast(" FMT_VN ", " FMT_VN ") returns ", srcVN, castTypeVN);
8493 m_pComp->vnPrint(resultVN, 1);
8501 // Compute the ValueNumberPair for a cast operation
8502 ValueNumPair ValueNumStore::VNPairForCast(ValueNumPair srcVNPair,
8503 var_types castToType,
8504 var_types castFromType,
8505 bool srcIsUnsigned, /* = false */
8506 bool hasOverflowCheck) /* = false */
8508 // The resulting type after performingthe cast is always widened to a supported IL stack size
8509 var_types resultType = genActualType(castToType);
8511 ValueNumPair castArgVNP;
8512 ValueNumPair castArgxVNP;
8513 VNPUnpackExc(srcVNPair, &castArgVNP, &castArgxVNP);
8515 // When we're considering actual value returned by a non-checking cast, (hasOverflowCheck is false)
8516 // whether or not the source is unsigned does *not* matter for non-widening casts.
8517 // That is, if we cast an int or a uint to short, we just extract the first two bytes from the source
8518 // bit pattern, not worrying about the interpretation. The same is true in casting between signed/unsigned
8519 // types of the same width. Only when we're doing a widening cast do we care about whether the source
8520 // was unsigned, so we know whether to sign or zero extend it.
8522 // Important: Casts to floating point cannot be optimized in this fashion. (bug 946768)
8524 bool srcIsUnsignedNorm = srcIsUnsigned;
8525 if (!hasOverflowCheck && !varTypeIsFloating(castToType) && (genTypeSize(castToType) <= genTypeSize(castFromType)))
8527 srcIsUnsignedNorm = false;
8530 VNFunc vnFunc = hasOverflowCheck ? VNF_CastOvf : VNF_Cast;
8531 ValueNum castTypeVN = VNForCastOper(castToType, srcIsUnsignedNorm);
8532 ValueNumPair castTypeVNPair(castTypeVN, castTypeVN);
8533 ValueNumPair castNormRes = VNPairForFunc(resultType, vnFunc, castArgVNP, castTypeVNPair);
8535 ValueNumPair resultVNP = VNPWithExc(castNormRes, castArgxVNP);
8537 // If we have a check for overflow, add the exception information.
8538 if (hasOverflowCheck)
8540 ValueNumPair ovfChk = VNPairForFunc(TYP_REF, VNF_ConvOverflowExc, castArgVNP, castTypeVNPair);
8541 ValueNumPair excSet = VNPExcSetSingleton(ovfChk);
8542 excSet = VNPExcSetUnion(excSet, castArgxVNP);
8543 resultVNP = VNPWithExc(castNormRes, excSet);
8549 void Compiler::fgValueNumberHelperCallFunc(GenTreeCall* call, VNFunc vnf, ValueNumPair vnpExc)
8551 unsigned nArgs = ValueNumStore::VNFuncArity(vnf);
8552 assert(vnf != VNF_Boundary);
8553 GenTreeArgList* args = call->gtCallArgs;
8554 bool generateUniqueVN = false;
8555 bool useEntryPointAddrAsArg0 = false;
8561 generateUniqueVN = true;
8562 vnpExc = ValueNumStore::VNPForEmptyExcSet();
8568 generateUniqueVN = true;
8569 ValueNumPair vnp1 = vnStore->VNPNormalPair(args->Rest()->Current()->gtVNPair);
8571 // The New Array helper may throw an overflow exception
8572 vnpExc = vnStore->VNPExcSetSingleton(vnStore->VNPairForFunc(TYP_REF, VNF_NewArrOverflowExc, vnp1));
8577 case VNF_BoxNullable:
8579 // Generate unique VN so, VNForFunc generates a uniq value number for box nullable.
8580 // Alternatively instead of using vnpUniq below in VNPairForFunc(...),
8581 // we could use the value number of what the byref arg0 points to.
8583 // But retrieving the value number of what the byref arg0 points to is quite a bit more work
8584 // and doing so only very rarely allows for an additional optimization.
8585 generateUniqueVN = true;
8589 case VNF_JitReadyToRunNew:
8591 generateUniqueVN = true;
8592 vnpExc = ValueNumStore::VNPForEmptyExcSet();
8593 useEntryPointAddrAsArg0 = true;
8597 case VNF_JitReadyToRunNewArr:
8599 generateUniqueVN = true;
8600 ValueNumPair vnp1 = vnStore->VNPNormalPair(args->Current()->gtVNPair);
8602 // The New Array helper may throw an overflow exception
8603 vnpExc = vnStore->VNPExcSetSingleton(vnStore->VNPairForFunc(TYP_REF, VNF_NewArrOverflowExc, vnp1));
8604 useEntryPointAddrAsArg0 = true;
8608 case VNF_ReadyToRunStaticBase:
8609 case VNF_ReadyToRunGenericStaticBase:
8610 case VNF_ReadyToRunIsInstanceOf:
8611 case VNF_ReadyToRunCastClass:
8613 useEntryPointAddrAsArg0 = true;
8619 assert(s_helperCallProperties.IsPure(eeGetHelperNum(call->gtCallMethHnd)));
8624 if (generateUniqueVN)
8629 ValueNumPair vnpUniq;
8630 if (generateUniqueVN)
8632 // Generate unique VN so, VNForFunc generates a unique value number.
8633 vnpUniq.SetBoth(vnStore->VNForExpr(compCurBB, call->TypeGet()));
8636 #if defined(FEATURE_READYTORUN_COMPILER) && defined(_TARGET_ARMARCH_)
8637 if (call->IsR2RRelativeIndir())
8640 assert(args->Current()->OperGet() == GT_ARGPLACE);
8642 // Find the corresponding late arg.
8643 GenTree* indirectCellAddress = call->fgArgInfo->GetArgNode(0);
8644 assert(indirectCellAddress->IsCnsIntOrI() && indirectCellAddress->gtRegNum == REG_R2R_INDIRECT_PARAM);
8647 // For ARM indirectCellAddress is consumed by the call itself, so it should have added as an implicit argument
8648 // in morph. So we do not need to use EntryPointAddrAsArg0, because arg0 is already an entry point addr.
8649 useEntryPointAddrAsArg0 = false;
8651 #endif // FEATURE_READYTORUN_COMPILER && _TARGET_ARMARCH_
8655 if (generateUniqueVN)
8657 call->gtVNPair = vnStore->VNPairForFunc(call->TypeGet(), vnf, vnpUniq);
8661 call->gtVNPair.SetBoth(vnStore->VNForFunc(call->TypeGet(), vnf));
8666 auto getCurrentArg = [call, &args, useEntryPointAddrAsArg0](int currentIndex) {
8667 GenTree* arg = args->Current();
8668 if ((arg->gtFlags & GTF_LATE_ARG) != 0)
8670 // This arg is a setup node that moves the arg into position.
8671 // Value-numbering will have visited the separate late arg that
8672 // holds the actual value, and propagated/computed the value number
8673 // for this arg there.
8674 if (useEntryPointAddrAsArg0)
8676 // The args in the fgArgInfo don't include the entry point, so
8677 // index into them using one less than the requested index.
8680 return call->fgArgInfo->GetArgNode(currentIndex);
8684 // Has at least one argument.
8686 ValueNumPair vnp0x = ValueNumStore::VNPForEmptyExcSet();
8687 #ifdef FEATURE_READYTORUN_COMPILER
8688 if (useEntryPointAddrAsArg0)
8690 ssize_t addrValue = (ssize_t)call->gtEntryPoint.addr;
8691 ValueNum callAddrVN = vnStore->VNForHandle(addrValue, GTF_ICON_FTN_ADDR);
8692 vnp0 = ValueNumPair(callAddrVN, callAddrVN);
8695 #endif // FEATURE_READYTORUN_COMPILER
8697 assert(!useEntryPointAddrAsArg0);
8698 ValueNumPair vnp0wx = getCurrentArg(0)->gtVNPair;
8699 vnStore->VNPUnpackExc(vnp0wx, &vnp0, &vnp0x);
8701 // Also include in the argument exception sets
8702 vnpExc = vnStore->VNPExcSetUnion(vnpExc, vnp0x);
8704 args = args->Rest();
8708 if (generateUniqueVN)
8710 call->gtVNPair = vnStore->VNPairForFunc(call->TypeGet(), vnf, vnp0, vnpUniq);
8714 call->gtVNPair = vnStore->VNPairForFunc(call->TypeGet(), vnf, vnp0);
8719 // Has at least two arguments.
8720 ValueNumPair vnp1wx = getCurrentArg(1)->gtVNPair;
8723 vnStore->VNPUnpackExc(vnp1wx, &vnp1, &vnp1x);
8724 vnpExc = vnStore->VNPExcSetUnion(vnpExc, vnp1x);
8726 args = args->Rest();
8729 if (generateUniqueVN)
8731 call->gtVNPair = vnStore->VNPairForFunc(call->TypeGet(), vnf, vnp0, vnp1, vnpUniq);
8735 call->gtVNPair = vnStore->VNPairForFunc(call->TypeGet(), vnf, vnp0, vnp1);
8740 ValueNumPair vnp2wx = getCurrentArg(2)->gtVNPair;
8743 vnStore->VNPUnpackExc(vnp2wx, &vnp2, &vnp2x);
8744 vnpExc = vnStore->VNPExcSetUnion(vnpExc, vnp2x);
8746 args = args->Rest();
8747 assert(nArgs == 3); // Our current maximum.
8748 assert(args == nullptr);
8749 if (generateUniqueVN)
8751 call->gtVNPair = vnStore->VNPairForFunc(call->TypeGet(), vnf, vnp0, vnp1, vnp2, vnpUniq);
8755 call->gtVNPair = vnStore->VNPairForFunc(call->TypeGet(), vnf, vnp0, vnp1, vnp2);
8759 // Add the accumulated exceptions.
8760 call->gtVNPair = vnStore->VNPWithExc(call->gtVNPair, vnpExc);
8762 assert(args == nullptr || generateUniqueVN); // All arguments should be processed or we generate unique VN and do
8766 void Compiler::fgValueNumberCall(GenTreeCall* call)
8768 // First: do value numbering of any argument placeholder nodes in the argument list
8769 // (by transferring from the VN of the late arg that they are standing in for...)
8771 GenTreeArgList* args = call->gtCallArgs;
8772 bool updatedArgPlace = false;
8773 while (args != nullptr)
8775 GenTree* arg = args->Current();
8776 if (arg->OperGet() == GT_ARGPLACE)
8778 // Find the corresponding late arg.
8779 GenTree* lateArg = call->fgArgInfo->GetArgNode(i);
8780 assert(lateArg->gtVNPair.BothDefined());
8781 arg->gtVNPair = lateArg->gtVNPair;
8782 updatedArgPlace = true;
8786 printf("VN of ARGPLACE tree ");
8787 Compiler::printTreeID(arg);
8788 printf(" updated to ");
8789 vnpPrint(arg->gtVNPair, 1);
8795 args = args->Rest();
8797 if (updatedArgPlace)
8799 // Now we have to update the VN's of the argument list nodes, since that will be used in determining
8801 fgUpdateArgListVNs(call->gtCallArgs);
8804 if (call->gtCallType == CT_HELPER)
8806 bool modHeap = fgValueNumberHelperCall(call);
8810 // For now, arbitrary side effect on GcHeap/ByrefExposed.
8811 fgMutateGcHeap(call DEBUGARG("HELPER - modifies heap"));
8816 if (call->TypeGet() == TYP_VOID)
8818 call->gtVNPair.SetBoth(ValueNumStore::VNForVoid());
8822 call->gtVNPair.SetBoth(vnStore->VNForExpr(compCurBB, call->TypeGet()));
8825 // For now, arbitrary side effect on GcHeap/ByrefExposed.
8826 fgMutateGcHeap(call DEBUGARG("CALL"));
8830 void Compiler::fgUpdateArgListVNs(GenTreeArgList* args)
8832 if (args == nullptr)
8837 fgUpdateArgListVNs(args->Rest());
8838 fgValueNumberTree(args);
8841 VNFunc Compiler::fgValueNumberJitHelperMethodVNFunc(CorInfoHelpFunc helpFunc)
8843 assert(s_helperCallProperties.IsPure(helpFunc) || s_helperCallProperties.IsAllocator(helpFunc));
8845 VNFunc vnf = VNF_Boundary; // An illegal value...
8848 // These translate to other function symbols:
8849 case CORINFO_HELP_DIV:
8850 vnf = VNFunc(GT_DIV);
8852 case CORINFO_HELP_MOD:
8853 vnf = VNFunc(GT_MOD);
8855 case CORINFO_HELP_UDIV:
8856 vnf = VNFunc(GT_UDIV);
8858 case CORINFO_HELP_UMOD:
8859 vnf = VNFunc(GT_UMOD);
8861 case CORINFO_HELP_LLSH:
8862 vnf = VNFunc(GT_LSH);
8864 case CORINFO_HELP_LRSH:
8865 vnf = VNFunc(GT_RSH);
8867 case CORINFO_HELP_LRSZ:
8868 vnf = VNFunc(GT_RSZ);
8870 case CORINFO_HELP_LMUL:
8871 case CORINFO_HELP_LMUL_OVF:
8872 vnf = VNFunc(GT_MUL);
8874 case CORINFO_HELP_ULMUL_OVF:
8875 vnf = VNFunc(GT_MUL);
8876 break; // Is this the right thing?
8877 case CORINFO_HELP_LDIV:
8878 vnf = VNFunc(GT_DIV);
8880 case CORINFO_HELP_LMOD:
8881 vnf = VNFunc(GT_MOD);
8883 case CORINFO_HELP_ULDIV:
8884 vnf = VNFunc(GT_UDIV);
8886 case CORINFO_HELP_ULMOD:
8887 vnf = VNFunc(GT_UMOD);
8890 case CORINFO_HELP_LNG2DBL:
8893 case CORINFO_HELP_ULNG2DBL:
8896 case CORINFO_HELP_DBL2INT:
8899 case CORINFO_HELP_DBL2INT_OVF:
8902 case CORINFO_HELP_DBL2LNG:
8905 case CORINFO_HELP_DBL2LNG_OVF:
8908 case CORINFO_HELP_DBL2UINT:
8911 case CORINFO_HELP_DBL2UINT_OVF:
8914 case CORINFO_HELP_DBL2ULNG:
8917 case CORINFO_HELP_DBL2ULNG_OVF:
8920 case CORINFO_HELP_FLTREM:
8921 vnf = VNFunc(GT_MOD);
8923 case CORINFO_HELP_DBLREM:
8924 vnf = VNFunc(GT_MOD);
8926 case CORINFO_HELP_FLTROUND:
8928 break; // Is this the right thing?
8929 case CORINFO_HELP_DBLROUND:
8931 break; // Is this the right thing?
8933 // These allocation operations probably require some augmentation -- perhaps allocSiteId,
8934 // something about array length...
8935 case CORINFO_HELP_NEW_CROSSCONTEXT:
8936 case CORINFO_HELP_NEWFAST:
8937 case CORINFO_HELP_NEWSFAST:
8938 case CORINFO_HELP_NEWSFAST_FINALIZE:
8939 case CORINFO_HELP_NEWSFAST_ALIGN8:
8940 case CORINFO_HELP_NEWSFAST_ALIGN8_VC:
8941 case CORINFO_HELP_NEWSFAST_ALIGN8_FINALIZE:
8945 case CORINFO_HELP_READYTORUN_NEW:
8946 vnf = VNF_JitReadyToRunNew;
8949 case CORINFO_HELP_NEWARR_1_DIRECT:
8950 case CORINFO_HELP_NEWARR_1_OBJ:
8951 case CORINFO_HELP_NEWARR_1_VC:
8952 case CORINFO_HELP_NEWARR_1_ALIGN8:
8953 vnf = VNF_JitNewArr;
8956 case CORINFO_HELP_NEWARR_1_R2R_DIRECT:
8957 case CORINFO_HELP_READYTORUN_NEWARR_1:
8958 vnf = VNF_JitReadyToRunNewArr;
8961 case CORINFO_HELP_GETGENERICS_GCSTATIC_BASE:
8962 vnf = VNF_GetgenericsGcstaticBase;
8964 case CORINFO_HELP_GETGENERICS_NONGCSTATIC_BASE:
8965 vnf = VNF_GetgenericsNongcstaticBase;
8967 case CORINFO_HELP_GETSHARED_GCSTATIC_BASE:
8968 vnf = VNF_GetsharedGcstaticBase;
8970 case CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE:
8971 vnf = VNF_GetsharedNongcstaticBase;
8973 case CORINFO_HELP_GETSHARED_GCSTATIC_BASE_NOCTOR:
8974 vnf = VNF_GetsharedGcstaticBaseNoctor;
8976 case CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE_NOCTOR:
8977 vnf = VNF_GetsharedNongcstaticBaseNoctor;
8979 case CORINFO_HELP_READYTORUN_STATIC_BASE:
8980 vnf = VNF_ReadyToRunStaticBase;
8982 case CORINFO_HELP_READYTORUN_GENERIC_STATIC_BASE:
8983 vnf = VNF_ReadyToRunGenericStaticBase;
8985 case CORINFO_HELP_GETSHARED_GCSTATIC_BASE_DYNAMICCLASS:
8986 vnf = VNF_GetsharedGcstaticBaseDynamicclass;
8988 case CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE_DYNAMICCLASS:
8989 vnf = VNF_GetsharedNongcstaticBaseDynamicclass;
8991 case CORINFO_HELP_CLASSINIT_SHARED_DYNAMICCLASS:
8992 vnf = VNF_ClassinitSharedDynamicclass;
8994 case CORINFO_HELP_GETGENERICS_GCTHREADSTATIC_BASE:
8995 vnf = VNF_GetgenericsGcthreadstaticBase;
8997 case CORINFO_HELP_GETGENERICS_NONGCTHREADSTATIC_BASE:
8998 vnf = VNF_GetgenericsNongcthreadstaticBase;
9000 case CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE:
9001 vnf = VNF_GetsharedGcthreadstaticBase;
9003 case CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE:
9004 vnf = VNF_GetsharedNongcthreadstaticBase;
9006 case CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE_NOCTOR:
9007 vnf = VNF_GetsharedGcthreadstaticBaseNoctor;
9009 case CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE_NOCTOR:
9010 vnf = VNF_GetsharedNongcthreadstaticBaseNoctor;
9012 case CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE_DYNAMICCLASS:
9013 vnf = VNF_GetsharedGcthreadstaticBaseDynamicclass;
9015 case CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE_DYNAMICCLASS:
9016 vnf = VNF_GetsharedNongcthreadstaticBaseDynamicclass;
9018 case CORINFO_HELP_GETSTATICFIELDADDR_CONTEXT:
9019 vnf = VNF_GetStaticAddrContext;
9021 case CORINFO_HELP_GETSTATICFIELDADDR_TLS:
9022 vnf = VNF_GetStaticAddrTLS;
9025 case CORINFO_HELP_RUNTIMEHANDLE_METHOD:
9026 case CORINFO_HELP_RUNTIMEHANDLE_METHOD_LOG:
9027 vnf = VNF_RuntimeHandleMethod;
9030 case CORINFO_HELP_RUNTIMEHANDLE_CLASS:
9031 case CORINFO_HELP_RUNTIMEHANDLE_CLASS_LOG:
9032 vnf = VNF_RuntimeHandleClass;
9035 case CORINFO_HELP_STRCNS:
9039 case CORINFO_HELP_CHKCASTCLASS:
9040 case CORINFO_HELP_CHKCASTCLASS_SPECIAL:
9041 case CORINFO_HELP_CHKCASTARRAY:
9042 case CORINFO_HELP_CHKCASTINTERFACE:
9043 case CORINFO_HELP_CHKCASTANY:
9044 vnf = VNF_CastClass;
9047 case CORINFO_HELP_READYTORUN_CHKCAST:
9048 vnf = VNF_ReadyToRunCastClass;
9051 case CORINFO_HELP_ISINSTANCEOFCLASS:
9052 case CORINFO_HELP_ISINSTANCEOFINTERFACE:
9053 case CORINFO_HELP_ISINSTANCEOFARRAY:
9054 case CORINFO_HELP_ISINSTANCEOFANY:
9055 vnf = VNF_IsInstanceOf;
9058 case CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPE:
9059 vnf = VNF_TypeHandleToRuntimeType;
9062 case CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPEHANDLE:
9063 vnf = VNF_TypeHandleToRuntimeTypeHandle;
9066 case CORINFO_HELP_ARE_TYPES_EQUIVALENT:
9067 vnf = VNF_AreTypesEquivalent;
9070 case CORINFO_HELP_READYTORUN_ISINSTANCEOF:
9071 vnf = VNF_ReadyToRunIsInstanceOf;
9074 case CORINFO_HELP_LDELEMA_REF:
9078 case CORINFO_HELP_UNBOX:
9082 // A constant within any method.
9083 case CORINFO_HELP_GETCURRENTMANAGEDTHREADID:
9084 vnf = VNF_ManagedThreadId;
9087 case CORINFO_HELP_GETREFANY:
9088 // TODO-CQ: This should really be interpreted as just a struct field reference, in terms of values.
9089 vnf = VNF_GetRefanyVal;
9092 case CORINFO_HELP_GETCLASSFROMMETHODPARAM:
9093 vnf = VNF_GetClassFromMethodParam;
9096 case CORINFO_HELP_GETSYNCFROMCLASSHANDLE:
9097 vnf = VNF_GetSyncFromClassHandle;
9100 case CORINFO_HELP_LOOP_CLONE_CHOICE_ADDR:
9101 vnf = VNF_LoopCloneChoiceAddr;
9104 case CORINFO_HELP_BOX:
9108 case CORINFO_HELP_BOX_NULLABLE:
9109 vnf = VNF_BoxNullable;
9116 assert(vnf != VNF_Boundary);
9120 bool Compiler::fgValueNumberHelperCall(GenTreeCall* call)
9122 CorInfoHelpFunc helpFunc = eeGetHelperNum(call->gtCallMethHnd);
9123 bool pure = s_helperCallProperties.IsPure(helpFunc);
9124 bool isAlloc = s_helperCallProperties.IsAllocator(helpFunc);
9125 bool modHeap = s_helperCallProperties.MutatesHeap(helpFunc);
9126 bool mayRunCctor = s_helperCallProperties.MayRunCctor(helpFunc);
9127 bool noThrow = s_helperCallProperties.NoThrow(helpFunc);
9129 ValueNumPair vnpExc = ValueNumStore::VNPForEmptyExcSet();
9131 // If the JIT helper can throw an exception make sure that we fill in
9132 // vnpExc with a Value Number that represents the exception(s) that can be thrown.
9135 // If the helper is known to only throw only one particular exception
9136 // we can set vnpExc to that exception, otherwise we conservatively
9137 // model the JIT helper as possibly throwing multiple different exceptions
9141 case CORINFO_HELP_OVERFLOW:
9142 // This helper always throws the VNF_OverflowExc exception
9143 vnpExc = vnStore->VNPExcSetSingleton(
9144 vnStore->VNPairForFunc(TYP_REF, VNF_OverflowExc, vnStore->VNPForVoid()));
9148 // Setup vnpExc with the information that multiple different exceptions
9149 // could be generated by this helper
9150 vnpExc = vnStore->VNPExcSetSingleton(vnStore->VNPairForFunc(TYP_REF, VNF_HelperMultipleExc));
9154 ValueNumPair vnpNorm;
9156 if (call->TypeGet() == TYP_VOID)
9158 vnpNorm = ValueNumStore::VNPForVoid();
9162 // TODO-CQ: this is a list of helpers we're going to treat as non-pure,
9163 // because they raise complications. Eventually, we need to handle those complications...
9164 bool needsFurtherWork = false;
9167 case CORINFO_HELP_NEW_MDARR:
9168 // This is a varargs helper. We need to represent the array shape in the VN world somehow.
9169 needsFurtherWork = true;
9175 if (!needsFurtherWork && (pure || isAlloc))
9177 VNFunc vnf = fgValueNumberJitHelperMethodVNFunc(helpFunc);
9181 if ((call->gtFlags & GTF_CALL_HOISTABLE) == 0)
9187 fgValueNumberHelperCallFunc(call, vnf, vnpExc);
9192 vnpNorm.SetBoth(vnStore->VNForExpr(compCurBB, call->TypeGet()));
9196 call->gtVNPair = vnStore->VNPWithExc(vnpNorm, vnpExc);
9200 //--------------------------------------------------------------------------------
9201 // fgValueNumberAddExceptionSetForIndirection
9202 // - Adds the exception sets for the current tree node
9203 // which is performing a memory indirection operation
9206 // tree - The current GenTree node,
9207 // It must be some kind of an indirection node
9208 // or have an implicit indirection
9209 // baseAddr - The address that we are indirecting
9212 // - The tree's gtVNPair is updated to include the VNF_nullPtrExc
9213 // exception set. We calculate a base address to use as the
9214 // argument to the VNF_nullPtrExc function.
9216 // Notes: - The calculation of the base address removes any constant
9217 // offsets, so that obj.x and obj.y will both have obj as
9218 // their base address.
9219 // For arrays the base address currently includes the
9220 // index calculations.
9222 void Compiler::fgValueNumberAddExceptionSetForIndirection(GenTree* tree, GenTree* baseAddr)
9224 // We should have tree that a unary indirection or a tree node with an implicit indirection
9225 assert(tree->OperIsUnary() || tree->OperIsImplicitIndir());
9227 // We evaluate the baseAddr ValueNumber further in order
9228 // to obtain a better value to use for the null check exeception.
9230 ValueNumPair baseVNP = baseAddr->gtVNPair;
9231 ValueNum baseLVN = baseVNP.GetLiberal();
9232 ValueNum baseCVN = baseVNP.GetConservative();
9233 ssize_t offsetL = 0;
9234 ssize_t offsetC = 0;
9237 while (vnStore->GetVNFunc(baseLVN, &funcAttr) && (funcAttr.m_func == (VNFunc)GT_ADD) &&
9238 (vnStore->TypeOfVN(baseLVN) == TYP_BYREF))
9240 // The arguments in value numbering functions are sorted in increasing order
9241 // Thus either arg could be the constant.
9242 if (vnStore->IsVNConstant(funcAttr.m_args[0]) && varTypeIsIntegral(vnStore->TypeOfVN(funcAttr.m_args[0])))
9244 offsetL += vnStore->CoercedConstantValue<ssize_t>(funcAttr.m_args[0]);
9245 baseLVN = funcAttr.m_args[1];
9247 else if (vnStore->IsVNConstant(funcAttr.m_args[1]) && varTypeIsIntegral(vnStore->TypeOfVN(funcAttr.m_args[1])))
9249 offsetL += vnStore->CoercedConstantValue<ssize_t>(funcAttr.m_args[1]);
9250 baseLVN = funcAttr.m_args[0];
9252 else // neither argument is a constant
9257 if (fgIsBigOffset(offsetL))
9259 // Failure: Exit this loop if we have a "big" offset
9261 // reset baseLVN back to the full address expression
9262 baseLVN = baseVNP.GetLiberal();
9267 while (vnStore->GetVNFunc(baseCVN, &funcAttr) && (funcAttr.m_func == (VNFunc)GT_ADD) &&
9268 (vnStore->TypeOfVN(baseCVN) == TYP_BYREF))
9270 // The arguments in value numbering functions are sorted in increasing order
9271 // Thus either arg could be the constant.
9272 if (vnStore->IsVNConstant(funcAttr.m_args[0]) && varTypeIsIntegral(vnStore->TypeOfVN(funcAttr.m_args[0])))
9274 offsetL += vnStore->CoercedConstantValue<ssize_t>(funcAttr.m_args[0]);
9275 baseCVN = funcAttr.m_args[1];
9277 else if (vnStore->IsVNConstant(funcAttr.m_args[1]) && varTypeIsIntegral(vnStore->TypeOfVN(funcAttr.m_args[1])))
9279 offsetC += vnStore->CoercedConstantValue<ssize_t>(funcAttr.m_args[1]);
9280 baseCVN = funcAttr.m_args[0];
9282 else // neither argument is a constant
9287 if (fgIsBigOffset(offsetC))
9289 // Failure: Exit this loop if we have a "big" offset
9291 // reset baseCVN back to the full address expression
9292 baseCVN = baseVNP.GetConservative();
9297 // Create baseVNP, from the values we just computed,
9298 baseVNP = ValueNumPair(baseLVN, baseCVN);
9300 // Unpack, Norm,Exc for the tree's op1 VN
9301 ValueNumPair vnpBaseNorm;
9302 ValueNumPair vnpBaseExc;
9303 vnStore->VNPUnpackExc(baseVNP, &vnpBaseNorm, &vnpBaseExc);
9305 // The Norm VN for op1 is used to create the NullPtrExc
9306 ValueNumPair excChkSet = vnStore->VNPExcSetSingleton(vnStore->VNPairForFunc(TYP_REF, VNF_NullPtrExc, vnpBaseNorm));
9308 // Combine the excChkSet with exception set of op1
9309 ValueNumPair excSetBoth = vnStore->VNPExcSetUnion(excChkSet, vnpBaseExc);
9311 // Retrieve the Normal VN for tree, note that it may be NoVN, so we handle that case
9312 ValueNumPair vnpNorm = vnStore->VNPNormalPair(tree->gtVNPair);
9314 // For as GT_IND on the lhs of an assignment we will get a NoVN value
9315 if (vnpNorm.GetLiberal() == ValueNumStore::NoVN)
9317 // Use the special Void VN value instead.
9318 vnpNorm = vnStore->VNPForVoid();
9320 tree->gtVNPair = vnStore->VNPWithExc(vnpNorm, excSetBoth);
9323 //--------------------------------------------------------------------------------
9324 // fgValueNumberAddExceptionSetForDivison
9325 // - Adds the exception sets for the current tree node
9326 // which is performing an integer division operation
9329 // tree - The current GenTree node,
9330 // It must be a node that performs an integer division
9333 // - The tree's gtVNPair is updated to include
9334 // VNF_DivideByZeroExc and VNF_ArithmeticExc,
9335 // We will omit one or both of them when the operation
9336 // has constants arguments that preclude the exception.
9338 void Compiler::fgValueNumberAddExceptionSetForDivision(GenTree* tree)
9340 genTreeOps oper = tree->OperGet();
9342 // A Divide By Zero exception may be possible.
9343 // The divisor is held in tree->gtOp.gtOp2
9345 bool isUnsignedOper = (oper == GT_UDIV) || (oper == GT_UMOD);
9346 bool needDivideByZeroExcLib = true;
9347 bool needDivideByZeroExcCon = true;
9348 bool needArithmeticExcLib = !isUnsignedOper; // Overflow isn't possible for unsigned divide
9349 bool needArithmeticExcCon = !isUnsignedOper;
9351 // Determine if we have a 32-bit or 64-bit divide operation
9352 var_types typ = genActualType(tree->TypeGet());
9353 assert((typ == TYP_INT) || (typ == TYP_LONG));
9355 // Retrieve the Norm VN for op2 to use it for the DivideByZeroExc
9356 ValueNumPair vnpOp2Norm = vnStore->VNPNormalPair(tree->gtOp.gtOp2->gtVNPair);
9357 ValueNum vnOp2NormLib = vnpOp2Norm.GetLiberal();
9358 ValueNum vnOp2NormCon = vnpOp2Norm.GetConservative();
9362 if (vnStore->IsVNConstant(vnOp2NormLib))
9364 INT32 kVal = vnStore->ConstantValue<INT32>(vnOp2NormLib);
9367 needDivideByZeroExcLib = false;
9369 if (!isUnsignedOper && (kVal != -1))
9371 needArithmeticExcLib = false;
9374 if (vnStore->IsVNConstant(vnOp2NormCon))
9376 INT32 kVal = vnStore->ConstantValue<INT32>(vnOp2NormCon);
9379 needDivideByZeroExcCon = false;
9381 if (!isUnsignedOper && (kVal != -1))
9383 needArithmeticExcCon = false;
9387 else // (typ == TYP_LONG)
9389 if (vnStore->IsVNConstant(vnOp2NormLib))
9391 INT64 kVal = vnStore->ConstantValue<INT64>(vnOp2NormLib);
9394 needDivideByZeroExcLib = false;
9396 if (!isUnsignedOper && (kVal != -1))
9398 needArithmeticExcLib = false;
9401 if (vnStore->IsVNConstant(vnOp2NormCon))
9403 INT64 kVal = vnStore->ConstantValue<INT64>(vnOp2NormCon);
9406 needDivideByZeroExcCon = false;
9408 if (!isUnsignedOper && (kVal != -1))
9410 needArithmeticExcCon = false;
9415 // Retrieve the Norm VN for op1 to use it for the ArithmeticExc
9416 ValueNumPair vnpOp1Norm = vnStore->VNPNormalPair(tree->gtOp.gtOp1->gtVNPair);
9417 ValueNum vnOp1NormLib = vnpOp1Norm.GetLiberal();
9418 ValueNum vnOp1NormCon = vnpOp1Norm.GetConservative();
9420 if (needArithmeticExcLib || needArithmeticExcCon)
9424 if (vnStore->IsVNConstant(vnOp1NormLib))
9426 INT32 kVal = vnStore->ConstantValue<INT32>(vnOp1NormLib);
9428 if (!isUnsignedOper && (kVal != INT32_MIN))
9430 needArithmeticExcLib = false;
9433 if (vnStore->IsVNConstant(vnOp1NormCon))
9435 INT32 kVal = vnStore->ConstantValue<INT32>(vnOp1NormCon);
9437 if (!isUnsignedOper && (kVal != INT32_MIN))
9439 needArithmeticExcCon = false;
9443 else // (typ == TYP_LONG)
9445 if (vnStore->IsVNConstant(vnOp1NormLib))
9447 INT64 kVal = vnStore->ConstantValue<INT64>(vnOp1NormLib);
9449 if (!isUnsignedOper && (kVal != INT64_MIN))
9451 needArithmeticExcLib = false;
9454 if (vnStore->IsVNConstant(vnOp1NormCon))
9456 INT64 kVal = vnStore->ConstantValue<INT64>(vnOp1NormCon);
9458 if (!isUnsignedOper && (kVal != INT64_MIN))
9460 needArithmeticExcCon = false;
9466 // Unpack, Norm,Exc for the tree's VN
9467 ValueNumPair vnpTreeNorm;
9468 ValueNumPair vnpTreeExc;
9469 ValueNumPair vnpDivZeroExc = ValueNumStore::VNPForEmptyExcSet();
9470 ValueNumPair vnpArithmExc = ValueNumStore::VNPForEmptyExcSet();
9472 vnStore->VNPUnpackExc(tree->gtVNPair, &vnpTreeNorm, &vnpTreeExc);
9474 if (needDivideByZeroExcLib)
9476 vnpDivZeroExc.SetLiberal(
9477 vnStore->VNExcSetSingleton(vnStore->VNForFunc(TYP_REF, VNF_DivideByZeroExc, vnOp2NormLib)));
9479 if (needDivideByZeroExcCon)
9481 vnpDivZeroExc.SetConservative(
9482 vnStore->VNExcSetSingleton(vnStore->VNForFunc(TYP_REF, VNF_DivideByZeroExc, vnOp2NormCon)));
9484 if (needArithmeticExcLib)
9486 vnpArithmExc.SetLiberal(
9487 vnStore->VNExcSetSingleton(vnStore->VNForFunc(TYP_REF, VNF_ArithmeticExc, vnOp1NormLib, vnOp2NormLib)));
9489 if (needArithmeticExcCon)
9491 vnpArithmExc.SetConservative(
9492 vnStore->VNExcSetSingleton(vnStore->VNForFunc(TYP_REF, VNF_ArithmeticExc, vnOp1NormLib, vnOp2NormCon)));
9495 // Combine vnpDivZeroExc with the exception set of tree
9496 ValueNumPair newExcSet = vnStore->VNPExcSetUnion(vnpTreeExc, vnpDivZeroExc);
9497 // Combine vnpArithmExc with the newExcSet
9498 newExcSet = vnStore->VNPExcSetUnion(newExcSet, vnpArithmExc);
9500 // Updated VN for tree, it now includes DivideByZeroExc and/or ArithmeticExc
9501 tree->gtVNPair = vnStore->VNPWithExc(vnpTreeNorm, newExcSet);
9504 //--------------------------------------------------------------------------------
9505 // fgValueNumberAddExceptionSetForOverflow
9506 // - Adds the exception set for the current tree node
9507 // which is performing an overflow checking math operation
9510 // tree - The current GenTree node,
9511 // It must be a node that performs an overflow
9512 // checking math operation
9515 // - The tree's gtVNPair is updated to include the VNF_OverflowExc
9518 void Compiler::fgValueNumberAddExceptionSetForOverflow(GenTree* tree)
9520 assert(tree->gtOverflowEx());
9522 // We should only be dealing with an Overflow checking ALU operation.
9523 VNFunc vnf = GetVNFuncForNode(tree);
9524 assert((vnf >= VNF_ADD_OVF) && (vnf <= VNF_MUL_UN_OVF));
9526 // Unpack, Norm,Exc for the tree's VN
9528 ValueNumPair vnpTreeNorm;
9529 ValueNumPair vnpTreeExc;
9531 vnStore->VNPUnpackExc(tree->gtVNPair, &vnpTreeNorm, &vnpTreeExc);
9534 // The normal value number function should be the same overflow checking ALU operation as 'vnf'
9535 VNFuncApp treeNormFuncApp;
9536 assert(vnStore->GetVNFunc(vnpTreeNorm.GetLiberal(), &treeNormFuncApp) && (treeNormFuncApp.m_func == vnf));
9539 // Overflow-checking operations add an overflow exception
9540 // The normal result is used as the input argument for the OverflowExc
9541 ValueNumPair overflowExcSet =
9542 vnStore->VNPExcSetSingleton(vnStore->VNPairForFunc(TYP_REF, VNF_OverflowExc, vnpTreeNorm));
9544 // Combine the new Overflow exception with the original exception set of tree
9545 ValueNumPair newExcSet = vnStore->VNPExcSetUnion(vnpTreeExc, overflowExcSet);
9547 // Updated VN for tree, it now includes Overflow exception
9548 tree->gtVNPair = vnStore->VNPWithExc(vnpTreeNorm, newExcSet);
9551 //--------------------------------------------------------------------------------
9552 // fgValueNumberAddExceptionSetForCkFinite
9553 // - Adds the exception set for the current tree node
9554 // which is a CkFinite operation
9557 // tree - The current GenTree node,
9558 // It must be a CkFinite node
9561 // - The tree's gtVNPair is updated to include the VNF_ArithmeticExc
9564 void Compiler::fgValueNumberAddExceptionSetForCkFinite(GenTree* tree)
9566 // We should only be dealing with an check finite operation.
9567 assert(tree->OperGet() == GT_CKFINITE);
9569 // Unpack, Norm,Exc for the tree's VN
9571 ValueNumPair vnpTreeNorm;
9572 ValueNumPair vnpTreeExc;
9573 ValueNumPair newExcSet;
9575 vnStore->VNPUnpackExc(tree->gtVNPair, &vnpTreeNorm, &vnpTreeExc);
9577 // ckfinite adds an Arithmetic exception
9578 // The normal result is used as the input argument for the ArithmeticExc
9579 ValueNumPair arithmeticExcSet =
9580 vnStore->VNPExcSetSingleton(vnStore->VNPairForFunc(TYP_REF, VNF_ArithmeticExc, vnpTreeNorm));
9582 // Combine the new Arithmetic exception with the original exception set of tree
9583 newExcSet = vnStore->VNPExcSetUnion(vnpTreeExc, arithmeticExcSet);
9585 // Updated VN for tree, it now includes Arithmetic exception
9586 tree->gtVNPair = vnStore->VNPWithExc(vnpTreeNorm, newExcSet);
9589 //--------------------------------------------------------------------------------
9590 // fgValueNumberAddExceptionSet
9591 // - Adds any exception sets needed for the current tree node
9594 // tree - The current GenTree node,
9597 // - The tree's gtVNPair is updated to include the exception sets.
9599 // Notes: - This method relies upon OperMayTHrow to determine if we need
9600 // to add an exception set. If OPerMayThrow returns false no
9601 // exception set will be added.
9603 void Compiler::fgValueNumberAddExceptionSet(GenTree* tree)
9605 if (tree->OperMayThrow(this))
9607 switch (tree->OperGet())
9609 case GT_CAST: // A cast with an overflow check
9610 break; // Already handled by VNPairForCast()
9612 case GT_ADD: // An Overflow checking ALU operation
9615 fgValueNumberAddExceptionSetForOverflow(tree);
9619 // It is not necessary to model the StackOverflow exception for GT_LCLHEAP
9623 // ToDo: model the exceptions for Intrinsics
9626 case GT_IND: // Implicit null check.
9627 if ((tree->gtFlags & GTF_IND_ASG_LHS) != 0)
9629 // Don't add exception set on LHS of assignment
9638 fgValueNumberAddExceptionSetForIndirection(tree, tree->AsIndir()->Addr());
9642 fgValueNumberAddExceptionSetForIndirection(tree, tree->AsArrLen()->ArrRef());
9646 fgValueNumberAddExceptionSetForIndirection(tree, tree->gtArrElem.gtArrObj);
9650 fgValueNumberAddExceptionSetForIndirection(tree, tree->gtArrIndex.ArrObj());
9654 fgValueNumberAddExceptionSetForIndirection(tree, tree->gtArrOffs.gtArrObj);
9661 fgValueNumberAddExceptionSetForDivision(tree);
9665 fgValueNumberAddExceptionSetForCkFinite(tree);
9669 assert(!"Handle this oper in fgValueNumberAddExceptionSet");
9676 // This method asserts that SSA name constraints specified are satisfied.
9677 // Until we figure out otherwise, all VN's are assumed to be liberal.
9678 // TODO-Cleanup: new JitTestLabels for lib vs cons vs both VN classes?
9679 void Compiler::JitTestCheckVN()
9681 typedef JitHashTable<ssize_t, JitSmallPrimitiveKeyFuncs<ssize_t>, ValueNum> LabelToVNMap;
9682 typedef JitHashTable<ValueNum, JitSmallPrimitiveKeyFuncs<ValueNum>, ssize_t> VNToLabelMap;
9684 // If we have no test data, early out.
9685 if (m_nodeTestData == nullptr)
9690 NodeToTestDataMap* testData = GetNodeTestData();
9692 // First we have to know which nodes in the tree are reachable.
9693 typedef JitHashTable<GenTree*, JitPtrKeyFuncs<GenTree>, int> NodeToIntMap;
9694 NodeToIntMap* reachable = FindReachableNodesInNodeTestData();
9696 LabelToVNMap* labelToVN = new (getAllocatorDebugOnly()) LabelToVNMap(getAllocatorDebugOnly());
9697 VNToLabelMap* vnToLabel = new (getAllocatorDebugOnly()) VNToLabelMap(getAllocatorDebugOnly());
9701 printf("\nJit Testing: Value numbering.\n");
9703 for (NodeToTestDataMap::KeyIterator ki = testData->Begin(); !ki.Equal(testData->End()); ++ki)
9705 TestLabelAndNum tlAndN;
9706 GenTree* node = ki.Get();
9707 ValueNum nodeVN = node->GetVN(VNK_Liberal);
9709 bool b = testData->Lookup(node, &tlAndN);
9711 if (tlAndN.m_tl == TL_VN || tlAndN.m_tl == TL_VNNorm)
9714 if (!reachable->Lookup(node, &dummy))
9717 Compiler::printTreeID(node);
9718 printf(" had a test constraint declared, but has become unreachable at the time the constraint is "
9720 "(This is probably as a result of some optimization -- \n"
9721 "you may need to modify the test case to defeat this opt.)\n");
9728 Compiler::printTreeID(node);
9729 printf(" -- VN class %d.\n", tlAndN.m_num);
9732 if (tlAndN.m_tl == TL_VNNorm)
9734 nodeVN = vnStore->VNNormalValue(nodeVN);
9738 if (labelToVN->Lookup(tlAndN.m_num, &vn))
9742 printf(" Already in hash tables.\n");
9744 // The mapping(s) must be one-to-one: if the label has a mapping, then the ssaNm must, as well.
9746 bool found = vnToLabel->Lookup(vn, &num2);
9748 // And the mappings must be the same.
9749 if (tlAndN.m_num != num2)
9752 Compiler::printTreeID(node);
9753 printf(", with value number " FMT_VN ", was declared in VN class %d,\n", nodeVN, tlAndN.m_num);
9754 printf("but this value number " FMT_VN
9755 " has already been associated with a different SSA name class: %d.\n",
9759 // And the current node must be of the specified SSA family.
9763 Compiler::printTreeID(node);
9764 printf(", " FMT_VN " was declared in SSA name class %d,\n", nodeVN, tlAndN.m_num);
9765 printf("but that name class was previously bound to a different value number: " FMT_VN ".\n", vn);
9772 // The mapping(s) must be one-to-one: if the label has no mapping, then the ssaNm may not, either.
9773 if (vnToLabel->Lookup(nodeVN, &num))
9776 Compiler::printTreeID(node);
9777 printf(", " FMT_VN " was declared in value number class %d,\n", nodeVN, tlAndN.m_num);
9779 "but this value number has already been associated with a different value number class: %d.\n",
9783 // Add to both mappings.
9784 labelToVN->Set(tlAndN.m_num, nodeVN);
9785 vnToLabel->Set(nodeVN, tlAndN.m_num);
9788 printf(" added to hash tables.\n");
9795 void Compiler::vnpPrint(ValueNumPair vnp, unsigned level)
9797 if (vnp.BothEqual())
9799 vnPrint(vnp.GetLiberal(), level);
9804 vnPrint(vnp.GetLiberal(), level);
9806 vnPrint(vnp.GetConservative(), level);
9811 void Compiler::vnPrint(ValueNum vn, unsigned level)
9814 if (ValueNumStore::isReservedVN(vn))
9816 printf(ValueNumStore::reservedName(vn));
9823 vnStore->vnDump(this, vn);
9830 // Methods of ValueNumPair.
9831 ValueNumPair::ValueNumPair() : m_liberal(ValueNumStore::NoVN), m_conservative(ValueNumStore::NoVN)
9835 bool ValueNumPair::BothDefined() const
9837 return (m_liberal != ValueNumStore::NoVN) && (m_conservative != ValueNumStore::NoVN);