c++ 在LLVM中插入说明

qmelpv7a  于 2023-05-02  发布在  其他
关注(0)|答案(2)|浏览(114)

如果我试图创建一条指令并在函数的开头插入,这是正确的方法吗?因为当我使用opt加载时,我看不到插入的指令。所以归档并处理。ll文件。

if (auto* op = dyn_cast<Instruction>(&I))
{
    if(prepend_first == false)
    {
        llvm::LLVMContext& context = I.getContext();
        Value* lhs = ConstantInt::get(Type::getInt32Ty(context), 4);
        Value* rhs = ConstantInt::get(Type::getInt32Ty(context), 6);

        IRBuilder<> builder(op);
        builder.CreateMul(lhs, rhs);
        builder.SetInsertPoint(&I);

        prepend_first = true;
    }
}

编辑(2020年11月27日)这是整个通行证,这是基于其他文章以及。

using namespace llvm;

namespace
{
    struct customllvm : public PassInfoMixin<customllvm>
    {
        llvm:PreservedAnalyses run(Function &F, FunctionAnalysisManager &FAM)
        {
            for(BasicBlock &BB : F)
            {
                bool prepend_first = false;

                for(Instruction &I : BB)
                {
                    if(auto *op = dyn_cast<Instruction>(&I))
                    {
                        if(prepend_first != true)
                        {
                            llvm::LLVMContext& context = I.getContext();
                            Value* lhs = ConstantInt::get(Type::getInt32Ty(context), 4);
                            Value* rhs = ConstantInt::get(Type::getInt32Ty(context), 6);

                            IRBuilder<> builder(op);
                            builder.CreateMul(lhs, rhs);
                            builder.SetInsertPoint(&I);

                            prepend_first = true;
                        }
                    }
                }
            }
        }
    }
}
ut6juiuv

ut6juiuv1#

您需要先设置插入点。只有这样,才能创造奇迹。

idfiyjo8

idfiyjo82#

这既不是关于错误的插入,也不是优化未使用的指令。这是所有关于不断折叠。

它与SetInsertPoint()无关,因为**IRBuilder<> IRB(&I)默认将新指令插入到指令I之前。因此,在本例中,您甚至不需要显式设置插入点**。
我将很快说明为什么它与删除未使用的指令无关,但我首先解释您的pass的另外两个问题

  • 你的pass并不一定要在函数的开始处插入新指令,而是在函数的每个基本块的开始处插入新指令。以下是两种解决方案:
  • for(BasicBlock &BB : F)循环开始之前,只将prepend_first设置为false一次,这样当到达检查if语句时,prepend_first仅在第一个基本块中为false。但是,由于不必要的迭代,这种方法并不有趣。
  • 更好的方法是通过直接选择第一基本块的第一指令来避免在基本块和指令上的迭代。我将在我的传球中使用这种方法。
  • 您的dyn_cast<Instruction>(&I)检查强制转换是不需要。这个强制转换主要检查I是否为Instruction类型,显然这总是正确的。

下面是我的first_pass,它的灵感来自您的代码,但没有上面解释的两个问题:

#include "llvm/IR/IRBuilder.h"
...
PreservedAnalyses first_pass(Function &F) {
  Function::iterator FI = F.begin();  //iterator to the first basic block
  BasicBlock &BB = *FI;
  BasicBlock::iterator BBI = BB.begin();  //iterator to the first instruction
  Instruction &I = *BBI;

  LLVMContext& context = I.getContext();
  Value* lhs = ConstantInt::get(Type::getInt32Ty(context), 4);
  Value* rhs = ConstantInt::get(Type::getInt32Ty(context), 6);

  IRBuilder<> builder(&I);
  builder.CreateMul(lhs, rhs, "new_mul");

  return PreservedAnalyses::none();
}

注意,first_pass还没有将常数运算new_mul插入输入IR。在解释常量折叠的根本原因之前,让我先做一个有趣的实验来证明你的问题并不源于删除未使用的代码。
下面是我的test.c

int foo(int a, int b) {
  int c = a + 1;
  int d = b - 2;
  return c / d;
}

下面是我用来生成test.ll的命令,它将作为输入IR:

clang -O0 -Xclang -disable-O0-optnone -emit-llvm -c test.c -o test.bc
opt -S -mem2reg test.bc -o test.ll

下面是生成的test.ll

; Function Attrs: noinline nounwind uwtable
define dso_local i32 @foo(i32 noundef %a, i32 noundef %b) #0 {
entry:
  %add = add nsw i32 %a, 1
  %sub = sub nsw i32 %b, 2
  %div = sdiv i32 %add, %sub
  ret i32 %div
}

下面是我的second_pass,它与我的first_pass相同,不同之处在于它在创建的第二条指令new_add中使用new_mul的结果,因此这次将使用new_mulnew_add应该被插入到输入IR的第二个指令之前:

#include "llvm/IR/IRBuilder.h"
...
PreservedAnalyses second_pass(Function &F) {
  Function::iterator FI = F.begin();
  BasicBlock &BB = *FI;
  BasicBlock::iterator BBI = BB.begin();
  Instruction &I = *BBI;

  LLVMContext& context = I.getContext();
  Value* lhs = ConstantInt::get(Type::getInt32Ty(context), 4);
  Value* rhs = ConstantInt::get(Type::getInt32Ty(context), 6);

  IRBuilder<> IRB(&I);
  auto new_mul = IRB.CreateMul(lhs, rhs, "new_mul");

  // Specific to this pass.
  Instruction &I_Sec = *(++BBI);    
  IRBuilder<> IRB_Sec(&I_Sec);
  IRB_Sec.CreateAdd(new_mul, I_Sec.getOperand(0), "new_add");

  return PreservedAnalyses::none();
}

我运行了test.llsecond_pass,得到了这个结果IR:

; Function Attrs: noinline nounwind uwtable
define dso_local i32 @foo(i32 noundef %a, i32 noundef %b) #0 {
entry:
  %add = add nsw i32 %a, 1
  %new_add = add i32 24, %b
  %sub = sub nsw i32 %b, 2
  %div = sdiv i32 %add, %sub
  ret i32 %div
}

正如我们所看到的,虽然这次使用了new_mul,但它没有插入到结果IR中。更有趣的是,new_add被成功地插入到结果IR中,尽管它没有被使用。所以很明显,优化未使用的代码不能成为不生成new_mul的原因
回到我的主要观点:不断折叠!

常量折叠是一种编译器优化,其中常量操作在编译时进行评估,而不是在运行时计算它们。换句话说,编译器不会为常量操作生成代码,并且如果该操作的结果稍后被使用,编译器只使用其值来代替。

在您的示例中,您尝试创建的新指令(new_mul)是一个常量操作,因为它的两个参数都是常量(4和6)。因此,在存在常量折叠的情况下,编译器不会为其生成代码,并且如果稍后使用它(如new_addsecond_pass),编译器将使用其值(4 * 6 = 24)。
非常重要的是要知道**IRBuilder执行常量折叠,除非另有明确指定折叠类型。换句话说,如果我们希望编译器不执行常量折叠,我们必须使用IRBuilder<llvm::NoFolder>而不是IRBuilder<>。**
最后,这里是我的third_pass,它与我的first_pass相同,不同之处在于它使用IRBuilder<llvm::NoFolder>而不是IRBuilder<>来创建常量操作new_mul

#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/NoFolder.h"  // Do not forget to include.
...
PreservedAnalyses third_pass(Function &F) {
  Function::iterator FI = F.begin();  //iterator to the first basic block
  BasicBlock &BB = *FI;
  BasicBlock::iterator BBI = BB.begin();  //iterator to the first instruction
  Instruction &I = *BBI;

  LLVMContext& context = I.getContext();
  Value* lhs = ConstantInt::get(Type::getInt32Ty(context), 4);
  Value* rhs = ConstantInt::get(Type::getInt32Ty(context), 6);

  IRBuilder<llvm::NoFolder> builder(&I);  // NoFolder
  builder.CreateMul(lhs, rhs, "new_mul");

  return PreservedAnalyses::none();
}

现在,如果我运行test.llthird_pass,我会得到这个结果IR:

; Function Attrs: noinline nounwind uwtable
define dso_local i32 @foo(i32 noundef %a, i32 noundef %b) #0 {
entry:
  %new_mul = mul i32 4, 6
  %add = add nsw i32 %a, 1
  %sub = sub nsw i32 %b, 2
  %div = sdiv i32 %add, %sub
  ret i32 %div
}

正如我们所看到的,new_mul被插入到函数的开头。

相关问题