summaryrefslogtreecommitdiff
path: root/src/vm/method.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/vm/method.cpp')
-rw-r--r--src/vm/method.cpp221
1 files changed, 218 insertions, 3 deletions
diff --git a/src/vm/method.cpp b/src/vm/method.cpp
index 77a6a0d37f..34ae6d9489 100644
--- a/src/vm/method.cpp
+++ b/src/vm/method.cpp
@@ -4571,6 +4571,35 @@ c_CentralJumpCode = {
};
#include <poppack.h>
+#elif defined(_TARGET_ARM_)
+
+#include <pshpack1.h>
+struct CentralJumpCode {
+ BYTE m_ldrPC[4];
+ BYTE m_short[2];
+ MethodDescChunk *m_pChunk;
+ PCODE m_target;
+
+ inline void Setup(PCODE target, MethodDescChunk *pChunk) {
+ WRAPPER_NO_CONTRACT;
+
+ m_target = target;
+ m_pChunk = pChunk;
+ }
+
+ inline BOOL CheckTarget(TADDR target) {
+ WRAPPER_NO_CONTRACT;
+ return ((TADDR)m_target == target);
+ }
+}
+c_CentralJumpCode = {
+ { 0xDF, 0xF8, 0x08, 0xF0 }, // ldr pc, =pTarget
+ { 0x00, 0x00 }, // short offset for alignment
+ 0, // pChunk
+ 0 // pTarget
+};
+#include <poppack.h>
+
#else
#error Unsupported platform
#endif
@@ -4580,10 +4609,92 @@ typedef DPTR(struct CentralJumpCode) PTR_CentralJumpCode;
static_assert_no_msg((TEP_CENTRAL_JUMP_SIZE & 1) == 0);
#define TEP_ENTRY_SIZE 4
+
+#ifdef _TARGET_ARM_
+
+#define TEP_HALF_ENTRY_SIZE (TEP_ENTRY_SIZE / 2)
+
+// Compact entry point on arm consists of two thumb instructions:
+// mov r12, pc
+// b CentralJumpCode
+
+// First instruction 0x46fc
+#define TEP_ENTRY_INSTR1_BYTE1 0xFC
+#define TEP_ENTRY_INSTR1_BYTE2 0x46
+
+// Mask for unconditional branch opcode
+#define TEP_ENTRY_INSTR2_MASK1 0xE0
+
+// Mask for opcode
+#define TEP_ENTRY_INSTR2_MASK2 0xF8
+
+// Bit used for ARM to identify compact entry points
+#define COMPACT_ENTRY_ARM_CODE 0x2
+
+/* static */ int MethodDescChunk::GetCompactEntryPointMaxCount ()
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+
+ return MAX_OFFSET_UNCONDITIONAL_BRANCH_THUMB / TEP_ENTRY_SIZE;
+}
+
+// Get offset from the start of current compact entry point to the CentralJumpCode
+static uint16_t DecodeOffsetFromBranchToCentralJump (uint16_t instr)
+{
+ int16_t offset = decodeUnconditionalBranchThumb ((LPBYTE) &instr);
+
+ offset += PC_REG_RELATIVE_OFFSET + TEP_HALF_ENTRY_SIZE;
+
+ _ASSERTE (offset >= TEP_ENTRY_SIZE && (offset % TEP_ENTRY_SIZE == 0));
+
+ return (uint16_t) offset;
+}
+
+#ifndef DACCESS_COMPILE
+
+// Encode branch instruction to central jump for current compact entry point
+static uint16_t EncodeBranchToCentralJump (int16_t offset)
+{
+ _ASSERTE (offset >= 0 && (offset % TEP_ENTRY_SIZE == 0));
+
+ offset += TEP_HALF_ENTRY_SIZE - PC_REG_RELATIVE_OFFSET;
+
+ uint16_t instr;
+ emitUnconditionalBranchThumb ((LPBYTE) &instr, offset);
+
+ return instr;
+}
+
+#endif // DACCESS_COMPILE
+
+#else // _TARGET_ARM_
+
#define TEP_MAX_BEFORE_INDEX (1 + (127 / TEP_ENTRY_SIZE))
#define TEP_MAX_BLOCK_INDEX (TEP_MAX_BEFORE_INDEX + (128 - TEP_CENTRAL_JUMP_SIZE) / TEP_ENTRY_SIZE)
#define TEP_FULL_BLOCK_SIZE (TEP_MAX_BLOCK_INDEX * TEP_ENTRY_SIZE + TEP_CENTRAL_JUMP_SIZE)
+#endif // _TARGET_ARM_
+
+BOOL MethodDescChunk::IsCompactEntryPointAtAddress(PCODE addr)
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+
+#if defined(_TARGET_X86_) || defined(_TARGET_AMD64_)
+
+ // Compact entrypoints start at odd addresses
+ return (addr & 1) != 0;
+
+#elif defined(_TARGET_ARM_)
+
+ // Compact entrypoints start at odd addresses (thumb) with second bit set to 1
+ uint8_t compactEntryPointMask = THUMB_CODE | COMPACT_ENTRY_ARM_CODE;
+ return (addr & compactEntryPointMask) == compactEntryPointMask;
+
+#else
+ #error Unsupported platform
+#endif
+}
+
//*******************************************************************************
/* static */ MethodDesc* MethodDescChunk::GetMethodDescFromCompactEntryPoint(PCODE addr, BOOL fSpeculative /*=FALSE*/)
{
@@ -4597,18 +4708,39 @@ static_assert_no_msg((TEP_CENTRAL_JUMP_SIZE & 1) == 0);
// Always do consistency check in debug
if (fSpeculative INDEBUG(|| TRUE))
{
+#ifdef _TARGET_ARM_
+ if (!IsCompactEntryPointAtAddress(addr))
+#else // _TARGET_ARM_
if ((addr & 3) != 1 ||
*PTR_BYTE(addr) != X86_INSTR_MOV_AL ||
*PTR_BYTE(addr+2) != X86_INSTR_JMP_REL8)
+#endif // _TARGET_ARM_
{
if (fSpeculative) return NULL;
_ASSERTE(!"Unexpected code in temporary entrypoint");
}
}
+#ifdef _TARGET_ARM_
+
+ // On ARM compact entry points are thumb
+ _ASSERTE ((addr & THUMB_CODE) != 0);
+ addr = addr - THUMB_CODE;
+
+ // Get offset for CentralJumpCode from current compact entry point
+ PTR_UINT16 pBranchInstr = (PTR_UINT16(addr)) + 1;
+ uint16_t offset = DecodeOffsetFromBranchToCentralJump (*pBranchInstr);
+
+ TADDR centralJump = addr + offset;
+ int index = (centralJump - addr - TEP_ENTRY_SIZE) / TEP_ENTRY_SIZE;
+
+#else // _TARGET_ARM_
+
int index = *PTR_BYTE(addr+1);
TADDR centralJump = addr + 4 + *PTR_SBYTE(addr+3);
+#endif // _TARGET_ARM_
+
CentralJumpCode* pCentralJumpCode = PTR_CentralJumpCode(centralJump);
// Always do consistency check in debug
@@ -4625,10 +4757,42 @@ static_assert_no_msg((TEP_CENTRAL_JUMP_SIZE & 1) == 0);
}
}
+#ifdef _TARGET_ARM_
+
+ _ASSERTE_IMPL(pCentralJumpCode->CheckTarget(GetPreStubCompactARMEntryPoint()));
+
+#else // _TARGET_ARM_
+
_ASSERTE_IMPL(pCentralJumpCode->CheckTarget(GetPreStubEntryPoint()));
+
+#endif // _TARGET_ARM_
}
+#ifdef _TARGET_ARM_
+ // Go through all MethodDesc in MethodDescChunk and find the one with the required index
+ PTR_MethodDescChunk pChunk = *((DPTR(PTR_MethodDescChunk))(centralJump + offsetof(CentralJumpCode, m_pChunk)));
+ TADDR pMD = PTR_HOST_TO_TADDR (pChunk->GetFirstMethodDesc ());
+
+ _ASSERTE (index >= 0 && index < ((int) pChunk->GetCount ()));
+
+ index = ((int) pChunk->GetCount ()) - 1 - index;
+
+ SIZE_T totalSize = 0;
+ int curIndex = 0;
+
+ while (index != curIndex)
+ {
+ SIZE_T sizeCur = (PTR_MethodDesc (pMD))->SizeOf ();
+ totalSize += sizeCur;
+
+ pMD += sizeCur;
+ ++curIndex;
+ }
+
+ return PTR_MethodDesc (pMD);
+#else // _TARGET_ARM_
return PTR_MethodDesc((TADDR)pCentralJumpCode->m_pBaseMD + index * MethodDesc::ALIGNMENT);
+#endif // _TARGET_ARM_
}
//*******************************************************************************
@@ -4636,11 +4800,19 @@ SIZE_T MethodDescChunk::SizeOfCompactEntryPoints(int count)
{
LIMITED_METHOD_DAC_CONTRACT;
+#ifdef _TARGET_ARM_
+
+ return COMPACT_ENTRY_ARM_CODE + count * TEP_ENTRY_SIZE + TEP_CENTRAL_JUMP_SIZE;
+
+#else // _TARGET_ARM_
+
int fullBlocks = count / TEP_MAX_BLOCK_INDEX;
int remainder = count % TEP_MAX_BLOCK_INDEX;
return 1 + (fullBlocks * TEP_FULL_BLOCK_SIZE) +
(remainder * TEP_ENTRY_SIZE) + ((remainder != 0) ? TEP_CENTRAL_JUMP_SIZE : 0);
+
+#endif // _TARGET_ARM_
}
#ifndef DACCESS_COMPILE
@@ -4657,16 +4829,37 @@ TADDR MethodDescChunk::AllocateCompactEntryPoints(LoaderAllocator *pLoaderAlloca
TADDR temporaryEntryPoints = (TADDR)pamTracker->Track(pLoaderAllocator->GetPrecodeHeap()->AllocAlignedMem(size, sizeof(TADDR)));
+#ifdef _TARGET_ARM_
+ BYTE* p = (BYTE*)temporaryEntryPoints + COMPACT_ENTRY_ARM_CODE;
+ int relOffset = count * TEP_ENTRY_SIZE - TEP_ENTRY_SIZE; // relative offset for the short jump
+
+ _ASSERTE (relOffset < MAX_OFFSET_UNCONDITIONAL_BRANCH_THUMB);
+#else // _TARGET_ARM_
// make the temporary entrypoints unaligned, so they are easy to identify
BYTE* p = (BYTE*)temporaryEntryPoints + 1;
+ int indexInBlock = TEP_MAX_BLOCK_INDEX; // recompute relOffset in first iteration
+ int relOffset = 0; // relative offset for the short jump
+#endif // _TARGET_ARM_
- int indexInBlock = TEP_MAX_BLOCK_INDEX; // recompute relOffset in first iteration
- int relOffset = 0; // relative offset for the short jump
MethodDesc * pBaseMD = 0; // index of the start of the block
MethodDesc * pMD = GetFirstMethodDesc();
for (int index = 0; index < count; index++)
{
+#ifdef _TARGET_ARM_
+
+ uint8_t *pMovInstrByte1 = (uint8_t *)p;
+ uint8_t *pMovInstrByte2 = (uint8_t *)p+1;
+ uint16_t *pBranchInstr = ((uint16_t *)p)+1;
+
+ *pMovInstrByte1 = TEP_ENTRY_INSTR1_BYTE1;
+ *pMovInstrByte2 = TEP_ENTRY_INSTR1_BYTE2;
+ *pBranchInstr = EncodeBranchToCentralJump ((int16_t) relOffset);
+
+ p += TEP_ENTRY_SIZE;
+
+#else // _TARGET_ARM_
+
if (indexInBlock == TEP_MAX_BLOCK_INDEX)
{
relOffset = (min(count - index, TEP_MAX_BEFORE_INDEX) - 1) * TEP_ENTRY_SIZE;
@@ -4698,14 +4891,28 @@ TADDR MethodDescChunk::AllocateCompactEntryPoints(LoaderAllocator *pLoaderAlloca
relOffset -= TEP_CENTRAL_JUMP_SIZE;
}
- relOffset -= TEP_ENTRY_SIZE;
indexInBlock++;
+#endif // _TARGET_ARM_
+
+ relOffset -= TEP_ENTRY_SIZE;
pMD = (MethodDesc *)((BYTE *)pMD + pMD->SizeOf());
}
+#ifdef _TARGET_ARM_
+
+ CentralJumpCode* pCode = (CentralJumpCode*)p;
+ memcpy(pCode, &c_CentralJumpCode, TEP_CENTRAL_JUMP_SIZE);
+ pCode->Setup (GetPreStubCompactARMEntryPoint(), this);
+
+ _ASSERTE(p + TEP_CENTRAL_JUMP_SIZE == (BYTE*)temporaryEntryPoints + size);
+
+#else // _TARGET_ARM_
+
_ASSERTE(p == (BYTE*)temporaryEntryPoints + size);
+#endif // _TARGET_ARM_
+
ClrFlushInstructionCache((LPVOID)temporaryEntryPoints, size);
SetHasCompactEntryPoints();
@@ -4725,11 +4932,19 @@ PCODE MethodDescChunk::GetTemporaryEntryPoint(int index)
#ifdef HAS_COMPACT_ENTRYPOINTS
if (HasCompactEntryPoints())
{
+#ifdef _TARGET_ARM_
+
+ return GetTemporaryEntryPoints() + COMPACT_ENTRY_ARM_CODE + THUMB_CODE + index * TEP_ENTRY_SIZE;
+
+#else // _TARGET_ARM_
+
int fullBlocks = index / TEP_MAX_BLOCK_INDEX;
int remainder = index % TEP_MAX_BLOCK_INDEX;
return GetTemporaryEntryPoints() + 1 + (fullBlocks * TEP_FULL_BLOCK_SIZE) +
(remainder * TEP_ENTRY_SIZE) + ((remainder >= TEP_MAX_BEFORE_INDEX) ? TEP_CENTRAL_JUMP_SIZE : 0);
+
+#endif // _TARGET_ARM_
}
#endif // HAS_COMPACT_ENTRYPOINTS