VirtualBox

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

Last change on this file since 108233 was 108233, checked in by vboxsync, 3 months ago

VMM/IEM: ARM spec reading hacking.

  • Property svn:eol-style set to LF
  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
File size: 10.7 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3# $Id: bsd-spec-analyze.py 108233 2025-02-15 22:23:29Z vboxsync $
4
5"""
6ARM BSD specification analyser.
7"""
8
9from __future__ import print_function;
10
11__copyright__ = \
12"""
13Copyright (C) 2025 Oracle and/or its affiliates.
14
15This file is part of VirtualBox base platform packages, as
16available from https://www.215389.xyz.
17
18This program is free software; you can redistribute it and/or
19modify it under the terms of the GNU General Public License
20as published by the Free Software Foundation, in version 3 of the
21License.
22
23This program is distributed in the hope that it will be useful, but
24WITHOUT ANY WARRANTY; without even the implied warranty of
25MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
26General Public License for more details.
27
28You should have received a copy of the GNU General Public License
29along with this program; if not, see <https://www.gnu.org/licenses>.
30
31The contents of this file may alternatively be used under the terms
32of the Common Development and Distribution License Version 1.0
33(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
34in the VirtualBox distribution, in which case the provisions of the
35CDDL are applicable instead of those of the GPL.
36
37You may elect to license modified versions of this file under the
38terms and conditions of either the GPL or the CDDL or both.
39
40SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
41"""
42__version__ = "$Revision: 108233 $"
43
44# Standard python imports.
45import argparse;
46import collections;
47import json;
48import os;
49import sys;
50import tarfile;
51
52
53class ArmEncodesetField(object):
54 """
55 ARM Encodeset.Bits & Encodeset.Field.
56 """
57 def __init__(self, oJson, iFirstBit, cBitsWidth, fFixed, fValue, sName = None):
58 self.oJson = oJson;
59 self.iFirstBit = iFirstBit;
60 self.cBitsWidth = cBitsWidth;
61 self.fFixed = fFixed;
62 self.fValue = fValue;
63 self.sName = sName; ##< None if Encodeset.Bits.
64
65 def __str__(self):
66 sRet = '[%2u:%-2u] = %#x/%#x/%#x' % (
67 self.iFirstBit + self.cBitsWidth - 1, self.iFirstBit, self.fValue, self.fFixed, self.getMask()
68 );
69 if self.sName:
70 sRet += ' # %s' % (self.sName,)
71 return sRet;
72
73 def __repr__(self):
74 return self.__str__();
75
76 def getMask(self):
77 """ Field mask (unshifted). """
78 return (1 << self.cBitsWidth) - 1;
79
80 def getShiftedMask(self):
81 """ Field mask, shifted. """
82 return ((1 << self.cBitsWidth) - 1) << self.iFirstBit;
83
84 @staticmethod
85 def fromJson(oJson):
86 """ """
87 assert oJson['_type'] in ('Instruction.Encodeset.Field', 'Instruction.Encodeset.Bits'), oJson['_type'];
88
89 oRange = oJson['range'];
90 assert oRange['_type'] == 'Range';
91 iFirstBit = int(oRange['start']);
92 cBitsWidth = int(oRange['width']);
93
94 sValue = oJson['value']['value'];
95 assert sValue[0] == '\'' and sValue[-1] == '\'', sValue;
96 sValue = sValue[1:-1];
97 assert len(sValue) == cBitsWidth, 'cBitsWidth=%s sValue=%s' % (cBitsWidth, sValue,);
98 fFixed = 0;
99 fValue = 0;
100 for ch in sValue:
101 assert ch in 'x10', 'ch=%s' % ch;
102 fFixed <<= 1;
103 fValue <<= 1;
104 if ch != 'x':
105 fFixed |= 1;
106 if ch == '1':
107 fValue |= 1;
108
109 sName = oJson['name'] if oJson['_type'] == 'Instruction.Encodeset.Field' else None;
110 return ArmEncodesetField(oJson, iFirstBit, cBitsWidth, fFixed, fValue, sName);
111
112 @staticmethod
113 def fromJsonEncodeset(oJson, aoSet, fCovered):
114 """ """
115 assert oJson['_type'] == 'Instruction.Encodeset.Encodeset', oJson['_type'];
116 for oJsonValue in oJson['values']:
117 oNewField = ArmEncodesetField.fromJson(oJsonValue);
118 fNewMask = oNewField.getShiftedMask();
119 if (fNewMask & fCovered) != fNewMask:
120 aoSet.append(oNewField)
121 fCovered |= fNewMask;
122 return (aoSet, fCovered);
123
124
125class ArmInstruction(object):
126 """
127 ARM instruction
128 """
129 def __init__(self, oJson, sName, sMemonic, aoEncodesets):
130 self.oJson = oJson;
131 self.sName = sName;
132 self.sMnemonic = sMemonic;
133 self.aoEncodesets = aoEncodesets;
134 self.fFixedMask = 0;
135 self.fFixedValue = 0;
136 for oField in aoEncodesets:
137 self.fFixedMask |= oField.fFixed << oField.iFirstBit;
138 self.fFixedValue |= oField.fValue << oField.iFirstBit;
139
140 def __str__(self):
141 sRet = 'sName=%s; sMnemonic=%s fFixedValue/Mask=%#x/%#x encoding=\n %s' % (
142 self.sName, self.sMnemonic, self.fFixedValue, self.fFixedMask,
143 ',\n '.join([str(s) for s in self.aoEncodesets]),
144 );
145 return sRet;
146
147 def __repr__(self):
148 return self.__str__();
149
150## All the instructions.
151g_aoAllArmInstructions = [] # type: List[ArmInstruction]
152
153## All the instructions by name (not mnemonic.
154g_dAllArmInstructionsByName = {} # type: Dict[ArmInstruction]
155
156
157def parseInstructions(aoStack, aoJson):
158 for oJson in aoJson:
159 if oJson['_type'] == "Instruction.InstructionSet":
160 parseInstructions([oJson,] + aoStack, oJson['children']);
161 elif oJson['_type'] == "Instruction.InstructionGroup":
162 parseInstructions([oJson,] + aoStack, oJson['children']);
163 elif oJson['_type'] == "Instruction.Instruction":
164 #aoJsonEncodings = [oJson['encoding'],];
165 (aoEncodesets, fCovered) = ArmEncodesetField.fromJsonEncodeset(oJson['encoding'], [], 0);
166 for oParent in aoStack:
167 if 'encoding' in oParent:
168 (aoEncodesets, fCovered) = ArmEncodesetField.fromJsonEncodeset(oParent['encoding'], aoEncodesets, fCovered);
169 oInstr = ArmInstruction(oJson, oJson['name'], oJson['name'], aoEncodesets);
170
171 g_aoAllArmInstructions.append(oInstr);
172 assert oInstr.sName not in g_dAllArmInstructionsByName;
173 g_dAllArmInstructionsByName[oInstr.sName] = oInstr;
174
175
176def bsdSpecAnalysis(asArgs):
177 """ Main function. """
178
179 #
180 # Parse arguments.
181 #
182 oArgParser = argparse.ArgumentParser(add_help = False);
183 oArgParser.add_argument('--tar',
184 metavar = 'AARCHMRS_BSD_A_profile-2024-12.tar.gz',
185 dest = 'sTarFile',
186 action = 'store',
187 default = None,
188 help = 'Specification TAR file to get the files from.');
189 oArgParser.add_argument('--instructions',
190 metavar = 'Instructions.json',
191 dest = 'sFileInstructions',
192 action = 'store',
193 default = 'Instructions.json',
194 help = 'The path to the instruction specficiation file.');
195 oArgParser.add_argument('--features',
196 metavar = 'Features.json',
197 dest = 'sFileFeatures',
198 action = 'store',
199 default = 'Features.json',
200 help = 'The path to the features specficiation file.');
201 oArgParser.add_argument('--registers',
202 metavar = 'Registers.json',
203 dest = 'sFileRegisters',
204 action = 'store',
205 default = 'Registers.json',
206 help = 'The path to the registers specficiation file.');
207 oArgParser.add_argument('--spec-dir',
208 metavar = 'dir',
209 dest = 'sSpecDir',
210 action = 'store',
211 default = '',
212 help = 'Specification directory to prefix the specficiation files with.');
213 oOptions = oArgParser.parse_args(asArgs[1:]);
214
215 #
216 # Load the files.
217 #
218 print("loading specs ...");
219 if oOptions.sTarFile:
220 with tarfile.open(oOptions.sTarFile, 'r') as oTarFile:
221 with oTarFile.extractfile(oOptions.sFileInstructions) as oFile:
222 dRawInstructions = json.load(oFile);
223 #with open(sFileFeatures, 'r', encoding = 'utf-8') as oFile:
224 # dRawFeatures = json.load(oFile);
225 #with open(sFileRegisters, 'r', encoding = 'utf-8') as oFile:
226 # dRawRegisters = json.load(oFile);
227 else:
228 if oOptions.sSpecDir:
229 if not os.path.isabs(oOptions.sFileInstructions):
230 oOptions.sFileInstructions = os.path.normpath(os.path.join(oOptions.sSpecDir, oOptions.sFileInstructions));
231 if not os.path.isabs(oOptions.sFileFeatures):
232 oOptions.sFileFeatures = os.path.normpath(os.path.join(oOptions.sSpecDir, oOptions.sFileFeatures));
233 if not os.path.isabs(oOptions.sFileRegisters):
234 oOptions.sFileRegisters = os.path.normpath(os.path.join(oOptions.sSpecDir, oOptions.sFileRegisters));
235
236 with open(oOptions.sFileInstructions, 'r', encoding = 'utf-8') as oFile:
237 dRawInstructions = json.load(oFile);
238 #with open(oOptions.sFileFeatures, 'r', encoding = 'utf-8') as oFile:
239 # dRawFeatures = json.load(oFile);
240 #with open(oOptions.sFileRegisters, 'r', encoding = 'utf-8') as oFile:
241 # dRawRegisters = json.load(oFile);
242 print("... done loading.");
243
244 #
245 # Parse the Instructions.
246 #
247 print("parsing instructions ...");
248 parseInstructions([], dRawInstructions['instructions']);
249 print("Found %u instructions." % (len(g_aoAllArmInstructions),));
250
251 #oBrk = g_dAllArmInstructionsByName['BRK_EX_exception'];
252 #print("oBrk=%s" % (oBrk,))
253
254 if False:
255 for oInstr in g_aoAllArmInstructions:
256 print('%08x/%08x %s' % (oInstr.fFixedMask, oInstr.fFixedValue, oInstr.sName));
257
258 # Gather stats on fixed bits:
259 if True:
260 dCounts = collections.Counter();
261 for oInstr in g_aoAllArmInstructions:
262 cPopCount = bin(oInstr.fFixedMask).count('1');
263 dCounts[cPopCount] += 1;
264
265 print('');
266 print('Fixed bit pop count distribution:');
267 for i in range(33):
268 if i in dCounts:
269 print(' %2u: %u' % (i, dCounts[i]));
270
271 # Top 10 fixed masks.
272 if True:
273 dCounts = collections.Counter();
274 for oInstr in g_aoAllArmInstructions:
275 dCounts[oInstr.fFixedMask] += 1;
276
277 print('');
278 print('Top 20 fixed masks:');
279 for fFixedMask, cHits in dCounts.most_common(20):
280 print(' %#x: %u times' % (fFixedMask, cHits,));
281
282 return 0;
283
284
285if __name__ == '__main__':
286 sys.exit(bsdSpecAnalysis(sys.argv));
287
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