cpu-x86-uarch-abi.py (4875B)
1#!/usr/bin/python3 2# 3# SPDX-License-Identifier: GPL-2.0-or-later 4# 5# A script to generate a CSV file showing the x86_64 ABI 6# compatibility levels for each CPU model. 7# 8 9from qemu import qmp 10import sys 11 12if 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) 18 19# Mandatory CPUID features for each microarch ABI level 20levels = [ 21 [ # x86-64 baseline 22 "cmov", 23 "cx8", 24 "fpu", 25 "fxsr", 26 "mmx", 27 "syscall", 28 "sse", 29 "sse2", 30 ], 31 [ # x86-64-v2 32 "cx16", 33 "lahf-lm", 34 "popcnt", 35 "pni", 36 "sse4.1", 37 "sse4.2", 38 "ssse3", 39 ], 40 [ # x86-64-v3 41 "avx", 42 "avx2", 43 "bmi1", 44 "bmi2", 45 "f16c", 46 "fma", 47 "abm", 48 "movbe", 49 ], 50 [ # x86-64-v4 51 "avx512f", 52 "avx512bw", 53 "avx512cd", 54 "avx512dq", 55 "avx512vl", 56 ], 57] 58 59# Assumes externally launched process such as 60# 61# qemu-system-x86_64 -qmp unix:/tmp/qmp,server,nowait -display none -accel kvm 62# 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. 66 67 68sock = sys.argv[1] 69cmd = sys.argv[2] 70shell = qmp.QEMUMonitorProtocol(sock) 71shell.connect() 72 73models = shell.cmd("query-cpu-definitions") 74 75# These QMP props don't correspond to CPUID fatures 76# so ignore them 77skip = [ 78 "family", 79 "min-level", 80 "min-xlevel", 81 "vendor", 82 "model", 83 "model-id", 84 "stepping", 85] 86 87names = [] 88 89for model in models["return"]: 90 if "alias-of" in model: 91 continue 92 names.append(model["name"]) 93 94models = {} 95 96for name in sorted(names): 97 cpu = shell.cmd("query-cpu-model-expansion", 98 { "type": "static", 99 "model": { "name": name }}) 100 101 got = {} 102 for (feature, present) in cpu["return"]["model"]["props"].items(): 103 if present and feature not in skip: 104 got[feature] = True 105 106 if name in ["host", "max", "base"]: 107 continue 108 109 models[name] = { 110 # Dict of all present features in this CPU model 111 "features": got, 112 113 # Whether each x86-64 ABI level is satisfied 114 "levels": [False, False, False, False], 115 116 # Number of extra CPUID features compared to the x86-64 ABI level 117 "distance":[-1, -1, -1, -1], 118 119 # CPUID features present in model, but not in ABI level 120 "delta":[[], [], [], []], 121 122 # CPUID features in ABI level but not present in model 123 "missing": [[], [], [], []], 124 } 125 126 127# Calculate whether the CPU models satisfy each ABI level 128for 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 138 139# Cache list of CPU models satisfying each ABI level 140abi_models = [ 141 [], 142 [], 143 [], 144 [], 145] 146 147for name in models.keys(): 148 for level in range(len(levels)): 149 if models[name]["levels"][level]: 150 abi_models[level].append(name) 151 152 153for 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 159 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) 171 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 177 178 delta = set(models[name]["features"].keys()) - set(commonfeatures) 179 models[name]["distance"][level] = len(delta) 180 models[name]["delta"][level] = delta 181 182def 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() 193 194print_uarch_abi_csv()