1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
8 #error Should only include "cGenCpu.h" for ARM builds
16 // preferred alignment for data
17 #define DATA_ALIGNMENT 4
19 #define DISPATCH_STUB_FIRST_WORD 0xf8d0
20 #define RESOLVE_STUB_FIRST_WORD 0xf8d0
23 class FramedMethodFrame;
25 struct DeclActionInfo;
26 class ComCallMethodDesc;
31 extern PCODE GetPreStubEntryPoint();
34 #define USE_REDIRECT_FOR_GCSTRESS
37 // CPU-dependent functions
38 Stub * GenerateInitPInvokeFrameHelper();
40 EXTERN_C void checkStack(void);
44 #ifdef CROSSGEN_COMPILE
45 #define GetEEFuncEntryPoint(pfn) 0x1001
47 #define GetEEFuncEntryPoint(pfn) (GFN_TADDR(pfn) | THUMB_CODE)
50 //**********************************************************************
52 #define COMMETHOD_PREPAD 12 // # extra bytes to allocate in addition to sizeof(ComCallMethodDesc)
53 #ifdef FEATURE_COMINTEROP
54 #define COMMETHOD_CALL_PRESTUB_SIZE 12
55 #define COMMETHOD_CALL_PRESTUB_ADDRESS_OFFSET 8 // the offset of the call target address inside the prestub
56 #endif // FEATURE_COMINTEROP
58 #define STACK_ALIGN_SIZE 4
60 #define JUMP_ALLOCATE_SIZE 8 // # bytes to allocate for a jump instruction
61 #define BACK_TO_BACK_JUMP_ALLOCATE_SIZE 8 // # bytes to allocate for a back to back jump instruction
63 #define HAS_COMPACT_ENTRYPOINTS 1
65 #define HAS_NDIRECT_IMPORT_PRECODE 1
67 #define USE_INDIRECT_CODEHEADER
70 EXTERN_C void getFPReturn(int fpSize, INT64 *pRetVal);
71 EXTERN_C void setFPReturn(int fpSize, INT64 retVal);
73 #define HAS_FIXUP_PRECODE 1
74 #define HAS_FIXUP_PRECODE_CHUNKS 1
76 // ThisPtrRetBufPrecode one is necessary for closed delegates over static methods with return buffer
77 #define HAS_THISPTR_RETBUF_PRECODE 1
79 #define CODE_SIZE_ALIGN 4
80 #define CACHE_LINE_SIZE 32 // As per Intel Optimization Manual the cache line size is 32 bytes
81 #define LOG2SLOT LOG2_PTRSIZE
83 #define ENREGISTERED_RETURNTYPE_MAXSIZE 32 // bytes (maximum HFA size is 4 doubles)
84 #define ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE 4 // bytes
86 #define CALLDESCR_ARGREGS 1 // CallDescrWorker has ArgumentRegister parameter
87 #define CALLDESCR_FPARGREGS 1 // CallDescrWorker has FloatArgumentRegisters parameter
89 // Given a return address retrieved during stackwalk,
90 // this is the offset by which it should be decremented to arrive at the callsite.
91 #define STACKWALK_CONTROLPC_ADJUST_OFFSET 2
93 // Max offset for unconditional thumb branch
94 #define MAX_OFFSET_UNCONDITIONAL_BRANCH_THUMB 2048
96 // Offset of pc register
97 #define PC_REG_RELATIVE_OFFSET 4
99 //=======================================================================
100 // IMPORTANT: This value is used to figure out how much to allocate
101 // for a fixed array of FieldMarshaler's. That means it must be at least
102 // as large as the largest FieldMarshaler subclass. This requirement
103 // is guarded by an assert.
104 //=======================================================================
106 #define MAXFIELDMARSHALERSIZE 40
108 #define MAXFIELDMARSHALERSIZE 24
111 //**********************************************************************
113 //**********************************************************************
115 typedef INT32 StackElemType;
116 #define STACK_ELEM_SIZE sizeof(StackElemType)
118 // !! This expression assumes STACK_ELEM_SIZE is a power of 2.
119 #define StackElemSize(parmSize) (((parmSize) + STACK_ELEM_SIZE - 1) & ~((ULONG)(STACK_ELEM_SIZE - 1)))
121 //**********************************************************************
123 //**********************************************************************
125 //--------------------------------------------------------------------
126 // This represents the callee saved (non-volatile) registers saved as
127 // of a FramedMethodFrame.
128 //--------------------------------------------------------------------
129 typedef DPTR(struct CalleeSavedRegisters) PTR_CalleeSavedRegisters;
130 struct CalleeSavedRegisters {
131 INT32 r4, r5, r6, r7, r8, r9, r10;
132 INT32 r11; // frame pointer
133 INT32 r14; // link register
136 //--------------------------------------------------------------------
137 // This represents the arguments that are stored in volatile registers.
138 // This should not overlap the CalleeSavedRegisters since those are already
139 // saved separately and it would be wasteful to save the same register twice.
140 // If we do use a non-volatile register as an argument, then the ArgIterator
141 // will probably have to communicate this back to the PromoteCallerStack
142 // routine to avoid a double promotion.
143 //--------------------------------------------------------------------
144 typedef DPTR(struct ArgumentRegisters) PTR_ArgumentRegisters;
145 struct ArgumentRegisters {
146 INT32 r[4]; // r0, r1, r2, r3
148 #define NUM_ARGUMENT_REGISTERS 4
150 //--------------------------------------------------------------------
151 // This represents the floating point argument registers which are saved
152 // as part of the NegInfo for a FramedMethodFrame. Note that these
153 // might not be saved by all stubs: typically only those that call into
154 // C++ helpers will need to preserve the values in these volatile
156 //--------------------------------------------------------------------
157 typedef DPTR(struct FloatArgumentRegisters) PTR_FloatArgumentRegisters;
158 struct FloatArgumentRegisters {
161 float s[16]; // s0-s15
162 double d[8]; // d0-d7
168 typedef REGDISPLAY *PREGDISPLAY;
170 // Sufficient context for Try/Catch restoration.
172 INT32 r[16]; // note: includes r15(pc)
173 void Setup(PCODE resumePC, PREGDISPLAY regs);
175 inline TADDR GetSP() {
176 LIMITED_METHOD_CONTRACT;
179 inline void SetSP(LPVOID esp) {
180 LIMITED_METHOD_CONTRACT;
181 r[13] = (INT32)(size_t)esp;
184 inline LPVOID GetFP() {
185 LIMITED_METHOD_CONTRACT;
186 return (LPVOID)(UINT_PTR)r[11];
189 inline void SetArg(LPVOID arg) {
190 LIMITED_METHOD_CONTRACT;
191 r[0] = (INT32)(size_t)arg;
195 #define ARGUMENTREGISTERS_SIZE sizeof(ArgumentRegisters)
197 //**********************************************************************
198 // Exception handling
199 //**********************************************************************
201 inline PCODE GetIP(const T_CONTEXT * context) {
202 LIMITED_METHOD_DAC_CONTRACT;
203 return PCODE(context->Pc);
206 inline void SetIP(T_CONTEXT *context, PCODE eip) {
207 LIMITED_METHOD_DAC_CONTRACT;
208 context->Pc = DWORD(eip);
211 inline TADDR GetSP(const T_CONTEXT * context) {
212 LIMITED_METHOD_DAC_CONTRACT;
213 return TADDR(context->Sp);
216 inline PCODE GetLR(const T_CONTEXT * context) {
217 LIMITED_METHOD_DAC_CONTRACT;
218 return PCODE(context->Lr);
221 extern "C" LPVOID __stdcall GetCurrentSP();
223 inline void SetSP(T_CONTEXT *context, TADDR esp) {
224 LIMITED_METHOD_DAC_CONTRACT;
225 context->Sp = DWORD(esp);
228 inline void SetFP(T_CONTEXT *context, TADDR ebp) {
229 LIMITED_METHOD_DAC_CONTRACT;
230 context->R11 = DWORD(ebp);
233 inline TADDR GetFP(const T_CONTEXT * context)
235 LIMITED_METHOD_DAC_CONTRACT;
236 return (TADDR)(context->R11);
239 inline void ClearITState(T_CONTEXT *context) {
240 LIMITED_METHOD_DAC_CONTRACT;
241 context->Cpsr = context->Cpsr & 0xf9ff03ff;
244 #ifdef FEATURE_COMINTEROP
245 void emitCOMStubCall (ComCallMethodDesc *pCOMMethod, PCODE target);
246 #endif // FEATURE_COMINTEROP
248 //------------------------------------------------------------------------
249 inline void emitUnconditionalBranchThumb(LPBYTE pBuffer, int16_t offset)
251 LIMITED_METHOD_CONTRACT;
253 uint16_t *pInstr = (uint16_t *) pBuffer;
255 // offset from -2KB to +2KB
256 _ASSERTE (offset >= - MAX_OFFSET_UNCONDITIONAL_BRANCH_THUMB && offset < MAX_OFFSET_UNCONDITIONAL_BRANCH_THUMB);
260 offset = offset >> 1;
264 offset = ((MAX_OFFSET_UNCONDITIONAL_BRANCH_THUMB + offset) >> 1) | 0x400;
267 *pInstr = 0xE000 | offset;
270 //------------------------------------------------------------------------
271 inline int16_t decodeUnconditionalBranchThumb(LPBYTE pBuffer)
273 LIMITED_METHOD_CONTRACT;
275 uint16_t *pInstr = (uint16_t *) pBuffer;
277 int16_t offset = (~0xE000) & (*pInstr);
279 if ((offset & 0x400) == 0)
281 offset = offset << 1;
285 offset = (~0x400) & offset;
286 offset = (offset << 1) - MAX_OFFSET_UNCONDITIONAL_BRANCH_THUMB;
289 // offset from -2KB to +2KB
290 _ASSERTE (offset >= - MAX_OFFSET_UNCONDITIONAL_BRANCH_THUMB && offset < MAX_OFFSET_UNCONDITIONAL_BRANCH_THUMB);
295 //------------------------------------------------------------------------
296 inline void emitJump(LPBYTE pBuffer, LPVOID target)
298 LIMITED_METHOD_CONTRACT;
300 // The PC-relative load we emit below requires 4-byte alignment for the offset to be calculated correctly.
301 _ASSERTE(((UINT_PTR)pBuffer & 3) == 0);
303 DWORD * pCode = (DWORD *)pBuffer;
306 pCode[0] = 0xf000f8df;
307 pCode[1] = (DWORD)target;
310 //------------------------------------------------------------------------
311 // Given the same pBuffer that was used by emitJump this method
312 // decodes the instructions and returns the jump target
313 inline PCODE decodeJump(PCODE pCode)
315 LIMITED_METHOD_CONTRACT;
317 TADDR pInstr = PCODEToPINSTR(pCode);
319 return *dac_cast<PTR_PCODE>(pInstr + sizeof(DWORD));
323 // On IA64 back to back jumps should be separated by a nop bundle to get
324 // the best performance from the hardware's branch prediction logic.
325 // For all other platforms back to back jumps don't require anything special
326 // That is why we have these two wrapper functions that call emitJump and decodeJump
329 //------------------------------------------------------------------------
330 inline BOOL isJump(PCODE pCode)
332 LIMITED_METHOD_DAC_CONTRACT;
334 TADDR pInstr = PCODEToPINSTR(pCode);
336 return *dac_cast<PTR_DWORD>(pInstr) == 0xf000f8df;
339 //------------------------------------------------------------------------
340 inline BOOL isBackToBackJump(PCODE pBuffer)
344 return isJump(pBuffer);
347 //------------------------------------------------------------------------
348 inline void emitBackToBackJump(LPBYTE pBuffer, LPVOID target)
351 emitJump(pBuffer, target);
354 //------------------------------------------------------------------------
355 inline PCODE decodeBackToBackJump(PCODE pBuffer)
358 return decodeJump(pBuffer);
361 //----------------------------------------------------------------------
362 #include "stublink.h"
363 struct ArrayOpScript;
365 inline BOOL IsThumbCode(PCODE pCode)
367 return (pCode & THUMB_CODE) != 0;
373 ThumbReg(int reg):reg(reg)
375 _ASSERTE(0 <= reg && reg < 16);
383 int operator == (ThumbReg other)
385 return reg == other.reg;
388 int operator != (ThumbReg other)
390 return reg != other.reg;
403 ThumbCond(int cond):cond(cond)
405 _ASSERTE(0 <= cond && cond < 16);
409 struct ThumbVFPSingleReg
412 ThumbVFPSingleReg(int reg):reg(reg)
414 _ASSERTE(0 <= reg && reg < 31);
422 int operator == (ThumbVFPSingleReg other)
424 return reg == other.reg;
427 int operator != (ThumbVFPSingleReg other)
429 return reg != other.reg;
439 struct ThumbVFPDoubleReg
442 ThumbVFPDoubleReg(int reg):reg(reg)
444 _ASSERTE(0 <= reg && reg < 31);
452 int operator == (ThumbVFPDoubleReg other)
454 return reg == other.reg;
457 int operator != (ThumbVFPDoubleReg other)
459 return reg != other.reg;
468 const ThumbReg thumbRegFp = ThumbReg(11);
469 const ThumbReg thumbRegSp = ThumbReg(13);
470 const ThumbReg thumbRegLr = ThumbReg(14);
471 const ThumbReg thumbRegPc = ThumbReg(15);
473 const ThumbCond thumbCondEq = ThumbCond(0);
474 const ThumbCond thumbCondNe = ThumbCond(1);
475 const ThumbCond thumbCondCs = ThumbCond(2);
476 const ThumbCond thumbCondCc = ThumbCond(3);
477 const ThumbCond thumbCondMi = ThumbCond(4);
478 const ThumbCond thumbCondPl = ThumbCond(5);
479 const ThumbCond thumbCondVs = ThumbCond(6);
480 const ThumbCond thumbCondVc = ThumbCond(7);
481 const ThumbCond thumbCondHi = ThumbCond(8);
482 const ThumbCond thumbCondLs = ThumbCond(9);
483 const ThumbCond thumbCondGe = ThumbCond(10);
484 const ThumbCond thumbCondLt = ThumbCond(11);
485 const ThumbCond thumbCondGt = ThumbCond(12);
486 const ThumbCond thumbCondLe = ThumbCond(13);
487 const ThumbCond thumbCondAl = ThumbCond(14);
489 class StubLinkerCPU : public StubLinker
494 void ThumbEmitProlog(UINT cCalleeSavedRegs, UINT cbStackFrame, BOOL fPushArgRegs)
496 _ASSERTE(!m_fProlog);
498 // Record the parameters of this prolog so that we can generate a matching epilog and unwind info.
499 DescribeProlog(cCalleeSavedRegs, cbStackFrame, fPushArgRegs);
501 // Trivial prologs (which is all that we support initially) consist of between one and three
504 // 1) Push argument registers. This is all or nothing (if we push, we push R0-R3).
508 ThumbEmitPush(ThumbReg(0).Mask() | ThumbReg(1).Mask() | ThumbReg(2).Mask() | ThumbReg(3).Mask());
511 // 2) Push callee saved registers. We always start pushing at R4, and only saved consecutive registers
512 // from there (max is R11). Additionally we always assume LR is saved for these types of prolog.
514 WORD wRegisters = thumbRegLr.Mask();
515 for (unsigned int i = 4; i < (4 + cCalleeSavedRegs); i++)
516 wRegisters |= ThumbReg(i).Mask();
517 ThumbEmitPush(wRegisters);
519 // 3) Reserve space on the stack for the rest of the frame.
522 // sub sp, #cbStackFrame
523 ThumbEmitSubSp(cbStackFrame);
527 void ThumbEmitEpilog()
529 // Generate an epilog matching a prolog generated by ThumbEmitProlog.
532 // If additional stack space for a frame was allocated remove it now.
535 // add sp, #m_cbStackFrame
536 ThumbEmitAddSp(m_cbStackFrame);
539 // Pop callee saved registers (we always have at least LR). If no argument registers were saved then
540 // we can restore LR back into PC and we're done. Otherwise LR needs to be restored into LR.
542 WORD wRegisters = m_fPushArgRegs ? thumbRegLr.Mask() : thumbRegPc.Mask();
543 for (unsigned int i = 4; i < (4 + m_cCalleeSavedRegs); i++)
544 wRegisters |= ThumbReg(i).Mask();
545 ThumbEmitPop(wRegisters);
550 // We pushed the argument registers. These aren't restored, but we need to reclaim the stack space.
554 // Return. The return address has been restored into LR at this point.
556 ThumbEmitJumpRegister(thumbRegLr);
559 void ThumbEmitGetThread(ThumbReg dest);
567 void ThumbEmitBreakpoint()
569 // Permanently undefined instruction #0xfe (see ARMv7-A A6.2.6). The debugger seems to accept this as
570 // a reasonable breakpoint substitute (it's what DebugBreak uses). Bkpt #0, on the other hand, always
571 // seems to flow directly to the kernel debugger (even if we ignore it there it doesn't seem to be
572 // picked up by the user mode debugger).
580 void ThumbEmitMovConstant(ThumbReg dest, int constant)
582 _ASSERT(dest != thumbRegPc);
584 //Emit 2 Byte instructions when dest reg < 8 & constant <256
585 if(dest <= 7 && constant < 256 && constant >= 0)
587 Emit16((WORD)(0x2000 | dest<<8 | (WORD)constant));
589 else // emit 4 byte instructions
591 WORD wConstantLow = (WORD)(constant & 0xffff);
592 WORD wConstantHigh = (WORD)(constant >> 16);
594 // movw regDest, #wConstantLow
595 Emit16((WORD)(0xf240 | (wConstantLow >> 12) | ((wConstantLow & 0x0800) ? 0x0400 : 0x0000)));
596 Emit16((WORD)((dest << 8) | (((wConstantLow >> 8) & 0x0007) << 12) | (wConstantLow & 0x00ff)));
600 // movt regDest, #wConstantHighw
601 Emit16((WORD)(0xf2c0 | (wConstantHigh >> 12) | ((wConstantHigh & 0x0800) ? 0x0400 : 0x0000)));
602 Emit16((WORD)((dest << 8) | (((wConstantHigh >> 8) & 0x0007) << 12) | (wConstantHigh & 0x00ff)));
607 void ThumbEmitLoadRegIndirect(ThumbReg dest, ThumbReg source, int offset)
609 _ASSERTE((offset >= 0) && (offset <= 4095));
611 // ldr regDest, [regSource + #offset]
612 if ((dest < 8) && (source < 8) && ((offset & 0x3) == 0) && (offset < 125))
615 Emit16((WORD)(0x6800 | ((offset >> 2) << 6) | (source << 3) | dest));
620 Emit16((WORD)(0xf8d0 | source));
621 Emit16((WORD)((dest << 12) | offset));
625 void ThumbEmitLoadIndirectPostIncrement(ThumbReg dest, ThumbReg source, int offset)
627 _ASSERTE((offset >= 0) && (offset <= 255));
629 // ldr regDest, [regSource], #offset
630 Emit16((WORD)(0xf850 | source));
631 Emit16((WORD)(0x0b00 | (dest << 12) | offset));
634 void ThumbEmitStoreRegIndirect(ThumbReg source, ThumbReg dest, int offset)
636 _ASSERTE((offset >= -255) && (offset <= 4095));
638 // str regSource, [regDest + #offset]
641 Emit16((WORD)(0xf840 | dest));
642 Emit16((WORD)(0x0C00 | (source << 12) | (UINT8)(-offset)));
645 if ((dest < 8) && (source < 8) && ((offset & 0x3) == 0) && (offset < 125))
648 Emit16((WORD)(0x6000 | ((offset >> 2) << 6) | (dest << 3) | source));
653 Emit16((WORD)(0xf8c0 | dest));
654 Emit16((WORD)((source << 12) | offset));
658 void ThumbEmitStoreIndirectPostIncrement(ThumbReg source, ThumbReg dest, int offset)
660 _ASSERTE((offset >= 0) && (offset <= 255));
662 // str regSource, [regDest], #offset
663 Emit16((WORD)(0xf840 | dest));
664 Emit16((WORD)(0x0b00 | (source << 12) | offset));
667 void ThumbEmitLoadOffsetScaledReg(ThumbReg dest, ThumbReg base, ThumbReg offset, int shift)
669 _ASSERTE(shift >=0 && shift <=3);
671 Emit16((WORD)(0xf850 | base));
672 Emit16((WORD)((dest << 12) | (shift << 4) | offset));
675 void ThumbEmitCallRegister(ThumbReg target)
678 Emit16((WORD)(0x4780 | (target << 3)));
681 void ThumbEmitJumpRegister(ThumbReg target)
684 Emit16((WORD)(0x4700 | (target << 3)));
687 void ThumbEmitMovRegReg(ThumbReg dest, ThumbReg source)
689 // mov regDest, regSource
690 Emit16((WORD)(0x4600 | ((dest > 7) ? 0x0080 : 0x0000) | (source << 3) | (dest & 0x0007)));
693 //Assuming SP is only subtracted in prolog
694 void ThumbEmitSubSp(int value)
696 _ASSERTE(value >= 0);
697 _ASSERTE((value & 0x3) == 0);
702 // sub sp, sp, #(value >> 2)
703 Emit16((WORD)(0xb080 | (value >> 2)));
705 else if(value < 4096)
707 // Using 32-bit encoding
708 Emit16((WORD)(0xf2ad| ((value & 0x0800) >> 1)));
709 Emit16((WORD)(0x0d00| ((value & 0x0700) << 4) | (value & 0x00ff)));
713 // For values >= 4K (pageSize) must check for guard page
715 #ifndef CROSSGEN_COMPILE
717 ThumbEmitMovConstant(ThumbReg(4), value);
718 // mov r12, checkStack
719 ThumbEmitMovConstant(ThumbReg(12), (int)checkStack);
721 ThumbEmitCallRegister(ThumbReg(12));
725 Emit16((WORD)0xebad);
726 Emit16((WORD)0x0d04);
730 void ThumbEmitAddSp(int value)
732 _ASSERTE(value >= 0);
733 _ASSERTE((value & 0x3) == 0);
738 // add sp, sp, #(value >> 2)
739 Emit16((WORD)(0xb000 | (value >> 2)));
741 else if(value < 4096)
743 // Using 32-bit encoding T4
744 Emit16((WORD)(0xf20d| ((value & 0x0800) >> 1)));
745 Emit16((WORD)(0x0d00| ((value & 0x0700) << 4) | (value & 0x00ff)));
749 //Must use temp register for values >=4096
750 ThumbEmitMovConstant(ThumbReg(12), value);
752 Emit16((WORD)0x44e5);
756 void ThumbEmitAddReg(ThumbReg dest, ThumbReg source)
759 _ASSERTE(dest != source);
760 Emit16((WORD)(0x4400 | ((dest & 0x8)<<4) | (source<<3) | (dest & 0x7)));
763 void ThumbEmitAdd(ThumbReg dest, ThumbReg source, unsigned int value)
768 // addw dest, source, #value
769 unsigned int i = (value & 0x800) >> 11;
770 unsigned int imm3 = (value & 0x700) >> 8;
771 unsigned int imm8 = value & 0xff;
772 Emit16((WORD)(0xf200 | (i << 10) | source));
773 Emit16((WORD)((imm3 << 12) | (dest << 8) | imm8));
777 // if immediate is more than 4096 only ADD (register) will work
778 // move immediate to dest reg and call ADD(reg)
779 // this will not work if dest is same as source.
780 _ASSERTE(dest != source);
781 ThumbEmitMovConstant(dest, value);
782 ThumbEmitAddReg(dest, source);
786 void ThumbEmitSub(ThumbReg dest, ThumbReg source, unsigned int value)
788 _ASSERTE(value < 4096);
790 // subw dest, source, #value
791 unsigned int i = (value & 0x800) >> 11;
792 unsigned int imm3 = (value & 0x700) >> 8;
793 unsigned int imm8 = value & 0xff;
794 Emit16((WORD)(0xf2a0 | (i << 10) | source));
795 Emit16((WORD)((imm3 << 12) | (dest << 8) | imm8));
798 void ThumbEmitCmpReg(ThumbReg reg1, ThumbReg reg2)
800 if(reg1 < 8 && reg2 <8)
802 Emit16((WORD)(0x4280 | reg2 << 3 | reg1));
806 _ASSERTE(reg1 != ThumbReg(15) && reg2 != ThumbReg(15));
807 Emit16((WORD)(0x4500 | reg2 << 3 | (reg1 & 0x7) | (reg1 & 0x8 ? 0x80 : 0x0)));
811 void ThumbEmitIncrement(ThumbReg dest, unsigned int value)
817 // addw <dest>, <dest>, #4095
818 ThumbEmitAdd(dest, dest, 4095);
821 else if (value <= 255)
823 // add <dest>, #value
824 Emit16((WORD)(0x3000 | (dest << 8) | value));
829 // addw <dest>, <dest>, #value
830 ThumbEmitAdd(dest, dest, value);
836 void ThumbEmitPush(WORD registers)
838 _ASSERTE(registers != 0);
839 _ASSERTE((registers & 0xa000) == 0); // Pushing SP or PC undefined
842 if (CountBits(registers) == 1)
844 // Encoding T3 (exactly one register, high or low)
846 while ((registers & (WORD)(1 << reg)) == 0)
851 Emit16(0x0d04 | (reg << 12));
853 else if ((registers & 0xbf00) == 0)
855 // Encoding T1 (low registers plus maybe LR)
856 Emit16(0xb400 | (registers & thumbRegLr.Mask() ? 0x0100: 0x0000) | (registers & 0x00ff));
860 // Encoding T2 (two or more registers, high or low)
866 void ThumbEmitLoadStoreMultiple(ThumbReg base, bool load, WORD registers)
868 _ASSERTE(CountBits(registers) > 1);
869 _ASSERTE((registers & 0xFF00) == 0); // This only supports the small encoding
870 _ASSERTE(base < 8); // This only supports the small encoding
871 _ASSERTE((base.Mask() & registers) == 0); // This only supports the small encoding
873 // (LDM|STM) base, {registers}
874 WORD flag = load ? 0x0800 : 0;
875 Emit16(0xc000 | flag | ((base & 7) << 8) | (registers & 0xFF));
878 void ThumbEmitPop(WORD registers)
880 _ASSERTE(registers != 0);
881 _ASSERTE((registers & 0xc000) != 0xc000); // Popping PC and LR together undefined
884 if (CountBits(registers) == 1)
886 // Encoding T3 (exactly one register, high or low)
888 while ((registers & (WORD)(1 << reg)) == 0)
893 Emit16(0x0b04 | (reg << 12));
895 else if ((registers & 0x7f00) == 0)
897 // Encoding T1 (low registers plus maybe PC)
898 Emit16(0xbc00 | (registers & thumbRegPc.Mask() ? 0x0100: 0x0000) | (registers & 0x00ff));
902 // Encoding T2 (two or more registers, high or low)
908 void ThumbEmitLoadVFPSingleRegIndirect(ThumbVFPSingleReg dest, ThumbReg source, int offset)
910 _ASSERTE((offset >= -1020) && (offset <= 1020));
911 _ASSERTE(offset%4==0);
913 Emit16((WORD) (0xed10 | ((offset > 0 ? 0x1: 0x0) << 7) | ((dest & 0x1) << 6) | source));
914 Emit16((WORD) (0x0a00 | ((dest & 0x1e) << 11) | (abs(offset)>>2)));
917 void ThumbEmitLoadVFPDoubleRegIndirect(ThumbVFPDoubleReg dest, ThumbReg source, int offset)
919 _ASSERTE((offset >= -1020) && (offset <= 1020));
920 _ASSERTE(offset%4==0);
922 Emit16((WORD) (0xed10 | ((offset > 0 ? 0x1: 0x0) << 7) | ((dest & 0x10) << 6) | source));
923 Emit16((WORD) (0x0b00 | ((dest & 0xf) << 12) | (abs(offset)>>2)));
926 #ifdef FEATURE_INTERPRETER
927 void ThumbEmitStoreMultipleVFPDoubleReg(ThumbVFPDoubleReg source, ThumbReg dest, unsigned numRegs)
929 _ASSERTE((numRegs + source) <= 16);
931 // The third nibble is 0x8; the 0x4 bit (D) is zero because the source reg number must be less
932 // than 16 for double registers.
933 Emit16((WORD) (0xec80 | 0x80 | dest));
934 Emit16((WORD) (((source & 0xf) << 12) | 0xb00 | numRegs));
937 void ThumbEmitLoadMultipleVFPDoubleReg(ThumbVFPDoubleReg dest, ThumbReg source, unsigned numRegs)
939 _ASSERTE((numRegs + dest) <= 16);
941 // The third nibble is 0x8; the 0x4 bit (D) is zero because the source reg number must be less
942 // than 16 for double registers.
943 Emit16((WORD) (0xec90 | 0x80 | source));
944 Emit16((WORD) (((dest & 0xf) << 12) | 0xb00 | numRegs));
946 #endif // FEATURE_INTERPRETER
948 void EmitStubLinkFrame(TADDR pFrameVptr, int offsetOfFrame, int offsetOfTransitionBlock);
949 void EmitStubUnlinkFrame();
951 void ThumbEmitCondFlagJump(CodeLabel * target,UINT cond);
953 void ThumbEmitCondRegJump(CodeLabel *target, BOOL nonzero, ThumbReg reg);
955 void ThumbEmitNearJump(CodeLabel *target);
958 void ThumbEmitCallManagedMethod(MethodDesc *pMD, bool fTailcall);
960 void EmitUnboxMethodStub(MethodDesc* pRealMD);
961 static UINT_PTR HashMulticastInvoke(MetaSig* pSig);
963 void EmitMulticastInvoke(UINT_PTR hash);
964 void EmitSecureDelegateInvoke(UINT_PTR hash);
965 void EmitShuffleThunk(struct ShuffleEntry *pShuffleEntryArray);
966 #if defined(FEATURE_SHARE_GENERIC_CODE)
967 void EmitInstantiatingMethodStub(MethodDesc* pSharedMD, void* extra);
968 #endif // FEATURE_SHARE_GENERIC_CODE
970 static Stub * CreateTailCallCopyArgsThunk(CORINFO_SIG_INFO * pSig,
972 CorInfoHelperTailCallSpecialHandling flags);
975 void ThumbCopyOneTailCallArg(UINT * pnSrcAlign, const ArgLocDesc * pArgLoc, UINT * pcbStackSpace);
976 void ThumbEmitCallWithGenericInstantiationParameter(MethodDesc *pMD, void *pHiddenArg);
979 extern "C" void SinglecastDelegateInvokeStub();
981 // SEH info forward declarations
983 inline BOOL IsUnmanagedValueTypeReturnedByRef(UINT sizeofvaluetype)
985 LIMITED_METHOD_CONTRACT;
987 // structure that dont fit in the machine-word size are returned
989 return (sizeofvaluetype > 4);
993 #pragma warning(push)
994 #pragma warning(disable:4359) // Prevent "warning C4359: 'UMEntryThunkCode': Alignment specifier is less than actual alignment (8), and will be ignored." in crossbitness scenario
997 struct DECLSPEC_ALIGN(4) UMEntryThunkCode
1001 TADDR m_pTargetCode;
1002 TADDR m_pvSecretParam;
1004 void Encode(BYTE* pTargetCode, void* pvSecretParam);
1007 LPCBYTE GetEntryPoint() const
1009 LIMITED_METHOD_CONTRACT;
1011 return (LPCBYTE)((TADDR)this | THUMB_CODE);
1014 static int GetEntryPointOffset()
1016 LIMITED_METHOD_CONTRACT;
1023 #pragma warning(pop)
1031 size_t ReturnValue[1]; // this may not be the return value when return is >32bits
1032 // or return value is in VFP reg but it works for us as
1033 // this is only used by functions OnHijackWorker()
1037 // Non-volatile Integer registers
1051 size_t ReturnAddress;
1055 // ClrFlushInstructionCache is used when we want to call FlushInstructionCache
1056 // for a specific architecture in the common code, but not for other architectures.
1057 // On IA64 ClrFlushInstructionCache calls the Kernel FlushInstructionCache function
1058 // to flush the instruction cache.
1059 // We call ClrFlushInstructionCache whenever we create or modify code in the heap.
1060 // Currently ClrFlushInstructionCache has no effect on X86
1063 inline BOOL ClrFlushInstructionCache(LPCVOID pCodeAddr, size_t sizeOfCode)
1065 #ifdef CROSSGEN_COMPILE
1066 // The code won't be executed when we are cross-compiling so flush instruction cache is unnecessary
1069 return FlushInstructionCache(GetCurrentProcess(), pCodeAddr, sizeOfCode);
1074 // JIT HELPER ALIASING FOR PORTABILITY.
1076 // Create alias for optimized implementations of helpers provided on this platform
1078 #define JIT_GetSharedGCStaticBase JIT_GetSharedGCStaticBase_SingleAppDomain
1079 #define JIT_GetSharedNonGCStaticBase JIT_GetSharedNonGCStaticBase_SingleAppDomain
1080 #define JIT_GetSharedGCStaticBaseNoCtor JIT_GetSharedGCStaticBaseNoCtor_SingleAppDomain
1081 #define JIT_GetSharedNonGCStaticBaseNoCtor JIT_GetSharedNonGCStaticBaseNoCtor_SingleAppDomain
1084 #define JIT_Stelem_Ref JIT_Stelem_Ref
1087 //------------------------------------------------------------------------
1089 // Precode definitions
1091 //------------------------------------------------------------------------
1093 // Note: If you introduce new precode implementation below, then please
1094 // update PrecodeStubManager::CheckIsStub_Internal to account for it.
1096 EXTERN_C VOID STDCALL PrecodeFixupThunk();
1098 #define PRECODE_ALIGNMENT sizeof(void*)
1099 #define SIZEOF_PRECODE_BASE CODE_SIZE_ALIGN
1100 #define OFFSETOF_PRECODE_TYPE 0
1102 // Invalid precode type
1103 struct InvalidPrecode {
1104 static const int Type = 0;
1107 struct StubPrecode {
1109 static const int Type = 0xdf;
1111 // ldr r12, [pc, #8] ; =m_pMethodDesc
1112 // ldr pc, [pc, #0] ; =m_pTarget
1117 TADDR m_pMethodDesc;
1119 void Init(MethodDesc* pMD, LoaderAllocator *pLoaderAllocator);
1121 TADDR GetMethodDesc()
1123 LIMITED_METHOD_DAC_CONTRACT;
1124 return m_pMethodDesc;
1129 LIMITED_METHOD_DAC_CONTRACT;
1133 void ResetTargetInterlocked()
1142 EnsureWritableExecutablePages(&m_pTarget);
1143 InterlockedExchange((LONG*)&m_pTarget, (LONG)GetPreStubEntryPoint());
1146 BOOL SetTargetInterlocked(TADDR target, TADDR expected)
1155 EnsureWritableExecutablePages(&m_pTarget);
1156 return (TADDR)InterlockedCompareExchange(
1157 (LONG*)&m_pTarget, (LONG)target, (LONG)expected) == expected;
1160 #ifdef FEATURE_PREJIT
1161 void Fixup(DataImage *image);
1164 typedef DPTR(StubPrecode) PTR_StubPrecode;
1167 struct NDirectImportPrecode {
1169 static const int Type = 0xe0;
1171 // ldr r12, [pc, #4] ; =m_pMethodDesc
1172 // ldr pc, [pc, #4] ; =m_pTarget
1176 TADDR m_pMethodDesc; // Notice that the fields are reversed compared to StubPrecode. Precode::GetType
1177 // takes advantage of this to detect NDirectImportPrecode.
1180 void Init(MethodDesc* pMD, LoaderAllocator *pLoaderAllocator);
1182 TADDR GetMethodDesc()
1184 LIMITED_METHOD_DAC_CONTRACT;
1185 return m_pMethodDesc;
1190 LIMITED_METHOD_DAC_CONTRACT;
1194 LPVOID GetEntrypoint()
1196 LIMITED_METHOD_CONTRACT;
1197 return (LPVOID)(dac_cast<TADDR>(this) + THUMB_CODE);
1200 #ifdef FEATURE_PREJIT
1201 void Fixup(DataImage *image);
1204 typedef DPTR(NDirectImportPrecode) PTR_NDirectImportPrecode;
1207 struct FixupPrecode {
1209 static const int Type = 0xfc;
1212 // ldr pc, [pc, #4] ; =m_pTarget
1213 // dcb m_MethodDescChunkIndex
1214 // dcb m_PrecodeChunkIndex
1217 BYTE m_MethodDescChunkIndex;
1218 BYTE m_PrecodeChunkIndex;
1221 void Init(MethodDesc* pMD, LoaderAllocator *pLoaderAllocator, int iMethodDescChunkIndex = 0, int iPrecodeChunkIndex = 0);
1225 LIMITED_METHOD_CONTRACT;
1228 return dac_cast<TADDR>(this) + (m_PrecodeChunkIndex + 1) * sizeof(FixupPrecode);
1231 TADDR GetMethodDesc();
1235 LIMITED_METHOD_DAC_CONTRACT;
1239 void ResetTargetInterlocked()
1248 EnsureWritableExecutablePages(&m_pTarget);
1249 InterlockedExchange((LONG*)&m_pTarget, (LONG)GetEEFuncEntryPoint(PrecodeFixupThunk));
1252 BOOL SetTargetInterlocked(TADDR target, TADDR expected)
1261 EnsureWritableExecutablePages(&m_pTarget);
1262 return (TADDR)InterlockedCompareExchange(
1263 (LONG*)&m_pTarget, (LONG)target, (LONG)expected) == expected;
1266 static BOOL IsFixupPrecodeByASM(PCODE addr)
1268 PTR_WORD pInstr = dac_cast<PTR_WORD>(PCODEToPINSTR(addr));
1271 (pInstr[0] == 0x46fc) &&
1272 (pInstr[1] == 0xf8df) &&
1273 (pInstr[2] == 0xf004);
1276 #ifdef FEATURE_PREJIT
1277 // Partial initialization. Used to save regrouped chunks.
1278 void InitForSave(int iPrecodeChunkIndex);
1280 void Fixup(DataImage *image, MethodDesc * pMD);
1283 #ifdef DACCESS_COMPILE
1284 void EnumMemoryRegions(CLRDataEnumMemoryFlags flags);
1287 typedef DPTR(FixupPrecode) PTR_FixupPrecode;
1290 // Precode to shuffle this and retbuf for closed delegates over static methods with return buffer
1291 struct ThisPtrRetBufPrecode {
1293 static const int Type = 0x84;
1298 // ldr pc, [pc, #0] ; =m_pTarget
1303 TADDR m_pMethodDesc;
1305 void Init(MethodDesc* pMD, LoaderAllocator *pLoaderAllocator);
1307 TADDR GetMethodDesc()
1309 LIMITED_METHOD_DAC_CONTRACT;
1311 return m_pMethodDesc;
1316 LIMITED_METHOD_DAC_CONTRACT;
1320 BOOL SetTargetInterlocked(TADDR target, TADDR expected)
1329 EnsureWritableExecutablePages(&m_pTarget);
1330 return FastInterlockCompareExchange((LONG*)&m_pTarget, (LONG)target, (LONG)expected) == (LONG)expected;
1333 typedef DPTR(ThisPtrRetBufPrecode) PTR_ThisPtrRetBufPrecode;
1336 #ifdef HAS_REMOTING_PRECODE
1338 // Precode with embedded remoting interceptor
1339 struct RemotingPrecode {
1341 static const int Type = 0x02;
1344 // ldr r1, [pc, #16] ; =m_pPrecodeRemotingThunk
1347 // ldr pc, [pc, #12] ; =m_pLocalTarget
1348 // nop ; padding for alignment
1349 // dcd m_pMethodDesc
1350 // dcd m_pPrecodeRemotingThunk
1351 // dcd m_pLocalTarget
1353 TADDR m_pMethodDesc;
1354 TADDR m_pPrecodeRemotingThunk;
1355 TADDR m_pLocalTarget;
1357 void Init(MethodDesc* pMD, LoaderAllocator *pLoaderAllocator = NULL);
1359 TADDR GetMethodDesc()
1361 LIMITED_METHOD_DAC_CONTRACT;
1362 return m_pMethodDesc;
1367 LIMITED_METHOD_DAC_CONTRACT;
1368 return m_pLocalTarget;
1371 BOOL SetTargetInterlocked(TADDR target, TADDR expected)
1380 EnsureWritableExecutablePages(&m_pLocalTarget);
1381 return FastInterlockCompareExchange((LONG*)&m_pLocalTarget, (LONG)target, (LONG)expected) == (LONG)expected;
1384 #ifdef FEATURE_PREJIT
1385 void Fixup(DataImage *image, ZapNode *pCodeNode);
1388 typedef DPTR(RemotingPrecode) PTR_RemotingPrecode;
1390 EXTERN_C void PrecodeRemotingThunk();
1392 #endif // HAS_REMOTING_PRECODE
1394 //**********************************************************************
1396 //**********************************************************************
1398 // Given the first halfword value of an ARM (Thumb) instruction (which is either an entire
1399 // 16-bit instruction, or the high-order halfword of a 32-bit instruction), determine how many bytes
1400 // the instruction is (2 or 4) and return that.
1401 inline size_t GetARMInstructionLength(WORD instr)
1403 // From the ARM Architecture Reference Manual, A6.1 "Thumb instruction set encoding":
1404 // If bits [15:11] of the halfword being decoded take any of the following values, the halfword is the first
1405 // halfword of a 32-bit instruction:
1409 // Otherwise, the halfword is a 16-bit instruction.
1410 if ((instr & 0xf800) > 0xe000)
1420 // Given a pointer to an ARM (Thumb) instruction address, determine how many bytes
1421 // the instruction is (2 or 4) and return that.
1422 inline size_t GetARMInstructionLength(PBYTE pInstr)
1424 return GetARMInstructionLength(*(WORD*)pInstr);
1427 EXTERN_C void FCallMemcpy(BYTE* dest, BYTE* src, int len);
1429 #endif // __cgencpu_h__