v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
interpreter.cc
Go to the documentation of this file.
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
6
7#include <fstream>
8#include <memory>
9
10#include "builtins-generated/bytecodes-builtins-list.h"
12#include "src/ast/scopes.h"
15#include "src/common/globals.h"
25#include "src/utils/ostreams.h"
26
27namespace v8 {
28namespace internal {
29namespace interpreter {
30
32 public:
34 Handle<Script> script,
35 AccountingAllocator* allocator,
36 std::vector<FunctionLiteral*>* eager_inner_literals,
37 LocalIsolate* local_isolate);
40 delete;
41
42 protected:
43 Status ExecuteJobImpl() final;
45 Isolate* isolate) final;
47 LocalIsolate* isolate) final;
48
49 private:
50 BytecodeGenerator* generator() { return &generator_; }
51 template <typename IsolateT>
52 void CheckAndPrintBytecodeMismatch(IsolateT* isolate, Handle<Script> script,
54
55 template <typename IsolateT>
57 IsolateT* isolate);
58
63};
64
66 : isolate_(isolate),
67 interpreter_entry_trampoline_instruction_start_(kNullAddress) {
68 memset(dispatch_table_, 0, sizeof(dispatch_table_));
69
72 }
73}
74
76 static const int kBytecodeCount = static_cast<int>(Bytecode::kLast) + 1;
78 new uintptr_t[kBytecodeCount * kBytecodeCount]);
80 sizeof(uintptr_t) * kBytecodeCount * kBytecodeCount);
81}
82
83namespace {
84
85Builtin BuiltinIndexFromBytecode(Bytecode bytecode,
86 OperandScale operand_scale) {
87 int index = static_cast<int>(bytecode);
88 if (operand_scale == OperandScale::kSingle) {
89 if (Bytecodes::IsShortStar(bytecode)) {
90 index = static_cast<int>(Bytecode::kFirstShortStar);
91 } else if (bytecode > Bytecode::kLastShortStar) {
92 // Adjust the index due to repeated handlers.
93 index -= Bytecodes::kShortStarCount - 1;
94 }
95 } else {
96 // The table contains uint8_t offsets starting at 0 with
97 // kIllegalBytecodeHandlerEncoding for illegal bytecode/scale combinations.
98 uint8_t offset = kWideBytecodeToBuiltinsMapping[index];
100 return Builtin::kIllegalHandler;
101 } else {
102 index = kNumberOfBytecodeHandlers + offset;
103 if (operand_scale == OperandScale::kQuadruple) {
104 index += kNumberOfWideBytecodeHandlers;
105 }
106 }
107 }
108 return Builtins::FromInt(static_cast<int>(Builtin::kFirstBytecodeHandler) +
109 index);
110}
111
112} // namespace
113
115 OperandScale operand_scale) {
116 Builtin builtin = BuiltinIndexFromBytecode(bytecode, operand_scale);
117 return isolate_->builtins()->code(builtin);
118}
119
121 OperandScale operand_scale,
122 Tagged<Code> handler) {
123 DCHECK(!handler->has_instruction_stream());
124 DCHECK(handler->kind() == CodeKind::BYTECODE_HANDLER);
125 size_t index = GetDispatchTableIndex(bytecode, operand_scale);
126 dispatch_table_[index] = handler->instruction_start();
127}
128
129// static
131 OperandScale operand_scale) {
132 static const size_t kEntriesPerOperandScale = 1u << kBitsPerByte;
133 size_t index = static_cast<size_t>(bytecode);
134 return index + BytecodeOperands::OperandScaleAsIndex(operand_scale) *
135 kEntriesPerOperandScale;
136}
137
138namespace {
139
140void MaybePrintAst(ParseInfo* parse_info,
141 UnoptimizedCompilationInfo* compilation_info) {
142 if (!v8_flags.print_ast) return;
143
144 StdoutStream os;
145 std::unique_ptr<char[]> name = compilation_info->literal()->GetDebugName();
146 os << "[generating bytecode for function: " << name.get() << "]" << std::endl;
147#ifdef DEBUG
148 os << "--- AST ---" << std::endl
149 << AstPrinter(parse_info->stack_limit())
150 .PrintProgram(compilation_info->literal())
151 << std::endl;
152#endif // DEBUG
153}
154
155bool ShouldPrintBytecode(DirectHandle<SharedFunctionInfo> shared) {
156 if (!v8_flags.print_bytecode) return false;
157
158 // Checks whether function passed the filter.
159 if (shared->is_toplevel()) {
161 base::CStrVector(v8_flags.print_bytecode_filter);
162 return filter.empty() || (filter.length() == 1 && filter[0] == '*');
163 } else {
164 return shared->PassesFilter(v8_flags.print_bytecode_filter);
165 }
166}
167
168} // namespace
169
171 ParseInfo* parse_info, FunctionLiteral* literal, Handle<Script> script,
172 AccountingAllocator* allocator,
173 std::vector<FunctionLiteral*>* eager_inner_literals,
174 LocalIsolate* local_isolate)
175 : UnoptimizedCompilationJob(parse_info->stack_limit(), parse_info,
176 &compilation_info_),
177 zone_(allocator, ZONE_NAME),
178 compilation_info_(&zone_, parse_info, literal),
179 local_isolate_(local_isolate),
180 generator_(local_isolate, &zone_, &compilation_info_,
181 parse_info->ast_string_constants(), eager_inner_literals,
182 script) {}
183
185 RCS_SCOPE(parse_info()->runtime_call_stats(),
186 RuntimeCallCounterId::kCompileIgnition,
187 RuntimeCallStats::kThreadSpecific);
188 // TODO(lpy): add support for background compilation RCS trace.
189 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"), "V8.CompileIgnition");
190
191 // Print AST if flag is enabled. Note, if compiling on a background thread
192 // then ASTs from different functions may be intersperse when printed.
193 {
194 DisallowGarbageCollection no_heap_access;
195 MaybePrintAst(parse_info(), compilation_info());
196 }
197
199 [this]() { generator()->GenerateBytecode(stack_limit()); });
200
201 if (generator()->HasStackOverflow()) {
202 return FAILED;
203 }
204 return SUCCEEDED;
205}
206
207#ifdef DEBUG
208template <typename IsolateT>
210 IsolateT* isolate, Handle<Script> script,
212 int first_mismatch = generator()->CheckBytecodeMatches(*bytecode);
213 if (first_mismatch >= 0) {
216
217 DirectHandle<BytecodeArray> new_bytecode =
218 generator()->FinalizeBytecode(isolate, script);
219
220 std::cerr << "Bytecode mismatch";
221#ifdef OBJECT_PRINT
222 std::cerr << " found for function: ";
223 MaybeDirectHandle<String> maybe_name =
224 parse_info()->literal()->GetName(isolate);
226 if (maybe_name.ToHandle(&name) && name->length() != 0) {
227 name->PrintUC16(std::cerr);
228 } else {
229 std::cerr << "anonymous";
230 }
231 Tagged<Object> script_name = script->GetNameOrSourceURL();
232 if (IsString(script_name)) {
233 std::cerr << " ";
234 Cast<String>(script_name)->PrintUC16(std::cerr);
235 std::cerr << ":" << parse_info()->literal()->start_position();
236 }
237#endif
238 std::cerr << "\nOriginal bytecode:\n";
239 bytecode->Disassemble(std::cerr);
240 std::cerr << "\nNew bytecode:\n";
241 new_bytecode->Disassemble(std::cerr);
242 FATAL("Bytecode mismatch at offset %d\n", first_mismatch);
243 }
244}
245#endif
246
248 DirectHandle<SharedFunctionInfo> shared_info, Isolate* isolate) {
249 RCS_SCOPE(parse_info()->runtime_call_stats(),
250 RuntimeCallCounterId::kCompileIgnitionFinalization);
252 "V8.CompileIgnitionFinalization");
253 return DoFinalizeJobImpl(shared_info, isolate);
254}
255
257 DirectHandle<SharedFunctionInfo> shared_info, LocalIsolate* isolate) {
258 RCS_SCOPE(isolate, RuntimeCallCounterId::kCompileIgnitionFinalization,
259 RuntimeCallStats::kThreadSpecific);
261 "V8.CompileIgnitionFinalization");
262 return DoFinalizeJobImpl(shared_info, isolate);
263}
264
265template <typename IsolateT>
267 DirectHandle<SharedFunctionInfo> shared_info, IsolateT* isolate) {
269 if (bytecodes.is_null()) {
270 bytecodes = generator()->FinalizeBytecode(
271 isolate, handle(Cast<Script>(shared_info->script()), isolate));
272 if (generator()->HasStackOverflow()) {
273 return FAILED;
274 }
276 }
277
278 if (compilation_info()->SourcePositionRecordingMode() ==
280 DirectHandle<TrustedByteArray> source_position_table =
282 bytecodes->set_source_position_table(*source_position_table, kReleaseStore);
283 }
284
285 if (ShouldPrintBytecode(shared_info)) {
286 StdoutStream os;
287 std::unique_ptr<char[]> name =
289 os << "[generated bytecode for function: " << name.get() << " ("
290 << shared_info << ")]" << std::endl;
291 os << "Bytecode length: " << bytecodes->length() << std::endl;
292 bytecodes->Disassemble(os);
293 os << std::flush;
294 }
295
296#ifdef DEBUG
297 if (parse_info()->literal()->shared_function_info().is_null()) {
299 indirect_handle(shared_info, isolate));
300 }
302 isolate, handle(Cast<Script>(shared_info->script()), isolate), bytecodes);
303#endif
304
305 return SUCCEEDED;
306}
307
308std::unique_ptr<UnoptimizedCompilationJob> Interpreter::NewCompilationJob(
309 ParseInfo* parse_info, FunctionLiteral* literal, Handle<Script> script,
310 AccountingAllocator* allocator,
311 std::vector<FunctionLiteral*>* eager_inner_literals,
312 LocalIsolate* local_isolate) {
313 return std::make_unique<InterpreterCompilationJob>(
314 parse_info, literal, script, allocator, eager_inner_literals,
315 local_isolate);
316}
317
318std::unique_ptr<UnoptimizedCompilationJob>
320 ParseInfo* parse_info, FunctionLiteral* literal,
321 Handle<BytecodeArray> existing_bytecode, AccountingAllocator* allocator,
322 LocalIsolate* local_isolate) {
323 auto job = std::make_unique<InterpreterCompilationJob>(
324 parse_info, literal, Handle<Script>(), allocator, nullptr, local_isolate);
325 job->compilation_info()->SetBytecodeArray(existing_bytecode);
326 return job;
327}
328
330 const std::function<void(Bytecode, OperandScale)>& f) {
331 constexpr OperandScale kOperandScales[] = {
332#define VALUE(Name, _) OperandScale::k##Name,
334#undef VALUE
335 };
336
337 for (OperandScale operand_scale : kOperandScales) {
338 for (int i = 0; i < Bytecodes::kBytecodeCount; i++) {
339 f(Bytecodes::FromByte(i), operand_scale);
340 }
341 }
342}
343
345 Builtins* builtins = isolate_->builtins();
346
347 // Set the interpreter entry trampoline entry point now that builtins are
348 // initialized.
349 DirectHandle<Code> code = BUILTIN_CODE(isolate_, InterpreterEntryTrampoline);
350 DCHECK(builtins->is_initialized());
351 DCHECK(!code->has_instruction_stream());
352 interpreter_entry_trampoline_instruction_start_ = code->instruction_start();
353
354 // Initialize the dispatch table.
355 ForEachBytecode([=, this](Bytecode bytecode, OperandScale operand_scale) {
356 Builtin builtin = BuiltinIndexFromBytecode(bytecode, operand_scale);
357 Tagged<Code> 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 });
373}
374
378
380 int from_index = Bytecodes::ToByte(from);
381 int to_index = Bytecodes::ToByte(to);
383 "Dispatch counters require building with "
384 "v8_enable_ignition_dispatch_counting");
386 to_index];
387}
388
390 DirectHandle<JSObject> counters_map =
392
393 // Output is a JSON-encoded object of objects.
394 //
395 // The keys on the top level object are source bytecodes,
396 // and corresponding value are objects. Keys on these last are the
397 // destinations of the dispatch and the value associated is a counter for
398 // the correspondent source-destination dispatch chain.
399 //
400 // Only non-zero counters are written to file, but an entry in the top-level
401 // object is always present, even if the value is empty because all counters
402 // for that source are zero.
403
404 for (int from_index = 0; from_index < kNumberOfBytecodes; ++from_index) {
405 Bytecode from_bytecode = Bytecodes::FromByte(from_index);
406 DirectHandle<JSObject> counters_row =
408
409 for (int to_index = 0; to_index < kNumberOfBytecodes; ++to_index) {
410 Bytecode to_bytecode = Bytecodes::FromByte(to_index);
411 uintptr_t counter = GetDispatchCounter(from_bytecode, to_bytecode);
412
413 if (counter > 0) {
416 JSObject::AddProperty(isolate_, counters_row,
417 Bytecodes::ToString(to_bytecode), value, NONE);
418 }
419 }
420
421 JSObject::AddProperty(isolate_, counters_map,
422 Bytecodes::ToString(from_bytecode), counters_row,
423 NONE);
424 }
425
426 return counters_map;
427}
428
429} // namespace interpreter
430} // namespace internal
431} // namespace v8
Isolate * isolate_
interpreter::Bytecode bytecode
Definition builtins.cc:43
#define BUILTIN_CODE(isolate, name)
Definition builtins.h:45
#define OPERAND_SCALE_LIST(V)
void Internalize(IsolateT *isolate)
V8_EXPORT_PRIVATE Tagged< Code > code(Builtin builtin)
Definition builtins.cc:149
static constexpr Builtin FromInt(int id)
Definition builtins.h:140
static V8_EXPORT_PRIVATE const char * name(Builtin builtin)
Definition builtins.cc:226
static V8_EXPORT_PRIVATE void AllocateScopeInfos(ParseInfo *info, DirectHandle< Script > script, IsolateT *isolate)
DirectHandle< Number > NewNumberFromSize(size_t value)
Handle< JSObject > NewJSObjectWithNullProto()
Definition factory.cc:3004
MaybeHandle< String > GetName(IsolateT *isolate) const
Definition ast.h:2309
int start_position() const
Definition ast.cc:221
std::unique_ptr< char[]> GetDebugName() const
Definition ast.cc:233
void set_shared_function_info(Handle< SharedFunctionInfo > shared_function_info)
Definition ast.cc:202
Builtins * builtins()
Definition isolate.h:1443
v8::internal::Factory * factory()
Definition isolate.h:1527
static V8_EXPORT_PRIVATE void AddProperty(Isolate *isolate, DirectHandle< JSObject > object, DirectHandle< Name > name, DirectHandle< Object > value, PropertyAttributes attributes)
V8_INLINE void ParkIfOnBackgroundAndExecute(Callback callback)
V8_WARN_UNUSED_RESULT V8_INLINE bool ToHandle(DirectHandle< S > *out) const
FunctionLiteral * literal() const
Definition parse-info.h:319
uintptr_t stack_limit() const
Definition parse-info.h:273
AstValueFactory * ast_value_factory() const
Definition parse-info.h:310
void SetBytecodeArray(Handle< BytecodeArray > bytecode_array)
UnoptimizedCompilationInfo * compilation_info() const
Definition compiler.h:364
DirectHandle< TrustedByteArray > FinalizeSourcePositionTable(IsolateT *isolate)
Handle< BytecodeArray > FinalizeBytecode(IsolateT *isolate, Handle< Script > script)
static constexpr int OperandScaleAsIndex(OperandScale operand_scale)
static Bytecode FromByte(uint8_t value)
Definition bytecodes.h:624
static uint8_t ToByte(Bytecode bytecode)
Definition bytecodes.h:618
static constexpr bool IsShortStar(Bytecode bytecode)
Definition bytecodes.h:732
static const char * ToString(Bytecode bytecode)
Definition bytecodes.cc:123
static bool BytecodeHasHandler(Bytecode bytecode, OperandScale operand_scale)
Definition bytecodes.cc:333
InterpreterCompilationJob & operator=(const InterpreterCompilationJob &)=delete
Status FinalizeJobImpl(DirectHandle< SharedFunctionInfo > shared_info, Isolate *isolate) final
InterpreterCompilationJob(ParseInfo *parse_info, FunctionLiteral *literal, Handle< Script > script, AccountingAllocator *allocator, std::vector< FunctionLiteral * > *eager_inner_literals, LocalIsolate *local_isolate)
InterpreterCompilationJob(const InterpreterCompilationJob &)=delete
Status DoFinalizeJobImpl(DirectHandle< SharedFunctionInfo > shared_info, IsolateT *isolate)
void CheckAndPrintBytecodeMismatch(IsolateT *isolate, Handle< Script > script, DirectHandle< BytecodeArray > bytecode)
static std::unique_ptr< UnoptimizedCompilationJob > NewSourcePositionCollectionJob(ParseInfo *parse_info, FunctionLiteral *literal, Handle< BytecodeArray > existing_bytecode, AccountingAllocator *allocator, LocalIsolate *local_isolate)
V8_EXPORT_PRIVATE void InitDispatchCounters()
void ForEachBytecode(const std::function< void(Bytecode, OperandScale)> &f)
void SetBytecodeHandler(Bytecode bytecode, OperandScale operand_scale, Tagged< Code > handler)
static size_t GetDispatchTableIndex(Bytecode bytecode, OperandScale operand_scale)
V8_EXPORT_PRIVATE DirectHandle< JSObject > GetDispatchCountersObject()
Address dispatch_table_[kDispatchTableSize]
static std::unique_ptr< UnoptimizedCompilationJob > NewCompilationJob(ParseInfo *parse_info, FunctionLiteral *literal, Handle< Script > script, AccountingAllocator *allocator, std::vector< FunctionLiteral * > *eager_inner_literals, LocalIsolate *local_isolate)
std::unique_ptr< uintptr_t[]> bytecode_dispatch_counters_table_
V8_EXPORT_PRIVATE Tagged< Code > GetBytecodeHandler(Bytecode bytecode, OperandScale operand_scale)
V8_EXPORT_PRIVATE uintptr_t GetDispatchCounter(Bytecode from, Bytecode to) const
Zone * zone_
int32_t offset
#define VALUE(Name, _)
#define V8_IGNITION_DISPATCH_COUNTING_BOOL
std::unique_ptr< icu::DateTimePatternGenerator > generator_
FunctionLiteral * literal
Definition liveedit.cc:294
LocalIsolate * local_isolate_
Vector< const char > CStrVector(const char *data)
Definition vector.h:331
V8_INLINE IndirectHandle< T > handle(Tagged< T > object, Isolate *isolate)
Definition handles-inl.h:72
constexpr int kBitsPerByte
Definition globals.h:682
V8_INLINE IndirectHandle< T > indirect_handle(DirectHandle< T > handle)
Definition handles.h:757
too high values may cause the compiler to set high thresholds for inlining to as much as possible avoid inlined allocation of objects that cannot escape trace load stores from virtual maglev objects use TurboFan fast string builder analyze liveness of environment slots and zap dead values trace TurboFan load elimination emit data about basic block usage in builtins to this enable builtin reordering when run mksnapshot flag for emit warnings when applying builtin profile data verify register allocation in TurboFan randomly schedule instructions to stress dependency tracking enable store store elimination in TurboFan rewrite far to near simulate GC compiler thread race related to allow float parameters to be passed in simulator mode JS Wasm Run additional turbo_optimize_inlined_js_wasm_wrappers enable experimental feedback collection in generic lowering enable Turboshaft s WasmLoadElimination enable Turboshaft s low level load elimination for JS enable Turboshaft s escape analysis for string concatenation use enable Turbolev features that we want to ship in the not too far future trace individual Turboshaft reduction steps trace intermediate Turboshaft reduction steps invocation count threshold for early optimization Enables optimizations which favor memory size over execution speed Enables sampling allocation profiler with X as a sample interval min size of a semi the new space consists of two semi spaces max size of the Collect garbage after Collect garbage after keeps maps alive for< n > old space garbage collections print one detailed trace line in name
Definition flags.cc:2086
V8_EXPORT_PRIVATE FlagValues v8_flags
static constexpr Address kNullAddress
Definition v8-internal.h:53
Tagged< To > Cast(Tagged< From > value, const v8::SourceLocation &loc=INIT_SOURCE_LOCATION_IN_DEBUG)
Definition casting.h:150
static constexpr ReleaseStoreTag kReleaseStore
Definition globals.h:2910
#define RCS_SCOPE(...)
#define FATAL(...)
Definition logging.h:47
#define CHECK_WITH_MSG(condition, message)
Definition logging.h:118
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_EQ(v1, v2)
Definition logging.h:485
#define TRACE_EVENT0(category_group, name)
#define TRACE_DISABLED_BY_DEFAULT(name)
#define ZONE_NAME
Definition zone.h:22