| 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 | |