From 236082a0fe4dcd0362c5c74923a93e3db6a01291 Mon Sep 17 00:00:00 2001 From: Richard-Qin-X Date: Thu, 13 Aug 2026 05:45:37 +0800 Subject: [CodeGen] Align pass manager ownership --- backend/unittests/CodeGen/CMakeLists.txt | 41 +- .../CodeGen/GlobalISel/InstructionSelectTest.cpp | 14 +- .../unittests/CodeGen/GlobalISel/LegalizerTest.cpp | 10 +- .../CodeGen/GlobalISel/RegBankSelectTest.cpp | 4 +- backend/unittests/CodeGen/LiveIntervalTest.cpp | 2 +- backend/unittests/CodeGen/LiveIntervalsTest.cpp | 8 +- backend/unittests/CodeGen/LivePhysRegsTest.cpp | 2 +- backend/unittests/CodeGen/LiveRegMatrixTest.cpp | 4 +- backend/unittests/CodeGen/LiveVariablesTest.cpp | 26 +- backend/unittests/CodeGen/MachineAnalysesTest.cpp | 24 +- .../unittests/CodeGen/MachineOptimizationTest.cpp | 20 +- .../unittests/CodeGen/MachinePassManagerTest.cpp | 426 ++++++++++ backend/unittests/CodeGen/MachinePassTest.cpp | 918 --------------------- .../unittests/CodeGen/MachineSchedulingTest.cpp | 8 +- backend/unittests/CodeGen/PHIEliminationTest.cpp | 8 +- .../unittests/CodeGen/PassInstrumentationTest.cpp | 173 ++++ backend/unittests/CodeGen/PassManagerTest.cpp | 260 ++++++ .../unittests/CodeGen/RegisterAllocationTest.cpp | 14 +- .../CodeGen/RegisterAllocationTestSupport.h | 4 +- backend/unittests/CodeGen/SlotIndexesTest.cpp | 2 +- .../CodeGen/TwoAddressInstructionPassTest.cpp | 8 +- 21 files changed, 975 insertions(+), 1001 deletions(-) create mode 100644 backend/unittests/CodeGen/MachinePassManagerTest.cpp delete mode 100644 backend/unittests/CodeGen/MachinePassTest.cpp create mode 100644 backend/unittests/CodeGen/PassInstrumentationTest.cpp create mode 100644 backend/unittests/CodeGen/PassManagerTest.cpp (limited to 'backend/unittests/CodeGen') 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(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(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(Subtarget.getLegalizerInfo())) || !Pipeline.addPass(std::make_unique(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(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(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(*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(Function, Target) : rcc::Expected{ @@ -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(*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(*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(*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(*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::MachineAnalysisManager Analyses; + rcc::MachineFunctionAnalysisManager Analyses; auto Run = Added ? Pipeline.run(Function, Target, Analyses) : rcc::Expected{std::unexpected(Added.error())}; if (!Run) @@ -169,8 +169,9 @@ class DummyAnalysis final { public: struct Result final {}; - [[nodiscard]] static rcc::Expected - run(rcc::MachineFunction &, const rcc::TargetDescription &, rcc::MachineAnalysisManager &) { + [[nodiscard]] static rcc::Expected 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::MachineFunctionAnalysisManager::id()), "operand changes must retain SlotIndexes while invalidating liveness dependents"); auto Recomputed = Analyses.getResult(*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::MachineFunctionAnalysisManager::id()), "layout changes must invalidate SlotIndexes and LiveIntervals only"); - rcc::MachineAnalysisManager Atomic; + rcc::MachineFunctionAnalysisManager Atomic; rcc::MachineAnalysisRegistration DummyRegistration; DummyRegistration.Name = "SlotIndexes"; auto Dummy = Atomic.registerAnalysis(std::move(DummyRegistration)); auto Rejected = rcc::registerStage11MachineAnalyses(Atomic); Context.expect( - Dummy && !Rejected && Atomic.isRegistered(rcc::MachineAnalysisManager::id()) && - !Atomic.isRegistered(rcc::MachineAnalysisManager::id()) && - !Atomic.isRegistered(rcc::MachineAnalysisManager::id()), + Dummy && !Rejected && + Atomic.isRegistered(rcc::MachineFunctionAnalysisManager::id()) && + !Atomic.isRegistered( + rcc::MachineFunctionAnalysisManager::id()) && + !Atomic.isRegistered( + rcc::MachineFunctionAnalysisManager::id()), "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(**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::MachineFunctionAnalysisManager::id()), "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(**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 - run(rcc::MachineFunction &, const rcc::TargetDescription &, rcc::MachineAnalysisManager &) { + [[nodiscard]] static rcc::Expected run(rcc::MachineFunction &, + const rcc::TargetDescription &, + rcc::MachineFunctionAnalysisManager &) { return Result{}; } }; void testAtomicStandardRegistration(TestContext &Context) { - rcc::MachineAnalysisManager Analyses; + rcc::MachineFunctionAnalysisManager Analyses; Context.expect(Analyses .registerAnalysis({ .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()), - "failed standard-analysis registration must not publish a partial set"); + Context.expect( + !rcc::registerStage8MachineAnalyses(Analyses) && + !Analyses.isRegistered( + rcc::MachineFunctionAnalysisManager::id()), + "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 +#include +#include +#include +#include +#include +#include +#include +#include + +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 createTarget() { + std::vector Descriptors; + std::vector 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 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 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()}, + .VerificationStage = std::nullopt, + }; + return Contract; + } + + [[nodiscard]] rcc::Expected + run(rcc::MachineFunction &Function, const rcc::TargetDescription &Target, + rcc::MachineFunctionAnalysisManager &Analyses) override { + auto Blocks = Analyses.getResult(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 + 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()}, + .VerificationStage = std::nullopt, + }; + return Contract; + } + + [[nodiscard]] rcc::Expected + 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 &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 + run(rcc::MachineFunction &, const rcc::TargetDescription &, + rcc::MachineFunctionAnalysisManager &) override { + Count->fetch_add(1, std::memory_order_relaxed); + return rcc::MachinePassResult{}; + } + +private: + std::atomic *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(), + }, + .VerificationStage = std::nullopt, + }; + Value.ThreadSafety = rcc::PassThreadSafety::ThreadSafe; + return Value; + }(); + return Contract; + } + + [[nodiscard]] rcc::Expected + 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 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()}, + .Parameters = {}, + .DeclaredChanges = {}, + }; + return Contract; + } + + [[nodiscard]] rcc::Expected + run(rcc::CompilationUnit &Unit, const rcc::TargetDescription &Target, + rcc::CompilationUnitAnalysisManager &Analyses, + rcc::MachineFunctionAnalysisManager &) override { + auto Count = Analyses.getResult(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()}, + .Parameters = {}, + .DeclaredChanges = {rcc::MachinePassChange::BlockLayout}, + }; + return Contract; + } + + [[nodiscard]] rcc::Expected + 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({ + .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()).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()).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({ + .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()).has_value() && + !FailingPipeline.run(**FailedFunction, Target, FailureAnalyses) && + !FailureAnalyses.hasCached(**FailedFunction, + rcc::MachineFunctionAnalysisManager::id()), + "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 Runs = 0; + rcc::MachineFunctionPassManager Parallel; + Context.expect(Parallel.addPass(std::make_unique(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({ + .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()).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({ + .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()).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({ + .Name = "BlockCount", + .Dependencies = {}, + .InvalidatedBy = rcc::MachinePassChanges::all(), + }) + .has_value() && + FunctionAnalyses.getResult(*FirstFunction, Target).has_value(), + "compilation-unit failure cache fixtures must materialize"); + rcc::CompilationUnitPassManager FailingUnitPipeline; + Context.expect( + FirstFunction != nullptr && + FailingUnitPipeline.addPass(std::make_unique()).has_value() && + !FailingUnitPipeline.run(Unit, Target, UnitAnalyses, FunctionAnalyses) && + !UnitAnalyses.hasCached( + Unit, rcc::CompilationUnitAnalysisManager::id()) && + !FunctionAnalyses.hasCached( + *FirstFunction, rcc::MachineFunctionAnalysisManager::id()), + "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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -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 createTarget() { - std::vector Descriptors; - std::vector 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 run(rcc::MachineFunction &Function, - const rcc::TargetDescription &, - rcc::MachineAnalysisManager &) { - ++Runs; - return Result{Function.blockCount()}; - } - - static inline std::atomic 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 run(rcc::MachineFunction &Function, - const rcc::TargetDescription &Target, - rcc::MachineAnalysisManager &Analyses) { - auto Blocks = Analyses.getResult(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 - 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 - 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 - 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 - 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 - 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 - 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()}, - .VerificationStage = std::nullopt, - }; - return Contract; - } - - [[nodiscard]] rcc::Expected - run(rcc::MachineFunction &Function, const rcc::TargetDescription &Target, - rcc::MachineAnalysisManager &Analyses) override { - auto Dependent = Analyses.getResult(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 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()}, - .VerificationStage = std::nullopt, - }; - return Contract; - } - - [[nodiscard]] rcc::Expected 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()}, - .VerificationStage = std::nullopt, - }; - return Contract; - } - - [[nodiscard]] rcc::Expected 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 &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 run(rcc::MachineFunction &, - const rcc::TargetDescription &, - rcc::MachineAnalysisManager &) override { - Count->fetch_add(1, std::memory_order_relaxed); - return rcc::MachinePassResult{}; - } - -private: - std::atomic *Count; -}; - -class InstrumentedParallelPass final : public rcc::MachineFunctionPass { -public: - InstrumentedParallelPass(std::atomic &Runs, std::atomic &Active, - std::atomic &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 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 *Runs; - std::atomic *Active; - std::atomic *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(), - }, - .VerificationStage = std::nullopt, - }; - Value.ThreadSafety = rcc::PassThreadSafety::ThreadSafe; - return Value; - }(); - return Contract; - } - - [[nodiscard]] rcc::Expected 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 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 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(), - }, - .Parameters = {}, - .DeclaredChanges = {}, - }; - return Contract; - } - - [[nodiscard]] rcc::Expected - 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()}, - .Parameters = {}, - .DeclaredChanges = {}, - }; - return Contract; - } - - [[nodiscard]] rcc::Expected - run(rcc::CompilationUnit &Unit, const rcc::TargetDescription &Target, - rcc::CompilationUnitAnalysisManager &Analyses, rcc::MachineAnalysisManager &) override { - auto Count = Analyses.getResult(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()}, - .Parameters = {}, - .DeclaredChanges = {rcc::MachinePassChange::BlockLayout}, - }; - return Contract; - } - - [[nodiscard]] rcc::Expected - 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({ - .Name = "BlockCount", - .Dependencies = {}, - .InvalidatedBy = {rcc::MachinePassChange::CFG}, - }) - .has_value(), - "base analysis registration must succeed"); - Context.expect(Analyses - .registerAnalysis({ - .Name = "Dependent", - .Dependencies = {rcc::MachineAnalysisManager::id()}, - .InvalidatedBy = {rcc::MachinePassChange::Operands}, - }) - .has_value(), - "dependent analysis registration must succeed"); - - BlockCountAnalysis::Runs = 0; - auto First = Analyses.getResult(**Function, Target); - auto Second = Analyses.getResult(**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({ - .Name = "CycleA", - .Dependencies = {rcc::MachineAnalysisManager::id()}, - }) - .has_value() && - Cyclic - .registerAnalysis({ - .Name = "CycleB", - .Dependencies = {rcc::MachineAnalysisManager::id()}, - }) - .has_value(), - "cyclic analysis registrations must be accepted until requested"); - Context.expect(!Cyclic.getResult(**Function, Target), - "analysis dependency cycles must produce a structured error"); - - rcc::MachineAnalysisManager CustomInvalidation; - Context.expect(CustomInvalidation - .registerAnalysis({ - .Name = "Sticky", - .Dependencies = {}, - .InvalidatedBy = {rcc::MachinePassChange::CFG}, - }) - .has_value() && - CustomInvalidation - .registerAnalysis({ - .Name = "Expiring", - .Dependencies = {}, - .InvalidatedBy = {rcc::MachinePassChange::CFG}, - }) - .has_value() && - CustomInvalidation.getResult(**Function, Target) && - CustomInvalidation.getResult(**Function, Target), - "custom invalidation analyses must materialize"); - CustomInvalidation.invalidate(**Function, rcc::PreservedAnalyses::none(), - {rcc::MachinePassChange::CFG}); - Context.expect( - CustomInvalidation.hasCached(**Function, rcc::MachineAnalysisManager::id()) && - !CustomInvalidation.hasCached(**Function, - rcc::MachineAnalysisManager::id()), - "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({ - .Name = "BlockCount", - .Dependencies = {}, - .InvalidatedBy = rcc::MachinePassChanges::all(), - .ThreadSafety = rcc::PassThreadSafety::ThreadSafe, - }) - .has_value() && - Analyses - .registerAnalysis({ - .Name = "Dependent", - .Dependencies = {rcc::MachineAnalysisManager::id()}, - }) - .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()).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()).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({ - .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()).has_value() && - !FailingPipeline.run(**FailedFunction, Target, FailureAnalyses) && - !FailureAnalyses.hasCached( - **FailedFunction, rcc::MachineAnalysisManager::id()), - "a pass that mutates and fails must not leave stale function analyses cached"); - - rcc::MachineAnalysisManager FailingAnalyses; - Context.expect(FailingAnalyses - .registerAnalysis({ - .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()) - .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 Runs = 0; - rcc::MachinePassManager Parallel; - Context.expect(Parallel.addPass(std::make_unique(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 DuplicateRuns = 0; - rcc::MachinePassManager Repeated; - Context.expect( - Repeated.addPass(std::make_unique(DuplicateRuns)).has_value() && - Repeated.addPass(std::make_unique(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 ObservedFunctions; - std::vector BeforeBlockCounts; - std::vector 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 InstrumentedRuns = 0; - std::atomic ActiveRuns = 0; - std::atomic MaximumActiveRuns = 0; - rcc::MachinePassManager Instrumented; - Context.expect( - Instrumented - .addPass(std::make_unique(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({ - .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()).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({ - .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()).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({ - .Name = "FailingUnit", - .Dependencies = {}, - }) - .has_value(), - "failing compilation-unit analysis registration must succeed"); - rcc::CompilationUnitPassManager FailingUnitAnalysisPipeline; - Context.expect( - FailingUnitAnalysisPipeline.addPass(std::make_unique()) - .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({ - .Name = "BlockCount", - .Dependencies = {}, - .InvalidatedBy = rcc::MachinePassChanges::all(), - }) - .has_value() && - FunctionAnalyses.getResult(*FirstFunction, Target).has_value(), - "compilation-unit failure cache fixtures must materialize"); - rcc::CompilationUnitPassManager FailingUnitPipeline; - Context.expect( - FirstFunction != nullptr && - FailingUnitPipeline.addPass(std::make_unique()).has_value() && - !FailingUnitPipeline.run(Unit, Target, UnitAnalyses, FunctionAnalyses) && - !UnitAnalyses.hasCached( - Unit, rcc::CompilationUnitAnalysisManager::id()) && - !FunctionAnalyses.hasCached(*FirstFunction, - rcc::MachineAnalysisManager::id()), - "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 @@ -62,11 +62,11 @@ buildPhi(rcc::MachineFunction &Function, const rcc::TargetDescription &Target, [[nodiscard]] rcc::Expected 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(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 +#include +#include +#include +#include +#include +#include + +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 createTarget() { + std::vector Descriptors; + std::vector 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 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(), + }, + .VerificationStage = std::nullopt, + }; + return Contract; + } + + [[nodiscard]] rcc::Expected + run(rcc::MachineFunction &Function, const rcc::TargetDescription &Target, + rcc::MachineFunctionAnalysisManager &Analyses) override { + auto First = Analyses.getResult(Function, Target); + auto Second = Analyses.getResult(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({ + .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()).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 +#include +#include +#include +#include +#include +#include + +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 createTarget() { + std::vector Descriptors; + std::vector 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 run(rcc::MachineFunction &Function, + const rcc::TargetDescription &, + rcc::MachineFunctionAnalysisManager &) { + ++Runs; + return Result{Function.blockCount()}; + } + + static inline std::atomic 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 run(rcc::MachineFunction &Function, + const rcc::TargetDescription &Target, + rcc::MachineFunctionAnalysisManager &Analyses) { + auto Blocks = Analyses.getResult(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 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 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 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 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({ + .Name = "BlockCount", + .Dependencies = {}, + .InvalidatedBy = {rcc::MachinePassChange::CFG}, + }) + .has_value(), + "base analysis registration must succeed"); + Context.expect(Analyses + .registerAnalysis({ + .Name = "Dependent", + .Dependencies = + { + rcc::MachineFunctionAnalysisManager::id(), + }, + .InvalidatedBy = {rcc::MachinePassChange::Operands}, + }) + .has_value(), + "dependent analysis registration must succeed"); + + BlockCountAnalysis::Runs = 0; + auto First = Analyses.getResult(**Function, Target); + auto Second = Analyses.getResult(**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({ + .Name = "CycleA", + .Dependencies = + { + rcc::MachineFunctionAnalysisManager::id(), + }, + }) + .has_value() && + Cyclic + .registerAnalysis({ + .Name = "CycleB", + .Dependencies = + { + rcc::MachineFunctionAnalysisManager::id(), + }, + }) + .has_value(), + "cyclic analysis registrations must be accepted until requested"); + Context.expect(!Cyclic.getResult(**Function, Target), + "analysis dependency cycles must produce a structured error"); + + rcc::MachineFunctionAnalysisManager CustomInvalidation; + Context.expect(CustomInvalidation + .registerAnalysis({ + .Name = "Sticky", + .Dependencies = {}, + .InvalidatedBy = {rcc::MachinePassChange::CFG}, + }) + .has_value() && + CustomInvalidation + .registerAnalysis({ + .Name = "Expiring", + .Dependencies = {}, + .InvalidatedBy = {rcc::MachinePassChange::CFG}, + }) + .has_value() && + CustomInvalidation.getResult(**Function, Target) && + CustomInvalidation.getResult(**Function, Target), + "custom invalidation analyses must materialize"); + CustomInvalidation.invalidate(**Function, rcc::PreservedAnalyses::none(), + {rcc::MachinePassChange::CFG}); + Context.expect(CustomInvalidation.hasCached( + **Function, rcc::MachineFunctionAnalysisManager::id()) && + !CustomInvalidation.hasCached( + **Function, rcc::MachineFunctionAnalysisManager::id()), + "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(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(*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(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 @@ -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(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, -- cgit v1.2.3