koffi 1.1.2 → 1.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/CMakeLists.txt +23 -19
  2. package/README.md +16 -27
  3. package/benchmark/CMakeLists.txt +11 -3
  4. package/benchmark/raylib_cc.cc +9 -9
  5. package/build/qemu/1.1.5/koffi_darwin_x64.tar.gz +0 -0
  6. package/build/qemu/1.1.5/koffi_freebsd_arm64.tar.gz +0 -0
  7. package/build/qemu/1.1.5/koffi_freebsd_ia32.tar.gz +0 -0
  8. package/build/qemu/1.1.5/koffi_freebsd_x64.tar.gz +0 -0
  9. package/build/qemu/1.1.5/koffi_linux_arm.tar.gz +0 -0
  10. package/build/qemu/1.1.5/koffi_linux_arm64.tar.gz +0 -0
  11. package/build/qemu/1.1.5/koffi_linux_ia32.tar.gz +0 -0
  12. package/build/qemu/1.1.5/koffi_linux_riscv64.tar.gz +0 -0
  13. package/build/qemu/1.1.5/koffi_linux_x64.tar.gz +0 -0
  14. package/build/qemu/1.1.5/koffi_openbsd_ia32.tar.gz +0 -0
  15. package/build/qemu/1.1.5/koffi_openbsd_x64.tar.gz +0 -0
  16. package/build/qemu/1.1.5/koffi_win32_ia32.tar.gz +0 -0
  17. package/build/qemu/1.1.5/koffi_win32_x64.tar.gz +0 -0
  18. package/package.json +2 -2
  19. package/qemu/qemu.js +18 -10
  20. package/qemu/registry/machines.json +138 -3
  21. package/qemu/registry/sha256sum.txt +27 -12
  22. package/src/abi_arm32.cc +29 -16
  23. package/src/abi_arm32_fwd.S +1 -1
  24. package/src/abi_arm64.cc +33 -17
  25. package/src/abi_arm64_fwd.S +1 -1
  26. package/src/abi_arm64_fwd.asm +107 -0
  27. package/src/abi_riscv64.cc +468 -0
  28. package/src/abi_riscv64_fwd.S +129 -0
  29. package/src/abi_x64_sysv.cc +9 -10
  30. package/src/abi_x64_sysv_fwd.S +113 -1
  31. package/src/abi_x64_win.cc +5 -8
  32. package/src/abi_x86.cc +11 -6
  33. package/src/call.cc +6 -18
  34. package/src/call.hh +13 -23
  35. package/src/ffi.cc +87 -25
  36. package/src/ffi.hh +14 -4
  37. package/src/parser.cc +18 -6
  38. package/src/util.cc +26 -57
  39. package/src/util.hh +17 -1
  40. package/test/CMakeLists.txt +4 -1
  41. package/test/misc.c +34 -0
  42. package/vendor/_patches/glfw_001_fix_openbsd_xlib_soname.patch +145 -0
  43. package/vendor/libcc/libcc.cc +7 -7
  44. package/vendor/libcc/libcc.hh +8 -2
  45. package/vendor/raylib/src/external/glfw/src/egl_context.c +6 -0
  46. package/vendor/raylib/src/external/glfw/src/osmesa_context.c +2 -0
  47. package/vendor/raylib/src/external/glfw/src/vulkan.c +2 -0
  48. package/vendor/raylib/src/external/glfw/src/x11_init.c +20 -0
  49. package/build/qemu/1.1.2/koffi_darwin_x64.tar.gz +0 -0
  50. package/build/qemu/1.1.2/koffi_freebsd_arm64.tar.gz +0 -0
  51. package/build/qemu/1.1.2/koffi_freebsd_ia32.tar.gz +0 -0
  52. package/build/qemu/1.1.2/koffi_freebsd_x64.tar.gz +0 -0
  53. package/build/qemu/1.1.2/koffi_linux_arm.tar.gz +0 -0
  54. package/build/qemu/1.1.2/koffi_linux_arm64.tar.gz +0 -0
  55. package/build/qemu/1.1.2/koffi_linux_ia32.tar.gz +0 -0
  56. package/build/qemu/1.1.2/koffi_linux_x64.tar.gz +0 -0
  57. package/build/qemu/1.1.2/koffi_win32_ia32.tar.gz +0 -0
  58. package/build/qemu/1.1.2/koffi_win32_x64.tar.gz +0 -0
package/src/abi_arm64.cc CHANGED
@@ -11,7 +11,7 @@
11
11
  // You should have received a copy of the GNU Affero General Public License
12
12
  // along with this program. If not, see https://www.gnu.org/licenses/.
13
13
 
14
- #if defined(__aarch64__)
14
+ #if defined(__aarch64__) || defined(_M_ARM64)
15
15
 
16
16
  #include "vendor/libcc/libcc.hh"
17
17
  #include "ffi.hh"
@@ -41,10 +41,15 @@ extern "C" X0X1Ret ForwardCallXGG(const void *func, uint8_t *sp);
41
41
  extern "C" float ForwardCallXF(const void *func, uint8_t *sp);
42
42
  extern "C" HfaRet ForwardCallXDDDD(const void *func, uint8_t *sp);
43
43
 
44
+ static inline int IsHFA(const TypeInfo *type)
45
+ {
46
+ return IsHFA(type, 1, 4);
47
+ }
48
+
44
49
  bool AnalyseFunction(InstanceData *, FunctionInfo *func)
45
50
  {
46
- if (IsHFA(func->ret.type, 1, 4)) {
47
- func->ret.vec_count = func->ret.type->members.len;
51
+ if (int hfa = IsHFA(func->ret.type); hfa) {
52
+ func->ret.vec_count = hfa;
48
53
  } else if (func->ret.type->size <= 16) {
49
54
  func->ret.gpr_count = (func->ret.type->size + 7) / 8;
50
55
  } else {
@@ -81,19 +86,23 @@ bool AnalyseFunction(InstanceData *, FunctionInfo *func)
81
86
  }
82
87
  } break;
83
88
  case PrimitiveKind::Record: {
84
- #ifdef __APPLE__
89
+ int hfa = IsHFA(param.type);
90
+
91
+ #if defined(_WIN32)
92
+ if (!param.variadic) {
93
+ hfa = 0;
94
+ }
95
+ #elif defined(__APPLE__)
85
96
  if (param.variadic) {
86
97
  param.use_memory = (param.type->size > 16);
87
98
  break;
88
99
  }
89
100
  #endif
90
101
 
91
- if (IsHFA(param.type, 1, 4)) {
92
- int vec_count = (int)param.type->members.len;
93
-
94
- if (vec_count <= vec_avail) {
95
- param.vec_count = vec_count;
96
- vec_avail -= vec_count;
102
+ if (hfa) {
103
+ if (hfa <= vec_avail) {
104
+ param.vec_count = hfa;
105
+ vec_avail -= hfa;
97
106
  } else {
98
107
  vec_avail = 0;
99
108
  }
@@ -118,7 +127,15 @@ bool AnalyseFunction(InstanceData *, FunctionInfo *func)
118
127
  case PrimitiveKind::Array: { RG_UNREACHABLE(); } break;
119
128
  case PrimitiveKind::Float32:
120
129
  case PrimitiveKind::Float64: {
121
- #ifdef __APPLE__
130
+ #if defined(_WIN32)
131
+ if (param.variadic) {
132
+ if (gpr_avail) {
133
+ param.gpr_count = 1;
134
+ gpr_avail--;
135
+ }
136
+ break;
137
+ }
138
+ #elif defined(__APPLE__)
122
139
  if (param.variadic)
123
140
  break;
124
141
  #endif
@@ -378,8 +395,7 @@ bool CallData::Prepare(const Napi::CallbackInfo &info)
378
395
  }
379
396
  }
380
397
 
381
- stack = MakeSpan(mem->stack.end(), old_stack_mem.end() - mem->stack.end());
382
- heap = MakeSpan(old_heap_mem.ptr, mem->heap.ptr - old_heap_mem.ptr);
398
+ sp = mem->stack.end();
383
399
 
384
400
  return true;
385
401
  }
@@ -388,8 +404,8 @@ void CallData::Execute()
388
404
  {
389
405
  #define PERFORM_CALL(Suffix) \
390
406
  ([&]() { \
391
- auto ret = (func->forward_fp ? ForwardCallX ## Suffix(func->func, stack.ptr) \
392
- : ForwardCall ## Suffix(func->func, stack.ptr)); \
407
+ auto ret = (func->forward_fp ? ForwardCallX ## Suffix(func->func, sp) \
408
+ : ForwardCall ## Suffix(func->func, sp)); \
393
409
  return ret; \
394
410
  })()
395
411
 
@@ -411,10 +427,10 @@ void CallData::Execute()
411
427
  case PrimitiveKind::Record: {
412
428
  if (func->ret.gpr_count) {
413
429
  X0X1Ret ret = PERFORM_CALL(GG);
414
- memcpy_safe(&result.buf, &ret, RG_SIZE(ret));
430
+ memcpy(&result.buf, &ret, RG_SIZE(ret));
415
431
  } else if (func->ret.vec_count) {
416
432
  HfaRet ret = PERFORM_CALL(DDDD);
417
- memcpy_safe(&result.buf, &ret, RG_SIZE(ret));
433
+ memcpy(&result.buf, &ret, RG_SIZE(ret));
418
434
  } else {
419
435
  PERFORM_CALL(GG);
420
436
  }
@@ -7,7 +7,7 @@
7
7
  // but WITHOUT ANY WARRANTY; without even the implied warranty of
8
8
  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9
9
  // GNU Affero General Public License for more details.
10
-
10
+ //
11
11
  // You should have received a copy of the GNU Affero General Public License
12
12
  // along with this program. If not, see https://www.gnu.org/licenses/.
13
13
 
@@ -0,0 +1,107 @@
1
+ ; This program is free software: you can redistribute it and/or modify
2
+ ; it under the terms of the GNU Affero General Public License as published by
3
+ ; the Free Software Foundation, either version 3 of the License, or
4
+ ; (at your option) any later version.
5
+ ;
6
+ ; This program is distributed in the hope that it will be useful,
7
+ ; but WITHOUT ANY WARRANTY; without even the implied warranty of
8
+ ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9
+ ; GNU Affero General Public License for more details.
10
+ ;
11
+ ; You should have received a copy of the GNU Affero General Public License
12
+ ; along with this program. If not, see https://www.gnu.org/licenses/.
13
+
14
+ AREA |.text|, CODE
15
+
16
+ ; Copy function pointer to r9, in order to save it through argument forwarding.
17
+ ; Save RSP in r29 (non-volatile), and use carefully assembled stack provided by caller.
18
+ MACRO
19
+ prologue
20
+
21
+ stp x29, x30, [sp, -16]!
22
+ mov x29, sp
23
+ mov x9, x0
24
+ add sp, x1, #136
25
+ MEND
26
+
27
+ ; Call native function.
28
+ ; Once done, restore normal stack pointer and return.
29
+ ; The return value is passed untouched through r0, r1, v0 and/or v1.
30
+ MACRO
31
+ epilogue
32
+
33
+ blr x9
34
+ mov sp, x29
35
+ ldp x29, x30, [sp], 16
36
+ ret
37
+ MEND
38
+
39
+ ; Prepare general purpose argument registers from array passed by caller.
40
+ MACRO
41
+ forward_int
42
+
43
+ ldr x8, [x1, 64]
44
+ ldr x7, [x1, 56]
45
+ ldr x6, [x1, 48]
46
+ ldr x5, [x1, 40]
47
+ ldr x4, [x1, 32]
48
+ ldr x3, [x1, 24]
49
+ ldr x2, [x1, 16]
50
+ ldr x0, [x1, 0]
51
+ ldr x1, [x1, 8]
52
+ MEND
53
+
54
+ ; Prepare vector argument registers from array passed by caller.
55
+ MACRO
56
+ forward_vec
57
+
58
+ ldr d7, [x1, 128]
59
+ ldr d6, [x1, 120]
60
+ ldr d5, [x1, 112]
61
+ ldr d4, [x1, 104]
62
+ ldr d3, [x1, 96]
63
+ ldr d2, [x1, 88]
64
+ ldr d1, [x1, 80]
65
+ ldr d0, [x1, 72]
66
+ MEND
67
+
68
+ ForwardCallGG PROC
69
+ prologue
70
+ forward_int
71
+ epilogue
72
+ ENDP
73
+
74
+ ForwardCallF PROC
75
+ prologue
76
+ forward_int
77
+ epilogue
78
+ ENDP
79
+
80
+ ForwardCallDDDD PROC
81
+ prologue
82
+ forward_int
83
+ epilogue
84
+ ENDP
85
+
86
+ ForwardCallXGG PROC
87
+ prologue
88
+ forward_vec
89
+ forward_int
90
+ epilogue
91
+ ENDP
92
+
93
+ ForwardCallXF PROC
94
+ prologue
95
+ forward_vec
96
+ forward_int
97
+ epilogue
98
+ ENDP
99
+
100
+ ForwardCallXDDDD PROC
101
+ prologue
102
+ forward_vec
103
+ forward_int
104
+ epilogue
105
+ ENDP
106
+
107
+ END
@@ -0,0 +1,468 @@
1
+ // This program is free software: you can redistribute it and/or modify
2
+ // it under the terms of the GNU Affero General Public License as published by
3
+ // the Free Software Foundation, either version 3 of the License, or
4
+ // (at your option) any later version.
5
+ //
6
+ // This program is distributed in the hope that it will be useful,
7
+ // but WITHOUT ANY WARRANTY; without even the implied warranty of
8
+ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9
+ // GNU Affero General Public License for more details.
10
+ //
11
+ // You should have received a copy of the GNU Affero General Public License
12
+ // along with this program. If not, see https://www.gnu.org/licenses/.
13
+
14
+ #if __riscv_xlen == 64
15
+
16
+ #include "vendor/libcc/libcc.hh"
17
+ #include "ffi.hh"
18
+ #include "call.hh"
19
+ #include "util.hh"
20
+
21
+ #include <napi.h>
22
+
23
+ namespace RG {
24
+
25
+ struct A0A1Ret {
26
+ uint64_t a0;
27
+ uint64_t a1;
28
+ };
29
+ struct A0Fa0Ret {
30
+ uint64_t a0;
31
+ double fa0;
32
+ };
33
+ struct Fa0A0Ret {
34
+ double fa0;
35
+ uint64_t a0;
36
+ };
37
+ struct Fa0Fa1Ret {
38
+ double fa0;
39
+ double fa1;
40
+ };
41
+
42
+ extern "C" A0A1Ret ForwardCallGG(const void *func, uint8_t *sp);
43
+ extern "C" float ForwardCallF(const void *func, uint8_t *sp);
44
+ extern "C" Fa0A0Ret ForwardCallDG(const void *func, uint8_t *sp);
45
+ extern "C" A0Fa0Ret ForwardCallGD(const void *func, uint8_t *sp);
46
+ extern "C" Fa0Fa1Ret ForwardCallDD(const void *func, uint8_t *sp);
47
+
48
+ extern "C" A0A1Ret ForwardCallXGG(const void *func, uint8_t *sp);
49
+ extern "C" float ForwardCallXF(const void *func, uint8_t *sp);
50
+ extern "C" Fa0A0Ret ForwardCallXDG(const void *func, uint8_t *sp);
51
+ extern "C" A0Fa0Ret ForwardCallXGD(const void *func, uint8_t *sp);
52
+ extern "C" Fa0Fa1Ret ForwardCallXDD(const void *func, uint8_t *sp);
53
+
54
+ static void AnalyseParameter(ParameterInfo *param, int gpr_avail, int vec_avail)
55
+ {
56
+ gpr_avail = std::min(2, gpr_avail);
57
+ vec_avail = std::min(2, vec_avail);
58
+
59
+ if (param->type->size > 16) {
60
+ param->gpr_count = gpr_avail ? 1 : 0;
61
+ param->use_memory = true;
62
+
63
+ return;
64
+ }
65
+
66
+ int gpr_count = 0;
67
+ int vec_count = 0;
68
+ bool gpr_first = false;
69
+
70
+ AnalyseFlat(param->type, [&](const TypeInfo *type, int offset, int count) {
71
+ #if defined(__riscv_float_abi_double)
72
+ bool fp = IsFloat(type);
73
+ #elif defined(__riscv_float_abi_soft)
74
+ bool fp = false;
75
+ #else
76
+ #error The RISC-V single-precision float ABI (LP64F) is not supported
77
+ #endif
78
+
79
+ if (fp) {
80
+ vec_count += count;
81
+ } else {
82
+ gpr_count += count;
83
+ gpr_first |= !vec_count;
84
+ }
85
+ });
86
+
87
+ if (gpr_count == 1 && vec_count == 1 && gpr_avail && vec_avail) {
88
+ param->gpr_count = 1;
89
+ param->vec_count = 1;
90
+ param->gpr_first = gpr_first;
91
+ } else if (vec_count && !gpr_count && vec_count <= vec_avail) {
92
+ param->vec_count = vec_count;
93
+ } else if (gpr_avail) {
94
+ param->gpr_count = (param->type->size + 7) / 8;
95
+ param->gpr_first = true;
96
+ }
97
+ }
98
+
99
+ bool AnalyseFunction(InstanceData *, FunctionInfo *func)
100
+ {
101
+ AnalyseParameter(&func->ret, 2, 2);
102
+
103
+ int gpr_avail = 8 - func->ret.use_memory;
104
+ int vec_avail = 8;
105
+
106
+ for (ParameterInfo &param: func->parameters) {
107
+ AnalyseParameter(&param, gpr_avail, !param.variadic ? vec_avail : 0);
108
+
109
+ gpr_avail = std::max(0, gpr_avail - param.gpr_count);
110
+ vec_avail = std::max(0, vec_avail - param.vec_count);
111
+ }
112
+
113
+ func->args_size = 8 * func->parameters.len;
114
+ func->forward_fp = (vec_avail < 8);
115
+
116
+ return true;
117
+ }
118
+
119
+ bool CallData::Prepare(const Napi::CallbackInfo &info)
120
+ {
121
+ uint8_t *args_ptr = nullptr;
122
+ uint64_t *gpr_ptr = nullptr;
123
+ uint64_t *vec_ptr = nullptr;
124
+
125
+ // Return through registers unless it's too big
126
+ if (RG_UNLIKELY(!AllocStack(func->args_size, 16, &args_ptr)))
127
+ return false;
128
+ if (RG_UNLIKELY(!AllocStack(8 * 8, 8, &gpr_ptr)))
129
+ return false;
130
+ if (RG_UNLIKELY(!AllocStack(8 * 8, 8, &vec_ptr)))
131
+ return false;
132
+ if (func->ret.use_memory) {
133
+ if (RG_UNLIKELY(!AllocHeap(func->ret.type->size, 16, &return_ptr)))
134
+ return false;
135
+ *(uint8_t **)(gpr_ptr++) = return_ptr;
136
+ }
137
+
138
+ // Push arguments
139
+ for (Size i = 0; i < func->parameters.len; i++) {
140
+ const ParameterInfo &param = func->parameters[i];
141
+ RG_ASSERT(param.directions >= 1 && param.directions <= 3);
142
+
143
+ Napi::Value value = info[param.offset];
144
+
145
+ switch (param.type->primitive) {
146
+ case PrimitiveKind::Void: { RG_UNREACHABLE(); } break;
147
+
148
+ case PrimitiveKind::Bool: {
149
+ if (RG_UNLIKELY(!value.IsBoolean())) {
150
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for argument %2, expected boolean", GetValueType(instance, value), i + 1);
151
+ return false;
152
+ }
153
+
154
+ bool b = value.As<Napi::Boolean>();
155
+
156
+ if (RG_LIKELY(param.gpr_count)) {
157
+ *(gpr_ptr++) = (uint64_t)b;
158
+ } else {
159
+ *(uint64_t *)args_ptr = (uint64_t)b;
160
+ args_ptr += 8;
161
+ }
162
+ } break;
163
+ case PrimitiveKind::Int8:
164
+ case PrimitiveKind::Int16:
165
+ case PrimitiveKind::Int32:
166
+ case PrimitiveKind::Int64: {
167
+ if (RG_UNLIKELY(!value.IsNumber() && !value.IsBigInt())) {
168
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for argument %2, expected number", GetValueType(instance, value), i + 1);
169
+ return false;
170
+ }
171
+
172
+ int64_t v = CopyNumber<int64_t>(value);
173
+
174
+ if (RG_LIKELY(param.gpr_count)) {
175
+ *(int64_t *)(gpr_ptr++) = v;
176
+ } else {
177
+ *(int64_t *)args_ptr = v;
178
+ args_ptr += 8;
179
+ }
180
+ } break;
181
+ case PrimitiveKind::UInt8:
182
+ case PrimitiveKind::UInt16:
183
+ case PrimitiveKind::UInt32:
184
+ case PrimitiveKind::UInt64: {
185
+ if (RG_UNLIKELY(!value.IsNumber() && !value.IsBigInt())) {
186
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for argument %2, expected number", GetValueType(instance, value), i + 1);
187
+ return false;
188
+ }
189
+
190
+ uint64_t v = CopyNumber<uint64_t>(value);
191
+
192
+ if (RG_LIKELY(param.gpr_count)) {
193
+ *(uint64_t *)(gpr_ptr++) = v;
194
+ } else {
195
+ *(uint64_t *)args_ptr = v;
196
+ args_ptr += 8;
197
+ }
198
+ } break;
199
+ case PrimitiveKind::String: {
200
+ const char *str;
201
+ if (RG_LIKELY(value.IsString())) {
202
+ str = PushString(value);
203
+ if (RG_UNLIKELY(!str))
204
+ return false;
205
+ } else if (IsNullOrUndefined(value)) {
206
+ str = nullptr;
207
+ } else {
208
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for argument %2, expected string", GetValueType(instance, value), i + 1);
209
+ return false;
210
+ }
211
+
212
+ if (RG_LIKELY(param.gpr_count)) {
213
+ *(const char **)(gpr_ptr++) = str;
214
+ } else {
215
+ *(const char **)args_ptr = str;
216
+ args_ptr += 8;
217
+ }
218
+ } break;
219
+ case PrimitiveKind::String16: {
220
+ const char16_t *str16;
221
+ if (RG_LIKELY(value.IsString())) {
222
+ str16 = PushString16(value);
223
+ if (RG_UNLIKELY(!str16))
224
+ return false;
225
+ } else if (IsNullOrUndefined(value)) {
226
+ str16 = nullptr;
227
+ } else {
228
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for argument %2, expected string", GetValueType(instance, value), i + 1);
229
+ return false;
230
+ }
231
+
232
+ if (RG_LIKELY(param.gpr_count)) {
233
+ *(const char16_t **)(gpr_ptr++) = str16;
234
+ } else {
235
+ *(const char16_t **)args_ptr = str16;
236
+ args_ptr += 8;
237
+ }
238
+ } break;
239
+ case PrimitiveKind::Pointer: {
240
+ uint8_t *ptr;
241
+
242
+ if (CheckValueTag(instance, value, param.type)) {
243
+ ptr = value.As<Napi::External<uint8_t>>().Data();
244
+ } else if (IsObject(value) && param.type->ref->primitive == PrimitiveKind::Record) {
245
+ Napi::Object obj = value.As<Napi::Object>();
246
+
247
+ if (RG_UNLIKELY(!AllocHeap(param.type->ref->size, 16, &ptr)))
248
+ return false;
249
+
250
+ if (param.directions & 1) {
251
+ if (!PushObject(obj, param.type->ref, ptr))
252
+ return false;
253
+ } else {
254
+ memset(ptr, 0, param.type->size);
255
+ }
256
+ if (param.directions & 2) {
257
+ OutObject *out = out_objects.AppendDefault();
258
+
259
+ out->ref.Reset(obj, 1);
260
+ out->ptr = ptr;
261
+ out->type = param.type->ref;
262
+ }
263
+ } else if (IsNullOrUndefined(value)) {
264
+ ptr = nullptr;
265
+ } else {
266
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for argument %2, expected %3", GetValueType(instance, value), i + 1, param.type->name);
267
+ return false;
268
+ }
269
+
270
+ if (RG_LIKELY(param.gpr_count)) {
271
+ *(uint8_t **)(gpr_ptr++) = ptr;
272
+ } else {
273
+ *(uint8_t **)args_ptr = ptr;
274
+ args_ptr += 8;
275
+ }
276
+ } break;
277
+ case PrimitiveKind::Record: {
278
+ if (RG_UNLIKELY(!IsObject(value))) {
279
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for argument %2, expected object", GetValueType(instance, value), i + 1);
280
+ return false;
281
+ }
282
+
283
+ Napi::Object obj = value.As<Napi::Object>();
284
+
285
+ if (!param.use_memory) {
286
+ RG_ASSERT(param.type->size <= 16);
287
+
288
+ // Split float or mixed int-float structs to registers
289
+ int realign = param.vec_count ? 8 : 0;
290
+
291
+ uint64_t buf[2] = { 0xFFFFFFFFFFFFFFFFull, 0xFFFFFFFFFFFFFFFFull };
292
+ if (!PushObject(obj, param.type, (uint8_t *)buf, realign))
293
+ return false;
294
+ uint64_t *ptr = buf;
295
+
296
+ if (param.gpr_first) {
297
+ *(gpr_ptr++) = *(ptr++);
298
+ *((param.vec_count ? vec_ptr : gpr_ptr)++) = *(ptr++);
299
+ gpr_ptr -= (param.gpr_count == 1);
300
+ } else if (param.vec_count) {
301
+ *(vec_ptr++) = *(ptr++);
302
+ *((param.gpr_count ? gpr_ptr : vec_ptr)++) = *(ptr++);
303
+ } else {
304
+ RG_ASSERT(param.type->align <= 8);
305
+
306
+ memcpy_safe(args_ptr, ptr, param.type->size);
307
+ args_ptr += AlignLen(param.type->size, 8);
308
+ }
309
+ } else {
310
+ uint8_t *ptr;
311
+ if (RG_UNLIKELY(!AllocHeap(param.type->size, 16, &ptr)))
312
+ return false;
313
+
314
+ if (param.gpr_count) {
315
+ RG_ASSERT(param.gpr_count == 1);
316
+ RG_ASSERT(param.vec_count == 0);
317
+
318
+ *(uint8_t **)(gpr_ptr++) = ptr;
319
+ } else {
320
+ *(uint8_t **)args_ptr = ptr;
321
+ args_ptr += 8;
322
+ }
323
+
324
+ if (!PushObject(obj, param.type, ptr))
325
+ return false;
326
+ }
327
+ } break;
328
+ case PrimitiveKind::Array: { RG_UNREACHABLE(); } break;
329
+ case PrimitiveKind::Float32: {
330
+ if (RG_UNLIKELY(!value.IsNumber() && !value.IsBigInt())) {
331
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for argument %2, expected number", GetValueType(instance, value), i + 1);
332
+ return false;
333
+ }
334
+
335
+ float f = CopyNumber<float>(value);
336
+
337
+ if (RG_LIKELY(param.vec_count)) {
338
+ memset((uint8_t *)vec_ptr + 4, 0xFF, 4);
339
+ *(float *)(vec_ptr++) = f;
340
+ } else if (param.gpr_count) {
341
+ memset((uint8_t *)gpr_ptr + 4, 0xFF, 4);
342
+ *(float *)(gpr_ptr++) = f;
343
+ } else {
344
+ memset(args_ptr + 4, 0xFF, 4);
345
+ *(float *)args_ptr = f;
346
+ }
347
+ } break;
348
+ case PrimitiveKind::Float64: {
349
+ if (RG_UNLIKELY(!value.IsNumber() && !value.IsBigInt())) {
350
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for argument %2, expected number", GetValueType(instance, value), i + 1);
351
+ return false;
352
+ }
353
+
354
+ double d = CopyNumber<double>(value);
355
+
356
+ if (RG_LIKELY(param.vec_count)) {
357
+ *(double *)(vec_ptr++) = d;
358
+ } else if (param.gpr_count) {
359
+ *(double *)(gpr_ptr++) = d;
360
+ } else {
361
+ *(double *)args_ptr = d;
362
+ args_ptr += 8;
363
+ }
364
+ } break;
365
+ }
366
+ }
367
+
368
+ sp = mem->stack.end();
369
+
370
+ return true;
371
+ }
372
+
373
+ void CallData::Execute()
374
+ {
375
+ #define PERFORM_CALL(Suffix) \
376
+ ([&]() { \
377
+ auto ret = (func->forward_fp ? ForwardCallX ## Suffix(func->func, sp) \
378
+ : ForwardCall ## Suffix(func->func, sp)); \
379
+ return ret; \
380
+ })()
381
+
382
+ // Execute and convert return value
383
+ switch (func->ret.type->primitive) {
384
+ case PrimitiveKind::Void:
385
+ case PrimitiveKind::Bool:
386
+ case PrimitiveKind::Int8:
387
+ case PrimitiveKind::UInt8:
388
+ case PrimitiveKind::Int16:
389
+ case PrimitiveKind::UInt16:
390
+ case PrimitiveKind::Int32:
391
+ case PrimitiveKind::UInt32:
392
+ case PrimitiveKind::Int64:
393
+ case PrimitiveKind::UInt64:
394
+ case PrimitiveKind::String:
395
+ case PrimitiveKind::String16:
396
+ case PrimitiveKind::Pointer: { result.u64 = PERFORM_CALL(GG).a0; } break;
397
+ case PrimitiveKind::Record: {
398
+ if (func->ret.gpr_first && !func->ret.vec_count) {
399
+ A0A1Ret ret = PERFORM_CALL(GG);
400
+ memcpy(&result.buf, &ret, RG_SIZE(ret));
401
+ } else if (func->ret.gpr_first) {
402
+ A0Fa0Ret ret = PERFORM_CALL(GD);
403
+ memcpy(&result.buf, &ret, RG_SIZE(ret));
404
+ } else if (func->ret.vec_count == 2) {
405
+ Fa0Fa1Ret ret = PERFORM_CALL(DD);
406
+ memcpy(&result.buf, &ret, RG_SIZE(ret));
407
+ } else {
408
+ Fa0A0Ret ret = PERFORM_CALL(DG);
409
+ memcpy(&result.buf, &ret, RG_SIZE(ret));
410
+ }
411
+ } break;
412
+ case PrimitiveKind::Array: { RG_UNREACHABLE(); } break;
413
+ case PrimitiveKind::Float32: { result.f = PERFORM_CALL(F); } break;
414
+ case PrimitiveKind::Float64: { result.d = PERFORM_CALL(DD).fa0; } break;
415
+ }
416
+
417
+ #undef PERFORM_CALL
418
+ }
419
+
420
+ Napi::Value CallData::Complete()
421
+ {
422
+ for (const OutObject &out: out_objects) {
423
+ Napi::Object obj = out.ref.Value().As<Napi::Object>();
424
+ PopObject(obj, out.ptr, out.type);
425
+ }
426
+
427
+ switch (func->ret.type->primitive) {
428
+ case PrimitiveKind::Void: return env.Null();
429
+ case PrimitiveKind::Bool: return Napi::Boolean::New(env, result.u32);
430
+ case PrimitiveKind::Int8:
431
+ case PrimitiveKind::UInt8:
432
+ case PrimitiveKind::Int16:
433
+ case PrimitiveKind::UInt16:
434
+ case PrimitiveKind::Int32:
435
+ case PrimitiveKind::UInt32: return Napi::Number::New(env, (double)result.u32);
436
+ case PrimitiveKind::Int64: return Napi::BigInt::New(env, (int64_t)result.u64);
437
+ case PrimitiveKind::UInt64: return Napi::BigInt::New(env, result.u64);
438
+ case PrimitiveKind::String: return Napi::String::New(env, (const char *)result.ptr);
439
+ case PrimitiveKind::String16: return Napi::String::New(env, (const char16_t *)result.ptr);
440
+ case PrimitiveKind::Pointer: {
441
+ Napi::External<void> external = Napi::External<void>::New(env, result.ptr);
442
+ SetValueTag(instance, external, func->ret.type);
443
+
444
+ return external;
445
+ } break;
446
+ case PrimitiveKind::Record: {
447
+ if (func->ret.vec_count) { // HFA
448
+ Napi::Object obj = PopObject((const uint8_t *)&result.buf, func->ret.type, 8);
449
+ return obj;
450
+ } else {
451
+ const uint8_t *ptr = return_ptr ? (const uint8_t *)return_ptr
452
+ : (const uint8_t *)&result.buf;
453
+
454
+ Napi::Object obj = PopObject(ptr, func->ret.type);
455
+ return obj;
456
+ }
457
+ } break;
458
+ case PrimitiveKind::Array: { RG_UNREACHABLE(); } break;
459
+ case PrimitiveKind::Float32: return Napi::Number::New(env, (double)result.f);
460
+ case PrimitiveKind::Float64: return Napi::Number::New(env, result.d);
461
+ }
462
+
463
+ RG_UNREACHABLE();
464
+ }
465
+
466
+ }
467
+
468
+ #endif