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 @@
// found in the LICENSE file.
export const MAX_RANK_SENTINEL = 0;
export const BEZIER_CONSTANT = 0.3;
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_COLLAPSE_ID = "source-shrink";
export const SOURCE_EXPAND_ID = "source-expand";
......
......@@ -2,100 +2,23 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import { GNode, MINIMUM_EDGE_SEPARATION, DEFAULT_NODE_BUBBLE_RADIUS } from "./node";
import { Graph } from "./graph";
import { GraphNode } from "./phases/graph-phase/graph-node";
import { TurboshaftGraphNode } from "./phases/turboshaft-graph-phase/turboshaft-graph-node";
const BEZIER_CONSTANT = 0.3;
export class Edge {
target: GNode;
source: GNode;
index: number;
type: string;
export abstract class Edge<NodeType extends GraphNode | TurboshaftGraphNode> {
target: NodeType;
source: NodeType;
backEdgeNumber: number;
visible: boolean;
constructor(target: GNode, index: number, source: GNode, type: string) {
constructor(target: NodeType, source: NodeType) {
this.target = target;
this.source = source;
this.index = index;
this.type = type;
this.backEdgeNumber = 0;
this.visible = false;
}
stringID() {
return this.source.id + "," + this.index + "," + this.target.id;
}
isVisible() {
public isVisible(): boolean {
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";
import { Edge } from "./edge";
// 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 { GraphPhase } from "./phases/graph-phase";
import { GraphEdge } from "./phases/graph-phase/graph-edge";
import { GraphNode } from "./phases/graph-phase/graph-node";
export class Graph {
nodeMap: Array<GNode>;
nodeMap: Array<GraphNode>;
minGraphX: number;
maxGraphX: number;
minGraphY: number;
......@@ -23,14 +28,14 @@ export class Graph {
this.width = 1;
this.height = 1;
graphPhase.data.nodes.forEach((jsonNode: GNode) => {
this.nodeMap[jsonNode.id] = new GNode(jsonNode.nodeLabel);
graphPhase.data.nodes.forEach((jsonNode: GraphNode) => {
this.nodeMap[jsonNode.id] = new GraphNode(jsonNode.nodeLabel);
});
graphPhase.data.edges.forEach((e: any) => {
const t = this.nodeMap[e.target.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);
s.outputs.push(newEdge);
if (e.type == 'control') {
......@@ -40,14 +45,14 @@ export class Graph {
});
}
*nodes(p = (n: GNode) => true) {
*nodes(p = (n: GraphNode) => true) {
for (const node of this.nodeMap) {
if (!node || !p(node)) continue;
yield node;
}
}
*filteredEdges(p: (e: Edge) => boolean) {
*filteredEdges(p: (e: GraphEdge) => boolean) {
for (const node of this.nodes()) {
for (const edge of node.inputs) {
if (p(edge)) yield edge;
......@@ -55,7 +60,7 @@ export class Graph {
}
}
forEachEdge(p: (e: Edge) => void) {
forEachEdge(p: (e: GraphEdge) => void) {
for (const node of this.nodeMap) {
if (!node) continue;
for (const edge of node.inputs) {
......@@ -91,7 +96,7 @@ export class Graph {
}
this.maxGraphX = this.maxGraphNodeX +
this.maxBackEdgeNumber * MINIMUM_EDGE_SEPARATION;
this.maxBackEdgeNumber * C.MINIMUM_EDGE_SEPARATION;
this.width = this.maxGraphX - this.minGraphX;
this.height = this.maxGraphY - this.minGraphY;
......
......@@ -8,8 +8,8 @@ import { SequenceView } from "./views/sequence-view";
import { SourceResolver } from "./source-resolver";
import { SelectionBroker } from "./selection/selection-broker";
import { PhaseView, View } from "./views/view";
import { GNode } from "./node";
import { GraphPhase } from "./phases/graph-phase";
import { GraphNode } from "./phases/graph-phase/graph-node";
const multiviewID = "multiview";
......@@ -108,7 +108,7 @@ export class GraphMultiView extends View {
this.displayPhase(this.sourceResolver.getPhase(initialPhaseIndex));
}
displayPhase(phase, selection?: Map<string, GNode>) {
displayPhase(phase, selection?: Map<string, GraphNode>) {
if (phase.type == "graph") {
this.displayPhaseView(this.graph, phase, selection);
} else if (phase.type == "schedule") {
......@@ -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();
view.initializeContent(data, rememberedSelection);
this.currentPhaseView = view;
}
displayPhaseByName(phaseName, selection?: Map<string, GNode>) {
displayPhaseByName(phaseName, selection?: Map<string, GraphNode>) {
const phaseId = this.sourceResolver.getPhaseIdByName(phaseName);
this.selectMenu.selectedIndex = phaseId;
this.displayPhase(this.sourceResolver.getPhase(phaseId), selection);
......
......@@ -2,16 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// 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 {
id: number;
label: string;
......@@ -51,20 +41,14 @@ export class NodeLabel {
if (this.opcode != that.opcode) return false;
if (this.control != that.control) return false;
if (this.opinfo != that.opinfo) return false;
if (this.type != that.type) return false;
return true;
return this.type == that.type;
}
getTitle() {
let propsString = "";
if (this.properties === "") {
propsString = "no properties";
} else {
propsString = "[" + this.properties + "]";
}
let title = this.title + "\n" + propsString + "\n" + this.opinfo;
public getTitle(): string {
const propsString = this.properties === "" ? "no properties" : `[${this.properties}]`;
let title = `${this.title}\n${propsString}\n${this.opinfo}`;
if (this.origin) {
title += `\nOrigin: ${formatOrigin(this.origin)}`;
title += `\nOrigin: ${this.origin.toString()}`;
}
if (this.inplaceUpdatePhase) {
title += `\nInplace update in phase: ${this.inplaceUpdatePhase}`;
......@@ -72,15 +56,12 @@ export class NodeLabel {
return title;
}
getDisplayLabel() {
const result = `${this.id}: ${this.label}`;
if (result.length > 40) {
return `${this.id}: ${this.opcode}`;
}
return result;
public getDisplayLabel(): string {
const label = `${this.id}: ${this.label}`;
return label.length > 40 ? `${this.id}: ${this.opcode}` : label;
}
setInplaceUpdatePhase(name: string): any {
public setInplaceUpdatePhase(name: string): void {
this.inplaceUpdatePhase = name;
}
}
......@@ -2,182 +2,73 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import { Edge } from "./edge";
import { NodeLabel } from "./node-label";
import { MAX_RANK_SENTINEL } from "./common/constants";
import { alignUp, measureText } from "./common/util";
import { measureText } from "./common/util";
import { GraphEdge } from "./phases/graph-phase/graph-edge";
import { TurboshaftGraphEdge } from "./phases/turboshaft-graph-phase/turboshaft-graph-edge";
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;
const MINIMUM_NODE_INPUT_APPROACH = 15 + 2 * DEFAULT_NODE_BUBBLE_RADIUS;
export class GNode {
export abstract class Node<EdgeType extends GraphEdge | TurboshaftGraphEdge> {
id: number;
nodeLabel: NodeLabel;
displayLabel: string;
inputs: Array<Edge>;
outputs: Array<Edge>;
inputs: Array<EdgeType>;
outputs: Array<EdgeType>;
visible: boolean;
x: number;
y: number;
rank: number;
outputApproach: number;
cfg: boolean;
labelbbox: { width: number, height: number };
width: number;
normalheight: number;
labelBox: { width: number, height: number };
visitOrderWithinRank: number;
constructor(nodeLabel: NodeLabel) {
this.id = nodeLabel.id;
this.nodeLabel = nodeLabel;
this.displayLabel = nodeLabel.getDisplayLabel();
this.inputs = [];
this.outputs = [];
constructor(id: number, displayLabel?: string) {
this.id = id;
this.displayLabel = displayLabel;
this.inputs = new Array<EdgeType>();
this.outputs = new Array<EdgeType>();
this.visible = false;
this.x = 0;
this.y = 0;
this.rank = MAX_RANK_SENTINEL;
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.labelBox = measureText(this.displayLabel);
this.visitOrderWithinRank = 0;
}
isControl() {
return this.nodeLabel.control;
}
isInput() {
return this.nodeLabel.opcode == 'Parameter' || this.nodeLabel.opcode.endsWith('Constant');
}
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() {
public areAnyOutputsVisible(): number {
// TODO (danylo boiko) Move 0, 1, 2 logic to enum
let visibleCount = 0;
this.outputs.forEach(function (e) { if (e.isVisible())++visibleCount; });
if (this.outputs.length == visibleCount) return 2;
if (visibleCount != 0) return 1;
for (const edge of this.outputs) {
if (edge.isVisible()) {
++visibleCount;
}
}
if (this.outputs.length === visibleCount) return 2;
if (visibleCount !== 0) return 1;
return 0;
}
setOutputVisibility(v) {
public setOutputVisibility(visibility: boolean): boolean {
let result = false;
this.outputs.forEach(function (e) {
e.visible = v;
if (v) {
if (!e.target.visible) {
e.target.visible = true;
result = true;
}
for (const edge of this.outputs) {
edge.visible = visibility;
if (visibility && !edge.target.visible) {
edge.target.visible = true;
result = true;
}
});
}
return result;
}
setInputVisibility(i, v) {
const edge = this.inputs[i];
edge.visible = v;
if (v) {
if (!edge.source.visible) {
edge.source.visible = true;
return true;
}
public setInputVisibility(edgeIdx: number, visibility: boolean): boolean {
const edge = this.inputs[edgeIdx];
edge.visible = visibility;
if (visibility && !edge.source.visible) {
edge.source.visible = true;
return true;
}
return false;
}
getInputApproach(index) {
return this.y - MINIMUM_NODE_INPUT_APPROACH -
(index % 4) * MINIMUM_EDGE_SEPARATION - DEFAULT_NODE_BUBBLE_RADIUS;
}
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 {
return `${this.id}`;
}
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 {
this.nodeId = nodeId;
}
public identifier = (): string => `${this.nodeId}`;
public toString = (): string => `#${this.nodeId} in phase '${this.phase}/${this.reducer}'`;
public identifier(): string {
return `${this.nodeId}`;
}
public toString(): string {
return `#${this.nodeId} in phase '${this.phase}/${this.reducer}'`;
}
}
export class BytecodeOrigin extends Origin {
......@@ -32,8 +37,11 @@ export class BytecodeOrigin extends Origin {
this.bytecodePosition = bytecodePosition;
}
public identifier = (): string => `${this.bytecodePosition}`;
public toString = (): string => {
public identifier(): string {
return `${this.bytecodePosition}`;
}
public toString(): string {
return `Bytecode line ${this.bytecodePosition} in phase '${this.phase}/${this.reducer}'`;
}
}
......@@ -4,33 +4,34 @@
import { Phase, PhaseType } from "./phase";
import { NodeLabel } from "../node-label";
import { GNode } from "../node";
import { Edge } from "../edge";
import { BytecodeOrigin, NodeOrigin } from "../origin";
import { SourcePosition } from "../position";
import { GraphNode } from "./graph-phase/graph-node";
import { GraphEdge } from "./graph-phase/graph-edge";
export class GraphPhase extends Phase {
highestNodeId: number;
data: GraphData;
nodeLabelMap: Array<NodeLabel>;
nodeIdToNodeMap: Array<GNode>;
nodeIdToNodeMap: Array<GraphNode>;
constructor(name: string, highestNodeId: number, data?: GraphData,
nodeLabelMap?: Array<NodeLabel>, nodeIdToNodeMap?: Array<GNode>) {
nodeLabelMap?: Array<NodeLabel>, nodeIdToNodeMap?: Array<GraphNode>) {
super(name, PhaseType.Graph);
this.highestNodeId = highestNodeId;
this.data = data ?? new GraphData();
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 {
this.data = new GraphData();
this.nodeIdToNodeMap = this.parseNodesFromJSON(dataJson.nodes, nodeLabelMap);
this.parseEdgesFromJSON(dataJson.edges);
}
private parseNodesFromJSON(nodesJSON, nodeLabelMap: Array<NodeLabel>): Array<GNode> {
const nodeIdToNodeMap = new Array<GNode>();
private parseNodesFromJSON(nodesJSON, nodeLabelMap: Array<NodeLabel>): Array<GraphNode> {
const nodeIdToNodeMap = new Array<GraphNode>();
for (const node of nodesJSON) {
let origin: NodeOrigin | BytecodeOrigin = null;
const jsonOrigin = node.origin;
......@@ -60,7 +61,7 @@ export class GraphPhase extends Phase {
}
nodeLabelMap[label.id] = label;
}
const newNode = new GNode(label);
const newNode = new GraphNode(label);
this.data.nodes.push(newNode);
nodeIdToNodeMap[newNode.id] = newNode;
}
......@@ -71,7 +72,7 @@ export class GraphPhase extends Phase {
for (const edge of edgesJSON) {
const target = this.nodeIdToNodeMap[edge.target];
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);
target.inputs.push(newEdge);
source.outputs.push(newEdge);
......@@ -83,11 +84,11 @@ export class GraphPhase extends Phase {
}
export class GraphData {
nodes: Array<GNode>;
edges: Array<Edge>;
nodes: Array<GraphNode>;
edges: Array<GraphEdge>;
constructor(nodes?: Array<GNode>, edges?: Array<Edge>) {
this.nodes = nodes ?? new Array<GNode>();
this.edges = edges ?? new Array<Edge>();
constructor(nodes?: Array<GraphNode>, edges?: Array<GraphEdge>) {
this.nodes = nodes ?? new Array<GraphNode>();
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 @@
// 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 { anyToString } from "../common/util";
import { Phase, PhaseType } from "./phase";
export class InstructionsPhase extends Phase {
// 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 @@
// 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 { Edge } from "../../edge";
export class TurboshaftGraphPhase extends Phase {
constructor(name: string) {
super(name, PhaseType.TurboshaftGraph);
// To be continued ...
export class TurboshaftGraphEdge extends Edge<TurboshaftGraphNode> {
constructor(target: TurboshaftGraphNode, source: TurboshaftGraphNode) {
super(target, source);
}
}
// 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 {
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 {
......@@ -51,5 +53,7 @@ export class BytecodePosition {
return typeof this.bytecodePosition !== undefined;
}
public toString = (): string => `BCP:${this.bytecodePosition}`;
public toString(): string {
return `BCP:${this.bytecodePosition}`;
}
}
......@@ -2,12 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import { GNode } from "../node";
import { GraphNode } from "../phases/graph-phase/graph-node";
export class MySelection {
selection: any;
stringKey: (o: any) => string;
originStringKey: (node: GNode) => string;
originStringKey: (node: GraphNode) => string;
constructor(stringKeyFnc, originStringKeyFnc?) {
this.selection = new Map();
......
......@@ -13,7 +13,7 @@ import { SequencePhase } from "./phases/sequence-phase";
import { BytecodeOrigin } from "./origin";
import { Source } from "./source";
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) {
if (a.inliningId == b.inliningId) {
......@@ -80,6 +80,19 @@ export class SourceResolver {
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) {
if (sources) {
for (const [sourceId, source] of Object.entries(sources)) {
......@@ -166,10 +179,6 @@ export class SourceResolver {
return sourcePositionArray;
}
forEachSource(f: (value: Source, index: number, array: Array<Source>) => void) {
this.sources.forEach(f);
}
translateToSourceId(sourceId: number, location?: SourcePosition) {
for (const position of this.getInlineStack(location)) {
const inlining = this.inlinings[position.inliningId];
......@@ -326,7 +335,11 @@ export class SourceResolver {
this.phases.push(graphPhase);
break;
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;
default:
throw "Unsupported phase type";
......
......@@ -27,5 +27,7 @@ export class Source {
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 {
private tabBar: HTMLElement;
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) {
this.container = container;
this.nextTabId = 0;
this.mkTabBar(container);
this.makeTabBar(container);
}
activateTab(tab: HTMLLIElement) {
public activateTab(tab: HTMLLIElement): void {
if (typeof tab.dataset.divid !== "string") return;
for (const li of this.tabBar.querySelectorAll<HTMLLIElement>("li.active")) {
li.classList.remove("active");
......@@ -39,30 +23,16 @@ export class Tabs {
this.showTab(tab, true);
}
clearTabsAndContent() {
public clearTabsAndContent(): void {
for (const tab of this.tabBar.querySelectorAll(".nav-tabs > li")) {
if (!(tab instanceof HTMLLIElement)) continue;
if (tab.classList.contains("persistent-tab")) continue;
const tabDiv = document.getElementById(tab.dataset.divid);
tabDiv.parentNode.removeChild(tabDiv);
tab.parentNode.removeChild(tab);
if (!(tab instanceof HTMLLIElement)) continue;
if (tab.classList.contains("persistent-tab")) continue;
const tabDiv = document.getElementById(tab.dataset.divid);
tabDiv.parentNode.removeChild(tabDiv);
tab.parentNode.removeChild(tab);
}
}
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] {
const contentDiv = document.createElement("div");
contentDiv.className = "tab-content tab-default";
......@@ -79,14 +49,44 @@ export class Tabs {
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);
tabDiv.style.display = "none";
tab.classList.remove("active");
this.tabBar.parentNode.appendChild(tabDiv);
}
private tabBarOnDrop(e: DragEvent) {
private tabBarOnDrop(e: DragEvent): void {
if (!(e.target instanceof HTMLElement)) return;
e.preventDefault();
const tabId = e.dataTransfer.getData("text");
......@@ -94,23 +94,23 @@ export class Tabs {
if (tab.parentNode != this.tabBar) {
this.moveTabDiv(tab);
}
const dropTab =
e.target.parentNode == this.tabBar
? e.target : this.tabBar.querySelector("li.last-tab");
const dropTab = e.target.parentNode == this.tabBar
? e.target
: this.tabBar.querySelector("li.last-tab");
this.tabBar.insertBefore(tab, dropTab);
this.activateTab(tab);
}
private tabBarOnDragover(e) {
private tabBarOnDragover(e: DragEvent): void {
e.preventDefault();
}
private tabOnDragStart(e: DragEvent) {
private tabOnDragStart(e: DragEvent): void {
if (!(e.target instanceof HTMLElement)) return;
e.dataTransfer.setData("text", e.target.id);
}
private tabBarOnClick(e: MouseEvent) {
private tabBarOnClick(e: MouseEvent): void {
const li = e.target as HTMLLIElement;
this.activateTab(li);
}
......
......@@ -13,18 +13,21 @@ import { Resizer } from "./resizer";
import { InfoView } from "./views/info-view";
window.onload = function () {
let multiview: GraphMultiView = null;
let disassemblyView: DisassemblyView = null;
let sourceViews: Array<CodeView> = [];
let selectionBroker: SelectionBroker = null;
let sourceResolver: SourceResolver = null;
const resizer = new Resizer(panesUpdatedCallback, 75, 75);
const sourceTabsContainer = document.getElementById(C.SOURCE_PANE_ID);
const sourceTabs = new Tabs(sourceTabsContainer);
sourceTabs.addTab("&#x2b;").classList.add("last-tab", "persistent-tab");
let multiview: GraphMultiView;
let disassemblyView: DisassemblyView;
let sourceViews: Array<CodeView> = new Array<CodeView>();
let selectionBroker: SelectionBroker;
let sourceResolver: SourceResolver;
const resizer = new Resizer(() => multiview?.onresize(), 75, 75);
const disassemblyTabsContainer = document.getElementById(C.GENERATED_PANE_ID);
const disassemblyTabs = new Tabs(disassemblyTabsContainer);
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");
infoTab.classList.add("persistent-tab");
infoContainer.classList.add("viewpane", "scrollable");
......@@ -32,71 +35,56 @@ window.onload = function () {
infoView.show();
sourceTabs.activateTab(infoTab);
function panesUpdatedCallback() {
if (multiview) multiview.onresize();
}
function loadFile(txtRes: string) {
function loadFile(txtRes: string): void {
sourceTabs.clearTabsAndContent();
disassemblyTabs.clearTabsAndContent();
// If the JSON isn't properly terminated, assume compiler crashed and
// add best-guess empty termination
if (txtRes[txtRes.length - 2] == ',') {
if (txtRes[txtRes.length - 2] === ",") {
txtRes += '{"name":"disassembly","type":"disassembly","data":""}]}';
}
try {
sourceViews.forEach(sv => sv.hide());
if (multiview) multiview.hide();
multiview?.hide();
multiview = null;
document.getElementById("ranges").innerHTML = '';
document.getElementById('ranges').style.visibility = "hidden";
document.getElementById('show-hide-ranges').style.visibility = "hidden";
if (disassemblyView) disassemblyView.hide();
sourceViews = [];
document.getElementById("ranges").innerHTML = "";
document.getElementById("ranges").style.visibility = "hidden";
document.getElementById("show-hide-ranges").style.visibility = "hidden";
disassemblyView?.hide();
sourceViews = new Array<CodeView>();
sourceResolver = new SourceResolver();
selectionBroker = new SelectionBroker(sourceResolver);
const jsonObj = JSON.parse(txtRes);
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 });
}
const mainFunction = sourceResolver.getMainFunction(jsonObj);
sourceResolver.setInlinings(jsonObj.inlinings);
sourceResolver.setSourceLineToBytecodePosition(jsonObj.sourceLineToBytecodePosition);
sourceResolver.setSources(jsonObj.sources, fnc);
sourceResolver.setSources(jsonObj.sources, mainFunction);
sourceResolver.setNodePositionMap(jsonObj.nodePositions);
sourceResolver.parsePhases(jsonObj.phases);
const [sourceTab, sourceContainer] = sourceTabs.addTabAndContent("Source");
sourceContainer.classList.add("viewpane", "scrollable");
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();
sourceViews.push(sourceView);
sourceResolver.forEachSource(source => {
const sourceView = new CodeView(sourceContainer, selectionBroker, sourceResolver, source, CodeMode.INLINED_SOURCE);
for (const source of sourceResolver.sources) {
const sourceView = new CodeView(sourceContainer, selectionBroker, sourceResolver,
source, CodeMode.INLINED_SOURCE);
sourceView.show();
sourceViews.push(sourceView);
});
}
const [disassemblyTab, disassemblyContainer] = disassemblyTabs.addTabAndContent("Disassembly");
disassemblyContainer.classList.add("viewpane", "scrollable");
disassemblyTabs.activateTab(disassemblyTab);
disassemblyView = new DisassemblyView(disassemblyContainer, selectionBroker);
disassemblyView.initializeCode(fnc.sourceText);
disassemblyView.initializeCode(mainFunction.sourceText);
if (sourceResolver.disassemblyPhase) {
disassemblyView.initializePerfProfile(jsonObj.eventCounts);
disassemblyView.showContent(sourceResolver.disassemblyPhase.data);
......@@ -107,7 +95,7 @@ window.onload = function () {
multiview.show();
} catch (err) {
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();
}
return;
......@@ -122,21 +110,23 @@ window.onload = function () {
document.getElementById("upload-helper").click();
e.stopPropagation();
});
document.getElementById("upload-helper").addEventListener("change",
function (this: HTMLInputElement) {
const uploadFile = this.files && this.files[0];
if (uploadFile) {
const filereader = new FileReader();
filereader.onload = () => {
const txtRes = filereader.result;
if (typeof txtRes == 'string') {
const fileReader = new FileReader();
fileReader.onload = () => {
const txtRes = fileReader.result;
if (typeof txtRes === "string") {
loadFile(txtRes);
}
};
filereader.readAsText(uploadFile);
fileReader.readAsText(uploadFile);
}
}
);
window.addEventListener("keydown", (e: KeyboardEvent) => {
if (e.keyCode == 76 && e.ctrlKey) { // CTRL + L
document.getElementById("upload-helper").click();
......
This diff is collapsed.
......@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import { GNode } from "../node";
import { GraphNode } from "../phases/graph-phase/graph-node";
export abstract class View {
protected container: HTMLElement;
......@@ -24,8 +24,8 @@ export abstract class View {
}
export abstract class PhaseView extends View {
public abstract initializeContent(data: any, rememberedSelection: Map<string, GNode>): void;
public abstract detachSelection(): Map<string, GNode>;
public abstract initializeContent(data: any, rememberedSelection: Map<string, GraphNode>): void;
public abstract detachSelection(): Map<string, GraphNode>;
public abstract onresize(): void;
public abstract searchInputAction(searchInput: HTMLInputElement, e: Event, onlyVisible: boolean): void;
......
......@@ -17,13 +17,18 @@
"src/common/util.ts",
"src/common/constants.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/graph-phase.ts",
"src/phases/instructions-phase.ts",
"src/phases/phase.ts",
"src/phases/schedule-phase.ts",
"src/phases/sequence-phase.ts",
"src/phases/turboshaft-graph-phase.ts",
"src/selection/selection.ts",
"src/selection/selection-broker.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