VirtualBox

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

Last change on this file since 100594 was 100587, checked in by vboxsync, 17 months ago

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

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