【问题标题】:How Would I create this sequence in the LLVM builder framework?我将如何在 LLVM 构建器框架中创建此序列?
【发布时间】:2015-03-24 01:42:16
【问题描述】:

我正在学习 LLVM 基础知识。我正在尝试进入构建器框架并设置模块、函数头等,但我还没有找到一种方法来在构建器中创建像这样的简单序列:

%0 = 41
%1 = add i32 42, %0 

意思是如何通过构建器框架使用伪寄存器符号?

我试图创建一个基于两个常量的加号指令。我用来生成(整数)加法的核心行是:

    Value *L = (Value *)m_left->Create_LLVM(  );
    Value *R = (Value *)m_right->Create_LLVM();
    if ( L == 0 || R == 0 ) return 0;

    llvm::Value *p_instruction =  Get_Builder().CreateAdd( L, R, "addtmp" );

这包含很多我自己的功能,但我想基础很清楚。我得到一个左右操作数的值指针,它们都是常量,然后使用构建器框架创建一个添加操作。再次正确设置了模块和构建器,当我调用 dump() 时,我看到了我所做的所有其他事情,但上面的这一行没有创建任何 IR 代码。

我希望它会共同创造类似

%4 = add i32 %3, %2 

或类似的东西。我是否误解了与构建器一起构建操作的方式的基本原理,还是只是对某些细节的小疏忽?

谢谢

【问题讨论】:

    标签: llvm llvm-ir llvm-c++-api


    【解决方案1】:

    如果没有花哨的 Create_LLVM() 函数,很难说你做错了什么,但通常用于添加两个常量:

    你必须创建 2 个ConstantInt:

    const auto& ctx = getGlobalContext(); // just your LLVMContext
    auto* L = ConstantInt::get(Type::getInt32Ty(ctx), 41);
    auto* R = ConstantInt::get(Type::getInt32Ty(ctx), 42);
    const auto& builder = Get_Builder();
    builder.Insert(L);  // just a no-op in standard builder impl
    builder.Insert(R);  // just a no-op in standard builder impl
    builder.CreateAdd(L, R, "addtmp");
    

    你应该得到:

    %addtmp = add i32 41, i32 42;
    

    您说您的构建器设置正确,因此它将在当前运行的 BasicBlock 末尾添加add。我假设你已经创建了一个Function,至少有一个BasicBlock

    编辑: 将以任何方式为您提供添加指令的是在没有构建器的情况下仅调用 C++ API 来创建它:

    BinaryOperator* add = BinaryOperator::Create(BinaryOps::Add, L, R, "addtmp", BB); 
    

    BB 是当前的BasicBlock

    为了得到更复杂的东西(添加到变量中),规范的方法是这样的:

    首先你需要一些记忆。 AllocaInst 在栈上分配内存:

    您可以为此使用构建器:

    auto* A = builder.CreateAlloca (Type::getInt32Ty(ctx), nullptr, "a");
    auto* B = builder.CreateAlloca (Type::getInt32Ty(ctx), nullptr, "b");
    

    为简单起见,我将只取上面的​​常量并将它们存储在 A 和 B 中。 要存储值,我们需要StoreInst

    builder.CreateStore (L, A, /*isVolatile=*/false);
    builder.CreateStore (R, B, /*isVolatile=*/false);
    

    对于加法,我们使用 LoadInst 将值从内存加载到寄存器:

    auto* addLHS = builder.CreateLoad(A);
    auto* addRHS = builder.CreateLoad(B);
    

    最后添加如上: auto* add = builder.CreateAdd(addLHS, addRHS, "add");

    并且使用要添加的指针,您可以继续,例如,将其返回或将其存储到另一个变量。

    IR 应如下所示:

    define i32 foo() {
    entry:
      %a = alloca i32, align 4
      %b = alloca i32, align 4
      store i32 41, i32* %a, align 4
      store i32 42, i32* %b, align 4
      %0 = load i32* %a, align 4
      %1 = load i32* %b, align 4
      %add = add i32 %0, %1
      ret i32 %add
    }
    

    【讨论】:

    • 经过一天的实验,我走得更远了,我意识到构建器现在只是创建了一个带有常量值的 return 语句,而没有实际执行加法,这让我一开始很烦恼,因为我没想到会这样没有明确的优化请求,但对于 LLVM 来说,这种事情可能是微不足道的,不言而喻。
    • 我现在的主要问题是第一个问题。我实际上无法将值存储在变量中,事实上我还没有找到任何类似于变量的值派生类,所以我假设以与关于 IR 的基本教程兼容的方式处理变量,一定有一些复杂的方法来处理它,这种方法被混淆了,不容易被发现。
    • 构建器有一个常量文件夹,它仅在 IR 生成期间执行常量折叠,这很可能是您在没有优化器通过的情况下看到这种优化的原因。
    • 是的,我想到了,当您不期望它时,它只是令人困惑。关于如何将值存储在变量中的任何提示?这真的是我的主要障碍
    • 我明白了。好吧,终于事情开始变得更加清晰,非常感谢您的解释。我的印象是,我应该能够通过 %0 = 42 之类的序列创建变量序列,而无需首先分配存储空间,就像在纯 IR 代码示例中一样。到目前为止,我还没有看到任何方法可以做到这一点,你的回答似乎表明在构建器中没有办法做到这一点。我必须再研究一下这个例子才能让它深入人心,但我现在开始看得更清楚了,谢谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-04-15
    • 1970-01-01
    • 2014-08-13
    • 1970-01-01
    • 2021-12-15
    • 1970-01-01
    • 2020-03-18
    相关资源
    最近更新 更多