Home | History | Annotate | Download | only in builtins
      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 
     74 BailoutId Builtins::GetContinuationBailoutId(Name name) {
     75   DCHECK(Builtins::KindOf(name) == TFJ || Builtins::KindOf(name) == TFC);
     76   return BailoutId(BailoutId::kFirstBuiltinContinuationId + name);
     77 }
     78 
     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 
     86 void Builtins::TearDown() { initialized_ = false; }
     87 
     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 
    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 
    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 
    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 
    138 void Builtins::set_builtin(int index, HeapObject* builtin) {
    139   isolate_->heap()->set_builtin(index, builtin);
    140 }
    141 
    142 Code* Builtins::builtin(int index) { return isolate_->heap()->builtin(index); }
    143 
    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
    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
    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
    184 const char* Builtins::name(int index) {
    185   DCHECK(IsBuiltinId(index));
    186   return builtin_metadata[index].name;
    187 }
    188 
    189 // static
    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
    196 bool Builtins::IsBuiltin(const Code* code) {
    197   return Builtins::IsBuiltinId(code->builtin_index());
    198 }
    199 
    200 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
    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
    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
    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
    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
    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
    409 Builtins::Kind Builtins::KindOf(int index) {
    410   DCHECK(IsBuiltinId(index));
    411   return builtin_metadata[index].kind;
    412 }
    413 
    414 // static
    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
    433 bool Builtins::IsCpp(int index) { return Builtins::KindOf(index) == CPP; }
    434 
    435 // static
    436 bool Builtins::HasCppImplementation(int index) {
    437   Kind kind = Builtins::KindOf(index);
    438   return (kind == CPP || kind == API);
    439 }
    440 
    441 // static
    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