LLVM — Как заставить вложенную функцию видеть переменные внешней функции

#c #llvm

#c #llvm

Вопрос:

Я пытаюсь написать компилятор для языка, который поддерживает вложенные функции, например:

 func a()
   int x;
   func b()
      int y;
      {
       // code of func b - both x and y are visible here
      }
   {
    // code of func a - only x is visible here
   }
 

Я использую LLVM API на c для компиляции кода. Моя проблема в том, что я не знаю, как сделать переменную x видимой в функции b, поскольку, насколько мне известно, llvm не поддерживает вложенную функцию. На данный момент я объявляю переменную так::

 static AllocaInst *CreateEntryBlockAlloca(Function *TheFunction, const std::string amp;VarName, Type *T) {
    IRBuilder<> TmpB(amp;TheFunction->getEntryBlock(), TheFunction->getEntryBlock().begin());
    return TmpB.CreateAlloca(T, 0, VarName.c_str());
}
 

Как показано в руководстве по llvm https://llvm.org/docs/tutorial/LangImpl07.html#adjusting-existing-variables-for-mutation .
При использовании этого объявления и попытке использовать внешнюю переменную во вложенной функции появляется эта ошибка: инструкция не доминирует во всех применениях!.
Есть ли способ это исправить?

Комментарии:

1. c не поддерживает определения вложенных функций, кроме определений лямбда-функций. Неясно, что вам нужно.

2. Это не поддерживаемая практика программирования на C . Если вы хотите получить доступ к переменной в родительской функции, передайте ссылку на нее.

3. Я пытаюсь скомпилировать код в примере (с функциями a и b) в код llvm (не уверен, что это то, что вызывается) для компиляции llc . Но поскольку llvm является c-подобным языком и не поддерживает вложенные функции, мне нужен другой способ сделать переменную x видимой и доступной в функции b . Глобальные переменные были бы способом, но я не думаю, что их действительно целесообразно использовать.

4. @JohnFikioris итак, это то, что делают настоящие компиляторы: они преобразуют такие вложенные функции в класс с конструктором и методом (являющимся фактическим телом функции). Конструктор принимает ссылку (или значение, зависит) на внешнюю переменную и сохраняет ее в объекте. Затем код преобразуется в инструкции «создать объект» и «вызвать метод». Это то, что вам нужно реализовать.

5. @freakish У вас есть какие-либо предложения о том, как это сделать?

Ответ №1:

LLVM поддерживает структуры, верно? Итак, вот что делают типичные компиляторы.

Вам нужно создать анонимную структуру с полями, сгенерированными из каждой внешней переменной, на которую вы ссылаетесь. Затем вы создаете анонимную функцию, соответствующую b() той, которая принимает эту структуру в качестве аргумента и работает с ней. По сути, вы превращаетесь b() в обычную функцию верхнего уровня. Наконец, вы преобразуете a() код так, чтобы он создавал экземпляр struct и вызывал анонимную функцию. На этом этапе возможны дальнейшие оптимизации. Будьте готовы: это совсем не просто, возможно, очень сложная тема для преобразований кода.

Например

 func a()
   int x = 1;
   func b() {
      return x 1;
   }
   return b()   2;
}
 

становится

 struct tmp {
    int tmpx;  // reference or value?
}
func tmp_b(tmpamp; instance) {
    instance.tmpx  = 1;
    return instance.tmpx;
}
func a() {
    int x = 1;
    tmp instance(tmpx = x);  // should it go by reference or value?
    return tmp_b(instance)   2;
}
 

В качестве альтернативы вы можете преобразовать b() в b(int x) функцию верхнего уровня. Но этот подход менее гибкий, IMO. Или, в зависимости от контекста, использовать оба подхода, почему бы и нет.

Обратите внимание, что все это, вероятно, можно упростить, если ваш язык поддерживает надлежащие классы с перегрузкой методов и / или операторов (в данном случае call operator ).

Ответ №2:

Это не поддерживается в C .

Если вы хотите получить доступ к переменной таким образом из вложенной или любой другой функции, передайте ссылку на нее. 🙂

Комментарии:

1. Код, который я пытаюсь скомпилировать, не является C . Я использую LLVM API для создания скомпилированного исполняемого файла. Язык, который я хочу скомпилировать, поддерживает вложенные функции.

2. Ах… У вас это помечено как вопрос C . Я неправильно понял, о чем вы спрашивали.