aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--backend/include/rcc/CodeGen/MachineDebugInfo.h154
-rw-r--r--backend/include/rcc/CodeGen/MachineFrameInfo.h2
-rw-r--r--backend/include/rcc/CodeGen/MachineFunctionRecords.h16
-rw-r--r--backend/include/rcc/CodeGen/MachineIRBuilder.h6
-rw-r--r--backend/include/rcc/CodeGen/MachineInstr.h11
-rw-r--r--backend/include/rcc/CodeGen/MachineMetadata.h3
-rw-r--r--backend/include/rcc/CodeGen/MachineOperand.h5
-rw-r--r--backend/lib/CodeGen/CMakeLists.txt4
-rw-r--r--backend/lib/CodeGen/MachineDebugInfo.cpp164
-rw-r--r--backend/lib/CodeGen/MachineFrameInfo.cpp5
-rw-r--r--backend/lib/CodeGen/MachineFunctionCloner.cpp6
-rw-r--r--backend/lib/CodeGen/MachineFunctionRecords.cpp27
-rw-r--r--backend/lib/CodeGen/MachineIRBuilder.cpp34
-rw-r--r--backend/lib/CodeGen/MachineInstr.cpp9
-rw-r--r--backend/lib/CodeGen/MachineMetadata.cpp13
-rw-r--r--backend/lib/CodeGen/MachineOperand.cpp9
-rw-r--r--backend/lib/CodeGen/MachineVerifier.cpp25
-rw-r--r--backend/unittests/CodeGen/MachineDebugInfoTest.cpp221
-rw-r--r--backend/unittests/CodeGen/MachineFrameInfoTest.cpp19
-rw-r--r--backend/unittests/CodeGen/MachineFunctionRecordsTest.cpp79
-rw-r--r--backend/unittests/CodeGen/MachineIRBuilderTest.cpp24
-rw-r--r--backend/unittests/CodeGen/MachineIRCoreTest.cpp16
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,