Compiler construction 获取LLVM值的原始变量名

Compiler construction 获取LLVM值的原始变量名,compiler-construction,clang,llvm,debug-symbols,llvm-ir,Compiler Construction,Clang,Llvm,Debug Symbols,Llvm Ir,一个(如指令)的操作数是s 在mem2reg传递之后,变量进入,并且它们与原始源代码对应的名称丢失值::getName()仅为某些内容设置;对于大多数变量(即中间变量),未设置其值 可以运行instnamer过程来指定所有变量的名称,如tmp1和tmp2,但这并不能捕获它们最初的来源。下面是原始C代码旁边的一些LLVM IR: 我正在构建一个简单的html页面来可视化和调试我正在进行的一些优化,我希望将SSA变量显示为namever符号,而不仅仅是临时instnamer名称。这只是为了帮助我的

一个(如指令)的操作数是s

在mem2reg传递之后,变量进入,并且它们与原始源代码对应的名称丢失<代码>值::getName()仅为某些内容设置;对于大多数变量(即中间变量),未设置其值

可以运行instnamer过程来指定所有变量的名称,如tmp1和tmp2,但这并不能捕获它们最初的来源。下面是原始C代码旁边的一些LLVM IR:

我正在构建一个简单的html页面来可视化和调试我正在进行的一些优化,我希望将SSA变量显示为namever符号,而不仅仅是临时instnamer名称。这只是为了帮助我的可读性

我使用命令行从clang获取LLVM IR,例如:

 clang -g3 -O1 -emit-llvm -o test.bc -c test.c
在IR中有对
llvm.dbg.declare
llvm.dbg.value
的调用;如何将原始源代码名和SSA版本号转换为源代码


那么,如何从
llvm::Value
中确定原始变量(或命名常量名)?调试器必须能够做到这一点,那么我该怎么做呢?

这是以元数据形式附加到LLVM IR的调试信息的一部分。文档一篇有一定背景的老博客文章


产生以下结果:

define i64 @fact(i64 %arg, i64 %farg, i64 %bart) #0 {
entry:
  tail call void @llvm.dbg.value(metadata !{i64 %arg}, i64 0, metadata !10), !dbg !17
  tail call void @llvm.dbg.value(metadata !{i64 %farg}, i64 0, metadata !11), !dbg !17
  tail call void @llvm.dbg.value(metadata !{i64 %bart}, i64 0, metadata !12), !dbg !17
  %add = add nsw i64 %bart, %farg, !dbg !18
  tail call void @llvm.dbg.value(metadata !{i64 %add}, i64 0, metadata !13), !dbg !18
  %mul = mul nsw i64 %add, %arg, !dbg !19
  ret i64 %mul, !dbg !19
}

使用
-O0
而不是
-O3
,您将看不到
llvm.dbg.value
,但您将看到
llvm.dbg.declare
给定
,通过遍历封闭函数中的所有
llvm.dbg.declare
llvm.dbg.value
调用,可以从中获取变量名,检查是否有引用该值的值,如果有,则返回与该内在调用所关联的值相关联的
divvariable

因此,代码应该大致如下(未经测试或编译):

const函数*findEnclosingFunc(const值*V){
if(常量参数*Arg=dyn_cast(V)){
返回Arg->getParent();
}
if(常量指令*I=dyn_cast(V)){
返回I->getParent()->getParent();
}
返回NULL;
}
常量MDNode*findVar(常量值*V,常量函数*F){
对于(常量指令迭代器Iter=inst\u begin(F),End=inst\u End(F);Iter!=End;++Iter){
常量指令*I=&*Iter;
如果(常数DbgDeclareInst*DbgDeclare=dyn_cast(I)){
如果(DbgDeclare->getAddress()==V)返回DbgDeclare->getVariable();
}else if(const DbgValue inst*DbgValue=dyn_cast(I)){
如果(DbgValue->getValue()==V)返回DbgValue->getVariable();
}
}
返回NULL;
}
StringRef getOriginalName(常量值*V){
//TODO也可以处理全局事务
常量函数*F=findEnclosingFunc(V);
如果(!F)返回V->getName();
常量MDNode*Var=findVar(V,F);
如果(!Var)返回“tmp”;
返回可分变量(Var).getName();
}
您可以在上面看到,我懒得添加全局变量的处理,但实际上这并没有什么大不了的-这需要迭代当前编译单元调试信息下列出的所有全局变量(使用
M.getNamedMetadata(“llvm.dbg.cu”)
获取当前模块中所有编译单元的列表),然后检查哪个变量与您的变量匹配(通过
getGlobal
方法)并返回其名称


但是请记住,以上仅适用于与原始变量直接相关的值。任何计算结果的任何值都不会以这种方式正确命名;特别是,表示字段访问的值不会用字段名命名。这是可行的,但需要更复杂的处理——您必须从GEP中标识字段号,然后深入挖掘结构的类型调试信息以获取字段名。调试器可以做到这一点,是的,但没有调试器在LLVM IR土地上运行——据我所知,即使是LLVM自己的LLDB也可以通过将对象文件中的侏儒解析为铿锵类型来实现不同的工作方式。

我也有类似的要求,将IR转换为“SSA变量作为VarNamever符号”。以下文档和链接帮助了我。 1) (二)


希望这对社区有帮助

如果您使用的是最新版本的Clang,则其他一些方法将不起作用。
相反,使用-fno discard value names标志来表示clang。这将使llvm::Values保留其原始名称

我认为理论和实践存在分歧:(我从未收到过任何由clang发出的llvm.dbg.value调用。我在请求之前阅读过的文档。@will:clang不会发出
llvm.dbg.value
。优化会在将值放入寄存器时发出它们(而不是更容易访问的堆栈插槽)。好吧,那你怎么让mem2reg或其他什么东西发出它们呢?它们会是让我将值转换为源代码名的秘方吗?如果是,怎么做?@Will:如果你使用一个非平凡的C函数,并使用clang-g(debug-info-enabled)从中发出LLVM IRLVv.dgg声明链接堆栈值到原来的C对象。<代码> MEM2Reg < /C>可以创建一些LLV.dB.Gub的内含函数;当然,并非所有LLVM级别的值都直接映射到原始的C对象,当然有些只是临时的、ABI相关的值、C++相关的降低等等。这正是我一直在做的。我只是在发布的bc中没有任何llvm.dbg.values。我已经为我的问题添加了更多的信息,我正在等待悬赏期的开始,希望有人能够真正计算出从值到名称/版本的实际代码。你用哪个程序创建了这么好的代码集|源代码比较?@JackL.我很快自己写了它。它只是一个javascript画布。当有人赚500分时,给这些值提供人类可读的名称,我甚至可能会发布它hint hint;)@你最终会发布你的比较工具吗?它对很多人都非常有用
define i64 @fact(i64 %arg, i64 %farg, i64 %bart) #0 {
entry:
  tail call void @llvm.dbg.value(metadata !{i64 %arg}, i64 0, metadata !10), !dbg !17
  tail call void @llvm.dbg.value(metadata !{i64 %farg}, i64 0, metadata !11), !dbg !17
  tail call void @llvm.dbg.value(metadata !{i64 %bart}, i64 0, metadata !12), !dbg !17
  %add = add nsw i64 %bart, %farg, !dbg !18
  tail call void @llvm.dbg.value(metadata !{i64 %add}, i64 0, metadata !13), !dbg !18
  %mul = mul nsw i64 %add, %arg, !dbg !19
  ret i64 %mul, !dbg !19
}