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

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

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

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

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

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

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

Есть звонки llvm.dbg.declare а также llvm.dbg.value в их; как вы превращаетесь в исходные имена исходного кода и номера версий SSA?

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

3 ответа

Решение

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

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

Если вы используете последнюю версию Clang, некоторые другие подходы не будут работать. Вместо этого используйте флаг -fno-discard-value-names для clang. Это заставит llvm::Values ​​сохранить свои оригинальные имена

В этой статье содержится подробное объяснение получения исходных имен переменных, в которых можно использовать эти API. llvm.dbg.declare а также llvm.dbg.value

https://www.thetopsites.net/article/50369832.shtml

У меня было аналогичное требование, преобразовав IR в "переменные SSA как VerName vertation ". Следующая документация и ссылки помогли мне. 1) https://releases.llvm.org/3.4.2/docs/tutorial/LangImpl7.html 2) LLVM opt mem2reg не действует

Надеюсь, это поможет сообществу!!!

Другие вопросы по тегам