Как сопоставить несколько циклов одного типа под функцией с сгенерированным базовым блоком в LLVM IR?

Если циклы разного типа, я могу легко идентифицировать их по имени, но если есть несколько циклов одного типа (скажем, 5 while циклов), как я могу определить, какой базовый блок в LLVM IR соответствует какому циклу в исходном коде? код?

Вручную это легко идентифицировать, поскольку мы последовательно посещаем код и LLVM IR, но я смотрю, как мы можем идентифицировать то же самое программно.

Например, у меня есть приведенный ниже исходный код на C:

int main()
{
   int count=1;
   while (count <= 4)
   {
        count++;
   }
   while (count > 4)
   {
        count--;
   }
   return 0;
}

когда я выполняю команду clang -S -emit-llvm fileName.c, я получаю файл fileName.ll со следующим содержимым:

; ModuleID = 'abc.c'
source_filename = "abc.c"
target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-pc-windows-msvc19.0.23026"

; Function Attrs: noinline nounwind uwtable
define i32 @main() #0 {
entry:
  %retval = alloca i32, align 4
  %count = alloca i32, align 4
  store i32 0, i32* %retval, align 4
  store i32 1, i32* %count, align 4
  br label %while.cond

while.cond:                                       ; preds = %while.body, %entry
  %0 = load i32, i32* %count, align 4
  %cmp = icmp sle i32 %0, 4
  br i1 %cmp, label %while.body, label %while.end

while.body:                                       ; preds = %while.cond
  %1 = load i32, i32* %count, align 4
  %inc = add nsw i32 %1, 1
  store i32 %inc, i32* %count, align 4
  br label %while.cond

while.end:                                        ; preds = %while.cond
  br label %while.cond1

while.cond1:                                      ; preds = %while.body3, %while.end
  %2 = load i32, i32* %count, align 4
  %cmp2 = icmp sgt i32 %2, 4
  br i1 %cmp2, label %while.body3, label %while.end4

while.body3:                                      ; preds = %while.cond1
  %3 = load i32, i32* %count, align 4
  %dec = add nsw i32 %3, -1
  store i32 %dec, i32* %count, align 4
  br label %while.cond1

while.end4:                                       ; preds = %while.cond1
  ret i32 0
}

attributes #0 = { noinline nounwind uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }

!llvm.module.flags = !{!0}
!llvm.ident = !{!1}

!0 = !{i32 1, !"PIC Level", i32 2}
!1 = !{!"clang version 4.0.0 (tags/RELEASE_400/final)"}

Теперь есть два основных блока, созданных для данного исходного файла, как while.cond и while.cond1, как я могу определить, какой базовый блок для какого цикла в исходном коде?


person Sanjit Kumar Mishra    schedule 22.12.2017    source источник


Ответы (1)


Прежде чем я попытаюсь ответить, я просто хочу отметить, что в зависимости от выбранного уровня оптимизации или выбранного вручную прохода с opt этой информации может не быть или она может быть не такой точной (например, из-за встраивания, клонирования и т. д.).

Теперь способом связи между низкоуровневыми представлениями и исходным кодом является использование отладочной информации (например, с помощью DWARF формат). Чтобы получить отладочную информацию, вам нужно использовать флаг командной строки -g во время компиляции.

Для LLVM IR, если вы посмотрите на API Loop, вы найдете соответствующие вызовы, такие как getStartLoc. Итак, вы можете сделать что-то вроде этого (например, внутри метода runOn прохода llvm::Function):

llvm::SmallVector<llvm::Loop *> workList;
auto &LI = getAnalysis<llvm::LoopInfoWrapperPass>(CurFunc).getLoopInfo();

std::for_each(LI.begin(), LI.end(), [&workList](llvm::Loop *e) { workList.push_back(e); });

for(auto *e : workList) {
  auto line = e->getStartLoc().getLine();
  auto *scope = llvm::dyn_cast<llvm::DIScope>(e->getStartLoc().getScope());
  auto filename = scope->getFilename();

  // do stuff here
}

Более того, для BasicBlock вы также можете использовать связанные с отладкой методы в Instruction (например, getDebugLoc) и объединить его с вызовами других Loop, такие как getHeader и т. д.

Кроме того, обратите внимание, что существует метод getLoopID, который использует внутренний уникальный идентификатор для каждого цикла, но он не всегда существует и подвержен потенциальным исключениям, о которых я упоминал в начале. В любом случае, если вам нужно манипулировать им, посмотрите примеры в исходном коде LLVM, следуя методу setLoopID (например, в lib/Transforms/Scalar/LoopRotation.cpp).

person compor    schedule 22.12.2017
comment
Не могли бы вы помочь мне с заголовочным файлом, который я должен включить для метода getAnalysis()? - person Sanjit Kumar Mishra; 28.12.2017
comment
Метод getAnalysis наследуется всеми проходными классами, поэтому он должен быть легко доступен. LoopInfoWrapperPass можно приобрести у llvm/Analysis/LoopInfo.h. Я думаю, что использование grep или подобных инструментов в дереве исходного кода LLVM очень помогает. - person compor; 28.12.2017
comment
Вы также можете ознакомиться с этим репозиторием. Он аннотирует циклы числовым идентификатором. Вы можете соответствующим образом адаптироваться. - person compor; 02.01.2018
comment
спасибо за ваш ответ, я буду обновлять здесь по мере реализации. - person Sanjit Kumar Mishra; 02.01.2018
comment
Я использую llvm 4.0.1, и когда я пытаюсь добавить приведенный выше код, я получаю сообщение об ошибке, поскольку идентификатор getAnalysis не определен. Я пытался решить, но не смог. - person Sanjit Kumar Mishra; 03.01.2018
comment
Если вы используете его в проходе (используя LegacyPassManager), он должен быть легко доступен, так как этот метод унаследован от класса прохода (описано здесь). У меня нет работающей установки LLVM 4.0.1, но я скомпилировал ранее предоставленный репозиторий с текущим транком (6.0.0). Эта часть инфраструктуры не изменилась. - person compor; 03.01.2018
comment
На самом деле, я не использую его внутри прохода, я читаю базовые блоки из файла битового кода и обрабатываю их. - person Sanjit Kumar Mishra; 03.01.2018
comment
Это помогло бы, если бы это было опубликовано в вопросе; поэтому политика SO требует публикации части вашего кода. В любом случае, в случае автономного инструмента вам придется сгенерировать LoopInfo вручную. Для этого вы можете взглянуть на набор тестов в подкаталоге unittests, например, Analysis/LoopInfoTest.cpp, где внутри функции runWithLoopInfo создается автономный объект LoopInfo для переданного llvm::Function с использованием его сконструированного DominatorTree. Я не уверен, доступен ли этот тест в 4.0.1, но он есть в текущем транке (6.0.1). - person compor; 03.01.2018