VirtualBox

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

Last change on this file since 100743 was 100743, checked in by vboxsync, 22 months ago

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