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