diff --git a/FEXCore/Source/Interface/Core/CPUID.cpp b/FEXCore/Source/Interface/Core/CPUID.cpp index d8e28181e..661b1f86c 100644 --- a/FEXCore/Source/Interface/Core/CPUID.cpp +++ b/FEXCore/Source/Interface/Core/CPUID.cpp @@ -14,6 +14,7 @@ $end_info$ #include #include #include +#include #include #include @@ -443,10 +444,10 @@ FEXCore::CPUID::FunctionResults CPUIDEmu::Function_01h(uint32_t Leaf) const { Res.eax = FAMILY_IDENTIFIER; - Res.ebx = 0 | // Brand index - (8 << 8) | // Cache line size in bytes - (Cores << 16) | // Number of addressable IDs for the logical cores in the physical CPU - (0 << 24); // Local APIC ID + Res.ebx = 0 | // Brand index + (8 << 8) | // Cache line size in bytes + (Cores << 16) | // Number of addressable IDs for the logical cores in the physical CPU + (GetCPUID() << 24); // Local APIC ID Res.ecx = (1 << 0) | // SSE3 (CTX->HostFeatures.SupportsPMULL_128Bit << 1) | // PCLMULQDQ @@ -509,7 +510,7 @@ FEXCore::CPUID::FunctionResults CPUIDEmu::Function_01h(uint32_t Leaf) const { (1 << 25) | // SSE (1 << 26) | // SSE2 (0 << 27) | // Self Snoop - (1 << 28) | // Max APIC IDs reserved field is valid + (0 << 28) | // (HTT) Max APIC IDs reserved field is valid (1 << 29) | // Thermal monitor (0 << 30) | // Reserved (0 << 31); // Pending break enable @@ -1096,9 +1097,9 @@ FEXCore::CPUID::FunctionResults CPUIDEmu::Function_8000_0008h(uint32_t Leaf) con (CTX->HostFeatures.SupportsCLZERO << 0); // CLZERO support uint32_t CoreCount = Cores - 1; - Res.ecx = (0 << 16) | // PerfTscSize: Performance timestamp count size - ((uint32_t)std::log2(CoreCount + 1) << 12) | // ApicIdSize: Number of bits in ApicID - (CoreCount << 0); // Count count subtract one + Res.ecx = (0 << 16) | // PerfTscSize: Performance timestamp count size + (std::bit_ceil(Cores) << 12) | // ApicIdSize: Number of bits in ApicID + (CoreCount << 0); // Count count subtract one return Res; } diff --git a/unittests/FEXLinuxTests/tests/cpu/cpu_count.cpp b/unittests/FEXLinuxTests/tests/cpu/cpu_count.cpp new file mode 100644 index 000000000..89f219d0f --- /dev/null +++ b/unittests/FEXLinuxTests/tests/cpu/cpu_count.cpp @@ -0,0 +1,104 @@ +#include + +#include +#include +#include +#include + +TEST_CASE("cpu count - libraries") { + const auto hw_concurrency = std::thread::hardware_concurrency(); + CHECK(hw_concurrency == sysconf(_SC_NPROCESSORS_CONF)); + CHECK(hw_concurrency == sysconf(_SC_NPROCESSORS_ONLN)); +} + +struct core_info { + uint32_t local_apicid; + uint32_t max_addressible_ids; + uint32_t cores; + uint32_t threads; + bool HTT; +}; + +struct cpuid_fn { + uint32_t eax, ebx, ecx, edx; +}; + +cpuid_fn get_cpuid(uint32_t func, uint32_t leaf = 0) { + cpuid_fn fn {}; + __cpuid_count(func, leaf, fn.eax, fn.ebx, fn.ecx, fn.edx); + return fn; +} + +std::optional cpuid_calculate_core_info() { + core_info info {}; + + // Legacy path + const auto cpuid_fn_0 = get_cpuid(0); + if (cpuid_fn_0.eax < 1) { + return std::nullopt; + } + + const auto cpuid_fn_1 = get_cpuid(1); + + info.local_apicid = cpuid_fn_1.ebx >> 24; + info.HTT = (cpuid_fn_1.edx >> 28) & 1; + + const auto cpuid_fn_8000_0000 = get_cpuid(0x8000'0000U); + + if (cpuid_fn_8000_0000.eax < 0x8000'0008) { + return std::nullopt; + } + + const auto cpuid_fn_8000_0008 = get_cpuid(0x8000'0008U); + const uint32_t apic_id_size = (cpuid_fn_8000_0008.ecx >> 12) & 0xF; + + // E.5.2: MNLP (Maximum Number of Logical Processors) + uint32_t MNLP {}; + + if (apic_id_size) { + // Extended topology. + MNLP = 1 << apic_id_size; + } else { + // Legacy path. + MNLP = (cpuid_fn_8000_0008.ecx & 0xF) + 1; + } + + info.max_addressible_ids = MNLP; + + const auto cpuid_fn_4 = get_cpuid(4); + if (cpuid_fn_4.eax & 0xF) { + // Intel exclusive cpuid function, AMD returns zero as unsupported. + // `Maximum number of addressable IDs for processor cores in the physical package` + info.cores = (cpuid_fn_4.eax >> 26) + 1; + if (info.HTT) { + // `A value of 1 for HTT indicates the value in CPUID.1.EBX[23:16] (the Maximum number of addressable IDs for logical processors in + // this package) is valid for the package. `Maximum number of addressable IDs for logical processors in this physical package` + info.threads = (cpuid_fn_1.ebx >> 16) & 0xFF; + } else { + info.threads = info.cores; + } + } else if (info.HTT) { + info.cores = (cpuid_fn_1.ebx >> 16) & 0xFF; + info.threads = info.cores * 2; + } else { + // Legacy path means cores/threads is equal to MNLP. + info.cores = info.threads = MNLP; + } + + return info; +} + +TEST_CASE("cpu count - cpuid") { + const auto hw_concurrency = std::thread::hardware_concurrency(); + const auto core_info = cpuid_calculate_core_info(); + REQUIRE(core_info.has_value()); + CHECK(core_info->local_apicid < hw_concurrency); + CHECK(core_info->local_apicid < core_info->max_addressible_ids); + CHECK(core_info->max_addressible_ids >= hw_concurrency); + if (core_info->HTT) { + // May not be entirely correct on systems that mix HTT and non-HTT cpu cores. + CHECK((core_info->cores * 2) == core_info->threads); + } else { + CHECK(core_info->cores == core_info->threads); + } +}