如何通过编写LLVM通道将原始程序中的浮点变量类型更改为long double?

时间:2016-07-26 00:56:33

标签: llvm

我正在写一个LLVM传递来将原始程序中浮点变量的类型更改为long double。玩具测试程序是:

int main(){
    double i = 0.0000000000000001;
    if(i + 1 > 1)
        printf("correct answer");
    else
        printf("wrong answer");
    return 0;
}

我的传球需要将i的类型更改为“long double”。 原始程序的IR与转换后的程序之间有五个不同的位置。

<   %i = alloca x86_fp80, align 16
---
>   %i = alloca double, align 8

<   store x86_fp80 0xK3FC9E69594BEC44DE000, x86_fp80* %i, align 16
<   %0 = load x86_fp80, x86_fp80* %i, align 16
<   %add = fadd x86_fp80 %0, 0xK3FFF8000000000000000
<   %cmp = fcmp ogt x86_fp80 %add, 0xK3FFF8000000000000000
---
>   store double 1.000000e-16, double* %i, align 8
>   %0 = load double, double* %i, align 8
>   %add = fadd double %0, 1.000000e+00
>   %cmp = fcmp ogt double %add, 1.000000e+00

我完成上述转换的传递大纲如下:

virtual bool runOnFunction(Function &F) {
    std::string svariable ("i");
    const ValueSymbolTable& symbolTable = F.getValueSymbolTable();
    Value* target = symbolTable.lookup(svariable);
    AllocaInst* old_target = dyn_cast<AllocaInst>(target);
    errs() <<"old_target: " << *target << "\n";
    errs() <<"num of old_target_uses:" << old_target->getNumUses() <<"\n";

    //get the type of long double and construct new AllocaInst
    LLVMContext &context = F.getContext();
    Type* type = Type::getX86_FP80Ty(context);
    unsigned alignment = 16;
    AllocaInst* new_target = new AllocaInst(type, 0, alignment, "new", old_target);
    new_target->takeName(old_target);

    // iterating through instructions using old AllocaInst
    Value::use_iterator it = old_target->use_begin();
    for(; it != old_target->use_end(); it++) {
          Value * temp = it->get();
          errs() <<"temp:" << *temp <<"\n";
          //transform() is under construction
          transform(it, new_target, type, alignment);

    }
    old_target->eraseFromParent();
    return false;
}

如原始程序的IR中所示,应该有两个与double i相关的指令:

>   store double 1.000000e-16, double* %i, align 8
>   %0 = load double, double* %i, align 8
>   %add = fadd double %0, 1.000000e+00
>   %cmp = fcmp ogt double %add, 1.000000e+00

但是传递的输出不符合上述预期:

old_target: %i = alloca double, align 8
num of old_target_uses:2
temp:  %0 = alloca double, align 8
temp:  %0 = alloca double, align 8

所以我的第一个问题是为什么getNumUses()和use_iterator没有返回正确的答案,我是否在我的传递大纲中以错误的方式使用它们?

我的第二个问题是在我的transform()函数中,我需要枚举各种指令,如LoadInst,StoreInst,BinaryOperation,并用新类型重构它们,对吗?

非常感谢提前:)

1 个答案:

答案 0 :(得分:1)

至于你的第一个问题,每个Use对象基本上是数据流图中的一个边缘,它将Value(主要是指令或常量)与其Users之一(指令或不变)。这两个值都可以分别通过Use::getUse::getUser访问。

Value::use_iterator it = old_target->use_begin();
for(; it != old_target->use_end(); it++) {
      Value * temp = it->get();
}

在这里,当您迭代使用old_target并将temp分配给每次使用的使用值时,分配的内容实际上是old_target本身。我相信你之后的it->getUser用户,每次都不同。

请注意getNumUses()实际上是正确的,因为在您的示例中%i使用了两次,第一次使用store,然后是load

  

我的第二个问题是在我的transform()函数中,我需要枚举   各种指令,如LoadInst,StoreInst,BinaryOperation   并用新类型重建它们,对吧?

至于实际更换类型,我认为这大致是需要的。请注意,通常以这种方式替换类型可能会导致错误的结果,因此您可能需要先检查bitcastptrtoint等指令是否先对这些变量进行操作。我建议您仅支持allocas来源和从一开始就对这些来源进行操作的有限子集,并从此处增加子集。

然后,您将以某种方式转换每个用户,以适应其中一个操作数,将类型从double / double*更改为x86_fp80 / x86_fp80*。如果其结果类型发生更改,您还需要传播它。为此,您可能会发现工作列表模式很有用 - 这是LLVM本身组织的传递次数(example)。

更新IR的常用方法是Value::replaceAllUsesWith。但是,在您的情况下,类型很可能也会更改,这会导致此函数失败并显示错误消息。因此,您需要手动更改IR和类型,可能需要使用User::setOperandValue::mutateType的某种组合,并在需要时创建新指令。

例如:

  • 代表loadfaddfcmp和类似说明,替换操作数并将结果添加到工作清单中;
  • 用于store指令:
    • 如果第一个操作数的类型已更改,则除非第二个操作数的类型也已更改,否则将中止处理;
    • 如果第二个操作数的类型已更改,但第一个操作数的类型未更改,请将第一个操作数转换为x86_fp80(使用fpext);
    • 无论如何,请将结果添加到工作清单中;
  • 针对callinvoke说明,使用fptruncx86_fp80个操作数强制转换为double(如果您想禁止此类向下广播,则取消中止); < / LI>
  • 用于bitcastptrtoint说明,中止处理;
  • ...