请问如何通过写一个LLVM pass把原程序中浮点型变量的类型改成long double?
How to change the type of float-point variables in the original program to long double by writing a LLVM pass please?
我正在编写一个LLVM pass,将原程序中的浮点变量类型更改为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
我做上述变换的pass的大纲如下:
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相关的指令应该有4条:
> 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
但是pass的输出和上面预期的不一样:
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 没有 return 正确答案,我是不是在我的通行证大纲中以错误的方式使用了它们?
我的第二个问题是在我的 transform() 函数中,我需要枚举每一种指令,例如 LoadInst、StoreInst、BinaryOperation 并用新的类型重建它们,对吗?
非常感谢:)
关于你的第一个问题,每个 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
的uses并且将temp
赋值给每个use的used值时,赋值的实际上是old_target
本身。我相信您想要的是 it->getUser
,这将是一个 用户,每次都不一样。
请注意 getNumUses()
实际上是正确的,因为在您的示例中 %i
使用了两次,第一次在 store
中,然后在 load
.[=49= 中]
My second question is in my transform() function, I need to enumerate
every kind of instruction such as LoadInst, StoreInst, BinaryOperation
and reconstruct them with the new type, right?
至于实际替换类型,我想这就是这里所需要的。请注意,通常以这种方式替换类型可能会导致不正确的结果,因此您可能需要先检查没有 bitcast
或 ptrtoint
等指令对这些变量进行操作。我建议您从一开始就只支持 allocas
源和对这些源进行操作的有限指令子集,并从这里扩展子集。
然后,您将以某种方式转换每个用户,以适应其操作数之一从 double
/double*
到 x86_fp80
/x86_fp80*
的更改类型。如果其结果类型发生变化,您还需要传播它。为此,您可能会发现工作列表模式很有帮助——这就是 LLVM 本身组织的遍数 (example)。
更新 IR 的常用方法是 Value::replaceAllUsesWith
。但是,在您的情况下,类型很可能也会更改,这会导致此函数失败并显示错误消息。因此您需要手动更改 IR 和类型,可能使用 User::setOperand, Value::mutateType 的某种组合,并在需要时创建新指令。
例如:
我正在编写一个LLVM pass,将原程序中的浮点变量类型更改为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
我做上述变换的pass的大纲如下:
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相关的指令应该有4条:
> 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
但是pass的输出和上面预期的不一样:
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 没有 return 正确答案,我是不是在我的通行证大纲中以错误的方式使用了它们?
我的第二个问题是在我的 transform() 函数中,我需要枚举每一种指令,例如 LoadInst、StoreInst、BinaryOperation 并用新的类型重建它们,对吗?
非常感谢:)
关于你的第一个问题,每个 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
的uses并且将temp
赋值给每个use的used值时,赋值的实际上是old_target
本身。我相信您想要的是 it->getUser
,这将是一个 用户,每次都不一样。
请注意 getNumUses()
实际上是正确的,因为在您的示例中 %i
使用了两次,第一次在 store
中,然后在 load
.[=49= 中]
My second question is in my transform() function, I need to enumerate every kind of instruction such as LoadInst, StoreInst, BinaryOperation and reconstruct them with the new type, right?
至于实际替换类型,我想这就是这里所需要的。请注意,通常以这种方式替换类型可能会导致不正确的结果,因此您可能需要先检查没有 bitcast
或 ptrtoint
等指令对这些变量进行操作。我建议您从一开始就只支持 allocas
源和对这些源进行操作的有限指令子集,并从这里扩展子集。
然后,您将以某种方式转换每个用户,以适应其操作数之一从 double
/double*
到 x86_fp80
/x86_fp80*
的更改类型。如果其结果类型发生变化,您还需要传播它。为此,您可能会发现工作列表模式很有帮助——这就是 LLVM 本身组织的遍数 (example)。
更新 IR 的常用方法是 Value::replaceAllUsesWith
。但是,在您的情况下,类型很可能也会更改,这会导致此函数失败并显示错误消息。因此您需要手动更改 IR 和类型,可能使用 User::setOperand, Value::mutateType 的某种组合,并在需要时创建新指令。
例如: