• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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