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