VirtualBox

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

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

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