diff options
| author | Richard-Qin-X <richard.qin.001@gmail.com> | 2026-08-02 03:37:46 +0800 |
|---|---|---|
| committer | Richard-Qin-X <richard.qin.001@gmail.com> | 2026-08-02 03:37:46 +0800 |
| commit | a8a9d7d926202450645deae872d4d9dc11d63215 (patch) | |
| tree | 16a3640a93e3f567ff31045969c02a42e1f467e0 /backend | |
| parent | 49d70380a6a95e4039f6b5e5ccce09671d5b4796 (diff) | |
| download | rcc-project-a8a9d7d926202450645deae872d4d9dc11d63215.tar.gz rcc-project-a8a9d7d926202450645deae872d4d9dc11d63215.tar.xz | |
[CodeGen] Model machine debug metadata
Replace marker-based metadata operands with scoped references and inline DIExpression values. Add structured DILocation chains, instruction debug attachments, stack variable records, and entry values with unified verification, builder support, and deterministic cloning.
Diffstat (limited to 'backend')
22 files changed, 814 insertions, 38 deletions
diff --git a/backend/include/rcc/CodeGen/MachineDebugInfo.h b/backend/include/rcc/CodeGen/MachineDebugInfo.h new file mode 100644 index 0000000..0d95e96 --- /dev/null +++ b/backend/include/rcc/CodeGen/MachineDebugInfo.h @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_MACHINEDEBUGINFO_H +#define RCC_CODEGEN_MACHINEDEBUGINFO_H + +#include "rcc/CodeGen/MachineMetadata.h" +#include "rcc/Support/Error.h" + +#include <cstdint> +#include <optional> +#include <span> +#include <utility> +#include <variant> +#include <vector> + +namespace rcc { + +class MachineDIExpression final { +public: + MachineDIExpression() = default; + explicit MachineDIExpression(std::vector<std::uint64_t> Elements) + : Elements(std::move(Elements)) {} + + [[nodiscard]] std::span<const std::uint64_t> elements() const noexcept { + return Elements; + } + + friend bool operator==(const MachineDIExpression &Left, + const MachineDIExpression &Right) = default; + +private: + std::vector<std::uint64_t> Elements; +}; + +class MachineMetadataValue final { +public: + MachineMetadataValue() = default; + + [[nodiscard]] static Expected<MachineMetadataValue> reference(MachineMetadataReference Reference); + [[nodiscard]] static MachineMetadataValue expression(MachineDIExpression Expression); + + [[nodiscard]] bool isValid() const noexcept { + return !std::holds_alternative<std::monostate>(Storage); + } + + [[nodiscard]] const MachineMetadataReference *reference() const noexcept { + return std::get_if<MachineMetadataReference>(&Storage); + } + + [[nodiscard]] const MachineDIExpression *expression() const noexcept { + return std::get_if<MachineDIExpression>(&Storage); + } + + [[nodiscard]] Expected<void> verify(const MachineMetadataInfo &Metadata) const; + + friend bool operator==(const MachineMetadataValue &Left, + const MachineMetadataValue &Right) = default; + +private: + explicit MachineMetadataValue(MachineMetadataReference Reference) : Storage(Reference) {} + explicit MachineMetadataValue(MachineDIExpression Expression) : Storage(std::move(Expression)) {} + + std::variant<std::monostate, MachineMetadataReference, MachineDIExpression> Storage; +}; + +struct MachineDILocationFrame final { + std::uint32_t Line = 0; + std::uint16_t Column = 0; + MachineMetadataReference Scope; + bool IsImplicitCode = false; + std::uint64_t AtomGroup = 0; + std::uint8_t AtomRank = 0; + + friend constexpr bool operator==(MachineDILocationFrame Left, + MachineDILocationFrame Right) noexcept = default; +}; + +class MachineDILocation final { +public: + static constexpr std::uint64_t MaxAtomGroup = (std::uint64_t{1} << 61) - 1; + + [[nodiscard]] static Expected<MachineDILocation> + create(std::vector<MachineDILocationFrame> InlineChain, + std::optional<MachineMetadataReference> TerminalInlinedAt = std::nullopt); + + [[nodiscard]] std::span<const MachineDILocationFrame> inlineChain() const noexcept { + return InlineChain; + } + + [[nodiscard]] std::optional<MachineMetadataReference> terminalInlinedAt() const noexcept { + return TerminalInlinedAt; + } + + [[nodiscard]] Expected<void> verify(const MachineMetadataInfo &Metadata) const; + + friend bool operator==(const MachineDILocation &Left, const MachineDILocation &Right) = default; + +private: + MachineDILocation(std::vector<MachineDILocationFrame> InlineChain, + std::optional<MachineMetadataReference> TerminalInlinedAt) + : InlineChain(std::move(InlineChain)), TerminalInlinedAt(TerminalInlinedAt) {} + + [[nodiscard]] Expected<void> verifyStructure() const; + + std::vector<MachineDILocationFrame> InlineChain; + std::optional<MachineMetadataReference> TerminalInlinedAt; +}; + +class MachineDebugLocation final { +public: + MachineDebugLocation() = default; + + [[nodiscard]] static Expected<MachineDebugLocation> reference(MachineMetadataReference Reference); + [[nodiscard]] static MachineDebugLocation inlineLocation(MachineDILocation Location); + + [[nodiscard]] bool isValid() const noexcept { + return !std::holds_alternative<std::monostate>(Storage); + } + + [[nodiscard]] const MachineMetadataReference *reference() const noexcept { + return std::get_if<MachineMetadataReference>(&Storage); + } + + [[nodiscard]] const MachineDILocation *inlineLocation() const noexcept { + return std::get_if<MachineDILocation>(&Storage); + } + + [[nodiscard]] Expected<void> verify(const MachineMetadataInfo &Metadata) const; + + friend bool operator==(const MachineDebugLocation &Left, + const MachineDebugLocation &Right) = default; + +private: + explicit MachineDebugLocation(MachineMetadataReference Reference) : Storage(Reference) {} + explicit MachineDebugLocation(MachineDILocation Location) : Storage(std::move(Location)) {} + + std::variant<std::monostate, MachineMetadataReference, MachineDILocation> Storage; +}; + +struct MachineVariableDebugInfo final { + std::optional<MachineMetadataReference> Variable; + std::optional<MachineMetadataValue> Expression; + std::optional<MachineDebugLocation> Location; + + [[nodiscard]] Expected<void> verifyStructure() const; + [[nodiscard]] Expected<void> verify(const MachineMetadataInfo &Metadata) const; + + friend bool operator==(const MachineVariableDebugInfo &Left, + const MachineVariableDebugInfo &Right) = default; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_MACHINEDEBUGINFO_H diff --git a/backend/include/rcc/CodeGen/MachineFrameInfo.h b/backend/include/rcc/CodeGen/MachineFrameInfo.h index ad1de53..b837ff3 100644 --- a/backend/include/rcc/CodeGen/MachineFrameInfo.h +++ b/backend/include/rcc/CodeGen/MachineFrameInfo.h @@ -3,6 +3,7 @@ #ifndef RCC_CODEGEN_MACHINEFRAMEINFO_H #define RCC_CODEGEN_MACHINEFRAMEINFO_H +#include "rcc/CodeGen/MachineDebugInfo.h" #include "rcc/CodeGen/MachineIRTypes.h" #include "rcc/CodeGen/MachineMemOperand.h" #include "rcc/CodeGen/StableID.h" @@ -95,6 +96,7 @@ struct StackObjectDesc final { bool IsAliased = false; bool AddressTaken = false; std::vector<StackObjectLifetimeRange> Lifetime; + std::optional<MachineVariableDebugInfo> DebugInfo; std::uint64_t TargetFlags = 0; friend bool operator==(const StackObjectDesc &Left, const StackObjectDesc &Right) = default; diff --git a/backend/include/rcc/CodeGen/MachineFunctionRecords.h b/backend/include/rcc/CodeGen/MachineFunctionRecords.h index 7929ed3..b792494 100644 --- a/backend/include/rcc/CodeGen/MachineFunctionRecords.h +++ b/backend/include/rcc/CodeGen/MachineFunctionRecords.h @@ -4,6 +4,7 @@ #define RCC_CODEGEN_MACHINEFUNCTIONRECORDS_H #include "rcc/CodeGen/MachineBasicBlockAttributes.h" +#include "rcc/CodeGen/MachineDebugInfo.h" #include "rcc/CodeGen/Register.h" #include "rcc/CodeGen/StableID.h" #include "rcc/Support/Error.h" @@ -37,6 +38,14 @@ struct MachineDebugValueSubstitution final { MachineDebugValueSubstitution Right) noexcept = default; }; +struct MachineEntryValueInfo final { + PhysicalRegister Reg; + MachineVariableDebugInfo DebugInfo; + + friend bool operator==(const MachineEntryValueInfo &Left, + const MachineEntryValueInfo &Right) = default; +}; + struct MachineCallSiteArgument final { PhysicalRegister Reg; std::uint16_t ArgumentNumber = 0; @@ -122,6 +131,12 @@ public: [[nodiscard]] Expected<void> addDebugValueSubstitution(MachineDebugValueSubstitution Substitution); + [[nodiscard]] std::span<const MachineEntryValueInfo> entryValues() const noexcept { + return EntryValues; + } + + [[nodiscard]] Expected<void> addEntryValue(MachineEntryValueInfo EntryValue); + [[nodiscard]] Expected<void> verify() const; [[nodiscard]] Expected<MachineFunctionRecords> clone(const MachineFunctionCloneContext &CloneContext) const; @@ -135,6 +150,7 @@ private: std::vector<MachineCalledGlobal> CalledGlobals; std::vector<MachinePrefetchTarget> PrefetchTargets; std::vector<MachineDebugValueSubstitution> DebugValueSubstitutions; + std::vector<MachineEntryValueInfo> EntryValues; }; } // namespace rcc diff --git a/backend/include/rcc/CodeGen/MachineIRBuilder.h b/backend/include/rcc/CodeGen/MachineIRBuilder.h index 08ec9ba..918f389 100644 --- a/backend/include/rcc/CodeGen/MachineIRBuilder.h +++ b/backend/include/rcc/CodeGen/MachineIRBuilder.h @@ -70,7 +70,8 @@ public: std::uint32_t TargetFlags = 0); [[nodiscard]] Expected<void> addRegisterMask(std::shared_ptr<const RegisterMask> Mask, std::uint32_t TargetFlags = 0); - [[nodiscard]] Expected<void> addMetadata(std::string Marker, std::uint32_t TargetFlags = 0); + [[nodiscard]] Expected<void> addMetadata(MachineMetadataValue Value, + std::uint32_t TargetFlags = 0); [[nodiscard]] Expected<void> addTargetIndex(TargetIndex Index, std::int64_t Offset = 0, std::uint32_t TargetFlags = 0); [[nodiscard]] Expected<void> addMCSymbol(std::string Name, std::int64_t Offset = 0, @@ -83,6 +84,8 @@ public: [[nodiscard]] Expected<void> setSourceLocation(SourceLocation NewLocation); [[nodiscard]] Expected<void> clearSourceLocation(); + [[nodiscard]] Expected<void> setDebugLocation(MachineDebugLocation NewLocation); + [[nodiscard]] Expected<void> clearDebugLocation(); [[nodiscard]] Expected<void> setOrigin(OriginID NewOrigin); [[nodiscard]] Expected<void> clearOrigin(); @@ -116,6 +119,7 @@ private: std::vector<const MachineMemOperand *> MemOperands; MachineInstrFlags Flags; std::optional<SourceLocation> Location; + std::optional<MachineDebugLocation> DebugLocation; std::optional<OriginID> Origin; bool IsBuilt = false; diff --git a/backend/include/rcc/CodeGen/MachineInstr.h b/backend/include/rcc/CodeGen/MachineInstr.h index 78d48cb..69d7dd7 100644 --- a/backend/include/rcc/CodeGen/MachineInstr.h +++ b/backend/include/rcc/CodeGen/MachineInstr.h @@ -121,6 +121,16 @@ public: Location.reset(); } + [[nodiscard]] const std::optional<MachineDebugLocation> &debugLocation() const noexcept { + return DebugLocation; + } + + [[nodiscard]] Expected<void> setDebugLocation(MachineDebugLocation NewLocation); + + void clearDebugLocation() noexcept { + DebugLocation.reset(); + } + [[nodiscard]] std::optional<OriginID> origin() const noexcept { return Origin; } @@ -164,6 +174,7 @@ private: std::vector<const MachineMemOperand *> MemOperands; MachineInstrFlags Flags; std::optional<SourceLocation> Location; + std::optional<MachineDebugLocation> DebugLocation; std::optional<OriginID> Origin; std::uint32_t DebugInstrNumber = 0; diff --git a/backend/include/rcc/CodeGen/MachineMetadata.h b/backend/include/rcc/CodeGen/MachineMetadata.h index 577d5da..95033f6 100644 --- a/backend/include/rcc/CodeGen/MachineMetadata.h +++ b/backend/include/rcc/CodeGen/MachineMetadata.h @@ -126,6 +126,9 @@ private: std::unordered_map<std::uint32_t, std::size_t> SlotToIndex; }; +[[nodiscard]] Expected<void> verifyMachineMetadataReference(MachineMetadataReference Reference, + const MachineMetadataInfo &Metadata); + } // namespace rcc #endif // RCC_CODEGEN_MACHINEMETADATA_H diff --git a/backend/include/rcc/CodeGen/MachineOperand.h b/backend/include/rcc/CodeGen/MachineOperand.h index 35cf45b..ce5eb60 100644 --- a/backend/include/rcc/CodeGen/MachineOperand.h +++ b/backend/include/rcc/CodeGen/MachineOperand.h @@ -4,6 +4,7 @@ #define RCC_CODEGEN_MACHINEOPERAND_H #include "rcc/CodeGen/LowLevelType.h" +#include "rcc/CodeGen/MachineDebugInfo.h" #include "rcc/CodeGen/MachineIRTypes.h" #include "rcc/CodeGen/Register.h" #include "rcc/CodeGen/RegisterMask.h" @@ -176,7 +177,7 @@ struct RegisterMaskOperand final { }; struct MetadataOperand final { - std::string Marker; + MachineMetadataValue Value; friend bool operator==(const MetadataOperand &Left, const MetadataOperand &Right) = default; }; @@ -247,7 +248,7 @@ public: std::uint32_t TargetFlags = 0); [[nodiscard]] static Expected<MachineOperand> registerMask(std::shared_ptr<const RegisterMask> Mask, std::uint32_t TargetFlags = 0); - [[nodiscard]] static Expected<MachineOperand> metadata(std::string Marker, + [[nodiscard]] static Expected<MachineOperand> metadata(MachineMetadataValue Value, std::uint32_t TargetFlags = 0); [[nodiscard]] static MachineOperand targetIndex(TargetIndex Index, std::int64_t Offset = 0, std::uint32_t TargetFlags = 0); diff --git a/backend/lib/CodeGen/CMakeLists.txt b/backend/lib/CodeGen/CMakeLists.txt index ec95c3c..08ca00e 100644 --- a/backend/lib/CodeGen/CMakeLists.txt +++ b/backend/lib/CodeGen/CMakeLists.txt @@ -10,6 +10,7 @@ set( MachineBasicBlock.cpp MachineCFGUpdater.cpp MachineConstantPool.cpp + MachineDebugInfo.cpp MachineFrameInfo.cpp MachineFunction.cpp MachineFunctionAttributes.cpp @@ -42,6 +43,7 @@ set( "${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/MachineBasicBlock.h" "${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/MachineCFGUpdater.h" "${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/MachineConstantPool.h" + "${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/MachineDebugInfo.h" "${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/MachineFrameInfo.h" "${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/MachineFunction.h" "${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/MachineFunctionAttributes.h" @@ -103,6 +105,7 @@ rcc_backend_register_format_files( "${CMAKE_CURRENT_SOURCE_DIR}/MachineBasicBlock.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/MachineCFGUpdater.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/MachineConstantPool.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/MachineDebugInfo.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/MachineFrameInfo.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/MachineFunction.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/MachineFunctionAttributes.cpp" @@ -135,6 +138,7 @@ rcc_backend_register_tidy_files( "${CMAKE_CURRENT_SOURCE_DIR}/MachineBasicBlock.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/MachineCFGUpdater.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/MachineConstantPool.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/MachineDebugInfo.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/MachineFrameInfo.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/MachineFunction.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/MachineFunctionAttributes.cpp" diff --git a/backend/lib/CodeGen/MachineDebugInfo.cpp b/backend/lib/CodeGen/MachineDebugInfo.cpp new file mode 100644 index 0000000..35f68ca --- /dev/null +++ b/backend/lib/CodeGen/MachineDebugInfo.cpp @@ -0,0 +1,164 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "rcc/CodeGen/MachineDebugInfo.h" + +#include <utility> + +namespace rcc { + +Expected<MachineMetadataValue> MachineMetadataValue::reference(MachineMetadataReference Reference) { + if (!Reference.isValid()) { + return makeUnexpected(ErrorCode::InvalidArgument, + "machine metadata value reference must be valid"); + } + return MachineMetadataValue(Reference); +} + +MachineMetadataValue MachineMetadataValue::expression(MachineDIExpression Expression) { + return MachineMetadataValue(std::move(Expression)); +} + +Expected<void> MachineMetadataValue::verify(const MachineMetadataInfo &Metadata) const { + if (!isValid()) { + return makeUnexpected(ErrorCode::VerificationError, + "machine metadata value has no representation"); + } + const MachineMetadataReference *Reference = reference(); + if (Reference == nullptr) + return {}; + return verifyMachineMetadataReference(*Reference, Metadata); +} + +Expected<MachineDILocation> +MachineDILocation::create(std::vector<MachineDILocationFrame> InlineChain, + std::optional<MachineMetadataReference> TerminalInlinedAt) { + MachineDILocation Result(std::move(InlineChain), TerminalInlinedAt); + auto Verification = Result.verifyStructure(); + if (!Verification) + return std::unexpected(Verification.error()); + return Result; +} + +Expected<void> MachineDILocation::verifyStructure() const { + if (InlineChain.empty()) { + return makeUnexpected(ErrorCode::VerificationError, + "inline debug location requires at least one frame"); + } + for (const MachineDILocationFrame &Frame : InlineChain) { + if (!Frame.Scope.isValid()) { + return makeUnexpected(ErrorCode::VerificationError, + "inline debug location frame has an invalid scope"); + } + if (Frame.Scope.scope() != MachineMetadataScope::EmbeddedIR) { + return makeUnexpected(ErrorCode::VerificationError, + "inline debug location scope must reference embedded IR metadata"); + } + if (Frame.AtomGroup > MaxAtomGroup) { + return makeUnexpected(ErrorCode::VerificationError, + "inline debug location atom group exceeds 61 bits"); + } + if (Frame.AtomRank > 7) { + return makeUnexpected(ErrorCode::VerificationError, + "inline debug location atom rank exceeds three bits"); + } + } + if (TerminalInlinedAt && !TerminalInlinedAt->isValid()) { + return makeUnexpected(ErrorCode::VerificationError, + "inline debug location has an invalid terminal inlined-at reference"); + } + if (TerminalInlinedAt && TerminalInlinedAt->scope() != MachineMetadataScope::EmbeddedIR) { + return makeUnexpected(ErrorCode::VerificationError, + "inline debug location inlined-at must reference embedded IR metadata"); + } + return {}; +} + +Expected<void> MachineDILocation::verify(const MachineMetadataInfo &Metadata) const { + auto Structure = verifyStructure(); + if (!Structure) + return Structure; + for (const MachineDILocationFrame &Frame : InlineChain) { + auto Scope = verifyMachineMetadataReference(Frame.Scope, Metadata); + if (!Scope) + return Scope; + } + if (TerminalInlinedAt) + return verifyMachineMetadataReference(*TerminalInlinedAt, Metadata); + return {}; +} + +Expected<MachineDebugLocation> MachineDebugLocation::reference(MachineMetadataReference Reference) { + if (!Reference.isValid()) { + return makeUnexpected(ErrorCode::InvalidArgument, + "machine debug location reference must be valid"); + } + if (Reference.scope() != MachineMetadataScope::EmbeddedIR) { + return makeUnexpected(ErrorCode::InvalidArgument, + "machine debug location must reference embedded IR metadata"); + } + return MachineDebugLocation(Reference); +} + +MachineDebugLocation MachineDebugLocation::inlineLocation(MachineDILocation Location) { + return MachineDebugLocation(std::move(Location)); +} + +Expected<void> MachineDebugLocation::verify(const MachineMetadataInfo &Metadata) const { + if (!isValid()) { + return makeUnexpected(ErrorCode::VerificationError, + "machine debug location has no representation"); + } + if (const MachineMetadataReference *Reference = reference()) + return verifyMachineMetadataReference(*Reference, Metadata); + return inlineLocation()->verify(Metadata); +} + +Expected<void> MachineVariableDebugInfo::verifyStructure() const { + if (!Variable && !Expression && !Location) { + return makeUnexpected(ErrorCode::VerificationError, + "variable debug information has no metadata"); + } + if (Variable && !Variable->isValid()) { + return makeUnexpected(ErrorCode::VerificationError, + "variable debug information has an invalid variable reference"); + } + if (Variable && Variable->scope() != MachineMetadataScope::EmbeddedIR) { + return makeUnexpected(ErrorCode::VerificationError, + "variable debug information must reference embedded IR metadata"); + } + if (Expression && !Expression->isValid()) { + return makeUnexpected(ErrorCode::VerificationError, + "variable debug information has an invalid expression"); + } + if (Expression && Expression->reference() != nullptr && + Expression->reference()->scope() != MachineMetadataScope::EmbeddedIR) { + return makeUnexpected(ErrorCode::VerificationError, + "variable debug expression must reference embedded IR metadata"); + } + if (Location && !Location->isValid()) { + return makeUnexpected(ErrorCode::VerificationError, + "variable debug information has an invalid location"); + } + return {}; +} + +Expected<void> MachineVariableDebugInfo::verify(const MachineMetadataInfo &Metadata) const { + auto Structure = verifyStructure(); + if (!Structure) + return Structure; + if (Variable) { + auto VariableResult = verifyMachineMetadataReference(*Variable, Metadata); + if (!VariableResult) + return VariableResult; + } + if (Expression) { + auto ExpressionResult = Expression->verify(Metadata); + if (!ExpressionResult) + return ExpressionResult; + } + if (Location) + return Location->verify(Metadata); + return {}; +} + +} // namespace rcc diff --git a/backend/lib/CodeGen/MachineFrameInfo.cpp b/backend/lib/CodeGen/MachineFrameInfo.cpp index 58eab00..c4d3cda 100644 --- a/backend/lib/CodeGen/MachineFrameInfo.cpp +++ b/backend/lib/CodeGen/MachineFrameInfo.cpp @@ -77,6 +77,11 @@ Expected<void> MachineFrameInfo::verifyDescription(const StackObjectDesc &Descri "stack object lifetime boundaries must not be reused"); } } + if (Description.DebugInfo) { + auto DebugInfoResult = Description.DebugInfo->verifyStructure(); + if (!DebugInfoResult) + return DebugInfoResult; + } return {}; } diff --git a/backend/lib/CodeGen/MachineFunctionCloner.cpp b/backend/lib/CodeGen/MachineFunctionCloner.cpp index e12e33e..dcf9712 100644 --- a/backend/lib/CodeGen/MachineFunctionCloner.cpp +++ b/backend/lib/CodeGen/MachineFunctionCloner.cpp @@ -386,6 +386,12 @@ cloneMemOperand(const MachineMemOperand &SourceOperand, CloneState &State) { if (SourceInstruction->sourceLocation()) { NewInstruction->setSourceLocation(*SourceInstruction->sourceLocation()); } + if (SourceInstruction->debugLocation()) { + auto DebugLocationResult = + NewInstruction->setDebugLocation(*SourceInstruction->debugLocation()); + if (!DebugLocationResult) + return std::unexpected(DebugLocationResult.error()); + } if (SourceInstruction->origin()) NewInstruction->setOrigin(*SourceInstruction->origin()); if (SourceInstruction->debugInstrNumber() != 0) { diff --git a/backend/lib/CodeGen/MachineFunctionRecords.cpp b/backend/lib/CodeGen/MachineFunctionRecords.cpp index 9fda0d7..0a21581 100644 --- a/backend/lib/CodeGen/MachineFunctionRecords.cpp +++ b/backend/lib/CodeGen/MachineFunctionRecords.cpp @@ -80,6 +80,14 @@ verifyDebugValueSubstitution(const MachineDebugValueSubstitution &Substitution) return {}; } +[[nodiscard]] Expected<void> verifyEntryValue(const MachineEntryValueInfo &EntryValue) { + if (!EntryValue.Reg.isValid()) { + return makeUnexpected(ErrorCode::VerificationError, + "entry-value record requires a physical register"); + } + return EntryValue.DebugInfo.verifyStructure(); +} + } // namespace Expected<void> @@ -143,6 +151,14 @@ MachineFunctionRecords::addDebugValueSubstitution(MachineDebugValueSubstitution return {}; } +Expected<void> MachineFunctionRecords::addEntryValue(MachineEntryValueInfo EntryValue) { + auto Verification = verifyEntryValue(EntryValue); + if (!Verification) + return std::unexpected(Verification.error()); + EntryValues.push_back(std::move(EntryValue)); + return {}; +} + Expected<void> MachineFunctionRecords::verify() const { if (CalleeSavedRegisters) { auto CalleeSavedVerification = verifyCalleeSavedRegisters(*CalleeSavedRegisters); @@ -210,6 +226,12 @@ Expected<void> MachineFunctionRecords::verify() const { } CompletedDebugReferences.insert(Path.begin(), Path.end()); } + + for (const MachineEntryValueInfo &EntryValue : EntryValues) { + auto EntryValueVerification = verifyEntryValue(EntryValue); + if (!EntryValueVerification) + return EntryValueVerification; + } return {}; } @@ -258,6 +280,11 @@ MachineFunctionRecords::clone(const MachineFunctionCloneContext &CloneContext) c if (!AddResult) return std::unexpected(AddResult.error()); } + for (const MachineEntryValueInfo &Source : EntryValues) { + auto AddResult = Result.addEntryValue(Source); + if (!AddResult) + return std::unexpected(AddResult.error()); + } return Result; } diff --git a/backend/lib/CodeGen/MachineIRBuilder.cpp b/backend/lib/CodeGen/MachineIRBuilder.cpp index 7f6421b..5afdd72 100644 --- a/backend/lib/CodeGen/MachineIRBuilder.cpp +++ b/backend/lib/CodeGen/MachineIRBuilder.cpp @@ -33,8 +33,8 @@ MachineInstrBuilder::MachineInstrBuilder(MachineInstrBuilder &&Other) noexcept ExplicitOperands(std::move(Other.ExplicitOperands)), ExtraImplicitOperands(std::move(Other.ExtraImplicitOperands)), RequestedTies(std::move(Other.RequestedTies)), MemOperands(std::move(Other.MemOperands)), - Flags(Other.Flags), Location(std::move(Other.Location)), Origin(Other.Origin), - IsBuilt(Other.IsBuilt) { + Flags(Other.Flags), Location(std::move(Other.Location)), + DebugLocation(std::move(Other.DebugLocation)), Origin(Other.Origin), IsBuilt(Other.IsBuilt) { Other.Function = nullptr; Other.Target = nullptr; Other.Descriptor = nullptr; @@ -58,6 +58,7 @@ MachineInstrBuilder &MachineInstrBuilder::operator=(MachineInstrBuilder &&Other) MemOperands = std::move(Other.MemOperands); Flags = Other.Flags; Location = std::move(Other.Location); + DebugLocation = std::move(Other.DebugLocation); Origin = Other.Origin; IsBuilt = Other.IsBuilt; @@ -219,8 +220,9 @@ Expected<void> MachineInstrBuilder::addRegisterMask(std::shared_ptr<const Regist return addOperand(std::move(*Operand)); } -Expected<void> MachineInstrBuilder::addMetadata(std::string Marker, std::uint32_t TargetFlags) { - auto Operand = MachineOperand::metadata(std::move(Marker), TargetFlags); +Expected<void> MachineInstrBuilder::addMetadata(MachineMetadataValue Value, + std::uint32_t TargetFlags) { + auto Operand = MachineOperand::metadata(std::move(Value), TargetFlags); if (!Operand) return std::unexpected(Operand.error()); return addOperand(std::move(*Operand)); @@ -301,6 +303,25 @@ Expected<void> MachineInstrBuilder::clearSourceLocation() { return {}; } +Expected<void> MachineInstrBuilder::setDebugLocation(MachineDebugLocation NewLocation) { + auto Pending = ensurePending(); + if (!Pending) + return std::unexpected(Pending.error()); + if (!NewLocation.isValid()) { + return makeUnexpected(ErrorCode::InvalidArgument, "builder debug location is invalid"); + } + DebugLocation = std::move(NewLocation); + return {}; +} + +Expected<void> MachineInstrBuilder::clearDebugLocation() { + auto Pending = ensurePending(); + if (!Pending) + return std::unexpected(Pending.error()); + DebugLocation.reset(); + return {}; +} + Expected<void> MachineInstrBuilder::setOrigin(OriginID NewOrigin) { auto Pending = ensurePending(); if (!Pending) @@ -518,6 +539,11 @@ Expected<MachineInstr *> MachineInstrBuilder::build() { Result->setFlags(Flags); if (Location) Result->setSourceLocation(std::move(*Location)); + if (DebugLocation) { + auto DebugLocationResult = Result->setDebugLocation(std::move(*DebugLocation)); + if (!DebugLocationResult) + return std::unexpected(DebugLocationResult.error()); + } if (Origin) Result->setOrigin(*Origin); diff --git a/backend/lib/CodeGen/MachineInstr.cpp b/backend/lib/CodeGen/MachineInstr.cpp index aaeeacd..a8fe9b5 100644 --- a/backend/lib/CodeGen/MachineInstr.cpp +++ b/backend/lib/CodeGen/MachineInstr.cpp @@ -148,6 +148,15 @@ Expected<void> MachineInstr::untieOperand(std::size_t Index) { return {}; } +Expected<void> MachineInstr::setDebugLocation(MachineDebugLocation NewLocation) { + if (!NewLocation.isValid()) { + return makeUnexpected(ErrorCode::InvalidArgument, + "machine instruction debug location must be valid"); + } + DebugLocation = std::move(NewLocation); + return {}; +} + Expected<void> MachineInstr::addMemOperand(const MachineMemOperand &MemOperand) { if (!OwnerFunction->ownsMemOperand(MemOperand)) { return makeUnexpected(ErrorCode::InvalidArgument, diff --git a/backend/lib/CodeGen/MachineMetadata.cpp b/backend/lib/CodeGen/MachineMetadata.cpp index 24a04e9..f789a74 100644 --- a/backend/lib/CodeGen/MachineMetadata.cpp +++ b/backend/lib/CodeGen/MachineMetadata.cpp @@ -111,4 +111,17 @@ Expected<MachineMetadataInfo> MachineMetadataInfo::clone() const { return Result; } +Expected<void> verifyMachineMetadataReference(MachineMetadataReference Reference, + const MachineMetadataInfo &Metadata) { + if (!Reference.isValid()) { + return makeUnexpected(ErrorCode::VerificationError, "machine metadata reference is invalid"); + } + if (Reference.scope() == MachineMetadataScope::MachineFunction && + Metadata.node(Reference.slot()) == nullptr) { + return makeUnexpected(ErrorCode::VerificationError, + "machine metadata reference uses an undefined function slot"); + } + return {}; +} + } // namespace rcc diff --git a/backend/lib/CodeGen/MachineOperand.cpp b/backend/lib/CodeGen/MachineOperand.cpp index 0e5febd..cd2d217 100644 --- a/backend/lib/CodeGen/MachineOperand.cpp +++ b/backend/lib/CodeGen/MachineOperand.cpp @@ -158,11 +158,12 @@ Expected<MachineOperand> MachineOperand::registerMask(std::shared_ptr<const Regi return MachineOperand(Kind::RegisterMask, RegisterMaskOperand{std::move(Mask)}, TargetFlags); } -Expected<MachineOperand> MachineOperand::metadata(std::string Marker, std::uint32_t TargetFlags) { - if (Marker.empty()) { - return makeUnexpected(ErrorCode::InvalidArgument, "metadata operand marker cannot be empty"); +Expected<MachineOperand> MachineOperand::metadata(MachineMetadataValue Value, + std::uint32_t TargetFlags) { + if (!Value.isValid()) { + return makeUnexpected(ErrorCode::InvalidArgument, "metadata operand value must be valid"); } - return MachineOperand(Kind::Metadata, MetadataOperand{std::move(Marker)}, TargetFlags); + return MachineOperand(Kind::Metadata, MetadataOperand{std::move(Value)}, TargetFlags); } MachineOperand MachineOperand::targetIndex(TargetIndex Index, std::int64_t Offset, diff --git a/backend/lib/CodeGen/MachineVerifier.cpp b/backend/lib/CodeGen/MachineVerifier.cpp index 2a29343..7e050ec 100644 --- a/backend/lib/CodeGen/MachineVerifier.cpp +++ b/backend/lib/CodeGen/MachineVerifier.cpp @@ -272,6 +272,11 @@ private: if (Instruction.sourceLocation() && !Instruction.sourceLocation()->isValid()) { return fail("instruction has an invalid source location"); } + if (Instruction.debugLocation()) { + auto DebugLocationResult = Instruction.debugLocation()->verify(Function.metadata()); + if (!DebugLocationResult) + return failWith("instruction debug location", DebugLocationResult.error()); + } if (Instruction.origin() && Instruction.origin()->value() == 0) return fail("instruction has an invalid origin ID"); @@ -293,9 +298,10 @@ private: 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 && Metadata->Marker.empty()) { - return fail("metadata operand has an empty marker"); + 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()) { @@ -342,6 +348,11 @@ private: 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()); + } } return {}; } @@ -356,6 +367,14 @@ private: 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 *> { diff --git a/backend/unittests/CodeGen/MachineDebugInfoTest.cpp b/backend/unittests/CodeGen/MachineDebugInfoTest.cpp index 6c5eea7..80a83e0 100644 --- a/backend/unittests/CodeGen/MachineDebugInfoTest.cpp +++ b/backend/unittests/CodeGen/MachineDebugInfoTest.cpp @@ -1,8 +1,11 @@ // SPDX-License-Identifier: GPL-3.0-or-later +#include "rcc/CodeGen/MachineDebugInfo.h" + #include "Target/TestTarget.h" #include "rcc/CodeGen/InstructionDescriptor.h" #include "rcc/CodeGen/MachineBasicBlock.h" +#include "rcc/CodeGen/MachineFrameInfo.h" #include "rcc/CodeGen/MachineFunction.h" #include "rcc/CodeGen/MachineFunctionAttributes.h" #include "rcc/CodeGen/MachineFunctionCloner.h" @@ -15,6 +18,7 @@ #include <iostream> #include <limits> #include <memory> +#include <optional> #include <string> #include <string_view> #include <utility> @@ -47,6 +51,10 @@ private: Value.InstructionOpcode = rcc::Opcode::fromValue(ValueOpcode); Value.Name = "TEST_VALUE"; Value.Flags = {rcc::InstructionFlag::ProducesCode}; + rcc::OperandDescriptor OptionalMetadata; + OptionalMetadata.Kinds = {rcc::MachineOperand::Kind::Metadata}; + OptionalMetadata.Optional = true; + Value.Operands.push_back(OptionalMetadata); rcc::InstructionDescriptor Meta; Meta.InstructionOpcode = rcc::Opcode::fromValue(MetaOpcode); @@ -90,6 +98,217 @@ createFunction(TestContext &Context, std::uint64_t StableID, std::string Name) { return Instruction ? *Instruction : nullptr; } +void testMetadataValuesAndLocations(TestContext &Context) { + rcc::MachineMetadataInfo Metadata; + Context.expect(Metadata.addNode({.Slot = 0, .IsDistinct = false, .Elements = {}}).has_value(), + "function-local debug metadata setup must succeed"); + + const rcc::MachineDIExpression Expression( + std::vector<std::uint64_t>{0x1005, 0, 0x22, 0x1000, 0, 64}); + Context.expect(Expression.elements().size() == 6 && Expression.elements()[0] == 0x1005, + "inline DIExpression elements must preserve their exact unsigned encoding"); + auto ExpressionValue = rcc::MachineMetadataValue::expression(Expression); + Context.expect(ExpressionValue.verify(Metadata).has_value() && + ExpressionValue.expression() != nullptr && + *ExpressionValue.expression() == Expression, + "inline DIExpression metadata values must remain structured"); + Context.expect(!rcc::MachineMetadataValue{}.verify(Metadata), + "an unrepresented metadata value must be rejected"); + + Context.expect(!rcc::MachineDILocation::create({}), + "an inline DILocation must contain a root frame"); + Context.expect(!rcc::MachineDILocation::create({{}}), + "an inline DILocation frame must contain a valid scope"); + Context.expect(!rcc::MachineDILocation::create({ + { + .Line = 1, + .Column = 0, + .Scope = rcc::MachineMetadataReference::embeddedIR(1), + .IsImplicitCode = false, + .AtomGroup = 0, + .AtomRank = 8, + }, + }), + "an inline DILocation atom rank must fit the fixed three-bit field"); + Context.expect(!rcc::MachineDILocation::create({ + { + .Line = 1, + .Column = 0, + .Scope = rcc::MachineMetadataReference::embeddedIR(1), + .IsImplicitCode = false, + .AtomGroup = rcc::MachineDILocation::MaxAtomGroup + 1, + .AtomRank = 0, + }, + }), + "an inline DILocation atom group must fit the fixed 61-bit field"); + + auto InlineLocation = rcc::MachineDILocation::create( + { + { + .Line = 7, + .Column = 65'535, + .Scope = rcc::MachineMetadataReference::embeddedIR(3), + .IsImplicitCode = true, + .AtomGroup = rcc::MachineDILocation::MaxAtomGroup, + .AtomRank = 7, + }, + { + .Line = 9, + .Column = 4, + .Scope = rcc::MachineMetadataReference::embeddedIR(0), + .IsImplicitCode = false, + .AtomGroup = 0, + .AtomRank = 0, + }, + }, + rcc::MachineMetadataReference::embeddedIR(4)); + Context.expect(InlineLocation.has_value() && InlineLocation->verify(Metadata).has_value() && + InlineLocation->inlineChain().size() == 2 && + InlineLocation->terminalInlinedAt() == + rcc::MachineMetadataReference::embeddedIR(4), + "inline DILocation chains must preserve nested and terminal references"); + if (!InlineLocation) + return; + + auto DebugLocation = rcc::MachineDebugLocation::inlineLocation(*InlineLocation); + Context.expect(DebugLocation.verify(Metadata).has_value() && + DebugLocation.inlineLocation() != nullptr, + "a structured inline DILocation must form a debug-location attachment"); + rcc::MachineVariableDebugInfo VariableInfo{ + .Variable = rcc::MachineMetadataReference::embeddedIR(5), + .Expression = ExpressionValue, + .Location = DebugLocation, + }; + Context.expect(VariableInfo.verify(Metadata).has_value() && + !rcc::MachineVariableDebugInfo{}.verify(Metadata), + "variable debug records must carry at least one verified metadata component"); + + auto Undefined = + rcc::MachineDebugLocation::reference(rcc::MachineMetadataReference::machineFunction(99)); + Context.expect(!Undefined, "typed debug locations must not accept function-local tuple metadata"); +} + +void testInstructionMetadataAndClone(TestContext &Context) { + auto Target = createTarget(); + auto Function = createFunction(Context, 20, "debug-metadata"); + if (!Target || Function == nullptr) + return; + Context.expect( + Function->metadata().addNode({.Slot = 0, .IsDistinct = false, .Elements = {}}).has_value(), + "instruction metadata graph setup must succeed"); + rcc::MachineBasicBlock *Block = appendBlock(Context, *Function); + if (Block == nullptr) + return; + rcc::MachineInstr *Instruction = appendInstruction(Context, *Function, *Block, ValueOpcode); + if (Instruction == nullptr) + return; + + auto MetadataValue = + rcc::MachineMetadataValue::reference(rcc::MachineMetadataReference::machineFunction(0)); + Context.expect(MetadataValue.has_value(), "instruction metadata reference must be valid"); + if (!MetadataValue) + return; + auto Operand = rcc::MachineOperand::metadata(std::move(*MetadataValue)); + auto InlineLocation = rcc::MachineDILocation::create({ + { + .Line = 12, + .Column = 8, + .Scope = rcc::MachineMetadataReference::embeddedIR(7), + .IsImplicitCode = false, + .AtomGroup = 0, + .AtomRank = 0, + }, + }); + Context.expect(Operand.has_value() && InlineLocation.has_value(), + "instruction metadata values must be constructible"); + if (!Operand || !InlineLocation) + return; + const rcc::MachineDebugLocation DebugLocation = + rcc::MachineDebugLocation::inlineLocation(*InlineLocation); + Context.expect(Instruction->addOperand(std::move(*Operand)).has_value() && + Instruction->setDebugLocation(DebugLocation).has_value(), + "instruction metadata operand and debug location must attach independently"); + Instruction->setSourceLocation({"debug-input.mir", 3, 5, 0}); + const rcc::MachineVariableDebugInfo StackDebugInfo{ + .Variable = rcc::MachineMetadataReference::embeddedIR(8), + .Expression = rcc::MachineMetadataValue::expression( + rcc::MachineDIExpression(std::vector<std::uint64_t>{0x1000, 0, 64})), + .Location = DebugLocation, + }; + rcc::StackObjectDesc StackDescription; + StackDescription.Size = 8; + StackDescription.Align = *rcc::Alignment::create(8); + StackDescription.DebugInfo = StackDebugInfo; + auto StackIndex = Function->frameInfo().createObject(std::move(StackDescription)); + Context.expect(StackIndex.has_value(), + "structured variable debug information must attach to a stack object"); + if (!StackIndex) + return; + Context.expect(rcc::MachineVerifier::verify(*Function, *Target).has_value(), + "well-formed instruction metadata must pass unified verification"); + + auto Clone = + rcc::cloneMachineFunction(*Function, *Target, + { + .NewStableID = rcc::MachineFunctionStableID::fromValue(21), + .NewName = std::string("debug-metadata.clone"), + }); + Context.expect(Clone.has_value(), "instruction debug metadata must support deep cloning"); + if (!Clone) + return; + const rcc::MachineInstr *ClonedInstruction = + Clone->ClonedFunction->firstBlock()->firstInstruction(); + const auto ClonedStackIndex = Clone->FrameIndices.find(*StackIndex); + const rcc::StackObject *ClonedStack = + ClonedStackIndex == Clone->FrameIndices.end() + ? nullptr + : Clone->ClonedFunction->frameInfo().object(ClonedStackIndex->second); + Context.expect( + ClonedInstruction->debugLocation() == DebugLocation && + ClonedInstruction->sourceLocation() == rcc::SourceLocation{"debug-input.mir", 3, 5, 0} && + ClonedInstruction->operandAt(0)->getIf<rcc::MetadataOperand>()->Value.reference() != + nullptr && + ClonedStack != nullptr && ClonedStack->description() != nullptr && + ClonedStack->description()->DebugInfo == StackDebugInfo, + "clone must preserve distinct parser source and semantic debug information"); + + auto InvalidFunction = createFunction(Context, 22, "invalid-debug-metadata"); + if (InvalidFunction == nullptr) + return; + rcc::MachineBasicBlock *InvalidBlock = appendBlock(Context, *InvalidFunction); + if (InvalidBlock == nullptr) + return; + rcc::MachineInstr *InvalidInstruction = + appendInstruction(Context, *InvalidFunction, *InvalidBlock, ValueOpcode); + if (InvalidInstruction == nullptr) + return; + auto InvalidValue = + rcc::MachineMetadataValue::reference(rcc::MachineMetadataReference::machineFunction(9)); + Context.expect(InvalidValue.has_value(), "undefined scoped references remain representable"); + if (!InvalidValue) + return; + auto InvalidOperand = rcc::MachineOperand::metadata(std::move(*InvalidValue)); + Context.expect(InvalidOperand.has_value() && + InvalidInstruction->addOperand(std::move(*InvalidOperand)).has_value() && + !rcc::MachineVerifier::verify(*InvalidFunction, *Target), + "undefined instruction metadata references must fail unified verification"); + + auto InvalidFrameFunction = createFunction(Context, 23, "invalid-frame-debug-metadata"); + if (InvalidFrameFunction == nullptr) + return; + rcc::StackObjectDesc InvalidStackDescription; + InvalidStackDescription.Size = 8; + InvalidStackDescription.Align = *rcc::Alignment::create(8); + InvalidStackDescription.DebugInfo = rcc::MachineVariableDebugInfo{ + .Variable = rcc::MachineMetadataReference::machineFunction(9), + .Expression = std::nullopt, + .Location = std::nullopt, + }; + Context.expect( + !InvalidFrameFunction->frameInfo().createObject(std::move(InvalidStackDescription)), + "typed stack debug information must reject function-local tuple metadata"); +} + void testInstructionNumbering(TestContext &Context) { auto Target = createTarget(); auto Function = createFunction(Context, 1, "debug-numbering"); @@ -261,6 +480,8 @@ void testVerifierFailures(TestContext &Context) { int main() { TestContext Context; + testMetadataValuesAndLocations(Context); + testInstructionMetadataAndClone(Context); testInstructionNumbering(Context); testSubstitutionsAndClone(Context); testVerifierFailures(Context); diff --git a/backend/unittests/CodeGen/MachineFrameInfoTest.cpp b/backend/unittests/CodeGen/MachineFrameInfoTest.cpp index 7368815..c9289c7 100644 --- a/backend/unittests/CodeGen/MachineFrameInfoTest.cpp +++ b/backend/unittests/CodeGen/MachineFrameInfoTest.cpp @@ -2,8 +2,11 @@ #include "rcc/CodeGen/MachineFrameInfo.h" +#include "rcc/CodeGen/MachineDebugInfo.h" + #include <cstdint> #include <iostream> +#include <optional> #include <string_view> namespace { @@ -130,6 +133,22 @@ void testInvalidDescriptions(TestContext &Context) { Context.expect(!Frame.createObject(InvalidLifetime), "stack object lifetime must reject equal boundaries"); + auto InvalidDebugInfo = fixedObject(8, 8); + InvalidDebugInfo.DebugInfo.emplace(); + Context.expect(!Frame.createObject(InvalidDebugInfo), + "stack object debug information must not be structurally empty"); + + auto DebugObject = fixedObject(8, 8); + DebugObject.DebugInfo = rcc::MachineVariableDebugInfo{ + .Variable = rcc::MachineMetadataReference::embeddedIR(1), + .Expression = rcc::MachineMetadataValue::expression(rcc::MachineDIExpression{}), + .Location = std::nullopt, + }; + auto DebugIndex = Frame.createObject(DebugObject); + Context.expect(DebugIndex.has_value() && + Frame.object(*DebugIndex)->description()->DebugInfo == DebugObject.DebugInfo, + "stack object debug information must remain structured"); + Context.expect(!rcc::StackID::targetStack(4), "target stack IDs must not overlap built-in IDs"); auto TargetStack = rcc::StackID::targetStack(256); auto Target = fixedObject(8, 8); diff --git a/backend/unittests/CodeGen/MachineFunctionRecordsTest.cpp b/backend/unittests/CodeGen/MachineFunctionRecordsTest.cpp index 4f41b39..8ca6bf3 100644 --- a/backend/unittests/CodeGen/MachineFunctionRecordsTest.cpp +++ b/backend/unittests/CodeGen/MachineFunctionRecordsTest.cpp @@ -5,6 +5,7 @@ #include "Target/TestTarget.h" #include "rcc/CodeGen/InstructionDescriptor.h" #include "rcc/CodeGen/MachineBasicBlock.h" +#include "rcc/CodeGen/MachineDebugInfo.h" #include "rcc/CodeGen/MachineFunction.h" #include "rcc/CodeGen/MachineFunctionCloner.h" #include "rcc/CodeGen/MachineInstr.h" @@ -14,6 +15,7 @@ #include <cstdint> #include <iostream> #include <memory> +#include <optional> #include <string> #include <string_view> #include <utility> @@ -120,6 +122,8 @@ void testValueValidation(TestContext &Context) { .Flags = 0, }), "a called-global record must require a nonempty callee"); + Context.expect(!Records.addEntryValue({}), + "an entry-value record must require a physical register and debug metadata"); const rcc::MachinePrefetchTarget Target{ .Block = {.BaseID = 7, .CloneID = 3}, @@ -135,6 +139,9 @@ void testVerifierAndClone(TestContext &Context) { auto Function = createFunction(Context, 1, "records"); if (!Target || Function == nullptr) return; + Context.expect( + Function->metadata().addNode({.Slot = 0, .IsDistinct = false, .Elements = {}}).has_value(), + "function record metadata setup must succeed"); rcc::MachineBasicBlock *Block = appendBlock(Context, *Function); if (Block == nullptr) @@ -175,6 +182,22 @@ void testVerifierAndClone(TestContext &Context) { }) .has_value(), "structured prefetch target information must be stored"); + auto EntryLocation = + rcc::MachineDebugLocation::reference(rcc::MachineMetadataReference::embeddedIR(12)); + Context.expect(EntryLocation.has_value(), "entry-value debug location must be representable"); + if (!EntryLocation) + return; + const rcc::MachineEntryValueInfo EntryValue{ + .Reg = G0, + .DebugInfo = + { + .Variable = rcc::MachineMetadataReference::embeddedIR(10), + .Expression = rcc::MachineMetadataValue::expression(rcc::MachineDIExpression{}), + .Location = *EntryLocation, + }, + }; + Context.expect(Function->records().addEntryValue(EntryValue).has_value(), + "structured entry-value debug information must be stored"); Context.expect(rcc::MachineVerifier::verify(*Function, *Target).has_value(), "well-formed function records must pass unified verification"); @@ -190,24 +213,25 @@ void testVerifierAndClone(TestContext &Context) { const auto InstructionID = Clone->InstructionStableIDs.find(Call->stableID()); const rcc::MachineFunctionRecords &ClonedRecords = Clone->ClonedFunction->records(); - Context.expect(InstructionID != Clone->InstructionStableIDs.end() && - ClonedRecords.hasCalleeSavedRegisters() && - ClonedRecords.calleeSavedRegisters().size() == 2 && - ClonedRecords.callSites().size() == 1 && - ClonedRecords.callSites()[0].CallInstruction == InstructionID->second && - ClonedRecords.callSites()[0].ForwardedArguments.size() == 2 && - ClonedRecords.callSites()[0].ForwardedArguments[1].ArgumentNumber == 0 && - ClonedRecords.callSites()[0].CalleeTypeIDs == - std::vector<std::uint64_t>({0x11, 0x22}) && - ClonedRecords.calledGlobals().size() == 1 && - ClonedRecords.calledGlobals()[0].CallInstruction == InstructionID->second && - ClonedRecords.calledGlobals()[0].Callee == "callee" && - ClonedRecords.calledGlobals()[0].Flags == 5 && - ClonedRecords.prefetchTargets().size() == 1 && - ClonedRecords.prefetchTargets()[0].Block == - rcc::MachineBasicBlockProfileID{.BaseID = 19, .CloneID = 2} && - ClonedRecords.prefetchTargets()[0].CallsiteIndex == 8, - "deep clone must preserve records and remap instruction StableIDs"); + Context.expect( + InstructionID != Clone->InstructionStableIDs.end() && + ClonedRecords.hasCalleeSavedRegisters() && + ClonedRecords.calleeSavedRegisters().size() == 2 && + ClonedRecords.callSites().size() == 1 && + ClonedRecords.callSites()[0].CallInstruction == InstructionID->second && + ClonedRecords.callSites()[0].ForwardedArguments.size() == 2 && + ClonedRecords.callSites()[0].ForwardedArguments[1].ArgumentNumber == 0 && + ClonedRecords.callSites()[0].CalleeTypeIDs == std::vector<std::uint64_t>({0x11, 0x22}) && + ClonedRecords.calledGlobals().size() == 1 && + ClonedRecords.calledGlobals()[0].CallInstruction == InstructionID->second && + ClonedRecords.calledGlobals()[0].Callee == "callee" && + ClonedRecords.calledGlobals()[0].Flags == 5 && + ClonedRecords.prefetchTargets().size() == 1 && + ClonedRecords.prefetchTargets()[0].Block == + rcc::MachineBasicBlockProfileID{.BaseID = 19, .CloneID = 2} && + ClonedRecords.prefetchTargets()[0].CallsiteIndex == 8 && + ClonedRecords.entryValues().size() == 1 && ClonedRecords.entryValues()[0] == EntryValue, + "deep clone must preserve records and remap instruction StableIDs"); } void testCrossReferenceFailures(TestContext &Context) { @@ -306,6 +330,25 @@ void testCrossReferenceFailures(TestContext &Context) { Context.expect(!rcc::MachineVerifier::verify(*Function, *Target), "linked block bb_id values must be unique"); } + + { + auto Function = createFunction(Context, 13, "entry-value-register"); + if (Function == nullptr) + return; + Context.expect(Function->records() + .addEntryValue({ + .Reg = phys(999), + .DebugInfo = + { + .Variable = rcc::MachineMetadataReference::embeddedIR(1), + .Expression = std::nullopt, + .Location = std::nullopt, + }, + }) + .has_value() && + !rcc::MachineVerifier::verify(*Function, *Target), + "entry-value records must reject target-unknown physical registers"); + } } } // namespace diff --git a/backend/unittests/CodeGen/MachineIRBuilderTest.cpp b/backend/unittests/CodeGen/MachineIRBuilderTest.cpp index 15f6cd2..9f5cf5e 100644 --- a/backend/unittests/CodeGen/MachineIRBuilderTest.cpp +++ b/backend/unittests/CodeGen/MachineIRBuilderTest.cpp @@ -7,6 +7,7 @@ #include "rcc/CodeGen/LowLevelType.h" #include "rcc/CodeGen/MachineBasicBlock.h" #include "rcc/CodeGen/MachineConstantPool.h" +#include "rcc/CodeGen/MachineDebugInfo.h" #include "rcc/CodeGen/MachineFunction.h" #include "rcc/CodeGen/MachineInstr.h" #include "rcc/CodeGen/MachineJumpTableInfo.h" @@ -208,6 +209,7 @@ void testTransactionalBuild(TestContext &Context) { if (!Incomplete) return; Context.expect(!Incomplete->tieOperands(0, 1) && !Incomplete->setSourceLocation({"", 0, 0, 0}) && + !Incomplete->setDebugLocation({}) && !Incomplete->setOrigin(rcc::OriginID::fromValue(0)), "invalid pending metadata and tie indices must be rejected"); Context.expect(!Incomplete->build(), "descriptor validation must reject a missing operand"); @@ -225,10 +227,27 @@ void testTransactionalBuild(TestContext &Context) { "an explicit tie matching the descriptor default must be idempotent"); Context.expect(Complete->setFlags({rcc::MachineInstrFlag::FrameSetup}).has_value(), "valid instruction flags must be accepted"); + auto InlineDebugLocation = rcc::MachineDILocation::create({ + { + .Line = 11, + .Column = 2, + .Scope = rcc::MachineMetadataReference::embeddedIR(3), + .IsImplicitCode = false, + .AtomGroup = 0, + .AtomRank = 0, + }, + }); + Context.expect(InlineDebugLocation.has_value(), + "builder debug location setup must be structurally valid"); + if (!InlineDebugLocation) + return; + const rcc::MachineDebugLocation DebugLocation = + rcc::MachineDebugLocation::inlineLocation(*InlineDebugLocation); Context.expect( Complete->setSourceLocation(rcc::SourceLocation{"builder.mir", 7, 3, 1}).has_value() && + Complete->setDebugLocation(DebugLocation).has_value() && Complete->setOrigin(rcc::OriginID::fromValue(19)).has_value(), - "valid source and origin metadata must be accepted"); + "valid parser source, debug location, and origin metadata must be accepted"); auto Built = Complete->build(); Context.expect(Built.has_value(), "valid pending instruction must build"); if (!Built) @@ -244,13 +263,14 @@ void testTransactionalBuild(TestContext &Context) { "descriptor-default tie must be materialized bidirectionally"); Context.expect(Instruction->flags().has(rcc::MachineInstrFlag::FrameSetup) && Instruction->sourceLocation() == rcc::SourceLocation{"builder.mir", 7, 3, 1} && + Instruction->debugLocation() == DebugLocation && Instruction->origin() == rcc::OriginID::fromValue(19), "builder metadata must be preserved on the published instruction"); Context.expect(Target->validateInstruction(*Instruction, Function->registerInfo()).has_value(), "builder output must satisfy the shared instruction descriptor"); Context.expect(!Complete->build(), "a successful pending builder must be single-use"); Context.expect(!Complete->setFlags({}) && !Complete->clearSourceLocation() && - !Complete->clearOrigin(), + !Complete->clearDebugLocation() && !Complete->clearOrigin(), "all pending-builder mutators must reject use after publication"); Context.expect(Function->registerInfo().verifyDefUseChains().has_value(), "published builder operands must enter exact def-use chains"); diff --git a/backend/unittests/CodeGen/MachineIRCoreTest.cpp b/backend/unittests/CodeGen/MachineIRCoreTest.cpp index 1083422..028afc5 100644 --- a/backend/unittests/CodeGen/MachineIRCoreTest.cpp +++ b/backend/unittests/CodeGen/MachineIRCoreTest.cpp @@ -169,10 +169,18 @@ void testOperandKinds(TestContext &Context) { Context.expect(!rcc::MachineOperand::registerMask(nullptr), "null register mask operand must be rejected"); - auto Metadata = rcc::MachineOperand::metadata("source-marker"); - Context.expect(Metadata && Metadata->kind() == rcc::MachineOperand::Kind::Metadata, - "metadata operand must preserve its kind"); - Context.expect(!rcc::MachineOperand::metadata(""), "empty metadata marker must be rejected"); + auto MetadataReference = + rcc::MachineMetadataValue::reference(rcc::MachineMetadataReference::embeddedIR(0)); + Context.expect(MetadataReference.has_value(), + "a valid scoped metadata reference must form an operand value"); + if (!MetadataReference) + return; + auto Metadata = rcc::MachineOperand::metadata(std::move(*MetadataReference)); + Context.expect(Metadata && Metadata->kind() == rcc::MachineOperand::Kind::Metadata && + Metadata->getIf<rcc::MetadataOperand>()->Value.reference() != nullptr, + "metadata operand must preserve its scoped semantic value"); + Context.expect(!rcc::MachineOperand::metadata({}), + "an unrepresented metadata operand value must be rejected"); auto Target = rcc::MachineOperand::targetIndex(rcc::TargetIndex::fromValue(8), 9); Context.expect(Target.getIf<rcc::TargetIndexOperand>()->Offset == 9, |
