VirtualBox

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

Last change on this file since 100089 was 100089, checked in by vboxsync, 18 months ago

VMM/IEM: Reworking the variations generated by the python script to match part of the fExec mode better. 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: 66.3 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3# $Id: IEMAllThreadedPython.py 100089 2023-06-07 01:39:11Z 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: 100089 $"
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, 'uint8_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 ## @}
174
175 def __init__(self, oThreadedFunction, sVariation = ksVariation_Default):
176 self.oParent = oThreadedFunction # type: ThreadedFunction
177 ##< ksVariation_Xxxx.
178 self.sVariation = sVariation
179
180 ## Threaded function parameter references.
181 self.aoParamRefs = [] # type: list(ThreadedParamRef)
182 ## Unique parameter references.
183 self.dParamRefs = {} # type: dict(str,list(ThreadedParamRef))
184 ## Minimum number of parameters to the threaded function.
185 self.cMinParams = 0;
186
187 ## List/tree of statements for the threaded function.
188 self.aoStmtsForThreadedFunction = [] # type: list(McStmt)
189
190 def getIndexName(self):
191 sName = self.oParent.oMcBlock.sFunction;
192 if sName.startswith('iemOp_'):
193 sName = sName[len('iemOp_'):];
194 if self.oParent.oMcBlock.iInFunction == 0:
195 return 'kIemThreadedFunc_%s%s' % ( sName, self.sVariation, );
196 return 'kIemThreadedFunc_%s_%s%s' % ( sName, self.oParent.oMcBlock.iInFunction, self.sVariation, );
197
198 def getFunctionName(self):
199 sName = self.oParent.oMcBlock.sFunction;
200 if sName.startswith('iemOp_'):
201 sName = sName[len('iemOp_'):];
202 if self.oParent.oMcBlock.iInFunction == 0:
203 return 'iemThreadedFunc_%s%s' % ( sName, self.sVariation, );
204 return 'iemThreadedFunc_%s_%s%s' % ( sName, self.oParent.oMcBlock.iInFunction, self.sVariation, );
205
206 #
207 # Analysis and code morphing.
208 #
209
210 def raiseProblem(self, sMessage):
211 """ Raises a problem. """
212 self.oParent.raiseProblem(sMessage);
213
214 def analyzeReferenceToType(self, sRef):
215 """
216 Translates a variable or structure reference to a type.
217 Returns type name.
218 Raises exception if unable to figure it out.
219 """
220 ch0 = sRef[0];
221 if ch0 == 'u':
222 if sRef.startswith('u32'):
223 return 'uint32_t';
224 if sRef.startswith('u8') or sRef == 'uReg':
225 return 'uint8_t';
226 if sRef.startswith('u64'):
227 return 'uint64_t';
228 if sRef.startswith('u16'):
229 return 'uint16_t';
230 elif ch0 == 'b':
231 return 'uint8_t';
232 elif ch0 == 'f':
233 return 'bool';
234 elif ch0 == 'i':
235 if sRef.startswith('i8'):
236 return 'int8_t';
237 if sRef.startswith('i16'):
238 return 'int32_t';
239 if sRef.startswith('i32'):
240 return 'int32_t';
241 if sRef.startswith('i64'):
242 return 'int64_t';
243 if sRef in ('iReg', 'iGReg', 'iSegReg', 'iSrcReg', 'iDstReg'):
244 return 'uint8_t';
245 elif ch0 == 'p':
246 if sRef.find('-') < 0:
247 return 'uintptr_t';
248 if sRef.startswith('pVCpu->iem.s.'):
249 sField = sRef[len('pVCpu->iem.s.') : ];
250 if sField in g_kdIemFieldToType:
251 if g_kdIemFieldToType[sField][0]:
252 return g_kdIemFieldToType[sField][0];
253 self.raiseProblem('Reference out-of-bounds decoder field: %s' % (sRef,));
254 elif ch0 == 'G' and sRef.startswith('GCPtr'):
255 return 'uint64_t';
256 elif ch0 == 'e':
257 if sRef == 'enmEffOpSize':
258 return 'IEMMODE';
259 elif sRef == 'cShift': ## @todo risky
260 return 'uint8_t';
261
262 self.raiseProblem('Unknown reference: %s' % (sRef,));
263 return None; # Shut up pylint 2.16.2.
264
265 def analyzeCallToType(self, sFnRef):
266 """
267 Determins the type of an indirect function call.
268 """
269 assert sFnRef[0] == 'p';
270
271 #
272 # Simple?
273 #
274 if sFnRef.find('-') < 0:
275 oDecoderFunction = self.oParent.oMcBlock.oFunction;
276
277 # Try the argument list of the function defintion macro invocation first.
278 iArg = 2;
279 while iArg < len(oDecoderFunction.asDefArgs):
280 if sFnRef == oDecoderFunction.asDefArgs[iArg]:
281 return oDecoderFunction.asDefArgs[iArg - 1];
282 iArg += 1;
283
284 # Then check out line that includes the word and looks like a variable declaration.
285 oRe = re.compile(' +(P[A-Z0-9_]+|const +IEMOP[A-Z0-9_]+ *[*]) +(const |) *' + sFnRef + ' *(;|=)');
286 for sLine in oDecoderFunction.asLines:
287 oMatch = oRe.match(sLine);
288 if oMatch:
289 if not oMatch.group(1).startswith('const'):
290 return oMatch.group(1);
291 return 'PC' + oMatch.group(1)[len('const ') : -1].strip();
292
293 #
294 # Deal with the pImpl->pfnXxx:
295 #
296 elif sFnRef.startswith('pImpl->pfn'):
297 sMember = sFnRef[len('pImpl->') : ];
298 sBaseType = self.analyzeCallToType('pImpl');
299 offBits = sMember.rfind('U') + 1;
300 if sBaseType == 'PCIEMOPBINSIZES': return 'PFNIEMAIMPLBINU' + sMember[offBits:];
301 if sBaseType == 'PCIEMOPUNARYSIZES': return 'PFNIEMAIMPLUNARYU' + sMember[offBits:];
302 if sBaseType == 'PCIEMOPSHIFTSIZES': return 'PFNIEMAIMPLSHIFTU' + sMember[offBits:];
303 if sBaseType == 'PCIEMOPSHIFTDBLSIZES': return 'PFNIEMAIMPLSHIFTDBLU' + sMember[offBits:];
304 if sBaseType == 'PCIEMOPMULDIVSIZES': return 'PFNIEMAIMPLMULDIVU' + sMember[offBits:];
305 if sBaseType == 'PCIEMOPMEDIAF3': return 'PFNIEMAIMPLMEDIAF3U' + sMember[offBits:];
306 if sBaseType == 'PCIEMOPMEDIAOPTF3': return 'PFNIEMAIMPLMEDIAOPTF3U' + sMember[offBits:];
307 if sBaseType == 'PCIEMOPMEDIAOPTF2': return 'PFNIEMAIMPLMEDIAOPTF2U' + sMember[offBits:];
308 if sBaseType == 'PCIEMOPMEDIAOPTF3IMM8': return 'PFNIEMAIMPLMEDIAOPTF3U' + sMember[offBits:] + 'IMM8';
309 if sBaseType == 'PCIEMOPBLENDOP': return 'PFNIEMAIMPLAVXBLENDU' + sMember[offBits:];
310
311 self.raiseProblem('Unknown call reference: %s::%s (%s)' % (sBaseType, sMember, sFnRef,));
312
313 self.raiseProblem('Unknown call reference: %s' % (sFnRef,));
314 return None; # Shut up pylint 2.16.2.
315
316 def analyze8BitGRegStmt(self, oStmt):
317 """
318 Gets the 8-bit general purpose register access details of the given statement.
319 ASSUMES the statement is one accessing an 8-bit GREG.
320 """
321 idxReg = 0;
322 if ( oStmt.sName.find('_FETCH_') > 0
323 or oStmt.sName.find('_REF_') > 0
324 or oStmt.sName.find('_TO_LOCAL') > 0):
325 idxReg = 1;
326
327 sRegRef = oStmt.asParams[idxReg];
328 sOrgExpr = '((%s) < 4 || (pVCpu->iem.s.fPrefixes & IEM_OP_PRF_REX) ? (%s) : (%s) | 16)' % (sRegRef, sRegRef, sRegRef);
329
330 if sRegRef.find('IEM_GET_MODRM_RM') > 0: sStdRef = 'bRmRm8Ex';
331 elif sRegRef.find('IEM_GET_MODRM_REG') > 0: sStdRef = 'bRmReg8Ex';
332 else: sStdRef = 'bOther8Ex';
333
334 return (idxReg, sOrgExpr, sStdRef);
335
336
337 def analyzeMorphStmtForThreaded(self, aoStmts, iParamRef = 0):
338 """
339 Transforms (copy) the statements into those for the threaded function.
340
341 Returns list/tree of statements (aoStmts is not modified) and the new
342 iParamRef value.
343 """
344 #
345 # We'll be traversing aoParamRefs in parallel to the statements, so we
346 # must match the traversal in analyzeFindThreadedParamRefs exactly.
347 #
348 #print('McBlock at %s:%s' % (os.path.split(self.oMcBlock.sSrcFile)[1], self.oMcBlock.iBeginLine,));
349 aoThreadedStmts = [];
350 for oStmt in aoStmts:
351 # Skip C++ statements that is purely related to decoding.
352 if not oStmt.isCppStmt() or not oStmt.fDecode:
353 # Copy the statement. Make a deep copy to make sure we've got our own
354 # copies of all instance variables, even if a bit overkill at the moment.
355 oNewStmt = copy.deepcopy(oStmt);
356 aoThreadedStmts.append(oNewStmt);
357 #print('oNewStmt %s %s' % (oNewStmt.sName, len(oNewStmt.asParams),));
358
359 # If the statement has parameter references, process the relevant parameters.
360 # We grab the references relevant to this statement and apply them in reserve order.
361 if iParamRef < len(self.aoParamRefs) and self.aoParamRefs[iParamRef].oStmt == oStmt:
362 iParamRefFirst = iParamRef;
363 while True:
364 iParamRef += 1;
365 if iParamRef >= len(self.aoParamRefs) or self.aoParamRefs[iParamRef].oStmt != oStmt:
366 break;
367
368 #print('iParamRefFirst=%s iParamRef=%s' % (iParamRefFirst, iParamRef));
369 for iCurRef in range(iParamRef - 1, iParamRefFirst - 1, -1):
370 oCurRef = self.aoParamRefs[iCurRef];
371 if oCurRef.iParam is not None:
372 assert oCurRef.oStmt == oStmt;
373 #print('iCurRef=%s iParam=%s sOrgRef=%s' % (iCurRef, oCurRef.iParam, oCurRef.sOrgRef));
374 sSrcParam = oNewStmt.asParams[oCurRef.iParam];
375 assert ( sSrcParam[oCurRef.offParam : oCurRef.offParam + len(oCurRef.sOrgRef)] == oCurRef.sOrgRef
376 or oCurRef.fCustomRef), \
377 'offParam=%s sOrgRef=%s iParam=%s oStmt.sName=%s sSrcParam=%s<eos>' \
378 % (oCurRef.offParam, oCurRef.sOrgRef, oCurRef.iParam, oStmt.sName, sSrcParam);
379 oNewStmt.asParams[oCurRef.iParam] = sSrcParam[0 : oCurRef.offParam] \
380 + oCurRef.sNewName \
381 + sSrcParam[oCurRef.offParam + len(oCurRef.sOrgRef) : ];
382
383 # Morph IEM_MC_CALC_RM_EFF_ADDR into IEM_MC_CALC_RM_EFF_ADDR_THREADED ...
384 if oNewStmt.sName == 'IEM_MC_CALC_RM_EFF_ADDR':
385 assert self.sVariation != self.ksVariation_Default;
386 oNewStmt.sName = 'IEM_MC_CALC_RM_EFF_ADDR_THREADED' + self.sVariation.upper();
387 assert len(oNewStmt.asParams) == 3;
388 if self.sVariation in (self.ksVariation_16, self.ksVariation_16_Pre386, self.ksVariation_32_Addr16):
389 oNewStmt.asParams = [
390 oNewStmt.asParams[0], oNewStmt.asParams[1], self.dParamRefs['u16Disp'][0].sNewName,
391 ];
392 elif self.sVariation in (self.ksVariation_32, self.ksVariation_32_Flat, self.ksVariation_16_Addr32):
393 oNewStmt.asParams = [
394 oNewStmt.asParams[0], oNewStmt.asParams[1], self.dParamRefs['bSib'][0].sNewName,
395 self.dParamRefs['u32Disp'][0].sNewName,
396 ];
397 else:
398 oNewStmt.asParams = [
399 oNewStmt.asParams[0], self.dParamRefs['bRmEx'][0].sNewName, self.dParamRefs['bSib'][0].sNewName,
400 self.dParamRefs['u32Disp'][0].sNewName, self.dParamRefs['cbInstr'][0].sNewName,
401 ];
402 # ... and IEM_MC_ADVANCE_RIP_AND_FINISH into *_THREADED and maybe *_LM64/_NOT64 ...
403 elif oNewStmt.sName in ('IEM_MC_ADVANCE_RIP_AND_FINISH', 'IEM_MC_REL_JMP_S8_AND_FINISH',
404 'IEM_MC_REL_JMP_S16_AND_FINISH', 'IEM_MC_REL_JMP_S32_AND_FINISH'):
405 oNewStmt.asParams.append(self.dParamRefs['cbInstr'][0].sNewName);
406 if oNewStmt.sName in ('IEM_MC_REL_JMP_S8_AND_FINISH', 'IEM_MC_REL_JMP_S32_AND_FINISH'):
407 oNewStmt.asParams.append(self.dParamRefs['pVCpu->iem.s.enmEffOpSize'][0].sNewName);
408 oNewStmt.sName += '_THREADED';
409 if self.sVariation in (self.ksVariation_64, self.ksVariation_64_Addr32):
410 oNewStmt.sName += '_PC64';
411 elif self.sVariation == self.ksVariation_16_Pre386:
412 oNewStmt.sName += '_PC16';
413 elif self.sVariation != self.ksVariation_Default:
414 oNewStmt.sName += '_PC32';
415
416 # ... and IEM_MC_*_GREG_U8 into *_THREADED w/ reworked index taking REX into account
417 elif oNewStmt.sName.startswith('IEM_MC_') and oNewStmt.sName.find('_GREG_U8') > 0:
418 (idxReg, _, sStdRef) = self.analyze8BitGRegStmt(oNewStmt);
419 oNewStmt.asParams[idxReg] = self.dParamRefs[sStdRef][0].sNewName;
420 oNewStmt.sName += '_THREADED';
421
422 # ... and IEM_MC_CALL_CIMPL_[0-5] into *_THREADED ...
423 elif oNewStmt.sName.startswith('IEM_MC_CALL_CIMPL_'):
424 oNewStmt.sName += '_THREADED';
425 oNewStmt.asParams.insert(0, self.dParamRefs['cbInstr'][0].sNewName);
426
427 # Process branches of conditionals recursively.
428 if isinstance(oStmt, iai.McStmtCond):
429 (oNewStmt.aoIfBranch, iParamRef) = self.analyzeMorphStmtForThreaded(oStmt.aoIfBranch, iParamRef);
430 if oStmt.aoElseBranch:
431 (oNewStmt.aoElseBranch, iParamRef) = self.analyzeMorphStmtForThreaded(oStmt.aoElseBranch, iParamRef);
432
433 return (aoThreadedStmts, iParamRef);
434
435 def analyzeConsolidateThreadedParamRefs(self):
436 """
437 Consolidate threaded function parameter references into a dictionary
438 with lists of the references to each variable/field.
439 """
440 # Gather unique parameters.
441 self.dParamRefs = {};
442 for oRef in self.aoParamRefs:
443 if oRef.sStdRef not in self.dParamRefs:
444 self.dParamRefs[oRef.sStdRef] = [oRef,];
445 else:
446 self.dParamRefs[oRef.sStdRef].append(oRef);
447
448 # Generate names for them for use in the threaded function.
449 dParamNames = {};
450 for sName, aoRefs in self.dParamRefs.items():
451 # Morph the reference expression into a name.
452 if sName.startswith('IEM_GET_MODRM_REG'): sName = 'bModRmRegP';
453 elif sName.startswith('IEM_GET_MODRM_RM'): sName = 'bModRmRmP';
454 elif sName.startswith('IEM_GET_MODRM_REG_8'): sName = 'bModRmReg8P';
455 elif sName.startswith('IEM_GET_MODRM_RM_8'): sName = 'bModRmRm8P';
456 elif sName.startswith('IEM_GET_EFFECTIVE_VVVV'): sName = 'bEffVvvvP';
457 elif sName.find('.') >= 0 or sName.find('->') >= 0:
458 sName = sName[max(sName.rfind('.'), sName.rfind('>')) + 1 : ] + 'P';
459 else:
460 sName += 'P';
461
462 # Ensure it's unique.
463 if sName in dParamNames:
464 for i in range(10):
465 if sName + str(i) not in dParamNames:
466 sName += str(i);
467 break;
468 dParamNames[sName] = True;
469
470 # Update all the references.
471 for oRef in aoRefs:
472 oRef.sNewName = sName;
473
474 # Organize them by size too for the purpose of optimize them.
475 dBySize = {} # type: dict(str,str)
476 for sStdRef, aoRefs in self.dParamRefs.items():
477 if aoRefs[0].sType[0] != 'P':
478 cBits = g_kdTypeInfo[aoRefs[0].sType][0];
479 assert(cBits <= 64);
480 else:
481 cBits = 64;
482
483 if cBits not in dBySize:
484 dBySize[cBits] = [sStdRef,]
485 else:
486 dBySize[cBits].append(sStdRef);
487
488 # Pack the parameters as best as we can, starting with the largest ones
489 # and ASSUMING a 64-bit parameter size.
490 self.cMinParams = 0;
491 offNewParam = 0;
492 for cBits in sorted(dBySize.keys(), reverse = True):
493 for sStdRef in dBySize[cBits]:
494 if offNewParam == 0 or offNewParam + cBits > 64:
495 self.cMinParams += 1;
496 offNewParam = cBits;
497 else:
498 offNewParam += cBits;
499 assert(offNewParam <= 64);
500
501 for oRef in self.dParamRefs[sStdRef]:
502 oRef.iNewParam = self.cMinParams - 1;
503 oRef.offNewParam = offNewParam - cBits;
504
505 # Currently there are a few that requires 4 parameters, list these so we can figure out why:
506 if self.cMinParams >= 4:
507 print('debug: cMinParams=%s cRawParams=%s - %s:%d'
508 % (self.cMinParams, len(self.dParamRefs), self.oParent.oMcBlock.sSrcFile, self.oParent.oMcBlock.iBeginLine,));
509
510 return True;
511
512 ksHexDigits = '0123456789abcdefABCDEF';
513
514 def analyzeFindThreadedParamRefs(self, aoStmts):
515 """
516 Scans the statements for things that have to passed on to the threaded
517 function (populates self.aoParamRefs).
518 """
519 for oStmt in aoStmts:
520 # Some statements we can skip alltogether.
521 if isinstance(oStmt, iai.McCppPreProc):
522 continue;
523 if oStmt.isCppStmt() and oStmt.fDecode:
524 continue;
525
526 if isinstance(oStmt, iai.McStmtVar):
527 if oStmt.sConstValue is None:
528 continue;
529 aiSkipParams = { 0: True, 1: True, 3: True };
530 else:
531 aiSkipParams = {};
532
533 # Several statements have implicit parameters and some have different parameters.
534 if oStmt.sName in ('IEM_MC_ADVANCE_RIP_AND_FINISH', 'IEM_MC_REL_JMP_S8_AND_FINISH', 'IEM_MC_REL_JMP_S16_AND_FINISH',
535 'IEM_MC_REL_JMP_S32_AND_FINISH', 'IEM_MC_CALL_CIMPL_0', 'IEM_MC_CALL_CIMPL_1',
536 'IEM_MC_CALL_CIMPL_2', 'IEM_MC_CALL_CIMPL_3', 'IEM_MC_CALL_CIMPL_4', 'IEM_MC_CALL_CIMPL_5', ):
537 self.aoParamRefs.append(ThreadedParamRef('IEM_GET_INSTR_LEN(pVCpu)', 'uint4_t', oStmt, sStdRef = 'cbInstr'));
538
539 if oStmt.sName in ('IEM_MC_REL_JMP_S8_AND_FINISH', 'IEM_MC_REL_JMP_S32_AND_FINISH'):
540 self.aoParamRefs.append(ThreadedParamRef('pVCpu->iem.s.enmEffOpSize', 'IEMMODE', oStmt));
541
542 if oStmt.sName == 'IEM_MC_CALC_RM_EFF_ADDR':
543 # This is being pretty presumptive about bRm always being the RM byte...
544 if self.sVariation in (self.ksVariation_16, self.ksVariation_16_Pre386, self.ksVariation_32_Addr16):
545 self.aoParamRefs.append(ThreadedParamRef('bRm', 'uint8_t', oStmt));
546 self.aoParamRefs.append(ThreadedParamRef('(uint16_t)uEffAddrInfo' ,
547 'uint16_t', oStmt, sStdRef = 'u16Disp'));
548 elif self.sVariation in (self.ksVariation_32, self.ksVariation_32_Flat, self.ksVariation_16_Addr32):
549 self.aoParamRefs.append(ThreadedParamRef('bRm', 'uint8_t', oStmt));
550 self.aoParamRefs.append(ThreadedParamRef('(uint8_t)(uEffAddrInfo >> 32)',
551 'uint8_t', oStmt, sStdRef = 'bSib'));
552 self.aoParamRefs.append(ThreadedParamRef('(uint32_t)uEffAddrInfo',
553 'uint32_t', oStmt, sStdRef = 'u32Disp'));
554 else:
555 assert self.sVariation in (self.ksVariation_64, self.ksVariation_64_Addr32);
556 self.aoParamRefs.append(ThreadedParamRef('IEM_GET_MODRM_EX(pVCpu, bRm)',
557 'uint8_t', oStmt, sStdRef = 'bRmEx'));
558 self.aoParamRefs.append(ThreadedParamRef('(uint8_t)(uEffAddrInfo >> 32)',
559 'uint8_t', oStmt, sStdRef = 'bSib'));
560 self.aoParamRefs.append(ThreadedParamRef('(uint32_t)uEffAddrInfo',
561 'uint32_t', oStmt, sStdRef = 'u32Disp'));
562 self.aoParamRefs.append(ThreadedParamRef('IEM_GET_INSTR_LEN(pVCpu)',
563 'uint4_t', oStmt, sStdRef = 'cbInstr'));
564 assert len(oStmt.asParams) == 3;
565 assert oStmt.asParams[1].startswith('bRm');
566 aiSkipParams[1] = True; # Skip the bRm parameter
567
568 # 8-bit register accesses needs to have their index argument reworked to take REX into account.
569 if oStmt.sName.startswith('IEM_MC_') and oStmt.sName.find('_GREG_U8') > 0:
570 (idxReg, sOrgRef, sStdRef) = self.analyze8BitGRegStmt(oStmt);
571 self.aoParamRefs.append(ThreadedParamRef(sOrgRef, 'uint4_t', oStmt, idxReg, sStdRef = sStdRef));
572 aiSkipParams[idxReg] = True; # Skip the parameter below.
573
574 # Inspect the target of calls to see if we need to pass down a
575 # function pointer or function table pointer for it to work.
576 if isinstance(oStmt, iai.McStmtCall):
577 if oStmt.sFn[0] == 'p':
578 self.aoParamRefs.append(ThreadedParamRef(oStmt.sFn, self.analyzeCallToType(oStmt.sFn), oStmt, oStmt.idxFn));
579 elif ( oStmt.sFn[0] != 'i'
580 and not oStmt.sFn.startswith('IEMTARGETCPU_EFL_BEHAVIOR_SELECT')
581 and not oStmt.sFn.startswith('IEM_SELECT_HOST_OR_FALLBACK') ):
582 self.raiseProblem('Bogus function name in %s: %s' % (oStmt.sName, oStmt.sFn,));
583 aiSkipParams[oStmt.idxFn] = True;
584
585 # Skip the hint parameter (first) for IEM_MC_CALL_CIMPL_X.
586 if oStmt.sName.startswith('IEM_MC_CALL_CIMPL_'):
587 assert oStmt.idxFn == 1;
588 aiSkipParams[0] = True;
589
590
591 # Check all the parameters for bogus references.
592 for iParam, sParam in enumerate(oStmt.asParams):
593 if iParam not in aiSkipParams and sParam not in self.oParent.dVariables:
594 # The parameter may contain a C expression, so we have to try
595 # extract the relevant bits, i.e. variables and fields while
596 # ignoring operators and parentheses.
597 offParam = 0;
598 while offParam < len(sParam):
599 # Is it the start of an C identifier? If so, find the end, but don't stop on field separators (->, .).
600 ch = sParam[offParam];
601 if ch.isalpha() or ch == '_':
602 offStart = offParam;
603 offParam += 1;
604 while offParam < len(sParam):
605 ch = sParam[offParam];
606 if not ch.isalnum() and ch != '_' and ch != '.':
607 if ch != '-' or sParam[offParam + 1] != '>':
608 # Special hack for the 'CTX_SUFF(pVM)' bit in pVCpu->CTX_SUFF(pVM)->xxxx:
609 if ( ch == '('
610 and sParam[offStart : offParam + len('(pVM)->')] == 'pVCpu->CTX_SUFF(pVM)->'):
611 offParam += len('(pVM)->') - 1;
612 else:
613 break;
614 offParam += 1;
615 offParam += 1;
616 sRef = sParam[offStart : offParam];
617
618 # For register references, we pass the full register indexes instead as macros
619 # like IEM_GET_MODRM_REG implicitly references pVCpu->iem.s.uRexReg and the
620 # threaded function will be more efficient if we just pass the register index
621 # as a 4-bit param.
622 if ( sRef.startswith('IEM_GET_MODRM')
623 or sRef.startswith('IEM_GET_EFFECTIVE_VVVV') ):
624 offParam = iai.McBlock.skipSpacesAt(sParam, offParam, len(sParam));
625 if sParam[offParam] != '(':
626 self.raiseProblem('Expected "(" following %s in "%s"' % (sRef, oStmt.renderCode(),));
627 (asMacroParams, offCloseParam) = iai.McBlock.extractParams(sParam, offParam);
628 if asMacroParams is None:
629 self.raiseProblem('Unable to find ")" for %s in "%s"' % (sRef, oStmt.renderCode(),));
630 offParam = offCloseParam + 1;
631 self.aoParamRefs.append(ThreadedParamRef(sParam[offStart : offParam], 'uint8_t',
632 oStmt, iParam, offStart));
633
634 # We can skip known variables.
635 elif sRef in self.oParent.dVariables:
636 pass;
637
638 # Skip certain macro invocations.
639 elif sRef in ('IEM_GET_HOST_CPU_FEATURES',
640 'IEM_GET_GUEST_CPU_FEATURES',
641 'IEM_IS_GUEST_CPU_AMD',
642 'IEM_IS_16BIT_CODE',
643 'IEM_IS_32BIT_CODE',
644 'IEM_IS_64BIT_CODE',
645 ):
646 offParam = iai.McBlock.skipSpacesAt(sParam, offParam, len(sParam));
647 if sParam[offParam] != '(':
648 self.raiseProblem('Expected "(" following %s in "%s"' % (sRef, oStmt.renderCode(),));
649 (asMacroParams, offCloseParam) = iai.McBlock.extractParams(sParam, offParam);
650 if asMacroParams is None:
651 self.raiseProblem('Unable to find ")" for %s in "%s"' % (sRef, oStmt.renderCode(),));
652 offParam = offCloseParam + 1;
653
654 # Skip any dereference following it, unless it's a predicate like IEM_IS_GUEST_CPU_AMD.
655 if sRef not in ('IEM_IS_GUEST_CPU_AMD',
656 'IEM_IS_16BIT_CODE',
657 'IEM_IS_32BIT_CODE',
658 'IEM_IS_64BIT_CODE',
659 ):
660 offParam = iai.McBlock.skipSpacesAt(sParam, offParam, len(sParam));
661 if offParam + 2 <= len(sParam) and sParam[offParam : offParam + 2] == '->':
662 offParam = iai.McBlock.skipSpacesAt(sParam, offParam + 2, len(sParam));
663 while offParam < len(sParam) and (sParam[offParam].isalnum() or sParam[offParam] in '_.'):
664 offParam += 1;
665
666 # Skip constants, globals, types (casts), sizeof and macros.
667 elif ( sRef.startswith('IEM_OP_PRF_')
668 or sRef.startswith('IEM_ACCESS_')
669 or sRef.startswith('IEMINT_')
670 or sRef.startswith('X86_GREG_')
671 or sRef.startswith('X86_SREG_')
672 or sRef.startswith('X86_EFL_')
673 or sRef.startswith('X86_FSW_')
674 or sRef.startswith('X86_FCW_')
675 or sRef.startswith('X86_XCPT_')
676 or sRef.startswith('IEMMODE_')
677 or sRef.startswith('IEM_F_')
678 or sRef.startswith('g_')
679 or sRef in ( 'int8_t', 'int16_t', 'int32_t',
680 'INT8_C', 'INT16_C', 'INT32_C', 'INT64_C',
681 'UINT8_C', 'UINT16_C', 'UINT32_C', 'UINT64_C',
682 'UINT8_MAX', 'UINT16_MAX', 'UINT32_MAX', 'UINT64_MAX',
683 'INT8_MAX', 'INT16_MAX', 'INT32_MAX', 'INT64_MAX',
684 'INT8_MIN', 'INT16_MIN', 'INT32_MIN', 'INT64_MIN',
685 'sizeof', 'NOREF', 'RT_NOREF', 'IEMMODE_64BIT',
686 'NIL_RTGCPTR' ) ):
687 pass;
688
689 # Skip certain macro invocations.
690 # Any variable (non-field) and decoder fields in IEMCPU will need to be parameterized.
691 elif ( ( '.' not in sRef
692 and '-' not in sRef
693 and sRef not in ('pVCpu', ) )
694 or iai.McBlock.koReIemDecoderVars.search(sRef) is not None):
695 self.aoParamRefs.append(ThreadedParamRef(sRef, self.analyzeReferenceToType(sRef),
696 oStmt, iParam, offStart));
697 # Number.
698 elif ch.isdigit():
699 if ( ch == '0'
700 and offParam + 2 <= len(sParam)
701 and sParam[offParam + 1] in 'xX'
702 and sParam[offParam + 2] in self.ksHexDigits ):
703 offParam += 2;
704 while offParam < len(sParam) and sParam[offParam] in self.ksHexDigits:
705 offParam += 1;
706 else:
707 while offParam < len(sParam) and sParam[offParam].isdigit():
708 offParam += 1;
709 # Whatever else.
710 else:
711 offParam += 1;
712
713 # Traverse the branches of conditionals.
714 if isinstance(oStmt, iai.McStmtCond):
715 self.analyzeFindThreadedParamRefs(oStmt.aoIfBranch);
716 self.analyzeFindThreadedParamRefs(oStmt.aoElseBranch);
717 return True;
718
719 def analyzeVariation(self, aoStmts):
720 """
721 2nd part of the analysis, done on each variation.
722
723 The variations may differ in parameter requirements and will end up with
724 slightly different MC sequences. Thus this is done on each individually.
725
726 Returns dummy True - raises exception on trouble.
727 """
728 # Now scan the code for variables and field references that needs to
729 # be passed to the threaded function because they are related to the
730 # instruction decoding.
731 self.analyzeFindThreadedParamRefs(aoStmts);
732 self.analyzeConsolidateThreadedParamRefs();
733
734 # Morph the statement stream for the block into what we'll be using in the threaded function.
735 (self.aoStmtsForThreadedFunction, iParamRef) = self.analyzeMorphStmtForThreaded(aoStmts);
736 if iParamRef != len(self.aoParamRefs):
737 raise Exception('iParamRef=%s, expected %s!' % (iParamRef, len(self.aoParamRefs),));
738
739 return True;
740
741 def emitThreadedCallStmts(self, cchIndent):
742 """
743 Produces a generic C++ statment that emits a call to the thread function variation.
744 """
745 sCode = 'IEM_MC2_EMIT_CALL_%s(%s' % (self.cMinParams, self.getIndexName(), );
746 for iParam in range(self.cMinParams):
747 asFrags = [];
748 for aoRefs in self.dParamRefs.values():
749 oRef = aoRefs[0];
750 if oRef.iNewParam == iParam:
751 if oRef.offNewParam == 0:
752 asFrags.append('(uint64_t)(' + oRef.sOrgRef + ')');
753 else:
754 asFrags.append('((uint64_t)(%s) << %s)' % (oRef.sOrgRef, oRef.offNewParam));
755 assert asFrags;
756 sCode += ', ' + ' | '.join(asFrags);
757 sCode += ');';
758 return [iai.McCppGeneric(sCode, cchIndent = cchIndent),];
759
760
761class ThreadedFunction(object):
762 """
763 A threaded function.
764 """
765
766 def __init__(self, oMcBlock):
767 self.oMcBlock = oMcBlock # type: IEMAllInstructionsPython.McBlock
768 ## Variations for this block. There is at least one.
769 self.aoVariations = [] # type: list(ThreadedFunctionVariation)
770 ## Dictionary of local variables (IEM_MC_LOCAL[_CONST]) and call arguments (IEM_MC_ARG*).
771 self.dVariables = {} # type: dict(str,McStmtVar)
772
773 @staticmethod
774 def dummyInstance():
775 """ Gets a dummy instance. """
776 return ThreadedFunction(iai.McBlock('null', 999999999, 999999999,
777 iai.DecoderFunction('null', 999999999, 'nil', ('','')), 999999999));
778
779 def raiseProblem(self, sMessage):
780 """ Raises a problem. """
781 raise Exception('%s:%s: error: %s' % (self.oMcBlock.sSrcFile, self.oMcBlock.iBeginLine, sMessage, ));
782
783 def analyzeFindVariablesAndCallArgs(self, aoStmts):
784 """ Scans the statements for MC variables and call arguments. """
785 for oStmt in aoStmts:
786 if isinstance(oStmt, iai.McStmtVar):
787 if oStmt.sVarName in self.dVariables:
788 raise Exception('Variable %s is defined more than once!' % (oStmt.sVarName,));
789 self.dVariables[oStmt.sVarName] = oStmt.sVarName;
790
791 # There shouldn't be any variables or arguments declared inside if/
792 # else blocks, but scan them too to be on the safe side.
793 if isinstance(oStmt, iai.McStmtCond):
794 cBefore = len(self.dVariables);
795 self.analyzeFindVariablesAndCallArgs(oStmt.aoIfBranch);
796 self.analyzeFindVariablesAndCallArgs(oStmt.aoElseBranch);
797 if len(self.dVariables) != cBefore:
798 raise Exception('Variables/arguments defined in conditional branches!');
799 return True;
800
801 def analyze(self):
802 """
803 Analyzes the code, identifying the number of parameters it requires and such.
804
805 Returns dummy True - raises exception on trouble.
806 """
807
808 # Decode the block into a list/tree of McStmt objects.
809 aoStmts = self.oMcBlock.decode();
810
811 # Scan the statements for local variables and call arguments (self.dVariables).
812 self.analyzeFindVariablesAndCallArgs(aoStmts);
813
814 # Create variations if needed.
815 if iai.McStmt.findStmtByNames(aoStmts,
816 { 'IEM_MC_DEFER_TO_CIMPL_0_RET': True,
817 'IEM_MC_DEFER_TO_CIMPL_1_RET': True,
818 'IEM_MC_DEFER_TO_CIMPL_2_RET': True,
819 'IEM_MC_DEFER_TO_CIMPL_3_RET': True, }):
820 self.aoVariations = [ThreadedFunctionVariation(self, ThreadedFunctionVariation.ksVariation_Default),];
821
822 elif iai.McStmt.findStmtByNames(aoStmts, {'IEM_MC_CALC_RM_EFF_ADDR' : True,}):
823 self.aoVariations = [ThreadedFunctionVariation(self, sVar)
824 for sVar in ThreadedFunctionVariation.kasVariationsWithAddress];
825 else:
826 self.aoVariations = [ThreadedFunctionVariation(self, sVar)
827 for sVar in ThreadedFunctionVariation.kasVariationsWithoutAddress];
828
829 # Continue the analysis on each variation.
830 for oVariation in self.aoVariations:
831 oVariation.analyzeVariation(aoStmts);
832
833 return True;
834
835 def emitThreadedCallStmts(self):
836 """
837 Worker for morphInputCode that returns a list of statements that emits
838 the call to the threaded functions for the block.
839 """
840 # Special case for only default variation:
841 if len(self.aoVariations) == 1:
842 assert self.aoVariations[0].sVariation == ThreadedFunctionVariation.ksVariation_Default;
843 return self.aoVariations[0].emitThreadedCallStmts(0);
844
845 # Currently only have variations for address mode.
846 dByVari = { oVar.sVariation: oVar for oVar in self.aoVariations };
847
848 sExecMask = 'IEM_F_MODE_CPUMODE_MASK';
849 if ( ThreadedFunctionVariation.ksVariation_64_Addr32 in dByVari
850 or ThreadedFunctionVariation.ksVariation_32_Addr16 in dByVari
851 or ThreadedFunctionVariation.ksVariation_32_Flat in dByVari
852 or ThreadedFunctionVariation.ksVariation_16_Addr32 in dByVari):
853 sExecMask = '(IEM_F_MODE_CPUMODE_MASK | IEM_F_MODE_X86_FLAT_OR_PRE_386_MASK)';
854 aoStmts = [
855 iai.McCppGeneric('switch (pVCpu->iem.s.fExec & %s)' % (sExecMask,)),
856 iai.McCppGeneric('{'),
857 ];
858
859 if ThreadedFunctionVariation.ksVariation_64_Addr32 in dByVari:
860 aoStmts.extend([
861 iai.McCppGeneric('case IEMMODE_64BIT:', cchIndent = 4),
862 iai.McCppCond('RT_LIKELY(pVCpu->iem.s.enmEffAddrMode == IEMMODE_64BIT)', fDecode = True, cchIndent = 8,
863 aoIfBranch = dByVari[ThreadedFunctionVariation.ksVariation_64].emitThreadedCallStmts(0),
864 aoElseBranch = dByVari[ThreadedFunctionVariation.ksVariation_64_Addr32].emitThreadedCallStmts(0)),
865 iai.McCppGeneric('break;', cchIndent = 8),
866 ]);
867 elif ThreadedFunctionVariation.ksVariation_64 in dByVari:
868 aoStmts.append(iai.McCppGeneric('case IEMMODE_64BIT:', cchIndent = 4));
869 aoStmts.extend(dByVari[ThreadedFunctionVariation.ksVariation_64].emitThreadedCallStmts(8));
870 aoStmts.append(iai.McCppGeneric('break;', cchIndent = 8));
871
872 if ThreadedFunctionVariation.ksVariation_32_Addr16 in dByVari:
873 aoStmts.extend([
874 iai.McCppGeneric('case IEMMODE_32BIT | IEM_F_MODE_X86_FLAT_OR_PRE_386_MASK:', cchIndent = 4),
875 iai.McCppCond('RT_LIKELY(pVCpu->iem.s.enmEffAddrMode == IEMMODE_32BIT)', fDecode = True, cchIndent = 8,
876 aoIfBranch = dByVari[ThreadedFunctionVariation.ksVariation_32_Flat].emitThreadedCallStmts(0),
877 aoElseBranch = dByVari[ThreadedFunctionVariation.ksVariation_32_Addr16].emitThreadedCallStmts(0)),
878 iai.McCppGeneric('break;', cchIndent = 8),
879 iai.McCppGeneric('case IEMMODE_32BIT:', cchIndent = 4),
880 iai.McCppCond('RT_LIKELY(pVCpu->iem.s.enmEffAddrMode == IEMMODE_32BIT)', fDecode = True, cchIndent = 8,
881 aoIfBranch = dByVari[ThreadedFunctionVariation.ksVariation_32].emitThreadedCallStmts(0),
882 aoElseBranch = dByVari[ThreadedFunctionVariation.ksVariation_32_Addr16].emitThreadedCallStmts(0)),
883 iai.McCppGeneric('break;', cchIndent = 8),
884 ]);
885 elif ThreadedFunctionVariation.ksVariation_32 in dByVari:
886 aoStmts.append(iai.McCppGeneric('case IEMMODE_32BIT:', cchIndent = 4));
887 aoStmts.extend(dByVari[ThreadedFunctionVariation.ksVariation_32].emitThreadedCallStmts(8));
888 aoStmts.append(iai.McCppGeneric('break;', cchIndent = 8));
889
890 if ThreadedFunctionVariation.ksVariation_16_Addr32 in dByVari:
891 aoStmts.extend([
892 iai.McCppGeneric('case IEMMODE_16BIT:', cchIndent = 4),
893 iai.McCppCond('RT_LIKELY(pVCpu->iem.s.enmEffAddrMode == IEMMODE_16BIT)', fDecode = True, cchIndent = 8,
894 aoIfBranch = dByVari[ThreadedFunctionVariation.ksVariation_16].emitThreadedCallStmts(0),
895 aoElseBranch = dByVari[ThreadedFunctionVariation.ksVariation_16_Addr32].emitThreadedCallStmts(0)),
896 iai.McCppGeneric('break;', cchIndent = 8),
897 ]);
898 elif ThreadedFunctionVariation.ksVariation_16 in dByVari:
899 aoStmts.append(iai.McCppGeneric('case IEMMODE_16BIT:', cchIndent = 4));
900 aoStmts.extend(dByVari[ThreadedFunctionVariation.ksVariation_16].emitThreadedCallStmts(8));
901 aoStmts.append(iai.McCppGeneric('break;', cchIndent = 8));
902
903 if ThreadedFunctionVariation.ksVariation_16_Pre386 in dByVari:
904 aoStmts.append(iai.McCppGeneric('case IEMMODE_16BIT | IEM_F_MODE_X86_FLAT_OR_PRE_386_MASK:', cchIndent = 4));
905 aoStmts.extend(dByVari[ThreadedFunctionVariation.ksVariation_16_Pre386].emitThreadedCallStmts(8));
906 aoStmts.append(iai.McCppGeneric('break;', cchIndent = 8));
907
908 aoStmts.extend([
909 iai.McCppGeneric('IEM_NOT_REACHED_DEFAULT_CASE_RET();', cchIndent = 4),
910 iai.McCppGeneric('}'),
911 ]);
912
913 return aoStmts;
914
915 def morphInputCode(self, aoStmts, fCallEmitted = False, cDepth = 0):
916 """
917 Adjusts (& copies) the statements for the input/decoder so it will emit
918 calls to the right threaded functions for each block.
919
920 Returns list/tree of statements (aoStmts is not modified) and updated
921 fCallEmitted status.
922 """
923 #print('McBlock at %s:%s' % (os.path.split(self.oMcBlock.sSrcFile)[1], self.oMcBlock.iBeginLine,));
924 aoDecoderStmts = [];
925 for oStmt in aoStmts:
926 # Copy the statement. Make a deep copy to make sure we've got our own
927 # copies of all instance variables, even if a bit overkill at the moment.
928 oNewStmt = copy.deepcopy(oStmt);
929 aoDecoderStmts.append(oNewStmt);
930 #print('oNewStmt %s %s' % (oNewStmt.sName, len(oNewStmt.asParams),));
931
932 # If we haven't emitted the threaded function call yet, look for
933 # statements which it would naturally follow or preceed.
934 if not fCallEmitted:
935 if not oStmt.isCppStmt():
936 if ( oStmt.sName.startswith('IEM_MC_MAYBE_RAISE_') \
937 or (oStmt.sName.endswith('_AND_FINISH') and oStmt.sName.startswith('IEM_MC_'))
938 or oStmt.sName.startswith('IEM_MC_CALL_CIMPL_')
939 or oStmt.sName in ('IEM_MC_RAISE_DIVIDE_ERROR',)):
940 aoDecoderStmts.pop();
941 aoDecoderStmts.extend(self.emitThreadedCallStmts());
942 aoDecoderStmts.append(oNewStmt);
943 fCallEmitted = True;
944 elif ( oStmt.fDecode
945 and ( oStmt.asParams[0].find('IEMOP_HLP_DONE_') >= 0
946 or oStmt.asParams[0].find('IEMOP_HLP_DECODED_') >= 0)):
947 aoDecoderStmts.extend(self.emitThreadedCallStmts());
948 fCallEmitted = True;
949
950 # Process branches of conditionals recursively.
951 if isinstance(oStmt, iai.McStmtCond):
952 (oNewStmt.aoIfBranch, fCallEmitted1) = self.morphInputCode(oStmt.aoIfBranch, fCallEmitted, cDepth + 1);
953 if oStmt.aoElseBranch:
954 (oNewStmt.aoElseBranch, fCallEmitted2) = self.morphInputCode(oStmt.aoElseBranch, fCallEmitted, cDepth + 1);
955 else:
956 fCallEmitted2 = False;
957 fCallEmitted = fCallEmitted or (fCallEmitted1 and fCallEmitted2);
958
959 if not fCallEmitted and cDepth == 0:
960 self.raiseProblem('Unable to insert call to threaded function.');
961
962 return (aoDecoderStmts, fCallEmitted);
963
964
965 def generateInputCode(self):
966 """
967 Modifies the input code.
968 """
969 assert len(self.oMcBlock.asLines) > 2, "asLines=%s" % (self.oMcBlock.asLines,);
970 cchIndent = (self.oMcBlock.cchIndent + 3) // 4 * 4;
971 return iai.McStmt.renderCodeForList(self.morphInputCode(self.oMcBlock.aoStmts)[0],
972 cchIndent = cchIndent).replace('\n', ' /* gen */\n', 1);
973
974
975class IEMThreadedGenerator(object):
976 """
977 The threaded code generator & annotator.
978 """
979
980 def __init__(self):
981 self.aoThreadedFuncs = [] # type: list(ThreadedFunction)
982 self.oOptions = None # type: argparse.Namespace
983 self.aoParsers = [] # type: list(IEMAllInstructionsPython.SimpleParser)
984
985 #
986 # Processing.
987 #
988
989 def processInputFiles(self):
990 """
991 Process the input files.
992 """
993
994 # Parse the files.
995 self.aoParsers = iai.parseFiles(self.oOptions.asInFiles);
996
997 # Create threaded functions for the MC blocks.
998 self.aoThreadedFuncs = [ThreadedFunction(oMcBlock) for oMcBlock in iai.g_aoMcBlocks];
999
1000 # Analyze the threaded functions.
1001 dRawParamCounts = {};
1002 dMinParamCounts = {};
1003 for oThreadedFunction in self.aoThreadedFuncs:
1004 oThreadedFunction.analyze();
1005 for oVariation in oThreadedFunction.aoVariations:
1006 dRawParamCounts[len(oVariation.dParamRefs)] = dRawParamCounts.get(len(oVariation.dParamRefs), 0) + 1;
1007 dMinParamCounts[oVariation.cMinParams] = dMinParamCounts.get(oVariation.cMinParams, 0) + 1;
1008 print('debug: param count distribution, raw and optimized:', file = sys.stderr);
1009 for cCount in sorted({cBits: True for cBits in list(dRawParamCounts.keys()) + list(dMinParamCounts.keys())}.keys()):
1010 print('debug: %s params: %4s raw, %4s min'
1011 % (cCount, dRawParamCounts.get(cCount, 0), dMinParamCounts.get(cCount, 0)),
1012 file = sys.stderr);
1013
1014 return True;
1015
1016 #
1017 # Output
1018 #
1019
1020 def generateLicenseHeader(self):
1021 """
1022 Returns the lines for a license header.
1023 """
1024 return [
1025 '/*',
1026 ' * Autogenerated by $Id: IEMAllThreadedPython.py 100089 2023-06-07 01:39:11Z vboxsync $ ',
1027 ' * Do not edit!',
1028 ' */',
1029 '',
1030 '/*',
1031 ' * Copyright (C) 2023-' + str(datetime.date.today().year) + ' Oracle and/or its affiliates.',
1032 ' *',
1033 ' * This file is part of VirtualBox base platform packages, as',
1034 ' * available from https://www.virtualbox.org.',
1035 ' *',
1036 ' * This program is free software; you can redistribute it and/or',
1037 ' * modify it under the terms of the GNU General Public License',
1038 ' * as published by the Free Software Foundation, in version 3 of the',
1039 ' * License.',
1040 ' *',
1041 ' * This program is distributed in the hope that it will be useful, but',
1042 ' * WITHOUT ANY WARRANTY; without even the implied warranty of',
1043 ' * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU',
1044 ' * General Public License for more details.',
1045 ' *',
1046 ' * You should have received a copy of the GNU General Public License',
1047 ' * along with this program; if not, see <https://www.gnu.org/licenses>.',
1048 ' *',
1049 ' * The contents of this file may alternatively be used under the terms',
1050 ' * of the Common Development and Distribution License Version 1.0',
1051 ' * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included',
1052 ' * in the VirtualBox distribution, in which case the provisions of the',
1053 ' * CDDL are applicable instead of those of the GPL.',
1054 ' *',
1055 ' * You may elect to license modified versions of this file under the',
1056 ' * terms and conditions of either the GPL or the CDDL or both.',
1057 ' *',
1058 ' * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0',
1059 ' */',
1060 '',
1061 '',
1062 '',
1063 ];
1064
1065
1066 def generateThreadedFunctionsHeader(self, oOut):
1067 """
1068 Generates the threaded functions header file.
1069 Returns success indicator.
1070 """
1071
1072 asLines = self.generateLicenseHeader();
1073
1074 # Generate the threaded function table indexes.
1075 asLines += [
1076 'typedef enum IEMTHREADEDFUNCS',
1077 '{',
1078 ' kIemThreadedFunc_Invalid = 0,',
1079 ];
1080 for oThreadedFunction in self.aoThreadedFuncs:
1081 for oVariation in oThreadedFunction.aoVariations:
1082 asLines.append(' ' + oVariation.getIndexName() + ',');
1083 asLines += [
1084 ' kIemThreadedFunc_End',
1085 '} IEMTHREADEDFUNCS;',
1086 '',
1087 ];
1088
1089 # Prototype the function table.
1090 sFnType = 'typedef IEM_DECL_IMPL_TYPE(VBOXSTRICTRC, FNIEMTHREADEDFUNC, (PVMCPU pVCpu';
1091 for iParam in range(g_kcThreadedParams):
1092 sFnType += ', uint64_t uParam' + str(iParam);
1093 sFnType += '));'
1094
1095 asLines += [
1096 sFnType,
1097 'typedef FNIEMTHREADEDFUNC *PFNIEMTHREADEDFUNC;',
1098 '',
1099 'extern const PFNIEMTHREADEDFUNC g_apfnIemThreadedFunctions[kIemThreadedFunc_End];',
1100 ];
1101
1102 oOut.write('\n'.join(asLines));
1103 return True;
1104
1105 ksBitsToIntMask = {
1106 1: "UINT64_C(0x1)",
1107 2: "UINT64_C(0x3)",
1108 4: "UINT64_C(0xf)",
1109 8: "UINT64_C(0xff)",
1110 16: "UINT64_C(0xffff)",
1111 32: "UINT64_C(0xffffffff)",
1112 };
1113 def generateThreadedFunctionsSource(self, oOut):
1114 """
1115 Generates the threaded functions source file.
1116 Returns success indicator.
1117 """
1118
1119 asLines = self.generateLicenseHeader();
1120 oOut.write('\n'.join(asLines));
1121
1122 # Prepare the fixed bits.
1123 sParamList = '(PVMCPU pVCpu';
1124 for iParam in range(g_kcThreadedParams):
1125 sParamList += ', uint64_t uParam' + str(iParam);
1126 sParamList += '))\n';
1127
1128 #
1129 # Emit the function definitions.
1130 #
1131 for oThreadedFunction in self.aoThreadedFuncs:
1132 oMcBlock = oThreadedFunction.oMcBlock;
1133 for oVariation in oThreadedFunction.aoVariations:
1134 # Function header
1135 oOut.write( '\n'
1136 + '\n'
1137 + '/**\n'
1138 + ' * %s at line %s offset %s in %s%s\n'
1139 % (oMcBlock.sFunction, oMcBlock.iBeginLine, oMcBlock.offBeginLine,
1140 os.path.split(oMcBlock.sSrcFile)[1],
1141 ' (macro expansion)' if oMcBlock.iBeginLine == oMcBlock.iEndLine else '')
1142 + ' */\n'
1143 + 'static IEM_DECL_IMPL_DEF(VBOXSTRICTRC, ' + oVariation.getFunctionName() + ',\n'
1144 + ' ' + sParamList
1145 + '{\n');
1146
1147 aasVars = [];
1148 for aoRefs in oVariation.dParamRefs.values():
1149 oRef = aoRefs[0];
1150 if oRef.sType[0] != 'P':
1151 cBits = g_kdTypeInfo[oRef.sType][0];
1152 sType = g_kdTypeInfo[oRef.sType][2];
1153 else:
1154 cBits = 64;
1155 sType = oRef.sType;
1156
1157 sTypeDecl = sType + ' const';
1158
1159 if cBits == 64:
1160 assert oRef.offNewParam == 0;
1161 if sType == 'uint64_t':
1162 sUnpack = 'uParam%s;' % (oRef.iNewParam,);
1163 else:
1164 sUnpack = '(%s)uParam%s;' % (sType, oRef.iNewParam,);
1165 elif oRef.offNewParam == 0:
1166 sUnpack = '(%s)(uParam%s & %s);' % (sType, oRef.iNewParam, self.ksBitsToIntMask[cBits]);
1167 else:
1168 sUnpack = '(%s)((uParam%s >> %s) & %s);' \
1169 % (sType, oRef.iNewParam, oRef.offNewParam, self.ksBitsToIntMask[cBits]);
1170
1171 sComment = '/* %s - %s ref%s */' % (oRef.sOrgRef, len(aoRefs), 's' if len(aoRefs) != 1 else '',);
1172
1173 aasVars.append([ '%s:%02u' % (oRef.iNewParam, oRef.offNewParam),
1174 sTypeDecl, oRef.sNewName, sUnpack, sComment ]);
1175 acchVars = [0, 0, 0, 0, 0];
1176 for asVar in aasVars:
1177 for iCol, sStr in enumerate(asVar):
1178 acchVars[iCol] = max(acchVars[iCol], len(sStr));
1179 sFmt = ' %%-%ss %%-%ss = %%-%ss %%s\n' % (acchVars[1], acchVars[2], acchVars[3]);
1180 for asVar in sorted(aasVars):
1181 oOut.write(sFmt % (asVar[1], asVar[2], asVar[3], asVar[4],));
1182
1183 # RT_NOREF for unused parameters.
1184 if oVariation.cMinParams < g_kcThreadedParams:
1185 oOut.write(' RT_NOREF('
1186 + ', '.join(['uParam%u' % (i,) for i in range(oVariation.cMinParams, g_kcThreadedParams)])
1187 + ');\n');
1188
1189 # Now for the actual statements.
1190 oOut.write(iai.McStmt.renderCodeForList(oVariation.aoStmtsForThreadedFunction, cchIndent = 4));
1191
1192 oOut.write('}\n');
1193
1194
1195 #
1196 # Emit the function table.
1197 #
1198 oOut.write( '\n'
1199 + '\n'
1200 + '/**\n'
1201 + ' * Function table.\n'
1202 + ' */\n'
1203 + 'const PFNIEMTHREADEDFUNC g_apfnIemThreadedFunctions[kIemThreadedFunc_End] =\n'
1204 + '{\n'
1205 + ' /*Invalid*/ NULL, \n');
1206 iThreadedFunction = 0;
1207 for oThreadedFunction in self.aoThreadedFuncs:
1208 for oVariation in oThreadedFunction.aoVariations:
1209 iThreadedFunction += 1;
1210 oOut.write(' /*%4u*/ %s,\n' % (iThreadedFunction, oVariation.getFunctionName(),));
1211 oOut.write('};\n');
1212
1213 return True;
1214
1215 def getThreadedFunctionByIndex(self, idx):
1216 """
1217 Returns a ThreadedFunction object for the given index. If the index is
1218 out of bounds, a dummy is returned.
1219 """
1220 if idx < len(self.aoThreadedFuncs):
1221 return self.aoThreadedFuncs[idx];
1222 return ThreadedFunction.dummyInstance();
1223
1224 def findEndOfMcEndStmt(self, sLine, offEndStmt):
1225 """
1226 Helper that returns the line offset following the 'IEM_MC_END();'.
1227 """
1228 assert sLine[offEndStmt:].startswith('IEM_MC_END');
1229 off = sLine.find(';', offEndStmt + len('IEM_MC_END'));
1230 assert off > 0, 'sLine="%s"' % (sLine, );
1231 return off + 1 if off > 0 else 99999998;
1232
1233 def generateModifiedInput(self, oOut):
1234 """
1235 Generates the combined modified input source/header file.
1236 Returns success indicator.
1237 """
1238 #
1239 # File header.
1240 #
1241 oOut.write('\n'.join(self.generateLicenseHeader()));
1242
1243 #
1244 # ASSUMING that g_aoMcBlocks/self.aoThreadedFuncs are in self.aoParsers
1245 # order, we iterate aoThreadedFuncs in parallel to the lines from the
1246 # parsers and apply modifications where required.
1247 #
1248 iThreadedFunction = 0;
1249 oThreadedFunction = self.getThreadedFunctionByIndex(0);
1250 for oParser in self.aoParsers: # type: IEMAllInstructionsPython.SimpleParser
1251 oOut.write("\n\n/* ****** BEGIN %s ******* */\n" % (oParser.sSrcFile,));
1252
1253 iLine = 0;
1254 while iLine < len(oParser.asLines):
1255 sLine = oParser.asLines[iLine];
1256 iLine += 1; # iBeginLine and iEndLine are 1-based.
1257
1258 # Can we pass it thru?
1259 if ( iLine not in [oThreadedFunction.oMcBlock.iBeginLine, oThreadedFunction.oMcBlock.iEndLine]
1260 or oThreadedFunction.oMcBlock.sSrcFile != oParser.sSrcFile):
1261 oOut.write(sLine);
1262 #
1263 # Single MC block. Just extract it and insert the replacement.
1264 #
1265 elif oThreadedFunction.oMcBlock.iBeginLine != oThreadedFunction.oMcBlock.iEndLine:
1266 assert sLine.count('IEM_MC_') == 1;
1267 oOut.write(sLine[:oThreadedFunction.oMcBlock.offBeginLine]);
1268 sModified = oThreadedFunction.generateInputCode().strip();
1269 oOut.write(sModified);
1270
1271 iLine = oThreadedFunction.oMcBlock.iEndLine;
1272 sLine = oParser.asLines[iLine - 1];
1273 assert sLine.count('IEM_MC_') == 1;
1274 oOut.write(sLine[self.findEndOfMcEndStmt(sLine, oThreadedFunction.oMcBlock.offEndLine) : ]);
1275
1276 # Advance
1277 iThreadedFunction += 1;
1278 oThreadedFunction = self.getThreadedFunctionByIndex(iThreadedFunction);
1279 #
1280 # Macro expansion line that have sublines and may contain multiple MC blocks.
1281 #
1282 else:
1283 offLine = 0;
1284 while iLine == oThreadedFunction.oMcBlock.iBeginLine:
1285 oOut.write(sLine[offLine : oThreadedFunction.oMcBlock.offBeginLine]);
1286
1287 sModified = oThreadedFunction.generateInputCode().strip();
1288 assert sModified.startswith('IEM_MC_BEGIN'), 'sModified="%s"' % (sModified,);
1289 oOut.write(sModified);
1290
1291 offLine = self.findEndOfMcEndStmt(sLine, oThreadedFunction.oMcBlock.offEndLine);
1292
1293 # Advance
1294 iThreadedFunction += 1;
1295 oThreadedFunction = self.getThreadedFunctionByIndex(iThreadedFunction);
1296
1297 # Last line segment.
1298 if offLine < len(sLine):
1299 oOut.write(sLine[offLine : ]);
1300
1301 oOut.write("/* ****** END %s ******* */\n" % (oParser.sSrcFile,));
1302
1303 return True;
1304
1305 #
1306 # Main
1307 #
1308
1309 def main(self, asArgs):
1310 """
1311 C-like main function.
1312 Returns exit code.
1313 """
1314
1315 #
1316 # Parse arguments
1317 #
1318 sScriptDir = os.path.dirname(__file__);
1319 oParser = argparse.ArgumentParser(add_help = False);
1320 oParser.add_argument('asInFiles', metavar = 'input.cpp.h', nargs = '*',
1321 default = [os.path.join(sScriptDir, asFM[0]) for asFM in iai.g_aasAllInstrFilesAndDefaultMap],
1322 help = "Selection of VMMAll/IEMAllInstructions*.cpp.h files to use as input.");
1323 oParser.add_argument('--out-funcs-hdr', metavar = 'file-funcs.h', dest = 'sOutFileFuncsHdr', action = 'store',
1324 default = '-', help = 'The output header file for the functions.');
1325 oParser.add_argument('--out-funcs-cpp', metavar = 'file-funcs.cpp', dest = 'sOutFileFuncsCpp', action = 'store',
1326 default = '-', help = 'The output C++ file for the functions.');
1327 oParser.add_argument('--out-mod-input', metavar = 'file-instr.cpp.h', dest = 'sOutFileModInput', action = 'store',
1328 default = '-', help = 'The output C++/header file for the modified input instruction files.');
1329 oParser.add_argument('--help', '-h', '-?', action = 'help', help = 'Display help and exit.');
1330 oParser.add_argument('--version', '-V', action = 'version',
1331 version = 'r%s (IEMAllThreadedPython.py), r%s (IEMAllInstructionsPython.py)'
1332 % (__version__.split()[1], iai.__version__.split()[1],),
1333 help = 'Displays the version/revision of the script and exit.');
1334 self.oOptions = oParser.parse_args(asArgs[1:]);
1335 print("oOptions=%s" % (self.oOptions,));
1336
1337 #
1338 # Process the instructions specified in the IEM sources.
1339 #
1340 if self.processInputFiles():
1341 #
1342 # Generate the output files.
1343 #
1344 aaoOutputFiles = (
1345 ( self.oOptions.sOutFileFuncsHdr, self.generateThreadedFunctionsHeader ),
1346 ( self.oOptions.sOutFileFuncsCpp, self.generateThreadedFunctionsSource ),
1347 ( self.oOptions.sOutFileModInput, self.generateModifiedInput ),
1348 );
1349 fRc = True;
1350 for sOutFile, fnGenMethod in aaoOutputFiles:
1351 if sOutFile == '-':
1352 fRc = fnGenMethod(sys.stdout) and fRc;
1353 else:
1354 try:
1355 oOut = open(sOutFile, 'w'); # pylint: disable=consider-using-with,unspecified-encoding
1356 except Exception as oXcpt:
1357 print('error! Failed open "%s" for writing: %s' % (sOutFile, oXcpt,), file = sys.stderr);
1358 return 1;
1359 fRc = fnGenMethod(oOut) and fRc;
1360 oOut.close();
1361 if fRc:
1362 return 0;
1363
1364 return 1;
1365
1366
1367if __name__ == '__main__':
1368 sys.exit(IEMThreadedGenerator().main(sys.argv));
1369
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