return static_cast<INT32>(offset);
}
+INT32 rel32UsingPreallocatedJumpStub(INT32 UNALIGNED * pRel32, PCODE target, PCODE jumpStubAddr)
+{
+ CONTRACTL
+ {
+ THROWS; // emitBackToBackJump may throw (see emitJump)
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ TADDR baseAddr = (TADDR)pRel32 + 4;
+ _ASSERTE(FitsInI4(jumpStubAddr - baseAddr));
+
+ INT_PTR offset = target - baseAddr;
+ if (!FitsInI4(offset) INDEBUG(|| PEDecoder::GetForceRelocs()))
+ {
+ offset = jumpStubAddr - baseAddr;
+ if (!FitsInI4(offset))
+ {
+ _ASSERTE(!"jump stub was not in expected range");
+ EEPOLICY_HANDLE_FATAL_ERROR(COR_E_EXECUTIONENGINE);
+ }
+
+ emitBackToBackJump((LPBYTE)jumpStubAddr, (LPVOID)target);
+ }
+
+ _ASSERTE(FitsInI4(offset));
+ return static_cast<INT32>(offset);
+}
+
BOOL DoesSlotCallPrestub(PCODE pCode)
{
CONTRACTL {
//#define HAS_REMOTING_PRECODE 1 // TODO: Implement
#define HAS_FIXUP_PRECODE 1
#define HAS_FIXUP_PRECODE_CHUNKS 1
+#define FIXUP_PRECODE_PREALLOCATE_DYNAMIC_METHOD_JUMP_STUBS 1
// ThisPtrRetBufPrecode one is necessary for closed delegates over static methods with return buffer
#define HAS_THISPTR_RETBUF_PRECODE 1
// Get Rel32 destination, emit jumpStub if necessary
INT32 rel32UsingJumpStub(INT32 UNALIGNED * pRel32, PCODE target, MethodDesc *pMethod, LoaderAllocator *pLoaderAllocator = NULL);
+// Get Rel32 destination, emit jumpStub if necessary into a preallocated location
+INT32 rel32UsingPreallocatedJumpStub(INT32 UNALIGNED * pRel32, PCODE target, PCODE jumpStubAddr);
+
void emitCOMStubCall (ComCallMethodDesc *pCOMMethod, PCODE target);
void emitJump(LPBYTE pBuffer, LPVOID target);
// crossgen does not have jump stubs
return 0;
}
+
+INT32 rel32UsingPreallocatedJumpStub(INT32 UNALIGNED * pRel32, PCODE target, PCODE jumpStubAddr)
+{
+ // crossgen does not have jump stubs
+ return 0;
+}
#endif
}
#endif
+#ifdef FIXUP_PRECODE_PREALLOCATE_DYNAMIC_METHOD_JUMP_STUBS
+PCODE FixupPrecode::GetDynamicMethodEntryJumpStub()
+{
+ _ASSERTE(((PTR_MethodDesc)GetMethodDesc())->IsLCGMethod());
+
+ // m_PrecodeChunkIndex has a value inverted to the order of precodes in memory (the precode at the lowest address has the
+ // highest index, and the precode at the highest address has the lowest index). To map a precode to its jump stub by memory
+ // order, invert the precode index to get the jump stub index.
+ UINT32 count = ((PTR_MethodDesc)GetMethodDesc())->GetMethodDescChunk()->GetCount();
+ _ASSERTE(m_PrecodeChunkIndex < count);
+ SIZE_T jumpStubIndex = count - 1 - m_PrecodeChunkIndex;
+
+ return GetBase() + sizeof(PTR_MethodDesc) + jumpStubIndex * BACK_TO_BACK_JUMP_ALLOCATE_SIZE;
+}
+#endif // FIXUP_PRECODE_PREALLOCATE_DYNAMIC_METHOD_JUMP_STUBS
+
#ifdef DACCESS_COMPILE
void FixupPrecode::EnumMemoryRegions(CLRDataEnumMemoryFlags flags)
{
_ASSERTE(GetMethodDesc() == (TADDR)pMD);
+ PCODE target = (PCODE)GetEEFuncEntryPoint(PrecodeFixupThunk);
+#ifdef FIXUP_PRECODE_PREALLOCATE_DYNAMIC_METHOD_JUMP_STUBS
+ if (pMD->IsLCGMethod())
+ {
+ m_rel32 = rel32UsingPreallocatedJumpStub(&m_rel32, target, GetDynamicMethodEntryJumpStub());
+ return;
+ }
+#endif // FIXUP_PRECODE_PREALLOCATE_DYNAMIC_METHOD_JUMP_STUBS
if (pLoaderAllocator != NULL)
{
- m_rel32 = rel32UsingJumpStub(&m_rel32,
- GetEEFuncEntryPoint(PrecodeFixupThunk), NULL /* pMD */, pLoaderAllocator);
+ m_rel32 = rel32UsingJumpStub(&m_rel32, target, NULL /* pMD */, pLoaderAllocator);
}
}
pOldValue[offsetof(FixupPrecode,m_op)] = X86_INSTR_CALL_REL32;
pNewValue[offsetof(FixupPrecode,m_op)] = X86_INSTR_JMP_REL32;
- *(INT32*)(&pNewValue[offsetof(FixupPrecode,m_rel32)]) = rel32UsingJumpStub(&m_rel32, target, pMD);
+ *(INT32*)(&pNewValue[offsetof(FixupPrecode, m_rel32)]) =
+#ifdef FIXUP_PRECODE_PREALLOCATE_DYNAMIC_METHOD_JUMP_STUBS
+ pMD->IsLCGMethod() ?
+ rel32UsingPreallocatedJumpStub(&m_rel32, target, GetDynamicMethodEntryJumpStub()) :
+#endif // FIXUP_PRECODE_PREALLOCATE_DYNAMIC_METHOD_JUMP_STUBS
+ rel32UsingJumpStub(&m_rel32, target, pMD);
_ASSERTE(IS_ALIGNED(this, sizeof(INT64)));
EnsureWritableExecutablePages(this, sizeof(INT64));
}
#endif // HAS_FIXUP_PRECODE_CHUNKS
+#ifdef FIXUP_PRECODE_PREALLOCATE_DYNAMIC_METHOD_JUMP_STUBS
+ PCODE GetDynamicMethodEntryJumpStub();
+#endif // FIXUP_PRECODE_PREALLOCATE_DYNAMIC_METHOD_JUMP_STUBS
+
PCODE GetTarget()
{
LIMITED_METHOD_DAC_CONTRACT;
return PTR_Precode(temporaryEntryPoints + index * oneSize);
}
-SIZE_T Precode::SizeOfTemporaryEntryPoints(PrecodeType t, int count)
+SIZE_T Precode::SizeOfTemporaryEntryPoints(PrecodeType t, bool preallocateJumpStubs, int count)
{
WRAPPER_NO_CONTRACT;
SUPPORTS_DAC;
+
#ifdef HAS_FIXUP_PRECODE_CHUNKS
if (t == PRECODE_FIXUP)
{
- return count * (sizeof(FixupPrecode) + sizeof(PCODE)) + sizeof(PTR_MethodDesc);
+ SIZE_T size = count * sizeof(FixupPrecode) + sizeof(PTR_MethodDesc);
+
+#ifdef FIXUP_PRECODE_PREALLOCATE_DYNAMIC_METHOD_JUMP_STUBS
+ if (preallocateJumpStubs)
+ {
+ // For dynamic methods, space for jump stubs is allocated along with the precodes as part of the temporary entry
+ // points block. The first jump stub begins immediately after the PTR_MethodDesc.
+ size += count * BACK_TO_BACK_JUMP_ALLOCATE_SIZE;
+ }
+#else // !FIXUP_PRECODE_PREALLOCATE_DYNAMIC_METHOD_JUMP_STUBS
+ _ASSERTE(!preallocateJumpStubs);
+#endif // FIXUP_PRECODE_PREALLOCATE_DYNAMIC_METHOD_JUMP_STUBS
+
+ return size;
+ }
+ else
+ {
+ _ASSERTE(!preallocateJumpStubs);
}
#endif
SIZE_T oneSize = SizeOfTemporaryEntryPoint(t);
return count * oneSize;
}
+SIZE_T Precode::SizeOfTemporaryEntryPoints(TADDR temporaryEntryPoints, int count)
+{
+ WRAPPER_NO_CONTRACT;
+ SUPPORTS_DAC;
+
+ PrecodeType precodeType = PTR_Precode(temporaryEntryPoints)->GetType();
+#ifdef FIXUP_PRECODE_PREALLOCATE_DYNAMIC_METHOD_JUMP_STUBS
+ bool preallocateJumpStubs =
+ precodeType == PRECODE_FIXUP &&
+ ((PTR_MethodDesc)((PTR_FixupPrecode)temporaryEntryPoints)->GetMethodDesc())->IsLCGMethod();
+#else // !FIXUP_PRECODE_PREALLOCATE_DYNAMIC_METHOD_JUMP_STUBS
+ bool preallocateJumpStubs = false;
+#endif // FIXUP_PRECODE_PREALLOCATE_DYNAMIC_METHOD_JUMP_STUBS
+ return SizeOfTemporaryEntryPoints(precodeType, preallocateJumpStubs, count);
+}
+
#ifndef DACCESS_COMPILE
Precode* Precode::Allocate(PrecodeType t, MethodDesc* pMD,
int count = pChunk->GetCount();
PrecodeType t = PRECODE_STUB;
+ bool preallocateJumpStubs = false;
#ifdef HAS_FIXUP_PRECODE
// Default to faster fixup precode if possible
if (!pFirstMD->RequiresMethodDescCallingConvention(count > 1))
{
t = PRECODE_FIXUP;
+
+#ifdef FIXUP_PRECODE_PREALLOCATE_DYNAMIC_METHOD_JUMP_STUBS
+ if (pFirstMD->IsLCGMethod())
+ {
+ preallocateJumpStubs = true;
+ }
+#endif // FIXUP_PRECODE_PREALLOCATE_DYNAMIC_METHOD_JUMP_STUBS
+ }
+ else
+ {
+ _ASSERTE(!pFirstMD->IsLCGMethod());
}
#endif // HAS_FIXUP_PRECODE
- SIZE_T totalSize = SizeOfTemporaryEntryPoints(t, count);
+ SIZE_T totalSize = SizeOfTemporaryEntryPoints(t, preallocateJumpStubs, count);
#ifdef HAS_COMPACT_ENTRYPOINTS
// Note that these are just best guesses to save memory. If we guessed wrong,
static Precode * GetPrecodeForTemporaryEntryPoint(TADDR temporaryEntryPoints, int index);
- static SIZE_T SizeOfTemporaryEntryPoints(PrecodeType t, int count);
- static SIZE_T SizeOfTemporaryEntryPoints(TADDR temporaryEntryPoints, int count)
- {
- WRAPPER_NO_CONTRACT;
- SUPPORTS_DAC;
- return SizeOfTemporaryEntryPoints(PTR_Precode(temporaryEntryPoints)->GetType(), count);
- }
+ static SIZE_T SizeOfTemporaryEntryPoints(PrecodeType t, bool preallocateJumpStubs, int count);
+ static SIZE_T SizeOfTemporaryEntryPoints(TADDR temporaryEntryPoints, int count);
static TADDR AllocateTemporaryEntryPoints(MethodDescChunk* pChunk,
LoaderAllocator *pLoaderAllocator, AllocMemTracker *pamTracker);
--- /dev/null
+// 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.
+
+using System;
+using System.Reflection;
+using System.Reflection.Emit;
+using System.Runtime.InteropServices;
+using System.Threading;
+
+public static class DynamicMethodJumpStubTests
+{
+ private static int Main()
+ {
+ DynamicMethodJumpStubTest();
+ return 100;
+ }
+
+ public static void DynamicMethodJumpStubTest()
+ {
+ if (!Environment.Is64BitProcess)
+ {
+ return;
+ }
+
+ // Reserve memory around framework libraries. This is just a best attempt, it typically doesn't help since the
+ // precode allocator may have already committed pages it can allocate from, or it may commit reserved pages close to
+ // framework libraries.
+ ReserveMemoryAround(new Action(ExecutionContext.RestoreFlow).Method.MethodHandle);
+
+ for (int i = 0; i < 64; ++i)
+ {
+ DynamicMethod dynamicMethod = CreateDynamicMethod("DynMethod" + i);
+ Action dynamicMethodDelegate = (Action)dynamicMethod.CreateDelegate(typeof(Action));
+
+ // Before compiling the dynamic method, reserve memory around its current entry point, which should be its
+ // precode. Then, when compiling the method, there would be a good chance that the code will be located far from
+ // the precode, forcing the use of a jump stub.
+ ReserveMemoryAround(
+ (RuntimeMethodHandle)
+ typeof(DynamicMethod).InvokeMember(
+ "GetMethodDescriptor",
+ BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.NonPublic,
+ null,
+ dynamicMethod,
+ null));
+
+ dynamicMethodDelegate();
+ }
+
+ // This test does not release reserved pages because they may have been committed by other components on the system
+ }
+
+ private static DynamicMethod CreateDynamicMethod(string name)
+ {
+ var dynamicMethod = new DynamicMethod(name, null, null);
+ ILGenerator ilGenerator = dynamicMethod.GetILGenerator();
+ ilGenerator.Emit(OpCodes.Ret);
+ return dynamicMethod;
+ }
+
+ private const uint AllocationGranularity = (uint)64 << 10;
+ private const ulong ReserveRangeRadius = (ulong)4 << 30; // reserve 4 GB before and after the base address
+
+ private static void ReserveMemoryAround(RuntimeMethodHandle methodHandle)
+ {
+ ulong baseAddress = (ulong)methodHandle.Value.ToInt64();
+
+ ulong low = baseAddress - ReserveRangeRadius;
+ if (low > baseAddress)
+ {
+ low = ulong.MinValue;
+ }
+ else
+ {
+ low &= ~((ulong)AllocationGranularity - 1);
+ }
+
+ ulong high = baseAddress + ReserveRangeRadius;
+ if (high < baseAddress)
+ {
+ high = ulong.MaxValue;
+ }
+
+ ulong address = low;
+ while (address <= high)
+ {
+ VirtualAlloc(
+ new UIntPtr(address),
+ new UIntPtr(AllocationGranularity),
+ AllocationType.RESERVE,
+ MemoryProtection.NOACCESS);
+
+ if (address + AllocationGranularity < address)
+ {
+ break;
+ }
+ address += AllocationGranularity;
+ }
+ }
+
+ [Flags]
+ private enum AllocationType : uint
+ {
+ COMMIT = 0x1000,
+ RESERVE = 0x2000,
+ RESET = 0x80000,
+ LARGE_PAGES = 0x20000000,
+ PHYSICAL = 0x400000,
+ TOP_DOWN = 0x100000,
+ WRITE_WATCH = 0x200000
+ }
+
+ [Flags]
+ private enum MemoryProtection : uint
+ {
+ EXECUTE = 0x10,
+ EXECUTE_READ = 0x20,
+ EXECUTE_READWRITE = 0x40,
+ EXECUTE_WRITECOPY = 0x80,
+ NOACCESS = 0x01,
+ READONLY = 0x02,
+ READWRITE = 0x04,
+ WRITECOPY = 0x08,
+ GUARD_Modifierflag = 0x100,
+ NOCACHE_Modifierflag = 0x200,
+ WRITECOMBINE_Modifierflag = 0x400
+ }
+
+ [DllImport("kernel32.dll", SetLastError = true)]
+ private static extern UIntPtr VirtualAlloc(
+ UIntPtr lpAddress,
+ UIntPtr dwSize,
+ AllocationType flAllocationType,
+ MemoryProtection flProtect);
+}
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">x64</Platform>
+ <AssemblyName>DynMethodJumpStubTests</AssemblyName>
+ <ProjectGuid>{742D9E05-668D-4B90-97F9-717A7572FE6C}</ProjectGuid>
+ <OutputType>Exe</OutputType>
+ <FileAlignment>512</FileAlignment>
+ </PropertyGroup>
+ <!-- Default configurations to help VS understand the configurations -->
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
+ </PropertyGroup>
+ <ItemGroup>
+ <Compile Include="DynMethodJumpStubTests.cs" />
+ </ItemGroup>
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+</Project>
+CoreMangLib/cti/system/reflection/emit/DynMethodJumpStubTests/DynMethodJumpStubTests/DynMethodJumpStubTests.sh
JIT/Directed/tailcall/tailcall/tailcall.sh
JIT/Methodical/tailcall_v4/hijacking/hijacking.sh
JIT/Methodical/tailcall_v4/smallFrame/smallFrame.sh
CoreMangLib/cti/system/double/DoubleToString4/DoubleToString4.sh
CoreMangLib/cti/system/int/Int32ToString3/Int32ToString3.sh
CoreMangLib/cti/system/int64/Int64ToString3/Int64ToString3.sh
+CoreMangLib/cti/system/reflection/emit/DynMethodJumpStubTests/DynMethodJumpStubTests/DynMethodJumpStubTests.sh
CoreMangLib/cti/system/string/StringCompare1/StringCompare1.sh
CoreMangLib/cti/system/string/StringCompare15/StringCompare15.sh
CoreMangLib/cti/system/string/StringCompare2/StringCompare2.sh