remotemono
RMonoAPIFunctionWrap_Impl.h
1 /*
2  Copyright 2020 David "Alemarius Nexus" Lerch
3 
4  This file is part of RemoteMono.
5 
6  RemoteMono is free software: you can redistribute it and/or modify
7  it under the terms of the GNU Lesser General Public License as published
8  by the Free Software Foundation, either version 3 of the License, or
9  (at your option) any later version.
10 
11  RemoteMono is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  GNU Lesser General Public License for more details.
15 
16  You should have received a copy of the GNU Lesser General Public License
17  along with RemoteMono. If not, see <https://www.gnu.org/licenses/>.
18  */
19 
20 #pragma once
21 
22 #include "../config.h"
23 
24 #include "RMonoAPIFunctionWrap_Def.h"
25 
26 #include <type_traits>
27 #include "RMonoAPIBase_Def.h"
28 #include "../util.h"
29 #include "../asmutil.h"
30 #include "../log.h"
31 
32 using namespace blackbone;
33 
34 
35 
36 namespace remotemono
37 {
38 
39 
40 
41 
42 
43 
44 // **************************************************************
45 // * *
46 // * FRONT CLASS *
47 // * *
48 // **************************************************************
49 
50 
51 // !!!!! IMPORTANT !!!!!
52 //
53 // I know that this is a bit of a hairy mess. This is why this is so heavily documented.
54 //
55 // A few notes and general guidelines for writing assembly code in this class:
56 //
57 // * Whenever possible, try to write code that works on both x86 and x64 without resorting to conditional
58 // checks. This is not about performance, but about readability and maintenance. This means: Don't
59 // use registers r8-r15 unless absolutely necessary, even if it's tempting.
60 // * The wrapper functions have a __cdecl calling convention on x86, and Microsoft x64 on x64, because
61 // those calling conventions are relatively similar and make for a more unified code.
62 // * Almost all of the assembler code here is interleaved with the corresponding (pseudo-)C code in
63 // comments. When editing the assembly, make sure to update the C code as well.
64 // * It's okay to do a bunch of copying between registers and stack, even if that could be optimized
65 // out. We are calling methods in a remote process - this function is certainly not the bottleneck
66 // of the system.
67 
68 
69 
70 template <typename CommonT, typename ABI, typename RetT, typename... ArgsT>
71 asmjit::Label RMonoAPIFunctionWrap<CommonT, ABI, RetT, ArgsT...>::compileWrap(blackbone::IAsmHelper& a)
72 {
73  asmjit::Label label = a->newLabel();
74  a->bind(label);
75 
76  RMonoAPIBase* mono = getRemoteMonoAPI();
77  RMonoAPIDispatcher* apid = mono->getAPIDispatcher();
78 
79  AsmBuildContext ctx;
80  ctx.a = &a;
81 
82  apid->apply([&](auto& e) {
83  ctx.gchandleGetTargetAddr = e.api.gchandle_get_target.getRawFuncAddress();
84  ctx.gchandleNewAddr = e.api.gchandle_new.getRawFuncAddress();
85  ctx.objectGetClassAddr = e.api.object_get_class.getRawFuncAddress();
86  ctx.classIsValuetypeAddr = e.api.class_is_valuetype.getRawFuncAddress();
87  ctx.objectUnboxAddr = e.api.object_unbox.getRawFuncAddress();
88  });
89 
90  if constexpr(needsWrapFunc()) {
91  generateWrapperAsm(ctx);
92  }
93 
94  return label;
95 }
96 
97 
98 template <typename CommonT, typename ABI, typename RetT, typename... ArgsT>
99 void RMonoAPIFunctionWrap<CommonT, ABI, RetT, ArgsT...>::generateWrapperAsm(AsmBuildContext& ctx)
100 {
101  using namespace asmjit;
102  using namespace asmjit::host;
103 
104  IAsmHelper& a = *ctx.a;
105 
106  // TODO: We might want to handle exceptions differently: When an exception output parameter is set (i.e. an exception
107  // was thrown), return values and other output parameters may not actually be valid. So is it safe to handle them
108  // like we do when an exception occurs?
109 
110 
111  ptr_t rawFuncAddr = static_cast<CommonT*>(this)->getRawFuncAddress();
112 
113 
114  Label& lFuncRet = a->newLabel();
115 
116  ctx.x64 = (sizeof(irmono_voidp) == 8);
117  ctx.regSize = sizeof(irmono_voidp);
118  ctx.rawArgStackSize = 0;
119 
120 
121 
122  // **********************************************************
123  // * FUNCTION PROLOG *
124  // **********************************************************
125 
126 
127  // ********** Save registers to stack **********
128 
129  std::vector<GpReg> savedRegisters = { a->zbp, a->zbx, a->zsi, a->zdi };
130 
131  if (ctx.x64) {
132  genWrapperSpillArgsToStackX64<0>(ctx);
133  }
134 
135  for (GpReg reg : savedRegisters) {
136  a->push(reg);
137  }
138 
139 
140  // ********** Reserve misc. static stack space **********
141 
142  int32_t miscStaticStackSize = 0;
143 
144  // Return value
145  miscStaticStackSize += ctx.regSize;
146 
147  a->sub(a->zsp, miscStaticStackSize);
148 
149  ctx.stackOffsRetval = 0;
150 
151 
152  // ********** Save static stack base in ZBP **********
153 
154  a->mov(a->zbp, a->zsp);
155 
156  // Misc. space, saved registers, return address
157  ctx.stackOffsArgBase = int32_t(miscStaticStackSize + savedRegisters.size()*ctx.regSize + ctx.regSize);
158 
159 
160  // ********** Reserve dynamic stack space **********
161 
162  // Align stack to pointer size before we start to allocate dynamic stack. Not entirely sure if it's necessary, but it's
163  // safer to do so for two reasons:
164  // 1. We'll pass pointers to elements in the dynamic stack region to the raw function, and some calling
165  // conventions might require "natural alignment" for values behind argument pointers.
166  // 2. We'll put some MonoObjectPtrRaws there, so the Mono GC doesn't move them, and it's possible that the GC
167  // only scans for pointers at pointer-aligned addresses (don't know if it actually does).
168  if (ctx.x64) {
169  a->and_(rsp, 0xFFFFFFFFFFFFFFF0);
170  } else {
171  a->and_(esp, 0xFFFFFFF8);
172  }
173 
174  // zbx := curDynStackPtr
175  a->mov(a->zbx, a->zsp);
176 
177  // We don't need stackRetval until we start processing the raw function's return value, so we can use its stack
178  // location to store curDynStackPtr for just after the raw function call.
179  //
180  // stackRetval = curDynStackPtr;
181  a->mov(ptr(a->zbp, ctx.stackOffsRetval), a->zbx);
182 
183  // NOTE: From here on out, the only non-volatile registers that can be freely used (by the functions reserving stack,
184  // handling arguments and return types, etc.) are ZSI and ZDI. This is not a lot, but it will have to do.
185  //
186  // ZBX is in use for curDynStackPtr.
187  // ZBP points to the static stack base (and is used later to restore ZSP)
188  // ZSP is obviously used as a stack pointer.
189  // R12-R15 should be avoided because they are x64-only.
190 
191  genWrapperReserveStack(ctx);
192 
193  genWrapperCalculateRawArgStackSize<0>(ctx);
194 
195  if (ctx.x64) {
196  // Reserve at least the 32 bytes of shadow space for x64 calling conventions
197  if (ctx.rawArgStackSize < 32) {
198  ctx.rawArgStackSize = 32;
199  }
200  }
201 
202  // uint8_t stackMisalignment = (zsp - ctx.rawArgStackSize) & 0xF;
203  a->mov(a->zcx, a->zsp);
204  a->sub(a->zcx, ctx.rawArgStackSize);
205  a->and_(a->zcx, 0xF);
206 
207  // zsp -= stackMisalignment
208  a->sub(a->zsp, a->zcx);
209 
210  // zsp -= ctx.rawArgStackSize;
211  a->sub(a->zsp, ctx.rawArgStackSize);
212 
213  // -> Stack is now aligned, raw function argument space directly above ZSP.
214 
215 
216 
217 
218  // **********************************************************
219  // * FUNCTION PAYLOAD *
220  // **********************************************************
221 
222 
223  // ********** Build raw function arguments **********
224 
225  genWrapperBuildRawArgs(ctx);
226 
227 
228  // ********** Call raw function **********
229 
230  if (ctx.x64) {
231  // Move first 4 arguments from stack to registers
232  genWrapperMoveStackArgsToRegsX64<0>(ctx);
233 
234  // NOTE: Shadow space has already been allocated as part of the rawArgStack. Even if less than 4 parameters were
235  // used, rawArgStack has been extended to at least 32 bytes. Also, we don't need to remove the shadow space right
236  // away - we'll just restore ZSP from ZBP in the epilog anyway.
237  a->mov(a->zax, rawFuncAddr);
238  a->call(a->zax);
239  } else {
240  a->mov(a->zax, rawFuncAddr);
241  a->call(a->zax);
242  }
243 
244 
245  // ********** Restore dynamic stack pointer **********
246 
247  // zbx := curDynStackPtr
248  a->mov(a->zbx, ptr(a->zbp, ctx.stackOffsRetval)); // We stored it there when we first defined curDynStackPtr
249 
250 
251  // ********** Handle return value and output arguments **********
252 
253  genWrapperHandleRetAndOutParams(ctx);
254 
255  // return stackRetval;
256  a->mov(a->zax, ptr(a->zbp, ctx.stackOffsRetval));
257 
258 
259 
260 
261  // **********************************************************
262  // * FUNCTION EPILOG *
263  // **********************************************************
264 
265  a->bind(lFuncRet);
266 
267  a->mov(a->zsp, a->zbp);
268 
269  a->add(a->zsp, miscStaticStackSize);
270 
271  for (auto it = savedRegisters.rbegin() ; it != savedRegisters.rend() ; it++) {
272  a->pop(*it);
273  }
274 
275  // NOTE: We don't need to restore register values spilled by a.GenPrologue(), so we can safely omit most of the epilogue
276  //a.GenEpilogue();
277  a->ret();
278 }
279 
280 
281 template <typename CommonT, typename ABI, typename RetT, typename... ArgsT>
282 template <size_t wrapArgIdx>
283 void RMonoAPIFunctionWrap<CommonT, ABI, RetT, ArgsT...>::genWrapperSpillArgsToStackX64(AsmBuildContext& ctx)
284 {
285  using namespace asmjit;
286  using namespace asmjit::host;
287 
288  typedef typename RMonoAPIFunctionWrapTraits<CommonT>::WrapArgsTuple WrapArgsTuple;
289 
290  IAsmHelper& a = *ctx.a;
291 
292  if constexpr (
293  wrapArgIdx < 4
294  && wrapArgIdx < std::tuple_size_v<WrapArgsTuple>
295  ) {
296  static const GpReg intRegs[] = { rcx, rdx, r8, r9 };
297  static const XmmReg floatRegs[] = { xmm0, xmm1, xmm2, xmm3 };
298 
299  static_assert(sizeof(std::tuple_element_t<wrapArgIdx, WrapArgsTuple>) <= sizeof(irmono_voidp),
300  "Spilling large arguments is not supported in X64.");
301 
302  if constexpr(std::is_floating_point_v<std::tuple_element_t<wrapArgIdx, WrapArgsTuple>>) {
303  a->movq(ptr(a->zsp, (wrapArgIdx+1) * sizeof(irmono_voidp)), floatRegs[wrapArgIdx]);
304  } else {
305  a->mov(ptr(a->zsp, (wrapArgIdx+1) * sizeof(irmono_voidp)), intRegs[wrapArgIdx]);
306  }
307 
308  genWrapperSpillArgsToStackX64<wrapArgIdx+1>(ctx);
309  }
310 }
311 
312 
313 template <typename CommonT, typename ABI, typename RetT, typename... ArgsT>
314 void RMonoAPIFunctionWrap<CommonT, ABI, RetT, ArgsT...>::genWrapperReserveStack(AsmBuildContext& ctx)
315 {
316  using namespace asmjit;
317  using namespace asmjit::host;
318 
319  IAsmHelper& a = *ctx.a;
320 
321  if constexpr(std::is_base_of_v<Variant, typename RetT::Type>) {
322  if constexpr(sizeof...(ArgsT) != 0) {
323  // Skip first wrap argument (hidden Variant output parameter)
324  genWrapperReserveArgStack<1, ArgsT...>(ctx, PackHelper<ArgsT>()...);
325  }
326  } else if constexpr (
327  std::is_base_of_v<std::string, typename RetT::Type>
328  || std::is_base_of_v<std::u16string, typename RetT::Type>
329  || std::is_base_of_v<std::u32string, typename RetT::Type>
330  ) {
331  if constexpr(sizeof...(ArgsT) != 0) {
332  // Skip first wrap argument (hidden dataBlockPtr parameter)
333  genWrapperReserveArgStack<1, ArgsT...>(ctx, PackHelper<ArgsT>()...);
334  }
335  } else if constexpr(sizeof...(ArgsT) != 0) {
336  genWrapperReserveArgStack<0, ArgsT...>(ctx, PackHelper<ArgsT>()...);
337  }
338 }
339 
340 
341 template <typename CommonT, typename ABI, typename RetT, typename... ArgsT>
342 template <size_t wrapArgIdx, typename ArgT, typename... RestT>
343 void RMonoAPIFunctionWrap<CommonT, ABI, RetT, ArgsT...>::genWrapperReserveArgStack (
344  AsmBuildContext& ctx,
345  PackHelper<ArgT>,
346  PackHelper<RestT>... rest
347 ) {
348  using namespace asmjit;
349  using namespace asmjit::host;
350 
351  IAsmHelper& a = *ctx.a;
352 
353  // IMPORTANT: Always allocate stack space in multiples of sizeof(RemotePtrT) to ensure that values are aligned to
354  // the pointer size. See comment at dynamic stack allocation above for why that's necessary.
355 
356  if constexpr(std::is_base_of_v<Variant, typename ArgT::Type>) {
357  Label lReserveEnd = a->newLabel();
358 
359  // if (wrapArgs[wrapArgIdx] != nullptr) {
360  a->mov(a->zcx, ptrWrapFuncArg<wrapArgIdx>(ctx));
361  a->jecxz(a->zcx, lReserveEnd);
362 
363  // // !!! Wrap argument points to payload, and flags are stored BEFORE the payload !!!
364  // variantflags_t flags = *((variantflags_t*) (wrapArgs[wrapArgIdx] - sizeof(variantflags_t)));
365  a->movzx(a->zcx, ptr(a->zcx, - (int32_t) sizeof(variantflags_t), sizeof(variantflags_t)));
366 
367  // if ((flags & ParamFlagMonoObjectPtr) != 0) {
368  a->test(a->zcx, ParamFlagMonoObjectPtr);
369  a->jz(lReserveEnd);
370 
371  // __dynstack IRMonoObjectPtrRaw variantDummyPtr;
372  a->sub(a->zsp, sizeof(IRMonoObjectPtrRaw));
373  // }
374  // }
375  a->bind(lReserveEnd);
376  }
377 
378  else if constexpr(std::is_base_of_v<VariantArray, typename ArgT::Type>) {
379  // We need to reserve sizeof(RemotePtrT) bytes on the stack for each MonoObjectPtr argument (except for our own
380  // fixed arguments), because when calling mono_runtime_invoke(), we need to make sure that the raw pointers can
381  // be found on the stack so the Mono GC doesn't move them. It is NOT enough to have the raw pointers in the
382  // dataBlock, because dataBlock is heap-allocated and Mono's GC doesn't look for references on the heap.
383  // Because it's easier and quicker, we'll just allocate space for all arguments, even the non-MonoObjectPtr ones.
384  // It doesn't make a difference unless we call methods with thousands of arguments, and who does that?
385 
386  Label lReserveEnd = a->newLabel();
387 
388  // uint8_t* blockPtr = wrapArgs[wrapArgIdx];
389  a->mov(a->zcx, ptrWrapFuncArg<wrapArgIdx>(ctx));
390 
391  // if (blockPtr != nullptr) {
392  a->jecxz(a->zcx, lReserveEnd);
393 
394  // uint32_t numElems = *((uint32_t*) blockPtr);
395  a->mov(ecx, ptr(a->zcx));
396 
397  // struct VariantArrayStackEntry {
398  // IRMonoObjectPtrRaw objPtr;
399  // IRMonoObjectPtrRaw origArrPtr; // Only valid if (MonoObjectPtr && Out), otherwise NULL
400  // }
401  // __dynstack VariantArrayStackEntry variantArrStackData[numElems];
402  a->shl(a->zcx, static_ilog2(2*sizeof(IRMonoObjectPtrRaw)));
403  a->sub(a->zsp, a->zcx);
404 
405  // }
406  a->bind(lReserveEnd);
407  }
408 
409  else if constexpr(std::is_base_of_v<RMonoObjectHandleTag, typename ArgT::Type>) {
410  if constexpr (tags::has_param_tag_v<ArgT, tags::ParamOutTag>) {
411  Label lReserveEnd = a->newLabel();
412 
413  // if (wrapArgs[wrapArgIdx] != nullptr) {
414  a->mov(a->zcx, ptrWrapFuncArg<wrapArgIdx>(ctx));
415  a->jecxz(a->zcx, lReserveEnd);
416 
417  // __dynstack IRMonoObjectPtrRaw outMonoObjectPtr;
418  a->sub(a->zsp, sizeof(IRMonoObjectPtrRaw));
419 
420  // }
421  a->bind(lReserveEnd);
422  }
423  }
424 
425  genWrapperReserveArgStack<wrapArgIdx+1, RestT...>(ctx, rest...);
426 }
427 
428 
429 template <typename CommonT, typename ABI, typename RetT, typename... ArgsT>
430 template <size_t argIdx>
431 void RMonoAPIFunctionWrap<CommonT, ABI, RetT, ArgsT...>::genWrapperCalculateRawArgStackSize(AsmBuildContext& ctx)
432 {
433  if constexpr(argIdx < std::tuple_size_v<typename RMonoAPIFunctionRawTraits<CommonT>::RawArgsTuple>) {
434  ctx.rawArgStackSize += static_align (
435  (int32_t) sizeof(std::tuple_element_t<argIdx, typename RMonoAPIFunctionRawTraits<CommonT>::RawArgsTuple>),
436  (int32_t) sizeof(irmono_voidp)
437  );
438 
439  genWrapperCalculateRawArgStackSize<argIdx+1>(ctx);
440  }
441 }
442 
443 
444 template <typename CommonT, typename ABI, typename RetT, typename... ArgsT>
445 void RMonoAPIFunctionWrap<CommonT, ABI, RetT, ArgsT...>::genWrapperBuildRawArgs(AsmBuildContext& ctx)
446 {
447  if constexpr(std::is_base_of_v<Variant, typename RetT::Type>) {
448  if constexpr(sizeof...(ArgsT) != 0) {
449  // Skip first wrap argument (hidden Variant output parameter)
450  genWrapperBuildRawArg<1, 0, ArgsT...>(ctx, PackHelper<ArgsT>()...);
451  }
452  } else if constexpr (
453  std::is_base_of_v<std::string, typename RetT::Type>
454  || std::is_base_of_v<std::u16string, typename RetT::Type>
455  || std::is_base_of_v<std::u32string, typename RetT::Type>
456  ) {
457  if constexpr(sizeof...(ArgsT) != 0) {
458  // Skip first wrap argument (hidden dataBlockPtr parameter)
459  genWrapperBuildRawArg<1, 0, ArgsT...>(ctx, PackHelper<ArgsT>()...);
460  }
461  } else if constexpr(sizeof...(ArgsT) != 0) {
462  genWrapperBuildRawArg<0, 0, ArgsT...>(ctx, PackHelper<ArgsT>()...);
463  }
464 }
465 
466 
467 template <typename CommonT, typename ABI, typename RetT, typename... ArgsT>
468 template <size_t wrapArgIdx, size_t rawArgIdx, typename ArgT, typename... RestT>
469 void RMonoAPIFunctionWrap<CommonT, ABI, RetT, ArgsT...>::genWrapperBuildRawArg (
470  AsmBuildContext& ctx,
471  PackHelper<ArgT>,
472  PackHelper<RestT>... rest
473 ) {
474  using namespace asmjit;
475  using namespace asmjit::host;
476 
477  IAsmHelper& a = *ctx.a;
478 
479  if constexpr(std::is_base_of_v<Variant, typename ArgT::Type>) {
480  Label lBuildEnd = a->newLabel();
481  Label lNullPtr = a->newLabel();
482  Label lNotMonoObjectPtr = a->newLabel();
483  Label lNoAutoUnbox = a->newLabel();
484  Label lNotDirectPtr = a->newLabel();
485  Label lMonoObjectPtrNotOut = a->newLabel();
486 
487  // if (wrapArgs[wrapArgIdx] != nullptr) {
488  a->mov(a->zcx, ptrWrapFuncArg<wrapArgIdx>(ctx));
489  a->test(a->zcx, a->zcx);
490  a->jz(lNullPtr);
491 
492  // // !!! Wrap argument points to payload, and flags are stored BEFORE the payload !!!
493  // variantflags_t flags = *((variantflags_t*) (wrapArgs[wrapArgIdx] - sizeof(variantflags_t)));
494  a->movzx(a->zsi, ptr(a->zcx, - (int32_t) sizeof(variantflags_t), sizeof(variantflags_t)));
495 
496  // if ((flags & ParamFlagMonoObjectPtr) != 0) {
497  a->test(a->zsi, ParamFlagMonoObjectPtr);
498  a->jz(lNotMonoObjectPtr);
499 
500  // irmono_gchandle gchandle = *((irmono_gchandle*) wrapArgs[wrapArgIdx]);
501  // IRMonoObjectPtrRaw objPtr = mono_gchandle_get_target_checked(gchandle);
502  a->mov(ecx, ptr(a->zcx));
503  genGchandleGetTargetChecked(ctx);
504  a->mov(a->zdi, a->zax);
505 
506  // curDynStackPtr -= sizeof(IRMonoObjectPtrRaw);
507  a->sub(a->zbx, sizeof(IRMonoObjectPtrRaw));
508 
509  // variantDummyPtr = objPtr;
510  a->mov(ptr(a->zbx), a->zdi);
511 
512  // if ((flags & ParamFlagDisableAutoUnbox) == 0 && is_value_type_instance(objPtr)) {
513  a->test(a->zsi, ParamFlagDisableAutoUnbox);
514  a->jnz(lNoAutoUnbox);
515  a->mov(a->zcx, a->zdi);
516  genIsValueTypeInstance(ctx);
517  a->test(a->zax, a->zax);
518  a->jz(lNoAutoUnbox);
519 
520  // irmono_voidp unboxed = mono_object_unbox(objPtr);
521  a->mov(a->zcx, a->zdi);
522  genObjectUnbox(ctx);
523 
524  // rawArgs[rawArgIdx] = unboxed;
525  a->mov(ptrRawFuncArg<rawArgIdx>(ctx), a->zax);
526 
527  a->jmp(lBuildEnd);
528  // } else {
529  a->bind(lNoAutoUnbox);
530 
531  // if ((flags & ParamFlagOut) != 0) {
532  a->test(a->zsi, ParamFlagOut);
533  a->jz(lMonoObjectPtrNotOut);
534 
535  // rawArgs[rawArgIdx] = &variantDummyPtr;
536  a->mov(ptrRawFuncArg<rawArgIdx>(ctx), a->zbx);
537 
538  a->jmp(lBuildEnd);
539  // } else {
540  a->bind(lMonoObjectPtrNotOut);
541 
542  // rawArgs[rawArgIdx] = objPtr;
543  a->mov(ptrRawFuncArg<rawArgIdx>(ctx), a->zdi);
544 
545  // }
546 
547  // }
548 
549  a->jmp(lBuildEnd);
550  // } else if ((flags & ParamFlagDirectPtr) != 0) {
551  a->bind(lNotMonoObjectPtr);
552  a->test(a->zsi, ParamFlagDirectPtr);
553  a->jz(lNotDirectPtr);
554 
555  // rawArgs[rawArgIdx] = *((irmono_voidp*) wrapArgs[wrapArgIdx]);
556  a->mov(a->zax, ptr(a->zcx));
557  a->mov(ptrRawFuncArg<rawArgIdx>(ctx), a->zax);
558 
559  a->jmp(lBuildEnd);
560  // } else {
561  a->bind(lNotDirectPtr);
562 
563  // rawArgs[rawArgIdx] = (irmono_voidp) wrapArgs[wrapArgIdx];
564  a->lea(a->zax, ptr(a->zcx));
565  a->mov(ptrRawFuncArg<rawArgIdx>(ctx), a->zax);
566 
567  // }
568 
569  a->jmp(lBuildEnd);
570  // } else {
571  a->bind(lNullPtr);
572 
573  a->mov(ptrRawFuncArg<rawArgIdx>(ctx, 0, sizeof(irmono_voidp)), 0);
574 
575  // }
576  a->bind(lBuildEnd);
577  }
578 
579  else if constexpr(std::is_base_of_v<VariantArray, typename ArgT::Type>) {
580  Label lBuildEnd = a->newLabel();
581  Label lBlockPtrNull = a->newLabel();
582  Label lLoopStart = a->newLabel();
583  Label lLoopFinal = a->newLabel();
584  Label lLoopEnd = a->newLabel();
585  Label lNotMonoObjectPtr = a->newLabel();
586  Label lNotOut = a->newLabel();
587  Label lNoAutoUnbox = a->newLabel();
588  Label lNoAutoUnboxNotOut = a->newLabel();
589 
590  // blockPtr = wrapArgs[wrapArgIdx];
591  a->mov(a->zcx, ptrWrapFuncArg<wrapArgIdx>(ctx));
592 
593  // if (blockPtr != nullptr && numElems != 0) {
594  a->test(a->zcx, a->zcx);
595  a->jz(lBlockPtrNull);
596  a->cmp(dword_ptr(a->zcx), 0);
597  a->jz(lBlockPtrNull);
598 
599  // uint32_t numElems = *((uint32_t*) blockPtr);
600  a->mov(a->zdx, dword_ptr(a->zcx));
601 
602  // // For the alignment, if sizeof(irmono_voidp) is 4, we are automatically aligned. If it's 8, we are
603  // // either aligned or exactly 4 bytes off.
604  // irmono_voidpp arrEntryPtr = (irmono_voidpp) align(blockPtr + sizeof(uint32_t), sizeof(irmono_voidpp));
605  a->lea(a->zsi, ptr(a->zcx, sizeof(uint32_t)));
606  if (sizeof(irmono_voidp) == 8) {
607  // arrEntryPtr += (arrEntryPtr & 0x7);
608  a->mov(a->zax, a->zsi);
609  a->and_(a->zax, 0x7);
610  a->add(a->zsi, a->zax);
611  }
612 
613  // // As long as sizeof(variantflags_t) <= sizeof(irmono_voidp), we are automatically aligned.
614  // variantflags_t* flagsPtr = (uint8_t*) align(arrEntryPtr + numElems*sizeof(irmono_voidp), sizeof(variantflags_t);
615  a->lea(a->zdi, ptr(a->zsi, a->zdx, static_ilog2(sizeof(irmono_voidp))));
616 
617  // rawArgs[rawArgIdx] = arrEntryPtr;
618  a->mov(ptrRawFuncArg<rawArgIdx>(ctx), a->zsi);
619 
620  // // We'll use the presence of ParamFlagLastArrayElement as a stop condition for the loop. We've already checked
621  // that there's at least one entry in the array above. This way, we don't need to store the loop counter nor
622  // the number of elements in the array. Now we can use ZSI and ZDI for other purposes. :)
623  // do {
624  a->bind(lLoopStart);
625 
626  // curDynStackPtr -= sizeof(VariantArrayStackEntry);
627  a->sub(a->zbx, 2*sizeof(IRMonoObjectPtrRaw));
628 
629  // // Must be NULL unless (MonoObjectPtr && Out == true) for genWrapperHandleOutParams() below.
630  // variantArrStackData[i].origArrPtr = 0;
631  a->mov(ptr(a->zbx, sizeof(IRMonoObjectPtrRaw), sizeof(IRMonoObjectPtrRaw)), 0);
632 
633  // if (*arrEntryPtr != nullptr) {
634  a->cmp(ptr(a->zsi, 0, sizeof(irmono_voidp)), 0);
635  a->je(lLoopFinal);
636 
637  // if (((*flagsPtr) & ParamFlagMonoObjectPtr) != 0) {
638  a->test(ptr(a->zdi, 0, sizeof(variantflags_t)), ParamFlagMonoObjectPtr);
639  a->jz(lNotMonoObjectPtr);
640 
641  // irmono_gchandle gchandle = *((irmono_gchandle*) *arrEntryPtr);
642  // IRMonoObjectPtrRaw objPtr = mono_gchandle_get_target_checked(gchandle);
643  a->mov(a->zcx, ptr(a->zsi));
644  a->mov(ecx, ptr(a->zcx));
645  genGchandleGetTargetChecked(ctx);
646 
647  // variantArrStackData[i].objPtr = objPtr;
648  a->mov(ptr(a->zbx), a->zax);
649 
650  // if (((*flagsPtr) & ParamFlagOut) != 0) {
651  a->test(ptr(a->zdi, 0, sizeof(variantflags_t)), ParamFlagOut);
652  a->jz(lNotOut);
653 
654  // variantArrStackData[i].origArrPtr = *arrEntryPtr;
655  a->mov(a->zcx, ptr(a->zsi));
656  a->mov(ptr(a->zbx, sizeof(IRMonoObjectPtrRaw)), a->zcx);
657 
658  // }
659  a->bind(lNotOut);
660 
661  // // Always store objPtr into the array first. Even if it's overwritten in the next few lines
662  // // of code, we can use this to restore objPtr from the array after zcx has been overwritten
663  // // by a function call.
664  // *arrEntryPtr = objPtr;
665  a->mov(ptr(a->zsi), a->zax);
666 
667  // if (((*flagsPtr) & ParamFlagDisableAutoUnbox) == 0 && is_value_type_instance(objPtr)) {
668  a->test(ptr(a->zdi, 0, sizeof(variantflags_t)), ParamFlagDisableAutoUnbox);
669  a->jnz(lNoAutoUnbox);
670  a->mov(a->zcx, a->zax);
671  genIsValueTypeInstance(ctx);
672  a->test(a->zax, a->zax);
673  a->mov(a->zax, ptr(a->zsi)); // Restore objPtr (overwritten by function call)
674  a->jz(lNoAutoUnbox);
675 
676  // irmono_voidp unboxed = mono_object_unbox(objPtr);
677  a->mov(a->zcx, a->zax);
678  genObjectUnbox(ctx);
679 
680  // *arrEntryPtr = unboxed;
681  a->mov(ptr(a->zsi), a->zax);
682 
683  a->jmp(lLoopFinal);
684  // } else {
685  a->bind(lNoAutoUnbox);
686 
687  // if (((*flagsPtr) & ParamFlagOut) != 0) {
688  a->test(ptr(a->zdi, 0, sizeof(variantflags_t)), ParamFlagOut);
689  a->jz(lNoAutoUnboxNotOut);
690 
691  // *arrEntryPtr = &variantArrStackData[i].objPtr;
692  a->mov(ptr(a->zsi), a->zbx);
693 
694  // }
695  a->bind(lNoAutoUnboxNotOut);
696 
697  // }
698 
699  a->jmp(lLoopFinal);
700  // } else if (((*flagsPtr) & ParamFlagDirectPtr) != 0) {
701  a->bind(lNotMonoObjectPtr);
702  a->test(ptr(a->zdi, 0, sizeof(variantflags_t)), ParamFlagDirectPtr);
703  a->jz(lLoopFinal);
704 
705  // *arrEntryPtr = *((irmono_voidp*) *arrEntryPtr);
706  a->mov(a->zax, ptr(a->zsi));
707  a->mov(a->zax, ptr(a->zax));
708  a->mov(ptr(a->zsi), a->zax);
709 
710  // }
711  // }
712 
713  a->bind(lLoopFinal);
714 
715  // arrEntryPtr += sizeof(irmono_voidp);
716  a->add(a->zsi, sizeof(irmono_voidp));
717 
718  // } while (((*flagsPtr++) & ParamFlagLastArrayElement) == 0);
719  a->mov(a->zcx, ptr(a->zdi, 0, sizeof(variantflags_t)));
720  a->add(a->zdi, sizeof(variantflags_t));
721  a->test(a->zcx, ParamFlagLastArrayElement);
722  a->jz(lLoopStart);
723  a->bind(lLoopEnd);
724 
725  a->jmp(lBuildEnd);
726  // } else {
727  a->bind(lBlockPtrNull);
728 
729  // rawArgs[rawArgIdx] = (irmono_voidp) 0;
730  a->mov(ptrRawFuncArg<rawArgIdx>(ctx, 0, sizeof(irmono_voidp)), 0);
731 
732  // }
733  a->bind(lBuildEnd);
734  }
735 
736  else if constexpr (
737  std::is_base_of_v<std::string_view, typename ArgT::Type>
738  || std::is_base_of_v<std::u16string_view, typename ArgT::Type>
739  || std::is_base_of_v<std::u32string_view, typename ArgT::Type>
740  ) {
741  // rawArgs[rawArgIdx] = wrapArgs[wrapArgIdx]
742  a->mov(a->zcx, ptrWrapFuncArg<wrapArgIdx>(ctx));
743  a->mov(ptrRawFuncArg<rawArgIdx>(ctx), a->zcx);
744  }
745 
746  else if constexpr(std::is_base_of_v<RMonoObjectHandleTag, typename ArgT::Type>) {
747  Label lBuildEnd = a->newLabel();
748  Label lNull = a->newLabel();
749 
750  // uint8_t* blockPtr = wrapArgs[wrapArgIdx];
751  a->mov(a->zcx, ptrWrapFuncArg<wrapArgIdx>(ctx));
752 
753  // if (blockPtr != nullptr) {
754  a->jecxz(a->zcx, lNull);
755 
756  if constexpr (tags::has_param_tag_v<ArgT, tags::ParamOutTag>) {
757  // irmono_gchandle gchandle = *((irmono_gchandle*) blockPtr);
758  // IRMonoObjectPtrRaw objPtr = mono_gchandle_get_target_checked(gchandle);
759  a->mov(ecx, ptr(a->zcx));
760  genGchandleGetTargetChecked(ctx);
761 
762  // curDynStackPtr -= sizeof(IRMonoObjectPtrRaw);
763  a->sub(a->zbx, sizeof(IRMonoObjectPtrRaw));
764 
765  // outMonoObjectPtr = objPtr;
766  a->mov(ptr(a->zbx), a->zax);
767 
768  // rawArgs[rawArgIdx] = &outMonoObjectPtr;
769  a->mov(ptrRawFuncArg<rawArgIdx>(ctx), a->zbx);
770  } else {
771  // irmono_gchandle gchandle = (irmono_gchandle) wrapArgs[wrapArgIdx];
772  // rawArgs[rawArgIdx] = mono_gchandle_get_target_checked(gchandle);
773  genGchandleGetTargetChecked(ctx);
774  a->mov(ptrRawFuncArg<rawArgIdx>(ctx), a->zax);
775  }
776 
777  a->jmp(lBuildEnd);
778  // } else {
779  a->bind(lNull);
780 
781  // rawArgs[rawArgIdx] = (irmono_voidp) 0;
782  a->mov(ptrRawFuncArg<rawArgIdx>(ctx, 0, sizeof(irmono_voidp)), 0);
783 
784  // }
785  a->bind(lBuildEnd);
786  }
787 
788  else {
789  constexpr int32_t argSize = (int32_t) sizeof(std::tuple_element_t<rawArgIdx, typename RMonoAPIFunctionRawTraits<CommonT>::RawArgsTuple>);
790 
791  static_assert(argSize == sizeof(std::tuple_element_t<wrapArgIdx, typename RMonoAPIFunctionWrapTraits<CommonT>::WrapArgsTuple>),
792  "Different non-special argument type size for wrap and raw functions is not supported.");
793 
794  // Arguments larger than sizeof(irmono_voidp) are passed as multiple stack entries (e.g. double or uint64_t on 32-bit).
795  for (size_t partIdx = 0 ; partIdx*sizeof(irmono_voidp) < argSize ; partIdx++) {
796  a->mov(a->zcx, ptrWrapFuncArg<wrapArgIdx>(ctx, partIdx));
797  a->mov(ptrRawFuncArg<rawArgIdx>(ctx, partIdx), a->zcx);
798  }
799  }
800 
801  genWrapperBuildRawArg<wrapArgIdx+1, rawArgIdx+1, RestT...>(ctx, rest...);
802 }
803 
804 
805 template <typename CommonT, typename ABI, typename RetT, typename... ArgsT>
806 template <size_t rawArgIdx>
807 void RMonoAPIFunctionWrap<CommonT, ABI, RetT, ArgsT...>::genWrapperMoveStackArgsToRegsX64 (
808  AsmBuildContext& ctx
809 ) {
810  using namespace asmjit;
811  using namespace asmjit::host;
812 
813  typedef typename RMonoAPIFunctionRawTraits<CommonT>::RawArgsTuple RawArgsTuple;
814 
815  IAsmHelper& a = *ctx.a;
816 
817  assert(ctx.x64);
818 
819  if constexpr (
820  rawArgIdx < 4
821  && rawArgIdx < std::tuple_size_v<typename RMonoAPIFunctionRawTraits<CommonT>::RawArgsTuple>
822  ) {
823  static const GpReg intRegs[] = { rcx, rdx, r8, r9 };
824  static const XmmReg floatRegs[] = { xmm0, xmm1, xmm2, xmm3 };
825 
826  static_assert(sizeof(std::tuple_element_t<rawArgIdx, RawArgsTuple>) <= sizeof(irmono_voidp),
827  "Raw argument larger than 8 bytes isn't supported for wrapper function on x64.");
828 
829  if constexpr(std::is_floating_point_v<std::tuple_element_t<rawArgIdx, RawArgsTuple>>) {
830  a->movq(floatRegs[rawArgIdx], ptr(a->zsp, rawArgIdx*sizeof(irmono_voidp)));
831  } else {
832  a->mov(intRegs[rawArgIdx], ptr(a->zsp, rawArgIdx*sizeof(irmono_voidp)));
833  }
834 
835  genWrapperMoveStackArgsToRegsX64<rawArgIdx+1>(ctx);
836  }
837 }
838 
839 
840 template <typename CommonT, typename ABI, typename RetT, typename... ArgsT>
841 void RMonoAPIFunctionWrap<CommonT, ABI, RetT, ArgsT...>::genWrapperHandleRetAndOutParams(AsmBuildContext& ctx)
842 {
843  using namespace asmjit;
844  using namespace asmjit::host;
845 
846  IAsmHelper& a = *ctx.a;
847 
848  if constexpr(std::is_base_of_v<Variant, typename RetT::Type>) {
849  Label lHandleEnd = a->newLabel();
850 
851  // TODO: This assumes that the raw function returns MonoObject**, and not MonoObject* directly. This is
852  // what mono_array_addr_with_size() does, but are there functions that return it directly? Should we make
853  // it configurable via a ReturnTag<>?
854 
855  // variantflags_t flags = (variantflags_t) wrapArgs[0];
856  a->mov(a->zcx, ptrWrapFuncArg<0>(ctx));
857 
858  // if ((flags & ParamFlagMonoObjectPtr) != 0) {
859  a->test(a->zcx, ParamFlagMonoObjectPtr);
860  a->jz(lHandleEnd);
861 
862  // IRMonoObjectPtrRaw rawObj = *((IRMonoObjectPtrRaw*) rawRetval);
863  a->mov(a->zcx, ptr(a->zax));
864 
865  // rawRetval = mono_gchandle_new_checked(rawObj);
866  genGchandleNewChecked(ctx);
867 
868  // }
869  a->bind(lHandleEnd);
870 
871  // stackRetval = rawRetval;
872  a->mov(ptr(a->zbp, ctx.stackOffsRetval), a->zax);
873 
874  // Skip first wrap argument (hidden Variant output param)
875  genWrapperHandleOutParams<1, 0, ArgsT...>(ctx, PackHelper<ArgsT>()...);
876  }
877 
878  else if constexpr(std::is_base_of_v<RMonoObjectHandleTag, typename RetT::Type>) {
879  // stackRetval = mono_gchandle_new_checked(rawRetval);
880  a->mov(a->zcx, a->zax);
881  genGchandleNewChecked(ctx);
882  a->mov(ptr(a->zbp, ctx.stackOffsRetval), a->zax);
883 
884  genWrapperHandleOutParams<0, 0, ArgsT...>(ctx, PackHelper<ArgsT>()...);
885  }
886 
887  else if constexpr (
888  std::is_base_of_v<std::string, typename RetT::Type>
889  || std::is_base_of_v<std::u16string, typename RetT::Type>
890  || std::is_base_of_v<std::u32string, typename RetT::Type>
891  ) {
892  Label lHandleEnd = a->newLabel();
893  Label lNull = a->newLabel();
894  Label lStrlenLoopStart = a->newLabel();
895  Label lStrlenLoopEnd = a->newLabel();
896 
897  // TODO: Maybe find and use a proper strlen() function if available.
898 
899  // if (rawRetval != nullptr) {
900  a->test(a->zax, a->zax);
901  a->jz(lNull);
902 
903  // irmono_voidp str = rawRetval;
904  a->mov(a->zsi, a->zax);
905 
906  // while ( *((CharTPtr) str) != 0 ) {
907  a->bind(lStrlenLoopStart);
908  a->cmp(ptr(a->zsi, 0, sizeof(typename RetT::Type::value_type)), 0);
909  a->je(lStrlenLoopEnd);
910 
911  // str += sizeof(CharT);
912  if (sizeof(typename RetT::Type::value_type) == 1) {
913  a->inc(a->zsi);
914  } else {
915  a->add(a->zsi, sizeof(typename RetT::Type::value_type));
916  }
917 
918  a->jmp(lStrlenLoopStart);
919  // }
920  a->bind(lStrlenLoopEnd);
921 
922  // uint8_t* blockPtr = wrapArgs[0];
923  a->mov(a->zcx, ptrWrapFuncArg<0>(ctx));
924 
925  // *((uint32_t*) blockPtr) = (str-rawRetval) / sizeof(CharT);
926  a->sub(a->zsi, a->zax);
927  if (sizeof(typename RetT::Type::value_type) != 1) {
928  a->shr(a->zsi, static_ilog2(sizeof(typename RetT::Type::value_type)));
929  }
930  a->mov(dword_ptr(a->zcx), esi);
931 
932  // stackRetval = rawRetval;
933  a->mov(ptr(a->zbp, ctx.stackOffsRetval), a->zax);
934 
935  a->jmp(lHandleEnd);
936  // } else {
937  a->bind(lNull);
938 
939  // stackRetval = 0;
940  a->mov(ptr(a->zbp, ctx.stackOffsRetval, sizeof(irmono_voidp)), 0);
941 
942  // }
943  a->bind(lHandleEnd);
944 
945  // Skip first wrap argument (hidden dataBlockPtr parameter)
946  genWrapperHandleOutParams<1, 0, ArgsT...>(ctx, PackHelper<ArgsT>()...);
947  }
948 
949  else if constexpr(std::is_same_v<typename RMonoAPIFunctionWrapTraits<CommonT>::WrapRetType, void>) {
950  // Nothing to do for return value
951 
952  genWrapperHandleOutParams<0, 0, ArgsT...>(ctx, PackHelper<ArgsT>()...);
953  }
954 
955  else {
956  // stackRetval = rawRetval;
957  a->mov(ptr(a->zbp, ctx.stackOffsRetval), a->zax);
958 
959  genWrapperHandleOutParams<0, 0, ArgsT...>(ctx, PackHelper<ArgsT>()...);
960  }
961 }
962 
963 
964 template <typename CommonT, typename ABI, typename RetT, typename... ArgsT>
965 template <size_t wrapArgIdx, size_t rawArgIdx, typename ArgT, typename... RestT>
966 void RMonoAPIFunctionWrap<CommonT, ABI, RetT, ArgsT...>::genWrapperHandleOutParams (
967  AsmBuildContext& ctx,
968  PackHelper<ArgT>,
969  PackHelper<RestT>... rest
970 ) {
971  using namespace asmjit;
972  using namespace asmjit::host;
973 
974  IAsmHelper& a = *ctx.a;
975 
976  if constexpr(std::is_base_of_v<Variant, typename ArgT::Type>) {
977  Label lHandleEnd = a->newLabel();
978 
979  // uint8_t* blockPtr = wrapArgs[wrapArgIdx];
980  a->mov(a->zdi, ptrWrapFuncArg<wrapArgIdx>(ctx));
981 
982  // if (blockPtr != nullptr) {
983  a->test(a->zdi, a->zdi);
984  a->jz(lHandleEnd);
985 
986  // // !!! Wrap argument points to payload, and flags are stored BEFORE the payload !!!
987  // variantflags_t flags = *((variantflags_t*) (blockPtr - sizeof(variantflags_t)));
988  a->movzx(a->zcx, ptr(a->zdi, - (int32_t) sizeof(variantflags_t), sizeof(variantflags_t)));
989 
990  // if ((flags & ParamFlagMonoObjectPtr) != 0) {
991  a->test(a->zcx, ParamFlagMonoObjectPtr);
992  a->jz(lHandleEnd);
993 
994  // curDynStackPtr -= sizeof(IRMonoObjectPtrRaw);
995  a->sub(a->zbx, sizeof(IRMonoObjectPtrRaw));
996 
997  // if ((flags & ParamFlagOut) != 0) {
998  a->test(a->zcx, ParamFlagOut);
999  a->jz(lHandleEnd);
1000 
1001  if constexpr (
1002  tags::has_param_tag_v<ArgT, tags::ParamOutTag>
1003  || tags::has_param_tag_v<ArgT, tags::ParamOvwrInOutTag>
1004  ) {
1005  // irmono_gchandle gchandle = mono_gchandle_new_checked(variantDummyPtr);
1006  a->mov(a->zcx, ptr(a->zbx));
1007  genGchandleNewChecked(ctx);
1008 
1009  // *((irmono_gchandle*) blockPtr) = gchandle;
1010  a->mov(dword_ptr(a->zdi), eax);
1011  }
1012 
1013  // }
1014 
1015  // }
1016 
1017  // }
1018  a->bind(lHandleEnd);
1019  }
1020 
1021  else if constexpr(std::is_base_of_v<VariantArray, typename ArgT::Type>) {
1022  Label lHandleEnd = a->newLabel();
1023  Label lLoopStart = a->newLabel();
1024  Label lLoopFinal = a->newLabel();
1025  Label lLoopEnd = a->newLabel();
1026 
1027  // blockPtr = wrapArgs[wrapArgIdx];
1028  a->mov(a->zdi, ptrWrapFuncArg<wrapArgIdx>(ctx));
1029 
1030  // if (blockPtr != nullptr) {
1031  a->test(a->zdi, a->zdi);
1032  a->jz(lHandleEnd);
1033 
1034  if constexpr (
1035  tags::has_param_tag_v<ArgT, tags::ParamOutTag>
1036  || tags::has_param_tag_v<ArgT, tags::ParamOvwrInOutTag>
1037  ) {
1038  // uint32_t i = 0;
1039  a->xor_(esi, esi);
1040 
1041  // while (i < *((uint32_t*) blockPtr)) {
1042  a->bind(lLoopStart);
1043  a->cmp(esi, dword_ptr(a->zdi));
1044  a->je(lLoopEnd);
1045 
1046  // curDynStackPtr -= sizeof(VariantArrayStackEntry);
1047  a->sub(a->zbx, 2*sizeof(IRMonoObjectPtrRaw));
1048 
1049  // if (variantArrStackData[i].origArrPtr != nullptr) {
1050  a->cmp(ptr(a->zbx, sizeof(IRMonoObjectPtrRaw), sizeof(irmono_voidp)), 0);
1051  a->je(lLoopFinal);
1052 
1053  // irmono_gchandle gchandle = mono_gchandle_new_checked(variantArrStackData[i].objPtr);
1054  a->mov(a->zcx, ptr(a->zbx));
1055  genGchandleNewChecked(ctx);
1056 
1057  // *((irmono_gchandle*) variantArrStackData[i].origArrPtr) = gchandle;
1058  a->mov(a->zcx, ptr(a->zbx, sizeof(IRMonoObjectPtrRaw)));
1059  a->mov(dword_ptr(a->zcx), eax);
1060 
1061  // }
1062 
1063  // i++;
1064  a->bind(lLoopFinal);
1065  a->inc(esi);
1066  a->jmp(lLoopStart);
1067 
1068  // }
1069  a->bind(lLoopEnd);
1070  } else {
1071  // curDynStackPtr -= *((uint32_t*) blockPtr) * sizeof(VariantArrayStackEntry);
1072  a->mov(a->zcx, dword_ptr(a->zdi));
1073  a->shl(a->zcx, 2*sizeof(IRMonoObjectPtrRaw));
1074  a->sub(a->zbx, a->zcx);
1075  }
1076 
1077  // }
1078  a->bind(lHandleEnd);
1079  }
1080 
1081  else if constexpr(std::is_base_of_v<RMonoObjectHandleTag, typename ArgT::Type>) {
1082  if constexpr (tags::has_param_tag_v<ArgT, tags::ParamOutTag>) {
1083  Label lHandleEnd = a->newLabel();
1084 
1085  // uint8_t* blockPtr = wrapArgs[wrapArgIdx];
1086  a->mov(a->zdi, ptrWrapFuncArg<wrapArgIdx>(ctx));
1087 
1088  // if (blockPtr != nullptr) {
1089  a->test(a->zdi, a->zdi);
1090  a->jz(lHandleEnd);
1091 
1092  // curDynStackPtr -= sizeof(IRMonoObjectPtrRaw);
1093  a->sub(a->zbx, sizeof(IRMonoObjectPtrRaw));
1094 
1095  // irmono_gchandle gchandle = mono_gchandle_new_checked(outMonoObjectPtr);
1096  a->mov(a->zcx, ptr(a->zbx));
1097  genGchandleNewChecked(ctx);
1098 
1099  // *((irmono_gchandle*) blockPtr) = gchandle;
1100  a->mov(dword_ptr(a->zdi), eax);
1101 
1102  // }
1103  a->bind(lHandleEnd);
1104  }
1105  }
1106 
1107  genWrapperHandleOutParams<wrapArgIdx+1, rawArgIdx+1, RestT...>(ctx, rest...);
1108 }
1109 
1110 
1111 
1112 
1113 
1114 
1115 template <typename CommonT, typename ABI, typename RetT, typename... ArgsT>
1116 void RMonoAPIFunctionWrap<CommonT, ABI, RetT, ArgsT...>::genGchandleGetTargetChecked(AsmBuildContext& ctx)
1117 {
1118  // NOTE: Always expects a MonoGCHandle in zcx.
1119 
1120  AsmGenGchandleGetTargetChecked(*ctx.a, ctx.gchandleGetTargetAddr, ctx.x64);
1121 }
1122 
1123 
1124 template <typename CommonT, typename ABI, typename RetT, typename... ArgsT>
1125 void RMonoAPIFunctionWrap<CommonT, ABI, RetT, ArgsT...>::genGchandleNewChecked(AsmBuildContext& ctx)
1126 {
1127  // NOTE: Always expects a MonoObjectPtrRaw in zcx.
1128 
1129  AsmGenGchandleNewChecked(*ctx.a, ctx.gchandleNewAddr, ctx.x64);
1130 }
1131 
1132 
1133 template <typename CommonT, typename ABI, typename RetT, typename... ArgsT>
1134 void RMonoAPIFunctionWrap<CommonT, ABI, RetT, ArgsT...>::genIsValueTypeInstance(AsmBuildContext& ctx)
1135 {
1136  AsmGenIsValueTypeInstance(*ctx.a, ctx.objectGetClassAddr, ctx.classIsValuetypeAddr, ctx.x64);
1137 }
1138 
1139 
1140 template <typename CommonT, typename ABI, typename RetT, typename... ArgsT>
1141 void RMonoAPIFunctionWrap<CommonT, ABI, RetT, ArgsT...>::genObjectUnbox(AsmBuildContext& ctx)
1142 {
1143  AsmGenObjectUnbox(*ctx.a, ctx.objectUnboxAddr, ctx.x64);
1144 }
1145 
1146 
1147 }