• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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