Commit f61d1afe authored by Danylo Boiko's avatar Danylo Boiko Committed by V8 LUCI CQ

[turbolizer] Turboshaft view initial commit

General:
- Graph view refactoring
Turboshaft:
- Blocks representation
- Inline nodes representation
- Minimum required turboshaft toolbox actions
- Layout caching

Bug: v8:7327
Change-Id: I2ac07965ac775c68c522cfc9367b7ce0ff18672a
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3726287Reviewed-by: 's avatarNico Hartmann <nicohartmann@chromium.org>
Commit-Queue: Danylo Boiko <danielboyko02@gmail.com>
Cr-Commit-Position: refs/heads/main@{#81553}
parent 6639962a
g.turboshaft-block rect {
stroke-dasharray: 20;
stroke-width: 7;
}
g.turboshaft-block:hover rect {
stroke-dasharray: 0;
stroke-width: 10;
}
g.block rect {
fill: #ecf3fe;
stroke: #4285f4;
}
g.merge rect {
fill: #e9fcee;
stroke: #2bde5a;
}
g.loop rect {
fill: #fdecea;
stroke: #e94235;
}
.block-label tspan {
font-weight: bold;
}
.block .block-label tspan {
fill: #4285f4;
}
.merge .block-label tspan {
fill: #34a853;
}
.loop .block-label tspan {
fill: #ea4335;
}
.inline-node-properties tspan {
fill: #9227b0;
}
This diff was suppressed by a .gitattributes entry.
...@@ -10,6 +10,7 @@ code is governed by a BSD-style license that can be found in the LICENSE file. ...@@ -10,6 +10,7 @@ code is governed by a BSD-style license that can be found in the LICENSE file.
<link rel="stylesheet" href="css/turbo-visualizer.css"> <link rel="stylesheet" href="css/turbo-visualizer.css">
<link rel="stylesheet" href="css/turbo-visualizer-ranges.css"> <link rel="stylesheet" href="css/turbo-visualizer-ranges.css">
<link rel="stylesheet" href="css/tabs.css"> <link rel="stylesheet" href="css/tabs.css">
<link rel="stylesheet" href="css/turboshaft.css">
<link rel="icon" href="turbolizer.png"> <link rel="icon" href="turbolizer.png">
</head> </head>
......
...@@ -4,8 +4,10 @@ ...@@ -4,8 +4,10 @@
import { GraphNode } from "./phases/graph-phase/graph-node"; import { GraphNode } from "./phases/graph-phase/graph-node";
import { TurboshaftGraphNode } from "./phases/turboshaft-graph-phase/turboshaft-graph-node"; import { TurboshaftGraphNode } from "./phases/turboshaft-graph-phase/turboshaft-graph-node";
import { TurboshaftGraphBlock } from "./phases/turboshaft-graph-phase/turboshaft-graph-block";
export abstract class Edge<NodeType extends GraphNode | TurboshaftGraphNode> { export abstract class Edge<NodeType extends GraphNode | TurboshaftGraphNode
| TurboshaftGraphBlock> {
target: NodeType; target: NodeType;
source: NodeType; source: NodeType;
backEdgeNumber: number; backEdgeNumber: number;
......
...@@ -36,7 +36,7 @@ export class GraphLayout { ...@@ -36,7 +36,7 @@ export class GraphLayout {
this.graph.graphPhase.rendered = true; this.graph.graphPhase.rendered = true;
} }
public fullRebuild(showTypes: boolean): void { private fullRebuild(showTypes: boolean): void {
this.startTime = performance.now(); this.startTime = performance.now();
this.maxRank = 0; this.maxRank = 0;
this.visitOrderWithinRank = 0; this.visitOrderWithinRank = 0;
...@@ -56,7 +56,7 @@ export class GraphLayout { ...@@ -56,7 +56,7 @@ export class GraphLayout {
this.graph.graphPhase.stateType = GraphStateType.Cached; this.graph.graphPhase.stateType = GraphStateType.Cached;
} }
public cachedRebuild(): void { private cachedRebuild(): void {
this.calculateBackEdgeNumbers(); this.calculateBackEdgeNumbers();
} }
...@@ -176,7 +176,7 @@ export class GraphLayout { ...@@ -176,7 +176,7 @@ export class GraphLayout {
const rankSets = new Array<Array<GraphNode>>(); const rankSets = new Array<Array<GraphNode>>();
for (const node of this.graph.nodes()) { for (const node of this.graph.nodes()) {
node.y = node.rank * (C.DEFAULT_NODE_ROW_SEPARATION + node.y = node.rank * (C.DEFAULT_NODE_ROW_SEPARATION +
node.getNodeHeight(showTypes) + 2 * C.DEFAULT_NODE_BUBBLE_RADIUS); node.getHeight(showTypes) + 2 * C.DEFAULT_NODE_BUBBLE_RADIUS);
if (node.visible) { if (node.visible) {
if (!rankSets[node.rank]) { if (!rankSets[node.rank]) {
rankSets[node.rank] = new Array<GraphNode>(node); rankSets[node.rank] = new Array<GraphNode>(node);
...@@ -204,8 +204,7 @@ export class GraphLayout { ...@@ -204,8 +204,7 @@ export class GraphLayout {
for (const node of rankSet) { for (const node of rankSet) {
if (node.visible) { if (node.visible) {
node.x = this.graphOccupation.occupyNode(node); node.x = this.graphOccupation.occupyNode(node);
const nodeTotalWidth = node.getTotalNodeWidth(); this.trace(`Node ${node.id} is placed between [${node.x}, ${node.x + node.getWidth()})`);
this.trace(`Node ${node.id} is placed between [${node.x}, ${node.x + nodeTotalWidth})`);
const staggeredFlooredI = Math.floor(placedCount++ % 3); const staggeredFlooredI = Math.floor(placedCount++ % 3);
const delta = C.MINIMUM_EDGE_SEPARATION * staggeredFlooredI; const delta = C.MINIMUM_EDGE_SEPARATION * staggeredFlooredI;
node.outputApproach += delta; node.outputApproach += delta;
...@@ -288,7 +287,7 @@ class GraphOccupation { ...@@ -288,7 +287,7 @@ class GraphOccupation {
} }
public occupyNode(node: GraphNode): number { public occupyNode(node: GraphNode): number {
const width = node.getTotalNodeWidth(); const width = node.getWidth();
const margin = C.MINIMUM_EDGE_SEPARATION; const margin = C.MINIMUM_EDGE_SEPARATION;
const paddedWidth = width + 2 * margin; const paddedWidth = width + 2 * margin;
const [direction, position] = this.getPlacementHint(node); const [direction, position] = this.getPlacementHint(node);
......
...@@ -6,28 +6,15 @@ import * as C from "./common/constants"; ...@@ -6,28 +6,15 @@ import * as C from "./common/constants";
import { GraphPhase, GraphStateType } from "./phases/graph-phase/graph-phase"; import { GraphPhase, GraphStateType } from "./phases/graph-phase/graph-phase";
import { GraphEdge } from "./phases/graph-phase/graph-edge"; import { GraphEdge } from "./phases/graph-phase/graph-edge";
import { GraphNode } from "./phases/graph-phase/graph-node"; import { GraphNode } from "./phases/graph-phase/graph-node";
import { MovableContainer } from "./movable-container";
export class Graph { export class Graph extends MovableContainer<GraphPhase> {
graphPhase: GraphPhase;
nodeMap: Array<GraphNode>; nodeMap: Array<GraphNode>;
minGraphX: number;
maxGraphX: number;
minGraphY: number;
maxGraphY: number;
maxGraphNodeX: number;
maxBackEdgeNumber: number; maxBackEdgeNumber: number;
width: number;
height: number;
constructor(graphPhase: GraphPhase) { constructor(graphPhase: GraphPhase) {
this.graphPhase = graphPhase; super(graphPhase);
this.nodeMap = graphPhase.nodeIdToNodeMap; this.nodeMap = graphPhase.nodeIdToNodeMap;
this.minGraphX = 0;
this.maxGraphX = 1;
this.minGraphY = 0;
this.maxGraphY = 1;
this.width = 1;
this.height = 1;
} }
public *nodes(func = (n: GraphNode) => true) { public *nodes(func = (n: GraphNode) => true) {
...@@ -65,12 +52,11 @@ export class Graph { ...@@ -65,12 +52,11 @@ export class Graph {
if (!node.visible) continue; if (!node.visible) continue;
this.minGraphX = Math.min(this.minGraphX, node.x); this.minGraphX = Math.min(this.minGraphX, node.x);
this.maxGraphNodeX = Math.max(this.maxGraphNodeX, this.maxGraphNodeX = Math.max(this.maxGraphNodeX, node.x + node.getWidth());
node.x + node.getTotalNodeWidth());
this.minGraphY = Math.min(this.minGraphY, node.y - C.NODE_INPUT_WIDTH); this.minGraphY = Math.min(this.minGraphY, node.y - C.NODE_INPUT_WIDTH);
this.maxGraphY = Math.max(this.maxGraphY, this.maxGraphY = Math.max(this.maxGraphY, node.y + node.getHeight(showTypes)
node.y + node.getNodeHeight(showTypes) + C.NODE_INPUT_WIDTH); + C.NODE_INPUT_WIDTH);
} }
this.maxGraphX = this.maxGraphNodeX + this.maxBackEdgeNumber this.maxGraphX = this.maxGraphNodeX + this.maxBackEdgeNumber
...@@ -97,8 +83,4 @@ export class Graph { ...@@ -97,8 +83,4 @@ export class Graph {
); );
} }
} }
public isRendered(): boolean {
return this.graphPhase.rendered;
}
} }
...@@ -13,6 +13,7 @@ import { GraphPhase } from "./phases/graph-phase/graph-phase"; ...@@ -13,6 +13,7 @@ import { GraphPhase } from "./phases/graph-phase/graph-phase";
import { GraphNode } from "./phases/graph-phase/graph-node"; import { GraphNode } from "./phases/graph-phase/graph-node";
import { storageGetItem, storageSetItem } from "./common/util"; import { storageGetItem, storageSetItem } from "./common/util";
import { PhaseType } from "./phases/phase"; import { PhaseType } from "./phases/phase";
import { TurboshaftGraphView } from "./views/turboshaft-graph-view";
const toolboxHTML = ` const toolboxHTML = `
<div class="graph-toolbox"> <div class="graph-toolbox">
...@@ -28,6 +29,7 @@ export class GraphMultiView extends View { ...@@ -28,6 +29,7 @@ export class GraphMultiView extends View {
sourceResolver: SourceResolver; sourceResolver: SourceResolver;
selectionBroker: SelectionBroker; selectionBroker: SelectionBroker;
graph: GraphView; graph: GraphView;
turboshaftGraph: TurboshaftGraphView;
schedule: ScheduleView; schedule: ScheduleView;
sequence: SequenceView; sequence: SequenceView;
selectMenu: HTMLSelectElement; selectMenu: HTMLSelectElement;
...@@ -59,6 +61,8 @@ export class GraphMultiView extends View { ...@@ -59,6 +61,8 @@ export class GraphMultiView extends View {
searchInput.setAttribute("value", storageGetItem("lastSearch", "", false)); searchInput.setAttribute("value", storageGetItem("lastSearch", "", false));
this.graph = new GraphView(this.divNode, selectionBroker, view.displayPhaseByName.bind(this), this.graph = new GraphView(this.divNode, selectionBroker, view.displayPhaseByName.bind(this),
toolbox.querySelector(".graph-toolbox")); toolbox.querySelector(".graph-toolbox"));
this.turboshaftGraph = new TurboshaftGraphView(this.divNode, selectionBroker,
view.displayPhaseByName.bind(this), toolbox.querySelector(".graph-toolbox"));
this.schedule = new ScheduleView(this.divNode, selectionBroker); this.schedule = new ScheduleView(this.divNode, selectionBroker);
this.sequence = new SequenceView(this.divNode, selectionBroker); this.sequence = new SequenceView(this.divNode, selectionBroker);
this.selectMenu = toolbox.querySelector("#phase-select") as HTMLSelectElement; this.selectMenu = toolbox.querySelector("#phase-select") as HTMLSelectElement;
...@@ -96,6 +100,8 @@ export class GraphMultiView extends View { ...@@ -96,6 +100,8 @@ export class GraphMultiView extends View {
private displayPhase(phase: GenericPhase, selection?: Map<string, GraphNode>): void { private displayPhase(phase: GenericPhase, selection?: Map<string, GraphNode>): void {
if (phase.type == PhaseType.Graph) { if (phase.type == PhaseType.Graph) {
this.displayPhaseView(this.graph, phase, selection); this.displayPhaseView(this.graph, phase, selection);
} else if (phase.type == PhaseType.TurboshaftGraph) {
this.displayPhaseView(this.turboshaftGraph, phase, selection);
} else if (phase.type == PhaseType.Schedule) { } else if (phase.type == PhaseType.Schedule) {
this.displayPhaseView(this.schedule, phase, selection); this.displayPhaseView(this.schedule, phase, selection);
} else if (phase.type == PhaseType.Sequence) { } else if (phase.type == PhaseType.Sequence) {
...@@ -120,7 +126,7 @@ export class GraphMultiView extends View { ...@@ -120,7 +126,7 @@ export class GraphMultiView extends View {
let nextPhaseIndex = this.selectMenu.selectedIndex + 1; let nextPhaseIndex = this.selectMenu.selectedIndex + 1;
while (nextPhaseIndex < this.sourceResolver.phases.length) { while (nextPhaseIndex < this.sourceResolver.phases.length) {
const nextPhase = this.sourceResolver.getPhase(nextPhaseIndex); const nextPhase = this.sourceResolver.getPhase(nextPhaseIndex);
if (nextPhase.type == PhaseType.Graph) { if (nextPhase.isGraph()) {
this.selectMenu.selectedIndex = nextPhaseIndex; this.selectMenu.selectedIndex = nextPhaseIndex;
storageSetItem("lastSelectedPhase", nextPhaseIndex); storageSetItem("lastSelectedPhase", nextPhaseIndex);
this.displayPhase(nextPhase); this.displayPhase(nextPhase);
...@@ -134,7 +140,7 @@ export class GraphMultiView extends View { ...@@ -134,7 +140,7 @@ export class GraphMultiView extends View {
let previousPhaseIndex = this.selectMenu.selectedIndex - 1; let previousPhaseIndex = this.selectMenu.selectedIndex - 1;
while (previousPhaseIndex >= 0) { while (previousPhaseIndex >= 0) {
const previousPhase = this.sourceResolver.getPhase(previousPhaseIndex); const previousPhase = this.sourceResolver.getPhase(previousPhaseIndex);
if (previousPhase.type === PhaseType.Graph) { if (previousPhase.isGraph()) {
this.selectMenu.selectedIndex = previousPhaseIndex; this.selectMenu.selectedIndex = previousPhaseIndex;
storageSetItem("lastSelectedPhase", previousPhaseIndex); storageSetItem("lastSelectedPhase", previousPhaseIndex);
this.displayPhase(previousPhase); this.displayPhase(previousPhase);
......
// Copyright 2022 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 { GraphPhase } from "./phases/graph-phase/graph-phase";
import { TurboshaftGraphPhase } from "./phases/turboshaft-graph-phase/turboshaft-graph-phase";
export abstract class MovableContainer<GraphPhaseType extends GraphPhase | TurboshaftGraphPhase> {
graphPhase: GraphPhaseType;
minGraphX: number;
maxGraphX: number;
maxGraphNodeX: number;
minGraphY: number;
maxGraphY: number;
width: number;
height: number;
public abstract redetermineGraphBoundingBox(extendHeight: boolean):
[[number, number], [number, number]];
constructor(graphPhase: GraphPhaseType) {
this.graphPhase = graphPhase;
this.minGraphX = 0;
this.maxGraphX = 1;
this.minGraphY = 0;
this.maxGraphY = 1;
this.width = 1;
this.height = 1;
}
public isRendered(): boolean {
return this.graphPhase.rendered;
}
}
...@@ -2,11 +2,15 @@ ...@@ -2,11 +2,15 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import * as C from "./common/constants";
import { measureText } from "./common/util"; import { measureText } from "./common/util";
import { GraphEdge } from "./phases/graph-phase/graph-edge"; import { GraphEdge } from "./phases/graph-phase/graph-edge";
import { TurboshaftGraphEdge } from "./phases/turboshaft-graph-phase/turboshaft-graph-edge"; import { TurboshaftGraphEdge } from "./phases/turboshaft-graph-phase/turboshaft-graph-edge";
import { TurboshaftGraphNode } from "./phases/turboshaft-graph-phase/turboshaft-graph-node";
import { TurboshaftGraphBlock } from "./phases/turboshaft-graph-phase/turboshaft-graph-block";
export abstract class Node<EdgeType extends GraphEdge | TurboshaftGraphEdge> { export abstract class Node<EdgeType extends GraphEdge | TurboshaftGraphEdge<TurboshaftGraphNode
| TurboshaftGraphBlock>> {
id: number; id: number;
displayLabel: string; displayLabel: string;
inputs: Array<EdgeType>; inputs: Array<EdgeType>;
...@@ -15,7 +19,9 @@ export abstract class Node<EdgeType extends GraphEdge | TurboshaftGraphEdge> { ...@@ -15,7 +19,9 @@ export abstract class Node<EdgeType extends GraphEdge | TurboshaftGraphEdge> {
x: number; x: number;
y: number; y: number;
labelBox: { width: number, height: number }; labelBox: { width: number, height: number };
visitOrderWithinRank: number;
public abstract getHeight(extendHeight: boolean): number;
public abstract getWidth(): number;
constructor(id: number, displayLabel?: string) { constructor(id: number, displayLabel?: string) {
this.id = id; this.id = id;
...@@ -26,20 +32,22 @@ export abstract class Node<EdgeType extends GraphEdge | TurboshaftGraphEdge> { ...@@ -26,20 +32,22 @@ export abstract class Node<EdgeType extends GraphEdge | TurboshaftGraphEdge> {
this.x = 0; this.x = 0;
this.y = 0; this.y = 0;
this.labelBox = measureText(this.displayLabel); this.labelBox = measureText(this.displayLabel);
this.visitOrderWithinRank = 0;
} }
public areAnyOutputsVisible(): number { public areAnyOutputsVisible(): OutputVisibilityType {
// TODO (danylo boiko) Move 0, 1, 2 logic to enum
let visibleCount = 0; let visibleCount = 0;
for (const edge of this.outputs) { for (const edge of this.outputs) {
if (edge.isVisible()) { if (edge.isVisible()) {
++visibleCount; ++visibleCount;
} }
} }
if (this.outputs.length === visibleCount) return 2; if (this.outputs.length == visibleCount) {
if (visibleCount !== 0) return 1; return OutputVisibilityType.AllNodesVisible;
return 0; }
if (visibleCount != 0) {
return OutputVisibilityType.SomeNodesVisible;
}
return OutputVisibilityType.NoVisibleNodes;
} }
public setOutputVisibility(visibility: boolean): boolean { public setOutputVisibility(visibility: boolean): boolean {
...@@ -64,6 +72,15 @@ export abstract class Node<EdgeType extends GraphEdge | TurboshaftGraphEdge> { ...@@ -64,6 +72,15 @@ export abstract class Node<EdgeType extends GraphEdge | TurboshaftGraphEdge> {
return false; return false;
} }
public getInputX(index: number): number {
return this.getWidth() - (C.NODE_INPUT_WIDTH / 2) +
(index - this.inputs.length + 1) * C.NODE_INPUT_WIDTH;
}
public getOutputX(): number {
return this.getWidth() - (C.NODE_INPUT_WIDTH / 2);
}
public identifier(): string { public identifier(): string {
return `${this.id}`; return `${this.id}`;
} }
...@@ -72,3 +89,9 @@ export abstract class Node<EdgeType extends GraphEdge | TurboshaftGraphEdge> { ...@@ -72,3 +89,9 @@ export abstract class Node<EdgeType extends GraphEdge | TurboshaftGraphEdge> {
return `N${this.id}`; return `N${this.id}`;
} }
} }
export enum OutputVisibilityType {
NoVisibleNodes,
SomeNodesVisible,
AllNodesVisible
}
...@@ -32,7 +32,7 @@ export class GraphEdge extends Edge<GraphNode> { ...@@ -32,7 +32,7 @@ export class GraphEdge extends Edge<GraphNode> {
} }
const inputOffset = C.MINIMUM_EDGE_SEPARATION * (index + 1); const inputOffset = C.MINIMUM_EDGE_SEPARATION * (index + 1);
return target.x < source.x return target.x < source.x
? target.x + target.getTotalNodeWidth() + inputOffset ? target.x + target.getWidth() + inputOffset
: target.x - inputOffset; : target.x - inputOffset;
} }
...@@ -43,7 +43,7 @@ export class GraphEdge extends Edge<GraphNode> { ...@@ -43,7 +43,7 @@ export class GraphEdge extends Edge<GraphNode> {
const arrowheadHeight = 7; const arrowheadHeight = 7;
const inputY = target.y - 2 * C.DEFAULT_NODE_BUBBLE_RADIUS - arrowheadHeight; const inputY = target.y - 2 * C.DEFAULT_NODE_BUBBLE_RADIUS - arrowheadHeight;
const outputX = source.x + source.getOutputX(); const outputX = source.x + source.getOutputX();
const outputY = source.y + source.getNodeHeight(showTypes) + C.DEFAULT_NODE_BUBBLE_RADIUS; const outputY = source.y + source.getHeight(showTypes) + C.DEFAULT_NODE_BUBBLE_RADIUS;
let inputApproach = target.getInputApproach(this.index); let inputApproach = target.getInputApproach(this.index);
const outputApproach = source.getOutputApproach(showTypes); const outputApproach = source.getOutputApproach(showTypes);
const horizontalPos = this.getInputHorizontalPosition(graph, showTypes); const horizontalPos = this.getInputHorizontalPosition(graph, showTypes);
......
...@@ -15,6 +15,7 @@ export class GraphNode extends Node<GraphEdge> { ...@@ -15,6 +15,7 @@ export class GraphNode extends Node<GraphEdge> {
cfg: boolean; cfg: boolean;
width: number; width: number;
normalHeight: number; normalHeight: number;
visitOrderWithinRank: number;
constructor(nodeLabel: NodeLabel) { constructor(nodeLabel: NodeLabel) {
super(nodeLabel.id, nodeLabel.getDisplayLabel()); super(nodeLabel.id, nodeLabel.getDisplayLabel());
...@@ -28,6 +29,18 @@ export class GraphNode extends Node<GraphEdge> { ...@@ -28,6 +29,18 @@ export class GraphNode extends Node<GraphEdge> {
this.width = alignUp(innerWidth + C.NODE_INPUT_WIDTH * 2, C.NODE_INPUT_WIDTH); this.width = alignUp(innerWidth + C.NODE_INPUT_WIDTH * 2, C.NODE_INPUT_WIDTH);
const innerHeight = Math.max(this.labelBox.height, typeBox.height); const innerHeight = Math.max(this.labelBox.height, typeBox.height);
this.normalHeight = innerHeight + 20; this.normalHeight = innerHeight + 20;
this.visitOrderWithinRank = 0;
}
public getHeight(showTypes: boolean): number {
if (showTypes) {
return this.normalHeight + this.labelBox.height;
}
return this.normalHeight;
}
public getWidth(): number {
return Math.max(this.inputs.length * C.NODE_INPUT_WIDTH, this.width);
} }
public isControl(): boolean { public isControl(): boolean {
...@@ -68,10 +81,6 @@ export class GraphNode extends Node<GraphEdge> { ...@@ -68,10 +81,6 @@ export class GraphNode extends Node<GraphEdge> {
this.isJavaScript() || this.isSimplified()); this.isJavaScript() || this.isSimplified());
} }
public getTotalNodeWidth(): number {
return Math.max(this.inputs.length * C.NODE_INPUT_WIDTH, this.width);
}
public getTitle(): string { public getTitle(): string {
return this.nodeLabel.getTitle(); return this.nodeLabel.getTitle();
} }
...@@ -105,27 +114,11 @@ export class GraphNode extends Node<GraphEdge> { ...@@ -105,27 +114,11 @@ export class GraphNode extends Node<GraphEdge> {
(index % 4) * C.MINIMUM_EDGE_SEPARATION - C.DEFAULT_NODE_BUBBLE_RADIUS; (index % 4) * C.MINIMUM_EDGE_SEPARATION - C.DEFAULT_NODE_BUBBLE_RADIUS;
} }
public getNodeHeight(showTypes: boolean): number {
if (showTypes) {
return this.normalHeight + this.labelBox.height;
}
return this.normalHeight;
}
public getOutputApproach(showTypes: boolean): number { public getOutputApproach(showTypes: boolean): number {
return this.y + this.outputApproach + this.getNodeHeight(showTypes) + return this.y + this.outputApproach + this.getHeight(showTypes) +
+ C.DEFAULT_NODE_BUBBLE_RADIUS; + C.DEFAULT_NODE_BUBBLE_RADIUS;
} }
public getInputX(index: number): number {
return this.getTotalNodeWidth() - (C.NODE_INPUT_WIDTH / 2) +
(index - this.inputs.length + 1) * C.NODE_INPUT_WIDTH;
}
public getOutputX(): number {
return this.getTotalNodeWidth() - (C.NODE_INPUT_WIDTH / 2);
}
public hasBackEdges(): boolean { public hasBackEdges(): boolean {
return (this.nodeLabel.opcode === "Loop") || return (this.nodeLabel.opcode === "Loop") ||
((this.nodeLabel.opcode === "Phi" || this.nodeLabel.opcode === "EffectPhi" || ((this.nodeLabel.opcode === "Phi" || this.nodeLabel.opcode === "EffectPhi" ||
......
...@@ -10,6 +10,11 @@ export abstract class Phase { ...@@ -10,6 +10,11 @@ export abstract class Phase {
this.name = name; this.name = name;
this.type = type; this.type = type;
} }
public isGraph(): boolean {
return this.type == PhaseType.Graph ||
this.type == PhaseType.TurboshaftGraph;
}
} }
export enum PhaseType { export enum PhaseType {
...@@ -20,3 +25,8 @@ export enum PhaseType { ...@@ -20,3 +25,8 @@ export enum PhaseType {
Sequence = "sequence", Sequence = "sequence",
Schedule = "schedule" Schedule = "schedule"
} }
export enum GraphStateType {
NeedToFullRebuild,
Cached
}
...@@ -3,21 +3,38 @@ ...@@ -3,21 +3,38 @@
// found in the LICENSE file. // found in the LICENSE file.
import { TurboshaftGraphNode } from "./turboshaft-graph-node"; import { TurboshaftGraphNode } from "./turboshaft-graph-node";
import { Node } from "../../node";
import { TurboshaftGraphEdge } from "./turboshaft-graph-edge";
export class TurboshaftGraphBlock { export class TurboshaftGraphBlock extends Node<TurboshaftGraphEdge<TurboshaftGraphBlock>> {
id: string;
type: TurboshaftGraphBlockType; type: TurboshaftGraphBlockType;
deferred: boolean; deferred: boolean;
predecessors: Array<string>; predecessors: Array<string>;
nodes: Array<TurboshaftGraphNode>; nodes: Array<TurboshaftGraphNode>;
constructor(id: string, type: TurboshaftGraphBlockType, deferred: boolean, constructor(id: number, type: TurboshaftGraphBlockType, deferred: boolean,
predecessors: Array<string>) { predecessors: Array<string>) {
this.id = id; super(id, `${type} ${id}${deferred ? " (deferred)" : ""}`);
this.type = type; this.type = type;
this.deferred = deferred; this.deferred = deferred;
this.predecessors = predecessors ?? new Array<string>(); this.predecessors = predecessors ?? new Array<string>();
this.nodes = new Array<TurboshaftGraphNode>(); this.nodes = new Array<TurboshaftGraphNode>();
this.visible = true;
}
public getHeight(showProperties: boolean): number {
return this.nodes.reduce<number>((accumulator: number, node: TurboshaftGraphNode) => {
return accumulator + node.getHeight(showProperties);
}, this.labelBox.height);
}
public getWidth(): number {
const maxWidth = Math.max(...this.nodes.map((node: TurboshaftGraphNode) => node.getWidth()));
return Math.max(maxWidth, this.labelBox.width) + 50;
}
public toString(): string {
return `B${this.id}`;
} }
} }
......
...@@ -4,9 +4,18 @@ ...@@ -4,9 +4,18 @@
import { TurboshaftGraphNode } from "./turboshaft-graph-node"; import { TurboshaftGraphNode } from "./turboshaft-graph-node";
import { Edge } from "../../edge"; import { Edge } from "../../edge";
import { TurboshaftGraphBlock } from "./turboshaft-graph-block";
export class TurboshaftGraphEdge extends Edge<TurboshaftGraphNode> { export class TurboshaftGraphEdge<Type extends TurboshaftGraphNode | TurboshaftGraphBlock> extends
constructor(target: TurboshaftGraphNode, source: TurboshaftGraphNode) { Edge<Type> {
constructor(target: Type, source: Type) {
super(target, source); super(target, source);
this.visible = target.visible && source.visible;
}
public toString(idx?: number): string {
if (idx !== null) return `${this.source.id},${idx},${this.target.id}`;
return `${this.source.id},${this.target.id}`;
} }
} }
...@@ -2,19 +2,78 @@ ...@@ -2,19 +2,78 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import * as C from "../../common/constants";
import { measureText } from "../../common/util";
import { TurboshaftGraphEdge } from "./turboshaft-graph-edge"; import { TurboshaftGraphEdge } from "./turboshaft-graph-edge";
import { TurboshaftGraphBlock } from "./turboshaft-graph-block"; import { TurboshaftGraphBlock } from "./turboshaft-graph-block";
import { Node } from "../../node"; import { Node } from "../../node";
export class TurboshaftGraphNode extends Node<TurboshaftGraphEdge> { export class TurboshaftGraphNode extends Node<TurboshaftGraphEdge<TurboshaftGraphNode>> {
title: string; title: string;
block: TurboshaftGraphBlock; block: TurboshaftGraphBlock;
opPropertiesType: OpPropertiesType;
properties: string; properties: string;
constructor(id: number, title: string, block: TurboshaftGraphBlock, properties: string) { constructor(id: number, title: string, block: TurboshaftGraphBlock,
super(id); opPropertiesType: OpPropertiesType, properties: string) {
super(id, `${id} ${title}`);
this.title = title; this.title = title;
this.block = block; this.block = block;
this.opPropertiesType = opPropertiesType;
this.properties = properties; this.properties = properties;
this.visible = true;
} }
public getHeight(showProperties: boolean): number {
if (this.properties && showProperties) {
return this.labelBox.height * 2;
}
return this.labelBox.height;
}
public getWidth(): number {
const measure = measureText(
`${this.getInlineLabel()}[${this.getPropertiesTypeAbbreviation()}]`
);
return Math.max(this.inputs.length * C.NODE_INPUT_WIDTH, measure.width);
}
public getInlineLabel(): string {
if (this.inputs.length == 0) return `${this.id} ${this.title}`;
return `${this.id} ${this.title}(${this.inputs.map(i => i.source.id).join(",")})`;
}
public getReadableProperties(blockWidth: number): string {
const propertiesWidth = measureText(this.properties).width;
if (blockWidth > propertiesWidth) return this.properties;
const widthOfOneSymbol = Math.floor(propertiesWidth / this.properties.length);
const lengthOfReadableProperties = Math.floor(blockWidth / widthOfOneSymbol);
return `${this.properties.slice(0, lengthOfReadableProperties - 3)}..`;
}
public getPropertiesTypeAbbreviation(): string {
switch (this.opPropertiesType) {
case OpPropertiesType.Pure:
return "P";
case OpPropertiesType.Reading:
return "R";
case OpPropertiesType.Writing:
return "W";
case OpPropertiesType.CanDeopt:
return "CD";
case OpPropertiesType.AnySideEffects:
return "ASE";
case OpPropertiesType.BlockTerminator:
return "BT";
}
}
}
export enum OpPropertiesType {
Pure = "Pure",
Reading = "Reading",
Writing = "Writing",
CanDeopt = "CanDeopt",
AnySideEffects = "AnySideEffects",
BlockTerminator = "BlockTerminator"
} }
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import { Phase, PhaseType } from "../phase"; import { GraphStateType, Phase, PhaseType } from "../phase";
import { TurboshaftGraphNode } from "./turboshaft-graph-node"; import { TurboshaftGraphNode } from "./turboshaft-graph-node";
import { TurboshaftGraphEdge } from "./turboshaft-graph-edge"; import { TurboshaftGraphEdge } from "./turboshaft-graph-edge";
import { TurboshaftGraphBlock } from "./turboshaft-graph-block"; import { TurboshaftGraphBlock } from "./turboshaft-graph-block";
...@@ -10,8 +10,12 @@ import { TurboshaftGraphBlock } from "./turboshaft-graph-block"; ...@@ -10,8 +10,12 @@ import { TurboshaftGraphBlock } from "./turboshaft-graph-block";
export class TurboshaftGraphPhase extends Phase { export class TurboshaftGraphPhase extends Phase {
highestBlockId: number; highestBlockId: number;
data: TurboshaftGraphData; data: TurboshaftGraphData;
stateType: GraphStateType;
layoutType: TurboshaftLayoutType;
nodeIdToNodeMap: Array<TurboshaftGraphNode>; nodeIdToNodeMap: Array<TurboshaftGraphNode>;
blockIdToBlockMap: Array<TurboshaftGraphBlock>; blockIdToBlockMap: Array<TurboshaftGraphBlock>;
rendered: boolean;
transform: { x: number, y: number, scale: number };
constructor(name: string, highestBlockId: number, data?: TurboshaftGraphData, constructor(name: string, highestBlockId: number, data?: TurboshaftGraphData,
nodeIdToNodeMap?: Array<TurboshaftGraphNode>, nodeIdToNodeMap?: Array<TurboshaftGraphNode>,
...@@ -19,8 +23,11 @@ export class TurboshaftGraphPhase extends Phase { ...@@ -19,8 +23,11 @@ export class TurboshaftGraphPhase extends Phase {
super(name, PhaseType.TurboshaftGraph); super(name, PhaseType.TurboshaftGraph);
this.highestBlockId = highestBlockId; this.highestBlockId = highestBlockId;
this.data = data ?? new TurboshaftGraphData(); this.data = data ?? new TurboshaftGraphData();
this.stateType = GraphStateType.NeedToFullRebuild;
this.layoutType = TurboshaftLayoutType.Inline;
this.nodeIdToNodeMap = nodeIdToNodeMap ?? new Array<TurboshaftGraphNode>(); this.nodeIdToNodeMap = nodeIdToNodeMap ?? new Array<TurboshaftGraphNode>();
this.blockIdToBlockMap = blockIdToBlockMap ?? new Array<TurboshaftGraphBlock>(); this.blockIdToBlockMap = blockIdToBlockMap ?? new Array<TurboshaftGraphBlock>();
this.rendered = false;
} }
public parseDataFromJSON(dataJson): void { public parseDataFromJSON(dataJson): void {
...@@ -32,18 +39,29 @@ export class TurboshaftGraphPhase extends Phase { ...@@ -32,18 +39,29 @@ export class TurboshaftGraphPhase extends Phase {
private parseBlocksFromJSON(blocksJson): void { private parseBlocksFromJSON(blocksJson): void {
for (const blockJson of blocksJson) { for (const blockJson of blocksJson) {
const block = new TurboshaftGraphBlock(blockJson.id, blockJson.type, // TODO (danylo boiko) Change type of block id in JSON output
const numId = Number(blockJson.id.substring(1));
const block = new TurboshaftGraphBlock(numId, blockJson.type,
blockJson.deferred, blockJson.predecessors); blockJson.deferred, blockJson.predecessors);
this.data.blocks.push(block); this.data.blocks.push(block);
this.blockIdToBlockMap[block.id] = block; this.blockIdToBlockMap[block.id] = block;
} }
for (const block of this.blockIdToBlockMap) {
for (const predecessor of block.predecessors) {
const source = this.blockIdToBlockMap[Number(predecessor.substring(1))];
const edge = new TurboshaftGraphEdge(block, source);
block.inputs.push(edge);
source.outputs.push(edge);
}
}
} }
private parseNodesFromJSON(nodesJson): void { private parseNodesFromJSON(nodesJson): void {
for (const nodeJson of nodesJson) { for (const nodeJson of nodesJson) {
const block = this.blockIdToBlockMap[nodeJson.block_id]; const numId = Number(nodeJson.block_id.substring(1));
const block = this.blockIdToBlockMap[numId];
const node = new TurboshaftGraphNode(nodeJson.id, nodeJson.title, const node = new TurboshaftGraphNode(nodeJson.id, nodeJson.title,
block, nodeJson.properties); block, nodeJson.op_properties_type, nodeJson.properties);
block.nodes.push(node); block.nodes.push(node);
this.data.nodes.push(node); this.data.nodes.push(node);
this.nodeIdToNodeMap[node.id] = node; this.nodeIdToNodeMap[node.id] = node;
...@@ -64,13 +82,19 @@ export class TurboshaftGraphPhase extends Phase { ...@@ -64,13 +82,19 @@ export class TurboshaftGraphPhase extends Phase {
export class TurboshaftGraphData { export class TurboshaftGraphData {
nodes: Array<TurboshaftGraphNode>; nodes: Array<TurboshaftGraphNode>;
edges: Array<TurboshaftGraphEdge>; edges: Array<TurboshaftGraphEdge<TurboshaftGraphNode>>;
blocks: Array<TurboshaftGraphBlock>; blocks: Array<TurboshaftGraphBlock>;
constructor(nodes?: Array<TurboshaftGraphNode>, edges?: Array<TurboshaftGraphEdge>, constructor(nodes?: Array<TurboshaftGraphNode>,
edges?: Array<TurboshaftGraphEdge<TurboshaftGraphNode>>,
blocks?: Array<TurboshaftGraphBlock>) { blocks?: Array<TurboshaftGraphBlock>) {
this.nodes = nodes ?? new Array<TurboshaftGraphNode>(); this.nodes = nodes ?? new Array<TurboshaftGraphNode>();
this.edges = edges ?? new Array<TurboshaftGraphEdge>(); this.edges = edges ?? new Array<TurboshaftGraphEdge<TurboshaftGraphNode>>();
this.blocks = blocks ?? new Array<TurboshaftGraphBlock>(); this.blocks = blocks ?? new Array<TurboshaftGraphBlock>();
} }
} }
export enum TurboshaftLayoutType {
Inline,
Nodes
}
import { TurboshaftGraph } from "./turboshaft-graph";
import { GraphStateType } from "./phases/phase";
import { TurboshaftLayoutType } from "./phases/turboshaft-graph-phase/turboshaft-graph-phase";
export class TurboshaftGraphLayout {
graph: TurboshaftGraph;
constructor(graph: TurboshaftGraph) {
this.graph = graph;
}
public rebuild(showProperties: boolean): void {
if (this.graph.graphPhase.stateType == GraphStateType.NeedToFullRebuild) {
switch (this.graph.graphPhase.layoutType) {
case TurboshaftLayoutType.Inline:
this.inlineRebuild(showProperties);
break;
default:
throw "Unsupported graph layout type";
}
this.graph.graphPhase.stateType = GraphStateType.Cached;
}
this.graph.graphPhase.rendered = true;
}
private inlineRebuild(showProperties): void {
// Useless logic to simulate blocks coordinates (will be replaced in the future)
let x = 0;
let y = 0;
for (const block of this.graph.blockMap) {
block.x = x;
block.y = y;
y += block.getHeight(showProperties) + 50;
if (y > 1800) {
x += block.getWidth() * 1.5;
y = 0;
}
}
}
}
// Copyright 2022 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 * as C from "./common/constants";
import { MovableContainer } from "./movable-container";
import { TurboshaftGraphPhase } from "./phases/turboshaft-graph-phase/turboshaft-graph-phase";
import { TurboshaftGraphNode } from "./phases/turboshaft-graph-phase/turboshaft-graph-node";
import { TurboshaftGraphBlock } from "./phases/turboshaft-graph-phase/turboshaft-graph-block";
import { TurboshaftGraphEdge } from "./phases/turboshaft-graph-phase/turboshaft-graph-edge";
export class TurboshaftGraph extends MovableContainer<TurboshaftGraphPhase> {
blockMap: Array<TurboshaftGraphBlock>;
nodeMap: Array<TurboshaftGraphNode>;
constructor(graphPhase: TurboshaftGraphPhase) {
super(graphPhase);
this.blockMap = graphPhase.blockIdToBlockMap;
this.nodeMap = graphPhase.nodeIdToNodeMap;
}
public *blocks(func = (b: TurboshaftGraphBlock) => true) {
for (const block of this.blockMap) {
if (!block || !func(block)) continue;
yield block;
}
}
public *nodes(func = (n: TurboshaftGraphNode) => true) {
for (const node of this.nodeMap) {
if (!node || !func(node)) continue;
yield node;
}
}
public *blocksEdges(func = (e: TurboshaftGraphEdge<TurboshaftGraphBlock>) => true) {
for (const block of this.blockMap) {
if (!block) continue;
for (const edge of block.inputs) {
if (!edge || func(edge)) continue;
yield edge;
}
}
}
public redetermineGraphBoundingBox(showProperties: boolean):
[[number, number], [number, number]] {
this.minGraphX = 0;
this.maxGraphNodeX = 1;
this.minGraphY = 0;
this.maxGraphY = 1;
for (const block of this.blocks()) {
if (!block.visible) continue;
this.minGraphX = Math.min(this.minGraphX, block.x);
this.maxGraphNodeX = Math.max(this.maxGraphNodeX, block.x + block.getWidth());
this.minGraphY = Math.min(this.minGraphY, block.y - C.NODE_INPUT_WIDTH);
this.maxGraphY = Math.max(this.maxGraphY, block.y + block.getHeight(showProperties)
+ C.NODE_INPUT_WIDTH);
}
this.maxGraphX = this.maxGraphNodeX + 3 * C.MINIMUM_EDGE_SEPARATION;
this.width = this.maxGraphX - this.minGraphX;
this.height = this.maxGraphY - this.minGraphY;
return [
[this.minGraphX - this.width / 2, this.minGraphY - this.height / 2],
[this.maxGraphX + this.width / 2, this.maxGraphY + this.height / 2]
];
}
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -39,7 +39,12 @@ ...@@ -39,7 +39,12 @@
"src/views/disassembly-view.ts", "src/views/disassembly-view.ts",
"src/views/text-view.ts", "src/views/text-view.ts",
"src/views/info-view.ts", "src/views/info-view.ts",
"src/views/movable-view.ts",
"src/views/turboshaft-graph-view.ts",
"src/origin.ts", "src/origin.ts",
"src/movable-container.ts",
"src/turboshaft-graph.ts",
"src/turboshaft-graph-layout.ts",
"src/position.ts", "src/position.ts",
"src/source.ts", "src/source.ts",
"src/node.ts", "src/node.ts",
......
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