aboutsummaryrefslogtreecommitdiff
path: root/backend/unittests/CodeGen/PassInstrumentationTest.cpp
diff options
context:
space:
mode:
authorRichard-Qin-X <richard.qin.001@gmail.com>2026-08-13 05:45:37 +0800
committerRichard-Qin-X <richard.qin.001@gmail.com>2026-08-13 05:45:37 +0800
commit236082a0fe4dcd0362c5c74923a93e3db6a01291 (patch)
tree5d8a329ca526b087b6ce51895b5135997e8e03bd /backend/unittests/CodeGen/PassInstrumentationTest.cpp
parenta2f8d7c5e22b510965327383410bd576f7cf28e7 (diff)
downloadrcc-project-236082a0fe4dcd0362c5c74923a93e3db6a01291.tar.gz
rcc-project-236082a0fe4dcd0362c5c74923a93e3db6a01291.tar.xz
[CodeGen] Align pass manager ownership
Diffstat (limited to 'backend/unittests/CodeGen/PassInstrumentationTest.cpp')
-rw-r--r--backend/unittests/CodeGen/PassInstrumentationTest.cpp173
1 files changed, 173 insertions, 0 deletions
diff --git a/backend/unittests/CodeGen/PassInstrumentationTest.cpp b/backend/unittests/CodeGen/PassInstrumentationTest.cpp
new file mode 100644
index 0000000..97ce445
--- /dev/null
+++ b/backend/unittests/CodeGen/PassInstrumentationTest.cpp
@@ -0,0 +1,173 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "Target/TestTarget.h"
+#include "rcc/CodeGen/GenericOpcodes.h"
+#include "rcc/CodeGen/MachineFunction.h"
+#include "rcc/CodeGen/MachinePassManager.h"
+
+#include <cstddef>
+#include <cstdint>
+#include <iostream>
+#include <memory>
+#include <string_view>
+#include <utility>
+#include <vector>
+
+namespace {
+
+class TestContext final {
+public:
+ void expect(bool Condition, std::string_view Message) {
+ if (Condition)
+ return;
+ std::cerr << Message << '\n';
+ ++Failures;
+ }
+
+ [[nodiscard]] int result() const noexcept {
+ return Failures == 0 ? 0 : 1;
+ }
+
+private:
+ int Failures = 0;
+};
+
+[[nodiscard]] rcc::Expected<rcc::TargetDescription> createTarget() {
+ std::vector<rcc::InstructionDescriptor> Descriptors;
+ std::vector<rcc::ImmediatePredicate> Predicates;
+ auto Appended = rcc::appendGenericInstructionInfo(Descriptors, Predicates);
+ if (!Appended)
+ return std::unexpected(Appended.error());
+ auto Instructions = rcc::InstructionSet::create(std::move(Descriptors), std::move(Predicates));
+ if (!Instructions)
+ return std::unexpected(Instructions.error());
+ return rcc::test::createTestTargetDescription(std::move(*Instructions));
+}
+
+class BlockCountAnalysis final {
+public:
+ struct Result final {
+ std::size_t Count = 0;
+ };
+
+ [[nodiscard]] static constexpr std::string_view name() noexcept {
+ return "BlockCount";
+ }
+
+ [[nodiscard]] static rcc::Expected<Result> run(rcc::MachineFunction &Function,
+ const rcc::TargetDescription &,
+ rcc::MachineFunctionAnalysisManager &) {
+ return Result{Function.blockCount()};
+ }
+};
+
+class InstrumentedPass final : public rcc::MachineFunctionPass {
+public:
+ [[nodiscard]] const rcc::MachinePassContract &contract() const noexcept override {
+ static const rcc::MachinePassContract Contract{
+ .Name = "Instrumented",
+ .Requires = {},
+ .Sets = {},
+ .Clears = {},
+ .RequiredAnalyses =
+ {
+ rcc::MachineFunctionAnalysisManager::id<BlockCountAnalysis>(),
+ },
+ .VerificationStage = std::nullopt,
+ };
+ return Contract;
+ }
+
+ [[nodiscard]] rcc::Expected<rcc::MachinePassResult>
+ run(rcc::MachineFunction &Function, const rcc::TargetDescription &Target,
+ rcc::MachineFunctionAnalysisManager &Analyses) override {
+ auto First = Analyses.getResult<BlockCountAnalysis>(Function, Target);
+ auto Second = Analyses.getResult<BlockCountAnalysis>(Function, Target);
+ auto FirstAction = Analyses.requestAction("first", Function);
+ auto SecondAction = Analyses.requestAction("second", Function);
+ if (!First || !Second || !FirstAction || !SecondAction || !*FirstAction || *SecondAction)
+ return rcc::makeUnexpected(rcc::ErrorCode::VerificationError,
+ "instrumentation action policy was not enforced");
+ rcc::MachinePassResult Result;
+ Result.Preserved = rcc::PreservedAnalyses::all();
+ return Result;
+ }
+};
+
+void testInstrumentation(TestContext &Context, const rcc::TargetDescription &Target) {
+ auto Function =
+ rcc::MachineFunction::create("instrumentation", rcc::MachineFunctionStableID::fromValue(1));
+ Context.expect(Function.has_value(), "instrumentation function creation must succeed");
+ if (!Function)
+ return;
+
+ rcc::MachineFunctionAnalysisManager Analyses;
+ Context.expect(Analyses
+ .registerAnalysis<BlockCountAnalysis>({
+ .Name = "BlockCount",
+ .Dependencies = {},
+ .InvalidatedBy = rcc::MachinePassChanges::all(),
+ .ThreadSafety = rcc::PassThreadSafety::ThreadSafe,
+ })
+ .has_value(),
+ "instrumentation analysis registration must succeed");
+
+ rcc::MachinePassInstrumentation Instrumentation;
+ Instrumentation.setPerFunctionFuel(1);
+ std::uint32_t BeforePass = 0;
+ std::uint32_t AfterPass = 0;
+ std::uint32_t BeforeAnalysis = 0;
+ std::uint32_t AfterAnalysis = 0;
+ std::uint32_t CacheHits = 0;
+ std::uint32_t Printed = 0;
+ std::uint32_t Changes = 0;
+ Instrumentation.addBeforeCallback(
+ [&BeforePass](std::string_view, const rcc::MachineFunction &) { ++BeforePass; });
+ Instrumentation.addAfterCallback([&AfterPass](std::string_view, const rcc::MachineFunction &,
+ const rcc::MachinePassResult &) { ++AfterPass; });
+ Instrumentation.addBeforeAnalysisCallback(
+ [&BeforeAnalysis](std::string_view, const rcc::MachineFunction &) { ++BeforeAnalysis; });
+ Instrumentation.addAfterAnalysisCallback(
+ [&AfterAnalysis, &CacheHits](std::string_view, const rcc::MachineFunction &, bool CacheHit,
+ std::uint64_t) {
+ ++AfterAnalysis;
+ CacheHits += CacheHit ? 1U : 0U;
+ });
+ Instrumentation.addIRPrintCallback([&Printed](rcc::InstrumentationPhase, std::string_view,
+ const rcc::MachineFunction &) { ++Printed; });
+ Instrumentation.addChangeCallback([&Changes](std::string_view, const rcc::MachineFunction &,
+ rcc::MachinePassChanges ReportedChanges,
+ bool FingerprintChanged) {
+ if (ReportedChanges.empty() && !FingerprintChanged)
+ ++Changes;
+ });
+
+ rcc::MachineFunctionPassManager Pipeline;
+ Context.expect(Pipeline.addPass(std::make_unique<InstrumentedPass>()).has_value() &&
+ Pipeline.run(**Function, Target, Analyses, &Instrumentation).has_value(),
+ "instrumented pass must run");
+ Context.expect(BeforePass == 1 && AfterPass == 1 && BeforeAnalysis == 1 && AfterAnalysis == 3 &&
+ CacheHits == 2 && Printed == 2 && Changes == 1,
+ "all pass and analysis instrumentation points must run");
+
+ const auto Events = Instrumentation.actionEvents();
+ Context.expect(Events.size() == 2 && Events[0].Accepted && !Events[1].Accepted,
+ "action instrumentation must record accepted and rejected decisions");
+ const auto Statistics = Instrumentation.statistics();
+ Context.expect(Statistics.size() == 2,
+ "instrumentation must collect one pass and one analysis statistic");
+}
+
+} // namespace
+
+int main() {
+ TestContext Context;
+ auto Target = createTarget();
+ Context.expect(Target.has_value(),
+ "pass instrumentation tests require a valid target description");
+ if (!Target)
+ return Context.result();
+
+ testInstrumentation(Context, *Target);
+ return Context.result();
+}