diff --git a/llvm/docs/UserGuides.rst b/llvm/docs/UserGuides.rst --- a/llvm/docs/UserGuides.rst +++ b/llvm/docs/UserGuides.rst @@ -54,6 +54,7 @@ TableGenFundamentals Vectorizers WritingAnLLVMPass + WritingAnLLVMNewPMPass WritingAnLLVMBackend yaml2obj @@ -107,6 +108,10 @@ :doc:`WritingAnLLVMPass` Information on how to write LLVM transformations and analyses. +:doc:`WritingAnLLVMNewPMPass` + Information on how to write LLVM transformations under the new pass + manager. + :doc:`Passes` A list of optimizations and analyses implemented in LLVM. diff --git a/llvm/docs/WritingAnLLVMNewPMPass.rst b/llvm/docs/WritingAnLLVMNewPMPass.rst new file mode 100644 --- /dev/null +++ b/llvm/docs/WritingAnLLVMNewPMPass.rst @@ -0,0 +1,209 @@ +==================== +Writing an LLVM Pass +==================== + +.. program:: opt + +.. contents:: + :local: + +Introduction --- What is a pass? +================================ + +The LLVM pass framework is an important part of the LLVM system, because LLVM +passes are where most of the interesting parts of the compiler exist. Passes +perform the transformations and optimizations that make up the compiler, they +build the analysis results that are used by these transformations, and they +are, above all, a structuring technique for compiler code. + +Unlike passes under the legacy pass manager where the pass interface is +defined via inheritance, passes under the new pass manager rely on +concept-based polymorphism, meaning there is no explicit interface (see +comments in ``PassManager.h`` for more details). All LLVM passes inherit from +the CRTP mix-in ``PassInfoMixin``. The pass should have a ``run()`` +method which returns a ``PreservedAnalyses`` and takes in some unit of IR +along with an analysis manager. For example, a function pass would have a +``PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM);`` method. + +We start by showing you how to construct a pass, from setting up the build, +creating the pass, to executing and testing it. Looking at existing passes is +always a great way to learn details. + +Quick Start --- Writing hello world +=================================== + +Here we describe how to write the "hello world" of passes. The "HelloWorld" +pass is designed to simply print out the name of non-external functions that +exist in the program being compiled. It does not modify the program at all, +it just inspects it. + +The code below already exists; feel free to create a pass with a different +name alongside the HelloWorld source files. + +.. _writing-an-llvm-npm-pass-build: + +Setting up the build +-------------------- + +First, configure and build LLVM as described in :doc:`GettingStarted`. + +Next, we will reuse an existing directory (creating a new directory involves +modifying more ``CMakeLists.txt``s and ``LLVMBuild.txt``s than we want). For +this example, we'll use ``llvm/lib/Transforms/HelloNew/HelloWorld.cpp``, +which has already been created. If you'd like to create your own pass, add a +new source file into ``llvm/lib/Transforms/HelloNew/CMakeLists.txt`` under +``HelloWorld.cpp``: + +.. code-block:: cmake + + add_llvm_component_library(LLVMHelloWorld + HelloWorld.cpp + + DEPENDS + intrinsics_gen + ) + +Now that we have the build set up for a new pass, we need to write the code +for the pass itself. + +.. _writing-an-llvm-npm-pass-basiccode: + +Basic code required +------------------- + +Now that the build is setup for a new pass, we just have to write it. + +First we need to define the pass in a header file. We'll create +``llvm/include/llvm/Transforms/HelloNew/HelloWorld.h``. The file should +containing the following boilerplate: + +.. code-block:: c++ + + #ifndef LLVM_TRANSFORMS_HELLONEW_HELLOWORLD_H + #define LLVM_TRANSFORMS_HELLONEW_HELLOWORLD_H + + #include "llvm/IR/PassManager.h" + + namespace llvm { + + class HelloWorldPass : public PassInfoMixin { + public: + PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM); + }; + + } // namespace llvm + + #endif // LLVM_TRANSFORMS_HELLONEW_HELLOWORLD_H + +This creates the class for the pass with a declaration of the ``run()`` +method which actually runs the pass. Inheriting from ``PassInfoMixin`` +sets up some more boilerplate so that we don't have to write it ourselves. + +Our class is in the ``llvm`` namespace so that we don't pollute the global +namespace. + +Next we'll create ``llvm/lib/Transforms/HelloNew/HelloWorld.cpp``, starting +with + +.. code-block:: c++ + + #include "llvm/Transforms/HelloNew/HelloWorld.h" + +... to include the header file we just created. + +.. code-block:: c++ + + using namespace llvm; + +... is required because the functions from the include files live in the llvm +namespace. This should only be done in non-header files. + +Next we have the pass's ``run()`` definition: + +.. code-block:: c++ + + PreservedAnalyses HelloWorldPass::run(Function &F, + FunctionAnalysisManager &AM) { + errs() << F.getName() << "\n"; + return PreservedAnalyses::all(); + } + +... which simply prints out the name of the function to stderr. The pass +manager will ensure that the pass will be run on every function in a module. +The ``PreservedAnalyses`` return value says that all analyses (e.g. dominator +tree) are still valid after this pass since we didn't modify any functions. + +That's it for the pass itself. Now in order to "register" the pass, we need +to add it to a couple places. Add the following to +``llvm\lib\Passes\PassRegistry.def`` in the ``FUNCTION_PASS`` section + +.. code-block:: c++ + + FUNCTION_PASS("helloworld", HelloWorldPass()) + +... which adds the pass under the name "helloworld". + +``llvm\lib\Passes\PassRegistry.def`` is #include'd into +``llvm\lib\Passes\PassBuilder.cpp`` multiple times for various reasons. Since +it constructs our pass, we need to also add the proper #include in +``llvm\lib\Passes\PassBuilder.cpp``: + +.. code-block:: c++ + + #include "llvm/Transforms/HelloNew/HelloWorld.h" + +This should be all the code necessary for our pass, now it's time to compile +and run it. + +Running a pass with ``opt`` +--------------------------- + +Now that you have a brand new shiny pass, we can build :program:`opt` and use +it to run some LLVM IR through the pass. + +.. code-block:: console + + $ ninja -C build/ opt + # or whatever build system/build directory you are using + + $ cat /tmp/a.ll + define i32 @foo() { + %a = add i32 2, 3 + ret i32 %a + } + + define void @bar() { + ret void + } + + $ build/bin/opt -disable-output /tmp/a.ll -passes=helloworld + foo + bar + +Our pass ran and printed the names of functions as expected! + +Testing a pass +-------------- + +Testing our pass is important to prevent future regressions. We'll add a lit +test at ``llvm/test/Transforms/HelloNew/helloworld.ll``. See +:doc:`TestingGuide` for more information on testing. + +.. code-block:: llvm + + $ cat llvm/test/Transforms/HelloNew/helloworld.ll + ; RUN: opt -disable-output -passes=helloworld %s 2>&1 | FileCheck %s + + ; CHECK: {{^}}foo{{$}} + define i32 @foo() { + %a = add i32 2, 3 + ret i32 %a + } + + ; CHECK-NEXT: {{^}}bar{{$}} + define void @bar() { + ret void + } + + $ ninja -C build check-llvm + # runs our new test alongside all other llvm lit tests diff --git a/llvm/docs/WritingAnLLVMPass.rst b/llvm/docs/WritingAnLLVMPass.rst --- a/llvm/docs/WritingAnLLVMPass.rst +++ b/llvm/docs/WritingAnLLVMPass.rst @@ -34,6 +34,10 @@ code, to compiling, loading, and executing it. After the basics are down, more advanced features are discussed. +This document deals with the legacy pass manager. LLVM is transitioning to +the new pass manager, which has its own way of defining passes. For more +details, see :doc:`WritingAnLLVMNewPMPass`. + Quick Start --- Writing hello world =================================== diff --git a/llvm/include/llvm/Transforms/HelloNew/HelloWorld.h b/llvm/include/llvm/Transforms/HelloNew/HelloWorld.h new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/Transforms/HelloNew/HelloWorld.h @@ -0,0 +1,23 @@ +//===-- HelloWorld.h - Example Transformations ------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TRANSFORMS_HELLONEW_HELLOWORLD_H +#define LLVM_TRANSFORMS_HELLONEW_HELLOWORLD_H + +#include "llvm/IR/PassManager.h" + +namespace llvm { + +class HelloWorldPass : public PassInfoMixin { +public: + PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM); +}; + +} // namespace llvm + +#endif // LLVM_TRANSFORMS_HELLONEW_HELLOWORLD_H diff --git a/llvm/lib/Passes/LLVMBuild.txt b/llvm/lib/Passes/LLVMBuild.txt --- a/llvm/lib/Passes/LLVMBuild.txt +++ b/llvm/lib/Passes/LLVMBuild.txt @@ -18,4 +18,4 @@ type = Library name = Passes parent = Libraries -required_libraries = AggressiveInstCombine Analysis Core Coroutines IPO InstCombine ObjCARC Scalar Support Target TransformUtils Vectorize Instrumentation +required_libraries = AggressiveInstCombine Analysis Core Coroutines HelloNew IPO InstCombine ObjCARC Scalar Support Target TransformUtils Vectorize Instrumentation diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp --- a/llvm/lib/Passes/PassBuilder.cpp +++ b/llvm/lib/Passes/PassBuilder.cpp @@ -74,6 +74,7 @@ #include "llvm/Transforms/Coroutines/CoroEarly.h" #include "llvm/Transforms/Coroutines/CoroElide.h" #include "llvm/Transforms/Coroutines/CoroSplit.h" +#include "llvm/Transforms/HelloNew/HelloWorld.h" #include "llvm/Transforms/IPO/AlwaysInliner.h" #include "llvm/Transforms/IPO/ArgumentPromotion.h" #include "llvm/Transforms/IPO/Attributor.h" diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def --- a/llvm/lib/Passes/PassRegistry.def +++ b/llvm/lib/Passes/PassRegistry.def @@ -197,6 +197,7 @@ FUNCTION_PASS("make-guards-explicit", MakeGuardsExplicitPass()) FUNCTION_PASS("post-inline-ee-instrument", EntryExitInstrumenterPass(/*PostInlining=*/true)) FUNCTION_PASS("gvn-hoist", GVNHoistPass()) +FUNCTION_PASS("helloworld", HelloWorldPass()) FUNCTION_PASS("instcombine", InstCombinePass()) FUNCTION_PASS("instcount", InstCountPass()) FUNCTION_PASS("instsimplify", InstSimplifyPass()) diff --git a/llvm/lib/Transforms/CMakeLists.txt b/llvm/lib/Transforms/CMakeLists.txt --- a/llvm/lib/Transforms/CMakeLists.txt +++ b/llvm/lib/Transforms/CMakeLists.txt @@ -6,6 +6,7 @@ add_subdirectory(IPO) add_subdirectory(Vectorize) add_subdirectory(Hello) +add_subdirectory(HelloNew) add_subdirectory(ObjCARC) add_subdirectory(Coroutines) add_subdirectory(CFGuard) diff --git a/llvm/lib/Transforms/HelloNew/CMakeLists.txt b/llvm/lib/Transforms/HelloNew/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/llvm/lib/Transforms/HelloNew/CMakeLists.txt @@ -0,0 +1,6 @@ +add_llvm_component_library(LLVMHelloNew + HelloWorld.cpp + + DEPENDS + intrinsics_gen + ) diff --git a/llvm/lib/Transforms/HelloNew/HelloWorld.cpp b/llvm/lib/Transforms/HelloNew/HelloWorld.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/Transforms/HelloNew/HelloWorld.cpp @@ -0,0 +1,17 @@ +//===-- HelloWorld.cpp - Example Transformations --------------------------===// +// +// 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 "llvm/Transforms/HelloNew/HelloWorld.h" + +using namespace llvm; + +PreservedAnalyses HelloWorldPass::run(Function &F, + FunctionAnalysisManager &AM) { + errs() << F.getName() << "\n"; + return PreservedAnalyses::all(); +} diff --git a/llvm/lib/Transforms/LLVMBuild.txt b/llvm/lib/Transforms/HelloNew/LLVMBuild.txt copy from llvm/lib/Transforms/LLVMBuild.txt copy to llvm/lib/Transforms/HelloNew/LLVMBuild.txt --- a/llvm/lib/Transforms/LLVMBuild.txt +++ b/llvm/lib/Transforms/HelloNew/LLVMBuild.txt @@ -1,4 +1,4 @@ -;===- ./lib/Transforms/LLVMBuild.txt ---------------------------*- Conf -*--===; +;===- ./lib/Transforms/HelloNew/LLVMBuild.txt ------------------*- Conf -*--===; ; ; Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. ; See https://llvm.org/LICENSE.txt for license information. @@ -14,10 +14,9 @@ ; ;===------------------------------------------------------------------------===; -[common] -subdirectories = AggressiveInstCombine Coroutines IPO InstCombine Instrumentation Scalar Utils Vectorize ObjCARC CFGuard - [component_0] -type = Group -name = Transforms -parent = Libraries +type = Library +name = HelloNew +parent = Transforms +library_name = HelloNew +required_libraries = Core diff --git a/llvm/lib/Transforms/LLVMBuild.txt b/llvm/lib/Transforms/LLVMBuild.txt --- a/llvm/lib/Transforms/LLVMBuild.txt +++ b/llvm/lib/Transforms/LLVMBuild.txt @@ -15,7 +15,7 @@ ;===------------------------------------------------------------------------===; [common] -subdirectories = AggressiveInstCombine Coroutines IPO InstCombine Instrumentation Scalar Utils Vectorize ObjCARC CFGuard +subdirectories = AggressiveInstCombine Coroutines HelloNew IPO InstCombine Instrumentation Scalar Utils Vectorize ObjCARC CFGuard [component_0] type = Group diff --git a/llvm/test/Transforms/HelloNew/helloworld.ll b/llvm/test/Transforms/HelloNew/helloworld.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/HelloNew/helloworld.ll @@ -0,0 +1,12 @@ +; RUN: opt -disable-output -passes=helloworld %s 2>&1 | FileCheck %s + +; CHECK: {{^}}foo{{$}} +define i32 @foo() { + %a = add i32 2, 3 + ret i32 %a +} + +; CHECK-NEXT: {{^}}bar{{$}} +define void @bar() { + ret void +} diff --git a/llvm/utils/gn/secondary/llvm/lib/Passes/BUILD.gn b/llvm/utils/gn/secondary/llvm/lib/Passes/BUILD.gn --- a/llvm/utils/gn/secondary/llvm/lib/Passes/BUILD.gn +++ b/llvm/utils/gn/secondary/llvm/lib/Passes/BUILD.gn @@ -8,6 +8,7 @@ "//llvm/lib/Target", "//llvm/lib/Transforms/AggressiveInstCombine", "//llvm/lib/Transforms/Coroutines", + "//llvm/lib/Transforms/HelloNew", "//llvm/lib/Transforms/IPO", "//llvm/lib/Transforms/InstCombine", "//llvm/lib/Transforms/Instrumentation", diff --git a/llvm/utils/gn/secondary/llvm/lib/Transforms/HelloNew/BUILD.gn b/llvm/utils/gn/secondary/llvm/lib/Transforms/HelloNew/BUILD.gn new file mode 100644 --- /dev/null +++ b/llvm/utils/gn/secondary/llvm/lib/Transforms/HelloNew/BUILD.gn @@ -0,0 +1,9 @@ +static_library("HelloNew") { + output_name = "LLVMHelloNew" + deps = [ + "//llvm/lib/Analysis", + "//llvm/lib/IR", + "//llvm/lib/Support", + ] + sources = [ "HelloWorld.cpp" ] +}