diff --git a/llvm/docs/WritingAnLLVMPass.rst b/llvm/docs/WritingAnLLVMPass.rst --- a/llvm/docs/WritingAnLLVMPass.rst +++ b/llvm/docs/WritingAnLLVMPass.rst @@ -1332,6 +1332,14 @@ Here the command option is "``mypass``", with ``createDefaultMyPass`` as the default creator. +Out-of-tree MachineIR passes +---------------------------- + +MachineIR passes can be implemented out-of-tree and loaded using the ``--load`` +flag of ``llc``, or ``clang`` thanks to ``-Xclang -load -Xclang Pass.so``. +Examples of such passes are provided in the ``examples/OutOfTreeMIR`` +directory. + Using GDB with dynamically loaded passes ---------------------------------------- diff --git a/llvm/examples/CMakeLists.txt b/llvm/examples/CMakeLists.txt --- a/llvm/examples/CMakeLists.txt +++ b/llvm/examples/CMakeLists.txt @@ -9,6 +9,7 @@ add_subdirectory(SpeculativeJIT) add_subdirectory(Bye) add_subdirectory(ThinLtoJIT) +add_subdirectory(OutOfTreeMIR) if(LLVM_ENABLE_EH AND (NOT WIN32) AND (NOT "${LLVM_NATIVE_ARCH}" STREQUAL "ARM")) add_subdirectory(ExceptionDemo) diff --git a/llvm/examples/OutOfTreeMIR/CMakeLists.txt b/llvm/examples/OutOfTreeMIR/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/llvm/examples/OutOfTreeMIR/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(FunctionPass) +add_subdirectory(ModulePass) diff --git a/llvm/examples/OutOfTreeMIR/FunctionPass/CMakeLists.txt b/llvm/examples/OutOfTreeMIR/FunctionPass/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/llvm/examples/OutOfTreeMIR/FunctionPass/CMakeLists.txt @@ -0,0 +1,4 @@ +add_llvm_library(MIRFunctionPass MODULE + FunctionPass.cpp + BUILDTREE_ONLY +) diff --git a/llvm/examples/OutOfTreeMIR/FunctionPass/FunctionPass.cpp b/llvm/examples/OutOfTreeMIR/FunctionPass/FunctionPass.cpp new file mode 100644 --- /dev/null +++ b/llvm/examples/OutOfTreeMIR/FunctionPass/FunctionPass.cpp @@ -0,0 +1,39 @@ +//===-- FunctionPass.cpp - Out-of-tree MachineIR pass example -------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include +#include +#include +#include +#include +#include + +using namespace llvm; + +namespace { +struct MIRPrinter : public MachineFunctionPass { + static char ID; + + MIRPrinter() : MachineFunctionPass(ID) {} + + bool runOnMachineFunction(MachineFunction &MF) override { + printMIR(errs(), MF); + return true; + } +}; + +char MIRPrinter::ID = 0; +} // namespace + +MachineFunctionPass *createMIRPrinterPass() { return new MIRPrinter{}; } + +static void callback(TargetPassConfig const &, legacy::PassManagerBase &PM) { + PM.add(createMIRPrinterPass()); +} + +static RegisterMIRPasses Register(TargetPassConfig::EP_OptimizerLast, callback); diff --git a/llvm/examples/OutOfTreeMIR/ModulePass/CMakeLists.txt b/llvm/examples/OutOfTreeMIR/ModulePass/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/llvm/examples/OutOfTreeMIR/ModulePass/CMakeLists.txt @@ -0,0 +1,4 @@ +add_llvm_library(MIRModulePass MODULE + ModulePass.cpp + BUILDTREE_ONLY +) diff --git a/llvm/examples/OutOfTreeMIR/ModulePass/ModulePass.cpp b/llvm/examples/OutOfTreeMIR/ModulePass/ModulePass.cpp new file mode 100644 --- /dev/null +++ b/llvm/examples/OutOfTreeMIR/ModulePass/ModulePass.cpp @@ -0,0 +1,54 @@ +//===-- ModulePass.cpp - Out-of-tree MachineIR pass example ---------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include +#include +#include +#include +#include +#include +#include + +using namespace llvm; + +namespace { +struct MIRPrinter : public ModulePass { + static char ID; + + MIRPrinter() : ModulePass(ID) {} + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.addRequired(); + AU.addPreserved(); + AU.setPreservesAll(); + ModulePass::getAnalysisUsage(AU); + } + + bool runOnModule(Module &M) override { + MachineModuleInfo &MMI = + getAnalysis().getMMI(); + + for (Function &F : M) { + if (MachineFunction *MF = MMI.getMachineFunction(F)) { + printMIR(errs(), *MF); + } + } + return true; + } +}; + +char MIRPrinter::ID = 0; +} // namespace + +ModulePass *createMIRPrinterPass() { return new MIRPrinter{}; } + +static void callback(TargetPassConfig const &, legacy::PassManagerBase &PM) { + PM.add(createMIRPrinterPass()); +} + +static RegisterMIRPasses Register(TargetPassConfig::EP_OptimizerLast, callback); diff --git a/llvm/include/llvm/CodeGen/RegisterMIRPasses.h b/llvm/include/llvm/CodeGen/RegisterMIRPasses.h new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/CodeGen/RegisterMIRPasses.h @@ -0,0 +1,31 @@ +//==- include/llvm/CodeGen/RegisterMIRPasses.h - OOT MIR passes ---*- C++ +//-*-==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file contains utilities to register out-of-tree MIR passes. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CODEGEN_REGISTERMIRPASSES_H +#define LLVM_CODEGEN_REGISTERMIRPASSES_H + +#include "llvm/CodeGen/TargetPassConfig.h" + +namespace llvm { + +class RegisterMIRPasses { +public: + RegisterMIRPasses(TargetPassConfig::ExtensionPointTy Ty, + TargetPassConfig::ExtensionFn Fn) { + TargetPassConfig::addGlobalExtension(Ty, Fn); + } +}; + +} // namespace llvm + +#endif 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 @@ -13,6 +13,7 @@ #ifndef LLVM_CODEGEN_TARGETPASSCONFIG_H #define LLVM_CODEGEN_TARGETPASSCONFIG_H +#include "llvm/ADT/DenseMap.h" #include "llvm/Pass.h" #include "llvm/Support/CodeGen.h" #include @@ -81,7 +82,32 @@ /// This is an ImmutablePass solely for the purpose of exposing CodeGen options /// to the internals of other CodeGen passes. class TargetPassConfig : public ImmutablePass { +public: + typedef void (*ExtensionFn)(const TargetPassConfig &Builder, + legacy::PassManagerBase &PM); + + enum ExtensionPointTy { + /// This extension point allows adding passes before + /// any other transformations. + EP_EarlyAsPossible, + + /// This extension point allows adding passes before register allocation. + EP_BeforeRA, + /// This extension point allows adding passes after register allocation. + EP_AfterRA, + /// This extension point allows adding passes that / run after everything + // else. + EP_OptimizerLast, + }; + static constexpr size_t EP_EnumSize = EP_OptimizerLast + 1; + + static void addGlobalExtension(ExtensionPointTy Ty, ExtensionFn Fn); + void addExtension(ExtensionPointTy Ty, ExtensionFn Fn); + void addExtensionsToPM(ExtensionPointTy ETy) const; + private: + SmallVector Extensions[EP_EnumSize]; + PassManagerBase *PM = nullptr; AnalysisID StartBefore = nullptr; AnalysisID StartAfter = nullptr; 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 @@ -212,6 +212,30 @@ cl::desc("Stop compilation before a specific pass"), cl::value_desc("pass-name"), cl::init(""), cl::Hidden); +static ManagedStatic, + TargetPassConfig::EP_EnumSize>> + GlobalExtensions; + +void TargetPassConfig::addGlobalExtension(TargetPassConfig::ExtensionPointTy Ty, + TargetPassConfig::ExtensionFn Fn) { + (*GlobalExtensions)[Ty].push_back(Fn); +} + +void TargetPassConfig::addExtension(ExtensionPointTy Ty, ExtensionFn Fn) { + Extensions[Ty].push_back(Fn); +} + +void TargetPassConfig::addExtensionsToPM(ExtensionPointTy ETy) const { + if (GlobalExtensions.isConstructed()) { + for (auto Fn : (*GlobalExtensions)[ETy]) { + Fn(*this, *PM); + } + } + for (auto Fn : Extensions[ETy]) { + Fn(*this, *PM); + } +} + /// Allow standard passes to be disabled by command line options. This supports /// simple binary flags that either suppress the pass or do nothing. /// i.e. -disable-mypass=false has no effect. @@ -908,6 +932,8 @@ void TargetPassConfig::addMachinePasses() { AddingMachinePasses = true; + addExtensionsToPM(EP_EarlyAsPossible); + // Add passes that optimize machine instructions in SSA form. if (getOptLevel() != CodeGenOpt::None) { addMachineSSAOptimization(); @@ -920,6 +946,8 @@ if (TM->Options.EnableIPRA) addPass(createRegUsageInfoPropPass()); + addExtensionsToPM(EP_BeforeRA); + // Run pre-ra passes. addPreRegAlloc(); @@ -940,6 +968,8 @@ addPass(&FixupStatepointCallerSavedID); + addExtensionsToPM(EP_AfterRA); + // Insert prolog/epilog code. Eliminate abstract frame index references... if (getOptLevel() != CodeGenOpt::None) { addPass(&PostRAMachineSinkingID); @@ -1020,6 +1050,8 @@ // Add passes that directly emit MI after all other MI passes. addPreEmitPass2(); + addExtensionsToPM(EP_OptimizerLast); + AddingMachinePasses = false; } diff --git a/llvm/test/Examples/OutOfTreeMIR/llc.test b/llvm/test/Examples/OutOfTreeMIR/llc.test new file mode 100644 --- /dev/null +++ b/llvm/test/Examples/OutOfTreeMIR/llc.test @@ -0,0 +1,12 @@ +; RUN: llc %s --load=%llvmshlibdir/MIRFunctionPass%shlibext -o /dev/null 2>&1 | FileCheck %s +; RUN: llc %s --load=%llvmshlibdir/MIRModulePass%shlibext -o /dev/null 2>&1 | FileCheck %s + + +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +define dso_local i32 @foo() { +entry: + ; CHECK: MOV32ri 42 + ret i32 42 +}