LLVM Pass 简介(5)
本节将介绍一种特殊的IR指令——PHINode。
关于PHI,首先需要介绍SSA静态单赋值https://en.wikipedia.org/wiki/Static_single_assignment_form
为了实现SSA,需要将类似的branch语句:
int max(int a, int b) { if(a>b) return a; else return b; }
转换为(不是真的这样在语言层面进行转换,是IR,为了说明方便):
int max(int a, int b) { int ret0, ret1; if(a>b) ret0= a; else ret1 = b; int ret = phi(ret0, ret1); return ret; }
这里的phi函数根据条件进行选择是使用ret0还是ret1的值,或者换一种说法的话,phi是一种类似于C语言三地址码?的作用。不过phi的源是BasicBlock。
下边介绍如何使用PHI节点
1 #include "llvm/ADT/Statistic.h" 2 #include "llvm/IR/Function.h" 3 #include "llvm/Pass.h" 4 #include "llvm/Support/raw_ostream.h" 5 #include <map> 6 #include <string> 7 #include "llvm/IR/Instructions.h" 8 #include "llvm/IR/InstVisitor.h" 9 #include "llvm/ADT/DepthFirstIterator.h" 10 #include "llvm/ADT/SmallPtrSet.h" 11 #include "llvm/ADT/SmallVector.h" 12 #include "llvm/IR/InstIterator.h" 13 #include "llvm/IR/Instructions.h" 14 #include "llvm/IR/IntrinsicInst.h" 15 using namespace llvm; 16 17 #define DEBUG_TYPE "hello" 18 19 STATISTIC(HelloCounter, "Counts number of functions greeted"); 20 namespace { 21 struct PNINodeVisitor : public InstVisitor<PNINodeVisitor> { 22 public: 23 PNINodeVisitor(BasicBlock &bb){ 24 visit(bb); 25 } 26 27 void visitPHINode(PHINode &PN){ 28 errs() <<"PN.getNumIncomingValues() " <<PN.getNumIncomingValues() <<"\n"; 29 for (PHINode::block_iterator i = PN.block_begin(), e = PN.block_end(); i != e; 30 ++i) { 31 (*i)->dump(); 32 } 33 } 34 35 }; 36 // Hello5 - The second implementation with getAnalysisUsage implemented. 37 struct Hello5 : public FunctionPass { 38 static char ID; // Pass identification, replacement for typeid 39 Hello5() : FunctionPass(ID) {} 40 using BasicBlockListType = SymbolTableList<BasicBlock>; 41 42 bool runOnFunction(Function &F) override { 43 errs() << "now process funcName: "; 44 errs().write_escaped(F.getName()) << '\n'; 45 BasicBlockListType::iterator bbEnd = F.end(); 46 for(BasicBlockListType::iterator bbIter=F.begin(); bbIter!=bbEnd; ++bbIter){ 47 errs() <<"bb Tr\n"; 48 BasicBlock &bbs = *bbIter; 49 PNINodeVisitor piv(bbs); 50 } 51 return false; 52 } 53 54 // We don't modify the program, so we preserve all analyses. 55 void getAnalysisUsage(AnalysisUsage &AU) const override { 56 AU.setPreservesAll(); 57 } 58 std::map<std::string, uint> opCodeMap; 59 }; 60 } 61 62 char Hello5::ID = 0; 63 static RegisterPass<Hello5> 64 XZ("hello5", "show how to solve PHINode");
为了能真的处理到PHINode,我们这里使用的测试源码是:
int max(int a, int b){ if(a>b) return a; else return b; } int new_max(int a, int b) { int ret = 0; if(a>b) ret = a; ret = b; return ret; } void omi(int r, int y) { int l = y || r; }
使用./bin/clang -c -emit-llvm max.c编译成max.bc后,测试结果如下:
下边附生成的max.ll
; ModuleID = 'max.c' source_filename = "max.c" target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-unknown-linux-gnu" ; Function Attrs: noinline nounwind optnone uwtable define dso_local i32 @max(i32 %a, i32 %b) #0 { entry: %retval = alloca i32, align 4 %a.addr = alloca i32, align 4 %b.addr = alloca i32, align 4 store i32 %a, i32* %a.addr, align 4 store i32 %b, i32* %b.addr, align 4 %0 = load i32, i32* %a.addr, align 4 %1 = load i32, i32* %b.addr, align 4 %cmp = icmp sgt i32 %0, %1 br i1 %cmp, label %if.then, label %if.else if.then: ; preds = %entry %2 = load i32, i32* %a.addr, align 4 store i32 %2, i32* %retval, align 4 br label %return if.else: ; preds = %entry %3 = load i32, i32* %b.addr, align 4 store i32 %3, i32* %retval, align 4 br label %return return: ; preds = %if.else, %if.then %4 = load i32, i32* %retval, align 4 ret i32 %4 } ; Function Attrs: noinline nounwind optnone uwtable define dso_local i32 @new_max(i32 %a, i32 %b) #0 { entry: %a.addr = alloca i32, align 4 %b.addr = alloca i32, align 4 %ret = alloca i32, align 4 store i32 %a, i32* %a.addr, align 4 store i32 %b, i32* %b.addr, align 4 store i32 0, i32* %ret, align 4 %0 = load i32, i32* %a.addr, align 4 %1 = load i32, i32* %b.addr, align 4 %cmp = icmp sgt i32 %0, %1 br i1 %cmp, label %if.then, label %if.end if.then: ; preds = %entry %2 = load i32, i32* %a.addr, align 4 store i32 %2, i32* %ret, align 4 br label %if.end if.end: ; preds = %if.then, %entry %3 = load i32, i32* %b.addr, align 4 store i32 %3, i32* %ret, align 4 %4 = load i32, i32* %ret, align 4 ret i32 %4 } ; Function Attrs: noinline nounwind optnone uwtable define dso_local void @omi(i32 %r, i32 %y) #0 { entry: %r.addr = alloca i32, align 4 %y.addr = alloca i32, align 4 %l = alloca i32, align 4 store i32 %r, i32* %r.addr, align 4 store i32 %y, i32* %y.addr, align 4 %0 = load i32, i32* %y.addr, align 4 %tobool = icmp ne i32 %0, 0 br i1 %tobool, label %lor.end, label %lor.rhs lor.rhs: ; preds = %entry %1 = load i32, i32* %r.addr, align 4 %tobool1 = icmp ne i32 %1, 0 br label %lor.end lor.end: ; preds = %lor.rhs, %entry %2 = phi i1 [ true, %entry ], [ %tobool1, %lor.rhs ] %lor.ext = zext i1 %2 to i32 store i32 %lor.ext, i32* %l, align 4 ret void } attributes #0 = { noinline nounwind optnone uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } !llvm.module.flags = !{!0} !llvm.ident = !{!1} !0 = !{i32 1, !"wchar_size", i32 4} !1 = !{!"clang version 7.0.0 (tags/RELEASE_700/final)"}
Reference:
https://mapping-high-level-constructs-to-llvm-ir.readthedocs.io/en/latest/control-structures/ssa-phi.html
https://wiki.aalto.fi/display/t1065450/LLVM+SSA
https://stackoverflow.com/questions/11485531/what-exactly-phi-instruction-does-and-how-to-use-it-in-llvm