VirtualBox

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

Last change on this file since 100856 was 100856, checked in by vboxsync, 21 months ago

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

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