Различные функции с одинаковыми именами и как их вызывать в LLVM IR - PullRequest
0 голосов
/ 18 июня 2020

Я пишу игрушечный компилятор для pascal -подобного языка. Предположим, у меня есть псевдокод вроде следующего:

define main

   define foo
      define b
         writeInteger: 1
      call b

   define bar
      define b
        writeInteger: 2
      call b

   call foo
   call bar

, где две разные функции b определены внутри foo и bar.

Итак, соответствующая функция LLVM IR для b внутри foo это

define void @b(%foo_type* %foo_frame) {
...

А для b внутри bar это

define void @b.1(%bar_type* %bar_frame) {
...

Проблема в том, что когда я пытаюсь вызвать функцию b, LLVM автоматически предполагает, что я вызываю void @b, но у меня нет возможности позвонить void @b.1.

Итак, напечатанный результат 11, но я бы хотел, чтобы это было 12.

Я использую команду TheModule->getFunction("b"); для доступа к объекту функции LLVM.

Итак, как я могу вызвать функцию b, определенную внутри bar?

ОБНОВЛЕНИЕ:

Как указал AlexDenisov, я мог бы назвать первый b как main_foo_b, а второй как main_bar_b. Таким образом, я мог затем вызвать нужную функцию b, выяснив, из какой части кода я вызываю b. Я попробую это сделать, сохранив список всех имён функций до сих пор (которые не были закрыты). Назовем его function_names_list.

Итак, когда я вызываю функцию b, которая находится внутри main и foo, я бы вызвал main_foo_b, потому что function_names_list=["main","foo"]. И когда я вызываю функцию b, которая находится внутри main и bar, я вызываю main_bar_b, потому что function_names_list=["main","bar"]. Это, вероятно, решит проблему для текущего примера.

Но в следующем примере:

define main

   define f

      writeInteger: 1

   define g

      define f

         writeInteger: 2

      call f

   define h
      call f 


   call g     # prints 2
   call f     # prints 1
   call h     # prints 1

мне также нужно будет проверить, на каком уровне вложенности находится функция. Под уровнем вложенности я подразумеваю то, сколько в настоящее время я выполняю закрывающих функций. main имеет нулевые уровни вложенности, main_f, main_g и main_h имеют 1 уровень вложенности, а main_g_f имеет два уровня вложенности.

Мне нужно было бы проверить уровень вложенности, потому что я бы нужно изменить способ вызова f внутри h. Мне пришлось бы называть его main_f, а не main_h_f. Зная, что f, который я пытаюсь вызвать, находится на предыдущем уровне вложенности, Затем я могу использовать все элементы, кроме одного, из function_names_list (function_names_list[0:n-1]), чтобы получить имя main_f.

1 Ответ

1 голос
/ 19 июня 2020

Добро пожаловать в SO, Nik.

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

Думаю, в этом случае вам нужно добавить какой-то интервал имен, то есть назвать первый b как main_foo_b, а второй main_bar_b. Таким образом, вы можете четко различать «конфликтующие» имена.

Надеюсь, это поможет :)

UPD:

Кажется, я пошел слишком далеко, и предложение @arnt звучит более разумно. Ваше решение с function_names_list - правильное направление. Обычно это решается с помощью так называемой таблицы символов : таблица символов - это просто структура данных ключ-значение, которая также имеет указатель на родительскую таблицу символов. Ключи в вашем случае - это имена функций, а значения - это функции LLVM.

Вы начинаете с глобальной таблицы символов (для всей единицы перевода), затем всякий раз, когда вы входите в новую область, вы создаете новую таблицу символов и установите родительский элемент на «предыдущую» таблицу символов. Затем, когда вам нужно выполнить вызов функции, вы ищите названную функцию в текущей таблице символов, если ее нет, вы подходите и смотрите на родительскую таблицу символов. Делайте это рекурсивно, пока не дойдете до глобальной таблицы символов, и в этом случае об отсутствующей функции можно сообщить об ошибке.

Вот пример вашего первого фрагмента кода:

global_table:
  scope = <whole translation unit>
  parent = null
  "main" = <main function>

main_table:
  scope = <main function>
  parent = global_table
  "foo" = <foo function>
  "bar" = <bar function>

foo_table:
  scope = <foo function>
  parent = main_table
  "b" = <b function>

bar_table:
  scope = <bar function>
  parent = main_table
  "b" = <b.1 function>

( Таблицы для b и b.1 пусты, поэтому я пропустил их для краткости).

Затем, в зависимости от области действия (foo или bar), вы начнете искать либо * 1032. * или bar_table и должна найти правильную функцию b.

...