aboutsummaryrefslogtreecommitdiff
path: root/backend/lib/CodeGen/MachineVerifier.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'backend/lib/CodeGen/MachineVerifier.cpp')
-rw-r--r--backend/lib/CodeGen/MachineVerifier.cpp987
1 files changed, 987 insertions, 0 deletions
diff --git a/backend/lib/CodeGen/MachineVerifier.cpp b/backend/lib/CodeGen/MachineVerifier.cpp
new file mode 100644
index 0000000..085d7ac
--- /dev/null
+++ b/backend/lib/CodeGen/MachineVerifier.cpp
@@ -0,0 +1,987 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "rcc/CodeGen/MachineVerifier.h"
+
+#include "rcc/CodeGen/GenericOpcodes.h"
+#include "rcc/CodeGen/LiveVariables.h"
+#include "rcc/CodeGen/MachineBasicBlock.h"
+#include "rcc/CodeGen/MachineFrameInfo.h"
+#include "rcc/CodeGen/MachineFunction.h"
+#include "rcc/CodeGen/MachineFunctionProperties.h"
+#include "rcc/CodeGen/MachineInstr.h"
+#include "rcc/CodeGen/MachineMemOperand.h"
+#include "rcc/CodeGen/MachineMetadata.h"
+#include "rcc/CodeGen/MachineOperand.h"
+#include "rcc/CodeGen/MachineRegisterInfo.h"
+
+#include <algorithm>
+#include <array>
+#include <cstddef>
+#include <cstdint>
+#include <optional>
+#include <ranges>
+#include <string>
+#include <string_view>
+#include <unordered_map>
+#include <unordered_set>
+#include <utility>
+
+namespace rcc {
+namespace {
+
+[[nodiscard]] std::string formatDebugInstruction(const MachineInstr &Instruction,
+ const TargetDescription &Target) {
+ const InstructionDescriptor *Descriptor = Target.instructionDescriptor(Instruction.opcode());
+ std::string Result = Descriptor == nullptr ? "<unknown>" : Descriptor->Name;
+ for (std::size_t Index = 0; Index < Instruction.operandCount(); ++Index) {
+ if (Index == 0)
+ Result.push_back(' ');
+ else
+ Result.append(", ");
+ const MachineOperand &Operand = Instruction.operands()[Index];
+ if (const auto *Immediate = Operand.getIf<std::int64_t>()) {
+ Result.append(std::to_string(*Immediate));
+ continue;
+ }
+ if (const auto *RegisterValue = Operand.getIf<Register>()) {
+ if (RegisterValue->isNoRegister()) {
+ Result.append("$noreg");
+ continue;
+ }
+ if (RegisterValue->isVirtual()) {
+ Result.append(RegisterValue->toString());
+ continue;
+ }
+ const auto Physical = RegisterValue->physicalRegister();
+ const PhysicalRegisterDesc *Register =
+ Physical ? Target.registers().physicalRegister(*Physical) : nullptr;
+ Result.push_back('$');
+ Result.append(Register == nullptr ? RegisterValue->toString() : Register->Name);
+ continue;
+ }
+ Result.append("<operand>");
+ }
+ return Result;
+}
+
+class FunctionVerifier final {
+public:
+ FunctionVerifier(const MachineFunction &Function, const TargetDescription &Target,
+ MachineVerifierOptions Options) noexcept
+ : Function(Function), Target(Target), Options(Options) {}
+
+ [[nodiscard]] Expected<void> run() {
+ if (Function.properties().has(MachineFunctionProperty::FailedISel))
+ return {};
+ if (!Function.stableID().isValid())
+ return fail("function identity is invalid");
+
+ auto AttributeResult = Function.attributes().verify();
+ if (!AttributeResult)
+ return failWith("function attributes", AttributeResult.error());
+ auto MetadataResult = Function.metadata().verify();
+ if (!MetadataResult)
+ return failWith("machine metadata", MetadataResult.error());
+ auto CFIResult = Function.cfiInfo().verify();
+ if (!CFIResult)
+ return failWith("CFI information", CFIResult.error());
+ auto TargetCFIResult = Target.validateCFIInfo(Function.cfiInfo());
+ if (!TargetCFIResult)
+ return failWith("target CFI information", TargetCFIResult.error());
+ if (Function.targetState().hasValue()) {
+ auto TargetStateResult = Function.targetState().verify();
+ if (!TargetStateResult)
+ return failWith("target function state", TargetStateResult.error());
+ const auto RequiredType = Target.mirText().functionStateType();
+ if (RequiredType && Function.targetState().typeID() != RequiredType) {
+ return fail("target function state does not match the target MIR schema");
+ }
+ }
+ auto CatalogResult = catalogArenaObjects();
+ if (!CatalogResult)
+ return CatalogResult;
+ auto FrameResult = Function.frameInfo().verify();
+ if (!FrameResult)
+ return failWith("frame info", FrameResult.error());
+ auto TargetFrameResult = Target.validateFrameInfo(Function.frameInfo());
+ if (!TargetFrameResult)
+ return failWith("target frame info", TargetFrameResult.error());
+ auto ConstantPoolResult = Function.constantPool().verify();
+ if (!ConstantPoolResult)
+ return failWith("constant pool", ConstantPoolResult.error());
+ auto LayoutResult = verifyBlockLayout();
+ if (!LayoutResult)
+ return LayoutResult;
+ auto RecordResult = verifyFunctionRecords();
+ if (!RecordResult)
+ return RecordResult;
+ auto CompilationStatusResult = verifyCompilationStatus();
+ if (!CompilationStatusResult)
+ return CompilationStatusResult;
+ auto JumpTableResult = Function.jumpTableInfo().verify();
+ if (!JumpTableResult)
+ return failWith("jump table info", JumpTableResult.error());
+ auto FrameLifetimeResult = verifyFrameLifetimes();
+ if (!FrameLifetimeResult)
+ return FrameLifetimeResult;
+ auto CFGResult = verifyCFG();
+ if (!CFGResult)
+ return CFGResult;
+ auto PHIResult = verifyPHIs();
+ if (!PHIResult)
+ return PHIResult;
+ auto RegisterResult = verifyRegisterInfo();
+ if (!RegisterResult)
+ return RegisterResult;
+ auto DefUseResult = Function.registerInfo().verifyDefUseChains();
+ if (!DefUseResult)
+ return failWith("register info", DefUseResult.error());
+ if (Options.VerifyProperties) {
+ auto PropertyResult = verifyProperties();
+ if (!PropertyResult)
+ return PropertyResult;
+ }
+ return {};
+ }
+
+private:
+ [[nodiscard]] Expected<void> fail(std::string Message) const {
+ return makeUnexpected(ErrorCode::VerificationError, "machine function '" +
+ std::string(Function.name()) +
+ "': " + std::move(Message));
+ }
+
+ [[nodiscard]] Expected<void> failWith(std::string_view Context, const Error &Nested) const {
+ return fail(std::string(Context) + ": " + std::string(Nested.message()));
+ }
+
+ [[nodiscard]] static bool hasMatchingStorage(const MachineOperand &Operand) noexcept {
+ switch (Operand.kind()) {
+ case MachineOperand::Kind::Register:
+ return Operand.getIf<Register>() != nullptr;
+ case MachineOperand::Kind::Immediate:
+ return Operand.getIf<std::int64_t>() != nullptr;
+ case MachineOperand::Kind::IntegerImmediate:
+ return Operand.getIf<IntegerImmediateOperand>() != nullptr;
+ case MachineOperand::Kind::FloatingImmediate:
+ return Operand.getIf<FloatingImmediateOperand>() != nullptr;
+ case MachineOperand::Kind::BasicBlock:
+ return Operand.getIf<BasicBlockOperand>() != nullptr;
+ case MachineOperand::Kind::FrameIndex:
+ return Operand.getIf<FrameIndexOperand>() != nullptr;
+ case MachineOperand::Kind::ConstantPoolIndex:
+ return Operand.getIf<ConstantPoolOperand>() != nullptr;
+ case MachineOperand::Kind::JumpTableIndex:
+ return Operand.getIf<JumpTableOperand>() != nullptr;
+ case MachineOperand::Kind::GlobalSymbol:
+ return Operand.getIf<GlobalSymbolOperand>() != nullptr;
+ case MachineOperand::Kind::ExternalSymbol:
+ return Operand.getIf<ExternalSymbolOperand>() != nullptr;
+ case MachineOperand::Kind::BlockAddress:
+ return Operand.getIf<BlockAddressOperand>() != nullptr;
+ case MachineOperand::Kind::RegisterMask:
+ return Operand.getIf<RegisterMaskOperand>() != nullptr;
+ case MachineOperand::Kind::LiveOutRegisterMask:
+ return Operand.getIf<LiveOutRegisterMaskOperand>() != nullptr;
+ case MachineOperand::Kind::Metadata:
+ return Operand.getIf<MetadataOperand>() != nullptr;
+ case MachineOperand::Kind::TargetIndex:
+ return Operand.getIf<TargetIndexOperand>() != nullptr;
+ case MachineOperand::Kind::MCSymbol:
+ return Operand.getIf<MCSymbolOperand>() != nullptr;
+ case MachineOperand::Kind::CFIIndex:
+ return Operand.getIf<CFIIndexOperand>() != nullptr;
+ case MachineOperand::Kind::Intrinsic:
+ return Operand.getIf<IntrinsicOperand>() != nullptr;
+ case MachineOperand::Kind::Predicate:
+ return Operand.getIf<PredicateOperand>() != nullptr;
+ case MachineOperand::Kind::ShuffleMask:
+ return Operand.getIf<ShuffleMaskOperand>() != nullptr;
+ case MachineOperand::Kind::DebugInstrRef:
+ return Operand.getIf<DebugInstrRefOperand>() != nullptr;
+ case MachineOperand::Kind::LaneMask:
+ return Operand.getIf<LaneMaskOperand>() != nullptr;
+ case MachineOperand::Kind::Count:
+ return false;
+ }
+ return false;
+ }
+
+ [[nodiscard]] Expected<void> catalogArenaObjects() {
+ std::unordered_set<MachineBlockStableID> BlockIDs;
+ for (const MachineBasicBlock *Block : Function.allBlocks()) {
+ if (Block == nullptr || !RegisteredBlocks.insert(Block).second)
+ return fail("arena block registry contains a null or duplicate pointer");
+ if (&Block->function() != &Function)
+ return fail("arena block registry contains a foreign block");
+ if (!Block->stableID().isValid() || !BlockIDs.insert(Block->stableID()).second) {
+ return fail("arena block registry contains an invalid or duplicate StableID");
+ }
+ auto AttributeResult = Block->attributes().verify();
+ if (!AttributeResult)
+ return failWith("basic block attributes", AttributeResult.error());
+ }
+
+ std::unordered_set<MachineInstrStableID> InstructionIDs;
+ for (const MachineInstr *Instruction : Function.allInstructions()) {
+ if (Instruction == nullptr || !RegisteredInstructions.insert(Instruction).second) {
+ return fail("arena instruction registry contains a null or duplicate pointer");
+ }
+ if (&Instruction->function() != &Function)
+ return fail("arena instruction registry contains a foreign instruction");
+ if (!Instruction->stableID().isValid() ||
+ !InstructionIDs.insert(Instruction->stableID()).second) {
+ return fail("arena instruction registry contains an invalid or duplicate StableID");
+ }
+ }
+
+ for (const MachineMemOperand *MemOperand : Function.allMemOperands()) {
+ if (MemOperand == nullptr || !RegisteredMemOperands.insert(MemOperand).second) {
+ return fail("arena MMO registry contains a null or duplicate pointer");
+ }
+ }
+ return {};
+ }
+
+ [[nodiscard]] Expected<void> verifyBlockLayout() {
+ const MachineBasicBlock *Previous = nullptr;
+ const MachineBasicBlock *Block = Function.firstBlock();
+ std::uint32_t LayoutIndex = 0;
+ while (Block != nullptr) {
+ if (!RegisteredBlocks.contains(Block))
+ return fail("block layout references an unregistered block");
+ if (!LinkedBlocks.insert(Block).second)
+ return fail("block layout contains a cycle or duplicate block");
+ if (!Block->isLinked() || Block->previous() != Previous)
+ return fail("block layout ownership or previous link is inconsistent");
+ LinkedBlockIDs.insert(Block->stableID());
+ if (Block->denseNumber() != LayoutIndex || Block->layoutIndex() != LayoutIndex) {
+ return fail("block dense number or layout index is stale");
+ }
+ if (const auto &ProfileID = Block->attributes().profileID()) {
+ const std::uint64_t Key = (static_cast<std::uint64_t>(ProfileID->BaseID) << 32U) |
+ static_cast<std::uint64_t>(ProfileID->CloneID);
+ if (!LinkedBlockProfileIDs.insert(Key).second)
+ return fail("multiple linked blocks have the same bb_id");
+ }
+
+ auto InstructionResult = verifyInstructionLayout(*Block);
+ if (!InstructionResult)
+ return InstructionResult;
+ Previous = Block;
+ Block = Block->next();
+ ++LayoutIndex;
+ }
+
+ if (Previous != Function.lastBlock())
+ return fail("function last-block pointer is inconsistent");
+ if ((Function.firstBlock() == nullptr) != (Function.lastBlock() == nullptr)) {
+ return fail("function has only one of its first/last block pointers");
+ }
+
+ for (const MachineBasicBlock *Registered : RegisteredBlocks) {
+ if (LinkedBlocks.contains(Registered)) {
+ if (!Registered->isLinked())
+ return fail("reachable block is not marked linked");
+ continue;
+ }
+ if (Registered->isLinked())
+ return fail("unreachable arena block is marked linked");
+ if (Registered->previous() != nullptr || Registered->next() != nullptr ||
+ Registered->firstInstruction() != nullptr || Registered->lastInstruction() != nullptr ||
+ !Registered->predecessors().empty() || !Registered->successors().empty() ||
+ Registered->denseNumber() || Registered->layoutIndex()) {
+ return fail("detached block retains layout, instruction, or CFG state");
+ }
+ }
+
+ for (const MachineInstr *Instruction : RegisteredInstructions) {
+ auto LocalResult = verifyInstructionLocalState(*Instruction);
+ if (!LocalResult)
+ return LocalResult;
+ if (Instruction->parent() != nullptr) {
+ if (!LinkedInstructions.contains(Instruction))
+ return fail("instruction has a parent but is absent from block layout");
+ } else {
+ if (Instruction->previous() != nullptr || Instruction->next() != nullptr) {
+ return fail("detached instruction retains layout links");
+ }
+ for (const MachineOperand &Operand : Instruction->operands()) {
+ if (Function.registerInfo().isLinkedOperand(Operand))
+ return fail("detached instruction operand remains in a def-use chain");
+ }
+ }
+ }
+ return {};
+ }
+
+ [[nodiscard]] Expected<void> verifyInstructionLayout(const MachineBasicBlock &Block) {
+ const MachineInstr *Previous = nullptr;
+ const MachineInstr *Instruction = Block.firstInstruction();
+ while (Instruction != nullptr) {
+ if (!RegisteredInstructions.contains(Instruction))
+ return fail("block layout references an unregistered instruction");
+ if (!LinkedInstructions.insert(Instruction).second)
+ return fail("instruction layout contains a cycle or duplicate instruction");
+ LinkedInstructionIDs.insert(Instruction->stableID());
+ LinkedInstructionsByID.emplace(Instruction->stableID(), Instruction);
+ if (Instruction->parent() != &Block || Instruction->previous() != Previous) {
+ return fail("instruction parent or previous link is inconsistent");
+ }
+
+ auto LiveResult = verifyLiveInstruction(*Instruction);
+ if (!LiveResult)
+ return LiveResult;
+ Previous = Instruction;
+ Instruction = Instruction->next();
+ }
+ if (Previous != Block.lastInstruction())
+ return fail("block last-instruction pointer is inconsistent");
+ if ((Block.firstInstruction() == nullptr) != (Block.lastInstruction() == nullptr)) {
+ return fail("block has only one of its first/last instruction pointers");
+ }
+ return {};
+ }
+
+ [[nodiscard]] Expected<void> verifyInstructionLocalState(const MachineInstr &Instruction) {
+ if (Instruction.sourceLocation() && !Instruction.sourceLocation()->isValid()) {
+ return fail("instruction has an invalid source location");
+ }
+ const Opcode OpcodeValue = Instruction.opcode();
+ const bool IsDebugValue = OpcodeValue == opcode(GenericOpcode::DBG_VALUE) ||
+ OpcodeValue == opcode(GenericOpcode::DBG_VALUE_LIST);
+ if (IsDebugValue && Instruction.operandCount() == 4 && !Instruction.debugLocation()) {
+ return fail("Bad machine code: Missing DebugLoc for debug instruction\n"
+ "- instruction: " +
+ formatDebugInstruction(Instruction, Target));
+ }
+ if (Instruction.debugLocation()) {
+ auto DebugLocationResult = Instruction.debugLocation()->verify(Function.metadata());
+ if (!DebugLocationResult)
+ return failWith("instruction debug location", DebugLocationResult.error());
+ }
+ const auto VerifyAttachment = [this](const std::optional<MachineMetadataReference> &Reference,
+ std::string_view Name) -> Expected<void> {
+ if (!Reference)
+ return {};
+ auto Verification = verifyMachineMetadataReference(*Reference, Function.metadata());
+ if (!Verification)
+ return failWith(Name, Verification.error());
+ return {};
+ };
+ if (auto Verification =
+ VerifyAttachment(Instruction.heapAllocMarker(), "instruction heap-allocation marker");
+ !Verification) {
+ return Verification;
+ }
+ if (auto Verification =
+ VerifyAttachment(Instruction.pcSections(), "instruction PC sections metadata");
+ !Verification) {
+ return Verification;
+ }
+ if (auto Verification = VerifyAttachment(Instruction.mmra(), "instruction MMRA metadata");
+ !Verification) {
+ return Verification;
+ }
+ if (Instruction.origin() && Instruction.origin()->value() == 0)
+ return fail("instruction has an invalid origin ID");
+
+ for (const MachineOperand &Operand : Instruction.operands()) {
+ if (Operand.parent() != &Instruction || !hasMatchingStorage(Operand))
+ return fail("instruction operand parent or tagged storage is inconsistent");
+ if (const auto *Mask = Operand.getIf<RegisterMaskOperand>(); Mask != nullptr && !Mask->Mask) {
+ return fail("register-mask operand contains a null mask");
+ }
+ if (const auto *Mask = Operand.getIf<LiveOutRegisterMaskOperand>();
+ Mask != nullptr && !Mask->Mask) {
+ return fail("live-out register-mask operand contains a null mask");
+ }
+ if (const auto *Integer = Operand.getIf<IntegerImmediateOperand>(); Integer != nullptr) {
+ auto IntegerResult = Integer->Value.verify();
+ if (!IntegerResult)
+ return failWith("integer immediate operand", IntegerResult.error());
+ }
+ if (const auto *Global = Operand.getIf<GlobalSymbolOperand>();
+ Global != nullptr && Global->Name.empty()) {
+ return fail("global symbol operand has an empty name");
+ }
+ if (const auto *Address = Operand.getIf<BlockAddressOperand>();
+ Address != nullptr && (Address->FunctionName.empty() || Address->BlockName.empty())) {
+ return fail("block-address operand has an incomplete name");
+ }
+ if (const auto *Metadata = Operand.getIf<MetadataOperand>(); Metadata != nullptr) {
+ auto MetadataResult = Metadata->Value.verify(Function.metadata());
+ if (!MetadataResult)
+ return failWith("metadata operand", MetadataResult.error());
+ }
+ if (const auto *Symbol = Operand.getIf<MCSymbolOperand>();
+ Symbol != nullptr && Symbol->Name.empty()) {
+ return fail("MC symbol operand has an empty name");
+ }
+ if (const auto *Intrinsic = Operand.getIf<IntrinsicOperand>();
+ Intrinsic != nullptr && Intrinsic->ID.value() == 0) {
+ return fail("intrinsic operand has an invalid ID");
+ }
+ if (const auto *Predicate = Operand.getIf<PredicateOperand>(); Predicate != nullptr) {
+ auto PredicateResult = Predicate->verify();
+ if (!PredicateResult)
+ return failWith("predicate operand", PredicateResult.error());
+ }
+ if (const auto *Shuffle = Operand.getIf<ShuffleMaskOperand>();
+ Shuffle != nullptr && Shuffle->Elements.size() < 2) {
+ return fail("shuffle-mask operand has fewer than two elements");
+ }
+ }
+
+ for (const MachineMemOperand *MemOperand : Instruction.memOperands()) {
+ if (MemOperand == nullptr || !RegisteredMemOperands.contains(MemOperand)) {
+ return fail("instruction references a foreign or null MMO");
+ }
+ const MachineMemOperandDesc &Description = MemOperand->description();
+ const std::array MetadataReferences{
+ Description.TBAA, Description.AliasScope,
+ Description.NoAlias, Description.NoAliasAddressSpace,
+ Description.RangeMetadata,
+ };
+ for (const auto &Reference : MetadataReferences) {
+ if (!Reference)
+ continue;
+ auto Verification = verifyMachineMetadataReference(*Reference, Function.metadata());
+ if (!Verification)
+ return failWith("machine memory operand metadata", Verification.error());
+ }
+ }
+ return {};
+ }
+
+ [[nodiscard]] Expected<void> verifyFrameIndex(FrameIndex Index) const {
+ const StackObject *Object = Function.frameInfo().object(Index);
+ const StackObjectDesc *Description = Object == nullptr ? nullptr : Object->description();
+ if (Description == nullptr || Description->IsDead)
+ return fail("live instruction references an unknown or dead stack object");
+ return {};
+ }
+
+ [[nodiscard]] Expected<void> verifyConstantPoolIndex(ConstantPoolIndex Index) const {
+ if (Function.constantPool().entry(Index) == nullptr)
+ return fail("live instruction references an unknown constant pool entry");
+ return {};
+ }
+
+ [[nodiscard]] Expected<void> verifyJumpTableIndex(JumpTableIndex Index) const {
+ if (Function.jumpTableInfo().table(Index) == nullptr)
+ return fail("live instruction references an unknown jump table");
+ return {};
+ }
+
+ [[nodiscard]] Expected<void> verifyFrameLifetimes() const {
+ auto VerifyPoints = [this](std::span<const MachineFrameSaveRestorePoint> Points,
+ std::string_view Kind) -> Expected<void> {
+ for (const MachineFrameSaveRestorePoint &Point : Points) {
+ if (!LinkedBlockIDs.contains(Point.Block)) {
+ return fail(std::string("frame ") + std::string(Kind) +
+ " point references a detached or unknown block");
+ }
+ for (const PhysicalRegister Reg : Point.Registers) {
+ if (!Target.registers().isKnownPhysicalRegister(Reg)) {
+ return fail(std::string("frame ") + std::string(Kind) +
+ " point references an unknown physical register");
+ }
+ }
+ }
+ return {};
+ };
+ auto SaveResult = VerifyPoints(Function.frameInfo().state().SavePoints, "save");
+ if (!SaveResult)
+ return SaveResult;
+ auto RestoreResult = VerifyPoints(Function.frameInfo().state().RestorePoints, "restore");
+ if (!RestoreResult)
+ return RestoreResult;
+
+ for (const StackObject &Object : Function.frameInfo().objects()) {
+ const StackObjectDesc *Description = Object.description();
+ if (Description == nullptr)
+ continue;
+ for (const StackObjectLifetimeRange &Range : Description->Lifetime) {
+ if (!LinkedInstructionIDs.contains(Range.Begin) ||
+ !LinkedInstructionIDs.contains(Range.End)) {
+ return fail("stack object lifetime references a detached or unknown instruction");
+ }
+ }
+ if (Description->DebugInfo) {
+ auto DebugInfoResult = Description->DebugInfo->verify(Function.metadata());
+ if (!DebugInfoResult)
+ return failWith("stack object debug information", DebugInfoResult.error());
+ }
+ if (Description->CalleeSavedRegister &&
+ !Target.registers().isKnownPhysicalRegister(*Description->CalleeSavedRegister)) {
+ return fail("callee-saved stack object references an unknown physical register");
+ }
+ }
+ return {};
+ }
+
+ [[nodiscard]] Expected<void> verifyFunctionRecords() const {
+ const MachineFunctionRecords &Records = Function.records();
+ auto Verification = Records.verify();
+ if (!Verification)
+ return failWith("function records", Verification.error());
+
+ for (const PhysicalRegister Reg : Records.calleeSavedRegisters()) {
+ if (!Target.registers().isKnownPhysicalRegister(Reg))
+ return fail("callee-saved register list references an unknown physical register");
+ }
+ for (const MachineEntryValueInfo &EntryValue : Records.entryValues()) {
+ if (!Target.registers().isKnownPhysicalRegister(EntryValue.Reg)) {
+ return fail("entry-value record references an unknown physical register");
+ }
+ auto DebugInfoResult = EntryValue.DebugInfo.verify(Function.metadata());
+ if (!DebugInfoResult)
+ return failWith("entry-value debug information", DebugInfoResult.error());
+ }
+
+ auto VerifyCall = [this](MachineInstrStableID ID,
+ std::string_view Kind) -> Expected<const MachineInstr *> {
+ const auto Position = LinkedInstructionsByID.find(ID);
+ if (Position == LinkedInstructionsByID.end()) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "machine function '" + std::string(Function.name()) +
+ "': " + std::string(Kind) +
+ " references a detached or unknown instruction");
+ }
+ const InstructionDescriptor *Descriptor =
+ Target.instructionDescriptor(Position->second->opcode());
+ if (Descriptor == nullptr || !Descriptor->Flags.has(InstructionFlag::Call)) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "machine function '" + std::string(Function.name()) +
+ "': " + std::string(Kind) + " references a non-call instruction");
+ }
+ return Position->second;
+ };
+
+ for (const MachineCallSiteInfo &CallSite : Records.callSites()) {
+ auto Call = VerifyCall(CallSite.CallInstruction, "call-site record");
+ if (!Call)
+ return std::unexpected(Call.error());
+ for (const MachineCallSiteArgument &Argument : CallSite.ForwardedArguments) {
+ if (!Target.registers().isKnownPhysicalRegister(Argument.Reg)) {
+ return fail("call-site record references an unknown forwarded physical register");
+ }
+ }
+ }
+
+ for (const MachineCalledGlobal &CalledGlobal : Records.calledGlobals()) {
+ auto Call = VerifyCall(CalledGlobal.CallInstruction, "called-global record");
+ if (!Call)
+ return std::unexpected(Call.error());
+ }
+
+ if (!Records.debugValueSubstitutions().empty() &&
+ !Function.attributes().has(MachineFunctionAttribute::DebugInstrRef)) {
+ return fail("debug value substitutions require DebugInstrRef");
+ }
+ for (const MachineDebugValueSubstitution &Substitution : Records.debugValueSubstitutions()) {
+ if (Substitution.Subregister.value() != 0 &&
+ Target.registers().subRegIndex(Substitution.Subregister) == nullptr) {
+ return fail("debug value substitution references an unknown subregister index");
+ }
+ }
+ return {};
+ }
+
+ [[nodiscard]] Expected<void> verifyCompilationStatus() const {
+ const MachineCompilationStatus &Status = Function.compilationStatus();
+ auto Verification = Status.verify();
+ if (!Verification)
+ return failWith("compilation status", Verification.error());
+
+ const MachineCompilationFailure *Failure = Status.failure();
+ if (Failure != nullptr && Failure->FailureInstruction &&
+ !LinkedInstructionIDs.contains(*Failure->FailureInstruction)) {
+ return fail("compilation failure references a detached or unknown instruction");
+ }
+ return {};
+ }
+
+ [[nodiscard]] Expected<void> verifyLiveInstruction(const MachineInstr &Instruction) {
+ auto LocalResult = verifyInstructionLocalState(Instruction);
+ if (!LocalResult)
+ return LocalResult;
+
+ const InstructionDescriptor *Descriptor = Target.instructionDescriptor(Instruction.opcode());
+ if (Descriptor == nullptr)
+ return fail("live instruction uses an unregistered opcode");
+ if (Instruction.debugInstrNumber() != 0) {
+ if (Descriptor->Flags.has(InstructionFlag::Meta)) {
+ return fail("metadata instruction has a debug value tracking number");
+ }
+ if (!DebugInstructionNumbers.insert(Instruction.debugInstrNumber()).second) {
+ return fail("multiple instructions have the same debug value tracking number");
+ }
+ }
+ auto DescriptorResult = Target.validateInstruction(Instruction, Function.registerInfo());
+ if (!DescriptorResult)
+ return failWith("instruction descriptor", DescriptorResult.error());
+
+ if (Options.Stage && Descriptor->Flags.has(InstructionFlag::Pseudo) &&
+ static_cast<std::uint8_t>(*Options.Stage) >
+ static_cast<std::uint8_t>(Descriptor->Deadline)) {
+ return fail("pseudo instruction survives beyond its expansion deadline");
+ }
+
+ HasPhi |= Descriptor->Flags.has(InstructionFlag::Phi);
+ HasStageTenPseudo |= Instruction.opcode() == opcode(GenericOpcode::REG_SEQUENCE) ||
+ Instruction.opcode() == opcode(GenericOpcode::INSERT_SUBREG);
+ HasGenericInstruction |= Descriptor->Flags.has(InstructionFlag::Generic);
+ HasExecutablePseudo |= Descriptor->Flags.has(InstructionFlag::Pseudo) &&
+ !Descriptor->Flags.has(InstructionFlag::Meta) &&
+ !Descriptor->Flags.has(InstructionFlag::TerminalEmission);
+
+ const auto Operands = Instruction.operands();
+ for (std::size_t Index = 0; Index < Operands.size(); ++Index) {
+ const MachineOperand &Operand = Operands[Index];
+ if (const auto *Frame = Operand.getIf<FrameIndexOperand>(); Frame != nullptr) {
+ HasFrameIndex = true;
+ auto FrameResult = verifyFrameIndex(Frame->Index);
+ if (!FrameResult)
+ return FrameResult;
+ }
+ if (const auto *Constant = Operand.getIf<ConstantPoolOperand>(); Constant != nullptr) {
+ auto ConstantResult = verifyConstantPoolIndex(Constant->Index);
+ if (!ConstantResult)
+ return ConstantResult;
+ }
+ if (const auto *JumpTable = Operand.getIf<JumpTableOperand>(); JumpTable != nullptr) {
+ auto JumpTableResult = verifyJumpTableIndex(JumpTable->Index);
+ if (!JumpTableResult)
+ return JumpTableResult;
+ }
+ if (const auto *CFI = Operand.getIf<CFIIndexOperand>();
+ CFI != nullptr && Function.cfiInfo().instruction(CFI->Index) == nullptr) {
+ return fail("live instruction references an unknown CFI instruction");
+ }
+
+ if (const auto *Block = Operand.getIf<BasicBlockOperand>();
+ Block != nullptr &&
+ (Block->Block == nullptr || &Block->Block->function() != &Function ||
+ !RegisteredBlocks.contains(Block->Block) || !Block->Block->isLinked())) {
+ return fail("basic-block operand references a foreign or detached block");
+ }
+
+ const Register *Reg = Operand.getIf<Register>();
+ if (Reg == nullptr)
+ continue;
+ const bool ShouldBeLinked = !Reg->isNoRegister();
+ if (Function.registerInfo().isLinkedOperand(Operand) != ShouldBeLinked) {
+ return fail("live register operand has incorrect def-use linkage");
+ }
+ if (Reg->isVirtual()) {
+ if (!Function.registerInfo().isKnownVirtualRegister(*Reg))
+ return fail("instruction references an unknown virtual register");
+ if (Operand.isDef())
+ ++VirtualDefCounts[*Reg];
+ }
+
+ if (Operand.tiedOperandIndex() && Index < *Operand.tiedOperandIndex()) {
+ const MachineOperand *Other = Instruction.operandAt(*Operand.tiedOperandIndex());
+ const Register *OtherReg = Other == nullptr ? nullptr : Other->getIf<Register>();
+ if (OtherReg == nullptr || *OtherReg != *Reg)
+ HasUnrewrittenTie = true;
+ }
+ }
+ for (const MachineMemOperand *MemOperand : Instruction.memOperands()) {
+ const MemoryBase &Base = MemOperand->description().Base;
+ if (const auto Frame = Base.frameIndex()) {
+ auto FrameResult = verifyFrameIndex(*Frame);
+ if (!FrameResult)
+ return FrameResult;
+ }
+ if (const auto Constant = Base.constantPoolIndex()) {
+ auto ConstantResult = verifyConstantPoolIndex(*Constant);
+ if (!ConstantResult)
+ return ConstantResult;
+ }
+ if (const auto JumpTable = Base.jumpTableIndex()) {
+ auto JumpTableResult = verifyJumpTableIndex(*JumpTable);
+ if (!JumpTableResult)
+ return JumpTableResult;
+ }
+ }
+ return {};
+ }
+
+ [[nodiscard]] Expected<void> verifyCFG() {
+ for (const MachineBasicBlock *Block : LinkedBlocks) {
+ std::unordered_set<const MachineBasicBlock *> Successors;
+ for (const MachineSuccessor &Successor : Block->successors()) {
+ if (Successor.Block == nullptr || !LinkedBlocks.contains(Successor.Block) ||
+ !Successors.insert(Successor.Block).second) {
+ return fail("CFG successor list contains a null, detached, or duplicate block");
+ }
+ if (std::ranges::count(Successor.Block->predecessors(), Block) != 1)
+ return fail("CFG successor lacks one reciprocal predecessor");
+ }
+
+ std::unordered_set<const MachineBasicBlock *> Predecessors;
+ for (const MachineBasicBlock *Predecessor : Block->predecessors()) {
+ if (Predecessor == nullptr || !LinkedBlocks.contains(Predecessor) ||
+ !Predecessors.insert(Predecessor).second) {
+ return fail("CFG predecessor list contains a null, detached, or duplicate block");
+ }
+ const auto Matches = std::ranges::count_if(
+ Predecessor->successors(),
+ [Block](const MachineSuccessor &Successor) { return Successor.Block == Block; });
+ if (Matches != 1)
+ return fail("CFG predecessor lacks one reciprocal successor");
+ }
+
+ std::unordered_set<PhysicalRegister> LiveIns;
+ for (const MachineBasicBlockLiveIn &LiveIn : Block->liveIns()) {
+ if (!LiveIn.PhysReg.isValid() || LiveIn.Lanes.empty() ||
+ !LiveIns.insert(LiveIn.PhysReg).second) {
+ return fail("block live-in list contains invalid or duplicate entries");
+ }
+ }
+ auto TargetLiveInResult = Target.validateBlockLiveIns(*Block);
+ if (!TargetLiveInResult)
+ return failWith("block live-ins", TargetLiveInResult.error());
+ }
+ return {};
+ }
+
+ [[nodiscard]] Expected<void> verifyPHIs() {
+ for (const MachineBasicBlock *Block = Function.firstBlock(); Block != nullptr;
+ Block = Block->next()) {
+ bool SawNonMetaInstruction = false;
+ for (const MachineInstr *Instruction = Block->firstInstruction(); Instruction != nullptr;
+ Instruction = Instruction->next()) {
+ const InstructionDescriptor *Descriptor =
+ Target.instructionDescriptor(Instruction->opcode());
+ if (Descriptor == nullptr)
+ return fail("PHI verification encountered an unregistered instruction");
+
+ if (!Descriptor->Flags.has(InstructionFlag::Phi)) {
+ SawNonMetaInstruction |= !Descriptor->Flags.has(InstructionFlag::Meta);
+ continue;
+ }
+ if (SawNonMetaInstruction)
+ return fail("PHI instruction appears after a non-meta instruction");
+
+ const std::span<const MachineOperand> Operands = Instruction->operands();
+ if (Operands.size() < 3 || (Operands.size() - 1U) % 2U != 0) {
+ return fail("PHI must contain a destination and at least one incoming pair");
+ }
+ const Register *Destination = Operands.front().getIf<Register>();
+ if (Destination == nullptr || !Destination->isVirtual() || !Operands.front().isDef() ||
+ Operands.front().isUse() || Operands.front().isImplicit()) {
+ return fail("PHI destination must be an explicit virtual-register definition");
+ }
+
+ std::unordered_set<const MachineBasicBlock *> IncomingBlocks;
+ for (std::size_t Index = 1; Index < Operands.size(); Index += 2U) {
+ const MachineOperand &ValueOperand = Operands[Index];
+ const MachineOperand &BlockOperand = Operands[Index + 1U];
+ const Register *Value = ValueOperand.getIf<Register>();
+ const BasicBlockOperand *Incoming = BlockOperand.getIf<BasicBlockOperand>();
+ if (Value == nullptr || !ValueOperand.isUse() || ValueOperand.isDef() ||
+ ValueOperand.isImplicit() || Incoming == nullptr || Incoming->Block == nullptr ||
+ BlockOperand.isImplicit()) {
+ return fail("PHI incoming operands must be explicit register/block pairs");
+ }
+ if (std::ranges::find(Block->predecessors(), Incoming->Block) ==
+ Block->predecessors().end()) {
+ return fail("PHI incoming block is not a CFG predecessor");
+ }
+ if (!IncomingBlocks.insert(Incoming->Block).second)
+ return fail("PHI contains duplicate incoming blocks");
+
+ if (!Value->isNoRegister() && Destination->isVirtual() && Value->isVirtual() &&
+ Operands.front().subReg().value() == 0 && ValueOperand.subReg().value() == 0) {
+ auto DestinationInfo = Function.registerInfo().virtualRegisterInfo(*Destination);
+ auto ValueInfo = Function.registerInfo().virtualRegisterInfo(*Value);
+ if (!DestinationInfo || !ValueInfo)
+ return fail("PHI references an unknown virtual register");
+ if (DestinationInfo->get().Type && ValueInfo->get().Type &&
+ DestinationInfo->get().Type != ValueInfo->get().Type) {
+ return fail("PHI destination and incoming value types do not match");
+ }
+ }
+ auto Compatibility = Target.validateTieCompatibility(Operands.front(), ValueOperand,
+ Function.registerInfo());
+ if (!Compatibility)
+ return failWith("PHI register compatibility", Compatibility.error());
+ }
+ if (IncomingBlocks.size() != Block->predecessors().size())
+ return fail("PHI incoming blocks do not exactly cover CFG predecessors");
+ }
+ }
+ return {};
+ }
+
+ [[nodiscard]] Expected<void> verifyRegisterInfo() {
+ const MachineRegisterInfo &RegInfo = Function.registerInfo();
+ std::unordered_set<PhysicalRegister> FunctionLiveIns;
+ for (const PhysicalRegisterLiveIn &LiveIn : RegInfo.liveIns()) {
+ if (!LiveIn.PhysReg.isValid() || !FunctionLiveIns.insert(LiveIn.PhysReg).second) {
+ return fail("function live-in list contains invalid or duplicate entries");
+ }
+ if (LiveIn.InitialVirtualRegister &&
+ !RegInfo.isKnownVirtualRegister(*LiveIn.InitialVirtualRegister)) {
+ return fail("function live-in references an unknown virtual register");
+ }
+ }
+
+ std::unordered_set<PhysicalRegister> Reserved;
+ for (const PhysicalRegister Reg : RegInfo.reservedRegisters()) {
+ if (!Reg.isValid() || !Reserved.insert(Reg).second)
+ return fail("reserved-register list contains invalid or duplicate entries");
+ }
+
+ const auto VirtualRegisters = RegInfo.virtualRegisters();
+ for (std::size_t Index = 0; Index < VirtualRegisters.size(); ++Index) {
+ const VirtualRegisterInfo &Info = VirtualRegisters[Index];
+ if (Info.IsTombstone) {
+ if (Info.Type || Info.Bank || Info.RegClass || !Info.AllocationHints.empty() ||
+ Info.TargetFlags != 0) {
+ return fail("virtual-register tombstone retains semantic state");
+ }
+ continue;
+ }
+ auto Reg = Register::makeVirtual(Index);
+ if (!Reg)
+ return failWith("virtual register", Reg.error());
+ const auto Operands = RegInfo.regOperands(*Reg);
+ if (Operands.begin() != Operands.end()) {
+ HasLiveVirtualRegister = true;
+ MissingVirtualType |= !Info.Type;
+ MissingVirtualBank |= !Info.Bank;
+ MissingVirtualClass |= !Info.RegClass;
+ }
+ if (Info.Type && !Info.Type->isValid())
+ return fail("virtual register has an invalid LowLevelType");
+
+ std::unordered_set<PhysicalRegister> Hints;
+ for (const PhysicalRegister Hint : Info.AllocationHints) {
+ if (!Hint.isValid() || !Hints.insert(Hint).second)
+ return fail("virtual register has an invalid or duplicate allocation hint");
+ }
+ }
+ auto TargetResult = Target.validateRegisterInfo(RegInfo);
+ if (!TargetResult)
+ return failWith("target register info", TargetResult.error());
+ return {};
+ }
+
+ [[nodiscard]] Expected<void> verifyProperties() {
+ const MachineFunctionProperties &Properties = Function.properties();
+ auto Has = [&Properties](MachineFunctionProperty Property) { return Properties.has(Property); };
+
+ if (Has(MachineFunctionProperty::RegBankSelected) && !Has(MachineFunctionProperty::Legalized)) {
+ return fail("RegBankSelected requires Legalized");
+ }
+ if (Has(MachineFunctionProperty::Selected) && !Has(MachineFunctionProperty::RegBankSelected)) {
+ return fail("Selected requires RegBankSelected");
+ }
+ if (Has(MachineFunctionProperty::TiedOpsRewritten) && !Has(MachineFunctionProperty::Selected)) {
+ return fail("TiedOpsRewritten requires Selected");
+ }
+ if (Has(MachineFunctionProperty::TiedOpsRewritten) && !Has(MachineFunctionProperty::NoPHIs)) {
+ return fail("TiedOpsRewritten requires NoPHIs");
+ }
+ if (Has(MachineFunctionProperty::FrameFinalized) &&
+ (!Has(MachineFunctionProperty::NoVRegs) || !Has(MachineFunctionProperty::NoFrameIndices))) {
+ return fail("FrameFinalized requires NoVRegs and NoFrameIndices");
+ }
+ if (Has(MachineFunctionProperty::FrameFinalized) && !Function.frameInfo().isFrozen())
+ return fail("FrameFinalized requires a frozen frame layout");
+
+ if (Has(MachineFunctionProperty::IsSSA)) {
+ for (const auto &[Reg, DefinitionCount] : VirtualDefCounts) {
+ if (DefinitionCount > 1)
+ return fail("IsSSA function has a multiply-defined virtual register");
+ }
+ }
+ if (Has(MachineFunctionProperty::NoPHIs) && HasPhi)
+ return fail("NoPHIs function contains a PHI instruction");
+ if (Has(MachineFunctionProperty::NoVRegs) && HasLiveVirtualRegister) {
+ return fail("NoVRegs function retains a live virtual register");
+ }
+ if (Has(MachineFunctionProperty::NoFrameIndices) && HasFrameIndex)
+ return fail("NoFrameIndices function contains a frame-index operand");
+ if (Has(MachineFunctionProperty::NoExecutablePseudos) && HasExecutablePseudo) {
+ return fail("NoExecutablePseudos function contains an executable pseudo");
+ }
+ if (Has(MachineFunctionProperty::RegBankSelected) && MissingVirtualBank) {
+ return fail("RegBankSelected function has a virtual register without a bank");
+ }
+ if (Has(MachineFunctionProperty::Selected) && (HasGenericInstruction || MissingVirtualClass)) {
+ return fail("Selected function contains generic code or an unclassified virtual register");
+ }
+ if (Has(MachineFunctionProperty::TiedOpsRewritten) && HasUnrewrittenTie) {
+ return fail("TiedOpsRewritten function has unequal tied registers");
+ }
+ if (Has(MachineFunctionProperty::TiedOpsRewritten) && HasStageTenPseudo) {
+ return fail("TiedOpsRewritten function contains a Stage 10 pseudo");
+ }
+ if (Has(MachineFunctionProperty::TracksLiveness)) {
+ auto Liveness = verifyTrackedMachineLiveness(Function, Target);
+ if (!Liveness)
+ return failWith("tracked machine liveness", Liveness.error());
+ }
+
+ const bool NeedsTypes = Options.Mode == MachineVerifierMode::PreLegalizer ||
+ (!Has(MachineFunctionProperty::Selected) &&
+ (Has(MachineFunctionProperty::Legalized) ||
+ Has(MachineFunctionProperty::RegBankSelected)));
+ if (NeedsTypes && MissingVirtualType)
+ return fail("typed pipeline stage has a virtual register without LowLevelType");
+
+ if (Options.Mode == MachineVerifierMode::PreLegalizer) {
+ if (!Has(MachineFunctionProperty::IsSSA) || !Has(MachineFunctionProperty::TracksLiveness)) {
+ return fail("pre-legalizer input requires IsSSA and TracksLiveness");
+ }
+ if (Has(MachineFunctionProperty::Legalized) ||
+ Has(MachineFunctionProperty::RegBankSelected) || Has(MachineFunctionProperty::Selected) ||
+ Has(MachineFunctionProperty::TiedOpsRewritten) || Has(MachineFunctionProperty::NoVRegs) ||
+ Has(MachineFunctionProperty::FrameFinalized)) {
+ return fail("pre-legalizer input carries a later-stage property");
+ }
+ }
+ return {};
+ }
+
+ const MachineFunction &Function;
+ const TargetDescription &Target;
+ MachineVerifierOptions Options;
+ std::unordered_set<const MachineBasicBlock *> RegisteredBlocks;
+ std::unordered_set<const MachineBasicBlock *> LinkedBlocks;
+ std::unordered_set<MachineBlockStableID> LinkedBlockIDs;
+ std::unordered_set<const MachineInstr *> RegisteredInstructions;
+ std::unordered_set<const MachineInstr *> LinkedInstructions;
+ std::unordered_set<MachineInstrStableID> LinkedInstructionIDs;
+ std::unordered_map<MachineInstrStableID, const MachineInstr *> LinkedInstructionsByID;
+ std::unordered_set<std::uint64_t> LinkedBlockProfileIDs;
+ std::unordered_set<std::uint32_t> DebugInstructionNumbers;
+ std::unordered_set<const MachineMemOperand *> RegisteredMemOperands;
+ std::unordered_map<Register, std::size_t> VirtualDefCounts;
+ bool HasPhi = false;
+ bool HasGenericInstruction = false;
+ bool HasExecutablePseudo = false;
+ bool HasFrameIndex = false;
+ bool HasUnrewrittenTie = false;
+ bool HasStageTenPseudo = false;
+ bool HasLiveVirtualRegister = false;
+ bool MissingVirtualType = false;
+ bool MissingVirtualBank = false;
+ bool MissingVirtualClass = false;
+};
+
+} // namespace
+
+Expected<void> MachineVerifier::verify(const MachineFunction &Function,
+ const TargetDescription &Target,
+ MachineVerifierOptions Options) {
+ return FunctionVerifier(Function, Target, Options).run();
+}
+
+} // namespace rcc