Skip to content

Commit 50fcbb2

Browse files
benglclaude
andcommitted
ffi: remove receiver-strip stub emitter and JIT memory
With v8 fast-call patched to support HasReceiver=kNo (previous commit), the dlsym'd target function pointer is registered directly with v8 as the C function address — no JIT'd receiver-strip trampoline is needed. This removes the entire stub-emitter and JIT-memory infrastructure, along with the platform-specific argument-cap logic (v8's own 8-arg fast-call cap takes over) and the boot-time self-test that verified JIT pages were executable. Deleted: src/ffi/fastcall/jit_memory.{h,cc}, stub_emitter.h, the four per-platform stub_emitter_*.cc files (aarch64, arm, x64_sysv, x64_win), test/cctest/test_ffi_fastcall_{emitter,jit}.cc, and the related node.gyp / config.gypi entries. The CFunctionInfoBundle drops its arg_classes and result_class members (they only existed to feed the stub emitters). IsFastCallEligible loses the per-platform GP/FP/Win64 caps and the AArch32 i64-arg rejection — v8 now handles those uniformly. FastCall- State drops stub_entry/stub_alloc_size; its destructor no longer needs to free JIT pages. Net change: ~2000 lines deleted across src/ and test/cctest/. Functional behavior is unchanged: all 15 FFI tests and the FFI cctests still pass. The benchmark gain over the prior shipping wrapper is +2-24% on AArch64 macOS (largest on many-args at +24% — saved register- shifts in the deleted stub). Signed-off-by: Bryan English <bryan@bryanenglish.com> Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 0cc89a6 commit 50fcbb2

17 files changed

Lines changed: 55 additions & 1520 deletions

node.gyp

Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -474,9 +474,6 @@
474474
'src/ffi/types.h',
475475
],
476476
'node_ffi_fastcall_sources': [
477-
'src/ffi/fastcall/jit_memory.cc',
478-
'src/ffi/fastcall/jit_memory.h',
479-
'src/ffi/fastcall/stub_emitter.h',
480477
'src/ffi/fastcall/cfunction_info.cc',
481478
'src/ffi/fastcall/cfunction_info.h',
482479
],
@@ -1021,20 +1018,6 @@
10211018
[ 'node_use_ffi_fastcall=="true"', {
10221019
'defines': [ 'HAVE_FFI_FASTCALL=1' ],
10231020
'sources': [ '<@(node_ffi_fastcall_sources)' ],
1024-
'conditions': [
1025-
[ 'target_arch=="arm64"', {
1026-
'sources': [ 'src/ffi/fastcall/stub_emitter_aarch64.cc' ],
1027-
}],
1028-
[ 'target_arch=="x64" and OS!="win"', {
1029-
'sources': [ 'src/ffi/fastcall/stub_emitter_x64_sysv.cc' ],
1030-
}],
1031-
[ 'target_arch=="x64" and OS=="win"', {
1032-
'sources': [ 'src/ffi/fastcall/stub_emitter_x64_win.cc' ],
1033-
}],
1034-
[ 'target_arch=="arm"', {
1035-
'sources': [ 'src/ffi/fastcall/stub_emitter_arm.cc' ],
1036-
}],
1037-
],
10381021
}],
10391022
],
10401023
}],
@@ -1106,20 +1089,6 @@
11061089
[ 'node_use_ffi_fastcall=="true"', {
11071090
'defines': [ 'HAVE_FFI_FASTCALL=1' ],
11081091
'sources': [ '<@(node_ffi_fastcall_sources)' ],
1109-
'conditions': [
1110-
[ 'target_arch=="arm64"', {
1111-
'sources': [ 'src/ffi/fastcall/stub_emitter_aarch64.cc' ],
1112-
}],
1113-
[ 'target_arch=="x64" and OS!="win"', {
1114-
'sources': [ 'src/ffi/fastcall/stub_emitter_x64_sysv.cc' ],
1115-
}],
1116-
[ 'target_arch=="x64" and OS=="win"', {
1117-
'sources': [ 'src/ffi/fastcall/stub_emitter_x64_win.cc' ],
1118-
}],
1119-
[ 'target_arch=="arm"', {
1120-
'sources': [ 'src/ffi/fastcall/stub_emitter_arm.cc' ],
1121-
}],
1122-
],
11231092
}],
11241093
],
11251094
}],
@@ -1460,8 +1429,6 @@
14601429
'sources!': [
14611430
'test/cctest/test_ffi_fastcall_cfunction.cc',
14621431
'test/cctest/test_ffi_fastcall_eligibility.cc',
1463-
'test/cctest/test_ffi_fastcall_emitter.cc',
1464-
'test/cctest/test_ffi_fastcall_jit.cc',
14651432
],
14661433
}],
14671434
['v8_enable_inspector==1', {

src/ffi/fastcall/cfunction_info.cc

Lines changed: 20 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -9,50 +9,38 @@ namespace node::ffi::fastcall {
99

1010
namespace {
1111

12-
// Map an ffi_type to (CTypeInfo::Type, ArgClass) for argument positions.
13-
struct ArgMapping {
14-
v8::CTypeInfo::Type ctype;
15-
ArgClass cls;
16-
};
17-
18-
// Map an ffi_type to (CTypeInfo::Type, ResultClass) for the return position.
19-
struct ResultMapping {
20-
v8::CTypeInfo::Type ctype;
21-
ResultClass cls;
22-
};
23-
24-
ArgMapping MapArgType(ffi_type* t) {
12+
v8::CTypeInfo::Type MapArgType(ffi_type* t) {
2513
using T = v8::CTypeInfo::Type;
2614
if (t == &ffi_type_sint8 ||
2715
t == &ffi_type_sint16 ||
28-
t == &ffi_type_sint32) return {T::kInt32, ArgClass::kGP};
16+
t == &ffi_type_sint32) return T::kInt32;
2917
if (t == &ffi_type_uint8 ||
30-
t == &ffi_type_uint16) return {T::kInt32, ArgClass::kGP};
31-
if (t == &ffi_type_uint32) return {T::kUint32, ArgClass::kGP};
32-
if (t == &ffi_type_sint64) return {T::kInt64, ArgClass::kGP};
18+
t == &ffi_type_uint16) return T::kInt32;
19+
if (t == &ffi_type_uint32) return T::kUint32;
20+
if (t == &ffi_type_sint64) return T::kInt64;
3321
if (t == &ffi_type_uint64 ||
34-
t == &ffi_type_pointer) return {T::kUint64, ArgClass::kGP};
35-
if (t == &ffi_type_float) return {T::kFloat32, ArgClass::kFP};
36-
if (t == &ffi_type_double) return {T::kFloat64, ArgClass::kFP};
22+
t == &ffi_type_pointer) return T::kUint64;
23+
if (t == &ffi_type_float) return T::kFloat32;
24+
if (t == &ffi_type_double) return T::kFloat64;
3725
// Unreachable if eligibility was checked before calling BuildCFunctionInfo.
3826
UNREACHABLE("FFI fast-call: MapArgType called with type that passed "
3927
"eligibility but has no arg mapping");
4028
}
4129

42-
ResultMapping MapResultType(ffi_type* t) {
30+
v8::CTypeInfo::Type MapResultType(ffi_type* t) {
4331
using T = v8::CTypeInfo::Type;
44-
if (t == &ffi_type_void) return {T::kVoid, ResultClass::kVoid};
32+
if (t == &ffi_type_void) return T::kVoid;
4533
if (t == &ffi_type_sint8 ||
4634
t == &ffi_type_sint16 ||
47-
t == &ffi_type_sint32) return {T::kInt32, ResultClass::kGP};
35+
t == &ffi_type_sint32) return T::kInt32;
4836
if (t == &ffi_type_uint8 ||
49-
t == &ffi_type_uint16) return {T::kInt32, ResultClass::kGP};
50-
if (t == &ffi_type_uint32) return {T::kUint32, ResultClass::kGP};
51-
if (t == &ffi_type_sint64) return {T::kInt64, ResultClass::kGP};
37+
t == &ffi_type_uint16) return T::kInt32;
38+
if (t == &ffi_type_uint32) return T::kUint32;
39+
if (t == &ffi_type_sint64) return T::kInt64;
5240
if (t == &ffi_type_uint64 ||
53-
t == &ffi_type_pointer) return {T::kUint64, ResultClass::kGP};
54-
if (t == &ffi_type_float) return {T::kFloat32, ResultClass::kFP};
55-
if (t == &ffi_type_double) return {T::kFloat64, ResultClass::kFP};
41+
t == &ffi_type_pointer) return T::kUint64;
42+
if (t == &ffi_type_float) return T::kFloat32;
43+
if (t == &ffi_type_double) return T::kFloat64;
5644
UNREACHABLE("FFI fast-call: MapResultType called with type that passed "
5745
"eligibility but has no result mapping");
5846
}
@@ -66,12 +54,9 @@ CFunctionInfoBundle::~CFunctionInfoBundle() {
6654

6755
CFunctionInfoBundle::CFunctionInfoBundle(CFunctionInfoBundle&& o) noexcept
6856
: info(o.info),
69-
arg_types(o.arg_types),
70-
arg_classes(std::move(o.arg_classes)),
71-
result_class(o.result_class) {
57+
arg_types(o.arg_types) {
7258
o.info = nullptr;
7359
o.arg_types = nullptr;
74-
o.result_class = ResultClass::kVoid;
7560
}
7661

7762
CFunctionInfoBundle& CFunctionInfoBundle::operator=(
@@ -81,11 +66,8 @@ CFunctionInfoBundle& CFunctionInfoBundle::operator=(
8166
delete info;
8267
info = o.info;
8368
arg_types = o.arg_types;
84-
arg_classes = std::move(o.arg_classes);
85-
result_class = o.result_class;
8669
o.info = nullptr;
8770
o.arg_types = nullptr;
88-
o.result_class = ResultClass::kVoid;
8971
}
9072
return *this;
9173
}
@@ -104,17 +86,12 @@ CFunctionInfoBundle BuildCFunctionInfo(const FFIFunction& fn) {
10486
if (n > 0) {
10587
void* raw = ::operator new[](n * sizeof(v8::CTypeInfo));
10688
b.arg_types = static_cast<v8::CTypeInfo*>(raw);
107-
b.arg_classes.reserve(n);
10889
for (size_t i = 0; i < n; ++i) {
109-
auto m = MapArgType(fn.args[i]);
110-
new (&b.arg_types[i]) v8::CTypeInfo(m.ctype);
111-
b.arg_classes.push_back(m.cls);
90+
new (&b.arg_types[i]) v8::CTypeInfo(MapArgType(fn.args[i]));
11291
}
11392
}
11493

115-
auto rm = MapResultType(fn.return_type);
116-
b.result_class = rm.cls;
117-
v8::CTypeInfo return_info(rm.ctype);
94+
v8::CTypeInfo return_info(MapResultType(fn.return_type));
11895

11996
b.info = new v8::CFunctionInfo(
12097
return_info,

src/ffi/fastcall/cfunction_info.h

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,16 @@
33
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS && \
44
defined(HAVE_FFI_FASTCALL)
55

6-
#include <vector>
7-
8-
#include "ffi/fastcall/stub_emitter.h"
96
#include "node_ffi.h"
107
#include "v8-fast-api-calls.h"
118

129
namespace node::ffi::fastcall {
1310

1411
// Owns dynamically-allocated CFunctionInfo + CTypeInfo[] arrays for a
15-
// single FFI function. Lifetime is tied to the FFIFunctionInfo. Free
16-
// on weak-callback / dynamic library teardown.
12+
// single FFI function. Lifetime is tied to the FFIFunctionInfo.
1713
struct CFunctionInfoBundle {
1814
v8::CFunctionInfo* info = nullptr;
1915
v8::CTypeInfo* arg_types = nullptr;
20-
std::vector<ArgClass> arg_classes;
21-
ResultClass result_class = ResultClass::kVoid;
2216

2317
CFunctionInfoBundle() = default;
2418
~CFunctionInfoBundle();
@@ -28,10 +22,10 @@ struct CFunctionInfoBundle {
2822
CFunctionInfoBundle& operator=(CFunctionInfoBundle&&) noexcept;
2923
};
3024

31-
// Build a CFunctionInfo + CTypeInfo[] for `fn`. The caller must already have
32-
// verified `fn` via IsFastCallEligible before calling this. With
33-
// -fno-exceptions, `new` aborts on OOM rather than throwing, so this function
34-
// never fails — it returns directly instead of using optional.
25+
// Build a CFunctionInfo + CTypeInfo[] for `fn` in HasReceiver=kNo mode.
26+
// The caller must already have verified `fn` via IsFastCallEligible. With
27+
// -fno-exceptions, `new` aborts on OOM rather than throwing, so this
28+
// function never fails — it returns directly instead of using optional.
3529
CFunctionInfoBundle BuildCFunctionInfo(const FFIFunction& fn);
3630

3731
} // namespace node::ffi::fastcall

0 commit comments

Comments
 (0)