1// Copyright 2017 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/setup-isolate.h"
6
7#include "src/assembler-inl.h"
8#include "src/builtins/builtins.h"
9#include "src/code-events.h"
10#include "src/compiler/code-assembler.h"
11#include "src/handles-inl.h"
12#include "src/heap/heap-inl.h" // For MemoryAllocator::code_range.
13#include "src/interface-descriptors.h"
14#include "src/interpreter/bytecodes.h"
15#include "src/interpreter/interpreter-generator.h"
16#include "src/interpreter/interpreter.h"
17#include "src/isolate.h"
18#include "src/macro-assembler.h"
19#include "src/objects-inl.h"
20#include "src/objects/shared-function-info.h"
21#include "src/objects/smi.h"
22
23namespace v8 {
24namespace internal {
25
26// Forward declarations for C++ builtins.
27#define FORWARD_DECLARE(Name) \
28 Address Builtin_##Name(int argc, Address* args, Isolate* isolate);
29BUILTIN_LIST_C(FORWARD_DECLARE)
30#undef FORWARD_DECLARE
31
32namespace {
33
34void PostBuildProfileAndTracing(Isolate* isolate, Code code, const char* name) {
35 PROFILE(isolate, CodeCreateEvent(CodeEventListener::BUILTIN_TAG,
36 AbstractCode::cast(code), name));
37}
38
39AssemblerOptions BuiltinAssemblerOptions(Isolate* isolate,
40 int32_t builtin_index) {
41 AssemblerOptions options = AssemblerOptions::Default(isolate);
42 CHECK(!options.isolate_independent_code);
43 CHECK(!options.use_pc_relative_calls_and_jumps);
44 CHECK(!options.collect_win64_unwind_info);
45
46 if (!isolate->IsGeneratingEmbeddedBuiltins() ||
47 !Builtins::IsIsolateIndependent(builtin_index)) {
48 return options;
49 }
50
51 const base::AddressRegion& code_range =
52 isolate->heap()->memory_allocator()->code_range();
53 bool pc_relative_calls_fit_in_code_range =
54 !code_range.is_empty() &&
55 std::ceil(static_cast<float>(code_range.size() / MB)) <=
56 kMaxPCRelativeCodeRangeInMB;
57
58 options.isolate_independent_code = true;
59 options.use_pc_relative_calls_and_jumps = pc_relative_calls_fit_in_code_range;
60 options.collect_win64_unwind_info = true;
61
62 return options;
63}
64
65typedef void (*MacroAssemblerGenerator)(MacroAssembler*);
66typedef void (*CodeAssemblerGenerator)(compiler::CodeAssemblerState*);
67
68Handle<Code> BuildPlaceholder(Isolate* isolate, int32_t builtin_index) {
69 HandleScope scope(isolate);
70 constexpr int kBufferSize = 1 * KB;
71 byte buffer[kBufferSize];
72 MacroAssembler masm(isolate, CodeObjectRequired::kYes,
73 ExternalAssemblerBuffer(buffer, kBufferSize));
74 DCHECK(!masm.has_frame());
75 {
76 FrameScope scope(&masm, StackFrame::NONE);
77 // The contents of placeholder don't matter, as long as they don't create
78 // embedded constants or external references.
79 masm.Move(kJavaScriptCallCodeStartRegister, Smi::zero());
80 masm.Call(kJavaScriptCallCodeStartRegister);
81 }
82 CodeDesc desc;
83 masm.GetCode(isolate, &desc);
84 Handle<Code> code = isolate->factory()->NewCode(
85 desc, Code::BUILTIN, masm.CodeObject(), builtin_index);
86 return scope.CloseAndEscape(code);
87}
88
89Code BuildWithMacroAssembler(Isolate* isolate, int32_t builtin_index,
90 MacroAssemblerGenerator generator,
91 const char* s_name) {
92 HandleScope scope(isolate);
93 // Canonicalize handles, so that we can share constant pool entries pointing
94 // to code targets without dereferencing their handles.
95 CanonicalHandleScope canonical(isolate);
96 constexpr int kBufferSize = 32 * KB;
97 byte buffer[kBufferSize];
98
99 MacroAssembler masm(isolate, BuiltinAssemblerOptions(isolate, builtin_index),
100 CodeObjectRequired::kYes,
101 ExternalAssemblerBuffer(buffer, kBufferSize));
102 masm.set_builtin_index(builtin_index);
103 DCHECK(!masm.has_frame());
104 generator(&masm);
105
106 int handler_table_offset = 0;
107
108 // JSEntry builtins are a special case and need to generate a handler table.
109 DCHECK_EQ(Builtins::KindOf(Builtins::kJSEntry), Builtins::ASM);
110 DCHECK_EQ(Builtins::KindOf(Builtins::kJSConstructEntry), Builtins::ASM);
111 DCHECK_EQ(Builtins::KindOf(Builtins::kJSRunMicrotasksEntry), Builtins::ASM);
112 if (Builtins::IsJSEntryVariant(builtin_index)) {
113 static constexpr int kJSEntryHandlerCount = 1;
114 handler_table_offset =
115 HandlerTable::EmitReturnTableStart(&masm, kJSEntryHandlerCount);
116 HandlerTable::EmitReturnEntry(
117 &masm, 0, isolate->builtins()->js_entry_handler_offset());
118 }
119
120 CodeDesc desc;
121 masm.GetCode(isolate, &desc, MacroAssembler::kNoSafepointTable,
122 handler_table_offset);
123
124 static constexpr bool kIsNotTurbofanned = false;
125 static constexpr int kStackSlots = 0;
126
127 Handle<Code> code = isolate->factory()->NewCode(
128 desc, Code::BUILTIN, masm.CodeObject(), builtin_index,
129 MaybeHandle<ByteArray>(), DeoptimizationData::Empty(isolate), kMovable,
130 kIsNotTurbofanned, kStackSlots);
131#if defined(V8_OS_WIN_X64)
132 isolate->SetBuiltinUnwindData(builtin_index, masm.GetUnwindInfo());
133#endif
134 PostBuildProfileAndTracing(isolate, *code, s_name);
135 return *code;
136}
137
138Code BuildAdaptor(Isolate* isolate, int32_t builtin_index,
139 Address builtin_address,
140 Builtins::ExitFrameType exit_frame_type, const char* name) {
141 HandleScope scope(isolate);
142 // Canonicalize handles, so that we can share constant pool entries pointing
143 // to code targets without dereferencing their handles.
144 CanonicalHandleScope canonical(isolate);
145 constexpr int kBufferSize = 32 * KB;
146 byte buffer[kBufferSize];
147 MacroAssembler masm(isolate, BuiltinAssemblerOptions(isolate, builtin_index),
148 CodeObjectRequired::kYes,
149 ExternalAssemblerBuffer(buffer, kBufferSize));
150 masm.set_builtin_index(builtin_index);
151 DCHECK(!masm.has_frame());
152 Builtins::Generate_Adaptor(&masm, builtin_address, exit_frame_type);
153 CodeDesc desc;
154 masm.GetCode(isolate, &desc);
155 Handle<Code> code = isolate->factory()->NewCode(
156 desc, Code::BUILTIN, masm.CodeObject(), builtin_index);
157 PostBuildProfileAndTracing(isolate, *code, name);
158 return *code;
159}
160
161// Builder for builtins implemented in TurboFan with JS linkage.
162Code BuildWithCodeStubAssemblerJS(Isolate* isolate, int32_t builtin_index,
163 CodeAssemblerGenerator generator, int argc,
164 const char* name) {
165 HandleScope scope(isolate);
166 // Canonicalize handles, so that we can share constant pool entries pointing
167 // to code targets without dereferencing their handles.
168 CanonicalHandleScope canonical(isolate);
169
170 SegmentSize segment_size = isolate->serializer_enabled()
171 ? SegmentSize::kLarge
172 : SegmentSize::kDefault;
173 Zone zone(isolate->allocator(), ZONE_NAME, segment_size);
174 const int argc_with_recv =
175 (argc == SharedFunctionInfo::kDontAdaptArgumentsSentinel) ? 0 : argc + 1;
176 compiler::CodeAssemblerState state(
177 isolate, &zone, argc_with_recv, Code::BUILTIN, name,
178 PoisoningMitigationLevel::kDontPoison, builtin_index);
179 generator(&state);
180 Handle<Code> code = compiler::CodeAssembler::GenerateCode(
181 &state, BuiltinAssemblerOptions(isolate, builtin_index));
182 PostBuildProfileAndTracing(isolate, *code, name);
183 return *code;
184}
185
186// Builder for builtins implemented in TurboFan with CallStub linkage.
187Code BuildWithCodeStubAssemblerCS(Isolate* isolate, int32_t builtin_index,
188 CodeAssemblerGenerator generator,
189 CallDescriptors::Key interface_descriptor,
190 const char* name) {
191 HandleScope scope(isolate);
192 // Canonicalize handles, so that we can share constant pool entries pointing
193 // to code targets without dereferencing their handles.
194 CanonicalHandleScope canonical(isolate);
195 SegmentSize segment_size = isolate->serializer_enabled()
196 ? SegmentSize::kLarge
197 : SegmentSize::kDefault;
198 Zone zone(isolate->allocator(), ZONE_NAME, segment_size);
199 // The interface descriptor with given key must be initialized at this point
200 // and this construction just queries the details from the descriptors table.
201 CallInterfaceDescriptor descriptor(interface_descriptor);
202 // Ensure descriptor is already initialized.
203 DCHECK_LE(0, descriptor.GetRegisterParameterCount());
204 compiler::CodeAssemblerState state(
205 isolate, &zone, descriptor, Code::BUILTIN, name,
206 PoisoningMitigationLevel::kDontPoison, builtin_index);
207 generator(&state);
208 Handle<Code> code = compiler::CodeAssembler::GenerateCode(
209 &state, BuiltinAssemblerOptions(isolate, builtin_index));
210 PostBuildProfileAndTracing(isolate, *code, name);
211 return *code;
212}
213
214} // anonymous namespace
215
216// static
217void SetupIsolateDelegate::AddBuiltin(Builtins* builtins, int index,
218 Code code) {
219 DCHECK_EQ(index, code->builtin_index());
220 builtins->set_builtin(index, code);
221}
222
223// static
224void SetupIsolateDelegate::PopulateWithPlaceholders(Isolate* isolate) {
225 // Fill the builtins list with placeholders. References to these placeholder
226 // builtins are eventually replaced by the actual builtins. This is to
227 // support circular references between builtins.
228 Builtins* builtins = isolate->builtins();
229 HandleScope scope(isolate);
230 for (int i = 0; i < Builtins::builtin_count; i++) {
231 Handle<Code> placeholder = BuildPlaceholder(isolate, i);
232 AddBuiltin(builtins, i, *placeholder);
233 }
234}
235
236// static
237void SetupIsolateDelegate::ReplacePlaceholders(Isolate* isolate) {
238 // Replace references from all code objects to placeholders.
239 Builtins* builtins = isolate->builtins();
240 DisallowHeapAllocation no_gc;
241 CodeSpaceMemoryModificationScope modification_scope(isolate->heap());
242 static const int kRelocMask =
243 RelocInfo::ModeMask(RelocInfo::CODE_TARGET) |
244 RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT) |
245 RelocInfo::ModeMask(RelocInfo::RELATIVE_CODE_TARGET);
246 HeapIterator iterator(isolate->heap());
247 for (HeapObject obj = iterator.next(); !obj.is_null();
248 obj = iterator.next()) {
249 if (!obj->IsCode()) continue;
250 Code code = Code::cast(obj);
251 bool flush_icache = false;
252 for (RelocIterator it(code, kRelocMask); !it.done(); it.next()) {
253 RelocInfo* rinfo = it.rinfo();
254 if (RelocInfo::IsCodeTargetMode(rinfo->rmode())) {
255 Code target = Code::GetCodeFromTargetAddress(rinfo->target_address());
256 DCHECK_IMPLIES(RelocInfo::IsRelativeCodeTarget(rinfo->rmode()),
257 Builtins::IsIsolateIndependent(target->builtin_index()));
258 if (!target->is_builtin()) continue;
259 Code new_target = builtins->builtin(target->builtin_index());
260 rinfo->set_target_address(new_target->raw_instruction_start(),
261 UPDATE_WRITE_BARRIER, SKIP_ICACHE_FLUSH);
262 } else {
263 DCHECK(RelocInfo::IsEmbeddedObject(rinfo->rmode()));
264 Object object = rinfo->target_object();
265 if (!object->IsCode()) continue;
266 Code target = Code::cast(object);
267 if (!target->is_builtin()) continue;
268 Code new_target = builtins->builtin(target->builtin_index());
269 rinfo->set_target_object(isolate->heap(), new_target,
270 UPDATE_WRITE_BARRIER, SKIP_ICACHE_FLUSH);
271 }
272 flush_icache = true;
273 }
274 if (flush_icache) {
275 FlushInstructionCache(code->raw_instruction_start(),
276 code->raw_instruction_size());
277 }
278 }
279}
280
281namespace {
282
283Code GenerateBytecodeHandler(Isolate* isolate, int builtin_index,
284 const char* name,
285 interpreter::OperandScale operand_scale,
286 interpreter::Bytecode bytecode) {
287 DCHECK(interpreter::Bytecodes::BytecodeHasHandler(bytecode, operand_scale));
288
289 Handle<Code> code = interpreter::GenerateBytecodeHandler(
290 isolate, bytecode, operand_scale, builtin_index,
291 BuiltinAssemblerOptions(isolate, builtin_index));
292
293 PostBuildProfileAndTracing(isolate, *code, name);
294
295 return *code;
296}
297
298} // namespace
299
300// static
301void SetupIsolateDelegate::SetupBuiltinsInternal(Isolate* isolate) {
302 Builtins* builtins = isolate->builtins();
303 DCHECK(!builtins->initialized_);
304
305 PopulateWithPlaceholders(isolate);
306
307 // Create a scope for the handles in the builtins.
308 HandleScope scope(isolate);
309
310 int index = 0;
311 Code code;
312#define BUILD_CPP(Name) \
313 code = BuildAdaptor(isolate, index, FUNCTION_ADDR(Builtin_##Name), \
314 Builtins::BUILTIN_EXIT, #Name); \
315 AddBuiltin(builtins, index++, code);
316#define BUILD_API(Name) \
317 code = BuildAdaptor(isolate, index, FUNCTION_ADDR(Builtin_##Name), \
318 Builtins::EXIT, #Name); \
319 AddBuiltin(builtins, index++, code);
320#define BUILD_TFJ(Name, Argc, ...) \
321 code = BuildWithCodeStubAssemblerJS( \
322 isolate, index, &Builtins::Generate_##Name, Argc, #Name); \
323 AddBuiltin(builtins, index++, code);
324#define BUILD_TFC(Name, InterfaceDescriptor) \
325 /* Return size is from the provided CallInterfaceDescriptor. */ \
326 code = BuildWithCodeStubAssemblerCS( \
327 isolate, index, &Builtins::Generate_##Name, \
328 CallDescriptors::InterfaceDescriptor, #Name); \
329 AddBuiltin(builtins, index++, code);
330#define BUILD_TFS(Name, ...) \
331 /* Return size for generic TF builtins (stub linkage) is always 1. */ \
332 code = \
333 BuildWithCodeStubAssemblerCS(isolate, index, &Builtins::Generate_##Name, \
334 CallDescriptors::Name, #Name); \
335 AddBuiltin(builtins, index++, code);
336#define BUILD_TFH(Name, InterfaceDescriptor) \
337 /* Return size for IC builtins/handlers is always 1. */ \
338 code = BuildWithCodeStubAssemblerCS( \
339 isolate, index, &Builtins::Generate_##Name, \
340 CallDescriptors::InterfaceDescriptor, #Name); \
341 AddBuiltin(builtins, index++, code);
342
343#define BUILD_BCH(Name, OperandScale, Bytecode) \
344 code = GenerateBytecodeHandler(isolate, index, Builtins::name(index), \
345 OperandScale, Bytecode); \
346 AddBuiltin(builtins, index++, code);
347
348#define BUILD_ASM(Name, InterfaceDescriptor) \
349 code = BuildWithMacroAssembler(isolate, index, Builtins::Generate_##Name, \
350 #Name); \
351 AddBuiltin(builtins, index++, code);
352
353 BUILTIN_LIST(BUILD_CPP, BUILD_API, BUILD_TFJ, BUILD_TFC, BUILD_TFS, BUILD_TFH,
354 BUILD_BCH, BUILD_ASM);
355
356#undef BUILD_CPP
357#undef BUILD_API
358#undef BUILD_TFJ
359#undef BUILD_TFC
360#undef BUILD_TFS
361#undef BUILD_TFH
362#undef BUILD_BCH
363#undef BUILD_ASM
364 CHECK_EQ(Builtins::builtin_count, index);
365
366 ReplacePlaceholders(isolate);
367
368#define SET_PROMISE_REJECTION_PREDICTION(Name) \
369 builtins->builtin(Builtins::k##Name)->set_is_promise_rejection(true);
370
371 BUILTIN_PROMISE_REJECTION_PREDICTION_LIST(SET_PROMISE_REJECTION_PREDICTION)
372#undef SET_PROMISE_REJECTION_PREDICTION
373
374#define SET_EXCEPTION_CAUGHT_PREDICTION(Name) \
375 builtins->builtin(Builtins::k##Name)->set_is_exception_caught(true);
376
377 BUILTIN_EXCEPTION_CAUGHT_PREDICTION_LIST(SET_EXCEPTION_CAUGHT_PREDICTION)
378#undef SET_EXCEPTION_CAUGHT_PREDICTION
379
380 builtins->MarkInitialized();
381}
382
383} // namespace internal
384} // namespace v8
385