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