aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--backend/include/rcc/CodeGen/MachineFrameInfo.h41
-rw-r--r--backend/include/rcc/CodeGen/MachineMemOperand.h1
-rw-r--r--backend/include/rcc/CodeGen/MachineRegisterInfo.h3
-rw-r--r--backend/include/rcc/CodeGen/TargetDescription.h23
-rw-r--r--backend/include/rcc/CodeGen/TargetMIRTextInfo.h208
-rw-r--r--backend/include/rcc/CodeGen/TargetRegisterInfo.h16
-rw-r--r--backend/lib/CodeGen/CMakeLists.txt4
-rw-r--r--backend/lib/CodeGen/MachineFrameInfo.cpp51
-rw-r--r--backend/lib/CodeGen/MachineFunctionCloner.cpp6
-rw-r--r--backend/lib/CodeGen/MachineRegisterInfo.cpp13
-rw-r--r--backend/lib/CodeGen/MachineVerifier.cpp13
-rw-r--r--backend/lib/CodeGen/TargetDescription.cpp134
-rw-r--r--backend/lib/CodeGen/TargetMIRTextInfo.cpp368
-rw-r--r--backend/lib/CodeGen/TargetRegisterInfo.cpp75
-rw-r--r--backend/unittests/CodeGen/CMakeLists.txt15
-rw-r--r--backend/unittests/CodeGen/MachineFrameInfoTest.cpp21
-rw-r--r--backend/unittests/CodeGen/MachineFunctionClonerTest.cpp7
-rw-r--r--backend/unittests/CodeGen/TargetMIRTextInfoTest.cpp371
-rw-r--r--backend/unittests/Target/TestTarget.cpp41
-rw-r--r--backend/unittests/Target/TestTarget.h2
20 files changed, 1363 insertions, 50 deletions
diff --git a/backend/include/rcc/CodeGen/MachineFrameInfo.h b/backend/include/rcc/CodeGen/MachineFrameInfo.h
index b837ff3..7c42bc2 100644
--- a/backend/include/rcc/CodeGen/MachineFrameInfo.h
+++ b/backend/include/rcc/CodeGen/MachineFrameInfo.h
@@ -12,6 +12,7 @@
#include <cstdint>
#include <optional>
#include <span>
+#include <string_view>
#include <utility>
#include <variant>
#include <vector>
@@ -38,46 +39,58 @@ public:
return StackID(DefaultStackValue);
}
- [[nodiscard]] static constexpr StackID safeStack() noexcept {
- return StackID(SafeStackValue);
- }
-
- [[nodiscard]] static constexpr StackID shadowCallStack() noexcept {
- return StackID(ShadowCallStackValue);
+ [[nodiscard]] static constexpr StackID sgprSpillStack() noexcept {
+ return StackID(SGPRSpillStackValue);
}
[[nodiscard]] static constexpr StackID scalableVectorStack() noexcept {
return StackID(ScalableVectorStackValue);
}
- [[nodiscard]] static Expected<StackID> targetStack(std::uint32_t Value);
+ [[nodiscard]] static constexpr StackID wasmLocalStack() noexcept {
+ return StackID(WasmLocalStackValue);
+ }
+
+ [[nodiscard]] static constexpr StackID scalablePredicateVectorStack() noexcept {
+ return StackID(ScalablePredicateVectorStackValue);
+ }
+
+ [[nodiscard]] static constexpr StackID noAllocStack() noexcept {
+ return StackID(NoAllocStackValue);
+ }
+
+ [[nodiscard]] static Expected<StackID> fromValue(std::uint32_t Value);
[[nodiscard]] constexpr std::uint32_t value() const noexcept {
return Value;
}
- [[nodiscard]] constexpr bool isTargetStack() const noexcept {
- return Value >= FirstTargetStackValue;
+ [[nodiscard]] constexpr bool isSpecialStack() const noexcept {
+ return Value != DefaultStackValue;
}
[[nodiscard]] constexpr bool isValid() const noexcept {
- return Value <= ScalableVectorStackValue || isTargetStack();
+ return Value <= ScalablePredicateVectorStackValue || Value == NoAllocStackValue;
}
friend constexpr bool operator==(StackID Left, StackID Right) noexcept = default;
private:
static constexpr std::uint32_t DefaultStackValue = 0;
- static constexpr std::uint32_t SafeStackValue = 1;
- static constexpr std::uint32_t ShadowCallStackValue = 2;
- static constexpr std::uint32_t ScalableVectorStackValue = 3;
- static constexpr std::uint32_t FirstTargetStackValue = 256;
+ static constexpr std::uint32_t SGPRSpillStackValue = 1;
+ static constexpr std::uint32_t ScalableVectorStackValue = 2;
+ static constexpr std::uint32_t WasmLocalStackValue = 3;
+ static constexpr std::uint32_t ScalablePredicateVectorStackValue = 4;
+ static constexpr std::uint32_t NoAllocStackValue = 255;
explicit constexpr StackID(std::uint32_t Value) noexcept : Value(Value) {}
std::uint32_t Value = DefaultStackValue;
};
+[[nodiscard]] std::optional<StackID> parseStackIDName(std::string_view Name) noexcept;
+[[nodiscard]] std::string_view getStackIDName(StackID ID) noexcept;
+
struct StackObjectLifetimeRange final {
MachineInstrStableID Begin;
MachineInstrStableID End;
diff --git a/backend/include/rcc/CodeGen/MachineMemOperand.h b/backend/include/rcc/CodeGen/MachineMemOperand.h
index 3f1fc64..b6cb07f 100644
--- a/backend/include/rcc/CodeGen/MachineMemOperand.h
+++ b/backend/include/rcc/CodeGen/MachineMemOperand.h
@@ -182,6 +182,7 @@ struct MachineMemOperandDesc final {
std::int64_t Offset = 0;
std::optional<MemoryOffsetRange> OffsetRange;
std::optional<MemoryAliasSourceID> AliasSource;
+ std::uint16_t TargetFlags = 0;
friend bool operator==(const MachineMemOperandDesc &Left,
const MachineMemOperandDesc &Right) = default;
diff --git a/backend/include/rcc/CodeGen/MachineRegisterInfo.h b/backend/include/rcc/CodeGen/MachineRegisterInfo.h
index da0f530..b6485e3 100644
--- a/backend/include/rcc/CodeGen/MachineRegisterInfo.h
+++ b/backend/include/rcc/CodeGen/MachineRegisterInfo.h
@@ -83,6 +83,7 @@ struct VirtualRegisterInfo final {
std::optional<RegisterBankID> Bank;
std::optional<RegisterClassID> RegClass;
std::vector<PhysicalRegister> AllocationHints;
+ std::uint8_t TargetFlags = 0;
bool IsTombstone = false;
};
@@ -123,6 +124,8 @@ public:
[[nodiscard]] Expected<void> setRegisterClass(Register Reg, RegisterClassID RegClass);
[[nodiscard]] Expected<void> clearRegisterClass(Register Reg);
[[nodiscard]] Expected<void> addAllocationHint(Register Reg, PhysicalRegister Hint);
+ [[nodiscard]] Expected<void> setTargetRegisterFlags(Register Reg, std::uint8_t Flags);
+ [[nodiscard]] Expected<void> clearTargetRegisterFlags(Register Reg);
void clearAllVirtualRegisterTypes() noexcept;
[[nodiscard]] Expected<void>
diff --git a/backend/include/rcc/CodeGen/TargetDescription.h b/backend/include/rcc/CodeGen/TargetDescription.h
index 51a820a..c2a4133 100644
--- a/backend/include/rcc/CodeGen/TargetDescription.h
+++ b/backend/include/rcc/CodeGen/TargetDescription.h
@@ -4,6 +4,7 @@
#define RCC_CODEGEN_TARGETDESCRIPTION_H
#include "rcc/CodeGen/InstructionDescriptor.h"
+#include "rcc/CodeGen/TargetMIRTextInfo.h"
#include "rcc/CodeGen/TargetRegisterInfo.h"
#include "rcc/Support/Error.h"
@@ -15,6 +16,8 @@
namespace rcc {
class MachineBasicBlock;
+class MachineCFIInfo;
+class MachineFrameInfo;
class MachineInstr;
class MachineMemOperand;
class MachineRegisterInfo;
@@ -29,7 +32,8 @@ public:
~TargetDescription() = default;
[[nodiscard]] static Expected<TargetDescription> create(InstructionSet Instructions,
- TargetRegisterInfo Registers);
+ TargetRegisterInfo Registers,
+ TargetMIRTextInfoDesc MIRText = {});
[[nodiscard]] const InstructionSet &instructions() const noexcept {
return Instructions;
@@ -39,6 +43,10 @@ public:
return Registers;
}
+ [[nodiscard]] const TargetMIRTextInfo &mirText() const noexcept {
+ return MIRText;
+ }
+
[[nodiscard]] const InstructionDescriptor *
instructionDescriptor(Opcode InstructionOpcode) const noexcept {
return Instructions.descriptor(InstructionOpcode);
@@ -61,11 +69,11 @@ public:
std::span<const MachineOperandTie> Ties) const;
[[nodiscard]] Expected<void>
validateMemOperands(Opcode InstructionOpcode,
- std::span<const MachineMemOperand *const> MemOperands) const {
- return Instructions.validateMemOperands(InstructionOpcode, MemOperands);
- }
+ std::span<const MachineMemOperand *const> MemOperands) const;
[[nodiscard]] Expected<void> validateInstruction(const MachineInstr &Instruction,
const MachineRegisterInfo &RegInfo) const;
+ [[nodiscard]] Expected<void> validateCFIInfo(const MachineCFIInfo &Info) const;
+ [[nodiscard]] Expected<void> validateFrameInfo(const MachineFrameInfo &Info) const;
[[nodiscard]] Expected<void> validateRegisterInfo(const MachineRegisterInfo &RegInfo) const;
[[nodiscard]] Expected<void> validateBlockLiveIns(const MachineBasicBlock &Block) const;
@@ -75,8 +83,10 @@ public:
}
private:
- TargetDescription(InstructionSet Instructions, TargetRegisterInfo Registers)
- : Instructions(std::move(Instructions)), Registers(std::move(Registers)) {}
+ TargetDescription(InstructionSet Instructions, TargetRegisterInfo Registers,
+ TargetMIRTextInfo MIRText)
+ : Instructions(std::move(Instructions)), Registers(std::move(Registers)),
+ MIRText(std::move(MIRText)) {}
[[nodiscard]] Expected<void> validateTargetOperand(const OperandDescriptor *Descriptor,
const MachineOperand &Operand,
@@ -91,6 +101,7 @@ private:
InstructionSet Instructions;
TargetRegisterInfo Registers;
+ TargetMIRTextInfo MIRText;
};
} // namespace rcc
diff --git a/backend/include/rcc/CodeGen/TargetMIRTextInfo.h b/backend/include/rcc/CodeGen/TargetMIRTextInfo.h
new file mode 100644
index 0000000..4fdbb3a
--- /dev/null
+++ b/backend/include/rcc/CodeGen/TargetMIRTextInfo.h
@@ -0,0 +1,208 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#ifndef RCC_CODEGEN_TARGETMIRTEXTINFO_H
+#define RCC_CODEGEN_TARGETMIRTEXTINFO_H
+
+#include "rcc/CodeGen/MachineFrameInfo.h"
+#include "rcc/CodeGen/MachineIRTypes.h"
+#include "rcc/CodeGen/Register.h"
+#include "rcc/CodeGen/RegisterMask.h"
+#include "rcc/Support/Error.h"
+
+#include <cstdint>
+#include <memory>
+#include <optional>
+#include <span>
+#include <string>
+#include <string_view>
+#include <unordered_map>
+#include <unordered_set>
+#include <utility>
+#include <vector>
+
+namespace rcc {
+
+class TargetRegisterInfo;
+
+struct MIRRegisterMaskDesc final {
+ std::string Name;
+ std::shared_ptr<const RegisterMask> Mask;
+};
+
+struct MIRTargetIndexDesc final {
+ std::string Name;
+ TargetIndex Index;
+};
+
+struct MIROperandFlagDesc final {
+ std::string Name;
+ std::uint32_t Value = 0;
+};
+
+struct MIRMachineMemOperandFlagDesc final {
+ std::string Name;
+ std::uint16_t Value = 0;
+};
+
+struct MIRVirtualRegisterFlagDesc final {
+ std::string Name;
+ std::uint8_t Value = 0;
+};
+
+struct MIRIntrinsicDesc final {
+ std::string Name;
+ IntrinsicID ID;
+};
+
+struct MIRCFIRegisterMappingDesc final {
+ PhysicalRegister Register;
+ std::uint32_t DwarfRegister = 0;
+ bool IsCanonical = true;
+};
+
+struct TargetMIRTextInfoDesc final {
+ std::vector<MIRRegisterMaskDesc> RegisterMasks;
+ std::vector<MIRTargetIndexDesc> TargetIndices;
+ std::uint32_t DirectOperandFlagMask = 0;
+ std::vector<MIROperandFlagDesc> DirectOperandFlags;
+ std::vector<MIROperandFlagDesc> BitmaskOperandFlags;
+ std::vector<MIRMachineMemOperandFlagDesc> MachineMemOperandFlags;
+ std::vector<MIRVirtualRegisterFlagDesc> VirtualRegisterFlags;
+ std::vector<MIRIntrinsicDesc> Intrinsics;
+ std::vector<MIRCFIRegisterMappingDesc> CFIRegisters;
+ std::vector<StackID> SupportedStackIDs{
+ StackID::defaultStack(),
+ StackID::noAllocStack(),
+ };
+ std::optional<TargetFunctionStateTypeID> FunctionStateType;
+};
+
+class TargetMIRTextInfo final {
+public:
+ TargetMIRTextInfo(const TargetMIRTextInfo &) = delete;
+ TargetMIRTextInfo &operator=(const TargetMIRTextInfo &) = delete;
+ TargetMIRTextInfo(TargetMIRTextInfo &&) noexcept = default;
+ TargetMIRTextInfo &operator=(TargetMIRTextInfo &&) noexcept = default;
+ ~TargetMIRTextInfo() = default;
+
+ [[nodiscard]] static Expected<TargetMIRTextInfo> create(TargetMIRTextInfoDesc Description,
+ const TargetRegisterInfo &Registers);
+
+ [[nodiscard]] std::span<const MIRRegisterMaskDesc> registerMasks() const noexcept {
+ return Description.RegisterMasks;
+ }
+
+ [[nodiscard]] std::span<const MIRTargetIndexDesc> targetIndices() const noexcept {
+ return Description.TargetIndices;
+ }
+
+ [[nodiscard]] std::span<const MIROperandFlagDesc> directOperandFlags() const noexcept {
+ return Description.DirectOperandFlags;
+ }
+
+ [[nodiscard]] std::span<const MIROperandFlagDesc> bitmaskOperandFlags() const noexcept {
+ return Description.BitmaskOperandFlags;
+ }
+
+ [[nodiscard]] std::span<const MIRMachineMemOperandFlagDesc>
+ machineMemOperandFlags() const noexcept {
+ return Description.MachineMemOperandFlags;
+ }
+
+ [[nodiscard]] std::span<const MIRVirtualRegisterFlagDesc> virtualRegisterFlags() const noexcept {
+ return Description.VirtualRegisterFlags;
+ }
+
+ [[nodiscard]] std::span<const MIRIntrinsicDesc> intrinsics() const noexcept {
+ return Description.Intrinsics;
+ }
+
+ [[nodiscard]] std::shared_ptr<const RegisterMask>
+ registerMask(std::string_view Name) const noexcept;
+ [[nodiscard]] std::optional<std::string_view>
+ registerMaskName(const RegisterMask &Mask) const noexcept;
+
+ [[nodiscard]] std::optional<TargetIndex> targetIndex(std::string_view Name) const noexcept;
+ [[nodiscard]] std::optional<std::string_view> targetIndexName(TargetIndex Index) const noexcept;
+
+ [[nodiscard]] std::optional<std::uint32_t>
+ directOperandFlag(std::string_view Name) const noexcept;
+ [[nodiscard]] std::optional<std::uint32_t>
+ bitmaskOperandFlag(std::string_view Name) const noexcept;
+ [[nodiscard]] Expected<std::vector<std::string_view>> operandFlagNames(std::uint32_t Flags) const;
+ [[nodiscard]] Expected<void> validateOperandFlags(std::uint32_t Flags) const;
+
+ [[nodiscard]] std::optional<std::uint16_t>
+ machineMemOperandFlag(std::string_view Name) const noexcept;
+ [[nodiscard]] Expected<std::vector<std::string_view>>
+ machineMemOperandFlagNames(std::uint16_t Flags) const;
+ [[nodiscard]] Expected<void> validateMachineMemOperandFlags(std::uint16_t Flags) const;
+
+ [[nodiscard]] std::optional<std::uint8_t>
+ virtualRegisterFlag(std::string_view Name) const noexcept;
+ [[nodiscard]] Expected<std::vector<std::string_view>>
+ virtualRegisterFlagNames(std::uint8_t Flags) const;
+ [[nodiscard]] Expected<void> validateVirtualRegisterFlags(std::uint8_t Flags) const;
+
+ [[nodiscard]] std::optional<IntrinsicID> intrinsic(std::string_view Name) const noexcept;
+ [[nodiscard]] std::optional<std::string_view> intrinsicName(IntrinsicID ID) const noexcept;
+
+ [[nodiscard]] std::optional<std::uint32_t>
+ dwarfRegister(PhysicalRegister Register) const noexcept;
+ [[nodiscard]] std::optional<PhysicalRegister>
+ physicalRegister(std::uint32_t DwarfRegister) const noexcept;
+
+ [[nodiscard]] std::span<const StackID> supportedStackIDs() const noexcept {
+ return Description.SupportedStackIDs;
+ }
+
+ [[nodiscard]] bool supportsStackID(StackID ID) const noexcept {
+ return SupportedStackIDs.contains(ID.value());
+ }
+
+ [[nodiscard]] std::uint32_t directOperandFlagMask() const noexcept {
+ return Description.DirectOperandFlagMask;
+ }
+
+ [[nodiscard]] std::uint32_t serializableOperandFlagMask() const noexcept {
+ return SerializableOperandFlagMask;
+ }
+
+ [[nodiscard]] std::uint16_t serializableMachineMemOperandFlagMask() const noexcept {
+ return SerializableMachineMemOperandFlagMask;
+ }
+
+ [[nodiscard]] std::uint8_t serializableVirtualRegisterFlagMask() const noexcept {
+ return SerializableVirtualRegisterFlagMask;
+ }
+
+ [[nodiscard]] std::optional<TargetFunctionStateTypeID> functionStateType() const noexcept {
+ return Description.FunctionStateType;
+ }
+
+private:
+ explicit TargetMIRTextInfo(TargetMIRTextInfoDesc Description)
+ : Description(std::move(Description)) {}
+
+ TargetMIRTextInfoDesc Description;
+ std::unordered_map<std::string, std::size_t> RegisterMaskNameToIndex;
+ std::unordered_map<std::string, std::size_t> TargetIndexNameToIndex;
+ std::unordered_map<TargetIndex, std::size_t> TargetIndexToIndex;
+ std::unordered_map<std::string, std::size_t> DirectOperandFlagNameToIndex;
+ std::unordered_map<std::uint32_t, std::size_t> DirectOperandFlagToIndex;
+ std::unordered_map<std::string, std::size_t> BitmaskOperandFlagNameToIndex;
+ std::unordered_map<std::string, std::size_t> MachineMemOperandFlagNameToIndex;
+ std::unordered_map<std::string, std::size_t> VirtualRegisterFlagNameToIndex;
+ std::unordered_map<std::string, std::size_t> IntrinsicNameToIndex;
+ std::unordered_map<IntrinsicID, std::size_t> IntrinsicToIndex;
+ std::unordered_map<PhysicalRegister, std::uint32_t> RegisterToDwarf;
+ std::unordered_map<std::uint32_t, PhysicalRegister> DwarfToRegister;
+ std::unordered_set<std::uint32_t> SupportedStackIDs;
+ std::uint32_t SerializableOperandFlagMask = 0;
+ std::uint16_t SerializableMachineMemOperandFlagMask = 0;
+ std::uint8_t SerializableVirtualRegisterFlagMask = 0;
+};
+
+} // namespace rcc
+
+#endif // RCC_CODEGEN_TARGETMIRTEXTINFO_H
diff --git a/backend/include/rcc/CodeGen/TargetRegisterInfo.h b/backend/include/rcc/CodeGen/TargetRegisterInfo.h
index 5d7de2c..9357eb2 100644
--- a/backend/include/rcc/CodeGen/TargetRegisterInfo.h
+++ b/backend/include/rcc/CodeGen/TargetRegisterInfo.h
@@ -129,8 +129,18 @@ public:
[[nodiscard]] const PhysicalRegisterDesc *physicalRegister(PhysicalRegister Reg) const noexcept;
[[nodiscard]] const PhysicalRegisterDesc *physicalRegister(std::string_view Name) const noexcept;
[[nodiscard]] const SubRegIndexDesc *subRegIndex(SubRegIndex Index) const noexcept;
+ [[nodiscard]] const SubRegIndexDesc *subRegIndex(std::string_view Name) const noexcept;
[[nodiscard]] const RegisterClassDesc *registerClass(RegisterClassID ID) const noexcept;
+ [[nodiscard]] const RegisterClassDesc *registerClass(std::string_view Name) const noexcept;
[[nodiscard]] const RegisterBankDesc *registerBank(RegisterBankID ID) const noexcept;
+ [[nodiscard]] const RegisterBankDesc *registerBank(std::string_view Name) const noexcept;
+
+ [[nodiscard]] std::optional<std::string_view>
+ physicalRegisterMIRName(PhysicalRegister Reg) const noexcept;
+ [[nodiscard]] std::optional<std::string_view>
+ registerClassMIRName(RegisterClassID ID) const noexcept;
+ [[nodiscard]] std::optional<std::string_view>
+ registerBankMIRName(RegisterBankID ID) const noexcept;
[[nodiscard]] bool isKnownPhysicalRegister(PhysicalRegister Reg) const noexcept {
return physicalRegister(Reg) != nullptr;
@@ -177,14 +187,20 @@ private:
std::unordered_map<PhysicalRegister, std::size_t> RegisterToIndex;
std::unordered_map<std::string, std::size_t> RegisterNameToIndex;
std::unordered_map<SubRegIndex, std::size_t> SubRegIndexToIndex;
+ std::unordered_map<std::string, std::size_t> SubRegNameToIndex;
std::unordered_map<RegisterClassID, std::size_t> ClassToIndex;
+ std::unordered_map<std::string, std::size_t> ClassNameToIndex;
std::unordered_map<RegisterBankID, std::size_t> BankToIndex;
+ std::unordered_map<std::string, std::size_t> BankNameToIndex;
std::unordered_map<std::uint64_t, PhysicalRegister> SubRegisterMap;
std::unordered_map<std::uint64_t, SubRegIndex> CompositionMap;
std::unordered_map<std::uint64_t, RegisterClassID> SubRegClassMap;
std::unordered_set<std::uint64_t> SubclassRelations;
std::vector<std::vector<PhysicalRegister>> Aliases;
std::vector<std::vector<PhysicalRegister>> SuperRegisters;
+ std::vector<std::string> RegisterMIRNames;
+ std::vector<std::string> ClassMIRNames;
+ std::vector<std::string> BankMIRNames;
};
} // namespace rcc
diff --git a/backend/lib/CodeGen/CMakeLists.txt b/backend/lib/CodeGen/CMakeLists.txt
index d0f725d..ce53cd2 100644
--- a/backend/lib/CodeGen/CMakeLists.txt
+++ b/backend/lib/CodeGen/CMakeLists.txt
@@ -32,6 +32,7 @@ set(
TargetConstant.cpp
TargetDescription.cpp
TargetFunctionState.cpp
+ TargetMIRTextInfo.cpp
TargetRegisterInfo.cpp
)
set(
@@ -67,6 +68,7 @@ set(
"${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/TargetConstant.h"
"${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/TargetDescription.h"
"${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/TargetFunctionState.h"
+ "${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/TargetMIRTextInfo.h"
"${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/TargetRegisterInfo.h"
)
@@ -129,6 +131,7 @@ rcc_backend_register_format_files(
"${CMAKE_CURRENT_SOURCE_DIR}/TargetConstant.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/TargetDescription.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/TargetFunctionState.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/TargetMIRTextInfo.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/TargetRegisterInfo.cpp"
${RCCBackendCodeGenHeaders}
)
@@ -163,5 +166,6 @@ rcc_backend_register_tidy_files(
"${CMAKE_CURRENT_SOURCE_DIR}/TargetConstant.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/TargetDescription.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/TargetFunctionState.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/TargetMIRTextInfo.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/TargetRegisterInfo.cpp"
)
diff --git a/backend/lib/CodeGen/MachineFrameInfo.cpp b/backend/lib/CodeGen/MachineFrameInfo.cpp
index c4d3cda..23f9852 100644
--- a/backend/lib/CodeGen/MachineFrameInfo.cpp
+++ b/backend/lib/CodeGen/MachineFrameInfo.cpp
@@ -5,17 +5,49 @@
#include <cstddef>
#include <cstdint>
#include <limits>
+#include <string_view>
#include <unordered_set>
#include <utility>
namespace rcc {
-Expected<StackID> StackID::targetStack(std::uint32_t Value) {
- if (Value < FirstTargetStackValue) {
- return makeUnexpected(ErrorCode::InvalidArgument,
- "target stack ID must not overlap a built-in stack ID");
- }
- return StackID(Value);
+Expected<StackID> StackID::fromValue(std::uint32_t Value) {
+ StackID Result(Value);
+ if (!Result.isValid())
+ return makeUnexpected(ErrorCode::InvalidArgument, "stack ID is not serializable in LLVM MIR");
+ return Result;
+}
+
+std::optional<StackID> parseStackIDName(std::string_view Name) noexcept {
+ if (Name == "default")
+ return StackID::defaultStack();
+ if (Name == "sgpr-spill")
+ return StackID::sgprSpillStack();
+ if (Name == "scalable-vector")
+ return StackID::scalableVectorStack();
+ if (Name == "scalable-predicate-vector")
+ return StackID::scalablePredicateVectorStack();
+ if (Name == "wasm-local")
+ return StackID::wasmLocalStack();
+ if (Name == "noalloc")
+ return StackID::noAllocStack();
+ return std::nullopt;
+}
+
+std::string_view getStackIDName(StackID ID) noexcept {
+ if (ID == StackID::defaultStack())
+ return "default";
+ if (ID == StackID::sgprSpillStack())
+ return "sgpr-spill";
+ if (ID == StackID::scalableVectorStack())
+ return "scalable-vector";
+ if (ID == StackID::scalablePredicateVectorStack())
+ return "scalable-predicate-vector";
+ if (ID == StackID::wasmLocalStack())
+ return "wasm-local";
+ if (ID == StackID::noAllocStack())
+ return "noalloc";
+ return "unknown";
}
bool StackObject::isSpill() const noexcept {
@@ -59,10 +91,11 @@ Expected<void> MachineFrameInfo::verifyDescription(const StackObjectDesc &Descri
return makeUnexpected(ErrorCode::InvalidArgument,
"fixed ABI stack objects require a fixed offset");
}
- if (Description.Kind == StackObjectKind::Target && !Description.Stack.isTargetStack() &&
+ if (Description.Kind == StackObjectKind::Target && !Description.Stack.isSpecialStack() &&
Description.TargetFlags == 0) {
- return makeUnexpected(ErrorCode::InvalidArgument,
- "target stack objects require a target stack ID or nonzero target flags");
+ return makeUnexpected(
+ ErrorCode::InvalidArgument,
+ "target stack objects require a special stack ID or nonzero target flags");
}
std::unordered_set<MachineInstrStableID> LifetimeBoundaries;
diff --git a/backend/lib/CodeGen/MachineFunctionCloner.cpp b/backend/lib/CodeGen/MachineFunctionCloner.cpp
index b769f7d..7dcf5e2 100644
--- a/backend/lib/CodeGen/MachineFunctionCloner.cpp
+++ b/backend/lib/CodeGen/MachineFunctionCloner.cpp
@@ -214,6 +214,12 @@ cloneMemOperand(const MachineMemOperand &SourceOperand, CloneState &State) {
if (!SetResult)
return std::unexpected(SetResult.error());
}
+ if (SourceInfo.TargetFlags != 0) {
+ auto SetResult = State.Destination.registerInfo().setTargetRegisterFlags(
+ *DestinationReg, SourceInfo.TargetFlags);
+ if (!SetResult)
+ return std::unexpected(SetResult.error());
+ }
for (const PhysicalRegister Hint : SourceInfo.AllocationHints) {
auto AddResult = State.Destination.registerInfo().addAllocationHint(*DestinationReg, Hint);
if (!AddResult)
diff --git a/backend/lib/CodeGen/MachineRegisterInfo.cpp b/backend/lib/CodeGen/MachineRegisterInfo.cpp
index c31b38b..3784b22 100644
--- a/backend/lib/CodeGen/MachineRegisterInfo.cpp
+++ b/backend/lib/CodeGen/MachineRegisterInfo.cpp
@@ -94,6 +94,7 @@ Expected<void> MachineRegisterInfo::eraseVirtualRegister(Register Reg) {
Info->get().Bank.reset();
Info->get().RegClass.reset();
Info->get().AllocationHints.clear();
+ Info->get().TargetFlags = 0;
return {};
}
@@ -188,6 +189,18 @@ Expected<void> MachineRegisterInfo::addAllocationHint(Register Reg, PhysicalRegi
return {};
}
+Expected<void> MachineRegisterInfo::setTargetRegisterFlags(Register Reg, std::uint8_t Flags) {
+ auto Info = virtualRegisterInfo(Reg);
+ if (!Info)
+ return std::unexpected(Info.error());
+ Info->get().TargetFlags = Flags;
+ return {};
+}
+
+Expected<void> MachineRegisterInfo::clearTargetRegisterFlags(Register Reg) {
+ return setTargetRegisterFlags(Reg, 0);
+}
+
void MachineRegisterInfo::clearAllVirtualRegisterTypes() noexcept {
for (VirtualRegisterInfo &Info : VirtualRegisters) {
if (!Info.IsTombstone)
diff --git a/backend/lib/CodeGen/MachineVerifier.cpp b/backend/lib/CodeGen/MachineVerifier.cpp
index 98fb9f7..fd027fe 100644
--- a/backend/lib/CodeGen/MachineVerifier.cpp
+++ b/backend/lib/CodeGen/MachineVerifier.cpp
@@ -43,10 +43,17 @@ public:
auto CFIResult = Function.cfiInfo().verify();
if (!CFIResult)
return failWith("CFI information", CFIResult.error());
+ auto TargetCFIResult = Target.validateCFIInfo(Function.cfiInfo());
+ if (!TargetCFIResult)
+ return failWith("target CFI information", TargetCFIResult.error());
if (Function.targetState().hasValue()) {
auto TargetStateResult = Function.targetState().verify();
if (!TargetStateResult)
return failWith("target function state", TargetStateResult.error());
+ const auto RequiredType = Target.mirText().functionStateType();
+ if (RequiredType && Function.targetState().typeID() != RequiredType) {
+ return fail("target function state does not match the target MIR schema");
+ }
}
auto CatalogResult = catalogArenaObjects();
if (!CatalogResult)
@@ -54,6 +61,9 @@ public:
auto FrameResult = Function.frameInfo().verify();
if (!FrameResult)
return failWith("frame info", FrameResult.error());
+ auto TargetFrameResult = Target.validateFrameInfo(Function.frameInfo());
+ if (!TargetFrameResult)
+ return failWith("target frame info", TargetFrameResult.error());
auto ConstantPoolResult = Function.constantPool().verify();
if (!ConstantPoolResult)
return failWith("constant pool", ConstantPoolResult.error());
@@ -652,7 +662,8 @@ private:
for (const VirtualRegisterInfo &Info : RegInfo.virtualRegisters()) {
if (Info.IsTombstone) {
- if (Info.Type || Info.Bank || Info.RegClass || !Info.AllocationHints.empty()) {
+ if (Info.Type || Info.Bank || Info.RegClass || !Info.AllocationHints.empty() ||
+ Info.TargetFlags != 0) {
return fail("virtual-register tombstone retains semantic state");
}
continue;
diff --git a/backend/lib/CodeGen/TargetDescription.cpp b/backend/lib/CodeGen/TargetDescription.cpp
index a32534d..b9f7606 100644
--- a/backend/lib/CodeGen/TargetDescription.cpp
+++ b/backend/lib/CodeGen/TargetDescription.cpp
@@ -4,7 +4,10 @@
#include "rcc/CodeGen/LowLevelType.h"
#include "rcc/CodeGen/MachineBasicBlock.h"
+#include "rcc/CodeGen/MachineCFIInfo.h"
+#include "rcc/CodeGen/MachineFrameInfo.h"
#include "rcc/CodeGen/MachineInstr.h"
+#include "rcc/CodeGen/MachineMemOperand.h"
#include "rcc/CodeGen/MachineOperand.h"
#include "rcc/CodeGen/MachineRegisterInfo.h"
@@ -47,9 +50,19 @@ namespace {
} // namespace
Expected<TargetDescription> TargetDescription::create(InstructionSet Instructions,
- TargetRegisterInfo Registers) {
+ TargetRegisterInfo Registers,
+ TargetMIRTextInfoDesc MIRTextDescription) {
+ auto MIRText = TargetMIRTextInfo::create(std::move(MIRTextDescription), Registers);
+ if (!MIRText)
+ return std::unexpected(MIRText.error());
+
for (const InstructionDescriptor &Instruction : Instructions.descriptors()) {
for (const OperandDescriptor &Operand : Instruction.Operands) {
+ if ((Operand.AllowedTargetFlags & ~MIRText->serializableOperandFlagMask()) != 0) {
+ return makeUnexpected(
+ ErrorCode::InvalidArgument,
+ "instruction operand allows a target flag outside the MIR text schema");
+ }
if (Operand.RegClass && Registers.registerClass(*Operand.RegClass) == nullptr) {
return makeUnexpected(ErrorCode::InvalidArgument,
"instruction operand references an unknown target register class");
@@ -103,7 +116,7 @@ Expected<TargetDescription> TargetDescription::create(InstructionSet Instruction
}
}
- return TargetDescription(std::move(Instructions), std::move(Registers));
+ return TargetDescription(std::move(Instructions), std::move(Registers), std::move(*MIRText));
}
Expected<void> TargetDescription::validateVirtualRegister(Register Reg,
@@ -113,6 +126,10 @@ Expected<void> TargetDescription::validateVirtualRegister(Register Reg,
"target virtual-register validation received a non-virtual register");
}
+ auto TargetFlagValidation = MIRText.validateVirtualRegisterFlags(Info.TargetFlags);
+ if (!TargetFlagValidation)
+ return std::unexpected(TargetFlagValidation.error());
+
const RegisterBankDesc *Bank = nullptr;
if (Info.Bank) {
Bank = Registers.registerBank(*Info.Bank);
@@ -196,6 +213,21 @@ TargetDescription::effectiveRegisterClass(const MachineOperand &Operand,
Expected<void> TargetDescription::validateTargetOperand(const OperandDescriptor *Descriptor,
const MachineOperand &Operand,
const MachineRegisterInfo &RegInfo) const {
+ auto TargetFlagValidation = MIRText.validateOperandFlags(Operand.targetFlags());
+ if (!TargetFlagValidation)
+ return std::unexpected(TargetFlagValidation.error());
+
+ if (const auto *Index = Operand.getIf<TargetIndexOperand>();
+ Index != nullptr && !MIRText.targetIndexName(Index->Index)) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "target-index operand is not registered for MIR serialization");
+ }
+ if (const auto *Intrinsic = Operand.getIf<IntrinsicOperand>();
+ Intrinsic != nullptr && !MIRText.intrinsicName(Intrinsic->ID)) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "intrinsic operand is not registered for MIR serialization");
+ }
+
if (const auto *Mask = Operand.getIf<RegisterMaskOperand>(); Mask != nullptr) {
if (!Mask->Mask) {
return makeUnexpected(ErrorCode::VerificationError,
@@ -301,6 +333,104 @@ Expected<void> TargetDescription::validateTargetOperand(const OperandDescriptor
return {};
}
+Expected<void> TargetDescription::validateMemOperands(
+ Opcode InstructionOpcode, std::span<const MachineMemOperand *const> MemOperands) const {
+ auto DescriptorValidation = Instructions.validateMemOperands(InstructionOpcode, MemOperands);
+ if (!DescriptorValidation)
+ return std::unexpected(DescriptorValidation.error());
+ for (const MachineMemOperand *MemOperand : MemOperands) {
+ if (MemOperand == nullptr)
+ continue;
+ auto TargetFlagValidation =
+ MIRText.validateMachineMemOperandFlags(MemOperand->description().TargetFlags);
+ if (!TargetFlagValidation)
+ return std::unexpected(TargetFlagValidation.error());
+ }
+ return {};
+}
+
+Expected<void> TargetDescription::validateCFIInfo(const MachineCFIInfo &Info) const {
+ auto ValidateRegister = [this](DwarfRegisterNumber Register) -> Expected<void> {
+ if (!MIRText.physicalRegister(Register.value())) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "CFI instruction references an unmapped DWARF register");
+ }
+ return {};
+ };
+
+ for (const MachineCFIInstruction &Instruction : Info.instructions()) {
+ if (const auto *Rule = Instruction.getIf<MachineCFIRegisterRule>()) {
+ auto Result = ValidateRegister(Rule->Register);
+ if (!Result)
+ return Result;
+ }
+ if (const auto *Rule = Instruction.getIf<MachineCFIRegisterOffsetRule>()) {
+ auto Result = ValidateRegister(Rule->Register);
+ if (!Result)
+ return Result;
+ }
+ if (const auto *Rule = Instruction.getIf<MachineCFIRegisterRegisterRule>()) {
+ auto RegisterResult = ValidateRegister(Rule->Register);
+ if (!RegisterResult)
+ return RegisterResult;
+ auto SourceResult = ValidateRegister(Rule->SourceRegister);
+ if (!SourceResult)
+ return SourceResult;
+ }
+ if (const auto *Rule = Instruction.getIf<MachineCFIAddressSpaceCFARule>()) {
+ auto Result = ValidateRegister(Rule->Register);
+ if (!Result)
+ return Result;
+ }
+ if (const auto *Rule = Instruction.getIf<MachineCFIRegisterPairRule>()) {
+ for (const DwarfRegisterNumber Register :
+ {Rule->Register, Rule->FirstRegister, Rule->SecondRegister}) {
+ auto Result = ValidateRegister(Register);
+ if (!Result)
+ return Result;
+ }
+ }
+ if (const auto *Rule = Instruction.getIf<MachineCFIVectorRegistersRule>()) {
+ auto Result = ValidateRegister(Rule->Register);
+ if (!Result)
+ return Result;
+ for (const MachineCFIVectorRegisterLane &Lane : Rule->VectorRegisters) {
+ Result = ValidateRegister(Lane.Register);
+ if (!Result)
+ return Result;
+ }
+ }
+ if (const auto *Rule = Instruction.getIf<MachineCFIVectorOffsetRule>()) {
+ auto RegisterResult = ValidateRegister(Rule->Register);
+ if (!RegisterResult)
+ return RegisterResult;
+ auto MaskResult = ValidateRegister(Rule->MaskRegister);
+ if (!MaskResult)
+ return MaskResult;
+ }
+ if (const auto *Rule = Instruction.getIf<MachineCFIVectorRegisterMaskRule>()) {
+ for (const DwarfRegisterNumber Register :
+ {Rule->Register, Rule->SpillRegister, Rule->MaskRegister}) {
+ auto Result = ValidateRegister(Register);
+ if (!Result)
+ return Result;
+ }
+ }
+ }
+ return {};
+}
+
+Expected<void> TargetDescription::validateFrameInfo(const MachineFrameInfo &Info) const {
+ for (const StackObject &Object : Info.objects()) {
+ const StackObjectDesc *Description = Object.description();
+ if (Description != nullptr && !MIRText.supportsStackID(Description->Stack)) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "stack object uses a stack ID unsupported by the target");
+ }
+ }
+ return {};
+}
+
Expected<void>
TargetDescription::validateExplicitOperands(Opcode InstructionOpcode,
std::span<const MachineOperand> Operands,
diff --git a/backend/lib/CodeGen/TargetMIRTextInfo.cpp b/backend/lib/CodeGen/TargetMIRTextInfo.cpp
new file mode 100644
index 0000000..a349432
--- /dev/null
+++ b/backend/lib/CodeGen/TargetMIRTextInfo.cpp
@@ -0,0 +1,368 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "rcc/CodeGen/TargetMIRTextInfo.h"
+
+#include "rcc/CodeGen/TargetRegisterInfo.h"
+
+#include <bit>
+#include <cstddef>
+#include <cstdint>
+#include <string>
+#include <unordered_set>
+#include <utility>
+#include <vector>
+
+namespace rcc {
+namespace {
+
+[[nodiscard]] std::unexpected<Error> invalidDescription(std::string Message) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "target MIR text description: " + std::move(Message));
+}
+
+[[nodiscard]] bool isASCIIAlpha(char Character) noexcept {
+ return (Character >= 'a' && Character <= 'z') || (Character >= 'A' && Character <= 'Z');
+}
+
+[[nodiscard]] bool isASCIIDigit(char Character) noexcept {
+ return Character >= '0' && Character <= '9';
+}
+
+[[nodiscard]] bool isMIRIdentifier(std::string_view Name) noexcept {
+ if (Name.empty() || (!isASCIIAlpha(Name.front()) && Name.front() != '_'))
+ return false;
+ for (const char Character : Name) {
+ if (!isASCIIAlpha(Character) && !isASCIIDigit(Character) && Character != '_' &&
+ Character != '-' && Character != '.' && Character != '$') {
+ return false;
+ }
+ }
+ return true;
+}
+
+[[nodiscard]] std::string lowerASCII(std::string_view Name) {
+ std::string Result(Name);
+ for (char &Character : Result) {
+ if (Character >= 'A' && Character <= 'Z')
+ Character = static_cast<char>(Character - 'A' + 'a');
+ }
+ return Result;
+}
+
+} // namespace
+
+Expected<TargetMIRTextInfo> TargetMIRTextInfo::create(TargetMIRTextInfoDesc Description,
+ const TargetRegisterInfo &Registers) {
+ TargetMIRTextInfo Result(std::move(Description));
+
+ for (std::size_t Index = 0; Index < Result.Description.RegisterMasks.size(); ++Index) {
+ MIRRegisterMaskDesc &Mask = Result.Description.RegisterMasks[Index];
+ Mask.Name = lowerASCII(Mask.Name);
+ if (!isMIRIdentifier(Mask.Name) || Mask.Mask == nullptr) {
+ return invalidDescription("named register masks require an identifier and nonnull mask");
+ }
+ auto Validation = Registers.validateRegisterMask(*Mask.Mask);
+ if (!Validation)
+ return invalidDescription("named register mask contains an unknown physical register");
+ if (!Result.RegisterMaskNameToIndex.emplace(Mask.Name, Index).second) {
+ return invalidDescription("register mask names must be unique case-insensitively");
+ }
+ for (std::size_t Previous = 0; Previous < Index; ++Previous) {
+ if (*Result.Description.RegisterMasks[Previous].Mask == *Mask.Mask) {
+ return invalidDescription("named register mask values must have one canonical name");
+ }
+ }
+ }
+
+ for (std::size_t Index = 0; Index < Result.Description.TargetIndices.size(); ++Index) {
+ const MIRTargetIndexDesc &TargetIndex = Result.Description.TargetIndices[Index];
+ if (!isMIRIdentifier(TargetIndex.Name)) {
+ return invalidDescription("target index names must be MIR identifiers");
+ }
+ if (!Result.TargetIndexNameToIndex.emplace(TargetIndex.Name, Index).second ||
+ !Result.TargetIndexToIndex.emplace(TargetIndex.Index, Index).second) {
+ return invalidDescription("target index names and values must be unique");
+ }
+ }
+
+ if (Result.Description.DirectOperandFlags.empty() !=
+ (Result.Description.DirectOperandFlagMask == 0)) {
+ return invalidDescription(
+ "direct operand flags and the direct operand flag mask must be present together");
+ }
+
+ std::unordered_set<std::string> OperandFlagNames;
+ for (std::size_t Index = 0; Index < Result.Description.DirectOperandFlags.size(); ++Index) {
+ const MIROperandFlagDesc &Flag = Result.Description.DirectOperandFlags[Index];
+ if (!isMIRIdentifier(Flag.Name) || Flag.Value == 0 ||
+ (Flag.Value & ~Result.Description.DirectOperandFlagMask) != 0) {
+ return invalidDescription(
+ "direct operand flags require an identifier and a nonzero value inside the direct mask");
+ }
+ if (!OperandFlagNames.insert(Flag.Name).second ||
+ !Result.DirectOperandFlagNameToIndex.emplace(Flag.Name, Index).second ||
+ !Result.DirectOperandFlagToIndex.emplace(Flag.Value, Index).second) {
+ return invalidDescription("direct operand flag names and values must be unique");
+ }
+ }
+
+ std::uint32_t BitmaskOperandFlags = 0;
+ for (std::size_t Index = 0; Index < Result.Description.BitmaskOperandFlags.size(); ++Index) {
+ const MIROperandFlagDesc &Flag = Result.Description.BitmaskOperandFlags[Index];
+ if (!isMIRIdentifier(Flag.Name) || !std::has_single_bit(Flag.Value) ||
+ (Flag.Value & Result.Description.DirectOperandFlagMask) != 0) {
+ return invalidDescription(
+ "bitmask operand flags require an identifier and one bit outside the direct mask");
+ }
+ if (!OperandFlagNames.insert(Flag.Name).second ||
+ !Result.BitmaskOperandFlagNameToIndex.emplace(Flag.Name, Index).second ||
+ (BitmaskOperandFlags & Flag.Value) != 0) {
+ return invalidDescription("operand flag names and bitmask values must be unique");
+ }
+ BitmaskOperandFlags |= Flag.Value;
+ }
+ Result.SerializableOperandFlagMask =
+ Result.Description.DirectOperandFlagMask | BitmaskOperandFlags;
+
+ for (std::size_t Index = 0; Index < Result.Description.MachineMemOperandFlags.size(); ++Index) {
+ const MIRMachineMemOperandFlagDesc &Flag = Result.Description.MachineMemOperandFlags[Index];
+ if (!isMIRIdentifier(Flag.Name) || !std::has_single_bit(Flag.Value)) {
+ return invalidDescription(
+ "machine memory operand flags require an identifier and a single-bit value");
+ }
+ if (!Result.MachineMemOperandFlagNameToIndex.emplace(Flag.Name, Index).second ||
+ (Result.SerializableMachineMemOperandFlagMask & Flag.Value) != 0) {
+ return invalidDescription("machine memory operand flag names and values must be unique");
+ }
+ Result.SerializableMachineMemOperandFlagMask |= Flag.Value;
+ }
+
+ for (std::size_t Index = 0; Index < Result.Description.VirtualRegisterFlags.size(); ++Index) {
+ const MIRVirtualRegisterFlagDesc &Flag = Result.Description.VirtualRegisterFlags[Index];
+ if (!isMIRIdentifier(Flag.Name) || !std::has_single_bit(Flag.Value)) {
+ return invalidDescription(
+ "virtual register flags require an identifier and a single-bit value");
+ }
+ if (!Result.VirtualRegisterFlagNameToIndex.emplace(Flag.Name, Index).second ||
+ (Result.SerializableVirtualRegisterFlagMask & Flag.Value) != 0) {
+ return invalidDescription("virtual register flag names and values must be unique");
+ }
+ Result.SerializableVirtualRegisterFlagMask |= Flag.Value;
+ }
+
+ for (std::size_t Index = 0; Index < Result.Description.Intrinsics.size(); ++Index) {
+ const MIRIntrinsicDesc &Intrinsic = Result.Description.Intrinsics[Index];
+ if (!isMIRIdentifier(Intrinsic.Name) || !Intrinsic.Name.starts_with("llvm.") ||
+ Intrinsic.ID.value() == 0) {
+ return invalidDescription("intrinsics require an llvm-prefixed MIR name and a nonzero ID");
+ }
+ if (!Result.IntrinsicNameToIndex.emplace(Intrinsic.Name, Index).second ||
+ !Result.IntrinsicToIndex.emplace(Intrinsic.ID, Index).second) {
+ return invalidDescription("intrinsic names and IDs must be unique");
+ }
+ }
+
+ std::unordered_set<std::uint32_t> ReferencedDwarfRegisters;
+ for (const MIRCFIRegisterMappingDesc &Mapping : Result.Description.CFIRegisters) {
+ if (!Registers.isKnownPhysicalRegister(Mapping.Register) ||
+ !Result.RegisterToDwarf.emplace(Mapping.Register, Mapping.DwarfRegister).second) {
+ return invalidDescription("CFI register mappings require unique, known physical registers");
+ }
+ ReferencedDwarfRegisters.insert(Mapping.DwarfRegister);
+ if (Mapping.IsCanonical &&
+ !Result.DwarfToRegister.emplace(Mapping.DwarfRegister, Mapping.Register).second) {
+ return invalidDescription(
+ "each DWARF register must have exactly one canonical physical register");
+ }
+ }
+ if (ReferencedDwarfRegisters.size() != Result.DwarfToRegister.size()) {
+ return invalidDescription(
+ "every referenced DWARF register requires a canonical physical register");
+ }
+
+ for (const StackID ID : Result.Description.SupportedStackIDs) {
+ if (!ID.isValid() || !Result.SupportedStackIDs.insert(ID.value()).second)
+ return invalidDescription("supported stack IDs must be valid and unique");
+ }
+ if (!Result.SupportedStackIDs.contains(StackID::defaultStack().value()) ||
+ !Result.SupportedStackIDs.contains(StackID::noAllocStack().value())) {
+ return invalidDescription("targets must support the default and noalloc stack IDs");
+ }
+
+ if (Result.Description.FunctionStateType && Result.Description.FunctionStateType->value() == 0) {
+ return invalidDescription("target function state TypeID must be nonzero");
+ }
+
+ return Result;
+}
+
+std::shared_ptr<const RegisterMask>
+TargetMIRTextInfo::registerMask(std::string_view Name) const noexcept {
+ const auto Position = RegisterMaskNameToIndex.find(std::string(Name));
+ if (Position == RegisterMaskNameToIndex.end())
+ return {};
+ return Description.RegisterMasks[Position->second].Mask;
+}
+
+std::optional<std::string_view>
+TargetMIRTextInfo::registerMaskName(const RegisterMask &Mask) const noexcept {
+ for (const MIRRegisterMaskDesc &NamedMask : Description.RegisterMasks) {
+ if (*NamedMask.Mask == Mask)
+ return NamedMask.Name;
+ }
+ return std::nullopt;
+}
+
+std::optional<TargetIndex> TargetMIRTextInfo::targetIndex(std::string_view Name) const noexcept {
+ const auto Position = TargetIndexNameToIndex.find(std::string(Name));
+ if (Position == TargetIndexNameToIndex.end())
+ return std::nullopt;
+ return Description.TargetIndices[Position->second].Index;
+}
+
+std::optional<std::string_view>
+TargetMIRTextInfo::targetIndexName(TargetIndex Index) const noexcept {
+ const auto Position = TargetIndexToIndex.find(Index);
+ if (Position == TargetIndexToIndex.end())
+ return std::nullopt;
+ return Description.TargetIndices[Position->second].Name;
+}
+
+std::optional<std::uint32_t>
+TargetMIRTextInfo::directOperandFlag(std::string_view Name) const noexcept {
+ const auto Position = DirectOperandFlagNameToIndex.find(std::string(Name));
+ if (Position == DirectOperandFlagNameToIndex.end())
+ return std::nullopt;
+ return Description.DirectOperandFlags[Position->second].Value;
+}
+
+std::optional<std::uint32_t>
+TargetMIRTextInfo::bitmaskOperandFlag(std::string_view Name) const noexcept {
+ const auto Position = BitmaskOperandFlagNameToIndex.find(std::string(Name));
+ if (Position == BitmaskOperandFlagNameToIndex.end())
+ return std::nullopt;
+ return Description.BitmaskOperandFlags[Position->second].Value;
+}
+
+Expected<void> TargetMIRTextInfo::validateOperandFlags(std::uint32_t Flags) const {
+ if ((Flags & ~SerializableOperandFlagMask) != 0) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "machine operand contains an unknown target flag bit");
+ }
+ const std::uint32_t DirectFlag = Flags & Description.DirectOperandFlagMask;
+ if (DirectFlag != 0 && !DirectOperandFlagToIndex.contains(DirectFlag)) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "machine operand contains an unregistered direct target flag");
+ }
+ return {};
+}
+
+Expected<std::vector<std::string_view>>
+TargetMIRTextInfo::operandFlagNames(std::uint32_t Flags) const {
+ auto Validation = validateOperandFlags(Flags);
+ if (!Validation)
+ return std::unexpected(Validation.error());
+
+ std::vector<std::string_view> Names;
+ const std::uint32_t DirectFlag = Flags & Description.DirectOperandFlagMask;
+ if (DirectFlag != 0) {
+ Names.push_back(Description.DirectOperandFlags[DirectOperandFlagToIndex.at(DirectFlag)].Name);
+ }
+ for (const MIROperandFlagDesc &Flag : Description.BitmaskOperandFlags) {
+ if ((Flags & Flag.Value) != 0)
+ Names.push_back(Flag.Name);
+ }
+ return Names;
+}
+
+std::optional<std::uint16_t>
+TargetMIRTextInfo::machineMemOperandFlag(std::string_view Name) const noexcept {
+ const auto Position = MachineMemOperandFlagNameToIndex.find(std::string(Name));
+ if (Position == MachineMemOperandFlagNameToIndex.end())
+ return std::nullopt;
+ return Description.MachineMemOperandFlags[Position->second].Value;
+}
+
+Expected<void> TargetMIRTextInfo::validateMachineMemOperandFlags(std::uint16_t Flags) const {
+ if ((Flags & static_cast<std::uint16_t>(~SerializableMachineMemOperandFlagMask)) != 0) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "machine memory operand contains an unknown target flag");
+ }
+ return {};
+}
+
+Expected<std::vector<std::string_view>>
+TargetMIRTextInfo::machineMemOperandFlagNames(std::uint16_t Flags) const {
+ auto Validation = validateMachineMemOperandFlags(Flags);
+ if (!Validation)
+ return std::unexpected(Validation.error());
+
+ std::vector<std::string_view> Names;
+ for (const MIRMachineMemOperandFlagDesc &Flag : Description.MachineMemOperandFlags) {
+ if ((Flags & Flag.Value) != 0)
+ Names.push_back(Flag.Name);
+ }
+ return Names;
+}
+
+std::optional<std::uint8_t>
+TargetMIRTextInfo::virtualRegisterFlag(std::string_view Name) const noexcept {
+ const auto Position = VirtualRegisterFlagNameToIndex.find(std::string(Name));
+ if (Position == VirtualRegisterFlagNameToIndex.end())
+ return std::nullopt;
+ return Description.VirtualRegisterFlags[Position->second].Value;
+}
+
+Expected<void> TargetMIRTextInfo::validateVirtualRegisterFlags(std::uint8_t Flags) const {
+ if ((Flags & static_cast<std::uint8_t>(~SerializableVirtualRegisterFlagMask)) != 0) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "virtual register contains an unknown target flag");
+ }
+ return {};
+}
+
+Expected<std::vector<std::string_view>>
+TargetMIRTextInfo::virtualRegisterFlagNames(std::uint8_t Flags) const {
+ auto Validation = validateVirtualRegisterFlags(Flags);
+ if (!Validation)
+ return std::unexpected(Validation.error());
+
+ std::vector<std::string_view> Names;
+ for (const MIRVirtualRegisterFlagDesc &Flag : Description.VirtualRegisterFlags) {
+ if ((Flags & Flag.Value) != 0)
+ Names.push_back(Flag.Name);
+ }
+ return Names;
+}
+
+std::optional<IntrinsicID> TargetMIRTextInfo::intrinsic(std::string_view Name) const noexcept {
+ const auto Position = IntrinsicNameToIndex.find(std::string(Name));
+ if (Position == IntrinsicNameToIndex.end())
+ return std::nullopt;
+ return Description.Intrinsics[Position->second].ID;
+}
+
+std::optional<std::string_view> TargetMIRTextInfo::intrinsicName(IntrinsicID ID) const noexcept {
+ const auto Position = IntrinsicToIndex.find(ID);
+ if (Position == IntrinsicToIndex.end())
+ return std::nullopt;
+ return Description.Intrinsics[Position->second].Name;
+}
+
+std::optional<std::uint32_t>
+TargetMIRTextInfo::dwarfRegister(PhysicalRegister Register) const noexcept {
+ const auto Position = RegisterToDwarf.find(Register);
+ if (Position == RegisterToDwarf.end())
+ return std::nullopt;
+ return Position->second;
+}
+
+std::optional<PhysicalRegister>
+TargetMIRTextInfo::physicalRegister(std::uint32_t DwarfRegister) const noexcept {
+ const auto Position = DwarfToRegister.find(DwarfRegister);
+ if (Position == DwarfToRegister.end())
+ return std::nullopt;
+ return Position->second;
+}
+
+} // namespace rcc
diff --git a/backend/lib/CodeGen/TargetRegisterInfo.cpp b/backend/lib/CodeGen/TargetRegisterInfo.cpp
index 7c8af82..3ece639 100644
--- a/backend/lib/CodeGen/TargetRegisterInfo.cpp
+++ b/backend/lib/CodeGen/TargetRegisterInfo.cpp
@@ -26,6 +26,15 @@ namespace {
return Value != 0 && std::has_single_bit(Value);
}
+[[nodiscard]] std::string lowerASCII(std::string_view Name) {
+ std::string Result(Name);
+ for (char &Character : Result) {
+ if (Character >= 'A' && Character <= 'Z')
+ Character = static_cast<char>(Character - 'A' + 'a');
+ }
+ return Result;
+}
+
template <typename RangeT, typename ValueT>
[[nodiscard]] bool contains(const RangeT &Range, const ValueT &Value) {
return std::ranges::find(Range, Value) != std::ranges::end(Range);
@@ -64,10 +73,13 @@ Expected<TargetRegisterInfo> TargetRegisterInfo::create(TargetRegisterInfoDesc D
if (!Register.Reg.isValid() || Register.Name.empty()) {
return invalidDescription("physical register requires a valid number and name");
}
+ std::string MIRName = lowerASCII(Register.Name);
if (!Result.RegisterToIndex.emplace(Register.Reg, Index).second ||
- !Result.RegisterNameToIndex.emplace(Register.Name, Index).second) {
- return invalidDescription("physical register numbers and names must be unique");
+ !Result.RegisterNameToIndex.emplace(MIRName, Index).second) {
+ return invalidDescription(
+ "physical register numbers and names must be unique case-insensitively");
}
+ Result.RegisterMIRNames.push_back(std::move(MIRName));
}
std::unordered_set<std::string> SubRegNames;
@@ -81,32 +93,36 @@ Expected<TargetRegisterInfo> TargetRegisterInfo::create(TargetRegisterInfoDesc D
"subregister index requires a nonzero ID, name, lanes, and valid bit range");
}
if (!Result.SubRegIndexToIndex.emplace(SubReg.ID, Index).second ||
- !SubRegNames.insert(SubReg.Name).second) {
+ !SubRegNames.insert(SubReg.Name).second ||
+ !Result.SubRegNameToIndex.emplace(SubReg.Name, Index).second) {
return invalidDescription("subregister index IDs and names must be unique");
}
}
- std::unordered_set<std::string> ClassNames;
for (std::size_t Index = 0; Index < Result.Description.RegisterClasses.size(); ++Index) {
const RegisterClassDesc &Class = Result.Description.RegisterClasses[Index];
if (Class.ID.value() == 0 || Class.Name.empty()) {
return invalidDescription("register class requires a nonzero ID and name");
}
+ std::string MIRName = lowerASCII(Class.Name);
if (!Result.ClassToIndex.emplace(Class.ID, Index).second ||
- !ClassNames.insert(Class.Name).second) {
- return invalidDescription("register class IDs and names must be unique");
+ !Result.ClassNameToIndex.emplace(MIRName, Index).second) {
+ return invalidDescription("register class IDs and names must be unique case-insensitively");
}
+ Result.ClassMIRNames.push_back(std::move(MIRName));
}
- std::unordered_set<std::string> BankNames;
for (std::size_t Index = 0; Index < Result.Description.RegisterBanks.size(); ++Index) {
const RegisterBankDesc &Bank = Result.Description.RegisterBanks[Index];
if (Bank.ID.value() == 0 || Bank.Name.empty() || Bank.MaximumSizeInBits == 0) {
return invalidDescription("register bank requires a nonzero ID, name, and maximum size");
}
- if (!Result.BankToIndex.emplace(Bank.ID, Index).second || !BankNames.insert(Bank.Name).second) {
- return invalidDescription("register bank IDs and names must be unique");
+ std::string MIRName = lowerASCII(Bank.Name);
+ if (!Result.BankToIndex.emplace(Bank.ID, Index).second ||
+ !Result.BankNameToIndex.emplace(MIRName, Index).second) {
+ return invalidDescription("register bank IDs and names must be unique case-insensitively");
}
+ Result.BankMIRNames.push_back(std::move(MIRName));
}
std::unordered_set<RegisterUnitID> ReferencedUnits;
@@ -443,16 +459,57 @@ const SubRegIndexDesc *TargetRegisterInfo::subRegIndex(SubRegIndex Index) const
: &Description.SubRegIndices[Position->second];
}
+const SubRegIndexDesc *TargetRegisterInfo::subRegIndex(std::string_view Name) const noexcept {
+ const auto Position = SubRegNameToIndex.find(std::string(Name));
+ return Position == SubRegNameToIndex.end() ? nullptr
+ : &Description.SubRegIndices[Position->second];
+}
+
const RegisterClassDesc *TargetRegisterInfo::registerClass(RegisterClassID ID) const noexcept {
const auto Position = ClassToIndex.find(ID);
return Position == ClassToIndex.end() ? nullptr : &Description.RegisterClasses[Position->second];
}
+const RegisterClassDesc *TargetRegisterInfo::registerClass(std::string_view Name) const noexcept {
+ const auto Position = ClassNameToIndex.find(std::string(Name));
+ return Position == ClassNameToIndex.end() ? nullptr
+ : &Description.RegisterClasses[Position->second];
+}
+
const RegisterBankDesc *TargetRegisterInfo::registerBank(RegisterBankID ID) const noexcept {
const auto Position = BankToIndex.find(ID);
return Position == BankToIndex.end() ? nullptr : &Description.RegisterBanks[Position->second];
}
+const RegisterBankDesc *TargetRegisterInfo::registerBank(std::string_view Name) const noexcept {
+ const auto Position = BankNameToIndex.find(std::string(Name));
+ return Position == BankNameToIndex.end() ? nullptr : &Description.RegisterBanks[Position->second];
+}
+
+std::optional<std::string_view>
+TargetRegisterInfo::physicalRegisterMIRName(PhysicalRegister Reg) const noexcept {
+ const auto Position = RegisterToIndex.find(Reg);
+ if (Position == RegisterToIndex.end())
+ return std::nullopt;
+ return RegisterMIRNames[Position->second];
+}
+
+std::optional<std::string_view>
+TargetRegisterInfo::registerClassMIRName(RegisterClassID ID) const noexcept {
+ const auto Position = ClassToIndex.find(ID);
+ if (Position == ClassToIndex.end())
+ return std::nullopt;
+ return ClassMIRNames[Position->second];
+}
+
+std::optional<std::string_view>
+TargetRegisterInfo::registerBankMIRName(RegisterBankID ID) const noexcept {
+ const auto Position = BankToIndex.find(ID);
+ if (Position == BankToIndex.end())
+ return std::nullopt;
+ return BankMIRNames[Position->second];
+}
+
LaneMask TargetRegisterInfo::registerLaneMask(PhysicalRegister Reg) const noexcept {
const PhysicalRegisterDesc *Register = physicalRegister(Reg);
if (Register == nullptr)
diff --git a/backend/unittests/CodeGen/CMakeLists.txt b/backend/unittests/CodeGen/CMakeLists.txt
index 69f3646..ef96358 100644
--- a/backend/unittests/CodeGen/CMakeLists.txt
+++ b/backend/unittests/CodeGen/CMakeLists.txt
@@ -22,6 +22,7 @@ set(
MachineValueTypesTest.cpp
TargetConstantTest.cpp
TargetFunctionStateTest.cpp
+ TargetMIRTextInfoTest.cpp
TargetRegisterInfoTest.cpp
)
@@ -253,6 +254,18 @@ add_test(
COMMAND RCCBackendCodeGenTargetFunctionStateTests
)
+add_executable(RCCBackendCodeGenTargetMIRTextInfoTests TargetMIRTextInfoTest.cpp)
+rcc_backend_configure_target(RCCBackendCodeGenTargetMIRTextInfoTests)
+target_link_libraries(
+ RCCBackendCodeGenTargetMIRTextInfoTests
+ PRIVATE RCCBackend::CodeGen RCCBackend::TestTarget
+)
+
+add_test(
+ NAME RCCBackend.CodeGen.TargetMIRTextInfo
+ COMMAND RCCBackendCodeGenTargetMIRTextInfoTests
+)
+
add_executable(RCCBackendCodeGenTargetRegisterInfoTests TargetRegisterInfoTest.cpp)
rcc_backend_configure_target(RCCBackendCodeGenTargetRegisterInfoTests)
target_link_libraries(
@@ -286,6 +299,7 @@ rcc_backend_register_format_files(
"${CMAKE_CURRENT_SOURCE_DIR}/MachineValueTypesTest.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/TargetConstantTest.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/TargetFunctionStateTest.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/TargetMIRTextInfoTest.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/TargetRegisterInfoTest.cpp"
)
rcc_backend_register_tidy_files(
@@ -309,5 +323,6 @@ rcc_backend_register_tidy_files(
"${CMAKE_CURRENT_SOURCE_DIR}/MachineValueTypesTest.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/TargetConstantTest.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/TargetFunctionStateTest.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/TargetMIRTextInfoTest.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/TargetRegisterInfoTest.cpp"
)
diff --git a/backend/unittests/CodeGen/MachineFrameInfoTest.cpp b/backend/unittests/CodeGen/MachineFrameInfoTest.cpp
index c9289c7..c67339d 100644
--- a/backend/unittests/CodeGen/MachineFrameInfoTest.cpp
+++ b/backend/unittests/CodeGen/MachineFrameInfoTest.cpp
@@ -149,15 +149,24 @@ void testInvalidDescriptions(TestContext &Context) {
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);
+ Context.expect(
+ rcc::StackID::fromValue(0) && rcc::StackID::fromValue(4) && rcc::StackID::fromValue(255) &&
+ !rcc::StackID::fromValue(5) && !rcc::StackID::fromValue(256) &&
+ rcc::parseStackIDName("sgpr-spill") == rcc::StackID::sgprSpillStack() &&
+ rcc::parseStackIDName("scalable-vector") == rcc::StackID::scalableVectorStack() &&
+ rcc::parseStackIDName("scalable-predicate-vector") ==
+ rcc::StackID::scalablePredicateVectorStack() &&
+ rcc::parseStackIDName("wasm-local") == rcc::StackID::wasmLocalStack() &&
+ rcc::parseStackIDName("noalloc") == rcc::StackID::noAllocStack() &&
+ !rcc::parseStackIDName("safe") &&
+ rcc::getStackIDName(rcc::StackID::noAllocStack()) == "noalloc",
+ "stack IDs and names must match the fixed LLVM MIR enumeration");
+
auto Target = fixedObject(8, 8);
Target.Kind = rcc::StackObjectKind::Target;
- if (TargetStack)
- Target.Stack = *TargetStack;
+ Target.Stack = rcc::StackID::sgprSpillStack();
auto TargetIndex = Frame.createObject(Target);
- Context.expect(TargetStack && TargetIndex,
- "target stack identity must admit target stack objects");
+ Context.expect(TargetIndex.has_value(), "special stack identity must admit target stack objects");
auto Unresolved = Frame.createObject(fixedObject(8, 8));
Context.expect(Unresolved && !Frame.freeze(16, *rcc::Alignment::create(8)) && !Frame.isFrozen() &&
diff --git a/backend/unittests/CodeGen/MachineFunctionClonerTest.cpp b/backend/unittests/CodeGen/MachineFunctionClonerTest.cpp
index d330f6a..a019c8b 100644
--- a/backend/unittests/CodeGen/MachineFunctionClonerTest.cpp
+++ b/backend/unittests/CodeGen/MachineFunctionClonerTest.cpp
@@ -268,6 +268,7 @@ createFunction(TestContext &Context, std::string Name, std::uint64_t ID) {
Description.Align = *rcc::Alignment::create(8);
Description.Base = std::move(Base);
Description.Offset = -8;
+ Description.TargetFlags = 0x3;
return Description;
}
@@ -383,7 +384,8 @@ struct SourceFixture final {
const auto RegClass = rcc::RegisterClassID::fromValue(2);
for (const rcc::Register Reg : {Fixture.Value, Fixture.CopyValue}) {
Context.expect(Function.registerInfo().setRegisterBank(Reg, Bank).has_value() &&
- Function.registerInfo().setRegisterClass(Reg, RegClass).has_value(),
+ Function.registerInfo().setRegisterClass(Reg, RegClass).has_value() &&
+ Function.registerInfo().setTargetRegisterFlags(Reg, 0x3).has_value(),
"source vreg constraints must be recorded");
}
@@ -591,7 +593,8 @@ void testDeepClone(TestContext &Context) {
SourceInfo->get().Type == DestinationInfo->get().Type &&
SourceInfo->get().Bank == DestinationInfo->get().Bank &&
SourceInfo->get().RegClass == DestinationInfo->get().RegClass &&
- SourceInfo->get().AllocationHints == DestinationInfo->get().AllocationHints,
+ SourceInfo->get().AllocationHints == DestinationInfo->get().AllocationHints &&
+ SourceInfo->get().TargetFlags == DestinationInfo->get().TargetFlags,
"clone must preserve vreg type and allocation constraints");
Context.expect(Destination.registerInfo().liveIns().size() == 1 &&
Destination.registerInfo().liveIns()[0].InitialVirtualRegister == NewValue &&
diff --git a/backend/unittests/CodeGen/TargetMIRTextInfoTest.cpp b/backend/unittests/CodeGen/TargetMIRTextInfoTest.cpp
new file mode 100644
index 0000000..d7e2f13
--- /dev/null
+++ b/backend/unittests/CodeGen/TargetMIRTextInfoTest.cpp
@@ -0,0 +1,371 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "rcc/CodeGen/TargetMIRTextInfo.h"
+
+#include "Target/TestTarget.h"
+#include "rcc/CodeGen/InstructionDescriptor.h"
+#include "rcc/CodeGen/MachineCFIInfo.h"
+#include "rcc/CodeGen/MachineFunction.h"
+#include "rcc/CodeGen/MachineMemOperand.h"
+#include "rcc/CodeGen/MachineOperand.h"
+#include "rcc/CodeGen/MachineRegisterInfo.h"
+#include "rcc/CodeGen/TargetDescription.h"
+#include "rcc/CodeGen/TargetRegisterInfo.h"
+
+#include <array>
+#include <cstdint>
+#include <iostream>
+#include <memory>
+#include <span>
+#include <string_view>
+#include <utility>
+#include <vector>
+
+namespace {
+
+class TestContext final {
+public:
+ void expect(bool Condition, std::string_view Message) {
+ if (Condition)
+ return;
+ std::cerr << Message << '\n';
+ ++FailureCount;
+ }
+
+ [[nodiscard]] int result() const noexcept {
+ return FailureCount == 0 ? 0 : 1;
+ }
+
+private:
+ int FailureCount = 0;
+};
+
+[[nodiscard]] rcc::PhysicalRegister phys(std::uint32_t Number) {
+ return *rcc::PhysicalRegister::create(Number);
+}
+
+void testTargetRegisterNames(TestContext &Context) {
+ auto Registers = rcc::test::createTestTargetRegisterInfo();
+ Context.expect(Registers.has_value(), "TestTarget register schema must be valid");
+ if (!Registers)
+ return;
+
+ const auto G0 = phys(rcc::test::TestRegister::G0);
+ Context.expect(Registers->physicalRegister("g0") != nullptr &&
+ Registers->physicalRegister("G0") == nullptr &&
+ Registers->physicalRegisterMIRName(G0) == "g0",
+ "physical register MIR names must be canonical lowercase names");
+ Context.expect(Registers->subRegIndex("low32") != nullptr &&
+ Registers->subRegIndex("LOW32") == nullptr,
+ "subregister MIR names must retain their exact target spelling");
+ Context.expect(Registers->registerClass("gpr64") != nullptr &&
+ Registers->registerClass("GPR64") == nullptr &&
+ Registers->registerClassMIRName(rcc::test::TestRegisterClass::GPR64) ==
+ "gpr64",
+ "register class MIR names must be canonical lowercase names");
+ Context.expect(Registers->registerBank("gpr") != nullptr &&
+ Registers->registerBank("GPR") == nullptr &&
+ Registers->registerBankMIRName(rcc::test::TestRegisterBank::GPR) == "gpr",
+ "register bank MIR names must be canonical lowercase names");
+}
+
+void testTextSchemaQueries(TestContext &Context) {
+ auto Registers = rcc::test::createTestTargetRegisterInfo();
+ if (!Registers)
+ return;
+ auto Text = rcc::TargetMIRTextInfo::create(rcc::test::createTestTargetMIRTextInfoDescription(),
+ *Registers);
+ Context.expect(Text.has_value(), "TestTarget MIR text schema must be valid");
+ if (!Text)
+ return;
+
+ const auto Preserved = rcc::test::createTestCallPreservedMask();
+ const auto NamedMask = Text->registerMask("test-preserved");
+ Context.expect(NamedMask && *NamedMask == *Preserved && !Text->registerMask("Test-Preserved") &&
+ Text->registerMaskName(*Preserved) == "test-preserved",
+ "named register masks must use one canonical lowercase spelling");
+
+ Context.expect(Text->targetIndex("test-index") == rcc::TargetIndex::fromValue(0) &&
+ Text->targetIndexName(rcc::TargetIndex::fromValue(0)) == "test-index",
+ "target index zero must remain a valid named target index");
+ Context.expect(Text->directOperandFlag("test-direct-three") == 0x3 &&
+ Text->bitmaskOperandFlag("test-bit-four") == 0x4,
+ "direct and bitmask target flags must retain distinct registries");
+
+ auto OperandNames = Text->operandFlagNames(0xD);
+ Context.expect(OperandNames &&
+ *OperandNames == std::vector<std::string_view>(
+ {"test-direct-one", "test-bit-four", "test-bit-eight"}),
+ "operand flag decomposition must be canonical and declaration ordered");
+ Context.expect(!Text->operandFlagNames(0x10),
+ "operand flag decomposition must reject unknown bits");
+
+ auto MMONames = Text->machineMemOperandFlagNames(0x3);
+ auto VRegNames = Text->virtualRegisterFlagNames(0x3);
+ Context.expect(
+ MMONames && *MMONames == std::vector<std::string_view>({"test-mmo-one", "test-mmo-two"}) &&
+ VRegNames &&
+ *VRegNames == std::vector<std::string_view>({"test-vreg-one", "test-vreg-two"}) &&
+ !Text->machineMemOperandFlagNames(0x4) && !Text->virtualRegisterFlagNames(0x4),
+ "MMO and virtual-register flags must reject every unregistered bit");
+
+ Context.expect(Text->intrinsic("llvm.test.seven") == rcc::IntrinsicID::fromValue(7) &&
+ Text->intrinsicName(rcc::IntrinsicID::fromValue(17)) == "llvm.test.seventeen",
+ "intrinsic names and strong IDs must round-trip");
+
+ const auto G0 = phys(rcc::test::TestRegister::G0);
+ Context.expect(Text->dwarfRegister(G0) == rcc::test::TestRegister::G0 &&
+ Text->physicalRegister(rcc::test::TestRegister::G0) == G0,
+ "CFI register mappings must support both physical and DWARF lookup");
+ Context.expect(Text->supportsStackID(rcc::StackID::defaultStack()) &&
+ Text->supportsStackID(rcc::StackID::noAllocStack()) &&
+ !Text->supportsStackID(rcc::StackID::sgprSpillStack()),
+ "target stack support must be explicit and include fixed baseline defaults");
+}
+
+void testTextSchemaRejection(TestContext &Context) {
+ auto Registers = rcc::test::createTestTargetRegisterInfo();
+ if (!Registers)
+ return;
+ auto Reject = [&Context, &Registers](rcc::TargetMIRTextInfoDesc Description,
+ std::string_view Message) {
+ Context.expect(!rcc::TargetMIRTextInfo::create(std::move(Description), *Registers), Message);
+ };
+
+ {
+ auto Description = rcc::test::createTestTargetMIRTextInfoDescription();
+ Description.RegisterMasks.push_back(
+ {"TEST-PRESERVED",
+ std::make_shared<const rcc::RegisterMask>(std::vector<std::uint64_t>{})});
+ Reject(std::move(Description), "register mask names must be unique case-insensitively");
+ }
+ {
+ auto Description = rcc::test::createTestTargetMIRTextInfoDescription();
+ Description.RegisterMasks.push_back({"other-mask", rcc::test::createTestCallPreservedMask()});
+ Reject(std::move(Description), "register mask values must have one canonical name");
+ }
+ {
+ auto Description = rcc::test::createTestTargetMIRTextInfoDescription();
+ Description.RegisterMasks.front().Mask = std::make_shared<const rcc::RegisterMask>(
+ std::vector<std::uint64_t>{std::uint64_t{1} << 63U});
+ Reject(std::move(Description), "named masks must reject unknown physical registers");
+ }
+ {
+ auto Description = rcc::test::createTestTargetMIRTextInfoDescription();
+ Description.TargetIndices.push_back({"other-index", rcc::TargetIndex::fromValue(0)});
+ Reject(std::move(Description), "target index values must be unique");
+ }
+ {
+ auto Description = rcc::test::createTestTargetMIRTextInfoDescription();
+ Description.DirectOperandFlagMask = 0;
+ Reject(std::move(Description), "direct flags require a direct flag mask");
+ }
+ {
+ auto Description = rcc::test::createTestTargetMIRTextInfoDescription();
+ Description.DirectOperandFlags.front().Value = 0x4;
+ Reject(std::move(Description), "direct flags must remain inside their direct field");
+ }
+ {
+ auto Description = rcc::test::createTestTargetMIRTextInfoDescription();
+ Description.BitmaskOperandFlags.front().Value = 0xC;
+ Reject(std::move(Description), "bitmask operand flags must contain one bit");
+ }
+ {
+ auto Description = rcc::test::createTestTargetMIRTextInfoDescription();
+ Description.MachineMemOperandFlags.front().Value = 0x3;
+ Reject(std::move(Description), "MMO target flags must contain one bit");
+ }
+ {
+ auto Description = rcc::test::createTestTargetMIRTextInfoDescription();
+ Description.VirtualRegisterFlags.back().Value = Description.VirtualRegisterFlags.front().Value;
+ Reject(std::move(Description), "virtual-register target flag values must be unique");
+ }
+ {
+ auto Description = rcc::test::createTestTargetMIRTextInfoDescription();
+ Description.Intrinsics.front().Name = "test.not-an-intrinsic";
+ Reject(std::move(Description), "intrinsic names must use the llvm namespace");
+ }
+ {
+ auto Description = rcc::test::createTestTargetMIRTextInfoDescription();
+ Description.CFIRegisters[1].DwarfRegister = Description.CFIRegisters[0].DwarfRegister;
+ Reject(std::move(Description),
+ "a DWARF register must not have multiple canonical physical registers");
+ }
+ {
+ auto Description = rcc::test::createTestTargetMIRTextInfoDescription();
+ Description.CFIRegisters.front().IsCanonical = false;
+ Reject(std::move(Description),
+ "every referenced DWARF register must have a canonical physical register");
+ }
+ {
+ auto Description = rcc::test::createTestTargetMIRTextInfoDescription();
+ Description.CFIRegisters.front().Register = phys(99);
+ Reject(std::move(Description), "CFI mappings must reject unknown physical registers");
+ }
+ {
+ auto Description = rcc::test::createTestTargetMIRTextInfoDescription();
+ Description.FunctionStateType = rcc::TargetFunctionStateTypeID{};
+ Reject(std::move(Description), "target function state schemas require a nonzero TypeID");
+ }
+ {
+ auto Description = rcc::test::createTestTargetMIRTextInfoDescription();
+ Description.SupportedStackIDs.pop_back();
+ Reject(std::move(Description), "targets must support the noalloc stack ID");
+ }
+ {
+ auto Description = rcc::test::createTestTargetMIRTextInfoDescription();
+ Description.SupportedStackIDs.push_back(rcc::StackID::defaultStack());
+ Reject(std::move(Description), "supported stack IDs must be unique");
+ }
+
+ {
+ auto Description = rcc::test::createTestTargetMIRTextInfoDescription();
+ Description.CFIRegisters[1].DwarfRegister = Description.CFIRegisters[0].DwarfRegister;
+ Description.CFIRegisters[1].IsCanonical = false;
+ auto Text = rcc::TargetMIRTextInfo::create(std::move(Description), *Registers);
+ Context.expect(Text &&
+ Text->dwarfRegister(phys(rcc::test::TestRegister::G0Low32)) ==
+ rcc::test::TestRegister::G0 &&
+ Text->physicalRegister(rcc::test::TestRegister::G0) ==
+ phys(rcc::test::TestRegister::G0),
+ "noncanonical physical aliases must map to one canonical DWARF register");
+ }
+
+ {
+ auto Description = rcc::test::createTestTargetRegisterInfoDescription();
+ Description.PhysicalRegisters[1].Name = "G0";
+ Context.expect(!rcc::TargetRegisterInfo::create(std::move(Description)),
+ "physical register names must be case-insensitively unique");
+ }
+ {
+ auto Description = rcc::test::createTestTargetRegisterInfoDescription();
+ Description.RegisterClasses[1].Name = "gpr64";
+ Context.expect(!rcc::TargetRegisterInfo::create(std::move(Description)),
+ "register class names must be case-insensitively unique");
+ }
+ {
+ auto Description = rcc::test::createTestTargetRegisterInfoDescription();
+ Description.RegisterBanks[1].Name = "gpr";
+ Context.expect(!rcc::TargetRegisterInfo::create(std::move(Description)),
+ "register bank names must be case-insensitively unique");
+ }
+}
+
+void testSemanticValidation(TestContext &Context) {
+ rcc::OperandDescriptor Operand;
+ Operand.Kinds = {rcc::MachineOperand::Kind::Immediate};
+ Operand.AllowedTargetFlags = 0xF;
+
+ rcc::InstructionDescriptor Instruction;
+ Instruction.InstructionOpcode = rcc::Opcode::fromValue(1);
+ Instruction.Name = "TEST_LOAD_IMM";
+ Instruction.Operands.push_back(Operand);
+ Instruction.Flags = {
+ rcc::InstructionFlag::ProducesCode,
+ rcc::InstructionFlag::MayLoad,
+ };
+ Instruction.RequiresMemoryOperands = true;
+
+ auto Instructions = rcc::InstructionSet::create({std::move(Instruction)});
+ if (!Instructions)
+ return;
+ auto Target = rcc::test::createTestTargetDescription(std::move(*Instructions));
+ auto Function =
+ rcc::MachineFunction::create("schema_validation", rcc::MachineFunctionStableID::fromValue(1));
+ Context.expect(Target.has_value() && Function.has_value(),
+ "semantic target schema fixture must be constructible");
+ if (!Target || !Function)
+ return;
+
+ auto Reg = (*Function)->registerInfo().createVirtualRegister();
+ Context.expect(Reg && (*Function)->registerInfo().setTargetRegisterFlags(*Reg, 0x3).has_value() &&
+ Target->validateRegisterInfo((*Function)->registerInfo()).has_value(),
+ "registered virtual-register target flags must pass target validation");
+ if (Reg) {
+ Context.expect((*Function)->registerInfo().setTargetRegisterFlags(*Reg, 0x4).has_value() &&
+ !Target->validateRegisterInfo((*Function)->registerInfo()),
+ "unregistered virtual-register target flags must fail target validation");
+ Context.expect(
+ (*Function)->registerInfo().clearTargetRegisterFlags(*Reg).has_value(),
+ "semantic validation fixture must restore registered virtual-register target flags");
+ }
+
+ const rcc::MachineOperand ValidOperand = rcc::MachineOperand::immediate(7, 0x5);
+ const rcc::MachineOperand InvalidOperand = rcc::MachineOperand::immediate(7, 0x10);
+ Context.expect(
+ Target->validateExplicitOperands(rcc::Opcode::fromValue(1),
+ std::span<const rcc::MachineOperand>(&ValidOperand, 1),
+ (*Function)->registerInfo())
+ .has_value() &&
+ !Target->validateExplicitOperands(
+ rcc::Opcode::fromValue(1), std::span<const rcc::MachineOperand>(&InvalidOperand, 1),
+ (*Function)->registerInfo()),
+ "machine operands must use only descriptor-allowed serializable target flags");
+
+ rcc::MachineMemOperandDesc MemoryDescription;
+ MemoryDescription.Accesses = {rcc::MemoryAccessFlag::Load};
+ MemoryDescription.Size = *rcc::MemorySize::fixed(8);
+ MemoryDescription.Align = *rcc::Alignment::create(8);
+ MemoryDescription.TargetFlags = 0x3;
+ auto ValidMemory = rcc::MachineMemOperand::create(MemoryDescription);
+ MemoryDescription.TargetFlags = 0x4;
+ auto InvalidMemory = rcc::MachineMemOperand::create(MemoryDescription);
+ if (ValidMemory && InvalidMemory) {
+ const std::array<const rcc::MachineMemOperand *, 1> ValidOperands{&*ValidMemory};
+ const std::array<const rcc::MachineMemOperand *, 1> InvalidOperands{&*InvalidMemory};
+ Context.expect(
+ Target->validateMemOperands(rcc::Opcode::fromValue(1), ValidOperands).has_value() &&
+ !Target->validateMemOperands(rcc::Opcode::fromValue(1), InvalidOperands),
+ "machine memory operands must use only registered target flags");
+ }
+
+ rcc::MachineCFIInfo CFI;
+ auto CFIIndex = CFI.addInstruction(
+ rcc::MachineCFIInstruction::sameValue(rcc::DwarfRegisterNumber::fromValue(99)));
+ Context.expect(CFIIndex && !Target->validateCFIInfo(CFI),
+ "CFI instructions must reject unmapped DWARF registers");
+
+ rcc::MachineFrameInfo Frame;
+ rcc::StackObjectDesc StackObject;
+ StackObject.Kind = rcc::StackObjectKind::Target;
+ StackObject.Size = 8;
+ StackObject.Align = *rcc::Alignment::create(8);
+ StackObject.Stack = rcc::StackID::sgprSpillStack();
+ auto StackIndex = Frame.createObject(StackObject);
+ Context.expect(StackIndex && !Target->validateFrameInfo(Frame),
+ "frame objects must reject stack IDs unsupported by the target");
+
+ rcc::OperandDescriptor BadOperand;
+ BadOperand.Kinds = {rcc::MachineOperand::Kind::Immediate};
+ BadOperand.AllowedTargetFlags = 0x10;
+ rcc::InstructionDescriptor BadInstruction;
+ BadInstruction.InstructionOpcode = rcc::Opcode::fromValue(2);
+ BadInstruction.Name = "TEST_BAD_FLAGS";
+ BadInstruction.Operands.push_back(BadOperand);
+ BadInstruction.Flags = {rcc::InstructionFlag::ProducesCode};
+ auto BadInstructions = rcc::InstructionSet::create({std::move(BadInstruction)});
+ Context.expect(BadInstructions &&
+ !rcc::test::createTestTargetDescription(std::move(*BadInstructions)),
+ "target construction must reject operand flag bits outside its text schema");
+
+ auto PartialDescription = rcc::test::createTestTargetMIRTextInfoDescription();
+ PartialDescription.DirectOperandFlags.erase(PartialDescription.DirectOperandFlags.begin() + 1,
+ PartialDescription.DirectOperandFlags.end());
+ auto Registers = rcc::test::createTestTargetRegisterInfo();
+ auto PartialText =
+ Registers ? rcc::TargetMIRTextInfo::create(std::move(PartialDescription), *Registers)
+ : rcc::Expected<rcc::TargetMIRTextInfo>(std::unexpected(Registers.error()));
+ Context.expect(PartialText && !PartialText->validateOperandFlags(0x2),
+ "unused direct-field encodings must remain invalid");
+}
+
+} // namespace
+
+int main() {
+ TestContext Context;
+ testTargetRegisterNames(Context);
+ testTextSchemaQueries(Context);
+ testTextSchemaRejection(Context);
+ testSemanticValidation(Context);
+ return Context.result();
+}
diff --git a/backend/unittests/Target/TestTarget.cpp b/backend/unittests/Target/TestTarget.cpp
index 30fa22f..4d31fb9 100644
--- a/backend/unittests/Target/TestTarget.cpp
+++ b/backend/unittests/Target/TestTarget.cpp
@@ -156,11 +156,50 @@ Expected<TargetRegisterInfo> createTestTargetRegisterInfo() {
return TargetRegisterInfo::create(createTestTargetRegisterInfoDescription());
}
+TargetMIRTextInfoDesc createTestTargetMIRTextInfoDescription() {
+ TargetMIRTextInfoDesc Description;
+ Description.RegisterMasks = {
+ {"Test-Preserved", createTestCallPreservedMask()},
+ };
+ Description.TargetIndices = {
+ {"test-index", TargetIndex::fromValue(0)},
+ };
+ Description.DirectOperandFlagMask = 0x3;
+ Description.DirectOperandFlags = {
+ {"test-direct-one", 0x1},
+ {"test-direct-two", 0x2},
+ {"test-direct-three", 0x3},
+ };
+ Description.BitmaskOperandFlags = {
+ {"test-bit-four", 0x4},
+ {"test-bit-eight", 0x8},
+ };
+ Description.MachineMemOperandFlags = {
+ {"test-mmo-one", 0x1},
+ {"test-mmo-two", 0x2},
+ };
+ Description.VirtualRegisterFlags = {
+ {"test-vreg-one", 0x1},
+ {"test-vreg-two", 0x2},
+ };
+ Description.Intrinsics = {
+ {"llvm.test.seven", IntrinsicID::fromValue(7)},
+ {"llvm.test.seventeen", IntrinsicID::fromValue(17)},
+ };
+
+ for (const PhysicalRegisterDesc &Register :
+ createTestTargetRegisterInfoDescription().PhysicalRegisters) {
+ Description.CFIRegisters.push_back({Register.Reg, Register.Reg.number(), true});
+ }
+ return Description;
+}
+
Expected<TargetDescription> createTestTargetDescription(InstructionSet Instructions) {
auto Registers = createTestTargetRegisterInfo();
if (!Registers)
return std::unexpected(Registers.error());
- return TargetDescription::create(std::move(Instructions), std::move(*Registers));
+ return TargetDescription::create(std::move(Instructions), std::move(*Registers),
+ createTestTargetMIRTextInfoDescription());
}
std::shared_ptr<const RegisterMask> createTestCallPreservedMask() {
diff --git a/backend/unittests/Target/TestTarget.h b/backend/unittests/Target/TestTarget.h
index 3c2aa51..fe434a2 100644
--- a/backend/unittests/Target/TestTarget.h
+++ b/backend/unittests/Target/TestTarget.h
@@ -6,6 +6,7 @@
#include "rcc/CodeGen/InstructionDescriptor.h"
#include "rcc/CodeGen/RegisterMask.h"
#include "rcc/CodeGen/TargetDescription.h"
+#include "rcc/CodeGen/TargetMIRTextInfo.h"
#include "rcc/CodeGen/TargetRegisterInfo.h"
#include "rcc/Support/Error.h"
@@ -56,6 +57,7 @@ struct TestRegisterClass final {
[[nodiscard]] TargetRegisterInfoDesc createTestTargetRegisterInfoDescription();
[[nodiscard]] Expected<TargetRegisterInfo> createTestTargetRegisterInfo();
+[[nodiscard]] TargetMIRTextInfoDesc createTestTargetMIRTextInfoDescription();
[[nodiscard]] Expected<TargetDescription> createTestTargetDescription(InstructionSet Instructions);
[[nodiscard]] std::shared_ptr<const RegisterMask> createTestCallPreservedMask();