Получение исходного имени переменной для значения 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
У меня было аналогичное требование, преобразовав IR в "переменные SSA как VerName vertation ". Следующая документация и ссылки помогли мне. 1) https://releases.llvm.org/3.4.2/docs/tutorial/LangImpl7.html 2) LLVM opt mem2reg не действует
Надеюсь, это поможет сообществу!!!