Почему LLVM выделяет избыточную переменную? - PullRequest
9 голосов
/ 06 января 2020

Вот простой C файл с определением перечисления и функцией main:

enum days {MON, TUE, WED, THU};

int main() {
    enum days d;
    d = WED;
    return 0;
}

Он переносится в следующий IR LLVM:

define dso_local i32 @main() #0 {
  %1 = alloca i32, align 4
  %2 = alloca i32, align 4
  store i32 0, i32* %1, align 4
  store i32 2, i32* %2, align 4
  ret i32 0
}

%2 очевидно, это переменная d, которая получает 2. Что соответствует %1, если ноль возвращается напрямую?

Ответы [ 3 ]

3 голосов
/ 03 мая 2020

Этот регистр %1 был сгенерирован clang для обработки нескольких операторов возврата в функции . Представьте, что у вас есть функция для вычисления факториала целого числа. Вместо того, чтобы писать это так:

int factorial(int n){
    int result;
    if(n < 2)
      result = 1;
    else{
      result = n * factorial(n-1);
    }
    return result;
}

Вы, вероятно, сделали бы это

int factorial(int n){
    if(n < 2)
      return 1;
    return n * factorial(n-1);
}

Почему? Потому что Clang вставит переменную result, которая содержит возвращаемое значение для вас. Ура. Это точная цель этого %1. Посмотрите на ИК для немного измененной версии вашего кода.

Модифицированный код,

enum days {MON, TUE, WED, THU};

int main() {
    enum days d;
    d = WED;
    if(d) return 1;
    return 0;
}

IR,

define dso_local i32 @main() #0 !dbg !15 {
    %1 = alloca i32, align 4
    %2 = alloca i32, align 4
    store i32 0, i32* %1, align 4
    store i32 2, i32* %2, align 4, !dbg !22
    %3 = load i32, i32* %2, align 4, !dbg !23
    %4 = icmp ne i32 %3, 0, !dbg !23
    br i1 %4, label %5, label %6, !dbg !25

 5:                                                ; preds = %0
   store i32 1, i32* %1, align 4, !dbg !26
   br label %7, !dbg !26

 6:                                                ; preds = %0
  store i32 0, i32* %1, align 4, !dbg !27
  br label %7, !dbg !27

 7:                                                ; preds = %6, %5
  %8 = load i32, i32* %1, align 4, !dbg !28
  ret i32 %8, !dbg !28
}

Теперь вы видите, что %1 делает сам полезен да? Как уже отмечали другие, для функций только с одним оператором return эта переменная, вероятно, будет удалена одним из оптимистических проходов llvm.

1 голос
/ 02 мая 2020

Поскольку Clang выполняется с помощью синтаксического анализа, но LLVM даже не запускается с оптимизацией.

Внешний интерфейс Clang генерирует IR (промежуточное представление), а не машинный код. Этими переменными являются SSA (Single Stati c Assignments); они еще не были связаны с регистрами и фактически после оптимизации никогда не будут, потому что они избыточны.

Этот код является несколько буквальным представлением источника. Это то, что раздается LLVM для оптимизации По сути, LLVM начинается с этого и оптимизируется оттуда. Действительно, для версии 10 и x86_64 ll c -O2 в конечном итоге сгенерирует:

main: # @main
  xor eax, eax
  ret
1 голос
/ 06 января 2020

Почему это имеет значение - в чем собственно проблема?

Я думаю, что более глубокий ответ, который вы ищете, может быть таким: архитектура LLVM основана на довольно простых интерфейсах и множестве проходов. Интерфейсы должны генерировать правильный код, но это не обязательно должен быть хороший код. Они могут сделать самое простое, что работает.

В этом случае Clang генерирует пару инструкций, которые, как оказалось, ни для чего не используются. Обычно это не проблема, потому что некоторая часть LLVM избавится от лишних инструкций. Clang верит, что это произойдет. Clang не нужно избегать испускания мертвого кода; его реализация может фокусироваться на правильности, простоте, тестируемости и т. д. c.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...