Search code examples
embeddedarmneonintrinsicscortex-a8

Efficient algorithm to convert(sum) 128-bit data in q-register to 16-bit data


I have 128-bit data in q-register. I want to sum the individual 16-bit block in this q-register to finally have a 16-bit final sum (any carry beyond 16-bit should be taken and added to the LSB of this 16-bit num).

what I want to achieve is:

VADD.U16 (some 16-bit variable) {q0[0] q0[1] q0[2] ......... q0[7]}

but using intrinsics,

would appreciate if someone could give me an algorithm for this.

I tried using pair-wise addition, but I'm ending up with rather a clumsy solution..

Heres how it looks:

int convert128to16(uint16x8_t data128){

    uint16_t data16 = 0;
    uint16x4_t ddata;
    print16(data128);

    uint32x4_t data = vpaddlq_u16(data128);
    print32(data);

    uint16x4_t data_hi = vget_high_u16(data);
    print16x4(data_hi);

    uint16x4_t data_low = vget_low_u16(data);
    print16x4(data_low);

    ddata = vpadd_u16( data_hi, data_low);
    print16x4(ddata);

}

It's still incomplete and a bit clumsy.. Any help would be much appreciated.


Solution

  • You can use the horizontal add instructions:

    Here is a fragment:

      uint16x8_t input = /* load your data128 here */
    
      uint64x2_t temp   = vpaddlq_u32 (vpaddlq_u16 (input)); 
    
      uint64x1_t result = vadd_u64 (vget_high_u64 (temp), 
                                    vget_low_u64  (temp));
    
    
      // result now contains the sum of all 16 bit unsigned words
      // stored in data128. 
    
      // to add the values that overflow from 16 bit just do another 16 bit
      // horizontal addition and return the lowest 16 bit as the final result:
    
     uint16x4_t w = vpadd_u16 (
         vreinterpret_u16_u64 (result),                              
         vreinterpret_u16_u64 (result));
    
     uint16_t wrappedResult = vget_lane_u16 (w, 0);