Получение исходного имени переменной для значения LLVM

Операнды для llvm::User (например, инструкция) llvm::Value с.

После прохождения mem2reg переменные находятся в форме SSA, и их имена, соответствующие исходному исходному коду, теряются. Value::getName() устанавливается только для некоторых вещей; для большинства переменных, которые являются посредниками, он не установлен.

Пропускная способность instnamer может быть запущена, чтобы предоставить все имена переменных, такие как tmp1 и tmp2, но это не отражает, откуда они изначально пришли. Здесь несколько LLVM IR рядом с исходным кодом C:

enter image description here

Я создаю простую html-страницу для визуализации и отладки некоторых оптимизаций, над которыми я работаю, и я хочу показать переменные SSA как обозначение name ver, а не просто временные имена инициалов. Это просто для того, чтобы помочь моей читаемости.

Я получаю свой LLVM IR от clang с командной строкой, например:

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

В IR есть вызовы llvm.dbg.declare и llvm.dbg.value; как вы включаете исходные имена исходных кодов и номера версий SSA?

Итак, как я могу определить исходную переменную (или имя константы) из llvm::Value? Отладчики должны быть в состоянии сделать это, так как я могу?

Ответ 1

Это часть отладочной информации, которая привязана к 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.

Ответ 2

Учитывая Value, получить имя переменной из нее можно, пройдя все вызовы llvm.dbg.declare и llvm.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 land - насколько я знаю, даже LLVM собственный LLDB работает по-разному, анализируя DWARF в объектном файле на типы Clang.