2013-03-25 4 views
35

Рассмотрим следующие два фрагмента:GCC Aliasing Проверка ж/Ограничить указатели

#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 вставляет альясинга проверки для второй функции.

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

собрания (x86-64, AVX способный чип); альясинга хлама на .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 
+0

Имеется ли вообще опция '--param vect-max-version-for-alias-checks = n'? – teppic

+0

Это помогает, когда много указателей находятся в игре (часто GCC просто перестает пытаться векторизовать функцию, если n ~ 100). Тем не менее, мне интересно, как я могу убедить GCC, что эти проверки бессмысленны. –

+0

Не могли бы вы показать сборку, создаваемую вашим компилятором? –

ответ

0

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

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 ...

+0

К сожалению, нет; GCC по-прежнему испускает проверки наложения. –

0

Ну, что флаг

-fno-strict-aliasing 

?

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

EDIT:

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

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

1.

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

4.

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

И гораздо более интересный момент, так же, как и регистр это один:

6.

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

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

+0

Вы пробовали? –

+0

@Freddie Witherden Я не очень хорошо разбираюсь в ассемблере, но знаю, что строки, которые вы упомянули, будут вне the.o этим флагом (который я тестировал в моих случаях до сих пор). и я также знаю, что эта строка советует компилятору не делать никакой оптимизации, основанной на правилах псевдонимов, поэтому я предлагаю, чтобы он не проверял псевдонимы с этим флагом, если ему не разрешено действовать в зависимости от проверок thoose. Так что это должно решить вам proplem, да. – dhein

+0

Быстрый просмотр кода сборки не изменит. –

1

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

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

#pragma GCC ivdep 

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

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

+0

Мы на 4.7.2, не так ли? 4.7.4 дает мне: предупреждение: игнорирование #pragma GCC ivdep [-Wunknown-pragmas] –

0

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

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

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

  2. Используйте синтаксис, описанный в ответах на using restrict qualifier with C99 variable length arrays (VLAs).

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

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

#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]; 
} 

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

+0

В gcc-4.7.4 как 'ARRAY_RESTRICT', так и' TYPEDEF_RESTRICT' генерируют ту же сборку для 'fn1', что и случай по умолчанию (aliasing check). –

+0

Да, это то, что я тоже видел, но я не думаю, что GCC 4.7 является самым агрессивным компилятором для автоматической векторизации. – Jeff