| 1 | /** |
| 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. |
| 3 | * |
| 4 | * This source code is licensed under the MIT license found in the |
| 5 | * LICENSE file in the root directory of this source tree. |
| 6 | */ |
| 7 | |
| 8 | import {CompilerError} from '../CompilerError'; |
| 9 | import { |
| 10 | GeneratedSource, |
| 11 | HIRFunction, |
| 12 | Identifier, |
| 13 | IdentifierId, |
| 14 | SourceLocation, |
| 15 | } from './HIR'; |
| 16 | import {printPlace} from './PrintHIR'; |
| 17 | import { |
| 18 | eachInstructionLValue, |
| 19 | eachInstructionValueOperand, |
| 20 | eachTerminalOperand, |
| 21 | } from './visitors'; |
| 22 | |
| 23 | /* |
| 24 | * Validation pass to check that there is a 1:1 mapping between Identifier objects and IdentifierIds, |
| 25 | * ie there can only be one Identifier instance per IdentifierId. |
| 26 | */ |
| 27 | export function assertConsistentIdentifiers(fn: HIRFunction): void { |
| 28 | const identifiers: Identifiers = new Map(); |
| 29 | const assignments: Set<IdentifierId> = new Set(); |
| 30 | for (const [, block] of fn.body.blocks) { |
| 31 | for (const phi of block.phis) { |
| 32 | validate(identifiers, phi.place.identifier); |
| 33 | for (const [, operand] of phi.operands) { |
| 34 | validate(identifiers, operand.identifier); |
| 35 | } |
| 36 | } |
| 37 | for (const instr of block.instructions) { |
| 38 | CompilerError.invariant(instr.lvalue.identifier.name === null, { |
| 39 | reason: `Expected all lvalues to be temporaries`, |
| 40 | description: `Found named lvalue \`${instr.lvalue.identifier.name}\``, |
| 41 | loc: instr.lvalue.loc, |
| 42 | }); |
| 43 | CompilerError.invariant(!assignments.has(instr.lvalue.identifier.id), { |
| 44 | reason: `Expected lvalues to be assigned exactly once`, |
| 45 | description: `Found duplicate assignment of '${printPlace( |
| 46 | instr.lvalue, |
| 47 | )}'`, |
| 48 | loc: instr.lvalue.loc, |
| 49 | }); |
| 50 | assignments.add(instr.lvalue.identifier.id); |
| 51 | for (const operand of eachInstructionLValue(instr)) { |
| 52 | validate(identifiers, operand.identifier, operand.loc); |
| 53 | } |
| 54 | for (const operand of eachInstructionValueOperand(instr.value)) { |
| 55 | validate(identifiers, operand.identifier, operand.loc); |
| 56 | } |
| 57 | } |
| 58 | for (const operand of eachTerminalOperand(block.terminal)) { |
| 59 | validate(identifiers, operand.identifier, operand.loc); |
| 60 | } |
| 61 | } |
| 62 | } |
| 63 | |
| 64 | type Identifiers = Map<IdentifierId, Identifier>; |
| 65 | |
| 66 | function validate( |
| 67 | identifiers: Identifiers, |
| 68 | identifier: Identifier, |
| 69 | loc: SourceLocation | null = null, |
| 70 | ): void { |
| 71 | const previous = identifiers.get(identifier.id); |
| 72 | if (previous === undefined) { |
| 73 | identifiers.set(identifier.id, identifier); |
| 74 | } else { |
| 75 | CompilerError.invariant(identifier === previous, { |
| 76 | reason: `Duplicate identifier object`, |
| 77 | description: `Found duplicate identifier object for id ${identifier.id}`, |
| 78 | loc: loc ?? GeneratedSource, |
| 79 | }); |
| 80 | } |
| 81 | } |