VirtualBox

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

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

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