21

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

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

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

在此处输入图像描述

我正在构建一个简单的 html 页面来可视化和调试我正在处理的一些优化,并且我想将 SSA 变量显示为名称ver表示法,而不仅仅是临时的 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?调试器必须能够做到这一点,那么我该怎么做呢?

4

4 回答 4

13

这是以元数据形式附加到 LLVM IR 的调试信息的一部分。文档在这里还可以找到具有一定背景的旧博客文章。


$ 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

于 2014-01-28T16:37:50.177 回答
10

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

所以,代码应该看起来像(大致,未经测试甚至编译):

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 类型。

于 2014-01-31T19:06:53.397 回答
1

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

于 2019-02-07T16:52:46.743 回答
0

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

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

于 2018-05-16T11:33:15.497 回答