v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
fast-api-calls.cc
Go to the documentation of this file.
1// Copyright 2021 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
6
9
10namespace v8 {
11
12// Local handles should be trivially copyable so that the contained value can be
13// efficiently passed by value in a register. This is important for two
14// reasons: better performance and a simpler ABI for generated code and fast
15// API calls.
17#ifdef V8_ENABLE_DIRECT_HANDLE
18ASSERT_TRIVIALLY_COPYABLE(api_internal::DirectHandleBase);
19#endif
21
22#if !(defined(V8_ENABLE_LOCAL_OFF_STACK_CHECK) && V8_HAS_ATTRIBUTE_TRIVIAL_ABI)
23// Direct local handles should be trivially copyable, for the same reasons as
24// above. In debug builds, however, where we want to check that such handles are
25// stack-allocated, we define a non-default copy constructor and destructor.
26// This makes them non-trivially copyable. We only do it in builds where we can
27// declare them as "trivial ABI", which guarantees that they can be efficiently
28// passed by value in a register.
32#endif
33
34namespace internal {
35namespace compiler {
36namespace fast_api_call {
37
39 switch (type) {
41 return UINT8_ELEMENTS;
43 return INT32_ELEMENTS;
45 return UINT32_ELEMENTS;
47 return BIGINT64_ELEMENTS;
49 return BIGUINT64_ELEMENTS;
51 return FLOAT32_ELEMENTS;
53 return FLOAT64_ELEMENTS;
62 }
63}
64
65bool CanOptimizeFastSignature(const CFunctionInfo* c_signature) {
66 USE(c_signature);
67
68#if defined(V8_OS_MACOS) && defined(V8_TARGET_ARCH_ARM64)
69 // On MacArm64 hardware we don't support passing of arguments on the stack.
70 if (c_signature->ArgumentCount() > 8) {
71 return false;
72 }
73#endif // defined(V8_OS_MACOS) && defined(V8_TARGET_ARCH_ARM64)
74
75#ifndef V8_ENABLE_FP_PARAMS_IN_C_LINKAGE
76 if (c_signature->ReturnInfo().GetType() == CTypeInfo::Type::kFloat32 ||
77 c_signature->ReturnInfo().GetType() == CTypeInfo::Type::kFloat64) {
78 return false;
79 }
80#endif
81
82#ifdef V8_USE_SIMULATOR_WITH_GENERIC_C_CALLS
83 if (!v8_flags.fast_api_allow_float_in_sim &&
84 (c_signature->ReturnInfo().GetType() == CTypeInfo::Type::kFloat32 ||
85 c_signature->ReturnInfo().GetType() == CTypeInfo::Type::kFloat64)) {
86 return false;
87 }
88#endif
89
90#ifndef V8_TARGET_ARCH_64_BIT
91 if (c_signature->ReturnInfo().GetType() == CTypeInfo::Type::kInt64 ||
92 c_signature->ReturnInfo().GetType() == CTypeInfo::Type::kUint64) {
93 return false;
94 }
95#endif
96
97 for (unsigned int i = 0; i < c_signature->ArgumentCount(); ++i) {
98 USE(i);
99
100#ifdef V8_TARGET_ARCH_X64
101 // Clamp lowering in EffectControlLinearizer uses rounding.
102 uint8_t flags = uint8_t(c_signature->ArgumentInfo(i).GetFlags());
103 if (flags & uint8_t(CTypeInfo::Flags::kClampBit)) {
104 return CpuFeatures::IsSupported(SSE4_2);
105 }
106#endif // V8_TARGET_ARCH_X64
107
108#ifndef V8_ENABLE_FP_PARAMS_IN_C_LINKAGE
109 if (c_signature->ArgumentInfo(i).GetType() == CTypeInfo::Type::kFloat32 ||
110 c_signature->ArgumentInfo(i).GetType() == CTypeInfo::Type::kFloat64) {
111 return false;
112 }
113#endif
114
115#ifdef V8_USE_SIMULATOR_WITH_GENERIC_C_CALLS
116 if (!v8_flags.fast_api_allow_float_in_sim &&
117 (c_signature->ArgumentInfo(i).GetType() == CTypeInfo::Type::kFloat32 ||
118 c_signature->ArgumentInfo(i).GetType() == CTypeInfo::Type::kFloat64)) {
119 return false;
120 }
121#endif
122
123#ifndef V8_TARGET_ARCH_64_BIT
124 if (c_signature->ArgumentInfo(i).GetType() == CTypeInfo::Type::kInt64 ||
125 c_signature->ArgumentInfo(i).GetType() == CTypeInfo::Type::kUint64) {
126 return false;
127 }
128#endif
129 }
130
131 return true;
132}
133
134#define __ gasm()->
135
137 public:
139 GraphAssembler* graph_assembler,
140 const GetParameter& get_parameter,
141 const ConvertReturnValue& convert_return_value,
142 const InitializeOptions& initialize_options,
143 const GenerateSlowApiCall& generate_slow_api_call)
144 : isolate_(isolate),
145 graph_(graph),
146 graph_assembler_(graph_assembler),
147 get_parameter_(get_parameter),
148 convert_return_value_(convert_return_value),
149 initialize_options_(initialize_options),
150 generate_slow_api_call_(generate_slow_api_call) {}
151
152 Node* Build(FastApiCallFunction c_function, Node* data_argument);
153
154 private:
155 Node* WrapFastCall(const CallDescriptor* call_descriptor, int inputs_size,
156 Node** inputs, Node* target,
157 const CFunctionInfo* c_signature, int c_arg_count,
158 Node* stack_slot);
159 void PropagateException();
160
161 Isolate* isolate() const { return isolate_; }
162 TFGraph* graph() const { return graph_; }
171};
172
174 int inputs_size, Node** inputs,
175 Node* target,
176 const CFunctionInfo* c_signature,
177 int c_arg_count, Node* stack_slot) {
178 // CPU profiler support
179 Node* target_address = __ IsolateField(IsolateFieldId::kFastApiCallTarget);
182 target_address, 0, __ BitcastTaggedToWord(target));
183
184 // Update effect and control
185 if (stack_slot != nullptr) {
186 inputs[c_arg_count + 1] = stack_slot;
187 inputs[c_arg_count + 2] = __ effect();
188 inputs[c_arg_count + 3] = __ control();
189 } else {
190 inputs[c_arg_count + 1] = __ effect();
191 inputs[c_arg_count + 2] = __ control();
192 }
193
194 // Create the fast call
195 Node* call = __ Call(call_descriptor, inputs_size, inputs);
196
197 // Reset the CPU profiler target address.
200 target_address, 0, __ IntPtrConstant(0));
201
202 return call;
203}
204
206 Runtime::FunctionId fun_id = Runtime::FunctionId::kPropagateException;
207 const Runtime::Function* fun = Runtime::FunctionForId(fun_id);
208 auto call_descriptor = Linkage::GetRuntimeCallDescriptor(
209 graph()->zone(), fun_id, fun->nargs, Operator::kNoProperties,
211 // The CEntryStub is loaded from the IsolateRoot so that generated code is
212 // Isolate independent. At the moment this is only done for CEntryStub(1).
213 Node* isolate_root = __ LoadRootRegister();
214 DCHECK_EQ(1, fun->result_size);
215 auto centry_id = Builtin::kWasmCEntry;
216 int builtin_slot_offset = IsolateData::BuiltinSlotOffset(centry_id);
217 Node* centry_stub =
218 __ Load(MachineType::Pointer(), isolate_root, builtin_slot_offset);
219 const int kInputCount = 6;
220 Node* inputs[kInputCount];
221 int count = 0;
222 inputs[count++] = centry_stub;
223 inputs[count++] = __ ExternalConstant(ExternalReference::Create(fun_id));
224 inputs[count++] = __ Int32Constant(fun->nargs);
225 inputs[count++] = __ IntPtrConstant(0);
226 inputs[count++] = __ effect();
227 inputs[count++] = __ control();
228 DCHECK_EQ(kInputCount, count);
229
230 __ Call(call_descriptor, count, inputs);
231}
232
234 Node* data_argument) {
235 const CFunctionInfo* c_signature = c_function.signature;
236 const int c_arg_count = c_signature->ArgumentCount();
237
238 // Hint to fast path.
239 auto if_success = __ MakeLabel();
240 auto if_error = __ MakeDeferredLabel();
241
242 // Generate fast call.
243
244 const int kFastTargetAddressInputIndex = 0;
245 const int kFastTargetAddressInputCount = 1;
246
247 const int kEffectAndControlInputCount = 2;
248
249 int extra_input_count =
250 kEffectAndControlInputCount + (c_signature->HasOptions() ? 1 : 0);
251
252 Node** const inputs = graph()->zone()->AllocateArray<Node*>(
253 kFastTargetAddressInputCount + c_arg_count + extra_input_count);
254
256
257 // The inputs to {Call} node for the fast call look like:
258 // [fast callee, receiver, ... C arguments, [optional Options], effect,
259 // control].
260 //
261 // The first input node represents the target address for the fast call.
262 // If the function is not overloaded (c_functions.size() == 1) this is the
263 // address associated to the first and only element in the c_functions vector.
264 // If there are multiple overloads the value of this input will be set later
265 // with a Phi node created by AdaptOverloadedFastCallArgument.
266 inputs[kFastTargetAddressInputIndex] = __ ExternalConstant(
267 ExternalReference::Create(c_function.address, ref_type));
268
269 for (int i = 0; i < c_arg_count; ++i) {
270 inputs[i + kFastTargetAddressInputCount] = get_parameter_(i, &if_error);
271 }
272 DCHECK_NOT_NULL(inputs[kFastTargetAddressInputIndex]);
273
275 graph()->zone(), 1, c_arg_count + (c_signature->HasOptions() ? 1 : 0));
276 MachineType return_type =
278 builder.AddReturn(return_type);
279 for (int i = 0; i < c_arg_count; ++i) {
280 CTypeInfo type = c_signature->ArgumentInfo(i);
282 MachineType machine_type =
283 type.GetSequenceType() == CTypeInfo::SequenceType::kScalar
287 builder.AddParam(machine_type);
288 }
289
290 Node* stack_slot = nullptr;
291 if (c_signature->HasOptions()) {
292 const int kAlign = alignof(v8::FastApiCallbackOptions);
293 const int kSize = sizeof(v8::FastApiCallbackOptions);
294 // If this check fails, you've probably added new fields to
295 // v8::FastApiCallbackOptions, which means you'll need to write code
296 // that initializes and reads from them too.
297 static_assert(kSize == sizeof(uintptr_t) * 2);
298 stack_slot = __ StackSlot(kSize, kAlign);
299
302 stack_slot,
303 static_cast<int>(offsetof(v8::FastApiCallbackOptions, isolate)),
304 __ ExternalConstant(ExternalReference::isolate_address()));
305
306 Node* data_argument_to_pass = __ AdaptLocalArgument(data_argument);
307
310 stack_slot,
311 static_cast<int>(offsetof(v8::FastApiCallbackOptions, data)),
312 data_argument_to_pass);
313
314 initialize_options_(stack_slot);
315
316 builder.AddParam(MachineType::Pointer()); // stack_slot
317 }
318
319 CallDescriptor* call_descriptor =
320 Linkage::GetSimplifiedCDescriptor(graph()->zone(), builder.Get());
321
322 Node* c_call_result =
323 WrapFastCall(call_descriptor, c_arg_count + extra_input_count + 1, inputs,
324 inputs[0], c_signature, c_arg_count, stack_slot);
325
326 Node* exception = __ Load(MachineType::IntPtr(),
327 __ ExternalConstant(ExternalReference::Create(
328 IsolateAddressId::kExceptionAddress, isolate_)),
329 0);
330
331 Node* the_hole =
332 __ Load(MachineType::IntPtr(), __ LoadRootRegister(),
333 IsolateData::root_slot_offset(RootIndex::kTheHoleValue));
334
335 auto throw_label = __ MakeDeferredLabel();
336 auto done = __ MakeLabel();
337 __ GotoIfNot(__ IntPtrEqual(exception, the_hole), &throw_label);
338 __ Goto(&done);
339
340 __ Bind(&throw_label);
342 __ Unreachable();
343
344 __ Bind(&done);
345 Node* fast_call_result = convert_return_value_(c_signature, c_call_result);
346
347 auto merge = __ MakeLabel(MachineRepresentation::kTagged);
348 __ Goto(&if_success);
349
350 // We need to generate a fallback (both fast and slow call) in case
351 // the generated code might fail, in case e.g. a Smi was passed where
352 // a JSObject was expected and an error must be thrown
353 if (if_error.IsUsed()) {
354 // Generate direct slow call.
355 __ Bind(&if_error);
356 {
357 Node* slow_call_result = generate_slow_api_call_();
358 __ Goto(&merge, slow_call_result);
359 }
360 }
361
362 __ Bind(&if_success);
363 __ Goto(&merge, fast_call_result);
364
365 __ Bind(&merge);
366 return merge.PhiAt(0);
367}
368
369#undef __
370
372 GraphAssembler* graph_assembler,
373 FastApiCallFunction c_function, Node* data_argument,
374 const GetParameter& get_parameter,
375 const ConvertReturnValue& convert_return_value,
376 const InitializeOptions& initialize_options,
377 const GenerateSlowApiCall& generate_slow_api_call) {
378 FastApiCallBuilder builder(isolate, graph, graph_assembler, get_parameter,
379 convert_return_value, initialize_options,
380 generate_slow_api_call);
381 return builder.Build(c_function, data_argument);
382}
383
385 JSHeapBroker* broker, FunctionTemplateInfoRef function_template_info,
386 size_t arg_count) {
387 if (!v8_flags.turbo_fast_api_calls) return {0, nullptr};
388
389 static constexpr int kReceiver = 1;
390
391 const ZoneVector<const CFunctionInfo*>& signatures =
392 function_template_info.c_signatures(broker);
393 const size_t overloads_count = signatures.size();
394
395 // Only considers entries whose type list length matches arg_count.
396 for (size_t i = 0; i < overloads_count; i++) {
397 const CFunctionInfo* c_signature = signatures[i];
398 const size_t len = c_signature->ArgumentCount() - kReceiver;
399 bool optimize_to_fast_call =
400 (len == arg_count) &&
402
403 if (optimize_to_fast_call) {
404 // TODO(nicohartmann@): {Flags::kEnforceRangeBit} is currently only
405 // supported on 64 bit architectures. We should support this on 32 bit
406 // architectures.
407#if defined(V8_TARGET_ARCH_32_BIT)
408 for (unsigned int j = 0; j < c_signature->ArgumentCount(); ++j) {
409 const uint8_t flags =
410 static_cast<uint8_t>(c_signature->ArgumentInfo(j).GetFlags());
411 if (flags & static_cast<uint8_t>(CTypeInfo::Flags::kEnforceRangeBit)) {
412 // Bailout
413 return {0, nullptr};
414 }
415 }
416#endif
417 return {function_template_info.c_functions(broker)[i], c_signature};
418 }
419 }
420
421 return {0, nullptr};
422}
423
424} // namespace fast_api_call
425} // namespace compiler
426} // namespace internal
427} // namespace v8
const CTypeInfo & ReturnInfo() const
const CTypeInfo & ArgumentInfo(unsigned int index) const
Definition api.cc:11874
unsigned int ArgumentCount() const
static bool IsSupported(CpuFeature f)
static V8_EXPORT_PRIVATE ExternalReference isolate_address()
static ExternalReference Create(const SCTableReference &table_ref)
static constexpr int root_slot_offset(RootIndex root_index)
static constexpr int BuiltinSlotOffset(Builtin id)
static constexpr MachineType Pointer()
static constexpr MachineType AnyTagged()
static MachineType TypeForCType(const CTypeInfo &type)
static constexpr MachineRepresentation PointerRepresentation()
static constexpr MachineType IntPtr()
static V8_EXPORT_PRIVATE const Function * FunctionForId(FunctionId id)
Definition runtime.cc:350
T * AllocateArray(size_t length)
Definition zone.h:127
ZoneVector< const CFunctionInfo * > c_signatures(JSHeapBroker *broker) const
ZoneVector< Address > c_functions(JSHeapBroker *broker) const
static CallDescriptor * GetSimplifiedCDescriptor(Zone *zone, const MachineSignature *sig, CallDescriptor::Flags flags=CallDescriptor::kNoFlags, Operator::Properties properties=Operator::kNoThrow)
Definition c-linkage.cc:269
static CallDescriptor * GetRuntimeCallDescriptor(Zone *zone, Runtime::FunctionId function, int js_parameter_count, Operator::Properties properties, CallDescriptor::Flags flags, LazyDeoptOnThrow lazy_deopt_on_throw=LazyDeoptOnThrow::kNo)
Definition linkage.cc:426
Node * Build(FastApiCallFunction c_function, Node *data_argument)
FastApiCallBuilder(Isolate *isolate, TFGraph *graph, GraphAssembler *graph_assembler, const GetParameter &get_parameter, const ConvertReturnValue &convert_return_value, const InitializeOptions &initialize_options, const GenerateSlowApiCall &generate_slow_api_call)
Node * WrapFastCall(const CallDescriptor *call_descriptor, int inputs_size, Node **inputs, Node *target, const CFunctionInfo *c_signature, int c_arg_count, Node *stack_slot)
JSHeapBroker * broker
std::function< Node *(const CFunctionInfo *, Node *)> ConvertReturnValue
ElementsKind GetTypedArrayElementsKind(CTypeInfo::Type type)
std::function< Node *(int, GraphAssemblerLabel< 0 > *)> GetParameter
std::function< void(Node *)> InitializeOptions
std::function< Node *()> GenerateSlowApiCall
FastApiCallFunction GetFastApiCallTarget(JSHeapBroker *broker, FunctionTemplateInfoRef function_template_info, size_t arg_count)
Node * BuildFastApiCall(Isolate *isolate, TFGraph *graph, GraphAssembler *graph_assembler, FastApiCallFunction c_function, Node *data_argument, const GetParameter &get_parameter, const ConvertReturnValue &convert_return_value, const InitializeOptions &initialize_options, const GenerateSlowApiCall &generate_slow_api_call)
bool CanOptimizeFastSignature(const CFunctionInfo *c_signature)
static const Operator * IntPtrConstant(CommonOperatorBuilder *common, intptr_t value)
V8_EXPORT_PRIVATE FlagValues v8_flags
i::Address Load(i::Address address)
Definition unwinder.cc:19
#define DCHECK_NOT_NULL(val)
Definition logging.h:492
#define DCHECK_EQ(v1, v2)
Definition logging.h:485
#define USE(...)
Definition macros.h:293
#define ASSERT_TRIVIALLY_COPYABLE(T)
Definition macros.h:267
#define END_ALLOW_USE_DEPRECATED()
Definition v8config.h:634
#define START_ALLOW_USE_DEPRECATED()
Definition v8config.h:633