aboutsummaryrefslogtreecommitdiff
path: root/backend/unittests/CodeGen
diff options
context:
space:
mode:
authorRichard-Qin-X <richard.qin.001@gmail.com>2026-08-13 05:45:37 +0800
committerRichard-Qin-X <richard.qin.001@gmail.com>2026-08-13 05:45:37 +0800
commit236082a0fe4dcd0362c5c74923a93e3db6a01291 (patch)
tree5d8a329ca526b087b6ce51895b5135997e8e03bd /backend/unittests/CodeGen
parenta2f8d7c5e22b510965327383410bd576f7cf28e7 (diff)
downloadrcc-project-236082a0fe4dcd0362c5c74923a93e3db6a01291.tar.gz
rcc-project-236082a0fe4dcd0362c5c74923a93e3db6a01291.tar.xz
[CodeGen] Align pass manager ownership
Diffstat (limited to 'backend/unittests/CodeGen')
-rw-r--r--backend/unittests/CodeGen/CMakeLists.txt41
-rw-r--r--backend/unittests/CodeGen/GlobalISel/InstructionSelectTest.cpp14
-rw-r--r--backend/unittests/CodeGen/GlobalISel/LegalizerTest.cpp10
-rw-r--r--backend/unittests/CodeGen/GlobalISel/RegBankSelectTest.cpp4
-rw-r--r--backend/unittests/CodeGen/LiveIntervalTest.cpp2
-rw-r--r--backend/unittests/CodeGen/LiveIntervalsTest.cpp8
-rw-r--r--backend/unittests/CodeGen/LivePhysRegsTest.cpp2
-rw-r--r--backend/unittests/CodeGen/LiveRegMatrixTest.cpp4
-rw-r--r--backend/unittests/CodeGen/LiveVariablesTest.cpp26
-rw-r--r--backend/unittests/CodeGen/MachineAnalysesTest.cpp24
-rw-r--r--backend/unittests/CodeGen/MachineOptimizationTest.cpp20
-rw-r--r--backend/unittests/CodeGen/MachinePassManagerTest.cpp426
-rw-r--r--backend/unittests/CodeGen/MachinePassTest.cpp918
-rw-r--r--backend/unittests/CodeGen/MachineSchedulingTest.cpp8
-rw-r--r--backend/unittests/CodeGen/PHIEliminationTest.cpp8
-rw-r--r--backend/unittests/CodeGen/PassInstrumentationTest.cpp173
-rw-r--r--backend/unittests/CodeGen/PassManagerTest.cpp260
-rw-r--r--backend/unittests/CodeGen/RegisterAllocationTest.cpp14
-rw-r--r--backend/unittests/CodeGen/RegisterAllocationTestSupport.h4
-rw-r--r--backend/unittests/CodeGen/SlotIndexesTest.cpp2
-rw-r--r--backend/unittests/CodeGen/TwoAddressInstructionPassTest.cpp8
21 files changed, 975 insertions, 1001 deletions
diff --git a/backend/unittests/CodeGen/CMakeLists.txt b/backend/unittests/CodeGen/CMakeLists.txt
index 0715def..ac6ab36 100644
--- a/backend/unittests/CodeGen/CMakeLists.txt
+++ b/backend/unittests/CodeGen/CMakeLists.txt
@@ -27,7 +27,9 @@ set(
MachineMetadataTest.cpp
MachineOperandTest.cpp
MachineOptimizationTest.cpp
- MachinePassTest.cpp
+ PassManagerTest.cpp
+ PassInstrumentationTest.cpp
+ MachinePassManagerTest.cpp
MachineSchedulingTest.cpp
PBQPTest.cpp
RegisterAllocationTest.cpp
@@ -343,14 +345,35 @@ add_test(
COMMAND RCCBackendCodeGenMachineOperandTests
)
-add_executable(RCCBackendCodeGenMachinePassTests MachinePassTest.cpp)
-rcc_backend_configure_target(RCCBackendCodeGenMachinePassTests)
+add_executable(RCCBackendCodeGenPassManagerTests PassManagerTest.cpp)
+rcc_backend_configure_target(RCCBackendCodeGenPassManagerTests)
target_link_libraries(
- RCCBackendCodeGenMachinePassTests
+ RCCBackendCodeGenPassManagerTests
PRIVATE RCCBackend::CodeGen RCCBackend::TestTarget
)
+add_test(NAME RCCBackend.CodeGen.PassManager COMMAND RCCBackendCodeGenPassManagerTests)
-add_test(NAME RCCBackend.CodeGen.MachinePass COMMAND RCCBackendCodeGenMachinePassTests)
+add_executable(RCCBackendCodeGenPassInstrumentationTests PassInstrumentationTest.cpp)
+rcc_backend_configure_target(RCCBackendCodeGenPassInstrumentationTests)
+target_link_libraries(
+ RCCBackendCodeGenPassInstrumentationTests
+ PRIVATE RCCBackend::CodeGen RCCBackend::TestTarget
+)
+add_test(
+ NAME RCCBackend.CodeGen.PassInstrumentation
+ COMMAND RCCBackendCodeGenPassInstrumentationTests
+)
+
+add_executable(RCCBackendCodeGenMachinePassManagerTests MachinePassManagerTest.cpp)
+rcc_backend_configure_target(RCCBackendCodeGenMachinePassManagerTests)
+target_link_libraries(
+ RCCBackendCodeGenMachinePassManagerTests
+ PRIVATE RCCBackend::CodeGen RCCBackend::TestTarget
+)
+add_test(
+ NAME RCCBackend.CodeGen.MachinePassManager
+ COMMAND RCCBackendCodeGenMachinePassManagerTests
+)
add_subdirectory(GlobalISel)
@@ -495,7 +518,9 @@ rcc_backend_register_format_files(
"${CMAKE_CURRENT_SOURCE_DIR}/MachineMetadataTest.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/MachineOperandTest.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/MachineOptimizationTest.cpp"
- "${CMAKE_CURRENT_SOURCE_DIR}/MachinePassTest.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/PassManagerTest.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/PassInstrumentationTest.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/MachinePassManagerTest.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/MachineSchedulingTest.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/PBQPTest.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/RegisterAllocationTest.cpp"
@@ -544,7 +569,9 @@ rcc_backend_register_tidy_files(
"${CMAKE_CURRENT_SOURCE_DIR}/MachineMetadataTest.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/MachineOperandTest.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/MachineOptimizationTest.cpp"
- "${CMAKE_CURRENT_SOURCE_DIR}/MachinePassTest.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/PassManagerTest.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/PassInstrumentationTest.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/MachinePassManagerTest.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/MachineSchedulingTest.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/PBQPTest.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/RegisterAllocationTest.cpp"
diff --git a/backend/unittests/CodeGen/GlobalISel/InstructionSelectTest.cpp b/backend/unittests/CodeGen/GlobalISel/InstructionSelectTest.cpp
index e98f121..30c2b89 100644
--- a/backend/unittests/CodeGen/GlobalISel/InstructionSelectTest.cpp
+++ b/backend/unittests/CodeGen/GlobalISel/InstructionSelectTest.cpp
@@ -120,7 +120,7 @@ void testPipeline(TestContext &Context) {
Fixture.Function->properties() = {rcc::MachineFunctionProperty::IsSSA,
rcc::MachineFunctionProperty::TracksLiveness};
const rcc::riscv::RISCVSubtarget Subtarget(rcc::riscv::RISCVSubtargetFeatures::rv64gc());
- rcc::MachinePassManager Pipeline;
+ rcc::MachineFunctionPassManager Pipeline;
Context.expect(
Pipeline.addPass(std::make_unique<rcc::Legalizer>(Subtarget.getLegalizerInfo())).has_value(),
"legalizer must be accepted by the pass manager");
@@ -131,7 +131,7 @@ void testPipeline(TestContext &Context) {
Pipeline.addPass(std::make_unique<rcc::InstructionSelect>(Subtarget.getInstructionSelector()))
.has_value(),
"instruction selector must be accepted by the pass manager");
- rcc::MachineAnalysisManager Analyses;
+ rcc::MachineFunctionAnalysisManager Analyses;
auto Run = Pipeline.run(*Fixture.Function, *Target, Analyses);
Context.expect(Run.has_value(), "RV64GC generic pipeline must complete");
Context.expect(Fixture.Function->properties().has(rcc::MachineFunctionProperty::Selected),
@@ -167,7 +167,7 @@ void testInstructionSelectionTransaction(TestContext &Context) {
rcc::MachineFunctionProperty::RegBankSelected};
MutatingFailingSelector Selector;
rcc::InstructionSelect Pass(Selector);
- rcc::MachineAnalysisManager Analyses;
+ rcc::MachineFunctionAnalysisManager Analyses;
auto Run = Pass.run(*Fixture.Function, *Target, Analyses);
Context.expect(!Run && Run.error().code() == rcc::ErrorCode::Unsupported,
"a renderer failure must be reported before publication");
@@ -203,7 +203,7 @@ void testInstructionSelectionOrder(TestContext &Context) {
const rcc::riscv::RISCVSubtarget Subtarget(rcc::riscv::RISCVSubtargetFeatures::rv64gc());
RecordingSelector Selector(*Subtarget.getInstructionSelector());
rcc::InstructionSelect Pass(Selector);
- rcc::MachineAnalysisManager Analyses;
+ rcc::MachineFunctionAnalysisManager Analyses;
auto Run = Pass.run(*Fixture.Function, *Target, Analyses);
Context.expect(Run.has_value(), "recorded instruction selection must complete");
std::ranges::reverse(LayoutIDs);
@@ -233,7 +233,7 @@ void testInstructionSelectionWorklist(TestContext &Context) {
rcc::MachineFunctionProperty::RegBankSelected};
TwoStepSelector Selector;
rcc::InstructionSelect Pass(Selector);
- rcc::MachineAnalysisManager Analyses;
+ rcc::MachineFunctionAnalysisManager Analyses;
auto Run = Pass.run(*Fixture.Function, *Target, Analyses);
Context.expect(Run.has_value(), "a progressing multi-step selection must complete");
Context.expect(Selector.selections() == 4,
@@ -326,7 +326,7 @@ void testExtendedRV64GCPipeline(TestContext &Context) {
Fixture.Function->properties() = {rcc::MachineFunctionProperty::IsSSA,
rcc::MachineFunctionProperty::TracksLiveness};
const rcc::riscv::RISCVSubtarget Subtarget(rcc::riscv::RISCVSubtargetFeatures::rv64gc());
- rcc::MachinePassManager Pipeline;
+ rcc::MachineFunctionPassManager Pipeline;
if (!Pipeline.addPass(std::make_unique<rcc::Legalizer>(Subtarget.getLegalizerInfo())) ||
!Pipeline.addPass(std::make_unique<rcc::RegBankSelect>(Subtarget.getRegBankInfo())) ||
!Pipeline.addPass(
@@ -334,7 +334,7 @@ void testExtendedRV64GCPipeline(TestContext &Context) {
Context.expect(false, "extended RV64GC pipeline construction must succeed");
return;
}
- rcc::MachineAnalysisManager Analyses;
+ rcc::MachineFunctionAnalysisManager Analyses;
auto Run = Pipeline.run(*Fixture.Function, *Target, Analyses);
Context.expect(Run.has_value(), "extended RV64GC pipeline must complete");
Context.expect(Run && Fixture.Function->properties().has(rcc::MachineFunctionProperty::Selected),
diff --git a/backend/unittests/CodeGen/GlobalISel/LegalizerTest.cpp b/backend/unittests/CodeGen/GlobalISel/LegalizerTest.cpp
index 9cb8a67..b191a3f 100644
--- a/backend/unittests/CodeGen/GlobalISel/LegalizerTest.cpp
+++ b/backend/unittests/CodeGen/GlobalISel/LegalizerTest.cpp
@@ -73,7 +73,7 @@ void testNullAndMissingContracts(TestContext &Context) {
return;
rcc::Legalizer NullPass(nullptr);
- rcc::MachineAnalysisManager Analyses;
+ rcc::MachineFunctionAnalysisManager Analyses;
auto NullResult = NullPass.run(*Fixture.Function, *Target, Analyses);
Context.expect(!NullResult && NullResult.error().code() == rcc::ErrorCode::InvalidArgument,
"a null legalizer interface must be rejected");
@@ -117,12 +117,12 @@ void testUnsupportedRollback(TestContext &Context) {
Unsupported.Function->properties() = {rcc::MachineFunctionProperty::IsSSA,
rcc::MachineFunctionProperty::TracksLiveness};
const rcc::riscv::RISCVSubtarget Subtarget(rcc::riscv::RISCVSubtargetFeatures::rv64gc());
- rcc::MachinePassManager OnlyLegalizer;
+ rcc::MachineFunctionPassManager OnlyLegalizer;
Context.expect(
OnlyLegalizer.addPass(std::make_unique<rcc::Legalizer>(Subtarget.getLegalizerInfo()))
.has_value(),
"standalone legalizer must be accepted");
- rcc::MachineAnalysisManager UnsupportedAnalyses;
+ rcc::MachineFunctionAnalysisManager UnsupportedAnalyses;
auto Failure = OnlyLegalizer.run(*Unsupported.Function, *Target, UnsupportedAnalyses);
Context.expect(!Failure && Failure.error().code() == rcc::ErrorCode::Unsupported,
"unsupported generic instructions must produce a target diagnostic");
@@ -152,7 +152,7 @@ void testLegalizerCycleDiagnostic(TestContext &Context) {
rcc::MachineFunctionProperty::TracksLiveness};
TogglingLegalizer Legalizer;
rcc::Legalizer StablePass(Legalizer);
- rcc::MachineAnalysisManager Analyses;
+ rcc::MachineFunctionAnalysisManager Analyses;
auto ResultRun = StablePass.run(*Fixture.Function, *Target, Analyses);
Context.expect(!ResultRun && ResultRun.error().code() == rcc::ErrorCode::InternalError,
"repeated legalization states must report InternalError");
@@ -188,7 +188,7 @@ void testLegalizerRevisitsInvalidatedInstructions(TestContext &Context) {
rcc::MachineFunctionProperty::TracksLiveness};
BackwardInvalidatingLegalizer Legalizer;
rcc::Legalizer Pass(Legalizer);
- rcc::MachineAnalysisManager Analyses;
+ rcc::MachineFunctionAnalysisManager Analyses;
auto Run = Pass.run(*Fixture.Function, *Target, Analyses);
const rcc::Opcode LegalOpcode =
rcc::Opcode::fromValue(static_cast<std::uint32_t>(rcc::riscv::InstructionOpcode::ADD));
diff --git a/backend/unittests/CodeGen/GlobalISel/RegBankSelectTest.cpp b/backend/unittests/CodeGen/GlobalISel/RegBankSelectTest.cpp
index d4925eb..833435f 100644
--- a/backend/unittests/CodeGen/GlobalISel/RegBankSelectTest.cpp
+++ b/backend/unittests/CodeGen/GlobalISel/RegBankSelectTest.cpp
@@ -33,7 +33,7 @@ void testBankConflict(TestContext &Context) {
Fixture.Function->properties() = {rcc::MachineFunctionProperty::Legalized};
const rcc::riscv::RISCVSubtarget Subtarget(rcc::riscv::RISCVSubtargetFeatures::rv64gc());
rcc::RegBankSelect Pass(Subtarget.getRegBankInfo());
- rcc::MachineAnalysisManager Analyses;
+ rcc::MachineFunctionAnalysisManager Analyses;
auto Result = Pass.run(*Fixture.Function, *Target, Analyses);
Context.expect(!Result && Result.error().code() == rcc::ErrorCode::VerificationError,
"bank selection must reject a conflicting pre-existing bank");
@@ -66,7 +66,7 @@ void testBankClassOnlyChange(TestContext &Context) {
Fixture.Function->properties() = {rcc::MachineFunctionProperty::Legalized};
const rcc::riscv::RISCVSubtarget Subtarget(rcc::riscv::RISCVSubtargetFeatures::rv64gc());
rcc::RegBankSelect Pass(Subtarget.getRegBankInfo());
- rcc::MachineAnalysisManager Analyses;
+ rcc::MachineFunctionAnalysisManager Analyses;
auto Result = Pass.run(*Fixture.Function, *Target, Analyses);
Context.expect(Result.has_value() && Result->Changed &&
Result->Changes.has(rcc::MachinePassChange::Registers),
diff --git a/backend/unittests/CodeGen/LiveIntervalTest.cpp b/backend/unittests/CodeGen/LiveIntervalTest.cpp
index 7e48b05..34ebd76 100644
--- a/backend/unittests/CodeGen/LiveIntervalTest.cpp
+++ b/backend/unittests/CodeGen/LiveIntervalTest.cpp
@@ -74,7 +74,7 @@ void testLaneSubrangesAndEarlyClobber(TestContext &Context, const rcc::TargetDes
return;
}
- rcc::MachineAnalysisManager Analyses;
+ rcc::MachineFunctionAnalysisManager Analyses;
auto Registered = rcc::registerStage11MachineAnalyses(Analyses);
auto Intervals = Registered
? Analyses.getResult<rcc::LiveIntervalsAnalysis>(*Fixture->Function, Target)
diff --git a/backend/unittests/CodeGen/LiveIntervalsTest.cpp b/backend/unittests/CodeGen/LiveIntervalsTest.cpp
index 5c127a2..a080e4d 100644
--- a/backend/unittests/CodeGen/LiveIntervalsTest.cpp
+++ b/backend/unittests/CodeGen/LiveIntervalsTest.cpp
@@ -64,7 +64,7 @@ void testCFGAndValueNumbers(TestContext &Context, const rcc::TargetDescription &
return;
}
- rcc::MachineAnalysisManager Analyses;
+ rcc::MachineFunctionAnalysisManager Analyses;
auto Registered = rcc::registerStage11MachineAnalyses(Analyses);
auto Variables = Registered ? Analyses.getResult<rcc::LiveVariablesAnalysis>(Function, Target)
: rcc::Expected<rcc::LiveVariablesAnalysis::Result *>{
@@ -133,7 +133,7 @@ void testPhysicalUnitsAndRegMasks(TestContext &Context, const rcc::TargetDescrip
Context.expect(false, "physical-alias instructions must be built");
return;
}
- rcc::MachineAnalysisManager AliasAnalyses;
+ rcc::MachineFunctionAnalysisManager AliasAnalyses;
auto AliasRegistered = rcc::registerStage11MachineAnalyses(AliasAnalyses);
auto AliasIntervals =
AliasRegistered
@@ -167,7 +167,7 @@ void testPhysicalUnitsAndRegMasks(TestContext &Context, const rcc::TargetDescrip
Context.expect(false, "RegMask instructions must be built");
return;
}
- rcc::MachineAnalysisManager MaskAnalyses;
+ rcc::MachineFunctionAnalysisManager MaskAnalyses;
auto MaskRegistered = rcc::registerStage11MachineAnalyses(MaskAnalyses);
auto MaskIntervals =
MaskRegistered
@@ -237,7 +237,7 @@ void testRISCVLiveness(TestContext &Context) {
Context.expect(false, "RV64GC ADDI instructions must be built");
return;
}
- rcc::MachineAnalysisManager Analyses;
+ rcc::MachineFunctionAnalysisManager Analyses;
auto Registered = rcc::registerStage11MachineAnalyses(Analyses);
auto Intervals = Registered
? Analyses.getResult<rcc::LiveIntervalsAnalysis>(*Fixture->Function, *Target)
diff --git a/backend/unittests/CodeGen/LivePhysRegsTest.cpp b/backend/unittests/CodeGen/LivePhysRegsTest.cpp
index ee579be..6eea796 100644
--- a/backend/unittests/CodeGen/LivePhysRegsTest.cpp
+++ b/backend/unittests/CodeGen/LivePhysRegsTest.cpp
@@ -24,7 +24,7 @@ void testPostRALivenessRepair(TestContext &Context, const rcc::TargetDescription
return;
}
- rcc::MachineAnalysisManager Analyses;
+ rcc::MachineFunctionAnalysisManager Analyses;
auto Registered = rcc::registerStage11MachineAnalyses(Analyses);
auto Liveness = Registered
? Analyses.getResult<rcc::LiveVariablesAnalysis>(*Fixture->Function, Target)
diff --git a/backend/unittests/CodeGen/LiveRegMatrixTest.cpp b/backend/unittests/CodeGen/LiveRegMatrixTest.cpp
index 21c0778..423da50 100644
--- a/backend/unittests/CodeGen/LiveRegMatrixTest.cpp
+++ b/backend/unittests/CodeGen/LiveRegMatrixTest.cpp
@@ -26,7 +26,7 @@ void testLaneAndAssignment(TestContext &Context, const TargetDescription &Target
!buildUse(*Fixture->Function, Target, *Fixture->Block, *Low, TestSubReg::Low32))
return;
- MachineAnalysisManager Analyses;
+ MachineFunctionAnalysisManager Analyses;
if (!registerStage11MachineAnalyses(Analyses))
return;
auto Intervals = Analyses.getResult<LiveIntervalsAnalysis>(*Fixture->Function, Target);
@@ -55,7 +55,7 @@ void testFixedAndMaskInterference(TestContext &Context, const TargetDescription
!buildUse(*Fixture->Function, Target, *Fixture->Block, *Value))
return;
- MachineAnalysisManager Analyses;
+ MachineFunctionAnalysisManager Analyses;
if (!registerStage11MachineAnalyses(Analyses))
return;
auto Intervals = Analyses.getResult<LiveIntervalsAnalysis>(*Fixture->Function, Target);
diff --git a/backend/unittests/CodeGen/LiveVariablesTest.cpp b/backend/unittests/CodeGen/LiveVariablesTest.cpp
index 577ecde..1cc4e91 100644
--- a/backend/unittests/CodeGen/LiveVariablesTest.cpp
+++ b/backend/unittests/CodeGen/LiveVariablesTest.cpp
@@ -36,9 +36,9 @@ void testLivenessRebuildAndVerifier(TestContext &Context, const rcc::TargetDescr
Context.expect(StaleLiveIn.has_value(), "stale live-in setup must succeed");
setRebuildProperties(Function);
- rcc::MachinePassManager Pipeline;
+ rcc::MachineFunctionPassManager Pipeline;
auto Added = Pipeline.addPass(std::make_unique<rcc::RebuildMachineLivenessPass>());
- rcc::MachineAnalysisManager Analyses;
+ rcc::MachineFunctionAnalysisManager Analyses;
auto Run = Added ? Pipeline.run(Function, Target, Analyses)
: rcc::Expected<void>{std::unexpected(Added.error())};
if (!Run)
@@ -169,8 +169,9 @@ class DummyAnalysis final {
public:
struct Result final {};
- [[nodiscard]] static rcc::Expected<Result>
- run(rcc::MachineFunction &, const rcc::TargetDescription &, rcc::MachineAnalysisManager &) {
+ [[nodiscard]] static rcc::Expected<Result> run(rcc::MachineFunction &,
+ const rcc::TargetDescription &,
+ rcc::MachineFunctionAnalysisManager &) {
return Result{};
}
};
@@ -192,7 +193,7 @@ void testAnalysisRegistrationAndInvalidation(TestContext &Context,
return;
}
- rcc::MachineAnalysisManager Analyses;
+ rcc::MachineFunctionAnalysisManager Analyses;
auto FirstRegistration = rcc::registerStage11MachineAnalyses(Analyses);
auto SecondRegistration = rcc::registerStage11MachineAnalyses(Analyses);
auto Intervals = FirstRegistration
@@ -211,7 +212,7 @@ void testAnalysisRegistrationAndInvalidation(TestContext &Context,
Context.expect(
Analyses.cachedResultCount(*Fixture->Function) == 1 &&
Analyses.hasCached(*Fixture->Function,
- rcc::MachineAnalysisManager::id<rcc::SlotIndexesAnalysis>()),
+ rcc::MachineFunctionAnalysisManager::id<rcc::SlotIndexesAnalysis>()),
"operand changes must retain SlotIndexes while invalidating liveness dependents");
auto Recomputed = Analyses.getResult<rcc::LiveIntervalsAnalysis>(*Fixture->Function, Target);
Context.expect(Recomputed && Analyses.cachedResultCount(*Fixture->Function) == 3,
@@ -221,18 +222,21 @@ void testAnalysisRegistrationAndInvalidation(TestContext &Context,
Context.expect(
Analyses.cachedResultCount(*Fixture->Function) == 1 &&
Analyses.hasCached(*Fixture->Function,
- rcc::MachineAnalysisManager::id<rcc::LiveVariablesAnalysis>()),
+ rcc::MachineFunctionAnalysisManager::id<rcc::LiveVariablesAnalysis>()),
"layout changes must invalidate SlotIndexes and LiveIntervals only");
- rcc::MachineAnalysisManager Atomic;
+ rcc::MachineFunctionAnalysisManager Atomic;
rcc::MachineAnalysisRegistration DummyRegistration;
DummyRegistration.Name = "SlotIndexes";
auto Dummy = Atomic.registerAnalysis<DummyAnalysis>(std::move(DummyRegistration));
auto Rejected = rcc::registerStage11MachineAnalyses(Atomic);
Context.expect(
- Dummy && !Rejected && Atomic.isRegistered(rcc::MachineAnalysisManager::id<DummyAnalysis>()) &&
- !Atomic.isRegistered(rcc::MachineAnalysisManager::id<rcc::SlotIndexesAnalysis>()) &&
- !Atomic.isRegistered(rcc::MachineAnalysisManager::id<rcc::LiveVariablesAnalysis>()),
+ Dummy && !Rejected &&
+ Atomic.isRegistered(rcc::MachineFunctionAnalysisManager::id<DummyAnalysis>()) &&
+ !Atomic.isRegistered(
+ rcc::MachineFunctionAnalysisManager::id<rcc::SlotIndexesAnalysis>()) &&
+ !Atomic.isRegistered(
+ rcc::MachineFunctionAnalysisManager::id<rcc::LiveVariablesAnalysis>()),
"failed standard-analysis registration must leave the manager unchanged");
}
diff --git a/backend/unittests/CodeGen/MachineAnalysesTest.cpp b/backend/unittests/CodeGen/MachineAnalysesTest.cpp
index 1c9f255..997d031 100644
--- a/backend/unittests/CodeGen/MachineAnalysesTest.cpp
+++ b/backend/unittests/CodeGen/MachineAnalysesTest.cpp
@@ -86,7 +86,7 @@ void testCFGAnalyses(TestContext &Context, const rcc::TargetDescription &Target)
Updater.addSuccessor(**Body, **Header, rcc::BranchProbability::one()),
"reducible loop CFG construction must succeed");
- rcc::MachineAnalysisManager Analyses;
+ rcc::MachineFunctionAnalysisManager Analyses;
Context.expect(rcc::registerStage8MachineAnalyses(Analyses).has_value(),
"standard Stage 8 analyses must register atomically");
auto Dominators = Analyses.getResult<rcc::MachineDominatorTreeAnalysis>(**Function, Target);
@@ -129,7 +129,7 @@ void testCFGAnalyses(TestContext &Context, const rcc::TargetDescription &Target)
Context.expect(
Analyses.cachedResultCount(**Function) == 1 &&
Analyses.hasCached(**Function,
- rcc::MachineAnalysisManager::id<rcc::MachineAliasAnalysis>()),
+ rcc::MachineFunctionAnalysisManager::id<rcc::MachineAliasAnalysis>()),
"CFG changes must invalidate CFG dependents and memory dependence but preserve aliasing");
auto RefreshedDominators =
@@ -167,7 +167,7 @@ void testIrreducibleCFG(TestContext &Context, const rcc::TargetDescription &Targ
Updater.addSuccessor(**Right, **Exit, *Half),
"irreducible CFG construction must succeed");
- rcc::MachineAnalysisManager Analyses;
+ rcc::MachineFunctionAnalysisManager Analyses;
auto Registered = rcc::registerStage8MachineAnalyses(Analyses);
auto Loops =
Registered
@@ -317,7 +317,7 @@ void testMemoryDependence(TestContext &Context, const rcc::TargetDescription &Ta
!VolatileStoreInstruction || !VolatileLoadInstruction || !OtherBlockLoadInstruction)
return;
- rcc::MachineAnalysisManager Analyses;
+ rcc::MachineFunctionAnalysisManager Analyses;
auto Registered = rcc::registerStage8MachineAnalyses(Analyses);
auto Dependencies =
Registered ? Analyses.getResult<rcc::MachineMemoryDependenceAnalysis>(**Function, Target)
@@ -352,14 +352,15 @@ void testMemoryDependence(TestContext &Context, const rcc::TargetDescription &Ta
class ConflictingAnalysis final {
public:
struct Result final {};
- [[nodiscard]] static rcc::Expected<Result>
- run(rcc::MachineFunction &, const rcc::TargetDescription &, rcc::MachineAnalysisManager &) {
+ [[nodiscard]] static rcc::Expected<Result> run(rcc::MachineFunction &,
+ const rcc::TargetDescription &,
+ rcc::MachineFunctionAnalysisManager &) {
return Result{};
}
};
void testAtomicStandardRegistration(TestContext &Context) {
- rcc::MachineAnalysisManager Analyses;
+ rcc::MachineFunctionAnalysisManager Analyses;
Context.expect(Analyses
.registerAnalysis<ConflictingAnalysis>({
.Name = "MachineLoopInfo",
@@ -367,10 +368,11 @@ void testAtomicStandardRegistration(TestContext &Context) {
})
.has_value(),
"analysis-name conflict setup must succeed");
- Context.expect(!rcc::registerStage8MachineAnalyses(Analyses) &&
- !Analyses.isRegistered(
- rcc::MachineAnalysisManager::id<rcc::MachineDominatorTreeAnalysis>()),
- "failed standard-analysis registration must not publish a partial set");
+ Context.expect(
+ !rcc::registerStage8MachineAnalyses(Analyses) &&
+ !Analyses.isRegistered(
+ rcc::MachineFunctionAnalysisManager::id<rcc::MachineDominatorTreeAnalysis>()),
+ "failed standard-analysis registration must not publish a partial set");
}
void testCFGUpdaterPHIs(TestContext &Context, const rcc::TargetDescription &Target) {
diff --git a/backend/unittests/CodeGen/MachineOptimizationTest.cpp b/backend/unittests/CodeGen/MachineOptimizationTest.cpp
index d246e39..bb2c0e1 100644
--- a/backend/unittests/CodeGen/MachineOptimizationTest.cpp
+++ b/backend/unittests/CodeGen/MachineOptimizationTest.cpp
@@ -321,7 +321,7 @@ void testDCEFixedPointAndDebugUses(TestContext &Context, const rcc::TargetDescri
}
const std::size_t Before = Fixture.Entry->instructionCount();
- rcc::MachineAnalysisManager Analyses;
+ rcc::MachineFunctionAnalysisManager Analyses;
rcc::MachineDCEPass Pass;
auto Result = Pass.run(*Fixture.Function, Target, Analyses);
if (!Result)
@@ -369,7 +369,7 @@ void testMachineCombinerIdentityRules(TestContext &Context, const rcc::TargetDes
return;
(void)buildStore(Context, Builder, Consumer, X2, *Memory);
- rcc::MachineAnalysisManager Analyses;
+ rcc::MachineFunctionAnalysisManager Analyses;
rcc::MachineCombinerPass Pass(rcc::riscv::machineCombinerInfo(),
rcc::MachineCombinerStage::SelectedSSA);
auto Result = Pass.run(*Fixture.Function, Target, Analyses);
@@ -404,7 +404,7 @@ void testPostRAMachineCombinerIdentity(TestContext &Context, const rcc::TargetDe
rcc::Register::fromPhysical(X10), 0);
if (FrameIdentity != nullptr)
FrameIdentity->setFlags({rcc::MachineInstrFlag::FrameDestroy});
- rcc::MachineAnalysisManager Analyses;
+ rcc::MachineFunctionAnalysisManager Analyses;
rcc::MachineCombinerPass Pass(rcc::riscv::machineCombinerInfo(),
rcc::MachineCombinerStage::PostRegisterAllocation);
auto Result = Pass.run(*Fixture.Function, Target, Analyses);
@@ -450,7 +450,7 @@ void testCSERegisterAndMemoryRules(TestContext &Context, const rcc::TargetDescri
rcc::MachineInstr *InvariantTwo =
buildLoad(Context, Builder, InvariantLoadTwo, Seed, *InvariantMemory);
- rcc::MachineAnalysisManager Analyses;
+ rcc::MachineFunctionAnalysisManager Analyses;
rcc::MachineCSEPass Pass;
auto Result = Pass.run(*Fixture.Function, Target, Analyses);
if (!Result)
@@ -490,7 +490,7 @@ void testCSEBudgetPreservesLegalMIR(TestContext &Context, const rcc::TargetDescr
(void)buildADDI(Context, Builder, First, Seed, 2);
rcc::MachineInstr *SecondInstruction = buildADDI(Context, Builder, Second, Seed, 2);
- rcc::MachineAnalysisManager Analyses;
+ rcc::MachineFunctionAnalysisManager Analyses;
rcc::MachineCSEPass Pass({.MaximumComparisons = 0});
auto Result = Pass.run(*Fixture.Function, Target, Analyses);
if (!Result)
@@ -545,7 +545,7 @@ void testLICMHoistsOnlyInvariantInstructions(TestContext &Context,
rcc::MachineInstr *Load =
Memory == nullptr ? nullptr : buildLoad(Context, Builder, MutableLoad, Seed, *Memory);
- rcc::MachineAnalysisManager Analyses;
+ rcc::MachineFunctionAnalysisManager Analyses;
rcc::MachineLICMPass Pass;
auto Result = Pass.run(*Fixture.Function, Target, Analyses);
if (!Result)
@@ -597,7 +597,7 @@ void testMachineSinkUsesColdUniqueSuccessor(TestContext &Context,
const rcc::Register Consumer = makeGPR(Context, *Fixture.Function);
rcc::MachineInstr *Use = buildADD(Context, Builder, Consumer, SunkValue, Seed);
- rcc::MachineAnalysisManager Analyses;
+ rcc::MachineFunctionAnalysisManager Analyses;
rcc::MachineSinkPass Pass;
auto Result = Pass.run(*Fixture.Function, Target, Analyses);
if (!Result)
@@ -664,7 +664,7 @@ void testPostRACodeMotion(TestContext &Context, const rcc::TargetDescription &Ta
setPostRAProperties(*LICMFixture.Function);
Context.expect(rcc::rebuildMachineLiveness(*LICMFixture.Function, Target).has_value(),
"post-RA LICM fixture liveness must be built");
- rcc::MachineAnalysisManager LICMAnalyses;
+ rcc::MachineFunctionAnalysisManager LICMAnalyses;
rcc::MachineLICMPass PostRALICM(rcc::MachineCodeMotionStage::PostRegisterAllocation);
auto LICMResult = PostRALICM.run(*LICMFixture.Function, Target, LICMAnalyses);
Context.expect(LICMResult && LICMResult->Changed && Hoisted->parent() == LICMFixture.Entry &&
@@ -703,7 +703,7 @@ void testPostRACodeMotion(TestContext &Context, const rcc::TargetDescription &Ta
setPostRAProperties(*SinkFixture.Function);
Context.expect(rcc::rebuildMachineLiveness(*SinkFixture.Function, Target).has_value(),
"post-RA sink fixture liveness must be built");
- rcc::MachineAnalysisManager SinkAnalyses;
+ rcc::MachineFunctionAnalysisManager SinkAnalyses;
rcc::MachineSinkPass PostRASink(rcc::MachineCodeMotionStage::PostRegisterAllocation);
auto SinkResult = PostRASink.run(*SinkFixture.Function, Target, SinkAnalyses);
Context.expect(SinkResult && SinkResult->Changed && Sunk->parent() == *Cold &&
@@ -742,7 +742,7 @@ void testMemoryFoldingContract(TestContext &Context, const rcc::TargetDescriptio
if (!Folded.Function || Folded.Entry == nullptr)
return;
TestMemoryFoldingInfo GoodInfo;
- rcc::MachineAnalysisManager Analyses;
+ rcc::MachineFunctionAnalysisManager Analyses;
rcc::MachineMemoryFoldingPass Pass(GoodInfo);
auto Result = Pass.run(*Folded.Function, Target, Analyses);
if (!Result)
diff --git a/backend/unittests/CodeGen/MachinePassManagerTest.cpp b/backend/unittests/CodeGen/MachinePassManagerTest.cpp
new file mode 100644
index 0000000..6e6c44d
--- /dev/null
+++ b/backend/unittests/CodeGen/MachinePassManagerTest.cpp
@@ -0,0 +1,426 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "rcc/CodeGen/MachinePassManager.h"
+
+#include "Target/TestTarget.h"
+#include "rcc/CodeGen/CompilationUnit.h"
+#include "rcc/CodeGen/GenericOpcodes.h"
+#include "rcc/CodeGen/MachineFunction.h"
+
+#include <atomic>
+#include <cstddef>
+#include <cstdint>
+#include <iostream>
+#include <memory>
+#include <string>
+#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';
+ ++Failures;
+ }
+
+ [[nodiscard]] int result() const noexcept {
+ return Failures == 0 ? 0 : 1;
+ }
+
+private:
+ int Failures = 0;
+};
+
+[[nodiscard]] rcc::Expected<rcc::TargetDescription> createTarget() {
+ std::vector<rcc::InstructionDescriptor> Descriptors;
+ std::vector<rcc::ImmediatePredicate> Predicates;
+ auto Appended = rcc::appendGenericInstructionInfo(Descriptors, Predicates);
+ if (!Appended)
+ return std::unexpected(Appended.error());
+ auto Instructions = rcc::InstructionSet::create(std::move(Descriptors), std::move(Predicates));
+ if (!Instructions)
+ return std::unexpected(Instructions.error());
+ return rcc::test::createTestTargetDescription(std::move(*Instructions));
+}
+
+class BlockCountAnalysis final {
+public:
+ struct Result final {
+ std::size_t Count = 0;
+ };
+
+ [[nodiscard]] static constexpr std::string_view name() noexcept {
+ return "BlockCount";
+ }
+
+ [[nodiscard]] static rcc::Expected<Result> run(rcc::MachineFunction &Function,
+ const rcc::TargetDescription &,
+ rcc::MachineFunctionAnalysisManager &) {
+ return Result{Function.blockCount()};
+ }
+};
+
+class NonThreadSafeAnalysis final {
+public:
+ struct Result final {};
+
+ [[nodiscard]] static constexpr std::string_view name() noexcept {
+ return "NonThreadSafe";
+ }
+
+ [[nodiscard]] static rcc::Expected<Result> run(rcc::MachineFunction &,
+ const rcc::TargetDescription &,
+ rcc::MachineFunctionAnalysisManager &) {
+ return Result{};
+ }
+};
+
+class RequiredAnalysisPass final : public rcc::MachineFunctionPass {
+public:
+ [[nodiscard]] const rcc::MachinePassContract &contract() const noexcept override {
+ static const rcc::MachinePassContract Contract{
+ .Name = "RequiredAnalysis",
+ .Requires = {},
+ .Sets = {},
+ .Clears = {},
+ .RequiredAnalyses = {rcc::MachineFunctionAnalysisManager::id<BlockCountAnalysis>()},
+ .VerificationStage = std::nullopt,
+ };
+ return Contract;
+ }
+
+ [[nodiscard]] rcc::Expected<rcc::MachinePassResult>
+ run(rcc::MachineFunction &Function, const rcc::TargetDescription &Target,
+ rcc::MachineFunctionAnalysisManager &Analyses) override {
+ auto Blocks = Analyses.getResult<BlockCountAnalysis>(Function, Target);
+ if (!Blocks)
+ return std::unexpected(Blocks.error());
+ rcc::MachinePassResult Result;
+ Result.Preserved = rcc::PreservedAnalyses::all();
+ return Result;
+ }
+};
+
+class UnreportedChangePass final : public rcc::MachineFunctionPass {
+public:
+ [[nodiscard]] const rcc::MachinePassContract &contract() const noexcept override {
+ static const rcc::MachinePassContract Contract{
+ .Name = "UnreportedChange",
+ .Requires = {},
+ .Sets = {},
+ .Clears = {},
+ .RequiredAnalyses = {},
+ .VerificationStage = std::nullopt,
+ };
+ return Contract;
+ }
+
+ [[nodiscard]] rcc::Expected<rcc::MachinePassResult>
+ run(rcc::MachineFunction &Function, const rcc::TargetDescription &,
+ rcc::MachineFunctionAnalysisManager &) override {
+ auto Block = Function.createBasicBlock();
+ if (!Block)
+ return std::unexpected(Block.error());
+ auto Appended = Function.appendBlock(**Block);
+ if (!Appended)
+ return std::unexpected(Appended.error());
+ return rcc::MachinePassResult{};
+ }
+};
+
+class MutatingFailurePass final : public rcc::MachineFunctionPass {
+public:
+ [[nodiscard]] const rcc::MachinePassContract &contract() const noexcept override {
+ static const rcc::MachinePassContract Contract{
+ .Name = "MutatingFailure",
+ .Requires = {},
+ .Sets = {},
+ .Clears = {},
+ .RequiredAnalyses = {rcc::MachineFunctionAnalysisManager::id<BlockCountAnalysis>()},
+ .VerificationStage = std::nullopt,
+ };
+ return Contract;
+ }
+
+ [[nodiscard]] rcc::Expected<rcc::MachinePassResult>
+ run(rcc::MachineFunction &Function, const rcc::TargetDescription &,
+ rcc::MachineFunctionAnalysisManager &) override {
+ auto Block = Function.createBasicBlock();
+ if (!Block)
+ return std::unexpected(Block.error());
+ auto Appended = Function.appendBlock(**Block);
+ if (!Appended)
+ return std::unexpected(Appended.error());
+ return rcc::makeUnexpected(rcc::ErrorCode::VerificationError,
+ "intentional mutating pass failure");
+ }
+};
+
+class ParallelCountingPass final : public rcc::MachineFunctionPass {
+public:
+ explicit ParallelCountingPass(std::atomic<std::uint32_t> &Count) : Count(&Count) {}
+
+ [[nodiscard]] const rcc::MachinePassContract &contract() const noexcept override {
+ static const rcc::MachinePassContract Contract = [] {
+ rcc::MachinePassContract Value{
+ .Name = "ParallelCounting",
+ .Requires = {},
+ .Sets = {},
+ .Clears = {},
+ .RequiredAnalyses = {},
+ .VerificationStage = std::nullopt,
+ };
+ Value.ThreadSafety = rcc::PassThreadSafety::ThreadSafe;
+ return Value;
+ }();
+ return Contract;
+ }
+
+ [[nodiscard]] rcc::Expected<rcc::MachinePassResult>
+ run(rcc::MachineFunction &, const rcc::TargetDescription &,
+ rcc::MachineFunctionAnalysisManager &) override {
+ Count->fetch_add(1, std::memory_order_relaxed);
+ return rcc::MachinePassResult{};
+ }
+
+private:
+ std::atomic<std::uint32_t> *Count;
+};
+
+class RequiresNonThreadSafeAnalysisPass final : public rcc::MachineFunctionPass {
+public:
+ [[nodiscard]] const rcc::MachinePassContract &contract() const noexcept override {
+ static const rcc::MachinePassContract Contract = [] {
+ rcc::MachinePassContract Value{
+ .Name = "RequiresNonThreadSafe",
+ .Requires = {},
+ .Sets = {},
+ .Clears = {},
+ .RequiredAnalyses =
+ {
+ rcc::MachineFunctionAnalysisManager::id<NonThreadSafeAnalysis>(),
+ },
+ .VerificationStage = std::nullopt,
+ };
+ Value.ThreadSafety = rcc::PassThreadSafety::ThreadSafe;
+ return Value;
+ }();
+ return Contract;
+ }
+
+ [[nodiscard]] rcc::Expected<rcc::MachinePassResult>
+ run(rcc::MachineFunction &, const rcc::TargetDescription &,
+ rcc::MachineFunctionAnalysisManager &) override {
+ return rcc::MachinePassResult{};
+ }
+};
+
+class UnitFunctionCountAnalysis final {
+public:
+ struct Result final {
+ std::size_t Count = 0;
+ };
+ [[nodiscard]] static constexpr std::string_view name() noexcept {
+ return "UnitFunctionCount";
+ }
+ [[nodiscard]] static rcc::Expected<Result> run(rcc::CompilationUnit &Unit,
+ const rcc::TargetDescription &,
+ rcc::CompilationUnitAnalysisManager &) {
+ return Result{Unit.functionCount()};
+ }
+};
+
+class ObserveUnitPass final : public rcc::CompilationUnitPass {
+public:
+ [[nodiscard]] const rcc::CompilationUnitPassContract &contract() const noexcept override {
+ static const rcc::CompilationUnitPassContract Contract{
+ .Name = "ObserveUnit",
+ .RequiredAnalyses = {rcc::CompilationUnitAnalysisManager::id<UnitFunctionCountAnalysis>()},
+ .Parameters = {},
+ .DeclaredChanges = {},
+ };
+ return Contract;
+ }
+
+ [[nodiscard]] rcc::Expected<rcc::CompilationUnitPassResult>
+ run(rcc::CompilationUnit &Unit, const rcc::TargetDescription &Target,
+ rcc::CompilationUnitAnalysisManager &Analyses,
+ rcc::MachineFunctionAnalysisManager &) override {
+ auto Count = Analyses.getResult<UnitFunctionCountAnalysis>(Unit, Target);
+ if (!Count)
+ return std::unexpected(Count.error());
+ if ((*Count)->Count != Unit.functionCount()) {
+ return rcc::makeUnexpected(rcc::ErrorCode::VerificationError,
+ "unit analysis returned a stale function count");
+ }
+ return rcc::CompilationUnitPassResult{};
+ }
+};
+
+class MutatingUnitFailurePass final : public rcc::CompilationUnitPass {
+public:
+ [[nodiscard]] const rcc::CompilationUnitPassContract &contract() const noexcept override {
+ static const rcc::CompilationUnitPassContract Contract{
+ .Name = "MutatingUnitFailure",
+ .RequiredAnalyses = {rcc::CompilationUnitAnalysisManager::id<UnitFunctionCountAnalysis>()},
+ .Parameters = {},
+ .DeclaredChanges = {rcc::MachinePassChange::BlockLayout},
+ };
+ return Contract;
+ }
+
+ [[nodiscard]] rcc::Expected<rcc::CompilationUnitPassResult>
+ run(rcc::CompilationUnit &Unit, const rcc::TargetDescription &,
+ rcc::CompilationUnitAnalysisManager &, rcc::MachineFunctionAnalysisManager &) override {
+ auto Created = Unit.createMachineFunction("added-before-failure");
+ if (!Created)
+ return std::unexpected(Created.error());
+ return rcc::makeUnexpected(rcc::ErrorCode::VerificationError,
+ "intentional compilation-unit pass failure");
+ }
+};
+
+void testMachinePassManager(TestContext &Context, const rcc::TargetDescription &Target) {
+ auto Function = rcc::MachineFunction::create("pass", rcc::MachineFunctionStableID::fromValue(2));
+ Context.expect(Function.has_value(), "pass function creation must succeed");
+ if (!Function)
+ return;
+
+ rcc::MachineFunctionAnalysisManager Analyses;
+ Context.expect(Analyses
+ .registerAnalysis<BlockCountAnalysis>({
+ .Name = "BlockCount",
+ .Dependencies = {},
+ .InvalidatedBy = rcc::MachinePassChanges::all(),
+ .ThreadSafety = rcc::PassThreadSafety::ThreadSafe,
+ })
+ .has_value(),
+ "pass analysis registration must succeed");
+
+ rcc::MachineFunctionPassManager Pipeline;
+ Context.expect(Pipeline.addPass(std::make_unique<RequiredAnalysisPass>()).has_value(),
+ "a valid pass must be accepted");
+ auto Run = Pipeline.run(**Function, Target, Analyses);
+ Context.expect(Run.has_value(), "a pass with a materialized required analysis must run");
+
+ auto ChangedFunction =
+ rcc::MachineFunction::create("unreported", rcc::MachineFunctionStableID::fromValue(3));
+ rcc::MachineFunctionAnalysisManager EmptyAnalyses;
+ rcc::MachineFunctionPassManager Detecting(
+ {.VerifyBefore = true, .VerifyEach = true, .DetectUnreportedChanges = true});
+ Context.expect(ChangedFunction &&
+ Detecting.addPass(std::make_unique<UnreportedChangePass>()).has_value() &&
+ !Detecting.run(**ChangedFunction, Target, EmptyAnalyses),
+ "change detection must reject an unreported MachineIR mutation");
+
+ auto FailedFunction =
+ rcc::MachineFunction::create("failed", rcc::MachineFunctionStableID::fromValue(4));
+ rcc::MachineFunctionAnalysisManager FailureAnalyses;
+ Context.expect(FailedFunction && FailureAnalyses
+ .registerAnalysis<BlockCountAnalysis>({
+ .Name = "BlockCount",
+ .Dependencies = {},
+ .InvalidatedBy = rcc::MachinePassChanges::all(),
+ })
+ .has_value(),
+ "mutating failure analysis fixtures must be valid");
+ rcc::MachineFunctionPassManager FailingPipeline;
+ Context.expect(
+ FailedFunction &&
+ FailingPipeline.addPass(std::make_unique<MutatingFailurePass>()).has_value() &&
+ !FailingPipeline.run(**FailedFunction, Target, FailureAnalyses) &&
+ !FailureAnalyses.hasCached(**FailedFunction,
+ rcc::MachineFunctionAnalysisManager::id<BlockCountAnalysis>()),
+ "a pass that mutates and fails must not leave stale function analyses cached");
+}
+
+void testCompilationUnitAndParallelism(TestContext &Context, const rcc::TargetDescription &Target) {
+ rcc::CompilationUnit Unit;
+ for (std::size_t Index = 0; Index < 8; ++Index) {
+ auto Function = Unit.createMachineFunction("f" + std::to_string(Index));
+ Context.expect(Function.has_value(), "parallel test function creation must succeed");
+ }
+
+ std::atomic<std::uint32_t> Runs = 0;
+ rcc::MachineFunctionPassManager Parallel;
+ Context.expect(Parallel.addPass(std::make_unique<ParallelCountingPass>(Runs)).has_value(),
+ "thread-safe pass registration must succeed");
+ rcc::MachineFunctionAnalysisManager Prototype;
+ auto ParallelRun = Parallel.runFunctions(Unit, Target, Prototype, std::size_t{4});
+ Context.expect(ParallelRun.has_value() && Runs == Unit.functionCount(),
+ "parallel function execution must visit every function exactly once");
+
+ rcc::MachineFunctionAnalysisManager UnsafePrototype;
+ Context.expect(UnsafePrototype
+ .registerAnalysis<NonThreadSafeAnalysis>({
+ .Name = "NonThreadSafe",
+ .Dependencies = {},
+ .InvalidatedBy = rcc::MachinePassChanges::all(),
+ .ThreadSafety = rcc::PassThreadSafety::NotThreadSafe,
+ })
+ .has_value(),
+ "non-thread-safe analysis registration must be explicit");
+ rcc::MachineFunctionPassManager UnsafeParallel;
+ Context.expect(
+ UnsafeParallel.addPass(std::make_unique<RequiresNonThreadSafeAnalysisPass>()).has_value() &&
+ !UnsafeParallel.runFunctions(Unit, Target, UnsafePrototype, std::size_t{4}),
+ "parallel pipelines must reject a required analysis not marked thread-safe");
+
+ rcc::CompilationUnitAnalysisManager UnitAnalyses;
+ Context.expect(UnitAnalyses
+ .registerAnalysis<UnitFunctionCountAnalysis>({
+ .Name = "UnitFunctionCount",
+ .Dependencies = {},
+ .InvalidatedBy = rcc::MachinePassChanges::all(),
+ })
+ .has_value(),
+ "compilation-unit analysis registration must succeed");
+ rcc::MachineFunctionAnalysisManager FunctionAnalyses;
+ rcc::CompilationUnitPassManager UnitPipeline;
+ Context.expect(UnitPipeline.addPass(std::make_unique<ObserveUnitPass>()).has_value(),
+ "compilation-unit pass registration must succeed");
+ Context.expect(UnitPipeline.run(Unit, Target, UnitAnalyses, FunctionAnalyses).has_value(),
+ "compilation-unit pass and analysis must run through their own manager");
+
+ rcc::MachineFunction *FirstFunction = Unit.functionAt(0);
+ Context.expect(
+ FirstFunction != nullptr &&
+ FunctionAnalyses
+ .registerAnalysis<BlockCountAnalysis>({
+ .Name = "BlockCount",
+ .Dependencies = {},
+ .InvalidatedBy = rcc::MachinePassChanges::all(),
+ })
+ .has_value() &&
+ FunctionAnalyses.getResult<BlockCountAnalysis>(*FirstFunction, Target).has_value(),
+ "compilation-unit failure cache fixtures must materialize");
+ rcc::CompilationUnitPassManager FailingUnitPipeline;
+ Context.expect(
+ FirstFunction != nullptr &&
+ FailingUnitPipeline.addPass(std::make_unique<MutatingUnitFailurePass>()).has_value() &&
+ !FailingUnitPipeline.run(Unit, Target, UnitAnalyses, FunctionAnalyses) &&
+ !UnitAnalyses.hasCached(
+ Unit, rcc::CompilationUnitAnalysisManager::id<UnitFunctionCountAnalysis>()) &&
+ !FunctionAnalyses.hasCached(
+ *FirstFunction, rcc::MachineFunctionAnalysisManager::id<BlockCountAnalysis>()),
+ "a mutating compilation-unit failure must clear unit and function analysis caches");
+}
+
+} // namespace
+
+int main() {
+ TestContext Context;
+ auto Target = createTarget();
+ Context.expect(Target.has_value(), "machine pass tests require a valid target description");
+ if (!Target)
+ return Context.result();
+
+ testMachinePassManager(Context, *Target);
+ testCompilationUnitAndParallelism(Context, *Target);
+ return Context.result();
+}
diff --git a/backend/unittests/CodeGen/MachinePassTest.cpp b/backend/unittests/CodeGen/MachinePassTest.cpp
deleted file mode 100644
index fb125a4..0000000
--- a/backend/unittests/CodeGen/MachinePassTest.cpp
+++ /dev/null
@@ -1,918 +0,0 @@
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-#include "rcc/CodeGen/MachinePass.h"
-
-#include "Target/TestTarget.h"
-#include "rcc/CodeGen/CompilationUnit.h"
-#include "rcc/CodeGen/GenericOpcodes.h"
-#include "rcc/CodeGen/MachineFunction.h"
-
-#include <algorithm>
-#include <atomic>
-#include <chrono>
-#include <cstddef>
-#include <cstdint>
-#include <iostream>
-#include <memory>
-#include <ranges>
-#include <string>
-#include <string_view>
-#include <thread>
-#include <utility>
-#include <vector>
-
-namespace {
-
-class TestContext final {
-public:
- void expect(bool Condition, std::string_view Message) {
- if (Condition)
- return;
- std::cerr << Message << '\n';
- ++Failures;
- }
-
- [[nodiscard]] int result() const noexcept {
- return Failures == 0 ? 0 : 1;
- }
-
-private:
- int Failures = 0;
-};
-
-[[nodiscard]] rcc::Expected<rcc::TargetDescription> createTarget() {
- std::vector<rcc::InstructionDescriptor> Descriptors;
- std::vector<rcc::ImmediatePredicate> Predicates;
- auto Appended = rcc::appendGenericInstructionInfo(Descriptors, Predicates);
- if (!Appended)
- return std::unexpected(Appended.error());
- auto Instructions = rcc::InstructionSet::create(std::move(Descriptors), std::move(Predicates));
- if (!Instructions)
- return std::unexpected(Instructions.error());
- return rcc::test::createTestTargetDescription(std::move(*Instructions));
-}
-
-class BlockCountAnalysis final {
-public:
- struct Result final {
- std::size_t Count = 0;
- };
-
- [[nodiscard]] static constexpr std::string_view name() noexcept {
- return "BlockCount";
- }
-
- [[nodiscard]] static rcc::Expected<Result> run(rcc::MachineFunction &Function,
- const rcc::TargetDescription &,
- rcc::MachineAnalysisManager &) {
- ++Runs;
- return Result{Function.blockCount()};
- }
-
- static inline std::atomic<std::uint32_t> Runs = 0;
-};
-
-class DependentAnalysis final {
-public:
- struct Result final {
- std::size_t Count = 0;
- };
-
- [[nodiscard]] static constexpr std::string_view name() noexcept {
- return "Dependent";
- }
-
- [[nodiscard]] static rcc::Expected<Result> run(rcc::MachineFunction &Function,
- const rcc::TargetDescription &Target,
- rcc::MachineAnalysisManager &Analyses) {
- auto Blocks = Analyses.getResult<BlockCountAnalysis>(Function, Target);
- if (!Blocks)
- return std::unexpected(Blocks.error());
- return Result{(*Blocks)->Count};
- }
-};
-
-class NonThreadSafeAnalysis final {
-public:
- struct Result final {};
-
- [[nodiscard]] static constexpr std::string_view name() noexcept {
- return "NonThreadSafe";
- }
-
- [[nodiscard]] static rcc::Expected<Result>
- run(rcc::MachineFunction &, const rcc::TargetDescription &, rcc::MachineAnalysisManager &) {
- return Result{};
- }
-};
-
-class FailingAnalysis final {
-public:
- struct Result final {};
-
- [[nodiscard]] static constexpr std::string_view name() noexcept {
- return "Failing";
- }
-
- [[nodiscard]] static rcc::Expected<Result>
- run(rcc::MachineFunction &, const rcc::TargetDescription &, rcc::MachineAnalysisManager &) {
- return rcc::makeUnexpected(rcc::ErrorCode::VerificationError,
- "intentional machine analysis failure");
- }
-};
-
-class StickyAnalysis final {
-public:
- struct Result final {
- [[nodiscard]] bool invalidate(const rcc::MachineFunction &,
- const rcc::PreservedAnalyses &) noexcept {
- return false;
- }
- };
-
- [[nodiscard]] static constexpr std::string_view name() noexcept {
- return "Sticky";
- }
- [[nodiscard]] static rcc::Expected<Result>
- run(rcc::MachineFunction &, const rcc::TargetDescription &, rcc::MachineAnalysisManager &) {
- return Result{};
- }
-};
-
-class ExpiringAnalysis final {
-public:
- struct Result final {
- [[nodiscard]] bool invalidate(const rcc::MachineFunction &,
- const rcc::PreservedAnalyses &) noexcept {
- return true;
- }
- };
-
- [[nodiscard]] static constexpr std::string_view name() noexcept {
- return "Expiring";
- }
- [[nodiscard]] static rcc::Expected<Result>
- run(rcc::MachineFunction &, const rcc::TargetDescription &, rcc::MachineAnalysisManager &) {
- return Result{};
- }
-};
-
-class CycleA final {
-public:
- struct Result final {};
- [[nodiscard]] static constexpr std::string_view name() noexcept {
- return "CycleA";
- }
- [[nodiscard]] static rcc::Expected<Result>
- run(rcc::MachineFunction &, const rcc::TargetDescription &, rcc::MachineAnalysisManager &) {
- return Result{};
- }
-};
-
-class CycleB final {
-public:
- struct Result final {};
- [[nodiscard]] static constexpr std::string_view name() noexcept {
- return "CycleB";
- }
- [[nodiscard]] static rcc::Expected<Result>
- run(rcc::MachineFunction &, const rcc::TargetDescription &, rcc::MachineAnalysisManager &) {
- return Result{};
- }
-};
-
-class RequiredAnalysisPass final : public rcc::MachineFunctionPass {
-public:
- [[nodiscard]] const rcc::MachinePassContract &contract() const noexcept override {
- static const rcc::MachinePassContract Contract{
- .Name = "RequiredAnalysis",
- .Requires = {},
- .Sets = {},
- .Clears = {},
- .RequiredAnalyses = {rcc::MachineAnalysisManager::id<DependentAnalysis>()},
- .VerificationStage = std::nullopt,
- };
- return Contract;
- }
-
- [[nodiscard]] rcc::Expected<rcc::MachinePassResult>
- run(rcc::MachineFunction &Function, const rcc::TargetDescription &Target,
- rcc::MachineAnalysisManager &Analyses) override {
- auto Dependent = Analyses.getResult<DependentAnalysis>(Function, Target);
- if (!Dependent)
- return std::unexpected(Dependent.error());
- auto FirstAction = Analyses.requestAction("first", Function);
- auto SecondAction = Analyses.requestAction("second", Function);
- if (!FirstAction || !SecondAction || !*FirstAction || *SecondAction) {
- return rcc::makeUnexpected(rcc::ErrorCode::VerificationError,
- "per-function action fuel was not enforced");
- }
- rcc::MachinePassResult Result;
- Result.Preserved = rcc::PreservedAnalyses::all();
- return Result;
- }
-};
-
-class UnreportedChangePass final : public rcc::MachineFunctionPass {
-public:
- [[nodiscard]] const rcc::MachinePassContract &contract() const noexcept override {
- static const rcc::MachinePassContract Contract{
- .Name = "UnreportedChange",
- .Requires = {},
- .Sets = {},
- .Clears = {},
- .RequiredAnalyses = {},
- .VerificationStage = std::nullopt,
- };
- return Contract;
- }
-
- [[nodiscard]] rcc::Expected<rcc::MachinePassResult> run(rcc::MachineFunction &Function,
- const rcc::TargetDescription &,
- rcc::MachineAnalysisManager &) override {
- auto Block = Function.createBasicBlock();
- if (!Block)
- return std::unexpected(Block.error());
- auto Appended = Function.appendBlock(**Block);
- if (!Appended)
- return std::unexpected(Appended.error());
- return rcc::MachinePassResult{};
- }
-};
-
-class RequiresFailingAnalysisPass final : public rcc::MachineFunctionPass {
-public:
- [[nodiscard]] const rcc::MachinePassContract &contract() const noexcept override {
- static const rcc::MachinePassContract Contract{
- .Name = "RequiresFailingAnalysis",
- .Requires = {},
- .Sets = {},
- .Clears = {},
- .RequiredAnalyses = {rcc::MachineAnalysisManager::id<FailingAnalysis>()},
- .VerificationStage = std::nullopt,
- };
- return Contract;
- }
-
- [[nodiscard]] rcc::Expected<rcc::MachinePassResult> run(rcc::MachineFunction &,
- const rcc::TargetDescription &,
- rcc::MachineAnalysisManager &) override {
- return rcc::MachinePassResult{};
- }
-};
-
-class MutatingFailurePass final : public rcc::MachineFunctionPass {
-public:
- [[nodiscard]] const rcc::MachinePassContract &contract() const noexcept override {
- static const rcc::MachinePassContract Contract{
- .Name = "MutatingFailure",
- .Requires = {},
- .Sets = {},
- .Clears = {},
- .RequiredAnalyses = {rcc::MachineAnalysisManager::id<BlockCountAnalysis>()},
- .VerificationStage = std::nullopt,
- };
- return Contract;
- }
-
- [[nodiscard]] rcc::Expected<rcc::MachinePassResult> run(rcc::MachineFunction &Function,
- const rcc::TargetDescription &,
- rcc::MachineAnalysisManager &) override {
- auto Block = Function.createBasicBlock();
- if (!Block)
- return std::unexpected(Block.error());
- auto Appended = Function.appendBlock(**Block);
- if (!Appended)
- return std::unexpected(Appended.error());
- return rcc::makeUnexpected(rcc::ErrorCode::VerificationError,
- "intentional mutating pass failure");
- }
-};
-
-class ParallelCountingPass final : public rcc::MachineFunctionPass {
-public:
- explicit ParallelCountingPass(std::atomic<std::uint32_t> &Count) : Count(&Count) {}
-
- [[nodiscard]] const rcc::MachinePassContract &contract() const noexcept override {
- static const rcc::MachinePassContract Contract = [] {
- rcc::MachinePassContract Value{
- .Name = "ParallelCounting",
- .Requires = {},
- .Sets = {},
- .Clears = {},
- .RequiredAnalyses = {},
- .VerificationStage = std::nullopt,
- };
- Value.ThreadSafety = rcc::PassThreadSafety::ThreadSafe;
- return Value;
- }();
- return Contract;
- }
-
- [[nodiscard]] rcc::Expected<rcc::MachinePassResult> run(rcc::MachineFunction &,
- const rcc::TargetDescription &,
- rcc::MachineAnalysisManager &) override {
- Count->fetch_add(1, std::memory_order_relaxed);
- return rcc::MachinePassResult{};
- }
-
-private:
- std::atomic<std::uint32_t> *Count;
-};
-
-class InstrumentedParallelPass final : public rcc::MachineFunctionPass {
-public:
- InstrumentedParallelPass(std::atomic<std::uint32_t> &Runs, std::atomic<std::uint32_t> &Active,
- std::atomic<std::uint32_t> &MaximumActive)
- : Runs(&Runs), Active(&Active), MaximumActive(&MaximumActive) {}
-
- [[nodiscard]] const rcc::MachinePassContract &contract() const noexcept override {
- static const rcc::MachinePassContract Contract = [] {
- rcc::MachinePassContract Value{
- .Name = "InstrumentedParallel",
- .Requires = {},
- .Sets = {},
- .Clears = {},
- .RequiredAnalyses = {},
- .VerificationStage = std::nullopt,
- };
- Value.ThreadSafety = rcc::PassThreadSafety::ThreadSafe;
- Value.DeclaredChanges = {rcc::MachinePassChange::BlockLayout};
- return Value;
- }();
- return Contract;
- }
-
- [[nodiscard]] rcc::Expected<rcc::MachinePassResult> run(rcc::MachineFunction &Function,
- const rcc::TargetDescription &,
- rcc::MachineAnalysisManager &) override {
- const std::uint32_t Current = Active->fetch_add(1, std::memory_order_acq_rel) + 1U;
- std::uint32_t Maximum = MaximumActive->load(std::memory_order_relaxed);
- while (Maximum < Current &&
- !MaximumActive->compare_exchange_weak(Maximum, Current, std::memory_order_relaxed)) {
- }
- std::this_thread::sleep_for(std::chrono::milliseconds(2));
- auto Block = Function.createBasicBlock();
- if (!Block) {
- Active->fetch_sub(1, std::memory_order_acq_rel);
- return std::unexpected(Block.error());
- }
- auto Appended = Function.appendBlock(**Block);
- Active->fetch_sub(1, std::memory_order_acq_rel);
- if (!Appended)
- return std::unexpected(Appended.error());
- Runs->fetch_add(1, std::memory_order_relaxed);
- rcc::MachinePassResult Result;
- Result.Changed = true;
- Result.Changes.add(rcc::MachinePassChange::BlockLayout);
- return Result;
- }
-
-private:
- std::atomic<std::uint32_t> *Runs;
- std::atomic<std::uint32_t> *Active;
- std::atomic<std::uint32_t> *MaximumActive;
-};
-
-class RequiresNonThreadSafeAnalysisPass final : public rcc::MachineFunctionPass {
-public:
- [[nodiscard]] const rcc::MachinePassContract &contract() const noexcept override {
- static const rcc::MachinePassContract Contract = [] {
- rcc::MachinePassContract Value{
- .Name = "RequiresNonThreadSafe",
- .Requires = {},
- .Sets = {},
- .Clears = {},
- .RequiredAnalyses =
- {
- rcc::MachineAnalysisManager::id<NonThreadSafeAnalysis>(),
- },
- .VerificationStage = std::nullopt,
- };
- Value.ThreadSafety = rcc::PassThreadSafety::ThreadSafe;
- return Value;
- }();
- return Contract;
- }
-
- [[nodiscard]] rcc::Expected<rcc::MachinePassResult> run(rcc::MachineFunction &,
- const rcc::TargetDescription &,
- rcc::MachineAnalysisManager &) override {
- return rcc::MachinePassResult{};
- }
-};
-
-class UnitFunctionCountAnalysis final {
-public:
- struct Result final {
- std::size_t Count = 0;
- };
- [[nodiscard]] static constexpr std::string_view name() noexcept {
- return "UnitFunctionCount";
- }
- [[nodiscard]] static rcc::Expected<Result> run(rcc::CompilationUnit &Unit,
- const rcc::TargetDescription &,
- rcc::CompilationUnitAnalysisManager &) {
- return Result{Unit.functionCount()};
- }
-};
-
-class FailingUnitAnalysis final {
-public:
- struct Result final {};
-
- [[nodiscard]] static constexpr std::string_view name() noexcept {
- return "FailingUnit";
- }
-
- [[nodiscard]] static rcc::Expected<Result> run(rcc::CompilationUnit &,
- const rcc::TargetDescription &,
- rcc::CompilationUnitAnalysisManager &) {
- return rcc::makeUnexpected(rcc::ErrorCode::VerificationError,
- "intentional compilation-unit analysis failure");
- }
-};
-
-class RequiresFailingUnitAnalysisPass final : public rcc::CompilationUnitPass {
-public:
- [[nodiscard]] const rcc::CompilationUnitPassContract &contract() const noexcept override {
- static const rcc::CompilationUnitPassContract Contract{
- .Name = "RequiresFailingUnitAnalysis",
- .RequiredAnalyses =
- {
- rcc::CompilationUnitAnalysisManager::id<FailingUnitAnalysis>(),
- },
- .Parameters = {},
- .DeclaredChanges = {},
- };
- return Contract;
- }
-
- [[nodiscard]] rcc::Expected<rcc::CompilationUnitPassResult>
- run(rcc::CompilationUnit &, const rcc::TargetDescription &, rcc::CompilationUnitAnalysisManager &,
- rcc::MachineAnalysisManager &) override {
- return rcc::CompilationUnitPassResult{};
- }
-};
-
-class ObserveUnitPass final : public rcc::CompilationUnitPass {
-public:
- [[nodiscard]] const rcc::CompilationUnitPassContract &contract() const noexcept override {
- static const rcc::CompilationUnitPassContract Contract{
- .Name = "ObserveUnit",
- .RequiredAnalyses = {rcc::CompilationUnitAnalysisManager::id<UnitFunctionCountAnalysis>()},
- .Parameters = {},
- .DeclaredChanges = {},
- };
- return Contract;
- }
-
- [[nodiscard]] rcc::Expected<rcc::CompilationUnitPassResult>
- run(rcc::CompilationUnit &Unit, const rcc::TargetDescription &Target,
- rcc::CompilationUnitAnalysisManager &Analyses, rcc::MachineAnalysisManager &) override {
- auto Count = Analyses.getResult<UnitFunctionCountAnalysis>(Unit, Target);
- if (!Count)
- return std::unexpected(Count.error());
- if ((*Count)->Count != Unit.functionCount()) {
- return rcc::makeUnexpected(rcc::ErrorCode::VerificationError,
- "unit analysis returned a stale function count");
- }
- return rcc::CompilationUnitPassResult{};
- }
-};
-
-class MutatingUnitFailurePass final : public rcc::CompilationUnitPass {
-public:
- [[nodiscard]] const rcc::CompilationUnitPassContract &contract() const noexcept override {
- static const rcc::CompilationUnitPassContract Contract{
- .Name = "MutatingUnitFailure",
- .RequiredAnalyses = {rcc::CompilationUnitAnalysisManager::id<UnitFunctionCountAnalysis>()},
- .Parameters = {},
- .DeclaredChanges = {rcc::MachinePassChange::BlockLayout},
- };
- return Contract;
- }
-
- [[nodiscard]] rcc::Expected<rcc::CompilationUnitPassResult>
- run(rcc::CompilationUnit &Unit, const rcc::TargetDescription &,
- rcc::CompilationUnitAnalysisManager &, rcc::MachineAnalysisManager &) override {
- auto Created = Unit.createMachineFunction("added-before-failure");
- if (!Created)
- return std::unexpected(Created.error());
- return rcc::makeUnexpected(rcc::ErrorCode::VerificationError,
- "intentional compilation-unit pass failure");
- }
-};
-
-void testAnalysisInfrastructure(TestContext &Context, const rcc::TargetDescription &Target) {
- auto Function =
- rcc::MachineFunction::create("analysis", rcc::MachineFunctionStableID::fromValue(1));
- Context.expect(Function.has_value(), "analysis function creation must succeed");
- if (!Function)
- return;
-
- rcc::MachineAnalysisManager Analyses;
- Context.expect(Analyses
- .registerAnalysis<BlockCountAnalysis>({
- .Name = "BlockCount",
- .Dependencies = {},
- .InvalidatedBy = {rcc::MachinePassChange::CFG},
- })
- .has_value(),
- "base analysis registration must succeed");
- Context.expect(Analyses
- .registerAnalysis<DependentAnalysis>({
- .Name = "Dependent",
- .Dependencies = {rcc::MachineAnalysisManager::id<BlockCountAnalysis>()},
- .InvalidatedBy = {rcc::MachinePassChange::Operands},
- })
- .has_value(),
- "dependent analysis registration must succeed");
-
- BlockCountAnalysis::Runs = 0;
- auto First = Analyses.getResult<DependentAnalysis>(**Function, Target);
- auto Second = Analyses.getResult<DependentAnalysis>(**Function, Target);
- Context.expect(First && Second && *First == *Second && BlockCountAnalysis::Runs == 1,
- "analysis dependencies must materialize once and then use the cache");
-
- Analyses.invalidate(**Function, rcc::PreservedAnalyses::none(),
- {rcc::MachinePassChange::Registers});
- Context.expect(Analyses.cachedResultCount(**Function) == 2,
- "unrelated changes must preserve cached analyses precisely");
- Analyses.invalidate(**Function, rcc::PreservedAnalyses::none(), {rcc::MachinePassChange::CFG});
- Context.expect(Analyses.cachedResultCount(**Function) == 0,
- "invalidating a dependency must invalidate its dependent analysis");
-
- rcc::MachineAnalysisManager Cyclic;
- Context.expect(Cyclic
- .registerAnalysis<CycleA>({
- .Name = "CycleA",
- .Dependencies = {rcc::MachineAnalysisManager::id<CycleB>()},
- })
- .has_value() &&
- Cyclic
- .registerAnalysis<CycleB>({
- .Name = "CycleB",
- .Dependencies = {rcc::MachineAnalysisManager::id<CycleA>()},
- })
- .has_value(),
- "cyclic analysis registrations must be accepted until requested");
- Context.expect(!Cyclic.getResult<CycleA>(**Function, Target),
- "analysis dependency cycles must produce a structured error");
-
- rcc::MachineAnalysisManager CustomInvalidation;
- Context.expect(CustomInvalidation
- .registerAnalysis<StickyAnalysis>({
- .Name = "Sticky",
- .Dependencies = {},
- .InvalidatedBy = {rcc::MachinePassChange::CFG},
- })
- .has_value() &&
- CustomInvalidation
- .registerAnalysis<ExpiringAnalysis>({
- .Name = "Expiring",
- .Dependencies = {},
- .InvalidatedBy = {rcc::MachinePassChange::CFG},
- })
- .has_value() &&
- CustomInvalidation.getResult<StickyAnalysis>(**Function, Target) &&
- CustomInvalidation.getResult<ExpiringAnalysis>(**Function, Target),
- "custom invalidation analyses must materialize");
- CustomInvalidation.invalidate(**Function, rcc::PreservedAnalyses::none(),
- {rcc::MachinePassChange::CFG});
- Context.expect(
- CustomInvalidation.hasCached(**Function, rcc::MachineAnalysisManager::id<StickyAnalysis>()) &&
- !CustomInvalidation.hasCached(**Function,
- rcc::MachineAnalysisManager::id<ExpiringAnalysis>()),
- "analysis invalidate() must return false to preserve and true to invalidate");
-}
-
-void testPassAndInstrumentation(TestContext &Context, const rcc::TargetDescription &Target) {
- auto Function = rcc::MachineFunction::create("pass", rcc::MachineFunctionStableID::fromValue(2));
- Context.expect(Function.has_value(), "pass function creation must succeed");
- if (!Function)
- return;
-
- rcc::MachineAnalysisManager Analyses;
- Context.expect(
- Analyses
- .registerAnalysis<BlockCountAnalysis>({
- .Name = "BlockCount",
- .Dependencies = {},
- .InvalidatedBy = rcc::MachinePassChanges::all(),
- .ThreadSafety = rcc::PassThreadSafety::ThreadSafe,
- })
- .has_value() &&
- Analyses
- .registerAnalysis<DependentAnalysis>({
- .Name = "Dependent",
- .Dependencies = {rcc::MachineAnalysisManager::id<BlockCountAnalysis>()},
- })
- .has_value(),
- "pass analysis registrations must succeed");
-
- rcc::MachinePassInstrumentation Instrumentation;
- Instrumentation.setPerFunctionFuel(1);
- std::uint32_t BeforePass = 0;
- std::uint32_t AfterPass = 0;
- std::uint32_t BeforeAnalysis = 0;
- std::uint32_t AfterAnalysis = 0;
- std::uint32_t AnalysisCacheHits = 0;
- std::uint32_t Printed = 0;
- std::uint32_t ChangeEvents = 0;
- std::uint32_t FailureEvents = 0;
- Instrumentation.addBeforeCallback(
- [&BeforePass](std::string_view, const rcc::MachineFunction &) { ++BeforePass; });
- Instrumentation.addAfterCallback([&AfterPass](std::string_view, const rcc::MachineFunction &,
- const rcc::MachinePassResult &) { ++AfterPass; });
- Instrumentation.addBeforeAnalysisCallback(
- [&BeforeAnalysis](std::string_view, const rcc::MachineFunction &) { ++BeforeAnalysis; });
- Instrumentation.addAfterAnalysisCallback(
- [&AfterAnalysis, &AnalysisCacheHits](std::string_view, const rcc::MachineFunction &,
- bool CacheHit, std::uint64_t) {
- ++AfterAnalysis;
- AnalysisCacheHits += CacheHit ? 1U : 0U;
- });
- Instrumentation.addIRPrintCallback([&Printed](rcc::InstrumentationPhase, std::string_view,
- const rcc::MachineFunction &) { ++Printed; });
- Instrumentation.addChangeCallback([&ChangeEvents](std::string_view, const rcc::MachineFunction &,
- rcc::MachinePassChanges Changes,
- bool FingerprintChanged) {
- if (Changes.empty() && !FingerprintChanged)
- ++ChangeEvents;
- });
- Instrumentation.addFailureCallback([&FailureEvents](std::string_view,
- const rcc::MachineFunction &,
- const rcc::Error &) { ++FailureEvents; });
-
- rcc::MachinePassManager Pipeline;
- Context.expect(Pipeline.addPass(std::make_unique<RequiredAnalysisPass>()).has_value(),
- "a valid pass must be accepted");
- auto Run = Pipeline.run(**Function, Target, Analyses, &Instrumentation);
- Context.expect(Run.has_value(), "a pass with a materialized required analysis must run");
- Context.expect(BeforePass == 1 && AfterPass == 1 && BeforeAnalysis == 2 && AfterAnalysis == 4 &&
- AnalysisCacheHits == 2 && Printed == 2 && ChangeEvents == 1,
- "pass, analysis, cache-hit, IR-print, and change callbacks must all run");
- const auto Statistics = Instrumentation.statistics();
- Context.expect(Statistics.size() == 3,
- "instrumentation must collect one pass and two analysis statistics");
-
- auto ChangedFunction =
- rcc::MachineFunction::create("unreported", rcc::MachineFunctionStableID::fromValue(3));
- rcc::MachineAnalysisManager EmptyAnalyses;
- rcc::MachinePassManager Detecting(
- {.VerifyBefore = true, .VerifyEach = true, .DetectUnreportedChanges = true});
- Context.expect(ChangedFunction &&
- Detecting.addPass(std::make_unique<UnreportedChangePass>()).has_value() &&
- !Detecting.run(**ChangedFunction, Target, EmptyAnalyses, &Instrumentation) &&
- FailureEvents == 1,
- "change detection must reject an unreported MachineIR mutation");
-
- auto FailedFunction =
- rcc::MachineFunction::create("failed", rcc::MachineFunctionStableID::fromValue(4));
- rcc::MachineAnalysisManager FailureAnalyses;
- Context.expect(FailedFunction && FailureAnalyses
- .registerAnalysis<BlockCountAnalysis>({
- .Name = "BlockCount",
- .Dependencies = {},
- .InvalidatedBy = rcc::MachinePassChanges::all(),
- })
- .has_value(),
- "mutating failure analysis fixtures must be valid");
- rcc::MachinePassManager FailingPipeline;
- Context.expect(FailedFunction &&
- FailingPipeline.addPass(std::make_unique<MutatingFailurePass>()).has_value() &&
- !FailingPipeline.run(**FailedFunction, Target, FailureAnalyses) &&
- !FailureAnalyses.hasCached(
- **FailedFunction, rcc::MachineAnalysisManager::id<BlockCountAnalysis>()),
- "a pass that mutates and fails must not leave stale function analyses cached");
-
- rcc::MachineAnalysisManager FailingAnalyses;
- Context.expect(FailingAnalyses
- .registerAnalysis<FailingAnalysis>({
- .Name = "Failing",
- .Dependencies = {},
- })
- .has_value(),
- "failing analysis registration must succeed");
- rcc::MachinePassInstrumentation FailureInstrumentation;
- std::uint32_t BeforeFailingAnalysis = 0;
- std::uint32_t FailingAnalysisEvents = 0;
- std::uint32_t RequiredAnalysisPassFailures = 0;
- FailureInstrumentation.addBeforeAnalysisCallback(
- [&BeforeFailingAnalysis](std::string_view, const rcc::MachineFunction &) {
- ++BeforeFailingAnalysis;
- });
- FailureInstrumentation.addAnalysisFailureCallback(
- [&FailingAnalysisEvents](std::string_view, const rcc::MachineFunction &, const rcc::Error &) {
- ++FailingAnalysisEvents;
- });
- FailureInstrumentation.addFailureCallback(
- [&RequiredAnalysisPassFailures](std::string_view, const rcc::MachineFunction &,
- const rcc::Error &) { ++RequiredAnalysisPassFailures; });
- rcc::MachinePassManager RequiredAnalysisFailurePipeline;
- Context.expect(
- RequiredAnalysisFailurePipeline.addPass(std::make_unique<RequiresFailingAnalysisPass>())
- .has_value() &&
- !RequiredAnalysisFailurePipeline.run(**Function, Target, FailingAnalyses,
- &FailureInstrumentation) &&
- BeforeFailingAnalysis == 1 && FailingAnalysisEvents == 1 &&
- RequiredAnalysisPassFailures == 1,
- "analysis and pass failure instrumentation must report required-analysis failures");
-}
-
-void testCompilationUnitAndParallelism(TestContext &Context, const rcc::TargetDescription &Target) {
- rcc::CompilationUnit Unit;
- for (std::size_t Index = 0; Index < 8; ++Index) {
- auto Function = Unit.createMachineFunction("f" + std::to_string(Index));
- Context.expect(Function.has_value(), "parallel test function creation must succeed");
- }
-
- std::atomic<std::uint32_t> Runs = 0;
- rcc::MachinePassManager Parallel;
- Context.expect(Parallel.addPass(std::make_unique<ParallelCountingPass>(Runs)).has_value(),
- "thread-safe pass registration must succeed");
- rcc::MachineAnalysisManager Prototype;
- auto ParallelRun = Parallel.runFunctions(Unit, Target, Prototype, 4);
- Context.expect(ParallelRun.has_value() && Runs == Unit.functionCount(),
- "parallel function execution must visit every function exactly once");
-
- std::atomic<std::uint32_t> DuplicateRuns = 0;
- rcc::MachinePassManager Repeated;
- Context.expect(
- Repeated.addPass(std::make_unique<ParallelCountingPass>(DuplicateRuns)).has_value() &&
- Repeated.addPass(std::make_unique<ParallelCountingPass>(DuplicateRuns)).has_value() &&
- Repeated.runFunctions(Unit, Target, Prototype, 4).has_value() &&
- DuplicateRuns == Unit.functionCount() * 2,
- "a pipeline must allow the same named pass to run more than once");
-
- rcc::MachinePassInstrumentation Instrumentation;
- std::vector<std::string> ObservedFunctions;
- std::vector<std::size_t> BeforeBlockCounts;
- std::vector<std::size_t> AfterBlockCounts;
- Instrumentation.addBeforeCallback([&ObservedFunctions, &BeforeBlockCounts](
- std::string_view, const rcc::MachineFunction &Function) {
- ObservedFunctions.emplace_back(Function.name());
- BeforeBlockCounts.push_back(Function.blockCount());
- });
- Instrumentation.addAfterCallback([&AfterBlockCounts](std::string_view,
- const rcc::MachineFunction &Function,
- const rcc::MachinePassResult &) {
- AfterBlockCounts.push_back(Function.blockCount());
- });
- std::atomic<std::uint32_t> InstrumentedRuns = 0;
- std::atomic<std::uint32_t> ActiveRuns = 0;
- std::atomic<std::uint32_t> MaximumActiveRuns = 0;
- rcc::MachinePassManager Instrumented;
- Context.expect(
- Instrumented
- .addPass(std::make_unique<InstrumentedParallelPass>(InstrumentedRuns, ActiveRuns,
- MaximumActiveRuns))
- .has_value() &&
- Instrumented.runFunctions(Unit, Target, Prototype, 4, &Instrumentation).has_value() &&
- InstrumentedRuns == Unit.functionCount() && MaximumActiveRuns > 1,
- "instrumented function pipelines must execute concurrently with a parallel request");
- bool StableCallbackOrder = ObservedFunctions.size() == Unit.functionCount();
- for (std::size_t Index = 0; StableCallbackOrder && Index < ObservedFunctions.size(); ++Index)
- StableCallbackOrder = ObservedFunctions[Index] == "f" + std::to_string(Index);
- Context.expect(StableCallbackOrder,
- "instrumentation callbacks must commit in stable compilation-unit order");
- Context.expect(
- BeforeBlockCounts.size() == Unit.functionCount() &&
- AfterBlockCounts.size() == Unit.functionCount() &&
- std::ranges::all_of(BeforeBlockCounts, [](std::size_t Count) { return Count == 0; }) &&
- std::ranges::all_of(AfterBlockCounts, [](std::size_t Count) { return Count == 1; }),
- "buffered instrumentation must retain distinct before/after IR snapshots");
-
- rcc::MachineAnalysisManager UnsafePrototype;
- Context.expect(UnsafePrototype
- .registerAnalysis<NonThreadSafeAnalysis>({
- .Name = "NonThreadSafe",
- .Dependencies = {},
- .InvalidatedBy = rcc::MachinePassChanges::all(),
- .ThreadSafety = rcc::PassThreadSafety::NotThreadSafe,
- })
- .has_value(),
- "non-thread-safe analysis registration must be explicit");
- rcc::MachinePassManager UnsafeParallel;
- Context.expect(
- UnsafeParallel.addPass(std::make_unique<RequiresNonThreadSafeAnalysisPass>()).has_value() &&
- !UnsafeParallel.runFunctions(Unit, Target, UnsafePrototype, 4),
- "parallel pipelines must reject a required analysis not marked thread-safe");
-
- rcc::CompilationUnitAnalysisManager UnitAnalyses;
- Context.expect(UnitAnalyses
- .registerAnalysis<UnitFunctionCountAnalysis>({
- .Name = "UnitFunctionCount",
- .Dependencies = {},
- .InvalidatedBy = rcc::MachinePassChanges::all(),
- })
- .has_value(),
- "compilation-unit analysis registration must succeed");
- rcc::MachineAnalysisManager FunctionAnalyses;
- rcc::CompilationUnitPassManager UnitPipeline;
- Context.expect(UnitPipeline.addPass(std::make_unique<ObserveUnitPass>()).has_value(),
- "compilation-unit pass registration must succeed");
- rcc::MachinePassInstrumentation UnitInstrumentation;
- std::uint32_t BeforeUnit = 0;
- std::uint32_t AfterUnit = 0;
- std::uint32_t BeforeUnitAnalysis = 0;
- std::uint32_t AfterUnitAnalysis = 0;
- std::uint32_t UnitAnalysisCacheHits = 0;
- UnitInstrumentation.addBeforeUnitCallback(
- [&BeforeUnit](std::string_view, const rcc::CompilationUnit &) { ++BeforeUnit; });
- UnitInstrumentation.addAfterUnitCallback(
- [&AfterUnit](std::string_view, const rcc::CompilationUnit &,
- const rcc::CompilationUnitPassResult &) { ++AfterUnit; });
- UnitInstrumentation.addBeforeUnitAnalysisCallback(
- [&BeforeUnitAnalysis](std::string_view, const rcc::CompilationUnit &) {
- ++BeforeUnitAnalysis;
- });
- UnitInstrumentation.addAfterUnitAnalysisCallback(
- [&AfterUnitAnalysis, &UnitAnalysisCacheHits](std::string_view, const rcc::CompilationUnit &,
- bool CacheHit, std::uint64_t) {
- ++AfterUnitAnalysis;
- UnitAnalysisCacheHits += CacheHit ? 1U : 0U;
- });
- Context.expect(
- UnitPipeline.run(Unit, Target, UnitAnalyses, FunctionAnalyses, &UnitInstrumentation)
- .has_value(),
- "compilation-unit pass and analysis must run through their own manager");
- Context.expect(BeforeUnit == 1 && AfterUnit == 1 && BeforeUnitAnalysis == 1 &&
- AfterUnitAnalysis == 2 && UnitAnalysisCacheHits == 1,
- "compilation-unit instrumentation must report pass and analysis cache events");
-
- rcc::CompilationUnitAnalysisManager FailingUnitAnalyses;
- Context.expect(FailingUnitAnalyses
- .registerAnalysis<FailingUnitAnalysis>({
- .Name = "FailingUnit",
- .Dependencies = {},
- })
- .has_value(),
- "failing compilation-unit analysis registration must succeed");
- rcc::CompilationUnitPassManager FailingUnitAnalysisPipeline;
- Context.expect(
- FailingUnitAnalysisPipeline.addPass(std::make_unique<RequiresFailingUnitAnalysisPass>())
- .has_value(),
- "a pass requiring a failing unit analysis must register");
- rcc::MachinePassInstrumentation FailingUnitInstrumentation;
- std::uint32_t BeforeFailingUnitAnalysis = 0;
- std::uint32_t FailingUnitAnalysisEvents = 0;
- std::uint32_t RequiredUnitAnalysisPassFailures = 0;
- FailingUnitInstrumentation.addBeforeUnitAnalysisCallback(
- [&BeforeFailingUnitAnalysis](std::string_view, const rcc::CompilationUnit &) {
- ++BeforeFailingUnitAnalysis;
- });
- FailingUnitInstrumentation.addUnitAnalysisFailureCallback(
- [&FailingUnitAnalysisEvents](std::string_view, const rcc::CompilationUnit &,
- const rcc::Error &) { ++FailingUnitAnalysisEvents; });
- FailingUnitInstrumentation.addUnitFailureCallback(
- [&RequiredUnitAnalysisPassFailures](std::string_view, const rcc::CompilationUnit &,
- const rcc::Error &) {
- ++RequiredUnitAnalysisPassFailures;
- });
- Context.expect(
- !FailingUnitAnalysisPipeline.run(Unit, Target, FailingUnitAnalyses, FunctionAnalyses,
- &FailingUnitInstrumentation) &&
- BeforeFailingUnitAnalysis == 1 && FailingUnitAnalysisEvents == 1 &&
- RequiredUnitAnalysisPassFailures == 1,
- "unit analysis and pass failure instrumentation must report required-analysis failures");
-
- rcc::MachineFunction *FirstFunction = Unit.functionAt(0);
- Context.expect(
- FirstFunction != nullptr &&
- FunctionAnalyses
- .registerAnalysis<BlockCountAnalysis>({
- .Name = "BlockCount",
- .Dependencies = {},
- .InvalidatedBy = rcc::MachinePassChanges::all(),
- })
- .has_value() &&
- FunctionAnalyses.getResult<BlockCountAnalysis>(*FirstFunction, Target).has_value(),
- "compilation-unit failure cache fixtures must materialize");
- rcc::CompilationUnitPassManager FailingUnitPipeline;
- Context.expect(
- FirstFunction != nullptr &&
- FailingUnitPipeline.addPass(std::make_unique<MutatingUnitFailurePass>()).has_value() &&
- !FailingUnitPipeline.run(Unit, Target, UnitAnalyses, FunctionAnalyses) &&
- !UnitAnalyses.hasCached(
- Unit, rcc::CompilationUnitAnalysisManager::id<UnitFunctionCountAnalysis>()) &&
- !FunctionAnalyses.hasCached(*FirstFunction,
- rcc::MachineAnalysisManager::id<BlockCountAnalysis>()),
- "a mutating compilation-unit failure must clear unit and function analysis caches");
-}
-
-} // namespace
-
-int main() {
- TestContext Context;
- auto Target = createTarget();
- Context.expect(Target.has_value(), "machine pass tests require a valid target description");
- if (!Target)
- return Context.result();
-
- testAnalysisInfrastructure(Context, *Target);
- testPassAndInstrumentation(Context, *Target);
- testCompilationUnitAndParallelism(Context, *Target);
- return Context.result();
-}
diff --git a/backend/unittests/CodeGen/MachineSchedulingTest.cpp b/backend/unittests/CodeGen/MachineSchedulingTest.cpp
index bcd4100..5162fb8 100644
--- a/backend/unittests/CodeGen/MachineSchedulingTest.cpp
+++ b/backend/unittests/CodeGen/MachineSchedulingTest.cpp
@@ -386,7 +386,7 @@ void testTargetSchedulingHooks(TestContext &Context, const rcc::TargetDescriptio
Context.expect(false, "target scheduling preference model must be created");
return;
}
- rcc::MachineAnalysisManager Analyses;
+ rcc::MachineFunctionAnalysisManager Analyses;
rcc::MachineSchedulerPass PreferencePass(
*PreferenceModel, {.MaximumRegionSize = 8, .EnableBidirectionalScheduling = false});
auto Preferred = PreferencePass.run(*Fixture.Function, Target, Analyses);
@@ -408,7 +408,7 @@ void testTargetSchedulingHooks(TestContext &Context, const rcc::TargetDescriptio
rcc::MachineInstr *BoundaryFirst =
buildADDI(Context, BoundaryBuilder, makeGPR(Context, *BoundaryFixture.Function), X0, 1);
(void)buildADDI(Context, BoundaryBuilder, makeGPR(Context, *BoundaryFixture.Function), X0, 2);
- rcc::MachineAnalysisManager BoundaryAnalyses;
+ rcc::MachineFunctionAnalysisManager BoundaryAnalyses;
rcc::MachineSchedulerPass BoundaryPass(
*BoundaryModel, {.MaximumRegionSize = 8, .EnableBidirectionalScheduling = false});
auto BoundaryResult = BoundaryPass.run(*BoundaryFixture.Function, Target, BoundaryAnalyses);
@@ -455,7 +455,7 @@ void testPreRAScheduler(TestContext &Context, const rcc::TargetDescription &Targ
buildSchedulingChain(Context, Fixture, Target, false, Short, Long);
Context.expect(Fixture.Block->firstInstruction() == Short,
"pre-RA fixture must begin in intentionally suboptimal order");
- rcc::MachineAnalysisManager Analyses;
+ rcc::MachineFunctionAnalysisManager Analyses;
rcc::MachineSchedulerPass Pass(rcc::riscv::schedulingModel(),
{.MaximumRegionSize = 64, .EnableBidirectionalScheduling = false});
auto Result = Pass.run(*Fixture.Function, Target, Analyses);
@@ -482,7 +482,7 @@ void testPostRASchedulerAndLivenessRepair(TestContext &Context,
if (!Rebuilt)
return;
- rcc::MachineAnalysisManager Analyses;
+ rcc::MachineFunctionAnalysisManager Analyses;
rcc::PostRASchedulerPass Pass(rcc::riscv::schedulingModel(),
{.MaximumRegionSize = 64, .EnableBidirectionalScheduling = false});
auto Result = Pass.run(*Fixture.Function, Target, Analyses);
diff --git a/backend/unittests/CodeGen/PHIEliminationTest.cpp b/backend/unittests/CodeGen/PHIEliminationTest.cpp
index 00668e4..9bc9a57 100644
--- a/backend/unittests/CodeGen/PHIEliminationTest.cpp
+++ b/backend/unittests/CodeGen/PHIEliminationTest.cpp
@@ -4,7 +4,7 @@
#include "SSAExitTestSupport.h"
#include "rcc/CodeGen/MachineCFGUpdater.h"
-#include "rcc/CodeGen/MachinePass.h"
+#include "rcc/CodeGen/MachinePassManager.h"
#include "rcc/CodeGen/MachineVerifier.h"
#include <array>
@@ -62,11 +62,11 @@ buildPhi(rcc::MachineFunction &Function, const rcc::TargetDescription &Target,
[[nodiscard]] rcc::Expected<void> runPHIElimination(rcc::MachineFunction &Function,
const rcc::TargetDescription &Target,
const rcc::TargetInstrInfo &Info) {
- rcc::MachinePassManager Pipeline;
+ rcc::MachineFunctionPassManager Pipeline;
auto Added = Pipeline.addPass(std::make_unique<rcc::PHIEliminationPass>(Info));
if (!Added)
return std::unexpected(Added.error());
- rcc::MachineAnalysisManager Analyses;
+ rcc::MachineFunctionAnalysisManager Analyses;
return Pipeline.run(Function, Target, Analyses);
}
@@ -391,7 +391,7 @@ void testUndefAndFailureAtomicity(TestContext &Context, const rcc::TargetDescrip
const std::size_t RegistersBefore = Atomic.Function->registerInfo().virtualRegisters().size();
const std::uint64_t PropertiesBefore = Atomic.Function->properties().rawValue();
TestInstrInfo RejectingInfo(true);
- rcc::MachineAnalysisManager Analyses;
+ rcc::MachineFunctionAnalysisManager Analyses;
rcc::PHIEliminationPass FailingPass(RejectingInfo);
auto Failed = FailingPass.run(*Atomic.Function, Target, Analyses);
Context.expect(!Failed && Failed.error().code() == rcc::ErrorCode::Unsupported,
diff --git a/backend/unittests/CodeGen/PassInstrumentationTest.cpp b/backend/unittests/CodeGen/PassInstrumentationTest.cpp
new file mode 100644
index 0000000..97ce445
--- /dev/null
+++ b/backend/unittests/CodeGen/PassInstrumentationTest.cpp
@@ -0,0 +1,173 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "Target/TestTarget.h"
+#include "rcc/CodeGen/GenericOpcodes.h"
+#include "rcc/CodeGen/MachineFunction.h"
+#include "rcc/CodeGen/MachinePassManager.h"
+
+#include <cstddef>
+#include <cstdint>
+#include <iostream>
+#include <memory>
+#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';
+ ++Failures;
+ }
+
+ [[nodiscard]] int result() const noexcept {
+ return Failures == 0 ? 0 : 1;
+ }
+
+private:
+ int Failures = 0;
+};
+
+[[nodiscard]] rcc::Expected<rcc::TargetDescription> createTarget() {
+ std::vector<rcc::InstructionDescriptor> Descriptors;
+ std::vector<rcc::ImmediatePredicate> Predicates;
+ auto Appended = rcc::appendGenericInstructionInfo(Descriptors, Predicates);
+ if (!Appended)
+ return std::unexpected(Appended.error());
+ auto Instructions = rcc::InstructionSet::create(std::move(Descriptors), std::move(Predicates));
+ if (!Instructions)
+ return std::unexpected(Instructions.error());
+ return rcc::test::createTestTargetDescription(std::move(*Instructions));
+}
+
+class BlockCountAnalysis final {
+public:
+ struct Result final {
+ std::size_t Count = 0;
+ };
+
+ [[nodiscard]] static constexpr std::string_view name() noexcept {
+ return "BlockCount";
+ }
+
+ [[nodiscard]] static rcc::Expected<Result> run(rcc::MachineFunction &Function,
+ const rcc::TargetDescription &,
+ rcc::MachineFunctionAnalysisManager &) {
+ return Result{Function.blockCount()};
+ }
+};
+
+class InstrumentedPass final : public rcc::MachineFunctionPass {
+public:
+ [[nodiscard]] const rcc::MachinePassContract &contract() const noexcept override {
+ static const rcc::MachinePassContract Contract{
+ .Name = "Instrumented",
+ .Requires = {},
+ .Sets = {},
+ .Clears = {},
+ .RequiredAnalyses =
+ {
+ rcc::MachineFunctionAnalysisManager::id<BlockCountAnalysis>(),
+ },
+ .VerificationStage = std::nullopt,
+ };
+ return Contract;
+ }
+
+ [[nodiscard]] rcc::Expected<rcc::MachinePassResult>
+ run(rcc::MachineFunction &Function, const rcc::TargetDescription &Target,
+ rcc::MachineFunctionAnalysisManager &Analyses) override {
+ auto First = Analyses.getResult<BlockCountAnalysis>(Function, Target);
+ auto Second = Analyses.getResult<BlockCountAnalysis>(Function, Target);
+ auto FirstAction = Analyses.requestAction("first", Function);
+ auto SecondAction = Analyses.requestAction("second", Function);
+ if (!First || !Second || !FirstAction || !SecondAction || !*FirstAction || *SecondAction)
+ return rcc::makeUnexpected(rcc::ErrorCode::VerificationError,
+ "instrumentation action policy was not enforced");
+ rcc::MachinePassResult Result;
+ Result.Preserved = rcc::PreservedAnalyses::all();
+ return Result;
+ }
+};
+
+void testInstrumentation(TestContext &Context, const rcc::TargetDescription &Target) {
+ auto Function =
+ rcc::MachineFunction::create("instrumentation", rcc::MachineFunctionStableID::fromValue(1));
+ Context.expect(Function.has_value(), "instrumentation function creation must succeed");
+ if (!Function)
+ return;
+
+ rcc::MachineFunctionAnalysisManager Analyses;
+ Context.expect(Analyses
+ .registerAnalysis<BlockCountAnalysis>({
+ .Name = "BlockCount",
+ .Dependencies = {},
+ .InvalidatedBy = rcc::MachinePassChanges::all(),
+ .ThreadSafety = rcc::PassThreadSafety::ThreadSafe,
+ })
+ .has_value(),
+ "instrumentation analysis registration must succeed");
+
+ rcc::MachinePassInstrumentation Instrumentation;
+ Instrumentation.setPerFunctionFuel(1);
+ std::uint32_t BeforePass = 0;
+ std::uint32_t AfterPass = 0;
+ std::uint32_t BeforeAnalysis = 0;
+ std::uint32_t AfterAnalysis = 0;
+ std::uint32_t CacheHits = 0;
+ std::uint32_t Printed = 0;
+ std::uint32_t Changes = 0;
+ Instrumentation.addBeforeCallback(
+ [&BeforePass](std::string_view, const rcc::MachineFunction &) { ++BeforePass; });
+ Instrumentation.addAfterCallback([&AfterPass](std::string_view, const rcc::MachineFunction &,
+ const rcc::MachinePassResult &) { ++AfterPass; });
+ Instrumentation.addBeforeAnalysisCallback(
+ [&BeforeAnalysis](std::string_view, const rcc::MachineFunction &) { ++BeforeAnalysis; });
+ Instrumentation.addAfterAnalysisCallback(
+ [&AfterAnalysis, &CacheHits](std::string_view, const rcc::MachineFunction &, bool CacheHit,
+ std::uint64_t) {
+ ++AfterAnalysis;
+ CacheHits += CacheHit ? 1U : 0U;
+ });
+ Instrumentation.addIRPrintCallback([&Printed](rcc::InstrumentationPhase, std::string_view,
+ const rcc::MachineFunction &) { ++Printed; });
+ Instrumentation.addChangeCallback([&Changes](std::string_view, const rcc::MachineFunction &,
+ rcc::MachinePassChanges ReportedChanges,
+ bool FingerprintChanged) {
+ if (ReportedChanges.empty() && !FingerprintChanged)
+ ++Changes;
+ });
+
+ rcc::MachineFunctionPassManager Pipeline;
+ Context.expect(Pipeline.addPass(std::make_unique<InstrumentedPass>()).has_value() &&
+ Pipeline.run(**Function, Target, Analyses, &Instrumentation).has_value(),
+ "instrumented pass must run");
+ Context.expect(BeforePass == 1 && AfterPass == 1 && BeforeAnalysis == 1 && AfterAnalysis == 3 &&
+ CacheHits == 2 && Printed == 2 && Changes == 1,
+ "all pass and analysis instrumentation points must run");
+
+ const auto Events = Instrumentation.actionEvents();
+ Context.expect(Events.size() == 2 && Events[0].Accepted && !Events[1].Accepted,
+ "action instrumentation must record accepted and rejected decisions");
+ const auto Statistics = Instrumentation.statistics();
+ Context.expect(Statistics.size() == 2,
+ "instrumentation must collect one pass and one analysis statistic");
+}
+
+} // namespace
+
+int main() {
+ TestContext Context;
+ auto Target = createTarget();
+ Context.expect(Target.has_value(),
+ "pass instrumentation tests require a valid target description");
+ if (!Target)
+ return Context.result();
+
+ testInstrumentation(Context, *Target);
+ return Context.result();
+}
diff --git a/backend/unittests/CodeGen/PassManagerTest.cpp b/backend/unittests/CodeGen/PassManagerTest.cpp
new file mode 100644
index 0000000..56c30f3
--- /dev/null
+++ b/backend/unittests/CodeGen/PassManagerTest.cpp
@@ -0,0 +1,260 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "Target/TestTarget.h"
+#include "rcc/CodeGen/GenericOpcodes.h"
+#include "rcc/CodeGen/MachineFunction.h"
+#include "rcc/CodeGen/MachineFunctionAnalysisManager.h"
+
+#include <atomic>
+#include <cstddef>
+#include <cstdint>
+#include <iostream>
+#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';
+ ++Failures;
+ }
+
+ [[nodiscard]] int result() const noexcept {
+ return Failures == 0 ? 0 : 1;
+ }
+
+private:
+ int Failures = 0;
+};
+
+[[nodiscard]] rcc::Expected<rcc::TargetDescription> createTarget() {
+ std::vector<rcc::InstructionDescriptor> Descriptors;
+ std::vector<rcc::ImmediatePredicate> Predicates;
+ auto Appended = rcc::appendGenericInstructionInfo(Descriptors, Predicates);
+ if (!Appended)
+ return std::unexpected(Appended.error());
+ auto Instructions = rcc::InstructionSet::create(std::move(Descriptors), std::move(Predicates));
+ if (!Instructions)
+ return std::unexpected(Instructions.error());
+ return rcc::test::createTestTargetDescription(std::move(*Instructions));
+}
+
+class BlockCountAnalysis final {
+public:
+ struct Result final {
+ std::size_t Count = 0;
+ };
+
+ [[nodiscard]] static constexpr std::string_view name() noexcept {
+ return "BlockCount";
+ }
+
+ [[nodiscard]] static rcc::Expected<Result> run(rcc::MachineFunction &Function,
+ const rcc::TargetDescription &,
+ rcc::MachineFunctionAnalysisManager &) {
+ ++Runs;
+ return Result{Function.blockCount()};
+ }
+
+ static inline std::atomic<std::uint32_t> Runs = 0;
+};
+
+class DependentAnalysis final {
+public:
+ struct Result final {
+ std::size_t Count = 0;
+ };
+
+ [[nodiscard]] static constexpr std::string_view name() noexcept {
+ return "Dependent";
+ }
+
+ [[nodiscard]] static rcc::Expected<Result> run(rcc::MachineFunction &Function,
+ const rcc::TargetDescription &Target,
+ rcc::MachineFunctionAnalysisManager &Analyses) {
+ auto Blocks = Analyses.getResult<BlockCountAnalysis>(Function, Target);
+ if (!Blocks)
+ return std::unexpected(Blocks.error());
+ return Result{(*Blocks)->Count};
+ }
+};
+
+class StickyAnalysis final {
+public:
+ struct Result final {
+ [[nodiscard]] bool invalidate(const rcc::MachineFunction &,
+ const rcc::PreservedAnalyses &) noexcept {
+ return false;
+ }
+ };
+
+ [[nodiscard]] static constexpr std::string_view name() noexcept {
+ return "Sticky";
+ }
+
+ [[nodiscard]] static rcc::Expected<Result> run(rcc::MachineFunction &,
+ const rcc::TargetDescription &,
+ rcc::MachineFunctionAnalysisManager &) {
+ return Result{};
+ }
+};
+
+class ExpiringAnalysis final {
+public:
+ struct Result final {
+ [[nodiscard]] bool invalidate(const rcc::MachineFunction &,
+ const rcc::PreservedAnalyses &) noexcept {
+ return true;
+ }
+ };
+
+ [[nodiscard]] static constexpr std::string_view name() noexcept {
+ return "Expiring";
+ }
+
+ [[nodiscard]] static rcc::Expected<Result> run(rcc::MachineFunction &,
+ const rcc::TargetDescription &,
+ rcc::MachineFunctionAnalysisManager &) {
+ return Result{};
+ }
+};
+
+class CycleA final {
+public:
+ struct Result final {};
+
+ [[nodiscard]] static constexpr std::string_view name() noexcept {
+ return "CycleA";
+ }
+
+ [[nodiscard]] static rcc::Expected<Result> run(rcc::MachineFunction &,
+ const rcc::TargetDescription &,
+ rcc::MachineFunctionAnalysisManager &) {
+ return Result{};
+ }
+};
+
+class CycleB final {
+public:
+ struct Result final {};
+
+ [[nodiscard]] static constexpr std::string_view name() noexcept {
+ return "CycleB";
+ }
+
+ [[nodiscard]] static rcc::Expected<Result> run(rcc::MachineFunction &,
+ const rcc::TargetDescription &,
+ rcc::MachineFunctionAnalysisManager &) {
+ return Result{};
+ }
+};
+
+void testAnalysisManager(TestContext &Context, const rcc::TargetDescription &Target) {
+ auto Function =
+ rcc::MachineFunction::create("analysis", rcc::MachineFunctionStableID::fromValue(1));
+ Context.expect(Function.has_value(), "analysis function creation must succeed");
+ if (!Function)
+ return;
+
+ rcc::MachineFunctionAnalysisManager Analyses;
+ Context.expect(Analyses
+ .registerAnalysis<BlockCountAnalysis>({
+ .Name = "BlockCount",
+ .Dependencies = {},
+ .InvalidatedBy = {rcc::MachinePassChange::CFG},
+ })
+ .has_value(),
+ "base analysis registration must succeed");
+ Context.expect(Analyses
+ .registerAnalysis<DependentAnalysis>({
+ .Name = "Dependent",
+ .Dependencies =
+ {
+ rcc::MachineFunctionAnalysisManager::id<BlockCountAnalysis>(),
+ },
+ .InvalidatedBy = {rcc::MachinePassChange::Operands},
+ })
+ .has_value(),
+ "dependent analysis registration must succeed");
+
+ BlockCountAnalysis::Runs = 0;
+ auto First = Analyses.getResult<DependentAnalysis>(**Function, Target);
+ auto Second = Analyses.getResult<DependentAnalysis>(**Function, Target);
+ Context.expect(First && Second && *First == *Second && BlockCountAnalysis::Runs == 1,
+ "analysis dependencies must materialize once and then use the cache");
+
+ Analyses.invalidate(**Function, rcc::PreservedAnalyses::none(),
+ {rcc::MachinePassChange::Registers});
+ Context.expect(Analyses.cachedResultCount(**Function) == 2,
+ "unrelated changes must preserve cached analyses precisely");
+ Analyses.invalidate(**Function, rcc::PreservedAnalyses::none(), {rcc::MachinePassChange::CFG});
+ Context.expect(Analyses.cachedResultCount(**Function) == 0,
+ "invalidating a dependency must invalidate its dependent analysis");
+
+ rcc::MachineFunctionAnalysisManager Cyclic;
+ Context.expect(
+ Cyclic.registerAnalysis<CycleA>({
+ .Name = "CycleA",
+ .Dependencies =
+ {
+ rcc::MachineFunctionAnalysisManager::id<CycleB>(),
+ },
+ })
+ .has_value() &&
+ Cyclic
+ .registerAnalysis<CycleB>({
+ .Name = "CycleB",
+ .Dependencies =
+ {
+ rcc::MachineFunctionAnalysisManager::id<CycleA>(),
+ },
+ })
+ .has_value(),
+ "cyclic analysis registrations must be accepted until requested");
+ Context.expect(!Cyclic.getResult<CycleA>(**Function, Target),
+ "analysis dependency cycles must produce a structured error");
+
+ rcc::MachineFunctionAnalysisManager CustomInvalidation;
+ Context.expect(CustomInvalidation
+ .registerAnalysis<StickyAnalysis>({
+ .Name = "Sticky",
+ .Dependencies = {},
+ .InvalidatedBy = {rcc::MachinePassChange::CFG},
+ })
+ .has_value() &&
+ CustomInvalidation
+ .registerAnalysis<ExpiringAnalysis>({
+ .Name = "Expiring",
+ .Dependencies = {},
+ .InvalidatedBy = {rcc::MachinePassChange::CFG},
+ })
+ .has_value() &&
+ CustomInvalidation.getResult<StickyAnalysis>(**Function, Target) &&
+ CustomInvalidation.getResult<ExpiringAnalysis>(**Function, Target),
+ "custom invalidation analyses must materialize");
+ CustomInvalidation.invalidate(**Function, rcc::PreservedAnalyses::none(),
+ {rcc::MachinePassChange::CFG});
+ Context.expect(CustomInvalidation.hasCached(
+ **Function, rcc::MachineFunctionAnalysisManager::id<StickyAnalysis>()) &&
+ !CustomInvalidation.hasCached(
+ **Function, rcc::MachineFunctionAnalysisManager::id<ExpiringAnalysis>()),
+ "analysis invalidate() must return false to preserve and true to invalidate");
+}
+
+} // namespace
+
+int main() {
+ TestContext Context;
+ auto Target = createTarget();
+ Context.expect(Target.has_value(), "pass manager tests require a valid target description");
+ if (!Target)
+ return Context.result();
+
+ testAnalysisManager(Context, *Target);
+ return Context.result();
+}
diff --git a/backend/unittests/CodeGen/RegisterAllocationTest.cpp b/backend/unittests/CodeGen/RegisterAllocationTest.cpp
index 1a7c5c7..7dd0dd0 100644
--- a/backend/unittests/CodeGen/RegisterAllocationTest.cpp
+++ b/backend/unittests/CodeGen/RegisterAllocationTest.cpp
@@ -14,7 +14,7 @@
#include "rcc/CodeGen/MachineInstr.h"
#include "rcc/CodeGen/MachineMemOperand.h"
#include "rcc/CodeGen/MachineOperand.h"
-#include "rcc/CodeGen/MachinePass.h"
+#include "rcc/CodeGen/MachinePassManager.h"
#include "rcc/CodeGen/MachineRegisterInfo.h"
#include "rcc/CodeGen/RegAllocCommon.h"
#include "rcc/CodeGen/RegAllocFast.h"
@@ -496,7 +496,7 @@ enum class AllocatorKind : std::uint8_t {
const rcc::TargetRegisterAllocationInfo &Info,
AllocatorKind Kind,
rcc::RegisterAllocationOptions Options = {}) {
- rcc::MachineAnalysisManager Analyses;
+ rcc::MachineFunctionAnalysisManager Analyses;
auto Registered = rcc::registerStage11MachineAnalyses(Analyses);
if (!Registered)
return std::unexpected(Registered.error());
@@ -512,7 +512,7 @@ enum class AllocatorKind : std::uint8_t {
Pass = std::make_unique<rcc::PBQPAllocatorPass>(Info, Options);
break;
}
- rcc::MachinePassManager Pipeline;
+ rcc::MachineFunctionPassManager Pipeline;
auto Added = Pipeline.addPass(std::move(Pass));
if (!Added)
return std::unexpected(Added.error());
@@ -603,7 +603,7 @@ void testLiveRegMatrix(TestContext &Context, const rcc::TargetDescription &Targe
}
if (!rcc::rebuildMachineLiveness(*LaneFixture->Function, Target))
return;
- rcc::MachineAnalysisManager LaneAnalyses;
+ rcc::MachineFunctionAnalysisManager LaneAnalyses;
if (!rcc::registerStage11MachineAnalyses(LaneAnalyses))
return;
auto LaneIntervals =
@@ -632,7 +632,7 @@ void testLiveRegMatrix(TestContext &Context, const rcc::TargetDescription &Targe
}
if (!rcc::rebuildMachineLiveness(*FixedFixture->Function, Target))
return;
- rcc::MachineAnalysisManager FixedAnalyses;
+ rcc::MachineFunctionAnalysisManager FixedAnalyses;
if (!rcc::registerStage11MachineAnalyses(FixedAnalyses))
return;
auto FixedIntervals =
@@ -664,7 +664,7 @@ void testLiveRegMatrix(TestContext &Context, const rcc::TargetDescription &Targe
!rcc::rebuildMachineLiveness(*FixedLaneFixture->Function, Target)) {
return;
}
- rcc::MachineAnalysisManager FixedLaneAnalyses;
+ rcc::MachineFunctionAnalysisManager FixedLaneAnalyses;
if (!rcc::registerStage11MachineAnalyses(FixedLaneAnalyses))
return;
auto FixedLaneIntervals =
@@ -694,7 +694,7 @@ void testSharedUnitLaneMatrix(TestContext &Context, const rcc::TargetDescription
!rcc::rebuildMachineLiveness(*Fixture->Function, Target)) {
return;
}
- rcc::MachineAnalysisManager Analyses;
+ rcc::MachineFunctionAnalysisManager Analyses;
if (!rcc::registerStage11MachineAnalyses(Analyses))
return;
auto Intervals = Analyses.getResult<rcc::LiveIntervalsAnalysis>(*Fixture->Function, Target);
diff --git a/backend/unittests/CodeGen/RegisterAllocationTestSupport.h b/backend/unittests/CodeGen/RegisterAllocationTestSupport.h
index 49c9e8a..d93b14e 100644
--- a/backend/unittests/CodeGen/RegisterAllocationTestSupport.h
+++ b/backend/unittests/CodeGen/RegisterAllocationTestSupport.h
@@ -436,7 +436,7 @@ public:
const TargetRegisterAllocationInfo &Info,
AllocatorKind Kind,
RegisterAllocationOptions Options = {}) {
- MachineAnalysisManager Analyses;
+ MachineFunctionAnalysisManager Analyses;
auto Registered = registerStage11MachineAnalyses(Analyses);
if (!Registered)
return std::unexpected(Registered.error());
@@ -452,7 +452,7 @@ public:
Pass = std::make_unique<PBQPAllocatorPass>(Info, Options);
break;
}
- MachinePassManager Pipeline;
+ MachineFunctionPassManager Pipeline;
auto Added = Pipeline.addPass(std::move(Pass));
if (!Added)
return std::unexpected(Added.error());
diff --git a/backend/unittests/CodeGen/SlotIndexesTest.cpp b/backend/unittests/CodeGen/SlotIndexesTest.cpp
index d34ff84..9d3f54b 100644
--- a/backend/unittests/CodeGen/SlotIndexesTest.cpp
+++ b/backend/unittests/CodeGen/SlotIndexesTest.cpp
@@ -25,7 +25,7 @@ void testSlotIndexes(TestContext &Context, const rcc::TargetDescription &Target)
return;
}
- rcc::MachineAnalysisManager Analyses;
+ rcc::MachineFunctionAnalysisManager Analyses;
auto Registered = rcc::registerStage11MachineAnalyses(Analyses);
auto Indexes =
Registered
diff --git a/backend/unittests/CodeGen/TwoAddressInstructionPassTest.cpp b/backend/unittests/CodeGen/TwoAddressInstructionPassTest.cpp
index 6853a36..e3ad4eb 100644
--- a/backend/unittests/CodeGen/TwoAddressInstructionPassTest.cpp
+++ b/backend/unittests/CodeGen/TwoAddressInstructionPassTest.cpp
@@ -3,7 +3,7 @@
#include "rcc/CodeGen/TwoAddressInstructionPass.h"
#include "SSAExitTestSupport.h"
-#include "rcc/CodeGen/MachinePass.h"
+#include "rcc/CodeGen/MachinePassManager.h"
#include "rcc/CodeGen/MachineVerifier.h"
#include <array>
@@ -130,11 +130,11 @@ void testTwoAddressAndSubregisterPseudos(TestContext &Context, const rcc::Target
if (!InitialLiveness)
return;
Function.properties().set(rcc::MachineFunctionProperty::NoPHIs);
- rcc::MachinePassManager Pipeline;
+ rcc::MachineFunctionPassManager Pipeline;
Context.expect(
Pipeline.addPass(std::make_unique<rcc::TwoAddressInstructionPass>(Info)).has_value(),
"two-address pass must be registered");
- rcc::MachineAnalysisManager Analyses;
+ rcc::MachineFunctionAnalysisManager Analyses;
auto Run = Pipeline.run(Function, Target, Analyses);
Context.expect(Run.has_value(), "two-address rewrite must succeed");
if (!Run)
@@ -238,7 +238,7 @@ void testTwoAddressAndSubregisterPseudos(TestContext &Context, const rcc::Target
const std::uint64_t AtomicPropertiesBefore = AtomicFixture.Function->properties().rawValue();
TestInstrInfo RejectingInfo(true);
- rcc::MachineAnalysisManager AtomicAnalyses;
+ rcc::MachineFunctionAnalysisManager AtomicAnalyses;
rcc::TwoAddressInstructionPass FailingPass(RejectingInfo);
auto Failed = FailingPass.run(*AtomicFixture.Function, Target, AtomicAnalyses);
Context.expect(!Failed && Failed.error().code() == rcc::ErrorCode::Unsupported,