【问题标题】:Getting the original variable name for an LLVM Value获取 LLVM 值的原始变量名
【发布时间】:2014-02-20 01:14:10
【问题描述】:

llvm::User(例如指令)的操作数是 llvm::Values。

mem2reg 传递后,变量在SSA form,与原始源代码对应的名称丢失。 Value::getName() 只为某些东西设置;对于大多数作为中介的变量,它没有设置。

instnamer 传递可以运行以提供所有变量名称,例如 tmp1tmp2,但这并不能捕获它们最初的位置来自。这是原始 C 代码旁边的一些 LLVM IR:

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

我正在使用命令行从 clang 获取我的 LLVM IR:

 clang -g3 -O1 -emit-llvm -o test.bc -c test.c

IR中有对llvm.dbg.declarellvm.dbg.value的调用;怎么变成原始源代码名称和SSA版本号?

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

【问题讨论】:

  • 你用哪个程序来创建这么好的代码集 |来源对比?
  • @JackL。我自己很快就写好了。它只是一个 javascript 画布。当有人为价值提供人类可读的名称ver 获得 500 分时,我什至可能会发布它的提示提示 ;)
  • @Will 你最终发布了你的比较工具吗?我怀疑这对很多人来说都非常有用。
  • @ransford 不怕。而且,可悲的是,从中间名到 SSAnum,我也从未如此接近过。我的项目遇到了其他困难,例如 LLVM 不保留指针,据我了解,这让很多人想要精确的 GC 和移植到 VLIW 等:(

标签: compiler-construction clang llvm debug-symbols llvm-ir


【解决方案1】:

这是以元数据形式附加到 LLVM IR 的调试信息的一部分。文档is here。有一些背景的旧博文is also available


$ cat  > z.c
long fact(long arg, long farg, long bart)
{
    long foo = farg + bart;
    return foo * arg;
}

$ clang -emit-llvm -O3 -g -c z.c
$ llvm-dis z.bc -o -

产生这个:

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

【讨论】:

  • 我认为理论和实践存在分歧 :( 我从来没有遇到过 clang 发出的任何 llvm.dbg.value 调用。在我询问 SO 之前我读过的文档。
  • @Will:clang 不会发出 llvm.dbg.value。优化在将值放入寄存器(而不是更容易访问的堆栈槽)时发出它们。
  • 好吧,那么你如何获得 mem2reg 或其他任何东西来发出它们?它们会是让我将值转换为源代码名称的秘诀吗?如果是,如何实现?
  • @Will:如果您采用非平凡的 C 函数并使用 clang -g(启用调试信息)从中发出 LLVM IR,您将在其中看到调试信息。 llvm.dbg.declare 将堆栈值链接到原始 C 对象。 mem2reg 然后可能会创建一些 llvm.dbg.value 内在函数;当然,并非所有 LLVM 级别的值都直接映射到原始 C 对象 - 有些只是临时的、与 ABI 相关的值、与 C++ 相关的降低等。
  • 实际上,这正是我一直在做的事情。我只是在发出的 bc 中没有任何 llvm.dbg.values。我已经在我的问题中添加了更多信息,我正在等待赏金期开始,希望有人真正计算出从值到名称/版本的实际代码。
【解决方案2】:

给定一个Value,可以通过遍历封闭函数中的所有llvm.dbg.declarellvm.dbg.value 调用来从中获取变量名,检查是否有引用该值,如果是,则返回@987654324 @ 与该内部调用的值相关联。

因此,代码应该类似于(粗略地,未经测试甚至编译):

const Function* findEnclosingFunc(const Value* V) {
  if (const Argument* Arg = dyn_cast<Argument>(V)) {
    return Arg->getParent();
  }
  if (const Instruction* I = dyn_cast<Instruction>(V)) {
    return I->getParent()->getParent();
  }
  return NULL;
}

const MDNode* findVar(const Value* V, const Function* F) {
  for (const_inst_iterator Iter = inst_begin(F), End = inst_end(F); Iter != End; ++Iter) {
    const Instruction* I = &*Iter;
    if (const DbgDeclareInst* DbgDeclare = dyn_cast<DbgDeclareInst>(I)) {
      if (DbgDeclare->getAddress() == V) return DbgDeclare->getVariable();
    } else if (const DbgValueInst* DbgValue = dyn_cast<DbgValueInst>(I)) {
      if (DbgValue->getValue() == V) return DbgValue->getVariable();
    }
  }
  return NULL;
}

StringRef getOriginalName(const Value* V) {
  // TODO handle globals as well

  const Function* F = findEnclosingFunc(V);
  if (!F) return V->getName();

  const MDNode* Var = findVar(V, F);
  if (!Var) return "tmp";

  return DIVariable(Var).getName();
}

您可以在上面看到我懒得添加对全局变量的处理,但这实际上并没有什么大不了的 - 这需要遍历当前编译单元调试信息下列出的所有全局变量(使用 M.getNamedMetadata("llvm.dbg.cu") 获取列表当前模块中的所有编译单元),然后检查哪个与您的变量匹配(通过getGlobal 方法)并返回其名称。

但是,请记住,上述内容仅适用于与原始变量直接关联的值。任何计算结果的任何值都不会以这种方式正确命名;特别是,表示字段访问的值不会以字段名称命名。这是可行的,但需要更多涉及的处理 - 您必须从 GEP 中识别字段编号,然后深入了解结构的类型调试信息以获取字段名称。调试器会这样做,是的,但没有调试器在 LLVM IR 领域运行 - 据我所知,即使 LLVM 自己的 LLDB 也可以通过将目标文件中的 DWARF 解析为 Clang 类型来实现不同的工作方式。

【讨论】:

  • 非常好,非常类似于我一直在寻找专有名称的方式(尽管我一直在制作函数符号表的反向映射以稍微加快速度)。不过,最大的问题在于 however 部分; IR 中的几乎所有变量都是临时变量,尽管作为人类,您可以跟踪它们并查看它们的来源。
  • 它看起来不再有效(调试值在此答案后不久进行了重大修改,现在它们不返回 Value * 等等),我不确定什么是正确的方法立即执行此操作。
【解决方案3】:

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

【讨论】:

    【解决方案4】:

    我有类似的要求,将 IR 转换为“SSA 变量作为 VarNamever 表示法”。以下文档和链接帮助了我。 1)https://releases.llvm.org/3.4.2/docs/tutorial/LangImpl7.html 2)LLVM opt mem2reg has no effect

    希望这对社区有所帮助!!!

    【讨论】:

    • 嗨,这些链接很有帮助,但我仍然无法做到这一点。您能否详细说明您是如何设法获得原始变量名的?谢谢
    猜你喜欢
    • 1970-01-01
    • 2011-06-03
    • 2011-02-14
    • 1970-01-01
    • 2018-07-25
    • 1970-01-01
    • 2017-12-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多