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

[turbolizer] Parsing Turboshaft JSON output

- Added parsing Turboshaft JSON output
- Refactored node.ts, edge.ts, node-label.ts, turbo-visualizer.ts, tabs.ts

P.S.: graph-phase.ts will be moved to graph-phase folder in the next CL

Bug: v8:7327
Change-Id: Ida854307392a2d513c36f86869ea00cadcf3667c
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3706603
Commit-Queue: Danylo Boiko <danielboyko02@gmail.com>
Reviewed-by: 's avatarTobias Tebbi <tebbi@chromium.org>
Cr-Commit-Position: refs/heads/main@{#81264}
parent ba3a2890
...@@ -3,7 +3,15 @@ ...@@ -3,7 +3,15 @@
// found in the LICENSE file. // found in the LICENSE file.
export const MAX_RANK_SENTINEL = 0; export const MAX_RANK_SENTINEL = 0;
export const BEZIER_CONSTANT = 0.3;
export const GRAPH_MARGIN = 250; export const GRAPH_MARGIN = 250;
export const DEFAULT_NODE_BUBBLE_RADIUS = 12;
export const NODE_INPUT_WIDTH = 50;
export const MINIMUM_NODE_OUTPUT_APPROACH = 15;
export const MINIMUM_EDGE_SEPARATION = 20;
export const MINIMUM_NODE_INPUT_APPROACH = 15 + 2 * DEFAULT_NODE_BUBBLE_RADIUS;
export const DEFAULT_NODE_ROW_SEPARATION = 150;
export const TRACE_LAYOUT = false;
export const SOURCE_PANE_ID = "left"; export const SOURCE_PANE_ID = "left";
export const SOURCE_COLLAPSE_ID = "source-shrink"; export const SOURCE_COLLAPSE_ID = "source-shrink";
export const SOURCE_EXPAND_ID = "source-expand"; export const SOURCE_EXPAND_ID = "source-expand";
......
...@@ -2,100 +2,23 @@ ...@@ -2,100 +2,23 @@
// 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 { GNode, MINIMUM_EDGE_SEPARATION, DEFAULT_NODE_BUBBLE_RADIUS } from "./node"; import { GraphNode } from "./phases/graph-phase/graph-node";
import { Graph } from "./graph"; import { TurboshaftGraphNode } from "./phases/turboshaft-graph-phase/turboshaft-graph-node";
const BEZIER_CONSTANT = 0.3; export abstract class Edge<NodeType extends GraphNode | TurboshaftGraphNode> {
target: NodeType;
export class Edge { source: NodeType;
target: GNode;
source: GNode;
index: number;
type: string;
backEdgeNumber: number; backEdgeNumber: number;
visible: boolean; visible: boolean;
constructor(target: GNode, index: number, source: GNode, type: string) { constructor(target: NodeType, source: NodeType) {
this.target = target; this.target = target;
this.source = source; this.source = source;
this.index = index;
this.type = type;
this.backEdgeNumber = 0; this.backEdgeNumber = 0;
this.visible = false; this.visible = false;
} }
stringID() { public isVisible(): boolean {
return this.source.id + "," + this.index + "," + this.target.id;
}
isVisible() {
return this.visible && this.source.visible && this.target.visible; return this.visible && this.source.visible && this.target.visible;
} }
getInputHorizontalPosition(graph: Graph, showTypes: boolean) {
if (this.backEdgeNumber > 0) {
return graph.maxGraphNodeX + this.backEdgeNumber * MINIMUM_EDGE_SEPARATION;
}
const source = this.source;
const target = this.target;
const index = this.index;
const inputX = target.x + target.getInputX(index);
const inputApproach = target.getInputApproach(this.index);
const outputApproach = source.getOutputApproach(showTypes);
if (inputApproach > outputApproach) {
return inputX;
} else {
const inputOffset = MINIMUM_EDGE_SEPARATION * (index + 1);
return (target.x < source.x)
? (target.x + target.getTotalNodeWidth() + inputOffset)
: (target.x - inputOffset);
}
}
generatePath(graph: Graph, showTypes: boolean) {
const target = this.target;
const source = this.source;
const inputX = target.x + target.getInputX(this.index);
const arrowheadHeight = 7;
const inputY = target.y - 2 * DEFAULT_NODE_BUBBLE_RADIUS - arrowheadHeight;
const outputX = source.x + source.getOutputX();
const outputY = source.y + source.getNodeHeight(showTypes) + DEFAULT_NODE_BUBBLE_RADIUS;
let inputApproach = target.getInputApproach(this.index);
const outputApproach = source.getOutputApproach(showTypes);
const horizontalPos = this.getInputHorizontalPosition(graph, showTypes);
let result: string;
if (inputY < outputY) {
result = `M ${outputX} ${outputY}
L ${outputX} ${outputApproach}
L ${horizontalPos} ${outputApproach}`;
if (horizontalPos != inputX) {
result += `L ${horizontalPos} ${inputApproach}`;
} else {
if (inputApproach < outputApproach) {
inputApproach = outputApproach;
}
}
result += `L ${inputX} ${inputApproach}
L ${inputX} ${inputY}`;
} else {
const controlY = outputY + (inputY - outputY) * BEZIER_CONSTANT;
result = `M ${outputX} ${outputY}
C ${outputX} ${controlY},
${inputX} ${outputY},
${inputX} ${inputY}`;
}
return result;
}
isBackEdge() {
return this.target.hasBackEdges() && (this.target.rank < this.source.rank);
}
} }
export const edgeToStr = (e: Edge) => e.stringID();
This diff is collapsed.
import { GNode, MINIMUM_EDGE_SEPARATION } from "./node"; // Copyright 2022 the V8 project authors. All rights reserved.
import { Edge } from "./edge"; // 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 { GraphPhase } from "./phases/graph-phase"; import { GraphPhase } from "./phases/graph-phase";
import { GraphEdge } from "./phases/graph-phase/graph-edge";
import { GraphNode } from "./phases/graph-phase/graph-node";
export class Graph { export class Graph {
nodeMap: Array<GNode>; nodeMap: Array<GraphNode>;
minGraphX: number; minGraphX: number;
maxGraphX: number; maxGraphX: number;
minGraphY: number; minGraphY: number;
...@@ -23,14 +28,14 @@ export class Graph { ...@@ -23,14 +28,14 @@ export class Graph {
this.width = 1; this.width = 1;
this.height = 1; this.height = 1;
graphPhase.data.nodes.forEach((jsonNode: GNode) => { graphPhase.data.nodes.forEach((jsonNode: GraphNode) => {
this.nodeMap[jsonNode.id] = new GNode(jsonNode.nodeLabel); this.nodeMap[jsonNode.id] = new GraphNode(jsonNode.nodeLabel);
}); });
graphPhase.data.edges.forEach((e: any) => { graphPhase.data.edges.forEach((e: any) => {
const t = this.nodeMap[e.target.id]; const t = this.nodeMap[e.target.id];
const s = this.nodeMap[e.source.id]; const s = this.nodeMap[e.source.id];
const newEdge = new Edge(t, e.index, s, e.type); const newEdge = new GraphEdge(t, e.index, s, e.type);
t.inputs.push(newEdge); t.inputs.push(newEdge);
s.outputs.push(newEdge); s.outputs.push(newEdge);
if (e.type == 'control') { if (e.type == 'control') {
...@@ -40,14 +45,14 @@ export class Graph { ...@@ -40,14 +45,14 @@ export class Graph {
}); });
} }
*nodes(p = (n: GNode) => true) { *nodes(p = (n: GraphNode) => true) {
for (const node of this.nodeMap) { for (const node of this.nodeMap) {
if (!node || !p(node)) continue; if (!node || !p(node)) continue;
yield node; yield node;
} }
} }
*filteredEdges(p: (e: Edge) => boolean) { *filteredEdges(p: (e: GraphEdge) => boolean) {
for (const node of this.nodes()) { for (const node of this.nodes()) {
for (const edge of node.inputs) { for (const edge of node.inputs) {
if (p(edge)) yield edge; if (p(edge)) yield edge;
...@@ -55,7 +60,7 @@ export class Graph { ...@@ -55,7 +60,7 @@ export class Graph {
} }
} }
forEachEdge(p: (e: Edge) => void) { forEachEdge(p: (e: GraphEdge) => void) {
for (const node of this.nodeMap) { for (const node of this.nodeMap) {
if (!node) continue; if (!node) continue;
for (const edge of node.inputs) { for (const edge of node.inputs) {
...@@ -91,7 +96,7 @@ export class Graph { ...@@ -91,7 +96,7 @@ export class Graph {
} }
this.maxGraphX = this.maxGraphNodeX + this.maxGraphX = this.maxGraphNodeX +
this.maxBackEdgeNumber * MINIMUM_EDGE_SEPARATION; this.maxBackEdgeNumber * C.MINIMUM_EDGE_SEPARATION;
this.width = this.maxGraphX - this.minGraphX; this.width = this.maxGraphX - this.minGraphX;
this.height = this.maxGraphY - this.minGraphY; this.height = this.maxGraphY - this.minGraphY;
......
...@@ -8,8 +8,8 @@ import { SequenceView } from "./views/sequence-view"; ...@@ -8,8 +8,8 @@ import { SequenceView } from "./views/sequence-view";
import { SourceResolver } from "./source-resolver"; import { SourceResolver } from "./source-resolver";
import { SelectionBroker } from "./selection/selection-broker"; import { SelectionBroker } from "./selection/selection-broker";
import { PhaseView, View } from "./views/view"; import { PhaseView, View } from "./views/view";
import { GNode } from "./node";
import { GraphPhase } from "./phases/graph-phase"; import { GraphPhase } from "./phases/graph-phase";
import { GraphNode } from "./phases/graph-phase/graph-node";
const multiviewID = "multiview"; const multiviewID = "multiview";
...@@ -108,7 +108,7 @@ export class GraphMultiView extends View { ...@@ -108,7 +108,7 @@ export class GraphMultiView extends View {
this.displayPhase(this.sourceResolver.getPhase(initialPhaseIndex)); this.displayPhase(this.sourceResolver.getPhase(initialPhaseIndex));
} }
displayPhase(phase, selection?: Map<string, GNode>) { displayPhase(phase, selection?: Map<string, GraphNode>) {
if (phase.type == "graph") { if (phase.type == "graph") {
this.displayPhaseView(this.graph, phase, selection); this.displayPhaseView(this.graph, phase, selection);
} else if (phase.type == "schedule") { } else if (phase.type == "schedule") {
...@@ -118,13 +118,13 @@ export class GraphMultiView extends View { ...@@ -118,13 +118,13 @@ export class GraphMultiView extends View {
} }
} }
displayPhaseView(view: PhaseView, data, selection?: Map<string, GNode>) { displayPhaseView(view: PhaseView, data, selection?: Map<string, GraphNode>) {
const rememberedSelection = selection ? selection : this.hideCurrentPhase(); const rememberedSelection = selection ? selection : this.hideCurrentPhase();
view.initializeContent(data, rememberedSelection); view.initializeContent(data, rememberedSelection);
this.currentPhaseView = view; this.currentPhaseView = view;
} }
displayPhaseByName(phaseName, selection?: Map<string, GNode>) { displayPhaseByName(phaseName, selection?: Map<string, GraphNode>) {
const phaseId = this.sourceResolver.getPhaseIdByName(phaseName); const phaseId = this.sourceResolver.getPhaseIdByName(phaseName);
this.selectMenu.selectedIndex = phaseId; this.selectMenu.selectedIndex = phaseId;
this.displayPhase(this.sourceResolver.getPhase(phaseId), selection); this.displayPhase(this.sourceResolver.getPhase(phaseId), selection);
......
...@@ -2,16 +2,6 @@ ...@@ -2,16 +2,6 @@
// 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.
function formatOrigin(origin) {
if (origin.nodeId) {
return `#${origin.nodeId} in phase ${origin.phase}/${origin.reducer}`;
}
if (origin.bytecodePosition) {
return `Bytecode line ${origin.bytecodePosition} in phase ${origin.phase}/${origin.reducer}`;
}
return "unknown origin";
}
export class NodeLabel { export class NodeLabel {
id: number; id: number;
label: string; label: string;
...@@ -51,20 +41,14 @@ export class NodeLabel { ...@@ -51,20 +41,14 @@ export class NodeLabel {
if (this.opcode != that.opcode) return false; if (this.opcode != that.opcode) return false;
if (this.control != that.control) return false; if (this.control != that.control) return false;
if (this.opinfo != that.opinfo) return false; if (this.opinfo != that.opinfo) return false;
if (this.type != that.type) return false; return this.type == that.type;
return true;
} }
getTitle() { public getTitle(): string {
let propsString = ""; const propsString = this.properties === "" ? "no properties" : `[${this.properties}]`;
if (this.properties === "") { let title = `${this.title}\n${propsString}\n${this.opinfo}`;
propsString = "no properties";
} else {
propsString = "[" + this.properties + "]";
}
let title = this.title + "\n" + propsString + "\n" + this.opinfo;
if (this.origin) { if (this.origin) {
title += `\nOrigin: ${formatOrigin(this.origin)}`; title += `\nOrigin: ${this.origin.toString()}`;
} }
if (this.inplaceUpdatePhase) { if (this.inplaceUpdatePhase) {
title += `\nInplace update in phase: ${this.inplaceUpdatePhase}`; title += `\nInplace update in phase: ${this.inplaceUpdatePhase}`;
...@@ -72,15 +56,12 @@ export class NodeLabel { ...@@ -72,15 +56,12 @@ export class NodeLabel {
return title; return title;
} }
getDisplayLabel() { public getDisplayLabel(): string {
const result = `${this.id}: ${this.label}`; const label = `${this.id}: ${this.label}`;
if (result.length > 40) { return label.length > 40 ? `${this.id}: ${this.opcode}` : label;
return `${this.id}: ${this.opcode}`;
}
return result;
} }
setInplaceUpdatePhase(name: string): any { public setInplaceUpdatePhase(name: string): void {
this.inplaceUpdatePhase = name; this.inplaceUpdatePhase = name;
} }
} }
...@@ -2,182 +2,73 @@ ...@@ -2,182 +2,73 @@
// 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 { Edge } from "./edge"; import { measureText } from "./common/util";
import { NodeLabel } from "./node-label"; import { GraphEdge } from "./phases/graph-phase/graph-edge";
import { MAX_RANK_SENTINEL } from "./common/constants"; import { TurboshaftGraphEdge } from "./phases/turboshaft-graph-phase/turboshaft-graph-edge";
import { alignUp, measureText } from "./common/util";
export const DEFAULT_NODE_BUBBLE_RADIUS = 12; export abstract class Node<EdgeType extends GraphEdge | TurboshaftGraphEdge> {
export const NODE_INPUT_WIDTH = 50;
export const MINIMUM_NODE_OUTPUT_APPROACH = 15;
export const MINIMUM_EDGE_SEPARATION = 20;
const MINIMUM_NODE_INPUT_APPROACH = 15 + 2 * DEFAULT_NODE_BUBBLE_RADIUS;
export class GNode {
id: number; id: number;
nodeLabel: NodeLabel;
displayLabel: string; displayLabel: string;
inputs: Array<Edge>; inputs: Array<EdgeType>;
outputs: Array<Edge>; outputs: Array<EdgeType>;
visible: boolean; visible: boolean;
x: number; x: number;
y: number; y: number;
rank: number; labelBox: { width: number, height: number };
outputApproach: number;
cfg: boolean;
labelbbox: { width: number, height: number };
width: number;
normalheight: number;
visitOrderWithinRank: number; visitOrderWithinRank: number;
constructor(nodeLabel: NodeLabel) { constructor(id: number, displayLabel?: string) {
this.id = nodeLabel.id; this.id = id;
this.nodeLabel = nodeLabel; this.displayLabel = displayLabel;
this.displayLabel = nodeLabel.getDisplayLabel(); this.inputs = new Array<EdgeType>();
this.inputs = []; this.outputs = new Array<EdgeType>();
this.outputs = [];
this.visible = false; this.visible = false;
this.x = 0; this.x = 0;
this.y = 0; this.y = 0;
this.rank = MAX_RANK_SENTINEL; this.labelBox = measureText(this.displayLabel);
this.outputApproach = MINIMUM_NODE_OUTPUT_APPROACH;
// Every control node is a CFG node.
this.cfg = nodeLabel.control;
this.labelbbox = measureText(this.displayLabel);
const typebbox = measureText(this.getDisplayType());
const innerwidth = Math.max(this.labelbbox.width, typebbox.width);
this.width = alignUp(innerwidth + NODE_INPUT_WIDTH * 2,
NODE_INPUT_WIDTH);
const innerheight = Math.max(this.labelbbox.height, typebbox.height);
this.normalheight = innerheight + 20;
this.visitOrderWithinRank = 0; this.visitOrderWithinRank = 0;
} }
isControl() { public areAnyOutputsVisible(): number {
return this.nodeLabel.control; // TODO (danylo boiko) Move 0, 1, 2 logic to enum
} let visibleCount = 0;
isInput() { for (const edge of this.outputs) {
return this.nodeLabel.opcode == 'Parameter' || this.nodeLabel.opcode.endsWith('Constant'); if (edge.isVisible()) {
} ++visibleCount;
isLive() {
return this.nodeLabel.live !== false;
}
isJavaScript() {
return this.nodeLabel.opcode.startsWith('JS');
}
isSimplified() {
if (this.isJavaScript()) return false;
const opcode = this.nodeLabel.opcode;
return opcode.endsWith('Phi') ||
opcode.startsWith('Boolean') ||
opcode.startsWith('Number') ||
opcode.startsWith('String') ||
opcode.startsWith('Change') ||
opcode.startsWith('Object') ||
opcode.startsWith('Reference') ||
opcode.startsWith('Any') ||
opcode.endsWith('ToNumber') ||
(opcode == 'AnyToBoolean') ||
(opcode.startsWith('Load') && opcode.length > 4) ||
(opcode.startsWith('Store') && opcode.length > 5);
}
isMachine() {
return !(this.isControl() || this.isInput() ||
this.isJavaScript() || this.isSimplified());
}
getTotalNodeWidth() {
const inputWidth = this.inputs.length * NODE_INPUT_WIDTH;
return Math.max(inputWidth, this.width);
}
getTitle() {
return this.nodeLabel.getTitle();
}
getDisplayLabel() {
return this.nodeLabel.getDisplayLabel();
}
getType() {
return this.nodeLabel.type;
}
getDisplayType() {
let typeString = this.nodeLabel.type;
if (typeString == undefined) return "";
if (typeString.length > 24) {
typeString = typeString.substr(0, 25) + "...";
}
return typeString;
}
deepestInputRank() {
let deepestRank = 0;
this.inputs.forEach(function (e) {
if (e.isVisible() && !e.isBackEdge()) {
if (e.source.rank > deepestRank) {
deepestRank = e.source.rank;
}
} }
});
return deepestRank;
} }
areAnyOutputsVisible() { if (this.outputs.length === visibleCount) return 2;
let visibleCount = 0; if (visibleCount !== 0) return 1;
this.outputs.forEach(function (e) { if (e.isVisible())++visibleCount; });
if (this.outputs.length == visibleCount) return 2;
if (visibleCount != 0) return 1;
return 0; return 0;
} }
setOutputVisibility(v) {
public setOutputVisibility(visibility: boolean): boolean {
let result = false; let result = false;
this.outputs.forEach(function (e) { for (const edge of this.outputs) {
e.visible = v; edge.visible = visibility;
if (v) { if (visibility && !edge.target.visible) {
if (!e.target.visible) { edge.target.visible = true;
e.target.visible = true;
result = true; result = true;
} }
} }
});
return result; return result;
} }
setInputVisibility(i, v) {
const edge = this.inputs[i]; public setInputVisibility(edgeIdx: number, visibility: boolean): boolean {
edge.visible = v; const edge = this.inputs[edgeIdx];
if (v) { edge.visible = visibility;
if (!edge.source.visible) { if (visibility && !edge.source.visible) {
edge.source.visible = true; edge.source.visible = true;
return true; return true;
} }
}
return false; return false;
} }
getInputApproach(index) {
return this.y - MINIMUM_NODE_INPUT_APPROACH - public identifier(): string {
(index % 4) * MINIMUM_EDGE_SEPARATION - DEFAULT_NODE_BUBBLE_RADIUS; return `${this.id}`;
}
getNodeHeight(showTypes: boolean): number {
if (showTypes) {
return this.normalheight + this.labelbbox.height;
} else {
return this.normalheight;
}
}
getOutputApproach(showTypes: boolean) {
return this.y + this.outputApproach + this.getNodeHeight(showTypes) +
+ DEFAULT_NODE_BUBBLE_RADIUS;
}
getInputX(index) {
const result = this.getTotalNodeWidth() - (NODE_INPUT_WIDTH / 2) +
(index - this.inputs.length + 1) * NODE_INPUT_WIDTH;
return result;
}
getOutputX() {
return this.getTotalNodeWidth() - (NODE_INPUT_WIDTH / 2);
}
hasBackEdges() {
return (this.nodeLabel.opcode == "Loop") ||
((this.nodeLabel.opcode == "Phi" || this.nodeLabel.opcode == "EffectPhi" || this.nodeLabel.opcode == "InductionVariablePhi") &&
this.inputs[this.inputs.length - 1].source.nodeLabel.opcode == "Loop");
} }
public identifier = (): string => `${this.id}`; public toString(): string {
return `N${this.id}`;
}
} }
export const nodeToStr = (n: GNode) => "N" + n.id;
...@@ -20,8 +20,13 @@ export class NodeOrigin extends Origin { ...@@ -20,8 +20,13 @@ export class NodeOrigin extends Origin {
this.nodeId = nodeId; this.nodeId = nodeId;
} }
public identifier = (): string => `${this.nodeId}`; public identifier(): string {
public toString = (): string => `#${this.nodeId} in phase '${this.phase}/${this.reducer}'`; return `${this.nodeId}`;
}
public toString(): string {
return `#${this.nodeId} in phase '${this.phase}/${this.reducer}'`;
}
} }
export class BytecodeOrigin extends Origin { export class BytecodeOrigin extends Origin {
...@@ -32,8 +37,11 @@ export class BytecodeOrigin extends Origin { ...@@ -32,8 +37,11 @@ export class BytecodeOrigin extends Origin {
this.bytecodePosition = bytecodePosition; this.bytecodePosition = bytecodePosition;
} }
public identifier = (): string => `${this.bytecodePosition}`; public identifier(): string {
public toString = (): string => { return `${this.bytecodePosition}`;
}
public toString(): string {
return `Bytecode line ${this.bytecodePosition} in phase '${this.phase}/${this.reducer}'`; return `Bytecode line ${this.bytecodePosition} in phase '${this.phase}/${this.reducer}'`;
} }
} }
...@@ -4,33 +4,34 @@ ...@@ -4,33 +4,34 @@
import { Phase, PhaseType } from "./phase"; import { Phase, PhaseType } from "./phase";
import { NodeLabel } from "../node-label"; import { NodeLabel } from "../node-label";
import { GNode } from "../node";
import { Edge } from "../edge";
import { BytecodeOrigin, NodeOrigin } from "../origin"; import { BytecodeOrigin, NodeOrigin } from "../origin";
import { SourcePosition } from "../position"; import { SourcePosition } from "../position";
import { GraphNode } from "./graph-phase/graph-node";
import { GraphEdge } from "./graph-phase/graph-edge";
export class GraphPhase extends Phase { export class GraphPhase extends Phase {
highestNodeId: number; highestNodeId: number;
data: GraphData; data: GraphData;
nodeLabelMap: Array<NodeLabel>; nodeLabelMap: Array<NodeLabel>;
nodeIdToNodeMap: Array<GNode>; nodeIdToNodeMap: Array<GraphNode>;
constructor(name: string, highestNodeId: number, data?: GraphData, constructor(name: string, highestNodeId: number, data?: GraphData,
nodeLabelMap?: Array<NodeLabel>, nodeIdToNodeMap?: Array<GNode>) { nodeLabelMap?: Array<NodeLabel>, nodeIdToNodeMap?: Array<GraphNode>) {
super(name, PhaseType.Graph); super(name, PhaseType.Graph);
this.highestNodeId = highestNodeId; this.highestNodeId = highestNodeId;
this.data = data ?? new GraphData(); this.data = data ?? new GraphData();
this.nodeLabelMap = nodeLabelMap ?? new Array<NodeLabel>(); this.nodeLabelMap = nodeLabelMap ?? new Array<NodeLabel>();
this.nodeIdToNodeMap = nodeIdToNodeMap ?? new Array<GNode>(); this.nodeIdToNodeMap = nodeIdToNodeMap ?? new Array<GraphNode>();
} }
public parseDataFromJSON(dataJson, nodeLabelMap: Array<NodeLabel>): void { public parseDataFromJSON(dataJson, nodeLabelMap: Array<NodeLabel>): void {
this.data = new GraphData();
this.nodeIdToNodeMap = this.parseNodesFromJSON(dataJson.nodes, nodeLabelMap); this.nodeIdToNodeMap = this.parseNodesFromJSON(dataJson.nodes, nodeLabelMap);
this.parseEdgesFromJSON(dataJson.edges); this.parseEdgesFromJSON(dataJson.edges);
} }
private parseNodesFromJSON(nodesJSON, nodeLabelMap: Array<NodeLabel>): Array<GNode> { private parseNodesFromJSON(nodesJSON, nodeLabelMap: Array<NodeLabel>): Array<GraphNode> {
const nodeIdToNodeMap = new Array<GNode>(); const nodeIdToNodeMap = new Array<GraphNode>();
for (const node of nodesJSON) { for (const node of nodesJSON) {
let origin: NodeOrigin | BytecodeOrigin = null; let origin: NodeOrigin | BytecodeOrigin = null;
const jsonOrigin = node.origin; const jsonOrigin = node.origin;
...@@ -60,7 +61,7 @@ export class GraphPhase extends Phase { ...@@ -60,7 +61,7 @@ export class GraphPhase extends Phase {
} }
nodeLabelMap[label.id] = label; nodeLabelMap[label.id] = label;
} }
const newNode = new GNode(label); const newNode = new GraphNode(label);
this.data.nodes.push(newNode); this.data.nodes.push(newNode);
nodeIdToNodeMap[newNode.id] = newNode; nodeIdToNodeMap[newNode.id] = newNode;
} }
...@@ -71,7 +72,7 @@ export class GraphPhase extends Phase { ...@@ -71,7 +72,7 @@ export class GraphPhase extends Phase {
for (const edge of edgesJSON) { for (const edge of edgesJSON) {
const target = this.nodeIdToNodeMap[edge.target]; const target = this.nodeIdToNodeMap[edge.target];
const source = this.nodeIdToNodeMap[edge.source]; const source = this.nodeIdToNodeMap[edge.source];
const newEdge = new Edge(target, edge.index, source, edge.type); const newEdge = new GraphEdge(target, edge.index, source, edge.type);
this.data.edges.push(newEdge); this.data.edges.push(newEdge);
target.inputs.push(newEdge); target.inputs.push(newEdge);
source.outputs.push(newEdge); source.outputs.push(newEdge);
...@@ -83,11 +84,11 @@ export class GraphPhase extends Phase { ...@@ -83,11 +84,11 @@ export class GraphPhase extends Phase {
} }
export class GraphData { export class GraphData {
nodes: Array<GNode>; nodes: Array<GraphNode>;
edges: Array<Edge>; edges: Array<GraphEdge>;
constructor(nodes?: Array<GNode>, edges?: Array<Edge>) { constructor(nodes?: Array<GraphNode>, edges?: Array<GraphEdge>) {
this.nodes = nodes ?? new Array<GNode>(); this.nodes = nodes ?? new Array<GraphNode>();
this.edges = edges ?? new Array<Edge>(); this.edges = edges ?? new Array<GraphEdge>();
} }
} }
// 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 { Graph } from "../../graph";
import { Edge } from "../../edge";
import { GraphNode } from "./graph-node";
export class GraphEdge extends Edge<GraphNode> {
index: number;
type: string;
constructor(target: GraphNode, index: number, source: GraphNode, type: string) {
super(target, source);
this.index = index;
this.type = type;
}
public getInputHorizontalPosition(graph: Graph, showTypes: boolean): number {
if (this.backEdgeNumber > 0) {
return graph.maxGraphNodeX + this.backEdgeNumber * C.MINIMUM_EDGE_SEPARATION;
}
const source = this.source;
const target = this.target;
const index = this.index;
const inputX = target.x + target.getInputX(index);
const inputApproach = target.getInputApproach(this.index);
const outputApproach = source.getOutputApproach(showTypes);
if (inputApproach > outputApproach) {
return inputX;
}
const inputOffset = C.MINIMUM_EDGE_SEPARATION * (index + 1);
return target.x < source.x
? target.x + target.getTotalNodeWidth() + inputOffset
: target.x - inputOffset;
}
public generatePath(graph: Graph, showTypes: boolean): string {
const target = this.target;
const source = this.source;
const inputX = target.x + target.getInputX(this.index);
const arrowheadHeight = 7;
const inputY = target.y - 2 * C.DEFAULT_NODE_BUBBLE_RADIUS - arrowheadHeight;
const outputX = source.x + source.getOutputX();
const outputY = source.y + source.getNodeHeight(showTypes) + C.DEFAULT_NODE_BUBBLE_RADIUS;
let inputApproach = target.getInputApproach(this.index);
const outputApproach = source.getOutputApproach(showTypes);
const horizontalPos = this.getInputHorizontalPosition(graph, showTypes);
let path: string;
if (inputY < outputY) {
path = `M ${outputX} ${outputY}\nL ${outputX} ${outputApproach}\nL ${horizontalPos} ${outputApproach}`;
if (horizontalPos !== inputX) {
path += `L ${horizontalPos} ${inputApproach}`;
} else if (inputApproach < outputApproach) {
inputApproach = outputApproach;
}
path += `L ${inputX} ${inputApproach}\nL ${inputX} ${inputY}`;
} else {
const controlY = outputY + (inputY - outputY) * C.BEZIER_CONSTANT;
path = `M ${outputX} ${outputY}\nC ${outputX} ${controlY},\n${inputX} ${outputY},\n${inputX} ${inputY}`;
}
return path;
}
public isBackEdge(): boolean {
return this.target.hasBackEdges() && (this.target.rank < this.source.rank);
}
public toString(): string {
return `${this.source.id},${this.index},${this.target.id}`;
}
}
// 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 { alignUp, measureText } from "../../common/util";
import { NodeLabel } from "../../node-label";
import { Node } from "../../node";
import { GraphEdge } from "./graph-edge";
export class GraphNode extends Node<GraphEdge> {
nodeLabel: NodeLabel;
rank: number;
outputApproach: number;
cfg: boolean;
width: number;
normalHeight: number;
constructor(nodeLabel: NodeLabel) {
super(nodeLabel.id, nodeLabel.getDisplayLabel());
this.nodeLabel = nodeLabel;
this.rank = C.MAX_RANK_SENTINEL;
this.outputApproach = C.MINIMUM_NODE_OUTPUT_APPROACH;
// Every control node is a CFG node.
this.cfg = nodeLabel.control;
const typeBox = measureText(this.getDisplayType());
const innerWidth = Math.max(this.labelBox.width, typeBox.width);
this.width = alignUp(innerWidth + C.NODE_INPUT_WIDTH * 2, C.NODE_INPUT_WIDTH);
const innerHeight = Math.max(this.labelBox.height, typeBox.height);
this.normalHeight = innerHeight + 20;
}
public isControl(): boolean {
return this.nodeLabel.control;
}
public isInput(): boolean {
return this.nodeLabel.opcode === "Parameter" || this.nodeLabel.opcode.endsWith("Constant");
}
public isLive(): boolean {
return this.nodeLabel.live !== false;
}
public isJavaScript(): boolean {
return this.nodeLabel.opcode.startsWith("JS");
}
public isSimplified(): boolean {
if (this.isJavaScript()) return false;
const opcode = this.nodeLabel.opcode;
return opcode.endsWith("Phi") ||
opcode.startsWith("Boolean") ||
opcode.startsWith("Number") ||
opcode.startsWith("String") ||
opcode.startsWith("Change") ||
opcode.startsWith("Object") ||
opcode.startsWith("Reference") ||
opcode.startsWith("Any") ||
opcode.endsWith("ToNumber") ||
(opcode == "AnyToBoolean") ||
(opcode.startsWith("Load") && opcode.length > 4) ||
(opcode.startsWith("Store") && opcode.length > 5);
}
public isMachine(): boolean {
return !(this.isControl() || this.isInput() ||
this.isJavaScript() || this.isSimplified());
}
public getTotalNodeWidth(): number {
return Math.max(this.inputs.length * C.NODE_INPUT_WIDTH, this.width);
}
public getTitle(): string {
return this.nodeLabel.getTitle();
}
public getDisplayLabel(): string {
return this.nodeLabel.getDisplayLabel();
}
public getType(): string {
return this.nodeLabel.type;
}
public getDisplayType(): string {
const typeString = this.nodeLabel.type;
if (typeString == undefined) return "";
return typeString.length > 24 ? `${typeString.slice(0, 25)}...` : typeString;
}
public deepestInputRank(): number {
let deepestRank = 0;
for (const edge of this.inputs) {
if ((edge.isVisible() && !edge.isBackEdge()) && edge.source.rank > deepestRank) {
deepestRank = edge.source.rank;
}
}
return deepestRank;
}
public getInputApproach(index: number): number {
return this.y - C.MINIMUM_NODE_INPUT_APPROACH -
(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 {
return this.y + this.outputApproach + this.getNodeHeight(showTypes) +
+ 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 {
return (this.nodeLabel.opcode === "Loop") ||
((this.nodeLabel.opcode === "Phi" || this.nodeLabel.opcode === "EffectPhi" ||
this.nodeLabel.opcode === "InductionVariablePhi") &&
this.inputs[this.inputs.length - 1].source.nodeLabel.opcode === "Loop");
}
}
...@@ -2,8 +2,8 @@ ...@@ -2,8 +2,8 @@
// 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 { anyToString } from "../common/util"; import { anyToString } from "../common/util";
import { Phase, PhaseType } from "./phase";
export class InstructionsPhase extends Phase { export class InstructionsPhase extends Phase {
// Maps node ids to instruction ranges. // Maps node ids to instruction ranges.
......
// 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 { TurboshaftGraphNode } from "./turboshaft-graph-node";
export class TurboshaftGraphBlock {
id: string;
type: TurboshaftGraphBlockType;
deferred: boolean;
predecessors: Array<string>;
nodes: Array<TurboshaftGraphNode>;
constructor(id: string, type: TurboshaftGraphBlockType, deferred: boolean,
predecessors: Array<string>) {
this.id = id;
this.type = type;
this.deferred = deferred;
this.predecessors = predecessors ?? new Array<string>();
this.nodes = new Array<TurboshaftGraphNode>();
}
}
export enum TurboshaftGraphBlockType {
Loop = "LOOP",
Merge = "MERGE",
Block = "BLOCK"
}
...@@ -2,11 +2,11 @@ ...@@ -2,11 +2,11 @@
// 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 { TurboshaftGraphNode } from "./turboshaft-graph-node";
import { Edge } from "../../edge";
export class TurboshaftGraphPhase extends Phase { export class TurboshaftGraphEdge extends Edge<TurboshaftGraphNode> {
constructor(name: string) { constructor(target: TurboshaftGraphNode, source: TurboshaftGraphNode) {
super(name, PhaseType.TurboshaftGraph); super(target, source);
// To be continued ...
} }
} }
// 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 { TurboshaftGraphEdge } from "./turboshaft-graph-edge";
import { TurboshaftGraphBlock } from "./turboshaft-graph-block";
import { Node } from "../../node";
export class TurboshaftGraphNode extends Node<TurboshaftGraphEdge> {
title: string;
block: TurboshaftGraphBlock;
properties: string;
constructor(id: number, title: string, block: TurboshaftGraphBlock, properties: string) {
super(id);
this.title = title;
this.block = block;
this.properties = properties;
}
}
// 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 { Phase, PhaseType } from "../phase";
import { TurboshaftGraphNode } from "./turboshaft-graph-node";
import { TurboshaftGraphEdge } from "./turboshaft-graph-edge";
import { TurboshaftGraphBlock } from "./turboshaft-graph-block";
export class TurboshaftGraphPhase extends Phase {
highestBlockId: number;
data: TurboshaftGraphData;
nodeIdToNodeMap: Array<TurboshaftGraphNode>;
blockIdToBlockMap: Array<TurboshaftGraphBlock>;
constructor(name: string, highestBlockId: number, data?: TurboshaftGraphData,
nodeIdToNodeMap?: Array<TurboshaftGraphNode>,
blockIdToBlockMap?: Array<TurboshaftGraphBlock>) {
super(name, PhaseType.TurboshaftGraph);
this.highestBlockId = highestBlockId;
this.data = data ?? new TurboshaftGraphData();
this.nodeIdToNodeMap = nodeIdToNodeMap ?? new Array<TurboshaftGraphNode>();
this.blockIdToBlockMap = blockIdToBlockMap ?? new Array<TurboshaftGraphBlock>();
}
public parseDataFromJSON(dataJson): void {
this.data = new TurboshaftGraphData();
this.parseBlocksFromJSON(dataJson.blocks);
this.parseNodesFromJSON(dataJson.nodes);
this.parseEdgesFromJSON(dataJson.edges);
}
private parseBlocksFromJSON(blocksJson): void {
for (const blockJson of blocksJson) {
const block = new TurboshaftGraphBlock(blockJson.id, blockJson.type,
blockJson.deferred, blockJson.predecessors);
this.data.blocks.push(block);
this.blockIdToBlockMap[block.id] = block;
}
}
private parseNodesFromJSON(nodesJson): void {
for (const nodeJson of nodesJson) {
const block = this.blockIdToBlockMap[nodeJson.block_id];
const node = new TurboshaftGraphNode(nodeJson.id, nodeJson.title,
block, nodeJson.properties);
block.nodes.push(node);
this.data.nodes.push(node);
this.nodeIdToNodeMap[node.id] = node;
}
}
private parseEdgesFromJSON(edgesJson): void {
for (const edgeJson of edgesJson) {
const target = this.nodeIdToNodeMap[edgeJson.target];
const source = this.nodeIdToNodeMap[edgeJson.source];
const edge = new TurboshaftGraphEdge(target, source);
this.data.edges.push(edge);
target.inputs.push(edge);
source.outputs.push(edge);
}
}
}
export class TurboshaftGraphData {
nodes: Array<TurboshaftGraphNode>;
edges: Array<TurboshaftGraphEdge>;
blocks: Array<TurboshaftGraphBlock>;
constructor(nodes?: Array<TurboshaftGraphNode>, edges?: Array<TurboshaftGraphEdge>,
blocks?: Array<TurboshaftGraphBlock>) {
this.nodes = nodes ?? new Array<TurboshaftGraphNode>();
this.edges = edges ?? new Array<TurboshaftGraphEdge>();
this.blocks = blocks ?? new Array<TurboshaftGraphBlock>();
}
}
...@@ -37,7 +37,9 @@ export class SourcePosition { ...@@ -37,7 +37,9 @@ export class SourcePosition {
return typeof this.scriptOffset !== undefined && typeof this.inliningId !== undefined; return typeof this.scriptOffset !== undefined && typeof this.inliningId !== undefined;
} }
public toString = (): string => `SP:${this.inliningId}:${this.scriptOffset}`; public toString(): string {
return `SP:${this.inliningId}:${this.scriptOffset}`;
}
} }
export class BytecodePosition { export class BytecodePosition {
...@@ -51,5 +53,7 @@ export class BytecodePosition { ...@@ -51,5 +53,7 @@ export class BytecodePosition {
return typeof this.bytecodePosition !== undefined; return typeof this.bytecodePosition !== undefined;
} }
public toString = (): string => `BCP:${this.bytecodePosition}`; public toString(): string {
return `BCP:${this.bytecodePosition}`;
}
} }
...@@ -2,12 +2,12 @@ ...@@ -2,12 +2,12 @@
// 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 { GNode } from "../node"; import { GraphNode } from "../phases/graph-phase/graph-node";
export class MySelection { export class MySelection {
selection: any; selection: any;
stringKey: (o: any) => string; stringKey: (o: any) => string;
originStringKey: (node: GNode) => string; originStringKey: (node: GraphNode) => string;
constructor(stringKeyFnc, originStringKeyFnc?) { constructor(stringKeyFnc, originStringKeyFnc?) {
this.selection = new Map(); this.selection = new Map();
......
...@@ -13,7 +13,7 @@ import { SequencePhase } from "./phases/sequence-phase"; ...@@ -13,7 +13,7 @@ import { SequencePhase } from "./phases/sequence-phase";
import { BytecodeOrigin } from "./origin"; import { BytecodeOrigin } from "./origin";
import { Source } from "./source"; import { Source } from "./source";
import { NodeLabel } from "./node-label"; import { NodeLabel } from "./node-label";
import { TurboshaftGraphPhase } from "./phases/turboshaft-graph-phase"; import { TurboshaftGraphPhase } from "./phases/turboshaft-graph-phase/turboshaft-graph-phase";
function sourcePositionLe(a, b) { function sourcePositionLe(a, b) {
if (a.inliningId == b.inliningId) { if (a.inliningId == b.inliningId) {
...@@ -80,6 +80,19 @@ export class SourceResolver { ...@@ -80,6 +80,19 @@ export class SourceResolver {
this.linePositionMap = new Map(); this.linePositionMap = new Map();
} }
public getMainFunction(jsonObj): Source {
const fncJson = jsonObj.function;
// Backwards compatibility.
if (typeof fncJson === 'string') {
return new Source(null, null, jsonObj.source, -1, true,
new Array<SourcePosition>(), jsonObj.sourcePosition,
jsonObj.sourcePosition + jsonObj.source.length);
}
return new Source(fncJson.sourceName, fncJson.functionName, fncJson.sourceText,
fncJson.sourceId, false, new Array<SourcePosition>(), fncJson.startPosition,
fncJson.endPosition);
}
setSources(sources, mainBackup) { setSources(sources, mainBackup) {
if (sources) { if (sources) {
for (const [sourceId, source] of Object.entries(sources)) { for (const [sourceId, source] of Object.entries(sources)) {
...@@ -166,10 +179,6 @@ export class SourceResolver { ...@@ -166,10 +179,6 @@ export class SourceResolver {
return sourcePositionArray; return sourcePositionArray;
} }
forEachSource(f: (value: Source, index: number, array: Array<Source>) => void) {
this.sources.forEach(f);
}
translateToSourceId(sourceId: number, location?: SourcePosition) { translateToSourceId(sourceId: number, location?: SourcePosition) {
for (const position of this.getInlineStack(location)) { for (const position of this.getInlineStack(location)) {
const inlining = this.inlinings[position.inliningId]; const inlining = this.inlinings[position.inliningId];
...@@ -326,7 +335,11 @@ export class SourceResolver { ...@@ -326,7 +335,11 @@ export class SourceResolver {
this.phases.push(graphPhase); this.phases.push(graphPhase);
break; break;
case PhaseType.TurboshaftGraph: case PhaseType.TurboshaftGraph:
// Allow to avoid exception and view turboshaft schedule phase const castedTurboshaftGraph = genericPhase as TurboshaftGraphPhase;
const turboshaftGraphPhase = new TurboshaftGraphPhase(castedTurboshaftGraph.name, 0);
turboshaftGraphPhase.parseDataFromJSON(castedTurboshaftGraph.data);
this.phaseNames.set(turboshaftGraphPhase.name, this.phases.length);
this.phases.push(turboshaftGraphPhase);
break; break;
default: default:
throw "Unsupported phase type"; throw "Unsupported phase type";
......
...@@ -27,5 +27,7 @@ export class Source { ...@@ -27,5 +27,7 @@ export class Source {
this.endPosition = endPosition; this.endPosition = endPosition;
} }
public toString = (): string => `${this.sourceName}:${this.functionName}`; public toString(): string {
return `${this.sourceName}:${this.functionName}`;
}
} }
...@@ -7,29 +7,13 @@ export class Tabs { ...@@ -7,29 +7,13 @@ export class Tabs {
private tabBar: HTMLElement; private tabBar: HTMLElement;
private nextTabId: number; private nextTabId: number;
private mkTabBar(container: HTMLElement) {
container.classList.add("nav-tabs-container");
this.tabBar = document.createElement("ul");
this.tabBar.id = `tab-bar-${container.id}`;
this.tabBar.className = "nav-tabs";
this.tabBar.ondrop = this.tabBarOnDrop.bind(this);
this.tabBar.ondragover = this.tabBarOnDragover.bind(this);
this.tabBar.onclick = this.tabBarOnClick.bind(this);
const defaultDiv = document.createElement("div");
defaultDiv.className = "tab-content tab-default";
defaultDiv.id = `tab-content-${container.id}-default`;
container.insertBefore(defaultDiv, container.firstChild);
container.insertBefore(this.tabBar, container.firstChild);
}
constructor(container: HTMLElement) { constructor(container: HTMLElement) {
this.container = container; this.container = container;
this.nextTabId = 0; this.nextTabId = 0;
this.mkTabBar(container); this.makeTabBar(container);
} }
activateTab(tab: HTMLLIElement) { public activateTab(tab: HTMLLIElement): void {
if (typeof tab.dataset.divid !== "string") return; if (typeof tab.dataset.divid !== "string") return;
for (const li of this.tabBar.querySelectorAll<HTMLLIElement>("li.active")) { for (const li of this.tabBar.querySelectorAll<HTMLLIElement>("li.active")) {
li.classList.remove("active"); li.classList.remove("active");
...@@ -39,7 +23,7 @@ export class Tabs { ...@@ -39,7 +23,7 @@ export class Tabs {
this.showTab(tab, true); this.showTab(tab, true);
} }
clearTabsAndContent() { public clearTabsAndContent(): void {
for (const tab of this.tabBar.querySelectorAll(".nav-tabs > li")) { for (const tab of this.tabBar.querySelectorAll(".nav-tabs > li")) {
if (!(tab instanceof HTMLLIElement)) continue; if (!(tab instanceof HTMLLIElement)) continue;
if (tab.classList.contains("persistent-tab")) continue; if (tab.classList.contains("persistent-tab")) continue;
...@@ -49,20 +33,6 @@ export class Tabs { ...@@ -49,20 +33,6 @@ export class Tabs {
} }
} }
private showTab(li: HTMLElement, show: boolean = true) {
const tabDiv = document.getElementById(li.dataset.divid);
tabDiv.style.display = show ? "block" : "none";
}
public addTab(caption: string): HTMLLIElement {
const newTab = document.createElement("li");
newTab.innerHTML = caption;
newTab.id = `tab-header-${this.container.id}-${this.nextTabId++}`;
const lastTab = this.tabBar.querySelector("li.last-tab");
this.tabBar.insertBefore(newTab, lastTab);
return newTab;
}
public addTabAndContent(caption: string): [HTMLLIElement, HTMLDivElement] { public addTabAndContent(caption: string): [HTMLLIElement, HTMLDivElement] {
const contentDiv = document.createElement("div"); const contentDiv = document.createElement("div");
contentDiv.className = "tab-content tab-default"; contentDiv.className = "tab-content tab-default";
...@@ -79,14 +49,44 @@ export class Tabs { ...@@ -79,14 +49,44 @@ export class Tabs {
return [newTab, contentDiv]; return [newTab, contentDiv];
} }
private moveTabDiv(tab: HTMLLIElement) { public addTab(caption: string): HTMLLIElement {
const newTab = document.createElement("li");
newTab.innerHTML = caption;
newTab.id = `tab-header-${this.container.id}-${this.nextTabId++}`;
const lastTab = this.tabBar.querySelector("li.last-tab");
this.tabBar.insertBefore(newTab, lastTab);
return newTab;
}
private makeTabBar(container: HTMLElement): void {
container.classList.add("nav-tabs-container");
this.tabBar = document.createElement("ul");
this.tabBar.id = `tab-bar-${container.id}`;
this.tabBar.className = "nav-tabs";
this.tabBar.ondrop = this.tabBarOnDrop.bind(this);
this.tabBar.ondragover = this.tabBarOnDragover.bind(this);
this.tabBar.onclick = this.tabBarOnClick.bind(this);
const defaultDiv = document.createElement("div");
defaultDiv.className = "tab-content tab-default";
defaultDiv.id = `tab-content-${container.id}-default`;
container.insertBefore(defaultDiv, container.firstChild);
container.insertBefore(this.tabBar, container.firstChild);
}
private showTab(li: HTMLElement, show: boolean = true) {
const tabDiv = document.getElementById(li.dataset.divid);
tabDiv.style.display = show ? "block" : "none";
}
private moveTabDiv(tab: HTMLLIElement): void {
const tabDiv = document.getElementById(tab.dataset.divid); const tabDiv = document.getElementById(tab.dataset.divid);
tabDiv.style.display = "none"; tabDiv.style.display = "none";
tab.classList.remove("active"); tab.classList.remove("active");
this.tabBar.parentNode.appendChild(tabDiv); this.tabBar.parentNode.appendChild(tabDiv);
} }
private tabBarOnDrop(e: DragEvent) { private tabBarOnDrop(e: DragEvent): void {
if (!(e.target instanceof HTMLElement)) return; if (!(e.target instanceof HTMLElement)) return;
e.preventDefault(); e.preventDefault();
const tabId = e.dataTransfer.getData("text"); const tabId = e.dataTransfer.getData("text");
...@@ -94,23 +94,23 @@ export class Tabs { ...@@ -94,23 +94,23 @@ export class Tabs {
if (tab.parentNode != this.tabBar) { if (tab.parentNode != this.tabBar) {
this.moveTabDiv(tab); this.moveTabDiv(tab);
} }
const dropTab = const dropTab = e.target.parentNode == this.tabBar
e.target.parentNode == this.tabBar ? e.target
? e.target : this.tabBar.querySelector("li.last-tab"); : this.tabBar.querySelector("li.last-tab");
this.tabBar.insertBefore(tab, dropTab); this.tabBar.insertBefore(tab, dropTab);
this.activateTab(tab); this.activateTab(tab);
} }
private tabBarOnDragover(e) { private tabBarOnDragover(e: DragEvent): void {
e.preventDefault(); e.preventDefault();
} }
private tabOnDragStart(e: DragEvent) { private tabOnDragStart(e: DragEvent): void {
if (!(e.target instanceof HTMLElement)) return; if (!(e.target instanceof HTMLElement)) return;
e.dataTransfer.setData("text", e.target.id); e.dataTransfer.setData("text", e.target.id);
} }
private tabBarOnClick(e: MouseEvent) { private tabBarOnClick(e: MouseEvent): void {
const li = e.target as HTMLLIElement; const li = e.target as HTMLLIElement;
this.activateTab(li); this.activateTab(li);
} }
......
...@@ -13,18 +13,21 @@ import { Resizer } from "./resizer"; ...@@ -13,18 +13,21 @@ import { Resizer } from "./resizer";
import { InfoView } from "./views/info-view"; import { InfoView } from "./views/info-view";
window.onload = function () { window.onload = function () {
let multiview: GraphMultiView = null; let multiview: GraphMultiView;
let disassemblyView: DisassemblyView = null; let disassemblyView: DisassemblyView;
let sourceViews: Array<CodeView> = []; let sourceViews: Array<CodeView> = new Array<CodeView>();
let selectionBroker: SelectionBroker = null; let selectionBroker: SelectionBroker;
let sourceResolver: SourceResolver = null; let sourceResolver: SourceResolver;
const resizer = new Resizer(panesUpdatedCallback, 75, 75);
const sourceTabsContainer = document.getElementById(C.SOURCE_PANE_ID); const resizer = new Resizer(() => multiview?.onresize(), 75, 75);
const sourceTabs = new Tabs(sourceTabsContainer);
sourceTabs.addTab("&#x2b;").classList.add("last-tab", "persistent-tab");
const disassemblyTabsContainer = document.getElementById(C.GENERATED_PANE_ID); const disassemblyTabsContainer = document.getElementById(C.GENERATED_PANE_ID);
const disassemblyTabs = new Tabs(disassemblyTabsContainer); const disassemblyTabs = new Tabs(disassemblyTabsContainer);
disassemblyTabs.addTab("&#x2b;").classList.add("last-tab", "persistent-tab"); disassemblyTabs.addTab("&#x2b;").classList.add("last-tab", "persistent-tab");
const sourceTabsContainer = document.getElementById(C.SOURCE_PANE_ID);
const sourceTabs = new Tabs(sourceTabsContainer);
sourceTabs.addTab("&#x2b;").classList.add("last-tab", "persistent-tab");
const [infoTab, infoContainer] = sourceTabs.addTabAndContent("Info"); const [infoTab, infoContainer] = sourceTabs.addTabAndContent("Info");
infoTab.classList.add("persistent-tab"); infoTab.classList.add("persistent-tab");
infoContainer.classList.add("viewpane", "scrollable"); infoContainer.classList.add("viewpane", "scrollable");
...@@ -32,71 +35,56 @@ window.onload = function () { ...@@ -32,71 +35,56 @@ window.onload = function () {
infoView.show(); infoView.show();
sourceTabs.activateTab(infoTab); sourceTabs.activateTab(infoTab);
function panesUpdatedCallback() { function loadFile(txtRes: string): void {
if (multiview) multiview.onresize();
}
function loadFile(txtRes: string) {
sourceTabs.clearTabsAndContent(); sourceTabs.clearTabsAndContent();
disassemblyTabs.clearTabsAndContent(); disassemblyTabs.clearTabsAndContent();
// If the JSON isn't properly terminated, assume compiler crashed and // If the JSON isn't properly terminated, assume compiler crashed and
// add best-guess empty termination // add best-guess empty termination
if (txtRes[txtRes.length - 2] == ',') { if (txtRes[txtRes.length - 2] === ",") {
txtRes += '{"name":"disassembly","type":"disassembly","data":""}]}'; txtRes += '{"name":"disassembly","type":"disassembly","data":""}]}';
} }
try { try {
sourceViews.forEach(sv => sv.hide()); sourceViews.forEach(sv => sv.hide());
if (multiview) multiview.hide(); multiview?.hide();
multiview = null; multiview = null;
document.getElementById("ranges").innerHTML = ''; document.getElementById("ranges").innerHTML = "";
document.getElementById('ranges').style.visibility = "hidden"; document.getElementById("ranges").style.visibility = "hidden";
document.getElementById('show-hide-ranges').style.visibility = "hidden"; document.getElementById("show-hide-ranges").style.visibility = "hidden";
if (disassemblyView) disassemblyView.hide(); disassemblyView?.hide();
sourceViews = []; sourceViews = new Array<CodeView>();
sourceResolver = new SourceResolver(); sourceResolver = new SourceResolver();
selectionBroker = new SelectionBroker(sourceResolver); selectionBroker = new SelectionBroker(sourceResolver);
const jsonObj = JSON.parse(txtRes); const jsonObj = JSON.parse(txtRes);
const mainFunction = sourceResolver.getMainFunction(jsonObj);
let fnc = null;
// Backwards compatibility.
if (typeof jsonObj.function == 'string') {
fnc = {
functionName: fnc,
sourceId: -1,
startPosition: jsonObj.sourcePosition,
endPosition: jsonObj.sourcePosition + jsonObj.source.length,
sourceText: jsonObj.source,
backwardsCompatibility: true
};
} else {
fnc = Object.assign(jsonObj.function, { backwardsCompatibility: false });
}
sourceResolver.setInlinings(jsonObj.inlinings); sourceResolver.setInlinings(jsonObj.inlinings);
sourceResolver.setSourceLineToBytecodePosition(jsonObj.sourceLineToBytecodePosition); sourceResolver.setSourceLineToBytecodePosition(jsonObj.sourceLineToBytecodePosition);
sourceResolver.setSources(jsonObj.sources, fnc); sourceResolver.setSources(jsonObj.sources, mainFunction);
sourceResolver.setNodePositionMap(jsonObj.nodePositions); sourceResolver.setNodePositionMap(jsonObj.nodePositions);
sourceResolver.parsePhases(jsonObj.phases); sourceResolver.parsePhases(jsonObj.phases);
const [sourceTab, sourceContainer] = sourceTabs.addTabAndContent("Source"); const [sourceTab, sourceContainer] = sourceTabs.addTabAndContent("Source");
sourceContainer.classList.add("viewpane", "scrollable"); sourceContainer.classList.add("viewpane", "scrollable");
sourceTabs.activateTab(sourceTab); sourceTabs.activateTab(sourceTab);
const sourceView = new CodeView(sourceContainer, selectionBroker, sourceResolver, fnc, CodeMode.MAIN_SOURCE);
const sourceView = new CodeView(sourceContainer, selectionBroker, sourceResolver,
mainFunction, CodeMode.MAIN_SOURCE);
sourceView.show(); sourceView.show();
sourceViews.push(sourceView); sourceViews.push(sourceView);
sourceResolver.forEachSource(source => { for (const source of sourceResolver.sources) {
const sourceView = new CodeView(sourceContainer, selectionBroker, sourceResolver, source, CodeMode.INLINED_SOURCE); const sourceView = new CodeView(sourceContainer, selectionBroker, sourceResolver,
source, CodeMode.INLINED_SOURCE);
sourceView.show(); sourceView.show();
sourceViews.push(sourceView); sourceViews.push(sourceView);
}); }
const [disassemblyTab, disassemblyContainer] = disassemblyTabs.addTabAndContent("Disassembly"); const [disassemblyTab, disassemblyContainer] = disassemblyTabs.addTabAndContent("Disassembly");
disassemblyContainer.classList.add("viewpane", "scrollable"); disassemblyContainer.classList.add("viewpane", "scrollable");
disassemblyTabs.activateTab(disassemblyTab); disassemblyTabs.activateTab(disassemblyTab);
disassemblyView = new DisassemblyView(disassemblyContainer, selectionBroker); disassemblyView = new DisassemblyView(disassemblyContainer, selectionBroker);
disassemblyView.initializeCode(fnc.sourceText); disassemblyView.initializeCode(mainFunction.sourceText);
if (sourceResolver.disassemblyPhase) { if (sourceResolver.disassemblyPhase) {
disassemblyView.initializePerfProfile(jsonObj.eventCounts); disassemblyView.initializePerfProfile(jsonObj.eventCounts);
disassemblyView.showContent(sourceResolver.disassemblyPhase.data); disassemblyView.showContent(sourceResolver.disassemblyPhase.data);
...@@ -107,7 +95,7 @@ window.onload = function () { ...@@ -107,7 +95,7 @@ window.onload = function () {
multiview.show(); multiview.show();
} catch (err) { } catch (err) {
if (window.confirm("Error: Exception during load of TurboFan JSON file:\n" + if (window.confirm("Error: Exception during load of TurboFan JSON file:\n" +
"error: " + err.message + "\nDo you want to clear session storage?")) { `error: ${err.message} \nDo you want to clear session storage?`)) {
window.sessionStorage.clear(); window.sessionStorage.clear();
} }
return; return;
...@@ -122,21 +110,23 @@ window.onload = function () { ...@@ -122,21 +110,23 @@ window.onload = function () {
document.getElementById("upload-helper").click(); document.getElementById("upload-helper").click();
e.stopPropagation(); e.stopPropagation();
}); });
document.getElementById("upload-helper").addEventListener("change", document.getElementById("upload-helper").addEventListener("change",
function (this: HTMLInputElement) { function (this: HTMLInputElement) {
const uploadFile = this.files && this.files[0]; const uploadFile = this.files && this.files[0];
if (uploadFile) { if (uploadFile) {
const filereader = new FileReader(); const fileReader = new FileReader();
filereader.onload = () => { fileReader.onload = () => {
const txtRes = filereader.result; const txtRes = fileReader.result;
if (typeof txtRes == 'string') { if (typeof txtRes === "string") {
loadFile(txtRes); loadFile(txtRes);
} }
}; };
filereader.readAsText(uploadFile); fileReader.readAsText(uploadFile);
} }
} }
); );
window.addEventListener("keydown", (e: KeyboardEvent) => { window.addEventListener("keydown", (e: KeyboardEvent) => {
if (e.keyCode == 76 && e.ctrlKey) { // CTRL + L if (e.keyCode == 76 && e.ctrlKey) { // CTRL + L
document.getElementById("upload-helper").click(); document.getElementById("upload-helper").click();
......
This diff is collapsed.
...@@ -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 { GNode } from "../node"; import { GraphNode } from "../phases/graph-phase/graph-node";
export abstract class View { export abstract class View {
protected container: HTMLElement; protected container: HTMLElement;
...@@ -24,8 +24,8 @@ export abstract class View { ...@@ -24,8 +24,8 @@ export abstract class View {
} }
export abstract class PhaseView extends View { export abstract class PhaseView extends View {
public abstract initializeContent(data: any, rememberedSelection: Map<string, GNode>): void; public abstract initializeContent(data: any, rememberedSelection: Map<string, GraphNode>): void;
public abstract detachSelection(): Map<string, GNode>; public abstract detachSelection(): Map<string, GraphNode>;
public abstract onresize(): void; public abstract onresize(): void;
public abstract searchInputAction(searchInput: HTMLInputElement, e: Event, onlyVisible: boolean): void; public abstract searchInputAction(searchInput: HTMLInputElement, e: Event, onlyVisible: boolean): void;
......
...@@ -17,13 +17,18 @@ ...@@ -17,13 +17,18 @@
"src/common/util.ts", "src/common/util.ts",
"src/common/constants.ts", "src/common/constants.ts",
"src/common/view-elements.ts", "src/common/view-elements.ts",
"src/phases/graph-phase/graph-node.ts",
"src/phases/graph-phase/graph-edge.ts",
"src/phases/turboshaft-graph-phase/turboshaft-graph-phase.ts",
"src/phases/turboshaft-graph-phase/turboshaft-graph-block.ts",
"src/phases/turboshaft-graph-phase/turboshaft-graph-node.ts",
"src/phases/turboshaft-graph-phase/turboshaft-graph-edge.ts",
"src/phases/disassembly-phase.ts", "src/phases/disassembly-phase.ts",
"src/phases/graph-phase.ts", "src/phases/graph-phase.ts",
"src/phases/instructions-phase.ts", "src/phases/instructions-phase.ts",
"src/phases/phase.ts", "src/phases/phase.ts",
"src/phases/schedule-phase.ts", "src/phases/schedule-phase.ts",
"src/phases/sequence-phase.ts", "src/phases/sequence-phase.ts",
"src/phases/turboshaft-graph-phase.ts",
"src/selection/selection.ts", "src/selection/selection.ts",
"src/selection/selection-broker.ts", "src/selection/selection-broker.ts",
"src/selection/selection-handler.ts", "src/selection/selection-handler.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