VirtualBox

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

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

VMM/IEM: More recompilation code. 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: 73.7 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3# $Id: IEMAllThreadedPython.py 100223 2023-06-20 02:53:04Z 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: 100223 $"
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 - only used by IEM_MC_DEFER_TO_CIMPL_X_RET.
138 ksVariation_16 = '_16'; ##< 16-bit mode code (386+).
139 ksVariation_16_Addr32 = '_16_Addr32'; ##< 16-bit mode code (386+), address size prefixed to 32-bit addressing.
140 ksVariation_16_Pre386 = '_16_Pre386'; ##< 16-bit mode code, pre-386 CPU target.
141 ksVariation_32 = '_32'; ##< 32-bit mode code (386+).
142 ksVariation_32_Flat = '_32_Flat'; ##< 32-bit mode code (386+) with CS, DS, E,S and SS flat and 4GB wide.
143 ksVariation_32_Addr16 = '_32_Addr16'; ##< 32-bit mode code (386+), address size prefixed to 16-bit addressing.
144 ksVariation_64 = '_64'; ##< 64-bit mode code.
145 ksVariation_64_Addr32 = '_64_Addr32'; ##< 64-bit mode code, address size prefixed to 32-bit addressing.
146 kasVariations = (
147 ksVariation_Default,
148 ksVariation_16,
149 ksVariation_16_Addr32,
150 ksVariation_16_Pre386,
151 ksVariation_32,
152 ksVariation_32_Flat,
153 ksVariation_32_Addr16,
154 ksVariation_64,
155 ksVariation_64_Addr32,
156 );
157 kasVariationsWithoutAddress = (
158 ksVariation_16,
159 ksVariation_16_Pre386,
160 ksVariation_32,
161 ksVariation_64,
162 );
163 kasVariationsWithAddress = (
164 ksVariation_16,
165 ksVariation_16_Addr32,
166 ksVariation_16_Pre386,
167 ksVariation_32,
168 ksVariation_32_Flat,
169 ksVariation_32_Addr16,
170 ksVariation_64,
171 ksVariation_64_Addr32,
172 );
173 kasVariationsEmitOrder = (
174 ksVariation_Default,
175 ksVariation_64,
176 ksVariation_32_Flat,
177 ksVariation_32,
178 ksVariation_16,
179 ksVariation_16_Addr32,
180 ksVariation_16_Pre386,
181 ksVariation_32_Addr16,
182 ksVariation_64_Addr32,
183 );
184 kdVariationNames = {
185 ksVariation_Default: 'defer-to-cimpl',
186 ksVariation_16: '16-bit',
187 ksVariation_16_Addr32: '16-bit w/ address prefix (Addr32)',
188 ksVariation_16_Pre386: '16-bit on pre-386 CPU',
189 ksVariation_32: '32-bit',
190 ksVariation_32_Flat: '32-bit flat and wide open CS, SS, DS and ES',
191 ksVariation_32_Addr16: '32-bit w/ address prefix (Addr16)',
192 ksVariation_64: '64-bit',
193 ksVariation_64_Addr32: '64-bit w/ address prefix (Addr32)',
194
195 };
196 ## @}
197
198 ## IEM_CIMPL_F_XXX flags that we know.
199 kdCImplFlags = {
200 'IEM_CIMPL_F_MODE': True,
201 'IEM_CIMPL_F_BRANCH': False,
202 'IEM_CIMPL_F_RFLAGS': False,
203 'IEM_CIMPL_F_STATUS_FLAGS': False,
204 'IEM_CIMPL_F_VMEXIT': False,
205 'IEM_CIMPL_F_FPU': False,
206 'IEM_CIMPL_F_REP': False,
207 'IEM_CIMPL_F_END_TB': False,
208 'IEM_CIMPL_F_XCPT': True,
209 };
210
211 def __init__(self, oThreadedFunction, sVariation = ksVariation_Default):
212 self.oParent = oThreadedFunction # type: ThreadedFunction
213 ##< ksVariation_Xxxx.
214 self.sVariation = sVariation
215
216 ## Threaded function parameter references.
217 self.aoParamRefs = [] # type: list(ThreadedParamRef)
218 ## Unique parameter references.
219 self.dParamRefs = {} # type: dict(str,list(ThreadedParamRef))
220 ## Minimum number of parameters to the threaded function.
221 self.cMinParams = 0;
222
223 ## List/tree of statements for the threaded function.
224 self.aoStmtsForThreadedFunction = [] # type: list(McStmt)
225
226 ## Dictionary with any IEM_CIMPL_F_XXX flags associated to the code block.
227 self.dsCImplFlags = { } # type: dict(str, bool)
228
229 ## Function enum number, for verification. Set by generateThreadedFunctionsHeader.
230 self.iEnumValue = -1;
231
232 def getIndexName(self):
233 sName = self.oParent.oMcBlock.sFunction;
234 if sName.startswith('iemOp_'):
235 sName = sName[len('iemOp_'):];
236 if self.oParent.oMcBlock.iInFunction == 0:
237 return 'kIemThreadedFunc_%s%s' % ( sName, self.sVariation, );
238 return 'kIemThreadedFunc_%s_%s%s' % ( sName, self.oParent.oMcBlock.iInFunction, self.sVariation, );
239
240 def getFunctionName(self):
241 sName = self.oParent.oMcBlock.sFunction;
242 if sName.startswith('iemOp_'):
243 sName = sName[len('iemOp_'):];
244 if self.oParent.oMcBlock.iInFunction == 0:
245 return 'iemThreadedFunc_%s%s' % ( sName, self.sVariation, );
246 return 'iemThreadedFunc_%s_%s%s' % ( sName, self.oParent.oMcBlock.iInFunction, self.sVariation, );
247
248 #
249 # Analysis and code morphing.
250 #
251
252 def raiseProblem(self, sMessage):
253 """ Raises a problem. """
254 self.oParent.raiseProblem(sMessage);
255
256 def analyzeReferenceToType(self, sRef):
257 """
258 Translates a variable or structure reference to a type.
259 Returns type name.
260 Raises exception if unable to figure it out.
261 """
262 ch0 = sRef[0];
263 if ch0 == 'u':
264 if sRef.startswith('u32'):
265 return 'uint32_t';
266 if sRef.startswith('u8') or sRef == 'uReg':
267 return 'uint8_t';
268 if sRef.startswith('u64'):
269 return 'uint64_t';
270 if sRef.startswith('u16'):
271 return 'uint16_t';
272 elif ch0 == 'b':
273 return 'uint8_t';
274 elif ch0 == 'f':
275 return 'bool';
276 elif ch0 == 'i':
277 if sRef.startswith('i8'):
278 return 'int8_t';
279 if sRef.startswith('i16'):
280 return 'int16_t';
281 if sRef.startswith('i32'):
282 return 'int32_t';
283 if sRef.startswith('i64'):
284 return 'int64_t';
285 if sRef in ('iReg', 'iGReg', 'iSegReg', 'iSrcReg', 'iDstReg', 'iCrReg'):
286 return 'uint8_t';
287 elif ch0 == 'p':
288 if sRef.find('-') < 0:
289 return 'uintptr_t';
290 if sRef.startswith('pVCpu->iem.s.'):
291 sField = sRef[len('pVCpu->iem.s.') : ];
292 if sField in g_kdIemFieldToType:
293 if g_kdIemFieldToType[sField][0]:
294 return g_kdIemFieldToType[sField][0];
295 elif ch0 == 'G' and sRef.startswith('GCPtr'):
296 return 'uint64_t';
297 elif ch0 == 'e':
298 if sRef == 'enmEffOpSize':
299 return 'IEMMODE';
300 elif ch0 == 'o':
301 if sRef.startswith('off32'):
302 return 'uint32_t';
303 elif sRef == 'cbFrame': # enter
304 return 'uint16_t';
305 elif sRef == 'cShift': ## @todo risky
306 return 'uint8_t';
307
308 self.raiseProblem('Unknown reference: %s' % (sRef,));
309 return None; # Shut up pylint 2.16.2.
310
311 def analyzeCallToType(self, sFnRef):
312 """
313 Determins the type of an indirect function call.
314 """
315 assert sFnRef[0] == 'p';
316
317 #
318 # Simple?
319 #
320 if sFnRef.find('-') < 0:
321 oDecoderFunction = self.oParent.oMcBlock.oFunction;
322
323 # Try the argument list of the function defintion macro invocation first.
324 iArg = 2;
325 while iArg < len(oDecoderFunction.asDefArgs):
326 if sFnRef == oDecoderFunction.asDefArgs[iArg]:
327 return oDecoderFunction.asDefArgs[iArg - 1];
328 iArg += 1;
329
330 # Then check out line that includes the word and looks like a variable declaration.
331 oRe = re.compile(' +(P[A-Z0-9_]+|const +IEMOP[A-Z0-9_]+ *[*]) +(const |) *' + sFnRef + ' *(;|=)');
332 for sLine in oDecoderFunction.asLines:
333 oMatch = oRe.match(sLine);
334 if oMatch:
335 if not oMatch.group(1).startswith('const'):
336 return oMatch.group(1);
337 return 'PC' + oMatch.group(1)[len('const ') : -1].strip();
338
339 #
340 # Deal with the pImpl->pfnXxx:
341 #
342 elif sFnRef.startswith('pImpl->pfn'):
343 sMember = sFnRef[len('pImpl->') : ];
344 sBaseType = self.analyzeCallToType('pImpl');
345 offBits = sMember.rfind('U') + 1;
346 if sBaseType == 'PCIEMOPBINSIZES': return 'PFNIEMAIMPLBINU' + sMember[offBits:];
347 if sBaseType == 'PCIEMOPUNARYSIZES': return 'PFNIEMAIMPLUNARYU' + sMember[offBits:];
348 if sBaseType == 'PCIEMOPSHIFTSIZES': return 'PFNIEMAIMPLSHIFTU' + sMember[offBits:];
349 if sBaseType == 'PCIEMOPSHIFTDBLSIZES': return 'PFNIEMAIMPLSHIFTDBLU' + sMember[offBits:];
350 if sBaseType == 'PCIEMOPMULDIVSIZES': return 'PFNIEMAIMPLMULDIVU' + sMember[offBits:];
351 if sBaseType == 'PCIEMOPMEDIAF3': return 'PFNIEMAIMPLMEDIAF3U' + sMember[offBits:];
352 if sBaseType == 'PCIEMOPMEDIAOPTF3': return 'PFNIEMAIMPLMEDIAOPTF3U' + sMember[offBits:];
353 if sBaseType == 'PCIEMOPMEDIAOPTF2': return 'PFNIEMAIMPLMEDIAOPTF2U' + sMember[offBits:];
354 if sBaseType == 'PCIEMOPMEDIAOPTF3IMM8': return 'PFNIEMAIMPLMEDIAOPTF3U' + sMember[offBits:] + 'IMM8';
355 if sBaseType == 'PCIEMOPBLENDOP': return 'PFNIEMAIMPLAVXBLENDU' + sMember[offBits:];
356
357 self.raiseProblem('Unknown call reference: %s::%s (%s)' % (sBaseType, sMember, sFnRef,));
358
359 self.raiseProblem('Unknown call reference: %s' % (sFnRef,));
360 return None; # Shut up pylint 2.16.2.
361
362 def analyze8BitGRegStmt(self, oStmt):
363 """
364 Gets the 8-bit general purpose register access details of the given statement.
365 ASSUMES the statement is one accessing an 8-bit GREG.
366 """
367 idxReg = 0;
368 if ( oStmt.sName.find('_FETCH_') > 0
369 or oStmt.sName.find('_REF_') > 0
370 or oStmt.sName.find('_TO_LOCAL') > 0):
371 idxReg = 1;
372
373 sRegRef = oStmt.asParams[idxReg];
374 sOrgExpr = '((%s) < 4 || (pVCpu->iem.s.fPrefixes & IEM_OP_PRF_REX) ? (%s) : (%s) | 16)' % (sRegRef, sRegRef, sRegRef);
375
376 if sRegRef.find('IEM_GET_MODRM_RM') > 0: sStdRef = 'bRmRm8Ex';
377 elif sRegRef.find('IEM_GET_MODRM_REG') > 0: sStdRef = 'bRmReg8Ex';
378 else: sStdRef = 'bOther8Ex';
379
380 return (idxReg, sOrgExpr, sStdRef);
381
382
383 def analyzeMorphStmtForThreaded(self, aoStmts, iParamRef = 0):
384 """
385 Transforms (copy) the statements into those for the threaded function.
386
387 Returns list/tree of statements (aoStmts is not modified) and the new
388 iParamRef value.
389 """
390 #
391 # We'll be traversing aoParamRefs in parallel to the statements, so we
392 # must match the traversal in analyzeFindThreadedParamRefs exactly.
393 #
394 #print('McBlock at %s:%s' % (os.path.split(self.oMcBlock.sSrcFile)[1], self.oMcBlock.iBeginLine,));
395 aoThreadedStmts = [];
396 for oStmt in aoStmts:
397 # Skip C++ statements that is purely related to decoding.
398 if not oStmt.isCppStmt() or not oStmt.fDecode:
399 # Copy the statement. Make a deep copy to make sure we've got our own
400 # copies of all instance variables, even if a bit overkill at the moment.
401 oNewStmt = copy.deepcopy(oStmt);
402 aoThreadedStmts.append(oNewStmt);
403 #print('oNewStmt %s %s' % (oNewStmt.sName, len(oNewStmt.asParams),));
404
405 # If the statement has parameter references, process the relevant parameters.
406 # We grab the references relevant to this statement and apply them in reserve order.
407 if iParamRef < len(self.aoParamRefs) and self.aoParamRefs[iParamRef].oStmt == oStmt:
408 iParamRefFirst = iParamRef;
409 while True:
410 iParamRef += 1;
411 if iParamRef >= len(self.aoParamRefs) or self.aoParamRefs[iParamRef].oStmt != oStmt:
412 break;
413
414 #print('iParamRefFirst=%s iParamRef=%s' % (iParamRefFirst, iParamRef));
415 for iCurRef in range(iParamRef - 1, iParamRefFirst - 1, -1):
416 oCurRef = self.aoParamRefs[iCurRef];
417 if oCurRef.iParam is not None:
418 assert oCurRef.oStmt == oStmt;
419 #print('iCurRef=%s iParam=%s sOrgRef=%s' % (iCurRef, oCurRef.iParam, oCurRef.sOrgRef));
420 sSrcParam = oNewStmt.asParams[oCurRef.iParam];
421 assert ( sSrcParam[oCurRef.offParam : oCurRef.offParam + len(oCurRef.sOrgRef)] == oCurRef.sOrgRef
422 or oCurRef.fCustomRef), \
423 'offParam=%s sOrgRef=%s iParam=%s oStmt.sName=%s sSrcParam=%s<eos>' \
424 % (oCurRef.offParam, oCurRef.sOrgRef, oCurRef.iParam, oStmt.sName, sSrcParam);
425 oNewStmt.asParams[oCurRef.iParam] = sSrcParam[0 : oCurRef.offParam] \
426 + oCurRef.sNewName \
427 + sSrcParam[oCurRef.offParam + len(oCurRef.sOrgRef) : ];
428
429 # Morph IEM_MC_CALC_RM_EFF_ADDR into IEM_MC_CALC_RM_EFF_ADDR_THREADED ...
430 if oNewStmt.sName == 'IEM_MC_CALC_RM_EFF_ADDR':
431 assert self.sVariation != self.ksVariation_Default;
432 oNewStmt.sName = 'IEM_MC_CALC_RM_EFF_ADDR_THREADED' + self.sVariation.upper();
433 assert len(oNewStmt.asParams) == 3;
434 if self.sVariation in (self.ksVariation_16, self.ksVariation_16_Pre386, self.ksVariation_32_Addr16):
435 oNewStmt.asParams = [
436 oNewStmt.asParams[0], oNewStmt.asParams[1], self.dParamRefs['u16Disp'][0].sNewName,
437 ];
438 elif self.sVariation in (self.ksVariation_32, self.ksVariation_32_Flat, self.ksVariation_16_Addr32):
439 oNewStmt.asParams = [
440 oNewStmt.asParams[0], oNewStmt.asParams[1], self.dParamRefs['bSib'][0].sNewName,
441 self.dParamRefs['u32Disp'][0].sNewName,
442 ];
443 else:
444 oNewStmt.asParams = [
445 oNewStmt.asParams[0], self.dParamRefs['bRmEx'][0].sNewName, self.dParamRefs['bSib'][0].sNewName,
446 self.dParamRefs['u32Disp'][0].sNewName, self.dParamRefs['cbInstr'][0].sNewName,
447 ];
448 # ... and IEM_MC_ADVANCE_RIP_AND_FINISH into *_THREADED and maybe *_LM64/_NOT64 ...
449 elif oNewStmt.sName in ('IEM_MC_ADVANCE_RIP_AND_FINISH', 'IEM_MC_REL_JMP_S8_AND_FINISH',
450 'IEM_MC_REL_JMP_S16_AND_FINISH', 'IEM_MC_REL_JMP_S32_AND_FINISH'):
451 oNewStmt.asParams.append(self.dParamRefs['cbInstr'][0].sNewName);
452 if ( oNewStmt.sName in ('IEM_MC_REL_JMP_S8_AND_FINISH', )
453 and self.sVariation != self.ksVariation_16_Pre386):
454 oNewStmt.asParams.append(self.dParamRefs['pVCpu->iem.s.enmEffOpSize'][0].sNewName);
455 oNewStmt.sName += '_THREADED';
456 if self.sVariation in (self.ksVariation_64, self.ksVariation_64_Addr32):
457 oNewStmt.sName += '_PC64';
458 elif self.sVariation == self.ksVariation_16_Pre386:
459 oNewStmt.sName += '_PC16';
460 elif self.sVariation != self.ksVariation_Default:
461 oNewStmt.sName += '_PC32';
462
463 # ... and IEM_MC_*_GREG_U8 into *_THREADED w/ reworked index taking REX into account
464 elif oNewStmt.sName.startswith('IEM_MC_') and oNewStmt.sName.find('_GREG_U8') > 0:
465 (idxReg, _, sStdRef) = self.analyze8BitGRegStmt(oNewStmt);
466 oNewStmt.asParams[idxReg] = self.dParamRefs[sStdRef][0].sNewName;
467 oNewStmt.sName += '_THREADED';
468
469 # ... and IEM_MC_CALL_CIMPL_[0-5] and IEM_MC_DEFER_TO_CIMPL_[0-5]_RET into *_THREADED ...
470 elif oNewStmt.sName.startswith('IEM_MC_CALL_CIMPL_') or oNewStmt.sName.startswith('IEM_MC_DEFER_TO_CIMPL_'):
471 oNewStmt.sName += '_THREADED';
472 oNewStmt.asParams.insert(0, self.dParamRefs['cbInstr'][0].sNewName);
473
474 # Process branches of conditionals recursively.
475 if isinstance(oStmt, iai.McStmtCond):
476 (oNewStmt.aoIfBranch, iParamRef) = self.analyzeMorphStmtForThreaded(oStmt.aoIfBranch, iParamRef);
477 if oStmt.aoElseBranch:
478 (oNewStmt.aoElseBranch, iParamRef) = self.analyzeMorphStmtForThreaded(oStmt.aoElseBranch, iParamRef);
479
480 return (aoThreadedStmts, iParamRef);
481
482 def analyzeCodeOperation(self, aoStmts):
483 """
484 Analyzes the code looking clues as to additional side-effects.
485
486 Currently this is simply looking for any IEM_IMPL_C_F_XXX flags and
487 collecting these in self.dsCImplFlags.
488 """
489 for oStmt in aoStmts:
490 if oStmt.sName.startswith('IEM_MC_CALL_CIMPL_') or oStmt.sName.startswith('IEM_MC_DEFER_TO_CIMPL_'):
491 sFlagsSansComments = iai.McBlock.stripComments(oStmt.asParams[0]);
492 for sFlag in sFlagsSansComments.split('|'):
493 sFlag = sFlag.strip();
494 if sFlag != '0':
495 if sFlag in self.kdCImplFlags:
496 self.dsCImplFlags[sFlag] = True;
497 else:
498 self.raiseProblem('Unknown CIMPL flag value: %s' % (sFlag,));
499
500 # Process branches of conditionals recursively.
501 if isinstance(oStmt, iai.McStmtCond):
502 self.analyzeCodeOperation(oStmt.aoIfBranch);
503 if oStmt.aoElseBranch:
504 self.analyzeCodeOperation(oStmt.aoElseBranch);
505
506 return True;
507
508
509 def analyzeConsolidateThreadedParamRefs(self):
510 """
511 Consolidate threaded function parameter references into a dictionary
512 with lists of the references to each variable/field.
513 """
514 # Gather unique parameters.
515 self.dParamRefs = {};
516 for oRef in self.aoParamRefs:
517 if oRef.sStdRef not in self.dParamRefs:
518 self.dParamRefs[oRef.sStdRef] = [oRef,];
519 else:
520 self.dParamRefs[oRef.sStdRef].append(oRef);
521
522 # Generate names for them for use in the threaded function.
523 dParamNames = {};
524 for sName, aoRefs in self.dParamRefs.items():
525 # Morph the reference expression into a name.
526 if sName.startswith('IEM_GET_MODRM_REG'): sName = 'bModRmRegP';
527 elif sName.startswith('IEM_GET_MODRM_RM'): sName = 'bModRmRmP';
528 elif sName.startswith('IEM_GET_MODRM_REG_8'): sName = 'bModRmReg8P';
529 elif sName.startswith('IEM_GET_MODRM_RM_8'): sName = 'bModRmRm8P';
530 elif sName.startswith('IEM_GET_EFFECTIVE_VVVV'): sName = 'bEffVvvvP';
531 elif sName.find('.') >= 0 or sName.find('->') >= 0:
532 sName = sName[max(sName.rfind('.'), sName.rfind('>')) + 1 : ] + 'P';
533 else:
534 sName += 'P';
535
536 # Ensure it's unique.
537 if sName in dParamNames:
538 for i in range(10):
539 if sName + str(i) not in dParamNames:
540 sName += str(i);
541 break;
542 dParamNames[sName] = True;
543
544 # Update all the references.
545 for oRef in aoRefs:
546 oRef.sNewName = sName;
547
548 # Organize them by size too for the purpose of optimize them.
549 dBySize = {} # type: dict(str,str)
550 for sStdRef, aoRefs in self.dParamRefs.items():
551 if aoRefs[0].sType[0] != 'P':
552 cBits = g_kdTypeInfo[aoRefs[0].sType][0];
553 assert(cBits <= 64);
554 else:
555 cBits = 64;
556
557 if cBits not in dBySize:
558 dBySize[cBits] = [sStdRef,]
559 else:
560 dBySize[cBits].append(sStdRef);
561
562 # Pack the parameters as best as we can, starting with the largest ones
563 # and ASSUMING a 64-bit parameter size.
564 self.cMinParams = 0;
565 offNewParam = 0;
566 for cBits in sorted(dBySize.keys(), reverse = True):
567 for sStdRef in dBySize[cBits]:
568 if offNewParam == 0 or offNewParam + cBits > 64:
569 self.cMinParams += 1;
570 offNewParam = cBits;
571 else:
572 offNewParam += cBits;
573 assert(offNewParam <= 64);
574
575 for oRef in self.dParamRefs[sStdRef]:
576 oRef.iNewParam = self.cMinParams - 1;
577 oRef.offNewParam = offNewParam - cBits;
578
579 # Currently there are a few that requires 4 parameters, list these so we can figure out why:
580 if self.cMinParams >= 4:
581 print('debug: cMinParams=%s cRawParams=%s - %s:%d'
582 % (self.cMinParams, len(self.dParamRefs), self.oParent.oMcBlock.sSrcFile, self.oParent.oMcBlock.iBeginLine,));
583
584 return True;
585
586 ksHexDigits = '0123456789abcdefABCDEF';
587
588 def analyzeFindThreadedParamRefs(self, aoStmts): # pylint: disable=too-many-statements
589 """
590 Scans the statements for things that have to passed on to the threaded
591 function (populates self.aoParamRefs).
592 """
593 for oStmt in aoStmts:
594 # Some statements we can skip alltogether.
595 if isinstance(oStmt, iai.McCppPreProc):
596 continue;
597 if oStmt.isCppStmt() and oStmt.fDecode:
598 continue;
599
600 if isinstance(oStmt, iai.McStmtVar):
601 if oStmt.sConstValue is None:
602 continue;
603 aiSkipParams = { 0: True, 1: True, 3: True };
604 else:
605 aiSkipParams = {};
606
607 # Several statements have implicit parameters and some have different parameters.
608 if oStmt.sName in ('IEM_MC_ADVANCE_RIP_AND_FINISH', 'IEM_MC_REL_JMP_S8_AND_FINISH', 'IEM_MC_REL_JMP_S16_AND_FINISH',
609 'IEM_MC_REL_JMP_S32_AND_FINISH', 'IEM_MC_CALL_CIMPL_0', 'IEM_MC_CALL_CIMPL_1',
610 'IEM_MC_CALL_CIMPL_2', 'IEM_MC_CALL_CIMPL_3', 'IEM_MC_CALL_CIMPL_4', 'IEM_MC_CALL_CIMPL_5',
611 'IEM_MC_DEFER_TO_CIMPL_0_RET', 'IEM_MC_DEFER_TO_CIMPL_1_RET', 'IEM_MC_DEFER_TO_CIMPL_2_RET',
612 'IEM_MC_DEFER_TO_CIMPL_3_RET', 'IEM_MC_DEFER_TO_CIMPL_4_RET', 'IEM_MC_DEFER_TO_CIMPL_5_RET', ):
613 self.aoParamRefs.append(ThreadedParamRef('IEM_GET_INSTR_LEN(pVCpu)', 'uint4_t', oStmt, sStdRef = 'cbInstr'));
614
615 if ( oStmt.sName in ('IEM_MC_REL_JMP_S8_AND_FINISH',)
616 and self.sVariation != self.ksVariation_16_Pre386):
617 self.aoParamRefs.append(ThreadedParamRef('pVCpu->iem.s.enmEffOpSize', 'IEMMODE', oStmt));
618
619 if oStmt.sName == 'IEM_MC_CALC_RM_EFF_ADDR':
620 # This is being pretty presumptive about bRm always being the RM byte...
621 if self.sVariation in (self.ksVariation_16, self.ksVariation_16_Pre386, self.ksVariation_32_Addr16):
622 self.aoParamRefs.append(ThreadedParamRef('bRm', 'uint8_t', oStmt));
623 self.aoParamRefs.append(ThreadedParamRef('(uint16_t)uEffAddrInfo' ,
624 'uint16_t', oStmt, sStdRef = 'u16Disp'));
625 elif self.sVariation in (self.ksVariation_32, self.ksVariation_32_Flat, self.ksVariation_16_Addr32):
626 self.aoParamRefs.append(ThreadedParamRef('bRm', 'uint8_t', oStmt));
627 self.aoParamRefs.append(ThreadedParamRef('(uint8_t)(uEffAddrInfo >> 32)',
628 'uint8_t', oStmt, sStdRef = 'bSib'));
629 self.aoParamRefs.append(ThreadedParamRef('(uint32_t)uEffAddrInfo',
630 'uint32_t', oStmt, sStdRef = 'u32Disp'));
631 else:
632 assert self.sVariation in (self.ksVariation_64, self.ksVariation_64_Addr32);
633 self.aoParamRefs.append(ThreadedParamRef('IEM_GET_MODRM_EX(pVCpu, bRm)',
634 'uint8_t', oStmt, sStdRef = 'bRmEx'));
635 self.aoParamRefs.append(ThreadedParamRef('(uint8_t)(uEffAddrInfo >> 32)',
636 'uint8_t', oStmt, sStdRef = 'bSib'));
637 self.aoParamRefs.append(ThreadedParamRef('(uint32_t)uEffAddrInfo',
638 'uint32_t', oStmt, sStdRef = 'u32Disp'));
639 self.aoParamRefs.append(ThreadedParamRef('IEM_GET_INSTR_LEN(pVCpu)',
640 'uint4_t', oStmt, sStdRef = 'cbInstr'));
641 assert len(oStmt.asParams) == 3;
642 assert oStmt.asParams[1].startswith('bRm');
643 aiSkipParams[1] = True; # Skip the bRm parameter
644
645 # 8-bit register accesses needs to have their index argument reworked to take REX into account.
646 if oStmt.sName.startswith('IEM_MC_') and oStmt.sName.find('_GREG_U8') > 0:
647 (idxReg, sOrgRef, sStdRef) = self.analyze8BitGRegStmt(oStmt);
648 self.aoParamRefs.append(ThreadedParamRef(sOrgRef, 'uint4_t', oStmt, idxReg, sStdRef = sStdRef));
649 aiSkipParams[idxReg] = True; # Skip the parameter below.
650
651 # Inspect the target of calls to see if we need to pass down a
652 # function pointer or function table pointer for it to work.
653 if isinstance(oStmt, iai.McStmtCall):
654 if oStmt.sFn[0] == 'p':
655 self.aoParamRefs.append(ThreadedParamRef(oStmt.sFn, self.analyzeCallToType(oStmt.sFn), oStmt, oStmt.idxFn));
656 elif ( oStmt.sFn[0] != 'i'
657 and not oStmt.sFn.startswith('IEMTARGETCPU_EFL_BEHAVIOR_SELECT')
658 and not oStmt.sFn.startswith('IEM_SELECT_HOST_OR_FALLBACK') ):
659 self.raiseProblem('Bogus function name in %s: %s' % (oStmt.sName, oStmt.sFn,));
660 aiSkipParams[oStmt.idxFn] = True;
661
662 # Skip the hint parameter (first) for IEM_MC_CALL_CIMPL_X.
663 if oStmt.sName.startswith('IEM_MC_CALL_CIMPL_'):
664 assert oStmt.idxFn == 1;
665 aiSkipParams[0] = True;
666
667
668 # Check all the parameters for bogus references.
669 for iParam, sParam in enumerate(oStmt.asParams):
670 if iParam not in aiSkipParams and sParam not in self.oParent.dVariables:
671 # The parameter may contain a C expression, so we have to try
672 # extract the relevant bits, i.e. variables and fields while
673 # ignoring operators and parentheses.
674 offParam = 0;
675 while offParam < len(sParam):
676 # Is it the start of an C identifier? If so, find the end, but don't stop on field separators (->, .).
677 ch = sParam[offParam];
678 if ch.isalpha() or ch == '_':
679 offStart = offParam;
680 offParam += 1;
681 while offParam < len(sParam):
682 ch = sParam[offParam];
683 if not ch.isalnum() and ch != '_' and ch != '.':
684 if ch != '-' or sParam[offParam + 1] != '>':
685 # Special hack for the 'CTX_SUFF(pVM)' bit in pVCpu->CTX_SUFF(pVM)->xxxx:
686 if ( ch == '('
687 and sParam[offStart : offParam + len('(pVM)->')] == 'pVCpu->CTX_SUFF(pVM)->'):
688 offParam += len('(pVM)->') - 1;
689 else:
690 break;
691 offParam += 1;
692 offParam += 1;
693 sRef = sParam[offStart : offParam];
694
695 # For register references, we pass the full register indexes instead as macros
696 # like IEM_GET_MODRM_REG implicitly references pVCpu->iem.s.uRexReg and the
697 # threaded function will be more efficient if we just pass the register index
698 # as a 4-bit param.
699 if ( sRef.startswith('IEM_GET_MODRM')
700 or sRef.startswith('IEM_GET_EFFECTIVE_VVVV') ):
701 offParam = iai.McBlock.skipSpacesAt(sParam, offParam, len(sParam));
702 if sParam[offParam] != '(':
703 self.raiseProblem('Expected "(" following %s in "%s"' % (sRef, oStmt.renderCode(),));
704 (asMacroParams, offCloseParam) = iai.McBlock.extractParams(sParam, offParam);
705 if asMacroParams is None:
706 self.raiseProblem('Unable to find ")" for %s in "%s"' % (sRef, oStmt.renderCode(),));
707 offParam = offCloseParam + 1;
708 self.aoParamRefs.append(ThreadedParamRef(sParam[offStart : offParam], 'uint8_t',
709 oStmt, iParam, offStart));
710
711 # We can skip known variables.
712 elif sRef in self.oParent.dVariables:
713 pass;
714
715 # Skip certain macro invocations.
716 elif sRef in ('IEM_GET_HOST_CPU_FEATURES',
717 'IEM_GET_GUEST_CPU_FEATURES',
718 'IEM_IS_GUEST_CPU_AMD',
719 'IEM_IS_16BIT_CODE',
720 'IEM_IS_32BIT_CODE',
721 'IEM_IS_64BIT_CODE',
722 ):
723 offParam = iai.McBlock.skipSpacesAt(sParam, offParam, len(sParam));
724 if sParam[offParam] != '(':
725 self.raiseProblem('Expected "(" following %s in "%s"' % (sRef, oStmt.renderCode(),));
726 (asMacroParams, offCloseParam) = iai.McBlock.extractParams(sParam, offParam);
727 if asMacroParams is None:
728 self.raiseProblem('Unable to find ")" for %s in "%s"' % (sRef, oStmt.renderCode(),));
729 offParam = offCloseParam + 1;
730
731 # Skip any dereference following it, unless it's a predicate like IEM_IS_GUEST_CPU_AMD.
732 if sRef not in ('IEM_IS_GUEST_CPU_AMD',
733 'IEM_IS_16BIT_CODE',
734 'IEM_IS_32BIT_CODE',
735 'IEM_IS_64BIT_CODE',
736 ):
737 offParam = iai.McBlock.skipSpacesAt(sParam, offParam, len(sParam));
738 if offParam + 2 <= len(sParam) and sParam[offParam : offParam + 2] == '->':
739 offParam = iai.McBlock.skipSpacesAt(sParam, offParam + 2, len(sParam));
740 while offParam < len(sParam) and (sParam[offParam].isalnum() or sParam[offParam] in '_.'):
741 offParam += 1;
742
743 # Skip constants, globals, types (casts), sizeof and macros.
744 elif ( sRef.startswith('IEM_OP_PRF_')
745 or sRef.startswith('IEM_ACCESS_')
746 or sRef.startswith('IEMINT_')
747 or sRef.startswith('X86_GREG_')
748 or sRef.startswith('X86_SREG_')
749 or sRef.startswith('X86_EFL_')
750 or sRef.startswith('X86_FSW_')
751 or sRef.startswith('X86_FCW_')
752 or sRef.startswith('X86_XCPT_')
753 or sRef.startswith('IEMMODE_')
754 or sRef.startswith('IEM_F_')
755 or sRef.startswith('IEM_CIMPL_F_')
756 or sRef.startswith('g_')
757 or sRef.startswith('iemAImpl_')
758 or sRef in ( 'int8_t', 'int16_t', 'int32_t',
759 'INT8_C', 'INT16_C', 'INT32_C', 'INT64_C',
760 'UINT8_C', 'UINT16_C', 'UINT32_C', 'UINT64_C',
761 'UINT8_MAX', 'UINT16_MAX', 'UINT32_MAX', 'UINT64_MAX',
762 'INT8_MAX', 'INT16_MAX', 'INT32_MAX', 'INT64_MAX',
763 'INT8_MIN', 'INT16_MIN', 'INT32_MIN', 'INT64_MIN',
764 'sizeof', 'NOREF', 'RT_NOREF', 'IEMMODE_64BIT',
765 'RT_BIT_32', 'true', 'false', 'NIL_RTGCPTR',) ):
766 pass;
767
768 # Skip certain macro invocations.
769 # Any variable (non-field) and decoder fields in IEMCPU will need to be parameterized.
770 elif ( ( '.' not in sRef
771 and '-' not in sRef
772 and sRef not in ('pVCpu', ) )
773 or iai.McBlock.koReIemDecoderVars.search(sRef) is not None):
774 self.aoParamRefs.append(ThreadedParamRef(sRef, self.analyzeReferenceToType(sRef),
775 oStmt, iParam, offStart));
776 # Number.
777 elif ch.isdigit():
778 if ( ch == '0'
779 and offParam + 2 <= len(sParam)
780 and sParam[offParam + 1] in 'xX'
781 and sParam[offParam + 2] in self.ksHexDigits ):
782 offParam += 2;
783 while offParam < len(sParam) and sParam[offParam] in self.ksHexDigits:
784 offParam += 1;
785 else:
786 while offParam < len(sParam) and sParam[offParam].isdigit():
787 offParam += 1;
788 # Comment?
789 elif ( ch == '/'
790 and offParam + 4 <= len(sParam)
791 and sParam[offParam + 1] == '*'):
792 offParam += 2;
793 offNext = sParam.find('*/', offParam);
794 if offNext < offParam:
795 self.raiseProblem('Unable to find "*/" in "%s" ("%s")' % (sRef, oStmt.renderCode(),));
796 offParam = offNext + 2;
797 # Whatever else.
798 else:
799 offParam += 1;
800
801 # Traverse the branches of conditionals.
802 if isinstance(oStmt, iai.McStmtCond):
803 self.analyzeFindThreadedParamRefs(oStmt.aoIfBranch);
804 self.analyzeFindThreadedParamRefs(oStmt.aoElseBranch);
805 return True;
806
807 def analyzeVariation(self, aoStmts):
808 """
809 2nd part of the analysis, done on each variation.
810
811 The variations may differ in parameter requirements and will end up with
812 slightly different MC sequences. Thus this is done on each individually.
813
814 Returns dummy True - raises exception on trouble.
815 """
816 # Now scan the code for variables and field references that needs to
817 # be passed to the threaded function because they are related to the
818 # instruction decoding.
819 self.analyzeFindThreadedParamRefs(aoStmts);
820 self.analyzeConsolidateThreadedParamRefs();
821
822 # Scan the code for IEM_CIMPL_F_ and other clues.
823 self.analyzeCodeOperation(aoStmts);
824
825 # Morph the statement stream for the block into what we'll be using in the threaded function.
826 (self.aoStmtsForThreadedFunction, iParamRef) = self.analyzeMorphStmtForThreaded(aoStmts);
827 if iParamRef != len(self.aoParamRefs):
828 raise Exception('iParamRef=%s, expected %s!' % (iParamRef, len(self.aoParamRefs),));
829
830 return True;
831
832 def emitThreadedCallStmts(self, cchIndent):
833 """
834 Produces generic C++ statments that emits a call to the thread function
835 variation and any subsequent checks that may be necessary after that.
836 """
837 # The call to the threaded function.
838 sCode = 'IEM_MC2_EMIT_CALL_%s(%s' % (self.cMinParams, self.getIndexName(), );
839 for iParam in range(self.cMinParams):
840 asFrags = [];
841 for aoRefs in self.dParamRefs.values():
842 oRef = aoRefs[0];
843 if oRef.iNewParam == iParam:
844 sCast = '(uint64_t)'
845 if oRef.sType in ('int8_t', 'int16_t', 'int32_t'): # Make sure these doesn't get sign-extended.
846 sCast = '(uint64_t)(u' + oRef.sType + ')';
847 if oRef.offNewParam == 0:
848 asFrags.append(sCast + '(' + oRef.sOrgRef + ')');
849 else:
850 asFrags.append('(%s(%s) << %s)' % (sCast, oRef.sOrgRef, oRef.offNewParam));
851 assert asFrags;
852 sCode += ', ' + ' | '.join(asFrags);
853 sCode += ');';
854 aoStmts = [ iai.McCppGeneric(sCode, cchIndent = cchIndent), ];
855
856 # For CIMPL stuff, we need to consult the associated IEM_CIMPL_F_XXX
857 # mask and maybe emit additional checks.
858 if 'IEM_CIMPL_F_MODE' in self.dsCImplFlags or 'IEM_CIMPL_F_XCPT' in self.dsCImplFlags:
859 aoStmts.append(iai.McCppGeneric('IEM_MC2_EMIT_CALL_1(kIemThreadedFunc_CheckMode, pVCpu->iem.s.fExec);',
860 cchIndent = cchIndent));
861
862 return aoStmts;
863
864
865class ThreadedFunction(object):
866 """
867 A threaded function.
868 """
869
870 def __init__(self, oMcBlock):
871 self.oMcBlock = oMcBlock # type: IEMAllInstructionsPython.McBlock
872 ## Variations for this block. There is at least one.
873 self.aoVariations = [] # type: list(ThreadedFunctionVariation)
874 ## Variation dictionary containing the same as aoVariations.
875 self.dVariations = {} # type: dict(str,ThreadedFunctionVariation)
876 ## Dictionary of local variables (IEM_MC_LOCAL[_CONST]) and call arguments (IEM_MC_ARG*).
877 self.dVariables = {} # type: dict(str,McStmtVar)
878
879 @staticmethod
880 def dummyInstance():
881 """ Gets a dummy instance. """
882 return ThreadedFunction(iai.McBlock('null', 999999999, 999999999,
883 iai.DecoderFunction('null', 999999999, 'nil', ('','')), 999999999));
884
885 def raiseProblem(self, sMessage):
886 """ Raises a problem. """
887 raise Exception('%s:%s: error: %s' % (self.oMcBlock.sSrcFile, self.oMcBlock.iBeginLine, sMessage, ));
888
889 def analyzeFindVariablesAndCallArgs(self, aoStmts):
890 """ Scans the statements for MC variables and call arguments. """
891 for oStmt in aoStmts:
892 if isinstance(oStmt, iai.McStmtVar):
893 if oStmt.sVarName in self.dVariables:
894 raise Exception('Variable %s is defined more than once!' % (oStmt.sVarName,));
895 self.dVariables[oStmt.sVarName] = oStmt.sVarName;
896
897 # There shouldn't be any variables or arguments declared inside if/
898 # else blocks, but scan them too to be on the safe side.
899 if isinstance(oStmt, iai.McStmtCond):
900 cBefore = len(self.dVariables);
901 self.analyzeFindVariablesAndCallArgs(oStmt.aoIfBranch);
902 self.analyzeFindVariablesAndCallArgs(oStmt.aoElseBranch);
903 if len(self.dVariables) != cBefore:
904 raise Exception('Variables/arguments defined in conditional branches!');
905 return True;
906
907 def analyze(self):
908 """
909 Analyzes the code, identifying the number of parameters it requires and such.
910
911 Returns dummy True - raises exception on trouble.
912 """
913
914 # Decode the block into a list/tree of McStmt objects.
915 aoStmts = self.oMcBlock.decode();
916
917 # Scan the statements for local variables and call arguments (self.dVariables).
918 self.analyzeFindVariablesAndCallArgs(aoStmts);
919
920 # Create variations if needed.
921 if iai.McStmt.findStmtByNames(aoStmts,
922 { 'IEM_MC_DEFER_TO_CIMPL_0_RET': True,
923 'IEM_MC_DEFER_TO_CIMPL_1_RET': True,
924 'IEM_MC_DEFER_TO_CIMPL_2_RET': True,
925 'IEM_MC_DEFER_TO_CIMPL_3_RET': True, }):
926 self.aoVariations = [ThreadedFunctionVariation(self, ThreadedFunctionVariation.ksVariation_Default),];
927
928 elif iai.McStmt.findStmtByNames(aoStmts, {'IEM_MC_CALC_RM_EFF_ADDR' : True,}):
929 self.aoVariations = [ThreadedFunctionVariation(self, sVar)
930 for sVar in ThreadedFunctionVariation.kasVariationsWithAddress];
931 else:
932 self.aoVariations = [ThreadedFunctionVariation(self, sVar)
933 for sVar in ThreadedFunctionVariation.kasVariationsWithoutAddress];
934
935 # Dictionary variant of the list.
936 self.dVariations = { oVar.sVariation: oVar for oVar in self.aoVariations };
937
938 # Continue the analysis on each variation.
939 for oVariation in self.aoVariations:
940 oVariation.analyzeVariation(aoStmts);
941
942 return True;
943
944 def emitThreadedCallStmts(self):
945 """
946 Worker for morphInputCode that returns a list of statements that emits
947 the call to the threaded functions for the block.
948 """
949 # Special case for only default variation:
950 if len(self.aoVariations) == 1:
951 assert self.aoVariations[0].sVariation == ThreadedFunctionVariation.ksVariation_Default;
952 return self.aoVariations[0].emitThreadedCallStmts(0);
953
954 # Currently only have variations for address mode.
955 dByVari = self.dVariations;
956
957 sExecMask = 'IEM_F_MODE_CPUMODE_MASK';
958 if ( ThreadedFunctionVariation.ksVariation_64_Addr32 in dByVari
959 or ThreadedFunctionVariation.ksVariation_32_Addr16 in dByVari
960 or ThreadedFunctionVariation.ksVariation_32_Flat in dByVari
961 or ThreadedFunctionVariation.ksVariation_16_Addr32 in dByVari):
962 sExecMask = '(IEM_F_MODE_CPUMODE_MASK | IEM_F_MODE_X86_FLAT_OR_PRE_386_MASK)';
963 aoStmts = [
964 iai.McCppGeneric('switch (pVCpu->iem.s.fExec & %s)' % (sExecMask,)),
965 iai.McCppGeneric('{'),
966 ];
967
968 if ThreadedFunctionVariation.ksVariation_64_Addr32 in dByVari:
969 aoStmts.extend([
970 iai.McCppGeneric('case IEMMODE_64BIT:', cchIndent = 4),
971 iai.McCppCond('RT_LIKELY(pVCpu->iem.s.enmEffAddrMode == IEMMODE_64BIT)', fDecode = True, cchIndent = 8,
972 aoIfBranch = dByVari[ThreadedFunctionVariation.ksVariation_64].emitThreadedCallStmts(0),
973 aoElseBranch = dByVari[ThreadedFunctionVariation.ksVariation_64_Addr32].emitThreadedCallStmts(0)),
974 iai.McCppGeneric('break;', cchIndent = 8),
975 ]);
976 elif ThreadedFunctionVariation.ksVariation_64 in dByVari:
977 aoStmts.append(iai.McCppGeneric('case IEMMODE_64BIT:', cchIndent = 4));
978 aoStmts.extend(dByVari[ThreadedFunctionVariation.ksVariation_64].emitThreadedCallStmts(8));
979 aoStmts.append(iai.McCppGeneric('break;', cchIndent = 8));
980
981 if ThreadedFunctionVariation.ksVariation_32_Addr16 in dByVari:
982 aoStmts.extend([
983 iai.McCppGeneric('case IEMMODE_32BIT | IEM_F_MODE_X86_FLAT_OR_PRE_386_MASK:', cchIndent = 4),
984 iai.McCppCond('RT_LIKELY(pVCpu->iem.s.enmEffAddrMode == IEMMODE_32BIT)', fDecode = True, cchIndent = 8,
985 aoIfBranch = dByVari[ThreadedFunctionVariation.ksVariation_32_Flat].emitThreadedCallStmts(0),
986 aoElseBranch = dByVari[ThreadedFunctionVariation.ksVariation_32_Addr16].emitThreadedCallStmts(0)),
987 iai.McCppGeneric('break;', cchIndent = 8),
988 iai.McCppGeneric('case IEMMODE_32BIT:', cchIndent = 4),
989 iai.McCppCond('RT_LIKELY(pVCpu->iem.s.enmEffAddrMode == IEMMODE_32BIT)', fDecode = True, cchIndent = 8,
990 aoIfBranch = dByVari[ThreadedFunctionVariation.ksVariation_32].emitThreadedCallStmts(0),
991 aoElseBranch = dByVari[ThreadedFunctionVariation.ksVariation_32_Addr16].emitThreadedCallStmts(0)),
992 iai.McCppGeneric('break;', cchIndent = 8),
993 ]);
994 elif ThreadedFunctionVariation.ksVariation_32 in dByVari:
995 aoStmts.append(iai.McCppGeneric('case IEMMODE_32BIT:', cchIndent = 4));
996 aoStmts.extend(dByVari[ThreadedFunctionVariation.ksVariation_32].emitThreadedCallStmts(8));
997 aoStmts.append(iai.McCppGeneric('break;', cchIndent = 8));
998
999 if ThreadedFunctionVariation.ksVariation_16_Addr32 in dByVari:
1000 aoStmts.extend([
1001 iai.McCppGeneric('case IEMMODE_16BIT:', cchIndent = 4),
1002 iai.McCppCond('RT_LIKELY(pVCpu->iem.s.enmEffAddrMode == IEMMODE_16BIT)', fDecode = True, cchIndent = 8,
1003 aoIfBranch = dByVari[ThreadedFunctionVariation.ksVariation_16].emitThreadedCallStmts(0),
1004 aoElseBranch = dByVari[ThreadedFunctionVariation.ksVariation_16_Addr32].emitThreadedCallStmts(0)),
1005 iai.McCppGeneric('break;', cchIndent = 8),
1006 ]);
1007 elif ThreadedFunctionVariation.ksVariation_16 in dByVari:
1008 aoStmts.append(iai.McCppGeneric('case IEMMODE_16BIT:', cchIndent = 4));
1009 aoStmts.extend(dByVari[ThreadedFunctionVariation.ksVariation_16].emitThreadedCallStmts(8));
1010 aoStmts.append(iai.McCppGeneric('break;', cchIndent = 8));
1011
1012 if ThreadedFunctionVariation.ksVariation_16_Pre386 in dByVari:
1013 aoStmts.append(iai.McCppGeneric('case IEMMODE_16BIT | IEM_F_MODE_X86_FLAT_OR_PRE_386_MASK:', cchIndent = 4));
1014 aoStmts.extend(dByVari[ThreadedFunctionVariation.ksVariation_16_Pre386].emitThreadedCallStmts(8));
1015 aoStmts.append(iai.McCppGeneric('break;', cchIndent = 8));
1016
1017 aoStmts.extend([
1018 iai.McCppGeneric('IEM_NOT_REACHED_DEFAULT_CASE_RET();', cchIndent = 4),
1019 iai.McCppGeneric('}'),
1020 ]);
1021
1022 return aoStmts;
1023
1024 def morphInputCode(self, aoStmts, fCallEmitted = False, cDepth = 0):
1025 """
1026 Adjusts (& copies) the statements for the input/decoder so it will emit
1027 calls to the right threaded functions for each block.
1028
1029 Returns list/tree of statements (aoStmts is not modified) and updated
1030 fCallEmitted status.
1031 """
1032 #print('McBlock at %s:%s' % (os.path.split(self.oMcBlock.sSrcFile)[1], self.oMcBlock.iBeginLine,));
1033 aoDecoderStmts = [];
1034 for oStmt in aoStmts:
1035 # Copy the statement. Make a deep copy to make sure we've got our own
1036 # copies of all instance variables, even if a bit overkill at the moment.
1037 oNewStmt = copy.deepcopy(oStmt);
1038 aoDecoderStmts.append(oNewStmt);
1039 #print('oNewStmt %s %s' % (oNewStmt.sName, len(oNewStmt.asParams),));
1040
1041 # If we haven't emitted the threaded function call yet, look for
1042 # statements which it would naturally follow or preceed.
1043 if not fCallEmitted:
1044 if not oStmt.isCppStmt():
1045 if ( oStmt.sName.startswith('IEM_MC_MAYBE_RAISE_') \
1046 or (oStmt.sName.endswith('_AND_FINISH') and oStmt.sName.startswith('IEM_MC_'))
1047 or oStmt.sName.startswith('IEM_MC_CALL_CIMPL_')
1048 or oStmt.sName.startswith('IEM_MC_DEFER_TO_CIMPL_')
1049 or oStmt.sName in ('IEM_MC_RAISE_DIVIDE_ERROR',)):
1050 aoDecoderStmts.pop();
1051 aoDecoderStmts.extend(self.emitThreadedCallStmts());
1052 aoDecoderStmts.append(oNewStmt);
1053 fCallEmitted = True;
1054 elif ( oStmt.fDecode
1055 and ( oStmt.asParams[0].find('IEMOP_HLP_DONE_') >= 0
1056 or oStmt.asParams[0].find('IEMOP_HLP_DECODED_') >= 0)):
1057 aoDecoderStmts.extend(self.emitThreadedCallStmts());
1058 fCallEmitted = True;
1059
1060 # Process branches of conditionals recursively.
1061 if isinstance(oStmt, iai.McStmtCond):
1062 (oNewStmt.aoIfBranch, fCallEmitted1) = self.morphInputCode(oStmt.aoIfBranch, fCallEmitted, cDepth + 1);
1063 if oStmt.aoElseBranch:
1064 (oNewStmt.aoElseBranch, fCallEmitted2) = self.morphInputCode(oStmt.aoElseBranch, fCallEmitted, cDepth + 1);
1065 else:
1066 fCallEmitted2 = False;
1067 fCallEmitted = fCallEmitted or (fCallEmitted1 and fCallEmitted2);
1068
1069 if not fCallEmitted and cDepth == 0:
1070 self.raiseProblem('Unable to insert call to threaded function.');
1071
1072 return (aoDecoderStmts, fCallEmitted);
1073
1074
1075 def generateInputCode(self):
1076 """
1077 Modifies the input code.
1078 """
1079 cchIndent = (self.oMcBlock.cchIndent + 3) // 4 * 4;
1080
1081 if len(self.oMcBlock.aoStmts) == 1:
1082 # IEM_MC_DEFER_TO_CIMPL_X_RET - need to wrap in {} to make it safe to insert into random code.
1083 sCode = iai.McStmt.renderCodeForList(self.morphInputCode(self.oMcBlock.aoStmts)[0],
1084 cchIndent = cchIndent).replace('\n', ' /* gen */\n', 1);
1085 sCode = ' ' * (min(cchIndent, 2) - 2) + '{\n' \
1086 + sCode \
1087 + ' ' * (min(cchIndent, 2) - 2) + '}\n';
1088 return sCode;
1089
1090 # IEM_MC_BEGIN/END block
1091 assert len(self.oMcBlock.asLines) > 2, "asLines=%s" % (self.oMcBlock.asLines,);
1092 return iai.McStmt.renderCodeForList(self.morphInputCode(self.oMcBlock.aoStmts)[0],
1093 cchIndent = cchIndent).replace('\n', ' /* gen */\n', 1);
1094
1095
1096class IEMThreadedGenerator(object):
1097 """
1098 The threaded code generator & annotator.
1099 """
1100
1101 def __init__(self):
1102 self.aoThreadedFuncs = [] # type: list(ThreadedFunction)
1103 self.oOptions = None # type: argparse.Namespace
1104 self.aoParsers = [] # type: list(IEMAllInstructionsPython.SimpleParser)
1105
1106 #
1107 # Processing.
1108 #
1109
1110 def processInputFiles(self):
1111 """
1112 Process the input files.
1113 """
1114
1115 # Parse the files.
1116 self.aoParsers = iai.parseFiles(self.oOptions.asInFiles);
1117
1118 # Create threaded functions for the MC blocks.
1119 self.aoThreadedFuncs = [ThreadedFunction(oMcBlock) for oMcBlock in iai.g_aoMcBlocks];
1120
1121 # Analyze the threaded functions.
1122 dRawParamCounts = {};
1123 dMinParamCounts = {};
1124 for oThreadedFunction in self.aoThreadedFuncs:
1125 oThreadedFunction.analyze();
1126 for oVariation in oThreadedFunction.aoVariations:
1127 dRawParamCounts[len(oVariation.dParamRefs)] = dRawParamCounts.get(len(oVariation.dParamRefs), 0) + 1;
1128 dMinParamCounts[oVariation.cMinParams] = dMinParamCounts.get(oVariation.cMinParams, 0) + 1;
1129 print('debug: param count distribution, raw and optimized:', file = sys.stderr);
1130 for cCount in sorted({cBits: True for cBits in list(dRawParamCounts.keys()) + list(dMinParamCounts.keys())}.keys()):
1131 print('debug: %s params: %4s raw, %4s min'
1132 % (cCount, dRawParamCounts.get(cCount, 0), dMinParamCounts.get(cCount, 0)),
1133 file = sys.stderr);
1134
1135 return True;
1136
1137 #
1138 # Output
1139 #
1140
1141 def generateLicenseHeader(self):
1142 """
1143 Returns the lines for a license header.
1144 """
1145 return [
1146 '/*',
1147 ' * Autogenerated by $Id: IEMAllThreadedPython.py 100223 2023-06-20 02:53:04Z vboxsync $ ',
1148 ' * Do not edit!',
1149 ' */',
1150 '',
1151 '/*',
1152 ' * Copyright (C) 2023-' + str(datetime.date.today().year) + ' Oracle and/or its affiliates.',
1153 ' *',
1154 ' * This file is part of VirtualBox base platform packages, as',
1155 ' * available from https://www.virtualbox.org.',
1156 ' *',
1157 ' * This program is free software; you can redistribute it and/or',
1158 ' * modify it under the terms of the GNU General Public License',
1159 ' * as published by the Free Software Foundation, in version 3 of the',
1160 ' * License.',
1161 ' *',
1162 ' * This program is distributed in the hope that it will be useful, but',
1163 ' * WITHOUT ANY WARRANTY; without even the implied warranty of',
1164 ' * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU',
1165 ' * General Public License for more details.',
1166 ' *',
1167 ' * You should have received a copy of the GNU General Public License',
1168 ' * along with this program; if not, see <https://www.gnu.org/licenses>.',
1169 ' *',
1170 ' * The contents of this file may alternatively be used under the terms',
1171 ' * of the Common Development and Distribution License Version 1.0',
1172 ' * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included',
1173 ' * in the VirtualBox distribution, in which case the provisions of the',
1174 ' * CDDL are applicable instead of those of the GPL.',
1175 ' *',
1176 ' * You may elect to license modified versions of this file under the',
1177 ' * terms and conditions of either the GPL or the CDDL or both.',
1178 ' *',
1179 ' * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0',
1180 ' */',
1181 '',
1182 '',
1183 '',
1184 ];
1185
1186
1187 def generateThreadedFunctionsHeader(self, oOut):
1188 """
1189 Generates the threaded functions header file.
1190 Returns success indicator.
1191 """
1192
1193 asLines = self.generateLicenseHeader();
1194
1195 # Generate the threaded function table indexes.
1196 asLines += [
1197 'typedef enum IEMTHREADEDFUNCS',
1198 '{',
1199 ' kIemThreadedFunc_Invalid = 0,',
1200 '',
1201 ' /*'
1202 ' * Predefined'
1203 ' */'
1204 ' kIemThreadedFunc_CheckMode,',
1205 ];
1206 iThreadedFunction = 1;
1207 for sVariation in ThreadedFunctionVariation.kasVariationsEmitOrder:
1208 asLines += [
1209 '',
1210 ' /*',
1211 ' * Variation: ' + ThreadedFunctionVariation.kdVariationNames[sVariation] + '',
1212 ' */',
1213 ];
1214 for oThreadedFunction in self.aoThreadedFuncs:
1215 oVariation = oThreadedFunction.dVariations.get(sVariation, None);
1216 if oVariation:
1217 iThreadedFunction += 1;
1218 oVariation.iEnumValue = iThreadedFunction;
1219 asLines.append(' ' + oVariation.getIndexName() + ',');
1220 asLines += [
1221 ' kIemThreadedFunc_End',
1222 '} IEMTHREADEDFUNCS;',
1223 '',
1224 ];
1225
1226 # Prototype the function table.
1227 sFnType = 'typedef IEM_DECL_IMPL_TYPE(VBOXSTRICTRC, FNIEMTHREADEDFUNC, (PVMCPU pVCpu';
1228 for iParam in range(g_kcThreadedParams):
1229 sFnType += ', uint64_t uParam' + str(iParam);
1230 sFnType += '));'
1231
1232 asLines += [
1233 sFnType,
1234 'typedef FNIEMTHREADEDFUNC *PFNIEMTHREADEDFUNC;',
1235 '',
1236 'extern const PFNIEMTHREADEDFUNC g_apfnIemThreadedFunctions[kIemThreadedFunc_End];',
1237 ];
1238
1239 oOut.write('\n'.join(asLines));
1240 return True;
1241
1242 ksBitsToIntMask = {
1243 1: "UINT64_C(0x1)",
1244 2: "UINT64_C(0x3)",
1245 4: "UINT64_C(0xf)",
1246 8: "UINT64_C(0xff)",
1247 16: "UINT64_C(0xffff)",
1248 32: "UINT64_C(0xffffffff)",
1249 };
1250 def generateThreadedFunctionsSource(self, oOut):
1251 """
1252 Generates the threaded functions source file.
1253 Returns success indicator.
1254 """
1255
1256 asLines = self.generateLicenseHeader();
1257 oOut.write('\n'.join(asLines));
1258
1259 # Prepare the fixed bits.
1260 sParamList = '(PVMCPU pVCpu';
1261 for iParam in range(g_kcThreadedParams):
1262 sParamList += ', uint64_t uParam' + str(iParam);
1263 sParamList += '))\n';
1264
1265 #
1266 # Emit the function definitions.
1267 #
1268 for sVariation in ThreadedFunctionVariation.kasVariationsEmitOrder:
1269 sVarName = ThreadedFunctionVariation.kdVariationNames[sVariation];
1270 oOut.write( '\n'
1271 + '\n'
1272 + '\n'
1273 + '\n'
1274 + '/*' + '*' * 128 + '\n'
1275 + '* Variation: ' + sVarName + ' ' * (129 - len(sVarName) - 15) + '*\n'
1276 + '*' * 128 + '*/\n');
1277
1278 for oThreadedFunction in self.aoThreadedFuncs:
1279 oVariation = oThreadedFunction.dVariations.get(sVariation, None);
1280 if oVariation:
1281 oMcBlock = oThreadedFunction.oMcBlock;
1282
1283 # Function header
1284 oOut.write( '\n'
1285 + '\n'
1286 + '/**\n'
1287 + ' * %s at line %s offset %s in %s%s\n'
1288 % (oMcBlock.sFunction, oMcBlock.iBeginLine, oMcBlock.offBeginLine,
1289 os.path.split(oMcBlock.sSrcFile)[1],
1290 ' (macro expansion)' if oMcBlock.iBeginLine == oMcBlock.iEndLine else '')
1291 + ' */\n'
1292 + 'static IEM_DECL_IMPL_DEF(VBOXSTRICTRC, ' + oVariation.getFunctionName() + ',\n'
1293 + ' ' + sParamList
1294 + '{\n');
1295
1296 aasVars = [];
1297 for aoRefs in oVariation.dParamRefs.values():
1298 oRef = aoRefs[0];
1299 if oRef.sType[0] != 'P':
1300 cBits = g_kdTypeInfo[oRef.sType][0];
1301 sType = g_kdTypeInfo[oRef.sType][2];
1302 else:
1303 cBits = 64;
1304 sType = oRef.sType;
1305
1306 sTypeDecl = sType + ' const';
1307
1308 if cBits == 64:
1309 assert oRef.offNewParam == 0;
1310 if sType == 'uint64_t':
1311 sUnpack = 'uParam%s;' % (oRef.iNewParam,);
1312 else:
1313 sUnpack = '(%s)uParam%s;' % (sType, oRef.iNewParam,);
1314 elif oRef.offNewParam == 0:
1315 sUnpack = '(%s)(uParam%s & %s);' % (sType, oRef.iNewParam, self.ksBitsToIntMask[cBits]);
1316 else:
1317 sUnpack = '(%s)((uParam%s >> %s) & %s);' \
1318 % (sType, oRef.iNewParam, oRef.offNewParam, self.ksBitsToIntMask[cBits]);
1319
1320 sComment = '/* %s - %s ref%s */' % (oRef.sOrgRef, len(aoRefs), 's' if len(aoRefs) != 1 else '',);
1321
1322 aasVars.append([ '%s:%02u' % (oRef.iNewParam, oRef.offNewParam),
1323 sTypeDecl, oRef.sNewName, sUnpack, sComment ]);
1324 acchVars = [0, 0, 0, 0, 0];
1325 for asVar in aasVars:
1326 for iCol, sStr in enumerate(asVar):
1327 acchVars[iCol] = max(acchVars[iCol], len(sStr));
1328 sFmt = ' %%-%ss %%-%ss = %%-%ss %%s\n' % (acchVars[1], acchVars[2], acchVars[3]);
1329 for asVar in sorted(aasVars):
1330 oOut.write(sFmt % (asVar[1], asVar[2], asVar[3], asVar[4],));
1331
1332 # RT_NOREF for unused parameters.
1333 if oVariation.cMinParams < g_kcThreadedParams:
1334 oOut.write(' RT_NOREF('
1335 + ', '.join(['uParam%u' % (i,) for i in range(oVariation.cMinParams, g_kcThreadedParams)])
1336 + ');\n');
1337
1338 # Now for the actual statements.
1339 oOut.write(iai.McStmt.renderCodeForList(oVariation.aoStmtsForThreadedFunction, cchIndent = 4));
1340
1341 oOut.write('}\n');
1342
1343
1344 #
1345 # Emit the function table.
1346 #
1347 oOut.write( '\n'
1348 + '\n'
1349 + '/**\n'
1350 + ' * Function table.\n'
1351 + ' */\n'
1352 + 'const PFNIEMTHREADEDFUNC g_apfnIemThreadedFunctions[kIemThreadedFunc_End] =\n'
1353 + '{\n'
1354 + ' /*Invalid*/ NULL,\n'
1355 + '\n'
1356 + ' /*\n'
1357 + ' * Predefined.\n'
1358 + ' */'
1359 + ' iemThreadedFunc_BltIn_CheckMode,\n'
1360 );
1361 iThreadedFunction = 1;
1362 for sVariation in ThreadedFunctionVariation.kasVariationsEmitOrder:
1363 oOut.write( '\n'
1364 + ' /*\n'
1365 + ' * Variation: ' + ThreadedFunctionVariation.kdVariationNames[sVariation] + '\n'
1366 + ' */\n');
1367 for oThreadedFunction in self.aoThreadedFuncs:
1368 oVariation = oThreadedFunction.dVariations.get(sVariation, None);
1369 if oVariation:
1370 iThreadedFunction += 1;
1371 assert oVariation.iEnumValue == iThreadedFunction;
1372 oOut.write(' /*%4u*/ %s,\n' % (iThreadedFunction, oVariation.getFunctionName(),));
1373 oOut.write('};\n');
1374
1375 return True;
1376
1377 def getThreadedFunctionByIndex(self, idx):
1378 """
1379 Returns a ThreadedFunction object for the given index. If the index is
1380 out of bounds, a dummy is returned.
1381 """
1382 if idx < len(self.aoThreadedFuncs):
1383 return self.aoThreadedFuncs[idx];
1384 return ThreadedFunction.dummyInstance();
1385
1386 def generateModifiedInput(self, oOut):
1387 """
1388 Generates the combined modified input source/header file.
1389 Returns success indicator.
1390 """
1391 #
1392 # File header.
1393 #
1394 oOut.write('\n'.join(self.generateLicenseHeader()));
1395
1396 #
1397 # ASSUMING that g_aoMcBlocks/self.aoThreadedFuncs are in self.aoParsers
1398 # order, we iterate aoThreadedFuncs in parallel to the lines from the
1399 # parsers and apply modifications where required.
1400 #
1401 iThreadedFunction = 0;
1402 oThreadedFunction = self.getThreadedFunctionByIndex(0);
1403 for oParser in self.aoParsers: # type: IEMAllInstructionsPython.SimpleParser
1404 oOut.write("\n\n/* ****** BEGIN %s ******* */\n" % (oParser.sSrcFile,));
1405
1406 iLine = 0;
1407 while iLine < len(oParser.asLines):
1408 sLine = oParser.asLines[iLine];
1409 iLine += 1; # iBeginLine and iEndLine are 1-based.
1410
1411 # Can we pass it thru?
1412 if ( iLine not in [oThreadedFunction.oMcBlock.iBeginLine, oThreadedFunction.oMcBlock.iEndLine]
1413 or oThreadedFunction.oMcBlock.sSrcFile != oParser.sSrcFile):
1414 oOut.write(sLine);
1415 #
1416 # Single MC block. Just extract it and insert the replacement.
1417 #
1418 elif oThreadedFunction.oMcBlock.iBeginLine != oThreadedFunction.oMcBlock.iEndLine:
1419 assert sLine.count('IEM_MC_') == 1;
1420 oOut.write(sLine[:oThreadedFunction.oMcBlock.offBeginLine]);
1421 sModified = oThreadedFunction.generateInputCode().strip();
1422 oOut.write(sModified);
1423
1424 iLine = oThreadedFunction.oMcBlock.iEndLine;
1425 sLine = oParser.asLines[iLine - 1];
1426 assert sLine.count('IEM_MC_') == 1 or len(oThreadedFunction.oMcBlock.aoStmts) == 1;
1427 oOut.write(sLine[oThreadedFunction.oMcBlock.offAfterEnd : ]);
1428
1429 # Advance
1430 iThreadedFunction += 1;
1431 oThreadedFunction = self.getThreadedFunctionByIndex(iThreadedFunction);
1432 #
1433 # Macro expansion line that have sublines and may contain multiple MC blocks.
1434 #
1435 else:
1436 offLine = 0;
1437 while iLine == oThreadedFunction.oMcBlock.iBeginLine:
1438 oOut.write(sLine[offLine : oThreadedFunction.oMcBlock.offBeginLine]);
1439
1440 sModified = oThreadedFunction.generateInputCode().strip();
1441 assert ( sModified.startswith('IEM_MC_BEGIN')
1442 or (sModified.find('IEM_MC_DEFER_TO_CIMPL_') > 0 and sModified.strip().startswith('{\n'))
1443 ), 'sModified="%s"' % (sModified,);
1444 oOut.write(sModified);
1445
1446 offLine = oThreadedFunction.oMcBlock.offAfterEnd;
1447
1448 # Advance
1449 iThreadedFunction += 1;
1450 oThreadedFunction = self.getThreadedFunctionByIndex(iThreadedFunction);
1451
1452 # Last line segment.
1453 if offLine < len(sLine):
1454 oOut.write(sLine[offLine : ]);
1455
1456 oOut.write("/* ****** END %s ******* */\n" % (oParser.sSrcFile,));
1457
1458 return True;
1459
1460 #
1461 # Main
1462 #
1463
1464 def main(self, asArgs):
1465 """
1466 C-like main function.
1467 Returns exit code.
1468 """
1469
1470 #
1471 # Parse arguments
1472 #
1473 sScriptDir = os.path.dirname(__file__);
1474 oParser = argparse.ArgumentParser(add_help = False);
1475 oParser.add_argument('asInFiles', metavar = 'input.cpp.h', nargs = '*',
1476 default = [os.path.join(sScriptDir, asFM[0]) for asFM in iai.g_aasAllInstrFilesAndDefaultMap],
1477 help = "Selection of VMMAll/IEMAllInstructions*.cpp.h files to use as input.");
1478 oParser.add_argument('--out-funcs-hdr', metavar = 'file-funcs.h', dest = 'sOutFileFuncsHdr', action = 'store',
1479 default = '-', help = 'The output header file for the functions.');
1480 oParser.add_argument('--out-funcs-cpp', metavar = 'file-funcs.cpp', dest = 'sOutFileFuncsCpp', action = 'store',
1481 default = '-', help = 'The output C++ file for the functions.');
1482 oParser.add_argument('--out-mod-input', metavar = 'file-instr.cpp.h', dest = 'sOutFileModInput', action = 'store',
1483 default = '-', help = 'The output C++/header file for the modified input instruction files.');
1484 oParser.add_argument('--help', '-h', '-?', action = 'help', help = 'Display help and exit.');
1485 oParser.add_argument('--version', '-V', action = 'version',
1486 version = 'r%s (IEMAllThreadedPython.py), r%s (IEMAllInstructionsPython.py)'
1487 % (__version__.split()[1], iai.__version__.split()[1],),
1488 help = 'Displays the version/revision of the script and exit.');
1489 self.oOptions = oParser.parse_args(asArgs[1:]);
1490 print("oOptions=%s" % (self.oOptions,));
1491
1492 #
1493 # Process the instructions specified in the IEM sources.
1494 #
1495 if self.processInputFiles():
1496 #
1497 # Generate the output files.
1498 #
1499 aaoOutputFiles = (
1500 ( self.oOptions.sOutFileFuncsHdr, self.generateThreadedFunctionsHeader ),
1501 ( self.oOptions.sOutFileFuncsCpp, self.generateThreadedFunctionsSource ),
1502 ( self.oOptions.sOutFileModInput, self.generateModifiedInput ),
1503 );
1504 fRc = True;
1505 for sOutFile, fnGenMethod in aaoOutputFiles:
1506 if sOutFile == '-':
1507 fRc = fnGenMethod(sys.stdout) and fRc;
1508 else:
1509 try:
1510 oOut = open(sOutFile, 'w'); # pylint: disable=consider-using-with,unspecified-encoding
1511 except Exception as oXcpt:
1512 print('error! Failed open "%s" for writing: %s' % (sOutFile, oXcpt,), file = sys.stderr);
1513 return 1;
1514 fRc = fnGenMethod(oOut) and fRc;
1515 oOut.close();
1516 if fRc:
1517 return 0;
1518
1519 return 1;
1520
1521
1522if __name__ == '__main__':
1523 sys.exit(IEMThreadedGenerator().main(sys.argv));
1524
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