• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/ast/scopes.h"
13 #include "src/codegen/compiler.h"
14 #include "src/codegen/unoptimized-compilation-info.h"
15 #include "src/common/globals.h"
16 #include "src/execution/local-isolate.h"
17 #include "src/heap/parked-scope.h"
18 #include "src/init/bootstrapper.h"
19 #include "src/init/setup-isolate.h"
20 #include "src/interpreter/bytecode-generator.h"
21 #include "src/interpreter/bytecodes.h"
22 #include "src/logging/runtime-call-stats-scope.h"
23 #include "src/objects/objects-inl.h"
24 #include "src/objects/shared-function-info.h"
25 #include "src/objects/slots.h"
26 #include "src/objects/visitors.h"
27 #include "src/parsing/parse-info.h"
28 #include "src/snapshot/snapshot.h"
29 #include "src/utils/ostreams.h"
30 
31 namespace v8 {
32 namespace internal {
33 namespace interpreter {
34 
35 class InterpreterCompilationJob final : public UnoptimizedCompilationJob {
36  public:
37   InterpreterCompilationJob(ParseInfo* parse_info, FunctionLiteral* literal,
38                             Handle<Script> script,
39                             AccountingAllocator* allocator,
40                             std::vector<FunctionLiteral*>* eager_inner_literals,
41                             LocalIsolate* local_isolate);
42   InterpreterCompilationJob(const InterpreterCompilationJob&) = delete;
43   InterpreterCompilationJob& operator=(const InterpreterCompilationJob&) =
44       delete;
45 
46  protected:
47   Status ExecuteJobImpl() final;
48   Status FinalizeJobImpl(Handle<SharedFunctionInfo> shared_info,
49                          Isolate* isolate) final;
50   Status FinalizeJobImpl(Handle<SharedFunctionInfo> shared_info,
51                          LocalIsolate* isolate) final;
52 
53  private:
generator()54   BytecodeGenerator* generator() { return &generator_; }
55   template <typename IsolateT>
56   void CheckAndPrintBytecodeMismatch(IsolateT* isolate, Handle<Script> script,
57                                      Handle<BytecodeArray> bytecode);
58 
59   template <typename IsolateT>
60   Status DoFinalizeJobImpl(Handle<SharedFunctionInfo> shared_info,
61                            IsolateT* isolate);
62 
63   Zone zone_;
64   UnoptimizedCompilationInfo compilation_info_;
65   LocalIsolate* local_isolate_;
66   BytecodeGenerator generator_;
67 };
68 
Interpreter(Isolate * isolate)69 Interpreter::Interpreter(Isolate* isolate)
70     : isolate_(isolate),
71       interpreter_entry_trampoline_instruction_start_(kNullAddress) {
72   memset(dispatch_table_, 0, sizeof(dispatch_table_));
73 
74   if (V8_IGNITION_DISPATCH_COUNTING_BOOL) {
75     InitDispatchCounters();
76   }
77 }
78 
InitDispatchCounters()79 void Interpreter::InitDispatchCounters() {
80   static const int kBytecodeCount = static_cast<int>(Bytecode::kLast) + 1;
81   bytecode_dispatch_counters_table_.reset(
82       new uintptr_t[kBytecodeCount * kBytecodeCount]);
83   memset(bytecode_dispatch_counters_table_.get(), 0,
84          sizeof(uintptr_t) * kBytecodeCount * kBytecodeCount);
85 }
86 
87 namespace {
88 
BuiltinIndexFromBytecode(Bytecode bytecode,OperandScale operand_scale)89 Builtin BuiltinIndexFromBytecode(Bytecode bytecode,
90                                  OperandScale operand_scale) {
91   int index = static_cast<int>(bytecode);
92   if (operand_scale == OperandScale::kSingle) {
93     if (Bytecodes::IsShortStar(bytecode)) {
94       index = static_cast<int>(Bytecode::kFirstShortStar);
95     } else if (bytecode > Bytecode::kLastShortStar) {
96       // Adjust the index due to repeated handlers.
97       index -= Bytecodes::kShortStarCount - 1;
98     }
99   } else {
100     // The table contains uint8_t offsets starting at 0 with
101     // kIllegalBytecodeHandlerEncoding for illegal bytecode/scale combinations.
102     uint8_t offset = kWideBytecodeToBuiltinsMapping[index];
103     if (offset == kIllegalBytecodeHandlerEncoding) {
104       return Builtin::kIllegalHandler;
105     } else {
106       index = kNumberOfBytecodeHandlers + offset;
107       if (operand_scale == OperandScale::kQuadruple) {
108         index += kNumberOfWideBytecodeHandlers;
109       }
110     }
111   }
112   return Builtins::FromInt(static_cast<int>(Builtin::kFirstBytecodeHandler) +
113                            index);
114 }
115 
116 }  // namespace
117 
GetBytecodeHandler(Bytecode bytecode,OperandScale operand_scale)118 CodeT Interpreter::GetBytecodeHandler(Bytecode bytecode,
119                                       OperandScale operand_scale) {
120   Builtin builtin = BuiltinIndexFromBytecode(bytecode, operand_scale);
121   return isolate_->builtins()->code(builtin);
122 }
123 
SetBytecodeHandler(Bytecode bytecode,OperandScale operand_scale,CodeT handler)124 void Interpreter::SetBytecodeHandler(Bytecode bytecode,
125                                      OperandScale operand_scale,
126                                      CodeT handler) {
127   DCHECK(handler.is_off_heap_trampoline());
128   DCHECK(handler.kind() == CodeKind::BYTECODE_HANDLER);
129   size_t index = GetDispatchTableIndex(bytecode, operand_scale);
130   dispatch_table_[index] = handler.InstructionStart();
131 }
132 
133 // static
GetDispatchTableIndex(Bytecode bytecode,OperandScale operand_scale)134 size_t Interpreter::GetDispatchTableIndex(Bytecode bytecode,
135                                           OperandScale operand_scale) {
136   static const size_t kEntriesPerOperandScale = 1u << kBitsPerByte;
137   size_t index = static_cast<size_t>(bytecode);
138   return index + BytecodeOperands::OperandScaleAsIndex(operand_scale) *
139                      kEntriesPerOperandScale;
140 }
141 
142 namespace {
143 
MaybePrintAst(ParseInfo * parse_info,UnoptimizedCompilationInfo * compilation_info)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 
ShouldPrintBytecode(Handle<SharedFunctionInfo> shared)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     base::Vector<const char> filter =
165         base::CStrVector(FLAG_print_bytecode_filter);
166     return (filter.length() == 0) || (filter.length() == 1 && filter[0] == '*');
167   } else {
168     return shared->PassesFilter(FLAG_print_bytecode_filter);
169   }
170 }
171 
172 }  // namespace
173 
InterpreterCompilationJob(ParseInfo * parse_info,FunctionLiteral * literal,Handle<Script> script,AccountingAllocator * allocator,std::vector<FunctionLiteral * > * eager_inner_literals,LocalIsolate * local_isolate)174 InterpreterCompilationJob::InterpreterCompilationJob(
175     ParseInfo* parse_info, FunctionLiteral* literal, Handle<Script> script,
176     AccountingAllocator* allocator,
177     std::vector<FunctionLiteral*>* eager_inner_literals,
178     LocalIsolate* local_isolate)
179     : UnoptimizedCompilationJob(parse_info->stack_limit(), parse_info,
180                                 &compilation_info_),
181       zone_(allocator, ZONE_NAME),
182       compilation_info_(&zone_, parse_info, literal),
183       local_isolate_(local_isolate),
184       generator_(local_isolate, &zone_, &compilation_info_,
185                  parse_info->ast_string_constants(), eager_inner_literals,
186                  script) {}
187 
ExecuteJobImpl()188 InterpreterCompilationJob::Status InterpreterCompilationJob::ExecuteJobImpl() {
189   RCS_SCOPE(parse_info()->runtime_call_stats(),
190             RuntimeCallCounterId::kCompileIgnition,
191             RuntimeCallStats::kThreadSpecific);
192   // TODO(lpy): add support for background compilation RCS trace.
193   TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"), "V8.CompileIgnition");
194 
195   // Print AST if flag is enabled. Note, if compiling on a background thread
196   // then ASTs from different functions may be intersperse when printed.
197   {
198     DisallowGarbageCollection no_heap_access;
199     MaybePrintAst(parse_info(), compilation_info());
200   }
201 
202   ParkedScope parked_scope(local_isolate_);
203 
204   generator()->GenerateBytecode(stack_limit());
205 
206   if (generator()->HasStackOverflow()) {
207     return FAILED;
208   }
209   return SUCCEEDED;
210 }
211 
212 #ifdef DEBUG
213 template <typename IsolateT>
CheckAndPrintBytecodeMismatch(IsolateT * isolate,Handle<Script> script,Handle<BytecodeArray> bytecode)214 void InterpreterCompilationJob::CheckAndPrintBytecodeMismatch(
215     IsolateT* isolate, Handle<Script> script, Handle<BytecodeArray> bytecode) {
216   int first_mismatch = generator()->CheckBytecodeMatches(*bytecode);
217   if (first_mismatch >= 0) {
218     parse_info()->ast_value_factory()->Internalize(isolate);
219     DeclarationScope::AllocateScopeInfos(parse_info(), isolate);
220 
221     Handle<BytecodeArray> new_bytecode =
222         generator()->FinalizeBytecode(isolate, script);
223 
224     std::cerr << "Bytecode mismatch";
225 #ifdef OBJECT_PRINT
226     std::cerr << " found for function: ";
227     MaybeHandle<String> maybe_name = parse_info()->literal()->GetName(isolate);
228     Handle<String> name;
229     if (maybe_name.ToHandle(&name) && name->length() != 0) {
230       name->PrintUC16(std::cerr);
231     } else {
232       std::cerr << "anonymous";
233     }
234     Object script_name = script->GetNameOrSourceURL();
235     if (script_name.IsString()) {
236       std::cerr << " ";
237       String::cast(script_name).PrintUC16(std::cerr);
238       std::cerr << ":" << parse_info()->literal()->start_position();
239     }
240 #endif
241     std::cerr << "\nOriginal bytecode:\n";
242     bytecode->Disassemble(std::cerr);
243     std::cerr << "\nNew bytecode:\n";
244     new_bytecode->Disassemble(std::cerr);
245     FATAL("Bytecode mismatch at offset %d\n", first_mismatch);
246   }
247 }
248 #endif
249 
FinalizeJobImpl(Handle<SharedFunctionInfo> shared_info,Isolate * isolate)250 InterpreterCompilationJob::Status InterpreterCompilationJob::FinalizeJobImpl(
251     Handle<SharedFunctionInfo> shared_info, Isolate* isolate) {
252   RCS_SCOPE(parse_info()->runtime_call_stats(),
253             RuntimeCallCounterId::kCompileIgnitionFinalization);
254   TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"),
255                "V8.CompileIgnitionFinalization");
256   return DoFinalizeJobImpl(shared_info, isolate);
257 }
258 
FinalizeJobImpl(Handle<SharedFunctionInfo> shared_info,LocalIsolate * isolate)259 InterpreterCompilationJob::Status InterpreterCompilationJob::FinalizeJobImpl(
260     Handle<SharedFunctionInfo> shared_info, LocalIsolate* isolate) {
261   RCS_SCOPE(isolate, RuntimeCallCounterId::kCompileIgnitionFinalization,
262             RuntimeCallStats::kThreadSpecific);
263   TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"),
264                "V8.CompileIgnitionFinalization");
265   return DoFinalizeJobImpl(shared_info, isolate);
266 }
267 
268 template <typename IsolateT>
DoFinalizeJobImpl(Handle<SharedFunctionInfo> shared_info,IsolateT * isolate)269 InterpreterCompilationJob::Status InterpreterCompilationJob::DoFinalizeJobImpl(
270     Handle<SharedFunctionInfo> shared_info, IsolateT* isolate) {
271   Handle<BytecodeArray> bytecodes = compilation_info_.bytecode_array();
272   if (bytecodes.is_null()) {
273     bytecodes = generator()->FinalizeBytecode(
274         isolate, handle(Script::cast(shared_info->script()), isolate));
275     if (generator()->HasStackOverflow()) {
276       return FAILED;
277     }
278     compilation_info()->SetBytecodeArray(bytecodes);
279   }
280 
281   if (compilation_info()->SourcePositionRecordingMode() ==
282       SourcePositionTableBuilder::RecordingMode::RECORD_SOURCE_POSITIONS) {
283     Handle<ByteArray> source_position_table =
284         generator()->FinalizeSourcePositionTable(isolate);
285     bytecodes->set_source_position_table(*source_position_table, kReleaseStore);
286   }
287 
288   if (ShouldPrintBytecode(shared_info)) {
289     StdoutStream os;
290     std::unique_ptr<char[]> name =
291         compilation_info()->literal()->GetDebugName();
292     os << "[generated bytecode for function: " << name.get() << " ("
293        << shared_info << ")]" << std::endl;
294     os << "Bytecode length: " << bytecodes->length() << std::endl;
295     bytecodes->Disassemble(os);
296     os << std::flush;
297   }
298 
299 #ifdef DEBUG
300   CheckAndPrintBytecodeMismatch(
301       isolate, handle(Script::cast(shared_info->script()), isolate), bytecodes);
302 #endif
303 
304   return SUCCEEDED;
305 }
306 
NewCompilationJob(ParseInfo * parse_info,FunctionLiteral * literal,Handle<Script> script,AccountingAllocator * allocator,std::vector<FunctionLiteral * > * eager_inner_literals,LocalIsolate * local_isolate)307 std::unique_ptr<UnoptimizedCompilationJob> Interpreter::NewCompilationJob(
308     ParseInfo* parse_info, FunctionLiteral* literal, Handle<Script> script,
309     AccountingAllocator* allocator,
310     std::vector<FunctionLiteral*>* eager_inner_literals,
311     LocalIsolate* local_isolate) {
312   return std::make_unique<InterpreterCompilationJob>(
313       parse_info, literal, script, allocator, eager_inner_literals,
314       local_isolate);
315 }
316 
317 std::unique_ptr<UnoptimizedCompilationJob>
NewSourcePositionCollectionJob(ParseInfo * parse_info,FunctionLiteral * literal,Handle<BytecodeArray> existing_bytecode,AccountingAllocator * allocator,LocalIsolate * local_isolate)318 Interpreter::NewSourcePositionCollectionJob(
319     ParseInfo* parse_info, FunctionLiteral* literal,
320     Handle<BytecodeArray> existing_bytecode, AccountingAllocator* allocator,
321     LocalIsolate* local_isolate) {
322   auto job = std::make_unique<InterpreterCompilationJob>(
323       parse_info, literal, Handle<Script>(), allocator, nullptr, local_isolate);
324   job->compilation_info()->SetBytecodeArray(existing_bytecode);
325   return job;
326 }
327 
ForEachBytecode(const std::function<void (Bytecode,OperandScale)> & f)328 void Interpreter::ForEachBytecode(
329     const std::function<void(Bytecode, OperandScale)>& f) {
330   constexpr OperandScale kOperandScales[] = {
331 #define VALUE(Name, _) OperandScale::k##Name,
332       OPERAND_SCALE_LIST(VALUE)
333 #undef VALUE
334   };
335 
336   for (OperandScale operand_scale : kOperandScales) {
337     for (int i = 0; i < Bytecodes::kBytecodeCount; i++) {
338       f(Bytecodes::FromByte(i), operand_scale);
339     }
340   }
341 }
342 
Initialize()343 void Interpreter::Initialize() {
344   Builtins* builtins = isolate_->builtins();
345 
346   // Set the interpreter entry trampoline entry point now that builtins are
347   // initialized.
348   Handle<CodeT> code = BUILTIN_CODE(isolate_, InterpreterEntryTrampoline);
349   DCHECK(builtins->is_initialized());
350   DCHECK(code->is_off_heap_trampoline() ||
351          isolate_->heap()->IsImmovable(FromCodeT(*code)));
352   interpreter_entry_trampoline_instruction_start_ = code->InstructionStart();
353 
354   // Initialize the dispatch table.
355   ForEachBytecode([=](Bytecode bytecode, OperandScale operand_scale) {
356     Builtin builtin = BuiltinIndexFromBytecode(bytecode, operand_scale);
357     CodeT handler = builtins->code(builtin);
358     if (Bytecodes::BytecodeHasHandler(bytecode, operand_scale)) {
359 #ifdef DEBUG
360       std::string builtin_name(Builtins::name(builtin));
361       std::string expected_name =
362           (Bytecodes::IsShortStar(bytecode)
363                ? "ShortStar"
364                : Bytecodes::ToString(bytecode, operand_scale, "")) +
365           "Handler";
366       DCHECK_EQ(expected_name, builtin_name);
367 #endif
368     }
369 
370     SetBytecodeHandler(bytecode, operand_scale, handler);
371   });
372   DCHECK(IsDispatchTableInitialized());
373 }
374 
IsDispatchTableInitialized() const375 bool Interpreter::IsDispatchTableInitialized() const {
376   return dispatch_table_[0] != kNullAddress;
377 }
378 
LookupNameOfBytecodeHandler(const Code code)379 const char* Interpreter::LookupNameOfBytecodeHandler(const Code code) {
380   if (code.kind() == CodeKind::BYTECODE_HANDLER) {
381     return Builtins::name(code.builtin_id());
382   }
383   return nullptr;
384 }
385 
GetDispatchCounter(Bytecode from,Bytecode to) const386 uintptr_t Interpreter::GetDispatchCounter(Bytecode from, Bytecode to) const {
387   int from_index = Bytecodes::ToByte(from);
388   int to_index = Bytecodes::ToByte(to);
389   CHECK_WITH_MSG(bytecode_dispatch_counters_table_ != nullptr,
390                  "Dispatch counters require building with "
391                  "v8_enable_ignition_dispatch_counting");
392   return bytecode_dispatch_counters_table_[from_index * kNumberOfBytecodes +
393                                            to_index];
394 }
395 
GetDispatchCountersObject()396 Handle<JSObject> Interpreter::GetDispatchCountersObject() {
397   Handle<JSObject> counters_map =
398       isolate_->factory()->NewJSObjectWithNullProto();
399 
400   // Output is a JSON-encoded object of objects.
401   //
402   // The keys on the top level object are source bytecodes,
403   // and corresponding value are objects. Keys on these last are the
404   // destinations of the dispatch and the value associated is a counter for
405   // the correspondent source-destination dispatch chain.
406   //
407   // Only non-zero counters are written to file, but an entry in the top-level
408   // object is always present, even if the value is empty because all counters
409   // for that source are zero.
410 
411   for (int from_index = 0; from_index < kNumberOfBytecodes; ++from_index) {
412     Bytecode from_bytecode = Bytecodes::FromByte(from_index);
413     Handle<JSObject> counters_row =
414         isolate_->factory()->NewJSObjectWithNullProto();
415 
416     for (int to_index = 0; to_index < kNumberOfBytecodes; ++to_index) {
417       Bytecode to_bytecode = Bytecodes::FromByte(to_index);
418       uintptr_t counter = GetDispatchCounter(from_bytecode, to_bytecode);
419 
420       if (counter > 0) {
421         Handle<Object> value = isolate_->factory()->NewNumberFromSize(counter);
422         JSObject::AddProperty(isolate_, counters_row,
423                               Bytecodes::ToString(to_bytecode), value, NONE);
424       }
425     }
426 
427     JSObject::AddProperty(isolate_, counters_map,
428                           Bytecodes::ToString(from_bytecode), counters_row,
429                           NONE);
430   }
431 
432   return counters_map;
433 }
434 
435 }  // namespace interpreter
436 }  // namespace internal
437 }  // namespace v8
438