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 "src/ast/prettyprinter.h"
11 #include "src/bootstrapper.h"
12 #include "src/compiler.h"
13 #include "src/counters-inl.h"
14 #include "src/interpreter/bytecode-generator.h"
15 #include "src/interpreter/bytecodes.h"
16 #include "src/log.h"
17 #include "src/objects-inl.h"
18 #include "src/objects/shared-function-info.h"
19 #include "src/parsing/parse-info.h"
20 #include "src/setup-isolate.h"
21 #include "src/snapshot/snapshot.h"
22 #include "src/unoptimized-compilation-info.h"
23 #include "src/visitors.h"
24
25 namespace v8 {
26 namespace internal {
27 namespace interpreter {
28
29 class InterpreterCompilationJob final : public UnoptimizedCompilationJob {
30 public:
31 InterpreterCompilationJob(ParseInfo* parse_info, FunctionLiteral* literal,
32 AccountingAllocator* allocator,
33 ZoneVector<FunctionLiteral*>* eager_inner_literals);
34
35 protected:
36 Status ExecuteJobImpl() final;
37 Status FinalizeJobImpl(Handle<SharedFunctionInfo> shared_info,
38 Isolate* isolate) final;
39
40 private:
generator()41 BytecodeGenerator* generator() { return &generator_; }
42
43 Zone zone_;
44 UnoptimizedCompilationInfo compilation_info_;
45 BytecodeGenerator generator_;
46
47 DISALLOW_COPY_AND_ASSIGN(InterpreterCompilationJob);
48 };
49
Interpreter(Isolate * isolate)50 Interpreter::Interpreter(Isolate* isolate) : isolate_(isolate) {
51 memset(dispatch_table_, 0, sizeof(dispatch_table_));
52
53 if (FLAG_trace_ignition_dispatches) {
54 static const int kBytecodeCount = static_cast<int>(Bytecode::kLast) + 1;
55 bytecode_dispatch_counters_table_.reset(
56 new uintptr_t[kBytecodeCount * kBytecodeCount]);
57 memset(bytecode_dispatch_counters_table_.get(), 0,
58 sizeof(uintptr_t) * kBytecodeCount * kBytecodeCount);
59 }
60 }
61
GetAndMaybeDeserializeBytecodeHandler(Bytecode bytecode,OperandScale operand_scale)62 Code* Interpreter::GetAndMaybeDeserializeBytecodeHandler(
63 Bytecode bytecode, OperandScale operand_scale) {
64 Code* code = GetBytecodeHandler(bytecode, operand_scale);
65
66 // Already deserialized? Then just return the handler.
67 if (!isolate_->heap()->IsDeserializeLazyHandler(code)) return code;
68
69 DCHECK(FLAG_lazy_handler_deserialization);
70 DCHECK(Bytecodes::BytecodeHasHandler(bytecode, operand_scale));
71 code = Snapshot::DeserializeHandler(isolate_, bytecode, operand_scale);
72
73 DCHECK(code->IsCode());
74 DCHECK_EQ(code->kind(), Code::BYTECODE_HANDLER);
75 DCHECK(!isolate_->heap()->IsDeserializeLazyHandler(code));
76
77 SetBytecodeHandler(bytecode, operand_scale, code);
78
79 return code;
80 }
81
GetBytecodeHandler(Bytecode bytecode,OperandScale operand_scale)82 Code* Interpreter::GetBytecodeHandler(Bytecode bytecode,
83 OperandScale operand_scale) {
84 DCHECK(IsDispatchTableInitialized());
85 DCHECK(Bytecodes::BytecodeHasHandler(bytecode, operand_scale));
86 size_t index = GetDispatchTableIndex(bytecode, operand_scale);
87 Address code_entry = dispatch_table_[index];
88 return Code::GetCodeFromTargetAddress(code_entry);
89 }
90
SetBytecodeHandler(Bytecode bytecode,OperandScale operand_scale,Code * handler)91 void Interpreter::SetBytecodeHandler(Bytecode bytecode,
92 OperandScale operand_scale,
93 Code* handler) {
94 DCHECK(handler->kind() == Code::BYTECODE_HANDLER);
95 size_t index = GetDispatchTableIndex(bytecode, operand_scale);
96 dispatch_table_[index] = handler->entry();
97 }
98
99 // static
GetDispatchTableIndex(Bytecode bytecode,OperandScale operand_scale)100 size_t Interpreter::GetDispatchTableIndex(Bytecode bytecode,
101 OperandScale operand_scale) {
102 static const size_t kEntriesPerOperandScale = 1u << kBitsPerByte;
103 size_t index = static_cast<size_t>(bytecode);
104 switch (operand_scale) {
105 case OperandScale::kSingle:
106 return index;
107 case OperandScale::kDouble:
108 return index + kEntriesPerOperandScale;
109 case OperandScale::kQuadruple:
110 return index + 2 * kEntriesPerOperandScale;
111 }
112 UNREACHABLE();
113 }
114
IterateDispatchTable(RootVisitor * v)115 void Interpreter::IterateDispatchTable(RootVisitor* v) {
116 for (int i = 0; i < kDispatchTableSize; i++) {
117 Address code_entry = dispatch_table_[i];
118 Object* code = code_entry == kNullAddress
119 ? nullptr
120 : Code::GetCodeFromTargetAddress(code_entry);
121 Object* old_code = code;
122 v->VisitRootPointer(Root::kDispatchTable, nullptr, &code);
123 if (code != old_code) {
124 dispatch_table_[i] = reinterpret_cast<Code*>(code)->entry();
125 }
126 }
127 }
128
InterruptBudget()129 int Interpreter::InterruptBudget() {
130 return FLAG_interrupt_budget;
131 }
132
133 namespace {
134
MaybePrintAst(ParseInfo * parse_info,UnoptimizedCompilationInfo * compilation_info)135 void MaybePrintAst(ParseInfo* parse_info,
136 UnoptimizedCompilationInfo* compilation_info) {
137 if (!FLAG_print_ast) return;
138
139 OFStream os(stdout);
140 std::unique_ptr<char[]> name = compilation_info->literal()->GetDebugName();
141 os << "[generating bytecode for function: " << name.get() << "]" << std::endl;
142 #ifdef DEBUG
143 os << "--- AST ---" << std::endl
144 << AstPrinter(parse_info->stack_limit())
145 .PrintProgram(compilation_info->literal())
146 << std::endl;
147 #endif // DEBUG
148 }
149
ShouldPrintBytecode(Handle<SharedFunctionInfo> shared)150 bool ShouldPrintBytecode(Handle<SharedFunctionInfo> shared) {
151 if (!FLAG_print_bytecode) return false;
152
153 // Checks whether function passed the filter.
154 if (shared->is_toplevel()) {
155 Vector<const char> filter = CStrVector(FLAG_print_bytecode_filter);
156 return (filter.length() == 0) || (filter.length() == 1 && filter[0] == '*');
157 } else {
158 return shared->PassesFilter(FLAG_print_bytecode_filter);
159 }
160 }
161
162 } // namespace
163
InterpreterCompilationJob(ParseInfo * parse_info,FunctionLiteral * literal,AccountingAllocator * allocator,ZoneVector<FunctionLiteral * > * eager_inner_literals)164 InterpreterCompilationJob::InterpreterCompilationJob(
165 ParseInfo* parse_info, FunctionLiteral* literal,
166 AccountingAllocator* allocator,
167 ZoneVector<FunctionLiteral*>* eager_inner_literals)
168 : UnoptimizedCompilationJob(parse_info->stack_limit(), parse_info,
169 &compilation_info_),
170 zone_(allocator, ZONE_NAME),
171 compilation_info_(&zone_, parse_info, literal),
172 generator_(&compilation_info_, parse_info->ast_string_constants(),
173 eager_inner_literals) {}
174
ExecuteJobImpl()175 InterpreterCompilationJob::Status InterpreterCompilationJob::ExecuteJobImpl() {
176 RuntimeCallTimerScope runtimeTimerScope(
177 parse_info()->runtime_call_stats(),
178 parse_info()->on_background_thread()
179 ? RuntimeCallCounterId::kCompileBackgroundIgnition
180 : RuntimeCallCounterId::kCompileIgnition);
181 // TODO(lpy): add support for background compilation RCS trace.
182 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"), "V8.CompileIgnition");
183
184 // Print AST if flag is enabled. Note, if compiling on a background thread
185 // then ASTs from different functions may be intersperse when printed.
186 MaybePrintAst(parse_info(), compilation_info());
187
188 generator()->GenerateBytecode(stack_limit());
189
190 if (generator()->HasStackOverflow()) {
191 return FAILED;
192 }
193 return SUCCEEDED;
194 }
195
FinalizeJobImpl(Handle<SharedFunctionInfo> shared_info,Isolate * isolate)196 InterpreterCompilationJob::Status InterpreterCompilationJob::FinalizeJobImpl(
197 Handle<SharedFunctionInfo> shared_info, Isolate* isolate) {
198 RuntimeCallTimerScope runtimeTimerScope(
199 parse_info()->runtime_call_stats(),
200 RuntimeCallCounterId::kCompileIgnitionFinalization);
201 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"),
202 "V8.CompileIgnitionFinalization");
203
204 Handle<BytecodeArray> bytecodes =
205 generator()->FinalizeBytecode(isolate, parse_info()->script());
206 if (generator()->HasStackOverflow()) {
207 return FAILED;
208 }
209
210 if (ShouldPrintBytecode(shared_info)) {
211 OFStream os(stdout);
212 std::unique_ptr<char[]> name =
213 compilation_info()->literal()->GetDebugName();
214 os << "[generated bytecode for function: " << name.get() << "]"
215 << std::endl;
216 bytecodes->Disassemble(os);
217 os << std::flush;
218 }
219
220 compilation_info()->SetBytecodeArray(bytecodes);
221 return SUCCEEDED;
222 }
223
NewCompilationJob(ParseInfo * parse_info,FunctionLiteral * literal,AccountingAllocator * allocator,ZoneVector<FunctionLiteral * > * eager_inner_literals)224 UnoptimizedCompilationJob* Interpreter::NewCompilationJob(
225 ParseInfo* parse_info, FunctionLiteral* literal,
226 AccountingAllocator* allocator,
227 ZoneVector<FunctionLiteral*>* eager_inner_literals) {
228 return new InterpreterCompilationJob(parse_info, literal, allocator,
229 eager_inner_literals);
230 }
231
IsDispatchTableInitialized() const232 bool Interpreter::IsDispatchTableInitialized() const {
233 return dispatch_table_[0] != kNullAddress;
234 }
235
LookupNameOfBytecodeHandler(Code * code)236 const char* Interpreter::LookupNameOfBytecodeHandler(Code* code) {
237 #ifdef ENABLE_DISASSEMBLER
238 #define RETURN_NAME(Name, ...) \
239 if (dispatch_table_[Bytecodes::ToByte(Bytecode::k##Name)] == \
240 code->entry()) { \
241 return #Name; \
242 }
243 BYTECODE_LIST(RETURN_NAME)
244 #undef RETURN_NAME
245 #endif // ENABLE_DISASSEMBLER
246 return nullptr;
247 }
248
GetDispatchCounter(Bytecode from,Bytecode to) const249 uintptr_t Interpreter::GetDispatchCounter(Bytecode from, Bytecode to) const {
250 int from_index = Bytecodes::ToByte(from);
251 int to_index = Bytecodes::ToByte(to);
252 return bytecode_dispatch_counters_table_[from_index * kNumberOfBytecodes +
253 to_index];
254 }
255
GetDispatchCountersObject()256 Local<v8::Object> Interpreter::GetDispatchCountersObject() {
257 v8::Isolate* isolate = reinterpret_cast<v8::Isolate*>(isolate_);
258 Local<v8::Context> context = isolate->GetCurrentContext();
259
260 Local<v8::Object> counters_map = v8::Object::New(isolate);
261
262 // Output is a JSON-encoded object of objects.
263 //
264 // The keys on the top level object are source bytecodes,
265 // and corresponding value are objects. Keys on these last are the
266 // destinations of the dispatch and the value associated is a counter for
267 // the correspondent source-destination dispatch chain.
268 //
269 // Only non-zero counters are written to file, but an entry in the top-level
270 // object is always present, even if the value is empty because all counters
271 // for that source are zero.
272
273 for (int from_index = 0; from_index < kNumberOfBytecodes; ++from_index) {
274 Bytecode from_bytecode = Bytecodes::FromByte(from_index);
275 Local<v8::Object> counters_row = v8::Object::New(isolate);
276
277 for (int to_index = 0; to_index < kNumberOfBytecodes; ++to_index) {
278 Bytecode to_bytecode = Bytecodes::FromByte(to_index);
279 uintptr_t counter = GetDispatchCounter(from_bytecode, to_bytecode);
280
281 if (counter > 0) {
282 std::string to_name = Bytecodes::ToString(to_bytecode);
283 Local<v8::String> to_name_object =
284 v8::String::NewFromUtf8(isolate, to_name.c_str(),
285 NewStringType::kNormal)
286 .ToLocalChecked();
287 Local<v8::Number> counter_object = v8::Number::New(isolate, counter);
288 CHECK(counters_row
289 ->DefineOwnProperty(context, to_name_object, counter_object)
290 .IsJust());
291 }
292 }
293
294 std::string from_name = Bytecodes::ToString(from_bytecode);
295 Local<v8::String> from_name_object =
296 v8::String::NewFromUtf8(isolate, from_name.c_str(),
297 NewStringType::kNormal)
298 .ToLocalChecked();
299
300 CHECK(
301 counters_map->DefineOwnProperty(context, from_name_object, counters_row)
302 .IsJust());
303 }
304
305 return counters_map;
306 }
307
308 } // namespace interpreter
309 } // namespace internal
310 } // namespace v8
311