05_llvm IR测试创建ifelse语句及多个block

一个ifelse源码编译

先看如下代码:

int func(int a) {
  int b;
  if (a > 5) b = 6;
  else b = 5;
  return b;
}

编译命令 ../output/bin/clang++ -emit-llvm llvm_if_else.c -S -o ir.ll

编译后的IR如下:

; ModuleID = 'llvm_if_else.c'
source_filename = "llvm_if_else.c"
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"

; Function Attrs: mustprogress noinline nounwind optnone uwtable
define dso_local noundef i32 @_Z4funci(i32 noundef %a) #0 {
entry:
  %a.addr = alloca i32, align 4 ;解释:使用alloca指令在栈上分配一个i32的局部变量,4字节对齐,获得内存地址指针变量a.addr
  %b = alloca i32, align 4 ;解释:同样在栈上分配一个变量,获得指针变量b
  store i32 %a, ptr %a.addr, align 4 ;解释:取出参数a的值,store给a.addr指针指向的内存
  %0 = load i32, ptr %a.addr, align 4 ;从a.addr指针指向的内存中取出值load给临时变量0%
  %cmp = icmp sgt i32 %0, 5
  br i1 %cmp, label %if.then, label %if.else

if.then:                                          ; preds = %entry 这是if block
  store i32 6, ptr %b, align 4
  br label %if.end

if.else:                                          ; preds = %entry
  store i32 5, ptr %b, align 4
  br label %if.end

if.end:                                           ; preds = %if.else, %if.then
  %1 = load i32, ptr %b, align 4
  ret i32 %1
}

attributes #0 = { mustprogress noinline nounwind optnone uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }

!llvm.module.flags = !{!0, !1, !2, !3, !4}
!llvm.ident = !{!5}

!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{i32 8, !"PIC Level", i32 2}
!2 = !{i32 7, !"PIE Level", i32 2}
!3 = !{i32 7, !"uwtable", i32 2}
!4 = !{i32 7, !"frame-pointer", i32 2}
!5 = !{!"clang version 16.0.5"}

使用接口创建if-else代码块

解释及代码如下:

#include <vector>
#include "llvm/IR/Module.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/Verifier.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/GlobalVariable.h"
// #include "llvm/Support/Alignment.h"
#include "llvm/IR/Constants.h"

using namespace llvm;
using namespace std;

/*
根据上边IR,使用相应接口创建出IR。
*/
int main() {
  LLVMContext c;
  Module *m = new Module("test module", c);
  IRBuilder<> builder(c);

  // Type *voidTy = Type::getVoidTy(c);// 原来创建void 参数类型
  vector<Type *> paramTys(2, builder.getInt32Ty());
  /*get函数在 include/llvm/IR/DerivedTypes.h:102:class FunctionType : public Type {
  /// This static method is the primary way of constructing a FunctionType.
  static FunctionType *get(Type *Result, ArrayRef<Type*> Params, bool isVarArg);
  */
  FunctionType *funcTy = FunctionType::get(builder.getInt32Ty(), paramTys, false);
  Function *func = Function::Create(funcTy, GlobalValue::ExternalLinkage, "test_function", m);
  // 低版本无getArg()函数
  Value *arg0 = func->getArg(0);
  arg0->setName("a");
  func->getArg(1)->setName("c");

  // 1 给函数创建主block
  BasicBlock *bEntry = BasicBlock::Create(c, "entry_block", func);
  // 创建出if else需要的block
  BasicBlock *bIf = BasicBlock::Create(c, "if.then", func);
  BasicBlock *bElse = BasicBlock::Create(c, "if.else", func);
  BasicBlock *bIfEnd = BasicBlock::Create(c, "if.end", func);

  // 2 给主block填充分配b和获取a值、比较等指令
  builder.SetInsertPoint(bEntry); // 设置当前Builder的操作对象
  // b局部变量:alloca指令创建局部变量b,接口为AllocaInst * llvm::IRBuilderBase::CreateAlloca(Type * Ty, Value * ArraySize = nullptr, const Twine & Name = "")
  Value *bPtr = builder.CreateAlloca(builder.getInt32Ty(), nullptr, "b.addr");
  // 再创建出对参数a的判断入口
  ConstantInt *vConst5 = builder.getInt32(5);
  Value *vCondition = builder.CreateICmpSGT(arg0, vConst5, "compare.ret"); // if a>5
  builder.CreateCondBr(vCondition, bIf, bElse); // 条件跳转指令,vCondition为真 跳转到if.then代码块内执行,否则到if.else执行

  // 3 接下来给if.then代码块填充指令
  builder.SetInsertPoint(bIf); // 设置当前Builder的操作对象为if.then
  ConstantInt *vConst6 = builder.getInt32(6);
  builder.CreateStore(vConst6, bPtr); // b=6语句
  builder.CreateBr(bIfEnd); // 无条件跳转指令,无条件跳转到return代码块

  // 4 接下来填充if.else代码块
  builder.SetInsertPoint(bElse); // 设置当前Builder的操作对象为if.then
  ConstantInt *vConst4 = builder.getInt32(4);
  builder.CreateStore(vConst4, bPtr);
  builder.CreateStore(vConst5, bPtr);
  builder.CreateBr(bIfEnd); // 也无条件跳转到return代码块

  // 5 最后构造返回代码块
  builder.SetInsertPoint(bIfEnd);
  // 创建返回值,从b的地址中load出数据后返回
  Value *bVal = builder.CreateLoad(bPtr, "ret.val");
  builder.CreateRet(bVal);

  verifyFunction(*func);
  m->print(outs(), nullptr);
  return 0;
}

// 编译命令 ../clang-16/bin/clang++ -w -o test_ifelse_bin `llvm-config --cxxflags --ldflags --system-libs --libs core` ./08_test_ifelse_stm.cpp
// 运行结果
/*
; ModuleID = 'test module'
source_filename = "test module"

define i32 @test_function(i32 %a, i32 %c) {
entry_block:
  %b.addr = alloca i32
  %compare.ret = icmp sgt i32 %a, 5
  br i1 %compare.ret, label %if.then, label %if.else

if.then:                                          ; preds = %entry_block
  store i32 6, i32* %b.addr
  br label %if.end

if.else:                                          ; preds = %entry_block
  store i32 4, i32* %b.addr
  store i32 5, i32* %b.addr
  br label %if.end

if.end:                                           ; preds = %if.else, %if.then
  %ret.val = load i32, i32* %b.addr
  ret i32 %ret.val
}

*/

使用phi指令实现if-else

介绍与实现及结果见如下代码与注释:

#include <vector>
#include "llvm/IR/Module.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/Verifier.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/GlobalVariable.h"
// #include "llvm/Support/Alignment.h"
#include "llvm/IR/Constants.h"

using namespace llvm;
using namespace std;

/*
使用phi指令获取分支block的返回值,格式如下:
%value = phi i32 [5, %branch1], [6, %branch2], [7, %branch3]
解释:待赋值value,名称,返回值类型i32,如果执行的是branch1,则返回5,则value=5,如果执行的是branch2,则value=6,以此类推。
创建API:
PHINode * llvm::IRBuilderBase::CreatePHI(Type * Ty, unsigned NumReservedValues, const Twine & Name = "")
void llvm::PHINode::addIncoming(Value * V, BasicBlock * BB) // 添加branch及对应的返回值
具体结合代码体会
*/
int main() {
  LLVMContext c;
  Module *m = new Module("test module", c);
  IRBuilder<> builder(c);

  vector<Type *> paramTys(2, builder.getInt32Ty());
  /*get函数在 include/llvm/IR/DerivedTypes.h:102:class FunctionType : public Type {
  /// This static method is the primary way of constructing a FunctionType.
  static FunctionType *get(Type *Result, ArrayRef<Type*> Params, bool isVarArg);
  */
  FunctionType *funcTy = FunctionType::get(builder.getInt32Ty(), paramTys, false);
  Function *func = Function::Create(funcTy, GlobalValue::ExternalLinkage, "test_function", m);
  // 低版本无getArg()函数
  Value *arg0 = func->getArg(0);
  arg0->setName("a");
  func->getArg(1)->setName("c");

  // 1 给函数创建主block
  BasicBlock *bEntry = BasicBlock::Create(c, "entry_block", func);
  // 创建出if else需要的block
  BasicBlock *bIf = BasicBlock::Create(c, "if.then", func);
  BasicBlock *bElse = BasicBlock::Create(c, "if.else", func);
  BasicBlock *bIfEnd = BasicBlock::Create(c, "if.end", func);

  // 2 给主block填充分配b和获取a值、比较等指令
  builder.SetInsertPoint(bEntry); // 设置当前Builder的操作对象
  // b局部变量:alloca指令创建局部变量b,接口为AllocaInst * llvm::IRBuilderBase::CreateAlloca(Type * Ty, Value * ArraySize = nullptr, const Twine & Name = "")
  Value *bPtr = builder.CreateAlloca(builder.getInt32Ty(), nullptr, "b.addr");
  // 再创建出对参数a的判断入口
  ConstantInt *vConst5 = builder.getInt32(5);
  Value *vCondition = builder.CreateICmpSGT(arg0, vConst5, "compare.ret"); // if a>5
  builder.CreateCondBr(vCondition, bIf, bElse); // 条件跳转指令,vCondition为真 跳转到if.then代码块内执行,否则到if.else执行

  /***前边代码和上一个一样,以下开始和上一个if else有所不同***/
  // 3 if.then代码块:无需再store 6给b,改成创建出目标value=6,后设置给phi指令
  builder.SetInsertPoint(bIf); // 设置当前Builder的操作对象为if.then
  ConstantInt *vConst6 = builder.getInt32(6); // 只创建出一个6常量,设置给phi指令即可
  builder.CreateBr(bIfEnd); // 无条件跳转指令,无条件跳转到return代码块

  // 4 if.else代码块:同样也去除store指令,将目标值设给phi指令
  builder.SetInsertPoint(bElse); // 设置当前Builder的操作对象为if.then
  ConstantInt *vConst4 = builder.getInt32(4);
  builder.CreateBr(bIfEnd); // 也无条件跳转到return代码块

  // 5 最后构造返回代码块,return b
  builder.SetInsertPoint(bIfEnd);
  // 函数接口:llvm::PHINode * CreatePHI(llvm::Type *Ty, unsigned int NumReservedValues, const llvm::Twine &Name = "")
  PHINode *phi = builder.CreatePHI(builder.getInt32Ty(), 2);
  phi->addIncoming(vConst6, bIf);
  phi->addIncoming(vConst4, bElse);
  builder.CreateStore(phi, bPtr); // 这里再把phi获得的值设置给b
  // 创建返回值,从b的地址中load出数据后返回
  Value *bVal = builder.CreateLoad(bPtr, "ret.val");
  builder.CreateRet(bVal);

  verifyFunction(*func);
  m->print(outs(), nullptr);
  return 0;
}

// 编译命令 ../clang-16/bin/clang++ -w -o test_phi_bin `llvm-config --cxxflags --ldflags --system-libs --libs core` ./09_test_phi_stm.cpp
// 运行结果,运行test_phi_bin
/*
; ModuleID = 'test module'
source_filename = "test module"

define i32 @test_function(i32 %a, i32 %c) {
entry_block:
  %b.addr = alloca i32
  %compare.ret = icmp sgt i32 %a, 5
  br i1 %compare.ret, label %if.then, label %if.else

if.then:                                          ; preds = %entry_block
  br label %if.end

if.else:                                          ; preds = %entry_block
  br label %if.end

if.end:                                           ; preds = %if.else, %if.then
  %0 = phi i32 [ 6, %if.then ], [ 4, %if.else ]
  store i32 %0, i32* %b.addr
  %ret.val = load i32, i32* %b.addr
  ret i32 %ret.val
}

*/
posted @ 2023-08-02 21:10  00lab  阅读(78)  评论(0编辑  收藏  举报