Я пишу компилятор для функционального языка игрушки, генерируя ИК-код LLVM. Тем не менее, у меня возникают некоторые проблемы, оптимизирующие случаи с вложенными функциями.Оптимизация LLVM IR вложенных функций
Функции и операторы на карри, поэтому y = 10 + 20
выполняется как f = plus(10); y = f(20)
. Пока что переходы оптимизации LLVM были достаточно умны, чтобы оптимизировать приведенное выше значение только до ret i32 30
.
Как только я добавил поддержку вложенных функций (с помощью лямбда-подъема с дополнительным аргументом env
для свободных идентификаторов), LLVM больше не мог оптимизировать даже простые примеры, например, выше.
Вот как работают вложенные функции: при вызове вложенной функции каждая свободная переменная, в которой она нуждается, записывается в массив указателей (env
) и передается вложенной функции. Вложенная функция загружает env
и считывает каждый индекс в локальный регистр. Я ожидал бы, что оптимизатор выполнит вызов, а затем устранит избыточное хранение и повторную загрузку env
. Оптимизатор строит вызов, но не может устранить env
.
минимальный пример исходного и оптимизированного кода ниже:
declare i8* @malloc(i32)
@x = private constant i32 42
define i32 @main() {
%env_pass = insertvalue [1 x i32*] zeroinitializer, i32* @x, 0
%env_pass_ptr = call i8* @malloc(i32 8)
%env_pass_cast = bitcast i8* %env_pass_ptr to [1 x i32*]*
store [1 x i32*] %env_pass, [1 x i32*]* %env_pass_cast
%res = call i32 @nested_func(i8* %env_pass_ptr)
ret i32 %res
}
define private i32 @nested_func(i8* %env) {
%env_ptr = bitcast i8* %env to [1 x i32*]*
%env_val = load [1 x i32*]* %env_ptr
%my_x = extractvalue [1 x i32*] %env_val, 0
%val = load i32* %my_x
ret i32 %val
}
Оптимизация для:
@x = private constant i32 42
declare noalias i8* @malloc(i32) #0
define i32 @main() #0 {
%env_pass_ptr = tail call i8* @malloc(i32 8)
%env_pass_cast = bitcast i8* %env_pass_ptr to [1 x i32*]*
store [1 x i32*] [i32* @x], [1 x i32*]* %env_pass_cast
%1 = getelementptr inbounds [1 x i32*]* %env_pass_cast, i32 0, i32 0
%2 = load i32** %1
%val.i = load i32* %2
ret i32 %val.i
}
attributes #0 = { nounwind }
Этот пример должен сократить до просто ret i32 42
. Я подозреваю, что проблема связана с getelementptr
, который создается оптимизатором.
Вот некоторые пасты из полного оригинального и оптимизированного кода для выражения 10 + 20
: original, optimized
В этом случае я мог бы выбрать, чтобы не передать функции «добавить» в окружающей среде, так как это глобальная. Тем не менее, я бы по-прежнему ожидал, что этот пример будет правильно оптимизирован.