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

[turbolizer] Source and bytecode positions

New features:
- bytecode source view handlers
- turboshaft's nodes origins
- turboshaft's nodes history
- turboshaft's nodes source/bytecode positions

Bug: v8:7327
Change-Id: Icb240dd84762284f1aa37db3c93bd133f8e70960
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3829481Reviewed-by: 's avatarNico Hartmann <nicohartmann@chromium.org>
Commit-Queue: Danylo Boiko <danielboyko02@gmail.com>
Cr-Commit-Position: refs/heads/main@{#82682}
parent bf5e3a8a
...@@ -30,6 +30,10 @@ ...@@ -30,6 +30,10 @@
<td>b</td> <td>b</td>
<td>Show graph with selected nodes for previous phase</td> <td>Show graph with selected nodes for previous phase</td>
</tr> </tr>
<tr>
<td>h</td>
<td>Show hovered node's history</td>
</tr>
<tr> <tr>
<td>a</td> <td>a</td>
<td>Select all nodes</td> <td>Select all nodes</td>
...@@ -61,10 +65,6 @@ ...@@ -61,10 +65,6 @@
<td>u</td> <td>u</td>
<td>Hide unselected nodes</td> <td>Hide unselected nodes</td>
</tr> </tr>
<tr>
<td>h</td>
<td>Show hovered node's history</td>
</tr>
</table> </table>
</div> </div>
</div> </div>
......
...@@ -7,13 +7,14 @@ import { storageGetItem, storageSetItem } from "./common/util"; ...@@ -7,13 +7,14 @@ import { storageGetItem, storageSetItem } from "./common/util";
import { GraphView } from "./views/graph-view"; import { GraphView } from "./views/graph-view";
import { ScheduleView } from "./views/schedule-view"; import { ScheduleView } from "./views/schedule-view";
import { SequenceView } from "./views/sequence-view"; import { SequenceView } from "./views/sequence-view";
import { GenericPhase, SourceResolver } from "./source-resolver"; import { DynamicPhase, 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 { GraphPhase } from "./phases/graph-phase/graph-phase"; import { GraphPhase } from "./phases/graph-phase/graph-phase";
import { PhaseType } from "./phases/phase"; import { PhaseType } from "./phases/phase";
import { TurboshaftGraphView } from "./views/turboshaft-graph-view"; import { TurboshaftGraphView } from "./views/turboshaft-graph-view";
import { SelectionStorage } from "./selection/selection-storage"; import { SelectionStorage } from "./selection/selection-storage";
import { TurboshaftGraphPhase } from "./phases/turboshaft-graph-phase/turboshaft-graph-phase";
const toolboxHTML = ` const toolboxHTML = `
<div class="graph-toolbox"> <div class="graph-toolbox">
...@@ -90,21 +91,23 @@ export class GraphMultiView extends View { ...@@ -90,21 +91,23 @@ export class GraphMultiView extends View {
const lastPhaseIndex = storageGetItem("lastSelectedPhase"); const lastPhaseIndex = storageGetItem("lastSelectedPhase");
const initialPhaseIndex = this.sourceResolver.repairPhaseId(lastPhaseIndex); const initialPhaseIndex = this.sourceResolver.repairPhaseId(lastPhaseIndex);
this.selectMenu.selectedIndex = initialPhaseIndex; this.selectMenu.selectedIndex = initialPhaseIndex;
this.displayPhase(this.sourceResolver.getPhase(initialPhaseIndex)); this.displayPhase(this.sourceResolver.getDynamicPhase(initialPhaseIndex));
} }
public displayPhaseByName(phaseName: string, selection?: SelectionStorage): void { public displayPhaseByName(phaseName: string, selection?: SelectionStorage): void {
this.currentPhaseView.hide(); this.currentPhaseView.hide();
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.getDynamicPhase(phaseId), selection);
} }
public onresize(): void { public onresize(): void {
this.currentPhaseView?.onresize(); this.currentPhaseView?.onresize();
} }
private displayPhase(phase: GenericPhase, selection?: SelectionStorage): void { private displayPhase(phase: DynamicPhase, selection?: SelectionStorage): void {
this.sourceResolver.positions = phase.positions;
this.sourceResolver.instructionsPhase = phase.instructionsPhase;
if (phase.type == PhaseType.Graph) { if (phase.type == PhaseType.Graph) {
this.displayPhaseView(this.graph, phase, selection); this.displayPhaseView(this.graph, phase, selection);
} else if (phase.type == PhaseType.TurboshaftGraph) { } else if (phase.type == PhaseType.TurboshaftGraph) {
...@@ -116,7 +119,7 @@ export class GraphMultiView extends View { ...@@ -116,7 +119,7 @@ export class GraphMultiView extends View {
} }
} }
private displayPhaseView(view: PhaseView, data: GenericPhase, selection?: SelectionStorage): private displayPhaseView(view: PhaseView, data: DynamicPhase, selection?: SelectionStorage):
void { void {
const rememberedSelection = selection ? selection : this.hideCurrentPhase(); const rememberedSelection = selection ? selection : this.hideCurrentPhase();
view.initializeContent(data, rememberedSelection); view.initializeContent(data, rememberedSelection);
...@@ -126,8 +129,8 @@ export class GraphMultiView extends View { ...@@ -126,8 +129,8 @@ export class GraphMultiView extends View {
private displayNextGraphPhase(): void { private displayNextGraphPhase(): void {
let nextPhaseIndex = this.selectMenu.selectedIndex + 1; let nextPhaseIndex = this.selectMenu.selectedIndex + 1;
while (nextPhaseIndex < this.sourceResolver.phases.length) { while (nextPhaseIndex < this.sourceResolver.phases.length) {
const nextPhase = this.sourceResolver.getPhase(nextPhaseIndex); const nextPhase = this.sourceResolver.getDynamicPhase(nextPhaseIndex);
if (nextPhase.isGraph()) { if (nextPhase && nextPhase.isGraph()) {
this.selectMenu.selectedIndex = nextPhaseIndex; this.selectMenu.selectedIndex = nextPhaseIndex;
storageSetItem("lastSelectedPhase", nextPhaseIndex); storageSetItem("lastSelectedPhase", nextPhaseIndex);
this.displayPhase(nextPhase); this.displayPhase(nextPhase);
...@@ -140,8 +143,8 @@ export class GraphMultiView extends View { ...@@ -140,8 +143,8 @@ export class GraphMultiView extends View {
private displayPreviousGraphPhase(): void { private displayPreviousGraphPhase(): void {
let previousPhaseIndex = this.selectMenu.selectedIndex - 1; let previousPhaseIndex = this.selectMenu.selectedIndex - 1;
while (previousPhaseIndex >= 0) { while (previousPhaseIndex >= 0) {
const previousPhase = this.sourceResolver.getPhase(previousPhaseIndex); const previousPhase = this.sourceResolver.getDynamicPhase(previousPhaseIndex);
if (previousPhase.isGraph()) { if (previousPhase && previousPhase.isGraph()) {
this.selectMenu.selectedIndex = previousPhaseIndex; this.selectMenu.selectedIndex = previousPhaseIndex;
storageSetItem("lastSelectedPhase", previousPhaseIndex); storageSetItem("lastSelectedPhase", previousPhaseIndex);
this.displayPhase(previousPhase); this.displayPhase(previousPhase);
...@@ -157,7 +160,8 @@ export class GraphMultiView extends View { ...@@ -157,7 +160,8 @@ export class GraphMultiView extends View {
for (const phase of view.sourceResolver.phases) { for (const phase of view.sourceResolver.phases) {
const optionElement = document.createElement("option"); const optionElement = document.createElement("option");
let maxNodeId = ""; let maxNodeId = "";
if (phase instanceof GraphPhase && phase.highestNodeId != 0) { if ((phase instanceof GraphPhase || phase instanceof TurboshaftGraphPhase)
&& phase.highestNodeId != 0) {
maxNodeId = ` ${phase.highestNodeId}`; maxNodeId = ` ${phase.highestNodeId}`;
} }
optionElement.text = `${phase.name}${maxNodeId}`; optionElement.text = `${phase.name}${maxNodeId}`;
...@@ -166,7 +170,7 @@ export class GraphMultiView extends View { ...@@ -166,7 +170,7 @@ export class GraphMultiView extends View {
this.selectMenu.onchange = function (this: HTMLSelectElement) { this.selectMenu.onchange = function (this: HTMLSelectElement) {
const phaseIndex = this.selectedIndex; const phaseIndex = this.selectedIndex;
storageSetItem("lastSelectedPhase", phaseIndex); storageSetItem("lastSelectedPhase", phaseIndex);
view.displayPhase(view.sourceResolver.getPhase(phaseIndex)); view.displayPhase(view.sourceResolver.getDynamicPhase(phaseIndex));
}; };
} }
......
...@@ -11,7 +11,8 @@ export class NodeLabel { ...@@ -11,7 +11,8 @@ export class NodeLabel {
title: string; title: string;
live: boolean; live: boolean;
properties: string; properties: string;
sourcePosition: SourcePosition | BytecodePosition; sourcePosition: SourcePosition;
bytecodePosition: BytecodePosition;
origin: NodeOrigin | BytecodeOrigin; origin: NodeOrigin | BytecodeOrigin;
opcode: string; opcode: string;
control: boolean; control: boolean;
...@@ -20,15 +21,16 @@ export class NodeLabel { ...@@ -20,15 +21,16 @@ export class NodeLabel {
inplaceUpdatePhase: string; inplaceUpdatePhase: string;
constructor(id: number, label: string, title: string, live: boolean, constructor(id: number, label: string, title: string, live: boolean,
properties: string, sourcePosition: SourcePosition | BytecodePosition, properties: string, sourcePosition: SourcePosition,
origin: NodeOrigin | BytecodeOrigin, opcode: string, control: boolean, bytecodePosition: BytecodePosition, origin: NodeOrigin | BytecodeOrigin,
opinfo: string, type: string) { opcode: string, control: boolean, opinfo: string, type: string) {
this.id = id; this.id = id;
this.label = label; this.label = label;
this.title = title; this.title = title;
this.live = live; this.live = live;
this.properties = properties; this.properties = properties;
this.sourcePosition = sourcePosition; this.sourcePosition = sourcePosition;
this.bytecodePosition = bytecodePosition;
this.origin = origin; this.origin = origin;
this.opcode = opcode; this.opcode = opcode;
this.control = control; this.control = control;
......
...@@ -2,6 +2,9 @@ ...@@ -2,6 +2,9 @@
// 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 { GraphNode } from "./phases/graph-phase/graph-node";
import { TurboshaftGraphNode } from "./phases/turboshaft-graph-phase/turboshaft-graph-node";
export abstract class Origin { export abstract class Origin {
phase: string; phase: string;
reducer: string; reducer: string;
...@@ -14,10 +17,13 @@ export abstract class Origin { ...@@ -14,10 +17,13 @@ export abstract class Origin {
export class NodeOrigin extends Origin { export class NodeOrigin extends Origin {
nodeId: number; nodeId: number;
node: GraphNode | TurboshaftGraphNode;
constructor(nodeId: number, phase: string, reducer: string) { constructor(nodeId: number, node: GraphNode | TurboshaftGraphNode, phase: string,
reducer: string) {
super(phase, reducer); super(phase, reducer);
this.nodeId = nodeId; this.nodeId = nodeId;
this.node = node;
} }
public identifier(): string { public identifier(): string {
......
...@@ -7,6 +7,7 @@ import { alignUp, measureText } from "../../common/util"; ...@@ -7,6 +7,7 @@ import { alignUp, measureText } from "../../common/util";
import { NodeLabel } from "../../node-label"; import { NodeLabel } from "../../node-label";
import { Node } from "../../node"; import { Node } from "../../node";
import { GraphEdge } from "./graph-edge"; import { GraphEdge } from "./graph-edge";
import { NodeOrigin } from "../../origin";
export class GraphNode extends Node<GraphEdge> { export class GraphNode extends Node<GraphEdge> {
nodeLabel: NodeLabel; nodeLabel: NodeLabel;
...@@ -79,6 +80,19 @@ export class GraphNode extends Node<GraphEdge> { ...@@ -79,6 +80,19 @@ export class GraphNode extends Node<GraphEdge> {
return this.nodeLabel.getTitle(); return this.nodeLabel.getTitle();
} }
public getHistoryLabel(): string {
return `${this.id} ${this.nodeLabel.opcode}`;
}
public getNodeOrigin(): NodeOrigin {
const origin = this.nodeLabel.origin;
return origin instanceof NodeOrigin ? origin : null;
}
public getInplaceUpdatePhase(): string {
return this?.nodeLabel?.inplaceUpdatePhase;
}
public getDisplayLabel(): string { public getDisplayLabel(): string {
return this.nodeLabel.getDisplayLabel(); return this.nodeLabel.getDisplayLabel();
} }
......
...@@ -5,81 +5,109 @@ ...@@ -5,81 +5,109 @@
import { Phase, PhaseType } from "../phase"; import { Phase, PhaseType } from "../phase";
import { NodeLabel } from "../../node-label"; import { NodeLabel } from "../../node-label";
import { BytecodeOrigin, NodeOrigin } from "../../origin"; import { BytecodeOrigin, NodeOrigin } from "../../origin";
import { SourcePosition } from "../../position";
import { GraphNode } from "./graph-node"; import { GraphNode } from "./graph-node";
import { GraphEdge } from "./graph-edge"; import { GraphEdge } from "./graph-edge";
import { Source } from "../../source";
import { InstructionsPhase } from "../instructions-phase";
import {
BytecodePosition,
InliningPosition,
PositionsContainer,
SourcePosition
} from "../../position";
export class GraphPhase extends Phase { export class GraphPhase extends Phase {
highestNodeId: number;
data: GraphData; data: GraphData;
stateType: GraphStateType; stateType: GraphStateType;
nodeLabelMap: Array<NodeLabel>; instructionsPhase: InstructionsPhase;
nodeIdToNodeMap: Array<GraphNode>; nodeIdToNodeMap: Array<GraphNode>;
originIdToNodesMap: Map<string, Array<GraphNode>>; originIdToNodesMap: Map<string, Array<GraphNode>>;
positions: PositionsContainer;
highestNodeId: number;
rendered: boolean; rendered: boolean;
transform: { x: number, y: number, scale: number }; transform: { x: number, y: number, scale: number };
constructor(name: string, highestNodeId: number, dataJson, nodeLabelMap?: Array<NodeLabel>) { constructor(name: string, dataJson, nodeMap: Array<GraphNode>, sources: Array<Source>,
inlinings: Array<InliningPosition>) {
super(name, PhaseType.Graph); super(name, PhaseType.Graph);
this.highestNodeId = highestNodeId;
this.data = new GraphData(); this.data = new GraphData();
this.stateType = GraphStateType.NeedToFullRebuild; this.stateType = GraphStateType.NeedToFullRebuild;
this.instructionsPhase = new InstructionsPhase();
this.nodeIdToNodeMap = new Array<GraphNode>(); this.nodeIdToNodeMap = new Array<GraphNode>();
this.originIdToNodesMap = new Map<string, Array<GraphNode>>(); this.originIdToNodesMap = new Map<string, Array<GraphNode>>();
this.positions = new PositionsContainer();
this.highestNodeId = 0;
this.rendered = false; this.rendered = false;
this.parseDataFromJSON(dataJson, nodeLabelMap); this.parseDataFromJSON(dataJson, nodeMap, sources, inlinings);
this.nodeLabelMap = nodeLabelMap?.slice();
} }
private parseDataFromJSON(dataJson, nodeLabelMap: Array<NodeLabel>): void { private parseDataFromJSON(dataJson, nodeMap: Array<GraphNode>, sources: Array<Source>,
inlinings: Array<InliningPosition>): void {
this.data = new GraphData(); this.data = new GraphData();
this.nodeIdToNodeMap = this.parseNodesFromJSON(dataJson.nodes, nodeLabelMap); this.parseNodesFromJSON(dataJson.nodes, nodeMap, sources, inlinings);
this.parseEdgesFromJSON(dataJson.edges); this.parseEdgesFromJSON(dataJson.edges);
} }
private parseNodesFromJSON(nodesJSON, nodeLabelMap: Array<NodeLabel>): Array<GraphNode> { private parseNodesFromJSON(nodesJSON, nodeMap: Array<GraphNode>, sources: Array<Source>,
const nodeIdToNodeMap = new Array<GraphNode>(); inlinings: Array<InliningPosition>): void {
for (const node of nodesJSON) { for (const node of nodesJSON) {
let sourcePosition: SourcePosition = null;
const sourcePositionJson = node.sourcePosition;
if (sourcePositionJson) {
const scriptOffset = sourcePositionJson.scriptOffset;
const inliningId = sourcePositionJson.inliningId;
sourcePosition = new SourcePosition(scriptOffset, inliningId);
}
let origin: NodeOrigin | BytecodeOrigin = null; let origin: NodeOrigin | BytecodeOrigin = null;
const jsonOrigin = node.origin; let bytecodePosition: BytecodePosition = null;
if (jsonOrigin) { const originJson = node.origin;
if (jsonOrigin.nodeId) { if (originJson) {
origin = new NodeOrigin(jsonOrigin.nodeId, jsonOrigin.phase, jsonOrigin.reducer); const nodeId = originJson.nodeId;
if (nodeId) {
origin = new NodeOrigin(nodeId, nodeMap[nodeId], originJson.phase, originJson.reducer);
bytecodePosition = nodeMap[nodeId]?.nodeLabel.bytecodePosition;
} else { } else {
origin = new BytecodeOrigin(jsonOrigin.bytecodePosition, jsonOrigin.phase, origin = new BytecodeOrigin(originJson.bytecodePosition, originJson.phase,
jsonOrigin.reducer); originJson.reducer);
const inliningId = sourcePosition ? sourcePosition.inliningId : -1;
bytecodePosition = new BytecodePosition(originJson.bytecodePosition, inliningId);
} }
} }
let sourcePosition: SourcePosition = null;
if (node.sourcePosition) {
const scriptOffset = node.sourcePosition.scriptOffset;
const inliningId = node.sourcePosition.inliningId;
sourcePosition = new SourcePosition(scriptOffset, inliningId);
}
const label = new NodeLabel(node.id, node.label, node.title, node.live, node.properties, const label = new NodeLabel(node.id, node.label, node.title, node.live, node.properties,
sourcePosition, origin, node.opcode, node.control, node.opinfo, node.type); sourcePosition, bytecodePosition, origin, node.opcode, node.control, node.opinfo,
node.type);
const previous = nodeLabelMap[label.id];
if (!label.equals(previous)) {
if (previous !== undefined) {
label.setInplaceUpdatePhase(this.name);
}
nodeLabelMap[label.id] = label;
}
const newNode = new GraphNode(label); const newNode = new GraphNode(label);
this.data.nodes.push(newNode); this.data.nodes.push(newNode);
nodeIdToNodeMap[newNode.identifier()] = newNode; this.highestNodeId = Math.max(this.highestNodeId, newNode.id);
if (origin) { this.nodeIdToNodeMap[newNode.identifier()] = newNode;
const previous = nodeMap[newNode.id];
if (!newNode.equals(previous)) {
if (previous) newNode.nodeLabel.setInplaceUpdatePhase(this.name);
nodeMap[newNode.id] = newNode;
}
if (origin && origin instanceof NodeOrigin) {
const identifier = origin.identifier(); const identifier = origin.identifier();
if (!this.originIdToNodesMap.has(identifier)) { if (!this.originIdToNodesMap.has(identifier)) {
this.originIdToNodesMap.set(identifier, new Array<GraphNode>()); this.originIdToNodesMap.set(identifier, new Array<GraphNode>());
} }
this.originIdToNodesMap.get(identifier).push(newNode); this.originIdToNodesMap.get(identifier).push(newNode);
} }
if (sourcePosition) {
const inlining = inlinings[sourcePosition.inliningId];
if (inlining) sources[inlining.sourceId].sourcePositions.push(sourcePosition);
this.positions.addSourcePosition(newNode.identifier(), sourcePosition);
}
if (bytecodePosition) {
this.positions.addBytecodePosition(newNode.identifier(), bytecodePosition);
}
} }
return nodeIdToNodeMap;
} }
private parseEdgesFromJSON(edgesJSON): void { private parseEdgesFromJSON(edgesJSON): void {
......
...@@ -37,6 +37,18 @@ export class InstructionsPhase extends Phase { ...@@ -37,6 +37,18 @@ export class InstructionsPhase extends Phase {
return -1; return -1;
} }
public merge(other: InstructionsPhase): void {
if (!other) return;
if (this.name == "") this.name = "merged data";
this.nodeIdToInstructionRange = new Array<[number, number]>();
this.blockIdToInstructionRange = other.blockIdToInstructionRange;
this.instructionOffsetToPCOffset = other.instructionOffsetToPCOffset;
this.codeOffsetsInfo = other.codeOffsetsInfo;
this.instructionToPCOffset = other.instructionToPCOffset;
this.pcOffsetToInstructions = other.pcOffsetToInstructions;
this.pcOffsets = other.pcOffsets;
}
public instructionToPcOffsets(instruction: number): TurbolizerInstructionStartInfo { public instructionToPcOffsets(instruction: number): TurbolizerInstructionStartInfo {
return this.instructionToPCOffset[instruction]; return this.instructionToPCOffset[instruction];
} }
......
...@@ -12,8 +12,11 @@ export abstract class Phase { ...@@ -12,8 +12,11 @@ export abstract class Phase {
} }
public isGraph(): boolean { public isGraph(): boolean {
return this.type == PhaseType.Graph || return this.type == PhaseType.Graph || this.type == PhaseType.TurboshaftGraph;
this.type == PhaseType.TurboshaftGraph; }
public isDynamic(): boolean {
return this.isGraph() || this.type == PhaseType.Schedule || this.type == PhaseType.Sequence;
} }
} }
......
...@@ -3,13 +3,19 @@ ...@@ -3,13 +3,19 @@
// found in the LICENSE file. // found in the LICENSE file.
import { Phase, PhaseType } from "./phase"; import { Phase, PhaseType } from "./phase";
import { PositionsContainer } from "../position";
import { InstructionsPhase } from "./instructions-phase";
export class SchedulePhase extends Phase { export class SchedulePhase extends Phase {
data: ScheduleData; data: ScheduleData;
instructionsPhase: InstructionsPhase;
positions: PositionsContainer;
constructor(name: string, dataJson) { constructor(name: string, dataJson) {
super(name, PhaseType.Schedule); super(name, PhaseType.Schedule);
this.data = new ScheduleData(); this.data = new ScheduleData();
this.instructionsPhase = new InstructionsPhase();
this.positions = new PositionsContainer();
this.parseScheduleFromJSON(dataJson); this.parseScheduleFromJSON(dataJson);
} }
......
...@@ -4,13 +4,19 @@ ...@@ -4,13 +4,19 @@
import * as C from "../common/constants"; import * as C from "../common/constants";
import { Phase, PhaseType } from "./phase"; import { Phase, PhaseType } from "./phase";
import { PositionsContainer } from "../position";
import { InstructionsPhase } from "./instructions-phase";
export class SequencePhase extends Phase { export class SequencePhase extends Phase {
blocks: Array<SequenceBlock>; blocks: Array<SequenceBlock>;
instructionsPhase: InstructionsPhase;
positions: PositionsContainer;
registerAllocation: RegisterAllocation; registerAllocation: RegisterAllocation;
constructor(name: string, blocksJSON, registerAllocationJSON) { constructor(name: string, blocksJSON, registerAllocationJSON) {
super(name, PhaseType.Sequence); super(name, PhaseType.Sequence);
this.instructionsPhase = new InstructionsPhase();
this.positions = new PositionsContainer();
this.parseBlocksFromJSON(blocksJSON); this.parseBlocksFromJSON(blocksJSON);
this.parseRegisterAllocationFromJSON(registerAllocationJSON); this.parseRegisterAllocationFromJSON(registerAllocationJSON);
} }
......
...@@ -7,17 +7,26 @@ import { measureText } from "../../common/util"; ...@@ -7,17 +7,26 @@ import { measureText } from "../../common/util";
import { TurboshaftGraphEdge } from "./turboshaft-graph-edge"; import { TurboshaftGraphEdge } from "./turboshaft-graph-edge";
import { TurboshaftGraphBlock } from "./turboshaft-graph-block"; import { TurboshaftGraphBlock } from "./turboshaft-graph-block";
import { Node } from "../../node"; import { Node } from "../../node";
import { BytecodePosition, SourcePosition } from "../../position";
import { NodeOrigin } from "../../origin";
export class TurboshaftGraphNode extends Node<TurboshaftGraphEdge<TurboshaftGraphNode>> { export class TurboshaftGraphNode extends Node<TurboshaftGraphEdge<TurboshaftGraphNode>> {
title: string; title: string;
block: TurboshaftGraphBlock; block: TurboshaftGraphBlock;
sourcePosition: SourcePosition;
bytecodePosition: BytecodePosition;
origin: NodeOrigin;
opPropertiesType: OpPropertiesType; opPropertiesType: OpPropertiesType;
constructor(id: number, title: string, block: TurboshaftGraphBlock, constructor(id: number, title: string, block: TurboshaftGraphBlock,
opPropertiesType: OpPropertiesType) { sourcePosition: SourcePosition, bytecodePosition: BytecodePosition,
origin: NodeOrigin, opPropertiesType: OpPropertiesType) {
super(id); super(id);
this.title = title; this.title = title;
this.block = block; this.block = block;
this.sourcePosition = sourcePosition;
this.bytecodePosition = bytecodePosition;
this.origin = origin;
this.opPropertiesType = opPropertiesType; this.opPropertiesType = opPropertiesType;
this.visible = true; this.visible = true;
} }
...@@ -37,6 +46,9 @@ export class TurboshaftGraphNode extends Node<TurboshaftGraphEdge<TurboshaftGrap ...@@ -37,6 +46,9 @@ export class TurboshaftGraphNode extends Node<TurboshaftGraphEdge<TurboshaftGrap
public getTitle(): string { public getTitle(): string {
let title = `${this.id} ${this.title} ${this.opPropertiesType}`; let title = `${this.id} ${this.title} ${this.opPropertiesType}`;
if (this.origin) {
title += `\nOrigin: ${this.origin.toString()}`;
}
if (this.inputs.length > 0) { if (this.inputs.length > 0) {
title += `\nInputs: ${this.inputs.map(i => i.source.id).join(", ")}`; title += `\nInputs: ${this.inputs.map(i => i.source.id).join(", ")}`;
} }
...@@ -46,10 +58,24 @@ export class TurboshaftGraphNode extends Node<TurboshaftGraphEdge<TurboshaftGrap ...@@ -46,10 +58,24 @@ export class TurboshaftGraphNode extends Node<TurboshaftGraphEdge<TurboshaftGrap
return title; return title;
} }
public getHistoryLabel(): string {
return `${this.id} ${this.title}`;
}
public getNodeOrigin(): NodeOrigin {
return this.origin;
}
public getInlineLabel(): string { public getInlineLabel(): string {
if (this.inputs.length == 0) return `${this.id} ${this.title}`; if (this.inputs.length == 0) return `${this.id} ${this.title}`;
return `${this.id} ${this.title}(${this.inputs.map(i => i.source.id).join(",")})`; return `${this.id} ${this.title}(${this.inputs.map(i => i.source.id).join(",")})`;
} }
public equals(that?: TurboshaftGraphNode): boolean {
if (!that) return false;
if (this.id !== that.id) return false;
return this.title === that.title;
}
} }
export enum OpPropertiesType { export enum OpPropertiesType {
......
...@@ -7,31 +7,51 @@ import { TurboshaftGraphNode } from "./turboshaft-graph-node"; ...@@ -7,31 +7,51 @@ import { TurboshaftGraphNode } from "./turboshaft-graph-node";
import { TurboshaftGraphEdge } from "./turboshaft-graph-edge"; import { TurboshaftGraphEdge } from "./turboshaft-graph-edge";
import { TurboshaftGraphBlock } from "./turboshaft-graph-block"; import { TurboshaftGraphBlock } from "./turboshaft-graph-block";
import { DataTarget, TurboshaftCustomDataPhase } from "../turboshaft-custom-data-phase"; import { DataTarget, TurboshaftCustomDataPhase } from "../turboshaft-custom-data-phase";
import { GraphNode } from "../graph-phase/graph-node";
import { NodeOrigin } from "../../origin";
import { Source } from "../../source";
import { InstructionsPhase } from "../instructions-phase";
import {
BytecodePosition,
InliningPosition,
PositionsContainer,
SourcePosition
} from "../../position";
export class TurboshaftGraphPhase extends Phase { export class TurboshaftGraphPhase extends Phase {
data: TurboshaftGraphData; data: TurboshaftGraphData;
customData: TurboshaftCustomData; customData: TurboshaftCustomData;
stateType: GraphStateType; stateType: GraphStateType;
instructionsPhase: InstructionsPhase;
nodeIdToNodeMap: Array<TurboshaftGraphNode>; nodeIdToNodeMap: Array<TurboshaftGraphNode>;
blockIdToBlockMap: Array<TurboshaftGraphBlock>; blockIdToBlockMap: Array<TurboshaftGraphBlock>;
originIdToNodesMap: Map<string, Array<TurboshaftGraphNode>>;
positions: PositionsContainer;
highestNodeId: number;
rendered: boolean; rendered: boolean;
customDataShowed: boolean; customDataShowed: boolean;
transform: { x: number, y: number, scale: number }; transform: { x: number, y: number, scale: number };
constructor(name: string, dataJson) { constructor(name: string, dataJson, nodeMap: Array<GraphNode | TurboshaftGraphNode>,
sources: Array<Source>, inlinings: Array<InliningPosition>) {
super(name, PhaseType.TurboshaftGraph); super(name, PhaseType.TurboshaftGraph);
this.stateType = GraphStateType.NeedToFullRebuild; this.stateType = GraphStateType.NeedToFullRebuild;
this.instructionsPhase = new InstructionsPhase();
this.customData = new TurboshaftCustomData(); this.customData = new TurboshaftCustomData();
this.nodeIdToNodeMap = new Array<TurboshaftGraphNode>(); this.nodeIdToNodeMap = new Array<TurboshaftGraphNode>();
this.blockIdToBlockMap = new Array<TurboshaftGraphBlock>(); this.blockIdToBlockMap = new Array<TurboshaftGraphBlock>();
this.originIdToNodesMap = new Map<string, Array<TurboshaftGraphNode>>();
this.positions = new PositionsContainer();
this.highestNodeId = 0;
this.rendered = false; this.rendered = false;
this.parseDataFromJSON(dataJson); this.parseDataFromJSON(dataJson, nodeMap, sources, inlinings);
} }
private parseDataFromJSON(dataJson): void { private parseDataFromJSON(dataJson, nodeMap: Array<GraphNode | TurboshaftGraphNode>,
sources: Array<Source>, inlinings: Array<InliningPosition>): void {
this.data = new TurboshaftGraphData(); this.data = new TurboshaftGraphData();
this.parseBlocksFromJSON(dataJson.blocks); this.parseBlocksFromJSON(dataJson.blocks);
this.parseNodesFromJSON(dataJson.nodes); this.parseNodesFromJSON(dataJson.nodes, nodeMap, sources, inlinings);
this.parseEdgesFromJSON(dataJson.edges); this.parseEdgesFromJSON(dataJson.edges);
} }
...@@ -52,14 +72,62 @@ export class TurboshaftGraphPhase extends Phase { ...@@ -52,14 +72,62 @@ export class TurboshaftGraphPhase extends Phase {
} }
} }
private parseNodesFromJSON(nodesJson): void { private parseNodesFromJSON(nodesJson, nodeMap: Array<GraphNode | TurboshaftGraphNode>,
sources: Array<Source>, inlinings: Array<InliningPosition>): void {
for (const nodeJson of nodesJson) { for (const nodeJson of nodesJson) {
const block = this.blockIdToBlockMap[nodeJson.block_id]; const block = this.blockIdToBlockMap[nodeJson.block_id];
const node = new TurboshaftGraphNode(nodeJson.id, nodeJson.title,
block, nodeJson.op_properties_type); let sourcePosition: SourcePosition = null;
const sourcePositionJson = nodeJson.sourcePosition;
if (sourcePositionJson) {
const scriptOffset = sourcePositionJson.scriptOffset;
const inliningId = sourcePositionJson.inliningId;
sourcePosition = new SourcePosition(scriptOffset, inliningId);
}
let origin: NodeOrigin = null;
let bytecodePosition: BytecodePosition = null;
const originJson = nodeJson.origin;
if (originJson) {
const nodeId = originJson.nodeId;
const originNode = nodeMap[nodeId];
origin = new NodeOrigin(nodeId, originNode, originJson.phase, originJson.reducer);
if (originNode) {
if (originNode instanceof GraphNode) {
bytecodePosition = originNode.nodeLabel.bytecodePosition;
} else {
bytecodePosition = originNode.bytecodePosition;
}
}
}
const node = new TurboshaftGraphNode(nodeJson.id, nodeJson.title, block, sourcePosition,
bytecodePosition, origin, nodeJson.op_properties_type);
block.nodes.push(node); block.nodes.push(node);
this.data.nodes.push(node); this.data.nodes.push(node);
this.nodeIdToNodeMap[node.identifier()] = node; this.nodeIdToNodeMap[node.identifier()] = node;
this.highestNodeId = Math.max(this.highestNodeId, node.id);
if (origin) {
const identifier = origin.identifier();
if (!this.originIdToNodesMap.has(identifier)) {
this.originIdToNodesMap.set(identifier, new Array<TurboshaftGraphNode>());
}
this.originIdToNodesMap.get(identifier).push(node);
}
nodeMap[node.id] = node;
if (sourcePosition) {
const inlining = inlinings[sourcePosition.inliningId];
if (inlining) sources[inlining.sourceId].sourcePositions.push(sourcePosition);
this.positions.addSourcePosition(node.identifier(), sourcePosition);
}
if (bytecodePosition) {
this.positions.addBytecodePosition(node.identifier(), bytecodePosition);
}
} }
for (const block of this.blockIdToBlockMap) { for (const block of this.blockIdToBlockMap) {
block.initCollapsedLabel(); block.initCollapsedLabel();
...@@ -127,7 +195,9 @@ export class TurboshaftCustomData { ...@@ -127,7 +195,9 @@ export class TurboshaftCustomData {
private concatCustomData(key: number, items: Map<string, TurboshaftCustomDataPhase>): string { private concatCustomData(key: number, items: Map<string, TurboshaftCustomDataPhase>): string {
let customData = ""; let customData = "";
for (const [name, dataPhase] of items.entries()) { for (const [name, dataPhase] of items.entries()) {
customData += `\n${name}: ${dataPhase.data[key] ?? ""}`; if (dataPhase.data[key] && dataPhase.data[key].length > 0) {
customData += `\n${name}: ${dataPhase.data[key]}`;
}
} }
return customData; return customData;
} }
......
...@@ -2,6 +2,8 @@ ...@@ -2,6 +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 { TurboshaftGraphNode } from "./phases/turboshaft-graph-phase/turboshaft-graph-node";
export class InliningPosition { export class InliningPosition {
sourceId: number; sourceId: number;
inliningPosition: SourcePosition; inliningPosition: SourcePosition;
...@@ -43,9 +45,11 @@ export class SourcePosition { ...@@ -43,9 +45,11 @@ export class SourcePosition {
export class BytecodePosition { export class BytecodePosition {
bytecodePosition: number; bytecodePosition: number;
inliningId: number;
constructor(bytecodePosition: number) { constructor(bytecodePosition: number, inliningId: number) {
this.bytecodePosition = bytecodePosition; this.bytecodePosition = bytecodePosition;
this.inliningId = inliningId;
} }
public isValid(): boolean { public isValid(): boolean {
...@@ -53,6 +57,54 @@ export class BytecodePosition { ...@@ -53,6 +57,54 @@ export class BytecodePosition {
} }
public toString(): string { public toString(): string {
return `BCP:${this.bytecodePosition}`; return `BCP:${this.inliningId}:${this.bytecodePosition}`;
}
}
export class PositionsContainer {
nodeIdToSourcePositionMap: Array<SourcePosition>;
nodeIdToBytecodePositionMap: Array<BytecodePosition>;
sourcePositionToNodes: Map<string, Array<string>>;
bytecodePositionToNodes: Map<string, Array<string>>;
constructor() {
this.nodeIdToSourcePositionMap = new Array<SourcePosition>();
this.nodeIdToBytecodePositionMap = new Array<BytecodePosition>();
this.sourcePositionToNodes = new Map<string, Array<string>>();
this.bytecodePositionToNodes = new Map<string, Array<string>>();
}
public addSourcePosition(nodeIdentifier: string, sourcePosition: SourcePosition): void {
this.nodeIdToSourcePositionMap[nodeIdentifier] = sourcePosition;
const key = sourcePosition.toString();
if (!this.sourcePositionToNodes.has(key)) {
this.sourcePositionToNodes.set(key, new Array<string>());
}
const nodes = this.sourcePositionToNodes.get(key);
if (!nodes.includes(nodeIdentifier)) nodes.push(nodeIdentifier);
}
public addBytecodePosition(nodeIdentifier: string, bytecodePosition: BytecodePosition): void {
this.nodeIdToBytecodePositionMap[nodeIdentifier] = bytecodePosition;
const key = bytecodePosition.toString();
if (!this.bytecodePositionToNodes.has(key)) {
this.bytecodePositionToNodes.set(key, new Array<string>());
}
const nodes = this.bytecodePositionToNodes.get(key);
if (!nodes.includes(nodeIdentifier)) nodes.push(nodeIdentifier);
}
public merge(nodes: Array<TurboshaftGraphNode>, replacements: Map<number, number>): void {
for (const node of nodes) {
const sourcePosition = node.sourcePosition;
const bytecodePosition = node.bytecodePosition;
const nodeId = replacements.has(node.id) ? replacements.get(node.id) : node.id;
if (sourcePosition && !this.nodeIdToSourcePositionMap[nodeId]) {
this.addSourcePosition(String(nodeId), sourcePosition);
}
if (bytecodePosition && !this.nodeIdToBytecodePositionMap[nodeId]) {
this.addBytecodePosition(String(nodeId), bytecodePosition);
}
}
} }
} }
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
import { GenericPosition, SourceResolver } from "../source-resolver"; import { GenericPosition, SourceResolver } from "../source-resolver";
import { GraphNode } from "../phases/graph-phase/graph-node"; import { GraphNode } from "../phases/graph-phase/graph-node";
import { BytecodePosition } from "../position";
import { TurboshaftGraphNode } from "../phases/turboshaft-graph-phase/turboshaft-graph-node";
import { import {
ClearableHandler, ClearableHandler,
SourcePositionSelectionHandler, SourcePositionSelectionHandler,
...@@ -11,7 +13,8 @@ import { ...@@ -11,7 +13,8 @@ import {
BlockSelectionHandler, BlockSelectionHandler,
InstructionSelectionHandler, InstructionSelectionHandler,
RegisterAllocationSelectionHandler, RegisterAllocationSelectionHandler,
HistoryHandler HistoryHandler,
BytecodeOffsetSelectionHandler
} from "./selection-handler"; } from "./selection-handler";
export class SelectionBroker { export class SelectionBroker {
...@@ -22,6 +25,7 @@ export class SelectionBroker { ...@@ -22,6 +25,7 @@ export class SelectionBroker {
blockHandlers: Array<BlockSelectionHandler>; blockHandlers: Array<BlockSelectionHandler>;
instructionHandlers: Array<InstructionSelectionHandler>; instructionHandlers: Array<InstructionSelectionHandler>;
sourcePositionHandlers: Array<SourcePositionSelectionHandler>; sourcePositionHandlers: Array<SourcePositionSelectionHandler>;
bytecodeOffsetHandlers: Array<BytecodeOffsetSelectionHandler>;
registerAllocationHandlers: Array<RegisterAllocationSelectionHandler>; registerAllocationHandlers: Array<RegisterAllocationSelectionHandler>;
constructor(sourceResolver: SourceResolver) { constructor(sourceResolver: SourceResolver) {
...@@ -32,6 +36,7 @@ export class SelectionBroker { ...@@ -32,6 +36,7 @@ export class SelectionBroker {
this.blockHandlers = new Array<BlockSelectionHandler>(); this.blockHandlers = new Array<BlockSelectionHandler>();
this.instructionHandlers = new Array<InstructionSelectionHandler>(); this.instructionHandlers = new Array<InstructionSelectionHandler>();
this.sourcePositionHandlers = new Array<SourcePositionSelectionHandler>(); this.sourcePositionHandlers = new Array<SourcePositionSelectionHandler>();
this.bytecodeOffsetHandlers = new Array<BytecodeOffsetSelectionHandler>();
this.registerAllocationHandlers = new Array<RegisterAllocationSelectionHandler>(); this.registerAllocationHandlers = new Array<RegisterAllocationSelectionHandler>();
} }
...@@ -74,15 +79,22 @@ export class SelectionBroker { ...@@ -74,15 +79,22 @@ export class SelectionBroker {
this.sourcePositionHandlers.push(handler); this.sourcePositionHandlers.push(handler);
} }
public addBytecodeOffsetHandler(handler: BytecodeOffsetSelectionHandler & ClearableHandler):
void {
this.allHandlers.push(handler);
this.bytecodeOffsetHandlers.push(handler);
}
public addRegisterAllocatorHandler(handler: RegisterAllocationSelectionHandler public addRegisterAllocatorHandler(handler: RegisterAllocationSelectionHandler
& ClearableHandler): void { & ClearableHandler): void {
this.allHandlers.push(handler); this.allHandlers.push(handler);
this.registerAllocationHandlers.push(handler); this.registerAllocationHandlers.push(handler);
} }
public broadcastHistoryShow(from, node: GraphNode, phaseName: string): void { public broadcastHistoryShow(from, node: GraphNode | TurboshaftGraphNode, phaseName: string):
void {
for (const handler of this.historyHandlers) { for (const handler of this.historyHandlers) {
if (handler != from) handler.showTurbofanNodeHistory(node, phaseName); if (handler != from) handler.showNodeHistory(node, phaseName);
} }
} }
...@@ -93,7 +105,7 @@ export class SelectionBroker { ...@@ -93,7 +105,7 @@ export class SelectionBroker {
if (handler != from) handler.brokeredInstructionSelect([instructionOffsets], selected); if (handler != from) handler.brokeredInstructionSelect([instructionOffsets], selected);
} }
// Select the lines from the source panel (left panel) // Select the lines from the source and bytecode panels (left panels)
const pcOffsets = this.sourceResolver.instructionsPhase const pcOffsets = this.sourceResolver.instructionsPhase
.instructionsToKeyPcOffsets(instructionOffsets); .instructionsToKeyPcOffsets(instructionOffsets);
...@@ -103,12 +115,16 @@ export class SelectionBroker { ...@@ -103,12 +115,16 @@ export class SelectionBroker {
for (const handler of this.sourcePositionHandlers) { for (const handler of this.sourcePositionHandlers) {
if (handler != from) handler.brokeredSourcePositionSelect(sourcePositions, selected); if (handler != from) handler.brokeredSourcePositionSelect(sourcePositions, selected);
} }
const bytecodePositions = this.sourceResolver.nodeIdsToBytecodePositions(nodes);
for (const handler of this.bytecodeOffsetHandlers) {
if (handler != from) handler.brokeredBytecodeOffsetSelect(bytecodePositions, selected);
}
} }
// The middle panel lines have already been selected so there's no need to reselect them. // The middle panel lines have already been selected so there's no need to reselect them.
} }
public broadcastSourcePositionSelect(from, sourcePositions: Array<GenericPosition>, public broadcastSourcePositionSelect(from, sourcePositions: Array<GenericPosition>,
selected: boolean): void { selected: boolean, selectedNodes?: Set<string>): void {
sourcePositions = sourcePositions.filter(sourcePosition => { sourcePositions = sourcePositions.filter(sourcePosition => {
if (!sourcePosition.isValid()) { if (!sourcePosition.isValid()) {
console.warn("Invalid source position"); console.warn("Invalid source position");
...@@ -128,6 +144,44 @@ export class SelectionBroker { ...@@ -128,6 +144,44 @@ export class SelectionBroker {
if (handler != from) handler.brokeredNodeSelect(nodes, selected); if (handler != from) handler.brokeredNodeSelect(nodes, selected);
} }
// Select bytecode source panel (left panel)
const bytecodePositions = selectedNodes
? this.sourceResolver.nodeIdsToBytecodePositions(selectedNodes)
: this.sourceResolver.nodeIdsToBytecodePositions(nodes);
for (const handler of this.bytecodeOffsetHandlers) {
if (handler != from) handler.brokeredBytecodeOffsetSelect(bytecodePositions, selected);
}
this.selectInstructionsAndRegisterAllocations(from, nodes, selected);
}
public broadcastBytecodePositionsSelect(from, bytecodePositions: Array<BytecodePosition>,
selected: boolean): void {
bytecodePositions = bytecodePositions.filter(bytecodePosition => {
if (!bytecodePosition.isValid()) {
console.warn("Invalid bytecode position");
return false;
}
return true;
});
// Select the lines from the bytecode panel (left panel)
for (const handler of this.bytecodeOffsetHandlers) {
if (handler != from) handler.brokeredBytecodeOffsetSelect(bytecodePositions, selected);
}
// Select the nodes (middle panel)
const nodes = this.sourceResolver.bytecodePositionsToNodeIds(bytecodePositions);
for (const handler of this.nodeHandlers) {
if (handler != from) handler.brokeredNodeSelect(nodes, selected);
}
// Select the lines from the source panel (left panel)
const sourcePositions = this.sourceResolver.nodeIdsToSourcePositions(nodes);
for (const handler of this.sourcePositionHandlers) {
if (handler != from) handler.brokeredSourcePositionSelect(sourcePositions, selected);
}
this.selectInstructionsAndRegisterAllocations(from, nodes, selected); this.selectInstructionsAndRegisterAllocations(from, nodes, selected);
} }
...@@ -143,6 +197,12 @@ export class SelectionBroker { ...@@ -143,6 +197,12 @@ export class SelectionBroker {
if (handler != from) handler.brokeredSourcePositionSelect(sourcePositions, selected); if (handler != from) handler.brokeredSourcePositionSelect(sourcePositions, selected);
} }
// Select bytecode source panel (left panel)
const bytecodePositions = this.sourceResolver.nodeIdsToBytecodePositions(nodes);
for (const handler of this.bytecodeOffsetHandlers) {
if (handler != from) handler.brokeredBytecodeOffsetSelect(bytecodePositions, selected);
}
this.selectInstructionsAndRegisterAllocations(from, nodes, selected); this.selectInstructionsAndRegisterAllocations(from, nodes, selected);
} }
......
...@@ -6,13 +6,14 @@ import { TurboshaftGraphNode } from "../phases/turboshaft-graph-phase/turboshaft ...@@ -6,13 +6,14 @@ import { TurboshaftGraphNode } from "../phases/turboshaft-graph-phase/turboshaft
import { GraphNode } from "../phases/graph-phase/graph-node"; import { GraphNode } from "../phases/graph-phase/graph-node";
import { TurboshaftGraphBlock } from "../phases/turboshaft-graph-phase/turboshaft-graph-block"; import { TurboshaftGraphBlock } from "../phases/turboshaft-graph-phase/turboshaft-graph-block";
import { GenericPosition } from "../source-resolver"; import { GenericPosition } from "../source-resolver";
import { BytecodePosition } from "../position";
export interface ClearableHandler { export interface ClearableHandler {
brokeredClear(): void; brokeredClear(): void;
} }
export interface HistoryHandler { export interface HistoryHandler {
showTurbofanNodeHistory(node: GraphNode, phaseName: string): void; showNodeHistory(node: GraphNode | TurboshaftGraphNode, phaseName: string): void;
} }
export interface NodeSelectionHandler { export interface NodeSelectionHandler {
...@@ -41,6 +42,12 @@ export interface SourcePositionSelectionHandler { ...@@ -41,6 +42,12 @@ export interface SourcePositionSelectionHandler {
brokeredSourcePositionSelect(sourcePositions: Array<GenericPosition>, selected: boolean): void; brokeredSourcePositionSelect(sourcePositions: Array<GenericPosition>, selected: boolean): void;
} }
export interface BytecodeOffsetSelectionHandler {
select(offsets: Array<number>, selected: boolean): void;
clear(): void;
brokeredBytecodeOffsetSelect(positions: Array<BytecodePosition>, selected: boolean): void;
}
export interface RegisterAllocationSelectionHandler { export interface RegisterAllocationSelectionHandler {
// These are called instructionIds since the class of the divs is "instruction-id" // These are called instructionIds since the class of the divs is "instruction-id"
select(instructionIds: Array<number>, selected: boolean): void; select(instructionIds: Array<number>, selected: boolean): void;
......
This diff is collapsed.
...@@ -15,16 +15,15 @@ export class Source { ...@@ -15,16 +15,15 @@ export class Source {
endPosition?: number; endPosition?: number;
constructor(sourceName: string, functionName: string, sourceText: string, sourceId: number, constructor(sourceName: string, functionName: string, sourceText: string, sourceId: number,
backwardsCompatibility: boolean, sourcePositions?: Array<SourcePosition>, backwardsCompatibility: boolean, startPosition?: number, endPosition?: number) {
startPosition?: number, endPosition?: number) {
this.sourceName = sourceName; this.sourceName = sourceName;
this.functionName = functionName; this.functionName = functionName;
this.sourceText = sourceText; this.sourceText = sourceText;
this.sourceId = sourceId; this.sourceId = sourceId;
this.backwardsCompatibility = backwardsCompatibility; this.backwardsCompatibility = backwardsCompatibility;
this.sourcePositions = sourcePositions ?? new Array<SourcePosition>();
this.startPosition = startPosition; this.startPosition = startPosition;
this.endPosition = endPosition; this.endPosition = endPosition;
this.sourcePositions = new Array<SourcePosition>();
} }
public toString(): string { public toString(): string {
......
...@@ -70,7 +70,7 @@ window.onload = function () { ...@@ -70,7 +70,7 @@ window.onload = function () {
sourceResolver.setSourceLineToBytecodePosition(jsonObj.sourceLineToBytecodePosition); sourceResolver.setSourceLineToBytecodePosition(jsonObj.sourceLineToBytecodePosition);
sourceResolver.setSources(jsonObj.sources, mainFunction); sourceResolver.setSources(jsonObj.sources, mainFunction);
sourceResolver.setBytecodeSources(jsonObj.bytecodeSources); sourceResolver.setBytecodeSources(jsonObj.bytecodeSources);
sourceResolver.setNodePositionMap(jsonObj.nodePositions); sourceResolver.setFinalNodeOrigins(jsonObj.nodeOrigins);
sourceResolver.parsePhases(jsonObj.phases); sourceResolver.parsePhases(jsonObj.phases);
const [sourceTab, sourceContainer] = sourceTabs.addTabAndContent("Source"); const [sourceTab, sourceContainer] = sourceTabs.addTabAndContent("Source");
......
...@@ -7,6 +7,10 @@ import { CodeMode, View } from "./view"; ...@@ -7,6 +7,10 @@ import { CodeMode, View } from "./view";
import { SelectionBroker } from "../selection/selection-broker"; import { SelectionBroker } from "../selection/selection-broker";
import { BytecodeSource } from "../source"; import { BytecodeSource } from "../source";
import { SourceResolver } from "../source-resolver"; import { SourceResolver } from "../source-resolver";
import { SelectionMap } from "../selection/selection-map";
import { ViewElements } from "../common/view-elements";
import { BytecodeOffsetSelectionHandler, ClearableHandler } from "../selection/selection-handler";
import { BytecodePosition } from "../position";
export class BytecodeSourceView extends View { export class BytecodeSourceView extends View {
broker: SelectionBroker; broker: SelectionBroker;
...@@ -14,6 +18,8 @@ export class BytecodeSourceView extends View { ...@@ -14,6 +18,8 @@ export class BytecodeSourceView extends View {
sourceResolver: SourceResolver; sourceResolver: SourceResolver;
codeMode: CodeMode; codeMode: CodeMode;
bytecodeOffsetToHtmlElement: Map<number, HTMLElement>; bytecodeOffsetToHtmlElement: Map<number, HTMLElement>;
bytecodeOffsetSelection: SelectionMap;
bytecodeOffsetSelectionHandler: BytecodeOffsetSelectionHandler & ClearableHandler;
constructor(parent: HTMLElement, broker: SelectionBroker, sourceFunction: BytecodeSource, constructor(parent: HTMLElement, broker: SelectionBroker, sourceFunction: BytecodeSource,
sourceResolver: SourceResolver, codeMode: CodeMode) { sourceResolver: SourceResolver, codeMode: CodeMode) {
...@@ -23,6 +29,9 @@ export class BytecodeSourceView extends View { ...@@ -23,6 +29,9 @@ export class BytecodeSourceView extends View {
this.sourceResolver = sourceResolver; this.sourceResolver = sourceResolver;
this.codeMode = codeMode; this.codeMode = codeMode;
this.bytecodeOffsetToHtmlElement = new Map<number, HTMLElement>(); this.bytecodeOffsetToHtmlElement = new Map<number, HTMLElement>();
this.bytecodeOffsetSelection = new SelectionMap((offset: number) => String(offset));
this.bytecodeOffsetSelectionHandler = this.initializeBytecodeOffsetSelectionHandler();
this.broker.addBytecodeOffsetHandler(this.bytecodeOffsetSelectionHandler);
this.initializeCode(); this.initializeCode();
} }
...@@ -86,6 +95,44 @@ export class BytecodeSourceView extends View { ...@@ -86,6 +95,44 @@ export class BytecodeSourceView extends View {
codePre.appendChild(constantList); codePre.appendChild(constantList);
} }
private initializeBytecodeOffsetSelectionHandler(): BytecodeOffsetSelectionHandler
& ClearableHandler {
const view = this;
const broker = this.broker;
return {
select: function (offsets: Array<number>, selected: boolean) {
const bytecodePositions = new Array<BytecodePosition>();
for (const offset of offsets) {
bytecodePositions.push(new BytecodePosition(offset, view.source.sourceId));
}
view.bytecodeOffsetSelection.select(offsets, selected);
view.updateSelection();
broker.broadcastBytecodePositionsSelect(this, bytecodePositions, selected);
},
clear: function () {
view.bytecodeOffsetSelection.clear();
view.updateSelection();
broker.broadcastClear(this);
},
brokeredBytecodeOffsetSelect: function (positions: Array<BytecodePosition>,
selected: boolean) {
const offsets = new Array<number>();
const firstSelect = view.bytecodeOffsetSelection.isEmpty();
for (const position of positions) {
if (position.inliningId == view.source.sourceId) {
offsets.push(position.bytecodePosition);
}
}
view.bytecodeOffsetSelection.select(offsets, selected);
view.updateSelection(firstSelect);
},
brokeredClear: function () {
view.bytecodeOffsetSelection.clear();
view.updateSelection();
},
};
}
private getBytecodeHeaderHtmlElementName(): string { private getBytecodeHeaderHtmlElementName(): string {
return `source-pre-${this.source.sourceId}-header`; return `source-pre-${this.source.sourceId}-header`;
} }
...@@ -98,13 +145,36 @@ export class BytecodeSourceView extends View { ...@@ -98,13 +145,36 @@ export class BytecodeSourceView extends View {
return this.codeMode == CodeMode.MainSource ? "main-source" : "inlined-source"; return this.codeMode == CodeMode.MainSource ? "main-source" : "inlined-source";
} }
private updateSelection(scrollIntoView: boolean = false): void {
const mkVisible = new ViewElements(this.divNode.parentNode as HTMLElement);
for (const [offset, element] of this.bytecodeOffsetToHtmlElement.entries()) {
const key = this.bytecodeOffsetSelection.stringKey(offset);
const isSelected = this.bytecodeOffsetSelection.isKeySelected(key);
mkVisible.consider(element, isSelected);
element.classList.toggle("selected", isSelected);
}
mkVisible.apply(scrollIntoView);
}
private onSelectBytecodeOffset(offset: number, doClear: boolean) {
if (doClear) {
this.bytecodeOffsetSelectionHandler.clear();
}
this.bytecodeOffsetSelectionHandler.select([offset], undefined);
}
private insertLineContent(lineElement: HTMLElement, content: string): void { private insertLineContent(lineElement: HTMLElement, content: string): void {
const lineContentElement = createElement("span", "", content); const lineContentElement = createElement("span", "", content);
lineElement.appendChild(lineContentElement); lineElement.appendChild(lineContentElement);
} }
private insertLineNumber(lineElement: HTMLElement, lineNumber: number): void { private insertLineNumber(lineElement: HTMLElement, lineNumber: number): void {
const view = this;
const lineNumberElement = createElement("div", "line-number", String(lineNumber)); const lineNumberElement = createElement("div", "line-number", String(lineNumber));
lineNumberElement.onclick = function (e: MouseEvent) {
e.stopPropagation();
view.onSelectBytecodeOffset(lineNumber, !e.shiftKey);
};
lineElement.insertBefore(lineNumberElement, lineElement.firstChild); lineElement.insertBefore(lineNumberElement, lineElement.firstChild);
} }
} }
...@@ -182,7 +182,8 @@ export class CodeView extends View { ...@@ -182,7 +182,8 @@ export class CodeView extends View {
}; };
} }
private addHtmlElementToSourcePosition(sourcePosition, element): void { private addHtmlElementToSourcePosition(sourcePosition: GenericPosition, element: HTMLElement):
void {
const key = sourcePosition.toString(); const key = sourcePosition.toString();
if (!this.sourcePositionToHtmlElements.has(key)) { if (!this.sourcePositionToHtmlElements.has(key)) {
this.sourcePositionToHtmlElements.set(key, new Array<HTMLElement>()); this.sourcePositionToHtmlElements.set(key, new Array<HTMLElement>());
......
...@@ -12,8 +12,7 @@ import { GraphNode } from "../phases/graph-phase/graph-node"; ...@@ -12,8 +12,7 @@ import { GraphNode } from "../phases/graph-phase/graph-node";
import { GraphEdge } from "../phases/graph-phase/graph-edge"; import { GraphEdge } from "../phases/graph-phase/graph-edge";
import { GraphLayout } from "../graph-layout"; import { GraphLayout } from "../graph-layout";
import { GraphPhase, GraphStateType } from "../phases/graph-phase/graph-phase"; import { GraphPhase, GraphStateType } from "../phases/graph-phase/graph-phase";
import { BytecodePosition } from "../position"; import { NodeOrigin } from "../origin";
import { BytecodeOrigin, NodeOrigin } from "../origin";
import { MovableView } from "./movable-view"; import { MovableView } from "./movable-view";
import { ClearableHandler, NodeSelectionHandler } from "../selection/selection-handler"; import { ClearableHandler, NodeSelectionHandler } from "../selection/selection-handler";
import { GenericPosition } from "../source-resolver"; import { GenericPosition } from "../source-resolver";
...@@ -32,8 +31,10 @@ export class GraphView extends MovableView<Graph> { ...@@ -32,8 +31,10 @@ export class GraphView extends MovableView<Graph> {
toolbox: HTMLElement) { toolbox: HTMLElement) {
super(idOrContainer, broker, showPhaseByName, toolbox); super(idOrContainer, broker, showPhaseByName, toolbox);
this.state.selection = new SelectionMap(node => node.identifier(), this.state.selection = new SelectionMap(node => node.identifier(), node => {
node => node.nodeLabel?.origin?.identifier()); if (node instanceof GraphNode) return node.nodeLabel?.origin?.identifier();
return node?.origin?.identifier();
});
this.nodeSelectionHandler = this.initializeNodeSelectionHandler(); this.nodeSelectionHandler = this.initializeNodeSelectionHandler();
this.svg.on("click", () => this.nodeSelectionHandler.clear()); this.svg.on("click", () => this.nodeSelectionHandler.clear());
...@@ -406,16 +407,19 @@ export class GraphView extends MovableView<Graph> { ...@@ -406,16 +407,19 @@ export class GraphView extends MovableView<Graph> {
return { return {
select: function (selectedNodes: Array<GraphNode>, selected: boolean) { select: function (selectedNodes: Array<GraphNode>, selected: boolean) {
const locations = new Array<GenericPosition>(); const locations = new Array<GenericPosition>();
const nodes = new Set<string>();
for (const node of selectedNodes) { for (const node of selectedNodes) {
if (node.nodeLabel.sourcePosition) { if (node.nodeLabel.sourcePosition) {
locations.push(node.nodeLabel.sourcePosition); locations.push(node.nodeLabel.sourcePosition);
nodes.add(node.identifier());
} }
if (node.nodeLabel.origin && node.nodeLabel.origin instanceof BytecodeOrigin) { if (node.nodeLabel.bytecodePosition) {
locations.push(new BytecodePosition(node.nodeLabel.origin.bytecodePosition)); locations.push(node.nodeLabel.bytecodePosition);
nodes.add(node.identifier());
} }
} }
view.state.selection.select(selectedNodes, selected); view.state.selection.select(selectedNodes, selected);
view.broker.broadcastSourcePositionSelect(this, locations, selected); view.broker.broadcastSourcePositionSelect(this, locations, selected, nodes);
view.updateGraphVisibility(); view.updateGraphVisibility();
}, },
clear: function () { clear: function () {
...@@ -434,10 +438,10 @@ export class GraphView extends MovableView<Graph> { ...@@ -434,10 +438,10 @@ export class GraphView extends MovableView<Graph> {
if (!node) continue; if (!node) continue;
node.visible = true; node.visible = true;
node.inputs.forEach(edge => { node.inputs.forEach(edge => {
edge.visible = edge.visible || view.state.selection.isSelected(edge.source); edge.visible = edge.visible || edge.source.visible;
}); });
node.outputs.forEach(edge => { node.outputs.forEach(edge => {
edge.visible = edge.visible || view.state.selection.isSelected(edge.target); edge.visible = edge.visible || edge.target.visible;
}); });
} }
view.updateGraphVisibility(); view.updateGraphVisibility();
...@@ -710,12 +714,6 @@ export class GraphView extends MovableView<Graph> { ...@@ -710,12 +714,6 @@ export class GraphView extends MovableView<Graph> {
this.updateGraphVisibility(); this.updateGraphVisibility();
} }
public showHoveredNodeHistory(): void {
const node = this.graph.nodeMap[this.hoveredNodeIdentifier];
if (!node) return;
this.broker.broadcastHistoryShow(null, node, this.phaseName);
}
private selectOrigins(): void { private selectOrigins(): void {
const selection = new SelectionStorage(); const selection = new SelectionStorage();
const origins = new Array<GraphNode>(); const origins = new Array<GraphNode>();
......
...@@ -11,11 +11,15 @@ import { SourceResolver } from "../source-resolver"; ...@@ -11,11 +11,15 @@ import { SourceResolver } from "../source-resolver";
import { GraphNode } from "../phases/graph-phase/graph-node"; import { GraphNode } from "../phases/graph-phase/graph-node";
import { HistoryHandler } from "../selection/selection-handler"; import { HistoryHandler } from "../selection/selection-handler";
import { GraphPhase } from "../phases/graph-phase/graph-phase"; import { GraphPhase } from "../phases/graph-phase/graph-phase";
import { NodeOrigin } from "../origin";
import { SelectionStorage } from "../selection/selection-storage"; import { SelectionStorage } from "../selection/selection-storage";
import { TurboshaftGraphNode } from "../phases/turboshaft-graph-phase/turboshaft-graph-node";
import { TurboshaftGraphPhase } from "../phases/turboshaft-graph-phase/turboshaft-graph-phase";
type GNode = GraphNode | TurboshaftGraphNode;
type GPhase = GraphPhase | TurboshaftGraphPhase;
export class HistoryView extends View { export class HistoryView extends View {
node: GraphNode; node: GNode;
broker: SelectionBroker; broker: SelectionBroker;
sourceResolver: SourceResolver; sourceResolver: SourceResolver;
historyHandler: HistoryHandler; historyHandler: HistoryHandler;
...@@ -78,7 +82,7 @@ export class HistoryView extends View { ...@@ -78,7 +82,7 @@ export class HistoryView extends View {
private initializeNodeSelectionHandler(): HistoryHandler { private initializeNodeSelectionHandler(): HistoryHandler {
const view = this; const view = this;
return { return {
showTurbofanNodeHistory: function (node: GraphNode, phaseName: string) { showNodeHistory: function (node: GNode, phaseName: string) {
view.clear(); view.clear();
view.node = node; view.node = node;
const phaseId = view.sourceResolver.getPhaseIdByName(phaseName); const phaseId = view.sourceResolver.getPhaseIdByName(phaseName);
...@@ -264,7 +268,7 @@ export class HistoryView extends View { ...@@ -264,7 +268,7 @@ export class HistoryView extends View {
} }
private setLabel(): void { private setLabel(): void {
this.label = `${this.node.id} ${this.node.nodeLabel.opcode}`; this.label = this.node.getHistoryLabel();
const coefficient = this.getCoefficient("history-tspan-font-size"); const coefficient = this.getCoefficient("history-tspan-font-size");
this.labelBox = measureText(this.label, coefficient); this.labelBox = measureText(this.label, coefficient);
} }
...@@ -275,14 +279,14 @@ export class HistoryView extends View { ...@@ -275,14 +279,14 @@ export class HistoryView extends View {
return Math.min(tspanSize, varSize) / Math.max(tspanSize, varSize); return Math.min(tspanSize, varSize) / Math.max(tspanSize, varSize);
} }
private getPhaseHistory(historyChain: Map<number, GraphNode>): void { private getPhaseHistory(historyChain: Map<number, GNode>): void {
const uniqueAncestors = new Set<string>(); const uniqueAncestors = new Set<string>();
const coefficient = this.getCoefficient("history-item-tspan-font-size"); const coefficient = this.getCoefficient("history-item-tspan-font-size");
let prevNode = null; let prevNode = null;
let first = true; let first = true;
for (let i = 0; i < this.sourceResolver.phases.length; i++) { for (let i = 0; i < this.sourceResolver.phases.length; i++) {
const phase = this.sourceResolver.getPhase(i); const phase = this.sourceResolver.getGraphPhase(i);
if (!(phase instanceof GraphPhase)) continue; if (!phase) continue;
const phaseNameMeasure = measureText(phase.name, coefficient); const phaseNameMeasure = measureText(phase.name, coefficient);
this.maxPhaseNameWidth = Math.max(this.maxPhaseNameWidth, phaseNameMeasure.width); this.maxPhaseNameWidth = Math.max(this.maxPhaseNameWidth, phaseNameMeasure.width);
...@@ -298,10 +302,11 @@ export class HistoryView extends View { ...@@ -298,10 +302,11 @@ export class HistoryView extends View {
if (prevNode && !prevNode.equals(node) && if (prevNode && !prevNode.equals(node) &&
phase.originIdToNodesMap.has(prevNode.identifier())) { phase.originIdToNodesMap.has(prevNode.identifier())) {
const prevNodeCurrentState = phase.nodeIdToNodeMap[prevNode.identifier()]; const prevNodeCurrentState = phase.nodeIdToNodeMap[prevNode.identifier()];
const inplaceUpdate = prevNodeCurrentState?.nodeLabel?.inplaceUpdatePhase;
if (!prevNodeCurrentState) { if (!prevNodeCurrentState) {
this.addToHistory(i, prevNode, HistoryChange.Removed); this.addToHistory(i, prevNode, HistoryChange.Removed);
} else if (!prevNodeCurrentState?.equals(node) && inplaceUpdate == phase.name) { } else if (!this.nodeEquals(prevNodeCurrentState, node) &&
prevNodeCurrentState instanceof GraphNode &&
prevNodeCurrentState.getInplaceUpdatePhase() == phase.name) {
this.addToHistory(i, prevNodeCurrentState, HistoryChange.InplaceUpdated); this.addToHistory(i, prevNodeCurrentState, HistoryChange.InplaceUpdated);
} else if (node.identifier() != prevNode.identifier()) { } else if (node.identifier() != prevNode.identifier()) {
this.addToHistory(i, prevNodeCurrentState, HistoryChange.Survived); this.addToHistory(i, prevNodeCurrentState, HistoryChange.Survived);
...@@ -314,7 +319,7 @@ export class HistoryView extends View { ...@@ -314,7 +319,7 @@ export class HistoryView extends View {
continue; continue;
} }
if (node.nodeLabel.inplaceUpdatePhase && node.nodeLabel.inplaceUpdatePhase == phase.name) { if (node instanceof GraphNode && node.getInplaceUpdatePhase() == phase.name) {
this.addToHistory(i, node, HistoryChange.InplaceUpdated); this.addToHistory(i, node, HistoryChange.InplaceUpdated);
} }
...@@ -328,7 +333,7 @@ export class HistoryView extends View { ...@@ -328,7 +333,7 @@ export class HistoryView extends View {
} }
} }
private addHistoryAncestors(key: string, phase: GraphPhase, uniqueAncestors: Set<string>): private addHistoryAncestors(key: string, phase: GPhase, uniqueAncestors: Set<string>):
boolean { boolean {
let changed = false; let changed = false;
const phaseId = this.sourceResolver.getPhaseIdByName(phase.name); const phaseId = this.sourceResolver.getPhaseIdByName(phase.name);
...@@ -343,22 +348,22 @@ export class HistoryView extends View { ...@@ -343,22 +348,22 @@ export class HistoryView extends View {
return changed; return changed;
} }
private getHistoryChain(phaseId: number, node: GraphNode): Map<number, GraphNode> { private getHistoryChain(phaseId: number, node: GNode): Map<number, GNode> {
const leftChain = this.getLeftHistoryChain(phaseId, node); const leftChain = this.getLeftHistoryChain(phaseId, node);
const rightChain = this.getRightHistoryChain(phaseId, node); const rightChain = this.getRightHistoryChain(phaseId, node);
return new Map([...leftChain, ...rightChain]); return new Map([...leftChain, ...rightChain]);
} }
private getLeftHistoryChain(phaseId: number, node: GraphNode): Map<number, GraphNode> { private getLeftHistoryChain(phaseId: number, node: GNode): Map<number, GNode> {
const leftChain = new Map<number, GraphNode>(); const leftChain = new Map<number, GNode>();
for (let i = phaseId; i >= 0; i--) { for (let i = phaseId; i >= 0; i--) {
const phase = this.sourceResolver.getPhase(i); const phase = this.sourceResolver.getGraphPhase(i);
if (!(phase instanceof GraphPhase)) continue; if (!phase) continue;
let currentNode = phase.nodeIdToNodeMap[node.identifier()]; let currentNode = phase.nodeIdToNodeMap[node.identifier()];
if (!currentNode) { if (!currentNode) {
const nodeOrigin = node.nodeLabel.origin; const nodeOrigin = node.getNodeOrigin();
if (nodeOrigin instanceof NodeOrigin) { if (nodeOrigin) {
currentNode = phase.nodeIdToNodeMap[nodeOrigin.identifier()]; currentNode = phase.nodeIdToNodeMap[nodeOrigin.identifier()];
} }
if (!currentNode) return leftChain; if (!currentNode) return leftChain;
...@@ -370,12 +375,12 @@ export class HistoryView extends View { ...@@ -370,12 +375,12 @@ export class HistoryView extends View {
return leftChain; return leftChain;
} }
private getRightHistoryChain(phaseId: number, node: GraphNode): Map<number, GraphNode> { private getRightHistoryChain(phaseId: number, node: GNode): Map<number, GNode> {
const rightChain = new Map<number, GraphNode>(); const rightChain = new Map<number, GNode>();
for (let i = phaseId + 1; i < this.sourceResolver.phases.length; i++) { for (let i = phaseId + 1; i < this.sourceResolver.phases.length; i++) {
const phase = this.sourceResolver.getPhase(i); const phase = this.sourceResolver.getGraphPhase(i);
if (!(phase instanceof GraphPhase)) continue; if (!phase) continue;
const currentNode = phase.nodeIdToNodeMap[node.identifier()]; const currentNode = phase.nodeIdToNodeMap[node.identifier()];
if (!currentNode) return rightChain; if (!currentNode) return rightChain;
rightChain.set(i, currentNode); rightChain.set(i, currentNode);
...@@ -385,7 +390,7 @@ export class HistoryView extends View { ...@@ -385,7 +390,7 @@ export class HistoryView extends View {
return rightChain; return rightChain;
} }
private addToHistory(phaseId: number, node: GraphNode, change: HistoryChange): void { private addToHistory(phaseId: number, node: GNode, change: HistoryChange): void {
if (!this.phaseIdToHistory.has(phaseId)) { if (!this.phaseIdToHistory.has(phaseId)) {
this.phaseIdToHistory.set(phaseId, new PhaseHistory(phaseId)); this.phaseIdToHistory.set(phaseId, new PhaseHistory(phaseId));
} }
...@@ -397,6 +402,16 @@ export class HistoryView extends View { ...@@ -397,6 +402,16 @@ export class HistoryView extends View {
} }
} }
private nodeEquals(first: GNode, second: GNode): boolean {
if (!first || !second) return false;
if ((first instanceof GraphNode && second instanceof GraphNode)) {
return first.equals(second);
} else if (first instanceof TurboshaftGraphNode && second instanceof TurboshaftGraphNode) {
return first.equals(second);
}
return first.getHistoryLabel() == second.getHistoryLabel();
}
private clear(): void { private clear(): void {
this.phaseIdToHistory.clear(); this.phaseIdToHistory.clear();
this.maxNodeWidth = 0; this.maxNodeWidth = 0;
...@@ -458,7 +473,7 @@ export class PhaseHistory { ...@@ -458,7 +473,7 @@ export class PhaseHistory {
this.nodeIdToRecord = new Map<string, HistoryRecord>(); this.nodeIdToRecord = new Map<string, HistoryRecord>();
} }
public addChange(node: GraphNode, change: HistoryChange): void { public addChange(node: GNode, change: HistoryChange): void {
const key = node.identifier(); const key = node.identifier();
if (!this.nodeIdToRecord.has(key)) { if (!this.nodeIdToRecord.has(key)) {
this.nodeIdToRecord.set(key, new HistoryRecord(node)); this.nodeIdToRecord.set(key, new HistoryRecord(node));
...@@ -475,10 +490,10 @@ export class PhaseHistory { ...@@ -475,10 +490,10 @@ export class PhaseHistory {
} }
export class HistoryRecord { export class HistoryRecord {
node: GraphNode; node: GNode;
changes: Set<HistoryChange>; changes: Set<HistoryChange>;
constructor(node: GraphNode) { constructor(node: GNode) {
this.node = node; this.node = node;
this.changes = new Set<HistoryChange>(); this.changes = new Set<HistoryChange>();
} }
......
...@@ -252,6 +252,12 @@ export abstract class MovableView<GraphType extends Graph | TurboshaftGraph> ext ...@@ -252,6 +252,12 @@ export abstract class MovableView<GraphType extends Graph | TurboshaftGraph> ext
})]; })];
} }
protected showHoveredNodeHistory(): void {
const node = this.graph.nodeMap[this.hoveredNodeIdentifier];
if (!node) return;
this.broker.broadcastHistoryShow(null, node, this.phaseName);
}
protected createImgToggleInput(id: string, title: string, initState: boolean, onClick): protected createImgToggleInput(id: string, title: string, initState: boolean, onClick):
HTMLElement { HTMLElement {
const input = this.createImgInput(id, title, onClick); const input = this.createImgInput(id, title, onClick);
......
...@@ -21,6 +21,7 @@ import { TurboshaftGraphLayout } from "../turboshaft-graph-layout"; ...@@ -21,6 +21,7 @@ import { TurboshaftGraphLayout } from "../turboshaft-graph-layout";
import { GraphStateType } from "../phases/graph-phase/graph-phase"; import { GraphStateType } from "../phases/graph-phase/graph-phase";
import { SelectionStorage } from "../selection/selection-storage"; import { SelectionStorage } from "../selection/selection-storage";
import { DataTarget } from "../phases/turboshaft-custom-data-phase"; import { DataTarget } from "../phases/turboshaft-custom-data-phase";
import { SourcePosition } from "../position";
import { import {
TurboshaftCustomData, TurboshaftCustomData,
TurboshaftGraphPhase TurboshaftGraphPhase
...@@ -99,8 +100,12 @@ export class TurboshaftGraphView extends MovableView<TurboshaftGraph> { ...@@ -99,8 +100,12 @@ export class TurboshaftGraphView extends MovableView<TurboshaftGraph> {
this.broker.addNodeHandler(this.nodeSelectionHandler); this.broker.addNodeHandler(this.nodeSelectionHandler);
this.broker.addBlockHandler(this.blockSelectionHandler); this.broker.addBlockHandler(this.blockSelectionHandler);
if (adaptedSelection.isAdapted()) { const countOfSelectedItems = adaptedSelection.isAdapted()
this.attachSelection(adaptedSelection); ? this.attachSelection(adaptedSelection)
: 0;
if (countOfSelectedItems > 0) {
this.updateGraphVisibility();
this.viewSelection(); this.viewSelection();
} else { } else {
if (this.state.cacheLayout && data.transform) { if (this.state.cacheLayout && data.transform) {
...@@ -142,6 +147,9 @@ export class TurboshaftGraphView extends MovableView<TurboshaftGraph> { ...@@ -142,6 +147,9 @@ export class TurboshaftGraphView extends MovableView<TurboshaftGraph> {
eventHandled = false; eventHandled = false;
} }
break; break;
case 72: // 'h'
this.showHoveredNodeHistory();
break;
case 73: // 'i' case 73: // 'i'
this.selectNodesOfSelectedBlocks(); this.selectNodesOfSelectedBlocks();
break; break;
...@@ -255,7 +263,16 @@ export class TurboshaftGraphView extends MovableView<TurboshaftGraph> { ...@@ -255,7 +263,16 @@ export class TurboshaftGraphView extends MovableView<TurboshaftGraph> {
const view = this; const view = this;
return { return {
select: function (selectedNodes: Array<TurboshaftGraphNode>, selected: boolean) { select: function (selectedNodes: Array<TurboshaftGraphNode>, selected: boolean) {
const sourcePositions = new Array<SourcePosition>();
const nodes = new Set<string>();
for (const node of selectedNodes) {
if (node.sourcePosition) {
sourcePositions.push(node.sourcePosition);
nodes.add(node.identifier());
}
}
view.state.selection.select(selectedNodes, selected); view.state.selection.select(selectedNodes, selected);
view.broker.broadcastSourcePositionSelect(this, sourcePositions, selected, nodes);
view.updateGraphVisibility(); view.updateGraphVisibility();
}, },
clear: function () { clear: function () {
...@@ -367,6 +384,7 @@ export class TurboshaftGraphView extends MovableView<TurboshaftGraph> { ...@@ -367,6 +384,7 @@ export class TurboshaftGraphView extends MovableView<TurboshaftGraph> {
const selectedCustomData = select.options[this.selectedIndex].text; const selectedCustomData = select.options[this.selectedIndex].text;
storageSetItem(storageKey, selectedCustomData); storageSetItem(storageKey, selectedCustomData);
view.updateGraphVisibility(); view.updateGraphVisibility();
view.updateInlineNodesCustomData();
}; };
this.toolbox.appendChild(select); this.toolbox.appendChild(select);
...@@ -603,13 +621,11 @@ export class TurboshaftGraphView extends MovableView<TurboshaftGraph> { ...@@ -603,13 +621,11 @@ export class TurboshaftGraphView extends MovableView<TurboshaftGraph> {
private updateInlineNodes(): void { private updateInlineNodes(): void {
const view = this; const view = this;
const state = this.state; const state = this.state;
const storageKey = this.customDataStorageKey(); const showCustomData = this.nodesCustomDataShowed();
const selectedCustomData = storageGetItem(storageKey, null, false);
let totalHeight = 0; let totalHeight = 0;
let blockId = 0; let blockId = 0;
view.visibleNodes.each(function (node: TurboshaftGraphNode) { view.visibleNodes.each(function (node: TurboshaftGraphNode) {
const nodeSvg = d3.select(this); const nodeSvg = d3.select(this);
const showCustomData = view.nodesCustomDataShowed();
if (blockId != node.block.id) { if (blockId != node.block.id) {
blockId = node.block.id; blockId = node.block.id;
totalHeight = 0; totalHeight = 0;
...@@ -623,18 +639,25 @@ export class TurboshaftGraphView extends MovableView<TurboshaftGraph> { ...@@ -623,18 +639,25 @@ export class TurboshaftGraphView extends MovableView<TurboshaftGraph> {
.attr("dy", nodeY) .attr("dy", nodeY)
.attr("visibility", !node.block.collapsed ? "visible" : "hidden"); .attr("visibility", !node.block.collapsed ? "visible" : "hidden");
const svgNodeCustomData = nodeSvg nodeSvg
.select(".inline-node-custom-data") .select(".inline-node-custom-data")
.attr("visibility", !node.block.collapsed && showCustomData ? "visible" : "hidden"); .attr("visibility", !node.block.collapsed && showCustomData ? "visible" : "hidden");
});
}
if (!node.block.collapsed && showCustomData) { private updateInlineNodesCustomData(): void {
const customData = view.graph.getCustomData(selectedCustomData, node.id, DataTarget.Nodes); const view = this;
svgNodeCustomData const storageKey = this.customDataStorageKey();
.select("tspan") const selectedCustomData = storageGetItem(storageKey, null, false);
.text(view.getReadableString(customData, node.block.width)) if (!this.nodesCustomDataShowed()) return;
.append("title") view.visibleNodes.each(function (node: TurboshaftGraphNode) {
.text(customData); const customData = view.graph.getCustomData(selectedCustomData, node.id, DataTarget.Nodes);
} d3.select(this)
.select(".inline-node-custom-data")
.select("tspan")
.text(view.getReadableString(customData, node.block.width))
.append("title")
.text(customData);
}); });
} }
...@@ -705,8 +728,8 @@ export class TurboshaftGraphView extends MovableView<TurboshaftGraph> { ...@@ -705,8 +728,8 @@ export class TurboshaftGraphView extends MovableView<TurboshaftGraph> {
} }
} }
private attachSelection(selection: SelectionStorage): void { private attachSelection(selection: SelectionStorage): number {
if (!(selection instanceof SelectionStorage)) return; if (!(selection instanceof SelectionStorage)) return 0;
this.nodeSelectionHandler.clear(); this.nodeSelectionHandler.clear();
this.blockSelectionHandler.clear(); this.blockSelectionHandler.clear();
const selectedNodes = [ const selectedNodes = [
...@@ -719,6 +742,7 @@ export class TurboshaftGraphView extends MovableView<TurboshaftGraph> { ...@@ -719,6 +742,7 @@ export class TurboshaftGraphView extends MovableView<TurboshaftGraph> {
selection.adaptedBocks.has(this.state.blocksSelection.stringKey(block))) selection.adaptedBocks.has(this.state.blocksSelection.stringKey(block)))
]; ];
this.blockSelectionHandler.select(selectedBlocks, true); this.blockSelectionHandler.select(selectedBlocks, true);
return selectedNodes.length + selectedBlocks.length;
} }
private nodesCustomDataShowed(): boolean { private nodesCustomDataShowed(): boolean {
...@@ -813,6 +837,7 @@ export class TurboshaftGraphView extends MovableView<TurboshaftGraph> { ...@@ -813,6 +837,7 @@ export class TurboshaftGraphView extends MovableView<TurboshaftGraph> {
const extent = view.graph.redetermineGraphBoundingBox(view.state.showCustomData); const extent = view.graph.redetermineGraphBoundingBox(view.state.showCustomData);
view.panZoom.translateExtent(extent); view.panZoom.translateExtent(extent);
view.adaptiveUpdateGraphVisibility(); view.adaptiveUpdateGraphVisibility();
view.updateInlineNodesCustomData();
} }
private toggleLayoutCachingAction(view: TurboshaftGraphView): void { private toggleLayoutCachingAction(view: TurboshaftGraphView): void {
...@@ -823,7 +848,7 @@ export class TurboshaftGraphView extends MovableView<TurboshaftGraph> { ...@@ -823,7 +848,7 @@ export class TurboshaftGraphView extends MovableView<TurboshaftGraph> {
// Hotkeys handlers // Hotkeys handlers
private selectAllNodes(): void { private selectAllNodes(): void {
this.state.selection.select(this.graph.nodeMap, true); this.nodeSelectionHandler.select(this.graph.nodeMap, true);
this.updateGraphVisibility(); this.updateGraphVisibility();
} }
...@@ -865,7 +890,7 @@ export class TurboshaftGraphView extends MovableView<TurboshaftGraph> { ...@@ -865,7 +890,7 @@ export class TurboshaftGraphView extends MovableView<TurboshaftGraph> {
block.collapsed = false; block.collapsed = false;
selectedNodes = selectedNodes.concat(block.nodes); selectedNodes = selectedNodes.concat(block.nodes);
} }
this.state.selection.select(selectedNodes, true); this.nodeSelectionHandler.select(selectedNodes, true);
this.updateGraphVisibility(); this.updateGraphVisibility();
} }
} }
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