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 Reviewed-on: https://chromium-review.googlesource.com/c/libyuv/libyuv/+/8672001 Commit-Queue: Frank Barchard <fbarchard@google.com> Reviewed-by: Dale Curtis <dalecurtis@chromium.org>
This commit is contained in:
parent
f506767c5f
commit
a84bf45a21
22
GEMINI.md
22
GEMINI.md
@ -80,6 +80,28 @@ and compiler compatibility.
|
|||||||
`"ymmN"` or `"zmmN"`, including for AVX2 and AVX512 code. Clang for
|
`"ymmN"` or `"zmmN"`, including for AVX2 and AVX512 code. Clang for
|
||||||
Windows must preserve xmm6-xmm15 and does not save them when they are
|
Windows must preserve xmm6-xmm15 and does not save them when they are
|
||||||
declared as ymm or zmm.
|
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
|
### 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) {
|
void ARGBCopyYToAlphaRow_AVX512BW(const uint8_t* src, uint8_t* dst, int width) {
|
||||||
uintptr_t temp;
|
uintptr_t temp;
|
||||||
asm volatile(
|
asm volatile(
|
||||||
"vpternlogd $0xff,%%zmm0,%%zmm0,%%zmm0 \n"
|
"kmovq %4,%%k1 \n"
|
||||||
"vpsrld $0x8,%%zmm0,%%zmm0 \n"
|
"sub $0x20,%2 \n"
|
||||||
"sub $0x20,%2 \n"
|
"jl 2f \n"
|
||||||
"jl 2f \n"
|
|
||||||
|
|
||||||
LABELALIGN
|
LABELALIGN
|
||||||
"1: \n"
|
"1: \n"
|
||||||
"vpmovzxbd (%0),%%zmm1 \n"
|
"vpmovzxbd (%0),%%zmm1 \n"
|
||||||
"vpmovzxbd 0x10(%0),%%zmm2 \n"
|
"vpmovzxbd 0x10(%0),%%zmm2 \n"
|
||||||
"lea 0x20(%0),%0 \n"
|
"lea 0x20(%0),%0 \n"
|
||||||
"vpslld $0x18,%%zmm1,%%zmm1 \n"
|
"vpslld $0x18,%%zmm1,%%zmm1 \n"
|
||||||
"vpslld $0x18,%%zmm2,%%zmm2 \n"
|
"vpslld $0x18,%%zmm2,%%zmm2 \n"
|
||||||
"vpternlogd $0xf8,(%1),%%zmm0,%%zmm1 \n"
|
"vmovdqu8 %%zmm1,(%1)%{%%k1%} \n"
|
||||||
"vpternlogd $0xf8,0x40(%1),%%zmm0,%%zmm2 \n"
|
"vmovdqu8 %%zmm2,0x40(%1)%{%%k1%} \n"
|
||||||
"vmovdqu32 %%zmm1,(%1) \n"
|
"lea 0x80(%1),%1 \n"
|
||||||
"vmovdqu32 %%zmm2,0x40(%1) \n"
|
"sub $0x20,%2 \n"
|
||||||
"lea 0x80(%1),%1 \n"
|
"jge 1b \n"
|
||||||
"sub $0x20,%2 \n"
|
|
||||||
"jge 1b \n"
|
|
||||||
|
|
||||||
"2: \n"
|
"2: \n"
|
||||||
"add $0x20,%2 \n"
|
"add $0x20,%2 \n"
|
||||||
"jle 99f \n"
|
"jle 99f \n"
|
||||||
"cmp $0x10,%2 \n"
|
"cmp $0x10,%2 \n"
|
||||||
"jl 3f \n"
|
"jl 3f \n"
|
||||||
"vpmovzxbd (%0),%%zmm1 \n"
|
"vpmovzxbd (%0),%%zmm1 \n"
|
||||||
"lea 0x10(%0),%0 \n"
|
"lea 0x10(%0),%0 \n"
|
||||||
"vpslld $0x18,%%zmm1,%%zmm1 \n"
|
"vpslld $0x18,%%zmm1,%%zmm1 \n"
|
||||||
"vpternlogd $0xf8,(%1),%%zmm0,%%zmm1 \n"
|
"vmovdqu8 %%zmm1,(%1)%{%%k1%} \n"
|
||||||
"vmovdqu32 %%zmm1,(%1) \n"
|
"lea 0x40(%1),%1 \n"
|
||||||
"lea 0x40(%1),%1 \n"
|
"sub $0x10,%2 \n"
|
||||||
"sub $0x10,%2 \n"
|
"jz 99f \n"
|
||||||
"jz 99f \n"
|
|
||||||
|
|
||||||
"3: \n"
|
"3: \n"
|
||||||
"mov $-1,%k3 \n"
|
"mov $-1,%k3 \n"
|
||||||
"bzhi %k2,%k3,%k3 \n"
|
"bzhi %k2,%k3,%k3 \n"
|
||||||
"kmovw %k3,%%k1 \n"
|
"kmovw %k3,%%k1 \n"
|
||||||
"vpmovzxbd (%0),%%zmm1%{%%k1%}%{z%} \n"
|
"vpmovzxbd (%0),%%zmm1%{%%k1%}%{z%} \n"
|
||||||
"vpslld $0x18,%%zmm1,%%zmm1 \n"
|
"vpslld $0x18,%%zmm1,%%zmm1 \n"
|
||||||
"vpternlogd $0xf8,(%1),%%zmm0,%%zmm1%{%%k1%}%{z%} \n"
|
"shl $0x2,%k2 \n"
|
||||||
"vmovdqu32 %%zmm1,(%1)%{%%k1%} \n"
|
"bzhi %q2,%4,%3 \n"
|
||||||
|
"kmovq %3,%%k1 \n"
|
||||||
|
"vmovdqu8 %%zmm1,(%1)%{%%k1%} \n"
|
||||||
|
|
||||||
"99: \n"
|
"99: \n"
|
||||||
"vzeroupper \n"
|
"vzeroupper \n"
|
||||||
: "+r"(src), // %0
|
: "+r"(src), // %0
|
||||||
"+r"(dst), // %1
|
"+r"(dst), // %1
|
||||||
"+r"(width), // %2
|
"+r"(width), // %2
|
||||||
"=&r"(temp) // %3
|
"=&r"(temp) // %3
|
||||||
:
|
: "r"(0x8888888888888888ull) // %4
|
||||||
: "memory", "cc", "xmm0", "xmm1", "xmm2", "k1");
|
: "memory", "cc", "xmm1", "xmm2", "k1");
|
||||||
}
|
}
|
||||||
#endif // HAS_ARGBCOPYYTOALPHAROW_AVX512BW
|
#endif // HAS_ARGBCOPYYTOALPHAROW_AVX512BW
|
||||||
|
|
||||||
|
|||||||
@ -3266,7 +3266,11 @@ void ARGBExtractAlphaRow_AVX512BW(const uint8_t* src_argb,
|
|||||||
if (width > 0) {
|
if (width > 0) {
|
||||||
__mmask16 mask = (__mmask16)((1u << width) - 1u);
|
__mmask16 mask = (__mmask16)((1u << width) - 1u);
|
||||||
__m512i z0 = _mm512_maskz_loadu_epi32(mask, src_argb);
|
__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();
|
_mm256_zeroupper();
|
||||||
}
|
}
|
||||||
@ -3275,18 +3279,18 @@ void ARGBExtractAlphaRow_AVX512BW(const uint8_t* src_argb,
|
|||||||
#ifdef HAS_ARGBCOPYYTOALPHAROW_AVX512BW
|
#ifdef HAS_ARGBCOPYYTOALPHAROW_AVX512BW
|
||||||
LIBYUV_TARGET_AVX512BW
|
LIBYUV_TARGET_AVX512BW
|
||||||
void ARGBCopyYToAlphaRow_AVX512BW(const uint8_t* src, uint8_t* dst, int width) {
|
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) {
|
while (width >= 32) {
|
||||||
__m512i a0 = _mm512_slli_epi32(
|
__m512i a0 = _mm512_slli_epi32(
|
||||||
_mm512_cvtepu8_epi32(_mm_loadu_si128((const __m128i*)src)), 24);
|
_mm512_cvtepu8_epi32(_mm_loadu_si128((const __m128i*)src)), 24);
|
||||||
__m512i a1 = _mm512_slli_epi32(
|
__m512i a1 = _mm512_slli_epi32(
|
||||||
_mm512_cvtepu8_epi32(_mm_loadu_si128((const __m128i*)(src + 16))), 24);
|
_mm512_cvtepu8_epi32(_mm_loadu_si128((const __m128i*)(src + 16))), 24);
|
||||||
a0 = _mm512_ternarylogic_epi32(
|
_mm512_mask_storeu_epi8(dst, alpha_mask, a0);
|
||||||
a0, rgb_mask, _mm512_loadu_si512((const __m512i*)dst), 0xf8);
|
_mm512_mask_storeu_epi8(dst + 64, alpha_mask, a1);
|
||||||
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);
|
|
||||||
src += 32;
|
src += 32;
|
||||||
dst += 128;
|
dst += 128;
|
||||||
width -= 32;
|
width -= 32;
|
||||||
@ -3294,20 +3298,17 @@ void ARGBCopyYToAlphaRow_AVX512BW(const uint8_t* src, uint8_t* dst, int width) {
|
|||||||
if (width >= 16) {
|
if (width >= 16) {
|
||||||
__m512i a0 = _mm512_slli_epi32(
|
__m512i a0 = _mm512_slli_epi32(
|
||||||
_mm512_cvtepu8_epi32(_mm_loadu_si128((const __m128i*)src)), 24);
|
_mm512_cvtepu8_epi32(_mm_loadu_si128((const __m128i*)src)), 24);
|
||||||
a0 = _mm512_ternarylogic_epi32(
|
_mm512_mask_storeu_epi8(dst, alpha_mask, a0);
|
||||||
a0, rgb_mask, _mm512_loadu_si512((const __m512i*)dst), 0xf8);
|
|
||||||
_mm512_storeu_si512((__m512i*)dst, a0);
|
|
||||||
src += 16;
|
src += 16;
|
||||||
dst += 64;
|
dst += 64;
|
||||||
width -= 16;
|
width -= 16;
|
||||||
}
|
}
|
||||||
if (width > 0) {
|
if (width > 0) {
|
||||||
__mmask16 mask = (__mmask16)((1u << width) - 1u);
|
__mmask16 mask = (__mmask16)((1u << width) - 1u);
|
||||||
|
__mmask64 tail_alpha_mask = alpha_mask >> (64 - width * 4);
|
||||||
__m512i a0 = _mm512_slli_epi32(
|
__m512i a0 = _mm512_slli_epi32(
|
||||||
_mm512_maskz_cvtepu8_epi32(mask, _mm_maskz_loadu_epi8(mask, src)), 24);
|
_mm512_maskz_cvtepu8_epi32(mask, _mm_maskz_loadu_epi8(mask, src)), 24);
|
||||||
a0 = _mm512_ternarylogic_epi32(a0, rgb_mask,
|
_mm512_mask_storeu_epi8(dst, tail_alpha_mask, a0);
|
||||||
_mm512_maskz_loadu_epi32(mask, dst), 0xf8);
|
|
||||||
_mm512_mask_storeu_epi32(dst, mask, a0);
|
|
||||||
}
|
}
|
||||||
_mm256_zeroupper();
|
_mm256_zeroupper();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1456,7 +1456,10 @@ void ScaleAddCols1_AVX512BW(int dst_width,
|
|||||||
__m512i sum = _mm512_add_epi32(sum01, sum23);
|
__m512i sum = _mm512_add_epi32(sum01, sum23);
|
||||||
sum = _mm512_srli_epi32(_mm512_mullo_epi32(sum, vscale), 16);
|
sum = _mm512_srli_epi32(_mm512_mullo_epi32(sum, vscale), 16);
|
||||||
__mmask16 km = (__mmask16)((1U << dst_width) - 1);
|
__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) {
|
} else if (boxwidth <= 8) {
|
||||||
__m512i seq =
|
__m512i seq =
|
||||||
@ -1507,7 +1510,10 @@ void ScaleAddCols1_AVX512BW(int dst_width,
|
|||||||
sum = _mm512_add_epi32(sum, _mm512_srli_epi64(sum, 32));
|
sum = _mm512_add_epi32(sum, _mm512_srli_epi64(sum, 32));
|
||||||
sum = _mm512_srli_epi32(_mm512_mullo_epi32(sum, vscale), 16);
|
sum = _mm512_srli_epi32(_mm512_mullo_epi32(sum, vscale), 16);
|
||||||
__mmask8 km = (__mmask8)((1U << dst_width) - 1);
|
__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 {
|
} else {
|
||||||
int rem = boxwidth & 31;
|
int rem = boxwidth & 31;
|
||||||
@ -1622,7 +1628,10 @@ void ScaleAddCols2_AVX512BW(int dst_width,
|
|||||||
__m512i vsc = _mm512_mask_blend_epi32(k4, vscale0, vscale1);
|
__m512i vsc = _mm512_mask_blend_epi32(k4, vscale0, vscale1);
|
||||||
sum = _mm512_srli_epi32(_mm512_mullo_epi32(sum, vsc), 16);
|
sum = _mm512_srli_epi32(_mm512_mullo_epi32(sum, vsc), 16);
|
||||||
__mmask16 km = (__mmask16)((1U << dst_width) - 1);
|
__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) {
|
} else if (minboxwidth >= 4 && minboxwidth <= 7) {
|
||||||
__m512i seq =
|
__m512i seq =
|
||||||
@ -1698,7 +1707,10 @@ void ScaleAddCols2_AVX512BW(int dst_width,
|
|||||||
__m512i vsc = _mm512_mask_blend_epi64(k4, vscale0, vscale1);
|
__m512i vsc = _mm512_mask_blend_epi64(k4, vscale0, vscale1);
|
||||||
sum = _mm512_srli_epi32(_mm512_mullo_epi32(sum, vsc), 16);
|
sum = _mm512_srli_epi32(_mm512_mullo_epi32(sum, vsc), 16);
|
||||||
__mmask8 km = (__mmask8)((1U << dst_width) - 1);
|
__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 {
|
} else {
|
||||||
while (dst_width > 0) {
|
while (dst_width > 0) {
|
||||||
|
|||||||
@ -29,6 +29,16 @@
|
|||||||
#include "libyuv/row.h" /* For ScaleSumSamples_Neon */
|
#include "libyuv/row.h" /* For ScaleSumSamples_Neon */
|
||||||
#endif
|
#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
|
#define EXPECTED_UNATTENUATE_DIFF 0
|
||||||
|
|
||||||
namespace libyuv {
|
namespace libyuv {
|
||||||
@ -2798,111 +2808,244 @@ TEST_F(LibYUVPlanarTest, TestARGBLumaColorTable) {
|
|||||||
free_aligned_buffer_page_end(lumacolortable);
|
free_aligned_buffer_page_end(lumacolortable);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(LibYUVPlanarTest, TestARGBCopyAlpha) {
|
static int TestARGBCopyAlpha(int width,
|
||||||
const int kSize = benchmark_width_ * benchmark_height_ * 4;
|
int height,
|
||||||
align_buffer_page_end(orig_pixels, kSize);
|
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_opt, kSize);
|
||||||
align_buffer_page_end(dst_pixels_c, kSize);
|
align_buffer_page_end(dst_pixels_c, kSize);
|
||||||
|
|
||||||
MemRandomize(orig_pixels, kSize);
|
MemRandomize(orig_pixels + off, kSize);
|
||||||
MemRandomize(dst_pixels_opt, kSize);
|
MemRandomize(dst_pixels_c, kSize);
|
||||||
memcpy(dst_pixels_c, dst_pixels_opt, kSize);
|
// Copy RGB bytes only, leaving alpha bytes in dst_pixels_opt uninitialized.
|
||||||
|
MSAN_POISON(dst_pixels_opt, kSize);
|
||||||
MaskCpuFlags(disable_cpu_flags_);
|
for (int i = 0; i < width * height; ++i) {
|
||||||
ARGBCopyAlpha(orig_pixels, benchmark_width_ * 4, dst_pixels_c,
|
dst_pixels_opt[i * 4 + 0] = dst_pixels_c[i * 4 + 0];
|
||||||
benchmark_width_ * 4, benchmark_width_, benchmark_height_);
|
dst_pixels_opt[i * 4 + 1] = dst_pixels_c[i * 4 + 1];
|
||||||
MaskCpuFlags(benchmark_cpu_info_);
|
dst_pixels_opt[i * 4 + 2] = dst_pixels_c[i * 4 + 2];
|
||||||
|
|
||||||
for (int i = 0; i < benchmark_iterations_; ++i) {
|
|
||||||
ARGBCopyAlpha(orig_pixels, benchmark_width_ * 4, dst_pixels_opt,
|
|
||||||
benchmark_width_ * 4, benchmark_width_, benchmark_height_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
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_c);
|
||||||
free_aligned_buffer_page_end(dst_pixels_opt);
|
free_aligned_buffer_page_end(dst_pixels_opt);
|
||||||
free_aligned_buffer_page_end(orig_pixels);
|
free_aligned_buffer_page_end(orig_pixels);
|
||||||
|
return max_diff;
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(LibYUVPlanarTest, TestARGBExtractAlpha) {
|
TEST_F(LibYUVPlanarTest, ARGBCopyAlpha_Any) {
|
||||||
const int kPixels = benchmark_width_ * benchmark_height_;
|
int max_diff = TestARGBCopyAlpha(benchmark_width_ - 1, benchmark_height_,
|
||||||
align_buffer_page_end(src_pixels, kPixels * 4);
|
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_opt, kPixels);
|
||||||
align_buffer_page_end(dst_pixels_c, kPixels);
|
align_buffer_page_end(dst_pixels_c, kPixels);
|
||||||
|
|
||||||
MemRandomize(src_pixels, kPixels * 4);
|
MemRandomize(src_pixels + off, kPixels * 4);
|
||||||
MemRandomize(dst_pixels_opt, kPixels);
|
// Leave dst_pixels_opt uninitialized to verify MSan unpoisons all written
|
||||||
memcpy(dst_pixels_c, dst_pixels_opt, kPixels);
|
// alpha bytes (including masked tail stores).
|
||||||
|
MSAN_POISON(dst_pixels_opt, kPixels);
|
||||||
|
|
||||||
MaskCpuFlags(disable_cpu_flags_);
|
MaskCpuFlags(disable_cpu_flags);
|
||||||
ARGBExtractAlpha(src_pixels, benchmark_width_ * 4, dst_pixels_c,
|
ARGBExtractAlpha(src_pixels + off, width * 4, dst_pixels_c, width, width,
|
||||||
benchmark_width_, benchmark_width_, benchmark_height_);
|
invert * 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(benchmark_cpu_info_);
|
MaskCpuFlags(benchmark_cpu_info);
|
||||||
ARGBExtractAlpha(src_pixels, benchmark_width_ * 4, dst_pixels_opt,
|
for (int i = 0; i < benchmark_iterations; ++i) {
|
||||||
benchmark_width_, benchmark_width_, benchmark_height_);
|
ARGBExtractAlpha(src_pixels + off, width * 4, dst_pixels_opt, width, width,
|
||||||
double opt_time = get_time();
|
invert * height);
|
||||||
for (int i = 0; i < benchmark_iterations_; ++i) {
|
|
||||||
ARGBExtractAlpha(src_pixels, benchmark_width_ * 4, dst_pixels_opt,
|
|
||||||
benchmark_width_, benchmark_width_, benchmark_height_);
|
|
||||||
}
|
}
|
||||||
opt_time = (get_time() - opt_time) / benchmark_iterations_;
|
int max_diff = 0;
|
||||||
// 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));
|
|
||||||
for (int i = 0; i < kPixels; ++i) {
|
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_c);
|
||||||
free_aligned_buffer_page_end(dst_pixels_opt);
|
free_aligned_buffer_page_end(dst_pixels_opt);
|
||||||
free_aligned_buffer_page_end(src_pixels);
|
free_aligned_buffer_page_end(src_pixels);
|
||||||
|
return max_diff;
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(LibYUVPlanarTest, TestARGBCopyYToAlpha) {
|
TEST_F(LibYUVPlanarTest, ARGBExtractAlpha_Any) {
|
||||||
const int kPixels = benchmark_width_ * benchmark_height_;
|
int max_diff = TestARGBExtractAlpha(benchmark_width_ - 1, benchmark_height_,
|
||||||
align_buffer_page_end(orig_pixels, kPixels);
|
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_opt, kPixels * 4);
|
||||||
align_buffer_page_end(dst_pixels_c, 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(orig_pixels + off, kPixels);
|
||||||
MemRandomize(dst_pixels_opt, kPixels * 4);
|
MemRandomize(dst_pixels_c, kPixels * 4);
|
||||||
memcpy(dst_pixels_c, dst_pixels_opt, kPixels * 4);
|
// Copy RGB bytes only, leaving alpha bytes in dst_pixels_opt uninitialized.
|
||||||
|
MSAN_POISON(dst_pixels_opt, kPixels * 4);
|
||||||
MaskCpuFlags(disable_cpu_flags_);
|
for (int i = 0; i < kPixels; ++i) {
|
||||||
ARGBCopyYToAlpha(orig_pixels, benchmark_width_, dst_pixels_c,
|
dst_pixels_opt[i * 4 + 0] = dst_pixels_c[i * 4 + 0];
|
||||||
benchmark_width_ * 4, benchmark_width_, benchmark_height_);
|
dst_pixels_opt[i * 4 + 1] = dst_pixels_c[i * 4 + 1];
|
||||||
double c_time = get_time();
|
dst_pixels_opt[i * 4 + 2] = dst_pixels_c[i * 4 + 2];
|
||||||
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_);
|
|
||||||
}
|
}
|
||||||
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
|
MaskCpuFlags(disable_cpu_flags);
|
||||||
printf("%8d us C - %8d us OPT\n", static_cast<int>(c_time * 1e6),
|
ARGBCopyYToAlpha(orig_pixels + off, width, dst_pixels_c, width * 4, width,
|
||||||
static_cast<int>(opt_time * 1e6));
|
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) {
|
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_c);
|
||||||
free_aligned_buffer_page_end(dst_pixels_opt);
|
free_aligned_buffer_page_end(dst_pixels_opt);
|
||||||
free_aligned_buffer_page_end(orig_pixels);
|
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,
|
static int TestARGBRect(int width,
|
||||||
|
|||||||
@ -25,6 +25,16 @@
|
|||||||
#include "libyuv/scale_row.h" // For ScaleRowDown2Box_Odd_C
|
#include "libyuv/scale_row.h" // For ScaleRowDown2Box_Odd_C
|
||||||
#endif
|
#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 STRINGIZE(line) #line
|
||||||
#define FILELINESTR(file, line) file ":" STRINGIZE(line)
|
#define FILELINESTR(file, line) file ":" STRINGIZE(line)
|
||||||
|
|
||||||
@ -517,6 +527,35 @@ TEST_F(LibYUVScaleTest, PlaneTestRotate_Box) {
|
|||||||
free_aligned_buffer_page_end(orig_pixels);
|
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) {
|
TEST_F(LibYUVScaleTest, PlaneTest1_Box) {
|
||||||
align_buffer_page_end(orig_pixels, 3);
|
align_buffer_page_end(orig_pixels, 3);
|
||||||
align_buffer_page_end(dst_pixels, 3);
|
align_buffer_page_end(dst_pixels, 3);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user