diff --git a/tools/turbolizer/css/turbo-visualizer.css b/tools/turbolizer/css/turbo-visualizer.css index 4d55c26766..1ec293e0ee 100644 --- a/tools/turbolizer/css/turbo-visualizer.css +++ b/tools/turbolizer/css/turbo-visualizer.css @@ -76,6 +76,11 @@ ol.linenums { -webkit-padding-start: 8px; } +ol.linenums.constants { + margin-top: 3px; + color: #298f45; +} + .line-number { display: inline-block; min-width: 3ex; @@ -110,7 +115,7 @@ li.selected .line-number { display: block; } -.source-container { +.source-container, .bytecode-source-container { border-bottom: 2px solid #AAAAAA; } diff --git a/tools/turbolizer/src/source-resolver.ts b/tools/turbolizer/src/source-resolver.ts index a9d8f44efe..ac46ec7291 100644 --- a/tools/turbolizer/src/source-resolver.ts +++ b/tools/turbolizer/src/source-resolver.ts @@ -11,7 +11,7 @@ import { InstructionsPhase } from "./phases/instructions-phase"; import { SchedulePhase } from "./phases/schedule-phase"; import { SequencePhase } from "./phases/sequence-phase"; import { BytecodeOrigin } from "./origin"; -import { Source } from "./source"; +import { BytecodeSource, BytecodeSourceData, Source } from "./source"; import { NodeLabel } from "./node-label"; import { TurboshaftCustomDataPhase } from "./phases/turboshaft-custom-data-phase"; import { TurboshaftGraphPhase } from "./phases/turboshaft-graph-phase/turboshaft-graph-phase"; @@ -23,6 +23,7 @@ export type GenericPhase = GraphPhase | TurboshaftGraphPhase | TurboshaftCustomD export class SourceResolver { nodePositionMap: Array; sources: Array; + bytecodeSources: Map; inlinings: Array; inliningsMap: Map; positionToNodes: Map>; @@ -37,6 +38,8 @@ export class SourceResolver { this.nodePositionMap = new Array(); // Maps source ids to source objects. this.sources = new Array(); + // Maps bytecode source ids to bytecode source objects. + this.bytecodeSources = new Map(); // Maps inlining ids to inlining objects. this.inlinings = new Array(); // Maps source position keys to inlinings. @@ -96,6 +99,22 @@ export class SourceResolver { } } + public setBytecodeSources(bytecodeSourcesJson): void { + if (!bytecodeSourcesJson) return; + for (const [sourceId, source] of Object.entries(bytecodeSourcesJson)) { + const bytecodeSource = source.bytecodeSource; + const data = new Array(); + + for (const bytecode of Object.values(bytecodeSource.data)) { + data.push(new BytecodeSourceData(bytecode.offset, bytecode.disassembly)); + } + + const numSourceId = Number(sourceId); + this.bytecodeSources.set(numSourceId, new BytecodeSource(source.sourceId, source.functionName, + data, bytecodeSource.constantPool)); + } + } + public setNodePositionMap(mapJson): void { if (!mapJson) return; if (typeof mapJson[0] !== "object") { diff --git a/tools/turbolizer/src/source.ts b/tools/turbolizer/src/source.ts index efd16e96ff..0fb94810df 100644 --- a/tools/turbolizer/src/source.ts +++ b/tools/turbolizer/src/source.ts @@ -31,3 +31,28 @@ export class Source { return `${this.sourceName}:${this.functionName}`; } } + +export class BytecodeSource { + sourceId: number; + functionName: string; + data: Array; + constantPool: Array; + + constructor(sourceId: number, functionName: string, data: Array, + constantPool: Array) { + this.sourceId = sourceId; + this.functionName = functionName; + this.data = data; + this.constantPool = constantPool; + } +} + +export class BytecodeSourceData { + offset: number; + disassembly: string; + + constructor(offset: number, disassembly: string) { + this.offset = offset; + this.disassembly = disassembly; + } +} diff --git a/tools/turbolizer/src/turbo-visualizer.ts b/tools/turbolizer/src/turbo-visualizer.ts index 58dc246fa7..dacc748d0c 100644 --- a/tools/turbolizer/src/turbo-visualizer.ts +++ b/tools/turbolizer/src/turbo-visualizer.ts @@ -7,16 +7,19 @@ import { SourceResolver } from "./source-resolver"; import { SelectionBroker } from "./selection/selection-broker"; import { DisassemblyView } from "./views/disassembly-view"; import { GraphMultiView } from "./graphmultiview"; -import { CodeMode, CodeView } from "./views/code-view"; +import { CodeView } from "./views/code-view"; import { Tabs } from "./tabs"; import { Resizer } from "./resizer"; import { InfoView } from "./views/info-view"; import { HistoryView } from "./views/history-view"; +import { CodeMode } from "./views/view"; +import { BytecodeSourceView } from "./views/bytecode-source-view"; window.onload = function () { let multiview: GraphMultiView; let disassemblyView: DisassemblyView; let sourceViews: Array = new Array(); + let bytecodeSourceViews: Array = new Array(); let historyView: HistoryView; let selectionBroker: SelectionBroker; let sourceResolver: SourceResolver; @@ -47,6 +50,7 @@ window.onload = function () { } try { sourceViews.forEach(sv => sv.hide()); + bytecodeSourceViews.forEach(bsv => bsv.hide()); multiview?.hide(); multiview = null; document.getElementById("ranges").innerHTML = ""; @@ -55,6 +59,7 @@ window.onload = function () { disassemblyView?.hide(); historyView?.hide(); sourceViews = new Array(); + bytecodeSourceViews = new Array(); sourceResolver = new SourceResolver(); selectionBroker = new SelectionBroker(sourceResolver); @@ -64,6 +69,7 @@ window.onload = function () { sourceResolver.setInlinings(jsonObj.inlinings); sourceResolver.setSourceLineToBytecodePosition(jsonObj.sourceLineToBytecodePosition); sourceResolver.setSources(jsonObj.sources, mainFunction); + sourceResolver.setBytecodeSources(jsonObj.bytecodeSources); sourceResolver.setNodePositionMap(jsonObj.nodePositions); sourceResolver.parsePhases(jsonObj.phases); @@ -83,6 +89,20 @@ window.onload = function () { sourceViews.push(sourceView); } + if (sourceResolver.bytecodeSources.size > 0) { + const [_, bytecodeContainer] = sourceTabs.addTabAndContent("Bytecode"); + bytecodeContainer.classList.add("viewpane", "scrollable"); + + const bytecodeSources = Array.from(sourceResolver.bytecodeSources.values()).reverse(); + for (const source of bytecodeSources) { + const codeMode = source.sourceId == -1 ? CodeMode.MainSource : CodeMode.InlinedSource; + const bytecodeSourceView = new BytecodeSourceView(bytecodeContainer, selectionBroker, + source, sourceResolver, codeMode); + bytecodeSourceView.show(); + bytecodeSourceViews.push(bytecodeSourceView); + } + } + const [disassemblyTab, disassemblyContainer] = disassemblyTabs.addTabAndContent("Disassembly"); disassemblyContainer.classList.add("viewpane", "scrollable"); disassemblyTabs.activateTab(disassemblyTab); diff --git a/tools/turbolizer/src/views/bytecode-source-view.ts b/tools/turbolizer/src/views/bytecode-source-view.ts new file mode 100644 index 0000000000..9670990035 --- /dev/null +++ b/tools/turbolizer/src/views/bytecode-source-view.ts @@ -0,0 +1,110 @@ +// Copyright 2022 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import { createElement } from "../common/util"; +import { CodeMode, View } from "./view"; +import { SelectionBroker } from "../selection/selection-broker"; +import { BytecodeSource } from "../source"; +import { SourceResolver } from "../source-resolver"; + +export class BytecodeSourceView extends View { + broker: SelectionBroker; + source: BytecodeSource; + sourceResolver: SourceResolver; + codeMode: CodeMode; + bytecodeOffsetToHtmlElement: Map; + + constructor(parent: HTMLElement, broker: SelectionBroker, sourceFunction: BytecodeSource, + sourceResolver: SourceResolver, codeMode: CodeMode) { + super(parent); + this.broker = broker; + this.source = sourceFunction; + this.sourceResolver = sourceResolver; + this.codeMode = codeMode; + this.bytecodeOffsetToHtmlElement = new Map(); + + this.initializeCode(); + } + + protected createViewElement(): HTMLElement { + return createElement("div", "bytecode-source-container"); + } + + private initializeCode(): void { + const view = this; + const source = this.source; + const bytecodeContainer = this.divNode; + bytecodeContainer.classList.add(view.getSourceClass()); + + const bytecodeHeader = createElement("div", "code-header"); + bytecodeHeader.setAttribute("id", view.getBytecodeHeaderHtmlElementName()); + + const codeFileFunction = createElement("div", "code-file-function", source.functionName); + bytecodeHeader.appendChild(codeFileFunction); + + const codeMode = createElement("div", "code-mode", view.codeMode); + bytecodeHeader.appendChild(codeMode); + + const clearElement = document.createElement("div"); + clearElement.style.clear = "both"; + bytecodeHeader.appendChild(clearElement); + bytecodeContainer.appendChild(bytecodeHeader); + + const codePre = createElement("pre", "prettyprint linenums"); + codePre.setAttribute("id", view.getBytecodeHtmlElementName()); + bytecodeContainer.appendChild(codePre); + + bytecodeHeader.onclick = () => { + codePre.style.display = codePre.style.display === "none" ? "block" : "none"; + }; + + const sourceList = createElement("ol", "linenums"); + for (const bytecodeSource of view.source.data) { + const currentLine = createElement("li", `L${bytecodeSource.offset}`); + currentLine.setAttribute("id", `li${bytecodeSource.offset}`); + view.insertLineContent(currentLine, bytecodeSource.disassembly); + view.insertLineNumber(currentLine, bytecodeSource.offset); + view.bytecodeOffsetToHtmlElement.set(bytecodeSource.offset, currentLine); + sourceList.appendChild(currentLine); + } + codePre.appendChild(sourceList); + + if (view.source.constantPool.length === 0) return; + + const constantList = createElement("ol", "linenums constants"); + const constantListHeader = createElement("li", ""); + view.insertLineContent(constantListHeader, + `Constant pool (size = ${view.source.constantPool.length})`); + constantList.appendChild(constantListHeader); + + for (const [idx, constant] of view.source.constantPool.entries()) { + const currentLine = createElement("li", `C${idx}`); + view.insertLineContent(currentLine, `${idx}: ${constant}`); + constantList.appendChild(currentLine); + } + codePre.appendChild(constantList); + } + + private getBytecodeHeaderHtmlElementName(): string { + return `source-pre-${this.source.sourceId}-header`; + } + + private getBytecodeHtmlElementName(): string { + return `source-pre-${this.source.sourceId}`; + } + + private getSourceClass(): string { + return this.codeMode == CodeMode.MainSource ? "main-source" : "inlined-source"; + } + + private insertLineContent(lineElement: HTMLElement, content: string): void { + const lineContentElement = createElement("span", "", content); + lineElement.appendChild(lineContentElement); + } + + private insertLineNumber(lineElement: HTMLElement, lineNumber: number): void { + const lineNumberElement = createElement("div", "line-number", String(lineNumber)); + lineElement.insertBefore(lineNumberElement, lineElement.firstChild); + } +} diff --git a/tools/turbolizer/src/views/code-view.ts b/tools/turbolizer/src/views/code-view.ts index 714ed9625a..8968c69664 100644 --- a/tools/turbolizer/src/views/code-view.ts +++ b/tools/turbolizer/src/views/code-view.ts @@ -5,7 +5,7 @@ import { Source } from "../source"; import { GenericPosition, SourceResolver } from "../source-resolver"; import { SelectionBroker } from "../selection/selection-broker"; -import { View } from "./view"; +import { CodeMode, View } from "./view"; import { SelectionMap } from "../selection/selection-map"; import { ViewElements } from "../common/view-elements"; import { ClearableHandler, SourcePositionSelectionHandler } from "../selection/selection-handler"; @@ -19,11 +19,6 @@ declare global { const PR: PR; } -export enum CodeMode { - MainSource = "main function", - InlinedSource = "inlined function" -} - export class CodeView extends View { broker: SelectionBroker; source: Source; diff --git a/tools/turbolizer/src/views/view.ts b/tools/turbolizer/src/views/view.ts index 2fae05e869..9b85d05e8e 100644 --- a/tools/turbolizer/src/views/view.ts +++ b/tools/turbolizer/src/views/view.ts @@ -37,3 +37,8 @@ export abstract class PhaseView extends View { super(idOrContainer); } } + +export enum CodeMode { + MainSource = "main function", + InlinedSource = "inlined function" +} diff --git a/tools/turbolizer/tsconfig.json b/tools/turbolizer/tsconfig.json index 739eeeb938..74c02495af 100644 --- a/tools/turbolizer/tsconfig.json +++ b/tools/turbolizer/tsconfig.json @@ -36,6 +36,7 @@ "src/selection/selection-handler.ts", "src/views/view.ts", "src/views/code-view.ts", + "src/views/bytecode-source-view.ts", "src/views/graph-view.ts", "src/views/history-view.ts", "src/views/schedule-view.ts",