[turbolizer] Schedule view/phase refactoring

Bug: v8:7327
Change-Id: I45085b4b2dcb76948e39e79fcf15711deb531541
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3764441
Commit-Queue: Danylo Boiko <danielboyko02@gmail.com>
Reviewed-by: Nico Hartmann <nicohartmann@chromium.org>
Cr-Commit-Position: refs/heads/main@{#81824}
This commit is contained in:
Danylo Boiko 2022-07-18 21:48:13 +03:00 committed by V8 LUCI CQ
parent 9230a278e1
commit a1bdea56ca
5 changed files with 232 additions and 200 deletions

View File

@ -92,6 +92,20 @@ export class InstructionsPhase extends Phase {
return this.blockIdToInstructionRange[blockId] ?? [-1, -1]; return this.blockIdToInstructionRange[blockId] ?? [-1, -1];
} }
public getInstructionMarker(start: number, end: number): [string, string] {
if (start != end) {
return ["&#8857;", `This node generated instructions in range [${start},${end}). ` +
`This is currently unreliable for constants.`];
}
if (start != -1) {
return ["&#183;", `The instruction selector did not generate instructions ` +
`for this node, but processed the node at instruction ${start}. ` +
`This usually means that this node was folded into another node; ` +
`the highlighted machine code is a guess.`];
}
return ["", `This not is not in the final schedule.`];
}
public getInstructionKindForPCOffset(offset: number): InstructionKind { public getInstructionKindForPCOffset(offset: number): InstructionKind {
if (this.codeOffsetsInfo) { if (this.codeOffsetsInfo) {
if (offset >= this.codeOffsetsInfo.deoptimizationExits) { if (offset >= this.codeOffsetsInfo.deoptimizationExits) {

View File

@ -5,22 +5,16 @@
import { Phase, PhaseType } from "./phase"; import { Phase, PhaseType } from "./phase";
export class SchedulePhase extends Phase { export class SchedulePhase extends Phase {
data: string; data: ScheduleData;
schedule: { currentBlock, blocks: Array<any>, nodes: Array<any> };
constructor(name: string, data: string) { constructor(name: string, dataJson) {
super(name, PhaseType.Schedule); super(name, PhaseType.Schedule);
this.data = data; this.data = new ScheduleData();
this.schedule = { this.parseScheduleFromJSON(dataJson);
currentBlock: undefined,
blocks: new Array<any>(),
nodes: new Array<any>()
};
this.parseScheduleFromJSON(data);
} }
private parseScheduleFromJSON(scheduleDataJson): void { private parseScheduleFromJSON(dataJson): void {
const lines = scheduleDataJson.split(/[\n]/); const lines = dataJson.split(/[\n]/);
nextLine: nextLine:
for (const line of lines) { for (const line of lines) {
for (const rule of this.parsingRules) { for (const rule of this.parsingRules) {
@ -37,46 +31,39 @@ export class SchedulePhase extends Phase {
} }
private createNode = match => { private createNode = match => {
let inputs = []; let inputs = new Array<number>();
if (match.groups.args) { if (match.groups.args) {
const nodeIdsString = match.groups.args.replace(/\s/g, ''); const nodeIdsString = match.groups.args.replace(/\s/g, "");
const nodeIdStrings = nodeIdsString.split(','); const nodeIdStrings = nodeIdsString.split(",");
inputs = nodeIdStrings.map(n => Number.parseInt(n, 10)); inputs = nodeIdStrings.map(n => Number.parseInt(n, 10));
} }
const node = { const nodeId = Number.parseInt(match.groups.id, 10);
id: Number.parseInt(match.groups.id, 10), const node = new ScheduleNode(nodeId, match.groups.label, inputs);
label: match.groups.label,
inputs: inputs
};
if (match.groups.blocks) { if (match.groups.blocks) {
const nodeIdsString = match.groups.blocks.replace(/\s/g, '').replace(/B/g, ''); const nodeIdsString = match.groups.blocks.replace(/\s/g, "").replace(/B/g, "");
const nodeIdStrings = nodeIdsString.split(','); const nodeIdStrings = nodeIdsString.split(",");
this.schedule.currentBlock.succ = nodeIdStrings.map(n => Number.parseInt(n, 10)); this.data.lastBlock().successors = nodeIdStrings.map(n => Number.parseInt(n, 10));
} }
this.schedule.nodes[node.id] = node; this.data.nodes[node.id] = node;
this.schedule.currentBlock.nodes.push(node); this.data.lastBlock().nodes.push(node);
} }
private createBlock = match => { private createBlock = match => {
let predecessors = []; let predecessors = new Array<number>();
if (match.groups.in) { if (match.groups.in) {
const blockIdsString = match.groups.in.replace(/\s/g, '').replace(/B/g, ''); const blockIdsString = match.groups.in.replace(/\s/g, "").replace(/B/g, "");
const blockIdStrings = blockIdsString.split(','); const blockIdStrings = blockIdsString.split(",");
predecessors = blockIdStrings.map(n => Number.parseInt(n, 10)); predecessors = blockIdStrings.map(n => Number.parseInt(n, 10));
} }
const block = { const blockId = Number.parseInt(match.groups.id, 10);
id: Number.parseInt(match.groups.id, 10), const block = new ScheduleBlock(blockId, match.groups.deferred !== undefined,
isDeferred: match.groups.deferred != undefined, predecessors.sort());
pred: predecessors.sort(), this.data.blocks[block.id] = block;
succ: [],
nodes: []
};
this.schedule.blocks[block.id] = block;
this.schedule.currentBlock = block;
} }
private setGotoSuccessor = match => { private setGotoSuccessor = match => {
this.schedule.currentBlock.succ = [Number.parseInt(match.groups.successor.replace(/\s/g, ''), 10)]; this.data.lastBlock().successors =
[Number.parseInt(match.groups.successor.replace(/\s/g, ""), 10)];
} }
private parsingRules = [ private parsingRules = [
@ -98,3 +85,50 @@ export class SchedulePhase extends Phase {
} }
]; ];
} }
export class ScheduleNode {
id: number;
label: string;
inputs: Array<number>;
constructor(id: number, label: string, inputs: Array<number>) {
this.id = id;
this.label = label;
this.inputs = inputs;
}
public toString(): string {
return `${this.id}: ${this.label}(${this.inputs.join(", ")})`;
}
}
export class ScheduleBlock {
id: number;
deferred: boolean;
predecessors: Array<number>;
successors: Array<number>;
nodes: Array<ScheduleNode>;
constructor(id: number, deferred: boolean, predecessors: Array<number>) {
this.id = id;
this.deferred = deferred;
this.predecessors = predecessors;
this.successors = new Array<number>();
this.nodes = new Array<ScheduleNode>();
}
}
export class ScheduleData {
nodes: Array<ScheduleNode>;
blocks: Array<ScheduleBlock>;
constructor() {
this.nodes = new Array<ScheduleNode>();
this.blocks = new Array<ScheduleBlock>();
}
public lastBlock(): ScheduleBlock {
if (this.blocks.length == 0) return null;
return this.blocks[this.blocks.length - 1];
}
}

View File

@ -12,13 +12,14 @@ export interface ClearableHandler {
} }
export interface NodeSelectionHandler { export interface NodeSelectionHandler {
select(nodes: Iterable<TurboshaftGraphNode | GraphNode | string>, selected: boolean): void; select(nodes: Iterable<TurboshaftGraphNode | GraphNode | string | number>, selected: boolean):
void;
clear(): void; clear(): void;
brokeredNodeSelect(nodeIds: Set<string>, selected: boolean): void; brokeredNodeSelect(nodeIds: Set<string>, selected: boolean): void;
} }
export interface BlockSelectionHandler { export interface BlockSelectionHandler {
select(blocks: Iterable<TurboshaftGraphBlock | string>, selected: boolean): void; select(blocks: Iterable<TurboshaftGraphBlock | string | number>, selected: boolean): void;
clear(): void; clear(): void;
brokeredBlockSelect(blockIds: Array<string>, selected: boolean): void; brokeredBlockSelect(blockIds: Array<string>, selected: boolean): void;
} }

View File

@ -2,32 +2,38 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import * as C from "./../common/constants";
import { storageSetItem } from "../common/util";
import { TextView } from "./text-view"; import { TextView } from "./text-view";
import { SchedulePhase } from "../phases/schedule-phase";
import { SelectionStorage } from "../selection/selection-storage"; import { SelectionStorage } from "../selection/selection-storage";
import { SelectionBroker } from "../selection/selection-broker";
import { ScheduleBlock, ScheduleNode, SchedulePhase } from "../phases/schedule-phase";
export class ScheduleView extends TextView { export class ScheduleView extends TextView {
schedule: SchedulePhase; schedule: SchedulePhase;
createViewElement() { constructor(parent: HTMLElement, broker: SelectionBroker) {
const pane = document.createElement('div'); super(parent, broker);
pane.setAttribute('id', "schedule"); this.sourceResolver = broker.sourceResolver;
}
public createViewElement(): HTMLDivElement {
const pane = document.createElement("div");
pane.setAttribute("id", C.SCHEDULE_PANE_ID);
pane.classList.add("scrollable"); pane.classList.add("scrollable");
pane.setAttribute("tabindex", "0"); pane.setAttribute("tabindex", "0");
return pane; return pane;
} }
constructor(parentId, broker) { public initializeContent(schedule: SchedulePhase, rememberedSelection: SelectionStorage): void {
super(parentId, broker); this.divNode.innerHTML = "";
this.sourceResolver = broker.sourceResolver; this.schedule = schedule;
this.addBlocks(schedule.data.blocks);
this.show();
if (rememberedSelection) {
const adaptedSelection = this.adaptSelection(rememberedSelection);
this.attachSelection(adaptedSelection);
} }
private attachSelection(adaptedSelection: SelectionStorage): void {
if (!(adaptedSelection instanceof SelectionStorage)) return;
this.nodeSelectionHandler.clear();
this.blockSelectionHandler.clear();
this.nodeSelectionHandler.select(adaptedSelection.adaptedNodes, true);
this.blockSelectionHandler.select(adaptedSelection.adaptedBocks, true);
} }
public detachSelection(): SelectionStorage { public detachSelection(): SelectionStorage {
@ -41,34 +47,122 @@ export class ScheduleView extends TextView {
return selection; return selection;
} }
public initializeContent(schedule: SchedulePhase, rememberedSelection: SelectionStorage): void { public searchInputAction(searchBar: HTMLInputElement, e: KeyboardEvent, onlyVisible: boolean):
this.divNode.innerHTML = ""; void {
this.schedule = schedule; e.stopPropagation();
this.addBlocks(schedule.schedule.blocks); this.nodeSelectionHandler.clear();
this.show(); const query = searchBar.value;
if (rememberedSelection) { if (query.length == 0) return;
const adaptedSelection = this.adaptSelection(rememberedSelection); const select = new Array<number>();
this.attachSelection(adaptedSelection); storageSetItem("lastSearch", query);
const reg = new RegExp(query);
for (const node of this.schedule.data.nodes) {
if (node === undefined) continue;
if (reg.exec(node.toString()) !== null) {
select.push(node.id);
}
}
this.nodeSelectionHandler.select(select, true);
}
private addBlocks(blocks: Array<ScheduleBlock>) {
for (const block of blocks) {
const blockEl = this.createElementForBlock(block);
this.divNode.appendChild(blockEl);
} }
} }
createElementFromString(htmlString) { private attachSelection(adaptedSelection: SelectionStorage): void {
const div = document.createElement('div'); if (!(adaptedSelection instanceof SelectionStorage)) return;
div.innerHTML = htmlString.trim(); this.nodeSelectionHandler.clear();
return div.firstChild; this.blockSelectionHandler.clear();
this.nodeSelectionHandler.select(adaptedSelection.adaptedNodes, true);
this.blockSelectionHandler.select(adaptedSelection.adaptedBocks, true);
} }
elementForBlock(block) { private createElementForBlock(block: ScheduleBlock): HTMLElement {
const scheduleBlock = this.createElement("div", "schedule-block");
scheduleBlock.classList.toggle("deferred", block.deferred);
const [start, end] = this.sourceResolver.instructionsPhase
.getInstructionRangeForBlock(block.id);
const instrMarker = this.createElement("div", "instr-marker com", "&#8857;");
instrMarker.setAttribute("title", `Instructions range for this block is [${start}, ${end})`);
instrMarker.onclick = this.mkBlockLinkHandler(block.id);
scheduleBlock.appendChild(instrMarker);
const blockId = this.createElement("div", "block-id com clickable", String(block.id));
blockId.onclick = this.mkBlockLinkHandler(block.id);
scheduleBlock.appendChild(blockId);
const blockPred = this.createElement("div", "predecessor-list block-list comma-sep-list");
for (const pred of block.predecessors) {
const predEl = this.createElement("div", "block-id com clickable", String(pred));
predEl.onclick = this.mkBlockLinkHandler(pred);
blockPred.appendChild(predEl);
}
if (block.predecessors.length) scheduleBlock.appendChild(blockPred);
const nodes = this.createElement("div", "nodes");
for (const node of block.nodes) {
nodes.appendChild(this.createElementForNode(node));
}
scheduleBlock.appendChild(nodes);
const blockSucc = this.createElement("div", "successor-list block-list comma-sep-list");
for (const succ of block.successors) {
const succEl = this.createElement("div", "block-id com clickable", String(succ));
succEl.onclick = this.mkBlockLinkHandler(succ);
blockSucc.appendChild(succEl);
}
if (block.successors.length) scheduleBlock.appendChild(blockSucc);
this.addHtmlElementForBlockId(block.id, scheduleBlock);
return scheduleBlock;
}
private createElementForNode(node: ScheduleNode): HTMLElement {
const nodeEl = this.createElement("div", "node");
const [start, end] = this.sourceResolver.instructionsPhase.getInstruction(node.id);
const [marker, tooltip] = this.sourceResolver.instructionsPhase
.getInstructionMarker(start, end);
const instrMarker = this.createElement("div", "instr-marker com", marker);
instrMarker.setAttribute("title", tooltip);
instrMarker.onclick = this.mkNodeLinkHandler(node.id);
nodeEl.appendChild(instrMarker);
const nodeIdEl = this.createElement("div", "node-id tag clickable", String(node.id));
nodeIdEl.onclick = this.mkNodeLinkHandler(node.id);
this.addHtmlElementForNodeId(node.id, nodeIdEl);
nodeEl.appendChild(nodeIdEl);
const nodeLabel = this.createElement("div", "node-label", node.label);
nodeEl.appendChild(nodeLabel);
if (node.inputs.length > 0) {
const nodeParameters = this.createElement("div", "parameter-list comma-sep-list");
for (const param of node.inputs) {
const paramEl = this.createElement("div", "parameter tag clickable", String(param));
nodeParameters.appendChild(paramEl);
paramEl.onclick = this.mkNodeLinkHandler(param);
this.addHtmlElementForNodeId(param, paramEl);
}
nodeEl.appendChild(nodeParameters);
}
return nodeEl;
}
private mkBlockLinkHandler(blockId: number): (e: MouseEvent) => void {
const view = this; const view = this;
function createElement(tag: string, cls: string, content?: string) { return function (e: MouseEvent) {
const el = document.createElement(tag); e.stopPropagation();
el.className = cls; if (!e.shiftKey) {
if (content != undefined) el.innerHTML = content; view.blockSelectionHandler.clear();
return el; }
view.blockSelectionHandler.select([blockId], true);
};
} }
function mkNodeLinkHandler(nodeId) { private mkNodeLinkHandler(nodeId: number): (e: MouseEvent) => void {
return function (e) { const view = this;
return function (e: MouseEvent) {
e.stopPropagation(); e.stopPropagation();
if (!e.shiftKey) { if (!e.shiftKey) {
view.nodeSelectionHandler.clear(); view.nodeSelectionHandler.clear();
@ -77,121 +171,10 @@ export class ScheduleView extends TextView {
}; };
} }
function getMarker(start, end) { private createElement(tag: string, cls: string, content?: string) {
if (start != end) { const el = document.createElement(tag);
return ["&#8857;", `This node generated instructions in range [${start},${end}). ` + el.className = cls;
`This is currently unreliable for constants.`]; if (content !== undefined) el.innerHTML = content;
} return el;
if (start != -1) {
return ["&#183;", `The instruction selector did not generate instructions ` +
`for this node, but processed the node at instruction ${start}. ` +
`This usually means that this node was folded into another node; ` +
`the highlighted machine code is a guess.`];
}
return ["", `This not is not in the final schedule.`];
}
function createElementForNode(node) {
const nodeEl = createElement("div", "node");
const [start, end] = view.sourceResolver.instructionsPhase.getInstruction(node.id);
const [marker, tooltip] = getMarker(start, end);
const instrMarker = createElement("div", "instr-marker com", marker);
instrMarker.setAttribute("title", tooltip);
instrMarker.onclick = mkNodeLinkHandler(node.id);
nodeEl.appendChild(instrMarker);
const nodeId = createElement("div", "node-id tag clickable", node.id);
nodeId.onclick = mkNodeLinkHandler(node.id);
view.addHtmlElementForNodeId(node.id, nodeId);
nodeEl.appendChild(nodeId);
const nodeLabel = createElement("div", "node-label", node.label);
nodeEl.appendChild(nodeLabel);
if (node.inputs.length > 0) {
const nodeParameters = createElement("div", "parameter-list comma-sep-list");
for (const param of node.inputs) {
const paramEl = createElement("div", "parameter tag clickable", param);
nodeParameters.appendChild(paramEl);
paramEl.onclick = mkNodeLinkHandler(param);
view.addHtmlElementForNodeId(param, paramEl);
}
nodeEl.appendChild(nodeParameters);
}
return nodeEl;
}
function mkBlockLinkHandler(blockId) {
return function (e) {
e.stopPropagation();
if (!e.shiftKey) {
view.blockSelectionHandler.clear();
}
view.blockSelectionHandler.select(["" + blockId], true);
};
}
const scheduleBlock = createElement("div", "schedule-block");
scheduleBlock.classList.toggle("deferred", block.isDeferred);
const [start, end] = view.sourceResolver.instructionsPhase
.getInstructionRangeForBlock(block.id);
const instrMarker = createElement("div", "instr-marker com", "&#8857;");
instrMarker.setAttribute("title", `Instructions range for this block is [${start}, ${end})`);
instrMarker.onclick = mkBlockLinkHandler(block.id);
scheduleBlock.appendChild(instrMarker);
const blockId = createElement("div", "block-id com clickable", block.id);
blockId.onclick = mkBlockLinkHandler(block.id);
scheduleBlock.appendChild(blockId);
const blockPred = createElement("div", "predecessor-list block-list comma-sep-list");
for (const pred of block.pred) {
const predEl = createElement("div", "block-id com clickable", pred);
predEl.onclick = mkBlockLinkHandler(pred);
blockPred.appendChild(predEl);
}
if (block.pred.length) scheduleBlock.appendChild(blockPred);
const nodes = createElement("div", "nodes");
for (const node of block.nodes) {
nodes.appendChild(createElementForNode(node));
}
scheduleBlock.appendChild(nodes);
const blockSucc = createElement("div", "successor-list block-list comma-sep-list");
for (const succ of block.succ) {
const succEl = createElement("div", "block-id com clickable", succ);
succEl.onclick = mkBlockLinkHandler(succ);
blockSucc.appendChild(succEl);
}
if (block.succ.length) scheduleBlock.appendChild(blockSucc);
this.addHtmlElementForBlockId(block.id, scheduleBlock);
return scheduleBlock;
}
addBlocks(blocks) {
for (const block of blocks) {
const blockEl = this.elementForBlock(block);
this.divNode.appendChild(blockEl);
}
}
lineString(node) {
return `${node.id}: ${node.label}(${node.inputs.join(", ")})`;
}
searchInputAction(searchBar, e, onlyVisible) {
e.stopPropagation();
this.nodeSelectionHandler.clear();
const query = searchBar.value;
if (query.length == 0) return;
const select = [];
window.sessionStorage.setItem("lastSearch", query);
const reg = new RegExp(query);
for (const node of this.schedule.schedule.nodes) {
if (node === undefined) continue;
if (reg.exec(this.lineString(node)) != null) {
select.push(node.id);
}
}
this.nodeSelectionHandler.select(select, true);
} }
} }

View File

@ -36,7 +36,7 @@ export abstract class TextView extends PhaseView {
nodeIdToBlockId: Array<string>; nodeIdToBlockId: Array<string>;
patterns: Array<Array<any>>; patterns: Array<Array<any>>;
constructor(parent: HTMLDivElement, broker: SelectionBroker) { constructor(parent: HTMLElement, broker: SelectionBroker) {
super(parent); super(parent);
this.broker = broker; this.broker = broker;
this.sourceResolver = broker.sourceResolver; this.sourceResolver = broker.sourceResolver;
@ -69,7 +69,7 @@ export abstract class TextView extends PhaseView {
public initializeContent(genericPhase: GenericTextPhase, _): void { public initializeContent(genericPhase: GenericTextPhase, _): void {
this.clearText(); this.clearText();
if (!(genericPhase instanceof SequencePhase)) { if (genericPhase instanceof DisassemblyPhase) {
this.processText(genericPhase.data); this.processText(genericPhase.data);
} }
this.show(); this.show();
@ -207,7 +207,7 @@ export abstract class TextView extends PhaseView {
private initializeNodeSelectionHandler(): NodeSelectionHandler & ClearableHandler { private initializeNodeSelectionHandler(): NodeSelectionHandler & ClearableHandler {
const view = this; const view = this;
return { return {
select: function (nodeIds: Array<string>, selected: boolean) { select: function (nodeIds: Array<string | number>, selected: boolean) {
view.nodeSelection.select(nodeIds, selected); view.nodeSelection.select(nodeIds, selected);
view.updateSelection(); view.updateSelection();
view.broker.broadcastNodeSelect(this, view.nodeSelection.selectedKeys(), selected); view.broker.broadcastNodeSelect(this, view.nodeSelection.selectedKeys(), selected);