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 | // |
112 | inline 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 | /*********************************************************************/ |
125 | HCIMPL2_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 | } |
137 | HCIMPLEND |
138 | #endif // !_TARGET_X86_ || FEATURE_PAL |
139 | |
140 | /*********************************************************************/ |
141 | HCIMPL2_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 | |
201 | ThrowExcep: |
202 | FCThrow(kOverflowException); |
203 | } |
204 | HCIMPLEND |
205 | |
206 | /*********************************************************************/ |
207 | HCIMPL2_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 | |
246 | ThrowExcep: |
247 | FCThrow(kOverflowException); |
248 | } |
249 | HCIMPLEND |
250 | |
251 | /*********************************************************************/ |
252 | HCIMPL2(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 | |
278 | ThrowExcep: |
279 | FCThrow(ehKind); |
280 | } |
281 | HCIMPLEND |
282 | |
283 | /*********************************************************************/ |
284 | HCIMPL2(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 | |
310 | ThrowExcep: |
311 | FCThrow(ehKind); |
312 | } |
313 | HCIMPLEND |
314 | |
315 | /*********************************************************************/ |
316 | HCIMPL2(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 | } |
325 | HCIMPLEND |
326 | |
327 | /*********************************************************************/ |
328 | HCIMPL2(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 | } |
337 | HCIMPLEND |
338 | |
339 | /*********************************************************************/ |
340 | HCIMPL2_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 | |
372 | ThrowExcep: |
373 | FCThrow(ehKind); |
374 | } |
375 | HCIMPLEND |
376 | |
377 | /*********************************************************************/ |
378 | HCIMPL2_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 | |
412 | ThrowExcep: |
413 | FCThrow(ehKind); |
414 | } |
415 | HCIMPLEND |
416 | |
417 | /*********************************************************************/ |
418 | HCIMPL2_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 | } |
433 | HCIMPLEND |
434 | |
435 | /*********************************************************************/ |
436 | HCIMPL2_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 | } |
451 | HCIMPLEND |
452 | |
453 | #if !defined(BIT64) && !defined(_TARGET_X86_) |
454 | /*********************************************************************/ |
455 | HCIMPL2_VV(UINT64, JIT_LLsh, UINT64 num, int shift) |
456 | { |
457 | FCALL_CONTRACT; |
458 | return num << (shift & 0x3F); |
459 | } |
460 | HCIMPLEND |
461 | |
462 | /*********************************************************************/ |
463 | HCIMPL2_VV(INT64, JIT_LRsh, INT64 num, int shift) |
464 | { |
465 | FCALL_CONTRACT; |
466 | return num >> (shift & 0x3F); |
467 | } |
468 | HCIMPLEND |
469 | |
470 | /*********************************************************************/ |
471 | HCIMPL2_VV(UINT64, JIT_LRsz, UINT64 num, int shift) |
472 | { |
473 | FCALL_CONTRACT; |
474 | return num >> (shift & 0x3F); |
475 | } |
476 | HCIMPLEND |
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 | // |
492 | HCIMPL1_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 | } |
502 | HCIMPLEND |
503 | |
504 | /*********************************************************************/ |
505 | // needed for ARM and RyuJIT-x86 |
506 | HCIMPL1_V(double, JIT_Lng2Dbl, INT64 val) |
507 | { |
508 | FCALL_CONTRACT; |
509 | return double(val); |
510 | } |
511 | HCIMPLEND |
512 | |
513 | //-------------------------------------------------------------------------- |
514 | template <class ftype> |
515 | ftype modftype(ftype value, ftype *iptr); |
516 | template <> float modftype(float value, float *iptr) { return modff(value, iptr); } |
517 | template <> double modftype(double value, double *iptr) { return modf(value, iptr); } |
518 | |
519 | // round to nearest, round to even if tied |
520 | template <class ftype> |
521 | ftype 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) |
556 | HCIMPL1_V(double, JIT_DoubleRound, double val) |
557 | { |
558 | FCALL_CONTRACT; |
559 | return BankersRound(val); |
560 | } |
561 | HCIMPLEND |
562 | |
563 | /*********************************************************************/ |
564 | // round float to nearest int (as float) |
565 | HCIMPL1_V(float, JIT_FloatRound, float val) |
566 | { |
567 | FCALL_CONTRACT; |
568 | return BankersRound(val); |
569 | } |
570 | HCIMPLEND |
571 | |
572 | /*********************************************************************/ |
573 | // Call fast Dbl2Lng conversion - used by functions below |
574 | FORCEINLINE 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 | /*********************************************************************/ |
586 | HCIMPL1_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 | } |
596 | HCIMPLEND |
597 | |
598 | /*********************************************************************/ |
599 | HCIMPL1_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 | } |
614 | HCIMPLEND |
615 | |
616 | /*********************************************************************/ |
617 | HCIMPL1_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 | } |
643 | HCIMPLEND |
644 | |
645 | |
646 | #if !defined(_TARGET_X86_) || defined(FEATURE_PAL) |
647 | |
648 | HCIMPL1_V(INT64, JIT_Dbl2Lng, double val) |
649 | { |
650 | FCALL_CONTRACT; |
651 | |
652 | return((INT64)val); |
653 | } |
654 | HCIMPLEND |
655 | |
656 | HCIMPL1_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 | } |
668 | HCIMPLEND |
669 | |
670 | HCIMPL1_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 | } |
683 | HCIMPLEND |
684 | |
685 | HCIMPL2_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 | } |
717 | HCIMPLEND |
718 | |
719 | HCIMPL2_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 | } |
745 | HCIMPLEND |
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 |
762 | HCIMPL2(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 | } |
784 | HCIMPLEND |
785 | |
786 | #include <optsmallperfcritical.h> |
787 | HCIMPL2(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 | } |
802 | HCIMPLEND |
803 | #include <optdefault.h> |
804 | |
805 | /*********************************************************************/ |
806 | #define HCallAssert(cache, target) // suppressed to avoid ambiguous cast errors caused by use of template |
807 | template <typename FIELDTYPE> |
808 | NOINLINE 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 | } |
829 | HCIMPLEND |
830 | |
831 | /*********************************************************************/ |
832 | #include <optsmallperfcritical.h> |
833 | |
834 | HCIMPL2(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 | } |
848 | HCIMPLEND |
849 | |
850 | HCIMPL2(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 | } |
864 | HCIMPLEND |
865 | |
866 | HCIMPL2(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 | } |
880 | HCIMPLEND |
881 | |
882 | HCIMPL2(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 | } |
896 | HCIMPLEND |
897 | |
898 | HCIMPL2(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 | } |
913 | HCIMPLEND |
914 | |
915 | HCIMPL2(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 | } |
930 | HCIMPLEND |
931 | |
932 | #include <optdefault.h> |
933 | |
934 | /*********************************************************************/ |
935 | #define HCallAssert(cache, target) // suppressed to avoid ambiguous cast errors caused by use of template |
936 | template <typename FIELDTYPE> |
937 | NOINLINE 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 | } |
954 | HCIMPLEND |
955 | |
956 | /*********************************************************************/ |
957 | #include <optsmallperfcritical.h> |
958 | |
959 | HCIMPL3(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 | } |
972 | HCIMPLEND |
973 | |
974 | HCIMPL3(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 | } |
987 | HCIMPLEND |
988 | |
989 | HCIMPL3(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 | } |
1002 | HCIMPLEND |
1003 | |
1004 | HCIMPL3(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 | } |
1017 | HCIMPLEND |
1018 | |
1019 | HCIMPL3(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 | } |
1032 | HCIMPLEND |
1033 | |
1034 | HCIMPL3(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 | } |
1047 | HCIMPLEND |
1048 | |
1049 | #include <optdefault.h> |
1050 | |
1051 | /*********************************************************************/ |
1052 | HCIMPL2(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 | } |
1072 | HCIMPLEND |
1073 | |
1074 | #include <optsmallperfcritical.h> |
1075 | HCIMPL2(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 | } |
1094 | HCIMPLEND |
1095 | #include <optdefault.h> |
1096 | |
1097 | /*********************************************************************/ |
1098 | HCIMPL3(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 | } |
1116 | HCIMPLEND |
1117 | |
1118 | #include <optsmallperfcritical.h> |
1119 | HCIMPL3(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 | } |
1137 | HCIMPLEND |
1138 | #include <optdefault.h> |
1139 | |
1140 | /*********************************************************************/ |
1141 | HCIMPL4(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 | } |
1176 | HCIMPLEND |
1177 | |
1178 | #include <optsmallperfcritical.h> |
1179 | HCIMPL4(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 | } |
1194 | HCIMPLEND |
1195 | #include <optdefault.h> |
1196 | |
1197 | /*********************************************************************/ |
1198 | HCIMPL4(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 | } |
1230 | HCIMPLEND |
1231 | |
1232 | #include <optsmallperfcritical.h> |
1233 | HCIMPL4(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 | } |
1248 | HCIMPLEND |
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 |
1262 | NOINLINE 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 | } |
1278 | HCIMPLEND |
1279 | |
1280 | |
1281 | /*************************************************************/ |
1282 | #include <optsmallperfcritical.h> |
1283 | HCIMPL1(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 | } |
1298 | HCIMPLEND |
1299 | #include <optdefault.h> |
1300 | |
1301 | /*************************************************************/ |
1302 | HCIMPL2(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 | } |
1331 | HCIMPLEND |
1332 | |
1333 | |
1334 | //======================================================================== |
1335 | // |
1336 | // SHARED STATIC FIELD HELPERS |
1337 | // |
1338 | //======================================================================== |
1339 | |
1340 | #include <optsmallperfcritical.h> |
1341 | |
1342 | HCIMPL2(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 | } |
1367 | HCIMPLEND |
1368 | |
1369 | // No constructor version of JIT_GetSharedNonGCStaticBase. Does not check if class has |
1370 | // been initialized. |
1371 | HCIMPL1(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 | } |
1387 | HCIMPLEND |
1388 | |
1389 | HCIMPL2(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 | } |
1414 | HCIMPLEND |
1415 | |
1416 | // No constructor version of JIT_GetSharedGCStaticBase. Does not check if class has been |
1417 | // initialized. |
1418 | HCIMPL1(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 | } |
1434 | HCIMPLEND |
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 |
1441 | HCIMPL2(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 | } |
1456 | HCIMPLEND |
1457 | |
1458 | HCIMPL2(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 | } |
1473 | HCIMPLEND |
1474 | |
1475 | /*********************************************************************/ |
1476 | // Slow helper to tail call from the fast one |
1477 | HCIMPL2(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 | } |
1495 | HCIMPLEND |
1496 | |
1497 | /*************************************************************/ |
1498 | #include <optsmallperfcritical.h> |
1499 | HCIMPL2(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 | } |
1528 | HCIMPLEND |
1529 | #include <optdefault.h> |
1530 | |
1531 | /*************************************************************/ |
1532 | // Slow helper to tail call from the fast one |
1533 | HCIMPL2(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 | } |
1548 | HCIMPLEND |
1549 | |
1550 | #include <optsmallperfcritical.h> |
1551 | HCIMPL2(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 | } |
1575 | HCIMPLEND |
1576 | #include <optdefault.h> |
1577 | |
1578 | /*************************************************************/ |
1579 | // Slow helper to tail call from the fast one |
1580 | HCIMPL2(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 | } |
1598 | HCIMPLEND |
1599 | |
1600 | /*************************************************************/ |
1601 | #include <optsmallperfcritical.h> |
1602 | HCIMPL2(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 | } |
1631 | HCIMPLEND |
1632 | #include <optdefault.h> |
1633 | |
1634 | /*********************************************************************/ |
1635 | // Slow helper to tail call from the fast one |
1636 | NOINLINE 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 | } |
1659 | HCIMPLEND |
1660 | |
1661 | /*********************************************************************/ |
1662 | #include <optsmallperfcritical.h> |
1663 | HCIMPL1(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 | } |
1692 | HCIMPLEND |
1693 | #include <optdefault.h> |
1694 | |
1695 | /*********************************************************************/ |
1696 | // Slow helper to tail call from the fast one |
1697 | NOINLINE 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 | } |
1726 | HCIMPLEND |
1727 | |
1728 | /*********************************************************************/ |
1729 | #include <optsmallperfcritical.h> |
1730 | HCIMPL1(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 | } |
1764 | HCIMPLEND |
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 | |
1778 | HCIMPL1(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 | } |
1808 | HCIMPLEND |
1809 | |
1810 | HCIMPL1(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 | } |
1840 | HCIMPLEND |
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> |
1850 | HCIMPL2(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 | } |
1884 | HCIMPLEND |
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> |
1894 | HCIMPL2(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 | } |
1928 | HCIMPLEND |
1929 | #include <optdefault.h> |
1930 | |
1931 | // *** This helper corresponds to CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE_DYNAMICCLASS |
1932 | |
1933 | #include <optsmallperfcritical.h> |
1934 | HCIMPL2(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 | } |
1982 | HCIMPLEND |
1983 | #include <optdefault.h> |
1984 | |
1985 | // *** This helper corresponds to CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE_DYNAMICCLASS |
1986 | |
1987 | #include <optsmallperfcritical.h> |
1988 | HCIMPL2(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 | } |
2035 | HCIMPLEND |
2036 | #include <optdefault.h> |
2037 | |
2038 | // *** This helper corresponds to CORINFO_HELP_GETGENERICS_NONGCTHREADSTATIC_BASE |
2039 | |
2040 | #include <optsmallperfcritical.h> |
2041 | HCIMPL1(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 | } |
2087 | HCIMPLEND |
2088 | #include <optdefault.h> |
2089 | |
2090 | // *** This helper corresponds to CORINFO_HELP_GETGENERICS_GCTHREADSTATIC_BASE |
2091 | |
2092 | #include <optsmallperfcritical.h> |
2093 | HCIMPL1(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 | } |
2139 | HCIMPLEND |
2140 | #include <optdefault.h> |
2141 | |
2142 | //======================================================================== |
2143 | // |
2144 | // STATIC FIELD DYNAMIC HELPERS |
2145 | // |
2146 | //======================================================================== |
2147 | |
2148 | #include <optsmallperfcritical.h> |
2149 | HCIMPL1_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 | } |
2156 | HCIMPLEND_RAW |
2157 | #include <optdefault.h> |
2158 | |
2159 | #include <optsmallperfcritical.h> |
2160 | HCIMPL1_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 | } |
2167 | HCIMPLEND_RAW |
2168 | #include <optdefault.h> |
2169 | |
2170 | //======================================================================== |
2171 | // |
2172 | // CASTING HELPERS |
2173 | // |
2174 | //======================================================================== |
2175 | |
2176 | // pObject MUST be an instance of an array. |
2177 | TypeHandle::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. |
2230 | TypeHandle::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 | |
2262 | TypeHandle::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 | |
2313 | BOOL 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 | |
2398 | HCIMPL2(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 | } |
2423 | HCIMPLEND |
2424 | |
2425 | // |
2426 | // This helper assumes that the check for the trivial cases has been inlined by the JIT. |
2427 | // |
2428 | HCIMPL2(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 | } |
2450 | HCIMPLEND |
2451 | |
2452 | HCIMPL2(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 | } |
2482 | HCIMPLEND |
2483 | |
2484 | HCIMPL2(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 | } |
2504 | HCIMPLEND |
2505 | |
2506 | HCIMPL2(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 | } |
2531 | HCIMPLEND |
2532 | |
2533 | HCIMPL2(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 | } |
2562 | HCIMPLEND |
2563 | |
2564 | |
2565 | HCIMPL2(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 | } |
2602 | HCIMPLEND |
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 |
2608 | HCIMPL2(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 | } |
2630 | HCIMPLEND |
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 |
2635 | HCIMPL2(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 | } |
2657 | HCIMPLEND |
2658 | |
2659 | |
2660 | NOINLINE 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 | } |
2681 | HCIMPLEND |
2682 | |
2683 | NOINLINE 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 | } |
2698 | HCIMPLEND |
2699 | |
2700 | |
2701 | #include <optdefault.h> |
2702 | |
2703 | |
2704 | // |
2705 | // Framed helpers |
2706 | // |
2707 | NOINLINE 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 | } |
2728 | HCIMPLEND |
2729 | |
2730 | NOINLINE 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 | } |
2749 | HCIMPLEND |
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 | // |
2764 | HCIMPL1(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 | } |
2805 | HCIMPLEND |
2806 | |
2807 | #include <optdefault.h> |
2808 | |
2809 | /*************************************************************/ |
2810 | HCIMPL1(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 | } |
2834 | HCIMPLEND |
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 | // |
2849 | HCIMPL1(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 | } |
2901 | HCIMPLEND |
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 | |
2909 | HCIMPL1_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 | } |
2926 | HCIMPLEND_RAW |
2927 | |
2928 | HCIMPL1(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 | } |
2940 | HCIMPLEND |
2941 | |
2942 | /*********************************************************************/ |
2943 | OBJECTHANDLE 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 | /*********************************************************************/ |
2977 | HCIMPL2(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 | } |
2992 | HCIMPLEND |
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 | // |
3006 | HCIMPL2(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 | } |
3069 | HCIMPLEND |
3070 | |
3071 | //************************************************************* |
3072 | // Array allocation fast path for arrays of object elements |
3073 | // |
3074 | HCIMPL2(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 | } |
3128 | HCIMPLEND |
3129 | |
3130 | //************************************************************* |
3131 | // R2R-specific array allocation wrapper that extracts array method table from ArrayTypeDesc |
3132 | // |
3133 | HCIMPL2(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 | } |
3144 | HCIMPLEND |
3145 | |
3146 | #include <optdefault.h> |
3147 | |
3148 | /*************************************************************/ |
3149 | HCIMPL2(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 | } |
3234 | HCIMPLEND |
3235 | |
3236 | /********************************************************************* |
3237 | // Allocate a multi-dimensional array |
3238 | */ |
3239 | OBJECTREF 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 | |
3280 | HCIMPL2VA(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 | } |
3300 | HCIMPLEND |
3301 | |
3302 | /*************************************************************/ |
3303 | HCIMPL3(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 | } |
3319 | HCIMPLEND |
3320 | |
3321 | /*************************************************************/ |
3322 | /* returns '&array[idx], after doing all the proper checks */ |
3323 | |
3324 | #include <optsmallperfcritical.h> |
3325 | HCIMPL3(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 | } |
3346 | HCIMPLEND |
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 | |
3354 | HCIMPL2(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 | } |
3369 | HCIMPLEND |
3370 | |
3371 | /****************************************************************************/ |
3372 | /* assigns 'val to 'array[idx], after doing all the proper checks */ |
3373 | |
3374 | HCIMPL3(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 | } |
3427 | HCIMPLEND |
3428 | |
3429 | |
3430 | |
3431 | //======================================================================== |
3432 | // |
3433 | // VALUETYPE/BYREF HELPERS |
3434 | // |
3435 | //======================================================================== |
3436 | |
3437 | /*************************************************************/ |
3438 | HCIMPL2(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 | } |
3473 | HCIMPLEND |
3474 | |
3475 | /*************************************************************/ |
3476 | NOINLINE 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 | } |
3487 | HCIMPLEND |
3488 | |
3489 | /*************************************************************/ |
3490 | HCIMPL3(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 | } |
3511 | HCIMPLEND |
3512 | |
3513 | /*************************************************************/ |
3514 | /* framed helper that handles full-blown type equivalence */ |
3515 | NOINLINE 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 | } |
3536 | HCIMPLEND |
3537 | |
3538 | /*************************************************************/ |
3539 | /* the uncommon case for the helper below (allowing enums to be unboxed |
3540 | as their underlying type */ |
3541 | LPVOID __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 | /*************************************************************/ |
3570 | HCIMPL2(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 | } |
3593 | HCIMPLEND |
3594 | |
3595 | /*************************************************************/ |
3596 | HCIMPL2_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 | } |
3611 | HCIMPLEND |
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 | |
3660 | struct 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 | |
3706 | class JitGenericHandleCacheTraits |
3707 | { |
3708 | public: |
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 | |
3748 | typedef EEHashTable<const JitGenericHandleCacheKey *, JitGenericHandleCacheTraits, FALSE> JitGenericHandleCache; |
3749 | |
3750 | JitGenericHandleCache *g_pJitGenericHandleCache = NULL; //cache of calls to JIT_GenericHandle |
3751 | CrstStatic g_pJitGenericHandleCacheCrst; |
3752 | |
3753 | void 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 */ |
3780 | void 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. |
3826 | CORINFO_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 |
3918 | NOINLINE 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 | } |
3950 | HCIMPLEND |
3951 | |
3952 | /*********************************************************************/ |
3953 | #include <optsmallperfcritical.h> |
3954 | HCIMPL2(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 | } |
3972 | HCIMPLEND |
3973 | |
3974 | HCIMPL2(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 | } |
3992 | HCIMPLEND |
3993 | #include <optdefault.h> |
3994 | |
3995 | /*********************************************************************/ |
3996 | HCIMPL2(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 | } |
4016 | HCIMPLEND |
4017 | |
4018 | /*********************************************************************/ |
4019 | #include <optsmallperfcritical.h> |
4020 | HCIMPL2(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 | } |
4038 | HCIMPLEND |
4039 | |
4040 | HCIMPL2(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 | } |
4058 | HCIMPLEND |
4059 | #include <optdefault.h> |
4060 | |
4061 | /*********************************************************************/ |
4062 | HCIMPL2(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 | } |
4082 | HCIMPLEND |
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 |
4094 | NOINLINE 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 | } |
4143 | HCIMPLEND |
4144 | |
4145 | HCIMPL2(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 | } |
4157 | HCIMPLEND |
4158 | |
4159 | HCIMPL1(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 | } |
4174 | HCIMPLEND |
4175 | |
4176 | HCIMPL2(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 | } |
4188 | HCIMPLEND |
4189 | |
4190 | HCIMPL1(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 | } |
4205 | HCIMPLEND |
4206 | |
4207 | HCIMPL2(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 | } |
4232 | HCIMPLEND |
4233 | |
4234 | |
4235 | NOINLINE 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 | } |
4252 | HCIMPLEND |
4253 | |
4254 | #include <optsmallperfcritical.h> |
4255 | HCIMPL1(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 | } |
4274 | HCIMPLEND |
4275 | |
4276 | HCIMPL1(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 | } |
4286 | HCIMPLEND |
4287 | #include <optdefault.h> |
4288 | |
4289 | /*********************************************************************/ |
4290 | #include <optsmallperfcritical.h> |
4291 | HCIMPL3(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 | } |
4311 | HCIMPLEND |
4312 | |
4313 | HCIMPL2(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 | } |
4331 | HCIMPLEND |
4332 | |
4333 | #include <optdefault.h> |
4334 | |
4335 | // Helper for synchronized static methods in shared generics code |
4336 | #include <optsmallperfcritical.h> |
4337 | HCIMPL1(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); |
4349 | HCIMPLEND |
4350 | #include <optdefault.h> |
4351 | |
4352 | |
4353 | |
4354 | //======================================================================== |
4355 | // |
4356 | // MONITOR HELPERS |
4357 | // |
4358 | //======================================================================== |
4359 | |
4360 | /*********************************************************************/ |
4361 | NOINLINE 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 | |
4397 | HCIMPL_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 | } |
4409 | HCIMPLEND |
4410 | |
4411 | HCIMPL1(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 | } |
4422 | HCIMPLEND |
4423 | |
4424 | HCIMPL2(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 | } |
4436 | HCIMPLEND |
4437 | |
4438 | #include <optdefault.h> |
4439 | |
4440 | |
4441 | /*********************************************************************/ |
4442 | NOINLINE 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> |
4474 | HCIMPL3(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 | |
4519 | FramedLockHelper: |
4520 | FC_INNER_RETURN_VOID(JIT_MonTryEnter_Helper(obj, timeOut, pbLockTaken)); |
4521 | } |
4522 | HCIMPLEND |
4523 | #include <optdefault.h> |
4524 | |
4525 | /*********************************************************************/ |
4526 | NOINLINE 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 | |
4554 | NOINLINE 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> |
4580 | FCIMPL1(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 | |
4602 | FramedLockHelper: |
4603 | FC_INNER_RETURN_VOID(JIT_MonExit_Helper(obj, NULL)); |
4604 | } |
4605 | HCIMPLEND |
4606 | |
4607 | HCIMPL_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 | |
4634 | FramedLockHelper: |
4635 | FC_INNER_RETURN_VOID(JIT_MonExit_Helper(obj, MONHELPER_ARG)); |
4636 | } |
4637 | HCIMPLEND |
4638 | #include <optdefault.h> |
4639 | |
4640 | /*********************************************************************/ |
4641 | NOINLINE 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> |
4660 | HCIMPL_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 | |
4688 | FramedLockHelper: |
4689 | FC_INNER_RETURN_VOID(JIT_MonEnterStatic_Helper(lock, MONHELPER_ARG)); |
4690 | } |
4691 | HCIMPLEND |
4692 | #include <optdefault.h> |
4693 | |
4694 | /*********************************************************************/ |
4695 | NOINLINE 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 | |
4716 | NOINLINE 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> |
4735 | HCIMPL_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 | } |
4760 | HCIMPLEND |
4761 | #include <optdefault.h> |
4762 | |
4763 | HCIMPL1(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 | |
4785 | HCIMPLEND |
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 | |
4805 | HCIMPL1(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 | } |
4879 | HCIMPLEND |
4880 | |
4881 | /*************************************************************/ |
4882 | |
4883 | HCIMPL0(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 | } |
4911 | HCIMPLEND |
4912 | |
4913 | /*********************************************************************/ |
4914 | HCIMPL0(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 | } |
4929 | HCIMPLEND |
4930 | |
4931 | /*********************************************************************/ |
4932 | HCIMPL0(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 | } |
4947 | HCIMPLEND |
4948 | |
4949 | /*********************************************************************/ |
4950 | HCIMPL0(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 | } |
4965 | HCIMPLEND |
4966 | |
4967 | /*********************************************************************/ |
4968 | HCIMPL0(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 | } |
4983 | HCIMPLEND |
4984 | |
4985 | /*********************************************************************/ |
4986 | HCIMPL0(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 | } |
5001 | HCIMPLEND |
5002 | |
5003 | /*********************************************************************/ |
5004 | HCIMPL0(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 | } |
5019 | HCIMPLEND |
5020 | |
5021 | /*********************************************************************/ |
5022 | HCIMPL0(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 | } |
5037 | HCIMPLEND |
5038 | |
5039 | /*********************************************************************/ |
5040 | HCIMPL0(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 | } |
5055 | HCIMPLEND |
5056 | |
5057 | /*********************************************************************/ |
5058 | HCIMPL0(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 | } |
5073 | HCIMPLEND |
5074 | |
5075 | /*********************************************************************/ |
5076 | HCIMPL1(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 | } |
5087 | HCIMPLEND |
5088 | |
5089 | /*********************************************************************/ |
5090 | HCIMPL1(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 | } |
5102 | HCIMPLEND |
5103 | |
5104 | /*********************************************************************/ |
5105 | static 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 | /*********************************************************************/ |
5139 | HCIMPL1(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 | } |
5149 | HCIMPLEND |
5150 | |
5151 | /*********************************************************************/ |
5152 | HCIMPL1(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 | } |
5162 | HCIMPLEND |
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 | |
5177 | void 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 | |
5222 | HCIMPL0(void, JIT_FailFast) |
5223 | { |
5224 | FCALL_CONTRACT; |
5225 | DoJITFailFast (); |
5226 | } |
5227 | HCIMPLEND |
5228 | |
5229 | HCIMPL2(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 | } |
5246 | HCIMPLEND |
5247 | |
5248 | HCIMPL2(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 | } |
5265 | HCIMPLEND; |
5266 | |
5267 | HCIMPL2(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 | } |
5284 | HCIMPLEND; |
5285 | |
5286 | //======================================================================== |
5287 | // |
5288 | // SECURITY HELPERS |
5289 | // |
5290 | //======================================================================== |
5291 | |
5292 | HCIMPL2(void, JIT_DelegateSecurityCheck, CORINFO_CLASS_HANDLE delegateHnd, CORINFO_METHOD_HANDLE calleeMethodHnd) |
5293 | { |
5294 | FCALL_CONTRACT; |
5295 | } |
5296 | HCIMPLEND |
5297 | |
5298 | HCIMPL4(void, JIT_MethodAccessCheck, CORINFO_METHOD_HANDLE callerMethodHnd, CORINFO_METHOD_HANDLE calleeMethodHnd, CORINFO_CLASS_HANDLE calleeTypeHnd, CorInfoSecurityRuntimeChecks check) |
5299 | { |
5300 | FCALL_CONTRACT; |
5301 | } |
5302 | HCIMPLEND |
5303 | |
5304 | HCIMPL3(void, JIT_FieldAccessCheck, CORINFO_METHOD_HANDLE callerMethodHnd, CORINFO_FIELD_HANDLE calleeFieldHnd, CorInfoSecurityRuntimeChecks check) |
5305 | { |
5306 | FCALL_CONTRACT; |
5307 | } |
5308 | HCIMPLEND |
5309 | |
5310 | HCIMPL3(void, JIT_ClassAccessCheck, CORINFO_METHOD_HANDLE callerMethodHnd, CORINFO_CLASS_HANDLE calleeClassHnd, CorInfoSecurityRuntimeChecks check) |
5311 | { |
5312 | FCALL_CONTRACT; |
5313 | } |
5314 | HCIMPLEND |
5315 | |
5316 | HCIMPL2(void, JIT_Security_Prolog, CORINFO_METHOD_HANDLE methHnd_, OBJECTREF* ppFrameSecDesc) |
5317 | { |
5318 | FCALL_CONTRACT; |
5319 | } |
5320 | HCIMPLEND |
5321 | |
5322 | HCIMPL2(void, JIT_Security_Prolog_Framed, CORINFO_METHOD_HANDLE methHnd_, OBJECTREF* ppFrameSecDesc) |
5323 | { |
5324 | FCALL_CONTRACT; |
5325 | } |
5326 | HCIMPLEND |
5327 | |
5328 | HCIMPL1(void, JIT_VerificationRuntimeCheck, CORINFO_METHOD_HANDLE methHnd_) |
5329 | { |
5330 | FCALL_CONTRACT; |
5331 | } |
5332 | HCIMPLEND |
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 | |
5353 | HCIMPL0(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 | } |
5376 | HCIMPLEND |
5377 | |
5378 | #if defined(_MSC_VER) |
5379 | // VC++ Compiler intrinsic. |
5380 | extern "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. |
5390 | HCIMPL0(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 | } |
5414 | HCIMPLEND |
5415 | |
5416 | #if !(defined(_TARGET_X86_) || defined(_WIN64)) |
5417 | void 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 | |
5447 | HRESULT 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 | /*************************************************************/ |
5479 | HCIMPL1(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 | |
5492 | HCIMPLEND |
5493 | |
5494 | |
5495 | |
5496 | //======================================================================== |
5497 | // |
5498 | // GC HELPERS |
5499 | // |
5500 | //======================================================================== |
5501 | |
5502 | /*************************************************************/ |
5503 | HCIMPL3(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 | } |
5515 | HCIMPLEND |
5516 | |
5517 | /*************************************************************/ |
5518 | HCIMPL0(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 | } |
5543 | HCIMPLEND |
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. |
5553 | extern "C" FCDECL0(VOID, JIT_RareDisableHelper); |
5554 | extern "C" FCDECL0(VOID, JIT_RareDisableHelperWorker); |
5555 | |
5556 | HCIMPL0(void, JIT_RareDisableHelperWorker) |
5557 | #else |
5558 | HCIMPL0(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 | } |
5599 | HCIMPLEND |
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 |
5604 | HCIMPL0(VOID, JIT_StressGC_NOP) |
5605 | { |
5606 | FCALL_CONTRACT; |
5607 | } |
5608 | HCIMPLEND |
5609 | |
5610 | |
5611 | HCIMPL0(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 | } |
5638 | HCIMPLEND |
5639 | |
5640 | |
5641 | |
5642 | HCIMPL0(INT32, JIT_GetCurrentManagedThreadId) |
5643 | { |
5644 | FCALL_CONTRACT; |
5645 | |
5646 | FC_GC_POLL_NOT_NEEDED(); |
5647 | |
5648 | Thread * pThread = GetThread(); |
5649 | return pThread->GetThreadId(); |
5650 | } |
5651 | HCIMPLEND |
5652 | |
5653 | |
5654 | /*********************************************************************/ |
5655 | /* we don't use HCIMPL macros because we don't want the overhead even in debug mode */ |
5656 | |
5657 | HCIMPL1_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 | } |
5670 | HCIMPLEND_RAW |
5671 | |
5672 | static 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". |
5680 | HCIMPL0(void*, JIT_LoopCloneChoiceAddr) |
5681 | { |
5682 | CONTRACTL { |
5683 | FCALL_CHECK; |
5684 | } CONTRACTL_END; |
5685 | |
5686 | return &loopChoice; |
5687 | } |
5688 | HCIMPLEND |
5689 | |
5690 | // Prints a message that loop cloning optimization has occurred. |
5691 | HCIMPL0(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 | } |
5701 | HCIMPLEND |
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 */ |
5715 | Thread * __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 | |
5744 | FCIMPL3(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 | } |
5754 | FCIMPLEND |
5755 | |
5756 | #ifdef _TARGET_ARM_ |
5757 | // This function is used from the FCallMemcpy for GC polling |
5758 | EXTERN_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 |
5782 | enum __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 |
5796 | const 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 |
5804 | VMHELPDEF 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 | |
5814 | VMHELPCOUNTDEF 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 | |
5825 | void _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 | /*********************************************************************/ |
5844 | void 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 | |
5868 | NOINLINE 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) |
5911 | void 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 | // ***************************************************************************** |
6153 | void 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. |
6233 | void 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 | |