mirror of
https://chromium.googlesource.com/libyuv/libyuv
synced 2026-10-28 18:07:12 +08:00
[libyuv] Fix MSan uninitialized errors in AVX512 alpha and scale rows
Clang MemorySanitizer builds define LIBYUV_ENABLE_ROWWIN and compile row_win.cc and scale_win.cc instead of inline assembly. Two AVX-512 intrinsic patterns in those files are not modeled by LLVM MSan: 1. _mm512_mask_cvtepi32_storeu_epi8 and _mm512_mask_cvtepi64_storeu_epi8 lower to @llvm.x86.avx512.mask.pmov.db.mem.512 and @llvm.x86.avx512.mask.pmov.qb.mem.512, which LLVM MSan handles as unknown intrinsics without unpoisoning destination shadow memory. Replace them in ARGBExtractAlphaRow_AVX512BW, ScaleAddCols1_AVX512BW, and ScaleAddCols2_AVX512BW with _mm512_cvtepi32_epi8 / _mm512_cvtepi64_epi8 followed by _mm_mask_storeu_epi8, which lowers to @llvm.masked.store and still compiles to a single vpmovdb/vpmovqb instruction. 2. ARGBCopyYToAlphaRow_AVX512BW loaded dst and blended the alpha channel using _mm512_ternarylogic_epi32, which LLVM MSan does not model bitwise and flags when any destination byte (such as the old alpha channel) is uninitialized. Replace the load + vpternlogd + store in both row_win.cc and row_gcc.cc with byte-masked stores (_mm512_mask_storeu_epi8 / vmovdqu8 with mask 0x8888888888888888) that write only the alpha bytes without reading dst. Expand ARGBCopyAlpha, ARGBExtractAlpha, and ARGBCopyYToAlpha unit tests into _Any, _Unaligned, _Invert, and _Opt variants and poison destination buffers under MSan to catch unmodeled masked stores. Document MSan AVX-512 intrinsic guidelines in GEMINI.md. Test: out/msan/libyuv_unittest --gtest_filter=*Alpha*:*ScalePlaneBox_Any* Bug: 578615529 Change-Id: I54ac4a5844aca6cfe3aa3445fa04b8221c610b35
This commit is contained in:
parent
4bc0419865
commit
1799701dc2
22
GEMINI.md
22
GEMINI.md
@ -80,6 +80,28 @@ and compiler compatibility.
|
||||
`"ymmN"` or `"zmmN"`, including for AVX2 and AVX512 code. Clang for
|
||||
Windows must preserve xmm6-xmm15 and does not save them when they are
|
||||
declared as ymm or zmm.
|
||||
4. **MSan Intrinsics (`row_win.cc` / `scale_win.cc`)**: Clang builds with
|
||||
MemorySanitizer (`-fsanitize=memory`) define `LIBYUV_ENABLE_ROWWIN` and
|
||||
compile `row_win.cc` and `scale_win.cc` instead of inline assembly. Avoid
|
||||
AVX-512 intrinsics that LLVM's MSan pass does not instrument:
|
||||
* **Down-converting masked stores**: Never use
|
||||
`_mm512_mask_cvtepi32_storeu_epi8`, `_mm512_mask_cvtepi64_storeu_epi8`,
|
||||
`_mm512_mask_cvtepi16_storeu_epi8`, or
|
||||
`_mm512_mask_cvtepi32_storeu_epi16`. They lower to target-specific
|
||||
`@llvm.x86.avx512.mask.pmov.*.mem.*` intrinsics that LLVM MSan does not
|
||||
model, leaving the destination shadow memory poisoned (uninitialized).
|
||||
Instead, convert to a narrower vector first and use a standard masked
|
||||
store (e.g. `_mm_mask_storeu_epi8(dst, mask, _mm512_cvtepi32_epi8(v))`),
|
||||
which lowers to `@llvm.masked.store` for MSan and still compiles to a
|
||||
single `vpmovdb` instruction at `-O2`.
|
||||
* **Ternary logic (`_mm512_ternarylogic_epi32` / `epi64`) on partially
|
||||
uninitialized vectors**: LLVM MSan does not model `vpternlog` truth
|
||||
tables bitwise and flags `use-of-uninitialized-value` if any bit of any
|
||||
of the three inputs is uninitialized, even if masked off by `imm8`. For
|
||||
partial channel updates (e.g. writing alpha into `dst` in
|
||||
`ARGBCopyYToAlphaRow_AVX512BW`), use byte-masked stores
|
||||
(`_mm512_mask_storeu_epi8`) instead of reading `dst` and blending with
|
||||
`_mm512_ternarylogic_epi32`.
|
||||
|
||||
### AArch64
|
||||
|
||||
|
||||
@ -8825,57 +8825,55 @@ void ARGBCopyYToAlphaRow_AVX2(const uint8_t* src, uint8_t* dst, int width) {
|
||||
void ARGBCopyYToAlphaRow_AVX512BW(const uint8_t* src, uint8_t* dst, int width) {
|
||||
uintptr_t temp;
|
||||
asm volatile(
|
||||
"vpternlogd $0xff,%%zmm0,%%zmm0,%%zmm0 \n"
|
||||
"vpsrld $0x8,%%zmm0,%%zmm0 \n"
|
||||
"sub $0x20,%2 \n"
|
||||
"jl 2f \n"
|
||||
"kmovq %4,%%k1 \n"
|
||||
"sub $0x20,%2 \n"
|
||||
"jl 2f \n"
|
||||
|
||||
LABELALIGN
|
||||
"1: \n"
|
||||
"vpmovzxbd (%0),%%zmm1 \n"
|
||||
"vpmovzxbd 0x10(%0),%%zmm2 \n"
|
||||
"lea 0x20(%0),%0 \n"
|
||||
"vpslld $0x18,%%zmm1,%%zmm1 \n"
|
||||
"vpslld $0x18,%%zmm2,%%zmm2 \n"
|
||||
"vpternlogd $0xf8,(%1),%%zmm0,%%zmm1 \n"
|
||||
"vpternlogd $0xf8,0x40(%1),%%zmm0,%%zmm2 \n"
|
||||
"vmovdqu32 %%zmm1,(%1) \n"
|
||||
"vmovdqu32 %%zmm2,0x40(%1) \n"
|
||||
"lea 0x80(%1),%1 \n"
|
||||
"sub $0x20,%2 \n"
|
||||
"jge 1b \n"
|
||||
"vpmovzxbd (%0),%%zmm1 \n"
|
||||
"vpmovzxbd 0x10(%0),%%zmm2 \n"
|
||||
"lea 0x20(%0),%0 \n"
|
||||
"vpslld $0x18,%%zmm1,%%zmm1 \n"
|
||||
"vpslld $0x18,%%zmm2,%%zmm2 \n"
|
||||
"vmovdqu8 %%zmm1,(%1)%{%%k1%} \n"
|
||||
"vmovdqu8 %%zmm2,0x40(%1)%{%%k1%} \n"
|
||||
"lea 0x80(%1),%1 \n"
|
||||
"sub $0x20,%2 \n"
|
||||
"jge 1b \n"
|
||||
|
||||
"2: \n"
|
||||
"add $0x20,%2 \n"
|
||||
"jle 99f \n"
|
||||
"cmp $0x10,%2 \n"
|
||||
"jl 3f \n"
|
||||
"vpmovzxbd (%0),%%zmm1 \n"
|
||||
"lea 0x10(%0),%0 \n"
|
||||
"vpslld $0x18,%%zmm1,%%zmm1 \n"
|
||||
"vpternlogd $0xf8,(%1),%%zmm0,%%zmm1 \n"
|
||||
"vmovdqu32 %%zmm1,(%1) \n"
|
||||
"lea 0x40(%1),%1 \n"
|
||||
"sub $0x10,%2 \n"
|
||||
"jz 99f \n"
|
||||
"add $0x20,%2 \n"
|
||||
"jle 99f \n"
|
||||
"cmp $0x10,%2 \n"
|
||||
"jl 3f \n"
|
||||
"vpmovzxbd (%0),%%zmm1 \n"
|
||||
"lea 0x10(%0),%0 \n"
|
||||
"vpslld $0x18,%%zmm1,%%zmm1 \n"
|
||||
"vmovdqu8 %%zmm1,(%1)%{%%k1%} \n"
|
||||
"lea 0x40(%1),%1 \n"
|
||||
"sub $0x10,%2 \n"
|
||||
"jz 99f \n"
|
||||
|
||||
"3: \n"
|
||||
"mov $-1,%k3 \n"
|
||||
"bzhi %k2,%k3,%k3 \n"
|
||||
"kmovw %k3,%%k1 \n"
|
||||
"vpmovzxbd (%0),%%zmm1%{%%k1%}%{z%} \n"
|
||||
"vpslld $0x18,%%zmm1,%%zmm1 \n"
|
||||
"vpternlogd $0xf8,(%1),%%zmm0,%%zmm1%{%%k1%}%{z%} \n"
|
||||
"vmovdqu32 %%zmm1,(%1)%{%%k1%} \n"
|
||||
"mov $-1,%k3 \n"
|
||||
"bzhi %k2,%k3,%k3 \n"
|
||||
"kmovw %k3,%%k1 \n"
|
||||
"vpmovzxbd (%0),%%zmm1%{%%k1%}%{z%} \n"
|
||||
"vpslld $0x18,%%zmm1,%%zmm1 \n"
|
||||
"shl $0x2,%k2 \n"
|
||||
"bzhi %q2,%4,%3 \n"
|
||||
"kmovq %3,%%k1 \n"
|
||||
"vmovdqu8 %%zmm1,(%1)%{%%k1%} \n"
|
||||
|
||||
"99: \n"
|
||||
"vzeroupper \n"
|
||||
: "+r"(src), // %0
|
||||
"+r"(dst), // %1
|
||||
"+r"(width), // %2
|
||||
"=&r"(temp) // %3
|
||||
:
|
||||
: "memory", "cc", "xmm0", "xmm1", "xmm2", "k1");
|
||||
: "+r"(src), // %0
|
||||
"+r"(dst), // %1
|
||||
"+r"(width), // %2
|
||||
"=&r"(temp) // %3
|
||||
: "r"(0x8888888888888888ull) // %4
|
||||
: "memory", "cc", "xmm1", "xmm2", "k1");
|
||||
}
|
||||
#endif // HAS_ARGBCOPYYTOALPHAROW_AVX512BW
|
||||
|
||||
|
||||
@ -3266,7 +3266,11 @@ void ARGBExtractAlphaRow_AVX512BW(const uint8_t* src_argb,
|
||||
if (width > 0) {
|
||||
__mmask16 mask = (__mmask16)((1u << width) - 1u);
|
||||
__m512i z0 = _mm512_maskz_loadu_epi32(mask, src_argb);
|
||||
_mm512_mask_cvtepi32_storeu_epi8(dst_a, mask, _mm512_srli_epi32(z0, 24));
|
||||
__m128i a0 = _mm512_cvtepi32_epi8(_mm512_srli_epi32(z0, 24));
|
||||
// Use _mm_mask_storeu_epi8 instead of _mm512_mask_cvtepi32_storeu_epi8
|
||||
// because LLVM MSan does not instrument @llvm.x86.avx512.mask.pmov.db.mem
|
||||
// to unpoison destination shadow memory.
|
||||
_mm_mask_storeu_epi8(dst_a, mask, a0);
|
||||
}
|
||||
_mm256_zeroupper();
|
||||
}
|
||||
@ -3275,18 +3279,18 @@ void ARGBExtractAlphaRow_AVX512BW(const uint8_t* src_argb,
|
||||
#ifdef HAS_ARGBCOPYYTOALPHAROW_AVX512BW
|
||||
LIBYUV_TARGET_AVX512BW
|
||||
void ARGBCopyYToAlphaRow_AVX512BW(const uint8_t* src, uint8_t* dst, int width) {
|
||||
__m512i rgb_mask = _mm512_set1_epi32(0x00ffffff);
|
||||
// Store only the alpha byte (every 4th byte) using a byte mask instead of
|
||||
// loading dst and blending with _mm512_ternarylogic_epi32. This avoids
|
||||
// reading uninitialized destination bytes and avoids
|
||||
// @llvm.x86.avx512.pternlog which LLVM MSan does not model bitwise.
|
||||
const __mmask64 alpha_mask = 0x8888888888888888ull;
|
||||
while (width >= 32) {
|
||||
__m512i a0 = _mm512_slli_epi32(
|
||||
_mm512_cvtepu8_epi32(_mm_loadu_si128((const __m128i*)src)), 24);
|
||||
__m512i a1 = _mm512_slli_epi32(
|
||||
_mm512_cvtepu8_epi32(_mm_loadu_si128((const __m128i*)(src + 16))), 24);
|
||||
a0 = _mm512_ternarylogic_epi32(
|
||||
a0, rgb_mask, _mm512_loadu_si512((const __m512i*)dst), 0xf8);
|
||||
a1 = _mm512_ternarylogic_epi32(
|
||||
a1, rgb_mask, _mm512_loadu_si512((const __m512i*)(dst + 64)), 0xf8);
|
||||
_mm512_storeu_si512((__m512i*)dst, a0);
|
||||
_mm512_storeu_si512((__m512i*)(dst + 64), a1);
|
||||
_mm512_mask_storeu_epi8(dst, alpha_mask, a0);
|
||||
_mm512_mask_storeu_epi8(dst + 64, alpha_mask, a1);
|
||||
src += 32;
|
||||
dst += 128;
|
||||
width -= 32;
|
||||
@ -3294,20 +3298,17 @@ void ARGBCopyYToAlphaRow_AVX512BW(const uint8_t* src, uint8_t* dst, int width) {
|
||||
if (width >= 16) {
|
||||
__m512i a0 = _mm512_slli_epi32(
|
||||
_mm512_cvtepu8_epi32(_mm_loadu_si128((const __m128i*)src)), 24);
|
||||
a0 = _mm512_ternarylogic_epi32(
|
||||
a0, rgb_mask, _mm512_loadu_si512((const __m512i*)dst), 0xf8);
|
||||
_mm512_storeu_si512((__m512i*)dst, a0);
|
||||
_mm512_mask_storeu_epi8(dst, alpha_mask, a0);
|
||||
src += 16;
|
||||
dst += 64;
|
||||
width -= 16;
|
||||
}
|
||||
if (width > 0) {
|
||||
__mmask16 mask = (__mmask16)((1u << width) - 1u);
|
||||
__mmask64 tail_alpha_mask = alpha_mask >> (64 - width * 4);
|
||||
__m512i a0 = _mm512_slli_epi32(
|
||||
_mm512_maskz_cvtepu8_epi32(mask, _mm_maskz_loadu_epi8(mask, src)), 24);
|
||||
a0 = _mm512_ternarylogic_epi32(a0, rgb_mask,
|
||||
_mm512_maskz_loadu_epi32(mask, dst), 0xf8);
|
||||
_mm512_mask_storeu_epi32(dst, mask, a0);
|
||||
_mm512_mask_storeu_epi8(dst, tail_alpha_mask, a0);
|
||||
}
|
||||
_mm256_zeroupper();
|
||||
}
|
||||
|
||||
@ -1456,7 +1456,10 @@ void ScaleAddCols1_AVX512BW(int dst_width,
|
||||
__m512i sum = _mm512_add_epi32(sum01, sum23);
|
||||
sum = _mm512_srli_epi32(_mm512_mullo_epi32(sum, vscale), 16);
|
||||
__mmask16 km = (__mmask16)((1U << dst_width) - 1);
|
||||
_mm512_mask_cvtepi32_storeu_epi8(dst_ptr, km, sum);
|
||||
// Use _mm_mask_storeu_epi8 instead of _mm512_mask_cvtepi32_storeu_epi8
|
||||
// because LLVM MSan does not instrument @llvm.x86.avx512.mask.pmov.db.mem
|
||||
// to unpoison destination shadow memory.
|
||||
_mm_mask_storeu_epi8(dst_ptr, km, _mm512_cvtepi32_epi8(sum));
|
||||
}
|
||||
} else if (boxwidth <= 8) {
|
||||
__m512i seq =
|
||||
@ -1507,7 +1510,10 @@ void ScaleAddCols1_AVX512BW(int dst_width,
|
||||
sum = _mm512_add_epi32(sum, _mm512_srli_epi64(sum, 32));
|
||||
sum = _mm512_srli_epi32(_mm512_mullo_epi32(sum, vscale), 16);
|
||||
__mmask8 km = (__mmask8)((1U << dst_width) - 1);
|
||||
_mm512_mask_cvtepi64_storeu_epi8(dst_ptr, km, sum);
|
||||
// Use _mm_mask_storeu_epi8 instead of _mm512_mask_cvtepi64_storeu_epi8
|
||||
// because LLVM MSan does not instrument @llvm.x86.avx512.mask.pmov.qb.mem
|
||||
// to unpoison destination shadow memory.
|
||||
_mm_mask_storeu_epi8(dst_ptr, km, _mm512_cvtepi64_epi8(sum));
|
||||
}
|
||||
} else {
|
||||
int rem = boxwidth & 31;
|
||||
@ -1622,7 +1628,10 @@ void ScaleAddCols2_AVX512BW(int dst_width,
|
||||
__m512i vsc = _mm512_mask_blend_epi32(k4, vscale0, vscale1);
|
||||
sum = _mm512_srli_epi32(_mm512_mullo_epi32(sum, vsc), 16);
|
||||
__mmask16 km = (__mmask16)((1U << dst_width) - 1);
|
||||
_mm512_mask_cvtepi32_storeu_epi8(dst_ptr, km, sum);
|
||||
// Use _mm_mask_storeu_epi8 instead of _mm512_mask_cvtepi32_storeu_epi8
|
||||
// because LLVM MSan does not instrument @llvm.x86.avx512.mask.pmov.db.mem
|
||||
// to unpoison destination shadow memory.
|
||||
_mm_mask_storeu_epi8(dst_ptr, km, _mm512_cvtepi32_epi8(sum));
|
||||
}
|
||||
} else if (minboxwidth >= 4 && minboxwidth <= 7) {
|
||||
__m512i seq =
|
||||
@ -1698,7 +1707,10 @@ void ScaleAddCols2_AVX512BW(int dst_width,
|
||||
__m512i vsc = _mm512_mask_blend_epi64(k4, vscale0, vscale1);
|
||||
sum = _mm512_srli_epi32(_mm512_mullo_epi32(sum, vsc), 16);
|
||||
__mmask8 km = (__mmask8)((1U << dst_width) - 1);
|
||||
_mm512_mask_cvtepi64_storeu_epi8(dst_ptr, km, sum);
|
||||
// Use _mm_mask_storeu_epi8 instead of _mm512_mask_cvtepi64_storeu_epi8
|
||||
// because LLVM MSan does not instrument @llvm.x86.avx512.mask.pmov.qb.mem
|
||||
// to unpoison destination shadow memory.
|
||||
_mm_mask_storeu_epi8(dst_ptr, km, _mm512_cvtepi64_epi8(sum));
|
||||
}
|
||||
} else {
|
||||
while (dst_width > 0) {
|
||||
|
||||
@ -29,6 +29,16 @@
|
||||
#include "libyuv/row.h" /* For ScaleSumSamples_Neon */
|
||||
#endif
|
||||
|
||||
#if defined(__has_feature)
|
||||
#if __has_feature(memory_sanitizer)
|
||||
#include <sanitizer/msan_interface.h>
|
||||
#define MSAN_POISON(ptr, size) __msan_allocated_memory(ptr, size)
|
||||
#endif
|
||||
#endif
|
||||
#ifndef MSAN_POISON
|
||||
#define MSAN_POISON(ptr, size)
|
||||
#endif
|
||||
|
||||
#define EXPECTED_UNATTENUATE_DIFF 0
|
||||
|
||||
namespace libyuv {
|
||||
@ -2798,111 +2808,244 @@ TEST_F(LibYUVPlanarTest, TestARGBLumaColorTable) {
|
||||
free_aligned_buffer_page_end(lumacolortable);
|
||||
}
|
||||
|
||||
TEST_F(LibYUVPlanarTest, TestARGBCopyAlpha) {
|
||||
const int kSize = benchmark_width_ * benchmark_height_ * 4;
|
||||
align_buffer_page_end(orig_pixels, kSize);
|
||||
static int TestARGBCopyAlpha(int width,
|
||||
int height,
|
||||
int benchmark_iterations,
|
||||
int disable_cpu_flags,
|
||||
int benchmark_cpu_info,
|
||||
int invert,
|
||||
int off) {
|
||||
if (width < 1) {
|
||||
width = 1;
|
||||
}
|
||||
const int kStride = width * 4;
|
||||
const int kSize = kStride * height;
|
||||
align_buffer_page_end(orig_pixels, kSize + off);
|
||||
align_buffer_page_end(dst_pixels_opt, kSize);
|
||||
align_buffer_page_end(dst_pixels_c, kSize);
|
||||
|
||||
MemRandomize(orig_pixels, kSize);
|
||||
MemRandomize(dst_pixels_opt, kSize);
|
||||
memcpy(dst_pixels_c, dst_pixels_opt, kSize);
|
||||
|
||||
MaskCpuFlags(disable_cpu_flags_);
|
||||
ARGBCopyAlpha(orig_pixels, benchmark_width_ * 4, dst_pixels_c,
|
||||
benchmark_width_ * 4, benchmark_width_, benchmark_height_);
|
||||
MaskCpuFlags(benchmark_cpu_info_);
|
||||
|
||||
for (int i = 0; i < benchmark_iterations_; ++i) {
|
||||
ARGBCopyAlpha(orig_pixels, benchmark_width_ * 4, dst_pixels_opt,
|
||||
benchmark_width_ * 4, benchmark_width_, benchmark_height_);
|
||||
MemRandomize(orig_pixels + off, kSize);
|
||||
MemRandomize(dst_pixels_c, kSize);
|
||||
// Copy RGB bytes only, leaving alpha bytes in dst_pixels_opt uninitialized.
|
||||
MSAN_POISON(dst_pixels_opt, kSize);
|
||||
for (int i = 0; i < width * height; ++i) {
|
||||
dst_pixels_opt[i * 4 + 0] = dst_pixels_c[i * 4 + 0];
|
||||
dst_pixels_opt[i * 4 + 1] = dst_pixels_c[i * 4 + 1];
|
||||
dst_pixels_opt[i * 4 + 2] = dst_pixels_c[i * 4 + 2];
|
||||
}
|
||||
|
||||
MaskCpuFlags(disable_cpu_flags);
|
||||
ARGBCopyAlpha(orig_pixels + off, kStride, dst_pixels_c, kStride, width,
|
||||
invert * height);
|
||||
MaskCpuFlags(benchmark_cpu_info);
|
||||
|
||||
for (int i = 0; i < benchmark_iterations; ++i) {
|
||||
ARGBCopyAlpha(orig_pixels + off, kStride, dst_pixels_opt, kStride, width,
|
||||
invert * height);
|
||||
}
|
||||
int max_diff = 0;
|
||||
for (int i = 0; i < kSize; ++i) {
|
||||
ASSERT_EQ(dst_pixels_c[i], dst_pixels_opt[i]);
|
||||
int abs_diff = abs(static_cast<int>(dst_pixels_c[i]) -
|
||||
static_cast<int>(dst_pixels_opt[i]));
|
||||
if (abs_diff > max_diff) {
|
||||
max_diff = abs_diff;
|
||||
}
|
||||
}
|
||||
|
||||
free_aligned_buffer_page_end(dst_pixels_c);
|
||||
free_aligned_buffer_page_end(dst_pixels_opt);
|
||||
free_aligned_buffer_page_end(orig_pixels);
|
||||
return max_diff;
|
||||
}
|
||||
|
||||
TEST_F(LibYUVPlanarTest, TestARGBExtractAlpha) {
|
||||
const int kPixels = benchmark_width_ * benchmark_height_;
|
||||
align_buffer_page_end(src_pixels, kPixels * 4);
|
||||
TEST_F(LibYUVPlanarTest, ARGBCopyAlpha_Any) {
|
||||
int max_diff = TestARGBCopyAlpha(benchmark_width_ - 1, benchmark_height_,
|
||||
benchmark_iterations_, disable_cpu_flags_,
|
||||
benchmark_cpu_info_, +1, 0);
|
||||
ASSERT_EQ(0, max_diff);
|
||||
}
|
||||
|
||||
TEST_F(LibYUVPlanarTest, ARGBCopyAlpha_Unaligned) {
|
||||
int max_diff = TestARGBCopyAlpha(benchmark_width_, benchmark_height_,
|
||||
benchmark_iterations_, disable_cpu_flags_,
|
||||
benchmark_cpu_info_, +1, 1);
|
||||
ASSERT_EQ(0, max_diff);
|
||||
}
|
||||
|
||||
TEST_F(LibYUVPlanarTest, ARGBCopyAlpha_Invert) {
|
||||
int max_diff = TestARGBCopyAlpha(benchmark_width_, benchmark_height_,
|
||||
benchmark_iterations_, disable_cpu_flags_,
|
||||
benchmark_cpu_info_, -1, 0);
|
||||
ASSERT_EQ(0, max_diff);
|
||||
}
|
||||
|
||||
TEST_F(LibYUVPlanarTest, ARGBCopyAlpha_Opt) {
|
||||
int max_diff = TestARGBCopyAlpha(benchmark_width_, benchmark_height_,
|
||||
benchmark_iterations_, disable_cpu_flags_,
|
||||
benchmark_cpu_info_, +1, 0);
|
||||
ASSERT_EQ(0, max_diff);
|
||||
}
|
||||
|
||||
static int TestARGBExtractAlpha(int width,
|
||||
int height,
|
||||
int benchmark_iterations,
|
||||
int disable_cpu_flags,
|
||||
int benchmark_cpu_info,
|
||||
int invert,
|
||||
int off) {
|
||||
if (width < 1) {
|
||||
width = 1;
|
||||
}
|
||||
const int kPixels = width * height;
|
||||
align_buffer_page_end(src_pixels, kPixels * 4 + off);
|
||||
align_buffer_page_end(dst_pixels_opt, kPixels);
|
||||
align_buffer_page_end(dst_pixels_c, kPixels);
|
||||
|
||||
MemRandomize(src_pixels, kPixels * 4);
|
||||
MemRandomize(dst_pixels_opt, kPixels);
|
||||
memcpy(dst_pixels_c, dst_pixels_opt, kPixels);
|
||||
MemRandomize(src_pixels + off, kPixels * 4);
|
||||
// Leave dst_pixels_opt uninitialized to verify MSan unpoisons all written
|
||||
// alpha bytes (including masked tail stores).
|
||||
MSAN_POISON(dst_pixels_opt, kPixels);
|
||||
|
||||
MaskCpuFlags(disable_cpu_flags_);
|
||||
ARGBExtractAlpha(src_pixels, benchmark_width_ * 4, dst_pixels_c,
|
||||
benchmark_width_, benchmark_width_, benchmark_height_);
|
||||
double c_time = get_time();
|
||||
ARGBExtractAlpha(src_pixels, benchmark_width_ * 4, dst_pixels_c,
|
||||
benchmark_width_, benchmark_width_, benchmark_height_);
|
||||
c_time = (get_time() - c_time);
|
||||
MaskCpuFlags(disable_cpu_flags);
|
||||
ARGBExtractAlpha(src_pixels + off, width * 4, dst_pixels_c, width, width,
|
||||
invert * height);
|
||||
|
||||
MaskCpuFlags(benchmark_cpu_info_);
|
||||
ARGBExtractAlpha(src_pixels, benchmark_width_ * 4, dst_pixels_opt,
|
||||
benchmark_width_, benchmark_width_, benchmark_height_);
|
||||
double opt_time = get_time();
|
||||
for (int i = 0; i < benchmark_iterations_; ++i) {
|
||||
ARGBExtractAlpha(src_pixels, benchmark_width_ * 4, dst_pixels_opt,
|
||||
benchmark_width_, benchmark_width_, benchmark_height_);
|
||||
MaskCpuFlags(benchmark_cpu_info);
|
||||
for (int i = 0; i < benchmark_iterations; ++i) {
|
||||
ARGBExtractAlpha(src_pixels + off, width * 4, dst_pixels_opt, width, width,
|
||||
invert * height);
|
||||
}
|
||||
opt_time = (get_time() - opt_time) / benchmark_iterations_;
|
||||
// Report performance of C vs OPT
|
||||
printf("%8d us C - %8d us OPT\n", static_cast<int>(c_time * 1e6),
|
||||
static_cast<int>(opt_time * 1e6));
|
||||
int max_diff = 0;
|
||||
for (int i = 0; i < kPixels; ++i) {
|
||||
ASSERT_EQ(dst_pixels_c[i], dst_pixels_opt[i]);
|
||||
int abs_diff = abs(static_cast<int>(dst_pixels_c[i]) -
|
||||
static_cast<int>(dst_pixels_opt[i]));
|
||||
if (abs_diff > max_diff) {
|
||||
max_diff = abs_diff;
|
||||
}
|
||||
}
|
||||
|
||||
free_aligned_buffer_page_end(dst_pixels_c);
|
||||
free_aligned_buffer_page_end(dst_pixels_opt);
|
||||
free_aligned_buffer_page_end(src_pixels);
|
||||
return max_diff;
|
||||
}
|
||||
|
||||
TEST_F(LibYUVPlanarTest, TestARGBCopyYToAlpha) {
|
||||
const int kPixels = benchmark_width_ * benchmark_height_;
|
||||
align_buffer_page_end(orig_pixels, kPixels);
|
||||
TEST_F(LibYUVPlanarTest, ARGBExtractAlpha_Any) {
|
||||
int max_diff = TestARGBExtractAlpha(benchmark_width_ - 1, benchmark_height_,
|
||||
benchmark_iterations_, disable_cpu_flags_,
|
||||
benchmark_cpu_info_, +1, 0);
|
||||
ASSERT_EQ(0, max_diff);
|
||||
}
|
||||
|
||||
TEST_F(LibYUVPlanarTest, ARGBExtractAlpha_Unaligned) {
|
||||
int max_diff = TestARGBExtractAlpha(benchmark_width_, benchmark_height_,
|
||||
benchmark_iterations_, disable_cpu_flags_,
|
||||
benchmark_cpu_info_, +1, 1);
|
||||
ASSERT_EQ(0, max_diff);
|
||||
}
|
||||
|
||||
TEST_F(LibYUVPlanarTest, ARGBExtractAlpha_Invert) {
|
||||
int max_diff = TestARGBExtractAlpha(benchmark_width_, benchmark_height_,
|
||||
benchmark_iterations_, disable_cpu_flags_,
|
||||
benchmark_cpu_info_, -1, 0);
|
||||
ASSERT_EQ(0, max_diff);
|
||||
}
|
||||
|
||||
TEST_F(LibYUVPlanarTest, ARGBExtractAlpha_Opt) {
|
||||
int max_diff = TestARGBExtractAlpha(benchmark_width_, benchmark_height_,
|
||||
benchmark_iterations_, disable_cpu_flags_,
|
||||
benchmark_cpu_info_, +1, 0);
|
||||
ASSERT_EQ(0, max_diff);
|
||||
}
|
||||
|
||||
static int TestARGBCopyYToAlpha(int width,
|
||||
int height,
|
||||
int benchmark_iterations,
|
||||
int disable_cpu_flags,
|
||||
int benchmark_cpu_info,
|
||||
int invert,
|
||||
int off) {
|
||||
if (width < 1) {
|
||||
width = 1;
|
||||
}
|
||||
const int kPixels = width * height;
|
||||
align_buffer_page_end(orig_pixels, kPixels + off);
|
||||
align_buffer_page_end(dst_pixels_opt, kPixels * 4);
|
||||
align_buffer_page_end(dst_pixels_c, kPixels * 4);
|
||||
align_buffer_page_end(dst_uninit_opt, kPixels * 4);
|
||||
|
||||
MemRandomize(orig_pixels, kPixels);
|
||||
MemRandomize(dst_pixels_opt, kPixels * 4);
|
||||
memcpy(dst_pixels_c, dst_pixels_opt, kPixels * 4);
|
||||
|
||||
MaskCpuFlags(disable_cpu_flags_);
|
||||
ARGBCopyYToAlpha(orig_pixels, benchmark_width_, dst_pixels_c,
|
||||
benchmark_width_ * 4, benchmark_width_, benchmark_height_);
|
||||
double c_time = get_time();
|
||||
ARGBCopyYToAlpha(orig_pixels, benchmark_width_, dst_pixels_c,
|
||||
benchmark_width_ * 4, benchmark_width_, benchmark_height_);
|
||||
c_time = (get_time() - c_time);
|
||||
|
||||
MaskCpuFlags(benchmark_cpu_info_);
|
||||
ARGBCopyYToAlpha(orig_pixels, benchmark_width_, dst_pixels_opt,
|
||||
benchmark_width_ * 4, benchmark_width_, benchmark_height_);
|
||||
double opt_time = get_time();
|
||||
for (int i = 0; i < benchmark_iterations_; ++i) {
|
||||
ARGBCopyYToAlpha(orig_pixels, benchmark_width_, dst_pixels_opt,
|
||||
benchmark_width_ * 4, benchmark_width_, benchmark_height_);
|
||||
MemRandomize(orig_pixels + off, kPixels);
|
||||
MemRandomize(dst_pixels_c, kPixels * 4);
|
||||
// Copy RGB bytes only, leaving alpha bytes in dst_pixels_opt uninitialized.
|
||||
MSAN_POISON(dst_pixels_opt, kPixels * 4);
|
||||
for (int i = 0; i < kPixels; ++i) {
|
||||
dst_pixels_opt[i * 4 + 0] = dst_pixels_c[i * 4 + 0];
|
||||
dst_pixels_opt[i * 4 + 1] = dst_pixels_c[i * 4 + 1];
|
||||
dst_pixels_opt[i * 4 + 2] = dst_pixels_c[i * 4 + 2];
|
||||
}
|
||||
opt_time = (get_time() - opt_time) / benchmark_iterations_;
|
||||
// Also test with completely uninitialized destination buffer for MSan.
|
||||
MSAN_POISON(dst_uninit_opt, kPixels * 4);
|
||||
|
||||
// Report performance of C vs OPT
|
||||
printf("%8d us C - %8d us OPT\n", static_cast<int>(c_time * 1e6),
|
||||
static_cast<int>(opt_time * 1e6));
|
||||
MaskCpuFlags(disable_cpu_flags);
|
||||
ARGBCopyYToAlpha(orig_pixels + off, width, dst_pixels_c, width * 4, width,
|
||||
invert * height);
|
||||
|
||||
MaskCpuFlags(benchmark_cpu_info);
|
||||
ARGBCopyYToAlpha(orig_pixels + off, width, dst_uninit_opt, width * 4, width,
|
||||
invert * height);
|
||||
for (int i = 0; i < benchmark_iterations; ++i) {
|
||||
ARGBCopyYToAlpha(orig_pixels + off, width, dst_pixels_opt, width * 4, width,
|
||||
invert * height);
|
||||
}
|
||||
int max_diff = 0;
|
||||
for (int i = 0; i < kPixels * 4; ++i) {
|
||||
ASSERT_EQ(dst_pixels_c[i], dst_pixels_opt[i]);
|
||||
int abs_diff = abs(static_cast<int>(dst_pixels_c[i]) -
|
||||
static_cast<int>(dst_pixels_opt[i]));
|
||||
if (abs_diff > max_diff) {
|
||||
max_diff = abs_diff;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < kPixels; ++i) {
|
||||
int abs_diff = abs(static_cast<int>(dst_pixels_c[i * 4 + 3]) -
|
||||
static_cast<int>(dst_uninit_opt[i * 4 + 3]));
|
||||
if (abs_diff > max_diff) {
|
||||
max_diff = abs_diff;
|
||||
}
|
||||
}
|
||||
|
||||
free_aligned_buffer_page_end(dst_uninit_opt);
|
||||
free_aligned_buffer_page_end(dst_pixels_c);
|
||||
free_aligned_buffer_page_end(dst_pixels_opt);
|
||||
free_aligned_buffer_page_end(orig_pixels);
|
||||
return max_diff;
|
||||
}
|
||||
|
||||
TEST_F(LibYUVPlanarTest, ARGBCopyYToAlpha_Any) {
|
||||
int max_diff = TestARGBCopyYToAlpha(benchmark_width_ - 1, benchmark_height_,
|
||||
benchmark_iterations_, disable_cpu_flags_,
|
||||
benchmark_cpu_info_, +1, 0);
|
||||
ASSERT_EQ(0, max_diff);
|
||||
}
|
||||
|
||||
TEST_F(LibYUVPlanarTest, ARGBCopyYToAlpha_Unaligned) {
|
||||
int max_diff = TestARGBCopyYToAlpha(benchmark_width_, benchmark_height_,
|
||||
benchmark_iterations_, disable_cpu_flags_,
|
||||
benchmark_cpu_info_, +1, 1);
|
||||
ASSERT_EQ(0, max_diff);
|
||||
}
|
||||
|
||||
TEST_F(LibYUVPlanarTest, ARGBCopyYToAlpha_Invert) {
|
||||
int max_diff = TestARGBCopyYToAlpha(benchmark_width_, benchmark_height_,
|
||||
benchmark_iterations_, disable_cpu_flags_,
|
||||
benchmark_cpu_info_, -1, 0);
|
||||
ASSERT_EQ(0, max_diff);
|
||||
}
|
||||
|
||||
TEST_F(LibYUVPlanarTest, ARGBCopyYToAlpha_Opt) {
|
||||
int max_diff = TestARGBCopyYToAlpha(benchmark_width_, benchmark_height_,
|
||||
benchmark_iterations_, disable_cpu_flags_,
|
||||
benchmark_cpu_info_, +1, 0);
|
||||
ASSERT_EQ(0, max_diff);
|
||||
}
|
||||
|
||||
static int TestARGBRect(int width,
|
||||
|
||||
@ -25,6 +25,16 @@
|
||||
#include "libyuv/scale_row.h" // For ScaleRowDown2Box_Odd_C
|
||||
#endif
|
||||
|
||||
#if defined(__has_feature)
|
||||
#if __has_feature(memory_sanitizer)
|
||||
#include <sanitizer/msan_interface.h>
|
||||
#define MSAN_POISON(ptr, size) __msan_allocated_memory(ptr, size)
|
||||
#endif
|
||||
#endif
|
||||
#ifndef MSAN_POISON
|
||||
#define MSAN_POISON(ptr, size)
|
||||
#endif
|
||||
|
||||
#define STRINGIZE(line) #line
|
||||
#define FILELINESTR(file, line) file ":" STRINGIZE(line)
|
||||
|
||||
@ -517,6 +527,35 @@ TEST_F(LibYUVScaleTest, PlaneTestRotate_Box) {
|
||||
free_aligned_buffer_page_end(orig_pixels);
|
||||
}
|
||||
|
||||
TEST_F(LibYUVScaleTest, ScalePlaneBox_Any) {
|
||||
const int kDstW = 29;
|
||||
const int kDstH = 3;
|
||||
const int kSrcH = 9;
|
||||
for (int src_w : {87, 174, 73, 155}) {
|
||||
const int kSrcSize = src_w * kSrcH;
|
||||
const int kDstSize = kDstW * kDstH;
|
||||
align_buffer_page_end(src_pixels, kSrcSize);
|
||||
align_buffer_page_end(dst_c_pixels, kDstSize);
|
||||
align_buffer_page_end(dst_opt_pixels, kDstSize);
|
||||
MemRandomize(src_pixels, kSrcSize);
|
||||
MSAN_POISON(dst_opt_pixels, kDstSize);
|
||||
|
||||
MaskCpuFlags(disable_cpu_flags_);
|
||||
ScalePlane(src_pixels, src_w, src_w, kSrcH, dst_c_pixels, kDstW, kDstW,
|
||||
kDstH, kFilterBox);
|
||||
MaskCpuFlags(benchmark_cpu_info_);
|
||||
ScalePlane(src_pixels, src_w, src_w, kSrcH, dst_opt_pixels, kDstW, kDstW,
|
||||
kDstH, kFilterBox);
|
||||
|
||||
for (int i = 0; i < kDstSize; ++i) {
|
||||
ASSERT_EQ(dst_c_pixels[i], dst_opt_pixels[i]);
|
||||
}
|
||||
free_aligned_buffer_page_end(dst_opt_pixels);
|
||||
free_aligned_buffer_page_end(dst_c_pixels);
|
||||
free_aligned_buffer_page_end(src_pixels);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(LibYUVScaleTest, PlaneTest1_Box) {
|
||||
align_buffer_page_end(orig_pixels, 3);
|
||||
align_buffer_page_end(dst_pixels, 3);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user