koffi 0.9.0

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 (131) hide show
  1. package/CMakeLists.txt +94 -0
  2. package/README.md +153 -0
  3. package/build/ALL_BUILD.vcxproj +190 -0
  4. package/build/ALL_BUILD.vcxproj.filters +8 -0
  5. package/build/CMakeCache.txt +429 -0
  6. package/build/CMakeFiles/3.23.0-rc1/CMakeASMCompiler.cmake +20 -0
  7. package/build/CMakeFiles/3.23.0-rc1/CMakeASM_MASMCompiler.cmake +20 -0
  8. package/build/CMakeFiles/3.23.0-rc1/CMakeCCompiler.cmake +72 -0
  9. package/build/CMakeFiles/3.23.0-rc1/CMakeCXXCompiler.cmake +83 -0
  10. package/build/CMakeFiles/3.23.0-rc1/CMakeDetermineCompilerABI_C.bin +0 -0
  11. package/build/CMakeFiles/3.23.0-rc1/CMakeDetermineCompilerABI_CXX.bin +0 -0
  12. package/build/CMakeFiles/3.23.0-rc1/CMakeRCCompiler.cmake +6 -0
  13. package/build/CMakeFiles/3.23.0-rc1/CMakeSystem.cmake +15 -0
  14. package/build/CMakeFiles/3.23.0-rc1/CompilerIdC/CMakeCCompilerId.c +828 -0
  15. package/build/CMakeFiles/3.23.0-rc1/CompilerIdC/CompilerIdC.exe +0 -0
  16. package/build/CMakeFiles/3.23.0-rc1/CompilerIdC/CompilerIdC.vcxproj +71 -0
  17. package/build/CMakeFiles/3.23.0-rc1/CompilerIdC/Debug/CMakeCCompilerId.obj +0 -0
  18. package/build/CMakeFiles/3.23.0-rc1/CompilerIdC/Debug/CompilerIdC.exe.recipe +11 -0
  19. package/build/CMakeFiles/3.23.0-rc1/CompilerIdC/Debug/CompilerIdC.tlog/CL.command.1.tlog +0 -0
  20. package/build/CMakeFiles/3.23.0-rc1/CompilerIdC/Debug/CompilerIdC.tlog/CL.read.1.tlog +0 -0
  21. package/build/CMakeFiles/3.23.0-rc1/CompilerIdC/Debug/CompilerIdC.tlog/CL.write.1.tlog +0 -0
  22. package/build/CMakeFiles/3.23.0-rc1/CompilerIdC/Debug/CompilerIdC.tlog/CompilerIdC.lastbuildstate +2 -0
  23. package/build/CMakeFiles/3.23.0-rc1/CompilerIdC/Debug/CompilerIdC.tlog/link.command.1.tlog +0 -0
  24. package/build/CMakeFiles/3.23.0-rc1/CompilerIdC/Debug/CompilerIdC.tlog/link.read.1.tlog +0 -0
  25. package/build/CMakeFiles/3.23.0-rc1/CompilerIdC/Debug/CompilerIdC.tlog/link.write.1.tlog +0 -0
  26. package/build/CMakeFiles/3.23.0-rc1/CompilerIdCXX/CMakeCXXCompilerId.cpp +816 -0
  27. package/build/CMakeFiles/3.23.0-rc1/CompilerIdCXX/CompilerIdCXX.exe +0 -0
  28. package/build/CMakeFiles/3.23.0-rc1/CompilerIdCXX/CompilerIdCXX.vcxproj +71 -0
  29. package/build/CMakeFiles/3.23.0-rc1/CompilerIdCXX/Debug/CMakeCXXCompilerId.obj +0 -0
  30. package/build/CMakeFiles/3.23.0-rc1/CompilerIdCXX/Debug/CompilerIdCXX.exe.recipe +11 -0
  31. package/build/CMakeFiles/3.23.0-rc1/CompilerIdCXX/Debug/CompilerIdCXX.tlog/CL.command.1.tlog +0 -0
  32. package/build/CMakeFiles/3.23.0-rc1/CompilerIdCXX/Debug/CompilerIdCXX.tlog/CL.read.1.tlog +0 -0
  33. package/build/CMakeFiles/3.23.0-rc1/CompilerIdCXX/Debug/CompilerIdCXX.tlog/CL.write.1.tlog +0 -0
  34. package/build/CMakeFiles/3.23.0-rc1/CompilerIdCXX/Debug/CompilerIdCXX.tlog/CompilerIdCXX.lastbuildstate +2 -0
  35. package/build/CMakeFiles/3.23.0-rc1/CompilerIdCXX/Debug/CompilerIdCXX.tlog/link.command.1.tlog +0 -0
  36. package/build/CMakeFiles/3.23.0-rc1/CompilerIdCXX/Debug/CompilerIdCXX.tlog/link.read.1.tlog +0 -0
  37. package/build/CMakeFiles/3.23.0-rc1/CompilerIdCXX/Debug/CompilerIdCXX.tlog/link.write.1.tlog +0 -0
  38. package/build/CMakeFiles/3.23.0-rc1/VCTargetsPath.txt +1 -0
  39. package/build/CMakeFiles/3.23.0-rc1/VCTargetsPath.vcxproj +31 -0
  40. package/build/CMakeFiles/3.23.0-rc1/x64/Debug/VCTargetsPath.recipe +11 -0
  41. package/build/CMakeFiles/3.23.0-rc1/x64/Debug/VCTargetsPath.tlog/VCTargetsPath.lastbuildstate +2 -0
  42. package/build/CMakeFiles/41bcd16856091d4a38fd1f71fbe2f202/generate.stamp.rule +1 -0
  43. package/build/CMakeFiles/CMakeError.log +108 -0
  44. package/build/CMakeFiles/CMakeOutput.log +413 -0
  45. package/build/CMakeFiles/TargetDirectories.txt +4 -0
  46. package/build/CMakeFiles/cmake.check_cache +1 -0
  47. package/build/CMakeFiles/generate.stamp +1 -0
  48. package/build/CMakeFiles/generate.stamp.depend +28 -0
  49. package/build/CMakeFiles/generate.stamp.list +1 -0
  50. package/build/Raylib.dir/Release/Raylib.dll.recipe +14 -0
  51. package/build/Raylib.dir/Release/Raylib.tlog/CL.command.1.tlog +0 -0
  52. package/build/Raylib.dir/Release/Raylib.tlog/CL.read.1.tlog +0 -0
  53. package/build/Raylib.dir/Release/Raylib.tlog/CL.write.1.tlog +0 -0
  54. package/build/Raylib.dir/Release/Raylib.tlog/CustomBuild.command.1.tlog +10 -0
  55. package/build/Raylib.dir/Release/Raylib.tlog/CustomBuild.read.1.tlog +27 -0
  56. package/build/Raylib.dir/Release/Raylib.tlog/CustomBuild.write.1.tlog +2 -0
  57. package/build/Raylib.dir/Release/Raylib.tlog/Raylib.lastbuildstate +2 -0
  58. package/build/Raylib.dir/Release/Raylib.tlog/Raylib.write.1u.tlog +0 -0
  59. package/build/Raylib.dir/Release/Raylib.tlog/link.command.1.tlog +0 -0
  60. package/build/Raylib.dir/Release/Raylib.tlog/link.read.1.tlog +0 -0
  61. package/build/Raylib.dir/Release/Raylib.tlog/link.write.1.tlog +0 -0
  62. package/build/Raylib.dir/Release/raudio.obj +0 -0
  63. package/build/Raylib.dir/Release/rcore.obj +0 -0
  64. package/build/Raylib.dir/Release/rglfw.obj +0 -0
  65. package/build/Raylib.dir/Release/rmodels.obj +0 -0
  66. package/build/Raylib.dir/Release/rshapes.obj +0 -0
  67. package/build/Raylib.dir/Release/rtext.obj +0 -0
  68. package/build/Raylib.dir/Release/rtextures.obj +0 -0
  69. package/build/Raylib.dir/Release/utils.obj +0 -0
  70. package/build/Raylib.vcxproj +358 -0
  71. package/build/Raylib.vcxproj.filters +37 -0
  72. package/build/Release/Raylib.dll +0 -0
  73. package/build/Release/Raylib.exp +0 -0
  74. package/build/Release/Raylib.lib +0 -0
  75. package/build/Release/koffi.exp +0 -0
  76. package/build/Release/koffi.lib +0 -0
  77. package/build/Release/koffi.node +0 -0
  78. package/build/ZERO_CHECK.vcxproj +176 -0
  79. package/build/ZERO_CHECK.vcxproj.filters +13 -0
  80. package/build/cmake_install.cmake +44 -0
  81. package/build/koffi.dir/Release/call_arm64.obj +0 -0
  82. package/build/koffi.dir/Release/call_x64_sysv.obj +0 -0
  83. package/build/koffi.dir/Release/call_x64_win.obj +0 -0
  84. package/build/koffi.dir/Release/call_x64_win_fwd.obj +0 -0
  85. package/build/koffi.dir/Release/call_x86.obj +0 -0
  86. package/build/koffi.dir/Release/ffi.obj +0 -0
  87. package/build/koffi.dir/Release/koffi.node.recipe +14 -0
  88. package/build/koffi.dir/Release/koffi.tlog/CL.command.1.tlog +0 -0
  89. package/build/koffi.dir/Release/koffi.tlog/CL.read.1.tlog +0 -0
  90. package/build/koffi.dir/Release/koffi.tlog/CL.write.1.tlog +0 -0
  91. package/build/koffi.dir/Release/koffi.tlog/CustomBuild.command.1.tlog +10 -0
  92. package/build/koffi.dir/Release/koffi.tlog/CustomBuild.read.1.tlog +27 -0
  93. package/build/koffi.dir/Release/koffi.tlog/CustomBuild.write.1.tlog +2 -0
  94. package/build/koffi.dir/Release/koffi.tlog/Masm.read.1u.tlog +0 -0
  95. package/build/koffi.dir/Release/koffi.tlog/Masm.write.1u.tlog +0 -0
  96. package/build/koffi.dir/Release/koffi.tlog/koffi.lastbuildstate +2 -0
  97. package/build/koffi.dir/Release/koffi.tlog/koffi.write.1u.tlog +0 -0
  98. package/build/koffi.dir/Release/koffi.tlog/link.command.1.tlog +0 -0
  99. package/build/koffi.dir/Release/koffi.tlog/link.read.1.tlog +0 -0
  100. package/build/koffi.dir/Release/koffi.tlog/link.write.1.tlog +0 -0
  101. package/build/koffi.dir/Release/libcc.obj +0 -0
  102. package/build/koffi.dir/Release/util.obj +0 -0
  103. package/build/koffi.dir/Release/win_delay_load_hook.obj +0 -0
  104. package/build/koffi.sln +67 -0
  105. package/build/koffi.vcxproj +363 -0
  106. package/build/koffi.vcxproj.filters +40 -0
  107. package/build/x64/Release/ALL_BUILD/ALL_BUILD.recipe +20 -0
  108. package/build/x64/Release/ALL_BUILD/ALL_BUILD.tlog/ALL_BUILD.lastbuildstate +2 -0
  109. package/build/x64/Release/ALL_BUILD/ALL_BUILD.tlog/CustomBuild.command.1.tlog +10 -0
  110. package/build/x64/Release/ALL_BUILD/ALL_BUILD.tlog/CustomBuild.read.1.tlog +27 -0
  111. package/build/x64/Release/ALL_BUILD/ALL_BUILD.tlog/CustomBuild.write.1.tlog +2 -0
  112. package/build/x64/Release/ZERO_CHECK/ZERO_CHECK.recipe +11 -0
  113. package/build/x64/Release/ZERO_CHECK/ZERO_CHECK.tlog/CustomBuild.command.1.tlog +10 -0
  114. package/build/x64/Release/ZERO_CHECK/ZERO_CHECK.tlog/CustomBuild.read.1.tlog +28 -0
  115. package/build/x64/Release/ZERO_CHECK/ZERO_CHECK.tlog/CustomBuild.write.1.tlog +2 -0
  116. package/build/x64/Release/ZERO_CHECK/ZERO_CHECK.tlog/ZERO_CHECK.lastbuildstate +2 -0
  117. package/package.json +14 -0
  118. package/src/call.hh +27 -0
  119. package/src/call_arm64.cc +482 -0
  120. package/src/call_arm64_fwd.S +115 -0
  121. package/src/call_x64_sysv.cc +477 -0
  122. package/src/call_x64_sysv_fwd.S +131 -0
  123. package/src/call_x64_win.cc +243 -0
  124. package/src/call_x64_win_fwd.asm +105 -0
  125. package/src/call_x86.cc +259 -0
  126. package/src/call_x86_fwd.S +48 -0
  127. package/src/call_x86_fwd.asm +50 -0
  128. package/src/ffi.cc +504 -0
  129. package/src/ffi.hh +135 -0
  130. package/src/util.cc +296 -0
  131. package/src/util.hh +80 -0
@@ -0,0 +1,477 @@
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 defined(__x86_64__) && !defined(_WIN64)
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
+ enum class RegisterClass {
26
+ NoClass = 0, // Explicitly 0
27
+ Integer,
28
+ SSE,
29
+ Memory
30
+ };
31
+
32
+ struct RaxRdxRet {
33
+ uint64_t rax;
34
+ uint64_t rdx;
35
+ };
36
+ struct RaxXmm0Ret {
37
+ uint64_t rax;
38
+ double xmm0;
39
+ };
40
+ struct Xmm0RaxRet {
41
+ double xmm0;
42
+ uint64_t rax;
43
+ };
44
+ struct Xmm0Xmm1Ret {
45
+ double xmm0;
46
+ double xmm1;
47
+ };
48
+
49
+ extern "C" RaxRdxRet ForwardCallGG(const void *func, uint8_t *sp);
50
+ extern "C" float ForwardCallF(const void *func, uint8_t *sp);
51
+ extern "C" Xmm0RaxRet ForwardCallDG(const void *func, uint8_t *sp);
52
+ extern "C" RaxXmm0Ret ForwardCallGD(const void *func, uint8_t *sp);
53
+ extern "C" Xmm0Xmm1Ret ForwardCallDD(const void *func, uint8_t *sp);
54
+
55
+ extern "C" RaxRdxRet ForwardCallXGG(const void *func, uint8_t *sp);
56
+ extern "C" float ForwardCallXF(const void *func, uint8_t *sp);
57
+ extern "C" Xmm0RaxRet ForwardCallXDG(const void *func, uint8_t *sp);
58
+ extern "C" RaxXmm0Ret ForwardCallXGD(const void *func, uint8_t *sp);
59
+ extern "C" Xmm0Xmm1Ret ForwardCallXDD(const void *func, uint8_t *sp);
60
+
61
+ static inline RegisterClass MergeClasses(RegisterClass cls1, RegisterClass cls2)
62
+ {
63
+ if (cls1 == cls2)
64
+ return cls1;
65
+
66
+ if (cls1 == RegisterClass::NoClass)
67
+ return cls2;
68
+ if (cls2 == RegisterClass::NoClass)
69
+ return cls1;
70
+
71
+ if (cls1 == RegisterClass::Memory || cls2 == RegisterClass::Memory)
72
+ return RegisterClass::Memory;
73
+ if (cls1 == RegisterClass::Integer || cls2 == RegisterClass::Integer)
74
+ return RegisterClass::Integer;
75
+
76
+ return RegisterClass::SSE;
77
+ }
78
+
79
+ static Size ClassifyType(const TypeInfo *type, Size offset, Span<RegisterClass> classes)
80
+ {
81
+ RG_ASSERT(classes.len > 0);
82
+
83
+ switch (type->primitive) {
84
+ case PrimitiveKind::Void: { return 0; } break;
85
+
86
+ case PrimitiveKind::Bool:
87
+ case PrimitiveKind::Int8:
88
+ case PrimitiveKind::UInt8:
89
+ case PrimitiveKind::Int16:
90
+ case PrimitiveKind::UInt16:
91
+ case PrimitiveKind::Int32:
92
+ case PrimitiveKind::UInt32:
93
+ case PrimitiveKind::Int64:
94
+ case PrimitiveKind::UInt64:
95
+ case PrimitiveKind::String:
96
+ case PrimitiveKind::Pointer: {
97
+ classes[0] = MergeClasses(classes[0], RegisterClass::Integer);
98
+ return 1;
99
+ } break;
100
+
101
+ case PrimitiveKind::Float32:
102
+ case PrimitiveKind::Float64: {
103
+ classes[0] = MergeClasses(classes[0], RegisterClass::SSE);
104
+ return 1;
105
+ } break;
106
+
107
+ case PrimitiveKind::Record: {
108
+ if (type->size > 64) {
109
+ classes[0] = MergeClasses(classes[0], RegisterClass::Memory);
110
+ return 1;
111
+ }
112
+
113
+ for (const RecordMember &member: type->members) {
114
+ Size start = offset / 8;
115
+ ClassifyType(member.type, offset % 8, classes.Take(start, classes.len - start));
116
+ offset += member.type->size;
117
+ }
118
+
119
+ return (offset + 7) / 8;
120
+ } break;
121
+ }
122
+
123
+ RG_UNREACHABLE();
124
+ }
125
+
126
+ static void AnalyseParameter(ParameterInfo *param, int gpr_avail, int xmm_avail)
127
+ {
128
+ LocalArray<RegisterClass, 8> classes = {};
129
+ classes.len = ClassifyType(param->type, 0, classes.data);
130
+
131
+ if (!classes.len)
132
+ return;
133
+ if (classes.len > 2) {
134
+ param->use_memory = true;
135
+ return;
136
+ }
137
+
138
+ int gpr_count = 0;
139
+ int xmm_count = 0;
140
+
141
+ for (RegisterClass cls: classes) {
142
+ RG_ASSERT(cls != RegisterClass::NoClass);
143
+
144
+ if (cls == RegisterClass::Memory) {
145
+ param->use_memory = true;
146
+ return;
147
+ }
148
+
149
+ gpr_count += (cls == RegisterClass::Integer);
150
+ xmm_count += (cls == RegisterClass::SSE);
151
+ }
152
+
153
+ if (gpr_count <= gpr_avail && xmm_count <= xmm_avail){
154
+ param->gpr_count = (int8_t)gpr_count;
155
+ param->xmm_count = (int8_t)xmm_count;
156
+ param->gpr_first = (classes[0] == RegisterClass::Integer);
157
+ } else {
158
+ param->use_memory = true;
159
+ }
160
+ }
161
+
162
+ bool AnalyseFunction(FunctionInfo *func)
163
+ {
164
+ AnalyseParameter(&func->ret, 2, 2);
165
+
166
+ int gpr_avail = 6 - func->ret.use_memory;
167
+ int xmm_avail = 8;
168
+
169
+ for (ParameterInfo &param: func->parameters) {
170
+ AnalyseParameter(&param, gpr_avail, xmm_avail);
171
+
172
+ gpr_avail -= param.gpr_count;
173
+ xmm_avail -= param.xmm_count;
174
+ }
175
+
176
+ func->forward_fp = (xmm_avail < 8);
177
+
178
+ return true;
179
+ }
180
+
181
+ Napi::Value TranslateCall(const Napi::CallbackInfo &info)
182
+ {
183
+ Napi::Env env = info.Env();
184
+ InstanceData *instance = env.GetInstanceData<InstanceData>();
185
+
186
+ FunctionInfo *func = (FunctionInfo *)info.Data();
187
+ LibraryData *lib = func->lib.get();
188
+
189
+ RG_DEFER { lib->tmp_alloc.ReleaseAll(); };
190
+
191
+ // Sanity checks
192
+ if (info.Length() < (uint32_t)func->parameters.len) {
193
+ ThrowError<Napi::TypeError>(env, "Expected %1 arguments, got %2", func->parameters.len, info.Length());
194
+ return env.Null();
195
+ }
196
+
197
+ // Stack pointer and register
198
+ uint8_t *top_ptr = lib->stack.end();
199
+ uint8_t *return_ptr = nullptr;
200
+ uint8_t *args_ptr = nullptr;
201
+ uint64_t *gpr_ptr = nullptr, *xmm_ptr = nullptr;
202
+ uint8_t *sp_ptr = nullptr;
203
+
204
+ // Return through registers unless it's too big
205
+ if (!func->ret.use_memory) {
206
+ args_ptr = top_ptr - func->scratch_size;
207
+ xmm_ptr = (uint64_t *)args_ptr - 8;
208
+ gpr_ptr = xmm_ptr - 6;
209
+ sp_ptr = (uint8_t *)gpr_ptr;
210
+
211
+ #ifdef RG_DEBUG
212
+ memset(sp_ptr, 0, top_ptr - sp_ptr);
213
+ #endif
214
+ } else {
215
+ return_ptr = top_ptr - AlignLen(func->ret.type->size, 16);
216
+
217
+ args_ptr = return_ptr - func->scratch_size;
218
+ xmm_ptr = (uint64_t *)args_ptr - 8;
219
+ gpr_ptr = xmm_ptr - 6;
220
+ sp_ptr = (uint8_t *)gpr_ptr;
221
+
222
+ #ifdef RG_DEBUG
223
+ memset(sp_ptr, 0, top_ptr - sp_ptr);
224
+ #endif
225
+
226
+ *(gpr_ptr++) = (uint64_t)return_ptr;
227
+ }
228
+
229
+ RG_ASSERT(AlignUp(lib->stack.ptr, 16) == lib->stack.ptr);
230
+ RG_ASSERT(AlignUp(lib->stack.end(), 16) == lib->stack.end());
231
+ RG_ASSERT(AlignUp(args_ptr, 16) == args_ptr);
232
+
233
+ // Push arguments
234
+ for (Size i = 0; i < func->parameters.len; i++) {
235
+ const ParameterInfo &param = func->parameters[i];
236
+ Napi::Value value = info[i];
237
+
238
+ switch (param.type->primitive) {
239
+ case PrimitiveKind::Void: { RG_UNREACHABLE(); } break;
240
+
241
+ case PrimitiveKind::Bool: {
242
+ if (RG_UNLIKELY(!value.IsBoolean())) {
243
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for argmument %2, expected boolean", GetValueType(instance, value), i + 1);
244
+ return env.Null();
245
+ }
246
+
247
+ bool b = value.As<Napi::Boolean>();
248
+
249
+ if (RG_LIKELY(param.gpr_count)) {
250
+ *(gpr_ptr++) = (uint64_t)b;
251
+ } else {
252
+ *(args_ptr++) = (uint8_t)b;
253
+ }
254
+ } break;
255
+ case PrimitiveKind::Int8:
256
+ case PrimitiveKind::UInt8:
257
+ case PrimitiveKind::Int16:
258
+ case PrimitiveKind::UInt16:
259
+ case PrimitiveKind::Int32:
260
+ case PrimitiveKind::UInt32:
261
+ case PrimitiveKind::Int64:
262
+ case PrimitiveKind::UInt64: {
263
+ if (RG_UNLIKELY(!value.IsNumber() && !value.IsBigInt())) {
264
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for argument %2, expected number", GetValueType(instance, value), i + 1);
265
+ return env.Null();
266
+ }
267
+
268
+ int64_t v = CopyNodeNumber<int64_t>(value);
269
+
270
+ if (RG_LIKELY(param.gpr_count)) {
271
+ *(gpr_ptr++) = (uint64_t)v;
272
+ } else {
273
+ args_ptr = AlignUp(args_ptr, param.type->align);
274
+ memcpy(args_ptr, &v, param.type->size); // Little Endian
275
+ args_ptr += param.type->size;
276
+ }
277
+ } break;
278
+ case PrimitiveKind::Float32: {
279
+ if (RG_UNLIKELY(!value.IsNumber() && !value.IsBigInt())) {
280
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for argument %2, expected number", GetValueType(instance, value), i + 1);
281
+ return env.Null();
282
+ }
283
+
284
+ float f = CopyNodeNumber<float>(value);
285
+
286
+ if (RG_LIKELY(param.xmm_count)) {
287
+ memcpy(xmm_ptr++, &f, 4);
288
+ } else {
289
+ args_ptr = AlignUp(args_ptr, 4);
290
+ memcpy(args_ptr, &f, 4);
291
+ args_ptr += 4;
292
+ }
293
+ } break;
294
+ case PrimitiveKind::Float64: {
295
+ if (RG_UNLIKELY(!value.IsNumber() && !value.IsBigInt())) {
296
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for argument %2, expected number", GetValueType(instance, value), i + 1);
297
+ return env.Null();
298
+ }
299
+
300
+ double d = CopyNodeNumber<double>(value);
301
+
302
+ if (RG_LIKELY(param.xmm_count)) {
303
+ memcpy(xmm_ptr++, &d, 8);
304
+ } else {
305
+ args_ptr = AlignUp(args_ptr, 8);
306
+ memcpy(args_ptr, &d, 8);
307
+ args_ptr += 8;
308
+ }
309
+ } break;
310
+ case PrimitiveKind::String: {
311
+ if (RG_UNLIKELY(!value.IsString())) {
312
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for argument %2, expected string", GetValueType(instance, value), i + 1);
313
+ return env.Null();
314
+ }
315
+
316
+ const char *str = CopyNodeString(value, &lib->tmp_alloc);
317
+
318
+ if (RG_LIKELY(param.gpr_count)) {
319
+ *(gpr_ptr++) = (uint64_t)str;
320
+ } else {
321
+ args_ptr = AlignUp(args_ptr, 8);
322
+ *(uint64_t *)args_ptr = (uint64_t)str;
323
+ args_ptr += 8;
324
+ }
325
+ } break;
326
+ case PrimitiveKind::Pointer: {
327
+ if (RG_UNLIKELY(!CheckValueTag(instance, value, param.type))) {
328
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for argument %2, expected %3", GetValueType(instance, value), i + 1, param.type->name);
329
+ return env.Null();
330
+ }
331
+
332
+ void *ptr = value.As<Napi::External<void>>();
333
+
334
+ if (RG_LIKELY(param.gpr_count)) {
335
+ *(gpr_ptr++) = (uint64_t)ptr;
336
+ } else {
337
+ args_ptr = AlignUp(args_ptr, 8);
338
+ *(uint64_t *)args_ptr = (uint64_t)ptr;
339
+ args_ptr += 8;
340
+ }
341
+ } break;
342
+
343
+ case PrimitiveKind::Record: {
344
+ if (RG_UNLIKELY(!value.IsObject())) {
345
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for argument %2, expected object", GetValueType(instance, value), i + 1);
346
+ return env.Null();
347
+ }
348
+
349
+ Napi::Object obj = value.As<Napi::Object>();
350
+
351
+ if (param.gpr_count || param.xmm_count) {
352
+ RG_ASSERT(param.type->size <= 16);
353
+
354
+ uint64_t buf[2] = {};
355
+ if (!PushObject(obj, param.type, &lib->tmp_alloc, (uint8_t *)buf))
356
+ return env.Null();
357
+
358
+ if (param.gpr_first) {
359
+ uint64_t *ptr = buf;
360
+
361
+ *(gpr_ptr++) = *(ptr++);
362
+ if (param.gpr_count == 2) {
363
+ *(gpr_ptr++) = *(ptr++);
364
+ } else if (param.xmm_count == 1) {
365
+ *(xmm_ptr++) = *(ptr++);
366
+ }
367
+ } else {
368
+ uint64_t *ptr = buf;
369
+
370
+ *(xmm_ptr++) = *(ptr++);
371
+ if (param.xmm_count == 2) {
372
+ *(xmm_ptr++) = *(ptr++);
373
+ } else if (param.gpr_count == 1) {
374
+ *(gpr_ptr++) = *(ptr++);
375
+ }
376
+ }
377
+ } else if (param.use_memory) {
378
+ args_ptr = AlignUp(args_ptr, param.type->align);
379
+ if (!PushObject(obj, param.type, &lib->tmp_alloc, args_ptr))
380
+ return env.Null();
381
+ args_ptr += param.type->size;
382
+ }
383
+ } break;
384
+ }
385
+ }
386
+
387
+ // DumpStack(func, MakeSpan(sp_ptr, top_ptr - sp_ptr));
388
+
389
+ #define PERFORM_CALL(Suffix) \
390
+ (func->forward_fp ? ForwardCallX ## Suffix(func->func, sp_ptr) \
391
+ : ForwardCall ## Suffix(func->func, sp_ptr))
392
+
393
+ // Execute and convert return value
394
+ switch (func->ret.type->primitive) {
395
+ case PrimitiveKind::Float32: {
396
+ float f = PERFORM_CALL(F);
397
+
398
+ return Napi::Number::New(env, (double)f);
399
+ } break;
400
+
401
+ case PrimitiveKind::Float64: {
402
+ Xmm0RaxRet ret = PERFORM_CALL(DG);
403
+
404
+ return Napi::Number::New(env, ret.xmm0);
405
+ } break;
406
+
407
+ case PrimitiveKind::Record: {
408
+ if (func->ret.gpr_first && !func->ret.xmm_count) {
409
+ RaxRdxRet ret = PERFORM_CALL(GG);
410
+
411
+ Napi::Object obj = PopObject(env, (const uint8_t *)&ret, func->ret.type);
412
+ return obj;
413
+ } else if (func->ret.gpr_first) {
414
+ RaxXmm0Ret ret = PERFORM_CALL(GD);
415
+
416
+ Napi::Object obj = PopObject(env, (const uint8_t *)&ret, func->ret.type);
417
+ return obj;
418
+ } else if (func->ret.xmm_count) {
419
+ Xmm0RaxRet ret = PERFORM_CALL(DG);
420
+
421
+ Napi::Object obj = PopObject(env, (const uint8_t *)&ret, func->ret.type);
422
+ return obj;
423
+ } else if (func->ret.type->size) {
424
+ RG_ASSERT(return_ptr);
425
+
426
+ RaxRdxRet ret = PERFORM_CALL(GG);
427
+ RG_ASSERT(ret.rax == (uint64_t)return_ptr);
428
+
429
+ Napi::Object obj = PopObject(env, return_ptr, func->ret.type);
430
+ return obj;
431
+ } else {
432
+ PERFORM_CALL(GG);
433
+
434
+ Napi::Object obj = Napi::Object::New(env);
435
+ return obj;
436
+ }
437
+ } break;
438
+
439
+ default: {
440
+ RaxRdxRet ret = PERFORM_CALL(GG);
441
+
442
+ switch (func->ret.type->primitive) {
443
+ case PrimitiveKind::Void: return env.Null();
444
+ case PrimitiveKind::Bool: return Napi::Boolean::New(env, ret.rax);
445
+ case PrimitiveKind::Int8: return Napi::Number::New(env, (double)ret.rax);
446
+ case PrimitiveKind::UInt8: return Napi::Number::New(env, (double)ret.rax);
447
+ case PrimitiveKind::Int16: return Napi::Number::New(env, (double)ret.rax);
448
+ case PrimitiveKind::UInt16: return Napi::Number::New(env, (double)ret.rax);
449
+ case PrimitiveKind::Int32: return Napi::Number::New(env, (double)ret.rax);
450
+ case PrimitiveKind::UInt32: return Napi::Number::New(env, (double)ret.rax);
451
+ case PrimitiveKind::Int64: return Napi::BigInt::New(env, (int64_t)ret.rax);
452
+ case PrimitiveKind::UInt64: return Napi::BigInt::New(env, ret.rax);
453
+ case PrimitiveKind::Float32: { RG_UNREACHABLE(); } break;
454
+ case PrimitiveKind::Float64: { RG_UNREACHABLE(); } break;
455
+ case PrimitiveKind::String: return Napi::String::New(env, (const char *)ret.rax);
456
+ case PrimitiveKind::Pointer: {
457
+ void *ptr = (void *)ret.rax;
458
+
459
+ Napi::External<void> external = Napi::External<void>::New(env, ptr);
460
+ SetValueTag(instance, external, func->ret.type);
461
+
462
+ return external;
463
+ } break;
464
+
465
+ case PrimitiveKind::Record: { RG_UNREACHABLE(); } break;
466
+ }
467
+ } break;
468
+ }
469
+
470
+ #undef PERFORM_CALL
471
+
472
+ RG_UNREACHABLE();
473
+ }
474
+
475
+ }
476
+
477
+ #endif
@@ -0,0 +1,131 @@
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
+ // These five are the same, but they differ (in the C side) by their return type.
15
+ // Unlike the five next functions, these ones don't forward XMM argument registers.
16
+ .global ForwardCallGG
17
+ .global ForwardCallF
18
+ .global ForwardCallDG
19
+ .global ForwardCallGD
20
+ .global ForwardCallDD
21
+
22
+ // The X variants are slightly slower, and are used when XMM arguments must be forwarded.
23
+ .global ForwardCallXGG
24
+ .global ForwardCallXF
25
+ .global ForwardCallXDG
26
+ .global ForwardCallXGD
27
+ .global ForwardCallXDD
28
+
29
+ // Copy function pointer to RAX, in order to save it through argument forwarding.
30
+ // Save RSP in RBX (non-volatile), and use carefully assembled stack provided by caller.
31
+ .macro prologue
32
+ .cfi_startproc
33
+ .cfi_def_cfa rsp, 8
34
+ endbr64
35
+ movq %rdi, %rax
36
+ pushq %rbx
37
+ .cfi_def_cfa rsp, 16
38
+ movq %rsp, %rbx
39
+ .cfi_def_cfa rbx, 16
40
+ movq %rsi, %rsp
41
+ addq $112, %rsp
42
+ .endm
43
+
44
+ // Call native function.
45
+ // Once done, restore normal stack pointer and return.
46
+ // The return value is passed untouched through RAX or XMM0.
47
+ .macro epilogue
48
+ call *%rax
49
+ movq %rbx, %rsp
50
+ popq %rbx
51
+ .cfi_def_cfa rsp, 8
52
+ ret
53
+ .cfi_endproc
54
+ .endm
55
+
56
+ // Prepare integer argument registers from array passed by caller.
57
+ .macro forward_int
58
+ movq 40(%rsi), %r9
59
+ movq 32(%rsi), %r8
60
+ movq 24(%rsi), %rcx
61
+ movq 16(%rsi), %rdx
62
+ movq 0(%rsi), %rdi
63
+ movq 8(%rsi), %rsi
64
+ .endm
65
+
66
+ // Prepare XMM argument registers from array passed by caller.
67
+ .macro forward_xmm
68
+ movsd 104(%rsi), %xmm7
69
+ movsd 96(%rsi), %xmm6
70
+ movsd 88(%rsi), %xmm5
71
+ movsd 80(%rsi), %xmm4
72
+ movsd 72(%rsi), %xmm3
73
+ movsd 64(%rsi), %xmm2
74
+ movsd 56(%rsi), %xmm1
75
+ movsd 48(%rsi), %xmm0
76
+ .endm
77
+
78
+ ForwardCallGG:
79
+ prologue
80
+ forward_int
81
+ epilogue
82
+
83
+ ForwardCallF:
84
+ prologue
85
+ forward_int
86
+ epilogue
87
+
88
+ ForwardCallDG:
89
+ prologue
90
+ forward_int
91
+ epilogue
92
+
93
+ ForwardCallGD:
94
+ prologue
95
+ forward_int
96
+ epilogue
97
+
98
+ ForwardCallDD:
99
+ prologue
100
+ forward_int
101
+ epilogue
102
+
103
+ ForwardCallXGG:
104
+ prologue
105
+ forward_xmm
106
+ forward_int
107
+ epilogue
108
+
109
+ ForwardCallXF:
110
+ prologue
111
+ forward_xmm
112
+ forward_int
113
+ epilogue
114
+
115
+ ForwardCallXDG:
116
+ prologue
117
+ forward_xmm
118
+ forward_int
119
+ epilogue
120
+
121
+ ForwardCallXGD:
122
+ prologue
123
+ forward_xmm
124
+ forward_int
125
+ epilogue
126
+
127
+ ForwardCallXDD:
128
+ prologue
129
+ forward_xmm
130
+ forward_int
131
+ epilogue