Подтвердить что ты не робот

GCC Aliasing Checks w/Ограничивающие указатели

Рассмотрим следующие два фрагмента:

#define ALIGN_BYTES 32
#define ASSUME_ALIGNED(x) x = __builtin_assume_aligned(x, ALIGN_BYTES)

void fn0(const float *restrict a0, const float *restrict a1,
         float *restrict b, int n)
{
    ASSUME_ALIGNED(a0); ASSUME_ALIGNED(a1); ASSUME_ALIGNED(b);

    for (int i = 0; i < n; ++i)
        b[i] = a0[i] + a1[i];
}

void fn1(const float *restrict *restrict a, float *restrict b, int n)
{
    ASSUME_ALIGNED(a[0]); ASSUME_ALIGNED(a[1]); ASSUME_ALIGNED(b);

    for (int i = 0; i < n; ++i)
        b[i] = a[0][i] + a[1][i];
}

Когда я компилирую функцию как gcc-4.7.2 -Ofast -march=native -std=c99 -ftree-vectorizer-verbose=5 -S test.c -Wall, я обнаруживаю, что GCC вставляет aliasing проверки для второй функции.

Как я могу предотвратить это, чтобы результирующая сборка для fn1 была такой же, как для fn0? (Когда число параметров увеличивается от трех до, скажем, 30, подход с аргументацией (fn0) становится громоздким, а число проверок сглаживания в подходе fn1 становится смешным.)

Сборка (x86-64, чип, совместимый с AVX); aliasing cruft at.LFB10

fn0:
.LFB9:
    .cfi_startproc
    testl   %ecx, %ecx
    jle .L1
    movl    %ecx, %r10d
    shrl    $3, %r10d
    leal    0(,%r10,8), %r9d
    testl   %r9d, %r9d
    je  .L8
    cmpl    $7, %ecx
    jbe .L8
    xorl    %eax, %eax
    xorl    %r8d, %r8d
    .p2align 4,,10
    .p2align 3
.L4:
    vmovaps (%rsi,%rax), %ymm0
    addl    $1, %r8d
    vaddps  (%rdi,%rax), %ymm0, %ymm0
    vmovaps %ymm0, (%rdx,%rax)
    addq    $32, %rax
    cmpl    %r8d, %r10d
    ja  .L4
    cmpl    %r9d, %ecx
    je  .L1
.L3:
    movslq  %r9d, %rax
    salq    $2, %rax
    addq    %rax, %rdi
    addq    %rax, %rsi
    addq    %rax, %rdx
    xorl    %eax, %eax
    .p2align 4,,10
    .p2align 3
.L6:
    vmovss  (%rsi,%rax,4), %xmm0
    vaddss  (%rdi,%rax,4), %xmm0, %xmm0
    vmovss  %xmm0, (%rdx,%rax,4)
    addq    $1, %rax
    leal    (%r9,%rax), %r8d
    cmpl    %r8d, %ecx
    jg  .L6
.L1:
    vzeroupper
    ret
.L8:
    xorl    %r9d, %r9d
    jmp .L3
    .cfi_endproc
.LFE9:
    .size   fn0, .-fn0
    .p2align 4,,15
    .globl  fn1
    .type   fn1, @function
fn1:
.LFB10:
    .cfi_startproc
    testq   %rdx, %rdx
    movq    (%rdi), %r8
    movq    8(%rdi), %r9
    je  .L12
    leaq    32(%rsi), %rdi
    movq    %rdx, %r10
    leaq    32(%r8), %r11
    shrq    $3, %r10
    cmpq    %rdi, %r8
    leaq    0(,%r10,8), %rax
    setae   %cl
    cmpq    %r11, %rsi
    setae   %r11b
    orl %r11d, %ecx
    cmpq    %rdi, %r9
    leaq    32(%r9), %r11
    setae   %dil
    cmpq    %r11, %rsi
    setae   %r11b
    orl %r11d, %edi
    andl    %edi, %ecx
    cmpq    $7, %rdx
    seta    %dil
    testb   %dil, %cl
    je  .L19
    testq   %rax, %rax
    je  .L19
    xorl    %ecx, %ecx
    xorl    %edi, %edi
    .p2align 4,,10
    .p2align 3
.L15:
    vmovaps (%r9,%rcx), %ymm0
    addq    $1, %rdi
    vaddps  (%r8,%rcx), %ymm0, %ymm0
    vmovaps %ymm0, (%rsi,%rcx)
    addq    $32, %rcx
    cmpq    %rdi, %r10
    ja  .L15
    cmpq    %rax, %rdx
    je  .L12
    .p2align 4,,10
    .p2align 3
.L20:
    vmovss  (%r9,%rax,4), %xmm0
    vaddss  (%r8,%rax,4), %xmm0, %xmm0
    vmovss  %xmm0, (%rsi,%rax,4)
    addq    $1, %rax
    cmpq    %rax, %rdx
    ja  .L20
.L12:
    vzeroupper
    ret
.L19:
    xorl    %eax, %eax
    jmp .L20
    .cfi_endproc
4b9b3361

Ответ 1

Отключить, чтобы сообщить компилятору прекратить проверку псевдонимов:

добавьте строку:

#pragma GCC ivdep

прямо перед циклом, который вы хотите векторизовать, если вам нужна дополнительная информация, пожалуйста, прочитайте:

https://gcc.gnu.org/onlinedocs/gcc-4.9.2/gcc/Loop-Specific-Pragmas.html

Ответ 2

Может ли это помочь?

void fn1(const float **restrict a, float *restrict b, int n)
{
    const float * restrict a0 = a[0];
    const float * restrict a1 = a[1];

    ASSUME_ALIGNED(a0); ASSUME_ALIGNED(a1); ASSUME_ALIGNED(b);

    for (int i = 0; i < n; ++i)
        b[i] = a0[i] + a1[i];
}

Изменить: вторая попытка:). С информацией из http://locklessinc.com/articles/vectorize/

gcc --fast-math ...

Ответ 3

Ну, а как насчет флага

-fno-strict-aliasing

?

Как я понял, вы правы, вы просто хотите знать, как отключить эти проверки? Если это все, этот параметр в командной строке gcc должен помочь вам.

EDIT:

В дополнение к вашему комментарию: запрещено ли использовать указатели ограничения типа const?

это из ISO/IEC 9899 (6.7.3.1 Формальное определение ограничения):

1.

Пусть D - объявление обычного идентификатора, которое предоставляет средство для обозначения объект P как указатель с ограничениями типа T.

4.

Во время каждого исполнения B пусть L - любое l-значение, которое имеет & L на основе P. Если L используется для доступ к значению объекта X, который он обозначает, и X также модифицируется (любым способом), то применяются следующие требования: T не должен быть const-квалифицированным. Все остальные значения используемый для доступа к значению X, также должен иметь свой адрес на основе P. Каждый доступ, который изменяет X, также следует рассматривать P для целей настоящего подпункта. Если P присваивается значение выражения указателя E, которое основано на другом ограниченном указателе объект P2, связанный с блоком B2, то либо выполнение B2 должно начинаться раньше выполнение B или выполнение B2 заканчивается до назначения. Если эти требования не выполняются, тогда поведение undefined.

И гораздо интереснее, так же как и в регистре:

6.

Переводчик может игнорировать любые или все последствия использования ограничений ограничения.

Итак, если вы не можете найти параметр команды, который заставляет gcc делать это, возможно, это невозможно, потому что по стандарту он не должен давать возможность сделать это.

Ответ 4

Извиняюсь заранее, потому что я не могу воспроизвести результаты с GCC 4.7 на своей машине, но есть два возможных решения.

  • Используйте typedef для правильной компоновки * restrict * restrict. Это, по словам бывшего коллеги, который разработчик компилятора LLVM, единственное исключение из typedef ведет себя как препроцессор в C, и он существует, чтобы позволить вам поведение сглаживания.

    Я попытался это сделать ниже, но я не уверен, что мне это удалось. Пожалуйста, внимательно проверьте мою попытку.

  • Используйте синтаксис, описанный в ответах на используя ограничитель ограничений с массивами переменной длины (VLA) C99.

    Я попытался это сделать ниже, но я не уверен, что мне это удалось. Пожалуйста, внимательно проверьте мою попытку.

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

#define ALIGN_BYTES 32
#define ASSUME_ALIGNED(x) x = __builtin_assume_aligned(x, ALIGN_BYTES)

void fn0(const float *restrict a0, const float *restrict a1,
         float *restrict b, int n)
{
    ASSUME_ALIGNED(a0); ASSUME_ALIGNED(a1); ASSUME_ALIGNED(b);

    for (int i = 0; i < n; ++i)
        b[i] = a0[i] + a1[i];
}

#if defined(ARRAY_RESTRICT)
void fn1(const float *restrict a[restrict], float * restrict b, int n)
#elif defined(TYPEDEF_SOLUTION)
typedef float * restrict frp;
void fn1(const frp *restrict a, float *restrict b, int n)
#else
void fn1(const float *restrict *restrict a, float *restrict b, int n)
#endif
{
    //ASSUME_ALIGNED(a[0]); ASSUME_ALIGNED(a[1]); ASSUME_ALIGNED(b);

    for (int i = 0; i < n; ++i)
        b[i] = a[0][i] + a[1][i];
}

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