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