我正在写一个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,并用新类型重构它们,对吗?
非常感谢提前:)
答案 0 :(得分:1)
至于你的第一个问题,每个Use
对象基本上是数据流图中的一个边缘,它将Value
(主要是指令或常量)与其Users
之一(指令或不变)。这两个值都可以分别通过Use::get
和Use::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 并用新类型重建它们,对吧?
至于实际更换类型,我认为这大致是需要的。请注意,通常以这种方式替换类型可能会导致错误的结果,因此您可能需要先检查bitcast
或ptrtoint
等指令是否先对这些变量进行操作。我建议您仅支持allocas
来源和从一开始就对这些来源进行操作的有限子集,并从此处增加子集。
然后,您将以某种方式转换每个用户,以适应其中一个操作数,将类型从double
/ double*
更改为x86_fp80
/ x86_fp80*
。如果其结果类型发生更改,您还需要传播它。为此,您可能会发现工作列表模式很有用 - 这是LLVM本身组织的传递次数(example)。
更新IR的常用方法是Value::replaceAllUsesWith
。但是,在您的情况下,类型很可能也会更改,这会导致此函数失败并显示错误消息。因此,您需要手动更改IR和类型,可能需要使用User::setOperand,Value::mutateType的某种组合,并在需要时创建新指令。
例如: