VirtualBox

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

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

bs3-cpu-generated-1,IEM: updates.

  • Property svn:eol-style set to LF
  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
File size: 19.7 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3# $Id: bs3-cpu-generated-1-data.py 66113 2017-03-15 14:32:28Z 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: 66113 $"
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 _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', '=', fFlags, 'uint'));
125
126 if oInstr.asFlClear:
127 fFlags = oInstr.getClearedFlagsMask();
128 assert fFlags != 0;
129 aoOutputs.append(iai.TestInOut('efl', '&~=', fFlags, 'uint'));
130
131 if oInstr.asFlSet:
132 fFlags = oInstr.getSetFlagsMask();
133 assert fFlags != 0;
134 aoOutputs.append(iai.TestInOut('efl', '|=', 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
223class 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 = [];
232
233 # Encode the tests.
234 for iTest, oTest in enumerate(oInstr.aoTests):
235 oEncodedTest = Bs3Cg1TestEncoder(iTest + 1 == len(oInstr.aoTests));
236 oEncodedTest.encodeTest(oTest);
237
238 self.cbTests += len(oEncodedTest.asHdr) + len(oEncodedTest.asSelectors) \
239 + len(oEncodedTest.asInputs) + len(oEncodedTest.asOutputs);
240
241 self.asLines += self.bytesToLines(' /*hdr:*/ ', oEncodedTest.asHdr);
242 if oEncodedTest.asSelectors:
243 self.asLines += self.bytesToLines(' /*sel:*/ ', oEncodedTest.asSelectors);
244 if oEncodedTest.asInputs:
245 self.asLines += self.bytesToLines(' /* in:*/ ', oEncodedTest.asInputs);
246 if oEncodedTest.asOutputs:
247 self.asLines += self.bytesToLines(' /*out:*/ ', oEncodedTest.asOutputs);
248
249 @staticmethod
250 def bytesToLines(sPrefix, asBytes):
251 """
252 Formats a series of bytes into one or more lines.
253 A byte ending with a newline indicates that we should start a new line,
254 and prefix it by len(sPrefix) spaces.
255
256 Returns list of lines.
257 """
258 asRet = [];
259 sLine = sPrefix;
260 for sByte in asBytes:
261 if sByte[-1] == '\n':
262 sLine += sByte[:-1] + ',';
263 asRet.append(sLine);
264 sLine = ' ' * len(sPrefix);
265 else:
266 if len(sLine) + 2 + len(sByte) > 132 and len(sLine) > len(sPrefix):
267 asRet.append(sLine[:-1]);
268 sLine = ' ' * len(sPrefix);
269 sLine += sByte + ', ';
270
271
272 if len(sLine) > len(sPrefix):
273 asRet.append(sLine);
274 return asRet;
275
276
277 def isEqual(self, oOther):
278 """ Compares two encoded tests. """
279 if self.cbTests != oOther.cbTests:
280 return False;
281 if len(self.asLines) != len(oOther.asLines):
282 return False;
283 for iLine, sLines in enumerate(self.asLines):
284 if sLines != oOther.asLines[iLine]:
285 return False;
286 return True;
287
288
289
290class Bs3Cg1Instruction(object):
291 """
292 An instruction with tests.
293 """
294
295 def __init__(self, oMap, oInstr, oTests):
296 self.oMap = oMap; # type: iai.InstructionMap
297 self.oInstr = oInstr; # type: iai.Instruction
298 self.oTests = oTests; # type: Bs3Cg1EncodedTests
299
300 self.asOpcodes = oMap.asLeadOpcodes + [ '0x%02x' % (oInstr.getOpcodeByte(),) ];
301 self.sEncoding = iai.g_kdEncodings[oInstr.sEncoding][0];
302 for oOp in oInstr.aoOperands:
303 self.sEncoding += '_' + oOp.sType;
304 self.asFlags = [];
305 self.fAdvanceMnemonic = True; ##< Set by the caller.
306 if self.sEncoding == 'ModR/M':
307 if 'ignores_op_size' not in oInstr.dHints:
308 self.sPfxKind = 'BS3CGPFXKIND_MODRM';
309 else:
310 self.sPfxKind = 'BS3CGPFXKIND_MODRM_NO_OP_SIZES';
311 else:
312 self.sPfxKind = '0';
313
314
315 def getOperands(self):
316 """ Returns comma separated string of operand values for g_abBs3Cg1Operands. """
317 return ', '.join(['(uint8_t)BS3CG1OP_%s' % (oOp.sType,) for oOp in self.oInstr.aoOperands]);
318
319 def getInstructionEntry(self):
320 """ Returns an array of BS3CG1INSTR member initializers. """
321 return [
322 ' /* cbOpcodes = */ %s,' % (len(self.asOpcodes),),
323 ' /* cOperands = */ %s,' % (len(self.oInstr.aoOperands),),
324 ' /* cchMnemonic = */ %s,' % (len(self.oInstr.sMnemonic),),
325 ' /* fAdvanceMnemonic = */ %s,' % ('true' if self.fAdvanceMnemonic else 'false',),
326 ' /* offTests = */ %s,' % (self.oTests.offTests,),
327 ' /* enmEncoding = */ (unsigned)%s,' % (self.sEncoding,),
328 ' /* enmPfxKind = */ (unsigned)%s,' % (self.sPfxKind,),
329 ' /* uUnused = */ 0,',
330 ' /* fFlags = */ %s' % (' | '.join(self.asFlags) if self.asFlags else '0'),
331 ];
332
333
334class Bs3CpuGenerated1Generator(object):
335 """
336 The generator code for bs3-cpu-generated-1.
337 """
338
339 def __init__(self):
340 self.aoInstructions = []; # type: Bs3Cg1Instruction
341 self.aoTests = []; # type: Bs3Cg1EncodedTests
342 self.cbTests = 0;
343
344 def addTests(self, oTests):
345 """
346 Adds oTests to self.aoTests, setting the oTests.offTests member.
347 Checks for and eliminates duplicates.
348 Returns the tests to use.
349 """
350 # Check for duplicates.
351 for oExisting in self.aoTests:
352 if oTests.isEqual(oExisting):
353 return oExisting;
354
355 # New test, so add it.
356 oTests.offTests = self.cbTests;
357 self.aoTests.append(oTests);
358 self.cbTests += oTests.cbTests;
359
360 return oTests;
361
362 def processInstruction(self):
363 """
364 Processes the IEM specified instructions.
365 Returns success indicator.
366 """
367
368 #
369 # Group instructions by mnemonic to reduce the number of sub-tests.
370 #
371 for oInstr in sorted(iai.g_aoAllInstructions,
372 key = lambda oInstr: oInstr.sMnemonic + ''.join([oOp.sType for oOp in oInstr.aoOperands])
373 + (oInstr.sOpcode if oInstr.sOpcode else 'zz')):
374 if oInstr.aoTests:
375 oTests = Bs3Cg1EncodedTests(oInstr);
376 oTests = self.addTests(oTests);
377
378 for oMap in oInstr.aoMaps:
379 self.aoInstructions.append(Bs3Cg1Instruction(oMap, oInstr, oTests));
380
381 # Set fAdvanceMnemonic.
382 for iInstr, oInstr in enumerate(self.aoInstructions):
383 oInstr.fAdvanceMnemonic = iInstr + 1 >= len(self.aoInstructions) \
384 or oInstr.oInstr.sMnemonic != self.aoInstructions[iInstr + 1].oInstr.sMnemonic;
385
386 return True;
387
388 def generateCode(self, oOut):
389 """
390 Generates the C code.
391 Returns success indicator.
392 """
393
394 # First, a file header.
395 asLines = [
396 '/*',
397 ' * Autogenerated by $Id: bs3-cpu-generated-1-data.py 66113 2017-03-15 14:32:28Z vboxsync $ ',
398 ' * Do not edit!',
399 ' */',
400 '',
401 '/*',
402 ' * Copyright (C) 2017 Oracle Corporation',
403 ' *',
404 ' * This file is part of VirtualBox Open Source Edition (OSE), as',
405 ' * available from http://www.virtualbox.org. This file is free software;',
406 ' * you can redistribute it and/or modify it under the terms of the GNU',
407 ' * General Public License (GPL) as published by the Free Software',
408 ' * Foundation, in version 2 as it comes in the "COPYING" file of the',
409 ' * VirtualBox OSE distribution. VirtualBox OSE is distributed in the',
410 ' * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.',
411 ' * ',
412 ' * The contents of this file may alternatively be used under the terms',
413 ' * of the Common Development and Distribution License Version 1.0',
414 ' * (CDDL) only, as it comes in the "COPYING.CDDL" file of the',
415 ' * VirtualBox OSE distribution, in which case the provisions of the',
416 ' * CDDL are applicable instead of those of the GPL.',
417 ' * ',
418 ' * You may elect to license modified versions of this file under the',
419 ' * terms and conditions of either the GPL or the CDDL or both.',
420 ' */',
421 '',
422 '',
423 '#include "bs3-cpu-generated-1.h"',
424 '',
425 '',
426 '#pragma data_seg ("BS3DATA16")',
427 ];
428
429 # Generate the g_achBs3Cg1Mnemonics array.
430 asLines += [
431 'const char BS3_FAR_DATA g_achBs3Cg1Mnemonics[] = ',
432 '{',
433 ];
434 for oInstr in self.aoInstructions:
435 asLines.append(' \"%s\"' % (oInstr.oInstr.sMnemonic,));
436 asLines += [
437 '};',
438 '',
439 '',
440 ];
441
442 # Generate the g_abBs3Cg1Opcodes array.
443 asLines += [
444 'const uint8_t BS3_FAR_DATA g_abBs3Cg1Opcodes[] = ',
445 '{',
446 ];
447 for oInstr in self.aoInstructions:
448 asLines.append(' ' + ', '.join(oInstr.asOpcodes) + ',');
449 asLines += [
450 '};',
451 '',
452 '',
453 ];
454
455 # Generate the g_abBs3Cg1Opcodes array.
456 asLines += [
457 'const uint8_t BS3_FAR_DATA g_abBs3Cg1Operands[] = ',
458 '{',
459 ];
460 for oInstr in self.aoInstructions:
461 asLines.append(' ' + oInstr.getOperands() + ',');
462 asLines += [
463 '};',
464 '',
465 '',
466 ];
467
468 # Generate the g_abBs3Cg1Operands array.
469 asLines += [
470 'const BS3CG1INSTR BS3_FAR_DATA g_aBs3Cg1Instructions[] = ',
471 '{',
472 ];
473 for oInstr in self.aoInstructions:
474 asLines.append(' {');
475 asLines += oInstr.getInstructionEntry();
476 asLines.append(' },');
477 asLines += [
478 '};',
479 'const uint16_t BS3_FAR_DATA g_cBs3Cg1Instructions = RT_ELEMENTS(g_aBs3Cg1Instructions);',
480 '',
481 '',
482 ];
483
484 # Generate the g_abBs3Cg1Tests array.
485 asLines += [
486 'const uint8_t BS3_FAR_DATA g_abBs3Cg1Tests[] = ',
487 '{',
488 ];
489 for oTests in self.aoTests:
490 asLines.append(' /* offTests=%s */' % (oTests.offTests,));
491 asLines += oTests.asLines;
492 asLines += [
493 '};',
494 '',
495 ];
496
497
498 #/** The test data that BS3CG1INSTR.
499 # * In order to simplify generating these, we use a byte array. */
500 #extern const uint8_t BS3_FAR_DATA g_abBs3Cg1Tests[];
501
502
503 oOut.write('\n'.join(asLines));
504 return True;
505
506
507 def usage(self):
508 """ Prints usage. """
509 print('usage: bs3-cpu-generated-1-data.py [output file|-]');
510 return 0;
511
512 def main(self, asArgs):
513 """
514 C-like main function.
515 Returns exit code.
516 """
517
518 #
519 # Quick argument parsing.
520 #
521 if len(asArgs) == 1:
522 sOutFile = '-';
523 elif len(asArgs) != 2:
524 print('syntax error! Expected exactly one argument.');
525 return 2;
526 elif asArgs[1] in [ '-h', '-?', '--help' ]:
527 return self.usage();
528 else:
529 sOutFile = asArgs[1];
530
531 #
532 # Process the instructions specified in the IEM sources.
533 #
534 if self.processInstruction():
535
536 #
537 # Open the output file and generate the code.
538 #
539 if sOutFile == '-':
540 oOut = sys.stdout;
541 else:
542 try:
543 oOut = open(sOutFile, 'w');
544 except Exception as oXcpt:
545 print('error! Failed open "%s" for writing: %s' % (sOutFile, oXcpt,));
546 return 1;
547 if self.generateCode(oOut):
548 return 0;
549
550 return 1;
551
552
553if __name__ == '__main__':
554 sys.exit(Bs3CpuGenerated1Generator().main(sys.argv));
555
556
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