Commit c9524b41 authored by Georgia Kouveli's avatar Georgia Kouveli Committed by Commit Bot

[turbolizer] Display instruction sequences

Display the InstructionSequence before and after register allocation.

Change-Id: If5169ae1af4f1fda95b3d150f9318ad11679caee
Reviewed-on: https://chromium-review.googlesource.com/c/1258005
Commit-Queue: Martyn Capewell <martyn.capewell@arm.com>
Reviewed-by: 's avatarSigurd Schneider <sigurds@chromium.org>
Cr-Commit-Position: refs/heads/master@{#56451}
parent b721f595
......@@ -963,6 +963,290 @@ std::ostream& operator<<(std::ostream& os, const AsScheduledGraph& scheduled) {
return os;
}
std::ostream& operator<<(std::ostream& os, const InstructionOperandAsJSON& o) {
const RegisterConfiguration* conf = o.register_configuration_;
const InstructionOperand* op = o.op_;
const InstructionSequence* code = o.code_;
os << "{";
switch (op->kind()) {
case InstructionOperand::UNALLOCATED: {
const UnallocatedOperand* unalloc = UnallocatedOperand::cast(op);
os << "\"type\": \"unallocated\", ";
os << "\"text\": \"v" << unalloc->virtual_register() << "\"";
if (unalloc->basic_policy() == UnallocatedOperand::FIXED_SLOT) {
os << ",\"tooltip\": \"FIXED_SLOT: " << unalloc->fixed_slot_index()
<< "\"";
break;
}
switch (unalloc->extended_policy()) {
case UnallocatedOperand::NONE:
break;
case UnallocatedOperand::FIXED_REGISTER: {
os << ",\"tooltip\": \"FIXED_REGISTER: "
<< conf->GetGeneralRegisterName(unalloc->fixed_register_index())
<< "\"";
break;
}
case UnallocatedOperand::FIXED_FP_REGISTER: {
os << ",\"tooltip\": \"FIXED_FP_REGISTER: "
<< conf->GetDoubleRegisterName(unalloc->fixed_register_index())
<< "\"";
break;
}
case UnallocatedOperand::MUST_HAVE_REGISTER: {
os << ",\"tooltip\": \"MUST_HAVE_REGISTER\"";
break;
}
case UnallocatedOperand::MUST_HAVE_SLOT: {
os << ",\"tooltip\": \"MUST_HAVE_SLOT\"";
break;
}
case UnallocatedOperand::SAME_AS_FIRST_INPUT: {
os << ",\"tooltip\": \"SAME_AS_FIRST_INPUT\"";
break;
}
case UnallocatedOperand::REGISTER_OR_SLOT: {
os << ",\"tooltip\": \"REGISTER_OR_SLOT\"";
break;
}
case UnallocatedOperand::REGISTER_OR_SLOT_OR_CONSTANT: {
os << ",\"tooltip\": \"REGISTER_OR_SLOT_OR_CONSTANT\"";
break;
}
}
break;
}
case InstructionOperand::CONSTANT: {
int vreg = ConstantOperand::cast(op)->virtual_register();
os << "\"type\": \"constant\", ";
os << "\"text\": \"v" << vreg << "\",";
os << "\"tooltip\": \"";
std::stringstream tooltip;
tooltip << code->GetConstant(vreg);
for (const auto& c : tooltip.str()) {
os << AsEscapedUC16ForJSON(c);
}
os << "\"";
break;
}
case InstructionOperand::IMMEDIATE: {
os << "\"type\": \"immediate\", ";
const ImmediateOperand* imm = ImmediateOperand::cast(op);
switch (imm->type()) {
case ImmediateOperand::INLINE: {
os << "\"text\": \"#" << imm->inline_value() << "\"";
break;
}
case ImmediateOperand::INDEXED: {
int index = imm->indexed_value();
os << "\"text\": \"imm:" << index << "\",";
os << "\"tooltip\": \"";
std::stringstream tooltip;
tooltip << code->GetImmediate(imm);
for (const auto& c : tooltip.str()) {
os << AsEscapedUC16ForJSON(c);
}
os << "\"";
break;
}
}
break;
}
case InstructionOperand::EXPLICIT:
case InstructionOperand::ALLOCATED: {
const LocationOperand* allocated = LocationOperand::cast(op);
os << "\"type\": ";
if (allocated->IsExplicit()) {
os << "\"explicit\", ";
} else {
os << "\"allocated\", ";
}
os << "\"text\": \"";
if (op->IsStackSlot()) {
os << "stack:" << allocated->index();
} else if (op->IsFPStackSlot()) {
os << "fp_stack:" << allocated->index();
} else if (op->IsRegister()) {
os << conf->GetGeneralOrSpecialRegisterName(allocated->register_code());
} else if (op->IsDoubleRegister()) {
os << conf->GetDoubleRegisterName(allocated->register_code());
} else if (op->IsFloatRegister()) {
os << conf->GetFloatRegisterName(allocated->register_code());
} else {
DCHECK(op->IsSimd128Register());
os << conf->GetSimd128RegisterName(allocated->register_code());
}
os << "\",";
os << "\"tooltip\": \""
<< MachineReprToString(allocated->representation()) << "\"";
break;
}
case InstructionOperand::INVALID:
UNREACHABLE();
}
os << "}";
return os;
}
std::ostream& operator<<(std::ostream& os, const InstructionAsJSON& i) {
const Instruction* instr = i.instr_;
InstructionOperandAsJSON json_op = {i.register_configuration_, nullptr,
i.code_};
os << "{";
os << "\"id\": " << i.index_ << ",";
os << "\"opcode\": \"" << ArchOpcodeField::decode(instr->opcode()) << "\",";
os << "\"flags\": \"";
FlagsMode fm = FlagsModeField::decode(instr->opcode());
AddressingMode am = AddressingModeField::decode(instr->opcode());
if (am != kMode_None) {
os << " : " << AddressingModeField::decode(instr->opcode());
}
if (fm != kFlags_none) {
os << " && " << fm << " if "
<< FlagsConditionField::decode(instr->opcode());
}
os << "\",";
os << "\"gaps\": [";
for (int i = Instruction::FIRST_GAP_POSITION;
i <= Instruction::LAST_GAP_POSITION; i++) {
if (i != Instruction::FIRST_GAP_POSITION) os << ",";
os << "[";
const ParallelMove* pm = instr->parallel_moves()[i];
if (pm == nullptr) {
os << "]";
continue;
}
bool first = true;
for (MoveOperands* move : *pm) {
if (move->IsEliminated()) continue;
if (!first) os << ",";
first = false;
json_op.op_ = &move->destination();
os << "[" << json_op << ",";
json_op.op_ = &move->source();
os << json_op << "]";
}
os << "]";
}
os << "],";
os << "\"outputs\": [";
bool need_comma = false;
for (size_t i = 0; i < instr->OutputCount(); i++) {
if (need_comma) os << ",";
need_comma = true;
json_op.op_ = instr->OutputAt(i);
os << json_op;
}
os << "],";
os << "\"inputs\": [";
need_comma = false;
for (size_t i = 0; i < instr->InputCount(); i++) {
if (need_comma) os << ",";
need_comma = true;
json_op.op_ = instr->InputAt(i);
os << json_op;
}
os << "],";
os << "\"temps\": [";
need_comma = false;
for (size_t i = 0; i < instr->TempCount(); i++) {
if (need_comma) os << ",";
need_comma = true;
json_op.op_ = instr->TempAt(i);
os << json_op;
}
os << "]";
os << "}";
return os;
}
std::ostream& operator<<(std::ostream& os, const InstructionBlockAsJSON& b) {
const InstructionBlock* block = b.block_;
const InstructionSequence* code = b.code_;
os << "{";
os << "\"id\": " << block->rpo_number() << ",";
os << "\"deferred\": " << block->IsDeferred() << ",";
os << "\"loop_header\": " << block->IsLoopHeader() << ",";
if (block->IsLoopHeader()) {
os << "\"loop_end\": " << block->loop_end() << ",";
}
os << "\"predecessors\": [";
bool need_comma = false;
for (RpoNumber pred : block->predecessors()) {
if (need_comma) os << ",";
need_comma = true;
os << pred.ToInt();
}
os << "],";
os << "\"successors\": [";
need_comma = false;
for (RpoNumber succ : block->successors()) {
if (need_comma) os << ",";
need_comma = true;
os << succ.ToInt();
}
os << "],";
os << "\"phis\": [";
bool needs_comma = false;
InstructionOperandAsJSON json_op = {b.register_configuration_, nullptr, code};
for (const PhiInstruction* phi : block->phis()) {
if (needs_comma) os << ",";
needs_comma = true;
json_op.op_ = &phi->output();
os << "{\"output\" : " << json_op << ",";
os << "\"operands\": [";
bool op_needs_comma = false;
for (int input : phi->operands()) {
if (op_needs_comma) os << ",";
op_needs_comma = true;
os << "\"v" << input << "\"";
}
os << "]}";
}
os << "],";
os << "\"instructions\": [";
InstructionAsJSON json_instr = {b.register_configuration_, -1, nullptr, code};
need_comma = false;
for (int j = block->first_instruction_index();
j <= block->last_instruction_index(); j++) {
if (need_comma) os << ",";
need_comma = true;
json_instr.index_ = j;
json_instr.instr_ = code->InstructionAt(j);
os << json_instr;
}
os << "]";
os << "}";
return os;
}
std::ostream& operator<<(std::ostream& os, const InstructionSequenceAsJSON& s) {
const InstructionSequence* code = s.sequence_;
os << "\"blocks\": [";
InstructionBlockAsJSON json_block = {s.register_configuration_, nullptr,
code};
bool need_comma = false;
for (int i = 0; i < code->InstructionBlockCount(); i++) {
if (need_comma) os << ",";
need_comma = true;
json_block.block_ = code->InstructionBlockAt(RpoNumber::FromInt(i));
os << json_block;
}
os << "]";
return os;
}
} // namespace compiler
} // namespace internal
} // namespace v8
......@@ -17,11 +17,15 @@ namespace v8 {
namespace internal {
class OptimizedCompilationInfo;
class RegisterConfiguration;
class SharedFunctionInfo;
class SourcePosition;
namespace compiler {
class Graph;
class Instruction;
class InstructionBlock;
class InstructionOperand;
class InstructionSequence;
class NodeOrigin;
class NodeOriginTable;
......@@ -152,6 +156,36 @@ std::ostream& operator<<(std::ostream& os, const AsC1V& ac);
std::ostream& operator<<(std::ostream& os,
const AsC1VRegisterAllocationData& ac);
struct InstructionOperandAsJSON {
const RegisterConfiguration* register_configuration_;
const InstructionOperand* op_;
const InstructionSequence* code_;
};
std::ostream& operator<<(std::ostream& os, const InstructionOperandAsJSON& o);
struct InstructionAsJSON {
const RegisterConfiguration* register_configuration_;
int index_;
const Instruction* instr_;
const InstructionSequence* code_;
};
std::ostream& operator<<(std::ostream& os, const InstructionAsJSON& i);
struct InstructionBlockAsJSON {
const RegisterConfiguration* register_configuration_;
const InstructionBlock* block_;
const InstructionSequence* code_;
};
std::ostream& operator<<(std::ostream& os, const InstructionBlockAsJSON& b);
struct InstructionSequenceAsJSON {
const RegisterConfiguration* register_configuration_;
const InstructionSequence* sequence_;
};
std::ostream& operator<<(std::ostream& os, const InstructionSequenceAsJSON& s);
} // namespace compiler
} // namespace internal
} // namespace v8
......
......@@ -2635,6 +2635,29 @@ bool PipelineImpl::CommitDependencies(Handle<Code> code) {
data_->dependencies()->Commit(code);
}
namespace {
void TraceSequence(OptimizedCompilationInfo* info, PipelineData* data,
const RegisterConfiguration* config,
const char* phase_name) {
if (info->trace_turbo_json_enabled()) {
AllowHandleDereference allow_deref;
TurboJsonFile json_of(info, std::ios_base::app);
json_of << "{\"name\":\"" << phase_name << "\",\"type\":\"sequence\",";
json_of << InstructionSequenceAsJSON{config, data->sequence()};
json_of << "},\n";
}
if (info->trace_turbo_graph_enabled()) {
AllowHandleDereference allow_deref;
CodeTracer::Scope tracing_scope(data->GetCodeTracer());
OFStream os(tracing_scope.file());
os << "----- Instruction sequence " << phase_name << " -----\n"
<< PrintableInstructionSequence({config, data->sequence()});
}
}
} // namespace
void PipelineImpl::AllocateRegisters(const RegisterConfiguration* config,
CallDescriptor* call_descriptor,
bool run_verifier) {
......@@ -2660,13 +2683,7 @@ void PipelineImpl::AllocateRegisters(const RegisterConfiguration* config,
Run<MeetRegisterConstraintsPhase>();
Run<ResolvePhisPhase>();
Run<BuildLiveRangesPhase>();
if (info()->trace_turbo_graph_enabled()) {
AllowHandleDereference allow_deref;
CodeTracer::Scope tracing_scope(data->GetCodeTracer());
OFStream os(tracing_scope.file());
os << "----- Instruction sequence before register allocation -----\n"
<< PrintableInstructionSequence({config, data->sequence()});
}
TraceSequence(info(), data, config, "before register allocation");
if (verifier != nullptr) {
CHECK(!data->register_allocation_data()->ExistsUseWithoutDefinition());
CHECK(data->register_allocation_data()
......@@ -2707,13 +2724,7 @@ void PipelineImpl::AllocateRegisters(const RegisterConfiguration* config,
Run<LocateSpillSlotsPhase>();
if (info()->trace_turbo_graph_enabled()) {
AllowHandleDereference allow_deref;
CodeTracer::Scope tracing_scope(data->GetCodeTracer());
OFStream os(tracing_scope.file());
os << "----- Instruction sequence after register allocation -----\n"
<< PrintableInstructionSequence({config, data->sequence()});
}
TraceSequence(info(), data, config, "after register allocation");
if (verifier != nullptr) {
verifier->VerifyAssignment("End of regalloc pipeline.");
......
......@@ -4,6 +4,7 @@
import {GraphView} from "./graph-view.js"
import {ScheduleView} from "./schedule-view.js"
import {SequenceView} from "./sequence-view.js"
import {SourceResolver} from "./source-resolver.js"
import {SelectionBroker} from "./selection-broker.js"
import {View, PhaseView} from "./view.js"
......@@ -13,6 +14,7 @@ export class GraphMultiView extends View {
selectionBroker: SelectionBroker;
graph: GraphView;
schedule: ScheduleView;
sequence: SequenceView;
selectMenu: HTMLSelectElement;
currentPhaseView: View & PhaseView;
......@@ -36,6 +38,7 @@ export class GraphMultiView extends View {
this.graph = new GraphView(id, selectionBroker,
(phaseName) => view.displayPhaseByName(phaseName));
this.schedule = new ScheduleView(id, selectionBroker);
this.sequence = new SequenceView(id, selectionBroker);
this.selectMenu = (<HTMLSelectElement>document.getElementById('display-selector'));
}
......@@ -69,6 +72,8 @@ export class GraphMultiView extends View {
this.displayPhaseView(this.graph, phase.data);
} else if (phase.type == 'schedule') {
this.displayPhaseView(this.schedule, phase);
} else if (phase.type == 'sequence') {
this.displayPhaseView(this.sequence, phase);
}
}
......
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import {Sequence} from "./source-resolver.js"
import {isIterable} from "./util.js"
import {PhaseView} from "./view.js"
import {TextView} from "./text-view.js"
export class SequenceView extends TextView implements PhaseView {
sequence: Sequence;
search_info: Array<any>;
createViewElement() {
const pane = document.createElement('div');
pane.setAttribute('id', "sequence");
return pane;
}
constructor(parentId, broker) {
super(parentId, broker, null);
}
attachSelection(s) {
const view = this;
if (!(s instanceof Set)) return;
view.selectionHandler.clear();
view.blockSelectionHandler.clear();
const selected = new Array();
for (const key of s) selected.push(key);
view.selectionHandler.select(selected, true);
}
detachSelection() {
this.blockSelection.clear();
return this.selection.detachSelection();
}
initializeContent(data, rememberedSelection) {
this.divNode.innerHTML = '';
this.sequence = data.sequence;
this.search_info = [];
this.addBlocks(this.sequence.blocks);
this.attachSelection(rememberedSelection);
}
elementForBlock(block) {
const view = this;
function createElement(tag: string, cls: string | Array<string>, content?: string) {
const el = document.createElement(tag);
if (isIterable(cls)) {
for (const c of cls) el.classList.add(c);
} else {
el.classList.add(cls);
}
if (content != undefined) el.innerHTML = content;
return el;
}
function mkLinkHandler(id, handler) {
return function (e) {
e.stopPropagation();
if (!e.shiftKey) {
handler.clear();
}
handler.select(["" + id], true);
};
}
function mkBlockLinkHandler(blockId) {
return mkLinkHandler(blockId, view.blockSelectionHandler);
}
function mkOperandLinkHandler(text) {
return mkLinkHandler(text, view.selectionHandler);
}
function elementForOperand(operand, search_info) {
var text = operand.text;
const operandEl = createElement("div", ["parameter", "tag", "clickable", operand.type], text);
if (operand.tooltip) {
operandEl.setAttribute("title", operand.tooltip);
}
operandEl.onclick = mkOperandLinkHandler(text);
search_info.push(text);
view.addHtmlElementForNodeId(text, operandEl);
return operandEl;
}
function elementForInstruction(instruction, search_info) {
const instNodeEl = createElement("div", "instruction-node");
const inst_id = createElement("div", "instruction-id", instruction.id);
instNodeEl.appendChild(inst_id);
const instContentsEl = createElement("div", "instruction-contents");
instNodeEl.appendChild(instContentsEl);
// Print gap moves.
const gapEl = createElement("div", "gap", "gap");
instContentsEl.appendChild(gapEl);
for (const gap of instruction.gaps) {
const moves = createElement("div", ["comma-sep-list", "gap-move"]);
for (const move of gap) {
const moveEl = createElement("div", "move");
const destinationEl = elementForOperand(move[0], search_info);
moveEl.appendChild(destinationEl);
const assignEl = createElement("div", "assign", "=");
moveEl.appendChild(assignEl);
const sourceEl = elementForOperand(move[1], search_info);
moveEl.appendChild(sourceEl);
moves.appendChild(moveEl);
}
gapEl.appendChild(moves);
}
const instEl = createElement("div", "instruction");
instContentsEl.appendChild(instEl);
if (instruction.outputs.length > 0) {
const outputs = createElement("div", ["comma-sep-list", "input-output-list"]);
for (const output of instruction.outputs) {
const outputEl = elementForOperand(output, search_info);
outputs.appendChild(outputEl);
}
instEl.appendChild(outputs);
const assignEl = createElement("div", "assign", "=");
instEl.appendChild(assignEl);
}
var text = instruction.opcode + instruction.flags;
const inst_label = createElement("div", "node-label", text);
search_info.push(text);
view.addHtmlElementForNodeId(text, inst_label);
instEl.appendChild(inst_label);
if (instruction.inputs.length > 0) {
const inputs = createElement("div", ["comma-sep-list", "input-output-list"]);
for (const input of instruction.inputs) {
const inputEl = elementForOperand(input, search_info);
inputs.appendChild(inputEl);
}
instEl.appendChild(inputs);
}
if (instruction.temps.length > 0) {
const temps = createElement("div", ["comma-sep-list", "input-output-list", "temps"]);
for (const temp of instruction.temps) {
const tempEl = elementForOperand(temp, search_info);
temps.appendChild(tempEl);
}
instEl.appendChild(temps);
}
return instNodeEl;
}
const sequence_block = createElement("div", "schedule-block");
const block_id = createElement("div", ["block-id", "com", "clickable"], block.id);
block_id.onclick = mkBlockLinkHandler(block.id);
sequence_block.appendChild(block_id);
const block_pred = createElement("div", ["predecessor-list", "block-list", "comma-sep-list"]);
for (const pred of block.predecessors) {
const predEl = createElement("div", ["block-id", "com", "clickable"], pred);
predEl.onclick = mkBlockLinkHandler(pred);
block_pred.appendChild(predEl);
}
if (block.predecessors.length > 0) sequence_block.appendChild(block_pred);
const phis = createElement("div", "phis");
sequence_block.appendChild(phis);
const phiLabel = createElement("div", "phi-label", "phi:");
phis.appendChild(phiLabel);
const phiContents = createElement("div", "phi-contents");
phis.appendChild(phiContents);
for (const phi of block.phis) {
const phiEl = createElement("div", "phi");
phiContents.appendChild(phiEl);
const outputEl = elementForOperand(phi.output, this.search_info);
phiEl.appendChild(outputEl);
const assignEl = createElement("div", "assign", "=");
phiEl.appendChild(assignEl);
for (const input of phi.operands) {
const inputEl = createElement("div", ["parameter", "tag", "clickable"], input);
phiEl.appendChild(inputEl);
}
}
const instructions = createElement("div", "instructions");
for (const instruction of block.instructions) {
instructions.appendChild(elementForInstruction(instruction, this.search_info));
}
sequence_block.appendChild(instructions);
const block_succ = createElement("div", ["successor-list", "block-list", "comma-sep-list"]);
for (const succ of block.successors) {
const succEl = createElement("div", ["block-id", "com", "clickable"], succ);
succEl.onclick = mkBlockLinkHandler(succ);
block_succ.appendChild(succEl);
}
if (block.successors.length > 0) sequence_block.appendChild(block_succ);
this.addHtmlElementForBlockId(block.id, sequence_block);
return sequence_block;
}
addBlocks(blocks) {
for (const block of blocks) {
const blockEl = this.elementForBlock(block);
this.divNode.appendChild(blockEl);
}
}
searchInputAction(searchBar, e) {
e.stopPropagation();
this.selectionHandler.clear();
const query = searchBar.value;
if (query.length == 0) return;
const select = [];
window.sessionStorage.setItem("lastSearch", query);
const reg = new RegExp(query);
for (const item of this.search_info) {
if (reg.exec(item) != null) {
select.push(item);
}
}
this.selectionHandler.select(select, true);
}
onresize() { }
}
......@@ -76,6 +76,10 @@ export interface Schedule {
nodes: Array<any>;
}
export interface Sequence {
blocks: Array<any>;
}
export class SourceResolver {
nodePositionMap: Array<AnyPosition>;
sources: Array<Source>;
......@@ -383,7 +387,10 @@ export class SourceResolver {
if (phase.type == 'disassembly') {
this.disassemblyPhase = phase;
} else if (phase.type == 'schedule') {
this.phases.push(this.parseSchedule(phase))
this.phases.push(this.parseSchedule(phase));
this.phaseNames.set(phase.name, this.phases.length);
} else if (phase.type == 'sequence') {
this.phases.push(this.parseSequence(phase));
this.phaseNames.set(phase.name, this.phases.length);
} else if (phase.type == 'instructions') {
if (phase.nodeIdToInstructionRange) {
......@@ -525,4 +532,8 @@ export class SourceResolver {
phase.schedule = state;
return phase;
}
parseSequence(phase) {
phase.sequence = { blocks: phase.blocks };
return phase;
}
}
......@@ -485,6 +485,11 @@ text {
margin-left: -80px;
}
#sequence {
font-family: monospace;
margin-top: 50px;
}
#schedule {
font-family: monospace;
margin-top: 50px;
......@@ -579,6 +584,52 @@ text {
display: inline;
}
.instruction * {
padding-right: .5ex;
}
.phi-label, .instruction-id {
display: inline-block;
padding-right: .5ex;
padding-left: .5ex;
min-width: 1ex;
vertical-align: top;
}
.instruction-id:after {
content: ":";
}
.instruction-node, .gap, .instruction {
display: block;
}
.phi-contents, .instruction-contents, .gap *, .instruction * {
display: inline-block;
}
.phi * {
padding-right: 1ex;
display: inline-block;
}
.gap .gap-move {
padding-left: .5ex;
padding-right: .5ex;
}
.gap > *:before {
content: "(";
}
.gap > *:after {
content: ")";
}
.parameter.constant {
outline: 1px dotted red;
}
.clickable:hover {
text-decoration: underline;
}
......@@ -601,4 +652,13 @@ text {
.comma-sep-list > *:last-child {
padding-right: 0ex;
}
\ No newline at end of file
}
.temps:before {
content: "temps: ";
}
.temps {
padding-left: .5ex;
outline: 1px dotted grey;
}
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