[turbofan] Add instruction ranges to --trace-turbo
Bug: v8:7327 Change-Id: I6f378f0d36444e8413dfe7ad3e097091e3b86df1 Reviewed-on: https://chromium-review.googlesource.com/1098919 Commit-Queue: Sigurd Schneider <sigurds@chromium.org> Reviewed-by: Jaroslav Sevcik <jarin@chromium.org> Cr-Commit-Position: refs/heads/master@{#53712}
This commit is contained in:
parent
0fe56b84d5
commit
e5e15e579e
@ -78,7 +78,9 @@ CodeGenerator::CodeGenerator(Zone* codegen_zone, Frame* frame, Linkage* linkage,
|
||||
SourcePositionTableBuilder::RECORD_SOURCE_POSITIONS),
|
||||
wasm_compilation_data_(wasm_compilation_data),
|
||||
result_(kSuccess),
|
||||
poisoning_level_(poisoning_level) {
|
||||
poisoning_level_(poisoning_level),
|
||||
block_starts_(zone()),
|
||||
instr_starts_(zone()) {
|
||||
for (int i = 0; i < code->InstructionBlockCount(); ++i) {
|
||||
new (&labels_[i]) Label;
|
||||
}
|
||||
@ -179,6 +181,10 @@ void CodeGenerator::AssembleCode() {
|
||||
unwinding_info_writer_.SetNumberOfInstructionBlocks(
|
||||
code()->InstructionBlockCount());
|
||||
|
||||
if (info->trace_turbo_json_enabled()) {
|
||||
block_starts_.assign(code()->instruction_blocks().size(), -1);
|
||||
instr_starts_.assign(code()->instructions().size(), -1);
|
||||
}
|
||||
// Assemble all non-deferred blocks, followed by deferred ones.
|
||||
for (int deferred = 0; deferred < 2; ++deferred) {
|
||||
for (const InstructionBlock* block : code()->instruction_blocks()) {
|
||||
@ -190,6 +196,9 @@ void CodeGenerator::AssembleCode() {
|
||||
if (block->IsLoopHeader() && !tasm()->jump_optimization_info()) {
|
||||
tasm()->Align(16);
|
||||
}
|
||||
if (info->trace_turbo_json_enabled()) {
|
||||
block_starts_[block->rpo_number().ToInt()] = tasm()->pc_offset();
|
||||
}
|
||||
// Bind a label for a block.
|
||||
current_block_ = block->rpo_number();
|
||||
unwinding_info_writer_.BeginInstructionBlock(tasm()->pc_offset(), block);
|
||||
@ -436,6 +445,9 @@ bool CodeGenerator::IsMaterializableFromRoot(
|
||||
CodeGenerator::CodeGenResult CodeGenerator::AssembleBlock(
|
||||
const InstructionBlock* block) {
|
||||
for (int i = block->code_start(); i < block->code_end(); ++i) {
|
||||
if (info()->trace_turbo_json_enabled()) {
|
||||
instr_starts_[i] = tasm()->pc_offset();
|
||||
}
|
||||
Instruction* instr = code()->InstructionAt(i);
|
||||
CodeGenResult result = AssembleInstruction(instr, block);
|
||||
if (result != kSuccess) return result;
|
||||
|
@ -123,6 +123,9 @@ class CodeGenerator final : public GapResolver::Assembler {
|
||||
size_t GetSafepointTableOffset() const { return safepoints_.GetCodeOffset(); }
|
||||
size_t GetHandlerTableOffset() const { return handler_table_offset_; }
|
||||
|
||||
const ZoneVector<int>& block_starts() const { return block_starts_; }
|
||||
const ZoneVector<int>& instr_starts() const { return instr_starts_; }
|
||||
|
||||
private:
|
||||
GapResolver* resolver() { return &resolver_; }
|
||||
SafepointTableBuilder* safepoints() { return &safepoints_; }
|
||||
@ -419,6 +422,8 @@ class CodeGenerator final : public GapResolver::Assembler {
|
||||
WasmCompilationData* wasm_compilation_data_;
|
||||
CodeGenResult result_;
|
||||
PoisoningMitigationLevel poisoning_level_;
|
||||
ZoneVector<int> block_starts_;
|
||||
ZoneVector<int> instr_starts_;
|
||||
};
|
||||
|
||||
} // namespace compiler
|
||||
|
@ -32,12 +32,19 @@ namespace v8 {
|
||||
namespace internal {
|
||||
namespace compiler {
|
||||
|
||||
const char* get_cached_trace_turbo_filename(OptimizedCompilationInfo* info) {
|
||||
if (!info->trace_turbo_filename()) {
|
||||
info->set_trace_turbo_filename(
|
||||
GetVisualizerLogFileName(info, FLAG_trace_turbo_path, nullptr, "json"));
|
||||
}
|
||||
return info->trace_turbo_filename();
|
||||
}
|
||||
|
||||
TurboJsonFile::TurboJsonFile(OptimizedCompilationInfo* info,
|
||||
std::ios_base::openmode mode)
|
||||
: std::ofstream(
|
||||
GetVisualizerLogFileName(info, FLAG_trace_turbo_path, nullptr, "json")
|
||||
.get(),
|
||||
mode) {}
|
||||
: std::ofstream(get_cached_trace_turbo_filename(info), mode) {}
|
||||
|
||||
TurboJsonFile::~TurboJsonFile() { flush(); }
|
||||
|
||||
std::ostream& operator<<(std::ostream& out,
|
||||
const SourcePositionAsJSON& asJSON) {
|
||||
|
@ -30,6 +30,7 @@ class SourcePositionTable;
|
||||
|
||||
struct TurboJsonFile : public std::ofstream {
|
||||
TurboJsonFile(OptimizedCompilationInfo* info, std::ios_base::openmode mode);
|
||||
~TurboJsonFile();
|
||||
};
|
||||
|
||||
struct SourcePositionAsJSON {
|
||||
|
@ -28,7 +28,7 @@ InstructionSelector::InstructionSelector(
|
||||
SourcePositionMode source_position_mode, Features features,
|
||||
EnableScheduling enable_scheduling,
|
||||
EnableSerialization enable_serialization,
|
||||
PoisoningMitigationLevel poisoning_level)
|
||||
PoisoningMitigationLevel poisoning_level, EnableTraceTurboJson trace_turbo)
|
||||
: zone_(zone),
|
||||
linkage_(linkage),
|
||||
sequence_(sequence),
|
||||
@ -52,10 +52,16 @@ InstructionSelector::InstructionSelector(
|
||||
enable_switch_jump_table_(enable_switch_jump_table),
|
||||
poisoning_level_(poisoning_level),
|
||||
frame_(frame),
|
||||
instruction_selection_failed_(false) {
|
||||
instruction_selection_failed_(false),
|
||||
instr_origins_(sequence->zone()),
|
||||
trace_turbo_(trace_turbo) {
|
||||
instructions_.reserve(node_count);
|
||||
continuation_inputs_.reserve(5);
|
||||
continuation_outputs_.reserve(2);
|
||||
|
||||
if (trace_turbo_ == kEnableTraceTurboJson) {
|
||||
instr_origins_.assign(node_count, {-1, 0});
|
||||
}
|
||||
}
|
||||
|
||||
bool InstructionSelector::SelectInstructions() {
|
||||
@ -1092,14 +1098,19 @@ void InstructionSelector::VisitBlock(BasicBlock* block) {
|
||||
// Visit code in reverse control flow order, because architecture-specific
|
||||
// matching may cover more than one node at a time.
|
||||
for (auto node : base::Reversed(*block)) {
|
||||
int current_node_end = current_num_instructions();
|
||||
// Skip nodes that are unused or already defined.
|
||||
if (!IsUsed(node) || IsDefined(node)) continue;
|
||||
if (IsUsed(node) && !IsDefined(node)) {
|
||||
// Generate code for this node "top down", but schedule the code "bottom
|
||||
// up".
|
||||
int current_node_end = current_num_instructions();
|
||||
VisitNode(node);
|
||||
if (!FinishEmittedInstructions(node, current_node_end)) return;
|
||||
}
|
||||
if (trace_turbo_ == kEnableTraceTurboJson) {
|
||||
instr_origins_[node->id()] = {current_num_instructions(),
|
||||
current_node_end};
|
||||
}
|
||||
}
|
||||
|
||||
// We're done with the block.
|
||||
InstructionBlock* instruction_block =
|
||||
@ -1110,7 +1121,6 @@ void InstructionSelector::VisitBlock(BasicBlock* block) {
|
||||
}
|
||||
instruction_block->set_code_start(current_num_instructions());
|
||||
instruction_block->set_code_end(current_block_end);
|
||||
|
||||
current_block_ = nullptr;
|
||||
}
|
||||
|
||||
@ -1136,25 +1146,34 @@ void InstructionSelector::VisitControl(BasicBlock* block) {
|
||||
#endif
|
||||
|
||||
Node* input = block->control_input();
|
||||
int instruction_end = static_cast<int>(instructions_.size());
|
||||
switch (block->control()) {
|
||||
case BasicBlock::kGoto:
|
||||
return VisitGoto(block->SuccessorAt(0));
|
||||
VisitGoto(block->SuccessorAt(0));
|
||||
break;
|
||||
case BasicBlock::kCall: {
|
||||
DCHECK_EQ(IrOpcode::kCall, input->opcode());
|
||||
BasicBlock* success = block->SuccessorAt(0);
|
||||
BasicBlock* exception = block->SuccessorAt(1);
|
||||
return VisitCall(input, exception), VisitGoto(success);
|
||||
VisitCall(input, exception);
|
||||
VisitGoto(success);
|
||||
break;
|
||||
}
|
||||
case BasicBlock::kTailCall: {
|
||||
DCHECK_EQ(IrOpcode::kTailCall, input->opcode());
|
||||
return VisitTailCall(input);
|
||||
VisitTailCall(input);
|
||||
break;
|
||||
}
|
||||
case BasicBlock::kBranch: {
|
||||
DCHECK_EQ(IrOpcode::kBranch, input->opcode());
|
||||
BasicBlock* tbranch = block->SuccessorAt(0);
|
||||
BasicBlock* fbranch = block->SuccessorAt(1);
|
||||
if (tbranch == fbranch) return VisitGoto(tbranch);
|
||||
return VisitBranch(input, tbranch, fbranch);
|
||||
if (tbranch == fbranch) {
|
||||
VisitGoto(tbranch);
|
||||
} else {
|
||||
VisitBranch(input, tbranch, fbranch);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BasicBlock::kSwitch: {
|
||||
DCHECK_EQ(IrOpcode::kSwitch, input->opcode());
|
||||
@ -1176,20 +1195,24 @@ void InstructionSelector::VisitControl(BasicBlock* block) {
|
||||
// Ensure that comparison order of if-cascades is preserved.
|
||||
std::stable_sort(cases.begin(), cases.end());
|
||||
SwitchInfo sw(cases, min_value, max_value, default_branch);
|
||||
return VisitSwitch(input, sw);
|
||||
VisitSwitch(input, sw);
|
||||
break;
|
||||
}
|
||||
case BasicBlock::kReturn: {
|
||||
DCHECK_EQ(IrOpcode::kReturn, input->opcode());
|
||||
return VisitReturn(input);
|
||||
VisitReturn(input);
|
||||
break;
|
||||
}
|
||||
case BasicBlock::kDeoptimize: {
|
||||
DeoptimizeParameters p = DeoptimizeParametersOf(input->op());
|
||||
Node* value = input->InputAt(0);
|
||||
return VisitDeoptimize(p.kind(), p.reason(), p.feedback(), value);
|
||||
VisitDeoptimize(p.kind(), p.reason(), p.feedback(), value);
|
||||
break;
|
||||
}
|
||||
case BasicBlock::kThrow:
|
||||
DCHECK_EQ(IrOpcode::kThrow, input->opcode());
|
||||
return VisitThrow(input);
|
||||
VisitThrow(input);
|
||||
break;
|
||||
case BasicBlock::kNone: {
|
||||
// Exit block doesn't have control.
|
||||
DCHECK_NULL(input);
|
||||
@ -1199,6 +1222,10 @@ void InstructionSelector::VisitControl(BasicBlock* block) {
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
if (trace_turbo_ == kEnableTraceTurboJson && input) {
|
||||
int instruction_start = static_cast<int>(instructions_.size());
|
||||
instr_origins_[input->id()] = {instruction_start, instruction_end};
|
||||
}
|
||||
}
|
||||
|
||||
void InstructionSelector::MarkPairProjectionsAsWord32(Node* node) {
|
||||
|
@ -256,6 +256,7 @@ class V8_EXPORT_PRIVATE InstructionSelector final {
|
||||
kDisableSwitchJumpTable,
|
||||
kEnableSwitchJumpTable
|
||||
};
|
||||
enum EnableTraceTurboJson { kDisableTraceTurboJson, kEnableTraceTurboJson };
|
||||
|
||||
InstructionSelector(
|
||||
Zone* zone, size_t node_count, Linkage* linkage,
|
||||
@ -269,7 +270,8 @@ class V8_EXPORT_PRIVATE InstructionSelector final {
|
||||
: kDisableScheduling,
|
||||
EnableSerialization enable_serialization = kDisableSerialization,
|
||||
PoisoningMitigationLevel poisoning_level =
|
||||
PoisoningMitigationLevel::kDontPoison);
|
||||
PoisoningMitigationLevel::kDontPoison,
|
||||
EnableTraceTurboJson trace_turbo = kDisableTraceTurboJson);
|
||||
|
||||
// Visit code for the entire graph with the included schedule.
|
||||
bool SelectInstructions();
|
||||
@ -439,6 +441,10 @@ class V8_EXPORT_PRIVATE InstructionSelector final {
|
||||
|
||||
Isolate* isolate() const { return sequence()->isolate(); }
|
||||
|
||||
const ZoneVector<std::pair<int, int>>& instr_origins() const {
|
||||
return instr_origins_;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class OperandGenerator;
|
||||
|
||||
@ -704,6 +710,8 @@ class V8_EXPORT_PRIVATE InstructionSelector final {
|
||||
PoisoningMitigationLevel poisoning_level_;
|
||||
Frame* frame_;
|
||||
bool instruction_selection_failed_;
|
||||
ZoneVector<std::pair<int, int>> instr_origins_;
|
||||
EnableTraceTurboJson trace_turbo_;
|
||||
};
|
||||
|
||||
} // namespace compiler
|
||||
|
@ -1592,6 +1592,37 @@ struct ComputeSchedulePhase {
|
||||
}
|
||||
};
|
||||
|
||||
struct InstructionRangesAsJSON {
|
||||
const InstructionSequence* sequence;
|
||||
const ZoneVector<std::pair<int, int>>* instr_origins;
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& out, const InstructionRangesAsJSON& s) {
|
||||
const int max = static_cast<int>(s.sequence->LastInstructionIndex());
|
||||
|
||||
out << ", \"nodeIdToInstructionRange\": {";
|
||||
bool need_comma = false;
|
||||
for (size_t i = 0; i < s.instr_origins->size(); ++i) {
|
||||
std::pair<int, int> offset = (*s.instr_origins)[i];
|
||||
if (offset.first == -1) continue;
|
||||
const int first = max - offset.first + 1;
|
||||
const int second = max - offset.second + 1;
|
||||
if (need_comma) out << ", ";
|
||||
out << "\"" << i << "\": [" << first << ", " << second << "]";
|
||||
need_comma = true;
|
||||
}
|
||||
out << "}";
|
||||
out << ", \"blockIdtoInstructionRange\": {";
|
||||
need_comma = false;
|
||||
for (auto block : s.sequence->instruction_blocks()) {
|
||||
if (need_comma) out << ", ";
|
||||
out << "\"" << block->rpo_number() << "\": [" << block->code_start() << ", "
|
||||
<< block->code_end() << "]";
|
||||
need_comma = true;
|
||||
}
|
||||
out << "}";
|
||||
return out;
|
||||
}
|
||||
|
||||
struct InstructionSelectionPhase {
|
||||
static const char* phase_name() { return "select instructions"; }
|
||||
@ -1613,10 +1644,21 @@ struct InstructionSelectionPhase {
|
||||
data->isolate()->serializer_enabled()
|
||||
? InstructionSelector::kEnableSerialization
|
||||
: InstructionSelector::kDisableSerialization,
|
||||
data->info()->GetPoisoningMitigationLevel());
|
||||
data->info()->GetPoisoningMitigationLevel(),
|
||||
data->info()->trace_turbo_json_enabled()
|
||||
? InstructionSelector::kEnableTraceTurboJson
|
||||
: InstructionSelector::kDisableTraceTurboJson);
|
||||
if (!selector.SelectInstructions()) {
|
||||
data->set_compilation_failed();
|
||||
}
|
||||
if (data->info()->trace_turbo_json_enabled()) {
|
||||
TurboJsonFile json_of(data->info(), std::ios_base::app);
|
||||
json_of << "{\"name\":\"" << phase_name()
|
||||
<< "\",\"type\":\"instructions\""
|
||||
<< InstructionRangesAsJSON{data->sequence(),
|
||||
&selector.instr_origins()}
|
||||
<< "},\n";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -2316,14 +2358,55 @@ bool PipelineImpl::SelectInstructions(Linkage* linkage) {
|
||||
return true;
|
||||
}
|
||||
|
||||
struct InstructionStartsAsJSON {
|
||||
const ZoneVector<int>* instr_starts;
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& out, const InstructionStartsAsJSON& s) {
|
||||
out << ", \"instructionOffsetToPCOffset\": {";
|
||||
bool need_comma = false;
|
||||
for (size_t i = 0; i < s.instr_starts->size(); ++i) {
|
||||
if (need_comma) out << ", ";
|
||||
int offset = (*s.instr_starts)[i];
|
||||
out << "\"" << i << "\":" << offset;
|
||||
need_comma = true;
|
||||
}
|
||||
out << "}";
|
||||
return out;
|
||||
}
|
||||
|
||||
void PipelineImpl::AssembleCode(Linkage* linkage) {
|
||||
PipelineData* data = this->data_;
|
||||
data->BeginPhaseKind("code generation");
|
||||
data->InitializeCodeGenerator(linkage);
|
||||
Run<AssembleCodePhase>();
|
||||
if (data->info()->trace_turbo_json_enabled()) {
|
||||
TurboJsonFile json_of(data->info(), std::ios_base::app);
|
||||
json_of << "{\"name\":\"code generation\""
|
||||
<< ", \"type\":\"instructions\""
|
||||
<< InstructionStartsAsJSON{&data->code_generator()->instr_starts()};
|
||||
json_of << "},\n";
|
||||
}
|
||||
data->DeleteInstructionZone();
|
||||
}
|
||||
|
||||
struct BlockStartsAsJSON {
|
||||
const ZoneVector<int>* block_starts;
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& out, const BlockStartsAsJSON& s) {
|
||||
out << ", \"blockIdToOffset\": {";
|
||||
bool need_comma = false;
|
||||
for (size_t i = 0; i < s.block_starts->size(); ++i) {
|
||||
if (need_comma) out << ", ";
|
||||
int offset = (*s.block_starts)[i];
|
||||
out << "\"" << i << "\":" << offset;
|
||||
need_comma = true;
|
||||
}
|
||||
out << "},";
|
||||
return out;
|
||||
}
|
||||
|
||||
Handle<Code> PipelineImpl::FinalizeCode() {
|
||||
PipelineData* data = this->data_;
|
||||
Run<FinalizeCodePhase>();
|
||||
@ -2344,7 +2427,10 @@ Handle<Code> PipelineImpl::FinalizeCode() {
|
||||
|
||||
if (info()->trace_turbo_json_enabled()) {
|
||||
TurboJsonFile json_of(info(), std::ios_base::app);
|
||||
json_of << "{\"name\":\"disassembly\",\"type\":\"disassembly\",\"data\":\"";
|
||||
|
||||
json_of << "{\"name\":\"disassembly\",\"type\":\"disassembly\""
|
||||
<< BlockStartsAsJSON{&data->code_generator()->block_starts()}
|
||||
<< "\"data\":\"";
|
||||
#ifdef ENABLE_DISASSEMBLER
|
||||
std::stringstream disassembly_stream;
|
||||
code->Disassemble(nullptr, disassembly_stream);
|
||||
|
@ -266,6 +266,14 @@ class V8_EXPORT_PRIVATE OptimizedCompilationInfo final {
|
||||
|
||||
StackFrame::Type GetOutputStackFrameType() const;
|
||||
|
||||
const char* trace_turbo_filename() const {
|
||||
return trace_turbo_filename_.get();
|
||||
}
|
||||
|
||||
void set_trace_turbo_filename(std::unique_ptr<char[]> filename) {
|
||||
trace_turbo_filename_ = std::move(filename);
|
||||
}
|
||||
|
||||
private:
|
||||
OptimizedCompilationInfo(Vector<const char> debug_name,
|
||||
AbstractCode::Kind code_kind, Zone* zone);
|
||||
@ -313,6 +321,7 @@ class V8_EXPORT_PRIVATE OptimizedCompilationInfo final {
|
||||
JavaScriptFrame* osr_frame_ = nullptr;
|
||||
|
||||
Vector<const char> debug_name_;
|
||||
std::unique_ptr<char[]> trace_turbo_filename_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(OptimizedCompilationInfo);
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user