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.
4
5/*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
6XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
7XX XX
8XX typeInfo XX
9XX XX
10XX XX
11XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
12XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
13*/
14
15#include "jitpch.h"
16#ifdef _MSC_VER
17#pragma hdrstop
18#endif
19
20#include "_typeinfo.h"
21
22BOOL Compiler::tiCompatibleWith(const typeInfo& child, const typeInfo& parent, bool normalisedForStack) const
23{
24#ifdef DEBUG
25#if VERBOSE_VERIFY
26 if (VERBOSE && tiVerificationNeeded)
27 {
28 printf("\n");
29 printf(TI_DUMP_PADDING);
30 printf("Verifying compatibility against types: ");
31 child.Dump();
32 printf(" and ");
33 parent.Dump();
34 }
35#endif // VERBOSE_VERIFY
36#endif // DEBUG
37
38 BOOL compatible = typeInfo::tiCompatibleWith(info.compCompHnd, child, parent, normalisedForStack);
39
40#ifdef DEBUG
41#if VERBOSE_VERIFY
42 if (VERBOSE && tiVerificationNeeded)
43 {
44 printf(compatible ? " [YES]" : " [NO]");
45 }
46#endif // VERBOSE_VERIFY
47#endif // DEBUG
48
49 return compatible;
50}
51
52BOOL Compiler::tiMergeCompatibleWith(const typeInfo& child, const typeInfo& parent, bool normalisedForStack) const
53{
54 return typeInfo::tiMergeCompatibleWith(info.compCompHnd, child, parent, normalisedForStack);
55}
56
57BOOL Compiler::tiMergeToCommonParent(typeInfo* pDest, const typeInfo* pSrc, bool* changed) const
58{
59#ifdef DEBUG
60#if VERBOSE_VERIFY
61 if (VERBOSE && tiVerificationNeeded)
62 {
63 printf("\n");
64 printf(TI_DUMP_PADDING);
65 printf("Attempting to merge types: ");
66 pDest->Dump();
67 printf(" and ");
68 pSrc->Dump();
69 printf("\n");
70 }
71#endif // VERBOSE_VERIFY
72#endif // DEBUG
73
74 BOOL mergeable = typeInfo::tiMergeToCommonParent(info.compCompHnd, pDest, pSrc, changed);
75
76#ifdef DEBUG
77#if VERBOSE_VERIFY
78 if (VERBOSE && tiVerificationNeeded)
79 {
80 printf(TI_DUMP_PADDING);
81 printf((mergeable == TRUE) ? "Merge successful" : "Couldn't merge types");
82 if (*changed)
83 {
84 assert(mergeable);
85 printf(", destination type changed to: ");
86 pDest->Dump();
87 }
88 printf("\n");
89 }
90#endif // VERBOSE_VERIFY
91#endif // DEBUG
92
93 return mergeable;
94}
95
96static BOOL tiCompatibleWithByRef(COMP_HANDLE CompHnd, const typeInfo& child, const typeInfo& parent)
97{
98 assert(parent.IsByRef());
99
100 if (!child.IsByRef())
101 {
102 return FALSE;
103 }
104
105 if (child.IsReadonlyByRef() && !parent.IsReadonlyByRef())
106 {
107 return FALSE;
108 }
109
110 // Byrefs are compatible if the underlying types are equivalent
111 typeInfo childTarget = ::DereferenceByRef(child);
112 typeInfo parentTarget = ::DereferenceByRef(parent);
113
114 if (typeInfo::AreEquivalent(childTarget, parentTarget))
115 {
116 return TRUE;
117 }
118
119 // Make sure that both types have a valid m_cls
120 if ((childTarget.IsType(TI_REF) || childTarget.IsType(TI_STRUCT)) &&
121 (parentTarget.IsType(TI_REF) || parentTarget.IsType(TI_STRUCT)))
122 {
123 return CompHnd->areTypesEquivalent(childTarget.GetClassHandle(), parentTarget.GetClassHandle());
124 }
125
126 return FALSE;
127}
128
129/*****************************************************************************
130 * Verify child is compatible with the template parent. Basically, that
131 * child is a "subclass" of parent -it can be substituted for parent
132 * anywhere. Note that if parent contains fancy flags, such as "uninitialized"
133 * , "is this ptr", or "has byref local/field" info, then child must also
134 * contain those flags, otherwise FALSE will be returned !
135 *
136 * Rules for determining compatibility:
137 *
138 * If parent is a primitive type or value class, then child must be the
139 * same primitive type or value class. The exception is that the built in
140 * value classes System/Boolean etc. are treated as synonyms for
141 * TI_BYTE etc.
142 *
143 * If parent is a byref of a primitive type or value class, then child
144 * must be a byref of the same (rules same as above case).
145 *
146 * Byrefs are compatible only with byrefs.
147 *
148 * If parent is an object, child must be a subclass of it, implement it
149 * (if it is an interface), or be null.
150 *
151 * If parent is an array, child must be the same or subclassed array.
152 *
153 * If parent is a null objref, only null is compatible with it.
154 *
155 * If the "uninitialized", "by ref local/field", "this pointer" or other flags
156 * are different, the items are incompatible.
157 *
158 * parent CANNOT be an undefined (dead) item.
159 *
160 */
161
162BOOL typeInfo::tiCompatibleWith(COMP_HANDLE CompHnd,
163 const typeInfo& child,
164 const typeInfo& parent,
165 bool normalisedForStack)
166{
167 assert(child.IsDead() || !normalisedForStack || typeInfo::AreEquivalent(::NormaliseForStack(child), child));
168 assert(parent.IsDead() || !normalisedForStack || typeInfo::AreEquivalent(::NormaliseForStack(parent), parent));
169
170 if (typeInfo::AreEquivalent(child, parent))
171 {
172 return TRUE;
173 }
174
175 if (parent.IsUnboxedGenericTypeVar() || child.IsUnboxedGenericTypeVar())
176 {
177 return (FALSE); // need to have had child == parent
178 }
179 else if (parent.IsType(TI_REF))
180 {
181 // An uninitialized objRef is not compatible to initialized.
182 if (child.IsUninitialisedObjRef() && !parent.IsUninitialisedObjRef())
183 {
184 return FALSE;
185 }
186
187 if (child.IsNullObjRef())
188 { // NULL can be any reference type
189 return TRUE;
190 }
191 if (!child.IsType(TI_REF))
192 {
193 return FALSE;
194 }
195
196 return CompHnd->canCast(child.m_cls, parent.m_cls);
197 }
198 else if (parent.IsType(TI_METHOD))
199 {
200 if (!child.IsType(TI_METHOD))
201 {
202 return FALSE;
203 }
204
205 // Right now we don't bother merging method handles
206 return FALSE;
207 }
208 else if (parent.IsType(TI_STRUCT))
209 {
210 if (!child.IsType(TI_STRUCT))
211 {
212 return FALSE;
213 }
214
215 // Structures are compatible if they are equivalent
216 return CompHnd->areTypesEquivalent(child.m_cls, parent.m_cls);
217 }
218 else if (parent.IsByRef())
219 {
220 return tiCompatibleWithByRef(CompHnd, child, parent);
221 }
222#ifdef _TARGET_64BIT_
223 // On 64-bit targets we have precise representation for native int, so these rules
224 // represent the fact that the ECMA spec permits the implicit conversion
225 // between an int32 and a native int.
226 else if (parent.IsType(TI_INT) && typeInfo::AreEquivalent(nativeInt(), child))
227 {
228 return TRUE;
229 }
230 else if (typeInfo::AreEquivalent(nativeInt(), parent) && child.IsType(TI_INT))
231 {
232 return TRUE;
233 }
234#endif // _TARGET_64BIT_
235 return FALSE;
236}
237
238BOOL typeInfo::tiMergeCompatibleWith(COMP_HANDLE CompHnd,
239 const typeInfo& child,
240 const typeInfo& parent,
241 bool normalisedForStack)
242{
243 if (!child.IsPermanentHomeByRef() && parent.IsPermanentHomeByRef())
244 {
245 return FALSE;
246 }
247
248 return typeInfo::tiCompatibleWith(CompHnd, child, parent, normalisedForStack);
249}
250
251/*****************************************************************************
252 * Merge pDest and pSrc to find some commonality (e.g. a common parent).
253 * Copy the result to pDest, marking it dead if no commonality can be found.
254 *
255 * null ^ null -> null
256 * Object ^ null -> Object
257 * [I4 ^ null -> [I4
258 * InputStream ^ OutputStream -> Stream
259 * InputStream ^ NULL -> InputStream
260 * [I4 ^ Object -> Object
261 * [I4 ^ [Object -> Array
262 * [I4 ^ [R8 -> Array
263 * [Foo ^ I4 -> DEAD
264 * [Foo ^ [I1 -> Array
265 * [InputStream ^ [OutputStream -> Array
266 * DEAD ^ X -> DEAD
267 * [Intfc ^ [OutputStream -> Array
268 * Intf ^ [OutputStream -> Object
269 * [[InStream ^ [[OutStream -> Array
270 * [[InStream ^ [OutStream -> Array
271 * [[Foo ^ [Object -> Array
272 *
273 * Importantly:
274 * [I1 ^ [U1 -> either [I1 or [U1
275 * etc.
276 *
277 * Also, System/Int32 and I4 merge -> I4, etc.
278 *
279 * Returns FALSE if the merge was completely incompatible (i.e. the item became
280 * dead).
281 *
282 */
283
284BOOL typeInfo::tiMergeToCommonParent(COMP_HANDLE CompHnd, typeInfo* pDest, const typeInfo* pSrc, bool* changed)
285{
286 assert(pSrc->IsDead() || typeInfo::AreEquivalent(::NormaliseForStack(*pSrc), *pSrc));
287 assert(pDest->IsDead() || typeInfo::AreEquivalent(::NormaliseForStack(*pDest), *pDest));
288
289 // Merge the auxiliary information like "this" pointer tracking, etc...
290
291 // Remember the pre-state, so we can tell if it changed.
292 *changed = false;
293 DWORD destFlagsBefore = pDest->m_flags;
294
295 // This bit is only set if both pDest and pSrc have it set
296 pDest->m_flags &= (pSrc->m_flags | ~TI_FLAG_THIS_PTR);
297
298 // This bit is set if either pDest or pSrc have it set
299 pDest->m_flags |= (pSrc->m_flags & TI_FLAG_UNINIT_OBJREF);
300
301 // This bit is set if either pDest or pSrc have it set
302 pDest->m_flags |= (pSrc->m_flags & TI_FLAG_BYREF_READONLY);
303
304 // If the byref wasn't permanent home in both sides, then merge won't have the bit set
305 pDest->m_flags &= (pSrc->m_flags | ~TI_FLAG_BYREF_PERMANENT_HOME);
306
307 if (pDest->m_flags != destFlagsBefore)
308 {
309 *changed = true;
310 }
311
312 // OK the main event. Merge the main types
313 if (typeInfo::AreEquivalent(*pDest, *pSrc))
314 {
315 return (TRUE);
316 }
317
318 if (pDest->IsUnboxedGenericTypeVar() || pSrc->IsUnboxedGenericTypeVar())
319 {
320 // Should have had *pDest == *pSrc
321 goto FAIL;
322 }
323 if (pDest->IsType(TI_REF))
324 {
325 if (pSrc->IsType(TI_NULL))
326 { // NULL can be any reference type
327 return TRUE;
328 }
329 if (!pSrc->IsType(TI_REF))
330 {
331 goto FAIL;
332 }
333
334 // Ask the EE to find the common parent, This always succeeds since System.Object always works
335 CORINFO_CLASS_HANDLE pDestClsBefore = pDest->m_cls;
336 pDest->m_cls = CompHnd->mergeClasses(pDest->GetClassHandle(), pSrc->GetClassHandle());
337 if (pDestClsBefore != pDest->m_cls)
338 {
339 *changed = true;
340 }
341 return TRUE;
342 }
343 else if (pDest->IsType(TI_NULL))
344 {
345 if (pSrc->IsType(TI_REF)) // NULL can be any reference type
346 {
347 *pDest = *pSrc;
348 *changed = true;
349 return TRUE;
350 }
351 goto FAIL;
352 }
353 else if (pDest->IsType(TI_STRUCT))
354 {
355 if (pSrc->IsType(TI_STRUCT) && CompHnd->areTypesEquivalent(pDest->GetClassHandle(), pSrc->GetClassHandle()))
356 {
357 return TRUE;
358 }
359 goto FAIL;
360 }
361 else if (pDest->IsByRef())
362 {
363 return tiCompatibleWithByRef(CompHnd, *pSrc, *pDest);
364 }
365#ifdef _TARGET_64BIT_
366 // On 64-bit targets we have precise representation for native int, so these rules
367 // represent the fact that the ECMA spec permits the implicit conversion
368 // between an int32 and a native int.
369 else if (typeInfo::AreEquivalent(*pDest, typeInfo::nativeInt()) && pSrc->IsType(TI_INT))
370 {
371 return TRUE;
372 }
373 else if (typeInfo::AreEquivalent(*pSrc, typeInfo::nativeInt()) && pDest->IsType(TI_INT))
374 {
375 *pDest = *pSrc;
376 *changed = true;
377 return TRUE;
378 }
379#endif // _TARGET_64BIT_
380
381FAIL:
382 *pDest = typeInfo();
383 return FALSE;
384}
385
386#ifdef DEBUG
387#if VERBOSE_VERIFY
388// Utility method to have a detailed dump of a TypeInfo object
389void typeInfo::Dump() const
390{
391 char flagsStr[8];
392
393 flagsStr[0] = ((m_flags & TI_FLAG_UNINIT_OBJREF) != 0) ? 'U' : '-';
394 flagsStr[1] = ((m_flags & TI_FLAG_BYREF) != 0) ? 'B' : '-';
395 flagsStr[2] = ((m_flags & TI_FLAG_BYREF_READONLY) != 0) ? 'R' : '-';
396 flagsStr[3] = ((m_flags & TI_FLAG_NATIVE_INT) != 0) ? 'N' : '-';
397 flagsStr[4] = ((m_flags & TI_FLAG_THIS_PTR) != 0) ? 'T' : '-';
398 flagsStr[5] = ((m_flags & TI_FLAG_BYREF_PERMANENT_HOME) != 0) ? 'P' : '-';
399 flagsStr[6] = ((m_flags & TI_FLAG_GENERIC_TYPE_VAR) != 0) ? 'G' : '-';
400 flagsStr[7] = '\0';
401
402 printf("[%s(%X) {%s}]", tiType2Str(m_bits.type), m_cls, flagsStr);
403}
404#endif // VERBOSE_VERIFY
405#endif // DEBUG
406