diff --git a/llvm/examples/Bye/CMakeLists.txt b/llvm/examples/Bye/CMakeLists.txt --- a/llvm/examples/Bye/CMakeLists.txt +++ b/llvm/examples/Bye/CMakeLists.txt @@ -7,6 +7,12 @@ # work with DLLs on Windows (where a shared library can't have undefined # references), so just skip this example on Windows. if (NOT WIN32) + # Remove any -zdefs flag (which disallows undefined symbols in + # shared objects) added by the main cmake files on Linux when + # BUILD_SHARED_LIBS is enabled + STRING(REPLACE "-Wl,-z,defs" "" CMAKE_SHARED_LINKER_FLAGS + "${CMAKE_SHARED_LINKER_FLAGS}") + add_llvm_pass_plugin(Bye Bye.cpp DEPENDS diff --git a/llvm/examples/CMakeLists.txt b/llvm/examples/CMakeLists.txt --- a/llvm/examples/CMakeLists.txt +++ b/llvm/examples/CMakeLists.txt @@ -8,6 +8,7 @@ add_subdirectory(OrcV2Examples) add_subdirectory(SpeculativeJIT) add_subdirectory(Bye) +add_subdirectory(MachineExtension) if(LLVM_ENABLE_EH AND (NOT WIN32) AND (NOT "${LLVM_NATIVE_ARCH}" STREQUAL "ARM")) add_subdirectory(ExceptionDemo) diff --git a/llvm/examples/MachineExtension/CMakeLists.txt b/llvm/examples/MachineExtension/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/llvm/examples/MachineExtension/CMakeLists.txt @@ -0,0 +1,17 @@ +# The plugin expects not to link against the Support and Core libraries, +# but expects them to exist in the process loading the plugin. This doesn't +# work with DLLs on Windows (where a shared library can't have undefined +# references), so just skip this example on Windows. +if (NOT WIN32) + # Remove any -zdefs flag (which disallows undefined symbols in + # shared objects) added by the main cmake files on Linux when + # BUILD_SHARED_LIBS is enabled + STRING(REPLACE "-Wl,-z,defs" "" CMAKE_SHARED_LINKER_FLAGS + "${CMAKE_SHARED_LINKER_FLAGS}") + + # We have to build this shared so we can llc -load it + add_llvm_example_library(MachineExtension + MachineExtension.cpp + MODULE + ) +endif() diff --git a/llvm/examples/MachineExtension/MachineExtension.cpp b/llvm/examples/MachineExtension/MachineExtension.cpp new file mode 100644 --- /dev/null +++ b/llvm/examples/MachineExtension/MachineExtension.cpp @@ -0,0 +1,62 @@ +// Example showing how to manipulate a MachineFunction using your own pass via +// the TargetPassConfig::addExtension mechanism + +#include "llvm/CodeGen/MachineFunctionPass.h" +#include "llvm/CodeGen/MachineModuleInfo.h" +#include "llvm/CodeGen/TargetPassConfig.h" +#include "llvm/IR/LegacyPassManager.h" +#include "llvm/IR/Module.h" +#include "llvm/Support/Host.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Target/TargetMachine.h" + +using namespace llvm; + +namespace { +class ExampleMachinePass : public MachineFunctionPass { +public: + static char ID; + static void selfRegister(TargetPassConfig &TPC); + bool runOnMachineFunction(MachineFunction &MF) override; + ExampleMachinePass(); +}; + +char ExampleMachinePass::ID = 0; + +ExampleMachinePass::ExampleMachinePass() : MachineFunctionPass(ID) {} + +void ExampleMachinePass::selfRegister(TargetPassConfig &TPC) { + TPC.addPass(new ExampleMachinePass); +} + +bool ExampleMachinePass::runOnMachineFunction(MachineFunction &MF) { + // This example shows deletion of calls to a supposed "_dummy_intrinsic" + // function. + SmallVector Erasures; + + for (MachineBasicBlock &Block : MF) { + for (MachineInstr &Inst : Block) { + if (Inst.isCall() && Inst.getNumOperands() > 0) { + MachineOperand Callee = Inst.getOperand(0); + + if (Callee.isGlobal()) { + const GlobalValue *CalleeGlobal = Callee.getGlobal(); + + if (CalleeGlobal && CalleeGlobal->getName() == "_dummy_intrinsic") { + Erasures.push_back(&Inst); + } + } + } + } + } + for (MachineInstr *Target : Erasures) { + Target->eraseFromParent(); + // What to do about the callframe setup and teardown? + } + return !Erasures.empty(); +} + +} // end of anonymous namespace + +static RegisterTargetExtension registrar(TargetPassConfig::MPEP_EarlyAsPossible, + &ExampleMachinePass::selfRegister); diff --git a/llvm/include/llvm/CodeGen/TargetPassConfig.h b/llvm/include/llvm/CodeGen/TargetPassConfig.h --- a/llvm/include/llvm/CodeGen/TargetPassConfig.h +++ b/llvm/include/llvm/CodeGen/TargetPassConfig.h @@ -364,11 +364,13 @@ /// Returns the CSEConfig object to use for the current optimization level. virtual std::unique_ptr getCSEConfig() const; - /// Add an extension to be applied for the given Target, returning a + /// Add an extension to be applied to all Targets, returning a /// nonzero ID for use with removeExtension. The preferred way to - /// use this is via the RegisterTargetExtension class defined below. - static ExtensionID addExtension(const Target *Target, - MachinePassExtensionPointTy MPEP, + /// register is via the RegisterTargetExtension class defined + /// below. If the extension only applies to particular target(s) the + /// ExtensionFn callback can check the properties its + /// TargetPassConfig argument, e.g. via TPC.getTM(). + static ExtensionID addExtension(MachinePassExtensionPointTy MPEP, ExtensionFn Fn); /// Remove a previously added extension. The preferred way to use @@ -506,18 +508,16 @@ void registerCodeGenCallback(PassInstrumentationCallbacks &PIC, LLVMTargetMachine &); -/// Registers an extension function for a given target +/// Registers and de-registers a target extension function class RegisterTargetExtension { TargetPassConfig::ExtensionID ExtensionID; public: - RegisterTargetExtension(const Target *Target, - TargetPassConfig::MachinePassExtensionPointTy MPEP, + RegisterTargetExtension(TargetPassConfig::MachinePassExtensionPointTy MPEP, TargetPassConfig::ExtensionFn Fn) : - ExtensionID( - TargetPassConfig::addExtension(Target, MPEP, std::move(Fn))) {} + ExtensionID(TargetPassConfig::addExtension(MPEP, std::move(Fn))) {} ~RegisterTargetExtension() { if (ExtensionID) diff --git a/llvm/lib/CodeGen/TargetPassConfig.cpp b/llvm/lib/CodeGen/TargetPassConfig.cpp --- a/llvm/lib/CodeGen/TargetPassConfig.cpp +++ b/llvm/lib/CodeGen/TargetPassConfig.cpp @@ -233,7 +233,7 @@ cl::desc("Disable the expand reduction intrinsics pass from running")); using GlobalExtensionTy = - std::tuple; static ManagedStatic> GlobalExtensions; @@ -679,12 +679,11 @@ FinalPtr.getID() != ID; } -auto TargetPassConfig::addExtension(const Target *Target, - MachinePassExtensionPointTy MPEP, +auto TargetPassConfig::addExtension(MachinePassExtensionPointTy MPEP, ExtensionFn Fn) -> ExtensionID { static ExtensionID Counter = 0; ExtensionID ID = ++Counter; - GlobalExtensions->push_back(std::make_tuple(Target, MPEP, Fn, ID)); + GlobalExtensions->push_back(std::make_tuple(MPEP, Fn, ID)); return ID; } @@ -693,7 +692,7 @@ return; auto I = llvm::find_if(*GlobalExtensions, [ID](const auto &elem) { - return std::get<3>(elem) == ID; + return std::get<2>(elem) == ID; }); if (I != GlobalExtensions->end()) @@ -702,10 +701,9 @@ void TargetPassConfig::applyAnyExtensions(MachinePassExtensionPointTy MPEP) { if (GlobalExtensions.isConstructed() && !GlobalExtensions->empty()) { - const Target *Target = &TM->getTarget(); for (const auto &Ext : *GlobalExtensions) { - if (std::get<0>(Ext) == Target && std::get<1>(Ext) == MPEP) - std::get<2>(Ext)(*this); + if (std::get<0>(Ext) == MPEP) + std::get<1>(Ext)(*this); } } } diff --git a/llvm/test/CMakeLists.txt b/llvm/test/CMakeLists.txt --- a/llvm/test/CMakeLists.txt +++ b/llvm/test/CMakeLists.txt @@ -161,6 +161,7 @@ if (NOT WIN32) list(APPEND LLVM_TEST_DEPENDS Bye + MachineExtension ) endif() endif() diff --git a/llvm/test/Feature/machine_extension.ll b/llvm/test/Feature/machine_extension.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Feature/machine_extension.ll @@ -0,0 +1,23 @@ +; RUN: llc < %s 2>&1 | FileCheck %s --check-prefixes BOTH,NOLOAD +; RUN: llc %loadmachineextension < %s 2>&1 | FileCheck %s --check-prefixes BOTH,LOAD +; REQUIRES: examples +; UNSUPPORTED: windows + +; undefined extern functions which can't be elided normally +declare void @_dummy_intrinsic() +declare void @some_real_function() + +; Check that the _dummy_instrinc is passed through in the "NOLOAD" +; test case and replaced or removed by the machine-extension-example +; pass in the "LOAD" test case. Both cases should see the foo function +; label and a call to some_real_function. +; +; BOTH-LABEL: foo +; NOLOAD: _dummy_intrinsic +; LOAD-NOT: _dummy_intrinsic +; BOTH: some_real_function +define void @foo() { + call void @_dummy_intrinsic() + call void @some_real_function() + ret void +} diff --git a/llvm/test/lit.cfg.py b/llvm/test/lit.cfg.py --- a/llvm/test/lit.cfg.py +++ b/llvm/test/lit.cfg.py @@ -209,6 +209,9 @@ if config.build_examples: config.available_features.add('examples') + config.substitutions.append(('%loadmachineextension', + '-load={}/MachineExtension{}'.format(config.llvm_shlib_dir, + config.llvm_shlib_ext))) if config.linked_bye_extension: config.substitutions.append(('%llvmcheckext', 'CHECK-EXT')) diff --git a/llvm/unittests/CodeGen/TargetExtensionTest.cpp b/llvm/unittests/CodeGen/TargetExtensionTest.cpp --- a/llvm/unittests/CodeGen/TargetExtensionTest.cpp +++ b/llvm/unittests/CodeGen/TargetExtensionTest.cpp @@ -2,6 +2,7 @@ #include "llvm/CodeGen/TargetPassConfig.h" #include "llvm/IR/LegacyPassManager.h" #include "llvm/IR/Module.h" +#include "llvm/Support/Host.h" #include "llvm/Support/TargetRegistry.h" #include "llvm/Support/TargetSelect.h" #include "llvm/Target/TargetMachine.h" @@ -12,24 +13,6 @@ using namespace llvm; -static std::unique_ptr makeModule(LLVMContext &Context, - const Target &Target) { - auto M = std::make_unique("target-extensions", Context); - M->setTargetTriple(Target.getName()); - return M; -} - -namespace { -struct TargetMachineTest { - const Target &T; - std::unique_ptr Mod; - std::unique_ptr TM; - LLVMTargetMachine *LTM; - unsigned CallCount; - std::vector Registrars; -}; -} // namespace - static void addMachinePasses(LLVMTargetMachine *LLVMTM) { legacy::PassManager PM; TargetPassConfig *TPC(LLVMTM->createPassConfig(PM)); @@ -41,82 +24,63 @@ } TEST(TargetExtensionTest, ExtensionOrdering) { - InitializeAllTargets(); - InitializeAllTargetMCs(); - InitializeAllAsmPrinters(); - InitializeAllAsmParsers(); + InitializeNativeTarget(); LLVMContext Context; - std::vector TestCases; + Module Mod("target-extensions-test", Context); + Mod.setTargetTriple(llvm::sys::getDefaultTargetTriple()); + std::string Error; + const Target *Target( + TargetRegistry::lookupTarget(Mod.getTargetTriple(), Error)); - for (const Target &T : TargetRegistry::targets()) { - std::unique_ptr Mod(makeModule(Context, T)); - ASSERT_TRUE(Mod.get() != nullptr); - std::unique_ptr TM(T.createTargetMachine( - Mod->getTargetTriple(), "", "", TargetOptions{}, None)); - ASSERT_TRUE(TM.get() != nullptr); - LLVMTargetMachine *LTM(TM->asLLVMTargetMachine()); + ASSERT_NE(Target, nullptr); + std::unique_ptr TM(Target->createTargetMachine( + Mod.getTargetTriple(), "", "", TargetOptions{}, None)); + ASSERT_NE(TM.get(), nullptr); + LLVMTargetMachine *LTM(TM->asLLVMTargetMachine()); - if (LTM) { - TestCases.emplace_back(TargetMachineTest{ - T, - std::move(Mod), - std::move(TM), - LTM, - static_cast(TargetPassConfig::MPEP_EarlyAsPossible), - {}}); - } - } + if (LTM) { + // Register extensions at all possible points + unsigned CallCount = + static_cast(TargetPassConfig::MPEP_EarlyAsPossible); + std::vector Registrars; - // Register all extensions for all targets - for (TargetMachineTest &TestCase : TestCases) { for (unsigned MPEP = TargetPassConfig::MPEP_EarlyAsPossible; MPEP <= TargetPassConfig::MPEP_LateAsPossible; ++MPEP) { // Register an extension at each extension point for this machine // to allow checks for any enums we missed in the implementation - // and they are called in the correct order - TestCase.Registrars.emplace_back( - &TestCase.T, + // and that they are called in the correct order + Registrars.emplace_back( static_cast(MPEP), - [MPEP, &TestCase](TargetPassConfig &tpc) { - // Check that we're called for the right target machine - EXPECT_EQ(TestCase.TM.get(), &tpc.getTM()) - << "TargetMachine pointer mismatch for target " - << TestCase.T.getName(); - // Check that we are called in expected order for the machine - EXPECT_EQ(TestCase.CallCount, MPEP) - << "Calls out of order for target " << TestCase.T.getName(); - ++TestCase.CallCount; + [MPEP, &CallCount, &Mod](TargetPassConfig &tpc) { + if (tpc.getTM().getTargetTriple().str() == + Mod.getTargetTriple()) { + // Check that we are called in expected order for the machine + EXPECT_EQ(CallCount, MPEP) << "Calls arrived out of order"; + ++CallCount; + } }); } - } - // Check that all extensions are called for all targets - for (TargetMachineTest &TestCase : TestCases) { - EXPECT_EQ(TestCase.CallCount, + EXPECT_EQ(CallCount, static_cast(TargetPassConfig::MPEP_EarlyAsPossible)) - << "Initial callcount mismatch for target " << TestCase.T.getName(); - - addMachinePasses(TestCase.LTM); + << "Initial callcount mismatch"; - EXPECT_EQ(TestCase.CallCount, - static_cast(TargetPassConfig::MPEP_LateAsPossible) + 1) - << "Check1 callcount mismatch for target " << TestCase.T.getName(); - } + // Check that all registered extension points get called + addMachinePasses(LTM); - // Unregister and check extensions are not called again - for (TargetMachineTest &TestCase : TestCases) { - EXPECT_EQ(TestCase.CallCount, + EXPECT_EQ(CallCount, static_cast(TargetPassConfig::MPEP_LateAsPossible) + 1) - << "Check2 callcount mismatch for target " << TestCase.T.getName(); + << "Check1 callcount mismatch"; - TestCase.Registrars.clear(); + // Now remove all extensions and check they are not called again + Registrars.clear(); + addMachinePasses(LTM); - addMachinePasses(TestCase.LTM); - EXPECT_EQ(TestCase.CallCount, + EXPECT_EQ(CallCount, static_cast(TargetPassConfig::MPEP_LateAsPossible) + 1) - << "Final callcount mismatch for target " << TestCase.T.getName(); + << "Final callcount mismatch"; } }