VirtualBox

source: vbox/trunk/src/VBox/ValidationKit/bootsectors/bs3-cpu-generated-1-data.py@ 65959

Last change on this file since 65959 was 65959, checked in by vboxsync, 8 years ago

bs3-cpu-generated-1: updates

  • Property svn:eol-style set to LF
  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
File size: 18.3 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3# $Id: bs3-cpu-generated-1-data.py 65959 2017-03-06 21:24:32Z vboxsync $
4
5"""
6Generates testcases from @optest specifications in IEM.
7"""
8
9from __future__ import print_function;
10
11__copyright__ = \
12"""
13Copyright (C) 2017 Oracle Corporation
14
15This file is part of VirtualBox Open Source Edition (OSE), as
16available from http://www.virtualbox.org. This file is free software;
17you can redistribute it and/or modify it under the terms of the GNU
18General Public License (GPL) as published by the Free Software
19Foundation, in version 2 as it comes in the "COPYING" file of the
20VirtualBox OSE distribution. VirtualBox OSE is distributed in the
21hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
22
23The contents of this file may alternatively be used under the terms
24of the Common Development and Distribution License Version 1.0
25(CDDL) only, as it comes in the "COPYING.CDDL" file of the
26VirtualBox OSE distribution, in which case the provisions of the
27CDDL are applicable instead of those of the GPL.
28
29You may elect to license modified versions of this file under the
30terms and conditions of either the GPL or the CDDL or both.
31"""
32__version__ = "$Revision: 65959 $"
33
34# Standard python imports.
35import os;
36import re;
37import sys;
38import time;
39
40# Only the main script needs to modify the path.
41g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)));
42g_ksVmmAllDir = os.path.join(os.path.dirname(g_ksValidationKitDir), 'VMM', 'VMMAll')
43sys.path.append(g_ksVmmAllDir);
44
45import IEMAllInstructionsPython as iai;
46
47# Python 3 hacks:
48if sys.version_info[0] >= 3:
49 long = int; # pylint: disable=redefined-builtin,invalid-name
50
51
52class 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.sValue.sOp == '==':
76 sByte = '(BS3CG1PRED_%s << BS3CG1SEL_OP_PRED_SHIFT) | BS3CG1SEL_OP_IS_TRUE' % (sConstant,);
77 elif oSelector.sValue.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 _compileContextModifers(aoOperations): # (list(iai.TestInOut))
107 """
108 Compile a list of iai.TestInOut context modifiers.
109 """
110 asRet = [];
111 for oOperation in aoOperations:
112 oType = iai.TestInOut.kdTypes[oOperation.sType];
113 aaoValues = oType.get(oOperation.sValue);
114 assert len(aaoValues) == 1 or len(aaoValues) == 2;
115
116 sOp = oOperation.sOp;
117 if sOp == '&|=':
118 sOp = '|=' if len(aaoValues) == 1 else '&~=';
119
120 for fSignExtend, abValue in aaoValues:
121 cbValue = len(abValue);
122
123 # The opcode byte.
124 sOpcode = Bs3Cg1TestEncoder.kdOperators[sOp];
125 sOpcode += ' | ';
126 if oOperation.sField in Bs3Cg1TestEncoder.kdSmallFields:
127 sOpcode += Bs3Cg1TestEncoder.kdSmallFields[oOperation.sField];
128 else:
129 sOpcode += 'BS3CG1_CTXOP_DST_ESC';
130 sOpcode += ' | ';
131 if cbValue in Bs3Cg1TestEncoder.kdSmallSizes:
132 sOpcode += Bs3Cg1TestEncoder.kdSmallSizes[cbValue];
133 else:
134 sOpcode += 'BS3CG1_CTXOP_SIZE_ESC';
135 if fSignExtend:
136 sOpcode += '| BS3CG1_CTXOP_SIGN_EXT';
137 asRet.append(sOpcode);
138
139 # Escaped size byte?
140 if cbValue not in Bs3Cg1TestEncoder.kdSmallSizes:
141 if cbValue >= 256 or cbValue not in [ 1, 2, 4, 6, 8, 12, 16, 32, 64, 128, ]:
142 raise Exception('Invalid value size: %s' % (cbValue,));
143 asRet.append('0x%02x' % (cbValue,));
144
145 # Escaped field identifier.
146 if oOperation.sField not in Bs3Cg1TestEncoder.kdSmallFields:
147 asRet.append('BS3CG1DST_%s' % (oOperation.sField.upper().replace('.', '_'),));
148
149 # The value bytes.
150 for b in abValue:
151 asRet.append('0x%02x' % (b,));
152
153 sOp = '|=';
154
155 return asRet;
156
157 def _constructHeader(self):
158 """
159 Returns C byte expression strings for BS3CG1TESTHDR.
160 """
161 cbSelectors = len(self.asSelectors);
162 if cbSelectors >= 256:
163 raise Exception('Too many selectors: %s bytes, max 255 bytes' % (cbSelectors,))
164
165 cbInputs = len(self.asInputs);
166 if cbInputs >= 4096:
167 raise Exception('Too many input context modifiers: %s bytes, max 4095 bytes' % (cbInputs,))
168
169 cbOutputs = len(self.asOutputs);
170 if cbOutputs >= 2048:
171 raise Exception('Too many output context modifiers: %s bytes, max 2047 bytes' % (cbOutputs,))
172
173 return [
174 '%#04x' % (cbSelectors,), # 8-bit
175 '%#05x & 0xff' % (cbInputs,), # first 8 bits of cbInputs
176 '(%#05x >> 8) | ((%#05x & 0xf) << 4)' % (cbInputs, cbOutputs,), # last 4 bits of cbInputs, lower 4 bits of cbOutputs.
177 '(%#05x >> 4) | (%#05x << 7)' % (cbOutputs, self.fLast), # last 7 bits of cbOutputs and 1 bit fLast.
178 ];
179
180 def encodeTest(self, oTest): # type: (iai.InstructionTest)
181 """
182 Does the encoding.
183 """
184 self.asSelectors = self._compileSelectors(oTest.aoSelectors);
185 self.asInputs = self._compileContextModifers(oTest.aoInputs);
186 self.asOutputs = self._compileContextModifers(oTest.aoOutputs);
187 self.asHdr = self._constructHeader();
188
189
190class Bs3Cg1EncodedTests(object):
191 """
192 Encodes the tests for an instruction.
193 """
194
195 def __init__(self, oInstr):
196 self.offTests = -1;
197 self.cbTests = 0;
198 self.asLines = [];
199
200 # Encode the tests.
201 for iTest, oTest in enumerate(oInstr.aoTests):
202 oEncodedTest = Bs3Cg1TestEncoder(iTest + 1 == len(oInstr.aoTests));
203 oEncodedTest.encodeTest(oTest);
204
205 self.cbTests += len(oEncodedTest.asHdr) + len(oEncodedTest.asSelectors) \
206 + len(oEncodedTest.asInputs) + len(oEncodedTest.asOutputs);
207
208 self.asLines += self.bytesToLines(' /*hdr:*/ ', oEncodedTest.asHdr);
209 if oEncodedTest.asSelectors:
210 self.asLines += self.bytesToLines(' /*sel:*/ ', oEncodedTest.asSelectors);
211 if oEncodedTest.asInputs:
212 self.asLines += self.bytesToLines(' /* in:*/ ', oEncodedTest.asInputs);
213 if oEncodedTest.asOutputs:
214 self.asLines += self.bytesToLines(' /*out:*/ ', oEncodedTest.asOutputs);
215
216 @staticmethod
217 def bytesToLines(sPrefix, asBytes):
218 """
219 Formats a series of bytes into one or more lines.
220 A byte ending with a newline indicates that we should start a new line,
221 and prefix it by len(sPrefix) spaces.
222
223 Returns list of lines.
224 """
225 asRet = [];
226 sLine = sPrefix;
227 for sByte in asBytes:
228 if sByte[-1] == '\n':
229 sLine += sByte[:-1] + ',';
230 asRet.append(sLine);
231 sLine = ' ' * len(sPrefix);
232 else:
233 if len(sLine) + 2 + len(sByte) > 132 and len(sLine) > len(sPrefix):
234 asRet.append(sLine[:-1]);
235 sLine = ' ' * len(sPrefix);
236 sLine += sByte + ', ';
237
238
239 if len(sLine) > len(sPrefix):
240 asRet.append(sLine);
241 return asRet;
242
243
244 def isEqual(self, oOther):
245 """ Compares two encoded tests. """
246 if self.cbTests != oOther.cbTests:
247 return False;
248 if len(self.asLines) != len(oOther.asLines):
249 return False;
250 for iLine, sLines in enumerate(self.asLines):
251 if sLines != oOther.asLines[iLine]:
252 return False;
253 return True;
254
255
256
257class Bs3Cg1Instruction(object):
258 """
259 An instruction with tests.
260 """
261
262 def __init__(self, oMap, oInstr, oTests):
263 self.oMap = oMap; # type: iai.InstructionMap
264 self.oInstr = oInstr; # type: iai.Instruction
265 self.oTests = oTests; # type: Bs3Cg1EncodedTests
266
267 self.asOpcodes = oMap.asLeadOpcodes + [ '0x%02x' % (oInstr.getOpcodeByte(),) ];
268 self.sEncoding = iai.g_kdEncodings[oInstr.sEncoding][0];
269 for oOp in oInstr.aoOperands:
270 self.sEncoding += '_' + oOp.sType;
271 self.asFlags = [];
272 self.fAdvanceMnemonic = True; ##< Set by the caller.
273 if self.sEncoding == 'ModR/M':
274 if 'ignores_op_size' not in oInstr.dHints:
275 self.sPfxKind = 'BS3CGPFXKIND_MODRM';
276 else:
277 self.sPfxKind = 'BS3CGPFXKIND_MODRM_NO_OP_SIZES';
278 else:
279 self.sPfxKind = '0';
280
281
282 def getOperands(self):
283 """ Returns comma separated string of operand values for g_abBs3Cg1Operands. """
284 return ', '.join(['(uint8_t)BS3CG1OP_%s' % (oOp.sType,) for oOp in self.oInstr.aoOperands]);
285
286 def getInstructionEntry(self):
287 """ Returns an array of BS3CG1INSTR member initializers. """
288 return [
289 ' /* cbOpcodes = */ %s,' % (len(self.asOpcodes),),
290 ' /* cOperands = */ %s,' % (len(self.oInstr.aoOperands),),
291 ' /* cchMnemonic = */ %s,' % (len(self.oInstr.sMnemonic),),
292 ' /* fAdvanceMnemonic = */ %s,' % ('true' if self.fAdvanceMnemonic else 'false',),
293 ' /* offTests = */ %s,' % (self.oTests.offTests,),
294 ' /* enmEncoding = */ (unsigned)%s,' % (self.sEncoding,),
295 ' /* enmPfxKind = */ (unsigned)%s,' % (self.sPfxKind,),
296 ' /* uUnused = */ 0,',
297 ' /* fFlags = */ %s' % (' | '.join(self.asFlags) if self.asFlags else '0'),
298 ];
299
300
301class Bs3CpuGenerated1Generator(object):
302 """
303 The generator code for bs3-cpu-generated-1.
304 """
305
306 def __init__(self):
307 self.aoInstructions = []; # type: Bs3Cg1Instruction
308 self.aoTests = []; # type: Bs3Cg1EncodedTests
309 self.cbTests = 0;
310
311 def addTests(self, oTests):
312 """
313 Adds oTests to self.aoTests, setting the oTests.offTests member.
314 Checks for and eliminates duplicates.
315 Returns the tests to use.
316 """
317 # Check for duplicates.
318 for oExisting in self.aoTests:
319 if oTests.isEqual(oExisting):
320 return oExisting;
321
322 # New test, so add it.
323 oTests.offTests = self.cbTests;
324 self.aoTests.append(oTests);
325 self.cbTests += oTests.cbTests;
326
327 return oTests;
328
329 def processInstruction(self):
330 """
331 Processes the IEM specified instructions.
332 Returns success indicator.
333 """
334
335 #
336 # Group instructions by mnemonic to reduce the number of sub-tests.
337 #
338 for oInstr in sorted(iai.g_aoAllInstructions,
339 key = lambda oInstr: oInstr.sMnemonic + ''.join([oOp.sType for oOp in oInstr.aoOperands])
340 + (oInstr.sOpcode if oInstr.sOpcode else 'zz')):
341 if len(oInstr.aoTests) > 0:
342 oTests = Bs3Cg1EncodedTests(oInstr);
343 oTests = self.addTests(oTests);
344
345 for oMap in oInstr.aoMaps:
346 self.aoInstructions.append(Bs3Cg1Instruction(oMap, oInstr, oTests));
347
348 # Set fAdvanceMnemonic.
349 for iInstr, oInstr in enumerate(self.aoInstructions):
350 oInstr.fAdvanceMnemonic = iInstr + 1 >= len(self.aoInstructions) \
351 or oInstr.oInstr.sMnemonic != self.aoInstructions[iInstr + 1].oInstr.sMnemonic;
352
353 return True;
354
355 def generateCode(self, oOut):
356 """
357 Generates the C code.
358 Returns success indicator.
359 """
360
361 # First, a file header.
362 asLines = [
363 '/*',
364 ' * Autogenerated by $Id: bs3-cpu-generated-1-data.py 65959 2017-03-06 21:24:32Z vboxsync $ ',
365 ' * Do not edit!',
366 ' */',
367 '',
368 '/*',
369 ' * Copyright (C) 2017 Oracle Corporation',
370 ' *',
371 ' * This file is part of VirtualBox Open Source Edition (OSE), as',
372 ' * available from http://www.virtualbox.org. This file is free software;',
373 ' * you can redistribute it and/or modify it under the terms of the GNU',
374 ' * General Public License (GPL) as published by the Free Software',
375 ' * Foundation, in version 2 as it comes in the "COPYING" file of the',
376 ' * VirtualBox OSE distribution. VirtualBox OSE is distributed in the',
377 ' * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.',
378 ' * ',
379 ' * The contents of this file may alternatively be used under the terms',
380 ' * of the Common Development and Distribution License Version 1.0',
381 ' * (CDDL) only, as it comes in the "COPYING.CDDL" file of the',
382 ' * VirtualBox OSE distribution, in which case the provisions of the',
383 ' * CDDL are applicable instead of those of the GPL.',
384 ' * ',
385 ' * You may elect to license modified versions of this file under the',
386 ' * terms and conditions of either the GPL or the CDDL or both.',
387 ' */',
388 '',
389 '',
390 '#include "bs3-cpu-generated-1.h"',
391 '',
392 '',
393 ];
394
395 # Generate the g_achBs3Cg1Mnemonics array.
396 asLines += [
397 'const char BS3_FAR_DATA g_achBs3Cg1Mnemonics[] = ',
398 '{',
399 ];
400 for oInstr in self.aoInstructions:
401 asLines.append(' \"%s\"' % (oInstr.oInstr.sMnemonic,));
402 asLines += [
403 '};',
404 '',
405 '',
406 ];
407
408 # Generate the g_abBs3Cg1Opcodes array.
409 asLines += [
410 'const uint8_t BS3_FAR_DATA g_abBs3Cg1Opcodes[] = ',
411 '{',
412 ];
413 for oInstr in self.aoInstructions:
414 asLines.append(' ' + ', '.join(oInstr.asOpcodes) + ',');
415 asLines += [
416 '};',
417 '',
418 '',
419 ];
420
421 # Generate the g_abBs3Cg1Opcodes array.
422 asLines += [
423 'const uint8_t BS3_FAR_DATA g_abBs3Cg1Operands[] = ',
424 '{',
425 ];
426 for oInstr in self.aoInstructions:
427 asLines.append(' ' + oInstr.getOperands() + ',');
428 asLines += [
429 '};',
430 '',
431 '',
432 ];
433
434 # Generate the g_abBs3Cg1Operands array.
435 asLines += [
436 'const BS3CG1INSTR BS3_FAR_DATA g_aBs3Cg1Instructions[] = ',
437 '{',
438 ];
439 for oInstr in self.aoInstructions:
440 asLines.append(' {');
441 asLines += oInstr.getInstructionEntry();
442 asLines.append(' },');
443 asLines += [
444 '};',
445 'const uint16_t BS3_FAR_DATA g_cBs3Cg1Instructions = RT_ELEMENTS(g_aBs3Cg1Instructions);',
446 '',
447 '',
448 ];
449
450 # Generate the g_abBs3Cg1Tests array.
451 asLines += [
452 'const uint8_t BS3_FAR_DATA g_abBs3Cg1Tests[] = ',
453 '{',
454 ];
455 for oTests in self.aoTests:
456 asLines.append(' /* offTests=%s */' % (oTests.offTests,));
457 asLines += oTests.asLines;
458 asLines += [
459 '};',
460 '',
461 ];
462
463
464 #/** The test data that BS3CG1INSTR.
465 # * In order to simplify generating these, we use a byte array. */
466 #extern const uint8_t BS3_FAR_DATA g_abBs3Cg1Tests[];
467
468
469 oOut.write('\n'.join(asLines));
470 return True;
471
472
473 def usage(self):
474 """ Prints usage. """
475 print('usage: bs3-cpu-generated-1-data.py [output file|-]');
476 return 0;
477
478 def main(self, asArgs):
479 """
480 C-like main function.
481 Returns exit code.
482 """
483
484 #
485 # Quick argument parsing.
486 #
487 if len(asArgs) == 1:
488 sOutFile = '-';
489 elif len(asArgs) != 2:
490 print('syntax error! Expected exactly one argument.');
491 return 2;
492 elif asArgs[1] in [ '-h', '-?', '--help' ]:
493 return self.usage();
494 else:
495 sOutFile = asArgs[1];
496
497 #
498 # Process the instructions specified in the IEM sources.
499 #
500 if self.processInstruction():
501
502 #
503 # Open the output file and generate the code.
504 #
505 if sOutFile == '-':
506 oOut = sys.stdout;
507 else:
508 try:
509 oOut = open(sOutFile, 'wt');
510 except Exception as oXcpt:
511 print('error! Failed open "%s" for writing: %s' % (sOutFile, oXcpt,));
512 return 1;
513 if self.generateCode(oOut):
514 return 0;
515
516 return 1;
517
518
519if __name__ == '__main__':
520 sys.exit(Bs3CpuGenerated1Generator().main(sys.argv));
521
522
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