diff options
| author | Richard-Qin-X <richard.qin.001@gmail.com> | 2026-08-11 17:37:42 +0800 |
|---|---|---|
| committer | Richard-Qin-X <richard.qin.001@gmail.com> | 2026-08-11 17:37:42 +0800 |
| commit | 6a7b083f322f4ac47e58cbf9d4d5fd4d1f6fecd0 (patch) | |
| tree | f363c502f0e2b9cda2f12928049275a433db6ad4 /backend/lib/CodeGen | |
| parent | adcf8c9b1bd1b47e00bf859693112cea1f7e8c9f (diff) | |
| download | rcc-project-6a7b083f322f4ac47e58cbf9d4d5fd4d1f6fecd0.tar.gz rcc-project-6a7b083f322f4ac47e58cbf9d4d5fd4d1f6fecd0.tar.xz | |
[MIR] Accept fixed-baseline LLVM syntax
Diffstat (limited to 'backend/lib/CodeGen')
| -rw-r--r-- | backend/lib/CodeGen/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | backend/lib/CodeGen/CompilationUnit.cpp | 39 | ||||
| -rw-r--r-- | backend/lib/CodeGen/InstructionDescriptor.cpp | 8 | ||||
| -rw-r--r-- | backend/lib/CodeGen/MachineFunction.cpp | 2 | ||||
| -rw-r--r-- | backend/lib/CodeGen/MachineFunctionCloner.cpp | 8 | ||||
| -rw-r--r-- | backend/lib/CodeGen/MachineMemOperand.cpp | 40 | ||||
| -rw-r--r-- | backend/lib/CodeGen/MachineOperand.cpp | 59 | ||||
| -rw-r--r-- | backend/lib/CodeGen/MachinePseudoExpansion.cpp | 22 | ||||
| -rw-r--r-- | backend/lib/CodeGen/MachineRegisterInfo.cpp | 1 | ||||
| -rw-r--r-- | backend/lib/CodeGen/MachineVerifier.cpp | 58 |
10 files changed, 165 insertions, 73 deletions
diff --git a/backend/lib/CodeGen/CMakeLists.txt b/backend/lib/CodeGen/CMakeLists.txt index 15f1772..d25d164 100644 --- a/backend/lib/CodeGen/CMakeLists.txt +++ b/backend/lib/CodeGen/CMakeLists.txt @@ -88,6 +88,7 @@ set( "${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/GenericConstant.h" "${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/FrameIndexElimination.h" "${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/InstructionDescriptor.h" + "${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/InlineAsm.h" "${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/LowLevelType.h" "${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/MachineBasicBlockAttributes.h" "${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/MachineCFIInfo.h" diff --git a/backend/lib/CodeGen/CompilationUnit.cpp b/backend/lib/CodeGen/CompilationUnit.cpp index 5ec54bc..a017851 100644 --- a/backend/lib/CodeGen/CompilationUnit.cpp +++ b/backend/lib/CodeGen/CompilationUnit.cpp @@ -22,13 +22,11 @@ CompilationUnit &CompilationUnit::operator=(CompilationUnit &&) noexcept = defau CompilationUnit::~CompilationUnit() = default; Expected<MachineFunction *> CompilationUnit::createMachineFunction(std::string Name) { - if (Name.empty()) - return makeUnexpected(ErrorCode::InvalidArgument, "machine function name cannot be empty"); - if (FunctionNameToIndex.contains(Name)) { + if (!Name.empty() && FunctionNameToIndex.contains(Name)) { return makeUnexpected(ErrorCode::InvalidArgument, "machine function name is already present in the compilation unit"); } - if (GlobalNameToIndex.contains(Name)) { + if (!Name.empty() && GlobalNameToIndex.contains(Name)) { return makeUnexpected(ErrorCode::InvalidArgument, "machine function name conflicts with a global object"); } @@ -42,7 +40,8 @@ Expected<MachineFunction *> CompilationUnit::createMachineFunction(std::string N MachineFunction *Result = Function->get(); const std::size_t Index = Functions.size(); - FunctionNameToIndex.emplace(std::string(Result->name()), Index); + if (!Result->name().empty()) + FunctionNameToIndex.emplace(std::string(Result->name()), Index); Functions.push_back(std::move(*Function)); return Result; } @@ -129,23 +128,20 @@ const GlobalObjectDesc *CompilationUnit::globalObject(std::string_view Name) con } Expected<void> CompilationUnit::verify() const { - if (FunctionNameToIndex.size() != Functions.size()) { - return makeUnexpected(ErrorCode::VerificationError, - "compilation unit function name index has an inconsistent size"); - } if (GlobalNameToIndex.size() != GlobalObjects.size()) { return makeUnexpected(ErrorCode::VerificationError, "compilation unit global object name index has an inconsistent size"); } std::unordered_set<MachineFunctionStableID> StableIDs; + std::size_t NamedFunctionCount = 0; for (std::size_t Index = 0; Index < Functions.size(); ++Index) { const std::unique_ptr<MachineFunction> &Function = Functions[Index]; if (Function == nullptr) { return makeUnexpected(ErrorCode::VerificationError, "compilation unit contains a null machine function"); } - if (Function->name().empty() || !Function->stableID().isValid()) { + if (!Function->stableID().isValid()) { return makeUnexpected(ErrorCode::VerificationError, "compilation unit contains an invalid machine function identity"); } @@ -154,16 +150,23 @@ Expected<void> CompilationUnit::verify() const { "compilation unit contains duplicate machine function StableIDs"); } - const auto Position = FunctionNameToIndex.find(Function->name()); - if (Position == FunctionNameToIndex.end() || Position->second != Index) { - return makeUnexpected(ErrorCode::VerificationError, - "compilation unit function name index is inconsistent"); - } - if (GlobalNameToIndex.contains(Function->name())) { - return makeUnexpected(ErrorCode::VerificationError, - "function name conflicts with a global object symbol"); + if (!Function->name().empty()) { + ++NamedFunctionCount; + const auto Position = FunctionNameToIndex.find(Function->name()); + if (Position == FunctionNameToIndex.end() || Position->second != Index) { + return makeUnexpected(ErrorCode::VerificationError, + "compilation unit function name index is inconsistent"); + } + if (GlobalNameToIndex.contains(Function->name())) { + return makeUnexpected(ErrorCode::VerificationError, + "function name conflicts with a global object symbol"); + } } } + if (FunctionNameToIndex.size() != NamedFunctionCount) { + return makeUnexpected(ErrorCode::VerificationError, + "compilation unit function name index has an inconsistent size"); + } for (std::size_t Index = 0; Index < GlobalObjects.size(); ++Index) { const std::unique_ptr<GlobalObjectDesc> &Object = GlobalObjects[Index]; diff --git a/backend/lib/CodeGen/InstructionDescriptor.cpp b/backend/lib/CodeGen/InstructionDescriptor.cpp index 48d4f10..fa00537 100644 --- a/backend/lib/CodeGen/InstructionDescriptor.cpp +++ b/backend/lib/CodeGen/InstructionDescriptor.cpp @@ -395,8 +395,12 @@ Expected<void> InstructionSet::validateExplicitOperands(const InstructionDescrip } const OperandDescriptor &OperandDesc = descriptorForOperand(Descriptor, *Shape, Index); const auto Validation = validateOperand(OperandDesc, Operand, RegInfo); - if (!Validation) - return std::unexpected(Validation.error()); + if (!Validation) { + return makeUnexpected( + Validation.error().code(), + "operand " + std::to_string(Index) + " of '" + Descriptor.Name + "': " + + std::string(Validation.error().message())); + } if (Operand.isRegister()) { if (isDefRole(OperandDesc.RegisterRole)) { if (SawUse) { diff --git a/backend/lib/CodeGen/MachineFunction.cpp b/backend/lib/CodeGen/MachineFunction.cpp index 7f4c3a9..b870858 100644 --- a/backend/lib/CodeGen/MachineFunction.cpp +++ b/backend/lib/CodeGen/MachineFunction.cpp @@ -57,8 +57,6 @@ private: Expected<std::unique_ptr<MachineFunction>> MachineFunction::create(std::string Name, MachineFunctionStableID StableID) { - if (Name.empty()) - return makeUnexpected(ErrorCode::InvalidArgument, "machine function name cannot be empty"); if (!StableID.isValid()) return makeUnexpected(ErrorCode::InvalidArgument, "machine function StableID must be valid"); return std::unique_ptr<MachineFunction>(new MachineFunction(std::move(Name), StableID)); diff --git a/backend/lib/CodeGen/MachineFunctionCloner.cpp b/backend/lib/CodeGen/MachineFunctionCloner.cpp index 0048194..1a66e66 100644 --- a/backend/lib/CodeGen/MachineFunctionCloner.cpp +++ b/backend/lib/CodeGen/MachineFunctionCloner.cpp @@ -202,6 +202,14 @@ cloneMemOperand(const MachineMemOperand &SourceOperand, CloneState &State) { if (!DestinationReg) return std::unexpected(DestinationReg.error()); + if (SourceInfo.Name) { + auto DestinationInfo = + State.Destination.registerInfo().virtualRegisterInfo(*DestinationReg); + if (!DestinationInfo) + return std::unexpected(DestinationInfo.error()); + DestinationInfo->get().Name = SourceInfo.Name; + } + if (SourceInfo.Bank) { auto SetResult = State.Destination.registerInfo().setRegisterBank(*DestinationReg, *SourceInfo.Bank); diff --git a/backend/lib/CodeGen/MachineMemOperand.cpp b/backend/lib/CodeGen/MachineMemOperand.cpp index 95e7237..5f126fd 100644 --- a/backend/lib/CodeGen/MachineMemOperand.cpp +++ b/backend/lib/CodeGen/MachineMemOperand.cpp @@ -4,12 +4,22 @@ #include <algorithm> #include <array> +#include <bit> #include <ranges> #include <utility> namespace rcc { namespace { +[[nodiscard]] std::uint64_t effectiveAlignment(std::uint64_t BaseAlignment, + std::int64_t Offset) noexcept { + if (Offset == 0) + return BaseAlignment; + const auto OffsetBits = static_cast<std::uint64_t>(Offset); + const std::uint64_t OffsetAlignment = std::uint64_t{1} << std::countr_zero(OffsetBits); + return std::min(BaseAlignment, OffsetAlignment); +} + [[nodiscard]] bool isValidAtomicLoadOrdering(AtomicOrdering Ordering) noexcept { return Ordering == AtomicOrdering::Unordered || Ordering == AtomicOrdering::Monotonic || Ordering == AtomicOrdering::Acquire || Ordering == AtomicOrdering::SequentiallyConsistent; @@ -167,6 +177,17 @@ MemoryBase MemoryBase::irValue(std::string Name) { return MemoryBase(Kind::IRValue, std::move(Name)); } +Expected<MemoryBase> MemoryBase::irConstant(GenericConstantData Constant) { + auto Verification = Constant.verify(); + if (!Verification) + return std::unexpected(Verification.error()); + if (Constant.Type.Kind != GenericConstantTypeKind::Pointer) { + return makeUnexpected(ErrorCode::InvalidArgument, + "machine memory IR constant base must have pointer type"); + } + return MemoryBase(Kind::IRConstant, std::move(Constant)); +} + MemoryBase MemoryBase::stack() { return MemoryBase(Kind::Stack, std::monostate{}); } @@ -222,6 +243,12 @@ std::optional<std::string_view> MemoryBase::symbolName() const noexcept { return std::get<std::string>(Value); } +const GenericConstantData *MemoryBase::constantValue() const noexcept { + if (BaseKind != Kind::IRConstant) + return nullptr; + return std::get_if<GenericConstantData>(&Value); +} + Expected<MachineMemOperand> MachineMemOperand::create(MachineMemOperandDesc Description) { const bool IsLoad = Description.Accesses.has(MemoryAccessFlag::Load); const bool IsStore = Description.Accesses.has(MemoryAccessFlag::Store); @@ -233,9 +260,16 @@ Expected<MachineMemOperand> MachineMemOperand::create(MachineMemOperandDesc Desc return makeUnexpected(ErrorCode::InvalidArgument, "machine memory operand address space must fit in 24 bits"); } - if (Description.BaseAlign && Description.BaseAlign->bytes() < Description.Align.bytes()) { - return makeUnexpected(ErrorCode::InvalidArgument, - "machine memory operand base alignment cannot be smaller than alignment"); + if (Description.BaseAlign) { + const std::uint64_t Effective = + effectiveAlignment(Description.BaseAlign->bytes(), Description.Offset); + if (Effective != Description.Align.bytes()) { + return makeUnexpected( + ErrorCode::InvalidArgument, + "machine memory operand alignment is inconsistent with base alignment and offset"); + } + if (*Description.BaseAlign == Description.Align) + Description.BaseAlign.reset(); } if (Description.Accesses.has(MemoryAccessFlag::Invariant) && (!IsLoad || IsStore)) { return makeUnexpected(ErrorCode::InvalidArgument, diff --git a/backend/lib/CodeGen/MachineOperand.cpp b/backend/lib/CodeGen/MachineOperand.cpp index d1d152e..c81a7c3 100644 --- a/backend/lib/CodeGen/MachineOperand.cpp +++ b/backend/lib/CodeGen/MachineOperand.cpp @@ -121,31 +121,12 @@ Expected<IntegerImmediate> IntegerImmediate::parseDecimal(std::uint32_t BitWidth Word = static_cast<std::uint64_t>(Product); Carry = static_cast<std::uint64_t>(Product >> 64U); } - if (Carry != 0 || (Words.back() & ~integerHighWordMask(BitWidth)) != 0) { - return makeUnexpected(ErrorCode::ParseError, - "integer immediate does not fit its declared bit width"); - } } + // LLVM integer literals are arbitrary precision and are extended or + // truncated to the declared bit width before becoming a constant. + Words.back() &= integerHighWordMask(BitWidth); if (Negative) { - if (std::ranges::all_of(Words, [](std::uint64_t Word) { return Word == 0; })) { - return makeUnexpected(ErrorCode::ParseError, - "negative zero is not a canonical integer immediate"); - } - const std::uint32_t SignBit = BitWidth - 1U; - const std::size_t SignWord = SignBit / 64U; - const std::uint64_t SignMask = std::uint64_t{1} << (SignBit % 64U); - bool Fits = Words[SignWord] <= SignMask; - for (std::size_t Index = SignWord + 1; Index < Words.size(); ++Index) - Fits = Fits && Words[Index] == 0; - if (Words[SignWord] == SignMask) { - for (std::size_t Index = 0; Index < SignWord; ++Index) - Fits = Fits && Words[Index] == 0; - } - if (!Fits) { - return makeUnexpected(ErrorCode::ParseError, - "negative integer immediate does not fit its declared bit width"); - } for (std::uint64_t &Word : Words) Word = ~Word; std::uint64_t Carry = 1; @@ -186,6 +167,36 @@ std::string IntegerImmediate::toUnsignedDecimal() const { return Digits; } +std::string IntegerImmediate::toSignedDecimal() const { + if (!isNegative()) + return toUnsignedDecimal(); + + std::vector<std::uint64_t> Magnitude = Words; + for (std::uint64_t &Word : Magnitude) + Word = ~Word; + std::uint64_t Carry = 1; + for (std::uint64_t &Word : Magnitude) { + const std::uint64_t Previous = Word; + Word += Carry; + Carry = Carry != 0 && Word < Previous ? 1 : 0; + } + Magnitude.back() &= integerHighWordMask(BitWidth); + + std::string Digits; + while (!std::ranges::all_of(Magnitude, [](std::uint64_t Word) { return Word == 0; })) { + unsigned __int128 Remainder = 0; + for (auto Iterator = Magnitude.rbegin(); Iterator != Magnitude.rend(); ++Iterator) { + const unsigned __int128 Dividend = (Remainder << 64U) | *Iterator; + *Iterator = static_cast<std::uint64_t>(Dividend / 10U); + Remainder = Dividend % 10U; + } + Digits.push_back(static_cast<char>('0' + static_cast<unsigned>(Remainder))); + } + std::ranges::reverse(Digits); + Digits.insert(Digits.begin(), '-'); + return Digits; +} + Expected<void> IntegerImmediate::verify() const { return verifyIntegerImmediate(BitWidth, Words, ErrorCode::VerificationError); } @@ -324,10 +335,6 @@ Expected<MachineOperand> MachineOperand::globalSymbol(std::string Name, std::int Expected<MachineOperand> MachineOperand::externalSymbol(std::string Name, std::int64_t Offset, std::uint32_t TargetFlags) { - if (Name.empty()) { - return makeUnexpected(ErrorCode::InvalidArgument, - "external symbol operand name cannot be empty"); - } return MachineOperand(Kind::ExternalSymbol, ExternalSymbolOperand{std::move(Name), Offset}, TargetFlags); } diff --git a/backend/lib/CodeGen/MachinePseudoExpansion.cpp b/backend/lib/CodeGen/MachinePseudoExpansion.cpp index 676804a..924c515 100644 --- a/backend/lib/CodeGen/MachinePseudoExpansion.cpp +++ b/backend/lib/CodeGen/MachinePseudoExpansion.cpp @@ -200,13 +200,20 @@ using BlockSizeSnapshot = std::vector<std::pair<MachineBlockStableID, std::uint6 return Error(Failure.code(), std::move(Prefix)); } +[[nodiscard]] std::vector<MachineInstr *> layoutInstructions(MachineFunction &Function) { + std::vector<MachineInstr *> Result; + for (MachineBasicBlock *Block = Function.firstBlock(); Block != nullptr; Block = Block->next()) + for (MachineInstr *Instruction = Block->firstInstruction(); Instruction != nullptr; + Instruction = Instruction->next()) + Result.push_back(Instruction); + return Result; +} + [[nodiscard]] Expected<MachinePassResult> prepareImplementation(MachineFunction &Function, const TargetDescription &Target, const TargetPseudoExpansionInfo &Info, MachineActionReplay &Actions) { bool Changed = false; - for (MachineInstr *Instruction : Function.allInstructions()) { - if (Instruction == nullptr || Instruction->parent() == nullptr) - continue; + for (MachineInstr *Instruction : layoutInstructions(Function)) { const InstructionDescriptor *Descriptor = Target.instructionDescriptor(Instruction->opcode()); if (Descriptor == nullptr) return makeUnexpected(ErrorCode::VerificationError, @@ -255,10 +262,7 @@ expandImplementation(MachineFunction &Function, const TargetDescription &Target, SizesBefore = std::move(*Snapshot); } std::vector<MachineInstr *> Worklist; - for (MachineInstr *Instruction : Function.allInstructions()) { - if (Instruction != nullptr && Instruction->parent() != nullptr) - Worklist.push_back(Instruction); - } + Worklist = layoutInstructions(Function); bool Changed = false; for (MachineInstr *Instruction : Worklist) { if (Instruction->parent() == nullptr) @@ -325,9 +329,7 @@ expandImplementation(MachineFunction &Function, const TargetDescription &Target, } } } - for (MachineInstr *Instruction : Function.allInstructions()) { - if (Instruction == nullptr || Instruction->parent() == nullptr) - continue; + for (MachineInstr *Instruction : layoutInstructions(Function)) { const InstructionDescriptor *Descriptor = Target.instructionDescriptor(Instruction->opcode()); if (Descriptor != nullptr && Descriptor->Flags.has(InstructionFlag::Pseudo) && Descriptor->Owner == ownerFor(Phase)) { diff --git a/backend/lib/CodeGen/MachineRegisterInfo.cpp b/backend/lib/CodeGen/MachineRegisterInfo.cpp index 7bd0ef3..2fe74d0 100644 --- a/backend/lib/CodeGen/MachineRegisterInfo.cpp +++ b/backend/lib/CodeGen/MachineRegisterInfo.cpp @@ -90,6 +90,7 @@ Expected<void> MachineRegisterInfo::eraseVirtualRegister(Register Reg) { } Info->get().IsTombstone = true; + Info->get().Name.reset(); Info->get().Type.reset(); Info->get().Bank.reset(); Info->get().RegClass.reset(); diff --git a/backend/lib/CodeGen/MachineVerifier.cpp b/backend/lib/CodeGen/MachineVerifier.cpp index be7462b..3d9fbdb 100644 --- a/backend/lib/CodeGen/MachineVerifier.cpp +++ b/backend/lib/CodeGen/MachineVerifier.cpp @@ -10,6 +10,7 @@ #include "rcc/CodeGen/MachineInstr.h" #include "rcc/CodeGen/MachineLiveness.h" #include "rcc/CodeGen/MachineMemOperand.h" +#include "rcc/CodeGen/MachineMetadata.h" #include "rcc/CodeGen/MachineOperand.h" #include "rcc/CodeGen/MachineRegisterInfo.h" @@ -28,6 +29,42 @@ 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.push_back('%'); + Result.append(std::to_string(*RegisterValue->virtualIndex())); + 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, @@ -35,7 +72,7 @@ public: : Function(Function), Target(Target), Options(Options) {} [[nodiscard]] Expected<void> run() { - if (Function.name().empty() || !Function.stableID().isValid()) + if (!Function.stableID().isValid()) return fail("function identity is invalid"); auto AttributeResult = Function.attributes().verify(); @@ -309,6 +346,14 @@ private: 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) @@ -359,10 +404,6 @@ private: Global != nullptr && Global->Name.empty()) { return fail("global symbol operand has an empty name"); } - if (const auto *External = Operand.getIf<ExternalSymbolOperand>(); - External != nullptr && External->Name.empty()) { - return fail("external 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"); @@ -534,13 +575,6 @@ private: return std::unexpected(Call.error()); } - for (const MachinePrefetchTarget &Prefetch : Records.prefetchTargets()) { - const std::uint64_t Key = (static_cast<std::uint64_t>(Prefetch.Block.BaseID) << 32U) | - static_cast<std::uint64_t>(Prefetch.Block.CloneID); - if (!LinkedBlockProfileIDs.contains(Key)) - return fail("prefetch target references an unknown bb_id"); - } - if (!Records.debugValueSubstitutions().empty() && !Function.attributes().has(MachineFunctionAttribute::DebugInstrRef)) { return fail("debug value substitutions require DebugInstrRef"); |
