VirtualBox

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

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

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