LLVM 初探(2) Optimization Pass 编写

Dead code elimination Pass

源码如下,发现llvm cookbook用的api挺老的,把旧的api换成新的了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include "llvm/Transforms/Scalar.h"
#include "llvm/ADT/DepthFirstIterator.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/CFG.h"
#include "llvm/IR/InstIterator.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/IntrinsicInst.h"
#include "llvm/IR/Function.h"
#include "llvm/Pass.h"
using namespace llvm;

namespace {

struct MYADCE : public FunctionPass {
static char ID;
MYADCE() : FunctionPass(ID) {
}
bool runOnFunction(Function& F) override;
void getAnalysisUsage(AnalysisUsage& AU) const override{
AU.setPreservesCFG();
}
};
}

char MYADCE::ID = 0;

bool MYADCE::runOnFunction(Function& F) {
if (skipFunction(F))
return false;
SmallPtrSet<Instruction*, 32> Alive;
SmallVector<Instruction*, 128> Worklist;
for(Instruction & I:instructions(F)){
if(isa<TerminatorInst>(I) || isa<DbgInfoIntrinsic>(I)
|| isa<LandingPadInst>(I) || I.mayHaveSideEffects()){
Alive.insert(&I);
Worklist.push_back(&I);
}
}
while(!Worklist.empty()){
Instruction* Curr = Worklist.pop_back_val();
for( Use &OI : Curr->operands()) {
if (Instruction* Inst = dyn_cast<Instruction>(OI))
if(Alive.insert(Inst).second)
Worklist.push_back(Inst);
}
}
for(Instruction &I : instructions(F)){
if(!Alive.count(&I)){
Worklist.push_back(&I);
I.dropAllReferences();
}
}
for(Instruction* I : Worklist){
I->eraseFromParent();
}
return !Worklist.empty();

return false;
}
static RegisterPass<MYADCE> X("myadce", "My Aggressive Dead Code Elimination", false, false);

测试用IR如下

1
2
3
4
5
declare i32 @strlen(i8*) readonly nounwind
define void @test() {
call i32 @strlen( i8* null )
ret void
}

Pass首先是遍历Instruction,寻找这4类Instruction:TerminatorInst,DbgInfoIntrinsic,LandingPadInst,mayHaveSideEffects,也就是最后有意义的指令,然后用bfs反向遍历回去,类似于污点传播的感觉,把所有有意义的指令都插入到Alive里面,最后把不在Alive里面的指令都去除掉

Inlining Transformation Pass

源码如下,因为cookbook的都是旧版本的,所以我下了源码,用lib/Transforms/IPO/InlineSimple.cpp魔改了下cookbook的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include "llvm/Transforms/IPO.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/Analysis/AliasAnalysis.h"
#include "llvm/Analysis/AssumptionCache.h"
#include "llvm/Analysis/CallGraph.h"
#include "llvm/Analysis/InlineCost.h"
#include "llvm/IR/CallSite.h"
#include "llvm/IR/CallingConv.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/IntrinsicInst.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Type.h"
#include "llvm/Transforms/IPO/Inliner.h"

using namespace llvm;
namespace {

class MyInliner : public LegacyInlinerBase {

public:
MyInliner(): LegacyInlinerBase(ID) {}

static char ID;

InlineCost getInlineCost(CallSite CS) override;
void getAnalysisUsage(AnalysisUsage &AU) const override;

bool runOnSCC(CallGraphSCC & SCC) override;
bool doFinalization(CallGraph &CG) override {
return removeDeadFunctions(CG,true);
}
};
}

char MyInliner::ID = 0;
InlineCost MyInliner::getInlineCost(CallSite CS){
Function* Callee = CS.getCalledFunction();
if(Callee && !Callee->isDeclaration() &&
CS.hasFnAttr(Attribute::AlwaysInline))
return InlineCost::getAlways();
return InlineCost::getNever();
}

bool MyInliner::runOnSCC(CallGraphSCC & SCC) {
return LegacyInlinerBase::runOnSCC(SCC);
}

void MyInliner::getAnalysisUsage(AnalysisUsage &AU) const {
AU.addRequired<TargetTransformInfoWrapperPass>();
LegacyInlinerBase::getAnalysisUsage(AU);
}

static RegisterPass<MyInliner> X("myinliner", "My Inliner", false, false);

测试IR如下

1
2
3
4
5
6
7
define i32 @inner1() alwaysinline {
ret i32 1
}
define i32 @outer1() {
%r = call i32 @inner1()
ret i32 %r
}

运行效果

这个Pass是将标志了alwaysinline的函数全部都内联,其他都不内联