1// Copyright 2011 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_BUILTINS_BUILTINS_H_ 6#define V8_BUILTINS_BUILTINS_H_ 7 8#include "src/base/flags.h" 9#include "src/builtins/builtins-definitions.h" 10#include "src/common/globals.h" 11 12namespace v8 { 13namespace internal { 14 15class ByteArray; 16class CallInterfaceDescriptor; 17class Callable; 18template <typename T> 19class Handle; 20class Isolate; 21 22// Forward declarations. 23class BytecodeOffset; 24class RootVisitor; 25enum class InterpreterPushArgsMode : unsigned; 26namespace compiler { 27class CodeAssemblerState; 28} // namespace compiler 29 30template <typename T> 31static constexpr T FirstFromVarArgs(T x, ...) noexcept { 32 return x; 33} 34 35// Convenience macro to avoid generating named accessors for all builtins. 36#define BUILTIN_CODE(isolate, name) \ 37 (isolate)->builtins()->code_handle(i::Builtin::k##name) 38 39enum class Builtin : int32_t { 40 kNoBuiltinId = -1, 41#define DEF_ENUM(Name, ...) k##Name, 42 BUILTIN_LIST(DEF_ENUM, DEF_ENUM, DEF_ENUM, DEF_ENUM, DEF_ENUM, DEF_ENUM, 43 DEF_ENUM) 44#undef DEF_ENUM 45#define EXTRACT_NAME(Name, ...) k##Name, 46 // Define kFirstBytecodeHandler, 47 kFirstBytecodeHandler = 48 FirstFromVarArgs(BUILTIN_LIST_BYTECODE_HANDLERS(EXTRACT_NAME) 0) 49#undef EXTRACT_NAME 50}; 51 52V8_INLINE constexpr bool operator<(Builtin a, Builtin b) { 53 using type = typename std::underlying_type<Builtin>::type; 54 return static_cast<type>(a) < static_cast<type>(b); 55} 56 57V8_INLINE Builtin operator++(Builtin& builtin) { 58 using type = typename std::underlying_type<Builtin>::type; 59 return builtin = static_cast<Builtin>(static_cast<type>(builtin) + 1); 60} 61 62class Builtins { 63 public: 64 explicit Builtins(Isolate* isolate) : isolate_(isolate) {} 65 66 Builtins(const Builtins&) = delete; 67 Builtins& operator=(const Builtins&) = delete; 68 69 void TearDown(); 70 71 // Disassembler support. 72 const char* Lookup(Address pc); 73 74#define ADD_ONE(Name, ...) +1 75 static constexpr int kBuiltinCount = 0 BUILTIN_LIST( 76 ADD_ONE, ADD_ONE, ADD_ONE, ADD_ONE, ADD_ONE, ADD_ONE, ADD_ONE); 77 static constexpr int kBuiltinTier0Count = 0 BUILTIN_LIST_TIER0( 78 ADD_ONE, ADD_ONE, ADD_ONE, ADD_ONE, ADD_ONE, ADD_ONE, ADD_ONE); 79#undef ADD_ONE 80 81 static constexpr Builtin kFirst = static_cast<Builtin>(0); 82 static constexpr Builtin kLast = static_cast<Builtin>(kBuiltinCount - 1); 83 static constexpr Builtin kLastTier0 = 84 static_cast<Builtin>(kBuiltinTier0Count - 1); 85 86 static constexpr int kFirstWideBytecodeHandler = 87 static_cast<int>(Builtin::kFirstBytecodeHandler) + 88 kNumberOfBytecodeHandlers; 89 static constexpr int kFirstExtraWideBytecodeHandler = 90 kFirstWideBytecodeHandler + kNumberOfWideBytecodeHandlers; 91 static constexpr int kLastBytecodeHandlerPlusOne = 92 kFirstExtraWideBytecodeHandler + kNumberOfWideBytecodeHandlers; 93 STATIC_ASSERT(kLastBytecodeHandlerPlusOne == kBuiltinCount); 94 95 static constexpr bool IsBuiltinId(Builtin builtin) { 96 return builtin != Builtin::kNoBuiltinId; 97 } 98 static constexpr bool IsBuiltinId(int maybe_id) { 99 STATIC_ASSERT(static_cast<int>(Builtin::kNoBuiltinId) == -1); 100 return static_cast<uint32_t>(maybe_id) < 101 static_cast<uint32_t>(kBuiltinCount); 102 } 103 static constexpr bool IsTier0(Builtin builtin) { 104 return builtin <= kLastTier0 && IsBuiltinId(builtin); 105 } 106 107 static constexpr Builtin FromInt(int id) { 108 DCHECK(IsBuiltinId(id)); 109 return static_cast<Builtin>(id); 110 } 111 static constexpr int ToInt(Builtin id) { 112 DCHECK(IsBuiltinId(id)); 113 return static_cast<int>(id); 114 } 115 116 // The different builtin kinds are documented in builtins-definitions.h. 117 enum Kind { CPP, TFJ, TFC, TFS, TFH, BCH, ASM }; 118 119 static BytecodeOffset GetContinuationBytecodeOffset(Builtin builtin); 120 static Builtin GetBuiltinFromBytecodeOffset(BytecodeOffset); 121 122 static constexpr Builtin GetRecordWriteStub( 123 RememberedSetAction remembered_set_action, SaveFPRegsMode fp_mode) { 124 switch (remembered_set_action) { 125 case RememberedSetAction::kEmit: 126 switch (fp_mode) { 127 case SaveFPRegsMode::kIgnore: 128 return Builtin::kRecordWriteEmitRememberedSetIgnoreFP; 129 case SaveFPRegsMode::kSave: 130 return Builtin::kRecordWriteEmitRememberedSetSaveFP; 131 } 132 case RememberedSetAction::kOmit: 133 switch (fp_mode) { 134 case SaveFPRegsMode::kIgnore: 135 return Builtin::kRecordWriteOmitRememberedSetIgnoreFP; 136 case SaveFPRegsMode::kSave: 137 return Builtin::kRecordWriteOmitRememberedSetSaveFP; 138 } 139 } 140 } 141 142 static constexpr Builtin GetEphemeronKeyBarrierStub(SaveFPRegsMode fp_mode) { 143 switch (fp_mode) { 144 case SaveFPRegsMode::kIgnore: 145 return Builtin::kEphemeronKeyBarrierIgnoreFP; 146 case SaveFPRegsMode::kSave: 147 return Builtin::kEphemeronKeyBarrierSaveFP; 148 } 149 } 150 151 // Convenience wrappers. 152 Handle<CodeT> CallFunction(ConvertReceiverMode = ConvertReceiverMode::kAny); 153 Handle<CodeT> Call(ConvertReceiverMode = ConvertReceiverMode::kAny); 154 Handle<CodeT> NonPrimitiveToPrimitive( 155 ToPrimitiveHint hint = ToPrimitiveHint::kDefault); 156 Handle<CodeT> OrdinaryToPrimitive(OrdinaryToPrimitiveHint hint); 157 Handle<CodeT> JSConstructStubGeneric(); 158 159 // Used by CreateOffHeapTrampolines in isolate.cc. 160 void set_code(Builtin builtin, CodeT code); 161 162 V8_EXPORT_PRIVATE CodeT code(Builtin builtin); 163 V8_EXPORT_PRIVATE Handle<CodeT> code_handle(Builtin builtin); 164 165 static CallInterfaceDescriptor CallInterfaceDescriptorFor(Builtin builtin); 166 V8_EXPORT_PRIVATE static Callable CallableFor(Isolate* isolate, 167 Builtin builtin); 168 static bool HasJSLinkage(Builtin builtin); 169 170 static int GetStackParameterCount(Builtin builtin); 171 172 static const char* name(Builtin builtin); 173 174 // Support for --print-builtin-size and --print-builtin-code. 175 void PrintBuiltinCode(); 176 void PrintBuiltinSize(); 177 178 // Returns the C++ entry point for builtins implemented in C++, and the null 179 // Address otherwise. 180 static Address CppEntryOf(Builtin builtin); 181 182 static Kind KindOf(Builtin builtin); 183 static const char* KindNameOf(Builtin builtin); 184 185 static bool IsCpp(Builtin builtin); 186 187 // True, iff the given code object is a builtin. Note that this does not 188 // necessarily mean that its kind is Code::BUILTIN. 189 static bool IsBuiltin(const Code code); 190 191 // As above, but safe to access off the main thread since the check is done 192 // by handle location. Similar to Heap::IsRootHandle. 193 bool IsBuiltinHandle(Handle<HeapObject> maybe_code, Builtin* index) const; 194 195 // True, iff the given code object is a builtin with off-heap embedded code. 196 static bool IsIsolateIndependentBuiltin(const Code code); 197 198 // True, iff the given builtin contains no isolate-specific code and can be 199 // embedded into the binary. 200 static constexpr bool kAllBuiltinsAreIsolateIndependent = true; 201 static constexpr bool AllBuiltinsAreIsolateIndependent() { 202 return kAllBuiltinsAreIsolateIndependent; 203 } 204 static constexpr bool IsIsolateIndependent(Builtin builtin) { 205 STATIC_ASSERT(kAllBuiltinsAreIsolateIndependent); 206 return kAllBuiltinsAreIsolateIndependent; 207 } 208 209 static void InitializeIsolateDataTables(Isolate* isolate); 210 211 // Emits a CodeCreateEvent for every builtin. 212 static void EmitCodeCreateEvents(Isolate* isolate); 213 214 bool is_initialized() const { return initialized_; } 215 216 // Used by SetupIsolateDelegate and Deserializer. 217 void MarkInitialized() { 218 DCHECK(!initialized_); 219 initialized_ = true; 220 } 221 222 V8_WARN_UNUSED_RESULT static MaybeHandle<Object> InvokeApiFunction( 223 Isolate* isolate, bool is_construct, Handle<HeapObject> function, 224 Handle<Object> receiver, int argc, Handle<Object> args[], 225 Handle<HeapObject> new_target); 226 227 static void Generate_Adaptor(MacroAssembler* masm, Address builtin_address); 228 229 static void Generate_CEntry(MacroAssembler* masm, int result_size, 230 SaveFPRegsMode save_doubles, ArgvMode argv_mode, 231 bool builtin_exit_frame); 232 233 static bool AllowDynamicFunction(Isolate* isolate, Handle<JSFunction> target, 234 Handle<JSObject> target_global_proxy); 235 236 // Creates a trampoline code object that jumps to the given off-heap entry. 237 // The result should not be used directly, but only from the related Factory 238 // function. 239 // TODO(delphick): Come up with a better name since it may not generate an 240 // executable trampoline. 241 static Handle<Code> GenerateOffHeapTrampolineFor( 242 Isolate* isolate, Address off_heap_entry, int32_t kind_specific_flags, 243 bool generate_jump_to_instruction_stream); 244 245 // Generate the RelocInfo ByteArray that would be generated for an offheap 246 // trampoline. 247 static Handle<ByteArray> GenerateOffHeapTrampolineRelocInfo(Isolate* isolate); 248 249 // Only builtins with JS linkage should ever need to be called via their 250 // trampoline Code object. The remaining builtins have non-executable Code 251 // objects. 252 static bool CodeObjectIsExecutable(Builtin builtin); 253 254 static bool IsJSEntryVariant(Builtin builtin) { 255 switch (builtin) { 256 case Builtin::kJSEntry: 257 case Builtin::kJSConstructEntry: 258 case Builtin::kJSRunMicrotasksEntry: 259 return true; 260 default: 261 return false; 262 } 263 UNREACHABLE(); 264 } 265 266 int js_entry_handler_offset() const { 267 DCHECK_NE(js_entry_handler_offset_, 0); 268 return js_entry_handler_offset_; 269 } 270 271 void SetJSEntryHandlerOffset(int offset) { 272 // Check the stored offset is either uninitialized or unchanged (we 273 // generate multiple variants of this builtin but they should all have the 274 // same handler offset). 275 CHECK(js_entry_handler_offset_ == 0 || js_entry_handler_offset_ == offset); 276 js_entry_handler_offset_ = offset; 277 } 278 279 // Returns given builtin's slot in the main builtin table. 280 FullObjectSlot builtin_slot(Builtin builtin); 281 // Returns given builtin's slot in the tier0 builtin table. 282 FullObjectSlot builtin_tier0_slot(Builtin builtin); 283 284 private: 285 static void Generate_CallFunction(MacroAssembler* masm, 286 ConvertReceiverMode mode); 287 288 static void Generate_CallBoundFunctionImpl(MacroAssembler* masm); 289 290 static void Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode); 291 292 enum class CallOrConstructMode { kCall, kConstruct }; 293 static void Generate_CallOrConstructVarargs(MacroAssembler* masm, 294 Handle<CodeT> code); 295 static void Generate_CallOrConstructForwardVarargs(MacroAssembler* masm, 296 CallOrConstructMode mode, 297 Handle<CodeT> code); 298 299 static void Generate_InterpreterPushArgsThenCallImpl( 300 MacroAssembler* masm, ConvertReceiverMode receiver_mode, 301 InterpreterPushArgsMode mode); 302 303 static void Generate_InterpreterPushArgsThenConstructImpl( 304 MacroAssembler* masm, InterpreterPushArgsMode mode); 305 306#define DECLARE_ASM(Name, ...) \ 307 static void Generate_##Name(MacroAssembler* masm); 308#define DECLARE_TF(Name, ...) \ 309 static void Generate_##Name(compiler::CodeAssemblerState* state); 310 311 BUILTIN_LIST(IGNORE_BUILTIN, DECLARE_TF, DECLARE_TF, DECLARE_TF, DECLARE_TF, 312 IGNORE_BUILTIN, DECLARE_ASM) 313 314#undef DECLARE_ASM 315#undef DECLARE_TF 316 317 Isolate* isolate_; 318 bool initialized_ = false; 319 320 // Stores the offset of exception handler entry point (the handler_entry 321 // label) in JSEntry and its variants. It's used to generate the handler table 322 // during codegen (mksnapshot-only). 323 int js_entry_handler_offset_ = 0; 324 325 friend class SetupIsolateDelegate; 326}; 327 328V8_INLINE constexpr bool IsInterpreterTrampolineBuiltin(Builtin builtin_id) { 329 // Check for kNoBuiltinId first to abort early when the current Code object 330 // is not a builtin. 331 return builtin_id != Builtin::kNoBuiltinId && 332 (builtin_id == Builtin::kInterpreterEntryTrampoline || 333 builtin_id == Builtin::kInterpreterEnterAtBytecode || 334 builtin_id == Builtin::kInterpreterEnterAtNextBytecode); 335} 336 337V8_INLINE constexpr bool IsBaselineTrampolineBuiltin(Builtin builtin_id) { 338 // Check for kNoBuiltinId first to abort early when the current Code object 339 // is not a builtin. 340 return builtin_id != Builtin::kNoBuiltinId && 341 (builtin_id == Builtin::kBaselineOutOfLinePrologue || 342 builtin_id == Builtin::kBaselineOrInterpreterEnterAtBytecode || 343 builtin_id == Builtin::kBaselineOrInterpreterEnterAtNextBytecode); 344} 345 346Builtin ExampleBuiltinForTorqueFunctionPointerType( 347 size_t function_pointer_type_id); 348 349} // namespace internal 350} // namespace v8 351 352#endif // V8_BUILTINS_BUILTINS_H_ 353