llvm Writing a custom pass 用llvm编写自己的pass

《Getting Started with LLVM Core Libraries》

Writing a custom pass

Suppose that we want to count the number of arguments for each function in a program, outputting the function name as well. Let’s write a pass to do this. First, we need to choose the right Pass subclass. FunctionPass seems appropriate since
we require no particular function order and do not need to delete anything.

We name our pass FnArgCnt and place it under the LLVM source code tree:

$ cd <llvm_source_tree>
$ mkdir lib/Transforms/FnArgCnt
$ cd lib/Transforms/FnArgCnt

The FnArgCnt.cpp file, located at lib/Transforms/FnArgCnt , needs to contain the pass implementation, which is reproduced here:

//First, we include the necessary header files and gather symbols from the
llvm namespace:
#include "llvm/IR/Function.h"
#include "llvm/Pass.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;

//Next, we declare  FnArgCnt —our  FunctionPass subclass—and implement the
//main pass mechanism in the  runOnFunction() method. From within each function context,
//we print the function name and the number of arguments it receives. 
//The method returns  false because no changes have been made to the analyzed function.
//The code of our subclass is as follows:
namespace {
class FnArgCnt : public FunctionPass {
public:
static char ID;
FnArgCnt() : FunctionPass(ID) {}
virtual bool runOnFunction(Function &F) {
errs() << "FnArgCnt --- ";
errs() << F.getName() << ": ";
errs() << F.getArgumentList().size() << '\n';
return false;
}
};
}
//The ID is determined internally by LLVM to identify a pass,
// and it can be declared with any value:
char FnArgCnt::ID = 0;
//Finally, we deal with the pass registration mechanism, which registers the pass with
//the current pass manager during the pass load time:
static RegisterPass<FnArgCnt> X("fnargcnt", "Function Argument
Count Pass", false, false);

The first argument, fnargcnt , is the name used by the opt tool to identify the pass, whereas the second argument contains its extended name. The third argument tells us whether the pass changes the current CFG, and the last returns true only if it implements an analysis pass.

Building and running your new pass with the LLVM build system

To compile and install the pass, we need a Makefile within the same directory of the source code. Different from our previous projects, we are not building a standalone tool anymore, and this Makefile is integrated in the LLVM build system. Since it relies on the LLVM main Makefile, which implements a great deal of rules, its contents are considerably simpler than a standalone Makefile. Refer to the following code:

# Makefile for FnArgCnt pass
# Path to top level of LLVM hierarchy
LEVEL = ../../..
# Name of the library to build
LIBRARYNAME = LLVMFnArgCnt
# Make the shared library become a loadable module so the tools can
# dlopen/dlsym on the resulting library.
LOADABLE_MODULE = 1
# Include the makefile implementation stuff
include $(LEVEL)/Makefile.common

The comments in the Makefile are self-explanatory, and a shared library is created using the common LLVM Makefile. Using this infrastructure, our pass is installed together with other standard passes and can be loaded directly by opt , but it requires that you rebuild your LLVM installation.
Makefile中的注释是不言而喻的,并且使用公共LLVM Makefile创建一个共享库。使用这个基础设施,我们的pass与其他标准过程一起安装,并且可以通过opt直接加载,但是它需要您重新构建LLVM安装。

We also want our pass to be compiled in the object directory, and we need to include our pass in the Transforms directory, Makefile . Thus, in lib/Transforms/Makefile , the PARALLEL_DIRS variable needs to be changed to include the FnArgCnt pass:

PARALLEL_DIRS = Utils Instrumentation Scalar InstCombine IPO
Vectorize Hello ObjCARC FnArgCnt

Building and running your new pass with your own Makefile

The dependence on the LLVM build system can be annoying, such as needing to reconfigure the entire project or rebuild all the LLVM tools with our new code. However, we can create a standalone Makefile that compiles our pass outside the LLVM source tree in the same way that we have been building our projects in previously. The comfort of being independent from the LLVM source tree is sometimes worth the extra effort of building your own Makefile.
对LLVM构建系统的依赖可能很烦人,比如需要重新配置整个项目或使用新代码重新构建所有LLVM工具。但是,我们可以创建一个独立的Makefile,它在LLVM源代码树之外编译pass,方法与我们以前构建项目的方式相同。独立于LLVM源代码树的舒适性有时值得额外努力构建自己的Makefile。

We will base our standalone Makefile on the one used to build a tool in Chapter 3, Tools and Design. The challenge now is that we are not building a tool anymore, but a shared library that has the code of our pass and will be loaded on demand by the opt tool.
我们将以第3章“工具和设计”中用于构建工具的Makefile为基础。现在的挑战是,我们不再构建一个工具,而是一个共享库,它包含我们的通行证代码,并将按需由opt工具加载。

First, we create a separate folder for our project that does not live inside the LLVM source tree. We put the FnArgCnt.cpp file in this folder with the pass implementation. Second, we create the Makefile as follows:

LLVM_CONFIG?=llvm-config
ifndef VERBOSE
QUIET:=@
endif
SRC_DIR?=$(PWD)
LDFLAGS+=$(shell $(LLVM_CONFIG) --ldflags)
COMMON_FLAGS=-Wall -Wextra
CXXFLAGS+=$(COMMON_FLAGS) $(shell $(LLVM_CONFIG) --cxxflags)
CPPFLAGS+=$(shell $(LLVM_CONFIG) --cppflags) -I$(SRC_DIR)
ifeq ($(shell uname),Darwin)
LOADABLE_MODULE_OPTIONS=-bundle -undefined dynamic_lookup
else
LOADABLE_MODULE_OPTIONS=-shared -Wl,-O1
endif
FNARGPASS=fnarg.so
FNARGPASS_OBJECTS=FnArgCnt.o
default: $(FNARGPASS)
%.o : $(SRC_DIR)/%.cpp
@echo Compiling $*.cpp
$(QUIET)$(CXX) -c $(CPPFLAGS) $(CXXFLAGS) $<
$(FNARGPASS) : $(FNARGPASS_OBJECTS)
@echo Linking $@
$(QUIET)$(CXX) -o $@ $(LOADABLE_MODULE_OPTIONS) $(CXXFLAGS)
$(LDFLAGS) $^
clean::
$(QUIET)rm -f $(FNARGPASS) $(FNARGPASS_OBJECTS)

The novelties (highlighted in the preceding code) in this Makefile, in comparison to the one from Chapter 3, Tools and Design, is the conditional definition of the LOADABLE_MODULE_OPTIONS variable, which is used in the command line that links our shared library. It defines the platform-dependent set of compiler flags that instructs it to generate a shared library instead of an executable. For Linux, for example, it uses the -shared flag to create a shared library as well as the -Wl,-O1 flag, which passes the -O1 flag to GNU ld. This flag asks the GNU linker to perform symbol table optimizations, reducing the library load time. If you do not use GNU linker, you can omit this flag.
与第3章“工具与设计”中的定义相比,这个Makefile中的新特性(在前面的代码中突出显示)是可加载的“MODULE”选项变量的条件定义,该变量在链接共享库的命令行中使用。它定义了一组依赖于平台的编译器标志,指示它生成共享库而不是可执行文件。例如,对于Linux,它使用-shared标志创建一个共享库,以及-Wl,-O1标志,它将-O1标志传递给gnu ld。此标志要求GNU链接器执行符号表优化,从而减少库加载时间。如果不使用GNU链接器,则可以省略此标志。

We also removed the llvm-config --libs shell command from our linker command line. This command was used to supply the libraries that our project links to. Since we know that the opt executable already has all the necessary symbols that we use, we simply do not include any redundant libraries, allowing for faster link times.
To build your project, use the following command line:
$ make
To run your pass that was built in fnarg.so , use the following command lines:
$ opt -load=fnarg.so -fnargcnt < sum.bc > /dev/null
FnArgCnt — sum: 2

Summary

The LLVM IR is the middle point between the frontend and backend. It is the place where target-independent optimizations take place. In this chapter, we explored the tools for the manipulation of the LLVM IR, the assembly syntax, and how to write a custom IR code generator. Moreover, we showed how the pass interface works, how to apply optimizations, and then provided examples on how to write our own IR transform or analysis passes.

Writing the Makefile

Linking with LLVM libraries requires the use of long command lines that are not practical to write without the help of a build system. We show you a Makefile in the following code, based on the one used in DragonEgg , to accomplish this task, explaining each part as we present it. If you copy and paste this code, you will lose the tab character; remember that Makefiles depend on using the tab character to specify the commands that define a rule. Thus, you should manually insert them:
与LLVM库链接需要使用很长的命令行,如果没有构建系统的帮助,这些命令行是不实用的。我们将在下面的代码中向您展示一个Makefile,它基于DragonEgg中使用的Makefile来完成这个任务,并在演示时解释每个部分。如果复制并粘贴此代码,将丢失制表符;请记住,makefile依赖于使用制表符来指定定义规则的命令。因此,您应该手动插入它们:

LLVM_CONFIG?=llvm-config
ifndef VERBOSE
QUIET:=@
endif

SRC_DIR?=$(PWD)
LDFLAGS+=$(shell $(LLVM_CONFIG) --ldflags)
COMMON_FLAGS=-Wall -Wextra
CXXFLAGS+=$(COMMON_FLAGS) $(shell $(LLVM_CONFIG) --cxxflags)
CPPFLAGS+=$(shell $(LLVM_CONFIG) --cppflags) -I$(SRC_DIR)

This first part defines the first Makefile variables that will be used as the compiler flags. The first variable determines the location of the llvm-config program. In this case, it needs to be in your path. The llvm-config tool is an LLVM program that prints a variety of useful information to build an external project that needs to be linked with the LLVM libraries.
When defining the set of flags to be used in the C++ compiler, for instance, notice that we ask Make to launch the llvm-config --cxxflags shell command line, which prints the set of C++ flags that you used to compile the LLVM project. This way, we make the compilation of the sources of our project compatible with LLVM sources. The last variable defines the set of flags that are to be passed to the compiler preprocessor.
第一部分定义了将用作编译器标志的第一个Makefile变量。第一个变量确定llvm config程序的位置。在这种情况下,它必须在你的道路上。llvm配置工具是一个llvm程序,它打印各种有用的信息,以构建需要与llvm库链接的外部项目。
例如,在定义C++编译器中使用的标志集时,请注意,我们要求启动LLVM CONFIG-CXFLUBACK shell命令行,它打印用于编译LLVM项目的C++标志集。这样,我们就可以使项目源代码的编译与LLVM源代码兼容。最后一个变量定义要传递给编译器预处理器的标志集。

工作中pass编写,细节流程如下:
cpp文件:

#include "llvm/Pass.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;

#define DEBUG_TYPE "one"

//STATISTIC(OneCounter, "Counts number of functions greeted");

namespace {
  // One - The first implementation, without getAnalysisUsage.
  struct One : public FunctionPass {
    static char ID; // Pass identification, replacement for typeid
    One() : FunctionPass(ID) {}

    bool runOnFunction(Function &F) override {
      errs() << "One: ";
      return false;
    }
  };
}

char One::ID = 1;
static RegisterPass<One> X("one", "Hello World Pass");
<create one.cpp with pass name one in any location>

$ clang++ -c one.cpp `llvm-config --cxxflags`
$ clang++ -shared -o one.so one.o `llvm-config --ldflags`
$ opt -load ./one.so -one < hello.bc > /dev/null

其中llvm-config --cxxflags指令不了,可以在terminal输出路径,替换进行编译,指令一生成.o文件。

当在remove struct多个例子中,需要同时编译多个cpp文件成.o文件,再将.o文件编译为一个.so文件。

clang++ -c -w remove_struct.cpp memory_table.cpp struct_type_DAG.cpp remove_tool_function.cpp  -I/usr/lib/llvm-6.0/include -std=c++0x -fuse-ld=gold -Wl,--no-keep-files-mapped -Wl,--no-map-whole-files -fPIC -fvisibility-inlines-hidden -Werror=date-time -std=c++11 -Wall -W -Wno-unused-parameter -Wwrite-strings -Wcast-qual -Wno-missing-field-initializers -pedantic -Wno-long-long -Wno-maybe-uninitialized -Wdelete-non-virtual-dtor -Wno-comment -ffunction-sections -fdata-sections -O2 -DNDEBUG  -fno-exceptions -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS

clang++ remove_tool_function.o remove_struct.o memory_table.o struct_type_DAG.o -shared -o a.so -L/usr/lib/llvm-6.0/lib

opt -load ./a.so -remove_struct < 1.bc > /dev/null -o 2.bc

实际clang_run.tcl中的调用程序:
不能有errs返回值,否则跳出tcl脚本

set design digit_recognition 
set out_dir output
# Set device, which will load the specific *.tcl
set_device Virtex7 
set_parameter CLOCK_PERIOD 10

exec clang ${design}.c -emit-llvm -c -fno-builtin -O1 -fno-vectorize -fno-slp-vectorize -fno-unroll-loops  -o ${design}_O1.bc

exec opt -mem2reg -loops -loop-simplify < ${design}_O1.bc > ${design}_mem2reg.bc

exec opt -internalize-public-api-list=main -internalize -globaldce ${design}_mem2reg.bc -o ${design}_internalize.bc

exec opt -instcombine -std-link-opts < ${design}_internalize.bc -o ${design}_standard.bc

#exec opt -basicaa -loop-simplify -instcombine ${name}.1.bc -o ${name}.bc

exec opt -load=../../IR_opt/convert_constantexpr_inst/convert_constantexpr_inst.so -convert_constantexpr_inst  < ${design}_standard.bc > /dev/null -o convert_constantexpr_inst.bc

exec opt -load=../../IR_opt/remove_struct/remove_struct.so -remove_struct  < convert_constantexpr_inst.bc > /dev/null -o remove_struct.bc

exec opt -load=../../IR_opt/opt_getelementptr/opt_getelementptr.so -opt_getelementptr  < remove_struct.bc > /dev/null -o opt_getelementptrs.bc

exec opt -load=../../IR_opt/pointer_arg_process/pointer_arg_process.so -pointer_arg_process  < opt_getelementptrs.bc > /dev/null -o pointer_arg_process.bc

exec opt -load=../../IR_opt/memintrinsic_process/memintrinsic_process.so -memintrinsic_process  < pointer_arg_process.bc > /dev/null -o memintrinsic_process.bc

exec opt -globaldce -gvn -dce -deadargelim -die -dse -functionattrs memintrinsic_process.bc > opt.bc

#exec opt -load=../../IR_opt/opt_getelementptr/opt_getelementptr.so -opt_getelementptr  < ${design}_O1.bc > /dev/null -o res.bc


if {[file isdirectory $out_dir]} {
  file delete -force $out_dir
}
file mkdir $out_dir

run_hw opt.bc  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值