stream: Traverse graph after modification
[qemu.git] / scripts / cpu-x86-uarch-abi.py
blob08acc52a816fd6cb504ca74959f3a9f7c4994f43
1 #!/usr/bin/python3
3 # SPDX-License-Identifier: GPL-2.0-or-later
5 # A script to generate a CSV file showing the x86_64 ABI
6 # compatibility levels for each CPU model.
9 from qemu import qmp
10 import sys
12 if len(sys.argv) != 1:
13 print("syntax: %s QMP-SOCK\n\n" % __file__ +
14 "Where QMP-SOCK points to a QEMU process such as\n\n" +
15 " # qemu-system-x86_64 -qmp unix:/tmp/qmp,server,nowait " +
16 "-display none -accel kvm", file=sys.stderr)
17 sys.exit(1)
19 # Mandatory CPUID features for each microarch ABI level
20 levels = [
21 [ # x86-64 baseline
22 "cmov",
23 "cx8",
24 "fpu",
25 "fxsr",
26 "mmx",
27 "syscall",
28 "sse",
29 "sse2",
31 [ # x86-64-v2
32 "cx16",
33 "lahf-lm",
34 "popcnt",
35 "pni",
36 "sse4.1",
37 "sse4.2",
38 "ssse3",
40 [ # x86-64-v3
41 "avx",
42 "avx2",
43 "bmi1",
44 "bmi2",
45 "f16c",
46 "fma",
47 "abm",
48 "movbe",
50 [ # x86-64-v4
51 "avx512f",
52 "avx512bw",
53 "avx512cd",
54 "avx512dq",
55 "avx512vl",
59 # Assumes externally launched process such as
61 # qemu-system-x86_64 -qmp unix:/tmp/qmp,server,nowait -display none -accel kvm
63 # Note different results will be obtained with TCG, as
64 # TCG masks out certain features otherwise present in
65 # the CPU model definitions, as does KVM.
68 sock = sys.argv[1]
69 cmd = sys.argv[2]
70 shell = qmp.QEMUMonitorProtocol(sock)
71 shell.connect()
73 models = shell.cmd("query-cpu-definitions")
75 # These QMP props don't correspond to CPUID fatures
76 # so ignore them
77 skip = [
78 "family",
79 "min-level",
80 "min-xlevel",
81 "vendor",
82 "model",
83 "model-id",
84 "stepping",
87 names = []
89 for model in models["return"]:
90 if "alias-of" in model:
91 continue
92 names.append(model["name"])
94 models = {}
96 for name in sorted(names):
97 cpu = shell.cmd("query-cpu-model-expansion",
98 { "type": "static",
99 "model": { "name": name }})
101 got = {}
102 for (feature, present) in cpu["return"]["model"]["props"].items():
103 if present and feature not in skip:
104 got[feature] = True
106 if name in ["host", "max", "base"]:
107 continue
109 models[name] = {
110 # Dict of all present features in this CPU model
111 "features": got,
113 # Whether each x86-64 ABI level is satisfied
114 "levels": [False, False, False, False],
116 # Number of extra CPUID features compared to the x86-64 ABI level
117 "distance":[-1, -1, -1, -1],
119 # CPUID features present in model, but not in ABI level
120 "delta":[[], [], [], []],
122 # CPUID features in ABI level but not present in model
123 "missing": [[], [], [], []],
127 # Calculate whether the CPU models satisfy each ABI level
128 for name in models.keys():
129 for level in range(len(levels)):
130 got = set(models[name]["features"])
131 want = set(levels[level])
132 missing = want - got
133 match = True
134 if len(missing) > 0:
135 match = False
136 models[name]["levels"][level] = match
137 models[name]["missing"][level] = missing
139 # Cache list of CPU models satisfying each ABI level
140 abi_models = [
147 for name in models.keys():
148 for level in range(len(levels)):
149 if models[name]["levels"][level]:
150 abi_models[level].append(name)
153 for level in range(len(abi_models)):
154 # Find the union of features in all CPU models satisfying this ABI
155 allfeatures = {}
156 for name in abi_models[level]:
157 for feat in models[name]["features"]:
158 allfeatures[feat] = True
160 # Find the intersection of features in all CPU models satisfying this ABI
161 commonfeatures = []
162 for feat in allfeatures:
163 present = True
164 for name in models.keys():
165 if not models[name]["levels"][level]:
166 continue
167 if feat not in models[name]["features"]:
168 present = False
169 if present:
170 commonfeatures.append(feat)
172 # Determine how many extra features are present compared to the lowest
173 # common denominator
174 for name in models.keys():
175 if not models[name]["levels"][level]:
176 continue
178 delta = set(models[name]["features"].keys()) - set(commonfeatures)
179 models[name]["distance"][level] = len(delta)
180 models[name]["delta"][level] = delta
182 def print_uarch_abi_csv():
183 print("# Automatically generated from '%s'" % __file__)
184 print("Model,baseline,v2,v3,v4")
185 for name in models.keys():
186 print(name, end="")
187 for level in range(len(levels)):
188 if models[name]["levels"][level]:
189 print(",✅", end="")
190 else:
191 print(",", end="")
192 print()
194 print_uarch_abi_csv()