VirtualBox

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

Last change on this file since 99748 was 99686, checked in by vboxsync, 20 months ago

VMM/IEM: More work on the decoder/recompiler part. 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: 61.0 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3# $Id: IEMAllThreadedPython.py 99686 2023-05-08 22:44:25Z 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: 99686 $"
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('(uint16_t)uEffAddrInfo' ,
518 'uint16_t', oStmt, sStdRef = 'u16Disp'));
519 elif self.sVariation in (self.ksVariation_Addr32, self.ksVariation_Addr32Flat):
520 self.aoParamRefs.append(ThreadedParamRef('bRm', 'uint8_t', oStmt));
521 self.aoParamRefs.append(ThreadedParamRef('(uint8_t)(uEffAddrInfo >> 32)',
522 'uint8_t', oStmt, sStdRef = 'bSib'));
523 self.aoParamRefs.append(ThreadedParamRef('(uint32_t)uEffAddrInfo',
524 'uint32_t', oStmt, sStdRef = 'u32Disp'));
525 else:
526 assert self.sVariation in (self.ksVariation_Addr64, self.ksVariation_Addr64_32);
527 self.aoParamRefs.append(ThreadedParamRef('IEM_GET_MODRM_EX(pVCpu, bRm)',
528 'uint8_t', oStmt, sStdRef = 'bRmEx'));
529 self.aoParamRefs.append(ThreadedParamRef('(uint8_t)(uEffAddrInfo >> 32)',
530 'uint8_t', oStmt, sStdRef = 'bSib'));
531 self.aoParamRefs.append(ThreadedParamRef('(uint32_t)uEffAddrInfo',
532 'uint32_t', oStmt, sStdRef = 'u32Disp'));
533 self.aoParamRefs.append(ThreadedParamRef('IEM_GET_INSTR_LEN(pVCpu)', 'uint4_t', oStmt, sStdRef = 'cbInstr'));
534 assert len(oStmt.asParams) == 3;
535 assert oStmt.asParams[1].startswith('bRm');
536 aiSkipParams[1] = True; # Skip the bRm parameter
537
538 # 8-bit register accesses needs to have their index argument reworked to take REX into account.
539 if oStmt.sName.startswith('IEM_MC_') and oStmt.sName.find('_GREG_U8') > 0:
540 (idxReg, sOrgRef, sStdRef) = self.analyze8BitGRegStmt(oStmt);
541 self.aoParamRefs.append(ThreadedParamRef(sOrgRef, 'uint4_t', oStmt, idxReg, sStdRef = sStdRef));
542 aiSkipParams[idxReg] = True; # Skip the parameter below.
543
544 # Inspect the target of calls to see if we need to pass down a
545 # function pointer or function table pointer for it to work.
546 if isinstance(oStmt, iai.McStmtCall):
547 if oStmt.sFn[0] == 'p':
548 self.aoParamRefs.append(ThreadedParamRef(oStmt.sFn, self.analyzeCallToType(oStmt.sFn), oStmt, oStmt.idxFn));
549 elif ( oStmt.sFn[0] != 'i'
550 and not oStmt.sFn.startswith('IEMTARGETCPU_EFL_BEHAVIOR_SELECT')
551 and not oStmt.sFn.startswith('IEM_SELECT_HOST_OR_FALLBACK') ):
552 self.raiseProblem('Bogus function name in %s: %s' % (oStmt.sName, oStmt.sFn,));
553 aiSkipParams[oStmt.idxFn] = True;
554
555 # Check all the parameters for bogus references.
556 for iParam, sParam in enumerate(oStmt.asParams):
557 if iParam not in aiSkipParams and sParam not in self.oParent.dVariables:
558 # The parameter may contain a C expression, so we have to try
559 # extract the relevant bits, i.e. variables and fields while
560 # ignoring operators and parentheses.
561 offParam = 0;
562 while offParam < len(sParam):
563 # Is it the start of an C identifier? If so, find the end, but don't stop on field separators (->, .).
564 ch = sParam[offParam];
565 if ch.isalpha() or ch == '_':
566 offStart = offParam;
567 offParam += 1;
568 while offParam < len(sParam):
569 ch = sParam[offParam];
570 if not ch.isalnum() and ch != '_' and ch != '.':
571 if ch != '-' or sParam[offParam + 1] != '>':
572 # Special hack for the 'CTX_SUFF(pVM)' bit in pVCpu->CTX_SUFF(pVM)->xxxx:
573 if ( ch == '('
574 and sParam[offStart : offParam + len('(pVM)->')] == 'pVCpu->CTX_SUFF(pVM)->'):
575 offParam += len('(pVM)->') - 1;
576 else:
577 break;
578 offParam += 1;
579 offParam += 1;
580 sRef = sParam[offStart : offParam];
581
582 # For register references, we pass the full register indexes instead as macros
583 # like IEM_GET_MODRM_REG implicitly references pVCpu->iem.s.uRexReg and the
584 # threaded function will be more efficient if we just pass the register index
585 # as a 4-bit param.
586 if ( sRef.startswith('IEM_GET_MODRM')
587 or sRef.startswith('IEM_GET_EFFECTIVE_VVVV') ):
588 offParam = iai.McBlock.skipSpacesAt(sParam, offParam, len(sParam));
589 if sParam[offParam] != '(':
590 self.raiseProblem('Expected "(" following %s in "%s"' % (sRef, oStmt.renderCode(),));
591 (asMacroParams, offCloseParam) = iai.McBlock.extractParams(sParam, offParam);
592 if asMacroParams is None:
593 self.raiseProblem('Unable to find ")" for %s in "%s"' % (sRef, oStmt.renderCode(),));
594 offParam = offCloseParam + 1;
595 self.aoParamRefs.append(ThreadedParamRef(sParam[offStart : offParam], 'uint8_t',
596 oStmt, iParam, offStart));
597
598 # We can skip known variables.
599 elif sRef in self.oParent.dVariables:
600 pass;
601
602 # Skip certain macro invocations.
603 elif sRef in ('IEM_GET_HOST_CPU_FEATURES',
604 'IEM_GET_GUEST_CPU_FEATURES',
605 'IEM_IS_GUEST_CPU_AMD'):
606 offParam = iai.McBlock.skipSpacesAt(sParam, offParam, len(sParam));
607 if sParam[offParam] != '(':
608 self.raiseProblem('Expected "(" following %s in "%s"' % (sRef, oStmt.renderCode(),));
609 (asMacroParams, offCloseParam) = iai.McBlock.extractParams(sParam, offParam);
610 if asMacroParams is None:
611 self.raiseProblem('Unable to find ")" for %s in "%s"' % (sRef, oStmt.renderCode(),));
612 offParam = offCloseParam + 1;
613
614 # Skip any dereference following it, unless it's a predicate like IEM_IS_GUEST_CPU_AMD.
615 if sRef not in ('IEM_IS_GUEST_CPU_AMD', ):
616 offParam = iai.McBlock.skipSpacesAt(sParam, offParam, len(sParam));
617 if offParam + 2 <= len(sParam) and sParam[offParam : offParam + 2] == '->':
618 offParam = iai.McBlock.skipSpacesAt(sParam, offParam + 2, len(sParam));
619 while offParam < len(sParam) and (sParam[offParam].isalnum() or sParam[offParam] in '_.'):
620 offParam += 1;
621
622 # Skip constants, globals, types (casts), sizeof and macros.
623 elif ( sRef.startswith('IEM_OP_PRF_')
624 or sRef.startswith('IEM_ACCESS_')
625 or sRef.startswith('IEMINT_')
626 or sRef.startswith('X86_GREG_')
627 or sRef.startswith('X86_SREG_')
628 or sRef.startswith('X86_EFL_')
629 or sRef.startswith('X86_FSW_')
630 or sRef.startswith('X86_FCW_')
631 or sRef.startswith('X86_XCPT_')
632 or sRef.startswith('IEMMODE_')
633 or sRef.startswith('g_')
634 or sRef in ( 'int8_t', 'int16_t', 'int32_t',
635 'INT8_C', 'INT16_C', 'INT32_C', 'INT64_C',
636 'UINT8_C', 'UINT16_C', 'UINT32_C', 'UINT64_C',
637 'UINT8_MAX', 'UINT16_MAX', 'UINT32_MAX', 'UINT64_MAX',
638 'INT8_MAX', 'INT16_MAX', 'INT32_MAX', 'INT64_MAX',
639 'INT8_MIN', 'INT16_MIN', 'INT32_MIN', 'INT64_MIN',
640 'sizeof', 'NOREF', 'RT_NOREF', 'IEMMODE_64BIT',
641 'NIL_RTGCPTR' ) ):
642 pass;
643
644 # Skip certain macro invocations.
645 # Any variable (non-field) and decoder fields in IEMCPU will need to be parameterized.
646 elif ( ( '.' not in sRef
647 and '-' not in sRef
648 and sRef not in ('pVCpu', ) )
649 or iai.McBlock.koReIemDecoderVars.search(sRef) is not None):
650 self.aoParamRefs.append(ThreadedParamRef(sRef, self.analyzeReferenceToType(sRef),
651 oStmt, iParam, offStart));
652 # Number.
653 elif ch.isdigit():
654 if ( ch == '0'
655 and offParam + 2 <= len(sParam)
656 and sParam[offParam + 1] in 'xX'
657 and sParam[offParam + 2] in self.ksHexDigits ):
658 offParam += 2;
659 while offParam < len(sParam) and sParam[offParam] in self.ksHexDigits:
660 offParam += 1;
661 else:
662 while offParam < len(sParam) and sParam[offParam].isdigit():
663 offParam += 1;
664 # Whatever else.
665 else:
666 offParam += 1;
667
668 # Traverse the branches of conditionals.
669 if isinstance(oStmt, iai.McStmtCond):
670 self.analyzeFindThreadedParamRefs(oStmt.aoIfBranch);
671 self.analyzeFindThreadedParamRefs(oStmt.aoElseBranch);
672 return True;
673
674 def analyzeVariation(self, aoStmts):
675 """
676 2nd part of the analysis, done on each variation.
677
678 The variations may differ in parameter requirements and will end up with
679 slightly different MC sequences. Thus this is done on each individually.
680
681 Returns dummy True - raises exception on trouble.
682 """
683 # Now scan the code for variables and field references that needs to
684 # be passed to the threaded function because they are related to the
685 # instruction decoding.
686 self.analyzeFindThreadedParamRefs(aoStmts);
687 self.analyzeConsolidateThreadedParamRefs();
688
689 # Morph the statement stream for the block into what we'll be using in the threaded function.
690 (self.aoStmtsForThreadedFunction, iParamRef) = self.analyzeMorphStmtForThreaded(aoStmts);
691 if iParamRef != len(self.aoParamRefs):
692 raise Exception('iParamRef=%s, expected %s!' % (iParamRef, len(self.aoParamRefs),));
693
694 return True;
695
696 def emitThreadedCallStmt(self, cchIndent):
697 """
698 Produces a generic C++ statment that emits a call to the thread function variation.
699 """
700 sCode = ' ' * cchIndent;
701 sCode += 'IEM_MC2_EMIT_CALL_%s(%s' % (self.cMinParams, self.getIndexName(), );
702 for iParam in range(self.cMinParams):
703 asFrags = [];
704 for aoRefs in self.dParamRefs.values():
705 oRef = aoRefs[0];
706 if oRef.iNewParam == iParam:
707 if oRef.offNewParam == 0:
708 asFrags.append('(uint64_t)(' + oRef.sOrgRef + ')');
709 else:
710 asFrags.append('((uint64_t)(%s) << %s)' % (oRef.sOrgRef, oRef.offNewParam));
711 assert asFrags;
712 sCode += ', ' + ' | '.join(asFrags);
713 sCode += ');';
714 return iai.McCppGeneric(sCode);
715
716
717class ThreadedFunction(object):
718 """
719 A threaded function.
720 """
721
722 def __init__(self, oMcBlock):
723 self.oMcBlock = oMcBlock # type: IEMAllInstructionsPython.McBlock
724 ## Variations for this block. There is at least one.
725 self.aoVariations = [] # type: list(ThreadedFunctionVariation)
726 ## Dictionary of local variables (IEM_MC_LOCAL[_CONST]) and call arguments (IEM_MC_ARG*).
727 self.dVariables = {} # type: dict(str,McStmtVar)
728
729 @staticmethod
730 def dummyInstance():
731 """ Gets a dummy instance. """
732 return ThreadedFunction(iai.McBlock('null', 999999999, 999999999,
733 iai.DecoderFunction('null', 999999999, 'nil', ('','')), 999999999));
734
735 def raiseProblem(self, sMessage):
736 """ Raises a problem. """
737 raise Exception('%s:%s: error: %s' % (self.oMcBlock.sSrcFile, self.oMcBlock.iBeginLine, sMessage, ));
738
739 def analyzeFindVariablesAndCallArgs(self, aoStmts):
740 """ Scans the statements for MC variables and call arguments. """
741 for oStmt in aoStmts:
742 if isinstance(oStmt, iai.McStmtVar):
743 if oStmt.sVarName in self.dVariables:
744 raise Exception('Variable %s is defined more than once!' % (oStmt.sVarName,));
745 self.dVariables[oStmt.sVarName] = oStmt.sVarName;
746
747 # There shouldn't be any variables or arguments declared inside if/
748 # else blocks, but scan them too to be on the safe side.
749 if isinstance(oStmt, iai.McStmtCond):
750 cBefore = len(self.dVariables);
751 self.analyzeFindVariablesAndCallArgs(oStmt.aoIfBranch);
752 self.analyzeFindVariablesAndCallArgs(oStmt.aoElseBranch);
753 if len(self.dVariables) != cBefore:
754 raise Exception('Variables/arguments defined in conditional branches!');
755 return True;
756
757 def analyze(self):
758 """
759 Analyzes the code, identifying the number of parameters it requires and such.
760
761 Returns dummy True - raises exception on trouble.
762 """
763
764 # Decode the block into a list/tree of McStmt objects.
765 aoStmts = self.oMcBlock.decode();
766
767 # Scan the statements for local variables and call arguments (self.dVariables).
768 self.analyzeFindVariablesAndCallArgs(aoStmts);
769
770 # Create variations if needed.
771 if iai.McStmt.findStmtByNames(aoStmts, {'IEM_MC_CALC_RM_EFF_ADDR' : True,}):
772 self.aoVariations = [ThreadedFunctionVariation(self, sVar)
773 for sVar in ThreadedFunctionVariation.kasVariations_EffAddr];
774 else:
775 self.aoVariations = [ThreadedFunctionVariation(self),];
776
777 # Continue the analysis on each variation.
778 for oVariation in self.aoVariations:
779 oVariation.analyzeVariation(aoStmts);
780
781 return True;
782
783 def emitThreadedCallStmts(self):
784 """
785 Worker for morphInputCode that returns a list of statements that emits
786 the call to the threaded functions for the block.
787 """
788 # Special case for only default variation:
789 if len(self.aoVariations) == 1:
790 assert self.aoVariations[0].sVariation == ThreadedFunctionVariation.ksVariation_Default;
791 return [self.aoVariations[0].emitThreadedCallStmt(0),];
792
793 # Currently only have variations for address mode.
794 dByVariation = { oVar.sVariation: oVar for oVar in self.aoVariations };
795 aoStmts = [
796 iai.McCppGeneric('switch (pVCpu->iem.s.enmCpuMode | (pVCpu->iem.s.enmEffAddrMode << 2))'),
797 iai.McCppGeneric('{'),
798 ];
799 if ThreadedFunctionVariation.ksVariation_Addr64 in dByVariation:
800 aoStmts.extend([
801 iai.McCppGeneric(' case IEMMODE_64BIT | (IEMMODE_64BIT << 2):'),
802 dByVariation[ThreadedFunctionVariation.ksVariation_Addr64].emitThreadedCallStmt(8),
803 iai.McCppGeneric(' break;'),
804 ]);
805 if ( ThreadedFunctionVariation.ksVariation_Addr32 in dByVariation
806 or ThreadedFunctionVariation.ksVariation_Addr32Flat in dByVariation):
807 aoStmts.append(iai.McCppGeneric(' case IEMMODE_32BIT | (IEMMODE_32BIT << 2):'));
808 if ThreadedFunctionVariation.ksVariation_Addr32Flat in dByVariation:
809 aoStmts.extend([
810 iai.McCppGeneric(' if (false /** @todo */) '),
811 dByVariation[ThreadedFunctionVariation.ksVariation_Addr32Flat].emitThreadedCallStmt(12),
812 ]);
813 aoStmts.extend([
814 iai.McCppGeneric(' case IEMMODE_16BIT | (IEMMODE_32BIT << 2):'),
815 dByVariation[ThreadedFunctionVariation.ksVariation_Addr32].emitThreadedCallStmt(8),
816 iai.McCppGeneric(' break;'),
817 ]);
818 if ThreadedFunctionVariation.ksVariation_Addr16 in dByVariation:
819 aoStmts.extend([
820 iai.McCppGeneric(' case IEMMODE_16BIT | (IEMMODE_16BIT << 2):'),
821 iai.McCppGeneric(' case IEMMODE_32BIT | (IEMMODE_16BIT << 2):'),
822 dByVariation[ThreadedFunctionVariation.ksVariation_Addr16].emitThreadedCallStmt(8),
823 iai.McCppGeneric(' break;'),
824 ]);
825 if ThreadedFunctionVariation.ksVariation_Addr64_32 in dByVariation:
826 aoStmts.extend([
827 iai.McCppGeneric(' case IEMMODE_64BIT | (IEMMODE_32BIT << 2):'),
828 dByVariation[ThreadedFunctionVariation.ksVariation_Addr64_32].emitThreadedCallStmt(8),
829 iai.McCppGeneric(' break;'),
830 ]);
831 aoStmts.extend([
832 iai.McCppGeneric(' IEM_NOT_REACHED_DEFAULT_CASE_RET();'),
833 iai.McCppGeneric('}'),
834 ]);
835
836 return aoStmts;
837
838 def morphInputCode(self, aoStmts, fCallEmitted = False):
839 """
840 Adjusts (& copies) the statements for the input/decoder so it will emit
841 calls to the right threaded functions for each block.
842
843 Returns list/tree of statements (aoStmts is not modified) and updated
844 fCallEmitted status.
845 """
846 #print('McBlock at %s:%s' % (os.path.split(self.oMcBlock.sSrcFile)[1], self.oMcBlock.iBeginLine,));
847 aoDecoderStmts = [];
848 for oStmt in aoStmts:
849 # Copy the statement. Make a deep copy to make sure we've got our own
850 # copies of all instance variables, even if a bit overkill at the moment.
851 oNewStmt = copy.deepcopy(oStmt);
852 aoDecoderStmts.append(oNewStmt);
853 #print('oNewStmt %s %s' % (oNewStmt.sName, len(oNewStmt.asParams),));
854
855 # If we haven't emitted the threaded function call yet, look for
856 # statements which it would naturally follow or preceed.
857 if not fCallEmitted:
858 if not oStmt.isCppStmt():
859 pass;
860 elif ( oStmt.fDecode
861 and ( oStmt.asParams[0].find('IEMOP_HLP_DONE_') >= 0
862 or oStmt.asParams[0].find('IEMOP_HLP_DECODED_') >= 0)):
863 aoDecoderStmts.extend(self.emitThreadedCallStmts());
864 fCallEmitted = True;
865
866 # Process branches of conditionals recursively.
867 if isinstance(oStmt, iai.McStmtCond):
868 (oNewStmt.aoIfBranch, fCallEmitted1) = self.morphInputCode(oStmt.aoIfBranch, fCallEmitted);
869 if oStmt.aoElseBranch:
870 (oNewStmt.aoElseBranch, fCallEmitted2) = self.morphInputCode(oStmt.aoElseBranch, fCallEmitted);
871 else:
872 fCallEmitted2 = False;
873 fCallEmitted = fCallEmitted or (fCallEmitted1 and fCallEmitted2);
874
875 return (aoDecoderStmts, fCallEmitted);
876
877
878 def generateInputCode(self):
879 """
880 Modifies the input code.
881 """
882 assert len(self.oMcBlock.asLines) > 2, "asLines=%s" % (self.oMcBlock.asLines,);
883 cchIndent = (self.oMcBlock.cchIndent + 3) // 4 * 4;
884 return iai.McStmt.renderCodeForList(self.morphInputCode(self.oMcBlock.aoStmts)[0],
885 cchIndent = cchIndent).replace('\n', ' /* gen */\n', 1);
886
887
888class IEMThreadedGenerator(object):
889 """
890 The threaded code generator & annotator.
891 """
892
893 def __init__(self):
894 self.aoThreadedFuncs = [] # type: list(ThreadedFunction)
895 self.oOptions = None # type: argparse.Namespace
896 self.aoParsers = [] # type: list(IEMAllInstructionsPython.SimpleParser)
897
898 #
899 # Processing.
900 #
901
902 def processInputFiles(self):
903 """
904 Process the input files.
905 """
906
907 # Parse the files.
908 self.aoParsers = iai.parseFiles(self.oOptions.asInFiles);
909
910 # Create threaded functions for the MC blocks.
911 self.aoThreadedFuncs = [ThreadedFunction(oMcBlock) for oMcBlock in iai.g_aoMcBlocks];
912
913 # Analyze the threaded functions.
914 dRawParamCounts = {};
915 dMinParamCounts = {};
916 for oThreadedFunction in self.aoThreadedFuncs:
917 oThreadedFunction.analyze();
918 for oVariation in oThreadedFunction.aoVariations:
919 dRawParamCounts[len(oVariation.dParamRefs)] = dRawParamCounts.get(len(oVariation.dParamRefs), 0) + 1;
920 dMinParamCounts[oVariation.cMinParams] = dMinParamCounts.get(oVariation.cMinParams, 0) + 1;
921 print('debug: param count distribution, raw and optimized:', file = sys.stderr);
922 for cCount in sorted({cBits: True for cBits in list(dRawParamCounts.keys()) + list(dMinParamCounts.keys())}.keys()):
923 print('debug: %s params: %4s raw, %4s min'
924 % (cCount, dRawParamCounts.get(cCount, 0), dMinParamCounts.get(cCount, 0)),
925 file = sys.stderr);
926
927 return True;
928
929 #
930 # Output
931 #
932
933 def generateLicenseHeader(self):
934 """
935 Returns the lines for a license header.
936 """
937 return [
938 '/*',
939 ' * Autogenerated by $Id: IEMAllThreadedPython.py 99686 2023-05-08 22:44:25Z vboxsync $ ',
940 ' * Do not edit!',
941 ' */',
942 '',
943 '/*',
944 ' * Copyright (C) 2023-' + str(datetime.date.today().year) + ' Oracle and/or its affiliates.',
945 ' *',
946 ' * This file is part of VirtualBox base platform packages, as',
947 ' * available from https://www.virtualbox.org.',
948 ' *',
949 ' * This program is free software; you can redistribute it and/or',
950 ' * modify it under the terms of the GNU General Public License',
951 ' * as published by the Free Software Foundation, in version 3 of the',
952 ' * License.',
953 ' *',
954 ' * This program is distributed in the hope that it will be useful, but',
955 ' * WITHOUT ANY WARRANTY; without even the implied warranty of',
956 ' * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU',
957 ' * General Public License for more details.',
958 ' *',
959 ' * You should have received a copy of the GNU General Public License',
960 ' * along with this program; if not, see <https://www.gnu.org/licenses>.',
961 ' *',
962 ' * The contents of this file may alternatively be used under the terms',
963 ' * of the Common Development and Distribution License Version 1.0',
964 ' * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included',
965 ' * in the VirtualBox distribution, in which case the provisions of the',
966 ' * CDDL are applicable instead of those of the GPL.',
967 ' *',
968 ' * You may elect to license modified versions of this file under the',
969 ' * terms and conditions of either the GPL or the CDDL or both.',
970 ' *',
971 ' * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0',
972 ' */',
973 '',
974 '',
975 '',
976 ];
977
978
979 def generateThreadedFunctionsHeader(self, oOut):
980 """
981 Generates the threaded functions header file.
982 Returns success indicator.
983 """
984
985 asLines = self.generateLicenseHeader();
986
987 # Generate the threaded function table indexes.
988 asLines += [
989 'typedef enum IEMTHREADEDFUNCS',
990 '{',
991 ' kIemThreadedFunc_Invalid = 0,',
992 ];
993 for oThreadedFunction in self.aoThreadedFuncs:
994 for oVariation in oThreadedFunction.aoVariations:
995 asLines.append(' ' + oVariation.getIndexName() + ',');
996 asLines += [
997 ' kIemThreadedFunc_End',
998 '} IEMTHREADEDFUNCS;',
999 '',
1000 ];
1001
1002 # Prototype the function table.
1003 sFnType = 'typedef IEM_DECL_IMPL_TYPE(VBOXSTRICTRC, FNIEMTHREADEDFUNC, (PVMCPU pVCpu';
1004 for iParam in range(g_kcThreadedParams):
1005 sFnType += ', uint64_t uParam' + str(iParam);
1006 sFnType += '));'
1007
1008 asLines += [
1009 sFnType,
1010 'typedef FNIEMTHREADEDFUNC *PFNIEMTHREADEDFUNC;',
1011 '',
1012 'extern const PFNIEMTHREADEDFUNC g_apfnIemThreadedFunctions[kIemThreadedFunc_End];',
1013 ];
1014
1015 oOut.write('\n'.join(asLines));
1016 return True;
1017
1018 ksBitsToIntMask = {
1019 1: "UINT64_C(0x1)",
1020 2: "UINT64_C(0x3)",
1021 4: "UINT64_C(0xf)",
1022 8: "UINT64_C(0xff)",
1023 16: "UINT64_C(0xffff)",
1024 32: "UINT64_C(0xffffffff)",
1025 };
1026 def generateThreadedFunctionsSource(self, oOut):
1027 """
1028 Generates the threaded functions source file.
1029 Returns success indicator.
1030 """
1031
1032 asLines = self.generateLicenseHeader();
1033 oOut.write('\n'.join(asLines));
1034
1035 # Prepare the fixed bits.
1036 sParamList = '(PVMCPU pVCpu';
1037 for iParam in range(g_kcThreadedParams):
1038 sParamList += ', uint64_t uParam' + str(iParam);
1039 sParamList += '))\n';
1040
1041 #
1042 # Emit the function definitions.
1043 #
1044 for oThreadedFunction in self.aoThreadedFuncs:
1045 oMcBlock = oThreadedFunction.oMcBlock;
1046 for oVariation in oThreadedFunction.aoVariations:
1047 # Function header
1048 oOut.write( '\n'
1049 + '\n'
1050 + '/**\n'
1051 + ' * %s at line %s offset %s in %s%s\n'
1052 % (oMcBlock.sFunction, oMcBlock.iBeginLine, oMcBlock.offBeginLine,
1053 os.path.split(oMcBlock.sSrcFile)[1],
1054 ' (macro expansion)' if oMcBlock.iBeginLine == oMcBlock.iEndLine else '')
1055 + ' */\n'
1056 + 'static IEM_DECL_IMPL_DEF(VBOXSTRICTRC, ' + oVariation.getFunctionName() + ',\n'
1057 + ' ' + sParamList
1058 + '{\n');
1059
1060 aasVars = [];
1061 for aoRefs in oVariation.dParamRefs.values():
1062 oRef = aoRefs[0];
1063 if oRef.sType[0] != 'P':
1064 cBits = g_kdTypeInfo[oRef.sType][0];
1065 sType = g_kdTypeInfo[oRef.sType][2];
1066 else:
1067 cBits = 64;
1068 sType = oRef.sType;
1069
1070 sTypeDecl = sType + ' const';
1071
1072 if cBits == 64:
1073 assert oRef.offNewParam == 0;
1074 if sType == 'uint64_t':
1075 sUnpack = 'uParam%s;' % (oRef.iNewParam,);
1076 else:
1077 sUnpack = '(%s)uParam%s;' % (sType, oRef.iNewParam,);
1078 elif oRef.offNewParam == 0:
1079 sUnpack = '(%s)(uParam%s & %s);' % (sType, oRef.iNewParam, self.ksBitsToIntMask[cBits]);
1080 else:
1081 sUnpack = '(%s)((uParam%s >> %s) & %s);' \
1082 % (sType, oRef.iNewParam, oRef.offNewParam, self.ksBitsToIntMask[cBits]);
1083
1084 sComment = '/* %s - %s ref%s */' % (oRef.sOrgRef, len(aoRefs), 's' if len(aoRefs) != 1 else '',);
1085
1086 aasVars.append([ '%s:%02u' % (oRef.iNewParam, oRef.offNewParam),
1087 sTypeDecl, oRef.sNewName, sUnpack, sComment ]);
1088 acchVars = [0, 0, 0, 0, 0];
1089 for asVar in aasVars:
1090 for iCol, sStr in enumerate(asVar):
1091 acchVars[iCol] = max(acchVars[iCol], len(sStr));
1092 sFmt = ' %%-%ss %%-%ss = %%-%ss %%s\n' % (acchVars[1], acchVars[2], acchVars[3]);
1093 for asVar in sorted(aasVars):
1094 oOut.write(sFmt % (asVar[1], asVar[2], asVar[3], asVar[4],));
1095
1096 # RT_NOREF for unused parameters.
1097 if oVariation.cMinParams < g_kcThreadedParams:
1098 oOut.write(' RT_NOREF('
1099 + ', '.join(['uParam%u' % (i,) for i in range(oVariation.cMinParams, g_kcThreadedParams)])
1100 + ');\n');
1101
1102 # Now for the actual statements.
1103 oOut.write(iai.McStmt.renderCodeForList(oVariation.aoStmtsForThreadedFunction, cchIndent = 4));
1104
1105 oOut.write('}\n');
1106
1107
1108 #
1109 # Emit the function table.
1110 #
1111 oOut.write( '\n'
1112 + '\n'
1113 + '/**\n'
1114 + ' * Function table.\n'
1115 + ' */\n'
1116 + 'const PFNIEMTHREADEDFUNC g_apfnIemThreadedFunctions[kIemThreadedFunc_End] =\n'
1117 + '{\n'
1118 + ' /*Invalid*/ NULL, \n');
1119 iThreadedFunction = 0;
1120 for oThreadedFunction in self.aoThreadedFuncs:
1121 for oVariation in oThreadedFunction.aoVariations:
1122 iThreadedFunction += 1;
1123 oOut.write(' /*%4u*/ %s,\n' % (iThreadedFunction, oVariation.getFunctionName(),));
1124 oOut.write('};\n');
1125
1126 return True;
1127
1128 def getThreadedFunctionByIndex(self, idx):
1129 """
1130 Returns a ThreadedFunction object for the given index. If the index is
1131 out of bounds, a dummy is returned.
1132 """
1133 if idx < len(self.aoThreadedFuncs):
1134 return self.aoThreadedFuncs[idx];
1135 return ThreadedFunction.dummyInstance();
1136
1137 def findEndOfMcEndStmt(self, sLine, offEndStmt):
1138 """
1139 Helper that returns the line offset following the 'IEM_MC_END();'.
1140 """
1141 assert sLine[offEndStmt:].startswith('IEM_MC_END');
1142 off = sLine.find(';', offEndStmt + len('IEM_MC_END'));
1143 assert off > 0, 'sLine="%s"' % (sLine, );
1144 return off + 1 if off > 0 else 99999998;
1145
1146 def generateModifiedInput(self, oOut):
1147 """
1148 Generates the combined modified input source/header file.
1149 Returns success indicator.
1150 """
1151 #
1152 # File header.
1153 #
1154 oOut.write('\n'.join(self.generateLicenseHeader()));
1155
1156 #
1157 # ASSUMING that g_aoMcBlocks/self.aoThreadedFuncs are in self.aoParsers
1158 # order, we iterate aoThreadedFuncs in parallel to the lines from the
1159 # parsers and apply modifications where required.
1160 #
1161 iThreadedFunction = 0;
1162 oThreadedFunction = self.getThreadedFunctionByIndex(0);
1163 for oParser in self.aoParsers: # type: IEMAllInstructionsPython.SimpleParser
1164 oOut.write("\n\n/* ****** BEGIN %s ******* */\n" % (oParser.sSrcFile,));
1165
1166 iLine = 0;
1167 while iLine < len(oParser.asLines):
1168 sLine = oParser.asLines[iLine];
1169 iLine += 1; # iBeginLine and iEndLine are 1-based.
1170
1171 # Can we pass it thru?
1172 if ( iLine not in [oThreadedFunction.oMcBlock.iBeginLine, oThreadedFunction.oMcBlock.iEndLine]
1173 or oThreadedFunction.oMcBlock.sSrcFile != oParser.sSrcFile):
1174 oOut.write(sLine);
1175 #
1176 # Single MC block. Just extract it and insert the replacement.
1177 #
1178 elif oThreadedFunction.oMcBlock.iBeginLine != oThreadedFunction.oMcBlock.iEndLine:
1179 assert sLine.count('IEM_MC_') == 1;
1180 oOut.write(sLine[:oThreadedFunction.oMcBlock.offBeginLine]);
1181 sModified = oThreadedFunction.generateInputCode().strip();
1182 oOut.write(sModified);
1183
1184 iLine = oThreadedFunction.oMcBlock.iEndLine;
1185 sLine = oParser.asLines[iLine - 1];
1186 assert sLine.count('IEM_MC_') == 1;
1187 oOut.write(sLine[self.findEndOfMcEndStmt(sLine, oThreadedFunction.oMcBlock.offEndLine) : ]);
1188
1189 # Advance
1190 iThreadedFunction += 1;
1191 oThreadedFunction = self.getThreadedFunctionByIndex(iThreadedFunction);
1192 #
1193 # Macro expansion line that have sublines and may contain multiple MC blocks.
1194 #
1195 else:
1196 offLine = 0;
1197 while iLine == oThreadedFunction.oMcBlock.iBeginLine:
1198 oOut.write(sLine[offLine : oThreadedFunction.oMcBlock.offBeginLine]);
1199
1200 sModified = oThreadedFunction.generateInputCode().strip();
1201 assert sModified.startswith('IEM_MC_BEGIN'), 'sModified="%s"' % (sModified,);
1202 oOut.write(sModified);
1203
1204 offLine = self.findEndOfMcEndStmt(sLine, oThreadedFunction.oMcBlock.offEndLine);
1205
1206 # Advance
1207 iThreadedFunction += 1;
1208 oThreadedFunction = self.getThreadedFunctionByIndex(iThreadedFunction);
1209
1210 # Last line segment.
1211 if offLine < len(sLine):
1212 oOut.write(sLine[offLine : ]);
1213
1214 oOut.write("/* ****** END %s ******* */\n" % (oParser.sSrcFile,));
1215
1216 return True;
1217
1218 #
1219 # Main
1220 #
1221
1222 def main(self, asArgs):
1223 """
1224 C-like main function.
1225 Returns exit code.
1226 """
1227
1228 #
1229 # Parse arguments
1230 #
1231 sScriptDir = os.path.dirname(__file__);
1232 oParser = argparse.ArgumentParser(add_help = False);
1233 oParser.add_argument('asInFiles', metavar = 'input.cpp.h', nargs = '*',
1234 default = [os.path.join(sScriptDir, asFM[0]) for asFM in iai.g_aasAllInstrFilesAndDefaultMap],
1235 help = "Selection of VMMAll/IEMAllInstructions*.cpp.h files to use as input.");
1236 oParser.add_argument('--out-funcs-hdr', metavar = 'file-funcs.h', dest = 'sOutFileFuncsHdr', action = 'store',
1237 default = '-', help = 'The output header file for the functions.');
1238 oParser.add_argument('--out-funcs-cpp', metavar = 'file-funcs.cpp', dest = 'sOutFileFuncsCpp', action = 'store',
1239 default = '-', help = 'The output C++ file for the functions.');
1240 oParser.add_argument('--out-mod-input', metavar = 'file-instr.cpp.h', dest = 'sOutFileModInput', action = 'store',
1241 default = '-', help = 'The output C++/header file for the modified input instruction files.');
1242 oParser.add_argument('--help', '-h', '-?', action = 'help', help = 'Display help and exit.');
1243 oParser.add_argument('--version', '-V', action = 'version',
1244 version = 'r%s (IEMAllThreadedPython.py), r%s (IEMAllInstructionsPython.py)'
1245 % (__version__.split()[1], iai.__version__.split()[1],),
1246 help = 'Displays the version/revision of the script and exit.');
1247 self.oOptions = oParser.parse_args(asArgs[1:]);
1248 print("oOptions=%s" % (self.oOptions,));
1249
1250 #
1251 # Process the instructions specified in the IEM sources.
1252 #
1253 if self.processInputFiles():
1254 #
1255 # Generate the output files.
1256 #
1257 aaoOutputFiles = (
1258 ( self.oOptions.sOutFileFuncsHdr, self.generateThreadedFunctionsHeader ),
1259 ( self.oOptions.sOutFileFuncsCpp, self.generateThreadedFunctionsSource ),
1260 ( self.oOptions.sOutFileModInput, self.generateModifiedInput ),
1261 );
1262 fRc = True;
1263 for sOutFile, fnGenMethod in aaoOutputFiles:
1264 if sOutFile == '-':
1265 fRc = fnGenMethod(sys.stdout) and fRc;
1266 else:
1267 try:
1268 oOut = open(sOutFile, 'w'); # pylint: disable=consider-using-with,unspecified-encoding
1269 except Exception as oXcpt:
1270 print('error! Failed open "%s" for writing: %s' % (sOutFile, oXcpt,), file = sys.stderr);
1271 return 1;
1272 fRc = fnGenMethod(oOut) and fRc;
1273 oOut.close();
1274 if fRc:
1275 return 0;
1276
1277 return 1;
1278
1279
1280if __name__ == '__main__':
1281 sys.exit(IEMThreadedGenerator().main(sys.argv));
1282
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