Permalink
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
6378 lines (5074 sloc) 196 KB
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
#include "common.h"
#include "jitinterface.h"
#include "codeman.h"
#include "method.hpp"
#include "class.h"
#include "object.h"
#include "field.h"
#include "stublink.h"
#include "virtualcallstub.h"
#include "corjit.h"
#include "eeconfig.h"
#include "excep.h"
#include "log.h"
#include "excep.h"
#include "float.h" // for isnan
#include "dbginterface.h"
#include "dllimport.h"
#include "gcheaputilities.h"
#include "comdelegate.h"
#include "jitperf.h" // to track jit perf
#include "corprof.h"
#include "eeprofinterfaces.h"
#ifndef FEATURE_PAL
// Included for referencing __report_gsfailure
#include "process.h"
#endif // !FEATURE_PAL
#include "perfcounters.h"
#ifdef PROFILING_SUPPORTED
#include "proftoeeinterfaceimpl.h"
#endif
#include "ecall.h"
#include "generics.h"
#include "typestring.h"
#include "stackprobe.h"
#include "typedesc.h"
#include "genericdict.h"
#include "array.h"
#include "debuginfostore.h"
#include "safemath.h"
#include "threadstatics.h"
#ifdef FEATURE_PREJIT
#include "compile.h"
#endif
#ifdef HAVE_GCCOVER
#include "gccover.h"
#endif // HAVE_GCCOVER
#include "runtimehandles.h"
//========================================================================
//
// This file contains implementation of all JIT helpers. The helpers are
// divided into following categories:
//
// INTEGER ARITHMETIC HELPERS
// FLOATING POINT HELPERS
// INSTANCE FIELD HELPERS
// STATIC FIELD HELPERS
// SHARED STATIC FIELD HELPERS
// CASTING HELPERS
// ALLOCATION HELPERS
// STRING HELPERS
// ARRAY HELPERS
// VALUETYPE/BYREF HELPERS
// GENERICS HELPERS
// EXCEPTION HELPERS
// SECURITY HELPERS
// DEBUGGER/PROFILER HELPERS
// GC HELPERS
// INTEROP HELPERS
//
//========================================================================
//========================================================================
//
// INTEGER ARITHMETIC HELPERS
//
//========================================================================
#include <optsmallperfcritical.h>
//
// helper macro to multiply two 32-bit uints
//
#define Mul32x32To64(a, b) ((UINT64)((UINT32)(a)) * (UINT64)((UINT32)(b)))
//
// helper macro to get high 32-bit of 64-bit int
//
#define Hi32Bits(a) ((UINT32)((UINT64)(a) >> 32))
//
// helper macro to check whether 64-bit signed int fits into 32-bit signed (compiles into one 32-bit compare)
//
#define Is32BitSigned(a) (Hi32Bits(a) == Hi32Bits((INT64)(INT32)(a)))
//
// helper function to shift the result by 32-bits
//
inline UINT64 ShiftToHi32Bits(UINT32 x)
{
// The shift compiles into slow multiplication by 2^32! VSWhidbey 360736
// return ((UINT64)x) << 32;
ULARGE_INTEGER ret;
ret.u.HighPart = x;
ret.u.LowPart = 0;
return ret.QuadPart;
}
#if !defined(_TARGET_X86_) || defined(FEATURE_PAL)
/*********************************************************************/
HCIMPL2_VV(INT64, JIT_LMul, INT64 val1, INT64 val2)
{
FCALL_CONTRACT;
UINT32 val1High = Hi32Bits(val1);
UINT32 val2High = Hi32Bits(val2);
if ((val1High == 0) && (val2High == 0))
return Mul32x32To64(val1, val2);
return (val1 * val2);
}
HCIMPLEND
#endif // !_TARGET_X86_ || FEATURE_PAL
/*********************************************************************/
HCIMPL2_VV(INT64, JIT_LMulOvf, INT64 val1, INT64 val2)
{
FCALL_CONTRACT;
// This short-cut does not actually help since the multiplication
// of two 32-bit signed ints compiles into the call to a slow helper
// if (Is32BitSigned(val1) && Is32BitSigned(val2))
// return (INT64)(INT32)val1 * (INT64)(INT32)val2;
INDEBUG(INT64 expected = val1 * val2;)
INT64 ret;
// Remember the sign of the result
INT32 sign = Hi32Bits(val1) ^ Hi32Bits(val2);
// Convert to unsigned multiplication
if (val1 < 0) val1 = -val1;
if (val2 < 0) val2 = -val2;
// Get the upper 32 bits of the numbers
UINT32 val1High = Hi32Bits(val1);
UINT32 val2High = Hi32Bits(val2);
UINT64 valMid;
if (val1High == 0) {
// Compute the 'middle' bits of the long multiplication
valMid = Mul32x32To64(val2High, val1);
}
else {
if (val2High != 0)
goto ThrowExcep;
// Compute the 'middle' bits of the long multiplication
valMid = Mul32x32To64(val1High, val2);
}
// See if any bits after bit 32 are set
if (Hi32Bits(valMid) != 0)
goto ThrowExcep;
ret = Mul32x32To64(val1, val2) + ShiftToHi32Bits((UINT32)(valMid));
// check for overflow
if (Hi32Bits(ret) < (UINT32)valMid)
goto ThrowExcep;
if (sign >= 0) {
// have we spilled into the sign bit?
if (ret < 0)
goto ThrowExcep;
}
else {
ret = -ret;
// have we spilled into the sign bit?
if (ret > 0)
goto ThrowExcep;
}
_ASSERTE(ret == expected);
return ret;
ThrowExcep:
FCThrow(kOverflowException);
}
HCIMPLEND
/*********************************************************************/
HCIMPL2_VV(UINT64, JIT_ULMulOvf, UINT64 val1, UINT64 val2)
{
FCALL_CONTRACT;
INDEBUG(UINT64 expected = val1 * val2;)
UINT64 ret;
// Get the upper 32 bits of the numbers
UINT32 val1High = Hi32Bits(val1);
UINT32 val2High = Hi32Bits(val2);
UINT64 valMid;
if (val1High == 0) {
if (val2High == 0)
return Mul32x32To64(val1, val2);
// Compute the 'middle' bits of the long multiplication
valMid = Mul32x32To64(val2High, val1);
}
else {
if (val2High != 0)
goto ThrowExcep;
// Compute the 'middle' bits of the long multiplication
valMid = Mul32x32To64(val1High, val2);
}
// See if any bits after bit 32 are set
if (Hi32Bits(valMid) != 0)
goto ThrowExcep;
ret = Mul32x32To64(val1, val2) + ShiftToHi32Bits((UINT32)(valMid));
// check for overflow
if (Hi32Bits(ret) < (UINT32)valMid)
goto ThrowExcep;
_ASSERTE(ret == expected);
return ret;
ThrowExcep:
FCThrow(kOverflowException);
}
HCIMPLEND
/*********************************************************************/
HCIMPL2(INT32, JIT_Div, INT32 dividend, INT32 divisor)
{
FCALL_CONTRACT;
RuntimeExceptionKind ehKind;
if (((UINT32) (divisor + 1)) <= 1) // Unsigned test for divisor in [-1 .. 0]
{
if (divisor == 0)
{
ehKind = kDivideByZeroException;
goto ThrowExcep;
}
else if (divisor == -1)
{
if (dividend == _I32_MIN)
{
ehKind = kOverflowException;
goto ThrowExcep;
}
return -dividend;
}
}
return(dividend / divisor);
ThrowExcep:
FCThrow(ehKind);
}
HCIMPLEND
/*********************************************************************/
HCIMPL2(INT32, JIT_Mod, INT32 dividend, INT32 divisor)
{
FCALL_CONTRACT;
RuntimeExceptionKind ehKind;
if (((UINT32) (divisor + 1)) <= 1) // Unsigned test for divisor in [-1 .. 0]
{
if (divisor == 0)
{
ehKind = kDivideByZeroException;
goto ThrowExcep;
}
else if (divisor == -1)
{
if (dividend == _I32_MIN)
{
ehKind = kOverflowException;
goto ThrowExcep;
}
return 0;
}
}
return(dividend % divisor);
ThrowExcep:
FCThrow(ehKind);
}
HCIMPLEND
/*********************************************************************/
HCIMPL2(UINT32, JIT_UDiv, UINT32 dividend, UINT32 divisor)
{
FCALL_CONTRACT;
if (divisor == 0)
FCThrow(kDivideByZeroException);
return(dividend / divisor);
}
HCIMPLEND
/*********************************************************************/
HCIMPL2(UINT32, JIT_UMod, UINT32 dividend, UINT32 divisor)
{
FCALL_CONTRACT;
if (divisor == 0)
FCThrow(kDivideByZeroException);
return(dividend % divisor);
}
HCIMPLEND
/*********************************************************************/
HCIMPL2_VV(INT64, JIT_LDiv, INT64 dividend, INT64 divisor)
{
FCALL_CONTRACT;
RuntimeExceptionKind ehKind;
if (Is32BitSigned(divisor))
{
if ((INT32)divisor == 0)
{
ehKind = kDivideByZeroException;
goto ThrowExcep;
}
if ((INT32)divisor == -1)
{
if ((UINT64) dividend == UI64(0x8000000000000000))
{
ehKind = kOverflowException;
goto ThrowExcep;
}
return -dividend;
}
// Check for -ive or +ive numbers in the range -2**31 to 2**31
if (Is32BitSigned(dividend))
return((INT32)dividend / (INT32)divisor);
}
// For all other combinations fallback to int64 div.
return(dividend / divisor);
ThrowExcep:
FCThrow(ehKind);
}
HCIMPLEND
/*********************************************************************/
HCIMPL2_VV(INT64, JIT_LMod, INT64 dividend, INT64 divisor)
{
FCALL_CONTRACT;
RuntimeExceptionKind ehKind;
if (Is32BitSigned(divisor))
{
if ((INT32)divisor == 0)
{
ehKind = kDivideByZeroException;
goto ThrowExcep;
}
if ((INT32)divisor == -1)
{
// <TODO>TODO, we really should remove this as it lengthens the code path
// and the spec really says that it should not throw an exception. </TODO>
if ((UINT64) dividend == UI64(0x8000000000000000))
{
ehKind = kOverflowException;
goto ThrowExcep;
}
return 0;
}
// Check for -ive or +ive numbers in the range -2**31 to 2**31
if (Is32BitSigned(dividend))
return((INT32)dividend % (INT32)divisor);
}
// For all other combinations fallback to int64 div.
return(dividend % divisor);
ThrowExcep:
FCThrow(ehKind);
}
HCIMPLEND
/*********************************************************************/
HCIMPL2_VV(UINT64, JIT_ULDiv, UINT64 dividend, UINT64 divisor)
{
FCALL_CONTRACT;
if (Hi32Bits(divisor) == 0)
{
if ((UINT32)(divisor) == 0)
FCThrow(kDivideByZeroException);
if (Hi32Bits(dividend) == 0)
return((UINT32)dividend / (UINT32)divisor);
}
return(dividend / divisor);
}
HCIMPLEND
/*********************************************************************/
HCIMPL2_VV(UINT64, JIT_ULMod, UINT64 dividend, UINT64 divisor)
{
FCALL_CONTRACT;
if (Hi32Bits(divisor) == 0)
{
if ((UINT32)(divisor) == 0)
FCThrow(kDivideByZeroException);
if (Hi32Bits(dividend) == 0)
return((UINT32)dividend % (UINT32)divisor);
}
return(dividend % divisor);
}
HCIMPLEND
#if !defined(BIT64) && !defined(_TARGET_X86_)
/*********************************************************************/
HCIMPL2_VV(UINT64, JIT_LLsh, UINT64 num, int shift)
{
FCALL_CONTRACT;
return num << (shift & 0x3F);
}
HCIMPLEND
/*********************************************************************/
HCIMPL2_VV(INT64, JIT_LRsh, INT64 num, int shift)
{
FCALL_CONTRACT;
return num >> (shift & 0x3F);
}
HCIMPLEND
/*********************************************************************/
HCIMPL2_VV(UINT64, JIT_LRsz, UINT64 num, int shift)
{
FCALL_CONTRACT;
return num >> (shift & 0x3F);
}
HCIMPLEND
#endif // !BIT64 && !_TARGET_X86_
#include <optdefault.h>
//========================================================================
//
// FLOATING POINT HELPERS
//
//========================================================================
#include <optsmallperfcritical.h>
/*********************************************************************/
//
HCIMPL1_V(double, JIT_ULng2Dbl, UINT64 val)
{
FCALL_CONTRACT;
double conv = (double) ((INT64) val);
if (conv < 0)
conv += (4294967296.0 * 4294967296.0); // add 2^64
_ASSERTE(conv >= 0);
return(conv);
}
HCIMPLEND
/*********************************************************************/
// needed for ARM and RyuJIT-x86
HCIMPL1_V(double, JIT_Lng2Dbl, INT64 val)
{
FCALL_CONTRACT;
return double(val);
}
HCIMPLEND
//--------------------------------------------------------------------------
template <class ftype>
ftype modftype(ftype value, ftype *iptr);
template <> float modftype(float value, float *iptr) { return modff(value, iptr); }
template <> double modftype(double value, double *iptr) { return modf(value, iptr); }
// round to nearest, round to even if tied
template <class ftype>
ftype BankersRound(ftype value)
{
if (value < 0.0) return -BankersRound <ftype> (-value);
ftype integerPart;
modftype( value, &integerPart );
// if decimal part is exactly .5
if ((value -(integerPart +0.5)) == 0.0)
{
// round to even
#if defined(_TARGET_ARM_) && defined(FEATURE_CORESYSTEM)
// @ARMTODO: On ARM when building on CoreSystem (where we link against the system CRT) an attempt to
// use fmod(float, float) fails to link (apparently this is converted to a reference to fmodf, which
// is not included in the system CRT). Use the double version instead.
if (fmod(double(integerPart), double(2.0)) == 0.0)
return integerPart;
#else
if (fmod(ftype(integerPart), ftype(2.0)) == 0.0)
return integerPart;
#endif
// Else return the nearest even integer
return (ftype)_copysign(ceil(fabs(value+0.5)),
value);
}
// Otherwise round to closest
return (ftype)_copysign(floor(fabs(value)+0.5),
value);
}
/*********************************************************************/
// round double to nearest int (as double)
HCIMPL1_V(double, JIT_DoubleRound, double val)
{
FCALL_CONTRACT;
return BankersRound(val);
}
HCIMPLEND
/*********************************************************************/
// round float to nearest int (as float)
HCIMPL1_V(float, JIT_FloatRound, float val)
{
FCALL_CONTRACT;
return BankersRound(val);
}
HCIMPLEND
/*********************************************************************/
// Call fast Dbl2Lng conversion - used by functions below
FORCEINLINE INT64 FastDbl2Lng(double val)
{
#ifdef _TARGET_X86_
FCALL_CONTRACT;
return HCCALL1_V(JIT_Dbl2Lng, val);
#else
FCALL_CONTRACT;
return((__int64) val);
#endif
}
/*********************************************************************/
HCIMPL1_V(UINT32, JIT_Dbl2UIntOvf, double val)
{
FCALL_CONTRACT;
// Note that this expression also works properly for val = NaN case
if (val > -1.0 && val < 4294967296.0)
return((UINT32)FastDbl2Lng(val));
FCThrow(kOverflowException);
}
HCIMPLEND
/*********************************************************************/
HCIMPL1_V(UINT64, JIT_Dbl2ULng, double val)
{
FCALL_CONTRACT;
const double two63 = 2147483648.0 * 4294967296.0;
UINT64 ret;
if (val < two63) {
ret = FastDbl2Lng(val);
}
else {
// subtract 0x8000000000000000, do the convert then add it back again
ret = FastDbl2Lng(val - two63) + I64(0x8000000000000000);
}
return ret;
}
HCIMPLEND
/*********************************************************************/
HCIMPL1_V(UINT64, JIT_Dbl2ULngOvf, double val)
{
FCALL_CONTRACT;
const double two64 = 4294967296.0 * 4294967296.0;
// Note that this expression also works properly for val = NaN case
if (val > -1.0 && val < two64) {
const double two63 = 2147483648.0 * 4294967296.0;
UINT64 ret;
if (val < two63) {
ret = FastDbl2Lng(val);
}
else {
// subtract 0x8000000000000000, do the convert then add it back again
ret = FastDbl2Lng(val - two63) + I64(0x8000000000000000);
}
#ifdef _DEBUG
// since no overflow can occur, the value always has to be within 1
double roundTripVal = HCCALL1_V(JIT_ULng2Dbl, ret);
_ASSERTE(val - 1.0 <= roundTripVal && roundTripVal <= val + 1.0);
#endif // _DEBUG
return ret;
}
FCThrow(kOverflowException);
}
HCIMPLEND
#if !defined(_TARGET_X86_) || defined(FEATURE_PAL)
HCIMPL1_V(INT64, JIT_Dbl2Lng, double val)
{
FCALL_CONTRACT;
return((INT64)val);
}
HCIMPLEND
HCIMPL1_V(int, JIT_Dbl2IntOvf, double val)
{
FCALL_CONTRACT;
const double two31 = 2147483648.0;
// Note that this expression also works properly for val = NaN case
if (val > -two31 - 1 && val < two31)
return((INT32)val);
FCThrow(kOverflowException);
}
HCIMPLEND
HCIMPL1_V(INT64, JIT_Dbl2LngOvf, double val)
{
FCALL_CONTRACT;
const double two63 = 2147483648.0 * 4294967296.0;
// Note that this expression also works properly for val = NaN case
// We need to compare with the very next double to two63. 0x402 is epsilon to get us there.
if (val > -two63 - 0x402 && val < two63)
return((INT64)val);
FCThrow(kOverflowException);
}
HCIMPLEND
HCIMPL2_VV(float, JIT_FltRem, float dividend, float divisor)
{
FCALL_CONTRACT;
//
// From the ECMA standard:
//
// If [divisor] is zero or [dividend] is infinity
// the result is NaN.
// If [divisor] is infinity,
// the result is [dividend] (negated for -infinity***).
//
// ***"negated for -infinity" has been removed from the spec
//
if (divisor==0 || !_finite(dividend))
{
UINT32 NaN = CLR_NAN_32;
return *(float *)(&NaN);
}
else if (!_finite(divisor) && !_isnan(divisor))
{
return dividend;
}
// else...
#if 0
// COMPILER BUG WITH FMODF() + /Oi, USE FMOD() INSTEAD
return fmodf(dividend,divisor);
#else
return (float)fmod((double)dividend,(double)divisor);
#endif
}
HCIMPLEND
HCIMPL2_VV(double, JIT_DblRem, double dividend, double divisor)
{
FCALL_CONTRACT;
//
// From the ECMA standard:
//
// If [divisor] is zero or [dividend] is infinity
// the result is NaN.
// If [divisor] is infinity,
// the result is [dividend] (negated for -infinity***).
//
// ***"negated for -infinity" has been removed from the spec
//
if (divisor==0 || !_finite(dividend))
{
UINT64 NaN = CLR_NAN_64;
return *(double *)(&NaN);
}
else if (!_finite(divisor) && !_isnan(divisor))
{
return dividend;
}
// else...
return(fmod(dividend,divisor));
}
HCIMPLEND
#endif // !_TARGET_X86_ || FEATURE_PAL
#include <optdefault.h>
//========================================================================
//
// INSTANCE FIELD HELPERS
//
//========================================================================
/*********************************************************************/
// Returns the address of the field in the object (This is an interior
// pointer and the caller has to use it appropriately). obj can be
// either a reference or a byref
HCIMPL2(void*, JIT_GetFieldAddr_Framed, Object *obj, FieldDesc* pFD)
{
CONTRACTL {
FCALL_CHECK;
PRECONDITION(CheckPointer(pFD));
} CONTRACTL_END;
void * fldAddr = NULL;
OBJECTREF objRef = ObjectToOBJECTREF(obj);
HELPER_METHOD_FRAME_BEGIN_RET_1(objRef);
if (objRef == NULL)
COMPlusThrow(kNullReferenceException);
fldAddr = pFD->GetAddress(OBJECTREFToObject(objRef));
HELPER_METHOD_FRAME_END();
return fldAddr;
}
HCIMPLEND
#include <optsmallperfcritical.h>
HCIMPL2(void*, JIT_GetFieldAddr, Object *obj, FieldDesc* pFD)
{
CONTRACTL {
FCALL_CHECK;
PRECONDITION(CheckPointer(pFD));
} CONTRACTL_END;
if (obj == NULL || g_IBCLogger.InstrEnabled() || pFD->IsEnCNew())
{
ENDFORBIDGC();
return HCCALL2(JIT_GetFieldAddr_Framed, obj, pFD);
}
return pFD->GetAddressGuaranteedInHeap(obj);
}
HCIMPLEND
#include <optdefault.h>
/*********************************************************************/
#define HCallAssert(cache, target) // suppressed to avoid ambiguous cast errors caused by use of template
template <typename FIELDTYPE>
NOINLINE HCIMPL2(FIELDTYPE, JIT_GetField_Framed, Object *obj, FieldDesc *pFD)
#undef HCallAssert
{
FCALL_CONTRACT;
FIELDTYPE value = 0;
// This is an instance field helper
_ASSERTE(!pFD->IsStatic());
OBJECTREF objRef = ObjectToOBJECTREF(obj);
HELPER_METHOD_FRAME_BEGIN_RET_1(objRef);
if (objRef == NULL)
COMPlusThrow(kNullReferenceException);
pFD->GetInstanceField(objRef, &value);
HELPER_METHOD_POLL();
HELPER_METHOD_FRAME_END();
return value;
}
HCIMPLEND
/*********************************************************************/
#include <optsmallperfcritical.h>
HCIMPL2(INT8, JIT_GetField8, Object *obj, FieldDesc *pFD)
{
FCALL_CONTRACT;
if (obj == NULL || g_IBCLogger.InstrEnabled() || pFD->IsEnCNew())
{
ENDFORBIDGC();
return HCCALL2(JIT_GetField_Framed<INT8>, obj, pFD);
}
INT8 val = VolatileLoad<INT8>((INT8*)pFD->GetAddressGuaranteedInHeap(obj));
FC_GC_POLL_RET();
return val;
}
HCIMPLEND
HCIMPL2(INT16, JIT_GetField16, Object *obj, FieldDesc *pFD)
{
FCALL_CONTRACT;
if (obj == NULL || g_IBCLogger.InstrEnabled() || pFD->IsEnCNew())
{
ENDFORBIDGC();
return HCCALL2(JIT_GetField_Framed<INT16>, obj, pFD);
}
INT16 val = VolatileLoad<INT16>((INT16*)pFD->GetAddressGuaranteedInHeap(obj));
FC_GC_POLL_RET();
return val;
}
HCIMPLEND
HCIMPL2(INT32, JIT_GetField32, Object *obj, FieldDesc *pFD)
{
FCALL_CONTRACT;
if (obj == NULL || g_IBCLogger.InstrEnabled() || pFD->IsEnCNew())
{
ENDFORBIDGC();
return HCCALL2(JIT_GetField_Framed<INT32>, obj, pFD);
}
INT32 val = VolatileLoad<INT32>((INT32*)pFD->GetAddressGuaranteedInHeap(obj));
FC_GC_POLL_RET();
return val;
}
HCIMPLEND
HCIMPL2(INT64, JIT_GetField64, Object *obj, FieldDesc *pFD)
{
FCALL_CONTRACT;
if (obj == NULL || g_IBCLogger.InstrEnabled() || pFD->IsEnCNew())
{
ENDFORBIDGC();
return HCCALL2(JIT_GetField_Framed<INT64>, obj, pFD);
}
INT64 val = VolatileLoad<INT64>((INT64*)pFD->GetAddressGuaranteedInHeap(obj));
FC_GC_POLL_RET();
return val;
}
HCIMPLEND
HCIMPL2(FLOAT, JIT_GetFieldFloat, Object *obj, FieldDesc *pFD)
{
FCALL_CONTRACT;
if (obj == NULL || g_IBCLogger.InstrEnabled() || pFD->IsEnCNew())
{
ENDFORBIDGC();
return HCCALL2(JIT_GetField_Framed<FLOAT>, obj, pFD);
}
FLOAT val;
(INT32&)val = VolatileLoad<INT32>((INT32*)pFD->GetAddressGuaranteedInHeap(obj));
FC_GC_POLL_RET();
return val;
}
HCIMPLEND
HCIMPL2(DOUBLE, JIT_GetFieldDouble, Object *obj, FieldDesc *pFD)
{
FCALL_CONTRACT;
if (obj == NULL || g_IBCLogger.InstrEnabled() || pFD->IsEnCNew())
{
ENDFORBIDGC();
return HCCALL2(JIT_GetField_Framed<DOUBLE>, obj, pFD);
}
DOUBLE val;
(INT64&)val = VolatileLoad<INT64>((INT64*)pFD->GetAddressGuaranteedInHeap(obj));
FC_GC_POLL_RET();
return val;
}
HCIMPLEND
#include <optdefault.h>
/*********************************************************************/
#define HCallAssert(cache, target) // suppressed to avoid ambiguous cast errors caused by use of template
template <typename FIELDTYPE>
NOINLINE HCIMPL3(VOID, JIT_SetField_Framed, Object *obj, FieldDesc* pFD, FIELDTYPE val)
#undef HCallAssert
{
FCALL_CONTRACT;
// This is an instance field helper
_ASSERTE(!pFD->IsStatic());
OBJECTREF objRef = ObjectToOBJECTREF(obj);
HELPER_METHOD_FRAME_BEGIN_1(objRef);
if (objRef == NULL)
COMPlusThrow(kNullReferenceException);
pFD->SetInstanceField(objRef, &val);
HELPER_METHOD_POLL();
HELPER_METHOD_FRAME_END();
}
HCIMPLEND
/*********************************************************************/
#include <optsmallperfcritical.h>
HCIMPL3(VOID, JIT_SetField8, Object *obj, FieldDesc *pFD, INT8 val)
{
FCALL_CONTRACT;
if (obj == NULL || g_IBCLogger.InstrEnabled() || pFD->IsEnCNew())
{
ENDFORBIDGC();
return HCCALL3(JIT_SetField_Framed<INT8>, obj, pFD, val);
}
VolatileStore<INT8>((INT8*)pFD->GetAddressGuaranteedInHeap(obj), val);
FC_GC_POLL();
}
HCIMPLEND
HCIMPL3(VOID, JIT_SetField16, Object *obj, FieldDesc *pFD, INT16 val)
{
FCALL_CONTRACT;
if (obj == NULL || g_IBCLogger.InstrEnabled() || pFD->IsEnCNew())
{
ENDFORBIDGC();
return HCCALL3(JIT_SetField_Framed<INT16>, obj, pFD, val);
}
VolatileStore<INT16>((INT16*)pFD->GetAddressGuaranteedInHeap(obj), val);
FC_GC_POLL();
}
HCIMPLEND
HCIMPL3(VOID, JIT_SetField32, Object *obj, FieldDesc *pFD, INT32 val)
{
FCALL_CONTRACT;
if (obj == NULL || g_IBCLogger.InstrEnabled() || pFD->IsEnCNew())
{
ENDFORBIDGC();
return HCCALL3(JIT_SetField_Framed<INT32>, obj, pFD, val);
}
VolatileStore<INT32>((INT32*)pFD->GetAddressGuaranteedInHeap(obj), val);
FC_GC_POLL();
}
HCIMPLEND
HCIMPL3(VOID, JIT_SetField64, Object *obj, FieldDesc *pFD, INT64 val)
{
FCALL_CONTRACT;
if (obj == NULL || g_IBCLogger.InstrEnabled() || pFD->IsEnCNew())
{
ENDFORBIDGC();
return HCCALL3(JIT_SetField_Framed<INT64>, obj, pFD, val);
}
VolatileStore<INT64>((INT64*)pFD->GetAddressGuaranteedInHeap(obj), val);
FC_GC_POLL();
}
HCIMPLEND
HCIMPL3(VOID, JIT_SetFieldFloat, Object *obj, FieldDesc *pFD, FLOAT val)
{
FCALL_CONTRACT;
if (obj == NULL || g_IBCLogger.InstrEnabled() || pFD->IsEnCNew())
{
ENDFORBIDGC();
return HCCALL3(JIT_SetField_Framed<FLOAT>, obj, pFD, val);
}
VolatileStore<INT32>((INT32*)pFD->GetAddressGuaranteedInHeap(obj), (INT32&)val);
FC_GC_POLL();
}
HCIMPLEND
HCIMPL3(VOID, JIT_SetFieldDouble, Object *obj, FieldDesc *pFD, DOUBLE val)
{
FCALL_CONTRACT;
if (obj == NULL || g_IBCLogger.InstrEnabled() || pFD->IsEnCNew())
{
ENDFORBIDGC();
return HCCALL3(JIT_SetField_Framed<DOUBLE>, obj, pFD, val);
}
VolatileStore<INT64>((INT64*)pFD->GetAddressGuaranteedInHeap(obj), (INT64&)val);
FC_GC_POLL();
}
HCIMPLEND
#include <optdefault.h>
/*********************************************************************/
HCIMPL2(Object*, JIT_GetFieldObj_Framed, Object *obj, FieldDesc *pFD)
{
CONTRACTL {
FCALL_CHECK;
PRECONDITION(!pFD->IsStatic());
PRECONDITION(!pFD->IsPrimitive() && !pFD->IsByValue()); // Assert that we are called only for objects
} CONTRACTL_END;
OBJECTREF objRef = ObjectToOBJECTREF(obj);
OBJECTREF val = NULL;
HELPER_METHOD_FRAME_BEGIN_RET_2(objRef, val); // Set up a frame
if (objRef == NULL)
COMPlusThrow(kNullReferenceException);
pFD->GetInstanceField(objRef, &val);
HELPER_METHOD_POLL();
HELPER_METHOD_FRAME_END();
return OBJECTREFToObject(val);
}
HCIMPLEND
#include <optsmallperfcritical.h>
HCIMPL2(Object*, JIT_GetFieldObj, Object *obj, FieldDesc *pFD)
{
CONTRACTL {
FCALL_CHECK;
PRECONDITION(!pFD->IsStatic());
PRECONDITION(!pFD->IsPrimitive() && !pFD->IsByValue()); // Assert that we are called only for objects
} CONTRACTL_END;
if (obj == NULL || g_IBCLogger.InstrEnabled() || pFD->IsEnCNew())
{
ENDFORBIDGC();
return HCCALL2(JIT_GetFieldObj_Framed, obj, pFD);
}
void * address = pFD->GetAddressGuaranteedInHeap(obj);
OBJECTREF val = ObjectToOBJECTREF(VolatileLoad((Object **)address));
FC_GC_POLL_AND_RETURN_OBJREF(val);
}
HCIMPLEND
#include <optdefault.h>
/*********************************************************************/
HCIMPL3(VOID, JIT_SetFieldObj_Framed, Object *obj, FieldDesc *pFD, Object *value)
{
CONTRACTL {
FCALL_CHECK;
PRECONDITION(!pFD->IsStatic());
PRECONDITION(!pFD->IsPrimitive() && !pFD->IsByValue()); // Assert that we are called only for objects
} CONTRACTL_END;
OBJECTREF objRef = ObjectToOBJECTREF(obj);
OBJECTREF val = ObjectToOBJECTREF(value);
HELPER_METHOD_FRAME_BEGIN_2(objRef, val);
if (objRef == NULL)
COMPlusThrow(kNullReferenceException);
pFD->SetInstanceField(objRef, &val);
HELPER_METHOD_POLL();
HELPER_METHOD_FRAME_END();
}
HCIMPLEND
#include <optsmallperfcritical.h>
HCIMPL3(VOID, JIT_SetFieldObj, Object *obj, FieldDesc *pFD, Object *value)
{
CONTRACTL {
FCALL_CHECK;
PRECONDITION(!pFD->IsStatic());
PRECONDITION(!pFD->IsPrimitive() && !pFD->IsByValue()); // Assert that we are called only for objects
} CONTRACTL_END;
if (obj == NULL || g_IBCLogger.InstrEnabled() || pFD->IsEnCNew())
{
ENDFORBIDGC();
return HCCALL3(JIT_SetFieldObj_Framed, obj, pFD, value);
}
void * address = pFD->GetAddressGuaranteedInHeap(obj);
SetObjectReference((OBJECTREF*)address, ObjectToOBJECTREF(value), GetAppDomain());
FC_GC_POLL();
}
HCIMPLEND
#include <optdefault.h>
/*********************************************************************/
HCIMPL4(VOID, JIT_GetFieldStruct_Framed, LPVOID retBuff, Object *obj, FieldDesc *pFD, MethodTable *pFieldMT)
{
FCALL_CONTRACT;
// This may be a cross context field access. Setup a frame as we will
// transition to managed code later
// This is an instance field helper
_ASSERTE(!pFD->IsStatic());
// Assert that we are not called for objects or primitive types
_ASSERTE(!pFD->IsPrimitive());
OBJECTREF objRef = ObjectToOBJECTREF(obj);
HELPER_METHOD_FRAME_BEGIN_1(objRef); // Set up a frame
if (objRef == NULL)
COMPlusThrow(kNullReferenceException);
// Try an unwrap operation in case that we are not being called
// in the same context as the server.
// If that is the case then GetObjectFromProxy will return
// the server object.
BOOL fRemoted = FALSE;
if (!fRemoted)
{
void * pAddr = pFD->GetAddress(OBJECTREFToObject(objRef));
CopyValueClass(retBuff, pAddr, pFieldMT, objRef->GetAppDomain());
}
HELPER_METHOD_FRAME_END(); // Tear down the frame
}
HCIMPLEND
#include <optsmallperfcritical.h>
HCIMPL4(VOID, JIT_GetFieldStruct, LPVOID retBuff, Object *obj, FieldDesc *pFD, MethodTable *pFieldMT)
{
FCALL_CONTRACT;
_ASSERTE(pFieldMT->IsValueType());
if (obj == NULL || g_IBCLogger.InstrEnabled() || pFD->IsEnCNew())
{
ENDFORBIDGC();
return HCCALL4(JIT_GetFieldStruct_Framed, retBuff, obj, pFD, pFieldMT);
}
void * pAddr = pFD->GetAddressGuaranteedInHeap(obj);
CopyValueClass(retBuff, pAddr, pFieldMT, obj->GetAppDomain());
}
HCIMPLEND
#include <optdefault.h>
/*********************************************************************/
HCIMPL4(VOID, JIT_SetFieldStruct_Framed, Object *obj, FieldDesc *pFD, MethodTable *pFieldMT, LPVOID valuePtr)
{
FCALL_CONTRACT;
// Assert that we are not called for objects or primitive types
_ASSERTE(!pFD->IsPrimitive());
OBJECTREF objRef = ObjectToOBJECTREF(obj);
// This may be a cross context field access. Setup a frame as we will
// transition to managed code later
HELPER_METHOD_FRAME_BEGIN_1(objRef); // Set up a frame
if (objRef == NULL)
COMPlusThrow(kNullReferenceException);
// Try an unwrap operation in case that we are not being called
// in the same context as the server.
// If that is the case then GetObjectFromProxy will return
// the server object.
BOOL fRemoted = FALSE;
if (!fRemoted)
{
void * pAddr = pFD->GetAddress(OBJECTREFToObject(objRef));
CopyValueClass(pAddr, valuePtr, pFieldMT, objRef->GetAppDomain());
}
HELPER_METHOD_FRAME_END(); // Tear down the frame
}
HCIMPLEND
#include <optsmallperfcritical.h>
HCIMPL4(VOID, JIT_SetFieldStruct, Object *obj, FieldDesc *pFD, MethodTable *pFieldMT, LPVOID valuePtr)
{
FCALL_CONTRACT;
_ASSERTE(pFieldMT->IsValueType());
if (obj == NULL || g_IBCLogger.InstrEnabled() || pFD->IsEnCNew())
{
ENDFORBIDGC();
return HCCALL4(JIT_SetFieldStruct_Framed, obj, pFD, pFieldMT, valuePtr);
}
void * pAddr = pFD->GetAddressGuaranteedInHeap(obj);
CopyValueClass(pAddr, valuePtr, pFieldMT, obj->GetAppDomain());
}
HCIMPLEND
#include <optdefault.h>
//========================================================================
//
// STATIC FIELD HELPERS
//
//========================================================================
// Slow helper to tailcall from the fast one
NOINLINE HCIMPL1(void, JIT_InitClass_Framed, MethodTable* pMT)
{
FCALL_CONTRACT;
HELPER_METHOD_FRAME_BEGIN_0();
// We don't want to be calling JIT_InitClass at all for perf reasons
// on the Global Class <Module> as the Class loading logic ensures that we
// already have initialized the Gloabl Class <Module>
CONSISTENCY_CHECK(!pMT->IsGlobalClass());
pMT->CheckRestore();
pMT->CheckRunClassInitThrowing();
HELPER_METHOD_FRAME_END();
}
HCIMPLEND
/*************************************************************/
#include <optsmallperfcritical.h>
HCIMPL1(void, JIT_InitClass, CORINFO_CLASS_HANDLE typeHnd_)
{
FCALL_CONTRACT;
TypeHandle typeHnd(typeHnd_);
MethodTable *pMT = typeHnd.AsMethodTable();
_ASSERTE(!pMT->IsClassPreInited());
if (pMT->GetDomainLocalModule()->IsClassInitialized(pMT))
return;
// Tailcall to the slow helper
ENDFORBIDGC();
HCCALL1(JIT_InitClass_Framed, pMT);
}
HCIMPLEND
#include <optdefault.h>
/*************************************************************/
HCIMPL2(void, JIT_InitInstantiatedClass, CORINFO_CLASS_HANDLE typeHnd_, CORINFO_METHOD_HANDLE methHnd_)
{
CONTRACTL {
FCALL_CHECK;
PRECONDITION(methHnd_ != NULL);
} CONTRACTL_END;
HELPER_METHOD_FRAME_BEGIN_NOPOLL(); // Set up a frame
MethodTable * pMT = (MethodTable*) typeHnd_;
MethodDesc * pMD = (MethodDesc*) methHnd_;
MethodTable * pTemplateMT = pMD->GetMethodTable();
if (pTemplateMT->IsSharedByGenericInstantiations())
{
pMT = ClassLoader::LoadGenericInstantiationThrowing(pTemplateMT->GetModule(),
pTemplateMT->GetCl(),
pMD->GetExactClassInstantiation(pMT)).AsMethodTable();
}
else
{
pMT = pTemplateMT;
}
pMT->CheckRestore();
pMT->EnsureInstanceActive();
pMT->CheckRunClassInitThrowing();
HELPER_METHOD_FRAME_END();
}
HCIMPLEND
//========================================================================
//
// SHARED STATIC FIELD HELPERS
//
//========================================================================
#include <optsmallperfcritical.h>
HCIMPL2(void*, JIT_GetSharedNonGCStaticBase_Portable, SIZE_T moduleDomainID, DWORD dwClassDomainID)
{
FCALL_CONTRACT;
DomainLocalModule *pLocalModule = NULL;
if (!Module::IsEncodedModuleIndex(moduleDomainID))
pLocalModule = (DomainLocalModule *) moduleDomainID;
else
{
DomainLocalBlock *pLocalBlock = GetAppDomain()->GetDomainLocalBlock();
pLocalModule = pLocalBlock->GetModuleSlot(Module::IDToIndex(moduleDomainID));
}
// If type doesn't have a class constructor, the contents of this if statement may
// still get executed. JIT_GetSharedNonGCStaticBaseNoCtor should be used in this case.
if (pLocalModule->IsPrecomputedClassInitialized(dwClassDomainID))
{
return (void*)pLocalModule->GetPrecomputedNonGCStaticsBasePointer();
}
// Tailcall to the slow helper
ENDFORBIDGC();
return HCCALL2(JIT_GetSharedNonGCStaticBase_Helper, pLocalModule, dwClassDomainID);
}
HCIMPLEND
// No constructor version of JIT_GetSharedNonGCStaticBase. Does not check if class has
// been initialized.
HCIMPL1(void*, JIT_GetSharedNonGCStaticBaseNoCtor_Portable, SIZE_T moduleDomainID)
{
FCALL_CONTRACT;
DomainLocalModule *pLocalModule = NULL;
if (!Module::IsEncodedModuleIndex(moduleDomainID))
pLocalModule = (DomainLocalModule *) moduleDomainID;
else
{
DomainLocalBlock *pLocalBlock = GetAppDomain()->GetDomainLocalBlock();
pLocalModule = pLocalBlock->GetModuleSlot(Module::IDToIndex(moduleDomainID));
}
return (void*)pLocalModule->GetPrecomputedNonGCStaticsBasePointer();
}
HCIMPLEND
HCIMPL2(void*, JIT_GetSharedGCStaticBase_Portable, SIZE_T moduleDomainID, DWORD dwClassDomainID)
{
FCALL_CONTRACT;
DomainLocalModule *pLocalModule = NULL;
if (!Module::IsEncodedModuleIndex(moduleDomainID))
pLocalModule = (DomainLocalModule *) moduleDomainID;
else
{
DomainLocalBlock *pLocalBlock = GetAppDomain()->GetDomainLocalBlock();
pLocalModule = pLocalBlock->GetModuleSlot(Module::IDToIndex(moduleDomainID));
}
// If type doesn't have a class constructor, the contents of this if statement may
// still get executed. JIT_GetSharedGCStaticBaseNoCtor should be used in this case.
if (pLocalModule->IsPrecomputedClassInitialized(dwClassDomainID))
{
return (void*)pLocalModule->GetPrecomputedGCStaticsBasePointer();
}
// Tailcall to the slow helper
ENDFORBIDGC();
return HCCALL2(JIT_GetSharedGCStaticBase_Helper, pLocalModule, dwClassDomainID);
}
HCIMPLEND
// No constructor version of JIT_GetSharedGCStaticBase. Does not check if class has been
// initialized.
HCIMPL1(void*, JIT_GetSharedGCStaticBaseNoCtor_Portable, SIZE_T moduleDomainID)
{
FCALL_CONTRACT;
DomainLocalModule *pLocalModule = NULL;
if (!Module::IsEncodedModuleIndex(moduleDomainID))
pLocalModule = (DomainLocalModule *) moduleDomainID;
else
{
DomainLocalBlock *pLocalBlock = GetAppDomain()->GetDomainLocalBlock();
pLocalModule = pLocalBlock->GetModuleSlot(Module::IDToIndex(moduleDomainID));
}
return (void*)pLocalModule->GetPrecomputedGCStaticsBasePointer();
}
HCIMPLEND
#include <optdefault.h>
// The following two functions can be tail called from platform dependent versions of
// JIT_GetSharedGCStaticBase and JIT_GetShareNonGCStaticBase
HCIMPL2(void*, JIT_GetSharedNonGCStaticBase_Helper, DomainLocalModule *pLocalModule, DWORD dwClassDomainID)
{
FCALL_CONTRACT;
HELPER_METHOD_FRAME_BEGIN_RET_0();
// Obtain Method table
MethodTable * pMT = pLocalModule->GetMethodTableFromClassDomainID(dwClassDomainID);
PREFIX_ASSUME(pMT != NULL);
pMT->CheckRunClassInitThrowing();
HELPER_METHOD_FRAME_END();
return (void*)pLocalModule->GetPrecomputedNonGCStaticsBasePointer();
}
HCIMPLEND
HCIMPL2(void*, JIT_GetSharedGCStaticBase_Helper, DomainLocalModule *pLocalModule, DWORD dwClassDomainID)
{
FCALL_CONTRACT;
HELPER_METHOD_FRAME_BEGIN_RET_0();
// Obtain Method table
MethodTable * pMT = pLocalModule->GetMethodTableFromClassDomainID(dwClassDomainID);
PREFIX_ASSUME(pMT != NULL);
pMT->CheckRunClassInitThrowing();
HELPER_METHOD_FRAME_END();
return (void*)pLocalModule->GetPrecomputedGCStaticsBasePointer();
}
HCIMPLEND
/*********************************************************************/
// Slow helper to tail call from the fast one
HCIMPL2(void*, JIT_GetSharedNonGCStaticBaseDynamicClass_Helper, DomainLocalModule *pLocalModule, DWORD dwDynamicClassDomainID)
{
FCALL_CONTRACT;
void* result = NULL;
HELPER_METHOD_FRAME_BEGIN_RET_0();
MethodTable *pMT = pLocalModule->GetDomainFile()->GetModule()->GetDynamicClassMT(dwDynamicClassDomainID);
_ASSERTE(pMT);
pMT->CheckRunClassInitThrowing();
result = (void*)pLocalModule->GetDynamicEntryNonGCStaticsBasePointer(dwDynamicClassDomainID, pMT->GetLoaderAllocator());
HELPER_METHOD_FRAME_END();
return result;
}
HCIMPLEND
/*************************************************************/
#include <optsmallperfcritical.h>
HCIMPL2(void*, JIT_GetSharedNonGCStaticBaseDynamicClass, SIZE_T moduleDomainID, DWORD dwDynamicClassDomainID)
{
FCALL_CONTRACT;
DomainLocalModule *pLocalModule;
if (!Module::IsEncodedModuleIndex(moduleDomainID))
pLocalModule = (DomainLocalModule *) moduleDomainID;
else
{
DomainLocalBlock *pLocalBlock = GetAppDomain()->GetDomainLocalBlock();
pLocalModule = pLocalBlock->GetModuleSlot(Module::IDToIndex(moduleDomainID));
}
DomainLocalModule::PTR_DynamicClassInfo pLocalInfo = pLocalModule->GetDynamicClassInfoIfInitialized(dwDynamicClassDomainID);
if (pLocalInfo != NULL)
{
PTR_BYTE retval;
GET_DYNAMICENTRY_NONGCSTATICS_BASEPOINTER(pLocalModule->GetDomainFile()->GetModule()->GetLoaderAllocator(),
pLocalInfo,
&retval);
return retval;
}
// Tailcall to the slow helper
ENDFORBIDGC();
return HCCALL2(JIT_GetSharedNonGCStaticBaseDynamicClass_Helper, pLocalModule, dwDynamicClassDomainID);
}
HCIMPLEND
#include <optdefault.h>
/*************************************************************/
// Slow helper to tail call from the fast one
HCIMPL2(void, JIT_ClassInitDynamicClass_Helper, DomainLocalModule *pLocalModule, DWORD dwDynamicClassDomainID)
{
FCALL_CONTRACT;
HELPER_METHOD_FRAME_BEGIN_0();
MethodTable *pMT = pLocalModule->GetDomainFile()->GetModule()->GetDynamicClassMT(dwDynamicClassDomainID);
_ASSERTE(pMT);
pMT->CheckRunClassInitThrowing();
HELPER_METHOD_FRAME_END();
return;
}
HCIMPLEND
#include <optsmallperfcritical.h>
HCIMPL2(void, JIT_ClassInitDynamicClass, SIZE_T moduleDomainID, DWORD dwDynamicClassDomainID)
{
FCALL_CONTRACT;
DomainLocalModule *pLocalModule;
if (!Module::IsEncodedModuleIndex(moduleDomainID))
pLocalModule = (DomainLocalModule *) moduleDomainID;
else
{
DomainLocalBlock *pLocalBlock = GetAppDomain()->GetDomainLocalBlock();
pLocalModule = pLocalBlock->GetModuleSlot(Module::IDToIndex(moduleDomainID));
}
DomainLocalModule::PTR_DynamicClassInfo pLocalInfo = pLocalModule->GetDynamicClassInfoIfInitialized(dwDynamicClassDomainID);
if (pLocalInfo != NULL)
{
return;
}
// Tailcall to the slow helper
ENDFORBIDGC();
return HCCALL2(JIT_ClassInitDynamicClass_Helper, pLocalModule, dwDynamicClassDomainID);
}
HCIMPLEND
#include <optdefault.h>
/*************************************************************/
// Slow helper to tail call from the fast one
HCIMPL2(void*, JIT_GetSharedGCStaticBaseDynamicClass_Helper, DomainLocalModule *pLocalModule, DWORD dwDynamicClassDomainID)
{
FCALL_CONTRACT;
void* result = NULL;
HELPER_METHOD_FRAME_BEGIN_RET_0();
MethodTable *pMT = pLocalModule->GetDomainFile()->GetModule()->GetDynamicClassMT(dwDynamicClassDomainID);
_ASSERTE(pMT);
pMT->CheckRunClassInitThrowing();
result = (void*)pLocalModule->GetDynamicEntryGCStaticsBasePointer(dwDynamicClassDomainID, pMT->GetLoaderAllocator());
HELPER_METHOD_FRAME_END();
return result;
}
HCIMPLEND
/*************************************************************/
#include <optsmallperfcritical.h>
HCIMPL2(void*, JIT_GetSharedGCStaticBaseDynamicClass, SIZE_T moduleDomainID, DWORD dwDynamicClassDomainID)
{
FCALL_CONTRACT;
DomainLocalModule *pLocalModule;
if (!Module::IsEncodedModuleIndex(moduleDomainID))
pLocalModule = (DomainLocalModule *) moduleDomainID;
else
{
DomainLocalBlock *pLocalBlock = GetAppDomain()->GetDomainLocalBlock();
pLocalModule = pLocalBlock->GetModuleSlot(Module::IDToIndex(moduleDomainID));
}
DomainLocalModule::PTR_DynamicClassInfo pLocalInfo = pLocalModule->GetDynamicClassInfoIfInitialized(dwDynamicClassDomainID);
if (pLocalInfo != NULL)
{
PTR_BYTE retval;
GET_DYNAMICENTRY_GCSTATICS_BASEPOINTER(pLocalModule->GetDomainFile()->GetModule()->GetLoaderAllocator(),
pLocalInfo,
&retval);
return retval;
}
// Tailcall to the slow helper
ENDFORBIDGC();
return HCCALL2(JIT_GetSharedGCStaticBaseDynamicClass_Helper, pLocalModule, dwDynamicClassDomainID);
}
HCIMPLEND
#include <optdefault.h>
/*********************************************************************/
// Slow helper to tail call from the fast one
NOINLINE HCIMPL1(void*, JIT_GetGenericsGCStaticBase_Framed, MethodTable *pMT)
{
CONTRACTL {
FCALL_CHECK;
PRECONDITION(CheckPointer(pMT));
PRECONDITION(pMT->HasGenericsStaticsInfo());
} CONTRACTL_END;
void* base = NULL;
HELPER_METHOD_FRAME_BEGIN_RET_0();
pMT->CheckRestore();
pMT->CheckRunClassInitThrowing();
base = (void*) pMT->GetGCStaticsBasePointer();
CONSISTENCY_CHECK(base != NULL);
HELPER_METHOD_FRAME_END();
return base;
}
HCIMPLEND
/*********************************************************************/
#include <optsmallperfcritical.h>
HCIMPL1(void*, JIT_GetGenericsGCStaticBase, MethodTable *pMT)
{
CONTRACTL {
FCALL_CHECK;
PRECONDITION(CheckPointer(pMT));
PRECONDITION(pMT->HasGenericsStaticsInfo());
} CONTRACTL_END;
DWORD dwDynamicClassDomainID;
PTR_Module pModuleForStatics = pMT->GetGenericsStaticsModuleAndID(&dwDynamicClassDomainID);
DomainLocalModule *pLocalModule = pModuleForStatics->GetDomainLocalModule();
_ASSERTE(pLocalModule);
DomainLocalModule::PTR_DynamicClassInfo pLocalInfo = pLocalModule->GetDynamicClassInfoIfInitialized(dwDynamicClassDomainID);
if (pLocalInfo != NULL)
{
PTR_BYTE retval;
GET_DYNAMICENTRY_GCSTATICS_BASEPOINTER(pMT->GetLoaderAllocator(),
pLocalInfo,
&retval);
return retval;
}
// Tailcall to the slow helper
ENDFORBIDGC();
return HCCALL1(JIT_GetGenericsGCStaticBase_Framed, pMT);
}
HCIMPLEND
#include <optdefault.h>
/*********************************************************************/
// Slow helper to tail call from the fast one
NOINLINE HCIMPL1(void*, JIT_GetGenericsNonGCStaticBase_Framed, MethodTable *pMT)
{
CONTRACTL {
FCALL_CHECK;
PRECONDITION(CheckPointer(pMT));
PRECONDITION(pMT->HasGenericsStaticsInfo());
} CONTRACTL_END;
void* base = NULL;
HELPER_METHOD_FRAME_BEGIN_RET_0();
pMT->CheckRestore();
// If pMT refers to a method table that requires some initialization work,
// then pMT cannot to a method table that is shared by generic instantiations,
// because method tables that are shared by generic instantiations do not have
// a base for statics to live in.
_ASSERTE(pMT->IsClassPreInited() || !pMT->IsSharedByGenericInstantiations());
pMT->CheckRunClassInitThrowing();
// We could just return null here instead of returning base when this helper is called just to trigger the cctor
base = (void*) pMT->GetNonGCStaticsBasePointer();
HELPER_METHOD_FRAME_END();
return base;
}
HCIMPLEND
/*********************************************************************/
#include <optsmallperfcritical.h>
HCIMPL1(void*, JIT_GetGenericsNonGCStaticBase, MethodTable *pMT)
{
CONTRACTL {
FCALL_CHECK;
PRECONDITION(CheckPointer(pMT));
PRECONDITION(pMT->HasGenericsStaticsInfo());
} CONTRACTL_END;
// This fast path will typically always be taken once the slow framed path below
// has executed once. Sometimes the slow path will be executed more than once,
// e.g. if static fields are accessed during the call to CheckRunClassInitThrowing()
// in the slow path.
DWORD dwDynamicClassDomainID;
PTR_Module pModuleForStatics = pMT->GetGenericsStaticsModuleAndID(&dwDynamicClassDomainID);
DomainLocalModule *pLocalModule = pModuleForStatics->GetDomainLocalModule();
_ASSERTE(pLocalModule);
DomainLocalModule::PTR_DynamicClassInfo pLocalInfo = pLocalModule->GetDynamicClassInfoIfInitialized(dwDynamicClassDomainID);
if (pLocalInfo != NULL)
{
PTR_BYTE retval;
GET_DYNAMICENTRY_NONGCSTATICS_BASEPOINTER(pMT->GetLoaderAllocator(),
pLocalInfo,
&retval);
return retval;
}
// Tailcall to the slow helper
ENDFORBIDGC();
return HCCALL1(JIT_GetGenericsNonGCStaticBase_Framed, pMT);
}
HCIMPLEND
#include <optdefault.h>
//========================================================================
//
// THREAD STATIC FIELD HELPERS
//
//========================================================================
// *** These framed helpers get called if allocation needs to occur or
// if the class constructor needs to run
HCIMPL1(void*, JIT_GetNonGCThreadStaticBase_Helper, MethodTable * pMT)
{
CONTRACTL {
FCALL_CHECK;
PRECONDITION(CheckPointer(pMT));
} CONTRACTL_END;
void* base = NULL;
HELPER_METHOD_FRAME_BEGIN_RET_0();
// For generics, we need to call CheckRestore() for some reason
if (pMT->HasGenericsStaticsInfo())
pMT->CheckRestore();
// Get the TLM
ThreadLocalModule * pThreadLocalModule = ThreadStatics::GetTLM(pMT);
_ASSERTE(pThreadLocalModule != NULL);
// Check if the class constructor needs to be run
pThreadLocalModule->CheckRunClassInitThrowing(pMT);
// Lookup the non-GC statics base pointer
base = (void*) pMT->GetNonGCThreadStaticsBasePointer();
CONSISTENCY_CHECK(base != NULL);
HELPER_METHOD_FRAME_END();
return base;
}
HCIMPLEND
HCIMPL1(void*, JIT_GetGCThreadStaticBase_Helper, MethodTable * pMT)
{
CONTRACTL {
FCALL_CHECK;
PRECONDITION(CheckPointer(pMT));
} CONTRACTL_END;
void* base = NULL;
HELPER_METHOD_FRAME_BEGIN_RET_0();
// For generics, we need to call CheckRestore() for some reason
if (pMT->HasGenericsStaticsInfo())
pMT->CheckRestore();
// Get the TLM
ThreadLocalModule * pThreadLocalModule = ThreadStatics::GetTLM(pMT);
_ASSERTE(pThreadLocalModule != NULL);
// Check if the class constructor needs to be run
pThreadLocalModule->CheckRunClassInitThrowing(pMT);
// Lookup the GC statics base pointer
base = (void*) pMT->GetGCThreadStaticsBasePointer();
CONSISTENCY_CHECK(base != NULL);
HELPER_METHOD_FRAME_END();
return base;
}
HCIMPLEND
// *** This helper corresponds to both CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE and
// CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE_NOCTOR. Even though we always check
// if the class constructor has been run, we have a separate helper ID for the "no ctor"
// version because it allows the JIT to do some reordering that otherwise wouldn't be
// possible.
#include <optsmallperfcritical.h>
HCIMPL2(void*, JIT_GetSharedNonGCThreadStaticBase, SIZE_T moduleDomainID, DWORD dwClassDomainID)
{
FCALL_CONTRACT;
// Get the ModuleIndex
ModuleIndex index =
(Module::IsEncodedModuleIndex(moduleDomainID)) ?
Module::IDToIndex(moduleDomainID) :
((DomainLocalModule *)moduleDomainID)->GetModuleIndex();
// Get the relevant ThreadLocalModule
ThreadLocalModule * pThreadLocalModule = ThreadStatics::GetTLMIfExists(index);
// If the TLM has been allocated and the class has been marked as initialized,
// get the pointer to the non-GC statics base and return
if (pThreadLocalModule != NULL && pThreadLocalModule->IsPrecomputedClassInitialized(dwClassDomainID))
return (void*)pThreadLocalModule->GetPrecomputedNonGCStaticsBasePointer();
// If the TLM was not allocated or if the class was not marked as initialized
// then we have to go through the slow path
// Get the DomainLocalModule
DomainLocalModule *pDomainLocalModule =
(Module::IsEncodedModuleIndex(moduleDomainID)) ?
GetAppDomain()->GetDomainLocalBlock()->GetModuleSlot(Module::IDToIndex(moduleDomainID)) :
(DomainLocalModule *) moduleDomainID;
// Obtain the MethodTable
MethodTable * pMT = pDomainLocalModule->GetMethodTableFromClassDomainID(dwClassDomainID);
_ASSERTE(!pMT->HasGenericsStaticsInfo());
ENDFORBIDGC();
return HCCALL1(JIT_GetNonGCThreadStaticBase_Helper, pMT);
}
HCIMPLEND
#include <optdefault.h>
// *** This helper corresponds to both CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE and
// CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE_NOCTOR. Even though we always check
// if the class constructor has been run, we have a separate helper ID for the "no ctor"
// version because it allows the JIT to do some reordering that otherwise wouldn't be
// possible.
#include <optsmallperfcritical.h>
HCIMPL2(void*, JIT_GetSharedGCThreadStaticBase, SIZE_T moduleDomainID, DWORD dwClassDomainID)
{
FCALL_CONTRACT;
// Get the ModuleIndex
ModuleIndex index =
(Module::IsEncodedModuleIndex(moduleDomainID)) ?
Module::IDToIndex(moduleDomainID) :
((DomainLocalModule *)moduleDomainID)->GetModuleIndex();
// Get the relevant ThreadLocalModule
ThreadLocalModule * pThreadLocalModule = ThreadStatics::GetTLMIfExists(index);
// If the TLM has been allocated and the class has been marked as initialized,
// get the pointer to the GC statics base and return
if (pThreadLocalModule != NULL && pThreadLocalModule->IsPrecomputedClassInitialized(dwClassDomainID))
return (void*)pThreadLocalModule->GetPrecomputedGCStaticsBasePointer();
// If the TLM was not allocated or if the class was not marked as initialized
// then we have to go through the slow path
// Get the DomainLocalModule
DomainLocalModule *pDomainLocalModule =
(Module::IsEncodedModuleIndex(moduleDomainID)) ?
GetAppDomain()->GetDomainLocalBlock()->GetModuleSlot(Module::IDToIndex(moduleDomainID)) :
(DomainLocalModule *) moduleDomainID;
// Obtain the MethodTable
MethodTable * pMT = pDomainLocalModule->GetMethodTableFromClassDomainID(dwClassDomainID);
_ASSERTE(!pMT->HasGenericsStaticsInfo());
ENDFORBIDGC();
return HCCALL1(JIT_GetGCThreadStaticBase_Helper, pMT);
}
HCIMPLEND
#include <optdefault.h>
// *** This helper corresponds to CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE_DYNAMICCLASS
#include <optsmallperfcritical.h>
HCIMPL2(void*, JIT_GetSharedNonGCThreadStaticBaseDynamicClass, SIZE_T moduleDomainID, DWORD dwDynamicClassDomainID)
{
FCALL_CONTRACT;
// Obtain the DomainLocalModule
DomainLocalModule *pDomainLocalModule =
(Module::IsEncodedModuleIndex(moduleDomainID)) ?
GetAppDomain()->GetDomainLocalBlock()->GetModuleSlot(Module::IDToIndex(moduleDomainID)) :
(DomainLocalModule *)moduleDomainID;
// Get the ModuleIndex
ModuleIndex index = pDomainLocalModule->GetModuleIndex();
// Get the relevant ThreadLocalModule
ThreadLocalModule * pThreadLocalModule = ThreadStatics::GetTLMIfExists(index);
// If the TLM has been allocated and the class has been marked as initialized,
// get the pointer to the non-GC statics base and return
if (pThreadLocalModule != NULL)
{
ThreadLocalModule::PTR_DynamicClassInfo pLocalInfo = pThreadLocalModule->GetDynamicClassInfoIfInitialized(dwDynamicClassDomainID);
if (pLocalInfo != NULL)
{
PTR_BYTE retval;
GET_DYNAMICENTRY_NONGCTHREADSTATICS_BASEPOINTER(pDomainLocalModule->GetDomainFile()->GetModule()->GetLoaderAllocator(),
pLocalInfo,
&retval);
return retval;
}
}
// If the TLM was not allocated or if the class was not marked as initialized
// then we have to go through the slow path
// Obtain the Module
Module * pModule = pDomainLocalModule->GetDomainFile()->GetModule();
// Obtain the MethodTable
MethodTable * pMT = pModule->GetDynamicClassMT(dwDynamicClassDomainID);
_ASSERTE(pMT != NULL);
_ASSERTE(!pMT->IsSharedByGenericInstantiations());
// Tailcall to the slow helper
ENDFORBIDGC();
return HCCALL1(JIT_GetNonGCThreadStaticBase_Helper, pMT);
}
HCIMPLEND
#include <optdefault.h>
// *** This helper corresponds to CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE_DYNAMICCLASS
#include <optsmallperfcritical.h>
HCIMPL2(void*, JIT_GetSharedGCThreadStaticBaseDynamicClass, SIZE_T moduleDomainID, DWORD dwDynamicClassDomainID)
{
FCALL_CONTRACT;
// Obtain the DomainLocalModule
DomainLocalModule *pDomainLocalModule =
(Module::IsEncodedModuleIndex(moduleDomainID)) ?
GetAppDomain()->GetDomainLocalBlock()->GetModuleSlot(Module::IDToIndex(moduleDomainID)) :
(DomainLocalModule *)moduleDomainID;
// Get the ModuleIndex
ModuleIndex index = pDomainLocalModule->GetModuleIndex();
// Get the relevant ThreadLocalModule
ThreadLocalModule * pThreadLocalModule = ThreadStatics::GetTLMIfExists(index);
// If the TLM has been allocated and the class has been marked as initialized,
// get the pointer to the GC statics base and return
if (pThreadLocalModule != NULL)
{
ThreadLocalModule::PTR_DynamicClassInfo pLocalInfo = pThreadLocalModule->GetDynamicClassInfoIfInitialized(dwDynamicClassDomainID);
if (pLocalInfo != NULL)
{
PTR_BYTE retval;
GET_DYNAMICENTRY_GCTHREADSTATICS_BASEPOINTER(pDomainLocalModule->GetDomainFile()->GetModule()->GetLoaderAllocator(),
pLocalInfo,
&retval);
return retval;
}
}
// If the TLM was not allocated or if the class was not marked as initialized
// then we have to go through the slow path
// Obtain the Module
Module * pModule = pDomainLocalModule->GetDomainFile()->GetModule();
// Obtain the MethodTable
MethodTable * pMT = pModule->GetDynamicClassMT(dwDynamicClassDomainID);
_ASSERTE(pMT != NULL);
_ASSERTE(!pMT->IsSharedByGenericInstantiations());
// Tailcall to the slow helper
ENDFORBIDGC();
return HCCALL1(JIT_GetGCThreadStaticBase_Helper, pMT);
}
HCIMPLEND
#include <optdefault.h>
// *** This helper corresponds to CORINFO_HELP_GETGENERICS_NONGCTHREADSTATIC_BASE
#include <optsmallperfcritical.h>
HCIMPL1(void*, JIT_GetGenericsNonGCThreadStaticBase, MethodTable *pMT)
{
CONTRACTL {
FCALL_CHECK;
PRECONDITION(CheckPointer(pMT));
PRECONDITION(pMT->HasGenericsStaticsInfo());
} CONTRACTL_END;
// This fast path will typically always be taken once the slow framed path below
// has executed once. Sometimes the slow path will be executed more than once,
// e.g. if static fields are accessed during the call to CheckRunClassInitThrowing()
// in the slow path.
// Get the Module and dynamic class ID
DWORD dwDynamicClassDomainID;
PTR_Module pModule = pMT->GetGenericsStaticsModuleAndID(&dwDynamicClassDomainID);
// Get ModuleIndex
ModuleIndex index = pModule->GetModuleIndex();
// Get the relevant ThreadLocalModule
ThreadLocalModule * pThreadLocalModule = ThreadStatics::GetTLMIfExists(index);
// If the TLM has been allocated and the class has been marked as initialized,
// get the pointer to the non-GC statics base and return
if (pThreadLocalModule != NULL)
{
ThreadLocalModule::PTR_DynamicClassInfo pLocalInfo = pThreadLocalModule->GetDynamicClassInfoIfInitialized(dwDynamicClassDomainID);
if (pLocalInfo != NULL)
{
PTR_BYTE retval;
GET_DYNAMICENTRY_NONGCSTATICS_BASEPOINTER(pMT->GetLoaderAllocator(),
pLocalInfo,
&retval);
return retval;
}
}
// If the TLM was not allocated or if the class was not marked as initialized
// then we have to go through the slow path
// Tailcall to the slow helper
ENDFORBIDGC();
return HCCALL1(JIT_GetNonGCThreadStaticBase_Helper, pMT);
}
HCIMPLEND
#include <optdefault.h>
// *** This helper corresponds to CORINFO_HELP_GETGENERICS_GCTHREADSTATIC_BASE
#include <optsmallperfcritical.h>
HCIMPL1(void*, JIT_GetGenericsGCThreadStaticBase, MethodTable *pMT)
{
CONTRACTL {
FCALL_CHECK;
PRECONDITION(CheckPointer(pMT));
PRECONDITION(pMT->HasGenericsStaticsInfo());
} CONTRACTL_END;
// This fast path will typically always be taken once the slow framed path below
// has executed once. Sometimes the slow path will be executed more than once,
// e.g. if static fields are accessed during the call to CheckRunClassInitThrowing()
// in the slow path.
// Get the Module and dynamic class ID
DWORD dwDynamicClassDomainID;
PTR_Module pModule = pMT->GetGenericsStaticsModuleAndID(&dwDynamicClassDomainID);
// Get ModuleIndex
ModuleIndex index = pModule->GetModuleIndex();
// Get the relevant ThreadLocalModule
ThreadLocalModule * pThreadLocalModule = ThreadStatics::GetTLMIfExists(index);
// If the TLM has been allocated and the class has been marked as initialized,
// get the pointer to the GC statics base and return
if (pThreadLocalModule != NULL)
{
ThreadLocalModule::PTR_DynamicClassInfo pLocalInfo = pThreadLocalModule->GetDynamicClassInfoIfInitialized(dwDynamicClassDomainID);
if (pLocalInfo != NULL)
{
PTR_BYTE retval;
GET_DYNAMICENTRY_GCTHREADSTATICS_BASEPOINTER(pMT->GetLoaderAllocator(),
pLocalInfo,
&retval);
return retval;
}
}
// If the TLM was not allocated or if the class was not marked as initialized
// then we have to go through the slow path
// Tailcall to the slow helper
ENDFORBIDGC();
return HCCALL1(JIT_GetGCThreadStaticBase_Helper, pMT);
}
HCIMPLEND
#include <optdefault.h>
//========================================================================
//
// STATIC FIELD DYNAMIC HELPERS
//
//========================================================================
#include <optsmallperfcritical.h>
HCIMPL1_RAW(TADDR, JIT_StaticFieldAddress_Dynamic, StaticFieldAddressArgs * pArgs)
{
FCALL_CONTRACT;
TADDR base = HCCALL2(pArgs->staticBaseHelper, pArgs->arg0, pArgs->arg1);
return base + pArgs->offset;
}
HCIMPLEND_RAW
#include <optdefault.h>
#include <optsmallperfcritical.h>
HCIMPL1_RAW(TADDR, JIT_StaticFieldAddressUnbox_Dynamic, StaticFieldAddressArgs * pArgs)
{
FCALL_CONTRACT;
TADDR base = HCCALL2(pArgs->staticBaseHelper, pArgs->arg0, pArgs->arg1);
return *(TADDR *)(base + pArgs->offset) + Object::GetOffsetOfFirstField();
}
HCIMPLEND_RAW
#include <optdefault.h>
//========================================================================
//
// CASTING HELPERS
//
//========================================================================
// pObject MUST be an instance of an array.
TypeHandle::CastResult ArrayIsInstanceOfNoGC(Object *pObject, TypeHandle toTypeHnd)
{
CONTRACTL {
NOTHROW;
GC_NOTRIGGER;
MODE_COOPERATIVE;
SO_TOLERANT;
PRECONDITION(CheckPointer(pObject));
PRECONDITION(pObject->GetMethodTable()->IsArray());
PRECONDITION(toTypeHnd.IsArray());
} CONTRACTL_END;
ArrayBase *pArray = (ArrayBase*) pObject;
ArrayTypeDesc *toArrayType = toTypeHnd.AsArray();
// GetRank touches EEClass. Try to avoid it for SZArrays.
if (toArrayType->GetInternalCorElementType() == ELEMENT_TYPE_SZARRAY)
{
if (pArray->GetMethodTable()->IsMultiDimArray())
return TypeHandle::CannotCast;
}
else
{
if (pArray->GetRank() != toArrayType->GetRank())
return TypeHandle::CannotCast;
}
_ASSERTE(pArray->GetRank() == toArrayType->GetRank());
// ArrayBase::GetTypeHandle consults the loader tables to find the
// exact type handle for an array object. This can be disproportionately slow - but after
// all, why should we need to go looking up hash tables just to do a cast test?
//
// Thus we can always special-case the casting logic to avoid fetching this
// exact type handle. Here we have only done so for one
// particular case, i.e. when we are trying to cast to an array type where
// there is an exact match between the rank, kind and element type of the two
// array types. This happens when, for example, assigning an int32[] into an int32[][].
//
TypeHandle elementTypeHandle = pArray->GetArrayElementTypeHandle();
TypeHandle toElementTypeHandle = toArrayType->GetArrayElementTypeHandle();
if (elementTypeHandle == toElementTypeHandle)
return TypeHandle::CanCast;
// By this point we know that toArrayType->GetInternalCorElementType matches the element type of the Array object
// so we can use a faster constructor to create the TypeDesc. (It so happens that ArrayTypeDescs derives from ParamTypeDesc
// and can be created as identical in a slightly faster way with the following set of parameters.)
ParamTypeDesc arrayType(toArrayType->GetInternalCorElementType(), pArray->GetMethodTable(), elementTypeHandle);
return arrayType.CanCastToNoGC(toTypeHnd);
}
// pObject MUST be an instance of an array.
TypeHandle::CastResult ArrayObjSupportsBizarreInterfaceNoGC(Object *pObject, MethodTable * pInterfaceMT)
{
CONTRACTL {
NOTHROW;
GC_NOTRIGGER;
MODE_COOPERATIVE;
SO_TOLERANT;
PRECONDITION(CheckPointer(pObject));
PRECONDITION(pObject->GetMethodTable()->IsArray());
PRECONDITION(pInterfaceMT->IsInterface());
} CONTRACTL_END;
ArrayBase *pArray = (ArrayBase*) pObject;
// IList<T> & IReadOnlyList<T> only supported for SZ_ARRAYS
if (pArray->GetMethodTable()->IsMultiDimArray())
return TypeHandle::CannotCast;
if (pInterfaceMT->GetLoadLevel() < CLASS_DEPENDENCIES_LOADED)
{
if (!pInterfaceMT->HasInstantiation())
return TypeHandle::CannotCast;
// The slow path will take care of restoring the interface
return TypeHandle::MaybeCast;
}
if (!IsImplicitInterfaceOfSZArray(pInterfaceMT))
return TypeHandle::CannotCast;
return TypeDesc::CanCastParamNoGC(pArray->GetArrayElementTypeHandle(), pInterfaceMT->GetInstantiation()[0]);
}
TypeHandle::CastResult STDCALL ObjIsInstanceOfNoGC(Object *pObject, TypeHandle toTypeHnd)
{
CONTRACTL {
NOTHROW;
GC_NOTRIGGER;
MODE_COOPERATIVE;
SO_TOLERANT;
PRECONDITION(CheckPointer(pObject));
} CONTRACTL_END;
MethodTable *pMT = pObject->GetMethodTable();
// Quick exact match first
if (TypeHandle(pMT) == toTypeHnd)
return TypeHandle::CanCast;
if ((toTypeHnd.IsInterface() && ( pMT->IsComObjectType() || pMT->IsICastable())))
{
return TypeHandle::MaybeCast;
}
if (pMT->IsArray())
{
if (toTypeHnd.IsArray())
return ArrayIsInstanceOfNoGC(pObject, toTypeHnd);
if (toTypeHnd.IsInterface())
{
MethodTable * pInterfaceMT = toTypeHnd.AsMethodTable();
if (pInterfaceMT->HasInstantiation())
return ArrayObjSupportsBizarreInterfaceNoGC(pObject, pInterfaceMT);
return pMT->ImplementsInterface(pInterfaceMT) ? TypeHandle::CanCast : TypeHandle::CannotCast;
}
if (toTypeHnd == TypeHandle(g_pObjectClass) || toTypeHnd == TypeHandle(g_pArrayClass))
return TypeHandle::CanCast;
return TypeHandle::CannotCast;
}
if (toTypeHnd.IsTypeDesc())
return TypeHandle::CannotCast;
// allow an object of type T to be cast to Nullable<T> (they have the same representation)
if (Nullable::IsNullableForTypeNoGC(toTypeHnd, pMT))
return TypeHandle::CanCast;
return pMT->CanCastToClassOrInterfaceNoGC(toTypeHnd.AsMethodTable());
}
BOOL ObjIsInstanceOf(Object *pObject, TypeHandle toTypeHnd, BOOL throwCastException)
{
CONTRACTL {
THROWS;
GC_TRIGGERS;
MODE_COOPERATIVE;
PRECONDITION(CheckPointer(pObject));
} CONTRACTL_END;
BOOL fCast = FALSE;
OBJECTREF obj = ObjectToOBJECTREF(pObject);
GCPROTECT_BEGIN(obj);
TypeHandle fromTypeHnd = obj->GetTypeHandle();
// If we are trying to cast a proxy we need to delegate to remoting
// services which will determine whether the proxy and the type are compatible.
// Start by doing a quick static cast check to see if the type information captured in
// the metadata indicates that the cast is legal.
if (fromTypeHnd.CanCastTo(toTypeHnd))
{
fCast = TRUE;
}
else
#ifdef FEATURE_COMINTEROP
// If we are casting a COM object from interface then we need to do a check to see
// if it implements the interface.
if (toTypeHnd.IsInterface() && fromTypeHnd.GetMethodTable()->IsComObjectType())
{
fCast = ComObject::SupportsInterface(obj, toTypeHnd.AsMethodTable());
}
else
#endif // FEATURE_COMINTEROP
if (Nullable::IsNullableForType(toTypeHnd, obj->GetMethodTable()))
{
// allow an object of type T to be cast to Nullable<T> (they have the same representation)
fCast = TRUE;
}
#ifdef FEATURE_ICASTABLE
// If type implements ICastable interface we give it a chance to tell us if it can be casted
// to a given type.
else if (toTypeHnd.IsInterface() && fromTypeHnd.GetMethodTable()->IsICastable())
{
// Make actuall call to ICastableHelpers.IsInstanceOfInterface(obj, interfaceTypeObj, out exception)
OBJECTREF exception = NULL;
GCPROTECT_BEGIN(exception);
PREPARE_NONVIRTUAL_CALLSITE(METHOD__ICASTABLEHELPERS__ISINSTANCEOF);
OBJECTREF managedType = toTypeHnd.GetManagedClassObject(); //GC triggers
DECLARE_ARGHOLDER_ARRAY(args, 3);
args[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER(obj);
args[ARGNUM_1] = OBJECTREF_TO_ARGHOLDER(managedType);
args[ARGNUM_2] = PTR_TO_ARGHOLDER(&exception);
CALL_MANAGED_METHOD(fCast, BOOL, args);
INDEBUG(managedType = NULL); // managedType isn't protected during the call
if (!fCast && throwCastException && exception != NULL)
{
RealCOMPlusThrow(exception);
}
GCPROTECT_END(); //exception
}
#endif // FEATURE_ICASTABLE
if (!fCast && throwCastException)
{
COMPlusThrowInvalidCastException(&obj, toTypeHnd);
}
GCPROTECT_END(); // obj
return(fCast);
}
//
// This optimization is intended for all non-framed casting helpers
//
#include <optsmallperfcritical.h>
HCIMPL2(Object*, JIT_ChkCastClass_Portable, MethodTable* pTargetMT, Object* pObject)
{
FCALL_CONTRACT;
//
// casts pObject to type pMT
//
if (NULL == pObject)
{
return NULL;
}
PTR_VOID pMT = pObject->GetMethodTable();
do {
if (pMT == pTargetMT)
return pObject;
pMT = MethodTable::GetParentMethodTableOrIndirection(pMT);
} while (pMT);
ENDFORBIDGC();
return HCCALL2(JITutil_ChkCastAny, CORINFO_CLASS_HANDLE(pTargetMT), pObject);
}
HCIMPLEND
//
// This helper assumes that the check for the trivial cases has been inlined by the JIT.
//
HCIMPL2(Object*, JIT_ChkCastClassSpecial_Portable, MethodTable* pTargetMT, Object* pObject)
{
CONTRACTL {
FCALL_CHECK;
// This assumes that the check for the trivial cases has been inlined by the JIT.
PRECONDITION(pObject != NULL);
PRECONDITION(pObject->GetMethodTable() != pTargetMT);
} CONTRACTL_END;
PTR_VOID pMT = MethodTable::GetParentMethodTableOrIndirection(pObject->GetMethodTable());
while (pMT)
{
if (pMT == pTargetMT)
return pObject;
pMT = MethodTable::GetParentMethodTableOrIndirection(pMT);
}
ENDFORBIDGC();
return HCCALL2(JITutil_ChkCastAny, CORINFO_CLASS_HANDLE(pTargetMT), pObject);
}
HCIMPLEND
HCIMPL2(Object*, JIT_IsInstanceOfClass_Portable, MethodTable* pTargetMT, Object* pObject)
{
FCALL_CONTRACT;
//
// casts pObject to type pMT
//
if (NULL == pObject)
{
return NULL;
}
PTR_VOID pMT = pObject->GetMethodTable();
do {
if (pMT == pTargetMT)
return pObject;
pMT = MethodTable::GetParentMethodTableOrIndirection(pMT);
} while (pMT);
if (!pObject->GetMethodTable()->HasTypeEquivalence())
{
return NULL;
}
ENDFORBIDGC();
return HCCALL2(JITutil_IsInstanceOfAny, CORINFO_CLASS_HANDLE(pTargetMT), pObject);
}
HCIMPLEND
HCIMPL2(Object*, JIT_ChkCastInterface_Portable, MethodTable *pInterfaceMT, Object* pObject)
{
CONTRACTL {
FCALL_CHECK;
PRECONDITION(pInterfaceMT->IsInterface());
} CONTRACTL_END;
if (NULL == pObject)
{
return pObject;
}
if (pObject->GetMethodTable()->ImplementsInterfaceInline(pInterfaceMT))
{
return pObject;
}
ENDFORBIDGC();
return HCCALL2(JITutil_ChkCastInterface, pInterfaceMT, pObject);
}
HCIMPLEND
HCIMPL2(Object*, JIT_IsInstanceOfInterface_Portable, MethodTable *pInterfaceMT, Object* pObject)
{
CONTRACTL {
FCALL_CHECK;
PRECONDITION(pInterfaceMT->IsInterface());
} CONTRACTL_END;
if (NULL == pObject)
{
return NULL;
}
if (pObject->GetMethodTable()->ImplementsInterfaceInline(pInterfaceMT))
{
return pObject;
}
if (!pObject->GetMethodTable()->InstanceRequiresNonTrivialInterfaceCast())
{
return NULL;
}
ENDFORBIDGC();
return HCCALL2(JITutil_IsInstanceOfInterface, pInterfaceMT, pObject);
}
HCIMPLEND
HCIMPL2(Object *, JIT_ChkCastArray, CORINFO_CLASS_HANDLE type, Object *pObject)
{
CONTRACTL {
FCALL_CHECK;
PRECONDITION(TypeHandle(type).IsArray());
} CONTRACTL_END;
if (pObject == NULL)
{
return NULL;
}
OBJECTREF refObj = ObjectToOBJECTREF(pObject);
VALIDATEOBJECTREF(refObj);
TypeHandle::CastResult result = refObj->GetMethodTable()->IsArray() ?
ArrayIsInstanceOfNoGC(pObject, TypeHandle(type)) : TypeHandle::CannotCast;
if (result == TypeHandle::CanCast)
{
return pObject;
}
ENDFORBIDGC();
Object* pRet = HCCALL2(JITutil_ChkCastAny, type, pObject);
// Make sure that the fast helper have not lied
_ASSERTE(result != TypeHandle::CannotCast);
return pRet;
}
HCIMPLEND
HCIMPL2(Object *, JIT_IsInstanceOfArray, CORINFO_CLASS_HANDLE type, Object *pObject)
{
CONTRACTL {
FCALL_CHECK;
PRECONDITION(TypeHandle(type).IsArray());
} CONTRACTL_END;
if (pObject == NULL)
{
return NULL;
}
OBJECTREF refObj = ObjectToOBJECTREF(pObject);
VALIDATEOBJECTREF(refObj);
MethodTable *pMT = refObj->GetMethodTable();
if (!pMT->IsArray())
{
// We know that the clsHnd is an array so check the object. If it is not an array return null
return NULL;
}
else
{
switch (ArrayIsInstanceOfNoGC(pObject, TypeHandle(type))) {
case TypeHandle::CanCast:
return pObject;
case TypeHandle::CannotCast:
return NULL;
default:
// fall through to the slow helper
break;
}
}
ENDFORBIDGC();
return HCCALL2(JITutil_IsInstanceOfAny, type, pObject);
}
HCIMPLEND
/*********************************************************************/
// IsInstanceOf test used for unusual cases (naked type parameters, variant generic types)
// Unlike the IsInstanceOfInterface, IsInstanceOfClass, and IsIsntanceofArray functions,
// this test must deal with all kinds of type tests
HCIMPL2(Object *, JIT_IsInstanceOfAny, CORINFO_CLASS_HANDLE type, Object* obj)
{
FCALL_CONTRACT;
if (NULL == obj)
{
return NULL;
}
switch (ObjIsInstanceOfNoGC(obj, TypeHandle(type))) {
case TypeHandle::CanCast:
return obj;
case TypeHandle::CannotCast:
return NULL;
default:
// fall through to the slow helper
break;
}
ENDFORBIDGC();
return HCCALL2(JITutil_IsInstanceOfAny, type, obj);
}
HCIMPLEND
// ChkCast test used for unusual cases (naked type parameters, variant generic types)
// Unlike the ChkCastInterface, ChkCastClass, and ChkCastArray functions,
// this test must deal with all kinds of type tests
HCIMPL2(Object *, JIT_ChkCastAny, CORINFO_CLASS_HANDLE type, Object *obj)
{
FCALL_CONTRACT;
if (NULL == obj)
{
return NULL;
}
TypeHandle::CastResult result = ObjIsInstanceOfNoGC(obj, TypeHandle(type));
if (result == TypeHandle::CanCast)
{
return obj;
}
ENDFORBIDGC();
Object* pRet = HCCALL2(JITutil_ChkCastAny, type, obj);
// Make sure that the fast helper have not lied
_ASSERTE(result != TypeHandle::CannotCast);
return pRet;
}
HCIMPLEND
NOINLINE HCIMPL2(Object *, JITutil_IsInstanceOfInterface, MethodTable *pInterfaceMT, Object* obj)
{
FCALL_CONTRACT;
if (obj->GetMethodTable()->IsArray())
{
switch (ArrayObjSupportsBizarreInterfaceNoGC(obj, pInterfaceMT)) {
case TypeHandle::CanCast:
return obj;
case TypeHandle::CannotCast:
return NULL;
default:
// fall through to the slow helper
break;
}
}
ENDFORBIDGC();
return HCCALL2(JITutil_IsInstanceOfAny, CORINFO_CLASS_HANDLE(pInterfaceMT), obj);
}
HCIMPLEND
NOINLINE HCIMPL2(Object *, JITutil_ChkCastInterface, MethodTable *pInterfaceMT, Object *obj)
{
FCALL_CONTRACT;
if (obj->GetMethodTable()->IsArray())
{
if (ArrayObjSupportsBizarreInterfaceNoGC(obj, pInterfaceMT) == TypeHandle::CanCast)
{
return obj;
}
}
ENDFORBIDGC();
return HCCALL2(JITutil_ChkCastAny, CORINFO_CLASS_HANDLE(pInterfaceMT), obj);
}
HCIMPLEND
#include <optdefault.h>
//
// Framed helpers
//
NOINLINE HCIMPL2(Object *, JITutil_ChkCastAny, CORINFO_CLASS_HANDLE type, Object *obj)
{
FCALL_CONTRACT;
// This case should be handled by frameless helper
_ASSERTE(obj != NULL);
OBJECTREF oref = ObjectToOBJECTREF (obj);
VALIDATEOBJECTREF(oref);
TypeHandle clsHnd(type);
HELPER_METHOD_FRAME_BEGIN_RET_1(oref);
if (!ObjIsInstanceOf(OBJECTREFToObject(oref), clsHnd, TRUE))
{
UNREACHABLE(); //ObjIsInstanceOf will throw if cast can't be done
}
HELPER_METHOD_FRAME_END();
return OBJECTREFToObject(oref);
}
HCIMPLEND
NOINLINE HCIMPL2(Object *, JITutil_IsInstanceOfAny, CORINFO_CLASS_HANDLE type, Object *obj)
{
FCALL_CONTRACT;
// This case should be handled by frameless helper
_ASSERTE(obj != NULL);
OBJECTREF oref = ObjectToOBJECTREF (obj);
VALIDATEOBJECTREF(oref);
TypeHandle clsHnd(type);
HELPER_METHOD_FRAME_BEGIN_RET_1(oref);
if (!ObjIsInstanceOf(OBJECTREFToObject(oref), clsHnd))
oref = NULL;
HELPER_METHOD_FRAME_END();
return OBJECTREFToObject(oref);
}
HCIMPLEND
//========================================================================
//
// ALLOCATION HELPERS
//
//========================================================================
#include <optsmallperfcritical.h>
//*************************************************************
// Allocation fast path for typical objects
//
HCIMPL1(Object*, JIT_NewS_MP_FastPortable, CORINFO_CLASS_HANDLE typeHnd_)
{
FCALL_CONTRACT;
do
{
_ASSERTE(GCHeapUtilities::UseThreadAllocationContexts());
// This is typically the only call in the fast path. Making the call early seems to be better, as it allows the compiler
// to use volatile registers for intermediate values. This reduces the number of push/pop instructions and eliminates
// some reshuffling of intermediate values into nonvolatile registers around the call.
Thread *thread = GetThread();
TypeHandle typeHandle(typeHnd_);
_ASSERTE(!typeHandle.IsTypeDesc());
MethodTable *methodTable = typeHandle.AsMethodTable();
SIZE_T size = methodTable->GetBaseSize();
_ASSERTE(size % DATA_ALIGNMENT == 0);
gc_alloc_context *allocContext = thread->GetAllocContext();
BYTE *allocPtr = allocContext->alloc_ptr;
_ASSERTE(allocPtr <= allocContext->alloc_limit);
if (size > static_cast<SIZE_T>(allocContext->alloc_limit - allocPtr))
{
break;
}
allocContext->alloc_ptr = allocPtr + size;
_ASSERTE(allocPtr != nullptr);
Object *object = reinterpret_cast<Object *>(allocPtr);
_ASSERTE(object->HasEmptySyncBlockInfo());
object->SetMethodTable(methodTable);
return object;
} while (false);
// Tail call to the slow helper
ENDFORBIDGC();
return HCCALL1(JIT_New, typeHnd_);
}
HCIMPLEND
#include <optdefault.h>
/*************************************************************/
HCIMPL1(Object*, JIT_New, CORINFO_CLASS_HANDLE typeHnd_)
{
FCALL_CONTRACT;
OBJECTREF newobj = NULL;
HELPER_METHOD_FRAME_BEGIN_RET_0(); // Set up a frame
TypeHandle typeHnd(typeHnd_);
_ASSERTE(!typeHnd.IsTypeDesc()); // we never use this helper for arrays
MethodTable *pMT = typeHnd.AsMethodTable();
_ASSERTE(pMT->IsRestored_NoLogging());
#ifdef _DEBUG
if (g_pConfig->FastGCStressLevel()) {
GetThread()->DisableStressHeap();
}
#endif // _DEBUG
newobj = AllocateObject(pMT);
HELPER_METHOD_FRAME_END();
return(OBJECTREFToObject(newobj));
}
HCIMPLEND
//========================================================================
//
// STRING HELPERS
//
//========================================================================
#include <optsmallperfcritical.h>
//*************************************************************
// Allocation fast path for typical objects
//
HCIMPL1(StringObject*, AllocateString_MP_FastPortable, DWORD stringLength)
{
FCALL_CONTRACT;
do
{
_ASSERTE(GCHeapUtilities::UseThreadAllocationContexts());
// Instead of doing elaborate overflow checks, we just limit the number of elements. This will avoid all overflow
// problems, as well as making sure big string objects are correctly allocated in the big object heap.
if (stringLength >= (LARGE_OBJECT_SIZE - 256) / sizeof(WCHAR))
{
break;
}
// This is typically the only call in the fast path. Making the call early seems to be better, as it allows the compiler
// to use volatile registers for intermediate values. This reduces the number of push/pop instructions and eliminates
// some reshuffling of intermediate values into nonvolatile registers around the call.
Thread *thread = GetThread();
SIZE_T totalSize = StringObject::GetSize(stringLength);
// The method table's base size includes space for a terminating null character
_ASSERTE(totalSize >= g_pStringClass->GetBaseSize());
_ASSERTE((totalSize - g_pStringClass->GetBaseSize()) / sizeof(WCHAR) == stringLength);
SIZE_T alignedTotalSize = ALIGN_UP(totalSize, DATA_ALIGNMENT);
_ASSERTE(alignedTotalSize >= totalSize);
totalSize = alignedTotalSize;
gc_alloc_context *allocContext = thread->GetAllocContext();
BYTE *allocPtr = allocContext->alloc_ptr;
_ASSERTE(allocPtr <= allocContext->alloc_limit);
if (totalSize > static_cast<SIZE_T>(allocContext->alloc_limit - allocPtr))
{
break;
}
allocContext->alloc_ptr = allocPtr + totalSize;
_ASSERTE(allocPtr != nullptr);
StringObject *stringObject = reinterpret_cast<StringObject *>(allocPtr);
stringObject->SetMethodTable(g_pStringClass);
stringObject->SetStringLength(stringLength);
_ASSERTE(stringObject->GetBuffer()[stringLength] == W('\0'));
return stringObject;
} while (false);
// Tail call to the slow helper
ENDFORBIDGC();
return HCCALL1(FramedAllocateString, stringLength);
}
HCIMPLEND
#include <optdefault.h>
/*********************************************************************/
/* We don't use HCIMPL macros because this is not a real helper call */
/* This function just needs mangled arguments like a helper call */
HCIMPL1_RAW(StringObject*, UnframedAllocateString, DWORD stringLength)
{
// This isn't _really_ an FCALL and therefore shouldn't have the
// SO_TOLERANT part of the FCALL_CONTRACT b/c it is not entered
// from managed code.
CONTRACTL {
THROWS;
GC_TRIGGERS;
MODE_COOPERATIVE;
SO_INTOLERANT;
} CONTRACTL_END;
STRINGREF result;
result = SlowAllocateString(stringLength);
return((StringObject*) OBJECTREFToObject(result));
}
HCIMPLEND_RAW
HCIMPL1(StringObject*, FramedAllocateString, DWORD stringLength)
{
FCALL_CONTRACT;
STRINGREF result = NULL;
HELPER_METHOD_FRAME_BEGIN_RET_0(); // Set up a frame
result = SlowAllocateString(stringLength);
HELPER_METHOD_FRAME_END();
return((StringObject*) OBJECTREFToObject(result));
}
HCIMPLEND
/*********************************************************************/
OBJECTHANDLE ConstructStringLiteral(CORINFO_MODULE_HANDLE scopeHnd, mdToken metaTok)
{
CONTRACTL {
THROWS;
GC_TRIGGERS;
MODE_ANY;
} CONTRACTL_END;
_ASSERTE(TypeFromToken(metaTok) == mdtString);
Module* module = GetModule(scopeHnd);
// If our module is ngenned and we're calling this API, it means that we're not going through
// the fixup mechanism for strings. This can happen 2 ways:
//
// a) Lazy string object construction: This happens when JIT decides that initizalizing a
// string via fixup on method entry is very expensive. This is normally done for strings
// that appear in rarely executed blocks, such as throw blocks.
//
// b) The ngen image isn't complete (it's missing classes), therefore we're jitting methods.
//
// If we went ahead and called ResolveStringRef directly, we would be breaking the per module
// interning we're guaranteeing, so we will have to detect the case and handle it appropriately.
#ifdef FEATURE_PREJIT
if (module->HasNativeImage() && module->IsNoStringInterning())
{
return module->ResolveStringRef(metaTok, module->GetAssembly()->Parent(), true);
}
#endif
return module->ResolveStringRef(metaTok, module->GetAssembly()->Parent(), false);
}
/*********************************************************************/
HCIMPL2(Object *, JIT_StrCns, unsigned rid, CORINFO_MODULE_HANDLE scopeHnd)
{
FCALL_CONTRACT;
OBJECTHANDLE hndStr = 0;
HELPER_METHOD_FRAME_BEGIN_RET_0();
// Retrieve the handle to the COM+ string object.
hndStr = ConstructStringLiteral(scopeHnd, RidToToken(rid, mdtString));
HELPER_METHOD_FRAME_END();
// Don't use ObjectFromHandle; this isn't a real handle
return *(Object**)hndStr;
}
HCIMPLEND
//========================================================================
//
// ARRAY HELPERS
//
//========================================================================
#include <optsmallperfcritical.h>
//*************************************************************
// Array allocation fast path for arrays of value type elements
//
HCIMPL2(Object*, JIT_NewArr1VC_MP_FastPortable, CORINFO_CLASS_HANDLE arrayMT, INT_PTR size)
{
FCALL_CONTRACT;
do
{
_ASSERTE(GCHeapUtilities::UseThreadAllocationContexts());
// Do a conservative check here. This is to avoid overflow while doing the calculations. We don't
// have to worry about "large" objects, since the allocation quantum is never big enough for
// LARGE_OBJECT_SIZE.
//
// For Value Classes, this needs to be 2^16 - slack (2^32 / max component size),
// The slack includes the size for the array header and round-up ; for alignment. Use 256 for the
// slack value out of laziness.
SIZE_T componentCount = static_cast<SIZE_T>(size);
if (componentCount >= static_cast<SIZE_T>(65535 - 256))
{
break;
}
// This is typically the only call in the fast path. Making the call early seems to be better, as it allows the compiler
// to use volatile registers for intermediate values. This reduces the number of push/pop instructions and eliminates
// some reshuffling of intermediate values into nonvolatile registers around the call.
Thread *thread = GetThread();
MethodTable *pArrayMT = (MethodTable *)arrayMT;
_ASSERTE(pArrayMT->HasComponentSize());
SIZE_T componentSize = pArrayMT->RawGetComponentSize();
SIZE_T totalSize = componentCount * componentSize;
_ASSERTE(totalSize / componentSize == componentCount);
SIZE_T baseSize = pArrayMT->GetBaseSize();
totalSize += baseSize;
_ASSERTE(totalSize >= baseSize);
SIZE_T alignedTotalSize = ALIGN_UP(totalSize, DATA_ALIGNMENT);
_ASSERTE(alignedTotalSize >= totalSize);
totalSize = alignedTotalSize;
gc_alloc_context *allocContext = thread->GetAllocContext();
BYTE *allocPtr = allocContext->alloc_ptr;
_ASSERTE(allocPtr <= allocContext->alloc_limit);
if (totalSize > static_cast<SIZE_T>(allocContext->alloc_limit - allocPtr))
{
break;
}
allocContext->alloc_ptr = allocPtr + totalSize;
_ASSERTE(allocPtr != nullptr);
ArrayBase *array = reinterpret_cast<ArrayBase *>(allocPtr);
array->SetArrayMethodTable(pArrayMT);
_ASSERTE(static_cast<DWORD>(componentCount) == componentCount);
array->m_NumComponents = static_cast<DWORD>(componentCount);
return array;
} while (false);
// Tail call to the slow helper
ENDFORBIDGC();
return HCCALL2(JIT_NewArr1, arrayMT, size);
}
HCIMPLEND
//*************************************************************
// Array allocation fast path for arrays of object elements
//
HCIMPL2(Object*, JIT_NewArr1OBJ_MP_FastPortable, CORINFO_CLASS_HANDLE arrayMT, INT_PTR size)
{
FCALL_CONTRACT;
do
{
_ASSERTE(GCHeapUtilities::UseThreadAllocationContexts());
// Make sure that the total size cannot reach LARGE_OBJECT_SIZE, which also allows us to avoid overflow checks. The
// "256" slack is to cover the array header size and round-up, using a constant value here out of laziness.
SIZE_T componentCount = static_cast<SIZE_T>(size);
if (componentCount >= static_cast<SIZE_T>((LARGE_OBJECT_SIZE - 256) / sizeof(void *)))
{
break;
}
// This is typically the only call in the fast path. Making the call early seems to be better, as it allows the compiler
// to use volatile registers for intermediate values. This reduces the number of push/pop instructions and eliminates
// some reshuffling of intermediate values into nonvolatile registers around the call.
Thread *thread = GetThread();
SIZE_T totalSize = componentCount * sizeof(void *);
_ASSERTE(totalSize / sizeof(void *) == componentCount);
MethodTable *pArrayMT = (MethodTable *)arrayMT;
SIZE_T baseSize = pArrayMT->GetBaseSize();
totalSize += baseSize;
_ASSERTE(totalSize >= baseSize);
_ASSERTE(ALIGN_UP(totalSize, DATA_ALIGNMENT) == totalSize);
gc_alloc_context *allocContext = thread->GetAllocContext();
BYTE *allocPtr = allocContext->alloc_ptr;
_ASSERTE(allocPtr <= allocContext->alloc_limit);
if (totalSize > static_cast<SIZE_T>(allocContext->alloc_limit - allocPtr))
{
break;
}
allocContext->alloc_ptr = allocPtr + totalSize;
_ASSERTE(allocPtr != nullptr);
ArrayBase *array = reinterpret_cast<ArrayBase *>(allocPtr);
array->SetArrayMethodTable(pArrayMT);
_ASSERTE(static_cast<DWORD>(componentCount) == componentCount);
array->m_NumComponents = static_cast<DWORD>(componentCount);
return array;
} while (false);
// Tail call to the slow helper
ENDFORBIDGC();
return HCCALL2(JIT_NewArr1, arrayMT, size);
}
HCIMPLEND
//*************************************************************
// R2R-specific array allocation wrapper that extracts array method table from ArrayTypeDesc
//
HCIMPL2(Object*, JIT_NewArr1_R2R, CORINFO_CLASS_HANDLE arrayTypeHnd_, INT_PTR size)
{
FCALL_CONTRACT;
TypeHandle arrayTypeHandle(arrayTypeHnd_);
ArrayTypeDesc *pArrayTypeDesc = arrayTypeHandle.AsArray();
MethodTable *pArrayMT = pArrayTypeDesc->GetTemplateMethodTable();
ENDFORBIDGC();
return HCCALL2(JIT_NewArr1, (CORINFO_CLASS_HANDLE)pArrayMT, size);
}
HCIMPLEND
#include <optdefault.h>
/*************************************************************/
HCIMPL2(Object*, JIT_NewArr1, CORINFO_CLASS_HANDLE arrayMT, INT_PTR size)
{
FCALL_CONTRACT;
OBJECTREF newArray = NULL;
HELPER_METHOD_FRAME_BEGIN_RET_0(); // Set up a frame
MethodTable *pArrayMT = (MethodTable *)arrayMT;
_ASSERTE(pArrayMT->IsFullyLoaded());
_ASSERTE(pArrayMT->IsArray());
_ASSERTE(!pArrayMT->IsMultiDimArray());
if (size < 0)
COMPlusThrow(kOverflowException);
#ifdef _WIN64
// Even though ECMA allows using a native int as the argument to newarr instruction
// (therefore size is INT_PTR), ArrayBase::m_NumComponents is 32-bit, so even on 64-bit
// platforms we can't create an array whose size exceeds 32 bits.
if (size > INT_MAX)
EX_THROW(EEMessageException, (kOverflowException, IDS_EE_ARRAY_DIMENSIONS_EXCEEDED));
#endif
//
// is this a primitive type?
//
CorElementType elemType = pArrayMT->GetArrayElementType();
if (CorTypeInfo::IsPrimitiveType(elemType)
#ifdef FEATURE_64BIT_ALIGNMENT
// On platforms where 64-bit types require 64-bit alignment and don't obtain it naturally force us
// through the slow path where this will be handled.
&& (elemType != ELEMENT_TYPE_I8)
&& (elemType != ELEMENT_TYPE_U8)
&& (elemType != ELEMENT_TYPE_R8)
#endif
)
{
#ifdef _DEBUG
if (g_pConfig->FastGCStressLevel()) {
GetThread()->DisableStressHeap();
}
#endif // _DEBUG
// Disallow the creation of void[] (an array of System.Void)
if (elemType == ELEMENT_TYPE_VOID)
COMPlusThrow(kArgumentException);
BOOL bAllocateInLargeHeap = FALSE;
#ifdef FEATURE_DOUBLE_ALIGNMENT_HINT
if ((elemType == ELEMENT_TYPE_R8) &&
(static_cast<DWORD>(size) >= g_pConfig->GetDoubleArrayToLargeObjectHeapThreshold()))
{
STRESS_LOG1(LF_GC, LL_INFO10, "Allocating double array of size %d to large object heap\n", size);
bAllocateInLargeHeap = TRUE;
}
#endif
if (g_pPredefinedArrayTypes[elemType] == NULL)
{
TypeHandle elemTypeHnd = TypeHandle(MscorlibBinder::GetElementType(elemType));
g_pPredefinedArrayTypes[elemType] = ClassLoader::LoadArrayTypeThrowing(elemTypeHnd, ELEMENT_TYPE_SZARRAY, 0).AsArray();
}
newArray = FastAllocatePrimitiveArray(pArrayMT, static_cast<DWORD>(size), bAllocateInLargeHeap);
}
else
{
#ifdef _DEBUG
if (g_pConfig->FastGCStressLevel()) {
GetThread()->DisableStressHeap();
}
#endif // _DEBUG
INT32 size32 = (INT32)size;
newArray = AllocateArrayEx(pArrayMT, &size32, 1);
}
HELPER_METHOD_FRAME_END();
return(OBJECTREFToObject(newArray));
}
HCIMPLEND
/*********************************************************************
// Allocate a multi-dimensional array
*/
OBJECTREF allocNewMDArr(TypeHandle typeHnd, unsigned dwNumArgs, va_list args)
{
CONTRACTL {
THROWS;
GC_TRIGGERS;
MODE_COOPERATIVE;
PRECONDITION(dwNumArgs > 0);
} CONTRACTL_END;
// Get the arguments in the right order
INT32* fwdArgList;
#ifdef _TARGET_X86_
fwdArgList = (INT32*)args;
// reverse the order
INT32* p = fwdArgList;
INT32* q = fwdArgList + (dwNumArgs-1);
while (p < q)
{
INT32 t = *p; *p = *q; *q = t;
p++; q--;
}
#else
// create an array where fwdArgList[0] == arg[0] ...
fwdArgList = (INT32*) _alloca(dwNumArgs * sizeof(INT32));
for (unsigned i = 0; i < dwNumArgs; i++)
{
fwdArgList[i] = va_arg(args, INT32);
}
#endif
return AllocateArrayEx(typeHnd, fwdArgList, dwNumArgs);
}
/*********************************************************************
// Allocate a multi-dimensional array with lower bounds specified.
// The caller pushes both sizes AND/OR bounds for every dimension
*/
HCIMPL2VA(Object*, JIT_NewMDArr, CORINFO_CLASS_HANDLE classHnd, unsigned dwNumArgs)
{
FCALL_CONTRACT;
OBJECTREF ret = 0;
HELPER_METHOD_FRAME_BEGIN_RET_1(ret); // Set up a frame
TypeHandle typeHnd(classHnd);
typeHnd.CheckRestore();
_ASSERTE(typeHnd.GetMethodTable()->IsArray());
va_list dimsAndBounds;
va_start(dimsAndBounds, dwNumArgs);
ret = allocNewMDArr(typeHnd, dwNumArgs, dimsAndBounds);
va_end(dimsAndBounds);
HELPER_METHOD_FRAME_END();
return OBJECTREFToObject(ret);
}
HCIMPLEND
/*************************************************************/
HCIMPL3(Object*, JIT_NewMDArrNonVarArg, CORINFO_CLASS_HANDLE classHnd, unsigned dwNumArgs, INT32 * pArgList)
{
FCALL_CONTRACT;
OBJECTREF ret = 0;
HELPER_METHOD_FRAME_BEGIN_RET_1(ret); // Set up a frame
TypeHandle typeHnd(classHnd);
typeHnd.CheckRestore();
_ASSERTE(typeHnd.GetMethodTable()->IsArray());
ret = AllocateArrayEx(typeHnd, pArgList, dwNumArgs);
HELPER_METHOD_FRAME_END();
return OBJECTREFToObject(ret);
}
HCIMPLEND
/*************************************************************/
/* returns '&array[idx], after doing all the proper checks */
#include <optsmallperfcritical.h>
HCIMPL3(void*, JIT_Ldelema_Ref, PtrArray* array, unsigned idx, CORINFO_CLASS_HANDLE type)
{
FCALL_CONTRACT;
RuntimeExceptionKind except;
// This has been carefully arranged to ensure that in the common
// case the branches are predicted properly (fall through).
// and that we dont spill registers unnecessarily etc.
if (array != 0)
if (idx < array->GetNumComponents())
if (array->GetArrayElementTypeHandle() == TypeHandle(type))
return(&array->m_Array[idx]);
else
except = kArrayTypeMismatchException;
else
except = kIndexOutOfRangeException;
else
except = kNullReferenceException;
FCThrow(except);
}
HCIMPLEND
#include <optdefault.h>
//===========================================================================
// This routine is called if the Array store needs a frame constructed
// in order to do the array check. It should only be called from
// the array store check helpers.
HCIMPL2(LPVOID, ArrayStoreCheck, Object** pElement, PtrArray** pArray)
{
FCALL_CONTRACT;
HELPER_METHOD_FRAME_BEGIN_RET_ATTRIB_2(Frame::FRAME_ATTR_EXACT_DEPTH|Frame::FRAME_ATTR_CAPTURE_DEPTH_2, *pElement, *pArray);
GCStress<cfg_any, EeconfigFastGcSPolicy>::MaybeTrigger();
if (!ObjIsInstanceOf(*pElement, (*pArray)->GetArrayElementTypeHandle()))
COMPlusThrow(kArrayTypeMismatchException);
HELPER_METHOD_FRAME_END();
return (LPVOID)0; // Used to aid epilog walker
}
HCIMPLEND
/****************************************************************************/
/* assigns 'val to 'array[idx], after doing all the proper checks */
HCIMPL3(void, JIT_Stelem_Ref_Portable, PtrArray* array, unsigned idx, Object *val)
{
FCALL_CONTRACT;
if (!array)
{
FCThrowVoid(kNullReferenceException);
}
if (idx >= array->GetNumComponents())
{
FCThrowVoid(kIndexOutOfRangeException);
}
if (val)
{
MethodTable *valMT = val->GetMethodTable();
TypeHandle arrayElemTH = array->GetArrayElementTypeHandle();
if (arrayElemTH != TypeHandle(valMT) && arrayElemTH != TypeHandle(g_pObjectClass))
{
TypeHandle::CastResult result = ObjIsInstanceOfNoGC(val, arrayElemTH);
if (result != TypeHandle::CanCast)
{
// FCALL_CONTRACT increase ForbidGC count. Normally, HELPER_METHOD_FRAME macros decrease the count.
// But to avoid perf hit, we manually decrease the count here before calling another HCCALL.
ENDFORBIDGC();
if (HCCALL2(ArrayStoreCheck,(Object**)&val, (PtrArray**)&array) != NULL)
{
// This return is never executed. It helps epilog walker to find its way out.
return;
}
}
}
#ifdef _TARGET_ARM64_
SetObjectReferenceUnchecked((OBJECTREF*)&array->m_Array[idx], ObjectToOBJECTREF(val));
#else
// The performance gain of the optimized JIT_Stelem_Ref in
// jitinterfacex86.cpp is mainly due to calling JIT_WriteBarrier
// By calling write barrier directly here,
// we can avoid translating in-line assembly from MSVC to gcc
// while keeping most of the performance gain.
HCCALL2(JIT_WriteBarrier, (Object **)&array->m_Array[idx], val);
#endif
}
else
{
// no need to go through write-barrier for NULL
ClearObjectReference(&array->m_Array[idx]);
}
}
HCIMPLEND
//========================================================================
//
// VALUETYPE/BYREF HELPERS
//
//========================================================================
/*************************************************************/
HCIMPL2(Object*, JIT_Box, CORINFO_CLASS_HANDLE type, void* unboxedData)
{
FCALL_CONTRACT;
// <TODO>TODO: if we care, we could do a fast trial allocation
// and avoid the building the frame most times</TODO>
OBJECTREF newobj = NULL;
HELPER_METHOD_FRAME_BEGIN_RET_NOPOLL(); // Set up a frame
GCPROTECT_BEGININTERIOR(unboxedData);
HELPER_METHOD_POLL();
TypeHandle clsHnd(type);
_ASSERTE(!clsHnd.IsTypeDesc()); // we never use this helper for arrays
MethodTable *pMT = clsHnd.AsMethodTable();
pMT->CheckRestore();
// You can only box valuetypes
if (!pMT->IsValueType())
COMPlusThrow(kInvalidCastException, W("Arg_ObjObj"));
#ifdef _DEBUG
if (g_pConfig->FastGCStressLevel()) {
GetThread()->DisableStressHeap();
}
#endif // _DEBUG
newobj = pMT->FastBox(&unboxedData);
GCPROTECT_END();
HELPER_METHOD_FRAME_END();
return(OBJECTREFToObject(newobj));
}
HCIMPLEND
/*************************************************************/
NOINLINE HCIMPL3(VOID, JIT_Unbox_Nullable_Framed, void * destPtr, MethodTable* typeMT, OBJECTREF objRef)
{
FCALL_CONTRACT;
HELPER_METHOD_FRAME_BEGIN_1(objRef);
if (!Nullable::UnBox(destPtr, objRef, typeMT))
{
COMPlusThrowInvalidCastException(&objRef, TypeHandle(typeMT));
}
HELPER_METHOD_FRAME_END();
}
HCIMPLEND
/*************************************************************/
HCIMPL3(VOID, JIT_Unbox_Nullable, void * destPtr, CORINFO_CLASS_HANDLE type, Object* obj)
{
FCALL_CONTRACT;
TypeHandle typeHnd(type);
_ASSERTE(Nullable::IsNullableType(typeHnd));
MethodTable* typeMT = typeHnd.AsMethodTable();
OBJECTREF objRef = ObjectToOBJECTREF(obj);
if (Nullable::UnBoxNoGC(destPtr, objRef, typeMT))
{
// exact match (type equivalence not needed)
return;
}
// Fall back to a framed helper that handles type equivalence.
ENDFORBIDGC();
HCCALL3(JIT_Unbox_Nullable_Framed, destPtr, typeMT, objRef);
}
HCIMPLEND
/*************************************************************/
/* framed helper that handles full-blown type equivalence */
NOINLINE HCIMPL2(LPVOID, JIT_Unbox_Helper_Framed, CORINFO_CLASS_HANDLE type, Object* obj)
{
FCALL_CONTRACT;
LPVOID result = NULL;
OBJECTREF objRef = ObjectToOBJECTREF(obj);
HELPER_METHOD_FRAME_BEGIN_RET_1(objRef);
if (TypeHandle(type).IsEquivalentTo(objRef->GetTypeHandle()))
{
// the structures are equivalent
result = objRef->GetData();
}
else
{
COMPlusThrowInvalidCastException(&objRef, TypeHandle(type));
}
HELPER_METHOD_FRAME_END();
return result;
}
HCIMPLEND
/*************************************************************/
/* the uncommon case for the helper below (allowing enums to be unboxed
as their underlying type */
LPVOID __fastcall JIT_Unbox_Helper(CORINFO_CLASS_HANDLE type, Object* obj)
{
FCALL_CONTRACT;
TypeHandle typeHnd(type);
CorElementType type1 = typeHnd.GetInternalCorElementType();
// we allow enums and their primtive type to be interchangable
MethodTable* pMT2 = obj->GetMethodTable();
CorElementType type2 = pMT2->GetInternalCorElementType();
if (type1 == type2)
{
MethodTable* pMT1 = typeHnd.GetMethodTable();
if (pMT1 && (pMT1->IsEnum() || pMT1->IsTruePrimitive()) &&
(pMT2->IsEnum() || pMT2->IsTruePrimitive()))
{
_ASSERTE(CorTypeInfo::IsPrimitiveType_NoThrow(type1));
return(obj->GetData());
}
}
// Even less common cases (type equivalence) go to a framed helper.
ENDFORBIDGC();
return HCCALL2(JIT_Unbox_Helper_Framed, type, obj);
}
/*************************************************************/
HCIMPL2(LPVOID, JIT_Unbox, CORINFO_CLASS_HANDLE type, Object* obj)
{
FCALL_CONTRACT;
TypeHandle typeHnd(type);
VALIDATEOBJECT(obj);
_ASSERTE(!typeHnd.IsTypeDesc()); // value classes are always unshared
// This has been tuned so that branch predictions are good
// (fall through for forward branches) for the common case
if (obj != NULL) {
if (obj->GetMethodTable() == typeHnd.AsMethodTable())
return(obj->GetData());
else {
// Stuff the uncommon case into a helper so that
// its register needs don't cause spills that effect
// the common case above.
return JIT_Unbox_Helper(type, obj);
}
}
FCThrow(kNullReferenceException);
}
HCIMPLEND
/*************************************************************/
HCIMPL2_IV(LPVOID, JIT_GetRefAny, CORINFO_CLASS_HANDLE type, TypedByRef typedByRef)
{
FCALL_CONTRACT;
TypeHandle clsHnd(type);
// <TODO>@TODO right now we check for precisely the correct type.
// do we want to allow inheritance? (watch out since value
// classes inherit from object but do not normal object layout).</TODO>
if (clsHnd != typedByRef.type) {
FCThrow(kInvalidCastException);
}
return(typedByRef.data);
}
HCIMPLEND
//========================================================================
//
// GENERICS HELPERS
//
//========================================================================
/***********************************************************************/
// JIT_GenericHandle and its cache
//
// Perform a "polytypic" operation related to shared generic code at runtime, possibly filling in an entry in
// either a generic dictionary cache assocaited with a descriptor or placing an entry in the global
// JitGenericHandle cache.
//
// A polytypic operation is one such as
// * new List<T>
// * castclass List<T>
// where the code being executed is shared generic code. In these cases the outcome of the operation depends
// on the exact value for T, which is acquired from a dynamic parameter.
//
// The actual operation always boils down to finding a "handle" (TypeHandle, MethodDesc, call address,
// dispatch stub address etc.) based on some static information (passed as tokens) and on the exact runtime
// type context (passed as one or two parameters classHnd and methodHnd).
//
// The static information specifies which polytypic operation (and thus which kind of handle) we're
// interested in.
//
// The dynamic information (the type context, i.e. the exact instantiation of class and method type
// parameters is specified in one of two ways:
// * If classHnd is null then the methodHnd should be an exact method descriptor wrapping shared code that
// satisfies SharedByGenericMethodInstantiations().
//
// For example:
// * We may be running the shared code for a generic method instantiation C::m<object>. The methodHnd
// will carry the exact instantiation, e.g. C::m<string>
//
// * If classHnd is non-null (e.g. a type D<exact>) then:
// * methodHnd will indicate the representative code being run (which will be
// !SharedByGenericMethodInstantiations but will be SharedByGenericClassInstantiations). Let's say
// this code is C<repr>::m().
// * the type D will be a descendent of type C. In particular D<exact> will relate to some type C<exact'>
// where C<repr> is the represntative instantiation of C<exact>'
// * the relevant dictionary will be the one attached to C<exact'>.
//
// The JitGenericHandleCache is a global data structure shared across all application domains. It is only
// used if generic dictionaries have overflowed. It is flushed each time an application domain is unloaded.
struct JitGenericHandleCacheKey
{
JitGenericHandleCacheKey(CORINFO_CLASS_HANDLE classHnd, CORINFO_METHOD_HANDLE methodHnd, void *signature, BaseDomain* pDomain=NULL)
{
LIMITED_METHOD_CONTRACT;
m_Data1 = (size_t)classHnd;
m_Data2 = (size_t)methodHnd;
m_Data3 = (size_t)signature;
m_pDomainAndType = 0 | (size_t)pDomain;
}
JitGenericHandleCacheKey(MethodTable* pMT, CORINFO_CLASS_HANDLE classHnd, CORINFO_METHOD_HANDLE methodHnd, BaseDomain* pDomain=NULL)
{
LIMITED_METHOD_CONTRACT;
m_Data1 = (size_t)pMT;
m_Data2 = (size_t)classHnd;
m_Data3 = (size_t)methodHnd;
m_pDomainAndType = 1 | (size_t)pDomain;
}
size_t GetType() const
{
LIMITED_METHOD_CONTRACT;
return (m_pDomainAndType & 1);
}
BaseDomain* GetDomain() const
{
LIMITED_METHOD_CONTRACT;
return (BaseDomain*)(m_pDomainAndType & ~1);
}
size_t m_Data1;
size_t m_Data2;
size_t m_Data3;
size_t m_pDomainAndType; // Which domain the entry belongs to. Not actually part of the key.
// Used only so we can scrape the table on AppDomain termination.
// NULL appdomain means that the entry should be scratched
// on any appdomain unload.
//
// The lowest bit is used to indicate the type of the entry:
// 0 - JIT_GenericHandle entry
// 1 - JIT_VirtualFunctionPointer entry
};
class JitGenericHandleCacheTraits
{
public:
static EEHashEntry_t *AllocateEntry(const JitGenericHandleCacheKey *pKey, BOOL bDeepCopy, AllocationHeap pHeap = 0)
{
LIMITED_METHOD_CONTRACT;
EEHashEntry_t *pEntry = (EEHashEntry_t *) new (nothrow) BYTE[SIZEOF_EEHASH_ENTRY + sizeof(JitGenericHandleCacheKey)];
if (!pEntry)
return NULL;
*((JitGenericHandleCacheKey*)pEntry->Key) = *pKey;
return pEntry;
}
static void DeleteEntry(EEHashEntry_t *pEntry, AllocationHeap pHeap = 0)
{
LIMITED_METHOD_CONTRACT;
delete [] (BYTE*)pEntry;
}
static BOOL CompareKeys(EEHashEntry_t *pEntry, const JitGenericHandleCacheKey *e2)
{
LIMITED_METHOD_CONTRACT;
const JitGenericHandleCacheKey *e1 = (const JitGenericHandleCacheKey*)&pEntry->Key;
return (e1->m_Data1 == e2->m_Data1) && (e1->m_Data2 == e2->m_Data2) && (e1->m_Data3 == e2->m_Data3) &&
(e1->GetType() == e2->GetType()) &&
// Any domain will work if the lookup key does not specify it
((e2->GetDomain() == NULL) || (e1->GetDomain() == e2->GetDomain()));
}
static DWORD Hash(const JitGenericHandleCacheKey *k)
{
LIMITED_METHOD_CONTRACT;
return (DWORD)k->m_Data1 + _rotl((DWORD)k->m_Data2,5) + _rotr((DWORD)k->m_Data3,5);
}
static const JitGenericHandleCacheKey *GetKey(EEHashEntry_t *pEntry)
{
LIMITED_METHOD_CONTRACT;
return (const JitGenericHandleCacheKey*)&pEntry->Key;
}
};
typedef EEHashTable<const JitGenericHandleCacheKey *, JitGenericHandleCacheTraits, FALSE> JitGenericHandleCache;
JitGenericHandleCache *g_pJitGenericHandleCache = NULL; //cache of calls to JIT_GenericHandle
CrstStatic g_pJitGenericHandleCacheCrst;
void AddToGenericHandleCache(JitGenericHandleCacheKey* pKey, HashDatum datum)
{
CONTRACTL {
NOTHROW;
GC_TRIGGERS;
MODE_ANY;
PRECONDITION(CheckPointer(pKey));
PRECONDITION(CheckPointer(datum));
} CONTRACTL_END;
EX_TRY
{
GCX_COOP();
CrstHolder lock(&g_pJitGenericHandleCacheCrst);
HashDatum entry;
if (!g_pJitGenericHandleCache->GetValue(pKey,&entry))
g_pJitGenericHandleCache->InsertValue(pKey,datum);
}
EX_CATCH
{
}
EX_END_CATCH(SwallowAllExceptions) // Swallow OOM
}
/* static */
void ClearJitGenericHandleCache(AppDomain *pDomain)
{
CONTRACTL {
NOTHROW;
GC_NOTRIGGER;
} CONTRACTL_END;
// We call this on every AppDomain unload, because entries in the cache might include
// pointers into the AppDomain being unloaded. We would prefer to
// only flush entries that have that are no longer valid, but the entries don't yet contain
// enough information to do that. However everything in the cache can be found again by calling
// loader functions, and the total number of entries in the cache is typically very small (indeed
// normally the cache is not used at all - it is only used when the generic dictionaries overflow).
if (g_pJitGenericHandleCache)
{
// It's not necessary to take the lock here because this function should only be called when EE is suspended,
// the lock is only taken to fullfill the threadsafety check and to be consistent. If the lock becomes a problem, we
// could put it in a "ifdef _DEBUG" block
CrstHolder lock(&g_pJitGenericHandleCacheCrst);
EEHashTableIteration iter;
g_pJitGenericHandleCache->IterateStart(&iter);
BOOL keepGoing = g_pJitGenericHandleCache->IterateNext(&iter);
while(keepGoing)
{
const JitGenericHandleCacheKey *key = g_pJitGenericHandleCache->IterateGetKey(&iter);
BaseDomain* pKeyDomain = key->GetDomain();
if (pKeyDomain == pDomain || pKeyDomain == NULL
// We compute fake domain for types during NGen (see code:ClassLoader::ComputeLoaderModule).
// To avoid stale handles, we need to clear the cache unconditionally during NGen.
|| IsCompilationProcess())
{
// Advance the iterator before we delete!! See notes in EEHash.h
keepGoing = g_pJitGenericHandleCache->IterateNext(&iter);
g_pJitGenericHandleCache->DeleteValue(key);
}
else
{
keepGoing = g_pJitGenericHandleCache->IterateNext(&iter);
}
}
}
}
// Factored out most of the body of JIT_GenericHandle so it could be called easily from the CER reliability code to pre-populate the
// cache.
CORINFO_GENERIC_HANDLE JIT_GenericHandleWorker(MethodDesc * pMD, MethodTable * pMT, LPVOID signature, DWORD dictionaryIndexAndSlot, Module* pModule)
{
CONTRACTL {
THROWS;
GC_TRIGGERS;
} CONTRACTL_END;
MethodTable * pDeclaringMT = NULL;
if (pMT != NULL)
{
ULONG dictionaryIndex = 0;
if (pModule != NULL)
{
#ifdef _DEBUG
// Only in R2R mode are the module, dictionary index and dictionary slot provided as an input
_ASSERTE(dictionaryIndexAndSlot != -1);
_ASSERT(ExecutionManager::FindReadyToRunModule(dac_cast<TADDR>(signature)) == pModule);
#endif
dictionaryIndex = (dictionaryIndexAndSlot >> 16);
}
else
{
SigPointer ptr((PCCOR_SIGNATURE)signature);
ULONG kind; // DictionaryEntryKind
IfFailThrow(ptr.GetData(&kind));
// We need to normalize the class passed in (if any) for reliability purposes. That's because preparation of a code region that
// contains these handle lookups depends on being able to predict exactly which lookups are required (so we can pre-cache the
// answers and remove any possibility of failure at runtime). This is hard to do if the lookup (in this case the lookup of the
// dictionary overflow cache) is keyed off the somewhat arbitrary type of the instance on which the call is made (we'd need to
// prepare for every possible derived type of the type containing the method). So instead we have to locate the exactly
// instantiated (non-shared) super-type of the class passed in.
_ASSERTE(dictionaryIndexAndSlot == -1);
IfFailThrow(ptr.GetData(&dictionaryIndex));
}
pDeclaringMT = pMT;
for (;;)
{
MethodTable * pParentMT = pDeclaringMT->GetParentMethodTable();
if (pParentMT->GetNumDicts() <= dictionaryIndex)
break;
pDeclaringMT = pParentMT;
}
if (pDeclaringMT != pMT)
{
JitGenericHandleCacheKey key((CORINFO_CLASS_HANDLE)pDeclaringMT, NULL, signature);
HashDatum res;
if (g_pJitGenericHandleCache->GetValue(&key,&res))
{
// Add the denormalized key for faster lookup next time. This is not a critical entry - no need
// to specify appdomain affinity.
JitGenericHandleCacheKey denormKey((CORINFO_CLASS_HANDLE)pMT, NULL, signature);
AddToGenericHandleCache(&denormKey, res);
return (CORINFO_GENERIC_HANDLE) (DictionaryEntry) res;
}
}
}
DictionaryEntry * pSlot;
CORINFO_GENERIC_HANDLE result = (CORINFO_GENERIC_HANDLE)Dictionary::PopulateEntry(pMD, pDeclaringMT, signature, FALSE, &pSlot, dictionaryIndexAndSlot, pModule);
if (pSlot == NULL)
{
// If we've overflowed the dictionary write the result to the cache.
BaseDomain *pDictDomain = NULL;
if (pMT != NULL)
{
pDictDomain = pDeclaringMT->GetDomain();
}
else
{
pDictDomain = pMD->GetDomain();
}
// Add the normalized key (pDeclaringMT) here so that future lookups of any
// inherited types are faster next time rather than just just for this specific pMT.
JitGenericHandleCacheKey key((CORINFO_CLASS_HANDLE)pDeclaringMT, (CORINFO_METHOD_HANDLE)pMD, signature, pDictDomain);
AddToGenericHandleCache(&key, (HashDatum)result);
}
return result;
} // JIT_GenericHandleWorker
/*********************************************************************/
// slow helper to tail call from the fast one
NOINLINE HCIMPL5(CORINFO_GENERIC_HANDLE, JIT_GenericHandle_Framed,
CORINFO_CLASS_HANDLE classHnd,
CORINFO_METHOD_HANDLE methodHnd,
LPVOID signature,
DWORD dictionaryIndexAndSlot,
CORINFO_MODULE_HANDLE moduleHnd)
{
CONTRACTL {
FCALL_CHECK;
PRECONDITION(classHnd != NULL || methodHnd != NULL);
PRECONDITION(classHnd == NULL || methodHnd == NULL);
} CONTRACTL_END;
// Result is a generic handle (in fact, a CORINFO_CLASS_HANDLE, CORINFO_METHOD_HANDLE, or a code pointer)
CORINFO_GENERIC_HANDLE result = NULL;
MethodDesc * pMD = GetMethod(methodHnd);
MethodTable * pMT = TypeHandle(classHnd).AsMethodTable();
Module * pModule = GetModule(moduleHnd);
// Set up a frame
HELPER_METHOD_FRAME_BEGIN_RET_0();
result = JIT_GenericHandleWorker(pMD, pMT, signature, dictionaryIndexAndSlot, pModule);
HELPER_METHOD_FRAME_END();
_ASSERTE(result != NULL);
// Return the handle
return result;
}
HCIMPLEND
/*********************************************************************/
#include <optsmallperfcritical.h>
HCIMPL2(CORINFO_GENERIC_HANDLE, JIT_GenericHandleMethod, CORINFO_METHOD_HANDLE methodHnd, LPVOID signature)
{
CONTRACTL {
FCALL_CHECK;
PRECONDITION(CheckPointer(methodHnd));
PRECONDITION(GetMethod(methodHnd)->IsRestored());
PRECONDITION(CheckPointer(signature));
} CONTRACTL_END;
JitGenericHandleCacheKey key(NULL, methodHnd, signature);
HashDatum res;
if (g_pJitGenericHandleCache->GetValueSpeculative(&key,&res))
return (CORINFO_GENERIC_HANDLE) (DictionaryEntry) res;
// Tailcall to the slow helper
ENDFORBIDGC();
return HCCALL5(JIT_GenericHandle_Framed, NULL, methodHnd, signature, -1, NULL);
}
HCIMPLEND
HCIMPL2(CORINFO_GENERIC_HANDLE, JIT_GenericHandleMethodWithSlotAndModule, CORINFO_METHOD_HANDLE methodHnd, GenericHandleArgs * pArgs)
{
CONTRACTL{
FCALL_CHECK;
PRECONDITION(CheckPointer(methodHnd));
PRECONDITION(GetMethod(methodHnd)->IsRestored());
PRECONDITION(CheckPointer(pArgs));
} CONTRACTL_END;
JitGenericHandleCacheKey key(NULL, methodHnd, pArgs->signature);
HashDatum res;
if (g_pJitGenericHandleCache->GetValueSpeculative(&key, &res))
return (CORINFO_GENERIC_HANDLE)(DictionaryEntry)res;
// Tailcall to the slow helper
ENDFORBIDGC();
return HCCALL5(JIT_GenericHandle_Framed, NULL, methodHnd, pArgs->signature, pArgs->dictionaryIndexAndSlot, pArgs->module);
}
HCIMPLEND
#include <optdefault.h>
/*********************************************************************/
HCIMPL2(CORINFO_GENERIC_HANDLE, JIT_GenericHandleMethodLogging, CORINFO_METHOD_HANDLE methodHnd, LPVOID signature)
{
CONTRACTL {
FCALL_CHECK;
PRECONDITION(CheckPointer(methodHnd));
PRECONDITION(GetMethod(methodHnd)->IsRestored());
PRECONDITION(CheckPointer(signature));
} CONTRACTL_END;
g_IBCLogger.LogMethodDescAccess(GetMethod(methodHnd));
JitGenericHandleCacheKey key(NULL, methodHnd, signature);
HashDatum res;
if (g_pJitGenericHandleCache->GetValueSpeculative(&key,&res))
return (CORINFO_GENERIC_HANDLE) (DictionaryEntry) res;
// Tailcall to the slow helper
ENDFORBIDGC();
return HCCALL5(JIT_GenericHandle_Framed, NULL, methodHnd, signature, -1, NULL);
}
HCIMPLEND
/*********************************************************************/
#include <optsmallperfcritical.h>
HCIMPL2(CORINFO_GENERIC_HANDLE, JIT_GenericHandleClass, CORINFO_CLASS_HANDLE classHnd, LPVOID signature)
{
CONTRACTL {
FCALL_CHECK;
PRECONDITION(CheckPointer(classHnd));
PRECONDITION(TypeHandle(classHnd).IsRestored());
PRECONDITION(CheckPointer(signature));
} CONTRACTL_END;
JitGenericHandleCacheKey key(classHnd, NULL, signature);
HashDatum res;
if (g_pJitGenericHandleCache->GetValueSpeculative(&key,&res))
return (CORINFO_GENERIC_HANDLE) (DictionaryEntry) res;
// Tailcall to the slow helper
ENDFORBIDGC();
return HCCALL5(JIT_GenericHandle_Framed, classHnd, NULL, signature, -1, NULL);
}
HCIMPLEND
HCIMPL2(CORINFO_GENERIC_HANDLE, JIT_GenericHandleClassWithSlotAndModule, CORINFO_CLASS_HANDLE classHnd, GenericHandleArgs * pArgs)
{
CONTRACTL{
FCALL_CHECK;
PRECONDITION(CheckPointer(classHnd));
PRECONDITION(TypeHandle(classHnd).IsRestored());
PRECONDITION(CheckPointer(pArgs));
} CONTRACTL_END;
JitGenericHandleCacheKey key(classHnd, NULL, pArgs->signature);
HashDatum res;
if (g_pJitGenericHandleCache->GetValueSpeculative(&key, &res))
return (CORINFO_GENERIC_HANDLE)(DictionaryEntry)res;
// Tailcall to the slow helper
ENDFORBIDGC();
return HCCALL5(JIT_GenericHandle_Framed, classHnd, NULL, pArgs->signature, pArgs->dictionaryIndexAndSlot, pArgs->module);
}
HCIMPLEND
#include <optdefault.h>
/*********************************************************************/
HCIMPL2(CORINFO_GENERIC_HANDLE, JIT_GenericHandleClassLogging, CORINFO_CLASS_HANDLE classHnd, LPVOID signature)
{
CONTRACTL {
FCALL_CHECK;
PRECONDITION(CheckPointer(classHnd));
PRECONDITION(TypeHandle(classHnd).IsRestored());
PRECONDITION(CheckPointer(signature));
} CONTRACTL_END;
g_IBCLogger.LogMethodTableAccess((MethodTable *)classHnd);
JitGenericHandleCacheKey key(classHnd, NULL, signature);
HashDatum res;
if (g_pJitGenericHandleCache->GetValueSpeculative(&key,&res))
return (CORINFO_GENERIC_HANDLE) (DictionaryEntry) res;
// Tailcall to the slow helper
ENDFORBIDGC();
return HCCALL5(JIT_GenericHandle_Framed, classHnd, NULL, signature, -1, NULL);
}
HCIMPLEND
/*********************************************************************/
// Resolve a virtual method at run-time, either because of
// aggressive backpatching or because the call is to a generic
// method which is itself virtual.
//
// classHnd is the actual run-time type for the call is made.
// methodHnd is the exact (instantiated) method descriptor corresponding to the
// static method signature (i.e. might be for a superclass of classHnd)
// slow helper to tail call from the fast one
NOINLINE HCIMPL3(CORINFO_MethodPtr, JIT_VirtualFunctionPointer_Framed, Object * objectUNSAFE,
CORINFO_CLASS_HANDLE classHnd,
CORINFO_METHOD_HANDLE methodHnd)
{
FCALL_CONTRACT;
// The address of the method that's returned.
CORINFO_MethodPtr addr = NULL;
OBJECTREF objRef = ObjectToOBJECTREF(objectUNSAFE);
HELPER_METHOD_FRAME_BEGIN_RET_1(objRef); // Set up a frame
if (objRef == NULL)
COMPlusThrow(kNullReferenceException);
// This is the static method descriptor describing the call.
// It is not the destination of the call, which we must compute.
MethodDesc* pStaticMD = (MethodDesc*) methodHnd;
TypeHandle staticTH(classHnd);
pStaticMD->CheckRestore();
// MDIL: If IL specifies callvirt/ldvirtftn it remains a "virtual" instruction
// even if the target is an instance method at MDIL generation time because
// we want to keep MDIL as resilient as IL. Right now we can end up here with
// non-virtual generic methods called from a "shared generic code".
// As soon as this deficiency is fixed in the binder we can get rid of this test.
if (!pStaticMD->IsVtableMethod())
{
addr = (CORINFO_MethodPtr) pStaticMD->GetMultiCallableAddrOfCode();
_ASSERTE(addr);
}