13b20a588b71ab76667c90e1adf843c696c1972e
[platform/upstream/dotnet/runtime.git] / src / coreclr / vm / riscv64 / cgencpu.h
1
2 // The .NET Foundation licenses this file to you under the MIT license.
3
4 #ifndef TARGET_RISCV64
5 #error Should only include "cGenCpu.h" for RISCV64 builds
6 #endif
7
8 #ifndef __cgencpu_h__
9 #define __cgencpu_h__
10
11 #define INSTRFMT_K64
12 #include <stublink.h>
13
14 #ifndef TARGET_UNIX
15 #define USE_REDIRECT_FOR_GCSTRESS
16 #endif // TARGET_UNIX
17
18 EXTERN_C void getFPReturn(int fpSize, INT64 *pRetVal);
19 EXTERN_C void setFPReturn(int fpSize, INT64 retVal);
20
21
22 class ComCallMethodDesc;
23
24 extern PCODE GetPreStubEntryPoint();
25
26 #define COMMETHOD_PREPAD                        24   // # extra bytes to allocate in addition to sizeof(ComCallMethodDesc)
27 #ifdef FEATURE_COMINTEROP
28 #define COMMETHOD_CALL_PRESTUB_SIZE             24
29 #define COMMETHOD_CALL_PRESTUB_ADDRESS_OFFSET   16   // the offset of the call target address inside the prestub
30 #endif // FEATURE_COMINTEROP
31
32 #define STACK_ALIGN_SIZE                        16
33
34 #define JUMP_ALLOCATE_SIZE                      40  // # bytes to allocate for a jump instruction
35 #define BACK_TO_BACK_JUMP_ALLOCATE_SIZE         40  // # bytes to allocate for a back to back jump instruction
36
37 #define HAS_NDIRECT_IMPORT_PRECODE              1
38
39 #define USE_INDIRECT_CODEHEADER
40
41 #define HAS_FIXUP_PRECODE                       1
42
43 // ThisPtrRetBufPrecode one is necessary for closed delegates over static methods with return buffer
44 #define HAS_THISPTR_RETBUF_PRECODE              1
45
46 #define CODE_SIZE_ALIGN                         8
47 #define CACHE_LINE_SIZE                         64
48 #define LOG2SLOT                                LOG2_PTRSIZE
49
50 #define ENREGISTERED_RETURNTYPE_MAXSIZE         16  // bytes (two FP registers: f10 and f11
51 #define ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE 16  // bytes (two int registers: a0 and a1)
52 #define ENREGISTERED_PARAMTYPE_MAXSIZE          16  // bytes (max value type size that can be passed by value)
53
54 #define CALLDESCR_ARGREGS                       1   // CallDescrWorker has ArgumentRegister parameter
55 #define CALLDESCR_FPARGREGS                     1   // CallDescrWorker has FloatArgumentRegisters parameter
56
57 #define FLOAT_REGISTER_SIZE 8 // each register in FloatArgumentRegisters is 8 bytes.
58
59 // Given a return address retrieved during stackwalk,
60 // this is the offset by which it should be decremented to arrive at the callsite.
61 #define STACKWALK_CONTROLPC_ADJUST_OFFSET 4
62
63 //**********************************************************************
64 // Parameter size
65 //**********************************************************************
66
67 inline unsigned StackElemSize(unsigned parmSize, bool isValueType, bool isFloatHfa)
68 {
69     const unsigned stackSlotSize = 8;
70     return ALIGN_UP(parmSize, stackSlotSize);
71 }
72
73 //
74 // JIT HELPERS.
75 //
76 // Create alias for optimized implementations of helpers provided on this platform
77 //
78 #define JIT_GetSharedGCStaticBase           JIT_GetSharedGCStaticBase_SingleAppDomain
79 #define JIT_GetSharedNonGCStaticBase        JIT_GetSharedNonGCStaticBase_SingleAppDomain
80 #define JIT_GetSharedGCStaticBaseNoCtor     JIT_GetSharedGCStaticBaseNoCtor_SingleAppDomain
81 #define JIT_GetSharedNonGCStaticBaseNoCtor  JIT_GetSharedNonGCStaticBaseNoCtor_SingleAppDomain
82
83 //**********************************************************************
84 // Frames
85 //**********************************************************************
86
87 //--------------------------------------------------------------------
88 // This represents the callee saved (non-volatile) integer registers saved as
89 // of a FramedMethodFrame.
90 //--------------------------------------------------------------------
91 typedef DPTR(struct CalleeSavedRegisters) PTR_CalleeSavedRegisters;
92 struct CalleeSavedRegisters {
93     INT64 fp; // frame pointer
94     INT64 ra; // return register
95     INT64 s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11;
96     INT64 tp, gp;
97 };
98
99 //--------------------------------------------------------------------
100 // This represents the arguments that are stored in volatile integer registers.
101 // This should not overlap the CalleeSavedRegisters since those are already
102 // saved separately and it would be wasteful to save the same register twice.
103 // If we do use a non-volatile register as an argument, then the ArgIterator
104 // will probably have to communicate this back to the PromoteCallerStack
105 // routine to avoid a double promotion.
106 //--------------------------------------------------------------------
107 #define NUM_ARGUMENT_REGISTERS 8
108 typedef DPTR(struct ArgumentRegisters) PTR_ArgumentRegisters;
109 struct ArgumentRegisters {
110     INT64 a[NUM_ARGUMENT_REGISTERS]; // a0 ....a7
111 };
112
113 #define ARGUMENTREGISTERS_SIZE sizeof(ArgumentRegisters)
114
115
116 //--------------------------------------------------------------------
117 // This represents the floating point argument registers which are saved
118 // as part of the NegInfo for a FramedMethodFrame. Note that these
119 // might not be saved by all stubs: typically only those that call into
120 // C++ helpers will need to preserve the values in these volatile
121 // registers.
122 //--------------------------------------------------------------------
123 #define NUM_FLOAT_ARGUMENT_REGISTERS 8
124 typedef DPTR(struct FloatArgumentRegisters) PTR_FloatArgumentRegisters;
125 struct FloatArgumentRegisters {
126     //TODO: not supports RISCV64-SIMD.
127     double  f[NUM_FLOAT_ARGUMENT_REGISTERS];  // f0-f7
128 };
129
130 //**********************************************************************
131 // Profiling
132 //**********************************************************************
133
134 #ifdef PROFILING_SUPPORTED
135
136 struct PROFILE_PLATFORM_SPECIFIC_DATA
137 {
138     void*                  Fp;
139     void*                  Pc;
140     ArgumentRegisters      argumentRegisters;
141     FunctionID             functionId;
142     FloatArgumentRegisters floatArgumentRegisters;
143     void*                  probeSp;
144     void*                  profiledSp;
145     void*                  hiddenArg;
146     UINT64                 flags;
147     // Scratch space to reconstruct struct passed in two registers
148     BYTE                   buffer[sizeof(ArgumentRegisters) + sizeof(FloatArgumentRegisters)];
149 };
150 #endif  // PROFILING_SUPPORTED
151
152 //**********************************************************************
153 // Exception handling
154 //**********************************************************************
155
156 inline PCODE GetIP(const T_CONTEXT * context) {
157     LIMITED_METHOD_DAC_CONTRACT;
158     return context->Pc;
159 }
160
161 inline void SetIP(T_CONTEXT *context, PCODE ip) {
162     LIMITED_METHOD_DAC_CONTRACT;
163     context->Pc = ip;
164 }
165
166 inline TADDR GetSP(const T_CONTEXT * context) {
167     LIMITED_METHOD_DAC_CONTRACT;
168     return TADDR(context->Sp);
169 }
170
171 inline TADDR GetRA(const T_CONTEXT * context) {
172     LIMITED_METHOD_DAC_CONTRACT;
173     return context->Ra;
174 }
175
176 inline void SetRA( T_CONTEXT * context, TADDR ip) {
177     LIMITED_METHOD_DAC_CONTRACT;
178     context->Ra = ip;
179 }
180
181 inline TADDR GetReg(T_CONTEXT * context, int Regnum)
182 {
183     LIMITED_METHOD_DAC_CONTRACT;
184     _ASSERTE(Regnum >= 0 && Regnum < 32 );
185      return (TADDR)(&context->R0 + Regnum);
186 }
187
188 inline void SetReg(T_CONTEXT * context, int Regnum, PCODE RegContent)
189 {
190     LIMITED_METHOD_DAC_CONTRACT;
191     _ASSERTE(Regnum >= 0 && Regnum <=28 );
192     *(&context->R0 + Regnum) = RegContent;
193 }
194
195 extern "C" LPVOID __stdcall GetCurrentSP();
196
197 inline void SetSP(T_CONTEXT *context, TADDR sp) {
198     LIMITED_METHOD_DAC_CONTRACT;
199     context->Sp = DWORD64(sp);
200 }
201
202 inline void SetFP(T_CONTEXT *context, TADDR fp) {
203     LIMITED_METHOD_DAC_CONTRACT;
204     context->Fp = DWORD64(fp);
205 }
206
207 inline TADDR GetFP(const T_CONTEXT * context)
208 {
209     LIMITED_METHOD_DAC_CONTRACT;
210     return (TADDR)(context->Fp);
211 }
212
213
214 inline TADDR GetMem(PCODE address, SIZE_T size, bool signExtend)
215 {
216     TADDR mem;
217     LIMITED_METHOD_DAC_CONTRACT;
218     EX_TRY
219     {
220         switch (size)
221         {
222         case 4:
223             if (signExtend)
224                 mem = *(int32_t*)address;
225             else
226                 mem = *(uint32_t*)address;
227             break;
228         case 8:
229             mem = *(uint64_t*)address;
230             break;
231         default:
232             UNREACHABLE();
233         }
234     }
235     EX_CATCH
236     {
237         mem = NULL;
238         _ASSERTE(!"Memory read within jitted Code Failed, this should not happen!!!!");
239     }
240     EX_END_CATCH(SwallowAllExceptions);
241     return mem;
242 }
243
244 #ifdef FEATURE_COMINTEROP
245 void emitCOMStubCall (ComCallMethodDesc *pCOMMethodRX, ComCallMethodDesc *pCOMMethodRW, PCODE target);
246 #endif // FEATURE_COMINTEROP
247
248 inline BOOL ClrFlushInstructionCache(LPCVOID pCodeAddr, size_t sizeOfCode, bool hasCodeExecutedBefore = false)
249 {
250     return FlushInstructionCache(GetCurrentProcess(), pCodeAddr, sizeOfCode);
251 }
252
253 //------------------------------------------------------------------------
254 inline void emitJump(LPBYTE pBufferRX, LPBYTE pBufferRW, LPVOID target)
255 {
256     LIMITED_METHOD_CONTRACT;
257     UINT32* pCode = (UINT32*)pBufferRW;
258
259     // We require 8-byte alignment so the LD instruction is aligned properly
260     _ASSERTE(((UINT_PTR)pCode & 7) == 0);
261
262     // auipc ra, 0
263     // ld    ra, ra, 16
264     // jalr  x0, ra, 0
265     // nop    //padding.
266
267     pCode[0] = 0x00000097; // auipc ra, 0
268     pCode[1] = 0x0100b083; // ld    ra, 16(ra)
269     pCode[2] = 0x00008067; // jalr  x0, ra, 0
270     pCode[3] = 0x00000013; // padding nop.
271
272     // Ensure that the updated instructions get updated in the I-Cache
273     ClrFlushInstructionCache(pBufferRX, 16);
274
275     *((LPVOID *)(pCode + 4)) = target;   // 64-bit target address
276 }
277
278 //------------------------------------------------------------------------
279 //  Given the same pBuffer that was used by emitJump this method
280 //  decodes the instructions and returns the jump target
281 inline PCODE decodeJump(PCODE pCode)
282 {
283     LIMITED_METHOD_CONTRACT;
284
285     TADDR pInstr = PCODEToPINSTR(pCode);
286
287     return *dac_cast<PTR_PCODE>(pInstr + 4 * sizeof(UINT32));
288 }
289
290 //------------------------------------------------------------------------
291 inline void emitBackToBackJump(LPBYTE pBufferRX, LPBYTE pBufferRW, LPVOID target)
292 {
293     WRAPPER_NO_CONTRACT;
294     emitJump(pBufferRX, pBufferRW, target);
295 }
296
297 //------------------------------------------------------------------------
298 inline PCODE decodeBackToBackJump(PCODE pBuffer)
299 {
300     WRAPPER_NO_CONTRACT;
301     return decodeJump(pBuffer);
302 }
303
304 //----------------------------------------------------------------------
305
306 struct IntReg
307 {
308     int reg;
309     IntReg(int reg):reg(reg)
310     {
311         _ASSERTE(0 <= reg && reg < 32);
312     }
313
314     operator int () { return reg; }
315     operator int () const { return reg; }
316     int operator == (IntReg other) { return reg == other.reg; }
317     int operator != (IntReg other) { return reg != other.reg; }
318     WORD Mask() const { return 1 << reg; }
319 };
320
321 struct FloatReg
322 {
323     int reg;
324     FloatReg(int reg):reg(reg)
325     {
326         _ASSERTE(0 <= reg && reg < 32);
327     }
328
329     operator int () { return reg; }
330     operator int () const { return reg; }
331     int operator == (FloatReg other) { return reg == other.reg; }
332     int operator != (FloatReg other) { return reg != other.reg; }
333     WORD Mask() const { return 1 << reg; }
334 };
335
336 struct CondCode
337 {
338     int cond;
339     CondCode(int cond):cond(cond)
340     {
341         _ASSERTE(0 <= cond && cond < 16);
342     }
343 };
344
345 const IntReg RegSp  = IntReg(2);
346 const IntReg RegFp  = IntReg(8);
347 const IntReg RegRa  = IntReg(1);
348
349 #define GetEEFuncEntryPoint(pfn) GFN_TADDR(pfn)
350
351 class StubLinkerCPU : public StubLinker
352 {
353 public:
354     static void Init();
355     static bool isValidSimm12(int value) {
356         return -( ((int)1) << 11 ) <= value && value < ( ((int)1) << 11 );
357     }
358
359     void EmitCallManagedMethod(MethodDesc *pMD, BOOL fTailCall);
360     void EmitCallLabel(CodeLabel *target, BOOL fTailCall, BOOL fIndirect);
361
362     void EmitShuffleThunk(struct ShuffleEntry *pShuffleEntryArray);
363
364 #if defined(FEATURE_SHARE_GENERIC_CODE)
365     void EmitComputedInstantiatingMethodStub(MethodDesc* pSharedMD, struct ShuffleEntry *pShuffleEntryArray, void* extraArg);
366 #endif // FEATURE_SHARE_GENERIC_CODE
367
368     void EmitMovConstant(IntReg target, UINT64 constant);
369     void EmitJumpRegister(IntReg regTarget);
370     void EmitMovReg(IntReg dest, IntReg source);
371     void EmitMovReg(FloatReg dest, FloatReg source);
372
373     void EmitSubImm(IntReg Xd, IntReg Xn, unsigned int value);
374     void EmitAddImm(IntReg Xd, IntReg Xn, unsigned int value);
375     void EmitSllImm(IntReg Xd, IntReg Xn, unsigned int value);
376     void EmitLuImm(IntReg Xd, unsigned int value);
377
378     void EmitLoad(IntReg dest, IntReg srcAddr, int offset = 0);
379     void EmitLoad(FloatReg dest, IntReg srcAddr, int offset = 0);
380     void EmitStore(IntReg src, IntReg destAddr, int offset = 0);
381     void EmitStore(FloatReg src, IntReg destAddr, int offset = 0);
382
383     void EmitProlog(unsigned short cIntRegArgs, unsigned short cFpRegArgs, unsigned short cbStackSpace = 0);
384     void EmitEpilog();
385 };
386
387 extern "C" void SinglecastDelegateInvokeStub();
388
389
390 // preferred alignment for data
391 #define DATA_ALIGNMENT 8
392
393 // TODO RISCV64
394 struct DECLSPEC_ALIGN(16) UMEntryThunkCode
395 {
396     DWORD        m_code[4];
397
398     TADDR       m_pTargetCode;
399     TADDR       m_pvSecretParam;
400
401     void Encode(UMEntryThunkCode *pEntryThunkCodeRX, BYTE* pTargetCode, void* pvSecretParam);
402     void Poison();
403
404     LPCBYTE GetEntryPoint() const
405     {
406         LIMITED_METHOD_CONTRACT;
407
408         return (LPCBYTE)this;
409     }
410
411     static int GetEntryPointOffset()
412     {
413         LIMITED_METHOD_CONTRACT;
414
415         return 0;
416     }
417 };
418
419 struct HijackArgs
420 {
421     union
422     {
423         struct {
424              DWORD64 A0;
425              DWORD64 A1;
426          };
427         size_t ReturnValue[2];
428     };
429     union
430     {
431         struct {
432              DWORD64 FA0;
433              DWORD64 FA1;
434          };
435         size_t FPReturnValue[2];
436     };
437     DWORD64 Fp; // frame pointer
438     DWORD64 Gp, Tp, S1, S2, S3, S4, S5, S6, S7, S8, S9, S10, S11;
439     union
440     {
441         DWORD64 Ra;
442         size_t ReturnAddress;
443     };
444  };
445
446 // Precode to shuffle this and retbuf for closed delegates over static methods with return buffer
447 struct ThisPtrRetBufPrecode {
448
449     static const int Type = 0x93;
450
451     UINT32  m_rgCode[6];
452     TADDR   m_pTarget;
453     TADDR   m_pMethodDesc;
454
455     void Init(MethodDesc* pMD, LoaderAllocator *pLoaderAllocator);
456
457     TADDR GetMethodDesc()
458     {
459         LIMITED_METHOD_DAC_CONTRACT;
460
461         return m_pMethodDesc;
462     }
463
464     PCODE GetTarget()
465     {
466         LIMITED_METHOD_DAC_CONTRACT;
467         return m_pTarget;
468     }
469
470 #ifndef DACCESS_COMPILE
471     BOOL SetTargetInterlocked(TADDR target, TADDR expected)
472     {
473         CONTRACTL
474         {
475             THROWS;
476             GC_NOTRIGGER;
477         }
478         CONTRACTL_END;
479
480         ExecutableWriterHolder<ThisPtrRetBufPrecode> precodeWriterHolder(this, sizeof(ThisPtrRetBufPrecode));
481         return (TADDR)InterlockedCompareExchange64(
482             (LONGLONG*)&precodeWriterHolder.GetRW()->m_pTarget, (TADDR)target, (TADDR)expected) == expected;
483     }
484 #endif // !DACCESS_COMPILE
485 };
486 typedef DPTR(ThisPtrRetBufPrecode) PTR_ThisPtrRetBufPrecode;
487
488 #endif // __cgencpu_h__