Add ARM-NEON support to VectorMath in WebAudio
[WebKit-https.git] / Source / WebCore / platform / audio / VectorMath.cpp
1 /*
2  * Copyright (C) 2010, Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1.  Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2.  Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16  * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
17  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23  */
24
25 #include "config.h"
26
27 #if ENABLE(WEB_AUDIO)
28
29 #include "VectorMath.h"
30
31 #if OS(DARWIN)
32 #include <Accelerate/Accelerate.h>
33 #endif
34
35 #ifdef __SSE2__
36 #include <emmintrin.h>
37 #endif
38
39 #if HAVE(ARM_NEON_INTRINSICS)
40 #include <arm_neon.h>
41 #endif
42
43 #include <algorithm>
44 #include <math.h>
45
46 namespace WebCore {
47
48 namespace VectorMath {
49
50 #if OS(DARWIN)
51 // On the Mac we use the highly optimized versions in Accelerate.framework
52 // In 32-bit mode (__ppc__ or __i386__) <Accelerate/Accelerate.h> includes <vecLib/vDSP_translate.h> which defines macros of the same name as
53 // our namespaced function names, so we must handle this case differently. Other architectures (64bit, ARM, etc.) do not include this header file.
54
55 void vsmul(const float* sourceP, int sourceStride, const float* scale, float* destP, int destStride, size_t framesToProcess)
56 {
57 #if defined(__ppc__) || defined(__i386__)
58     ::vsmul(sourceP, sourceStride, scale, destP, destStride, framesToProcess);
59 #else
60     vDSP_vsmul(sourceP, sourceStride, scale, destP, destStride, framesToProcess);
61 #endif
62 }
63
64 void vadd(const float* source1P, int sourceStride1, const float* source2P, int sourceStride2, float* destP, int destStride, size_t framesToProcess)
65 {
66 #if defined(__ppc__) || defined(__i386__)
67     ::vadd(source1P, sourceStride1, source2P, sourceStride2, destP, destStride, framesToProcess);
68 #else
69     vDSP_vadd(source1P, sourceStride1, source2P, sourceStride2, destP, destStride, framesToProcess);
70 #endif
71 }
72
73 void vmul(const float* source1P, int sourceStride1, const float* source2P, int sourceStride2, float* destP, int destStride, size_t framesToProcess)
74 {
75 #if defined(__ppc__) || defined(__i386__)
76     ::vmul(source1P, sourceStride1, source2P, sourceStride2, destP, destStride, framesToProcess);
77 #else
78     vDSP_vmul(source1P, sourceStride1, source2P, sourceStride2, destP, destStride, framesToProcess);
79 #endif
80 }
81
82 void zvmul(const float* real1P, const float* imag1P, const float* real2P, const float* imag2P, float* realDestP, float* imagDestP, size_t framesToProcess)
83 {
84     DSPSplitComplex sc1;
85     DSPSplitComplex sc2;
86     DSPSplitComplex dest;
87     sc1.realp = const_cast<float*>(real1P);
88     sc1.imagp = const_cast<float*>(imag1P);
89     sc2.realp = const_cast<float*>(real2P);
90     sc2.imagp = const_cast<float*>(imag2P);
91     dest.realp = realDestP;
92     dest.imagp = imagDestP;
93 #if defined(__ppc__) || defined(__i386__)
94     ::zvmul(&sc1, 1, &sc2, 1, &dest, 1, framesToProcess, 1);
95 #else
96     vDSP_zvmul(&sc1, 1, &sc2, 1, &dest, 1, framesToProcess, 1);
97 #endif
98 }
99
100 void vsma(const float* sourceP, int sourceStride, const float* scale, float* destP, int destStride, size_t framesToProcess)
101 {
102     vDSP_vsma(sourceP, sourceStride, scale, destP, destStride, destP, destStride, framesToProcess);
103 }
104
105 void vmaxmgv(const float* sourceP, int sourceStride, float* maxP, size_t framesToProcess)
106 {
107     vDSP_maxmgv(sourceP, sourceStride, maxP, framesToProcess);
108 }
109
110 void vsvesq(const float* sourceP, int sourceStride, float* sumP, size_t framesToProcess)
111 {
112     vDSP_svesq(const_cast<float*>(sourceP), sourceStride, sumP, framesToProcess);
113 }
114 #else
115
116 void vsma(const float* sourceP, int sourceStride, const float* scale, float* destP, int destStride, size_t framesToProcess)
117 {
118     int n = framesToProcess;
119
120 #ifdef __SSE2__
121     if ((sourceStride == 1) && (destStride == 1)) {
122         float k = *scale;
123
124         // If the sourceP address is not 16-byte aligned, the first several frames (at most three) should be processed separately.
125         while ((reinterpret_cast<uintptr_t>(sourceP) & 0x0F) && n) {
126             *destP += k * *sourceP;
127             sourceP++;
128             destP++;
129             n--;
130         }
131
132         // Now the sourceP is aligned, use SSE.
133         int tailFrames = n % 4;
134         const float* endP = destP + n - tailFrames;
135
136         __m128 pSource;
137         __m128 dest;
138         __m128 temp;
139         __m128 mScale = _mm_set_ps1(k);
140
141         bool destAligned = !(reinterpret_cast<uintptr_t>(destP) & 0x0F);
142
143 #define SSE2_MULT_ADD(loadInstr, storeInstr)        \
144             while (destP < endP)                    \
145             {                                       \
146                 pSource = _mm_load_ps(sourceP);     \
147                 temp = _mm_mul_ps(pSource, mScale); \
148                 dest = _mm_##loadInstr##_ps(destP); \
149                 dest = _mm_add_ps(dest, temp);      \
150                 _mm_##storeInstr##_ps(destP, dest); \
151                 sourceP += 4;                       \
152                 destP += 4;                         \
153             }
154
155         if (destAligned) 
156             SSE2_MULT_ADD(load, store)
157         else 
158             SSE2_MULT_ADD(loadu, storeu)
159
160         n = tailFrames;
161     }
162 #elif HAVE(ARM_NEON_INTRINSICS)
163     if ((sourceStride == 1) && (destStride == 1)) {
164         int tailFrames = n % 4;
165         const float* endP = destP + n - tailFrames;
166
167         float32x4_t k = vdupq_n_f32(*scale);
168         while (destP < endP) {
169             float32x4_t source = vld1q_f32(sourceP);
170             float32x4_t dest = vld1q_f32(destP);
171
172             dest = vmlaq_f32(dest, source, k);
173             vst1q_f32(destP, dest);
174
175             sourceP += 4;
176             destP += 4;
177         }
178         n = tailFrames;
179     }
180 #endif
181     while (n) {
182         *destP += *sourceP * *scale;
183         sourceP += sourceStride;
184         destP += destStride;
185         n--;
186     }
187 }
188
189 void vsmul(const float* sourceP, int sourceStride, const float* scale, float* destP, int destStride, size_t framesToProcess)
190 {
191     int n = framesToProcess;
192
193 #ifdef __SSE2__
194     if ((sourceStride == 1) && (destStride == 1)) {
195         float k = *scale;
196
197         // If the sourceP address is not 16-byte aligned, the first several frames (at most three) should be processed separately.
198         while ((reinterpret_cast<size_t>(sourceP) & 0x0F) && n) {
199             *destP = k * *sourceP;
200             sourceP++;
201             destP++;
202             n--;
203         }
204
205         // Now the sourceP address is aligned and start to apply SSE.
206         int group = n / 4;
207         __m128 mScale = _mm_set_ps1(k);
208         __m128* pSource;
209         __m128* pDest;
210         __m128 dest;
211
212
213         if (reinterpret_cast<size_t>(destP) & 0x0F) {
214             while (group--) {
215                 pSource = reinterpret_cast<__m128*>(const_cast<float*>(sourceP));
216                 dest = _mm_mul_ps(*pSource, mScale);
217                 _mm_storeu_ps(destP, dest);
218
219                 sourceP += 4;
220                 destP += 4;
221             }
222         } else {
223             while (group--) {
224                 pSource = reinterpret_cast<__m128*>(const_cast<float*>(sourceP));
225                 pDest = reinterpret_cast<__m128*>(destP);
226                 *pDest = _mm_mul_ps(*pSource, mScale);
227
228                 sourceP += 4;
229                 destP += 4;
230             }
231         }
232
233         // Non-SSE handling for remaining frames which is less than 4.
234         n %= 4;
235         while (n) {
236             *destP = k * *sourceP;
237             sourceP++;
238             destP++;
239             n--;
240         }
241     } else { // If strides are not 1, rollback to normal algorithm.
242 #elif HAVE(ARM_NEON_INTRINSICS)
243     if ((sourceStride == 1) && (destStride == 1)) {
244         float k = *scale;
245         int tailFrames = n % 4;
246         const float* endP = destP + n - tailFrames;
247
248         while (destP < endP) {
249             float32x4_t source = vld1q_f32(sourceP);
250             vst1q_f32(destP, vmulq_n_f32(source, k));
251
252             sourceP += 4;
253             destP += 4;
254         }
255         n = tailFrames;
256     }
257 #endif
258     float k = *scale;
259     while (n--) {
260         *destP = k * *sourceP;
261         sourceP += sourceStride;
262         destP += destStride;
263     }
264 #ifdef __SSE2__
265     }
266 #endif
267 }
268
269 void vadd(const float* source1P, int sourceStride1, const float* source2P, int sourceStride2, float* destP, int destStride, size_t framesToProcess)
270 {
271     int n = framesToProcess;
272
273 #ifdef __SSE2__
274     if ((sourceStride1 ==1) && (sourceStride2 == 1) && (destStride == 1)) {
275         // If the sourceP address is not 16-byte aligned, the first several frames (at most three) should be processed separately.
276         while ((reinterpret_cast<size_t>(source1P) & 0x0F) && n) {
277             *destP = *source1P + *source2P;
278             source1P++;
279             source2P++;
280             destP++;
281             n--;
282         }
283
284         // Now the source1P address is aligned and start to apply SSE.
285         int group = n / 4;
286         __m128* pSource1;
287         __m128* pSource2;
288         __m128* pDest;
289         __m128 source2;
290         __m128 dest;
291
292         bool source2Aligned = !(reinterpret_cast<size_t>(source2P) & 0x0F);
293         bool destAligned = !(reinterpret_cast<size_t>(destP) & 0x0F);
294
295         if (source2Aligned && destAligned) { // all aligned
296             while (group--) {
297                 pSource1 = reinterpret_cast<__m128*>(const_cast<float*>(source1P));
298                 pSource2 = reinterpret_cast<__m128*>(const_cast<float*>(source2P));
299                 pDest = reinterpret_cast<__m128*>(destP);
300                 *pDest = _mm_add_ps(*pSource1, *pSource2);
301
302                 source1P += 4;
303                 source2P += 4;
304                 destP += 4;
305             }
306
307         } else if (source2Aligned && !destAligned) { // source2 aligned but dest not aligned 
308             while (group--) {
309                 pSource1 = reinterpret_cast<__m128*>(const_cast<float*>(source1P));
310                 pSource2 = reinterpret_cast<__m128*>(const_cast<float*>(source2P));
311                 dest = _mm_add_ps(*pSource1, *pSource2);
312                 _mm_storeu_ps(destP, dest);
313
314                 source1P += 4;
315                 source2P += 4;
316                 destP += 4;
317             }
318
319         } else if (!source2Aligned && destAligned) { // source2 not aligned but dest aligned 
320             while (group--) {
321                 pSource1 = reinterpret_cast<__m128*>(const_cast<float*>(source1P));
322                 source2 = _mm_loadu_ps(source2P);
323                 pDest = reinterpret_cast<__m128*>(destP);
324                 *pDest = _mm_add_ps(*pSource1, source2);
325
326                 source1P += 4;
327                 source2P += 4;
328                 destP += 4;
329             }
330         } else if (!source2Aligned && !destAligned) { // both source2 and dest not aligned 
331             while (group--) {
332                 pSource1 = reinterpret_cast<__m128*>(const_cast<float*>(source1P));
333                 source2 = _mm_loadu_ps(source2P);
334                 dest = _mm_add_ps(*pSource1, source2);
335                 _mm_storeu_ps(destP, dest);
336
337                 source1P += 4;
338                 source2P += 4;
339                 destP += 4;
340             }
341         }
342
343         // Non-SSE handling for remaining frames which is less than 4.
344         n %= 4;
345         while (n) {
346             *destP = *source1P + *source2P;
347             source1P++;
348             source2P++;
349             destP++;
350             n--;
351         }
352     } else { // if strides are not 1, rollback to normal algorithm
353 #elif HAVE(ARM_NEON_INTRINSICS)
354     if ((sourceStride1 ==1) && (sourceStride2 == 1) && (destStride == 1)) {
355         int tailFrames = n % 4;
356         const float* endP = destP + n - tailFrames;
357
358         while (destP < endP) {
359             float32x4_t source1 = vld1q_f32(source1P);
360             float32x4_t source2 = vld1q_f32(source2P);
361             vst1q_f32(destP, vaddq_f32(source1, source2));
362
363             source1P += 4;
364             source2P += 4;
365             destP += 4;
366         }
367         n = tailFrames;
368     }
369 #endif
370     while (n--) {
371         *destP = *source1P + *source2P;
372         source1P += sourceStride1;
373         source2P += sourceStride2;
374         destP += destStride;
375     }
376 #ifdef __SSE2__
377     }
378 #endif
379 }
380
381 void vmul(const float* source1P, int sourceStride1, const float* source2P, int sourceStride2, float* destP, int destStride, size_t framesToProcess)
382 {
383
384     int n = framesToProcess;
385
386 #ifdef __SSE2__
387     if ((sourceStride1 == 1) && (sourceStride2 == 1) && (destStride == 1)) {
388         // If the source1P address is not 16-byte aligned, the first several frames (at most three) should be processed separately.
389         while ((reinterpret_cast<uintptr_t>(source1P) & 0x0F) && n) {
390             *destP = *source1P * *source2P;
391             source1P++;
392             source2P++;
393             destP++;
394             n--;
395         }
396
397         // Now the source1P address aligned and start to apply SSE.
398         int tailFrames = n % 4;
399         const float* endP = destP + n - tailFrames;
400         __m128 pSource1;
401         __m128 pSource2;
402         __m128 dest;
403
404         bool source2Aligned = !(reinterpret_cast<uintptr_t>(source2P) & 0x0F);
405         bool destAligned = !(reinterpret_cast<uintptr_t>(destP) & 0x0F);
406
407 #define SSE2_MULT(loadInstr, storeInstr)                   \
408             while (destP < endP)                           \
409             {                                              \
410                 pSource1 = _mm_load_ps(source1P);          \
411                 pSource2 = _mm_##loadInstr##_ps(source2P); \
412                 dest = _mm_mul_ps(pSource1, pSource2);     \
413                 _mm_##storeInstr##_ps(destP, dest);        \
414                 source1P += 4;                             \
415                 source2P += 4;                             \
416                 destP += 4;                                \
417             }
418
419         if (source2Aligned && destAligned) // Both aligned.
420             SSE2_MULT(load, store)
421         else if (source2Aligned && !destAligned) // Source2 is aligned but dest not.
422             SSE2_MULT(load, storeu)
423         else if (!source2Aligned && destAligned) // Dest is aligned but source2 not.
424             SSE2_MULT(loadu, store)
425         else // Neither aligned.
426             SSE2_MULT(loadu, storeu)
427
428         n = tailFrames;
429     }
430 #elif HAVE(ARM_NEON_INTRINSICS)
431     if ((sourceStride1 ==1) && (sourceStride2 == 1) && (destStride == 1)) {
432         int tailFrames = n % 4;
433         const float* endP = destP + n - tailFrames;
434
435         while (destP < endP) {
436             float32x4_t source1 = vld1q_f32(source1P);
437             float32x4_t source2 = vld1q_f32(source2P);
438             vst1q_f32(destP, vmulq_f32(source1, source2));
439
440             source1P += 4;
441             source2P += 4;
442             destP += 4;
443         }
444         n = tailFrames;
445     }
446 #endif
447     while (n) {
448         *destP = *source1P * *source2P;
449         source1P += sourceStride1;
450         source2P += sourceStride2;
451         destP += destStride;
452         n--;
453     }
454 }
455
456 void zvmul(const float* real1P, const float* imag1P, const float* real2P, const float* imag2P, float* realDestP, float* imagDestP, size_t framesToProcess)
457 {
458     unsigned i = 0;
459 #ifdef __SSE2__
460     // Only use the SSE optimization in the very common case that all addresses are 16-byte aligned. 
461     // Otherwise, fall through to the scalar code below.
462     if (!(reinterpret_cast<uintptr_t>(real1P) & 0x0F)
463         && !(reinterpret_cast<uintptr_t>(imag1P) & 0x0F)
464         && !(reinterpret_cast<uintptr_t>(real2P) & 0x0F)
465         && !(reinterpret_cast<uintptr_t>(imag2P) & 0x0F)
466         && !(reinterpret_cast<uintptr_t>(realDestP) & 0x0F)
467         && !(reinterpret_cast<uintptr_t>(imagDestP) & 0x0F)) {
468         
469         unsigned endSize = framesToProcess - framesToProcess % 4;
470         while (i < endSize) {
471             __m128 real1 = _mm_load_ps(real1P + i);
472             __m128 real2 = _mm_load_ps(real2P + i);
473             __m128 imag1 = _mm_load_ps(imag1P + i);
474             __m128 imag2 = _mm_load_ps(imag2P + i);
475             __m128 real = _mm_mul_ps(real1, real2);
476             real = _mm_sub_ps(real, _mm_mul_ps(imag1, imag2));
477             __m128 imag = _mm_mul_ps(real1, imag2);
478             imag = _mm_add_ps(imag, _mm_mul_ps(imag1, real2));
479             _mm_store_ps(realDestP + i, real);
480             _mm_store_ps(imagDestP + i, imag);
481             i += 4;
482         }
483     }
484 #elif HAVE(ARM_NEON_INTRINSICS)
485         unsigned endSize = framesToProcess - framesToProcess % 4;
486         while (i < endSize) {
487             float32x4_t real1 = vld1q_f32(real1P + i);
488             float32x4_t real2 = vld1q_f32(real2P + i);
489             float32x4_t imag1 = vld1q_f32(imag1P + i);
490             float32x4_t imag2 = vld1q_f32(imag2P + i);
491
492             float32x4_t realResult = vmlsq_f32(vmulq_f32(real1, real2), imag1, imag2);
493             float32x4_t imagResult = vmlaq_f32(vmulq_f32(real1, imag2), imag1, real2);
494
495             vst1q_f32(realDestP + i, realResult);
496             vst1q_f32(imagDestP + i, imagResult);
497
498             i += 4;
499         }
500 #endif
501     for (; i < framesToProcess; ++i) {
502         // Read and compute result before storing them, in case the
503         // destination is the same as one of the sources.
504         float realResult = real1P[i] * real2P[i] - imag1P[i] * imag2P[i];
505         float imagResult = real1P[i] * imag2P[i] + imag1P[i] * real2P[i];
506
507         realDestP[i] = realResult;
508         imagDestP[i] = imagResult;
509     }
510 }
511
512 void vsvesq(const float* sourceP, int sourceStride, float* sumP, size_t framesToProcess)
513 {
514     int n = framesToProcess;
515     float sum = 0;
516
517 #ifdef __SSE2__ 
518     if (sourceStride == 1) { 
519         // If the sourceP address is not 16-byte aligned, the first several frames (at most three) should be processed separately. 
520         while ((reinterpret_cast<uintptr_t>(sourceP) & 0x0F) && n) { 
521             float sample = *sourceP; 
522             sum += sample * sample; 
523             sourceP++; 
524             n--; 
525         } 
526  
527         // Now the sourceP is aligned, use SSE.
528         int tailFrames = n % 4; 
529         const float* endP = sourceP + n - tailFrames; 
530         __m128 source; 
531         __m128 mSum = _mm_setzero_ps(); 
532  
533         while (sourceP < endP) { 
534             source = _mm_load_ps(sourceP); 
535             source = _mm_mul_ps(source, source); 
536             mSum = _mm_add_ps(mSum, source); 
537             sourceP += 4; 
538         } 
539  
540         // Summarize the SSE results. 
541         const float* groupSumP = reinterpret_cast<float*>(&mSum); 
542         sum += groupSumP[0] + groupSumP[1] + groupSumP[2] + groupSumP[3]; 
543  
544         n = tailFrames; 
545     } 
546 #elif HAVE(ARM_NEON_INTRINSICS)
547     if (sourceStride == 1) {
548         int tailFrames = n % 4;
549         const float* endP = sourceP + n - tailFrames;
550
551         float32x4_t fourSum = vdupq_n_f32(0);
552         while (sourceP < endP) {
553             float32x4_t source = vld1q_f32(sourceP);
554             fourSum = vmlaq_f32(fourSum, source, source);
555             sourceP += 4;
556         }
557         float32x2_t twoSum = vadd_f32(vget_low_f32(fourSum), vget_high_f32(fourSum));
558
559         float groupSum[2];
560         vst1_f32(groupSum, twoSum);
561         sum += groupSum[0] + groupSum[1];
562
563         n = tailFrames;
564     }
565 #endif
566
567     while (n--) {
568         float sample = *sourceP;
569         sum += sample * sample;
570         sourceP += sourceStride;
571     }
572
573     ASSERT(sumP);
574     *sumP = sum;
575 }
576
577 void vmaxmgv(const float* sourceP, int sourceStride, float* maxP, size_t framesToProcess)
578 {
579     int n = framesToProcess;
580     float max = 0;
581
582 #ifdef __SSE2__
583     if (sourceStride == 1) {
584         // If the sourceP address is not 16-byte aligned, the first several frames (at most three) should be processed separately.
585         while ((reinterpret_cast<uintptr_t>(sourceP) & 0x0F) && n) {
586             max = std::max(max, fabsf(*sourceP));
587             sourceP++;
588             n--;
589         }
590
591         // Now the sourceP is aligned, use SSE.
592         int tailFrames = n % 4;
593         const float* endP = sourceP + n - tailFrames;
594         __m128 source;
595         __m128 mMax = _mm_setzero_ps();
596         int mask = 0x7FFFFFFF;
597         __m128 mMask = _mm_set1_ps(*reinterpret_cast<float*>(&mask));
598
599         while (sourceP < endP) {
600             source = _mm_load_ps(sourceP);
601             // Calculate the absolute value by anding source with mask, the sign bit is set to 0.
602             source = _mm_and_ps(source, mMask);
603             mMax = _mm_max_ps(mMax, source);
604             sourceP += 4;
605         }
606
607         // Get max from the SSE results.
608         const float* groupMaxP = reinterpret_cast<float*>(&mMax);
609         max = std::max(max, groupMaxP[0]);
610         max = std::max(max, groupMaxP[1]);
611         max = std::max(max, groupMaxP[2]);
612         max = std::max(max, groupMaxP[3]);
613
614         n = tailFrames;
615     }
616 #elif HAVE(ARM_NEON_INTRINSICS)
617     if (sourceStride == 1) {
618         int tailFrames = n % 4;
619         const float* endP = sourceP + n - tailFrames;
620
621         float32x4_t fourMax = vdupq_n_f32(0);
622         while (sourceP < endP) {
623             float32x4_t source = vld1q_f32(sourceP);
624             fourMax = vmaxq_f32(fourMax, vabsq_f32(source));
625             sourceP += 4;
626         }
627         float32x2_t twoMax = vmax_f32(vget_low_f32(fourMax), vget_high_f32(fourMax));
628
629         float groupMax[2];
630         vst1_f32(groupMax, twoMax);
631         max = std::max(groupMax[0], groupMax[1]);
632
633         n = tailFrames;
634     }
635 #endif
636
637     while (n--) {
638         max = std::max(max, fabsf(*sourceP));
639         sourceP += sourceStride;
640     }
641
642     ASSERT(maxP);
643     *maxP = max;
644 }
645 #endif // OS(DARWIN)
646
647 } // namespace VectorMath
648
649 } // namespace WebCore
650
651 #endif // ENABLE(WEB_AUDIO)