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
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
34const int kBufferSize = 128 * KB;
35
36AssemblerOptions 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
59using MacroAssemblerGenerator = void (*)(MacroAssembler*);
60using CodeAssemblerGenerator = void (*)(compiler::CodeAssemblerState*);
61
62Handle<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
84Code 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
127Code 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.
150Code 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.
169Code 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
195void 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
202void 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
216void 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
262namespace {
263
264Code 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
277void 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