OSDN Git Service

Merge "Add guards around neon code."
[android-x86/system-extras.git] / tests / memtest / bandwidth.h
1 /*
2  * Copyright (C) 2013 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #ifndef __BANDWIDTH_H__
18 #define __BANDWIDTH_H__
19
20 #include "memtest.h"
21
22 // Bandwidth Class definitions.
23 class BandwidthBenchmark {
24 public:
25     BandwidthBenchmark()
26         : _size(0),
27           _num_warm_loops(DEFAULT_NUM_WARM_LOOPS),
28           _num_loops(DEFAULT_NUM_LOOPS) {}
29     virtual ~BandwidthBenchmark() {}
30
31     bool run() {
32         if (_size == 0) {
33             return false;
34         }
35         if (!canRun()) {
36             return false;
37         }
38
39         bench(_num_warm_loops);
40
41         nsecs_t t = system_time();
42         bench(_num_loops);
43         t = system_time() - t;
44
45         _mb_per_sec = (_size*(_num_loops/_BYTES_PER_MB))/(t/_NUM_NS_PER_SEC);
46
47         return true;
48     }
49
50     bool canRun() { return !usesNeon() || isNeonSupported(); }
51
52     virtual bool setSize(size_t size) = 0;
53
54     virtual const char *getName() = 0;
55
56     virtual bool verify() = 0;
57
58     virtual bool usesNeon() { return false; }
59
60     bool isNeonSupported() {
61 #if defined(__ARM_NEON__)
62         return true;
63 #else
64         return false;
65 #endif
66     }
67
68     // Accessors/mutators.
69     double mb_per_sec() { return _mb_per_sec; }
70     size_t num_warm_loops() { return _num_warm_loops; }
71     size_t num_loops() { return _num_loops; }
72     size_t size() { return _size; }
73
74     void set_num_warm_loops(size_t num_warm_loops) {
75         _num_warm_loops = num_warm_loops;
76     }
77     void set_num_loops(size_t num_loops) { _num_loops = num_loops; }
78
79     // Static constants
80     static const unsigned int DEFAULT_NUM_WARM_LOOPS = 1000000;
81     static const unsigned int DEFAULT_NUM_LOOPS = 20000000;
82
83 protected:
84     virtual void bench(size_t num_loops) = 0;
85
86     double _mb_per_sec;
87     size_t _size;
88     size_t _num_warm_loops;
89     size_t _num_loops;
90
91 private:
92     // Static constants
93     static const double _NUM_NS_PER_SEC = 1000000000.0;
94     static const double _BYTES_PER_MB = 1024.0* 1024.0;
95 };
96
97 class CopyBandwidthBenchmark : public BandwidthBenchmark {
98 public:
99     CopyBandwidthBenchmark() : BandwidthBenchmark(), _src(NULL), _dst(NULL) { }
100
101     bool setSize(size_t size) {
102         if (_src) {
103            free(_src);
104         }
105         if (_dst) {
106             free(_dst);
107         }
108
109         if (size == 0) {
110             _size = DEFAULT_COPY_SIZE;
111         } else {
112             _size = size;
113         }
114
115         _src = reinterpret_cast<char*>(memalign(64, _size));
116         if (!_src) {
117             perror("Failed to allocate memory for test.");
118             return false;
119         }
120         _dst = reinterpret_cast<char*>(memalign(64, _size));
121         if (!_dst) {
122             perror("Failed to allocate memory for test.");
123             return false;
124         }
125
126         return true;
127     }
128     virtual ~CopyBandwidthBenchmark() {
129         if (_src) {
130             free(_src);
131             _src = NULL;
132         }
133         if (_dst) {
134             free(_dst);
135             _dst = NULL;
136         }
137     }
138
139     bool verify() {
140         memset(_src, 0x23, _size);
141         memset(_dst, 0, _size);
142         bench(1);
143         if (memcmp(_src, _dst, _size) != 0) {
144             printf("Strings failed to compare after one loop.\n");
145             return false;
146         }
147
148         memset(_src, 0x23, _size);
149         memset(_dst, 0, _size);
150         _num_loops = 2;
151         bench(2);
152         if (memcmp(_src, _dst, _size) != 0) {
153             printf("Strings failed to compare after two loops.\n");
154             return false;
155         }
156
157         return true;
158     }
159
160 protected:
161     char *_src;
162     char *_dst;
163
164     static const unsigned int DEFAULT_COPY_SIZE = 8000;
165 };
166
167 class CopyLdrdStrdBenchmark : public CopyBandwidthBenchmark {
168 public:
169     CopyLdrdStrdBenchmark() : CopyBandwidthBenchmark() { }
170     virtual ~CopyLdrdStrdBenchmark() {}
171
172     const char *getName() { return "ldrd/strd"; }
173
174 protected:
175     // Copy using ldrd/strd instructions.
176     void bench(size_t num_loops) {
177         asm volatile(
178             "stmfd sp!, {r0,r1,r2,r3,r4,r6,r7}\n"
179
180             "mov r0, %0\n"
181             "mov r1, %1\n"
182             "mov r2, %2\n"
183             "mov r3, %3\n"
184
185             "0:\n"
186             "mov r4, r2, lsr #6\n"
187
188             "1:\n"
189             "ldrd r6, r7, [r0]\n"
190             "strd r6, r7, [r1]\n"
191             "ldrd r6, r7, [r0, #8]\n"
192             "strd r6, r7, [r1, #8]\n"
193             "ldrd r6, r7, [r0, #16]\n"
194             "strd r6, r7, [r1, #16]\n"
195             "ldrd r6, r7, [r0, #24]\n"
196             "strd r6, r7, [r1, #24]\n"
197             "ldrd r6, r7, [r0, #32]\n"
198             "strd r6, r7, [r1, #32]\n"
199             "ldrd r6, r7, [r0, #40]\n"
200             "strd r6, r7, [r1, #40]\n"
201             "ldrd r6, r7, [r0, #48]\n"
202             "strd r6, r7, [r1, #48]\n"
203             "ldrd r6, r7, [r0, #56]\n"
204             "strd r6, r7, [r1, #56]\n"
205
206             "add  r0, r0, #64\n"
207             "add  r1, r1, #64\n"
208             "subs r4, r4, #1\n"
209             "bgt 1b\n"
210
211             "sub r0, r0, r2\n"
212             "sub r1, r1, r2\n"
213             "subs r3, r3, #1\n"
214             "bgt 0b\n"
215
216             "ldmfd sp!, {r0,r1,r2,r3,r4,r6,r7}\n"
217         :: "r" (_src), "r" (_dst), "r" (_size), "r" (num_loops) : "r0", "r1", "r2", "r3");
218     }
219 };
220
221 class CopyLdmiaStmiaBenchmark : public CopyBandwidthBenchmark {
222 public:
223     CopyLdmiaStmiaBenchmark() : CopyBandwidthBenchmark() { }
224     virtual ~CopyLdmiaStmiaBenchmark() {}
225
226     const char *getName() { return "ldmia/stmia"; }
227
228 protected:
229     // Copy using ldmia/stmia instructions.
230     void bench(size_t num_loops) {
231         asm volatile(
232             "stmfd sp!, {r0,r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11,r12}\n"
233
234             "mov r0, %0\n"
235             "mov r1, %1\n"
236             "mov r2, %2\n"
237             "mov r3, %3\n"
238
239             "0:\n"
240             "mov r4, r2, lsr #6\n"
241
242             "1:\n"
243             "ldmia r0!, {r5, r6, r7, r8, r9, r10, r11, r12}\n"
244             "stmia r1!, {r5, r6, r7, r8, r9, r10, r11, r12}\n"
245             "subs r4, r4, #1\n"
246             "ldmia r0!, {r5, r6, r7, r8, r9, r10, r11, r12}\n"
247             "stmia r1!, {r5, r6, r7, r8, r9, r10, r11, r12}\n"
248             "bgt 1b\n"
249
250             "sub r0, r0, r2\n"
251             "sub r1, r1, r2\n"
252             "subs r3, r3, #1\n"
253             "bgt 0b\n"
254
255             "ldmfd sp!, {r0,r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11,r12}\n"
256         :: "r" (_src), "r" (_dst), "r" (_size), "r" (num_loops) : "r0", "r1", "r2", "r3");
257     }
258 };
259
260 class CopyVldVstBenchmark : public CopyBandwidthBenchmark {
261 public:
262     CopyVldVstBenchmark() : CopyBandwidthBenchmark() { }
263     virtual ~CopyVldVstBenchmark() {}
264
265     const char *getName() { return "vld/vst"; }
266
267     bool usesNeon() { return true; }
268
269 protected:
270     // Copy using vld/vst instructions.
271     void bench(size_t num_loops) {
272 #if defined(__ARM_NEON__)
273         asm volatile(
274             "stmfd sp!, {r0,r1,r2,r3,r4}\n"
275
276             "mov r0, %0\n"
277             "mov r1, %1\n"
278             "mov r2, %2\n"
279             "mov r3, %3\n"
280
281             "0:\n"
282             "mov r4, r2, lsr #6\n"
283
284             "1:\n"
285             "vld1.8 {d0-d3}, [r0]!\n"
286             "vld1.8 {d4-d7}, [r0]!\n"
287             "subs r4, r4, #1\n"
288             "vst1.8 {d0-d3}, [r1:128]!\n"
289             "vst1.8 {d4-d7}, [r1:128]!\n"
290             "bgt 1b\n"
291
292             "sub r0, r0, r2\n"
293             "sub r1, r1, r2\n"
294             "subs r3, r3, #1\n"
295             "bgt 0b\n"
296
297             "ldmfd sp!, {r0,r1,r2,r3,r4}\n"
298         :: "r" (_src), "r" (_dst), "r" (_size), "r" (num_loops) : "r0", "r1", "r2", "r3");
299 #endif
300     }
301 };
302
303 class CopyVldmiaVstmiaBenchmark : public CopyBandwidthBenchmark {
304 public:
305     CopyVldmiaVstmiaBenchmark() : CopyBandwidthBenchmark() { }
306     virtual ~CopyVldmiaVstmiaBenchmark() {}
307
308     const char *getName() { return "vldmia/vstmia"; }
309
310     bool usesNeon() { return true; }
311
312 protected:
313     // Copy using vld/vst instructions.
314     void bench(size_t num_loops) {
315 #if defined(__ARM_NEON__)
316         asm volatile(
317             "stmfd sp!, {r0,r1,r2,r3,r4}\n"
318
319             "mov r0, %0\n"
320             "mov r1, %1\n"
321             "mov r2, %2\n"
322             "mov r3, %3\n"
323
324             "0:\n"
325             "mov r4, r2, lsr #6\n"
326
327             "1:\n"
328             "vldmia r0!, {d0-d7}\n"
329             "subs r4, r4, #1\n"
330             "vstmia r1!, {d0-d7}\n"
331             "bgt 1b\n"
332
333             "sub r0, r0, r2\n"
334             "sub r1, r1, r2\n"
335             "subs r3, r3, #1\n"
336             "bgt 0b\n"
337
338             "ldmfd sp!, {r0,r1,r2,r3,r4}\n"
339         :: "r" (_src), "r" (_dst), "r" (_size), "r" (num_loops) : "r0", "r1", "r2", "r3");
340 #endif
341     }
342 };
343
344 class MemcpyBenchmark : public CopyBandwidthBenchmark {
345 public:
346     MemcpyBenchmark() : CopyBandwidthBenchmark() { }
347     virtual ~MemcpyBenchmark() {}
348
349     const char *getName() { return "memcpy"; }
350
351 protected:
352     void bench(size_t num_loops) {
353         for (size_t i = 0; i < num_loops; i++) {
354             memcpy(_dst, _src, _size);
355         }
356     }
357 };
358
359 class SingleBufferBandwidthBenchmark : public BandwidthBenchmark {
360 public:
361     SingleBufferBandwidthBenchmark() : BandwidthBenchmark(), _buffer(NULL) { }
362     virtual ~SingleBufferBandwidthBenchmark() {
363         if (_buffer) {
364             free(_buffer);
365             _buffer = NULL;
366         }
367     }
368
369     bool setSize(size_t size) {
370         if (_buffer) {
371             free(_buffer);
372             _buffer = NULL;
373         }
374
375         if (_size == 0) {
376             _size = DEFAULT_SINGLE_BUFFER_SIZE;
377         } else {
378             _size = size;
379         }
380
381         _buffer = reinterpret_cast<char*>(memalign(64, _size));
382         if (!_buffer) {
383             perror("Failed to allocate memory for test.");
384             return false;
385         }
386         memset(_buffer, 0, _size);
387
388         return true;
389     }
390
391     bool verify() { return true; }
392
393 protected:
394     char *_buffer;
395
396     static const unsigned int DEFAULT_SINGLE_BUFFER_SIZE = 16000;
397 };
398
399 class WriteBandwidthBenchmark : public SingleBufferBandwidthBenchmark {
400 public:
401     WriteBandwidthBenchmark() : SingleBufferBandwidthBenchmark() { }
402     virtual ~WriteBandwidthBenchmark() { }
403
404     bool verify() {
405         memset(_buffer, 0, _size);
406         bench(1);
407         for (size_t i = 0; i < _size; i++) {
408             if (_buffer[i] != 1) {
409                 printf("Strings failed to compare after one loop.\n");
410                 return false;
411             }
412         }
413
414         memset(_buffer, 0, _size);
415         bench(2);
416         for (size_t i = 0; i < _size; i++) {
417             if (_buffer[i] != 2) {
418                 printf("Strings failed to compare after two loops.\n");
419                 return false;
420             }
421         }
422
423         return true;
424     }
425 };
426
427 class WriteStrdBenchmark : public WriteBandwidthBenchmark {
428 public:
429     WriteStrdBenchmark() : WriteBandwidthBenchmark() { }
430     virtual ~WriteStrdBenchmark() {}
431
432     const char *getName() { return "strd"; }
433
434 protected:
435     // Write a given value using strd.
436     void bench(size_t num_loops) {
437         asm volatile(
438             "stmfd sp!, {r0,r1,r2,r3,r4,r5}\n"
439
440             "mov r0, %0\n"
441             "mov r1, %1\n"
442             "mov r2, %2\n"
443
444             "mov r4, #0\n"
445             "mov r5, #0\n"
446
447             "0:\n"
448             "mov r3, r1, lsr #5\n"
449
450             "add r4, r4, #0x01010101\n"
451             "mov r5, r4\n"
452
453             "1:\n"
454             "subs r3, r3, #1\n"
455             "strd r4, r5, [r0]\n"
456             "strd r4, r5, [r0, #8]\n"
457             "strd r4, r5, [r0, #16]\n"
458             "strd r4, r5, [r0, #24]\n"
459             "add  r0, r0, #32\n"
460             "bgt 1b\n"
461
462             "sub r0, r0, r1\n"
463             "subs r2, r2, #1\n"
464             "bgt 0b\n"
465
466             "ldmfd sp!, {r0,r1,r2,r3,r4,r5}\n"
467           :: "r" (_buffer), "r" (_size), "r" (num_loops) : "r0", "r1", "r2");
468     }
469 };
470
471 class WriteStmiaBenchmark : public WriteBandwidthBenchmark {
472 public:
473     WriteStmiaBenchmark() : WriteBandwidthBenchmark() { }
474     virtual ~WriteStmiaBenchmark() {}
475
476     const char *getName() { return "stmia"; }
477
478 protected:
479       // Write a given value using stmia.
480       void bench(size_t num_loops) {
481           asm volatile(
482               "stmfd sp!, {r0,r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11}\n"
483
484               "mov r0, %0\n"
485               "mov r1, %1\n"
486               "mov r2, %2\n"
487
488               "mov r4, #0\n"
489
490               "0:\n"
491               "mov r3, r1, lsr #5\n"
492
493               "add r4, r4, #0x01010101\n"
494               "mov r5, r4\n"
495               "mov r6, r4\n"
496               "mov r7, r4\n"
497               "mov r8, r4\n"
498               "mov r9, r4\n"
499               "mov r10, r4\n"
500               "mov r11, r4\n"
501
502               "1:\n"
503               "subs r3, r3, #1\n"
504               "stmia r0!, {r4, r5, r6, r7, r8, r9, r10, r11}\n"
505               "bgt 1b\n"
506
507               "sub r0, r0, r1\n"
508               "subs r2, r2, #1\n"
509               "bgt 0b\n"
510
511               "ldmfd sp!, {r0,r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11}\n"
512         :: "r" (_buffer), "r" (_size), "r" (num_loops) : "r0", "r1", "r2");
513     }
514 };
515
516 class WriteVstBenchmark : public WriteBandwidthBenchmark {
517 public:
518     WriteVstBenchmark() : WriteBandwidthBenchmark() { }
519     virtual ~WriteVstBenchmark() {}
520
521     const char *getName() { return "vst"; }
522
523     bool usesNeon() { return true; }
524
525 protected:
526     // Write a given value using vst.
527     void bench(size_t num_loops) {
528 #if defined(__ARM_NEON__)
529         asm volatile(
530             "stmfd sp!, {r0,r1,r2,r3,r4}\n"
531
532             "mov r0, %0\n"
533             "mov r1, %1\n"
534             "mov r2, %2\n"
535             "mov r4, #0\n"
536
537             "0:\n"
538             "mov r3, r1, lsr #5\n"
539
540             "add r4, r4, #1\n"
541             "vdup.8 d0, r4\n"
542             "vmov d1, d0\n"
543             "vmov d2, d0\n"
544             "vmov d3, d0\n"
545
546             "1:\n"
547             "subs r3, r3, #1\n"
548             "vst1.8 {d0-d3}, [r0:128]!\n"
549             "bgt 1b\n"
550
551             "sub r0, r0, r1\n"
552             "subs r2, r2, #1\n"
553             "bgt 0b\n"
554
555             "ldmfd sp!, {r0,r1,r2,r3,r4}\n"
556         :: "r" (_buffer), "r" (_size), "r" (num_loops) : "r0", "r1", "r2");
557 #endif
558     }
559 };
560
561 class WriteVstmiaBenchmark : public WriteBandwidthBenchmark {
562 public:
563     WriteVstmiaBenchmark() : WriteBandwidthBenchmark() { }
564     virtual ~WriteVstmiaBenchmark() {}
565
566     const char *getName() { return "vstmia"; }
567
568     bool usesNeon() { return true; }
569
570 protected:
571     // Write a given value using vstmia.
572     void bench(size_t num_loops) {
573 #if defined(__ARM_NEON__)
574         asm volatile(
575             "stmfd sp!, {r0,r1,r2,r3,r4}\n"
576
577             "mov r0, %0\n"
578             "mov r1, %1\n"
579             "mov r2, %2\n"
580             "mov r4, #0\n"
581
582             "0:\n"
583             "mov r3, r1, lsr #5\n"
584
585             "add r4, r4, #1\n"
586             "vdup.8 d0, r4\n"
587             "vmov d1, d0\n"
588             "vmov d2, d0\n"
589             "vmov d3, d0\n"
590
591             "1:\n"
592             "subs r3, r3, #1\n"
593             "vstmia r0!, {d0-d3}\n"
594             "bgt 1b\n"
595
596             "sub r0, r0, r1\n"
597             "subs r2, r2, #1\n"
598             "bgt 0b\n"
599
600             "ldmfd sp!, {r0,r1,r2,r3,r4}\n"
601         :: "r" (_buffer), "r" (_size), "r" (num_loops) : "r0", "r1", "r2");
602 #endif
603     }
604 };
605
606 class MemsetBenchmark : public WriteBandwidthBenchmark {
607 public:
608     MemsetBenchmark() : WriteBandwidthBenchmark() { }
609     virtual ~MemsetBenchmark() {}
610
611     const char *getName() { return "memset"; }
612
613 protected:
614     void bench(size_t num_loops) {
615         for (size_t i = 0; i < num_loops; i++) {
616             memset(_buffer, (i % 255) + 1, _size);
617         }
618     }
619 };
620
621 class ReadLdrdBenchmark : public SingleBufferBandwidthBenchmark {
622 public:
623     ReadLdrdBenchmark() : SingleBufferBandwidthBenchmark() { }
624     virtual ~ReadLdrdBenchmark() {}
625
626     const char *getName() { return "ldrd"; }
627
628 protected:
629     // Write a given value using strd.
630     void bench(size_t num_loops) {
631         asm volatile(
632             "stmfd sp!, {r0,r1,r2,r3,r4,r5}\n"
633
634             "mov r0, %0\n"
635             "mov r1, %1\n"
636             "mov r2, %2\n"
637
638             "0:\n"
639             "mov r3, r1, lsr #5\n"
640
641             "1:\n"
642             "subs r3, r3, #1\n"
643             "ldrd r4, r5, [r0]\n"
644             "ldrd r4, r5, [r0, #8]\n"
645             "ldrd r4, r5, [r0, #16]\n"
646             "ldrd r4, r5, [r0, #24]\n"
647             "add  r0, r0, #32\n"
648             "bgt 1b\n"
649
650             "sub r0, r0, r1\n"
651             "subs r2, r2, #1\n"
652             "bgt 0b\n"
653
654             "ldmfd sp!, {r0,r1,r2,r3,r4,r5}\n"
655           :: "r" (_buffer), "r" (_size), "r" (num_loops) : "r0", "r1", "r2");
656     }
657 };
658
659 class ReadLdmiaBenchmark : public SingleBufferBandwidthBenchmark {
660 public:
661     ReadLdmiaBenchmark() : SingleBufferBandwidthBenchmark() { }
662     virtual ~ReadLdmiaBenchmark() {}
663
664     const char *getName() { return "ldmia"; }
665
666 protected:
667       // Write a given value using stmia.
668       void bench(size_t num_loops) {
669           asm volatile(
670               "stmfd sp!, {r0,r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11}\n"
671
672               "mov r0, %0\n"
673               "mov r1, %1\n"
674               "mov r2, %2\n"
675
676               "0:\n"
677               "mov r3, r1, lsr #5\n"
678
679               "1:\n"
680               "subs r3, r3, #1\n"
681               "ldmia r0!, {r4, r5, r6, r7, r8, r9, r10, r11}\n"
682               "bgt 1b\n"
683
684               "sub r0, r0, r1\n"
685               "subs r2, r2, #1\n"
686               "bgt 0b\n"
687
688               "ldmfd sp!, {r0,r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11}\n"
689         :: "r" (_buffer), "r" (_size), "r" (num_loops) : "r0", "r1", "r2");
690     }
691 };
692
693 class ReadVldBenchmark : public SingleBufferBandwidthBenchmark {
694 public:
695     ReadVldBenchmark() : SingleBufferBandwidthBenchmark() { }
696     virtual ~ReadVldBenchmark() {}
697
698     const char *getName() { return "vld"; }
699
700     bool usesNeon() { return true; }
701
702 protected:
703     // Write a given value using vst.
704     void bench(size_t num_loops) {
705 #if defined(__ARM_NEON__)
706         asm volatile(
707             "stmfd sp!, {r0,r1,r2,r3}\n"
708
709             "mov r0, %0\n"
710             "mov r1, %1\n"
711             "mov r2, %2\n"
712
713             "0:\n"
714             "mov r3, r1, lsr #5\n"
715
716             "1:\n"
717             "subs r3, r3, #1\n"
718             "vld1.8 {d0-d3}, [r0:128]!\n"
719             "bgt 1b\n"
720
721             "sub r0, r0, r1\n"
722             "subs r2, r2, #1\n"
723             "bgt 0b\n"
724
725             "ldmfd sp!, {r0,r1,r2,r3}\n"
726         :: "r" (_buffer), "r" (_size), "r" (num_loops) : "r0", "r1", "r2");
727 #endif
728     }
729 };
730
731 class ReadVldmiaBenchmark : public SingleBufferBandwidthBenchmark {
732 public:
733     ReadVldmiaBenchmark() : SingleBufferBandwidthBenchmark() { }
734     virtual ~ReadVldmiaBenchmark() {}
735
736     const char *getName() { return "vldmia"; }
737
738     bool usesNeon() { return true; }
739
740 protected:
741     // Write a given value using vstmia.
742     void bench(size_t num_loops) {
743 #if defined(__ARM_NEON__)
744         asm volatile(
745             "stmfd sp!, {r0,r1,r2,r3}\n"
746
747             "mov r0, %0\n"
748             "mov r1, %1\n"
749             "mov r2, %2\n"
750
751             "0:\n"
752             "mov r3, r1, lsr #5\n"
753
754             "1:\n"
755             "subs r3, r3, #1\n"
756             "vldmia r0!, {d0-d3}\n"
757             "bgt 1b\n"
758
759             "sub r0, r0, r1\n"
760             "subs r2, r2, #1\n"
761             "bgt 0b\n"
762
763             "ldmfd sp!, {r0,r1,r2,r3}\n"
764         :: "r" (_buffer), "r" (_size), "r" (num_loops) : "r0", "r1", "r2");
765 #endif
766     }
767 };
768
769 #endif  // __BANDWIDTH_H__