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 | // File: JITinterface.CPP |
6 | // |
7 | |
8 | // =========================================================================== |
9 | |
10 | |
11 | #include "common.h" |
12 | #include "jitinterface.h" |
13 | #include "codeman.h" |
14 | #include "method.hpp" |
15 | #include "class.h" |
16 | #include "object.h" |
17 | #include "field.h" |
18 | #include "stublink.h" |
19 | #include "virtualcallstub.h" |
20 | #include "corjit.h" |
21 | #include "eeconfig.h" |
22 | #include "excep.h" |
23 | #include "log.h" |
24 | #include "excep.h" |
25 | #include "float.h" // for isnan |
26 | #include "dbginterface.h" |
27 | #include "dllimport.h" |
28 | #include "gcheaputilities.h" |
29 | #include "comdelegate.h" |
30 | #include "jitperf.h" // to track jit perf |
31 | #include "corprof.h" |
32 | #include "eeprofinterfaces.h" |
33 | #include "perfcounters.h" |
34 | #ifdef PROFILING_SUPPORTED |
35 | #include "proftoeeinterfaceimpl.h" |
36 | #include "eetoprofinterfaceimpl.h" |
37 | #include "eetoprofinterfaceimpl.inl" |
38 | #include "profilepriv.h" |
39 | #endif |
40 | #include "ecall.h" |
41 | #include "generics.h" |
42 | #include "typestring.h" |
43 | #include "stackprobe.h" |
44 | #include "typedesc.h" |
45 | #include "genericdict.h" |
46 | #include "array.h" |
47 | #include "debuginfostore.h" |
48 | #include "safemath.h" |
49 | #include "runtimehandles.h" |
50 | #include "sigbuilder.h" |
51 | #include "openum.h" |
52 | #ifdef HAVE_GCCOVER |
53 | #include "gccover.h" |
54 | #endif // HAVE_GCCOVER |
55 | |
56 | #include "mdaassistants.h" |
57 | |
58 | #ifdef FEATURE_PREJIT |
59 | #include "compile.h" |
60 | #include "corcompile.h" |
61 | #endif // FEATURE_PREJIT |
62 | |
63 | |
64 | #ifdef FEATURE_INTERPRETER |
65 | #include "interpreter.h" |
66 | #endif // FEATURE_INTERPRETER |
67 | |
68 | #ifdef FEATURE_PERFMAP |
69 | #include "perfmap.h" |
70 | #endif |
71 | |
72 | // The Stack Overflow probe takes place in the COOPERATIVE_TRANSITION_BEGIN() macro |
73 | // |
74 | |
75 | #define JIT_TO_EE_TRANSITION() MAKE_CURRENT_THREAD_AVAILABLE_EX(m_pThread); \ |
76 | _ASSERTE(CURRENT_THREAD == GetThread()); \ |
77 | INSTALL_UNWIND_AND_CONTINUE_HANDLER_NO_PROBE; \ |
78 | COOPERATIVE_TRANSITION_BEGIN(); \ |
79 | START_NON_JIT_PERF(); |
80 | |
81 | #define EE_TO_JIT_TRANSITION() STOP_NON_JIT_PERF(); \ |
82 | COOPERATIVE_TRANSITION_END(); \ |
83 | UNINSTALL_UNWIND_AND_CONTINUE_HANDLER_NO_PROBE; |
84 | |
85 | #define JIT_TO_EE_TRANSITION_LEAF() |
86 | #define EE_TO_JIT_TRANSITION_LEAF() |
87 | |
88 | |
89 | #if defined(CROSSGEN_COMPILE) |
90 | static const char *const hlpNameTable[CORINFO_HELP_COUNT] = { |
91 | #define JITHELPER(code, pfnHelper, sig) #code, |
92 | #include "jithelpers.h" |
93 | }; |
94 | #endif |
95 | |
96 | #ifdef DACCESS_COMPILE |
97 | |
98 | // The real definitions are in jithelpers.cpp. However, those files are not included in the DAC build. |
99 | // Hence, we add them here. |
100 | GARY_IMPL(VMHELPDEF, hlpFuncTable, CORINFO_HELP_COUNT); |
101 | GARY_IMPL(VMHELPDEF, hlpDynamicFuncTable, DYNAMIC_CORINFO_HELP_COUNT); |
102 | |
103 | #else // DACCESS_COMPILE |
104 | |
105 | /*********************************************************************/ |
106 | |
107 | #if defined(ENABLE_PERF_COUNTERS) |
108 | LARGE_INTEGER g_lastTimeInJitCompilation; |
109 | #endif |
110 | |
111 | /*********************************************************************/ |
112 | |
113 | inline CORINFO_MODULE_HANDLE GetScopeHandle(MethodDesc* method) |
114 | { |
115 | LIMITED_METHOD_CONTRACT; |
116 | if (method->IsDynamicMethod()) |
117 | { |
118 | return MakeDynamicScope(method->AsDynamicMethodDesc()->GetResolver()); |
119 | } |
120 | else |
121 | { |
122 | return GetScopeHandle(method->GetModule()); |
123 | } |
124 | } |
125 | |
126 | //This is common refactored code from within several of the access check functions. |
127 | BOOL ModifyCheckForDynamicMethod(DynamicResolver *pResolver, |
128 | TypeHandle *pOwnerTypeForSecurity, |
129 | AccessCheckOptions::AccessCheckType *pAccessCheckType, |
130 | DynamicResolver** ppAccessContext) |
131 | { |
132 | CONTRACTL { |
133 | STANDARD_VM_CHECK; |
134 | PRECONDITION(CheckPointer(pResolver)); |
135 | PRECONDITION(CheckPointer(pOwnerTypeForSecurity)); |
136 | PRECONDITION(CheckPointer(pAccessCheckType)); |
137 | PRECONDITION(CheckPointer(ppAccessContext)); |
138 | PRECONDITION(*pAccessCheckType == AccessCheckOptions::kNormalAccessibilityChecks); |
139 | } CONTRACTL_END; |
140 | |
141 | BOOL doAccessCheck = TRUE; |
142 | |
143 | //Do not blindly initialize fields, since they've already got important values. |
144 | DynamicResolver::SecurityControlFlags dwSecurityFlags = DynamicResolver::Default; |
145 | |
146 | TypeHandle dynamicOwner; |
147 | pResolver->GetJitContext(&dwSecurityFlags, &dynamicOwner); |
148 | if (!dynamicOwner.IsNull()) |
149 | *pOwnerTypeForSecurity = dynamicOwner; |
150 | |
151 | if (dwSecurityFlags & DynamicResolver::SkipVisibilityChecks) |
152 | { |
153 | doAccessCheck = FALSE; |
154 | } |
155 | else if (dwSecurityFlags & DynamicResolver::RestrictedSkipVisibilityChecks) |
156 | { |
157 | *pAccessCheckType = AccessCheckOptions::kRestrictedMemberAccessNoTransparency; |
158 | } |
159 | else |
160 | { |
161 | *pAccessCheckType = AccessCheckOptions::kNormalAccessNoTransparency; |
162 | } |
163 | |
164 | return doAccessCheck; |
165 | } |
166 | |
167 | /*****************************************************************************/ |
168 | |
169 | // Initialize from data we passed across to the JIT |
170 | inline static void GetTypeContext(const CORINFO_SIG_INST *info, SigTypeContext *pTypeContext) |
171 | { |
172 | LIMITED_METHOD_CONTRACT; |
173 | SigTypeContext::InitTypeContext( |
174 | Instantiation((TypeHandle *) info->classInst, info->classInstCount), |
175 | Instantiation((TypeHandle *) info->methInst, info->methInstCount), |
176 | pTypeContext); |
177 | } |
178 | |
179 | static MethodDesc* GetMethodFromContext(CORINFO_CONTEXT_HANDLE context) |
180 | { |
181 | LIMITED_METHOD_CONTRACT; |
182 | if (((size_t) context & CORINFO_CONTEXTFLAGS_MASK) == CORINFO_CONTEXTFLAGS_CLASS) |
183 | { |
184 | return NULL; |
185 | } |
186 | else |
187 | { |
188 | return GetMethod((CORINFO_METHOD_HANDLE)((size_t) context & ~CORINFO_CONTEXTFLAGS_MASK)); |
189 | } |
190 | } |
191 | |
192 | static TypeHandle GetTypeFromContext(CORINFO_CONTEXT_HANDLE context) |
193 | { |
194 | LIMITED_METHOD_CONTRACT; |
195 | if (((size_t) context & CORINFO_CONTEXTFLAGS_MASK) == CORINFO_CONTEXTFLAGS_CLASS) |
196 | { |
197 | return TypeHandle((CORINFO_CLASS_HANDLE) ((size_t) context & ~CORINFO_CONTEXTFLAGS_MASK)); |
198 | } |
199 | else |
200 | { |
201 | MethodTable * pMT = GetMethodFromContext(context)->GetMethodTable(); |
202 | return TypeHandle(pMT); |
203 | } |
204 | } |
205 | |
206 | // Initialize from a context parameter passed to the JIT and back. This is a parameter |
207 | // that indicates which method is being jitted. |
208 | |
209 | inline static void GetTypeContext(CORINFO_CONTEXT_HANDLE context, SigTypeContext *pTypeContext) |
210 | { |
211 | CONTRACTL |
212 | { |
213 | NOTHROW; |
214 | GC_NOTRIGGER; |
215 | SO_TOLERANT; |
216 | MODE_ANY; |
217 | PRECONDITION(context != NULL); |
218 | } |
219 | CONTRACTL_END; |
220 | if (GetMethodFromContext(context)) |
221 | { |
222 | SigTypeContext::InitTypeContext(GetMethodFromContext(context), pTypeContext); |
223 | } |
224 | else |
225 | { |
226 | SigTypeContext::InitTypeContext(GetTypeFromContext(context), pTypeContext); |
227 | } |
228 | } |
229 | |
230 | static BOOL ContextIsShared(CORINFO_CONTEXT_HANDLE context) |
231 | { |
232 | LIMITED_METHOD_CONTRACT; |
233 | MethodDesc *pContextMD = GetMethodFromContext(context); |
234 | if (pContextMD != NULL) |
235 | { |
236 | return pContextMD->IsSharedByGenericInstantiations(); |
237 | } |
238 | else |
239 | { |
240 | // Type handle contexts are non-shared and are used for inlining of |
241 | // non-generic methods in generic classes |
242 | return FALSE; |
243 | } |
244 | } |
245 | |
246 | // Returns true if context is providing any generic variables |
247 | static BOOL ContextIsInstantiated(CORINFO_CONTEXT_HANDLE context) |
248 | { |
249 | LIMITED_METHOD_CONTRACT; |
250 | if (GetMethodFromContext(context)) |
251 | { |
252 | return GetMethodFromContext(context)->HasClassOrMethodInstantiation(); |
253 | } |
254 | else |
255 | { |
256 | return GetTypeFromContext(context).HasInstantiation(); |
257 | } |
258 | } |
259 | |
260 | /*********************************************************************/ |
261 | // This normalizes EE type information into the form expected by the JIT. |
262 | // |
263 | // If typeHnd contains exact type information, then *clsRet will contain |
264 | // the normalized CORINFO_CLASS_HANDLE information on return. |
265 | |
266 | // Static |
267 | CorInfoType CEEInfo::asCorInfoType(CorElementType eeType, |
268 | TypeHandle typeHnd, /* optional in */ |
269 | CORINFO_CLASS_HANDLE *clsRet/* optional out */ ) { |
270 | CONTRACT(CorInfoType) { |
271 | THROWS; |
272 | GC_TRIGGERS; |
273 | PRECONDITION((CorTypeInfo::IsGenericVariable(eeType)) == |
274 | (!typeHnd.IsNull() && typeHnd.IsGenericVariable())); |
275 | PRECONDITION(eeType != ELEMENT_TYPE_GENERICINST); |
276 | } CONTRACT_END; |
277 | |
278 | TypeHandle typeHndUpdated = typeHnd; |
279 | |
280 | if (!typeHnd.IsNull()) |
281 | { |
282 | CorElementType normType = typeHnd.GetInternalCorElementType(); |
283 | // If we have a type handle, then it has the better type |
284 | // in some cases |
285 | if (eeType == ELEMENT_TYPE_VALUETYPE && !CorTypeInfo::IsObjRef(normType)) |
286 | eeType = normType; |
287 | |
288 | // Zap the typeHnd when the type _really_ is a primitive |
289 | // as far as verification is concerned. Returning a null class |
290 | // handle means it is is a primitive. |
291 | // |
292 | // Enums are exactly like primitives, even from a verification standpoint, |
293 | // so we zap the type handle in this case. |
294 | // |
295 | // However RuntimeTypeHandle etc. are reported as E_T_INT (or something like that) |
296 | // but don't count as primitives as far as verification is concerned... |
297 | // |
298 | // To make things stranger, TypedReference returns true for "IsTruePrimitive". |
299 | // However the JIT likes us to report the type handle in that case. |
300 | if (!typeHnd.IsTypeDesc() && ( |
301 | (typeHnd.AsMethodTable()->IsTruePrimitive() && typeHnd != TypeHandle(g_TypedReferenceMT)) |
302 | || typeHnd.AsMethodTable()->IsEnum()) ) |
303 | { |
304 | typeHndUpdated = TypeHandle(); |
305 | } |
306 | |
307 | } |
308 | |
309 | static const BYTE map[] = { |
310 | CORINFO_TYPE_UNDEF, |
311 | CORINFO_TYPE_VOID, |
312 | CORINFO_TYPE_BOOL, |
313 | CORINFO_TYPE_CHAR, |
314 | CORINFO_TYPE_BYTE, |
315 | CORINFO_TYPE_UBYTE, |
316 | CORINFO_TYPE_SHORT, |
317 | CORINFO_TYPE_USHORT, |
318 | CORINFO_TYPE_INT, |
319 | CORINFO_TYPE_UINT, |
320 | CORINFO_TYPE_LONG, |
321 | CORINFO_TYPE_ULONG, |
322 | CORINFO_TYPE_FLOAT, |
323 | CORINFO_TYPE_DOUBLE, |
324 | CORINFO_TYPE_STRING, |
325 | CORINFO_TYPE_PTR, // PTR |
326 | CORINFO_TYPE_BYREF, |
327 | CORINFO_TYPE_VALUECLASS, |
328 | CORINFO_TYPE_CLASS, |
329 | CORINFO_TYPE_VAR, // VAR (type variable) |
330 | CORINFO_TYPE_CLASS, // ARRAY |
331 | CORINFO_TYPE_CLASS, // WITH |
332 | CORINFO_TYPE_REFANY, |
333 | CORINFO_TYPE_UNDEF, // VALUEARRAY_UNSUPPORTED |
334 | CORINFO_TYPE_NATIVEINT, // I |
335 | CORINFO_TYPE_NATIVEUINT, // U |
336 | CORINFO_TYPE_UNDEF, // R_UNSUPPORTED |
337 | |
338 | // put the correct type when we know our implementation |
339 | CORINFO_TYPE_PTR, // FNPTR |
340 | CORINFO_TYPE_CLASS, // OBJECT |
341 | CORINFO_TYPE_CLASS, // SZARRAY |
342 | CORINFO_TYPE_VAR, // MVAR |
343 | |
344 | CORINFO_TYPE_UNDEF, // CMOD_REQD |
345 | CORINFO_TYPE_UNDEF, // CMOD_OPT |
346 | CORINFO_TYPE_UNDEF, // INTERNAL |
347 | }; |
348 | |
349 | _ASSERTE(sizeof(map) == ELEMENT_TYPE_MAX); |
350 | _ASSERTE(eeType < (CorElementType) sizeof(map)); |
351 | // spot check of the map |
352 | _ASSERTE((CorInfoType) map[ELEMENT_TYPE_I4] == CORINFO_TYPE_INT); |
353 | _ASSERTE((CorInfoType) map[ELEMENT_TYPE_PTR] == CORINFO_TYPE_PTR); |
354 | _ASSERTE((CorInfoType) map[ELEMENT_TYPE_TYPEDBYREF] == CORINFO_TYPE_REFANY); |
355 | |
356 | CorInfoType res = ((unsigned)eeType < ELEMENT_TYPE_MAX) ? ((CorInfoType) map[(unsigned)eeType]) : CORINFO_TYPE_UNDEF; |
357 | |
358 | if (clsRet) |
359 | *clsRet = CORINFO_CLASS_HANDLE(typeHndUpdated.AsPtr()); |
360 | |
361 | RETURN res; |
362 | } |
363 | |
364 | |
365 | inline static CorInfoType toJitType(TypeHandle typeHnd, CORINFO_CLASS_HANDLE *clsRet = NULL) |
366 | { |
367 | WRAPPER_NO_CONTRACT; |
368 | return CEEInfo::asCorInfoType(typeHnd.GetInternalCorElementType(), typeHnd, clsRet); |
369 | } |
370 | |
371 | void CheckForEquivalenceAndLoadTypeBeforeCodeIsRun(Module *pModule, mdToken token, Module *pDefModule, mdToken defToken, const SigParser *ptr, SigTypeContext *pTypeContext, void *pData) |
372 | { |
373 | CONTRACTL |
374 | { |
375 | THROWS; |
376 | GC_TRIGGERS; |
377 | SO_INTOLERANT; |
378 | } |
379 | CONTRACTL_END; |
380 | |
381 | if (IsTypeDefEquivalent(defToken, pDefModule)) |
382 | { |
383 | SigPointer sigPtr(*ptr); |
384 | TypeHandle th = sigPtr.GetTypeHandleThrowing(pModule, pTypeContext); |
385 | ((ICorDynamicInfo *)pData)->classMustBeLoadedBeforeCodeIsRun(CORINFO_CLASS_HANDLE(th.AsPtr())); |
386 | } |
387 | } |
388 | |
389 | inline static void TypeEquivalenceFixupSpecificationHelper(ICorDynamicInfo * pCorInfo, MethodDesc *pMD) |
390 | { |
391 | STANDARD_VM_CONTRACT; |
392 | |
393 | // A fixup is necessary to ensure that the parameters to the method are loaded before the method |
394 | // is called. In these cases we will not perform the appropriate loading when we load parameter |
395 | // types because with type equivalence, the parameter types at the call site do not necessarily |
396 | // match that those in the actual function. (They must be equivalent, but not necessarily the same.) |
397 | // In non-ngen scenarios this code here will force the types to be loaded directly by the call to |
398 | // HasTypeEquivalentStructParameters. |
399 | if (!pMD->IsVirtual()) |
400 | { |
401 | if (pMD->HasTypeEquivalentStructParameters()) |
402 | { |
403 | if (IsCompilationProcess()) |
404 | pMD->WalkValueTypeParameters(pMD->GetMethodTable(), CheckForEquivalenceAndLoadTypeBeforeCodeIsRun, pCorInfo); |
405 | } |
406 | } |
407 | else |
408 | { |
409 | if (pMD->GetMethodTable()->DependsOnEquivalentOrForwardedStructs()) |
410 | { |
411 | if (pMD->HasTypeEquivalentStructParameters()) |
412 | pCorInfo->classMustBeLoadedBeforeCodeIsRun((CORINFO_CLASS_HANDLE)pMD->GetMethodTable()); |
413 | } |
414 | } |
415 | } |
416 | |
417 | //--------------------------------------------------------------------------------------- |
418 | // |
419 | //@GENERICS: |
420 | // The method handle is used to instantiate method and class type parameters |
421 | // It's also used to determine whether an extra dictionary parameter is required |
422 | // |
423 | // sig - Input metadata signature |
424 | // scopeHnd - The signature is to be interpreted in the context of this scope (module) |
425 | // token - Metadata token used to refer to the signature (may be mdTokenNil for dynamic methods) |
426 | // sigRet - Resulting output signature in a format that is understood by native compilers |
427 | // pContextMD - The method with any instantiation information (may be NULL) |
428 | // localSig - Is it a local variables declaration, or a method signature (with return type, etc). |
429 | // contextType - The type with any instantiaton information |
430 | // |
431 | //static |
432 | void |
433 | CEEInfo::ConvToJitSig( |
434 | PCCOR_SIGNATURE pSig, |
435 | DWORD cbSig, |
436 | CORINFO_MODULE_HANDLE scopeHnd, |
437 | mdToken token, |
438 | CORINFO_SIG_INFO * sigRet, |
439 | MethodDesc * pContextMD, |
440 | bool localSig, |
441 | TypeHandle contextType) |
442 | { |
443 | CONTRACTL { |
444 | THROWS; |
445 | GC_TRIGGERS; |
446 | } CONTRACTL_END; |
447 | |
448 | SigTypeContext typeContext; |
449 | |
450 | if (pContextMD) |
451 | { |
452 | SigTypeContext::InitTypeContext(pContextMD, contextType, &typeContext); |
453 | } |
454 | else |
455 | { |
456 | SigTypeContext::InitTypeContext(contextType, &typeContext); |
457 | } |
458 | |
459 | _ASSERTE(CORINFO_CALLCONV_DEFAULT == (CorInfoCallConv) IMAGE_CEE_CS_CALLCONV_DEFAULT); |
460 | _ASSERTE(CORINFO_CALLCONV_VARARG == (CorInfoCallConv) IMAGE_CEE_CS_CALLCONV_VARARG); |
461 | _ASSERTE(CORINFO_CALLCONV_MASK == (CorInfoCallConv) IMAGE_CEE_CS_CALLCONV_MASK); |
462 | _ASSERTE(CORINFO_CALLCONV_HASTHIS == (CorInfoCallConv) IMAGE_CEE_CS_CALLCONV_HASTHIS); |
463 | |
464 | TypeHandle typeHnd = TypeHandle(); |
465 | |
466 | sigRet->pSig = pSig; |
467 | sigRet->cbSig = cbSig; |
468 | sigRet->retTypeClass = 0; |
469 | sigRet->retTypeSigClass = 0; |
470 | sigRet->scope = scopeHnd; |
471 | sigRet->token = token; |
472 | sigRet->sigInst.classInst = (CORINFO_CLASS_HANDLE *) typeContext.m_classInst.GetRawArgs(); |
473 | sigRet->sigInst.classInstCount = (unsigned) typeContext.m_classInst.GetNumArgs(); |
474 | sigRet->sigInst.methInst = (CORINFO_CLASS_HANDLE *) typeContext.m_methodInst.GetRawArgs(); |
475 | sigRet->sigInst.methInstCount = (unsigned) typeContext.m_methodInst.GetNumArgs(); |
476 | |
477 | SigPointer sig(pSig, cbSig); |
478 | |
479 | if (!localSig) |
480 | { |
481 | // This is a method signature which includes calling convention, return type, |
482 | // arguments, etc |
483 | |
484 | _ASSERTE(!sig.IsNull()); |
485 | Module * module = GetModule(scopeHnd); |
486 | sigRet->flags = 0; |
487 | |
488 | ULONG data; |
489 | IfFailThrow(sig.GetCallingConvInfo(&data)); |
490 | sigRet->callConv = (CorInfoCallConv) data; |
491 | |
492 | #ifdef PLATFORM_UNIX |
493 | if ((isCallConv(sigRet->callConv, IMAGE_CEE_CS_CALLCONV_VARARG)) || |
494 | (isCallConv(sigRet->callConv, IMAGE_CEE_CS_CALLCONV_NATIVEVARARG))) |
495 | { |
496 | // This signature corresponds to a method that uses varargs, which are not supported. |
497 | COMPlusThrow(kInvalidProgramException, IDS_EE_VARARG_NOT_SUPPORTED); |
498 | } |
499 | #endif // PLATFORM_UNIX |
500 | |
501 | // Skip number of type arguments |
502 | if (sigRet->callConv & IMAGE_CEE_CS_CALLCONV_GENERIC) |
503 | IfFailThrow(sig.GetData(NULL)); |
504 | |
505 | ULONG numArgs; |
506 | IfFailThrow(sig.GetData(&numArgs)); |
507 | if (numArgs != (unsigned short) numArgs) |
508 | COMPlusThrowHR(COR_E_INVALIDPROGRAM); |
509 | |
510 | sigRet->numArgs = (unsigned short) numArgs; |
511 | |
512 | CorElementType type = sig.PeekElemTypeClosed(module, &typeContext); |
513 | |
514 | if (!CorTypeInfo::IsPrimitiveType(type)) |
515 | { |
516 | typeHnd = sig.GetTypeHandleThrowing(module, &typeContext); |
517 | _ASSERTE(!typeHnd.IsNull()); |
518 | |
519 | // I believe it doesn't make any diff. if this is |
520 | // GetInternalCorElementType or GetSignatureCorElementType |
521 | type = typeHnd.GetSignatureCorElementType(); |
522 | |
523 | } |
524 | sigRet->retType = CEEInfo::asCorInfoType(type, typeHnd, &sigRet->retTypeClass); |
525 | sigRet->retTypeSigClass = CORINFO_CLASS_HANDLE(typeHnd.AsPtr()); |
526 | |
527 | IfFailThrow(sig.SkipExactlyOne()); // must to a skip so we skip any class tokens associated with the return type |
528 | _ASSERTE(sigRet->retType < CORINFO_TYPE_COUNT); |
529 | |
530 | sigRet->args = (CORINFO_ARG_LIST_HANDLE)sig.GetPtr(); |
531 | } |
532 | else |
533 | { |
534 | // This is local variables declaration |
535 | |
536 | sigRet->callConv = CORINFO_CALLCONV_DEFAULT; |
537 | sigRet->retType = CORINFO_TYPE_VOID; |
538 | sigRet->flags = CORINFO_SIGFLAG_IS_LOCAL_SIG; |
539 | sigRet->numArgs = 0; |
540 | if (!sig.IsNull()) |
541 | { |
542 | ULONG callConv; |
543 | IfFailThrow(sig.GetCallingConvInfo(&callConv)); |
544 | if (callConv != IMAGE_CEE_CS_CALLCONV_LOCAL_SIG) |
545 | { |
546 | COMPlusThrowHR(COR_E_BADIMAGEFORMAT, BFA_CALLCONV_NOT_LOCAL_SIG); |
547 | } |
548 | |
549 | ULONG numArgs; |
550 | IfFailThrow(sig.GetData(&numArgs)); |
551 | |
552 | if (numArgs != (unsigned short) numArgs) |
553 | COMPlusThrowHR(COR_E_INVALIDPROGRAM); |
554 | |
555 | sigRet->numArgs = (unsigned short) numArgs; |
556 | } |
557 | |
558 | sigRet->args = (CORINFO_ARG_LIST_HANDLE)sig.GetPtr(); |
559 | } |
560 | |
561 | _ASSERTE(SigInfoFlagsAreValid(sigRet)); |
562 | } // CEEInfo::ConvToJitSig |
563 | |
564 | //--------------------------------------------------------------------------------------- |
565 | // |
566 | CORINFO_CLASS_HANDLE CEEInfo::getTokenTypeAsHandle (CORINFO_RESOLVED_TOKEN * pResolvedToken) |
567 | { |
568 | CONTRACTL { |
569 | SO_TOLERANT; |
570 | THROWS; |
571 | GC_TRIGGERS; |
572 | MODE_PREEMPTIVE; |
573 | } CONTRACTL_END; |
574 | |
575 | CORINFO_CLASS_HANDLE tokenType = NULL; |
576 | |
577 | JIT_TO_EE_TRANSITION(); |
578 | |
579 | _ASSERTE((pResolvedToken->hMethod == NULL) || (pResolvedToken->hField == NULL)); |
580 | |
581 | BinderClassID classID = CLASS__TYPE_HANDLE; |
582 | |
583 | if (pResolvedToken->hMethod != NULL) |
584 | { |
585 | classID = CLASS__METHOD_HANDLE; |
586 | } |
587 | else |
588 | if (pResolvedToken->hField != NULL) |
589 | { |
590 | classID = CLASS__FIELD_HANDLE; |
591 | } |
592 | |
593 | tokenType = CORINFO_CLASS_HANDLE(MscorlibBinder::GetClass(classID)); |
594 | |
595 | EE_TO_JIT_TRANSITION(); |
596 | |
597 | return tokenType; |
598 | } |
599 | |
600 | /*********************************************************************/ |
601 | size_t CEEInfo::findNameOfToken ( |
602 | CORINFO_MODULE_HANDLE scopeHnd, |
603 | mdToken metaTOK, |
604 | __out_ecount (FQNameCapacity) char * szFQName, |
605 | size_t FQNameCapacity) |
606 | { |
607 | CONTRACTL { |
608 | SO_TOLERANT; |
609 | THROWS; |
610 | GC_TRIGGERS; |
611 | MODE_PREEMPTIVE; |
612 | } CONTRACTL_END; |
613 | |
614 | size_t NameLen = 0; |
615 | |
616 | JIT_TO_EE_TRANSITION(); |
617 | |
618 | if (IsDynamicScope(scopeHnd)) |
619 | { |
620 | strncpy_s (szFQName, FQNameCapacity, "DynamicToken" , FQNameCapacity - 1); |
621 | NameLen = strlen (szFQName); |
622 | } |
623 | else |
624 | { |
625 | Module* module = (Module *)scopeHnd; |
626 | NameLen = findNameOfToken(module, metaTOK, szFQName, FQNameCapacity); |
627 | } |
628 | |
629 | EE_TO_JIT_TRANSITION(); |
630 | |
631 | return NameLen; |
632 | } |
633 | |
634 | CorInfoCanSkipVerificationResult CEEInfo::canSkipMethodVerification(CORINFO_METHOD_HANDLE ftnHnd) |
635 | { |
636 | CONTRACTL { |
637 | SO_TOLERANT; |
638 | THROWS; |
639 | GC_TRIGGERS; |
640 | MODE_PREEMPTIVE; |
641 | } CONTRACTL_END; |
642 | |
643 | return CORINFO_VERIFICATION_CAN_SKIP; |
644 | } |
645 | |
646 | /*********************************************************************/ |
647 | BOOL CEEInfo::shouldEnforceCallvirtRestriction( |
648 | CORINFO_MODULE_HANDLE scopeHnd) |
649 | { |
650 | LIMITED_METHOD_CONTRACT; |
651 | return TRUE; |
652 | } |
653 | |
654 | #ifdef FEATURE_READYTORUN_COMPILER |
655 | |
656 | // Returns true if assemblies are in the same version bubble |
657 | // Right now each assembly is in its own version bubble. |
658 | // If the need arises (i.e. performance issues) we will define sets of assemblies (e.g. all app assemblies) |
659 | // The main point is that all this logic is concentrated in one place. |
660 | |
661 | // NOTICE: If you change this logic to allow multi-assembly version bubbles you |
662 | // need to consider the impact on diagnostic tools. Currently there is an inlining |
663 | // table which tracks inliner/inlinee relationships in R2R images but it is not |
664 | // yet capable of encoding cross-assembly inlines. The scenario where this |
665 | // may show are instrumenting profilers that want to instrument a given method A |
666 | // using the ReJit APIs. If method A happens to inlined within method B in another |
667 | // assembly then the profiler needs to know that so it can rejit B too. |
668 | // The recommended approach is to upgrade the inlining table (vm\inlinetracking.h\.cpp) |
669 | // now that presumably R2R images have some way to refer to methods in other |
670 | // assemblies in their version bubble. Chat with the diagnostics team if you need more |
671 | // details. |
672 | // |
673 | // There already is a case where cross-assembly inlining occurs in an |
674 | // unreported fashion for methods marked NonVersionable. There is a specific |
675 | // exemption called out for this on ICorProfilerInfo6::EnumNgenModuleMethodsInliningThisMethod |
676 | // and the impact of the cut was vetted with partners. It would not be appropriate |
677 | // to increase that unreported set without additional review. |
678 | |
679 | |
680 | bool IsInSameVersionBubble(Assembly * current, Assembly * target) |
681 | { |
682 | LIMITED_METHOD_CONTRACT; |
683 | |
684 | // trivial case: current and target are identical |
685 | // DO NOT change this without reading the notice above |
686 | if (current == target) |
687 | return true; |
688 | |
689 | return false; |
690 | } |
691 | |
692 | // Returns true if the assemblies defining current and target are in the same version bubble |
693 | static bool IsInSameVersionBubble(MethodDesc* pCurMD, MethodDesc *pTargetMD) |
694 | { |
695 | LIMITED_METHOD_CONTRACT; |
696 | // DO NOT change this without reading the notice above |
697 | if (IsInSameVersionBubble(pCurMD->GetModule()->GetAssembly(), |
698 | pTargetMD->GetModule()->GetAssembly())) |
699 | { |
700 | return true; |
701 | } |
702 | if (IsReadyToRunCompilation()) |
703 | { |
704 | if (pTargetMD->GetModule()->GetMDImport()->GetCustomAttributeByName(pTargetMD->GetMemberDef(), |
705 | NONVERSIONABLE_TYPE, NULL, NULL) == S_OK) |
706 | { |
707 | return true; |
708 | } |
709 | } |
710 | return false; |
711 | |
712 | } |
713 | |
714 | #endif // FEATURE_READYTORUN_COMPILER |
715 | |
716 | static bool CallerAndCalleeInSystemVersionBubble(MethodDesc* pCaller, MethodDesc* pCallee) |
717 | { |
718 | LIMITED_METHOD_CONTRACT; |
719 | |
720 | #ifdef FEATURE_READYTORUN_COMPILER |
721 | if (IsReadyToRunCompilation()) |
722 | return pCallee->GetModule()->IsSystem() && IsInSameVersionBubble(pCaller, pCallee); |
723 | #endif |
724 | |
725 | return false; |
726 | } |
727 | |
728 | |
729 | /*********************************************************************/ |
730 | CorInfoCanSkipVerificationResult CEEInfo::canSkipVerification( |
731 | CORINFO_MODULE_HANDLE moduleHnd) |
732 | { |
733 | CONTRACTL { |
734 | SO_TOLERANT; |
735 | THROWS; |
736 | GC_TRIGGERS; |
737 | MODE_PREEMPTIVE; |
738 | } CONTRACTL_END; |
739 | |
740 | return CORINFO_VERIFICATION_CAN_SKIP; |
741 | } |
742 | |
743 | /*********************************************************************/ |
744 | // Checks if the given metadata token is valid |
745 | BOOL CEEInfo::isValidToken ( |
746 | CORINFO_MODULE_HANDLE module, |
747 | mdToken metaTOK) |
748 | { |
749 | CONTRACTL { |
750 | SO_TOLERANT; |
751 | NOTHROW; |
752 | GC_NOTRIGGER; |
753 | MODE_ANY; |
754 | } CONTRACTL_END; |
755 | |
756 | BOOL result = FALSE; |
757 | |
758 | JIT_TO_EE_TRANSITION_LEAF(); |
759 | |
760 | if (IsDynamicScope(module)) |
761 | { |
762 | // No explicit token validation for dynamic code. Validation is |
763 | // side-effect of token resolution. |
764 | result = TRUE; |
765 | } |
766 | else |
767 | { |
768 | result = ((Module *)module)->GetMDImport()->IsValidToken(metaTOK); |
769 | } |
770 | |
771 | EE_TO_JIT_TRANSITION_LEAF(); |
772 | |
773 | return result; |
774 | } |
775 | |
776 | /*********************************************************************/ |
777 | // Checks if the given metadata token is valid StringRef |
778 | BOOL CEEInfo::isValidStringRef ( |
779 | CORINFO_MODULE_HANDLE module, |
780 | mdToken metaTOK) |
781 | { |
782 | CONTRACTL { |
783 | SO_TOLERANT; |
784 | THROWS; |
785 | GC_TRIGGERS; |
786 | MODE_PREEMPTIVE; |
787 | } CONTRACTL_END; |
788 | |
789 | BOOL result = FALSE; |
790 | |
791 | JIT_TO_EE_TRANSITION(); |
792 | |
793 | if (IsDynamicScope(module)) |
794 | { |
795 | result = GetDynamicResolver(module)->IsValidStringRef(metaTOK); |
796 | } |
797 | else |
798 | { |
799 | result = ((Module *)module)->CheckStringRef(metaTOK); |
800 | if (result) |
801 | { |
802 | DWORD dwCharCount; |
803 | LPCWSTR pString; |
804 | result = (!FAILED(((Module *)module)->GetMDImport()->GetUserString(metaTOK, &dwCharCount, NULL, &pString)) && |
805 | pString != NULL); |
806 | } |
807 | } |
808 | |
809 | EE_TO_JIT_TRANSITION(); |
810 | |
811 | return result; |
812 | } |
813 | |
814 | /* static */ |
815 | size_t CEEInfo::findNameOfToken (Module* module, |
816 | mdToken metaTOK, |
817 | __out_ecount (FQNameCapacity) char * szFQName, |
818 | size_t FQNameCapacity) |
819 | { |
820 | CONTRACTL { |
821 | NOTHROW; |
822 | GC_TRIGGERS; |
823 | } CONTRACTL_END; |
824 | |
825 | #ifdef _DEBUG |
826 | PCCOR_SIGNATURE sig = NULL; |
827 | DWORD cSig; |
828 | LPCUTF8 pszNamespace = NULL; |
829 | LPCUTF8 pszClassName = NULL; |
830 | |
831 | mdToken tokType = TypeFromToken(metaTOK); |
832 | switch(tokType) |
833 | { |
834 | case mdtTypeRef: |
835 | { |
836 | if (FAILED(module->GetMDImport()->GetNameOfTypeRef(metaTOK, &pszNamespace, &pszClassName))) |
837 | { |
838 | pszNamespace = pszClassName = "Invalid TypeRef record" ; |
839 | } |
840 | ns::MakePath(szFQName, (int)FQNameCapacity, pszNamespace, pszClassName); |
841 | break; |
842 | } |
843 | case mdtTypeDef: |
844 | { |
845 | if (FAILED(module->GetMDImport()->GetNameOfTypeDef(metaTOK, &pszClassName, &pszNamespace))) |
846 | { |
847 | pszClassName = pszNamespace = "Invalid TypeDef record" ; |
848 | } |
849 | ns::MakePath(szFQName, (int)FQNameCapacity, pszNamespace, pszClassName); |
850 | break; |
851 | } |
852 | case mdtFieldDef: |
853 | { |
854 | LPCSTR szFieldName; |
855 | if (FAILED(module->GetMDImport()->GetNameOfFieldDef(metaTOK, &szFieldName))) |
856 | { |
857 | szFieldName = "Invalid FieldDef record" ; |
858 | } |
859 | strncpy_s(szFQName, FQNameCapacity, (char*)szFieldName, FQNameCapacity - 1); |
860 | break; |
861 | } |
862 | case mdtMethodDef: |
863 | { |
864 | LPCSTR szMethodName; |
865 | if (FAILED(module->GetMDImport()->GetNameOfMethodDef(metaTOK, &szMethodName))) |
866 | { |
867 | szMethodName = "Invalid MethodDef record" ; |
868 | } |
869 | strncpy_s(szFQName, FQNameCapacity, (char*)szMethodName, FQNameCapacity - 1); |
870 | break; |
871 | } |
872 | case mdtMemberRef: |
873 | { |
874 | LPCSTR szName; |
875 | if (FAILED(module->GetMDImport()->GetNameAndSigOfMemberRef((mdMemberRef)metaTOK, &sig, &cSig, &szName))) |
876 | { |
877 | szName = "Invalid MemberRef record" ; |
878 | } |
879 | strncpy_s(szFQName, FQNameCapacity, (char *)szName, FQNameCapacity - 1); |
880 | break; |
881 | } |
882 | default: |
883 | sprintf_s(szFQName, FQNameCapacity, "!TK_%x" , metaTOK); |
884 | break; |
885 | } |
886 | |
887 | #else // !_DEBUG |
888 | strncpy_s (szFQName, FQNameCapacity, "<UNKNOWN>" , FQNameCapacity - 1); |
889 | #endif // _DEBUG |
890 | |
891 | |
892 | return strlen (szFQName); |
893 | } |
894 | |
895 | CorInfoHelpFunc CEEInfo::getLazyStringLiteralHelper(CORINFO_MODULE_HANDLE handle) |
896 | { |
897 | CONTRACTL { |
898 | SO_TOLERANT; |
899 | NOTHROW; |
900 | GC_NOTRIGGER; |
901 | MODE_PREEMPTIVE; |
902 | } CONTRACTL_END; |
903 | |
904 | CorInfoHelpFunc result = CORINFO_HELP_UNDEF; |
905 | |
906 | JIT_TO_EE_TRANSITION_LEAF(); |
907 | |
908 | result = IsDynamicScope(handle) ? CORINFO_HELP_UNDEF : CORINFO_HELP_STRCNS; |
909 | |
910 | EE_TO_JIT_TRANSITION_LEAF(); |
911 | |
912 | return result; |
913 | } |
914 | |
915 | |
916 | CHECK CheckContext(CORINFO_MODULE_HANDLE scopeHnd, CORINFO_CONTEXT_HANDLE context) |
917 | { |
918 | CHECK_MSG(scopeHnd != NULL, "Illegal null scope" ); |
919 | CHECK_MSG(((size_t) context & ~CORINFO_CONTEXTFLAGS_MASK) != NULL, "Illegal null context" ); |
920 | if (((size_t) context & CORINFO_CONTEXTFLAGS_MASK) == CORINFO_CONTEXTFLAGS_CLASS) |
921 | { |
922 | TypeHandle handle((CORINFO_CLASS_HANDLE) ((size_t) context & ~CORINFO_CONTEXTFLAGS_MASK)); |
923 | CHECK_MSG(handle.GetModule() == GetModule(scopeHnd), "Inconsistent scope and context" ); |
924 | } |
925 | else |
926 | { |
927 | MethodDesc* handle = (MethodDesc*) ((size_t) context & ~CORINFO_CONTEXTFLAGS_MASK); |
928 | CHECK_MSG(handle->GetModule() == GetModule(scopeHnd), "Inconsistent scope and context" ); |
929 | } |
930 | |
931 | CHECK_OK; |
932 | } |
933 | |
934 | |
935 | static DECLSPEC_NORETURN void ThrowBadTokenException(CORINFO_RESOLVED_TOKEN * pResolvedToken) |
936 | { |
937 | switch (pResolvedToken->tokenType & CORINFO_TOKENKIND_Mask) |
938 | { |
939 | case CORINFO_TOKENKIND_Class: |
940 | COMPlusThrowHR(COR_E_BADIMAGEFORMAT, BFA_BAD_CLASS_TOKEN); |
941 | case CORINFO_TOKENKIND_Method: |
942 | COMPlusThrowHR(COR_E_BADIMAGEFORMAT, BFA_INVALID_METHOD_TOKEN); |
943 | case CORINFO_TOKENKIND_Field: |
944 | COMPlusThrowHR(COR_E_BADIMAGEFORMAT, BFA_BAD_FIELD_TOKEN); |
945 | default: |
946 | COMPlusThrowHR(COR_E_BADIMAGEFORMAT); |
947 | } |
948 | } |
949 | |
950 | /*********************************************************************/ |
951 | void CEEInfo::resolveToken(/* IN, OUT */ CORINFO_RESOLVED_TOKEN * pResolvedToken) |
952 | { |
953 | CONTRACTL { |
954 | SO_TOLERANT; |
955 | THROWS; |
956 | GC_TRIGGERS; |
957 | MODE_PREEMPTIVE; |
958 | } CONTRACTL_END; |
959 | |
960 | JIT_TO_EE_TRANSITION(); |
961 | |
962 | _ASSERTE(CheckContext(pResolvedToken->tokenScope, pResolvedToken->tokenContext)); |
963 | |
964 | pResolvedToken->pTypeSpec = NULL; |
965 | pResolvedToken->cbTypeSpec = NULL; |
966 | pResolvedToken->pMethodSpec = NULL; |
967 | pResolvedToken->cbMethodSpec = NULL; |
968 | |
969 | TypeHandle th; |
970 | MethodDesc * pMD = NULL; |
971 | FieldDesc * pFD = NULL; |
972 | |
973 | CorInfoTokenKind tokenType = pResolvedToken->tokenType; |
974 | |
975 | if (IsDynamicScope(pResolvedToken->tokenScope)) |
976 | { |
977 | GetDynamicResolver(pResolvedToken->tokenScope)->ResolveToken(pResolvedToken->token, &th, &pMD, &pFD); |
978 | |
979 | // |
980 | // Check that we got the expected handles and fill in missing data if necessary |
981 | // |
982 | |
983 | CorTokenType tkType = (CorTokenType)TypeFromToken(pResolvedToken->token); |
984 | |
985 | if (pMD != NULL) |
986 | { |
987 | if ((tkType != mdtMethodDef) && (tkType != mdtMemberRef)) |
988 | ThrowBadTokenException(pResolvedToken); |
989 | if ((tokenType & CORINFO_TOKENKIND_Method) == 0) |
990 | ThrowBadTokenException(pResolvedToken); |
991 | if (th.IsNull()) |
992 | th = pMD->GetMethodTable(); |
993 | |
994 | // "PermitUninstDefOrRef" check |
995 | if ((tokenType != CORINFO_TOKENKIND_Ldtoken) && pMD->ContainsGenericVariables()) |
996 | { |
997 | COMPlusThrow(kInvalidProgramException); |
998 | } |
999 | |
1000 | // if this is a BoxedEntryPointStub get the UnboxedEntryPoint one |
1001 | if (pMD->IsUnboxingStub()) |
1002 | { |
1003 | pMD = pMD->GetMethodTable()->GetUnboxedEntryPointMD(pMD); |
1004 | } |
1005 | |
1006 | // Activate target if required |
1007 | if (tokenType != CORINFO_TOKENKIND_Ldtoken) |
1008 | { |
1009 | ScanTokenForDynamicScope(pResolvedToken, th, pMD); |
1010 | } |
1011 | } |
1012 | else |
1013 | if (pFD != NULL) |
1014 | { |
1015 | if ((tkType != mdtFieldDef) && (tkType != mdtMemberRef)) |
1016 | ThrowBadTokenException(pResolvedToken); |
1017 | if ((tokenType & CORINFO_TOKENKIND_Field) == 0) |
1018 | ThrowBadTokenException(pResolvedToken); |
1019 | if (th.IsNull()) |
1020 | th = pFD->GetApproxEnclosingMethodTable(); |
1021 | |
1022 | if (pFD->IsStatic() && (tokenType != CORINFO_TOKENKIND_Ldtoken)) |
1023 | { |
1024 | ScanTokenForDynamicScope(pResolvedToken, th); |
1025 | } |
1026 | } |
1027 | else |
1028 | { |
1029 | if ((tkType != mdtTypeDef) && (tkType != mdtTypeRef)) |
1030 | ThrowBadTokenException(pResolvedToken); |
1031 | if ((tokenType & CORINFO_TOKENKIND_Class) == 0) |
1032 | ThrowBadTokenException(pResolvedToken); |
1033 | if (th.IsNull()) |
1034 | ThrowBadTokenException(pResolvedToken); |
1035 | |
1036 | if (tokenType == CORINFO_TOKENKIND_Box || tokenType == CORINFO_TOKENKIND_Constrained) |
1037 | { |
1038 | ScanTokenForDynamicScope(pResolvedToken, th); |
1039 | } |
1040 | } |
1041 | |
1042 | _ASSERTE((pMD == NULL) || (pFD == NULL)); |
1043 | _ASSERTE(!th.IsNull()); |
1044 | |
1045 | // "PermitUninstDefOrRef" check |
1046 | if ((tokenType != CORINFO_TOKENKIND_Ldtoken) && th.ContainsGenericVariables()) |
1047 | { |
1048 | COMPlusThrow(kInvalidProgramException); |
1049 | } |
1050 | |
1051 | // The JIT always wants to see normalized typedescs for arrays |
1052 | if (!th.IsTypeDesc() && th.AsMethodTable()->IsArray()) |
1053 | { |
1054 | MethodTable * pMT = th.AsMethodTable(); |
1055 | |
1056 | // Load the TypeDesc for the array type. |
1057 | DWORD rank = pMT->GetRank(); |
1058 | TypeHandle elemType = pMT->GetApproxArrayElementTypeHandle(); |
1059 | th = ClassLoader::LoadArrayTypeThrowing(elemType, pMT->GetInternalCorElementType(), rank); |
1060 | } |
1061 | } |
1062 | else |
1063 | { |
1064 | unsigned metaTOK = pResolvedToken->token; |
1065 | Module * pModule = (Module *)pResolvedToken->tokenScope; |
1066 | |
1067 | switch (TypeFromToken(metaTOK)) |
1068 | { |
1069 | case mdtModuleRef: |
1070 | if ((tokenType & CORINFO_TOKENKIND_Class) == 0) |
1071 | ThrowBadTokenException(pResolvedToken); |
1072 | |
1073 | { |
1074 | DomainFile *pTargetModule = pModule->LoadModule(GetAppDomain(), metaTOK, FALSE /* loadResources */); |
1075 | if (pTargetModule == NULL) |
1076 | COMPlusThrowHR(COR_E_BADIMAGEFORMAT); |
1077 | th = TypeHandle(pTargetModule->GetModule()->GetGlobalMethodTable()); |
1078 | if (th.IsNull()) |
1079 | COMPlusThrowHR(COR_E_BADIMAGEFORMAT); |
1080 | } |
1081 | break; |
1082 | |
1083 | case mdtTypeDef: |
1084 | case mdtTypeRef: |
1085 | if ((tokenType & CORINFO_TOKENKIND_Class) == 0) |
1086 | ThrowBadTokenException(pResolvedToken); |
1087 | |
1088 | th = ClassLoader::LoadTypeDefOrRefThrowing(pModule, metaTOK, |
1089 | ClassLoader::ThrowIfNotFound, |
1090 | (tokenType == CORINFO_TOKENKIND_Ldtoken) ? |
1091 | ClassLoader::PermitUninstDefOrRef : ClassLoader::FailIfUninstDefOrRef); |
1092 | break; |
1093 | |
1094 | case mdtTypeSpec: |
1095 | { |
1096 | if ((tokenType & CORINFO_TOKENKIND_Class) == 0) |
1097 | ThrowBadTokenException(pResolvedToken); |
1098 | |
1099 | IfFailThrow(pModule->GetMDImport()->GetTypeSpecFromToken(metaTOK, &pResolvedToken->pTypeSpec, &pResolvedToken->cbTypeSpec)); |
1100 | |
1101 | SigTypeContext typeContext; |
1102 | GetTypeContext(pResolvedToken->tokenContext, &typeContext); |
1103 | |
1104 | SigPointer sigptr(pResolvedToken->pTypeSpec, pResolvedToken->cbTypeSpec); |
1105 | th = sigptr.GetTypeHandleThrowing(pModule, &typeContext); |
1106 | } |
1107 | break; |
1108 | |
1109 | case mdtMethodDef: |
1110 | if ((tokenType & CORINFO_TOKENKIND_Method) == 0) |
1111 | ThrowBadTokenException(pResolvedToken); |
1112 | |
1113 | pMD = MemberLoader::GetMethodDescFromMethodDef(pModule, metaTOK, (tokenType != CORINFO_TOKENKIND_Ldtoken)); |
1114 | |
1115 | th = pMD->GetMethodTable(); |
1116 | break; |
1117 | |
1118 | case mdtFieldDef: |
1119 | if ((tokenType & CORINFO_TOKENKIND_Field) == 0) |
1120 | ThrowBadTokenException(pResolvedToken); |
1121 | |
1122 | pFD = MemberLoader::GetFieldDescFromFieldDef(pModule, metaTOK, (tokenType != CORINFO_TOKENKIND_Ldtoken)); |
1123 | |
1124 | th = pFD->GetEnclosingMethodTable(); |
1125 | break; |
1126 | |
1127 | case mdtMemberRef: |
1128 | { |
1129 | SigTypeContext typeContext; |
1130 | GetTypeContext(pResolvedToken->tokenContext, &typeContext); |
1131 | |
1132 | MemberLoader::GetDescFromMemberRef(pModule, metaTOK, &pMD, &pFD, &typeContext, (tokenType != CORINFO_TOKENKIND_Ldtoken), |
1133 | &th, TRUE, &pResolvedToken->pTypeSpec, &pResolvedToken->cbTypeSpec); |
1134 | |
1135 | _ASSERTE((pMD != NULL) ^ (pFD != NULL)); |
1136 | _ASSERTE(!th.IsNull()); |
1137 | |
1138 | if (pMD != NULL) |
1139 | { |
1140 | if ((tokenType & CORINFO_TOKENKIND_Method) == 0) |
1141 | ThrowBadTokenException(pResolvedToken); |
1142 | } |
1143 | else |
1144 | { |
1145 | if ((tokenType & CORINFO_TOKENKIND_Field) == 0) |
1146 | ThrowBadTokenException(pResolvedToken); |
1147 | } |
1148 | } |
1149 | break; |
1150 | |
1151 | case mdtMethodSpec: |
1152 | { |
1153 | if ((tokenType & CORINFO_TOKENKIND_Method) == 0) |
1154 | ThrowBadTokenException(pResolvedToken); |
1155 | |
1156 | SigTypeContext typeContext; |
1157 | GetTypeContext(pResolvedToken->tokenContext, &typeContext); |
1158 | |
1159 | // We need the method desc to carry exact instantiation, thus allowInstParam == FALSE. |
1160 | pMD = MemberLoader::GetMethodDescFromMethodSpec(pModule, metaTOK, &typeContext, (tokenType != CORINFO_TOKENKIND_Ldtoken), FALSE /* allowInstParam */, |
1161 | &th, TRUE, &pResolvedToken->pTypeSpec, &pResolvedToken->cbTypeSpec, &pResolvedToken->pMethodSpec, &pResolvedToken->cbMethodSpec); |
1162 | } |
1163 | break; |
1164 | |
1165 | default: |
1166 | ThrowBadTokenException(pResolvedToken); |
1167 | } |
1168 | |
1169 | // |
1170 | // Module dependency tracking |
1171 | // |
1172 | if (pMD != NULL) |
1173 | { |
1174 | ScanToken(pModule, pResolvedToken, th, pMD); |
1175 | } |
1176 | else |
1177 | if (pFD != NULL) |
1178 | { |
1179 | if (pFD->IsStatic()) |
1180 | ScanToken(pModule, pResolvedToken, th); |
1181 | } |
1182 | else |
1183 | { |
1184 | // It should not be required to trigger the modules cctors for ldtoken, it is done for backward compatibility only. |
1185 | if (tokenType == CORINFO_TOKENKIND_Box || tokenType == CORINFO_TOKENKIND_Constrained || tokenType == CORINFO_TOKENKIND_Ldtoken) |
1186 | ScanToken(pModule, pResolvedToken, th); |
1187 | } |
1188 | } |
1189 | |
1190 | // |
1191 | // tokenType specific verification and transformations |
1192 | // |
1193 | CorElementType et = th.GetInternalCorElementType(); |
1194 | switch (tokenType) |
1195 | { |
1196 | case CORINFO_TOKENKIND_Ldtoken: |
1197 | // Allow everything. |
1198 | break; |
1199 | |
1200 | case CORINFO_TOKENKIND_Newarr: |
1201 | // Disallow ELEMENT_TYPE_BYREF and ELEMENT_TYPE_VOID |
1202 | if (et == ELEMENT_TYPE_BYREF || et == ELEMENT_TYPE_VOID) |
1203 | COMPlusThrow(kInvalidProgramException); |
1204 | |
1205 | th = ClassLoader::LoadArrayTypeThrowing(th); |
1206 | break; |
1207 | |
1208 | default: |
1209 | // Disallow ELEMENT_TYPE_BYREF and ELEMENT_TYPE_VOID |
1210 | if (et == ELEMENT_TYPE_BYREF || et == ELEMENT_TYPE_VOID) |
1211 | COMPlusThrow(kInvalidProgramException); |
1212 | break; |
1213 | } |
1214 | |
1215 | // The JIT interface should always return fully loaded types |
1216 | _ASSERTE(th.IsFullyLoaded()); |
1217 | |
1218 | pResolvedToken->hClass = CORINFO_CLASS_HANDLE(th.AsPtr()); |
1219 | pResolvedToken->hMethod = CORINFO_METHOD_HANDLE(pMD); |
1220 | pResolvedToken->hField = CORINFO_FIELD_HANDLE(pFD); |
1221 | |
1222 | EE_TO_JIT_TRANSITION(); |
1223 | } |
1224 | |
1225 | /*********************************************************************/ |
1226 | struct TryResolveTokenFilterParam |
1227 | { |
1228 | CEEInfo* m_this; |
1229 | CORINFO_RESOLVED_TOKEN* m_resolvedToken; |
1230 | EXCEPTION_POINTERS m_exceptionPointers; |
1231 | bool m_success; |
1232 | }; |
1233 | |
1234 | bool isValidTokenForTryResolveToken(CEEInfo* info, CORINFO_RESOLVED_TOKEN* resolvedToken) |
1235 | { |
1236 | CONTRACTL { |
1237 | NOTHROW; |
1238 | GC_NOTRIGGER; |
1239 | SO_TOLERANT; |
1240 | MODE_ANY; |
1241 | } CONTRACTL_END; |
1242 | |
1243 | if (!info->isValidToken(resolvedToken->tokenScope, resolvedToken->token)) |
1244 | { |
1245 | return false; |
1246 | } |
1247 | |
1248 | CorInfoTokenKind tokenType = resolvedToken->tokenType; |
1249 | switch (TypeFromToken(resolvedToken->token)) |
1250 | { |
1251 | case mdtModuleRef: |
1252 | case mdtTypeDef: |
1253 | case mdtTypeRef: |
1254 | case mdtTypeSpec: |
1255 | if ((tokenType & CORINFO_TOKENKIND_Class) == 0) |
1256 | return false; |
1257 | break; |
1258 | |
1259 | case mdtMethodDef: |
1260 | case mdtMethodSpec: |
1261 | if ((tokenType & CORINFO_TOKENKIND_Method) == 0) |
1262 | return false; |
1263 | break; |
1264 | |
1265 | case mdtFieldDef: |
1266 | if ((tokenType & CORINFO_TOKENKIND_Field) == 0) |
1267 | return false; |
1268 | break; |
1269 | |
1270 | case mdtMemberRef: |
1271 | if ((tokenType & (CORINFO_TOKENKIND_Method | CORINFO_TOKENKIND_Field)) == 0) |
1272 | return false; |
1273 | break; |
1274 | |
1275 | default: |
1276 | return false; |
1277 | } |
1278 | |
1279 | return true; |
1280 | } |
1281 | |
1282 | LONG EEFilterException(struct _EXCEPTION_POINTERS* exceptionPointers, void* unused); |
1283 | |
1284 | LONG TryResolveTokenFilter(struct _EXCEPTION_POINTERS* exceptionPointers, void* theParam) |
1285 | { |
1286 | CONTRACTL { |
1287 | NOTHROW; |
1288 | GC_NOTRIGGER; |
1289 | SO_TOLERANT; |
1290 | MODE_ANY; |
1291 | } CONTRACTL_END; |
1292 | |
1293 | // Backward compatibility: Convert bad image format exceptions thrown while resolving tokens |
1294 | // to simple true/false successes. This is done for backward compatibility only. Ideally, |
1295 | // we would always treat bad tokens in the IL stream as fatal errors. |
1296 | if (exceptionPointers->ExceptionRecord->ExceptionCode == EXCEPTION_COMPLUS) |
1297 | { |
1298 | auto* param = reinterpret_cast<TryResolveTokenFilterParam*>(theParam); |
1299 | if (!isValidTokenForTryResolveToken(param->m_this, param->m_resolvedToken)) |
1300 | { |
1301 | param->m_exceptionPointers = *exceptionPointers; |
1302 | return EEFilterException(exceptionPointers, nullptr); |
1303 | } |
1304 | } |
1305 | |
1306 | return EXCEPTION_CONTINUE_SEARCH; |
1307 | } |
1308 | |
1309 | bool CEEInfo::tryResolveToken(CORINFO_RESOLVED_TOKEN* resolvedToken) |
1310 | { |
1311 | // No dynamic contract here because SEH is used |
1312 | STATIC_CONTRACT_SO_TOLERANT; |
1313 | STATIC_CONTRACT_THROWS; |
1314 | STATIC_CONTRACT_GC_TRIGGERS; |
1315 | STATIC_CONTRACT_MODE_PREEMPTIVE; |
1316 | |
1317 | TryResolveTokenFilterParam param; |
1318 | param.m_this = this; |
1319 | param.m_resolvedToken = resolvedToken; |
1320 | param.m_success = true; |
1321 | |
1322 | PAL_TRY(TryResolveTokenFilterParam*, pParam, ¶m) |
1323 | { |
1324 | pParam->m_this->resolveToken(pParam->m_resolvedToken); |
1325 | } |
1326 | PAL_EXCEPT_FILTER(TryResolveTokenFilter) |
1327 | { |
1328 | if (param.m_exceptionPointers.ExceptionRecord->ExceptionCode == EXCEPTION_COMPLUS) |
1329 | { |
1330 | HandleException(¶m.m_exceptionPointers); |
1331 | } |
1332 | |
1333 | param.m_success = false; |
1334 | } |
1335 | PAL_ENDTRY |
1336 | |
1337 | return param.m_success; |
1338 | } |
1339 | |
1340 | /*********************************************************************/ |
1341 | // We have a few frequently used constants in mscorlib that are defined as |
1342 | // readonly static fields for historic reasons. Check for them here and |
1343 | // allow them to be treated as actual constants by the JIT. |
1344 | static CORINFO_FIELD_ACCESSOR getFieldIntrinsic(FieldDesc * field) |
1345 | { |
1346 | STANDARD_VM_CONTRACT; |
1347 | |
1348 | if (MscorlibBinder::GetField(FIELD__STRING__EMPTY) == field) |
1349 | { |
1350 | return CORINFO_FIELD_INTRINSIC_EMPTY_STRING; |
1351 | } |
1352 | else |
1353 | if ((MscorlibBinder::GetField(FIELD__INTPTR__ZERO) == field) || |
1354 | (MscorlibBinder::GetField(FIELD__UINTPTR__ZERO) == field)) |
1355 | { |
1356 | return CORINFO_FIELD_INTRINSIC_ZERO; |
1357 | } |
1358 | else |
1359 | if (MscorlibBinder::GetField(FIELD__BITCONVERTER__ISLITTLEENDIAN) == field) |
1360 | { |
1361 | return CORINFO_FIELD_INTRINSIC_ISLITTLEENDIAN; |
1362 | } |
1363 | |
1364 | return (CORINFO_FIELD_ACCESSOR)-1; |
1365 | } |
1366 | |
1367 | static CorInfoHelpFunc getGenericStaticsHelper(FieldDesc * pField) |
1368 | { |
1369 | STANDARD_VM_CONTRACT; |
1370 | |
1371 | int helper = CORINFO_HELP_GETGENERICS_NONGCSTATIC_BASE; |
1372 | |
1373 | if (pField->GetFieldType() == ELEMENT_TYPE_CLASS || |
1374 | pField->GetFieldType() == ELEMENT_TYPE_VALUETYPE) |
1375 | { |
1376 | helper = CORINFO_HELP_GETGENERICS_GCSTATIC_BASE; |
1377 | } |
1378 | |
1379 | if (pField->IsThreadStatic()) |
1380 | { |
1381 | const int delta = CORINFO_HELP_GETGENERICS_GCTHREADSTATIC_BASE - CORINFO_HELP_GETGENERICS_GCSTATIC_BASE; |
1382 | |
1383 | static_assert_no_msg(CORINFO_HELP_GETGENERICS_NONGCTHREADSTATIC_BASE |
1384 | == CORINFO_HELP_GETGENERICS_NONGCSTATIC_BASE + delta); |
1385 | |
1386 | helper += (CORINFO_HELP_GETGENERICS_GCTHREADSTATIC_BASE - CORINFO_HELP_GETGENERICS_GCSTATIC_BASE); |
1387 | } |
1388 | |
1389 | return (CorInfoHelpFunc)helper; |
1390 | } |
1391 | |
1392 | CorInfoHelpFunc CEEInfo::getSharedStaticsHelper(FieldDesc * pField, MethodTable * pFieldMT) |
1393 | { |
1394 | STANDARD_VM_CONTRACT; |
1395 | |
1396 | int helper = CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE; |
1397 | |
1398 | if (pField->GetFieldType() == ELEMENT_TYPE_CLASS || |
1399 | pField->GetFieldType() == ELEMENT_TYPE_VALUETYPE) |
1400 | { |
1401 | helper = CORINFO_HELP_GETSHARED_GCSTATIC_BASE; |
1402 | } |
1403 | |
1404 | if (pFieldMT->IsDynamicStatics()) |
1405 | { |
1406 | const int delta = CORINFO_HELP_GETSHARED_GCSTATIC_BASE_DYNAMICCLASS - CORINFO_HELP_GETSHARED_GCSTATIC_BASE; |
1407 | |
1408 | static_assert_no_msg(CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE_DYNAMICCLASS |
1409 | == CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE + delta); |
1410 | |
1411 | helper += delta; |
1412 | } |
1413 | else |
1414 | if (!pFieldMT->HasClassConstructor() && !pFieldMT->HasBoxedRegularStatics()) |
1415 | { |
1416 | const int delta = CORINFO_HELP_GETSHARED_GCSTATIC_BASE_NOCTOR - CORINFO_HELP_GETSHARED_GCSTATIC_BASE; |
1417 | |
1418 | static_assert_no_msg(CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE_NOCTOR |
1419 | == CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE + delta); |
1420 | |
1421 | helper += delta; |
1422 | } |
1423 | |
1424 | if (pField->IsThreadStatic()) |
1425 | { |
1426 | const int delta = CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE - CORINFO_HELP_GETSHARED_GCSTATIC_BASE; |
1427 | |
1428 | static_assert_no_msg(CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE |
1429 | == CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE + delta); |
1430 | static_assert_no_msg(CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE_NOCTOR |
1431 | == CORINFO_HELP_GETSHARED_GCSTATIC_BASE_NOCTOR + delta); |
1432 | static_assert_no_msg(CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE_NOCTOR |
1433 | == CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE_NOCTOR + delta); |
1434 | static_assert_no_msg(CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE_DYNAMICCLASS |
1435 | == CORINFO_HELP_GETSHARED_GCSTATIC_BASE_DYNAMICCLASS + delta); |
1436 | static_assert_no_msg(CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE_DYNAMICCLASS |
1437 | == CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE_DYNAMICCLASS + delta); |
1438 | |
1439 | helper += delta; |
1440 | } |
1441 | |
1442 | return (CorInfoHelpFunc)helper; |
1443 | } |
1444 | |
1445 | static CorInfoHelpFunc getInstanceFieldHelper(FieldDesc * pField, CORINFO_ACCESS_FLAGS flags) |
1446 | { |
1447 | STANDARD_VM_CONTRACT; |
1448 | |
1449 | int helper; |
1450 | |
1451 | CorElementType type = pField->GetFieldType(); |
1452 | |
1453 | if (CorTypeInfo::IsObjRef(type)) |
1454 | helper = CORINFO_HELP_GETFIELDOBJ; |
1455 | else |
1456 | switch (type) |
1457 | { |
1458 | case ELEMENT_TYPE_VALUETYPE: |
1459 | helper = CORINFO_HELP_GETFIELDSTRUCT; |
1460 | break; |
1461 | case ELEMENT_TYPE_I1: |
1462 | case ELEMENT_TYPE_BOOLEAN: |
1463 | case ELEMENT_TYPE_U1: |
1464 | helper = CORINFO_HELP_GETFIELD8; |
1465 | break; |
1466 | case ELEMENT_TYPE_I2: |
1467 | case ELEMENT_TYPE_CHAR: |
1468 | case ELEMENT_TYPE_U2: |
1469 | helper = CORINFO_HELP_GETFIELD16; |
1470 | break; |
1471 | case ELEMENT_TYPE_I4: |
1472 | case ELEMENT_TYPE_U4: |
1473 | IN_TARGET_32BIT(default:) |
1474 | helper = CORINFO_HELP_GETFIELD32; |
1475 | break; |
1476 | case ELEMENT_TYPE_I8: |
1477 | case ELEMENT_TYPE_U8: |
1478 | IN_TARGET_64BIT(default:) |
1479 | helper = CORINFO_HELP_GETFIELD64; |
1480 | break; |
1481 | case ELEMENT_TYPE_R4: |
1482 | helper = CORINFO_HELP_GETFIELDFLOAT; |
1483 | break; |
1484 | case ELEMENT_TYPE_R8: |
1485 | helper = CORINFO_HELP_GETFIELDDOUBLE; |
1486 | break; |
1487 | } |
1488 | |
1489 | if (flags & CORINFO_ACCESS_SET) |
1490 | { |
1491 | const int delta = CORINFO_HELP_SETFIELDOBJ - CORINFO_HELP_GETFIELDOBJ; |
1492 | |
1493 | static_assert_no_msg(CORINFO_HELP_SETFIELD8 == CORINFO_HELP_GETFIELD8 + delta); |
1494 | static_assert_no_msg(CORINFO_HELP_SETFIELD16 == CORINFO_HELP_GETFIELD16 + delta); |
1495 | static_assert_no_msg(CORINFO_HELP_SETFIELD32 == CORINFO_HELP_GETFIELD32 + delta); |
1496 | static_assert_no_msg(CORINFO_HELP_SETFIELD64 == CORINFO_HELP_GETFIELD64 + delta); |
1497 | static_assert_no_msg(CORINFO_HELP_SETFIELDSTRUCT == CORINFO_HELP_GETFIELDSTRUCT + delta); |
1498 | static_assert_no_msg(CORINFO_HELP_SETFIELDFLOAT == CORINFO_HELP_GETFIELDFLOAT + delta); |
1499 | static_assert_no_msg(CORINFO_HELP_SETFIELDDOUBLE == CORINFO_HELP_GETFIELDDOUBLE + delta); |
1500 | |
1501 | helper += delta; |
1502 | } |
1503 | |
1504 | return (CorInfoHelpFunc)helper; |
1505 | } |
1506 | |
1507 | /*********************************************************************/ |
1508 | void CEEInfo::getFieldInfo (CORINFO_RESOLVED_TOKEN * pResolvedToken, |
1509 | CORINFO_METHOD_HANDLE callerHandle, |
1510 | CORINFO_ACCESS_FLAGS flags, |
1511 | CORINFO_FIELD_INFO *pResult |
1512 | ) |
1513 | { |
1514 | CONTRACTL { |
1515 | SO_TOLERANT; |
1516 | THROWS; |
1517 | GC_TRIGGERS; |
1518 | MODE_PREEMPTIVE; |
1519 | } CONTRACTL_END; |
1520 | |
1521 | JIT_TO_EE_TRANSITION(); |
1522 | |
1523 | _ASSERTE((flags & (CORINFO_ACCESS_GET | CORINFO_ACCESS_SET | CORINFO_ACCESS_ADDRESS | CORINFO_ACCESS_INIT_ARRAY)) != 0); |
1524 | |
1525 | INDEBUG(memset(pResult, 0xCC, sizeof(*pResult))); |
1526 | |
1527 | FieldDesc * pField = (FieldDesc*)pResolvedToken->hField; |
1528 | MethodTable * pFieldMT = pField->GetApproxEnclosingMethodTable(); |
1529 | |
1530 | // Helper to use if the field access requires it |
1531 | CORINFO_FIELD_ACCESSOR fieldAccessor = (CORINFO_FIELD_ACCESSOR)-1; |
1532 | DWORD fieldFlags = 0; |
1533 | |
1534 | pResult->offset = pField->GetOffset(); |
1535 | if (pField->IsStatic()) |
1536 | { |
1537 | fieldFlags |= CORINFO_FLG_FIELD_STATIC; |
1538 | |
1539 | if (pField->IsRVA()) |
1540 | { |
1541 | fieldFlags |= CORINFO_FLG_FIELD_UNMANAGED; |
1542 | |
1543 | Module* module = pFieldMT->GetModule(); |
1544 | if (module->IsRvaFieldTls(pResult->offset)) |
1545 | { |
1546 | fieldAccessor = CORINFO_FIELD_STATIC_TLS; |
1547 | |
1548 | // Provide helper to use if the JIT is not able to emit the TLS access |
1549 | // as intrinsic |
1550 | pResult->helper = CORINFO_HELP_GETSTATICFIELDADDR_TLS; |
1551 | |
1552 | pResult->offset = module->GetFieldTlsOffset(pResult->offset); |
1553 | } |
1554 | else |
1555 | { |
1556 | fieldAccessor = CORINFO_FIELD_STATIC_RVA_ADDRESS; |
1557 | } |
1558 | |
1559 | // We are not going through a helper. The constructor has to be triggered explicitly. |
1560 | if (!pFieldMT->IsClassPreInited()) |
1561 | fieldFlags |= CORINFO_FLG_FIELD_INITCLASS; |
1562 | } |
1563 | else |
1564 | { |
1565 | // Regular or thread static |
1566 | CORINFO_FIELD_ACCESSOR intrinsicAccessor; |
1567 | |
1568 | if (pField->GetFieldType() == ELEMENT_TYPE_VALUETYPE) |
1569 | fieldFlags |= CORINFO_FLG_FIELD_STATIC_IN_HEAP; |
1570 | |
1571 | if (pFieldMT->IsSharedByGenericInstantiations()) |
1572 | { |
1573 | fieldAccessor = CORINFO_FIELD_STATIC_GENERICS_STATIC_HELPER; |
1574 | |
1575 | pResult->helper = getGenericStaticsHelper(pField); |
1576 | } |
1577 | else |
1578 | if (pFieldMT->GetModule()->IsSystem() && (flags & CORINFO_ACCESS_GET) && |
1579 | (intrinsicAccessor = getFieldIntrinsic(pField)) != (CORINFO_FIELD_ACCESSOR)-1) |
1580 | { |
1581 | // Intrinsics |
1582 | fieldAccessor = intrinsicAccessor; |
1583 | } |
1584 | else |
1585 | if (m_pMethodBeingCompiled->IsZapped() || IsCompilingForNGen() || |
1586 | // Static fields are not pinned in collectible types. We will always access |
1587 | // them using a helper since the address cannot be embeded into the code. |
1588 | pFieldMT->Collectible() || |
1589 | // We always treat accessing thread statics as if we are in domain neutral code. |
1590 | pField->IsThreadStatic() |
1591 | ) |
1592 | { |
1593 | fieldAccessor = CORINFO_FIELD_STATIC_SHARED_STATIC_HELPER; |
1594 | |
1595 | pResult->helper = getSharedStaticsHelper(pField, pFieldMT); |
1596 | } |
1597 | else |
1598 | { |
1599 | fieldAccessor = CORINFO_FIELD_STATIC_ADDRESS; |
1600 | |
1601 | // We are not going through a helper. The constructor has to be triggered explicitly. |
1602 | if (!pFieldMT->IsClassPreInited()) |
1603 | fieldFlags |= CORINFO_FLG_FIELD_INITCLASS; |
1604 | } |
1605 | } |
1606 | |
1607 | // |
1608 | // Currently, we only this optimization for regular statics, but it |
1609 | // looks like it may be permissible to do this optimization for |
1610 | // thread statics as well. |
1611 | // |
1612 | if ((flags & CORINFO_ACCESS_ADDRESS) && |
1613 | !pField->IsThreadStatic() && |
1614 | (fieldAccessor != CORINFO_FIELD_STATIC_TLS)) |
1615 | { |
1616 | fieldFlags |= CORINFO_FLG_FIELD_SAFESTATIC_BYREF_RETURN; |
1617 | } |
1618 | } |
1619 | else |
1620 | { |
1621 | BOOL fInstanceHelper = FALSE; |
1622 | |
1623 | if (fInstanceHelper) |
1624 | { |
1625 | if (flags & CORINFO_ACCESS_ADDRESS) |
1626 | { |
1627 | fieldAccessor = CORINFO_FIELD_INSTANCE_ADDR_HELPER; |
1628 | |
1629 | pResult->helper = CORINFO_HELP_GETFIELDADDR; |
1630 | } |
1631 | else |
1632 | { |
1633 | fieldAccessor = CORINFO_FIELD_INSTANCE_HELPER; |
1634 | |
1635 | pResult->helper = getInstanceFieldHelper(pField, flags); |
1636 | } |
1637 | } |
1638 | else |
1639 | if (pField->IsEnCNew()) |
1640 | { |
1641 | fieldAccessor = CORINFO_FIELD_INSTANCE_ADDR_HELPER; |
1642 | |
1643 | pResult->helper = CORINFO_HELP_GETFIELDADDR; |
1644 | } |
1645 | else |
1646 | { |
1647 | fieldAccessor = CORINFO_FIELD_INSTANCE; |
1648 | } |
1649 | |
1650 | // FieldDesc::GetOffset() does not include the size of Object |
1651 | if (!pFieldMT->IsValueType()) |
1652 | { |
1653 | pResult->offset += OBJECT_SIZE; |
1654 | } |
1655 | } |
1656 | |
1657 | // TODO: This is touching metadata. Can we avoid it? |
1658 | DWORD fieldAttribs = pField->GetAttributes(); |
1659 | |
1660 | if (IsFdFamily(fieldAttribs)) |
1661 | fieldFlags |= CORINFO_FLG_FIELD_PROTECTED; |
1662 | |
1663 | if (IsFdInitOnly(fieldAttribs)) |
1664 | fieldFlags |= CORINFO_FLG_FIELD_FINAL; |
1665 | |
1666 | pResult->fieldAccessor = fieldAccessor; |
1667 | pResult->fieldFlags = fieldFlags; |
1668 | |
1669 | if (!(flags & CORINFO_ACCESS_INLINECHECK)) |
1670 | { |
1671 | //get the field's type. Grab the class for structs. |
1672 | pResult->fieldType = getFieldTypeInternal(pResolvedToken->hField, &pResult->structType, pResolvedToken->hClass); |
1673 | |
1674 | |
1675 | MethodDesc * pCallerForSecurity = GetMethodForSecurity(callerHandle); |
1676 | |
1677 | // |
1678 | //Since we can't get the special verify-only instantiated FD like we can with MDs, go back to the parent |
1679 | //of the memberRef and load that one. That should give us the open instantiation. |
1680 | // |
1681 | //If the field we found is owned by a generic type, you have to go back to the signature and reload. |
1682 | //Otherwise we filled in !0. |
1683 | TypeHandle fieldTypeForSecurity = TypeHandle(pResolvedToken->hClass); |
1684 | if (pResolvedToken->pTypeSpec != NULL) |
1685 | { |
1686 | SigTypeContext typeContext; |
1687 | SigTypeContext::InitTypeContext(pCallerForSecurity, &typeContext); |
1688 | |
1689 | SigPointer sigptr(pResolvedToken->pTypeSpec, pResolvedToken->cbTypeSpec); |
1690 | fieldTypeForSecurity = sigptr.GetTypeHandleThrowing((Module *)pResolvedToken->tokenScope, &typeContext); |
1691 | |
1692 | // typeHnd can be a variable type |
1693 | if (fieldTypeForSecurity.GetMethodTable() == NULL) |
1694 | { |
1695 | COMPlusThrowHR(COR_E_BADIMAGEFORMAT, BFA_METHODDEF_PARENT_NO_MEMBERS); |
1696 | } |
1697 | } |
1698 | |
1699 | BOOL doAccessCheck = TRUE; |
1700 | AccessCheckOptions::AccessCheckType accessCheckType = AccessCheckOptions::kNormalAccessibilityChecks; |
1701 | |
1702 | DynamicResolver * pAccessContext = NULL; |
1703 | |
1704 | //More in code:CEEInfo::getCallInfo, but the short version is that the caller and callee Descs do |
1705 | //not completely describe the type. |
1706 | TypeHandle callerTypeForSecurity = TypeHandle(pCallerForSecurity->GetMethodTable()); |
1707 | if (IsDynamicScope(pResolvedToken->tokenScope)) |
1708 | { |
1709 | doAccessCheck = ModifyCheckForDynamicMethod(GetDynamicResolver(pResolvedToken->tokenScope), &callerTypeForSecurity, |
1710 | &accessCheckType, &pAccessContext); |
1711 | } |
1712 | |
1713 | //Now for some link time checks. |
1714 | //Um... where are the field link demands? |
1715 | |
1716 | pResult->accessAllowed = CORINFO_ACCESS_ALLOWED; |
1717 | |
1718 | if (doAccessCheck) |
1719 | { |
1720 | //Well, let's check some visibility at least. |
1721 | AccessCheckOptions accessCheckOptions(accessCheckType, |
1722 | pAccessContext, |
1723 | FALSE, |
1724 | pField); |
1725 | |
1726 | _ASSERTE(pCallerForSecurity != NULL && callerTypeForSecurity != NULL); |
1727 | StaticAccessCheckContext accessContext(pCallerForSecurity, callerTypeForSecurity.GetMethodTable()); |
1728 | |
1729 | BOOL canAccess = ClassLoader::CanAccess( |
1730 | &accessContext, |
1731 | fieldTypeForSecurity.GetMethodTable(), |
1732 | fieldTypeForSecurity.GetAssembly(), |
1733 | fieldAttribs, |
1734 | NULL, |
1735 | (flags & CORINFO_ACCESS_INIT_ARRAY) ? NULL : pField, // For InitializeArray, we don't need tocheck the type of the field. |
1736 | accessCheckOptions); |
1737 | |
1738 | if (!canAccess) |
1739 | { |
1740 | //Set up the throw helper |
1741 | pResult->accessAllowed = CORINFO_ACCESS_ILLEGAL; |
1742 | |
1743 | pResult->accessCalloutHelper.helperNum = CORINFO_HELP_FIELD_ACCESS_EXCEPTION; |
1744 | pResult->accessCalloutHelper.numArgs = 2; |
1745 | |
1746 | pResult->accessCalloutHelper.args[0].Set(CORINFO_METHOD_HANDLE(pCallerForSecurity)); |
1747 | pResult->accessCalloutHelper.args[1].Set(CORINFO_FIELD_HANDLE(pField)); |
1748 | |
1749 | if (IsCompilingForNGen()) |
1750 | { |
1751 | //see code:CEEInfo::getCallInfo for more information. |
1752 | if (pCallerForSecurity->ContainsGenericVariables()) |
1753 | COMPlusThrowNonLocalized(kNotSupportedException, W("Cannot embed generic MethodDesc" )); |
1754 | } |
1755 | } |
1756 | } |
1757 | } |
1758 | |
1759 | EE_TO_JIT_TRANSITION(); |
1760 | } |
1761 | |
1762 | //--------------------------------------------------------------------------------------- |
1763 | // |
1764 | bool CEEInfo::isFieldStatic(CORINFO_FIELD_HANDLE fldHnd) |
1765 | { |
1766 | CONTRACTL { |
1767 | SO_TOLERANT; |
1768 | THROWS; |
1769 | GC_TRIGGERS; |
1770 | MODE_PREEMPTIVE; |
1771 | } CONTRACTL_END; |
1772 | |
1773 | bool res = false; |
1774 | JIT_TO_EE_TRANSITION_LEAF(); |
1775 | FieldDesc* field = (FieldDesc*)fldHnd; |
1776 | res = (field->IsStatic() != 0); |
1777 | EE_TO_JIT_TRANSITION_LEAF(); |
1778 | return res; |
1779 | } |
1780 | |
1781 | //--------------------------------------------------------------------------------------- |
1782 | // |
1783 | void |
1784 | CEEInfo::findCallSiteSig( |
1785 | CORINFO_MODULE_HANDLE scopeHnd, |
1786 | unsigned sigMethTok, |
1787 | CORINFO_CONTEXT_HANDLE context, |
1788 | CORINFO_SIG_INFO * sigRet) |
1789 | { |
1790 | CONTRACTL { |
1791 | SO_TOLERANT; |
1792 | THROWS; |
1793 | GC_TRIGGERS; |
1794 | MODE_PREEMPTIVE; |
1795 | } CONTRACTL_END; |
1796 | |
1797 | JIT_TO_EE_TRANSITION(); |
1798 | |
1799 | PCCOR_SIGNATURE pSig = NULL; |
1800 | DWORD cbSig = 0; |
1801 | |
1802 | if (IsDynamicScope(scopeHnd)) |
1803 | { |
1804 | DynamicResolver * pResolver = GetDynamicResolver(scopeHnd); |
1805 | SigPointer sig; |
1806 | |
1807 | if (TypeFromToken(sigMethTok) == mdtMemberRef) |
1808 | { |
1809 | sig = pResolver->ResolveSignatureForVarArg(sigMethTok); |
1810 | } |
1811 | else |
1812 | { |
1813 | _ASSERTE(TypeFromToken(sigMethTok) == mdtMethodDef); |
1814 | |
1815 | TypeHandle classHandle; |
1816 | MethodDesc * pMD = NULL; |
1817 | FieldDesc * pFD = NULL; |
1818 | |
1819 | // in this case a method is asked for its sig. Resolve the method token and get the sig |
1820 | pResolver->ResolveToken(sigMethTok, &classHandle, &pMD, &pFD); |
1821 | if (pMD == NULL) |
1822 | COMPlusThrow(kInvalidProgramException); |
1823 | |
1824 | PCCOR_SIGNATURE pSig = NULL; |
1825 | DWORD cbSig; |
1826 | pMD->GetSig(&pSig, &cbSig); |
1827 | sig = SigPointer(pSig, cbSig); |
1828 | |
1829 | context = MAKE_METHODCONTEXT(pMD); |
1830 | scopeHnd = GetScopeHandle(pMD->GetModule()); |
1831 | } |
1832 | |
1833 | sig.GetSignature(&pSig, &cbSig); |
1834 | sigMethTok = mdTokenNil; |
1835 | } |
1836 | else |
1837 | { |
1838 | Module * module = (Module *)scopeHnd; |
1839 | LPCUTF8 szName; |
1840 | |
1841 | if (TypeFromToken(sigMethTok) == mdtMemberRef) |
1842 | { |
1843 | IfFailThrow(module->GetMDImport()->GetNameAndSigOfMemberRef(sigMethTok, &pSig, &cbSig, &szName)); |
1844 | } |
1845 | else if (TypeFromToken(sigMethTok) == mdtMethodDef) |
1846 | { |
1847 | IfFailThrow(module->GetMDImport()->GetSigOfMethodDef(sigMethTok, &cbSig, &pSig)); |
1848 | } |
1849 | } |
1850 | |
1851 | CEEInfo::ConvToJitSig( |
1852 | pSig, |
1853 | cbSig, |
1854 | scopeHnd, |
1855 | sigMethTok, |
1856 | sigRet, |
1857 | GetMethodFromContext(context), |
1858 | false, |
1859 | GetTypeFromContext(context)); |
1860 | EE_TO_JIT_TRANSITION(); |
1861 | } // CEEInfo::findCallSiteSig |
1862 | |
1863 | //--------------------------------------------------------------------------------------- |
1864 | // |
1865 | void |
1866 | CEEInfo::findSig( |
1867 | CORINFO_MODULE_HANDLE scopeHnd, |
1868 | unsigned sigTok, |
1869 | CORINFO_CONTEXT_HANDLE context, |
1870 | CORINFO_SIG_INFO * sigRet) |
1871 | { |
1872 | CONTRACTL { |
1873 | SO_TOLERANT; |
1874 | THROWS; |
1875 | GC_TRIGGERS; |
1876 | MODE_PREEMPTIVE; |
1877 | } CONTRACTL_END; |
1878 | |
1879 | JIT_TO_EE_TRANSITION(); |
1880 | |
1881 | PCCOR_SIGNATURE pSig = NULL; |
1882 | DWORD cbSig = 0; |
1883 | |
1884 | if (IsDynamicScope(scopeHnd)) |
1885 | { |
1886 | SigPointer sig = GetDynamicResolver(scopeHnd)->ResolveSignature(sigTok); |
1887 | sig.GetSignature(&pSig, &cbSig); |
1888 | sigTok = mdTokenNil; |
1889 | } |
1890 | else |
1891 | { |
1892 | Module * module = (Module *)scopeHnd; |
1893 | |
1894 | // We need to resolve this stand alone sig |
1895 | IfFailThrow(module->GetMDImport()->GetSigFromToken( |
1896 | (mdSignature)sigTok, |
1897 | &cbSig, |
1898 | &pSig)); |
1899 | } |
1900 | |
1901 | CEEInfo::ConvToJitSig( |
1902 | pSig, |
1903 | cbSig, |
1904 | scopeHnd, |
1905 | sigTok, |
1906 | sigRet, |
1907 | GetMethodFromContext(context), |
1908 | false, |
1909 | GetTypeFromContext(context)); |
1910 | |
1911 | EE_TO_JIT_TRANSITION(); |
1912 | } // CEEInfo::findSig |
1913 | |
1914 | //--------------------------------------------------------------------------------------- |
1915 | // |
1916 | unsigned |
1917 | CEEInfo::getClassSize( |
1918 | CORINFO_CLASS_HANDLE clsHnd) |
1919 | { |
1920 | CONTRACTL { |
1921 | SO_TOLERANT; |
1922 | NOTHROW; |
1923 | GC_NOTRIGGER; |
1924 | MODE_PREEMPTIVE; |
1925 | } CONTRACTL_END; |
1926 | |
1927 | unsigned result = 0; |
1928 | |
1929 | JIT_TO_EE_TRANSITION_LEAF(); |
1930 | |
1931 | TypeHandle VMClsHnd(clsHnd); |
1932 | result = VMClsHnd.GetSize(); |
1933 | |
1934 | EE_TO_JIT_TRANSITION_LEAF(); |
1935 | |
1936 | return result; |
1937 | } |
1938 | |
1939 | //--------------------------------------------------------------------------------------- |
1940 | // |
1941 | // Get the size of a reference type as allocated on the heap. This includes the size of the fields |
1942 | // (and any padding between the fields) and the size of a method table pointer but doesn't include |
1943 | // object header size or any padding for minimum size. |
1944 | unsigned |
1945 | CEEInfo::getHeapClassSize( |
1946 | CORINFO_CLASS_HANDLE clsHnd) |
1947 | { |
1948 | CONTRACTL{ |
1949 | SO_TOLERANT; |
1950 | NOTHROW; |
1951 | GC_NOTRIGGER; |
1952 | MODE_PREEMPTIVE; |
1953 | } CONTRACTL_END; |
1954 | |
1955 | unsigned result = 0; |
1956 | |
1957 | JIT_TO_EE_TRANSITION_LEAF(); |
1958 | |
1959 | TypeHandle VMClsHnd(clsHnd); |
1960 | MethodTable* pMT = VMClsHnd.GetMethodTable(); |
1961 | _ASSERTE(pMT); |
1962 | _ASSERTE(!pMT->IsValueType()); |
1963 | _ASSERTE(!pMT->HasComponentSize()); |
1964 | #ifdef FEATURE_READYTORUN_COMPILER |
1965 | _ASSERTE(!IsReadyToRunCompilation() || pMT->IsInheritanceChainLayoutFixedInCurrentVersionBubble()); |
1966 | #endif |
1967 | |
1968 | // Add OBJECT_SIZE to account for method table pointer. |
1969 | result = pMT->GetNumInstanceFieldBytes() + OBJECT_SIZE; |
1970 | |
1971 | EE_TO_JIT_TRANSITION_LEAF(); |
1972 | return result; |
1973 | } |
1974 | |
1975 | //--------------------------------------------------------------------------------------- |
1976 | // |
1977 | // Return TRUE if an object of this type can be allocated on the stack. |
1978 | BOOL CEEInfo::canAllocateOnStack(CORINFO_CLASS_HANDLE clsHnd) |
1979 | { |
1980 | CONTRACTL{ |
1981 | SO_TOLERANT; |
1982 | NOTHROW; |
1983 | GC_NOTRIGGER; |
1984 | MODE_PREEMPTIVE; |
1985 | } CONTRACTL_END; |
1986 | |
1987 | BOOL result = FALSE; |
1988 | |
1989 | JIT_TO_EE_TRANSITION_LEAF(); |
1990 | |
1991 | TypeHandle VMClsHnd(clsHnd); |
1992 | MethodTable* pMT = VMClsHnd.GetMethodTable(); |
1993 | _ASSERTE(pMT); |
1994 | _ASSERTE(!pMT->IsValueType()); |
1995 | |
1996 | result = !pMT->HasFinalizer(); |
1997 | |
1998 | #ifdef FEATURE_READYTORUN_COMPILER |
1999 | if (IsReadyToRunCompilation() && !pMT->IsInheritanceChainLayoutFixedInCurrentVersionBubble()) |
2000 | { |
2001 | result = false; |
2002 | } |
2003 | #endif |
2004 | |
2005 | EE_TO_JIT_TRANSITION_LEAF(); |
2006 | return result; |
2007 | } |
2008 | |
2009 | unsigned CEEInfo::getClassAlignmentRequirement(CORINFO_CLASS_HANDLE type, BOOL fDoubleAlignHint) |
2010 | { |
2011 | CONTRACTL { |
2012 | SO_TOLERANT; |
2013 | NOTHROW; |
2014 | GC_NOTRIGGER; |
2015 | MODE_PREEMPTIVE; |
2016 | } CONTRACTL_END; |
2017 | |
2018 | // Default alignment is sizeof(void*) |
2019 | unsigned result = TARGET_POINTER_SIZE; |
2020 | |
2021 | JIT_TO_EE_TRANSITION_LEAF(); |
2022 | |
2023 | TypeHandle clsHnd(type); |
2024 | |
2025 | #ifdef FEATURE_DOUBLE_ALIGNMENT_HINT |
2026 | if (fDoubleAlignHint) |
2027 | { |
2028 | MethodTable* pMT = clsHnd.GetMethodTable(); |
2029 | if (pMT != NULL) |
2030 | { |
2031 | // Return the size of the double align hint. Ignore the actual alignment info account |
2032 | // so that structs with 64-bit integer fields do not trigger double aligned frames on x86. |
2033 | if (pMT->GetClass()->IsAlign8Candidate()) |
2034 | result = 8; |
2035 | } |
2036 | } |
2037 | else |
2038 | #endif |
2039 | { |
2040 | result = getClassAlignmentRequirementStatic(clsHnd); |
2041 | } |
2042 | |
2043 | EE_TO_JIT_TRANSITION_LEAF(); |
2044 | |
2045 | return result; |
2046 | } |
2047 | |
2048 | unsigned CEEInfo::getClassAlignmentRequirementStatic(TypeHandle clsHnd) |
2049 | { |
2050 | LIMITED_METHOD_CONTRACT; |
2051 | |
2052 | // Default alignment is sizeof(void*) |
2053 | unsigned result = TARGET_POINTER_SIZE; |
2054 | |
2055 | MethodTable * pMT = clsHnd.GetMethodTable(); |
2056 | if (pMT == NULL) |
2057 | return result; |
2058 | |
2059 | if (pMT->HasLayout()) |
2060 | { |
2061 | EEClassLayoutInfo* pInfo = pMT->GetLayoutInfo(); |
2062 | |
2063 | if (clsHnd.IsNativeValueType()) |
2064 | { |
2065 | // if it's the unmanaged view of the managed type, we always use the unmanaged alignment requirement |
2066 | result = pInfo->m_LargestAlignmentRequirementOfAllMembers; |
2067 | } |
2068 | else |
2069 | if (pInfo->IsManagedSequential()) |
2070 | { |
2071 | _ASSERTE(!pMT->ContainsPointers()); |
2072 | |
2073 | // if it's managed sequential, we use the managed alignment requirement |
2074 | result = pInfo->m_ManagedLargestAlignmentRequirementOfAllMembers; |
2075 | } |
2076 | else if (pInfo->IsBlittable()) |
2077 | { |
2078 | _ASSERTE(!pMT->ContainsPointers()); |
2079 | |
2080 | // if it's blittable, we use the unmanaged alignment requirement |
2081 | result = pInfo->m_LargestAlignmentRequirementOfAllMembers; |
2082 | } |
2083 | } |
2084 | |
2085 | #ifdef FEATURE_64BIT_ALIGNMENT |
2086 | if (result < 8 && pMT->RequiresAlign8()) |
2087 | { |
2088 | // If the structure contains 64-bit primitive fields and the platform requires 8-byte alignment for |
2089 | // such fields then make sure we return at least 8-byte alignment. Note that it's technically possible |
2090 | // to create unmanaged APIs that take unaligned structures containing such fields and this |
2091 | // unconditional alignment bump would cause us to get the calling convention wrong on platforms such |
2092 | // as ARM. If we see such cases in the future we'd need to add another control (such as an alignment |
2093 | // property for the StructLayout attribute or a marshaling directive attribute for p/invoke arguments) |
2094 | // that allows more precise control. For now we'll go with the likely scenario. |
2095 | result = 8; |
2096 | } |
2097 | #endif // FEATURE_64BIT_ALIGNMENT |
2098 | |
2099 | return result; |
2100 | } |
2101 | |
2102 | CORINFO_FIELD_HANDLE |
2103 | CEEInfo::getFieldInClass(CORINFO_CLASS_HANDLE clsHnd, INT num) |
2104 | { |
2105 | CONTRACTL { |
2106 | SO_TOLERANT; |
2107 | NOTHROW; |
2108 | GC_NOTRIGGER; |
2109 | MODE_PREEMPTIVE; |
2110 | } CONTRACTL_END; |
2111 | |
2112 | CORINFO_FIELD_HANDLE result = NULL; |
2113 | |
2114 | JIT_TO_EE_TRANSITION_LEAF(); |
2115 | |
2116 | TypeHandle VMClsHnd(clsHnd); |
2117 | |
2118 | MethodTable* pMT= VMClsHnd.AsMethodTable(); |
2119 | |
2120 | result = (CORINFO_FIELD_HANDLE) ((pMT->GetApproxFieldDescListRaw()) + num); |
2121 | |
2122 | EE_TO_JIT_TRANSITION_LEAF(); |
2123 | |
2124 | return result; |
2125 | } |
2126 | |
2127 | mdMethodDef |
2128 | CEEInfo::getMethodDefFromMethod(CORINFO_METHOD_HANDLE hMethod) |
2129 | { |
2130 | CONTRACTL { |
2131 | SO_TOLERANT; |
2132 | NOTHROW; |
2133 | GC_NOTRIGGER; |
2134 | MODE_PREEMPTIVE; |
2135 | } CONTRACTL_END; |
2136 | |
2137 | mdMethodDef result = 0; |
2138 | |
2139 | JIT_TO_EE_TRANSITION_LEAF(); |
2140 | |
2141 | MethodDesc* pMD = GetMethod(hMethod); |
2142 | |
2143 | if (pMD->IsDynamicMethod()) |
2144 | { |
2145 | // Dynamic methods do not have tokens |
2146 | result = mdMethodDefNil; |
2147 | } |
2148 | else |
2149 | { |
2150 | result = pMD->GetMemberDef(); |
2151 | } |
2152 | |
2153 | EE_TO_JIT_TRANSITION_LEAF(); |
2154 | |
2155 | return result; |
2156 | } |
2157 | |
2158 | BOOL CEEInfo::checkMethodModifier(CORINFO_METHOD_HANDLE hMethod, |
2159 | LPCSTR modifier, |
2160 | BOOL fOptional) |
2161 | { |
2162 | CONTRACTL { |
2163 | SO_TOLERANT; |
2164 | THROWS; |
2165 | GC_TRIGGERS; |
2166 | MODE_PREEMPTIVE; |
2167 | } CONTRACTL_END; |
2168 | |
2169 | BOOL result = FALSE; |
2170 | |
2171 | JIT_TO_EE_TRANSITION(); |
2172 | |
2173 | MethodDesc* pMD = GetMethod(hMethod); |
2174 | Module* pModule = pMD->GetModule(); |
2175 | MetaSig sig(pMD); |
2176 | CorElementType eeType = fOptional ? ELEMENT_TYPE_CMOD_OPT : ELEMENT_TYPE_CMOD_REQD; |
2177 | |
2178 | // modopts/modreqs for the method are by convention stored on the return type |
2179 | result = sig.GetReturnProps().HasCustomModifier(pModule, modifier, eeType); |
2180 | |
2181 | EE_TO_JIT_TRANSITION(); |
2182 | |
2183 | return result; |
2184 | } |
2185 | |
2186 | /*********************************************************************/ |
2187 | static unsigned ComputeGCLayout(MethodTable * pMT, BYTE* gcPtrs) |
2188 | { |
2189 | STANDARD_VM_CONTRACT; |
2190 | |
2191 | unsigned result = 0; |
2192 | |
2193 | _ASSERTE(pMT->IsValueType()); |
2194 | |
2195 | // TODO: TypedReference should ideally be implemented as a by-ref-like struct containing a ByReference<T> field, in which |
2196 | // case the check for g_TypedReferenceMT below would not be necessary |
2197 | if (pMT == g_TypedReferenceMT || pMT->HasSameTypeDefAs(g_pByReferenceClass)) |
2198 | { |
2199 | if (gcPtrs[0] == TYPE_GC_NONE) |
2200 | { |
2201 | gcPtrs[0] = TYPE_GC_BYREF; |
2202 | result++; |
2203 | } |
2204 | else if (gcPtrs[0] != TYPE_GC_BYREF) |
2205 | { |
2206 | COMPlusThrowHR(COR_E_BADIMAGEFORMAT); |
2207 | } |
2208 | return result; |
2209 | } |
2210 | |
2211 | ApproxFieldDescIterator fieldIterator(pMT, ApproxFieldDescIterator::INSTANCE_FIELDS); |
2212 | for (FieldDesc *pFD = fieldIterator.Next(); pFD != NULL; pFD = fieldIterator.Next()) |
2213 | { |
2214 | int fieldStartIndex = pFD->GetOffset() / TARGET_POINTER_SIZE; |
2215 | |
2216 | if (pFD->GetFieldType() != ELEMENT_TYPE_VALUETYPE) |
2217 | { |
2218 | if (pFD->IsObjRef()) |
2219 | { |
2220 | if (gcPtrs[fieldStartIndex] == TYPE_GC_NONE) |
2221 | { |
2222 | gcPtrs[fieldStartIndex] = TYPE_GC_REF; |
2223 | result++; |
2224 | } |
2225 | else if (gcPtrs[fieldStartIndex] != TYPE_GC_REF) |
2226 | { |
2227 | COMPlusThrowHR(COR_E_BADIMAGEFORMAT); |
2228 | } |
2229 | } |
2230 | } |
2231 | else |
2232 | { |
2233 | MethodTable * pFieldMT = pFD->GetApproxFieldTypeHandleThrowing().AsMethodTable(); |
2234 | result += ComputeGCLayout(pFieldMT, gcPtrs + fieldStartIndex); |
2235 | } |
2236 | } |
2237 | return result; |
2238 | } |
2239 | |
2240 | unsigned CEEInfo::getClassGClayout (CORINFO_CLASS_HANDLE clsHnd, BYTE* gcPtrs) |
2241 | { |
2242 | CONTRACTL { |
2243 | SO_TOLERANT; |
2244 | THROWS; |
2245 | GC_TRIGGERS; |
2246 | MODE_PREEMPTIVE; |
2247 | } CONTRACTL_END; |
2248 | |
2249 | unsigned result = 0; |
2250 | |
2251 | JIT_TO_EE_TRANSITION(); |
2252 | |
2253 | TypeHandle VMClsHnd(clsHnd); |
2254 | |
2255 | MethodTable* pMT = VMClsHnd.GetMethodTable(); |
2256 | |
2257 | if (VMClsHnd.IsNativeValueType()) |
2258 | { |
2259 | // native value types have no GC pointers |
2260 | result = 0; |
2261 | memset(gcPtrs, TYPE_GC_NONE, |
2262 | (VMClsHnd.GetSize() + TARGET_POINTER_SIZE - 1) / TARGET_POINTER_SIZE); |
2263 | } |
2264 | else if (pMT->IsByRefLike()) |
2265 | { |
2266 | // TODO: TypedReference should ideally be implemented as a by-ref-like struct containing a ByReference<T> field, in |
2267 | // which case the check for g_TypedReferenceMT below would not be necessary |
2268 | if (pMT == g_TypedReferenceMT) |
2269 | { |
2270 | gcPtrs[0] = TYPE_GC_BYREF; |
2271 | gcPtrs[1] = TYPE_GC_NONE; |
2272 | result = 1; |
2273 | } |
2274 | else |
2275 | { |
2276 | memset(gcPtrs, TYPE_GC_NONE, |
2277 | (VMClsHnd.GetSize() + TARGET_POINTER_SIZE - 1) / TARGET_POINTER_SIZE); |
2278 | // Note: This case is more complicated than the TypedReference case |
2279 | // due to ByRefLike structs being included as fields in other value |
2280 | // types (TypedReference can not be.) |
2281 | result = ComputeGCLayout(VMClsHnd.AsMethodTable(), gcPtrs); |
2282 | } |
2283 | } |
2284 | else |
2285 | { |
2286 | _ASSERTE(sizeof(BYTE) == 1); |
2287 | |
2288 | BOOL isValueClass = pMT->IsValueType(); |
2289 | |
2290 | #ifdef FEATURE_READYTORUN_COMPILER |
2291 | _ASSERTE(isValueClass || !IsReadyToRunCompilation() || pMT->IsInheritanceChainLayoutFixedInCurrentVersionBubble()); |
2292 | #endif |
2293 | |
2294 | unsigned int size = isValueClass ? VMClsHnd.GetSize() : pMT->GetNumInstanceFieldBytes() + OBJECT_SIZE; |
2295 | |
2296 | // assume no GC pointers at first |
2297 | result = 0; |
2298 | memset(gcPtrs, TYPE_GC_NONE, |
2299 | (size + TARGET_POINTER_SIZE - 1) / TARGET_POINTER_SIZE); |
2300 | |
2301 | // walk the GC descriptors, turning on the correct bits |
2302 | if (pMT->ContainsPointers()) |
2303 | { |
2304 | CGCDesc* map = CGCDesc::GetCGCDescFromMT(pMT); |
2305 | CGCDescSeries * pByValueSeries = map->GetLowestSeries(); |
2306 | |
2307 | for (SIZE_T i = 0; i < map->GetNumSeries(); i++) |
2308 | { |
2309 | // Get offset into the value class of the first pointer field (includes a +Object) |
2310 | size_t cbSeriesSize = pByValueSeries->GetSeriesSize() + pMT->GetBaseSize(); |
2311 | size_t cbSeriesOffset = pByValueSeries->GetSeriesOffset(); |
2312 | size_t cbOffset = isValueClass ? cbSeriesOffset - OBJECT_SIZE : cbSeriesOffset; |
2313 | |
2314 | _ASSERTE (cbOffset % TARGET_POINTER_SIZE == 0); |
2315 | _ASSERTE (cbSeriesSize % TARGET_POINTER_SIZE == 0); |
2316 | |
2317 | result += (unsigned) (cbSeriesSize / TARGET_POINTER_SIZE); |
2318 | memset(&gcPtrs[cbOffset / TARGET_POINTER_SIZE], TYPE_GC_REF, cbSeriesSize / TARGET_POINTER_SIZE); |
2319 | |
2320 | pByValueSeries++; |
2321 | } |
2322 | } |
2323 | } |
2324 | |
2325 | EE_TO_JIT_TRANSITION(); |
2326 | |
2327 | return result; |
2328 | } |
2329 | |
2330 | // returns the enregister info for a struct based on type of fields, alignment, etc. |
2331 | bool CEEInfo::getSystemVAmd64PassStructInRegisterDescriptor( |
2332 | /*IN*/ CORINFO_CLASS_HANDLE structHnd, |
2333 | /*OUT*/ SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR* structPassInRegDescPtr) |
2334 | { |
2335 | CONTRACTL { |
2336 | SO_TOLERANT; |
2337 | THROWS; |
2338 | GC_TRIGGERS; |
2339 | MODE_PREEMPTIVE; |
2340 | } CONTRACTL_END; |
2341 | |
2342 | #if defined(UNIX_AMD64_ABI_ITF) |
2343 | JIT_TO_EE_TRANSITION(); |
2344 | |
2345 | _ASSERTE(structPassInRegDescPtr != nullptr); |
2346 | TypeHandle th(structHnd); |
2347 | |
2348 | structPassInRegDescPtr->passedInRegisters = false; |
2349 | |
2350 | // Make sure this is a value type. |
2351 | if (th.IsValueType()) |
2352 | { |
2353 | _ASSERTE((CorInfoType2UnixAmd64Classification(th.GetInternalCorElementType()) == SystemVClassificationTypeStruct) || |
2354 | (CorInfoType2UnixAmd64Classification(th.GetInternalCorElementType()) == SystemVClassificationTypeTypedReference)); |
2355 | |
2356 | // The useNativeLayout in this case tracks whether the classification |
2357 | // is for a native layout of the struct or not. |
2358 | // If the struct has special marshaling it has a native layout. |
2359 | // In such cases the classifier needs to use the native layout. |
2360 | // For structs with no native layout, the managed layout should be used |
2361 | // even if classified for the purposes of marshaling/PInvoke passing. |
2362 | bool useNativeLayout = false; |
2363 | MethodTable* methodTablePtr = nullptr; |
2364 | if (!th.IsTypeDesc()) |
2365 | { |
2366 | methodTablePtr = th.AsMethodTable(); |
2367 | } |
2368 | else |
2369 | { |
2370 | _ASSERTE(th.IsNativeValueType()); |
2371 | |
2372 | useNativeLayout = true; |
2373 | methodTablePtr = th.AsNativeValueType(); |
2374 | } |
2375 | _ASSERTE(methodTablePtr != nullptr); |
2376 | |
2377 | // If we have full support for UNIX_AMD64_ABI, and not just the interface, |
2378 | // then we've cached whether this is a reg passed struct in the MethodTable, computed during |
2379 | // MethodTable construction. Otherwise, we are just building in the interface, and we haven't |
2380 | // computed or cached anything, so we need to compute it now. |
2381 | #if defined(UNIX_AMD64_ABI) |
2382 | bool canPassInRegisters = useNativeLayout ? methodTablePtr->GetLayoutInfo()->IsNativeStructPassedInRegisters() |
2383 | : methodTablePtr->IsRegPassedStruct(); |
2384 | #else // !defined(UNIX_AMD64_ABI) |
2385 | bool canPassInRegisters = false; |
2386 | SystemVStructRegisterPassingHelper helper((unsigned int)th.GetSize()); |
2387 | if (th.GetSize() <= CLR_SYSTEMV_MAX_STRUCT_BYTES_TO_PASS_IN_REGISTERS) |
2388 | { |
2389 | canPassInRegisters = methodTablePtr->ClassifyEightBytes(&helper, 0, 0, useNativeLayout); |
2390 | } |
2391 | #endif // !defined(UNIX_AMD64_ABI) |
2392 | |
2393 | if (canPassInRegisters) |
2394 | { |
2395 | #if defined(UNIX_AMD64_ABI) |
2396 | SystemVStructRegisterPassingHelper helper((unsigned int)th.GetSize()); |
2397 | bool result = methodTablePtr->ClassifyEightBytes(&helper, 0, 0, useNativeLayout); |
2398 | |
2399 | // The answer must be true at this point. |
2400 | _ASSERTE(result); |
2401 | #endif // UNIX_AMD64_ABI |
2402 | |
2403 | structPassInRegDescPtr->passedInRegisters = true; |
2404 | |
2405 | structPassInRegDescPtr->eightByteCount = helper.eightByteCount; |
2406 | _ASSERTE(structPassInRegDescPtr->eightByteCount <= CLR_SYSTEMV_MAX_EIGHTBYTES_COUNT_TO_PASS_IN_REGISTERS); |
2407 | |
2408 | for (unsigned int i = 0; i < CLR_SYSTEMV_MAX_EIGHTBYTES_COUNT_TO_PASS_IN_REGISTERS; i++) |
2409 | { |
2410 | structPassInRegDescPtr->eightByteClassifications[i] = helper.eightByteClassifications[i]; |
2411 | structPassInRegDescPtr->eightByteSizes[i] = helper.eightByteSizes[i]; |
2412 | structPassInRegDescPtr->eightByteOffsets[i] = helper.eightByteOffsets[i]; |
2413 | } |
2414 | } |
2415 | |
2416 | _ASSERTE(structPassInRegDescPtr->passedInRegisters == canPassInRegisters); |
2417 | } |
2418 | |
2419 | EE_TO_JIT_TRANSITION(); |
2420 | |
2421 | return true; |
2422 | #else // !defined(UNIX_AMD64_ABI_ITF) |
2423 | return false; |
2424 | #endif // !defined(UNIX_AMD64_ABI_ITF) |
2425 | } |
2426 | |
2427 | /*********************************************************************/ |
2428 | unsigned CEEInfo::getClassNumInstanceFields (CORINFO_CLASS_HANDLE clsHnd) |
2429 | { |
2430 | CONTRACTL { |
2431 | SO_TOLERANT; |
2432 | NOTHROW; |
2433 | GC_NOTRIGGER; |
2434 | MODE_PREEMPTIVE; |
2435 | } CONTRACTL_END; |
2436 | |
2437 | unsigned result = 0; |
2438 | |
2439 | JIT_TO_EE_TRANSITION_LEAF(); |
2440 | |
2441 | TypeHandle th(clsHnd); |
2442 | |
2443 | if (!th.IsTypeDesc()) |
2444 | { |
2445 | result = th.AsMethodTable()->GetNumInstanceFields(); |
2446 | } |
2447 | else |
2448 | { |
2449 | // native value types are opaque aggregates with explicit size |
2450 | result = 0; |
2451 | } |
2452 | |
2453 | EE_TO_JIT_TRANSITION_LEAF(); |
2454 | |
2455 | return result; |
2456 | } |
2457 | |
2458 | |
2459 | CorInfoType CEEInfo::asCorInfoType (CORINFO_CLASS_HANDLE clsHnd) |
2460 | { |
2461 | CONTRACTL { |
2462 | SO_TOLERANT; |
2463 | THROWS; |
2464 | GC_TRIGGERS; |
2465 | MODE_PREEMPTIVE; |
2466 | } CONTRACTL_END; |
2467 | |
2468 | CorInfoType result = CORINFO_TYPE_UNDEF; |
2469 | |
2470 | JIT_TO_EE_TRANSITION(); |
2471 | |
2472 | TypeHandle VMClsHnd(clsHnd); |
2473 | result = toJitType(VMClsHnd); |
2474 | |
2475 | EE_TO_JIT_TRANSITION(); |
2476 | |
2477 | return result; |
2478 | } |
2479 | |
2480 | |
2481 | CORINFO_LOOKUP_KIND CEEInfo::getLocationOfThisType(CORINFO_METHOD_HANDLE context) |
2482 | { |
2483 | CONTRACTL { |
2484 | SO_TOLERANT; |
2485 | THROWS; |
2486 | GC_TRIGGERS; |
2487 | MODE_PREEMPTIVE; |
2488 | } CONTRACTL_END; |
2489 | |
2490 | CORINFO_LOOKUP_KIND result; |
2491 | |
2492 | /* Initialize fields of result for debug build warning */ |
2493 | result.needsRuntimeLookup = false; |
2494 | result.runtimeLookupKind = CORINFO_LOOKUP_THISOBJ; |
2495 | |
2496 | JIT_TO_EE_TRANSITION(); |
2497 | |
2498 | MethodDesc *pContextMD = GetMethod(context); |
2499 | |
2500 | // If the method table is not shared, then return CONST |
2501 | if (!pContextMD->GetMethodTable()->IsSharedByGenericInstantiations()) |
2502 | { |
2503 | result.needsRuntimeLookup = false; |
2504 | } |
2505 | else |
2506 | { |
2507 | result.needsRuntimeLookup = true; |
2508 | |
2509 | // If we've got a vtable extra argument, go through that |
2510 | if (pContextMD->RequiresInstMethodTableArg()) |
2511 | { |
2512 | result.runtimeLookupKind = CORINFO_LOOKUP_CLASSPARAM; |
2513 | } |
2514 | // If we've got an object, go through its vtable |
2515 | else if (pContextMD->AcquiresInstMethodTableFromThis()) |
2516 | { |
2517 | result.runtimeLookupKind = CORINFO_LOOKUP_THISOBJ; |
2518 | } |
2519 | // Otherwise go through the method-desc argument |
2520 | else |
2521 | { |
2522 | _ASSERTE(pContextMD->RequiresInstMethodDescArg()); |
2523 | result.runtimeLookupKind = CORINFO_LOOKUP_METHODPARAM; |
2524 | } |
2525 | } |
2526 | |
2527 | EE_TO_JIT_TRANSITION(); |
2528 | |
2529 | return result; |
2530 | } |
2531 | |
2532 | CORINFO_METHOD_HANDLE CEEInfo::GetDelegateCtor( |
2533 | CORINFO_METHOD_HANDLE methHnd, |
2534 | CORINFO_CLASS_HANDLE clsHnd, |
2535 | CORINFO_METHOD_HANDLE targetMethodHnd, |
2536 | DelegateCtorArgs *pCtorData) |
2537 | { |
2538 | CONTRACTL { |
2539 | SO_TOLERANT; |
2540 | THROWS; |
2541 | GC_TRIGGERS; |
2542 | MODE_PREEMPTIVE; |
2543 | } CONTRACTL_END; |
2544 | |
2545 | if (isVerifyOnly()) |
2546 | { |
2547 | // No sense going through the optimized case just for verification and it can cause issues parsing |
2548 | // uninstantiated generic signatures. |
2549 | return methHnd; |
2550 | } |
2551 | |
2552 | CORINFO_METHOD_HANDLE result = NULL; |
2553 | |
2554 | JIT_TO_EE_TRANSITION(); |
2555 | |
2556 | MethodDesc *pCurrentCtor = (MethodDesc*)methHnd; |
2557 | if (!pCurrentCtor->IsFCall()) |
2558 | { |
2559 | result = methHnd; |
2560 | } |
2561 | else |
2562 | { |
2563 | MethodDesc *pTargetMethod = (MethodDesc*)targetMethodHnd; |
2564 | TypeHandle delegateType = (TypeHandle)clsHnd; |
2565 | |
2566 | MethodDesc *pDelegateCtor = COMDelegate::GetDelegateCtor(delegateType, pTargetMethod, pCtorData); |
2567 | if (!pDelegateCtor) |
2568 | pDelegateCtor = pCurrentCtor; |
2569 | result = (CORINFO_METHOD_HANDLE)pDelegateCtor; |
2570 | } |
2571 | |
2572 | EE_TO_JIT_TRANSITION(); |
2573 | |
2574 | return result; |
2575 | } |
2576 | |
2577 | void CEEInfo::MethodCompileComplete(CORINFO_METHOD_HANDLE methHnd) |
2578 | { |
2579 | CONTRACTL { |
2580 | SO_TOLERANT; |
2581 | THROWS; |
2582 | GC_TRIGGERS; |
2583 | MODE_PREEMPTIVE; |
2584 | } CONTRACTL_END; |
2585 | |
2586 | JIT_TO_EE_TRANSITION(); |
2587 | |
2588 | MethodDesc* pMD = GetMethod(methHnd); |
2589 | |
2590 | if (pMD->IsDynamicMethod()) |
2591 | { |
2592 | pMD->AsDynamicMethodDesc()->GetResolver()->FreeCompileTimeState(); |
2593 | } |
2594 | |
2595 | EE_TO_JIT_TRANSITION(); |
2596 | } |
2597 | |
2598 | // Given a module scope (scopeHnd), a method handle (context) and an metadata token, |
2599 | // attempt to load the handle (type, field or method) associated with the token. |
2600 | // If this is not possible at compile-time (because the method code is shared and the token contains type parameters) |
2601 | // then indicate how the handle should be looked up at run-time. |
2602 | // |
2603 | // See corinfo.h for more details |
2604 | // |
2605 | void CEEInfo::embedGenericHandle( |
2606 | CORINFO_RESOLVED_TOKEN * pResolvedToken, |
2607 | BOOL fEmbedParent, |
2608 | CORINFO_GENERICHANDLE_RESULT *pResult) |
2609 | { |
2610 | CONTRACTL { |
2611 | SO_TOLERANT; |
2612 | THROWS; |
2613 | GC_TRIGGERS; |
2614 | MODE_PREEMPTIVE; |
2615 | } CONTRACTL_END; |
2616 | |
2617 | INDEBUG(memset(pResult, 0xCC, sizeof(*pResult))); |
2618 | |
2619 | JIT_TO_EE_TRANSITION(); |
2620 | |
2621 | BOOL fRuntimeLookup; |
2622 | MethodDesc * pTemplateMD = NULL; |
2623 | |
2624 | if (!fEmbedParent && pResolvedToken->hMethod != NULL) |
2625 | { |
2626 | MethodDesc * pMD = (MethodDesc *)pResolvedToken->hMethod; |
2627 | TypeHandle th(pResolvedToken->hClass); |
2628 | |
2629 | pResult->handleType = CORINFO_HANDLETYPE_METHOD; |
2630 | |
2631 | Instantiation methodInst = pMD->GetMethodInstantiation(); |
2632 | |
2633 | pMD = MethodDesc::FindOrCreateAssociatedMethodDesc(pMD, th.GetMethodTable(), FALSE, methodInst, FALSE); |
2634 | |
2635 | // Normalize the method handle for reflection |
2636 | if (pResolvedToken->tokenType == CORINFO_TOKENKIND_Ldtoken) |
2637 | pMD = MethodDesc::FindOrCreateAssociatedMethodDescForReflection(pMD, th, methodInst); |
2638 | |
2639 | pResult->compileTimeHandle = (CORINFO_GENERIC_HANDLE)pMD; |
2640 | pTemplateMD = pMD; |
2641 | |
2642 | // Runtime lookup is only required for stubs. Regular entrypoints are always the same shared MethodDescs. |
2643 | fRuntimeLookup = pMD->IsWrapperStub() && |
2644 | (pMD->GetMethodTable()->IsSharedByGenericInstantiations() || TypeHandle::IsCanonicalSubtypeInstantiation(methodInst)); |
2645 | } |
2646 | else |
2647 | if (!fEmbedParent && pResolvedToken->hField != NULL) |
2648 | { |
2649 | FieldDesc * pFD = (FieldDesc *)pResolvedToken->hField; |
2650 | TypeHandle th(pResolvedToken->hClass); |
2651 | |
2652 | pResult->handleType = CORINFO_HANDLETYPE_FIELD; |
2653 | |
2654 | pResult->compileTimeHandle = (CORINFO_GENERIC_HANDLE)pFD; |
2655 | |
2656 | fRuntimeLookup = th.IsSharedByGenericInstantiations() && pFD->IsStatic(); |
2657 | } |
2658 | else |
2659 | { |
2660 | TypeHandle th(pResolvedToken->hClass); |
2661 | |
2662 | pResult->handleType = CORINFO_HANDLETYPE_CLASS; |
2663 | |
2664 | if (pResolvedToken->tokenType == CORINFO_TOKENKIND_Newarr) |
2665 | { |
2666 | pResult->compileTimeHandle = (CORINFO_GENERIC_HANDLE)th.AsArray()->GetTemplateMethodTable(); |
2667 | } |
2668 | else |
2669 | { |
2670 | pResult->compileTimeHandle = (CORINFO_GENERIC_HANDLE)th.AsPtr(); |
2671 | } |
2672 | |
2673 | if (fEmbedParent && pResolvedToken->hMethod != NULL) |
2674 | { |
2675 | MethodDesc * pDeclaringMD = (MethodDesc *)pResolvedToken->hMethod; |
2676 | |
2677 | if (!pDeclaringMD->GetMethodTable()->HasSameTypeDefAs(th.GetMethodTable())) |
2678 | { |
2679 | // |
2680 | // The method type may point to a sub-class of the actual class that declares the method. |
2681 | // It is important to embed the declaring type in this case. |
2682 | // |
2683 | |
2684 | pTemplateMD = pDeclaringMD; |
2685 | |
2686 | pResult->compileTimeHandle = (CORINFO_GENERIC_HANDLE)pDeclaringMD->GetMethodTable(); |
2687 | } |
2688 | } |
2689 | |
2690 | // IsSharedByGenericInstantiations would not work here. The runtime lookup is required |
2691 | // even for standalone generic variables that show up as __Canon here. |
2692 | fRuntimeLookup = th.IsCanonicalSubtype(); |
2693 | } |
2694 | |
2695 | _ASSERTE(pResult->compileTimeHandle); |
2696 | |
2697 | if (fRuntimeLookup |
2698 | // Handle invalid IL - see comment in code:CEEInfo::ComputeRuntimeLookupForSharedGenericToken |
2699 | && ContextIsShared(pResolvedToken->tokenContext)) |
2700 | { |
2701 | DictionaryEntryKind entryKind = EmptySlot; |
2702 | switch (pResult->handleType) |
2703 | { |
2704 | case CORINFO_HANDLETYPE_CLASS: |
2705 | entryKind = (pTemplateMD != NULL) ? DeclaringTypeHandleSlot : TypeHandleSlot; |
2706 | break; |
2707 | case CORINFO_HANDLETYPE_METHOD: |
2708 | entryKind = MethodDescSlot; |
2709 | break; |
2710 | case CORINFO_HANDLETYPE_FIELD: |
2711 | entryKind = FieldDescSlot; |
2712 | break; |
2713 | default: |
2714 | _ASSERTE(false); |
2715 | } |
2716 | |
2717 | ComputeRuntimeLookupForSharedGenericToken(entryKind, |
2718 | pResolvedToken, |
2719 | NULL, |
2720 | pTemplateMD, |
2721 | &pResult->lookup); |
2722 | } |
2723 | else |
2724 | { |
2725 | // If the target is not shared then we've already got our result and |
2726 | // can simply do a static look up |
2727 | pResult->lookup.lookupKind.needsRuntimeLookup = false; |
2728 | |
2729 | pResult->lookup.constLookup.handle = pResult->compileTimeHandle; |
2730 | pResult->lookup.constLookup.accessType = IAT_VALUE; |
2731 | } |
2732 | |
2733 | EE_TO_JIT_TRANSITION(); |
2734 | } |
2735 | |
2736 | void CEEInfo::ScanForModuleDependencies(Module* pModule, SigPointer psig) |
2737 | { |
2738 | STANDARD_VM_CONTRACT; |
2739 | |
2740 | _ASSERTE(pModule && !pModule->IsSystem()); |
2741 | |
2742 | CorElementType eType; |
2743 | IfFailThrow(psig.GetElemType(&eType)); |
2744 | |
2745 | switch (eType) |
2746 | { |
2747 | case ELEMENT_TYPE_GENERICINST: |
2748 | { |
2749 | ScanForModuleDependencies(pModule,psig); |
2750 | IfFailThrow(psig.SkipExactlyOne()); |
2751 | |
2752 | ULONG ntypars; |
2753 | IfFailThrow(psig.GetData(&ntypars)); |
2754 | for (ULONG i = 0; i < ntypars; i++) |
2755 | { |
2756 | ScanForModuleDependencies(pModule,psig); |
2757 | IfFailThrow(psig.SkipExactlyOne()); |
2758 | } |
2759 | break; |
2760 | } |
2761 | |
2762 | case ELEMENT_TYPE_VALUETYPE: |
2763 | case ELEMENT_TYPE_CLASS: |
2764 | { |
2765 | mdToken tk; |
2766 | IfFailThrow(psig.GetToken(&tk)); |
2767 | if (TypeFromToken(tk) == mdtTypeRef) |
2768 | { |
2769 | Module * pTypeDefModule; |
2770 | mdToken tkTypeDef; |
2771 | |
2772 | if (ClassLoader::ResolveTokenToTypeDefThrowing(pModule, tk, &pTypeDefModule, &tkTypeDef)) |
2773 | break; |
2774 | |
2775 | if (!pTypeDefModule->IsSystem() && (pModule != pTypeDefModule)) |
2776 | { |
2777 | m_pOverride->addActiveDependency((CORINFO_MODULE_HANDLE)pModule, (CORINFO_MODULE_HANDLE)pTypeDefModule); |
2778 | } |
2779 | } |
2780 | break; |
2781 | } |
2782 | |
2783 | default: |
2784 | break; |
2785 | } |
2786 | } |
2787 | |
2788 | void CEEInfo::ScanMethodSpec(Module * pModule, PCCOR_SIGNATURE pMethodSpec, ULONG cbMethodSpec) |
2789 | { |
2790 | STANDARD_VM_CONTRACT; |
2791 | |
2792 | SigPointer sp(pMethodSpec, cbMethodSpec); |
2793 | |
2794 | BYTE etype; |
2795 | IfFailThrow(sp.GetByte(&etype)); |
2796 | |
2797 | _ASSERT(etype == (BYTE)IMAGE_CEE_CS_CALLCONV_GENERICINST); |
2798 | |
2799 | ULONG nGenericMethodArgs; |
2800 | IfFailThrow(sp.GetData(&nGenericMethodArgs)); |
2801 | |
2802 | for (ULONG i = 0; i < nGenericMethodArgs; i++) |
2803 | { |
2804 | ScanForModuleDependencies(pModule,sp); |
2805 | IfFailThrow(sp.SkipExactlyOne()); |
2806 | } |
2807 | } |
2808 | |
2809 | BOOL CEEInfo::ScanTypeSpec(Module * pModule, PCCOR_SIGNATURE pTypeSpec, ULONG cbTypeSpec) |
2810 | { |
2811 | STANDARD_VM_CONTRACT; |
2812 | |
2813 | SigPointer sp(pTypeSpec, cbTypeSpec); |
2814 | |
2815 | CorElementType eType; |
2816 | IfFailThrow(sp.GetElemType(&eType)); |
2817 | |
2818 | // Filter out non-instantiated types and typedescs (typevars, arrays, ...) |
2819 | if (eType != ELEMENT_TYPE_GENERICINST) |
2820 | { |
2821 | // Scanning of the parent chain is required for reference types only. |
2822 | // Note that the parent chain MUST NOT be scanned for instantiated |
2823 | // generic variables because of they are not a real dependencies. |
2824 | return (eType == ELEMENT_TYPE_CLASS); |
2825 | } |
2826 | |
2827 | IfFailThrow(sp.SkipExactlyOne()); |
2828 | |
2829 | ULONG ntypars; |
2830 | IfFailThrow(sp.GetData(&ntypars)); |
2831 | |
2832 | for (ULONG i = 0; i < ntypars; i++) |
2833 | { |
2834 | ScanForModuleDependencies(pModule,sp); |
2835 | IfFailThrow(sp.SkipExactlyOne()); |
2836 | } |
2837 | |
2838 | return TRUE; |
2839 | } |
2840 | |
2841 | void CEEInfo::ScanInstantiation(Module * pModule, Instantiation inst) |
2842 | { |
2843 | STANDARD_VM_CONTRACT; |
2844 | |
2845 | for (DWORD i = 0; i < inst.GetNumArgs(); i++) |
2846 | { |
2847 | TypeHandle th = inst[i]; |
2848 | if (th.IsTypeDesc()) |
2849 | continue; |
2850 | |
2851 | MethodTable * pMT = th.AsMethodTable(); |
2852 | |
2853 | Module * pDefModule = pMT->GetModule(); |
2854 | |
2855 | if (!pDefModule->IsSystem() && (pModule != pDefModule)) |
2856 | { |
2857 | m_pOverride->addActiveDependency((CORINFO_MODULE_HANDLE)pModule, (CORINFO_MODULE_HANDLE)pDefModule); |
2858 | } |
2859 | |
2860 | if (pMT->HasInstantiation()) |
2861 | { |
2862 | ScanInstantiation(pModule, pMT->GetInstantiation()); |
2863 | } |
2864 | } |
2865 | } |
2866 | |
2867 | // |
2868 | // ScanToken is used to track triggers for creation of per-AppDomain state instead, including allocations required for statics and |
2869 | // triggering of module cctors. |
2870 | // |
2871 | // The basic rule is: There should be no possibility of a shared module that is "active" to have a direct call into a module that |
2872 | // is not "active". And we don't want to intercept every call during runtime, so during compile time we track static calls and |
2873 | // everything that can result in new virtual calls. |
2874 | // |
2875 | // The current algorithm (scan the parent type chain and instantiation variables) is more than enough to maintain this invariant. |
2876 | // One could come up with a more efficient algorithm that still maintains the invariant, but it may introduce backward compatibility |
2877 | // issues. |
2878 | // |
2879 | // For efficiency, the implementation leverages the loaded types as much as possible. Unfortunately, we still have to go back to |
2880 | // metadata when the generic variables could have been substituted via generic context. |
2881 | // |
2882 | void CEEInfo::ScanToken(Module * pModule, CORINFO_RESOLVED_TOKEN * pResolvedToken, TypeHandle th, MethodDesc * pMD) |
2883 | { |
2884 | STANDARD_VM_CONTRACT; |
2885 | |
2886 | if (pModule->IsSystem()) |
2887 | return; |
2888 | |
2889 | if (isVerifyOnly()) |
2890 | return; |
2891 | |
2892 | // |
2893 | // Scan method instantiation |
2894 | // |
2895 | if (pMD != NULL && pResolvedToken->pMethodSpec != NULL) |
2896 | { |
2897 | if (ContextIsInstantiated(pResolvedToken->tokenContext)) |
2898 | { |
2899 | ScanMethodSpec(pModule, pResolvedToken->pMethodSpec, pResolvedToken->cbMethodSpec); |
2900 | } |
2901 | else |
2902 | { |
2903 | ScanInstantiation(pModule, pMD->GetMethodInstantiation()); |
2904 | } |
2905 | } |
2906 | |
2907 | if (th.IsTypeDesc()) |
2908 | return; |
2909 | |
2910 | MethodTable * pMT = th.AsMethodTable(); |
2911 | |
2912 | // |
2913 | // Scan type instantiation |
2914 | // |
2915 | if (pResolvedToken->pTypeSpec != NULL) |
2916 | { |
2917 | if (ContextIsInstantiated(pResolvedToken->tokenContext)) |
2918 | { |
2919 | if (!ScanTypeSpec(pModule, pResolvedToken->pTypeSpec, pResolvedToken->cbTypeSpec)) |
2920 | return; |
2921 | } |
2922 | else |
2923 | { |
2924 | ScanInstantiation(pModule, pMT->GetInstantiation()); |
2925 | } |
2926 | } |
2927 | |
2928 | // |
2929 | // Scan chain of parent types |
2930 | // |
2931 | for (;;) |
2932 | { |
2933 | Module * pDefModule = pMT->GetModule(); |
2934 | if (pDefModule->IsSystem()) |
2935 | break; |
2936 | |
2937 | if (pModule != pDefModule) |
2938 | { |
2939 | m_pOverride->addActiveDependency((CORINFO_MODULE_HANDLE)pModule, (CORINFO_MODULE_HANDLE)pDefModule); |
2940 | } |
2941 | |
2942 | MethodTable * pParentMT = pMT->GetParentMethodTable(); |
2943 | if (pParentMT == NULL) |
2944 | break; |
2945 | |
2946 | if (pParentMT->HasInstantiation()) |
2947 | { |
2948 | IMDInternalImport* pInternalImport = pDefModule->GetMDImport(); |
2949 | |
2950 | mdToken tkParent; |
2951 | IfFailThrow(pInternalImport->GetTypeDefProps(pMT->GetCl(), NULL, &tkParent)); |
2952 | |
2953 | if (TypeFromToken(tkParent) == mdtTypeSpec) |
2954 | { |
2955 | PCCOR_SIGNATURE pTypeSpec; |
2956 | ULONG cbTypeSpec; |
2957 | IfFailThrow(pInternalImport->GetTypeSpecFromToken(tkParent, &pTypeSpec, &cbTypeSpec)); |
2958 | |
2959 | ScanTypeSpec(pDefModule, pTypeSpec, cbTypeSpec); |
2960 | } |
2961 | } |
2962 | |
2963 | pMT = pParentMT; |
2964 | } |
2965 | } |
2966 | |
2967 | void CEEInfo::ScanTokenForDynamicScope(CORINFO_RESOLVED_TOKEN * pResolvedToken, TypeHandle th, MethodDesc * pMD) |
2968 | { |
2969 | STANDARD_VM_CONTRACT; |
2970 | |
2971 | if (m_pMethodBeingCompiled->IsLCGMethod()) |
2972 | { |
2973 | // The dependency tracking for LCG is irrelevant. Perform immediate activation. |
2974 | if (pMD != NULL && pMD->HasMethodInstantiation()) |
2975 | pMD->EnsureActive(); |
2976 | if (!th.IsTypeDesc()) |
2977 | th.AsMethodTable()->EnsureInstanceActive(); |
2978 | return; |
2979 | } |
2980 | |
2981 | // Stubs-as-IL have to do regular dependency tracking because they can be shared cross-domain. |
2982 | Module * pModule = GetDynamicResolver(pResolvedToken->tokenScope)->GetDynamicMethod()->GetModule(); |
2983 | ScanToken(pModule, pResolvedToken, th, pMD); |
2984 | } |
2985 | |
2986 | MethodDesc * CEEInfo::GetMethodForSecurity(CORINFO_METHOD_HANDLE callerHandle) |
2987 | { |
2988 | STANDARD_VM_CONTRACT; |
2989 | |
2990 | // Cache the cast lookup |
2991 | if (callerHandle == m_hMethodForSecurity_Key) |
2992 | { |
2993 | return m_pMethodForSecurity_Value; |
2994 | } |
2995 | |
2996 | MethodDesc * pCallerMethod = (MethodDesc *)callerHandle; |
2997 | |
2998 | //If the caller is generic, load the open type and then load the field again, This allows us to |
2999 | //differentiate between BadGeneric<T> containing a memberRef for a field of type InaccessibleClass and |
3000 | //GoodGeneric<T> containing a memberRef for a field of type T instantiated over InaccessibleClass. |
3001 | MethodDesc * pMethodForSecurity = pCallerMethod->IsILStub() ? |
3002 | pCallerMethod : pCallerMethod->LoadTypicalMethodDefinition(); |
3003 | |
3004 | m_hMethodForSecurity_Key = callerHandle; |
3005 | m_pMethodForSecurity_Value = pMethodForSecurity; |
3006 | |
3007 | return pMethodForSecurity; |
3008 | } |
3009 | |
3010 | // Check that the instantation is <!/!!0, ..., !/!!(n-1)> |
3011 | static BOOL IsSignatureForTypicalInstantiation(SigPointer sigptr, CorElementType varType, ULONG ntypars) |
3012 | { |
3013 | STANDARD_VM_CONTRACT; |
3014 | |
3015 | for (ULONG i = 0; i < ntypars; i++) |
3016 | { |
3017 | CorElementType type; |
3018 | IfFailThrow(sigptr.GetElemType(&type)); |
3019 | if (type != varType) |
3020 | return FALSE; |
3021 | |
3022 | ULONG data; |
3023 | IfFailThrow(sigptr.GetData(&data)); |
3024 | |
3025 | if (data != i) |
3026 | return FALSE; |
3027 | } |
3028 | |
3029 | return TRUE; |
3030 | } |
3031 | |
3032 | // Check that methodSpec instantiation is <!!0, ..., !!(n-1)> |
3033 | static BOOL IsMethodSpecForTypicalInstantation(SigPointer sigptr) |
3034 | { |
3035 | STANDARD_VM_CONTRACT; |
3036 | |
3037 | BYTE etype; |
3038 | IfFailThrow(sigptr.GetByte(&etype)); |
3039 | _ASSERTE(etype == (BYTE)IMAGE_CEE_CS_CALLCONV_GENERICINST); |
3040 | |
3041 | ULONG ntypars; |
3042 | IfFailThrow(sigptr.GetData(&ntypars)); |
3043 | |
3044 | return IsSignatureForTypicalInstantiation(sigptr, ELEMENT_TYPE_MVAR, ntypars); |
3045 | } |
3046 | |
3047 | // Check that typeSpec instantiation is <!0, ..., !(n-1)> |
3048 | static BOOL IsTypeSpecForTypicalInstantiation(SigPointer sigptr) |
3049 | { |
3050 | STANDARD_VM_CONTRACT; |
3051 | |
3052 | CorElementType type; |
3053 | IfFailThrow(sigptr.GetElemType(&type)); |
3054 | if (type != ELEMENT_TYPE_GENERICINST) |
3055 | return FALSE; |
3056 | |
3057 | IfFailThrow(sigptr.SkipExactlyOne()); |
3058 | |
3059 | ULONG ntypars; |
3060 | IfFailThrow(sigptr.GetData(&ntypars)); |
3061 | |
3062 | return IsSignatureForTypicalInstantiation(sigptr, ELEMENT_TYPE_VAR, ntypars); |
3063 | } |
3064 | |
3065 | void CEEInfo::ComputeRuntimeLookupForSharedGenericToken(DictionaryEntryKind entryKind, |
3066 | CORINFO_RESOLVED_TOKEN * pResolvedToken, |
3067 | CORINFO_RESOLVED_TOKEN * pConstrainedResolvedToken, |
3068 | MethodDesc * pTemplateMD /* for method-based slots */, |
3069 | CORINFO_LOOKUP *pResultLookup) |
3070 | { |
3071 | CONTRACTL{ |
3072 | STANDARD_VM_CHECK; |
3073 | PRECONDITION(CheckPointer(pResultLookup)); |
3074 | } CONTRACTL_END; |
3075 | |
3076 | |
3077 | // We should never get here when we are only verifying |
3078 | _ASSERTE(!isVerifyOnly()); |
3079 | |
3080 | pResultLookup->lookupKind.needsRuntimeLookup = true; |
3081 | pResultLookup->lookupKind.runtimeLookupFlags = 0; |
3082 | |
3083 | CORINFO_RUNTIME_LOOKUP *pResult = &pResultLookup->runtimeLookup; |
3084 | pResult->signature = NULL; |
3085 | |
3086 | pResult->indirectFirstOffset = 0; |
3087 | pResult->indirectSecondOffset = 0; |
3088 | |
3089 | // Unless we decide otherwise, just do the lookup via a helper function |
3090 | pResult->indirections = CORINFO_USEHELPER; |
3091 | |
3092 | MethodDesc *pContextMD = GetMethodFromContext(pResolvedToken->tokenContext); |
3093 | MethodTable *pContextMT = pContextMD->GetMethodTable(); |
3094 | |
3095 | // Do not bother computing the runtime lookup if we are inlining. The JIT is going |
3096 | // to abort the inlining attempt anyway. |
3097 | if (pContextMD != m_pMethodBeingCompiled) |
3098 | { |
3099 | return; |
3100 | } |
3101 | |
3102 | // There is a pathological case where invalid IL refereces __Canon type directly, but there is no dictionary availabled to store the lookup. |
3103 | // All callers of ComputeRuntimeLookupForSharedGenericToken have to filter out this case. We can't do much about it here. |
3104 | _ASSERTE(pContextMD->IsSharedByGenericInstantiations()); |
3105 | |
3106 | BOOL fInstrument = FALSE; |
3107 | |
3108 | #ifdef FEATURE_NATIVE_IMAGE_GENERATION |
3109 | // This will make sure that when IBC logging is turned on we will go through a version |
3110 | // of JIT_GenericHandle which logs the access. Note that we still want the dictionaries |
3111 | // to be populated to prepopulate the types at NGen time. |
3112 | if (IsCompilingForNGen() && |
3113 | GetAppDomain()->ToCompilationDomain()->m_fForceInstrument) |
3114 | { |
3115 | fInstrument = TRUE; |
3116 | } |
3117 | #endif // FEATURE_NATIVE_IMAGE_GENERATION |
3118 | |
3119 | if (pContextMD->RequiresInstMethodDescArg()) |
3120 | { |
3121 | pResultLookup->lookupKind.runtimeLookupKind = CORINFO_LOOKUP_METHODPARAM; |
3122 | } |
3123 | else |
3124 | { |
3125 | if (pContextMD->RequiresInstMethodTableArg()) |
3126 | pResultLookup->lookupKind.runtimeLookupKind = CORINFO_LOOKUP_CLASSPARAM; |
3127 | else |
3128 | pResultLookup->lookupKind.runtimeLookupKind = CORINFO_LOOKUP_THISOBJ; |
3129 | } |
3130 | |
3131 | #ifdef FEATURE_READYTORUN_COMPILER |
3132 | if (IsReadyToRunCompilation()) |
3133 | { |
3134 | pResultLookup->lookupKind.runtimeLookupArgs = NULL; |
3135 | |
3136 | switch (entryKind) |
3137 | { |
3138 | case DeclaringTypeHandleSlot: |
3139 | _ASSERTE(pTemplateMD != NULL); |
3140 | pResultLookup->lookupKind.runtimeLookupArgs = pTemplateMD->GetMethodTable(); |
3141 | pResultLookup->lookupKind.runtimeLookupFlags = READYTORUN_FIXUP_DeclaringTypeHandle; |
3142 | break; |
3143 | |
3144 | case TypeHandleSlot: |
3145 | pResultLookup->lookupKind.runtimeLookupFlags = READYTORUN_FIXUP_TypeHandle; |
3146 | break; |
3147 | |
3148 | case MethodDescSlot: |
3149 | case MethodEntrySlot: |
3150 | case ConstrainedMethodEntrySlot: |
3151 | case DispatchStubAddrSlot: |
3152 | { |
3153 | if (pTemplateMD != (MethodDesc*)pResolvedToken->hMethod) |
3154 | ThrowHR(E_NOTIMPL); |
3155 | |
3156 | if (entryKind == MethodDescSlot) |
3157 | pResultLookup->lookupKind.runtimeLookupFlags = READYTORUN_FIXUP_MethodHandle; |
3158 | else if (entryKind == MethodEntrySlot || entryKind == ConstrainedMethodEntrySlot) |
3159 | pResultLookup->lookupKind.runtimeLookupFlags = READYTORUN_FIXUP_MethodEntry; |
3160 | else |
3161 | pResultLookup->lookupKind.runtimeLookupFlags = READYTORUN_FIXUP_VirtualEntry; |
3162 | |
3163 | pResultLookup->lookupKind.runtimeLookupArgs = pConstrainedResolvedToken; |
3164 | |
3165 | break; |
3166 | } |
3167 | |
3168 | case FieldDescSlot: |
3169 | pResultLookup->lookupKind.runtimeLookupFlags = READYTORUN_FIXUP_FieldHandle; |
3170 | break; |
3171 | |
3172 | default: |
3173 | _ASSERTE(!"Unknown dictionary entry kind!" ); |
3174 | IfFailThrow(E_FAIL); |
3175 | } |
3176 | |
3177 | // For R2R compilations, we don't generate the dictionary lookup signatures (dictionary lookups are done in a |
3178 | // different way that is more version resilient... plus we can't have pointers to existing MTs/MDs in the sigs) |
3179 | return; |
3180 | } |
3181 | #endif |
3182 | // If we've got a method type parameter of any kind then we must look in the method desc arg |
3183 | if (pContextMD->RequiresInstMethodDescArg()) |
3184 | { |
3185 | pResult->helper = fInstrument ? CORINFO_HELP_RUNTIMEHANDLE_METHOD_LOG : CORINFO_HELP_RUNTIMEHANDLE_METHOD; |
3186 | |
3187 | if (fInstrument) |
3188 | goto NoSpecialCase; |
3189 | |
3190 | // Special cases: |
3191 | // (1) Naked method type variable: look up directly in instantiation hanging off runtime md |
3192 | // (2) Reference to method-spec of current method (e.g. a recursive call) i.e. currentmeth<!0,...,!(n-1)> |
3193 | if ((entryKind == TypeHandleSlot) && (pResolvedToken->tokenType != CORINFO_TOKENKIND_Newarr)) |
3194 | { |
3195 | SigPointer sigptr(pResolvedToken->pTypeSpec, pResolvedToken->cbTypeSpec); |
3196 | CorElementType type; |
3197 | IfFailThrow(sigptr.GetElemType(&type)); |
3198 | if (type == ELEMENT_TYPE_MVAR) |
3199 | { |
3200 | pResult->indirections = 2; |
3201 | pResult->testForNull = 0; |
3202 | #ifdef FEATURE_PREJIT |
3203 | pResult->testForFixup = 1; |
3204 | #else |
3205 | pResult->testForFixup = 0; |
3206 | #endif |
3207 | pResult->offsets[0] = offsetof(InstantiatedMethodDesc, m_pPerInstInfo); |
3208 | |
3209 | if (decltype(InstantiatedMethodDesc::m_pPerInstInfo)::isRelative) |
3210 | { |
3211 | pResult->indirectFirstOffset = 1; |
3212 | } |
3213 | |
3214 | ULONG data; |
3215 | IfFailThrow(sigptr.GetData(&data)); |
3216 | pResult->offsets[1] = sizeof(TypeHandle) * data; |
3217 | |
3218 | return; |
3219 | } |
3220 | } |
3221 | else if (entryKind == MethodDescSlot) |
3222 | { |
3223 | // It's the context itself (i.e. a recursive call) |
3224 | if (!pTemplateMD->HasSameMethodDefAs(pContextMD)) |
3225 | goto NoSpecialCase; |
3226 | |
3227 | // Now just check that the instantiation is (!!0, ..., !!(n-1)) |
3228 | if (!IsMethodSpecForTypicalInstantation(SigPointer(pResolvedToken->pMethodSpec, pResolvedToken->cbMethodSpec))) |
3229 | goto NoSpecialCase; |
3230 | |
3231 | // Type instantiation has to match too if there is one |
3232 | if (pContextMT->HasInstantiation()) |
3233 | { |
3234 | TypeHandle thTemplate(pResolvedToken->hClass); |
3235 | |
3236 | if (thTemplate.IsTypeDesc() || !thTemplate.AsMethodTable()->HasSameTypeDefAs(pContextMT)) |
3237 | goto NoSpecialCase; |
3238 | |
3239 | // This check filters out method instantiation on generic type definition, like G::M<!!0>() |
3240 | // We may not ever get it here. Filter it out just to be sure... |
3241 | if (pResolvedToken->pTypeSpec == NULL) |
3242 | goto NoSpecialCase; |
3243 | |
3244 | if (!IsTypeSpecForTypicalInstantiation(SigPointer(pResolvedToken->pTypeSpec, pResolvedToken->cbTypeSpec))) |
3245 | goto NoSpecialCase; |
3246 | } |
3247 | |
3248 | // Just use the method descriptor that was passed in! |
3249 | pResult->indirections = 0; |
3250 | pResult->testForNull = 0; |
3251 | pResult->testForFixup = 0; |
3252 | |
3253 | return; |
3254 | } |
3255 | } |
3256 | // Otherwise we must just have class type variables |
3257 | else |
3258 | { |
3259 | _ASSERTE(pContextMT->GetNumGenericArgs() > 0); |
3260 | |
3261 | if (pContextMD->RequiresInstMethodTableArg()) |
3262 | { |
3263 | // If we've got a vtable extra argument, go through that |
3264 | pResult->helper = fInstrument ? CORINFO_HELP_RUNTIMEHANDLE_CLASS_LOG : CORINFO_HELP_RUNTIMEHANDLE_CLASS; |
3265 | } |
3266 | // If we've got an object, go through its vtable |
3267 | else |
3268 | { |
3269 | _ASSERTE(pContextMD->AcquiresInstMethodTableFromThis()); |
3270 | pResult->helper = fInstrument ? CORINFO_HELP_RUNTIMEHANDLE_CLASS_LOG : CORINFO_HELP_RUNTIMEHANDLE_CLASS; |
3271 | } |
3272 | |
3273 | if (fInstrument) |
3274 | goto NoSpecialCase; |
3275 | |
3276 | // Special cases: |
3277 | // (1) Naked class type variable: look up directly in instantiation hanging off vtable |
3278 | // (2) C<!0,...,!(n-1)> where C is the context's class and C is sealed: just return vtable ptr |
3279 | if ((entryKind == TypeHandleSlot) && (pResolvedToken->tokenType != CORINFO_TOKENKIND_Newarr)) |
3280 | { |
3281 | SigPointer sigptr(pResolvedToken->pTypeSpec, pResolvedToken->cbTypeSpec); |
3282 | CorElementType type; |
3283 | IfFailThrow(sigptr.GetElemType(&type)); |
3284 | if (type == ELEMENT_TYPE_VAR) |
3285 | { |
3286 | pResult->indirections = 3; |
3287 | pResult->testForNull = 0; |
3288 | #ifdef FEATURE_PREJIT |
3289 | pResult->testForFixup = 1; |
3290 | #else |
3291 | pResult->testForFixup = 0; |
3292 | #endif |
3293 | pResult->offsets[0] = MethodTable::GetOffsetOfPerInstInfo(); |
3294 | pResult->offsets[1] = sizeof(TypeHandle*) * (pContextMT->GetNumDicts() - 1); |
3295 | ULONG data; |
3296 | IfFailThrow(sigptr.GetData(&data)); |
3297 | pResult->offsets[2] = sizeof(TypeHandle) * data; |
3298 | |
3299 | if (MethodTable::IsPerInstInfoRelative()) |
3300 | { |
3301 | pResult->indirectFirstOffset = 1; |
3302 | pResult->indirectSecondOffset = 1; |
3303 | } |
3304 | |
3305 | return; |
3306 | } |
3307 | else if (type == ELEMENT_TYPE_GENERICINST && |
3308 | (pContextMT->IsSealed() || pResultLookup->lookupKind.runtimeLookupKind == CORINFO_LOOKUP_CLASSPARAM)) |
3309 | { |
3310 | TypeHandle thTemplate(pResolvedToken->hClass); |
3311 | |
3312 | if (thTemplate.IsTypeDesc() || !thTemplate.AsMethodTable()->HasSameTypeDefAs(pContextMT)) |
3313 | goto NoSpecialCase; |
3314 | |
3315 | if (!IsTypeSpecForTypicalInstantiation(SigPointer(pResolvedToken->pTypeSpec, pResolvedToken->cbTypeSpec))) |
3316 | goto NoSpecialCase; |
3317 | |
3318 | // Just use the vtable pointer itself! |
3319 | pResult->indirections = 0; |
3320 | pResult->testForNull = 0; |
3321 | pResult->testForFixup = 0; |
3322 | |
3323 | return; |
3324 | } |
3325 | } |
3326 | } |
3327 | |
3328 | NoSpecialCase: |
3329 | |
3330 | SigBuilder sigBuilder; |
3331 | |
3332 | sigBuilder.AppendData(entryKind); |
3333 | |
3334 | if (pResultLookup->lookupKind.runtimeLookupKind != CORINFO_LOOKUP_METHODPARAM) |
3335 | { |
3336 | _ASSERTE(pContextMT->GetNumDicts() > 0); |
3337 | sigBuilder.AppendData(pContextMT->GetNumDicts() - 1); |
3338 | } |
3339 | |
3340 | Module * pModule = (Module *)pResolvedToken->tokenScope; |
3341 | |
3342 | switch (entryKind) |
3343 | { |
3344 | case DeclaringTypeHandleSlot: |
3345 | _ASSERTE(pTemplateMD != NULL); |
3346 | sigBuilder.AppendElementType(ELEMENT_TYPE_INTERNAL); |
3347 | sigBuilder.AppendPointer(pTemplateMD->GetMethodTable()); |
3348 | // fall through |
3349 | |
3350 | case TypeHandleSlot: |
3351 | { |
3352 | if (pResolvedToken->tokenType == CORINFO_TOKENKIND_Newarr) |
3353 | { |
3354 | if (!IsReadyToRunCompilation()) |
3355 | { |
3356 | sigBuilder.AppendElementType((CorElementType)ELEMENT_TYPE_NATIVE_ARRAY_TEMPLATE_ZAPSIG); |
3357 | } |
3358 | |
3359 | sigBuilder.AppendElementType(ELEMENT_TYPE_SZARRAY); |
3360 | } |
3361 | |
3362 | // Note that we can come here with pResolvedToken->pTypeSpec == NULL for invalid IL that |
3363 | // directly references __Canon |
3364 | if (pResolvedToken->pTypeSpec != NULL) |
3365 | { |
3366 | SigPointer sigptr(pResolvedToken->pTypeSpec, pResolvedToken->cbTypeSpec); |
3367 | sigptr.ConvertToInternalExactlyOne(pModule, NULL, &sigBuilder); |
3368 | } |
3369 | else |
3370 | { |
3371 | sigBuilder.AppendElementType(ELEMENT_TYPE_INTERNAL); |
3372 | sigBuilder.AppendPointer(pResolvedToken->hClass); |
3373 | } |
3374 | } |
3375 | break; |
3376 | |
3377 | case ConstrainedMethodEntrySlot: |
3378 | // Encode constrained type token |
3379 | if (pConstrainedResolvedToken->pTypeSpec != NULL) |
3380 | { |
3381 | SigPointer sigptr(pConstrainedResolvedToken->pTypeSpec, pConstrainedResolvedToken->cbTypeSpec); |
3382 | sigptr.ConvertToInternalExactlyOne(pModule, NULL, &sigBuilder); |
3383 | } |
3384 | else |
3385 | { |
3386 | sigBuilder.AppendElementType(ELEMENT_TYPE_INTERNAL); |
3387 | sigBuilder.AppendPointer(pConstrainedResolvedToken->hClass); |
3388 | } |
3389 | // fall through |
3390 | |
3391 | case MethodDescSlot: |
3392 | case MethodEntrySlot: |
3393 | case DispatchStubAddrSlot: |
3394 | { |
3395 | // Encode containing type |
3396 | if (pResolvedToken->pTypeSpec != NULL) |
3397 | { |
3398 | SigPointer sigptr(pResolvedToken->pTypeSpec, pResolvedToken->cbTypeSpec); |
3399 | sigptr.ConvertToInternalExactlyOne(pModule, NULL, &sigBuilder); |
3400 | } |
3401 | else |
3402 | { |
3403 | sigBuilder.AppendElementType(ELEMENT_TYPE_INTERNAL); |
3404 | sigBuilder.AppendPointer(pResolvedToken->hClass); |
3405 | } |
3406 | |
3407 | // Encode method |
3408 | _ASSERTE(pTemplateMD != NULL); |
3409 | |
3410 | mdMethodDef methodToken = pTemplateMD->GetMemberDef_NoLogging(); |
3411 | DWORD methodFlags = 0; |
3412 | |
3413 | // Check for non-NULL method spec first. We can encode the method instantiation only if we have one in method spec to start with. Note that there are weird cases |
3414 | // like instantiating stub for generic method definition that do not have method spec but that won't be caught by the later conditions either. |
3415 | BOOL fMethodNeedsInstantiation = (pResolvedToken->pMethodSpec != NULL) && pTemplateMD->HasMethodInstantiation() && !pTemplateMD->IsGenericMethodDefinition(); |
3416 | |
3417 | if (pTemplateMD->IsUnboxingStub()) |
3418 | methodFlags |= ENCODE_METHOD_SIG_UnboxingStub; |
3419 | // Always create instantiating stub for method entry points even if the template does not ask for it. It saves caller |
3420 | // from creating throw-away instantiating stub. |
3421 | if (pTemplateMD->IsInstantiatingStub() || (entryKind == MethodEntrySlot)) |
3422 | methodFlags |= ENCODE_METHOD_SIG_InstantiatingStub; |
3423 | if (fMethodNeedsInstantiation) |
3424 | methodFlags |= ENCODE_METHOD_SIG_MethodInstantiation; |
3425 | if (IsNilToken(methodToken)) |
3426 | { |
3427 | methodFlags |= ENCODE_METHOD_SIG_SlotInsteadOfToken; |
3428 | } |
3429 | else |
3430 | if (entryKind == DispatchStubAddrSlot && pTemplateMD->IsVtableMethod()) |
3431 | { |
3432 | // Encode the method for dispatch stub using slot to avoid touching the interface method MethodDesc at runtime |
3433 | |
3434 | // There should be no other flags set if we are encoding the method using slot for virtual stub dispatch |
3435 | _ASSERTE(methodFlags == 0); |
3436 | |
3437 | methodFlags |= ENCODE_METHOD_SIG_SlotInsteadOfToken; |
3438 | } |
3439 | else |
3440 | if (!pTemplateMD->GetModule()->IsInCurrentVersionBubble()) |
3441 | { |
3442 | // Using a method defined in another version bubble. We can assume the slot number is stable only for real interface methods. |
3443 | if (!pTemplateMD->GetMethodTable()->IsInterface() || pTemplateMD->IsStatic() || pTemplateMD->HasMethodInstantiation()) |
3444 | { |
3445 | _ASSERTE(!"References to non-interface methods not yet supported in version resilient images" ); |
3446 | IfFailThrow(E_FAIL); |
3447 | } |
3448 | methodFlags |= ENCODE_METHOD_SIG_SlotInsteadOfToken; |
3449 | } |
3450 | |
3451 | sigBuilder.AppendData(methodFlags); |
3452 | |
3453 | if ((methodFlags & ENCODE_METHOD_SIG_SlotInsteadOfToken) == 0) |
3454 | { |
3455 | // Encode method token and its module context (as method's type) |
3456 | sigBuilder.AppendElementType(ELEMENT_TYPE_INTERNAL); |
3457 | sigBuilder.AppendPointer(pTemplateMD->GetMethodTable()); |
3458 | |
3459 | sigBuilder.AppendData(RidFromToken(methodToken)); |
3460 | } |
3461 | else |
3462 | { |
3463 | sigBuilder.AppendData(pTemplateMD->GetSlot()); |
3464 | } |
3465 | |
3466 | if (fMethodNeedsInstantiation) |
3467 | { |
3468 | SigPointer sigptr(pResolvedToken->pMethodSpec, pResolvedToken->cbMethodSpec); |
3469 | |
3470 | BYTE etype; |
3471 | IfFailThrow(sigptr.GetByte(&etype)); |
3472 | |
3473 | // Load the generic method instantiation |
3474 | THROW_BAD_FORMAT_MAYBE(etype == (BYTE)IMAGE_CEE_CS_CALLCONV_GENERICINST, 0, pModule); |
3475 | |
3476 | DWORD nGenericMethodArgs; |
3477 | IfFailThrow(sigptr.GetData(&nGenericMethodArgs)); |
3478 | sigBuilder.AppendData(nGenericMethodArgs); |
3479 | |
3480 | _ASSERTE(nGenericMethodArgs == pTemplateMD->GetNumGenericMethodArgs()); |
3481 | |
3482 | for (DWORD i = 0; i < nGenericMethodArgs; i++) |
3483 | { |
3484 | sigptr.ConvertToInternalExactlyOne(pModule, NULL, &sigBuilder); |
3485 | } |
3486 | } |
3487 | } |
3488 | break; |
3489 | |
3490 | case FieldDescSlot: |
3491 | { |
3492 | if (pResolvedToken->pTypeSpec != NULL) |
3493 | { |
3494 | // Encode containing type |
3495 | SigPointer sigptr(pResolvedToken->pTypeSpec, pResolvedToken->cbTypeSpec); |
3496 | sigptr.ConvertToInternalExactlyOne(pModule, NULL, &sigBuilder); |
3497 | } |
3498 | else |
3499 | { |
3500 | sigBuilder.AppendElementType(ELEMENT_TYPE_INTERNAL); |
3501 | sigBuilder.AppendPointer(pResolvedToken->hClass); |
3502 | } |
3503 | |
3504 | FieldDesc * pField = (FieldDesc *)pResolvedToken->hField; |
3505 | _ASSERTE(pField != NULL); |
3506 | |
3507 | DWORD fieldIndex = pField->GetApproxEnclosingMethodTable()->GetIndexForFieldDesc(pField); |
3508 | sigBuilder.AppendData(fieldIndex); |
3509 | } |
3510 | break; |
3511 | |
3512 | default: |
3513 | _ASSERTE(false); |
3514 | } |
3515 | |
3516 | DictionaryEntrySignatureSource signatureSource = (IsCompilationProcess() ? FromZapImage : FromJIT); |
3517 | |
3518 | // It's a method dictionary lookup |
3519 | if (pResultLookup->lookupKind.runtimeLookupKind == CORINFO_LOOKUP_METHODPARAM) |
3520 | { |
3521 | _ASSERTE(pContextMD != NULL); |
3522 | _ASSERTE(pContextMD->HasMethodInstantiation()); |
3523 | |
3524 | if (DictionaryLayout::FindToken(pContextMD->GetLoaderAllocator(), pContextMD->GetNumGenericMethodArgs(), pContextMD->GetDictionaryLayout(), pResult, &sigBuilder, 1, signatureSource)) |
3525 | { |
3526 | pResult->testForNull = 1; |
3527 | pResult->testForFixup = 0; |
3528 | |
3529 | // Indirect through dictionary table pointer in InstantiatedMethodDesc |
3530 | pResult->offsets[0] = offsetof(InstantiatedMethodDesc, m_pPerInstInfo); |
3531 | |
3532 | if (decltype(InstantiatedMethodDesc::m_pPerInstInfo)::isRelative) |
3533 | { |
3534 | pResult->indirectFirstOffset = 1; |
3535 | } |
3536 | } |
3537 | } |
3538 | |
3539 | // It's a class dictionary lookup (CORINFO_LOOKUP_CLASSPARAM or CORINFO_LOOKUP_THISOBJ) |
3540 | else |
3541 | { |
3542 | if (DictionaryLayout::FindToken(pContextMT->GetLoaderAllocator(), pContextMT->GetNumGenericArgs(), pContextMT->GetClass()->GetDictionaryLayout(), pResult, &sigBuilder, 2, signatureSource)) |
3543 | { |
3544 | pResult->testForNull = 1; |
3545 | pResult->testForFixup = 0; |
3546 | |
3547 | // Indirect through dictionary table pointer in vtable |
3548 | pResult->offsets[0] = MethodTable::GetOffsetOfPerInstInfo(); |
3549 | |
3550 | // Next indirect through the dictionary appropriate to this instantiated type |
3551 | pResult->offsets[1] = sizeof(TypeHandle*) * (pContextMT->GetNumDicts() - 1); |
3552 | |
3553 | if (MethodTable::IsPerInstInfoRelative()) |
3554 | { |
3555 | pResult->indirectFirstOffset = 1; |
3556 | pResult->indirectSecondOffset = 1; |
3557 | } |
3558 | } |
3559 | } |
3560 | } |
3561 | |
3562 | |
3563 | |
3564 | /*********************************************************************/ |
3565 | const char* CEEInfo::getClassName (CORINFO_CLASS_HANDLE clsHnd) |
3566 | { |
3567 | CONTRACTL { |
3568 | SO_TOLERANT; |
3569 | THROWS; |
3570 | GC_TRIGGERS; |
3571 | MODE_PREEMPTIVE; |
3572 | } CONTRACTL_END; |
3573 | |
3574 | const char* result = NULL; |
3575 | |
3576 | JIT_TO_EE_TRANSITION(); |
3577 | |
3578 | TypeHandle VMClsHnd(clsHnd); |
3579 | MethodTable* pMT = VMClsHnd.GetMethodTable(); |
3580 | if (pMT == NULL) |
3581 | { |
3582 | result = "" ; |
3583 | } |
3584 | else |
3585 | { |
3586 | #ifdef _DEBUG |
3587 | result = pMT->GetDebugClassName(); |
3588 | #else // !_DEBUG |
3589 | // since this is for diagnostic purposes only, |
3590 | // give up on the namespace, as we don't have a buffer to concat it |
3591 | // also note this won't show array class names. |
3592 | LPCUTF8 nameSpace; |
3593 | result = pMT->GetFullyQualifiedNameInfo(&nameSpace); |
3594 | #endif |
3595 | } |
3596 | |
3597 | EE_TO_JIT_TRANSITION(); |
3598 | |
3599 | return result; |
3600 | } |
3601 | |
3602 | /***********************************************************************/ |
3603 | const char* CEEInfo::getHelperName (CorInfoHelpFunc ftnNum) |
3604 | { |
3605 | CONTRACTL { |
3606 | SO_TOLERANT; |
3607 | NOTHROW; |
3608 | GC_NOTRIGGER; |
3609 | MODE_PREEMPTIVE; |
3610 | PRECONDITION(ftnNum >= 0 && ftnNum < CORINFO_HELP_COUNT); |
3611 | } CONTRACTL_END; |
3612 | |
3613 | const char* result = NULL; |
3614 | |
3615 | JIT_TO_EE_TRANSITION_LEAF(); |
3616 | |
3617 | #ifdef CROSSGEN_COMPILE |
3618 | result = hlpNameTable[ftnNum]; |
3619 | #else |
3620 | #ifdef _DEBUG |
3621 | result = hlpFuncTable[ftnNum].name; |
3622 | #else |
3623 | result = "AnyJITHelper" ; |
3624 | #endif |
3625 | #endif |
3626 | |
3627 | EE_TO_JIT_TRANSITION_LEAF(); |
3628 | |
3629 | return result; |
3630 | } |
3631 | |
3632 | |
3633 | /*********************************************************************/ |
3634 | int CEEInfo::appendClassName(__deref_inout_ecount(*pnBufLen) WCHAR** ppBuf, |
3635 | int* pnBufLen, |
3636 | CORINFO_CLASS_HANDLE clsHnd, |
3637 | BOOL fNamespace, |
3638 | BOOL fFullInst, |
3639 | BOOL fAssembly) |
3640 | { |
3641 | CONTRACTL { |
3642 | SO_TOLERANT; |
3643 | MODE_PREEMPTIVE; |
3644 | THROWS; |
3645 | GC_TRIGGERS; |
3646 | } CONTRACTL_END; |
3647 | |
3648 | int nLen = 0; |
3649 | |
3650 | JIT_TO_EE_TRANSITION(); |
3651 | |
3652 | TypeHandle th(clsHnd); |
3653 | StackSString ss; |
3654 | TypeString::AppendType(ss,th, |
3655 | (fNamespace ? TypeString::FormatNamespace : 0) | |
3656 | (fFullInst ? TypeString::FormatFullInst : 0) | |
3657 | (fAssembly ? TypeString::FormatAssembly : 0)); |
3658 | const WCHAR* szString = ss.GetUnicode(); |
3659 | nLen = (int)wcslen(szString); |
3660 | if (*pnBufLen > 0) |
3661 | { |
3662 | wcscpy_s(*ppBuf, *pnBufLen, szString ); |
3663 | (*ppBuf)[(*pnBufLen) - 1] = W('\0'); |
3664 | (*ppBuf) += nLen; |
3665 | (*pnBufLen) -= nLen; |
3666 | } |
3667 | |
3668 | EE_TO_JIT_TRANSITION(); |
3669 | |
3670 | return nLen; |
3671 | } |
3672 | |
3673 | /*********************************************************************/ |
3674 | CORINFO_MODULE_HANDLE CEEInfo::getClassModule(CORINFO_CLASS_HANDLE clsHnd) |
3675 | { |
3676 | CONTRACTL { |
3677 | SO_TOLERANT; |
3678 | NOTHROW; |
3679 | GC_NOTRIGGER; |
3680 | MODE_PREEMPTIVE; |
3681 | } CONTRACTL_END; |
3682 | |
3683 | CORINFO_MODULE_HANDLE result = NULL; |
3684 | |
3685 | JIT_TO_EE_TRANSITION_LEAF(); |
3686 | |
3687 | TypeHandle VMClsHnd(clsHnd); |
3688 | |
3689 | result = CORINFO_MODULE_HANDLE(VMClsHnd.GetModule()); |
3690 | |
3691 | EE_TO_JIT_TRANSITION_LEAF(); |
3692 | |
3693 | return result; |
3694 | } |
3695 | |
3696 | /*********************************************************************/ |
3697 | CORINFO_ASSEMBLY_HANDLE CEEInfo::getModuleAssembly(CORINFO_MODULE_HANDLE modHnd) |
3698 | { |
3699 | CONTRACTL { |
3700 | SO_TOLERANT; |
3701 | NOTHROW; |
3702 | GC_NOTRIGGER; |
3703 | MODE_PREEMPTIVE; |
3704 | } CONTRACTL_END; |
3705 | |
3706 | CORINFO_ASSEMBLY_HANDLE result = NULL; |
3707 | |
3708 | JIT_TO_EE_TRANSITION_LEAF(); |
3709 | |
3710 | result = CORINFO_ASSEMBLY_HANDLE(GetModule(modHnd)->GetAssembly()); |
3711 | |
3712 | EE_TO_JIT_TRANSITION_LEAF(); |
3713 | |
3714 | return result; |
3715 | } |
3716 | |
3717 | /*********************************************************************/ |
3718 | const char* CEEInfo::getAssemblyName(CORINFO_ASSEMBLY_HANDLE asmHnd) |
3719 | { |
3720 | CONTRACTL { |
3721 | SO_TOLERANT; |
3722 | THROWS; |
3723 | GC_TRIGGERS; |
3724 | MODE_PREEMPTIVE; |
3725 | } CONTRACTL_END; |
3726 | |
3727 | const char* result = NULL; |
3728 | |
3729 | JIT_TO_EE_TRANSITION(); |
3730 | result = ((Assembly*)asmHnd)->GetSimpleName(); |
3731 | EE_TO_JIT_TRANSITION(); |
3732 | |
3733 | return result; |
3734 | } |
3735 | |
3736 | /*********************************************************************/ |
3737 | void* CEEInfo::LongLifetimeMalloc(size_t sz) |
3738 | { |
3739 | CONTRACTL { |
3740 | SO_TOLERANT; |
3741 | NOTHROW; |
3742 | GC_NOTRIGGER; |
3743 | MODE_PREEMPTIVE; |
3744 | } CONTRACTL_END; |
3745 | |
3746 | void* result = NULL; |
3747 | |
3748 | JIT_TO_EE_TRANSITION_LEAF(); |
3749 | result = new (nothrow) char[sz]; |
3750 | EE_TO_JIT_TRANSITION_LEAF(); |
3751 | |
3752 | return result; |
3753 | } |
3754 | |
3755 | /*********************************************************************/ |
3756 | void CEEInfo::LongLifetimeFree(void* obj) |
3757 | { |
3758 | CONTRACTL { |
3759 | SO_TOLERANT; |
3760 | NOTHROW; |
3761 | GC_NOTRIGGER; |
3762 | MODE_PREEMPTIVE; |
3763 | } CONTRACTL_END; |
3764 | |
3765 | JIT_TO_EE_TRANSITION_LEAF(); |
3766 | (operator delete)(obj); |
3767 | EE_TO_JIT_TRANSITION_LEAF(); |
3768 | } |
3769 | |
3770 | /*********************************************************************/ |
3771 | size_t CEEInfo::getClassModuleIdForStatics(CORINFO_CLASS_HANDLE clsHnd, CORINFO_MODULE_HANDLE *pModuleHandle, void **ppIndirection) |
3772 | { |
3773 | CONTRACTL { |
3774 | SO_TOLERANT; |
3775 | NOTHROW; |
3776 | GC_NOTRIGGER; |
3777 | MODE_PREEMPTIVE; |
3778 | } CONTRACTL_END; |
3779 | |
3780 | size_t result = 0; |
3781 | |
3782 | JIT_TO_EE_TRANSITION_LEAF(); |
3783 | |
3784 | TypeHandle VMClsHnd(clsHnd); |
3785 | Module *pModule = VMClsHnd.AsMethodTable()->GetModuleForStatics(); |
3786 | |
3787 | if (ppIndirection != NULL) |
3788 | *ppIndirection = NULL; |
3789 | |
3790 | // The zapper needs the module handle. The jit should not use it at all. |
3791 | if (pModuleHandle) |
3792 | *pModuleHandle = CORINFO_MODULE_HANDLE(pModule); |
3793 | |
3794 | result = pModule->GetModuleID(); |
3795 | |
3796 | _ASSERTE(result); |
3797 | |
3798 | EE_TO_JIT_TRANSITION_LEAF(); |
3799 | |
3800 | return result; |
3801 | } |
3802 | |
3803 | /*********************************************************************/ |
3804 | BOOL CEEInfo::isValueClass(CORINFO_CLASS_HANDLE clsHnd) |
3805 | { |
3806 | CONTRACTL { |
3807 | SO_TOLERANT; |
3808 | NOTHROW; |
3809 | GC_NOTRIGGER; |
3810 | MODE_PREEMPTIVE; |
3811 | } CONTRACTL_END; |
3812 | |
3813 | BOOL ret = FALSE; |
3814 | |
3815 | JIT_TO_EE_TRANSITION_LEAF(); |
3816 | |
3817 | _ASSERTE(clsHnd); |
3818 | |
3819 | // Note that clsHnd.IsValueType() would not return what the JIT expects |
3820 | // for corner cases like ELEMENT_TYPE_FNPTR |
3821 | TypeHandle VMClsHnd(clsHnd); |
3822 | MethodTable * pMT = VMClsHnd.GetMethodTable(); |
3823 | ret = (pMT != NULL) ? pMT->IsValueType() : 0; |
3824 | |
3825 | EE_TO_JIT_TRANSITION_LEAF(); |
3826 | |
3827 | return ret; |
3828 | } |
3829 | |
3830 | /*********************************************************************/ |
3831 | // Decides how the JIT should do the optimization to inline the check for |
3832 | // GetTypeFromHandle(handle) == obj.GetType() (for CORINFO_INLINE_TYPECHECK_SOURCE_VTABLE) |
3833 | // GetTypeFromHandle(X) == GetTypeFromHandle(Y) (for CORINFO_INLINE_TYPECHECK_SOURCE_TOKEN) |
3834 | // |
3835 | // This will enable to use directly the typehandle instead of going through getClassByHandle |
3836 | CorInfoInlineTypeCheck CEEInfo::canInlineTypeCheck(CORINFO_CLASS_HANDLE clsHnd, CorInfoInlineTypeCheckSource source) |
3837 | { |
3838 | CONTRACTL { |
3839 | SO_TOLERANT; |
3840 | NOTHROW; |
3841 | GC_NOTRIGGER; |
3842 | MODE_PREEMPTIVE; |
3843 | } CONTRACTL_END; |
3844 | |
3845 | CorInfoInlineTypeCheck ret; |
3846 | |
3847 | JIT_TO_EE_TRANSITION_LEAF(); |
3848 | |
3849 | if (source == CORINFO_INLINE_TYPECHECK_SOURCE_TOKEN) |
3850 | { |
3851 | // It's always okay to compare type handles coming from IL tokens |
3852 | ret = CORINFO_INLINE_TYPECHECK_PASS; |
3853 | } |
3854 | else |
3855 | { |
3856 | _ASSERTE(source == CORINFO_INLINE_TYPECHECK_SOURCE_VTABLE); |
3857 | ret = canInlineTypeCheckWithObjectVTable(clsHnd) ? |
3858 | CORINFO_INLINE_TYPECHECK_PASS : CORINFO_INLINE_TYPECHECK_NONE; |
3859 | } |
3860 | |
3861 | EE_TO_JIT_TRANSITION_LEAF(); |
3862 | |
3863 | return(ret); |
3864 | } |
3865 | |
3866 | /*********************************************************************/ |
3867 | // If this method returns true, JIT will do optimization to inline the check for |
3868 | // GetTypeFromHandle(handle) == obj.GetType() |
3869 | // |
3870 | // This will enable to use directly the typehandle instead of going through getClassByHandle |
3871 | BOOL CEEInfo::canInlineTypeCheckWithObjectVTable (CORINFO_CLASS_HANDLE clsHnd) |
3872 | { |
3873 | CONTRACTL { |
3874 | SO_TOLERANT; |
3875 | NOTHROW; |
3876 | GC_NOTRIGGER; |
3877 | MODE_PREEMPTIVE; |
3878 | } CONTRACTL_END; |
3879 | |
3880 | BOOL ret = FALSE; |
3881 | |
3882 | JIT_TO_EE_TRANSITION_LEAF(); |
3883 | |
3884 | _ASSERTE(clsHnd); |
3885 | |
3886 | TypeHandle VMClsHnd(clsHnd); |
3887 | |
3888 | if (VMClsHnd.IsTypeDesc()) |
3889 | { |
3890 | // We can't do this optimization for arrays because of the object methodtable is template methodtable |
3891 | ret = FALSE; |
3892 | } |
3893 | else |
3894 | if (VMClsHnd.AsMethodTable()->IsMarshaledByRef()) |
3895 | { |
3896 | // We can't do this optimization for marshalbyrefs because of the object methodtable can be transparent proxy |
3897 | ret = FALSE; |
3898 | } |
3899 | else |
3900 | if (VMClsHnd.AsMethodTable()->IsInterface()) |
3901 | { |
3902 | // Object.GetType() should not ever return interface. However, WCF custom remoting proxy does it. Disable this |
3903 | // optimization for interfaces so that (autogenerated) code that compares Object.GetType() with interface type works |
3904 | // as expected for WCF custom remoting proxy. Note that this optimization is still not going to work well for custom |
3905 | // remoting proxies that are even more broken than the WCF one, e.g. returning random non-marshalbyref types |
3906 | // from Object.GetType(). |
3907 | ret = FALSE; |
3908 | } |
3909 | else |
3910 | if (VMClsHnd == TypeHandle(g_pCanonMethodTableClass)) |
3911 | { |
3912 | // We can't do this optimization in shared generics code because of we do not know what the actual type is going to be. |
3913 | // (It can be array, marshalbyref, etc.) |
3914 | ret = FALSE; |
3915 | } |
3916 | else |
3917 | { |
3918 | // It is safe to perform this optimization |
3919 | ret = TRUE; |
3920 | } |
3921 | |
3922 | EE_TO_JIT_TRANSITION_LEAF(); |
3923 | |
3924 | return(ret); |
3925 | } |
3926 | |
3927 | /*********************************************************************/ |
3928 | DWORD CEEInfo::getClassAttribs (CORINFO_CLASS_HANDLE clsHnd) |
3929 | { |
3930 | CONTRACTL { |
3931 | SO_TOLERANT; |
3932 | THROWS; |
3933 | GC_TRIGGERS; |
3934 | MODE_PREEMPTIVE; |
3935 | } CONTRACTL_END; |
3936 | |
3937 | // <REVISIT_TODO>@todo FIX need to really fetch the class atributes. at present |
3938 | // we don't need to because the JIT only cares in the case of COM classes</REVISIT_TODO> |
3939 | DWORD ret = 0; |
3940 | |
3941 | JIT_TO_EE_TRANSITION(); |
3942 | |
3943 | ret = getClassAttribsInternal(clsHnd); |
3944 | |
3945 | EE_TO_JIT_TRANSITION(); |
3946 | |
3947 | return ret; |
3948 | } |
3949 | |
3950 | |
3951 | /*********************************************************************/ |
3952 | BOOL CEEInfo::isStructRequiringStackAllocRetBuf(CORINFO_CLASS_HANDLE clsHnd) |
3953 | { |
3954 | CONTRACTL { |
3955 | SO_TOLERANT; |
3956 | THROWS; |
3957 | GC_TRIGGERS; |
3958 | MODE_PREEMPTIVE; |
3959 | } CONTRACTL_END; |
3960 | |
3961 | BOOL ret = 0; |
3962 | |
3963 | JIT_TO_EE_TRANSITION_LEAF(); |
3964 | |
3965 | TypeHandle VMClsHnd(clsHnd); |
3966 | MethodTable * pMT = VMClsHnd.GetMethodTable(); |
3967 | ret = (pMT != NULL && pMT->IsStructRequiringStackAllocRetBuf()); |
3968 | |
3969 | EE_TO_JIT_TRANSITION_LEAF(); |
3970 | |
3971 | return ret; |
3972 | } |
3973 | |
3974 | /*********************************************************************/ |
3975 | DWORD CEEInfo::getClassAttribsInternal (CORINFO_CLASS_HANDLE clsHnd) |
3976 | { |
3977 | STANDARD_VM_CONTRACT; |
3978 | |
3979 | DWORD ret = 0; |
3980 | |
3981 | _ASSERTE(clsHnd); |
3982 | |
3983 | TypeHandle VMClsHnd(clsHnd); |
3984 | |
3985 | // Byrefs should only occur in method and local signatures, which are accessed |
3986 | // using ICorClassInfo and ICorClassInfo.getChildType. |
3987 | // So getClassAttribs() should not be called for byrefs |
3988 | |
3989 | if (VMClsHnd.IsByRef()) |
3990 | { |
3991 | _ASSERTE(!"Did findClass() return a Byref?" ); |
3992 | COMPlusThrowHR(COR_E_BADIMAGEFORMAT); |
3993 | } |
3994 | else if (VMClsHnd.IsGenericVariable()) |
3995 | { |
3996 | //@GENERICSVER: for now, type variables simply report "variable". |
3997 | ret |= CORINFO_FLG_GENERIC_TYPE_VARIABLE; |
3998 | } |
3999 | else |
4000 | { |
4001 | MethodTable *pMT = VMClsHnd.GetMethodTable(); |
4002 | |
4003 | if (!pMT) |
4004 | { |
4005 | _ASSERTE(!"Did findClass() return a Byref?" ); |
4006 | COMPlusThrowHR(COR_E_BADIMAGEFORMAT); |
4007 | } |
4008 | |
4009 | EEClass * pClass = pMT->GetClass(); |
4010 | |
4011 | // The array flag is used to identify the faked-up methods on |
4012 | // array types, i.e. .ctor, Get, Set and Address |
4013 | if (pMT->IsArray()) |
4014 | ret |= CORINFO_FLG_ARRAY; |
4015 | |
4016 | if (pMT->IsInterface()) |
4017 | ret |= CORINFO_FLG_INTERFACE; |
4018 | |
4019 | if (pMT->HasComponentSize()) |
4020 | ret |= CORINFO_FLG_VAROBJSIZE; |
4021 | |
4022 | if (pMT->IsValueType()) |
4023 | { |
4024 | ret |= CORINFO_FLG_VALUECLASS; |
4025 | |
4026 | if (pMT->IsByRefLike()) |
4027 | ret |= CORINFO_FLG_CONTAINS_STACK_PTR; |
4028 | |
4029 | if ((pClass->IsNotTightlyPacked() && (!pClass->IsManagedSequential() || pClass->HasExplicitSize())) || |
4030 | pMT == g_TypedReferenceMT || |
4031 | VMClsHnd.IsNativeValueType()) |
4032 | { |
4033 | ret |= CORINFO_FLG_CUSTOMLAYOUT; |
4034 | } |
4035 | |
4036 | if (pClass->IsUnsafeValueClass()) |
4037 | ret |= CORINFO_FLG_UNSAFE_VALUECLASS; |
4038 | } |
4039 | if (pClass->HasExplicitFieldOffsetLayout() && pClass->HasOverLayedField()) |
4040 | ret |= CORINFO_FLG_OVERLAPPING_FIELDS; |
4041 | if (VMClsHnd.IsCanonicalSubtype()) |
4042 | ret |= CORINFO_FLG_SHAREDINST; |
4043 | |
4044 | if (pMT->HasVariance()) |
4045 | ret |= CORINFO_FLG_VARIANCE; |
4046 | |
4047 | if (pMT->IsContextful()) |
4048 | ret |= CORINFO_FLG_CONTEXTFUL; |
4049 | |
4050 | if (pMT->IsMarshaledByRef()) |
4051 | ret |= CORINFO_FLG_MARSHAL_BYREF; |
4052 | |
4053 | if (pMT->ContainsPointers() || pMT == g_TypedReferenceMT) |
4054 | ret |= CORINFO_FLG_CONTAINS_GC_PTR; |
4055 | |
4056 | if (pMT->IsDelegate()) |
4057 | ret |= CORINFO_FLG_DELEGATE; |
4058 | |
4059 | if (pClass->IsBeforeFieldInit()) |
4060 | { |
4061 | if (IsReadyToRunCompilation() && !pMT->GetModule()->IsInCurrentVersionBubble()) |
4062 | { |
4063 | // For version resiliency do not allow hoisting static constructors out of loops |
4064 | } |
4065 | else |
4066 | { |
4067 | ret |= CORINFO_FLG_BEFOREFIELDINIT; |
4068 | } |
4069 | } |
4070 | |
4071 | if (pClass->IsAbstract()) |
4072 | ret |= CORINFO_FLG_ABSTRACT; |
4073 | |
4074 | if (pClass->IsSealed()) |
4075 | ret |= CORINFO_FLG_FINAL; |
4076 | |
4077 | if (pMT->IsIntrinsicType()) |
4078 | ret |= CORINFO_FLG_INTRINSIC_TYPE; |
4079 | } |
4080 | |
4081 | return ret; |
4082 | } |
4083 | |
4084 | /*********************************************************************/ |
4085 | // |
4086 | // See code:CorInfoFlag#ClassConstructionFlags for details. |
4087 | // |
4088 | CorInfoInitClassResult CEEInfo::initClass( |
4089 | CORINFO_FIELD_HANDLE field, |
4090 | CORINFO_METHOD_HANDLE method, |
4091 | CORINFO_CONTEXT_HANDLE context, |
4092 | BOOL speculative) |
4093 | { |
4094 | CONTRACTL { |
4095 | SO_TOLERANT; |
4096 | THROWS; |
4097 | GC_TRIGGERS; |
4098 | MODE_PREEMPTIVE; |
4099 | } CONTRACTL_END; |
4100 | |
4101 | DWORD result = CORINFO_INITCLASS_NOT_REQUIRED; |
4102 | |
4103 | JIT_TO_EE_TRANSITION(); |
4104 | { |
4105 | |
4106 | // Do not bother figuring out the initialization if we are only verifying the method. |
4107 | if (isVerifyOnly()) |
4108 | { |
4109 | result = CORINFO_INITCLASS_NOT_REQUIRED; |
4110 | goto exit; |
4111 | } |
4112 | |
4113 | FieldDesc * pFD = (FieldDesc *)field; |
4114 | _ASSERTE(pFD == NULL || pFD->IsStatic()); |
4115 | |
4116 | MethodDesc * pMD = (MethodDesc *)method; |
4117 | |
4118 | TypeHandle typeToInitTH = (pFD != NULL) ? pFD->GetEnclosingMethodTable() : GetTypeFromContext(context); |
4119 | |
4120 | MethodDesc *methodBeingCompiled = m_pMethodBeingCompiled; |
4121 | |
4122 | BOOL fMethodZappedOrNGen = methodBeingCompiled->IsZapped() || IsCompilingForNGen(); |
4123 | |
4124 | MethodTable *pTypeToInitMT = typeToInitTH.AsMethodTable(); |
4125 | |
4126 | // This should be the most common early-out case. |
4127 | if (fMethodZappedOrNGen) |
4128 | { |
4129 | if (pTypeToInitMT->IsClassPreInited()) |
4130 | { |
4131 | result = CORINFO_INITCLASS_NOT_REQUIRED; |
4132 | goto exit; |
4133 | } |
4134 | } |
4135 | else |
4136 | { |
4137 | #ifdef CROSSGEN_COMPILE |
4138 | _ASSERTE(FALSE); |
4139 | #else // CROSSGEN_COMPILE |
4140 | if (pTypeToInitMT->IsClassInited()) |
4141 | { |
4142 | // If the type is initialized there really is nothing to do. |
4143 | result = CORINFO_INITCLASS_INITIALIZED; |
4144 | goto exit; |
4145 | } |
4146 | #endif // CROSSGEN_COMPILE |
4147 | } |
4148 | |
4149 | if (pTypeToInitMT->IsGlobalClass()) |
4150 | { |
4151 | // For both jitted and ngen code the global class is always considered initialized |
4152 | result = CORINFO_INITCLASS_NOT_REQUIRED; |
4153 | goto exit; |
4154 | } |
4155 | |
4156 | bool fIgnoreBeforeFieldInit = false; |
4157 | |
4158 | if (pFD == NULL) |
4159 | { |
4160 | if (!fIgnoreBeforeFieldInit && pTypeToInitMT->GetClass()->IsBeforeFieldInit()) |
4161 | { |
4162 | // We can wait for field accesses to run .cctor |
4163 | result = CORINFO_INITCLASS_NOT_REQUIRED; |
4164 | goto exit; |
4165 | } |
4166 | |
4167 | // Run .cctor on statics & constructors |
4168 | if (pMD->IsStatic()) |
4169 | { |
4170 | // Except don't class construct on .cctor - it would be circular |
4171 | if (pMD->IsClassConstructor()) |
4172 | { |
4173 | result = CORINFO_INITCLASS_NOT_REQUIRED; |
4174 | goto exit; |
4175 | } |
4176 | } |
4177 | else |
4178 | // According to the spec, we should be able to do this optimization for both reference and valuetypes. |
4179 | // To maintain backward compatibility, we are doing it for reference types only. |
4180 | // We don't do this for interfaces though, as those don't have instance constructors. |
4181 | if (!pMD->IsCtor() && !pTypeToInitMT->IsValueType() && !pTypeToInitMT->IsInterface()) |
4182 | { |
4183 | // For instance methods of types with precise-initialization |
4184 | // semantics, we can assume that the .ctor triggerred the |
4185 | // type initialization. |
4186 | // This does not hold for NULL "this" object. However, the spec does |
4187 | // not require that case to work. |
4188 | result = CORINFO_INITCLASS_NOT_REQUIRED; |
4189 | goto exit; |
4190 | } |
4191 | } |
4192 | |
4193 | if (pTypeToInitMT->IsSharedByGenericInstantiations()) |
4194 | { |
4195 | // Shared generic code has to use helper. Moreover, tell JIT not to inline since |
4196 | // inlining of generic dictionary lookups is not supported. |
4197 | result = CORINFO_INITCLASS_USE_HELPER | CORINFO_INITCLASS_DONT_INLINE; |
4198 | goto exit; |
4199 | } |
4200 | |
4201 | // |
4202 | // Try to prove that the initialization is not necessary because of nesting |
4203 | // |
4204 | |
4205 | if (pFD == NULL) |
4206 | { |
4207 | // Handled above |
4208 | _ASSERTE(fIgnoreBeforeFieldInit || !pTypeToInitMT->GetClass()->IsBeforeFieldInit()); |
4209 | |
4210 | // Note that jit has both methods the same if asking whether to emit cctor |
4211 | // for a given method's code (as opposed to inlining codegen). |
4212 | if (context != MAKE_METHODCONTEXT(methodBeingCompiled) && pTypeToInitMT == methodBeingCompiled->GetMethodTable()) |
4213 | { |
4214 | // If we're inling a call to a method in our own type, then we should already |
4215 | // have triggered the .cctor when caller was itself called. |
4216 | result = CORINFO_INITCLASS_NOT_REQUIRED; |
4217 | goto exit; |
4218 | } |
4219 | } |
4220 | else |
4221 | { |
4222 | // This optimization may cause static fields in reference types to be accessed without cctor being triggered |
4223 | // for NULL "this" object. It does not conform with what the spec says. However, we have been historically |
4224 | // doing it for perf reasons. |
4225 | if (!pTypeToInitMT->IsValueType() && !pTypeToInitMT->IsInterface() && !pTypeToInitMT->GetClass()->IsBeforeFieldInit()) |
4226 | { |
4227 | if (pTypeToInitMT == GetTypeFromContext(context).AsMethodTable() || pTypeToInitMT == methodBeingCompiled->GetMethodTable()) |
4228 | { |
4229 | // The class will be initialized by the time we access the field. |
4230 | result = CORINFO_INITCLASS_NOT_REQUIRED; |
4231 | goto exit; |
4232 | } |
4233 | } |
4234 | |
4235 | // If we are currently compiling the class constructor for this static field access then we can skip the initClass |
4236 | if (methodBeingCompiled->GetMethodTable() == pTypeToInitMT && methodBeingCompiled->IsStatic() && methodBeingCompiled->IsClassConstructor()) |
4237 | { |
4238 | // The class will be initialized by the time we access the field. |
4239 | result = CORINFO_INITCLASS_NOT_REQUIRED; |
4240 | goto exit; |
4241 | } |
4242 | } |
4243 | |
4244 | if (fMethodZappedOrNGen) |
4245 | { |
4246 | // Well, because of code sharing we can't do anything at coge generation time. |
4247 | // We have to do it at runtime. |
4248 | result = CORINFO_INITCLASS_USE_HELPER; |
4249 | goto exit; |
4250 | } |
4251 | |
4252 | #ifndef CROSSGEN_COMPILE |
4253 | // |
4254 | // Optimizations for domain specific code |
4255 | // |
4256 | |
4257 | // Allocate space for the local class if necessary, but don't trigger |
4258 | // class construction. |
4259 | DomainLocalModule *pModule = pTypeToInitMT->GetDomainLocalModule(); |
4260 | pModule->PopulateClass(pTypeToInitMT); |
4261 | |
4262 | if (pTypeToInitMT->IsClassInited()) |
4263 | { |
4264 | result = CORINFO_INITCLASS_INITIALIZED; |
4265 | goto exit; |
4266 | } |
4267 | #endif // CROSSGEN_COMPILE |
4268 | |
4269 | result = CORINFO_INITCLASS_USE_HELPER; |
4270 | } |
4271 | exit: ; |
4272 | EE_TO_JIT_TRANSITION(); |
4273 | |
4274 | return (CorInfoInitClassResult)result; |
4275 | } |
4276 | |
4277 | |
4278 | |
4279 | void CEEInfo::classMustBeLoadedBeforeCodeIsRun (CORINFO_CLASS_HANDLE typeToLoadHnd) |
4280 | { |
4281 | CONTRACTL { |
4282 | SO_TOLERANT; |
4283 | NOTHROW; |
4284 | GC_NOTRIGGER; |
4285 | MODE_PREEMPTIVE; |
4286 | } CONTRACTL_END; |
4287 | |
4288 | JIT_TO_EE_TRANSITION_LEAF(); |
4289 | |
4290 | TypeHandle th = TypeHandle(typeToLoadHnd); |
4291 | |
4292 | // Type handles returned to JIT at runtime are always fully loaded. Verify that it is the case. |
4293 | _ASSERTE(th.IsFullyLoaded()); |
4294 | |
4295 | EE_TO_JIT_TRANSITION_LEAF(); |
4296 | } |
4297 | |
4298 | /*********************************************************************/ |
4299 | void CEEInfo::methodMustBeLoadedBeforeCodeIsRun (CORINFO_METHOD_HANDLE methHnd) |
4300 | { |
4301 | CONTRACTL { |
4302 | SO_TOLERANT; |
4303 | NOTHROW; |
4304 | GC_NOTRIGGER; |
4305 | MODE_PREEMPTIVE; |
4306 | } CONTRACTL_END; |
4307 | |
4308 | JIT_TO_EE_TRANSITION_LEAF(); |
4309 | |
4310 | MethodDesc *pMD = (MethodDesc*) methHnd; |
4311 | |
4312 | // MethodDescs returned to JIT at runtime are always fully loaded. Verify that it is the case. |
4313 | _ASSERTE(pMD->IsRestored() && pMD->GetMethodTable()->IsFullyLoaded()); |
4314 | |
4315 | EE_TO_JIT_TRANSITION_LEAF(); |
4316 | } |
4317 | |
4318 | /*********************************************************************/ |
4319 | CORINFO_METHOD_HANDLE CEEInfo::mapMethodDeclToMethodImpl(CORINFO_METHOD_HANDLE methHnd) |
4320 | { |
4321 | CONTRACTL { |
4322 | SO_TOLERANT; |
4323 | THROWS; |
4324 | GC_TRIGGERS; |
4325 | MODE_PREEMPTIVE; |
4326 | } CONTRACTL_END; |
4327 | |
4328 | CORINFO_METHOD_HANDLE result = NULL; |
4329 | |
4330 | JIT_TO_EE_TRANSITION(); |
4331 | |
4332 | MethodDesc *pMD = GetMethod(methHnd); |
4333 | pMD = MethodTable::MapMethodDeclToMethodImpl(pMD); |
4334 | result = (CORINFO_METHOD_HANDLE) pMD; |
4335 | |
4336 | EE_TO_JIT_TRANSITION(); |
4337 | |
4338 | return result; |
4339 | } |
4340 | |
4341 | /*********************************************************************/ |
4342 | CORINFO_CLASS_HANDLE CEEInfo::getBuiltinClass(CorInfoClassId classId) |
4343 | { |
4344 | CONTRACTL { |
4345 | SO_TOLERANT; |
4346 | THROWS; |
4347 | GC_TRIGGERS; |
4348 | MODE_PREEMPTIVE; |
4349 | } CONTRACTL_END; |
4350 | |
4351 | CORINFO_CLASS_HANDLE result = (CORINFO_CLASS_HANDLE) 0; |
4352 | |
4353 | JIT_TO_EE_TRANSITION(); |
4354 | |
4355 | switch (classId) |
4356 | { |
4357 | case CLASSID_SYSTEM_OBJECT: |
4358 | result = CORINFO_CLASS_HANDLE(g_pObjectClass); |
4359 | break; |
4360 | case CLASSID_TYPED_BYREF: |
4361 | result = CORINFO_CLASS_HANDLE(g_TypedReferenceMT); |
4362 | break; |
4363 | case CLASSID_TYPE_HANDLE: |
4364 | result = CORINFO_CLASS_HANDLE(MscorlibBinder::GetClass(CLASS__TYPE_HANDLE)); |
4365 | break; |
4366 | case CLASSID_FIELD_HANDLE: |
4367 | result = CORINFO_CLASS_HANDLE(MscorlibBinder::GetClass(CLASS__FIELD_HANDLE)); |
4368 | break; |
4369 | case CLASSID_METHOD_HANDLE: |
4370 | result = CORINFO_CLASS_HANDLE(MscorlibBinder::GetClass(CLASS__METHOD_HANDLE)); |
4371 | break; |
4372 | case CLASSID_ARGUMENT_HANDLE: |
4373 | result = CORINFO_CLASS_HANDLE(MscorlibBinder::GetClass(CLASS__ARGUMENT_HANDLE)); |
4374 | break; |
4375 | case CLASSID_STRING: |
4376 | result = CORINFO_CLASS_HANDLE(g_pStringClass); |
4377 | break; |
4378 | case CLASSID_RUNTIME_TYPE: |
4379 | result = CORINFO_CLASS_HANDLE(g_pRuntimeTypeClass); |
4380 | break; |
4381 | default: |
4382 | _ASSERTE(!"NYI: unknown classId" ); |
4383 | break; |
4384 | } |
4385 | |
4386 | EE_TO_JIT_TRANSITION(); |
4387 | |
4388 | return result; |
4389 | } |
4390 | |
4391 | |
4392 | |
4393 | /*********************************************************************/ |
4394 | CorInfoType CEEInfo::getTypeForPrimitiveValueClass( |
4395 | CORINFO_CLASS_HANDLE clsHnd) |
4396 | { |
4397 | CONTRACTL { |
4398 | SO_TOLERANT; |
4399 | THROWS; |
4400 | GC_TRIGGERS; |
4401 | MODE_PREEMPTIVE; |
4402 | } CONTRACTL_END; |
4403 | |
4404 | CorInfoType result = CORINFO_TYPE_UNDEF; |
4405 | |
4406 | JIT_TO_EE_TRANSITION(); |
4407 | |
4408 | TypeHandle th(clsHnd); |
4409 | _ASSERTE (!th.IsGenericVariable()); |
4410 | |
4411 | MethodTable *pMT = th.GetMethodTable(); |
4412 | PREFIX_ASSUME(pMT != NULL); |
4413 | |
4414 | // Is it a non primitive struct such as |
4415 | // RuntimeTypeHandle, RuntimeMethodHandle, RuntimeArgHandle? |
4416 | if (pMT->IsValueType() && |
4417 | !pMT->IsTruePrimitive() && |
4418 | !pMT->IsEnum()) |
4419 | { |
4420 | // default value CORINFO_TYPE_UNDEF is what we want |
4421 | } |
4422 | else |
4423 | { |
4424 | switch (th.GetInternalCorElementType()) |
4425 | { |
4426 | case ELEMENT_TYPE_I1: |
4427 | case ELEMENT_TYPE_U1: |
4428 | case ELEMENT_TYPE_BOOLEAN: |
4429 | result = asCorInfoType(ELEMENT_TYPE_I1); |
4430 | break; |
4431 | |
4432 | case ELEMENT_TYPE_I2: |
4433 | case ELEMENT_TYPE_U2: |
4434 | case ELEMENT_TYPE_CHAR: |
4435 | result = asCorInfoType(ELEMENT_TYPE_I2); |
4436 | break; |
4437 | |
4438 | case ELEMENT_TYPE_I4: |
4439 | case ELEMENT_TYPE_U4: |
4440 | result = asCorInfoType(ELEMENT_TYPE_I4); |
4441 | break; |
4442 | |
4443 | case ELEMENT_TYPE_I8: |
4444 | case ELEMENT_TYPE_U8: |
4445 | result = asCorInfoType(ELEMENT_TYPE_I8); |
4446 | break; |
4447 | |
4448 | case ELEMENT_TYPE_I: |
4449 | case ELEMENT_TYPE_U: |
4450 | result = asCorInfoType(ELEMENT_TYPE_I); |
4451 | break; |
4452 | |
4453 | case ELEMENT_TYPE_R4: |
4454 | result = asCorInfoType(ELEMENT_TYPE_R4); |
4455 | break; |
4456 | |
4457 | case ELEMENT_TYPE_R8: |
4458 | result = asCorInfoType(ELEMENT_TYPE_R8); |
4459 | break; |
4460 | |
4461 | case ELEMENT_TYPE_VOID: |
4462 | result = asCorInfoType(ELEMENT_TYPE_VOID); |
4463 | break; |
4464 | |
4465 | case ELEMENT_TYPE_PTR: |
4466 | case ELEMENT_TYPE_FNPTR: |
4467 | result = asCorInfoType(ELEMENT_TYPE_PTR); |
4468 | break; |
4469 | |
4470 | default: |
4471 | break; |
4472 | } |
4473 | } |
4474 | |
4475 | EE_TO_JIT_TRANSITION(); |
4476 | |
4477 | return result; |
4478 | } |
4479 | |
4480 | /*********************************************************************/ |
4481 | CorInfoType CEEInfo::getTypeForPrimitiveNumericClass( |
4482 | CORINFO_CLASS_HANDLE clsHnd) |
4483 | { |
4484 | CONTRACTL{ |
4485 | SO_TOLERANT; |
4486 | NOTHROW; |
4487 | GC_NOTRIGGER; |
4488 | MODE_PREEMPTIVE; |
4489 | } CONTRACTL_END; |
4490 | |
4491 | CorInfoType result = CORINFO_TYPE_UNDEF; |
4492 | |
4493 | JIT_TO_EE_TRANSITION_LEAF(); |
4494 | |
4495 | TypeHandle th(clsHnd); |
4496 | _ASSERTE (!th.IsGenericVariable()); |
4497 | |
4498 | CorElementType ty = th.GetSignatureCorElementType(); |
4499 | switch (ty) |
4500 | { |
4501 | case ELEMENT_TYPE_I1: |
4502 | result = CORINFO_TYPE_BYTE; |
4503 | break; |
4504 | case ELEMENT_TYPE_U1: |
4505 | result = CORINFO_TYPE_UBYTE; |
4506 | break; |
4507 | case ELEMENT_TYPE_I2: |
4508 | result = CORINFO_TYPE_SHORT; |
4509 | break; |
4510 | case ELEMENT_TYPE_U2: |
4511 | result = CORINFO_TYPE_USHORT; |
4512 | break; |
4513 | case ELEMENT_TYPE_I4: |
4514 | result = CORINFO_TYPE_INT; |
4515 | break; |
4516 | case ELEMENT_TYPE_U4: |
4517 | result = CORINFO_TYPE_UINT; |
4518 | break; |
4519 | case ELEMENT_TYPE_I8: |
4520 | result = CORINFO_TYPE_LONG; |
4521 | break; |
4522 | case ELEMENT_TYPE_U8: |
4523 | result = CORINFO_TYPE_ULONG; |
4524 | break; |
4525 | case ELEMENT_TYPE_R4: |
4526 | result = CORINFO_TYPE_FLOAT; |
4527 | break; |
4528 | case ELEMENT_TYPE_R8: |
4529 | result = CORINFO_TYPE_DOUBLE; |
4530 | break; |
4531 | |
4532 | default: |
4533 | // Error case, we will return CORINFO_TYPE_UNDEF |
4534 | break; |
4535 | } |
4536 | |
4537 | JIT_TO_EE_TRANSITION_LEAF(); |
4538 | |
4539 | return result; |
4540 | } |
4541 | |
4542 | |
4543 | void CEEInfo::getGSCookie(GSCookie * pCookieVal, GSCookie ** ppCookieVal) |
4544 | { |
4545 | CONTRACTL { |
4546 | SO_TOLERANT; |
4547 | THROWS; |
4548 | GC_TRIGGERS; |
4549 | MODE_PREEMPTIVE; |
4550 | } CONTRACTL_END; |
4551 | |
4552 | JIT_TO_EE_TRANSITION(); |
4553 | |
4554 | if (pCookieVal) |
4555 | { |
4556 | *pCookieVal = GetProcessGSCookie(); |
4557 | *ppCookieVal = NULL; |
4558 | } |
4559 | else |
4560 | { |
4561 | *ppCookieVal = GetProcessGSCookiePtr(); |
4562 | } |
4563 | |
4564 | EE_TO_JIT_TRANSITION(); |
4565 | } |
4566 | |
4567 | |
4568 | /*********************************************************************/ |
4569 | // TRUE if child is a subtype of parent |
4570 | // if parent is an interface, then does child implement / extend parent |
4571 | BOOL CEEInfo::canCast( |
4572 | CORINFO_CLASS_HANDLE child, |
4573 | CORINFO_CLASS_HANDLE parent) |
4574 | { |
4575 | CONTRACTL { |
4576 | SO_TOLERANT; |
4577 | THROWS; |
4578 | GC_TRIGGERS; |
4579 | MODE_PREEMPTIVE; |
4580 | } CONTRACTL_END; |
4581 | |
4582 | BOOL result = FALSE; |
4583 | |
4584 | JIT_TO_EE_TRANSITION(); |
4585 | |
4586 | result = ((TypeHandle)child).CanCastTo((TypeHandle)parent); |
4587 | |
4588 | EE_TO_JIT_TRANSITION(); |
4589 | |
4590 | return result; |
4591 | } |
4592 | |
4593 | /*********************************************************************/ |
4594 | // TRUE if cls1 and cls2 are considered equivalent types. |
4595 | BOOL CEEInfo::areTypesEquivalent( |
4596 | CORINFO_CLASS_HANDLE cls1, |
4597 | CORINFO_CLASS_HANDLE cls2) |
4598 | { |
4599 | CONTRACTL { |
4600 | SO_TOLERANT; |
4601 | THROWS; |
4602 | GC_TRIGGERS; |
4603 | MODE_PREEMPTIVE; |
4604 | } CONTRACTL_END; |
4605 | |
4606 | BOOL result = FALSE; |
4607 | |
4608 | JIT_TO_EE_TRANSITION(); |
4609 | |
4610 | result = ((TypeHandle)cls1).IsEquivalentTo((TypeHandle)cls2); |
4611 | |
4612 | EE_TO_JIT_TRANSITION(); |
4613 | |
4614 | return result; |
4615 | } |
4616 | |
4617 | /*********************************************************************/ |
4618 | // See if a cast from fromClass to toClass will succeed, fail, or needs |
4619 | // to be resolved at runtime. |
4620 | TypeCompareState CEEInfo::compareTypesForCast( |
4621 | CORINFO_CLASS_HANDLE fromClass, |
4622 | CORINFO_CLASS_HANDLE toClass) |
4623 | { |
4624 | CONTRACTL { |
4625 | SO_TOLERANT; |
4626 | THROWS; |
4627 | GC_TRIGGERS; |
4628 | MODE_PREEMPTIVE; |
4629 | } CONTRACTL_END; |
4630 | |
4631 | TypeCompareState result = TypeCompareState::May; |
4632 | |
4633 | JIT_TO_EE_TRANSITION(); |
4634 | |
4635 | TypeHandle fromHnd = (TypeHandle) fromClass; |
4636 | TypeHandle toHnd = (TypeHandle) toClass; |
4637 | |
4638 | #ifdef FEATURE_COMINTEROP |
4639 | // If casting from a com object class, don't try to optimize. |
4640 | if (fromHnd.IsComObjectType()) |
4641 | { |
4642 | result = TypeCompareState::May; |
4643 | } |
4644 | else |
4645 | #endif // FEATURE_COMINTEROP |
4646 | |
4647 | // If casting from ICastable, don't try to optimize |
4648 | if (fromHnd.GetMethodTable()->IsICastable()) |
4649 | { |
4650 | result = TypeCompareState::May; |
4651 | } |
4652 | // If casting to Nullable<T>, don't try to optimize |
4653 | else if (Nullable::IsNullableType(toHnd)) |
4654 | { |
4655 | result = TypeCompareState::May; |
4656 | } |
4657 | // If the types are not shared, we can check directly. |
4658 | else if (!fromHnd.IsCanonicalSubtype() && !toHnd.IsCanonicalSubtype()) |
4659 | { |
4660 | result = fromHnd.CanCastTo(toHnd) ? TypeCompareState::Must : TypeCompareState::MustNot; |
4661 | } |
4662 | // Casting from a shared type to an unshared type. |
4663 | else if (fromHnd.IsCanonicalSubtype() && !toHnd.IsCanonicalSubtype()) |
4664 | { |
4665 | // Only handle casts to interface types for now |
4666 | if (toHnd.IsInterface()) |
4667 | { |
4668 | // Do a preliminary check. |
4669 | BOOL canCast = fromHnd.CanCastTo(toHnd); |
4670 | |
4671 | // Pass back positive results unfiltered. The unknown type |
4672 | // parameters in fromClass did not come into play. |
4673 | if (canCast) |
4674 | { |
4675 | result = TypeCompareState::Must; |
4676 | } |
4677 | // For negative results, the unknown type parameter in |
4678 | // fromClass might match some instantiated interface, |
4679 | // either directly or via variance. |
4680 | // |
4681 | // However, CanCastTo will report failure in such cases since |
4682 | // __Canon won't match the instantiated type on the |
4683 | // interface (which can't be __Canon since we screened out |
4684 | // canonical subtypes for toClass above). So only report |
4685 | // failure if the interface is not instantiated. |
4686 | else if (!toHnd.HasInstantiation()) |
4687 | { |
4688 | result = TypeCompareState::MustNot; |
4689 | } |
4690 | } |
4691 | } |
4692 | |
4693 | #ifdef FEATURE_READYTORUN_COMPILER |
4694 | // In R2R it is a breaking change for a previously positive |
4695 | // cast to become negative, but not for a previously negative |
4696 | // cast to become positive. So in R2R a negative result is |
4697 | // always reported back as May. |
4698 | if (IsReadyToRunCompilation() && (result == TypeCompareState::MustNot)) |
4699 | { |
4700 | result = TypeCompareState::May; |
4701 | } |
4702 | #endif // FEATURE_READYTORUN_COMPILER |
4703 | |
4704 | EE_TO_JIT_TRANSITION(); |
4705 | |
4706 | return result; |
4707 | } |
4708 | |
4709 | /*********************************************************************/ |
4710 | // See if types represented by cls1 and cls2 compare equal, not |
4711 | // equal, or the comparison needs to be resolved at runtime. |
4712 | TypeCompareState CEEInfo::compareTypesForEquality( |
4713 | CORINFO_CLASS_HANDLE cls1, |
4714 | CORINFO_CLASS_HANDLE cls2) |
4715 | { |
4716 | CONTRACTL { |
4717 | SO_TOLERANT; |
4718 | THROWS; |
4719 | GC_TRIGGERS; |
4720 | MODE_PREEMPTIVE; |
4721 | } CONTRACTL_END; |
4722 | |
4723 | TypeCompareState result = TypeCompareState::May; |
4724 | |
4725 | JIT_TO_EE_TRANSITION(); |
4726 | |
4727 | TypeHandle hnd1 = (TypeHandle) cls1; |
4728 | TypeHandle hnd2 = (TypeHandle) cls2; |
4729 | |
4730 | // If neither type is a canonical subtype, type handle comparison suffices |
4731 | if (!hnd1.IsCanonicalSubtype() && !hnd2.IsCanonicalSubtype()) |
4732 | { |
4733 | result = (hnd1 == hnd2 ? TypeCompareState::Must : TypeCompareState::MustNot); |
4734 | } |
4735 | // If either or both types are canonical subtypes, we can sometimes prove inequality. |
4736 | else |
4737 | { |
4738 | // If either is a value type then the types cannot |
4739 | // be equal unless the type defs are the same. |
4740 | if (hnd1.IsValueType() || hnd2.IsValueType()) |
4741 | { |
4742 | if (!hnd1.GetMethodTable()->HasSameTypeDefAs(hnd2.GetMethodTable())) |
4743 | { |
4744 | result = TypeCompareState::MustNot; |
4745 | } |
4746 | } |
4747 | // If we have two ref types that are not __Canon, then the |
4748 | // types cannot be equal unless the type defs are the same. |
4749 | else |
4750 | { |
4751 | TypeHandle canonHnd = TypeHandle(g_pCanonMethodTableClass); |
4752 | if ((hnd1 != canonHnd) && (hnd2 != canonHnd)) |
4753 | { |
4754 | if (!hnd1.GetMethodTable()->HasSameTypeDefAs(hnd2.GetMethodTable())) |
4755 | { |
4756 | result = TypeCompareState::MustNot; |
4757 | } |
4758 | } |
4759 | } |
4760 | } |
4761 | |
4762 | EE_TO_JIT_TRANSITION(); |
4763 | |
4764 | return result; |
4765 | } |
4766 | |
4767 | /*********************************************************************/ |
4768 | // returns is the intersection of cls1 and cls2. |
4769 | CORINFO_CLASS_HANDLE CEEInfo::mergeClasses( |
4770 | CORINFO_CLASS_HANDLE cls1, |
4771 | CORINFO_CLASS_HANDLE cls2) |
4772 | { |
4773 | CONTRACTL { |
4774 | SO_TOLERANT; |
4775 | THROWS; |
4776 | GC_TRIGGERS; |
4777 | MODE_PREEMPTIVE; |
4778 | } CONTRACTL_END; |
4779 | |
4780 | CORINFO_CLASS_HANDLE result = NULL; |
4781 | |
4782 | JIT_TO_EE_TRANSITION(); |
4783 | |
4784 | TypeHandle merged = TypeHandle::MergeTypeHandlesToCommonParent(TypeHandle(cls1), TypeHandle(cls2)); |
4785 | #ifdef _DEBUG |
4786 | { |
4787 | //Make sure the merge is reflexive in the cases we "support". |
4788 | TypeHandle hnd1 = TypeHandle(cls1); |
4789 | TypeHandle hnd2 = TypeHandle(cls2); |
4790 | TypeHandle reflexive = TypeHandle::MergeTypeHandlesToCommonParent(hnd2, hnd1); |
4791 | |
4792 | //If both sides are classes than either they have a common non-interface parent (in which case it is |
4793 | //reflexive) |
4794 | //OR they share a common interface, and it can be order dependent (if they share multiple interfaces |
4795 | //in common) |
4796 | if (!hnd1.IsInterface() && !hnd2.IsInterface()) |
4797 | { |
4798 | if (merged.IsInterface()) |
4799 | { |
4800 | _ASSERTE(reflexive.IsInterface()); |
4801 | } |
4802 | else |
4803 | { |
4804 | _ASSERTE(merged == reflexive); |
4805 | } |
4806 | } |
4807 | //Both results must either be interfaces or classes. They cannot be mixed. |
4808 | _ASSERTE((!!merged.IsInterface()) == (!!reflexive.IsInterface())); |
4809 | |
4810 | //If the result of the merge was a class, then the result of the reflexive merge was the same class. |
4811 | if (!merged.IsInterface()) |
4812 | { |
4813 | _ASSERTE(merged == reflexive); |
4814 | } |
4815 | |
4816 | //If both sides are arrays, then the result is either an array or g_pArrayClass. The above is |
4817 | //actually true about the element type for references types, but I think that that is a little |
4818 | //excessive for sanity. |
4819 | if (hnd1.IsArray() && hnd2.IsArray()) |
4820 | { |
4821 | _ASSERTE((merged.IsArray() && reflexive.IsArray()) |
4822 | || ((merged == g_pArrayClass) && (reflexive == g_pArrayClass))); |
4823 | } |
4824 | |
4825 | //Can I assert anything about generic variables? |
4826 | |
4827 | //The results must always be assignable |
4828 | _ASSERTE(hnd1.CanCastTo(merged) && hnd2.CanCastTo(merged) && hnd1.CanCastTo(reflexive) |
4829 | && hnd2.CanCastTo(reflexive)); |
4830 | } |
4831 | #endif |
4832 | result = CORINFO_CLASS_HANDLE(merged.AsPtr()); |
4833 | |
4834 | EE_TO_JIT_TRANSITION(); |
4835 | return result; |
4836 | } |
4837 | |
4838 | /*********************************************************************/ |
4839 | // Given a class handle, returns the Parent type. |
4840 | // For COMObjectType, it returns Class Handle of System.Object. |
4841 | // Returns 0 if System.Object is passed in. |
4842 | CORINFO_CLASS_HANDLE CEEInfo::getParentType( |
4843 | CORINFO_CLASS_HANDLE cls) |
4844 | { |
4845 | CONTRACTL { |
4846 | SO_TOLERANT; |
4847 | THROWS; |
4848 | GC_TRIGGERS; |
4849 | MODE_PREEMPTIVE; |
4850 | } CONTRACTL_END; |
4851 | |
4852 | CORINFO_CLASS_HANDLE result = NULL; |
4853 | |
4854 | JIT_TO_EE_TRANSITION(); |
4855 | |
4856 | TypeHandle th(cls); |
4857 | |
4858 | _ASSERTE(!th.IsNull()); |
4859 | _ASSERTE(!th.IsGenericVariable()); |
4860 | |
4861 | TypeHandle thParent = th.GetParent(); |
4862 | |
4863 | #ifdef FEATURE_COMINTEROP |
4864 | // If we encounter __ComObject in the hierarchy, we need to skip it |
4865 | // since this hierarchy is introduced by the EE, but won't be present |
4866 | // in the metadata. |
4867 | if (!thParent.IsNull() && IsComObjectClass(thParent)) |
4868 | { |
4869 | result = (CORINFO_CLASS_HANDLE) g_pObjectClass; |
4870 | } |
4871 | else |
4872 | #endif // FEATURE_COMINTEROP |
4873 | { |
4874 | result = CORINFO_CLASS_HANDLE(thParent.AsPtr()); |
4875 | } |
4876 | |
4877 | EE_TO_JIT_TRANSITION(); |
4878 | |
4879 | return result; |
4880 | } |
4881 | |
4882 | |
4883 | /*********************************************************************/ |
4884 | // Returns the CorInfoType of the "child type". If the child type is |
4885 | // not a primitive type, *clsRet will be set. |
4886 | // Given an Array of Type Foo, returns Foo. |
4887 | // Given BYREF Foo, returns Foo |
4888 | CorInfoType CEEInfo::getChildType ( |
4889 | CORINFO_CLASS_HANDLE clsHnd, |
4890 | CORINFO_CLASS_HANDLE *clsRet |
4891 | ) |
4892 | { |
4893 | CONTRACTL { |
4894 | SO_TOLERANT; |
4895 | THROWS; |
4896 | GC_TRIGGERS; |
4897 | MODE_PREEMPTIVE; |
4898 | } CONTRACTL_END; |
4899 | |
4900 | CorInfoType ret = CORINFO_TYPE_UNDEF; |
4901 | *clsRet = 0; |
4902 | TypeHandle retType = TypeHandle(); |
4903 | |
4904 | JIT_TO_EE_TRANSITION(); |
4905 | |
4906 | TypeHandle th(clsHnd); |
4907 | |
4908 | _ASSERTE(!th.IsNull()); |
4909 | |
4910 | // BYREF, ARRAY types |
4911 | if (th.IsTypeDesc()) |
4912 | { |
4913 | retType = th.AsTypeDesc()->GetTypeParam(); |
4914 | } |
4915 | else |
4916 | { |
4917 | // <REVISIT_TODO> we really should not have this case. arrays type handles |
4918 | // used in the JIT interface should never be ordinary method tables, |
4919 | // indeed array type handles should really never be ordinary MTs |
4920 | // at all. Perhaps we should assert !th.IsTypeDesc() && th.AsMethodTable().IsArray()? </REVISIT_TODO> |
4921 | MethodTable* pMT= th.AsMethodTable(); |
4922 | if (pMT->IsArray()) |
4923 | retType = pMT->GetApproxArrayElementTypeHandle(); |
4924 | } |
4925 | |
4926 | if (!retType.IsNull()) { |
4927 | CorElementType type = retType.GetInternalCorElementType(); |
4928 | ret = CEEInfo::asCorInfoType(type,retType, clsRet); |
4929 | |
4930 | // <REVISIT_TODO>What if this one is a value array ?</REVISIT_TODO> |
4931 | } |
4932 | |
4933 | EE_TO_JIT_TRANSITION(); |
4934 | |
4935 | return ret; |
4936 | } |
4937 | |
4938 | /*********************************************************************/ |
4939 | // Check any constraints on class type arguments |
4940 | BOOL CEEInfo::satisfiesClassConstraints(CORINFO_CLASS_HANDLE cls) |
4941 | { |
4942 | CONTRACTL { |
4943 | SO_TOLERANT; |
4944 | THROWS; |
4945 | GC_TRIGGERS; |
4946 | MODE_PREEMPTIVE; |
4947 | } CONTRACTL_END; |
4948 | |
4949 | BOOL result = FALSE; |
4950 | |
4951 | JIT_TO_EE_TRANSITION(); |
4952 | |
4953 | _ASSERTE(cls != NULL); |
4954 | result = TypeHandle(cls).SatisfiesClassConstraints(); |
4955 | |
4956 | EE_TO_JIT_TRANSITION(); |
4957 | |
4958 | return result; |
4959 | } |
4960 | |
4961 | /*********************************************************************/ |
4962 | // Check if this is a single dimensional array type |
4963 | BOOL CEEInfo::isSDArray(CORINFO_CLASS_HANDLE cls) |
4964 | { |
4965 | CONTRACTL { |
4966 | SO_TOLERANT; |
4967 | THROWS; |
4968 | GC_TRIGGERS; |
4969 | MODE_PREEMPTIVE; |
4970 | } CONTRACTL_END; |
4971 | |
4972 | BOOL result = FALSE; |
4973 | |
4974 | JIT_TO_EE_TRANSITION(); |
4975 | |
4976 | TypeHandle th(cls); |
4977 | |
4978 | _ASSERTE(!th.IsNull()); |
4979 | |
4980 | if (th.IsArrayType()) |
4981 | { |
4982 | // Lots of code used to think that System.Array's methodtable returns TRUE for IsArray(). It doesn't. |
4983 | _ASSERTE(th != TypeHandle(g_pArrayClass)); |
4984 | |
4985 | result = (th.GetInternalCorElementType() == ELEMENT_TYPE_SZARRAY); |
4986 | } |
4987 | |
4988 | EE_TO_JIT_TRANSITION(); |
4989 | |
4990 | return result; |
4991 | } |
4992 | |
4993 | /*********************************************************************/ |
4994 | // Get the number of dimensions in an array |
4995 | unsigned CEEInfo::getArrayRank(CORINFO_CLASS_HANDLE cls) |
4996 | { |
4997 | CONTRACTL { |
4998 | SO_TOLERANT; |
4999 | THROWS; |
5000 | GC_TRIGGERS; |
5001 | MODE_PREEMPTIVE; |
5002 | } CONTRACTL_END; |
5003 | |
5004 | unsigned result = 0; |
5005 | |
5006 | JIT_TO_EE_TRANSITION(); |
5007 | |
5008 | TypeHandle th(cls); |
5009 | |
5010 | _ASSERTE(!th.IsNull()); |
5011 | |
5012 | if (th.IsArrayType()) |
5013 | { |
5014 | // Lots of code used to think that System.Array's methodtable returns TRUE for IsArray(). It doesn't. |
5015 | _ASSERTE(th != TypeHandle(g_pArrayClass)); |
5016 | |
5017 | result = th.GetPossiblySharedArrayMethodTable()->GetRank(); |
5018 | } |
5019 | |
5020 | EE_TO_JIT_TRANSITION(); |
5021 | |
5022 | return result; |
5023 | } |
5024 | |
5025 | /*********************************************************************/ |
5026 | // Get static field data for an array |
5027 | // Note that it's OK to return NULL from this method. This will cause |
5028 | // the JIT to make a runtime call to InitializeArray instead of doing |
5029 | // the inline optimization (thus preserving the original behavior). |
5030 | void * CEEInfo::getArrayInitializationData( |
5031 | CORINFO_FIELD_HANDLE field, |
5032 | DWORD size |
5033 | ) |
5034 | { |
5035 | CONTRACTL { |
5036 | SO_TOLERANT; |
5037 | THROWS; |
5038 | GC_TRIGGERS; |
5039 | MODE_PREEMPTIVE; |
5040 | } CONTRACTL_END; |
5041 | |
5042 | void * result = NULL; |
5043 | |
5044 | JIT_TO_EE_TRANSITION(); |
5045 | |
5046 | FieldDesc* pField = (FieldDesc*) field; |
5047 | |
5048 | if (!pField || |
5049 | !pField->IsRVA() || |
5050 | (pField->LoadSize() < size) |
5051 | #ifdef FEATURE_NATIVE_IMAGE_GENERATION |
5052 | // This will make sure that when IBC logging is on, the array initialization happens thru |
5053 | // COMArrayInfo::InitializeArray. This gives a place to put the IBC probe that can help |
5054 | // separate hold and cold RVA blobs. |
5055 | || (IsCompilingForNGen() && |
5056 | GetAppDomain()->ToCompilationDomain()->m_fForceInstrument) |
5057 | #endif // FEATURE_NATIVE_IMAGE_GENERATION |
5058 | ) |
5059 | { |
5060 | result = NULL; |
5061 | } |
5062 | else |
5063 | { |
5064 | result = pField->GetStaticAddressHandle(NULL); |
5065 | } |
5066 | |
5067 | EE_TO_JIT_TRANSITION(); |
5068 | |
5069 | return result; |
5070 | } |
5071 | |
5072 | CorInfoIsAccessAllowedResult CEEInfo::canAccessClass( |
5073 | CORINFO_RESOLVED_TOKEN * pResolvedToken, |
5074 | CORINFO_METHOD_HANDLE callerHandle, |
5075 | CORINFO_HELPER_DESC *pAccessHelper |
5076 | ) |
5077 | { |
5078 | CONTRACTL { |
5079 | SO_TOLERANT; |
5080 | THROWS; |
5081 | GC_TRIGGERS; |
5082 | MODE_PREEMPTIVE; |
5083 | } CONTRACTL_END; |
5084 | |
5085 | CorInfoIsAccessAllowedResult isAccessAllowed = CORINFO_ACCESS_ALLOWED; |
5086 | |
5087 | JIT_TO_EE_TRANSITION(); |
5088 | |
5089 | INDEBUG(memset(pAccessHelper, 0xCC, sizeof(*pAccessHelper))); |
5090 | |
5091 | BOOL doAccessCheck = TRUE; |
5092 | AccessCheckOptions::AccessCheckType accessCheckType = AccessCheckOptions::kNormalAccessibilityChecks; |
5093 | DynamicResolver * pAccessContext = NULL; |
5094 | |
5095 | //All access checks must be done on the open instantiation. |
5096 | MethodDesc * pCallerForSecurity = GetMethodForSecurity(callerHandle); |
5097 | TypeHandle callerTypeForSecurity = TypeHandle(pCallerForSecurity->GetMethodTable()); |
5098 | |
5099 | TypeHandle pCalleeForSecurity = TypeHandle(pResolvedToken->hClass); |
5100 | if (pResolvedToken->pTypeSpec != NULL) |
5101 | { |
5102 | SigTypeContext typeContext; |
5103 | SigTypeContext::InitTypeContext(pCallerForSecurity, &typeContext); |
5104 | |
5105 | SigPointer sigptr(pResolvedToken->pTypeSpec, pResolvedToken->cbTypeSpec); |
5106 | pCalleeForSecurity = sigptr.GetTypeHandleThrowing((Module *)pResolvedToken->tokenScope, &typeContext); |
5107 | } |
5108 | |
5109 | while (pCalleeForSecurity.HasTypeParam()) |
5110 | { |
5111 | pCalleeForSecurity = pCalleeForSecurity.GetTypeParam(); |
5112 | } |
5113 | |
5114 | if (IsDynamicScope(pResolvedToken->tokenScope)) |
5115 | { |
5116 | doAccessCheck = ModifyCheckForDynamicMethod(GetDynamicResolver(pResolvedToken->tokenScope), |
5117 | &callerTypeForSecurity, &accessCheckType, |
5118 | &pAccessContext); |
5119 | } |
5120 | |
5121 | //Since this is a check against a TypeHandle, there are some things we can stick in a TypeHandle that |
5122 | //don't require access checks. |
5123 | if (pCalleeForSecurity.IsGenericVariable()) |
5124 | { |
5125 | //I don't need to check for access against !!0. |
5126 | doAccessCheck = FALSE; |
5127 | } |
5128 | |
5129 | //Now do the visibility checks |
5130 | if (doAccessCheck) |
5131 | { |
5132 | AccessCheckOptions accessCheckOptions(accessCheckType, |
5133 | pAccessContext, |
5134 | FALSE /*throw on error*/, |
5135 | pCalleeForSecurity.GetMethodTable()); |
5136 | |
5137 | _ASSERTE(pCallerForSecurity != NULL && callerTypeForSecurity != NULL); |
5138 | StaticAccessCheckContext accessContext(pCallerForSecurity, callerTypeForSecurity.GetMethodTable()); |
5139 | |
5140 | BOOL canAccessType = ClassLoader::CanAccessClass(&accessContext, |
5141 | pCalleeForSecurity.GetMethodTable(), |
5142 | pCalleeForSecurity.GetAssembly(), |
5143 | accessCheckOptions); |
5144 | |
5145 | isAccessAllowed = canAccessType ? CORINFO_ACCESS_ALLOWED : CORINFO_ACCESS_ILLEGAL; |
5146 | } |
5147 | |
5148 | |
5149 | if (isAccessAllowed != CORINFO_ACCESS_ALLOWED) |
5150 | { |
5151 | //These all get the throw helper |
5152 | pAccessHelper->helperNum = CORINFO_HELP_CLASS_ACCESS_EXCEPTION; |
5153 | pAccessHelper->numArgs = 2; |
5154 | |
5155 | pAccessHelper->args[0].Set(CORINFO_METHOD_HANDLE(pCallerForSecurity)); |
5156 | pAccessHelper->args[1].Set(CORINFO_CLASS_HANDLE(pCalleeForSecurity.AsPtr())); |
5157 | |
5158 | if (IsCompilingForNGen()) |
5159 | { |
5160 | //see code:CEEInfo::getCallInfo for more information. |
5161 | if (pCallerForSecurity->ContainsGenericVariables() || pCalleeForSecurity.ContainsGenericVariables()) |
5162 | COMPlusThrowNonLocalized(kNotSupportedException, W("Cannot embed generic TypeHandle" )); |
5163 | } |
5164 | } |
5165 | |
5166 | EE_TO_JIT_TRANSITION(); |
5167 | return isAccessAllowed; |
5168 | } |
5169 | |
5170 | /***********************************************************************/ |
5171 | // return the address of a pointer to a callable stub that will do the |
5172 | // virtual or interface call |
5173 | void CEEInfo::getCallInfo( |
5174 | CORINFO_RESOLVED_TOKEN * pResolvedToken, |
5175 | CORINFO_RESOLVED_TOKEN * pConstrainedResolvedToken, |
5176 | CORINFO_METHOD_HANDLE callerHandle, |
5177 | CORINFO_CALLINFO_FLAGS flags, |
5178 | CORINFO_CALL_INFO *pResult /*out */) |
5179 | { |
5180 | CONTRACTL { |
5181 | SO_TOLERANT; |
5182 | THROWS; |
5183 | GC_TRIGGERS; |
5184 | MODE_PREEMPTIVE; |
5185 | } CONTRACTL_END; |
5186 | |
5187 | JIT_TO_EE_TRANSITION(); |
5188 | |
5189 | _ASSERTE(CheckPointer(pResult)); |
5190 | |
5191 | INDEBUG(memset(pResult, 0xCC, sizeof(*pResult))); |
5192 | |
5193 | pResult->stubLookup.lookupKind.needsRuntimeLookup = false; |
5194 | |
5195 | MethodDesc* pMD = (MethodDesc *)pResolvedToken->hMethod; |
5196 | TypeHandle th(pResolvedToken->hClass); |
5197 | |
5198 | _ASSERTE(pMD); |
5199 | _ASSERTE((size_t(pMD) & 0x1) == 0); |
5200 | |
5201 | // Spec says that a callvirt lookup ignores static methods. Since static methods |
5202 | // can't have the exact same signature as instance methods, a lookup that found |
5203 | // a static method would have never found an instance method. |
5204 | if (pMD->IsStatic() && (flags & CORINFO_CALLINFO_CALLVIRT)) |
5205 | { |
5206 | EX_THROW(EEMessageException, (kMissingMethodException, IDS_EE_MISSING_METHOD, W("?" ))); |
5207 | } |
5208 | |
5209 | TypeHandle exactType = TypeHandle(pResolvedToken->hClass); |
5210 | |
5211 | TypeHandle constrainedType; |
5212 | if ((flags & CORINFO_CALLINFO_CALLVIRT) && (pConstrainedResolvedToken != NULL)) |
5213 | { |
5214 | constrainedType = TypeHandle(pConstrainedResolvedToken->hClass); |
5215 | } |
5216 | |
5217 | BOOL fResolvedConstraint = FALSE; |
5218 | BOOL fForceUseRuntimeLookup = FALSE; |
5219 | |
5220 | MethodDesc * pMDAfterConstraintResolution = pMD; |
5221 | if (constrainedType.IsNull()) |
5222 | { |
5223 | pResult->thisTransform = CORINFO_NO_THIS_TRANSFORM; |
5224 | } |
5225 | // <NICE> Things go wrong when this code path is used when verifying generic code. |
5226 | // It would be nice if we didn't go down this sort of code path when verifying but |
5227 | // not generating code. </NICE> |
5228 | else if (constrainedType.ContainsGenericVariables() || exactType.ContainsGenericVariables()) |
5229 | { |
5230 | // <NICE> It shouldn't really matter what we do here - but the x86 JIT is annoyingly sensitive |
5231 | // about what we do, since it pretend generic variables are reference types and generates |
5232 | // an internal JIT tree even when just verifying generic code. </NICE> |
5233 | if (constrainedType.IsGenericVariable()) |
5234 | { |
5235 | pResult->thisTransform = CORINFO_DEREF_THIS; // convert 'this' of type &T --> T |
5236 | } |
5237 | else if (constrainedType.IsValueType()) |
5238 | { |
5239 | pResult->thisTransform = CORINFO_BOX_THIS; // convert 'this' of type &VC<T> --> boxed(VC<T>) |
5240 | } |
5241 | else |
5242 | { |
5243 | pResult->thisTransform = CORINFO_DEREF_THIS; // convert 'this' of type &C<T> --> C<T> |
5244 | } |
5245 | } |
5246 | else |
5247 | { |
5248 | // We have a "constrained." call. Try a partial resolve of the constraint call. Note that this |
5249 | // will not necessarily resolve the call exactly, since we might be compiling |
5250 | // shared generic code - it may just resolve it to a candidate suitable for |
5251 | // JIT compilation, and require a runtime lookup for the actual code pointer |
5252 | // to call. |
5253 | if (constrainedType.IsEnum()) |
5254 | { |
5255 | // Optimize constrained calls to enum's GetHashCode method. TryResolveConstraintMethodApprox would return |
5256 | // null since the virtual method resolves to System.Enum's implementation and that's a reference type. |
5257 | // We can't do this for any other method since ToString and Equals have different semantics for enums |
5258 | // and their underlying type. |
5259 | if (pMD->GetSlot() == MscorlibBinder::GetMethod(METHOD__OBJECT__GET_HASH_CODE)->GetSlot()) |
5260 | { |
5261 | // Pretend this was a "constrained. UnderlyingType" instruction prefix |
5262 | constrainedType = TypeHandle(MscorlibBinder::GetElementType(constrainedType.GetVerifierCorElementType())); |
5263 | |
5264 | // Native image signature encoder will use this field. It needs to match that pretended type, a bogus signature |
5265 | // would be produced otherwise. |
5266 | pConstrainedResolvedToken->hClass = (CORINFO_CLASS_HANDLE)constrainedType.AsPtr(); |
5267 | |
5268 | // Clear the token and typespec because of they do not match hClass anymore. |
5269 | pConstrainedResolvedToken->token = mdTokenNil; |
5270 | pConstrainedResolvedToken->pTypeSpec = NULL; |
5271 | } |
5272 | } |
5273 | |
5274 | MethodDesc * directMethod = constrainedType.GetMethodTable()->TryResolveConstraintMethodApprox( |
5275 | exactType, |
5276 | pMD, |
5277 | &fForceUseRuntimeLookup); |
5278 | if (directMethod) |
5279 | { |
5280 | // Either |
5281 | // 1. no constraint resolution at compile time (!directMethod) |
5282 | // OR 2. no code sharing lookup in call |
5283 | // OR 3. we have have resolved to an instantiating stub |
5284 | |
5285 | pMDAfterConstraintResolution = directMethod; |
5286 | _ASSERTE(!pMDAfterConstraintResolution->IsInterface()); |
5287 | fResolvedConstraint = TRUE; |
5288 | pResult->thisTransform = CORINFO_NO_THIS_TRANSFORM; |
5289 | |
5290 | exactType = constrainedType; |
5291 | } |
5292 | else if (constrainedType.IsValueType()) |
5293 | { |
5294 | pResult->thisTransform = CORINFO_BOX_THIS; |
5295 | } |
5296 | else |
5297 | { |
5298 | pResult->thisTransform = CORINFO_DEREF_THIS; |
5299 | } |
5300 | } |
5301 | |
5302 | // |
5303 | // Initialize callee context used for inlining and instantiation arguments |
5304 | // |
5305 | |
5306 | MethodDesc * pTargetMD = pMDAfterConstraintResolution; |
5307 | |
5308 | if (pTargetMD->HasMethodInstantiation()) |
5309 | { |
5310 | pResult->contextHandle = MAKE_METHODCONTEXT(pTargetMD); |
5311 | pResult->exactContextNeedsRuntimeLookup = pTargetMD->GetMethodTable()->IsSharedByGenericInstantiations() || TypeHandle::IsCanonicalSubtypeInstantiation(pTargetMD->GetMethodInstantiation()); |
5312 | } |
5313 | else |
5314 | { |
5315 | if (!exactType.IsTypeDesc()) |
5316 | { |
5317 | // Because of .NET's notion of base calls, exactType may point to a sub-class |
5318 | // of the actual class that defines pTargetMD. If the JIT decides to inline, it is |
5319 | // important that they 'match', so we fix exactType here. |
5320 | #ifdef FEATURE_READYTORUN_COMPILER |
5321 | if (IsReadyToRunCompilation() && |
5322 | !isVerifyOnly() && |
5323 | !IsInSameVersionBubble((MethodDesc*)callerHandle, pTargetMD)) |
5324 | { |
5325 | // For version resilient code we can only inline within the same version bubble; |
5326 | // we "repair" the precise types only for those callees. |
5327 | // The above condition needs to stay in sync with CEEInfo::canInline |
5328 | } |
5329 | else |
5330 | #endif |
5331 | { |
5332 | |
5333 | exactType = pTargetMD->GetExactDeclaringType(exactType.AsMethodTable()); |
5334 | _ASSERTE(!exactType.IsNull()); |
5335 | } |
5336 | } |
5337 | |
5338 | pResult->contextHandle = MAKE_CLASSCONTEXT(exactType.AsPtr()); |
5339 | pResult->exactContextNeedsRuntimeLookup = exactType.IsSharedByGenericInstantiations(); |
5340 | } |
5341 | |
5342 | // |
5343 | // Determine whether to perform direct call |
5344 | // |
5345 | |
5346 | bool directCall = false; |
5347 | bool resolvedCallVirt = false; |
5348 | bool callVirtCrossingVersionBubble = false; |
5349 | |
5350 | // Delegate targets are always treated as direct calls here. (It would be nice to clean it up...). |
5351 | if (flags & CORINFO_CALLINFO_LDFTN) |
5352 | { |
5353 | if (m_pOverride != NULL) |
5354 | TypeEquivalenceFixupSpecificationHelper(m_pOverride, pTargetMD); |
5355 | directCall = true; |
5356 | } |
5357 | else |
5358 | // Static methods are always direct calls |
5359 | if (pTargetMD->IsStatic()) |
5360 | { |
5361 | directCall = true; |
5362 | } |
5363 | else |
5364 | // Backwards compat: calls to abstract interface methods are treated as callvirt |
5365 | if (pTargetMD->GetMethodTable()->IsInterface() && pTargetMD->IsAbstract()) |
5366 | { |
5367 | directCall = false; |
5368 | } |
5369 | else |
5370 | if (!(flags & CORINFO_CALLINFO_CALLVIRT) || fResolvedConstraint) |
5371 | { |
5372 | directCall = true; |
5373 | } |
5374 | else |
5375 | { |
5376 | bool devirt; |
5377 | |
5378 | #ifdef FEATURE_READYTORUN_COMPILER |
5379 | |
5380 | // if we are generating version resilient code |
5381 | // AND |
5382 | // caller/callee are in different version bubbles |
5383 | // we have to apply more restrictive rules |
5384 | // These rules are related to the "inlining rules" as far as the |
5385 | // boundaries of a version bubble are concerned. |
5386 | |
5387 | if (IsReadyToRunCompilation() && |
5388 | !isVerifyOnly() && |
5389 | !IsInSameVersionBubble((MethodDesc*)callerHandle, pTargetMD) |
5390 | ) |
5391 | { |
5392 | // For version resiliency we won't de-virtualize all final/sealed method calls. Because during a |
5393 | // servicing event it is legal to unseal a method or type. |
5394 | // |
5395 | // Note that it is safe to devirtualize in the following cases, since a servicing event cannot later modify it |
5396 | // 1) Callvirt on a virtual final method of a value type - since value types are sealed types as per ECMA spec |
5397 | // 2) Delegate.Invoke() - since a Delegate is a sealed class as per ECMA spec |
5398 | // 3) JIT intrinsics - since they have pre-defined behavior |
5399 | devirt = pTargetMD->GetMethodTable()->IsValueType() || |
5400 | (pTargetMD->GetMethodTable()->IsDelegate() && ((DelegateEEClass*)(pTargetMD->GetMethodTable()->GetClass()))->GetInvokeMethod() == pMD) || |
5401 | (pTargetMD->IsFCall() && ECall::GetIntrinsicID(pTargetMD) != CORINFO_INTRINSIC_Illegal); |
5402 | |
5403 | callVirtCrossingVersionBubble = true; |
5404 | } |
5405 | else |
5406 | #endif |
5407 | if (pTargetMD->GetMethodTable()->IsInterface()) |
5408 | { |
5409 | // Handle interface methods specially because the Sealed bit has no meaning on interfaces. |
5410 | devirt = !IsMdVirtual(pTargetMD->GetAttrs()); |
5411 | } |
5412 | else |
5413 | { |
5414 | DWORD dwMethodAttrs = pTargetMD->GetAttrs(); |
5415 | devirt = !IsMdVirtual(dwMethodAttrs) || IsMdFinal(dwMethodAttrs) || pTargetMD->GetMethodTable()->IsSealed(); |
5416 | } |
5417 | |
5418 | if (devirt) |
5419 | { |
5420 | resolvedCallVirt = true; |
5421 | directCall = true; |
5422 | } |
5423 | } |
5424 | |
5425 | if (directCall) |
5426 | { |
5427 | bool allowInstParam = (flags & CORINFO_CALLINFO_ALLOWINSTPARAM); |
5428 | |
5429 | // Create instantiating stub if necesary |
5430 | if (!allowInstParam && pTargetMD->RequiresInstArg()) |
5431 | { |
5432 | pTargetMD = MethodDesc::FindOrCreateAssociatedMethodDesc(pTargetMD, |
5433 | exactType.AsMethodTable(), |
5434 | FALSE /* forceBoxedEntryPoint */, |
5435 | pTargetMD->GetMethodInstantiation(), |
5436 | FALSE /* allowInstParam */); |
5437 | } |
5438 | |
5439 | // We don't allow a JIT to call the code directly if a runtime lookup is |
5440 | // needed. This is the case if |
5441 | // 1. the scan of the call token indicated that it involves code sharing |
5442 | // AND 2. the method is an instantiating stub |
5443 | // |
5444 | // In these cases the correct instantiating stub is only found via a runtime lookup. |
5445 | // |
5446 | // Note that most JITs don't call instantiating stubs directly if they can help it - |
5447 | // they call the underlying shared code and provide the type context parameter |
5448 | // explicitly. However |
5449 | // (a) some JITs may call instantiating stubs (it makes the JIT simpler) and |
5450 | // (b) if the method is a remote stub then the EE will force the |
5451 | // call through an instantiating stub and |
5452 | // (c) constraint calls that require runtime context lookup are never resolved |
5453 | // to underlying shared generic code |
5454 | |
5455 | if (((pResult->exactContextNeedsRuntimeLookup && pTargetMD->IsInstantiatingStub() && (!allowInstParam || fResolvedConstraint)) || fForceUseRuntimeLookup) |
5456 | // Handle invalid IL - see comment in code:CEEInfo::ComputeRuntimeLookupForSharedGenericToken |
5457 | && ContextIsShared(pResolvedToken->tokenContext)) |
5458 | { |
5459 | _ASSERTE(!m_pMethodBeingCompiled->IsDynamicMethod()); |
5460 | pResult->kind = CORINFO_CALL_CODE_POINTER; |
5461 | |
5462 | // For reference types, the constrained type does not affect method resolution |
5463 | DictionaryEntryKind entryKind = (!constrainedType.IsNull() && constrainedType.IsValueType()) ? ConstrainedMethodEntrySlot : MethodEntrySlot; |
5464 | |
5465 | ComputeRuntimeLookupForSharedGenericToken(entryKind, |
5466 | pResolvedToken, |
5467 | pConstrainedResolvedToken, |
5468 | pMD, |
5469 | &pResult->codePointerLookup); |
5470 | } |
5471 | else |
5472 | { |
5473 | if (allowInstParam && pTargetMD->IsInstantiatingStub()) |
5474 | { |
5475 | pTargetMD = pTargetMD->GetWrappedMethodDesc(); |
5476 | } |
5477 | |
5478 | pResult->kind = CORINFO_CALL; |
5479 | |
5480 | if (IsReadyToRunCompilation()) |
5481 | { |
5482 | // Compensate for always treating delegates as direct calls above |
5483 | if ((flags & CORINFO_CALLINFO_LDFTN) && (flags & CORINFO_CALLINFO_CALLVIRT) && !resolvedCallVirt) |
5484 | { |
5485 | pResult->kind = CORINFO_VIRTUALCALL_LDVIRTFTN; |
5486 | } |
5487 | } |
5488 | } |
5489 | pResult->nullInstanceCheck = resolvedCallVirt; |
5490 | } |
5491 | // All virtual calls which take method instantiations must |
5492 | // currently be implemented by an indirect call via a runtime-lookup |
5493 | // function pointer |
5494 | else if (pTargetMD->HasMethodInstantiation()) |
5495 | { |
5496 | pResult->kind = CORINFO_VIRTUALCALL_LDVIRTFTN; // stub dispatch can't handle generic method calls yet |
5497 | pResult->nullInstanceCheck = TRUE; |
5498 | } |
5499 | // Non-interface dispatches go through the vtable. |
5500 | else if (!pTargetMD->IsInterface()) |
5501 | { |
5502 | pResult->kind = CORINFO_VIRTUALCALL_VTABLE; |
5503 | pResult->nullInstanceCheck = TRUE; |
5504 | |
5505 | // We'll special virtual calls to target methods in the corelib assembly when compiling in R2R mode, and generate fragile-NI-like callsites for improved performance. We |
5506 | // can do that because today we'll always service the corelib assembly and the runtime in one bundle. Any caller in the corelib version bubble can benefit from this |
5507 | // performance optimization. |
5508 | if (IsReadyToRunCompilation() && !CallerAndCalleeInSystemVersionBubble((MethodDesc*)callerHandle, pTargetMD)) |
5509 | { |
5510 | pResult->kind = CORINFO_VIRTUALCALL_STUB; |
5511 | } |
5512 | } |
5513 | else |
5514 | { |
5515 | if (IsReadyToRunCompilation()) |
5516 | { |
5517 | // Insert explicit null checks for cross-version bubble non-interface calls. |
5518 | // It is required to handle null checks properly for non-virtual <-> virtual change between versions |
5519 | pResult->nullInstanceCheck = !!(callVirtCrossingVersionBubble && !pTargetMD->IsInterface()); |
5520 | } |
5521 | else |
5522 | { |
5523 | // No need to null check - the dispatch code will deal with null this. |
5524 | pResult->nullInstanceCheck = FALSE; |
5525 | } |
5526 | #ifdef STUB_DISPATCH_PORTABLE |
5527 | pResult->kind = CORINFO_VIRTUALCALL_LDVIRTFTN; |
5528 | #else // STUB_DISPATCH_PORTABLE |
5529 | pResult->kind = CORINFO_VIRTUALCALL_STUB; |
5530 | |
5531 | // We can't make stub calls when we need exact information |
5532 | // for interface calls from shared code. |
5533 | |
5534 | if (// If the token is not shared then we don't need a runtime lookup |
5535 | pResult->exactContextNeedsRuntimeLookup |
5536 | // Handle invalid IL - see comment in code:CEEInfo::ComputeRuntimeLookupForSharedGenericToken |
5537 | && ContextIsShared(pResolvedToken->tokenContext)) |
5538 | { |
5539 | _ASSERTE(!m_pMethodBeingCompiled->IsDynamicMethod()); |
5540 | |
5541 | ComputeRuntimeLookupForSharedGenericToken(DispatchStubAddrSlot, |
5542 | pResolvedToken, |
5543 | NULL, |
5544 | pMD, |
5545 | &pResult->stubLookup); |
5546 | } |
5547 | else |
5548 | { |
5549 | BYTE * indcell = NULL; |
5550 | |
5551 | if (!(flags & CORINFO_CALLINFO_KINDONLY) && !isVerifyOnly()) |
5552 | { |
5553 | #ifndef CROSSGEN_COMPILE |
5554 | // We shouldn't be using GetLoaderAllocator here because for LCG, we need to get the |
5555 | // VirtualCallStubManager from where the stub will be used. |
5556 | // For normal methods there is no difference. |
5557 | LoaderAllocator *pLoaderAllocator = m_pMethodBeingCompiled->GetLoaderAllocatorForCode(); |
5558 | VirtualCallStubManager *pMgr = pLoaderAllocator->GetVirtualCallStubManager(); |
5559 | |
5560 | PCODE addr = pMgr->GetCallStub(exactType, pTargetMD); |
5561 | _ASSERTE(pMgr->isStub(addr)); |
5562 | |
5563 | // Now we want to indirect through a cell so that updates can take place atomically. |
5564 | if (m_pMethodBeingCompiled->IsLCGMethod()) |
5565 | { |
5566 | // LCG methods should use recycled indcells to prevent leaks. |
5567 | indcell = pMgr->GenerateStubIndirection(addr, TRUE); |
5568 | |
5569 | // Add it to the per DM list so that we can recycle them when the resolver is finalized |
5570 | LCGMethodResolver *pResolver = m_pMethodBeingCompiled->AsDynamicMethodDesc()->GetLCGMethodResolver(); |
5571 | pResolver->AddToUsedIndCellList(indcell); |
5572 | } |
5573 | else |
5574 | { |
5575 | // Normal methods should avoid recycled cells to preserve the locality of all indcells |
5576 | // used by one method. |
5577 | indcell = pMgr->GenerateStubIndirection(addr, FALSE); |
5578 | } |
5579 | #else // CROSSGEN_COMPILE |
5580 | // This path should be unreachable during crossgen |
5581 | _ASSERTE(false); |
5582 | #endif // CROSSGEN_COMPILE |
5583 | } |
5584 | |
5585 | // We use an indirect call |
5586 | pResult->stubLookup.constLookup.accessType = IAT_PVALUE; |
5587 | pResult->stubLookup.constLookup.addr = indcell; |
5588 | } |
5589 | #endif // STUB_DISPATCH_PORTABLE |
5590 | } |
5591 | |
5592 | pResult->hMethod = CORINFO_METHOD_HANDLE(pTargetMD); |
5593 | |
5594 | pResult->accessAllowed = CORINFO_ACCESS_ALLOWED; |
5595 | if ((flags & CORINFO_CALLINFO_SECURITYCHECKS) && |
5596 | !((MethodDesc *)callerHandle)->IsILStub()) // IL stubs can access everything, don't bother doing access checks |
5597 | { |
5598 | //Our type system doesn't always represent the target exactly with the MethodDesc. In all cases, |
5599 | //carry around the parent MethodTable for both Caller and Callee. |
5600 | TypeHandle calleeTypeForSecurity = TypeHandle(pResolvedToken->hClass); |
5601 | MethodDesc * pCalleeForSecurity = pMD; |
5602 | |
5603 | MethodDesc * pCallerForSecurity = GetMethodForSecurity(callerHandle); //Should this be the open MD? |
5604 | |
5605 | if (pCallerForSecurity->HasClassOrMethodInstantiation()) |
5606 | { |
5607 | _ASSERTE(!IsDynamicScope(pResolvedToken->tokenScope)); |
5608 | |
5609 | SigTypeContext typeContext; |
5610 | SigTypeContext::InitTypeContext(pCallerForSecurity, &typeContext); |
5611 | _ASSERTE(!typeContext.IsEmpty()); |
5612 | |
5613 | //If the caller is generic, load the open type and resolve the token again. Use that for the access |
5614 | //checks. If we don't do this then we can't tell the difference between: |
5615 | // |
5616 | //BadGeneric<T> containing a methodspec for InaccessibleType::member (illegal) |
5617 | //and |
5618 | //BadGeneric<T> containing a methodspec for !!0::member instantiated over InaccessibleType (legal) |
5619 | |
5620 | if (pResolvedToken->pTypeSpec != NULL) |
5621 | { |
5622 | SigPointer sigptr(pResolvedToken->pTypeSpec, pResolvedToken->cbTypeSpec); |
5623 | calleeTypeForSecurity = sigptr.GetTypeHandleThrowing((Module *)pResolvedToken->tokenScope, &typeContext); |
5624 | |
5625 | // typeHnd can be a variable type |
5626 | if (calleeTypeForSecurity.GetMethodTable() == NULL) |
5627 | { |
5628 | COMPlusThrowHR(COR_E_BADIMAGEFORMAT, BFA_METHODDEF_PARENT_NO_MEMBERS); |
5629 | } |
5630 | } |
5631 | |
5632 | if (pCalleeForSecurity->IsArray()) |
5633 | { |
5634 | // FindOrCreateAssociatedMethodDesc won't remap array method desc because of array base type |
5635 | // is not part of instantiation. We have to special case it. |
5636 | pCalleeForSecurity = calleeTypeForSecurity.GetMethodTable()->GetParallelMethodDesc(pCalleeForSecurity); |
5637 | } |
5638 | else |
5639 | if (pResolvedToken->pMethodSpec != NULL) |
5640 | { |
5641 | DWORD nGenericMethodArgs = 0; |
5642 | CQuickBytes qbGenericMethodArgs; |
5643 | TypeHandle *genericMethodArgs = NULL; |
5644 | |
5645 | SigPointer sp(pResolvedToken->pMethodSpec, pResolvedToken->cbMethodSpec); |
5646 | |
5647 | BYTE etype; |
5648 | IfFailThrow(sp.GetByte(&etype)); |
5649 | |
5650 | // Load the generic method instantiation |
5651 | THROW_BAD_FORMAT_MAYBE(etype == (BYTE)IMAGE_CEE_CS_CALLCONV_GENERICINST, 0, (Module *)pResolvedToken->tokenScope); |
5652 | |
5653 | IfFailThrow(sp.GetData(&nGenericMethodArgs)); |
5654 | |
5655 | DWORD cbAllocSize = 0; |
5656 | if (!ClrSafeInt<DWORD>::multiply(nGenericMethodArgs, sizeof(TypeHandle), cbAllocSize)) |
5657 | { |
5658 | COMPlusThrowHR(COR_E_OVERFLOW); |
5659 | } |
5660 | |
5661 | genericMethodArgs = reinterpret_cast<TypeHandle *>(qbGenericMethodArgs.AllocThrows(cbAllocSize)); |
5662 | |
5663 | for (DWORD i = 0; i < nGenericMethodArgs; i++) |
5664 | { |
5665 | genericMethodArgs[i] = sp.GetTypeHandleThrowing((Module *)pResolvedToken->tokenScope, &typeContext); |
5666 | _ASSERTE (!genericMethodArgs[i].IsNull()); |
5667 | IfFailThrow(sp.SkipExactlyOne()); |
5668 | } |
5669 | |
5670 | pCalleeForSecurity = MethodDesc::FindOrCreateAssociatedMethodDesc(pMD, calleeTypeForSecurity.GetMethodTable(), FALSE, Instantiation(genericMethodArgs, nGenericMethodArgs), FALSE); |
5671 | } |
5672 | else |
5673 | if (pResolvedToken->pTypeSpec != NULL) |
5674 | { |
5675 | pCalleeForSecurity = MethodDesc::FindOrCreateAssociatedMethodDesc(pMD, calleeTypeForSecurity.GetMethodTable(), FALSE, Instantiation(), TRUE); |
5676 | } |
5677 | } |
5678 | |
5679 | TypeHandle callerTypeForSecurity = TypeHandle(pCallerForSecurity->GetMethodTable()); |
5680 | |
5681 | //Passed various link-time checks. Now do access checks. |
5682 | |
5683 | BOOL doAccessCheck = TRUE; |
5684 | BOOL canAccessMethod = TRUE; |
5685 | AccessCheckOptions::AccessCheckType accessCheckType = AccessCheckOptions::kNormalAccessibilityChecks; |
5686 | DynamicResolver * pAccessContext = NULL; |
5687 | |
5688 | callerTypeForSecurity = TypeHandle(pCallerForSecurity->GetMethodTable()); |
5689 | if (pCallerForSecurity->IsDynamicMethod()) |
5690 | { |
5691 | doAccessCheck = ModifyCheckForDynamicMethod(pCallerForSecurity->AsDynamicMethodDesc()->GetResolver(), |
5692 | &callerTypeForSecurity, |
5693 | &accessCheckType, &pAccessContext); |
5694 | } |
5695 | |
5696 | pResult->accessAllowed = CORINFO_ACCESS_ALLOWED; |
5697 | |
5698 | if (doAccessCheck) |
5699 | { |
5700 | AccessCheckOptions accessCheckOptions(accessCheckType, |
5701 | pAccessContext, |
5702 | FALSE, |
5703 | pCalleeForSecurity); |
5704 | |
5705 | _ASSERTE(pCallerForSecurity != NULL && callerTypeForSecurity != NULL); |
5706 | StaticAccessCheckContext accessContext(pCallerForSecurity, callerTypeForSecurity.GetMethodTable()); |
5707 | |
5708 | canAccessMethod = ClassLoader::CanAccess(&accessContext, |
5709 | calleeTypeForSecurity.GetMethodTable(), |
5710 | calleeTypeForSecurity.GetAssembly(), |
5711 | pCalleeForSecurity->GetAttrs(), |
5712 | pCalleeForSecurity, |
5713 | NULL, |
5714 | accessCheckOptions |
5715 | ); |
5716 | |
5717 | // If we were allowed access to the exact method, but it is on a type that has a type parameter |
5718 | // (for instance an array), we need to ensure that we also have access to the type parameter. |
5719 | if (canAccessMethod && calleeTypeForSecurity.HasTypeParam()) |
5720 | { |
5721 | TypeHandle typeParam = calleeTypeForSecurity.GetTypeParam(); |
5722 | while (typeParam.HasTypeParam()) |
5723 | { |
5724 | typeParam = typeParam.GetTypeParam(); |
5725 | } |
5726 | |
5727 | _ASSERTE(pCallerForSecurity != NULL && callerTypeForSecurity != NULL); |
5728 | StaticAccessCheckContext accessContext(pCallerForSecurity, callerTypeForSecurity.GetMethodTable()); |
5729 | |
5730 | MethodTable* pTypeParamMT = typeParam.GetMethodTable(); |
5731 | |
5732 | // No access check is need for Var, MVar, or FnPtr. |
5733 | if (pTypeParamMT != NULL) |
5734 | canAccessMethod = ClassLoader::CanAccessClass(&accessContext, |
5735 | pTypeParamMT, |
5736 | typeParam.GetAssembly(), |
5737 | accessCheckOptions); |
5738 | } |
5739 | |
5740 | pResult->accessAllowed = canAccessMethod ? CORINFO_ACCESS_ALLOWED : CORINFO_ACCESS_ILLEGAL; |
5741 | if (!canAccessMethod) |
5742 | { |
5743 | //Check failed, fill in the throw exception helper. |
5744 | pResult->callsiteCalloutHelper.helperNum = CORINFO_HELP_METHOD_ACCESS_EXCEPTION; |
5745 | pResult->callsiteCalloutHelper.numArgs = 2; |
5746 | |
5747 | pResult->callsiteCalloutHelper.args[0].Set(CORINFO_METHOD_HANDLE(pCallerForSecurity)); |
5748 | pResult->callsiteCalloutHelper.args[1].Set(CORINFO_METHOD_HANDLE(pCalleeForSecurity)); |
5749 | |
5750 | //We now embed open instantiations in a few places for security callouts (since you can only |
5751 | //do the security check on the open instantiation). We throw these methods out in |
5752 | //TriageMethodForZap. In addition, NGen has problems referencing them properly. Just throw out the whole |
5753 | //method and rejit at runtime. |
5754 | if (IsCompilingForNGen()) |
5755 | { |
5756 | if (pCallerForSecurity->ContainsGenericVariables() |
5757 | || pCalleeForSecurity->ContainsGenericVariables()) |
5758 | { |
5759 | COMPlusThrowNonLocalized(kNotSupportedException, W("Cannot embed generic MethodDesc" )); |
5760 | } |
5761 | } |
5762 | } |
5763 | } |
5764 | } |
5765 | |
5766 | //We're pretty much done at this point. Let's grab the rest of the information that the jit is going to |
5767 | //need. |
5768 | pResult->classFlags = getClassAttribsInternal(pResolvedToken->hClass); |
5769 | |
5770 | pResult->methodFlags = getMethodAttribsInternal(pResult->hMethod); |
5771 | |
5772 | SignatureKind signatureKind = flags & CORINFO_CALLINFO_CALLVIRT ? SK_VIRTUAL_CALLSITE : SK_CALLSITE; |
5773 | getMethodSigInternal(pResult->hMethod, &pResult->sig, (pResult->hMethod == pResolvedToken->hMethod) ? pResolvedToken->hClass : NULL, signatureKind); |
5774 | |
5775 | if (flags & CORINFO_CALLINFO_VERIFICATION) |
5776 | { |
5777 | if (pResult->hMethod != pResolvedToken->hMethod) |
5778 | { |
5779 | pResult->verMethodFlags = getMethodAttribsInternal(pResolvedToken->hMethod); |
5780 | getMethodSigInternal(pResolvedToken->hMethod, &pResult->verSig, pResolvedToken->hClass); |
5781 | } |
5782 | else |
5783 | { |
5784 | pResult->verMethodFlags = pResult->methodFlags; |
5785 | pResult->verSig = pResult->sig; |
5786 | } |
5787 | } |
5788 | |
5789 | pResult->secureDelegateInvoke = FALSE; |
5790 | |
5791 | #ifdef FEATURE_STUBS_AS_IL |
5792 | if (m_pMethodBeingCompiled->IsDynamicMethod()) |
5793 | { |
5794 | auto pMD = m_pMethodBeingCompiled->AsDynamicMethodDesc(); |
5795 | if (pMD->IsILStub() && pMD->IsSecureDelegateStub()) |
5796 | { |
5797 | pResult->secureDelegateInvoke = TRUE; |
5798 | } |
5799 | } |
5800 | #endif |
5801 | |
5802 | EE_TO_JIT_TRANSITION(); |
5803 | } |
5804 | |
5805 | BOOL CEEInfo::canAccessFamily(CORINFO_METHOD_HANDLE hCaller, |
5806 | CORINFO_CLASS_HANDLE hInstanceType) |
5807 | { |
5808 | WRAPPER_NO_CONTRACT; |
5809 | |
5810 | BOOL ret = FALSE; |
5811 | |
5812 | //Since this is only for verification, I don't need to do the demand. |
5813 | JIT_TO_EE_TRANSITION(); |
5814 | |
5815 | TypeHandle targetType = TypeHandle(hInstanceType); |
5816 | TypeHandle accessingType = TypeHandle(GetMethod(hCaller)->GetMethodTable()); |
5817 | AccessCheckOptions::AccessCheckType accessCheckOptions = AccessCheckOptions::kNormalAccessibilityChecks; |
5818 | DynamicResolver* pIgnored; |
5819 | BOOL doCheck = TRUE; |
5820 | if (GetMethod(hCaller)->IsDynamicMethod()) |
5821 | { |
5822 | //If this is a DynamicMethod, perform the check from the type to which the DynamicMethod was |
5823 | //attached. |
5824 | // |
5825 | //If this is a dynamic method, don't do this check. If they specified SkipVisibilityChecks |
5826 | //(ModifyCheckForDynamicMethod returned false), we should obviously skip the check for the C++ |
5827 | //protected rule (since we skipped all the other visibility checks). If they specified |
5828 | //RestrictedSkipVisibilityChecks, then they're a "free" DynamicMethod. This check is meaningless |
5829 | //(i.e. it would always fail). We've already done a demand for access to the member. Let that be |
5830 | //enough. |
5831 | doCheck = ModifyCheckForDynamicMethod(GetMethod(hCaller)->AsDynamicMethodDesc()->GetResolver(), |
5832 | &accessingType, &accessCheckOptions, &pIgnored); |
5833 | if (accessCheckOptions == AccessCheckOptions::kRestrictedMemberAccess |
5834 | || accessCheckOptions == AccessCheckOptions::kRestrictedMemberAccessNoTransparency |
5835 | ) |
5836 | doCheck = FALSE; |
5837 | } |
5838 | |
5839 | if (doCheck) |
5840 | { |
5841 | ret = ClassLoader::CanAccessFamilyVerification(accessingType, targetType); |
5842 | } |
5843 | else |
5844 | { |
5845 | ret = TRUE; |
5846 | } |
5847 | |
5848 | EE_TO_JIT_TRANSITION(); |
5849 | return ret; |
5850 | } |
5851 | void CEEInfo::ThrowExceptionForHelper(const CORINFO_HELPER_DESC * throwHelper) |
5852 | { |
5853 | CONTRACTL { |
5854 | SO_TOLERANT; |
5855 | THROWS; |
5856 | GC_TRIGGERS; |
5857 | MODE_PREEMPTIVE; |
5858 | } CONTRACTL_END; |
5859 | |
5860 | JIT_TO_EE_TRANSITION(); |
5861 | |
5862 | _ASSERTE(throwHelper->args[0].argType == CORINFO_HELPER_ARG_TYPE_Method); |
5863 | MethodDesc *pCallerMD = GetMethod(throwHelper->args[0].methodHandle); |
5864 | |
5865 | StaticAccessCheckContext accessContext(pCallerMD); |
5866 | |
5867 | switch (throwHelper->helperNum) |
5868 | { |
5869 | case CORINFO_HELP_METHOD_ACCESS_EXCEPTION: |
5870 | { |
5871 | _ASSERTE(throwHelper->args[1].argType == CORINFO_HELPER_ARG_TYPE_Method); |
5872 | ThrowMethodAccessException(&accessContext, GetMethod(throwHelper->args[1].methodHandle)); |
5873 | } |
5874 | break; |
5875 | case CORINFO_HELP_FIELD_ACCESS_EXCEPTION: |
5876 | { |
5877 | _ASSERTE(throwHelper->args[1].argType == CORINFO_HELPER_ARG_TYPE_Field); |
5878 | ThrowFieldAccessException(&accessContext, reinterpret_cast<FieldDesc *>(throwHelper->args[1].fieldHandle)); |
5879 | } |
5880 | break; |
5881 | case CORINFO_HELP_CLASS_ACCESS_EXCEPTION: |
5882 | { |
5883 | _ASSERTE(throwHelper->args[1].argType == CORINFO_HELPER_ARG_TYPE_Class); |
5884 | TypeHandle typeHnd(throwHelper->args[1].classHandle); |
5885 | ThrowTypeAccessException(&accessContext, typeHnd.GetMethodTable()); |
5886 | } |
5887 | break; |
5888 | |
5889 | default: |
5890 | _ASSERTE(!"Unknown access exception type" ); |
5891 | } |
5892 | EE_TO_JIT_TRANSITION(); |
5893 | } |
5894 | |
5895 | |
5896 | BOOL CEEInfo::isRIDClassDomainID(CORINFO_CLASS_HANDLE cls) |
5897 | { |
5898 | CONTRACTL { |
5899 | SO_TOLERANT; |
5900 | THROWS; |
5901 | GC_TRIGGERS; |
5902 | MODE_PREEMPTIVE; |
5903 | } CONTRACTL_END; |
5904 | |
5905 | BOOL result = FALSE; |
5906 | |
5907 | JIT_TO_EE_TRANSITION(); |
5908 | |
5909 | TypeHandle VMClsHnd(cls); |
5910 | |
5911 | result = !VMClsHnd.AsMethodTable()->IsDynamicStatics(); |
5912 | |
5913 | EE_TO_JIT_TRANSITION(); |
5914 | |
5915 | return result; |
5916 | } |
5917 | |
5918 | |
5919 | /***********************************************************************/ |
5920 | unsigned CEEInfo::getClassDomainID (CORINFO_CLASS_HANDLE clsHnd, |
5921 | void **ppIndirection) |
5922 | { |
5923 | CONTRACTL { |
5924 | SO_TOLERANT; |
5925 | THROWS; |
5926 | GC_TRIGGERS; |
5927 | MODE_PREEMPTIVE; |
5928 | } CONTRACTL_END; |
5929 | |
5930 | unsigned result = 0; |
5931 | |
5932 | if (ppIndirection != NULL) |
5933 | *ppIndirection = NULL; |
5934 | |
5935 | JIT_TO_EE_TRANSITION(); |
5936 | |
5937 | TypeHandle VMClsHnd(clsHnd); |
5938 | |
5939 | if (VMClsHnd.AsMethodTable()->IsDynamicStatics()) |
5940 | { |
5941 | result = (unsigned)VMClsHnd.AsMethodTable()->GetModuleDynamicEntryID(); |
5942 | } |
5943 | else |
5944 | { |
5945 | result = (unsigned)VMClsHnd.AsMethodTable()->GetClassIndex(); |
5946 | } |
5947 | |
5948 | EE_TO_JIT_TRANSITION(); |
5949 | |
5950 | return result; |
5951 | } |
5952 | |
5953 | //--------------------------------------------------------------------------------------- |
5954 | // |
5955 | // Used by the JIT to determine whether the profiler or IBC is tracking object |
5956 | // allocations |
5957 | // |
5958 | // Return Value: |
5959 | // bool indicating whether the profiler or IBC is tracking object allocations |
5960 | // |
5961 | // Notes: |
5962 | // Normally, a profiler would just directly call the inline helper to determine |
5963 | // whether the profiler set the relevant event flag (e.g., |
5964 | // CORProfilerTrackAllocationsEnabled). However, this wrapper also asks whether we're |
5965 | // running for IBC instrumentation or enabling the object allocated ETW event. If so, |
5966 | // we treat that the same as if the profiler requested allocation information, so that |
5967 | // the JIT will still use the profiling-friendly object allocation jit helper, so the |
5968 | // allocations can be tracked. |
5969 | // |
5970 | |
5971 | bool __stdcall TrackAllocationsEnabled() |
5972 | { |
5973 | CONTRACTL |
5974 | { |
5975 | NOTHROW; |
5976 | GC_NOTRIGGER; |
5977 | MODE_ANY; |
5978 | } |
5979 | CONTRACTL_END; |
5980 | |
5981 | return ( |
5982 | (g_IBCLogger.InstrEnabled() != FALSE) |
5983 | #ifdef PROFILING_SUPPORTED |
5984 | || CORProfilerTrackAllocationsEnabled() |
5985 | #endif // PROFILING_SUPPORTED |
5986 | #ifdef FEATURE_EVENT_TRACE |
5987 | || ETW::TypeSystemLog::IsHeapAllocEventEnabled() |
5988 | #endif // FEATURE_EVENT_TRACE |
5989 | ); |
5990 | } |
5991 | |
5992 | /***********************************************************************/ |
5993 | CorInfoHelpFunc CEEInfo::getNewHelper(CORINFO_RESOLVED_TOKEN * pResolvedToken, CORINFO_METHOD_HANDLE callerHandle, bool * pHasSideEffects) |
5994 | { |
5995 | CONTRACTL { |
5996 | SO_TOLERANT; |
5997 | THROWS; |
5998 | GC_TRIGGERS; |
5999 | MODE_PREEMPTIVE; |
6000 | } CONTRACTL_END; |
6001 | |
6002 | CorInfoHelpFunc result = CORINFO_HELP_UNDEF; |
6003 | |
6004 | JIT_TO_EE_TRANSITION(); |
6005 | |
6006 | TypeHandle VMClsHnd(pResolvedToken->hClass); |
6007 | |
6008 | if(VMClsHnd.IsTypeDesc()) |
6009 | { |
6010 | COMPlusThrow(kInvalidOperationException,W("InvalidOperation_CantInstantiateFunctionPointer" )); |
6011 | } |
6012 | |
6013 | if(VMClsHnd.IsAbstract()) |
6014 | { |
6015 | COMPlusThrow(kInvalidOperationException,W("InvalidOperation_CantInstantiateAbstractClass" )); |
6016 | } |
6017 | |
6018 | MethodTable* pMT = VMClsHnd.AsMethodTable(); |
6019 | result = getNewHelperStatic(pMT, pHasSideEffects); |
6020 | |
6021 | _ASSERTE(result != CORINFO_HELP_UNDEF); |
6022 | |
6023 | EE_TO_JIT_TRANSITION(); |
6024 | |
6025 | return result; |
6026 | } |
6027 | |
6028 | /***********************************************************************/ |
6029 | CorInfoHelpFunc CEEInfo::getNewHelperStatic(MethodTable * pMT, bool * pHasSideEffects) |
6030 | { |
6031 | STANDARD_VM_CONTRACT; |
6032 | |
6033 | |
6034 | // Slow helper is the default |
6035 | CorInfoHelpFunc helper = CORINFO_HELP_NEWFAST; |
6036 | BOOL hasFinalizer = pMT->HasFinalizer(); |
6037 | BOOL isComObjectType = pMT->IsComObjectType(); |
6038 | |
6039 | if (pHasSideEffects != nullptr) |
6040 | { |
6041 | if (isComObjectType) |
6042 | { |
6043 | *pHasSideEffects = true; |
6044 | } |
6045 | else |
6046 | #ifdef FEATURE_READYTORUN_COMPILER |
6047 | if (IsReadyToRunCompilation()) |
6048 | { |
6049 | *pHasSideEffects = hasFinalizer || !pMT->IsInheritanceChainFixedInCurrentVersionBubble(); |
6050 | } |
6051 | else |
6052 | #endif |
6053 | { |
6054 | *pHasSideEffects = !!hasFinalizer; |
6055 | } |
6056 | } |
6057 | |
6058 | if (isComObjectType) |
6059 | { |
6060 | // Use slow helper |
6061 | _ASSERTE(helper == CORINFO_HELP_NEWFAST); |
6062 | } |
6063 | else |
6064 | if ((pMT->GetBaseSize() >= LARGE_OBJECT_SIZE) || |
6065 | hasFinalizer) |
6066 | { |
6067 | // Use slow helper |
6068 | _ASSERTE(helper == CORINFO_HELP_NEWFAST); |
6069 | } |
6070 | else |
6071 | // don't call the super-optimized one since that does not check |
6072 | // for GCStress |
6073 | if (GCStress<cfg_alloc>::IsEnabled()) |
6074 | { |
6075 | // Use slow helper |
6076 | _ASSERTE(helper == CORINFO_HELP_NEWFAST); |
6077 | } |
6078 | else |
6079 | #ifdef _LOGALLOC |
6080 | #ifdef LOGGING |
6081 | // Super fast version doesn't do logging |
6082 | if (LoggingOn(LF_GCALLOC, LL_INFO10)) |
6083 | { |
6084 | // Use slow helper |
6085 | _ASSERTE(helper == CORINFO_HELP_NEWFAST); |
6086 | } |
6087 | else |
6088 | #endif // LOGGING |
6089 | #endif // _LOGALLOC |
6090 | // Don't use the SFAST allocator when tracking object allocations, |
6091 | // so we don't have to instrument it. |
6092 | if (TrackAllocationsEnabled()) |
6093 | { |
6094 | // Use slow helper |
6095 | _ASSERTE(helper == CORINFO_HELP_NEWFAST); |
6096 | } |
6097 | else |
6098 | #ifdef FEATURE_64BIT_ALIGNMENT |
6099 | // @ARMTODO: Force all 8-byte alignment requiring allocations down one slow path. As performance |
6100 | // measurements dictate we can spread these out to faster, more specialized helpers later. |
6101 | if (pMT->RequiresAlign8()) |
6102 | { |
6103 | // Use slow helper |
6104 | _ASSERTE(helper == CORINFO_HELP_NEWFAST); |
6105 | } |
6106 | else |
6107 | #endif |
6108 | { |
6109 | // Use the fast helper when all conditions are met |
6110 | helper = CORINFO_HELP_NEWSFAST; |
6111 | } |
6112 | |
6113 | #ifdef FEATURE_DOUBLE_ALIGNMENT_HINT |
6114 | // If we are use the the fast allocator we also may need the |
6115 | // specialized varion for align8 |
6116 | if (pMT->GetClass()->IsAlign8Candidate() && |
6117 | (helper == CORINFO_HELP_NEWSFAST)) |
6118 | { |
6119 | helper = CORINFO_HELP_NEWSFAST_ALIGN8; |
6120 | } |
6121 | #endif // FEATURE_DOUBLE_ALIGNMENT_HINT |
6122 | |
6123 | return helper; |
6124 | } |
6125 | |
6126 | /***********************************************************************/ |
6127 | // <REVIEW> this only works for shared generic code because all the |
6128 | // helpers are actually the same. If they were different then things might |
6129 | // break because the same helper would end up getting used for different but |
6130 | // representation-compatible arrays (e.g. one with a default constructor |
6131 | // and one without) </REVIEW> |
6132 | CorInfoHelpFunc CEEInfo::getNewArrHelper (CORINFO_CLASS_HANDLE arrayClsHnd) |
6133 | { |
6134 | CONTRACTL { |
6135 | SO_TOLERANT; |
6136 | THROWS; |
6137 | GC_TRIGGERS; |
6138 | MODE_PREEMPTIVE; |
6139 | } CONTRACTL_END; |
6140 | |
6141 | CorInfoHelpFunc result = CORINFO_HELP_UNDEF; |
6142 | |
6143 | JIT_TO_EE_TRANSITION(); |
6144 | |
6145 | TypeHandle arrayType(arrayClsHnd); |
6146 | |
6147 | result = getNewArrHelperStatic(arrayType); |
6148 | |
6149 | _ASSERTE(result != CORINFO_HELP_UNDEF); |
6150 | |
6151 | EE_TO_JIT_TRANSITION(); |
6152 | |
6153 | return result; |
6154 | } |
6155 | |
6156 | /***********************************************************************/ |
6157 | CorInfoHelpFunc CEEInfo::getNewArrHelperStatic(TypeHandle clsHnd) |
6158 | { |
6159 | STANDARD_VM_CONTRACT; |
6160 | |
6161 | ArrayTypeDesc* arrayTypeDesc = clsHnd.AsArray(); |
6162 | _ASSERTE(arrayTypeDesc->GetInternalCorElementType() == ELEMENT_TYPE_SZARRAY); |
6163 | |
6164 | if (GCStress<cfg_alloc>::IsEnabled()) |
6165 | { |
6166 | return CORINFO_HELP_NEWARR_1_DIRECT; |
6167 | } |
6168 | |
6169 | CorInfoHelpFunc result = CORINFO_HELP_UNDEF; |
6170 | |
6171 | TypeHandle thElemType = arrayTypeDesc->GetTypeParam(); |
6172 | CorElementType elemType = thElemType.GetInternalCorElementType(); |
6173 | |
6174 | // This is if we're asked for newarr !0 when verifying generic code |
6175 | // Of course ideally you wouldn't even be generating code when |
6176 | // simply doing verification (we run the JIT importer in import-only |
6177 | // mode), but importing does more than one would like so we try to be |
6178 | // tolerant when asked for non-sensical helpers. |
6179 | if (CorTypeInfo::IsGenericVariable(elemType)) |
6180 | { |
6181 | result = CORINFO_HELP_NEWARR_1_OBJ; |
6182 | } |
6183 | else if (CorTypeInfo::IsObjRef(elemType)) |
6184 | { |
6185 | // It is an array of object refs |
6186 | result = CORINFO_HELP_NEWARR_1_OBJ; |
6187 | } |
6188 | else |
6189 | { |
6190 | // These cases always must use the slow helper |
6191 | if ( |
6192 | #ifdef FEATURE_64BIT_ALIGNMENT |
6193 | thElemType.RequiresAlign8() || |
6194 | #endif |
6195 | (elemType == ELEMENT_TYPE_VOID) || |
6196 | LoggingOn(LF_GCALLOC, LL_INFO10) || |
6197 | TrackAllocationsEnabled()) |
6198 | { |
6199 | // Use the slow helper |
6200 | result = CORINFO_HELP_NEWARR_1_DIRECT; |
6201 | } |
6202 | #ifdef FEATURE_DOUBLE_ALIGNMENT_HINT |
6203 | else if (elemType == ELEMENT_TYPE_R8) |
6204 | { |
6205 | // Use the Align8 fast helper |
6206 | result = CORINFO_HELP_NEWARR_1_ALIGN8; |
6207 | } |
6208 | #endif |
6209 | else |
6210 | { |
6211 | // Yea, we can do it the fast way! |
6212 | result = CORINFO_HELP_NEWARR_1_VC; |
6213 | } |
6214 | } |
6215 | |
6216 | return result; |
6217 | } |
6218 | |
6219 | /***********************************************************************/ |
6220 | CorInfoHelpFunc CEEInfo::getCastingHelper(CORINFO_RESOLVED_TOKEN * pResolvedToken, bool fThrowing) |
6221 | { |
6222 | CONTRACTL { |
6223 | SO_TOLERANT; |
6224 | THROWS; |
6225 | GC_TRIGGERS; |
6226 | MODE_PREEMPTIVE; |
6227 | } CONTRACTL_END; |
6228 | |
6229 | if (isVerifyOnly()) |
6230 | return fThrowing ? CORINFO_HELP_CHKCASTANY : CORINFO_HELP_ISINSTANCEOFANY; |
6231 | |
6232 | CorInfoHelpFunc result = CORINFO_HELP_UNDEF; |
6233 | |
6234 | JIT_TO_EE_TRANSITION(); |
6235 | |
6236 | bool fClassMustBeRestored; |
6237 | result = getCastingHelperStatic(TypeHandle(pResolvedToken->hClass), fThrowing, &fClassMustBeRestored); |
6238 | if (fClassMustBeRestored && m_pOverride != NULL) |
6239 | m_pOverride->classMustBeLoadedBeforeCodeIsRun(pResolvedToken->hClass); |
6240 | |
6241 | EE_TO_JIT_TRANSITION(); |
6242 | |
6243 | return result; |
6244 | } |
6245 | |
6246 | /***********************************************************************/ |
6247 | CorInfoHelpFunc CEEInfo::getCastingHelperStatic(TypeHandle clsHnd, bool fThrowing, bool * pfClassMustBeRestored) |
6248 | { |
6249 | STANDARD_VM_CONTRACT; |
6250 | |
6251 | // Slow helper is the default |
6252 | int helper = CORINFO_HELP_ISINSTANCEOFANY; |
6253 | |
6254 | *pfClassMustBeRestored = false; |
6255 | |
6256 | if (clsHnd == TypeHandle(g_pCanonMethodTableClass)) |
6257 | { |
6258 | // In shared code just use the catch-all helper for type variables, as the same |
6259 | // code may be used for interface/array/class instantiations |
6260 | // |
6261 | // We may be able to take advantage of constraints to select a specialized helper. |
6262 | // This optimizations does not seem to be warranted at the moment. |
6263 | _ASSERTE(helper == CORINFO_HELP_ISINSTANCEOFANY); |
6264 | } |
6265 | else |
6266 | if (!clsHnd.IsTypeDesc() && clsHnd.AsMethodTable()->HasVariance()) |
6267 | { |
6268 | // Casting to variant type requires the type to be fully loaded |
6269 | *pfClassMustBeRestored = true; |
6270 | |
6271 | _ASSERTE(helper == CORINFO_HELP_ISINSTANCEOFANY); |
6272 | } |
6273 | else |
6274 | if (!clsHnd.IsTypeDesc() && clsHnd.AsMethodTable()->HasTypeEquivalence()) |
6275 | { |
6276 | // If the type can be equivalent with something, use the slow helper |
6277 | // Note: if the type of the instance is the one marked as equivalent, it will be |
6278 | // caught by the fast helpers in the same way as they catch transparent proxies. |
6279 | _ASSERTE(helper == CORINFO_HELP_ISINSTANCEOFANY); |
6280 | } |
6281 | else |
6282 | if (clsHnd.IsInterface()) |
6283 | { |
6284 | // If it is a non-variant interface, use the fast interface helper |
6285 | helper = CORINFO_HELP_ISINSTANCEOFINTERFACE; |
6286 | } |
6287 | else |
6288 | if (clsHnd.IsArray()) |
6289 | { |
6290 | if (clsHnd.AsArray()->GetInternalCorElementType() != ELEMENT_TYPE_SZARRAY) |
6291 | { |
6292 | // Casting to multidimensional array type requires restored pointer to EEClass to fetch rank |
6293 | *pfClassMustBeRestored = true; |
6294 | } |
6295 | |
6296 | // If it is an array, use the fast array helper |
6297 | helper = CORINFO_HELP_ISINSTANCEOFARRAY; |
6298 | } |
6299 | else |
6300 | if (!clsHnd.IsTypeDesc() && !Nullable::IsNullableType(clsHnd)) |
6301 | { |
6302 | // If it is a non-variant class, use the fast class helper |
6303 | helper = CORINFO_HELP_ISINSTANCEOFCLASS; |
6304 | } |
6305 | else |
6306 | { |
6307 | // Otherwise, use the slow helper |
6308 | _ASSERTE(helper == CORINFO_HELP_ISINSTANCEOFANY); |
6309 | } |
6310 | |
6311 | #ifdef FEATURE_PREJIT |
6312 | BOOL t1, t2, forceInstr; |
6313 | SystemDomain::GetCompilationOverrides(&t1, &t2, &forceInstr); |
6314 | if (forceInstr) |
6315 | { |
6316 | // If we're compiling for instrumentation, use the slowest but instrumented cast helper |
6317 | helper = CORINFO_HELP_ISINSTANCEOFANY; |
6318 | } |
6319 | #endif |
6320 | |
6321 | if (fThrowing) |
6322 | { |
6323 | const int delta = CORINFO_HELP_CHKCASTANY - CORINFO_HELP_ISINSTANCEOFANY; |
6324 | |
6325 | static_assert_no_msg(CORINFO_HELP_CHKCASTINTERFACE |
6326 | == CORINFO_HELP_ISINSTANCEOFINTERFACE + delta); |
6327 | static_assert_no_msg(CORINFO_HELP_CHKCASTARRAY |
6328 | == CORINFO_HELP_ISINSTANCEOFARRAY + delta); |
6329 | static_assert_no_msg(CORINFO_HELP_CHKCASTCLASS |
6330 | == CORINFO_HELP_ISINSTANCEOFCLASS + delta); |
6331 | |
6332 | helper += delta; |
6333 | } |
6334 | |
6335 | return (CorInfoHelpFunc)helper; |
6336 | } |
6337 | |
6338 | /***********************************************************************/ |
6339 | CorInfoHelpFunc CEEInfo::getSharedCCtorHelper(CORINFO_CLASS_HANDLE clsHnd) |
6340 | { |
6341 | CONTRACTL { |
6342 | SO_TOLERANT; |
6343 | NOTHROW; |
6344 | GC_NOTRIGGER; |
6345 | MODE_PREEMPTIVE; |
6346 | } CONTRACTL_END; |
6347 | |
6348 | CorInfoHelpFunc result = CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE; |
6349 | |
6350 | JIT_TO_EE_TRANSITION_LEAF(); |
6351 | |
6352 | TypeHandle cls(clsHnd); |
6353 | MethodTable* pMT = cls.AsMethodTable(); |
6354 | |
6355 | if (pMT->IsDynamicStatics()) |
6356 | { |
6357 | _ASSERTE(!cls.ContainsGenericVariables()); |
6358 | _ASSERTE(pMT->GetModuleDynamicEntryID() != (unsigned) -1); |
6359 | |
6360 | result = CORINFO_HELP_CLASSINIT_SHARED_DYNAMICCLASS; |
6361 | } |
6362 | |
6363 | EE_TO_JIT_TRANSITION_LEAF(); |
6364 | |
6365 | return result; |
6366 | } |
6367 | |
6368 | /***********************************************************************/ |
6369 | CorInfoHelpFunc CEEInfo::getUnBoxHelper(CORINFO_CLASS_HANDLE clsHnd) |
6370 | { |
6371 | LIMITED_METHOD_CONTRACT; |
6372 | |
6373 | if (m_pOverride != NULL) |
6374 | m_pOverride->classMustBeLoadedBeforeCodeIsRun(clsHnd); |
6375 | |
6376 | TypeHandle VMClsHnd(clsHnd); |
6377 | if (Nullable::IsNullableType(VMClsHnd)) |
6378 | return CORINFO_HELP_UNBOX_NULLABLE; |
6379 | |
6380 | return CORINFO_HELP_UNBOX; |
6381 | } |
6382 | |
6383 | /***********************************************************************/ |
6384 | bool CEEInfo::getReadyToRunHelper( |
6385 | CORINFO_RESOLVED_TOKEN * pResolvedToken, |
6386 | CORINFO_LOOKUP_KIND * pGenericLookupKind, |
6387 | CorInfoHelpFunc id, |
6388 | CORINFO_CONST_LOOKUP * pLookup |
6389 | ) |
6390 | { |
6391 | LIMITED_METHOD_CONTRACT; |
6392 | UNREACHABLE(); // only called during NGen |
6393 | } |
6394 | |
6395 | /***********************************************************************/ |
6396 | void CEEInfo::getReadyToRunDelegateCtorHelper( |
6397 | CORINFO_RESOLVED_TOKEN * pTargetMethod, |
6398 | CORINFO_CLASS_HANDLE delegateType, |
6399 | CORINFO_LOOKUP * pLookup |
6400 | ) |
6401 | { |
6402 | LIMITED_METHOD_CONTRACT; |
6403 | UNREACHABLE(); // only called during NGen |
6404 | } |
6405 | |
6406 | /***********************************************************************/ |
6407 | // see code:Nullable#NullableVerification |
6408 | |
6409 | CORINFO_CLASS_HANDLE CEEInfo::getTypeForBox(CORINFO_CLASS_HANDLE cls) |
6410 | { |
6411 | LIMITED_METHOD_CONTRACT; |
6412 | |
6413 | TypeHandle VMClsHnd(cls); |
6414 | if (Nullable::IsNullableType(VMClsHnd)) { |
6415 | VMClsHnd = VMClsHnd.AsMethodTable()->GetInstantiation()[0]; |
6416 | } |
6417 | return static_cast<CORINFO_CLASS_HANDLE>(VMClsHnd.AsPtr()); |
6418 | } |
6419 | |
6420 | /***********************************************************************/ |
6421 | // see code:Nullable#NullableVerification |
6422 | CorInfoHelpFunc CEEInfo::getBoxHelper(CORINFO_CLASS_HANDLE clsHnd) |
6423 | { |
6424 | CONTRACTL { |
6425 | SO_TOLERANT; |
6426 | THROWS; |
6427 | GC_TRIGGERS; |
6428 | MODE_PREEMPTIVE; |
6429 | } CONTRACTL_END; |
6430 | |
6431 | CorInfoHelpFunc result = CORINFO_HELP_UNDEF; |
6432 | |
6433 | JIT_TO_EE_TRANSITION(); |
6434 | |
6435 | TypeHandle VMClsHnd(clsHnd); |
6436 | if (Nullable::IsNullableType(VMClsHnd)) |
6437 | { |
6438 | result = CORINFO_HELP_BOX_NULLABLE; |
6439 | } |
6440 | else |
6441 | { |
6442 | if(VMClsHnd.IsTypeDesc()) |
6443 | COMPlusThrow(kInvalidOperationException,W("InvalidOperation_TypeCannotBeBoxed" )); |
6444 | |
6445 | // we shouldn't allow boxing of types that contains stack pointers |
6446 | // csc and vbc already disallow it. |
6447 | if (VMClsHnd.AsMethodTable()->IsByRefLike()) |
6448 | COMPlusThrow(kInvalidProgramException,W("NotSupported_ByRefLike" )); |
6449 | |
6450 | result = CORINFO_HELP_BOX; |
6451 | } |
6452 | |
6453 | EE_TO_JIT_TRANSITION(); |
6454 | |
6455 | return result; |
6456 | } |
6457 | |
6458 | /***********************************************************************/ |
6459 | CorInfoHelpFunc CEEInfo::getSecurityPrologHelper(CORINFO_METHOD_HANDLE ftn) |
6460 | { |
6461 | CONTRACTL { |
6462 | SO_TOLERANT; |
6463 | THROWS; |
6464 | GC_TRIGGERS; |
6465 | MODE_PREEMPTIVE; |
6466 | } CONTRACTL_END; |
6467 | |
6468 | CorInfoHelpFunc result = CORINFO_HELP_UNDEF; |
6469 | |
6470 | JIT_TO_EE_TRANSITION(); |
6471 | |
6472 | #ifdef FEATURE_NATIVE_IMAGE_GENERATION |
6473 | // This will make sure that when IBC logging is on, we call the slow helper with IBC probe |
6474 | if (IsCompilingForNGen() && |
6475 | GetAppDomain()->ToCompilationDomain()->m_fForceInstrument) |
6476 | { |
6477 | result = CORINFO_HELP_SECURITY_PROLOG_FRAMED; |
6478 | } |
6479 | #endif // FEATURE_NATIVE_IMAGE_GENERATION |
6480 | |
6481 | if (result == CORINFO_HELP_UNDEF) |
6482 | { |
6483 | result = CORINFO_HELP_SECURITY_PROLOG; |
6484 | } |
6485 | |
6486 | EE_TO_JIT_TRANSITION(); |
6487 | |
6488 | return result; |
6489 | } |
6490 | |
6491 | /***********************************************************************/ |
6492 | // registers a vararg sig & returns a class-specific cookie for it. |
6493 | |
6494 | CORINFO_VARARGS_HANDLE CEEInfo::getVarArgsHandle(CORINFO_SIG_INFO *sig, |
6495 | void **ppIndirection) |
6496 | { |
6497 | CONTRACTL { |
6498 | SO_TOLERANT; |
6499 | THROWS; |
6500 | GC_TRIGGERS; |
6501 | MODE_PREEMPTIVE; |
6502 | } CONTRACTL_END; |
6503 | |
6504 | CORINFO_VARARGS_HANDLE result = NULL; |
6505 | |
6506 | if (ppIndirection != NULL) |
6507 | *ppIndirection = NULL; |
6508 | |
6509 | JIT_TO_EE_TRANSITION(); |
6510 | |
6511 | Module* module = GetModule(sig->scope); |
6512 | |
6513 | result = CORINFO_VARARGS_HANDLE(module->GetVASigCookie(Signature(sig->pSig, sig->cbSig))); |
6514 | |
6515 | EE_TO_JIT_TRANSITION(); |
6516 | |
6517 | return result; |
6518 | } |
6519 | |
6520 | bool CEEInfo::canGetVarArgsHandle(CORINFO_SIG_INFO *sig) |
6521 | { |
6522 | LIMITED_METHOD_CONTRACT; |
6523 | return true; |
6524 | } |
6525 | |
6526 | /***********************************************************************/ |
6527 | unsigned CEEInfo::getMethodHash (CORINFO_METHOD_HANDLE ftnHnd) |
6528 | { |
6529 | CONTRACTL { |
6530 | SO_TOLERANT; |
6531 | THROWS; |
6532 | GC_TRIGGERS; |
6533 | MODE_PREEMPTIVE; |
6534 | } CONTRACTL_END; |
6535 | |
6536 | unsigned result = 0; |
6537 | |
6538 | JIT_TO_EE_TRANSITION(); |
6539 | |
6540 | MethodDesc* ftn = GetMethod(ftnHnd); |
6541 | |
6542 | result = (unsigned) ftn->GetStableHash(); |
6543 | |
6544 | EE_TO_JIT_TRANSITION(); |
6545 | |
6546 | return result; |
6547 | } |
6548 | |
6549 | /***********************************************************************/ |
6550 | const char* CEEInfo::getMethodName (CORINFO_METHOD_HANDLE ftnHnd, const char** scopeName) |
6551 | { |
6552 | CONTRACTL { |
6553 | SO_TOLERANT; |
6554 | THROWS; |
6555 | GC_TRIGGERS; |
6556 | MODE_PREEMPTIVE; |
6557 | } CONTRACTL_END; |
6558 | |
6559 | const char* result = NULL; |
6560 | |
6561 | JIT_TO_EE_TRANSITION(); |
6562 | |
6563 | MethodDesc *ftn; |
6564 | |
6565 | ftn = GetMethod(ftnHnd); |
6566 | |
6567 | if (scopeName != 0) |
6568 | { |
6569 | if (ftn->IsLCGMethod()) |
6570 | { |
6571 | *scopeName = "DynamicClass" ; |
6572 | } |
6573 | else if (ftn->IsILStub()) |
6574 | { |
6575 | *scopeName = ILStubResolver::GetStubClassName(ftn); |
6576 | } |
6577 | else |
6578 | { |
6579 | MethodTable * pMT = ftn->GetMethodTable(); |
6580 | #if defined(_DEBUG) |
6581 | #ifdef FEATURE_SYMDIFF |
6582 | if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_SymDiffDump)) |
6583 | { |
6584 | if (pMT->IsArray()) |
6585 | { |
6586 | ssClsNameBuff.Clear(); |
6587 | ssClsNameBuff.SetUTF8(pMT->GetDebugClassName()); |
6588 | } |
6589 | else |
6590 | pMT->_GetFullyQualifiedNameForClassNestedAware(ssClsNameBuff); |
6591 | } |
6592 | else |
6593 | { |
6594 | #endif |
6595 | // Calling _GetFullyQualifiedNameForClass in chk build is very expensive |
6596 | // since it construct the class name everytime we call this method. In chk |
6597 | // builds we already have a cheaper way to get the class name - |
6598 | // GetDebugClassName - which doesn't calculate the class name everytime. |
6599 | // This results in huge saving in Ngen time for checked builds. |
6600 | ssClsNameBuff.Clear(); |
6601 | ssClsNameBuff.SetUTF8(pMT->GetDebugClassName()); |
6602 | |
6603 | #ifdef FEATURE_SYMDIFF |
6604 | } |
6605 | #endif |
6606 | // Append generic instantiation at the end |
6607 | Instantiation inst = pMT->GetInstantiation(); |
6608 | if (!inst.IsEmpty()) |
6609 | TypeString::AppendInst(ssClsNameBuff, inst); |
6610 | |
6611 | *scopeName = ssClsNameBuff.GetUTF8(ssClsNameBuffScratch); |
6612 | #else // !_DEBUG |
6613 | // since this is for diagnostic purposes only, |
6614 | // give up on the namespace, as we don't have a buffer to concat it |
6615 | // also note this won't show array class names. |
6616 | LPCUTF8 nameSpace; |
6617 | *scopeName= pMT->GetFullyQualifiedNameInfo(&nameSpace); |
6618 | #endif // !_DEBUG |
6619 | } |
6620 | } |
6621 | |
6622 | result = ftn->GetName(); |
6623 | |
6624 | EE_TO_JIT_TRANSITION(); |
6625 | |
6626 | return result; |
6627 | } |
6628 | |
6629 | const char* CEEInfo::getMethodNameFromMetadata(CORINFO_METHOD_HANDLE ftnHnd, const char** className, const char** namespaceName, const char **enclosingClassName) |
6630 | { |
6631 | CONTRACTL { |
6632 | SO_TOLERANT; |
6633 | THROWS; |
6634 | GC_TRIGGERS; |
6635 | MODE_PREEMPTIVE; |
6636 | } CONTRACTL_END; |
6637 | |
6638 | const char* result = NULL; |
6639 | const char* classResult = NULL; |
6640 | const char* namespaceResult = NULL; |
6641 | const char* enclosingResult = NULL; |
6642 | |
6643 | JIT_TO_EE_TRANSITION(); |
6644 | |
6645 | MethodDesc *ftn = GetMethod(ftnHnd); |
6646 | mdMethodDef token = ftn->GetMemberDef(); |
6647 | |
6648 | if (!IsNilToken(token)) |
6649 | { |
6650 | MethodTable* pMT = ftn->GetMethodTable(); |
6651 | IMDInternalImport* pMDImport = pMT->GetMDImport(); |
6652 | |
6653 | IfFailThrow(pMDImport->GetNameOfMethodDef(token, &result)); |
6654 | IfFailThrow(pMDImport->GetNameOfTypeDef(pMT->GetCl(), &classResult, &namespaceResult)); |
6655 | // Query enclosingClassName when the method is in a nested class |
6656 | // and get the namespace of enclosing classes (nested class's namespace is empty) |
6657 | if (pMT->GetClass()->IsNested()) |
6658 | { |
6659 | IfFailThrow(pMDImport->GetNameOfTypeDef(pMT->GetEnclosingCl(), &enclosingResult, &namespaceResult)); |
6660 | } |
6661 | } |
6662 | |
6663 | if (className != NULL) |
6664 | { |
6665 | *className = classResult; |
6666 | } |
6667 | |
6668 | if (namespaceName != NULL) |
6669 | { |
6670 | *namespaceName = namespaceResult; |
6671 | } |
6672 | |
6673 | if (enclosingClassName != NULL) |
6674 | { |
6675 | *enclosingClassName = enclosingResult; |
6676 | } |
6677 | |
6678 | EE_TO_JIT_TRANSITION(); |
6679 | |
6680 | return result; |
6681 | } |
6682 | |
6683 | /*********************************************************************/ |
6684 | const char* CEEInfo::getClassNameFromMetadata(CORINFO_CLASS_HANDLE cls, const char** namespaceName) |
6685 | { |
6686 | CONTRACTL { |
6687 | SO_TOLERANT; |
6688 | THROWS; |
6689 | GC_TRIGGERS; |
6690 | MODE_PREEMPTIVE; |
6691 | } CONTRACTL_END; |
6692 | |
6693 | const char* result = NULL; |
6694 | const char* namespaceResult = NULL; |
6695 | |
6696 | JIT_TO_EE_TRANSITION(); |
6697 | TypeHandle VMClsHnd(cls); |
6698 | |
6699 | if (!VMClsHnd.IsTypeDesc()) |
6700 | { |
6701 | result = VMClsHnd.AsMethodTable()->GetFullyQualifiedNameInfo(&namespaceResult); |
6702 | } |
6703 | |
6704 | if (namespaceName != NULL) |
6705 | { |
6706 | *namespaceName = namespaceResult; |
6707 | } |
6708 | |
6709 | EE_TO_JIT_TRANSITION(); |
6710 | |
6711 | return result; |
6712 | } |
6713 | |
6714 | /*********************************************************************/ |
6715 | CORINFO_CLASS_HANDLE CEEInfo::getTypeInstantiationArgument(CORINFO_CLASS_HANDLE cls, unsigned index) |
6716 | { |
6717 | CONTRACTL { |
6718 | SO_TOLERANT; |
6719 | THROWS; |
6720 | GC_TRIGGERS; |
6721 | MODE_PREEMPTIVE; |
6722 | } CONTRACTL_END; |
6723 | |
6724 | CORINFO_CLASS_HANDLE result = NULL; |
6725 | |
6726 | JIT_TO_EE_TRANSITION_LEAF(); |
6727 | |
6728 | TypeHandle VMClsHnd(cls); |
6729 | Instantiation inst = VMClsHnd.GetInstantiation(); |
6730 | TypeHandle typeArg = index < inst.GetNumArgs() ? inst[index] : NULL; |
6731 | result = CORINFO_CLASS_HANDLE(typeArg.AsPtr()); |
6732 | |
6733 | EE_TO_JIT_TRANSITION_LEAF(); |
6734 | |
6735 | return result; |
6736 | } |
6737 | |
6738 | /*********************************************************************/ |
6739 | DWORD CEEInfo::getMethodAttribs (CORINFO_METHOD_HANDLE ftn) |
6740 | { |
6741 | CONTRACTL { |
6742 | SO_TOLERANT; |
6743 | THROWS; |
6744 | GC_TRIGGERS; |
6745 | MODE_PREEMPTIVE; |
6746 | } CONTRACTL_END; |
6747 | |
6748 | DWORD result = 0; |
6749 | |
6750 | JIT_TO_EE_TRANSITION(); |
6751 | |
6752 | result = getMethodAttribsInternal(ftn); |
6753 | |
6754 | EE_TO_JIT_TRANSITION(); |
6755 | |
6756 | return result; |
6757 | } |
6758 | |
6759 | /*********************************************************************/ |
6760 | DWORD CEEInfo::getMethodAttribsInternal (CORINFO_METHOD_HANDLE ftn) |
6761 | { |
6762 | STANDARD_VM_CONTRACT; |
6763 | |
6764 | /* |
6765 | returns method attribute flags (defined in corhdr.h) |
6766 | |
6767 | NOTE: This doesn't return certain method flags |
6768 | (mdAssem, mdFamANDAssem, mdFamORAssem, mdPrivateScope) |
6769 | */ |
6770 | |
6771 | MethodDesc* pMD = GetMethod(ftn); |
6772 | |
6773 | if (pMD->IsLCGMethod()) |
6774 | { |
6775 | return CORINFO_FLG_STATIC | CORINFO_FLG_DONT_INLINE | CORINFO_FLG_NOSECURITYWRAP; |
6776 | } |
6777 | |
6778 | DWORD result = CORINFO_FLG_NOSECURITYWRAP; |
6779 | |
6780 | // <REVISIT_TODO>@todo: can we git rid of CORINFO_FLG_ stuff and just include cor.h?</REVISIT_TODO> |
6781 | |
6782 | DWORD attribs = pMD->GetAttrs(); |
6783 | |
6784 | if (IsMdFamily(attribs)) |
6785 | result |= CORINFO_FLG_PROTECTED; |
6786 | if (IsMdStatic(attribs)) |
6787 | result |= CORINFO_FLG_STATIC; |
6788 | if (pMD->IsSynchronized()) |
6789 | result |= CORINFO_FLG_SYNCH; |
6790 | if (pMD->IsFCallOrIntrinsic()) |
6791 | result |= CORINFO_FLG_NOGCCHECK | CORINFO_FLG_INTRINSIC; |
6792 | if (pMD->IsJitIntrinsic()) |
6793 | result |= CORINFO_FLG_JIT_INTRINSIC; |
6794 | if (IsMdVirtual(attribs)) |
6795 | result |= CORINFO_FLG_VIRTUAL; |
6796 | if (IsMdAbstract(attribs)) |
6797 | result |= CORINFO_FLG_ABSTRACT; |
6798 | if (IsMdRTSpecialName(attribs)) |
6799 | { |
6800 | LPCUTF8 pName = pMD->GetName(); |
6801 | if (IsMdInstanceInitializer(attribs, pName) || |
6802 | IsMdClassConstructor(attribs, pName)) |
6803 | result |= CORINFO_FLG_CONSTRUCTOR; |
6804 | } |
6805 | |
6806 | // |
6807 | // See if we need to embed a .cctor call at the head of the |
6808 | // method body. |
6809 | // |
6810 | |
6811 | MethodTable* pMT = pMD->GetMethodTable(); |
6812 | |
6813 | // method or class might have the final bit |
6814 | if (IsMdFinal(attribs) || pMT->IsSealed()) |
6815 | { |
6816 | result |= CORINFO_FLG_FINAL; |
6817 | } |
6818 | |
6819 | if (pMD->IsEnCAddedMethod()) |
6820 | { |
6821 | result |= CORINFO_FLG_EnC; |
6822 | } |
6823 | |
6824 | if (pMD->IsSharedByGenericInstantiations()) |
6825 | { |
6826 | result |= CORINFO_FLG_SHAREDINST; |
6827 | } |
6828 | |
6829 | if (pMD->IsNDirect()) |
6830 | { |
6831 | result |= CORINFO_FLG_PINVOKE; |
6832 | } |
6833 | |
6834 | if (IsMdRequireSecObject(attribs)) |
6835 | { |
6836 | // Assume all methods marked as DynamicSecurity are |
6837 | // marked that way because they use StackCrawlMark to identify |
6838 | // the caller. |
6839 | // See comments in canInline or canTailCall |
6840 | result |= CORINFO_FLG_DONT_INLINE_CALLER; |
6841 | } |
6842 | |
6843 | // Check for the aggressive optimization directive. AggressiveOptimization only makes sense for IL methods. |
6844 | DWORD ilMethodImplAttribs = 0; |
6845 | if (pMD->IsIL()) |
6846 | { |
6847 | ilMethodImplAttribs = pMD->GetImplAttrs(); |
6848 | if (IsMiAggressiveOptimization(ilMethodImplAttribs)) |
6849 | { |
6850 | result |= CORINFO_FLG_AGGRESSIVE_OPT; |
6851 | } |
6852 | } |
6853 | |
6854 | // Check for an inlining directive. |
6855 | if (pMD->IsNotInline()) |
6856 | { |
6857 | /* Function marked as not inlineable */ |
6858 | result |= CORINFO_FLG_DONT_INLINE; |
6859 | } |
6860 | // AggressiveInlining only makes sense for IL methods. |
6861 | else if (pMD->IsIL() && IsMiAggressiveInlining(ilMethodImplAttribs)) |
6862 | { |
6863 | result |= CORINFO_FLG_FORCEINLINE; |
6864 | } |
6865 | |
6866 | if (pMT->IsDelegate() && ((DelegateEEClass*)(pMT->GetClass()))->GetInvokeMethod() == pMD) |
6867 | { |
6868 | // This is now used to emit efficient invoke code for any delegate invoke, |
6869 | // including multicast. |
6870 | result |= CORINFO_FLG_DELEGATE_INVOKE; |
6871 | } |
6872 | |
6873 | return result; |
6874 | } |
6875 | |
6876 | /*********************************************************************/ |
6877 | void CEEInfo::setMethodAttribs ( |
6878 | CORINFO_METHOD_HANDLE ftnHnd, |
6879 | CorInfoMethodRuntimeFlags attribs) |
6880 | { |
6881 | CONTRACTL { |
6882 | SO_TOLERANT; |
6883 | THROWS; |
6884 | GC_TRIGGERS; |
6885 | MODE_PREEMPTIVE; |
6886 | } CONTRACTL_END; |
6887 | |
6888 | JIT_TO_EE_TRANSITION(); |
6889 | |
6890 | MethodDesc* ftn = GetMethod(ftnHnd); |
6891 | |
6892 | if (attribs & CORINFO_FLG_BAD_INLINEE) |
6893 | { |
6894 | BOOL fCacheInliningHint = TRUE; |
6895 | |
6896 | #ifdef FEATURE_NATIVE_IMAGE_GENERATION |
6897 | if (IsCompilationProcess()) |
6898 | { |
6899 | // Since we are running managed code during NGen the inlining hint may be |
6900 | // changing underneeth us as the code is JITed. We need to prevent the inlining |
6901 | // hints from changing once we start to use them to place IL in the image. |
6902 | if (!g_pCEECompileInfo->IsCachingOfInliningHintsEnabled()) |
6903 | { |
6904 | fCacheInliningHint = FALSE; |
6905 | } |
6906 | else |
6907 | { |
6908 | // Don't cache inlining hints inside mscorlib during NGen of other assemblies, |
6909 | // since mscorlib is loaded domain neutral and will survive worker process recycling, |
6910 | // causing determinism problems. |
6911 | Module * pModule = ftn->GetModule(); |
6912 | if (pModule->IsSystem() && pModule->HasNativeImage()) |
6913 | { |
6914 | fCacheInliningHint = FALSE; |
6915 | } |
6916 | } |
6917 | } |
6918 | #endif |
6919 | |
6920 | if (fCacheInliningHint) |
6921 | { |
6922 | ftn->SetNotInline(true); |
6923 | } |
6924 | } |
6925 | |
6926 | EE_TO_JIT_TRANSITION(); |
6927 | } |
6928 | |
6929 | /*********************************************************************/ |
6930 | |
6931 | void getMethodInfoILMethodHeaderHelper( |
6932 | COR_ILMETHOD_DECODER* header, |
6933 | CORINFO_METHOD_INFO* methInfo |
6934 | ) |
6935 | { |
6936 | LIMITED_METHOD_CONTRACT; |
6937 | |
6938 | methInfo->ILCode = const_cast<BYTE*>(header->Code); |
6939 | methInfo->ILCodeSize = header->GetCodeSize(); |
6940 | methInfo->maxStack = static_cast<unsigned short>(header->GetMaxStack()); |
6941 | methInfo->EHcount = static_cast<unsigned short>(header->EHCount()); |
6942 | |
6943 | methInfo->options = |
6944 | (CorInfoOptions)((header->GetFlags() & CorILMethod_InitLocals) ? CORINFO_OPT_INIT_LOCALS : 0) ; |
6945 | } |
6946 | |
6947 | mdToken FindGenericMethodArgTypeSpec(IMDInternalImport* pInternalImport) |
6948 | { |
6949 | STANDARD_VM_CONTRACT; |
6950 | |
6951 | HENUMInternalHolder hEnumTypeSpecs(pInternalImport); |
6952 | mdToken token; |
6953 | |
6954 | static const BYTE signature[] = { ELEMENT_TYPE_MVAR, 0 }; |
6955 | |
6956 | hEnumTypeSpecs.EnumAllInit(mdtTypeSpec); |
6957 | while (hEnumTypeSpecs.EnumNext(&token)) |
6958 | { |
6959 | PCCOR_SIGNATURE pSig; |
6960 | ULONG cbSig; |
6961 | IfFailThrow(pInternalImport->GetTypeSpecFromToken(token, &pSig, &cbSig)); |
6962 | if (cbSig == sizeof(signature) && memcmp(pSig, signature, cbSig) == 0) |
6963 | return token; |
6964 | } |
6965 | |
6966 | COMPlusThrowHR(COR_E_BADIMAGEFORMAT); |
6967 | } |
6968 | |
6969 | /********************************************************************* |
6970 | |
6971 | IL is the most efficient and portable way to implement certain low level methods |
6972 | in mscorlib.dll. Unfortunately, there is no good way to link IL into mscorlib.dll today. |
6973 | Until we find a good way to link IL into mscorlib.dll, we will provide the IL implementation here. |
6974 | |
6975 | - All IL intrinsincs are members of System.Runtime.CompilerServices.JitHelpers class |
6976 | - All IL intrinsincs should be kept very simple. Implement the minimal reusable version of |
6977 | unsafe construct and depend on inlining to do the rest. |
6978 | - The C# implementation of the IL intrinsic should be good enough for functionalily. Everything should work |
6979 | correctly (but slower) if the IL intrinsics are removed. |
6980 | |
6981 | *********************************************************************/ |
6982 | |
6983 | bool getILIntrinsicImplementation(MethodDesc * ftn, |
6984 | CORINFO_METHOD_INFO * methInfo) |
6985 | { |
6986 | STANDARD_VM_CONTRACT; |
6987 | |
6988 | // Precondition: ftn is a method in mscorlib |
6989 | _ASSERTE(ftn->GetModule()->IsSystem()); |
6990 | |
6991 | mdMethodDef tk = ftn->GetMemberDef(); |
6992 | |
6993 | // Compare tokens to cover all generic instantiations |
6994 | // The body of the first method is simply ret Arg0. The second one first casts the arg to I4. |
6995 | |
6996 | if (tk == MscorlibBinder::GetMethod(METHOD__JIT_HELPERS__ENUM_EQUALS)->GetMemberDef()) |
6997 | { |
6998 | // Normally we would follow the above pattern and unconditionally replace the IL, |
6999 | // relying on generic type constraints to guarantee that it will only ever be instantiated |
7000 | // on the type/size of argument we expect. |
7001 | // |
7002 | // However C#/CLR does not support restricting a generic type to be an Enum, so the best |
7003 | // we can do is constrain it to be a value type. This is fine for run time, since we only |
7004 | // ever create instantiations on 4 byte or less Enums. But during NGen we may compile instantiations |
7005 | // on other value types (to be specific, every value type instatiation of EqualityComparer |
7006 | // because of its TypeDependencyAttribute; here again we would like to restrict this to |
7007 | // 4 byte or less Enums but cannot). |
7008 | // |
7009 | // This IL is invalid for those instantiations, and replacing it would lead to all sorts of |
7010 | // errors at NGen time. So we only replace it for instantiations where it would be valid, |
7011 | // leaving the others, which we should never execute, with the C# implementation of throwing. |
7012 | |
7013 | _ASSERTE(ftn->HasMethodInstantiation()); |
7014 | Instantiation inst = ftn->GetMethodInstantiation(); |
7015 | |
7016 | _ASSERTE(inst.GetNumArgs() == 1); |
7017 | CorElementType et = inst[0].GetVerifierCorElementType(); |
7018 | if (et == ELEMENT_TYPE_I4 || |
7019 | et == ELEMENT_TYPE_U4 || |
7020 | et == ELEMENT_TYPE_I2 || |
7021 | et == ELEMENT_TYPE_U2 || |
7022 | et == ELEMENT_TYPE_I1 || |
7023 | et == ELEMENT_TYPE_U1 || |
7024 | et == ELEMENT_TYPE_I8 || |
7025 | et == ELEMENT_TYPE_U8) |
7026 | { |
7027 | static const BYTE ilcode[] = { CEE_LDARG_0, CEE_LDARG_1, CEE_PREFIX1, (CEE_CEQ & 0xFF), CEE_RET }; |
7028 | methInfo->ILCode = const_cast<BYTE*>(ilcode); |
7029 | methInfo->ILCodeSize = sizeof(ilcode); |
7030 | methInfo->maxStack = 2; |
7031 | methInfo->EHcount = 0; |
7032 | methInfo->options = (CorInfoOptions)0; |
7033 | return true; |
7034 | } |
7035 | } |
7036 | else if (tk == MscorlibBinder::GetMethod(METHOD__JIT_HELPERS__ENUM_COMPARE_TO)->GetMemberDef()) |
7037 | { |
7038 | // The the comment above on why this is is not an unconditional replacement. This case handles |
7039 | // Enums backed by 8 byte values. |
7040 | |
7041 | _ASSERTE(ftn->HasMethodInstantiation()); |
7042 | Instantiation inst = ftn->GetMethodInstantiation(); |
7043 | |
7044 | _ASSERTE(inst.GetNumArgs() == 1); |
7045 | CorElementType et = inst[0].GetVerifierCorElementType(); |
7046 | if (et == ELEMENT_TYPE_I4 || |
7047 | et == ELEMENT_TYPE_U4 || |
7048 | et == ELEMENT_TYPE_I2 || |
7049 | et == ELEMENT_TYPE_U2 || |
7050 | et == ELEMENT_TYPE_I1 || |
7051 | et == ELEMENT_TYPE_U1 || |
7052 | et == ELEMENT_TYPE_I8 || |
7053 | et == ELEMENT_TYPE_U8) |
7054 | { |
7055 | static BYTE ilcode[8][9]; |
7056 | |
7057 | TypeHandle thUnderlyingType = MscorlibBinder::GetElementType(et); |
7058 | |
7059 | TypeHandle thIComparable = TypeHandle(MscorlibBinder::GetClass(CLASS__ICOMPARABLEGENERIC)).Instantiate(Instantiation(&thUnderlyingType, 1)); |
7060 | |
7061 | MethodDesc * pCompareToMD = thUnderlyingType.AsMethodTable()->GetMethodDescForInterfaceMethod( |
7062 | thIComparable, MscorlibBinder::GetMethod(METHOD__ICOMPARABLEGENERIC__COMPARE_TO), TRUE /* throwOnConflict */); |
7063 | |
7064 | // Call CompareTo method on the primitive type |
7065 | int tokCompareTo = pCompareToMD->GetMemberDef(); |
7066 | |
7067 | int index = (et - ELEMENT_TYPE_I1); |
7068 | _ASSERTE(index < _countof(ilcode)); |
7069 | |
7070 | ilcode[index][0] = CEE_LDARGA_S; |
7071 | ilcode[index][1] = 0; |
7072 | ilcode[index][2] = CEE_LDARG_1; |
7073 | ilcode[index][3] = CEE_CALL; |
7074 | ilcode[index][4] = (BYTE)(tokCompareTo); |
7075 | ilcode[index][5] = (BYTE)(tokCompareTo >> 8); |
7076 | ilcode[index][6] = (BYTE)(tokCompareTo >> 16); |
7077 | ilcode[index][7] = (BYTE)(tokCompareTo >> 24); |
7078 | ilcode[index][8] = CEE_RET; |
7079 | |
7080 | methInfo->ILCode = const_cast<BYTE*>(ilcode[index]); |
7081 | methInfo->ILCodeSize = sizeof(ilcode[index]); |
7082 | methInfo->maxStack = 2; |
7083 | methInfo->EHcount = 0; |
7084 | methInfo->options = (CorInfoOptions)0; |
7085 | return true; |
7086 | } |
7087 | } |
7088 | else if (tk == MscorlibBinder::GetMethod(METHOD__JIT_HELPERS__GET_RAW_SZ_ARRAY_DATA)->GetMemberDef()) |
7089 | { |
7090 | mdToken tokRawSzArrayData = MscorlibBinder::GetField(FIELD__RAW_SZARRAY_DATA__DATA)->GetMemberDef(); |
7091 | |
7092 | static BYTE ilcode[] = { CEE_LDARG_0, |
7093 | CEE_LDFLDA,0,0,0,0, |
7094 | CEE_RET }; |
7095 | |
7096 | ilcode[2] = (BYTE)(tokRawSzArrayData); |
7097 | ilcode[3] = (BYTE)(tokRawSzArrayData >> 8); |
7098 | ilcode[4] = (BYTE)(tokRawSzArrayData >> 16); |
7099 | ilcode[5] = (BYTE)(tokRawSzArrayData >> 24); |
7100 | |
7101 | methInfo->ILCode = const_cast<BYTE*>(ilcode); |
7102 | methInfo->ILCodeSize = sizeof(ilcode); |
7103 | methInfo->maxStack = 1; |
7104 | methInfo->EHcount = 0; |
7105 | methInfo->options = (CorInfoOptions)0; |
7106 | return true; |
7107 | } |
7108 | |
7109 | return false; |
7110 | } |
7111 | |
7112 | bool getILIntrinsicImplementationForUnsafe(MethodDesc * ftn, |
7113 | CORINFO_METHOD_INFO * methInfo) |
7114 | { |
7115 | STANDARD_VM_CONTRACT; |
7116 | |
7117 | // Precondition: ftn is a method in mscorlib |
7118 | _ASSERTE(ftn->GetModule()->IsSystem()); |
7119 | |
7120 | mdMethodDef tk = ftn->GetMemberDef(); |
7121 | |
7122 | if (tk == MscorlibBinder::GetMethod(METHOD__UNSAFE__AS_POINTER)->GetMemberDef()) |
7123 | { |
7124 | // Return the argument that was passed in. |
7125 | static const BYTE ilcode[] = { CEE_LDARG_0, CEE_CONV_U, CEE_RET }; |
7126 | methInfo->ILCode = const_cast<BYTE*>(ilcode); |
7127 | methInfo->ILCodeSize = sizeof(ilcode); |
7128 | methInfo->maxStack = 1; |
7129 | methInfo->EHcount = 0; |
7130 | methInfo->options = (CorInfoOptions)0; |
7131 | return true; |
7132 | } |
7133 | if (tk == MscorlibBinder::GetMethod(METHOD__UNSAFE__SIZEOF)->GetMemberDef()) |
7134 | { |
7135 | _ASSERTE(ftn->HasMethodInstantiation()); |
7136 | Instantiation inst = ftn->GetMethodInstantiation(); |
7137 | |
7138 | _ASSERTE(ftn->GetNumGenericMethodArgs() == 1); |
7139 | mdToken tokGenericArg = FindGenericMethodArgTypeSpec(MscorlibBinder::GetModule()->GetMDImport()); |
7140 | |
7141 | static BYTE ilcode[] = { CEE_PREFIX1, (CEE_SIZEOF & 0xFF), 0,0,0,0, CEE_RET }; |
7142 | |
7143 | ilcode[2] = (BYTE)(tokGenericArg); |
7144 | ilcode[3] = (BYTE)(tokGenericArg >> 8); |
7145 | ilcode[4] = (BYTE)(tokGenericArg >> 16); |
7146 | ilcode[5] = (BYTE)(tokGenericArg >> 24); |
7147 | |
7148 | methInfo->ILCode = const_cast<BYTE*>(ilcode); |
7149 | methInfo->ILCodeSize = sizeof(ilcode); |
7150 | methInfo->maxStack = 1; |
7151 | methInfo->EHcount = 0; |
7152 | methInfo->options = (CorInfoOptions)0; |
7153 | return true; |
7154 | } |
7155 | else if (tk == MscorlibBinder::GetMethod(METHOD__UNSAFE__BYREF_AS)->GetMemberDef() || |
7156 | tk == MscorlibBinder::GetMethod(METHOD__UNSAFE__OBJECT_AS)->GetMemberDef() || |
7157 | tk == MscorlibBinder::GetMethod(METHOD__UNSAFE__AS_REF_POINTER)->GetMemberDef() || |
7158 | tk == MscorlibBinder::GetMethod(METHOD__UNSAFE__AS_REF_IN)->GetMemberDef()) |
7159 | { |
7160 | // Return the argument that was passed in. |
7161 | static const BYTE ilcode[] = { CEE_LDARG_0, CEE_RET }; |
7162 | methInfo->ILCode = const_cast<BYTE*>(ilcode); |
7163 | methInfo->ILCodeSize = sizeof(ilcode); |
7164 | methInfo->maxStack = 1; |
7165 | methInfo->EHcount = 0; |
7166 | methInfo->options = (CorInfoOptions)0; |
7167 | return true; |
7168 | } |
7169 | else if (tk == MscorlibBinder::GetMethod(METHOD__UNSAFE__BYREF_ADD)->GetMemberDef() || |
7170 | tk == MscorlibBinder::GetMethod(METHOD__UNSAFE__PTR_ADD)->GetMemberDef()) |
7171 | { |
7172 | mdToken tokGenericArg = FindGenericMethodArgTypeSpec(MscorlibBinder::GetModule()->GetMDImport()); |
7173 | |
7174 | static BYTE ilcode[] = { CEE_LDARG_1, |
7175 | CEE_PREFIX1, (CEE_SIZEOF & 0xFF), 0,0,0,0, |
7176 | CEE_CONV_I, |
7177 | CEE_MUL, |
7178 | CEE_LDARG_0, |
7179 | CEE_ADD, |
7180 | CEE_RET }; |
7181 | |
7182 | ilcode[3] = (BYTE)(tokGenericArg); |
7183 | ilcode[4] = (BYTE)(tokGenericArg >> 8); |
7184 | ilcode[5] = (BYTE)(tokGenericArg >> 16); |
7185 | ilcode[6] = (BYTE)(tokGenericArg >> 24); |
7186 | |
7187 | methInfo->ILCode = const_cast<BYTE*>(ilcode); |
7188 | methInfo->ILCodeSize = sizeof(ilcode); |
7189 | methInfo->maxStack = 2; |
7190 | methInfo->EHcount = 0; |
7191 | methInfo->options = (CorInfoOptions)0; |
7192 | return true; |
7193 | } |
7194 | else if (tk == MscorlibBinder::GetMethod(METHOD__UNSAFE__BYREF_INTPTR_ADD)->GetMemberDef()) |
7195 | { |
7196 | mdToken tokGenericArg = FindGenericMethodArgTypeSpec(MscorlibBinder::GetModule()->GetMDImport()); |
7197 | |
7198 | static BYTE ilcode[] = { CEE_LDARG_1, |
7199 | CEE_PREFIX1, (CEE_SIZEOF & 0xFF), 0,0,0,0, |
7200 | CEE_MUL, |
7201 | CEE_LDARG_0, |
7202 | CEE_ADD, |
7203 | CEE_RET }; |
7204 | |
7205 | ilcode[3] = (BYTE)(tokGenericArg); |
7206 | ilcode[4] = (BYTE)(tokGenericArg >> 8); |
7207 | ilcode[5] = (BYTE)(tokGenericArg >> 16); |
7208 | ilcode[6] = (BYTE)(tokGenericArg >> 24); |
7209 | |
7210 | methInfo->ILCode = const_cast<BYTE*>(ilcode); |
7211 | methInfo->ILCodeSize = sizeof(ilcode); |
7212 | methInfo->maxStack = 2; |
7213 | methInfo->EHcount = 0; |
7214 | methInfo->options = (CorInfoOptions)0; |
7215 | return true; |
7216 | } |
7217 | else if (tk == MscorlibBinder::GetMethod(METHOD__UNSAFE__BYREF_ADD_BYTE_OFFSET)->GetMemberDef()) |
7218 | { |
7219 | static BYTE ilcode[] = { CEE_LDARG_0, CEE_LDARG_1, CEE_ADD, CEE_RET }; |
7220 | |
7221 | methInfo->ILCode = const_cast<BYTE*>(ilcode); |
7222 | methInfo->ILCodeSize = sizeof(ilcode); |
7223 | methInfo->maxStack = 2; |
7224 | methInfo->EHcount = 0; |
7225 | methInfo->options = (CorInfoOptions)0; |
7226 | return true; |
7227 | } |
7228 | else if (tk == MscorlibBinder::GetMethod(METHOD__UNSAFE__BYREF_ARE_SAME)->GetMemberDef()) |
7229 | { |
7230 | // Compare the two arguments |
7231 | static const BYTE ilcode[] = { CEE_LDARG_0, CEE_LDARG_1, CEE_PREFIX1, (CEE_CEQ & 0xFF), CEE_RET }; |
7232 | methInfo->ILCode = const_cast<BYTE*>(ilcode); |
7233 | methInfo->ILCodeSize = sizeof(ilcode); |
7234 | methInfo->maxStack = 2; |
7235 | methInfo->EHcount = 0; |
7236 | methInfo->options = (CorInfoOptions)0; |
7237 | return true; |
7238 | } |
7239 | else if (tk == MscorlibBinder::GetMethod(METHOD__UNSAFE__BYREF_IS_ADDRESS_GREATER_THAN)->GetMemberDef()) |
7240 | { |
7241 | // Compare the two arguments |
7242 | static const BYTE ilcode[] = { CEE_LDARG_0, CEE_LDARG_1, CEE_PREFIX1, (CEE_CGT_UN & 0xFF), CEE_RET }; |
7243 | methInfo->ILCode = const_cast<BYTE*>(ilcode); |
7244 | methInfo->ILCodeSize = sizeof(ilcode); |
7245 | methInfo->maxStack = 2; |
7246 | methInfo->EHcount = 0; |
7247 | methInfo->options = (CorInfoOptions)0; |
7248 | return true; |
7249 | } |
7250 | else if (tk == MscorlibBinder::GetMethod(METHOD__UNSAFE__BYREF_IS_ADDRESS_LESS_THAN)->GetMemberDef()) |
7251 | { |
7252 | // Compare the two arguments |
7253 | static const BYTE ilcode[] = { CEE_LDARG_0, CEE_LDARG_1, CEE_PREFIX1, (CEE_CLT_UN & 0xFF), CEE_RET }; |
7254 | methInfo->ILCode = const_cast<BYTE*>(ilcode); |
7255 | methInfo->ILCodeSize = sizeof(ilcode); |
7256 | methInfo->maxStack = 2; |
7257 | methInfo->EHcount = 0; |
7258 | methInfo->options = (CorInfoOptions)0; |
7259 | return true; |
7260 | } |
7261 | else if (tk == MscorlibBinder::GetMethod(METHOD__UNSAFE__BYREF_INIT_BLOCK_UNALIGNED)->GetMemberDef()) |
7262 | { |
7263 | static const BYTE ilcode[] = { CEE_LDARG_0, CEE_LDARG_1, CEE_LDARG_2, CEE_PREFIX1, (CEE_UNALIGNED & 0xFF), 0x01, CEE_PREFIX1, (CEE_INITBLK & 0xFF), CEE_RET }; |
7264 | methInfo->ILCode = const_cast<BYTE*>(ilcode); |
7265 | methInfo->ILCodeSize = sizeof(ilcode); |
7266 | methInfo->maxStack = 3; |
7267 | methInfo->EHcount = 0; |
7268 | methInfo->options = (CorInfoOptions)0; |
7269 | return true; |
7270 | } |
7271 | else if (tk == MscorlibBinder::GetMethod(METHOD__UNSAFE__BYREF_BYTE_OFFSET)->GetMemberDef()) |
7272 | { |
7273 | static const BYTE ilcode[] = |
7274 | { |
7275 | CEE_LDARG_1, |
7276 | CEE_LDARG_0, |
7277 | CEE_SUB, |
7278 | CEE_RET |
7279 | }; |
7280 | |
7281 | methInfo->ILCode = const_cast<BYTE*>(ilcode); |
7282 | methInfo->ILCodeSize = sizeof(ilcode); |
7283 | methInfo->maxStack = 2; |
7284 | methInfo->EHcount = 0; |
7285 | methInfo->options = (CorInfoOptions)0; |
7286 | return true; |
7287 | } |
7288 | else if (tk == MscorlibBinder::GetMethod(METHOD__UNSAFE__BYREF_READ_UNALIGNED)->GetMemberDef() || |
7289 | tk == MscorlibBinder::GetMethod(METHOD__UNSAFE__PTR_READ_UNALIGNED)->GetMemberDef()) |
7290 | { |
7291 | _ASSERTE(ftn->HasMethodInstantiation()); |
7292 | Instantiation inst = ftn->GetMethodInstantiation(); |
7293 | _ASSERTE(ftn->GetNumGenericMethodArgs() == 1); |
7294 | mdToken tokGenericArg = FindGenericMethodArgTypeSpec(MscorlibBinder::GetModule()->GetMDImport()); |
7295 | |
7296 | static const BYTE ilcode[] |
7297 | { |
7298 | CEE_LDARG_0, |
7299 | CEE_PREFIX1, (CEE_UNALIGNED & 0xFF), 1, |
7300 | CEE_LDOBJ, (BYTE)(tokGenericArg), (BYTE)(tokGenericArg >> 8), (BYTE)(tokGenericArg >> 16), (BYTE)(tokGenericArg >> 24), |
7301 | CEE_RET |
7302 | }; |
7303 | |
7304 | methInfo->ILCode = const_cast<BYTE*>(ilcode); |
7305 | methInfo->ILCodeSize = sizeof(ilcode); |
7306 | methInfo->maxStack = 2; |
7307 | methInfo->EHcount = 0; |
7308 | methInfo->options = (CorInfoOptions)0; |
7309 | return true; |
7310 | } |
7311 | else if (tk == MscorlibBinder::GetMethod(METHOD__UNSAFE__BYREF_WRITE_UNALIGNED)->GetMemberDef() || |
7312 | tk == MscorlibBinder::GetMethod(METHOD__UNSAFE__PTR_WRITE_UNALIGNED)->GetMemberDef()) |
7313 | { |
7314 | _ASSERTE(ftn->HasMethodInstantiation()); |
7315 | Instantiation inst = ftn->GetMethodInstantiation(); |
7316 | _ASSERTE(ftn->GetNumGenericMethodArgs() == 1); |
7317 | mdToken tokGenericArg = FindGenericMethodArgTypeSpec(MscorlibBinder::GetModule()->GetMDImport()); |
7318 | |
7319 | static const BYTE ilcode[] |
7320 | { |
7321 | CEE_LDARG_0, |
7322 | CEE_LDARG_1, |
7323 | CEE_PREFIX1, (CEE_UNALIGNED & 0xFF), 1, |
7324 | CEE_STOBJ, (BYTE)(tokGenericArg), (BYTE)(tokGenericArg >> 8), (BYTE)(tokGenericArg >> 16), (BYTE)(tokGenericArg >> 24), |
7325 | CEE_RET |
7326 | }; |
7327 | |
7328 | methInfo->ILCode = const_cast<BYTE*>(ilcode); |
7329 | methInfo->ILCodeSize = sizeof(ilcode); |
7330 | methInfo->maxStack = 2; |
7331 | methInfo->EHcount = 0; |
7332 | methInfo->options = (CorInfoOptions)0; |
7333 | return true; |
7334 | } |
7335 | |
7336 | return false; |
7337 | } |
7338 | |
7339 | bool getILIntrinsicImplementationForVolatile(MethodDesc * ftn, |
7340 | CORINFO_METHOD_INFO * methInfo) |
7341 | { |
7342 | STANDARD_VM_CONTRACT; |
7343 | |
7344 | // |
7345 | // This replaces the implementations of Volatile.* in mscorlib with more efficient ones. |
7346 | // We do this because we cannot otherwise express these in C#. What we *want* to do is |
7347 | // to treat the byref args to these methods as "volatile." In pseudo-C#, this would look |
7348 | // like: |
7349 | // |
7350 | // int Read(ref volatile int location) |
7351 | // { |
7352 | // return location; |
7353 | // } |
7354 | // |
7355 | // However, C# does not yet provide a way to declare a byref as "volatile." So instead, |
7356 | // we substitute raw IL bodies for these methods that use the correct volatile instructions. |
7357 | // |
7358 | |
7359 | // Precondition: ftn is a method in mscorlib in the System.Threading.Volatile class |
7360 | _ASSERTE(ftn->GetModule()->IsSystem()); |
7361 | _ASSERTE(MscorlibBinder::IsClass(ftn->GetMethodTable(), CLASS__VOLATILE)); |
7362 | _ASSERTE(strcmp(ftn->GetMethodTable()->GetClass()->GetDebugClassName(), "System.Threading.Volatile" ) == 0); |
7363 | |
7364 | const size_t VolatileMethodBodySize = 6; |
7365 | |
7366 | struct VolatileMethodImpl |
7367 | { |
7368 | BinderMethodID methodId; |
7369 | BYTE body[VolatileMethodBodySize]; |
7370 | }; |
7371 | |
7372 | #define VOLATILE_IMPL(type, loadinst, storeinst) \ |
7373 | { \ |
7374 | METHOD__VOLATILE__READ_##type, \ |
7375 | { \ |
7376 | CEE_LDARG_0, \ |
7377 | CEE_PREFIX1, (CEE_VOLATILE & 0xFF), \ |
7378 | loadinst, \ |
7379 | CEE_NOP, /*pad to VolatileMethodBodySize bytes*/ \ |
7380 | CEE_RET \ |
7381 | } \ |
7382 | }, \ |
7383 | { \ |
7384 | METHOD__VOLATILE__WRITE_##type, \ |
7385 | { \ |
7386 | CEE_LDARG_0, \ |
7387 | CEE_LDARG_1, \ |
7388 | CEE_PREFIX1, (CEE_VOLATILE & 0xFF), \ |
7389 | storeinst, \ |
7390 | CEE_RET \ |
7391 | } \ |
7392 | }, |
7393 | |
7394 | static const VolatileMethodImpl volatileImpls[] = |
7395 | { |
7396 | VOLATILE_IMPL(T, CEE_LDIND_REF, CEE_STIND_REF) |
7397 | VOLATILE_IMPL(Bool, CEE_LDIND_I1, CEE_STIND_I1) |
7398 | VOLATILE_IMPL(Int, CEE_LDIND_I4, CEE_STIND_I4) |
7399 | VOLATILE_IMPL(IntPtr, CEE_LDIND_I, CEE_STIND_I) |
7400 | VOLATILE_IMPL(UInt, CEE_LDIND_U4, CEE_STIND_I4) |
7401 | VOLATILE_IMPL(UIntPtr, CEE_LDIND_I, CEE_STIND_I) |
7402 | VOLATILE_IMPL(SByt, CEE_LDIND_I1, CEE_STIND_I1) |
7403 | VOLATILE_IMPL(Byte, CEE_LDIND_U1, CEE_STIND_I1) |
7404 | VOLATILE_IMPL(Shrt, CEE_LDIND_I2, CEE_STIND_I2) |
7405 | VOLATILE_IMPL(UShrt, CEE_LDIND_U2, CEE_STIND_I2) |
7406 | VOLATILE_IMPL(Flt, CEE_LDIND_R4, CEE_STIND_R4) |
7407 | |
7408 | // |
7409 | // Ordinary volatile loads and stores only guarantee atomicity for pointer-sized (or smaller) data. |
7410 | // So, on 32-bit platforms we must use Interlocked operations instead for the 64-bit types. |
7411 | // The implementation in mscorlib already does this, so we will only substitute a new |
7412 | // IL body if we're running on a 64-bit platform. |
7413 | // |
7414 | IN_TARGET_64BIT(VOLATILE_IMPL(Long, CEE_LDIND_I8, CEE_STIND_I8)) |
7415 | IN_TARGET_64BIT(VOLATILE_IMPL(ULong, CEE_LDIND_I8, CEE_STIND_I8)) |
7416 | IN_TARGET_64BIT(VOLATILE_IMPL(Dbl, CEE_LDIND_R8, CEE_STIND_R8)) |
7417 | }; |
7418 | |
7419 | mdMethodDef md = ftn->GetMemberDef(); |
7420 | for (unsigned i = 0; i < NumItems(volatileImpls); i++) |
7421 | { |
7422 | if (md == MscorlibBinder::GetMethod(volatileImpls[i].methodId)->GetMemberDef()) |
7423 | { |
7424 | methInfo->ILCode = const_cast<BYTE*>(volatileImpls[i].body); |
7425 | methInfo->ILCodeSize = VolatileMethodBodySize; |
7426 | methInfo->maxStack = 2; |
7427 | methInfo->EHcount = 0; |
7428 | methInfo->options = (CorInfoOptions)0; |
7429 | return true; |
7430 | } |
7431 | } |
7432 | |
7433 | return false; |
7434 | } |
7435 | |
7436 | bool getILIntrinsicImplementationForInterlocked(MethodDesc * ftn, |
7437 | CORINFO_METHOD_INFO * methInfo) |
7438 | { |
7439 | STANDARD_VM_CONTRACT; |
7440 | |
7441 | // Precondition: ftn is a method in mscorlib in the System.Threading.Interlocked class |
7442 | _ASSERTE(ftn->GetModule()->IsSystem()); |
7443 | _ASSERTE(MscorlibBinder::IsClass(ftn->GetMethodTable(), CLASS__INTERLOCKED)); |
7444 | |
7445 | // We are only interested if ftn's token and CompareExchange<T> token match |
7446 | if (ftn->GetMemberDef() != MscorlibBinder::GetMethod(METHOD__INTERLOCKED__COMPARE_EXCHANGE_T)->GetMemberDef()) |
7447 | return false; |
7448 | |
7449 | // Get MethodDesc for System.Threading.Interlocked.CompareExchangeFast() |
7450 | MethodDesc* cmpxchgFast = MscorlibBinder::GetMethod(METHOD__INTERLOCKED__COMPARE_EXCHANGE_OBJECT); |
7451 | |
7452 | // The MethodDesc lookup must not fail, and it should have the name "CompareExchangeFast" |
7453 | _ASSERTE(cmpxchgFast != NULL); |
7454 | _ASSERTE(strcmp(cmpxchgFast->GetName(), "CompareExchange" ) == 0); |
7455 | |
7456 | // Setup up the body of the method |
7457 | static BYTE il[] = { |
7458 | CEE_LDARG_0, |
7459 | CEE_LDARG_1, |
7460 | CEE_LDARG_2, |
7461 | CEE_CALL,0,0,0,0, |
7462 | CEE_RET |
7463 | }; |
7464 | |
7465 | // Get the token for System.Threading.Interlocked.CompareExchangeFast(), and patch [target] |
7466 | mdMethodDef cmpxchgFastToken = cmpxchgFast->GetMemberDef(); |
7467 | il[4] = (BYTE)((int)cmpxchgFastToken >> 0); |
7468 | il[5] = (BYTE)((int)cmpxchgFastToken >> 8); |
7469 | il[6] = (BYTE)((int)cmpxchgFastToken >> 16); |
7470 | il[7] = (BYTE)((int)cmpxchgFastToken >> 24); |
7471 | |
7472 | // Initialize methInfo |
7473 | methInfo->ILCode = const_cast<BYTE*>(il); |
7474 | methInfo->ILCodeSize = sizeof(il); |
7475 | methInfo->maxStack = 3; |
7476 | methInfo->EHcount = 0; |
7477 | methInfo->options = (CorInfoOptions)0; |
7478 | |
7479 | return true; |
7480 | } |
7481 | |
7482 | bool getILIntrinsicImplementationForRuntimeHelpers(MethodDesc * ftn, |
7483 | CORINFO_METHOD_INFO * methInfo) |
7484 | { |
7485 | STANDARD_VM_CONTRACT; |
7486 | |
7487 | // Precondition: ftn is a method in mscorlib |
7488 | _ASSERTE(ftn->GetModule()->IsSystem()); |
7489 | |
7490 | mdMethodDef tk = ftn->GetMemberDef(); |
7491 | |
7492 | if (tk == MscorlibBinder::GetMethod(METHOD__RUNTIME_HELPERS__IS_REFERENCE_OR_CONTAINS_REFERENCES)->GetMemberDef()) |
7493 | { |
7494 | _ASSERTE(ftn->HasMethodInstantiation()); |
7495 | Instantiation inst = ftn->GetMethodInstantiation(); |
7496 | |
7497 | _ASSERTE(ftn->GetNumGenericMethodArgs() == 1); |
7498 | TypeHandle typeHandle = inst[0]; |
7499 | MethodTable * methodTable = typeHandle.GetMethodTable(); |
7500 | |
7501 | static const BYTE returnTrue[] = { CEE_LDC_I4_1, CEE_RET }; |
7502 | static const BYTE returnFalse[] = { CEE_LDC_I4_0, CEE_RET }; |
7503 | |
7504 | if (!methodTable->IsValueType() || methodTable->ContainsPointers()) |
7505 | { |
7506 | methInfo->ILCode = const_cast<BYTE*>(returnTrue); |
7507 | } |
7508 | else |
7509 | { |
7510 | methInfo->ILCode = const_cast<BYTE*>(returnFalse); |
7511 | } |
7512 | |
7513 | methInfo->ILCodeSize = sizeof(returnTrue); |
7514 | methInfo->maxStack = 1; |
7515 | methInfo->EHcount = 0; |
7516 | methInfo->options = (CorInfoOptions)0; |
7517 | return true; |
7518 | } |
7519 | |
7520 | return false; |
7521 | } |
7522 | |
7523 | //--------------------------------------------------------------------------------------- |
7524 | // |
7525 | //static |
7526 | void |
7527 | getMethodInfoHelper( |
7528 | MethodDesc * ftn, |
7529 | CORINFO_METHOD_HANDLE ftnHnd, |
7530 | COR_ILMETHOD_DECODER * header, |
7531 | CORINFO_METHOD_INFO * methInfo) |
7532 | { |
7533 | STANDARD_VM_CONTRACT; |
7534 | |
7535 | _ASSERTE(ftn == GetMethod(ftnHnd)); |
7536 | |
7537 | methInfo->ftn = ftnHnd; |
7538 | methInfo->scope = GetScopeHandle(ftn); |
7539 | methInfo->regionKind = CORINFO_REGION_JIT; |
7540 | // |
7541 | // For Jitted code the regionKind is JIT; |
7542 | // For Ngen-ed code the zapper will set this to HOT or COLD, if we |
7543 | // are using IBC data to partition methods into Hot/Cold regions |
7544 | |
7545 | /* Grab information from the IL header */ |
7546 | |
7547 | PCCOR_SIGNATURE pLocalSig = NULL; |
7548 | DWORD cbLocalSig = 0; |
7549 | |
7550 | if (NULL != header) |
7551 | { |
7552 | bool fILIntrinsic = false; |
7553 | |
7554 | MethodTable * pMT = ftn->GetMethodTable(); |
7555 | |
7556 | if (pMT->GetModule()->IsSystem()) |
7557 | { |
7558 | if (MscorlibBinder::IsClass(pMT, CLASS__JIT_HELPERS)) |
7559 | { |
7560 | fILIntrinsic = getILIntrinsicImplementation(ftn, methInfo); |
7561 | } |
7562 | else if (MscorlibBinder::IsClass(pMT, CLASS__UNSAFE)) |
7563 | { |
7564 | fILIntrinsic = getILIntrinsicImplementationForUnsafe(ftn, methInfo); |
7565 | } |
7566 | else if (MscorlibBinder::IsClass(pMT, CLASS__INTERLOCKED)) |
7567 | { |
7568 | fILIntrinsic = getILIntrinsicImplementationForInterlocked(ftn, methInfo); |
7569 | } |
7570 | else if (MscorlibBinder::IsClass(pMT, CLASS__VOLATILE)) |
7571 | { |
7572 | fILIntrinsic = getILIntrinsicImplementationForVolatile(ftn, methInfo); |
7573 | } |
7574 | else if (MscorlibBinder::IsClass(pMT, CLASS__RUNTIME_HELPERS)) |
7575 | { |
7576 | fILIntrinsic = getILIntrinsicImplementationForRuntimeHelpers(ftn, methInfo); |
7577 | } |
7578 | } |
7579 | |
7580 | if (!fILIntrinsic) |
7581 | { |
7582 | getMethodInfoILMethodHeaderHelper(header, methInfo); |
7583 | pLocalSig = header->LocalVarSig; |
7584 | cbLocalSig = header->cbLocalVarSig; |
7585 | } |
7586 | } |
7587 | else |
7588 | { |
7589 | _ASSERTE(ftn->IsDynamicMethod()); |
7590 | |
7591 | DynamicResolver * pResolver = ftn->AsDynamicMethodDesc()->GetResolver(); |
7592 | unsigned int EHCount; |
7593 | methInfo->ILCode = pResolver->GetCodeInfo(&methInfo->ILCodeSize, |
7594 | &methInfo->maxStack, |
7595 | &methInfo->options, |
7596 | &EHCount); |
7597 | methInfo->EHcount = (unsigned short)EHCount; |
7598 | SigPointer localSig = pResolver->GetLocalSig(); |
7599 | localSig.GetSignature(&pLocalSig, &cbLocalSig); |
7600 | } |
7601 | |
7602 | methInfo->options = (CorInfoOptions)(((UINT32)methInfo->options) | |
7603 | ((ftn->AcquiresInstMethodTableFromThis() ? CORINFO_GENERICS_CTXT_FROM_THIS : 0) | |
7604 | (ftn->RequiresInstMethodTableArg() ? CORINFO_GENERICS_CTXT_FROM_METHODTABLE : 0) | |
7605 | (ftn->RequiresInstMethodDescArg() ? CORINFO_GENERICS_CTXT_FROM_METHODDESC : 0))); |
7606 | |
7607 | // EEJitManager::ResolveEHClause and CrawlFrame::GetExactGenericInstantiations |
7608 | // need to be able to get to CORINFO_GENERICS_CTXT_MASK if there are any |
7609 | // catch clauses like "try {} catch(MyException<T> e) {}". |
7610 | // Such constructs are rare, and having to extend the lifetime of variable |
7611 | // for such cases is reasonable |
7612 | |
7613 | if (methInfo->options & CORINFO_GENERICS_CTXT_MASK) |
7614 | { |
7615 | #if defined(PROFILING_SUPPORTED) |
7616 | BOOL fProfilerRequiresGenericsContextForEnterLeave = FALSE; |
7617 | { |
7618 | BEGIN_PIN_PROFILER(CORProfilerPresent()); |
7619 | if (g_profControlBlock.pProfInterface->RequiresGenericsContextForEnterLeave()) |
7620 | { |
7621 | fProfilerRequiresGenericsContextForEnterLeave = TRUE; |
7622 | } |
7623 | END_PIN_PROFILER(); |
7624 | } |
7625 | if (fProfilerRequiresGenericsContextForEnterLeave) |
7626 | { |
7627 | methInfo->options = CorInfoOptions(methInfo->options|CORINFO_GENERICS_CTXT_KEEP_ALIVE); |
7628 | } |
7629 | else |
7630 | #endif // defined(PROFILING_SUPPORTED) |
7631 | { |
7632 | // Check all the exception clauses |
7633 | |
7634 | if (ftn->IsDynamicMethod()) |
7635 | { |
7636 | // @TODO: how do we detect the need to mark this flag? |
7637 | } |
7638 | else |
7639 | { |
7640 | COR_ILMETHOD_SECT_EH_CLAUSE_FAT ehClause; |
7641 | |
7642 | for (unsigned i = 0; i < methInfo->EHcount; i++) |
7643 | { |
7644 | const COR_ILMETHOD_SECT_EH_CLAUSE_FAT* ehInfo = |
7645 | (COR_ILMETHOD_SECT_EH_CLAUSE_FAT*)header->EH->EHClause(i, &ehClause); |
7646 | |
7647 | // Is it a typed catch clause? |
7648 | if (ehInfo->GetFlags() != COR_ILEXCEPTION_CLAUSE_NONE) |
7649 | continue; |
7650 | |
7651 | // Check if we catch "C<T>" ? |
7652 | |
7653 | DWORD catchTypeToken = ehInfo->GetClassToken(); |
7654 | if (TypeFromToken(catchTypeToken) != mdtTypeSpec) |
7655 | continue; |
7656 | |
7657 | PCCOR_SIGNATURE pSig; |
7658 | ULONG cSig; |
7659 | IfFailThrow(ftn->GetMDImport()->GetTypeSpecFromToken(catchTypeToken, &pSig, &cSig)); |
7660 | |
7661 | SigPointer psig(pSig, cSig); |
7662 | |
7663 | SigTypeContext sigTypeContext(ftn); |
7664 | if (psig.IsPolyType(&sigTypeContext) & hasSharableVarsMask) |
7665 | { |
7666 | methInfo->options = CorInfoOptions(methInfo->options|CORINFO_GENERICS_CTXT_KEEP_ALIVE); |
7667 | break; |
7668 | } |
7669 | } |
7670 | } |
7671 | } |
7672 | } |
7673 | |
7674 | PCCOR_SIGNATURE pSig = NULL; |
7675 | DWORD cbSig = 0; |
7676 | ftn->GetSig(&pSig, &cbSig); |
7677 | |
7678 | /* Fetch the method signature */ |
7679 | // Type parameters in the signature should be instantiated according to the |
7680 | // class/method/array instantiation of ftnHnd |
7681 | CEEInfo::ConvToJitSig( |
7682 | pSig, |
7683 | cbSig, |
7684 | GetScopeHandle(ftn), |
7685 | mdTokenNil, |
7686 | &methInfo->args, |
7687 | ftn, |
7688 | false); |
7689 | |
7690 | // Shared generic or static per-inst methods and shared methods on generic structs |
7691 | // take an extra argument representing their instantiation |
7692 | if (ftn->RequiresInstArg()) |
7693 | methInfo->args.callConv = (CorInfoCallConv)(methInfo->args.callConv | CORINFO_CALLCONV_PARAMTYPE); |
7694 | |
7695 | _ASSERTE((IsMdStatic(ftn->GetAttrs()) == 0) == ((methInfo->args.callConv & CORINFO_CALLCONV_HASTHIS) != 0)); |
7696 | |
7697 | /* And its local variables */ |
7698 | // Type parameters in the signature should be instantiated according to the |
7699 | // class/method/array instantiation of ftnHnd |
7700 | CEEInfo::ConvToJitSig( |
7701 | pLocalSig, |
7702 | cbLocalSig, |
7703 | GetScopeHandle(ftn), |
7704 | mdTokenNil, |
7705 | &methInfo->locals, |
7706 | ftn, |
7707 | true); |
7708 | } // getMethodInfoHelper |
7709 | |
7710 | //--------------------------------------------------------------------------------------- |
7711 | // |
7712 | bool |
7713 | CEEInfo::getMethodInfo( |
7714 | CORINFO_METHOD_HANDLE ftnHnd, |
7715 | CORINFO_METHOD_INFO * methInfo) |
7716 | { |
7717 | CONTRACTL { |
7718 | SO_TOLERANT; |
7719 | THROWS; |
7720 | GC_TRIGGERS; |
7721 | MODE_PREEMPTIVE; |
7722 | } CONTRACTL_END; |
7723 | |
7724 | bool result = false; |
7725 | |
7726 | JIT_TO_EE_TRANSITION(); |
7727 | |
7728 | MethodDesc * ftn = GetMethod(ftnHnd); |
7729 | |
7730 | if (!ftn->IsDynamicMethod() && (!ftn->IsIL() || !ftn->GetRVA() || ftn->IsWrapperStub())) |
7731 | { |
7732 | /* Return false if not IL or has no code */ |
7733 | result = false; |
7734 | } |
7735 | else |
7736 | { |
7737 | /* Get the IL header */ |
7738 | |
7739 | if (ftn->IsDynamicMethod()) |
7740 | { |
7741 | getMethodInfoHelper(ftn, ftnHnd, NULL, methInfo); |
7742 | } |
7743 | else |
7744 | { |
7745 | COR_ILMETHOD_DECODER header(ftn->GetILHeader(TRUE), ftn->GetMDImport(), NULL); |
7746 | |
7747 | getMethodInfoHelper(ftn, ftnHnd, &header, methInfo); |
7748 | } |
7749 | |
7750 | LOG((LF_JIT, LL_INFO100000, "Getting method info (possible inline) %s::%s%s\n" , |
7751 | ftn->m_pszDebugClassName, ftn->m_pszDebugMethodName, ftn->m_pszDebugMethodSignature)); |
7752 | |
7753 | result = true; |
7754 | } |
7755 | |
7756 | EE_TO_JIT_TRANSITION(); |
7757 | |
7758 | return result; |
7759 | } |
7760 | |
7761 | #ifdef _DEBUG |
7762 | |
7763 | /************************************************************************ |
7764 | Return true when ftn contains a local of type CLASS__STACKCRAWMARK |
7765 | */ |
7766 | |
7767 | bool containsStackCrawlMarkLocal(MethodDesc* ftn) |
7768 | { |
7769 | STANDARD_VM_CONTRACT; |
7770 | |
7771 | COR_ILMETHOD* ilHeader = ftn->GetILHeader(); |
7772 | _ASSERTE(ilHeader); |
7773 | |
7774 | COR_ILMETHOD_DECODER header(ilHeader, ftn->GetMDImport(), NULL); |
7775 | |
7776 | if (header.LocalVarSig == NULL) |
7777 | return NULL; |
7778 | |
7779 | SigPointer ptr(header.LocalVarSig, header.cbLocalVarSig); |
7780 | |
7781 | IfFailThrow(ptr.GetData(NULL)); // IMAGE_CEE_CS_CALLCONV_LOCAL_SIG |
7782 | |
7783 | ULONG numLocals; |
7784 | IfFailThrow(ptr.GetData(&numLocals)); |
7785 | |
7786 | for(ULONG i = 0; i < numLocals; i++) |
7787 | { |
7788 | CorElementType eType; |
7789 | IfFailThrow(ptr.PeekElemType(&eType)); |
7790 | if (eType != ELEMENT_TYPE_VALUETYPE) |
7791 | { |
7792 | IfFailThrow(ptr.SkipExactlyOne()); |
7793 | continue; |
7794 | } |
7795 | |
7796 | IfFailThrow(ptr.GetElemType(NULL)); |
7797 | |
7798 | mdToken token; |
7799 | IfFailThrow(ptr.GetToken(&token)); |
7800 | |
7801 | // We are inside mscorlib - simple token match is sufficient |
7802 | if (token == MscorlibBinder::GetClass(CLASS__STACKCRAWMARK)->GetCl()) |
7803 | return TRUE; |
7804 | } |
7805 | |
7806 | return FALSE; |
7807 | } |
7808 | |
7809 | #endif |
7810 | |
7811 | /************************************************************* |
7812 | * Check if the caller and calle are in the same assembly |
7813 | * i.e. do not inline across assemblies |
7814 | *************************************************************/ |
7815 | |
7816 | CorInfoInline CEEInfo::canInline (CORINFO_METHOD_HANDLE hCaller, |
7817 | CORINFO_METHOD_HANDLE hCallee, |
7818 | DWORD* pRestrictions) |
7819 | { |
7820 | CONTRACTL { |
7821 | SO_TOLERANT; |
7822 | THROWS; |
7823 | GC_TRIGGERS; |
7824 | MODE_PREEMPTIVE; |
7825 | } CONTRACTL_END; |
7826 | |
7827 | CorInfoInline result = INLINE_PASS; // By default we pass. |
7828 | // Do not set pass in the rest of the method. |
7829 | DWORD dwRestrictions = 0; // By default, no restrictions |
7830 | const char * szFailReason = NULL; // for reportInlineDecision |
7831 | |
7832 | JIT_TO_EE_TRANSITION(); |
7833 | |
7834 | // This does not work in the multi-threaded case |
7835 | #if 0 |
7836 | // Caller should check this condition first |
7837 | _ASSERTE(!(CORINFO_FLG_DONT_INLINE & getMethodAttribsInternal(hCallee))); |
7838 | #endif |
7839 | |
7840 | MethodDesc* pCaller = GetMethod(hCaller); |
7841 | MethodDesc* pCallee = GetMethod(hCallee); |
7842 | |
7843 | if (pCallee->IsNoMetadata()) |
7844 | { |
7845 | result = INLINE_FAIL; |
7846 | szFailReason = "Inlinee is NoMetadata" ; |
7847 | goto exit; |
7848 | } |
7849 | |
7850 | #ifdef DEBUGGING_SUPPORTED |
7851 | |
7852 | // If the callee wants debuggable code, don't allow it to be inlined |
7853 | |
7854 | { |
7855 | // Combining the next two lines, and eliminating jitDebuggerFlags, leads to bad codegen in x86 Release builds using Visual C++ 19.00.24215.1. |
7856 | CORJIT_FLAGS jitDebuggerFlags = GetDebuggerCompileFlags(pCallee->GetModule(), CORJIT_FLAGS()); |
7857 | if (jitDebuggerFlags.IsSet(CORJIT_FLAGS::CORJIT_FLAG_DEBUG_CODE)) |
7858 | { |
7859 | result = INLINE_NEVER; |
7860 | szFailReason = "Inlinee is debuggable" ; |
7861 | goto exit; |
7862 | } |
7863 | } |
7864 | #endif |
7865 | |
7866 | // The orginal caller is the current method |
7867 | MethodDesc * pOrigCaller; |
7868 | pOrigCaller = m_pMethodBeingCompiled; |
7869 | Module * pOrigCallerModule; |
7870 | pOrigCallerModule = pOrigCaller->GetLoaderModule(); |
7871 | |
7872 | if (pCallee->IsNotInline()) |
7873 | { |
7874 | result = INLINE_NEVER; |
7875 | szFailReason = "Inlinee is marked as no inline" ; |
7876 | goto exit; |
7877 | } |
7878 | |
7879 | // Also check to see if the method requires a security object. This means they call demand and |
7880 | // shouldn't be inlined. |
7881 | if (IsMdRequireSecObject(pCallee->GetAttrs())) |
7882 | { |
7883 | result = INLINE_NEVER; |
7884 | szFailReason = "Inlinee requires a security object (or contains StackCrawlMark)" ; |
7885 | goto exit; |
7886 | } |
7887 | |
7888 | // If the method is MethodImpl'd by another method within the same type, then we have |
7889 | // an issue that the importer will import the wrong body. In this case, we'll just |
7890 | // disallow inlining because getFunctionEntryPoint will do the right thing. |
7891 | { |
7892 | MethodDesc *pMDDecl = pCallee; |
7893 | MethodTable *pMT = pMDDecl->GetMethodTable(); |
7894 | MethodDesc *pMDImpl = pMT->MapMethodDeclToMethodImpl(pMDDecl); |
7895 | |
7896 | if (pMDDecl != pMDImpl) |
7897 | { |
7898 | result = INLINE_NEVER; |
7899 | szFailReason = "Inlinee is MethodImpl'd by another method within the same type" ; |
7900 | goto exit; |
7901 | } |
7902 | } |
7903 | |
7904 | // |
7905 | // Perform the Cross-Assembly inlining checks |
7906 | // |
7907 | { |
7908 | Module * pCalleeModule = pCallee->GetModule(); |
7909 | |
7910 | #ifdef FEATURE_PREJIT |
7911 | Assembly * pCalleeAssembly = pCalleeModule->GetAssembly(); |
7912 | |
7913 | #ifdef _DEBUG |
7914 | // |
7915 | // Make sure that all methods with StackCrawlMark are marked as IsMdRequireSecObject |
7916 | // |
7917 | if (pCalleeAssembly->IsSystem()) |
7918 | { |
7919 | _ASSERTE(!containsStackCrawlMarkLocal(pCallee)); |
7920 | } |
7921 | #endif |
7922 | |
7923 | // To allow for servicing of Ngen images we want to disable most |
7924 | // Cross-Assembly inlining except for the cases that we explicitly allow. |
7925 | // |
7926 | if (IsCompilingForNGen()) |
7927 | { |
7928 | // This is an canInline call at Ngen time |
7929 | // |
7930 | // |
7931 | Assembly * pOrigCallerAssembly = pOrigCallerModule->GetAssembly(); |
7932 | |
7933 | if (pCalleeAssembly == pOrigCallerAssembly) |
7934 | { |
7935 | // Within the same assembly |
7936 | // we can freely inline with no restrictions |
7937 | } |
7938 | else |
7939 | { |
7940 | #ifdef FEATURE_READYTORUN_COMPILER |
7941 | // No inlinining for version resilient code except if in the same version bubble |
7942 | // If this condition changes, please make the corresponding change |
7943 | // in getCallInfo, too. |
7944 | if (IsReadyToRunCompilation() && |
7945 | !isVerifyOnly() && |
7946 | !IsInSameVersionBubble(pCaller, pCallee) |
7947 | ) |
7948 | { |
7949 | result = INLINE_NEVER; |
7950 | szFailReason = "Cross-module inlining in version resilient code" ; |
7951 | goto exit; |
7952 | } |
7953 | #endif |
7954 | } |
7955 | } |
7956 | #endif // FEATURE_PREJIT |
7957 | |
7958 | // TODO: We can probably be smarter here if the caller is jitted, as we will |
7959 | // know for sure if the inlinee has really no string interning active (currently |
7960 | // it's only on in the ngen case (besides requiring the attribute)), but this is getting |
7961 | // too subtle. Will only do if somebody screams about it, as bugs here are going to |
7962 | // be tough to find |
7963 | if ((pOrigCallerModule != pCalleeModule) && pCalleeModule->IsNoStringInterning()) |
7964 | { |
7965 | dwRestrictions |= INLINE_NO_CALLEE_LDSTR; |
7966 | } |
7967 | } |
7968 | |
7969 | #ifdef PROFILING_SUPPORTED |
7970 | if (CORProfilerPresent()) |
7971 | { |
7972 | // #rejit |
7973 | // |
7974 | // Currently the rejit path is the only path which sets this. |
7975 | // If we get more reasons to set this then we may need to change |
7976 | // the failure reason message or disambiguate them. |
7977 | if (!m_allowInlining) |
7978 | { |
7979 | result = INLINE_FAIL; |
7980 | szFailReason = "ReJIT request disabled inlining from caller" ; |
7981 | goto exit; |
7982 | } |
7983 | |
7984 | // If the profiler has set a mask preventing inlining, always return |
7985 | // false to the jit. |
7986 | if (CORProfilerDisableInlining()) |
7987 | { |
7988 | result = INLINE_FAIL; |
7989 | szFailReason = "Profiler disabled inlining globally" ; |
7990 | goto exit; |
7991 | } |
7992 | |
7993 | // If the profiler wishes to be notified of JIT events and the result from |
7994 | // the above tests will cause a function to be inlined, we need to tell the |
7995 | // profiler that this inlining is going to take place, and give them a |
7996 | // chance to prevent it. |
7997 | { |
7998 | BEGIN_PIN_PROFILER(CORProfilerTrackJITInfo()); |
7999 | if (pCaller->IsILStub() || pCallee->IsILStub()) |
8000 | { |
8001 | // do nothing |
8002 | } |
8003 | else |
8004 | { |
8005 | BOOL fShouldInline; |
8006 | |
8007 | HRESULT hr = g_profControlBlock.pProfInterface->JITInlining( |
8008 | (FunctionID)pCaller, |
8009 | (FunctionID)pCallee, |
8010 | &fShouldInline); |
8011 | |
8012 | if (SUCCEEDED(hr) && !fShouldInline) |
8013 | { |
8014 | result = INLINE_FAIL; |
8015 | szFailReason = "Profiler disabled inlining locally" ; |
8016 | goto exit; |
8017 | } |
8018 | } |
8019 | END_PIN_PROFILER(); |
8020 | } |
8021 | } |
8022 | #endif // PROFILING_SUPPORTED |
8023 | |
8024 | exit: ; |
8025 | |
8026 | EE_TO_JIT_TRANSITION(); |
8027 | |
8028 | if (result == INLINE_PASS && dwRestrictions) |
8029 | { |
8030 | if (pRestrictions) |
8031 | { |
8032 | *pRestrictions = dwRestrictions; |
8033 | } |
8034 | else |
8035 | { |
8036 | // If the jitter didn't want to know about restrictions, it shouldn't be inlining |
8037 | result = INLINE_FAIL; |
8038 | szFailReason = "Inlinee has restrictions the JIT doesn't want" ; |
8039 | } |
8040 | } |
8041 | else |
8042 | { |
8043 | if (pRestrictions) |
8044 | { |
8045 | // Denied inlining, makes no sense to pass out restrictions, |
8046 | *pRestrictions = 0; |
8047 | } |
8048 | } |
8049 | |
8050 | if (dontInline(result)) |
8051 | { |
8052 | // If you hit this assert, it means you added a new way to prevent inlining |
8053 | // without documenting it for ETW! |
8054 | _ASSERTE(szFailReason != NULL); |
8055 | reportInliningDecision(hCaller, hCallee, result, szFailReason); |
8056 | } |
8057 | |
8058 | return result; |
8059 | } |
8060 | |
8061 | void CEEInfo::reportInliningDecision (CORINFO_METHOD_HANDLE inlinerHnd, |
8062 | CORINFO_METHOD_HANDLE inlineeHnd, |
8063 | CorInfoInline inlineResult, |
8064 | const char * reason) |
8065 | { |
8066 | STATIC_CONTRACT_THROWS; |
8067 | STATIC_CONTRACT_GC_TRIGGERS; |
8068 | STATIC_CONTRACT_SO_TOLERANT; |
8069 | |
8070 | JIT_TO_EE_TRANSITION(); |
8071 | |
8072 | #ifdef _DEBUG |
8073 | if (LoggingOn(LF_JIT, LL_INFO100000)) |
8074 | { |
8075 | SString currentMethodName; |
8076 | currentMethodName.AppendUTF8(m_pMethodBeingCompiled->GetModule_NoLogging()->GetFile()->GetSimpleName()); |
8077 | currentMethodName.Append(L'/'); |
8078 | TypeString::AppendMethodInternal(currentMethodName, m_pMethodBeingCompiled, TypeString::FormatBasic); |
8079 | |
8080 | SString inlineeMethodName; |
8081 | if (GetMethod(inlineeHnd)) |
8082 | { |
8083 | inlineeMethodName.AppendUTF8(GetMethod(inlineeHnd)->GetModule_NoLogging()->GetFile()->GetSimpleName()); |
8084 | inlineeMethodName.Append(L'/'); |
8085 | TypeString::AppendMethodInternal(inlineeMethodName, GetMethod(inlineeHnd), TypeString::FormatBasic); |
8086 | } |
8087 | else |
8088 | { |
8089 | inlineeMethodName.AppendASCII( "<null>" ); |
8090 | } |
8091 | |
8092 | SString inlinerMethodName; |
8093 | if (GetMethod(inlinerHnd)) |
8094 | { |
8095 | inlinerMethodName.AppendUTF8(GetMethod(inlinerHnd)->GetModule_NoLogging()->GetFile()->GetSimpleName()); |
8096 | inlinerMethodName.Append(L'/'); |
8097 | TypeString::AppendMethodInternal(inlinerMethodName, GetMethod(inlinerHnd), TypeString::FormatBasic); |
8098 | } |
8099 | else |
8100 | { |
8101 | inlinerMethodName.AppendASCII("<null>" ); |
8102 | } |
8103 | |
8104 | if (dontInline(inlineResult)) |
8105 | { |
8106 | LOG((LF_JIT, LL_INFO100000, |
8107 | "While compiling '%S', inline of '%S' into '%S' failed because: '%s'.\n" , |
8108 | currentMethodName.GetUnicode(), inlineeMethodName.GetUnicode(), |
8109 | inlinerMethodName.GetUnicode(), reason)); |
8110 | } |
8111 | else |
8112 | { |
8113 | LOG((LF_JIT, LL_INFO100000, "While compiling '%S', inline of '%S' into '%S' succeeded.\n" , |
8114 | currentMethodName.GetUnicode(), inlineeMethodName.GetUnicode(), |
8115 | inlinerMethodName.GetUnicode())); |
8116 | |
8117 | } |
8118 | } |
8119 | #endif //_DEBUG |
8120 | |
8121 | //I'm gonna duplicate this code because the format is slightly different. And LoggingOn is debug only. |
8122 | if (ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, |
8123 | TRACE_LEVEL_VERBOSE, |
8124 | CLR_JITTRACING_KEYWORD)) |
8125 | { |
8126 | SString methodBeingCompiledNames[3]; |
8127 | SString inlinerNames[3]; |
8128 | SString inlineeNames[3]; |
8129 | MethodDesc * methodBeingCompiled = m_pMethodBeingCompiled; |
8130 | #define GMI(pMD, strArray) \ |
8131 | do { \ |
8132 | if (pMD) { \ |
8133 | (pMD)->GetMethodInfo((strArray)[0], (strArray)[1], (strArray)[2]); \ |
8134 | } else { \ |
8135 | (strArray)[0].Set(W("<null>")); \ |
8136 | (strArray)[1].Set(W("<null>")); \ |
8137 | (strArray)[2].Set(W("<null>")); \ |
8138 | } } while (0) |
8139 | |
8140 | GMI(methodBeingCompiled, methodBeingCompiledNames); |
8141 | GMI(GetMethod(inlinerHnd), inlinerNames); |
8142 | GMI(GetMethod(inlineeHnd), inlineeNames); |
8143 | #undef GMI |
8144 | if (dontInline(inlineResult)) |
8145 | { |
8146 | const char * str = (reason ? reason : "" ); |
8147 | SString strReason; |
8148 | strReason.SetANSI(str); |
8149 | |
8150 | |
8151 | FireEtwMethodJitInliningFailed(methodBeingCompiledNames[0].GetUnicode(), |
8152 | methodBeingCompiledNames[1].GetUnicode(), |
8153 | methodBeingCompiledNames[2].GetUnicode(), |
8154 | inlinerNames[0].GetUnicode(), |
8155 | inlinerNames[1].GetUnicode(), |
8156 | inlinerNames[2].GetUnicode(), |
8157 | inlineeNames[0].GetUnicode(), |
8158 | inlineeNames[1].GetUnicode(), |
8159 | inlineeNames[2].GetUnicode(), |
8160 | inlineResult == INLINE_NEVER, |
8161 | strReason.GetUnicode(), |
8162 | GetClrInstanceId()); |
8163 | } |
8164 | else |
8165 | { |
8166 | FireEtwMethodJitInliningSucceeded(methodBeingCompiledNames[0].GetUnicode(), |
8167 | methodBeingCompiledNames[1].GetUnicode(), |
8168 | methodBeingCompiledNames[2].GetUnicode(), |
8169 | inlinerNames[0].GetUnicode(), |
8170 | inlinerNames[1].GetUnicode(), |
8171 | inlinerNames[2].GetUnicode(), |
8172 | inlineeNames[0].GetUnicode(), |
8173 | inlineeNames[1].GetUnicode(), |
8174 | inlineeNames[2].GetUnicode(), |
8175 | GetClrInstanceId()); |
8176 | } |
8177 | |
8178 | } |
8179 | |
8180 | EE_TO_JIT_TRANSITION(); |
8181 | } |
8182 | |
8183 | |
8184 | /************************************************************* |
8185 | This loads the (formal) declared constraints on the class and method type parameters, |
8186 | and detects (but does not itself reject) circularities among the class type parameters |
8187 | and (separately) method type parameters. |
8188 | |
8189 | It must be called whenever we verify a typical method, ie any method (generic or |
8190 | nongeneric) in a typical class. It must be called for non-generic methods too, |
8191 | because their bodies may still mention class type parameters which will need to |
8192 | have their formal constraints loaded in order to perform type compatibility tests. |
8193 | |
8194 | We have to rule out cycles like "C<U,T> where T:U, U:T" only to avoid looping |
8195 | in the verifier (ie the T.CanCast(A) would loop calling U.CanCast(A) then |
8196 | T.CanCastTo(A) etc.). Since the JIT only tries to walk the hierarchy from a type |
8197 | a parameter when verifying, it should be safe to JIT unverified, but trusted, |
8198 | instantiations even in the presence of cycle constraints. |
8199 | @TODO: It should be possible (and easy) to detect cycles much earlier on by |
8200 | directly inspecting the metadata. All you have to do is check that, for each |
8201 | of the n type parameters to a class or method there is no path of length n |
8202 | obtained by following naked type parameter constraints of the same kind. |
8203 | This can be detected by looking directly at metadata, without actually loading |
8204 | the typehandles for the naked type parameters. |
8205 | *************************************************************/ |
8206 | |
8207 | void CEEInfo::initConstraintsForVerification(CORINFO_METHOD_HANDLE hMethod, |
8208 | BOOL *pfHasCircularClassConstraints, |
8209 | BOOL *pfHasCircularMethodConstraints) |
8210 | { |
8211 | CONTRACTL { |
8212 | SO_TOLERANT; |
8213 | THROWS; |
8214 | GC_TRIGGERS; |
8215 | MODE_PREEMPTIVE; |
8216 | PRECONDITION(CheckPointer(pfHasCircularClassConstraints)); |
8217 | PRECONDITION(CheckPointer(pfHasCircularMethodConstraints)); |
8218 | } CONTRACTL_END; |
8219 | |
8220 | *pfHasCircularClassConstraints = FALSE; |
8221 | *pfHasCircularMethodConstraints = FALSE; |
8222 | |
8223 | JIT_TO_EE_TRANSITION(); |
8224 | |
8225 | MethodDesc* pMethod = GetMethod(hMethod); |
8226 | if (pMethod->IsTypicalMethodDefinition()) |
8227 | { |
8228 | // Force a load of the constraints on the type parameters, detecting cyclic bounds |
8229 | pMethod->LoadConstraintsForTypicalMethodDefinition(pfHasCircularClassConstraints,pfHasCircularMethodConstraints); |
8230 | } |
8231 | |
8232 | EE_TO_JIT_TRANSITION(); |
8233 | } |
8234 | |
8235 | /************************************************************* |
8236 | * Check if a method to be compiled is an instantiation |
8237 | * of generic code that has already been verified. |
8238 | * Three possible return values (see corinfo.h) |
8239 | *************************************************************/ |
8240 | |
8241 | CorInfoInstantiationVerification |
8242 | CEEInfo::isInstantiationOfVerifiedGeneric(CORINFO_METHOD_HANDLE hMethod) |
8243 | { |
8244 | CONTRACTL { |
8245 | SO_TOLERANT; |
8246 | THROWS; |
8247 | GC_TRIGGERS; |
8248 | MODE_PREEMPTIVE; |
8249 | } CONTRACTL_END; |
8250 | |
8251 | CorInfoInstantiationVerification result = INSTVER_NOT_INSTANTIATION; |
8252 | |
8253 | JIT_TO_EE_TRANSITION(); |
8254 | |
8255 | MethodDesc * pMethod = GetMethod(hMethod); |
8256 | |
8257 | if (!(pMethod->HasClassOrMethodInstantiation())) |
8258 | { |
8259 | result = INSTVER_NOT_INSTANTIATION; |
8260 | goto exit; |
8261 | } |
8262 | |
8263 | if (pMethod->IsTypicalMethodDefinition()) |
8264 | { |
8265 | result = INSTVER_NOT_INSTANTIATION; |
8266 | goto exit; |
8267 | } |
8268 | |
8269 | result = INSTVER_GENERIC_PASSED_VERIFICATION; |
8270 | |
8271 | exit: ; |
8272 | |
8273 | EE_TO_JIT_TRANSITION(); |
8274 | |
8275 | return result; |
8276 | } |
8277 | |
8278 | /************************************************************* |
8279 | * Similar to above, but perform check for tail call |
8280 | * eligibility. The callee can be passed as NULL if not known |
8281 | * (calli and callvirt). |
8282 | *************************************************************/ |
8283 | |
8284 | bool CEEInfo::canTailCall (CORINFO_METHOD_HANDLE hCaller, |
8285 | CORINFO_METHOD_HANDLE hDeclaredCallee, |
8286 | CORINFO_METHOD_HANDLE hExactCallee, |
8287 | bool fIsTailPrefix) |
8288 | { |
8289 | CONTRACTL { |
8290 | SO_TOLERANT; |
8291 | THROWS; |
8292 | GC_TRIGGERS; |
8293 | MODE_PREEMPTIVE; |
8294 | } CONTRACTL_END; |
8295 | |
8296 | bool result = false; |
8297 | const char * szFailReason = NULL; |
8298 | |
8299 | JIT_TO_EE_TRANSITION(); |
8300 | |
8301 | // See comments in canInline above. |
8302 | |
8303 | MethodDesc* pCaller = GetMethod(hCaller); |
8304 | MethodDesc* pDeclaredCallee = GetMethod(hDeclaredCallee); |
8305 | MethodDesc* pExactCallee = GetMethod(hExactCallee); |
8306 | |
8307 | _ASSERTE(pCaller->GetModule()); |
8308 | _ASSERTE(pCaller->GetModule()->GetClassLoader()); |
8309 | |
8310 | _ASSERTE((pExactCallee == NULL) || pExactCallee->GetModule()); |
8311 | _ASSERTE((pExactCallee == NULL) || pExactCallee->GetModule()->GetClassLoader()); |
8312 | |
8313 | // If the caller is the static constructor (.cctor) of a class which has a ComImport base class |
8314 | // somewhere up the class hierarchy, then we cannot make the call into a tailcall. See |
8315 | // RegisterObjectCreationCallback() in ExtensibleClassFactory.cpp for more information. |
8316 | if (pCaller->IsClassConstructor() && |
8317 | pCaller->GetMethodTable()->IsComObjectType()) |
8318 | { |
8319 | result = false; |
8320 | szFailReason = "Caller is ComImport .cctor" ; |
8321 | goto exit; |
8322 | } |
8323 | |
8324 | if (!fIsTailPrefix) |
8325 | { |
8326 | mdMethodDef callerToken = pCaller->GetMemberDef(); |
8327 | |
8328 | // We don't want to tailcall the entrypoint for an application; JIT64 will sometimes |
8329 | // do this for simple entrypoints and it results in a rather confusing debugging |
8330 | // experience. |
8331 | if (callerToken == pCaller->GetModule()->GetEntryPointToken()) |
8332 | { |
8333 | result = false; |
8334 | szFailReason = "Caller is the entry point" ; |
8335 | goto exit; |
8336 | } |
8337 | |
8338 | if (!pCaller->IsNoMetadata()) |
8339 | { |
8340 | // Do not tailcall from methods that are marked as noinline (people often use no-inline |
8341 | // to mean "I want to always see this method in stacktrace") |
8342 | DWORD dwImplFlags = 0; |
8343 | IfFailThrow(pCaller->GetMDImport()->GetMethodImplProps(callerToken, NULL, &dwImplFlags)); |
8344 | |
8345 | if (IsMiNoInlining(dwImplFlags)) |
8346 | { |
8347 | result = false; |
8348 | szFailReason = "Caller is marked as no inline" ; |
8349 | goto exit; |
8350 | } |
8351 | } |
8352 | |
8353 | // Methods with StackCrawlMark depend on finding their caller on the stack. |
8354 | // If we tail call one of these guys, they get confused. For lack of |
8355 | // a better way of identifying them, we use DynamicSecurity attribute to identify |
8356 | // them. We have an assert in canInline that ensures all StackCrawlMark |
8357 | // methods are appropriately marked. |
8358 | // |
8359 | if ((pExactCallee != NULL) && IsMdRequireSecObject(pExactCallee->GetAttrs())) |
8360 | { |
8361 | result = false; |
8362 | szFailReason = "Callee might have a StackCrawlMark.LookForMyCaller" ; |
8363 | goto exit; |
8364 | } |
8365 | } |
8366 | |
8367 | |
8368 | result = true; |
8369 | |
8370 | exit: ; |
8371 | |
8372 | EE_TO_JIT_TRANSITION(); |
8373 | |
8374 | if (!result) |
8375 | { |
8376 | // If you hit this assert, it means you added a new way to prevent tail calls |
8377 | // without documenting it for ETW! |
8378 | _ASSERTE(szFailReason != NULL); |
8379 | reportTailCallDecision(hCaller, hExactCallee, fIsTailPrefix, TAILCALL_FAIL, szFailReason); |
8380 | } |
8381 | |
8382 | return result; |
8383 | } |
8384 | |
8385 | void CEEInfo::reportTailCallDecision (CORINFO_METHOD_HANDLE callerHnd, |
8386 | CORINFO_METHOD_HANDLE calleeHnd, |
8387 | bool fIsTailPrefix, |
8388 | CorInfoTailCall tailCallResult, |
8389 | const char * reason) |
8390 | { |
8391 | STATIC_CONTRACT_THROWS; |
8392 | STATIC_CONTRACT_GC_TRIGGERS; |
8393 | STATIC_CONTRACT_SO_TOLERANT; |
8394 | |
8395 | JIT_TO_EE_TRANSITION(); |
8396 | |
8397 | //put code here. Make sure to report the method being compiled in addition to inliner and inlinee. |
8398 | #ifdef _DEBUG |
8399 | if (LoggingOn(LF_JIT, LL_INFO100000)) |
8400 | { |
8401 | SString currentMethodName; |
8402 | TypeString::AppendMethodInternal(currentMethodName, m_pMethodBeingCompiled, |
8403 | TypeString::FormatBasic); |
8404 | |
8405 | SString calleeMethodName; |
8406 | if (GetMethod(calleeHnd)) |
8407 | { |
8408 | TypeString::AppendMethodInternal(calleeMethodName, GetMethod(calleeHnd), |
8409 | TypeString::FormatBasic); |
8410 | } |
8411 | else |
8412 | { |
8413 | calleeMethodName.AppendASCII( "<null>" ); |
8414 | } |
8415 | |
8416 | SString callerMethodName; |
8417 | if (GetMethod(callerHnd)) |
8418 | { |
8419 | TypeString::AppendMethodInternal(callerMethodName, GetMethod(callerHnd), |
8420 | TypeString::FormatBasic); |
8421 | } |
8422 | else |
8423 | { |
8424 | callerMethodName.AppendASCII( "<null>" ); |
8425 | } |
8426 | if (tailCallResult == TAILCALL_FAIL) |
8427 | { |
8428 | LOG((LF_JIT, LL_INFO100000, |
8429 | "While compiling '%S', %Splicit tail call from '%S' to '%S' failed because: '%s'.\n" , |
8430 | currentMethodName.GetUnicode(), fIsTailPrefix ? W("ex" ) : W("im" ), |
8431 | callerMethodName.GetUnicode(), calleeMethodName.GetUnicode(), reason)); |
8432 | } |
8433 | else |
8434 | { |
8435 | static const char * const tailCallType[] = { |
8436 | "optimized tail call" , "recursive loop" , "helper assisted tailcall" |
8437 | }; |
8438 | _ASSERTE(tailCallResult >= 0 && (size_t)tailCallResult < _countof(tailCallType)); |
8439 | LOG((LF_JIT, LL_INFO100000, |
8440 | "While compiling '%S', %Splicit tail call from '%S' to '%S' generated as a %s.\n" , |
8441 | currentMethodName.GetUnicode(), fIsTailPrefix ? W("ex" ) : W("im" ), |
8442 | callerMethodName.GetUnicode(), calleeMethodName.GetUnicode(), tailCallType[tailCallResult])); |
8443 | |
8444 | } |
8445 | } |
8446 | #endif //_DEBUG |
8447 | |
8448 | // I'm gonna duplicate this code because the format is slightly different. And LoggingOn is debug only. |
8449 | if (ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, |
8450 | TRACE_LEVEL_VERBOSE, |
8451 | CLR_JITTRACING_KEYWORD)) |
8452 | { |
8453 | SString methodBeingCompiledNames[3]; |
8454 | SString callerNames[3]; |
8455 | SString calleeNames[3]; |
8456 | MethodDesc * methodBeingCompiled = m_pMethodBeingCompiled; |
8457 | #define GMI(pMD, strArray) \ |
8458 | do { \ |
8459 | if (pMD) { \ |
8460 | (pMD)->GetMethodInfo((strArray)[0], (strArray)[1], (strArray)[2]); \ |
8461 | } else { \ |
8462 | (strArray)[0].Set(W("<null>")); \ |
8463 | (strArray)[1].Set(W("<null>")); \ |
8464 | (strArray)[2].Set(W("<null>")); \ |
8465 | } } while (0) |
8466 | |
8467 | GMI(methodBeingCompiled, methodBeingCompiledNames); |
8468 | GMI(GetMethod(callerHnd), callerNames); |
8469 | GMI(GetMethod(calleeHnd), calleeNames); |
8470 | #undef GMI |
8471 | if (tailCallResult == TAILCALL_FAIL) |
8472 | { |
8473 | const char * str = (reason ? reason : "" ); |
8474 | SString strReason; |
8475 | strReason.SetANSI(str); |
8476 | |
8477 | FireEtwMethodJitTailCallFailed(methodBeingCompiledNames[0].GetUnicode(), |
8478 | methodBeingCompiledNames[1].GetUnicode(), |
8479 | methodBeingCompiledNames[2].GetUnicode(), |
8480 | callerNames[0].GetUnicode(), |
8481 | callerNames[1].GetUnicode(), |
8482 | callerNames[2].GetUnicode(), |
8483 | calleeNames[0].GetUnicode(), |
8484 | calleeNames[1].GetUnicode(), |
8485 | calleeNames[2].GetUnicode(), |
8486 | fIsTailPrefix, |
8487 | strReason.GetUnicode(), |
8488 | GetClrInstanceId()); |
8489 | } |
8490 | else |
8491 | { |
8492 | FireEtwMethodJitTailCallSucceeded(methodBeingCompiledNames[0].GetUnicode(), |
8493 | methodBeingCompiledNames[1].GetUnicode(), |
8494 | methodBeingCompiledNames[2].GetUnicode(), |
8495 | callerNames[0].GetUnicode(), |
8496 | callerNames[1].GetUnicode(), |
8497 | callerNames[2].GetUnicode(), |
8498 | calleeNames[0].GetUnicode(), |
8499 | calleeNames[1].GetUnicode(), |
8500 | calleeNames[2].GetUnicode(), |
8501 | fIsTailPrefix, |
8502 | tailCallResult, |
8503 | GetClrInstanceId()); |
8504 | } |
8505 | |
8506 | } |
8507 | |
8508 | |
8509 | EE_TO_JIT_TRANSITION(); |
8510 | } |
8511 | |
8512 | void CEEInfo::getEHinfoHelper( |
8513 | CORINFO_METHOD_HANDLE ftnHnd, |
8514 | unsigned EHnumber, |
8515 | CORINFO_EH_CLAUSE* clause, |
8516 | COR_ILMETHOD_DECODER* pILHeader) |
8517 | { |
8518 | STANDARD_VM_CONTRACT; |
8519 | |
8520 | _ASSERTE(CheckPointer(pILHeader->EH)); |
8521 | _ASSERTE(EHnumber < pILHeader->EH->EHCount()); |
8522 | |
8523 | COR_ILMETHOD_SECT_EH_CLAUSE_FAT ehClause; |
8524 | const COR_ILMETHOD_SECT_EH_CLAUSE_FAT* ehInfo; |
8525 | ehInfo = (COR_ILMETHOD_SECT_EH_CLAUSE_FAT*)pILHeader->EH->EHClause(EHnumber, &ehClause); |
8526 | |
8527 | clause->Flags = (CORINFO_EH_CLAUSE_FLAGS)ehInfo->GetFlags(); |
8528 | clause->TryOffset = ehInfo->GetTryOffset(); |
8529 | clause->TryLength = ehInfo->GetTryLength(); |
8530 | clause->HandlerOffset = ehInfo->GetHandlerOffset(); |
8531 | clause->HandlerLength = ehInfo->GetHandlerLength(); |
8532 | if ((clause->Flags & CORINFO_EH_CLAUSE_FILTER) == 0) |
8533 | clause->ClassToken = ehInfo->GetClassToken(); |
8534 | else |
8535 | clause->FilterOffset = ehInfo->GetFilterOffset(); |
8536 | } |
8537 | |
8538 | /*********************************************************************/ |
8539 | // get individual exception handler |
8540 | void CEEInfo::getEHinfo( |
8541 | CORINFO_METHOD_HANDLE ftnHnd, |
8542 | unsigned EHnumber, |
8543 | CORINFO_EH_CLAUSE* clause) |
8544 | { |
8545 | CONTRACTL { |
8546 | SO_TOLERANT; |
8547 | THROWS; |
8548 | GC_TRIGGERS; |
8549 | MODE_PREEMPTIVE; |
8550 | } CONTRACTL_END; |
8551 | |
8552 | JIT_TO_EE_TRANSITION(); |
8553 | |
8554 | MethodDesc * ftn = GetMethod(ftnHnd); |
8555 | |
8556 | if (IsDynamicMethodHandle(ftnHnd)) |
8557 | { |
8558 | GetMethod(ftnHnd)->AsDynamicMethodDesc()->GetResolver()->GetEHInfo(EHnumber, clause); |
8559 | } |
8560 | else |
8561 | { |
8562 | COR_ILMETHOD_DECODER header(ftn->GetILHeader(TRUE), ftn->GetMDImport(), NULL); |
8563 | getEHinfoHelper(ftnHnd, EHnumber, clause, &header); |
8564 | } |
8565 | |
8566 | EE_TO_JIT_TRANSITION(); |
8567 | } |
8568 | |
8569 | //--------------------------------------------------------------------------------------- |
8570 | // |
8571 | void |
8572 | CEEInfo::getMethodSig( |
8573 | CORINFO_METHOD_HANDLE ftnHnd, |
8574 | CORINFO_SIG_INFO * sigRet, |
8575 | CORINFO_CLASS_HANDLE owner) |
8576 | { |
8577 | CONTRACTL { |
8578 | SO_TOLERANT; |
8579 | THROWS; |
8580 | GC_TRIGGERS; |
8581 | MODE_PREEMPTIVE; |
8582 | } CONTRACTL_END; |
8583 | |
8584 | JIT_TO_EE_TRANSITION(); |
8585 | |
8586 | getMethodSigInternal(ftnHnd, sigRet, owner); |
8587 | |
8588 | EE_TO_JIT_TRANSITION(); |
8589 | } |
8590 | |
8591 | //--------------------------------------------------------------------------------------- |
8592 | // |
8593 | void |
8594 | CEEInfo::getMethodSigInternal( |
8595 | CORINFO_METHOD_HANDLE ftnHnd, |
8596 | CORINFO_SIG_INFO * sigRet, |
8597 | CORINFO_CLASS_HANDLE owner, |
8598 | SignatureKind signatureKind) |
8599 | { |
8600 | STANDARD_VM_CONTRACT; |
8601 | |
8602 | MethodDesc * ftn = GetMethod(ftnHnd); |
8603 | |
8604 | PCCOR_SIGNATURE pSig = NULL; |
8605 | DWORD cbSig = 0; |
8606 | ftn->GetSig(&pSig, &cbSig); |
8607 | |
8608 | // Type parameters in the signature are instantiated |
8609 | // according to the class/method/array instantiation of ftnHnd and owner |
8610 | CEEInfo::ConvToJitSig( |
8611 | pSig, |
8612 | cbSig, |
8613 | GetScopeHandle(ftn), |
8614 | mdTokenNil, |
8615 | sigRet, |
8616 | ftn, |
8617 | false, |
8618 | (TypeHandle)owner); |
8619 | |
8620 | //@GENERICS: |
8621 | // Shared generic methods and shared methods on generic structs take an extra argument representing their instantiation |
8622 | if (ftn->RequiresInstArg()) |
8623 | { |
8624 | // |
8625 | // If we are making a virtual call to an instance method on an interface, we need to lie to the JIT. |
8626 | // The reason being that we already made sure target is always directly callable (through instantiation stubs), |
8627 | // JIT should not generate shared generics aware call code and insert the secret argument again at the callsite. |
8628 | // Otherwise we would end up with two secret generic dictionary arguments (since the stub also provides one). |
8629 | // |
8630 | BOOL isCallSiteThatGoesThroughInstantiatingStub = |
8631 | signatureKind == SK_VIRTUAL_CALLSITE && |
8632 | !ftn->IsStatic() && |
8633 | ftn->GetMethodTable()->IsInterface(); |
8634 | if (!isCallSiteThatGoesThroughInstantiatingStub) |
8635 | sigRet->callConv = (CorInfoCallConv) (sigRet->callConv | CORINFO_CALLCONV_PARAMTYPE); |
8636 | } |
8637 | |
8638 | // We want the calling convention bit to be consistant with the method attribute bit |
8639 | _ASSERTE( (IsMdStatic(ftn->GetAttrs()) == 0) == ((sigRet->callConv & CORINFO_CALLCONV_HASTHIS) != 0) ); |
8640 | } |
8641 | |
8642 | //--------------------------------------------------------------------------------------- |
8643 | // |
8644 | //@GENERICSVER: for a method desc in a typical instantiation of a generic class, |
8645 | // this will return the typical instantiation of the generic class, |
8646 | // but only provided type variables are never shared. |
8647 | // The JIT verifier relies on this behaviour to extract the typical class from an instantiated method's typical method handle. |
8648 | // |
8649 | CORINFO_CLASS_HANDLE |
8650 | CEEInfo::getMethodClass( |
8651 | CORINFO_METHOD_HANDLE methodHnd) |
8652 | { |
8653 | CONTRACTL { |
8654 | SO_TOLERANT; |
8655 | THROWS; |
8656 | GC_TRIGGERS; |
8657 | MODE_PREEMPTIVE; |
8658 | } CONTRACTL_END; |
8659 | |
8660 | CORINFO_CLASS_HANDLE result = NULL; |
8661 | |
8662 | JIT_TO_EE_TRANSITION(); |
8663 | |
8664 | MethodDesc* method = GetMethod(methodHnd); |
8665 | |
8666 | if (method->IsDynamicMethod()) |
8667 | { |
8668 | DynamicResolver::SecurityControlFlags securityControlFlags = DynamicResolver::Default; |
8669 | TypeHandle typeOwner; |
8670 | |
8671 | DynamicResolver* pResolver = method->AsDynamicMethodDesc()->GetResolver(); |
8672 | pResolver->GetJitContext(&securityControlFlags, &typeOwner); |
8673 | |
8674 | if (!typeOwner.IsNull() && (method == pResolver->GetDynamicMethod())) |
8675 | { |
8676 | result = CORINFO_CLASS_HANDLE(typeOwner.AsPtr()); |
8677 | } |
8678 | } |
8679 | |
8680 | if (result == NULL) |
8681 | { |
8682 | TypeHandle th = TypeHandle(method->GetMethodTable()); |
8683 | |
8684 | result = CORINFO_CLASS_HANDLE(th.AsPtr()); |
8685 | } |
8686 | |
8687 | EE_TO_JIT_TRANSITION(); |
8688 | |
8689 | return result; |
8690 | } |
8691 | |
8692 | /***********************************************************************/ |
8693 | CORINFO_MODULE_HANDLE CEEInfo::getMethodModule (CORINFO_METHOD_HANDLE methodHnd) |
8694 | { |
8695 | CONTRACTL { |
8696 | SO_TOLERANT; |
8697 | NOTHROW; |
8698 | GC_NOTRIGGER; |
8699 | MODE_PREEMPTIVE; |
8700 | } CONTRACTL_END; |
8701 | |
8702 | CORINFO_MODULE_HANDLE result = NULL; |
8703 | |
8704 | JIT_TO_EE_TRANSITION_LEAF(); |
8705 | |
8706 | MethodDesc* method = GetMethod(methodHnd); |
8707 | |
8708 | if (method->IsDynamicMethod()) |
8709 | { |
8710 | // this should never be called, thus the assert, I don't know if the (non existent) caller |
8711 | // expects the Module or the scope |
8712 | UNREACHABLE(); |
8713 | } |
8714 | else |
8715 | { |
8716 | result = (CORINFO_MODULE_HANDLE) method->GetModule(); |
8717 | } |
8718 | |
8719 | EE_TO_JIT_TRANSITION_LEAF(); |
8720 | |
8721 | return result; |
8722 | } |
8723 | |
8724 | /*********************************************************************/ |
8725 | CorInfoIntrinsics CEEInfo::getIntrinsicID(CORINFO_METHOD_HANDLE methodHnd, |
8726 | bool * pMustExpand) |
8727 | { |
8728 | CONTRACTL { |
8729 | SO_TOLERANT; |
8730 | THROWS; |
8731 | GC_TRIGGERS; |
8732 | MODE_PREEMPTIVE; |
8733 | } CONTRACTL_END; |
8734 | |
8735 | CorInfoIntrinsics result = CORINFO_INTRINSIC_Illegal; |
8736 | |
8737 | JIT_TO_EE_TRANSITION(); |
8738 | |
8739 | if (pMustExpand != NULL) |
8740 | { |
8741 | *pMustExpand = false; |
8742 | } |
8743 | |
8744 | MethodDesc* method = GetMethod(methodHnd); |
8745 | |
8746 | if (method->IsArray()) |
8747 | { |
8748 | ArrayMethodDesc * arrMethod = (ArrayMethodDesc *)method; |
8749 | result = arrMethod->GetIntrinsicID(); |
8750 | } |
8751 | else |
8752 | if (method->IsFCall()) |
8753 | { |
8754 | result = ECall::GetIntrinsicID(method); |
8755 | } |
8756 | else |
8757 | { |
8758 | MethodTable * pMT = method->GetMethodTable(); |
8759 | if (pMT->GetModule()->IsSystem() && pMT->IsByRefLike()) |
8760 | { |
8761 | if (pMT->HasSameTypeDefAs(g_pByReferenceClass)) |
8762 | { |
8763 | // ByReference<T> has just two methods: constructor and Value property |
8764 | if (method->IsCtor()) |
8765 | { |
8766 | result = CORINFO_INTRINSIC_ByReference_Ctor; |
8767 | } |
8768 | else |
8769 | { |
8770 | _ASSERTE(strcmp(method->GetName(), "get_Value" ) == 0); |
8771 | result = CORINFO_INTRINSIC_ByReference_Value; |
8772 | } |
8773 | if (pMustExpand != nullptr) |
8774 | { |
8775 | *pMustExpand = true; |
8776 | } |
8777 | } |
8778 | else if (pMT->HasSameTypeDefAs(MscorlibBinder::GetClass(CLASS__SPAN))) |
8779 | { |
8780 | if (method->HasSameMethodDefAs(MscorlibBinder::GetMethod(METHOD__SPAN__GET_ITEM))) |
8781 | { |
8782 | result = CORINFO_INTRINSIC_Span_GetItem; |
8783 | } |
8784 | } |
8785 | else if (pMT->HasSameTypeDefAs(MscorlibBinder::GetClass(CLASS__READONLY_SPAN))) |
8786 | { |
8787 | if (method->HasSameMethodDefAs(MscorlibBinder::GetMethod(METHOD__READONLY_SPAN__GET_ITEM))) |
8788 | { |
8789 | result = CORINFO_INTRINSIC_ReadOnlySpan_GetItem; |
8790 | } |
8791 | } |
8792 | } |
8793 | } |
8794 | |
8795 | EE_TO_JIT_TRANSITION(); |
8796 | |
8797 | return result; |
8798 | } |
8799 | |
8800 | /*********************************************************************/ |
8801 | // TODO: This method should probably be renamed to something like "isSIMDType" |
8802 | bool CEEInfo::isInSIMDModule(CORINFO_CLASS_HANDLE classHnd) |
8803 | { |
8804 | CONTRACTL { |
8805 | SO_TOLERANT; |
8806 | NOTHROW; |
8807 | GC_NOTRIGGER; |
8808 | MODE_PREEMPTIVE; |
8809 | } CONTRACTL_END; |
8810 | |
8811 | bool result = false; |
8812 | JIT_TO_EE_TRANSITION_LEAF(); |
8813 | |
8814 | TypeHandle VMClsHnd(classHnd); |
8815 | PTR_MethodTable methodTable = VMClsHnd.GetMethodTable(); |
8816 | if (methodTable->GetAssembly()->IsSIMDVectorAssembly()) |
8817 | { |
8818 | result = true; |
8819 | } |
8820 | else if (methodTable->IsIntrinsicType()) |
8821 | { |
8822 | LPCUTF8 namespaceName; |
8823 | LPCUTF8 className = methodTable->GetFullyQualifiedNameInfo(&namespaceName); |
8824 | |
8825 | if (strcmp(className, "Vector`1" ) == 0 || strcmp(className, "Vector" ) == 0) |
8826 | { |
8827 | assert(strcmp(namespaceName, "System.Numerics" ) == 0); |
8828 | |
8829 | result = true; |
8830 | } |
8831 | } |
8832 | EE_TO_JIT_TRANSITION_LEAF(); |
8833 | |
8834 | return result; |
8835 | } |
8836 | |
8837 | /*********************************************************************/ |
8838 | void CEEInfo::getMethodVTableOffset (CORINFO_METHOD_HANDLE methodHnd, |
8839 | unsigned * pOffsetOfIndirection, |
8840 | unsigned * pOffsetAfterIndirection, |
8841 | bool * isRelative) |
8842 | { |
8843 | CONTRACTL { |
8844 | SO_TOLERANT; |
8845 | NOTHROW; |
8846 | GC_NOTRIGGER; |
8847 | MODE_PREEMPTIVE; |
8848 | } CONTRACTL_END; |
8849 | |
8850 | JIT_TO_EE_TRANSITION_LEAF(); |
8851 | |
8852 | MethodDesc* method = GetMethod(methodHnd); |
8853 | |
8854 | //@GENERICS: shouldn't be doing this for instantiated methods as they live elsewhere |
8855 | _ASSERTE(!method->HasMethodInstantiation()); |
8856 | |
8857 | _ASSERTE(MethodTable::GetVtableOffset() < 256); // a rough sanity check |
8858 | |
8859 | // better be in the vtable |
8860 | _ASSERTE(method->GetSlot() < method->GetMethodTable()->GetNumVirtuals()); |
8861 | |
8862 | *pOffsetOfIndirection = MethodTable::GetVtableOffset() + MethodTable::GetIndexOfVtableIndirection(method->GetSlot()) * TARGET_POINTER_SIZE /* sizeof(MethodTable::VTableIndir_t) */; |
8863 | *pOffsetAfterIndirection = MethodTable::GetIndexAfterVtableIndirection(method->GetSlot()) * TARGET_POINTER_SIZE /* sizeof(MethodTable::VTableIndir2_t) */; |
8864 | *isRelative = MethodTable::VTableIndir_t::isRelative ? 1 : 0; |
8865 | _ASSERTE(MethodTable::VTableIndir_t::isRelative == MethodTable::VTableIndir2_t::isRelative); |
8866 | |
8867 | EE_TO_JIT_TRANSITION_LEAF(); |
8868 | } |
8869 | |
8870 | /*********************************************************************/ |
8871 | CORINFO_METHOD_HANDLE CEEInfo::resolveVirtualMethodHelper(CORINFO_METHOD_HANDLE baseMethod, |
8872 | CORINFO_CLASS_HANDLE derivedClass, |
8873 | CORINFO_CONTEXT_HANDLE ownerType) |
8874 | { |
8875 | CONTRACTL { |
8876 | THROWS; |
8877 | GC_TRIGGERS; |
8878 | MODE_PREEMPTIVE; |
8879 | } CONTRACTL_END; |
8880 | |
8881 | MethodDesc* pBaseMD = GetMethod(baseMethod); |
8882 | MethodTable* pBaseMT = pBaseMD->GetMethodTable(); |
8883 | |
8884 | // Method better be from a fully loaded class |
8885 | _ASSERTE(pBaseMD->IsRestored() && pBaseMT->IsFullyLoaded()); |
8886 | |
8887 | //@GENERICS: shouldn't be doing this for instantiated methods as they live elsewhere |
8888 | _ASSERTE(!pBaseMD->HasMethodInstantiation()); |
8889 | |
8890 | // Method better be virtual |
8891 | _ASSERTE(pBaseMD->IsVirtual()); |
8892 | |
8893 | MethodDesc* pDevirtMD = nullptr; |
8894 | |
8895 | TypeHandle DerivedClsHnd(derivedClass); |
8896 | MethodTable* pDerivedMT = DerivedClsHnd.GetMethodTable(); |
8897 | _ASSERTE(pDerivedMT->IsRestored() && pDerivedMT->IsFullyLoaded()); |
8898 | |
8899 | // Can't devirtualize from __Canon. |
8900 | if (DerivedClsHnd == TypeHandle(g_pCanonMethodTableClass)) |
8901 | { |
8902 | return nullptr; |
8903 | } |
8904 | |
8905 | if (pBaseMT->IsInterface()) |
8906 | { |
8907 | |
8908 | #ifdef FEATURE_COMINTEROP |
8909 | // Don't try and devirtualize com interface calls. |
8910 | if (pDerivedMT->IsComObjectType()) |
8911 | { |
8912 | return nullptr; |
8913 | } |
8914 | #endif // FEATURE_COMINTEROP |
8915 | |
8916 | // Interface call devirtualization. |
8917 | // |
8918 | // We must ensure that pDerivedMT actually implements the |
8919 | // interface corresponding to pBaseMD. |
8920 | if (!pDerivedMT->CanCastToInterface(pBaseMT)) |
8921 | { |
8922 | return nullptr; |
8923 | } |
8924 | |
8925 | // For generic interface methods we must have an ownerType to |
8926 | // safely devirtualize. |
8927 | if (ownerType != nullptr) |
8928 | { |
8929 | TypeHandle OwnerClsHnd = GetTypeFromContext(ownerType); |
8930 | MethodTable* pOwnerMT = OwnerClsHnd.GetMethodTable(); |
8931 | |
8932 | // If the derived class is a shared class, make sure the |
8933 | // owner class is too. |
8934 | if (pDerivedMT->IsSharedByGenericInstantiations()) |
8935 | { |
8936 | pOwnerMT = pOwnerMT->GetCanonicalMethodTable(); |
8937 | } |
8938 | |
8939 | pDevirtMD = pDerivedMT->GetMethodDescForInterfaceMethod(TypeHandle(pOwnerMT), pBaseMD, FALSE /* throwOnConflict */); |
8940 | } |
8941 | else if (!pBaseMD->HasClassOrMethodInstantiation()) |
8942 | { |
8943 | pDevirtMD = pDerivedMT->GetMethodDescForInterfaceMethod(pBaseMD, FALSE /* throwOnConflict */); |
8944 | } |
8945 | |
8946 | if (pDevirtMD == nullptr) |
8947 | { |
8948 | return nullptr; |
8949 | } |
8950 | |
8951 | // If we devirtualized into a default interface method on a generic type, we should actually return an |
8952 | // instantiating stub but this is not happening. |
8953 | // Making this work is tracked by https://github.com/dotnet/coreclr/issues/15977 |
8954 | if (pDevirtMD->GetMethodTable()->IsInterface() && pDevirtMD->HasClassInstantiation()) |
8955 | { |
8956 | return nullptr; |
8957 | } |
8958 | } |
8959 | else |
8960 | { |
8961 | // Virtual call devirtualization. |
8962 | // |
8963 | // The derived class should be a subclass of the the base class. |
8964 | MethodTable* pCheckMT = pDerivedMT; |
8965 | |
8966 | while (pCheckMT != nullptr) |
8967 | { |
8968 | if (pCheckMT->HasSameTypeDefAs(pBaseMT)) |
8969 | { |
8970 | break; |
8971 | } |
8972 | |
8973 | pCheckMT = pCheckMT->GetParentMethodTable(); |
8974 | } |
8975 | |
8976 | if (pCheckMT == nullptr) |
8977 | { |
8978 | return nullptr; |
8979 | } |
8980 | |
8981 | // The base method should be in the base vtable |
8982 | WORD slot = pBaseMD->GetSlot(); |
8983 | _ASSERTE(slot < pBaseMT->GetNumVirtuals()); |
8984 | |
8985 | // Fetch the method that would be invoked if the class were |
8986 | // exactly derived class. It is up to the jit to determine whether |
8987 | // directly calling this method is correct. |
8988 | pDevirtMD = pDerivedMT->GetMethodDescForSlot(slot); |
8989 | |
8990 | // If the derived method's slot does not match the vtable slot, |
8991 | // bail on devirtualization, as the method was installed into |
8992 | // the vtable slot via an explicit override and even if the |
8993 | // method is final, the slot may not be. |
8994 | // |
8995 | // Note the jit could still safely devirtualize if it had an exact |
8996 | // class, but such cases are likely rare. |
8997 | WORD dslot = pDevirtMD->GetSlot(); |
8998 | |
8999 | if (dslot != slot) |
9000 | { |
9001 | return nullptr; |
9002 | } |
9003 | } |
9004 | |
9005 | _ASSERTE(pDevirtMD->IsRestored()); |
9006 | |
9007 | #ifdef FEATURE_READYTORUN_COMPILER |
9008 | // Check if devirtualization is dependent upon cross-version |
9009 | // bubble information and if so, disallow it. |
9010 | if (IsReadyToRunCompilation()) |
9011 | { |
9012 | MethodDesc* callerMethod = m_pMethodBeingCompiled; |
9013 | Assembly* pCallerAssembly = callerMethod->GetModule()->GetAssembly(); |
9014 | bool allowDevirt = |
9015 | IsInSameVersionBubble(pCallerAssembly , pDevirtMD->GetModule()->GetAssembly()) |
9016 | && IsInSameVersionBubble(pCallerAssembly , pDerivedMT->GetAssembly()); |
9017 | |
9018 | if (!allowDevirt) |
9019 | { |
9020 | return nullptr; |
9021 | } |
9022 | } |
9023 | #endif |
9024 | |
9025 | return (CORINFO_METHOD_HANDLE) pDevirtMD; |
9026 | } |
9027 | |
9028 | CORINFO_METHOD_HANDLE CEEInfo::resolveVirtualMethod(CORINFO_METHOD_HANDLE methodHnd, |
9029 | CORINFO_CLASS_HANDLE derivedClass, |
9030 | CORINFO_CONTEXT_HANDLE ownerType) |
9031 | { |
9032 | CONTRACTL { |
9033 | SO_TOLERANT; |
9034 | THROWS; |
9035 | GC_TRIGGERS; |
9036 | MODE_PREEMPTIVE; |
9037 | } CONTRACTL_END; |
9038 | |
9039 | CORINFO_METHOD_HANDLE result = nullptr; |
9040 | |
9041 | JIT_TO_EE_TRANSITION(); |
9042 | |
9043 | result = resolveVirtualMethodHelper(methodHnd, derivedClass, ownerType); |
9044 | |
9045 | EE_TO_JIT_TRANSITION(); |
9046 | |
9047 | return result; |
9048 | } |
9049 | |
9050 | /*********************************************************************/ |
9051 | CORINFO_METHOD_HANDLE CEEInfo::getUnboxedEntry( |
9052 | CORINFO_METHOD_HANDLE ftn, |
9053 | bool* requiresInstMethodTableArg) |
9054 | { |
9055 | CONTRACTL { |
9056 | SO_TOLERANT; |
9057 | THROWS; |
9058 | GC_TRIGGERS; |
9059 | MODE_PREEMPTIVE; |
9060 | } CONTRACTL_END; |
9061 | |
9062 | CORINFO_METHOD_HANDLE result = NULL; |
9063 | |
9064 | JIT_TO_EE_TRANSITION(); |
9065 | |
9066 | MethodDesc* pMD = GetMethod(ftn); |
9067 | bool requiresInstMTArg = false; |
9068 | |
9069 | if (pMD->IsUnboxingStub()) |
9070 | { |
9071 | MethodTable* pMT = pMD->GetMethodTable(); |
9072 | MethodDesc* pUnboxedMD = pMT->GetUnboxedEntryPointMD(pMD); |
9073 | |
9074 | result = (CORINFO_METHOD_HANDLE)pUnboxedMD; |
9075 | requiresInstMTArg = !!pUnboxedMD->RequiresInstMethodTableArg(); |
9076 | } |
9077 | |
9078 | if (requiresInstMethodTableArg != NULL) |
9079 | { |
9080 | *requiresInstMethodTableArg = requiresInstMTArg; |
9081 | } |
9082 | |
9083 | EE_TO_JIT_TRANSITION(); |
9084 | |
9085 | return result; |
9086 | } |
9087 | |
9088 | /*********************************************************************/ |
9089 | void CEEInfo::expandRawHandleIntrinsic( |
9090 | CORINFO_RESOLVED_TOKEN * pResolvedToken, |
9091 | CORINFO_GENERICHANDLE_RESULT * pResult) |
9092 | { |
9093 | LIMITED_METHOD_CONTRACT; |
9094 | UNREACHABLE(); // only called with CoreRT. |
9095 | } |
9096 | |
9097 | /*********************************************************************/ |
9098 | CORINFO_CLASS_HANDLE CEEInfo::getDefaultEqualityComparerClass(CORINFO_CLASS_HANDLE elemType) |
9099 | { |
9100 | CONTRACTL { |
9101 | SO_TOLERANT; |
9102 | THROWS; |
9103 | GC_TRIGGERS; |
9104 | MODE_PREEMPTIVE; |
9105 | } CONTRACTL_END; |
9106 | |
9107 | CORINFO_CLASS_HANDLE result = NULL; |
9108 | |
9109 | JIT_TO_EE_TRANSITION(); |
9110 | |
9111 | result = getDefaultEqualityComparerClassHelper(elemType); |
9112 | |
9113 | EE_TO_JIT_TRANSITION(); |
9114 | |
9115 | return result; |
9116 | } |
9117 | |
9118 | CORINFO_CLASS_HANDLE CEEInfo::getDefaultEqualityComparerClassHelper(CORINFO_CLASS_HANDLE elemType) |
9119 | { |
9120 | CONTRACTL { |
9121 | SO_TOLERANT; |
9122 | THROWS; |
9123 | GC_TRIGGERS; |
9124 | MODE_PREEMPTIVE; |
9125 | } CONTRACTL_END; |
9126 | |
9127 | // Mirrors the logic in BCL's CompareHelpers.CreateDefaultEqualityComparer |
9128 | // And in compile.cpp's SpecializeEqualityComparer |
9129 | TypeHandle elemTypeHnd(elemType); |
9130 | |
9131 | // Special case for byte |
9132 | if (elemTypeHnd.AsMethodTable()->HasSameTypeDefAs(MscorlibBinder::GetClass(CLASS__ELEMENT_TYPE_U1))) |
9133 | { |
9134 | return CORINFO_CLASS_HANDLE(MscorlibBinder::GetClass(CLASS__BYTE_EQUALITYCOMPARER)); |
9135 | } |
9136 | |
9137 | // Else we'll need to find the appropriate instantation |
9138 | Instantiation inst(&elemTypeHnd, 1); |
9139 | |
9140 | // If T implements IEquatable<T> |
9141 | if (elemTypeHnd.CanCastTo(TypeHandle(MscorlibBinder::GetClass(CLASS__IEQUATABLEGENERIC)).Instantiate(inst))) |
9142 | { |
9143 | TypeHandle resultTh = ((TypeHandle)MscorlibBinder::GetClass(CLASS__GENERIC_EQUALITYCOMPARER)).Instantiate(inst); |
9144 | return CORINFO_CLASS_HANDLE(resultTh.GetMethodTable()); |
9145 | } |
9146 | |
9147 | // Nullable<T> |
9148 | if (Nullable::IsNullableType(elemTypeHnd)) |
9149 | { |
9150 | Instantiation nullableInst = elemTypeHnd.AsMethodTable()->GetInstantiation(); |
9151 | TypeHandle nullableComparer = TypeHandle(MscorlibBinder::GetClass(CLASS__IEQUATABLEGENERIC)).Instantiate(nullableInst); |
9152 | if (nullableInst[0].CanCastTo(nullableComparer)) |
9153 | { |
9154 | return CORINFO_CLASS_HANDLE(nullableComparer.GetMethodTable()); |
9155 | } |
9156 | } |
9157 | |
9158 | // Enum |
9159 | // |
9160 | // We need to special case the Enum comparers based on their underlying type, |
9161 | // to avoid boxing and call the correct versions of GetHashCode. |
9162 | if (elemTypeHnd.IsEnum()) |
9163 | { |
9164 | MethodTable* targetClass = NULL; |
9165 | CorElementType normType = elemTypeHnd.GetVerifierCorElementType(); |
9166 | |
9167 | switch(normType) |
9168 | { |
9169 | case ELEMENT_TYPE_I1: |
9170 | case ELEMENT_TYPE_I2: |
9171 | case ELEMENT_TYPE_U1: |
9172 | case ELEMENT_TYPE_U2: |
9173 | case ELEMENT_TYPE_I4: |
9174 | case ELEMENT_TYPE_U4: |
9175 | case ELEMENT_TYPE_I8: |
9176 | case ELEMENT_TYPE_U8: |
9177 | { |
9178 | targetClass = MscorlibBinder::GetClass(CLASS__ENUM_EQUALITYCOMPARER); |
9179 | break; |
9180 | } |
9181 | |
9182 | default: |
9183 | break; |
9184 | } |
9185 | |
9186 | if (targetClass != NULL) |
9187 | { |
9188 | TypeHandle resultTh = ((TypeHandle)targetClass->GetCanonicalMethodTable()).Instantiate(inst); |
9189 | return CORINFO_CLASS_HANDLE(resultTh.GetMethodTable()); |
9190 | } |
9191 | } |
9192 | |
9193 | // Default case |
9194 | TypeHandle resultTh = ((TypeHandle)MscorlibBinder::GetClass(CLASS__OBJECT_EQUALITYCOMPARER)).Instantiate(inst); |
9195 | |
9196 | return CORINFO_CLASS_HANDLE(resultTh.GetMethodTable()); |
9197 | } |
9198 | |
9199 | /*********************************************************************/ |
9200 | void CEEInfo::getFunctionEntryPoint(CORINFO_METHOD_HANDLE ftnHnd, |
9201 | CORINFO_CONST_LOOKUP * pResult, |
9202 | CORINFO_ACCESS_FLAGS accessFlags) |
9203 | { |
9204 | CONTRACTL { |
9205 | SO_TOLERANT; |
9206 | THROWS; |
9207 | GC_TRIGGERS; |
9208 | MODE_PREEMPTIVE; |
9209 | } CONTRACTL_END; |
9210 | |
9211 | void* ret = NULL; |
9212 | InfoAccessType accessType = IAT_VALUE; |
9213 | |
9214 | JIT_TO_EE_TRANSITION(); |
9215 | |
9216 | MethodDesc * ftn = GetMethod(ftnHnd); |
9217 | #if defined(FEATURE_GDBJIT) |
9218 | MethodDesc * orig_ftn = ftn; |
9219 | #endif |
9220 | |
9221 | // Resolve methodImpl. |
9222 | ftn = ftn->GetMethodTable()->MapMethodDeclToMethodImpl(ftn); |
9223 | |
9224 | ret = (void *)ftn->TryGetMultiCallableAddrOfCode(accessFlags); |
9225 | |
9226 | // TryGetMultiCallableAddrOfCode returns NULL if indirect access is desired |
9227 | if (ret == NULL) |
9228 | { |
9229 | // should never get here for EnC methods or if interception via remoting stub is required |
9230 | _ASSERTE(!ftn->IsEnCMethod()); |
9231 | |
9232 | ret = (void *)ftn->GetAddrOfSlot(); |
9233 | |
9234 | if (MethodTable::VTableIndir2_t::isRelative |
9235 | && ftn->IsVtableSlot()) |
9236 | { |
9237 | accessType = IAT_RELPVALUE; |
9238 | } |
9239 | else |
9240 | { |
9241 | accessType = IAT_PVALUE; |
9242 | } |
9243 | } |
9244 | |
9245 | |
9246 | #if defined(FEATURE_GDBJIT) |
9247 | CalledMethod * pCM = new CalledMethod(orig_ftn, ret, m_pCalledMethods); |
9248 | m_pCalledMethods = pCM; |
9249 | #endif |
9250 | |
9251 | EE_TO_JIT_TRANSITION(); |
9252 | |
9253 | _ASSERTE(ret != NULL); |
9254 | |
9255 | pResult->accessType = accessType; |
9256 | pResult->addr = ret; |
9257 | } |
9258 | |
9259 | /*********************************************************************/ |
9260 | void CEEInfo::getFunctionFixedEntryPoint(CORINFO_METHOD_HANDLE ftn, |
9261 | CORINFO_CONST_LOOKUP * pResult) |
9262 | { |
9263 | CONTRACTL { |
9264 | SO_TOLERANT; |
9265 | THROWS; |
9266 | GC_TRIGGERS; |
9267 | MODE_PREEMPTIVE; |
9268 | } CONTRACTL_END; |
9269 | |
9270 | JIT_TO_EE_TRANSITION(); |
9271 | |
9272 | MethodDesc * pMD = GetMethod(ftn); |
9273 | |
9274 | pResult->accessType = IAT_VALUE; |
9275 | |
9276 | |
9277 | #ifndef CROSSGEN_COMPILE |
9278 | // If LDFTN target has [NativeCallable] attribute , then create a UMEntryThunk. |
9279 | if (pMD->HasNativeCallableAttribute()) |
9280 | { |
9281 | pResult->addr = (void*)COMDelegate::ConvertToCallback(pMD); |
9282 | } |
9283 | else |
9284 | #endif //CROSSGEN_COMPILE |
9285 | { |
9286 | pResult->addr = (void *)pMD->GetMultiCallableAddrOfCode(); |
9287 | } |
9288 | EE_TO_JIT_TRANSITION(); |
9289 | } |
9290 | |
9291 | /*********************************************************************/ |
9292 | const char* CEEInfo::getFieldName (CORINFO_FIELD_HANDLE fieldHnd, const char** scopeName) |
9293 | { |
9294 | CONTRACTL { |
9295 | SO_TOLERANT; |
9296 | THROWS; |
9297 | GC_TRIGGERS; |
9298 | MODE_PREEMPTIVE; |
9299 | } CONTRACTL_END; |
9300 | |
9301 | const char* result = NULL; |
9302 | |
9303 | JIT_TO_EE_TRANSITION(); |
9304 | |
9305 | FieldDesc* field = (FieldDesc*) fieldHnd; |
9306 | if (scopeName != 0) |
9307 | { |
9308 | TypeHandle t = TypeHandle(field->GetApproxEnclosingMethodTable()); |
9309 | *scopeName = "" ; |
9310 | if (!t.IsNull()) |
9311 | { |
9312 | #ifdef _DEBUG |
9313 | t.GetName(ssClsNameBuff); |
9314 | *scopeName = ssClsNameBuff.GetUTF8(ssClsNameBuffScratch); |
9315 | #else // !_DEBUG |
9316 | // since this is for diagnostic purposes only, |
9317 | // give up on the namespace, as we don't have a buffer to concat it |
9318 | // also note this won't show array class names. |
9319 | LPCUTF8 nameSpace; |
9320 | *scopeName= t.GetMethodTable()->GetFullyQualifiedNameInfo(&nameSpace); |
9321 | #endif // !_DEBUG |
9322 | } |
9323 | } |
9324 | |
9325 | result = field->GetName(); |
9326 | |
9327 | EE_TO_JIT_TRANSITION(); |
9328 | |
9329 | return result; |
9330 | } |
9331 | |
9332 | /*********************************************************************/ |
9333 | // Get the type that declares the field |
9334 | CORINFO_CLASS_HANDLE CEEInfo::getFieldClass (CORINFO_FIELD_HANDLE fieldHnd) |
9335 | { |
9336 | CONTRACTL { |
9337 | SO_TOLERANT; |
9338 | NOTHROW; |
9339 | GC_NOTRIGGER; |
9340 | MODE_PREEMPTIVE; |
9341 | } CONTRACTL_END; |
9342 | |
9343 | CORINFO_CLASS_HANDLE result = NULL; |
9344 | |
9345 | JIT_TO_EE_TRANSITION_LEAF(); |
9346 | |
9347 | FieldDesc* field = (FieldDesc*) fieldHnd; |
9348 | result = CORINFO_CLASS_HANDLE(field->GetApproxEnclosingMethodTable()); |
9349 | |
9350 | EE_TO_JIT_TRANSITION_LEAF(); |
9351 | |
9352 | return result; |
9353 | } |
9354 | |
9355 | /*********************************************************************/ |
9356 | // Returns the basic type of the field (not the the type that declares the field) |
9357 | // |
9358 | // pTypeHnd - Optional. If not null then on return, for reference and value types, |
9359 | // *pTypeHnd will contain the normalized type of the field. |
9360 | // owner - Optional. For resolving in a generic context |
9361 | |
9362 | CorInfoType CEEInfo::getFieldType (CORINFO_FIELD_HANDLE fieldHnd, |
9363 | CORINFO_CLASS_HANDLE* pTypeHnd, |
9364 | CORINFO_CLASS_HANDLE owner) |
9365 | { |
9366 | CONTRACTL { |
9367 | SO_TOLERANT; |
9368 | THROWS; |
9369 | GC_TRIGGERS; |
9370 | MODE_PREEMPTIVE; |
9371 | } CONTRACTL_END; |
9372 | |
9373 | CorInfoType result = CORINFO_TYPE_UNDEF; |
9374 | |
9375 | JIT_TO_EE_TRANSITION(); |
9376 | |
9377 | result = getFieldTypeInternal(fieldHnd, pTypeHnd, owner); |
9378 | |
9379 | EE_TO_JIT_TRANSITION(); |
9380 | |
9381 | return result; |
9382 | } |
9383 | |
9384 | /*********************************************************************/ |
9385 | CorInfoType CEEInfo::getFieldTypeInternal (CORINFO_FIELD_HANDLE fieldHnd, |
9386 | CORINFO_CLASS_HANDLE* pTypeHnd, |
9387 | CORINFO_CLASS_HANDLE owner) |
9388 | { |
9389 | STANDARD_VM_CONTRACT; |
9390 | |
9391 | if (pTypeHnd != nullptr) |
9392 | { |
9393 | *pTypeHnd = 0; |
9394 | } |
9395 | |
9396 | TypeHandle clsHnd = TypeHandle(); |
9397 | FieldDesc* field = (FieldDesc*) fieldHnd; |
9398 | CorElementType type = field->GetFieldType(); |
9399 | |
9400 | // <REVISIT_TODO>TODO should not burn the time to do this for anything but Value Classes</REVISIT_TODO> |
9401 | _ASSERTE(type != ELEMENT_TYPE_BYREF); |
9402 | |
9403 | if (type == ELEMENT_TYPE_I) |
9404 | { |
9405 | PTR_MethodTable enclosingMethodTable = field->GetApproxEnclosingMethodTable(); |
9406 | if (enclosingMethodTable->IsByRefLike() && enclosingMethodTable->HasSameTypeDefAs(g_pByReferenceClass)) |
9407 | { |
9408 | _ASSERTE(field->GetOffset() == 0); |
9409 | return CORINFO_TYPE_BYREF; |
9410 | } |
9411 | } |
9412 | |
9413 | if (!CorTypeInfo::IsPrimitiveType(type)) |
9414 | { |
9415 | PCCOR_SIGNATURE sig; |
9416 | DWORD sigCount; |
9417 | CorCallingConvention conv; |
9418 | |
9419 | field->GetSig(&sig, &sigCount); |
9420 | |
9421 | conv = (CorCallingConvention)CorSigUncompressCallingConv(sig); |
9422 | _ASSERTE(isCallConv(conv, IMAGE_CEE_CS_CALLCONV_FIELD)); |
9423 | |
9424 | SigPointer ptr(sig, sigCount); |
9425 | |
9426 | // For verifying code involving generics, use the class instantiation |
9427 | // of the optional owner (to provide exact, not representative, |
9428 | // type information) |
9429 | SigTypeContext typeContext(field, (TypeHandle)owner); |
9430 | |
9431 | clsHnd = ptr.GetTypeHandleThrowing(field->GetModule(), &typeContext); |
9432 | _ASSERTE(!clsHnd.IsNull()); |
9433 | |
9434 | // I believe it doesn't make any diff. if this is GetInternalCorElementType |
9435 | // or GetSignatureCorElementType. |
9436 | type = clsHnd.GetSignatureCorElementType(); |
9437 | } |
9438 | |
9439 | return CEEInfo::asCorInfoType(type, clsHnd, pTypeHnd); |
9440 | } |
9441 | |
9442 | /*********************************************************************/ |
9443 | unsigned CEEInfo::getFieldOffset (CORINFO_FIELD_HANDLE fieldHnd) |
9444 | { |
9445 | CONTRACTL { |
9446 | SO_TOLERANT; |
9447 | THROWS; |
9448 | GC_TRIGGERS; |
9449 | MODE_PREEMPTIVE; |
9450 | } CONTRACTL_END; |
9451 | |
9452 | unsigned result = (unsigned) -1; |
9453 | |
9454 | JIT_TO_EE_TRANSITION(); |
9455 | |
9456 | FieldDesc* field = (FieldDesc*) fieldHnd; |
9457 | |
9458 | // GetOffset() does not include the size of Object |
9459 | result = field->GetOffset(); |
9460 | |
9461 | // So if it is not a value class, add the Object into it |
9462 | if (field->IsStatic()) |
9463 | { |
9464 | Module* pModule = field->GetModule(); |
9465 | if (field->IsRVA() && pModule->IsRvaFieldTls(field->GetOffset())) |
9466 | { |
9467 | result = pModule->GetFieldTlsOffset(field->GetOffset()); |
9468 | } |
9469 | } |
9470 | else if (!field->GetApproxEnclosingMethodTable()->IsValueType()) |
9471 | { |
9472 | result += OBJECT_SIZE; |
9473 | } |
9474 | |
9475 | EE_TO_JIT_TRANSITION(); |
9476 | |
9477 | return result; |
9478 | } |
9479 | |
9480 | /*********************************************************************/ |
9481 | bool CEEInfo::isWriteBarrierHelperRequired(CORINFO_FIELD_HANDLE field) |
9482 | { |
9483 | CONTRACTL { |
9484 | SO_TOLERANT; |
9485 | THROWS; |
9486 | GC_TRIGGERS; |
9487 | MODE_PREEMPTIVE; |
9488 | } CONTRACTL_END; |
9489 | |
9490 | bool fHelperRequired = false; |
9491 | |
9492 | JIT_TO_EE_TRANSITION(); |
9493 | |
9494 | FieldDesc * pField = (FieldDesc *)field; |
9495 | |
9496 | // TODO: jit64 should be switched to the same plan as the i386 jits - use |
9497 | // getClassGClayout to figure out the need for writebarrier helper, and inline the copying. |
9498 | // Once this happens, USE_WRITE_BARRIER_HELPERS and CORINFO_FLG_WRITE_BARRIER_HELPER can be removed. |
9499 | CorElementType type = pField->GetFieldType(); |
9500 | |
9501 | if(CorTypeInfo::IsObjRef(type)) |
9502 | fHelperRequired = true; |
9503 | else if (type == ELEMENT_TYPE_VALUETYPE) |
9504 | { |
9505 | TypeHandle th = pField->GetFieldTypeHandleThrowing(); |
9506 | _ASSERTE(!th.IsNull()); |
9507 | if(th.GetMethodTable()->ContainsPointers()) |
9508 | fHelperRequired = true; |
9509 | } |
9510 | |
9511 | EE_TO_JIT_TRANSITION(); |
9512 | |
9513 | return fHelperRequired; |
9514 | } |
9515 | |
9516 | /*********************************************************************/ |
9517 | DWORD CEEInfo::getFieldThreadLocalStoreID(CORINFO_FIELD_HANDLE fieldHnd, void **ppIndirection) |
9518 | { |
9519 | CONTRACTL { |
9520 | SO_TOLERANT; |
9521 | THROWS; |
9522 | GC_TRIGGERS; |
9523 | MODE_PREEMPTIVE; |
9524 | } CONTRACTL_END; |
9525 | |
9526 | DWORD result = 0; |
9527 | |
9528 | if (ppIndirection != NULL) |
9529 | *ppIndirection = NULL; |
9530 | |
9531 | JIT_TO_EE_TRANSITION(); |
9532 | |
9533 | FieldDesc* field = (FieldDesc*) fieldHnd; |
9534 | Module* module = field->GetModule(); |
9535 | |
9536 | _ASSERTE(field->IsRVA()); // Only RVA statics can be thread local |
9537 | _ASSERTE(module->IsRvaFieldTls(field->GetOffset())); |
9538 | |
9539 | result = module->GetTlsIndex(); |
9540 | |
9541 | EE_TO_JIT_TRANSITION(); |
9542 | |
9543 | return result; |
9544 | } |
9545 | |
9546 | void *CEEInfo::allocateArray(ULONG cBytes) |
9547 | { |
9548 | CONTRACTL { |
9549 | SO_TOLERANT; |
9550 | THROWS; |
9551 | GC_TRIGGERS; |
9552 | MODE_PREEMPTIVE; |
9553 | } CONTRACTL_END; |
9554 | |
9555 | void * result = NULL; |
9556 | |
9557 | JIT_TO_EE_TRANSITION(); |
9558 | |
9559 | result = new BYTE [cBytes]; |
9560 | |
9561 | EE_TO_JIT_TRANSITION(); |
9562 | |
9563 | return result; |
9564 | } |
9565 | |
9566 | void CEEInfo::freeArray(void *array) |
9567 | { |
9568 | CONTRACTL { |
9569 | SO_TOLERANT; |
9570 | THROWS; |
9571 | GC_TRIGGERS; |
9572 | MODE_PREEMPTIVE; |
9573 | } CONTRACTL_END; |
9574 | |
9575 | JIT_TO_EE_TRANSITION(); |
9576 | |
9577 | delete [] ((BYTE*) array); |
9578 | |
9579 | EE_TO_JIT_TRANSITION(); |
9580 | } |
9581 | |
9582 | void CEEInfo::getBoundaries(CORINFO_METHOD_HANDLE ftn, |
9583 | unsigned int *cILOffsets, DWORD **pILOffsets, |
9584 | ICorDebugInfo::BoundaryTypes *implicitBoundaries) |
9585 | { |
9586 | CONTRACTL { |
9587 | SO_TOLERANT; |
9588 | THROWS; |
9589 | GC_TRIGGERS; |
9590 | MODE_PREEMPTIVE; |
9591 | } CONTRACTL_END; |
9592 | |
9593 | JIT_TO_EE_TRANSITION(); |
9594 | |
9595 | #ifdef DEBUGGING_SUPPORTED |
9596 | if (g_pDebugInterface && !IsCompilationProcess()) |
9597 | { |
9598 | g_pDebugInterface->getBoundaries(GetMethod(ftn), cILOffsets, pILOffsets, |
9599 | implicitBoundaries); |
9600 | } |
9601 | else |
9602 | { |
9603 | *cILOffsets = 0; |
9604 | *pILOffsets = NULL; |
9605 | *implicitBoundaries = ICorDebugInfo::DEFAULT_BOUNDARIES; |
9606 | } |
9607 | #endif // DEBUGGING_SUPPORTED |
9608 | |
9609 | EE_TO_JIT_TRANSITION(); |
9610 | } |
9611 | |
9612 | void CEEInfo::getVars(CORINFO_METHOD_HANDLE ftn, ULONG32 *cVars, ICorDebugInfo::ILVarInfo **vars, |
9613 | bool *extendOthers) |
9614 | { |
9615 | CONTRACTL { |
9616 | SO_TOLERANT; |
9617 | THROWS; |
9618 | GC_TRIGGERS; |
9619 | MODE_PREEMPTIVE; |
9620 | } CONTRACTL_END; |
9621 | |
9622 | JIT_TO_EE_TRANSITION(); |
9623 | |
9624 | #ifdef DEBUGGING_SUPPORTED |
9625 | if (g_pDebugInterface && !IsCompilationProcess()) |
9626 | { |
9627 | g_pDebugInterface->getVars(GetMethod(ftn), cVars, vars, extendOthers); |
9628 | } |
9629 | else |
9630 | { |
9631 | *cVars = 0; |
9632 | *vars = NULL; |
9633 | |
9634 | // Just tell the JIT to extend everything. |
9635 | *extendOthers = true; |
9636 | } |
9637 | #endif // DEBUGGING_SUPPORTED |
9638 | |
9639 | EE_TO_JIT_TRANSITION(); |
9640 | } |
9641 | |
9642 | /*********************************************************************/ |
9643 | CORINFO_ARG_LIST_HANDLE CEEInfo::getArgNext(CORINFO_ARG_LIST_HANDLE args) |
9644 | { |
9645 | CONTRACTL { |
9646 | SO_TOLERANT; |
9647 | THROWS; |
9648 | GC_TRIGGERS; |
9649 | MODE_PREEMPTIVE; |
9650 | } CONTRACTL_END; |
9651 | |
9652 | CORINFO_ARG_LIST_HANDLE result = NULL; |
9653 | |
9654 | JIT_TO_EE_TRANSITION(); |
9655 | |
9656 | SigPointer ptr((unsigned __int8*) args); |
9657 | IfFailThrow(ptr.SkipExactlyOne()); |
9658 | |
9659 | result = (CORINFO_ARG_LIST_HANDLE) ptr.GetPtr(); |
9660 | |
9661 | EE_TO_JIT_TRANSITION(); |
9662 | |
9663 | return result; |
9664 | } |
9665 | |
9666 | |
9667 | /*********************************************************************/ |
9668 | |
9669 | CorInfoTypeWithMod CEEInfo::getArgType ( |
9670 | CORINFO_SIG_INFO* sig, |
9671 | CORINFO_ARG_LIST_HANDLE args, |
9672 | CORINFO_CLASS_HANDLE* vcTypeRet |
9673 | ) |
9674 | { |
9675 | CONTRACTL { |
9676 | SO_TOLERANT; |
9677 | THROWS; |
9678 | GC_TRIGGERS; |
9679 | MODE_PREEMPTIVE; |
9680 | } CONTRACTL_END; |
9681 | |
9682 | CorInfoTypeWithMod result = CorInfoTypeWithMod(CORINFO_TYPE_UNDEF); |
9683 | |
9684 | JIT_TO_EE_TRANSITION(); |
9685 | |
9686 | _ASSERTE((BYTE*) sig->pSig <= (BYTE*) sig->args && (BYTE*) args < (BYTE*) sig->pSig + sig->cbSig); |
9687 | _ASSERTE((BYTE*) sig->args <= (BYTE*) args); |
9688 | INDEBUG(*vcTypeRet = CORINFO_CLASS_HANDLE((size_t)INVALID_POINTER_CC)); |
9689 | |
9690 | SigPointer ptr((unsigned __int8*) args); |
9691 | CorElementType eType; |
9692 | IfFailThrow(ptr.PeekElemType(&eType)); |
9693 | while (eType == ELEMENT_TYPE_PINNED) |
9694 | { |
9695 | result = CORINFO_TYPE_MOD_PINNED; |
9696 | IfFailThrow(ptr.GetElemType(NULL)); |
9697 | IfFailThrow(ptr.PeekElemType(&eType)); |
9698 | } |
9699 | |
9700 | // Now read off the "real" element type after taking any instantiations into consideration |
9701 | SigTypeContext typeContext; |
9702 | GetTypeContext(&sig->sigInst,&typeContext); |
9703 | |
9704 | Module* pModule = GetModule(sig->scope); |
9705 | |
9706 | CorElementType type = ptr.PeekElemTypeClosed(pModule, &typeContext); |
9707 | |
9708 | TypeHandle typeHnd = TypeHandle(); |
9709 | switch (type) { |
9710 | case ELEMENT_TYPE_VAR : |
9711 | case ELEMENT_TYPE_MVAR : |
9712 | case ELEMENT_TYPE_VALUETYPE : |
9713 | case ELEMENT_TYPE_TYPEDBYREF : |
9714 | case ELEMENT_TYPE_INTERNAL : |
9715 | { |
9716 | typeHnd = ptr.GetTypeHandleThrowing(pModule, &typeContext); |
9717 | _ASSERTE(!typeHnd.IsNull()); |
9718 | |
9719 | CorElementType normType = typeHnd.GetInternalCorElementType(); |
9720 | |
9721 | // if we are looking up a value class, don't morph it to a refernece type |
9722 | // (This can only happen in illegal IL) |
9723 | if (!CorTypeInfo::IsObjRef(normType) || type != ELEMENT_TYPE_VALUETYPE) |
9724 | { |
9725 | type = normType; |
9726 | } |
9727 | } |
9728 | break; |
9729 | |
9730 | case ELEMENT_TYPE_PTR: |
9731 | // Load the type eagerly under debugger to make the eval work |
9732 | if (!isVerifyOnly() && CORDisableJITOptimizations(pModule->GetDebuggerInfoBits())) |
9733 | { |
9734 | // NOTE: in some IJW cases, when the type pointed at is unmanaged, |
9735 | // the GetTypeHandle may fail, because there is no TypeDef for such type. |
9736 | // Usage of GetTypeHandleThrowing would lead to class load exception |
9737 | TypeHandle thPtr = ptr.GetTypeHandleNT(pModule, &typeContext); |
9738 | if(!thPtr.IsNull()) |
9739 | { |
9740 | m_pOverride->classMustBeLoadedBeforeCodeIsRun(CORINFO_CLASS_HANDLE(thPtr.AsPtr())); |
9741 | } |
9742 | } |
9743 | break; |
9744 | |
9745 | case ELEMENT_TYPE_VOID: |
9746 | // void is not valid in local sigs |
9747 | if (sig->flags & CORINFO_SIGFLAG_IS_LOCAL_SIG) |
9748 | COMPlusThrowHR(COR_E_INVALIDPROGRAM); |
9749 | break; |
9750 | |
9751 | case ELEMENT_TYPE_END: |
9752 | COMPlusThrowHR(COR_E_BADIMAGEFORMAT); |
9753 | break; |
9754 | |
9755 | default: |
9756 | break; |
9757 | } |
9758 | |
9759 | result = CorInfoTypeWithMod(result | CEEInfo::asCorInfoType(type, typeHnd, vcTypeRet)); |
9760 | EE_TO_JIT_TRANSITION(); |
9761 | |
9762 | return result; |
9763 | } |
9764 | |
9765 | /*********************************************************************/ |
9766 | |
9767 | CORINFO_CLASS_HANDLE CEEInfo::getArgClass ( |
9768 | CORINFO_SIG_INFO* sig, |
9769 | CORINFO_ARG_LIST_HANDLE args |
9770 | ) |
9771 | { |
9772 | CONTRACTL { |
9773 | SO_TOLERANT; |
9774 | THROWS; |
9775 | GC_TRIGGERS; |
9776 | MODE_PREEMPTIVE; |
9777 | } CONTRACTL_END; |
9778 | |
9779 | CORINFO_CLASS_HANDLE result = NULL; |
9780 | |
9781 | JIT_TO_EE_TRANSITION(); |
9782 | |
9783 | // make certain we dont have a completely wacked out sig pointer |
9784 | _ASSERTE((BYTE*) sig->pSig <= (BYTE*) sig->args); |
9785 | _ASSERTE((BYTE*) sig->args <= (BYTE*) args && (BYTE*) args < &((BYTE*) sig->args)[0x10000*5]); |
9786 | |
9787 | Module* pModule = GetModule(sig->scope); |
9788 | |
9789 | SigPointer ptr((unsigned __int8*) args); |
9790 | |
9791 | CorElementType eType; |
9792 | IfFailThrow(ptr.PeekElemType(&eType)); |
9793 | |
9794 | while (eType == ELEMENT_TYPE_PINNED) |
9795 | { |
9796 | IfFailThrow(ptr.GetElemType(NULL)); |
9797 | IfFailThrow(ptr.PeekElemType(&eType)); |
9798 | } |
9799 | // Now read off the "real" element type after taking any instantiations into consideration |
9800 | SigTypeContext typeContext; |
9801 | GetTypeContext(&sig->sigInst, &typeContext); |
9802 | CorElementType type = ptr.PeekElemTypeClosed(pModule, &typeContext); |
9803 | |
9804 | if (!CorTypeInfo::IsPrimitiveType(type)) { |
9805 | TypeHandle th = ptr.GetTypeHandleThrowing(pModule, &typeContext); |
9806 | result = CORINFO_CLASS_HANDLE(th.AsPtr()); |
9807 | } |
9808 | |
9809 | EE_TO_JIT_TRANSITION(); |
9810 | |
9811 | return result; |
9812 | } |
9813 | |
9814 | /*********************************************************************/ |
9815 | |
9816 | CorInfoType CEEInfo::getHFAType(CORINFO_CLASS_HANDLE hClass) |
9817 | { |
9818 | CONTRACTL { |
9819 | SO_TOLERANT; |
9820 | THROWS; |
9821 | GC_TRIGGERS; |
9822 | MODE_PREEMPTIVE; |
9823 | } CONTRACTL_END; |
9824 | |
9825 | CorInfoType result = CORINFO_TYPE_UNDEF; |
9826 | |
9827 | JIT_TO_EE_TRANSITION(); |
9828 | |
9829 | TypeHandle VMClsHnd(hClass); |
9830 | |
9831 | result = asCorInfoType(VMClsHnd.GetHFAType()); |
9832 | |
9833 | EE_TO_JIT_TRANSITION(); |
9834 | |
9835 | return result; |
9836 | } |
9837 | |
9838 | /*********************************************************************/ |
9839 | |
9840 | // return the unmanaged calling convention for a PInvoke |
9841 | CorInfoUnmanagedCallConv CEEInfo::getUnmanagedCallConv(CORINFO_METHOD_HANDLE method) |
9842 | { |
9843 | CONTRACTL { |
9844 | SO_TOLERANT; |
9845 | THROWS; |
9846 | GC_TRIGGERS; |
9847 | MODE_PREEMPTIVE; |
9848 | } CONTRACTL_END; |
9849 | |
9850 | CorInfoUnmanagedCallConv result = CORINFO_UNMANAGED_CALLCONV_UNKNOWN; |
9851 | |
9852 | JIT_TO_EE_TRANSITION(); |
9853 | |
9854 | MethodDesc* pMD = NULL; |
9855 | pMD = GetMethod(method); |
9856 | _ASSERTE(pMD->IsNDirect()); |
9857 | |
9858 | #ifdef _TARGET_X86_ |
9859 | EX_TRY |
9860 | { |
9861 | PInvokeStaticSigInfo sigInfo(pMD, PInvokeStaticSigInfo::NO_THROW_ON_ERROR); |
9862 | |
9863 | switch (sigInfo.GetCallConv()) { |
9864 | case pmCallConvCdecl: |
9865 | result = CORINFO_UNMANAGED_CALLCONV_C; |
9866 | break; |
9867 | case pmCallConvStdcall: |
9868 | result = CORINFO_UNMANAGED_CALLCONV_STDCALL; |
9869 | break; |
9870 | case pmCallConvThiscall: |
9871 | result = CORINFO_UNMANAGED_CALLCONV_THISCALL; |
9872 | break; |
9873 | default: |
9874 | result = CORINFO_UNMANAGED_CALLCONV_UNKNOWN; |
9875 | } |
9876 | } |
9877 | EX_CATCH |
9878 | { |
9879 | result = CORINFO_UNMANAGED_CALLCONV_UNKNOWN; |
9880 | } |
9881 | EX_END_CATCH(SwallowAllExceptions) |
9882 | #else // !_TARGET_X86_ |
9883 | // |
9884 | // we have only one calling convention |
9885 | // |
9886 | result = CORINFO_UNMANAGED_CALLCONV_STDCALL; |
9887 | #endif // !_TARGET_X86_ |
9888 | |
9889 | EE_TO_JIT_TRANSITION(); |
9890 | |
9891 | return result; |
9892 | } |
9893 | |
9894 | /*********************************************************************/ |
9895 | BOOL NDirectMethodDesc::ComputeMarshalingRequired() |
9896 | { |
9897 | WRAPPER_NO_CONTRACT; |
9898 | |
9899 | return NDirect::MarshalingRequired(this); |
9900 | } |
9901 | |
9902 | /*********************************************************************/ |
9903 | BOOL CEEInfo::pInvokeMarshalingRequired(CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* callSiteSig) |
9904 | { |
9905 | CONTRACTL { |
9906 | SO_TOLERANT; |
9907 | THROWS; |
9908 | GC_TRIGGERS; |
9909 | MODE_PREEMPTIVE; |
9910 | } CONTRACTL_END; |
9911 | |
9912 | BOOL result = FALSE; |
9913 | |
9914 | JIT_TO_EE_TRANSITION(); |
9915 | |
9916 | if (method != 0) |
9917 | { |
9918 | MethodDesc* ftn = GetMethod(method); |
9919 | _ASSERTE(ftn->IsNDirect()); |
9920 | NDirectMethodDesc *pMD = (NDirectMethodDesc*)ftn; |
9921 | |
9922 | #if defined(HAS_NDIRECT_IMPORT_PRECODE) |
9923 | if (pMD->IsVarArg()) |
9924 | { |
9925 | // Varag P/Invoke must not be inlined because its NDirectMethodDesc |
9926 | // does not contain a meaningful stack size (it is call site specific). |
9927 | // See code:InlinedCallFrame.UpdateRegDisplay where this is needed. |
9928 | result = TRUE; |
9929 | } |
9930 | else if (pMD->MarshalingRequired()) |
9931 | { |
9932 | // This is not a no-marshal signature. |
9933 | result = TRUE; |
9934 | } |
9935 | else |
9936 | { |
9937 | // This is a no-marshal non-vararg signature. |
9938 | result = FALSE; |
9939 | } |
9940 | #else |
9941 | // Marshalling is required to lazy initialize the indirection cell |
9942 | // without NDirectImportPrecode. |
9943 | result = TRUE; |
9944 | #endif |
9945 | } |
9946 | else |
9947 | { |
9948 | // check the call site signature |
9949 | result = NDirect::MarshalingRequired( |
9950 | GetMethod(method), |
9951 | callSiteSig->pSig, |
9952 | GetModule(callSiteSig->scope)); |
9953 | } |
9954 | |
9955 | EE_TO_JIT_TRANSITION(); |
9956 | |
9957 | return result; |
9958 | } |
9959 | |
9960 | /*********************************************************************/ |
9961 | // Generate a cookie based on the signature that would needs to be passed |
9962 | // to CORINFO_HELP_PINVOKE_CALLI |
9963 | LPVOID CEEInfo::GetCookieForPInvokeCalliSig(CORINFO_SIG_INFO* szMetaSig, |
9964 | void **ppIndirection) |
9965 | { |
9966 | WRAPPER_NO_CONTRACT; |
9967 | |
9968 | return getVarArgsHandle(szMetaSig, ppIndirection); |
9969 | } |
9970 | |
9971 | bool CEEInfo::canGetCookieForPInvokeCalliSig(CORINFO_SIG_INFO* szMetaSig) |
9972 | { |
9973 | LIMITED_METHOD_CONTRACT; |
9974 | return true; |
9975 | } |
9976 | |
9977 | |
9978 | // Check any constraints on method type arguments |
9979 | BOOL CEEInfo::satisfiesMethodConstraints( |
9980 | CORINFO_CLASS_HANDLE parent, |
9981 | CORINFO_METHOD_HANDLE method) |
9982 | { |
9983 | CONTRACTL { |
9984 | SO_TOLERANT; |
9985 | THROWS; |
9986 | GC_TRIGGERS; |
9987 | MODE_PREEMPTIVE; |
9988 | } CONTRACTL_END; |
9989 | |
9990 | BOOL result = FALSE; |
9991 | |
9992 | JIT_TO_EE_TRANSITION(); |
9993 | |
9994 | _ASSERTE(parent != NULL); |
9995 | _ASSERTE(method != NULL); |
9996 | result = GetMethod(method)->SatisfiesMethodConstraints(TypeHandle(parent)); |
9997 | |
9998 | EE_TO_JIT_TRANSITION(); |
9999 | |
10000 | return result; |
10001 | } |
10002 | |
10003 | |
10004 | |
10005 | /*********************************************************************/ |
10006 | // Given a delegate target class, a target method parent class, a target method, |
10007 | // a delegate class, check if the method signature is compatible with the Invoke method of the delegate |
10008 | // (under the typical instantiation of any free type variables in the memberref signatures). |
10009 | // |
10010 | // objCls should be NULL if the target object is NULL |
10011 | //@GENERICSVER: new (suitable for generics) |
10012 | BOOL CEEInfo::isCompatibleDelegate( |
10013 | CORINFO_CLASS_HANDLE objCls, |
10014 | CORINFO_CLASS_HANDLE methodParentCls, |
10015 | CORINFO_METHOD_HANDLE method, |
10016 | CORINFO_CLASS_HANDLE delegateCls, |
10017 | BOOL* pfIsOpenDelegate) |
10018 | { |
10019 | CONTRACTL { |
10020 | SO_TOLERANT; |
10021 | THROWS; |
10022 | GC_TRIGGERS; |
10023 | MODE_PREEMPTIVE; |
10024 | } CONTRACTL_END; |
10025 | |
10026 | BOOL result = FALSE; |
10027 | |
10028 | JIT_TO_EE_TRANSITION(); |
10029 | |
10030 | _ASSERTE(method != NULL); |
10031 | _ASSERTE(delegateCls != NULL); |
10032 | |
10033 | TypeHandle delegateClsHnd = (TypeHandle) delegateCls; |
10034 | |
10035 | _ASSERTE(delegateClsHnd.GetMethodTable()->IsDelegate()); |
10036 | |
10037 | TypeHandle methodParentHnd = (TypeHandle) (methodParentCls); |
10038 | MethodDesc* pMDFtn = GetMethod(method); |
10039 | TypeHandle objClsHnd(objCls); |
10040 | |
10041 | EX_TRY |
10042 | { |
10043 | result = COMDelegate::ValidateCtor(objClsHnd, methodParentHnd, pMDFtn, delegateClsHnd, pfIsOpenDelegate); |
10044 | } |
10045 | EX_CATCH |
10046 | { |
10047 | } |
10048 | EX_END_CATCH(SwallowAllExceptions) |
10049 | |
10050 | EE_TO_JIT_TRANSITION(); |
10051 | |
10052 | return result; |
10053 | } |
10054 | |
10055 | /*********************************************************************/ |
10056 | // return the unmanaged target *if method has already been prelinked.* |
10057 | void* CEEInfo::getPInvokeUnmanagedTarget(CORINFO_METHOD_HANDLE method, |
10058 | void **ppIndirection) |
10059 | { |
10060 | CONTRACTL { |
10061 | SO_TOLERANT; |
10062 | THROWS; |
10063 | GC_TRIGGERS; |
10064 | MODE_PREEMPTIVE; |
10065 | } CONTRACTL_END; |
10066 | |
10067 | // Not expected to work due to multicore and tiered JIT potentially needing |
10068 | // to call managed cctors |
10069 | _ASSERTE(FALSE); |
10070 | |
10071 | if (ppIndirection != NULL) |
10072 | { |
10073 | *ppIndirection = NULL; |
10074 | } |
10075 | |
10076 | return NULL; |
10077 | } |
10078 | |
10079 | /*********************************************************************/ |
10080 | // return address of fixup area for late-bound N/Direct calls. |
10081 | void* CEEInfo::getAddressOfPInvokeFixup(CORINFO_METHOD_HANDLE method, |
10082 | void **ppIndirection) |
10083 | { |
10084 | CONTRACTL { |
10085 | SO_TOLERANT; |
10086 | NOTHROW; |
10087 | GC_NOTRIGGER; |
10088 | MODE_PREEMPTIVE; |
10089 | } CONTRACTL_END; |
10090 | |
10091 | void * result = NULL; |
10092 | |
10093 | if (ppIndirection != NULL) |
10094 | *ppIndirection = NULL; |
10095 | |
10096 | JIT_TO_EE_TRANSITION_LEAF(); |
10097 | |
10098 | MethodDesc* ftn = GetMethod(method); |
10099 | _ASSERTE(ftn->IsNDirect()); |
10100 | NDirectMethodDesc *pMD = (NDirectMethodDesc*)ftn; |
10101 | |
10102 | result = (LPVOID)&(pMD->GetWriteableData()->m_pNDirectTarget); |
10103 | |
10104 | EE_TO_JIT_TRANSITION_LEAF(); |
10105 | |
10106 | return result; |
10107 | } |
10108 | |
10109 | /*********************************************************************/ |
10110 | // return address of fixup area for late-bound N/Direct calls. |
10111 | void CEEInfo::getAddressOfPInvokeTarget(CORINFO_METHOD_HANDLE method, |
10112 | CORINFO_CONST_LOOKUP *pLookup) |
10113 | { |
10114 | WRAPPER_NO_CONTRACT; |
10115 | |
10116 | void *pIndirection; |
10117 | pLookup->accessType = IAT_PVALUE; |
10118 | pLookup->addr = getAddressOfPInvokeFixup(method, &pIndirection); |
10119 | _ASSERTE(pIndirection == NULL); |
10120 | } |
10121 | |
10122 | /*********************************************************************/ |
10123 | CORINFO_JUST_MY_CODE_HANDLE CEEInfo::getJustMyCodeHandle( |
10124 | CORINFO_METHOD_HANDLE method, |
10125 | CORINFO_JUST_MY_CODE_HANDLE**ppIndirection) |
10126 | { |
10127 | CONTRACTL { |
10128 | SO_TOLERANT; |
10129 | NOTHROW; |
10130 | GC_NOTRIGGER; |
10131 | MODE_PREEMPTIVE; |
10132 | } CONTRACTL_END; |
10133 | |
10134 | CORINFO_JUST_MY_CODE_HANDLE result = NULL; |
10135 | |
10136 | if (ppIndirection) |
10137 | *ppIndirection = NULL; |
10138 | |
10139 | JIT_TO_EE_TRANSITION_LEAF(); |
10140 | |
10141 | // Get the flag from the debugger. |
10142 | MethodDesc* ftn = GetMethod(method); |
10143 | DWORD * pFlagAddr = NULL; |
10144 | |
10145 | if (g_pDebugInterface) |
10146 | { |
10147 | pFlagAddr = g_pDebugInterface->GetJMCFlagAddr(ftn->GetModule()); |
10148 | } |
10149 | |
10150 | result = (CORINFO_JUST_MY_CODE_HANDLE) pFlagAddr; |
10151 | |
10152 | EE_TO_JIT_TRANSITION_LEAF(); |
10153 | |
10154 | return result; |
10155 | } |
10156 | |
10157 | /*********************************************************************/ |
10158 | void InlinedCallFrame::GetEEInfo(CORINFO_EE_INFO::InlinedCallFrameInfo *pInfo) |
10159 | { |
10160 | LIMITED_METHOD_CONTRACT; |
10161 | |
10162 | pInfo->size = sizeof(GSCookie) + sizeof(InlinedCallFrame); |
10163 | |
10164 | pInfo->offsetOfGSCookie = 0; |
10165 | pInfo->offsetOfFrameVptr = sizeof(GSCookie); |
10166 | pInfo->offsetOfFrameLink = sizeof(GSCookie) + Frame::GetOffsetOfNextLink(); |
10167 | pInfo->offsetOfCallSiteSP = sizeof(GSCookie) + offsetof(InlinedCallFrame, m_pCallSiteSP); |
10168 | pInfo->offsetOfCalleeSavedFP = sizeof(GSCookie) + offsetof(InlinedCallFrame, m_pCalleeSavedFP); |
10169 | pInfo->offsetOfCallTarget = sizeof(GSCookie) + offsetof(InlinedCallFrame, m_Datum); |
10170 | pInfo->offsetOfReturnAddress = sizeof(GSCookie) + offsetof(InlinedCallFrame, m_pCallerReturnAddress); |
10171 | } |
10172 | |
10173 | /*********************************************************************/ |
10174 | // Return details about EE internal data structures |
10175 | void CEEInfo::getEEInfo(CORINFO_EE_INFO *pEEInfoOut) |
10176 | { |
10177 | CONTRACTL { |
10178 | SO_TOLERANT; |
10179 | THROWS; |
10180 | GC_TRIGGERS; |
10181 | MODE_PREEMPTIVE; |
10182 | } CONTRACTL_END; |
10183 | |
10184 | INDEBUG(memset(pEEInfoOut, 0xCC, sizeof(*pEEInfoOut))); |
10185 | |
10186 | JIT_TO_EE_TRANSITION(); |
10187 | |
10188 | if (!IsReadyToRunCompilation()) |
10189 | { |
10190 | InlinedCallFrame::GetEEInfo(&pEEInfoOut->inlinedCallFrameInfo); |
10191 | |
10192 | // Offsets into the Thread structure |
10193 | pEEInfoOut->offsetOfThreadFrame = Thread::GetOffsetOfCurrentFrame(); |
10194 | pEEInfoOut->offsetOfGCState = Thread::GetOffsetOfGCFlag(); |
10195 | } |
10196 | else |
10197 | { |
10198 | // inlinedCallFrameInfo is not used for R2R compilation |
10199 | ZeroMemory(&pEEInfoOut->inlinedCallFrameInfo, sizeof(pEEInfoOut->inlinedCallFrameInfo)); |
10200 | |
10201 | pEEInfoOut->offsetOfThreadFrame = (DWORD)-1; |
10202 | pEEInfoOut->offsetOfGCState = (DWORD)-1; |
10203 | } |
10204 | |
10205 | #ifndef CROSSBITNESS_COMPILE |
10206 | // The assertions must hold in every non-crossbitness scenario |
10207 | _ASSERTE(OFFSETOF__DelegateObject__target == DelegateObject::GetOffsetOfTarget()); |
10208 | _ASSERTE(OFFSETOF__DelegateObject__methodPtr == DelegateObject::GetOffsetOfMethodPtr()); |
10209 | _ASSERTE(OFFSETOF__DelegateObject__methodPtrAux == DelegateObject::GetOffsetOfMethodPtrAux()); |
10210 | _ASSERTE(OFFSETOF__PtrArray__m_Array_ == PtrArray::GetDataOffset()); |
10211 | #endif |
10212 | |
10213 | // Delegate offsets |
10214 | pEEInfoOut->offsetOfDelegateInstance = OFFSETOF__DelegateObject__target; |
10215 | pEEInfoOut->offsetOfDelegateFirstTarget = OFFSETOF__DelegateObject__methodPtr; |
10216 | |
10217 | // Secure delegate offsets |
10218 | pEEInfoOut->offsetOfSecureDelegateIndirectCell = OFFSETOF__DelegateObject__methodPtrAux; |
10219 | |
10220 | // Remoting offsets |
10221 | pEEInfoOut->offsetOfTransparentProxyRP = (DWORD)-1; |
10222 | pEEInfoOut->offsetOfRealProxyServer = (DWORD)-1; |
10223 | |
10224 | pEEInfoOut->offsetOfObjArrayData = OFFSETOF__PtrArray__m_Array_; |
10225 | |
10226 | pEEInfoOut->sizeOfReversePInvokeFrame = (DWORD)-1; |
10227 | |
10228 | pEEInfoOut->osPageSize = GetOsPageSize(); |
10229 | pEEInfoOut->maxUncheckedOffsetForNullObject = MAX_UNCHECKED_OFFSET_FOR_NULL_OBJECT; |
10230 | pEEInfoOut->targetAbi = CORINFO_CORECLR_ABI; |
10231 | |
10232 | pEEInfoOut->osType = CORINFO_WINNT; |
10233 | |
10234 | // hardcode OS version to 0.0.0. These fields can be removed from JITEE interface |
10235 | pEEInfoOut->osMajor = 0; |
10236 | pEEInfoOut->osMinor = 0; |
10237 | pEEInfoOut->osBuild = 0; |
10238 | |
10239 | EE_TO_JIT_TRANSITION(); |
10240 | } |
10241 | |
10242 | LPCWSTR CEEInfo::getJitTimeLogFilename() |
10243 | { |
10244 | CONTRACTL { |
10245 | SO_TOLERANT; |
10246 | THROWS; |
10247 | GC_TRIGGERS; |
10248 | MODE_PREEMPTIVE; |
10249 | } CONTRACTL_END; |
10250 | |
10251 | LPCWSTR result = NULL; |
10252 | |
10253 | JIT_TO_EE_TRANSITION(); |
10254 | result = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_JitTimeLogFile); |
10255 | EE_TO_JIT_TRANSITION(); |
10256 | |
10257 | return result; |
10258 | } |
10259 | |
10260 | |
10261 | |
10262 | // Return details about EE internal data structures |
10263 | DWORD CEEInfo::getThreadTLSIndex(void **ppIndirection) |
10264 | { |
10265 | CONTRACTL { |
10266 | SO_TOLERANT; |
10267 | THROWS; |
10268 | GC_TRIGGERS; |
10269 | MODE_PREEMPTIVE; |
10270 | } CONTRACTL_END; |
10271 | |
10272 | DWORD result = (DWORD)-1; |
10273 | |
10274 | if (ppIndirection != NULL) |
10275 | *ppIndirection = NULL; |
10276 | |
10277 | return result; |
10278 | } |
10279 | |
10280 | const void * CEEInfo::getInlinedCallFrameVptr(void **ppIndirection) |
10281 | { |
10282 | CONTRACTL { |
10283 | SO_TOLERANT; |
10284 | NOTHROW; |
10285 | GC_NOTRIGGER; |
10286 | MODE_PREEMPTIVE; |
10287 | } CONTRACTL_END; |
10288 | |
10289 | void * result = NULL; |
10290 | |
10291 | if (ppIndirection != NULL) |
10292 | *ppIndirection = NULL; |
10293 | |
10294 | JIT_TO_EE_TRANSITION_LEAF(); |
10295 | |
10296 | #ifndef CROSSGEN_COMPILE |
10297 | result = (void*)InlinedCallFrame::GetMethodFrameVPtr(); |
10298 | #else |
10299 | result = (void*)0x43210; |
10300 | #endif |
10301 | |
10302 | EE_TO_JIT_TRANSITION_LEAF(); |
10303 | |
10304 | return result; |
10305 | } |
10306 | |
10307 | LONG * CEEInfo::getAddrOfCaptureThreadGlobal(void **ppIndirection) |
10308 | { |
10309 | CONTRACTL { |
10310 | SO_TOLERANT; |
10311 | NOTHROW; |
10312 | GC_NOTRIGGER; |
10313 | MODE_PREEMPTIVE; |
10314 | } CONTRACTL_END; |
10315 | |
10316 | LONG * result = NULL; |
10317 | |
10318 | if (ppIndirection != NULL) |
10319 | *ppIndirection = NULL; |
10320 | |
10321 | JIT_TO_EE_TRANSITION_LEAF(); |
10322 | |
10323 | result = (LONG *)&g_TrapReturningThreads; |
10324 | |
10325 | EE_TO_JIT_TRANSITION_LEAF(); |
10326 | |
10327 | return result; |
10328 | } |
10329 | |
10330 | |
10331 | |
10332 | HRESULT CEEInfo::GetErrorHRESULT(struct _EXCEPTION_POINTERS *pExceptionPointers) |
10333 | { |
10334 | CONTRACTL { |
10335 | SO_TOLERANT; |
10336 | NOTHROW; |
10337 | GC_TRIGGERS; |
10338 | MODE_ANY; |
10339 | } CONTRACTL_END; |
10340 | |
10341 | HRESULT hr = S_OK; |
10342 | |
10343 | //This function is called from the JIT64 exception filter during PEVerify. Because it is a filter, it |
10344 | //can be "called" from a NOTHROW region in the case of StackOverflow. Security::MapToHR throws |
10345 | //internally, but it catches all exceptions. Therefore, none of the children can cause an exception to |
10346 | //percolate out of this function (except for Stack Overflow). Obviously I can't explain most of this to |
10347 | //the Contracts system, and I can't add this CONTRACT_VIOLATION to the filter in Jit64. |
10348 | CONTRACT_VIOLATION(ThrowsViolation); |
10349 | |
10350 | JIT_TO_EE_TRANSITION(); |
10351 | |
10352 | GCX_COOP(); |
10353 | |
10354 | OBJECTREF throwable = GetThread()->LastThrownObject(); |
10355 | hr = GetExceptionHResult(throwable); |
10356 | |
10357 | EE_TO_JIT_TRANSITION(); |
10358 | |
10359 | return hr; |
10360 | } |
10361 | |
10362 | |
10363 | ULONG CEEInfo::GetErrorMessage(__inout_ecount(bufferLength) LPWSTR buffer, ULONG bufferLength) |
10364 | { |
10365 | CONTRACTL { |
10366 | SO_TOLERANT; |
10367 | THROWS; |
10368 | GC_TRIGGERS; |
10369 | MODE_PREEMPTIVE; |
10370 | } CONTRACTL_END; |
10371 | |
10372 | ULONG result = 0; |
10373 | |
10374 | #ifndef CROSSGEN_COMPILE |
10375 | JIT_TO_EE_TRANSITION(); |
10376 | |
10377 | GCX_COOP(); |
10378 | |
10379 | OBJECTREF throwable = GetThread()->LastThrownObject(); |
10380 | |
10381 | if (throwable != NULL) |
10382 | { |
10383 | EX_TRY |
10384 | { |
10385 | result = GetExceptionMessage(throwable, buffer, bufferLength); |
10386 | } |
10387 | EX_CATCH |
10388 | { |
10389 | } |
10390 | EX_END_CATCH(SwallowAllExceptions) |
10391 | } |
10392 | |
10393 | EE_TO_JIT_TRANSITION(); |
10394 | #endif |
10395 | |
10396 | return result; |
10397 | } |
10398 | |
10399 | // This method is called from CEEInfo::FilterException which |
10400 | // is run as part of the SEH filter clause for the JIT. |
10401 | // It is fatal to throw an exception while running a SEH filter clause |
10402 | // so our contract is NOTHROW, NOTRIGGER. |
10403 | // |
10404 | LONG EEFilterException(struct _EXCEPTION_POINTERS *pExceptionPointers, void *unused) |
10405 | { |
10406 | CONTRACTL { |
10407 | SO_TOLERANT; |
10408 | NOTHROW; |
10409 | GC_NOTRIGGER; |
10410 | } CONTRACTL_END; |
10411 | |
10412 | int result = 0; |
10413 | |
10414 | JIT_TO_EE_TRANSITION_LEAF(); |
10415 | |
10416 | VALIDATE_BACKOUT_STACK_CONSUMPTION; |
10417 | |
10418 | unsigned code = pExceptionPointers->ExceptionRecord->ExceptionCode; |
10419 | |
10420 | #ifdef _DEBUG |
10421 | if (code == EXCEPTION_ACCESS_VIOLATION) |
10422 | { |
10423 | static int hit = 0; |
10424 | if (hit++ == 0) |
10425 | { |
10426 | _ASSERTE(!"Access violation while Jitting!" ); |
10427 | // If you set the debugger to catch access violations and 'go' |
10428 | // you will get back to the point at which the access violation occurred |
10429 | result = EXCEPTION_CONTINUE_EXECUTION; |
10430 | } |
10431 | else |
10432 | { |
10433 | result = EXCEPTION_CONTINUE_SEARCH; |
10434 | } |
10435 | } |
10436 | else |
10437 | #endif // _DEBUG |
10438 | // No one should be catching breakpoint |
10439 | // Similarly the JIT doesn't know how to reset the guard page, so it shouldn't |
10440 | // be catching a hard stack overflow |
10441 | if (code == EXCEPTION_BREAKPOINT || code == EXCEPTION_SINGLE_STEP || code == EXCEPTION_STACK_OVERFLOW) |
10442 | { |
10443 | result = EXCEPTION_CONTINUE_SEARCH; |
10444 | } |
10445 | #ifdef CROSSGEN_COMPILE |
10446 | else |
10447 | { |
10448 | result = EXCEPTION_EXECUTE_HANDLER; |
10449 | } |
10450 | #else |
10451 | else if (!IsComPlusException(pExceptionPointers->ExceptionRecord)) |
10452 | { |
10453 | result = EXCEPTION_EXECUTE_HANDLER; |
10454 | } |
10455 | else |
10456 | { |
10457 | GCX_COOP(); |
10458 | |
10459 | // This is actually the LastThrown exception object. |
10460 | OBJECTREF throwable = CLRException::GetThrowableFromExceptionRecord(pExceptionPointers->ExceptionRecord); |
10461 | |
10462 | if (throwable != NULL) |
10463 | { |
10464 | struct |
10465 | { |
10466 | OBJECTREF oLastThrownObject; |
10467 | } _gc; |
10468 | |
10469 | ZeroMemory(&_gc, sizeof(_gc)); |
10470 | |
10471 | // Setup the throwables |
10472 | _gc.oLastThrownObject = throwable; |
10473 | |
10474 | GCPROTECT_BEGIN(_gc); |
10475 | |
10476 | // Don't catch ThreadAbort and other uncatchable exceptions |
10477 | if (IsUncatchable(&_gc.oLastThrownObject)) |
10478 | result = EXCEPTION_CONTINUE_SEARCH; |
10479 | else |
10480 | result = EXCEPTION_EXECUTE_HANDLER; |
10481 | |
10482 | GCPROTECT_END(); |
10483 | } |
10484 | } |
10485 | #endif |
10486 | |
10487 | EE_TO_JIT_TRANSITION_LEAF(); |
10488 | |
10489 | return result; |
10490 | } |
10491 | |
10492 | int CEEInfo::FilterException(struct _EXCEPTION_POINTERS *pExceptionPointers) |
10493 | { |
10494 | WRAPPER_NO_CONTRACT; |
10495 | return EEFilterException(pExceptionPointers, nullptr); |
10496 | } |
10497 | |
10498 | // This code is called if FilterException chose to handle the exception. |
10499 | void CEEInfo::HandleException(struct _EXCEPTION_POINTERS *pExceptionPointers) |
10500 | { |
10501 | CONTRACTL { |
10502 | SO_TOLERANT; |
10503 | NOTHROW; |
10504 | GC_NOTRIGGER; |
10505 | } CONTRACTL_END; |
10506 | |
10507 | JIT_TO_EE_TRANSITION_LEAF(); |
10508 | |
10509 | #ifndef CROSSGEN_COMPILE |
10510 | if (IsComPlusException(pExceptionPointers->ExceptionRecord)) |
10511 | { |
10512 | GCX_COOP(); |
10513 | |
10514 | // This is actually the LastThrown exception object. |
10515 | OBJECTREF throwable = CLRException::GetThrowableFromExceptionRecord(pExceptionPointers->ExceptionRecord); |
10516 | |
10517 | if (throwable != NULL) |
10518 | { |
10519 | struct |
10520 | { |
10521 | OBJECTREF oLastThrownObject; |
10522 | OBJECTREF oCurrentThrowable; |
10523 | } _gc; |
10524 | |
10525 | ZeroMemory(&_gc, sizeof(_gc)); |
10526 | |
10527 | PTR_Thread pCurThread = GetThread(); |
10528 | |
10529 | // Setup the throwables |
10530 | _gc.oLastThrownObject = throwable; |
10531 | |
10532 | // This will be NULL if no managed exception is active. Otherwise, |
10533 | // it will reference the active throwable. |
10534 | _gc.oCurrentThrowable = pCurThread->GetThrowable(); |
10535 | |
10536 | GCPROTECT_BEGIN(_gc); |
10537 | |
10538 | // JIT does not use or reference managed exceptions at all and simply swallows them, |
10539 | // or lets them fly through so that they will either get caught in managed code, the VM |
10540 | // or will go unhandled. |
10541 | // |
10542 | // Blind swallowing of managed exceptions can break the semantic of "which exception handler" |
10543 | // gets to process the managed exception first. The expected handler is managed code exception |
10544 | // handler (e.g. COMPlusFrameHandler on x86 and ProcessCLRException on 64bit) which will setup |
10545 | // the exception tracker for the exception that will enable the expected sync between the |
10546 | // LastThrownObject (LTO), setup in RaiseTheExceptionInternalOnly, and the exception tracker. |
10547 | // |
10548 | // However, JIT can break this by swallowing the managed exception before managed code exception |
10549 | // handler gets a chance to setup an exception tracker for it. Since there is no cleanup |
10550 | // done for the swallowed exception as part of the unwind (because no exception tracker may have been setup), |
10551 | // we need to reset the LTO, if it is out of sync from the active throwable. |
10552 | // |
10553 | // Hence, check if the LastThrownObject and active-exception throwable are in sync or not. |
10554 | // If not, bring them in sync. |
10555 | // |
10556 | // Example |
10557 | // ------- |
10558 | // It is possible that an exception was already in progress and while processing it (e.g. |
10559 | // invoking finally block), we invoked JIT that had another managed exception @ JIT-EE transition boundary |
10560 | // that is swallowed by the JIT before managed code exception handler sees it. This breaks the sync between |
10561 | // LTO and the active exception in the exception tracker. |
10562 | if (_gc.oCurrentThrowable != _gc.oLastThrownObject) |
10563 | { |
10564 | // Update the LTO. |
10565 | // |
10566 | // Note: Incase of OOM, this will get set to OOM instance. |
10567 | pCurThread->SafeSetLastThrownObject(_gc.oCurrentThrowable); |
10568 | } |
10569 | |
10570 | GCPROTECT_END(); |
10571 | } |
10572 | } |
10573 | #endif |
10574 | |
10575 | EE_TO_JIT_TRANSITION_LEAF(); |
10576 | } |
10577 | |
10578 | void ThrowExceptionForJit(HRESULT res); |
10579 | |
10580 | void CEEInfo::ThrowExceptionForJitResult( |
10581 | HRESULT result) |
10582 | { |
10583 | CONTRACTL { |
10584 | SO_TOLERANT; |
10585 | THROWS; |
10586 | GC_TRIGGERS; |
10587 | MODE_PREEMPTIVE; |
10588 | } CONTRACTL_END; |
10589 | |
10590 | JIT_TO_EE_TRANSITION(); |
10591 | |
10592 | if (!SUCCEEDED(result)) |
10593 | ThrowExceptionForJit(result); |
10594 | |
10595 | EE_TO_JIT_TRANSITION(); |
10596 | } |
10597 | |
10598 | |
10599 | CORINFO_MODULE_HANDLE CEEInfo::embedModuleHandle(CORINFO_MODULE_HANDLE handle, |
10600 | void **ppIndirection) |
10601 | { |
10602 | CONTRACTL { |
10603 | SO_TOLERANT; |
10604 | NOTHROW; |
10605 | GC_NOTRIGGER; |
10606 | MODE_PREEMPTIVE; |
10607 | PRECONDITION(!IsDynamicScope(handle)); |
10608 | } |
10609 | CONTRACTL_END; |
10610 | |
10611 | if (ppIndirection != NULL) |
10612 | *ppIndirection = NULL; |
10613 | |
10614 | JIT_TO_EE_TRANSITION_LEAF(); |
10615 | |
10616 | EE_TO_JIT_TRANSITION_LEAF(); |
10617 | |
10618 | return handle; |
10619 | } |
10620 | |
10621 | CORINFO_CLASS_HANDLE CEEInfo::embedClassHandle(CORINFO_CLASS_HANDLE handle, |
10622 | void **ppIndirection) |
10623 | { |
10624 | CONTRACTL { |
10625 | SO_TOLERANT; |
10626 | NOTHROW; |
10627 | GC_NOTRIGGER; |
10628 | MODE_PREEMPTIVE; |
10629 | } |
10630 | CONTRACTL_END; |
10631 | |
10632 | if (ppIndirection != NULL) |
10633 | *ppIndirection = NULL; |
10634 | |
10635 | JIT_TO_EE_TRANSITION_LEAF(); |
10636 | |
10637 | EE_TO_JIT_TRANSITION_LEAF(); |
10638 | |
10639 | return handle; |
10640 | } |
10641 | |
10642 | CORINFO_FIELD_HANDLE CEEInfo::embedFieldHandle(CORINFO_FIELD_HANDLE handle, |
10643 | void **ppIndirection) |
10644 | { |
10645 | CONTRACTL { |
10646 | SO_TOLERANT; |
10647 | NOTHROW; |
10648 | GC_NOTRIGGER; |
10649 | MODE_PREEMPTIVE; |
10650 | } |
10651 | CONTRACTL_END; |
10652 | |
10653 | if (ppIndirection != NULL) |
10654 | *ppIndirection = NULL; |
10655 | |
10656 | JIT_TO_EE_TRANSITION_LEAF(); |
10657 | |
10658 | EE_TO_JIT_TRANSITION_LEAF(); |
10659 | |
10660 | return handle; |
10661 | } |
10662 | |
10663 | CORINFO_METHOD_HANDLE CEEInfo::embedMethodHandle(CORINFO_METHOD_HANDLE handle, |
10664 | void **ppIndirection) |
10665 | { |
10666 | CONTRACTL { |
10667 | SO_TOLERANT; |
10668 | NOTHROW; |
10669 | GC_NOTRIGGER; |
10670 | MODE_PREEMPTIVE; |
10671 | } |
10672 | CONTRACTL_END; |
10673 | |
10674 | if (ppIndirection != NULL) |
10675 | *ppIndirection = NULL; |
10676 | |
10677 | JIT_TO_EE_TRANSITION_LEAF(); |
10678 | |
10679 | EE_TO_JIT_TRANSITION_LEAF(); |
10680 | |
10681 | return handle; |
10682 | } |
10683 | |
10684 | /*********************************************************************/ |
10685 | void CEEInfo::setJitFlags(const CORJIT_FLAGS& jitFlags) |
10686 | { |
10687 | LIMITED_METHOD_CONTRACT; |
10688 | |
10689 | m_jitFlags = jitFlags; |
10690 | } |
10691 | |
10692 | /*********************************************************************/ |
10693 | DWORD CEEInfo::getJitFlags(CORJIT_FLAGS* jitFlags, DWORD sizeInBytes) |
10694 | { |
10695 | CONTRACTL { |
10696 | SO_TOLERANT; |
10697 | NOTHROW; |
10698 | GC_NOTRIGGER; |
10699 | MODE_PREEMPTIVE; |
10700 | } CONTRACTL_END; |
10701 | |
10702 | JIT_TO_EE_TRANSITION_LEAF(); |
10703 | |
10704 | _ASSERTE(sizeInBytes >= sizeof(m_jitFlags)); |
10705 | *jitFlags = m_jitFlags; |
10706 | |
10707 | EE_TO_JIT_TRANSITION_LEAF(); |
10708 | |
10709 | return sizeof(m_jitFlags); |
10710 | } |
10711 | |
10712 | /*********************************************************************/ |
10713 | #if !defined(PLATFORM_UNIX) |
10714 | |
10715 | struct RunWithErrorTrapFilterParam |
10716 | { |
10717 | ICorDynamicInfo* m_corInfo; |
10718 | void (*m_function)(void*); |
10719 | void* m_param; |
10720 | EXCEPTION_POINTERS m_exceptionPointers; |
10721 | }; |
10722 | |
10723 | static LONG RunWithErrorTrapFilter(struct _EXCEPTION_POINTERS* exceptionPointers, void* theParam) |
10724 | { |
10725 | WRAPPER_NO_CONTRACT; |
10726 | |
10727 | auto* param = reinterpret_cast<RunWithErrorTrapFilterParam*>(theParam); |
10728 | param->m_exceptionPointers = *exceptionPointers; |
10729 | return param->m_corInfo->FilterException(exceptionPointers); |
10730 | } |
10731 | |
10732 | #endif // !defined(PLATFORM_UNIX) |
10733 | |
10734 | bool CEEInfo::runWithErrorTrap(void (*function)(void*), void* param) |
10735 | { |
10736 | // No dynamic contract here because SEH is used |
10737 | STATIC_CONTRACT_THROWS; |
10738 | STATIC_CONTRACT_GC_TRIGGERS; |
10739 | STATIC_CONTRACT_SO_TOLERANT; |
10740 | STATIC_CONTRACT_MODE_PREEMPTIVE; |
10741 | |
10742 | // NOTE: the lack of JIT/EE transition markers in this method is intentional. Any |
10743 | // transitions into the EE proper should occur either via the call to |
10744 | // `EEFilterException` (which is appropriately marked) or via JIT/EE |
10745 | // interface calls made by `function`. |
10746 | |
10747 | bool success = true; |
10748 | |
10749 | #if !defined(PLATFORM_UNIX) |
10750 | |
10751 | RunWithErrorTrapFilterParam trapParam; |
10752 | trapParam.m_corInfo = m_pOverride == nullptr ? this : m_pOverride; |
10753 | trapParam.m_function = function; |
10754 | trapParam.m_param = param; |
10755 | |
10756 | PAL_TRY(RunWithErrorTrapFilterParam*, pTrapParam, &trapParam) |
10757 | { |
10758 | pTrapParam->m_function(pTrapParam->m_param); |
10759 | } |
10760 | PAL_EXCEPT_FILTER(RunWithErrorTrapFilter) |
10761 | { |
10762 | HandleException(&trapParam.m_exceptionPointers); |
10763 | success = false; |
10764 | } |
10765 | PAL_ENDTRY |
10766 | |
10767 | #else // !defined(PLATFORM_UNIX) |
10768 | |
10769 | // We shouldn't need PAL_TRY on *nix: any exceptions that we are able to catch |
10770 | // ought to originate from the runtime itself and should be catchable inside of |
10771 | // EX_TRY/EX_CATCH, including emulated SEH exceptions. |
10772 | EX_TRY |
10773 | { |
10774 | function(param); |
10775 | } |
10776 | EX_CATCH |
10777 | { |
10778 | success = false; |
10779 | } |
10780 | EX_END_CATCH(RethrowTerminalExceptions); |
10781 | |
10782 | #endif |
10783 | |
10784 | return success; |
10785 | } |
10786 | |
10787 | /*********************************************************************/ |
10788 | IEEMemoryManager* CEEInfo::getMemoryManager() |
10789 | { |
10790 | CONTRACTL { |
10791 | SO_TOLERANT; |
10792 | NOTHROW; |
10793 | GC_NOTRIGGER; |
10794 | MODE_PREEMPTIVE; |
10795 | } CONTRACTL_END; |
10796 | |
10797 | IEEMemoryManager* result = NULL; |
10798 | |
10799 | JIT_TO_EE_TRANSITION_LEAF(); |
10800 | |
10801 | result = GetEEMemoryManager(); |
10802 | |
10803 | EE_TO_JIT_TRANSITION_LEAF(); |
10804 | |
10805 | return result; |
10806 | } |
10807 | |
10808 | /*********************************************************************/ |
10809 | int CEEInfo::doAssert(const char* szFile, int iLine, const char* szExpr) |
10810 | { |
10811 | STATIC_CONTRACT_SO_TOLERANT; |
10812 | STATIC_CONTRACT_THROWS; |
10813 | STATIC_CONTRACT_GC_TRIGGERS; |
10814 | STATIC_CONTRACT_MODE_PREEMPTIVE; |
10815 | STATIC_CONTRACT_DEBUG_ONLY; |
10816 | |
10817 | int result = 0; |
10818 | |
10819 | JIT_TO_EE_TRANSITION(); |
10820 | |
10821 | #ifdef CROSSGEN_COMPILE |
10822 | ThrowHR(COR_E_INVALIDPROGRAM); |
10823 | #else |
10824 | |
10825 | #ifdef _DEBUG |
10826 | BEGIN_DEBUG_ONLY_CODE; |
10827 | result = _DbgBreakCheck(szFile, iLine, szExpr); |
10828 | END_DEBUG_ONLY_CODE; |
10829 | #else // !_DEBUG |
10830 | result = 1; // break into debugger |
10831 | #endif // !_DEBUG |
10832 | |
10833 | #endif |
10834 | |
10835 | EE_TO_JIT_TRANSITION(); |
10836 | |
10837 | return result; |
10838 | } |
10839 | |
10840 | void CEEInfo::reportFatalError(CorJitResult result) |
10841 | { |
10842 | STATIC_CONTRACT_SO_TOLERANT; |
10843 | STATIC_CONTRACT_THROWS; |
10844 | STATIC_CONTRACT_GC_TRIGGERS; |
10845 | STATIC_CONTRACT_MODE_PREEMPTIVE; |
10846 | |
10847 | JIT_TO_EE_TRANSITION_LEAF(); |
10848 | |
10849 | STRESS_LOG2(LF_JIT,LL_ERROR, "Jit reported error 0x%x while compiling 0x%p\n" , |
10850 | (int)result, (INT_PTR)getMethodBeingCompiled()); |
10851 | |
10852 | EE_TO_JIT_TRANSITION_LEAF(); |
10853 | } |
10854 | |
10855 | BOOL CEEInfo::logMsg(unsigned level, const char* fmt, va_list args) |
10856 | { |
10857 | STATIC_CONTRACT_SO_TOLERANT; |
10858 | STATIC_CONTRACT_THROWS; |
10859 | STATIC_CONTRACT_GC_TRIGGERS; |
10860 | STATIC_CONTRACT_MODE_PREEMPTIVE; |
10861 | STATIC_CONTRACT_DEBUG_ONLY; |
10862 | |
10863 | BOOL result = FALSE; |
10864 | |
10865 | JIT_TO_EE_TRANSITION_LEAF(); |
10866 | |
10867 | #ifdef LOGGING |
10868 | if (LoggingOn(LF_JIT, level)) |
10869 | { |
10870 | LogSpewValist(LF_JIT, level, (char*) fmt, args); |
10871 | result = TRUE; |
10872 | } |
10873 | #endif // LOGGING |
10874 | |
10875 | EE_TO_JIT_TRANSITION_LEAF(); |
10876 | |
10877 | return result; |
10878 | } |
10879 | |
10880 | void CEEInfo::yieldExecution() |
10881 | { |
10882 | WRAPPER_NO_CONTRACT; |
10883 | } |
10884 | |
10885 | |
10886 | #ifndef CROSSGEN_COMPILE |
10887 | |
10888 | /*********************************************************************/ |
10889 | |
10890 | void* CEEJitInfo::getHelperFtn(CorInfoHelpFunc ftnNum, /* IN */ |
10891 | void ** ppIndirection) /* OUT */ |
10892 | { |
10893 | CONTRACTL { |
10894 | SO_TOLERANT; |
10895 | NOTHROW; |
10896 | GC_NOTRIGGER; |
10897 | MODE_PREEMPTIVE; |
10898 | } CONTRACTL_END; |
10899 | |
10900 | void* result = NULL; |
10901 | |
10902 | if (ppIndirection != NULL) |
10903 | *ppIndirection = NULL; |
10904 | |
10905 | JIT_TO_EE_TRANSITION_LEAF(); |
10906 | |
10907 | _ASSERTE(ftnNum < CORINFO_HELP_COUNT); |
10908 | |
10909 | void* pfnHelper = hlpFuncTable[ftnNum].pfnHelper; |
10910 | |
10911 | size_t dynamicFtnNum = ((size_t)pfnHelper - 1); |
10912 | if (dynamicFtnNum < DYNAMIC_CORINFO_HELP_COUNT) |
10913 | { |
10914 | #ifdef _PREFAST_ |
10915 | #pragma warning(push) |
10916 | #pragma warning(disable:26001) // "Bounds checked above using the underflow trick" |
10917 | #endif /*_PREFAST_ */ |
10918 | |
10919 | #if defined(_TARGET_AMD64_) |
10920 | // To avoid using a jump stub we always call certain helpers using an indirect call. |
10921 | // Because when using a direct call and the target is father away than 2^31 bytes, |
10922 | // the direct call instead goes to a jump stub which jumps to the jit helper. |
10923 | // However in this process the jump stub will corrupt RAX. |
10924 | // |
10925 | // The set of helpers for which RAX must be preserved are the profiler probes |
10926 | // and the STOP_FOR_GC helper which maps to JIT_RareDisableHelper. |
10927 | // In the case of the STOP_FOR_GC helper RAX can be holding a function return value. |
10928 | // |
10929 | if (dynamicFtnNum == DYNAMIC_CORINFO_HELP_STOP_FOR_GC || |
10930 | dynamicFtnNum == DYNAMIC_CORINFO_HELP_PROF_FCN_ENTER || |
10931 | dynamicFtnNum == DYNAMIC_CORINFO_HELP_PROF_FCN_LEAVE || |
10932 | dynamicFtnNum == DYNAMIC_CORINFO_HELP_PROF_FCN_TAILCALL) |
10933 | { |
10934 | _ASSERTE(ppIndirection != NULL); |
10935 | *ppIndirection = &hlpDynamicFuncTable[dynamicFtnNum].pfnHelper; |
10936 | return NULL; |
10937 | } |
10938 | #endif |
10939 | |
10940 | #if defined(ENABLE_FAST_GCPOLL_HELPER) |
10941 | //always call this indirectly so that we can swap GC Poll helpers. |
10942 | if (dynamicFtnNum == DYNAMIC_CORINFO_HELP_POLL_GC) |
10943 | { |
10944 | _ASSERTE(ppIndirection != NULL); |
10945 | *ppIndirection = &hlpDynamicFuncTable[dynamicFtnNum].pfnHelper; |
10946 | return NULL; |
10947 | } |
10948 | #endif |
10949 | |
10950 | pfnHelper = hlpDynamicFuncTable[dynamicFtnNum].pfnHelper; |
10951 | |
10952 | #ifdef _PREFAST_ |
10953 | #pragma warning(pop) |
10954 | #endif /*_PREFAST_*/ |
10955 | } |
10956 | |
10957 | _ASSERTE(pfnHelper); |
10958 | |
10959 | result = (LPVOID)GetEEFuncEntryPoint(pfnHelper); |
10960 | |
10961 | EE_TO_JIT_TRANSITION_LEAF(); |
10962 | |
10963 | return result; |
10964 | } |
10965 | |
10966 | PCODE CEEJitInfo::getHelperFtnStatic(CorInfoHelpFunc ftnNum) |
10967 | { |
10968 | LIMITED_METHOD_CONTRACT; |
10969 | |
10970 | void* pfnHelper = hlpFuncTable[ftnNum].pfnHelper; |
10971 | |
10972 | // If pfnHelper is an index into the dynamic helper table, it should be less |
10973 | // than DYNAMIC_CORINFO_HELP_COUNT. In this case we need to find the actual pfnHelper |
10974 | // using an extra indirection. Note the special case |
10975 | // where pfnHelper==0 where pfnHelper-1 will underflow and we will avoid the indirection. |
10976 | if (((size_t)pfnHelper - 1) < DYNAMIC_CORINFO_HELP_COUNT) |
10977 | { |
10978 | pfnHelper = hlpDynamicFuncTable[((size_t)pfnHelper - 1)].pfnHelper; |
10979 | } |
10980 | |
10981 | _ASSERTE(pfnHelper != NULL); |
10982 | |
10983 | return GetEEFuncEntryPoint(pfnHelper); |
10984 | } |
10985 | |
10986 | void CEEJitInfo::addActiveDependency(CORINFO_MODULE_HANDLE moduleFrom,CORINFO_MODULE_HANDLE moduleTo) |
10987 | { |
10988 | CONTRACTL |
10989 | { |
10990 | STANDARD_VM_CHECK; |
10991 | PRECONDITION(CheckPointer(moduleFrom)); |
10992 | PRECONDITION(!IsDynamicScope(moduleFrom)); |
10993 | PRECONDITION(CheckPointer(moduleTo)); |
10994 | PRECONDITION(!IsDynamicScope(moduleTo)); |
10995 | PRECONDITION(moduleFrom != moduleTo); |
10996 | } |
10997 | CONTRACTL_END; |
10998 | |
10999 | // This is only called internaly. JIT-EE transition is not needed. |
11000 | // JIT_TO_EE_TRANSITION(); |
11001 | |
11002 | Module *dependency = (Module *)moduleTo; |
11003 | _ASSERTE(!dependency->IsSystem()); |
11004 | |
11005 | dependency->EnsureActive(); |
11006 | |
11007 | // EE_TO_JIT_TRANSITION(); |
11008 | } |
11009 | |
11010 | |
11011 | // Wrapper around CEEInfo::GetProfilingHandle. The first time this is called for a |
11012 | // method desc, it calls through to EEToProfInterfaceImpl::EEFunctionIDMappe and caches the |
11013 | // result in CEEJitInfo::GetProfilingHandleCache. Thereafter, this wrapper regurgitates the cached values |
11014 | // rather than calling into CEEInfo::GetProfilingHandle each time. This avoids |
11015 | // making duplicate calls into the profiler's FunctionIDMapper callback. |
11016 | void CEEJitInfo::GetProfilingHandle(BOOL *pbHookFunction, |
11017 | void **pProfilerHandle, |
11018 | BOOL *pbIndirectedHandles) |
11019 | { |
11020 | CONTRACTL { |
11021 | SO_TOLERANT; |
11022 | THROWS; |
11023 | GC_TRIGGERS; |
11024 | MODE_PREEMPTIVE; |
11025 | } CONTRACTL_END; |
11026 | |
11027 | _ASSERTE(pbHookFunction != NULL); |
11028 | _ASSERTE(pProfilerHandle != NULL); |
11029 | _ASSERTE(pbIndirectedHandles != NULL); |
11030 | |
11031 | if (!m_gphCache.m_bGphIsCacheValid) |
11032 | { |
11033 | #ifdef PROFILING_SUPPORTED |
11034 | JIT_TO_EE_TRANSITION(); |
11035 | |
11036 | // Cache not filled in, so make our first and only call to CEEInfo::GetProfilingHandle here |
11037 | |
11038 | // methods with no metadata behind cannot be exposed to tools expecting metadata (profiler, debugger...) |
11039 | // they shouldnever come here as they are called out in GetCompileFlag |
11040 | _ASSERTE(!m_pMethodBeingCompiled->IsNoMetadata()); |
11041 | |
11042 | // We pass in the typical method definition to the function mapper because in |
11043 | // Whidbey all the profiling API transactions are done in terms of typical |
11044 | // method definitions not instantiations. |
11045 | BOOL bHookFunction = TRUE; |
11046 | void * profilerHandle = m_pMethodBeingCompiled; |
11047 | |
11048 | { |
11049 | BEGIN_PIN_PROFILER(CORProfilerFunctionIDMapperEnabled()); |
11050 | profilerHandle = (void *)g_profControlBlock.pProfInterface->EEFunctionIDMapper((FunctionID) m_pMethodBeingCompiled, &bHookFunction); |
11051 | END_PIN_PROFILER(); |
11052 | } |
11053 | |
11054 | m_gphCache.m_pvGphProfilerHandle = profilerHandle; |
11055 | m_gphCache.m_bGphHookFunction = (bHookFunction != FALSE); |
11056 | m_gphCache.m_bGphIsCacheValid = true; |
11057 | |
11058 | EE_TO_JIT_TRANSITION(); |
11059 | #endif //PROFILING_SUPPORTED |
11060 | } |
11061 | |
11062 | // Our cache of these values are bitfield bools, but the interface requires |
11063 | // BOOL. So to avoid setting aside a staging area on the stack for these |
11064 | // values, we filled them in directly in the if (not cached yet) case. |
11065 | *pbHookFunction = (m_gphCache.m_bGphHookFunction != false); |
11066 | |
11067 | // At this point, the remaining values must be in the cache by now, so use them |
11068 | *pProfilerHandle = m_gphCache.m_pvGphProfilerHandle; |
11069 | |
11070 | // |
11071 | // This is the JIT case, which is never indirected. |
11072 | // |
11073 | *pbIndirectedHandles = FALSE; |
11074 | } |
11075 | |
11076 | /*********************************************************************/ |
11077 | void CEEJitInfo::BackoutJitData(EEJitManager * jitMgr) |
11078 | { |
11079 | CONTRACTL { |
11080 | NOTHROW; |
11081 | GC_TRIGGERS; |
11082 | } CONTRACTL_END; |
11083 | |
11084 | CodeHeader* pCodeHeader = GetCodeHeader(); |
11085 | if (pCodeHeader) |
11086 | jitMgr->RemoveJitData(pCodeHeader, m_GCinfo_len, m_EHinfo_len); |
11087 | } |
11088 | |
11089 | /*********************************************************************/ |
11090 | // Route jit information to the Jit Debug store. |
11091 | void CEEJitInfo::setBoundaries(CORINFO_METHOD_HANDLE ftn, ULONG32 cMap, |
11092 | ICorDebugInfo::OffsetMapping *pMap) |
11093 | { |
11094 | CONTRACTL { |
11095 | SO_TOLERANT; |
11096 | THROWS; |
11097 | GC_TRIGGERS; |
11098 | MODE_PREEMPTIVE; |
11099 | } CONTRACTL_END; |
11100 | |
11101 | JIT_TO_EE_TRANSITION(); |
11102 | |
11103 | // We receive ownership of the array |
11104 | _ASSERTE(m_pOffsetMapping == NULL && m_iOffsetMapping == 0); |
11105 | m_iOffsetMapping = cMap; |
11106 | m_pOffsetMapping = pMap; |
11107 | |
11108 | EE_TO_JIT_TRANSITION(); |
11109 | } |
11110 | |
11111 | void CEEJitInfo::setVars(CORINFO_METHOD_HANDLE ftn, ULONG32 cVars, ICorDebugInfo::NativeVarInfo *vars) |
11112 | { |
11113 | CONTRACTL { |
11114 | SO_TOLERANT; |
11115 | THROWS; |
11116 | GC_TRIGGERS; |
11117 | MODE_PREEMPTIVE; |
11118 | } CONTRACTL_END; |
11119 | |
11120 | JIT_TO_EE_TRANSITION(); |
11121 | |
11122 | // We receive ownership of the array |
11123 | _ASSERTE(m_pNativeVarInfo == NULL && m_iNativeVarInfo == 0); |
11124 | m_iNativeVarInfo = cVars; |
11125 | m_pNativeVarInfo = vars; |
11126 | |
11127 | EE_TO_JIT_TRANSITION(); |
11128 | } |
11129 | |
11130 | void CEEJitInfo::CompressDebugInfo() |
11131 | { |
11132 | CONTRACTL { |
11133 | SO_TOLERANT; |
11134 | THROWS; |
11135 | GC_TRIGGERS; |
11136 | MODE_PREEMPTIVE; |
11137 | } CONTRACTL_END; |
11138 | |
11139 | // Don't track JIT info for DynamicMethods. |
11140 | if (m_pMethodBeingCompiled->IsDynamicMethod()) |
11141 | return; |
11142 | |
11143 | if (m_iOffsetMapping == 0 && m_iNativeVarInfo == 0) |
11144 | return; |
11145 | |
11146 | JIT_TO_EE_TRANSITION(); |
11147 | |
11148 | EX_TRY |
11149 | { |
11150 | PTR_BYTE pDebugInfo = CompressDebugInfo::CompressBoundariesAndVars( |
11151 | m_pOffsetMapping, m_iOffsetMapping, |
11152 | m_pNativeVarInfo, m_iNativeVarInfo, |
11153 | NULL, |
11154 | m_pMethodBeingCompiled->GetLoaderAllocator()->GetLowFrequencyHeap()); |
11155 | |
11156 | GetCodeHeader()->SetDebugInfo(pDebugInfo); |
11157 | } |
11158 | EX_CATCH |
11159 | { |
11160 | // Just ignore exceptions here. The debugger's structures will still be in a consistent state. |
11161 | } |
11162 | EX_END_CATCH(SwallowAllExceptions) |
11163 | |
11164 | EE_TO_JIT_TRANSITION(); |
11165 | } |
11166 | |
11167 | void reservePersonalityRoutineSpace(ULONG &unwindSize) |
11168 | { |
11169 | #if defined(_TARGET_X86_) |
11170 | // Do nothing |
11171 | #elif defined(_TARGET_AMD64_) |
11172 | // Add space for personality routine, it must be 4-byte aligned. |
11173 | // Everything in the UNWIND_INFO up to the variable-sized UnwindCodes |
11174 | // array has already had its size included in unwindSize by the caller. |
11175 | unwindSize += sizeof(ULONG); |
11176 | |
11177 | // Note that the count of unwind codes (2 bytes each) is stored as a UBYTE |
11178 | // So the largest size could be 510 bytes, plus the header and language |
11179 | // specific stuff. This can't overflow. |
11180 | |
11181 | _ASSERTE(FitsInU4(unwindSize + sizeof(ULONG))); |
11182 | unwindSize = (ULONG)(ALIGN_UP(unwindSize, sizeof(ULONG))); |
11183 | #elif defined(_TARGET_ARM_) || defined(_TARGET_ARM64_) |
11184 | // The JIT passes in a 4-byte aligned block of unwind data. |
11185 | _ASSERTE(IS_ALIGNED(unwindSize, sizeof(ULONG))); |
11186 | |
11187 | // Add space for personality routine, it must be 4-byte aligned. |
11188 | unwindSize += sizeof(ULONG); |
11189 | #else |
11190 | PORTABILITY_ASSERT("reservePersonalityRoutineSpace" ); |
11191 | #endif // !defined(_TARGET_AMD64_) |
11192 | |
11193 | } |
11194 | // Reserve memory for the method/funclet's unwind information. |
11195 | // Note that this must be called before allocMem. It should be |
11196 | // called once for the main method, once for every funclet, and |
11197 | // once for every block of cold code for which allocUnwindInfo |
11198 | // will be called. |
11199 | // |
11200 | // This is necessary because jitted code must allocate all the |
11201 | // memory needed for the unwindInfo at the allocMem call. |
11202 | // For prejitted code we split up the unwinding information into |
11203 | // separate sections .rdata and .pdata. |
11204 | // |
11205 | void CEEJitInfo::reserveUnwindInfo(BOOL isFunclet, BOOL isColdCode, ULONG unwindSize) |
11206 | { |
11207 | #ifdef WIN64EXCEPTIONS |
11208 | CONTRACTL { |
11209 | SO_TOLERANT; |
11210 | NOTHROW; |
11211 | GC_NOTRIGGER; |
11212 | MODE_PREEMPTIVE; |
11213 | } |
11214 | CONTRACTL_END; |
11215 | |
11216 | JIT_TO_EE_TRANSITION_LEAF(); |
11217 | |
11218 | CONSISTENCY_CHECK_MSG(!isColdCode, "Hot/Cold splitting is not supported in jitted code" ); |
11219 | _ASSERTE_MSG(m_theUnwindBlock == NULL, |
11220 | "reserveUnwindInfo() can only be called before allocMem(), but allocMem() has already been called. " |
11221 | "This may indicate the JIT has hit a NO_WAY assert after calling allocMem(), and is re-JITting. " |
11222 | "Set COMPlus_JitBreakOnBadCode=1 and rerun to get the real error." ); |
11223 | |
11224 | ULONG currentSize = unwindSize; |
11225 | |
11226 | reservePersonalityRoutineSpace(currentSize); |
11227 | |
11228 | m_totalUnwindSize += currentSize; |
11229 | |
11230 | m_totalUnwindInfos++; |
11231 | |
11232 | EE_TO_JIT_TRANSITION_LEAF(); |
11233 | #else // WIN64EXCEPTIONS |
11234 | LIMITED_METHOD_CONTRACT; |
11235 | // Dummy implementation to make cross-platform altjit work |
11236 | #endif // WIN64EXCEPTIONS |
11237 | } |
11238 | |
11239 | // Allocate and initialize the .rdata and .pdata for this method or |
11240 | // funclet and get the block of memory needed for the machine specific |
11241 | // unwind information (the info for crawling the stack frame). |
11242 | // Note that allocMem must be called first. |
11243 | // |
11244 | // The pHotCode parameter points at the first byte of the code of the method |
11245 | // The startOffset and endOffset are the region (main or funclet) that |
11246 | // we are to allocate and create .rdata and .pdata for. |
11247 | // The pUnwindBlock is copied and contains the .pdata unwind area |
11248 | // |
11249 | // Parameters: |
11250 | // |
11251 | // pHotCode main method code buffer, always filled in |
11252 | // pColdCode always NULL for jitted code |
11253 | // startOffset start of code block, relative to pHotCode |
11254 | // endOffset end of code block, relative to pHotCode |
11255 | // unwindSize size of unwind info pointed to by pUnwindBlock |
11256 | // pUnwindBlock pointer to unwind info |
11257 | // funcKind type of funclet (main method code, handler, filter) |
11258 | // |
11259 | void CEEJitInfo::allocUnwindInfo ( |
11260 | BYTE * pHotCode, /* IN */ |
11261 | BYTE * pColdCode, /* IN */ |
11262 | ULONG startOffset, /* IN */ |
11263 | ULONG endOffset, /* IN */ |
11264 | ULONG unwindSize, /* IN */ |
11265 | BYTE * pUnwindBlock, /* IN */ |
11266 | CorJitFuncKind funcKind /* IN */ |
11267 | ) |
11268 | { |
11269 | #ifdef WIN64EXCEPTIONS |
11270 | CONTRACTL { |
11271 | SO_TOLERANT; |
11272 | THROWS; |
11273 | GC_TRIGGERS; |
11274 | MODE_PREEMPTIVE; |
11275 | PRECONDITION(m_theUnwindBlock != NULL); |
11276 | PRECONDITION(m_usedUnwindSize < m_totalUnwindSize); |
11277 | PRECONDITION(m_usedUnwindInfos < m_totalUnwindInfos); |
11278 | PRECONDITION(endOffset <= m_codeSize); |
11279 | } CONTRACTL_END; |
11280 | |
11281 | CONSISTENCY_CHECK_MSG(pColdCode == NULL, "Hot/Cold code splitting not supported for jitted code" ); |
11282 | |
11283 | JIT_TO_EE_TRANSITION(); |
11284 | |
11285 | // |
11286 | // We add one callback-type dynamic function table per range section. |
11287 | // Therefore, the RUNTIME_FUNCTION info is always relative to the |
11288 | // image base contained in the dynamic function table, which happens |
11289 | // to be the LowAddress of the range section. The JIT has no |
11290 | // knowledge of the range section, so it gives us offsets that are |
11291 | // relative to the beginning of the method (pHotCode) and we allocate |
11292 | // and initialize the RUNTIME_FUNCTION data and record its location |
11293 | // in this function. |
11294 | // |
11295 | |
11296 | if (funcKind != CORJIT_FUNC_ROOT) |
11297 | { |
11298 | // The main method should be emitted before funclets |
11299 | _ASSERTE(m_usedUnwindInfos > 0); |
11300 | } |
11301 | |
11302 | PT_RUNTIME_FUNCTION pRuntimeFunction = m_CodeHeader->GetUnwindInfo(m_usedUnwindInfos); |
11303 | m_usedUnwindInfos++; |
11304 | |
11305 | // Make sure that the RUNTIME_FUNCTION is aligned on a DWORD sized boundary |
11306 | _ASSERTE(IS_ALIGNED(pRuntimeFunction, sizeof(DWORD))); |
11307 | |
11308 | UNWIND_INFO * pUnwindInfo = (UNWIND_INFO *) &(m_theUnwindBlock[m_usedUnwindSize]); |
11309 | m_usedUnwindSize += unwindSize; |
11310 | |
11311 | reservePersonalityRoutineSpace(m_usedUnwindSize); |
11312 | |
11313 | _ASSERTE(m_usedUnwindSize <= m_totalUnwindSize); |
11314 | |
11315 | // Make sure that the UnwindInfo is aligned |
11316 | _ASSERTE(IS_ALIGNED(pUnwindInfo, sizeof(ULONG))); |
11317 | |
11318 | /* Calculate Image Relative offset to add to the jit generated unwind offsets */ |
11319 | |
11320 | TADDR baseAddress = m_moduleBase; |
11321 | |
11322 | size_t currentCodeSizeT = (size_t)pHotCode - baseAddress; |
11323 | |
11324 | /* Check if currentCodeSizeT offset fits in 32-bits */ |
11325 | if (!FitsInU4(currentCodeSizeT)) |
11326 | { |
11327 | _ASSERTE(!"Bad currentCodeSizeT" ); |
11328 | COMPlusThrowHR(E_FAIL); |
11329 | } |
11330 | |
11331 | /* Check if EndAddress offset fits in 32-bit */ |
11332 | if (!FitsInU4(currentCodeSizeT + endOffset)) |
11333 | { |
11334 | _ASSERTE(!"Bad currentCodeSizeT" ); |
11335 | COMPlusThrowHR(E_FAIL); |
11336 | } |
11337 | |
11338 | unsigned currentCodeOffset = (unsigned) currentCodeSizeT; |
11339 | |
11340 | /* Calculate Unwind Info delta */ |
11341 | size_t unwindInfoDeltaT = (size_t) pUnwindInfo - baseAddress; |
11342 | |
11343 | /* Check if unwindDeltaT offset fits in 32-bits */ |
11344 | if (!FitsInU4(unwindInfoDeltaT)) |
11345 | { |
11346 | _ASSERTE(!"Bad unwindInfoDeltaT" ); |
11347 | COMPlusThrowHR(E_FAIL); |
11348 | } |
11349 | |
11350 | unsigned unwindInfoDelta = (unsigned) unwindInfoDeltaT; |
11351 | |
11352 | RUNTIME_FUNCTION__SetBeginAddress(pRuntimeFunction, currentCodeOffset + startOffset); |
11353 | |
11354 | #ifdef _TARGET_AMD64_ |
11355 | pRuntimeFunction->EndAddress = currentCodeOffset + endOffset; |
11356 | #endif |
11357 | |
11358 | RUNTIME_FUNCTION__SetUnwindInfoAddress(pRuntimeFunction, unwindInfoDelta); |
11359 | |
11360 | #ifdef _DEBUG |
11361 | if (funcKind != CORJIT_FUNC_ROOT) |
11362 | { |
11363 | // Check the the new funclet doesn't overlap any existing funclet. |
11364 | |
11365 | for (ULONG iUnwindInfo = 0; iUnwindInfo < m_usedUnwindInfos - 1; iUnwindInfo++) |
11366 | { |
11367 | PT_RUNTIME_FUNCTION pOtherFunction = m_CodeHeader->GetUnwindInfo(iUnwindInfo); |
11368 | _ASSERTE(( RUNTIME_FUNCTION__BeginAddress(pOtherFunction) >= RUNTIME_FUNCTION__EndAddress(pRuntimeFunction, baseAddress) |
11369 | || RUNTIME_FUNCTION__EndAddress(pOtherFunction, baseAddress) <= RUNTIME_FUNCTION__BeginAddress(pRuntimeFunction))); |
11370 | } |
11371 | } |
11372 | #endif // _DEBUG |
11373 | |
11374 | /* Copy the UnwindBlock */ |
11375 | memcpy(pUnwindInfo, pUnwindBlock, unwindSize); |
11376 | |
11377 | #if defined(_TARGET_X86_) |
11378 | |
11379 | // Do NOTHING |
11380 | |
11381 | #elif defined(_TARGET_AMD64_) |
11382 | |
11383 | pUnwindInfo->Flags = UNW_FLAG_EHANDLER | UNW_FLAG_UHANDLER; |
11384 | |
11385 | ULONG * pPersonalityRoutine = (ULONG*)ALIGN_UP(&(pUnwindInfo->UnwindCode[pUnwindInfo->CountOfUnwindCodes]), sizeof(ULONG)); |
11386 | *pPersonalityRoutine = ExecutionManager::GetCLRPersonalityRoutineValue(); |
11387 | |
11388 | #elif defined(_TARGET_ARM64_) |
11389 | |
11390 | *(LONG *)pUnwindInfo |= (1 << 20); // X bit |
11391 | |
11392 | ULONG * pPersonalityRoutine = (ULONG*)((BYTE *)pUnwindInfo + ALIGN_UP(unwindSize, sizeof(ULONG))); |
11393 | *pPersonalityRoutine = ExecutionManager::GetCLRPersonalityRoutineValue(); |
11394 | |
11395 | #elif defined(_TARGET_ARM_) |
11396 | |
11397 | *(LONG *)pUnwindInfo |= (1 << 20); // X bit |
11398 | |
11399 | ULONG * pPersonalityRoutine = (ULONG*)((BYTE *)pUnwindInfo + ALIGN_UP(unwindSize, sizeof(ULONG))); |
11400 | *pPersonalityRoutine = (TADDR)ProcessCLRException - baseAddress; |
11401 | |
11402 | #endif |
11403 | |
11404 | #if defined(_TARGET_AMD64_) |
11405 | // Publish the new unwind information in a way that the ETW stack crawler can find |
11406 | if (m_usedUnwindInfos == m_totalUnwindInfos) |
11407 | UnwindInfoTable::PublishUnwindInfoForMethod(baseAddress, m_CodeHeader->GetUnwindInfo(0), m_totalUnwindInfos); |
11408 | #endif // defined(_TARGET_AMD64_) |
11409 | |
11410 | EE_TO_JIT_TRANSITION(); |
11411 | #else // WIN64EXCEPTIONS |
11412 | LIMITED_METHOD_CONTRACT; |
11413 | // Dummy implementation to make cross-platform altjit work |
11414 | #endif // WIN64EXCEPTIONS |
11415 | } |
11416 | |
11417 | void CEEJitInfo::recordCallSite(ULONG instrOffset, |
11418 | CORINFO_SIG_INFO * callSig, |
11419 | CORINFO_METHOD_HANDLE methodHandle) |
11420 | { |
11421 | // Currently, only testing tools use this method. The EE itself doesn't need record this information. |
11422 | // N.B. The memory that callSig points to is managed by the JIT and isn't guaranteed to be around after |
11423 | // this function returns, so future implementations should copy the sig info if they want it to persist. |
11424 | LIMITED_METHOD_CONTRACT; |
11425 | } |
11426 | |
11427 | // This is a variant for AMD64 or other machines that |
11428 | // cannot always hold the destination address in a 32-bit location |
11429 | // A relocation is recorded if we are pre-jitting. |
11430 | // A jump thunk may be inserted if we are jitting |
11431 | |
11432 | void CEEJitInfo::recordRelocation(void * location, |
11433 | void * target, |
11434 | WORD fRelocType, |
11435 | WORD slot, |
11436 | INT32 addlDelta) |
11437 | { |
11438 | CONTRACTL { |
11439 | SO_TOLERANT; |
11440 | THROWS; |
11441 | GC_TRIGGERS; |
11442 | MODE_PREEMPTIVE; |
11443 | } CONTRACTL_END; |
11444 | |
11445 | #ifdef _WIN64 |
11446 | JIT_TO_EE_TRANSITION(); |
11447 | |
11448 | INT64 delta; |
11449 | |
11450 | switch (fRelocType) |
11451 | { |
11452 | case IMAGE_REL_BASED_DIR64: |
11453 | // Write 64-bits into location |
11454 | *((UINT64 *) ((BYTE *) location + slot)) = (UINT64) target; |
11455 | break; |
11456 | |
11457 | #ifdef _TARGET_AMD64_ |
11458 | case IMAGE_REL_BASED_REL32: |
11459 | { |
11460 | target = (BYTE *)target + addlDelta; |
11461 | |
11462 | INT32 * fixupLocation = (INT32 *) ((BYTE *) location + slot); |
11463 | BYTE * baseAddr = (BYTE *)fixupLocation + sizeof(INT32); |
11464 | |
11465 | delta = (INT64)((BYTE *)target - baseAddr); |
11466 | |
11467 | // |
11468 | // Do we need to insert a jump stub to make the source reach the target? |
11469 | // |
11470 | // Note that we cannot stress insertion of jump stub by inserting it unconditionally. JIT records the relocations |
11471 | // for intra-module jumps and calls. It does not expect the register used by the jump stub to be trashed. |
11472 | // |
11473 | if (!FitsInI4(delta)) |
11474 | { |
11475 | if (m_fAllowRel32) |
11476 | { |
11477 | // |
11478 | // When m_fAllowRel32 == TRUE, the JIT will use REL32s for both data addresses and direct code targets. |
11479 | // Since we cannot tell what the relocation is for, we have to defensively retry. |
11480 | // |
11481 | m_fJumpStubOverflow = TRUE; |
11482 | delta = 0; |
11483 | } |
11484 | else |
11485 | { |
11486 | // |
11487 | // When m_fAllowRel32 == FALSE, the JIT will use a REL32s for direct code targets only. |
11488 | // Use jump stub. |
11489 | // |
11490 | delta = rel32UsingJumpStub(fixupLocation, (PCODE)target, m_pMethodBeingCompiled, NULL, false /* throwOnOutOfMemoryWithinRange */); |
11491 | if (delta == 0) |
11492 | { |
11493 | // This forces the JIT to retry the method, which allows us to reserve more space for jump stubs and have a higher chance that |
11494 | // we will find space for them. |
11495 | m_fJumpStubOverflow = TRUE; |
11496 | } |
11497 | |
11498 | // Keep track of conservative estimate of how much memory may be needed by jump stubs. We will use it to reserve extra memory |
11499 | // on retry to increase chances that the retry succeeds. |
11500 | m_reserveForJumpStubs = max(0x400, m_reserveForJumpStubs + 0x10); |
11501 | } |
11502 | } |
11503 | |
11504 | LOG((LF_JIT, LL_INFO100000, "Encoded a PCREL32 at" FMT_ADDR "to" FMT_ADDR "+%d, delta is 0x%04x\n" , |
11505 | DBG_ADDR(fixupLocation), DBG_ADDR(target), addlDelta, delta)); |
11506 | |
11507 | // Write the 32-bits pc-relative delta into location |
11508 | *fixupLocation = (INT32) delta; |
11509 | } |
11510 | break; |
11511 | #endif // _TARGET_AMD64_ |
11512 | |
11513 | #ifdef _TARGET_ARM64_ |
11514 | case IMAGE_REL_ARM64_BRANCH26: // 26 bit offset << 2 & sign ext, for B and BL |
11515 | { |
11516 | _ASSERTE(slot == 0); |
11517 | _ASSERTE(addlDelta == 0); |
11518 | |
11519 | PCODE branchTarget = (PCODE) target; |
11520 | _ASSERTE((branchTarget & 0x3) == 0); // the low two bits must be zero |
11521 | |
11522 | PCODE fixupLocation = (PCODE) location; |
11523 | _ASSERTE((fixupLocation & 0x3) == 0); // the low two bits must be zero |
11524 | |
11525 | delta = (INT64)(branchTarget - fixupLocation); |
11526 | _ASSERTE((delta & 0x3) == 0); // the low two bits must be zero |
11527 | |
11528 | UINT32 branchInstr = *((UINT32*) fixupLocation); |
11529 | branchInstr &= 0xFC000000; // keep bits 31-26 |
11530 | _ASSERTE((branchInstr & 0x7FFFFFFF) == 0x14000000); // Must be B or BL |
11531 | |
11532 | // |
11533 | // Do we need to insert a jump stub to make the source reach the target? |
11534 | // |
11535 | // |
11536 | if (!FitsInRel28(delta)) |
11537 | { |
11538 | // Use jump stub. |
11539 | // |
11540 | TADDR baseAddr = (TADDR)fixupLocation; |
11541 | TADDR loAddr = baseAddr - 0x08000000; // -2^27 |
11542 | TADDR hiAddr = baseAddr + 0x07FFFFFF; // +2^27-1 |
11543 | |
11544 | // Check for the wrap around cases |
11545 | if (loAddr > baseAddr) |
11546 | loAddr = UINT64_MIN; // overflow |
11547 | if (hiAddr < baseAddr) |
11548 | hiAddr = UINT64_MAX; // overflow |
11549 | |
11550 | PCODE jumpStubAddr = ExecutionManager::jumpStub(m_pMethodBeingCompiled, |
11551 | (PCODE) target, |
11552 | (BYTE *) loAddr, |
11553 | (BYTE *) hiAddr, |
11554 | NULL, |
11555 | false); |
11556 | |
11557 | // Keep track of conservative estimate of how much memory may be needed by jump stubs. We will use it to reserve extra memory |
11558 | // on retry to increase chances that the retry succeeds. |
11559 | m_reserveForJumpStubs = max(0x400, m_reserveForJumpStubs + 2*BACK_TO_BACK_JUMP_ALLOCATE_SIZE); |
11560 | |
11561 | if (jumpStubAddr == 0) |
11562 | { |
11563 | // This forces the JIT to retry the method, which allows us to reserve more space for jump stubs and have a higher chance that |
11564 | // we will find space for them. |
11565 | m_fJumpStubOverflow = TRUE; |
11566 | break; |
11567 | } |
11568 | |
11569 | delta = (INT64)(jumpStubAddr - fixupLocation); |
11570 | |
11571 | if (!FitsInRel28(delta)) |
11572 | { |
11573 | _ASSERTE(!"jump stub was not in expected range" ); |
11574 | EEPOLICY_HANDLE_FATAL_ERROR(COR_E_EXECUTIONENGINE); |
11575 | } |
11576 | |
11577 | LOG((LF_JIT, LL_INFO100000, "Using JumpStub at" FMT_ADDR "that jumps to" FMT_ADDR "\n" , |
11578 | DBG_ADDR(jumpStubAddr), DBG_ADDR(target))); |
11579 | } |
11580 | |
11581 | LOG((LF_JIT, LL_INFO100000, "Encoded a BRANCH26 at" FMT_ADDR "to" FMT_ADDR ", delta is 0x%04x\n" , |
11582 | DBG_ADDR(fixupLocation), DBG_ADDR(target), delta)); |
11583 | |
11584 | _ASSERTE(FitsInRel28(delta)); |
11585 | |
11586 | PutArm64Rel28((UINT32*) fixupLocation, (INT32)delta); |
11587 | } |
11588 | break; |
11589 | |
11590 | case IMAGE_REL_ARM64_PAGEBASE_REL21: |
11591 | { |
11592 | _ASSERTE(slot == 0); |
11593 | _ASSERTE(addlDelta == 0); |
11594 | |
11595 | // Write the 21 bits pc-relative page address into location. |
11596 | INT64 targetPage = (INT64)target & 0xFFFFFFFFFFFFF000LL; |
11597 | INT64 locationPage = (INT64)location & 0xFFFFFFFFFFFFF000LL; |
11598 | INT64 relPage = (INT64)(targetPage - locationPage); |
11599 | INT32 imm21 = (INT32)(relPage >> 12) & 0x1FFFFF; |
11600 | PutArm64Rel21((UINT32 *)location, imm21); |
11601 | } |
11602 | break; |
11603 | |
11604 | case IMAGE_REL_ARM64_PAGEOFFSET_12A: |
11605 | { |
11606 | _ASSERTE(slot == 0); |
11607 | _ASSERTE(addlDelta == 0); |
11608 | |
11609 | // Write the 12 bits page offset into location. |
11610 | INT32 imm12 = (INT32)target & 0xFFFLL; |
11611 | PutArm64Rel12((UINT32 *)location, imm12); |
11612 | } |
11613 | break; |
11614 | |
11615 | #endif // _TARGET_ARM64_ |
11616 | |
11617 | default: |
11618 | _ASSERTE(!"Unknown reloc type" ); |
11619 | break; |
11620 | } |
11621 | |
11622 | EE_TO_JIT_TRANSITION(); |
11623 | #else // _WIN64 |
11624 | JIT_TO_EE_TRANSITION_LEAF(); |
11625 | |
11626 | // Nothing to do on 32-bit |
11627 | |
11628 | EE_TO_JIT_TRANSITION_LEAF(); |
11629 | #endif // _WIN64 |
11630 | } |
11631 | |
11632 | WORD CEEJitInfo::getRelocTypeHint(void * target) |
11633 | { |
11634 | CONTRACTL { |
11635 | SO_TOLERANT; |
11636 | THROWS; |
11637 | GC_TRIGGERS; |
11638 | MODE_PREEMPTIVE; |
11639 | } CONTRACTL_END; |
11640 | |
11641 | #ifdef _TARGET_AMD64_ |
11642 | if (m_fAllowRel32) |
11643 | { |
11644 | // The JIT calls this method for data addresses only. It always uses REL32s for direct code targets. |
11645 | if (IsPreferredExecutableRange(target)) |
11646 | return IMAGE_REL_BASED_REL32; |
11647 | } |
11648 | #endif // _TARGET_AMD64_ |
11649 | |
11650 | // No hints |
11651 | return (WORD)-1; |
11652 | } |
11653 | |
11654 | void CEEJitInfo::getModuleNativeEntryPointRange(void** pStart, void** pEnd) |
11655 | { |
11656 | CONTRACTL { |
11657 | SO_TOLERANT; |
11658 | NOTHROW; |
11659 | GC_NOTRIGGER; |
11660 | MODE_PREEMPTIVE; |
11661 | } |
11662 | CONTRACTL_END; |
11663 | |
11664 | JIT_TO_EE_TRANSITION_LEAF(); |
11665 | |
11666 | *pStart = *pEnd = 0; |
11667 | |
11668 | EE_TO_JIT_TRANSITION_LEAF(); |
11669 | } |
11670 | |
11671 | DWORD CEEJitInfo::getExpectedTargetArchitecture() |
11672 | { |
11673 | LIMITED_METHOD_CONTRACT; |
11674 | |
11675 | return IMAGE_FILE_MACHINE_NATIVE; |
11676 | } |
11677 | |
11678 | void CEEInfo::JitProcessShutdownWork() |
11679 | { |
11680 | LIMITED_METHOD_CONTRACT; |
11681 | |
11682 | EEJitManager* jitMgr = ExecutionManager::GetEEJitManager(); |
11683 | |
11684 | // If we didn't load the JIT, there is no work to do. |
11685 | if (jitMgr->m_jit != NULL) |
11686 | { |
11687 | // Do the shutdown work. |
11688 | jitMgr->m_jit->ProcessShutdownWork(this); |
11689 | } |
11690 | |
11691 | #ifdef ALLOW_SXS_JIT |
11692 | if (jitMgr->m_alternateJit != NULL) |
11693 | { |
11694 | jitMgr->m_alternateJit->ProcessShutdownWork(this); |
11695 | } |
11696 | #endif // ALLOW_SXS_JIT |
11697 | } |
11698 | |
11699 | /*********************************************************************/ |
11700 | InfoAccessType CEEJitInfo::constructStringLiteral(CORINFO_MODULE_HANDLE scopeHnd, |
11701 | mdToken metaTok, |
11702 | void **ppValue) |
11703 | { |
11704 | CONTRACTL { |
11705 | SO_TOLERANT; |
11706 | THROWS; |
11707 | GC_TRIGGERS; |
11708 | MODE_PREEMPTIVE; |
11709 | } CONTRACTL_END; |
11710 | |
11711 | InfoAccessType result = IAT_PVALUE; |
11712 | |
11713 | JIT_TO_EE_TRANSITION(); |
11714 | |
11715 | _ASSERTE(ppValue != NULL); |
11716 | |
11717 | if (IsDynamicScope(scopeHnd)) |
11718 | { |
11719 | *ppValue = (LPVOID)GetDynamicResolver(scopeHnd)->ConstructStringLiteral(metaTok); |
11720 | } |
11721 | else |
11722 | { |
11723 | *ppValue = (LPVOID)ConstructStringLiteral(scopeHnd, metaTok); // throws |
11724 | } |
11725 | |
11726 | EE_TO_JIT_TRANSITION(); |
11727 | |
11728 | return result; |
11729 | } |
11730 | |
11731 | /*********************************************************************/ |
11732 | InfoAccessType CEEJitInfo::emptyStringLiteral(void ** ppValue) |
11733 | { |
11734 | CONTRACTL { |
11735 | SO_TOLERANT; |
11736 | THROWS; |
11737 | GC_TRIGGERS; |
11738 | MODE_PREEMPTIVE; |
11739 | } CONTRACTL_END; |
11740 | |
11741 | InfoAccessType result = IAT_PVALUE; |
11742 | |
11743 | if(NingenEnabled()) |
11744 | { |
11745 | *ppValue = NULL; |
11746 | return result; |
11747 | } |
11748 | |
11749 | JIT_TO_EE_TRANSITION(); |
11750 | *ppValue = StringObject::GetEmptyStringRefPtr(); |
11751 | EE_TO_JIT_TRANSITION(); |
11752 | |
11753 | return result; |
11754 | } |
11755 | |
11756 | /*********************************************************************/ |
11757 | void* CEEJitInfo::getFieldAddress(CORINFO_FIELD_HANDLE fieldHnd, |
11758 | void **ppIndirection) |
11759 | { |
11760 | CONTRACTL { |
11761 | SO_TOLERANT; |
11762 | THROWS; |
11763 | GC_TRIGGERS; |
11764 | MODE_PREEMPTIVE; |
11765 | } CONTRACTL_END; |
11766 | |
11767 | void *result = NULL; |
11768 | |
11769 | if (ppIndirection != NULL) |
11770 | *ppIndirection = NULL; |
11771 | |
11772 | // Do not bother with initialization if we are only verifying the method. |
11773 | if (isVerifyOnly()) |
11774 | { |
11775 | return (void *)0x10; |
11776 | } |
11777 | |
11778 | JIT_TO_EE_TRANSITION(); |
11779 | |
11780 | FieldDesc* field = (FieldDesc*) fieldHnd; |
11781 | |
11782 | MethodTable* pMT = field->GetEnclosingMethodTable(); |
11783 | |
11784 | _ASSERTE(!pMT->ContainsGenericVariables()); |
11785 | |
11786 | void *base = NULL; |
11787 | |
11788 | if (!field->IsRVA()) |
11789 | { |
11790 | // <REVISIT_TODO>@todo: assert that the current method being compiled is unshared</REVISIT_TODO> |
11791 | // We must not call here for statics of collectible types. |
11792 | _ASSERTE(!pMT->Collectible()); |
11793 | |
11794 | // Allocate space for the local class if necessary, but don't trigger |
11795 | // class construction. |
11796 | DomainLocalModule *pLocalModule = pMT->GetDomainLocalModule(); |
11797 | pLocalModule->PopulateClass(pMT); |
11798 | |
11799 | GCX_COOP(); |
11800 | |
11801 | base = (void *) field->GetBase(); |
11802 | } |
11803 | |
11804 | result = field->GetStaticAddressHandle(base); |
11805 | |
11806 | EE_TO_JIT_TRANSITION(); |
11807 | |
11808 | return result; |
11809 | } |
11810 | |
11811 | /*********************************************************************/ |
11812 | CORINFO_CLASS_HANDLE CEEJitInfo::getStaticFieldCurrentClass(CORINFO_FIELD_HANDLE fieldHnd, |
11813 | bool* pIsSpeculative) |
11814 | { |
11815 | CONTRACTL { |
11816 | SO_TOLERANT; |
11817 | THROWS; |
11818 | GC_TRIGGERS; |
11819 | MODE_PREEMPTIVE; |
11820 | } CONTRACTL_END; |
11821 | |
11822 | CORINFO_CLASS_HANDLE result = NULL; |
11823 | |
11824 | if (pIsSpeculative != NULL) |
11825 | { |
11826 | *pIsSpeculative = true; |
11827 | } |
11828 | |
11829 | // Only examine the field's value if we are producing jitted code. |
11830 | if (isVerifyOnly() || IsCompilingForNGen() || IsReadyToRunCompilation()) |
11831 | { |
11832 | return result; |
11833 | } |
11834 | |
11835 | JIT_TO_EE_TRANSITION(); |
11836 | |
11837 | FieldDesc* field = (FieldDesc*) fieldHnd; |
11838 | bool isClassInitialized = false; |
11839 | |
11840 | // We're only interested in ref class typed static fields |
11841 | // where the field handle specifies a unique location. |
11842 | if (field->IsStatic() && field->IsObjRef() && !field->IsThreadStatic()) |
11843 | { |
11844 | MethodTable* pEnclosingMT = field->GetEnclosingMethodTable(); |
11845 | |
11846 | if (!pEnclosingMT->IsSharedByGenericInstantiations()) |
11847 | { |
11848 | // Allocate space for the local class if necessary, but don't trigger |
11849 | // class construction. |
11850 | DomainLocalModule *pLocalModule = pEnclosingMT->GetDomainLocalModule(); |
11851 | pLocalModule->PopulateClass(pEnclosingMT); |
11852 | |
11853 | GCX_COOP(); |
11854 | |
11855 | OBJECTREF fieldObj = field->GetStaticOBJECTREF(); |
11856 | VALIDATEOBJECTREF(fieldObj); |
11857 | |
11858 | // Check for initialization before looking at the value |
11859 | isClassInitialized = !!pEnclosingMT->IsClassInited(); |
11860 | |
11861 | if (fieldObj != NULL) |
11862 | { |
11863 | MethodTable *pObjMT = fieldObj->GetMethodTable(); |
11864 | |
11865 | // TODO: Check if the jit is allowed to embed this handle in jitted code. |
11866 | // Note for the initonly cases it probably won't embed. |
11867 | result = (CORINFO_CLASS_HANDLE) pObjMT; |
11868 | } |
11869 | } |
11870 | } |
11871 | |
11872 | // Did we find a class? |
11873 | if (result != NULL) |
11874 | { |
11875 | // Figure out what to report back. |
11876 | bool isResultImmutable = isClassInitialized && IsFdInitOnly(field->GetAttributes()); |
11877 | |
11878 | if (pIsSpeculative != NULL) |
11879 | { |
11880 | // Caller is ok with potentially mutable results. |
11881 | *pIsSpeculative = !isResultImmutable; |
11882 | } |
11883 | else |
11884 | { |
11885 | // Caller only wants to see immutable results. |
11886 | if (!isResultImmutable) |
11887 | { |
11888 | result = NULL; |
11889 | } |
11890 | } |
11891 | } |
11892 | |
11893 | EE_TO_JIT_TRANSITION(); |
11894 | |
11895 | return result; |
11896 | } |
11897 | |
11898 | /*********************************************************************/ |
11899 | static void *GetClassSync(MethodTable *pMT) |
11900 | { |
11901 | STANDARD_VM_CONTRACT; |
11902 | |
11903 | GCX_COOP(); |
11904 | |
11905 | OBJECTREF ref = pMT->GetManagedClassObject(); |
11906 | return (void*)ref->GetSyncBlock()->GetMonitor(); |
11907 | } |
11908 | |
11909 | /*********************************************************************/ |
11910 | void* CEEJitInfo::getMethodSync(CORINFO_METHOD_HANDLE ftnHnd, |
11911 | void **ppIndirection) |
11912 | { |
11913 | CONTRACTL { |
11914 | SO_TOLERANT; |
11915 | THROWS; |
11916 | GC_TRIGGERS; |
11917 | MODE_PREEMPTIVE; |
11918 | } CONTRACTL_END; |
11919 | |
11920 | void * result = NULL; |
11921 | |
11922 | if (ppIndirection != NULL) |
11923 | *ppIndirection = NULL; |
11924 | |
11925 | JIT_TO_EE_TRANSITION(); |
11926 | |
11927 | result = GetClassSync((GetMethod(ftnHnd))->GetMethodTable()); |
11928 | |
11929 | EE_TO_JIT_TRANSITION(); |
11930 | |
11931 | return result; |
11932 | } |
11933 | |
11934 | /*********************************************************************/ |
11935 | HRESULT CEEJitInfo::allocBBProfileBuffer ( |
11936 | ULONG count, |
11937 | ICorJitInfo::ProfileBuffer ** profileBuffer |
11938 | ) |
11939 | { |
11940 | CONTRACTL { |
11941 | SO_TOLERANT; |
11942 | THROWS; |
11943 | GC_TRIGGERS; |
11944 | MODE_PREEMPTIVE; |
11945 | } CONTRACTL_END; |
11946 | |
11947 | HRESULT hr = E_FAIL; |
11948 | |
11949 | JIT_TO_EE_TRANSITION(); |
11950 | |
11951 | #ifdef FEATURE_PREJIT |
11952 | |
11953 | // We need to know the code size. Typically we can get the code size |
11954 | // from m_ILHeader. For dynamic methods, m_ILHeader will be NULL, so |
11955 | // for that case we need to use DynamicResolver to get the code size. |
11956 | |
11957 | unsigned codeSize = 0; |
11958 | if (m_pMethodBeingCompiled->IsDynamicMethod()) |
11959 | { |
11960 | unsigned stackSize, ehSize; |
11961 | CorInfoOptions options; |
11962 | DynamicResolver * pResolver = m_pMethodBeingCompiled->AsDynamicMethodDesc()->GetResolver(); |
11963 | pResolver->GetCodeInfo(&codeSize, &stackSize, &options, &ehSize); |
11964 | } |
11965 | else |
11966 | { |
11967 | codeSize = m_ILHeader->GetCodeSize(); |
11968 | } |
11969 | |
11970 | *profileBuffer = m_pMethodBeingCompiled->GetLoaderModule()->AllocateProfileBuffer(m_pMethodBeingCompiled->GetMemberDef(), count, codeSize); |
11971 | hr = (*profileBuffer ? S_OK : E_OUTOFMEMORY); |
11972 | #else // FEATURE_PREJIT |
11973 | _ASSERTE(!"allocBBProfileBuffer not implemented on CEEJitInfo!" ); |
11974 | hr = E_NOTIMPL; |
11975 | #endif // !FEATURE_PREJIT |
11976 | |
11977 | EE_TO_JIT_TRANSITION(); |
11978 | |
11979 | return hr; |
11980 | } |
11981 | |
11982 | // Consider implementing getBBProfileData on CEEJitInfo. This will allow us |
11983 | // to use profile info in codegen for non zapped images. |
11984 | HRESULT CEEJitInfo::getBBProfileData ( |
11985 | CORINFO_METHOD_HANDLE ftnHnd, |
11986 | ULONG * size, |
11987 | ICorJitInfo::ProfileBuffer ** profileBuffer, |
11988 | ULONG * numRuns |
11989 | ) |
11990 | { |
11991 | LIMITED_METHOD_CONTRACT; |
11992 | _ASSERTE(!"getBBProfileData not implemented on CEEJitInfo!" ); |
11993 | return E_NOTIMPL; |
11994 | } |
11995 | |
11996 | void CEEJitInfo::allocMem ( |
11997 | ULONG hotCodeSize, /* IN */ |
11998 | ULONG coldCodeSize, /* IN */ |
11999 | ULONG roDataSize, /* IN */ |
12000 | ULONG xcptnsCount, /* IN */ |
12001 | CorJitAllocMemFlag flag, /* IN */ |
12002 | void ** hotCodeBlock, /* OUT */ |
12003 | void ** coldCodeBlock, /* OUT */ |
12004 | void ** roDataBlock /* OUT */ |
12005 | ) |
12006 | { |
12007 | CONTRACTL { |
12008 | SO_TOLERANT; |
12009 | THROWS; |
12010 | GC_TRIGGERS; |
12011 | MODE_PREEMPTIVE; |
12012 | } CONTRACTL_END; |
12013 | |
12014 | JIT_TO_EE_TRANSITION(); |
12015 | |
12016 | _ASSERTE(coldCodeSize == 0); |
12017 | if (coldCodeBlock) |
12018 | { |
12019 | *coldCodeBlock = NULL; |
12020 | } |
12021 | |
12022 | ULONG codeSize = hotCodeSize; |
12023 | void **codeBlock = hotCodeBlock; |
12024 | |
12025 | S_SIZE_T totalSize = S_SIZE_T(codeSize); |
12026 | |
12027 | size_t roDataAlignment = sizeof(void*); |
12028 | if ((flag & CORJIT_ALLOCMEM_FLG_RODATA_16BYTE_ALIGN)!= 0) |
12029 | { |
12030 | roDataAlignment = 16; |
12031 | } |
12032 | else if (roDataSize >= 8) |
12033 | { |
12034 | roDataAlignment = 8; |
12035 | } |
12036 | if (roDataSize > 0) |
12037 | { |
12038 | size_t codeAlignment = ((flag & CORJIT_ALLOCMEM_FLG_16BYTE_ALIGN)!= 0) |
12039 | ? 16 : sizeof(void*); |
12040 | totalSize.AlignUp(codeAlignment); |
12041 | if (roDataAlignment > codeAlignment) { |
12042 | // Add padding to align read-only data. |
12043 | totalSize += (roDataAlignment - codeAlignment); |
12044 | } |
12045 | totalSize += roDataSize; |
12046 | } |
12047 | |
12048 | #ifdef WIN64EXCEPTIONS |
12049 | totalSize.AlignUp(sizeof(DWORD)); |
12050 | totalSize += m_totalUnwindSize; |
12051 | #endif |
12052 | |
12053 | _ASSERTE(m_CodeHeader == 0 && |
12054 | // The jit-compiler sometimes tries to compile a method a second time |
12055 | // if it failed the first time. In such a situation, m_CodeHeader may |
12056 | // have already been assigned. Its OK to ignore this assert in such a |
12057 | // situation - we will leak some memory, but that is acceptable |
12058 | // since this should happen very rarely. |
12059 | "Note that this may fire if the JITCompiler tries to recompile a method" ); |
12060 | |
12061 | if( totalSize.IsOverflow() ) |
12062 | { |
12063 | COMPlusThrowHR(CORJIT_OUTOFMEM); |
12064 | } |
12065 | |
12066 | m_CodeHeader = m_jitManager->allocCode(m_pMethodBeingCompiled, totalSize.Value(), GetReserveForJumpStubs(), flag |
12067 | #ifdef WIN64EXCEPTIONS |
12068 | , m_totalUnwindInfos |
12069 | , &m_moduleBase |
12070 | #endif |
12071 | ); |
12072 | |
12073 | BYTE* current = (BYTE *)m_CodeHeader->GetCodeStartAddress(); |
12074 | |
12075 | *codeBlock = current; |
12076 | current += codeSize; |
12077 | |
12078 | if (roDataSize > 0) |
12079 | { |
12080 | current = (BYTE *)ALIGN_UP(current, roDataAlignment); |
12081 | *roDataBlock = current; |
12082 | current += roDataSize; |
12083 | } |
12084 | else |
12085 | { |
12086 | *roDataBlock = NULL; |
12087 | } |
12088 | |
12089 | #ifdef WIN64EXCEPTIONS |
12090 | current = (BYTE *)ALIGN_UP(current, sizeof(DWORD)); |
12091 | |
12092 | m_theUnwindBlock = current; |
12093 | current += m_totalUnwindSize; |
12094 | #endif |
12095 | |
12096 | _ASSERTE((SIZE_T)(current - (BYTE *)m_CodeHeader->GetCodeStartAddress()) <= totalSize.Value()); |
12097 | |
12098 | #ifdef _DEBUG |
12099 | m_codeSize = codeSize; |
12100 | #endif // _DEBUG |
12101 | |
12102 | EE_TO_JIT_TRANSITION(); |
12103 | } |
12104 | |
12105 | /*********************************************************************/ |
12106 | void * CEEJitInfo::allocGCInfo (size_t size) |
12107 | { |
12108 | CONTRACTL { |
12109 | SO_TOLERANT; |
12110 | THROWS; |
12111 | GC_TRIGGERS; |
12112 | MODE_PREEMPTIVE; |
12113 | } CONTRACTL_END; |
12114 | |
12115 | void * block = NULL; |
12116 | |
12117 | JIT_TO_EE_TRANSITION(); |
12118 | |
12119 | _ASSERTE(m_CodeHeader != 0); |
12120 | _ASSERTE(m_CodeHeader->GetGCInfo() == 0); |
12121 | |
12122 | #ifdef _WIN64 |
12123 | if (size & 0xFFFFFFFF80000000LL) |
12124 | { |
12125 | COMPlusThrowHR(CORJIT_OUTOFMEM); |
12126 | } |
12127 | #endif // _WIN64 |
12128 | |
12129 | block = m_jitManager->allocGCInfo(m_CodeHeader,(DWORD)size, &m_GCinfo_len); |
12130 | if (!block) |
12131 | { |
12132 | COMPlusThrowHR(CORJIT_OUTOFMEM); |
12133 | } |
12134 | |
12135 | _ASSERTE(m_CodeHeader->GetGCInfo() != 0 && block == m_CodeHeader->GetGCInfo()); |
12136 | |
12137 | EE_TO_JIT_TRANSITION(); |
12138 | |
12139 | return block; |
12140 | } |
12141 | |
12142 | /*********************************************************************/ |
12143 | void CEEJitInfo::setEHcount ( |
12144 | unsigned cEH) |
12145 | { |
12146 | CONTRACTL { |
12147 | SO_TOLERANT; |
12148 | THROWS; |
12149 | GC_TRIGGERS; |
12150 | MODE_PREEMPTIVE; |
12151 | } CONTRACTL_END; |
12152 | |
12153 | JIT_TO_EE_TRANSITION(); |
12154 | |
12155 | _ASSERTE(cEH != 0); |
12156 | _ASSERTE(m_CodeHeader != 0); |
12157 | _ASSERTE(m_CodeHeader->GetEHInfo() == 0); |
12158 | |
12159 | EE_ILEXCEPTION* ret; |
12160 | ret = m_jitManager->allocEHInfo(m_CodeHeader,cEH, &m_EHinfo_len); |
12161 | _ASSERTE(ret); // allocEHInfo throws if there's not enough memory |
12162 | |
12163 | _ASSERTE(m_CodeHeader->GetEHInfo() != 0 && m_CodeHeader->GetEHInfo()->EHCount() == cEH); |
12164 | |
12165 | EE_TO_JIT_TRANSITION(); |
12166 | } |
12167 | |
12168 | /*********************************************************************/ |
12169 | void CEEJitInfo::setEHinfo ( |
12170 | unsigned EHnumber, |
12171 | const CORINFO_EH_CLAUSE* clause) |
12172 | { |
12173 | CONTRACTL { |
12174 | SO_TOLERANT; |
12175 | THROWS; |
12176 | GC_TRIGGERS; |
12177 | MODE_PREEMPTIVE; |
12178 | } CONTRACTL_END; |
12179 | |
12180 | JIT_TO_EE_TRANSITION(); |
12181 | |
12182 | // <REVISIT_TODO> Fix make the Code Manager EH clauses EH_INFO+</REVISIT_TODO> |
12183 | _ASSERTE(m_CodeHeader->GetEHInfo() != 0 && EHnumber < m_CodeHeader->GetEHInfo()->EHCount()); |
12184 | |
12185 | EE_ILEXCEPTION_CLAUSE* pEHClause = m_CodeHeader->GetEHInfo()->EHClause(EHnumber); |
12186 | |
12187 | pEHClause->TryStartPC = clause->TryOffset; |
12188 | pEHClause->TryEndPC = clause->TryLength; |
12189 | pEHClause->HandlerStartPC = clause->HandlerOffset; |
12190 | pEHClause->HandlerEndPC = clause->HandlerLength; |
12191 | pEHClause->ClassToken = clause->ClassToken; |
12192 | pEHClause->Flags = (CorExceptionFlag)clause->Flags; |
12193 | |
12194 | LOG((LF_EH, LL_INFO1000000, "Setting EH clause #%d for %s::%s\n" , EHnumber, m_pMethodBeingCompiled->m_pszDebugClassName, m_pMethodBeingCompiled->m_pszDebugMethodName)); |
12195 | LOG((LF_EH, LL_INFO1000000, " Flags : 0x%08lx -> 0x%08lx\n" , clause->Flags, pEHClause->Flags)); |
12196 | LOG((LF_EH, LL_INFO1000000, " TryOffset : 0x%08lx -> 0x%08lx (startpc)\n" , clause->TryOffset, pEHClause->TryStartPC)); |
12197 | LOG((LF_EH, LL_INFO1000000, " TryLength : 0x%08lx -> 0x%08lx (endpc)\n" , clause->TryLength, pEHClause->TryEndPC)); |
12198 | LOG((LF_EH, LL_INFO1000000, " HandlerOffset : 0x%08lx -> 0x%08lx\n" , clause->HandlerOffset, pEHClause->HandlerStartPC)); |
12199 | LOG((LF_EH, LL_INFO1000000, " HandlerLength : 0x%08lx -> 0x%08lx\n" , clause->HandlerLength, pEHClause->HandlerEndPC)); |
12200 | LOG((LF_EH, LL_INFO1000000, " ClassToken : 0x%08lx -> 0x%08lx\n" , clause->ClassToken, pEHClause->ClassToken)); |
12201 | LOG((LF_EH, LL_INFO1000000, " FilterOffset : 0x%08lx -> 0x%08lx\n" , clause->FilterOffset, pEHClause->FilterOffset)); |
12202 | |
12203 | if (m_pMethodBeingCompiled->IsDynamicMethod() && |
12204 | ((pEHClause->Flags & COR_ILEXCEPTION_CLAUSE_FILTER) == 0) && |
12205 | (clause->ClassToken != NULL)) |
12206 | { |
12207 | MethodDesc * pMD; FieldDesc * pFD; |
12208 | m_pMethodBeingCompiled->AsDynamicMethodDesc()->GetResolver()->ResolveToken(clause->ClassToken, (TypeHandle *)&pEHClause->TypeHandle, &pMD, &pFD); |
12209 | SetHasCachedTypeHandle(pEHClause); |
12210 | LOG((LF_EH, LL_INFO1000000, " CachedTypeHandle: 0x%08lx -> 0x%08lx\n" , clause->ClassToken, pEHClause->TypeHandle)); |
12211 | } |
12212 | |
12213 | EE_TO_JIT_TRANSITION(); |
12214 | } |
12215 | |
12216 | /*********************************************************************/ |
12217 | // get individual exception handler |
12218 | void CEEJitInfo::getEHinfo( |
12219 | CORINFO_METHOD_HANDLE ftn, /* IN */ |
12220 | unsigned EHnumber, /* IN */ |
12221 | CORINFO_EH_CLAUSE* clause) /* OUT */ |
12222 | { |
12223 | CONTRACTL { |
12224 | SO_TOLERANT; |
12225 | THROWS; |
12226 | GC_TRIGGERS; |
12227 | MODE_PREEMPTIVE; |
12228 | } CONTRACTL_END; |
12229 | |
12230 | JIT_TO_EE_TRANSITION(); |
12231 | |
12232 | if (IsDynamicMethodHandle(ftn)) |
12233 | { |
12234 | GetMethod(ftn)->AsDynamicMethodDesc()->GetResolver()->GetEHInfo(EHnumber, clause); |
12235 | } |
12236 | else |
12237 | { |
12238 | _ASSERTE(ftn == CORINFO_METHOD_HANDLE(m_pMethodBeingCompiled)); // For now only support if the method being jitted |
12239 | getEHinfoHelper(ftn, EHnumber, clause, m_ILHeader); |
12240 | } |
12241 | |
12242 | EE_TO_JIT_TRANSITION(); |
12243 | } |
12244 | #endif // CROSSGEN_COMPILE |
12245 | |
12246 | #if defined(CROSSGEN_COMPILE) |
12247 | EXTERN_C ICorJitCompiler* __stdcall getJit(); |
12248 | #endif // defined(CROSSGEN_COMPILE) |
12249 | |
12250 | #ifdef FEATURE_INTERPRETER |
12251 | static CorJitResult CompileMethodWithEtwWrapper(EEJitManager *jitMgr, |
12252 | CEEInfo *comp, |
12253 | struct CORINFO_METHOD_INFO *info, |
12254 | unsigned flags, |
12255 | BYTE **nativeEntry, |
12256 | ULONG *nativeSizeOfCode) |
12257 | { |
12258 | STATIC_CONTRACT_THROWS; |
12259 | STATIC_CONTRACT_GC_TRIGGERS; |
12260 | STATIC_CONTRACT_MODE_PREEMPTIVE; |
12261 | STATIC_CONTRACT_SO_INTOLERANT; |
12262 | |
12263 | SString namespaceOrClassName, methodName, methodSignature; |
12264 | // Fire an ETW event to mark the beginning of JIT'ing |
12265 | ETW::MethodLog::MethodJitting(reinterpret_cast<MethodDesc*>(info->ftn), &namespaceOrClassName, &methodName, &methodSignature); |
12266 | |
12267 | CorJitResult ret = jitMgr->m_jit->compileMethod(comp, info, flags, nativeEntry, nativeSizeOfCode); |
12268 | |
12269 | // Logically, it would seem that the end-of-JITting ETW even should go here, but it must come after the native code has been |
12270 | // set for the given method desc, which happens in a caller. |
12271 | |
12272 | return ret; |
12273 | } |
12274 | #endif // FEATURE_INTERPRETER |
12275 | |
12276 | // |
12277 | // Helper function because can't have dtors in BEGIN_SO_TOLERANT_CODE. |
12278 | // |
12279 | CorJitResult invokeCompileMethodHelper(EEJitManager *jitMgr, |
12280 | CEEInfo *comp, |
12281 | struct CORINFO_METHOD_INFO *info, |
12282 | CORJIT_FLAGS jitFlags, |
12283 | BYTE **nativeEntry, |
12284 | ULONG *nativeSizeOfCode) |
12285 | { |
12286 | STATIC_CONTRACT_THROWS; |
12287 | STATIC_CONTRACT_GC_TRIGGERS; |
12288 | STATIC_CONTRACT_MODE_PREEMPTIVE; |
12289 | STATIC_CONTRACT_SO_INTOLERANT; |
12290 | |
12291 | CorJitResult ret = CORJIT_SKIPPED; // Note that CORJIT_SKIPPED is an error exit status code |
12292 | |
12293 | |
12294 | comp->setJitFlags(jitFlags); |
12295 | |
12296 | #ifdef FEATURE_STACK_SAMPLING |
12297 | // SO_INTOLERANT due to init affecting global state. |
12298 | static ConfigDWORD s_stackSamplingEnabled; |
12299 | bool samplingEnabled = (s_stackSamplingEnabled.val(CLRConfig::UNSUPPORTED_StackSamplingEnabled) != 0); |
12300 | #endif |
12301 | |
12302 | BEGIN_SO_TOLERANT_CODE(GetThread()); |
12303 | |
12304 | |
12305 | #if defined(ALLOW_SXS_JIT) && !defined(CROSSGEN_COMPILE) |
12306 | if (FAILED(ret) && jitMgr->m_alternateJit |
12307 | #ifdef FEATURE_STACK_SAMPLING |
12308 | && (!samplingEnabled || (jitFlags.IsSet(CORJIT_FLAGS::CORJIT_FLAG_SAMPLING_JIT_BACKGROUND))) |
12309 | #endif |
12310 | ) |
12311 | { |
12312 | ret = jitMgr->m_alternateJit->compileMethod( comp, |
12313 | info, |
12314 | CORJIT_FLAGS::CORJIT_FLAG_CALL_GETJITFLAGS, |
12315 | nativeEntry, |
12316 | nativeSizeOfCode ); |
12317 | |
12318 | #ifdef FEATURE_STACK_SAMPLING |
12319 | if (jitFlags.IsSet(CORJIT_FLAGS::CORJIT_FLAG_SAMPLING_JIT_BACKGROUND)) |
12320 | { |
12321 | // Don't bother with failures if we couldn't collect a trace. |
12322 | ret = CORJIT_OK; |
12323 | } |
12324 | #endif // FEATURE_STACK_SAMPLING |
12325 | |
12326 | // If we failed to jit, then fall back to the primary Jit. |
12327 | if (FAILED(ret)) |
12328 | { |
12329 | // Consider adding this call: |
12330 | // ((CEEJitInfo*)comp)->BackoutJitData(jitMgr); |
12331 | ((CEEJitInfo*)comp)->ResetForJitRetry(); |
12332 | ret = CORJIT_SKIPPED; |
12333 | } |
12334 | } |
12335 | #endif // defined(ALLOW_SXS_JIT) && !defined(CROSSGEN_COMPILE) |
12336 | |
12337 | #ifdef FEATURE_INTERPRETER |
12338 | static ConfigDWORD s_InterpreterFallback; |
12339 | |
12340 | bool isInterpreterStub = false; |
12341 | bool interpreterFallback = (s_InterpreterFallback.val(CLRConfig::INTERNAL_InterpreterFallback) != 0); |
12342 | |
12343 | if (interpreterFallback == false) |
12344 | { |
12345 | // If we're doing an "import_only" compilation, it's for verification, so don't interpret. |
12346 | // (We assume that importation is completely architecture-independent, or at least nearly so.) |
12347 | if (FAILED(ret) && !jitFlags.IsSet(CORJIT_FLAGS::CORJIT_FLAG_IMPORT_ONLY) && !jitFlags.IsSet(CORJIT_FLAGS::CORJIT_FLAG_MAKEFINALCODE)) |
12348 | { |
12349 | if (SUCCEEDED(ret = Interpreter::GenerateInterpreterStub(comp, info, nativeEntry, nativeSizeOfCode))) |
12350 | { |
12351 | isInterpreterStub = true; |
12352 | } |
12353 | } |
12354 | } |
12355 | |
12356 | if (FAILED(ret) && jitMgr->m_jit) |
12357 | { |
12358 | ret = CompileMethodWithEtwWrapper(jitMgr, |
12359 | comp, |
12360 | info, |
12361 | CORJIT_FLAGS::CORJIT_FLAG_CALL_GETJITFLAGS, |
12362 | nativeEntry, |
12363 | nativeSizeOfCode); |
12364 | } |
12365 | |
12366 | if (interpreterFallback == true) |
12367 | { |
12368 | // If we're doing an "import_only" compilation, it's for verification, so don't interpret. |
12369 | // (We assume that importation is completely architecture-independent, or at least nearly so.) |
12370 | if (FAILED(ret) && !jitFlags.IsSet(CORJIT_FLAGS::CORJIT_FLAG_IMPORT_ONLY) && !jitFlags.IsSet(CORJIT_FLAGS::CORJIT_FLAG_MAKEFINALCODE)) |
12371 | { |
12372 | if (SUCCEEDED(ret = Interpreter::GenerateInterpreterStub(comp, info, nativeEntry, nativeSizeOfCode))) |
12373 | { |
12374 | isInterpreterStub = true; |
12375 | } |
12376 | } |
12377 | } |
12378 | #else |
12379 | if (FAILED(ret)) |
12380 | { |
12381 | ret = jitMgr->m_jit->compileMethod( comp, |
12382 | info, |
12383 | CORJIT_FLAGS::CORJIT_FLAG_CALL_GETJITFLAGS, |
12384 | nativeEntry, |
12385 | nativeSizeOfCode); |
12386 | } |
12387 | #endif // FEATURE_INTERPRETER |
12388 | |
12389 | #if !defined(CROSSGEN_COMPILE) |
12390 | // Cleanup any internal data structures allocated |
12391 | // such as IL code after a successfull JIT compile |
12392 | // If the JIT fails we keep the IL around and will |
12393 | // try reJIT the same IL. VSW 525059 |
12394 | // |
12395 | if (SUCCEEDED(ret) && !jitFlags.IsSet(CORJIT_FLAGS::CORJIT_FLAG_IMPORT_ONLY) && !((CEEJitInfo*)comp)->JitAgain()) |
12396 | { |
12397 | ((CEEJitInfo*)comp)->CompressDebugInfo(); |
12398 | |
12399 | #ifdef FEATURE_INTERPRETER |
12400 | // We do this cleanup in the prestub, where we know whether the method |
12401 | // has been interpreted. |
12402 | #else |
12403 | comp->MethodCompileComplete(info->ftn); |
12404 | #endif // FEATURE_INTERPRETER |
12405 | } |
12406 | #endif // !defined(CROSSGEN_COMPILE) |
12407 | |
12408 | |
12409 | #if defined(FEATURE_GDBJIT) |
12410 | bool isJittedEntry = SUCCEEDED(ret) && *nativeEntry != NULL; |
12411 | |
12412 | #ifdef FEATURE_INTERPRETER |
12413 | isJittedEntry &= !isInterpreterStub; |
12414 | #endif // FEATURE_INTERPRETER |
12415 | |
12416 | if (isJittedEntry) |
12417 | { |
12418 | CodeHeader* pCH = ((CodeHeader*)((PCODE)*nativeEntry & ~1)) - 1; |
12419 | pCH->SetCalledMethods((PTR_VOID)comp->GetCalledMethods()); |
12420 | } |
12421 | #endif |
12422 | |
12423 | END_SO_TOLERANT_CODE; |
12424 | |
12425 | return ret; |
12426 | } |
12427 | |
12428 | |
12429 | /*********************************************************************/ |
12430 | CorJitResult invokeCompileMethod(EEJitManager *jitMgr, |
12431 | CEEInfo *comp, |
12432 | struct CORINFO_METHOD_INFO *info, |
12433 | CORJIT_FLAGS jitFlags, |
12434 | BYTE **nativeEntry, |
12435 | ULONG *nativeSizeOfCode) |
12436 | { |
12437 | CONTRACTL { |
12438 | THROWS; |
12439 | GC_TRIGGERS; |
12440 | MODE_COOPERATIVE; |
12441 | } CONTRACTL_END; |
12442 | // |
12443 | // The JIT runs in preemptive mode |
12444 | // |
12445 | |
12446 | GCX_PREEMP(); |
12447 | |
12448 | CorJitResult ret = invokeCompileMethodHelper(jitMgr, comp, info, jitFlags, nativeEntry, nativeSizeOfCode); |
12449 | |
12450 | // |
12451 | // Verify that we are still in preemptive mode when we return |
12452 | // from the JIT |
12453 | // |
12454 | |
12455 | _ASSERTE(GetThread()->PreemptiveGCDisabled() == FALSE); |
12456 | |
12457 | return ret; |
12458 | } |
12459 | |
12460 | CorJitResult CallCompileMethodWithSEHWrapper(EEJitManager *jitMgr, |
12461 | CEEInfo *comp, |
12462 | struct CORINFO_METHOD_INFO *info, |
12463 | CORJIT_FLAGS flags, |
12464 | BYTE **nativeEntry, |
12465 | ULONG *nativeSizeOfCode, |
12466 | MethodDesc *ftn) |
12467 | { |
12468 | // no dynamic contract here because SEH is used, with a finally clause |
12469 | STATIC_CONTRACT_NOTHROW; |
12470 | STATIC_CONTRACT_GC_TRIGGERS; |
12471 | |
12472 | LOG((LF_CORDB, LL_EVERYTHING, "CallCompileMethodWithSEHWrapper called...\n" )); |
12473 | |
12474 | struct Param |
12475 | { |
12476 | EEJitManager *jitMgr; |
12477 | CEEInfo *comp; |
12478 | struct CORINFO_METHOD_INFO *info; |
12479 | CORJIT_FLAGS flags; |
12480 | BYTE **nativeEntry; |
12481 | ULONG *nativeSizeOfCode; |
12482 | MethodDesc *ftn; |
12483 | CorJitResult res; |
12484 | }; Param param; |
12485 | param.jitMgr = jitMgr; |
12486 | param.comp = comp; |
12487 | param.info = info; |
12488 | param.flags = flags; |
12489 | param.nativeEntry = nativeEntry; |
12490 | param.nativeSizeOfCode = nativeSizeOfCode; |
12491 | param.ftn = ftn; |
12492 | param.res = CORJIT_INTERNALERROR; |
12493 | |
12494 | PAL_TRY(Param *, pParam, ¶m) |
12495 | { |
12496 | // |
12497 | // Call out to the JIT-compiler |
12498 | // |
12499 | |
12500 | pParam->res = invokeCompileMethod( pParam->jitMgr, |
12501 | pParam->comp, |
12502 | pParam->info, |
12503 | pParam->flags, |
12504 | pParam->nativeEntry, |
12505 | pParam->nativeSizeOfCode); |
12506 | } |
12507 | PAL_FINALLY |
12508 | { |
12509 | #if defined(DEBUGGING_SUPPORTED) && !defined(CROSSGEN_COMPILE) |
12510 | if (!flags.IsSet(CORJIT_FLAGS::CORJIT_FLAG_IMPORT_ONLY) && |
12511 | !flags.IsSet(CORJIT_FLAGS::CORJIT_FLAG_MCJIT_BACKGROUND) |
12512 | #ifdef FEATURE_STACK_SAMPLING |
12513 | && !flags.IsSet(CORJIT_FLAGS::CORJIT_FLAG_SAMPLING_JIT_BACKGROUND) |
12514 | #endif // FEATURE_STACK_SAMPLING |
12515 | ) |
12516 | { |
12517 | // |
12518 | // Notify the debugger that we have successfully jitted the function |
12519 | // |
12520 | if (g_pDebugInterface) |
12521 | { |
12522 | if (param.res == CORJIT_OK && !((CEEJitInfo*)param.comp)->JitAgain()) |
12523 | { |
12524 | g_pDebugInterface->JITComplete(ftn, (TADDR)*nativeEntry); |
12525 | } |
12526 | } |
12527 | } |
12528 | #endif // DEBUGGING_SUPPORTED && !CROSSGEN_COMPILE |
12529 | } |
12530 | PAL_ENDTRY |
12531 | |
12532 | return param.res; |
12533 | } |
12534 | |
12535 | /*********************************************************************/ |
12536 | // Figures out the compile flags that are used by both JIT and NGen |
12537 | |
12538 | /* static */ CORJIT_FLAGS CEEInfo::GetBaseCompileFlags(MethodDesc * ftn) |
12539 | { |
12540 | CONTRACTL { |
12541 | THROWS; |
12542 | GC_TRIGGERS; |
12543 | } CONTRACTL_END; |
12544 | |
12545 | // |
12546 | // Figure out the code quality flags |
12547 | // |
12548 | |
12549 | CORJIT_FLAGS flags; |
12550 | if (g_pConfig->JitFramed()) |
12551 | flags.Set(CORJIT_FLAGS::CORJIT_FLAG_FRAMED); |
12552 | if (g_pConfig->JitAlignLoops()) |
12553 | flags.Set(CORJIT_FLAGS::CORJIT_FLAG_ALIGN_LOOPS); |
12554 | if (ftn->IsVersionableWithJumpStamp() || g_pConfig->AddRejitNops()) |
12555 | flags.Set(CORJIT_FLAGS::CORJIT_FLAG_PROF_REJIT_NOPS); |
12556 | #ifdef _TARGET_X86_ |
12557 | if (g_pConfig->PInvokeRestoreEsp(ftn->GetModule()->IsPreV4Assembly())) |
12558 | flags.Set(CORJIT_FLAGS::CORJIT_FLAG_PINVOKE_RESTORE_ESP); |
12559 | #endif // _TARGET_X86_ |
12560 | |
12561 | //See if we should instruct the JIT to emit calls to JIT_PollGC for thread suspension. If we have a |
12562 | //non-default value in the EE Config, then use that. Otherwise select the platform specific default. |
12563 | #ifdef FEATURE_ENABLE_GCPOLL |
12564 | EEConfig::GCPollType pollType = g_pConfig->GetGCPollType(); |
12565 | if (EEConfig::GCPOLL_TYPE_POLL == pollType) |
12566 | flags.Set(CORJIT_FLAGS::CORJIT_FLAG_GCPOLL_CALLS); |
12567 | else if (EEConfig::GCPOLL_TYPE_INLINE == pollType) |
12568 | flags.Set(CORJIT_FLAGS::CORJIT_FLAG_GCPOLL_INLINE); |
12569 | #endif //FEATURE_ENABLE_GCPOLL |
12570 | |
12571 | // Set flags based on method's ImplFlags. |
12572 | if (!ftn->IsNoMetadata()) |
12573 | { |
12574 | DWORD dwImplFlags = 0; |
12575 | IfFailThrow(ftn->GetMDImport()->GetMethodImplProps(ftn->GetMemberDef(), NULL, &dwImplFlags)); |
12576 | |
12577 | if (IsMiNoOptimization(dwImplFlags)) |
12578 | { |
12579 | flags.Set(CORJIT_FLAGS::CORJIT_FLAG_MIN_OPT); |
12580 | } |
12581 | |
12582 | // Always emit frames for methods marked no-inline (see #define ETW_EBP_FRAMED in the JIT) |
12583 | if (IsMiNoInlining(dwImplFlags)) |
12584 | { |
12585 | flags.Set(CORJIT_FLAGS::CORJIT_FLAG_FRAMED); |
12586 | } |
12587 | } |
12588 | |
12589 | return flags; |
12590 | } |
12591 | |
12592 | /*********************************************************************/ |
12593 | // Figures out (some of) the flags to use to compile the method |
12594 | // Returns the new set to use |
12595 | |
12596 | CORJIT_FLAGS GetDebuggerCompileFlags(Module* pModule, CORJIT_FLAGS flags) |
12597 | { |
12598 | STANDARD_VM_CONTRACT; |
12599 | |
12600 | //Right now if we don't have a debug interface on CoreCLR, we can't generate debug info. So, in those |
12601 | //cases don't attempt it. |
12602 | if (!g_pDebugInterface) |
12603 | return flags; |
12604 | |
12605 | #ifdef DEBUGGING_SUPPORTED |
12606 | |
12607 | #ifdef _DEBUG |
12608 | if (g_pConfig->GenDebuggableCode()) |
12609 | flags.Set(CORJIT_FLAGS::CORJIT_FLAG_DEBUG_CODE); |
12610 | #endif // _DEBUG |
12611 | |
12612 | #ifdef EnC_SUPPORTED |
12613 | if (pModule->IsEditAndContinueEnabled()) |
12614 | { |
12615 | flags.Set(CORJIT_FLAGS::CORJIT_FLAG_DEBUG_EnC); |
12616 | } |
12617 | #endif // EnC_SUPPORTED |
12618 | |
12619 | // Debug info is always tracked |
12620 | flags.Set(CORJIT_FLAGS::CORJIT_FLAG_DEBUG_INFO); |
12621 | #endif // DEBUGGING_SUPPORTED |
12622 | |
12623 | if (CORDisableJITOptimizations(pModule->GetDebuggerInfoBits())) |
12624 | { |
12625 | flags.Set(CORJIT_FLAGS::CORJIT_FLAG_DEBUG_CODE); |
12626 | } |
12627 | |
12628 | if (flags.IsSet(CORJIT_FLAGS::CORJIT_FLAG_IMPORT_ONLY)) |
12629 | { |
12630 | // If we are only verifying the method, dont need any debug info and this |
12631 | // prevents getVars()/getBoundaries() from being called unnecessarily. |
12632 | flags.Clear(CORJIT_FLAGS::CORJIT_FLAG_DEBUG_INFO); |
12633 | flags.Clear(CORJIT_FLAGS::CORJIT_FLAG_DEBUG_CODE); |
12634 | } |
12635 | |
12636 | return flags; |
12637 | } |
12638 | |
12639 | CORJIT_FLAGS GetCompileFlags(MethodDesc * ftn, CORJIT_FLAGS flags, CORINFO_METHOD_INFO * methodInfo) |
12640 | { |
12641 | STANDARD_VM_CONTRACT; |
12642 | |
12643 | _ASSERTE(methodInfo->regionKind == CORINFO_REGION_JIT); |
12644 | |
12645 | // |
12646 | // Get the compile flags that are shared between JIT and NGen |
12647 | // |
12648 | flags.Add(CEEInfo::GetBaseCompileFlags(ftn)); |
12649 | |
12650 | // |
12651 | // Get CPU specific flags |
12652 | // |
12653 | if (!flags.IsSet(CORJIT_FLAGS::CORJIT_FLAG_IMPORT_ONLY)) |
12654 | { |
12655 | flags.Add(ExecutionManager::GetEEJitManager()->GetCPUCompileFlags()); |
12656 | } |
12657 | |
12658 | // |
12659 | // Find the debugger and profiler related flags |
12660 | // |
12661 | |
12662 | #ifdef DEBUGGING_SUPPORTED |
12663 | flags.Add(GetDebuggerCompileFlags(ftn->GetModule(), flags)); |
12664 | #endif |
12665 | |
12666 | #ifdef PROFILING_SUPPORTED |
12667 | if (CORProfilerTrackEnterLeave() && !ftn->IsNoMetadata()) |
12668 | flags.Set(CORJIT_FLAGS::CORJIT_FLAG_PROF_ENTERLEAVE); |
12669 | |
12670 | if (CORProfilerTrackTransitions()) |
12671 | flags.Set(CORJIT_FLAGS::CORJIT_FLAG_PROF_NO_PINVOKE_INLINE); |
12672 | #endif // PROFILING_SUPPORTED |
12673 | |
12674 | // Set optimization flags |
12675 | if (!flags.IsSet(CORJIT_FLAGS::CORJIT_FLAG_MIN_OPT)) |
12676 | { |
12677 | unsigned optType = g_pConfig->GenOptimizeType(); |
12678 | _ASSERTE(optType <= OPT_RANDOM); |
12679 | |
12680 | if (optType == OPT_RANDOM) |
12681 | optType = methodInfo->ILCodeSize % OPT_RANDOM; |
12682 | |
12683 | if (g_pConfig->JitMinOpts()) |
12684 | flags.Set(CORJIT_FLAGS::CORJIT_FLAG_MIN_OPT); |
12685 | |
12686 | if (optType == OPT_SIZE) |
12687 | { |
12688 | flags.Set(CORJIT_FLAGS::CORJIT_FLAG_SIZE_OPT); |
12689 | } |
12690 | else if (optType == OPT_SPEED) |
12691 | { |
12692 | flags.Set(CORJIT_FLAGS::CORJIT_FLAG_SPEED_OPT); |
12693 | } |
12694 | } |
12695 | |
12696 | flags.Set(CORJIT_FLAGS::CORJIT_FLAG_SKIP_VERIFICATION); |
12697 | |
12698 | if (ftn->IsILStub()) |
12699 | { |
12700 | // no debug info available for IL stubs |
12701 | flags.Clear(CORJIT_FLAGS::CORJIT_FLAG_DEBUG_INFO); |
12702 | } |
12703 | |
12704 | return flags; |
12705 | } |
12706 | |
12707 | // ******************************************************************** |
12708 | |
12709 | // Throw the right type of exception for the given JIT result |
12710 | |
12711 | void ThrowExceptionForJit(HRESULT res) |
12712 | { |
12713 | CONTRACTL |
12714 | { |
12715 | THROWS; |
12716 | GC_NOTRIGGER; |
12717 | SO_INTOLERANT; |
12718 | MODE_ANY; |
12719 | } |
12720 | CONTRACTL_END; |
12721 | switch (res) |
12722 | { |
12723 | case CORJIT_OUTOFMEM: |
12724 | COMPlusThrowOM(); |
12725 | break; |
12726 | |
12727 | case CORJIT_INTERNALERROR: |
12728 | COMPlusThrow(kInvalidProgramException, (UINT) IDS_EE_JIT_COMPILER_ERROR); |
12729 | break; |
12730 | |
12731 | case CORJIT_BADCODE: |
12732 | default: |
12733 | COMPlusThrow(kInvalidProgramException); |
12734 | break; |
12735 | } |
12736 | } |
12737 | |
12738 | // ******************************************************************** |
12739 | #ifdef _DEBUG |
12740 | LONG g_JitCount = 0; |
12741 | #endif |
12742 | |
12743 | //#define PERF_TRACK_METHOD_JITTIMES |
12744 | #ifdef _TARGET_AMD64_ |
12745 | BOOL g_fAllowRel32 = TRUE; |
12746 | #endif |
12747 | |
12748 | |
12749 | // ******************************************************************** |
12750 | // README!! |
12751 | // ******************************************************************** |
12752 | |
12753 | // The reason that this is named UnsafeJitFunction is that this helper |
12754 | // method is not thread safe! When multiple threads get in here for |
12755 | // the same pMD, ALL of them MUST return the SAME value. |
12756 | // To insure that this happens you must call MakeJitWorker. |
12757 | // It creates a DeadlockAware list of methods being jitted and prevents us |
12758 | // from trying to jit the same method more that once. |
12759 | // |
12760 | // Calls to this method that occur to check if inlining can occur on x86, |
12761 | // are OK since they discard the return value of this method. |
12762 | |
12763 | PCODE UnsafeJitFunction(MethodDesc* ftn, COR_ILMETHOD_DECODER* ILHeader, CORJIT_FLAGS flags, |
12764 | ULONG * pSizeOfCode) |
12765 | { |
12766 | STANDARD_VM_CONTRACT; |
12767 | |
12768 | PCODE ret = NULL; |
12769 | |
12770 | COOPERATIVE_TRANSITION_BEGIN(); |
12771 | |
12772 | #ifdef FEATURE_PREJIT |
12773 | |
12774 | if (g_pConfig->RequireZaps() == EEConfig::REQUIRE_ZAPS_ALL && |
12775 | ftn->GetModule()->GetDomainFile()->IsZapRequired() && |
12776 | PartialNGenStressPercentage() == 0 && |
12777 | #ifdef FEATURE_STACK_SAMPLING |
12778 | !flags.IsSet(CORJIT_FLAGS::CORJIT_FLAG_SAMPLING_JIT_BACKGROUND) && |
12779 | #endif |
12780 | !flags.IsSet(CORJIT_FLAGS::CORJIT_FLAG_IMPORT_ONLY)) |
12781 | { |
12782 | StackSString ss(SString::Ascii, "ZapRequire: JIT compiler invoked for " ); |
12783 | TypeString::AppendMethodInternal(ss, ftn); |
12784 | |
12785 | #ifdef _DEBUG |
12786 | // Assert as some test may not check their error codes well. So throwing an |
12787 | // exception may not cause a test failure (as it should). |
12788 | StackScratchBuffer scratch; |
12789 | DbgAssertDialog(__FILE__, __LINE__, (char*)ss.GetUTF8(scratch)); |
12790 | #endif // _DEBUG |
12791 | |
12792 | COMPlusThrowNonLocalized(kFileNotFoundException, ss.GetUnicode()); |
12793 | } |
12794 | |
12795 | #endif // FEATURE_PREJIT |
12796 | |
12797 | #ifndef CROSSGEN_COMPILE |
12798 | EEJitManager *jitMgr = ExecutionManager::GetEEJitManager(); |
12799 | if (!jitMgr->LoadJIT()) |
12800 | { |
12801 | #ifdef ALLOW_SXS_JIT |
12802 | if (!jitMgr->IsMainJitLoaded()) |
12803 | { |
12804 | // Don't want to throw InvalidProgram from here. |
12805 | EEPOLICY_HANDLE_FATAL_ERROR_WITH_MESSAGE(COR_E_EXECUTIONENGINE, W("Failed to load JIT compiler" )); |
12806 | } |
12807 | if (!jitMgr->IsAltJitLoaded()) |
12808 | { |
12809 | // Don't want to throw InvalidProgram from here. |
12810 | EEPOLICY_HANDLE_FATAL_ERROR_WITH_MESSAGE(COR_E_EXECUTIONENGINE, W("Failed to load alternative JIT compiler" )); |
12811 | } |
12812 | #else // ALLOW_SXS_JIT |
12813 | // Don't want to throw InvalidProgram from here. |
12814 | EEPOLICY_HANDLE_FATAL_ERROR_WITH_MESSAGE(COR_E_EXECUTIONENGINE, W("Failed to load JIT compiler" )); |
12815 | #endif // ALLOW_SXS_JIT |
12816 | } |
12817 | #endif // CROSSGEN_COMPILE |
12818 | |
12819 | #ifdef _DEBUG |
12820 | // This is here so we can see the name and class easily in the debugger |
12821 | |
12822 | LPCUTF8 cls = ftn->GetMethodTable()->GetDebugClassName(); |
12823 | LPCUTF8 name = ftn->GetName(); |
12824 | |
12825 | if (ftn->IsNoMetadata()) |
12826 | { |
12827 | if (ftn->IsILStub()) |
12828 | { |
12829 | LOG((LF_JIT, LL_INFO10000, "{ Jitting IL Stub }\n" )); |
12830 | } |
12831 | else |
12832 | { |
12833 | LOG((LF_JIT, LL_INFO10000, "{ Jitting dynamic method }\n" )); |
12834 | } |
12835 | } |
12836 | else |
12837 | { |
12838 | SString methodString; |
12839 | if (LoggingOn(LF_JIT, LL_INFO10000)) |
12840 | TypeString::AppendMethodDebug(methodString, ftn); |
12841 | |
12842 | LOG((LF_JIT, LL_INFO10000, "{ Jitting method (%p) %S %s\n" , ftn, methodString.GetUnicode(), ftn->m_pszDebugMethodSignature)); |
12843 | } |
12844 | |
12845 | #if 0 |
12846 | if (!SString::_stricmp(cls,"ENC" ) && |
12847 | (!SString::_stricmp(name,"G" ))) |
12848 | { |
12849 | static count = 0; |
12850 | count++; |
12851 | if (count > 0) |
12852 | DebugBreak(); |
12853 | } |
12854 | #endif // 0 |
12855 | #endif // _DEBUG |
12856 | |
12857 | CORINFO_METHOD_HANDLE ftnHnd = (CORINFO_METHOD_HANDLE)ftn; |
12858 | CORINFO_METHOD_INFO methodInfo; |
12859 | |
12860 | getMethodInfoHelper(ftn, ftnHnd, ILHeader, &methodInfo); |
12861 | |
12862 | // If it's generic then we can only enter through an instantiated md (unless we're just verifying it) |
12863 | _ASSERTE(flags.IsSet(CORJIT_FLAGS::CORJIT_FLAG_IMPORT_ONLY) || !ftn->IsGenericMethodDefinition()); |
12864 | |
12865 | // If it's an instance method then it must not be entered from a generic class |
12866 | _ASSERTE(flags.IsSet(CORJIT_FLAGS::CORJIT_FLAG_IMPORT_ONLY) || ftn->IsStatic() || |
12867 | ftn->GetNumGenericClassArgs() == 0 || ftn->HasClassInstantiation()); |
12868 | |
12869 | // method attributes and signature are consistant |
12870 | _ASSERTE(!!ftn->IsStatic() == ((methodInfo.args.callConv & CORINFO_CALLCONV_HASTHIS) == 0)); |
12871 | |
12872 | flags = GetCompileFlags(ftn, flags, &methodInfo); |
12873 | |
12874 | #ifdef _DEBUG |
12875 | if (!flags.IsSet(CORJIT_FLAGS::CORJIT_FLAG_SKIP_VERIFICATION)) |
12876 | { |
12877 | SString methodString; |
12878 | if (LoggingOn(LF_VERIFIER, LL_INFO100)) |
12879 | TypeString::AppendMethodDebug(methodString, ftn); |
12880 | |
12881 | LOG((LF_VERIFIER, LL_INFO100, "{ Will verify method (%p) %S %s\n" , ftn, methodString.GetUnicode(), ftn->m_pszDebugMethodSignature)); |
12882 | } |
12883 | #endif //_DEBUG |
12884 | |
12885 | #if defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_) |
12886 | BOOL fForceJumpStubOverflow = FALSE; |
12887 | |
12888 | #ifdef _DEBUG |
12889 | // Always exercise the overflow codepath with force relocs |
12890 | if (PEDecoder::GetForceRelocs()) |
12891 | fForceJumpStubOverflow = TRUE; |
12892 | #endif |
12893 | |
12894 | #if defined(_TARGET_AMD64_) |
12895 | BOOL fAllowRel32 = (g_fAllowRel32 | fForceJumpStubOverflow); |
12896 | #endif |
12897 | |
12898 | size_t reserveForJumpStubs = 0; |
12899 | |
12900 | #endif // defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_) |
12901 | |
12902 | for (;;) |
12903 | { |
12904 | #ifndef CROSSGEN_COMPILE |
12905 | CEEJitInfo jitInfo(ftn, ILHeader, jitMgr, flags.IsSet(CORJIT_FLAGS::CORJIT_FLAG_IMPORT_ONLY), |
12906 | !flags.IsSet(CORJIT_FLAGS::CORJIT_FLAG_NO_INLINING)); |
12907 | #else |
12908 | // This path should be only ever used for verification in crossgen and so we should not need EEJitManager |
12909 | _ASSERTE(flags.IsSet(CORJIT_FLAGS::CORJIT_FLAG_IMPORT_ONLY)); |
12910 | CEEInfo jitInfo(ftn, true); |
12911 | EEJitManager *jitMgr = NULL; |
12912 | #endif |
12913 | |
12914 | #if (defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_)) && !defined(CROSSGEN_COMPILE) |
12915 | #ifdef _TARGET_AMD64_ |
12916 | if (fForceJumpStubOverflow) |
12917 | jitInfo.SetJumpStubOverflow(fAllowRel32); |
12918 | jitInfo.SetAllowRel32(fAllowRel32); |
12919 | #else |
12920 | if (fForceJumpStubOverflow) |
12921 | jitInfo.SetJumpStubOverflow(fForceJumpStubOverflow); |
12922 | #endif |
12923 | jitInfo.SetReserveForJumpStubs(reserveForJumpStubs); |
12924 | #endif |
12925 | |
12926 | MethodDesc * pMethodForSecurity = jitInfo.GetMethodForSecurity(ftnHnd); |
12927 | |
12928 | //Since the check could trigger a demand, we have to do this every time. |
12929 | //This is actually an overly complicated way to make sure that a method can access all its arguments |
12930 | //and its return type. |
12931 | AccessCheckOptions::AccessCheckType accessCheckType = AccessCheckOptions::kNormalAccessibilityChecks; |
12932 | TypeHandle ownerTypeForSecurity = TypeHandle(pMethodForSecurity->GetMethodTable()); |
12933 | DynamicResolver *pAccessContext = NULL; |
12934 | BOOL doAccessCheck = TRUE; |
12935 | if (pMethodForSecurity->IsDynamicMethod()) |
12936 | { |
12937 | doAccessCheck = ModifyCheckForDynamicMethod(pMethodForSecurity->AsDynamicMethodDesc()->GetResolver(), |
12938 | &ownerTypeForSecurity, |
12939 | &accessCheckType, &pAccessContext); |
12940 | } |
12941 | if (doAccessCheck) |
12942 | { |
12943 | AccessCheckOptions accessCheckOptions(accessCheckType, |
12944 | pAccessContext, |
12945 | TRUE /*Throw on error*/, |
12946 | pMethodForSecurity); |
12947 | |
12948 | StaticAccessCheckContext accessContext(pMethodForSecurity, ownerTypeForSecurity.GetMethodTable()); |
12949 | |
12950 | // We now do an access check from pMethodForSecurity to pMethodForSecurity, its sole purpose is to |
12951 | // verify that pMethodForSecurity/ownerTypeForSecurity has access to all its parameters. |
12952 | |
12953 | // ownerTypeForSecurity.GetMethodTable() can be null if the pMethodForSecurity is a DynamicMethod |
12954 | // associated with a TypeDesc (Array, Ptr, Ref, or FnPtr). That doesn't make any sense, but we will |
12955 | // just do an access check from a NULL context which means only public types are accessible. |
12956 | if (!ClassLoader::CanAccess(&accessContext, |
12957 | ownerTypeForSecurity.GetMethodTable(), |
12958 | ownerTypeForSecurity.GetAssembly(), |
12959 | pMethodForSecurity->GetAttrs(), |
12960 | pMethodForSecurity, |
12961 | NULL, |
12962 | accessCheckOptions)) |
12963 | { |
12964 | EX_THROW(EEMethodException, (pMethodForSecurity)); |
12965 | } |
12966 | } |
12967 | |
12968 | CorJitResult res; |
12969 | PBYTE nativeEntry; |
12970 | ULONG sizeOfCode; |
12971 | |
12972 | { |
12973 | GCX_COOP(); |
12974 | |
12975 | /* There is a double indirection to call compileMethod - can we |
12976 | improve this with the new structure? */ |
12977 | |
12978 | #ifdef PERF_TRACK_METHOD_JITTIMES |
12979 | //Because we're not calling QPC enough. I'm not going to track times if we're just importing. |
12980 | LARGE_INTEGER methodJitTimeStart = {0}; |
12981 | if (!flags.IsSet(CORJIT_FLAGS::CORJIT_FLAG_IMPORT_ONLY)) |
12982 | QueryPerformanceCounter (&methodJitTimeStart); |
12983 | |
12984 | #endif |
12985 | #if defined(ENABLE_PERF_COUNTERS) |
12986 | START_JIT_PERF(); |
12987 | #endif |
12988 | |
12989 | #if defined(ENABLE_PERF_COUNTERS) |
12990 | LARGE_INTEGER CycleStart; |
12991 | QueryPerformanceCounter (&CycleStart); |
12992 | #endif // defined(ENABLE_PERF_COUNTERS) |
12993 | |
12994 | // Note on debuggerTrackInfo arg: if we're only importing (ie, verifying/ |
12995 | // checking to make sure we could JIT, but not actually generating code ( |
12996 | // eg, for inlining), then DON'T TELL THE DEBUGGER about this. |
12997 | res = CallCompileMethodWithSEHWrapper(jitMgr, |
12998 | &jitInfo, |
12999 | &methodInfo, |
13000 | flags, |
13001 | &nativeEntry, |
13002 | &sizeOfCode, |
13003 | (MethodDesc*)ftn); |
13004 | LOG((LF_CORDB, LL_EVERYTHING, "Got through CallCompile MethodWithSEHWrapper\n" )); |
13005 | |
13006 | #if FEATURE_PERFMAP |
13007 | // Save the code size so that it can be reported to the perfmap. |
13008 | if (pSizeOfCode != NULL) |
13009 | { |
13010 | *pSizeOfCode = sizeOfCode; |
13011 | } |
13012 | #endif |
13013 | |
13014 | #if defined(ENABLE_PERF_COUNTERS) |
13015 | LARGE_INTEGER CycleStop; |
13016 | QueryPerformanceCounter(&CycleStop); |
13017 | GetPerfCounters().m_Jit.timeInJitBase = GetPerfCounters().m_Jit.timeInJit; |
13018 | GetPerfCounters().m_Jit.timeInJit += static_cast<DWORD>(CycleStop.QuadPart - CycleStart.QuadPart); |
13019 | GetPerfCounters().m_Jit.cMethodsJitted++; |
13020 | GetPerfCounters().m_Jit.cbILJitted+=methodInfo.ILCodeSize; |
13021 | |
13022 | #endif // defined(ENABLE_PERF_COUNTERS) |
13023 | |
13024 | #if defined(ENABLE_PERF_COUNTERS) |
13025 | STOP_JIT_PERF(); |
13026 | #endif |
13027 | |
13028 | #ifdef PERF_TRACK_METHOD_JITTIMES |
13029 | //store the time in the string buffer. Module name and token are unique enough. Also, do not |
13030 | //capture importing time, just actual compilation time. |
13031 | if (!flags.IsSet(CORJIT_FLAGS::CORJIT_FLAG_IMPORT_ONLY)) |
13032 | { |
13033 | LARGE_INTEGER methodJitTimeStop; |
13034 | QueryPerformanceCounter(&methodJitTimeStop); |
13035 | SString codeBase; |
13036 | ftn->GetModule()->GetDomainFile()->GetFile()->GetCodeBaseOrName(codeBase); |
13037 | codeBase.AppendPrintf(W(",0x%x,%d,%d\n" ), |
13038 | //(const WCHAR *)codeBase, //module name |
13039 | ftn->GetMemberDef(), //method token |
13040 | (unsigned)(methodJitTimeStop.QuadPart - methodJitTimeStart.QuadPart), //cycle count |
13041 | methodInfo.ILCodeSize //il size |
13042 | ); |
13043 | WszOutputDebugString((const WCHAR*)codeBase); |
13044 | } |
13045 | #endif // PERF_TRACK_METHOD_JITTIMES |
13046 | |
13047 | } |
13048 | |
13049 | LOG((LF_JIT, LL_INFO10000, "Done Jitting method %s::%s %s }\n" ,cls,name, ftn->m_pszDebugMethodSignature)); |
13050 | |
13051 | if (!SUCCEEDED(res)) |
13052 | { |
13053 | COUNTER_ONLY(GetPerfCounters().m_Jit.cJitFailures++); |
13054 | |
13055 | #ifndef CROSSGEN_COMPILE |
13056 | jitInfo.BackoutJitData(jitMgr); |
13057 | #endif |
13058 | |
13059 | ThrowExceptionForJit(res); |
13060 | } |
13061 | |
13062 | if (flags.IsSet(CORJIT_FLAGS::CORJIT_FLAG_IMPORT_ONLY)) |
13063 | { |
13064 | // We are done |
13065 | break; |
13066 | } |
13067 | |
13068 | if (!nativeEntry) |
13069 | COMPlusThrow(kInvalidProgramException); |
13070 | |
13071 | #if (defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_)) && !defined(CROSSGEN_COMPILE) |
13072 | if (jitInfo.IsJumpStubOverflow()) |
13073 | { |
13074 | // Backout and try again with fAllowRel32 == FALSE. |
13075 | jitInfo.BackoutJitData(jitMgr); |
13076 | |
13077 | #ifdef _TARGET_AMD64_ |
13078 | // Disallow rel32 relocs in future. |
13079 | g_fAllowRel32 = FALSE; |
13080 | |
13081 | fAllowRel32 = FALSE; |
13082 | #endif // _TARGET_AMD64_ |
13083 | #ifdef _TARGET_ARM64_ |
13084 | fForceJumpStubOverflow = FALSE; |
13085 | #endif // _TARGET_ARM64_ |
13086 | |
13087 | reserveForJumpStubs = jitInfo.GetReserveForJumpStubs(); |
13088 | continue; |
13089 | } |
13090 | #endif // (_TARGET_AMD64_ || _TARGET_ARM64_) && !CROSSGEN_COMPILE |
13091 | |
13092 | LOG((LF_JIT, LL_INFO10000, |
13093 | "Jitted Entry at" FMT_ADDR "method %s::%s %s\n" , DBG_ADDR(nativeEntry), |
13094 | ftn->m_pszDebugClassName, ftn->m_pszDebugMethodName, ftn->m_pszDebugMethodSignature)); |
13095 | |
13096 | #if defined(FEATURE_CORESYSTEM) |
13097 | |
13098 | #ifdef _DEBUG |
13099 | LPCUTF8 pszDebugClassName = ftn->m_pszDebugClassName; |
13100 | LPCUTF8 pszDebugMethodName = ftn->m_pszDebugMethodName; |
13101 | LPCUTF8 pszDebugMethodSignature = ftn->m_pszDebugMethodSignature; |
13102 | #else |
13103 | LPCUTF8 pszNamespace; |
13104 | LPCUTF8 pszDebugClassName = ftn->GetMethodTable()->GetFullyQualifiedNameInfo(&pszNamespace); |
13105 | LPCUTF8 pszDebugMethodName = ftn->GetName(); |
13106 | LPCUTF8 pszDebugMethodSignature = "" ; |
13107 | #endif |
13108 | |
13109 | //DbgPrintf("Jitted Entry at" FMT_ADDR "method %s::%s %s size %08x\n", DBG_ADDR(nativeEntry), |
13110 | // pszDebugClassName, pszDebugMethodName, pszDebugMethodSignature, sizeOfCode); |
13111 | #endif |
13112 | |
13113 | ClrFlushInstructionCache(nativeEntry, sizeOfCode); |
13114 | ret = (PCODE)nativeEntry; |
13115 | |
13116 | #ifdef _TARGET_ARM_ |
13117 | ret |= THUMB_CODE; |
13118 | #endif |
13119 | |
13120 | // We are done |
13121 | break; |
13122 | } |
13123 | |
13124 | #ifdef _DEBUG |
13125 | FastInterlockIncrement(&g_JitCount); |
13126 | static BOOL fHeartbeat = -1; |
13127 | |
13128 | if (fHeartbeat == -1) |
13129 | fHeartbeat = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_JitHeartbeat); |
13130 | |
13131 | if (fHeartbeat) |
13132 | printf("." ); |
13133 | #endif // _DEBUG |
13134 | |
13135 | COOPERATIVE_TRANSITION_END(); |
13136 | return ret; |
13137 | } |
13138 | |
13139 | extern "C" unsigned __stdcall PartialNGenStressPercentage() |
13140 | { |
13141 | LIMITED_METHOD_CONTRACT; |
13142 | #ifndef _DEBUG |
13143 | return 0; |
13144 | #else // _DEBUG |
13145 | static ConfigDWORD partialNGenStress; |
13146 | DWORD partialNGenStressVal = partialNGenStress.val(CLRConfig::INTERNAL_partialNGenStress); |
13147 | _ASSERTE(partialNGenStressVal <= 100); |
13148 | return partialNGenStressVal; |
13149 | #endif // _DEBUG |
13150 | } |
13151 | |
13152 | #ifdef FEATURE_PREJIT |
13153 | /*********************************************************************/ |
13154 | |
13155 | // |
13156 | // Table loading functions |
13157 | // |
13158 | void Module::LoadHelperTable() |
13159 | { |
13160 | STANDARD_VM_CONTRACT; |
13161 | |
13162 | #ifndef CROSSGEN_COMPILE |
13163 | COUNT_T tableSize; |
13164 | BYTE * table = (BYTE *) GetNativeImage()->GetNativeHelperTable(&tableSize); |
13165 | |
13166 | if (tableSize == 0) |
13167 | return; |
13168 | |
13169 | EnsureWritableExecutablePages(table, tableSize); |
13170 | |
13171 | BYTE * curEntry = table; |
13172 | BYTE * tableEnd = table + tableSize; |
13173 | |
13174 | #ifdef FEATURE_PERFMAP |
13175 | PerfMap::LogStubs(__FUNCTION__, GetSimpleName(), (PCODE)table, tableSize); |
13176 | #endif |
13177 | |
13178 | #ifdef LOGGING |
13179 | int iEntryNumber = 0; |
13180 | #endif // LOGGING |
13181 | |
13182 | // |
13183 | // Fill in helpers |
13184 | // |
13185 | |
13186 | while (curEntry < tableEnd) |
13187 | { |
13188 | DWORD dwHelper = *(DWORD *)curEntry; |
13189 | |
13190 | int iHelper = (USHORT)dwHelper; |
13191 | _ASSERTE(iHelper < CORINFO_HELP_COUNT); |
13192 | |
13193 | LOG((LF_JIT, LL_INFO1000000, "JIT helper %3d (%-40s: table @ %p, size 0x%x, entry %3d @ %p, pfnHelper %p)\n" , |
13194 | iHelper, hlpFuncTable[iHelper].name, table, tableSize, iEntryNumber, curEntry, hlpFuncTable[iHelper].pfnHelper)); |
13195 | |
13196 | #if defined(ENABLE_FAST_GCPOLL_HELPER) |
13197 | // The fast GC poll helper works by calling indirect through a pointer that points to either |
13198 | // JIT_PollGC or JIT_PollGC_Nop, based on whether we need to poll or not. The JIT_PollGC_Nop |
13199 | // version is just a "ret". The pointer is stored in hlpDynamicFuncTable[DYNAMIC_CORINFO_HELP_POLL_GC]. |
13200 | // See EnableJitGCPoll() and DisableJitGCPoll(). |
13201 | // In NGEN images, we generate a direct call to the helper table. Here, we replace that with |
13202 | // an indirect jump through the pointer in hlpDynamicFuncTable[DYNAMIC_CORINFO_HELP_POLL_GC]. |
13203 | if (iHelper == CORINFO_HELP_POLL_GC) |
13204 | { |
13205 | LOG((LF_JIT, LL_INFO1000000, "JIT helper CORINFO_HELP_POLL_GC (%d); emitting indirect jump to 0x%x\n" , |
13206 | CORINFO_HELP_POLL_GC, &hlpDynamicFuncTable[DYNAMIC_CORINFO_HELP_POLL_GC].pfnHelper)); |
13207 | |
13208 | emitJumpInd(curEntry, &hlpDynamicFuncTable[DYNAMIC_CORINFO_HELP_POLL_GC].pfnHelper); |
13209 | curEntry = curEntry + HELPER_TABLE_ENTRY_LEN; |
13210 | } |
13211 | else |
13212 | #endif // ENABLE_FAST_GCPOLL_HELPER |
13213 | { |
13214 | PCODE pfnHelper = CEEJitInfo::getHelperFtnStatic((CorInfoHelpFunc)iHelper); |
13215 | |
13216 | if (dwHelper & CORCOMPILE_HELPER_PTR) |
13217 | { |
13218 | // |
13219 | // Indirection cell |
13220 | // |
13221 | |
13222 | *(TADDR *)curEntry = pfnHelper; |
13223 | |
13224 | curEntry = curEntry + sizeof(TADDR); |
13225 | } |
13226 | else |
13227 | { |
13228 | // |
13229 | // Jump thunk |
13230 | // |
13231 | |
13232 | #if defined(_TARGET_AMD64_) |
13233 | *curEntry = X86_INSTR_JMP_REL32; |
13234 | *(INT32 *)(curEntry + 1) = rel32UsingJumpStub((INT32 *)(curEntry + 1), pfnHelper, NULL, GetLoaderAllocator()); |
13235 | #else // all other platforms |
13236 | emitJump(curEntry, (LPVOID)pfnHelper); |
13237 | _ASSERTE(HELPER_TABLE_ENTRY_LEN >= JUMP_ALLOCATE_SIZE); |
13238 | #endif |
13239 | |
13240 | curEntry = curEntry + HELPER_TABLE_ENTRY_LEN; |
13241 | } |
13242 | } |
13243 | #ifdef LOGGING |
13244 | // Note that some table entries are sizeof(TADDR) in length, and some are HELPER_TABLE_ENTRY_LEN in length |
13245 | ++iEntryNumber; |
13246 | #endif // LOGGING |
13247 | } |
13248 | |
13249 | ClrFlushInstructionCache(table, tableSize); |
13250 | #endif // CROSSGEN_COMPILE |
13251 | } |
13252 | |
13253 | #ifdef FEATURE_READYTORUN |
13254 | CorInfoHelpFunc MapReadyToRunHelper(ReadyToRunHelper helperNum) |
13255 | { |
13256 | LIMITED_METHOD_CONTRACT; |
13257 | |
13258 | switch (helperNum) |
13259 | { |
13260 | #define HELPER(readyToRunHelper, corInfoHelpFunc, flags) \ |
13261 | case readyToRunHelper: return corInfoHelpFunc; |
13262 | #include "readytorunhelpers.h" |
13263 | |
13264 | case READYTORUN_HELPER_GetString: return CORINFO_HELP_STRCNS; |
13265 | |
13266 | default: return CORINFO_HELP_UNDEF; |
13267 | } |
13268 | } |
13269 | |
13270 | void ComputeGCRefMap(MethodTable * pMT, BYTE * pGCRefMap, size_t cbGCRefMap) |
13271 | { |
13272 | STANDARD_VM_CONTRACT; |
13273 | |
13274 | ZeroMemory(pGCRefMap, cbGCRefMap); |
13275 | |
13276 | if (!pMT->ContainsPointers()) |
13277 | return; |
13278 | |
13279 | CGCDesc* map = CGCDesc::GetCGCDescFromMT(pMT); |
13280 | CGCDescSeries* cur = map->GetHighestSeries(); |
13281 | CGCDescSeries* last = map->GetLowestSeries(); |
13282 | DWORD size = pMT->GetBaseSize(); |
13283 | _ASSERTE(cur >= last); |
13284 | |
13285 | do |
13286 | { |
13287 | // offset to embedded references in this series must be |
13288 | // adjusted by the VTable pointer, when in the unboxed state. |
13289 | size_t offset = cur->GetSeriesOffset() - TARGET_POINTER_SIZE; |
13290 | size_t offsetStop = offset + cur->GetSeriesSize() + size; |
13291 | while (offset < offsetStop) |
13292 | { |
13293 | size_t bit = offset / TARGET_POINTER_SIZE; |
13294 | |
13295 | size_t index = bit / 8; |
13296 | _ASSERTE(index < cbGCRefMap); |
13297 | pGCRefMap[index] |= (1 << (bit & 7)); |
13298 | |
13299 | offset += TARGET_POINTER_SIZE; |
13300 | } |
13301 | cur--; |
13302 | } while (cur >= last); |
13303 | } |
13304 | |
13305 | // |
13306 | // Type layout check verifies that there was no incompatible change in the value type layout. |
13307 | // If there was one, we will fall back to JIT instead of using the pre-generated code from the ready to run image. |
13308 | // This should be rare situation. Changes in value type layout not common. |
13309 | // |
13310 | // The following properties of the value type layout are checked: |
13311 | // - Size |
13312 | // - HFA-ness (on platform that support HFAs) |
13313 | // - Alignment |
13314 | // - Position of GC references |
13315 | // |
13316 | BOOL TypeLayoutCheck(MethodTable * pMT, PCCOR_SIGNATURE pBlob) |
13317 | { |
13318 | STANDARD_VM_CONTRACT; |
13319 | |
13320 | SigPointer p(pBlob); |
13321 | IfFailThrow(p.SkipExactlyOne()); |
13322 | |
13323 | DWORD dwFlags; |
13324 | IfFailThrow(p.GetData(&dwFlags)); |
13325 | |
13326 | // Size is checked unconditionally |
13327 | DWORD dwExpectedSize; |
13328 | IfFailThrow(p.GetData(&dwExpectedSize)); |
13329 | |
13330 | DWORD dwActualSize = pMT->GetNumInstanceFieldBytes(); |
13331 | if (dwExpectedSize != dwActualSize) |
13332 | return FALSE; |
13333 | |
13334 | #ifdef FEATURE_HFA |
13335 | if (dwFlags & READYTORUN_LAYOUT_HFA) |
13336 | { |
13337 | DWORD dwExpectedHFAType; |
13338 | IfFailThrow(p.GetData(&dwExpectedHFAType)); |
13339 | |
13340 | DWORD dwActualHFAType = pMT->GetHFAType(); |
13341 | if (dwExpectedHFAType != dwActualHFAType) |
13342 | return FALSE; |
13343 | } |
13344 | else |
13345 | { |
13346 | if (pMT->IsHFA()) |
13347 | return FALSE; |
13348 | } |
13349 | #else |
13350 | _ASSERTE(!(dwFlags & READYTORUN_LAYOUT_HFA)); |
13351 | #endif |
13352 | |
13353 | if (dwFlags & READYTORUN_LAYOUT_Alignment) |
13354 | { |
13355 | DWORD dwExpectedAlignment = TARGET_POINTER_SIZE; |
13356 | if (!(dwFlags & READYTORUN_LAYOUT_Alignment_Native)) |
13357 | { |
13358 | IfFailThrow(p.GetData(&dwExpectedAlignment)); |
13359 | } |
13360 | |
13361 | DWORD dwActualAlignment = CEEInfo::getClassAlignmentRequirementStatic(pMT); |
13362 | if (dwExpectedAlignment != dwActualAlignment) |
13363 | return FALSE; |
13364 | |
13365 | } |
13366 | |
13367 | if (dwFlags & READYTORUN_LAYOUT_GCLayout) |
13368 | { |
13369 | if (dwFlags & READYTORUN_LAYOUT_GCLayout_Empty) |
13370 | { |
13371 | if (pMT->ContainsPointers()) |
13372 | return FALSE; |
13373 | } |
13374 | else |
13375 | { |
13376 | size_t cbGCRefMap = (dwActualSize / TARGET_POINTER_SIZE + 7) / 8; |
13377 | _ASSERTE(cbGCRefMap > 0); |
13378 | |
13379 | BYTE * pGCRefMap = (BYTE *)_alloca(cbGCRefMap); |
13380 | |
13381 | ComputeGCRefMap(pMT, pGCRefMap, cbGCRefMap); |
13382 | |
13383 | if (memcmp(pGCRefMap, p.GetPtr(), cbGCRefMap) != 0) |
13384 | return FALSE; |
13385 | } |
13386 | } |
13387 | |
13388 | return TRUE; |
13389 | } |
13390 | |
13391 | #endif // FEATURE_READYTORUN |
13392 | |
13393 | BOOL LoadDynamicInfoEntry(Module *currentModule, |
13394 | RVA fixupRva, |
13395 | SIZE_T *entry) |
13396 | { |
13397 | STANDARD_VM_CONTRACT; |
13398 | |
13399 | PCCOR_SIGNATURE pBlob = currentModule->GetNativeFixupBlobData(fixupRva); |
13400 | |
13401 | BYTE kind = *pBlob++; |
13402 | |
13403 | Module * pInfoModule = currentModule; |
13404 | |
13405 | if (kind & ENCODE_MODULE_OVERRIDE) |
13406 | { |
13407 | pInfoModule = currentModule->GetModuleFromIndex(CorSigUncompressData(pBlob)); |
13408 | kind &= ~ENCODE_MODULE_OVERRIDE; |
13409 | } |
13410 | |
13411 | MethodDesc * pMD = NULL; |
13412 | |
13413 | #ifndef CROSSGEN_COMPILE |
13414 | PCCOR_SIGNATURE pSig; |
13415 | DWORD cSig; |
13416 | #endif // CROSSGEN_COMPILE |
13417 | |
13418 | size_t result = 0; |
13419 | |
13420 | switch (kind) |
13421 | { |
13422 | case ENCODE_MODULE_HANDLE: |
13423 | result = (size_t)pInfoModule; |
13424 | break; |
13425 | |
13426 | case ENCODE_TYPE_HANDLE: |
13427 | case ENCODE_TYPE_DICTIONARY: |
13428 | { |
13429 | TypeHandle th = ZapSig::DecodeType(currentModule, pInfoModule, pBlob); |
13430 | |
13431 | if (!th.IsTypeDesc()) |
13432 | { |
13433 | if (currentModule->IsReadyToRun()) |
13434 | { |
13435 | // We do not emit activation fixups for version resilient references. Activate the target explicitly. |
13436 | th.AsMethodTable()->EnsureInstanceActive(); |
13437 | } |
13438 | else |
13439 | { |
13440 | #ifdef FEATURE_WINMD_RESILIENT |
13441 | // We do not emit activation fixups for version resilient references. Activate the target explicitly. |
13442 | th.AsMethodTable()->EnsureInstanceActive(); |
13443 | #endif |
13444 | } |
13445 | } |
13446 | |
13447 | result = (size_t)th.AsPtr(); |
13448 | } |
13449 | break; |
13450 | |
13451 | case ENCODE_METHOD_HANDLE: |
13452 | case ENCODE_METHOD_DICTIONARY: |
13453 | { |
13454 | MethodDesc * pMD = ZapSig::DecodeMethod(currentModule, pInfoModule, pBlob); |
13455 | |
13456 | if (currentModule->IsReadyToRun()) |
13457 | { |
13458 | // We do not emit activation fixups for version resilient references. Activate the target explicitly. |
13459 | pMD->EnsureActive(); |
13460 | } |
13461 | |
13462 | result = (size_t)pMD; |
13463 | } |
13464 | break; |
13465 | |
13466 | case ENCODE_FIELD_HANDLE: |
13467 | result = (size_t) ZapSig::DecodeField(currentModule, pInfoModule, pBlob); |
13468 | break; |
13469 | |
13470 | #ifndef CROSSGEN_COMPILE |
13471 | case ENCODE_STRING_HANDLE: |
13472 | { |
13473 | // We need to update strings atomically (due to NoStringInterning attribute). Note |
13474 | // that modules with string interning dont really need this, as the hash tables have |
13475 | // their own locking, but dont add more complexity for what will be the non common |
13476 | // case. |
13477 | |
13478 | // We will have to lock and update the entry. (this is really a double check, where |
13479 | // the first check is done in the caller of this function) |
13480 | DWORD rid = CorSigUncompressData(pBlob); |
13481 | if (rid == 0) |
13482 | { |
13483 | // Empty string |
13484 | result = (size_t)StringObject::GetEmptyStringRefPtr(); |
13485 | } |
13486 | else |
13487 | { |
13488 | CrstHolder ch(pInfoModule->GetFixupCrst()); |
13489 | |
13490 | if (!CORCOMPILE_IS_POINTER_TAGGED(*entry) && (*entry != NULL)) |
13491 | { |
13492 | // We lost the race, just return |
13493 | return TRUE; |
13494 | } |
13495 | |
13496 | // For generic instantiations compiled into the ngen image of some other |
13497 | // client assembly, we need to ensure that we intern the string |
13498 | // in the defining assembly. |
13499 | bool mayNeedToSyncWithFixups = pInfoModule != currentModule; |
13500 | |
13501 | result = (size_t) pInfoModule->ResolveStringRef(TokenFromRid(rid, mdtString), currentModule->GetDomain(), mayNeedToSyncWithFixups); |
13502 | } |
13503 | } |
13504 | break; |
13505 | |
13506 | case ENCODE_VARARGS_SIG: |
13507 | { |
13508 | mdSignature token = TokenFromRid( |
13509 | CorSigUncompressData(pBlob), |
13510 | mdtSignature); |
13511 | |
13512 | IfFailThrow(pInfoModule->GetMDImport()->GetSigFromToken(token, &cSig, &pSig)); |
13513 | |
13514 | goto VarArgs; |
13515 | } |
13516 | break; |
13517 | |
13518 | case ENCODE_VARARGS_METHODREF: |
13519 | { |
13520 | mdSignature token = TokenFromRid( |
13521 | CorSigUncompressData(pBlob), |
13522 | mdtMemberRef); |
13523 | |
13524 | LPCSTR szName_Ignore; |
13525 | IfFailThrow(pInfoModule->GetMDImport()->GetNameAndSigOfMemberRef(token, &pSig, &cSig, &szName_Ignore)); |
13526 | |
13527 | goto VarArgs; |
13528 | } |
13529 | break; |
13530 | |
13531 | case ENCODE_VARARGS_METHODDEF: |
13532 | { |
13533 | mdSignature token = TokenFromRid( |
13534 | CorSigUncompressData(pBlob), |
13535 | mdtMethodDef); |
13536 | |
13537 | IfFailThrow(pInfoModule->GetMDImport()->GetSigOfMethodDef(token, &cSig, &pSig)); |
13538 | } |
13539 | { |
13540 | VarArgs: |
13541 | result = (size_t) CORINFO_VARARGS_HANDLE(currentModule->GetVASigCookie(Signature(pSig, cSig))); |
13542 | } |
13543 | break; |
13544 | |
13545 | // ENCODE_METHOD_NATIVECALLABLE_HANDLE is same as ENCODE_METHOD_ENTRY_DEF_TOKEN |
13546 | // except for AddrOfCode |
13547 | case ENCODE_METHOD_NATIVE_ENTRY: |
13548 | case ENCODE_METHOD_ENTRY_DEF_TOKEN: |
13549 | { |
13550 | mdToken MethodDef = TokenFromRid(CorSigUncompressData(pBlob), mdtMethodDef); |
13551 | pMD = MemberLoader::GetMethodDescFromMethodDef(pInfoModule, MethodDef, FALSE); |
13552 | |
13553 | pMD->PrepareForUseAsADependencyOfANativeImage(); |
13554 | |
13555 | if (currentModule->IsReadyToRun()) |
13556 | { |
13557 | // We do not emit activation fixups for version resilient references. Activate the target explicitly. |
13558 | pMD->EnsureActive(); |
13559 | } |
13560 | |
13561 | goto MethodEntry; |
13562 | } |
13563 | |
13564 | case ENCODE_METHOD_ENTRY_REF_TOKEN: |
13565 | { |
13566 | SigTypeContext typeContext; |
13567 | mdToken MemberRef = TokenFromRid(CorSigUncompressData(pBlob), mdtMemberRef); |
13568 | FieldDesc * pFD = NULL; |
13569 | TypeHandle th; |
13570 | |
13571 | MemberLoader::GetDescFromMemberRef(pInfoModule, MemberRef, &pMD, &pFD, &typeContext, FALSE /* strict metadata checks */, &th); |
13572 | _ASSERTE(pMD != NULL); |
13573 | |
13574 | pMD->PrepareForUseAsADependencyOfANativeImage(); |
13575 | |
13576 | if (currentModule->IsReadyToRun()) |
13577 | { |
13578 | // We do not emit activation fixups for version resilient references. Activate the target explicitly. |
13579 | pMD->EnsureActive(); |
13580 | } |
13581 | else |
13582 | { |
13583 | #ifdef FEATURE_WINMD_RESILIENT |
13584 | // We do not emit activation fixups for version resilient references. Activate the target explicitly. |
13585 | pMD->EnsureActive(); |
13586 | #endif |
13587 | } |
13588 | |
13589 | goto MethodEntry; |
13590 | } |
13591 | |
13592 | case ENCODE_METHOD_ENTRY: |
13593 | { |
13594 | pMD = ZapSig::DecodeMethod(currentModule, pInfoModule, pBlob); |
13595 | |
13596 | if (currentModule->IsReadyToRun()) |
13597 | { |
13598 | // We do not emit activation fixups for version resilient references. Activate the target explicitly. |
13599 | pMD->EnsureActive(); |
13600 | } |
13601 | |
13602 | MethodEntry: |
13603 | if (kind == ENCODE_METHOD_NATIVE_ENTRY) |
13604 | { |
13605 | result = COMDelegate::ConvertToCallback(pMD); |
13606 | } |
13607 | else |
13608 | { |
13609 | result = pMD->GetMultiCallableAddrOfCode(CORINFO_ACCESS_ANY); |
13610 | } |
13611 | |
13612 | #ifndef _TARGET_ARM_ |
13613 | if (CORCOMPILE_IS_PCODE_TAGGED(result)) |
13614 | { |
13615 | // There is a rare case where the function entrypoint may not be aligned. This could happen only for FCalls, |
13616 | // only on x86 and only if we failed to hardbind the fcall (e.g. ngen image for mscorlib.dll does not exist |
13617 | // and /nodependencies flag for ngen was used). The function entrypoints should be aligned in all other cases. |
13618 | // |
13619 | // We will wrap the unaligned method entrypoint by funcptr stub with aligned entrypoint. |
13620 | _ASSERTE(pMD->IsFCall()); |
13621 | result = pMD->GetLoaderAllocator()->GetFuncPtrStubs()->GetFuncPtrStub(pMD); |
13622 | } |
13623 | #endif |
13624 | } |
13625 | break; |
13626 | |
13627 | case ENCODE_SYNC_LOCK: |
13628 | { |
13629 | TypeHandle th = ZapSig::DecodeType(currentModule, pInfoModule, pBlob); |
13630 | |
13631 | result = (size_t) GetClassSync(th.AsMethodTable()); |
13632 | } |
13633 | break; |
13634 | |
13635 | case ENCODE_INDIRECT_PINVOKE_TARGET: |
13636 | { |
13637 | MethodDesc *pMethod = ZapSig::DecodeMethod(currentModule, pInfoModule, pBlob); |
13638 | |
13639 | _ASSERTE(pMethod->IsNDirect()); |
13640 | NDirectMethodDesc *pMD = (NDirectMethodDesc*)pMethod; |
13641 | result = (size_t)(LPVOID)&(pMD->GetWriteableData()->m_pNDirectTarget); |
13642 | } |
13643 | break; |
13644 | |
13645 | #if defined(PROFILING_SUPPORTED) |
13646 | case ENCODE_PROFILING_HANDLE: |
13647 | { |
13648 | MethodDesc *pMethod = ZapSig::DecodeMethod(currentModule, pInfoModule, pBlob); |
13649 | |
13650 | // methods with no metadata behind cannot be exposed to tools expecting metadata (profiler, debugger...) |
13651 | // they shouldnever come here as they are called out in GetCompileFlag |
13652 | _ASSERTE(!pMethod->IsNoMetadata()); |
13653 | |
13654 | FunctionID funId = (FunctionID)pMethod; |
13655 | |
13656 | BOOL bHookFunction = TRUE; |
13657 | CORINFO_PROFILING_HANDLE profilerHandle = (CORINFO_PROFILING_HANDLE)funId; |
13658 | |
13659 | { |
13660 | BEGIN_PIN_PROFILER(CORProfilerFunctionIDMapperEnabled()); |
13661 | profilerHandle = (CORINFO_PROFILING_HANDLE) g_profControlBlock.pProfInterface->EEFunctionIDMapper(funId, &bHookFunction); |
13662 | END_PIN_PROFILER(); |
13663 | } |
13664 | |
13665 | // Profiling handle is opaque token. It does not have to be aligned thus we can not store it in the same location as token. |
13666 | *EnsureWritablePages(entry+kZapProfilingHandleImportValueIndexClientData) = (SIZE_T)profilerHandle; |
13667 | |
13668 | if (bHookFunction) |
13669 | { |
13670 | *EnsureWritablePages(entry+kZapProfilingHandleImportValueIndexEnterAddr) = (SIZE_T)(void *)hlpDynamicFuncTable[DYNAMIC_CORINFO_HELP_PROF_FCN_ENTER].pfnHelper; |
13671 | *EnsureWritablePages(entry+kZapProfilingHandleImportValueIndexLeaveAddr) = (SIZE_T)(void *)hlpDynamicFuncTable[DYNAMIC_CORINFO_HELP_PROF_FCN_LEAVE].pfnHelper; |
13672 | *EnsureWritablePages(entry+kZapProfilingHandleImportValueIndexTailcallAddr) = (SIZE_T)(void *)hlpDynamicFuncTable[DYNAMIC_CORINFO_HELP_PROF_FCN_TAILCALL].pfnHelper; |
13673 | } |
13674 | else |
13675 | { |
13676 | *EnsureWritablePages(entry+kZapProfilingHandleImportValueIndexEnterAddr) = (SIZE_T)(void *)JIT_ProfilerEnterLeaveTailcallStub; |
13677 | *EnsureWritablePages(entry+kZapProfilingHandleImportValueIndexLeaveAddr) = (SIZE_T)(void *)JIT_ProfilerEnterLeaveTailcallStub; |
13678 | *EnsureWritablePages(entry+kZapProfilingHandleImportValueIndexTailcallAddr) = (SIZE_T)(void *)JIT_ProfilerEnterLeaveTailcallStub; |
13679 | } |
13680 | } |
13681 | break; |
13682 | #endif // PROFILING_SUPPORTED |
13683 | |
13684 | case ENCODE_STATIC_FIELD_ADDRESS: |
13685 | { |
13686 | FieldDesc *pField = ZapSig::DecodeField(currentModule, pInfoModule, pBlob); |
13687 | |
13688 | pField->GetEnclosingMethodTable()->CheckRestore(); |
13689 | |
13690 | // We can take address of RVA field only since ngened code is domain neutral |
13691 | _ASSERTE(pField->IsRVA()); |
13692 | |
13693 | // Field address is not aligned thus we can not store it in the same location as token. |
13694 | *EnsureWritablePages(entry+1) = (size_t)pField->GetStaticAddressHandle(NULL); |
13695 | } |
13696 | break; |
13697 | |
13698 | case ENCODE_VIRTUAL_ENTRY_SLOT: |
13699 | { |
13700 | DWORD slot = CorSigUncompressData(pBlob); |
13701 | |
13702 | TypeHandle ownerType = ZapSig::DecodeType(currentModule, pInfoModule, pBlob); |
13703 | |
13704 | LOG((LF_ZAP, LL_INFO100000, " Fixup stub dispatch\n" )); |
13705 | |
13706 | VirtualCallStubManager * pMgr = currentModule->GetLoaderAllocator()->GetVirtualCallStubManager(); |
13707 | |
13708 | // <REVISIT_TODO> |
13709 | // We should be generating a stub indirection here, but the zapper already uses one level |
13710 | // of indirection, i.e. we would have to return IAT_PPVALUE to the JIT, and on the whole the JITs |
13711 | // aren't quite set up to accept that. Furthermore the call sequences would be different - at |
13712 | // the moment an indirection cell uses "call [cell-addr]" on x86, and instead we would want the |
13713 | // euqivalent of "call [[call-addr]]". This could perhaps be implemented as "call [eax]" </REVISIT_TODO> |
13714 | result = pMgr->GetCallStub(ownerType, slot); |
13715 | } |
13716 | break; |
13717 | |
13718 | case ENCODE_CLASS_ID_FOR_STATICS: |
13719 | { |
13720 | TypeHandle th = ZapSig::DecodeType(currentModule, pInfoModule, pBlob); |
13721 | |
13722 | MethodTable * pMT = th.AsMethodTable(); |
13723 | if (pMT->IsDynamicStatics()) |
13724 | { |
13725 | result = pMT->GetModuleDynamicEntryID(); |
13726 | } |
13727 | else |
13728 | { |
13729 | result = pMT->GetClassIndex(); |
13730 | } |
13731 | } |
13732 | break; |
13733 | |
13734 | case ENCODE_MODULE_ID_FOR_STATICS: |
13735 | { |
13736 | result = pInfoModule->GetModuleID(); |
13737 | } |
13738 | break; |
13739 | |
13740 | case ENCODE_MODULE_ID_FOR_GENERIC_STATICS: |
13741 | { |
13742 | TypeHandle th = ZapSig::DecodeType(currentModule, pInfoModule, pBlob); |
13743 | |
13744 | MethodTable * pMT = th.AsMethodTable(); |
13745 | |
13746 | result = pMT->GetModuleForStatics()->GetModuleID(); |
13747 | } |
13748 | break; |
13749 | |
13750 | case ENCODE_ACTIVE_DEPENDENCY: |
13751 | { |
13752 | Module* pModule = currentModule->GetModuleFromIndex(CorSigUncompressData(pBlob)); |
13753 | |
13754 | STRESS_LOG3(LF_ZAP,LL_INFO10000,"Modules are: %08x,%08x,%08x" ,currentModule,pInfoModule,pModule); |
13755 | pInfoModule->AddActiveDependency(pModule, FALSE); |
13756 | } |
13757 | break; |
13758 | |
13759 | #ifdef FEATURE_READYTORUN |
13760 | case ENCODE_READYTORUN_HELPER: |
13761 | { |
13762 | DWORD helperNum = CorSigUncompressData(pBlob); |
13763 | |
13764 | CorInfoHelpFunc corInfoHelpFunc = MapReadyToRunHelper((ReadyToRunHelper)helperNum); |
13765 | if (corInfoHelpFunc != CORINFO_HELP_UNDEF) |
13766 | { |
13767 | result = (size_t)CEEJitInfo::getHelperFtnStatic(corInfoHelpFunc); |
13768 | } |
13769 | else |
13770 | { |
13771 | switch (helperNum) |
13772 | { |
13773 | case READYTORUN_HELPER_Module: |
13774 | { |
13775 | Module * pPrevious = InterlockedCompareExchangeT(EnsureWritablePages((Module **)entry), pInfoModule, NULL); |
13776 | if (pPrevious != pInfoModule && pPrevious != NULL) |
13777 | COMPlusThrowHR(COR_E_FILELOAD, IDS_NATIVE_IMAGE_CANNOT_BE_LOADED_MULTIPLE_TIMES, pInfoModule->GetPath()); |
13778 | return TRUE; |
13779 | } |
13780 | break; |
13781 | |
13782 | case READYTORUN_HELPER_GSCookie: |
13783 | result = (size_t)GetProcessGSCookie(); |
13784 | break; |
13785 | |
13786 | case READYTORUN_HELPER_DelayLoad_MethodCall: |
13787 | result = (size_t)GetEEFuncEntryPoint(DelayLoad_MethodCall); |
13788 | break; |
13789 | |
13790 | case READYTORUN_HELPER_DelayLoad_Helper: |
13791 | result = (size_t)GetEEFuncEntryPoint(DelayLoad_Helper); |
13792 | break; |
13793 | |
13794 | case READYTORUN_HELPER_DelayLoad_Helper_Obj: |
13795 | result = (size_t)GetEEFuncEntryPoint(DelayLoad_Helper_Obj); |
13796 | break; |
13797 | |
13798 | case READYTORUN_HELPER_DelayLoad_Helper_ObjObj: |
13799 | result = (size_t)GetEEFuncEntryPoint(DelayLoad_Helper_ObjObj); |
13800 | break; |
13801 | |
13802 | default: |
13803 | STRESS_LOG1(LF_ZAP, LL_WARNING, "Unknown READYTORUN_HELPER %d\n" , helperNum); |
13804 | _ASSERTE(!"Unknown READYTORUN_HELPER" ); |
13805 | return FALSE; |
13806 | } |
13807 | } |
13808 | } |
13809 | break; |
13810 | |
13811 | case ENCODE_FIELD_OFFSET: |
13812 | { |
13813 | FieldDesc * pFD = ZapSig::DecodeField(currentModule, pInfoModule, pBlob); |
13814 | _ASSERTE(!pFD->IsStatic()); |
13815 | _ASSERTE(!pFD->IsFieldOfValueType()); |
13816 | |
13817 | DWORD dwOffset = (DWORD)sizeof(Object) + pFD->GetOffset(); |
13818 | |
13819 | if (dwOffset > MAX_UNCHECKED_OFFSET_FOR_NULL_OBJECT) |
13820 | return FALSE; |
13821 | result = dwOffset; |
13822 | } |
13823 | break; |
13824 | |
13825 | case ENCODE_FIELD_BASE_OFFSET: |
13826 | { |
13827 | TypeHandle th = ZapSig::DecodeType(currentModule, pInfoModule, pBlob); |
13828 | |
13829 | MethodTable * pMT = th.AsMethodTable(); |
13830 | _ASSERTE(!pMT->IsValueType()); |
13831 | |
13832 | DWORD dwOffsetBase = ReadyToRunInfo::GetFieldBaseOffset(pMT); |
13833 | if (dwOffsetBase > MAX_UNCHECKED_OFFSET_FOR_NULL_OBJECT) |
13834 | return FALSE; |
13835 | result = dwOffsetBase; |
13836 | } |
13837 | break; |
13838 | |
13839 | case ENCODE_CHECK_TYPE_LAYOUT: |
13840 | { |
13841 | TypeHandle th = ZapSig::DecodeType(currentModule, pInfoModule, pBlob); |
13842 | MethodTable * pMT = th.AsMethodTable(); |
13843 | _ASSERTE(pMT->IsValueType()); |
13844 | |
13845 | if (!TypeLayoutCheck(pMT, pBlob)) |
13846 | return FALSE; |
13847 | |
13848 | result = 1; |
13849 | } |
13850 | break; |
13851 | |
13852 | case ENCODE_CHECK_FIELD_OFFSET: |
13853 | { |
13854 | DWORD dwExpectedOffset = CorSigUncompressData(pBlob); |
13855 | |
13856 | FieldDesc * pFD = ZapSig::DecodeField(currentModule, pInfoModule, pBlob); |
13857 | _ASSERTE(!pFD->IsStatic()); |
13858 | |
13859 | DWORD dwOffset = pFD->GetOffset(); |
13860 | if (!pFD->IsFieldOfValueType()) |
13861 | dwOffset += sizeof(Object); |
13862 | |
13863 | if (dwExpectedOffset != dwOffset) |
13864 | return FALSE; |
13865 | |
13866 | result = 1; |
13867 | } |
13868 | break; |
13869 | #endif // FEATURE_READYTORUN |
13870 | |
13871 | #endif // CROSSGEN_COMPILE |
13872 | |
13873 | default: |
13874 | STRESS_LOG1(LF_ZAP, LL_WARNING, "Unknown FIXUP_BLOB_KIND %d\n" , kind); |
13875 | _ASSERTE(!"Unknown FIXUP_BLOB_KIND" ); |
13876 | return FALSE; |
13877 | } |
13878 | |
13879 | MemoryBarrier(); |
13880 | *EnsureWritablePages(entry) = result; |
13881 | |
13882 | return TRUE; |
13883 | } |
13884 | #endif // FEATURE_PREJIT |
13885 | |
13886 | void* CEEInfo::getTailCallCopyArgsThunk(CORINFO_SIG_INFO *pSig, |
13887 | CorInfoHelperTailCallSpecialHandling flags) |
13888 | { |
13889 | CONTRACTL { |
13890 | SO_TOLERANT; |
13891 | THROWS; |
13892 | GC_TRIGGERS; |
13893 | MODE_PREEMPTIVE; |
13894 | } CONTRACTL_END; |
13895 | |
13896 | void * ftn = NULL; |
13897 | |
13898 | #if (defined(_TARGET_AMD64_) || defined(_TARGET_ARM_)) && !defined(FEATURE_PAL) |
13899 | |
13900 | JIT_TO_EE_TRANSITION(); |
13901 | |
13902 | Stub* pStub = CPUSTUBLINKER::CreateTailCallCopyArgsThunk(pSig, m_pMethodBeingCompiled, flags); |
13903 | |
13904 | ftn = (void*)pStub->GetEntryPoint(); |
13905 | |
13906 | EE_TO_JIT_TRANSITION(); |
13907 | |
13908 | #endif // (_TARGET_AMD64_ || _TARGET_ARM_) && !FEATURE_PAL |
13909 | |
13910 | return ftn; |
13911 | } |
13912 | |
13913 | bool CEEInfo::convertPInvokeCalliToCall(CORINFO_RESOLVED_TOKEN * pResolvedToken, bool fMustConvert) |
13914 | { |
13915 | return false; |
13916 | } |
13917 | |
13918 | void CEEInfo::allocMem ( |
13919 | ULONG hotCodeSize, /* IN */ |
13920 | ULONG coldCodeSize, /* IN */ |
13921 | ULONG roDataSize, /* IN */ |
13922 | ULONG xcptnsCount, /* IN */ |
13923 | CorJitAllocMemFlag flag, /* IN */ |
13924 | void ** hotCodeBlock, /* OUT */ |
13925 | void ** coldCodeBlock, /* OUT */ |
13926 | void ** roDataBlock /* OUT */ |
13927 | ) |
13928 | { |
13929 | LIMITED_METHOD_CONTRACT; |
13930 | UNREACHABLE(); // only called on derived class. |
13931 | } |
13932 | |
13933 | void CEEInfo::reserveUnwindInfo ( |
13934 | BOOL isFunclet, /* IN */ |
13935 | BOOL isColdCode, /* IN */ |
13936 | ULONG unwindSize /* IN */ |
13937 | ) |
13938 | { |
13939 | LIMITED_METHOD_CONTRACT; |
13940 | UNREACHABLE(); // only called on derived class. |
13941 | } |
13942 | |
13943 | void CEEInfo::allocUnwindInfo ( |
13944 | BYTE * pHotCode, /* IN */ |
13945 | BYTE * pColdCode, /* IN */ |
13946 | ULONG startOffset, /* IN */ |
13947 | ULONG endOffset, /* IN */ |
13948 | ULONG unwindSize, /* IN */ |
13949 | BYTE * pUnwindBlock, /* IN */ |
13950 | CorJitFuncKind funcKind /* IN */ |
13951 | ) |
13952 | { |
13953 | LIMITED_METHOD_CONTRACT; |
13954 | UNREACHABLE(); // only called on derived class. |
13955 | } |
13956 | |
13957 | void * CEEInfo::allocGCInfo ( |
13958 | size_t size /* IN */ |
13959 | ) |
13960 | { |
13961 | LIMITED_METHOD_CONTRACT; |
13962 | UNREACHABLE_RET(); // only called on derived class. |
13963 | } |
13964 | |
13965 | void CEEInfo::setEHcount ( |
13966 | unsigned cEH /* IN */ |
13967 | ) |
13968 | { |
13969 | LIMITED_METHOD_CONTRACT; |
13970 | UNREACHABLE(); // only called on derived class. |
13971 | } |
13972 | |
13973 | void CEEInfo::setEHinfo ( |
13974 | unsigned EHnumber, /* IN */ |
13975 | const CORINFO_EH_CLAUSE *clause /* IN */ |
13976 | ) |
13977 | { |
13978 | LIMITED_METHOD_CONTRACT; |
13979 | UNREACHABLE(); // only called on derived class. |
13980 | } |
13981 | |
13982 | InfoAccessType CEEInfo::constructStringLiteral(CORINFO_MODULE_HANDLE scopeHnd, |
13983 | mdToken metaTok, |
13984 | void **ppValue) |
13985 | { |
13986 | LIMITED_METHOD_CONTRACT; |
13987 | UNREACHABLE(); // only called on derived class. |
13988 | } |
13989 | |
13990 | InfoAccessType CEEInfo::emptyStringLiteral(void ** ppValue) |
13991 | { |
13992 | LIMITED_METHOD_CONTRACT; |
13993 | _ASSERTE(isVerifyOnly()); |
13994 | *ppValue = (void *)0x10; |
13995 | return IAT_PVALUE; |
13996 | } |
13997 | |
13998 | void* CEEInfo::getFieldAddress(CORINFO_FIELD_HANDLE fieldHnd, |
13999 | void **ppIndirection) |
14000 | { |
14001 | CONTRACTL{ |
14002 | SO_TOLERANT; |
14003 | THROWS; |
14004 | GC_TRIGGERS; |
14005 | MODE_PREEMPTIVE; |
14006 | } CONTRACTL_END; |
14007 | |
14008 | void *result = NULL; |
14009 | |
14010 | if (ppIndirection != NULL) |
14011 | *ppIndirection = NULL; |
14012 | |
14013 | // Do not bother with initialization if we are only verifying the method. |
14014 | if (isVerifyOnly()) |
14015 | { |
14016 | return (void *)0x10; |
14017 | } |
14018 | |
14019 | JIT_TO_EE_TRANSITION(); |
14020 | |
14021 | FieldDesc* field = (FieldDesc*)fieldHnd; |
14022 | |
14023 | _ASSERTE(field->IsRVA()); |
14024 | |
14025 | result = field->GetStaticAddressHandle(NULL); |
14026 | |
14027 | EE_TO_JIT_TRANSITION(); |
14028 | |
14029 | return result; |
14030 | } |
14031 | |
14032 | CORINFO_CLASS_HANDLE CEEInfo::getStaticFieldCurrentClass(CORINFO_FIELD_HANDLE fieldHnd, |
14033 | bool* pIsSpeculative) |
14034 | { |
14035 | LIMITED_METHOD_CONTRACT; |
14036 | _ASSERTE(isVerifyOnly()); |
14037 | if (pIsSpeculative != NULL) |
14038 | *pIsSpeculative = true; |
14039 | return NULL; |
14040 | } |
14041 | |
14042 | void* CEEInfo::getMethodSync(CORINFO_METHOD_HANDLE ftnHnd, |
14043 | void **ppIndirection) |
14044 | { |
14045 | LIMITED_METHOD_CONTRACT; |
14046 | UNREACHABLE(); // only called on derived class. |
14047 | } |
14048 | |
14049 | HRESULT CEEInfo::allocBBProfileBuffer ( |
14050 | ULONG count, // The number of basic blocks that we have |
14051 | ProfileBuffer ** profileBuffer |
14052 | ) |
14053 | { |
14054 | LIMITED_METHOD_CONTRACT; |
14055 | UNREACHABLE_RET(); // only called on derived class. |
14056 | } |
14057 | |
14058 | HRESULT CEEInfo::getBBProfileData( |
14059 | CORINFO_METHOD_HANDLE ftnHnd, |
14060 | ULONG * count, // The number of basic blocks that we have |
14061 | ProfileBuffer ** profileBuffer, |
14062 | ULONG * numRuns |
14063 | ) |
14064 | { |
14065 | LIMITED_METHOD_CONTRACT; |
14066 | UNREACHABLE_RET(); // only called on derived class. |
14067 | } |
14068 | |
14069 | |
14070 | void CEEInfo::recordCallSite( |
14071 | ULONG instrOffset, /* IN */ |
14072 | CORINFO_SIG_INFO * callSig, /* IN */ |
14073 | CORINFO_METHOD_HANDLE methodHandle /* IN */ |
14074 | ) |
14075 | { |
14076 | LIMITED_METHOD_CONTRACT; |
14077 | UNREACHABLE(); // only called on derived class. |
14078 | } |
14079 | |
14080 | void CEEInfo::recordRelocation( |
14081 | void * location, /* IN */ |
14082 | void * target, /* IN */ |
14083 | WORD fRelocType, /* IN */ |
14084 | WORD slotNum, /* IN */ |
14085 | INT32 addlDelta /* IN */ |
14086 | ) |
14087 | { |
14088 | LIMITED_METHOD_CONTRACT; |
14089 | UNREACHABLE(); // only called on derived class. |
14090 | } |
14091 | |
14092 | WORD CEEInfo::getRelocTypeHint(void * target) |
14093 | { |
14094 | LIMITED_METHOD_CONTRACT; |
14095 | UNREACHABLE_RET(); // only called on derived class. |
14096 | } |
14097 | |
14098 | void CEEInfo::getModuleNativeEntryPointRange( |
14099 | void ** pStart, /* OUT */ |
14100 | void ** pEnd /* OUT */ |
14101 | ) |
14102 | { |
14103 | LIMITED_METHOD_CONTRACT; |
14104 | UNREACHABLE(); // only called on derived class. |
14105 | } |
14106 | |
14107 | DWORD CEEInfo::getExpectedTargetArchitecture() |
14108 | { |
14109 | LIMITED_METHOD_CONTRACT; |
14110 | |
14111 | return IMAGE_FILE_MACHINE_NATIVE; |
14112 | } |
14113 | |
14114 | void CEEInfo::setBoundaries(CORINFO_METHOD_HANDLE ftn, ULONG32 cMap, |
14115 | ICorDebugInfo::OffsetMapping *pMap) |
14116 | { |
14117 | LIMITED_METHOD_CONTRACT; |
14118 | UNREACHABLE(); // only called on derived class. |
14119 | } |
14120 | |
14121 | void CEEInfo::setVars(CORINFO_METHOD_HANDLE ftn, ULONG32 cVars, ICorDebugInfo::NativeVarInfo *vars) |
14122 | { |
14123 | LIMITED_METHOD_CONTRACT; |
14124 | UNREACHABLE(); // only called on derived class. |
14125 | } |
14126 | |
14127 | void* CEEInfo::getHelperFtn(CorInfoHelpFunc ftnNum, /* IN */ |
14128 | void ** ppIndirection) /* OUT */ |
14129 | { |
14130 | LIMITED_METHOD_CONTRACT; |
14131 | UNREACHABLE(); // only called on derived class. |
14132 | } |
14133 | |
14134 | // Active dependency helpers |
14135 | void CEEInfo::addActiveDependency(CORINFO_MODULE_HANDLE moduleFrom,CORINFO_MODULE_HANDLE moduleTo) |
14136 | { |
14137 | LIMITED_METHOD_CONTRACT; |
14138 | UNREACHABLE(); // only called on derived class. |
14139 | } |
14140 | |
14141 | void CEEInfo::GetProfilingHandle(BOOL *pbHookFunction, |
14142 | void **pProfilerHandle, |
14143 | BOOL *pbIndirectedHandles) |
14144 | { |
14145 | LIMITED_METHOD_CONTRACT; |
14146 | UNREACHABLE(); // only called on derived class. |
14147 | } |
14148 | |
14149 | #endif // !DACCESS_COMPILE |
14150 | |
14151 | EECodeInfo::EECodeInfo() |
14152 | { |
14153 | WRAPPER_NO_CONTRACT; |
14154 | |
14155 | m_codeAddress = NULL; |
14156 | |
14157 | m_pJM = NULL; |
14158 | m_pMD = NULL; |
14159 | m_relOffset = 0; |
14160 | |
14161 | #ifdef WIN64EXCEPTIONS |
14162 | m_pFunctionEntry = NULL; |
14163 | #endif |
14164 | } |
14165 | |
14166 | void EECodeInfo::Init(PCODE codeAddress) |
14167 | { |
14168 | CONTRACTL { |
14169 | NOTHROW; |
14170 | GC_NOTRIGGER; |
14171 | SO_TOLERANT; |
14172 | } CONTRACTL_END; |
14173 | |
14174 | Init(codeAddress, ExecutionManager::GetScanFlags()); |
14175 | } |
14176 | |
14177 | void EECodeInfo::Init(PCODE codeAddress, ExecutionManager::ScanFlag scanFlag) |
14178 | { |
14179 | CONTRACTL { |
14180 | NOTHROW; |
14181 | GC_NOTRIGGER; |
14182 | SO_TOLERANT; |
14183 | } CONTRACTL_END; |
14184 | |
14185 | m_codeAddress = codeAddress; |
14186 | |
14187 | RangeSection * pRS = ExecutionManager::FindCodeRange(codeAddress, scanFlag); |
14188 | if (pRS == NULL) |
14189 | goto Invalid; |
14190 | |
14191 | if (!pRS->pjit->JitCodeToMethodInfo(pRS, codeAddress, &m_pMD, this)) |
14192 | goto Invalid; |
14193 | |
14194 | m_pJM = pRS->pjit; |
14195 | return; |
14196 | |
14197 | Invalid: |
14198 | m_pJM = NULL; |
14199 | m_pMD = NULL; |
14200 | m_relOffset = 0; |
14201 | |
14202 | #ifdef WIN64EXCEPTIONS |
14203 | m_pFunctionEntry = NULL; |
14204 | #endif |
14205 | } |
14206 | |
14207 | TADDR EECodeInfo::GetSavedMethodCode() |
14208 | { |
14209 | CONTRACTL { |
14210 | // All EECodeInfo methods must be NOTHROW/GC_NOTRIGGER since they can |
14211 | // be used during GC. |
14212 | NOTHROW; |
14213 | GC_NOTRIGGER; |
14214 | HOST_NOCALLS; |
14215 | SUPPORTS_DAC; |
14216 | } CONTRACTL_END; |
14217 | #ifndef _WIN64 |
14218 | #if defined(HAVE_GCCOVER) |
14219 | _ASSERTE (!m_pMD->m_GcCover || GCStress<cfg_instr>::IsEnabled()); |
14220 | if (GCStress<cfg_instr>::IsEnabled() |
14221 | && m_pMD->m_GcCover) |
14222 | { |
14223 | _ASSERTE(m_pMD->m_GcCover->savedCode); |
14224 | |
14225 | // Make sure we return the TADDR of savedCode here. The byte array is not marshaled automatically. |
14226 | // The caller is responsible for any necessary marshaling. |
14227 | return PTR_TO_MEMBER_TADDR(GCCoverageInfo, m_pMD->m_GcCover, savedCode); |
14228 | } |
14229 | #endif //defined(HAVE_GCCOVER) |
14230 | #endif |
14231 | |
14232 | return GetStartAddress(); |
14233 | } |
14234 | |
14235 | TADDR EECodeInfo::GetStartAddress() |
14236 | { |
14237 | CONTRACTL { |
14238 | NOTHROW; |
14239 | GC_NOTRIGGER; |
14240 | HOST_NOCALLS; |
14241 | SUPPORTS_DAC; |
14242 | } CONTRACTL_END; |
14243 | |
14244 | return m_pJM->JitTokenToStartAddress(m_methodToken); |
14245 | } |
14246 | |
14247 | #if defined(WIN64EXCEPTIONS) |
14248 | |
14249 | // ---------------------------------------------------------------------------- |
14250 | // EECodeInfo::GetMainFunctionInfo |
14251 | // |
14252 | // Description: |
14253 | // Simple helper to transform a funclet's EECodeInfo into a parent function EECodeInfo. |
14254 | // |
14255 | // Return Value: |
14256 | // An EECodeInfo for the start of the main function body (offset 0). |
14257 | // |
14258 | |
14259 | EECodeInfo EECodeInfo::GetMainFunctionInfo() |
14260 | { |
14261 | LIMITED_METHOD_CONTRACT; |
14262 | SUPPORTS_DAC; |
14263 | |
14264 | EECodeInfo result = *this; |
14265 | result.m_relOffset = 0; |
14266 | result.m_codeAddress = this->GetStartAddress(); |
14267 | result.m_pFunctionEntry = NULL; |
14268 | |
14269 | return result; |
14270 | } |
14271 | |
14272 | PTR_RUNTIME_FUNCTION EECodeInfo::GetFunctionEntry() |
14273 | { |
14274 | LIMITED_METHOD_CONTRACT; |
14275 | SUPPORTS_DAC; |
14276 | |
14277 | if (m_pFunctionEntry == NULL) |
14278 | m_pFunctionEntry = m_pJM->LazyGetFunctionEntry(this); |
14279 | return m_pFunctionEntry; |
14280 | } |
14281 | |
14282 | #if defined(_TARGET_AMD64_) |
14283 | |
14284 | BOOL EECodeInfo::HasFrameRegister() |
14285 | { |
14286 | LIMITED_METHOD_CONTRACT; |
14287 | |
14288 | PTR_RUNTIME_FUNCTION pFuncEntry = GetFunctionEntry(); |
14289 | _ASSERTE(pFuncEntry != NULL); |
14290 | |
14291 | BOOL fHasFrameRegister = FALSE; |
14292 | PUNWIND_INFO pUnwindInfo = (PUNWIND_INFO)(GetModuleBase() + pFuncEntry->UnwindData); |
14293 | if (pUnwindInfo->FrameRegister != 0) |
14294 | { |
14295 | fHasFrameRegister = TRUE; |
14296 | _ASSERTE(pUnwindInfo->FrameRegister == kRBP); |
14297 | } |
14298 | |
14299 | return fHasFrameRegister; |
14300 | } |
14301 | #endif // defined(_TARGET_AMD64_) |
14302 | |
14303 | #endif // defined(WIN64EXCEPTIONS) |
14304 | |
14305 | |
14306 | #if defined(_TARGET_AMD64_) |
14307 | // ---------------------------------------------------------------------------- |
14308 | // EECodeInfo::GetUnwindInfoHelper |
14309 | // |
14310 | // Description: |
14311 | // Simple helper to return a pointer to the UNWIND_INFO given the offset to the unwind info. |
14312 | // On DAC builds, this function will read the memory from the target process and create a host copy. |
14313 | // |
14314 | // Arguments: |
14315 | // * unwindInfoOffset - This is the offset to the unwind info, relative to the beginning of the code heap |
14316 | // for jitted code or to the module base for ngned code. this->GetModuleBase() will return the correct |
14317 | // module base. |
14318 | // |
14319 | // Return Value: |
14320 | // Return a pointer to the UNWIND_INFO. On DAC builds, this function will create a host copy of the |
14321 | // UNWIND_INFO and return a host pointer. It will correctly read all of the memory for the variable-sized |
14322 | // unwind info. |
14323 | // |
14324 | |
14325 | UNWIND_INFO * EECodeInfo::GetUnwindInfoHelper(ULONG unwindInfoOffset) |
14326 | { |
14327 | #if defined(DACCESS_COMPILE) |
14328 | return DacGetUnwindInfo(static_cast<TADDR>(this->GetModuleBase() + unwindInfoOffset)); |
14329 | #else // !DACCESS_COMPILE |
14330 | return reinterpret_cast<UNWIND_INFO *>(this->GetModuleBase() + unwindInfoOffset); |
14331 | #endif // !DACCESS_COMPILE |
14332 | } |
14333 | |
14334 | // ---------------------------------------------------------------------------- |
14335 | // EECodeInfo::GetFixedStackSize |
14336 | // |
14337 | // Description: |
14338 | // Return the fixed stack size of a specified managed method. This function DOES NOT take current control |
14339 | // PC into account. So the fixed stack size returned by this function is not valid in the prolog or |
14340 | // the epilog. |
14341 | // |
14342 | // Return Value: |
14343 | // Return the fixed stack size. |
14344 | // |
14345 | // Notes: |
14346 | // * For method with dynamic stack allocations, this function will return the fixed stack size on X64 (the |
14347 | // stack size immediately after the prolog), and it will return 0 on IA64. This difference is due to |
14348 | // the different unwind info encoding. |
14349 | // |
14350 | |
14351 | ULONG EECodeInfo::GetFixedStackSize() |
14352 | { |
14353 | WRAPPER_NO_CONTRACT; |
14354 | SUPPORTS_DAC; |
14355 | |
14356 | ULONG uFixedStackSize = 0; |
14357 | |
14358 | ULONG uDummy = 0; |
14359 | GetOffsetsFromUnwindInfo(&uFixedStackSize, &uDummy); |
14360 | |
14361 | return uFixedStackSize; |
14362 | } |
14363 | |
14364 | #define kRBP 5 |
14365 | // The information returned by this method is only valid if we are not in a prolog or an epilog. |
14366 | // Since this method is only used for the security stackwalk cache, this assumption is valid, since |
14367 | // we cannot make a call in a prolog or an epilog. |
14368 | // |
14369 | // The next assumption is that only rbp is used as a frame register in jitted code. There is an |
14370 | // assert below to guard this assumption. |
14371 | void EECodeInfo::GetOffsetsFromUnwindInfo(ULONG* pRSPOffset, ULONG* pRBPOffset) |
14372 | { |
14373 | LIMITED_METHOD_CONTRACT; |
14374 | SUPPORTS_DAC; |
14375 | |
14376 | _ASSERTE((pRSPOffset != NULL) && (pRBPOffset != NULL)); |
14377 | |
14378 | // moduleBase is a target address. |
14379 | TADDR moduleBase = GetModuleBase(); |
14380 | |
14381 | DWORD unwindInfo = RUNTIME_FUNCTION__GetUnwindInfoAddress(GetFunctionEntry()); |
14382 | |
14383 | if ((unwindInfo & RUNTIME_FUNCTION_INDIRECT) != 0) |
14384 | { |
14385 | unwindInfo = RUNTIME_FUNCTION__GetUnwindInfoAddress(PTR_RUNTIME_FUNCTION(moduleBase + (unwindInfo & ~RUNTIME_FUNCTION_INDIRECT))); |
14386 | } |
14387 | |
14388 | UNWIND_INFO * pInfo = GetUnwindInfoHelper(unwindInfo); |
14389 | if (pInfo->Flags & UNW_FLAG_CHAININFO) |
14390 | { |
14391 | _ASSERTE(!"GetRbpOffset() - chained unwind info used, violating assumptions of the security stackwalk cache" ); |
14392 | DebugBreak(); |
14393 | } |
14394 | |
14395 | // Either we are not using a frame pointer, or we are using rbp as the frame pointer. |
14396 | if ( (pInfo->FrameRegister != 0) && (pInfo->FrameRegister != kRBP) ) |
14397 | { |
14398 | _ASSERTE(!"GetRbpOffset() - non-RBP frame pointer used, violating assumptions of the security stackwalk cache" ); |
14399 | DebugBreak(); |
14400 | } |
14401 | |
14402 | // Walk the unwind info. |
14403 | ULONG StackOffset = 0; |
14404 | ULONG StackSize = 0; |
14405 | for (int i = 0; i < pInfo->CountOfUnwindCodes; i++) |
14406 | { |
14407 | ULONG UnwindOp = pInfo->UnwindCode[i].UnwindOp; |
14408 | ULONG OpInfo = pInfo->UnwindCode[i].OpInfo; |
14409 | |
14410 | if (UnwindOp == UWOP_SAVE_NONVOL) |
14411 | { |
14412 | if (OpInfo == kRBP) |
14413 | { |
14414 | StackOffset = pInfo->UnwindCode[i+1].FrameOffset * 8; |
14415 | } |
14416 | } |
14417 | else if (UnwindOp == UWOP_SAVE_NONVOL_FAR) |
14418 | { |
14419 | if (OpInfo == kRBP) |
14420 | { |
14421 | StackOffset = pInfo->UnwindCode[i + 1].FrameOffset; |
14422 | StackOffset += (pInfo->UnwindCode[i + 2].FrameOffset << 16); |
14423 | } |
14424 | } |
14425 | else if (UnwindOp == UWOP_ALLOC_SMALL) |
14426 | { |
14427 | StackSize += (OpInfo * 8) + 8; |
14428 | } |
14429 | else if (UnwindOp == UWOP_ALLOC_LARGE) |
14430 | { |
14431 | ULONG IncrementalStackSize = pInfo->UnwindCode[i + 1].FrameOffset; |
14432 | if (OpInfo == 0) |
14433 | { |
14434 | IncrementalStackSize *= 8; |
14435 | } |
14436 | else |
14437 | { |
14438 | IncrementalStackSize += (pInfo->UnwindCode[i + 2].FrameOffset << 16); |
14439 | |
14440 | // This is a special opcode. We need to increment the index by 1 in addition to the normal adjustments. |
14441 | i += 1; |
14442 | } |
14443 | StackSize += IncrementalStackSize; |
14444 | } |
14445 | else if (UnwindOp == UWOP_PUSH_NONVOL) |
14446 | { |
14447 | // Because of constraints on epilogs, this unwind opcode is always last in the unwind code array. |
14448 | // This means that StackSize has been initialized already when we first see this unwind opcode. |
14449 | // Note that the intial value of StackSize does not include the stack space used for pushes. |
14450 | // Thus, here we only need to increment StackSize 8 bytes at a time until we see the unwind code for "push rbp". |
14451 | if (OpInfo == kRBP) |
14452 | { |
14453 | StackOffset = StackSize; |
14454 | } |
14455 | |
14456 | StackSize += 8; |
14457 | } |
14458 | |
14459 | // Adjust the index into the unwind code array. |
14460 | i += UnwindOpExtraSlotTable[UnwindOp]; |
14461 | } |
14462 | |
14463 | *pRSPOffset = StackSize + 8; // add 8 for the return address |
14464 | *pRBPOffset = StackOffset; |
14465 | } |
14466 | #undef kRBP |
14467 | |
14468 | |
14469 | #if defined(_DEBUG) && defined(HAVE_GCCOVER) |
14470 | |
14471 | LPVOID EECodeInfo::findNextFunclet (LPVOID pvFuncletStart, SIZE_T cbCode, LPVOID *ppvFuncletEnd) |
14472 | { |
14473 | CONTRACTL { |
14474 | NOTHROW; |
14475 | GC_NOTRIGGER; |
14476 | } CONTRACTL_END; |
14477 | |
14478 | while (cbCode > 0) |
14479 | { |
14480 | PT_RUNTIME_FUNCTION pFunctionEntry; |
14481 | ULONGLONG uImageBase; |
14482 | #ifdef FEATURE_PAL |
14483 | EECodeInfo codeInfo; |
14484 | codeInfo.Init((PCODE)pvFuncletStart); |
14485 | pFunctionEntry = codeInfo.GetFunctionEntry(); |
14486 | uImageBase = (ULONGLONG)codeInfo.GetModuleBase(); |
14487 | #else // !FEATURE_PAL |
14488 | // |
14489 | // This is GCStress debug only - use the slow OS APIs to enumerate funclets |
14490 | // |
14491 | |
14492 | pFunctionEntry = (PT_RUNTIME_FUNCTION) RtlLookupFunctionEntry((ULONGLONG)pvFuncletStart, |
14493 | &uImageBase |
14494 | AMD64_ARG(NULL) |
14495 | ); |
14496 | #endif |
14497 | |
14498 | if (pFunctionEntry != NULL) |
14499 | { |
14500 | #ifdef FEATURE_PREJIT |
14501 | // workaround: Check for indirect entry that is generated for cold part of main method body. |
14502 | if ((TADDR)pvFuncletStart < (TADDR)uImageBase + pFunctionEntry->BeginAddress || |
14503 | (TADDR)uImageBase + pFunctionEntry->EndAddress <= (TADDR)pvFuncletStart) |
14504 | { |
14505 | Module * pZapModule = ExecutionManager::FindZapModule((TADDR)pvFuncletStart); |
14506 | NGenLayoutInfo * pLayoutInfo = pZapModule->GetNGenLayoutInfo(); |
14507 | |
14508 | int ColdFunctionIndex = NativeUnwindInfoLookupTable::LookupUnwindInfoForMethod((DWORD)((TADDR)pvFuncletStart - uImageBase), |
14509 | pLayoutInfo->m_pRuntimeFunctions[2], |
14510 | 0, pLayoutInfo->m_nRuntimeFunctions[2] - 1); |
14511 | |
14512 | pFunctionEntry = pLayoutInfo->m_pRuntimeFunctions[2] + ColdFunctionIndex; |
14513 | } |
14514 | #endif |
14515 | |
14516 | _ASSERTE((TADDR)pvFuncletStart == (TADDR)uImageBase + pFunctionEntry->BeginAddress); |
14517 | _ASSERTE((TADDR)uImageBase + pFunctionEntry->EndAddress <= (TADDR)pvFuncletStart + cbCode); |
14518 | *ppvFuncletEnd = (LPVOID)(uImageBase + pFunctionEntry->EndAddress); |
14519 | return (LPVOID)(uImageBase + pFunctionEntry->BeginAddress); |
14520 | } |
14521 | |
14522 | pvFuncletStart = (LPVOID)((TADDR)pvFuncletStart + 1); |
14523 | cbCode--; |
14524 | } |
14525 | |
14526 | return NULL; |
14527 | } |
14528 | #endif // defined(_DEBUG) && !defined(HAVE_GCCOVER) |
14529 | #endif // defined(_TARGET_AMD64_) |
14530 | |