Commit 1e3257d2 authored by ssanfilippo's avatar ssanfilippo Committed by Commit bot

[Interpreter] Enable tracing of bytecode handler dispatches.

When FLAG_trace_ignition_dispatches is enabled, a dispatch counter is
kept for each pair of source-destination bytecode handlers.

Each counter saturates at max uintptr_t value.

Counters are dumped as a JSON-encoded object of objects, such that
each key on the top level object is a source bytecode name, and each key
on the corresponding value is a destination bytecode name, with the
associated counter as value. The output file name can be controlled
with the FLAG_trace_ignition_dispatches_output_file flag.

The JSON file may be written by calling
Interpreter::WriteDispatchCounters(), which is done for d8 in
Shell::OnExit, if FLAG_trace_ignition_dispatches is enabled.

BUG=v8:4899
LOG=N

Review URL: https://codereview.chromium.org/1828633003

Cr-Commit-Position: refs/heads/master@{#35380}
parent 2938ef4e
......@@ -1072,6 +1072,12 @@ ExternalReference ExternalReference::interpreter_dispatch_table_address(
return ExternalReference(isolate->interpreter()->dispatch_table_address());
}
ExternalReference ExternalReference::interpreter_dispatch_counters(
Isolate* isolate) {
return ExternalReference(
isolate->interpreter()->bytecode_dispatch_count_table());
}
ExternalReference::ExternalReference(StatsCounter* counter)
: address_(reinterpret_cast<Address>(counter->GetInternalPointer())) {}
......
......@@ -909,6 +909,7 @@ class ExternalReference BASE_EMBEDDED {
// ExternalReferenceTable in serialize.cc manually.
static ExternalReference interpreter_dispatch_table_address(Isolate* isolate);
static ExternalReference interpreter_dispatch_counters(Isolate* isolate);
static ExternalReference incremental_marking_record_write_function(
Isolate* isolate);
......
......@@ -41,6 +41,7 @@
#include "src/base/platform/platform.h"
#include "src/base/sys-info.h"
#include "src/basic-block-profiler.h"
#include "src/interpreter/interpreter.h"
#include "src/snapshot/natives.h"
#include "src/utils.h"
#include "src/v8.h"
......@@ -1312,6 +1313,13 @@ void Shell::OnExit(v8::Isolate* isolate) {
"-------------+\n");
delete [] counters;
}
if (i::FLAG_trace_ignition_dispatches) {
reinterpret_cast<i::Isolate*>(isolate)
->interpreter()
->WriteDispatchCounters();
}
delete counters_file_;
delete counter_map_;
#endif // !V8_SHARED
......
......@@ -59,6 +59,8 @@ ExternalReferenceTable::ExternalReferenceTable(Isolate* isolate) {
Add(ExternalReference::isolate_address(isolate).address(), "isolate");
Add(ExternalReference::interpreter_dispatch_table_address(isolate).address(),
"Interpreter::dispatch_table_address");
Add(ExternalReference::interpreter_dispatch_counters(isolate).address(),
"Interpreter::interpreter_dispatch_counters");
Add(ExternalReference::address_of_negative_infinity().address(),
"LDoubleConstant::negative_infinity");
Add(ExternalReference::power_double_double_function(isolate).address(),
......
......@@ -290,6 +290,13 @@ DEFINE_BOOL(trace_ignition, false,
"trace the bytecodes executed by the ignition interpreter")
DEFINE_BOOL(trace_ignition_codegen, false,
"trace the codegen of ignition interpreter bytecode handlers")
DEFINE_BOOL(trace_ignition_dispatches, false,
"traces the dispatches to bytecode handlers by the ignition "
"interpreter")
DEFINE_STRING(trace_ignition_dispatches_output_file,
"v8.ignition_dispatches_table.json",
"the file to which the bytecode handler dispatch table is "
"written")
// Flags for Crankshaft.
DEFINE_BOOL(crankshaft, true, "use crankshaft")
......
......@@ -4,6 +4,7 @@
#include "src/interpreter/interpreter-assembler.h"
#include <limits>
#include <ostream>
#include "src/code-factory.h"
......@@ -554,6 +555,10 @@ void InterpreterAssembler::DispatchTo(Node* new_bytecode_offset) {
target_bytecode = ChangeUint32ToUint64(target_bytecode);
}
if (FLAG_trace_ignition_dispatches) {
TraceBytecodeDispatch(target_bytecode);
}
// TODO(rmcilroy): Create a code target dispatch table to avoid conversion
// from code object on every dispatch.
Node* target_code_object =
......@@ -590,6 +595,11 @@ void InterpreterAssembler::DispatchWide(OperandScale operand_scale) {
if (kPointerSize == 8) {
next_bytecode = ChangeUint32ToUint64(next_bytecode);
}
if (FLAG_trace_ignition_dispatches) {
TraceBytecodeDispatch(next_bytecode);
}
Node* base_index;
switch (operand_scale) {
case OperandScale::kDouble:
......@@ -673,6 +683,35 @@ void InterpreterAssembler::TraceBytecode(Runtime::FunctionId function_id) {
SmiTag(BytecodeOffset()), GetAccumulatorUnchecked());
}
void InterpreterAssembler::TraceBytecodeDispatch(Node* target_bytecode) {
Node* counters_table = ExternalConstant(
ExternalReference::interpreter_dispatch_counters(isolate()));
Node* source_bytecode_table_index = IntPtrConstant(
static_cast<int>(bytecode_) * (static_cast<int>(Bytecode::kLast) + 1));
Node* counter_offset =
WordShl(IntPtrAdd(source_bytecode_table_index, target_bytecode),
IntPtrConstant(kPointerSizeLog2));
Node* old_counter =
Load(MachineType::IntPtr(), counters_table, counter_offset);
CodeStubAssembler::Label counter_ok(this);
CodeStubAssembler::Label counter_saturated(this);
CodeStubAssembler::Label end(this);
Node* counter_reached_max = WordEqual(
old_counter, IntPtrConstant(std::numeric_limits<uintptr_t>::max()));
Branch(counter_reached_max, &counter_saturated, &counter_ok);
Bind(&counter_ok);
Node* new_counter = IntPtrAdd(old_counter, IntPtrConstant(1));
StoreNoWriteBarrier(MachineType::PointerRepresentation(), counters_table,
counter_offset, new_counter);
Goto(&end);
Bind(&counter_saturated);
Goto(&end);
Bind(&end);
}
// static
bool InterpreterAssembler::TargetSupportsUnalignedAccess() {
#if V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64
......
......@@ -173,6 +173,9 @@ class InterpreterAssembler : public compiler::CodeStubAssembler {
void CallPrologue() override;
void CallEpilogue() override;
// Increment the dispatch counter for the (current, next) bytecode pair.
void TraceBytecodeDispatch(compiler::Node* target_index);
// Traces the current bytecode by calling |function_id|.
void TraceBytecode(Runtime::FunctionId function_id);
......
......@@ -4,6 +4,8 @@
#include "src/interpreter/interpreter.h"
#include <fstream>
#include "src/ast/prettyprinter.h"
#include "src/code-factory.h"
#include "src/compiler.h"
......@@ -32,6 +34,14 @@ void Interpreter::Initialize() {
Zone zone(isolate_->allocator());
HandleScope scope(isolate_);
if (FLAG_trace_ignition_dispatches) {
static const int kBytecodeCount = static_cast<int>(Bytecode::kLast) + 1;
bytecode_dispatch_count_table_.Reset(
new uintptr_t[kBytecodeCount * kBytecodeCount]);
memset(bytecode_dispatch_count_table_.get(), 0,
sizeof(uintptr_t) * kBytecodeCount * kBytecodeCount);
}
// Generate bytecode handlers for all bytecodes and scales.
for (OperandScale operand_scale = OperandScale::kSingle;
operand_scale <= OperandScale::kMaxValid;
......@@ -147,9 +157,11 @@ bool Interpreter::MakeBytecode(CompilationInfo* info) {
}
bool Interpreter::IsDispatchTableInitialized() {
if (FLAG_trace_ignition || FLAG_trace_ignition_codegen) {
// Regenerate table to add bytecode tracing operations
// or to print the assembly code generated by TurboFan.
if (FLAG_trace_ignition || FLAG_trace_ignition_codegen ||
FLAG_trace_ignition_dispatches) {
// Regenerate table to add bytecode tracing operations,
// print the assembly code generated by TurboFan,
// or instrument handlers with dispatch counters.
return false;
}
return dispatch_table_[0] != nullptr;
......@@ -177,6 +189,52 @@ const char* Interpreter::LookupNameOfBytecodeHandler(Code* code) {
return nullptr;
}
void Interpreter::WriteDispatchCounters() {
std::ofstream stream(FLAG_trace_ignition_dispatches_output_file);
static const int kBytecodeCount = static_cast<int>(Bytecode::kLast) + 1;
// Output is a JSON-encoded object of objects.
//
// The keys on the top level object are source bytecodes,
// and corresponding value are objects. Keys on these last are the
// destinations of the dispatch and the value associated is a counter for
// the correspondent source-destination dispatch chain.
//
// Only non-zero counters are written to file, but an entry in the top-level
// object is always present, even if the value is empty because all counters
// for that source are zero.
stream << '{';
for (int from_index = 0; from_index < kBytecodeCount; ++from_index) {
if (from_index > 0) stream << ",\n ";
Bytecode from_bytecode = Bytecodes::FromByte(from_index);
stream << "\"" << Bytecodes::ToString(from_bytecode) << "\": {";
bool emitted_first = false;
for (int to_index = 0; to_index < kBytecodeCount; ++to_index) {
uintptr_t counter =
bytecode_dispatch_count_table_[from_index * kBytecodeCount +
to_index];
if (counter > 0) {
if (emitted_first) {
stream << ", ";
} else {
emitted_first = true;
}
Bytecode to_bytecode = Bytecodes::FromByte(to_index);
stream << '"' << Bytecodes::ToString(to_bytecode) << "\": " << counter;
}
}
stream << "}";
}
stream << '}';
}
// LdaZero
//
// Load literal '0' into the accumulator.
......
......@@ -49,10 +49,16 @@ class Interpreter {
void TraceCodegen(Handle<Code> code);
const char* LookupNameOfBytecodeHandler(Code* code);
void WriteDispatchCounters();
Address dispatch_table_address() {
return reinterpret_cast<Address>(&dispatch_table_[0]);
}
uintptr_t* bytecode_dispatch_count_table() {
return bytecode_dispatch_count_table_.get();
}
private:
// Bytecode handler generator functions.
#define DECLARE_BYTECODE_HANDLER_GENERATOR(Name, ...) \
......@@ -141,6 +147,7 @@ class Interpreter {
Isolate* isolate_;
Code* dispatch_table_[kDispatchTableSize];
v8::base::SmartArrayPointer<uintptr_t> bytecode_dispatch_count_table_;
DISALLOW_COPY_AND_ASSIGN(Interpreter);
};
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment