VirtualBox

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

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

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