Предложение "Предположим" в gcc

Имеет ли gcc (последние версии: 4.8, 4.9) предложение "предположить", подобное __assume() встроенный поддерживается ICC? Например, __assume( n % 8 == 0 );

3 ответа

Решение

В вашем примере вы хотите сообщить компилятору, что N кратно 8. Вы можете сделать это, просто вставив строку

N = N & 0xFFFFFFF8;

в вашем коде (если N является 32-разрядным целым числом). Это не меняет N, так как N кратно 8, но, начиная с GCC 4.9, компилятор, похоже, понимает, что N кратно 8, после этой строки.

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

int add_a(float * restrict a, float * restrict b, int N)
{
    a = (float*)__builtin_assume_aligned(a, 32);
    b = (float*)__builtin_assume_aligned(b, 32);
    N = N & 0xFFFFFFF8; 
    for (int i = 0; i < N; i++){
        a[i] = a[i] + b[i];
    }
    return 0;
}


int add_b(float * restrict a, float * restrict b, int N)
{
    a = (float*)__builtin_assume_aligned(a, 32);
    b = (float*)__builtin_assume_aligned(b, 32);
    for (int i = 0; i < N; i++){
        a[i] = a[i] + b[i];
    }
    return 0;
}

С gcc -m64 -std=c99 -O3, gcc версия 4.9, add_a компилируется в векторизованный код

add_a:
  and edx, -8
  jle .L6
  sub edx, 4
  xor ecx, ecx
  shr edx, 2
  lea eax, [rdx+1]
  xor edx, edx
.L3:
  movaps xmm0, XMMWORD PTR [rdi+rdx]
  add ecx, 1
  addps xmm0, XMMWORD PTR [rsi+rdx]
  movaps XMMWORD PTR [rdi+rdx], xmm0
  add rdx, 16
  cmp ecx, eax
  jb .L3
.L6:
  xor eax, eax
  ret

С функцией add_bболее 20 дополнительных инструкций необходимы для решенияN не кратно 8:

add_b:
  test edx, edx
  jle .L17
  lea ecx, [rdx-4]
  lea r8d, [rdx-1]
  shr ecx, 2
  add ecx, 1
  cmp r8d, 2
  lea eax, [0+rcx*4]
  jbe .L16
  xor r8d, r8d
  xor r9d, r9d
.L11:
  movaps xmm0, XMMWORD PTR [rdi+r8]
  add r9d, 1
  addps xmm0, XMMWORD PTR [rsi+r8]
  movaps XMMWORD PTR [rdi+r8], xmm0
  add r8, 16
  cmp ecx, r9d
  ja .L11
  cmp eax, edx
  je .L17
.L10:
  movsx r8, eax
  lea rcx, [rdi+r8*4]
  movss xmm0, DWORD PTR [rcx]
  addss xmm0, DWORD PTR [rsi+r8*4]
  movss DWORD PTR [rcx], xmm0
  lea ecx, [rax+1]
  cmp edx, ecx
  jle .L17
  movsx rcx, ecx
  add eax, 2
  lea r8, [rdi+rcx*4]
  cmp edx, eax
  movss xmm0, DWORD PTR [r8]
  addss xmm0, DWORD PTR [rsi+rcx*4]
  movss DWORD PTR [r8], xmm0
  jle .L17
  cdqe
  lea rdx, [rdi+rax*4]
  movss xmm0, DWORD PTR [rdx]
  addss xmm0, DWORD PTR [rsi+rax*4]
  movss DWORD PTR [rdx], xmm0
.L17:
  xor eax, eax
  ret
.L16:
  xor eax, eax
  jmp .L10

Смотрите ссылку Godbolt.

Начиная с gcc 4.8.2, в gcc нет эквивалента __assume(). Я не знаю почему - это было бы очень полезно. мафсо предложил:

#define __assume(cond) do { if (!(cond)) __builtin_unreachable(); } while (0)

Это старый трюк, известный по крайней мере еще в 2010 году и, вероятно, дольше. Компилятор обычно оптимизирует оценку 'cond', потому что любая оценка, для которой cond имеет значение false, в любом случае будет неопределенной. Тем не менее, похоже, что он не оптимизирует "cond", если он содержит вызов непрозрачной (не встроенной) функции. Компилятор должен предположить, что непрозрачный вызов может иметь побочный эффект (например, изменить глобальный) и не может оптимизировать вызов, хотя он может оптимизировать любые вычисления и ответвления в результате. По этой причине макроподход в лучшем случае является частичным решением.

[[assume(...)]];- в C++23 добавлена ​​переносимая версия.

__attribute__((__assume__(...)));- добавлено в GCC 13 вместе с вышеперечисленным, полезно для кода C.

Другие вопросы по тегам