Line data Source code
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/code-tracer.h"
12 : #include "src/isolate.h"
13 : #include "src/macro-assembler.h"
14 : #include "src/objects-inl.h"
15 : #include "src/objects/fixed-array.h"
16 : #include "src/ostreams.h"
17 : #include "src/snapshot/embedded-data.h"
18 : #include "src/visitors.h"
19 :
20 : namespace v8 {
21 : namespace internal {
22 :
23 : // Forward declarations for C++ builtins.
24 : #define FORWARD_DECLARE(Name) \
25 : Address Builtin_##Name(int argc, Address* args, Isolate* isolate);
26 : BUILTIN_LIST_C(FORWARD_DECLARE)
27 : #undef FORWARD_DECLARE
28 :
29 : namespace {
30 :
31 : // TODO(jgruber): Pack in CallDescriptors::Key.
32 : struct BuiltinMetadata {
33 : const char* name;
34 : Builtins::Kind kind;
35 : // For CPP and API builtins it's cpp_entry address and for TFJ it's a
36 : // parameter count.
37 : Address cpp_entry_or_parameter_count;
38 : };
39 :
40 : #define DECL_CPP(Name, ...) \
41 : {#Name, Builtins::CPP, FUNCTION_ADDR(Builtin_##Name)},
42 : #define DECL_API(Name, ...) \
43 : {#Name, Builtins::API, FUNCTION_ADDR(Builtin_##Name)},
44 : #define DECL_TFJ(Name, Count, ...) \
45 : {#Name, Builtins::TFJ, static_cast<Address>(Count)},
46 : #define DECL_TFC(Name, ...) {#Name, Builtins::TFC, kNullAddress},
47 : #define DECL_TFS(Name, ...) {#Name, Builtins::TFS, kNullAddress},
48 : #define DECL_TFH(Name, ...) {#Name, Builtins::TFH, kNullAddress},
49 : #define DECL_BCH(Name, ...) {#Name, Builtins::BCH, kNullAddress},
50 : #define DECL_ASM(Name, ...) {#Name, Builtins::ASM, kNullAddress},
51 : const BuiltinMetadata builtin_metadata[] = {
52 : BUILTIN_LIST(DECL_CPP, DECL_API, DECL_TFJ, DECL_TFC, DECL_TFS, DECL_TFH,
53 : DECL_BCH, DECL_ASM)
54 : };
55 : #undef DECL_CPP
56 : #undef DECL_API
57 : #undef DECL_TFJ
58 : #undef DECL_TFC
59 : #undef DECL_TFS
60 : #undef DECL_TFH
61 : #undef DECL_BCH
62 : #undef DECL_ASM
63 :
64 : } // namespace
65 :
66 9580 : BailoutId Builtins::GetContinuationBailoutId(Name name) {
67 : DCHECK(Builtins::KindOf(name) == TFJ || Builtins::KindOf(name) == TFC);
68 9580 : return BailoutId(BailoutId::kFirstBuiltinContinuationId + name);
69 : }
70 :
71 2254 : Builtins::Name Builtins::GetBuiltinFromBailoutId(BailoutId id) {
72 2254 : int builtin_index = id.ToInt() - BailoutId::kFirstBuiltinContinuationId;
73 : DCHECK(Builtins::KindOf(builtin_index) == TFJ ||
74 : Builtins::KindOf(builtin_index) == TFC);
75 2254 : return static_cast<Name>(builtin_index);
76 : }
77 :
78 62427 : void Builtins::TearDown() { initialized_ = false; }
79 :
80 0 : const char* Builtins::Lookup(Address pc) {
81 : // Off-heap pc's can be looked up through binary search.
82 : if (FLAG_embedded_builtins) {
83 0 : Code maybe_builtin = InstructionStream::TryLookupCode(isolate_, pc);
84 0 : if (!maybe_builtin.is_null()) return name(maybe_builtin->builtin_index());
85 : }
86 :
87 : // May be called during initialization (disassembler).
88 0 : if (initialized_) {
89 0 : for (int i = 0; i < builtin_count; i++) {
90 0 : if (isolate_->heap()->builtin(i)->contains(pc)) return name(i);
91 : }
92 : }
93 : return nullptr;
94 : }
95 :
96 3532 : Handle<Code> Builtins::NonPrimitiveToPrimitive(ToPrimitiveHint hint) {
97 3532 : switch (hint) {
98 : case ToPrimitiveHint::kDefault:
99 : return builtin_handle(kNonPrimitiveToPrimitive_Default);
100 : case ToPrimitiveHint::kNumber:
101 : return builtin_handle(kNonPrimitiveToPrimitive_Number);
102 : case ToPrimitiveHint::kString:
103 : return builtin_handle(kNonPrimitiveToPrimitive_String);
104 : }
105 0 : UNREACHABLE();
106 : }
107 :
108 280 : Handle<Code> Builtins::OrdinaryToPrimitive(OrdinaryToPrimitiveHint hint) {
109 280 : switch (hint) {
110 : case OrdinaryToPrimitiveHint::kNumber:
111 : return builtin_handle(kOrdinaryToPrimitive_Number);
112 : case OrdinaryToPrimitiveHint::kString:
113 : return builtin_handle(kOrdinaryToPrimitive_String);
114 : }
115 0 : UNREACHABLE();
116 : }
117 :
118 255696 : void Builtins::set_builtin(int index, Code builtin) {
119 511392 : isolate_->heap()->set_builtin(index, builtin);
120 255696 : }
121 :
122 108299806 : Code Builtins::builtin(int index) { return isolate_->heap()->builtin(index); }
123 :
124 28692450 : Handle<Code> Builtins::builtin_handle(int index) {
125 : DCHECK(IsBuiltinId(index));
126 : return Handle<Code>(
127 62762310 : reinterpret_cast<Address*>(isolate_->heap()->builtin_address(index)));
128 : }
129 :
130 : // static
131 6529 : int Builtins::GetStackParameterCount(Name name) {
132 : DCHECK(Builtins::KindOf(name) == TFJ);
133 6529 : return static_cast<int>(builtin_metadata[name].cpp_entry_or_parameter_count);
134 : }
135 :
136 : // static
137 2686267 : Callable Builtins::CallableFor(Isolate* isolate, Name name) {
138 : Handle<Code> code = isolate->builtins()->builtin_handle(name);
139 : CallDescriptors::Key key;
140 2686263 : switch (name) {
141 : // This macro is deliberately crafted so as to emit very little code,
142 : // in order to keep binary size of this function under control.
143 : #define CASE_OTHER(Name, ...) \
144 : case k##Name: { \
145 : key = Builtin_##Name##_InterfaceDescriptor::key(); \
146 : break; \
147 : }
148 56 : BUILTIN_LIST(IGNORE_BUILTIN, IGNORE_BUILTIN, IGNORE_BUILTIN, CASE_OTHER,
149 : CASE_OTHER, CASE_OTHER, IGNORE_BUILTIN, CASE_OTHER)
150 : #undef CASE_OTHER
151 : default:
152 : Builtins::Kind kind = Builtins::KindOf(name);
153 : DCHECK_NE(BCH, kind);
154 54929 : if (kind == TFJ || kind == CPP) {
155 : return Callable(code, JSTrampolineDescriptor{});
156 : }
157 0 : UNREACHABLE();
158 : }
159 : CallInterfaceDescriptor descriptor(key);
160 : return Callable(code, descriptor);
161 : }
162 :
163 : // static
164 379715701 : const char* Builtins::name(int index) {
165 : DCHECK(IsBuiltinId(index));
166 379715701 : return builtin_metadata[index].name;
167 : }
168 :
169 0 : void Builtins::PrintBuiltinCode() {
170 : DCHECK(FLAG_print_builtin_code);
171 : #ifdef ENABLE_DISASSEMBLER
172 : for (int i = 0; i < builtin_count; i++) {
173 : const char* builtin_name = name(i);
174 : Handle<Code> code = builtin_handle(i);
175 : if (PassesFilter(CStrVector(builtin_name),
176 : CStrVector(FLAG_print_builtin_code_filter))) {
177 : CodeTracer::Scope trace_scope(isolate_->GetCodeTracer());
178 : OFStream os(trace_scope.file());
179 : code->Disassemble(builtin_name, os);
180 : os << "\n";
181 : }
182 : }
183 : #endif
184 0 : }
185 :
186 0 : void Builtins::PrintBuiltinSize() {
187 : DCHECK(FLAG_print_builtin_size);
188 0 : for (int i = 0; i < builtin_count; i++) {
189 : const char* builtin_name = name(i);
190 0 : const char* kind = KindNameOf(i);
191 0 : Code code = builtin(i);
192 : PrintF(stdout, "%s Builtin, %s, %d\n", kind, builtin_name,
193 0 : code->InstructionSize());
194 : }
195 0 : }
196 :
197 : // static
198 2918 : Address Builtins::CppEntryOf(int index) {
199 : DCHECK(Builtins::HasCppImplementation(index));
200 2918 : return builtin_metadata[index].cpp_entry_or_parameter_count;
201 : }
202 :
203 : // static
204 0 : bool Builtins::IsBuiltin(const Code code) {
205 0 : return Builtins::IsBuiltinId(code->builtin_index());
206 : }
207 :
208 9919194 : bool Builtins::IsBuiltinHandle(Handle<HeapObject> maybe_code,
209 : int* index) const {
210 9919194 : Heap* heap = isolate_->heap();
211 : Address handle_location = maybe_code.address();
212 9919194 : Address start = heap->builtin_address(0);
213 9919197 : Address end = heap->builtin_address(Builtins::builtin_count);
214 9919199 : if (handle_location >= end) return false;
215 9862224 : if (handle_location < start) return false;
216 9832366 : *index = static_cast<int>(handle_location - start) >> kSystemPointerSizeLog2;
217 : DCHECK(Builtins::IsBuiltinId(*index));
218 9832366 : return true;
219 : }
220 :
221 : // static
222 1277450 : bool Builtins::IsIsolateIndependentBuiltin(const Code code) {
223 : if (FLAG_embedded_builtins) {
224 : const int builtin_index = code->builtin_index();
225 1277450 : return Builtins::IsBuiltinId(builtin_index) &&
226 : Builtins::IsIsolateIndependent(builtin_index);
227 : } else {
228 : return false;
229 : }
230 : }
231 :
232 : // static
233 208320 : bool Builtins::IsWasmRuntimeStub(int index) {
234 : DCHECK(IsBuiltinId(index));
235 208320 : switch (index) {
236 : #define CASE_TRAP(Name) case kThrowWasm##Name:
237 : #define CASE(Name) case k##Name:
238 : WASM_RUNTIME_STUB_LIST(CASE, CASE_TRAP)
239 : #undef CASE_TRAP
240 : #undef CASE
241 : return true;
242 : default:
243 203672 : return false;
244 : }
245 : UNREACHABLE();
246 : }
247 :
248 : // static
249 62473 : void Builtins::UpdateBuiltinEntryTable(Isolate* isolate) {
250 : Heap* heap = isolate->heap();
251 : Address* builtin_entry_table = isolate->builtin_entry_table();
252 190132959 : for (int i = 0; i < builtin_count; i++) {
253 190070517 : builtin_entry_table[i] = heap->builtin(i)->InstructionStart();
254 : }
255 62442 : }
256 :
257 : namespace {
258 :
259 85288 : class OffHeapTrampolineGenerator {
260 : public:
261 85288 : explicit OffHeapTrampolineGenerator(Isolate* isolate)
262 : : isolate_(isolate),
263 : masm_(isolate, CodeObjectRequired::kYes,
264 170576 : ExternalAssemblerBuffer(buffer_, kBufferSize)) {}
265 :
266 85288 : CodeDesc Generate(Address off_heap_entry) {
267 : // Generate replacement code that simply tail-calls the off-heap code.
268 : DCHECK(!masm_.has_frame());
269 : {
270 170576 : FrameScope scope(&masm_, StackFrame::NONE);
271 85288 : masm_.JumpToInstructionStream(off_heap_entry);
272 : }
273 :
274 85288 : CodeDesc desc;
275 85288 : masm_.GetCode(isolate_, &desc);
276 85288 : return desc;
277 : }
278 :
279 : Handle<HeapObject> CodeObject() { return masm_.CodeObject(); }
280 :
281 : private:
282 : Isolate* isolate_;
283 : // Enough to fit the single jmp.
284 : static constexpr int kBufferSize = 256;
285 : byte buffer_[kBufferSize];
286 : MacroAssembler masm_;
287 : };
288 :
289 : constexpr int OffHeapTrampolineGenerator::kBufferSize;
290 :
291 : } // namespace
292 :
293 : // static
294 85232 : Handle<Code> Builtins::GenerateOffHeapTrampolineFor(Isolate* isolate,
295 : Address off_heap_entry) {
296 : DCHECK_NOT_NULL(isolate->embedded_blob());
297 : DCHECK_NE(0, isolate->embedded_blob_size());
298 :
299 85232 : OffHeapTrampolineGenerator generator(isolate);
300 85232 : CodeDesc desc = generator.Generate(off_heap_entry);
301 :
302 : return isolate->factory()->NewCode(desc, Code::BUILTIN,
303 255696 : generator.CodeObject());
304 : }
305 :
306 : // static
307 56 : Handle<ByteArray> Builtins::GenerateOffHeapTrampolineRelocInfo(
308 : Isolate* isolate) {
309 56 : OffHeapTrampolineGenerator generator(isolate);
310 : // Generate a jump to a dummy address as we're not actually interested in the
311 : // generated instruction stream.
312 56 : CodeDesc desc = generator.Generate(kNullAddress);
313 :
314 : Handle<ByteArray> reloc_info = isolate->factory()->NewByteArray(
315 56 : desc.reloc_size, AllocationType::kReadOnly);
316 : Code::CopyRelocInfoToByteArray(*reloc_info, desc);
317 :
318 112 : return reloc_info;
319 : }
320 :
321 : // static
322 22851025 : Builtins::Kind Builtins::KindOf(int index) {
323 : DCHECK(IsBuiltinId(index));
324 22917663 : return builtin_metadata[index].kind;
325 : }
326 :
327 : // static
328 0 : const char* Builtins::KindNameOf(int index) {
329 : Kind kind = Builtins::KindOf(index);
330 : // clang-format off
331 0 : switch (kind) {
332 : case CPP: return "CPP";
333 0 : case API: return "API";
334 0 : case TFJ: return "TFJ";
335 0 : case TFC: return "TFC";
336 0 : case TFS: return "TFS";
337 0 : case TFH: return "TFH";
338 0 : case BCH: return "BCH";
339 0 : case ASM: return "ASM";
340 : }
341 : // clang-format on
342 0 : UNREACHABLE();
343 : }
344 :
345 : // static
346 5404 : bool Builtins::IsCpp(int index) { return Builtins::KindOf(index) == CPP; }
347 :
348 : // static
349 9007 : bool Builtins::HasCppImplementation(int index) {
350 : Kind kind = Builtins::KindOf(index);
351 9007 : return (kind == CPP || kind == API);
352 : }
353 :
354 : // static
355 1090603 : bool Builtins::AllowDynamicFunction(Isolate* isolate, Handle<JSFunction> target,
356 : Handle<JSObject> target_global_proxy) {
357 1090603 : if (FLAG_allow_unsafe_function_constructor) return true;
358 : HandleScopeImplementer* impl = isolate->handle_scope_implementer();
359 1090387 : Handle<Context> responsible_context = impl->LastEnteredOrMicrotaskContext();
360 : // TODO(jochen): Remove this.
361 1090387 : if (responsible_context.is_null()) {
362 : return true;
363 : }
364 1090387 : if (*responsible_context == target->context()) return true;
365 263 : return isolate->MayAccess(responsible_context, target_global_proxy);
366 : }
367 :
368 1816 : Builtins::Name ExampleBuiltinForTorqueFunctionPointerType(
369 : size_t function_pointer_type_id) {
370 1816 : switch (function_pointer_type_id) {
371 : #define FUNCTION_POINTER_ID_CASE(id, name) \
372 : case id: \
373 : return Builtins::k##name;
374 504 : TORQUE_FUNCTION_POINTER_TYPE_TO_BUILTIN_MAP(FUNCTION_POINTER_ID_CASE)
375 : #undef FUNCTION_POINTER_ID_CASE
376 : default:
377 0 : UNREACHABLE();
378 : }
379 : }
380 :
381 : } // namespace internal
382 122036 : } // namespace v8
|