VirtualBox

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

Last change on this file since 66058 was 66055, 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 66055 2017-03-10 21:00:14Z vboxsync $
4# pylint: disable=invalid-name
5
6"""
7Generates testcases from @optest specifications in IEM.
8"""
9
10from __future__ import print_function;
11
12__copyright__ = \
13"""
14Copyright (C) 2017 Oracle Corporation
15
16This file is part of VirtualBox Open Source Edition (OSE), as
17available from http://www.virtualbox.org. This file is free software;
18you can redistribute it and/or modify it under the terms of the GNU
19General Public License (GPL) as published by the Free Software
20Foundation, in version 2 as it comes in the "COPYING" file of the
21VirtualBox OSE distribution. VirtualBox OSE is distributed in the
22hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
23
24The contents of this file may alternatively be used under the terms
25of the Common Development and Distribution License Version 1.0
26(CDDL) only, as it comes in the "COPYING.CDDL" file of the
27VirtualBox OSE distribution, in which case the provisions of the
28CDDL are applicable instead of those of the GPL.
29
30You may elect to license modified versions of this file under the
31terms and conditions of either the GPL or the CDDL or both.
32"""
33__version__ = "$Revision: 66055 $"
34
35# Standard python imports.
36import os;
37import sys;
38
39# Only the main script needs to modify the path.
40g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)));
41g_ksVmmAllDir = os.path.join(os.path.dirname(g_ksValidationKitDir), 'VMM', 'VMMAll')
42sys.path.append(g_ksVmmAllDir);
43
44import IEMAllInstructionsPython as iai; # pylint: disable=import-error
45
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 field identifier.
140 if oOperation.sField not in Bs3Cg1TestEncoder.kdSmallFields:
141 asRet.append('BS3CG1DST_%s' % (oOperation.sField.upper().replace('.', '_'),));
142
143 # Escaped size byte?
144 if cbValue not in Bs3Cg1TestEncoder.kdSmallSizes:
145 if cbValue >= 256 or cbValue not in [ 1, 2, 4, 6, 8, 12, 16, 32, 64, 128, ]:
146 raise Exception('Invalid value size: %s' % (cbValue,));
147 asRet.append('0x%02x' % (cbValue,));
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 oInstr.aoTests:
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 66055 2017-03-10 21:00:14Z 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 '#pragma data_seg ("BS3DATA16")',
394 ];
395
396 # Generate the g_achBs3Cg1Mnemonics array.
397 asLines += [
398 'const char BS3_FAR_DATA g_achBs3Cg1Mnemonics[] = ',
399 '{',
400 ];
401 for oInstr in self.aoInstructions:
402 asLines.append(' \"%s\"' % (oInstr.oInstr.sMnemonic,));
403 asLines += [
404 '};',
405 '',
406 '',
407 ];
408
409 # Generate the g_abBs3Cg1Opcodes array.
410 asLines += [
411 'const uint8_t BS3_FAR_DATA g_abBs3Cg1Opcodes[] = ',
412 '{',
413 ];
414 for oInstr in self.aoInstructions:
415 asLines.append(' ' + ', '.join(oInstr.asOpcodes) + ',');
416 asLines += [
417 '};',
418 '',
419 '',
420 ];
421
422 # Generate the g_abBs3Cg1Opcodes array.
423 asLines += [
424 'const uint8_t BS3_FAR_DATA g_abBs3Cg1Operands[] = ',
425 '{',
426 ];
427 for oInstr in self.aoInstructions:
428 asLines.append(' ' + oInstr.getOperands() + ',');
429 asLines += [
430 '};',
431 '',
432 '',
433 ];
434
435 # Generate the g_abBs3Cg1Operands array.
436 asLines += [
437 'const BS3CG1INSTR BS3_FAR_DATA g_aBs3Cg1Instructions[] = ',
438 '{',
439 ];
440 for oInstr in self.aoInstructions:
441 asLines.append(' {');
442 asLines += oInstr.getInstructionEntry();
443 asLines.append(' },');
444 asLines += [
445 '};',
446 'const uint16_t BS3_FAR_DATA g_cBs3Cg1Instructions = RT_ELEMENTS(g_aBs3Cg1Instructions);',
447 '',
448 '',
449 ];
450
451 # Generate the g_abBs3Cg1Tests array.
452 asLines += [
453 'const uint8_t BS3_FAR_DATA g_abBs3Cg1Tests[] = ',
454 '{',
455 ];
456 for oTests in self.aoTests:
457 asLines.append(' /* offTests=%s */' % (oTests.offTests,));
458 asLines += oTests.asLines;
459 asLines += [
460 '};',
461 '',
462 ];
463
464
465 #/** The test data that BS3CG1INSTR.
466 # * In order to simplify generating these, we use a byte array. */
467 #extern const uint8_t BS3_FAR_DATA g_abBs3Cg1Tests[];
468
469
470 oOut.write('\n'.join(asLines));
471 return True;
472
473
474 def usage(self):
475 """ Prints usage. """
476 print('usage: bs3-cpu-generated-1-data.py [output file|-]');
477 return 0;
478
479 def main(self, asArgs):
480 """
481 C-like main function.
482 Returns exit code.
483 """
484
485 #
486 # Quick argument parsing.
487 #
488 if len(asArgs) == 1:
489 sOutFile = '-';
490 elif len(asArgs) != 2:
491 print('syntax error! Expected exactly one argument.');
492 return 2;
493 elif asArgs[1] in [ '-h', '-?', '--help' ]:
494 return self.usage();
495 else:
496 sOutFile = asArgs[1];
497
498 #
499 # Process the instructions specified in the IEM sources.
500 #
501 if self.processInstruction():
502
503 #
504 # Open the output file and generate the code.
505 #
506 if sOutFile == '-':
507 oOut = sys.stdout;
508 else:
509 try:
510 oOut = open(sOutFile, 'w');
511 except Exception as oXcpt:
512 print('error! Failed open "%s" for writing: %s' % (sOutFile, oXcpt,));
513 return 1;
514 if self.generateCode(oOut):
515 return 0;
516
517 return 1;
518
519
520if __name__ == '__main__':
521 sys.exit(Bs3CpuGenerated1Generator().main(sys.argv));
522
523
Note: See TracBrowser for help on using the repository browser.

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