1. Obtain the LLVM project

Official Github page: github.com/llvm/llvm-p…

git clone https://github.com/llvm/llvm-project.git

Here I am directly using the main branch, which should correspond to LLVM 13

2. Write passes based on Legacy Pass Manager

Here you can basically refer to the official document LLVMHEllo case Writing an LLVM Pass

In the LLVM project/LLVM/lib/Transforms folder custom pass new folder, there are other official pass case

cd llvm-project/llvm/lib/Transforms

mkdir MyPass

Create a new cmakelists.txt folder under MyPass

write

add_llvm_library( MYPass MODULE
  MYPass.cpp

  DEPENDS
  intrinsics_gen
  PLUGIN_TOOL
  opt
  )
Copy the code

The LLVM project/LLVM/lib/Transforms/CMakeLists. TXT, add folder directory

add_subdirectory(MYPass)
Copy the code

So in my MyPass folder, I’m going to write the pass code mypass.cpp and I’m going to just copy the official case from Hello and change it. The purpose of this pass is to print the method name as it is processed. If you want to write pile Pass, you can refer to llVM-pass to achieve C function pile

3. Build and compile LLVM

The build process can be referred to the official Git

As for the choice of generator, I’m running on MacOS and first tried Ninja because I didn’t select target and didn’t know what target was available. Run cmake –build build. The entire Build folder takes up 50GB. After that, Xcode is the option to build and compile, which is a little friendlier for me. You can more easily select the target you want for compilation. It doesn’t take up so much disk space. You can also refer to this article about compiling LLVM with Xcode: Developing clang plugins: 0 Basics feel the underlying groups

cd /yourPath/llvm-project

cmake -S llvm -B build -G Xcode -DLLVM_ENABLE_PROJECTS="clang; libcxx; libcxxabi"

As for the selection of DLLVM_ENABLE_PROJECTS parameter, some projects cannot generate build files on MacOS. I made a mistake in choosing the LiBC Times.

This part of the build can also be referenced by developing the Clang plugin: 0 Base feel bottom group

After a while, in the llvM-project directory, you can find the build directory, which is the build file you just generated with Xcode.

Open llvm. xcodeProj in the llvm-project/build directory and pop up the automatic creation of Schemes. Selecting Automatically Create Schemes will generate a large number of Schemes. If you know exactly what you need, you can choose to configure it manually. I’m going to go automatic.

Click on Manage Schemes at the bottom of your current Scheme

Filter to find the Scheme you want, double-click to select it, and compile

Here I chose Clang, opt, and MyPass

After compiling, you can find the compiled executable file in the llvm-project/build/Debug/bin directory and the dynamic library of LLVM Pass in the llvm-project/build/Debug/lib directory

4. Test

Create a new C file to test pass.

touch ctest.c

Contents of the document:

int main() {
    return 0;
}
Copy the code

touch ctest.c

So let’s compile it to IR

yourpath/llvm-project/build/Debug/bin/clang -emit-llvm -S ctest.c -o ctest.ll

Using opt tests

yourpath/llvm-project/build/Debug/bin/opt -load yourpath/llvm-project/build/Debug/lib/MYPass.dylib -mypass ctest.ll -enable-new-pm=0

Successful print

Use clang tests

yourpath/llvm-project/build/Debug/bin/clang ctest.c -Xclang -load -Xclang yourpath/llvm-project/build/Debug/lib/MYPass.dylib -flegacy-pass-manager

Successful print

Pay special attention to enable-new-pm of opt and flegacy-pass-manager of clang. Enable fLegacy Pass Manager. You can also refer to LLVM IR’s first Pass: Hello Pass

The flegacy-pass-manager parameter will be removed in the future

5. Use in Xcode

Create a New Xcode iPhone APP project.

Add user-defined in Build Settings

CC yourpath/llvm-project/build/Debug/bin/clang
CXX yourpath/llvm-project/build/Debug/bin/clang++
Copy the code

Let Xcode use the clang we compiled

Add the clang load Pass dynamic library parameter to Other C Flags in Build Settings

-Xclang -load -Xclang yourpath/llvm-project/build/Debug/lib/MYPass.dylib -flegacy-pass-manager

Eable index-while-building Funcitonality in Build Settings is changed to NO

Command +B starts compiling the project and takes effect

At this point, pass use based on legacy Pass Manager ends.

6. Pass writing based on new Pass Manager

You can refer to the official documentation here, and the steps below are described in the official documentation

New file LLVM project/LLVM/include/LLVM/Transforms/Utils/MyNewPass. H

See helloWorld.h in the same directory

#ifndef LLVM_TRANSFORMS_UTILS_MyNewPass_H
#define LLVM_TRANSFORMS_UTILS_MyNewPass_H

#include "llvm/IR/PassManager.h"

namespace llvm {

class MyNewPass : public PassInfoMixin<MyNewPass> {
public:
  PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM);
};

} // namespace llvm

#endif // LLVM_TRANSFORMS_UTILS_HELLOWORLD_H
Copy the code

New file LLVM project/LLVM/lib/Transforms/Utils/MyNewPass CPP, code is still the same, printing the method name

#include "llvm/Transforms/Utils/MyNewPass.h"

using namespace llvm;

PreservedAnalyses MyNewPass::run(Function &F,
                                      FunctionAnalysisManager &AM) {
  errs() << F.getName() << "\n";
  return PreservedAnalyses::all();
}
Copy the code

Modify the file LLVM – project/LLVM/lib/Passes/PassRegistry def, add a FUNCTION_PASS (” mynewpass, “mynewpass ())

Modify the file LLVM – project/LLVM/lib/Passes/PassBuilder CPP, new header files into # include “LLVM/Transforms/Utils/MyNewPass. H”

Introduce the newly created pass header and code file in Xcode’s LLVMTransformUtils folder

Recompile opt in Xcode

7. Test

Create a new IR file

touch a.ll

Write IR code of official case

define i32 @foo() {
  %a = add i32 2, 3
  ret i32 %a
}

define void @bar() {
  ret void
}
Copy the code

Make the recompiled opt use pass

llvm-project/build/Debug/bin/opt -disable-output a.ll -passes=mynewpass
Copy the code

The test pass

Use opt to process the ctest.ll of the previous example with pass

llvm-project/build/Debug/bin/opt -disable-output ctest.ll -passes=mynewpass

The test failed with no output

Ctest. ll is compiled in clang from the previous example. It is mentioned at the end of the official documentation that pass has the possibility of being skipped and that the isRequired method needs to be added

Modify the LLVM project/LLVM/include/LLVM/Transforms/Utils/MyNewPass. H, increase isRequired method

#ifndef LLVM_TRANSFORMS_UTILS_MyNewPass_H #define LLVM_TRANSFORMS_UTILS_MyNewPass_H #include "llvm/IR/PassManager.h" namespace llvm { class MyNewPass : public PassInfoMixin<MyNewPass> { public: PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM); static bool isRequired() { return true; }}; } // namespace llvm #endif // LLVM_TRANSFORMS_UTILS_HELLOWORLD_HCopy the code

Recompile opt

Test ctest.ll again and the test passes

8. Use the pass of the new Pass Manager in Xcode

In legcy pass manager, we are through the Xlang to clang and ginseng – Xclang – load – Xclang LLVM – project/build/Debug/lib/MYPass dylib, pass to use. So can I pass -passes= myNewPass?

Tried, it didn’t work

Check the LLVM project/build/Debug/bin/clang — help – hidden, feeling should be – fpass – the plugin

I also have a post on StackOverflow that uses the -fpass-plugin to load

Here is another way to add pass to the clang compilation process, as described in the official documentation

See how Clang in Backendutil. CPP adds passes, as noted in the official documentation. In order to make the printing more clear, I in the LLVM project/LLVM/lib/Transforms/Utils/MyNewPass CPP added a line

#include "llvm/Transforms/Utils/MyNewPass.h"

using namespace llvm;

PreservedAnalyses MyNewPass::run(Function &F,
                                      FunctionAnalysisManager &AM) {
  errs() << "MyNewPass: ";
  errs() << F.getName() << "\n";
  return PreservedAnalyses::all();
}
Copy the code

Modify the project BackendUtil. CPP introduced header file # include “LLVM/Transforms/Utils/MyNewPass. H” search registerPipelineStartEPCallback locate add other pass Add my pass

PB.registerPipelineStartEPCallback(
        [](ModulePassManager &MPM, OptimizationLevel Level) {
            MPM.addPass(
                createModuleToFunctionPassAdaptor(MyNewPass()));
        });
Copy the code

You can see that the other passes are added conditionally. For example, determine the OptimizationLevel. So here I’m going to add my pass unconditionally.

Recompile clang

Go back to the Xcode iPhone test project you created earlier and delete the Other C Flags in Build Settings to load the Legacy Pass parameter

Clean the Build cache, product-clean Build Folder, and compile the project

Test success

9. Finally, I want to do the swift code peg, insert some code into the swift function. Considering that swift’s compiler front-end is SWIFTC and LLVM Pass is used for LLVM IR, I want to see how to use LLVM Pass. In practice, the use of LLVM Pass is closely tied to the compiler front end. Either clang-load is required or the clANG load pass code needs to be modified. I guess I’ll have to take a look at SwifTC.