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