We were looking for any mention of the feature name in cpuinfo, which could have hit anything including features with common prefixes like sme, sme2, smefa64. Luckily this was not a problem but I'm changing this to find the features line and split the features into a list. Then we are only looking for exact matches. Here's the information for one core as an example: ``` processor : 7 BogoMIPS : 200.00 Features : fp asimd evtstrm crc32 atomics fphp asimdhp cpuid <...> CPU implementer : 0x41 CPU architecture: 8 CPU variant : 0x0 CPU part : 0xd0f CPU revision : 0 ``` (and to avoid any doubt, this is from a CPU simulated in Arm's FVP, it's not real) Note that the layout of the label, colon, values is sometimes aligned but not always. So I trim whitespace a few times to normalise that. This repeats once for each core so we only need to find one features line.
80 lines
2.4 KiB
Python
80 lines
2.4 KiB
Python
"""
|
|
Platform-agnostic helper to query for CPU features.
|
|
"""
|
|
|
|
import re
|
|
|
|
|
|
class CPUFeature:
|
|
def __init__(self, linux_cpu_info_flag: str = None, darwin_sysctl_key: str = None):
|
|
self.cpu_info_flag = linux_cpu_info_flag
|
|
self.sysctl_key = darwin_sysctl_key
|
|
|
|
def __str__(self):
|
|
for arch_class in ALL_ARCHS:
|
|
for feat_var in dir(arch_class):
|
|
if self == getattr(arch_class, feat_var):
|
|
return f"{arch_class.__name__}.{feat_var}"
|
|
raise AssertionError("unreachable")
|
|
|
|
def is_supported(self, triple, cmd_runner):
|
|
if re.match(".*-.*-linux", triple):
|
|
err_msg, res = self._is_supported_linux(cmd_runner)
|
|
elif re.match(".*-apple-.*", triple):
|
|
err_msg, res = self._is_supported_darwin(cmd_runner)
|
|
else:
|
|
err_msg, res = None, False
|
|
|
|
if err_msg:
|
|
print(f"CPU feature check failed: {err_msg}")
|
|
|
|
return res
|
|
|
|
def _is_supported_linux(self, cmd_runner):
|
|
if not self.cpu_info_flag:
|
|
return f"Unspecified cpuinfo flag for {self}", False
|
|
|
|
cmd = "cat /proc/cpuinfo"
|
|
err, retcode, output = cmd_runner(cmd)
|
|
if err.Fail() or retcode != 0:
|
|
return output, False
|
|
|
|
# Assume that every processor presents the same features.
|
|
# Look for the first "Features: ...." line. Features are space separated.
|
|
if m := re.search(r"Features\s*: (.*)\n", output):
|
|
features = m.group(1).split()
|
|
return None, (self.cpu_info_flag in features)
|
|
|
|
return 'No "Features:" line found in /proc/cpuinfo', False
|
|
|
|
def _is_supported_darwin(self, cmd_runner):
|
|
if not self.sysctl_key:
|
|
return f"Unspecified sysctl key for {self}", False
|
|
|
|
cmd = f"sysctl -n {self.sysctl_key}"
|
|
err, retcode, output = cmd_runner(cmd)
|
|
if err.Fail() or retcode != 0:
|
|
return output, False
|
|
|
|
return None, (output.strip() == "1")
|
|
|
|
|
|
class AArch64:
|
|
FPMR = CPUFeature("fpmr")
|
|
GCS = CPUFeature("gcs")
|
|
MTE = CPUFeature("mte")
|
|
MTE_STORE_ONLY = CPUFeature("mtestoreonly")
|
|
PTR_AUTH = CPUFeature("paca", "hw.optional.arm.FEAT_PAuth2")
|
|
SME = CPUFeature("sme", "hw.optional.arm.FEAT_SME")
|
|
SME_FA64 = CPUFeature("smefa64")
|
|
SME2 = CPUFeature("sme2", "hw.optional.arm.FEAT_SME2")
|
|
SVE = CPUFeature("sve")
|
|
|
|
|
|
class Loong:
|
|
LASX = CPUFeature("lasx")
|
|
LSX = CPUFeature("lsx")
|
|
|
|
|
|
ALL_ARCHS = [AArch64, Loong]
|