VirtualBox

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

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

IEM: Started on generating tests from instruction specs.

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