1 | #!/usr/bin/env python
|
---|
2 | # -*- coding: utf-8 -*-
|
---|
3 | # $Id: bs3-cpu-generated-1-data.py 94130 2022-03-08 14:57:49Z vboxsync $
|
---|
4 | # pylint: disable=invalid-name
|
---|
5 |
|
---|
6 | """
|
---|
7 | Generates testcases from @optest specifications in IEM.
|
---|
8 | """
|
---|
9 |
|
---|
10 | from __future__ import print_function;
|
---|
11 |
|
---|
12 | __copyright__ = \
|
---|
13 | """
|
---|
14 | Copyright (C) 2017-2022 Oracle Corporation
|
---|
15 |
|
---|
16 | This file is part of VirtualBox Open Source Edition (OSE), as
|
---|
17 | available from http://www.virtualbox.org. This file is free software;
|
---|
18 | you can redistribute it and/or modify it under the terms of the GNU
|
---|
19 | General Public License (GPL) as published by the Free Software
|
---|
20 | Foundation, in version 2 as it comes in the "COPYING" file of the
|
---|
21 | VirtualBox OSE distribution. VirtualBox OSE is distributed in the
|
---|
22 | hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
|
---|
23 |
|
---|
24 | The contents of this file may alternatively be used under the terms
|
---|
25 | of the Common Development and Distribution License Version 1.0
|
---|
26 | (CDDL) only, as it comes in the "COPYING.CDDL" file of the
|
---|
27 | VirtualBox OSE distribution, in which case the provisions of the
|
---|
28 | CDDL are applicable instead of those of the GPL.
|
---|
29 |
|
---|
30 | You may elect to license modified versions of this file under the
|
---|
31 | terms and conditions of either the GPL or the CDDL or both.
|
---|
32 | """
|
---|
33 | __version__ = "$Revision: 94130 $"
|
---|
34 |
|
---|
35 | # Standard python imports.
|
---|
36 | import os;
|
---|
37 | import sys;
|
---|
38 |
|
---|
39 | # Only the main script needs to modify the path.
|
---|
40 | g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)));
|
---|
41 | g_ksVmmAllDir = os.path.join(os.path.dirname(g_ksValidationKitDir), 'VMM', 'VMMAll')
|
---|
42 | sys.path.append(g_ksVmmAllDir);
|
---|
43 |
|
---|
44 | import IEMAllInstructionsPython as iai; # pylint: disable=import-error
|
---|
45 |
|
---|
46 |
|
---|
47 | # Python 3 hacks:
|
---|
48 | if sys.version_info[0] >= 3:
|
---|
49 | long = int; # pylint: disable=redefined-builtin,invalid-name
|
---|
50 |
|
---|
51 |
|
---|
52 | class Bs3Cg1TestEncoder(object):
|
---|
53 | """
|
---|
54 | Does the encoding of a single test.
|
---|
55 | """
|
---|
56 |
|
---|
57 | def __init__(self, fLast):
|
---|
58 | self.fLast = fLast;
|
---|
59 | # Each list member (in all lists) are C expression of a byte.
|
---|
60 | self.asHdr = [];
|
---|
61 | self.asSelectors = [];
|
---|
62 | self.asInputs = [];
|
---|
63 | self.asOutputs = [];
|
---|
64 |
|
---|
65 | @staticmethod
|
---|
66 | def _compileSelectors(aoSelectors): # (list(iai.TestSelector)) -> list(str)
|
---|
67 | """
|
---|
68 | Compiles a list of iai.TestSelector predicate checks.
|
---|
69 | Returns C byte expression strings.
|
---|
70 | """
|
---|
71 | asRet = [];
|
---|
72 | for oSelector in aoSelectors:
|
---|
73 | sConstant = oSelector.kdVariables[oSelector.sVariable][oSelector.sValue];
|
---|
74 | sConstant = sConstant.upper().replace('.', '_');
|
---|
75 | if oSelector.sOp == '==':
|
---|
76 | sByte = '(BS3CG1PRED_%s << BS3CG1SEL_OP_PRED_SHIFT) | BS3CG1SEL_OP_IS_TRUE' % (sConstant,);
|
---|
77 | elif oSelector.sOp == '!=':
|
---|
78 | sByte = '(BS3CG1PRED_%s << BS3CG1SEL_OP_PRED_SHIFT) | BS3CG1SEL_OP_IS_FALSE' % (sConstant,);
|
---|
79 | else:
|
---|
80 | raise Exception('Unknown selector operator: %s' % (oSelector.sOp,));
|
---|
81 | asRet.append(sByte);
|
---|
82 | return asRet;
|
---|
83 |
|
---|
84 | kdSmallFields = {
|
---|
85 | 'op1': 'BS3CG1_CTXOP_OP1',
|
---|
86 | 'op2': 'BS3CG1_CTXOP_OP2',
|
---|
87 | 'efl': 'BS3CG1_CTXOP_EFL',
|
---|
88 | };
|
---|
89 | kdOperators = {
|
---|
90 | '=': 'BS3CG1_CTXOP_ASSIGN',
|
---|
91 | '|=': 'BS3CG1_CTXOP_OR',
|
---|
92 | '&=': 'BS3CG1_CTXOP_AND',
|
---|
93 | '&~=': 'BS3CG1_CTXOP_AND_INV',
|
---|
94 | };
|
---|
95 | kdSmallSizes = {
|
---|
96 | 1: 'BS3CG1_CTXOP_1_BYTE',
|
---|
97 | 2: 'BS3CG1_CTXOP_2_BYTES',
|
---|
98 | 4: 'BS3CG1_CTXOP_4_BYTES',
|
---|
99 | 8: 'BS3CG1_CTXOP_8_BYTES',
|
---|
100 | 16: 'BS3CG1_CTXOP_16_BYTES',
|
---|
101 | 32: 'BS3CG1_CTXOP_32_BYTES',
|
---|
102 | 12: 'BS3CG1_CTXOP_12_BYTES',
|
---|
103 | };
|
---|
104 |
|
---|
105 | @staticmethod
|
---|
106 | def _amendOutputs(aoOutputs, oInstr): # type: (list(iai.TestInOut), iai.Instruction) -> list(iai.TestInOut)
|
---|
107 | """
|
---|
108 | Amends aoOutputs for instructions with special flag behaviour (undefined,
|
---|
109 | always set, always clear).
|
---|
110 |
|
---|
111 | Undefined flags are copied from the result context as the very first
|
---|
112 | operation so they can be set to CPU vendor specific values later if
|
---|
113 | desired.
|
---|
114 |
|
---|
115 | Always set or cleared flags are applied at the very end of the
|
---|
116 | modification operations so that we spot incorrect specifications.
|
---|
117 | """
|
---|
118 | if oInstr.asFlUndefined or oInstr.asFlClear or oInstr.asFlSet:
|
---|
119 | aoOutputs = list(aoOutputs);
|
---|
120 |
|
---|
121 | if oInstr.asFlUndefined:
|
---|
122 | fFlags = oInstr.getUndefinedFlagsMask();
|
---|
123 | assert fFlags != 0;
|
---|
124 | aoOutputs.insert(0, iai.TestInOut('efl_undef', '=', str(fFlags), 'uint'));
|
---|
125 |
|
---|
126 | if oInstr.asFlClear:
|
---|
127 | fFlags = oInstr.getClearedFlagsMask();
|
---|
128 | assert fFlags != 0;
|
---|
129 | aoOutputs.append(iai.TestInOut('efl', '&~=', str(fFlags), 'uint'));
|
---|
130 |
|
---|
131 | if oInstr.asFlSet:
|
---|
132 | fFlags = oInstr.getSetFlagsMask();
|
---|
133 | assert fFlags != 0;
|
---|
134 | aoOutputs.append(iai.TestInOut('efl', '|=', str(fFlags), 'uint'));
|
---|
135 |
|
---|
136 | return aoOutputs;
|
---|
137 |
|
---|
138 | @staticmethod
|
---|
139 | def _compileContextModifers(aoOperations): # (list(iai.TestInOut))
|
---|
140 | """
|
---|
141 | Compile a list of iai.TestInOut context modifiers.
|
---|
142 | """
|
---|
143 | asRet = [];
|
---|
144 | for oOperation in aoOperations:
|
---|
145 | oType = iai.TestInOut.kdTypes[oOperation.sType];
|
---|
146 | aaoValues = oType.get(oOperation.sValue);
|
---|
147 | assert len(aaoValues) == 1 or len(aaoValues) == 2;
|
---|
148 |
|
---|
149 | sOp = oOperation.sOp;
|
---|
150 | if sOp == '&|=':
|
---|
151 | sOp = '|=' if len(aaoValues) == 1 else '&~=';
|
---|
152 |
|
---|
153 | for fSignExtend, abValue in aaoValues:
|
---|
154 | cbValue = len(abValue);
|
---|
155 |
|
---|
156 | # The opcode byte.
|
---|
157 | sOpcode = Bs3Cg1TestEncoder.kdOperators[sOp];
|
---|
158 | sOpcode += ' | ';
|
---|
159 | if oOperation.sField in Bs3Cg1TestEncoder.kdSmallFields:
|
---|
160 | sOpcode += Bs3Cg1TestEncoder.kdSmallFields[oOperation.sField];
|
---|
161 | else:
|
---|
162 | sOpcode += 'BS3CG1_CTXOP_DST_ESC';
|
---|
163 | sOpcode += ' | ';
|
---|
164 | if cbValue in Bs3Cg1TestEncoder.kdSmallSizes:
|
---|
165 | sOpcode += Bs3Cg1TestEncoder.kdSmallSizes[cbValue];
|
---|
166 | else:
|
---|
167 | sOpcode += 'BS3CG1_CTXOP_SIZE_ESC';
|
---|
168 | if fSignExtend:
|
---|
169 | sOpcode += ' | BS3CG1_CTXOP_SIGN_EXT';
|
---|
170 | asRet.append(sOpcode);
|
---|
171 |
|
---|
172 | # Escaped field identifier.
|
---|
173 | if oOperation.sField not in Bs3Cg1TestEncoder.kdSmallFields:
|
---|
174 | asRet.append('BS3CG1DST_%s' % (oOperation.sField.upper().replace('.', '_'),));
|
---|
175 |
|
---|
176 | # Escaped size byte?
|
---|
177 | if cbValue not in Bs3Cg1TestEncoder.kdSmallSizes:
|
---|
178 | if cbValue >= 256 or cbValue not in [ 1, 2, 4, 6, 8, 12, 16, 32, 64, 128, ]:
|
---|
179 | raise Exception('Invalid value size: %s' % (cbValue,));
|
---|
180 | asRet.append('0x%02x' % (cbValue,));
|
---|
181 |
|
---|
182 | # The value bytes.
|
---|
183 | for b in abValue:
|
---|
184 | asRet.append('0x%02x' % (b,));
|
---|
185 |
|
---|
186 | sOp = '|=';
|
---|
187 |
|
---|
188 | return asRet;
|
---|
189 |
|
---|
190 | def _constructHeader(self):
|
---|
191 | """
|
---|
192 | Returns C byte expression strings for BS3CG1TESTHDR.
|
---|
193 | """
|
---|
194 | cbSelectors = len(self.asSelectors);
|
---|
195 | if cbSelectors >= 256:
|
---|
196 | raise Exception('Too many selectors: %s bytes, max 255 bytes' % (cbSelectors,))
|
---|
197 |
|
---|
198 | cbInputs = len(self.asInputs);
|
---|
199 | if cbInputs >= 4096:
|
---|
200 | raise Exception('Too many input context modifiers: %s bytes, max 4095 bytes' % (cbInputs,))
|
---|
201 |
|
---|
202 | cbOutputs = len(self.asOutputs);
|
---|
203 | if cbOutputs >= 2048:
|
---|
204 | raise Exception('Too many output context modifiers: %s bytes, max 2047 bytes' % (cbOutputs,))
|
---|
205 |
|
---|
206 | return [
|
---|
207 | '%#04x' % (cbSelectors,), # 8-bit
|
---|
208 | '%#05x & 0xff' % (cbInputs,), # first 8 bits of cbInputs
|
---|
209 | '(%#05x >> 8) | ((%#05x & 0xf) << 4)' % (cbInputs, cbOutputs,), # last 4 bits of cbInputs, lower 4 bits of cbOutputs.
|
---|
210 | '(%#05x >> 4) | (%#05x << 7)' % (cbOutputs, self.fLast), # last 7 bits of cbOutputs and 1 bit fLast.
|
---|
211 | ];
|
---|
212 |
|
---|
213 | def encodeTest(self, oTest): # type: (iai.InstructionTest)
|
---|
214 | """
|
---|
215 | Does the encoding.
|
---|
216 | """
|
---|
217 | self.asSelectors = self._compileSelectors(oTest.aoSelectors);
|
---|
218 | self.asInputs = self._compileContextModifers(oTest.aoInputs);
|
---|
219 | self.asOutputs = self._compileContextModifers(self._amendOutputs(oTest.aoOutputs, oTest.oInstr));
|
---|
220 | self.asHdr = self._constructHeader();
|
---|
221 |
|
---|
222 |
|
---|
223 | class Bs3Cg1EncodedTests(object):
|
---|
224 | """
|
---|
225 | Encodes the tests for an instruction.
|
---|
226 | """
|
---|
227 |
|
---|
228 | def __init__(self, oInstr):
|
---|
229 | self.offTests = -1;
|
---|
230 | self.cbTests = 0;
|
---|
231 | self.asLines = [] # type: list(str)
|
---|
232 | self.aoInstructions = [] # type: list(iai.Instruction)
|
---|
233 |
|
---|
234 | # Encode the tests.
|
---|
235 | for iTest, oTest in enumerate(oInstr.aoTests):
|
---|
236 | oEncodedTest = Bs3Cg1TestEncoder(iTest + 1 == len(oInstr.aoTests));
|
---|
237 | oEncodedTest.encodeTest(oTest);
|
---|
238 |
|
---|
239 | self.cbTests += len(oEncodedTest.asHdr) + len(oEncodedTest.asSelectors) \
|
---|
240 | + len(oEncodedTest.asInputs) + len(oEncodedTest.asOutputs);
|
---|
241 |
|
---|
242 | self.asLines.append(' /* test #%s: %s */' % (iTest, oTest,));
|
---|
243 | self.asLines += self.bytesToLines(' ', oEncodedTest.asHdr);
|
---|
244 | if oEncodedTest.asSelectors:
|
---|
245 | self.asLines += self.bytesToLines(' /*sel:*/ ', oEncodedTest.asSelectors);
|
---|
246 | if oEncodedTest.asInputs:
|
---|
247 | self.asLines += self.bytesToLines(' /* in:*/ ', oEncodedTest.asInputs);
|
---|
248 | if oEncodedTest.asOutputs:
|
---|
249 | self.asLines += self.bytesToLines(' /*out:*/ ', oEncodedTest.asOutputs);
|
---|
250 |
|
---|
251 | @staticmethod
|
---|
252 | def bytesToLines(sPrefix, asBytes):
|
---|
253 | """
|
---|
254 | Formats a series of bytes into one or more lines.
|
---|
255 | A byte ending with a newline indicates that we should start a new line,
|
---|
256 | and prefix it by len(sPrefix) spaces.
|
---|
257 |
|
---|
258 | Returns list of lines.
|
---|
259 | """
|
---|
260 | asRet = [];
|
---|
261 | sLine = sPrefix;
|
---|
262 | for sByte in asBytes:
|
---|
263 | if sByte[-1] == '\n':
|
---|
264 | sLine += sByte[:-1] + ',';
|
---|
265 | asRet.append(sLine);
|
---|
266 | sLine = ' ' * len(sPrefix);
|
---|
267 | else:
|
---|
268 | if len(sLine) + 2 + len(sByte) > 132 and len(sLine) > len(sPrefix):
|
---|
269 | asRet.append(sLine[:-1]);
|
---|
270 | sLine = ' ' * len(sPrefix);
|
---|
271 | sLine += sByte + ', ';
|
---|
272 |
|
---|
273 |
|
---|
274 | if len(sLine) > len(sPrefix):
|
---|
275 | asRet.append(sLine);
|
---|
276 | return asRet;
|
---|
277 |
|
---|
278 |
|
---|
279 | def isEqual(self, oOther):
|
---|
280 | """ Compares two encoded tests. """
|
---|
281 | if self.cbTests != oOther.cbTests:
|
---|
282 | return False;
|
---|
283 | if len(self.asLines) != len(oOther.asLines):
|
---|
284 | return False;
|
---|
285 | for iLine, sLines in enumerate(self.asLines):
|
---|
286 | if sLines != oOther.asLines[iLine]:
|
---|
287 | return False;
|
---|
288 | return True;
|
---|
289 |
|
---|
290 |
|
---|
291 |
|
---|
292 | class Bs3Cg1Instruction(object):
|
---|
293 | """
|
---|
294 | An instruction with tests.
|
---|
295 | """
|
---|
296 |
|
---|
297 | def __init__(self, oMap, oInstr, oTests):
|
---|
298 | self.oMap = oMap # type: iai.InstructionMap
|
---|
299 | self.oInstr = oInstr # type: iai.Instruction
|
---|
300 | self.oTests = oTests # type: Bs3Cg1EncodedTests
|
---|
301 |
|
---|
302 | self.asOpcodes = oMap.asLeadOpcodes + [ '0x%02x' % (oInstr.getOpcodeByte(),) ];
|
---|
303 | self.sEncoding = iai.g_kdEncodings[oInstr.sEncoding][0];
|
---|
304 |
|
---|
305 | for oOp in oInstr.aoOperands:
|
---|
306 | self.sEncoding += '_' + oOp.sType;
|
---|
307 | if oInstr.sSubOpcode and iai.g_kdSubOpcodes[oInstr.sSubOpcode][1]:
|
---|
308 | self.sEncoding += '_' + iai.g_kdSubOpcodes[oInstr.sSubOpcode][1];
|
---|
309 |
|
---|
310 | if oInstr.fUnused:
|
---|
311 | if oInstr.sInvalidStyle == 'immediate' and oInstr.sSubOpcode:
|
---|
312 | self.sEncoding += '_MOD_EQ_3' if oInstr.sSubOpcode == '11 mr/reg' else '_MOD_NE_3';
|
---|
313 | elif oInstr.sInvalidStyle == 'intel-modrm':
|
---|
314 | if oInstr.sSubOpcode is None:
|
---|
315 | self.sEncoding = 'BS3CG1ENC_MODRM_Gv_Ev';
|
---|
316 | elif oInstr.sSubOpcode == '11 mr/reg':
|
---|
317 | self.sEncoding = 'BS3CG1ENC_MODRM_MOD_EQ_3';
|
---|
318 | elif oInstr.sSubOpcode == '!11 mr/reg':
|
---|
319 | self.sEncoding = 'BS3CG1ENC_MODRM_MOD_NE_3';
|
---|
320 | else:
|
---|
321 | raise Exception('Unhandled sSubOpcode=%s for sInvalidStyle=%s' % (oInstr.sSubOpcode, oInstr.sInvalidStyle));
|
---|
322 | elif oInstr.sInvalidStyle == 'vex.modrm':
|
---|
323 | self.sEncoding = 'BS3CG1ENC_VEX_MODRM';
|
---|
324 |
|
---|
325 | self.asFlags = [];
|
---|
326 | if 'invalid_64' in oInstr.dHints:
|
---|
327 | self.asFlags.append('BS3CG1INSTR_F_INVALID_64BIT');
|
---|
328 | if oInstr.fUnused:
|
---|
329 | self.asFlags.append('BS3CG1INSTR_F_UNUSED');
|
---|
330 | elif oInstr.fInvalid:
|
---|
331 | self.asFlags.append('BS3CG1INSTR_F_INVALID');
|
---|
332 | if oInstr.sInvalidStyle and oInstr.sInvalidStyle.startswith('intel-'):
|
---|
333 | self.asFlags.append('BS3CG1INSTR_F_INTEL_DECODES_INVALID');
|
---|
334 | if 'vex_l_zero' in oInstr.dHints:
|
---|
335 | self.asFlags.append('BS3CG1INSTR_F_VEX_L_ZERO');
|
---|
336 | if 'vex_l_ignored' in oInstr.dHints:
|
---|
337 | self.asFlags.append('BS3CG1INSTR_F_VEX_L_IGNORED');
|
---|
338 |
|
---|
339 | self.fAdvanceMnemonic = True; ##< Set by the caller.
|
---|
340 | if oInstr.sPrefix:
|
---|
341 | if oInstr.sPrefix == 'none':
|
---|
342 | self.sPfxKind = 'BS3CG1PFXKIND_NO_F2_F3_66';
|
---|
343 | else:
|
---|
344 | self.sPfxKind = 'BS3CG1PFXKIND_REQ_' + oInstr.sPrefix[-2:].upper();
|
---|
345 | elif oInstr.sEncoding == 'ModR/M':
|
---|
346 | if 'ignores_op_size' not in oInstr.dHints:
|
---|
347 | self.sPfxKind = 'BS3CG1PFXKIND_MODRM';
|
---|
348 | else:
|
---|
349 | self.sPfxKind = 'BS3CG1PFXKIND_MODRM_NO_OP_SIZES';
|
---|
350 | else:
|
---|
351 | self.sPfxKind = '0';
|
---|
352 |
|
---|
353 | self.sCpu = 'BS3CG1CPU_';
|
---|
354 | assert len(oInstr.asCpuIds) in [0, 1], str(oInstr);
|
---|
355 | if oInstr.asCpuIds:
|
---|
356 | self.sCpu += oInstr.asCpuIds[0].upper().replace('.', '_');
|
---|
357 | elif oInstr.sMinCpu:
|
---|
358 | self.sCpu += 'GE_' + oInstr.sMinCpu;
|
---|
359 | else:
|
---|
360 | self.sCpu += 'ANY';
|
---|
361 |
|
---|
362 | if oInstr.sXcptType:
|
---|
363 | self.sXcptType = 'BS3CG1XCPTTYPE_' + oInstr.sXcptType.upper();
|
---|
364 | else:
|
---|
365 | self.sXcptType = 'BS3CG1XCPTTYPE_NONE';
|
---|
366 |
|
---|
367 | def getOperands(self):
|
---|
368 | """ Returns comma separated string of operand values for g_abBs3Cg1Operands. """
|
---|
369 | return ', '.join(['(uint8_t)BS3CG1OP_%s' % (oOp.sType,) for oOp in self.oInstr.aoOperands]);
|
---|
370 |
|
---|
371 | def getOpcodeMap(self):
|
---|
372 | """ Returns the opcode map number for the BS3CG1INSTR structure. """
|
---|
373 | sEncoding = self.oInstr.aoMaps[0].sEncoding;
|
---|
374 | if sEncoding == 'legacy': return 0;
|
---|
375 | if sEncoding == 'vex1': return 1;
|
---|
376 | if sEncoding == 'vex2': return 2;
|
---|
377 | if sEncoding == 'vex3': return 3;
|
---|
378 | if sEncoding == 'xop8': return 8;
|
---|
379 | if sEncoding == 'xop9': return 9;
|
---|
380 | if sEncoding == 'xop10': return 10;
|
---|
381 | assert False, sEncoding;
|
---|
382 | return 3;
|
---|
383 |
|
---|
384 | def getInstructionEntry(self):
|
---|
385 | """ Returns an array of BS3CG1INSTR member initializers. """
|
---|
386 | assert len(self.oInstr.sMnemonic) < 16;
|
---|
387 | sOperands = ', '.join([oOp.sType for oOp in self.oInstr.aoOperands]);
|
---|
388 | if sOperands:
|
---|
389 | sOperands = ' /* ' + sOperands + ' */';
|
---|
390 | return [
|
---|
391 | ' /* cbOpcodes = */ %s, /* %s */' % (len(self.asOpcodes), ' '.join(self.asOpcodes),),
|
---|
392 | ' /* cOperands = */ %s,%s' % (len(self.oInstr.aoOperands), sOperands,),
|
---|
393 | ' /* cchMnemonic = */ %s, /* %s */' % (len(self.oInstr.sMnemonic), self.oInstr.sMnemonic,),
|
---|
394 | ' /* fAdvanceMnemonic = */ %s,' % ('true' if self.fAdvanceMnemonic else 'false',),
|
---|
395 | ' /* offTests = */ %s,' % (self.oTests.offTests,),
|
---|
396 | ' /* enmEncoding = */ (unsigned)%s,' % (self.sEncoding,),
|
---|
397 | ' /* uOpcodeMap = */ (unsigned)%s,' % (self.getOpcodeMap(),),
|
---|
398 | ' /* enmPrefixKind = */ (unsigned)%s,' % (self.sPfxKind,),
|
---|
399 | ' /* enmCpuTest = */ (unsigned)%s,' % (self.sCpu,),
|
---|
400 | ' /* enmXcptType = */ (unsigned)%s,' % (self.sXcptType,),
|
---|
401 | ' /* uUnused = */ 0,',
|
---|
402 | ' /* fFlags = */ %s' % (' | '.join(self.asFlags) if self.asFlags else '0'),
|
---|
403 | ];
|
---|
404 |
|
---|
405 |
|
---|
406 | class Bs3CpuGenerated1Generator(object):
|
---|
407 | """
|
---|
408 | The generator code for bs3-cpu-generated-1.
|
---|
409 | """
|
---|
410 |
|
---|
411 | def __init__(self):
|
---|
412 | self.aoInstructions = [] # type: Bs3Cg1Instruction
|
---|
413 | self.aoTests = [] # type: Bs3Cg1EncodedTests
|
---|
414 | self.cbTests = 0;
|
---|
415 |
|
---|
416 | def addTests(self, oTests, oInstr): # type: (Bs3Cg1EncodedTests, iai.Instruction) -> Bs3Cg1EncodedTests
|
---|
417 | """
|
---|
418 | Adds oTests to self.aoTests, setting the oTests.offTests member.
|
---|
419 | Checks for and eliminates duplicates.
|
---|
420 | Returns the tests to use.
|
---|
421 | """
|
---|
422 | # Check for duplicates.
|
---|
423 | for oExisting in self.aoTests:
|
---|
424 | if oTests.isEqual(oExisting):
|
---|
425 | oExisting.aoInstructions.append(oInstr);
|
---|
426 | return oExisting;
|
---|
427 |
|
---|
428 | # New test, so add it.
|
---|
429 | oTests.offTests = self.cbTests;
|
---|
430 | self.aoTests.append(oTests);
|
---|
431 | self.cbTests += oTests.cbTests;
|
---|
432 |
|
---|
433 | assert not oTests.aoInstructions;
|
---|
434 | oTests.aoInstructions.append(oInstr);
|
---|
435 |
|
---|
436 | return oTests;
|
---|
437 |
|
---|
438 | def processInstruction(self):
|
---|
439 | """
|
---|
440 | Processes the IEM specified instructions.
|
---|
441 | Returns success indicator.
|
---|
442 | """
|
---|
443 |
|
---|
444 | #
|
---|
445 | # Group instructions by mnemonic to reduce the number of sub-tests.
|
---|
446 | #
|
---|
447 | for oInstr in sorted(iai.g_aoAllInstructions,
|
---|
448 | key = lambda oInstr: oInstr.sMnemonic + ''.join([oOp.sType for oOp in oInstr.aoOperands])
|
---|
449 | + (oInstr.sOpcode if oInstr.sOpcode else 'zz')):
|
---|
450 | if oInstr.aoTests:
|
---|
451 | oTests = Bs3Cg1EncodedTests(oInstr);
|
---|
452 | oTests = self.addTests(oTests, oInstr);
|
---|
453 |
|
---|
454 | for oMap in oInstr.aoMaps:
|
---|
455 | self.aoInstructions.append(Bs3Cg1Instruction(oMap, oInstr, oTests));
|
---|
456 |
|
---|
457 | # Set fAdvanceMnemonic.
|
---|
458 | for iInstr, oInstr in enumerate(self.aoInstructions):
|
---|
459 | oInstr.fAdvanceMnemonic = iInstr + 1 >= len(self.aoInstructions) \
|
---|
460 | or oInstr.oInstr.sMnemonic != self.aoInstructions[iInstr + 1].oInstr.sMnemonic;
|
---|
461 |
|
---|
462 | return True;
|
---|
463 |
|
---|
464 | def generateCode(self, oOut):
|
---|
465 | """
|
---|
466 | Generates the C code.
|
---|
467 | Returns success indicator.
|
---|
468 | """
|
---|
469 |
|
---|
470 | # First, a file header.
|
---|
471 | asLines = [
|
---|
472 | '/*',
|
---|
473 | ' * Autogenerated by $Id: bs3-cpu-generated-1-data.py 94130 2022-03-08 14:57:49Z vboxsync $ ',
|
---|
474 | ' * Do not edit!',
|
---|
475 | ' */',
|
---|
476 | '',
|
---|
477 | '/*',
|
---|
478 | ' * Copyright (C) 2017-2020 Oracle Corporation',
|
---|
479 | ' *',
|
---|
480 | ' * This file is part of VirtualBox Open Source Edition (OSE), as',
|
---|
481 | ' * available from http://www.virtualbox.org. This file is free software;',
|
---|
482 | ' * you can redistribute it and/or modify it under the terms of the GNU',
|
---|
483 | ' * General Public License (GPL) as published by the Free Software',
|
---|
484 | ' * Foundation, in version 2 as it comes in the "COPYING" file of the',
|
---|
485 | ' * VirtualBox OSE distribution. VirtualBox OSE is distributed in the',
|
---|
486 | ' * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.',
|
---|
487 | ' * ',
|
---|
488 | ' * The contents of this file may alternatively be used under the terms',
|
---|
489 | ' * of the Common Development and Distribution License Version 1.0',
|
---|
490 | ' * (CDDL) only, as it comes in the "COPYING.CDDL" file of the',
|
---|
491 | ' * VirtualBox OSE distribution, in which case the provisions of the',
|
---|
492 | ' * CDDL are applicable instead of those of the GPL.',
|
---|
493 | ' * ',
|
---|
494 | ' * You may elect to license modified versions of this file under the',
|
---|
495 | ' * terms and conditions of either the GPL or the CDDL or both.',
|
---|
496 | ' */',
|
---|
497 | '',
|
---|
498 | '',
|
---|
499 | '#include "bs3-cpu-generated-1.h"',
|
---|
500 | '',
|
---|
501 | '',
|
---|
502 | '#pragma data_seg ("BS3DATA16")',
|
---|
503 | ];
|
---|
504 |
|
---|
505 | # Generate the g_achBs3Cg1Mnemonics array.
|
---|
506 | asLines += [
|
---|
507 | 'const char BS3_FAR_DATA g_achBs3Cg1Mnemonics[] = ',
|
---|
508 | '{',
|
---|
509 | ];
|
---|
510 | fAdvanceMnemonic = True;
|
---|
511 | for oInstr in self.aoInstructions:
|
---|
512 | if fAdvanceMnemonic:
|
---|
513 | asLines.append(' \"%s\"' % (oInstr.oInstr.sMnemonic,));
|
---|
514 | fAdvanceMnemonic = oInstr.fAdvanceMnemonic;
|
---|
515 | asLines += [
|
---|
516 | '};',
|
---|
517 | '',
|
---|
518 | '',
|
---|
519 | ];
|
---|
520 |
|
---|
521 | # Generate the g_abBs3Cg1Opcodes array.
|
---|
522 | asLines += [
|
---|
523 | 'const uint8_t BS3_FAR_DATA g_abBs3Cg1Opcodes[] = ',
|
---|
524 | '{',
|
---|
525 | ];
|
---|
526 | for oInstr in self.aoInstructions:
|
---|
527 | asLines.append(' ' + ', '.join(oInstr.asOpcodes) + ',');
|
---|
528 | asLines += [
|
---|
529 | '};',
|
---|
530 | '',
|
---|
531 | '',
|
---|
532 | ];
|
---|
533 |
|
---|
534 | # Generate the g_abBs3Cg1Opcodes array.
|
---|
535 | asLines += [
|
---|
536 | 'const uint8_t BS3_FAR_DATA g_abBs3Cg1Operands[] = ',
|
---|
537 | '{',
|
---|
538 | ];
|
---|
539 | cOperands = 0;
|
---|
540 | for oInstr in self.aoInstructions:
|
---|
541 | if oInstr.oInstr.aoOperands:
|
---|
542 | cOperands += len(oInstr.oInstr.aoOperands);
|
---|
543 | asLines.append(' ' + oInstr.getOperands() + ', /* %s */' % (oInstr.oInstr.sStats,));
|
---|
544 | else:
|
---|
545 | asLines.append(' /* none */');
|
---|
546 | if not cOperands:
|
---|
547 | asLines.append(' 0 /* dummy */');
|
---|
548 | asLines += [
|
---|
549 | '};',
|
---|
550 | '',
|
---|
551 | '',
|
---|
552 | ];
|
---|
553 |
|
---|
554 | # Generate the g_abBs3Cg1Operands array.
|
---|
555 | asLines += [
|
---|
556 | 'const BS3CG1INSTR BS3_FAR_DATA g_aBs3Cg1Instructions[] = ',
|
---|
557 | '{',
|
---|
558 | ];
|
---|
559 | for oInstr in self.aoInstructions:
|
---|
560 | asLines.append(' {');
|
---|
561 | asLines += oInstr.getInstructionEntry();
|
---|
562 | asLines.append(' },');
|
---|
563 | asLines += [
|
---|
564 | '};',
|
---|
565 | 'const uint16_t BS3_FAR_DATA g_cBs3Cg1Instructions = RT_ELEMENTS(g_aBs3Cg1Instructions);',
|
---|
566 | '',
|
---|
567 | '',
|
---|
568 | ];
|
---|
569 |
|
---|
570 | # Generate the g_abBs3Cg1Tests array.
|
---|
571 | asLines += [
|
---|
572 | 'const uint8_t BS3_FAR_DATA g_abBs3Cg1Tests[] = ',
|
---|
573 | '{',
|
---|
574 | ];
|
---|
575 | for oTests in self.aoTests:
|
---|
576 | asLines.append(' /*');
|
---|
577 | asLines.append(' * offTests=%s' % (oTests.offTests,));
|
---|
578 | asLines.append(' * Instructions: %s' % (', '.join([oInstr.sStats for oInstr in oTests.aoInstructions]),));
|
---|
579 | asLines.append(' */');
|
---|
580 | asLines += oTests.asLines;
|
---|
581 | asLines += [
|
---|
582 | '};',
|
---|
583 | '',
|
---|
584 | ];
|
---|
585 |
|
---|
586 |
|
---|
587 | #/** The test data that BS3CG1INSTR.
|
---|
588 | # * In order to simplify generating these, we use a byte array. */
|
---|
589 | #extern const uint8_t BS3_FAR_DATA g_abBs3Cg1Tests[];
|
---|
590 |
|
---|
591 |
|
---|
592 | oOut.write('\n'.join(asLines));
|
---|
593 | return True;
|
---|
594 |
|
---|
595 |
|
---|
596 | def usage(self):
|
---|
597 | """ Prints usage. """
|
---|
598 | print('usage: bs3-cpu-generated-1-data.py [output file|-]');
|
---|
599 | return 0;
|
---|
600 |
|
---|
601 | def main(self, asArgs):
|
---|
602 | """
|
---|
603 | C-like main function.
|
---|
604 | Returns exit code.
|
---|
605 | """
|
---|
606 |
|
---|
607 | #
|
---|
608 | # Quick argument parsing.
|
---|
609 | #
|
---|
610 | if len(asArgs) == 1:
|
---|
611 | sOutFile = '-';
|
---|
612 | elif len(asArgs) != 2:
|
---|
613 | print('syntax error! Expected exactly one argument.');
|
---|
614 | return 2;
|
---|
615 | elif asArgs[1] in [ '-h', '-?', '--help' ]:
|
---|
616 | return self.usage();
|
---|
617 | else:
|
---|
618 | sOutFile = asArgs[1];
|
---|
619 |
|
---|
620 | #
|
---|
621 | # Process the instructions specified in the IEM sources.
|
---|
622 | #
|
---|
623 | if self.processInstruction():
|
---|
624 |
|
---|
625 | #
|
---|
626 | # Open the output file and generate the code.
|
---|
627 | #
|
---|
628 | if sOutFile == '-':
|
---|
629 | oOut = sys.stdout;
|
---|
630 | else:
|
---|
631 | try:
|
---|
632 | oOut = open(sOutFile, 'w'); # pylint: disable=consider-using-with
|
---|
633 | except Exception as oXcpt:
|
---|
634 | print('error! Failed open "%s" for writing: %s' % (sOutFile, oXcpt,));
|
---|
635 | return 1;
|
---|
636 | if self.generateCode(oOut):
|
---|
637 | return 0;
|
---|
638 |
|
---|
639 | return 1;
|
---|
640 |
|
---|
641 |
|
---|
642 | if __name__ == '__main__':
|
---|
643 | sys.exit(Bs3CpuGenerated1Generator().main(sys.argv));
|
---|
644 |
|
---|