VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMAll/IEMAllThreadedPython.py@ 98927

Last change on this file since 98927 was 98927, checked in by vboxsync, 21 months ago

VMM/IEM: More work on processing MC blocks and generating functions from them. bugref:10369

  • Property svn:eol-style set to LF
  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
File size: 36.3 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3# $Id: IEMAllThreadedPython.py 98927 2023-03-13 10:14:58Z vboxsync $
4# pylint: disable=invalid-name
5
6"""
7Annotates and generates threaded functions from IEMAllInstructions*.cpp.h.
8"""
9
10from __future__ import print_function;
11
12__copyright__ = \
13"""
14Copyright (C) 2023 Oracle and/or its affiliates.
15
16This file is part of VirtualBox base platform packages, as
17available from https://www.virtualbox.org.
18
19This program is free software; you can redistribute it and/or
20modify it under the terms of the GNU General Public License
21as published by the Free Software Foundation, in version 3 of the
22License.
23
24This program is distributed in the hope that it will be useful, but
25WITHOUT ANY WARRANTY; without even the implied warranty of
26MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
27General Public License for more details.
28
29You should have received a copy of the GNU General Public License
30along with this program; if not, see <https://www.gnu.org/licenses>.
31
32SPDX-License-Identifier: GPL-3.0-only
33"""
34__version__ = "$Revision: 98927 $"
35
36# Standard python imports.
37import datetime;
38import os;
39import sys;
40import argparse;
41
42import IEMAllInstructionsPython as iai;
43
44
45# Python 3 hacks:
46if sys.version_info[0] >= 3:
47 long = int; # pylint: disable=redefined-builtin,invalid-name
48
49## Number of generic parameters for the thread functions.
50g_kcThreadedParams = 3;
51
52g_kdTypeInfo = {
53 # type name: (cBits, fSigned,)
54 'int8_t': ( 8, True, ),
55 'int16_t': ( 16, True, ),
56 'int32_t': ( 32, True, ),
57 'int64_t': ( 64, True, ),
58 'uint8_t': ( 8, False, ),
59 'uint16_t': ( 16, False, ),
60 'uint32_t': ( 32, False, ),
61 'uint64_t': ( 64, False, ),
62 'uintptr_t': ( 64, False, ), # ASSUMES 64-bit host pointer size.
63 'bool': ( 1, False, ),
64 'IEMMODE': ( 2, False, ),
65};
66
67g_kdIemFieldToType = {
68 # Illegal ones:
69 'offInstrNextByte': ( None, ),
70 'cbInstrBuf': ( None, ),
71 'pbInstrBuf': ( None, ),
72 'uInstrBufPc': ( None, ),
73 'cbInstrBufTotal': ( None, ),
74 'offCurInstrStart': ( None, ),
75 'cbOpcode': ( None, ),
76 'offOpcode': ( None, ),
77 'offModRm': ( None, ),
78 # Okay ones.
79 'fPrefixes': ( 'uint32_t', ),
80 'uRexReg': ( 'uint8_t', ),
81 'uRexB': ( 'uint8_t', ),
82 'uRexIndex': ( 'uint8_t', ),
83 'iEffSeg': ( 'uint8_t', ),
84 'enmEffOpSize': ( 'IEMMODE', ),
85 'enmDefAddrMode': ( 'IEMMODE', ),
86 'enmEffAddrMode': ( 'IEMMODE', ),
87 'enmDefOpSize': ( 'IEMMODE', ),
88 'idxPrefix': ( 'uint8_t', ),
89 'uVex3rdReg': ( 'uint8_t', ),
90 'uVexLength': ( 'uint8_t', ),
91 'fEvexStuff': ( 'uint8_t', ),
92 'uFpuOpcode': ( 'uint16_t', ),
93};
94
95class ThreadedParamRef(object):
96 """
97 A parameter reference for a threaded function.
98 """
99
100 def __init__(self, sOrgRef, sType, oStmt, iParam, offParam = 0):
101 self.sNewName = 'x'; ##< The variable name in the threaded function.
102 self.sOrgRef = sOrgRef; ##< The name / reference in the original code.
103 self.sStdRef = ''.join(sOrgRef.split()); ##< Normalized name to deal with spaces in macro invocations and such.
104 self.sType = sType; ##< The type (typically derived).
105 self.oStmt = oStmt; ##< The statement making the reference.
106 self.iParam = iParam; ##< The parameter containing the references.
107 self.offParam = offParam; ##< The offset in the parameter of the reference.
108
109
110class ThreadedFunction(object):
111 """
112 A threaded function.
113 """
114
115 def __init__(self, oMcBlock):
116 self.oMcBlock = oMcBlock # type: IEMAllInstructionsPython.McBlock
117 ## Dictionary of local variables (IEM_MC_LOCAL[_CONST]) and call arguments (IEM_MC_ARG*).
118 self.dVariables = {} # type: dict(str,McStmtVar)
119 ##
120 self.aoParamRefs = [] # type: list(ThreadedParamRef)
121 self.dParamRefs = {} # type: dict(str,list(ThreadedParamRef))
122 self.cMinParams = 0; ##< Minimum number of parameters to the threaded function.
123
124 @staticmethod
125 def dummyInstance():
126 """ Gets a dummy instance. """
127 return ThreadedFunction(iai.McBlock('null', 999999999, 999999999, 'nil', 999999999));
128
129 def raiseProblem(self, sMessage):
130 """ Raises a problem. """
131 raise Exception('%s:%s: error: %s' % (self.oMcBlock.sSrcFile, self.oMcBlock.iBeginLine, sMessage, ));
132
133 def getIndexName(self):
134 sName = self.oMcBlock.sFunction;
135 if sName.startswith('iemOp_'):
136 sName = sName[len('iemOp_'):];
137 if self.oMcBlock.iInFunction == 0:
138 return 'kIemThreadedFunc_%s' % ( sName, );
139 return 'kIemThreadedFunc_%s_%s' % ( sName, self.oMcBlock.iInFunction, );
140
141 def getFunctionName(self):
142 sName = self.oMcBlock.sFunction;
143 if sName.startswith('iemOp_'):
144 sName = sName[len('iemOp_'):];
145 if self.oMcBlock.iInFunction == 0:
146 return 'iemThreadedFunc_%s' % ( sName, );
147 return 'iemThreadedFunc_%s_%s' % ( sName, self.oMcBlock.iInFunction, );
148
149 def analyzeReferenceToType(self, sRef):
150 """
151 Translates a variable or structure reference to a type.
152 Returns type name.
153 Raises exception if unable to figure it out.
154 """
155 ch0 = sRef[0];
156 if ch0 == 'u':
157 if sRef.startswith('u32'):
158 return 'uint32_t';
159 if sRef.startswith('u8') or sRef == 'uReg':
160 return 'uint8_t';
161 if sRef.startswith('u64'):
162 return 'uint64_t';
163 if sRef.startswith('u16'):
164 return 'uint16_t';
165 elif ch0 == 'b':
166 return 'uint8_t';
167 elif ch0 == 'f':
168 return 'bool';
169 elif ch0 == 'i':
170 if sRef.startswith('i8'):
171 return 'int8_t';
172 if sRef.startswith('i16'):
173 return 'int32_t';
174 if sRef.startswith('i32'):
175 return 'int32_t';
176 if sRef.startswith('i64'):
177 return 'int64_t';
178 if sRef in ('iReg', 'iSegReg', 'iSrcReg', 'iDstReg'):
179 return 'uint8_t';
180 elif ch0 == 'p':
181 if sRef.find('-') < 0:
182 return 'uintptr_t';
183 if sRef.startswith('pVCpu->iem.s.'):
184 sField = sRef[len('pVCpu->iem.s.') : ];
185 if sField in g_kdIemFieldToType:
186 if g_kdIemFieldToType[sField][0]:
187 return g_kdIemFieldToType[sField][0];
188 self.raiseProblem('Reference out-of-bounds decoder field: %s' % (sRef,));
189 elif ch0 == 'G' and sRef.startswith('GCPtr'):
190 return 'uint64_t';
191 elif sRef == 'cShift': ## @todo risky
192 return 'uint8_t';
193 self.raiseProblem('Unknown reference: %s' % (sRef,));
194 return None; # Shut up pylint 2.16.2.
195
196 def analyzeConsolidateThreadedParamRefs(self):
197 """
198 Consolidate threaded function parameter references into a dictionary
199 with lists of the references to each variable/field.
200 """
201 # Gather unique parameters.
202 self.dParamRefs = {};
203 for oRef in self.aoParamRefs:
204 if oRef.sStdRef not in self.dParamRefs:
205 self.dParamRefs[oRef.sStdRef] = [oRef,];
206 else:
207 self.dParamRefs[oRef.sStdRef].append(oRef);
208
209 # Generate names for them for use in the threaded function.
210 dParamNames = {};
211 for sName, aoRefs in self.dParamRefs.items():
212 # Morph the reference expression into a name.
213 if sName.startswith('IEM_GET_MODRM_REG'): sName = 'bModRmRegP';
214 elif sName.startswith('IEM_GET_MODRM_RM'): sName = 'bModRmRmP';
215 elif sName.startswith('IEM_GET_MODRM_REG_8'): sName = 'bModRmReg8P';
216 elif sName.startswith('IEM_GET_MODRM_RM_8'): sName = 'bModRmRm8P';
217 elif sName.startswith('IEM_GET_EFFECTIVE_VVVV'): sName = 'bEffVvvvP';
218 elif sName.find('.') >= 0 or sName.find('->') >= 0:
219 sName = sName[max(sName.rfind('.'), sName.rfind('>')) + 1 : ] + 'P';
220 else:
221 sName += 'P';
222
223 # Ensure it's unique.
224 if sName in dParamNames:
225 for i in range(10):
226 if sName + str(i) not in dParamNames:
227 sName += str(i);
228 break;
229 dParamNames[sName] = True;
230
231 # Update all the references.
232 for oRef in aoRefs:
233 oRef.sNewName = sName;
234
235 # Organize them by size too for the purpose of optimize them.
236 dBySize = {} # type: dict(str,str)
237 for sStdRef, aoRefs in self.dParamRefs.items():
238 cBits = g_kdTypeInfo[aoRefs[0].sType][0];
239 assert(cBits <= 64);
240 if cBits not in dBySize:
241 dBySize[cBits] = [sStdRef,]
242 else:
243 dBySize[cBits].append(sStdRef);
244
245 # Pack the parameters as best as we can, starting with the largest ones
246 # and ASSUMING a 64-bit parameter size.
247 self.cMinParams = 0;
248 offParam = 0;
249 for cBits in sorted(dBySize.keys(), reverse = True):
250 for sStdRef in dBySize[cBits]:
251 if offParam < 64:
252 offParam += cBits;
253 else:
254 self.cMinParams += 1;
255 offParam = cBits;
256 assert(offParam <= 64);
257
258 for oRef in self.dParamRefs[sStdRef]:
259 oRef.iParam = self.cMinParams;
260 oRef.offParam = offParam - cBits;
261
262 if offParam > 0:
263 self.cMinParams += 1;
264
265 # Currently there are a few that requires 4 parameters, list these so we can figure out why:
266 if self.cMinParams >= 3:
267 print('debug: cMinParams=%s cRawParams=%s - %s:%d'
268 % (self.cMinParams, len(self.dParamRefs), self.oMcBlock.sSrcFile, self.oMcBlock.iBeginLine,));
269
270 return True;
271
272 ksHexDigits = '0123456789abcdefABCDEF';
273
274 def analyzeFindThreadedParamRefs(self, aoStmts):
275 """
276 Scans the statements for things that have to passed on to the threaded
277 function (populates self.aoParamRefs).
278 """
279 for oStmt in aoStmts:
280 # Some statements we can skip alltogether.
281 if isinstance(oStmt, (iai.McStmtVar, iai.McCppPreProc)):
282 continue;
283 if not oStmt.asParams:
284 continue;
285 if oStmt.isCppStmt() and oStmt.fDecode:
286 continue;
287
288 # Inspect the target of calls to see if we need to pass down a
289 # function pointer or function table pointer for it to work.
290 aiSkipParams = {};
291 if isinstance(oStmt, iai.McStmtCall):
292 if oStmt.sFn[0] == 'p':
293 self.aoParamRefs.append(ThreadedParamRef(oStmt.sFn, 'uintptr_t', oStmt, oStmt.idxFn));
294 elif ( oStmt.sFn[0] != 'i'
295 and not oStmt.sFn.startswith('IEMTARGETCPU_EFL_BEHAVIOR_SELECT')
296 and not oStmt.sFn.startswith('IEM_SELECT_HOST_OR_FALLBACK') ):
297 self.raiseProblem('Bogus function name in %s: %s' % (oStmt.sName, oStmt.sFn,));
298 aiSkipParams[oStmt.idxFn] = True;
299
300 # Check all the parameters for bogus references.
301 for iParam, sParam in enumerate(oStmt.asParams):
302 if iParam not in aiSkipParams and sParam not in self.dVariables:
303 # The parameter may contain a C expression, so we have to try
304 # extract the relevant bits, i.e. variables and fields while
305 # ignoring operators and parentheses.
306 offParam = 0;
307 while offParam < len(sParam):
308 # Is it the start of an C identifier? If so, find the end, but don't stop on field separators (->, .).
309 ch = sParam[offParam];
310 if ch.isalpha() or ch == '_':
311 offStart = offParam;
312 offParam += 1;
313 while offParam < len(sParam):
314 ch = sParam[offParam];
315 if not ch.isalnum() and ch != '_' and ch != '.':
316 if ch != '-' or sParam[offParam + 1] != '>':
317 # Special hack for the 'CTX_SUFF(pVM)' bit in pVCpu->CTX_SUFF(pVM)->xxxx:
318 if ( ch == '('
319 and sParam[offStart : offParam + len('(pVM)->')] == 'pVCpu->CTX_SUFF(pVM)->'):
320 offParam += len('(pVM)->') - 1;
321 else:
322 break;
323 offParam += 1;
324 offParam += 1;
325 sRef = sParam[offStart : offParam];
326
327 # For register references, we pass the full register indexes instead as macros
328 # like IEM_GET_MODRM_REG implicitly references pVCpu->iem.s.uRexReg and the
329 # threaded function will be more efficient if we just pass the register index
330 # as a 4-bit param.
331 if ( sRef.startswith('IEM_GET_MODRM')
332 or sRef.startswith('IEM_GET_EFFECTIVE_VVVV') ):
333 offParam = iai.McBlock.skipSpacesAt(sParam, offParam, len(sParam));
334 if sParam[offParam] != '(':
335 self.raiseProblem('Expected "(" following %s in "%s"' % (sRef, oStmt.renderCode(),));
336 (asMacroParams, offCloseParam) = iai.McBlock.extractParams(sParam, offParam);
337 if asMacroParams is None:
338 self.raiseProblem('Unable to find ")" for %s in "%s"' % (sRef, oStmt.renderCode(),));
339 self.aoParamRefs.append(ThreadedParamRef(sRef, 'uint8_t', oStmt, iParam, offStart));
340 offParam = offCloseParam + 1;
341
342 # We can skip known variables.
343 elif sRef in self.dVariables:
344 pass;
345
346 # Skip certain macro invocations.
347 elif sRef in ('IEM_GET_HOST_CPU_FEATURES',
348 'IEM_GET_GUEST_CPU_FEATURES',
349 'IEM_IS_GUEST_CPU_AMD'):
350 offParam = iai.McBlock.skipSpacesAt(sParam, offParam, len(sParam));
351 if sParam[offParam] != '(':
352 self.raiseProblem('Expected "(" following %s in "%s"' % (sRef, oStmt.renderCode(),));
353 (asMacroParams, offCloseParam) = iai.McBlock.extractParams(sParam, offParam);
354 if asMacroParams is None:
355 self.raiseProblem('Unable to find ")" for %s in "%s"' % (sRef, oStmt.renderCode(),));
356 offParam = offCloseParam + 1;
357 while offParam < len(sParam) and (sParam[offParam].isalnum() or sParam[offParam] in '_.'):
358 offParam += 1;
359
360 # Skip constants, globals, types (casts), sizeof and macros.
361 elif ( sRef.startswith('IEM_OP_PRF_')
362 or sRef.startswith('IEM_ACCESS_')
363 or sRef.startswith('X86_GREG_')
364 or sRef.startswith('X86_SREG_')
365 or sRef.startswith('X86_EFL_')
366 or sRef.startswith('X86_FSW_')
367 or sRef.startswith('X86_FCW_')
368 or sRef.startswith('g_')
369 or sRef in ( 'int8_t', 'int16_t', 'int32_t',
370 'INT8_C', 'INT16_C', 'INT32_C', 'INT64_C',
371 'UINT8_C', 'UINT16_C', 'UINT32_C', 'UINT64_C',
372 'UINT8_MAX', 'UINT16_MAX', 'UINT32_MAX', 'UINT64_MAX',
373 'sizeof', 'NOREF', 'RT_NOREF', 'IEMMODE_64BIT' ) ):
374 pass;
375
376 # Skip certain macro invocations.
377 # Any variable (non-field) and decoder fields in IEMCPU will need to be parameterized.
378 elif ( ( '.' not in sRef
379 and '-' not in sRef
380 and sRef not in ('pVCpu', ) )
381 or iai.McBlock.koReIemDecoderVars.search(sRef) is not None):
382 self.aoParamRefs.append(ThreadedParamRef(sRef, self.analyzeReferenceToType(sRef),
383 oStmt, iParam, offStart));
384 # Number.
385 elif ch.isdigit():
386 if ( ch == '0'
387 and offParam + 2 <= len(sParam)
388 and sParam[offParam + 1] in 'xX'
389 and sParam[offParam + 2] in self.ksHexDigits ):
390 offParam += 2;
391 while offParam < len(sParam) and sParam[offParam] in self.ksHexDigits:
392 offParam += 1;
393 else:
394 while offParam < len(sParam) and sParam[offParam].isdigit():
395 offParam += 1;
396 # Whatever else.
397 else:
398 offParam += 1;
399
400 # Traverse the branches of conditionals.
401 if isinstance(oStmt, iai.McStmtCond):
402 self.analyzeFindThreadedParamRefs(oStmt.aoIfBranch);
403 self.analyzeFindThreadedParamRefs(oStmt.aoElseBranch);
404 return True;
405
406 def analyzeFindVariablesAndCallArgs(self, aoStmts):
407 """ Scans the statements for MC variables and call arguments. """
408 for oStmt in aoStmts:
409 if isinstance(oStmt, iai.McStmtVar):
410 if oStmt.sVarName in self.dVariables:
411 raise Exception('Variable %s is defined more than once!' % (oStmt.sVarName,));
412 self.dVariables[oStmt.sVarName] = oStmt.sVarName;
413
414 # There shouldn't be any variables or arguments declared inside if/
415 # else blocks, but scan them too to be on the safe side.
416 if isinstance(oStmt, iai.McStmtCond):
417 cBefore = len(self.dVariables);
418 self.analyzeFindVariablesAndCallArgs(oStmt.aoIfBranch);
419 self.analyzeFindVariablesAndCallArgs(oStmt.aoElseBranch);
420 if len(self.dVariables) != cBefore:
421 raise Exception('Variables/arguments defined in conditional branches!');
422 return True;
423
424 def analyze(self):
425 """
426 Analyzes the code, identifying the number of parameters it requires and such.
427 May raise exceptions if we cannot grok the code.
428 """
429
430 # Decode the block into a list/tree of McStmt objects.
431 aoStmts = self.oMcBlock.decode();
432
433 # Scan the statements for local variables and call arguments (self.dVariables).
434 self.analyzeFindVariablesAndCallArgs(aoStmts);
435
436 # Now scan the code for variables and field references that needs to
437 # be passed to the threaded function because they are related to the
438 # instruction decoding.
439 self.analyzeFindThreadedParamRefs(aoStmts);
440 self.analyzeConsolidateThreadedParamRefs();
441
442 return True;
443
444 def generateInputCode(self):
445 """
446 Modifies the input code.
447 """
448 assert len(self.oMcBlock.asLines) > 2, "asLines=%s" % (self.oMcBlock.asLines,);
449 cchIndent = (self.oMcBlock.cchIndent + 3) // 4 * 4;
450 return iai.McStmt.renderCodeForList(self.oMcBlock.aoStmts, cchIndent = cchIndent).replace('\n', ' /* gen */\n', 1);
451
452
453class IEMThreadedGenerator(object):
454 """
455 The threaded code generator & annotator.
456 """
457
458 def __init__(self):
459 self.aoThreadedFuncs = [] # type: list(ThreadedFunction)
460 self.oOptions = None # type: argparse.Namespace
461 self.aoParsers = [] # type: list(IEMAllInstructionsPython.SimpleParser)
462
463 #
464 # Processing.
465 #
466
467 def processInputFiles(self):
468 """
469 Process the input files.
470 """
471
472 # Parse the files.
473 self.aoParsers = iai.parseFiles(self.oOptions.asInFiles);
474
475 # Wrap MC blocks into threaded functions and analyze these.
476 self.aoThreadedFuncs = [ThreadedFunction(oMcBlock) for oMcBlock in iai.g_aoMcBlocks];
477 dRawParamCounts = {};
478 dMinParamCounts = {};
479 for oThreadedFunction in self.aoThreadedFuncs:
480 oThreadedFunction.analyze();
481 dRawParamCounts[len(oThreadedFunction.dParamRefs)] = dRawParamCounts.get(len(oThreadedFunction.dParamRefs), 0) + 1;
482 dMinParamCounts[oThreadedFunction.cMinParams] = dMinParamCounts.get(oThreadedFunction.cMinParams, 0) + 1;
483 print('debug: param count distribution, raw and optimized:', file = sys.stderr);
484 for cCount in sorted({cBits: True for cBits in list(dRawParamCounts.keys()) + list(dMinParamCounts.keys())}.keys()):
485 print('debug: %s params: %4s raw, %4s min'
486 % (cCount, dRawParamCounts.get(cCount, 0), dMinParamCounts.get(cCount, 0)),
487 file = sys.stderr);
488
489 return True;
490
491 #
492 # Output
493 #
494
495 def generateLicenseHeader(self):
496 """
497 Returns the lines for a license header.
498 """
499 return [
500 '/*',
501 ' * Autogenerated by $Id: IEMAllThreadedPython.py 98927 2023-03-13 10:14:58Z vboxsync $ ',
502 ' * Do not edit!',
503 ' */',
504 '',
505 '/*',
506 ' * Copyright (C) 2023-' + str(datetime.date.today().year) + ' Oracle and/or its affiliates.',
507 ' *',
508 ' * This file is part of VirtualBox base platform packages, as',
509 ' * available from https://www.virtualbox.org.',
510 ' *',
511 ' * This program is free software; you can redistribute it and/or',
512 ' * modify it under the terms of the GNU General Public License',
513 ' * as published by the Free Software Foundation, in version 3 of the',
514 ' * License.',
515 ' *',
516 ' * This program is distributed in the hope that it will be useful, but',
517 ' * WITHOUT ANY WARRANTY; without even the implied warranty of',
518 ' * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU',
519 ' * General Public License for more details.',
520 ' *',
521 ' * You should have received a copy of the GNU General Public License',
522 ' * along with this program; if not, see <https://www.gnu.org/licenses>.',
523 ' *',
524 ' * The contents of this file may alternatively be used under the terms',
525 ' * of the Common Development and Distribution License Version 1.0',
526 ' * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included',
527 ' * in the VirtualBox distribution, in which case the provisions of the',
528 ' * CDDL are applicable instead of those of the GPL.',
529 ' *',
530 ' * You may elect to license modified versions of this file under the',
531 ' * terms and conditions of either the GPL or the CDDL or both.',
532 ' *',
533 ' * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0',
534 ' */',
535 '',
536 '',
537 '',
538 ];
539
540
541 def generateThreadedFunctionsHeader(self, oOut):
542 """
543 Generates the threaded functions header file.
544 Returns success indicator.
545 """
546
547 asLines = self.generateLicenseHeader();
548
549 # Generate the threaded function table indexes.
550 asLines += [
551 'typedef enum IEMTHREADEDFUNCS',
552 '{',
553 ' kIemThreadedFunc_Invalid = 0,',
554 ];
555 for oFunc in self.aoThreadedFuncs:
556 asLines.append(' ' + oFunc.getIndexName() + ',');
557 asLines += [
558 ' kIemThreadedFunc_End',
559 '} IEMTHREADEDFUNCS;',
560 '',
561 ];
562
563 # Prototype the function table.
564 sFnType = 'typedef IEM_DECL_IMPL_TYPE(VBOXSTRICTRC, FNIEMTHREADEDFUNC, (PVMCPU pVCpu';
565 for iParam in range(g_kcThreadedParams):
566 sFnType += ', uint64_t uParam' + str(iParam);
567 sFnType += '));'
568
569 asLines += [
570 sFnType,
571 'typedef FNIEMTHREADEDFUNC *PFNIEMTHREADEDFUNC;',
572 '',
573 'extern const PFNIEMTHREADEDFUNC g_apfnIemThreadedFunctions[kIemThreadedFunc_End];',
574 ];
575
576 oOut.write('\n'.join(asLines));
577 return True;
578
579 ksBitsToIntMask = {
580 1: "UINT64_C(0x1)",
581 2: "UINT64_C(0x3)",
582 4: "UINT64_C(0xf)",
583 8: "UINT64_C(0xff)",
584 16: "UINT64_C(0xffff)",
585 32: "UINT64_C(0xffffffff)",
586 };
587 def generateThreadedFunctionsSource(self, oOut):
588 """
589 Generates the threaded functions source file.
590 Returns success indicator.
591 """
592
593 asLines = self.generateLicenseHeader();
594 oOut.write('\n'.join(asLines));
595
596 # Prepare the fixed bits.
597 sParamList = '(PVMCPU pVCpu';
598 for iParam in range(g_kcThreadedParams):
599 sParamList += ', uint64_t uParam' + str(iParam);
600 sParamList += '))\n';
601
602 #
603 # Emit the function definitions.
604 #
605 for oThreadedFunction in self.aoThreadedFuncs:
606 oMcBlock = oThreadedFunction.oMcBlock;
607 # Function header
608 oOut.write( '\n'
609 + '\n'
610 + '/**\n'
611 + ' * %s at line %s offset %s in %s%s\n'
612 % (oMcBlock.sFunction, oMcBlock.iBeginLine, oMcBlock.offBeginLine, os.path.split(oMcBlock.sSrcFile)[1],
613 ' (macro expansion)' if oMcBlock.iBeginLine == oMcBlock.iEndLine else '')
614 + ' */\n'
615 + 'static IEM_DECL_IMPL_DEF(VBOXSTRICTRC, ' + oThreadedFunction.getFunctionName() + ',\n'
616 + ' ' + sParamList
617 + '{\n');
618
619 aasVars = [];
620 for aoRefs in oThreadedFunction.dParamRefs.values():
621 oRef = aoRefs[0];
622 cBits = g_kdTypeInfo[oRef.sType][0];
623
624 sTypeDecl = oRef.sType + ' const';
625
626 if cBits == 64:
627 assert oRef.offParam == 0;
628 if oRef.sType == 'uint64_t':
629 sUnpack = 'uParam%s;' % (oRef.iParam,);
630 else:
631 sUnpack = '(%s)uParam%s;' % (oRef.sType, oRef.iParam,);
632 elif oRef.offParam == 0:
633 sUnpack = '(%s)(uParam%s & %s);' % (oRef.sType, oRef.iParam, self.ksBitsToIntMask[cBits]);
634 else:
635 sUnpack = '(%s)((uParam%s >> %s) & %s);' \
636 % (oRef.sType, oRef.iParam, oRef.offParam, self.ksBitsToIntMask[cBits]);
637
638 sComment = '/* %s - %s ref%s */' % (oRef.sOrgRef, len(aoRefs), 's' if len(aoRefs) != 1 else '',);
639
640 aasVars.append([ '%s:%02u' % (oRef.iParam, oRef.offParam), sTypeDecl, oRef.sNewName, sUnpack, sComment ]);
641 acchVars = [0, 0, 0, 0, 0];
642 for asVar in aasVars:
643 for iCol, sStr in enumerate(asVar):
644 acchVars[iCol] = max(acchVars[iCol], len(sStr));
645 sFmt = ' %%-%ss %%-%ss = %%-%ss %%s\n' % (acchVars[1], acchVars[2], acchVars[3]);
646 for asVar in sorted(aasVars):
647 oOut.write(sFmt % (asVar[1], asVar[2], asVar[3], asVar[4],));
648
649 # RT_NOREF for unused parameters.
650 if oThreadedFunction.cMinParams < g_kcThreadedParams:
651 oOut.write(' RT_NOREF('
652 + ', '.join(['uParam%u' % (i,) for i in range(oThreadedFunction.cMinParams, g_kcThreadedParams)])
653 + ');\n');
654
655
656 oOut.write('}\n');
657
658 #
659 # Emit the function table.
660 #
661 oOut.write( '\n'
662 + '\n'
663 + '/**\n'
664 + ' * Function table.\n'
665 + ' */\n'
666 + 'const PFNIEMTHREADEDFUNC g_apfnIemThreadedFunctions[kIemThreadedFunc_End] =\n'
667 + '{\n'
668 + ' /*Invalid*/ NULL, \n');
669 for iThreadedFunction, oThreadedFunction in enumerate(self.aoThreadedFuncs):
670 oOut.write(' /*%4u*/ %s,\n' % (iThreadedFunction + 1, oThreadedFunction.getFunctionName(),));
671 oOut.write('};\n');
672
673 return True;
674
675 def getThreadedFunctionByIndex(self, idx):
676 """
677 Returns a ThreadedFunction object for the given index. If the index is
678 out of bounds, a dummy is returned.
679 """
680 if idx < len(self.aoThreadedFuncs):
681 return self.aoThreadedFuncs[idx];
682 return ThreadedFunction.dummyInstance();
683
684 def findEndOfMcEndStmt(self, sLine, offEndStmt):
685 """
686 Helper that returns the line offset following the 'IEM_MC_END();'.
687 """
688 assert sLine[offEndStmt:].startswith('IEM_MC_END');
689 off = sLine.find(';', offEndStmt + len('IEM_MC_END'));
690 assert off > 0, 'sLine="%s"' % (sLine, );
691 return off + 1 if off > 0 else 99999998;
692
693 def generateModifiedInput(self, oOut):
694 """
695 Generates the combined modified input source/header file.
696 Returns success indicator.
697 """
698 #
699 # File header.
700 #
701 oOut.write('\n'.join(self.generateLicenseHeader()));
702
703 #
704 # ASSUMING that g_aoMcBlocks/self.aoThreadedFuncs are in self.aoParsers
705 # order, we iterate aoThreadedFuncs in parallel to the lines from the
706 # parsers and apply modifications where required.
707 #
708 iThreadedFunction = 0;
709 oThreadedFunction = self.getThreadedFunctionByIndex(0);
710 for oParser in self.aoParsers: # IEMAllInstructionsPython.SimpleParser
711 oOut.write("\n\n/* ****** BEGIN %s ******* */\n" % (oParser.sSrcFile,));
712
713 iLine = 0;
714 while iLine < len(oParser.asLines):
715 sLine = oParser.asLines[iLine];
716 iLine += 1; # iBeginLine and iEndLine are 1-based.
717
718 # Can we pass it thru?
719 if ( iLine not in [oThreadedFunction.oMcBlock.iBeginLine, oThreadedFunction.oMcBlock.iEndLine]
720 or oThreadedFunction.oMcBlock.sSrcFile != oParser.sSrcFile):
721 oOut.write(sLine);
722 #
723 # Single MC block. Just extract it and insert the replacement.
724 #
725 elif oThreadedFunction.oMcBlock.iBeginLine != oThreadedFunction.oMcBlock.iEndLine:
726 assert sLine.count('IEM_MC_') == 1;
727 oOut.write(sLine[:oThreadedFunction.oMcBlock.offBeginLine]);
728 sModified = oThreadedFunction.generateInputCode().strip();
729 oOut.write(sModified);
730
731 iLine = oThreadedFunction.oMcBlock.iEndLine;
732 sLine = oParser.asLines[iLine - 1];
733 assert sLine.count('IEM_MC_') == 1;
734 oOut.write(sLine[self.findEndOfMcEndStmt(sLine, oThreadedFunction.oMcBlock.offEndLine) : ]);
735
736 # Advance
737 iThreadedFunction += 1;
738 oThreadedFunction = self.getThreadedFunctionByIndex(iThreadedFunction);
739 #
740 # Macro expansion line that have sublines and may contain multiple MC blocks.
741 #
742 else:
743 offLine = 0;
744 while iLine == oThreadedFunction.oMcBlock.iBeginLine:
745 oOut.write(sLine[offLine : oThreadedFunction.oMcBlock.offBeginLine]);
746
747 sModified = oThreadedFunction.generateInputCode().strip();
748 assert sModified.startswith('IEM_MC_BEGIN'), 'sModified="%s"' % (sModified,);
749 oOut.write(sModified);
750
751 offLine = self.findEndOfMcEndStmt(sLine, oThreadedFunction.oMcBlock.offEndLine);
752
753 # Advance
754 iThreadedFunction += 1;
755 oThreadedFunction = self.getThreadedFunctionByIndex(iThreadedFunction);
756
757 # Last line segment.
758 if offLine < len(sLine):
759 oOut.write(sLine[offLine : ]);
760
761 oOut.write("/* ****** END %s ******* */\n" % (oParser.sSrcFile,));
762
763 return True;
764
765 #
766 # Main
767 #
768
769 def main(self, asArgs):
770 """
771 C-like main function.
772 Returns exit code.
773 """
774
775 #
776 # Parse arguments
777 #
778 sScriptDir = os.path.dirname(__file__);
779 oParser = argparse.ArgumentParser(add_help = False);
780 oParser.add_argument('asInFiles', metavar = 'input.cpp.h', nargs = '*',
781 default = [os.path.join(sScriptDir, asFM[0]) for asFM in iai.g_aasAllInstrFilesAndDefaultMap],
782 help = "Selection of VMMAll/IEMAllInstructions*.cpp.h files to use as input.");
783 oParser.add_argument('--out-funcs-hdr', metavar = 'file-funcs.h', dest = 'sOutFileFuncsHdr', action = 'store',
784 default = '-', help = 'The output header file for the functions.');
785 oParser.add_argument('--out-funcs-cpp', metavar = 'file-funcs.cpp', dest = 'sOutFileFuncsCpp', action = 'store',
786 default = '-', help = 'The output C++ file for the functions.');
787 oParser.add_argument('--out-mod-input', metavar = 'file-instr.cpp.h', dest = 'sOutFileModInput', action = 'store',
788 default = '-', help = 'The output C++/header file for the modified input instruction files.');
789 oParser.add_argument('--help', '-h', '-?', action = 'help', help = 'Display help and exit.');
790 oParser.add_argument('--version', '-V', action = 'version',
791 version = 'r%s (IEMAllThreadedPython.py), r%s (IEMAllInstructionsPython.py)'
792 % (__version__.split()[1], iai.__version__.split()[1],),
793 help = 'Displays the version/revision of the script and exit.');
794 self.oOptions = oParser.parse_args(asArgs[1:]);
795 print("oOptions=%s" % (self.oOptions,));
796
797 #
798 # Process the instructions specified in the IEM sources.
799 #
800 if self.processInputFiles():
801 #
802 # Generate the output files.
803 #
804 aaoOutputFiles = (
805 ( self.oOptions.sOutFileFuncsHdr, self.generateThreadedFunctionsHeader ),
806 ( self.oOptions.sOutFileFuncsCpp, self.generateThreadedFunctionsSource ),
807 ( self.oOptions.sOutFileModInput, self.generateModifiedInput ),
808 );
809 fRc = True;
810 for sOutFile, fnGenMethod in aaoOutputFiles:
811 if sOutFile == '-':
812 fRc = fnGenMethod(sys.stdout) and fRc;
813 else:
814 try:
815 oOut = open(sOutFile, 'w'); # pylint: disable=consider-using-with,unspecified-encoding
816 except Exception as oXcpt:
817 print('error! Failed open "%s" for writing: %s' % (sOutFile, oXcpt,), file = sys.stderr);
818 return 1;
819 fRc = fnGenMethod(oOut) and fRc;
820 oOut.close();
821 if fRc:
822 return 0;
823
824 return 1;
825
826
827if __name__ == '__main__':
828 sys.exit(IEMThreadedGenerator().main(sys.argv));
829
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