VirtualBox

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

Last change on this file since 99359 was 99359, checked in by vboxsync, 2 years ago

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette