Является ли эта неправильная генерация кода с массивами значений m256 ошибкой clang?
я сталкиваюсь с тем, что кажется ошибкой, вызывающей неправильную генерацию кода с Clang 3.4, 3.5 и 3.6 trunk. Источник, который фактически вызвал проблему, довольно сложен, но я смог свести его к этому автономному примеру:
#include <iostream>
#include <immintrin.h>
#include <string.h>
struct simd_pack
{
enum { num_vectors = 1 };
__m256i _val[num_vectors];
};
simd_pack load_broken(int8_t *p)
{
simd_pack pack;
for (int i = 0; i < simd_pack::num_vectors; ++i) pack._val[i] = _mm256_loadu_si256(reinterpret_cast<__m256i *>(p + i * 32));
return pack;
}
void store_broken(int8_t *p, simd_pack pack)
{
for (int i = 0; i < simd_pack::num_vectors; ++i) _mm256_storeu_si256(reinterpret_cast<__m256i *>(p + i * 32), pack._val[i]);
}
void test_broken(int8_t *out, int8_t *in1, size_t n)
{
size_t i = 0;
for (; i + 31 < n; i += 32)
{
simd_pack p1 = load_broken(in1 + i);
store_broken(out + i, p1);
}
}
int main()
{
int8_t in_buf[256];
int8_t out_buf[256];
for (size_t i = 0; i < 256; ++i) in_buf[i] = i;
test_broken(out_buf, in_buf, 256);
if (memcmp(in_buf, out_buf, 256)) std::cout << "test_broken() failed!" << std::endl;
return 0;
}
резюме выше: у меня простой типа simd_pack
, который содержит один член, массив __m256i
значение. В моем приложении, есть операторы и функции, которые принимают эти типы, но проблема можно проиллюстрировать приведенным выше примером. В частности, test_broken()
должен читать in1
массив, а затем просто скопируйте его значение в out
массив. Поэтому призыв к memcmp()
на main()
должна возвращать ноль. Я компилирую выше, используя следующее:
clang++-3.6 bug_test.cc -o bug_test -mavx -O3
я считаю, что на уровнях оптимизации -O0
и -O1
, тест проходит, в то время как на уровнях -O2
и -O3
, тест завершается неудачей. Я попытался скомпилировать тот же файл с gcc 4.4, 4.6, 4.7 и 4.8, как а так же Intel C++ 13.0, и тест проходит на всех уровнях оптимизации.
присмотревшись к сгенерированному коду, вот сборка, сгенерированная на уровне оптимизации -O3
:
0000000000400a40 <test_broken(signed char*, signed char*, unsigned long)>:
400a40: 55 push %rbp
400a41: 48 89 e5 mov %rsp,%rbp
400a44: 48 81 e4 e0 ff ff ff and xffffffffffffffe0,%rsp
400a4b: 48 83 ec 40 sub x40,%rsp
400a4f: 48 83 fa 20 cmp x20,%rdx
400a53: 72 2f jb 400a84 <test_broken(signed char*, signed char*, unsigned long)+0x44>
400a55: 31 c0 xor %eax,%eax
400a57: 66 0f 1f 84 00 00 00 nopw 0x0(%rax,%rax,1)
400a5e: 00 00
400a60: c5 fc 10 04 06 vmovups (%rsi,%rax,1),%ymm0
400a65: c5 f8 29 04 24 vmovaps %xmm0,(%rsp)
400a6a: c5 fc 28 04 24 vmovaps (%rsp),%ymm0
400a6f: c5 fc 11 04 07 vmovups %ymm0,(%rdi,%rax,1)
400a74: 48 8d 48 20 lea 0x20(%rax),%rcx
400a78: 48 83 c0 3f add x3f,%rax
400a7c: 48 39 d0 cmp %rdx,%rax
400a7f: 48 89 c8 mov %rcx,%rax
400a82: 72 dc jb 400a60 <test_broken(signed char*, signed char*, unsigned long)+0x20>
400a84: 48 89 ec mov %rbp,%rsp
400a87: 5d pop %rbp
400a88: c5 f8 77 vzeroupper
400a8b: c3 retq
400a8c: 0f 1f 40 00 nopl 0x0(%rax)
я воспроизведу ключевую часть для акцента:
400a60: c5 fc 10 04 06 vmovups (%rsi,%rax,1),%ymm0
400a65: c5 f8 29 04 24 vmovaps %xmm0,(%rsp)
400a6a: c5 fc 28 04 24 vmovaps (%rsp),%ymm0
400a6f: c5 fc 11 04 07 vmovups %ymm0,(%rdi,%rax,1)
это своего рода почесывание головы. Сначала он загружает 256 бит в ymm0
используя несогласованный ход, который я просил, затем он хранит xmm0
(который содержит только нижние 128 бит данных, read) в стек, затем немедленно считывает 256 бит в ymm0
из расположения стека, в которое только что было записано. Эффект таков, что ymm0
верхние 128 бит (которые записываются в выходной буфер) являются мусором, что приводит к сбою теста.
есть ли какая-то веская причина, по которой это может произойти, кроме ошибки компилятора? Я нарушаю какое-то правило, имея simd_pack
введите удерживать массив __m256i
значения? Это, безусловно, связано с этим; если я изменюсь _val
чтобы быть одним значением вместо массива, тогда сгенерированный код работает по назначению. Тем не менее, мое приложение требует _val
быть массивом (его длина зависит от параметра шаблона C++).
какие идеи?
1 ответов
Это ошибка в clang. Тот факт, что это произошло в-O0, является хорошим признаком того, что ошибка находится в интерфейсе, и в этом случае это темный угол реализации x86-64 ABI, связанной с обработкой структуры, которая содержит векторный массив точно размера 1!
ошибка присутствует в течение многих лет, но это первый раз, что кто-то ударил его, заметил его и сообщил об этом. Спасибо!