lts/deps/v8/tools/turbolizer/src/sequence-view.ts
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import { Sequence } from "../src/source-resolver";
import { isIterable } from "../src/util";
import { TextView } from "../src/text-view";
export class SequenceView extends TextView {
sequence: Sequence;
searchInfo: Array<any>;
createViewElement() {
const pane = document.createElement('div');
pane.setAttribute('id', "sequence");
return pane;
}
constructor(parentId, broker) {
super(parentId, broker);
}
attachSelection(s) {
const view = this;
if (!(s instanceof Set)) return;
view.selectionHandler.clear();
view.blockSelectionHandler.clear();
const selected = new Array();
for (const key of s) selected.push(key);
view.selectionHandler.select(selected, true);
}
detachSelection() {
this.blockSelection.clear();
return this.selection.detachSelection();
}
initializeContent(data, rememberedSelection) {
this.divNode.innerHTML = '';
this.sequence = data.sequence;
this.searchInfo = [];
this.divNode.addEventListener('click', (e: MouseEvent) => {
if (!(e.target instanceof HTMLElement)) return;
const instructionId = Number.parseInt(e.target.dataset.instructionId, 10);
if (!instructionId) return;
if (!e.shiftKey) this.broker.broadcastClear(null);
this.broker.broadcastInstructionSelect(null, [instructionId], true);
});
this.addBlocks(this.sequence.blocks);
this.attachSelection(rememberedSelection);
this.show();
}
elementForBlock(block) {
const view = this;
function createElement(tag: string, cls: string | Array<string>, content?: string) {
const el = document.createElement(tag);
if (isIterable(cls)) {
for (const c of cls) el.classList.add(c);
} else {
el.classList.add(cls);
}
if (content != undefined) el.innerHTML = content;
return el;
}
function mkLinkHandler(id, handler) {
return function (e) {
e.stopPropagation();
if (!e.shiftKey) {
handler.clear();
}
handler.select(["" + id], true);
};
}
function mkBlockLinkHandler(blockId) {
return mkLinkHandler(blockId, view.blockSelectionHandler);
}
function mkOperandLinkHandler(text) {
return mkLinkHandler(text, view.selectionHandler);
}
function elementForOperand(operand, searchInfo) {
const text = operand.text;
const operandEl = createElement("div", ["parameter", "tag", "clickable", operand.type], text);
if (operand.tooltip) {
operandEl.setAttribute("title", operand.tooltip);
}
operandEl.onclick = mkOperandLinkHandler(text);
searchInfo.push(text);
view.addHtmlElementForNodeId(text, operandEl);
return operandEl;
}
function elementForInstruction(instruction, searchInfo) {
const instNodeEl = createElement("div", "instruction-node");
const instId = createElement("div", "instruction-id", instruction.id);
const offsets = view.sourceResolver.instructionToPcOffsets(instruction.id);
instId.classList.add("clickable");
instId.dataset.instructionId = instruction.id;
instId.setAttribute("title", `This instruction generated gap code at pc-offset 0x${offsets.gap.toString(16)}, code at pc-offset 0x${offsets.arch.toString(16)}, condition handling at pc-offset 0x${offsets.condition.toString(16)}.`);
instNodeEl.appendChild(instId);
const instContentsEl = createElement("div", "instruction-contents");
instNodeEl.appendChild(instContentsEl);
// Print gap moves.
const gapEl = createElement("div", "gap", "gap");
let hasGaps = false;
for (const gap of instruction.gaps) {
const moves = createElement("div", ["comma-sep-list", "gap-move"]);
for (const move of gap) {
hasGaps = true;
const moveEl = createElement("div", "move");
const destinationEl = elementForOperand(move[0], searchInfo);
moveEl.appendChild(destinationEl);
const assignEl = createElement("div", "assign", "=");
moveEl.appendChild(assignEl);
const sourceEl = elementForOperand(move[1], searchInfo);
moveEl.appendChild(sourceEl);
moves.appendChild(moveEl);
}
gapEl.appendChild(moves);
}
if (hasGaps) {
instContentsEl.appendChild(gapEl);
}
const instEl = createElement("div", "instruction");
instContentsEl.appendChild(instEl);
if (instruction.outputs.length > 0) {
const outputs = createElement("div", ["comma-sep-list", "input-output-list"]);
for (const output of instruction.outputs) {
const outputEl = elementForOperand(output, searchInfo);
outputs.appendChild(outputEl);
}
instEl.appendChild(outputs);
const assignEl = createElement("div", "assign", "=");
instEl.appendChild(assignEl);
}
let text = instruction.opcode + instruction.flags;
const instLabel = createElement("div", "node-label", text)
if (instruction.opcode == "ArchNop" && instruction.outputs.length == 1 && instruction.outputs[0].tooltip) {
instLabel.innerText = instruction.outputs[0].tooltip;
}
searchInfo.push(text);
view.addHtmlElementForNodeId(text, instLabel);
instEl.appendChild(instLabel);
if (instruction.inputs.length > 0) {
const inputs = createElement("div", ["comma-sep-list", "input-output-list"]);
for (const input of instruction.inputs) {
const inputEl = elementForOperand(input, searchInfo);
inputs.appendChild(inputEl);
}
instEl.appendChild(inputs);
}
if (instruction.temps.length > 0) {
const temps = createElement("div", ["comma-sep-list", "input-output-list", "temps"]);
for (const temp of instruction.temps) {
const tempEl = elementForOperand(temp, searchInfo);
temps.appendChild(tempEl);
}
instEl.appendChild(temps);
}
return instNodeEl;
}
const sequenceBlock = createElement("div", "schedule-block");
sequenceBlock.classList.toggle("deferred", block.deferred);
const blockId = createElement("div", ["block-id", "com", "clickable"], block.id);
blockId.onclick = mkBlockLinkHandler(block.id);
sequenceBlock.appendChild(blockId);
const blockPred = createElement("div", ["predecessor-list", "block-list", "comma-sep-list"]);
for (const pred of block.predecessors) {
const predEl = createElement("div", ["block-id", "com", "clickable"], pred);
predEl.onclick = mkBlockLinkHandler(pred);
blockPred.appendChild(predEl);
}
if (block.predecessors.length > 0) sequenceBlock.appendChild(blockPred);
const phis = createElement("div", "phis");
sequenceBlock.appendChild(phis);
const phiLabel = createElement("div", "phi-label", "phi:");
phis.appendChild(phiLabel);
const phiContents = createElement("div", "phi-contents");
phis.appendChild(phiContents);
for (const phi of block.phis) {
const phiEl = createElement("div", "phi");
phiContents.appendChild(phiEl);
const outputEl = elementForOperand(phi.output, this.searchInfo);
phiEl.appendChild(outputEl);
const assignEl = createElement("div", "assign", "=");
phiEl.appendChild(assignEl);
for (const input of phi.operands) {
const inputEl = createElement("div", ["parameter", "tag", "clickable"], input);
phiEl.appendChild(inputEl);
}
}
const instructions = createElement("div", "instructions");
for (const instruction of block.instructions) {
instructions.appendChild(elementForInstruction(instruction, this.searchInfo));
}
sequenceBlock.appendChild(instructions);
const blockSucc = createElement("div", ["successor-list", "block-list", "comma-sep-list"]);
for (const succ of block.successors) {
const succEl = createElement("div", ["block-id", "com", "clickable"], succ);
succEl.onclick = mkBlockLinkHandler(succ);
blockSucc.appendChild(succEl);
}
if (block.successors.length > 0) sequenceBlock.appendChild(blockSucc);
this.addHtmlElementForBlockId(block.id, sequenceBlock);
return sequenceBlock;
}
addBlocks(blocks) {
for (const block of blocks) {
const blockEl = this.elementForBlock(block);
this.divNode.appendChild(blockEl);
}
}
searchInputAction(searchBar, e) {
e.stopPropagation();
this.selectionHandler.clear();
const query = searchBar.value;
if (query.length == 0) return;
const select = [];
window.sessionStorage.setItem("lastSearch", query);
const reg = new RegExp(query);
for (const item of this.searchInfo) {
if (reg.exec(item) != null) {
select.push(item);
}
}
this.selectionHandler.select(select, true);
}
}