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