VirtualBox

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

Last change on this file since 100764 was 100761, checked in by vboxsync, 19 months ago

VMM/IEM: Check for IRQs every so often, especially after sti, popf and iret. Increased the hash table size. Disabled some debug code. bugref:10369

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