#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 . Я неправильно понял, о чем вы спрашивали.