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