VirtualBox

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

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

VMM/IEM: Started on bugref:10371 ...

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