[turbolizer] TurboFan nodes history improvements

Added:
- history's circles titles
- history's records titles
- ability to move to node from history view
- new hotkey for turboshaft layout

Bug: v8:7327
Change-Id: I7ecfdbef2c1bf9534c76f8ac253e846beeea8cb3
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3779909
Reviewed-by: Tobias Tebbi <tebbi@chromium.org>
Commit-Queue: Danylo Boiko <danielboyko02@gmail.com>
Cr-Commit-Position: refs/heads/main@{#82089}
This commit is contained in:
Danylo Boiko 2022-07-29 21:10:29 +03:00 committed by V8 LUCI CQ
parent 07e7da140a
commit 614dbbff2f
13 changed files with 119 additions and 57 deletions

View File

@ -787,3 +787,8 @@ svg.history-svg-container .history-content-scroll {
svg.history-svg-container .history-item tspan {
font-size: var(--history-item-tspan-font-size);
}
svg.history-svg-container .history-item-record tspan:hover {
fill: #606060;
cursor: pointer;
}

View File

@ -89,9 +89,13 @@
<td>Copy hovered node's info</td>
</tr>
<tr>
<td>u</td>
<td>y</td>
<td>Collapse unused blocks (blocks that don't have direct inputs and outputs of a hovered node)</td>
</tr>
<tr>
<td>u</td>
<td>Collapse unused blocks (blocks that don't have direct inputs and outputs of selected nodes)</td>
</tr>
</table>
</div>
</div>

View File

@ -24,7 +24,7 @@ export const SOURCE_PANE_DEFAULT_PERCENT = 1 / 4;
export const DISASSEMBLY_PANE_DEFAULT_PERCENT = 3 / 4;
export const RANGES_PANE_HEIGHT_DEFAULT_PERCENT = 3 / 4;
export const RANGES_PANE_WIDTH_DEFAULT_PERCENT = 1 / 2;
export const HISTORY_DEFAULT_HEIGHT_PERCENT = 1 / 5;
export const HISTORY_DEFAULT_HEIGHT_PERCENT = 1 / 3.5;
export const HISTORY_CONTENT_INDENT = 8;
export const HISTORY_SCROLLBAR_WIDTH = 6;
export const CLOSE_BUTTON_RADIUS = 25;

View File

@ -93,6 +93,13 @@ export class GraphMultiView extends View {
this.displayPhase(this.sourceResolver.getPhase(initialPhaseIndex));
}
public displayPhaseByName(phaseName: string, selection?: SelectionStorage): void {
this.currentPhaseView.hide();
const phaseId = this.sourceResolver.getPhaseIdByName(phaseName);
this.selectMenu.selectedIndex = phaseId;
this.displayPhase(this.sourceResolver.getPhase(phaseId), selection);
}
public onresize(): void {
this.currentPhaseView?.onresize();
}
@ -116,12 +123,6 @@ export class GraphMultiView extends View {
this.currentPhaseView = view;
}
private displayPhaseByName(phaseName: string, selection?: SelectionStorage): void {
const phaseId = this.sourceResolver.getPhaseIdByName(phaseName);
this.selectMenu.selectedIndex = phaseId;
this.displayPhase(this.sourceResolver.getPhase(phaseId), selection);
}
private displayNextGraphPhase(): void {
let nextPhaseIndex = this.selectMenu.selectedIndex + 1;
while (nextPhaseIndex < this.sourceResolver.phases.length) {

View File

@ -86,11 +86,11 @@ export class SelectionBroker {
}
}
// TODO (danylo boiko) Add instructionOffsets type
public broadcastInstructionSelect(from, instructionOffsets, selected: boolean): void {
public broadcastInstructionSelect(from, instructionOffsets: Array<number>, selected: boolean):
void {
// Select the lines from the disassembly (right panel)
for (const handler of this.instructionHandlers) {
if (handler != from) handler.brokeredInstructionSelect(instructionOffsets, selected);
if (handler != from) handler.brokeredInstructionSelect([instructionOffsets], selected);
}
// Select the lines from the source panel (left panel)

View File

@ -31,7 +31,8 @@ export interface BlockSelectionHandler {
export interface InstructionSelectionHandler {
select(instructionIds: Array<string>, selected: boolean): void;
clear(): void;
brokeredInstructionSelect(instructionsOffsets: Array<[number, number]>, selected: boolean): void;
brokeredInstructionSelect(instructionsOffsets: Array<[number, number]> | Array<Array<number>>,
selected: boolean): void;
}
export interface SourcePositionSelectionHandler {
@ -42,7 +43,7 @@ export interface SourcePositionSelectionHandler {
export interface RegisterAllocationSelectionHandler {
// These are called instructionIds since the class of the divs is "instruction-id"
select(instructionIds: Array<string>, selected: boolean): void;
select(instructionIds: Array<number>, selected: boolean): void;
clear(): void;
brokeredRegisterAllocationSelect(instructionsOffsets: Array<[number, number]>, selected: boolean):
void;

View File

@ -97,7 +97,8 @@ window.onload = function () {
multiview = new GraphMultiView(C.INTERMEDIATE_PANE_ID, selectionBroker, sourceResolver);
multiview.show();
historyView = new HistoryView(C.HISTORY_ID, selectionBroker, sourceResolver);
historyView = new HistoryView(C.HISTORY_ID, selectionBroker, sourceResolver,
multiview.displayPhaseByName.bind(multiview));
historyView.show();
} catch (err) {
if (window.confirm("Error: Exception during load of TurboFan JSON file:\n" +

View File

@ -196,8 +196,8 @@ export class DisassemblyView extends TextView {
view.updateSelection();
broker.broadcastClear(this);
},
brokeredInstructionSelect: function (instructionsOffsets: Array<[number, number]>,
selected: boolean) {
brokeredInstructionSelect: function (instructionsOffsets: Array<[number, number]> |
Array<Array<number>>, selected: boolean) {
const firstSelect = view.offsetSelection.isEmpty();
for (const instructionOffset of instructionsOffsets) {
const keyPcOffsets = view.sourceResolver.instructionsPhase

View File

@ -28,7 +28,8 @@ export class GraphView extends MovableView<Graph> {
drag: d3.DragBehavior<any, GraphNode, GraphNode>;
constructor(idOrContainer: string | HTMLElement, broker: SelectionBroker,
showPhaseByName: (name: string) => void, toolbox: HTMLElement) {
showPhaseByName: (name: string, selection: SelectionStorage) => void,
toolbox: HTMLElement) {
super(idOrContainer, broker, showPhaseByName, toolbox);
this.state.selection = new SelectionMap(node => node.identifier(),
@ -79,6 +80,7 @@ export class GraphView extends MovableView<Graph> {
if (selectedNodes?.length > 0) {
this.connectVisibleSelectedElements(this.state.selection);
this.updateGraphVisibility();
this.viewSelection();
} else {
if (this.state.cacheLayout && data.transform) {
@ -367,6 +369,10 @@ export class GraphView extends MovableView<Graph> {
return new SelectionStorage();
}
for (const node of rememberedSelection.adaptedNodes) {
this.graph.makeNodeVisible(node);
}
for (const [key, node] of rememberedSelection.nodes.entries()) {
// Adding survived nodes (with the same id)
const survivedNode = this.graph.nodeMap[key];
@ -711,9 +717,9 @@ export class GraphView extends MovableView<Graph> {
}
private selectOrigins(): void {
const selection = new SelectionStorage();
const origins = new Array<GraphNode>();
let phase = this.phaseName;
const selection = new Set<string>();
for (const node of this.state.selection) {
const origin = node.nodeLabel.origin;
if (origin && origin instanceof NodeOrigin) {
@ -722,14 +728,13 @@ export class GraphView extends MovableView<Graph> {
if (phase === this.phaseName && node) {
origins.push(node);
} else {
selection.add(origin.identifier());
selection.adaptNode(origin.identifier());
}
}
}
// Only go through phase reselection if we actually need
// to display another phase.
if (selection.size > 0 && phase !== this.phaseName) {
this.hide();
if (selection.isAdapted() && phase !== this.phaseName) {
this.showPhaseByName(phase, selection);
} else if (origins.length > 0) {
this.nodeSelectionHandler.clear();

View File

@ -12,6 +12,7 @@ import { GraphNode } from "../phases/graph-phase/graph-node";
import { HistoryHandler } from "../selection/selection-handler";
import { GraphPhase } from "../phases/graph-phase/graph-phase";
import { NodeOrigin } from "../origin";
import { SelectionStorage } from "../selection/selection-storage";
export class HistoryView extends View {
node: GraphNode;
@ -27,11 +28,14 @@ export class HistoryView extends View {
y: number;
maxNodeWidth: number;
maxPhaseNameWidth: number;
showPhaseByName: (name: string, selection: SelectionStorage) => void;
constructor(id: string, broker: SelectionBroker, sourceResolver: SourceResolver) {
constructor(id: string, broker: SelectionBroker, sourceResolver: SourceResolver,
showPhaseByName: (name: string, selection: SelectionStorage) => void) {
super(id);
this.broker = broker;
this.sourceResolver = sourceResolver;
this.showPhaseByName = showPhaseByName;
this.historyHandler = this.initializeNodeSelectionHandler();
this.broker.addHistoryHandler(this.historyHandler);
this.phaseIdToHistory = new Map<number, PhaseHistory>();
@ -60,8 +64,11 @@ export class HistoryView extends View {
.style("visibility", "hidden");
const dragHandler = d3.drag().on("drag", () => {
this.x += d3.event.dx;
this.y += d3.event.dy;
const rect = document.body.getBoundingClientRect();
const x = this.x + d3.event.dx;
this.x = d3.event.dx > 0 ? Math.min(x, rect.width - this.getWidth()) : Math.max(x, 0);
const y = this.y + d3.event.dy;
this.y = d3.event.dy > 0 ? Math.min(y, rect.height - this.getHeight()) : Math.max(y, 0);
this.svg.attr("transform", _ => `translate(${this.x},${this.y})`);
});
@ -125,7 +132,9 @@ export class HistoryView extends View {
let recordY = 0;
for (const [phaseId, phaseHistory] of this.phaseIdToHistory.entries()) {
if (!phaseHistory.hasChanges()) continue;
const phaseName = this.sourceResolver.getPhaseNameById(phaseId);
this.historyList
.append("text")
.classed("history-item", true)
@ -164,16 +173,25 @@ export class HistoryView extends View {
.classed("history-item", true)
.attr("r", this.labelBox.height / 3.5)
.attr("cx", this.labelBox.height / 3)
.attr("cy", this.labelBox.height / 3 + recordY)
.attr("fill", `url(#${circleId})`);
.attr("cy", this.labelBox.height / 2.5 + recordY)
.attr("fill", `url(#${circleId})`)
.append("title")
.text(`[${record.toString()}]`);
this.historyList
.append("text")
.classed("history-item", true)
.classed("history-item history-item-record", true)
.attr("dy", recordY)
.attr("dx", this.labelBox.height * 0.75)
.append("tspan")
.text(record.node.displayLabel);
.text(record.node.displayLabel)
.on("click", () => {
const selectionStorage = new SelectionStorage();
selectionStorage.adaptNode(record.node.identifier());
this.showPhaseByName(phaseName, selectionStorage);
})
.append("title")
.text(record.node.getTitle());
recordY += this.labelBox.height;
}
}
@ -198,13 +216,6 @@ export class HistoryView extends View {
content.node().appendChild(d3.select(this).node() as HTMLElement);
});
this.historyList
.append("rect")
.attr("width", historyArea.width)
.attr("height", historyArea.height)
.attr("transform", `translate(${historyArea.x},${historyArea.y})`)
.attr("opacity", 0);
this.historyList
.append("clipPath")
.attr("id", "history-clip-path")
@ -240,7 +251,7 @@ export class HistoryView extends View {
if (!isNaN(scrollBarPosition)) scrollBar.attr("y", scrollBarPosition);
};
this.historyList.on("wheel", () => {
this.svg.on("wheel", () => {
updateScrollPosition(d3.event.deltaY);
});
@ -267,6 +278,7 @@ export class HistoryView extends View {
const uniqueAncestors = new Set<string>();
const coefficient = this.getCoefficient("history-item-tspan-font-size");
let prevNode = null;
let first = true;
for (let i = 0; i < this.sourceResolver.phases.length; i++) {
const phase = this.sourceResolver.getPhase(i);
if (!(phase instanceof GraphPhase)) continue;
@ -305,6 +317,11 @@ export class HistoryView extends View {
this.addToHistory(i, node, HistoryChange.InplaceUpdated);
}
if (first) {
this.addToHistory(i, node, HistoryChange.Emerged);
first = false;
}
this.addToHistory(i, node, HistoryChange.Current);
prevNode = node;
}
@ -395,18 +412,19 @@ export class HistoryView extends View {
}
private getHeight(): number {
const clientRect = document.body.getBoundingClientRect();
return clientRect.height * C.HISTORY_DEFAULT_HEIGHT_PERCENT;
return window.screen.availHeight * C.HISTORY_DEFAULT_HEIGHT_PERCENT;
}
private getHistoryChangeColor(historyChange: HistoryChange): string {
switch (historyChange) {
case HistoryChange.Current:
return "rgb(255, 167, 0)";
case HistoryChange.Emerged:
return "rgb(160, 83, 236)";
case HistoryChange.Lowered:
return "rgb(0, 255, 0)";
case HistoryChange.InplaceUpdated:
return "rgb(0, 0, 255)";
return "rgb(57, 57, 208)";
case HistoryChange.Removed:
return "rgb(255, 0, 0)";
case HistoryChange.Survived:
@ -420,9 +438,7 @@ export class HistoryView extends View {
console.log(`${key} ${this.sourceResolver.getPhaseNameById(key)}`);
const phaseHistory = this.phaseIdToHistory.get(key);
for (const record of phaseHistory.nodeIdToRecord.values()) {
const changes = Array.from(record.changes.values()).sort()
.map(i => HistoryChange[i]).join(", ");
console.log(`[${changes}] `, record.node);
console.log(record.toString(), record.node);
}
}
}
@ -444,6 +460,13 @@ export class PhaseHistory {
}
this.nodeIdToRecord.get(key).addChange(change);
}
public hasChanges(): boolean {
for (const record of this.nodeIdToRecord.values()) {
if (record.hasChanges()) return true;
}
return false;
}
}
export class HistoryRecord {
@ -458,10 +481,20 @@ export class HistoryRecord {
public addChange(change: HistoryChange): void {
this.changes.add(change);
}
public hasChanges(): boolean {
return this.changes.size > 1 ||
(this.changes.size == 1 && !this.changes.has(HistoryChange.Current));
}
public toString(): string {
return Array.from(this.changes.values()).sort().map(i => HistoryChange[i]).join(", ");
}
}
export enum HistoryChange {
Current,
Emerged,
Lowered,
InplaceUpdated,
Removed,

View File

@ -16,12 +16,13 @@ import { TurboshaftGraph } from "../turboshaft-graph";
import { Graph } from "../graph";
import { TurboshaftGraphNode } from "../phases/turboshaft-graph-phase/turboshaft-graph-node";
import { GraphNode } from "../phases/graph-phase/graph-node";
import { SelectionStorage } from "../selection/selection-storage";
export abstract class MovableView<GraphType extends Graph | TurboshaftGraph> extends PhaseView {
phaseName: string;
graph: GraphType;
broker: SelectionBroker;
showPhaseByName: (name: string, selection: Set<any>) => void;
showPhaseByName: (name: string, selection: SelectionStorage) => void;
toolbox: HTMLElement;
state: MovableViewState;
nodeSelectionHandler: NodeSelectionHandler & ClearableHandler;
@ -35,7 +36,8 @@ export abstract class MovableView<GraphType extends Graph | TurboshaftGraph> ext
public abstract svgKeyDown(): void;
constructor(idOrContainer: string | HTMLElement, broker: SelectionBroker,
showPhaseByName: (name: string) => void, toolbox: HTMLElement) {
showPhaseByName: (name: string, selection: SelectionStorage) => void,
toolbox: HTMLElement) {
super(idOrContainer);
this.broker = broker;
this.showPhaseByName = showPhaseByName;

View File

@ -258,10 +258,10 @@ export abstract class TextView extends PhaseView {
& ClearableHandler {
const view = this;
return {
select: function (instructionIds: Array<string>, selected: boolean) {
select: function (instructionIds: Array<number>, selected: boolean) {
view.registerAllocationSelection.select(instructionIds, selected);
view.updateSelection();
view.broker.broadcastInstructionSelect(null, [instructionIds], selected);
view.broker.broadcastInstructionSelect(null, instructionIds, selected);
},
clear: function () {
view.registerAllocationSelection.clear();

View File

@ -35,7 +35,8 @@ export class TurboshaftGraphView extends MovableView<TurboshaftGraph> {
blockDrag: d3.DragBehavior<any, TurboshaftGraphBlock, TurboshaftGraphBlock>;
constructor(idOrContainer: string | HTMLElement, broker: SelectionBroker,
showPhaseByName: (name: string) => void, toolbox: HTMLElement) {
showPhaseByName: (name: string, selection: SelectionStorage) => void,
toolbox: HTMLElement) {
super(idOrContainer, broker, showPhaseByName, toolbox);
this.state.selection = new SelectionMap(node => node.identifier());
@ -155,7 +156,12 @@ export class TurboshaftGraphView extends MovableView<TurboshaftGraph> {
}
break;
case 85: // 'u'
this.collapseUnusedBlocks();
this.collapseUnusedBlocks(this.state.selection.selection.values());
break;
case 89: // 'y'
const node = this.graph.nodeMap[this.hoveredNodeIdentifier];
if (!node) return;
this.collapseUnusedBlocks([node]);
break;
default:
eventHandled = false;
@ -735,18 +741,22 @@ export class TurboshaftGraphView extends MovableView<TurboshaftGraph> {
this.updateGraphVisibility();
}
private collapseUnusedBlocks(): void {
const node = this.graph.nodeMap[this.hoveredNodeIdentifier];
if (!node) return;
private collapseUnusedBlocks(usedNodes: Iterable<TurboshaftGraphNode>): void {
const usedBlocks = new Set<TurboshaftGraphBlock>();
for (const node of usedNodes) {
usedBlocks.add(node.block);
const usedBlocks = new Set<TurboshaftGraphBlock>([node.block]);
for (const input of node.inputs) {
usedBlocks.add(input.source.block);
}
for (const output of node.outputs) {
usedBlocks.add(output.target.block);
for (const input of node.inputs) {
usedBlocks.add(input.source.block);
}
for (const output of node.outputs) {
usedBlocks.add(output.target.block);
}
}
if (usedBlocks.size == 0) return;
for (const block of this.graph.blockMap) {
block.collapsed = !usedBlocks.has(block);
}