本节将介绍一种特殊的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 typeid39 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.elseif.then: ; preds = %entry %2 = load i32, i32* %a.addr, align 4 store i32 %2, i32* %retval, align 4 br label %returnif.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.endif.then: ; preds = %entry %2 = load i32, i32* %a.addr, align 4 store i32 %2, i32* %ret, align 4 br label %if.endif.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)"}
View Code
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