libbpg/x265/source/common/cpu.cpp
2015-10-27 11:46:00 +01:00

374 lines
12 KiB
C++

/*****************************************************************************
* Copyright (C) 2013 x265 project
*
* Authors: Loren Merritt <lorenm@u.washington.edu>
* Laurent Aimar <fenrir@via.ecp.fr>
* Fiona Glaser <fiona@x264.com>
* Steve Borho <steve@borho.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111, USA.
*
* This program is also available under a commercial proprietary license.
* For more information, contact us at license @ x265.com.
*****************************************************************************/
#include "cpu.h"
#include "common.h"
#if MACOS || SYS_FREEBSD
#include <sys/types.h>
#include <sys/sysctl.h>
#endif
#if SYS_OPENBSD
#include <sys/param.h>
#include <sys/sysctl.h>
#include <machine/cpu.h>
#endif
#if X265_ARCH_ARM && !defined(HAVE_NEON)
#include <signal.h>
#include <setjmp.h>
static sigjmp_buf jmpbuf;
static volatile sig_atomic_t canjump = 0;
static void sigill_handler(int sig)
{
if (!canjump)
{
signal(sig, SIG_DFL);
raise(sig);
}
canjump = 0;
siglongjmp(jmpbuf, 1);
}
#endif // if X265_ARCH_ARM
namespace X265_NS {
const cpu_name_t cpu_names[] =
{
#if X265_ARCH_X86
#define MMX2 X265_CPU_MMX | X265_CPU_MMX2 | X265_CPU_CMOV
{ "MMX2", MMX2 },
{ "MMXEXT", MMX2 },
{ "SSE", MMX2 | X265_CPU_SSE },
#define SSE2 MMX2 | X265_CPU_SSE | X265_CPU_SSE2
{ "SSE2Slow", SSE2 | X265_CPU_SSE2_IS_SLOW },
{ "SSE2", SSE2 },
{ "SSE2Fast", SSE2 | X265_CPU_SSE2_IS_FAST },
{ "SSE3", SSE2 | X265_CPU_SSE3 },
{ "SSSE3", SSE2 | X265_CPU_SSE3 | X265_CPU_SSSE3 },
{ "SSE4.1", SSE2 | X265_CPU_SSE3 | X265_CPU_SSSE3 | X265_CPU_SSE4 },
{ "SSE4", SSE2 | X265_CPU_SSE3 | X265_CPU_SSSE3 | X265_CPU_SSE4 },
{ "SSE4.2", SSE2 | X265_CPU_SSE3 | X265_CPU_SSSE3 | X265_CPU_SSE4 | X265_CPU_SSE42 },
#define AVX SSE2 | X265_CPU_SSE3 | X265_CPU_SSSE3 | X265_CPU_SSE4 | X265_CPU_SSE42 | X265_CPU_AVX
{ "AVX", AVX },
{ "XOP", AVX | X265_CPU_XOP },
{ "FMA4", AVX | X265_CPU_FMA4 },
{ "AVX2", AVX | X265_CPU_AVX2 },
{ "FMA3", AVX | X265_CPU_FMA3 },
#undef AVX
#undef SSE2
#undef MMX2
{ "Cache32", X265_CPU_CACHELINE_32 },
{ "Cache64", X265_CPU_CACHELINE_64 },
{ "LZCNT", X265_CPU_LZCNT },
{ "BMI1", X265_CPU_BMI1 },
{ "BMI2", X265_CPU_BMI1 | X265_CPU_BMI2 },
{ "SlowCTZ", X265_CPU_SLOW_CTZ },
{ "SlowAtom", X265_CPU_SLOW_ATOM },
{ "SlowPshufb", X265_CPU_SLOW_PSHUFB },
{ "SlowPalignr", X265_CPU_SLOW_PALIGNR },
{ "SlowShuffle", X265_CPU_SLOW_SHUFFLE },
{ "UnalignedStack", X265_CPU_STACK_MOD4 },
#elif X265_ARCH_ARM
{ "ARMv6", X265_CPU_ARMV6 },
{ "NEON", X265_CPU_NEON },
{ "FastNeonMRC", X265_CPU_FAST_NEON_MRC },
#endif // if X265_ARCH_X86
{ "", 0 },
};
#if X265_ARCH_X86
extern "C" {
/* cpu-a.asm */
int PFX(cpu_cpuid_test)(void);
void PFX(cpu_cpuid)(uint32_t op, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx);
void PFX(cpu_xgetbv)(uint32_t op, uint32_t *eax, uint32_t *edx);
}
#if defined(_MSC_VER)
#pragma warning(disable: 4309) // truncation of constant value
#endif
uint32_t cpu_detect(void)
{
uint32_t cpu = 0;
uint32_t eax, ebx, ecx, edx;
uint32_t vendor[4] = { 0 };
uint32_t max_extended_cap, max_basic_cap;
#if !X86_64
if (!PFX(cpu_cpuid_test)())
return 0;
#endif
PFX(cpu_cpuid)(0, &eax, vendor + 0, vendor + 2, vendor + 1);
max_basic_cap = eax;
if (max_basic_cap == 0)
return 0;
PFX(cpu_cpuid)(1, &eax, &ebx, &ecx, &edx);
if (edx & 0x00800000)
cpu |= X265_CPU_MMX;
else
return cpu;
if (edx & 0x02000000)
cpu |= X265_CPU_MMX2 | X265_CPU_SSE;
if (edx & 0x00008000)
cpu |= X265_CPU_CMOV;
else
return cpu;
if (edx & 0x04000000)
cpu |= X265_CPU_SSE2;
if (ecx & 0x00000001)
cpu |= X265_CPU_SSE3;
if (ecx & 0x00000200)
cpu |= X265_CPU_SSSE3;
if (ecx & 0x00080000)
cpu |= X265_CPU_SSE4;
if (ecx & 0x00100000)
cpu |= X265_CPU_SSE42;
/* Check OXSAVE and AVX bits */
if ((ecx & 0x18000000) == 0x18000000)
{
/* Check for OS support */
PFX(cpu_xgetbv)(0, &eax, &edx);
if ((eax & 0x6) == 0x6)
{
cpu |= X265_CPU_AVX;
if (ecx & 0x00001000)
cpu |= X265_CPU_FMA3;
}
}
if (max_basic_cap >= 7)
{
PFX(cpu_cpuid)(7, &eax, &ebx, &ecx, &edx);
/* AVX2 requires OS support, but BMI1/2 don't. */
if ((cpu & X265_CPU_AVX) && (ebx & 0x00000020))
cpu |= X265_CPU_AVX2;
if (ebx & 0x00000008)
{
cpu |= X265_CPU_BMI1;
if (ebx & 0x00000100)
cpu |= X265_CPU_BMI2;
}
}
if (cpu & X265_CPU_SSSE3)
cpu |= X265_CPU_SSE2_IS_FAST;
PFX(cpu_cpuid)(0x80000000, &eax, &ebx, &ecx, &edx);
max_extended_cap = eax;
if (max_extended_cap >= 0x80000001)
{
PFX(cpu_cpuid)(0x80000001, &eax, &ebx, &ecx, &edx);
if (ecx & 0x00000020)
cpu |= X265_CPU_LZCNT; /* Supported by Intel chips starting with Haswell */
if (ecx & 0x00000040) /* SSE4a, AMD only */
{
int family = ((eax >> 8) & 0xf) + ((eax >> 20) & 0xff);
cpu |= X265_CPU_SSE2_IS_FAST; /* Phenom and later CPUs have fast SSE units */
if (family == 0x14)
{
cpu &= ~X265_CPU_SSE2_IS_FAST; /* SSSE3 doesn't imply fast SSE anymore... */
cpu |= X265_CPU_SSE2_IS_SLOW; /* Bobcat has 64-bit SIMD units */
cpu |= X265_CPU_SLOW_PALIGNR; /* palignr is insanely slow on Bobcat */
}
if (family == 0x16)
{
cpu |= X265_CPU_SLOW_PSHUFB; /* Jaguar's pshufb isn't that slow, but it's slow enough
* compared to alternate instruction sequences that this
* is equal or faster on almost all such functions. */
}
}
if (cpu & X265_CPU_AVX)
{
if (ecx & 0x00000800) /* XOP */
cpu |= X265_CPU_XOP;
if (ecx & 0x00010000) /* FMA4 */
cpu |= X265_CPU_FMA4;
}
if (!strcmp((char*)vendor, "AuthenticAMD"))
{
if (edx & 0x00400000)
cpu |= X265_CPU_MMX2;
if (!(cpu & X265_CPU_LZCNT))
cpu |= X265_CPU_SLOW_CTZ;
if ((cpu & X265_CPU_SSE2) && !(cpu & X265_CPU_SSE2_IS_FAST))
cpu |= X265_CPU_SSE2_IS_SLOW; /* AMD CPUs come in two types: terrible at SSE and great at it */
}
}
if (!strcmp((char*)vendor, "GenuineIntel"))
{
PFX(cpu_cpuid)(1, &eax, &ebx, &ecx, &edx);
int family = ((eax >> 8) & 0xf) + ((eax >> 20) & 0xff);
int model = ((eax >> 4) & 0xf) + ((eax >> 12) & 0xf0);
if (family == 6)
{
/* 6/9 (pentium-m "banias"), 6/13 (pentium-m "dothan"), and 6/14 (core1 "yonah")
* theoretically support sse2, but it's significantly slower than mmx for
* almost all of x264's functions, so let's just pretend they don't. */
if (model == 9 || model == 13 || model == 14)
{
cpu &= ~(X265_CPU_SSE2 | X265_CPU_SSE3);
X265_CHECK(!(cpu & (X265_CPU_SSSE3 | X265_CPU_SSE4)), "unexpected CPU ID %d\n", cpu);
}
/* Detect Atom CPU */
else if (model == 28)
{
cpu |= X265_CPU_SLOW_ATOM;
cpu |= X265_CPU_SLOW_CTZ;
cpu |= X265_CPU_SLOW_PSHUFB;
}
/* Conroe has a slow shuffle unit. Check the model number to make sure not
* to include crippled low-end Penryns and Nehalems that don't have SSE4. */
else if ((cpu & X265_CPU_SSSE3) && !(cpu & X265_CPU_SSE4) && model < 23)
cpu |= X265_CPU_SLOW_SHUFFLE;
}
}
if ((!strcmp((char*)vendor, "GenuineIntel") || !strcmp((char*)vendor, "CyrixInstead")) && !(cpu & X265_CPU_SSE42))
{
/* cacheline size is specified in 3 places, any of which may be missing */
PFX(cpu_cpuid)(1, &eax, &ebx, &ecx, &edx);
int cache = (ebx & 0xff00) >> 5; // cflush size
if (!cache && max_extended_cap >= 0x80000006)
{
PFX(cpu_cpuid)(0x80000006, &eax, &ebx, &ecx, &edx);
cache = ecx & 0xff; // cacheline size
}
if (!cache && max_basic_cap >= 2)
{
// Cache and TLB Information
static const char cache32_ids[] = { 0x0a, 0x0c, 0x41, 0x42, 0x43, 0x44, 0x45, 0x82, 0x83, 0x84, 0x85, 0 };
static const char cache64_ids[] = { 0x22, 0x23, 0x25, 0x29, 0x2c, 0x46, 0x47, 0x49, 0x60, 0x66, 0x67,
0x68, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7c, 0x7f, 0x86, 0x87, 0 };
uint32_t buf[4];
int max, i = 0;
do
{
PFX(cpu_cpuid)(2, buf + 0, buf + 1, buf + 2, buf + 3);
max = buf[0] & 0xff;
buf[0] &= ~0xff;
for (int j = 0; j < 4; j++)
{
if (!(buf[j] >> 31))
while (buf[j])
{
if (strchr(cache32_ids, buf[j] & 0xff))
cache = 32;
if (strchr(cache64_ids, buf[j] & 0xff))
cache = 64;
buf[j] >>= 8;
}
}
}
while (++i < max);
}
if (cache == 32)
cpu |= X265_CPU_CACHELINE_32;
else if (cache == 64)
cpu |= X265_CPU_CACHELINE_64;
else
x265_log(NULL, X265_LOG_WARNING, "unable to determine cacheline size\n");
}
#if BROKEN_STACK_ALIGNMENT
cpu |= X265_CPU_STACK_MOD4;
#endif
return cpu;
}
#elif X265_ARCH_ARM
extern "C" {
void PFX(cpu_neon_test)(void);
int PFX(cpu_fast_neon_mrc_test)(void);
}
uint32_t cpu_detect(void)
{
int flags = 0;
#if HAVE_ARMV6
flags |= X265_CPU_ARMV6;
// don't do this hack if compiled with -mfpu=neon
#if !HAVE_NEON
static void (* oldsig)(int);
oldsig = signal(SIGILL, sigill_handler);
if (sigsetjmp(jmpbuf, 1))
{
signal(SIGILL, oldsig);
return flags;
}
canjump = 1;
PFX(cpu_neon_test)();
canjump = 0;
signal(SIGILL, oldsig);
#endif // if !HAVE_NEON
flags |= X265_CPU_NEON;
// fast neon -> arm (Cortex-A9) detection relies on user access to the
// cycle counter; this assumes ARMv7 performance counters.
// NEON requires at least ARMv7, ARMv8 may require changes here, but
// hopefully this hacky detection method will have been replaced by then.
// Note that there is potential for a race condition if another program or
// x264 instance disables or reinits the counters while x264 is using them,
// which may result in incorrect detection and the counters stuck enabled.
// right now Apple does not seem to support performance counters for this test
#ifndef __MACH__
flags |= PFX(cpu_fast_neon_mrc_test)() ? X265_CPU_FAST_NEON_MRC : 0;
#endif
// TODO: write dual issue test? currently it's A8 (dual issue) vs. A9 (fast mrc)
#endif // if HAVE_ARMV6
return flags;
}
#else // if X265_ARCH_X86
uint32_t cpu_detect(void)
{
return 0;
}
#endif // if X265_ARCH_X86
}