V8 API Reference, 7.2.502.16 (for Deno 0.2.4)
interpreter.cc
1 // Copyright 2015 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/interpreter/interpreter.h"
6 
7 #include <fstream>
8 #include <memory>
9 
10 #include "builtins-generated/bytecodes-builtins-list.h"
11 #include "src/ast/prettyprinter.h"
12 #include "src/bootstrapper.h"
13 #include "src/compiler.h"
14 #include "src/counters-inl.h"
15 #include "src/interpreter/bytecode-generator.h"
16 #include "src/interpreter/bytecodes.h"
17 #include "src/log.h"
18 #include "src/objects-inl.h"
19 #include "src/objects/shared-function-info.h"
20 #include "src/objects/slots.h"
21 #include "src/parsing/parse-info.h"
22 #include "src/setup-isolate.h"
23 #include "src/snapshot/snapshot.h"
24 #include "src/unoptimized-compilation-info.h"
25 #include "src/visitors.h"
26 
27 namespace v8 {
28 namespace internal {
29 namespace interpreter {
30 
32  public:
34  ParseInfo* parse_info, FunctionLiteral* literal,
35  AccountingAllocator* allocator,
36  std::vector<FunctionLiteral*>* eager_inner_literals);
37 
38  protected:
39  Status ExecuteJobImpl() final;
40  Status FinalizeJobImpl(Handle<SharedFunctionInfo> shared_info,
41  Isolate* isolate) final;
42 
43  private:
44  BytecodeGenerator* generator() { return &generator_; }
45 
46  Zone zone_;
47  UnoptimizedCompilationInfo compilation_info_;
48  BytecodeGenerator generator_;
49 
50  DISALLOW_COPY_AND_ASSIGN(InterpreterCompilationJob);
51 };
52 
53 Interpreter::Interpreter(Isolate* isolate)
54  : isolate_(isolate),
55  interpreter_entry_trampoline_instruction_start_(kNullAddress) {
56  memset(dispatch_table_, 0, sizeof(dispatch_table_));
57 
58  if (FLAG_trace_ignition_dispatches) {
59  static const int kBytecodeCount = static_cast<int>(Bytecode::kLast) + 1;
60  bytecode_dispatch_counters_table_.reset(
61  new uintptr_t[kBytecodeCount * kBytecodeCount]);
62  memset(bytecode_dispatch_counters_table_.get(), 0,
63  sizeof(uintptr_t) * kBytecodeCount * kBytecodeCount);
64  }
65 }
66 
67 namespace {
68 
69 int BuiltinIndexFromBytecode(Bytecode bytecode, OperandScale operand_scale) {
70  int index = BytecodeOperands::OperandScaleAsIndex(operand_scale) *
71  kNumberOfBytecodeHandlers +
72  static_cast<int>(bytecode);
73  int offset = kBytecodeToBuiltinsMapping[index];
74  return offset >= 0 ? Builtins::kFirstBytecodeHandler + offset
75  : Builtins::kIllegalHandler;
76 }
77 
78 } // namespace
79 
80 Code Interpreter::GetBytecodeHandler(Bytecode bytecode,
81  OperandScale operand_scale) {
82  int builtin_index = BuiltinIndexFromBytecode(bytecode, operand_scale);
83  Builtins* builtins = isolate_->builtins();
84  return builtins->builtin(builtin_index);
85 }
86 
87 void Interpreter::SetBytecodeHandler(Bytecode bytecode,
88  OperandScale operand_scale, Code handler) {
89  DCHECK(handler->kind() == Code::BYTECODE_HANDLER);
90  size_t index = GetDispatchTableIndex(bytecode, operand_scale);
91  dispatch_table_[index] = handler->InstructionStart();
92 }
93 
94 // static
95 size_t Interpreter::GetDispatchTableIndex(Bytecode bytecode,
96  OperandScale operand_scale) {
97  static const size_t kEntriesPerOperandScale = 1u << kBitsPerByte;
98  size_t index = static_cast<size_t>(bytecode);
99  return index + BytecodeOperands::OperandScaleAsIndex(operand_scale) *
100  kEntriesPerOperandScale;
101 }
102 
103 void Interpreter::IterateDispatchTable(RootVisitor* v) {
104  if (FLAG_embedded_builtins && !isolate_->serializer_enabled() &&
105  isolate_->embedded_blob() != nullptr) {
106 // If builtins are embedded (and we're not generating a snapshot), then
107 // every bytecode handler will be off-heap, so there's no point iterating
108 // over them.
109 #ifdef DEBUG
110  for (int i = 0; i < kDispatchTableSize; i++) {
111  Address code_entry = dispatch_table_[i];
112  CHECK(code_entry == kNullAddress ||
113  InstructionStream::PcIsOffHeap(isolate_, code_entry));
114  }
115 #endif // ENABLE_SLOW_DCHECKS
116  return;
117  }
118 
119  for (int i = 0; i < kDispatchTableSize; i++) {
120  Address code_entry = dispatch_table_[i];
121  // Skip over off-heap bytecode handlers since they will never move.
122  if (InstructionStream::PcIsOffHeap(isolate_, code_entry)) continue;
123 
124  // TODO(jkummerow): Would it hurt to simply do:
125  // if (code_entry == kNullAddress) continue;
126  Code code;
127  if (code_entry != kNullAddress) {
128  code = Code::GetCodeFromTargetAddress(code_entry);
129  }
130  Code old_code = code;
131  v->VisitRootPointer(Root::kDispatchTable, nullptr, ObjectSlot(&code));
132  if (code != old_code) {
133  dispatch_table_[i] = code->entry();
134  }
135  }
136 }
137 
138 int Interpreter::InterruptBudget() {
139  return FLAG_interrupt_budget;
140 }
141 
142 namespace {
143 
144 void MaybePrintAst(ParseInfo* parse_info,
145  UnoptimizedCompilationInfo* compilation_info) {
146  if (!FLAG_print_ast) return;
147 
148  StdoutStream os;
149  std::unique_ptr<char[]> name = compilation_info->literal()->GetDebugName();
150  os << "[generating bytecode for function: " << name.get() << "]" << std::endl;
151 #ifdef DEBUG
152  os << "--- AST ---" << std::endl
153  << AstPrinter(parse_info->stack_limit())
154  .PrintProgram(compilation_info->literal())
155  << std::endl;
156 #endif // DEBUG
157 }
158 
159 bool ShouldPrintBytecode(Handle<SharedFunctionInfo> shared) {
160  if (!FLAG_print_bytecode) return false;
161 
162  // Checks whether function passed the filter.
163  if (shared->is_toplevel()) {
164  Vector<const char> filter = CStrVector(FLAG_print_bytecode_filter);
165  return (filter.length() == 0) || (filter.length() == 1 && filter[0] == '*');
166  } else {
167  return shared->PassesFilter(FLAG_print_bytecode_filter);
168  }
169 }
170 
171 } // namespace
172 
173 InterpreterCompilationJob::InterpreterCompilationJob(
174  ParseInfo* parse_info, FunctionLiteral* literal,
175  AccountingAllocator* allocator,
176  std::vector<FunctionLiteral*>* eager_inner_literals)
177  : UnoptimizedCompilationJob(parse_info->stack_limit(), parse_info,
178  &compilation_info_),
179  zone_(allocator, ZONE_NAME),
180  compilation_info_(&zone_, parse_info, literal),
181  generator_(&compilation_info_, parse_info->ast_string_constants(),
182  eager_inner_literals) {}
183 
184 InterpreterCompilationJob::Status InterpreterCompilationJob::ExecuteJobImpl() {
185  RuntimeCallTimerScope runtimeTimerScope(
186  parse_info()->runtime_call_stats(),
187  parse_info()->on_background_thread()
188  ? RuntimeCallCounterId::kCompileBackgroundIgnition
189  : RuntimeCallCounterId::kCompileIgnition);
190  // TODO(lpy): add support for background compilation RCS trace.
191  TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"), "V8.CompileIgnition");
192 
193  // Print AST if flag is enabled. Note, if compiling on a background thread
194  // then ASTs from different functions may be intersperse when printed.
195  MaybePrintAst(parse_info(), compilation_info());
196 
197  generator()->GenerateBytecode(stack_limit());
198 
199  if (generator()->HasStackOverflow()) {
200  return FAILED;
201  }
202  return SUCCEEDED;
203 }
204 
205 InterpreterCompilationJob::Status InterpreterCompilationJob::FinalizeJobImpl(
206  Handle<SharedFunctionInfo> shared_info, Isolate* isolate) {
207  RuntimeCallTimerScope runtimeTimerScope(
208  parse_info()->runtime_call_stats(),
209  RuntimeCallCounterId::kCompileIgnitionFinalization);
210  TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"),
211  "V8.CompileIgnitionFinalization");
212 
213  Handle<BytecodeArray> bytecodes =
214  generator()->FinalizeBytecode(isolate, parse_info()->script());
215  if (generator()->HasStackOverflow()) {
216  return FAILED;
217  }
218 
219  if (ShouldPrintBytecode(shared_info)) {
220  StdoutStream os;
221  std::unique_ptr<char[]> name =
222  compilation_info()->literal()->GetDebugName();
223  os << "[generated bytecode for function: " << name.get() << "]"
224  << std::endl;
225  bytecodes->Disassemble(os);
226  os << std::flush;
227  }
228 
229  compilation_info()->SetBytecodeArray(bytecodes);
230  return SUCCEEDED;
231 }
232 
233 UnoptimizedCompilationJob* Interpreter::NewCompilationJob(
234  ParseInfo* parse_info, FunctionLiteral* literal,
235  AccountingAllocator* allocator,
236  std::vector<FunctionLiteral*>* eager_inner_literals) {
237  return new InterpreterCompilationJob(parse_info, literal, allocator,
238  eager_inner_literals);
239 }
240 
241 void Interpreter::ForEachBytecode(
242  const std::function<void(Bytecode, OperandScale)>& f) {
243  constexpr OperandScale kOperandScales[] = {
244 #define VALUE(Name, _) OperandScale::k##Name,
245  OPERAND_SCALE_LIST(VALUE)
246 #undef VALUE
247  };
248 
249  for (OperandScale operand_scale : kOperandScales) {
250  for (int i = 0; i < Bytecodes::kBytecodeCount; i++) {
251  f(Bytecodes::FromByte(i), operand_scale);
252  }
253  }
254 }
255 
256 void Interpreter::Initialize() {
257  Builtins* builtins = isolate_->builtins();
258 
259  // Set the interpreter entry trampoline entry point now that builtins are
260  // initialized.
261  Handle<Code> code = BUILTIN_CODE(isolate_, InterpreterEntryTrampoline);
262  DCHECK(builtins->is_initialized());
263  DCHECK(code->is_off_heap_trampoline() ||
264  isolate_->heap()->IsImmovable(*code));
265  interpreter_entry_trampoline_instruction_start_ = code->InstructionStart();
266 
267  // Initialize the dispatch table.
268  Code illegal = builtins->builtin(Builtins::kIllegalHandler);
269  int builtin_id = Builtins::kFirstBytecodeHandler;
270  ForEachBytecode([=, &builtin_id](Bytecode bytecode,
271  OperandScale operand_scale) {
272  Code handler = illegal;
273  if (Bytecodes::BytecodeHasHandler(bytecode, operand_scale)) {
274 #ifdef DEBUG
275  std::string builtin_name(Builtins::name(builtin_id));
276  std::string expected_name =
277  Bytecodes::ToString(bytecode, operand_scale, "") + "Handler";
278  DCHECK_EQ(expected_name, builtin_name);
279 #endif
280  handler = builtins->builtin(builtin_id++);
281  }
282  SetBytecodeHandler(bytecode, operand_scale, handler);
283  });
284  DCHECK(builtin_id == Builtins::builtin_count);
285  DCHECK(IsDispatchTableInitialized());
286 }
287 
288 bool Interpreter::IsDispatchTableInitialized() const {
289  return dispatch_table_[0] != kNullAddress;
290 }
291 
292 const char* Interpreter::LookupNameOfBytecodeHandler(const Code code) {
293 #ifdef ENABLE_DISASSEMBLER
294 #define RETURN_NAME(Name, ...) \
295  if (dispatch_table_[Bytecodes::ToByte(Bytecode::k##Name)] == \
296  code->entry()) { \
297  return #Name; \
298  }
299  BYTECODE_LIST(RETURN_NAME)
300 #undef RETURN_NAME
301 #endif // ENABLE_DISASSEMBLER
302  return nullptr;
303 }
304 
305 uintptr_t Interpreter::GetDispatchCounter(Bytecode from, Bytecode to) const {
306  int from_index = Bytecodes::ToByte(from);
307  int to_index = Bytecodes::ToByte(to);
308  return bytecode_dispatch_counters_table_[from_index * kNumberOfBytecodes +
309  to_index];
310 }
311 
312 Local<v8::Object> Interpreter::GetDispatchCountersObject() {
313  v8::Isolate* isolate = reinterpret_cast<v8::Isolate*>(isolate_);
314  Local<v8::Context> context = isolate->GetCurrentContext();
315 
316  Local<v8::Object> counters_map = v8::Object::New(isolate);
317 
318  // Output is a JSON-encoded object of objects.
319  //
320  // The keys on the top level object are source bytecodes,
321  // and corresponding value are objects. Keys on these last are the
322  // destinations of the dispatch and the value associated is a counter for
323  // the correspondent source-destination dispatch chain.
324  //
325  // Only non-zero counters are written to file, but an entry in the top-level
326  // object is always present, even if the value is empty because all counters
327  // for that source are zero.
328 
329  for (int from_index = 0; from_index < kNumberOfBytecodes; ++from_index) {
330  Bytecode from_bytecode = Bytecodes::FromByte(from_index);
331  Local<v8::Object> counters_row = v8::Object::New(isolate);
332 
333  for (int to_index = 0; to_index < kNumberOfBytecodes; ++to_index) {
334  Bytecode to_bytecode = Bytecodes::FromByte(to_index);
335  uintptr_t counter = GetDispatchCounter(from_bytecode, to_bytecode);
336 
337  if (counter > 0) {
338  std::string to_name = Bytecodes::ToString(to_bytecode);
339  Local<v8::String> to_name_object =
340  v8::String::NewFromUtf8(isolate, to_name.c_str(),
342  .ToLocalChecked();
343  Local<v8::Number> counter_object = v8::Number::New(isolate, counter);
344  CHECK(counters_row
345  ->DefineOwnProperty(context, to_name_object, counter_object)
346  .IsJust());
347  }
348  }
349 
350  std::string from_name = Bytecodes::ToString(from_bytecode);
351  Local<v8::String> from_name_object =
352  v8::String::NewFromUtf8(isolate, from_name.c_str(),
354  .ToLocalChecked();
355 
356  CHECK(
357  counters_map->DefineOwnProperty(context, from_name_object, counters_row)
358  .IsJust());
359  }
360 
361  return counters_map;
362 }
363 
364 } // namespace interpreter
365 } // namespace internal
366 } // namespace v8
Definition: libplatform.h:13
static V8_WARN_UNUSED_RESULT MaybeLocal< String > NewFromUtf8(Isolate *isolate, const char *data, v8::NewStringType type, int length=-1)
Definition: api.cc:6511