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
12 namespace v8 {
13 namespace internal {
14
15 class ByteArray;
16 class CallInterfaceDescriptor;
17 class Callable;
18 template <typename T>
19 class Handle;
20 class Isolate;
21
22 // Forward declarations.
23 class BytecodeOffset;
24 class RootVisitor;
25 enum class InterpreterPushArgsMode : unsigned;
26 namespace compiler {
27 class CodeAssemblerState;
28 } // namespace compiler
29
30 template <typename T>
FirstFromVarArgs(T x,...)31 static 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
39 enum 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
52 V8_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
57 V8_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
62 class Builtins {
63 public:
Builtins(Isolate * isolate)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
IsBuiltinId(Builtin builtin)95 static constexpr bool IsBuiltinId(Builtin builtin) {
96 return builtin != Builtin::kNoBuiltinId;
97 }
IsBuiltinId(int maybe_id)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 }
IsTier0(Builtin builtin)103 static constexpr bool IsTier0(Builtin builtin) {
104 return builtin <= kLastTier0 && IsBuiltinId(builtin);
105 }
106
FromInt(int id)107 static constexpr Builtin FromInt(int id) {
108 DCHECK(IsBuiltinId(id));
109 return static_cast<Builtin>(id);
110 }
ToInt(Builtin id)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
GetRecordWriteStub(RememberedSetAction remembered_set_action,SaveFPRegsMode fp_mode)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
GetEphemeronKeyBarrierStub(SaveFPRegsMode fp_mode)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;
AllBuiltinsAreIsolateIndependent()201 static constexpr bool AllBuiltinsAreIsolateIndependent() {
202 return kAllBuiltinsAreIsolateIndependent;
203 }
IsIsolateIndependent(Builtin builtin)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
is_initialized()214 bool is_initialized() const { return initialized_; }
215
216 // Used by SetupIsolateDelegate and Deserializer.
MarkInitialized()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
IsJSEntryVariant(Builtin builtin)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
js_entry_handler_offset()266 int js_entry_handler_offset() const {
267 DCHECK_NE(js_entry_handler_offset_, 0);
268 return js_entry_handler_offset_;
269 }
270
SetJSEntryHandlerOffset(int offset)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
328 V8_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
337 V8_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
346 Builtin ExampleBuiltinForTorqueFunctionPointerType(
347 size_t function_pointer_type_id);
348
349 } // namespace internal
350 } // namespace v8
351
352 #endif // V8_BUILTINS_BUILTINS_H_
353