2015-04-15 5 views
1

Я пишу компилятор для функционального языка игрушки, генерируя ИК-код 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

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

ответ

0

Я обнаружил, что проблема была уменьшена, если я вместо этого написал непосредственно индексы с getelementptr, вместо того, чтобы использовать insertvalue. Это произошло после нахождения этого другого вопроса, предлагающего LLVM isn't great at optimizing insertvalue.

После использования getelementptr приведенный выше пример сводится к ret i32 42, и теперь более простая арифметика на моем языке сводится к константе. Методы, которые используют более одного оператора (например, (10+20)*5), до сих пор не полностью уменьшаются.

0

В ваших примерах нет строки макета данных. Без этого многие оптимизаторы не могут обеспечить достойные результаты - они не могут знать размер указателя и т. Д.

Для получения дополнительной информации см. http://llvm.org/docs/LangRef.html#data-layout. Я бы предложил захватить строку макета данных, созданную clang на вашей платформе, и вставить в .ll в качестве первого шага.

Смежные вопросы