123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192 |
- #!/usr/bin/python3
- #
- # SPDX-License-Identifier: GPL-2.0-or-later
- #
- # A script to generate a CSV file showing the x86_64 ABI
- # compatibility levels for each CPU model.
- #
- from qemu.qmp.legacy import QEMUMonitorProtocol
- import sys
- if len(sys.argv) != 2:
- print("syntax: %s QMP-SOCK\n\n" % __file__ +
- "Where QMP-SOCK points to a QEMU process such as\n\n" +
- " # qemu-system-x86_64 -qmp unix:/tmp/qmp,server,nowait " +
- "-display none -accel kvm", file=sys.stderr)
- sys.exit(1)
- # Mandatory CPUID features for each microarch ABI level
- levels = [
- [ # x86-64 baseline
- "cmov",
- "cx8",
- "fpu",
- "fxsr",
- "mmx",
- "syscall",
- "sse",
- "sse2",
- ],
- [ # x86-64-v2
- "cx16",
- "lahf-lm",
- "popcnt",
- "pni",
- "sse4.1",
- "sse4.2",
- "ssse3",
- ],
- [ # x86-64-v3
- "avx",
- "avx2",
- "bmi1",
- "bmi2",
- "f16c",
- "fma",
- "abm",
- "movbe",
- ],
- [ # x86-64-v4
- "avx512f",
- "avx512bw",
- "avx512cd",
- "avx512dq",
- "avx512vl",
- ],
- ]
- # Assumes externally launched process such as
- #
- # qemu-system-x86_64 -qmp unix:/tmp/qmp,server,nowait -display none -accel kvm
- #
- # Note different results will be obtained with TCG, as
- # TCG masks out certain features otherwise present in
- # the CPU model definitions, as does KVM.
- sock = sys.argv[1]
- shell = QEMUMonitorProtocol(sock)
- shell.connect()
- models = shell.cmd("query-cpu-definitions")
- # These QMP props don't correspond to CPUID fatures
- # so ignore them
- skip = [
- "family",
- "min-level",
- "min-xlevel",
- "vendor",
- "model",
- "model-id",
- "stepping",
- ]
- names = []
- for model in models:
- if "alias-of" in model:
- continue
- names.append(model["name"])
- models = {}
- for name in sorted(names):
- cpu = shell.cmd("query-cpu-model-expansion",
- type="static",
- model={ "name": name })
- got = {}
- for (feature, present) in cpu["model"]["props"].items():
- if present and feature not in skip:
- got[feature] = True
- if name in ["host", "max", "base"]:
- continue
- models[name] = {
- # Dict of all present features in this CPU model
- "features": got,
- # Whether each x86-64 ABI level is satisfied
- "levels": [False, False, False, False],
- # Number of extra CPUID features compared to the x86-64 ABI level
- "distance":[-1, -1, -1, -1],
- # CPUID features present in model, but not in ABI level
- "delta":[[], [], [], []],
- # CPUID features in ABI level but not present in model
- "missing": [[], [], [], []],
- }
- # Calculate whether the CPU models satisfy each ABI level
- for name in models.keys():
- for level in range(len(levels)):
- got = set(models[name]["features"])
- want = set(levels[level])
- missing = want - got
- match = True
- if len(missing) > 0:
- match = False
- models[name]["levels"][level] = match
- models[name]["missing"][level] = missing
- # Cache list of CPU models satisfying each ABI level
- abi_models = [
- [],
- [],
- [],
- [],
- ]
- for name in models.keys():
- for level in range(len(levels)):
- if models[name]["levels"][level]:
- abi_models[level].append(name)
- for level in range(len(abi_models)):
- # Find the union of features in all CPU models satisfying this ABI
- allfeatures = {}
- for name in abi_models[level]:
- for feat in models[name]["features"]:
- allfeatures[feat] = True
- # Find the intersection of features in all CPU models satisfying this ABI
- commonfeatures = []
- for feat in allfeatures:
- present = True
- for name in models.keys():
- if not models[name]["levels"][level]:
- continue
- if feat not in models[name]["features"]:
- present = False
- if present:
- commonfeatures.append(feat)
- # Determine how many extra features are present compared to the lowest
- # common denominator
- for name in models.keys():
- if not models[name]["levels"][level]:
- continue
- delta = set(models[name]["features"].keys()) - set(commonfeatures)
- models[name]["distance"][level] = len(delta)
- models[name]["delta"][level] = delta
- def print_uarch_abi_csv():
- print("Model,baseline,v2,v3,v4")
- for name in models.keys():
- print(name, end="")
- for level in range(len(levels)):
- if models[name]["levels"][level]:
- print(",✅", end="")
- else:
- print(",", end="")
- print()
- print_uarch_abi_csv()
|