VirtualBox

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

Last change on this file since 99654 was 99647, checked in by vboxsync, 20 months ago

VMM/IEM: Started modifying the input/decode side of the threaded recompiler. 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: 60.4 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3# $Id: IEMAllThreadedPython.py 99647 2023-05-06 00:42:59Z 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: 99647 $"
35
36# Standard python imports.
37import copy;
38import datetime;
39import os;
40import re;
41import sys;
42import argparse;
43
44import IEMAllInstructionsPython as iai;
45
46
47# Python 3 hacks:
48if sys.version_info[0] >= 3:
49 long = int; # pylint: disable=redefined-builtin,invalid-name
50
51## Number of generic parameters for the thread functions.
52g_kcThreadedParams = 3;
53
54g_kdTypeInfo = {
55 # type name: (cBits, fSigned, C-type )
56 'int8_t': ( 8, True, 'uint8_t', ),
57 'int16_t': ( 16, True, 'int16_t', ),
58 'int32_t': ( 32, True, 'int32_t', ),
59 'int64_t': ( 64, True, 'int64_t', ),
60 'uint4_t': ( 4, False, 'uint8_t', ),
61 'uint8_t': ( 8, False, 'uint8_t', ),
62 'uint16_t': ( 16, False, 'uint16_t', ),
63 'uint32_t': ( 32, False, 'uint32_t', ),
64 'uint64_t': ( 64, False, 'uint64_t', ),
65 'uintptr_t': ( 64, False, 'uintptr_t', ), # ASSUMES 64-bit host pointer size.
66 'bool': ( 1, False, 'bool', ),
67 'IEMMODE': ( 2, False, 'IEMMODE', ),
68};
69
70g_kdIemFieldToType = {
71 # Illegal ones:
72 'offInstrNextByte': ( None, ),
73 'cbInstrBuf': ( None, ),
74 'pbInstrBuf': ( None, ),
75 'uInstrBufPc': ( None, ),
76 'cbInstrBufTotal': ( None, ),
77 'offCurInstrStart': ( None, ),
78 'cbOpcode': ( None, ),
79 'offOpcode': ( None, ),
80 'offModRm': ( None, ),
81 # Okay ones.
82 'fPrefixes': ( 'uint32_t', ),
83 'uRexReg': ( 'uint8_t', ),
84 'uRexB': ( 'uint8_t', ),
85 'uRexIndex': ( 'uint8_t', ),
86 'iEffSeg': ( 'uint8_t', ),
87 'enmEffOpSize': ( 'IEMMODE', ),
88 'enmDefAddrMode': ( 'IEMMODE', ),
89 'enmEffAddrMode': ( 'IEMMODE', ),
90 'enmDefOpSize': ( 'IEMMODE', ),
91 'idxPrefix': ( 'uint8_t', ),
92 'uVex3rdReg': ( 'uint8_t', ),
93 'uVexLength': ( 'uint8_t', ),
94 'fEvexStuff': ( 'uint8_t', ),
95 'uFpuOpcode': ( 'uint16_t', ),
96};
97
98class ThreadedParamRef(object):
99 """
100 A parameter reference for a threaded function.
101 """
102
103 def __init__(self, sOrgRef, sType, oStmt, iParam = None, offParam = 0, sStdRef = None):
104 ## The name / reference in the original code.
105 self.sOrgRef = sOrgRef;
106 ## Normalized name to deal with spaces in macro invocations and such.
107 self.sStdRef = sStdRef if sStdRef else ''.join(sOrgRef.split());
108 ## Indicates that sOrgRef may not match the parameter.
109 self.fCustomRef = sStdRef is not None;
110 ## The type (typically derived).
111 self.sType = sType;
112 ## The statement making the reference.
113 self.oStmt = oStmt;
114 ## The parameter containing the references. None if implicit.
115 self.iParam = iParam;
116 ## The offset in the parameter of the reference.
117 self.offParam = offParam;
118
119 ## The variable name in the threaded function.
120 self.sNewName = 'x';
121 ## The this is packed into.
122 self.iNewParam = 99;
123 ## The bit offset in iNewParam.
124 self.offNewParam = 1024
125
126
127class ThreadedFunctionVariation(object):
128 """ Threaded function variation. """
129
130 ## @name Variations.
131 ## These variations will match translation block selection/distinctions as well.
132 ## @note Effective operand size is generally handled in the decoder, at present
133 ## we only do variations on addressing and memory accessing.
134 ## @todo Blocks without addressing should have 64-bit and 32-bit PC update
135 ## variations to reduce code size (see iemRegAddToRip).
136 ## @{
137 ksVariation_Default = ''; ##< No variations.
138 ksVariation_Addr16 = '_Addr16'; ##< 16-bit addressing mode.
139 ksVariation_Addr32 = '_Addr32'; ##< 32-bit addressing mode.
140 ksVariation_Addr32Flat = '_Addr32Flat'; ##< 32-bit addressing mode with CS, DS, ES and SS flat and 4GB wide.
141 ksVariation_Addr64 = '_Addr64'; ##< 64-bit addressing mode.
142 ksVariation_Addr64_32 = '_Addr6432'; ##< 32-bit addressing in 64-bit mode.
143 kasVariations_EffAddr = (
144 ksVariation_Addr16, ksVariation_Addr32, ksVariation_Addr32Flat, ksVariation_Addr64, ksVariation_Addr64_32
145 );
146 ## @}
147
148 def __init__(self, oThreadedFunction, sVariation = ksVariation_Default):
149 self.oParent = oThreadedFunction # type: ThreadedFunction
150 ##< ksVariation_Xxxx.
151 self.sVariation = sVariation
152
153 ## Threaded function parameter references.
154 self.aoParamRefs = [] # type: list(ThreadedParamRef)
155 ## Unique parameter references.
156 self.dParamRefs = {} # type: dict(str,list(ThreadedParamRef))
157 ## Minimum number of parameters to the threaded function.
158 self.cMinParams = 0;
159
160 ## List/tree of statements for the threaded function.
161 self.aoStmtsForThreadedFunction = [] # type: list(McStmt)
162
163 def getIndexName(self):
164 sName = self.oParent.oMcBlock.sFunction;
165 if sName.startswith('iemOp_'):
166 sName = sName[len('iemOp_'):];
167 if self.oParent.oMcBlock.iInFunction == 0:
168 return 'kIemThreadedFunc_%s%s' % ( sName, self.sVariation, );
169 return 'kIemThreadedFunc_%s_%s%s' % ( sName, self.oParent.oMcBlock.iInFunction, self.sVariation, );
170
171 def getFunctionName(self):
172 sName = self.oParent.oMcBlock.sFunction;
173 if sName.startswith('iemOp_'):
174 sName = sName[len('iemOp_'):];
175 if self.oParent.oMcBlock.iInFunction == 0:
176 return 'iemThreadedFunc_%s%s' % ( sName, self.sVariation, );
177 return 'iemThreadedFunc_%s_%s%s' % ( sName, self.oParent.oMcBlock.iInFunction, self.sVariation, );
178
179 #
180 # Analysis and code morphing.
181 #
182
183 def raiseProblem(self, sMessage):
184 """ Raises a problem. """
185 self.oParent.raiseProblem(sMessage);
186
187 def analyzeReferenceToType(self, sRef):
188 """
189 Translates a variable or structure reference to a type.
190 Returns type name.
191 Raises exception if unable to figure it out.
192 """
193 ch0 = sRef[0];
194 if ch0 == 'u':
195 if sRef.startswith('u32'):
196 return 'uint32_t';
197 if sRef.startswith('u8') or sRef == 'uReg':
198 return 'uint8_t';
199 if sRef.startswith('u64'):
200 return 'uint64_t';
201 if sRef.startswith('u16'):
202 return 'uint16_t';
203 elif ch0 == 'b':
204 return 'uint8_t';
205 elif ch0 == 'f':
206 return 'bool';
207 elif ch0 == 'i':
208 if sRef.startswith('i8'):
209 return 'int8_t';
210 if sRef.startswith('i16'):
211 return 'int32_t';
212 if sRef.startswith('i32'):
213 return 'int32_t';
214 if sRef.startswith('i64'):
215 return 'int64_t';
216 if sRef in ('iReg', 'iGReg', 'iSegReg', 'iSrcReg', 'iDstReg'):
217 return 'uint8_t';
218 elif ch0 == 'p':
219 if sRef.find('-') < 0:
220 return 'uintptr_t';
221 if sRef.startswith('pVCpu->iem.s.'):
222 sField = sRef[len('pVCpu->iem.s.') : ];
223 if sField in g_kdIemFieldToType:
224 if g_kdIemFieldToType[sField][0]:
225 return g_kdIemFieldToType[sField][0];
226 self.raiseProblem('Reference out-of-bounds decoder field: %s' % (sRef,));
227 elif ch0 == 'G' and sRef.startswith('GCPtr'):
228 return 'uint64_t';
229 elif ch0 == 'e':
230 if sRef == 'enmEffOpSize':
231 return 'IEMMODE';
232 elif sRef == 'cShift': ## @todo risky
233 return 'uint8_t';
234
235 self.raiseProblem('Unknown reference: %s' % (sRef,));
236 return None; # Shut up pylint 2.16.2.
237
238 def analyzeCallToType(self, sFnRef):
239 """
240 Determins the type of an indirect function call.
241 """
242 assert sFnRef[0] == 'p';
243
244 #
245 # Simple?
246 #
247 if sFnRef.find('-') < 0:
248 oDecoderFunction = self.oParent.oMcBlock.oFunction;
249
250 # Try the argument list of the function defintion macro invocation first.
251 iArg = 2;
252 while iArg < len(oDecoderFunction.asDefArgs):
253 if sFnRef == oDecoderFunction.asDefArgs[iArg]:
254 return oDecoderFunction.asDefArgs[iArg - 1];
255 iArg += 1;
256
257 # Then check out line that includes the word and looks like a variable declaration.
258 oRe = re.compile(' +(P[A-Z0-9_]+|const +IEMOP[A-Z0-9_]+ *[*]) +(const |) *' + sFnRef + ' *(;|=)');
259 for sLine in oDecoderFunction.asLines:
260 oMatch = oRe.match(sLine);
261 if oMatch:
262 if not oMatch.group(1).startswith('const'):
263 return oMatch.group(1);
264 return 'PC' + oMatch.group(1)[len('const ') : -1].strip();
265
266 #
267 # Deal with the pImpl->pfnXxx:
268 #
269 elif sFnRef.startswith('pImpl->pfn'):
270 sMember = sFnRef[len('pImpl->') : ];
271 sBaseType = self.analyzeCallToType('pImpl');
272 offBits = sMember.rfind('U') + 1;
273 if sBaseType == 'PCIEMOPBINSIZES': return 'PFNIEMAIMPLBINU' + sMember[offBits:];
274 if sBaseType == 'PCIEMOPUNARYSIZES': return 'PFNIEMAIMPLUNARYU' + sMember[offBits:];
275 if sBaseType == 'PCIEMOPSHIFTSIZES': return 'PFNIEMAIMPLSHIFTU' + sMember[offBits:];
276 if sBaseType == 'PCIEMOPSHIFTDBLSIZES': return 'PFNIEMAIMPLSHIFTDBLU' + sMember[offBits:];
277 if sBaseType == 'PCIEMOPMULDIVSIZES': return 'PFNIEMAIMPLMULDIVU' + sMember[offBits:];
278 if sBaseType == 'PCIEMOPMEDIAF3': return 'PFNIEMAIMPLMEDIAF3U' + sMember[offBits:];
279 if sBaseType == 'PCIEMOPMEDIAOPTF3': return 'PFNIEMAIMPLMEDIAOPTF3U' + sMember[offBits:];
280 if sBaseType == 'PCIEMOPMEDIAOPTF2': return 'PFNIEMAIMPLMEDIAOPTF2U' + sMember[offBits:];
281 if sBaseType == 'PCIEMOPMEDIAOPTF3IMM8': return 'PFNIEMAIMPLMEDIAOPTF3U' + sMember[offBits:] + 'IMM8';
282 if sBaseType == 'PCIEMOPBLENDOP': return 'PFNIEMAIMPLAVXBLENDU' + sMember[offBits:];
283
284 self.raiseProblem('Unknown call reference: %s::%s (%s)' % (sBaseType, sMember, sFnRef,));
285
286 self.raiseProblem('Unknown call reference: %s' % (sFnRef,));
287 return None; # Shut up pylint 2.16.2.
288
289 def analyze8BitGRegStmt(self, oStmt):
290 """
291 Gets the 8-bit general purpose register access details of the given statement.
292 ASSUMES the statement is one accessing an 8-bit GREG.
293 """
294 idxReg = 0;
295 if ( oStmt.sName.find('_FETCH_') > 0
296 or oStmt.sName.find('_REF_') > 0
297 or oStmt.sName.find('_TO_LOCAL') > 0):
298 idxReg = 1;
299
300 sRegRef = oStmt.asParams[idxReg];
301 sOrgExpr = '((%s) < 4 || (pVCpu->iem.s.fPrefixes & IEM_OP_PRF_REX) ? (%s) : (%s) | 16)' % (sRegRef, sRegRef, sRegRef);
302
303 if sRegRef.find('IEM_GET_MODRM_RM') > 0: sStdRef = 'bRmRm8Ex';
304 elif sRegRef.find('IEM_GET_MODRM_REG') > 0: sStdRef = 'bRmReg8Ex';
305 else: sStdRef = 'bOther8Ex';
306
307 return (idxReg, sOrgExpr, sStdRef);
308
309
310 def analyzeMorphStmtForThreaded(self, aoStmts, iParamRef = 0):
311 """
312 Transforms (copy) the statements into those for the threaded function.
313
314 Returns list/tree of statements (aoStmts is not modified) and the new
315 iParamRef value.
316 """
317 #
318 # We'll be traversing aoParamRefs in parallel to the statements, so we
319 # must match the traversal in analyzeFindThreadedParamRefs exactly.
320 #
321 #print('McBlock at %s:%s' % (os.path.split(self.oMcBlock.sSrcFile)[1], self.oMcBlock.iBeginLine,));
322 aoThreadedStmts = [];
323 for oStmt in aoStmts:
324 # Skip C++ statements that is purely related to decoding.
325 if not oStmt.isCppStmt() or not oStmt.fDecode:
326 # Copy the statement. Make a deep copy to make sure we've got our own
327 # copies of all instance variables, even if a bit overkill at the moment.
328 oNewStmt = copy.deepcopy(oStmt);
329 aoThreadedStmts.append(oNewStmt);
330 #print('oNewStmt %s %s' % (oNewStmt.sName, len(oNewStmt.asParams),));
331
332 # If the statement has parameter references, process the relevant parameters.
333 # We grab the references relevant to this statement and apply them in reserve order.
334 if iParamRef < len(self.aoParamRefs) and self.aoParamRefs[iParamRef].oStmt == oStmt:
335 iParamRefFirst = iParamRef;
336 while True:
337 iParamRef += 1;
338 if iParamRef >= len(self.aoParamRefs) or self.aoParamRefs[iParamRef].oStmt != oStmt:
339 break;
340
341 #print('iParamRefFirst=%s iParamRef=%s' % (iParamRefFirst, iParamRef));
342 for iCurRef in range(iParamRef - 1, iParamRefFirst - 1, -1):
343 oCurRef = self.aoParamRefs[iCurRef];
344 if oCurRef.iParam is not None:
345 assert oCurRef.oStmt == oStmt;
346 #print('iCurRef=%s iParam=%s sOrgRef=%s' % (iCurRef, oCurRef.iParam, oCurRef.sOrgRef));
347 sSrcParam = oNewStmt.asParams[oCurRef.iParam];
348 assert ( sSrcParam[oCurRef.offParam : oCurRef.offParam + len(oCurRef.sOrgRef)] == oCurRef.sOrgRef
349 or oCurRef.fCustomRef), \
350 'offParam=%s sOrgRef=%s iParam=%s oStmt.sName=%s sSrcParam=%s<eos>' \
351 % (oCurRef.offParam, oCurRef.sOrgRef, oCurRef.iParam, oStmt.sName, sSrcParam);
352 oNewStmt.asParams[oCurRef.iParam] = sSrcParam[0 : oCurRef.offParam] \
353 + oCurRef.sNewName \
354 + sSrcParam[oCurRef.offParam + len(oCurRef.sOrgRef) : ];
355
356 # Morph IEM_MC_CALC_RM_EFF_ADDR into IEM_MC_CALC_RM_EFF_ADDR_THREADED ...
357 if oNewStmt.sName == 'IEM_MC_CALC_RM_EFF_ADDR':
358 assert self.sVariation != self.ksVariation_Default;
359 oNewStmt.sName = 'IEM_MC_CALC_RM_EFF_ADDR_THREADED' + self.sVariation.upper();
360 assert len(oNewStmt.asParams) == 3;
361 if self.sVariation == self.ksVariation_Addr16:
362 oNewStmt.asParams = [
363 oNewStmt.asParams[0], oNewStmt.asParams[1], self.dParamRefs['u16Disp'][0].sNewName,
364 ];
365 elif self.sVariation in (self.ksVariation_Addr32, self.ksVariation_Addr32Flat):
366 oNewStmt.asParams = [
367 oNewStmt.asParams[0], oNewStmt.asParams[1], self.dParamRefs['bSib'][0].sNewName,
368 self.dParamRefs['u32Disp'][0].sNewName,
369 ];
370 else:
371 oNewStmt.asParams = [
372 oNewStmt.asParams[0], self.dParamRefs['bRmEx'][0].sNewName, self.dParamRefs['bSib'][0].sNewName,
373 self.dParamRefs['u32Disp'][0].sNewName, self.dParamRefs['cbInstr'][0].sNewName,
374 ];
375 # ... and IEM_MC_ADVANCE_RIP_AND_FINISH into *_THREADED and maybe *_LM64/_NOT64 ...
376 elif oNewStmt.sName in ('IEM_MC_ADVANCE_RIP_AND_FINISH', 'IEM_MC_REL_JMP_S8_AND_FINISH',
377 'IEM_MC_REL_JMP_S16_AND_FINISH', 'IEM_MC_REL_JMP_S32_AND_FINISH'):
378 oNewStmt.asParams.append(self.dParamRefs['cbInstr'][0].sNewName);
379 if oNewStmt.sName in ('IEM_MC_REL_JMP_S8_AND_FINISH', 'IEM_MC_REL_JMP_S32_AND_FINISH'):
380 oNewStmt.asParams.append(self.dParamRefs['pVCpu->iem.s.enmEffOpSize'][0].sNewName);
381 oNewStmt.sName += '_THREADED';
382 if self.sVariation in (self.ksVariation_Addr64, self.ksVariation_Addr64_32):
383 oNewStmt.sName += '_LM64';
384 elif self.sVariation != self.ksVariation_Default:
385 oNewStmt.sName += '_NOT64';
386
387 # ... and IEM_MC_*_GREG_U8 into *_THREADED w/ reworked index taking REX into account
388 elif oNewStmt.sName.startswith('IEM_MC_') and oNewStmt.sName.find('_GREG_U8') > 0:
389 (idxReg, _, sStdRef) = self.analyze8BitGRegStmt(oNewStmt);
390 oNewStmt.asParams[idxReg] = self.dParamRefs[sStdRef][0].sNewName;
391 oNewStmt.sName += '_THREADED';
392
393 # ... and IEM_MC_CALL_CIMPL_[0-5] into *_THREADED ...
394 elif oNewStmt.sName.startswith('IEM_MC_CALL_CIMPL_'):
395 oNewStmt.sName += '_THREADED';
396 oNewStmt.asParams.insert(0, self.dParamRefs['cbInstr'][0].sNewName);
397
398 # Process branches of conditionals recursively.
399 if isinstance(oStmt, iai.McStmtCond):
400 (oNewStmt.aoIfBranch, iParamRef) = self.analyzeMorphStmtForThreaded(oStmt.aoIfBranch, iParamRef);
401 if oStmt.aoElseBranch:
402 (oNewStmt.aoElseBranch, iParamRef) = self.analyzeMorphStmtForThreaded(oStmt.aoElseBranch, iParamRef);
403
404 return (aoThreadedStmts, iParamRef);
405
406 def analyzeConsolidateThreadedParamRefs(self):
407 """
408 Consolidate threaded function parameter references into a dictionary
409 with lists of the references to each variable/field.
410 """
411 # Gather unique parameters.
412 self.dParamRefs = {};
413 for oRef in self.aoParamRefs:
414 if oRef.sStdRef not in self.dParamRefs:
415 self.dParamRefs[oRef.sStdRef] = [oRef,];
416 else:
417 self.dParamRefs[oRef.sStdRef].append(oRef);
418
419 # Generate names for them for use in the threaded function.
420 dParamNames = {};
421 for sName, aoRefs in self.dParamRefs.items():
422 # Morph the reference expression into a name.
423 if sName.startswith('IEM_GET_MODRM_REG'): sName = 'bModRmRegP';
424 elif sName.startswith('IEM_GET_MODRM_RM'): sName = 'bModRmRmP';
425 elif sName.startswith('IEM_GET_MODRM_REG_8'): sName = 'bModRmReg8P';
426 elif sName.startswith('IEM_GET_MODRM_RM_8'): sName = 'bModRmRm8P';
427 elif sName.startswith('IEM_GET_EFFECTIVE_VVVV'): sName = 'bEffVvvvP';
428 elif sName.find('.') >= 0 or sName.find('->') >= 0:
429 sName = sName[max(sName.rfind('.'), sName.rfind('>')) + 1 : ] + 'P';
430 else:
431 sName += 'P';
432
433 # Ensure it's unique.
434 if sName in dParamNames:
435 for i in range(10):
436 if sName + str(i) not in dParamNames:
437 sName += str(i);
438 break;
439 dParamNames[sName] = True;
440
441 # Update all the references.
442 for oRef in aoRefs:
443 oRef.sNewName = sName;
444
445 # Organize them by size too for the purpose of optimize them.
446 dBySize = {} # type: dict(str,str)
447 for sStdRef, aoRefs in self.dParamRefs.items():
448 if aoRefs[0].sType[0] != 'P':
449 cBits = g_kdTypeInfo[aoRefs[0].sType][0];
450 assert(cBits <= 64);
451 else:
452 cBits = 64;
453
454 if cBits not in dBySize:
455 dBySize[cBits] = [sStdRef,]
456 else:
457 dBySize[cBits].append(sStdRef);
458
459 # Pack the parameters as best as we can, starting with the largest ones
460 # and ASSUMING a 64-bit parameter size.
461 self.cMinParams = 0;
462 offNewParam = 0;
463 for cBits in sorted(dBySize.keys(), reverse = True):
464 for sStdRef in dBySize[cBits]:
465 if offNewParam == 0 or offNewParam + cBits > 64:
466 self.cMinParams += 1;
467 offNewParam = cBits;
468 else:
469 offNewParam += cBits;
470 assert(offNewParam <= 64);
471
472 for oRef in self.dParamRefs[sStdRef]:
473 oRef.iNewParam = self.cMinParams - 1;
474 oRef.offNewParam = offNewParam - cBits;
475
476 # Currently there are a few that requires 4 parameters, list these so we can figure out why:
477 if self.cMinParams >= 4:
478 print('debug: cMinParams=%s cRawParams=%s - %s:%d'
479 % (self.cMinParams, len(self.dParamRefs), self.oParent.oMcBlock.sSrcFile, self.oParent.oMcBlock.iBeginLine,));
480
481 return True;
482
483 ksHexDigits = '0123456789abcdefABCDEF';
484
485 def analyzeFindThreadedParamRefs(self, aoStmts):
486 """
487 Scans the statements for things that have to passed on to the threaded
488 function (populates self.aoParamRefs).
489 """
490 for oStmt in aoStmts:
491 # Some statements we can skip alltogether.
492 if isinstance(oStmt, iai.McCppPreProc):
493 continue;
494 if oStmt.isCppStmt() and oStmt.fDecode:
495 continue;
496
497 if isinstance(oStmt, iai.McStmtVar):
498 if oStmt.sConstValue is None:
499 continue;
500 aiSkipParams = { 0: True, 1: True, 3: True };
501 else:
502 aiSkipParams = {};
503
504 # Several statements have implicit parameters and some have different parameters.
505 if oStmt.sName in ('IEM_MC_ADVANCE_RIP_AND_FINISH', 'IEM_MC_REL_JMP_S8_AND_FINISH', 'IEM_MC_REL_JMP_S16_AND_FINISH',
506 'IEM_MC_REL_JMP_S32_AND_FINISH', 'IEM_MC_CALL_CIMPL_0', 'IEM_MC_CALL_CIMPL_1',
507 'IEM_MC_CALL_CIMPL_2', 'IEM_MC_CALL_CIMPL_3', 'IEM_MC_CALL_CIMPL_4', 'IEM_MC_CALL_CIMPL_5', ):
508 self.aoParamRefs.append(ThreadedParamRef('IEM_GET_INSTR_LEN(pVCpu)', 'uint4_t', oStmt, sStdRef = 'cbInstr'));
509
510 if oStmt.sName in ('IEM_MC_REL_JMP_S8_AND_FINISH', 'IEM_MC_REL_JMP_S32_AND_FINISH'):
511 self.aoParamRefs.append(ThreadedParamRef('pVCpu->iem.s.enmEffOpSize', 'IEMMODE', oStmt));
512
513 if oStmt.sName == 'IEM_MC_CALC_RM_EFF_ADDR':
514 ## @todo figure out how to do this in the input part...
515 if self.sVariation == self.ksVariation_Addr16:
516 self.aoParamRefs.append(ThreadedParamRef('bRm', 'uint8_t', oStmt));
517 self.aoParamRefs.append(ThreadedParamRef('u16Disp', 'uint16_t', oStmt));
518 elif self.sVariation in (self.ksVariation_Addr32, self.ksVariation_Addr32Flat):
519 self.aoParamRefs.append(ThreadedParamRef('bRm', 'uint8_t', oStmt));
520 self.aoParamRefs.append(ThreadedParamRef('bSib', 'uint8_t', oStmt));
521 self.aoParamRefs.append(ThreadedParamRef('u32Disp', 'uint32_t', oStmt));
522 else:
523 assert self.sVariation in (self.ksVariation_Addr64, self.ksVariation_Addr64_32);
524 self.aoParamRefs.append(ThreadedParamRef('bRmEx', 'uint8_t', oStmt));
525 self.aoParamRefs.append(ThreadedParamRef('bSib', 'uint8_t', oStmt));
526 self.aoParamRefs.append(ThreadedParamRef('u32Disp', 'uint32_t', oStmt));
527 self.aoParamRefs.append(ThreadedParamRef('IEM_GET_INSTR_LEN(pVCpu)', 'uint4_t', oStmt, sStdRef = 'cbInstr'));
528 assert len(oStmt.asParams) == 3;
529 assert oStmt.asParams[1].startswith('bRm');
530 aiSkipParams[1] = True; # Skip the bRm parameter
531
532 # 8-bit register accesses needs to have their index argument reworked to take REX into account.
533 if oStmt.sName.startswith('IEM_MC_') and oStmt.sName.find('_GREG_U8') > 0:
534 (idxReg, sOrgRef, sStdRef) = self.analyze8BitGRegStmt(oStmt);
535 self.aoParamRefs.append(ThreadedParamRef(sOrgRef, 'uint4_t', oStmt, idxReg, sStdRef = sStdRef));
536 aiSkipParams[idxReg] = True; # Skip the parameter below.
537
538 # Inspect the target of calls to see if we need to pass down a
539 # function pointer or function table pointer for it to work.
540 if isinstance(oStmt, iai.McStmtCall):
541 if oStmt.sFn[0] == 'p':
542 self.aoParamRefs.append(ThreadedParamRef(oStmt.sFn, self.analyzeCallToType(oStmt.sFn), oStmt, oStmt.idxFn));
543 elif ( oStmt.sFn[0] != 'i'
544 and not oStmt.sFn.startswith('IEMTARGETCPU_EFL_BEHAVIOR_SELECT')
545 and not oStmt.sFn.startswith('IEM_SELECT_HOST_OR_FALLBACK') ):
546 self.raiseProblem('Bogus function name in %s: %s' % (oStmt.sName, oStmt.sFn,));
547 aiSkipParams[oStmt.idxFn] = True;
548
549 # Check all the parameters for bogus references.
550 for iParam, sParam in enumerate(oStmt.asParams):
551 if iParam not in aiSkipParams and sParam not in self.oParent.dVariables:
552 # The parameter may contain a C expression, so we have to try
553 # extract the relevant bits, i.e. variables and fields while
554 # ignoring operators and parentheses.
555 offParam = 0;
556 while offParam < len(sParam):
557 # Is it the start of an C identifier? If so, find the end, but don't stop on field separators (->, .).
558 ch = sParam[offParam];
559 if ch.isalpha() or ch == '_':
560 offStart = offParam;
561 offParam += 1;
562 while offParam < len(sParam):
563 ch = sParam[offParam];
564 if not ch.isalnum() and ch != '_' and ch != '.':
565 if ch != '-' or sParam[offParam + 1] != '>':
566 # Special hack for the 'CTX_SUFF(pVM)' bit in pVCpu->CTX_SUFF(pVM)->xxxx:
567 if ( ch == '('
568 and sParam[offStart : offParam + len('(pVM)->')] == 'pVCpu->CTX_SUFF(pVM)->'):
569 offParam += len('(pVM)->') - 1;
570 else:
571 break;
572 offParam += 1;
573 offParam += 1;
574 sRef = sParam[offStart : offParam];
575
576 # For register references, we pass the full register indexes instead as macros
577 # like IEM_GET_MODRM_REG implicitly references pVCpu->iem.s.uRexReg and the
578 # threaded function will be more efficient if we just pass the register index
579 # as a 4-bit param.
580 if ( sRef.startswith('IEM_GET_MODRM')
581 or sRef.startswith('IEM_GET_EFFECTIVE_VVVV') ):
582 offParam = iai.McBlock.skipSpacesAt(sParam, offParam, len(sParam));
583 if sParam[offParam] != '(':
584 self.raiseProblem('Expected "(" following %s in "%s"' % (sRef, oStmt.renderCode(),));
585 (asMacroParams, offCloseParam) = iai.McBlock.extractParams(sParam, offParam);
586 if asMacroParams is None:
587 self.raiseProblem('Unable to find ")" for %s in "%s"' % (sRef, oStmt.renderCode(),));
588 offParam = offCloseParam + 1;
589 self.aoParamRefs.append(ThreadedParamRef(sParam[offStart : offParam], 'uint8_t',
590 oStmt, iParam, offStart));
591
592 # We can skip known variables.
593 elif sRef in self.oParent.dVariables:
594 pass;
595
596 # Skip certain macro invocations.
597 elif sRef in ('IEM_GET_HOST_CPU_FEATURES',
598 'IEM_GET_GUEST_CPU_FEATURES',
599 'IEM_IS_GUEST_CPU_AMD'):
600 offParam = iai.McBlock.skipSpacesAt(sParam, offParam, len(sParam));
601 if sParam[offParam] != '(':
602 self.raiseProblem('Expected "(" following %s in "%s"' % (sRef, oStmt.renderCode(),));
603 (asMacroParams, offCloseParam) = iai.McBlock.extractParams(sParam, offParam);
604 if asMacroParams is None:
605 self.raiseProblem('Unable to find ")" for %s in "%s"' % (sRef, oStmt.renderCode(),));
606 offParam = offCloseParam + 1;
607
608 # Skip any dereference following it, unless it's a predicate like IEM_IS_GUEST_CPU_AMD.
609 if sRef not in ('IEM_IS_GUEST_CPU_AMD', ):
610 offParam = iai.McBlock.skipSpacesAt(sParam, offParam, len(sParam));
611 if offParam + 2 <= len(sParam) and sParam[offParam : offParam + 2] == '->':
612 offParam = iai.McBlock.skipSpacesAt(sParam, offParam + 2, len(sParam));
613 while offParam < len(sParam) and (sParam[offParam].isalnum() or sParam[offParam] in '_.'):
614 offParam += 1;
615
616 # Skip constants, globals, types (casts), sizeof and macros.
617 elif ( sRef.startswith('IEM_OP_PRF_')
618 or sRef.startswith('IEM_ACCESS_')
619 or sRef.startswith('IEMINT_')
620 or sRef.startswith('X86_GREG_')
621 or sRef.startswith('X86_SREG_')
622 or sRef.startswith('X86_EFL_')
623 or sRef.startswith('X86_FSW_')
624 or sRef.startswith('X86_FCW_')
625 or sRef.startswith('X86_XCPT_')
626 or sRef.startswith('IEMMODE_')
627 or sRef.startswith('g_')
628 or sRef in ( 'int8_t', 'int16_t', 'int32_t',
629 'INT8_C', 'INT16_C', 'INT32_C', 'INT64_C',
630 'UINT8_C', 'UINT16_C', 'UINT32_C', 'UINT64_C',
631 'UINT8_MAX', 'UINT16_MAX', 'UINT32_MAX', 'UINT64_MAX',
632 'INT8_MAX', 'INT16_MAX', 'INT32_MAX', 'INT64_MAX',
633 'INT8_MIN', 'INT16_MIN', 'INT32_MIN', 'INT64_MIN',
634 'sizeof', 'NOREF', 'RT_NOREF', 'IEMMODE_64BIT',
635 'NIL_RTGCPTR' ) ):
636 pass;
637
638 # Skip certain macro invocations.
639 # Any variable (non-field) and decoder fields in IEMCPU will need to be parameterized.
640 elif ( ( '.' not in sRef
641 and '-' not in sRef
642 and sRef not in ('pVCpu', ) )
643 or iai.McBlock.koReIemDecoderVars.search(sRef) is not None):
644 self.aoParamRefs.append(ThreadedParamRef(sRef, self.analyzeReferenceToType(sRef),
645 oStmt, iParam, offStart));
646 # Number.
647 elif ch.isdigit():
648 if ( ch == '0'
649 and offParam + 2 <= len(sParam)
650 and sParam[offParam + 1] in 'xX'
651 and sParam[offParam + 2] in self.ksHexDigits ):
652 offParam += 2;
653 while offParam < len(sParam) and sParam[offParam] in self.ksHexDigits:
654 offParam += 1;
655 else:
656 while offParam < len(sParam) and sParam[offParam].isdigit():
657 offParam += 1;
658 # Whatever else.
659 else:
660 offParam += 1;
661
662 # Traverse the branches of conditionals.
663 if isinstance(oStmt, iai.McStmtCond):
664 self.analyzeFindThreadedParamRefs(oStmt.aoIfBranch);
665 self.analyzeFindThreadedParamRefs(oStmt.aoElseBranch);
666 return True;
667
668 def analyzeVariation(self, aoStmts):
669 """
670 2nd part of the analysis, done on each variation.
671
672 The variations may differ in parameter requirements and will end up with
673 slightly different MC sequences. Thus this is done on each individually.
674
675 Returns dummy True - raises exception on trouble.
676 """
677 # Now scan the code for variables and field references that needs to
678 # be passed to the threaded function because they are related to the
679 # instruction decoding.
680 self.analyzeFindThreadedParamRefs(aoStmts);
681 self.analyzeConsolidateThreadedParamRefs();
682
683 # Morph the statement stream for the block into what we'll be using in the threaded function.
684 (self.aoStmtsForThreadedFunction, iParamRef) = self.analyzeMorphStmtForThreaded(aoStmts);
685 if iParamRef != len(self.aoParamRefs):
686 raise Exception('iParamRef=%s, expected %s!' % (iParamRef, len(self.aoParamRefs),));
687
688 return True;
689
690 def emitThreadedCallStmt(self, cchIndent):
691 """
692 Produces a generic C++ statment that emits a call to the thread function variation.
693 """
694 sCode = ' ' * cchIndent;
695 sCode += 'IEM_MC2_EMIT_CALL_%s(%s' % (self.cMinParams, self.getIndexName(), );
696 for iParam in range(self.cMinParams):
697 asFrags = [];
698 for aoRefs in self.dParamRefs.values():
699 oRef = aoRefs[0];
700 if oRef.iNewParam == iParam:
701 if oRef.offNewParam == 0:
702 asFrags.append('(uint64_t)(' + oRef.sOrgRef + ')');
703 else:
704 asFrags.append('((uint64_t)(%s) << %s)' % (oRef.sOrgRef, oRef.offNewParam));
705 assert asFrags;
706 sCode += ', ' + ' | '.join(asFrags);
707 sCode += ');';
708 return iai.McCppGeneric(sCode);
709
710
711class ThreadedFunction(object):
712 """
713 A threaded function.
714 """
715
716 def __init__(self, oMcBlock):
717 self.oMcBlock = oMcBlock # type: IEMAllInstructionsPython.McBlock
718 ## Variations for this block. There is at least one.
719 self.aoVariations = [] # type: list(ThreadedFunctionVariation)
720 ## Dictionary of local variables (IEM_MC_LOCAL[_CONST]) and call arguments (IEM_MC_ARG*).
721 self.dVariables = {} # type: dict(str,McStmtVar)
722
723 @staticmethod
724 def dummyInstance():
725 """ Gets a dummy instance. """
726 return ThreadedFunction(iai.McBlock('null', 999999999, 999999999,
727 iai.DecoderFunction('null', 999999999, 'nil', ('','')), 999999999));
728
729 def raiseProblem(self, sMessage):
730 """ Raises a problem. """
731 raise Exception('%s:%s: error: %s' % (self.oMcBlock.sSrcFile, self.oMcBlock.iBeginLine, sMessage, ));
732
733 def analyzeFindVariablesAndCallArgs(self, aoStmts):
734 """ Scans the statements for MC variables and call arguments. """
735 for oStmt in aoStmts:
736 if isinstance(oStmt, iai.McStmtVar):
737 if oStmt.sVarName in self.dVariables:
738 raise Exception('Variable %s is defined more than once!' % (oStmt.sVarName,));
739 self.dVariables[oStmt.sVarName] = oStmt.sVarName;
740
741 # There shouldn't be any variables or arguments declared inside if/
742 # else blocks, but scan them too to be on the safe side.
743 if isinstance(oStmt, iai.McStmtCond):
744 cBefore = len(self.dVariables);
745 self.analyzeFindVariablesAndCallArgs(oStmt.aoIfBranch);
746 self.analyzeFindVariablesAndCallArgs(oStmt.aoElseBranch);
747 if len(self.dVariables) != cBefore:
748 raise Exception('Variables/arguments defined in conditional branches!');
749 return True;
750
751 def analyze(self):
752 """
753 Analyzes the code, identifying the number of parameters it requires and such.
754
755 Returns dummy True - raises exception on trouble.
756 """
757
758 # Decode the block into a list/tree of McStmt objects.
759 aoStmts = self.oMcBlock.decode();
760
761 # Scan the statements for local variables and call arguments (self.dVariables).
762 self.analyzeFindVariablesAndCallArgs(aoStmts);
763
764 # Create variations if needed.
765 if iai.McStmt.findStmtByNames(aoStmts, {'IEM_MC_CALC_RM_EFF_ADDR' : True,}):
766 self.aoVariations = [ThreadedFunctionVariation(self, sVar)
767 for sVar in ThreadedFunctionVariation.kasVariations_EffAddr];
768 else:
769 self.aoVariations = [ThreadedFunctionVariation(self),];
770
771 # Continue the analysis on each variation.
772 for oVariation in self.aoVariations:
773 oVariation.analyzeVariation(aoStmts);
774
775 return True;
776
777 def emitThreadedCallStmts(self):
778 """
779 Worker for morphInputCode that returns a list of statements that emits
780 the call to the threaded functions for the block.
781 """
782 # Special case for only default variation:
783 if len(self.aoVariations) == 1:
784 assert self.aoVariations[0].sVariation == ThreadedFunctionVariation.ksVariation_Default;
785 return [self.aoVariations[0].emitThreadedCallStmt(0),];
786
787 # Currently only have variations for address mode.
788 dByVariation = { oVar.sVariation: oVar for oVar in self.aoVariations };
789 aoStmts = [
790 iai.McCppGeneric('switch (pVCpu->iem.s.enmCpuMode | (pVCpu->iem.s.enmEffAddrMode << 2))'),
791 iai.McCppGeneric('{'),
792 ];
793 if ThreadedFunctionVariation.ksVariation_Addr64 in dByVariation:
794 aoStmts.extend([
795 iai.McCppGeneric(' case IEMMODE_64BIT | (IEMMODE_64BIT << 2):'),
796 dByVariation[ThreadedFunctionVariation.ksVariation_Addr64].emitThreadedCallStmt(8),
797 iai.McCppGeneric(' break;'),
798 ]);
799 if ( ThreadedFunctionVariation.ksVariation_Addr32 in dByVariation
800 or ThreadedFunctionVariation.ksVariation_Addr32Flat in dByVariation):
801 aoStmts.append(iai.McCppGeneric(' case IEMMODE_32BIT | (IEMMODE_32BIT << 2):'));
802 if ThreadedFunctionVariation.ksVariation_Addr32Flat in dByVariation:
803 aoStmts.extend([
804 iai.McCppGeneric(' if (false /** @todo */) '),
805 dByVariation[ThreadedFunctionVariation.ksVariation_Addr32Flat].emitThreadedCallStmt(12),
806 ]);
807 aoStmts.extend([
808 iai.McCppGeneric(' case IEMMODE_16BIT | (IEMMODE_32BIT << 2):'),
809 dByVariation[ThreadedFunctionVariation.ksVariation_Addr32].emitThreadedCallStmt(8),
810 iai.McCppGeneric(' break;'),
811 ]);
812 if ThreadedFunctionVariation.ksVariation_Addr16 in dByVariation:
813 aoStmts.extend([
814 iai.McCppGeneric(' case IEMMODE_16BIT | (IEMMODE_16BIT << 2):'),
815 iai.McCppGeneric(' case IEMMODE_32BIT | (IEMMODE_16BIT << 2):'),
816 dByVariation[ThreadedFunctionVariation.ksVariation_Addr16].emitThreadedCallStmt(8),
817 iai.McCppGeneric(' break;'),
818 ]);
819 if ThreadedFunctionVariation.ksVariation_Addr64_32 in dByVariation:
820 aoStmts.extend([
821 iai.McCppGeneric(' case IEMMODE_64BIT | (IEMMODE_32BIT << 2):'),
822 dByVariation[ThreadedFunctionVariation.ksVariation_Addr64_32].emitThreadedCallStmt(8),
823 iai.McCppGeneric(' break;'),
824 ]);
825 aoStmts.extend([
826 iai.McCppGeneric(' IEM_NOT_REACHED_DEFAULT_CASE_RET();'),
827 iai.McCppGeneric('}'),
828 ]);
829
830 return aoStmts;
831
832 def morphInputCode(self, aoStmts, fCallEmitted = False):
833 """
834 Adjusts (& copies) the statements for the input/decoder so it will emit
835 calls to the right threaded functions for each block.
836
837 Returns list/tree of statements (aoStmts is not modified) and updated
838 fCallEmitted status.
839 """
840 #print('McBlock at %s:%s' % (os.path.split(self.oMcBlock.sSrcFile)[1], self.oMcBlock.iBeginLine,));
841 aoDecoderStmts = [];
842 for oStmt in aoStmts:
843 # Copy the statement. Make a deep copy to make sure we've got our own
844 # copies of all instance variables, even if a bit overkill at the moment.
845 oNewStmt = copy.deepcopy(oStmt);
846 aoDecoderStmts.append(oNewStmt);
847 #print('oNewStmt %s %s' % (oNewStmt.sName, len(oNewStmt.asParams),));
848
849 # If we haven't emitted the threaded function call yet, look for
850 # statements which it would naturally follow or preceed.
851 if not fCallEmitted:
852 if not oStmt.isCppStmt():
853 pass;
854 elif ( oStmt.fDecode
855 and ( oStmt.asParams[0].find('IEMOP_HLP_DONE_') >= 0
856 or oStmt.asParams[0].find('IEMOP_HLP_DECODED_') >= 0)):
857 aoDecoderStmts.extend(self.emitThreadedCallStmts());
858 fCallEmitted = True;
859
860 # Process branches of conditionals recursively.
861 if isinstance(oStmt, iai.McStmtCond):
862 (oNewStmt.aoIfBranch, fCallEmitted1) = self.morphInputCode(oStmt.aoIfBranch, fCallEmitted);
863 if oStmt.aoElseBranch:
864 (oNewStmt.aoElseBranch, fCallEmitted2) = self.morphInputCode(oStmt.aoElseBranch, fCallEmitted);
865 else:
866 fCallEmitted2 = False;
867 fCallEmitted = fCallEmitted or (fCallEmitted1 and fCallEmitted2);
868
869 return (aoDecoderStmts, fCallEmitted);
870
871
872 def generateInputCode(self):
873 """
874 Modifies the input code.
875 """
876 assert len(self.oMcBlock.asLines) > 2, "asLines=%s" % (self.oMcBlock.asLines,);
877 cchIndent = (self.oMcBlock.cchIndent + 3) // 4 * 4;
878 return iai.McStmt.renderCodeForList(self.morphInputCode(self.oMcBlock.aoStmts)[0],
879 cchIndent = cchIndent).replace('\n', ' /* gen */\n', 1);
880
881
882class IEMThreadedGenerator(object):
883 """
884 The threaded code generator & annotator.
885 """
886
887 def __init__(self):
888 self.aoThreadedFuncs = [] # type: list(ThreadedFunction)
889 self.oOptions = None # type: argparse.Namespace
890 self.aoParsers = [] # type: list(IEMAllInstructionsPython.SimpleParser)
891
892 #
893 # Processing.
894 #
895
896 def processInputFiles(self):
897 """
898 Process the input files.
899 """
900
901 # Parse the files.
902 self.aoParsers = iai.parseFiles(self.oOptions.asInFiles);
903
904 # Create threaded functions for the MC blocks.
905 self.aoThreadedFuncs = [ThreadedFunction(oMcBlock) for oMcBlock in iai.g_aoMcBlocks];
906
907 # Analyze the threaded functions.
908 dRawParamCounts = {};
909 dMinParamCounts = {};
910 for oThreadedFunction in self.aoThreadedFuncs:
911 oThreadedFunction.analyze();
912 for oVariation in oThreadedFunction.aoVariations:
913 dRawParamCounts[len(oVariation.dParamRefs)] = dRawParamCounts.get(len(oVariation.dParamRefs), 0) + 1;
914 dMinParamCounts[oVariation.cMinParams] = dMinParamCounts.get(oVariation.cMinParams, 0) + 1;
915 print('debug: param count distribution, raw and optimized:', file = sys.stderr);
916 for cCount in sorted({cBits: True for cBits in list(dRawParamCounts.keys()) + list(dMinParamCounts.keys())}.keys()):
917 print('debug: %s params: %4s raw, %4s min'
918 % (cCount, dRawParamCounts.get(cCount, 0), dMinParamCounts.get(cCount, 0)),
919 file = sys.stderr);
920
921 return True;
922
923 #
924 # Output
925 #
926
927 def generateLicenseHeader(self):
928 """
929 Returns the lines for a license header.
930 """
931 return [
932 '/*',
933 ' * Autogenerated by $Id: IEMAllThreadedPython.py 99647 2023-05-06 00:42:59Z vboxsync $ ',
934 ' * Do not edit!',
935 ' */',
936 '',
937 '/*',
938 ' * Copyright (C) 2023-' + str(datetime.date.today().year) + ' Oracle and/or its affiliates.',
939 ' *',
940 ' * This file is part of VirtualBox base platform packages, as',
941 ' * available from https://www.virtualbox.org.',
942 ' *',
943 ' * This program is free software; you can redistribute it and/or',
944 ' * modify it under the terms of the GNU General Public License',
945 ' * as published by the Free Software Foundation, in version 3 of the',
946 ' * License.',
947 ' *',
948 ' * This program is distributed in the hope that it will be useful, but',
949 ' * WITHOUT ANY WARRANTY; without even the implied warranty of',
950 ' * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU',
951 ' * General Public License for more details.',
952 ' *',
953 ' * You should have received a copy of the GNU General Public License',
954 ' * along with this program; if not, see <https://www.gnu.org/licenses>.',
955 ' *',
956 ' * The contents of this file may alternatively be used under the terms',
957 ' * of the Common Development and Distribution License Version 1.0',
958 ' * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included',
959 ' * in the VirtualBox distribution, in which case the provisions of the',
960 ' * CDDL are applicable instead of those of the GPL.',
961 ' *',
962 ' * You may elect to license modified versions of this file under the',
963 ' * terms and conditions of either the GPL or the CDDL or both.',
964 ' *',
965 ' * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0',
966 ' */',
967 '',
968 '',
969 '',
970 ];
971
972
973 def generateThreadedFunctionsHeader(self, oOut):
974 """
975 Generates the threaded functions header file.
976 Returns success indicator.
977 """
978
979 asLines = self.generateLicenseHeader();
980
981 # Generate the threaded function table indexes.
982 asLines += [
983 'typedef enum IEMTHREADEDFUNCS',
984 '{',
985 ' kIemThreadedFunc_Invalid = 0,',
986 ];
987 for oThreadedFunction in self.aoThreadedFuncs:
988 for oVariation in oThreadedFunction.aoVariations:
989 asLines.append(' ' + oVariation.getIndexName() + ',');
990 asLines += [
991 ' kIemThreadedFunc_End',
992 '} IEMTHREADEDFUNCS;',
993 '',
994 ];
995
996 # Prototype the function table.
997 sFnType = 'typedef IEM_DECL_IMPL_TYPE(VBOXSTRICTRC, FNIEMTHREADEDFUNC, (PVMCPU pVCpu';
998 for iParam in range(g_kcThreadedParams):
999 sFnType += ', uint64_t uParam' + str(iParam);
1000 sFnType += '));'
1001
1002 asLines += [
1003 sFnType,
1004 'typedef FNIEMTHREADEDFUNC *PFNIEMTHREADEDFUNC;',
1005 '',
1006 'extern const PFNIEMTHREADEDFUNC g_apfnIemThreadedFunctions[kIemThreadedFunc_End];',
1007 ];
1008
1009 oOut.write('\n'.join(asLines));
1010 return True;
1011
1012 ksBitsToIntMask = {
1013 1: "UINT64_C(0x1)",
1014 2: "UINT64_C(0x3)",
1015 4: "UINT64_C(0xf)",
1016 8: "UINT64_C(0xff)",
1017 16: "UINT64_C(0xffff)",
1018 32: "UINT64_C(0xffffffff)",
1019 };
1020 def generateThreadedFunctionsSource(self, oOut):
1021 """
1022 Generates the threaded functions source file.
1023 Returns success indicator.
1024 """
1025
1026 asLines = self.generateLicenseHeader();
1027 oOut.write('\n'.join(asLines));
1028
1029 # Prepare the fixed bits.
1030 sParamList = '(PVMCPU pVCpu';
1031 for iParam in range(g_kcThreadedParams):
1032 sParamList += ', uint64_t uParam' + str(iParam);
1033 sParamList += '))\n';
1034
1035 #
1036 # Emit the function definitions.
1037 #
1038 for oThreadedFunction in self.aoThreadedFuncs:
1039 oMcBlock = oThreadedFunction.oMcBlock;
1040 for oVariation in oThreadedFunction.aoVariations:
1041 # Function header
1042 oOut.write( '\n'
1043 + '\n'
1044 + '/**\n'
1045 + ' * %s at line %s offset %s in %s%s\n'
1046 % (oMcBlock.sFunction, oMcBlock.iBeginLine, oMcBlock.offBeginLine,
1047 os.path.split(oMcBlock.sSrcFile)[1],
1048 ' (macro expansion)' if oMcBlock.iBeginLine == oMcBlock.iEndLine else '')
1049 + ' */\n'
1050 + 'static IEM_DECL_IMPL_DEF(VBOXSTRICTRC, ' + oVariation.getFunctionName() + ',\n'
1051 + ' ' + sParamList
1052 + '{\n');
1053
1054 aasVars = [];
1055 for aoRefs in oVariation.dParamRefs.values():
1056 oRef = aoRefs[0];
1057 if oRef.sType[0] != 'P':
1058 cBits = g_kdTypeInfo[oRef.sType][0];
1059 sType = g_kdTypeInfo[oRef.sType][2];
1060 else:
1061 cBits = 64;
1062 sType = oRef.sType;
1063
1064 sTypeDecl = sType + ' const';
1065
1066 if cBits == 64:
1067 assert oRef.offNewParam == 0;
1068 if sType == 'uint64_t':
1069 sUnpack = 'uParam%s;' % (oRef.iNewParam,);
1070 else:
1071 sUnpack = '(%s)uParam%s;' % (sType, oRef.iNewParam,);
1072 elif oRef.offNewParam == 0:
1073 sUnpack = '(%s)(uParam%s & %s);' % (sType, oRef.iNewParam, self.ksBitsToIntMask[cBits]);
1074 else:
1075 sUnpack = '(%s)((uParam%s >> %s) & %s);' \
1076 % (sType, oRef.iNewParam, oRef.offNewParam, self.ksBitsToIntMask[cBits]);
1077
1078 sComment = '/* %s - %s ref%s */' % (oRef.sOrgRef, len(aoRefs), 's' if len(aoRefs) != 1 else '',);
1079
1080 aasVars.append([ '%s:%02u' % (oRef.iNewParam, oRef.offNewParam),
1081 sTypeDecl, oRef.sNewName, sUnpack, sComment ]);
1082 acchVars = [0, 0, 0, 0, 0];
1083 for asVar in aasVars:
1084 for iCol, sStr in enumerate(asVar):
1085 acchVars[iCol] = max(acchVars[iCol], len(sStr));
1086 sFmt = ' %%-%ss %%-%ss = %%-%ss %%s\n' % (acchVars[1], acchVars[2], acchVars[3]);
1087 for asVar in sorted(aasVars):
1088 oOut.write(sFmt % (asVar[1], asVar[2], asVar[3], asVar[4],));
1089
1090 # RT_NOREF for unused parameters.
1091 if oVariation.cMinParams < g_kcThreadedParams:
1092 oOut.write(' RT_NOREF('
1093 + ', '.join(['uParam%u' % (i,) for i in range(oVariation.cMinParams, g_kcThreadedParams)])
1094 + ');\n');
1095
1096 # Now for the actual statements.
1097 oOut.write(iai.McStmt.renderCodeForList(oVariation.aoStmtsForThreadedFunction, cchIndent = 4));
1098
1099 oOut.write('}\n');
1100
1101
1102 #
1103 # Emit the function table.
1104 #
1105 oOut.write( '\n'
1106 + '\n'
1107 + '/**\n'
1108 + ' * Function table.\n'
1109 + ' */\n'
1110 + 'const PFNIEMTHREADEDFUNC g_apfnIemThreadedFunctions[kIemThreadedFunc_End] =\n'
1111 + '{\n'
1112 + ' /*Invalid*/ NULL, \n');
1113 iThreadedFunction = 0;
1114 for oThreadedFunction in self.aoThreadedFuncs:
1115 for oVariation in oThreadedFunction.aoVariations:
1116 iThreadedFunction += 1;
1117 oOut.write(' /*%4u*/ %s,\n' % (iThreadedFunction, oVariation.getFunctionName(),));
1118 oOut.write('};\n');
1119
1120 return True;
1121
1122 def getThreadedFunctionByIndex(self, idx):
1123 """
1124 Returns a ThreadedFunction object for the given index. If the index is
1125 out of bounds, a dummy is returned.
1126 """
1127 if idx < len(self.aoThreadedFuncs):
1128 return self.aoThreadedFuncs[idx];
1129 return ThreadedFunction.dummyInstance();
1130
1131 def findEndOfMcEndStmt(self, sLine, offEndStmt):
1132 """
1133 Helper that returns the line offset following the 'IEM_MC_END();'.
1134 """
1135 assert sLine[offEndStmt:].startswith('IEM_MC_END');
1136 off = sLine.find(';', offEndStmt + len('IEM_MC_END'));
1137 assert off > 0, 'sLine="%s"' % (sLine, );
1138 return off + 1 if off > 0 else 99999998;
1139
1140 def generateModifiedInput(self, oOut):
1141 """
1142 Generates the combined modified input source/header file.
1143 Returns success indicator.
1144 """
1145 #
1146 # File header.
1147 #
1148 oOut.write('\n'.join(self.generateLicenseHeader()));
1149
1150 #
1151 # ASSUMING that g_aoMcBlocks/self.aoThreadedFuncs are in self.aoParsers
1152 # order, we iterate aoThreadedFuncs in parallel to the lines from the
1153 # parsers and apply modifications where required.
1154 #
1155 iThreadedFunction = 0;
1156 oThreadedFunction = self.getThreadedFunctionByIndex(0);
1157 for oParser in self.aoParsers: # type: IEMAllInstructionsPython.SimpleParser
1158 oOut.write("\n\n/* ****** BEGIN %s ******* */\n" % (oParser.sSrcFile,));
1159
1160 iLine = 0;
1161 while iLine < len(oParser.asLines):
1162 sLine = oParser.asLines[iLine];
1163 iLine += 1; # iBeginLine and iEndLine are 1-based.
1164
1165 # Can we pass it thru?
1166 if ( iLine not in [oThreadedFunction.oMcBlock.iBeginLine, oThreadedFunction.oMcBlock.iEndLine]
1167 or oThreadedFunction.oMcBlock.sSrcFile != oParser.sSrcFile):
1168 oOut.write(sLine);
1169 #
1170 # Single MC block. Just extract it and insert the replacement.
1171 #
1172 elif oThreadedFunction.oMcBlock.iBeginLine != oThreadedFunction.oMcBlock.iEndLine:
1173 assert sLine.count('IEM_MC_') == 1;
1174 oOut.write(sLine[:oThreadedFunction.oMcBlock.offBeginLine]);
1175 sModified = oThreadedFunction.generateInputCode().strip();
1176 oOut.write(sModified);
1177
1178 iLine = oThreadedFunction.oMcBlock.iEndLine;
1179 sLine = oParser.asLines[iLine - 1];
1180 assert sLine.count('IEM_MC_') == 1;
1181 oOut.write(sLine[self.findEndOfMcEndStmt(sLine, oThreadedFunction.oMcBlock.offEndLine) : ]);
1182
1183 # Advance
1184 iThreadedFunction += 1;
1185 oThreadedFunction = self.getThreadedFunctionByIndex(iThreadedFunction);
1186 #
1187 # Macro expansion line that have sublines and may contain multiple MC blocks.
1188 #
1189 else:
1190 offLine = 0;
1191 while iLine == oThreadedFunction.oMcBlock.iBeginLine:
1192 oOut.write(sLine[offLine : oThreadedFunction.oMcBlock.offBeginLine]);
1193
1194 sModified = oThreadedFunction.generateInputCode().strip();
1195 assert sModified.startswith('IEM_MC_BEGIN'), 'sModified="%s"' % (sModified,);
1196 oOut.write(sModified);
1197
1198 offLine = self.findEndOfMcEndStmt(sLine, oThreadedFunction.oMcBlock.offEndLine);
1199
1200 # Advance
1201 iThreadedFunction += 1;
1202 oThreadedFunction = self.getThreadedFunctionByIndex(iThreadedFunction);
1203
1204 # Last line segment.
1205 if offLine < len(sLine):
1206 oOut.write(sLine[offLine : ]);
1207
1208 oOut.write("/* ****** END %s ******* */\n" % (oParser.sSrcFile,));
1209
1210 return True;
1211
1212 #
1213 # Main
1214 #
1215
1216 def main(self, asArgs):
1217 """
1218 C-like main function.
1219 Returns exit code.
1220 """
1221
1222 #
1223 # Parse arguments
1224 #
1225 sScriptDir = os.path.dirname(__file__);
1226 oParser = argparse.ArgumentParser(add_help = False);
1227 oParser.add_argument('asInFiles', metavar = 'input.cpp.h', nargs = '*',
1228 default = [os.path.join(sScriptDir, asFM[0]) for asFM in iai.g_aasAllInstrFilesAndDefaultMap],
1229 help = "Selection of VMMAll/IEMAllInstructions*.cpp.h files to use as input.");
1230 oParser.add_argument('--out-funcs-hdr', metavar = 'file-funcs.h', dest = 'sOutFileFuncsHdr', action = 'store',
1231 default = '-', help = 'The output header file for the functions.');
1232 oParser.add_argument('--out-funcs-cpp', metavar = 'file-funcs.cpp', dest = 'sOutFileFuncsCpp', action = 'store',
1233 default = '-', help = 'The output C++ file for the functions.');
1234 oParser.add_argument('--out-mod-input', metavar = 'file-instr.cpp.h', dest = 'sOutFileModInput', action = 'store',
1235 default = '-', help = 'The output C++/header file for the modified input instruction files.');
1236 oParser.add_argument('--help', '-h', '-?', action = 'help', help = 'Display help and exit.');
1237 oParser.add_argument('--version', '-V', action = 'version',
1238 version = 'r%s (IEMAllThreadedPython.py), r%s (IEMAllInstructionsPython.py)'
1239 % (__version__.split()[1], iai.__version__.split()[1],),
1240 help = 'Displays the version/revision of the script and exit.');
1241 self.oOptions = oParser.parse_args(asArgs[1:]);
1242 print("oOptions=%s" % (self.oOptions,));
1243
1244 #
1245 # Process the instructions specified in the IEM sources.
1246 #
1247 if self.processInputFiles():
1248 #
1249 # Generate the output files.
1250 #
1251 aaoOutputFiles = (
1252 ( self.oOptions.sOutFileFuncsHdr, self.generateThreadedFunctionsHeader ),
1253 ( self.oOptions.sOutFileFuncsCpp, self.generateThreadedFunctionsSource ),
1254 ( self.oOptions.sOutFileModInput, self.generateModifiedInput ),
1255 );
1256 fRc = True;
1257 for sOutFile, fnGenMethod in aaoOutputFiles:
1258 if sOutFile == '-':
1259 fRc = fnGenMethod(sys.stdout) and fRc;
1260 else:
1261 try:
1262 oOut = open(sOutFile, 'w'); # pylint: disable=consider-using-with,unspecified-encoding
1263 except Exception as oXcpt:
1264 print('error! Failed open "%s" for writing: %s' % (sOutFile, oXcpt,), file = sys.stderr);
1265 return 1;
1266 fRc = fnGenMethod(oOut) and fRc;
1267 oOut.close();
1268 if fRc:
1269 return 0;
1270
1271 return 1;
1272
1273
1274if __name__ == '__main__':
1275 sys.exit(IEMThreadedGenerator().main(sys.argv));
1276
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