VirtualBox

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

Last change on this file since 100088 was 100088, checked in by vboxsync, 18 months ago

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