Conversion de SSE en néon: comment compacter puis extraire un résultat 32 bits

Je dois traduire les instructions suivantes de SSE à Neon

uint32_t a = _mm_cvtsi128_si32(_mm_shuffle_epi8(a,SHUFFLE_MASK) ); 

Où:

 static const __m128i SHUFFLE_MASK = _mm_setr_epi8(3, 7, 11, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1); 

Donc, fondamentalement, je dois prendre 4ème, 8ème, 12ème et 16ème octets du registre et le mettre dans un uint32_t . Cela ressemble à une instruction d’emballage (dans SSE, il me semble me souvenir que j’ai utilisé shuffle car cela enregistre une instruction par rapport à une opération d’emballage. Cet exemple montre l’utilisation des instructions d’emballage).

Comment cette opération se traduit-elle en néon?
Devrais-je utiliser les instructions d’emballage?
Comment puis-je extraire 32bits? (Y a-t-il quelque chose d’équivalent à _mm_cvtsi128_si32 ?)

Modifier:
Pour commencer, vgetq_lane_u32 devrait permettre de remplacer _mm_cvtsi128_si32 (mais je devrai convertir mon uint8x16_t en uint32x4_t)

 uint32_t vgetq_lane_u32(uint32x4_t vec, __constrange(0,3) int lane); 

ou directement stocker la voie vst1q_lane_u32

 void vst1q_lane_u32(__transfersize(1) uint32_t * ptr, uint32x4_t val, __constrange(0,3) int lane); // VST1.32 {d0[0]}, [r0] 

J’ai trouvé cet excellent guide . Je travaille là-dessus, il semble que mon opération puisse être effectuée avec une instruction VTBL (table de consultation), mais je vais l’implémenter avec 2 opérations de désentrelacement car, pour le moment, cela semble plus simple.

 uint8x8x2_t vuzp_u8(uint8x8_t a, uint8x8_t b); 

Donc, quelque chose comme:

 uint8x16_t a; uint8_t* out; [...] //a = 138 0 0 0 140 0 0 0 146 0 0 0 147 0 0 0 a = vuzp_u8(vget_low_u8(a), vget_high_u8(a) ); //a = 138 0 140 0 146 0 147 0 0 0 0 0 0 0 0 0 a = vuzp_u8(vget_low_u8(a), vget_high_u8(a) ); //a = 138 140 146 147 0 0 0 0 0 0 0 0 0 0 0 0 vst1q_lane_u32(out,a,0); 

Le dernier ne donne pas d’avertissement en utilisant __atsortingbute__((optimize("lax-vector-conversions")))

Mais, à cause de la conversion des données, les 2 assignations ne sont pas possibles . Une solution de contournement est comme ceci ( Edit: Ceci enfreint les règles de crénelage ssortingctes! Le compilateur peut supposer que a ne change pas lors de l’atsortingbution de l’adresse de d .):

 uint8x8x2_t* d = (uint8x8x2_t*) &a; *d = vuzp_u8(vget_low_u8(a), vget_high_u8(a) ); *d = vuzp_u8(vget_low_u8(a), vget_high_u8(a) ); vst1q_lane_u32(out,a,0); 

J’ai implémenté une solution de contournement plus générale via un type de données flexible:

 NeonVectorType a; //a can be used as a uint8x16_t, uint8x8x2_t, uint32x4_t, etc. a = vuzp_u8(vget_low_u8(a), vget_high_u8(a) ); a = vuzp_u8(vget_low_u8(a), vget_high_u8(a) ); vst1q_lane_u32(out,a,0); 

Modifier:

Voici la version avec masque de shuffle / table de correspondance. Cela rend en effet ma boucle intérieure un peu plus rapide. Encore une fois, j’ai utilisé le type de données décrit ici .

 static const uint8x8_t MASK = {0x00,0x04,0x08,0x0C,0xff,0xff,0xff,0xff}; NeonVectorType a; //a can be used as a uint8x16_t, uint8x8x2_t, uint32x4_t, etc. NeonVectorType res; //res can be used as uint8x8_t, uint32x2_t, etc. [...] res = vtbl2_u8(a, MASK); vst1_lane_u32(out,res,0); 

Je l’écrirais ainsi:

 uint32_t extract (uint8x16_t x) { uint8x8x2_t a = vuzp_u8 (vget_low_u8 (x), vget_high_u8 (x)); uint8x8x2_t b = vuzp_u8 (a.val[0], a.val[1]); return vget_lane_u32 (vreinterpret_u32_u8 (b.val[0]), 0); } 

Qui sur une version récente de GCC se comstack pour:

 extract: vuzp.8 d0, d1 vuzp.8 d0, d1 vmov.32 r0, d0[0] bx lr