VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMAll/IEMAllThrdPython.py@ 100788

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

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

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