summaryrefslogtreecommitdiff
path: root/deps/v8/tools/turbolizer/src/source-resolver.ts
diff options
context:
space:
mode:
Diffstat (limited to 'deps/v8/tools/turbolizer/src/source-resolver.ts')
-rw-r--r--deps/v8/tools/turbolizer/src/source-resolver.ts528
1 files changed, 528 insertions, 0 deletions
diff --git a/deps/v8/tools/turbolizer/src/source-resolver.ts b/deps/v8/tools/turbolizer/src/source-resolver.ts
new file mode 100644
index 0000000000..b2412d3e31
--- /dev/null
+++ b/deps/v8/tools/turbolizer/src/source-resolver.ts
@@ -0,0 +1,528 @@
+// 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 {sortUnique, anyToString} from "./util.js"
+
+function sourcePositionLe(a, b) {
+ if (a.inliningId == b.inliningId) {
+ return a.scriptOffset - b.scriptOffset;
+ }
+ return a.inliningId - b.inliningId;
+}
+
+function sourcePositionEq(a, b) {
+ return a.inliningId == b.inliningId &&
+ a.scriptOffset == b.scriptOffset;
+}
+
+export function sourcePositionToStringKey(sourcePosition): string {
+ if (!sourcePosition) return "undefined";
+ if (sourcePosition.inliningId && sourcePosition.scriptOffset)
+ return "SP:" + sourcePosition.inliningId + ":" + sourcePosition.scriptOffset;
+ if (sourcePosition.bytecodePosition)
+ return "BCP:" + sourcePosition.bytecodePosition;
+ return "undefined";
+}
+
+export function sourcePositionValid(l) {
+ return (typeof l.scriptOffset !== undefined
+ && typeof l.inliningId !== undefined) || typeof l.bytecodePosition != undefined;
+}
+
+export interface SourcePosition {
+ scriptOffset: number;
+ inliningId: number;
+}
+
+interface TurboFanOrigin {
+ phase: string;
+ reducer: string;
+}
+
+export interface NodeOrigin {
+ nodeId: number;
+}
+
+interface BytecodePosition {
+ bytecodePosition: number;
+}
+
+type Origin = NodeOrigin | BytecodePosition;
+type TurboFanNodeOrigin = NodeOrigin & TurboFanOrigin;
+type TurboFanBytecodeOrigin = BytecodePosition & TurboFanOrigin;
+
+type AnyPosition = SourcePosition | BytecodePosition;
+
+export interface Source {
+ sourcePositions: Array<SourcePosition>;
+ sourceName: string;
+ functionName: string;
+ sourceText: string;
+ sourceId: number;
+ startPosition?: number;
+}
+interface Inlining {
+ inliningPosition: SourcePosition;
+ sourceId: number;
+}
+interface Phase {
+ type: string;
+ name: string;
+ data: any;
+}
+
+export interface Schedule {
+ nodes: Array<any>;
+}
+
+export class SourceResolver {
+ nodePositionMap: Array<AnyPosition>;
+ sources: Array<Source>;
+ inlinings: Array<Inlining>;
+ inliningsMap: Map<string, Inlining>;
+ positionToNodes: Map<string, Array<string>>;
+ phases: Array<Phase>;
+ phaseNames: Map<string, number>;
+ disassemblyPhase: Phase;
+ lineToSourcePositions: Map<string, Array<AnyPosition>>;
+ nodeIdToInstructionRange: Array<[number, number]>;
+ blockIdToInstructionRange: Array<[number, number]>;
+ instructionToPCOffset: Array<number>;
+ pcOffsetToInstructions: Map<number, Array<number>>;
+
+
+ constructor() {
+ // Maps node ids to source positions.
+ this.nodePositionMap = [];
+ // Maps source ids to source objects.
+ this.sources = [];
+ // Maps inlining ids to inlining objects.
+ this.inlinings = [];
+ // Maps source position keys to inlinings.
+ this.inliningsMap = new Map();
+ // Maps source position keys to node ids.
+ this.positionToNodes = new Map();
+ // Maps phase ids to phases.
+ this.phases = [];
+ // Maps phase names to phaseIds.
+ this.phaseNames = new Map();
+ // The disassembly phase is stored separately.
+ this.disassemblyPhase = undefined;
+ // Maps line numbers to source positions
+ this.lineToSourcePositions = new Map();
+ // Maps node ids to instruction ranges.
+ this.nodeIdToInstructionRange = [];
+ // Maps block ids to instruction ranges.
+ this.blockIdToInstructionRange = [];
+ // Maps instruction numbers to PC offsets.
+ this.instructionToPCOffset = [];
+ // Maps PC offsets to instructions.
+ this.pcOffsetToInstructions = new Map();
+ }
+
+ setSources(sources, mainBackup) {
+ if (sources) {
+ for (let [sourceId, source] of Object.entries(sources)) {
+ this.sources[sourceId] = source;
+ this.sources[sourceId].sourcePositions = [];
+ }
+ }
+ // This is a fallback if the JSON is incomplete (e.g. due to compiler crash).
+ if (!this.sources[-1]) {
+ this.sources[-1] = mainBackup;
+ this.sources[-1].sourcePositions = [];
+ }
+ }
+
+ setInlinings(inlinings) {
+ if (inlinings) {
+ for (const [inliningId, inlining] of Object.entries<Inlining>(inlinings)) {
+ this.inlinings[inliningId] = inlining;
+ this.inliningsMap.set(sourcePositionToStringKey(inlining.inliningPosition), inlining);
+ }
+ }
+ // This is a default entry for the script itself that helps
+ // keep other code more uniform.
+ this.inlinings[-1] = { sourceId: -1, inliningPosition: null };
+ }
+
+ setNodePositionMap(map) {
+ if (!map) return;
+ if (typeof map[0] != 'object') {
+ const alternativeMap = {};
+ for (const [nodeId, scriptOffset] of Object.entries<number>(map)) {
+ alternativeMap[nodeId] = { scriptOffset: scriptOffset, inliningId: -1 };
+ }
+ map = alternativeMap;
+ };
+
+ for (const [nodeId, sourcePosition] of Object.entries<SourcePosition>(map)) {
+ if (sourcePosition == undefined) {
+ console.log("Warning: undefined source position ", sourcePosition, " for nodeId ", nodeId);
+ }
+ const inliningId = sourcePosition.inliningId;
+ const inlining = this.inlinings[inliningId];
+ if (inlining) {
+ const sourceId = inlining.sourceId;
+ this.sources[sourceId].sourcePositions.push(sourcePosition);
+ }
+ this.nodePositionMap[nodeId] = sourcePosition;
+ let key = sourcePositionToStringKey(sourcePosition);
+ if (!this.positionToNodes.has(key)) {
+ this.positionToNodes.set(key, []);
+ }
+ this.positionToNodes.get(key).push(nodeId);
+ }
+ for (const [sourceId, source] of Object.entries(this.sources)) {
+ source.sourcePositions = sortUnique(source.sourcePositions,
+ sourcePositionLe, sourcePositionEq);
+ }
+ }
+
+ sourcePositionsToNodeIds(sourcePositions) {
+ const nodeIds = new Set();
+ for (const sp of sourcePositions) {
+ let key = sourcePositionToStringKey(sp);
+ let nodeIdsForPosition = this.positionToNodes.get(key);
+ if (!nodeIdsForPosition) continue;
+ for (const nodeId of nodeIdsForPosition) {
+ nodeIds.add(nodeId);
+ }
+ }
+ return nodeIds;
+ }
+
+ nodeIdsToSourcePositions(nodeIds): Array<AnyPosition> {
+ const sourcePositions = new Map();
+ for (const nodeId of nodeIds) {
+ let sp = this.nodePositionMap[nodeId];
+ let key = sourcePositionToStringKey(sp);
+ sourcePositions.set(key, sp);
+ }
+ const sourcePositionArray = [];
+ for (const sp of sourcePositions.values()) {
+ sourcePositionArray.push(sp);
+ }
+ return sourcePositionArray;
+ }
+
+ forEachSource(f) {
+ this.sources.forEach(f);
+ }
+
+ translateToSourceId(sourceId, location) {
+ for (const position of this.getInlineStack(location)) {
+ let inlining = this.inlinings[position.inliningId];
+ if (!inlining) continue;
+ if (inlining.sourceId == sourceId) {
+ return position;
+ }
+ }
+ return location;
+ }
+
+ addInliningPositions(sourcePosition, locations) {
+ let inlining = this.inliningsMap.get(sourcePositionToStringKey(sourcePosition));
+ if (!inlining) return;
+ let sourceId = inlining.sourceId
+ const source = this.sources[sourceId];
+ for (const sp of source.sourcePositions) {
+ locations.push(sp);
+ this.addInliningPositions(sp, locations);
+ }
+ }
+
+ getInliningForPosition(sourcePosition) {
+ return this.inliningsMap.get(sourcePositionToStringKey(sourcePosition));
+ }
+
+ getSource(sourceId) {
+ return this.sources[sourceId];
+ }
+
+ getSourceName(sourceId) {
+ const source = this.sources[sourceId];
+ return `${source.sourceName}:${source.functionName}`;
+ }
+
+ sourcePositionFor(sourceId, scriptOffset) {
+ if (!this.sources[sourceId]) {
+ return null;
+ }
+ const list = this.sources[sourceId].sourcePositions;
+ for (let i = 0; i < list.length; i++) {
+ const sourcePosition = list[i]
+ const position = sourcePosition.scriptOffset;
+ const nextPosition = list[Math.min(i + 1, list.length - 1)].scriptOffset;
+ if ((position <= scriptOffset && scriptOffset < nextPosition)) {
+ return sourcePosition;
+ }
+ }
+ return null;
+ }
+
+ sourcePositionsInRange(sourceId, start, end) {
+ if (!this.sources[sourceId]) return [];
+ const res = [];
+ const list = this.sources[sourceId].sourcePositions;
+ for (let i = 0; i < list.length; i++) {
+ const sourcePosition = list[i]
+ if (start <= sourcePosition.scriptOffset && sourcePosition.scriptOffset < end) {
+ res.push(sourcePosition);
+ }
+ }
+ return res;
+ }
+
+ getInlineStack(sourcePosition) {
+ if (!sourcePosition) {
+ return [];
+ }
+ let inliningStack = [];
+ let cur = sourcePosition;
+ while (cur && cur.inliningId != -1) {
+ inliningStack.push(cur);
+ let inlining = this.inlinings[cur.inliningId];
+ if (!inlining) {
+ break;
+ }
+ cur = inlining.inliningPosition;
+ }
+ if (cur && cur.inliningId == -1) {
+ inliningStack.push(cur);
+ }
+ return inliningStack;
+ }
+
+ recordOrigins(phase) {
+ if (phase.type != "graph") return;
+ for (const node of phase.data.nodes) {
+ if (node.origin != undefined &&
+ node.origin.bytecodePosition != undefined) {
+ const position = { bytecodePosition: node.origin.bytecodePosition };
+ this.nodePositionMap[node.id] = position;
+ let key = sourcePositionToStringKey(position);
+ if (!this.positionToNodes.has(key)) {
+ this.positionToNodes.set(key, []);
+ }
+ const A = this.positionToNodes.get(key);
+ if (!A.includes(node.id)) A.push("" + node.id);
+ }
+ }
+ }
+
+ readNodeIdToInstructionRange(nodeIdToInstructionRange) {
+ for (const [nodeId, range] of Object.entries<[number, number]>(nodeIdToInstructionRange)) {
+ this.nodeIdToInstructionRange[nodeId] = range;
+ }
+ }
+
+ readBlockIdToInstructionRange(blockIdToInstructionRange) {
+ for (const [blockId, range] of Object.entries<[number, number]>(blockIdToInstructionRange)) {
+ this.blockIdToInstructionRange[blockId] = range;
+ }
+ }
+
+ getInstruction(nodeId):[number, number] {
+ const X = this.nodeIdToInstructionRange[nodeId];
+ if (X === undefined) return [-1, -1];
+ return X;
+ }
+
+ getInstructionRangeForBlock(blockId):[number, number] {
+ const X = this.blockIdToInstructionRange[blockId];
+ if (X === undefined) return [-1, -1];
+ return X;
+ }
+
+ readInstructionOffsetToPCOffset(instructionToPCOffset) {
+ for (const [instruction, offset] of Object.entries<number>(instructionToPCOffset)) {
+ this.instructionToPCOffset[instruction] = offset;
+ if (!this.pcOffsetToInstructions.has(offset)) {
+ this.pcOffsetToInstructions.set(offset, []);
+ }
+ this.pcOffsetToInstructions.get(offset).push(instruction);
+ }
+ console.log(this.pcOffsetToInstructions);
+ }
+
+ hasPCOffsets() {
+ return this.pcOffsetToInstructions.size > 0;
+ }
+
+
+ nodesForPCOffset(offset): [Array<String>, Array<String>] {
+ const keys = Array.from(this.pcOffsetToInstructions.keys()).sort((a, b) => b - a);
+ if (keys.length === 0) return [[],[]];
+ for (const key of keys) {
+ if (key <= offset) {
+ const instrs = this.pcOffsetToInstructions.get(key);
+ const nodes = [];
+ const blocks = [];
+ for (const instr of instrs) {
+ for (const [nodeId, range] of this.nodeIdToInstructionRange.entries()) {
+ if (!range) continue;
+ const [start, end] = range;
+ if (start == end && instr == start) {
+ nodes.push("" + nodeId);
+ }
+ if (start <= instr && instr < end) {
+ nodes.push("" + nodeId);
+ }
+ }
+ }
+ return [nodes, blocks];
+ }
+ }
+ return [[],[]];
+ }
+
+ parsePhases(phases) {
+ for (const [phaseId, phase] of Object.entries<Phase>(phases)) {
+ if (phase.type == 'disassembly') {
+ this.disassemblyPhase = phase;
+ } else if (phase.type == 'schedule') {
+ this.phases.push(this.parseSchedule(phase))
+ this.phaseNames.set(phase.name, this.phases.length);
+ } else if (phase.type == 'instructions') {
+ if (phase.nodeIdToInstructionRange) {
+ this.readNodeIdToInstructionRange(phase.nodeIdToInstructionRange);
+ }
+ if (phase.blockIdtoInstructionRange) {
+ this.readBlockIdToInstructionRange(phase.blockIdtoInstructionRange);
+ }
+ if (phase.instructionOffsetToPCOffset) {
+ this.readInstructionOffsetToPCOffset(phase.instructionOffsetToPCOffset);
+ }
+ } else {
+ this.phases.push(phase);
+ this.recordOrigins(phase);
+ this.phaseNames.set(phase.name, this.phases.length);
+ }
+ }
+ }
+
+ repairPhaseId(anyPhaseId) {
+ return Math.max(0, Math.min(anyPhaseId, this.phases.length - 1))
+ }
+
+ getPhase(phaseId) {
+ return this.phases[phaseId];
+ }
+
+ getPhaseIdByName(phaseName) {
+ return this.phaseNames.get(phaseName);
+ }
+
+ forEachPhase(f) {
+ this.phases.forEach(f);
+ }
+
+ addAnyPositionToLine(lineNumber: number | String, sourcePosition: AnyPosition) {
+ const lineNumberString = anyToString(lineNumber);
+ if (!this.lineToSourcePositions.has(lineNumberString)) {
+ this.lineToSourcePositions.set(lineNumberString, []);
+ }
+ const A = this.lineToSourcePositions.get(lineNumberString);
+ if (!A.includes(sourcePosition)) A.push(sourcePosition);
+ }
+
+ setSourceLineToBytecodePosition(sourceLineToBytecodePosition: Array<number> | undefined) {
+ if (!sourceLineToBytecodePosition) return;
+ sourceLineToBytecodePosition.forEach((pos, i) => {
+ this.addAnyPositionToLine(i, { bytecodePosition: pos });
+ });
+ }
+
+ linetoSourcePositions(lineNumber: number | String) {
+ const positions = this.lineToSourcePositions.get(anyToString(lineNumber));
+ if (positions === undefined) return [];
+ return positions;
+ }
+
+ parseSchedule(phase) {
+ function createNode(state, match) {
+ let inputs = [];
+ if (match.groups.args) {
+ const nodeIdsString = match.groups.args.replace(/\s/g, '');
+ const nodeIdStrings = nodeIdsString.split(',');
+ inputs = nodeIdStrings.map((n) => Number.parseInt(n, 10));
+ }
+ const node = {
+ id: Number.parseInt(match.groups.id, 10),
+ label: match.groups.label,
+ inputs: inputs
+ };
+ if (match.groups.blocks) {
+ const nodeIdsString = match.groups.blocks.replace(/\s/g, '').replace(/B/g, '');
+ const nodeIdStrings = nodeIdsString.split(',');
+ const successors = nodeIdStrings.map((n) => Number.parseInt(n, 10));
+ state.currentBlock.succ = successors;
+ }
+ state.nodes[node.id] = node;
+ state.currentBlock.nodes.push(node);
+ }
+ function createBlock(state, match) {
+ let predecessors = [];
+ if (match.groups.in) {
+ const blockIdsString = match.groups.in.replace(/\s/g, '').replace(/B/g, '');
+ const blockIdStrings = blockIdsString.split(',');
+ predecessors = blockIdStrings.map((n) => Number.parseInt(n, 10));
+ }
+ const block = {
+ id: Number.parseInt(match.groups.id, 10),
+ isDeferred: match.groups.deferred != undefined,
+ pred: predecessors.sort(),
+ succ: [],
+ nodes: []
+ };
+ state.blocks[block.id] = block;
+ state.currentBlock = block;
+ }
+ function setGotoSuccessor(state, match) {
+ state.currentBlock.succ = [Number.parseInt(match.groups.successor.replace(/\s/g, ''), 10)];
+ }
+ const rules = [
+ {
+ lineRegexps:
+ [/^\s*(?<id>\d+):\ (?<label>.*)\((?<args>.*)\)$/,
+ /^\s*(?<id>\d+):\ (?<label>.*)\((?<args>.*)\)\ ->\ (?<blocks>.*)$/,
+ /^\s*(?<id>\d+):\ (?<label>.*)$/
+ ],
+ process: createNode
+ },
+ {
+ lineRegexps:
+ [/^\s*---\s*BLOCK\ B(?<id>\d+)\s*(?<deferred>\(deferred\))?(\ <-\ )?(?<in>[^-]*)?\ ---$/
+ ],
+ process: createBlock
+ },
+ {
+ lineRegexps:
+ [/^\s*Goto\s*->\s*B(?<successor>\d+)\s*$/
+ ],
+ process: setGotoSuccessor
+ }
+ ];
+
+ const lines = phase.data.split(/[\n]/);
+ const state = { currentBlock: undefined, blocks: [], nodes: [] };
+
+ nextLine:
+ for (const line of lines) {
+ for (const rule of rules) {
+ for (const lineRegexp of rule.lineRegexps) {
+ const match = line.match(lineRegexp);
+ if (match) {
+ rule.process(state, match);
+ continue nextLine;
+ }
+ }
+ }
+ console.log("Warning: unmatched schedule line \"" + line + "\"");
+ }
+ phase.schedule = state;
+ return phase;
+ }
+}