VirtualBox

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

Last change on this file since 99296 was 99296, checked in by vboxsync, 21 months ago

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