1 // Copyright 2012 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 #include "src/builtins/builtins.h"
6
7 #include "src/api-inl.h"
8 #include "src/assembler-inl.h"
9 #include "src/builtins/builtins-descriptors.h"
10 #include "src/callable.h"
11 #include "src/instruction-stream.h"
12 #include "src/isolate.h"
13 #include "src/macro-assembler.h"
14 #include "src/objects-inl.h"
15 #include "src/visitors.h"
16
17 namespace v8 {
18 namespace internal {
19
20 // Forward declarations for C++ builtins.
21 #define FORWARD_DECLARE(Name) \
22 Object* Builtin_##Name(int argc, Object** args, Isolate* isolate);
23 BUILTIN_LIST_C(FORWARD_DECLARE)
24 #undef FORWARD_DECLARE
25
26 namespace {
27
28 // TODO(jgruber): Pack in CallDescriptors::Key.
29 struct BuiltinMetadata {
30 const char* name;
31 Builtins::Kind kind;
32 union {
33 Address cpp_entry; // For CPP and API builtins.
34 int8_t parameter_count; // For TFJ builtins.
35 } kind_specific_data;
36 };
37
38 // clang-format off
39 #define DECL_CPP(Name, ...) { #Name, Builtins::CPP, \
40 { FUNCTION_ADDR(Builtin_##Name) }},
41 #define DECL_API(Name, ...) { #Name, Builtins::API, \
42 { FUNCTION_ADDR(Builtin_##Name) }},
43 #ifdef V8_TARGET_BIG_ENDIAN
44 #define DECL_TFJ(Name, Count, ...) { #Name, Builtins::TFJ, \
45 { static_cast<Address>(static_cast<uintptr_t>( \
46 Count) << (kBitsPerByte * (kPointerSize - 1))) }},
47 #else
48 #define DECL_TFJ(Name, Count, ...) { #Name, Builtins::TFJ, \
49 { static_cast<Address>(Count) }},
50 #endif
51 #define DECL_TFC(Name, ...) { #Name, Builtins::TFC, {} },
52 #define DECL_TFS(Name, ...) { #Name, Builtins::TFS, {} },
53 #define DECL_TFH(Name, ...) { #Name, Builtins::TFH, {} },
54 #define DECL_BCH(Name, ...) { #Name "Handler", Builtins::BCH, {} }, \
55 { #Name "WideHandler", Builtins::BCH, {} }, \
56 { #Name "ExtraWideHandler", Builtins::BCH, {} },
57 #define DECL_ASM(Name, ...) { #Name, Builtins::ASM, {} },
58 const BuiltinMetadata builtin_metadata[] = {
59 BUILTIN_LIST(DECL_CPP, DECL_API, DECL_TFJ, DECL_TFC, DECL_TFS, DECL_TFH,
60 DECL_BCH, DECL_ASM)
61 };
62 #undef DECL_CPP
63 #undef DECL_API
64 #undef DECL_TFJ
65 #undef DECL_TFC
66 #undef DECL_TFS
67 #undef DECL_TFH
68 #undef DECL_BCH
69 #undef DECL_ASM
70 // clang-format on
71
72 } // namespace
73
GetContinuationBailoutId(Name name)74 BailoutId Builtins::GetContinuationBailoutId(Name name) {
75 DCHECK(Builtins::KindOf(name) == TFJ || Builtins::KindOf(name) == TFC);
76 return BailoutId(BailoutId::kFirstBuiltinContinuationId + name);
77 }
78
GetBuiltinFromBailoutId(BailoutId id)79 Builtins::Name Builtins::GetBuiltinFromBailoutId(BailoutId id) {
80 int builtin_index = id.ToInt() - BailoutId::kFirstBuiltinContinuationId;
81 DCHECK(Builtins::KindOf(builtin_index) == TFJ ||
82 Builtins::KindOf(builtin_index) == TFC);
83 return static_cast<Name>(builtin_index);
84 }
85
TearDown()86 void Builtins::TearDown() { initialized_ = false; }
87
Lookup(Address pc)88 const char* Builtins::Lookup(Address pc) {
89 // Off-heap pc's can be looked up through binary search.
90 if (FLAG_embedded_builtins) {
91 Code* maybe_builtin = InstructionStream::TryLookupCode(isolate_, pc);
92 if (maybe_builtin != nullptr) return name(maybe_builtin->builtin_index());
93 }
94
95 // May be called during initialization (disassembler).
96 if (initialized_) {
97 for (int i = 0; i < builtin_count; i++) {
98 if (isolate_->heap()->builtin(i)->contains(pc)) return name(i);
99 }
100 }
101 return nullptr;
102 }
103
NewFunctionContext(ScopeType scope_type)104 Handle<Code> Builtins::NewFunctionContext(ScopeType scope_type) {
105 switch (scope_type) {
106 case ScopeType::EVAL_SCOPE:
107 return builtin_handle(kFastNewFunctionContextEval);
108 case ScopeType::FUNCTION_SCOPE:
109 return builtin_handle(kFastNewFunctionContextFunction);
110 default:
111 UNREACHABLE();
112 }
113 return Handle<Code>::null();
114 }
115
NonPrimitiveToPrimitive(ToPrimitiveHint hint)116 Handle<Code> Builtins::NonPrimitiveToPrimitive(ToPrimitiveHint hint) {
117 switch (hint) {
118 case ToPrimitiveHint::kDefault:
119 return builtin_handle(kNonPrimitiveToPrimitive_Default);
120 case ToPrimitiveHint::kNumber:
121 return builtin_handle(kNonPrimitiveToPrimitive_Number);
122 case ToPrimitiveHint::kString:
123 return builtin_handle(kNonPrimitiveToPrimitive_String);
124 }
125 UNREACHABLE();
126 }
127
OrdinaryToPrimitive(OrdinaryToPrimitiveHint hint)128 Handle<Code> Builtins::OrdinaryToPrimitive(OrdinaryToPrimitiveHint hint) {
129 switch (hint) {
130 case OrdinaryToPrimitiveHint::kNumber:
131 return builtin_handle(kOrdinaryToPrimitive_Number);
132 case OrdinaryToPrimitiveHint::kString:
133 return builtin_handle(kOrdinaryToPrimitive_String);
134 }
135 UNREACHABLE();
136 }
137
set_builtin(int index,HeapObject * builtin)138 void Builtins::set_builtin(int index, HeapObject* builtin) {
139 isolate_->heap()->set_builtin(index, builtin);
140 }
141
builtin(int index)142 Code* Builtins::builtin(int index) { return isolate_->heap()->builtin(index); }
143
builtin_handle(int index)144 Handle<Code> Builtins::builtin_handle(int index) {
145 DCHECK(IsBuiltinId(index));
146 return Handle<Code>(
147 reinterpret_cast<Code**>(isolate_->heap()->builtin_address(index)));
148 }
149
150 // static
GetStackParameterCount(Name name)151 int Builtins::GetStackParameterCount(Name name) {
152 DCHECK(Builtins::KindOf(name) == TFJ);
153 return builtin_metadata[name].kind_specific_data.parameter_count;
154 }
155
156 // static
CallableFor(Isolate * isolate,Name name)157 Callable Builtins::CallableFor(Isolate* isolate, Name name) {
158 Handle<Code> code = isolate->builtins()->builtin_handle(name);
159 CallDescriptors::Key key;
160 switch (name) {
161 // This macro is deliberately crafted so as to emit very little code,
162 // in order to keep binary size of this function under control.
163 #define CASE_OTHER(Name, ...) \
164 case k##Name: { \
165 key = Builtin_##Name##_InterfaceDescriptor::key(); \
166 break; \
167 }
168 BUILTIN_LIST(IGNORE_BUILTIN, IGNORE_BUILTIN, IGNORE_BUILTIN, CASE_OTHER,
169 CASE_OTHER, CASE_OTHER, IGNORE_BUILTIN, IGNORE_BUILTIN)
170 #undef CASE_OTHER
171 default:
172 Builtins::Kind kind = Builtins::KindOf(name);
173 DCHECK_NE(kind, BCH);
174 if (kind == TFJ || kind == CPP) {
175 return Callable(code, JSTrampolineDescriptor{});
176 }
177 UNREACHABLE();
178 }
179 CallInterfaceDescriptor descriptor(key);
180 return Callable(code, descriptor);
181 }
182
183 // static
name(int index)184 const char* Builtins::name(int index) {
185 DCHECK(IsBuiltinId(index));
186 return builtin_metadata[index].name;
187 }
188
189 // static
CppEntryOf(int index)190 Address Builtins::CppEntryOf(int index) {
191 DCHECK(Builtins::HasCppImplementation(index));
192 return builtin_metadata[index].kind_specific_data.cpp_entry;
193 }
194
195 // static
IsBuiltin(const Code * code)196 bool Builtins::IsBuiltin(const Code* code) {
197 return Builtins::IsBuiltinId(code->builtin_index());
198 }
199
IsBuiltinHandle(Handle<HeapObject> maybe_code,int * index) const200 bool Builtins::IsBuiltinHandle(Handle<HeapObject> maybe_code,
201 int* index) const {
202 Heap* heap = isolate_->heap();
203 Address handle_location = maybe_code.address();
204 Address start = heap->builtin_address(0);
205 Address end = heap->builtin_address(Builtins::builtin_count);
206 if (handle_location >= end) return false;
207 if (handle_location < start) return false;
208 *index = static_cast<int>(handle_location - start) >> kPointerSizeLog2;
209 DCHECK(Builtins::IsBuiltinId(*index));
210 return true;
211 }
212
213 // static
IsIsolateIndependentBuiltin(const Code * code)214 bool Builtins::IsIsolateIndependentBuiltin(const Code* code) {
215 if (FLAG_embedded_builtins) {
216 const int builtin_index = code->builtin_index();
217 return Builtins::IsBuiltinId(builtin_index) &&
218 Builtins::IsIsolateIndependent(builtin_index);
219 } else {
220 return false;
221 }
222 }
223
224 // static
IsLazy(int index)225 bool Builtins::IsLazy(int index) {
226 DCHECK(IsBuiltinId(index));
227
228 if (FLAG_embedded_builtins) {
229 // We don't want to lazy-deserialize off-heap builtins.
230 if (Builtins::IsIsolateIndependent(index)) return false;
231 }
232
233 // There are a couple of reasons that builtins can require eager-loading,
234 // i.e. deserialization at isolate creation instead of on-demand. For
235 // instance:
236 // * DeserializeLazy implements lazy loading.
237 // * Immovability requirement. This can only conveniently be guaranteed at
238 // isolate creation (at runtime, we'd have to allocate in LO space).
239 // * To avoid conflicts in SharedFunctionInfo::function_data (Illegal,
240 // HandleApiCall, interpreter entry trampolines).
241 // * Frequent use makes lazy loading unnecessary (CompileLazy).
242 // TODO(wasm): Remove wasm builtins once immovability is no longer required.
243 switch (index) {
244 case kAbort: // Required by wasm.
245 case kArrayEveryLoopEagerDeoptContinuation:
246 case kArrayEveryLoopLazyDeoptContinuation:
247 case kArrayFilterLoopEagerDeoptContinuation:
248 case kArrayFilterLoopLazyDeoptContinuation:
249 case kArrayFindIndexLoopAfterCallbackLazyDeoptContinuation:
250 case kArrayFindIndexLoopEagerDeoptContinuation:
251 case kArrayFindIndexLoopLazyDeoptContinuation:
252 case kArrayFindLoopAfterCallbackLazyDeoptContinuation:
253 case kArrayFindLoopEagerDeoptContinuation:
254 case kArrayFindLoopLazyDeoptContinuation:
255 case kArrayForEachLoopEagerDeoptContinuation:
256 case kArrayForEachLoopLazyDeoptContinuation:
257 case kArrayMapLoopEagerDeoptContinuation:
258 case kArrayMapLoopLazyDeoptContinuation:
259 case kArrayReduceLoopEagerDeoptContinuation:
260 case kArrayReduceLoopLazyDeoptContinuation:
261 case kArrayReducePreLoopEagerDeoptContinuation:
262 case kArrayReduceRightLoopEagerDeoptContinuation:
263 case kArrayReduceRightLoopLazyDeoptContinuation:
264 case kArrayReduceRightPreLoopEagerDeoptContinuation:
265 case kArraySomeLoopEagerDeoptContinuation:
266 case kArraySomeLoopLazyDeoptContinuation:
267 case kAsyncGeneratorAwaitCaught: // https://crbug.com/v8/6786.
268 case kAsyncGeneratorAwaitUncaught: // https://crbug.com/v8/6786.
269 // CEntry variants must be immovable, whereas lazy deserialization allocates
270 // movable code.
271 case kCEntry_Return1_DontSaveFPRegs_ArgvOnStack_NoBuiltinExit:
272 case kCEntry_Return1_DontSaveFPRegs_ArgvOnStack_BuiltinExit:
273 case kCEntry_Return1_DontSaveFPRegs_ArgvInRegister_NoBuiltinExit:
274 case kCEntry_Return1_SaveFPRegs_ArgvOnStack_NoBuiltinExit:
275 case kCEntry_Return1_SaveFPRegs_ArgvOnStack_BuiltinExit:
276 case kCEntry_Return2_DontSaveFPRegs_ArgvOnStack_NoBuiltinExit:
277 case kCEntry_Return2_DontSaveFPRegs_ArgvOnStack_BuiltinExit:
278 case kCEntry_Return2_DontSaveFPRegs_ArgvInRegister_NoBuiltinExit:
279 case kCEntry_Return2_SaveFPRegs_ArgvOnStack_NoBuiltinExit:
280 case kCEntry_Return2_SaveFPRegs_ArgvOnStack_BuiltinExit:
281 case kCompileLazy:
282 case kDebugBreakTrampoline:
283 case kDeserializeLazy:
284 case kFunctionPrototypeHasInstance: // https://crbug.com/v8/6786.
285 case kHandleApiCall:
286 case kIllegal:
287 case kInstantiateAsmJs:
288 case kInterpreterEnterBytecodeAdvance:
289 case kInterpreterEnterBytecodeDispatch:
290 case kInterpreterEntryTrampoline:
291 case kPromiseConstructorLazyDeoptContinuation:
292 case kRecordWrite: // https://crbug.com/chromium/765301.
293 case kThrowWasmTrapDivByZero: // Required by wasm.
294 case kThrowWasmTrapDivUnrepresentable: // Required by wasm.
295 case kThrowWasmTrapFloatUnrepresentable: // Required by wasm.
296 case kThrowWasmTrapFuncInvalid: // Required by wasm.
297 case kThrowWasmTrapFuncSigMismatch: // Required by wasm.
298 case kThrowWasmTrapMemOutOfBounds: // Required by wasm.
299 case kThrowWasmTrapRemByZero: // Required by wasm.
300 case kThrowWasmTrapUnreachable: // Required by wasm.
301 case kToBooleanLazyDeoptContinuation:
302 case kToNumber: // Required by wasm.
303 case kGenericConstructorLazyDeoptContinuation:
304 case kWasmCompileLazy: // Required by wasm.
305 case kWasmStackGuard: // Required by wasm.
306 return false;
307 default:
308 // TODO(6624): Extend to other kinds.
309 return KindOf(index) == TFJ;
310 }
311 UNREACHABLE();
312 }
313
314 // static
IsIsolateIndependent(int index)315 bool Builtins::IsIsolateIndependent(int index) {
316 DCHECK(IsBuiltinId(index));
317 #ifndef V8_TARGET_ARCH_IA32
318 switch (index) {
319 // Bytecode handlers do not yet support being embedded.
320 #ifdef V8_EMBEDDED_BYTECODE_HANDLERS
321 #define BYTECODE_BUILTIN(Name, ...) \
322 case k##Name##Handler: \
323 case k##Name##WideHandler: \
324 case k##Name##ExtraWideHandler: \
325 return false;
326 BUILTIN_LIST_BYTECODE_HANDLERS(BYTECODE_BUILTIN)
327 #undef BYTECODE_BUILTIN
328 #endif // V8_EMBEDDED_BYTECODE_HANDLERS
329
330 // TODO(jgruber): There's currently two blockers for moving
331 // InterpreterEntryTrampoline into the binary:
332 // 1. InterpreterEnterBytecode calculates a pointer into the middle of
333 // InterpreterEntryTrampoline (see interpreter_entry_return_pc_offset).
334 // When the builtin is embedded, the pointer would need to be calculated
335 // at an offset from the embedded instruction stream (instead of the
336 // trampoline code object).
337 // 2. We create distinct copies of the trampoline to make it possible to
338 // attribute ticks in the interpreter to individual JS functions.
339 // See https://crrev.com/c/959081 and InstallBytecodeArray. When the
340 // trampoline is embedded, we need to ensure that CopyCode creates a copy
341 // of the builtin itself (and not just the trampoline).
342 case kInterpreterEntryTrampoline:
343 return false;
344 default:
345 return true;
346 }
347 #else // V8_TARGET_ARCH_IA32
348 // TODO(jgruber, v8:6666): Implement support.
349 // ia32 is a work-in-progress. This will let us make builtins
350 // isolate-independent one-by-one.
351 switch (index) {
352 case kContinueToCodeStubBuiltin:
353 case kContinueToCodeStubBuiltinWithResult:
354 case kContinueToJavaScriptBuiltin:
355 case kContinueToJavaScriptBuiltinWithResult:
356 case kWasmAllocateHeapNumber:
357 case kWasmCallJavaScript:
358 case kWasmToNumber:
359 case kDoubleToI:
360 return true;
361 default:
362 return false;
363 }
364 #endif // V8_TARGET_ARCH_IA32
365 UNREACHABLE();
366 }
367
368 // static
IsWasmRuntimeStub(int index)369 bool Builtins::IsWasmRuntimeStub(int index) {
370 DCHECK(IsBuiltinId(index));
371 switch (index) {
372 #define CASE_TRAP(Name) case kThrowWasm##Name:
373 #define CASE(Name) case k##Name:
374 WASM_RUNTIME_STUB_LIST(CASE, CASE_TRAP)
375 #undef CASE_TRAP
376 #undef CASE
377 return true;
378 default:
379 return false;
380 }
381 UNREACHABLE();
382 }
383
384 // static
GenerateOffHeapTrampolineFor(Isolate * isolate,Address off_heap_entry)385 Handle<Code> Builtins::GenerateOffHeapTrampolineFor(Isolate* isolate,
386 Address off_heap_entry) {
387 DCHECK(isolate->serializer_enabled());
388 DCHECK_NOT_NULL(isolate->embedded_blob());
389 DCHECK_NE(0, isolate->embedded_blob_size());
390
391 constexpr size_t buffer_size = 256; // Enough to fit the single jmp.
392 byte buffer[buffer_size]; // NOLINT(runtime/arrays)
393
394 // Generate replacement code that simply tail-calls the off-heap code.
395 MacroAssembler masm(isolate, buffer, buffer_size, CodeObjectRequired::kYes);
396 DCHECK(!masm.has_frame());
397 {
398 FrameScope scope(&masm, StackFrame::NONE);
399 masm.JumpToInstructionStream(off_heap_entry);
400 }
401
402 CodeDesc desc;
403 masm.GetCode(isolate, &desc);
404
405 return isolate->factory()->NewCode(desc, Code::BUILTIN, masm.CodeObject());
406 }
407
408 // static
KindOf(int index)409 Builtins::Kind Builtins::KindOf(int index) {
410 DCHECK(IsBuiltinId(index));
411 return builtin_metadata[index].kind;
412 }
413
414 // static
KindNameOf(int index)415 const char* Builtins::KindNameOf(int index) {
416 Kind kind = Builtins::KindOf(index);
417 // clang-format off
418 switch (kind) {
419 case CPP: return "CPP";
420 case API: return "API";
421 case TFJ: return "TFJ";
422 case TFC: return "TFC";
423 case TFS: return "TFS";
424 case TFH: return "TFH";
425 case BCH: return "BCH";
426 case ASM: return "ASM";
427 }
428 // clang-format on
429 UNREACHABLE();
430 }
431
432 // static
IsCpp(int index)433 bool Builtins::IsCpp(int index) { return Builtins::KindOf(index) == CPP; }
434
435 // static
HasCppImplementation(int index)436 bool Builtins::HasCppImplementation(int index) {
437 Kind kind = Builtins::KindOf(index);
438 return (kind == CPP || kind == API);
439 }
440
441 // static
AllowDynamicFunction(Isolate * isolate,Handle<JSFunction> target,Handle<JSObject> target_global_proxy)442 bool Builtins::AllowDynamicFunction(Isolate* isolate, Handle<JSFunction> target,
443 Handle<JSObject> target_global_proxy) {
444 if (FLAG_allow_unsafe_function_constructor) return true;
445 HandleScopeImplementer* impl = isolate->handle_scope_implementer();
446 Handle<Context> responsible_context =
447 impl->MicrotaskContextIsLastEnteredContext() ? impl->MicrotaskContext()
448 : impl->LastEnteredContext();
449 // TODO(jochen): Remove this.
450 if (responsible_context.is_null()) {
451 return true;
452 }
453 if (*responsible_context == target->context()) return true;
454 return isolate->MayAccess(responsible_context, target_global_proxy);
455 }
456
457 } // namespace internal
458 } // namespace v8
459