From 9ca9fda2534bfc099f40628fe03cb17039ccb3f2 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 17 Dec 2018 13:25:27 +0100 Subject: [PATCH] cpu: add cpu support implementation --- spa/plugins/support/cpu-x86.c | 176 ++++++++++++++++++++++++++++ spa/plugins/support/cpu.c | 199 ++++++++++++++++++++++++++++++++ spa/plugins/support/meson.build | 3 +- 3 files changed, 377 insertions(+), 1 deletion(-) create mode 100644 spa/plugins/support/cpu-x86.c create mode 100644 spa/plugins/support/cpu.c diff --git a/spa/plugins/support/cpu-x86.c b/spa/plugins/support/cpu-x86.c new file mode 100644 index 000000000..0c7518338 --- /dev/null +++ b/spa/plugins/support/cpu-x86.c @@ -0,0 +1,176 @@ +/* Spa + * + * Copyright © 2018 Wim Taymans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include + +static int +x86_init(struct impl *impl) +{ + uint32_t flags; + + unsigned int vendor; + unsigned int model, family; + unsigned int max_level, ext_level, has_osxsave; + unsigned int eax, ebx, ecx, edx; + + + max_level = __get_cpuid_max(0, &vendor); + if (max_level < 1) + return 0; + + __cpuid(1, eax, ebx, ecx, edx); + + model = (eax >> 4) & 0x0f; + family = (eax >> 8) & 0x0f; + + if (vendor == signature_INTEL_ebx || + vendor == signature_AMD_ebx) { + unsigned int extended_model, extended_family; + + extended_model = (eax >> 12) & 0xf0; + extended_family = (eax >> 20) & 0xff; + if (family == 0x0f) { + family += extended_family; + model += extended_model; + } else if (family == 0x06) + model += extended_model; + } + + flags = 0; + if (ecx & bit_SSE3) + flags |= SPA_CPU_FLAG_SSE3; + if (ecx & bit_SSSE3) + flags |= SPA_CPU_FLAG_SSSE3; + if (ecx & bit_SSE4_1) + flags |= SPA_CPU_FLAG_SSE41; + if (ecx & bit_SSE4_2) + flags |= SPA_CPU_FLAG_SSE42; + if (ecx & bit_AVX) + flags |= SPA_CPU_FLAG_AVX; + has_osxsave = ecx & bit_OSXSAVE; + if (ecx & bit_FMA) + flags |= SPA_CPU_FLAG_FMA3; + + if (edx & bit_CMOV) + flags |= SPA_CPU_FLAG_CMOV; + if (edx & bit_MMX) + flags |= SPA_CPU_FLAG_MMX; + if (edx & bit_SSE) + flags |= SPA_CPU_FLAG_SSE; + if (edx & bit_SSE2) + flags |= SPA_CPU_FLAG_SSE2; + + + if (max_level >= 7) { + __cpuid_count(7, 0, eax, ebx, ecx, edx); + + if (ebx & bit_BMI) + flags |= SPA_CPU_FLAG_BMI1; + if (ebx & bit_AVX2) + flags |= SPA_CPU_FLAG_AVX2; + if (ebx & bit_BMI2) + flags |= SPA_CPU_FLAG_BMI2; +#define AVX512_BITS (bit_AVX512F | bit_AVX512DQ | bit_AVX512CD | bit_AVX512BW | bit_AVX512VL) + if ((ebx & AVX512_BITS) == AVX512_BITS) + flags |= SPA_CPU_FLAG_AVX512; + } + + /* Check cpuid level of extended features. */ + __cpuid (0x80000000, ext_level, ebx, ecx, edx); + + if (ext_level >= 0x80000001) { + __cpuid (0x80000001, eax, ebx, ecx, edx); + + if (edx & bit_3DNOW) + flags |= SPA_CPU_FLAG_3DNOW; + if (edx & bit_3DNOWP) + flags |= SPA_CPU_FLAG_3DNOWEXT; + if (edx & bit_MMX) + flags |= SPA_CPU_FLAG_MMX; + if (edx & bit_MMXEXT) + flags |= SPA_CPU_FLAG_MMXEXT; + if (ecx & bit_FMA4) + flags |= SPA_CPU_FLAG_FMA4; + if (ecx & bit_XOP) + flags |= SPA_CPU_FLAG_XOP; + } + + /* Get XCR_XFEATURE_ENABLED_MASK register with xgetbv. */ +#define XCR_XFEATURE_ENABLED_MASK 0x0 +#define XSTATE_FP 0x1 +#define XSTATE_SSE 0x2 +#define XSTATE_YMM 0x4 +#define XSTATE_OPMASK 0x20 +#define XSTATE_ZMM 0x40 +#define XSTATE_HI_ZMM 0x80 + +#define XCR_AVX_ENABLED_MASK \ + (XSTATE_SSE | XSTATE_YMM) +#define XCR_AVX512F_ENABLED_MASK \ + (XSTATE_SSE | XSTATE_YMM | XSTATE_OPMASK | XSTATE_ZMM | XSTATE_HI_ZMM) + + if (has_osxsave) + asm (".byte 0x0f; .byte 0x01; .byte 0xd0" + : "=a" (eax), "=d" (edx) + : "c" (XCR_XFEATURE_ENABLED_MASK)); + else + eax = 0; + + /* Check if AVX registers are supported. */ + if ((eax & XCR_AVX_ENABLED_MASK) != XCR_AVX_ENABLED_MASK) { + flags &= ~(SPA_CPU_FLAG_AVX | + SPA_CPU_FLAG_AVX2 | + SPA_CPU_FLAG_FMA3 | + SPA_CPU_FLAG_FMA4 | + SPA_CPU_FLAG_XOP); + } + + /* Check if AVX512F registers are supported. */ + if ((eax & XCR_AVX512F_ENABLED_MASK) != XCR_AVX512F_ENABLED_MASK) { + flags &= ~SPA_CPU_FLAG_AVX512; + } + + if (flags & SPA_CPU_FLAG_AVX512) + impl->max_align = 64; + else if (flags & (SPA_CPU_FLAG_AVX2 | + SPA_CPU_FLAG_AVX | + SPA_CPU_FLAG_XOP | + SPA_CPU_FLAG_FMA4 | + SPA_CPU_FLAG_FMA3)) + impl->max_align = 32; + else if (flags & (SPA_CPU_FLAG_AESNI | + SPA_CPU_FLAG_SSE42 | + SPA_CPU_FLAG_SSE41 | + SPA_CPU_FLAG_SSSE3 | + SPA_CPU_FLAG_SSE3 | + SPA_CPU_FLAG_SSE2 | + SPA_CPU_FLAG_SSE)) + impl->max_align = 16; + else + impl->max_align = 8; + + impl->flags = flags; + + return 0; +} diff --git a/spa/plugins/support/cpu.c b/spa/plugins/support/cpu.c new file mode 100644 index 000000000..7ef7b0811 --- /dev/null +++ b/spa/plugins/support/cpu.c @@ -0,0 +1,199 @@ +/* Spa + * + * Copyright © 2018 Wim Taymans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define NAME "cpu" + +struct impl { + struct spa_handle handle; + struct spa_cpu cpu; + + struct spa_log *log; + + uint32_t flags; + uint32_t count; + uint32_t max_align; +}; + +# if defined (__i386__) || defined (__x86_64__) +#include "cpu-x86.c" +#define init(t) x86_init(t) +#endif + +static uint32_t +impl_cpu_get_flags(struct spa_cpu *cpu) +{ + struct impl *impl = SPA_CONTAINER_OF(cpu, struct impl, cpu); + return impl->flags; +} + +static uint32_t get_count(struct impl *this) +{ + cpu_set_t cpuset; + CPU_ZERO(&cpuset); + if (sched_getaffinity(0, sizeof(cpuset), &cpuset) == 0) + return CPU_COUNT(&cpuset); + return 1; +} + +static uint32_t +impl_cpu_get_count(struct spa_cpu *cpu) +{ + struct impl *impl = SPA_CONTAINER_OF(cpu, struct impl, cpu); + return impl->count; +} + +static uint32_t +impl_cpu_get_max_align(struct spa_cpu *cpu) +{ + struct impl *impl = SPA_CONTAINER_OF(cpu, struct impl, cpu); + return impl->max_align; +} + +static const struct spa_cpu impl_cpu = { + SPA_VERSION_CPU, + NULL, + impl_cpu_get_flags, + impl_cpu_get_count, + impl_cpu_get_max_align, +}; + +static int impl_get_interface(struct spa_handle *handle, uint32_t type, void **interface) +{ + struct impl *this; + + spa_return_val_if_fail(handle != NULL, -EINVAL); + spa_return_val_if_fail(interface != NULL, -EINVAL); + + this = (struct impl *) handle; + + if (type == SPA_TYPE_INTERFACE_CPU) + *interface = &this->cpu; + else + return -ENOENT; + + return 0; +} + +static int impl_clear(struct spa_handle *handle) +{ + return 0; +} + +static size_t +impl_get_size(const struct spa_handle_factory *factory, + const struct spa_dict *params) +{ + return sizeof(struct impl); +} + +static int +impl_init(const struct spa_handle_factory *factory, + struct spa_handle *handle, + const struct spa_dict *info, + const struct spa_support *support, + uint32_t n_support) +{ + struct impl *this; + uint32_t i; + + spa_return_val_if_fail(factory != NULL, -EINVAL); + spa_return_val_if_fail(handle != NULL, -EINVAL); + + handle->get_interface = impl_get_interface; + handle->clear = impl_clear; + + this = (struct impl *) handle; + + this->cpu = impl_cpu; + + for (i = 0; i < n_support; i++) { + if (support[i].type == SPA_TYPE_INTERFACE_Log) + this->log = support[i].data; + } + this->flags = 0; + this->max_align = 16; + this->count = get_count(this); + init(this); + + spa_log_debug(this->log, NAME " %p: count:%d align:%d flags:%08x", + this, this->count, this->max_align, this->flags); + + return 0; +} + +static const struct spa_interface_info impl_interfaces[] = { + {SPA_TYPE_INTERFACE_CPU,}, +}; + +static int +impl_enum_interface_info(const struct spa_handle_factory *factory, + const struct spa_interface_info **info, + uint32_t *index) +{ + spa_return_val_if_fail(factory != NULL, -EINVAL); + spa_return_val_if_fail(info != NULL, -EINVAL); + spa_return_val_if_fail(index != NULL, -EINVAL); + + switch (*index) { + case 0: + *info = &impl_interfaces[*index]; + break; + default: + return 0; + } + (*index)++; + + return 1; +} + +static const struct spa_handle_factory cpu_factory = { + SPA_VERSION_HANDLE_FACTORY, + NAME, + NULL, + impl_get_size, + impl_init, + impl_enum_interface_info, +}; + +int spa_handle_factory_register(const struct spa_handle_factory *factory); + +static void reg(void) __attribute__ ((constructor)); +static void reg(void) +{ + spa_handle_factory_register(&cpu_factory); +} diff --git a/spa/plugins/support/meson.build b/spa/plugins/support/meson.build index c996c04ec..62294222e 100644 --- a/spa/plugins/support/meson.build +++ b/spa/plugins/support/meson.build @@ -1,4 +1,5 @@ -spa_support_sources = ['logger.c', +spa_support_sources = ['cpu.c', + 'logger.c', 'loop.c', 'plugin.c']