VirtualBox

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

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

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