VirtualBox

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

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