VirtualBox

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

最後變更 在這個檔案從100651是 100633,由 vboxsync 提交於 20 月 前

VMM/IEM: Check for too early IEMCPU::iEffSeg use during recompiler code generation. bugref:10369

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

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