VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMAll/target-armv8/bsd-spec-analyze.py

Last change on this file was 108957, checked in by vboxsync, 4 weeks ago

VMM/IEM: more scm fixes. jiraref:VBP-1598

  • Property svn:eol-style set to LF
  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
File size: 122.7 KB
Line 
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"""
7ARM BSD specification analyser.
8"""
9
10from __future__ import print_function;
11
12__copyright__ = \
13"""
14Copyright (C) 2025 Oracle and/or its affiliates.
15
16This file is part of VirtualBox base platform packages, as
17available from https://www.215389.xyz.
18
19This program is free software; you can redistribute it and/or
20modify it under the terms of the GNU General Public License
21as published by the Free Software Foundation, in version 3 of the
22License.
23
24This program is distributed in the hope that it will be useful, but
25WITHOUT ANY WARRANTY; without even the implied warranty of
26MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
27General Public License for more details.
28
29You should have received a copy of the GNU General Public License
30along with this program; if not, see <https://www.gnu.org/licenses>.
31
32SPDX-License-Identifier: GPL-3.0-only
33"""
34__version__ = "$Revision: 108957 $"
35
36# Standard python imports.
37import argparse;
38import collections;
39import datetime;
40import io;
41import json;
42import operator;
43import os;
44import re;
45import sys;
46import tarfile;
47import time;
48import traceback;
49# profiling:
50import cProfile;
51import pstats
52
53
54## Program start time for logging.
55g_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#
76g_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
360class 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
480class 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
600class 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
637class 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
665class 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
711class 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
740class 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
765class 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
788class 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
814class 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
839class 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
872class 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
965class 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
1019class 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
1062class 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
1090class 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
1117class 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.
1192g_oArmInstructionVerInfo = None;
1193
1194## All the instructions.
1195g_aoAllArmInstructions = [] # type: List[ArmInstruction]
1196
1197## All the instructions by name (not mnemonic).
1198g_dAllArmInstructionsByName = {} # type: Dict[ArmInstruction]
1199
1200## All the instructions by instruction set name.
1201g_dAllArmInstructionsBySet = collections.defaultdict(list) # type: Dict[List[ArmInstruction]]
1202
1203## All the instructions by (immediate) instruction group name.
1204g_dAllArmInstructionsByGroup = collections.defaultdict(list) # type: Dict[List[ArmInstruction]]
1205
1206## The instruction sets.
1207g_aoArmInstructionSets = [] # type: List[ArmInstructionSet]
1208
1209## The instruction sets by name.
1210g_dArmInstructionSets = {} # type: Dict[ArmInstructionSet]
1211
1212## The instruction groups.
1213g_aoArmInstructionGroups = [] # type: List[ArmInstructionGroup]
1214
1215## The instruction groups.
1216g_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.
1226g_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
1250def __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
1279def __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
1310def 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
1328def 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
1421def 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
1447def 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
1454def 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
1592def 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
1689class 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
1923class 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
1969class 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
2278class 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
2835def 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
2872if __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
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette