| /* | |
| * Copyright 2006 The Android Open Source Project | |
| * | |
| * Use of this source code is governed by a BSD-style license that can be | |
| * found in the LICENSE file. | |
| */ | |
| #ifndef SkUtils_DEFINED | |
| #define SkUtils_DEFINED | |
| #include "include/private/SkAttributes.h" | |
| #include <cstring> | |
| #include <type_traits> // is_trivially_copyable | |
| namespace SkHexadecimalDigits { | |
| extern const char gUpper[16]; // 0-9A-F | |
| extern const char gLower[16]; // 0-9a-f | |
| } // namespace SkHexadecimalDigits | |
| /////////////////////////////////////////////////////////////////////////////// | |
| // If T is an 8-byte GCC or Clang vector extension type, it would naturally pass or return in the | |
| // MMX mm0 register on 32-bit x86 builds. This has the fun side effect of clobbering any state in | |
| // the x87 st0 register. (There is no ABI governing who should preserve mm?/st? registers, so no | |
| // one does!) | |
| // | |
| // We force-inline sk_unaligned_load() and sk_unaligned_store() to avoid that, making them safe to | |
| // use for all types on all platforms, thus solving the problem once and for all! | |
| // A separate problem exists with 32-bit x86. The default calling convention returns values in | |
| // ST0 (the x87 FPU). Unfortunately, doing so can mutate some bit patterns (signaling NaNs | |
| // become quiet). If you're using these functions to pass data around as floats, but it's actually | |
| // integers, that can be bad -- raster pipeline does this. | |
| // | |
| // With GCC and Clang, the always_inline attribute ensures we don't have a problem. MSVC, though, | |
| // ignores __forceinline in debug builds, so the return-via-ST0 is always present. Switching to | |
| // __vectorcall changes the functions to return in xmm0. | |
| #if defined(_MSC_VER) && defined(_M_IX86) | |
| #define SK_FP_SAFE_ABI __vectorcall | |
| #else | |
| #define SK_FP_SAFE_ABI | |
| #endif | |
| template <typename T, typename P> | |
| static SK_ALWAYS_INLINE T SK_FP_SAFE_ABI sk_unaligned_load(const P* ptr) { | |
| static_assert(std::is_trivially_copyable_v<P> || std::is_void_v<P>); | |
| static_assert(std::is_trivially_copyable_v<T>); | |
| T val; | |
| // gcc's class-memaccess sometimes triggers when: | |
| // - `T` is trivially copyable but | |
| // - `T` is non-trivial (e.g. at least one eligible default constructor is | |
| // non-trivial). | |
| // Use `reinterpret_cast<const void*>` to explicit suppress this warning; a | |
| // trivially copyable type is safe to memcpy from/to. | |
| memcpy(&val, static_cast<const void*>(ptr), sizeof(val)); | |
| return val; | |
| } | |
| template <typename T, typename P> | |
| static SK_ALWAYS_INLINE void SK_FP_SAFE_ABI sk_unaligned_store(P* ptr, T val) { | |
| static_assert(std::is_trivially_copyable<T>::value); | |
| memcpy(ptr, &val, sizeof(val)); | |
| } | |
| // Copy the bytes from src into an instance of type Dst and return it. | |
| template <typename Dst, typename Src> | |
| static SK_ALWAYS_INLINE Dst SK_FP_SAFE_ABI sk_bit_cast(const Src& src) { | |
| static_assert(sizeof(Dst) == sizeof(Src)); | |
| static_assert(std::is_trivially_copyable<Dst>::value); | |
| static_assert(std::is_trivially_copyable<Src>::value); | |
| return sk_unaligned_load<Dst>(&src); | |
| } | |
| #undef SK_FP_SAFE_ABI | |
| #endif |