[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:
parent
07e7da140a
commit
614dbbff2f
@ -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;
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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" +
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user