File indexing completed on 2024-06-09 04:00:39

0001 /*
0002     SPDX-FileCopyrightText: 2010 Kevin Ottens <ervin@kde.org>
0003     SPDX-FileCopyrightText: 2013 Patrick Spendrin <ps_ml@gmx.de>
0004 
0005     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0006 */
0007 
0008 // clang-format off
0009 
0010 #include "cpufeatures.h"
0011 
0012 #ifndef _MSC_VER
0013 //for cpuFeatures
0014 #include <csignal>
0015 #include <csetjmp>
0016 #else
0017 #include <intrin.h>
0018 #endif
0019 #include <config-processor.h>
0020 
0021 #if defined(__GNUC__) || defined(__INTEL_COMPILER)
0022 #  define HAVE_GNU_INLINE_ASM
0023 #endif
0024 
0025 namespace Solid
0026 {
0027 namespace Backends
0028 {
0029 namespace Shared
0030 {
0031 
0032 #ifndef _MSC_VER
0033 typedef void (*kde_sighandler_t)(int);
0034 
0035 #if defined( __i386__ ) || defined( __x86_64__ )
0036 static jmp_buf env;
0037 
0038 #if HAVE_X86_SSE
0039 // Sighandler for the SSE OS support check
0040 static void sighandler(int)
0041 {
0042     std::longjmp(env, 1);
0043 }
0044 #endif
0045 #endif
0046 
0047 #ifdef __i386__
0048 #define ASM_REG(reg)              "%e" reg
0049 #define ASM_POP(reg)              "popl   %%e" reg "             \n\t"
0050 #define ASM_PUSH(reg)             "pushl  %%e" reg "             \n\t"
0051 #define ASM_XOR_REG(reg1, reg2)   "xorl   %%e" reg1 ", %%e" reg2 " \n\t"
0052 #define ASM_XOR_VAR(var, reg)     "xorl   " var ",     %%e" reg "  \n\t"
0053 #define ASM_CMP_REG(reg1, reg2)   "cmpl   %%e" reg1 ", %%e" reg2 " \n\t"
0054 #define ASM_MOV_REG(reg1, reg2)   "movl   %%e" reg1 ", %%e" reg2 " \n\t"
0055 #define ASM_MOV_VAR(var, reg)     "movl   " var ",     %%e" reg "  \n\t"
0056 #elif defined(__x86_64__)
0057 #define ASM_REG(reg)              "%r" reg
0058 #define ASM_POP(reg)              "popq   %%r" reg "             \n\t"
0059 #define ASM_PUSH(reg)             "pushq  %%r" reg "             \n\t"
0060 #define ASM_XOR_REG(reg1, reg2)   "xorq   %%r" reg1 ", %%r" reg2 " \n\t"
0061 #define ASM_XOR_VAR(var, reg)     "xorq   " var ",     %%r" reg "  \n\t"
0062 #define ASM_CMP_REG(reg1, reg2)   "cmpq   %%r" reg1 ", %%r" reg2 " \n\t"
0063 #define ASM_MOV_REG(reg1, reg2)   "movq   %%r" reg1 ", %%r" reg2 " \n\t"
0064 #define ASM_MOV_VAR(var, reg)     "movq   " var ",     %%r" reg "  \n\t"
0065 #endif
0066 #endif
0067 
0068 #ifdef __PPC__
0069 static sigjmp_buf jmpbuf;
0070 static sig_atomic_t canjump = 0;
0071 
0072 static void sigill_handler(int sig)
0073 {
0074     if (!canjump) {
0075         signal(sig, SIG_DFL);
0076         raise(sig);
0077     }
0078     canjump = 0;
0079     siglongjmp(jmpbuf, 1);
0080 }
0081 #endif
0082 
0083 Solid::Processor::InstructionSets cpuFeatures()
0084 {
0085     volatile unsigned int features = 0;
0086 
0087 #if defined( HAVE_GNU_INLINE_ASM )
0088 #if defined( __i386__ ) || defined( __x86_64__ )
0089     bool haveCPUID = false;
0090     unsigned int result = 0;
0091     unsigned int result2 = 0;
0092 
0093     // First check if the CPU supports the CPUID instruction
0094     __asm__ __volatile__(
0095         // Try to toggle the CPUID bit in the EFLAGS register
0096         "pushf                      \n\t"   // Push the EFLAGS register onto the stack
0097         ASM_POP("cx")                       // Pop the value into ECX
0098         ASM_MOV_REG("cx", "dx")             // Copy ECX to EDX
0099         ASM_XOR_VAR("$0x00200000", "cx")    // Toggle bit 21 (CPUID) in ECX
0100         ASM_PUSH("cx")                      // Push the modified value onto the stack
0101         "popf                       \n\t"   // Pop it back into EFLAGS
0102 
0103         // Check if the CPUID bit was successfully toggled
0104         "pushf                      \n\t"   // Push EFLAGS back onto the stack
0105         ASM_POP("cx")                       // Pop the value into ECX
0106         ASM_XOR_REG("ax", "ax")             // Zero out the EAX register
0107         ASM_CMP_REG("cx", "dx")             // Compare ECX with EDX
0108         "je    .Lno_cpuid_support%= \n\t"   // Jump if they're identical
0109         ASM_MOV_VAR("$1", "ax")             // Set EAX to true
0110         ".Lno_cpuid_support%=:      \n\t"
0111         : "=a"(haveCPUID) : : ASM_REG("cx"), ASM_REG("dx"));
0112 
0113     // If we don't have CPUID we won't have the other extensions either
0114     if (haveCPUID) {
0115         // Execute CPUID with the feature request bit set
0116         __asm__ __volatile__(
0117             ASM_PUSH("bx")                      // Save EBX
0118             ASM_MOV_VAR("$1", "ax")             // Set EAX to 1 (features request)
0119             "cpuid                      \n\t"   // Call CPUID
0120             ASM_POP("bx")                       // Restore EBX
0121             : "=d"(result), "=c"(result2) : : ASM_REG("ax"));
0122 
0123         features = result & 0x06800000; //copy the mmx and sse & sse2 bits to features
0124         features |= result2 & 0x00180101; //copy the ssse3, sse3 and sse4.1, sse4.2 bits to features
0125 
0126         __asm__ __volatile__(
0127             ASM_PUSH("bx")
0128             ASM_PUSH("dx")
0129             ASM_MOV_VAR("$0x80000000", "ax")
0130             ASM_MOV_VAR("$0x80000000", "dx")
0131             "cpuid                   \n\t"
0132             ASM_CMP_REG("dx", "ax")
0133             "jbe .Lno_extended%=     \n\t"
0134             ASM_MOV_VAR("$0x80000001", "ax")
0135             "cpuid                   \n\t"
0136             ".Lno_extended%=:        \n\t"
0137             ASM_POP("dx")
0138             ASM_POP("bx")                      // Restore EBX
0139             : "=d"(result) : : ASM_REG("ax"), ASM_REG("cx"));
0140 
0141         if (result & 0x80000000) {
0142             features |= 0x80000000;
0143         }
0144 
0145 #if HAVE_X86_SSE
0146         // Test bit 25 (SSE support)
0147         if (features & 0x02000000) {
0148             // OS support test for SSE.
0149             // Install our own sighandler for SIGILL.
0150             kde_sighandler_t oldhandler = std::signal(SIGILL, sighandler);
0151 
0152             // Try executing an SSE insn to see if we get a SIGILL
0153             if (setjmp(env)) {
0154                 features &= ~0x06080001;    // The OS support test failed
0155             } else {
0156                 __asm__ __volatile__("xorps %xmm0, %xmm0");
0157             }
0158 
0159             // Restore the default sighandler
0160             std::signal(SIGILL, oldhandler);
0161 
0162             // Note: The OS requirements for SSE2 are the same as for SSE
0163             //       so we don't have to do any additional tests for that.
0164         }
0165 #endif // HAVE_X86_SSE
0166     }
0167 #elif defined __PPC__ && HAVE_PPC_ALTIVEC
0168     signal(SIGILL, sigill_handler);
0169     if (sigsetjmp(jmpbuf, 1)) {
0170         signal(SIGILL, SIG_DFL);
0171     } else {
0172         canjump = 1;
0173         __asm__ __volatile__("mtspr 256, %0\n\t"
0174                              "vand %%v0, %%v0, %%v0"
0175                              : /* none */
0176                              : "r"(-1));
0177         signal(SIGILL, SIG_DFL);
0178         features = 0x2;
0179     }
0180 #endif // __i386__ || __x86_64__
0181 #elif defined(_MSC_VER)
0182     int array[4], ft = 1;
0183     __cpuid(array, ft);
0184 
0185     features = array[3] & 0x06800000; //copy the mmx and sse & sse2 bits to features
0186     features |= array[2] & 0x00180101; //copy the ssse3, sse3 and sse4.1, sse4.2 bits to features
0187 
0188     if (array[3] & 0x80000000) {
0189         features |= 0x80000000;
0190     }
0191 #endif //HAVE_GNU_INLINE_ASM
0192     Solid::Processor::InstructionSets featureflags;
0193 
0194     if (features & 0x80000000) {
0195         featureflags |= Solid::Processor::Amd3DNow;
0196     }
0197     if (features & 0x00800000) {
0198         featureflags |= Solid::Processor::IntelMmx;
0199     }
0200     if (features & 0x02000000) {
0201         featureflags |= Solid::Processor::IntelSse;
0202     }
0203     if (features & 0x04000000) {
0204         featureflags |= Solid::Processor::IntelSse2;
0205     }
0206     if (features & 0x00000001) {
0207         featureflags |= Solid::Processor::IntelSse3;
0208     }
0209     if (features & 0x00000100) {
0210         featureflags |= Solid::Processor::IntelSsse3;
0211     }
0212     if (features & 0x00080000) {
0213         featureflags |= Solid::Processor::IntelSse41;
0214     }
0215     if (features & 0x00100000) {
0216         featureflags |= Solid::Processor::IntelSse42;
0217     }
0218 
0219     if (features & 0x2) {
0220         featureflags |= Solid::Processor::AltiVec;
0221     }
0222 
0223     return featureflags;
0224 }
0225 
0226 }
0227 }
0228 }