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
6
7#include "common.h"
8#include "jitinterface.h"
9#include "codeman.h"
10#include "method.hpp"
11#include "class.h"
12#include "object.h"
13#include "field.h"
14#include "stublink.h"
15#include "virtualcallstub.h"
16#include "corjit.h"
17#include "eeconfig.h"
18#include "excep.h"
19#include "log.h"
20#include "excep.h"
21#include "float.h" // for isnan
22#include "dbginterface.h"
23#include "dllimport.h"
24#include "gcheaputilities.h"
25#include "comdelegate.h"
26#include "jitperf.h" // to track jit perf
27#include "corprof.h"
28#include "eeprofinterfaces.h"
29
30#ifndef FEATURE_PAL
31// Included for referencing __report_gsfailure
32#include "process.h"
33#endif // !FEATURE_PAL
34
35#include "perfcounters.h"
36#ifdef PROFILING_SUPPORTED
37#include "proftoeeinterfaceimpl.h"
38#endif
39#include "ecall.h"
40#include "generics.h"
41#include "typestring.h"
42#include "stackprobe.h"
43#include "typedesc.h"
44#include "genericdict.h"
45#include "array.h"
46#include "debuginfostore.h"
47#include "safemath.h"
48#include "threadstatics.h"
49
50#ifdef FEATURE_PREJIT
51#include "compile.h"
52#endif
53
54#ifdef HAVE_GCCOVER
55#include "gccover.h"
56#endif // HAVE_GCCOVER
57
58#include "runtimehandles.h"
59
60//========================================================================
61//
62// This file contains implementation of all JIT helpers. The helpers are
63// divided into following categories:
64//
65// INTEGER ARITHMETIC HELPERS
66// FLOATING POINT HELPERS
67// INSTANCE FIELD HELPERS
68// STATIC FIELD HELPERS
69// SHARED STATIC FIELD HELPERS
70// CASTING HELPERS
71// ALLOCATION HELPERS
72// STRING HELPERS
73// ARRAY HELPERS
74// VALUETYPE/BYREF HELPERS
75// GENERICS HELPERS
76// EXCEPTION HELPERS
77// SECURITY HELPERS
78// DEBUGGER/PROFILER HELPERS
79// GC HELPERS
80// INTEROP HELPERS
81//
82//========================================================================
83
84
85
86//========================================================================
87//
88// INTEGER ARITHMETIC HELPERS
89//
90//========================================================================
91
92#include <optsmallperfcritical.h>
93
94//
95// helper macro to multiply two 32-bit uints
96//
97#define Mul32x32To64(a, b) ((UINT64)((UINT32)(a)) * (UINT64)((UINT32)(b)))
98
99//
100// helper macro to get high 32-bit of 64-bit int
101//
102#define Hi32Bits(a) ((UINT32)((UINT64)(a) >> 32))
103
104//
105// helper macro to check whether 64-bit signed int fits into 32-bit signed (compiles into one 32-bit compare)
106//
107#define Is32BitSigned(a) (Hi32Bits(a) == Hi32Bits((INT64)(INT32)(a)))
108
109//
110// helper function to shift the result by 32-bits
111//
112inline UINT64 ShiftToHi32Bits(UINT32 x)
113{
114 // The shift compiles into slow multiplication by 2^32! VSWhidbey 360736
115 // return ((UINT64)x) << 32;
116
117 ULARGE_INTEGER ret;
118 ret.u.HighPart = x;
119 ret.u.LowPart = 0;
120 return ret.QuadPart;
121}
122
123#if !defined(_TARGET_X86_) || defined(FEATURE_PAL)
124/*********************************************************************/
125HCIMPL2_VV(INT64, JIT_LMul, INT64 val1, INT64 val2)
126{
127 FCALL_CONTRACT;
128
129 UINT32 val1High = Hi32Bits(val1);
130 UINT32 val2High = Hi32Bits(val2);
131
132 if ((val1High == 0) && (val2High == 0))
133 return Mul32x32To64(val1, val2);
134
135 return (val1 * val2);
136}
137HCIMPLEND
138#endif // !_TARGET_X86_ || FEATURE_PAL
139
140/*********************************************************************/
141HCIMPL2_VV(INT64, JIT_LMulOvf, INT64 val1, INT64 val2)
142{
143 FCALL_CONTRACT;
144
145 // This short-cut does not actually help since the multiplication
146 // of two 32-bit signed ints compiles into the call to a slow helper
147 // if (Is32BitSigned(val1) && Is32BitSigned(val2))
148 // return (INT64)(INT32)val1 * (INT64)(INT32)val2;
149
150 INDEBUG(INT64 expected = val1 * val2;)
151 INT64 ret;
152
153 // Remember the sign of the result
154 INT32 sign = Hi32Bits(val1) ^ Hi32Bits(val2);
155
156 // Convert to unsigned multiplication
157 if (val1 < 0) val1 = -val1;
158 if (val2 < 0) val2 = -val2;
159
160 // Get the upper 32 bits of the numbers
161 UINT32 val1High = Hi32Bits(val1);
162 UINT32 val2High = Hi32Bits(val2);
163
164 UINT64 valMid;
165
166 if (val1High == 0) {
167 // Compute the 'middle' bits of the long multiplication
168 valMid = Mul32x32To64(val2High, val1);
169 }
170 else {
171 if (val2High != 0)
172 goto ThrowExcep;
173 // Compute the 'middle' bits of the long multiplication
174 valMid = Mul32x32To64(val1High, val2);
175 }
176
177 // See if any bits after bit 32 are set
178 if (Hi32Bits(valMid) != 0)
179 goto ThrowExcep;
180
181 ret = Mul32x32To64(val1, val2) + ShiftToHi32Bits((UINT32)(valMid));
182
183 // check for overflow
184 if (Hi32Bits(ret) < (UINT32)valMid)
185 goto ThrowExcep;
186
187 if (sign >= 0) {
188 // have we spilled into the sign bit?
189 if (ret < 0)
190 goto ThrowExcep;
191 }
192 else {
193 ret = -ret;
194 // have we spilled into the sign bit?
195 if (ret > 0)
196 goto ThrowExcep;
197 }
198 _ASSERTE(ret == expected);
199 return ret;
200
201ThrowExcep:
202 FCThrow(kOverflowException);
203}
204HCIMPLEND
205
206/*********************************************************************/
207HCIMPL2_VV(UINT64, JIT_ULMulOvf, UINT64 val1, UINT64 val2)
208{
209 FCALL_CONTRACT;
210
211 INDEBUG(UINT64 expected = val1 * val2;)
212 UINT64 ret;
213
214 // Get the upper 32 bits of the numbers
215 UINT32 val1High = Hi32Bits(val1);
216 UINT32 val2High = Hi32Bits(val2);
217
218 UINT64 valMid;
219
220 if (val1High == 0) {
221 if (val2High == 0)
222 return Mul32x32To64(val1, val2);
223 // Compute the 'middle' bits of the long multiplication
224 valMid = Mul32x32To64(val2High, val1);
225 }
226 else {
227 if (val2High != 0)
228 goto ThrowExcep;
229 // Compute the 'middle' bits of the long multiplication
230 valMid = Mul32x32To64(val1High, val2);
231 }
232
233 // See if any bits after bit 32 are set
234 if (Hi32Bits(valMid) != 0)
235 goto ThrowExcep;
236
237 ret = Mul32x32To64(val1, val2) + ShiftToHi32Bits((UINT32)(valMid));
238
239 // check for overflow
240 if (Hi32Bits(ret) < (UINT32)valMid)
241 goto ThrowExcep;
242
243 _ASSERTE(ret == expected);
244 return ret;
245
246ThrowExcep:
247 FCThrow(kOverflowException);
248 }
249HCIMPLEND
250
251/*********************************************************************/
252HCIMPL2(INT32, JIT_Div, INT32 dividend, INT32 divisor)
253{
254 FCALL_CONTRACT;
255
256 RuntimeExceptionKind ehKind;
257
258 if (((UINT32) (divisor + 1)) <= 1) // Unsigned test for divisor in [-1 .. 0]
259 {
260 if (divisor == 0)
261 {
262 ehKind = kDivideByZeroException;
263 goto ThrowExcep;
264 }
265 else if (divisor == -1)
266 {
267 if (dividend == _I32_MIN)
268 {
269 ehKind = kOverflowException;
270 goto ThrowExcep;
271 }
272 return -dividend;
273 }
274 }
275
276 return(dividend / divisor);
277
278ThrowExcep:
279 FCThrow(ehKind);
280}
281HCIMPLEND
282
283/*********************************************************************/
284HCIMPL2(INT32, JIT_Mod, INT32 dividend, INT32 divisor)
285{
286 FCALL_CONTRACT;
287
288 RuntimeExceptionKind ehKind;
289
290 if (((UINT32) (divisor + 1)) <= 1) // Unsigned test for divisor in [-1 .. 0]
291 {
292 if (divisor == 0)
293 {
294 ehKind = kDivideByZeroException;
295 goto ThrowExcep;
296 }
297 else if (divisor == -1)
298 {
299 if (dividend == _I32_MIN)
300 {
301 ehKind = kOverflowException;
302 goto ThrowExcep;
303 }
304 return 0;
305 }
306 }
307
308 return(dividend % divisor);
309
310ThrowExcep:
311 FCThrow(ehKind);
312}
313HCIMPLEND
314
315/*********************************************************************/
316HCIMPL2(UINT32, JIT_UDiv, UINT32 dividend, UINT32 divisor)
317{
318 FCALL_CONTRACT;
319
320 if (divisor == 0)
321 FCThrow(kDivideByZeroException);
322
323 return(dividend / divisor);
324}
325HCIMPLEND
326
327/*********************************************************************/
328HCIMPL2(UINT32, JIT_UMod, UINT32 dividend, UINT32 divisor)
329{
330 FCALL_CONTRACT;
331
332 if (divisor == 0)
333 FCThrow(kDivideByZeroException);
334
335 return(dividend % divisor);
336}
337HCIMPLEND
338
339/*********************************************************************/
340HCIMPL2_VV(INT64, JIT_LDiv, INT64 dividend, INT64 divisor)
341{
342 FCALL_CONTRACT;
343
344 RuntimeExceptionKind ehKind;
345
346 if (Is32BitSigned(divisor))
347 {
348 if ((INT32)divisor == 0)
349 {
350 ehKind = kDivideByZeroException;
351 goto ThrowExcep;
352 }
353
354 if ((INT32)divisor == -1)
355 {
356 if ((UINT64) dividend == UI64(0x8000000000000000))
357 {
358 ehKind = kOverflowException;
359 goto ThrowExcep;
360 }
361 return -dividend;
362 }
363
364 // Check for -ive or +ive numbers in the range -2**31 to 2**31
365 if (Is32BitSigned(dividend))
366 return((INT32)dividend / (INT32)divisor);
367 }
368
369 // For all other combinations fallback to int64 div.
370 return(dividend / divisor);
371
372ThrowExcep:
373 FCThrow(ehKind);
374}
375HCIMPLEND
376
377/*********************************************************************/
378HCIMPL2_VV(INT64, JIT_LMod, INT64 dividend, INT64 divisor)
379{
380 FCALL_CONTRACT;
381
382 RuntimeExceptionKind ehKind;
383
384 if (Is32BitSigned(divisor))
385 {
386 if ((INT32)divisor == 0)
387 {
388 ehKind = kDivideByZeroException;
389 goto ThrowExcep;
390 }
391
392 if ((INT32)divisor == -1)
393 {
394 // <TODO>TODO, we really should remove this as it lengthens the code path
395 // and the spec really says that it should not throw an exception. </TODO>
396 if ((UINT64) dividend == UI64(0x8000000000000000))
397 {
398 ehKind = kOverflowException;
399 goto ThrowExcep;
400 }
401 return 0;
402 }
403
404 // Check for -ive or +ive numbers in the range -2**31 to 2**31
405 if (Is32BitSigned(dividend))
406 return((INT32)dividend % (INT32)divisor);
407 }
408
409 // For all other combinations fallback to int64 div.
410 return(dividend % divisor);
411
412ThrowExcep:
413 FCThrow(ehKind);
414}
415HCIMPLEND
416
417/*********************************************************************/
418HCIMPL2_VV(UINT64, JIT_ULDiv, UINT64 dividend, UINT64 divisor)
419{
420 FCALL_CONTRACT;
421
422 if (Hi32Bits(divisor) == 0)
423 {
424 if ((UINT32)(divisor) == 0)
425 FCThrow(kDivideByZeroException);
426
427 if (Hi32Bits(dividend) == 0)
428 return((UINT32)dividend / (UINT32)divisor);
429 }
430
431 return(dividend / divisor);
432}
433HCIMPLEND
434
435/*********************************************************************/
436HCIMPL2_VV(UINT64, JIT_ULMod, UINT64 dividend, UINT64 divisor)
437{
438 FCALL_CONTRACT;
439
440 if (Hi32Bits(divisor) == 0)
441 {
442 if ((UINT32)(divisor) == 0)
443 FCThrow(kDivideByZeroException);
444
445 if (Hi32Bits(dividend) == 0)
446 return((UINT32)dividend % (UINT32)divisor);
447 }
448
449 return(dividend % divisor);
450}
451HCIMPLEND
452
453#if !defined(BIT64) && !defined(_TARGET_X86_)
454/*********************************************************************/
455HCIMPL2_VV(UINT64, JIT_LLsh, UINT64 num, int shift)
456{
457 FCALL_CONTRACT;
458 return num << (shift & 0x3F);
459}
460HCIMPLEND
461
462/*********************************************************************/
463HCIMPL2_VV(INT64, JIT_LRsh, INT64 num, int shift)
464{
465 FCALL_CONTRACT;
466 return num >> (shift & 0x3F);
467}
468HCIMPLEND
469
470/*********************************************************************/
471HCIMPL2_VV(UINT64, JIT_LRsz, UINT64 num, int shift)
472{
473 FCALL_CONTRACT;
474 return num >> (shift & 0x3F);
475}
476HCIMPLEND
477#endif // !BIT64 && !_TARGET_X86_
478
479#include <optdefault.h>
480
481
482//========================================================================
483//
484// FLOATING POINT HELPERS
485//
486//========================================================================
487
488#include <optsmallperfcritical.h>
489
490/*********************************************************************/
491//
492HCIMPL1_V(double, JIT_ULng2Dbl, UINT64 val)
493{
494 FCALL_CONTRACT;
495
496 double conv = (double) ((INT64) val);
497 if (conv < 0)
498 conv += (4294967296.0 * 4294967296.0); // add 2^64
499 _ASSERTE(conv >= 0);
500 return(conv);
501}
502HCIMPLEND
503
504/*********************************************************************/
505// needed for ARM and RyuJIT-x86
506HCIMPL1_V(double, JIT_Lng2Dbl, INT64 val)
507{
508 FCALL_CONTRACT;
509 return double(val);
510}
511HCIMPLEND
512
513//--------------------------------------------------------------------------
514template <class ftype>
515ftype modftype(ftype value, ftype *iptr);
516template <> float modftype(float value, float *iptr) { return modff(value, iptr); }
517template <> double modftype(double value, double *iptr) { return modf(value, iptr); }
518
519// round to nearest, round to even if tied
520template <class ftype>
521ftype BankersRound(ftype value)
522{
523 if (value < 0.0) return -BankersRound <ftype> (-value);
524
525 ftype integerPart;
526 modftype( value, &integerPart );
527
528 // if decimal part is exactly .5
529 if ((value -(integerPart +0.5)) == 0.0)
530 {
531 // round to even
532#if defined(_TARGET_ARM_) && defined(FEATURE_CORESYSTEM)
533 // @ARMTODO: On ARM when building on CoreSystem (where we link against the system CRT) an attempt to
534 // use fmod(float, float) fails to link (apparently this is converted to a reference to fmodf, which
535 // is not included in the system CRT). Use the double version instead.
536 if (fmod(double(integerPart), double(2.0)) == 0.0)
537 return integerPart;
538#else
539 if (fmod(ftype(integerPart), ftype(2.0)) == 0.0)
540 return integerPart;
541#endif
542
543 // Else return the nearest even integer
544 return (ftype)_copysign(ceil(fabs(value+0.5)),
545 value);
546 }
547
548 // Otherwise round to closest
549 return (ftype)_copysign(floor(fabs(value)+0.5),
550 value);
551}
552
553
554/*********************************************************************/
555// round double to nearest int (as double)
556HCIMPL1_V(double, JIT_DoubleRound, double val)
557{
558 FCALL_CONTRACT;
559 return BankersRound(val);
560}
561HCIMPLEND
562
563/*********************************************************************/
564// round float to nearest int (as float)
565HCIMPL1_V(float, JIT_FloatRound, float val)
566{
567 FCALL_CONTRACT;
568 return BankersRound(val);
569}
570HCIMPLEND
571
572/*********************************************************************/
573// Call fast Dbl2Lng conversion - used by functions below
574FORCEINLINE INT64 FastDbl2Lng(double val)
575{
576#ifdef _TARGET_X86_
577 FCALL_CONTRACT;
578 return HCCALL1_V(JIT_Dbl2Lng, val);
579#else
580 FCALL_CONTRACT;
581 return((__int64) val);
582#endif
583}
584
585/*********************************************************************/
586HCIMPL1_V(UINT32, JIT_Dbl2UIntOvf, double val)
587{
588 FCALL_CONTRACT;
589
590 // Note that this expression also works properly for val = NaN case
591 if (val > -1.0 && val < 4294967296.0)
592 return((UINT32)FastDbl2Lng(val));
593
594 FCThrow(kOverflowException);
595}
596HCIMPLEND
597
598/*********************************************************************/
599HCIMPL1_V(UINT64, JIT_Dbl2ULng, double val)
600{
601 FCALL_CONTRACT;
602
603 const double two63 = 2147483648.0 * 4294967296.0;
604 UINT64 ret;
605 if (val < two63) {
606 ret = FastDbl2Lng(val);
607 }
608 else {
609 // subtract 0x8000000000000000, do the convert then add it back again
610 ret = FastDbl2Lng(val - two63) + I64(0x8000000000000000);
611 }
612 return ret;
613}
614HCIMPLEND
615
616/*********************************************************************/
617HCIMPL1_V(UINT64, JIT_Dbl2ULngOvf, double val)
618{
619 FCALL_CONTRACT;
620
621 const double two64 = 4294967296.0 * 4294967296.0;
622 // Note that this expression also works properly for val = NaN case
623 if (val > -1.0 && val < two64) {
624 const double two63 = 2147483648.0 * 4294967296.0;
625 UINT64 ret;
626 if (val < two63) {
627 ret = FastDbl2Lng(val);
628 }
629 else {
630 // subtract 0x8000000000000000, do the convert then add it back again
631 ret = FastDbl2Lng(val - two63) + I64(0x8000000000000000);
632 }
633#ifdef _DEBUG
634 // since no overflow can occur, the value always has to be within 1
635 double roundTripVal = HCCALL1_V(JIT_ULng2Dbl, ret);
636 _ASSERTE(val - 1.0 <= roundTripVal && roundTripVal <= val + 1.0);
637#endif // _DEBUG
638 return ret;
639 }
640
641 FCThrow(kOverflowException);
642}
643HCIMPLEND
644
645
646#if !defined(_TARGET_X86_) || defined(FEATURE_PAL)
647
648HCIMPL1_V(INT64, JIT_Dbl2Lng, double val)
649{
650 FCALL_CONTRACT;
651
652 return((INT64)val);
653}
654HCIMPLEND
655
656HCIMPL1_V(int, JIT_Dbl2IntOvf, double val)
657{
658 FCALL_CONTRACT;
659
660 const double two31 = 2147483648.0;
661
662 // Note that this expression also works properly for val = NaN case
663 if (val > -two31 - 1 && val < two31)
664 return((INT32)val);
665
666 FCThrow(kOverflowException);
667}
668HCIMPLEND
669
670HCIMPL1_V(INT64, JIT_Dbl2LngOvf, double val)
671{
672 FCALL_CONTRACT;
673
674 const double two63 = 2147483648.0 * 4294967296.0;
675
676 // Note that this expression also works properly for val = NaN case
677 // We need to compare with the very next double to two63. 0x402 is epsilon to get us there.
678 if (val > -two63 - 0x402 && val < two63)
679 return((INT64)val);
680
681 FCThrow(kOverflowException);
682}
683HCIMPLEND
684
685HCIMPL2_VV(float, JIT_FltRem, float dividend, float divisor)
686{
687 FCALL_CONTRACT;
688
689 //
690 // From the ECMA standard:
691 //
692 // If [divisor] is zero or [dividend] is infinity
693 // the result is NaN.
694 // If [divisor] is infinity,
695 // the result is [dividend] (negated for -infinity***).
696 //
697 // ***"negated for -infinity" has been removed from the spec
698 //
699
700 if (divisor==0 || !_finite(dividend))
701 {
702 UINT32 NaN = CLR_NAN_32;
703 return *(float *)(&NaN);
704 }
705 else if (!_finite(divisor) && !_isnan(divisor))
706 {
707 return dividend;
708 }
709 // else...
710#if 0
711 // COMPILER BUG WITH FMODF() + /Oi, USE FMOD() INSTEAD
712 return fmodf(dividend,divisor);
713#else
714 return (float)fmod((double)dividend,(double)divisor);
715#endif
716}
717HCIMPLEND
718
719HCIMPL2_VV(double, JIT_DblRem, double dividend, double divisor)
720{
721 FCALL_CONTRACT;
722
723 //
724 // From the ECMA standard:
725 //
726 // If [divisor] is zero or [dividend] is infinity
727 // the result is NaN.
728 // If [divisor] is infinity,
729 // the result is [dividend] (negated for -infinity***).
730 //
731 // ***"negated for -infinity" has been removed from the spec
732 //
733 if (divisor==0 || !_finite(dividend))
734 {
735 UINT64 NaN = CLR_NAN_64;
736 return *(double *)(&NaN);
737 }
738 else if (!_finite(divisor) && !_isnan(divisor))
739 {
740 return dividend;
741 }
742 // else...
743 return(fmod(dividend,divisor));
744}
745HCIMPLEND
746
747#endif // !_TARGET_X86_ || FEATURE_PAL
748
749#include <optdefault.h>
750
751
752//========================================================================
753//
754// INSTANCE FIELD HELPERS
755//
756//========================================================================
757
758/*********************************************************************/
759// Returns the address of the field in the object (This is an interior
760// pointer and the caller has to use it appropriately). obj can be
761// either a reference or a byref
762HCIMPL2(void*, JIT_GetFieldAddr_Framed, Object *obj, FieldDesc* pFD)
763{
764 CONTRACTL {
765 FCALL_CHECK;
766 PRECONDITION(CheckPointer(pFD));
767 } CONTRACTL_END;
768
769 void * fldAddr = NULL;
770 OBJECTREF objRef = ObjectToOBJECTREF(obj);
771
772 HELPER_METHOD_FRAME_BEGIN_RET_1(objRef);
773
774 if (objRef == NULL)
775 COMPlusThrow(kNullReferenceException);
776
777
778 fldAddr = pFD->GetAddress(OBJECTREFToObject(objRef));
779
780 HELPER_METHOD_FRAME_END();
781
782 return fldAddr;
783}
784HCIMPLEND
785
786#include <optsmallperfcritical.h>
787HCIMPL2(void*, JIT_GetFieldAddr, Object *obj, FieldDesc* pFD)
788{
789 CONTRACTL {
790 FCALL_CHECK;
791 PRECONDITION(CheckPointer(pFD));
792 } CONTRACTL_END;
793
794 if (obj == NULL || g_IBCLogger.InstrEnabled() || pFD->IsEnCNew())
795 {
796 ENDFORBIDGC();
797 return HCCALL2(JIT_GetFieldAddr_Framed, obj, pFD);
798 }
799
800 return pFD->GetAddressGuaranteedInHeap(obj);
801}
802HCIMPLEND
803#include <optdefault.h>
804
805/*********************************************************************/
806#define HCallAssert(cache, target) // suppressed to avoid ambiguous cast errors caused by use of template
807template <typename FIELDTYPE>
808NOINLINE HCIMPL2(FIELDTYPE, JIT_GetField_Framed, Object *obj, FieldDesc *pFD)
809#undef HCallAssert
810{
811 FCALL_CONTRACT;
812
813 FIELDTYPE value = 0;
814
815 // This is an instance field helper
816 _ASSERTE(!pFD->IsStatic());
817
818 OBJECTREF objRef = ObjectToOBJECTREF(obj);
819
820 HELPER_METHOD_FRAME_BEGIN_RET_1(objRef);
821 if (objRef == NULL)
822 COMPlusThrow(kNullReferenceException);
823 pFD->GetInstanceField(objRef, &value);
824 HELPER_METHOD_POLL();
825 HELPER_METHOD_FRAME_END();
826
827 return value;
828}
829HCIMPLEND
830
831/*********************************************************************/
832#include <optsmallperfcritical.h>
833
834HCIMPL2(INT8, JIT_GetField8, Object *obj, FieldDesc *pFD)
835{
836 FCALL_CONTRACT;
837
838 if (obj == NULL || g_IBCLogger.InstrEnabled() || pFD->IsEnCNew())
839 {
840 ENDFORBIDGC();
841 return HCCALL2(JIT_GetField_Framed<INT8>, obj, pFD);
842 }
843
844 INT8 val = VolatileLoad<INT8>((INT8*)pFD->GetAddressGuaranteedInHeap(obj));
845 FC_GC_POLL_RET();
846 return val;
847}
848HCIMPLEND
849
850HCIMPL2(INT16, JIT_GetField16, Object *obj, FieldDesc *pFD)
851{
852 FCALL_CONTRACT;
853
854 if (obj == NULL || g_IBCLogger.InstrEnabled() || pFD->IsEnCNew())
855 {
856 ENDFORBIDGC();
857 return HCCALL2(JIT_GetField_Framed<INT16>, obj, pFD);
858 }
859
860 INT16 val = VolatileLoad<INT16>((INT16*)pFD->GetAddressGuaranteedInHeap(obj));
861 FC_GC_POLL_RET();
862 return val;
863}
864HCIMPLEND
865
866HCIMPL2(INT32, JIT_GetField32, Object *obj, FieldDesc *pFD)
867{
868 FCALL_CONTRACT;
869
870 if (obj == NULL || g_IBCLogger.InstrEnabled() || pFD->IsEnCNew())
871 {
872 ENDFORBIDGC();
873 return HCCALL2(JIT_GetField_Framed<INT32>, obj, pFD);
874 }
875
876 INT32 val = VolatileLoad<INT32>((INT32*)pFD->GetAddressGuaranteedInHeap(obj));
877 FC_GC_POLL_RET();
878 return val;
879}
880HCIMPLEND
881
882HCIMPL2(INT64, JIT_GetField64, Object *obj, FieldDesc *pFD)
883{
884 FCALL_CONTRACT;
885
886 if (obj == NULL || g_IBCLogger.InstrEnabled() || pFD->IsEnCNew())
887 {
888 ENDFORBIDGC();
889 return HCCALL2(JIT_GetField_Framed<INT64>, obj, pFD);
890 }
891
892 INT64 val = VolatileLoad<INT64>((INT64*)pFD->GetAddressGuaranteedInHeap(obj));
893 FC_GC_POLL_RET();
894 return val;
895}
896HCIMPLEND
897
898HCIMPL2(FLOAT, JIT_GetFieldFloat, Object *obj, FieldDesc *pFD)
899{
900 FCALL_CONTRACT;
901
902 if (obj == NULL || g_IBCLogger.InstrEnabled() || pFD->IsEnCNew())
903 {
904 ENDFORBIDGC();
905 return HCCALL2(JIT_GetField_Framed<FLOAT>, obj, pFD);
906 }
907
908 FLOAT val;
909 (INT32&)val = VolatileLoad<INT32>((INT32*)pFD->GetAddressGuaranteedInHeap(obj));
910 FC_GC_POLL_RET();
911 return val;
912}
913HCIMPLEND
914
915HCIMPL2(DOUBLE, JIT_GetFieldDouble, Object *obj, FieldDesc *pFD)
916{
917 FCALL_CONTRACT;
918
919 if (obj == NULL || g_IBCLogger.InstrEnabled() || pFD->IsEnCNew())
920 {
921 ENDFORBIDGC();
922 return HCCALL2(JIT_GetField_Framed<DOUBLE>, obj, pFD);
923 }
924
925 DOUBLE val;
926 (INT64&)val = VolatileLoad<INT64>((INT64*)pFD->GetAddressGuaranteedInHeap(obj));
927 FC_GC_POLL_RET();
928 return val;
929}
930HCIMPLEND
931
932#include <optdefault.h>
933
934/*********************************************************************/
935#define HCallAssert(cache, target) // suppressed to avoid ambiguous cast errors caused by use of template
936template <typename FIELDTYPE>
937NOINLINE HCIMPL3(VOID, JIT_SetField_Framed, Object *obj, FieldDesc* pFD, FIELDTYPE val)
938#undef HCallAssert
939{
940 FCALL_CONTRACT;
941
942 // This is an instance field helper
943 _ASSERTE(!pFD->IsStatic());
944
945 OBJECTREF objRef = ObjectToOBJECTREF(obj);
946
947 HELPER_METHOD_FRAME_BEGIN_1(objRef);
948 if (objRef == NULL)
949 COMPlusThrow(kNullReferenceException);
950 pFD->SetInstanceField(objRef, &val);
951 HELPER_METHOD_POLL();
952 HELPER_METHOD_FRAME_END();
953}
954HCIMPLEND
955
956/*********************************************************************/
957#include <optsmallperfcritical.h>
958
959HCIMPL3(VOID, JIT_SetField8, Object *obj, FieldDesc *pFD, INT8 val)
960{
961 FCALL_CONTRACT;
962
963 if (obj == NULL || g_IBCLogger.InstrEnabled() || pFD->IsEnCNew())
964 {
965 ENDFORBIDGC();
966 return HCCALL3(JIT_SetField_Framed<INT8>, obj, pFD, val);
967 }
968
969 VolatileStore<INT8>((INT8*)pFD->GetAddressGuaranteedInHeap(obj), val);
970 FC_GC_POLL();
971}
972HCIMPLEND
973
974HCIMPL3(VOID, JIT_SetField16, Object *obj, FieldDesc *pFD, INT16 val)
975{
976 FCALL_CONTRACT;
977
978 if (obj == NULL || g_IBCLogger.InstrEnabled() || pFD->IsEnCNew())
979 {
980 ENDFORBIDGC();
981 return HCCALL3(JIT_SetField_Framed<INT16>, obj, pFD, val);
982 }
983
984 VolatileStore<INT16>((INT16*)pFD->GetAddressGuaranteedInHeap(obj), val);
985 FC_GC_POLL();
986}
987HCIMPLEND
988
989HCIMPL3(VOID, JIT_SetField32, Object *obj, FieldDesc *pFD, INT32 val)
990{
991 FCALL_CONTRACT;
992
993 if (obj == NULL || g_IBCLogger.InstrEnabled() || pFD->IsEnCNew())
994 {
995 ENDFORBIDGC();
996 return HCCALL3(JIT_SetField_Framed<INT32>, obj, pFD, val);
997 }
998
999 VolatileStore<INT32>((INT32*)pFD->GetAddressGuaranteedInHeap(obj), val);
1000 FC_GC_POLL();
1001}
1002HCIMPLEND
1003
1004HCIMPL3(VOID, JIT_SetField64, Object *obj, FieldDesc *pFD, INT64 val)
1005{
1006 FCALL_CONTRACT;
1007
1008 if (obj == NULL || g_IBCLogger.InstrEnabled() || pFD->IsEnCNew())
1009 {
1010 ENDFORBIDGC();
1011 return HCCALL3(JIT_SetField_Framed<INT64>, obj, pFD, val);
1012 }
1013
1014 VolatileStore<INT64>((INT64*)pFD->GetAddressGuaranteedInHeap(obj), val);
1015 FC_GC_POLL();
1016}
1017HCIMPLEND
1018
1019HCIMPL3(VOID, JIT_SetFieldFloat, Object *obj, FieldDesc *pFD, FLOAT val)
1020{
1021 FCALL_CONTRACT;
1022
1023 if (obj == NULL || g_IBCLogger.InstrEnabled() || pFD->IsEnCNew())
1024 {
1025 ENDFORBIDGC();
1026 return HCCALL3(JIT_SetField_Framed<FLOAT>, obj, pFD, val);
1027 }
1028
1029 VolatileStore<INT32>((INT32*)pFD->GetAddressGuaranteedInHeap(obj), (INT32&)val);
1030 FC_GC_POLL();
1031}
1032HCIMPLEND
1033
1034HCIMPL3(VOID, JIT_SetFieldDouble, Object *obj, FieldDesc *pFD, DOUBLE val)
1035{
1036 FCALL_CONTRACT;
1037
1038 if (obj == NULL || g_IBCLogger.InstrEnabled() || pFD->IsEnCNew())
1039 {
1040 ENDFORBIDGC();
1041 return HCCALL3(JIT_SetField_Framed<DOUBLE>, obj, pFD, val);
1042 }
1043
1044 VolatileStore<INT64>((INT64*)pFD->GetAddressGuaranteedInHeap(obj), (INT64&)val);
1045 FC_GC_POLL();
1046}
1047HCIMPLEND
1048
1049#include <optdefault.h>
1050
1051/*********************************************************************/
1052HCIMPL2(Object*, JIT_GetFieldObj_Framed, Object *obj, FieldDesc *pFD)
1053{
1054 CONTRACTL {
1055 FCALL_CHECK;
1056 PRECONDITION(!pFD->IsStatic());
1057 PRECONDITION(!pFD->IsPrimitive() && !pFD->IsByValue()); // Assert that we are called only for objects
1058 } CONTRACTL_END;
1059
1060 OBJECTREF objRef = ObjectToOBJECTREF(obj);
1061 OBJECTREF val = NULL;
1062
1063 HELPER_METHOD_FRAME_BEGIN_RET_2(objRef, val); // Set up a frame
1064 if (objRef == NULL)
1065 COMPlusThrow(kNullReferenceException);
1066 pFD->GetInstanceField(objRef, &val);
1067 HELPER_METHOD_POLL();
1068 HELPER_METHOD_FRAME_END();
1069
1070 return OBJECTREFToObject(val);
1071}
1072HCIMPLEND
1073
1074#include <optsmallperfcritical.h>
1075HCIMPL2(Object*, JIT_GetFieldObj, Object *obj, FieldDesc *pFD)
1076{
1077 CONTRACTL {
1078 FCALL_CHECK;
1079 PRECONDITION(!pFD->IsStatic());
1080 PRECONDITION(!pFD->IsPrimitive() && !pFD->IsByValue()); // Assert that we are called only for objects
1081 } CONTRACTL_END;
1082
1083 if (obj == NULL || g_IBCLogger.InstrEnabled() || pFD->IsEnCNew())
1084 {
1085 ENDFORBIDGC();
1086 return HCCALL2(JIT_GetFieldObj_Framed, obj, pFD);
1087 }
1088
1089 void * address = pFD->GetAddressGuaranteedInHeap(obj);
1090 OBJECTREF val = ObjectToOBJECTREF(VolatileLoad((Object **)address));
1091
1092 FC_GC_POLL_AND_RETURN_OBJREF(val);
1093}
1094HCIMPLEND
1095#include <optdefault.h>
1096
1097/*********************************************************************/
1098HCIMPL3(VOID, JIT_SetFieldObj_Framed, Object *obj, FieldDesc *pFD, Object *value)
1099{
1100 CONTRACTL {
1101 FCALL_CHECK;
1102 PRECONDITION(!pFD->IsStatic());
1103 PRECONDITION(!pFD->IsPrimitive() && !pFD->IsByValue()); // Assert that we are called only for objects
1104 } CONTRACTL_END;
1105
1106 OBJECTREF objRef = ObjectToOBJECTREF(obj);
1107 OBJECTREF val = ObjectToOBJECTREF(value);
1108
1109 HELPER_METHOD_FRAME_BEGIN_2(objRef, val);
1110 if (objRef == NULL)
1111 COMPlusThrow(kNullReferenceException);
1112 pFD->SetInstanceField(objRef, &val);
1113 HELPER_METHOD_POLL();
1114 HELPER_METHOD_FRAME_END();
1115}
1116HCIMPLEND
1117
1118#include <optsmallperfcritical.h>
1119HCIMPL3(VOID, JIT_SetFieldObj, Object *obj, FieldDesc *pFD, Object *value)
1120{
1121 CONTRACTL {
1122 FCALL_CHECK;
1123 PRECONDITION(!pFD->IsStatic());
1124 PRECONDITION(!pFD->IsPrimitive() && !pFD->IsByValue()); // Assert that we are called only for objects
1125 } CONTRACTL_END;
1126
1127 if (obj == NULL || g_IBCLogger.InstrEnabled() || pFD->IsEnCNew())
1128 {
1129 ENDFORBIDGC();
1130 return HCCALL3(JIT_SetFieldObj_Framed, obj, pFD, value);
1131 }
1132
1133 void * address = pFD->GetAddressGuaranteedInHeap(obj);
1134 SetObjectReference((OBJECTREF*)address, ObjectToOBJECTREF(value), GetAppDomain());
1135 FC_GC_POLL();
1136}
1137HCIMPLEND
1138#include <optdefault.h>
1139
1140/*********************************************************************/
1141HCIMPL4(VOID, JIT_GetFieldStruct_Framed, LPVOID retBuff, Object *obj, FieldDesc *pFD, MethodTable *pFieldMT)
1142{
1143 FCALL_CONTRACT;
1144
1145 // This may be a cross context field access. Setup a frame as we will
1146 // transition to managed code later
1147
1148 // This is an instance field helper
1149 _ASSERTE(!pFD->IsStatic());
1150
1151 // Assert that we are not called for objects or primitive types
1152 _ASSERTE(!pFD->IsPrimitive());
1153
1154 OBJECTREF objRef = ObjectToOBJECTREF(obj);
1155
1156 HELPER_METHOD_FRAME_BEGIN_1(objRef); // Set up a frame
1157
1158 if (objRef == NULL)
1159 COMPlusThrow(kNullReferenceException);
1160
1161 // Try an unwrap operation in case that we are not being called
1162 // in the same context as the server.
1163 // If that is the case then GetObjectFromProxy will return
1164 // the server object.
1165 BOOL fRemoted = FALSE;
1166
1167
1168 if (!fRemoted)
1169 {
1170 void * pAddr = pFD->GetAddress(OBJECTREFToObject(objRef));
1171 CopyValueClass(retBuff, pAddr, pFieldMT, objRef->GetAppDomain());
1172 }
1173
1174 HELPER_METHOD_FRAME_END(); // Tear down the frame
1175}
1176HCIMPLEND
1177
1178#include <optsmallperfcritical.h>
1179HCIMPL4(VOID, JIT_GetFieldStruct, LPVOID retBuff, Object *obj, FieldDesc *pFD, MethodTable *pFieldMT)
1180{
1181 FCALL_CONTRACT;
1182
1183 _ASSERTE(pFieldMT->IsValueType());
1184
1185 if (obj == NULL || g_IBCLogger.InstrEnabled() || pFD->IsEnCNew())
1186 {
1187 ENDFORBIDGC();
1188 return HCCALL4(JIT_GetFieldStruct_Framed, retBuff, obj, pFD, pFieldMT);
1189 }
1190
1191 void * pAddr = pFD->GetAddressGuaranteedInHeap(obj);
1192 CopyValueClass(retBuff, pAddr, pFieldMT, obj->GetAppDomain());
1193}
1194HCIMPLEND
1195#include <optdefault.h>
1196
1197/*********************************************************************/
1198HCIMPL4(VOID, JIT_SetFieldStruct_Framed, Object *obj, FieldDesc *pFD, MethodTable *pFieldMT, LPVOID valuePtr)
1199{
1200 FCALL_CONTRACT;
1201
1202 // Assert that we are not called for objects or primitive types
1203 _ASSERTE(!pFD->IsPrimitive());
1204
1205 OBJECTREF objRef = ObjectToOBJECTREF(obj);
1206
1207 // This may be a cross context field access. Setup a frame as we will
1208 // transition to managed code later
1209
1210 HELPER_METHOD_FRAME_BEGIN_1(objRef); // Set up a frame
1211
1212 if (objRef == NULL)
1213 COMPlusThrow(kNullReferenceException);
1214
1215 // Try an unwrap operation in case that we are not being called
1216 // in the same context as the server.
1217 // If that is the case then GetObjectFromProxy will return
1218 // the server object.
1219 BOOL fRemoted = FALSE;
1220
1221
1222 if (!fRemoted)
1223 {
1224 void * pAddr = pFD->GetAddress(OBJECTREFToObject(objRef));
1225 CopyValueClass(pAddr, valuePtr, pFieldMT, objRef->GetAppDomain());
1226 }
1227
1228 HELPER_METHOD_FRAME_END(); // Tear down the frame
1229}
1230HCIMPLEND
1231
1232#include <optsmallperfcritical.h>
1233HCIMPL4(VOID, JIT_SetFieldStruct, Object *obj, FieldDesc *pFD, MethodTable *pFieldMT, LPVOID valuePtr)
1234{
1235 FCALL_CONTRACT;
1236
1237 _ASSERTE(pFieldMT->IsValueType());
1238
1239 if (obj == NULL || g_IBCLogger.InstrEnabled() || pFD->IsEnCNew())
1240 {
1241 ENDFORBIDGC();
1242 return HCCALL4(JIT_SetFieldStruct_Framed, obj, pFD, pFieldMT, valuePtr);
1243 }
1244
1245 void * pAddr = pFD->GetAddressGuaranteedInHeap(obj);
1246 CopyValueClass(pAddr, valuePtr, pFieldMT, obj->GetAppDomain());
1247}
1248HCIMPLEND
1249#include <optdefault.h>
1250
1251
1252
1253//========================================================================
1254//
1255// STATIC FIELD HELPERS
1256//
1257//========================================================================
1258
1259
1260
1261// Slow helper to tailcall from the fast one
1262NOINLINE HCIMPL1(void, JIT_InitClass_Framed, MethodTable* pMT)
1263{
1264 FCALL_CONTRACT;
1265
1266 HELPER_METHOD_FRAME_BEGIN_0();
1267
1268 // We don't want to be calling JIT_InitClass at all for perf reasons
1269 // on the Global Class <Module> as the Class loading logic ensures that we
1270 // already have initialized the Gloabl Class <Module>
1271 CONSISTENCY_CHECK(!pMT->IsGlobalClass());
1272
1273 pMT->CheckRestore();
1274 pMT->CheckRunClassInitThrowing();
1275
1276 HELPER_METHOD_FRAME_END();
1277}
1278HCIMPLEND
1279
1280
1281/*************************************************************/
1282#include <optsmallperfcritical.h>
1283HCIMPL1(void, JIT_InitClass, CORINFO_CLASS_HANDLE typeHnd_)
1284{
1285 FCALL_CONTRACT;
1286
1287 TypeHandle typeHnd(typeHnd_);
1288 MethodTable *pMT = typeHnd.AsMethodTable();
1289 _ASSERTE(!pMT->IsClassPreInited());
1290
1291 if (pMT->GetDomainLocalModule()->IsClassInitialized(pMT))
1292 return;
1293
1294 // Tailcall to the slow helper
1295 ENDFORBIDGC();
1296 HCCALL1(JIT_InitClass_Framed, pMT);
1297}
1298HCIMPLEND
1299#include <optdefault.h>
1300
1301/*************************************************************/
1302HCIMPL2(void, JIT_InitInstantiatedClass, CORINFO_CLASS_HANDLE typeHnd_, CORINFO_METHOD_HANDLE methHnd_)
1303{
1304 CONTRACTL {
1305 FCALL_CHECK;
1306 PRECONDITION(methHnd_ != NULL);
1307 } CONTRACTL_END;
1308
1309 HELPER_METHOD_FRAME_BEGIN_NOPOLL(); // Set up a frame
1310
1311 MethodTable * pMT = (MethodTable*) typeHnd_;
1312 MethodDesc * pMD = (MethodDesc*) methHnd_;
1313
1314 MethodTable * pTemplateMT = pMD->GetMethodTable();
1315 if (pTemplateMT->IsSharedByGenericInstantiations())
1316 {
1317 pMT = ClassLoader::LoadGenericInstantiationThrowing(pTemplateMT->GetModule(),
1318 pTemplateMT->GetCl(),
1319 pMD->GetExactClassInstantiation(pMT)).AsMethodTable();
1320 }
1321 else
1322 {
1323 pMT = pTemplateMT;
1324 }
1325
1326 pMT->CheckRestore();
1327 pMT->EnsureInstanceActive();
1328 pMT->CheckRunClassInitThrowing();
1329 HELPER_METHOD_FRAME_END();
1330}
1331HCIMPLEND
1332
1333
1334//========================================================================
1335//
1336// SHARED STATIC FIELD HELPERS
1337//
1338//========================================================================
1339
1340#include <optsmallperfcritical.h>
1341
1342HCIMPL2(void*, JIT_GetSharedNonGCStaticBase_Portable, SIZE_T moduleDomainID, DWORD dwClassDomainID)
1343{
1344 FCALL_CONTRACT;
1345
1346 DomainLocalModule *pLocalModule = NULL;
1347
1348 if (!Module::IsEncodedModuleIndex(moduleDomainID))
1349 pLocalModule = (DomainLocalModule *) moduleDomainID;
1350 else
1351 {
1352 DomainLocalBlock *pLocalBlock = GetAppDomain()->GetDomainLocalBlock();
1353 pLocalModule = pLocalBlock->GetModuleSlot(Module::IDToIndex(moduleDomainID));
1354 }
1355
1356 // If type doesn't have a class constructor, the contents of this if statement may
1357 // still get executed. JIT_GetSharedNonGCStaticBaseNoCtor should be used in this case.
1358 if (pLocalModule->IsPrecomputedClassInitialized(dwClassDomainID))
1359 {
1360 return (void*)pLocalModule->GetPrecomputedNonGCStaticsBasePointer();
1361 }
1362
1363 // Tailcall to the slow helper
1364 ENDFORBIDGC();
1365 return HCCALL2(JIT_GetSharedNonGCStaticBase_Helper, pLocalModule, dwClassDomainID);
1366}
1367HCIMPLEND
1368
1369// No constructor version of JIT_GetSharedNonGCStaticBase. Does not check if class has
1370// been initialized.
1371HCIMPL1(void*, JIT_GetSharedNonGCStaticBaseNoCtor_Portable, SIZE_T moduleDomainID)
1372{
1373 FCALL_CONTRACT;
1374
1375 DomainLocalModule *pLocalModule = NULL;
1376
1377 if (!Module::IsEncodedModuleIndex(moduleDomainID))
1378 pLocalModule = (DomainLocalModule *) moduleDomainID;
1379 else
1380 {
1381 DomainLocalBlock *pLocalBlock = GetAppDomain()->GetDomainLocalBlock();
1382 pLocalModule = pLocalBlock->GetModuleSlot(Module::IDToIndex(moduleDomainID));
1383 }
1384
1385 return (void*)pLocalModule->GetPrecomputedNonGCStaticsBasePointer();
1386}
1387HCIMPLEND
1388
1389HCIMPL2(void*, JIT_GetSharedGCStaticBase_Portable, SIZE_T moduleDomainID, DWORD dwClassDomainID)
1390{
1391 FCALL_CONTRACT;
1392
1393 DomainLocalModule *pLocalModule = NULL;
1394
1395 if (!Module::IsEncodedModuleIndex(moduleDomainID))
1396 pLocalModule = (DomainLocalModule *) moduleDomainID;
1397 else
1398 {
1399 DomainLocalBlock *pLocalBlock = GetAppDomain()->GetDomainLocalBlock();
1400 pLocalModule = pLocalBlock->GetModuleSlot(Module::IDToIndex(moduleDomainID));
1401 }
1402
1403 // If type doesn't have a class constructor, the contents of this if statement may
1404 // still get executed. JIT_GetSharedGCStaticBaseNoCtor should be used in this case.
1405 if (pLocalModule->IsPrecomputedClassInitialized(dwClassDomainID))
1406 {
1407 return (void*)pLocalModule->GetPrecomputedGCStaticsBasePointer();
1408 }
1409
1410 // Tailcall to the slow helper
1411 ENDFORBIDGC();
1412 return HCCALL2(JIT_GetSharedGCStaticBase_Helper, pLocalModule, dwClassDomainID);
1413}
1414HCIMPLEND
1415
1416// No constructor version of JIT_GetSharedGCStaticBase. Does not check if class has been
1417// initialized.
1418HCIMPL1(void*, JIT_GetSharedGCStaticBaseNoCtor_Portable, SIZE_T moduleDomainID)
1419{
1420 FCALL_CONTRACT;
1421
1422 DomainLocalModule *pLocalModule = NULL;
1423
1424 if (!Module::IsEncodedModuleIndex(moduleDomainID))
1425 pLocalModule = (DomainLocalModule *) moduleDomainID;
1426 else
1427 {
1428 DomainLocalBlock *pLocalBlock = GetAppDomain()->GetDomainLocalBlock();
1429 pLocalModule = pLocalBlock->GetModuleSlot(Module::IDToIndex(moduleDomainID));
1430 }
1431
1432 return (void*)pLocalModule->GetPrecomputedGCStaticsBasePointer();
1433}
1434HCIMPLEND
1435
1436#include <optdefault.h>
1437
1438
1439// The following two functions can be tail called from platform dependent versions of
1440// JIT_GetSharedGCStaticBase and JIT_GetShareNonGCStaticBase
1441HCIMPL2(void*, JIT_GetSharedNonGCStaticBase_Helper, DomainLocalModule *pLocalModule, DWORD dwClassDomainID)
1442{
1443 FCALL_CONTRACT;
1444
1445 HELPER_METHOD_FRAME_BEGIN_RET_0();
1446
1447 // Obtain Method table
1448 MethodTable * pMT = pLocalModule->GetMethodTableFromClassDomainID(dwClassDomainID);
1449
1450 PREFIX_ASSUME(pMT != NULL);
1451 pMT->CheckRunClassInitThrowing();
1452 HELPER_METHOD_FRAME_END();
1453
1454 return (void*)pLocalModule->GetPrecomputedNonGCStaticsBasePointer();
1455}
1456HCIMPLEND
1457
1458HCIMPL2(void*, JIT_GetSharedGCStaticBase_Helper, DomainLocalModule *pLocalModule, DWORD dwClassDomainID)
1459{
1460 FCALL_CONTRACT;
1461
1462 HELPER_METHOD_FRAME_BEGIN_RET_0();
1463
1464 // Obtain Method table
1465 MethodTable * pMT = pLocalModule->GetMethodTableFromClassDomainID(dwClassDomainID);
1466
1467 PREFIX_ASSUME(pMT != NULL);
1468 pMT->CheckRunClassInitThrowing();
1469 HELPER_METHOD_FRAME_END();
1470
1471 return (void*)pLocalModule->GetPrecomputedGCStaticsBasePointer();
1472}
1473HCIMPLEND
1474
1475/*********************************************************************/
1476// Slow helper to tail call from the fast one
1477HCIMPL2(void*, JIT_GetSharedNonGCStaticBaseDynamicClass_Helper, DomainLocalModule *pLocalModule, DWORD dwDynamicClassDomainID)
1478{
1479 FCALL_CONTRACT;
1480
1481 void* result = NULL;
1482
1483 HELPER_METHOD_FRAME_BEGIN_RET_0();
1484
1485 MethodTable *pMT = pLocalModule->GetDomainFile()->GetModule()->GetDynamicClassMT(dwDynamicClassDomainID);
1486 _ASSERTE(pMT);
1487
1488 pMT->CheckRunClassInitThrowing();
1489
1490 result = (void*)pLocalModule->GetDynamicEntryNonGCStaticsBasePointer(dwDynamicClassDomainID, pMT->GetLoaderAllocator());
1491 HELPER_METHOD_FRAME_END();
1492
1493 return result;
1494}
1495HCIMPLEND
1496
1497/*************************************************************/
1498#include <optsmallperfcritical.h>
1499HCIMPL2(void*, JIT_GetSharedNonGCStaticBaseDynamicClass, SIZE_T moduleDomainID, DWORD dwDynamicClassDomainID)
1500{
1501 FCALL_CONTRACT;
1502
1503 DomainLocalModule *pLocalModule;
1504
1505 if (!Module::IsEncodedModuleIndex(moduleDomainID))
1506 pLocalModule = (DomainLocalModule *) moduleDomainID;
1507 else
1508 {
1509 DomainLocalBlock *pLocalBlock = GetAppDomain()->GetDomainLocalBlock();
1510 pLocalModule = pLocalBlock->GetModuleSlot(Module::IDToIndex(moduleDomainID));
1511 }
1512
1513 DomainLocalModule::PTR_DynamicClassInfo pLocalInfo = pLocalModule->GetDynamicClassInfoIfInitialized(dwDynamicClassDomainID);
1514 if (pLocalInfo != NULL)
1515 {
1516 PTR_BYTE retval;
1517 GET_DYNAMICENTRY_NONGCSTATICS_BASEPOINTER(pLocalModule->GetDomainFile()->GetModule()->GetLoaderAllocator(),
1518 pLocalInfo,
1519 &retval);
1520
1521 return retval;
1522 }
1523
1524 // Tailcall to the slow helper
1525 ENDFORBIDGC();
1526 return HCCALL2(JIT_GetSharedNonGCStaticBaseDynamicClass_Helper, pLocalModule, dwDynamicClassDomainID);
1527}
1528HCIMPLEND
1529#include <optdefault.h>
1530
1531/*************************************************************/
1532// Slow helper to tail call from the fast one
1533HCIMPL2(void, JIT_ClassInitDynamicClass_Helper, DomainLocalModule *pLocalModule, DWORD dwDynamicClassDomainID)
1534{
1535 FCALL_CONTRACT;
1536
1537 HELPER_METHOD_FRAME_BEGIN_0();
1538
1539 MethodTable *pMT = pLocalModule->GetDomainFile()->GetModule()->GetDynamicClassMT(dwDynamicClassDomainID);
1540 _ASSERTE(pMT);
1541
1542 pMT->CheckRunClassInitThrowing();
1543
1544 HELPER_METHOD_FRAME_END();
1545
1546 return;
1547}
1548HCIMPLEND
1549
1550#include <optsmallperfcritical.h>
1551HCIMPL2(void, JIT_ClassInitDynamicClass, SIZE_T moduleDomainID, DWORD dwDynamicClassDomainID)
1552{
1553 FCALL_CONTRACT;
1554
1555 DomainLocalModule *pLocalModule;
1556
1557 if (!Module::IsEncodedModuleIndex(moduleDomainID))
1558 pLocalModule = (DomainLocalModule *) moduleDomainID;
1559 else
1560 {
1561 DomainLocalBlock *pLocalBlock = GetAppDomain()->GetDomainLocalBlock();
1562 pLocalModule = pLocalBlock->GetModuleSlot(Module::IDToIndex(moduleDomainID));
1563 }
1564
1565 DomainLocalModule::PTR_DynamicClassInfo pLocalInfo = pLocalModule->GetDynamicClassInfoIfInitialized(dwDynamicClassDomainID);
1566 if (pLocalInfo != NULL)
1567 {
1568 return;
1569 }
1570
1571 // Tailcall to the slow helper
1572 ENDFORBIDGC();
1573 return HCCALL2(JIT_ClassInitDynamicClass_Helper, pLocalModule, dwDynamicClassDomainID);
1574}
1575HCIMPLEND
1576#include <optdefault.h>
1577
1578/*************************************************************/
1579// Slow helper to tail call from the fast one
1580HCIMPL2(void*, JIT_GetSharedGCStaticBaseDynamicClass_Helper, DomainLocalModule *pLocalModule, DWORD dwDynamicClassDomainID)
1581{
1582 FCALL_CONTRACT;
1583
1584 void* result = NULL;
1585
1586 HELPER_METHOD_FRAME_BEGIN_RET_0();
1587
1588 MethodTable *pMT = pLocalModule->GetDomainFile()->GetModule()->GetDynamicClassMT(dwDynamicClassDomainID);
1589 _ASSERTE(pMT);
1590
1591 pMT->CheckRunClassInitThrowing();
1592
1593 result = (void*)pLocalModule->GetDynamicEntryGCStaticsBasePointer(dwDynamicClassDomainID, pMT->GetLoaderAllocator());
1594 HELPER_METHOD_FRAME_END();
1595
1596 return result;
1597}
1598HCIMPLEND
1599
1600/*************************************************************/
1601#include <optsmallperfcritical.h>
1602HCIMPL2(void*, JIT_GetSharedGCStaticBaseDynamicClass, SIZE_T moduleDomainID, DWORD dwDynamicClassDomainID)
1603{
1604 FCALL_CONTRACT;
1605
1606 DomainLocalModule *pLocalModule;
1607
1608 if (!Module::IsEncodedModuleIndex(moduleDomainID))
1609 pLocalModule = (DomainLocalModule *) moduleDomainID;
1610 else
1611 {
1612 DomainLocalBlock *pLocalBlock = GetAppDomain()->GetDomainLocalBlock();
1613 pLocalModule = pLocalBlock->GetModuleSlot(Module::IDToIndex(moduleDomainID));
1614 }
1615
1616 DomainLocalModule::PTR_DynamicClassInfo pLocalInfo = pLocalModule->GetDynamicClassInfoIfInitialized(dwDynamicClassDomainID);
1617 if (pLocalInfo != NULL)
1618 {
1619 PTR_BYTE retval;
1620 GET_DYNAMICENTRY_GCSTATICS_BASEPOINTER(pLocalModule->GetDomainFile()->GetModule()->GetLoaderAllocator(),
1621 pLocalInfo,
1622 &retval);
1623
1624 return retval;
1625 }
1626
1627 // Tailcall to the slow helper
1628 ENDFORBIDGC();
1629 return HCCALL2(JIT_GetSharedGCStaticBaseDynamicClass_Helper, pLocalModule, dwDynamicClassDomainID);
1630}
1631HCIMPLEND
1632#include <optdefault.h>
1633
1634/*********************************************************************/
1635// Slow helper to tail call from the fast one
1636NOINLINE HCIMPL1(void*, JIT_GetGenericsGCStaticBase_Framed, MethodTable *pMT)
1637{
1638 CONTRACTL {
1639 FCALL_CHECK;
1640 PRECONDITION(CheckPointer(pMT));
1641 PRECONDITION(pMT->HasGenericsStaticsInfo());
1642 } CONTRACTL_END;
1643
1644 void* base = NULL;
1645
1646 HELPER_METHOD_FRAME_BEGIN_RET_0();
1647
1648 pMT->CheckRestore();
1649
1650 pMT->CheckRunClassInitThrowing();
1651
1652 base = (void*) pMT->GetGCStaticsBasePointer();
1653 CONSISTENCY_CHECK(base != NULL);
1654
1655 HELPER_METHOD_FRAME_END();
1656
1657 return base;
1658}
1659HCIMPLEND
1660
1661/*********************************************************************/
1662#include <optsmallperfcritical.h>
1663HCIMPL1(void*, JIT_GetGenericsGCStaticBase, MethodTable *pMT)
1664{
1665 CONTRACTL {
1666 FCALL_CHECK;
1667 PRECONDITION(CheckPointer(pMT));
1668 PRECONDITION(pMT->HasGenericsStaticsInfo());
1669 } CONTRACTL_END;
1670
1671 DWORD dwDynamicClassDomainID;
1672 PTR_Module pModuleForStatics = pMT->GetGenericsStaticsModuleAndID(&dwDynamicClassDomainID);
1673
1674 DomainLocalModule *pLocalModule = pModuleForStatics->GetDomainLocalModule();
1675 _ASSERTE(pLocalModule);
1676
1677 DomainLocalModule::PTR_DynamicClassInfo pLocalInfo = pLocalModule->GetDynamicClassInfoIfInitialized(dwDynamicClassDomainID);
1678 if (pLocalInfo != NULL)
1679 {
1680 PTR_BYTE retval;
1681 GET_DYNAMICENTRY_GCSTATICS_BASEPOINTER(pMT->GetLoaderAllocator(),
1682 pLocalInfo,
1683 &retval);
1684
1685 return retval;
1686 }
1687
1688 // Tailcall to the slow helper
1689 ENDFORBIDGC();
1690 return HCCALL1(JIT_GetGenericsGCStaticBase_Framed, pMT);
1691}
1692HCIMPLEND
1693#include <optdefault.h>
1694
1695/*********************************************************************/
1696// Slow helper to tail call from the fast one
1697NOINLINE HCIMPL1(void*, JIT_GetGenericsNonGCStaticBase_Framed, MethodTable *pMT)
1698{
1699 CONTRACTL {
1700 FCALL_CHECK;
1701 PRECONDITION(CheckPointer(pMT));
1702 PRECONDITION(pMT->HasGenericsStaticsInfo());
1703 } CONTRACTL_END;
1704
1705 void* base = NULL;
1706
1707 HELPER_METHOD_FRAME_BEGIN_RET_0();
1708
1709 pMT->CheckRestore();
1710
1711 // If pMT refers to a method table that requires some initialization work,
1712 // then pMT cannot to a method table that is shared by generic instantiations,
1713 // because method tables that are shared by generic instantiations do not have
1714 // a base for statics to live in.
1715 _ASSERTE(pMT->IsClassPreInited() || !pMT->IsSharedByGenericInstantiations());
1716
1717 pMT->CheckRunClassInitThrowing();
1718
1719 // We could just return null here instead of returning base when this helper is called just to trigger the cctor
1720 base = (void*) pMT->GetNonGCStaticsBasePointer();
1721
1722 HELPER_METHOD_FRAME_END();
1723
1724 return base;
1725}
1726HCIMPLEND
1727
1728/*********************************************************************/
1729#include <optsmallperfcritical.h>
1730HCIMPL1(void*, JIT_GetGenericsNonGCStaticBase, MethodTable *pMT)
1731{
1732 CONTRACTL {
1733 FCALL_CHECK;
1734 PRECONDITION(CheckPointer(pMT));
1735 PRECONDITION(pMT->HasGenericsStaticsInfo());
1736 } CONTRACTL_END;
1737
1738 // This fast path will typically always be taken once the slow framed path below
1739 // has executed once. Sometimes the slow path will be executed more than once,
1740 // e.g. if static fields are accessed during the call to CheckRunClassInitThrowing()
1741 // in the slow path.
1742
1743 DWORD dwDynamicClassDomainID;
1744 PTR_Module pModuleForStatics = pMT->GetGenericsStaticsModuleAndID(&dwDynamicClassDomainID);
1745
1746 DomainLocalModule *pLocalModule = pModuleForStatics->GetDomainLocalModule();
1747 _ASSERTE(pLocalModule);
1748
1749 DomainLocalModule::PTR_DynamicClassInfo pLocalInfo = pLocalModule->GetDynamicClassInfoIfInitialized(dwDynamicClassDomainID);
1750 if (pLocalInfo != NULL)
1751 {
1752 PTR_BYTE retval;
1753 GET_DYNAMICENTRY_NONGCSTATICS_BASEPOINTER(pMT->GetLoaderAllocator(),
1754 pLocalInfo,
1755 &retval);
1756
1757 return retval;
1758 }
1759
1760 // Tailcall to the slow helper
1761 ENDFORBIDGC();
1762 return HCCALL1(JIT_GetGenericsNonGCStaticBase_Framed, pMT);
1763}
1764HCIMPLEND
1765#include <optdefault.h>
1766
1767
1768//========================================================================
1769//
1770// THREAD STATIC FIELD HELPERS
1771//
1772//========================================================================
1773
1774
1775// *** These framed helpers get called if allocation needs to occur or
1776// if the class constructor needs to run
1777
1778HCIMPL1(void*, JIT_GetNonGCThreadStaticBase_Helper, MethodTable * pMT)
1779{
1780 CONTRACTL {
1781 FCALL_CHECK;
1782 PRECONDITION(CheckPointer(pMT));
1783 } CONTRACTL_END;
1784
1785 void* base = NULL;
1786
1787 HELPER_METHOD_FRAME_BEGIN_RET_0();
1788
1789 // For generics, we need to call CheckRestore() for some reason
1790 if (pMT->HasGenericsStaticsInfo())
1791 pMT->CheckRestore();
1792
1793 // Get the TLM
1794 ThreadLocalModule * pThreadLocalModule = ThreadStatics::GetTLM(pMT);
1795 _ASSERTE(pThreadLocalModule != NULL);
1796
1797 // Check if the class constructor needs to be run
1798 pThreadLocalModule->CheckRunClassInitThrowing(pMT);
1799
1800 // Lookup the non-GC statics base pointer
1801 base = (void*) pMT->GetNonGCThreadStaticsBasePointer();
1802 CONSISTENCY_CHECK(base != NULL);
1803
1804 HELPER_METHOD_FRAME_END();
1805
1806 return base;
1807}
1808HCIMPLEND
1809
1810HCIMPL1(void*, JIT_GetGCThreadStaticBase_Helper, MethodTable * pMT)
1811{
1812 CONTRACTL {
1813 FCALL_CHECK;
1814 PRECONDITION(CheckPointer(pMT));
1815 } CONTRACTL_END;
1816
1817 void* base = NULL;
1818
1819 HELPER_METHOD_FRAME_BEGIN_RET_0();
1820
1821 // For generics, we need to call CheckRestore() for some reason
1822 if (pMT->HasGenericsStaticsInfo())
1823 pMT->CheckRestore();
1824
1825 // Get the TLM
1826 ThreadLocalModule * pThreadLocalModule = ThreadStatics::GetTLM(pMT);
1827 _ASSERTE(pThreadLocalModule != NULL);
1828
1829 // Check if the class constructor needs to be run
1830 pThreadLocalModule->CheckRunClassInitThrowing(pMT);
1831
1832 // Lookup the GC statics base pointer
1833 base = (void*) pMT->GetGCThreadStaticsBasePointer();
1834 CONSISTENCY_CHECK(base != NULL);
1835
1836 HELPER_METHOD_FRAME_END();
1837
1838 return base;
1839}
1840HCIMPLEND
1841
1842
1843// *** This helper corresponds to both CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE and
1844// CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE_NOCTOR. Even though we always check
1845// if the class constructor has been run, we have a separate helper ID for the "no ctor"
1846// version because it allows the JIT to do some reordering that otherwise wouldn't be
1847// possible.
1848
1849#include <optsmallperfcritical.h>
1850HCIMPL2(void*, JIT_GetSharedNonGCThreadStaticBase, SIZE_T moduleDomainID, DWORD dwClassDomainID)
1851{
1852 FCALL_CONTRACT;
1853
1854 // Get the ModuleIndex
1855 ModuleIndex index =
1856 (Module::IsEncodedModuleIndex(moduleDomainID)) ?
1857 Module::IDToIndex(moduleDomainID) :
1858 ((DomainLocalModule *)moduleDomainID)->GetModuleIndex();
1859
1860 // Get the relevant ThreadLocalModule
1861 ThreadLocalModule * pThreadLocalModule = ThreadStatics::GetTLMIfExists(index);
1862
1863 // If the TLM has been allocated and the class has been marked as initialized,
1864 // get the pointer to the non-GC statics base and return
1865 if (pThreadLocalModule != NULL && pThreadLocalModule->IsPrecomputedClassInitialized(dwClassDomainID))
1866 return (void*)pThreadLocalModule->GetPrecomputedNonGCStaticsBasePointer();
1867
1868 // If the TLM was not allocated or if the class was not marked as initialized
1869 // then we have to go through the slow path
1870
1871 // Get the DomainLocalModule
1872 DomainLocalModule *pDomainLocalModule =
1873 (Module::IsEncodedModuleIndex(moduleDomainID)) ?
1874 GetAppDomain()->GetDomainLocalBlock()->GetModuleSlot(Module::IDToIndex(moduleDomainID)) :
1875 (DomainLocalModule *) moduleDomainID;
1876
1877 // Obtain the MethodTable
1878 MethodTable * pMT = pDomainLocalModule->GetMethodTableFromClassDomainID(dwClassDomainID);
1879 _ASSERTE(!pMT->HasGenericsStaticsInfo());
1880
1881 ENDFORBIDGC();
1882 return HCCALL1(JIT_GetNonGCThreadStaticBase_Helper, pMT);
1883}
1884HCIMPLEND
1885#include <optdefault.h>
1886
1887// *** This helper corresponds to both CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE and
1888// CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE_NOCTOR. Even though we always check
1889// if the class constructor has been run, we have a separate helper ID for the "no ctor"
1890// version because it allows the JIT to do some reordering that otherwise wouldn't be
1891// possible.
1892
1893#include <optsmallperfcritical.h>
1894HCIMPL2(void*, JIT_GetSharedGCThreadStaticBase, SIZE_T moduleDomainID, DWORD dwClassDomainID)
1895{
1896 FCALL_CONTRACT;
1897
1898 // Get the ModuleIndex
1899 ModuleIndex index =
1900 (Module::IsEncodedModuleIndex(moduleDomainID)) ?
1901 Module::IDToIndex(moduleDomainID) :
1902 ((DomainLocalModule *)moduleDomainID)->GetModuleIndex();
1903
1904 // Get the relevant ThreadLocalModule
1905 ThreadLocalModule * pThreadLocalModule = ThreadStatics::GetTLMIfExists(index);
1906
1907 // If the TLM has been allocated and the class has been marked as initialized,
1908 // get the pointer to the GC statics base and return
1909 if (pThreadLocalModule != NULL && pThreadLocalModule->IsPrecomputedClassInitialized(dwClassDomainID))
1910 return (void*)pThreadLocalModule->GetPrecomputedGCStaticsBasePointer();
1911
1912 // If the TLM was not allocated or if the class was not marked as initialized
1913 // then we have to go through the slow path
1914
1915 // Get the DomainLocalModule
1916 DomainLocalModule *pDomainLocalModule =
1917 (Module::IsEncodedModuleIndex(moduleDomainID)) ?
1918 GetAppDomain()->GetDomainLocalBlock()->GetModuleSlot(Module::IDToIndex(moduleDomainID)) :
1919 (DomainLocalModule *) moduleDomainID;
1920
1921 // Obtain the MethodTable
1922 MethodTable * pMT = pDomainLocalModule->GetMethodTableFromClassDomainID(dwClassDomainID);
1923 _ASSERTE(!pMT->HasGenericsStaticsInfo());
1924
1925 ENDFORBIDGC();
1926 return HCCALL1(JIT_GetGCThreadStaticBase_Helper, pMT);
1927}
1928HCIMPLEND
1929#include <optdefault.h>
1930
1931// *** This helper corresponds to CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE_DYNAMICCLASS
1932
1933#include <optsmallperfcritical.h>
1934HCIMPL2(void*, JIT_GetSharedNonGCThreadStaticBaseDynamicClass, SIZE_T moduleDomainID, DWORD dwDynamicClassDomainID)
1935{
1936 FCALL_CONTRACT;
1937
1938 // Obtain the DomainLocalModule
1939 DomainLocalModule *pDomainLocalModule =
1940 (Module::IsEncodedModuleIndex(moduleDomainID)) ?
1941 GetAppDomain()->GetDomainLocalBlock()->GetModuleSlot(Module::IDToIndex(moduleDomainID)) :
1942 (DomainLocalModule *)moduleDomainID;
1943
1944 // Get the ModuleIndex
1945 ModuleIndex index = pDomainLocalModule->GetModuleIndex();
1946
1947 // Get the relevant ThreadLocalModule
1948 ThreadLocalModule * pThreadLocalModule = ThreadStatics::GetTLMIfExists(index);
1949
1950 // If the TLM has been allocated and the class has been marked as initialized,
1951 // get the pointer to the non-GC statics base and return
1952 if (pThreadLocalModule != NULL)
1953 {
1954 ThreadLocalModule::PTR_DynamicClassInfo pLocalInfo = pThreadLocalModule->GetDynamicClassInfoIfInitialized(dwDynamicClassDomainID);
1955 if (pLocalInfo != NULL)
1956 {
1957 PTR_BYTE retval;
1958 GET_DYNAMICENTRY_NONGCTHREADSTATICS_BASEPOINTER(pDomainLocalModule->GetDomainFile()->GetModule()->GetLoaderAllocator(),
1959 pLocalInfo,
1960 &retval);
1961 return retval;
1962 }
1963 }
1964
1965 // If the TLM was not allocated or if the class was not marked as initialized
1966 // then we have to go through the slow path
1967
1968 // Obtain the Module
1969 Module * pModule = pDomainLocalModule->GetDomainFile()->GetModule();
1970
1971 // Obtain the MethodTable
1972 MethodTable * pMT = pModule->GetDynamicClassMT(dwDynamicClassDomainID);
1973 _ASSERTE(pMT != NULL);
1974 _ASSERTE(!pMT->IsSharedByGenericInstantiations());
1975
1976 // Tailcall to the slow helper
1977 ENDFORBIDGC();
1978
1979 return HCCALL1(JIT_GetNonGCThreadStaticBase_Helper, pMT);
1980
1981}
1982HCIMPLEND
1983#include <optdefault.h>
1984
1985// *** This helper corresponds to CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE_DYNAMICCLASS
1986
1987#include <optsmallperfcritical.h>
1988HCIMPL2(void*, JIT_GetSharedGCThreadStaticBaseDynamicClass, SIZE_T moduleDomainID, DWORD dwDynamicClassDomainID)
1989{
1990 FCALL_CONTRACT;
1991
1992 // Obtain the DomainLocalModule
1993 DomainLocalModule *pDomainLocalModule =
1994 (Module::IsEncodedModuleIndex(moduleDomainID)) ?
1995 GetAppDomain()->GetDomainLocalBlock()->GetModuleSlot(Module::IDToIndex(moduleDomainID)) :
1996 (DomainLocalModule *)moduleDomainID;
1997
1998 // Get the ModuleIndex
1999 ModuleIndex index = pDomainLocalModule->GetModuleIndex();
2000
2001 // Get the relevant ThreadLocalModule
2002 ThreadLocalModule * pThreadLocalModule = ThreadStatics::GetTLMIfExists(index);
2003
2004 // If the TLM has been allocated and the class has been marked as initialized,
2005 // get the pointer to the GC statics base and return
2006 if (pThreadLocalModule != NULL)
2007 {
2008 ThreadLocalModule::PTR_DynamicClassInfo pLocalInfo = pThreadLocalModule->GetDynamicClassInfoIfInitialized(dwDynamicClassDomainID);
2009 if (pLocalInfo != NULL)
2010 {
2011 PTR_BYTE retval;
2012 GET_DYNAMICENTRY_GCTHREADSTATICS_BASEPOINTER(pDomainLocalModule->GetDomainFile()->GetModule()->GetLoaderAllocator(),
2013 pLocalInfo,
2014 &retval);
2015
2016 return retval;
2017 }
2018 }
2019
2020 // If the TLM was not allocated or if the class was not marked as initialized
2021 // then we have to go through the slow path
2022
2023 // Obtain the Module
2024 Module * pModule = pDomainLocalModule->GetDomainFile()->GetModule();
2025
2026 // Obtain the MethodTable
2027 MethodTable * pMT = pModule->GetDynamicClassMT(dwDynamicClassDomainID);
2028 _ASSERTE(pMT != NULL);
2029 _ASSERTE(!pMT->IsSharedByGenericInstantiations());
2030
2031 // Tailcall to the slow helper
2032 ENDFORBIDGC();
2033 return HCCALL1(JIT_GetGCThreadStaticBase_Helper, pMT);
2034}
2035HCIMPLEND
2036#include <optdefault.h>
2037
2038// *** This helper corresponds to CORINFO_HELP_GETGENERICS_NONGCTHREADSTATIC_BASE
2039
2040#include <optsmallperfcritical.h>
2041HCIMPL1(void*, JIT_GetGenericsNonGCThreadStaticBase, MethodTable *pMT)
2042{
2043 CONTRACTL {
2044 FCALL_CHECK;
2045 PRECONDITION(CheckPointer(pMT));
2046 PRECONDITION(pMT->HasGenericsStaticsInfo());
2047 } CONTRACTL_END;
2048
2049 // This fast path will typically always be taken once the slow framed path below
2050 // has executed once. Sometimes the slow path will be executed more than once,
2051 // e.g. if static fields are accessed during the call to CheckRunClassInitThrowing()
2052 // in the slow path.
2053
2054 // Get the Module and dynamic class ID
2055 DWORD dwDynamicClassDomainID;
2056 PTR_Module pModule = pMT->GetGenericsStaticsModuleAndID(&dwDynamicClassDomainID);
2057
2058 // Get ModuleIndex
2059 ModuleIndex index = pModule->GetModuleIndex();
2060
2061 // Get the relevant ThreadLocalModule
2062 ThreadLocalModule * pThreadLocalModule = ThreadStatics::GetTLMIfExists(index);
2063
2064 // If the TLM has been allocated and the class has been marked as initialized,
2065 // get the pointer to the non-GC statics base and return
2066 if (pThreadLocalModule != NULL)
2067 {
2068 ThreadLocalModule::PTR_DynamicClassInfo pLocalInfo = pThreadLocalModule->GetDynamicClassInfoIfInitialized(dwDynamicClassDomainID);
2069 if (pLocalInfo != NULL)
2070 {
2071 PTR_BYTE retval;
2072 GET_DYNAMICENTRY_NONGCSTATICS_BASEPOINTER(pMT->GetLoaderAllocator(),
2073 pLocalInfo,
2074 &retval);
2075
2076 return retval;
2077 }
2078 }
2079
2080 // If the TLM was not allocated or if the class was not marked as initialized
2081 // then we have to go through the slow path
2082
2083 // Tailcall to the slow helper
2084 ENDFORBIDGC();
2085 return HCCALL1(JIT_GetNonGCThreadStaticBase_Helper, pMT);
2086}
2087HCIMPLEND
2088#include <optdefault.h>
2089
2090// *** This helper corresponds to CORINFO_HELP_GETGENERICS_GCTHREADSTATIC_BASE
2091
2092#include <optsmallperfcritical.h>
2093HCIMPL1(void*, JIT_GetGenericsGCThreadStaticBase, MethodTable *pMT)
2094{
2095 CONTRACTL {
2096 FCALL_CHECK;
2097 PRECONDITION(CheckPointer(pMT));
2098 PRECONDITION(pMT->HasGenericsStaticsInfo());
2099 } CONTRACTL_END;
2100
2101 // This fast path will typically always be taken once the slow framed path below
2102 // has executed once. Sometimes the slow path will be executed more than once,
2103 // e.g. if static fields are accessed during the call to CheckRunClassInitThrowing()
2104 // in the slow path.
2105
2106 // Get the Module and dynamic class ID
2107 DWORD dwDynamicClassDomainID;
2108 PTR_Module pModule = pMT->GetGenericsStaticsModuleAndID(&dwDynamicClassDomainID);
2109
2110 // Get ModuleIndex
2111 ModuleIndex index = pModule->GetModuleIndex();
2112
2113 // Get the relevant ThreadLocalModule
2114 ThreadLocalModule * pThreadLocalModule = ThreadStatics::GetTLMIfExists(index);
2115
2116 // If the TLM has been allocated and the class has been marked as initialized,
2117 // get the pointer to the GC statics base and return
2118 if (pThreadLocalModule != NULL)
2119 {
2120 ThreadLocalModule::PTR_DynamicClassInfo pLocalInfo = pThreadLocalModule->GetDynamicClassInfoIfInitialized(dwDynamicClassDomainID);
2121 if (pLocalInfo != NULL)
2122 {
2123 PTR_BYTE retval;
2124 GET_DYNAMICENTRY_GCTHREADSTATICS_BASEPOINTER(pMT->GetLoaderAllocator(),
2125 pLocalInfo,
2126 &retval);
2127
2128 return retval;
2129 }
2130 }
2131
2132 // If the TLM was not allocated or if the class was not marked as initialized
2133 // then we have to go through the slow path
2134
2135 // Tailcall to the slow helper
2136 ENDFORBIDGC();
2137 return HCCALL1(JIT_GetGCThreadStaticBase_Helper, pMT);
2138}
2139HCIMPLEND
2140#include <optdefault.h>
2141
2142//========================================================================
2143//
2144// STATIC FIELD DYNAMIC HELPERS
2145//
2146//========================================================================
2147
2148#include <optsmallperfcritical.h>
2149HCIMPL1_RAW(TADDR, JIT_StaticFieldAddress_Dynamic, StaticFieldAddressArgs * pArgs)
2150{
2151 FCALL_CONTRACT;
2152
2153 TADDR base = HCCALL2(pArgs->staticBaseHelper, pArgs->arg0, pArgs->arg1);
2154 return base + pArgs->offset;
2155}
2156HCIMPLEND_RAW
2157#include <optdefault.h>
2158
2159#include <optsmallperfcritical.h>
2160HCIMPL1_RAW(TADDR, JIT_StaticFieldAddressUnbox_Dynamic, StaticFieldAddressArgs * pArgs)
2161{
2162 FCALL_CONTRACT;
2163
2164 TADDR base = HCCALL2(pArgs->staticBaseHelper, pArgs->arg0, pArgs->arg1);
2165 return *(TADDR *)(base + pArgs->offset) + Object::GetOffsetOfFirstField();
2166}
2167HCIMPLEND_RAW
2168#include <optdefault.h>
2169
2170//========================================================================
2171//
2172// CASTING HELPERS
2173//
2174//========================================================================
2175
2176// pObject MUST be an instance of an array.
2177TypeHandle::CastResult ArrayIsInstanceOfNoGC(Object *pObject, TypeHandle toTypeHnd)
2178{
2179 CONTRACTL {
2180 NOTHROW;
2181 GC_NOTRIGGER;
2182 MODE_COOPERATIVE;
2183 SO_TOLERANT;
2184 PRECONDITION(CheckPointer(pObject));
2185 PRECONDITION(pObject->GetMethodTable()->IsArray());
2186 PRECONDITION(toTypeHnd.IsArray());
2187 } CONTRACTL_END;
2188
2189 ArrayBase *pArray = (ArrayBase*) pObject;
2190 ArrayTypeDesc *toArrayType = toTypeHnd.AsArray();
2191
2192 // GetRank touches EEClass. Try to avoid it for SZArrays.
2193 if (toArrayType->GetInternalCorElementType() == ELEMENT_TYPE_SZARRAY)
2194 {
2195 if (pArray->GetMethodTable()->IsMultiDimArray())
2196 return TypeHandle::CannotCast;
2197 }
2198 else
2199 {
2200 if (pArray->GetRank() != toArrayType->GetRank())
2201 return TypeHandle::CannotCast;
2202 }
2203 _ASSERTE(pArray->GetRank() == toArrayType->GetRank());
2204
2205 // ArrayBase::GetTypeHandle consults the loader tables to find the
2206 // exact type handle for an array object. This can be disproportionately slow - but after
2207 // all, why should we need to go looking up hash tables just to do a cast test?
2208 //
2209 // Thus we can always special-case the casting logic to avoid fetching this
2210 // exact type handle. Here we have only done so for one
2211 // particular case, i.e. when we are trying to cast to an array type where
2212 // there is an exact match between the rank, kind and element type of the two
2213 // array types. This happens when, for example, assigning an int32[] into an int32[][].
2214 //
2215
2216 TypeHandle elementTypeHandle = pArray->GetArrayElementTypeHandle();
2217 TypeHandle toElementTypeHandle = toArrayType->GetArrayElementTypeHandle();
2218
2219 if (elementTypeHandle == toElementTypeHandle)
2220 return TypeHandle::CanCast;
2221
2222 // By this point we know that toArrayType->GetInternalCorElementType matches the element type of the Array object
2223 // so we can use a faster constructor to create the TypeDesc. (It so happens that ArrayTypeDescs derives from ParamTypeDesc
2224 // and can be created as identical in a slightly faster way with the following set of parameters.)
2225 ParamTypeDesc arrayType(toArrayType->GetInternalCorElementType(), pArray->GetMethodTable(), elementTypeHandle);
2226 return arrayType.CanCastToNoGC(toTypeHnd);
2227}
2228
2229// pObject MUST be an instance of an array.
2230TypeHandle::CastResult ArrayObjSupportsBizarreInterfaceNoGC(Object *pObject, MethodTable * pInterfaceMT)
2231{
2232 CONTRACTL {
2233 NOTHROW;
2234 GC_NOTRIGGER;
2235 MODE_COOPERATIVE;
2236 SO_TOLERANT;
2237 PRECONDITION(CheckPointer(pObject));
2238 PRECONDITION(pObject->GetMethodTable()->IsArray());
2239 PRECONDITION(pInterfaceMT->IsInterface());
2240 } CONTRACTL_END;
2241
2242 ArrayBase *pArray = (ArrayBase*) pObject;
2243
2244 // IList<T> & IReadOnlyList<T> only supported for SZ_ARRAYS
2245 if (pArray->GetMethodTable()->IsMultiDimArray())
2246 return TypeHandle::CannotCast;
2247
2248 if (pInterfaceMT->GetLoadLevel() < CLASS_DEPENDENCIES_LOADED)
2249 {
2250 if (!pInterfaceMT->HasInstantiation())
2251 return TypeHandle::CannotCast;
2252 // The slow path will take care of restoring the interface
2253 return TypeHandle::MaybeCast;
2254 }
2255
2256 if (!IsImplicitInterfaceOfSZArray(pInterfaceMT))
2257 return TypeHandle::CannotCast;
2258
2259 return TypeDesc::CanCastParamNoGC(pArray->GetArrayElementTypeHandle(), pInterfaceMT->GetInstantiation()[0]);
2260}
2261
2262TypeHandle::CastResult STDCALL ObjIsInstanceOfNoGC(Object *pObject, TypeHandle toTypeHnd)
2263{
2264 CONTRACTL {
2265 NOTHROW;
2266 GC_NOTRIGGER;
2267 MODE_COOPERATIVE;
2268 SO_TOLERANT;
2269 PRECONDITION(CheckPointer(pObject));
2270 } CONTRACTL_END;
2271
2272
2273 MethodTable *pMT = pObject->GetMethodTable();
2274
2275 // Quick exact match first
2276 if (TypeHandle(pMT) == toTypeHnd)
2277 return TypeHandle::CanCast;
2278
2279 if ((toTypeHnd.IsInterface() && ( pMT->IsComObjectType() || pMT->IsICastable())))
2280 {
2281 return TypeHandle::MaybeCast;
2282 }
2283
2284 if (pMT->IsArray())
2285 {
2286 if (toTypeHnd.IsArray())
2287 return ArrayIsInstanceOfNoGC(pObject, toTypeHnd);
2288
2289 if (toTypeHnd.IsInterface())
2290 {
2291 MethodTable * pInterfaceMT = toTypeHnd.AsMethodTable();
2292 if (pInterfaceMT->HasInstantiation())
2293 return ArrayObjSupportsBizarreInterfaceNoGC(pObject, pInterfaceMT);
2294 return pMT->ImplementsInterface(pInterfaceMT) ? TypeHandle::CanCast : TypeHandle::CannotCast;
2295 }
2296
2297 if (toTypeHnd == TypeHandle(g_pObjectClass) || toTypeHnd == TypeHandle(g_pArrayClass))
2298 return TypeHandle::CanCast;
2299
2300 return TypeHandle::CannotCast;
2301 }
2302
2303 if (toTypeHnd.IsTypeDesc())
2304 return TypeHandle::CannotCast;
2305
2306 // allow an object of type T to be cast to Nullable<T> (they have the same representation)
2307 if (Nullable::IsNullableForTypeNoGC(toTypeHnd, pMT))
2308 return TypeHandle::CanCast;
2309
2310 return pMT->CanCastToClassOrInterfaceNoGC(toTypeHnd.AsMethodTable());
2311}
2312
2313BOOL ObjIsInstanceOf(Object *pObject, TypeHandle toTypeHnd, BOOL throwCastException)
2314{
2315 CONTRACTL {
2316 THROWS;
2317 GC_TRIGGERS;
2318 MODE_COOPERATIVE;
2319 PRECONDITION(CheckPointer(pObject));
2320 } CONTRACTL_END;
2321
2322 BOOL fCast = FALSE;
2323
2324 OBJECTREF obj = ObjectToOBJECTREF(pObject);
2325
2326 GCPROTECT_BEGIN(obj);
2327
2328 TypeHandle fromTypeHnd = obj->GetTypeHandle();
2329
2330 // If we are trying to cast a proxy we need to delegate to remoting
2331 // services which will determine whether the proxy and the type are compatible.
2332 // Start by doing a quick static cast check to see if the type information captured in
2333 // the metadata indicates that the cast is legal.
2334 if (fromTypeHnd.CanCastTo(toTypeHnd))
2335 {
2336 fCast = TRUE;
2337 }
2338 else
2339#ifdef FEATURE_COMINTEROP
2340 // If we are casting a COM object from interface then we need to do a check to see
2341 // if it implements the interface.
2342 if (toTypeHnd.IsInterface() && fromTypeHnd.GetMethodTable()->IsComObjectType())
2343 {
2344 fCast = ComObject::SupportsInterface(obj, toTypeHnd.AsMethodTable());
2345 }
2346 else
2347#endif // FEATURE_COMINTEROP
2348 if (Nullable::IsNullableForType(toTypeHnd, obj->GetMethodTable()))
2349 {
2350 // allow an object of type T to be cast to Nullable<T> (they have the same representation)
2351 fCast = TRUE;
2352 }
2353#ifdef FEATURE_ICASTABLE
2354 // If type implements ICastable interface we give it a chance to tell us if it can be casted
2355 // to a given type.
2356 else if (toTypeHnd.IsInterface() && fromTypeHnd.GetMethodTable()->IsICastable())
2357 {
2358 // Make actuall call to ICastableHelpers.IsInstanceOfInterface(obj, interfaceTypeObj, out exception)
2359 OBJECTREF exception = NULL;
2360 GCPROTECT_BEGIN(exception);
2361
2362 PREPARE_NONVIRTUAL_CALLSITE(METHOD__ICASTABLEHELPERS__ISINSTANCEOF);
2363
2364 OBJECTREF managedType = toTypeHnd.GetManagedClassObject(); //GC triggers
2365
2366 DECLARE_ARGHOLDER_ARRAY(args, 3);
2367 args[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER(obj);
2368 args[ARGNUM_1] = OBJECTREF_TO_ARGHOLDER(managedType);
2369 args[ARGNUM_2] = PTR_TO_ARGHOLDER(&exception);
2370
2371 CALL_MANAGED_METHOD(fCast, BOOL, args);
2372 INDEBUG(managedType = NULL); // managedType isn't protected during the call
2373
2374 if (!fCast && throwCastException && exception != NULL)
2375 {
2376 RealCOMPlusThrow(exception);
2377 }
2378 GCPROTECT_END(); //exception
2379 }
2380#endif // FEATURE_ICASTABLE
2381
2382 if (!fCast && throwCastException)
2383 {
2384 COMPlusThrowInvalidCastException(&obj, toTypeHnd);
2385 }
2386
2387 GCPROTECT_END(); // obj
2388
2389 return(fCast);
2390}
2391
2392//
2393// This optimization is intended for all non-framed casting helpers
2394//
2395
2396#include <optsmallperfcritical.h>
2397
2398HCIMPL2(Object*, JIT_ChkCastClass_Portable, MethodTable* pTargetMT, Object* pObject)
2399{
2400 FCALL_CONTRACT;
2401
2402 //
2403 // casts pObject to type pMT
2404 //
2405
2406 if (NULL == pObject)
2407 {
2408 return NULL;
2409 }
2410
2411 PTR_VOID pMT = pObject->GetMethodTable();
2412
2413 do {
2414 if (pMT == pTargetMT)
2415 return pObject;
2416
2417 pMT = MethodTable::GetParentMethodTableOrIndirection(pMT);
2418 } while (pMT);
2419
2420 ENDFORBIDGC();
2421 return HCCALL2(JITutil_ChkCastAny, CORINFO_CLASS_HANDLE(pTargetMT), pObject);
2422}
2423HCIMPLEND
2424
2425//
2426// This helper assumes that the check for the trivial cases has been inlined by the JIT.
2427//
2428HCIMPL2(Object*, JIT_ChkCastClassSpecial_Portable, MethodTable* pTargetMT, Object* pObject)
2429{
2430 CONTRACTL {
2431 FCALL_CHECK;
2432 // This assumes that the check for the trivial cases has been inlined by the JIT.
2433 PRECONDITION(pObject != NULL);
2434 PRECONDITION(pObject->GetMethodTable() != pTargetMT);
2435 } CONTRACTL_END;
2436
2437 PTR_VOID pMT = MethodTable::GetParentMethodTableOrIndirection(pObject->GetMethodTable());
2438
2439 while (pMT)
2440 {
2441 if (pMT == pTargetMT)
2442 return pObject;
2443
2444 pMT = MethodTable::GetParentMethodTableOrIndirection(pMT);
2445 }
2446
2447 ENDFORBIDGC();
2448 return HCCALL2(JITutil_ChkCastAny, CORINFO_CLASS_HANDLE(pTargetMT), pObject);
2449}
2450HCIMPLEND
2451
2452HCIMPL2(Object*, JIT_IsInstanceOfClass_Portable, MethodTable* pTargetMT, Object* pObject)
2453{
2454 FCALL_CONTRACT;
2455
2456 //
2457 // casts pObject to type pMT
2458 //
2459
2460 if (NULL == pObject)
2461 {
2462 return NULL;
2463 }
2464
2465 PTR_VOID pMT = pObject->GetMethodTable();
2466
2467 do {
2468 if (pMT == pTargetMT)
2469 return pObject;
2470
2471 pMT = MethodTable::GetParentMethodTableOrIndirection(pMT);
2472 } while (pMT);
2473
2474 if (!pObject->GetMethodTable()->HasTypeEquivalence())
2475 {
2476 return NULL;
2477 }
2478
2479 ENDFORBIDGC();
2480 return HCCALL2(JITutil_IsInstanceOfAny, CORINFO_CLASS_HANDLE(pTargetMT), pObject);
2481}
2482HCIMPLEND
2483
2484HCIMPL2(Object*, JIT_ChkCastInterface_Portable, MethodTable *pInterfaceMT, Object* pObject)
2485{
2486 CONTRACTL {
2487 FCALL_CHECK;
2488 PRECONDITION(pInterfaceMT->IsInterface());
2489 } CONTRACTL_END;
2490
2491 if (NULL == pObject)
2492 {
2493 return pObject;
2494 }
2495
2496 if (pObject->GetMethodTable()->ImplementsInterfaceInline(pInterfaceMT))
2497 {
2498 return pObject;
2499 }
2500
2501 ENDFORBIDGC();
2502 return HCCALL2(JITutil_ChkCastInterface, pInterfaceMT, pObject);
2503}
2504HCIMPLEND
2505
2506HCIMPL2(Object*, JIT_IsInstanceOfInterface_Portable, MethodTable *pInterfaceMT, Object* pObject)
2507{
2508 CONTRACTL {
2509 FCALL_CHECK;
2510 PRECONDITION(pInterfaceMT->IsInterface());
2511 } CONTRACTL_END;
2512
2513 if (NULL == pObject)
2514 {
2515 return NULL;
2516 }
2517
2518 if (pObject->GetMethodTable()->ImplementsInterfaceInline(pInterfaceMT))
2519 {
2520 return pObject;
2521 }
2522
2523 if (!pObject->GetMethodTable()->InstanceRequiresNonTrivialInterfaceCast())
2524 {
2525 return NULL;
2526 }
2527
2528 ENDFORBIDGC();
2529 return HCCALL2(JITutil_IsInstanceOfInterface, pInterfaceMT, pObject);
2530}
2531HCIMPLEND
2532
2533HCIMPL2(Object *, JIT_ChkCastArray, CORINFO_CLASS_HANDLE type, Object *pObject)
2534{
2535 CONTRACTL {
2536 FCALL_CHECK;
2537 PRECONDITION(TypeHandle(type).IsArray());
2538 } CONTRACTL_END;
2539
2540 if (pObject == NULL)
2541 {
2542 return NULL;
2543 }
2544
2545 OBJECTREF refObj = ObjectToOBJECTREF(pObject);
2546 VALIDATEOBJECTREF(refObj);
2547
2548 TypeHandle::CastResult result = refObj->GetMethodTable()->IsArray() ?
2549 ArrayIsInstanceOfNoGC(pObject, TypeHandle(type)) : TypeHandle::CannotCast;
2550
2551 if (result == TypeHandle::CanCast)
2552 {
2553 return pObject;
2554 }
2555
2556 ENDFORBIDGC();
2557 Object* pRet = HCCALL2(JITutil_ChkCastAny, type, pObject);
2558 // Make sure that the fast helper have not lied
2559 _ASSERTE(result != TypeHandle::CannotCast);
2560 return pRet;
2561}
2562HCIMPLEND
2563
2564
2565HCIMPL2(Object *, JIT_IsInstanceOfArray, CORINFO_CLASS_HANDLE type, Object *pObject)
2566{
2567 CONTRACTL {
2568 FCALL_CHECK;
2569 PRECONDITION(TypeHandle(type).IsArray());
2570 } CONTRACTL_END;
2571
2572 if (pObject == NULL)
2573 {
2574 return NULL;
2575 }
2576
2577 OBJECTREF refObj = ObjectToOBJECTREF(pObject);
2578 VALIDATEOBJECTREF(refObj);
2579 MethodTable *pMT = refObj->GetMethodTable();
2580
2581 if (!pMT->IsArray())
2582 {
2583 // We know that the clsHnd is an array so check the object. If it is not an array return null
2584 return NULL;
2585 }
2586 else
2587 {
2588 switch (ArrayIsInstanceOfNoGC(pObject, TypeHandle(type))) {
2589 case TypeHandle::CanCast:
2590 return pObject;
2591 case TypeHandle::CannotCast:
2592 return NULL;
2593 default:
2594 // fall through to the slow helper
2595 break;
2596 }
2597 }
2598
2599 ENDFORBIDGC();
2600 return HCCALL2(JITutil_IsInstanceOfAny, type, pObject);
2601}
2602HCIMPLEND
2603
2604/*********************************************************************/
2605// IsInstanceOf test used for unusual cases (naked type parameters, variant generic types)
2606// Unlike the IsInstanceOfInterface, IsInstanceOfClass, and IsIsntanceofArray functions,
2607// this test must deal with all kinds of type tests
2608HCIMPL2(Object *, JIT_IsInstanceOfAny, CORINFO_CLASS_HANDLE type, Object* obj)
2609{
2610 FCALL_CONTRACT;
2611
2612 if (NULL == obj)
2613 {
2614 return NULL;
2615 }
2616
2617 switch (ObjIsInstanceOfNoGC(obj, TypeHandle(type))) {
2618 case TypeHandle::CanCast:
2619 return obj;
2620 case TypeHandle::CannotCast:
2621 return NULL;
2622 default:
2623 // fall through to the slow helper
2624 break;
2625 }
2626
2627 ENDFORBIDGC();
2628 return HCCALL2(JITutil_IsInstanceOfAny, type, obj);
2629}
2630HCIMPLEND
2631
2632// ChkCast test used for unusual cases (naked type parameters, variant generic types)
2633// Unlike the ChkCastInterface, ChkCastClass, and ChkCastArray functions,
2634// this test must deal with all kinds of type tests
2635HCIMPL2(Object *, JIT_ChkCastAny, CORINFO_CLASS_HANDLE type, Object *obj)
2636{
2637 FCALL_CONTRACT;
2638
2639 if (NULL == obj)
2640 {
2641 return NULL;
2642 }
2643
2644 TypeHandle::CastResult result = ObjIsInstanceOfNoGC(obj, TypeHandle(type));
2645
2646 if (result == TypeHandle::CanCast)
2647 {
2648 return obj;
2649 }
2650
2651 ENDFORBIDGC();
2652 Object* pRet = HCCALL2(JITutil_ChkCastAny, type, obj);
2653 // Make sure that the fast helper have not lied
2654 _ASSERTE(result != TypeHandle::CannotCast);
2655 return pRet;
2656}
2657HCIMPLEND
2658
2659
2660NOINLINE HCIMPL2(Object *, JITutil_IsInstanceOfInterface, MethodTable *pInterfaceMT, Object* obj)
2661{
2662 FCALL_CONTRACT;
2663
2664 if (obj->GetMethodTable()->IsArray())
2665 {
2666 switch (ArrayObjSupportsBizarreInterfaceNoGC(obj, pInterfaceMT)) {
2667 case TypeHandle::CanCast:
2668 return obj;
2669 case TypeHandle::CannotCast:
2670 return NULL;
2671 default:
2672 // fall through to the slow helper
2673 break;
2674 }
2675 }
2676
2677 ENDFORBIDGC();
2678 return HCCALL2(JITutil_IsInstanceOfAny, CORINFO_CLASS_HANDLE(pInterfaceMT), obj);
2679
2680}
2681HCIMPLEND
2682
2683NOINLINE HCIMPL2(Object *, JITutil_ChkCastInterface, MethodTable *pInterfaceMT, Object *obj)
2684{
2685 FCALL_CONTRACT;
2686
2687 if (obj->GetMethodTable()->IsArray())
2688 {
2689 if (ArrayObjSupportsBizarreInterfaceNoGC(obj, pInterfaceMT) == TypeHandle::CanCast)
2690 {
2691 return obj;
2692 }
2693 }
2694
2695 ENDFORBIDGC();
2696 return HCCALL2(JITutil_ChkCastAny, CORINFO_CLASS_HANDLE(pInterfaceMT), obj);
2697}
2698HCIMPLEND
2699
2700
2701#include <optdefault.h>
2702
2703
2704//
2705// Framed helpers
2706//
2707NOINLINE HCIMPL2(Object *, JITutil_ChkCastAny, CORINFO_CLASS_HANDLE type, Object *obj)
2708{
2709 FCALL_CONTRACT;
2710
2711 // This case should be handled by frameless helper
2712 _ASSERTE(obj != NULL);
2713
2714 OBJECTREF oref = ObjectToOBJECTREF (obj);
2715 VALIDATEOBJECTREF(oref);
2716
2717 TypeHandle clsHnd(type);
2718
2719 HELPER_METHOD_FRAME_BEGIN_RET_1(oref);
2720 if (!ObjIsInstanceOf(OBJECTREFToObject(oref), clsHnd, TRUE))
2721 {
2722 UNREACHABLE(); //ObjIsInstanceOf will throw if cast can't be done
2723 }
2724 HELPER_METHOD_FRAME_END();
2725
2726 return OBJECTREFToObject(oref);
2727}
2728HCIMPLEND
2729
2730NOINLINE HCIMPL2(Object *, JITutil_IsInstanceOfAny, CORINFO_CLASS_HANDLE type, Object *obj)
2731{
2732 FCALL_CONTRACT;
2733
2734 // This case should be handled by frameless helper
2735 _ASSERTE(obj != NULL);
2736
2737 OBJECTREF oref = ObjectToOBJECTREF (obj);
2738 VALIDATEOBJECTREF(oref);
2739
2740 TypeHandle clsHnd(type);
2741
2742 HELPER_METHOD_FRAME_BEGIN_RET_1(oref);
2743 if (!ObjIsInstanceOf(OBJECTREFToObject(oref), clsHnd))
2744 oref = NULL;
2745 HELPER_METHOD_FRAME_END();
2746
2747 return OBJECTREFToObject(oref);
2748}
2749HCIMPLEND
2750
2751
2752
2753//========================================================================
2754//
2755// ALLOCATION HELPERS
2756//
2757//========================================================================
2758
2759#include <optsmallperfcritical.h>
2760
2761//*************************************************************
2762// Allocation fast path for typical objects
2763//
2764HCIMPL1(Object*, JIT_NewS_MP_FastPortable, CORINFO_CLASS_HANDLE typeHnd_)
2765{
2766 FCALL_CONTRACT;
2767
2768 do
2769 {
2770 _ASSERTE(GCHeapUtilities::UseThreadAllocationContexts());
2771
2772 // This is typically the only call in the fast path. Making the call early seems to be better, as it allows the compiler
2773 // to use volatile registers for intermediate values. This reduces the number of push/pop instructions and eliminates
2774 // some reshuffling of intermediate values into nonvolatile registers around the call.
2775 Thread *thread = GetThread();
2776
2777 TypeHandle typeHandle(typeHnd_);
2778 _ASSERTE(!typeHandle.IsTypeDesc());
2779 MethodTable *methodTable = typeHandle.AsMethodTable();
2780
2781 SIZE_T size = methodTable->GetBaseSize();
2782 _ASSERTE(size % DATA_ALIGNMENT == 0);
2783
2784 gc_alloc_context *allocContext = thread->GetAllocContext();
2785 BYTE *allocPtr = allocContext->alloc_ptr;
2786 _ASSERTE(allocPtr <= allocContext->alloc_limit);
2787 if (size > static_cast<SIZE_T>(allocContext->alloc_limit - allocPtr))
2788 {
2789 break;
2790 }
2791 allocContext->alloc_ptr = allocPtr + size;
2792
2793 _ASSERTE(allocPtr != nullptr);
2794 Object *object = reinterpret_cast<Object *>(allocPtr);
2795 _ASSERTE(object->HasEmptySyncBlockInfo());
2796 object->SetMethodTable(methodTable);
2797
2798 return object;
2799 } while (false);
2800
2801 // Tail call to the slow helper
2802 ENDFORBIDGC();
2803 return HCCALL1(JIT_New, typeHnd_);
2804}
2805HCIMPLEND
2806
2807#include <optdefault.h>
2808
2809/*************************************************************/
2810HCIMPL1(Object*, JIT_New, CORINFO_CLASS_HANDLE typeHnd_)
2811{
2812 FCALL_CONTRACT;
2813
2814 OBJECTREF newobj = NULL;
2815 HELPER_METHOD_FRAME_BEGIN_RET_0(); // Set up a frame
2816
2817 TypeHandle typeHnd(typeHnd_);
2818
2819 _ASSERTE(!typeHnd.IsTypeDesc()); // we never use this helper for arrays
2820 MethodTable *pMT = typeHnd.AsMethodTable();
2821 _ASSERTE(pMT->IsRestored_NoLogging());
2822
2823#ifdef _DEBUG
2824 if (g_pConfig->FastGCStressLevel()) {
2825 GetThread()->DisableStressHeap();
2826 }
2827#endif // _DEBUG
2828
2829 newobj = AllocateObject(pMT);
2830
2831 HELPER_METHOD_FRAME_END();
2832 return(OBJECTREFToObject(newobj));
2833}
2834HCIMPLEND
2835
2836
2837
2838//========================================================================
2839//
2840// STRING HELPERS
2841//
2842//========================================================================
2843
2844#include <optsmallperfcritical.h>
2845
2846//*************************************************************
2847// Allocation fast path for typical objects
2848//
2849HCIMPL1(StringObject*, AllocateString_MP_FastPortable, DWORD stringLength)
2850{
2851 FCALL_CONTRACT;
2852
2853 do
2854 {
2855 _ASSERTE(GCHeapUtilities::UseThreadAllocationContexts());
2856
2857 // Instead of doing elaborate overflow checks, we just limit the number of elements. This will avoid all overflow
2858 // problems, as well as making sure big string objects are correctly allocated in the big object heap.
2859 if (stringLength >= (LARGE_OBJECT_SIZE - 256) / sizeof(WCHAR))
2860 {
2861 break;
2862 }
2863
2864 // This is typically the only call in the fast path. Making the call early seems to be better, as it allows the compiler
2865 // to use volatile registers for intermediate values. This reduces the number of push/pop instructions and eliminates
2866 // some reshuffling of intermediate values into nonvolatile registers around the call.
2867 Thread *thread = GetThread();
2868
2869 SIZE_T totalSize = StringObject::GetSize(stringLength);
2870
2871 // The method table's base size includes space for a terminating null character
2872 _ASSERTE(totalSize >= g_pStringClass->GetBaseSize());
2873 _ASSERTE((totalSize - g_pStringClass->GetBaseSize()) / sizeof(WCHAR) == stringLength);
2874
2875 SIZE_T alignedTotalSize = ALIGN_UP(totalSize, DATA_ALIGNMENT);
2876 _ASSERTE(alignedTotalSize >= totalSize);
2877 totalSize = alignedTotalSize;
2878
2879 gc_alloc_context *allocContext = thread->GetAllocContext();
2880 BYTE *allocPtr = allocContext->alloc_ptr;
2881 _ASSERTE(allocPtr <= allocContext->alloc_limit);
2882 if (totalSize > static_cast<SIZE_T>(allocContext->alloc_limit - allocPtr))
2883 {
2884 break;
2885 }
2886 allocContext->alloc_ptr = allocPtr + totalSize;
2887
2888 _ASSERTE(allocPtr != nullptr);
2889 StringObject *stringObject = reinterpret_cast<StringObject *>(allocPtr);
2890 stringObject->SetMethodTable(g_pStringClass);
2891 stringObject->SetStringLength(stringLength);
2892 _ASSERTE(stringObject->GetBuffer()[stringLength] == W('\0'));
2893
2894 return stringObject;
2895 } while (false);
2896
2897 // Tail call to the slow helper
2898 ENDFORBIDGC();
2899 return HCCALL1(FramedAllocateString, stringLength);
2900}
2901HCIMPLEND
2902
2903#include <optdefault.h>
2904
2905/*********************************************************************/
2906/* We don't use HCIMPL macros because this is not a real helper call */
2907/* This function just needs mangled arguments like a helper call */
2908
2909HCIMPL1_RAW(StringObject*, UnframedAllocateString, DWORD stringLength)
2910{
2911 // This isn't _really_ an FCALL and therefore shouldn't have the
2912 // SO_TOLERANT part of the FCALL_CONTRACT b/c it is not entered
2913 // from managed code.
2914 CONTRACTL {
2915 THROWS;
2916 GC_TRIGGERS;
2917 MODE_COOPERATIVE;
2918 SO_INTOLERANT;
2919 } CONTRACTL_END;
2920
2921 STRINGREF result;
2922 result = SlowAllocateString(stringLength);
2923
2924 return((StringObject*) OBJECTREFToObject(result));
2925}
2926HCIMPLEND_RAW
2927
2928HCIMPL1(StringObject*, FramedAllocateString, DWORD stringLength)
2929{
2930 FCALL_CONTRACT;
2931
2932 STRINGREF result = NULL;
2933 HELPER_METHOD_FRAME_BEGIN_RET_0(); // Set up a frame
2934
2935 result = SlowAllocateString(stringLength);
2936
2937 HELPER_METHOD_FRAME_END();
2938 return((StringObject*) OBJECTREFToObject(result));
2939}
2940HCIMPLEND
2941
2942/*********************************************************************/
2943OBJECTHANDLE ConstructStringLiteral(CORINFO_MODULE_HANDLE scopeHnd, mdToken metaTok)
2944{
2945 CONTRACTL {
2946 THROWS;
2947 GC_TRIGGERS;
2948 MODE_ANY;
2949 } CONTRACTL_END;
2950
2951 _ASSERTE(TypeFromToken(metaTok) == mdtString);
2952
2953 Module* module = GetModule(scopeHnd);
2954
2955
2956 // If our module is ngenned and we're calling this API, it means that we're not going through
2957 // the fixup mechanism for strings. This can happen 2 ways:
2958 //
2959 // a) Lazy string object construction: This happens when JIT decides that initizalizing a
2960 // string via fixup on method entry is very expensive. This is normally done for strings
2961 // that appear in rarely executed blocks, such as throw blocks.
2962 //
2963 // b) The ngen image isn't complete (it's missing classes), therefore we're jitting methods.
2964 //
2965 // If we went ahead and called ResolveStringRef directly, we would be breaking the per module
2966 // interning we're guaranteeing, so we will have to detect the case and handle it appropriately.
2967#ifdef FEATURE_PREJIT
2968 if (module->HasNativeImage() && module->IsNoStringInterning())
2969 {
2970 return module->ResolveStringRef(metaTok, module->GetAssembly()->Parent(), true);
2971 }
2972#endif
2973 return module->ResolveStringRef(metaTok, module->GetAssembly()->Parent(), false);
2974}
2975
2976/*********************************************************************/
2977HCIMPL2(Object *, JIT_StrCns, unsigned rid, CORINFO_MODULE_HANDLE scopeHnd)
2978{
2979 FCALL_CONTRACT;
2980
2981 OBJECTHANDLE hndStr = 0;
2982
2983 HELPER_METHOD_FRAME_BEGIN_RET_0();
2984
2985 // Retrieve the handle to the COM+ string object.
2986 hndStr = ConstructStringLiteral(scopeHnd, RidToToken(rid, mdtString));
2987 HELPER_METHOD_FRAME_END();
2988
2989 // Don't use ObjectFromHandle; this isn't a real handle
2990 return *(Object**)hndStr;
2991}
2992HCIMPLEND
2993
2994
2995//========================================================================
2996//
2997// ARRAY HELPERS
2998//
2999//========================================================================
3000
3001#include <optsmallperfcritical.h>
3002
3003//*************************************************************
3004// Array allocation fast path for arrays of value type elements
3005//
3006HCIMPL2(Object*, JIT_NewArr1VC_MP_FastPortable, CORINFO_CLASS_HANDLE arrayMT, INT_PTR size)
3007{
3008 FCALL_CONTRACT;
3009
3010 do
3011 {
3012 _ASSERTE(GCHeapUtilities::UseThreadAllocationContexts());
3013
3014 // Do a conservative check here. This is to avoid overflow while doing the calculations. We don't
3015 // have to worry about "large" objects, since the allocation quantum is never big enough for
3016 // LARGE_OBJECT_SIZE.
3017 //
3018 // For Value Classes, this needs to be 2^16 - slack (2^32 / max component size),
3019 // The slack includes the size for the array header and round-up ; for alignment. Use 256 for the
3020 // slack value out of laziness.
3021 SIZE_T componentCount = static_cast<SIZE_T>(size);
3022 if (componentCount >= static_cast<SIZE_T>(65535 - 256))
3023 {
3024 break;
3025 }
3026
3027 // This is typically the only call in the fast path. Making the call early seems to be better, as it allows the compiler
3028 // to use volatile registers for intermediate values. This reduces the number of push/pop instructions and eliminates
3029 // some reshuffling of intermediate values into nonvolatile registers around the call.
3030 Thread *thread = GetThread();
3031
3032 MethodTable *pArrayMT = (MethodTable *)arrayMT;
3033
3034 _ASSERTE(pArrayMT->HasComponentSize());
3035 SIZE_T componentSize = pArrayMT->RawGetComponentSize();
3036 SIZE_T totalSize = componentCount * componentSize;
3037 _ASSERTE(totalSize / componentSize == componentCount);
3038
3039 SIZE_T baseSize = pArrayMT->GetBaseSize();
3040 totalSize += baseSize;
3041 _ASSERTE(totalSize >= baseSize);
3042
3043 SIZE_T alignedTotalSize = ALIGN_UP(totalSize, DATA_ALIGNMENT);
3044 _ASSERTE(alignedTotalSize >= totalSize);
3045 totalSize = alignedTotalSize;
3046
3047 gc_alloc_context *allocContext = thread->GetAllocContext();
3048 BYTE *allocPtr = allocContext->alloc_ptr;
3049 _ASSERTE(allocPtr <= allocContext->alloc_limit);
3050 if (totalSize > static_cast<SIZE_T>(allocContext->alloc_limit - allocPtr))
3051 {
3052 break;
3053 }
3054 allocContext->alloc_ptr = allocPtr + totalSize;
3055
3056 _ASSERTE(allocPtr != nullptr);
3057 ArrayBase *array = reinterpret_cast<ArrayBase *>(allocPtr);
3058 array->SetArrayMethodTable(pArrayMT);
3059 _ASSERTE(static_cast<DWORD>(componentCount) == componentCount);
3060 array->m_NumComponents = static_cast<DWORD>(componentCount);
3061
3062 return array;
3063 } while (false);
3064
3065 // Tail call to the slow helper
3066 ENDFORBIDGC();
3067 return HCCALL2(JIT_NewArr1, arrayMT, size);
3068}
3069HCIMPLEND
3070
3071//*************************************************************
3072// Array allocation fast path for arrays of object elements
3073//
3074HCIMPL2(Object*, JIT_NewArr1OBJ_MP_FastPortable, CORINFO_CLASS_HANDLE arrayMT, INT_PTR size)
3075{
3076 FCALL_CONTRACT;
3077
3078 do
3079 {
3080 _ASSERTE(GCHeapUtilities::UseThreadAllocationContexts());
3081
3082 // Make sure that the total size cannot reach LARGE_OBJECT_SIZE, which also allows us to avoid overflow checks. The
3083 // "256" slack is to cover the array header size and round-up, using a constant value here out of laziness.
3084 SIZE_T componentCount = static_cast<SIZE_T>(size);
3085 if (componentCount >= static_cast<SIZE_T>((LARGE_OBJECT_SIZE - 256) / sizeof(void *)))
3086 {
3087 break;
3088 }
3089
3090 // This is typically the only call in the fast path. Making the call early seems to be better, as it allows the compiler
3091 // to use volatile registers for intermediate values. This reduces the number of push/pop instructions and eliminates
3092 // some reshuffling of intermediate values into nonvolatile registers around the call.
3093 Thread *thread = GetThread();
3094
3095 SIZE_T totalSize = componentCount * sizeof(void *);
3096 _ASSERTE(totalSize / sizeof(void *) == componentCount);
3097
3098 MethodTable *pArrayMT = (MethodTable *)arrayMT;
3099
3100 SIZE_T baseSize = pArrayMT->GetBaseSize();
3101 totalSize += baseSize;
3102 _ASSERTE(totalSize >= baseSize);
3103
3104 _ASSERTE(ALIGN_UP(totalSize, DATA_ALIGNMENT) == totalSize);
3105
3106 gc_alloc_context *allocContext = thread->GetAllocContext();
3107 BYTE *allocPtr = allocContext->alloc_ptr;
3108 _ASSERTE(allocPtr <= allocContext->alloc_limit);
3109 if (totalSize > static_cast<SIZE_T>(allocContext->alloc_limit - allocPtr))
3110 {
3111 break;
3112 }
3113 allocContext->alloc_ptr = allocPtr + totalSize;
3114
3115 _ASSERTE(allocPtr != nullptr);
3116 ArrayBase *array = reinterpret_cast<ArrayBase *>(allocPtr);
3117 array->SetArrayMethodTable(pArrayMT);
3118 _ASSERTE(static_cast<DWORD>(componentCount) == componentCount);
3119 array->m_NumComponents = static_cast<DWORD>(componentCount);
3120
3121 return array;
3122 } while (false);
3123
3124 // Tail call to the slow helper
3125 ENDFORBIDGC();
3126 return HCCALL2(JIT_NewArr1, arrayMT, size);
3127}
3128HCIMPLEND
3129
3130//*************************************************************
3131// R2R-specific array allocation wrapper that extracts array method table from ArrayTypeDesc
3132//
3133HCIMPL2(Object*, JIT_NewArr1_R2R, CORINFO_CLASS_HANDLE arrayTypeHnd_, INT_PTR size)
3134{
3135 FCALL_CONTRACT;
3136
3137 TypeHandle arrayTypeHandle(arrayTypeHnd_);
3138 ArrayTypeDesc *pArrayTypeDesc = arrayTypeHandle.AsArray();
3139 MethodTable *pArrayMT = pArrayTypeDesc->GetTemplateMethodTable();
3140
3141 ENDFORBIDGC();
3142 return HCCALL2(JIT_NewArr1, (CORINFO_CLASS_HANDLE)pArrayMT, size);
3143}
3144HCIMPLEND
3145
3146#include <optdefault.h>
3147
3148/*************************************************************/
3149HCIMPL2(Object*, JIT_NewArr1, CORINFO_CLASS_HANDLE arrayMT, INT_PTR size)
3150{
3151 FCALL_CONTRACT;
3152
3153 OBJECTREF newArray = NULL;
3154
3155 HELPER_METHOD_FRAME_BEGIN_RET_0(); // Set up a frame
3156
3157 MethodTable *pArrayMT = (MethodTable *)arrayMT;
3158
3159 _ASSERTE(pArrayMT->IsFullyLoaded());
3160 _ASSERTE(pArrayMT->IsArray());
3161 _ASSERTE(!pArrayMT->IsMultiDimArray());
3162
3163 if (size < 0)
3164 COMPlusThrow(kOverflowException);
3165
3166#ifdef _WIN64
3167 // Even though ECMA allows using a native int as the argument to newarr instruction
3168 // (therefore size is INT_PTR), ArrayBase::m_NumComponents is 32-bit, so even on 64-bit
3169 // platforms we can't create an array whose size exceeds 32 bits.
3170 if (size > INT_MAX)
3171 EX_THROW(EEMessageException, (kOverflowException, IDS_EE_ARRAY_DIMENSIONS_EXCEEDED));
3172#endif
3173
3174 //
3175 // is this a primitive type?
3176 //
3177
3178 CorElementType elemType = pArrayMT->GetArrayElementType();
3179
3180 if (CorTypeInfo::IsPrimitiveType(elemType)
3181#ifdef FEATURE_64BIT_ALIGNMENT
3182 // On platforms where 64-bit types require 64-bit alignment and don't obtain it naturally force us
3183 // through the slow path where this will be handled.
3184 && (elemType != ELEMENT_TYPE_I8)
3185 && (elemType != ELEMENT_TYPE_U8)
3186 && (elemType != ELEMENT_TYPE_R8)
3187#endif
3188 )
3189 {
3190#ifdef _DEBUG
3191 if (g_pConfig->FastGCStressLevel()) {
3192 GetThread()->DisableStressHeap();
3193 }
3194#endif // _DEBUG
3195
3196 // Disallow the creation of void[] (an array of System.Void)
3197 if (elemType == ELEMENT_TYPE_VOID)
3198 COMPlusThrow(kArgumentException);
3199
3200 BOOL bAllocateInLargeHeap = FALSE;
3201#ifdef FEATURE_DOUBLE_ALIGNMENT_HINT
3202 if ((elemType == ELEMENT_TYPE_R8) &&
3203 (static_cast<DWORD>(size) >= g_pConfig->GetDoubleArrayToLargeObjectHeapThreshold()))
3204 {
3205 STRESS_LOG1(LF_GC, LL_INFO10, "Allocating double array of size %d to large object heap\n", size);
3206 bAllocateInLargeHeap = TRUE;
3207 }
3208#endif
3209
3210 if (g_pPredefinedArrayTypes[elemType] == NULL)
3211 {
3212 TypeHandle elemTypeHnd = TypeHandle(MscorlibBinder::GetElementType(elemType));
3213
3214 g_pPredefinedArrayTypes[elemType] = ClassLoader::LoadArrayTypeThrowing(elemTypeHnd, ELEMENT_TYPE_SZARRAY, 0).AsArray();
3215 }
3216
3217 newArray = FastAllocatePrimitiveArray(pArrayMT, static_cast<DWORD>(size), bAllocateInLargeHeap);
3218 }
3219 else
3220 {
3221#ifdef _DEBUG
3222 if (g_pConfig->FastGCStressLevel()) {
3223 GetThread()->DisableStressHeap();
3224 }
3225#endif // _DEBUG
3226 INT32 size32 = (INT32)size;
3227 newArray = AllocateArrayEx(pArrayMT, &size32, 1);
3228 }
3229
3230 HELPER_METHOD_FRAME_END();
3231
3232 return(OBJECTREFToObject(newArray));
3233}
3234HCIMPLEND
3235
3236/*********************************************************************
3237// Allocate a multi-dimensional array
3238*/
3239OBJECTREF allocNewMDArr(TypeHandle typeHnd, unsigned dwNumArgs, va_list args)
3240{
3241 CONTRACTL {
3242 THROWS;
3243 GC_TRIGGERS;
3244 MODE_COOPERATIVE;
3245 PRECONDITION(dwNumArgs > 0);
3246 } CONTRACTL_END;
3247
3248 // Get the arguments in the right order
3249
3250 INT32* fwdArgList;
3251
3252#ifdef _TARGET_X86_
3253 fwdArgList = (INT32*)args;
3254
3255 // reverse the order
3256 INT32* p = fwdArgList;
3257 INT32* q = fwdArgList + (dwNumArgs-1);
3258 while (p < q)
3259 {
3260 INT32 t = *p; *p = *q; *q = t;
3261 p++; q--;
3262 }
3263#else
3264 // create an array where fwdArgList[0] == arg[0] ...
3265 fwdArgList = (INT32*) _alloca(dwNumArgs * sizeof(INT32));
3266 for (unsigned i = 0; i < dwNumArgs; i++)
3267 {
3268 fwdArgList[i] = va_arg(args, INT32);
3269 }
3270#endif
3271
3272 return AllocateArrayEx(typeHnd, fwdArgList, dwNumArgs);
3273}
3274
3275/*********************************************************************
3276// Allocate a multi-dimensional array with lower bounds specified.
3277// The caller pushes both sizes AND/OR bounds for every dimension
3278*/
3279
3280HCIMPL2VA(Object*, JIT_NewMDArr, CORINFO_CLASS_HANDLE classHnd, unsigned dwNumArgs)
3281{
3282 FCALL_CONTRACT;
3283
3284 OBJECTREF ret = 0;
3285 HELPER_METHOD_FRAME_BEGIN_RET_1(ret); // Set up a frame
3286
3287 TypeHandle typeHnd(classHnd);
3288 typeHnd.CheckRestore();
3289 _ASSERTE(typeHnd.GetMethodTable()->IsArray());
3290
3291 va_list dimsAndBounds;
3292 va_start(dimsAndBounds, dwNumArgs);
3293
3294 ret = allocNewMDArr(typeHnd, dwNumArgs, dimsAndBounds);
3295 va_end(dimsAndBounds);
3296
3297 HELPER_METHOD_FRAME_END();
3298 return OBJECTREFToObject(ret);
3299}
3300HCIMPLEND
3301
3302/*************************************************************/
3303HCIMPL3(Object*, JIT_NewMDArrNonVarArg, CORINFO_CLASS_HANDLE classHnd, unsigned dwNumArgs, INT32 * pArgList)
3304{
3305 FCALL_CONTRACT;
3306
3307 OBJECTREF ret = 0;
3308 HELPER_METHOD_FRAME_BEGIN_RET_1(ret); // Set up a frame
3309
3310 TypeHandle typeHnd(classHnd);
3311 typeHnd.CheckRestore();
3312 _ASSERTE(typeHnd.GetMethodTable()->IsArray());
3313
3314 ret = AllocateArrayEx(typeHnd, pArgList, dwNumArgs);
3315
3316 HELPER_METHOD_FRAME_END();
3317 return OBJECTREFToObject(ret);
3318}
3319HCIMPLEND
3320
3321/*************************************************************/
3322/* returns '&array[idx], after doing all the proper checks */
3323
3324#include <optsmallperfcritical.h>
3325HCIMPL3(void*, JIT_Ldelema_Ref, PtrArray* array, unsigned idx, CORINFO_CLASS_HANDLE type)
3326{
3327 FCALL_CONTRACT;
3328
3329 RuntimeExceptionKind except;
3330 // This has been carefully arranged to ensure that in the common
3331 // case the branches are predicted properly (fall through).
3332 // and that we dont spill registers unnecessarily etc.
3333 if (array != 0)
3334 if (idx < array->GetNumComponents())
3335 if (array->GetArrayElementTypeHandle() == TypeHandle(type))
3336 return(&array->m_Array[idx]);
3337 else
3338 except = kArrayTypeMismatchException;
3339 else
3340 except = kIndexOutOfRangeException;
3341 else
3342 except = kNullReferenceException;
3343
3344 FCThrow(except);
3345}
3346HCIMPLEND
3347#include <optdefault.h>
3348
3349//===========================================================================
3350// This routine is called if the Array store needs a frame constructed
3351// in order to do the array check. It should only be called from
3352// the array store check helpers.
3353
3354HCIMPL2(LPVOID, ArrayStoreCheck, Object** pElement, PtrArray** pArray)
3355{
3356 FCALL_CONTRACT;
3357
3358 HELPER_METHOD_FRAME_BEGIN_RET_ATTRIB_2(Frame::FRAME_ATTR_EXACT_DEPTH|Frame::FRAME_ATTR_CAPTURE_DEPTH_2, *pElement, *pArray);
3359
3360 GCStress<cfg_any, EeconfigFastGcSPolicy>::MaybeTrigger();
3361
3362 if (!ObjIsInstanceOf(*pElement, (*pArray)->GetArrayElementTypeHandle()))
3363 COMPlusThrow(kArrayTypeMismatchException);
3364
3365 HELPER_METHOD_FRAME_END();
3366
3367 return (LPVOID)0; // Used to aid epilog walker
3368}
3369HCIMPLEND
3370
3371/****************************************************************************/
3372/* assigns 'val to 'array[idx], after doing all the proper checks */
3373
3374HCIMPL3(void, JIT_Stelem_Ref_Portable, PtrArray* array, unsigned idx, Object *val)
3375{
3376 FCALL_CONTRACT;
3377
3378 if (!array)
3379 {
3380 FCThrowVoid(kNullReferenceException);
3381 }
3382 if (idx >= array->GetNumComponents())
3383 {
3384 FCThrowVoid(kIndexOutOfRangeException);
3385 }
3386
3387 if (val)
3388 {
3389 MethodTable *valMT = val->GetMethodTable();
3390 TypeHandle arrayElemTH = array->GetArrayElementTypeHandle();
3391
3392 if (arrayElemTH != TypeHandle(valMT) && arrayElemTH != TypeHandle(g_pObjectClass))
3393 {
3394 TypeHandle::CastResult result = ObjIsInstanceOfNoGC(val, arrayElemTH);
3395 if (result != TypeHandle::CanCast)
3396 {
3397 // FCALL_CONTRACT increase ForbidGC count. Normally, HELPER_METHOD_FRAME macros decrease the count.
3398 // But to avoid perf hit, we manually decrease the count here before calling another HCCALL.
3399 ENDFORBIDGC();
3400
3401 if (HCCALL2(ArrayStoreCheck,(Object**)&val, (PtrArray**)&array) != NULL)
3402 {
3403 // This return is never executed. It helps epilog walker to find its way out.
3404 return;
3405 }
3406 }
3407 }
3408
3409#ifdef _TARGET_ARM64_
3410 SetObjectReferenceUnchecked((OBJECTREF*)&array->m_Array[idx], ObjectToOBJECTREF(val));
3411#else
3412 // The performance gain of the optimized JIT_Stelem_Ref in
3413 // jitinterfacex86.cpp is mainly due to calling JIT_WriteBarrier
3414 // By calling write barrier directly here,
3415 // we can avoid translating in-line assembly from MSVC to gcc
3416 // while keeping most of the performance gain.
3417 HCCALL2(JIT_WriteBarrier, (Object **)&array->m_Array[idx], val);
3418#endif
3419
3420 }
3421 else
3422 {
3423 // no need to go through write-barrier for NULL
3424 ClearObjectReference(&array->m_Array[idx]);
3425 }
3426}
3427HCIMPLEND
3428
3429
3430
3431//========================================================================
3432//
3433// VALUETYPE/BYREF HELPERS
3434//
3435//========================================================================
3436
3437/*************************************************************/
3438HCIMPL2(Object*, JIT_Box, CORINFO_CLASS_HANDLE type, void* unboxedData)
3439{
3440 FCALL_CONTRACT;
3441
3442 // <TODO>TODO: if we care, we could do a fast trial allocation
3443 // and avoid the building the frame most times</TODO>
3444 OBJECTREF newobj = NULL;
3445 HELPER_METHOD_FRAME_BEGIN_RET_NOPOLL(); // Set up a frame
3446 GCPROTECT_BEGININTERIOR(unboxedData);
3447 HELPER_METHOD_POLL();
3448
3449 TypeHandle clsHnd(type);
3450
3451 _ASSERTE(!clsHnd.IsTypeDesc()); // we never use this helper for arrays
3452
3453 MethodTable *pMT = clsHnd.AsMethodTable();
3454
3455 pMT->CheckRestore();
3456
3457 // You can only box valuetypes
3458 if (!pMT->IsValueType())
3459 COMPlusThrow(kInvalidCastException, W("Arg_ObjObj"));
3460
3461#ifdef _DEBUG
3462 if (g_pConfig->FastGCStressLevel()) {
3463 GetThread()->DisableStressHeap();
3464 }
3465#endif // _DEBUG
3466
3467 newobj = pMT->FastBox(&unboxedData);
3468
3469 GCPROTECT_END();
3470 HELPER_METHOD_FRAME_END();
3471 return(OBJECTREFToObject(newobj));
3472}
3473HCIMPLEND
3474
3475/*************************************************************/
3476NOINLINE HCIMPL3(VOID, JIT_Unbox_Nullable_Framed, void * destPtr, MethodTable* typeMT, OBJECTREF objRef)
3477{
3478 FCALL_CONTRACT;
3479
3480 HELPER_METHOD_FRAME_BEGIN_1(objRef);
3481 if (!Nullable::UnBox(destPtr, objRef, typeMT))
3482 {
3483 COMPlusThrowInvalidCastException(&objRef, TypeHandle(typeMT));
3484 }
3485 HELPER_METHOD_FRAME_END();
3486}
3487HCIMPLEND
3488
3489/*************************************************************/
3490HCIMPL3(VOID, JIT_Unbox_Nullable, void * destPtr, CORINFO_CLASS_HANDLE type, Object* obj)
3491{
3492 FCALL_CONTRACT;
3493
3494 TypeHandle typeHnd(type);
3495 _ASSERTE(Nullable::IsNullableType(typeHnd));
3496
3497 MethodTable* typeMT = typeHnd.AsMethodTable();
3498
3499 OBJECTREF objRef = ObjectToOBJECTREF(obj);
3500
3501 if (Nullable::UnBoxNoGC(destPtr, objRef, typeMT))
3502 {
3503 // exact match (type equivalence not needed)
3504 return;
3505 }
3506
3507 // Fall back to a framed helper that handles type equivalence.
3508 ENDFORBIDGC();
3509 HCCALL3(JIT_Unbox_Nullable_Framed, destPtr, typeMT, objRef);
3510}
3511HCIMPLEND
3512
3513/*************************************************************/
3514/* framed helper that handles full-blown type equivalence */
3515NOINLINE HCIMPL2(LPVOID, JIT_Unbox_Helper_Framed, CORINFO_CLASS_HANDLE type, Object* obj)
3516{
3517 FCALL_CONTRACT;
3518
3519 LPVOID result = NULL;
3520
3521 OBJECTREF objRef = ObjectToOBJECTREF(obj);
3522 HELPER_METHOD_FRAME_BEGIN_RET_1(objRef);
3523 if (TypeHandle(type).IsEquivalentTo(objRef->GetTypeHandle()))
3524 {
3525 // the structures are equivalent
3526 result = objRef->GetData();
3527 }
3528 else
3529 {
3530 COMPlusThrowInvalidCastException(&objRef, TypeHandle(type));
3531 }
3532 HELPER_METHOD_FRAME_END();
3533
3534 return result;
3535}
3536HCIMPLEND
3537
3538/*************************************************************/
3539/* the uncommon case for the helper below (allowing enums to be unboxed
3540 as their underlying type */
3541LPVOID __fastcall JIT_Unbox_Helper(CORINFO_CLASS_HANDLE type, Object* obj)
3542{
3543 FCALL_CONTRACT;
3544
3545 TypeHandle typeHnd(type);
3546
3547 CorElementType type1 = typeHnd.GetInternalCorElementType();
3548
3549 // we allow enums and their primtive type to be interchangable
3550
3551 MethodTable* pMT2 = obj->GetMethodTable();
3552 CorElementType type2 = pMT2->GetInternalCorElementType();
3553 if (type1 == type2)
3554 {
3555 MethodTable* pMT1 = typeHnd.GetMethodTable();
3556 if (pMT1 && (pMT1->IsEnum() || pMT1->IsTruePrimitive()) &&
3557 (pMT2->IsEnum() || pMT2->IsTruePrimitive()))
3558 {
3559 _ASSERTE(CorTypeInfo::IsPrimitiveType_NoThrow(type1));
3560 return(obj->GetData());
3561 }
3562 }
3563
3564 // Even less common cases (type equivalence) go to a framed helper.
3565 ENDFORBIDGC();
3566 return HCCALL2(JIT_Unbox_Helper_Framed, type, obj);
3567}
3568
3569/*************************************************************/
3570HCIMPL2(LPVOID, JIT_Unbox, CORINFO_CLASS_HANDLE type, Object* obj)
3571{
3572 FCALL_CONTRACT;
3573
3574 TypeHandle typeHnd(type);
3575 VALIDATEOBJECT(obj);
3576 _ASSERTE(!typeHnd.IsTypeDesc()); // value classes are always unshared
3577
3578 // This has been tuned so that branch predictions are good
3579 // (fall through for forward branches) for the common case
3580 if (obj != NULL) {
3581 if (obj->GetMethodTable() == typeHnd.AsMethodTable())
3582 return(obj->GetData());
3583 else {
3584 // Stuff the uncommon case into a helper so that
3585 // its register needs don't cause spills that effect
3586 // the common case above.
3587 return JIT_Unbox_Helper(type, obj);
3588 }
3589 }
3590
3591 FCThrow(kNullReferenceException);
3592}
3593HCIMPLEND
3594
3595/*************************************************************/
3596HCIMPL2_IV(LPVOID, JIT_GetRefAny, CORINFO_CLASS_HANDLE type, TypedByRef typedByRef)
3597{
3598 FCALL_CONTRACT;
3599
3600 TypeHandle clsHnd(type);
3601
3602 // <TODO>@TODO right now we check for precisely the correct type.
3603 // do we want to allow inheritance? (watch out since value
3604 // classes inherit from object but do not normal object layout).</TODO>
3605 if (clsHnd != typedByRef.type) {
3606 FCThrow(kInvalidCastException);
3607 }
3608
3609 return(typedByRef.data);
3610}
3611HCIMPLEND
3612
3613
3614//========================================================================
3615//
3616// GENERICS HELPERS
3617//
3618//========================================================================
3619
3620/***********************************************************************/
3621// JIT_GenericHandle and its cache
3622//
3623// Perform a "polytypic" operation related to shared generic code at runtime, possibly filling in an entry in
3624// either a generic dictionary cache assocaited with a descriptor or placing an entry in the global
3625// JitGenericHandle cache.
3626//
3627// A polytypic operation is one such as
3628// * new List<T>
3629// * castclass List<T>
3630// where the code being executed is shared generic code. In these cases the outcome of the operation depends
3631// on the exact value for T, which is acquired from a dynamic parameter.
3632//
3633// The actual operation always boils down to finding a "handle" (TypeHandle, MethodDesc, call address,
3634// dispatch stub address etc.) based on some static information (passed as tokens) and on the exact runtime
3635// type context (passed as one or two parameters classHnd and methodHnd).
3636//
3637// The static information specifies which polytypic operation (and thus which kind of handle) we're
3638// interested in.
3639//
3640// The dynamic information (the type context, i.e. the exact instantiation of class and method type
3641// parameters is specified in one of two ways:
3642// * If classHnd is null then the methodHnd should be an exact method descriptor wrapping shared code that
3643// satisfies SharedByGenericMethodInstantiations().
3644//
3645// For example:
3646// * We may be running the shared code for a generic method instantiation C::m<object>. The methodHnd
3647// will carry the exact instantiation, e.g. C::m<string>
3648//
3649// * If classHnd is non-null (e.g. a type D<exact>) then:
3650// * methodHnd will indicate the representative code being run (which will be
3651// !SharedByGenericMethodInstantiations but will be SharedByGenericClassInstantiations). Let's say
3652// this code is C<repr>::m().
3653// * the type D will be a descendent of type C. In particular D<exact> will relate to some type C<exact'>
3654// where C<repr> is the represntative instantiation of C<exact>'
3655// * the relevant dictionary will be the one attached to C<exact'>.
3656//
3657// The JitGenericHandleCache is a global data structure shared across all application domains. It is only
3658// used if generic dictionaries have overflowed. It is flushed each time an application domain is unloaded.
3659
3660struct JitGenericHandleCacheKey
3661{
3662 JitGenericHandleCacheKey(CORINFO_CLASS_HANDLE classHnd, CORINFO_METHOD_HANDLE methodHnd, void *signature, BaseDomain* pDomain=NULL)
3663 {
3664 LIMITED_METHOD_CONTRACT;
3665 m_Data1 = (size_t)classHnd;
3666 m_Data2 = (size_t)methodHnd;
3667 m_Data3 = (size_t)signature;
3668 m_pDomainAndType = 0 | (size_t)pDomain;
3669 }
3670
3671 JitGenericHandleCacheKey(MethodTable* pMT, CORINFO_CLASS_HANDLE classHnd, CORINFO_METHOD_HANDLE methodHnd, BaseDomain* pDomain=NULL)
3672 {
3673 LIMITED_METHOD_CONTRACT;
3674 m_Data1 = (size_t)pMT;
3675 m_Data2 = (size_t)classHnd;
3676 m_Data3 = (size_t)methodHnd;
3677 m_pDomainAndType = 1 | (size_t)pDomain;
3678 }
3679
3680 size_t GetType() const
3681 {
3682 LIMITED_METHOD_CONTRACT;
3683 return (m_pDomainAndType & 1);
3684 }
3685
3686 BaseDomain* GetDomain() const
3687 {
3688 LIMITED_METHOD_CONTRACT;
3689 return (BaseDomain*)(m_pDomainAndType & ~1);
3690 }
3691
3692 size_t m_Data1;
3693 size_t m_Data2;
3694 size_t m_Data3;
3695
3696 size_t m_pDomainAndType; // Which domain the entry belongs to. Not actually part of the key.
3697 // Used only so we can scrape the table on AppDomain termination.
3698 // NULL appdomain means that the entry should be scratched
3699 // on any appdomain unload.
3700 //
3701 // The lowest bit is used to indicate the type of the entry:
3702 // 0 - JIT_GenericHandle entry
3703 // 1 - JIT_VirtualFunctionPointer entry
3704};
3705
3706class JitGenericHandleCacheTraits
3707{
3708public:
3709 static EEHashEntry_t *AllocateEntry(const JitGenericHandleCacheKey *pKey, BOOL bDeepCopy, AllocationHeap pHeap = 0)
3710 {
3711 LIMITED_METHOD_CONTRACT;
3712 EEHashEntry_t *pEntry = (EEHashEntry_t *) new (nothrow) BYTE[SIZEOF_EEHASH_ENTRY + sizeof(JitGenericHandleCacheKey)];
3713 if (!pEntry)
3714 return NULL;
3715 *((JitGenericHandleCacheKey*)pEntry->Key) = *pKey;
3716 return pEntry;
3717 }
3718
3719 static void DeleteEntry(EEHashEntry_t *pEntry, AllocationHeap pHeap = 0)
3720 {
3721 LIMITED_METHOD_CONTRACT;
3722 delete [] (BYTE*)pEntry;
3723 }
3724
3725 static BOOL CompareKeys(EEHashEntry_t *pEntry, const JitGenericHandleCacheKey *e2)
3726 {
3727 LIMITED_METHOD_CONTRACT;
3728 const JitGenericHandleCacheKey *e1 = (const JitGenericHandleCacheKey*)&pEntry->Key;
3729 return (e1->m_Data1 == e2->m_Data1) && (e1->m_Data2 == e2->m_Data2) && (e1->m_Data3 == e2->m_Data3) &&
3730 (e1->GetType() == e2->GetType()) &&
3731 // Any domain will work if the lookup key does not specify it
3732 ((e2->GetDomain() == NULL) || (e1->GetDomain() == e2->GetDomain()));
3733 }
3734
3735 static DWORD Hash(const JitGenericHandleCacheKey *k)
3736 {
3737 LIMITED_METHOD_CONTRACT;
3738 return (DWORD)k->m_Data1 + _rotl((DWORD)k->m_Data2,5) + _rotr((DWORD)k->m_Data3,5);
3739 }
3740
3741 static const JitGenericHandleCacheKey *GetKey(EEHashEntry_t *pEntry)
3742 {
3743 LIMITED_METHOD_CONTRACT;
3744 return (const JitGenericHandleCacheKey*)&pEntry->Key;
3745 }
3746};
3747
3748typedef EEHashTable<const JitGenericHandleCacheKey *, JitGenericHandleCacheTraits, FALSE> JitGenericHandleCache;
3749
3750JitGenericHandleCache *g_pJitGenericHandleCache = NULL; //cache of calls to JIT_GenericHandle
3751CrstStatic g_pJitGenericHandleCacheCrst;
3752
3753void AddToGenericHandleCache(JitGenericHandleCacheKey* pKey, HashDatum datum)
3754{
3755 CONTRACTL {
3756 NOTHROW;
3757 GC_TRIGGERS;
3758 MODE_ANY;
3759 PRECONDITION(CheckPointer(pKey));
3760 PRECONDITION(CheckPointer(datum));
3761 } CONTRACTL_END;
3762
3763 EX_TRY
3764 {
3765 GCX_COOP();
3766
3767 CrstHolder lock(&g_pJitGenericHandleCacheCrst);
3768
3769 HashDatum entry;
3770 if (!g_pJitGenericHandleCache->GetValue(pKey,&entry))
3771 g_pJitGenericHandleCache->InsertValue(pKey,datum);
3772 }
3773 EX_CATCH
3774 {
3775 }
3776 EX_END_CATCH(SwallowAllExceptions) // Swallow OOM
3777}
3778
3779/* static */
3780void ClearJitGenericHandleCache(AppDomain *pDomain)
3781{
3782 CONTRACTL {
3783 NOTHROW;
3784 GC_NOTRIGGER;
3785 } CONTRACTL_END;
3786
3787
3788 // We call this on every AppDomain unload, because entries in the cache might include
3789 // pointers into the AppDomain being unloaded. We would prefer to
3790 // only flush entries that have that are no longer valid, but the entries don't yet contain
3791 // enough information to do that. However everything in the cache can be found again by calling
3792 // loader functions, and the total number of entries in the cache is typically very small (indeed
3793 // normally the cache is not used at all - it is only used when the generic dictionaries overflow).
3794 if (g_pJitGenericHandleCache)
3795 {
3796 // It's not necessary to take the lock here because this function should only be called when EE is suspended,
3797 // the lock is only taken to fullfill the threadsafety check and to be consistent. If the lock becomes a problem, we
3798 // could put it in a "ifdef _DEBUG" block
3799 CrstHolder lock(&g_pJitGenericHandleCacheCrst);
3800 EEHashTableIteration iter;
3801 g_pJitGenericHandleCache->IterateStart(&iter);
3802 BOOL keepGoing = g_pJitGenericHandleCache->IterateNext(&iter);
3803 while(keepGoing)
3804 {
3805 const JitGenericHandleCacheKey *key = g_pJitGenericHandleCache->IterateGetKey(&iter);
3806 BaseDomain* pKeyDomain = key->GetDomain();
3807 if (pKeyDomain == pDomain || pKeyDomain == NULL
3808 // We compute fake domain for types during NGen (see code:ClassLoader::ComputeLoaderModule).
3809 // To avoid stale handles, we need to clear the cache unconditionally during NGen.
3810 || IsCompilationProcess())
3811 {
3812 // Advance the iterator before we delete!! See notes in EEHash.h
3813 keepGoing = g_pJitGenericHandleCache->IterateNext(&iter);
3814 g_pJitGenericHandleCache->DeleteValue(key);
3815 }
3816 else
3817 {
3818 keepGoing = g_pJitGenericHandleCache->IterateNext(&iter);
3819 }
3820 }
3821 }
3822}
3823
3824// Factored out most of the body of JIT_GenericHandle so it could be called easily from the CER reliability code to pre-populate the
3825// cache.
3826CORINFO_GENERIC_HANDLE JIT_GenericHandleWorker(MethodDesc * pMD, MethodTable * pMT, LPVOID signature, DWORD dictionaryIndexAndSlot, Module* pModule)
3827{
3828 CONTRACTL {
3829 THROWS;
3830 GC_TRIGGERS;
3831 } CONTRACTL_END;
3832
3833 MethodTable * pDeclaringMT = NULL;
3834
3835 if (pMT != NULL)
3836 {
3837 ULONG dictionaryIndex = 0;
3838
3839 if (pModule != NULL)
3840 {
3841#ifdef _DEBUG
3842 // Only in R2R mode are the module, dictionary index and dictionary slot provided as an input
3843 _ASSERTE(dictionaryIndexAndSlot != -1);
3844 _ASSERT(ExecutionManager::FindReadyToRunModule(dac_cast<TADDR>(signature)) == pModule);
3845#endif
3846 dictionaryIndex = (dictionaryIndexAndSlot >> 16);
3847 }
3848 else
3849 {
3850 SigPointer ptr((PCCOR_SIGNATURE)signature);
3851
3852 ULONG kind; // DictionaryEntryKind
3853 IfFailThrow(ptr.GetData(&kind));
3854
3855 // We need to normalize the class passed in (if any) for reliability purposes. That's because preparation of a code region that
3856 // contains these handle lookups depends on being able to predict exactly which lookups are required (so we can pre-cache the
3857 // answers and remove any possibility of failure at runtime). This is hard to do if the lookup (in this case the lookup of the
3858 // dictionary overflow cache) is keyed off the somewhat arbitrary type of the instance on which the call is made (we'd need to
3859 // prepare for every possible derived type of the type containing the method). So instead we have to locate the exactly
3860 // instantiated (non-shared) super-type of the class passed in.
3861
3862 _ASSERTE(dictionaryIndexAndSlot == -1);
3863 IfFailThrow(ptr.GetData(&dictionaryIndex));
3864 }
3865
3866 pDeclaringMT = pMT;
3867 for (;;)
3868 {
3869 MethodTable * pParentMT = pDeclaringMT->GetParentMethodTable();
3870 if (pParentMT->GetNumDicts() <= dictionaryIndex)
3871 break;
3872 pDeclaringMT = pParentMT;
3873 }
3874
3875 if (pDeclaringMT != pMT)
3876 {
3877 JitGenericHandleCacheKey key((CORINFO_CLASS_HANDLE)pDeclaringMT, NULL, signature);
3878 HashDatum res;
3879 if (g_pJitGenericHandleCache->GetValue(&key,&res))
3880 {
3881 // Add the denormalized key for faster lookup next time. This is not a critical entry - no need
3882 // to specify appdomain affinity.
3883 JitGenericHandleCacheKey denormKey((CORINFO_CLASS_HANDLE)pMT, NULL, signature);
3884 AddToGenericHandleCache(&denormKey, res);
3885 return (CORINFO_GENERIC_HANDLE) (DictionaryEntry) res;
3886 }
3887 }
3888 }
3889
3890 DictionaryEntry * pSlot;
3891 CORINFO_GENERIC_HANDLE result = (CORINFO_GENERIC_HANDLE)Dictionary::PopulateEntry(pMD, pDeclaringMT, signature, FALSE, &pSlot, dictionaryIndexAndSlot, pModule);
3892
3893 if (pSlot == NULL)
3894 {
3895 // If we've overflowed the dictionary write the result to the cache.
3896 BaseDomain *pDictDomain = NULL;
3897
3898 if (pMT != NULL)
3899 {
3900 pDictDomain = pDeclaringMT->GetDomain();
3901 }
3902 else
3903 {
3904 pDictDomain = pMD->GetDomain();
3905 }
3906
3907 // Add the normalized key (pDeclaringMT) here so that future lookups of any
3908 // inherited types are faster next time rather than just just for this specific pMT.
3909 JitGenericHandleCacheKey key((CORINFO_CLASS_HANDLE)pDeclaringMT, (CORINFO_METHOD_HANDLE)pMD, signature, pDictDomain);
3910 AddToGenericHandleCache(&key, (HashDatum)result);
3911 }
3912
3913 return result;
3914} // JIT_GenericHandleWorker
3915
3916/*********************************************************************/
3917// slow helper to tail call from the fast one
3918NOINLINE HCIMPL5(CORINFO_GENERIC_HANDLE, JIT_GenericHandle_Framed,
3919 CORINFO_CLASS_HANDLE classHnd,
3920 CORINFO_METHOD_HANDLE methodHnd,
3921 LPVOID signature,
3922 DWORD dictionaryIndexAndSlot,
3923 CORINFO_MODULE_HANDLE moduleHnd)
3924{
3925 CONTRACTL {
3926 FCALL_CHECK;
3927 PRECONDITION(classHnd != NULL || methodHnd != NULL);
3928 PRECONDITION(classHnd == NULL || methodHnd == NULL);
3929 } CONTRACTL_END;
3930
3931 // Result is a generic handle (in fact, a CORINFO_CLASS_HANDLE, CORINFO_METHOD_HANDLE, or a code pointer)
3932 CORINFO_GENERIC_HANDLE result = NULL;
3933
3934 MethodDesc * pMD = GetMethod(methodHnd);
3935 MethodTable * pMT = TypeHandle(classHnd).AsMethodTable();
3936 Module * pModule = GetModule(moduleHnd);
3937
3938 // Set up a frame
3939 HELPER_METHOD_FRAME_BEGIN_RET_0();
3940
3941 result = JIT_GenericHandleWorker(pMD, pMT, signature, dictionaryIndexAndSlot, pModule);
3942
3943 HELPER_METHOD_FRAME_END();
3944
3945 _ASSERTE(result != NULL);
3946
3947 // Return the handle
3948 return result;
3949}
3950HCIMPLEND
3951
3952/*********************************************************************/
3953#include <optsmallperfcritical.h>
3954HCIMPL2(CORINFO_GENERIC_HANDLE, JIT_GenericHandleMethod, CORINFO_METHOD_HANDLE methodHnd, LPVOID signature)
3955{
3956 CONTRACTL {
3957 FCALL_CHECK;
3958 PRECONDITION(CheckPointer(methodHnd));
3959 PRECONDITION(GetMethod(methodHnd)->IsRestored());
3960 PRECONDITION(CheckPointer(signature));
3961 } CONTRACTL_END;
3962
3963 JitGenericHandleCacheKey key(NULL, methodHnd, signature);
3964 HashDatum res;
3965 if (g_pJitGenericHandleCache->GetValueSpeculative(&key,&res))
3966 return (CORINFO_GENERIC_HANDLE) (DictionaryEntry) res;
3967
3968 // Tailcall to the slow helper
3969 ENDFORBIDGC();
3970 return HCCALL5(JIT_GenericHandle_Framed, NULL, methodHnd, signature, -1, NULL);
3971}
3972HCIMPLEND
3973
3974HCIMPL2(CORINFO_GENERIC_HANDLE, JIT_GenericHandleMethodWithSlotAndModule, CORINFO_METHOD_HANDLE methodHnd, GenericHandleArgs * pArgs)
3975{
3976 CONTRACTL{
3977 FCALL_CHECK;
3978 PRECONDITION(CheckPointer(methodHnd));
3979 PRECONDITION(GetMethod(methodHnd)->IsRestored());
3980 PRECONDITION(CheckPointer(pArgs));
3981 } CONTRACTL_END;
3982
3983 JitGenericHandleCacheKey key(NULL, methodHnd, pArgs->signature);
3984 HashDatum res;
3985 if (g_pJitGenericHandleCache->GetValueSpeculative(&key, &res))
3986 return (CORINFO_GENERIC_HANDLE)(DictionaryEntry)res;
3987
3988 // Tailcall to the slow helper
3989 ENDFORBIDGC();
3990 return HCCALL5(JIT_GenericHandle_Framed, NULL, methodHnd, pArgs->signature, pArgs->dictionaryIndexAndSlot, pArgs->module);
3991}
3992HCIMPLEND
3993#include <optdefault.h>
3994
3995/*********************************************************************/
3996HCIMPL2(CORINFO_GENERIC_HANDLE, JIT_GenericHandleMethodLogging, CORINFO_METHOD_HANDLE methodHnd, LPVOID signature)
3997{
3998 CONTRACTL {
3999 FCALL_CHECK;
4000 PRECONDITION(CheckPointer(methodHnd));
4001 PRECONDITION(GetMethod(methodHnd)->IsRestored());
4002 PRECONDITION(CheckPointer(signature));
4003 } CONTRACTL_END;
4004
4005 g_IBCLogger.LogMethodDescAccess(GetMethod(methodHnd));
4006
4007 JitGenericHandleCacheKey key(NULL, methodHnd, signature);
4008 HashDatum res;
4009 if (g_pJitGenericHandleCache->GetValueSpeculative(&key,&res))
4010 return (CORINFO_GENERIC_HANDLE) (DictionaryEntry) res;
4011
4012 // Tailcall to the slow helper
4013 ENDFORBIDGC();
4014 return HCCALL5(JIT_GenericHandle_Framed, NULL, methodHnd, signature, -1, NULL);
4015}
4016HCIMPLEND
4017
4018/*********************************************************************/
4019#include <optsmallperfcritical.h>
4020HCIMPL2(CORINFO_GENERIC_HANDLE, JIT_GenericHandleClass, CORINFO_CLASS_HANDLE classHnd, LPVOID signature)
4021{
4022 CONTRACTL {
4023 FCALL_CHECK;
4024 PRECONDITION(CheckPointer(classHnd));
4025 PRECONDITION(TypeHandle(classHnd).IsRestored());
4026 PRECONDITION(CheckPointer(signature));
4027 } CONTRACTL_END;
4028
4029 JitGenericHandleCacheKey key(classHnd, NULL, signature);
4030 HashDatum res;
4031 if (g_pJitGenericHandleCache->GetValueSpeculative(&key,&res))
4032 return (CORINFO_GENERIC_HANDLE) (DictionaryEntry) res;
4033
4034 // Tailcall to the slow helper
4035 ENDFORBIDGC();
4036 return HCCALL5(JIT_GenericHandle_Framed, classHnd, NULL, signature, -1, NULL);
4037}
4038HCIMPLEND
4039
4040HCIMPL2(CORINFO_GENERIC_HANDLE, JIT_GenericHandleClassWithSlotAndModule, CORINFO_CLASS_HANDLE classHnd, GenericHandleArgs * pArgs)
4041{
4042 CONTRACTL{
4043 FCALL_CHECK;
4044 PRECONDITION(CheckPointer(classHnd));
4045 PRECONDITION(TypeHandle(classHnd).IsRestored());
4046 PRECONDITION(CheckPointer(pArgs));
4047 } CONTRACTL_END;
4048
4049 JitGenericHandleCacheKey key(classHnd, NULL, pArgs->signature);
4050 HashDatum res;
4051 if (g_pJitGenericHandleCache->GetValueSpeculative(&key, &res))
4052 return (CORINFO_GENERIC_HANDLE)(DictionaryEntry)res;
4053
4054 // Tailcall to the slow helper
4055 ENDFORBIDGC();
4056 return HCCALL5(JIT_GenericHandle_Framed, classHnd, NULL, pArgs->signature, pArgs->dictionaryIndexAndSlot, pArgs->module);
4057}
4058HCIMPLEND
4059#include <optdefault.h>
4060
4061/*********************************************************************/
4062HCIMPL2(CORINFO_GENERIC_HANDLE, JIT_GenericHandleClassLogging, CORINFO_CLASS_HANDLE classHnd, LPVOID signature)
4063{
4064 CONTRACTL {
4065 FCALL_CHECK;
4066 PRECONDITION(CheckPointer(classHnd));
4067 PRECONDITION(TypeHandle(classHnd).IsRestored());
4068 PRECONDITION(CheckPointer(signature));
4069 } CONTRACTL_END;
4070
4071 g_IBCLogger.LogMethodTableAccess((MethodTable *)classHnd);
4072
4073 JitGenericHandleCacheKey key(classHnd, NULL, signature);
4074 HashDatum res;
4075 if (g_pJitGenericHandleCache->GetValueSpeculative(&key,&res))
4076 return (CORINFO_GENERIC_HANDLE) (DictionaryEntry) res;
4077
4078 // Tailcall to the slow helper
4079 ENDFORBIDGC();
4080 return HCCALL5(JIT_GenericHandle_Framed, classHnd, NULL, signature, -1, NULL);
4081}
4082HCIMPLEND
4083
4084/*********************************************************************/
4085// Resolve a virtual method at run-time, either because of
4086// aggressive backpatching or because the call is to a generic
4087// method which is itself virtual.
4088//
4089// classHnd is the actual run-time type for the call is made.
4090// methodHnd is the exact (instantiated) method descriptor corresponding to the
4091// static method signature (i.e. might be for a superclass of classHnd)
4092
4093// slow helper to tail call from the fast one
4094NOINLINE HCIMPL3(CORINFO_MethodPtr, JIT_VirtualFunctionPointer_Framed, Object * objectUNSAFE,
4095 CORINFO_CLASS_HANDLE classHnd,
4096 CORINFO_METHOD_HANDLE methodHnd)
4097{
4098 FCALL_CONTRACT;
4099
4100 // The address of the method that's returned.
4101 CORINFO_MethodPtr addr = NULL;
4102
4103 OBJECTREF objRef = ObjectToOBJECTREF(objectUNSAFE);
4104
4105 HELPER_METHOD_FRAME_BEGIN_RET_1(objRef); // Set up a frame
4106
4107 if (objRef == NULL)
4108 COMPlusThrow(kNullReferenceException);
4109
4110 // This is the static method descriptor describing the call.
4111 // It is not the destination of the call, which we must compute.
4112 MethodDesc* pStaticMD = (MethodDesc*) methodHnd;
4113 TypeHandle staticTH(classHnd);
4114
4115 pStaticMD->CheckRestore();
4116
4117 // MDIL: If IL specifies callvirt/ldvirtftn it remains a "virtual" instruction
4118 // even if the target is an instance method at MDIL generation time because
4119 // we want to keep MDIL as resilient as IL. Right now we can end up here with
4120 // non-virtual generic methods called from a "shared generic code".
4121 // As soon as this deficiency is fixed in the binder we can get rid of this test.
4122 if (!pStaticMD->IsVtableMethod())
4123 {
4124 addr = (CORINFO_MethodPtr) pStaticMD->GetMultiCallableAddrOfCode();
4125 _ASSERTE(addr);
4126 }
4127 else
4128 {
4129 // This is the new way of resolving a virtual call, including generic virtual methods.
4130 // The code is now also used by reflection, remoting etc.
4131 addr = (CORINFO_MethodPtr) pStaticMD->GetMultiCallableAddrOfVirtualizedCode(&objRef, staticTH);
4132 _ASSERTE(addr);
4133
4134 // This is not a critical entry - no need to specify appdomain affinity
4135 JitGenericHandleCacheKey key(objRef->GetMethodTable(), classHnd, methodHnd);
4136 AddToGenericHandleCache(&key, (HashDatum)addr);
4137 }
4138
4139 HELPER_METHOD_FRAME_END();
4140
4141 return addr;
4142}
4143HCIMPLEND
4144
4145HCIMPL2(VOID, JIT_GetRuntimeFieldHandle, Object ** destPtr, CORINFO_FIELD_HANDLE field)
4146{
4147 FCALL_CONTRACT;
4148
4149 HELPER_METHOD_FRAME_BEGIN_0();
4150
4151 FieldDesc *pField = (FieldDesc *)field;
4152 SetObjectReference((OBJECTREF*) destPtr,
4153 pField->GetStubFieldInfo(), GetAppDomain());
4154
4155 HELPER_METHOD_FRAME_END();
4156}
4157HCIMPLEND
4158
4159HCIMPL1(Object*, JIT_GetRuntimeFieldStub, CORINFO_FIELD_HANDLE field)
4160{
4161 FCALL_CONTRACT;
4162
4163 OBJECTREF stubRuntimeField = NULL;
4164
4165 HELPER_METHOD_FRAME_BEGIN_RET_0(); // Set up a frame
4166
4167 FieldDesc *pField = (FieldDesc *)field;
4168 stubRuntimeField = (OBJECTREF)pField->GetStubFieldInfo();
4169
4170 HELPER_METHOD_FRAME_END();
4171
4172 return (OBJECTREFToObject(stubRuntimeField));
4173}
4174HCIMPLEND
4175
4176HCIMPL2(VOID, JIT_GetRuntimeMethodHandle, Object ** destPtr, CORINFO_METHOD_HANDLE method)
4177{
4178 FCALL_CONTRACT;
4179
4180 HELPER_METHOD_FRAME_BEGIN_0();
4181
4182 MethodDesc *pMethod = (MethodDesc *)method;
4183 SetObjectReference((OBJECTREF*) destPtr,
4184 pMethod->GetStubMethodInfo(), GetAppDomain());
4185
4186 HELPER_METHOD_FRAME_END();
4187}
4188HCIMPLEND
4189
4190HCIMPL1(Object*, JIT_GetRuntimeMethodStub, CORINFO_METHOD_HANDLE method)
4191{
4192 FCALL_CONTRACT;
4193
4194 OBJECTREF stubRuntimeMethod = NULL;
4195
4196 HELPER_METHOD_FRAME_BEGIN_RET_0(); // Set up a frame
4197
4198 MethodDesc *pMethod = (MethodDesc *)method;
4199 stubRuntimeMethod = (OBJECTREF)pMethod->GetStubMethodInfo();
4200
4201 HELPER_METHOD_FRAME_END();
4202
4203 return (OBJECTREFToObject(stubRuntimeMethod));
4204}
4205HCIMPLEND
4206
4207HCIMPL2(VOID, JIT_GetRuntimeTypeHandle, Object ** destPtr, CORINFO_CLASS_HANDLE type)
4208{
4209 FCALL_CONTRACT;
4210
4211 TypeHandle typeHnd(type);
4212
4213 if (!typeHnd.IsTypeDesc())
4214 {
4215 // Most common... and fastest case
4216 OBJECTREF typePtr = typeHnd.AsMethodTable()->GetManagedClassObjectIfExists();
4217 if (typePtr != NULL)
4218 {
4219 SetObjectReference((OBJECTREF*) destPtr,
4220 typePtr, GetAppDomain());
4221 return;
4222 }
4223 }
4224
4225 HELPER_METHOD_FRAME_BEGIN_0();
4226
4227 SetObjectReference((OBJECTREF*) destPtr,
4228 typeHnd.GetManagedClassObject(), GetAppDomain());
4229
4230 HELPER_METHOD_FRAME_END();
4231}
4232HCIMPLEND
4233
4234
4235NOINLINE HCIMPL1(Object*, JIT_GetRuntimeType_Framed, CORINFO_CLASS_HANDLE type)
4236{
4237 FCALL_CONTRACT;
4238
4239 TypeHandle typeHandle(type);
4240
4241 // Array/other type handle case.
4242 OBJECTREF refType = typeHandle.GetManagedClassObjectFast();
4243 if (refType == NULL)
4244 {
4245 HELPER_METHOD_FRAME_BEGIN_RET_1(refType);
4246 refType = typeHandle.GetManagedClassObject();
4247 HELPER_METHOD_FRAME_END();
4248 }
4249
4250 return OBJECTREFToObject(refType);
4251}
4252HCIMPLEND
4253
4254#include <optsmallperfcritical.h>
4255HCIMPL1(Object*, JIT_GetRuntimeType, CORINFO_CLASS_HANDLE type)
4256{
4257 FCALL_CONTRACT;
4258
4259 TypeHandle typeHnd(type);
4260
4261 if (!typeHnd.IsTypeDesc())
4262 {
4263 // Most common... and fastest case
4264 OBJECTREF typePtr = typeHnd.AsMethodTable()->GetManagedClassObjectIfExists();
4265 if (typePtr != NULL)
4266 {
4267 return OBJECTREFToObject(typePtr);
4268 }
4269 }
4270
4271 ENDFORBIDGC();
4272 return HCCALL1(JIT_GetRuntimeType_Framed, type);
4273}
4274HCIMPLEND
4275
4276HCIMPL1(Object*, JIT_GetRuntimeType_MaybeNull, CORINFO_CLASS_HANDLE type)
4277{
4278 FCALL_CONTRACT;
4279
4280 if (type == NULL)
4281 return NULL;;
4282
4283 ENDFORBIDGC();
4284 return HCCALL1(JIT_GetRuntimeType, type);
4285}
4286HCIMPLEND
4287#include <optdefault.h>
4288
4289/*********************************************************************/
4290#include <optsmallperfcritical.h>
4291HCIMPL3(CORINFO_MethodPtr, JIT_VirtualFunctionPointer, Object * objectUNSAFE,
4292 CORINFO_CLASS_HANDLE classHnd,
4293 CORINFO_METHOD_HANDLE methodHnd)
4294{
4295 FCALL_CONTRACT;
4296
4297 OBJECTREF objRef = ObjectToOBJECTREF(objectUNSAFE);
4298
4299 if (objRef != NULL)
4300 {
4301 JitGenericHandleCacheKey key(objRef->GetMethodTable(), classHnd, methodHnd);
4302 HashDatum res;
4303 if (g_pJitGenericHandleCache->GetValueSpeculative(&key,&res))
4304 return (CORINFO_GENERIC_HANDLE)res;
4305 }
4306
4307 // Tailcall to the slow helper
4308 ENDFORBIDGC();
4309 return HCCALL3(JIT_VirtualFunctionPointer_Framed, OBJECTREFToObject(objRef), classHnd, methodHnd);
4310}
4311HCIMPLEND
4312
4313HCIMPL2(CORINFO_MethodPtr, JIT_VirtualFunctionPointer_Dynamic, Object * objectUNSAFE, VirtualFunctionPointerArgs * pArgs)
4314{
4315 FCALL_CONTRACT;
4316
4317 OBJECTREF objRef = ObjectToOBJECTREF(objectUNSAFE);
4318
4319 if (objRef != NULL)
4320 {
4321 JitGenericHandleCacheKey key(objRef->GetMethodTable(), pArgs->classHnd, pArgs->methodHnd);
4322 HashDatum res;
4323 if (g_pJitGenericHandleCache->GetValueSpeculative(&key,&res))
4324 return (CORINFO_GENERIC_HANDLE)res;
4325 }
4326
4327 // Tailcall to the slow helper
4328 ENDFORBIDGC();
4329 return HCCALL3(JIT_VirtualFunctionPointer_Framed, OBJECTREFToObject(objRef), pArgs->classHnd, pArgs->methodHnd);
4330}
4331HCIMPLEND
4332
4333#include <optdefault.h>
4334
4335// Helper for synchronized static methods in shared generics code
4336#include <optsmallperfcritical.h>
4337HCIMPL1(CORINFO_CLASS_HANDLE, JIT_GetClassFromMethodParam, CORINFO_METHOD_HANDLE methHnd_)
4338 CONTRACTL {
4339 FCALL_CHECK;
4340 PRECONDITION(methHnd_ != NULL);
4341 } CONTRACTL_END;
4342
4343 MethodDesc * pMD = (MethodDesc*) methHnd_;
4344
4345 MethodTable * pMT = pMD->GetMethodTable();
4346 _ASSERTE(!pMT->IsSharedByGenericInstantiations());
4347
4348 return((CORINFO_CLASS_HANDLE)pMT);
4349HCIMPLEND
4350#include <optdefault.h>
4351
4352
4353
4354//========================================================================
4355//
4356// MONITOR HELPERS
4357//
4358//========================================================================
4359
4360/*********************************************************************/
4361NOINLINE static void JIT_MonEnter_Helper(Object* obj, BYTE* pbLockTaken, LPVOID __me)
4362{
4363 FC_INNER_PROLOG_NO_ME_SETUP();
4364
4365 OBJECTREF objRef = ObjectToOBJECTREF(obj);
4366
4367 // Monitor helpers are used as both hcalls and fcalls, thus we need exact depth.
4368 HELPER_METHOD_FRAME_BEGIN_ATTRIB_1(Frame::FRAME_ATTR_EXACT_DEPTH|Frame::FRAME_ATTR_CAPTURE_DEPTH_2, objRef);
4369
4370 if (objRef == NULL)
4371 COMPlusThrow(kArgumentNullException);
4372
4373 GCPROTECT_BEGININTERIOR(pbLockTaken);
4374
4375#ifdef _DEBUG
4376 Thread *pThread = GetThread();
4377 DWORD lockCount = pThread->m_dwLockCount;
4378#endif
4379 if (GET_THREAD()->CatchAtSafePointOpportunistic())
4380 {
4381 GET_THREAD()->PulseGCMode();
4382 }
4383 objRef->EnterObjMonitor();
4384 _ASSERTE ((objRef->GetSyncBlock()->GetMonitor()->GetRecursionLevel() == 1 && pThread->m_dwLockCount == lockCount + 1) ||
4385 pThread->m_dwLockCount == lockCount);
4386 if (pbLockTaken != 0) *pbLockTaken = 1;
4387
4388 GCPROTECT_END();
4389 HELPER_METHOD_FRAME_END();
4390
4391 FC_INNER_EPILOG();
4392}
4393
4394/*********************************************************************/
4395#include <optsmallperfcritical.h>
4396
4397HCIMPL_MONHELPER(JIT_MonEnterWorker_Portable, Object* obj)
4398{
4399 FCALL_CONTRACT;
4400
4401 if (obj != nullptr && obj->TryEnterObjMonitorSpinHelper())
4402 {
4403 MONHELPER_STATE(*pbLockTaken = 1);
4404 return;
4405 }
4406
4407 FC_INNER_RETURN_VOID(JIT_MonEnter_Helper(obj, MONHELPER_ARG, GetEEFuncEntryPointMacro(JIT_MonEnter)));
4408}
4409HCIMPLEND
4410
4411HCIMPL1(void, JIT_MonEnter_Portable, Object* obj)
4412{
4413 FCALL_CONTRACT;
4414
4415 if (obj != nullptr && obj->TryEnterObjMonitorSpinHelper())
4416 {
4417 return;
4418 }
4419
4420 FC_INNER_RETURN_VOID(JIT_MonEnter_Helper(obj, NULL, GetEEFuncEntryPointMacro(JIT_MonEnter)));
4421}
4422HCIMPLEND
4423
4424HCIMPL2(void, JIT_MonReliableEnter_Portable, Object* obj, BYTE* pbLockTaken)
4425{
4426 FCALL_CONTRACT;
4427
4428 if (obj != nullptr && obj->TryEnterObjMonitorSpinHelper())
4429 {
4430 *pbLockTaken = 1;
4431 return;
4432 }
4433
4434 FC_INNER_RETURN_VOID(JIT_MonEnter_Helper(obj, pbLockTaken, GetEEFuncEntryPointMacro(JIT_MonReliableEnter)));
4435}
4436HCIMPLEND
4437
4438#include <optdefault.h>
4439
4440
4441/*********************************************************************/
4442NOINLINE static void JIT_MonTryEnter_Helper(Object* obj, INT32 timeOut, BYTE* pbLockTaken)
4443{
4444 FC_INNER_PROLOG(JIT_MonTryEnter);
4445
4446 OBJECTREF objRef = ObjectToOBJECTREF(obj);
4447
4448 // Monitor helpers are used as both hcalls and fcalls, thus we need exact depth.
4449 HELPER_METHOD_FRAME_BEGIN_ATTRIB_1(Frame::FRAME_ATTR_EXACT_DEPTH|Frame::FRAME_ATTR_CAPTURE_DEPTH_2, objRef);
4450
4451 if (objRef == NULL)
4452 COMPlusThrow(kArgumentNullException);
4453
4454 if (timeOut < -1)
4455 COMPlusThrow(kArgumentOutOfRangeException);
4456
4457 GCPROTECT_BEGININTERIOR(pbLockTaken);
4458
4459 if (GET_THREAD()->CatchAtSafePointOpportunistic())
4460 {
4461 GET_THREAD()->PulseGCMode();
4462 }
4463
4464 BOOL result = objRef->TryEnterObjMonitor(timeOut);
4465 *pbLockTaken = result != FALSE;
4466
4467 GCPROTECT_END();
4468 HELPER_METHOD_FRAME_END();
4469
4470 FC_INNER_EPILOG();
4471}
4472
4473#include <optsmallperfcritical.h>
4474HCIMPL3(void, JIT_MonTryEnter_Portable, Object* obj, INT32 timeOut, BYTE* pbLockTaken)
4475{
4476 FCALL_CONTRACT;
4477
4478 AwareLock::EnterHelperResult result;
4479 Thread * pCurThread;
4480
4481 if (obj == NULL)
4482 {
4483 goto FramedLockHelper;
4484 }
4485
4486 if (timeOut < -1)
4487 {
4488 goto FramedLockHelper;
4489 }
4490
4491 pCurThread = GetThread();
4492
4493 if (pCurThread->CatchAtSafePointOpportunistic())
4494 {
4495 goto FramedLockHelper;
4496 }
4497
4498 result = obj->EnterObjMonitorHelper(pCurThread);
4499 if (result == AwareLock::EnterHelperResult_Entered)
4500 {
4501 *pbLockTaken = 1;
4502 return;
4503 }
4504 if (result == AwareLock::EnterHelperResult_Contention)
4505 {
4506 if (timeOut == 0)
4507 {
4508 return;
4509 }
4510
4511 result = obj->EnterObjMonitorHelperSpin(pCurThread);
4512 if (result == AwareLock::EnterHelperResult_Entered)
4513 {
4514 *pbLockTaken = 1;
4515 return;
4516 }
4517 }
4518
4519FramedLockHelper:
4520 FC_INNER_RETURN_VOID(JIT_MonTryEnter_Helper(obj, timeOut, pbLockTaken));
4521}
4522HCIMPLEND
4523#include <optdefault.h>
4524
4525/*********************************************************************/
4526NOINLINE static void JIT_MonExit_Helper(Object* obj, BYTE* pbLockTaken)
4527{
4528 FC_INNER_PROLOG(JIT_MonExit);
4529
4530 OBJECTREF objRef = ObjectToOBJECTREF(obj);
4531
4532 // Monitor helpers are used as both hcalls and fcalls, thus we need exact depth.
4533 HELPER_METHOD_FRAME_BEGIN_ATTRIB_1(Frame::FRAME_ATTR_NO_THREAD_ABORT|Frame::FRAME_ATTR_EXACT_DEPTH|Frame::FRAME_ATTR_CAPTURE_DEPTH_2, objRef);
4534
4535 if (objRef == NULL)
4536 COMPlusThrow(kArgumentNullException);
4537
4538 if (!objRef->LeaveObjMonitor())
4539 COMPlusThrow(kSynchronizationLockException);
4540
4541 if (pbLockTaken != 0) *pbLockTaken = 0;
4542
4543 TESTHOOKCALL(AppDomainCanBeUnloaded(GET_THREAD()->GetDomain()->GetId().m_dwId,FALSE));
4544
4545 if (GET_THREAD()->IsAbortRequested()) {
4546 GET_THREAD()->HandleThreadAbort();
4547 }
4548
4549 HELPER_METHOD_FRAME_END();
4550
4551 FC_INNER_EPILOG();
4552}
4553
4554NOINLINE static void JIT_MonExit_Signal(Object* obj)
4555{
4556 FC_INNER_PROLOG(JIT_MonExit);
4557
4558 OBJECTREF objRef = ObjectToOBJECTREF(obj);
4559
4560 // Monitor helpers are used as both hcalls and fcalls, thus we need exact depth.
4561 HELPER_METHOD_FRAME_BEGIN_ATTRIB_1(Frame::FRAME_ATTR_NO_THREAD_ABORT|Frame::FRAME_ATTR_EXACT_DEPTH|Frame::FRAME_ATTR_CAPTURE_DEPTH_2, objRef);
4562
4563 // Signal the event
4564 SyncBlock *psb = objRef->PassiveGetSyncBlock();
4565 if (psb != NULL)
4566 psb->QuickGetMonitor()->Signal();
4567
4568 TESTHOOKCALL(AppDomainCanBeUnloaded(GET_THREAD()->GetDomain()->GetId().m_dwId,FALSE));
4569
4570 if (GET_THREAD()->IsAbortRequested()) {
4571 GET_THREAD()->HandleThreadAbort();
4572 }
4573
4574 HELPER_METHOD_FRAME_END();
4575
4576 FC_INNER_EPILOG();
4577}
4578
4579#include <optsmallperfcritical.h>
4580FCIMPL1(void, JIT_MonExit_Portable, Object* obj)
4581{
4582 FCALL_CONTRACT;
4583
4584 AwareLock::LeaveHelperAction action;
4585
4586 if (obj == NULL)
4587 {
4588 goto FramedLockHelper;
4589 }
4590
4591 // Handle the simple case without erecting helper frame
4592 action = obj->LeaveObjMonitorHelper(GetThread());
4593 if (action == AwareLock::LeaveHelperAction_None)
4594 {
4595 return;
4596 }
4597 if (action == AwareLock::LeaveHelperAction_Signal)
4598 {
4599 FC_INNER_RETURN_VOID(JIT_MonExit_Signal(obj));
4600 }
4601
4602FramedLockHelper:
4603 FC_INNER_RETURN_VOID(JIT_MonExit_Helper(obj, NULL));
4604}
4605HCIMPLEND
4606
4607HCIMPL_MONHELPER(JIT_MonExitWorker_Portable, Object* obj)
4608{
4609 FCALL_CONTRACT;
4610
4611 MONHELPER_STATE(_ASSERTE(pbLockTaken != NULL));
4612 MONHELPER_STATE(if (*pbLockTaken == 0) return;)
4613
4614 AwareLock::LeaveHelperAction action;
4615
4616 if (obj == NULL)
4617 {
4618 goto FramedLockHelper;
4619 }
4620
4621 // Handle the simple case without erecting helper frame
4622 action = obj->LeaveObjMonitorHelper(GetThread());
4623 if (action == AwareLock::LeaveHelperAction_None)
4624 {
4625 MONHELPER_STATE(*pbLockTaken = 0;)
4626 return;
4627 }
4628 if (action == AwareLock::LeaveHelperAction_Signal)
4629 {
4630 MONHELPER_STATE(*pbLockTaken = 0;)
4631 FC_INNER_RETURN_VOID(JIT_MonExit_Signal(obj));
4632 }
4633
4634FramedLockHelper:
4635 FC_INNER_RETURN_VOID(JIT_MonExit_Helper(obj, MONHELPER_ARG));
4636}
4637HCIMPLEND
4638#include <optdefault.h>
4639
4640/*********************************************************************/
4641NOINLINE static void JIT_MonEnterStatic_Helper(AwareLock *lock, BYTE* pbLockTaken)
4642{
4643 // The following makes sure that Monitor.Enter shows up on thread abort
4644 // stack walks (otherwise Monitor.Enter called within a CER can block a
4645 // thread abort indefinitely). Setting the __me internal variable (normally
4646 // only set for fcalls) will cause the helper frame below to be able to
4647 // backtranslate into the method desc for the Monitor.Enter fcall.
4648 FC_INNER_PROLOG(JIT_MonEnter);
4649
4650 // Monitor helpers are used as both hcalls and fcalls, thus we need exact depth.
4651 HELPER_METHOD_FRAME_BEGIN_ATTRIB_NOPOLL(Frame::FRAME_ATTR_EXACT_DEPTH|Frame::FRAME_ATTR_CAPTURE_DEPTH_2);
4652 lock->Enter();
4653 MONHELPER_STATE(*pbLockTaken = 1;)
4654 HELPER_METHOD_FRAME_END_POLL();
4655
4656 FC_INNER_EPILOG();
4657}
4658
4659#include <optsmallperfcritical.h>
4660HCIMPL_MONHELPER(JIT_MonEnterStatic_Portable, AwareLock *lock)
4661{
4662 FCALL_CONTRACT;
4663
4664 _ASSERTE(lock);
4665
4666 MONHELPER_STATE(_ASSERTE(pbLockTaken != NULL && *pbLockTaken == 0));
4667
4668 Thread *pCurThread = GetThread();
4669
4670 if (pCurThread->CatchAtSafePointOpportunistic())
4671 {
4672 goto FramedLockHelper;
4673 }
4674
4675 if (lock->TryEnterHelper(pCurThread))
4676 {
4677#if defined(_DEBUG) && defined(TRACK_SYNC)
4678 // The best place to grab this is from the ECall frame
4679 Frame * pFrame = pCurThread->GetFrame();
4680 int caller = (pFrame && pFrame != FRAME_TOP ? (int) pFrame->GetReturnAddress() : -1);
4681 pCurThread->m_pTrackSync->EnterSync(caller, lock);
4682#endif
4683
4684 MONHELPER_STATE(*pbLockTaken = 1;)
4685 return;
4686 }
4687
4688FramedLockHelper:
4689 FC_INNER_RETURN_VOID(JIT_MonEnterStatic_Helper(lock, MONHELPER_ARG));
4690}
4691HCIMPLEND
4692#include <optdefault.h>
4693
4694/*********************************************************************/
4695NOINLINE static void JIT_MonExitStatic_Helper(AwareLock *lock, BYTE* pbLockTaken)
4696{
4697 FC_INNER_PROLOG(JIT_MonExit);
4698
4699 HELPER_METHOD_FRAME_BEGIN_ATTRIB(Frame::FRAME_ATTR_NO_THREAD_ABORT|Frame::FRAME_ATTR_EXACT_DEPTH|Frame::FRAME_ATTR_CAPTURE_DEPTH_2);
4700
4701 // Error, yield or contention
4702 if (!lock->Leave())
4703 COMPlusThrow(kSynchronizationLockException);
4704 MONHELPER_STATE(*pbLockTaken = 0;)
4705
4706 TESTHOOKCALL(AppDomainCanBeUnloaded(GET_THREAD()->GetDomain()->GetId().m_dwId,FALSE));
4707 if (GET_THREAD()->IsAbortRequested()) {
4708 GET_THREAD()->HandleThreadAbort();
4709 }
4710
4711 HELPER_METHOD_FRAME_END();
4712
4713 FC_INNER_EPILOG();
4714}
4715
4716NOINLINE static void JIT_MonExitStatic_Signal(AwareLock *lock)
4717{
4718 FC_INNER_PROLOG(JIT_MonExit);
4719
4720 HELPER_METHOD_FRAME_BEGIN_ATTRIB(Frame::FRAME_ATTR_NO_THREAD_ABORT|Frame::FRAME_ATTR_EXACT_DEPTH|Frame::FRAME_ATTR_CAPTURE_DEPTH_2);
4721
4722 lock->Signal();
4723
4724 TESTHOOKCALL(AppDomainCanBeUnloaded(GET_THREAD()->GetDomain()->GetId().m_dwId,FALSE));
4725 if (GET_THREAD()->IsAbortRequested()) {
4726 GET_THREAD()->HandleThreadAbort();
4727 }
4728
4729 HELPER_METHOD_FRAME_END();
4730
4731 FC_INNER_EPILOG();
4732}
4733
4734#include <optsmallperfcritical.h>
4735HCIMPL_MONHELPER(JIT_MonExitStatic_Portable, AwareLock *lock)
4736{
4737 FCALL_CONTRACT;
4738
4739 _ASSERTE(lock);
4740
4741 MONHELPER_STATE(_ASSERTE(pbLockTaken != NULL));
4742 MONHELPER_STATE(if (*pbLockTaken == 0) return;)
4743
4744 // Handle the simple case without erecting helper frame
4745 AwareLock::LeaveHelperAction action = lock->LeaveHelper(GetThread());
4746 if (action == AwareLock::LeaveHelperAction_None)
4747 {
4748 MONHELPER_STATE(*pbLockTaken = 0;)
4749 return;
4750 }
4751 else
4752 if (action == AwareLock::LeaveHelperAction_Signal)
4753 {
4754 MONHELPER_STATE(*pbLockTaken = 0;)
4755 FC_INNER_RETURN_VOID(JIT_MonExitStatic_Signal(lock));
4756 }
4757
4758 FC_INNER_RETURN_VOID(JIT_MonExitStatic_Helper(lock, MONHELPER_ARG));
4759}
4760HCIMPLEND
4761#include <optdefault.h>
4762
4763HCIMPL1(void *, JIT_GetSyncFromClassHandle, CORINFO_CLASS_HANDLE typeHnd_)
4764 CONTRACTL {
4765 FCALL_CHECK;
4766 PRECONDITION(typeHnd_ != NULL);
4767 } CONTRACTL_END;
4768
4769 void * result = NULL;
4770
4771 HELPER_METHOD_FRAME_BEGIN_RET_NOPOLL(); // Set up a frame
4772
4773 TypeHandle typeHnd(typeHnd_);
4774 MethodTable *pMT = typeHnd.AsMethodTable();
4775
4776 OBJECTREF ref = pMT->GetManagedClassObject();
4777 _ASSERTE(ref);
4778
4779 result = (void*)ref->GetSyncBlock()->GetMonitor();
4780
4781 HELPER_METHOD_FRAME_END();
4782
4783 return(result);
4784
4785HCIMPLEND
4786
4787
4788//========================================================================
4789//
4790// EXCEPTION HELPERS
4791//
4792//========================================================================
4793
4794// In general, we want to use COMPlusThrow to throw exceptions. However,
4795// the IL_Throw helper is a special case. Here, we're called from
4796// managed code. We have a guarantee that the first FS:0 handler
4797// is our COMPlusFrameHandler. We could call COMPlusThrow(), which pushes
4798// another handler, but there is a significant (10% on JGFExceptionBench)
4799// performance gain if we avoid this by calling RaiseTheException()
4800// directly.
4801//
4802
4803/*************************************************************/
4804
4805HCIMPL1(void, IL_Throw, Object* obj)
4806{
4807 FCALL_CONTRACT;
4808
4809 // This "violation" isn't a really a violation.
4810 // We are calling a assembly helper that can't have an SO Tolerance contract
4811 CONTRACT_VIOLATION(SOToleranceViolation);
4812 /* Make no assumptions about the current machine state */
4813 ResetCurrentContext();
4814
4815 FC_GC_POLL_NOT_NEEDED(); // throws always open up for GC
4816
4817 HELPER_METHOD_FRAME_BEGIN_ATTRIB_NOPOLL(Frame::FRAME_ATTR_EXCEPTION); // Set up a frame
4818
4819 OBJECTREF oref = ObjectToOBJECTREF(obj);
4820
4821#if defined(_DEBUG) && defined(_TARGET_X86_)
4822 __helperframe.InsureInit(false, NULL);
4823 g_ExceptionEIP = (LPVOID)__helperframe.GetReturnAddress();
4824#endif // defined(_DEBUG) && defined(_TARGET_X86_)
4825
4826
4827 if (oref == 0)
4828 COMPlusThrow(kNullReferenceException);
4829 else
4830 if (!IsException(oref->GetMethodTable()))
4831 {
4832 GCPROTECT_BEGIN(oref);
4833
4834 WrapNonCompliantException(&oref);
4835
4836 GCPROTECT_END();
4837 }
4838 else
4839 { // We know that the object derives from System.Exception
4840 if (g_CLRPolicyRequested &&
4841 oref->GetMethodTable() == g_pOutOfMemoryExceptionClass)
4842 {
4843 EEPolicy::HandleOutOfMemory();
4844 }
4845
4846 // If the flag indicating ForeignExceptionRaise has been set,
4847 // then do not clear the "_stackTrace" field of the exception object.
4848 if (GetThread()->GetExceptionState()->IsRaisingForeignException())
4849 {
4850 ((EXCEPTIONREF)oref)->SetStackTraceString(NULL);
4851 }
4852 else
4853 {
4854 ((EXCEPTIONREF)oref)->ClearStackTracePreservingRemoteStackTrace();
4855 }
4856 }
4857
4858#ifdef FEATURE_CORRUPTING_EXCEPTIONS
4859 if (!g_pConfig->LegacyCorruptedStateExceptionsPolicy())
4860 {
4861 // Within the VM, we could have thrown and caught a managed exception. This is done by
4862 // RaiseTheException that will flag that exception's corruption severity to be used
4863 // incase it leaks out to managed code.
4864 //
4865 // If it does not leak out, but ends up calling into managed code that throws,
4866 // we will come here. In such a case, simply reset the corruption-severity
4867 // since we want the exception being thrown to have its correct severity set
4868 // when CLR's managed code exception handler sets it.
4869
4870 ThreadExceptionState *pExState = GetThread()->GetExceptionState();
4871 pExState->SetLastActiveExceptionCorruptionSeverity(NotSet);
4872 }
4873#endif // FEATURE_CORRUPTING_EXCEPTIONS
4874
4875 RaiseTheExceptionInternalOnly(oref, FALSE);
4876
4877 HELPER_METHOD_FRAME_END();
4878}
4879HCIMPLEND
4880
4881/*************************************************************/
4882
4883HCIMPL0(void, IL_Rethrow)
4884{
4885 FCALL_CONTRACT;
4886
4887 FC_GC_POLL_NOT_NEEDED(); // throws always open up for GC
4888
4889 HELPER_METHOD_FRAME_BEGIN_ATTRIB_NOPOLL(Frame::FRAME_ATTR_EXCEPTION); // Set up a frame
4890
4891 OBJECTREF throwable = GetThread()->GetThrowable();
4892 if (throwable != NULL)
4893 {
4894 if (g_CLRPolicyRequested &&
4895 throwable->GetMethodTable() == g_pOutOfMemoryExceptionClass)
4896 {
4897 EEPolicy::HandleOutOfMemory();
4898 }
4899
4900 RaiseTheExceptionInternalOnly(throwable, TRUE);
4901 }
4902 else
4903 {
4904 // This can only be the result of bad IL (or some internal EE failure).
4905 _ASSERTE(!"No throwable on rethrow");
4906 RealCOMPlusThrow(kInvalidProgramException, (UINT)IDS_EE_RETHROW_NOT_ALLOWED);
4907 }
4908
4909 HELPER_METHOD_FRAME_END();
4910}
4911HCIMPLEND
4912
4913/*********************************************************************/
4914HCIMPL0(void, JIT_RngChkFail)
4915{
4916 FCALL_CONTRACT;
4917
4918 /* Make no assumptions about the current machine state */
4919 ResetCurrentContext();
4920
4921 FC_GC_POLL_NOT_NEEDED(); // throws always open up for GC
4922
4923 HELPER_METHOD_FRAME_BEGIN_ATTRIB_NOPOLL(Frame::FRAME_ATTR_EXCEPTION); // Set up a frame
4924
4925 COMPlusThrow(kIndexOutOfRangeException);
4926
4927 HELPER_METHOD_FRAME_END();
4928}
4929HCIMPLEND
4930
4931/*********************************************************************/
4932HCIMPL0(void, JIT_ThrowArgumentException)
4933{
4934 FCALL_CONTRACT;
4935
4936 /* Make no assumptions about the current machine state */
4937 ResetCurrentContext();
4938
4939 FC_GC_POLL_NOT_NEEDED(); // throws always open up for GC
4940
4941 HELPER_METHOD_FRAME_BEGIN_ATTRIB_NOPOLL(Frame::FRAME_ATTR_EXCEPTION); // Set up a frame
4942
4943 COMPlusThrow(kArgumentException);
4944
4945 HELPER_METHOD_FRAME_END();
4946}
4947HCIMPLEND
4948
4949/*********************************************************************/
4950HCIMPL0(void, JIT_ThrowArgumentOutOfRangeException)
4951{
4952 FCALL_CONTRACT;
4953
4954 /* Make no assumptions about the current machine state */
4955 ResetCurrentContext();
4956
4957 FC_GC_POLL_NOT_NEEDED(); // throws always open up for GC
4958
4959 HELPER_METHOD_FRAME_BEGIN_ATTRIB_NOPOLL(Frame::FRAME_ATTR_EXCEPTION); // Set up a frame
4960
4961 COMPlusThrow(kArgumentOutOfRangeException);
4962
4963 HELPER_METHOD_FRAME_END();
4964}
4965HCIMPLEND
4966
4967/*********************************************************************/
4968HCIMPL0(void, JIT_ThrowNotImplementedException)
4969{
4970 FCALL_CONTRACT;
4971
4972 /* Make no assumptions about the current machine state */
4973 ResetCurrentContext();
4974
4975 FC_GC_POLL_NOT_NEEDED(); // throws always open up for GC
4976
4977 HELPER_METHOD_FRAME_BEGIN_ATTRIB_NOPOLL(Frame::FRAME_ATTR_EXCEPTION); // Set up a frame
4978
4979 COMPlusThrow(kNotImplementedException);
4980
4981 HELPER_METHOD_FRAME_END();
4982}
4983HCIMPLEND
4984
4985/*********************************************************************/
4986HCIMPL0(void, JIT_ThrowPlatformNotSupportedException)
4987{
4988 FCALL_CONTRACT;
4989
4990 /* Make no assumptions about the current machine state */
4991 ResetCurrentContext();
4992
4993 FC_GC_POLL_NOT_NEEDED(); // throws always open up for GC
4994
4995 HELPER_METHOD_FRAME_BEGIN_ATTRIB_NOPOLL(Frame::FRAME_ATTR_EXCEPTION); // Set up a frame
4996
4997 COMPlusThrow(kPlatformNotSupportedException);
4998
4999 HELPER_METHOD_FRAME_END();
5000}
5001HCIMPLEND
5002
5003/*********************************************************************/
5004HCIMPL0(void, JIT_ThrowTypeNotSupportedException)
5005{
5006 FCALL_CONTRACT;
5007
5008 /* Make no assumptions about the current machine state */
5009 ResetCurrentContext();
5010
5011 FC_GC_POLL_NOT_NEEDED(); // throws always open up for GC
5012
5013 HELPER_METHOD_FRAME_BEGIN_ATTRIB_NOPOLL(Frame::FRAME_ATTR_EXCEPTION); // Set up a frame
5014
5015 COMPlusThrow(kNotSupportedException, W("Arg_TypeNotSupported"));
5016
5017 HELPER_METHOD_FRAME_END();
5018}
5019HCIMPLEND
5020
5021/*********************************************************************/
5022HCIMPL0(void, JIT_Overflow)
5023{
5024 FCALL_CONTRACT;
5025
5026 /* Make no assumptions about the current machine state */
5027 ResetCurrentContext();
5028
5029 FC_GC_POLL_NOT_NEEDED(); // throws always open up for GC
5030
5031 HELPER_METHOD_FRAME_BEGIN_ATTRIB_NOPOLL(Frame::FRAME_ATTR_EXCEPTION); // Set up a frame
5032
5033 COMPlusThrow(kOverflowException);
5034
5035 HELPER_METHOD_FRAME_END();
5036}
5037HCIMPLEND
5038
5039/*********************************************************************/
5040HCIMPL0(void, JIT_ThrowDivZero)
5041{
5042 FCALL_CONTRACT;
5043
5044 /* Make no assumptions about the current machine state */
5045 ResetCurrentContext();
5046
5047 FC_GC_POLL_NOT_NEEDED(); // throws always open up for GC
5048
5049 HELPER_METHOD_FRAME_BEGIN_ATTRIB_NOPOLL(Frame::FRAME_ATTR_EXCEPTION); // Set up a frame
5050
5051 COMPlusThrow(kDivideByZeroException);
5052
5053 HELPER_METHOD_FRAME_END();
5054}
5055HCIMPLEND
5056
5057/*********************************************************************/
5058HCIMPL0(void, JIT_ThrowNullRef)
5059{
5060 FCALL_CONTRACT;
5061
5062 /* Make no assumptions about the current machine state */
5063 ResetCurrentContext();
5064
5065 FC_GC_POLL_NOT_NEEDED(); // throws always open up for GC
5066
5067 HELPER_METHOD_FRAME_BEGIN_ATTRIB_NOPOLL(Frame::FRAME_ATTR_EXCEPTION); // Set up a frame
5068
5069 COMPlusThrow(kNullReferenceException);
5070
5071 HELPER_METHOD_FRAME_END();
5072}
5073HCIMPLEND
5074
5075/*********************************************************************/
5076HCIMPL1(void, IL_VerificationError, int ilOffset)
5077{
5078 FCALL_CONTRACT;
5079
5080 FC_GC_POLL_NOT_NEEDED(); // throws always open up for GC
5081 HELPER_METHOD_FRAME_BEGIN_ATTRIB_NOPOLL(Frame::FRAME_ATTR_EXCEPTION); // Set up a frame
5082
5083 COMPlusThrow(kVerificationException);
5084
5085 HELPER_METHOD_FRAME_END();
5086}
5087HCIMPLEND
5088
5089/*********************************************************************/
5090HCIMPL1(void, JIT_SecurityUnmanagedCodeException, CORINFO_CLASS_HANDLE typeHnd_)
5091{
5092 FCALL_CONTRACT;
5093
5094 FC_GC_POLL_NOT_NEEDED(); // throws always open up for GC
5095
5096 HELPER_METHOD_FRAME_BEGIN_ATTRIB_NOPOLL(Frame::FRAME_ATTR_EXCEPTION); // Set up a frame
5097
5098 COMPlusThrow(kSecurityException);
5099
5100 HELPER_METHOD_FRAME_END();
5101}
5102HCIMPLEND
5103
5104/*********************************************************************/
5105static RuntimeExceptionKind MapCorInfoExceptionToRuntimeExceptionKind(unsigned exceptNum)
5106{
5107 LIMITED_METHOD_CONTRACT;
5108
5109 static const RuntimeExceptionKind map[CORINFO_Exception_Count] =
5110 {
5111 kNullReferenceException,
5112 kDivideByZeroException,
5113 kInvalidCastException,
5114 kIndexOutOfRangeException,
5115 kOverflowException,
5116 kSynchronizationLockException,
5117 kArrayTypeMismatchException,
5118 kRankException,
5119 kArgumentNullException,
5120 kArgumentException,
5121 };
5122
5123 // spot check of the array above
5124 _ASSERTE(map[CORINFO_NullReferenceException] == kNullReferenceException);
5125 _ASSERTE(map[CORINFO_DivideByZeroException] == kDivideByZeroException);
5126 _ASSERTE(map[CORINFO_IndexOutOfRangeException] == kIndexOutOfRangeException);
5127 _ASSERTE(map[CORINFO_OverflowException] == kOverflowException);
5128 _ASSERTE(map[CORINFO_SynchronizationLockException] == kSynchronizationLockException);
5129 _ASSERTE(map[CORINFO_ArrayTypeMismatchException] == kArrayTypeMismatchException);
5130 _ASSERTE(map[CORINFO_RankException] == kRankException);
5131 _ASSERTE(map[CORINFO_ArgumentNullException] == kArgumentNullException);
5132 _ASSERTE(map[CORINFO_ArgumentException] == kArgumentException);
5133
5134 PREFIX_ASSUME(exceptNum < CORINFO_Exception_Count);
5135 return map[exceptNum];
5136}
5137
5138/*********************************************************************/
5139HCIMPL1(void, JIT_InternalThrow, unsigned exceptNum)
5140{
5141 FCALL_CONTRACT;
5142
5143 FC_GC_POLL_NOT_NEEDED(); // throws always open up for GC
5144
5145 HELPER_METHOD_FRAME_BEGIN_ATTRIB_NOPOLL(Frame::FRAME_ATTR_EXACT_DEPTH);
5146 COMPlusThrow(MapCorInfoExceptionToRuntimeExceptionKind(exceptNum));
5147 HELPER_METHOD_FRAME_END();
5148}
5149HCIMPLEND
5150
5151/*********************************************************************/
5152HCIMPL1(void*, JIT_InternalThrowFromHelper, unsigned exceptNum)
5153{
5154 FCALL_CONTRACT;
5155
5156 FC_GC_POLL_NOT_NEEDED(); // throws always open up for GC
5157 HELPER_METHOD_FRAME_BEGIN_RET_ATTRIB_NOPOLL(Frame::FRAME_ATTR_CAPTURE_DEPTH_2|Frame::FRAME_ATTR_EXACT_DEPTH);
5158 COMPlusThrow(MapCorInfoExceptionToRuntimeExceptionKind(exceptNum));
5159 HELPER_METHOD_FRAME_END();
5160 return NULL;
5161}
5162HCIMPLEND
5163
5164#ifndef STATUS_STACK_BUFFER_OVERRUN // Not defined yet in CESDK includes
5165# define STATUS_STACK_BUFFER_OVERRUN ((NTSTATUS)0xC0000409L)
5166#endif
5167
5168/*********************************************************************
5169 * Kill process without using any potentially corrupted data:
5170 * o Do not throw an exception
5171 * o Do not call any indirect/virtual functions
5172 * o Do not depend on any global data
5173 *
5174 * This function is used by the security checks for unsafe buffers (VC's -GS checks)
5175 */
5176
5177void DoJITFailFast ()
5178{
5179 CONTRACTL {
5180 MODE_ANY;
5181 WRAPPER(GC_TRIGGERS);
5182 WRAPPER(THROWS);
5183 SO_NOT_MAINLINE; // If process is coming down, SO probe is not going to do much good
5184 } CONTRACTL_END;
5185
5186 LOG((LF_ALWAYS, LL_FATALERROR, "Unsafe buffer security check failure: Buffer overrun detected"));
5187
5188#ifdef _DEBUG
5189 if (g_pConfig->fAssertOnFailFast())
5190 _ASSERTE(!"About to FailFast. set ComPlus_AssertOnFailFast=0 if this is expected");
5191#endif
5192
5193#ifndef FEATURE_PAL
5194 // Use the function provided by the C runtime.
5195 //
5196 // Ideally, this function is called directly from managed code so
5197 // that the address of the managed function will be included in the
5198 // error log. However, this function is also used by the stackwalker.
5199 // To keep things simple, we just call it from here.
5200#if defined(_TARGET_X86_)
5201 __report_gsfailure();
5202#else // !defined(_TARGET_X86_)
5203 // On AMD64/IA64/ARM, we need to pass a stack cookie, which will be saved in the context record
5204 // that is used to raise the buffer-overrun exception by __report_gsfailure.
5205 __report_gsfailure((ULONG_PTR)0);
5206#endif // defined(_TARGET_X86_)
5207#else // FEATURE_PAL
5208 if(ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_Context, FailFast))
5209 {
5210 // Fire an ETW FailFast event
5211 FireEtwFailFast(W("Unsafe buffer security check failure: Buffer overrun detected"),
5212 (const PVOID)GetThread()->GetFrame()->GetIP(),
5213 STATUS_STACK_BUFFER_OVERRUN,
5214 COR_E_EXECUTIONENGINE,
5215 GetClrInstanceId());
5216 }
5217
5218 TerminateProcess(GetCurrentProcess(), STATUS_STACK_BUFFER_OVERRUN);
5219#endif // !FEATURE_PAL
5220}
5221
5222HCIMPL0(void, JIT_FailFast)
5223{
5224 FCALL_CONTRACT;
5225 DoJITFailFast ();
5226}
5227HCIMPLEND
5228
5229HCIMPL2(void, JIT_ThrowMethodAccessException, CORINFO_METHOD_HANDLE caller, CORINFO_METHOD_HANDLE callee)
5230{
5231 FCALL_CONTRACT;
5232
5233 FC_GC_POLL_NOT_NEEDED(); // throws always open up for GC
5234
5235 HELPER_METHOD_FRAME_BEGIN_ATTRIB_NOPOLL(Frame::FRAME_ATTR_EXCEPTION); // Set up a frame
5236
5237 MethodDesc* pCallerMD = GetMethod(caller);
5238
5239 _ASSERTE(pCallerMD != NULL);
5240 StaticAccessCheckContext accessContext(pCallerMD);
5241
5242 ThrowMethodAccessException(&accessContext, GetMethod(callee));
5243
5244 HELPER_METHOD_FRAME_END();
5245}
5246HCIMPLEND
5247
5248HCIMPL2(void, JIT_ThrowFieldAccessException, CORINFO_METHOD_HANDLE caller, CORINFO_FIELD_HANDLE callee)
5249{
5250 FCALL_CONTRACT;
5251
5252 FC_GC_POLL_NOT_NEEDED(); // throws always open up for GC
5253
5254 HELPER_METHOD_FRAME_BEGIN_ATTRIB_NOPOLL(Frame::FRAME_ATTR_EXCEPTION); // Set up a frame
5255
5256 MethodDesc* pCallerMD = GetMethod(caller);
5257
5258 _ASSERTE(pCallerMD != NULL);
5259 StaticAccessCheckContext accessContext(pCallerMD);
5260
5261 ThrowFieldAccessException(&accessContext, reinterpret_cast<FieldDesc *>(callee));
5262
5263 HELPER_METHOD_FRAME_END();
5264}
5265HCIMPLEND;
5266
5267HCIMPL2(void, JIT_ThrowClassAccessException, CORINFO_METHOD_HANDLE caller, CORINFO_CLASS_HANDLE callee)
5268{
5269 FCALL_CONTRACT;
5270
5271 FC_GC_POLL_NOT_NEEDED(); // throws always open up for GC
5272
5273 HELPER_METHOD_FRAME_BEGIN_ATTRIB_NOPOLL(Frame::FRAME_ATTR_EXCEPTION); // Set up a frame
5274
5275 MethodDesc* pCallerMD = GetMethod(caller);
5276
5277 _ASSERTE(pCallerMD != NULL);
5278 StaticAccessCheckContext accessContext(pCallerMD);
5279
5280 ThrowTypeAccessException(&accessContext, TypeHandle(callee).GetMethodTable());
5281
5282 HELPER_METHOD_FRAME_END();
5283}
5284HCIMPLEND;
5285
5286//========================================================================
5287//
5288// SECURITY HELPERS
5289//
5290//========================================================================
5291
5292HCIMPL2(void, JIT_DelegateSecurityCheck, CORINFO_CLASS_HANDLE delegateHnd, CORINFO_METHOD_HANDLE calleeMethodHnd)
5293{
5294 FCALL_CONTRACT;
5295}
5296HCIMPLEND
5297
5298HCIMPL4(void, JIT_MethodAccessCheck, CORINFO_METHOD_HANDLE callerMethodHnd, CORINFO_METHOD_HANDLE calleeMethodHnd, CORINFO_CLASS_HANDLE calleeTypeHnd, CorInfoSecurityRuntimeChecks check)
5299{
5300 FCALL_CONTRACT;
5301}
5302HCIMPLEND
5303
5304HCIMPL3(void, JIT_FieldAccessCheck, CORINFO_METHOD_HANDLE callerMethodHnd, CORINFO_FIELD_HANDLE calleeFieldHnd, CorInfoSecurityRuntimeChecks check)
5305{
5306 FCALL_CONTRACT;
5307}
5308HCIMPLEND
5309
5310HCIMPL3(void, JIT_ClassAccessCheck, CORINFO_METHOD_HANDLE callerMethodHnd, CORINFO_CLASS_HANDLE calleeClassHnd, CorInfoSecurityRuntimeChecks check)
5311{
5312 FCALL_CONTRACT;
5313}
5314HCIMPLEND
5315
5316HCIMPL2(void, JIT_Security_Prolog, CORINFO_METHOD_HANDLE methHnd_, OBJECTREF* ppFrameSecDesc)
5317{
5318 FCALL_CONTRACT;
5319}
5320HCIMPLEND
5321
5322HCIMPL2(void, JIT_Security_Prolog_Framed, CORINFO_METHOD_HANDLE methHnd_, OBJECTREF* ppFrameSecDesc)
5323{
5324 FCALL_CONTRACT;
5325}
5326HCIMPLEND
5327
5328HCIMPL1(void, JIT_VerificationRuntimeCheck, CORINFO_METHOD_HANDLE methHnd_)
5329{
5330 FCALL_CONTRACT;
5331}
5332HCIMPLEND
5333
5334
5335//========================================================================
5336//
5337// DEBUGGER/PROFILER HELPERS
5338//
5339//========================================================================
5340
5341/*********************************************************************/
5342// JIT_UserBreakpoint
5343// Called by the JIT whenever a cee_break instruction should be executed.
5344// This ensures that enough info will be pushed onto the stack so that
5345// we can continue from the exception w/o having special code elsewhere.
5346// Body of function is written by debugger team
5347// Args: None
5348//
5349// <TODO> make sure this actually gets called by all JITters</TODO>
5350// Note: this code is duplicated in the ecall in VM\DebugDebugger:Break,
5351// so propogate changes to there
5352
5353HCIMPL0(void, JIT_UserBreakpoint)
5354{
5355 FCALL_CONTRACT;
5356
5357 HELPER_METHOD_FRAME_BEGIN_NOPOLL(); // Set up a frame
5358
5359#ifdef DEBUGGING_SUPPORTED
5360 FrameWithCookie<DebuggerExitFrame> __def;
5361
5362 MethodDescCallSite breakCanThrow(METHOD__DEBUGGER__BREAK_CAN_THROW);
5363
5364 // Call Diagnostic.Debugger.BreakCanThrow instead. This will make us demand
5365 // UnmanagedCode permission if debugger is not attached.
5366 //
5367 breakCanThrow.Call((ARG_SLOT*)NULL);
5368
5369 __def.Pop();
5370#else // !DEBUGGING_SUPPORTED
5371 _ASSERTE(!"JIT_UserBreakpoint called, but debugging support is not available in this build.");
5372#endif // !DEBUGGING_SUPPORTED
5373
5374 HELPER_METHOD_FRAME_END_POLL();
5375}
5376HCIMPLEND
5377
5378#if defined(_MSC_VER)
5379// VC++ Compiler intrinsic.
5380extern "C" void * _ReturnAddress(void);
5381#endif
5382
5383/*********************************************************************/
5384// Callback for Just-My-Code probe
5385// Probe looks like:
5386// if (*pFlag != 0) call JIT_DbgIsJustMyCode
5387// So this is only called if the flag (obtained by GetJMCFlagAddr) is
5388// non-zero.
5389// Body of this function is maintained by the debugger people.
5390HCIMPL0(void, JIT_DbgIsJustMyCode)
5391{
5392 FCALL_CONTRACT;
5393 SO_NOT_MAINLINE_FUNCTION;
5394
5395 // We need to get both the ip of the managed function this probe is in
5396 // (which will be our return address) and the frame pointer for that
5397 // function (since we can't get it later because we're pushing unmanaged
5398 // frames on the stack).
5399 void * ip = NULL;
5400
5401 // <NOTE>
5402 // In order for the return address to be correct, we must NOT call any
5403 // function before calling _ReturnAddress().
5404 // </NOTE>
5405 ip = _ReturnAddress();
5406
5407 _ASSERTE(ip != NULL);
5408
5409 // Call into debugger proper
5410 g_pDebugInterface->OnMethodEnter(ip);
5411
5412 return;
5413}
5414HCIMPLEND
5415
5416#if !(defined(_TARGET_X86_) || defined(_WIN64))
5417void JIT_ProfilerEnterLeaveTailcallStub(UINT_PTR ProfilerHandle)
5418{
5419 return;
5420}
5421#endif // !(_TARGET_X86_ || _WIN64)
5422
5423#ifdef PROFILING_SUPPORTED
5424
5425//---------------------------------------------------------------------------------------
5426//
5427// Sets the profiler's enter/leave/tailcall hooks into the JIT's dynamic helper
5428// function table.
5429//
5430// Arguments:
5431// pFuncEnter - Enter hook
5432// pFuncLeave - Leave hook
5433// pFuncTailcall - Tailcall hook
5434//
5435// For each hook parameter, if NULL is passed in, that will cause the JIT
5436// to insert calls to its default stub replacement for that hook, which
5437// just does a ret.
5438//
5439// Return Value:
5440// HRESULT indicating success or failure
5441//
5442// Notes:
5443// On IA64, this will allocate space for stubs to update GP, and that
5444// allocation may take locks and may throw on failure. Callers be warned.
5445//
5446
5447HRESULT EEToProfInterfaceImpl::SetEnterLeaveFunctionHooksForJit(FunctionEnter3 * pFuncEnter,
5448 FunctionLeave3 * pFuncLeave,
5449 FunctionTailcall3 * pFuncTailcall)
5450{
5451 CONTRACTL {
5452 THROWS;
5453 GC_NOTRIGGER;
5454 } CONTRACTL_END;
5455
5456 SetJitHelperFunction(
5457 CORINFO_HELP_PROF_FCN_ENTER,
5458 (pFuncEnter == NULL) ?
5459 reinterpret_cast<void *>(JIT_ProfilerEnterLeaveTailcallStub) :
5460 reinterpret_cast<void *>(pFuncEnter));
5461
5462 SetJitHelperFunction(
5463 CORINFO_HELP_PROF_FCN_LEAVE,
5464 (pFuncLeave == NULL) ?
5465 reinterpret_cast<void *>(JIT_ProfilerEnterLeaveTailcallStub) :
5466 reinterpret_cast<void *>(pFuncLeave));
5467
5468 SetJitHelperFunction(
5469 CORINFO_HELP_PROF_FCN_TAILCALL,
5470 (pFuncTailcall == NULL) ?
5471 reinterpret_cast<void *>(JIT_ProfilerEnterLeaveTailcallStub) :
5472 reinterpret_cast<void *>(pFuncTailcall));
5473
5474 return (S_OK);
5475}
5476#endif // PROFILING_SUPPORTED
5477
5478/*************************************************************/
5479HCIMPL1(void, JIT_LogMethodEnter, CORINFO_METHOD_HANDLE methHnd_)
5480 FCALL_CONTRACT;
5481
5482 //
5483 // Record an access to this method desc
5484 //
5485
5486 HELPER_METHOD_FRAME_BEGIN_NOPOLL();
5487
5488 g_IBCLogger.LogMethodCodeAccess(GetMethod(methHnd_));
5489
5490 HELPER_METHOD_FRAME_END_POLL();
5491
5492HCIMPLEND
5493
5494
5495
5496//========================================================================
5497//
5498// GC HELPERS
5499//
5500//========================================================================
5501
5502/*************************************************************/
5503HCIMPL3(VOID, JIT_StructWriteBarrier, void *dest, void* src, CORINFO_CLASS_HANDLE typeHnd_)
5504{
5505 FCALL_CONTRACT;
5506
5507 TypeHandle typeHnd(typeHnd_);
5508 MethodTable *pMT = typeHnd.AsMethodTable();
5509
5510 HELPER_METHOD_FRAME_BEGIN_NOPOLL(); // Set up a frame
5511 CopyValueClassUnchecked(dest, src, pMT);
5512 HELPER_METHOD_FRAME_END_POLL();
5513
5514}
5515HCIMPLEND
5516
5517/*************************************************************/
5518HCIMPL0(VOID, JIT_PollGC)
5519{
5520 FCALL_CONTRACT;
5521
5522 FC_GC_POLL_NOT_NEEDED();
5523
5524 Thread *thread = GetThread();
5525 if (thread->CatchAtSafePointOpportunistic()) // Does someone want this thread stopped?
5526 {
5527 HELPER_METHOD_FRAME_BEGIN_NOPOLL(); // Set up a frame
5528#ifdef _DEBUG
5529 BOOL GCOnTransition = FALSE;
5530 if (g_pConfig->FastGCStressLevel()) {
5531 GCOnTransition = GC_ON_TRANSITIONS (FALSE);
5532 }
5533#endif // _DEBUG
5534 CommonTripThread(); // Indicate we are at a GC safe point
5535#ifdef _DEBUG
5536 if (g_pConfig->FastGCStressLevel()) {
5537 GC_ON_TRANSITIONS (GCOnTransition);
5538 }
5539#endif // _DEBUG
5540 HELPER_METHOD_FRAME_END();
5541 }
5542}
5543HCIMPLEND
5544
5545/*************************************************************/
5546// For an inlined N/Direct call (and possibly for other places that need this service)
5547// we have noticed that the returning thread should trap for one reason or another.
5548// ECall sets up the frame.
5549
5550#if defined(_TARGET_ARM_) || defined(_TARGET_AMD64_)
5551// The JIT expects this helper to preserve the return value on AMD64 and ARM. We should eventually
5552// switch other platforms to the same convention since it produces smaller code.
5553extern "C" FCDECL0(VOID, JIT_RareDisableHelper);
5554extern "C" FCDECL0(VOID, JIT_RareDisableHelperWorker);
5555
5556HCIMPL0(void, JIT_RareDisableHelperWorker)
5557#else
5558HCIMPL0(void, JIT_RareDisableHelper)
5559#endif
5560{
5561 // We do this here (before we set up a frame), because the following scenario
5562 // We are in the process of doing an inlined pinvoke. Since we are in preemtive
5563 // mode, the thread is allowed to continue. The thread continues and gets a context
5564 // switch just after it has cleared the preemptive mode bit but before it gets
5565 // to this helper. When we do our stack crawl now, we think this thread is
5566 // in cooperative mode (and believed that it was suspended in the SuspendEE), so
5567 // we do a getthreadcontext (on the unsuspended thread!) and get an EIP in jitted code.
5568 // and proceed. Assume the crawl of jitted frames is proceeding on the other thread
5569 // when this thread wakes up and sets up a frame. Eventually the other thread
5570 // runs out of jitted frames and sees the frame we just established. This causes
5571 // an assert in the stack crawling code. If this assert is ignored, however, we
5572 // will end up scanning the jitted frames twice, which will lead to GC holes
5573 //
5574 // <TODO>TODO: It would be MUCH more robust if we should remember which threads
5575 // we suspended in the SuspendEE, and only even consider using EIP if it was suspended
5576 // in the first phase.
5577 // </TODO>
5578
5579 BEGIN_PRESERVE_LAST_ERROR;
5580
5581 FCALL_CONTRACT;
5582
5583 Thread *thread = GetThread();
5584
5585 // We need to disable the implicit FORBID GC region that exists inside an FCALL
5586 // in order to call RareDisablePreemptiveGC().
5587 FC_CAN_TRIGGER_GC();
5588 thread->RareDisablePreemptiveGC();
5589 FC_CAN_TRIGGER_GC_END();
5590
5591 FC_GC_POLL_NOT_NEEDED();
5592
5593 HELPER_METHOD_FRAME_BEGIN_NOPOLL(); // Set up a frame
5594 thread->HandleThreadAbort();
5595 HELPER_METHOD_FRAME_END();
5596
5597 END_PRESERVE_LAST_ERROR;
5598}
5599HCIMPLEND
5600
5601/*********************************************************************/
5602// This is called by the JIT after every instruction in fully interuptable
5603// code to make certain our GC tracking is OK
5604HCIMPL0(VOID, JIT_StressGC_NOP)
5605{
5606 FCALL_CONTRACT;
5607}
5608HCIMPLEND
5609
5610
5611HCIMPL0(VOID, JIT_StressGC)
5612{
5613 FCALL_CONTRACT;
5614
5615#ifdef _DEBUG
5616 HELPER_METHOD_FRAME_BEGIN_0(); // Set up a frame
5617
5618 bool fSkipGC = false;
5619
5620 if (!fSkipGC)
5621 GCHeapUtilities::GetGCHeap()->GarbageCollect();
5622
5623// <TODO>@TODO: the following ifdef is in error, but if corrected the
5624// compiler complains about the *__ms->pRetAddr() saying machine state
5625// doesn't allow -></TODO>
5626#ifdef _X86
5627 // Get the machine state, (from HELPER_METHOD_FRAME_BEGIN)
5628 // and wack our return address to a nop function
5629 BYTE* retInstrs = ((BYTE*) *__ms->pRetAddr()) - 4;
5630 _ASSERTE(retInstrs[-1] == 0xE8); // it is a call instruction
5631 // Wack it to point to the JITStressGCNop instead
5632 FastInterlockExchange((LONG*) retInstrs), (LONG) JIT_StressGC_NOP);
5633#endif // _X86
5634
5635 HELPER_METHOD_FRAME_END();
5636#endif // _DEBUG
5637}
5638HCIMPLEND
5639
5640
5641
5642HCIMPL0(INT32, JIT_GetCurrentManagedThreadId)
5643{
5644 FCALL_CONTRACT;
5645
5646 FC_GC_POLL_NOT_NEEDED();
5647
5648 Thread * pThread = GetThread();
5649 return pThread->GetThreadId();
5650}
5651HCIMPLEND
5652
5653
5654/*********************************************************************/
5655/* we don't use HCIMPL macros because we don't want the overhead even in debug mode */
5656
5657HCIMPL1_RAW(Object*, JIT_CheckObj, Object* obj)
5658{
5659 FCALL_CONTRACT;
5660
5661 if (obj != 0) {
5662 MethodTable* pMT = obj->GetMethodTable();
5663 if (!pMT->ValidateWithPossibleAV()) {
5664 _ASSERTE(!"Bad Method Table");
5665 FreeBuildDebugBreak();
5666 }
5667 }
5668 return obj;
5669}
5670HCIMPLEND_RAW
5671
5672static int loopChoice = 0;
5673
5674// This function supports a JIT mode in which we're debugging the mechanism for loop cloning.
5675// We want to clone loops, then make a semi-random choice, on each execution of the loop,
5676// whether to run the original loop or the cloned copy. We do this by incrementing the contents
5677// of a memory location, and testing whether the result is odd or even. The "loopChoice" variable
5678// above provides that memory location, and this JIT helper merely informs the JIT of the address of
5679// "loopChoice".
5680HCIMPL0(void*, JIT_LoopCloneChoiceAddr)
5681{
5682 CONTRACTL {
5683 FCALL_CHECK;
5684 } CONTRACTL_END;
5685
5686 return &loopChoice;
5687}
5688HCIMPLEND
5689
5690// Prints a message that loop cloning optimization has occurred.
5691HCIMPL0(void, JIT_DebugLogLoopCloning)
5692{
5693 CONTRACTL {
5694 FCALL_CHECK;
5695 } CONTRACTL_END;
5696
5697#ifdef _DEBUG
5698 printf(">> Logging loop cloning optimization\n");
5699#endif
5700}
5701HCIMPLEND
5702
5703//========================================================================
5704//
5705// INTEROP HELPERS
5706//
5707//========================================================================
5708
5709#ifdef _WIN64
5710
5711/**********************************************************************/
5712/* Fills out portions of an InlinedCallFrame for JIT64 */
5713/* The idea here is to allocate and initalize the frame to only once, */
5714/* regardless of how many PInvokes there are in the method */
5715Thread * __stdcall JIT_InitPInvokeFrame(InlinedCallFrame *pFrame, PTR_VOID StubSecretArg)
5716{
5717 CONTRACTL
5718 {
5719 SO_TOLERANT;
5720 NOTHROW;
5721 GC_TRIGGERS;
5722 } CONTRACTL_END;
5723
5724 Thread *pThread = GetThread();
5725
5726 // The JIT messed up and is initializing a frame that is already live on the stack?!?!?!?!
5727 _ASSERTE(pFrame != pThread->GetFrame());
5728
5729 pFrame->Init();
5730 pFrame->m_StubSecretArg = StubSecretArg;
5731 pFrame->m_Next = pThread->GetFrame();
5732
5733 return pThread;
5734}
5735
5736#endif // _WIN64
5737
5738//========================================================================
5739//
5740// JIT HELPERS IMPLEMENTED AS FCALLS
5741//
5742//========================================================================
5743
5744FCIMPL3(void, JitHelpers::UnsafeSetArrayElement, PtrArray* pPtrArrayUNSAFE, INT32 index, Object* objectUNSAFE) {
5745 FCALL_CONTRACT;
5746
5747 PTRARRAYREF pPtrArray = (PTRARRAYREF)pPtrArrayUNSAFE;
5748 OBJECTREF object = (OBJECTREF)objectUNSAFE;
5749
5750 _ASSERTE(index < (INT32)pPtrArray->GetNumComponents());
5751
5752 pPtrArray->SetAt(index, object);
5753}
5754FCIMPLEND
5755
5756#ifdef _TARGET_ARM_
5757// This function is used from the FCallMemcpy for GC polling
5758EXTERN_C VOID FCallMemCpy_GCPoll()
5759{
5760 FC_INNER_PROLOG(FCallMemcpy);
5761
5762 Thread *thread = GetThread();
5763 // CommonTripThread does this check, but doing this to avoid raising the frames
5764 if (thread->CatchAtSafePointOpportunistic())
5765 {
5766 HELPER_METHOD_FRAME_BEGIN_0();
5767 CommonTripThread();
5768 HELPER_METHOD_FRAME_END();
5769 }
5770
5771 FC_INNER_EPILOG();
5772}
5773#endif // _TARGET_ARM_
5774
5775//========================================================================
5776//
5777// JIT HELPERS INITIALIZATION
5778//
5779//========================================================================
5780
5781// verify consistency of jithelpers.h and corinfo.h
5782enum __CorInfoHelpFunc {
5783#define JITHELPER(code, pfnHelper, sig) __##code,
5784#include "jithelpers.h"
5785};
5786#define JITHELPER(code, pfnHelper, sig) C_ASSERT((int)__##code == (int)code);
5787#include "jithelpers.h"
5788
5789#ifdef _DEBUG
5790#define HELPERDEF(code, lpv, sig) { (LPVOID)(lpv), #code },
5791#else // !_DEBUG
5792#define HELPERDEF(code, lpv, sig) { (LPVOID)(lpv) },
5793#endif // !_DEBUG
5794
5795// static helpers - constant array
5796const VMHELPDEF hlpFuncTable[CORINFO_HELP_COUNT] =
5797{
5798#define JITHELPER(code, pfnHelper, sig) HELPERDEF(code, pfnHelper,sig)
5799#define DYNAMICJITHELPER(code, pfnHelper,sig) HELPERDEF(code, 1 + DYNAMIC_##code, sig)
5800#include "jithelpers.h"
5801};
5802
5803// dynamic helpers - filled in at runtime
5804VMHELPDEF hlpDynamicFuncTable[DYNAMIC_CORINFO_HELP_COUNT] =
5805{
5806#define JITHELPER(code, pfnHelper, sig)
5807#define DYNAMICJITHELPER(code, pfnHelper, sig) HELPERDEF(DYNAMIC_ ## code, pfnHelper, sig)
5808#include "jithelpers.h"
5809};
5810
5811#if defined(_DEBUG) && (defined(_TARGET_AMD64_) || defined(_TARGET_X86_)) && !defined(FEATURE_PAL)
5812#define HELPERCOUNTDEF(lpv) { (LPVOID)(lpv), NULL, 0 },
5813
5814VMHELPCOUNTDEF hlpFuncCountTable[CORINFO_HELP_COUNT+1] =
5815{
5816#define JITHELPER(code, pfnHelper, sig) HELPERCOUNTDEF(pfnHelper)
5817#define DYNAMICJITHELPER(code, pfnHelper, sig) HELPERCOUNTDEF(1 + DYNAMIC_##code)
5818#include "jithelpers.h"
5819};
5820#endif
5821
5822// Set the JIT helper function in the helper table
5823// Handles the case where the function does not reside in mscorwks.dll
5824
5825void _SetJitHelperFunction(DynamicCorInfoHelpFunc ftnNum, void * pFunc)
5826{
5827 CONTRACTL {
5828 NOTHROW;
5829 GC_NOTRIGGER;
5830 } CONTRACTL_END;
5831
5832 _ASSERTE(ftnNum < DYNAMIC_CORINFO_HELP_COUNT);
5833
5834 LOG((LF_JIT, LL_INFO1000000, "Setting JIT dynamic helper %3d (%s) to %p\n",
5835 ftnNum, hlpDynamicFuncTable[ftnNum].name, pFunc));
5836
5837 hlpDynamicFuncTable[ftnNum].pfnHelper = (void *) pFunc;
5838}
5839
5840/*********************************************************************/
5841// Initialize the part of the JIT helpers that require much of the
5842// EE infrastructure to be in place.
5843/*********************************************************************/
5844void InitJITHelpers2()
5845{
5846 STANDARD_VM_CONTRACT;
5847
5848#if defined(_TARGET_X86_) || defined(_TARGET_ARM_)
5849 SetJitHelperFunction(CORINFO_HELP_INIT_PINVOKE_FRAME, (void *)GenerateInitPInvokeFrameHelper()->GetEntryPoint());
5850#endif // _TARGET_X86_ || _TARGET_ARM_
5851
5852 ECall::DynamicallyAssignFCallImpl(GetEEFuncEntryPoint(GetThread), ECall::InternalGetCurrentThread);
5853
5854 InitJitHelperLogging();
5855
5856 g_pJitGenericHandleCacheCrst.Init(CrstJitGenericHandleCache, CRST_UNSAFE_COOPGC);
5857
5858 // Allocate and initialize the table
5859 NewHolder <JitGenericHandleCache> tempGenericHandleCache (new JitGenericHandleCache());
5860 LockOwner sLock = {&g_pJitGenericHandleCacheCrst, IsOwnerOfCrst};
5861 if (!tempGenericHandleCache->Init(59, &sLock))
5862 COMPlusThrowOM();
5863 g_pJitGenericHandleCache = tempGenericHandleCache.Extract();
5864}
5865
5866#if defined(_TARGET_AMD64_) || defined(_TARGET_ARM_)
5867
5868NOINLINE void DoCopy(CONTEXT * ctx, void * pvTempStack, size_t cbTempStack, Thread * pThread, Frame * pNewFrame)
5869{
5870 // We need to ensure that copying pvTempStack onto our stack will not in
5871 // *ANY* way trash the context record (or our pointer to it) that we need
5872 // in order to restore context
5873 _ASSERTE((DWORD_PTR)&ctx + sizeof(ctx) < (DWORD_PTR)GetSP(ctx));
5874
5875 CONTEXT ctx2;
5876 if ((DWORD_PTR)ctx + sizeof(*ctx) > (DWORD_PTR)GetSP(ctx))
5877 {
5878 // The context record is in danger, copy it down
5879 _ASSERTE((DWORD_PTR)&ctx2 + sizeof(ctx2) < (DWORD_PTR)GetSP(ctx));
5880 ctx2 = *ctx;
5881
5882 // Clear any context that we didn't copy...
5883 ctx2.ContextFlags &= CONTEXT_ALL;
5884 ctx = &ctx2;
5885 }
5886
5887 _ASSERTE((DWORD_PTR)ctx + sizeof(*ctx) <= (DWORD_PTR)GetSP(ctx));
5888
5889 // DevDiv 189140 - use memmove because source and dest might overlap.
5890 memmove((void*)GetSP(ctx), pvTempStack, cbTempStack);
5891
5892 if (pNewFrame != NULL)
5893 {
5894 // Now that the memmove above is complete, pNewFrame is actually pointing at a
5895 // TailCallFrame, and not garbage. So it's safe to add pNewFrame to the Frame
5896 // chain.
5897 _ASSERTE(pThread != NULL);
5898 pThread->SetFrame(pNewFrame);
5899 }
5900
5901 RtlRestoreContext(ctx, NULL);
5902}
5903
5904//
5905// Mostly Architecture-agnostic RtlVirtualUnwind-based tail call helper...
5906//
5907// Can't use HCIMPL macro because it requires unwind, and this method *NEVER* unwinds.
5908//
5909
5910#define INVOKE_COPY_ARGS_HELPER(helperFunc, arg1, arg2, arg3, arg4) ((pfnCopyArgs)helperFunc)(arg1, arg2, arg3, arg4)
5911void F_CALL_VA_CONV JIT_TailCall(PCODE copyArgs, PCODE target, ...)
5912{
5913 // Can't have a regular contract because we would never pop it
5914 // We only throw a stack overflow if needed, and we can't handle
5915 // a GC because the incoming parameters are totally unprotected.
5916 STATIC_CONTRACT_SO_TOLERANT;
5917 STATIC_CONTRACT_THROWS;
5918 STATIC_CONTRACT_GC_NOTRIGGER;
5919 STATIC_CONTRACT_MODE_COOPERATIVE
5920
5921#ifndef FEATURE_PAL
5922
5923 Thread *pThread = GetThread();
5924
5925#ifdef FEATURE_HIJACK
5926 // We can't crawl the stack of a thread that currently has a hijack pending
5927 // (since the hijack routine won't be recognized by any code manager). So we
5928 // undo any hijack, the EE will re-attempt it later.
5929 pThread->UnhijackThread();
5930#endif
5931
5932 ULONG_PTR establisherFrame = 0;
5933 PVOID handlerData = NULL;
5934 CONTEXT ctx;
5935
5936 // Unwind back to our caller in managed code
5937 static PT_RUNTIME_FUNCTION my_pdata;
5938 static ULONG_PTR my_imagebase;
5939
5940 ctx.ContextFlags = CONTEXT_ALL;
5941 RtlCaptureContext(&ctx);
5942
5943 if (!VolatileLoadWithoutBarrier(&my_imagebase)) {
5944 ULONG_PTR imagebase = 0;
5945 my_pdata = RtlLookupFunctionEntry(GetIP(&ctx), &imagebase, NULL);
5946 InterlockedExchangeT(&my_imagebase, imagebase);
5947 }
5948
5949 RtlVirtualUnwind(UNW_FLAG_NHANDLER, my_imagebase, GetIP(&ctx), my_pdata, &ctx, &handlerData,
5950 &establisherFrame, NULL);
5951
5952 EECodeInfo codeInfo(GetIP(&ctx));
5953
5954 // Now unwind back to our caller's caller
5955 establisherFrame = 0;
5956 RtlVirtualUnwind(UNW_FLAG_NHANDLER, codeInfo.GetModuleBase(), GetIP(&ctx), codeInfo.GetFunctionEntry(), &ctx, &handlerData,
5957 &establisherFrame, NULL);
5958
5959 va_list args;
5960
5961 // Compute the space needed for arguments
5962 va_start(args, target);
5963
5964 ULONG_PTR pGCLayout = 0;
5965 size_t cbArgArea = INVOKE_COPY_ARGS_HELPER(copyArgs, args, NULL, NULL, (size_t)&pGCLayout);
5966
5967 va_end(args);
5968
5969 // reset (in case the helper walked them)
5970 va_start(args, target);
5971
5972 // Fake call frame (if needed)
5973 size_t cbCopyFrame = 0;
5974 bool fCopyDown = false;
5975 BYTE rgFrameBuffer[sizeof(FrameWithCookie<TailCallFrame>)];
5976 Frame * pNewFrame = NULL;
5977
5978#if defined(_TARGET_AMD64_)
5979# define STACK_ADJUST_FOR_RETURN_ADDRESS (sizeof(void*))
5980# define STACK_ALIGN_MASK (0xF)
5981#elif defined(_TARGET_ARM_)
5982# define STACK_ADJUST_FOR_RETURN_ADDRESS (0)
5983# define STACK_ALIGN_MASK (0x7)
5984#else
5985#error "Unknown tail call architecture"
5986#endif
5987
5988 // figure out if we can re-use an existing TailCallHelperStub
5989 // or if we need to create a new one.
5990 if ((void*)GetIP(&ctx) == JIT_TailCallHelperStub_ReturnAddress) {
5991 TailCallFrame * pCurrentFrame = TailCallFrame::GetFrameFromContext(&ctx);
5992 _ASSERTE(pThread->GetFrame() == pCurrentFrame);
5993 // The caller was tail called, so we can re-use that frame
5994 // See if we need to enlarge the ArgArea
5995 // This can potentially enlarge cbArgArea to the size of the
5996 // existing TailCallFrame.
5997 const size_t endOfFrame = (size_t)pCurrentFrame - (size_t)sizeof(GSCookie);
5998 size_t cbOldArgArea = (endOfFrame - GetSP(&ctx));
5999 if (cbOldArgArea >= cbArgArea) {
6000 cbArgArea = cbOldArgArea;
6001 }
6002 else {
6003 SetSP(&ctx, (endOfFrame - cbArgArea));
6004 fCopyDown = true;
6005 }
6006
6007 // Reset the GCLayout
6008 pCurrentFrame->SetGCLayout((TADDR)pGCLayout);
6009
6010 // We're jumping to the new method, not calling it
6011 // so make room for the return address that the 'call'
6012 // would have pushed.
6013 SetSP(&ctx, GetSP(&ctx) - STACK_ADJUST_FOR_RETURN_ADDRESS);
6014 }
6015 else {
6016 // Create a fake fixed frame as if the new method was called by
6017 // TailCallHelperStub asm stub and did an
6018 // alloca, then called the target method.
6019 cbCopyFrame = sizeof(rgFrameBuffer);
6020 FrameWithCookie<TailCallFrame> * CookieFrame = new (rgFrameBuffer) FrameWithCookie<TailCallFrame>(&ctx, pThread);
6021 TailCallFrame * tailCallFrame = &*CookieFrame;
6022
6023 tailCallFrame->SetGCLayout((TADDR)pGCLayout);
6024 pNewFrame = TailCallFrame::AdjustContextForTailCallHelperStub(&ctx, cbArgArea, pThread);
6025 fCopyDown = true;
6026
6027 // Eventually, we'll add pNewFrame to our frame chain, but don't do it yet. It's
6028 // pointing to the place on the stack where the TailCallFrame contents WILL be,
6029 // but aren't there yet. In order to keep the stack walkable by profilers, wait
6030 // until the contents are moved over properly (inside DoCopy), and then add
6031 // pNewFrame onto the frame chain.
6032 }
6033
6034 // The stack should be properly aligned, modulo the pushed return
6035 // address (at least on x64)
6036 _ASSERTE((GetSP(&ctx) & STACK_ALIGN_MASK) == STACK_ADJUST_FOR_RETURN_ADDRESS);
6037
6038 // Set the target pointer so we land there when we restore the context
6039 SetIP(&ctx, (PCODE)target);
6040
6041 // Begin creating the new stack frame and copying arguments
6042 size_t cbTempStack = cbCopyFrame + cbArgArea + STACK_ADJUST_FOR_RETURN_ADDRESS;
6043
6044 // If we're going to have to overwrite some of our incoming argument slots
6045 // then do a double-copy, first to temporary copy below us on the stack and
6046 // then back up to the real stack.
6047 void * pvTempStack;
6048 if (!fCopyDown && (((ULONG_PTR)args + cbArgArea) < GetSP(&ctx))) {
6049
6050 //
6051 // After this our stack may no longer be walkable by the debugger!!!
6052 //
6053
6054 pvTempStack = (void*)GetSP(&ctx);
6055 }
6056 else {
6057 fCopyDown = true;
6058
6059 // Need to align properly for a return address (if it goes on the stack)
6060 //
6061 // AMD64 ONLY:
6062 // _alloca produces 16-byte aligned buffers, but the return address,
6063 // where our buffer 'starts' is off by 8, so make sure our buffer is
6064 // off by 8.
6065 //
6066 pvTempStack = (BYTE*)_alloca(cbTempStack + STACK_ADJUST_FOR_RETURN_ADDRESS) + STACK_ADJUST_FOR_RETURN_ADDRESS;
6067 }
6068
6069 _ASSERTE(((size_t)pvTempStack & STACK_ALIGN_MASK) == STACK_ADJUST_FOR_RETURN_ADDRESS);
6070
6071 // Start creating the new stack (bottom up)
6072 BYTE * pbTempStackFill = (BYTE*)pvTempStack;
6073 // Return address
6074 if (STACK_ADJUST_FOR_RETURN_ADDRESS > 0) {
6075 *((PVOID*)pbTempStackFill) = (PVOID)JIT_TailCallHelperStub_ReturnAddress; // return address
6076 pbTempStackFill += STACK_ADJUST_FOR_RETURN_ADDRESS;
6077 }
6078
6079 // arguments
6080 INVOKE_COPY_ARGS_HELPER(copyArgs, args, &ctx, (DWORD_PTR*)pbTempStackFill, cbArgArea);
6081
6082 va_end(args);
6083
6084 pbTempStackFill += cbArgArea;
6085
6086 // frame (includes TailCallFrame)
6087 if (cbCopyFrame > 0) {
6088 _ASSERTE(cbCopyFrame == sizeof(rgFrameBuffer));
6089 memcpy(pbTempStackFill, rgFrameBuffer, cbCopyFrame);
6090 pbTempStackFill += cbCopyFrame;
6091 }
6092
6093 // If this fires, check the math above, because we copied more than we should have
6094 _ASSERTE((size_t)((pbTempStackFill - (BYTE*)pvTempStack)) == cbTempStack);
6095
6096 // If this fires, it means we messed up the math and we're about to overwrite
6097 // some of our locals which would be bad because we still need them to call
6098 // RtlRestoreContext and pop the contract...
6099 _ASSERTE(fCopyDown || ((DWORD_PTR)&ctx + sizeof(ctx) < (DWORD_PTR)GetSP(&ctx)));
6100
6101 if (fCopyDown) {
6102 // We've created a dummy stack below our frame and now we overwrite
6103 // our own real stack.
6104
6105 //
6106 // After this our stack may no longer be walkable by the debugger!!!
6107 //
6108
6109 // This does the copy, adds pNewFrame to the frame chain, and calls RtlRestoreContext
6110 DoCopy(&ctx, pvTempStack, cbTempStack, pThread, pNewFrame);
6111 }
6112
6113 RtlRestoreContext(&ctx, NULL);
6114
6115#undef STACK_ADJUST_FOR_RETURN_ADDRESS
6116#undef STACK_ALIGN_MASK
6117
6118#else // !FEATURE_PAL
6119 PORTABILITY_ASSERT("TODO: Implement JIT_TailCall for PAL");
6120#endif // !FEATURE_PAL
6121
6122}
6123
6124#endif // _TARGET_AMD64_ || _TARGET_ARM_
6125
6126//========================================================================
6127//
6128// JIT HELPERS LOGGING
6129//
6130//========================================================================
6131
6132#if defined(_DEBUG) && (defined(_TARGET_AMD64_) || defined(_TARGET_X86_)) && !defined(FEATURE_PAL)
6133// *****************************************************************************
6134// JitHelperLogging usage:
6135// 1) Ngen using:
6136// COMPlus_HardPrejitEnabled=0
6137//
6138// This allows us to instrument even ngen'd image calls to JIT helpers.
6139// Remember to clear the key after ngen-ing and before actually running
6140// the app you want to log.
6141//
6142// 2) Then set:
6143// COMPlus_JitHelperLogging=1
6144// COMPlus_LogEnable=1
6145// COMPlus_LogLevel=1
6146// COMPlus_LogToFile=1
6147//
6148// 3) Run the app that you want to log; Results will be in COMPLUS.LOG(.X)
6149//
6150// 4) JitHelperLogging=2 and JitHelperLogging=3 result in different output
6151// as per code in WriteJitHelperCountToSTRESSLOG() below.
6152// *****************************************************************************
6153void WriteJitHelperCountToSTRESSLOG()
6154{
6155 CONTRACTL
6156 {
6157 THROWS;
6158 GC_NOTRIGGER;
6159 SO_TOLERANT;
6160 MODE_ANY;
6161 }
6162 CONTRACTL_END;
6163 int jitHelperLoggingLevel = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_JitHelperLogging);
6164 if (jitHelperLoggingLevel != 0)
6165 {
6166 DWORD logFacility, logLevel;
6167
6168 logFacility = LF_ALL; //LF_ALL/LL_ALWAYS is okay here only because this logging would normally
6169 logLevel = LL_ALWAYS; // would never be turned on at all (used only for performance measurements)
6170
6171 const int countPos = 60;
6172
6173 STRESS_LOG0(logFacility, logLevel, "Writing Jit Helper COUNT table to log\n");
6174
6175 VMHELPCOUNTDEF* hlpFuncCount = hlpFuncCountTable;
6176 while(hlpFuncCount < (hlpFuncCountTable + CORINFO_HELP_COUNT))
6177 {
6178 const char* name;
6179 LONG count;
6180
6181 name = hlpFuncCount->helperName;
6182 count = hlpFuncCount->count;
6183
6184 int nameLen = 0;
6185 switch (jitHelperLoggingLevel)
6186 {
6187 case 1:
6188 // This will print a comma seperated list:
6189 // CORINFO_XXX_HELPER, 10
6190 // CORINFO_YYYY_HELPER, 11
6191 STRESS_LOG2(logFacility, logLevel, "%s, %d\n", name, count);
6192 break;
6193
6194 case 2:
6195 // This will print a table like:
6196 // CORINFO_XXX_HELPER 10
6197 // CORINFO_YYYY_HELPER 11
6198 if (hlpFuncCount->helperName != NULL)
6199 nameLen = (int)strlen(name);
6200 else
6201 nameLen = (int)strlen("(null)");
6202
6203 if (nameLen < countPos)
6204 {
6205 char* buffer = new char[(countPos - nameLen) + 1];
6206 memset(buffer, (int)' ', (countPos-nameLen));
6207 buffer[(countPos - nameLen)] = '\0';
6208 STRESS_LOG3(logFacility, logLevel, "%s%s %d\n", name, buffer, count);
6209 }
6210 else
6211 {
6212 STRESS_LOG2(logFacility, logLevel, "%s %d\n", name, count);
6213 }
6214 break;
6215
6216 case 3:
6217 // This will print out the counts and the address range of the helper (if we know it)
6218 // CORINFO_XXX_HELPER, 10, (0x12345678 -> 0x12345778)
6219 // CORINFO_YYYY_HELPER, 11, (0x00011234 -> 0x00012234)
6220 STRESS_LOG4(logFacility, logLevel, "%s, %d, (0x%p -> 0x%p)\n", name, count, hlpFuncCount->pfnRealHelper, ((LPBYTE)hlpFuncCount->pfnRealHelper + hlpFuncCount->helperSize));
6221 break;
6222
6223 default:
6224 STRESS_LOG1(logFacility, logLevel, "Unsupported JitHelperLogging mode (%d)\n", jitHelperLoggingLevel);
6225 break;
6226 }
6227
6228 hlpFuncCount++;
6229 }
6230 }
6231}
6232// This will do the work to instrument the JIT helper table.
6233void InitJitHelperLogging()
6234{
6235 STANDARD_VM_CONTRACT;
6236
6237 if ((CLRConfig::GetConfigValue(CLRConfig::INTERNAL_JitHelperLogging) != 0))
6238 {
6239
6240#ifdef _TARGET_X86_
6241 IMAGE_DOS_HEADER *pDOS = (IMAGE_DOS_HEADER *)g_pMSCorEE;
6242 _ASSERTE(pDOS->e_magic == VAL16(IMAGE_DOS_SIGNATURE) && pDOS->e_lfanew != 0);
6243
6244 IMAGE_NT_HEADERS *pNT = (IMAGE_NT_HEADERS*)((LPBYTE)g_pMSCorEE + VAL32(pDOS->e_lfanew));
6245#ifdef _WIN64
6246 _ASSERTE(pNT->Signature == VAL32(IMAGE_NT_SIGNATURE)
6247 && pNT->FileHeader.SizeOfOptionalHeader == VAL16(sizeof(IMAGE_OPTIONAL_HEADER64))
6248 && pNT->OptionalHeader.Magic == VAL16(IMAGE_NT_OPTIONAL_HDR_MAGIC) );
6249#else
6250 _ASSERTE(pNT->Signature == VAL32(IMAGE_NT_SIGNATURE)
6251 && pNT->FileHeader.SizeOfOptionalHeader == VAL16(sizeof(IMAGE_OPTIONAL_HEADER32))
6252 && pNT->OptionalHeader.Magic == VAL16(IMAGE_NT_OPTIONAL_HDR_MAGIC) );
6253#endif
6254#endif // _TARGET_X86_
6255
6256 // Make the static hlpFuncTable read/write for purposes of writing the logging thunks
6257 DWORD dwOldProtect;
6258 if (!ClrVirtualProtect((LPVOID)hlpFuncTable, (sizeof(VMHELPDEF) * CORINFO_HELP_COUNT), PAGE_EXECUTE_READWRITE, &dwOldProtect))
6259 {
6260 ThrowLastError();
6261 }
6262
6263 LoaderHeap* pHeap = SystemDomain::GetGlobalLoaderAllocator()->GetStubHeap();
6264
6265 // iterate through the jit helper tables replacing helpers with logging thunks
6266 //
6267 // NOTE: if NGEN'd images were NGEN'd with hard binding on then static helper
6268 // calls will NOT be instrumented.
6269 VMHELPDEF* hlpFunc = const_cast<VMHELPDEF*>(hlpFuncTable);
6270 VMHELPCOUNTDEF* hlpFuncCount = hlpFuncCountTable;
6271 while(hlpFunc < (hlpFuncTable + CORINFO_HELP_COUNT))
6272 {
6273 if (hlpFunc->pfnHelper != NULL)
6274 {
6275 CPUSTUBLINKER sl;
6276 CPUSTUBLINKER* pSl = &sl;
6277
6278 if (((size_t)hlpFunc->pfnHelper - 1) > DYNAMIC_CORINFO_HELP_COUNT)
6279 {
6280 // While we're here initialize the table of VMHELPCOUNTDEF
6281 // guys with info about this helper
6282 hlpFuncCount->pfnRealHelper = hlpFunc->pfnHelper;
6283 hlpFuncCount->helperName = hlpFunc->name;
6284 hlpFuncCount->count = 0;
6285#ifdef _TARGET_AMD64_
6286 ULONGLONG uImageBase;
6287 PT_RUNTIME_FUNCTION pFunctionEntry;
6288 pFunctionEntry = RtlLookupFunctionEntry((ULONGLONG)hlpFunc->pfnHelper, &uImageBase, NULL);
6289
6290 if (pFunctionEntry != NULL)
6291 {
6292 _ASSERTE((uImageBase + pFunctionEntry->BeginAddress) == (ULONGLONG)hlpFunc->pfnHelper);
6293 hlpFuncCount->helperSize = pFunctionEntry->EndAddress - pFunctionEntry->BeginAddress;
6294 }
6295 else
6296 {
6297 hlpFuncCount->helperSize = 0;
6298 }
6299#else // _TARGET_X86_
6300 // How do I get this for x86?
6301 hlpFuncCount->helperSize = 0;
6302#endif // _TARGET_AMD64_
6303
6304 pSl->EmitJITHelperLoggingThunk(GetEEFuncEntryPoint(hlpFunc->pfnHelper), (LPVOID)hlpFuncCount);
6305 Stub* pStub = pSl->Link(pHeap);
6306 hlpFunc->pfnHelper = (void*)pStub->GetEntryPoint();
6307 }
6308 else
6309 {
6310 _ASSERTE(((size_t)hlpFunc->pfnHelper - 1) >= 0 &&
6311 ((size_t)hlpFunc->pfnHelper - 1) < COUNTOF(hlpDynamicFuncTable));
6312 VMHELPDEF* dynamicHlpFunc = &hlpDynamicFuncTable[((size_t)hlpFunc->pfnHelper - 1)];
6313
6314 // While we're here initialize the table of VMHELPCOUNTDEF
6315 // guys with info about this helper. There is only one table
6316 // for the count dudes that contains info about both dynamic
6317 // and static helpers.
6318
6319#ifdef _PREFAST_
6320#pragma warning(push)
6321#pragma warning(disable:26001) // "Bounds checked above"
6322#endif /*_PREFAST_ */
6323 hlpFuncCount->pfnRealHelper = dynamicHlpFunc->pfnHelper;
6324 hlpFuncCount->helperName = dynamicHlpFunc->name;
6325 hlpFuncCount->count = 0;
6326#ifdef _PREFAST_
6327#pragma warning(pop)
6328#endif /*_PREFAST_*/
6329
6330#ifdef _TARGET_AMD64_
6331 ULONGLONG uImageBase;
6332 PT_RUNTIME_FUNCTION pFunctionEntry;
6333 pFunctionEntry = RtlLookupFunctionEntry((ULONGLONG)hlpFunc->pfnHelper, &uImageBase, NULL);
6334
6335 if (pFunctionEntry != NULL)
6336 {
6337 _ASSERTE((uImageBase + pFunctionEntry->BeginAddress) == (ULONGLONG)hlpFunc->pfnHelper);
6338 hlpFuncCount->helperSize = pFunctionEntry->EndAddress - pFunctionEntry->BeginAddress;
6339 }
6340 else
6341 {
6342 // if we can't get a function entry for this we'll just pretend the size is 0
6343 hlpFuncCount->helperSize = 0;
6344 }
6345#else // _TARGET_X86_
6346 // Is the address in mscoree.dll at all? (All helpers are in
6347 // mscoree.dll)
6348 if (dynamicHlpFunc->pfnHelper >= (LPBYTE*)g_pMSCorEE && dynamicHlpFunc->pfnHelper < (LPBYTE*)g_pMSCorEE + VAL32(pNT->OptionalHeader.SizeOfImage))
6349 {
6350 // See note above. How do I get the size on x86 for a static method?
6351 hlpFuncCount->helperSize = 0;
6352 }
6353 else
6354 {
6355 Stub::RecoverStubAndSize((TADDR)dynamicHlpFunc->pfnHelper, (DWORD*)&hlpFuncCount->helperSize);
6356 hlpFuncCount->helperSize -= sizeof(Stub);
6357 }
6358
6359#endif // _TARGET_AMD64_
6360
6361 pSl->EmitJITHelperLoggingThunk(GetEEFuncEntryPoint(dynamicHlpFunc->pfnHelper), (LPVOID)hlpFuncCount);
6362 Stub* pStub = pSl->Link(pHeap);
6363 dynamicHlpFunc->pfnHelper = (void*)pStub->GetEntryPoint();
6364 }
6365 }
6366
6367 hlpFunc++;
6368 hlpFuncCount++;
6369 }
6370
6371 // Restore original access rights to the static hlpFuncTable
6372 ClrVirtualProtect((LPVOID)hlpFuncTable, (sizeof(VMHELPDEF) * CORINFO_HELP_COUNT), dwOldProtect, &dwOldProtect);
6373 }
6374
6375 return;
6376}
6377#endif // _DEBUG && (_TARGET_AMD64_ || _TARGET_X86_)
6378