v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
fast-api-call-lowering-reducer.h
Go to the documentation of this file.
1// Copyright 2023 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
5#ifndef V8_COMPILER_TURBOSHAFT_FAST_API_CALL_LOWERING_REDUCER_H_
6#define V8_COMPILER_TURBOSHAFT_FAST_API_CALL_LOWERING_REDUCER_H_
7
17
19
21
22template <typename Next>
23class FastApiCallLoweringReducer : public Next {
24 public:
25 TURBOSHAFT_REDUCER_BOILERPLATE(FastApiCallLowering)
26
28 V<FrameState> frame_state, V<Object> data_argument, V<Context> context,
29 base::Vector<const OpIndex> arguments,
30 const FastApiCallParameters* parameters,
31 base::Vector<const RegisterRepresentation> out_reps) {
32 __ data() -> set_graph_has_lowered_fast_api_calls();
33
34 FastApiCallFunction c_function = parameters->c_function;
35 const auto& c_signature = parameters->c_signature();
36 const int c_arg_count = c_signature->ArgumentCount();
37 DCHECK_EQ(c_arg_count, arguments.size());
38
39 Label<> handle_error(this);
40 Label<Word32> done(this);
42 c_signature->ReturnInfo(), c_signature->GetInt64Representation()));
43
44 OpIndex callee = __ ExternalConstant(ExternalReference::Create(
46
48 for (int i = 0; i < c_arg_count; ++i) {
49 CTypeInfo type = c_signature->ArgumentInfo(i);
50 args.push_back(AdaptFastCallArgument(arguments[i], type, handle_error));
51 }
52
53 // While adapting the arguments, we might have noticed an inconsistency that
54 // lead to unconditionally jumping to {handle_error}. If this happens, then
55 // we don't emit the call.
56 if (V8_LIKELY(!__ generating_unreachable_operations())) {
58 __ graph_zone(), 1,
59 c_arg_count + (c_signature->HasOptions() ? 1 : 0));
60
61 builder.AddReturn(MachineType::TypeForCType(c_signature->ReturnInfo()));
62
63 for (int i = 0; i < c_arg_count; ++i) {
64 CTypeInfo type = c_signature->ArgumentInfo(i);
66 MachineType machine_type =
67 type.GetSequenceType() == CTypeInfo::SequenceType::kScalar
71 builder.AddParam(machine_type);
72 }
73
74 OpIndex stack_slot;
75 if (c_signature->HasOptions()) {
76 const int kAlign = alignof(v8::FastApiCallbackOptions);
77 const int kSize = sizeof(v8::FastApiCallbackOptions);
78 // If this check fails, you've probably added new fields to
79 // v8::FastApiCallbackOptions, which means you'll need to write code
80 // that initializes and reads from them too.
81 static_assert(kSize == sizeof(uintptr_t) * 2);
82 stack_slot = __ StackSlot(kSize, kAlign);
83
84 // isolate
85 __ StoreOffHeap(
86 stack_slot,
87 __ ExternalConstant(ExternalReference::isolate_address()),
89 offsetof(v8::FastApiCallbackOptions, isolate));
90 // data = data_argument
91 OpIndex data_argument_to_pass = __ AdaptLocalArgument(data_argument);
92 __ StoreOffHeap(stack_slot, data_argument_to_pass,
94 offsetof(v8::FastApiCallbackOptions, data));
95
96 args.push_back(stack_slot);
98 }
99
100 // Build the actual call.
101 const TSCallDescriptor* call_descriptor = TSCallDescriptor::Create(
105 OpIndex c_call_result = WrapFastCall(call_descriptor, callee, frame_state,
106 context, base::VectorOf(args));
107
108 Label<> trigger_exception(this);
109
110 V<Object> exception =
111 __ Load(__ ExternalConstant(ExternalReference::Create(
112 IsolateAddressId::kExceptionAddress, isolate_)),
114 GOTO_IF_NOT(LIKELY(__ TaggedEqual(
115 exception,
116 __ HeapConstant(isolate_->factory()->the_hole_value()))),
117 trigger_exception);
118
119 V<Any> fast_call_result = ConvertReturnValue(c_signature, c_call_result);
120 __ SetVariable(result, fast_call_result);
121
123 BIND(trigger_exception);
124 __ template CallRuntime<
126 isolate_, frame_state, __ NoContextConstant(), LazyDeoptOnThrow::kNo,
127 {});
128
129 __ Unreachable();
130 }
131
132 if (BIND(handle_error)) {
133 __ SetVariable(result, DefaultReturnValue(c_signature));
134 // We pass Tagged<Smi>(0) as the value here, although this should never be
135 // visible when calling code reacts to `kFailureValue` properly.
137 }
138
139 BIND(done, state);
140 return __ Tuple(state, __ GetVariable(result));
141 }
142
143 private:
144 template <typename T>
146 V<Word32> result_state = __ template Projection<1>(result);
147 GOTO_IF_NOT(__ Word32Equal(result_state, TryChangeOp::kSuccessValue),
148 otherwise);
149 return __ template Projection<0>(result);
150 }
151
153 Label<>& handle_error) {
155 switch (arg_type.GetSequenceType()) {
156 case CTypeInfo::SequenceType::kScalar: {
157 uint8_t flags = static_cast<uint8_t>(arg_type.GetFlags());
158 if (flags & static_cast<uint8_t>(CTypeInfo::Flags::kEnforceRangeBit)) {
159 switch (arg_type.GetType()) {
161 auto result = __ TryTruncateFloat64ToInt32(argument);
162 return Checked(result, handle_error);
163 }
165 auto result = __ TryTruncateFloat64ToUint32(argument);
166 return Checked(result, handle_error);
167 }
169 auto result = __ TryTruncateFloat64ToInt64(argument);
170 return Checked(result, handle_error);
171 }
173 auto result = __ TryTruncateFloat64ToUint64(argument);
174 return Checked(result, handle_error);
175 }
176 default: {
177 GOTO(handle_error);
178 return argument;
179 }
180 }
181 } else if (flags & static_cast<uint8_t>(CTypeInfo::Flags::kClampBit)) {
182 return ClampFastCallArgument(argument, arg_type.GetType());
183 } else {
184 switch (arg_type.GetType()) {
186 return __ AdaptLocalArgument(argument);
187 }
189 return __ TruncateFloat64ToFloat32(argument);
190 }
192 // Check that the value is a HeapObject.
193 GOTO_IF(__ ObjectIsSmi(argument), handle_error);
194 Label<WordPtr> done(this);
195
196 // Check if the value is null.
197 GOTO_IF(UNLIKELY(__ TaggedEqual(
198 argument, __ HeapConstant(factory_->null_value()))),
199 done, 0);
200
201 // Check that the value is a JSExternalObject.
203 __ TaggedEqual(__ LoadMapField(argument),
204 __ HeapConstant(factory_->external_map())),
205 handle_error);
206
207 GOTO(done, __ template LoadField<WordPtr>(
208 V<HeapObject>::Cast(argument),
210
211 BIND(done, result);
212 return result;
213 }
215 // Check that the value is a HeapObject.
216 GOTO_IF(__ ObjectIsSmi(argument), handle_error);
217 V<HeapObject> argument_obj = V<HeapObject>::Cast(argument);
218
219 V<Map> map = __ LoadMapField(argument_obj);
220 V<Word32> instance_type = __ LoadInstanceTypeField(map);
221
222 V<Word32> encoding = __ Word32BitwiseAnd(
224 GOTO_IF_NOT(__ Word32Equal(encoding, kSeqOneByteStringTag),
225 handle_error);
226
227 V<WordPtr> length_in_bytes = __ template LoadField<WordPtr>(
228 argument_obj, AccessBuilder::ForStringLength());
229 V<WordPtr> data_ptr = __ GetElementStartPointer(
231
232 constexpr int kAlign = alignof(FastOneByteString);
233 constexpr int kSize = sizeof(FastOneByteString);
234 static_assert(kSize == sizeof(uintptr_t) + sizeof(size_t),
235 "The size of "
236 "FastOneByteString isn't equal to the sum of its "
237 "expected members.");
238 OpIndex stack_slot = __ StackSlot(kSize, kAlign);
239 __ StoreOffHeap(stack_slot, data_ptr,
241 __ StoreOffHeap(stack_slot, length_in_bytes,
242 MemoryRepresentation::Uint32(), sizeof(size_t));
243 static_assert(sizeof(uintptr_t) == sizeof(size_t),
244 "The string length can't "
245 "fit the PointerRepresentation used to store it.");
246 return stack_slot;
247 }
248 default: {
249 return argument;
250 }
251 }
252 }
253 }
254 case CTypeInfo::SequenceType::kIsSequence: {
256
257 // Check that the value is a HeapObject.
258 GOTO_IF(__ ObjectIsSmi(argument), handle_error);
259
260 // Check that the value is a JSArray.
261 V<Map> map = __ LoadMapField(argument);
262 V<Word32> instance_type = __ LoadInstanceTypeField(map);
263 GOTO_IF_NOT(__ Word32Equal(instance_type, JS_ARRAY_TYPE), handle_error);
264
265 return __ AdaptLocalArgument(argument);
266 }
267 default: {
268 UNREACHABLE();
269 }
270 }
272 }
273
275 CTypeInfo::Type scalar_type) {
276 double min, max;
277 switch (scalar_type) {
279 min = std::numeric_limits<int32_t>::min();
280 max = std::numeric_limits<int32_t>::max();
281 break;
283 min = 0;
284 max = std::numeric_limits<uint32_t>::max();
285 break;
287 min = kMinSafeInteger;
288 max = kMaxSafeInteger;
289 break;
291 min = 0;
292 max = kMaxSafeInteger;
293 break;
294 default:
295 UNREACHABLE();
296 }
297
298 V<Float64> clamped =
299 __ Conditional(__ Float64LessThan(min, argument),
300 __ Conditional(__ Float64LessThan(argument, max),
301 argument, __ Float64Constant(max)),
302 __ Float64Constant(min));
303
304 Label<Float64> done(this);
305 V<Float64> rounded = __ Float64RoundTiesEven(clamped);
306 GOTO_IF(__ Float64IsNaN(rounded), done, 0.0);
307 GOTO(done, rounded);
308
309 BIND(done, rounded_result);
310 switch (scalar_type) {
312 return __ ReversibleFloat64ToInt32(rounded_result);
314 return __ ReversibleFloat64ToUint32(rounded_result);
316 return __ ReversibleFloat64ToInt64(rounded_result);
318 return __ ReversibleFloat64ToUint64(rounded_result);
319 default:
320 UNREACHABLE();
321 }
322 }
323
325 switch (c_signature->ReturnInfo().GetType()) {
327 return __ HeapConstant(factory_->undefined_value());
331 return __ Word32Constant(0);
335 c_signature->GetInt64Representation();
337 return __ Word64Constant(int64_t{0});
338 }
340 return __ Float64Constant(0);
341 }
343 return __ Float32Constant(0);
345 return __ Float64Constant(0);
347 return __ HeapConstant(factory_->undefined_value());
353 UNREACHABLE();
354 }
355 }
356
358 switch (c_signature->ReturnInfo().GetType()) {
360 return __ HeapConstant(factory_->undefined_value());
362 static_assert(sizeof(bool) == 1, "unsupported bool size");
363 return __ Word32BitwiseAnd(result, __ Word32Constant(0xFF));
368 return result;
371 c_signature->GetInt64Representation();
373 return result;
374 }
376 return __ ChangeInt64ToFloat64(result);
377 }
380 c_signature->GetInt64Representation();
382 return result;
383 }
385 return __ ChangeUint64ToFloat64(result);
386 }
387
395 UNREACHABLE();
396 }
397 }
398
400 Label<HeapObject> done(this);
401
402 // Check if the pointer is a null pointer.
403 GOTO_IF(__ WordPtrEqual(pointer, 0), done,
404 __ HeapConstant(factory_->null_value()));
405
407 __ Allocate(JSExternalObject::kHeaderSize, AllocationType::kYoung);
408 __ InitializeField(external, AccessBuilder::ForMap(),
409 __ HeapConstant(factory_->external_map()));
410 V<FixedArray> empty_fixed_array =
411 __ HeapConstant(factory_->empty_fixed_array());
412 __ InitializeField(external, AccessBuilder::ForJSObjectPropertiesOrHash(),
413 empty_fixed_array);
414 __ InitializeField(external, AccessBuilder::ForJSObjectElements(),
415 empty_fixed_array);
416
417#ifdef V8_ENABLE_SANDBOX
418 OpIndex isolate_ptr =
419 __ ExternalConstant(ExternalReference::isolate_address());
420 MachineSignature::Builder builder(__ graph_zone(), 1, 2);
424 OpIndex allocate_and_initialize_young_external_pointer_table_entry =
425 __ ExternalConstant(
427 allocate_and_initialize_young_external_pointer_table_entry());
428 auto call_descriptor =
431 allocate_and_initialize_young_external_pointer_table_entry,
432 {isolate_ptr, pointer},
435 __ InitializeField(
436 external, AccessBuilder::ForJSExternalObjectPointerHandle(), handle);
437#else
438 __ InitializeField(external, AccessBuilder::ForJSExternalObjectValue(),
439 pointer);
440#endif // V8_ENABLE_SANDBOX
441 GOTO(done, __ FinishInitialization(std::move(external)));
442
443 BIND(done, result);
444 return result;
445 }
446
447 OpIndex WrapFastCall(const TSCallDescriptor* descriptor, OpIndex callee,
448 V<FrameState> frame_state, V<Context> context,
449 base::Vector<const OpIndex> arguments) {
450 // CPU profiler support.
451 OpIndex target_address =
452 __ IsolateField(IsolateFieldId::kFastApiCallTarget);
453 __ StoreOffHeap(target_address, __ BitcastHeapObjectToWordPtr(callee),
455
456 OpIndex context_address = __ ExternalConstant(
457 ExternalReference::Create(IsolateAddressId::kContextAddress, isolate_));
458
459 __ StoreOffHeap(context_address, __ BitcastHeapObjectToWordPtr(context),
461
462 // Create the fast call.
463 OpIndex result = __ Call(callee, frame_state, arguments, descriptor);
464
465 // Reset the CPU profiler target address.
466 __ StoreOffHeap(target_address, __ IntPtrConstant(0),
468
469#if DEBUG
470 // Reset the context again after the call, to make sure nobody is using the
471 // leftover context in the isolate.
472 __ StoreOffHeap(context_address,
473 __ WordPtrConstant(Context::kInvalidContext),
475#endif
476
477 return result;
478 }
479
482};
483
485
486} // namespace v8::internal::compiler::turboshaft
487
488#endif // V8_COMPILER_TURBOSHAFT_FAST_API_CALL_LOWERING_REDUCER_H_
#define BIND(label)
#define REDUCE(operation)
#define GOTO(label,...)
#define UNLIKELY(...)
#define LIKELY(...)
#define GOTO_IF_NOT(cond, label,...)
#define GOTO_IF(cond, label,...)
union v8::internal::@341::BuiltinMetadata::KindSpecificData data
Int64Representation GetInt64Representation() const
const CTypeInfo & ReturnInfo() const
constexpr Type GetType() const
constexpr Flags GetFlags() const
constexpr SequenceType GetSequenceType() const
static const int kInvalidContext
Definition contexts.h:578
static V8_EXPORT_PRIVATE ExternalReference isolate_address()
static ExternalReference Create(const SCTableReference &table_ref)
v8::internal::Factory * factory()
Definition isolate.h:1527
static constexpr MachineType Pointer()
static constexpr MachineType AnyTagged()
static constexpr MachineType Uint32()
static MachineType TypeForCType(const CTypeInfo &type)
static FieldAccess ForMap(WriteBarrierKind write_barrier=kMapWriteBarrier)
static FieldAccess ForJSExternalObjectValue()
static ElementAccess ForSeqOneByteStringCharacter()
static FieldAccess ForJSObjectPropertiesOrHash()
static CallDescriptor * GetSimplifiedCDescriptor(Zone *zone, const MachineSignature *sig, CallDescriptor::Flags flags=CallDescriptor::kNoFlags, Operator::Properties properties=Operator::kNoThrow)
Definition c-linkage.cc:269
V< T > Checked(V< Tuple< T, Word32 > > result, Label<> &otherwise)
OpIndex AdaptFastCallArgument(OpIndex argument, CTypeInfo arg_type, Label<> &handle_error)
OpIndex WrapFastCall(const TSCallDescriptor *descriptor, OpIndex callee, V< FrameState > frame_state, V< Context > context, base::Vector< const OpIndex > arguments)
OpIndex REDUCE FastApiCall(V< FrameState > frame_state, V< Object > data_argument, V< Context > context, base::Vector< const OpIndex > arguments, const FastApiCallParameters *parameters, base::Vector< const RegisterRepresentation > out_reps)
V< Any > ConvertReturnValue(const CFunctionInfo *c_signature, OpIndex result)
OpIndex ClampFastCallArgument(V< Float64 > argument, CTypeInfo::Type scalar_type)
static constexpr MemoryRepresentation Uint32()
static constexpr MemoryRepresentation UintPtr()
static constexpr RegisterRepresentation FromCTypeInfo(CTypeInfo t, CFunctionInfo::Int64Representation int64_repr)
static V< T > Cast(V< U > index)
Definition index.h:632
#define TURBOSHAFT_REDUCER_BOILERPLATE(Name)
Definition assembler.h:823
base::Vector< const DirectHandle< Object > > args
Definition execution.cc:74
Isolate * isolate
Zone * graph_zone
ZoneVector< RpoNumber > & result
constexpr Vector< T > VectorOf(T *start, size_t size)
Definition vector.h:360
template const Signature< wasm::ValueType > bool
static const Operator * IntPtrConstant(CommonOperatorBuilder *common, intptr_t value)
V8_INLINE IndirectHandle< T > handle(Tagged< T > object, Isolate *isolate)
Definition handles-inl.h:72
constexpr double kMaxSafeInteger
Definition globals.h:1985
constexpr uint32_t kStringRepresentationAndEncodingMask
constexpr uint32_t kSeqOneByteStringTag
constexpr double kMinSafeInteger
Definition globals.h:1987
i::Address Load(i::Address address)
Definition unwinder.cc:19
#define CHECK_EQ(lhs, rhs)
#define DCHECK_EQ(v1, v2)
Definition logging.h:485
static const TSCallDescriptor * Create(const CallDescriptor *descriptor, CanThrow can_throw, LazyDeoptOnThrow lazy_deopt_on_throw, Zone *graph_zone, const JSWasmCallParameters *js_wasm_call_parameters=nullptr)
#define END_ALLOW_USE_DEPRECATED()
Definition v8config.h:634
#define V8_LIKELY(condition)
Definition v8config.h:661
#define START_ALLOW_USE_DEPRECATED()
Definition v8config.h:633