Прагма переупорядочена на cpp - PullRequest
0 голосов
/ 05 апреля 2020

Я наблюдал странное поведение в cpp при работе с прагмами и макросами: он перемещает эти директивы с их фактической строки в начало раскрытия макроса.

Пример:

#define K( arg ) arg

K(
    int a = 0;

    #pragma unroll
    for(int i=0; i < 5; i++) {
        a++;
    }
)

Вывод:

#pragma unroll
int a = 0; for(int i=0; i < 5; i++) { a++; }

В чем причина этого? Есть ли способ сохранить прагму на месте? Пожалуйста, воздержитесь от предложения использовать sed или что-то подобное, я хочу решить эту проблему только с помощью препроцессора.

1 Ответ

1 голос
/ 05 апреля 2020

Передача выбранных комментариев в ответ и расширение информации…

Если что-то собирается сделать, используя §6.10.9 Прагма оператор - _Pragma("unroll") - это, вероятно, способ сделать это. Вы не можете также встраивать другие директивы предварительной обработки, такие как #if и #endif, в тело вызова макроса. Однако быстрый эксперимент показал, что gcc -E по-прежнему ставит #pragma перед расширенным l oop. Возможно, вам придется проверить, действует ли прагма, посмотрев код ассемблера. Оптимизатор может изменить этот конкретный l oop на a = 5;, исключив в целом i и ничего не развернув.

Кроме того, G CC L oop -Specifi * 1070 Документация * Pragmas гласит, что вам нужно использовать #pragma GCC unroll n (с указанием, сколько раз развернуть l oop).

Вы не можете полагаться на любой #pragma, который не задокументирован стандартом. быть переносимым на любой компилятор. Прагмы изначально определены компилятором c. Существует 3 стандартных прагмы, каждая из которых начинается с #pragma STDC - ни одна из них не связана с развертыванием l oop. См. Также §6.10.6 Директива Pragma для стандартных прагм и документацию "все зависит от компилятора".

Делая код в l oop немного более сложным, вы можно получить l oop развернутым, если вы используете:

  • _Pragma("GCC unroll 5")

Не работает, если вы попытаетесь:

  • #pragma GCC unroll 5

Пример кода:

extern double course_deviation(double x, double y);
#define K( arg ) arg

int main(void)
{
K(
    int a = 0;
    _Pragma("GCC unroll 5")
    //#pragma GCC unroll 5
    for (int i = 0; i < 5; i++)
    {
        a += course_deviation(i + 3.0, i - 3.0);
    }
)
    return a;
}

При наличии директивы #pragma вместо оператора _Pragma код не компилируется. Как показано, соответствующий раздел кода ассемблера, созданного G CC 9.3.0 в macOS Mojave 10.14.6:

$ gcc -O -S k37.c
$ sed '/LFE0/q' k37.s
        .text
        .section __TEXT,__text_startup,regular,pure_instructions
        .globl _main
_main:
LFB0:
        subq    $24, %rsp
LCFI0:
        movsd   lC0(%rip), %xmm1
        movsd   lC1(%rip), %xmm0
        call    _course_deviation
        movsd   %xmm0, 8(%rsp)
        movsd   lC2(%rip), %xmm1
        movsd   lC3(%rip), %xmm0
        call    _course_deviation
        movapd  %xmm0, %xmm1
        pxor    %xmm2, %xmm2
        addsd   8(%rsp), %xmm2
        cvttsd2sil      %xmm2, %eax
        pxor    %xmm0, %xmm0
        cvtsi2sdl       %eax, %xmm0
        addsd   %xmm1, %xmm0
        movsd   %xmm0, 8(%rsp)
        movsd   lC5(%rip), %xmm1
        movsd   lC6(%rip), %xmm0
        call    _course_deviation
        movapd  %xmm0, %xmm1
        cvttsd2sil      8(%rsp), %eax
        pxor    %xmm0, %xmm0
        cvtsi2sdl       %eax, %xmm0
        addsd   %xmm1, %xmm0
        movsd   %xmm0, 8(%rsp)
        pxor    %xmm1, %xmm1
        movsd   lC7(%rip), %xmm0
        call    _course_deviation
        movapd  %xmm0, %xmm1
        cvttsd2sil      8(%rsp), %eax
        pxor    %xmm0, %xmm0
        cvtsi2sdl       %eax, %xmm0
        addsd   %xmm1, %xmm0
        movsd   %xmm0, 8(%rsp)
        movsd   lC8(%rip), %xmm1
        movsd   lC9(%rip), %xmm0
        call    _course_deviation
        movapd  %xmm0, %xmm1
        cvttsd2sil      8(%rsp), %eax
        pxor    %xmm0, %xmm0
        cvtsi2sdl       %eax, %xmm0
        addsd   %xmm1, %xmm0
        cvttsd2sil      %xmm0, %eax
        addq    $24, %rsp
LCFI1:
        ret
LFE0:

Без опции -O (без оптимизации), соответствующий раздел Код ассемблера выглядит следующим образом - без l oop раскрутка. Похоже, что развертывание l oop является оптимизацией - без оптимизации, без развертывания l oop.

        .text
        .globl _main
_main:
LFB0:
        pushq   %rbp
LCFI0:
        movq    %rsp, %rbp
LCFI1:
        subq    $16, %rsp
        movl    $0, -4(%rbp)
        movl    $0, -8(%rbp)
        jmp     L2
L3:
        cvtsi2sdl       -8(%rbp), %xmm0
        movsd   lC0(%rip), %xmm1
        movapd  %xmm0, %xmm2
        subsd   %xmm1, %xmm2
        cvtsi2sdl       -8(%rbp), %xmm1
        movsd   lC0(%rip), %xmm0
        addsd   %xmm1, %xmm0
        movapd  %xmm2, %xmm1
        call    _course_deviation
        cvtsi2sdl       -4(%rbp), %xmm1
        addsd   %xmm1, %xmm0
        cvttsd2sil      %xmm0, %eax
        movl    %eax, -4(%rbp)
        addl    $1, -8(%rbp)
L2:
        cmpl    $4, -8(%rbp)
        setle   %al
        testb   %al, %al
        jne     L3
        movl    -4(%rbp), %eax
        leave
LCFI2:
        ret
LFE0:

Я пытался использовать atan2() из <math.h> вместо функции course_correction(), и оптимизатор оптимизировал код до - просто возвращая 7:

        .text
        .section __TEXT,__text_startup,regular,pure_instructions
        .globl _main
_main:
LFB19:
        movl    $7, %eax
        ret
LFE19:

С исходным кодом (a++; в теле l oop, результат возвращался 5 вместо 7.

...