aboutsummaryrefslogtreecommitdiff
path: root/backend/unittests/CodeGen
diff options
context:
space:
mode:
authorRichard-Qin-X <richard.qin.001@gmail.com>2026-08-01 17:36:40 +0800
committerRichard-Qin-X <richard.qin.001@gmail.com>2026-08-01 17:36:40 +0800
commit787f3a7a20958bfa6f231abccc617f4d8de25c4d (patch)
tree775d2913f48fc5f3a6e44bc6da986da9e52754ef /backend/unittests/CodeGen
parent77d76b73a614820bf45024b053db376f35b06d10 (diff)
downloadrcc-project-787f3a7a20958bfa6f231abccc617f4d8de25c4d.tar.gz
rcc-project-787f3a7a20958bfa6f231abccc617f4d8de25c4d.tar.xz
[CodeGen] Add MIR function and block attributes
Diffstat (limited to 'backend/unittests/CodeGen')
-rw-r--r--backend/unittests/CodeGen/CMakeLists.txt15
-rw-r--r--backend/unittests/CodeGen/MachineIRAttributesTest.cpp206
2 files changed, 221 insertions, 0 deletions
diff --git a/backend/unittests/CodeGen/CMakeLists.txt b/backend/unittests/CodeGen/CMakeLists.txt
index e128d0b..ff38ed7 100644
--- a/backend/unittests/CodeGen/CMakeLists.txt
+++ b/backend/unittests/CodeGen/CMakeLists.txt
@@ -9,6 +9,7 @@ set(
MachineConstantPoolTest.cpp
MachineFrameInfoTest.cpp
MachineFunctionClonerTest.cpp
+ MachineIRAttributesTest.cpp
MachineIRBuilderTest.cpp
MachineIRCoreTest.cpp
MachineJumpTableInfoTest.cpp
@@ -110,6 +111,18 @@ add_test(
COMMAND RCCBackendCodeGenMachineIRBuilderTests
)
+add_executable(RCCBackendCodeGenMachineIRAttributesTests MachineIRAttributesTest.cpp)
+rcc_backend_configure_target(RCCBackendCodeGenMachineIRAttributesTests)
+target_link_libraries(
+ RCCBackendCodeGenMachineIRAttributesTests
+ PRIVATE RCCBackend::CodeGen RCCBackend::TestTarget
+)
+
+add_test(
+ NAME RCCBackend.CodeGen.MachineIRAttributes
+ COMMAND RCCBackendCodeGenMachineIRAttributesTests
+)
+
add_executable(RCCBackendCodeGenMachineVerifierTests MachineVerifierTest.cpp)
rcc_backend_configure_target(RCCBackendCodeGenMachineVerifierTests)
target_link_libraries(
@@ -205,6 +218,7 @@ rcc_backend_register_format_files(
"${CMAKE_CURRENT_SOURCE_DIR}/MachineConstantPoolTest.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/MachineFrameInfoTest.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/MachineFunctionClonerTest.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/MachineIRAttributesTest.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/MachineIRBuilderTest.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/MachineIRCoreTest.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/MachineJumpTableInfoTest.cpp"
@@ -223,6 +237,7 @@ rcc_backend_register_tidy_files(
"${CMAKE_CURRENT_SOURCE_DIR}/MachineConstantPoolTest.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/MachineFrameInfoTest.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/MachineFunctionClonerTest.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/MachineIRAttributesTest.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/MachineIRBuilderTest.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/MachineIRCoreTest.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/MachineJumpTableInfoTest.cpp"
diff --git a/backend/unittests/CodeGen/MachineIRAttributesTest.cpp b/backend/unittests/CodeGen/MachineIRAttributesTest.cpp
new file mode 100644
index 0000000..5619210
--- /dev/null
+++ b/backend/unittests/CodeGen/MachineIRAttributesTest.cpp
@@ -0,0 +1,206 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "Target/TestTarget.h"
+#include "rcc/CodeGen/InstructionDescriptor.h"
+#include "rcc/CodeGen/MachineBasicBlock.h"
+#include "rcc/CodeGen/MachineBasicBlockAttributes.h"
+#include "rcc/CodeGen/MachineFunction.h"
+#include "rcc/CodeGen/MachineFunctionAttributes.h"
+#include "rcc/CodeGen/MachineFunctionCloner.h"
+#include "rcc/CodeGen/MachineIRTypes.h"
+#include "rcc/CodeGen/MachineInstr.h"
+#include "rcc/CodeGen/MachineVerifier.h"
+#include "rcc/CodeGen/TargetDescription.h"
+
+#include <cstdint>
+#include <iostream>
+#include <memory>
+#include <string>
+#include <string_view>
+#include <utility>
+#include <vector>
+
+namespace {
+
+constexpr std::uint32_t NopOpcode = 1;
+
+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::Expected<rcc::TargetDescription> createTarget() {
+ rcc::InstructionDescriptor Nop;
+ Nop.InstructionOpcode = rcc::Opcode::fromValue(NopOpcode);
+ Nop.Name = "TEST_NOP";
+ Nop.Flags = {rcc::InstructionFlag::ProducesCode};
+
+ std::vector<rcc::InstructionDescriptor> Descriptors;
+ Descriptors.push_back(std::move(Nop));
+ auto Instructions = rcc::InstructionSet::create(std::move(Descriptors));
+ if (!Instructions)
+ return std::unexpected(Instructions.error());
+ return rcc::test::createTestTargetDescription(std::move(*Instructions));
+}
+
+[[nodiscard]] std::unique_ptr<rcc::MachineFunction> createFunction(TestContext &Context,
+ std::uint64_t StableID) {
+ auto Function =
+ rcc::MachineFunction::create("attributes", rcc::MachineFunctionStableID::fromValue(StableID));
+ Context.expect(Function.has_value(), "machine function creation must succeed");
+ if (!Function)
+ return nullptr;
+ return std::move(*Function);
+}
+
+void testAttributeValueTypes(TestContext &Context) {
+ rcc::MachineFunctionAttributes FunctionAttributes;
+ FunctionAttributes.set(rcc::MachineFunctionAttribute::ExposesReturnsTwice);
+ FunctionAttributes.set(rcc::MachineFunctionAttribute::TracksDebugUserValues);
+ Context.expect(FunctionAttributes.has(rcc::MachineFunctionAttribute::ExposesReturnsTwice) &&
+ FunctionAttributes.has(rcc::MachineFunctionAttribute::TracksDebugUserValues) &&
+ rcc::getMachineFunctionAttributeName(
+ rcc::MachineFunctionAttribute::TracksDebugUserValues) ==
+ "TracksDebugUserValues",
+ "function attribute flags and names must be stable");
+
+ auto DecodedFunctionAttributes =
+ rcc::MachineFunctionAttributes::fromRawFlags(FunctionAttributes.rawFlags());
+ Context.expect(DecodedFunctionAttributes.has_value() &&
+ DecodedFunctionAttributes->rawFlags() == FunctionAttributes.rawFlags(),
+ "function attribute raw flags must round trip");
+ Context.expect(!rcc::MachineFunctionAttributes::fromRawFlags(std::uint16_t{1} << 15U),
+ "unknown function attribute bits must be rejected");
+
+ rcc::MachineBasicBlockAttributes BlockAttributes;
+ Context.expect(!BlockAttributes.setName("") && !BlockAttributes.setIRBlockName(""),
+ "empty block and IR block names must be rejected");
+ Context.expect(BlockAttributes.setName("entry").has_value() &&
+ BlockAttributes.setIRBlockName("entry").has_value(),
+ "decoded block and IR block names must be accepted");
+ BlockAttributes.set(rcc::MachineBasicBlockAttribute::IRBlockAddressTaken);
+ BlockAttributes.setCallFrameSize(0);
+ Context.expect(BlockAttributes.verify().has_value() && BlockAttributes.callFrameSize() == 0,
+ "zero call-frame size must be the semantic default");
+ BlockAttributes.setSectionID(rcc::MachineBasicBlockSectionID::exception());
+ BlockAttributes.setProfileID({.BaseID = 7, .CloneID = 2});
+ Context.expect(
+ BlockAttributes.sectionID().kind() == rcc::MachineBasicBlockSectionKind::Exception &&
+ BlockAttributes.sectionID().number() == 0 &&
+ BlockAttributes.profileID() == rcc::MachineBasicBlockProfileID{.BaseID = 7, .CloneID = 2},
+ "special section and fixed profile identities must remain structured");
+ Context.expect(!rcc::MachineBasicBlockAttributes::fromRawFlags(std::uint8_t{1} << 7U),
+ "unknown block attribute bits must be rejected");
+
+ rcc::MachineInstrFlags Flags{
+ rcc::MachineInstrFlag::NoConvergent, rcc::MachineInstrFlag::NonNeg,
+ rcc::MachineInstrFlag::Disjoint, rcc::MachineInstrFlag::NoUSWrap,
+ rcc::MachineInstrFlag::SameSign, rcc::MachineInstrFlag::InBounds,
+ };
+ Context.expect(
+ Flags.has(rcc::MachineInstrFlag::NoConvergent) && Flags.has(rcc::MachineInstrFlag::NonNeg) &&
+ Flags.has(rcc::MachineInstrFlag::Disjoint) &&
+ Flags.has(rcc::MachineInstrFlag::NoUSWrap) &&
+ Flags.has(rcc::MachineInstrFlag::SameSign) && Flags.has(rcc::MachineInstrFlag::InBounds),
+ "all fixed MIR instruction flags must have independent storage");
+}
+
+void testVerifierAndClone(TestContext &Context) {
+ auto Target = createTarget();
+ auto Function = createFunction(Context, 1);
+ if (!Target || Function == nullptr)
+ return;
+
+ auto FunctionAlignment = rcc::Alignment::create(32);
+ auto BlockAlignment = rcc::Alignment::create(16);
+ Context.expect(FunctionAlignment.has_value() && BlockAlignment.has_value(),
+ "power-of-two alignments must be accepted");
+ if (!FunctionAlignment || !BlockAlignment)
+ return;
+
+ Function->attributes().set(rcc::MachineFunctionAttribute::ExposesReturnsTwice);
+ Function->attributes().set(rcc::MachineFunctionAttribute::HasEHScopes);
+ Function->attributes().set(rcc::MachineFunctionAttribute::DebugInstrRef);
+ Function->attributes().setAlignment(*FunctionAlignment);
+
+ auto BlockResult = Function->createBasicBlock();
+ Context.expect(BlockResult.has_value(), "machine basic block creation must succeed");
+ if (!BlockResult)
+ return;
+ rcc::MachineBasicBlock *Block = *BlockResult;
+ Context.expect(Function->appendBlock(*Block).has_value(),
+ "machine basic block linking must succeed");
+ Context.expect(Block->attributes().setName("entry").has_value(),
+ "machine basic block name must be stored");
+ Block->attributes().set(rcc::MachineBasicBlockAttribute::IRBlockAddressTaken);
+ Context.expect(!rcc::MachineVerifier::verify(*Function, *Target),
+ "IR-block-address-taken must require an IR block association");
+
+ Context.expect(Block->attributes().setIRBlockName("entry").has_value(),
+ "IR block association must be stored");
+ Block->attributes().set(rcc::MachineBasicBlockAttribute::LandingPad);
+ Block->attributes().setAlignment(*BlockAlignment);
+ Block->attributes().setSectionID(rcc::MachineBasicBlockSectionID::numbered(3));
+ Block->attributes().setProfileID({.BaseID = 11, .CloneID = 4});
+ Block->attributes().setCallFrameSize(64);
+
+ auto InstructionResult = Function->createInstruction(rcc::Opcode::fromValue(NopOpcode));
+ Context.expect(InstructionResult.has_value(), "machine instruction creation must succeed");
+ if (!InstructionResult)
+ return;
+ rcc::MachineInstr *Instruction = *InstructionResult;
+ Instruction->setFlags({
+ rcc::MachineInstrFlag::NoConvergent,
+ rcc::MachineInstrFlag::NonNeg,
+ rcc::MachineInstrFlag::Disjoint,
+ rcc::MachineInstrFlag::NoUSWrap,
+ rcc::MachineInstrFlag::SameSign,
+ rcc::MachineInstrFlag::InBounds,
+ });
+ Context.expect(Block->appendInstruction(*Instruction).has_value(),
+ "machine instruction linking must succeed");
+ Context.expect(rcc::MachineVerifier::verify(*Function, *Target).has_value(),
+ "function and block MIR attributes must pass the unified verifier");
+
+ auto Clone =
+ rcc::cloneMachineFunction(*Function, *Target,
+ rcc::MachineFunctionCloneOptions{
+ .NewStableID = rcc::MachineFunctionStableID::fromValue(2),
+ .NewName = std::string("attributes.clone"),
+ });
+ Context.expect(Clone.has_value(), "attributed machine function clone must succeed");
+ if (!Clone)
+ return;
+
+ const rcc::MachineFunction &ClonedFunction = *Clone->ClonedFunction;
+ const rcc::MachineBasicBlock *ClonedBlock = ClonedFunction.firstBlock();
+ const rcc::MachineInstr *ClonedInstruction =
+ ClonedBlock == nullptr ? nullptr : ClonedBlock->firstInstruction();
+ Context.expect(ClonedFunction.attributes() == Function->attributes(),
+ "function MIR attributes must survive deep cloning");
+ Context.expect(ClonedBlock != nullptr && ClonedBlock->attributes() == Block->attributes(),
+ "basic block MIR attributes must survive deep cloning");
+ Context.expect(ClonedInstruction != nullptr && ClonedInstruction->flags() == Instruction->flags(),
+ "complete MIR instruction flags must survive deep cloning");
+}
+
+} // namespace
+
+int main() {
+ TestContext Context;
+ testAttributeValueTypes(Context);
+ testVerifierAndClone(Context);
+ return Context.result();
+}