Вы должны думать из коробки.Не придерживайтесь типа данных и ширины в битах.
uint32_t
- это не что иное, как массив 4 uint8_t
, который вы можете легко распределить по vld4
на лету во время загрузки.
Таким образом, проблема становится намного более управляемой.
void foo(uint32_t *pDst, uint32_t *pSrc, uint32_t length)
{
length >>= 4;
int i;
uint8x16x4_t in, out;
uint8x16_t temp0, temp1, temp2;
for (i = 0; i < length; ++i)
{
in = vld4q_u8(pSrc);
pSrc += 16;
temp0 = in.val[1] << 4;
temp1 = in.val[3] << 4;
temp1 += in.val[1] >> 4;
out.val[0] = in.val[0] | temp0;
out.val[1] = in.val[2] | temp1;
out.val[2] = in.val[3] >> 4;
out.val[3] = vdupq_n_u8(0);
vst4q_u8(pDst, out);
pDst += 16;
}
}
Обратите внимание, что я исключил остаточную сделку, и она будет работать намного быстрее, если вы развернетесь глубже.
Что еще более важно, я бы написал эту функцию в ассемблере, не задумываясь, потому что я не думаю, что компилятор так умно управлял бы регистрами, что out.val[3]
инициализируется нулем только один раз вне цикла.
И я также сомневаюсь, что temp1 += in.val[1] >> 4;
будет переводиться в vsra
из-за природы инструкции нераздельного целевого операнда.Кто знает?
Компиляторы отстой.
Обновление: Хорошо, вот коды, которые будут соответствовать вашим потребностям, написанные на ассемблере, для обеих архитектур.
aarch32
vtrn.16 q0, q1
vtrn.16 q2, q3
vtrn.32 q0, q2
vtrn.32 q1, q3
vadd.u16 q0, q1, q0
vadd.u16 q2, q3, q2
adr r12, shift_table
vadd.u16 q0, q2, q0
vld1.64 {q3}, [r12]
vadd.u16 d0, d1, d0
vclz.u16 d0, d0 // d0 contains the leading zeros
vmovl.u16 q0, d0
vshl.u32 q1, q0, q3
vpadal.u32 d3, d2 // d3 contains the final result
.balign 8
shift_table:
.dc.b 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00 // 0, 12, 24, 4
aarch64
trn1 v16.8h, v0.8h, v1.8h
trn1 v18.8h, v2.8h, v3.8h
trn2 v17.8h, v0.8h, v1.8h
trn2 v19.8h, v2.8h, v3.8h
trn2 v0.4s, v18.4s, v16.4s
trn1 v1.4s, v18.4s, v16.4s
trn2 v2.4s, v19.4s, v17.4s
trn1 v3.4s, v19.4s, v17.4s
add v0.8h, v1.8h, v0.8h
add v2.8h, v3.8h, v2.8h
adr x16, shift_table
add v0.8h, v2.8h, v0.8h
ld1 {v3.2d}, [x16]
mov v1.d[0], v0.d[1]
add v0.4h, v1.4h, v0.4h
clz v0.4h, v0.4h // v0 contains the leading zeros
uxtl v0.4s, v0.4h
ushl v0.4s, v0.4s, v3.4s
mov v1.d[0], v0.d[1]
uadalp v1.1d, v0.2s // v1 contains the final result
.balign 8
shift_table:
.dc.b 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00 // 0, 12, 24, 4
** Возможно, вам придется изменить .dc.b
на .byte
в Clang