qapi: allow unions to contain further unions
[qemu/armbru.git] / scripts / cpu-x86-uarch-abi.py
blob82ff07582f8a82dd1a3aa0ef58c1bfea62774450
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.qmp.legacy import QEMUMonitorProtocol
10 import sys
12 if len(sys.argv) != 2:
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 shell = QEMUMonitorProtocol(sock)
70 shell.connect()
72 models = shell.cmd("query-cpu-definitions")
74 # These QMP props don't correspond to CPUID fatures
75 # so ignore them
76 skip = [
77 "family",
78 "min-level",
79 "min-xlevel",
80 "vendor",
81 "model",
82 "model-id",
83 "stepping",
86 names = []
88 for model in models["return"]:
89 if "alias-of" in model:
90 continue
91 names.append(model["name"])
93 models = {}
95 for name in sorted(names):
96 cpu = shell.cmd("query-cpu-model-expansion",
97 { "type": "static",
98 "model": { "name": name }})
100 got = {}
101 for (feature, present) in cpu["return"]["model"]["props"].items():
102 if present and feature not in skip:
103 got[feature] = True
105 if name in ["host", "max", "base"]:
106 continue
108 models[name] = {
109 # Dict of all present features in this CPU model
110 "features": got,
112 # Whether each x86-64 ABI level is satisfied
113 "levels": [False, False, False, False],
115 # Number of extra CPUID features compared to the x86-64 ABI level
116 "distance":[-1, -1, -1, -1],
118 # CPUID features present in model, but not in ABI level
119 "delta":[[], [], [], []],
121 # CPUID features in ABI level but not present in model
122 "missing": [[], [], [], []],
126 # Calculate whether the CPU models satisfy each ABI level
127 for name in models.keys():
128 for level in range(len(levels)):
129 got = set(models[name]["features"])
130 want = set(levels[level])
131 missing = want - got
132 match = True
133 if len(missing) > 0:
134 match = False
135 models[name]["levels"][level] = match
136 models[name]["missing"][level] = missing
138 # Cache list of CPU models satisfying each ABI level
139 abi_models = [
146 for name in models.keys():
147 for level in range(len(levels)):
148 if models[name]["levels"][level]:
149 abi_models[level].append(name)
152 for level in range(len(abi_models)):
153 # Find the union of features in all CPU models satisfying this ABI
154 allfeatures = {}
155 for name in abi_models[level]:
156 for feat in models[name]["features"]:
157 allfeatures[feat] = True
159 # Find the intersection of features in all CPU models satisfying this ABI
160 commonfeatures = []
161 for feat in allfeatures:
162 present = True
163 for name in models.keys():
164 if not models[name]["levels"][level]:
165 continue
166 if feat not in models[name]["features"]:
167 present = False
168 if present:
169 commonfeatures.append(feat)
171 # Determine how many extra features are present compared to the lowest
172 # common denominator
173 for name in models.keys():
174 if not models[name]["levels"][level]:
175 continue
177 delta = set(models[name]["features"].keys()) - set(commonfeatures)
178 models[name]["distance"][level] = len(delta)
179 models[name]["delta"][level] = delta
181 def print_uarch_abi_csv():
182 print("# Automatically generated from '%s'" % __file__)
183 print("Model,baseline,v2,v3,v4")
184 for name in models.keys():
185 print(name, end="")
186 for level in range(len(levels)):
187 if models[name]["levels"][level]:
188 print(",✅", end="")
189 else:
190 print(",", end="")
191 print()
193 print_uarch_abi_csv()