1 | #!/usr/bin/env python
|
---|
2 | # -*- coding: utf-8 -*-
|
---|
3 | # $Id: bsd-spec-analyze.py 108904 2025-04-09 00:16:57Z vboxsync $
|
---|
4 | # pylint: disable=invalid-name
|
---|
5 |
|
---|
6 | """
|
---|
7 | ARM BSD specification analyser.
|
---|
8 | """
|
---|
9 |
|
---|
10 | from __future__ import print_function;
|
---|
11 |
|
---|
12 | __copyright__ = \
|
---|
13 | """
|
---|
14 | Copyright (C) 2025 Oracle and/or its affiliates.
|
---|
15 |
|
---|
16 | This file is part of VirtualBox base platform packages, as
|
---|
17 | available from https://www.virtualbox.org.
|
---|
18 |
|
---|
19 | This program is free software; you can redistribute it and/or
|
---|
20 | modify it under the terms of the GNU General Public License
|
---|
21 | as published by the Free Software Foundation, in version 3 of the
|
---|
22 | License.
|
---|
23 |
|
---|
24 | This program is distributed in the hope that it will be useful, but
|
---|
25 | WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
26 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
---|
27 | General Public License for more details.
|
---|
28 |
|
---|
29 | You should have received a copy of the GNU General Public License
|
---|
30 | along with this program; if not, see <https://www.gnu.org/licenses>.
|
---|
31 |
|
---|
32 | SPDX-License-Identifier: GPL-3.0-only
|
---|
33 | """
|
---|
34 | __version__ = "$Revision: 108904 $"
|
---|
35 |
|
---|
36 | # Standard python imports.
|
---|
37 | import argparse;
|
---|
38 | import collections;
|
---|
39 | import datetime;
|
---|
40 | import io;
|
---|
41 | import json;
|
---|
42 | import operator;
|
---|
43 | import os;
|
---|
44 | import re;
|
---|
45 | import sys;
|
---|
46 | import tarfile;
|
---|
47 | import time;
|
---|
48 | import traceback;
|
---|
49 | # profiling:
|
---|
50 | import cProfile;
|
---|
51 | import pstats
|
---|
52 |
|
---|
53 |
|
---|
54 | ## Program start time for logging.
|
---|
55 | g_nsProgStart = int(time.time_ns())
|
---|
56 |
|
---|
57 |
|
---|
58 | ## Mapping from ARM FEAT_xxxx to CPUMFEATURESARMV8 member.
|
---|
59 | #
|
---|
60 | # Sed script for extracting this stuff from cpum.h (-n option, sort output):
|
---|
61 | #
|
---|
62 | # # Match all comment lines with a (FEAT_XXX) in them.
|
---|
63 | # /[ (]FEAT_[A-Z].*\*\//!d
|
---|
64 | #
|
---|
65 | # # Extract the feature string, quote it for dict key and pad to the value column.
|
---|
66 | # s,^.*[ (]\(FEAT_[A-Z][^ )]*\)[ )].*\*\/.*$,'\1':,
|
---|
67 | # :morepadding
|
---|
68 | # s/$/ /
|
---|
69 | # /^................................/!b morepadding
|
---|
70 | #
|
---|
71 | # # Load the next line with the value, extract the member name and quote it for dict value.
|
---|
72 | # N
|
---|
73 | # s/\n *uint32_t *\(f[a-zA-Z0-9][a-zA-Z0-9_]*\) * :.*$/'\1',/
|
---|
74 | # p
|
---|
75 | #
|
---|
76 | g_dSpecFeatToCpumFeat = {
|
---|
77 | # Missing ones added manually.
|
---|
78 | 'FEAT_CMPBR': False, ##@todo 'fCmpBr',
|
---|
79 | 'FEAT_CPA': False, ##@todo 'fCpa',
|
---|
80 | 'FEAT_F8F16MM': False, ##@todo 'fF8F16mm',
|
---|
81 | 'FEAT_F8F32MM': False, ##@todo 'fF8F32mm',
|
---|
82 | 'FEAT_FAMINMAX': False, ##@todo 'fFaMinMax',
|
---|
83 | 'FEAT_FP8': False, ##@todo 'fFp8',
|
---|
84 | 'FEAT_FP8DOT2': False, ##@todo 'fFp8Dot2',
|
---|
85 | 'FEAT_FP8DOT4': False, ##@todo 'fFp8Dot4',
|
---|
86 | 'FEAT_FP8FMA': False, ##@todo 'fFp8Fma',
|
---|
87 | 'FEAT_FPRCVT': False, ##@todo 'fFpRcvt',
|
---|
88 | 'FEAT_LSFE': False, ##@todo 'fLsfe',
|
---|
89 | 'FEAT_LSUI': False, ##@todo 'fLsui',
|
---|
90 | 'FEAT_LUT': False, ##@todo 'fLut',
|
---|
91 | 'FEAT_PAuth_LR': False, ##@todo 'fPAuthLR',
|
---|
92 | 'FEAT_PCDPHINT': False, ##@todo 'fPCDPHint',
|
---|
93 | 'FEAT_SME2p2': False, ##@todo 'fSme2p2',
|
---|
94 | 'FEAT_SSVE_FP8DOT2': False, ##@todo 'fSsveFp8Dot2',
|
---|
95 | 'FEAT_SSVE_FP8DOT4': False, ##@todo 'fSsveFp8Dot4',
|
---|
96 | 'FEAT_SVE2p2': False, ##@todo 'fSve2p2',
|
---|
97 | 'FEAT_SVE_BFSCALE': False, ##@todo 'fSveBfscale',
|
---|
98 | 'FEAT_SVE_F16F32MM': False, ##@todo 'fSveF16F32mm',
|
---|
99 | 'FEAT_SME_B16B16': False, ##@todo 'fSmeB16B16',
|
---|
100 | 'FEAT_SME_F8F16': False, ##@todo 'fSmeF8F16',
|
---|
101 | 'FEAT_SME_F8F32': False, ##@todo 'fSmeF8F32',
|
---|
102 |
|
---|
103 | # Generated by sed + sort:
|
---|
104 | 'FEAT_AA32BF16': 'fAa32Bf16',
|
---|
105 | 'FEAT_AA32HPD': 'fAa32Hpd',
|
---|
106 | 'FEAT_AA32I8MM': 'fAa32I8mm',
|
---|
107 | 'FEAT_ABLE': 'fAble',
|
---|
108 | 'FEAT_ADERR': 'fAderr',
|
---|
109 | 'FEAT_AdvSIMD': 'fAdvSimd',
|
---|
110 | 'FEAT_AES': 'fAes',
|
---|
111 | 'FEAT_AFP': 'fAfp',
|
---|
112 | 'FEAT_AIE': 'fAie',
|
---|
113 | 'FEAT_AMUv1': 'fAmuV1',
|
---|
114 | 'FEAT_AMUv1p1': 'fAmuV1p1',
|
---|
115 | 'FEAT_ANERR': 'fAnerr',
|
---|
116 | 'FEAT_BBM': 'fBbm',
|
---|
117 | 'FEAT_BF16': 'fBf16',
|
---|
118 | 'FEAT_BRBE': 'fBrbe',
|
---|
119 | 'FEAT_BRBEv1p1': 'fBrbeV1p1',
|
---|
120 | 'FEAT_BTI': 'fBti',
|
---|
121 | 'FEAT_BWE': 'fBwe',
|
---|
122 | 'FEAT_CCIDX': 'fCcidx',
|
---|
123 | 'FEAT_CHK': 'fChk',
|
---|
124 | 'FEAT_CLRBHB': 'fClrBhb',
|
---|
125 | 'FEAT_CMOW': 'fCmow',
|
---|
126 | 'FEAT_CNTSC': 'fCntsc',
|
---|
127 | 'FEAT_CONSTPACFIELD': 'fConstPacField',
|
---|
128 | 'FEAT_CP15DISABLE2': 'fCp15Disable2',
|
---|
129 | 'FEAT_CRC32': 'fCrc32',
|
---|
130 | 'FEAT_CSSC': 'fCssc',
|
---|
131 | 'FEAT_CSV2': 'fCsv2',
|
---|
132 | 'FEAT_CSV2_1p1': 'fCsv21p1',
|
---|
133 | 'FEAT_CSV2_1p2': 'fCsv21p2',
|
---|
134 | 'FEAT_CSV2_3': 'fCsv2v3',
|
---|
135 | 'FEAT_CSV3': 'fCsv3',
|
---|
136 | 'FEAT_D128': 'fD128',
|
---|
137 | 'FEAT_Debugv8p1': 'fDebugV8p1',
|
---|
138 | 'FEAT_Debugv8p2': 'fDebugV8p2',
|
---|
139 | 'FEAT_Debugv8p4': 'fDebugV8p4',
|
---|
140 | 'FEAT_Debugv8p8': 'fDebugV8p8',
|
---|
141 | 'FEAT_Debugv8p9': 'fDebugV8p9',
|
---|
142 | 'FEAT_DGH': 'fDgh',
|
---|
143 | 'FEAT_DIT': 'fDit',
|
---|
144 | 'FEAT_DoPD': 'fDopd',
|
---|
145 | 'FEAT_DotProd': 'fDotProd',
|
---|
146 | 'FEAT_DoubleFault': 'fDoubleFault',
|
---|
147 | 'FEAT_DoubleFault2': 'fDoubleFault2',
|
---|
148 | 'FEAT_DoubleLock': 'fDoubleLock',
|
---|
149 | 'FEAT_DPB': 'fDpb',
|
---|
150 | 'FEAT_DPB2': 'fDpb2',
|
---|
151 | 'FEAT_E0PD': 'fE0Pd',
|
---|
152 | 'FEAT_EBEP': 'fEbep',
|
---|
153 | 'FEAT_EBF16': 'fEbf16',
|
---|
154 | 'FEAT_ECBHB': 'fEcBhb',
|
---|
155 | 'FEAT_ECV': 'fEcv',
|
---|
156 | 'FEAT_EDHSR': 'fEdhsr',
|
---|
157 | 'FEAT_EPAC': 'fEpac',
|
---|
158 | 'FEAT_ETE': 'fEte',
|
---|
159 | 'FEAT_ETEv1p1': 'fEteV1p1',
|
---|
160 | 'FEAT_ETEv1p2': 'fEteV1p2',
|
---|
161 | 'FEAT_ETEv1p3': 'fEteV1p3',
|
---|
162 | 'FEAT_ETMv4': 'fEtmV4',
|
---|
163 | 'FEAT_ETMv4p1': 'fEtmV4p1',
|
---|
164 | 'FEAT_ETMv4p2': 'fEtmV4p2',
|
---|
165 | 'FEAT_ETMv4p3': 'fEtmV4p3',
|
---|
166 | 'FEAT_ETMv4p4': 'fEtmV4p4',
|
---|
167 | 'FEAT_ETMv4p5': 'fEtmV4p5',
|
---|
168 | 'FEAT_ETMv4p6': 'fEtmV4p6',
|
---|
169 | 'FEAT_ETS2': 'fEts2',
|
---|
170 | 'FEAT_EVT': 'fEvt',
|
---|
171 | 'FEAT_ExS': 'fExs',
|
---|
172 | 'FEAT_F32MM': 'fF32mm',
|
---|
173 | 'FEAT_F64MM': 'fF64mm',
|
---|
174 | 'FEAT_FCMA': 'fFcma',
|
---|
175 | 'FEAT_FGT': 'fFgt',
|
---|
176 | 'FEAT_FGT2': 'fFgt2',
|
---|
177 | 'FEAT_FHM': 'fFhm',
|
---|
178 | 'FEAT_FlagM': 'fFlagM',
|
---|
179 | 'FEAT_FlagM2': 'fFlagM2',
|
---|
180 | 'FEAT_FP': 'fFp',
|
---|
181 | 'FEAT_FP16': 'fFp16',
|
---|
182 | 'FEAT_FPAC': 'fFpac',
|
---|
183 | 'FEAT_FPACCOMBINE': 'fFpacCombine',
|
---|
184 | 'FEAT_FRINTTS': 'fFrintts',
|
---|
185 | 'FEAT_GCS': 'fGcs',
|
---|
186 | 'FEAT_GICv3': 'fGicV3',
|
---|
187 | 'FEAT_GICv3_NMI': 'fGicV3Nmi',
|
---|
188 | 'FEAT_GICv3_TDIR': 'fGicV3Tdir',
|
---|
189 | 'FEAT_GICv3p1': 'fGicV3p1',
|
---|
190 | 'FEAT_GICv4': 'fGicV4',
|
---|
191 | 'FEAT_GICv4p1': 'fGicV4p1',
|
---|
192 | 'FEAT_GTG': 'fGtg',
|
---|
193 | 'FEAT_HAFDBS': 'fHafdbs',
|
---|
194 | 'FEAT_HAFT': 'fHaft',
|
---|
195 | 'FEAT_HBC': 'fHbc',
|
---|
196 | 'FEAT_HCX': 'fHcx',
|
---|
197 | 'FEAT_HPDS': 'fHpds',
|
---|
198 | 'FEAT_HPDS2': 'fHpds2',
|
---|
199 | 'FEAT_HPMN0': 'fHpmn0',
|
---|
200 | 'FEAT_I8MM': 'fI8mm',
|
---|
201 | 'FEAT_IDST': 'fIdst',
|
---|
202 | 'FEAT_IESB': 'fIesb',
|
---|
203 | 'FEAT_ITE': 'fIte',
|
---|
204 | 'FEAT_IVIPT': 'fIvipt',
|
---|
205 | 'FEAT_JSCVT': 'fJscvt',
|
---|
206 | 'FEAT_LOR': 'fLor',
|
---|
207 | 'FEAT_LPA': 'fLpa',
|
---|
208 | 'FEAT_LPA2': 'fLpa2',
|
---|
209 | 'FEAT_LRCPC': 'fLrcpc',
|
---|
210 | 'FEAT_LRCPC2': 'fLrcpc2',
|
---|
211 | 'FEAT_LRCPC3': 'fLrcpc3',
|
---|
212 | 'FEAT_LS64': 'fLs64',
|
---|
213 | 'FEAT_LS64_ACCDATA': 'fLs64Accdata',
|
---|
214 | 'FEAT_LS64_V': 'fLs64V',
|
---|
215 | 'FEAT_LSE': 'fLse',
|
---|
216 | 'FEAT_LSE128': 'fLse128',
|
---|
217 | 'FEAT_LSE2': 'fLse2',
|
---|
218 | 'FEAT_LSMAOC': 'fLsmaoc',
|
---|
219 | 'FEAT_LVA': 'fLva',
|
---|
220 | 'FEAT_LVA3': 'fLva3',
|
---|
221 | 'FEAT_MEC': 'fMec',
|
---|
222 | 'FEAT_MOPS': 'fMops',
|
---|
223 | 'FEAT_MPAM': 'fMpam',
|
---|
224 | 'FEAT_MPAMv0p1': 'fMpamV0p1',
|
---|
225 | 'FEAT_MPAMv1p1': 'fMpamV1p1',
|
---|
226 | 'FEAT_MTE': 'fMte',
|
---|
227 | 'FEAT_MTE_ASYM_FAULT': 'fMteAsymFault',
|
---|
228 | 'FEAT_MTE_ASYNC': 'fMteAsync',
|
---|
229 | 'FEAT_MTE_CANONCIAL_TAGS': 'fMteCanonicalTags',
|
---|
230 | 'FEAT_MTE_NO_ADDRESS_TAGS': 'fMteNoAddressTags',
|
---|
231 | 'FEAT_MTE_PERM_S1': 'fMtePermS1',
|
---|
232 | 'FEAT_MTE_STORE_ONLY': 'fMteStoreOnly',
|
---|
233 | 'FEAT_MTE_TAGGED_FAR': 'fMteTaggedFar',
|
---|
234 | 'FEAT_MTE2': 'fMte2',
|
---|
235 | 'FEAT_MTE3': 'fMte3',
|
---|
236 | 'FEAT_MTE4': 'fMte4',
|
---|
237 | 'FEAT_MTPMU': 'fMtPmu',
|
---|
238 | 'FEAT_NMI': 'fNmi',
|
---|
239 | 'FEAT_NV': 'fNv',
|
---|
240 | 'FEAT_NV2': 'fNv2',
|
---|
241 | 'FEAT_PACIMP': 'fPacImp',
|
---|
242 | 'FEAT_PACQARMA3': 'fPacQarma3',
|
---|
243 | 'FEAT_PACQARMA5': 'fPacQarma5',
|
---|
244 | 'FEAT_PAN': 'fPan',
|
---|
245 | 'FEAT_PAN2': 'fPan2',
|
---|
246 | 'FEAT_PAN3': 'fPan3',
|
---|
247 | 'FEAT_PAuth': 'fPAuth',
|
---|
248 | 'FEAT_PAuth2': 'fPAuth2',
|
---|
249 | 'FEAT_PCSRv8': 'fPcsrV8',
|
---|
250 | 'FEAT_PCSRv8p2': 'fPcsrV8p2',
|
---|
251 | 'FEAT_PCSRv8p9': 'fPcsrV8p9',
|
---|
252 | 'FEAT_PFAR': 'fPfar',
|
---|
253 | 'FEAT_PMULL': 'fPmull',
|
---|
254 | 'FEAT_PMUv3': 'fPmuV3',
|
---|
255 | 'FEAT_PMUv3_EDGE': 'fPmuV3Edge',
|
---|
256 | 'FEAT_PMUv3_EXT': 'fPmuV3Ext',
|
---|
257 | 'FEAT_PMUv3_EXT32': 'fPmuV3Ext32',
|
---|
258 | 'FEAT_PMUv3_EXT64': 'fPmuV3Ext64',
|
---|
259 | 'FEAT_PMUv3_ICNTR': 'fPmuV3Icntr',
|
---|
260 | 'FEAT_PMUv3_SS': 'fPmuV3Ss',
|
---|
261 | 'FEAT_PMUv3_TH': 'fPmuV3Th',
|
---|
262 | 'FEAT_PMUv3p1': 'fPmuV3p1',
|
---|
263 | 'FEAT_PMUv3p4': 'fPmuV3p4',
|
---|
264 | 'FEAT_PMUv3p5': 'fPmuV3p5',
|
---|
265 | 'FEAT_PMUv3p7': 'fPmuV3p7',
|
---|
266 | 'FEAT_PMUv3p8': 'fPmuV3p8',
|
---|
267 | 'FEAT_PMUv3p9': 'fPmuV3p9',
|
---|
268 | 'FEAT_PRFMSLC': 'fPrfmSlc',
|
---|
269 | 'FEAT_RAS': 'fRas',
|
---|
270 | 'FEAT_RASSAv1p1': 'fRassaV1p1',
|
---|
271 | 'FEAT_RASSAv2': 'fRasSaV2',
|
---|
272 | 'FEAT_RASv1p1': 'fRasV1p1',
|
---|
273 | 'FEAT_RASv2': 'fRasV2',
|
---|
274 | 'FEAT_RDM': 'fRdm',
|
---|
275 | 'FEAT_RME': 'fRme',
|
---|
276 | 'FEAT_RNG': 'fRng',
|
---|
277 | 'FEAT_RNG_TRAP': 'fRngTrap',
|
---|
278 | 'FEAT_RPRES': 'fRpres',
|
---|
279 | 'FEAT_RPRFM': 'fRprfm',
|
---|
280 | 'FEAT_S1PIE': 'fS1Pie',
|
---|
281 | 'FEAT_S1POE': 'fS1Poe',
|
---|
282 | 'FEAT_S2FWB': 'fS2Fwb',
|
---|
283 | 'FEAT_S2PIE': 'fS2Pie',
|
---|
284 | 'FEAT_S2POE': 'fS2Poe',
|
---|
285 | 'FEAT_SB': 'fSb',
|
---|
286 | 'FEAT_SCTLR2': 'fSctlr2',
|
---|
287 | 'FEAT_SEBEP': 'fSebep',
|
---|
288 | 'FEAT_SEL2': 'fSecEl2',
|
---|
289 | 'FEAT_SHA1': 'fSha1',
|
---|
290 | 'FEAT_SHA256': 'fSha256',
|
---|
291 | 'FEAT_SHA3': 'fSha3',
|
---|
292 | 'FEAT_SHA512': 'fSha512',
|
---|
293 | 'FEAT_SM3': 'fSm3',
|
---|
294 | 'FEAT_SM4': 'fSm4',
|
---|
295 | 'FEAT_SME': 'fSme',
|
---|
296 | 'FEAT_SME_F16F16': 'fSmeF16F16',
|
---|
297 | 'FEAT_SME_F64F64': 'fSmeF64F64',
|
---|
298 | 'FEAT_SME_FA64': 'fSmeFA64',
|
---|
299 | 'FEAT_SME_I16I64': 'fSmeI16I64',
|
---|
300 | 'FEAT_SME2': 'fSme2',
|
---|
301 | 'FEAT_SME2p1': 'fSme2p1',
|
---|
302 | 'FEAT_SPE': 'fSpe',
|
---|
303 | 'FEAT_SPE_CRR': 'fSpeCrr',
|
---|
304 | 'FEAT_SPE_FDS': 'fSpeFds',
|
---|
305 | 'FEAT_SPECRES': 'fSpecres',
|
---|
306 | 'FEAT_SPECRES2': 'fSpecres2',
|
---|
307 | 'FEAT_SPEv1p1': 'fSpeV1p1',
|
---|
308 | 'FEAT_SPEv1p2': 'fSpeV1p2',
|
---|
309 | 'FEAT_SPEv1p3': 'fSpeV1p3',
|
---|
310 | 'FEAT_SPEv1p4': 'fSpeV1p4',
|
---|
311 | 'FEAT_SPMU': 'fSpmu',
|
---|
312 | 'FEAT_SSBS': 'fSsbs',
|
---|
313 | 'FEAT_SSBS2': 'fSsbs2',
|
---|
314 | 'FEAT_SVE': 'fSve',
|
---|
315 | 'FEAT_SVE_AES': 'fSveAes',
|
---|
316 | 'FEAT_SVE_B16B16': 'fSveB16B16',
|
---|
317 | 'FEAT_SVE_BitPerm': 'fSveBitPerm',
|
---|
318 | 'FEAT_SVE_PMULL128': 'fSvePmull128',
|
---|
319 | 'FEAT_SVE_SHA3': 'fSveSha3',
|
---|
320 | 'FEAT_SVE_SM4': 'fSveSm4',
|
---|
321 | 'FEAT_SVE2': 'fSve2',
|
---|
322 | 'FEAT_SVE2p1': 'fSve2p1',
|
---|
323 | 'FEAT_SYSINSTR128': 'fSysInstr128',
|
---|
324 | 'FEAT_SYSREG128': 'fSysReg128',
|
---|
325 | 'FEAT_TCR2': 'fTcr2',
|
---|
326 | 'FEAT_THE': 'fThe',
|
---|
327 | 'FEAT_TIDCP1': 'fTidcp1',
|
---|
328 | 'FEAT_TLBIOS': 'fTlbios',
|
---|
329 | 'FEAT_TLBIRANGE': 'fTlbirange',
|
---|
330 | 'FEAT_TME': 'fTme',
|
---|
331 | 'FEAT_TRBE': 'fTrbe',
|
---|
332 | 'FEAT_TRBE_EXT': 'fTrbeExt',
|
---|
333 | 'FEAT_TRBE_MPAM': 'fTrbeMpam',
|
---|
334 | 'FEAT_TRF': 'fTrf',
|
---|
335 | 'FEAT_TTCNP': 'fTtcnp',
|
---|
336 | 'FEAT_TTL': 'fTtl',
|
---|
337 | 'FEAT_TTST': 'fTtst',
|
---|
338 | 'FEAT_TWED': 'fTwed',
|
---|
339 | 'FEAT_UAO': 'fUao',
|
---|
340 | 'FEAT_VHE': 'fVhe',
|
---|
341 | 'FEAT_VMID16': 'fVmid16',
|
---|
342 | 'FEAT_VPIPT': 'fVpipt',
|
---|
343 | 'FEAT_WFxT': 'fWfxt',
|
---|
344 | 'FEAT_XNX': 'fXnx',
|
---|
345 | 'FEAT_XS': 'fXs',
|
---|
346 | };
|
---|
347 |
|
---|
348 |
|
---|
349 | #
|
---|
350 | # The ARM instruction AST stuff.
|
---|
351 | #
|
---|
352 |
|
---|
353 | class ArmAstBase(object):
|
---|
354 | """
|
---|
355 | ARM instruction AST base class.
|
---|
356 | """
|
---|
357 |
|
---|
358 | ksTypeBinaryOp = 'AST.BinaryOp';
|
---|
359 | ksTypeBool = 'AST.Bool';
|
---|
360 | ksTypeConcat = 'AST.Concat';
|
---|
361 | ksTypeFunction = 'AST.Function';
|
---|
362 | ksTypeIdentifier = 'AST.Identifier';
|
---|
363 | ksTypeInteger = 'AST.Integer';
|
---|
364 | ksTypeSet = 'AST.Set';
|
---|
365 | ksTypeSquareOp = 'AST.SquareOp';
|
---|
366 | ksTypeUnaryOp = 'AST.UnaryOp';
|
---|
367 | ksTypeValue = 'Values.Value';
|
---|
368 |
|
---|
369 | def __init__(self, sType):
|
---|
370 | self.sType = sType;
|
---|
371 |
|
---|
372 | @staticmethod
|
---|
373 | def assertAttribsInSet(oJson, oAttribSet):
|
---|
374 | """ Checks that the JSON element has all the attributes in the set and nothing else. """
|
---|
375 | assert set(oJson) == oAttribSet, '%s - %s' % (set(oJson) ^ oAttribSet, oJson,);
|
---|
376 |
|
---|
377 | kAttribSetBinaryOp = frozenset(['_type', 'left', 'op', 'right']);
|
---|
378 | @staticmethod
|
---|
379 | def fromJsonBinaryOp(oJson):
|
---|
380 | ArmAstBase.assertAttribsInSet(oJson, ArmAstBase.kAttribSetBinaryOp);
|
---|
381 | return ArmAstBinaryOp(ArmAstBase.fromJson(oJson['left']), oJson['op'], ArmAstBase.fromJson(oJson['right']));
|
---|
382 |
|
---|
383 | kAttribSetUnaryOp = frozenset(['_type', 'op', 'expr']);
|
---|
384 | @staticmethod
|
---|
385 | def fromJsonUnaryOp(oJson):
|
---|
386 | ArmAstBase.assertAttribsInSet(oJson, ArmAstBase.kAttribSetUnaryOp);
|
---|
387 | return ArmAstUnaryOp(oJson['op'], ArmAstBase.fromJson(oJson['expr']));
|
---|
388 |
|
---|
389 | kAttribSetSquareOp = frozenset(['_type', 'var', 'arguments']);
|
---|
390 | @staticmethod
|
---|
391 | def fromJsonSquareOp(oJson):
|
---|
392 | ArmAstBase.assertAttribsInSet(oJson, ArmAstBase.kAttribSetSquareOp);
|
---|
393 | return ArmAstSquareOp(ArmAstBase.fromJson(oJson['var']), [ArmAstBase.fromJson(oArg) for oArg in oJson['arguments']]);
|
---|
394 |
|
---|
395 | kAttribSetConcat = frozenset(['_type', 'values']);
|
---|
396 | @staticmethod
|
---|
397 | def fromJsonConcat(oJson):
|
---|
398 | ArmAstBase.assertAttribsInSet(oJson, ArmAstBase.kAttribSetConcat);
|
---|
399 | return ArmAstConcat([ArmAstBase.fromJson(oArg) for oArg in oJson['values']]);
|
---|
400 |
|
---|
401 | kAttribSetFunction = frozenset(['_type', 'name', 'arguments']);
|
---|
402 | @staticmethod
|
---|
403 | def fromJsonFunction(oJson):
|
---|
404 | ArmAstBase.assertAttribsInSet(oJson, ArmAstBase.kAttribSetFunction);
|
---|
405 | return ArmAstFunction(oJson['name'], [ArmAstBase.fromJson(oArg) for oArg in oJson['arguments']]);
|
---|
406 |
|
---|
407 | kAttribSetIdentifier = frozenset(['_type', 'value']);
|
---|
408 | @staticmethod
|
---|
409 | def fromJsonIdentifier(oJson):
|
---|
410 | ArmAstBase.assertAttribsInSet(oJson, ArmAstBase.kAttribSetIdentifier);
|
---|
411 | return ArmAstIdentifier(oJson['value']);
|
---|
412 |
|
---|
413 | kAttribSetBool = frozenset(['_type', 'value']);
|
---|
414 | @staticmethod
|
---|
415 | def fromJsonBool(oJson):
|
---|
416 | ArmAstBase.assertAttribsInSet(oJson, ArmAstBase.kAttribSetBool);
|
---|
417 | return ArmAstBool(oJson['value']);
|
---|
418 |
|
---|
419 | kAttribSetInteger = frozenset(['_type', 'value']);
|
---|
420 | @staticmethod
|
---|
421 | def fromJsonInteger(oJson):
|
---|
422 | ArmAstBase.assertAttribsInSet(oJson, ArmAstBase.kAttribSetInteger);
|
---|
423 | return ArmAstInteger(oJson['value']);
|
---|
424 |
|
---|
425 | kAttribSetSet = frozenset(['_type', 'values']);
|
---|
426 | @staticmethod
|
---|
427 | def fromJsonSet(oJson):
|
---|
428 | ArmAstBase.assertAttribsInSet(oJson, ArmAstBase.kAttribSetSet);
|
---|
429 | return ArmAstSet([ArmAstBase.fromJson(oArg) for oArg in oJson['values']]);
|
---|
430 |
|
---|
431 | kAttribSetValue = frozenset(['_type', 'value', 'meaning']);
|
---|
432 | @staticmethod
|
---|
433 | def fromJsonValue(oJson):
|
---|
434 | ArmAstBase.assertAttribsInSet(oJson, ArmAstBase.kAttribSetValue);
|
---|
435 | return ArmAstValue(oJson['value']);
|
---|
436 |
|
---|
437 | kfnTypeMap = {
|
---|
438 | ksTypeBinaryOp: fromJsonBinaryOp,
|
---|
439 | ksTypeUnaryOp: fromJsonUnaryOp,
|
---|
440 | ksTypeSquareOp: fromJsonSquareOp,
|
---|
441 | ksTypeConcat: fromJsonConcat,
|
---|
442 | ksTypeFunction: fromJsonFunction,
|
---|
443 | ksTypeIdentifier: fromJsonIdentifier,
|
---|
444 | ksTypeBool: fromJsonBool,
|
---|
445 | ksTypeInteger: fromJsonInteger,
|
---|
446 | ksTypeSet: fromJsonSet,
|
---|
447 | ksTypeValue: fromJsonValue,
|
---|
448 | };
|
---|
449 |
|
---|
450 | @staticmethod
|
---|
451 | def fromJson(oJson):
|
---|
452 | """ Decodes an AST/Values expression. """
|
---|
453 | #print('debug ast: %s' % oJson['_type'])
|
---|
454 | return ArmAstBase.kfnTypeMap[oJson['_type']](oJson);
|
---|
455 |
|
---|
456 | def isBoolAndTrue(self):
|
---|
457 | """ Check if this is a boolean with the value True. """
|
---|
458 | #return isinstance(self, ArmAstBool) and self.fValue is True;
|
---|
459 | if isinstance(self, ArmAstBool):
|
---|
460 | return self.fValue is True;
|
---|
461 | return False;
|
---|
462 |
|
---|
463 | def toString(self):
|
---|
464 | return 'todo<%s>' % (self.sType,);
|
---|
465 |
|
---|
466 | def __str__(self):
|
---|
467 | return self.toString();
|
---|
468 |
|
---|
469 | def __repr__(self):
|
---|
470 | return self.toString();
|
---|
471 |
|
---|
472 |
|
---|
473 | class ArmAstBinaryOp(ArmAstBase):
|
---|
474 | kOpTypeCompare = 'cmp';
|
---|
475 | kOpTypeLogical = 'log';
|
---|
476 | kOpTypeArithmetical = 'arit';
|
---|
477 | kOpTypeSet = 'set';
|
---|
478 | kdOps = {
|
---|
479 | '||': kOpTypeLogical,
|
---|
480 | '&&': kOpTypeLogical,
|
---|
481 | '==': kOpTypeCompare,
|
---|
482 | '!=': kOpTypeCompare,
|
---|
483 | '>': kOpTypeCompare,
|
---|
484 | '>=': kOpTypeCompare,
|
---|
485 | '<=': kOpTypeCompare,
|
---|
486 | 'IN': kOpTypeSet,
|
---|
487 | '+': kOpTypeArithmetical,
|
---|
488 | };
|
---|
489 |
|
---|
490 | def __init__(self, oLeft, sOp, oRight):
|
---|
491 | ArmAstBase.__init__(self, ArmAstBase.ksTypeBinaryOp);
|
---|
492 | assert sOp in ArmAstBinaryOp.kdOps, 'sOp="%s"' % (sOp,);
|
---|
493 | self.oLeft = oLeft;
|
---|
494 | self.sOp = sOp;
|
---|
495 | self.oRight = oRight;
|
---|
496 |
|
---|
497 | # Switch value == field non-sense (simplifies transferConditionsToEncoding and such):
|
---|
498 | if ( isinstance(oRight, ArmAstIdentifier)
|
---|
499 | and isinstance(oLeft, (ArmAstValue, ArmAstInteger))
|
---|
500 | and sOp in ['==', '!=']):
|
---|
501 | self.oLeft = oRight;
|
---|
502 | self.oRight = oLeft;
|
---|
503 |
|
---|
504 | @staticmethod
|
---|
505 | def needParentheses(oNode, sOp = '&&'):
|
---|
506 | if isinstance(oNode, ArmAstBinaryOp):
|
---|
507 | if sOp != '&&' or oNode.sOp in ('||', '+'):
|
---|
508 | return True;
|
---|
509 | return False;
|
---|
510 |
|
---|
511 | def toString(self):
|
---|
512 | sLeft = self.oLeft.toString();
|
---|
513 | if ArmAstBinaryOp.needParentheses(self.oLeft, self.sOp):
|
---|
514 | sLeft = '(%s)' % (sLeft);
|
---|
515 |
|
---|
516 | sRight = self.oRight.toString();
|
---|
517 | if ArmAstBinaryOp.needParentheses(self.oRight, self.sOp):
|
---|
518 | sRight = '(%s)' % (sRight);
|
---|
519 |
|
---|
520 | return '%s %s %s' % (sLeft, self.sOp, sRight);
|
---|
521 |
|
---|
522 | def toCExpr(self, oHelper):
|
---|
523 | # Logical and compare operations are straight forward.
|
---|
524 | if ArmAstBinaryOp.kdOps[self.sOp] in (ArmAstBinaryOp.kOpTypeLogical, ArmAstBinaryOp.kOpTypeCompare):
|
---|
525 | sLeft = self.oLeft.toCExpr(oHelper);
|
---|
526 | if ArmAstBinaryOp.needParentheses(self.oLeft, self.sOp):
|
---|
527 | sLeft = '(%s)' % (sLeft);
|
---|
528 |
|
---|
529 | sRight = self.oRight.toCExpr(oHelper);
|
---|
530 | if ArmAstBinaryOp.needParentheses(self.oRight, self.sOp):
|
---|
531 | sRight = '(%s)' % (sRight);
|
---|
532 | return '%s %s %s' % (sLeft, self.sOp, sRight);
|
---|
533 |
|
---|
534 | # 'x IN (y,z,...)' needs rewriting.
|
---|
535 | if self.sOp == 'IN':
|
---|
536 | if not isinstance(self.oLeft, ArmAstIdentifier):
|
---|
537 | raise Exception('Unsupported left operand to IN operator: %s' % (self.toString(),));
|
---|
538 | if not isinstance(self.oRight, ArmAstSet):
|
---|
539 | raise Exception('Unsupported right operand to IN operator: %s' % (self.toString(),));
|
---|
540 | (sCName, cBitsWidth) = oHelper.getFieldInfo(self.oLeft.sName);
|
---|
541 |
|
---|
542 | asTests = [];
|
---|
543 | for oValue in self.oRight.aoValues:
|
---|
544 | if isinstance(oValue, ArmAstValue):
|
---|
545 | (fValue, fFixed, fWildcard) = ArmEncodesetField.parseValue(oValue.sValue, cBitsWidth);
|
---|
546 | fCombined = fValue | fFixed | fWildcard;
|
---|
547 | if fCombined < 0 or fCombined >= (1 << cBitsWidth):
|
---|
548 | raise Exception('Set value out of range: %s, width %u bits (expr: %s)'
|
---|
549 | % (oValue.toString(), cBitsWidth, self.toString(),));
|
---|
550 | if fFixed == ((1 << cBitsWidth) - 1):
|
---|
551 | if fValue < 10:
|
---|
552 | asTests.append('%s == %u' % (sCName, fValue,));
|
---|
553 | elif fValue < (1 << 31):
|
---|
554 | asTests.append('%s == %#x' % (sCName, fValue,));
|
---|
555 | else:
|
---|
556 | asTests.append('%s == UINT32_C(%#010x)' % (sCName, fValue,));
|
---|
557 | else:
|
---|
558 | if fFixed < 10:
|
---|
559 | asTests.append('(%s & %u) == %u' % (sCName, fFixed, fValue,));
|
---|
560 | elif fFixed < (1 << 31):
|
---|
561 | asTests.append('(%s & %#x) == %#x' % (sCName, fFixed, fValue,));
|
---|
562 | else:
|
---|
563 | asTests.append('(%s & %#010x) == UINT32_C(%#010x)' % (sCName, fFixed, fValue,));
|
---|
564 | elif isinstance(oValue, ArmAstInteger):
|
---|
565 | if oValue.iValue < 0 or oValue.iValue >= (1 << cBitsWidth):
|
---|
566 | raise Exception('Set value out of range: %s, width %u bits (expr: %s)'
|
---|
567 | % (oValue.toString(), cBitsWidth, self.toString(),));
|
---|
568 | asTests.append('(%s == %s)' % (sCName, oValue.toCExpr(oHelper),));
|
---|
569 | else:
|
---|
570 | raise Exception('Unsupported value in set: %s (expr: %s)' % (oValue.toString(), self.toString(),));
|
---|
571 |
|
---|
572 | if len(asTests) == 1:
|
---|
573 | return asTests[0];
|
---|
574 | return '(%s)' % (' || '.join(asTests),);
|
---|
575 |
|
---|
576 |
|
---|
577 | raise Exception('Unsupported binary operator: %s (%s)' % (self.sOp, self.toString(),));
|
---|
578 |
|
---|
579 |
|
---|
580 |
|
---|
581 | class ArmAstUnaryOp(ArmAstBase):
|
---|
582 | kOpTypeLogical = 'log';
|
---|
583 | kdOps = {
|
---|
584 | '!': kOpTypeLogical,
|
---|
585 | };
|
---|
586 |
|
---|
587 | def __init__(self, sOp, oExpr):
|
---|
588 | ArmAstBase.__init__(self, ArmAstBase.ksTypeUnaryOp);
|
---|
589 | assert sOp in ArmAstUnaryOp.kdOps, 'sOp=%s' % (sOp,);
|
---|
590 | self.sOp = sOp;
|
---|
591 | self.oExpr = oExpr;
|
---|
592 |
|
---|
593 | @staticmethod
|
---|
594 | def needParentheses(oNode):
|
---|
595 | return isinstance(oNode, ArmAstBinaryOp)
|
---|
596 |
|
---|
597 | def toString(self):
|
---|
598 | if ArmAstUnaryOp.needParentheses(self.oExpr):
|
---|
599 | return '%s(%s)' % (self.sOp, self.oExpr.toString(),);
|
---|
600 | return '%s%s' % (self.sOp, self.oExpr.toString(),);
|
---|
601 |
|
---|
602 | def toCExpr(self, oHelper):
|
---|
603 | if ArmAstUnaryOp.needParentheses(self.oExpr):
|
---|
604 | return '%s(%s)' % (self.sOp, self.oExpr.toCExpr(oHelper));
|
---|
605 | return '%s%s' % (self.sOp, self.oExpr.toCExpr(oHelper));
|
---|
606 |
|
---|
607 |
|
---|
608 | class ArmAstSquareOp(ArmAstBase):
|
---|
609 | def __init__(self, oVar, aoValues):
|
---|
610 | ArmAstBase.__init__(self, ArmAstBase.ksTypeSquareOp);
|
---|
611 | self.oVar = oVar;
|
---|
612 | self.aoValues = aoValues;
|
---|
613 |
|
---|
614 | def toString(self):
|
---|
615 | return '%s<%s>' % (self.oVar.toString(), ','.join([oValue.toString() for oValue in self.aoValues]),);
|
---|
616 |
|
---|
617 | def toCExpr(self, oHelper):
|
---|
618 | _ = oHelper;
|
---|
619 | raise Exception('ArmAstSquareOp does not support conversion to C expression: %s' % (self.toString()));
|
---|
620 |
|
---|
621 |
|
---|
622 | class ArmAstConcat(ArmAstBase):
|
---|
623 | def __init__(self, aoValues):
|
---|
624 | ArmAstBase.__init__(self, ArmAstBase.ksTypeConcat);
|
---|
625 | self.aoValues = aoValues;
|
---|
626 |
|
---|
627 | def toString(self):
|
---|
628 | sRet = '';
|
---|
629 | for oValue in self.aoValues:
|
---|
630 | if sRet:
|
---|
631 | sRet += ':'
|
---|
632 | if isinstance(oValue, ArmAstIdentifier):
|
---|
633 | sRet += oValue.sName;
|
---|
634 | else:
|
---|
635 | sRet += '(%s)' % (oValue.toString());
|
---|
636 | return sRet;
|
---|
637 |
|
---|
638 | def toCExpr(self, oHelper):
|
---|
639 | sConcat = '(';
|
---|
640 | iBitPos = 0;
|
---|
641 | for oPart in self.aoValues:
|
---|
642 | if len(sConcat) > 1:
|
---|
643 | sConcat += ' | ';
|
---|
644 | if isinstance(oPart, ArmAstIdentifier):
|
---|
645 | (sCName, cBitsWidth) = oHelper.getFieldInfo(oPart.sName);
|
---|
646 | if iBitPos == 0:
|
---|
647 | sConcat += sCName;
|
---|
648 | else:
|
---|
649 | sConcat += '(%s << %u)' % (sCName, iBitPos);
|
---|
650 | iBitPos += cBitsWidth;
|
---|
651 | else:
|
---|
652 | raise Exception('Unexpected value type for concat(): %s' % (oPart.sType,));
|
---|
653 | sConcat += ')';
|
---|
654 | return sConcat;
|
---|
655 |
|
---|
656 | class ArmAstFunction(ArmAstBase):
|
---|
657 | s_oReValidName = re.compile('^[_A-Za-z][_A-Za-z0-9]+$');
|
---|
658 |
|
---|
659 | def __init__(self, sName, aoArgs):
|
---|
660 | ArmAstBase.__init__(self, ArmAstBase.ksTypeFunction);
|
---|
661 | assert self.s_oReValidName.match(sName), 'sName=%s' % (sName);
|
---|
662 | self.sName = sName;
|
---|
663 | self.aoArgs = aoArgs;
|
---|
664 |
|
---|
665 | def toString(self):
|
---|
666 | return '%s(%s)' % (self.sName, ','.join([oArg.toString() for oArg in self.aoArgs]),);
|
---|
667 |
|
---|
668 | def toCExpr(self, oHelper):
|
---|
669 | return oHelper.convertFunctionCall(self);
|
---|
670 |
|
---|
671 |
|
---|
672 | class ArmAstIdentifier(ArmAstBase):
|
---|
673 | s_oReValidName = re.compile('^[_A-Za-z][_A-Za-z0-9]*$');
|
---|
674 |
|
---|
675 | def __init__(self, sName):
|
---|
676 | ArmAstBase.__init__(self, ArmAstBase.ksTypeIdentifier);
|
---|
677 | assert self.s_oReValidName.match(sName), 'sName=%s' % (sName);
|
---|
678 | self.sName = sName;
|
---|
679 |
|
---|
680 | def toString(self):
|
---|
681 | return self.sName;
|
---|
682 |
|
---|
683 | def toCExpr(self, oHelper):
|
---|
684 | (sCName, _) = oHelper.getFieldInfo(self.sName);
|
---|
685 | return sCName;
|
---|
686 |
|
---|
687 |
|
---|
688 | class ArmAstBool(ArmAstBase):
|
---|
689 | def __init__(self, fValue):
|
---|
690 | ArmAstBase.__init__(self, ArmAstBase.ksTypeBool);
|
---|
691 | assert fValue is True or fValue is False, '%s' % (fValue,);
|
---|
692 | self.fValue = fValue;
|
---|
693 |
|
---|
694 | def toString(self):
|
---|
695 | return 'true' if self.fValue is True else 'false';
|
---|
696 |
|
---|
697 | def toCExpr(self, oHelper):
|
---|
698 | _ = oHelper;
|
---|
699 | return 'true' if self.fValue is True else 'false';
|
---|
700 |
|
---|
701 | class ArmAstInteger(ArmAstBase):
|
---|
702 | def __init__(self, iValue):
|
---|
703 | ArmAstBase.__init__(self, ArmAstBase.ksTypeInteger);
|
---|
704 | self.iValue = int(iValue);
|
---|
705 |
|
---|
706 | def toString(self):
|
---|
707 | return '%#x' % (self.iValue,);
|
---|
708 |
|
---|
709 | def toCExpr(self, oHelper):
|
---|
710 | _ = oHelper;
|
---|
711 | if self.iValue < 10:
|
---|
712 | return '%u' % (self.iValue,);
|
---|
713 | if self.iValue & (1<<31):
|
---|
714 | return 'UINT32_C(%#x)' % (self.iValue,);
|
---|
715 | return '%#x' % (self.iValue,);
|
---|
716 |
|
---|
717 | class ArmAstSet(ArmAstBase):
|
---|
718 | def __init__(self, aoValues):
|
---|
719 | ArmAstBase.__init__(self, ArmAstBase.ksTypeSet);
|
---|
720 | self.aoValues = aoValues;
|
---|
721 |
|
---|
722 | def toString(self):
|
---|
723 | return '(%s)' % (', '.join([oValue.toString() for oValue in self.aoValues]),);
|
---|
724 |
|
---|
725 | def toCExpr(self, oHelper):
|
---|
726 | _ = oHelper;
|
---|
727 | raise Exception('ArmAstSet does not support conversion to C expression: %s' % (self.toString()));
|
---|
728 |
|
---|
729 | class ArmAstValue(ArmAstBase):
|
---|
730 | def __init__(self, sValue):
|
---|
731 | ArmAstBase.__init__(self, ArmAstBase.ksTypeValue);
|
---|
732 | self.sValue = sValue;
|
---|
733 |
|
---|
734 | def toString(self):
|
---|
735 | return self.sValue;
|
---|
736 |
|
---|
737 | def toCExpr(self, oHelper):
|
---|
738 | _ = oHelper;
|
---|
739 | (fValue, _, fWildcard) = ArmEncodesetField.parseValue(self.sValue, 0);
|
---|
740 | if fWildcard:
|
---|
741 | raise Exception('Value contains wildcard elements: %s' % (self.sValue,));
|
---|
742 | if fValue < 10:
|
---|
743 | return '%u' % (fValue,);
|
---|
744 | if fValue & (1<<31):
|
---|
745 | return 'UINT32_C(%#x)' % (fValue,);
|
---|
746 | return '%#x' % (fValue,);
|
---|
747 |
|
---|
748 |
|
---|
749 | #
|
---|
750 | # Instructions and their properties.
|
---|
751 | #
|
---|
752 |
|
---|
753 | class ArmEncodesetField(object):
|
---|
754 | """
|
---|
755 | ARM Encodeset.Bits & Encodeset.Field.
|
---|
756 | """
|
---|
757 | def __init__(self, oJson, iFirstBit, cBitsWidth, fFixed, fValue, sName = None):
|
---|
758 | self.oJson = oJson;
|
---|
759 | self.iFirstBit = iFirstBit;
|
---|
760 | self.cBitsWidth = cBitsWidth;
|
---|
761 | self.fFixed = fFixed;
|
---|
762 | self.fValue = fValue;
|
---|
763 | self.sName = sName; ##< None if Encodeset.Bits.
|
---|
764 |
|
---|
765 | def __str__(self):
|
---|
766 | sRet = '[%2u:%-2u] = %#x/%#x/%#x' % (
|
---|
767 | self.iFirstBit + self.cBitsWidth - 1, self.iFirstBit, self.fValue, self.fFixed, self.getMask()
|
---|
768 | );
|
---|
769 | if self.sName:
|
---|
770 | sRet += ' # %s' % (self.sName,)
|
---|
771 | return sRet;
|
---|
772 |
|
---|
773 | def __repr__(self):
|
---|
774 | return self.__str__();
|
---|
775 |
|
---|
776 | def getMask(self):
|
---|
777 | """ Field mask (unshifted). """
|
---|
778 | return (1 << self.cBitsWidth) - 1;
|
---|
779 |
|
---|
780 | def getShiftedMask(self):
|
---|
781 | """ Field mask, shifted. """
|
---|
782 | return ((1 << self.cBitsWidth) - 1) << self.iFirstBit;
|
---|
783 |
|
---|
784 | @staticmethod
|
---|
785 | def parseValue(sValue, cBitsWidth):
|
---|
786 | """
|
---|
787 | Returns (fValue, fFixed, fWildcard) tuple on success, raises AssertionError otherwise.
|
---|
788 | """
|
---|
789 | assert sValue[0] == '\'' and sValue[-1] == '\'', sValue;
|
---|
790 | sValue = sValue[1:-1];
|
---|
791 | assert not cBitsWidth or len(sValue) == cBitsWidth, 'cBitsWidth=%s sValue=%s' % (cBitsWidth, sValue,);
|
---|
792 | fFixed = 0;
|
---|
793 | fWildcard = 0;
|
---|
794 | fValue = 0;
|
---|
795 | for ch in sValue:
|
---|
796 | assert ch in 'x10', 'ch=%s' % ch;
|
---|
797 | fFixed <<= 1;
|
---|
798 | fValue <<= 1;
|
---|
799 | fWildcard <<= 1;
|
---|
800 | if ch != 'x':
|
---|
801 | fFixed |= 1;
|
---|
802 | if ch == '1':
|
---|
803 | fValue |= 1;
|
---|
804 | else:
|
---|
805 | fWildcard |= 1;
|
---|
806 | return (fValue, fFixed, fWildcard);
|
---|
807 |
|
---|
808 | @staticmethod
|
---|
809 | def fromJson(oJson):
|
---|
810 | assert oJson['_type'] in ('Instruction.Encodeset.Field', 'Instruction.Encodeset.Bits'), oJson['_type'];
|
---|
811 |
|
---|
812 | oRange = oJson['range'];
|
---|
813 | assert oRange['_type'] == 'Range';
|
---|
814 | iFirstBit = int(oRange['start']);
|
---|
815 | cBitsWidth = int(oRange['width']);
|
---|
816 | sName = oJson['name'] if oJson['_type'] == 'Instruction.Encodeset.Field' else None;
|
---|
817 | (fValue, fFixed, _) = ArmEncodesetField.parseValue(oJson['value']['value'], cBitsWidth);
|
---|
818 | return ArmEncodesetField(oJson, iFirstBit, cBitsWidth, fFixed, fValue, sName);
|
---|
819 |
|
---|
820 | @staticmethod
|
---|
821 | def fromJsonEncodeset(oJson, aoSet, fCovered):
|
---|
822 | assert oJson['_type'] == 'Instruction.Encodeset.Encodeset', oJson['_type'];
|
---|
823 | for oJsonValue in oJson['values']:
|
---|
824 | oNewField = ArmEncodesetField.fromJson(oJsonValue);
|
---|
825 | fNewMask = oNewField.getShiftedMask();
|
---|
826 | if (fNewMask & fCovered) != fNewMask:
|
---|
827 | aoSet.append(oNewField)
|
---|
828 | fCovered |= fNewMask;
|
---|
829 | return (aoSet, fCovered);
|
---|
830 |
|
---|
831 |
|
---|
832 | class ArmInstruction(object):
|
---|
833 | """
|
---|
834 | ARM instruction
|
---|
835 | """
|
---|
836 | s_oReValidName = re.compile('^[_A-Za-z][_A-Za-z0-9]+$');
|
---|
837 |
|
---|
838 | def __init__(self, oJson, sName, sMemonic, aoEncodesets, oCondition):
|
---|
839 | assert self.s_oReValidName.match(sName), 'sName=%s' % (sName);
|
---|
840 | self.oJson = oJson;
|
---|
841 | self.sName = sName;
|
---|
842 | self.sMnemonic = sMemonic;
|
---|
843 | self.sAsmDisplay = '';
|
---|
844 | self.aoEncodesets = aoEncodesets;
|
---|
845 | self.oCondition = oCondition;
|
---|
846 | self.fFixedMask = 0;
|
---|
847 | self.fFixedValue = 0;
|
---|
848 | for oField in aoEncodesets:
|
---|
849 | self.fFixedMask |= oField.fFixed << oField.iFirstBit;
|
---|
850 | self.fFixedValue |= oField.fValue << oField.iFirstBit;
|
---|
851 |
|
---|
852 | # State related to decoder.
|
---|
853 | self.fDecoderLeafCheckNeeded = False; ##< Whether we need to check fixed value/mask in leaf decoder functions.
|
---|
854 |
|
---|
855 | def toString(self, cchName = 0, fEncoding = False):
|
---|
856 | if self.sName == self.sMnemonic:
|
---|
857 | sRet = 'sName=%-*s' % (cchName, self.sName,);
|
---|
858 | else:
|
---|
859 | sRet = 'sName=%-*s sMnemonic=%-*s' % (cchName, self.sName, cchName, self.sMnemonic);
|
---|
860 | if not fEncoding:
|
---|
861 | return '%s fFixedValue/Mask=%#x/%#x #encoding=%s' % (sRet, self.fFixedValue, self.fFixedMask, len(self.aoEncodesets));
|
---|
862 | return '%s fFixedValue/Mask=%#x/%#x encoding=\n %s' \
|
---|
863 | % (sRet, self.fFixedValue, self.fFixedMask, ',\n '.join([str(s) for s in self.aoEncodesets]),);
|
---|
864 |
|
---|
865 | def __str__(self):
|
---|
866 | return self.toString();
|
---|
867 |
|
---|
868 | def __repr__(self):
|
---|
869 | return self.toString();
|
---|
870 |
|
---|
871 | def getCName(self):
|
---|
872 | # Get rid of trailing underscore as it seems pointless.
|
---|
873 | if self.sName[-1] != '_' or self.sName[:-1] in g_dAllArmInstructionsByName:
|
---|
874 | return self.sName;
|
---|
875 | return self.sName[:-1];
|
---|
876 |
|
---|
877 | def getFieldByName(self, sName, fRaiseXpctIfNotFound = True):
|
---|
878 | """ Looks up a named field in the aoEncodesets. """
|
---|
879 | for oField in self.aoEncodesets:
|
---|
880 | if oField.sName and oField.sName == sName:
|
---|
881 | return oField;
|
---|
882 | if fRaiseXpctIfNotFound:
|
---|
883 | raise Exception('Could not find field %s in instruction %s' % (sName, self.sName,));
|
---|
884 | return None;
|
---|
885 |
|
---|
886 |
|
---|
887 | #
|
---|
888 | # AArch64 Specification Loader.
|
---|
889 | #
|
---|
890 |
|
---|
891 | ## All the instructions.
|
---|
892 | g_aoAllArmInstructions = [] # type: List[ArmInstruction]
|
---|
893 |
|
---|
894 | ## All the instructions by name (not mnemonic.
|
---|
895 | g_dAllArmInstructionsByName = {} # type: Dict[ArmInstruction]
|
---|
896 |
|
---|
897 | #
|
---|
898 | # Pass #1 - Snoop up all the instructions and their encodings.
|
---|
899 | #
|
---|
900 | def parseInstructions(aoStack, aoJson):
|
---|
901 | for oJson in aoJson:
|
---|
902 | if oJson['_type'] == "Instruction.InstructionSet":
|
---|
903 | parseInstructions([oJson,] + aoStack, oJson['children']);
|
---|
904 | elif oJson['_type'] == "Instruction.InstructionGroup":
|
---|
905 | parseInstructions([oJson,] + aoStack, oJson['children']);
|
---|
906 | elif oJson['_type'] == "Instruction.Instruction":
|
---|
907 | sInstrNm = oJson['name'];
|
---|
908 |
|
---|
909 | (aoEncodesets, fCovered) = ArmEncodesetField.fromJsonEncodeset(oJson['encoding'], [], 0);
|
---|
910 | for oParent in aoStack:
|
---|
911 | if 'encoding' in oParent:
|
---|
912 | (aoEncodesets, fCovered) = ArmEncodesetField.fromJsonEncodeset(oParent['encoding'], aoEncodesets, fCovered);
|
---|
913 |
|
---|
914 | oCondition = ArmAstBase.fromJson(oJson['condition']);
|
---|
915 | #sCondBefore = oCondition.toString();
|
---|
916 | #print('debug transfer: %s: org: %s' % (sInstrNm, sCondBefore));
|
---|
917 | (oCondition, fMod) = transferConditionsToEncoding(oCondition, aoEncodesets, collections.defaultdict(list), sInstrNm);
|
---|
918 | #if fMod:
|
---|
919 | # print('debug transfer: %s: %s ----> %s' % (sInstrNm, sCondBefore, oCondition.toString()));
|
---|
920 | _ = fMod;
|
---|
921 |
|
---|
922 | oInstr = ArmInstruction(oJson, sInstrNm, sInstrNm, aoEncodesets, oCondition);
|
---|
923 |
|
---|
924 | g_aoAllArmInstructions.append(oInstr);
|
---|
925 | assert oInstr.sName not in g_dAllArmInstructionsByName;
|
---|
926 | g_dAllArmInstructionsByName[oInstr.sName] = oInstr;
|
---|
927 | return True;
|
---|
928 |
|
---|
929 | def transferConditionsToEncoding(oCondition, aoEncodesets, dPendingNotEq, sInstrNm, uDepth = 0, fMod = False):
|
---|
930 | """
|
---|
931 | This is for dealing with stuff like asr_z_p_zi_ and lsr_z_p_zi_ which has
|
---|
932 | the same fixed encoding fields in the specs, but differs in the value of
|
---|
933 | the named field 'U' as expressed in the conditions.
|
---|
934 |
|
---|
935 | This function will recursively take 'Field == value/integer' expression out
|
---|
936 | of the condition tree and add them to the encodeset conditions when possible.
|
---|
937 |
|
---|
938 | The dPendingNotEq stuff is a hack to deal with stuff like this:
|
---|
939 | sdot_z_zzz_: U == '0' && size != '01' && size != '00'
|
---|
940 | && (IsFeatureImplemented(FEAT_SVE) || IsFeatureImplemented(FEAT_SME))
|
---|
941 | The checks can be morphed into the 'size' field encoding criteria as '0b0x'.
|
---|
942 | """
|
---|
943 | if isinstance(oCondition, ArmAstBinaryOp):
|
---|
944 | if oCondition.sOp == '&&':
|
---|
945 | # Recurse into each side of an AND expression.
|
---|
946 | #print('debug transfer: %s: recursion...' % (sInstrNm,));
|
---|
947 | (oCondition.oLeft, fMod) = transferConditionsToEncoding(oCondition.oLeft, aoEncodesets, dPendingNotEq,
|
---|
948 | sInstrNm, uDepth + 1, fMod);
|
---|
949 | (oCondition.oRight, fMod) = transferConditionsToEncoding(oCondition.oRight, aoEncodesets, dPendingNotEq,
|
---|
950 | sInstrNm, uDepth + 1, fMod);
|
---|
951 | if oCondition.oLeft.isBoolAndTrue():
|
---|
952 | return (oCondition.oRight, fMod);
|
---|
953 | if oCondition.oRight.isBoolAndTrue():
|
---|
954 | return (oCondition.oLeft, fMod);
|
---|
955 |
|
---|
956 | elif oCondition.sOp in ('==', '!='):
|
---|
957 | # The pattern we're looking for is identifier (field) == fixed value.
|
---|
958 | #print('debug transfer: %s: binaryop %s vs %s ...' % (sInstrNm, oCondition.oLeft.sType, oCondition.oRight.sType));
|
---|
959 | if ( isinstance(oCondition.oLeft, ArmAstIdentifier)
|
---|
960 | and isinstance(oCondition.oRight, (ArmAstValue, ArmAstInteger))):
|
---|
961 | sFieldName = oCondition.oLeft.sName;
|
---|
962 | oValue = oCondition.oRight;
|
---|
963 | #print('debug transfer: %s: binaryop step 2...' % (sInstrNm,));
|
---|
964 | for oField in aoEncodesets: # ArmEncodesetField
|
---|
965 | if oField.sName and oField.sName == sFieldName:
|
---|
966 | # ArmAstInteger (unlikely):
|
---|
967 | if isinstance(oValue, ArmAstInteger):
|
---|
968 | if oField.fFixed != 0:
|
---|
969 | raise Exception('%s: Condition checks fixed field value: %s (%#x/%#x) %s %s'
|
---|
970 | % (sInstrNm, oField.sName, oField.fValue, oField.fFixed,
|
---|
971 | oCondition.sOp, oValue.iValue,));
|
---|
972 | assert oField.fValue == 0;
|
---|
973 | if oValue.iValue.bit_length() > oField.cBitsWidth:
|
---|
974 | raise Exception('%s: Condition field value check too wide: %s is %u bits, test value %s (%u bits)'
|
---|
975 | % (sInstrNm, oField.sName, oField.cBitsWidth, oValue.iValue,
|
---|
976 | oValue.iValue.bit_count(),));
|
---|
977 | if oValue.iValue < 0:
|
---|
978 | raise Exception('%s: Condition field checks against negative value: %s, test value is %s'
|
---|
979 | % (sInstrNm, oField.sName, oValue.iValue));
|
---|
980 | fFixed = (1 << oField.cBitsWidth) - 1;
|
---|
981 | if oCondition.sOp == '!=' and oField.cBitsWidth > 1:
|
---|
982 | dPendingNotEq[oField.sName] += [(oField, oValue.iValue, fFixed, oCondition)];
|
---|
983 | break;
|
---|
984 |
|
---|
985 | #print('debug transfer: %s: integer binaryop -> encoding: %s %s %#x/%#x'
|
---|
986 | # % (sInstrNm, oField.sName, oCondition.sOp, oValue.iValue, fFixed));
|
---|
987 | if oCondition.sOp == '==':
|
---|
988 | oField.fValue = oValue.iValue;
|
---|
989 | else:
|
---|
990 | oField.fValue = ~oValue.iValue & fFixed;
|
---|
991 | oField.fFixed = fFixed;
|
---|
992 | return (ArmAstBool(True), True);
|
---|
993 |
|
---|
994 | # ArmAstValue.
|
---|
995 | assert isinstance(oValue, ArmAstValue);
|
---|
996 | (fValue, fFixed, _) = ArmEncodesetField.parseValue(oValue.sValue, oField.cBitsWidth);
|
---|
997 |
|
---|
998 | if oCondition.sOp == '!=' and oField.cBitsWidth > 1 and (fFixed & (fFixed - 1)) != 0:
|
---|
999 | dPendingNotEq[oField.sName] += [(oField, fValue, fFixed, oCondition)];
|
---|
1000 | break;
|
---|
1001 | if fFixed & oField.fFixed:
|
---|
1002 | raise Exception('%s: Condition checks fixed field value: %s (%#x/%#x) %s %s (%#x/%#x)'
|
---|
1003 | % (sInstrNm, oField.sName, oField.fValue, oField.fFixed, oCondition.sOp,
|
---|
1004 | oValue.sValue, fValue, fFixed));
|
---|
1005 | #print('debug transfer: %s: value binaryop -> encoding: %s %s %#x (fFixed=%#x)'
|
---|
1006 | # % (sInstrNm, oField.sName, oCondition.sOp, fValue, fFixed,));
|
---|
1007 | if oCondition.sOp == '==':
|
---|
1008 | oField.fValue |= fValue;
|
---|
1009 | else:
|
---|
1010 | oField.fValue |= ~fValue & fFixed;
|
---|
1011 | oField.fFixed |= fFixed;
|
---|
1012 | return (ArmAstBool(True), True);
|
---|
1013 |
|
---|
1014 | #
|
---|
1015 | # Deal with pending '!=' optimizations for fields larger than a single bit.
|
---|
1016 | # Currently we only deal with two bit fields.
|
---|
1017 | #
|
---|
1018 | if uDepth == 0 and dPendingNotEq:
|
---|
1019 | def recursiveRemove(oCondition, aoToRemove):
|
---|
1020 | if isinstance(oCondition, ArmAstBinaryOp):
|
---|
1021 | if oCondition.sOp == '&&':
|
---|
1022 | oCondition.oLeft = recursiveRemove(oCondition.oLeft, aoToRemove);
|
---|
1023 | oCondition.oRight = recursiveRemove(oCondition.oLeft, aoToRemove);
|
---|
1024 | if oCondition.oLeft.isBoolAndTrue(): return oCondition.oRight;
|
---|
1025 | if oCondition.oRight.isBoolAndTrue(): return oCondition.oLeft;
|
---|
1026 | elif oCondition in aoToRemove:
|
---|
1027 | assert isinstance(oCondition.oLeft, ArmAstIdentifier);
|
---|
1028 | assert isinstance(oCondition.oRight, (ArmAstValue, ArmAstInteger));
|
---|
1029 | assert oCondition.sOp == '!=';
|
---|
1030 | return ArmAstBool(True);
|
---|
1031 | return oCondition;
|
---|
1032 |
|
---|
1033 | for sFieldNm, atOccurences in dPendingNotEq.items():
|
---|
1034 | # For a two bit field, we need at least two occurences to get any kind of fixed value.
|
---|
1035 | oField = atOccurences[0][0];
|
---|
1036 | if oField.cBitsWidth == 2 and len(atOccurences) >= 2:
|
---|
1037 | dValues = {};
|
---|
1038 | dFixed = {};
|
---|
1039 | for oCurField, fValue, fFixed, _ in atOccurences:
|
---|
1040 | assert oCurField is oField;
|
---|
1041 | dValues[fValue] = 1;
|
---|
1042 | dFixed[fFixed] = 1;
|
---|
1043 | if len(dValues) in (2, 3) and len(dFixed) == 1 and 3 in dFixed:
|
---|
1044 | afValues = list(dValues);
|
---|
1045 | if len(dValues) == 2:
|
---|
1046 | fFixed = 2 if (afValues[0] ^ afValues[1]) & 1 else 1; # One of the bits are fixed, the other ignored.
|
---|
1047 | else:
|
---|
1048 | fFixed = 3; # Both bits are fixed.
|
---|
1049 | fValue = afValues[0] & fFixed;
|
---|
1050 | print('debug transfer: %s: %u binaryops -> encoding: %s == %#x/%#x'
|
---|
1051 | % (sInstrNm, len(atOccurences), sFieldNm, ~fValue & fFixed, fFixed,));
|
---|
1052 | oField.fValue |= ~fValue & fFixed;
|
---|
1053 | oField.fFixed |= fFixed;
|
---|
1054 |
|
---|
1055 |
|
---|
1056 | # Remove the associated conditions (they'll be leaves).
|
---|
1057 | oCondition = recursiveRemove(oCondition, [oCondition for _, _, _, oCondition in atOccurences]);
|
---|
1058 | fMod = True;
|
---|
1059 | else:
|
---|
1060 | print('info: %s: transfer cond to enc failed for: %s dValues=%s dFixed=%s'
|
---|
1061 | % (sInstrNm, sFieldNm, dValues, dFixed));
|
---|
1062 | elif oField.cBitsWidth == 3 and len(atOccurences) >= 7:
|
---|
1063 | print('info: %s: TODO: transfer cond to enc for 3 bit field: %s (%s)' % (sInstrNm, sFieldNm, atOccurences,));
|
---|
1064 |
|
---|
1065 | return (oCondition, fMod);
|
---|
1066 |
|
---|
1067 |
|
---|
1068 | #
|
---|
1069 | # Pass #2 - Assembly syntax formatting (for display purposes)
|
---|
1070 | #
|
---|
1071 | def asmSymbolsToDisplayText(adSymbols, ddAsmRules, oInstr):
|
---|
1072 | sText = '';
|
---|
1073 | for dSym in adSymbols:
|
---|
1074 | sType = dSym['_type'];
|
---|
1075 | if sType == 'Instruction.Symbols.Literal':
|
---|
1076 | sText += dSym['value'];
|
---|
1077 | elif sType == 'Instruction.Symbols.RuleReference':
|
---|
1078 | sRuleId = dSym['rule_id'];
|
---|
1079 | sText += asmRuleIdToDisplayText(sRuleId, ddAsmRules, oInstr);
|
---|
1080 | else:
|
---|
1081 | raise Exception('%s: Unknown assembly symbol type: %s' % (oInstr.sMnemonic, sType,));
|
---|
1082 | return sText;
|
---|
1083 |
|
---|
1084 | def asmChoicesFilterOutDefaultAndAbsent(adChoices, ddAsmRules):
|
---|
1085 | # There are sometime a 'none' tail entry.
|
---|
1086 | if adChoices[-1] is None:
|
---|
1087 | adChoices = adChoices[:-1];
|
---|
1088 | if len(adChoices) > 1:
|
---|
1089 | # Typically, one of the choices is 'absent' or 'default', eliminate it before we start...
|
---|
1090 | for iChoice, dChoice in enumerate(adChoices):
|
---|
1091 | fAllAbsentOrDefault = True;
|
---|
1092 | for dSymbol in dChoice['symbols']:
|
---|
1093 | if dSymbol['_type'] != 'Instruction.Symbols.RuleReference':
|
---|
1094 | fAllAbsentOrDefault = False;
|
---|
1095 | break;
|
---|
1096 | sRuleId = dSymbol['rule_id'];
|
---|
1097 | oRule = ddAsmRules[sRuleId];
|
---|
1098 | if ( ('display' in oRule and oRule['display'])
|
---|
1099 | or ('symbols' in oRule and oRule['symbols'])):
|
---|
1100 | fAllAbsentOrDefault = False;
|
---|
1101 | break;
|
---|
1102 | if fAllAbsentOrDefault:
|
---|
1103 | return adChoices[:iChoice] + adChoices[iChoice + 1:];
|
---|
1104 | return adChoices;
|
---|
1105 |
|
---|
1106 | def asmRuleIdToDisplayText(sRuleId, ddAsmRules, oInstr):
|
---|
1107 | dRule = ddAsmRules[sRuleId];
|
---|
1108 | sRuleType = dRule['_type'];
|
---|
1109 | if sRuleType == 'Instruction.Rules.Token':
|
---|
1110 | assert dRule['default'], '%s: %s' % (oInstr.sMnemonic, sRuleId);
|
---|
1111 | return dRule['default'];
|
---|
1112 | if sRuleType == 'Instruction.Rules.Rule':
|
---|
1113 | assert dRule['display'], '%s: %s' % (oInstr.sMnemonic, sRuleId);
|
---|
1114 | return dRule['display'];
|
---|
1115 | if sRuleType == 'Instruction.Rules.Choice':
|
---|
1116 | # Some of these has display = None and we need to sort it out ourselves.
|
---|
1117 | if dRule['display']:
|
---|
1118 | return dRule['display'];
|
---|
1119 | sText = '{';
|
---|
1120 | assert len(dRule['choices']) > 1;
|
---|
1121 | for iChoice, dChoice in enumerate(asmChoicesFilterOutDefaultAndAbsent(dRule['choices'], ddAsmRules)):
|
---|
1122 | if iChoice > 0:
|
---|
1123 | sText += ' | ';
|
---|
1124 | sText += asmSymbolsToDisplayText(dChoice['symbols'], ddAsmRules, oInstr);
|
---|
1125 | sText += '}';
|
---|
1126 |
|
---|
1127 | # Cache it.
|
---|
1128 | dRule['display'] = sText;
|
---|
1129 | return sText;
|
---|
1130 |
|
---|
1131 | raise Exception('%s: Unknown assembly rule type: %s for %s' % (oInstr.sMnemonic, sRuleType, sRuleId));
|
---|
1132 |
|
---|
1133 | def parseInstructionsPass2(aoInstructions, ddAsmRules):
|
---|
1134 | """
|
---|
1135 | Uses the assembly rules to construct some assembly syntax string for each
|
---|
1136 | instruction in the array.
|
---|
1137 | """
|
---|
1138 | for oInstr in aoInstructions:
|
---|
1139 | if 'assembly' in oInstr.oJson:
|
---|
1140 | oAsm = oInstr.oJson['assembly'];
|
---|
1141 | assert oAsm['_type'] == 'Instruction.Assembly';
|
---|
1142 | assert 'symbols' in oAsm;
|
---|
1143 | oInstr.sAsmDisplay = asmSymbolsToDisplayText(oAsm['symbols'], ddAsmRules, oInstr);
|
---|
1144 | else:
|
---|
1145 | oInstr.sAsmDisplay = oInstr.sMnemonic;
|
---|
1146 | return True;
|
---|
1147 |
|
---|
1148 | def LoadArmOpenSourceSpecification(oOptions):
|
---|
1149 | #
|
---|
1150 | # Load the files.
|
---|
1151 | #
|
---|
1152 | print("loading specs ...");
|
---|
1153 | if oOptions.sTarFile:
|
---|
1154 | with tarfile.open(oOptions.sTarFile, 'r') as oTarFile:
|
---|
1155 | with oTarFile.extractfile(oOptions.sFileInstructions) as oFile:
|
---|
1156 | dRawInstructions = json.load(oFile);
|
---|
1157 | #with open(sFileFeatures, 'r', encoding = 'utf-8') as oFile:
|
---|
1158 | # dRawFeatures = json.load(oFile);
|
---|
1159 | #with open(sFileRegisters, 'r', encoding = 'utf-8') as oFile:
|
---|
1160 | # dRawRegisters = json.load(oFile);
|
---|
1161 | else:
|
---|
1162 | if oOptions.sSpecDir:
|
---|
1163 | if not os.path.isabs(oOptions.sFileInstructions):
|
---|
1164 | oOptions.sFileInstructions = os.path.normpath(os.path.join(oOptions.sSpecDir, oOptions.sFileInstructions));
|
---|
1165 | if not os.path.isabs(oOptions.sFileFeatures):
|
---|
1166 | oOptions.sFileFeatures = os.path.normpath(os.path.join(oOptions.sSpecDir, oOptions.sFileFeatures));
|
---|
1167 | if not os.path.isabs(oOptions.sFileRegisters):
|
---|
1168 | oOptions.sFileRegisters = os.path.normpath(os.path.join(oOptions.sSpecDir, oOptions.sFileRegisters));
|
---|
1169 |
|
---|
1170 | with open(oOptions.sFileInstructions, 'r', encoding = 'utf-8') as oFile:
|
---|
1171 | dRawInstructions = json.load(oFile);
|
---|
1172 | #with open(oOptions.sFileFeatures, 'r', encoding = 'utf-8') as oFile:
|
---|
1173 | # dRawFeatures = json.load(oFile);
|
---|
1174 | #with open(oOptions.sFileRegisters, 'r', encoding = 'utf-8') as oFile:
|
---|
1175 | # dRawRegisters = json.load(oFile);
|
---|
1176 | print("... done loading.");
|
---|
1177 |
|
---|
1178 | #
|
---|
1179 | # Parse the Instructions.
|
---|
1180 | #
|
---|
1181 | print("parsing instructions ...");
|
---|
1182 | # Pass #1: Collect the instructions.
|
---|
1183 | parseInstructions([], dRawInstructions['instructions']);
|
---|
1184 | # Pass #2: Assembly syntax.
|
---|
1185 | global g_aoAllArmInstructions;
|
---|
1186 | parseInstructionsPass2(g_aoAllArmInstructions, dRawInstructions['assembly_rules']);
|
---|
1187 |
|
---|
1188 | # Sort the instruction array by name.
|
---|
1189 | g_aoAllArmInstructions = sorted(g_aoAllArmInstructions, key = operator.attrgetter('sName', 'sAsmDisplay'));
|
---|
1190 |
|
---|
1191 | print("Found %u instructions." % (len(g_aoAllArmInstructions),));
|
---|
1192 | #oBrk = g_dAllArmInstructionsByName['BRK_EX_exception'];
|
---|
1193 | #print("oBrk=%s" % (oBrk,))
|
---|
1194 |
|
---|
1195 | if oOptions.fPrintInstructions:
|
---|
1196 | for oInstr in g_aoAllArmInstructions:
|
---|
1197 | print('%08x/%08x %s %s' % (oInstr.fFixedMask, oInstr.fFixedValue, oInstr.getCName(), oInstr.sAsmDisplay));
|
---|
1198 |
|
---|
1199 | # Gather stats on fixed bits:
|
---|
1200 | if oOptions.fPrintFixedMaskStats:
|
---|
1201 | dCounts = collections.Counter();
|
---|
1202 | for oInstr in g_aoAllArmInstructions:
|
---|
1203 | cPopCount = bin(oInstr.fFixedMask).count('1');
|
---|
1204 | dCounts[cPopCount] += 1;
|
---|
1205 |
|
---|
1206 | print('');
|
---|
1207 | print('Fixed bit pop count distribution:');
|
---|
1208 | for i in range(33):
|
---|
1209 | if i in dCounts:
|
---|
1210 | print(' %2u: %u' % (i, dCounts[i]));
|
---|
1211 |
|
---|
1212 | # Top 10 fixed masks.
|
---|
1213 | if oOptions.fPrintFixedMaskTop10:
|
---|
1214 | dCounts = collections.Counter();
|
---|
1215 | for oInstr in g_aoAllArmInstructions:
|
---|
1216 | dCounts[oInstr.fFixedMask] += 1;
|
---|
1217 |
|
---|
1218 | print('');
|
---|
1219 | print('Top 20 fixed masks:');
|
---|
1220 | for fFixedMask, cHits in dCounts.most_common(20):
|
---|
1221 | print(' %#x: %u times' % (fFixedMask, cHits,));
|
---|
1222 |
|
---|
1223 | return True;
|
---|
1224 |
|
---|
1225 |
|
---|
1226 | #
|
---|
1227 | # Decoder structure helpers.
|
---|
1228 | #
|
---|
1229 |
|
---|
1230 | class MaskZipper(object):
|
---|
1231 | """
|
---|
1232 | This is mainly a class for putting static methods relating to mask
|
---|
1233 | packing and unpack.
|
---|
1234 | """
|
---|
1235 |
|
---|
1236 | def __init__(self):
|
---|
1237 | pass;
|
---|
1238 |
|
---|
1239 | @staticmethod
|
---|
1240 | def compileAlgo(fMask):
|
---|
1241 | """
|
---|
1242 | Returns an with instructions for extracting the bits from the mask into
|
---|
1243 | a compacted form. Each array entry is an array/tuple of source bit [0],
|
---|
1244 | destination bit [1], and bit counts [2].
|
---|
1245 | """
|
---|
1246 | aaiAlgo = [];
|
---|
1247 | iSrcBit = 0;
|
---|
1248 | iDstBit = 0;
|
---|
1249 | while fMask > 0:
|
---|
1250 | # Skip leading zeros.
|
---|
1251 | cSkip = (fMask & -fMask).bit_length() - 1;
|
---|
1252 | #assert (fMask & ((1 << cSkip) - 1)) == 0 and ((fMask >> cSkip) & 1), 'fMask=%#x cSkip=%d' % (fMask, cSkip)
|
---|
1253 | iSrcBit += cSkip;
|
---|
1254 | fMask >>= cSkip;
|
---|
1255 |
|
---|
1256 | # Calculate leading ones the same way.
|
---|
1257 | cCount = (~fMask & -~fMask).bit_length() - 1;
|
---|
1258 | #assert (fMask & ((1 << cCount) - 1)) == ((1 << cCount) - 1) and (fMask & (1 << cCount)) == 0
|
---|
1259 |
|
---|
1260 | # Append to algo list.
|
---|
1261 | aaiAlgo.append((iSrcBit, iDstBit, (1 << cCount) - 1));
|
---|
1262 |
|
---|
1263 | # Advance.
|
---|
1264 | iDstBit += cCount;
|
---|
1265 | iSrcBit += cCount;
|
---|
1266 | fMask >>= cCount;
|
---|
1267 | return aaiAlgo;
|
---|
1268 |
|
---|
1269 | @staticmethod
|
---|
1270 | def compileAlgoLimited(fMask):
|
---|
1271 | """
|
---|
1272 | Version of compileAlgo that returns an empty list if there are
|
---|
1273 | more than three sections.
|
---|
1274 | """
|
---|
1275 | #assert fMask;
|
---|
1276 |
|
---|
1277 | #
|
---|
1278 | # Chunk 0:
|
---|
1279 | #
|
---|
1280 |
|
---|
1281 | # Skip leading zeros.
|
---|
1282 | iSrcBit0 = (fMask & -fMask).bit_length() - 1;
|
---|
1283 | fMask >>= iSrcBit0;
|
---|
1284 | # Calculate leading ones the same way.
|
---|
1285 | cCount0 = (~fMask & -~fMask).bit_length() - 1;
|
---|
1286 | fMask >>= cCount0;
|
---|
1287 | if not fMask:
|
---|
1288 | return [(iSrcBit0, 0, (1 << cCount0) - 1)];
|
---|
1289 |
|
---|
1290 | #
|
---|
1291 | # Chunk 1:
|
---|
1292 | #
|
---|
1293 |
|
---|
1294 | # Skip leading zeros.
|
---|
1295 | cSrcGap1 = (fMask & -fMask).bit_length() - 1;
|
---|
1296 | fMask >>= cSrcGap1;
|
---|
1297 | # Calculate leading ones the same way.
|
---|
1298 | cCount1 = (~fMask & -~fMask).bit_length() - 1;
|
---|
1299 | fMask >>= cCount1;
|
---|
1300 | if not fMask:
|
---|
1301 | return [ (iSrcBit0, 0, (1 << cCount0) - 1),
|
---|
1302 | (iSrcBit0 + cCount0 + cSrcGap1, cCount0, (1 << cCount1) - 1)];
|
---|
1303 |
|
---|
1304 | #
|
---|
1305 | # Chunk 2:
|
---|
1306 | #
|
---|
1307 |
|
---|
1308 | # Skip leading zeros.
|
---|
1309 | cSrcGap2 = (fMask & -fMask).bit_length() - 1;
|
---|
1310 | fMask >>= cSrcGap2;
|
---|
1311 | # Calculate leading ones the same way.
|
---|
1312 | cCount2 = (~fMask & -~fMask).bit_length() - 1;
|
---|
1313 | fMask >>= cCount2;
|
---|
1314 | if not fMask:
|
---|
1315 | iSrcBit1 = iSrcBit0 + cCount0 + cSrcGap1;
|
---|
1316 | return [ (iSrcBit0, 0, (1 << cCount0) - 1),
|
---|
1317 | (iSrcBit1, cCount0, (1 << cCount1) - 1),
|
---|
1318 | (iSrcBit1 + cCount1 + cSrcGap2, cCount0 + cCount1, (1 << cCount2) - 1), ];
|
---|
1319 |
|
---|
1320 | # Too many fragments.
|
---|
1321 | return [];
|
---|
1322 |
|
---|
1323 | @staticmethod
|
---|
1324 | def compileAlgoFromList(aiOrderedBits):
|
---|
1325 | """
|
---|
1326 | Returns an with instructions for extracting the bits from the mask into
|
---|
1327 | a compacted form. Each array entry is an array/tuple of source bit [0],
|
---|
1328 | destination bit [1], and mask (shifted to pos 0) [2].
|
---|
1329 | """
|
---|
1330 | aaiAlgo = [];
|
---|
1331 | iDstBit = 0;
|
---|
1332 | i = 0;
|
---|
1333 | while i < len(aiOrderedBits):
|
---|
1334 | iSrcBit = aiOrderedBits[i];
|
---|
1335 | cCount = 1;
|
---|
1336 | i += 1;
|
---|
1337 | while i < len(aiOrderedBits) and aiOrderedBits[i] == iSrcBit + cCount:
|
---|
1338 | cCount += 1;
|
---|
1339 | i += 1;
|
---|
1340 | aaiAlgo.append([iSrcBit, iDstBit, (1 << cCount) - 1])
|
---|
1341 | iDstBit += cCount;
|
---|
1342 | return aaiAlgo;
|
---|
1343 |
|
---|
1344 | @staticmethod
|
---|
1345 | def algoToBitList(aaiAlgo):
|
---|
1346 | aiRet = [];
|
---|
1347 | for iSrcBit, _, fMask in aaiAlgo:
|
---|
1348 | cCount = fMask.bit_count();
|
---|
1349 | aiRet += [iSrcBit + i for i in range(cCount)];
|
---|
1350 | return aiRet;
|
---|
1351 |
|
---|
1352 | @staticmethod
|
---|
1353 | def zipMask(uValue, aaiAlgo):
|
---|
1354 | idxRet = 0;
|
---|
1355 | for iSrcBit, iDstBit, fMask in aaiAlgo:
|
---|
1356 | idxRet |= ((uValue >> iSrcBit) & fMask) << iDstBit;
|
---|
1357 | return idxRet;
|
---|
1358 |
|
---|
1359 | @staticmethod
|
---|
1360 | def __zipMask1(uValue, aaiAlgo):
|
---|
1361 | iSrcBit, _, fMask = aaiAlgo[0];
|
---|
1362 | return (uValue >> iSrcBit) & fMask;
|
---|
1363 |
|
---|
1364 | @staticmethod
|
---|
1365 | def __zipMask2(uValue, aaiAlgo):
|
---|
1366 | iSrcBit0, _, fMask0 = aaiAlgo[0];
|
---|
1367 | iSrcBit1, iDstBit1, fMask1 = aaiAlgo[1];
|
---|
1368 | return ((uValue >> iSrcBit0) & fMask0) | (((uValue >> iSrcBit1) & fMask1) << iDstBit1);
|
---|
1369 |
|
---|
1370 | @staticmethod
|
---|
1371 | def __zipMask3(uValue, aaiAlgo):
|
---|
1372 | iSrcBit0, _, fMask0 = aaiAlgo[0];
|
---|
1373 | iSrcBit1, iDstBit1, fMask1 = aaiAlgo[1];
|
---|
1374 | iSrcBit2, iDstBit2, fMask2 = aaiAlgo[2];
|
---|
1375 | return ((uValue >> iSrcBit0) & fMask0) \
|
---|
1376 | | (((uValue >> iSrcBit1) & fMask1) << iDstBit1) \
|
---|
1377 | | (((uValue >> iSrcBit2) & fMask2) << iDstBit2);
|
---|
1378 |
|
---|
1379 | @staticmethod
|
---|
1380 | def algoToZipLambda(aaiAlgo, fAlgoMask, fCompileIt = True):
|
---|
1381 | assert aaiAlgo;
|
---|
1382 | if not fCompileIt:
|
---|
1383 | if len(aaiAlgo) == 1: return MaskZipper.__zipMask1;
|
---|
1384 | if len(aaiAlgo) == 2: return MaskZipper.__zipMask2;
|
---|
1385 | if len(aaiAlgo) == 3: return MaskZipper.__zipMask3;
|
---|
1386 | return MaskZipper.zipMask;
|
---|
1387 | # Compile it:
|
---|
1388 | sBody = '';
|
---|
1389 | for iSrcBit, iDstBit, fMask in aaiAlgo:
|
---|
1390 | if sBody:
|
---|
1391 | sBody += ' | ';
|
---|
1392 | assert iSrcBit >= iDstBit;
|
---|
1393 | if iDstBit == 0:
|
---|
1394 | if iSrcBit == 0:
|
---|
1395 | sBody += '(uValue & %#x)' % (fMask,);
|
---|
1396 | else:
|
---|
1397 | sBody += '((uValue >> %u) & %#x)' % (iSrcBit, fMask);
|
---|
1398 | else:
|
---|
1399 | sBody += '((uValue >> %u) & %#x)' % (iSrcBit - iDstBit, fMask << iDstBit);
|
---|
1400 | _ = fAlgoMask
|
---|
1401 | #sFn = 'zipMaskCompiled_%#010x' % (fAlgoMask,);
|
---|
1402 | #sFn = 'zipMaskCompiled';
|
---|
1403 | #dTmp = {};
|
---|
1404 | #exec('def %s(uValue,_): return %s' % (sFn, sBody), globals(), dTmp);
|
---|
1405 | #return dTmp[sFn];
|
---|
1406 | return eval('lambda uValue,_: ' + sBody);
|
---|
1407 |
|
---|
1408 | @staticmethod
|
---|
1409 | def unzipMask(uValue, aaiAlgo):
|
---|
1410 | fRet = 0;
|
---|
1411 | for iSrcBit, iDstBit, fMask in aaiAlgo:
|
---|
1412 | fRet |= ((uValue >> iDstBit) & fMask) << iSrcBit;
|
---|
1413 | return fRet;
|
---|
1414 |
|
---|
1415 | @staticmethod
|
---|
1416 | def __unzipMask1(uValue, aaiAlgo):
|
---|
1417 | return uValue << aaiAlgo[0][0];
|
---|
1418 |
|
---|
1419 | @staticmethod
|
---|
1420 | def __unzipMask2(uValue, aaiAlgo):
|
---|
1421 | iSrcBit0, _, fMask0 = aaiAlgo[0];
|
---|
1422 | iSrcBit1, iDstBit1, fMask1 = aaiAlgo[1];
|
---|
1423 | return ((uValue & fMask0) << iSrcBit0) | (((uValue >> iDstBit1) & fMask1) << iSrcBit1);
|
---|
1424 |
|
---|
1425 | @staticmethod
|
---|
1426 | def __unzipMask3(uValue, aaiAlgo):
|
---|
1427 | iSrcBit0, _, fMask0 = aaiAlgo[0];
|
---|
1428 | iSrcBit1, iDstBit1, fMask1 = aaiAlgo[1];
|
---|
1429 | iSrcBit2, iDstBit2, fMask2 = aaiAlgo[2];
|
---|
1430 | return ((uValue & fMask0) << iSrcBit0) \
|
---|
1431 | | (((uValue >> iDstBit1) & fMask1) << iSrcBit1) \
|
---|
1432 | | (((uValue >> iDstBit2) & fMask2) << iSrcBit2);
|
---|
1433 |
|
---|
1434 | @staticmethod
|
---|
1435 | def algoToUnzipLambda(aaiAlgo, fAlgoMask, fCompileIt = True):
|
---|
1436 | assert aaiAlgo;
|
---|
1437 | if not fCompileIt:
|
---|
1438 | if len(aaiAlgo) == 1: return MaskZipper.__unzipMask1;
|
---|
1439 | if len(aaiAlgo) == 2: return MaskZipper.__unzipMask2;
|
---|
1440 | if len(aaiAlgo) == 3: return MaskZipper.__unzipMask3;
|
---|
1441 | return MaskZipper.unzipMask;
|
---|
1442 | # Compile it:
|
---|
1443 | sBody = '';
|
---|
1444 | for iSrcBit, iDstBit, fMask in aaiAlgo:
|
---|
1445 | if sBody:
|
---|
1446 | sBody += ' | ';
|
---|
1447 | if iDstBit == 0:
|
---|
1448 | if iSrcBit == 0:
|
---|
1449 | sBody += '(uIdx & %#x)' % (fMask,);
|
---|
1450 | else:
|
---|
1451 | sBody += '((uIdx & %#x) << %u)' % (fMask, iSrcBit);
|
---|
1452 | else:
|
---|
1453 | sBody += '((uIdx << %u) & %#x)' % (iSrcBit - iDstBit, fMask << iSrcBit);
|
---|
1454 |
|
---|
1455 | _ = fAlgoMask
|
---|
1456 | #dTmp = {};
|
---|
1457 | #sFn = 'unzipMaskCompiled';
|
---|
1458 | #sFn = 'unzipMaskCompiled_%#010x' % (fAlgoMask,);
|
---|
1459 | #exec('def %s(uIdx,_): return %s' % (sFn, sBody), globals(), dTmp);
|
---|
1460 | #return dTmp[sFn];
|
---|
1461 | return eval('lambda uIdx,_: ' + sBody);
|
---|
1462 |
|
---|
1463 |
|
---|
1464 | class MaskIterator(object):
|
---|
1465 | """ Helper class for DecoderNode.constructNextLevel(). """
|
---|
1466 |
|
---|
1467 | ## Maximum number of mask sub-parts.
|
---|
1468 | # Lower number means fewer instructions required to convert it into an index.
|
---|
1469 | # This is implied by the code in MaskZipper.compileAlgoLimited.
|
---|
1470 | kcMaxMaskParts = 3
|
---|
1471 |
|
---|
1472 | def __init__(self, fMask, cMinTableSizeInBits, cMaxTableSizeInBits, fMaskNotDoneYet):
|
---|
1473 | self.fMask = fMask;
|
---|
1474 | self.aaiAlgo = MaskZipper.compileAlgo(fMask);
|
---|
1475 | self.fCompactMask = MaskZipper.zipMask(fMask, self.aaiAlgo);
|
---|
1476 | self.fnExpandMask = MaskZipper.algoToUnzipLambda(self.aaiAlgo, fMask,
|
---|
1477 | self.fCompactMask.bit_count() >= 8);
|
---|
1478 | self.cMinTableSizeInBits = cMinTableSizeInBits;
|
---|
1479 | self.cMaxTableSizeInBits = cMaxTableSizeInBits;
|
---|
1480 | self.fCompactMaskNotDoneYet = MaskZipper.zipMask(fMaskNotDoneYet, self.aaiAlgo);
|
---|
1481 | #print('debug: fMask=%#x -> fCompactMask=%#x aaiAlgo=%s' % (fMask, self.fCompactMask, self.aaiAlgo));
|
---|
1482 | #self.cReturned = 0;
|
---|
1483 |
|
---|
1484 | def __iter__(self):
|
---|
1485 | return self;
|
---|
1486 |
|
---|
1487 | def __next__(self):
|
---|
1488 | fCompactMask = self.fCompactMask;
|
---|
1489 | fCompactMaskNotDoneYet = self.fCompactMaskNotDoneYet;
|
---|
1490 | cMinTableSizeInBits = self.cMinTableSizeInBits
|
---|
1491 | cMaxTableSizeInBits = self.cMaxTableSizeInBits
|
---|
1492 | while fCompactMask != 0:
|
---|
1493 | if fCompactMask & fCompactMaskNotDoneYet:
|
---|
1494 | cCurBits = fCompactMask.bit_count();
|
---|
1495 | if cMinTableSizeInBits <= cCurBits <= cMaxTableSizeInBits:
|
---|
1496 | fMask = self.fnExpandMask(fCompactMask, self.aaiAlgo);
|
---|
1497 | aaiMaskAlgo = MaskZipper.compileAlgoLimited(fMask);
|
---|
1498 | if aaiMaskAlgo:
|
---|
1499 | #assert aaiMaskAlgo == MaskZipper.compileAlgo(fMask), \
|
---|
1500 | # '%s vs %s' % (aaiMaskAlgo, MaskZipper.compileAlgo(fMask));
|
---|
1501 | #self.cReturned += 1;
|
---|
1502 | self.fCompactMask = fCompactMask - 1;
|
---|
1503 | return (fMask, cCurBits, aaiMaskAlgo);
|
---|
1504 | fCompactMask -= 1;
|
---|
1505 | self.fCompactMask = 0;
|
---|
1506 | #print('MaskIterator: fMask=%#x -> %u items returned' % (self.fMask, self.cReturned));
|
---|
1507 | raise StopIteration;
|
---|
1508 |
|
---|
1509 |
|
---|
1510 | class DecoderNode(object):
|
---|
1511 |
|
---|
1512 | ## The absolute maximum table size in bits index by the log2 of the instruction count.
|
---|
1513 | kacMaxTableSizesInBits = (
|
---|
1514 | 2, # [2^0 = 1] => 4
|
---|
1515 | 4, # [2^1 = 2] => 16
|
---|
1516 | 5, # [2^2 = 4] => 32
|
---|
1517 | 6, # [2^3 = 8] => 64
|
---|
1518 | 7, # [2^4 = 16] => 128
|
---|
1519 | 7, # [2^5 = 32] => 128
|
---|
1520 | 8, # [2^6 = 64] => 256
|
---|
1521 | 9, # [2^7 = 128] => 512
|
---|
1522 | 10, # [2^8 = 256] => 1024
|
---|
1523 | 11, # [2^9 = 512] => 2048
|
---|
1524 | 12, # [2^10 = 1024] => 4096
|
---|
1525 | 13, # [2^11 = 2048] => 8192
|
---|
1526 | 14, # [2^12 = 4096] => 16384
|
---|
1527 | 14, # [2^13 = 8192] => 16384
|
---|
1528 | 15, # [2^14 =16384] => 32768
|
---|
1529 | );
|
---|
1530 |
|
---|
1531 | kChildMaskOpcodeValueIf = 0x7fffffff;
|
---|
1532 | kChildMaskMultipleOpcodeValueIfs = 0xffffffff;
|
---|
1533 |
|
---|
1534 | class TooExpensive(Exception):
|
---|
1535 | def __init__(self):
|
---|
1536 | Exception.__init__(self, None);
|
---|
1537 |
|
---|
1538 | def __init__(self, aoInstructions, fCheckedMask, fCheckedValue):
|
---|
1539 | #assert (~fCheckedMask & fCheckedValue) == 0;
|
---|
1540 | #for idxInstr, oInstr in enumerate(aoInstructions):
|
---|
1541 | # assert ((oInstr.fFixedValue ^ fCheckedValue) & fCheckedMask & oInstr.fFixedMask) == 0, \
|
---|
1542 | # '%s: fFixedValue=%#x fFixedMask=%#x fCheckedValue=%#x fCheckedMask=%#x -> %#x\naoInstructions: len=%s\n %s' \
|
---|
1543 | # % (idxInstr, oInstr.fFixedValue, oInstr.fFixedMask, fCheckedValue, fCheckedMask,
|
---|
1544 | # (oInstr.fFixedValue ^ fCheckedValue) & fCheckedMask & oInstr.fFixedMask,
|
---|
1545 | # len(aoInstructions),
|
---|
1546 | # '\n '.join(['%s%s: %#010x/%#010x %s' % ('*' if i == idxInstr else ' ', i,
|
---|
1547 | # oInstr2.fFixedValue, oInstr2.fFixedMask, oInstr2.sName)
|
---|
1548 | # for i, oInstr2 in enumerate(aoInstructions[:idxInstr+8])]));
|
---|
1549 |
|
---|
1550 | self.aoInstructions = aoInstructions; ##< The instructions at this level.
|
---|
1551 | self.fCheckedMask = fCheckedMask; ##< The opcode bit mask covered thus far.
|
---|
1552 | self.fCheckedValue = fCheckedValue; ##< The value that goes with fCheckedMask.
|
---|
1553 | self.fChildMask = 0; ##< The mask used to separate the children.
|
---|
1554 | self.dChildren = {}; ##< Children, sparsely populated by constructNextLevel().
|
---|
1555 |
|
---|
1556 | @staticmethod
|
---|
1557 | def popCount(uValue):
|
---|
1558 | cBits = 0;
|
---|
1559 | while uValue:
|
---|
1560 | cBits += 1;
|
---|
1561 | uValue &= uValue - 1;
|
---|
1562 | return cBits;
|
---|
1563 |
|
---|
1564 | s_uLogLine = 0;
|
---|
1565 | @staticmethod
|
---|
1566 | def dprint(uDepth, sMsg):
|
---|
1567 | msNow = (time.time_ns() - g_nsProgStart) // 1000000;
|
---|
1568 | print('%u.%03u: %u: debug/%u: %s%s' % (msNow // 1000, msNow % 1000, DecoderNode.s_uLogLine, uDepth, ' ' * uDepth, sMsg));
|
---|
1569 | DecoderNode.s_uLogLine += 1;
|
---|
1570 |
|
---|
1571 | def constructNextLevel(self, uDepth, uMaxCost): # pylint: disable=too-many-locals,too-many-statements
|
---|
1572 | """
|
---|
1573 | Recursively constructs the
|
---|
1574 | """
|
---|
1575 | if uDepth == 0:
|
---|
1576 | for i, oInstr in enumerate(self.aoInstructions):
|
---|
1577 | self.dprint(uDepth, '%4u: %s' % (i, oInstr.toString(cchName = 32),));
|
---|
1578 |
|
---|
1579 | #
|
---|
1580 | # Special cases: 4 or fewer entries.
|
---|
1581 | #
|
---|
1582 | cInstructions = len(self.aoInstructions)
|
---|
1583 | if cInstructions <= 4:
|
---|
1584 | assert not self.dChildren;
|
---|
1585 | uCost = 0;
|
---|
1586 | # Special case: 1 instruction - leaf.
|
---|
1587 | if cInstructions <= 1:
|
---|
1588 | if self.aoInstructions[0].fFixedMask & ~self.fCheckedMask != 0:
|
---|
1589 | self.fChildMask = DecoderNode.kChildMaskOpcodeValueIf;
|
---|
1590 | uCost = 16; # 16 = kCostOpcodeValueIf
|
---|
1591 | else:
|
---|
1592 | assert self.fChildMask == 0;
|
---|
1593 |
|
---|
1594 | # Special case: 2, 3 or 4 instructions - use a sequence of 'if ((uOpcode & fFixedMask) == fFixedValue)' checks.
|
---|
1595 | else:
|
---|
1596 | self.fChildMask = DecoderNode.kChildMaskMultipleOpcodeValueIfs;
|
---|
1597 | uCost = 32 * cInstructions * 2; # 32 = kCostMultipleOpcodeValueIfs
|
---|
1598 | return uCost;
|
---|
1599 |
|
---|
1600 | #
|
---|
1601 | # The cost of one indirect call is 32, so just bail if we don't have
|
---|
1602 | # the budget for any of that.
|
---|
1603 | #
|
---|
1604 | if uMaxCost <= 256: # 256 = kCostIndirectCall
|
---|
1605 | raise DecoderNode.TooExpensive();
|
---|
1606 | if uDepth > 5: # 5 = kMaxDepth
|
---|
1607 | raise DecoderNode.TooExpensive();
|
---|
1608 |
|
---|
1609 | #
|
---|
1610 | # Do an inventory of the fixed masks used by the instructions.
|
---|
1611 | #
|
---|
1612 | dMaskCounts = collections.Counter();
|
---|
1613 | fCheckedMask = self.fCheckedMask; # (Cache it as a local variable for speed.)
|
---|
1614 | for oInstr in self.aoInstructions:
|
---|
1615 | dMaskCounts[oInstr.fFixedMask & ~fCheckedMask] += 1;
|
---|
1616 | #assert 0 not in dMaskCounts or dMaskCounts[0] <= 1, \
|
---|
1617 | # 'dMaskCounts=%s cInstructions=%s\n%s' % (dMaskCounts, cInstructions, self.aoInstructions);
|
---|
1618 | # 0x00011c00 & 0xfffee000 = 0x0 (0)
|
---|
1619 |
|
---|
1620 | #
|
---|
1621 | # Whether to bother compiling the mask zip/unzip functions.
|
---|
1622 | #
|
---|
1623 | # The goal here is to keep the {built-in method builtins.eval} line far
|
---|
1624 | # away from top of the profiler stats, while at the same time keeping the
|
---|
1625 | # __zipMaskN and __unzipMaskN methods from taking up too much time.
|
---|
1626 | #
|
---|
1627 | fCompileMaskZipUnzip = cInstructions >= 12;
|
---|
1628 |
|
---|
1629 | #
|
---|
1630 | # Work thru the possible masks and test out the variations (brute force style).
|
---|
1631 | #
|
---|
1632 | uCostBest = uMaxCost;
|
---|
1633 | cChildrenBits = 0;
|
---|
1634 | fChildrenBest = 0;
|
---|
1635 | dChildrenBest = {};
|
---|
1636 |
|
---|
1637 | fMaskNotDoneYet = 0xffffffff;
|
---|
1638 | fCheckedValue = self.fCheckedValue; # (Cache it as a local variable for speed.)
|
---|
1639 | iOuterLoop = -1;
|
---|
1640 | for fOrgMask, cOccurences in dMaskCounts.most_common(3):
|
---|
1641 | iOuterLoop += 1;
|
---|
1642 |
|
---|
1643 | # Determin the max and min table sizes (in bits) based on the instructions using the mask.
|
---|
1644 | cMinTableSizeInBits = cOccurences.bit_length() - 1;
|
---|
1645 | if (1 << cMinTableSizeInBits) < cOccurences:
|
---|
1646 | cMinTableSizeInBits += 1;
|
---|
1647 | cMaxTableSizeInBits = self.kacMaxTableSizesInBits[cMinTableSizeInBits]; # Not quite sure about this...
|
---|
1648 | cMinTableSizeInBits -= 1;
|
---|
1649 |
|
---|
1650 | if uDepth <= 2:
|
---|
1651 | self.dprint(uDepth,
|
---|
1652 | '%s Start/%u: %#010x (%u) - %u/%u instructions - tab size %u-%u; fChecked=%#x/%#x uCostBest=%#x'
|
---|
1653 | % (('=' if iOuterLoop == 0 else '-') * 5, iOuterLoop, fOrgMask,
|
---|
1654 | self.popCount(fOrgMask), cOccurences, cInstructions, cMinTableSizeInBits, cMaxTableSizeInBits,
|
---|
1655 | fCheckedValue, fCheckedMask, uCostBest,));
|
---|
1656 |
|
---|
1657 | # Skip pointless stuff and things we've already covered.
|
---|
1658 | if cOccurences >= 2 and fOrgMask > 0 and fOrgMask != 0xffffffff and (fOrgMask & fMaskNotDoneYet) != 0:
|
---|
1659 | #
|
---|
1660 | # Step 1: Brute force relevant mask variations and pick a few masks.
|
---|
1661 | #
|
---|
1662 | # The MaskIterator skips masks that are too wide, too fragmented or
|
---|
1663 | # already covered.
|
---|
1664 | #
|
---|
1665 | # The cost calculation is mainly based on distribution vs table size,
|
---|
1666 | # trying to favor masks with more target slots.
|
---|
1667 | #
|
---|
1668 | dCandidates = {};
|
---|
1669 | for fMask, cMaskBits, aaiMaskToIdxAlgo in MaskIterator(fOrgMask, cMinTableSizeInBits, cMaxTableSizeInBits,
|
---|
1670 | fMaskNotDoneYet):
|
---|
1671 | #if uDepth <= 2:
|
---|
1672 | # self.dprint(uDepth, '1>> fMask=%#010x cMaskBits=%s aaiMaskToIdxAlgo=%s...'
|
---|
1673 | # % (fMask, cMaskBits, aaiMaskToIdxAlgo));
|
---|
1674 | #assert cMaskBits <= cMaxTableSizeInBits;
|
---|
1675 |
|
---|
1676 | # Calculate base cost and check it against uCostBest before continuing.
|
---|
1677 | uCostTmp = 256; # 256 = kCostIndirectCall
|
---|
1678 | uCostTmp += (len(aaiMaskToIdxAlgo) - 1) * 2; # 2 = kCostPerExtraIndexStep
|
---|
1679 | if uCostTmp >= uCostBest:
|
---|
1680 | #if uDepth <= 2:
|
---|
1681 | # self.dprint(uDepth, '!!! %#010x too expensive #1: %#x vs %#x' % (fMask, uCostTmp, uCostBest));
|
---|
1682 | continue;
|
---|
1683 |
|
---|
1684 | # Compile the indexing/unindexing functions.
|
---|
1685 | fnToIndex = MaskZipper.algoToZipLambda(aaiMaskToIdxAlgo, fMask, fCompileMaskZipUnzip);
|
---|
1686 |
|
---|
1687 | # Insert the instructions into the temporary table.
|
---|
1688 | daoTmp = collections.defaultdict(list);
|
---|
1689 | for oInstr in self.aoInstructions:
|
---|
1690 | idx = fnToIndex(oInstr.fFixedValue, aaiMaskToIdxAlgo);
|
---|
1691 | #self.dprint(uDepth, '%#010x -> %#05x %s' % (oInstr.fFixedValue, idx, oInstr.sName));
|
---|
1692 | daoTmp[idx].append(oInstr);
|
---|
1693 |
|
---|
1694 | # Reject anything that ends up putting all the stuff in a single slot.
|
---|
1695 | if len(daoTmp) <= 1:
|
---|
1696 | #if uDepth <= 2: self.dprint(uDepth, '!!! bad distribution #1: fMask=%#x' % (fMask,));
|
---|
1697 | continue;
|
---|
1698 |
|
---|
1699 | # Add cost for poor average distribution.
|
---|
1700 | rdAvgLen = float(cInstructions) / len(daoTmp);
|
---|
1701 | if rdAvgLen > 1.2:
|
---|
1702 | uCostTmp += int(rdAvgLen * 8)
|
---|
1703 | if uCostTmp >= uCostBest:
|
---|
1704 | #if uDepth <= 2:
|
---|
1705 | # self.dprint(uDepth, '!!! %#010x too expensive #2: %#x vs %#x (rdAvgLen=%s)'
|
---|
1706 | # % (fMask, uCostTmp, uCostBest, rdAvgLen));
|
---|
1707 | continue;
|
---|
1708 |
|
---|
1709 | # Add the cost for unused entries under reasonable table population.
|
---|
1710 | cNominalFill = 1 << (cMaskBits - 1); # 50% full or better is great.
|
---|
1711 | if len(daoTmp) < cNominalFill:
|
---|
1712 | uCostTmp += ((cNominalFill - len(daoTmp)) * 2); # 2 = kCostUnusedTabEntry
|
---|
1713 | if uCostTmp >= uCostBest:
|
---|
1714 | #if uDepth <= 2:
|
---|
1715 | # self.dprint(uDepth, '!!! %#010x too expensive #3: %#x vs %#x' % (fMask, uCostTmp, uCostBest));
|
---|
1716 | continue;
|
---|
1717 |
|
---|
1718 | # Record it as a candidate.
|
---|
1719 | dCandidates[uCostTmp] = (fMask, cMaskBits, aaiMaskToIdxAlgo, daoTmp);
|
---|
1720 | if len(dCandidates) > 64:
|
---|
1721 | dOld = dCandidates;
|
---|
1722 | dCandidates = { uKey:dOld[uKey] for uKey in sorted(dCandidates.keys())[:4] };
|
---|
1723 | del dOld;
|
---|
1724 |
|
---|
1725 | #
|
---|
1726 | # Step 2: Process the top 4 candidates.
|
---|
1727 | #
|
---|
1728 | for uCostTmp in sorted(dCandidates.keys())[:4]:
|
---|
1729 | fMask, cMaskBits, aaiMaskToIdxAlgo, daoTmp = dCandidates[uCostTmp];
|
---|
1730 |
|
---|
1731 | #if uDepth <= 2:
|
---|
1732 | # self.dprint(uDepth, '2>> fMask=%#010x cMaskBits=%s aaiMaskToIdxAlgo=%s #daoTmp=%s...'
|
---|
1733 | # % (fMask, cMaskBits, aaiMaskToIdxAlgo, len(daoTmp),));
|
---|
1734 | #assert cMaskBits <= cMaxTableSizeInBits;
|
---|
1735 |
|
---|
1736 | # Construct decoder nodes from the aaoTmp lists, construct sub-levels and calculate costs.
|
---|
1737 | fnFromIndex = MaskZipper.algoToUnzipLambda(aaiMaskToIdxAlgo, fMask, fCompileMaskZipUnzip);
|
---|
1738 | dChildrenTmp = {};
|
---|
1739 | try:
|
---|
1740 | for idx, aoInstrs in daoTmp.items():
|
---|
1741 | oChild = DecoderNode(aoInstrs,
|
---|
1742 | fCheckedMask | fMask,
|
---|
1743 | fCheckedValue | fnFromIndex(idx, aaiMaskToIdxAlgo));
|
---|
1744 | dChildrenTmp[idx] = oChild;
|
---|
1745 | uCostTmp += oChild.constructNextLevel(uDepth + 1, uCostBest - uCostTmp);
|
---|
1746 | if uCostTmp >= uCostBest:
|
---|
1747 | break;
|
---|
1748 | except DecoderNode.TooExpensive:
|
---|
1749 | #if uDepth <= 2:
|
---|
1750 | # self.dprint(uDepth, '!!! %#010x too expensive #4: %#x+child vs %#x' % (fMask, uCostTmp, uCostBest));
|
---|
1751 | continue;
|
---|
1752 |
|
---|
1753 | # Is this mask better than the previous?
|
---|
1754 | if uCostTmp < uCostBest:
|
---|
1755 | if uDepth <= 2:
|
---|
1756 | self.dprint(uDepth,
|
---|
1757 | '+++ %s best! %#010x (%u) uCost=%#x; %u ins in %u slots (previous %#010x / %#x) ...'
|
---|
1758 | % ('New' if cChildrenBits else '1st', fMask, cMaskBits, uCostTmp,
|
---|
1759 | cInstructions, len(dChildrenTmp), fChildrenBest, uCostBest, ));
|
---|
1760 | uCostBest = uCostTmp;
|
---|
1761 | cChildrenBits = cMaskBits;
|
---|
1762 | fChildrenBest = fMask;
|
---|
1763 | dChildrenBest = dChildrenTmp;
|
---|
1764 | #elif uDepth <= 2:
|
---|
1765 | # self.dprint(uDepth, '!!! %#010x too expensive #5: %#x vs %#x' % (fMask, uCostTmp, uCostBest));
|
---|
1766 |
|
---|
1767 | # Note that we've covered all the permutations in the given mask.
|
---|
1768 | fMaskNotDoneYet &= ~fOrgMask;
|
---|
1769 |
|
---|
1770 | # Drop it if too expensive.
|
---|
1771 | if uCostBest >= uMaxCost:
|
---|
1772 | raise DecoderNode.TooExpensive();
|
---|
1773 |
|
---|
1774 | if dChildrenBest is None:
|
---|
1775 | print('warning! No solution! depth=%u #Instruction=%u' % (uDepth, cInstructions));
|
---|
1776 | raise Exception('fixme')
|
---|
1777 |
|
---|
1778 | #assert fChildrenBest.bit_count() == cChildrenBits;
|
---|
1779 | #assert len(dChildrenBest) <= (1 << cChildrenBits)
|
---|
1780 | if uDepth <= 2:
|
---|
1781 | self.dprint(uDepth,
|
---|
1782 | '===== Final: fMask=%#010x (%u) uCost=%#x #Instructions=%u in %u slots over %u entries...'
|
---|
1783 | % (fChildrenBest, cChildrenBits, uCostBest, cInstructions, len(dChildrenBest), 1 << cChildrenBits));
|
---|
1784 |
|
---|
1785 | # Done.
|
---|
1786 | self.fChildMask = fChildrenBest;
|
---|
1787 | self.dChildren = dChildrenBest;
|
---|
1788 |
|
---|
1789 | return uCostBest;
|
---|
1790 |
|
---|
1791 | def setInstrProps(self, uDepth):
|
---|
1792 | """
|
---|
1793 | Sets the fDecoderLeafCheckNeeded instruction property.
|
---|
1794 | """
|
---|
1795 | if not self.dChildren:
|
---|
1796 | assert len(self.aoInstructions) != 1 or self.fChildMask in (0, DecoderNode.kChildMaskOpcodeValueIf);
|
---|
1797 | assert len(self.aoInstructions) == 1 or self.fChildMask == DecoderNode.kChildMaskMultipleOpcodeValueIfs;
|
---|
1798 | for oInstr in self.aoInstructions:
|
---|
1799 | oInstr.fDecoderLeafCheckNeeded = self.fChildMask == DecoderNode.kChildMaskOpcodeValueIf;
|
---|
1800 | else:
|
---|
1801 | for oChildNode in self.dChildren.values():
|
---|
1802 | oChildNode.setInstrProps(uDepth + 1);
|
---|
1803 |
|
---|
1804 | def getFuncName(self, uDepth):
|
---|
1805 | """
|
---|
1806 | Returns the function name at the specific depth.
|
---|
1807 | """
|
---|
1808 | if self.dChildren or len(self.aoInstructions) > 1:
|
---|
1809 | return 'iemDecodeA64_%08x_%08x_%u' % (self.fCheckedMask, self.fCheckedValue, uDepth,);
|
---|
1810 | return 'iemDecodeA64_%s' % (self.aoInstructions[0].getCName(),);
|
---|
1811 |
|
---|
1812 | #
|
---|
1813 | # Generators
|
---|
1814 | #
|
---|
1815 |
|
---|
1816 | class IEMArmGenerator(object):
|
---|
1817 |
|
---|
1818 | def __init__(self):
|
---|
1819 | self.oDecoderRoot = None;
|
---|
1820 | self.asRootIndexExpr = None;
|
---|
1821 |
|
---|
1822 |
|
---|
1823 | def constructDecoder(self):
|
---|
1824 | """
|
---|
1825 | Creates the decoder to the best our abilities.
|
---|
1826 | """
|
---|
1827 | self.oDecoderRoot = DecoderNode(sorted(g_aoAllArmInstructions,
|
---|
1828 | key = operator.attrgetter('fFixedMask', 'fFixedValue', 'sName')),#[:384],
|
---|
1829 | 0, 0);
|
---|
1830 | self.oDecoderRoot.constructNextLevel(0, sys.maxsize);
|
---|
1831 |
|
---|
1832 | # Set the fDecoderLeafCheckNeeded property of the instructions.
|
---|
1833 | self.oDecoderRoot.setInstrProps(0);
|
---|
1834 |
|
---|
1835 |
|
---|
1836 | def generateLicenseHeader(self):
|
---|
1837 | """
|
---|
1838 | Returns the lines for a license header.
|
---|
1839 | """
|
---|
1840 | return [
|
---|
1841 | '/*',
|
---|
1842 | ' * Autogenerated by $Id: bsd-spec-analyze.py 108904 2025-04-09 00:16:57Z vboxsync $ ',
|
---|
1843 | ' * Do not edit!',
|
---|
1844 | ' */',
|
---|
1845 | '',
|
---|
1846 | '/*',
|
---|
1847 | ' * Copyright (C) 2025-' + str(datetime.date.today().year) + ' Oracle and/or its affiliates.',
|
---|
1848 | ' *',
|
---|
1849 | ' * This file is part of VirtualBox base platform packages, as',
|
---|
1850 | ' * available from https://www.virtualbox.org.',
|
---|
1851 | ' *',
|
---|
1852 | ' * This program is free software; you can redistribute it and/or',
|
---|
1853 | ' * modify it under the terms of the GNU General Public License',
|
---|
1854 | ' * as published by the Free Software Foundation, in version 3 of the',
|
---|
1855 | ' * License.',
|
---|
1856 | ' *',
|
---|
1857 | ' * This program is distributed in the hope that it will be useful, but',
|
---|
1858 | ' * WITHOUT ANY WARRANTY; without even the implied warranty of',
|
---|
1859 | ' * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU',
|
---|
1860 | ' * General Public License for more details.',
|
---|
1861 | ' *',
|
---|
1862 | ' * You should have received a copy of the GNU General Public License',
|
---|
1863 | ' * along with this program; if not, see <https://www.gnu.org/licenses>.',
|
---|
1864 | ' *',
|
---|
1865 | ' * The contents of this file may alternatively be used under the terms',
|
---|
1866 | ' * of the Common Development and Distribution License Version 1.0',
|
---|
1867 | ' * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included',
|
---|
1868 | ' * in the VirtualBox distribution, in which case the provisions of the',
|
---|
1869 | ' * CDDL are applicable instead of those of the GPL.',
|
---|
1870 | ' *',
|
---|
1871 | ' * You may elect to license modified versions of this file under the',
|
---|
1872 | ' * terms and conditions of either the GPL or the CDDL or both.',
|
---|
1873 | ' *',
|
---|
1874 | ' * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0',
|
---|
1875 | ' */',
|
---|
1876 | '',
|
---|
1877 | '',
|
---|
1878 | '',
|
---|
1879 | ];
|
---|
1880 |
|
---|
1881 | def generateImplementationStubs(self):
|
---|
1882 | """
|
---|
1883 | Generate implementation stubs.
|
---|
1884 | """
|
---|
1885 | return [];
|
---|
1886 |
|
---|
1887 | def generateDecoderFunctions(self):
|
---|
1888 | """
|
---|
1889 | Generates the leaf decoder functions.
|
---|
1890 | """
|
---|
1891 |
|
---|
1892 | class CExprHelper(object):
|
---|
1893 | def __init__(self, oInstr):
|
---|
1894 | self.oInstr = oInstr;
|
---|
1895 |
|
---|
1896 | def getFieldInfo(self, sName):
|
---|
1897 | oField = oInstr.getFieldByName(sName)
|
---|
1898 | return (sName, oField.cBitsWidth);
|
---|
1899 |
|
---|
1900 | def convertFunctionCall(self, oCall):
|
---|
1901 | if oCall.sName == 'IsFeatureImplemented':
|
---|
1902 | if len(oCall.aoArgs) != 1:
|
---|
1903 | raise Exception('Unexpected argument count for IsFeatureImplemented call: %s' % (oCall.aoArgs,));
|
---|
1904 | if not isinstance(oCall.aoArgs[0], ArmAstIdentifier):
|
---|
1905 | raise Exception('Argument to IsFeatureImplemented is not an identifier: %s (%s)'
|
---|
1906 | % (oCall.aoArgs[0].sType, oCall.aoArgs[0]));
|
---|
1907 | sFeatureNm = oCall.aoArgs[0].sName;
|
---|
1908 | sCpumFeature = g_dSpecFeatToCpumFeat.get(sFeatureNm, None);
|
---|
1909 | if sCpumFeature is None:
|
---|
1910 | raise Exception('IsFeatureImplemented parameter not known: %s (see g_dSpecFeatToCpumFeat)'
|
---|
1911 | % (sFeatureNm));
|
---|
1912 | if sCpumFeature is False:
|
---|
1913 | return 'false /** @todo IEM_GET_GUEST_CPU_FEATURES(pVCpu)->%s*/' % (sFeatureNm,);
|
---|
1914 | return 'IEM_GET_GUEST_CPU_FEATURES(pVCpu)->%s /*%s*/' % (sCpumFeature, sFeatureNm,)
|
---|
1915 | raise Exception('Call to unsupported function: %s (%s)' % (oCall.sName, oCall.aoArgs,));
|
---|
1916 |
|
---|
1917 | asLines = [];
|
---|
1918 | for oInstr in g_aoAllArmInstructions:
|
---|
1919 | sCName = oInstr.getCName();
|
---|
1920 | asLines += [
|
---|
1921 | '',
|
---|
1922 | '/* %08x/%08x: %s */' % (oInstr.fFixedMask, oInstr.fFixedValue, oInstr.sAsmDisplay,),
|
---|
1923 | 'FNIEMOP_DEF_1(iemDecodeA64_%s, uint32_t, uOpcode)' % (sCName,),
|
---|
1924 | '{',
|
---|
1925 | ];
|
---|
1926 |
|
---|
1927 | # The final decoding step, if needed.
|
---|
1928 | sIndent = '';
|
---|
1929 | asTail = [];
|
---|
1930 | if oInstr.fDecoderLeafCheckNeeded:
|
---|
1931 | asLines += [
|
---|
1932 | ' if ((uOpcode & UINT32_C(%#010x)) == UINT32_C(%#010x))' % (oInstr.fFixedMask, oInstr.fFixedValue,),
|
---|
1933 | ' {',
|
---|
1934 | ];
|
---|
1935 | asTail = [
|
---|
1936 | ' LogFlow(("Invalid instruction %%#x at %%x\\n", uOpcode, pVCpu->cpum.GstCtx.Pc.u64));',
|
---|
1937 | ' IEMOP_RAISE_INVALID_OPCODE_RET();',
|
---|
1938 | '}',
|
---|
1939 | ];
|
---|
1940 | sIndent = ' ';
|
---|
1941 |
|
---|
1942 |
|
---|
1943 | # Decode the fields and prepare for passing them as arguments.
|
---|
1944 | asArgs = [];
|
---|
1945 | sLogFmt = '';
|
---|
1946 | for oField in sorted(oInstr.aoEncodesets, key = operator.attrgetter('iFirstBit')): # ArmEncodesetField
|
---|
1947 | if oField.sName:
|
---|
1948 | asArgs.append(oField.sName);
|
---|
1949 | if oField.cBitsWidth < 4:
|
---|
1950 | sLogFmt += ' %s=%%u' % (oField.sName,)
|
---|
1951 | else:
|
---|
1952 | sLogFmt += ' %s=%%#x' % (oField.sName,)
|
---|
1953 | asLines.append('%s uint32_t const %-10s = (uOpcode >> %2u) & %#010x;'
|
---|
1954 | % (sIndent, oField.sName, oField.iFirstBit, (1 << oField.cBitsWidth) - 1,));
|
---|
1955 |
|
---|
1956 | # Any additional conditions for the instructions.
|
---|
1957 | if not oInstr.oCondition.isBoolAndTrue():
|
---|
1958 | asLines += [
|
---|
1959 | sIndent + ' if (' + oInstr.oCondition.toCExpr(CExprHelper(oInstr)) + ')',
|
---|
1960 | sIndent + ' {',
|
---|
1961 | ];
|
---|
1962 |
|
---|
1963 | asTail = [
|
---|
1964 | sIndent + ' LogFlow(("Invalid instruction %%#x at %%x (cond)\\n", uOpcode, pVCpu->cpum.GstCtx.Pc.u64));',
|
---|
1965 | sIndent + ' IEMOP_RAISE_INVALID_OPCODE_RET();',
|
---|
1966 | sIndent + '}',
|
---|
1967 | ] + asTail;
|
---|
1968 | sIndent += ' ';
|
---|
1969 |
|
---|
1970 | # Log and call implementation.
|
---|
1971 | asLines += [
|
---|
1972 | '%s LogFlow(("%%010x: %s%s\\n",%s));' % (sIndent, sCName, sLogFmt, ', '.join(['uOpcode',] + asArgs),),
|
---|
1973 | '#ifdef HAS_IMPL_%s' % (sCName,),
|
---|
1974 | '%s return iemImpl_%s(%s);' % (sIndent, sCName, ', '.join(['pVCpu',] + asArgs),),
|
---|
1975 | '#else',
|
---|
1976 | '%s RT_NOREF(%s);' % (sIndent, ', '.join(asArgs + ['pVCpu', 'uOpcode',]),),
|
---|
1977 | '%s return VERR_IEM_INSTR_NOT_IMPLEMENTED;' % (sIndent,),
|
---|
1978 | '#endif',
|
---|
1979 | '%s}' % (sIndent),
|
---|
1980 | ];
|
---|
1981 |
|
---|
1982 | asLines.extend(asTail);
|
---|
1983 | return asLines;
|
---|
1984 |
|
---|
1985 | def generateDecoderCodeMultiIfFunc(self, oNode, uDepth):
|
---|
1986 | """
|
---|
1987 | Handles a leaf node.
|
---|
1988 | """
|
---|
1989 | assert not oNode.dChildren, \
|
---|
1990 | 'fChildMask=%#x dChildren=%s aoInstr=%s' % (oNode.fChildMask, oNode.dChildren, oNode.aoInstructions,);
|
---|
1991 |
|
---|
1992 | asLines = [
|
---|
1993 | '',
|
---|
1994 | '/* %08x/%08x level %u */' % (oNode.fCheckedMask, oNode.fCheckedValue, uDepth,),
|
---|
1995 | 'FNIEMOP_DEF_1(%s, uint32_t, uOpcode)' % (oNode.getFuncName(uDepth),),
|
---|
1996 | '{',
|
---|
1997 | ];
|
---|
1998 | ## @todo check if the masks are restricted to a few bit differences at
|
---|
1999 | ## this point and we can skip the iemDecodeA64_Invalid call.
|
---|
2000 | for oInstr in oNode.aoInstructions:
|
---|
2001 | asLines += [
|
---|
2002 | ' if ((uOpcode & UINT32_C(%#010x)) == UINT32_C(%#010x))' % (oInstr.fFixedMask, oInstr.fFixedValue,),
|
---|
2003 | ' return iemDecodeA64_%s(pVCpu, uOpcode);' % (oInstr.getCName(),),
|
---|
2004 | ];
|
---|
2005 | asLines += [
|
---|
2006 | ' return iemDecodeA64_Invalid(pVCpu, uOpcode);',
|
---|
2007 | '}',
|
---|
2008 | ];
|
---|
2009 | return asLines;
|
---|
2010 |
|
---|
2011 | def generateDecoderCode(self, oNode, uDepth):
|
---|
2012 | """
|
---|
2013 | Recursively generates the decoder code.
|
---|
2014 | """
|
---|
2015 | assert oNode.fChildMask != 0 and oNode.fChildMask not in (0x7fffffff, 0xffffffff, 0x4fffffff), \
|
---|
2016 | 'fChildMask=%s #dChildren=%s aoInstr=%s' % (oNode.fChildMask, len(oNode.dChildren), oNode.aoInstructions,);
|
---|
2017 | asLines = [];
|
---|
2018 |
|
---|
2019 | # First recurse.
|
---|
2020 | cLeafEntries = 0;
|
---|
2021 | cMultiIfEntries = 0;
|
---|
2022 | for idx in sorted(oNode.dChildren):
|
---|
2023 | oChildNode = oNode.dChildren[idx];
|
---|
2024 | if oChildNode.dChildren:
|
---|
2025 | asLines += self.generateDecoderCode(oChildNode, uDepth + 1);
|
---|
2026 | elif oChildNode.fChildMask == DecoderNode.kChildMaskMultipleOpcodeValueIfs:
|
---|
2027 | assert len(oChildNode.aoInstructions) > 1;
|
---|
2028 | asLines += self.generateDecoderCodeMultiIfFunc(oChildNode, uDepth + 1);
|
---|
2029 | cMultiIfEntries += 1;
|
---|
2030 | else:
|
---|
2031 | assert len(oChildNode.aoInstructions) == 1;
|
---|
2032 | assert oChildNode.fChildMask in [DecoderNode.kChildMaskOpcodeValueIf, 0];
|
---|
2033 | cLeafEntries += 1;
|
---|
2034 |
|
---|
2035 | # Generate the function. For the top level we just do the table, as
|
---|
2036 | # the functions are static and we need the interpreter code to be able
|
---|
2037 | # to address the symbol and this is the speedier way.
|
---|
2038 | cTabEntries = 1 << oNode.fChildMask.bit_count();
|
---|
2039 | asLines += [
|
---|
2040 | '',
|
---|
2041 | '/* %08x/%08x level %u - mask=%#x entries=%#x valid=%%%u (%#x) leaf=%%%u (%#x) multi-if=%%%u (%#x) */'
|
---|
2042 | % (oNode.fCheckedMask, oNode.fCheckedValue, uDepth, oNode.fChildMask, cTabEntries,
|
---|
2043 | int(round(len(oNode.dChildren) * 100.0 / cTabEntries)), len(oNode.dChildren),
|
---|
2044 | int(round(cLeafEntries * 100.0 / cTabEntries)), cLeafEntries,
|
---|
2045 | int(round(cMultiIfEntries * 100.0 / cTabEntries)), cMultiIfEntries, ),
|
---|
2046 | ];
|
---|
2047 | if uDepth > 0:
|
---|
2048 | asLines += [
|
---|
2049 | 'FNIEMOP_DEF_1(%s, uint32_t, uOpcode)' % (oNode.getFuncName(uDepth),),
|
---|
2050 | '{',
|
---|
2051 | ' static PFIEMOPU32 const s_apfn[] = ',
|
---|
2052 | ' {',
|
---|
2053 | ];
|
---|
2054 | sTabNm = 's_apfn';
|
---|
2055 | sIndent = ' ';
|
---|
2056 | else:
|
---|
2057 | asLines += [
|
---|
2058 | 'PFIEMOPU32 const g_apfnIemInterpretOnlyA64[] = ',
|
---|
2059 | '{',
|
---|
2060 | ];
|
---|
2061 | sTabNm = 'g_apfnIemInterpretOnlyA64';
|
---|
2062 | sIndent = '';
|
---|
2063 |
|
---|
2064 | idxPrev = -1;
|
---|
2065 | for idx in sorted(oNode.dChildren):
|
---|
2066 | oChildNode = oNode.dChildren[idx];
|
---|
2067 | idxPrev += 1;
|
---|
2068 | while idxPrev < idx:
|
---|
2069 | asLines.append(sIndent + ' iemDecodeA64_Invalid, // %s' % (idxPrev,));
|
---|
2070 | idxPrev += 1;
|
---|
2071 | asLines.append('%s %s,' % (sIndent, oChildNode.getFuncName(uDepth + 1),));
|
---|
2072 |
|
---|
2073 | while idxPrev + 1 < cTabEntries:
|
---|
2074 | idxPrev += 1;
|
---|
2075 | asLines.append(sIndent + ' iemDecodeA64_Invalid, // %s' % (idxPrev,));
|
---|
2076 |
|
---|
2077 | asLines += [
|
---|
2078 | '%s};' % (sIndent,),
|
---|
2079 | '%sAssertCompile(RT_ELEMENTS(%s) == %#x);' % (sIndent, sTabNm, cTabEntries,),
|
---|
2080 | '',
|
---|
2081 | ];
|
---|
2082 |
|
---|
2083 | # Extract the index from uOpcode.
|
---|
2084 | aaiAlgo = MaskZipper.compileAlgo(oNode.fChildMask);
|
---|
2085 | assert aaiAlgo, 'fChildMask=%s #children=%s instrs=%s' % (oNode.fChildMask, len(oNode.dChildren), oNode.aoInstructions,);
|
---|
2086 | asIdx = [
|
---|
2087 | ' /* fMask=%#010x -> %#010x */' % (oNode.fChildMask, cTabEntries - 1),
|
---|
2088 | ' uintptr_t const idx = ((uOpcode >> %2u) & UINT32_C(%#010x)) /* bit %2u L %u -> 0 */'
|
---|
2089 | % (aaiAlgo[0][0], aaiAlgo[0][2], aaiAlgo[0][0], aaiAlgo[0][2].bit_count(), ),
|
---|
2090 | ];
|
---|
2091 | for iSrcBit, iDstBit, fMask in aaiAlgo[1:]:
|
---|
2092 | asIdx.append(' | ((uOpcode >> %2u) & UINT32_C(%#010x)) /* bit %2u L %u -> %u */'
|
---|
2093 | % (iSrcBit - iDstBit, fMask << iDstBit, iSrcBit, fMask.bit_count(), iDstBit));
|
---|
2094 | asIdx[-1] += ';';
|
---|
2095 |
|
---|
2096 | # Make the call and complete the function. For the top level, we save
|
---|
2097 | # the expression so we can later put it in a header file.
|
---|
2098 | if uDepth > 0:
|
---|
2099 | asLines += asIdx;
|
---|
2100 | asLines += [
|
---|
2101 | ' return s_apfn[idx](pVCpu, uOpcode);',
|
---|
2102 | '}'
|
---|
2103 | ];
|
---|
2104 | else:
|
---|
2105 | self.asRootIndexExpr = asIdx;
|
---|
2106 | return asLines;
|
---|
2107 |
|
---|
2108 | def generateDecoderCpp(self, sFilename, iPartNo):
|
---|
2109 | """ Generates the decoder data & code. """
|
---|
2110 | _ = iPartNo; _ = sFilename;
|
---|
2111 | asLines = self.generateLicenseHeader();
|
---|
2112 | asLines.extend([
|
---|
2113 | '#define LOG_GROUP LOG_GROUP_IEM',
|
---|
2114 | '#define VMCPU_INCL_CPUM_GST_CTX',
|
---|
2115 | '#include "IEMInternal.h"',
|
---|
2116 | '#include <VBox/vmm/vm.h>',
|
---|
2117 | '#include "VBox/err.h"',
|
---|
2118 | '',
|
---|
2119 | '#include "iprt/armv8.h"',
|
---|
2120 | '',
|
---|
2121 | '#include "IEMMc.h"',
|
---|
2122 | '',
|
---|
2123 | '',
|
---|
2124 | '/** Invalid instruction decoder function. */',
|
---|
2125 | 'FNIEMOP_DEF_1(iemDecodeA64_Invalid, uint32_t, uOpcode)',
|
---|
2126 | '{',
|
---|
2127 | ' LogFlow(("Invalid instruction %%#x at %%x\\n", uOpcode, pVCpu->cpum.GstCtx.Pc.u64));',
|
---|
2128 | ' IEMOP_RAISE_INVALID_OPCODE_RET();',
|
---|
2129 | '}',
|
---|
2130 | ]);
|
---|
2131 |
|
---|
2132 | asLines += self.generateDecoderFunctions();
|
---|
2133 |
|
---|
2134 | assert self.oDecoderRoot.dChildren;
|
---|
2135 | asLines += self.generateDecoderCode(self.oDecoderRoot, 0);
|
---|
2136 |
|
---|
2137 | return (True, asLines);
|
---|
2138 |
|
---|
2139 | def generateDecoderHdr(self, sFilename, iPartNo):
|
---|
2140 | """ Generates the decoder header file. """
|
---|
2141 | _ = iPartNo;
|
---|
2142 |
|
---|
2143 | asLines = self.generateLicenseHeader();
|
---|
2144 | sBlockerName = re.sub('[.-]', '_', os.path.basename(sFilename));
|
---|
2145 | asLines += [
|
---|
2146 | '#ifndef VMM_INCLUDED_SRC_VMMAll_target_armv8_%s' % (sBlockerName,),
|
---|
2147 | '#define VMM_INCLUDED_SRC_VMMAll_target_armv8_%s' % (sBlockerName,),
|
---|
2148 | '#ifndef RT_WITHOUT_PRAGMA_ONCE',
|
---|
2149 | '# pragma once',
|
---|
2150 | '#endif',
|
---|
2151 | '',
|
---|
2152 | '/** The top-level aarch64 decoder table for the IEM interpreter. */',
|
---|
2153 | 'extern PFIEMOPU32 const g_apfnIemInterpretOnlyA64[%#x];' % (1 << self.oDecoderRoot.fChildMask.bit_count()),
|
---|
2154 | '',
|
---|
2155 | '/**',
|
---|
2156 | ' * Calculates the index for @a uOpcode in g_apfnIemInterpretOnlyA64.',
|
---|
2157 | ' */',
|
---|
2158 | 'DECL_FORCE_INLINE(uintptr_t) iemInterpretOnlyA64CalcIndex(uint32_t uOpcode)',
|
---|
2159 | '{',
|
---|
2160 | ];
|
---|
2161 | assert self.asRootIndexExpr; # Must be called after generateDecoderCpp()!
|
---|
2162 | asLines += self.asRootIndexExpr;
|
---|
2163 | asLines += [
|
---|
2164 | ' return idx;',
|
---|
2165 | '}',
|
---|
2166 | '',
|
---|
2167 | '#endif /* !VMM_INCLUDED_SRC_VMMAll_target_armv8_%s */' % (sBlockerName,),
|
---|
2168 | ];
|
---|
2169 | return (True, asLines);
|
---|
2170 |
|
---|
2171 |
|
---|
2172 | def main(self, asArgs):
|
---|
2173 | """ Main function. """
|
---|
2174 |
|
---|
2175 | #
|
---|
2176 | # Parse arguments.
|
---|
2177 | #
|
---|
2178 | oArgParser = argparse.ArgumentParser(add_help = False);
|
---|
2179 | oArgParser.add_argument('--tar',
|
---|
2180 | metavar = 'AARCHMRS_BSD_A_profile-2024-12.tar.gz',
|
---|
2181 | dest = 'sTarFile',
|
---|
2182 | action = 'store',
|
---|
2183 | default = None,
|
---|
2184 | help = 'Specification TAR file to get the files from.');
|
---|
2185 | oArgParser.add_argument('--instructions',
|
---|
2186 | metavar = 'Instructions.json',
|
---|
2187 | dest = 'sFileInstructions',
|
---|
2188 | action = 'store',
|
---|
2189 | default = 'Instructions.json',
|
---|
2190 | help = 'The path to the instruction specficiation file.');
|
---|
2191 | oArgParser.add_argument('--features',
|
---|
2192 | metavar = 'Features.json',
|
---|
2193 | dest = 'sFileFeatures',
|
---|
2194 | action = 'store',
|
---|
2195 | default = 'Features.json',
|
---|
2196 | help = 'The path to the features specficiation file.');
|
---|
2197 | oArgParser.add_argument('--registers',
|
---|
2198 | metavar = 'Registers.json',
|
---|
2199 | dest = 'sFileRegisters',
|
---|
2200 | action = 'store',
|
---|
2201 | default = 'Registers.json',
|
---|
2202 | help = 'The path to the registers specficiation file.');
|
---|
2203 | oArgParser.add_argument('--spec-dir',
|
---|
2204 | metavar = 'dir',
|
---|
2205 | dest = 'sSpecDir',
|
---|
2206 | action = 'store',
|
---|
2207 | default = '',
|
---|
2208 | help = 'Specification directory to prefix the specficiation files with.');
|
---|
2209 | oArgParser.add_argument('--out-decoder-cpp',
|
---|
2210 | metavar = 'file-decoder.cpp',
|
---|
2211 | dest = 'sFileDecoderCpp',
|
---|
2212 | action = 'store',
|
---|
2213 | default = '-',
|
---|
2214 | help = 'The output C++ file for the decoder.');
|
---|
2215 | oArgParser.add_argument('--out-decoder-hdr',
|
---|
2216 | metavar = 'file-decoder.h',
|
---|
2217 | dest = 'sFileDecoderH',
|
---|
2218 | action = 'store',
|
---|
2219 | default = '-',
|
---|
2220 | help = 'The output header file for the decoder.');
|
---|
2221 | # debug:
|
---|
2222 | oArgParser.add_argument('--print-instructions',
|
---|
2223 | dest = 'fPrintInstructions',
|
---|
2224 | action = 'store_true',
|
---|
2225 | default = False,
|
---|
2226 | help = 'List the instructions after loading.');
|
---|
2227 | oArgParser.add_argument('--print-fixed-mask-stats',
|
---|
2228 | dest = 'fPrintFixedMaskStats',
|
---|
2229 | action = 'store_true',
|
---|
2230 | default = False,
|
---|
2231 | help = 'List statistics on fixed bit masks.');
|
---|
2232 | oArgParser.add_argument('--print-fixed-mask-top-10',
|
---|
2233 | dest = 'fPrintFixedMaskTop10',
|
---|
2234 | action = 'store_true',
|
---|
2235 | default = False,
|
---|
2236 | help = 'List the 10 top fixed bit masks.');
|
---|
2237 | # Do it!
|
---|
2238 | oOptions = oArgParser.parse_args(asArgs[1:]);
|
---|
2239 |
|
---|
2240 | #
|
---|
2241 | # Load the specification.
|
---|
2242 | #
|
---|
2243 | if LoadArmOpenSourceSpecification(oOptions):
|
---|
2244 | #
|
---|
2245 | # Sort out the decoding.
|
---|
2246 | #
|
---|
2247 | self.constructDecoder();
|
---|
2248 |
|
---|
2249 | #
|
---|
2250 | # Output.
|
---|
2251 | #
|
---|
2252 | aaoOutputFiles = [
|
---|
2253 | ( oOptions.sFileDecoderCpp, self.generateDecoderCpp, 0, ),
|
---|
2254 | ( oOptions.sFileDecoderH, self.generateDecoderHdr, 0, ), # Must be after generateDecoderCpp!
|
---|
2255 | ];
|
---|
2256 | fRc = True;
|
---|
2257 | for sOutFile, fnGenMethod, iPartNo in aaoOutputFiles:
|
---|
2258 | if sOutFile == '-':
|
---|
2259 | oOut = sys.stdout;
|
---|
2260 | else:
|
---|
2261 | try:
|
---|
2262 | oOut = open(sOutFile, 'w'); # pylint: disable=consider-using-with,unspecified-encoding
|
---|
2263 | except Exception as oXcpt:
|
---|
2264 | print('error! Failed open "%s" for writing: %s' % (sOutFile, oXcpt,), file = sys.stderr);
|
---|
2265 | return 1;
|
---|
2266 |
|
---|
2267 | (fRc2, asLines) = fnGenMethod(sOutFile, iPartNo);
|
---|
2268 | fRc = fRc2 and fRc;
|
---|
2269 |
|
---|
2270 | oOut.write('\n'.join(asLines));
|
---|
2271 | if oOut != sys.stdout:
|
---|
2272 | oOut.close();
|
---|
2273 | if fRc:
|
---|
2274 | return 0;
|
---|
2275 |
|
---|
2276 | return 1;
|
---|
2277 |
|
---|
2278 | def printException(oXcpt):
|
---|
2279 | print('----- Exception Caught! -----', flush = True);
|
---|
2280 | cMaxLines = 1;
|
---|
2281 | try: cchMaxLen = os.get_terminal_size()[0] * cMaxLines;
|
---|
2282 | except: cchMaxLen = 80 * cMaxLines;
|
---|
2283 | cchMaxLen -= len(' = ...');
|
---|
2284 |
|
---|
2285 | oTB = traceback.TracebackException.from_exception(oXcpt, limit = None, capture_locals = True);
|
---|
2286 | # No locals for the outer frame.
|
---|
2287 | oTB.stack[0].locals = {};
|
---|
2288 | # Suppress insanely long variable values.
|
---|
2289 | for oFrameSummary in oTB.stack:
|
---|
2290 | if oFrameSummary.locals:
|
---|
2291 | #for sToDelete in ['ddAsmRules', 'aoInstructions',]:
|
---|
2292 | # if sToDelete in oFrameSummary.locals:
|
---|
2293 | # del oFrameSummary.locals[sToDelete];
|
---|
2294 | for sKey, sValue in oFrameSummary.locals.items():
|
---|
2295 | if len(sValue) > cchMaxLen - len(sKey):
|
---|
2296 | sValue = sValue[:cchMaxLen - len(sKey)] + ' ...';
|
---|
2297 | if '\n' in sValue:
|
---|
2298 | sValue = sValue.split('\n')[0] + ' ...';
|
---|
2299 | oFrameSummary.locals[sKey] = sValue;
|
---|
2300 | idxFrame = 0;
|
---|
2301 | asFormatted = [];
|
---|
2302 | oReFirstFrameLine = re.compile(r'^ File ".*", line \d+, in ')
|
---|
2303 | for sLine in oTB.format():
|
---|
2304 | if oReFirstFrameLine.match(sLine):
|
---|
2305 | idxFrame += 1;
|
---|
2306 | asFormatted.append(sLine);
|
---|
2307 | for sLine in asFormatted:
|
---|
2308 | if oReFirstFrameLine.match(sLine):
|
---|
2309 | idxFrame -= 1;
|
---|
2310 | sLine = '#%u %s' % (idxFrame, sLine.lstrip());
|
---|
2311 | print(sLine);
|
---|
2312 | print('----', flush = True);
|
---|
2313 |
|
---|
2314 |
|
---|
2315 | if __name__ == '__main__':
|
---|
2316 | fProfileIt = False;
|
---|
2317 | oProfiler = cProfile.Profile() if fProfileIt else None;
|
---|
2318 | try:
|
---|
2319 | if not oProfiler:
|
---|
2320 | rcExit = IEMArmGenerator().main(sys.argv);
|
---|
2321 | else:
|
---|
2322 | rcExit = oProfiler.runcall(IEMArmGenerator().main, sys.argv);
|
---|
2323 | except Exception as oXcptOuter:
|
---|
2324 | printException(oXcptOuter);
|
---|
2325 | rcExit = 2;
|
---|
2326 | except KeyboardInterrupt as oXcptOuter:
|
---|
2327 | printException(oXcptOuter);
|
---|
2328 | rcExit = 2;
|
---|
2329 | if oProfiler:
|
---|
2330 | if not oProfiler:
|
---|
2331 | oProfiler.print_stats(sort='tottime');
|
---|
2332 | else:
|
---|
2333 | oStringStream = io.StringIO();
|
---|
2334 | pstats.Stats(oProfiler, stream = oStringStream).strip_dirs().sort_stats('tottime').print_stats(64);
|
---|
2335 | for iStatLine, sStatLine in enumerate(oStringStream.getvalue().split('\n')):
|
---|
2336 | if iStatLine > 20:
|
---|
2337 | asStatWords = sStatLine.split();
|
---|
2338 | if asStatWords[1] in { '0.000', '0.001', '0.002', '0.003', '0.004', '0.005' }:
|
---|
2339 | break;
|
---|
2340 | print(sStatLine);
|
---|
2341 | sys.exit(rcExit);
|
---|
2342 |
|
---|