Hatalı görünen bir hatayla karşılaşıyorum, clang 3.4, 3.5 ve 3.6 trunk ile yanlış kod oluşturma. Aslında sorunu tetikleyen kaynak oldukça karmaşıktır, ama bu kendi kendine yeten örnekle azaltmak mümkün oldum:Bu yanlış kod üretimi, bir clang hatası olan __m256 değer dizileriyle mi?
#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;
}
yukarıda bir özeti: Ne var simd_pack
denilen basit bir tip birini içeren üye, bir __m256i
değeri. Uygulamamda, bu türleri alan işleçler ve işlevler var, ancak sorun yukarıdaki örnekle açıklanabilir. Spesifik olarak, test_broken()
in1
dizisinden okumalı ve daha sonra değerini out
dizisine kopyalamalıdır. Bu nedenle, main()
numaralı telefona memcmp()
numaralı çağrıya sıfır değeri döndürülmelidir. Aşağıdaki kullanarak yukarıdaki derleme:
clang++-3.6 bug_test.cc -o bug_test -mavx -O3
O optimizasyon seviyeleri -O0
ve -O1
bulduğunuz, seviyeler -O2
ve -O3
üzerine, Test başarısız olurken testi, geçer. Aynı dosyayı gcc 4.4, 4.6, 4.7 ve 4.8, ayrıca Intel C++ 13.0 ile derlemeyi denedim ve test tüm optimizasyon seviyelerinden geçiyor. Bu tür bir
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)
:
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 $0xffffffffffffffe0,%rsp
400a4b: 48 83 ec 40 sub $0x40,%rsp
400a4f: 48 83 fa 20 cmp $0x20,%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 $0x3f,%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)
Ben vurgu anahtar rol çoğaltmak gerekir: oluşturulan koda daha yakından bakmak alarak
, burada optimizasyon seviyesine -O3
oluşturulan montaj var kafa kaşıma. Önceden istediğim hizalanmamış hareketi kullanarak ymm0
'a 256 bit yükler, sonra xmm0
(yalnızca okunan verinin alt 128 bitini içerir) yığına depolar, ardından yığın konumundan hemen sonra ymm0
'a 256 bit okur. Bu sadece yazılmıştır. Efekt, ymm0
'un üst 128 biti (çıktı tamponuna yazılır), testin başarısız olmasına neden olan çöptür.
Sadece derleyici bir hatadan başka, bunun neden olabileceğinin iyi bir nedeni var mı? simd_pack
türünde bir tutmayı __m256i
değerler dizisine sahip olarak alarak bazı kuralları ihlal ediyor muyum? Kesinlikle bununla ilgili gibi görünüyor; _val
'u bir dizi yerine tek bir değer olarak değiştirirsem, oluşturulan kod tasarlandığı gibi çalışır. Ancak, benim uygulama bir dizi olmak için _val
gerektirir (uzunluğu bir C++ şablon parametresine bağlıdır).
Herhangi bir fikrin var mı?
görünüyor. Kod tamamen 256-bit olduğunda 'xmm' kayıtlarını kullanması için bir neden yoktur. (callee-save registerları bir kenara bırakarak) – Mysticial
Yolculuğa karışan katı bir taklit olmadıkça.Fakat her modern derleyici, SIMD kayıtlarını toplu olarak ele alır, bu yüzden bu uygulama yapılmamalıdır. – Mysticial
Giriş için teşekkürler. Değeri için, clang'ı -fno-strict-aliasing ile çağırmayı denedim, ancak oluşturulan kodu etkilemez. –