Соответствует ли оптимизация условного перемещения стандарту C? - PullRequest
0 голосов
/ 21 мая 2018

Обычной оптимизацией является использование условного перемещения (сборка cmov) для оптимизации условного выражения ?: в C. Однако стандарт C говорит:

Первый операнд вычисляется;между оценкой и оценкой второго или третьего операнда существует точка последовательности (в зависимости от того, что оценивается).Второй операнд оценивается, только если первый сравнивается с неравным 0;третий операнд оценивается, только если первый сравнивается равным 0;результатом является значение второго или третьего операнда (в зависимости от того, что вычислено), преобразованное в тип, описанный ниже. 110)

Например, следующий код C

#include <stdio.h>

int main() {
    int a, b;
    scanf("%d %d", &a, &b);
    int c= a > b ? a + 1 : 2 + b;
    printf("%d", c);
    return 0;
}

создаст оптимизированный связанный код asm следующим образом:

call    __isoc99_scanf
movl    (%rsp), %esi
movl    4(%rsp), %ecx
movl    $1, %edi
leal    2(%rcx), %eax
leal    1(%rsi), %edx
cmpl    %ecx, %esi
movl    $.LC1, %esi
cmovle  %eax, %edx
xorl    %eax, %eax
call    __printf_chk

В соответствии со стандартом, условное выражение будет иметь только одну оцененную ветвь.Но здесь оцениваются обе ветви, что противоречит семантике стандарта.Это оптимизация против стандарта C?Или у многих оптимизаций компилятора есть что-то несовместимое со стандартом языка?

Ответы [ 2 ]

0 голосов
/ 21 мая 2018

Оптимизация допустима из-за «как если бы правила» , то есть C11 5.1.2.3p6 .

Соответствующая реализация просто необходима для создания программы, которая при запуске выдает такое же наблюдаемое поведение , что и при выполнении программы с использованием абстрактной семантики получилось бы .Остальная часть стандарта просто описывает эти абстрактную семантику .

То, что внутренне скомпилированная программа не имеет значения, единственное, что имеет значение, это то, что когда программа заканчивается, она не имеетлюбое другое наблюдаемое поведение, за исключением чтения a и b и печати значения a + 1 или b + 2 в зависимости от того, какое из них a или b больше, если не происходит что-то, что вызывает поведение, не определенное,(Неправильный ввод приводит к тому, что a, b неинициализируются и, следовательно, доступы не определены; возможны ошибки диапазона и переполнение со знаком.) Если происходит неопределенное поведение, все ставки отключены.


Поскольку доступ к изменчивым переменным долженоцениваясь строго в соответствии с абстрактной семантикой, вы можете избавиться от условного перемещения, используя volatile здесь:

#include <stdio.h>

int main() {
    volatile int a, b;
    scanf("%d %d", &a, &b);
    int c = a > b ? a + 1 : 2 + b;
    printf("%d", c);
    return 0;
}

компилируется в

        call    __isoc99_scanf@PLT
        movl    (%rsp), %edx
        movl    4(%rsp), %eax
        cmpl    %eax, %edx
        jg      .L7
        movl    4(%rsp), %edx
        addl    $2, %edx
.L3:
        leaq    .LC1(%rip), %rsi
        xorl    %eax, %eax
        movl    $1, %edi
        call    __printf_chk@PLT

        [...]

.L7:
        .cfi_restore_state
        movl    (%rsp), %edx
        addl    $1, %edx
        jmp     .L3

моим GCC Ubuntu 7.2.0-8ubuntu3.2

0 голосов
/ 21 мая 2018

Стандарт C описывает абстрактную машину, выполняющую код C.Компилятор может выполнять любую оптимизацию до тех пор, пока эта абстракция не нарушается, то есть соответствующая программа не может определить разницу.

...