diff options
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, |
