summaryrefslogtreecommitdiff
path: root/src/vm/arm/stubs.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/vm/arm/stubs.cpp')
-rw-r--r--src/vm/arm/stubs.cpp925
1 files changed, 41 insertions, 884 deletions
diff --git a/src/vm/arm/stubs.cpp b/src/vm/arm/stubs.cpp
index 078a16cb8f..18eb9969c7 100644
--- a/src/vm/arm/stubs.cpp
+++ b/src/vm/arm/stubs.cpp
@@ -1682,526 +1682,75 @@ void StubLinkerCPU::ThumbEmitCallManagedMethod(MethodDesc *pMD, bool fTailcall)
}
}
-// Common code used to generate either an instantiating method stub or an unboxing stub (in the case where the
-// unboxing stub also needs to provide a generic instantiation parameter). The stub needs to add the
-// instantiation parameter provided in pHiddenArg and re-arrange the rest of the incoming arguments as a
-// result (since on ARM this hidden parameter is inserted before explicit user arguments we need a type of
-// shuffle thunk in the reverse direction of the type used for static delegates). If pHiddenArg == NULL it
-// indicates that we're in the unboxing case and should add sizeof(MethodTable*) to the incoming this pointer
-// before dispatching to the target. In this case the instantiating parameter is always the non-shared
-// MethodTable pointer we can deduce directly from the incoming 'this' reference.
-void StubLinkerCPU::ThumbEmitCallWithGenericInstantiationParameter(MethodDesc *pMD, void *pHiddenArg)
+VOID StubLinkerCPU::EmitComputedInstantiatingMethodStub(MethodDesc* pSharedMD, struct ShuffleEntry *pShuffleEntryArray, void* extraArg)
{
- // There is a simple case and a complex case.
- // 1) In the simple case the addition of the hidden arg doesn't push any user args onto the stack. In
- // this case we only have to re-arrange/initialize some argument registers and tail call to the
- // target.
- // 2) In the complex case we have to modify the stack by pushing some of the register based user
- // arguments. We can't tail call in this case because we've altered the size of the stack and our
- // caller doesn't expect this and can't compensate. Instead we'll need to create a stack frame
- // (including an explicit Frame to make it crawlable to the runtime) and copy the incoming arguments
- // over.
- //
- // First we need to analyze the signature of the target method both with and without the extra
- // instantiation argument. We use ArgIterator to determine the difference in location
- // (register or stack offset) for each argument between the two cases. This forms a set instructions that
- // tell us how to copy incoming arguments into outgoing arguments (and if those instructions don't include
- // any writes to stack locations in the outgoing case then we know we can generate a simple thunk).
-
- SigTypeContext sTypeContext(pMD, TypeHandle());
-
- // Incoming, source, method signature.
- MetaSig sSrcSig(pMD->GetSignature(),
- pMD->GetModule(),
- &sTypeContext,
- MetaSig::sigMember);
-
- // Outgoing, destination, method signature.
- MetaSig sDstSig(pMD->GetSignature(),
- pMD->GetModule(),
- &sTypeContext,
- MetaSig::sigMember);
-
- sDstSig.SetHasParamTypeArg();
-
- // Wrap calling convention parsers round the source and destination signatures. These will be responsible
- // for determining where each argument lives in registers or on the stack.
- ArgIterator sSrcArgLocations(&sSrcSig);
- ArgIterator sDstArgLocations(&sDstSig);
-
- // Define an argument descriptor type that describes how a single 4 byte portion of an argument is mapped
- // in the source and destination signature. We only have to worry about general registers and stack
- // locations here; floating point argument registers are left unmodified by this thunk.
- struct ArgDesc
- {
- int m_idxSrc; // Source register or stack offset
- int m_idxDst; // Destination register or stack offset
- bool m_fSrcIsReg; // Source index is a register number
- bool m_fDstIsReg; // Destination index is a register number
- };
-
- // The number of argument move descriptors we'll need is a function of the number of 4-byte registers or
- // stack slots the arguments occupy. The following calculation will over-estimate in a few side cases, but
- // not by much (it assumes all four argument registers are used plus the number of stack slots that
- // MetaSig calculates are needed for the rest of the arguments).
- DWORD cArgDescriptors = 4 + (sSrcArgLocations.SizeOfArgStack() / 4);
-
- // Allocate the array of argument descriptors.
- CQuickArray<ArgDesc> rgArgDescs;
- rgArgDescs.AllocThrows(cArgDescriptors);
-
- // We only need to map translations for arguments that could come after the instantiation parameter we're
- // inserting. On the ARM the only implicit argument that could follow is a vararg signature cookie, but
- // it's disallowed in this case. So we simply walk the user arguments.
- _ASSERTE(!sSrcSig.IsVarArg());
-
- INT srcOffset;
- INT dstOffset;
-
- DWORD idxCurrentDesc = 0;
- while ((srcOffset = sSrcArgLocations.GetNextOffset()) != TransitionBlock::InvalidOffset)
- {
- dstOffset = sDstArgLocations.GetNextOffset();
-
- // Get the placement for a single argument in the source and destination signatures (may include
- // multiple registers and/or stack locations if the argument is larger than 4 bytes).
- ArgLocDesc sSrcArgLoc;
- sSrcArgLocations.GetArgLoc(srcOffset, &sSrcArgLoc);
- ArgLocDesc sDstArgLoc;
- sDstArgLocations.GetArgLoc(dstOffset, &sDstArgLoc);
-
- // Fill in as many single-slot descriptors as the argument needs. Note that we ignore any floating
- // point register cases (m_cFloatReg > 0) since these will never change due to the hidden arg
- // insertion.
- while (sSrcArgLoc.m_cGenReg || sSrcArgLoc.m_cStack)
- {
- _ASSERTE(idxCurrentDesc < cArgDescriptors);
-
- if (sSrcArgLoc.m_cGenReg)
- {
- sSrcArgLoc.m_cGenReg--;
- rgArgDescs[idxCurrentDesc].m_idxSrc = sSrcArgLoc.m_idxGenReg++;
- rgArgDescs[idxCurrentDesc].m_fSrcIsReg = true;
- }
- else
- {
- _ASSERTE(sSrcArgLoc.m_cStack > 0);
- sSrcArgLoc.m_cStack--;
- rgArgDescs[idxCurrentDesc].m_idxSrc = sSrcArgLoc.m_idxStack++;
- rgArgDescs[idxCurrentDesc].m_fSrcIsReg = false;
- }
-
- if (sDstArgLoc.m_cGenReg)
- {
- sDstArgLoc.m_cGenReg--;
- rgArgDescs[idxCurrentDesc].m_idxDst = sDstArgLoc.m_idxGenReg++;
- rgArgDescs[idxCurrentDesc].m_fDstIsReg = true;
- }
- else
- {
- _ASSERTE(sDstArgLoc.m_cStack > 0);
- sDstArgLoc.m_cStack--;
- rgArgDescs[idxCurrentDesc].m_idxDst = sDstArgLoc.m_idxStack++;
- rgArgDescs[idxCurrentDesc].m_fDstIsReg = false;
- }
-
- idxCurrentDesc++;
- }
- }
-
- bool isRelative = MethodTable::VTableIndir2_t::isRelative
- && pMD->IsVtableSlot();
-
-#ifndef FEATURE_NGEN_RELOCS_OPTIMIZATIONS
- _ASSERTE(!isRelative);
-#endif
-
- // Update descriptor count to the actual number used.
- cArgDescriptors = idxCurrentDesc;
-
- // Note the position at which we have the first move to a stack location
- DWORD idxFirstMoveToStack = -1;
+ STANDARD_VM_CONTRACT;
- // We have a problem where register to register moves are concerned. Since we're adding an argument the
- // moves will be from a lower numbered register to a higher numbered one (e.g. r0 -> r1). But the argument
- // descriptors we just produced will order them starting from the lowest registers. If we emit move
- // instructions in this order we'll end up copying the value of the lowest register into all of the rest
- // (e.g. r0 -> r1, r1 -> r2 etc.). We don't have this problem with stack based arguments since the
- // argument stacks don't overlap in the same fashion. To solve this we'll reverse the order of the
- // descriptors with register destinations (there will be at most four of these so it's fairly cheap).
- if (cArgDescriptors > 1)
+ struct ShuffleEntry *pEntry = pShuffleEntryArray;
+ while (pEntry->srcofs != ShuffleEntry::SENTINEL)
{
- // Start by assuming we have all four register destination descriptors.
- int idxLastRegDesc = min(3, cArgDescriptors - 1);
+ _ASSERTE(pEntry->dstofs & ShuffleEntry::REGMASK);
+ _ASSERTE(pEntry->srcofs & ShuffleEntry::REGMASK);
+ _ASSERTE(!(pEntry->dstofs & ShuffleEntry::FPREGMASK));
+ _ASSERTE(!(pEntry->srcofs & ShuffleEntry::FPREGMASK));
+ _ASSERTE(pEntry->dstofs != ShuffleEntry::HELPERREG);
+ _ASSERTE(pEntry->srcofs != ShuffleEntry::HELPERREG);
- // Adjust that count to match reality.
- while (idxLastRegDesc >= 0 && !rgArgDescs[idxLastRegDesc].m_fDstIsReg)
- {
- idxLastRegDesc--;
- }
-
- if (idxLastRegDesc < 0)
- {
- // No register is used to pass any of the parameters. No need to reverse the order of the descriptors
- idxFirstMoveToStack = 0;
- }
- else
- {
- _ASSERTE(idxLastRegDesc >= 0 && ((DWORD)idxLastRegDesc) < cArgDescriptors);
-
- // First move to stack location happens after the last move to register location
- idxFirstMoveToStack = idxLastRegDesc+1;
-
- // Calculate how many descriptors we'll need to swap.
- DWORD cSwaps = (idxLastRegDesc + 1) / 2;
- // Finally we can swap the descriptors.
- int idxFirstRegDesc = 0;
- while (cSwaps)
- {
- ArgDesc sTempDesc = rgArgDescs[idxLastRegDesc];
- rgArgDescs[idxLastRegDesc] = rgArgDescs[idxFirstRegDesc];
- rgArgDescs[idxFirstRegDesc] = sTempDesc;
-
- _ASSERTE(idxFirstRegDesc < idxLastRegDesc);
- idxFirstRegDesc++;
- idxLastRegDesc--;
- cSwaps--;
- }
- }
+ ThumbEmitMovRegReg(ThumbReg(pEntry->dstofs & ShuffleEntry::OFSMASK),
+ ThumbReg(pEntry->srcofs & ShuffleEntry::OFSMASK));
+ pEntry++;
}
- // If we're ever required to write to the destination stack then we can't implement this case with a
- // simple tail call stub. (That's not technically true: there are edge cases caused by 64-bit alignment
- // requirements that might allow us to use a simple stub since the extra argument fits in a "hole" in the
- // arguments, but these are infrequent enough that it's likely not worth the effort of detecting them).
- ArgDesc *pLastArg = cArgDescriptors ? &rgArgDescs[cArgDescriptors - 1] : NULL;
- if ((pLastArg == NULL) || pLastArg->m_fDstIsReg)
+ MetaSig msig(pSharedMD);
+ ArgIterator argit(&msig);
+ if (argit.HasParamType())
{
- // Simple case where we can just rearrange a few argument registers and tail call.
-
- for (idxCurrentDesc = 0; idxCurrentDesc < cArgDescriptors; idxCurrentDesc++)
- {
- // Because we're in the simple case we know we'll never be asked to move a value onto the stack
- // and since we're adding a parameter we should never be required to move a value from the stack
- // to a register either. So all of the descriptors should be register to register moves.
- _ASSERTE(rgArgDescs[idxCurrentDesc].m_fSrcIsReg && rgArgDescs[idxCurrentDesc].m_fDstIsReg);
- ThumbEmitMovRegReg(ThumbReg(rgArgDescs[idxCurrentDesc].m_idxDst),
- ThumbReg(rgArgDescs[idxCurrentDesc].m_idxSrc));
- }
-
// Place instantiation parameter into the correct register.
ArgLocDesc sInstArgLoc;
- sDstArgLocations.GetParamTypeLoc(&sInstArgLoc);
+ argit.GetParamTypeLoc(&sInstArgLoc);
int regHidden = sInstArgLoc.m_idxGenReg;
_ASSERTE(regHidden != -1);
- if (pHiddenArg)
- {
- // mov regHidden, #pHiddenArg
- ThumbEmitMovConstant(ThumbReg(regHidden), (TADDR)pHiddenArg);
- }
- else
- {
- // Extract MethodTable pointer (the hidden arg) from the object instance.
- // ldr regHidden, [r0]
- ThumbEmitLoadRegIndirect(ThumbReg(regHidden), ThumbReg(0), 0);
- }
-
- if (pHiddenArg == NULL)
- {
- // Unboxing stub case.
-
- // Skip over the MethodTable* to find the address of the unboxed value type.
- // add r0, #sizeof(MethodTable*)
- ThumbEmitIncrement(ThumbReg(0), sizeof(MethodTable*));
- }
-
- // Emit a tail call to the target method.
- if (isRelative)
+ if (extraArg == NULL)
{
- ThumbEmitProlog(1, 0, FALSE);
- }
-
- ThumbEmitCallManagedMethod(pMD, true);
-
- if (isRelative)
- {
- ThumbEmitEpilog();
- }
- }
- else
- {
- // Complex case where we need to emit a new stack frame and copy the arguments.
-
- // Calculate the size of the new stack frame:
- //
- // +------------+
- // SP -> | | <-- Space for helper arg, if isRelative is true
- // +------------+
- // | | <-+
- // : : | Outgoing arguments
- // | | <-+
- // +------------+
- // | Padding | <-- Optional, maybe required so that SP is 64-bit aligned
- // +------------+
- // | GS Cookie |
- // +------------+
- // +-> | vtable ptr |
- // | +------------+
- // | | m_Next |
- // | +------------+
- // | | R4 | <-+
- // Stub | +------------+ |
- // Helper | : : |
- // Frame | +------------+ | Callee saved registers
- // | | R11 | |
- // | +------------+ |
- // | | LR/RetAddr | <-+
- // | +------------+
- // | | R0 | <-+
- // | +------------+ |
- // | : : | Argument registers
- // | +------------+ |
- // +-> | R3 | <-+
- // +------------+
- // Old SP -> | |
- //
- DWORD cbStackArgs = (pLastArg->m_idxDst + 1) * 4;
- DWORD cbStackFrame = cbStackArgs + sizeof(GSCookie) + sizeof(StubHelperFrame);
- cbStackFrame = ALIGN_UP(cbStackFrame, 8);
-
- if (isRelative)
- {
- cbStackFrame += 4;
- }
-
- DWORD cbStackFrameWithoutSavedRegs = cbStackFrame - (13 * 4); // r0-r11,lr
-
- // Prolog:
- ThumbEmitProlog(8, // Save r4-r11,lr (count doesn't include lr)
- cbStackFrameWithoutSavedRegs, // Additional space in the stack frame required
- TRUE); // Push argument registers
-
- DWORD offsetOfFrame = cbStackFrame - sizeof(StubHelperFrame);
-
- // Initialize and link the StubHelperFrame and associated GS cookie.
- EmitStubLinkFrame(StubHelperFrame::GetMethodFrameVPtr(), offsetOfFrame, StubHelperFrame::GetOffsetOfTransitionBlock());
-
- // Initialize temporary registers used when copying arguments:
- // r6 == pointer to first incoming stack-based argument
- // r7 == pointer to first outgoing stack-based argument
-
- // add r6, sp, #cbStackFrame
- ThumbEmitAdd(ThumbReg(6), thumbRegSp, cbStackFrame);
-
- // mov r7, sp
- ThumbEmitMovRegReg(ThumbReg(7), thumbRegSp);
-
- // Copy incoming to outgoing arguments. Stack arguments are generally written consecutively and as
- // such we use post-increment forms of register indirect addressing to keep our input (r6) and output
- // (r7) pointers up to date. But sometimes we'll skip four bytes due to 64-bit alignment requirements
- // and need to bump one or both of the pointers to compensate. We determine
- //
- // At this point, the ArgumentDescriptor array is divied into two parts:
- //
- // 1) Reverse sorted register to register moves (see the comment earlier in the method for details)
- // 2) Register or Stack to Stack moves (if any) in the original order.
- //
- // Its possible that the register to register moves may move to a target register that happens
- // to be a source for the register -> stack move. If this happens, and we emit the argument moves
- // in the current order, then we can lose the contents of the register involved in register->stack
- // move (stack->stack moves are not a problem as the locations dont overlap).
- //
- // To address this, we will emit the argument moves in two loops:
- //
- // 1) First loop will emit the moves that have stack location as the target
- // 2) Second loop will emit moves that have register as the target.
- DWORD idxCurrentLoopBegin = 0, idxCurrentLoopEnd = cArgDescriptors;
- if (idxFirstMoveToStack != -1)
- {
- _ASSERTE(idxFirstMoveToStack < cArgDescriptors);
- idxCurrentLoopBegin = idxFirstMoveToStack;
-
- for (idxCurrentDesc = idxCurrentLoopBegin; idxCurrentDesc < idxCurrentLoopEnd; idxCurrentDesc++)
+ if (pSharedMD->RequiresInstMethodTableArg())
{
- ArgDesc *pArgDesc = &rgArgDescs[idxCurrentDesc];
-
- if (pArgDesc->m_fSrcIsReg)
- {
- // Source value is in a register.
-
- _ASSERTE(!pArgDesc->m_fDstIsReg);
- // Register to stack. Calculate delta from last stack write; normally it will be 4 bytes
- // and our pointer has already been set up correctly by the post increment of the last
- // write. But in some cases we need to skip four bytes due to a 64-bit alignment
- // requirement. In those cases we need to emit an extra add to keep the pointer correct.
- // Note that the first stack argument is guaranteed to be 64-bit aligned by the ABI and as
- // such the first stack slot is never skipped.
- if ((pArgDesc->m_idxDst > 0) &&
- (pArgDesc->m_idxDst != (rgArgDescs[idxCurrentDesc - 1].m_idxDst + 1)))
- {
- _ASSERTE(pArgDesc->m_idxDst == (rgArgDescs[idxCurrentDesc - 1].m_idxDst + 2));
- ThumbEmitIncrement(ThumbReg(7), 4);
- }
-
- // str srcReg, [r7], #4
- ThumbEmitStoreIndirectPostIncrement(pArgDesc->m_idxSrc, ThumbReg(7), 4);
- }
- else
- {
- // Source value is on the stack. We should have no cases where a stack argument moves back to
- // a register (because we're adding an argument).
- _ASSERTE(!pArgDesc->m_fDstIsReg);
-
- // Stack to stack move. We need to use register (r6) to store the value temporarily between
- // the read and the write. See the comments above for why we need to check stack deltas and
- // possibly insert extra add instructions in some cases.
- if ((pArgDesc->m_idxSrc > 0) &&
- (pArgDesc->m_idxSrc != (rgArgDescs[idxCurrentDesc - 1].m_idxSrc + 1)))
- {
- _ASSERTE(pArgDesc->m_idxSrc == (rgArgDescs[idxCurrentDesc - 1].m_idxSrc + 2));
- ThumbEmitIncrement(ThumbReg(6), 4);
- }
- if ((pArgDesc->m_idxDst > 0) &&
- (pArgDesc->m_idxDst != (rgArgDescs[idxCurrentDesc - 1].m_idxDst + 1)))
- {
- _ASSERTE(pArgDesc->m_idxDst == (rgArgDescs[idxCurrentDesc - 1].m_idxDst + 2));
- ThumbEmitIncrement(ThumbReg(7), 4);
- }
-
- // ldr r8, [r6], #4
- ThumbEmitLoadIndirectPostIncrement(ThumbReg(8), ThumbReg(6), 4);
-
- // str r8, [r7], #4
- ThumbEmitStoreIndirectPostIncrement(ThumbReg(8), ThumbReg(7), 4);
- }
+ // Unboxing stub case
+ // Extract MethodTable pointer (the hidden arg) from the object instance.
+ // ldr regHidden, [r0]
+ ThumbEmitLoadRegIndirect(ThumbReg(regHidden), ThumbReg(0), 0);
}
-
- // Update the indexes to be used for the second loop
- idxCurrentLoopEnd = idxCurrentLoopBegin;
- idxCurrentLoopBegin = 0;
- }
-
- // Now, perform the register to register moves
- for (idxCurrentDesc = idxCurrentLoopBegin; idxCurrentDesc < idxCurrentLoopEnd; idxCurrentDesc++)
- {
- ArgDesc *pArgDesc = &rgArgDescs[idxCurrentDesc];
-
- // All moves to stack locations have been done (if applicable).
- // Since we are moving to a register destination, the source
- // will also be a register and cannot be a stack location (refer to the previous loop).
- _ASSERTE(pArgDesc->m_fSrcIsReg && pArgDesc->m_fDstIsReg);
-
- // Register to register case.
- ThumbEmitMovRegReg(pArgDesc->m_idxDst, pArgDesc->m_idxSrc);
- }
-
-
- // Place instantiation parameter into the correct register.
- ArgLocDesc sInstArgLoc;
- sDstArgLocations.GetParamTypeLoc(&sInstArgLoc);
- int regHidden = sInstArgLoc.m_idxGenReg;
- _ASSERTE(regHidden != -1);
- if (pHiddenArg)
- {
- // mov regHidden, #pHiddenArg
- ThumbEmitMovConstant(ThumbReg(regHidden), (TADDR)pHiddenArg);
}
else
{
- // Extract MethodTable pointer (the hidden arg) from the object instance.
- // ldr regHidden, [r0]
- ThumbEmitLoadRegIndirect(ThumbReg(regHidden), ThumbReg(0), 0);
- }
-
- if (pHiddenArg == NULL)
- {
- // Unboxing stub case.
-
- // Skip over the MethodTable* to find the address of the unboxed value type.
- // add r0, #sizeof(MethodTable*)
- ThumbEmitIncrement(ThumbReg(0), sizeof(MethodTable*));
+ // mov regHidden, #pHiddenArg
+ ThumbEmitMovConstant(ThumbReg(regHidden), (TADDR)extraArg);
}
-
- // Emit a regular (non-tail) call to the target method.
- ThumbEmitCallManagedMethod(pMD, false);
-
- // Unlink the StubHelperFrame.
- EmitStubUnlinkFrame();
-
- // Epilog
- ThumbEmitEpilog();
- }
-}
-
-#if defined(FEATURE_SHARE_GENERIC_CODE)
-// The stub generated by this method passes an extra dictionary argument before jumping to
-// shared-instantiation generic code.
-//
-// pSharedMD is either
-// * An InstantiatedMethodDesc for a generic method whose code is shared across instantiations.
-// In this case, the extra argument is the InstantiatedMethodDesc for the instantiation-specific stub itself.
-// or * A MethodDesc for a static method in a generic class whose code is shared across instantiations.
-// In this case, the extra argument is the MethodTable pointer of the instantiated type.
-VOID StubLinkerCPU::EmitInstantiatingMethodStub(MethodDesc* pSharedMD, void* extra)
-{
- CONTRACTL
- {
- THROWS;
- GC_TRIGGERS;
- INJECT_FAULT(COMPlusThrowOM(););
- PRECONDITION(pSharedMD->RequiresInstMethodTableArg() || pSharedMD->RequiresInstMethodDescArg());
}
- CONTRACTL_END;
-
- // Share code with the instantiating version of the unboxing stub (see below).
- ThumbEmitCallWithGenericInstantiationParameter(pSharedMD, extra);
-}
-#endif // FEATURE_SHARE_GENERIC_CODE
-
-void StubLinkerCPU::EmitUnboxMethodStub(MethodDesc *pMD)
-{
- if (pMD->RequiresInstMethodTableArg())
+ if (extraArg == NULL)
{
- // In this case we also have to add an instantiating parameter (which is always the MethodTable* from
- // the instance we're called on). Most of this code is shared with the instantiating method stub
- // above, the NULL parameter informs the emitter that we're both an unboxing stub and that the extra
- // parameter can be deduced from the 'this' reference.
- ThumbEmitCallWithGenericInstantiationParameter(pMD, NULL);
+ // Unboxing stub case
+ // Skip over the MethodTable* to find the address of the unboxed value type.
+ // add r0, #sizeof(MethodTable*)
+ ThumbEmitIncrement(ThumbReg(0), sizeof(MethodTable*));
}
- else
- {
- // We assume that we'll never see a case where a boxed value type method will require an instantiated
- // method desc as a parameter. The stubs on other platforms make this assumption (and indeed this
- // method isn't even passed an additional instantiation parameter). This is trivially true for the
- // non-interface call case: the only methods callable directly on the boxed instance are the methods
- // of Object, none of which are generic. For the interface dispatch case we're relying on the fact
- // that the jit always provides the instantiating argument explicitly.
- _ASSERTE(!pMD->RequiresInstMethodDescArg());
-
- // Address of the value type is address of the boxed instance plus four.
- // add r0, #4
- ThumbEmitIncrement(ThumbReg(0), 4);
- bool isRelative = MethodTable::VTableIndir2_t::isRelative
- && pMD->IsVtableSlot();
+ bool isRelative = MethodTable::VTableIndir2_t::isRelative
+ && pSharedMD->IsVtableSlot();
#ifndef FEATURE_NGEN_RELOCS_OPTIMIZATIONS
- _ASSERTE(!isRelative);
+ _ASSERTE(!isRelative);
#endif
+ if (isRelative)
+ {
+ ThumbEmitProlog(1, 0, FALSE);
+ }
- if (isRelative)
- {
- ThumbEmitProlog(1, 0, FALSE);
- }
-
- // Tail call the real target.
- ThumbEmitCallManagedMethod(pMD, true /* tail call */);
+ ThumbEmitCallManagedMethod(pSharedMD, true);
- if (isRelative)
- {
- ThumbEmitEpilog();
- }
+ if (isRelative)
+ {
+ ThumbEmitEpilog();
}
}
@@ -2558,355 +2107,6 @@ void InitJITHelpers1()
}
}
-// +64 stack-based arguments here
-// -- MulticastFrame end
-// +48 r0-r3 argument registers
-// +44 lr return address
-// +40 fp frame pointer
-// +12 r4-r10 callee saved registers
-// +8 datum (typically a MethodDesc*)
-// +4 m_Next
-// +0 the frame vptr
-// -- MulticastFrame start
-// -4 gs cookie
-// -... floating point argument registers
-void StubLinkerCPU::EmitMulticastInvoke(UINT_PTR hash)
-{
- //Decode Multicast Delegate hash
- unsigned int numStackBytes = hash >> 8;
- _ASSERTE(numStackBytes <= 0x7fff);
-
- unsigned int numFPRegs = (hash & 0xf8) >> 3;
- _ASSERTE(numFPRegs <= 16);
-
- unsigned int numGenRegs = hash & 0x7;
- _ASSERTE(numGenRegs <= 4);
-
- DWORD offsetOfFPRegs = 0;
-
- DWORD cbStackFrame = numStackBytes;
- if (numFPRegs)
- {
- cbStackFrame = ALIGN_UP(cbStackFrame, 8);
- offsetOfFPRegs = cbStackFrame;
- cbStackFrame += 4 * numFPRegs;
- }
- cbStackFrame += sizeof(GSCookie) + sizeof(MulticastFrame);
- cbStackFrame = ALIGN_UP(cbStackFrame, 8);
- DWORD cbStackFrameWithoutSavedRegs = cbStackFrame - (13 * 4); // r0-r11,lr
-
- // Prolog:
- ThumbEmitProlog(8, // Save r4-r11,lr (count doesn't include lr)
- cbStackFrameWithoutSavedRegs, // Additional space in the stack frame required
- TRUE); // Push argument registers
-
- DWORD offsetOfFrame = cbStackFrame - sizeof(MulticastFrame);
-
- // Move the MethodDesc* we're calling to r12.
- // ldr r12, [r0, #offsetof(DelegateObject, _methodPtrAux)]
- ThumbEmitLoadRegIndirect(ThumbReg(12), ThumbReg(0), DelegateObject::GetOffsetOfMethodPtrAux());
-
- // Initialize MulticastFrame::m_pMD to the MethodDesc* we're calling
- // str r12, [sp + #(offsetOfFrame + offsetof(MulticastFrame, m_pMD))]
- ThumbEmitStoreRegIndirect(ThumbReg(12), thumbRegSp, offsetOfFrame + MulticastFrame::GetOffsetOfDatum());
-
- if (numFPRegs)
- {
- ThumbEmitAdd(ThumbReg(4), thumbRegSp, offsetOfFPRegs);
-
- // save floating point arguments at offsetOfFPRegs
- //vstm{IA} R4,{s0-s(numFPRegs -1)}
- Emit16(0xec84);
- Emit16(0x0a00 | (WORD)numFPRegs);
- }
-
- // Initialize and link the MulticastFrame and associated GS cookie.
- EmitStubLinkFrame(MulticastFrame::GetMethodFrameVPtr(), offsetOfFrame, MulticastFrame::GetOffsetOfTransitionBlock());
-
- //r7 as counter. Initialize it to 0.
- // mov r7, 0
- ThumbEmitMovConstant(ThumbReg(7), 0);
-
- //initialize r9 to _invocationCount
- ThumbEmitLoadRegIndirect(ThumbReg(9), ThumbReg(0), DelegateObject::GetOffsetOfInvocationCount());
-
- CodeLabel *pLoopLabel = NewCodeLabel();
- CodeLabel *pEndLoopLabel = NewCodeLabel();
-
- //loop:
- EmitLabel(pLoopLabel);
-
- // cmp r7, r9
- ThumbEmitCmpReg(ThumbReg(7), ThumbReg(9));
-
- // if equal goto endloop
- // beq endloop
- ThumbEmitCondFlagJump(pEndLoopLabel, 0);
-
- UINT32 count = 0;
- if(numStackBytes)
- {
- //r1 = pos for stack args in Frame
- ThumbEmitAdd(ThumbReg(1), ThumbReg(4), MulticastFrame::GetOffsetOfTransitionBlock() + TransitionBlock::GetOffsetOfArgs());
-
- //r2 = stack pos for args of calling func
- ThumbEmitMovRegReg(ThumbReg(2), thumbRegSp);
-
- // ..move stack args..
- _ASSERTE(numStackBytes%4 == 0);
- while (count != numStackBytes)
- {
- ThumbEmitLoadIndirectPostIncrement(ThumbReg(0), ThumbReg(1), 4);
- ThumbEmitStoreIndirectPostIncrement(ThumbReg(0), ThumbReg(2), 4);
- count += 4;
- }
- }
-
- count = 1;
- while(count < numGenRegs)
- {
- ThumbEmitLoadRegIndirect(ThumbReg(count), ThumbReg(4), MulticastFrame::GetOffsetOfTransitionBlock() + TransitionBlock::GetOffsetOfArgumentRegisters() + count*4);
- count++;
- }
-
- if(numFPRegs)
- {
- ThumbEmitAdd(ThumbReg(0), thumbRegSp, offsetOfFPRegs);
- //vldm{IA}.32 R0, s0-s(numFPRegs-1)
- Emit16(0xec90);
- Emit16(0x0a00 | (WORD)numFPRegs);
- }
-
- //ldr r0, [r4+0x30] // get the first argument
- ThumbEmitLoadRegIndirect(ThumbReg(0),ThumbReg(4), MulticastFrame::GetOffsetOfTransitionBlock() + TransitionBlock::GetOffsetOfArgumentRegisters());
-
- // ldr r6, [r0+0x14] //invocationList
- ThumbEmitLoadRegIndirect(ThumbReg(6), ThumbReg(0), DelegateObject::GetOffsetOfInvocationList());
-
- // r6 - address of first delegate in invocation list
- // add r6,r6,0xC
- ThumbEmitAdd(ThumbReg(6), ThumbReg(6), PtrArray::GetDataOffset());
-
- //ldr r8,[r6+r7*4] //get delegate object
- ThumbEmitLoadOffsetScaledReg(ThumbReg(8), ThumbReg(6), ThumbReg(7), 2);
-
- // ldr r0, [r8+0x04] //_target from the delegate
- ThumbEmitLoadRegIndirect(ThumbReg(0), ThumbReg(8), DelegateObject::GetOffsetOfTarget());
-
- // ldr r8, [r8+0xC] // methodPtr from the delegate
- ThumbEmitLoadRegIndirect(ThumbReg(8), ThumbReg(8), DelegateObject::GetOffsetOfMethodPtr());
-
- //call delegate
- ThumbEmitCallRegister(ThumbReg(8));
-
- //increment counter
- ThumbEmitAdd(ThumbReg(7), ThumbReg(7), 1);
-
- // The debugger may need to stop here, so grab the offset of this code.
- EmitPatchLabel();
-
- //goto loop
- ThumbEmitNearJump(pLoopLabel);
-
- //endloop:
- EmitLabel(pEndLoopLabel);
-
-
- //At this point of the stub:
- //r4 must point to Frame
- //and r5 must be current Thread*
-
- EmitStubUnlinkFrame();
-
- // Epilog
- ThumbEmitEpilog();
-}
-
-void StubLinkerCPU::EmitSecureDelegateInvoke(UINT_PTR hash)
-{
- //Decode Multicast Delegate hash
- unsigned int numStackBytes = hash >> 8;
- _ASSERTE(numStackBytes <= 0x7fff);
-
- DWORD cbStackFrame = numStackBytes + sizeof(GSCookie) + sizeof(SecureDelegateFrame);
- cbStackFrame = ALIGN_UP(cbStackFrame, 8);
- DWORD cbStackFrameWithoutSavedRegs = cbStackFrame - (13 * 4); // r0-r11,lr
-
- // Prolog:
- ThumbEmitProlog(8, // Save r4-r11,lr (count doesn't include lr)
- cbStackFrameWithoutSavedRegs, // Additional space in the stack frame required
- TRUE); // Push argument registers
-
- DWORD offsetOfFrame = cbStackFrame - sizeof(SecureDelegateFrame);
-
- // Move the MethodDesc* we're calling to r12.
- // ldr r12, [r0, #offsetof(DelegateObject, _invocationCount)]
- ThumbEmitLoadRegIndirect(ThumbReg(12), ThumbReg(0), DelegateObject::GetOffsetOfInvocationCount());
-
- // Initialize SecureDelegateFrame::m_pMD to the MethodDesc* we're calling
- // str r12, [sp + #(offsetOfFrame + offsetof(SecureDelegateFrame, m_pMD))]
- ThumbEmitStoreRegIndirect(ThumbReg(12), thumbRegSp, offsetOfFrame + SecureDelegateFrame::GetOffsetOfDatum());
-
- // Initialize and link the SecureDelegateFrame and associated GS cookie.
- EmitStubLinkFrame(SecureDelegateFrame::GetMethodFrameVPtr(), offsetOfFrame, SecureDelegateFrame::GetOffsetOfTransitionBlock());
-
- // At this point:
- // r0 : secure delegate
- // r4 : SecureDelegateFrame *
- // r5 : Thread *
-
- if (numStackBytes)
- {
- // Copy stack based arguments from the calling frame into this one. Use the following registers:
- // r6 : pointer to source arguments
- // r7 : pointer to destination arguments
- // r8 : temporary storage during copy operation
-
- // add r6, r4, #MulticastFrame::GetOffsetOfArgs()
- ThumbEmitAdd(ThumbReg(6), ThumbReg(4), MulticastFrame::GetOffsetOfTransitionBlock() + TransitionBlock::GetOffsetOfArgs());
-
- // mov r7, sp
- ThumbEmitMovRegReg(ThumbReg(7), thumbRegSp);
-
- // Unrolled loop to copy the stack based arguments. Might want to consider a second path with a loop
- // for large argument lists if anyone complains about this.
- _ASSERTE((numStackBytes % 4) == 0);
- for (unsigned int i = 0; i < numStackBytes; i += 4)
- {
- // Read one 4-byte value from the source stack and copy it to the new stack, post-incrementing
- // both source and destination as we go.
- // ldr r8, [r6], #4
- // str r8, [r7], #4
- ThumbEmitLoadIndirectPostIncrement(ThumbReg(8), ThumbReg(6), 4);
- ThumbEmitStoreIndirectPostIncrement(ThumbReg(8), ThumbReg(7), 4);
- }
- }
-
- // Stack-based arguments are copied. Floating point argument registers and r1-r3 are all still correct.
- // All we need to do now is calculate the real value for r0 and the target address. Secure delegates wrap
- // an inner delegate (kept in _invocationList). We retrieve this inner delegate and then perform the usual
- // delegate invocation pattern on that.
-
- // Get "real" delegate.
- // ldr r0, [r0, #offsetof(DelegateObject, _invocationList)]
- ThumbEmitLoadRegIndirect(ThumbReg(0), ThumbReg(0), DelegateObject::GetOffsetOfInvocationList());
-
- // Load the destination address from the inner delegate.
- // ldr r12, [r0, #offsetof(DelegateObject, _methodPtr)]
- ThumbEmitLoadRegIndirect(ThumbReg(12), ThumbReg(0), DelegateObject::GetOffsetOfMethodPtr());
-
- // This is only required for unbound delegates which use VSD stubs..but does not harm if done unconditionally
- // add r4, r0+#offsetof(DelegateObject, _methodPtrAux) ; // r4 now contains indirection cell
- ThumbEmitAdd(ThumbReg(4), ThumbReg(0), DelegateObject::GetOffsetOfMethodPtrAux());
-
- // Replace the delegate reference with the object cached as the delegate's target.
- // ldr r0, [r0, #offsetof(DelegateObject, _target)]
- ThumbEmitLoadRegIndirect(ThumbReg(0), ThumbReg(0), DelegateObject::GetOffsetOfTarget());
-
- // Perform the call.
- // blx r12
- ThumbEmitCallRegister(ThumbReg(12));
-
- // restore frame pointer in r4
- ThumbEmitAdd(ThumbReg(4), thumbRegSp, offsetOfFrame);
-
- // Unlink SecureDelegateFrame. This requires the frame pointer in r4 and the thread pointer in r5.
- EmitStubUnlinkFrame();
-
- // Epilog
- ThumbEmitEpilog();
-}
-
-//The function expects r4 to point to frame
-//and r5 must be current Thread*
-void StubLinkerCPU::EmitStubUnlinkFrame()
-{
-#ifdef _DEBUG
- // EmitStubUnlinkFrame is emitted just before the epilog.
- // Thus, at this point, all other callee-saved registers
- // could be used since we are anyways going to restore them
- // via epilog execution.
-
- // Ensure that GSCookie is valid
- //
- // ldr r6, [r4-4]; Load the value of GSCookie
- ThumbEmitSub(ThumbReg(6), ThumbReg(4), 4);
- ThumbEmitLoadRegIndirect(ThumbReg(6), ThumbReg(6), 0);
-
- // mov r7, s_gsCookie
- ThumbEmitMovConstant(ThumbReg(7), GetProcessGSCookie());
-
- // cmp r6, r7 ; Are the GSCookie values in sync?
- ThumbEmitCmpReg(ThumbReg(6), ThumbReg(7));
-
- CodeLabel *pAllDoneLabel = NewCodeLabel();
-
- // beq AllDone; yes, GSCookie is good.
- ThumbEmitCondFlagJump(pAllDoneLabel, 0);
-
- // If we are here, then GSCookie was bad.
- // Call into DoJITFailFast.
- //
- // mov r12, DoJITFailFast
- ThumbEmitMovConstant(ThumbReg(12), (int)DoJITFailFast);
- // bl r12
- ThumbEmitCallRegister(ThumbReg(12));
- // Emit a breakpoint - we are not expected to come here at all
- // if we performed a FailFast.
- ThumbEmitBreakpoint();
-
- //AllDone:
- EmitLabel(pAllDoneLabel);
-#endif // _DEBUG
-
- // Unlink the MulticastFrame.
- // ldr r6, [r4 + #offsetof(MulticastFrame, m_Next)]
- // str r6, [r5 + #offsetof(Thread, m_pFrame)]
- ThumbEmitLoadRegIndirect(ThumbReg(6), ThumbReg(4), Frame::GetOffsetOfNextLink());
- ThumbEmitStoreRegIndirect(ThumbReg(6), ThumbReg(5), offsetof(Thread, m_pFrame));
-
-}
-
-//pFrameVptr = vtable ptr of Frame
-//offsetOfFrame = Frame offset in bytes from sp
-//After this method: r4 points to the Frame on stack
-// and r5 has current Thread*
-void StubLinkerCPU::EmitStubLinkFrame(TADDR pFrameVptr, int offsetOfFrame, int offsetOfTransitionBlock)
-{
- // Initialize r4 to point to where we start filling the frame.
- ThumbEmitAdd(ThumbReg(4), thumbRegSp, offsetOfFrame - sizeof(GSCookie));
-
- // Write the initial GS cookie value
- // mov r5, s_gsCookie
- // str r5, [r4]
- ThumbEmitMovConstant(ThumbReg(5), s_gsCookie);
- ThumbEmitStoreIndirectPostIncrement(ThumbReg(5), ThumbReg(4), 4);
-
- // Initialize the vtable pointer.
- // mov r5, #vfptr
- // str r5, [r4 + #offsetof(Frame, _vfptr)]
- ThumbEmitMovConstant(ThumbReg(5), pFrameVptr);
- ThumbEmitStoreRegIndirect(ThumbReg(5), ThumbReg(4), 0);
-
- // Link the frame to the thread's frame chain.
- // r5 <- current Thread*
- // ldr r6, [r5 + #offsetof(Thread, m_pFrame)]
- // str r6, [r4 + #offsetof(MulticastFrame, m_Next)]
- // str r4, [r5 + #offsetof(Thread, m_pFrame)]
-
- ThumbEmitGetThread(ThumbReg(5));
-#ifdef FEATURE_PAL
- // reload argument registers that could have been corrupted by the call
- for (int reg = 0; reg < 4; reg++)
- ThumbEmitLoadRegIndirect(ThumbReg(reg), ThumbReg(4),
- offsetOfTransitionBlock + TransitionBlock::GetOffsetOfArgumentRegisters() + offsetof(ArgumentRegisters, r[reg]));
-#endif
-
- ThumbEmitLoadRegIndirect(ThumbReg(6), ThumbReg(5), Thread::GetOffsetOfCurrentFrame());
- ThumbEmitStoreRegIndirect(ThumbReg(6), ThumbReg(4), Frame::GetOffsetOfNextLink());
- ThumbEmitStoreRegIndirect(ThumbReg(4), ThumbReg(5), Thread::GetOffsetOfCurrentFrame());
-}
-
#endif // CROSSGEN_COMPILE
void StubLinkerCPU::ThumbEmitNearJump(CodeLabel *target)
@@ -2931,49 +2131,6 @@ void StubLinkerCPU::ThumbEmitCondRegJump(CodeLabel *target, BOOL nonzero, ThumbR
EmitLabelRef(target, reinterpret_cast<ThumbCondJump&>(gThumbCondJump), variation);
}
-UINT_PTR StubLinkerCPU::HashMulticastInvoke(MetaSig *pSig)
-{
- // Generate a hash key as follows:
- // Bit0-2 : num of general purpose registers used
- // Bit3-7 : num of FP regs used (counting in terms of s0,s1...)
- // Bit8-22 : num of stack bytes used
-
- ArgIterator delegateCallConv(pSig);
-
- UINT numStackBytes = delegateCallConv.SizeOfArgStack();
-
- if (numStackBytes > 0x7FFF)
- COMPlusThrow(kNotSupportedException, W("NotSupported_TooManyArgs"));
-
- int cGenReg = 1; // r0 is always used for this pointer
- int cFPReg = 0;
-
- // if it has a return buffer argument r1 is also used
- if(delegateCallConv.HasRetBuffArg())
- cGenReg = 2;
-
- int argOffset;
- while ((argOffset = delegateCallConv.GetNextOffset()) != TransitionBlock::InvalidOffset)
- {
- ArgLocDesc currArgLoc;
- delegateCallConv.GetArgLoc(argOffset, &currArgLoc);
-
- if(currArgLoc.m_idxGenReg != -1)
- cGenReg = currArgLoc.m_idxGenReg + currArgLoc.m_cGenReg;
-
- if(currArgLoc.m_idxFloatReg != -1)
- cFPReg = currArgLoc.m_idxFloatReg + currArgLoc.m_cFloatReg;
- }
-
- // only r0-r3 can be used for arguments
- _ASSERTE(cGenReg <= 4);
-
- // only s0-s15 can be used for arguments
- _ASSERTE(cFPReg <= 16);
-
- return (numStackBytes << 8 | cFPReg << 3 | cGenReg);
-}
-
void StubLinkerCPU::ThumbCopyOneTailCallArg(UINT * pnSrcAlign, const ArgLocDesc * pArgLoc, UINT * pcbStackSpace)
{
if (pArgLoc->m_fRequires64BitAlignment && (*pnSrcAlign & 1)) {