From 4e4cb000a9a24802eaba7111436d8db7a5601012 Mon Sep 17 00:00:00 2001 From: Massimo Fioravanti Date: Fri, 28 May 2021 18:59:42 +0200 Subject: [PATCH] Added pure llvm pipelines --- CMakeLists.txt | 2 +- include/revng/ADT/Hierarchy.h | 15 ++- include/revng/AutoEnforcer/LLVMEnforcer.h | 40 +++++++ include/revng/AutoEnforcer/PipelineLoader.h | 110 +++++++++++++----- lib/AutoEnforcer/LLVMEnforcer.cpp | 13 +++ lib/AutoEnforcer/PipelineLoader.cpp | 18 +++ tests/tools/AutoEnforcer/CMakeLists.txt | 9 ++ .../AutoEnforcer/MissingPassPipelineTest.yaml | 9 ++ .../tools/AutoEnforcer/PassPipelineTest.yaml | 9 ++ .../AutoEnforcer/StringContainerLibrary.cpp | 12 ++ tests/unit/AutoEnforcer.cpp | 41 ++++++- tools/revng-autoenforcer/Main.cpp | 30 ++++- 12 files changed, 266 insertions(+), 42 deletions(-) create mode 100644 tests/tools/AutoEnforcer/MissingPassPipelineTest.yaml create mode 100644 tests/tools/AutoEnforcer/PassPipelineTest.yaml diff --git a/CMakeLists.txt b/CMakeLists.txt index 52f1f88ce..b2e552f4f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -45,7 +45,7 @@ find_package(LLVM REQUIRED CONFIG) include_directories(${LLVM_INCLUDE_DIRS}) add_definitions(${LLVM_DEFINITIONS}) llvm_map_components_to_libnames(LLVM_LIBRARIES core support irreader ScalarOpts - linker Analysis object transformutils InstCombine CodeGen Passes BitWriter) + linker Analysis object transformutils InstCombine CodeGen Passes BitWriter InstCombine) # Build the support module for each architecture and in several configurations set(CLANG "${LLVM_TOOLS_BINARY_DIR}/clang") diff --git a/include/revng/ADT/Hierarchy.h b/include/revng/ADT/Hierarchy.h index 3be91dc20..59a719861 100644 --- a/include/revng/ADT/Hierarchy.h +++ b/include/revng/ADT/Hierarchy.h @@ -88,14 +88,13 @@ public: llvm::StringRef getName() const { return Name; } size_t depth() const { - size_t ToReturn = 0; - auto Current = Parent; - while (Current != nullptr) - { - ToReturn++; - Current = Current->Parent; - } - return ToReturn; + size_t ToReturn = 0; + auto Current = Parent; + while (Current != nullptr) { + ToReturn++; + Current = Current->Parent; + } + return ToReturn; } private: diff --git a/include/revng/AutoEnforcer/LLVMEnforcer.h b/include/revng/AutoEnforcer/LLVMEnforcer.h index 4d7c400d0..a614c7824 100644 --- a/include/revng/AutoEnforcer/LLVMEnforcer.h +++ b/include/revng/AutoEnforcer/LLVMEnforcer.h @@ -7,6 +7,7 @@ #include #include #include +#include #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/SmallVector.h" @@ -222,6 +223,45 @@ private: llvm::SmallVector, 3> Passess; }; +class PureLLVMEnforcer { +public: + static constexpr auto Name = "PureLLVMEnforcer"; + + llvm::SmallVector getContract() const { return {}; } + void run(DefaultLLVMContainer &Container); + + template + void dump(OStream &OS, size_t Indents = 0) const debug_function { + for (const auto &Pass : PassNames) { + indent(OS, Indents); + OS << Pass << "\n"; + } + } + + static llvm::Expected + create(std::vector PassNames) { + for (const auto &Name : PassNames) + if (llvm::PassRegistry::getPassRegistry()->getPassInfo(Name) == nullptr) + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "Could not load llvm pass %s ", + Name.c_str()); + + return PureLLVMEnforcer(std::move(PassNames)); + } + + PureLLVMEnforcer() = default; + ~PureLLVMEnforcer() = default; + PureLLVMEnforcer(PureLLVMEnforcer &&) = default; + PureLLVMEnforcer(const PureLLVMEnforcer &) = default; + PureLLVMEnforcer &operator=(PureLLVMEnforcer &&) = default; + PureLLVMEnforcer &operator=(const PureLLVMEnforcer &) = default; + +private: + PureLLVMEnforcer(std::vector Names) : + PassNames(std::move(Names)) {} + std::vector PassNames; +}; + template EnforcerWrapper wrapLLVMPassess(std::string LLVMModuleName, LLVMEnforcerPassess &&... P) { diff --git a/include/revng/AutoEnforcer/PipelineLoader.h b/include/revng/AutoEnforcer/PipelineLoader.h index 009b99708..f86362473 100644 --- a/include/revng/AutoEnforcer/PipelineLoader.h +++ b/include/revng/AutoEnforcer/PipelineLoader.h @@ -3,6 +3,7 @@ // This file is distributed under the MIT License. See LICENSE.md for details. // +#include #include #include #include @@ -14,6 +15,7 @@ #include "revng/AutoEnforcer/AutoEnforcer.h" #include "revng/AutoEnforcer/LLVMEnforcer.h" #include "revng/Model/TupleTree.h" +#include "revng/Support/Assert.h" namespace AutoEnforcer { struct BackingContainerDeclaration { @@ -39,12 +41,39 @@ struct PipelineDeclaration { std::vector Steps; }; +template +char *getUniqueID() { + static char ID; + return &ID; +} + +class TypeSafePtrWrapper { +public: + template + TypeSafePtrWrapper(T &Object) : Ptr(&Object), ID(getUniqueID()) {} + + template + bool isA() const { + return ID == getUniqueID(); + } + + template + T &getAs() const { + revng_assert(isA()); + return *static_cast(Ptr); + } + +private: + void *Ptr; + char *ID; +}; + class PipelineLoader { public: llvm::Expected load(const PipelineDeclaration &) const; llvm::Expected load(llvm::StringRef Pipeline) const; - template + template void registerDefaultConstructibleContainer(llvm::StringRef Name) { KnownContainerTypes.try_emplace(Name, []() { using Type = DefaultConstructibleBackingContainerFactory; @@ -52,13 +81,14 @@ public: }); } - template - void registerContainerFactory(llvm::StringRef Name, Args &&...A) { - KnownContainerTypes.try_emplace( - Name, [&A...]() { return std::make_unique(A...); }); + template + void registerContainerFactory(llvm::StringRef Name, Args &&... A) { + KnownContainerTypes.try_emplace(Name, [&A...]() { + return std::make_unique(A...); + }); } - template + template void registerLLVMEnforcerPass(llvm::StringRef Name) { KnownLLVMEnforcerTypes.try_emplace(Name, []() { using Type = LLVMEnforcerImpl; @@ -66,19 +96,20 @@ public: }); } - template void registerEnforcer(llvm::StringRef Name) { + template + void registerEnforcer(llvm::StringRef Name) { const auto LambdaToEmplace = [](std::vector CName) { return EnforcerWrapper::makeWrapper(std::move(CName)); }; KnownEnforcersTypes.try_emplace(Name, LambdaToEmplace); } - template + template void registerEnforcer(llvm::StringRef Name, const EnforcerType &Enforcer) { const auto LambdaToEmplace = - [Enforcer](std::vector ContainerNames) { - return EnforcerWrapper(Enforcer, std::move(ContainerNames)); - }; + [Enforcer](std::vector ContainerNames) { + return EnforcerWrapper(Enforcer, std::move(ContainerNames)); + }; KnownEnforcersTypes.try_emplace(Name, LambdaToEmplace); } @@ -87,40 +118,66 @@ public: EnabledFlags.insert(Name); } + template + llvm::Expected get(llvm::StringRef Name) const { + auto Iter = Context.find(Name); + if (Iter == Context.end()) + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "pipeline loader context did not " + "contained object %s", + Name.str().c_str()); + if (not Iter->second.isA()) + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "pipeline loader was requested to cast %s " + "to the wrong type", + Name.str().c_str()); + + return &Iter->second.getAs(); + } + + void add(llvm::StringRef Name, auto &Object) { + Context.try_emplace(Name, Object); + } + private: - llvm::Error parseStepDeclaration(PipelineRunner &Runner, - const StepDeclaration &) const; - llvm::Error parseInvocation(Step &Step, - const EnforcerInvocation &Invocation) const; + llvm::Error + parseStepDeclaration(PipelineRunner &Runner, const StepDeclaration &) const; + llvm::Error + parseInvocation(Step &Step, const EnforcerInvocation &Invocation) const; llvm::Error parseContainerDeclaration(PipelineRunner &Runner, const BackingContainerDeclaration &) const; - llvm::Error parseLLVMPass(Step &Step, - const EnforcerInvocation &Invocation) const; + llvm::Error + parseLLVMPass(Step &Step, const EnforcerInvocation &Invocation) const; + + llvm::Error + parsePureLLVMPass(Step &Step, const EnforcerInvocation &Invocation) const; using FType = std::function()>; llvm::StringMap KnownContainerTypes; llvm::StringMap)>> - KnownEnforcersTypes; + KnownEnforcersTypes; llvm::StringMap()>> - KnownLLVMEnforcerTypes; + KnownLLVMEnforcerTypes; bool isInvocationUsed(const std::vector &Names) const; std::set EnabledFlags; + + llvm::StringMap Context; }; } // namespace AutoEnforcer INTROSPECTION_NS(AutoEnforcer, BackingContainerDeclaration, Name, Type); -template <> +template<> struct llvm::yaml::MappingTraits - : public TupleLikeMappingTraits { -}; + : public TupleLikeMappingTraits {}; LLVM_YAML_IS_SEQUENCE_VECTOR(AutoEnforcer::BackingContainerDeclaration) -template <> struct llvm::yaml::MappingTraits { +template<> +struct llvm::yaml::MappingTraits { static void mapping(IO &io, AutoEnforcer::EnforcerInvocation &info) { io.mapRequired("Name", info.Name); io.mapRequired("UsedContainers", info.UsedContainers); @@ -131,7 +188,8 @@ template <> struct llvm::yaml::MappingTraits { LLVM_YAML_IS_SEQUENCE_VECTOR(AutoEnforcer::EnforcerInvocation) -template <> struct llvm::yaml::MappingTraits { +template<> +struct llvm::yaml::MappingTraits { static void mapping(IO &io, AutoEnforcer::StepDeclaration &info) { io.mapRequired("Name", info.Name); io.mapRequired("Enforcers", info.Enforcers); @@ -142,6 +200,6 @@ template <> struct llvm::yaml::MappingTraits { LLVM_YAML_IS_SEQUENCE_VECTOR(AutoEnforcer::StepDeclaration) INTROSPECTION_NS(AutoEnforcer, PipelineDeclaration, Containers, Steps); -template <> +template<> struct llvm::yaml::MappingTraits - : public TupleLikeMappingTraits {}; + : public TupleLikeMappingTraits {}; diff --git a/lib/AutoEnforcer/LLVMEnforcer.cpp b/lib/AutoEnforcer/LLVMEnforcer.cpp index d824fedba..553ad758e 100644 --- a/lib/AutoEnforcer/LLVMEnforcer.cpp +++ b/lib/AutoEnforcer/LLVMEnforcer.cpp @@ -2,6 +2,9 @@ // This file is distributed under the MIT License. See LICENSE.md for details. // +#include "llvm/PassRegistry.h" +#include "llvm/PassSupport.h" + #include "revng/AutoEnforcer/LLVMEnforcer.h" using namespace std; @@ -35,6 +38,16 @@ SmallVector LLVMEnforcer::getContract() const { return Contract; } +void PureLLVMEnforcer::run(DefaultLLVMContainer &Container) { + legacy::PassManager Manager; + auto *Registry = PassRegistry::getPassRegistry(); + for (const auto &Element : PassNames) { + + Manager.add(Registry->getPassInfo(Element)->createPass()); + } + Manager.run(Container.getModule()); +} + void LLVMEnforcer::run(DefaultLLVMContainer &Container) { legacy::PassManager Manager; for (const auto &Element : Passess) diff --git a/lib/AutoEnforcer/PipelineLoader.cpp b/lib/AutoEnforcer/PipelineLoader.cpp index 31a528f79..adb66089c 100644 --- a/lib/AutoEnforcer/PipelineLoader.cpp +++ b/lib/AutoEnforcer/PipelineLoader.cpp @@ -44,12 +44,30 @@ PipelineLoader::parseLLVMPass(Step &Step, return Error::success(); } +llvm::Error +PipelineLoader::parsePureLLVMPass(Step &Step, + const EnforcerInvocation &Invocation) const { + + auto MaybeEnforcer = PureLLVMEnforcer::create(Invocation.Passess); + if (!MaybeEnforcer) + return MaybeEnforcer.takeError(); + + auto Wrapper = EnforcerWrapper(move(*MaybeEnforcer), + Invocation.UsedContainers); + Step.addEnforcer(move(Wrapper)); + + return Error::success(); +} + llvm::Error PipelineLoader::parseInvocation(Step &Step, const EnforcerInvocation &Invocation) const { if (Invocation.Name == "LLVMEnforcer") return parseLLVMPass(Step, Invocation); + if (Invocation.Name == "PureLLVMEnforcer") + return parsePureLLVMPass(Step, Invocation); + auto It = KnownEnforcersTypes.find(Invocation.Name); if (It == KnownEnforcersTypes.end()) return createStringError(inconvertibleErrorCode(), diff --git a/tests/tools/AutoEnforcer/CMakeLists.txt b/tests/tools/AutoEnforcer/CMakeLists.txt index bfab18d02..6d320e9c7 100644 --- a/tests/tools/AutoEnforcer/CMakeLists.txt +++ b/tests/tools/AutoEnforcer/CMakeLists.txt @@ -9,6 +9,10 @@ macro(add_auto_enforcer_test TestName PipelineFile Targets InputsList OutputList add_test(NAME ${TestName} COMMAND revng-autoenforcer ${CMAKE_CURRENT_SOURCE_DIR}/${PipelineFile} ${Targets} -i ${InputsList} -o ${OutputList} -l $ -p ${CMAKE_CURRENT_BINARY_DIR}/${TestName} -f ${Flags}) endmacro() +macro(add_auto_enforcer_dump_test TestName PipelineFile) + add_test(NAME ${TestName} COMMAND revng-autoenforcer ${CMAKE_CURRENT_SOURCE_DIR}/${PipelineFile} dc:dc:Root -l $ -d) +endmacro() + macro(add_auto_enforcer_test_with_output TestName PipelineFile Targets InputsList OutputList Flags ToCheckFile ExpectedOutput) add_auto_enforcer_test(${TestName} ${PipelineFile} ${Targets} ${InputsList} ${OutputList} ${Flags}) @@ -23,6 +27,7 @@ macro(add_auto_enforcer_failing_test TestName PipelineFile Targets InputsList Ou set_tests_properties(${TestName} PROPERTIES WILL_FAIL TRUE) endmacro() + set(CopyEnforcerTestInputs "FirstStep:Strings1:${CMAKE_CURRENT_SOURCE_DIR}/CopyEnforcerTestInput.txt") set(CopyEnforcerTestOutputs "End:Strings2:${CMAKE_CURRENT_BINARY_DIR}/CopyEnforcerTestOutput.txt") add_auto_enforcer_test_with_output(copy-enforcer-run-test CopyEnforcerTestPipeline.yaml Strings2:Root:Root ${CopyEnforcerTestInputs} ${CopyEnforcerTestOutputs} None CopyEnforcerTestOutput.txt ${CMAKE_CURRENT_SOURCE_DIR}/CopyEnforcerTestExpected.txt) @@ -31,3 +36,7 @@ add_auto_enforcer_test_with_output(flag-autoenforcer-test CopyEnforcerTestPipeli add_auto_enforcer_failing_test(flag-auto-enforcer-fail-test CopyEnforcerFlagTestPipeline.yaml Strings2:Root:Root ${CopyEnforcerTestInputs} ${CopyEnforcerTestOutputs} EnablingFlags) + +add_auto_enforcer_failing_test(missing-pass-autoenforcer-fail-test MissingPassPipelineTest.yaml Strings2:Root:Root ${CopyEnforcerTestInputs} ${CopyEnforcerTestOutputs} DontCare) + +add_auto_enforcer_dump_test(llvm-pass-test PassPipelineTest.yaml) diff --git a/tests/tools/AutoEnforcer/MissingPassPipelineTest.yaml b/tests/tools/AutoEnforcer/MissingPassPipelineTest.yaml new file mode 100644 index 000000000..dd64fa3ce --- /dev/null +++ b/tests/tools/AutoEnforcer/MissingPassPipelineTest.yaml @@ -0,0 +1,9 @@ +Containers: + - Name: C1 + Type: LLVMContainer +Steps: + - Name: FirstStep + Enforcers: + - Name: PureLLVMEnforcer + UsedContainers: [C1] + Passess: [NonExistantAtAllPass] diff --git a/tests/tools/AutoEnforcer/PassPipelineTest.yaml b/tests/tools/AutoEnforcer/PassPipelineTest.yaml new file mode 100644 index 000000000..56ae60400 --- /dev/null +++ b/tests/tools/AutoEnforcer/PassPipelineTest.yaml @@ -0,0 +1,9 @@ +Containers: + - Name: C1 + Type: LLVMContainer +Steps: + - Name: FirstStep + Enforcers: + - Name: PureLLVMEnforcer + UsedContainers: [C1] + Passess: [ExamplePass] diff --git a/tests/tools/AutoEnforcer/StringContainerLibrary.cpp b/tests/tools/AutoEnforcer/StringContainerLibrary.cpp index cbbc607c4..2bd7f5694 100644 --- a/tests/tools/AutoEnforcer/StringContainerLibrary.cpp +++ b/tests/tools/AutoEnforcer/StringContainerLibrary.cpp @@ -88,6 +88,18 @@ private: std::set ContainedStrings; }; +struct ExamplePass : public llvm::ModulePass { + static char ID; + + ExamplePass() : llvm::ModulePass(ID) {} + + bool runOnModule(llvm::Module &M) override { return true; } +}; + +char ExamplePass::ID = '_'; + +static llvm::RegisterPass X2("ExamplePass", "ExamplePass"); + char StringContainer::ID; class ExampleRegistryLibrary : public AutoEnforcerLibraryRegistry { diff --git a/tests/unit/AutoEnforcer.cpp b/tests/unit/AutoEnforcer.cpp index 37bd6d822..636ec7991 100644 --- a/tests/unit/AutoEnforcer.cpp +++ b/tests/unit/AutoEnforcer.cpp @@ -14,8 +14,11 @@ #include "llvm/IR/IRBuilder.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/LegacyPassManager.h" +#include "llvm/InitializePasses.h" #include "llvm/Pass.h" +#include "llvm/PassSupport.h" #include "llvm/Support/Casting.h" +#include "llvm/Support/TargetSelect.h" #include "llvm/Support/YAMLTraits.h" #include "llvm/Transforms/Utils/Cloning.h" @@ -524,6 +527,8 @@ struct IdentityPass : public llvm::ModulePass { char IdentityPass::ID = '_'; +static llvm::RegisterPass X2("IdentityPass", "IdentityPass"); + struct LLVMEnforcerPassFunctionCreator { static constexpr auto Name = "Function Creator"; @@ -580,6 +585,38 @@ BOOST_AUTO_TEST_CASE(SingleElementLLVMPipelineBackwardFinedGrained) { BOOST_TEST(F != nullptr); } +BOOST_AUTO_TEST_CASE(LLVMPureEnforcer) { + llvm::LLVMContext C; + + PipelineRunner AE; + AE.registerContainerFactory(CName, C); + + auto MaybePureLLVMEnforcer = PureLLVMEnforcer::create({ "IdentityPass" }); + BOOST_TEST((!!MaybePureLLVMEnforcer)); + + AE.addStep("first_step", + wrapLLVMPassess(CName, LLVMEnforcerPassFunctionCreator()), + bindEnforcer(move(*MaybePureLLVMEnforcer), CName)); + AE.addStep("End"); + + makeF(AE.getStartingContainer(CName).getModule(), + "root"); + + BackingContainersStatus Targets; + Targets.add(CName, + AutoEnforcerTarget({ AutoEnforcerQuantifier("root"), + AutoEnforcerQuantifier("f1") }, + FunctionKind)); + + auto Error = AE.run(Targets); + BOOST_TEST(!Error); + + const auto &Final = AE.getFinalContainer(CName); + const auto *F = Final.getModule().getFunction("f1"); + + BOOST_TEST(F != nullptr); +} + BOOST_AUTO_TEST_CASE(SingleElementPipelineForwardFinedGrained) { PipelineRunner AE; AE.registerDefaultConstructibleFactory(CName); @@ -702,7 +739,7 @@ BOOST_AUTO_TEST_CASE(PipelineLoaderTestFromYamlLLVM) { Loader.registerLLVMEnforcerPass("IdentityPa" "ss"); - std::string Pipeline(R"(--- + std::string LLVMPipeline(R"(--- Containers: - Name: CustomName Type: LLVMContainer @@ -717,7 +754,7 @@ BOOST_AUTO_TEST_CASE(PipelineLoaderTestFromYamlLLVM) { - IdentityPass )"); - auto MaybeAutoEnforcer = Loader.load(Pipeline); + auto MaybeAutoEnforcer = Loader.load(LLVMPipeline); BOOST_TEST(!!MaybeAutoEnforcer); if (!MaybeAutoEnforcer) diff --git a/tools/revng-autoenforcer/Main.cpp b/tools/revng-autoenforcer/Main.cpp index 4c744c24c..9080097c8 100644 --- a/tools/revng-autoenforcer/Main.cpp +++ b/tools/revng-autoenforcer/Main.cpp @@ -4,6 +4,8 @@ // This file is distributed under the MIT License. See LICENSE.md for details. // +#include + #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringSet.h" #include "llvm/IR/Function.h" @@ -16,6 +18,7 @@ #include "revng/AutoEnforcer/AutoEnforcerTarget.h" #include "revng/AutoEnforcer/BackingContainers.h" #include "revng/AutoEnforcer/CopyEnforcer.h" +#include "revng/AutoEnforcer/LLVMEnforcer.h" #include "revng/AutoEnforcer/PipelineLoader.h" #include "revng/Enforcers/RevngEnforcers.h" @@ -42,14 +45,18 @@ static list ContainerOverrides("i", "step"), cat(AutoEnforcerCategory)); +static opt DumpPipeline("d", + desc("Dump built pipeline and dont run"), + cat(AutoEnforcerCategory)); + static list StoresOverrides("o", desc("Store the target container at the " "target step in the target file"), cat(AutoEnforcerCategory)); static list EnablingFlags("f", - desc("list of pipeline enabling flags"), - cat(AutoEnforcerCategory)); + desc("list of pipeline enabling flags"), + cat(AutoEnforcerCategory)); static opt ExecutionFolder("p", desc("Folder from which all containers will " @@ -71,7 +78,14 @@ static ExitOnError exitOnError; class LLVMAutoEnforcerLibraryRegistry : public AutoEnforcerLibraryRegistry { public: - void registerContainersAndEnforcers(PipelineLoader &Loader) override {} + void registerContainersAndEnforcers(PipelineLoader &Loader) override { + auto MaybeLLVMContext = Loader.get("LLVMContext"); + if (!MaybeLLVMContext) + return; + + using Type = DefaultLLVMContainerFactory; + Loader.registerContainerFactory("LLVMContainer", **MaybeLLVMContext); + } void registerKinds(llvm::StringMap &KindDictionary) override { KindDictionary["Root"] = &Root; @@ -87,8 +101,9 @@ static auto getBuffer(StringRef Path) { return exitOnError(errorOrToExpected(MemoryBuffer::getFileOrSTDIN(Path))); } -static PipelineRunner setUpAutoEnforcer() { +static PipelineRunner setUpAutoEnforcer(LLVMContext &Context) { PipelineLoader Loader; + Loader.add("LLVMContext", Context); Loader.registerEnabledFlags(EnablingFlags); AutoEnforcerLibraryRegistry::registerAllContainersAndEnforcers(Loader); @@ -139,7 +154,12 @@ int main(int argc, const char *argv[]) { } } - auto AutoEnforcer = setUpAutoEnforcer(); + LLVMContext Context; + auto AutoEnforcer = setUpAutoEnforcer(Context); + if (DumpPipeline) { + AutoEnforcer.dump(); + return EXIT_SUCCESS; + } runAutoEnforcer(AutoEnforcer); tearDownAutoEnforcer(AutoEnforcer);