Commit 0282737d authored by George Wort's avatar George Wort Committed by Commit Bot

[turbolizer] Display live ranges with sequences

Display register allocation live ranges alongside sequences in
turbolizer.

The existing --trace-turbo flag now also outputs the register
allocation data as part of the json file alongside the
instruction sequence data that is already produced before and
after register allocation is performed. This data includes live
range intervals for each virtual and fixed register and the state
of their assignments.

This json data can now be displayed in turbolizer alongside the
instruction sequences. The information is presented as a grid,
with each grid cell representing a LifeTimePosition of a certain
virtual register, determined by the column and row indices
respectively. Each LifeTimePosition is shown to be part of an
instruction id which itself is shown to be part of a block id.
Each interval is shown as a coloured rectangle positioned over
the relevant cells, and displaying text to indicate the state of
their assignment.

The Resizer object has been extended to allow the grid's html
panel to be varied in size in the same manner that the left and
right panels can be. The size of the grid itself must also be
adjusted whenever the div container changes size.

The RangeView class is introduced and is created and held by the
single SequenceView object used to display the
InstructionSequence data before and after register allocation.
A checkbox allows the user to show/hide the range view, this is
disabled when register allocation data is not provided or more
than 249 instructions are in the sequence. The latter being
required due to the css grid-row-col limit of 1000 alond with
helping alleviate performance issues. The SequenceView object
tracks the phase index currently selected as well as whether or
not it is currently being shown. This ensures that the RangeView
is not hidden and shown when switching between before and after
register allocation, allowing for a smoother transition between
the two. The scroll position is also saved and restored for
convenience.

The data about the instruction sequence required for the display
is held by the RangeView object and reset whenever a new
instruction sequence is shown. The grid div must sync its scroll
with the headers and row labels so as to ensure a consistent
view. The register allocation data is extracted from the json,
with each register row showing all intervals within the relevant
ranges. When the view is switched between before and after
register allocation, the relevant intervals are swapped in.

Bug: v8:7327
Notry: true
Change-Id: I183535a2410a7d663382f387199885250fb98691
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2184232Reviewed-by: 's avatarGeorg Neis <neis@chromium.org>
Reviewed-by: 's avatarSantiago Aboy Solanes <solanes@chromium.org>
Commit-Queue: Santiago Aboy Solanes <solanes@chromium.org>
Cr-Commit-Position: refs/heads/master@{#68019}
parent 55616a24
...@@ -964,6 +964,97 @@ void PrintScheduledGraph(std::ostream& os, const Schedule* schedule) { ...@@ -964,6 +964,97 @@ void PrintScheduledGraph(std::ostream& os, const Schedule* schedule) {
} // namespace } // namespace
std::ostream& operator<<(std::ostream& os,
const LiveRangeAsJSON& live_range_json) {
const LiveRange& range = live_range_json.range_;
os << "{\"id\":" << range.relative_id() << ",\"type\":";
if (range.HasRegisterAssigned()) {
const InstructionOperand op = range.GetAssignedOperand();
os << "\"assigned\",\"op\":"
<< InstructionOperandAsJSON{&op, &(live_range_json.code_)};
} else if (range.spilled() && !range.TopLevel()->HasNoSpillType()) {
const TopLevelLiveRange* top = range.TopLevel();
if (top->HasSpillOperand()) {
os << "\"assigned\",\"op\":"
<< InstructionOperandAsJSON{top->GetSpillOperand(),
&(live_range_json.code_)};
} else {
int index = top->GetSpillRange()->assigned_slot();
os << "\"spilled\",\"op\":";
if (IsFloatingPoint(top->representation())) {
os << "\"fp_stack:" << index << "\"";
} else {
os << "\"stack:" << index << "\"";
}
}
} else {
os << "\"none\"";
}
os << ",\"intervals\":[";
bool first = true;
for (const UseInterval* interval = range.first_interval();
interval != nullptr; interval = interval->next()) {
if (!first) os << ",";
first = false;
os << "[" << interval->start().value() << "," << interval->end().value()
<< "]";
}
os << "]}";
return os;
}
std::ostream& operator<<(
std::ostream& os,
const TopLevelLiveRangeAsJSON& top_level_live_range_json) {
int vreg = top_level_live_range_json.range_.vreg();
bool first = true;
os << "\"" << (vreg > 0 ? vreg : -vreg) << "\":{ \"child_ranges\":[";
for (const LiveRange* child = &(top_level_live_range_json.range_);
child != nullptr; child = child->next()) {
if (!top_level_live_range_json.range_.IsEmpty()) {
if (!first) os << ",";
first = false;
os << LiveRangeAsJSON{*child, top_level_live_range_json.code_};
}
}
os << "]";
if (top_level_live_range_json.range_.IsFixed()) {
os << ", \"is_deferred\": "
<< (top_level_live_range_json.range_.IsDeferredFixed() ? "true"
: "false");
}
os << "}";
return os;
}
void PrintTopLevelLiveRanges(std::ostream& os,
const ZoneVector<TopLevelLiveRange*> ranges,
const InstructionSequence& code) {
bool first = true;
os << "{";
for (const TopLevelLiveRange* range : ranges) {
if (range != nullptr && !range->IsEmpty()) {
if (!first) os << ",";
first = false;
os << TopLevelLiveRangeAsJSON{*range, code};
}
}
os << "}";
}
std::ostream& operator<<(std::ostream& os,
const RegisterAllocationDataAsJSON& ac) {
os << "\"fixed_double_live_ranges\": ";
PrintTopLevelLiveRanges(os, ac.data_.fixed_double_live_ranges(), ac.code_);
os << ",\"fixed_live_ranges\": ";
PrintTopLevelLiveRanges(os, ac.data_.fixed_live_ranges(), ac.code_);
os << ",\"live_ranges\": ";
PrintTopLevelLiveRanges(os, ac.data_.live_ranges(), ac.code_);
return os;
}
std::ostream& operator<<(std::ostream& os, const AsScheduledGraph& scheduled) { std::ostream& operator<<(std::ostream& os, const AsScheduledGraph& scheduled) {
PrintScheduledGraph(os, scheduled.schedule); PrintScheduledGraph(os, scheduled.schedule);
return os; return os;
...@@ -1228,7 +1319,7 @@ std::ostream& operator<<(std::ostream& os, const InstructionBlockAsJSON& b) { ...@@ -1228,7 +1319,7 @@ std::ostream& operator<<(std::ostream& os, const InstructionBlockAsJSON& b) {
std::ostream& operator<<(std::ostream& os, const InstructionSequenceAsJSON& s) { std::ostream& operator<<(std::ostream& os, const InstructionSequenceAsJSON& s) {
const InstructionSequence* code = s.sequence_; const InstructionSequence* code = s.sequence_;
os << "\"blocks\": ["; os << "[";
bool need_comma = false; bool need_comma = false;
for (int i = 0; i < code->InstructionBlockCount(); i++) { for (int i = 0; i < code->InstructionBlockCount(); i++) {
......
...@@ -22,6 +22,8 @@ class SourcePosition; ...@@ -22,6 +22,8 @@ class SourcePosition;
namespace compiler { namespace compiler {
class Graph; class Graph;
class LiveRange;
class TopLevelLiveRange;
class Instruction; class Instruction;
class InstructionBlock; class InstructionBlock;
class InstructionOperand; class InstructionOperand;
...@@ -155,6 +157,30 @@ std::ostream& operator<<(std::ostream& os, const AsC1V& ac); ...@@ -155,6 +157,30 @@ std::ostream& operator<<(std::ostream& os, const AsC1V& ac);
std::ostream& operator<<(std::ostream& os, std::ostream& operator<<(std::ostream& os,
const AsC1VRegisterAllocationData& ac); const AsC1VRegisterAllocationData& ac);
struct LiveRangeAsJSON {
const LiveRange& range_;
const InstructionSequence& code_;
};
std::ostream& operator<<(std::ostream& os,
const LiveRangeAsJSON& live_range_json);
struct TopLevelLiveRangeAsJSON {
const TopLevelLiveRange& range_;
const InstructionSequence& code_;
};
std::ostream& operator<<(
std::ostream& os, const TopLevelLiveRangeAsJSON& top_level_live_range_json);
struct RegisterAllocationDataAsJSON {
const RegisterAllocationData& data_;
const InstructionSequence& code_;
};
std::ostream& operator<<(std::ostream& os,
const RegisterAllocationDataAsJSON& ac);
struct InstructionOperandAsJSON { struct InstructionOperandAsJSON {
const InstructionOperand* op_; const InstructionOperand* op_;
const InstructionSequence* code_; const InstructionSequence* code_;
......
...@@ -3332,9 +3332,12 @@ void TraceSequence(OptimizedCompilationInfo* info, PipelineData* data, ...@@ -3332,9 +3332,12 @@ void TraceSequence(OptimizedCompilationInfo* info, PipelineData* data,
if (info->trace_turbo_json_enabled()) { if (info->trace_turbo_json_enabled()) {
AllowHandleDereference allow_deref; AllowHandleDereference allow_deref;
TurboJsonFile json_of(info, std::ios_base::app); TurboJsonFile json_of(info, std::ios_base::app);
json_of << "{\"name\":\"" << phase_name << "\",\"type\":\"sequence\","; json_of << "{\"name\":\"" << phase_name << "\",\"type\":\"sequence\""
json_of << InstructionSequenceAsJSON{data->sequence()}; << ",\"blocks\":" << InstructionSequenceAsJSON{data->sequence()}
json_of << "},\n"; << ",\"register_allocation\":{"
<< RegisterAllocationDataAsJSON{*(data->register_allocation_data()),
*(data->sequence())}
<< "}},\n";
} }
if (info->trace_turbo_graph_enabled()) { if (info->trace_turbo_graph_enabled()) {
AllowHandleDereference allow_deref; AllowHandleDereference allow_deref;
......
This diff was suppressed by a .gitattributes entry.
...@@ -8,6 +8,7 @@ code is governed by a BSD-style license that can be found in the LICENSE file. ...@@ -8,6 +8,7 @@ code is governed by a BSD-style license that can be found in the LICENSE file.
<meta charset="utf-8"> <meta charset="utf-8">
<title>V8 Turbolizer</title> <title>V8 Turbolizer</title>
<link rel="stylesheet" href="turbo-visualizer.css"> <link rel="stylesheet" href="turbo-visualizer.css">
<link rel="stylesheet" href="turbo-visualizer-ranges.css">
<link rel="stylesheet" href="tabs.css"> <link rel="stylesheet" href="tabs.css">
<link rel="icon" type="image/png" href="turbolizer.png"> <link rel="icon" type="image/png" href="turbolizer.png">
</head> </head>
...@@ -21,6 +22,12 @@ code is governed by a BSD-style license that can be found in the LICENSE file. ...@@ -21,6 +22,12 @@ code is governed by a BSD-style license that can be found in the LICENSE file.
<input id="upload-helper" type="file"> <input id="upload-helper" type="file">
<input id="upload" type="image" title="load graph" class="button-input" src="upload-icon.png" alt="upload graph"> <input id="upload" type="image" title="load graph" class="button-input" src="upload-icon.png" alt="upload graph">
</div> </div>
<div id="resizer-ranges" class="resizer" style="visibility:hidden;"></div>
<div id="ranges" class="content" style="visibility:hidden;"></div>
<div id="show-hide-ranges" class="show-hide-pane" style="visibility: hidden">
<input id="ranges-expand" type="image" title="show ranges" src="up-arrow.png" class="button-input invisible">
<input id="ranges-shrink" type="image" title="hide ranges" src="down-arrow.png" class="button-input">
</div>
</div> </div>
<div id="resizer-right" class="resizer"></div> <div id="resizer-right" class="resizer"></div>
<div id="right" class="content"></div> <div id="right" class="content"></div>
......
...@@ -14,6 +14,9 @@ export const GENERATED_PANE_ID = 'right'; ...@@ -14,6 +14,9 @@ export const GENERATED_PANE_ID = 'right';
export const DISASSEMBLY_PANE_ID = 'disassembly'; export const DISASSEMBLY_PANE_ID = 'disassembly';
export const DISASSEMBLY_COLLAPSE_ID = 'disassembly-shrink'; export const DISASSEMBLY_COLLAPSE_ID = 'disassembly-shrink';
export const DISASSEMBLY_EXPAND_ID = 'disassembly-expand'; export const DISASSEMBLY_EXPAND_ID = 'disassembly-expand';
export const RANGES_PANE_ID = "ranges";
export const RANGES_COLLAPSE_ID = "ranges-shrink";
export const RANGES_EXPAND_ID = "ranges-expand";
export const UNICODE_BLOCK = '&#9611;'; export const UNICODE_BLOCK = '&#9611;';
export const PROF_COLS = [ export const PROF_COLS = [
{ perc: 0, col: { r: 255, g: 255, b: 255 } }, { perc: 0, col: { r: 255, g: 255, b: 255 } },
......
...@@ -38,6 +38,11 @@ export class GraphMultiView extends View { ...@@ -38,6 +38,11 @@ export class GraphMultiView extends View {
return pane; return pane;
} }
hide() {
this.hideCurrentPhase();
super.hide();
}
constructor(id, selectionBroker, sourceResolver) { constructor(id, selectionBroker, sourceResolver) {
super(id); super(id);
const view = this; const view = this;
...@@ -86,7 +91,9 @@ export class GraphMultiView extends View { ...@@ -86,7 +91,9 @@ export class GraphMultiView extends View {
} }
show() { show() {
super.show(); // Insert before is used so that the display is inserted before the
// resizer for the RangeView.
this.container.insertBefore(this.divNode, this.container.firstChild);
this.initializeSelect(); this.initializeSelect();
const lastPhaseIndex = +window.sessionStorage.getItem("lastSelectedPhase"); const lastPhaseIndex = +window.sessionStorage.getItem("lastSelectedPhase");
const initialPhaseIndex = this.sourceResolver.repairPhaseId(lastPhaseIndex); const initialPhaseIndex = this.sourceResolver.repairPhaseId(lastPhaseIndex);
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -83,7 +83,7 @@ interface InstructionsPhase { ...@@ -83,7 +83,7 @@ interface InstructionsPhase {
instructionOffsetToPCOffset?: any; instructionOffsetToPCOffset?: any;
blockIdtoInstructionRange?: any; blockIdtoInstructionRange?: any;
nodeIdToInstructionRange?: any; nodeIdToInstructionRange?: any;
codeOffsetsInfo?: CodeOffsetsInfo codeOffsetsInfo?: CodeOffsetsInfo;
} }
interface GraphPhase { interface GraphPhase {
...@@ -100,8 +100,43 @@ export interface Schedule { ...@@ -100,8 +100,43 @@ export interface Schedule {
nodes: Array<any>; nodes: Array<any>;
} }
export class Interval {
start: number;
end: number;
constructor(numbers: [number, number]) {
this.start = numbers[0];
this.end = numbers[1];
}
}
export interface ChildRange {
id: string;
type: string;
op: any;
intervals: Array<[number, number]>;
}
export interface Range {
child_ranges: Array<ChildRange>;
is_deferred: boolean;
}
export class RegisterAllocation {
fixedDoubleLiveRanges: Map<string, Range>;
fixedLiveRanges: Map<string, Range>;
liveRanges: Map<string, Range>;
constructor(registerAllocation) {
this.fixedDoubleLiveRanges = new Map<string, Range>(Object.entries(registerAllocation.fixed_double_live_ranges));
this.fixedLiveRanges = new Map<string, Range>(Object.entries(registerAllocation.fixed_live_ranges));
this.liveRanges = new Map<string, Range>(Object.entries(registerAllocation.live_ranges));
}
}
export interface Sequence { export interface Sequence {
blocks: Array<any>; blocks: Array<any>;
register_allocation: RegisterAllocation;
} }
class CodeOffsetsInfo { class CodeOffsetsInfo {
...@@ -720,8 +755,11 @@ export class SourceResolver { ...@@ -720,8 +755,11 @@ export class SourceResolver {
phase.schedule = state; phase.schedule = state;
return phase; return phase;
} }
parseSequence(phase) { parseSequence(phase) {
phase.sequence = { blocks: phase.blocks }; phase.sequence = { blocks: phase.blocks,
register_allocation: phase.register_allocation ? new RegisterAllocation(phase.register_allocation)
: undefined };
return phase; return phase;
} }
} }
...@@ -18,7 +18,7 @@ window.onload = function () { ...@@ -18,7 +18,7 @@ window.onload = function () {
let sourceViews: Array<CodeView> = []; let sourceViews: Array<CodeView> = [];
let selectionBroker: SelectionBroker = null; let selectionBroker: SelectionBroker = null;
let sourceResolver: SourceResolver = null; let sourceResolver: SourceResolver = null;
const resizer = new Resizer(panesUpdatedCallback, 75); const resizer = new Resizer(panesUpdatedCallback, 75, 75);
const sourceTabsContainer = document.getElementById(C.SOURCE_PANE_ID); const sourceTabsContainer = document.getElementById(C.SOURCE_PANE_ID);
const sourceTabs = new Tabs(sourceTabsContainer); const sourceTabs = new Tabs(sourceTabsContainer);
sourceTabs.addTab("&#x2b;").classList.add("last-tab", "persistent-tab"); sourceTabs.addTab("&#x2b;").classList.add("last-tab", "persistent-tab");
...@@ -48,6 +48,8 @@ window.onload = function () { ...@@ -48,6 +48,8 @@ window.onload = function () {
sourceViews.forEach(sv => sv.hide()); sourceViews.forEach(sv => sv.hide());
if (multiview) multiview.hide(); if (multiview) multiview.hide();
multiview = null; multiview = null;
document.getElementById("ranges").innerHTML = '';
document.getElementById('ranges').style.visibility = "hidden";
if (disassemblyView) disassemblyView.hide(); if (disassemblyView) disassemblyView.hide();
sourceViews = []; sourceViews = [];
sourceResolver = new SourceResolver(); sourceResolver = new SourceResolver();
......
...@@ -91,3 +91,10 @@ export function measureText(text: string) { ...@@ -91,3 +91,10 @@ export function measureText(text: string) {
export function interpolate(val: number, max: number, start: number, end: number) { export function interpolate(val: number, max: number, start: number, end: number) {
return start + (end - start) * (val / max); return start + (end - start) * (val / max);
} }
export function createElement(tag: string, cls: string, content?: string) {
const el = document.createElement(tag);
el.className = cls;
if (content != undefined) el.innerText = content;
return el;
}
/* CSS specific to the live ranges div associated with
the RangeView typescript class in src/range-view.ts. */
:root {
--range-y-axis-width: 18ch;
--range-position-width: 3.5ch;
--range-block-border: 6px;
--range-instr-border: 3px;
--range-position-border: 1px;
}
.range-bold {
font-weight: bold;
color: black;
}
#ranges {
font-family: monospace;
min-height: auto;
overflow: hidden;
}
#resizer-ranges {
height: 10px;
}
.range-title-div {
padding: 2ch 2ch 2ch 2ch;
white-space: nowrap;
overflow: auto;
}
.range-title {
text-decoration: underline;
font-weight: bold;
font-size: large;
display: inline-block;
}
.range-title-help {
margin-left: 2ch;
width: 1ch;
padding: 0 0.25ch;
border: 1px dotted black;
color: slategray;
display: inline-block;
}
input.range-toggle-show {
vertical-align: middle;
}
.range-header-label-x {
text-align: center;
margin-left: 13ch;
}
.range-header-label-y {
width: 11ch;
float: left;
white-space: pre-wrap;
word-wrap: break-word;
margin-left: 6ch;
margin-top: 4ch;
}
.range-y-axis {
display: inline-block;
width: var(--range-y-axis-width);
overflow: hidden;
white-space: nowrap;
vertical-align: top;
}
.range-header {
display: flex;
overflow: hidden;
height: 8ch;
margin-left: var(--range-y-axis-width);
}
.range-position-labels,
.range-register-labels {
background-color: lightgray;
}
.range-register-labels {
float: right;
}
.range-position-labels {
margin-top: auto;
}
.range-registers {
float: right;
overflow: hidden;
text-align: right;
}
.range-positions-header,
.range-instruction-ids,
.range-block-ids {
overflow: hidden;
white-space: nowrap;
display: grid;
grid-gap: 0;
}
.range-reg {
width: 13ch;
text-align: right;
}
.range-reg::after {
content: ":";
}
.range-grid {
overflow: auto;
display: inline-block;
white-space: nowrap;
}
.range-block-id {
display: inline-block;
text-align: center;
}
.range-instruction-id {
display: inline-block;
text-align: center;
}
.range-position {
display: inline-block;
text-align: center;
z-index: 1;
}
.range-transparent,
.range-position.range-empty {
color: transparent;
}
.range-block-id:hover,
.range-instruction-id:hover,
.range-reg:hover,
.range-position:hover {
background-color: rgba(0, 0, 255, 0.10);
}
.range-position.range-header-element {
border-bottom: 2px solid rgb(109, 107, 107);
}
.range-block-id,
.range-instruction-id,
.range-reg,
.range-interval,
.range-position {
position: relative;
border: var(--range-position-border) solid rgb(109, 107, 107);
}
.range-block-id,
.range-instruction-id,
.range-interval,
.range-position {
border-left: 0;
}
.range-block-ids > .range-block-id:first-child,
.range-instruction-ids > .range-instruction-id:first-child,
.range-positions > .range-position:first-child {
border-left: var(--range-position-border) solid rgb(109, 107, 107);
}
.range-position.range-interval-position {
border: none;
}
.range-interval-text {
position: absolute;
padding-left: 0.5ch;
z-index: 2;
pointer-events: none
}
.range-block-border,
.range-block-border.range-position.range-interval-position:last-child {
border-right: var(--range-block-border) solid rgb(109, 107, 107);
}
.range-block-border.range-position.range-interval-position {
border-right: var(--range-block-border) solid transparent;
}
.range-instr-border,
.range-instr-border.range-position.range-interval-position:last-child {
border-right: var(--range-instr-border) solid rgb(109, 107, 107);
}
.range-instr-border.range-position.range-interval-position {
border-right: var(--range-instr-border) solid transparent;
}
.range,
.range-interval,
.range-interval-wrapper,
.range-positions {
white-space: nowrap;
display: inline-block;
}
.range-interval-wrapper,
.range-positions {
display: grid;
grid-gap: 0;
}
.range-interval {
background-color: rgb(153, 158, 168);
}
.range-hidden {
display: none !important;
}
.range-positions-placeholder {
width: 100%;
border: var(--range-position-border) solid transparent;
color: transparent;
}
\ No newline at end of file
...@@ -342,6 +342,13 @@ input:hover, ...@@ -342,6 +342,13 @@ input:hover,
background-color: #F8F8F8; background-color: #F8F8F8;
user-select: none; user-select: none;
flex: 1; flex: 1;
z-index: 7;
}
#middle.display-inline-flex,
#middle.display-inline-flex #multiview,
#middle.display-inline-flex #ranges {
display: inline-flex;
} }
.viewpane { .viewpane {
...@@ -351,11 +358,6 @@ input:hover, ...@@ -351,11 +358,6 @@ input:hover,
flex-direction: column; flex-direction: column;
} }
.multiview {
width: 100%;
}
#show-hide-disassembly { #show-hide-disassembly {
right: 0; right: 0;
} }
...@@ -423,6 +425,10 @@ text { ...@@ -423,6 +425,10 @@ text {
dominant-baseline: text-before-edge; dominant-baseline: text-before-edge;
} }
.tab-content {
z-index: 6;
}
.resizer { .resizer {
z-index: 10; z-index: 10;
width: 10px; width: 10px;
...@@ -595,6 +601,10 @@ text { ...@@ -595,6 +601,10 @@ text {
padding-right: .5ex; padding-right: .5ex;
} }
.instruction span {
padding-right: 0;
}
.phi-label, .phi-label,
.instruction-id { .instruction-id {
display: inline-block; display: inline-block;
...@@ -626,6 +636,10 @@ text { ...@@ -626,6 +636,10 @@ text {
display: inline-block; display: inline-block;
} }
.phi span {
padding-right: 0;
}
.gap .gap-move { .gap .gap-move {
padding-left: .5ex; padding-left: .5ex;
padding-right: .5ex; padding-right: .5ex;
...@@ -639,6 +653,10 @@ text { ...@@ -639,6 +653,10 @@ text {
content: ")"; content: ")";
} }
.virtual-reg {
outline: 1px dotted blue;
}
.parameter.constant { .parameter.constant {
outline: 1px dotted red; outline: 1px dotted red;
} }
......
This diff was suppressed by a .gitattributes entry.
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