summaryrefslogtreecommitdiff
path: root/src/jit/typeinfo.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/jit/typeinfo.cpp')
-rw-r--r--src/jit/typeinfo.cpp405
1 files changed, 405 insertions, 0 deletions
diff --git a/src/jit/typeinfo.cpp b/src/jit/typeinfo.cpp
new file mode 100644
index 0000000000..51429cca38
--- /dev/null
+++ b/src/jit/typeinfo.cpp
@@ -0,0 +1,405 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+XX XX
+XX typeInfo XX
+XX XX
+XX XX
+XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+*/
+
+#include "jitpch.h"
+#ifdef _MSC_VER
+#pragma hdrstop
+#endif
+
+#include "_typeinfo.h"
+
+BOOL Compiler::tiCompatibleWith(const typeInfo& child, const typeInfo& parent, bool normalisedForStack) const
+{
+#ifdef DEBUG
+#if VERBOSE_VERIFY
+ if (VERBOSE && tiVerificationNeeded)
+ {
+ printf("\n");
+ printf(TI_DUMP_PADDING);
+ printf("Verifying compatibility against types: ");
+ child.Dump();
+ printf(" and ");
+ parent.Dump();
+ }
+#endif // VERBOSE_VERIFY
+#endif // DEBUG
+
+ BOOL compatible = typeInfo::tiCompatibleWith(info.compCompHnd, child, parent, normalisedForStack);
+
+#ifdef DEBUG
+#if VERBOSE_VERIFY
+ if (VERBOSE && tiVerificationNeeded)
+ {
+ printf(compatible ? " [YES]" : " [NO]");
+ }
+#endif // VERBOSE_VERIFY
+#endif // DEBUG
+
+ return compatible;
+}
+
+BOOL Compiler::tiMergeCompatibleWith(const typeInfo& child, const typeInfo& parent, bool normalisedForStack) const
+{
+ return typeInfo::tiMergeCompatibleWith(info.compCompHnd, child, parent, normalisedForStack);
+}
+
+BOOL Compiler::tiMergeToCommonParent(typeInfo* pDest, const typeInfo* pSrc, bool* changed) const
+{
+#ifdef DEBUG
+#if VERBOSE_VERIFY
+ if (VERBOSE && tiVerificationNeeded)
+ {
+ printf("\n");
+ printf(TI_DUMP_PADDING);
+ printf("Attempting to merge types: ");
+ pDest->Dump();
+ printf(" and ");
+ pSrc->Dump();
+ printf("\n");
+ }
+#endif // VERBOSE_VERIFY
+#endif // DEBUG
+
+ BOOL mergeable = typeInfo::tiMergeToCommonParent(info.compCompHnd, pDest, pSrc, changed);
+
+#ifdef DEBUG
+#if VERBOSE_VERIFY
+ if (VERBOSE && tiVerificationNeeded)
+ {
+ printf(TI_DUMP_PADDING);
+ printf((mergeable == TRUE) ? "Merge successful" : "Couldn't merge types");
+ if (*changed)
+ {
+ assert(mergeable);
+ printf(", destination type changed to: ");
+ pDest->Dump();
+ }
+ printf("\n");
+ }
+#endif // VERBOSE_VERIFY
+#endif // DEBUG
+
+ return mergeable;
+}
+
+static BOOL tiCompatibleWithByRef(COMP_HANDLE CompHnd, const typeInfo& child, const typeInfo& parent)
+{
+ assert(parent.IsByRef());
+
+ if (!child.IsByRef())
+ {
+ return FALSE;
+ }
+
+ if (child.IsReadonlyByRef() && !parent.IsReadonlyByRef())
+ {
+ return FALSE;
+ }
+
+ // Byrefs are compatible if the underlying types are equivalent
+ typeInfo childTarget = ::DereferenceByRef(child);
+ typeInfo parentTarget = ::DereferenceByRef(parent);
+
+ if (typeInfo::AreEquivalent(childTarget, parentTarget))
+ {
+ return TRUE;
+ }
+
+ // Make sure that both types have a valid m_cls
+ if ((childTarget.IsType(TI_REF) || childTarget.IsType(TI_STRUCT)) &&
+ (parentTarget.IsType(TI_REF) || parentTarget.IsType(TI_STRUCT)))
+ {
+ return CompHnd->areTypesEquivalent(childTarget.GetClassHandle(), parentTarget.GetClassHandle());
+ }
+
+ return FALSE;
+}
+
+/*****************************************************************************
+ * Verify child is compatible with the template parent. Basically, that
+ * child is a "subclass" of parent -it can be substituted for parent
+ * anywhere. Note that if parent contains fancy flags, such as "uninitialized"
+ * , "is this ptr", or "has byref local/field" info, then child must also
+ * contain those flags, otherwise FALSE will be returned !
+ *
+ * Rules for determining compatibility:
+ *
+ * If parent is a primitive type or value class, then child must be the
+ * same primitive type or value class. The exception is that the built in
+ * value classes System/Boolean etc. are treated as synonyms for
+ * TI_BYTE etc.
+ *
+ * If parent is a byref of a primitive type or value class, then child
+ * must be a byref of the same (rules same as above case).
+ *
+ * Byrefs are compatible only with byrefs.
+ *
+ * If parent is an object, child must be a subclass of it, implement it
+ * (if it is an interface), or be null.
+ *
+ * If parent is an array, child must be the same or subclassed array.
+ *
+ * If parent is a null objref, only null is compatible with it.
+ *
+ * If the "uninitialized", "by ref local/field", "this pointer" or other flags
+ * are different, the items are incompatible.
+ *
+ * parent CANNOT be an undefined (dead) item.
+ *
+ */
+
+BOOL typeInfo::tiCompatibleWith(COMP_HANDLE CompHnd,
+ const typeInfo& child,
+ const typeInfo& parent,
+ bool normalisedForStack)
+{
+ assert(child.IsDead() || !normalisedForStack || typeInfo::AreEquivalent(::NormaliseForStack(child), child));
+ assert(parent.IsDead() || !normalisedForStack || typeInfo::AreEquivalent(::NormaliseForStack(parent), parent));
+
+ if (typeInfo::AreEquivalent(child, parent))
+ {
+ return TRUE;
+ }
+
+ if (parent.IsUnboxedGenericTypeVar() || child.IsUnboxedGenericTypeVar())
+ {
+ return (FALSE); // need to have had child == parent
+ }
+ else if (parent.IsType(TI_REF))
+ {
+ // An uninitialized objRef is not compatible to initialized.
+ if (child.IsUninitialisedObjRef() && !parent.IsUninitialisedObjRef())
+ {
+ return FALSE;
+ }
+
+ if (child.IsNullObjRef())
+ { // NULL can be any reference type
+ return TRUE;
+ }
+ if (!child.IsType(TI_REF))
+ {
+ return FALSE;
+ }
+
+ return CompHnd->canCast(child.m_cls, parent.m_cls);
+ }
+ else if (parent.IsType(TI_METHOD))
+ {
+ if (!child.IsType(TI_METHOD))
+ {
+ return FALSE;
+ }
+
+ // Right now we don't bother merging method handles
+ return FALSE;
+ }
+ else if (parent.IsType(TI_STRUCT))
+ {
+ if (!child.IsType(TI_STRUCT))
+ {
+ return FALSE;
+ }
+
+ // Structures are compatible if they are equivalent
+ return CompHnd->areTypesEquivalent(child.m_cls, parent.m_cls);
+ }
+ else if (parent.IsByRef())
+ {
+ return tiCompatibleWithByRef(CompHnd, child, parent);
+ }
+#ifdef _TARGET_64BIT_
+ // On 64-bit targets we have precise representation for native int, so these rules
+ // represent the fact that the ECMA spec permits the implicit conversion
+ // between an int32 and a native int.
+ else if (parent.IsType(TI_INT) && typeInfo::AreEquivalent(nativeInt(), child))
+ {
+ return TRUE;
+ }
+ else if (typeInfo::AreEquivalent(nativeInt(), parent) && child.IsType(TI_INT))
+ {
+ return TRUE;
+ }
+#endif // _TARGET_64BIT_
+ return FALSE;
+}
+
+BOOL typeInfo::tiMergeCompatibleWith(COMP_HANDLE CompHnd,
+ const typeInfo& child,
+ const typeInfo& parent,
+ bool normalisedForStack)
+{
+ if (!child.IsPermanentHomeByRef() && parent.IsPermanentHomeByRef())
+ {
+ return FALSE;
+ }
+
+ return typeInfo::tiCompatibleWith(CompHnd, child, parent, normalisedForStack);
+}
+
+/*****************************************************************************
+ * Merge pDest and pSrc to find some commonality (e.g. a common parent).
+ * Copy the result to pDest, marking it dead if no commonality can be found.
+ *
+ * null ^ null -> null
+ * Object ^ null -> Object
+ * [I4 ^ null -> [I4
+ * InputStream ^ OutputStream -> Stream
+ * InputStream ^ NULL -> InputStream
+ * [I4 ^ Object -> Object
+ * [I4 ^ [Object -> Array
+ * [I4 ^ [R8 -> Array
+ * [Foo ^ I4 -> DEAD
+ * [Foo ^ [I1 -> Array
+ * [InputStream ^ [OutputStream -> Array
+ * DEAD ^ X -> DEAD
+ * [Intfc ^ [OutputStream -> Array
+ * Intf ^ [OutputStream -> Object
+ * [[InStream ^ [[OutStream -> Array
+ * [[InStream ^ [OutStream -> Array
+ * [[Foo ^ [Object -> Array
+ *
+ * Importantly:
+ * [I1 ^ [U1 -> either [I1 or [U1
+ * etc.
+ *
+ * Also, System/Int32 and I4 merge -> I4, etc.
+ *
+ * Returns FALSE if the merge was completely incompatible (i.e. the item became
+ * dead).
+ *
+ */
+
+BOOL typeInfo::tiMergeToCommonParent(COMP_HANDLE CompHnd, typeInfo* pDest, const typeInfo* pSrc, bool* changed)
+{
+ assert(pSrc->IsDead() || typeInfo::AreEquivalent(::NormaliseForStack(*pSrc), *pSrc));
+ assert(pDest->IsDead() || typeInfo::AreEquivalent(::NormaliseForStack(*pDest), *pDest));
+
+ // Merge the auxiliary information like "this" pointer tracking, etc...
+
+ // Remember the pre-state, so we can tell if it changed.
+ *changed = false;
+ DWORD destFlagsBefore = pDest->m_flags;
+
+ // This bit is only set if both pDest and pSrc have it set
+ pDest->m_flags &= (pSrc->m_flags | ~TI_FLAG_THIS_PTR);
+
+ // This bit is set if either pDest or pSrc have it set
+ pDest->m_flags |= (pSrc->m_flags & TI_FLAG_UNINIT_OBJREF);
+
+ // This bit is set if either pDest or pSrc have it set
+ pDest->m_flags |= (pSrc->m_flags & TI_FLAG_BYREF_READONLY);
+
+ // If the byref wasn't permanent home in both sides, then merge won't have the bit set
+ pDest->m_flags &= (pSrc->m_flags | ~TI_FLAG_BYREF_PERMANENT_HOME);
+
+ if (pDest->m_flags != destFlagsBefore)
+ {
+ *changed = true;
+ }
+
+ // OK the main event. Merge the main types
+ if (typeInfo::AreEquivalent(*pDest, *pSrc))
+ {
+ return (TRUE);
+ }
+
+ if (pDest->IsUnboxedGenericTypeVar() || pSrc->IsUnboxedGenericTypeVar())
+ {
+ // Should have had *pDest == *pSrc
+ goto FAIL;
+ }
+ if (pDest->IsType(TI_REF))
+ {
+ if (pSrc->IsType(TI_NULL))
+ { // NULL can be any reference type
+ return TRUE;
+ }
+ if (!pSrc->IsType(TI_REF))
+ {
+ goto FAIL;
+ }
+
+ // Ask the EE to find the common parent, This always succeeds since System.Object always works
+ CORINFO_CLASS_HANDLE pDestClsBefore = pDest->m_cls;
+ pDest->m_cls = CompHnd->mergeClasses(pDest->GetClassHandle(), pSrc->GetClassHandle());
+ if (pDestClsBefore != pDest->m_cls)
+ {
+ *changed = true;
+ }
+ return TRUE;
+ }
+ else if (pDest->IsType(TI_NULL))
+ {
+ if (pSrc->IsType(TI_REF)) // NULL can be any reference type
+ {
+ *pDest = *pSrc;
+ *changed = true;
+ return TRUE;
+ }
+ goto FAIL;
+ }
+ else if (pDest->IsType(TI_STRUCT))
+ {
+ if (pSrc->IsType(TI_STRUCT) && CompHnd->areTypesEquivalent(pDest->GetClassHandle(), pSrc->GetClassHandle()))
+ {
+ return TRUE;
+ }
+ goto FAIL;
+ }
+ else if (pDest->IsByRef())
+ {
+ return tiCompatibleWithByRef(CompHnd, *pSrc, *pDest);
+ }
+#ifdef _TARGET_64BIT_
+ // On 64-bit targets we have precise representation for native int, so these rules
+ // represent the fact that the ECMA spec permits the implicit conversion
+ // between an int32 and a native int.
+ else if (typeInfo::AreEquivalent(*pDest, typeInfo::nativeInt()) && pSrc->IsType(TI_INT))
+ {
+ return TRUE;
+ }
+ else if (typeInfo::AreEquivalent(*pSrc, typeInfo::nativeInt()) && pDest->IsType(TI_INT))
+ {
+ *pDest = *pSrc;
+ *changed = true;
+ return TRUE;
+ }
+#endif // _TARGET_64BIT_
+
+FAIL:
+ *pDest = typeInfo();
+ return FALSE;
+}
+
+#ifdef DEBUG
+#if VERBOSE_VERIFY
+// Utility method to have a detailed dump of a TypeInfo object
+void typeInfo::Dump() const
+{
+ char flagsStr[8];
+
+ flagsStr[0] = ((m_flags & TI_FLAG_UNINIT_OBJREF) != 0) ? 'U' : '-';
+ flagsStr[1] = ((m_flags & TI_FLAG_BYREF) != 0) ? 'B' : '-';
+ flagsStr[2] = ((m_flags & TI_FLAG_BYREF_READONLY) != 0) ? 'R' : '-';
+ flagsStr[3] = ((m_flags & TI_FLAG_NATIVE_INT) != 0) ? 'N' : '-';
+ flagsStr[4] = ((m_flags & TI_FLAG_THIS_PTR) != 0) ? 'T' : '-';
+ flagsStr[5] = ((m_flags & TI_FLAG_BYREF_PERMANENT_HOME) != 0) ? 'P' : '-';
+ flagsStr[6] = ((m_flags & TI_FLAG_GENERIC_TYPE_VAR) != 0) ? 'G' : '-';
+ flagsStr[7] = '\0';
+
+ printf("[%s(%X) {%s}]", tiType2Str(m_bits.type), m_cls, flagsStr);
+}
+#endif // VERBOSE_VERIFY
+#endif // DEBUG