1// Copyright 2018 the V8 project authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5import { sortUnique, anyToString } from "../src/util"; 6import { NodeLabel } from "./node-label"; 7 8function sourcePositionLe(a, b) { 9 if (a.inliningId == b.inliningId) { 10 return a.scriptOffset - b.scriptOffset; 11 } 12 return a.inliningId - b.inliningId; 13} 14 15function sourcePositionEq(a, b) { 16 return a.inliningId == b.inliningId && 17 a.scriptOffset == b.scriptOffset; 18} 19 20export function sourcePositionToStringKey(sourcePosition: AnyPosition): string { 21 if (!sourcePosition) return "undefined"; 22 if ('inliningId' in sourcePosition && 'scriptOffset' in sourcePosition) { 23 return "SP:" + sourcePosition.inliningId + ":" + sourcePosition.scriptOffset; 24 } 25 if (sourcePosition.bytecodePosition) { 26 return "BCP:" + sourcePosition.bytecodePosition; 27 } 28 return "undefined"; 29} 30 31export function sourcePositionValid(l) { 32 return (typeof l.scriptOffset !== undefined 33 && typeof l.inliningId !== undefined) || typeof l.bytecodePosition != undefined; 34} 35 36export interface SourcePosition { 37 scriptOffset: number; 38 inliningId: number; 39} 40 41interface TurboFanOrigin { 42 phase: string; 43 reducer: string; 44} 45 46export interface NodeOrigin { 47 nodeId: number; 48} 49 50interface BytecodePosition { 51 bytecodePosition: number; 52} 53 54export type Origin = NodeOrigin | BytecodePosition; 55export type TurboFanNodeOrigin = NodeOrigin & TurboFanOrigin; 56export type TurboFanBytecodeOrigin = BytecodePosition & TurboFanOrigin; 57 58type AnyPosition = SourcePosition | BytecodePosition; 59 60export interface Source { 61 sourcePositions: Array<SourcePosition>; 62 sourceName: string; 63 functionName: string; 64 sourceText: string; 65 sourceId: number; 66 startPosition?: number; 67 backwardsCompatibility: boolean; 68} 69interface Inlining { 70 inliningPosition: SourcePosition; 71 sourceId: number; 72} 73interface OtherPhase { 74 type: "disassembly" | "sequence" | "schedule"; 75 name: string; 76 data: any; 77} 78 79interface InstructionsPhase { 80 type: "instructions"; 81 name: string; 82 data: any; 83 instructionOffsetToPCOffset?: any; 84 blockIdToInstructionRange?: any; 85 nodeIdToInstructionRange?: any; 86 codeOffsetsInfo?: CodeOffsetsInfo; 87} 88 89interface GraphPhase { 90 type: "graph"; 91 name: string; 92 data: any; 93 highestNodeId: number; 94 nodeLabelMap: Array<NodeLabel>; 95} 96 97type Phase = GraphPhase | InstructionsPhase | OtherPhase; 98 99export interface Schedule { 100 nodes: Array<any>; 101} 102 103export class Interval { 104 start: number; 105 end: number; 106 107 constructor(numbers: [number, number]) { 108 this.start = numbers[0]; 109 this.end = numbers[1]; 110 } 111} 112 113export interface ChildRange { 114 id: string; 115 type: string; 116 op: any; 117 intervals: Array<[number, number]>; 118 uses: Array<number>; 119} 120 121export interface Range { 122 child_ranges: Array<ChildRange>; 123 is_deferred: boolean; 124} 125 126export class RegisterAllocation { 127 fixedDoubleLiveRanges: Map<string, Range>; 128 fixedLiveRanges: Map<string, Range>; 129 liveRanges: Map<string, Range>; 130 131 constructor(registerAllocation) { 132 this.fixedDoubleLiveRanges = new Map<string, Range>(Object.entries(registerAllocation.fixed_double_live_ranges)); 133 this.fixedLiveRanges = new Map<string, Range>(Object.entries(registerAllocation.fixed_live_ranges)); 134 this.liveRanges = new Map<string, Range>(Object.entries(registerAllocation.live_ranges)); 135 } 136} 137 138export interface Sequence { 139 blocks: Array<any>; 140 register_allocation: RegisterAllocation; 141} 142 143class CodeOffsetsInfo { 144 codeStartRegisterCheck: number; 145 deoptCheck: number; 146 initPoison: number; 147 blocksStart: number; 148 outOfLineCode: number; 149 deoptimizationExits: number; 150 pools: number; 151 jumpTables: number; 152} 153export class TurbolizerInstructionStartInfo { 154 gap: number; 155 arch: number; 156 condition: number; 157} 158 159export class SourceResolver { 160 nodePositionMap: Array<AnyPosition>; 161 sources: Array<Source>; 162 inlinings: Array<Inlining>; 163 inliningsMap: Map<string, Inlining>; 164 positionToNodes: Map<string, Array<string>>; 165 phases: Array<Phase>; 166 phaseNames: Map<string, number>; 167 disassemblyPhase: Phase; 168 linePositionMap: Map<string, Array<AnyPosition>>; 169 nodeIdToInstructionRange: Array<[number, number]>; 170 blockIdToInstructionRange: Array<[number, number]>; 171 instructionToPCOffset: Array<TurbolizerInstructionStartInfo>; 172 pcOffsetToInstructions: Map<number, Array<number>>; 173 pcOffsets: Array<number>; 174 blockIdToPCOffset: Array<number>; 175 blockStartPCtoBlockIds: Map<number, Array<number>>; 176 codeOffsetsInfo: CodeOffsetsInfo; 177 178 constructor() { 179 // Maps node ids to source positions. 180 this.nodePositionMap = []; 181 // Maps source ids to source objects. 182 this.sources = []; 183 // Maps inlining ids to inlining objects. 184 this.inlinings = []; 185 // Maps source position keys to inlinings. 186 this.inliningsMap = new Map(); 187 // Maps source position keys to node ids. 188 this.positionToNodes = new Map(); 189 // Maps phase ids to phases. 190 this.phases = []; 191 // Maps phase names to phaseIds. 192 this.phaseNames = new Map(); 193 // The disassembly phase is stored separately. 194 this.disassemblyPhase = undefined; 195 // Maps line numbers to source positions 196 this.linePositionMap = new Map(); 197 // Maps node ids to instruction ranges. 198 this.nodeIdToInstructionRange = []; 199 // Maps block ids to instruction ranges. 200 this.blockIdToInstructionRange = []; 201 // Maps instruction numbers to PC offsets. 202 this.instructionToPCOffset = []; 203 // Maps PC offsets to instructions. 204 this.pcOffsetToInstructions = new Map(); 205 this.pcOffsets = []; 206 this.blockIdToPCOffset = []; 207 this.blockStartPCtoBlockIds = new Map(); 208 this.codeOffsetsInfo = null; 209 } 210 211 getBlockIdsForOffset(offset): Array<number> { 212 return this.blockStartPCtoBlockIds.get(offset); 213 } 214 215 hasBlockStartInfo() { 216 return this.blockIdToPCOffset.length > 0; 217 } 218 219 setSources(sources, mainBackup) { 220 if (sources) { 221 for (const [sourceId, source] of Object.entries(sources)) { 222 this.sources[sourceId] = source; 223 this.sources[sourceId].sourcePositions = []; 224 } 225 } 226 // This is a fallback if the JSON is incomplete (e.g. due to compiler crash). 227 if (!this.sources[-1]) { 228 this.sources[-1] = mainBackup; 229 this.sources[-1].sourcePositions = []; 230 } 231 } 232 233 setInlinings(inlinings) { 234 if (inlinings) { 235 for (const [inliningId, inlining] of Object.entries<Inlining>(inlinings)) { 236 this.inlinings[inliningId] = inlining; 237 this.inliningsMap.set(sourcePositionToStringKey(inlining.inliningPosition), inlining); 238 } 239 } 240 // This is a default entry for the script itself that helps 241 // keep other code more uniform. 242 this.inlinings[-1] = { sourceId: -1, inliningPosition: null }; 243 } 244 245 setNodePositionMap(map) { 246 if (!map) return; 247 if (typeof map[0] != 'object') { 248 const alternativeMap = {}; 249 for (const [nodeId, scriptOffset] of Object.entries<number>(map)) { 250 alternativeMap[nodeId] = { scriptOffset: scriptOffset, inliningId: -1 }; 251 } 252 map = alternativeMap; 253 } 254 255 for (const [nodeId, sourcePosition] of Object.entries<SourcePosition>(map)) { 256 if (sourcePosition == undefined) { 257 console.log("Warning: undefined source position ", sourcePosition, " for nodeId ", nodeId); 258 } 259 const inliningId = sourcePosition.inliningId; 260 const inlining = this.inlinings[inliningId]; 261 if (inlining) { 262 const sourceId = inlining.sourceId; 263 this.sources[sourceId].sourcePositions.push(sourcePosition); 264 } 265 this.nodePositionMap[nodeId] = sourcePosition; 266 const key = sourcePositionToStringKey(sourcePosition); 267 if (!this.positionToNodes.has(key)) { 268 this.positionToNodes.set(key, []); 269 } 270 this.positionToNodes.get(key).push(nodeId); 271 } 272 for (const [, source] of Object.entries(this.sources)) { 273 source.sourcePositions = sortUnique(source.sourcePositions, 274 sourcePositionLe, sourcePositionEq); 275 } 276 } 277 278 sourcePositionsToNodeIds(sourcePositions) { 279 const nodeIds = new Set<string>(); 280 for (const sp of sourcePositions) { 281 const key = sourcePositionToStringKey(sp); 282 const nodeIdsForPosition = this.positionToNodes.get(key); 283 if (!nodeIdsForPosition) continue; 284 for (const nodeId of nodeIdsForPosition) { 285 nodeIds.add(nodeId); 286 } 287 } 288 return nodeIds; 289 } 290 291 nodeIdsToSourcePositions(nodeIds): Array<AnyPosition> { 292 const sourcePositions = new Map(); 293 for (const nodeId of nodeIds) { 294 const sp = this.nodePositionMap[nodeId]; 295 const key = sourcePositionToStringKey(sp); 296 sourcePositions.set(key, sp); 297 } 298 const sourcePositionArray = []; 299 for (const sp of sourcePositions.values()) { 300 sourcePositionArray.push(sp); 301 } 302 return sourcePositionArray; 303 } 304 305 forEachSource(f: (value: Source, index: number, array: Array<Source>) => void) { 306 this.sources.forEach(f); 307 } 308 309 translateToSourceId(sourceId: number, location?: SourcePosition) { 310 for (const position of this.getInlineStack(location)) { 311 const inlining = this.inlinings[position.inliningId]; 312 if (!inlining) continue; 313 if (inlining.sourceId == sourceId) { 314 return position; 315 } 316 } 317 return location; 318 } 319 320 addInliningPositions(sourcePosition: AnyPosition, locations: Array<SourcePosition>) { 321 const inlining = this.inliningsMap.get(sourcePositionToStringKey(sourcePosition)); 322 if (!inlining) return; 323 const sourceId = inlining.sourceId; 324 const source = this.sources[sourceId]; 325 for (const sp of source.sourcePositions) { 326 locations.push(sp); 327 this.addInliningPositions(sp, locations); 328 } 329 } 330 331 getInliningForPosition(sourcePosition: AnyPosition) { 332 return this.inliningsMap.get(sourcePositionToStringKey(sourcePosition)); 333 } 334 335 getSource(sourceId: number) { 336 return this.sources[sourceId]; 337 } 338 339 getSourceName(sourceId: number) { 340 const source = this.sources[sourceId]; 341 return `${source.sourceName}:${source.functionName}`; 342 } 343 344 sourcePositionFor(sourceId: number, scriptOffset: number) { 345 if (!this.sources[sourceId]) { 346 return null; 347 } 348 const list = this.sources[sourceId].sourcePositions; 349 for (let i = 0; i < list.length; i++) { 350 const sourcePosition = list[i]; 351 const position = sourcePosition.scriptOffset; 352 const nextPosition = list[Math.min(i + 1, list.length - 1)].scriptOffset; 353 if ((position <= scriptOffset && scriptOffset < nextPosition)) { 354 return sourcePosition; 355 } 356 } 357 return null; 358 } 359 360 sourcePositionsInRange(sourceId: number, start: number, end: number) { 361 if (!this.sources[sourceId]) return []; 362 const res = []; 363 const list = this.sources[sourceId].sourcePositions; 364 for (const sourcePosition of list) { 365 if (start <= sourcePosition.scriptOffset && sourcePosition.scriptOffset < end) { 366 res.push(sourcePosition); 367 } 368 } 369 return res; 370 } 371 372 getInlineStack(sourcePosition?: SourcePosition) { 373 if (!sourcePosition) return []; 374 375 const inliningStack = []; 376 let cur = sourcePosition; 377 while (cur && cur.inliningId != -1) { 378 inliningStack.push(cur); 379 const inlining = this.inlinings[cur.inliningId]; 380 if (!inlining) { 381 break; 382 } 383 cur = inlining.inliningPosition; 384 } 385 if (cur && cur.inliningId == -1) { 386 inliningStack.push(cur); 387 } 388 return inliningStack; 389 } 390 391 recordOrigins(phase: GraphPhase) { 392 if (phase.type != "graph") return; 393 for (const node of phase.data.nodes) { 394 phase.highestNodeId = Math.max(phase.highestNodeId, node.id); 395 if (node.origin != undefined && 396 node.origin.bytecodePosition != undefined) { 397 const position = { bytecodePosition: node.origin.bytecodePosition }; 398 this.nodePositionMap[node.id] = position; 399 const key = sourcePositionToStringKey(position); 400 if (!this.positionToNodes.has(key)) { 401 this.positionToNodes.set(key, []); 402 } 403 const A = this.positionToNodes.get(key); 404 if (!A.includes(node.id)) A.push(`${node.id}`); 405 } 406 407 // Backwards compatibility. 408 if (typeof node.pos === "number") { 409 node.sourcePosition = { scriptOffset: node.pos, inliningId: -1 }; 410 } 411 } 412 } 413 414 readNodeIdToInstructionRange(nodeIdToInstructionRange) { 415 for (const [nodeId, range] of Object.entries<[number, number]>(nodeIdToInstructionRange)) { 416 this.nodeIdToInstructionRange[nodeId] = range; 417 } 418 } 419 420 readBlockIdToInstructionRange(blockIdToInstructionRange) { 421 for (const [blockId, range] of Object.entries<[number, number]>(blockIdToInstructionRange)) { 422 this.blockIdToInstructionRange[blockId] = range; 423 } 424 } 425 426 getInstruction(nodeId: number): [number, number] { 427 const X = this.nodeIdToInstructionRange[nodeId]; 428 if (X === undefined) return [-1, -1]; 429 return X; 430 } 431 432 getInstructionRangeForBlock(blockId: number): [number, number] { 433 const X = this.blockIdToInstructionRange[blockId]; 434 if (X === undefined) return [-1, -1]; 435 return X; 436 } 437 438 readInstructionOffsetToPCOffset(instructionToPCOffset) { 439 for (const [instruction, numberOrInfo] of Object.entries<number | TurbolizerInstructionStartInfo>(instructionToPCOffset)) { 440 let info: TurbolizerInstructionStartInfo; 441 if (typeof numberOrInfo == "number") { 442 info = { gap: numberOrInfo, arch: numberOrInfo, condition: numberOrInfo }; 443 } else { 444 info = numberOrInfo; 445 } 446 this.instructionToPCOffset[instruction] = info; 447 if (!this.pcOffsetToInstructions.has(info.gap)) { 448 this.pcOffsetToInstructions.set(info.gap, []); 449 } 450 this.pcOffsetToInstructions.get(info.gap).push(Number(instruction)); 451 } 452 this.pcOffsets = Array.from(this.pcOffsetToInstructions.keys()).sort((a, b) => b - a); 453 } 454 455 hasPCOffsets() { 456 return this.pcOffsetToInstructions.size > 0; 457 } 458 459 getKeyPcOffset(offset: number): number { 460 if (this.pcOffsets.length === 0) return -1; 461 for (const key of this.pcOffsets) { 462 if (key <= offset) { 463 return key; 464 } 465 } 466 return -1; 467 } 468 469 getInstructionKindForPCOffset(offset: number) { 470 if (this.codeOffsetsInfo) { 471 if (offset >= this.codeOffsetsInfo.deoptimizationExits) { 472 if (offset >= this.codeOffsetsInfo.pools) { 473 return "pools"; 474 } else if (offset >= this.codeOffsetsInfo.jumpTables) { 475 return "jump-tables"; 476 } else { 477 return "deoptimization-exits"; 478 } 479 } 480 if (offset < this.codeOffsetsInfo.deoptCheck) { 481 return "code-start-register"; 482 } else if (offset < this.codeOffsetsInfo.initPoison) { 483 return "deopt-check"; 484 } else if (offset < this.codeOffsetsInfo.blocksStart) { 485 return "init-poison"; 486 } 487 } 488 const keyOffset = this.getKeyPcOffset(offset); 489 if (keyOffset != -1) { 490 const infos = this.pcOffsetToInstructions.get(keyOffset).map(instrId => this.instructionToPCOffset[instrId]).filter(info => info.gap != info.condition); 491 if (infos.length > 0) { 492 const info = infos[0]; 493 if (!info || info.gap == info.condition) return "unknown"; 494 if (offset < info.arch) return "gap"; 495 if (offset < info.condition) return "arch"; 496 return "condition"; 497 } 498 } 499 return "unknown"; 500 } 501 502 instructionKindToReadableName(instructionKind) { 503 switch (instructionKind) { 504 case "code-start-register": return "Check code register for right value"; 505 case "deopt-check": return "Check if function was marked for deoptimization"; 506 case "init-poison": return "Initialization of poison register"; 507 case "gap": return "Instruction implementing a gap move"; 508 case "arch": return "Instruction implementing the actual machine operation"; 509 case "condition": return "Code implementing conditional after instruction"; 510 case "pools": return "Data in a pool (e.g. constant pool)"; 511 case "jump-tables": return "Part of a jump table"; 512 case "deoptimization-exits": return "Jump to deoptimization exit"; 513 } 514 return null; 515 } 516 517 instructionRangeToKeyPcOffsets([start, end]: [number, number]): Array<TurbolizerInstructionStartInfo> { 518 if (start == end) return [this.instructionToPCOffset[start]]; 519 return this.instructionToPCOffset.slice(start, end); 520 } 521 522 instructionToPcOffsets(instr: number): TurbolizerInstructionStartInfo { 523 return this.instructionToPCOffset[instr]; 524 } 525 526 instructionsToKeyPcOffsets(instructionIds: Iterable<number>): Array<number> { 527 const keyPcOffsets = []; 528 for (const instructionId of instructionIds) { 529 const pcOffset = this.instructionToPCOffset[instructionId]; 530 if (pcOffset !== undefined) keyPcOffsets.push(pcOffset.gap); 531 } 532 return keyPcOffsets; 533 } 534 535 nodesToKeyPcOffsets(nodes) { 536 let offsets = []; 537 for (const node of nodes) { 538 const range = this.nodeIdToInstructionRange[node]; 539 if (!range) continue; 540 offsets = offsets.concat(this.instructionRangeToKeyPcOffsets(range)); 541 } 542 return offsets; 543 } 544 545 nodesForPCOffset(offset: number): [Array<string>, Array<string>] { 546 if (this.pcOffsets.length === 0) return [[], []]; 547 for (const key of this.pcOffsets) { 548 if (key <= offset) { 549 const instrs = this.pcOffsetToInstructions.get(key); 550 const nodes = []; 551 const blocks = []; 552 for (const instr of instrs) { 553 for (const [nodeId, range] of this.nodeIdToInstructionRange.entries()) { 554 if (!range) continue; 555 const [start, end] = range; 556 if (start == end && instr == start) { 557 nodes.push("" + nodeId); 558 } 559 if (start <= instr && instr < end) { 560 nodes.push("" + nodeId); 561 } 562 } 563 } 564 return [nodes, blocks]; 565 } 566 } 567 return [[], []]; 568 } 569 570 parsePhases(phases) { 571 const nodeLabelMap = []; 572 for (const [, phase] of Object.entries<Phase>(phases)) { 573 switch (phase.type) { 574 case 'disassembly': 575 this.disassemblyPhase = phase; 576 if (phase['blockIdToOffset']) { 577 for (const [blockId, pc] of Object.entries<number>(phase['blockIdToOffset'])) { 578 this.blockIdToPCOffset[blockId] = pc; 579 if (!this.blockStartPCtoBlockIds.has(pc)) { 580 this.blockStartPCtoBlockIds.set(pc, []); 581 } 582 this.blockStartPCtoBlockIds.get(pc).push(Number(blockId)); 583 } 584 } 585 break; 586 case 'schedule': 587 this.phaseNames.set(phase.name, this.phases.length); 588 this.phases.push(this.parseSchedule(phase)); 589 break; 590 case 'sequence': 591 this.phaseNames.set(phase.name, this.phases.length); 592 this.phases.push(this.parseSequence(phase)); 593 break; 594 case 'instructions': 595 if (phase.nodeIdToInstructionRange) { 596 this.readNodeIdToInstructionRange(phase.nodeIdToInstructionRange); 597 } 598 if (phase.blockIdToInstructionRange) { 599 this.readBlockIdToInstructionRange(phase.blockIdToInstructionRange); 600 } 601 if (phase.instructionOffsetToPCOffset) { 602 this.readInstructionOffsetToPCOffset(phase.instructionOffsetToPCOffset); 603 } 604 if (phase.codeOffsetsInfo) { 605 this.codeOffsetsInfo = phase.codeOffsetsInfo; 606 } 607 break; 608 case 'graph': 609 const graphPhase: GraphPhase = Object.assign(phase, { highestNodeId: 0 }); 610 this.phaseNames.set(graphPhase.name, this.phases.length); 611 this.phases.push(graphPhase); 612 this.recordOrigins(graphPhase); 613 this.internNodeLabels(graphPhase, nodeLabelMap); 614 graphPhase.nodeLabelMap = nodeLabelMap.slice(); 615 break; 616 default: 617 throw "Unsupported phase type"; 618 } 619 } 620 } 621 622 internNodeLabels(phase: GraphPhase, nodeLabelMap: Array<NodeLabel>) { 623 for (const n of phase.data.nodes) { 624 const label = new NodeLabel(n.id, n.label, n.title, n.live, 625 n.properties, n.sourcePosition, n.origin, n.opcode, n.control, 626 n.opinfo, n.type); 627 const previous = nodeLabelMap[label.id]; 628 if (!label.equals(previous)) { 629 if (previous != undefined) { 630 label.setInplaceUpdatePhase(phase.name); 631 } 632 nodeLabelMap[label.id] = label; 633 } 634 n.nodeLabel = nodeLabelMap[label.id]; 635 } 636 } 637 638 repairPhaseId(anyPhaseId) { 639 return Math.max(0, Math.min(anyPhaseId | 0, this.phases.length - 1)); 640 } 641 642 getPhase(phaseId: number) { 643 return this.phases[phaseId]; 644 } 645 646 getPhaseIdByName(phaseName: string) { 647 return this.phaseNames.get(phaseName); 648 } 649 650 forEachPhase(f: (value: Phase, index: number, array: Array<Phase>) => void) { 651 this.phases.forEach(f); 652 } 653 654 addAnyPositionToLine(lineNumber: number | string, sourcePosition: AnyPosition) { 655 const lineNumberString = anyToString(lineNumber); 656 if (!this.linePositionMap.has(lineNumberString)) { 657 this.linePositionMap.set(lineNumberString, []); 658 } 659 const A = this.linePositionMap.get(lineNumberString); 660 if (!A.includes(sourcePosition)) A.push(sourcePosition); 661 } 662 663 setSourceLineToBytecodePosition(sourceLineToBytecodePosition: Array<number> | undefined) { 664 if (!sourceLineToBytecodePosition) return; 665 sourceLineToBytecodePosition.forEach((pos, i) => { 666 this.addAnyPositionToLine(i, { bytecodePosition: pos }); 667 }); 668 } 669 670 lineToSourcePositions(lineNumber: number | string) { 671 const positions = this.linePositionMap.get(anyToString(lineNumber)); 672 if (positions === undefined) return []; 673 return positions; 674 } 675 676 parseSchedule(phase) { 677 function createNode(state: any, match) { 678 let inputs = []; 679 if (match.groups.args) { 680 const nodeIdsString = match.groups.args.replace(/\s/g, ''); 681 const nodeIdStrings = nodeIdsString.split(','); 682 inputs = nodeIdStrings.map(n => Number.parseInt(n, 10)); 683 } 684 const node = { 685 id: Number.parseInt(match.groups.id, 10), 686 label: match.groups.label, 687 inputs: inputs 688 }; 689 if (match.groups.blocks) { 690 const nodeIdsString = match.groups.blocks.replace(/\s/g, '').replace(/B/g, ''); 691 const nodeIdStrings = nodeIdsString.split(','); 692 const successors = nodeIdStrings.map(n => Number.parseInt(n, 10)); 693 state.currentBlock.succ = successors; 694 } 695 state.nodes[node.id] = node; 696 state.currentBlock.nodes.push(node); 697 } 698 function createBlock(state, match) { 699 let predecessors = []; 700 if (match.groups.in) { 701 const blockIdsString = match.groups.in.replace(/\s/g, '').replace(/B/g, ''); 702 const blockIdStrings = blockIdsString.split(','); 703 predecessors = blockIdStrings.map(n => Number.parseInt(n, 10)); 704 } 705 const block = { 706 id: Number.parseInt(match.groups.id, 10), 707 isDeferred: match.groups.deferred != undefined, 708 pred: predecessors.sort(), 709 succ: [], 710 nodes: [] 711 }; 712 state.blocks[block.id] = block; 713 state.currentBlock = block; 714 } 715 function setGotoSuccessor(state, match) { 716 state.currentBlock.succ = [Number.parseInt(match.groups.successor.replace(/\s/g, ''), 10)]; 717 } 718 const rules = [ 719 { 720 lineRegexps: 721 [/^\s*(?<id>\d+):\ (?<label>.*)\((?<args>.*)\)$/, 722 /^\s*(?<id>\d+):\ (?<label>.*)\((?<args>.*)\)\ ->\ (?<blocks>.*)$/, 723 /^\s*(?<id>\d+):\ (?<label>.*)$/ 724 ], 725 process: createNode 726 }, 727 { 728 lineRegexps: 729 [/^\s*---\s*BLOCK\ B(?<id>\d+)\s*(?<deferred>\(deferred\))?(\ <-\ )?(?<in>[^-]*)?\ ---$/ 730 ], 731 process: createBlock 732 }, 733 { 734 lineRegexps: 735 [/^\s*Goto\s*->\s*B(?<successor>\d+)\s*$/ 736 ], 737 process: setGotoSuccessor 738 } 739 ]; 740 741 const lines = phase.data.split(/[\n]/); 742 const state = { currentBlock: undefined, blocks: [], nodes: [] }; 743 744 nextLine: 745 for (const line of lines) { 746 for (const rule of rules) { 747 for (const lineRegexp of rule.lineRegexps) { 748 const match = line.match(lineRegexp); 749 if (match) { 750 rule.process(state, match); 751 continue nextLine; 752 } 753 } 754 } 755 console.log("Warning: unmatched schedule line \"" + line + "\""); 756 } 757 phase.schedule = state; 758 return phase; 759 } 760 761 parseSequence(phase) { 762 phase.sequence = { blocks: phase.blocks, 763 register_allocation: phase.register_allocation ? new RegisterAllocation(phase.register_allocation) 764 : undefined }; 765 return phase; 766 } 767} 768