Line data Source code
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/objects-inl.h"
18 : #include "src/objects/shared-function-info.h"
19 : #include "src/objects/slots.h"
20 : #include "src/ostreams.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 :
31 4193379 : class InterpreterCompilationJob final : public UnoptimizedCompilationJob {
32 : public:
33 : InterpreterCompilationJob(
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 4169500 : 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 61532 : Interpreter::Interpreter(Isolate* isolate)
54 : : isolate_(isolate),
55 123064 : interpreter_entry_trampoline_instruction_start_(kNullAddress) {
56 61532 : memset(dispatch_table_, 0, sizeof(dispatch_table_));
57 :
58 61532 : if (FLAG_trace_ignition_dispatches) {
59 : static const int kBytecodeCount = static_cast<int>(Bytecode::kLast) + 1;
60 : bytecode_dispatch_counters_table_.reset(
61 5 : new uintptr_t[kBytecodeCount * kBytecodeCount]);
62 : memset(bytecode_dispatch_counters_table_.get(), 0,
63 : sizeof(uintptr_t) * kBytecodeCount * kBytecodeCount);
64 : }
65 61532 : }
66 :
67 : namespace {
68 :
69 : int BuiltinIndexFromBytecode(Bytecode bytecode, OperandScale operand_scale) {
70 66682 : int index = BytecodeOperands::OperandScaleAsIndex(operand_scale) *
71 66682 : kNumberOfBytecodeHandlers +
72 66682 : static_cast<int>(bytecode);
73 66682 : int offset = kBytecodeToBuiltinsMapping[index];
74 : return offset >= 0 ? Builtins::kFirstBytecodeHandler + offset
75 66682 : : Builtins::kIllegalHandler;
76 : }
77 :
78 : } // namespace
79 :
80 66682 : Code Interpreter::GetBytecodeHandler(Bytecode bytecode,
81 : OperandScale operand_scale) {
82 : int builtin_index = BuiltinIndexFromBytecode(bytecode, operand_scale);
83 66682 : Builtins* builtins = isolate_->builtins();
84 66682 : return builtins->builtin(builtin_index);
85 : }
86 :
87 33228143 : 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 33228102 : dispatch_table_[index] = handler->InstructionStart();
92 33228102 : }
93 :
94 : // static
95 0 : size_t Interpreter::GetDispatchTableIndex(Bytecode bytecode,
96 : OperandScale operand_scale) {
97 : static const size_t kEntriesPerOperandScale = 1u << kBitsPerByte;
98 33228143 : size_t index = static_cast<size_t>(bytecode);
99 33228143 : return index + BytecodeOperands::OperandScaleAsIndex(operand_scale) *
100 33228135 : kEntriesPerOperandScale;
101 : }
102 :
103 194176 : void Interpreter::IterateDispatchTable(RootVisitor* v) {
104 387305 : if (FLAG_embedded_builtins && !isolate_->serializer_enabled() &&
105 193129 : 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 1609239 : for (int i = 0; i < kDispatchTableSize; i++) {
120 804096 : Address code_entry = dispatch_table_[i];
121 : // Skip over off-heap bytecode handlers since they will never move.
122 1369476 : if (InstructionStream::PcIsOffHeap(isolate_, code_entry)) continue;
123 :
124 : // TODO(jkummerow): Would it hurt to simply do:
125 : // if (code_entry == kNullAddress) continue;
126 238716 : Code code;
127 238716 : if (code_entry != kNullAddress) {
128 0 : code = Code::GetCodeFromTargetAddress(code_entry);
129 : }
130 238716 : Code old_code = code;
131 477432 : v->VisitRootPointer(Root::kDispatchTable, nullptr, FullObjectSlot(&code));
132 238716 : if (code != old_code) {
133 0 : dispatch_table_[i] = code->entry();
134 : }
135 : }
136 : }
137 :
138 2093559 : int Interpreter::InterruptBudget() {
139 2093559 : 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 2072819 : bool ShouldPrintBytecode(Handle<SharedFunctionInfo> shared) {
160 2072819 : if (!FLAG_print_bytecode) return false;
161 :
162 : // Checks whether function passed the filter.
163 0 : if (shared->is_toplevel()) {
164 0 : Vector<const char> filter = CStrVector(FLAG_print_bytecode_filter);
165 0 : return (filter.length() == 0) || (filter.length() == 1 && filter[0] == '*');
166 : } else {
167 0 : return shared->PassesFilter(FLAG_print_bytecode_filter);
168 : }
169 : }
170 :
171 : } // namespace
172 :
173 2096690 : 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 4193380 : eager_inner_literals) {}
183 :
184 2096685 : InterpreterCompilationJob::Status InterpreterCompilationJob::ExecuteJobImpl() {
185 : RuntimeCallTimerScope runtimeTimerScope(
186 : parse_info()->runtime_call_stats(),
187 : parse_info()->on_background_thread()
188 : ? RuntimeCallCounterId::kCompileBackgroundIgnition
189 2096685 : : RuntimeCallCounterId::kCompileIgnition);
190 : // TODO(lpy): add support for background compilation RCS trace.
191 6290002 : 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 2096685 : generator()->GenerateBytecode(stack_limit());
198 :
199 2096635 : if (generator()->HasStackOverflow()) {
200 : return FAILED;
201 : }
202 2096628 : return SUCCEEDED;
203 : }
204 :
205 2072818 : InterpreterCompilationJob::Status InterpreterCompilationJob::FinalizeJobImpl(
206 : Handle<SharedFunctionInfo> shared_info, Isolate* isolate) {
207 : RuntimeCallTimerScope runtimeTimerScope(
208 : parse_info()->runtime_call_stats(),
209 2072818 : RuntimeCallCounterId::kCompileIgnitionFinalization);
210 6218446 : TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"),
211 : "V8.CompileIgnitionFinalization");
212 :
213 : Handle<BytecodeArray> bytecodes =
214 2072815 : generator()->FinalizeBytecode(isolate, parse_info()->script());
215 2072819 : if (generator()->HasStackOverflow()) {
216 : return FAILED;
217 : }
218 :
219 2072820 : if (ShouldPrintBytecode(shared_info)) {
220 0 : StdoutStream os;
221 : std::unique_ptr<char[]> name =
222 0 : compilation_info()->literal()->GetDebugName();
223 0 : os << "[generated bytecode for function: " << name.get() << "]"
224 : << std::endl;
225 0 : bytecodes->Disassemble(os);
226 : os << std::flush;
227 : }
228 :
229 : compilation_info()->SetBytecodeArray(bytecodes);
230 2072818 : return SUCCEEDED;
231 : }
232 :
233 2096674 : 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 2096674 : eager_inner_literals);
239 : }
240 :
241 61540 : 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 61540 : };
248 :
249 430744 : for (OperandScale operand_scale : kOperandScales) {
250 66640786 : for (int i = 0; i < Bytecodes::kBytecodeCount; i++) {
251 : f(Bytecodes::FromByte(i), operand_scale);
252 : }
253 : }
254 61534 : }
255 :
256 61534 : void Interpreter::Initialize() {
257 61534 : Builtins* builtins = isolate_->builtins();
258 :
259 : // Set the interpreter entry trampoline entry point now that builtins are
260 : // initialized.
261 61534 : 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 123068 : interpreter_entry_trampoline_instruction_start_ = code->InstructionStart();
266 :
267 : // Initialize the dispatch table.
268 61534 : Code illegal = builtins->builtin(Builtins::kIllegalHandler);
269 61534 : int builtin_id = Builtins::kFirstBytecodeHandler;
270 123068 : ForEachBytecode([=, &builtin_id](Bytecode bytecode,
271 62395053 : OperandScale operand_scale) {
272 33228114 : Code handler = illegal;
273 33228114 : 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 29166939 : handler = builtins->builtin(builtin_id++);
281 : }
282 33228119 : SetBytecodeHandler(bytecode, operand_scale, handler);
283 33289616 : });
284 : DCHECK(builtin_id == Builtins::builtin_count);
285 : DCHECK(IsDispatchTableInitialized());
286 61534 : }
287 :
288 0 : bool Interpreter::IsDispatchTableInitialized() const {
289 0 : return dispatch_table_[0] != kNullAddress;
290 : }
291 :
292 0 : 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 0 : return nullptr;
303 : }
304 :
305 0 : uintptr_t Interpreter::GetDispatchCounter(Bytecode from, Bytecode to) const {
306 0 : int from_index = Bytecodes::ToByte(from);
307 0 : int to_index = Bytecodes::ToByte(to);
308 583200 : return bytecode_dispatch_counters_table_[from_index * kNumberOfBytecodes +
309 1166400 : to_index];
310 : }
311 :
312 18 : Local<v8::Object> Interpreter::GetDispatchCountersObject() {
313 18 : v8::Isolate* isolate = reinterpret_cast<v8::Isolate*>(isolate_);
314 18 : Local<v8::Context> context = isolate->GetCurrentContext();
315 :
316 18 : 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 6498 : for (int from_index = 0; from_index < kNumberOfBytecodes; ++from_index) {
330 : Bytecode from_bytecode = Bytecodes::FromByte(from_index);
331 3240 : Local<v8::Object> counters_row = v8::Object::New(isolate);
332 :
333 1169640 : 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 583200 : if (counter > 0) {
338 0 : std::string to_name = Bytecodes::ToString(to_bytecode);
339 : Local<v8::String> to_name_object =
340 0 : v8::String::NewFromUtf8(isolate, to_name.c_str(),
341 : NewStringType::kNormal)
342 : .ToLocalChecked();
343 0 : Local<v8::Number> counter_object = v8::Number::New(isolate, counter);
344 0 : CHECK(counters_row
345 : ->DefineOwnProperty(context, to_name_object, counter_object)
346 : .IsJust());
347 : }
348 : }
349 :
350 3240 : std::string from_name = Bytecodes::ToString(from_bytecode);
351 : Local<v8::String> from_name_object =
352 3240 : v8::String::NewFromUtf8(isolate, from_name.c_str(),
353 : NewStringType::kNormal)
354 : .ToLocalChecked();
355 :
356 6480 : CHECK(
357 : counters_map->DefineOwnProperty(context, from_name_object, counters_row)
358 : .IsJust());
359 : }
360 :
361 18 : return counters_map;
362 : }
363 :
364 : } // namespace interpreter
365 : } // namespace internal
366 120216 : } // namespace v8
|