Home | History | Annotate | Download | only in test
      1 /*
      2  *  Copyright (c) 2012 The WebM project authors. All Rights Reserved.
      3  *
      4  *  Use of this source code is governed by a BSD-style license
      5  *  that can be found in the LICENSE file in the root of the source
      6  *  tree. An additional intellectual property rights grant can be found
      7  *  in the file PATENTS.  All contributing project authors may
      8  *  be found in the AUTHORS file in the root of the source tree.
      9  */
     10 #include <limits.h>
     11 #include "./vpx_config.h"
     12 #include "./vpx_dsp_rtcd.h"
     13 #include "test/acm_random.h"
     14 #include "test/buffer.h"
     15 #include "test/clear_system_state.h"
     16 #include "test/register_state_check.h"
     17 #include "third_party/googletest/src/include/gtest/gtest.h"
     18 #include "vpx/vpx_integer.h"
     19 #include "vpx_mem/vpx_mem.h"
     20 
     21 using libvpx_test::ACMRandom;
     22 using libvpx_test::Buffer;
     23 
     24 typedef void (*VpxPostProcDownAndAcrossMbRowFunc)(
     25     unsigned char *src_ptr, unsigned char *dst_ptr, int src_pixels_per_line,
     26     int dst_pixels_per_line, int cols, unsigned char *flimit, int size);
     27 
     28 typedef void (*VpxMbPostProcAcrossIpFunc)(unsigned char *src, int pitch,
     29                                           int rows, int cols, int flimit);
     30 
     31 typedef void (*VpxMbPostProcDownFunc)(unsigned char *dst, int pitch, int rows,
     32                                       int cols, int flimit);
     33 
     34 namespace {
     35 
     36 // Compute the filter level used in post proc from the loop filter strength
     37 int q2mbl(int x) {
     38   if (x < 20) x = 20;
     39 
     40   x = 50 + (x - 50) * 10 / 8;
     41   return x * x / 3;
     42 }
     43 
     44 class VpxPostProcDownAndAcrossMbRowTest
     45     : public ::testing::TestWithParam<VpxPostProcDownAndAcrossMbRowFunc> {
     46  public:
     47   virtual void TearDown() { libvpx_test::ClearSystemState(); }
     48 };
     49 
     50 // Test routine for the VPx post-processing function
     51 // vpx_post_proc_down_and_across_mb_row_c.
     52 
     53 TEST_P(VpxPostProcDownAndAcrossMbRowTest, CheckFilterOutput) {
     54   // Size of the underlying data block that will be filtered.
     55   const int block_width = 16;
     56   const int block_height = 16;
     57 
     58   // 5-tap filter needs 2 padding rows above and below the block in the input.
     59   Buffer<uint8_t> src_image = Buffer<uint8_t>(block_width, block_height, 2);
     60 
     61   // Filter extends output block by 8 samples at left and right edges.
     62   // Though the left padding is only 8 bytes, the assembly code tries to
     63   // read 16 bytes before the pointer.
     64   Buffer<uint8_t> dst_image =
     65       Buffer<uint8_t>(block_width, block_height, 8, 16, 8, 8);
     66 
     67   uint8_t *const flimits =
     68       reinterpret_cast<uint8_t *>(vpx_memalign(16, block_width));
     69   (void)memset(flimits, 255, block_width);
     70 
     71   // Initialize pixels in the input:
     72   //   block pixels to value 1,
     73   //   border pixels to value 10.
     74   src_image.SetPadding(10);
     75   src_image.Set(1);
     76 
     77   // Initialize pixels in the output to 99.
     78   dst_image.Set(99);
     79 
     80   ASM_REGISTER_STATE_CHECK(GetParam()(
     81       src_image.TopLeftPixel(), dst_image.TopLeftPixel(), src_image.stride(),
     82       dst_image.stride(), block_width, flimits, 16));
     83 
     84   static const uint8_t kExpectedOutput[block_height] = {
     85     4, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 4
     86   };
     87 
     88   uint8_t *pixel_ptr = dst_image.TopLeftPixel();
     89   for (int i = 0; i < block_height; ++i) {
     90     for (int j = 0; j < block_width; ++j) {
     91       ASSERT_EQ(kExpectedOutput[i], pixel_ptr[j]) << "at (" << i << ", " << j
     92                                                   << ")";
     93     }
     94     pixel_ptr += dst_image.stride();
     95   }
     96 
     97   vpx_free(flimits);
     98 };
     99 
    100 TEST_P(VpxPostProcDownAndAcrossMbRowTest, CheckCvsAssembly) {
    101   // Size of the underlying data block that will be filtered.
    102   // Y blocks are always a multiple of 16 wide and exactly 16 high. U and V
    103   // blocks are always a multiple of 8 wide and exactly 8 high.
    104   const int block_width = 136;
    105   const int block_height = 16;
    106 
    107   // 5-tap filter needs 2 padding rows above and below the block in the input.
    108   // SSE2 reads in blocks of 16. Pad an extra 8 in case the width is not %16.
    109   Buffer<uint8_t> src_image =
    110       Buffer<uint8_t>(block_width, block_height, 2, 2, 10, 2);
    111 
    112   // Filter extends output block by 8 samples at left and right edges.
    113   // Though the left padding is only 8 bytes, there is 'above' padding as well
    114   // so when the assembly code tries to read 16 bytes before the pointer it is
    115   // not a problem.
    116   // SSE2 reads in blocks of 16. Pad an extra 8 in case the width is not %16.
    117   Buffer<uint8_t> dst_image =
    118       Buffer<uint8_t>(block_width, block_height, 8, 8, 16, 8);
    119   Buffer<uint8_t> dst_image_ref = Buffer<uint8_t>(block_width, block_height, 8);
    120 
    121   // Filter values are set in blocks of 16 for Y and 8 for U/V. Each macroblock
    122   // can have a different filter. SSE2 assembly reads flimits in blocks of 16 so
    123   // it must be padded out.
    124   const int flimits_width = block_width % 16 ? block_width + 8 : block_width;
    125   uint8_t *const flimits =
    126       reinterpret_cast<uint8_t *>(vpx_memalign(16, flimits_width));
    127 
    128   ACMRandom rnd;
    129   rnd.Reset(ACMRandom::DeterministicSeed());
    130   // Initialize pixels in the input:
    131   //   block pixels to random values.
    132   //   border pixels to value 10.
    133   src_image.SetPadding(10);
    134   src_image.Set(&rnd, &ACMRandom::Rand8);
    135 
    136   for (int blocks = 0; blocks < block_width; blocks += 8) {
    137     (void)memset(flimits, 0, sizeof(*flimits) * flimits_width);
    138 
    139     for (int f = 0; f < 255; f++) {
    140       (void)memset(flimits + blocks, f, sizeof(*flimits) * 8);
    141 
    142       dst_image.Set(0);
    143       dst_image_ref.Set(0);
    144 
    145       vpx_post_proc_down_and_across_mb_row_c(
    146           src_image.TopLeftPixel(), dst_image_ref.TopLeftPixel(),
    147           src_image.stride(), dst_image_ref.stride(), block_width, flimits,
    148           block_height);
    149       ASM_REGISTER_STATE_CHECK(
    150           GetParam()(src_image.TopLeftPixel(), dst_image.TopLeftPixel(),
    151                      src_image.stride(), dst_image.stride(), block_width,
    152                      flimits, block_height));
    153 
    154       ASSERT_TRUE(dst_image.CheckValues(dst_image_ref));
    155     }
    156   }
    157 
    158   vpx_free(flimits);
    159 }
    160 
    161 class VpxMbPostProcAcrossIpTest
    162     : public ::testing::TestWithParam<VpxMbPostProcAcrossIpFunc> {
    163  public:
    164   virtual void TearDown() { libvpx_test::ClearSystemState(); }
    165 
    166  protected:
    167   void SetCols(unsigned char *s, int rows, int cols, int src_width) {
    168     for (int r = 0; r < rows; r++) {
    169       for (int c = 0; c < cols; c++) {
    170         s[c] = c;
    171       }
    172       s += src_width;
    173     }
    174   }
    175 
    176   void RunComparison(const unsigned char *expected_output, unsigned char *src_c,
    177                      int rows, int cols, int src_pitch) {
    178     for (int r = 0; r < rows; r++) {
    179       for (int c = 0; c < cols; c++) {
    180         ASSERT_EQ(expected_output[c], src_c[c]) << "at (" << r << ", " << c
    181                                                 << ")";
    182       }
    183       src_c += src_pitch;
    184     }
    185   }
    186 
    187   void RunFilterLevel(unsigned char *s, int rows, int cols, int src_width,
    188                       int filter_level, const unsigned char *expected_output) {
    189     ASM_REGISTER_STATE_CHECK(
    190         GetParam()(s, src_width, rows, cols, filter_level));
    191     RunComparison(expected_output, s, rows, cols, src_width);
    192   }
    193 };
    194 
    195 TEST_P(VpxMbPostProcAcrossIpTest, CheckLowFilterOutput) {
    196   const int rows = 16;
    197   const int cols = 16;
    198 
    199   Buffer<uint8_t> src = Buffer<uint8_t>(cols, rows, 8, 8, 17, 8);
    200   src.SetPadding(10);
    201   SetCols(src.TopLeftPixel(), rows, cols, src.stride());
    202 
    203   Buffer<uint8_t> expected_output = Buffer<uint8_t>(cols, rows, 0);
    204   SetCols(expected_output.TopLeftPixel(), rows, cols, expected_output.stride());
    205 
    206   RunFilterLevel(src.TopLeftPixel(), rows, cols, src.stride(), q2mbl(0),
    207                  expected_output.TopLeftPixel());
    208 }
    209 
    210 TEST_P(VpxMbPostProcAcrossIpTest, CheckMediumFilterOutput) {
    211   const int rows = 16;
    212   const int cols = 16;
    213 
    214   Buffer<uint8_t> src = Buffer<uint8_t>(cols, rows, 8, 8, 17, 8);
    215   src.SetPadding(10);
    216   SetCols(src.TopLeftPixel(), rows, cols, src.stride());
    217 
    218   static const unsigned char kExpectedOutput[cols] = {
    219     2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 13
    220   };
    221 
    222   RunFilterLevel(src.TopLeftPixel(), rows, cols, src.stride(), q2mbl(70),
    223                  kExpectedOutput);
    224 }
    225 
    226 TEST_P(VpxMbPostProcAcrossIpTest, CheckHighFilterOutput) {
    227   const int rows = 16;
    228   const int cols = 16;
    229 
    230   Buffer<uint8_t> src = Buffer<uint8_t>(cols, rows, 8, 8, 17, 8);
    231   src.SetPadding(10);
    232   SetCols(src.TopLeftPixel(), rows, cols, src.stride());
    233 
    234   static const unsigned char kExpectedOutput[cols] = {
    235     2, 2, 3, 4, 4, 5, 6, 7, 8, 9, 10, 11, 11, 12, 13, 13
    236   };
    237 
    238   RunFilterLevel(src.TopLeftPixel(), rows, cols, src.stride(), INT_MAX,
    239                  kExpectedOutput);
    240 
    241   SetCols(src.TopLeftPixel(), rows, cols, src.stride());
    242 
    243   RunFilterLevel(src.TopLeftPixel(), rows, cols, src.stride(), q2mbl(100),
    244                  kExpectedOutput);
    245 }
    246 
    247 TEST_P(VpxMbPostProcAcrossIpTest, CheckCvsAssembly) {
    248   const int rows = 16;
    249   const int cols = 16;
    250 
    251   Buffer<uint8_t> c_mem = Buffer<uint8_t>(cols, rows, 8, 8, 17, 8);
    252   Buffer<uint8_t> asm_mem = Buffer<uint8_t>(cols, rows, 8, 8, 17, 8);
    253 
    254   // When level >= 100, the filter behaves the same as the level = INT_MAX
    255   // When level < 20, it behaves the same as the level = 0
    256   for (int level = 0; level < 100; level++) {
    257     c_mem.SetPadding(10);
    258     asm_mem.SetPadding(10);
    259     SetCols(c_mem.TopLeftPixel(), rows, cols, c_mem.stride());
    260     SetCols(asm_mem.TopLeftPixel(), rows, cols, asm_mem.stride());
    261 
    262     vpx_mbpost_proc_across_ip_c(c_mem.TopLeftPixel(), c_mem.stride(), rows,
    263                                 cols, q2mbl(level));
    264     ASM_REGISTER_STATE_CHECK(GetParam()(
    265         asm_mem.TopLeftPixel(), asm_mem.stride(), rows, cols, q2mbl(level)));
    266 
    267     ASSERT_TRUE(asm_mem.CheckValues(c_mem));
    268   }
    269 }
    270 
    271 class VpxMbPostProcDownTest
    272     : public ::testing::TestWithParam<VpxMbPostProcDownFunc> {
    273  public:
    274   virtual void TearDown() { libvpx_test::ClearSystemState(); }
    275 
    276  protected:
    277   void SetRows(unsigned char *src_c, int rows, int cols, int src_width) {
    278     for (int r = 0; r < rows; r++) {
    279       memset(src_c, r, cols);
    280       src_c += src_width;
    281     }
    282   }
    283 
    284   void RunComparison(const unsigned char *expected_output, unsigned char *src_c,
    285                      int rows, int cols, int src_pitch) {
    286     for (int r = 0; r < rows; r++) {
    287       for (int c = 0; c < cols; c++) {
    288         ASSERT_EQ(expected_output[r * rows + c], src_c[c]) << "at (" << r
    289                                                            << ", " << c << ")";
    290       }
    291       src_c += src_pitch;
    292     }
    293   }
    294 
    295   void RunFilterLevel(unsigned char *s, int rows, int cols, int src_width,
    296                       int filter_level, const unsigned char *expected_output) {
    297     ASM_REGISTER_STATE_CHECK(
    298         GetParam()(s, src_width, rows, cols, filter_level));
    299     RunComparison(expected_output, s, rows, cols, src_width);
    300   }
    301 };
    302 
    303 TEST_P(VpxMbPostProcDownTest, CheckHighFilterOutput) {
    304   const int rows = 16;
    305   const int cols = 16;
    306 
    307   Buffer<uint8_t> src_c = Buffer<uint8_t>(cols, rows, 8, 8, 8, 17);
    308   src_c.SetPadding(10);
    309 
    310   SetRows(src_c.TopLeftPixel(), rows, cols, src_c.stride());
    311 
    312   static const unsigned char kExpectedOutput[rows * cols] = {
    313     2,  2,  1,  1,  2,  2,  2,  2,  2,  2,  1,  1,  2,  2,  2,  2,  2,  2,  2,
    314     2,  3,  2,  2,  2,  2,  2,  2,  2,  3,  2,  2,  2,  3,  3,  3,  3,  3,  3,
    315     3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  4,  4,  3,  4,  4,  3,  3,  3,
    316     4,  4,  3,  4,  4,  3,  3,  4,  5,  4,  4,  4,  4,  4,  4,  4,  5,  4,  4,
    317     4,  4,  4,  4,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,
    318     5,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  7,  7,
    319     7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  8,  8,  8,  8,  8,
    320     8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  9,  8,  9,  9,  8,  8,  8,  9,
    321     9,  8,  9,  9,  8,  8,  8,  9,  9,  10, 10, 9,  9,  9,  10, 10, 9,  10, 10,
    322     9,  9,  9,  10, 10, 10, 11, 10, 10, 10, 11, 10, 11, 10, 11, 10, 10, 10, 11,
    323     10, 11, 11, 11, 11, 11, 11, 11, 12, 11, 11, 11, 11, 11, 11, 11, 12, 11, 12,
    324     12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 12,
    325     13, 12, 13, 12, 12, 12, 13, 12, 13, 12, 13, 12, 13, 13, 13, 14, 13, 13, 13,
    326     13, 13, 13, 13, 14, 13, 13, 13, 13
    327   };
    328 
    329   RunFilterLevel(src_c.TopLeftPixel(), rows, cols, src_c.stride(), INT_MAX,
    330                  kExpectedOutput);
    331 
    332   src_c.SetPadding(10);
    333   SetRows(src_c.TopLeftPixel(), rows, cols, src_c.stride());
    334   RunFilterLevel(src_c.TopLeftPixel(), rows, cols, src_c.stride(), q2mbl(100),
    335                  kExpectedOutput);
    336 }
    337 
    338 TEST_P(VpxMbPostProcDownTest, CheckMediumFilterOutput) {
    339   const int rows = 16;
    340   const int cols = 16;
    341 
    342   Buffer<uint8_t> src_c = Buffer<uint8_t>(cols, rows, 8, 8, 8, 17);
    343   src_c.SetPadding(10);
    344 
    345   SetRows(src_c.TopLeftPixel(), rows, cols, src_c.stride());
    346 
    347   static const unsigned char kExpectedOutput[rows * cols] = {
    348     2,  2,  1,  1,  2,  2,  2,  2,  2,  2,  1,  1,  2,  2,  2,  2,  2,  2,  2,
    349     2,  3,  2,  2,  2,  2,  2,  2,  2,  3,  2,  2,  2,  2,  2,  2,  2,  2,  2,
    350     2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  3,  3,  3,  3,  3,  3,  3,  3,  3,
    351     3,  3,  3,  3,  3,  3,  3,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,
    352     4,  4,  4,  4,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,
    353     5,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  7,  7,
    354     7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  8,  8,  8,  8,  8,
    355     8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  9,  9,  9,  9,  9,  9,  9,  9,
    356     9,  9,  9,  9,  9,  9,  9,  9,  10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
    357     10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
    358     11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13,
    359     13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 12, 12, 13, 12,
    360     13, 12, 13, 12, 12, 12, 13, 12, 13, 12, 13, 12, 13, 13, 13, 14, 13, 13, 13,
    361     13, 13, 13, 13, 14, 13, 13, 13, 13
    362   };
    363 
    364   RunFilterLevel(src_c.TopLeftPixel(), rows, cols, src_c.stride(), q2mbl(70),
    365                  kExpectedOutput);
    366 }
    367 
    368 TEST_P(VpxMbPostProcDownTest, CheckLowFilterOutput) {
    369   const int rows = 16;
    370   const int cols = 16;
    371 
    372   Buffer<uint8_t> src_c = Buffer<uint8_t>(cols, rows, 8, 8, 8, 17);
    373   src_c.SetPadding(10);
    374 
    375   SetRows(src_c.TopLeftPixel(), rows, cols, src_c.stride());
    376 
    377   unsigned char *expected_output = new unsigned char[rows * cols];
    378   ASSERT_TRUE(expected_output != NULL);
    379   SetRows(expected_output, rows, cols, cols);
    380 
    381   RunFilterLevel(src_c.TopLeftPixel(), rows, cols, src_c.stride(), q2mbl(0),
    382                  expected_output);
    383 
    384   delete[] expected_output;
    385 }
    386 
    387 TEST_P(VpxMbPostProcDownTest, CheckCvsAssembly) {
    388   const int rows = 16;
    389   const int cols = 16;
    390 
    391   ACMRandom rnd;
    392   rnd.Reset(ACMRandom::DeterministicSeed());
    393 
    394   Buffer<uint8_t> src_c = Buffer<uint8_t>(cols, rows, 8, 8, 8, 17);
    395   Buffer<uint8_t> src_asm = Buffer<uint8_t>(cols, rows, 8, 8, 8, 17);
    396 
    397   for (int level = 0; level < 100; level++) {
    398     src_c.SetPadding(10);
    399     src_asm.SetPadding(10);
    400     src_c.Set(&rnd, &ACMRandom::Rand8);
    401     src_asm.CopyFrom(src_c);
    402 
    403     vpx_mbpost_proc_down_c(src_c.TopLeftPixel(), src_c.stride(), rows, cols,
    404                            q2mbl(level));
    405     ASM_REGISTER_STATE_CHECK(GetParam()(
    406         src_asm.TopLeftPixel(), src_asm.stride(), rows, cols, q2mbl(level)));
    407     ASSERT_TRUE(src_asm.CheckValues(src_c));
    408 
    409     src_c.SetPadding(10);
    410     src_asm.SetPadding(10);
    411     src_c.Set(&rnd, &ACMRandom::Rand8Extremes);
    412     src_asm.CopyFrom(src_c);
    413 
    414     vpx_mbpost_proc_down_c(src_c.TopLeftPixel(), src_c.stride(), rows, cols,
    415                            q2mbl(level));
    416     ASM_REGISTER_STATE_CHECK(GetParam()(
    417         src_asm.TopLeftPixel(), src_asm.stride(), rows, cols, q2mbl(level)));
    418     ASSERT_TRUE(src_asm.CheckValues(src_c));
    419   }
    420 }
    421 
    422 INSTANTIATE_TEST_CASE_P(
    423     C, VpxPostProcDownAndAcrossMbRowTest,
    424     ::testing::Values(vpx_post_proc_down_and_across_mb_row_c));
    425 
    426 INSTANTIATE_TEST_CASE_P(C, VpxMbPostProcAcrossIpTest,
    427                         ::testing::Values(vpx_mbpost_proc_across_ip_c));
    428 
    429 INSTANTIATE_TEST_CASE_P(C, VpxMbPostProcDownTest,
    430                         ::testing::Values(vpx_mbpost_proc_down_c));
    431 
    432 #if HAVE_SSE2
    433 INSTANTIATE_TEST_CASE_P(
    434     SSE2, VpxPostProcDownAndAcrossMbRowTest,
    435     ::testing::Values(vpx_post_proc_down_and_across_mb_row_sse2));
    436 
    437 INSTANTIATE_TEST_CASE_P(SSE2, VpxMbPostProcAcrossIpTest,
    438                         ::testing::Values(vpx_mbpost_proc_across_ip_sse2));
    439 
    440 INSTANTIATE_TEST_CASE_P(SSE2, VpxMbPostProcDownTest,
    441                         ::testing::Values(vpx_mbpost_proc_down_sse2));
    442 #endif  // HAVE_SSE2
    443 
    444 #if HAVE_NEON
    445 INSTANTIATE_TEST_CASE_P(
    446     NEON, VpxPostProcDownAndAcrossMbRowTest,
    447     ::testing::Values(vpx_post_proc_down_and_across_mb_row_neon));
    448 
    449 INSTANTIATE_TEST_CASE_P(NEON, VpxMbPostProcAcrossIpTest,
    450                         ::testing::Values(vpx_mbpost_proc_across_ip_neon));
    451 
    452 INSTANTIATE_TEST_CASE_P(NEON, VpxMbPostProcDownTest,
    453                         ::testing::Values(vpx_mbpost_proc_down_neon));
    454 #endif  // HAVE_NEON
    455 
    456 #if HAVE_MSA
    457 INSTANTIATE_TEST_CASE_P(
    458     MSA, VpxPostProcDownAndAcrossMbRowTest,
    459     ::testing::Values(vpx_post_proc_down_and_across_mb_row_msa));
    460 
    461 INSTANTIATE_TEST_CASE_P(MSA, VpxMbPostProcAcrossIpTest,
    462                         ::testing::Values(vpx_mbpost_proc_across_ip_msa));
    463 
    464 INSTANTIATE_TEST_CASE_P(MSA, VpxMbPostProcDownTest,
    465                         ::testing::Values(vpx_mbpost_proc_down_msa));
    466 #endif  // HAVE_MSA
    467 
    468 }  // namespace
    469