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