VirtualBox

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

Last change on this file since 101386 was 101305, checked in by vboxsync, 16 months ago

VMM/IEM: Covert zero arg count defer-CImpl instruction emulations as well. Some build fixes for arm. 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: 110.3 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3# $Id: IEMAllThrdPython.py 101305 2023-09-29 01:18:28Z 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: 101305 $"
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 101305 2023-09-29 01:18:28Z 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 and
1605 ## whether it has a native recompiler implementation.
1606 katBltIns = (
1607 ( 'DeferToCImpl0', 2, True ),
1608 ( 'CheckIrq', 0, False ),
1609 ( 'CheckMode', 1, False ),
1610 ( 'CheckHwInstrBps', 0, False ),
1611 ( 'CheckCsLim', 1, False ),
1612
1613 ( 'CheckCsLimAndOpcodes', 3, False ),
1614 ( 'CheckOpcodes', 3, False ),
1615 ( 'CheckOpcodesConsiderCsLim', 3, False ),
1616
1617 ( 'CheckCsLimAndPcAndOpcodes', 3, False ),
1618 ( 'CheckPcAndOpcodes', 3, False ),
1619 ( 'CheckPcAndOpcodesConsiderCsLim', 3, False ),
1620
1621 ( 'CheckCsLimAndOpcodesAcrossPageLoadingTlb', 3, False ),
1622 ( 'CheckOpcodesAcrossPageLoadingTlb', 3, False ),
1623 ( 'CheckOpcodesAcrossPageLoadingTlbConsiderCsLim', 2, False ),
1624
1625 ( 'CheckCsLimAndOpcodesLoadingTlb', 3, False ),
1626 ( 'CheckOpcodesLoadingTlb', 3, False ),
1627 ( 'CheckOpcodesLoadingTlbConsiderCsLim', 3, False ),
1628
1629 ( 'CheckCsLimAndOpcodesOnNextPageLoadingTlb', 2, False ),
1630 ( 'CheckOpcodesOnNextPageLoadingTlb', 2, False ),
1631 ( 'CheckOpcodesOnNextPageLoadingTlbConsiderCsLim', 2, False ),
1632
1633 ( 'CheckCsLimAndOpcodesOnNewPageLoadingTlb', 2, False ),
1634 ( 'CheckOpcodesOnNewPageLoadingTlb', 2, False ),
1635 ( 'CheckOpcodesOnNewPageLoadingTlbConsiderCsLim', 2, False ),
1636 );
1637
1638 def generateThreadedFunctionsHeader(self, oOut):
1639 """
1640 Generates the threaded functions header file.
1641 Returns success indicator.
1642 """
1643
1644 asLines = self.generateLicenseHeader();
1645
1646 # Generate the threaded function table indexes.
1647 asLines += [
1648 'typedef enum IEMTHREADEDFUNCS',
1649 '{',
1650 ' kIemThreadedFunc_Invalid = 0,',
1651 '',
1652 ' /*',
1653 ' * Predefined',
1654 ' */',
1655 ];
1656 asLines += [' kIemThreadedFunc_BltIn_%s,' % (sFuncNm,) for sFuncNm, _, _ in self.katBltIns];
1657
1658 iThreadedFunction = 1 + len(self.katBltIns);
1659 for sVariation in ThreadedFunctionVariation.kasVariationsEmitOrder:
1660 asLines += [
1661 '',
1662 ' /*',
1663 ' * Variation: ' + ThreadedFunctionVariation.kdVariationNames[sVariation] + '',
1664 ' */',
1665 ];
1666 for oThreadedFunction in self.aoThreadedFuncs:
1667 oVariation = oThreadedFunction.dVariations.get(sVariation, None);
1668 if oVariation:
1669 iThreadedFunction += 1;
1670 oVariation.iEnumValue = iThreadedFunction;
1671 asLines.append(' ' + oVariation.getIndexName() + ',');
1672 asLines += [
1673 ' kIemThreadedFunc_End',
1674 '} IEMTHREADEDFUNCS;',
1675 '',
1676 ];
1677
1678 # Prototype the function table.
1679 asLines += [
1680 'extern const PFNIEMTHREADEDFUNC g_apfnIemThreadedFunctions[kIemThreadedFunc_End];',
1681 '#if defined(IN_RING3) || defined(LOG_ENABLED)',
1682 'extern const char * const g_apszIemThreadedFunctions[kIemThreadedFunc_End];',
1683 '#endif',
1684 'extern uint8_t const g_acIemThreadedFunctionUsedArgs[kIemThreadedFunc_End];',
1685 ];
1686
1687 oOut.write('\n'.join(asLines));
1688 return True;
1689
1690 ksBitsToIntMask = {
1691 1: "UINT64_C(0x1)",
1692 2: "UINT64_C(0x3)",
1693 4: "UINT64_C(0xf)",
1694 8: "UINT64_C(0xff)",
1695 16: "UINT64_C(0xffff)",
1696 32: "UINT64_C(0xffffffff)",
1697 };
1698
1699 def generateFunctionParameterUnpacking(self, oVariation, oOut, asParams):
1700 """
1701 Outputs code for unpacking parameters.
1702 This is shared by the threaded and native code generators.
1703 """
1704 aasVars = [];
1705 for aoRefs in oVariation.dParamRefs.values():
1706 oRef = aoRefs[0];
1707 if oRef.sType[0] != 'P':
1708 cBits = g_kdTypeInfo[oRef.sType][0];
1709 sType = g_kdTypeInfo[oRef.sType][2];
1710 else:
1711 cBits = 64;
1712 sType = oRef.sType;
1713
1714 sTypeDecl = sType + ' const';
1715
1716 if cBits == 64:
1717 assert oRef.offNewParam == 0;
1718 if sType == 'uint64_t':
1719 sUnpack = '%s;' % (asParams[oRef.iNewParam],);
1720 else:
1721 sUnpack = '(%s)%s;' % (sType, asParams[oRef.iNewParam],);
1722 elif oRef.offNewParam == 0:
1723 sUnpack = '(%s)(%s & %s);' % (sType, asParams[oRef.iNewParam], self.ksBitsToIntMask[cBits]);
1724 else:
1725 sUnpack = '(%s)((%s >> %s) & %s);' \
1726 % (sType, asParams[oRef.iNewParam], oRef.offNewParam, self.ksBitsToIntMask[cBits]);
1727
1728 sComment = '/* %s - %s ref%s */' % (oRef.sOrgRef, len(aoRefs), 's' if len(aoRefs) != 1 else '',);
1729
1730 aasVars.append([ '%s:%02u' % (oRef.iNewParam, oRef.offNewParam),
1731 sTypeDecl, oRef.sNewName, sUnpack, sComment ]);
1732 acchVars = [0, 0, 0, 0, 0];
1733 for asVar in aasVars:
1734 for iCol, sStr in enumerate(asVar):
1735 acchVars[iCol] = max(acchVars[iCol], len(sStr));
1736 sFmt = ' %%-%ss %%-%ss = %%-%ss %%s\n' % (acchVars[1], acchVars[2], acchVars[3]);
1737 for asVar in sorted(aasVars):
1738 oOut.write(sFmt % (asVar[1], asVar[2], asVar[3], asVar[4],));
1739 return True;
1740
1741 kasThreadedParamNames = ('uParam0', 'uParam1', 'uParam2');
1742 def generateThreadedFunctionsSource(self, oOut):
1743 """
1744 Generates the threaded functions source file.
1745 Returns success indicator.
1746 """
1747
1748 asLines = self.generateLicenseHeader();
1749 oOut.write('\n'.join(asLines));
1750
1751 #
1752 # Emit the function definitions.
1753 #
1754 for sVariation in ThreadedFunctionVariation.kasVariationsEmitOrder:
1755 sVarName = ThreadedFunctionVariation.kdVariationNames[sVariation];
1756 oOut.write( '\n'
1757 + '\n'
1758 + '\n'
1759 + '\n'
1760 + '/*' + '*' * 128 + '\n'
1761 + '* Variation: ' + sVarName + ' ' * (129 - len(sVarName) - 15) + '*\n'
1762 + '*' * 128 + '*/\n');
1763
1764 for oThreadedFunction in self.aoThreadedFuncs:
1765 oVariation = oThreadedFunction.dVariations.get(sVariation, None);
1766 if oVariation:
1767 oMcBlock = oThreadedFunction.oMcBlock;
1768
1769 # Function header
1770 oOut.write( '\n'
1771 + '\n'
1772 + '/**\n'
1773 + ' * #%u: %s at line %s offset %s in %s%s\n'
1774 % (oVariation.iEnumValue, oMcBlock.sFunction, oMcBlock.iBeginLine, oMcBlock.offBeginLine,
1775 os.path.split(oMcBlock.sSrcFile)[1],
1776 ' (macro expansion)' if oMcBlock.iBeginLine == oMcBlock.iEndLine else '')
1777 + ' */\n'
1778 + 'static IEM_DECL_IEMTHREADEDFUNC_DEF(' + oVariation.getThreadedFunctionName() + ')\n'
1779 + '{\n');
1780
1781 # Unpack parameters.
1782 self.generateFunctionParameterUnpacking(oVariation, oOut, self.kasThreadedParamNames);
1783
1784 # RT_NOREF for unused parameters.
1785 if oVariation.cMinParams < g_kcThreadedParams:
1786 oOut.write(' RT_NOREF(' + ', '.join(self.kasThreadedParamNames[oVariation.cMinParams:]) + ');\n');
1787
1788 # Now for the actual statements.
1789 oOut.write(iai.McStmt.renderCodeForList(oVariation.aoStmtsForThreadedFunction, cchIndent = 4));
1790
1791 oOut.write('}\n');
1792
1793
1794 #
1795 # Generate the output tables in parallel.
1796 #
1797 asFuncTable = [
1798 '/**',
1799 ' * Function pointer table.',
1800 ' */',
1801 'PFNIEMTHREADEDFUNC const g_apfnIemThreadedFunctions[kIemThreadedFunc_End] =',
1802 '{',
1803 ' /*Invalid*/ NULL,',
1804 ];
1805 asNameTable = [
1806 '/**',
1807 ' * Function name table.',
1808 ' */',
1809 'const char * const g_apszIemThreadedFunctions[kIemThreadedFunc_End] =',
1810 '{',
1811 ' "Invalid",',
1812 ];
1813 asArgCntTab = [
1814 '/**',
1815 ' * Argument count table.',
1816 ' */',
1817 'uint8_t const g_acIemThreadedFunctionUsedArgs[kIemThreadedFunc_End] =',
1818 '{',
1819 ' 0, /*Invalid*/',
1820 ];
1821 aasTables = (asFuncTable, asNameTable, asArgCntTab,);
1822
1823 for asTable in aasTables:
1824 asTable.extend((
1825 '',
1826 ' /*',
1827 ' * Predefined.',
1828 ' */',
1829 ));
1830 for sFuncNm, cArgs, _ in self.katBltIns:
1831 asFuncTable.append(' iemThreadedFunc_BltIn_%s,' % (sFuncNm,));
1832 asNameTable.append(' "BltIn_%s",' % (sFuncNm,));
1833 asArgCntTab.append(' %d, /*BltIn_%s*/' % (cArgs, sFuncNm,));
1834
1835 iThreadedFunction = 1 + len(self.katBltIns);
1836 for sVariation in ThreadedFunctionVariation.kasVariationsEmitOrder:
1837 for asTable in aasTables:
1838 asTable.extend((
1839 '',
1840 ' /*',
1841 ' * Variation: ' + ThreadedFunctionVariation.kdVariationNames[sVariation],
1842 ' */',
1843 ));
1844 for oThreadedFunction in self.aoThreadedFuncs:
1845 oVariation = oThreadedFunction.dVariations.get(sVariation, None);
1846 if oVariation:
1847 iThreadedFunction += 1;
1848 assert oVariation.iEnumValue == iThreadedFunction;
1849 sName = oVariation.getThreadedFunctionName();
1850 asFuncTable.append(' /*%4u*/ %s,' % (iThreadedFunction, sName,));
1851 asNameTable.append(' /*%4u*/ "%s",' % (iThreadedFunction, sName,));
1852 asArgCntTab.append(' /*%4u*/ %d, /*%s*/' % (iThreadedFunction, oVariation.cMinParams, sName,));
1853
1854 for asTable in aasTables:
1855 asTable.append('};');
1856
1857 #
1858 # Output the tables.
1859 #
1860 oOut.write( '\n'
1861 + '\n');
1862 oOut.write('\n'.join(asFuncTable));
1863 oOut.write( '\n'
1864 + '\n'
1865 + '\n'
1866 + '#if defined(IN_RING3) || defined(LOG_ENABLED)\n');
1867 oOut.write('\n'.join(asNameTable));
1868 oOut.write( '\n'
1869 + '#endif /* IN_RING3 || LOG_ENABLED */\n'
1870 + '\n'
1871 + '\n');
1872 oOut.write('\n'.join(asArgCntTab));
1873 oOut.write('\n');
1874
1875 return True;
1876
1877 def generateNativeFunctionsHeader(self, oOut):
1878 """
1879 Generates the native recompiler functions header file.
1880 Returns success indicator.
1881 """
1882 if not self.oOptions.sNativeRecompilerArch:
1883 return True;
1884
1885 asLines = self.generateLicenseHeader();
1886
1887 # Prototype the function table.
1888 asLines += [
1889 'extern const PFNIEMNATIVERECOMPFUNC g_apfnIemNativeRecompileFunctions[kIemThreadedFunc_End];',
1890 '',
1891 ];
1892
1893 oOut.write('\n'.join(asLines));
1894 return True;
1895
1896 def generateNativeFunctionsSource(self, oOut):
1897 """
1898 Generates the native recompiler functions source file.
1899 Returns success indicator.
1900 """
1901 if not self.oOptions.sNativeRecompilerArch:
1902 return True;
1903
1904 #
1905 # The file header.
1906 #
1907 oOut.write('\n'.join(self.generateLicenseHeader()));
1908
1909 #
1910 # Emit the functions.
1911 #
1912 for sVariation in ThreadedFunctionVariation.kasVariationsEmitOrder:
1913 sVarName = ThreadedFunctionVariation.kdVariationNames[sVariation];
1914 oOut.write( '\n'
1915 + '\n'
1916 + '\n'
1917 + '\n'
1918 + '/*' + '*' * 128 + '\n'
1919 + '* Variation: ' + sVarName + ' ' * (129 - len(sVarName) - 15) + '*\n'
1920 + '*' * 128 + '*/\n');
1921
1922 for oThreadedFunction in self.aoThreadedFuncs:
1923 oVariation = oThreadedFunction.dVariations.get(sVariation, None) # type: ThreadedFunctionVariation
1924 if oVariation and oVariation.oNativeRecomp and oVariation.oNativeRecomp.isRecompilable():
1925 oMcBlock = oThreadedFunction.oMcBlock;
1926
1927 # Function header
1928 oOut.write( '\n'
1929 + '\n'
1930 + '/**\n'
1931 + ' * #%u: %s at line %s offset %s in %s%s\n'
1932 % (oVariation.iEnumValue, oMcBlock.sFunction, oMcBlock.iBeginLine, oMcBlock.offBeginLine,
1933 os.path.split(oMcBlock.sSrcFile)[1],
1934 ' (macro expansion)' if oMcBlock.iBeginLine == oMcBlock.iEndLine else '')
1935 + ' */\n'
1936 + 'static IEM_DECL_IEMNATIVERECOMPFUNC_DEF(' + oVariation.getNativeFunctionName() + ')\n'
1937 + '{\n');
1938
1939 # Unpack parameters.
1940 self.generateFunctionParameterUnpacking(oVariation, oOut,
1941 ('pCallEntry->auParams[0]',
1942 'pCallEntry->auParams[1]',
1943 'pCallEntry->auParams[2]',));
1944
1945 # Now for the actual statements.
1946 oOut.write(oVariation.oNativeRecomp.renderCode(cchIndent = 4));
1947
1948 oOut.write('}\n');
1949
1950 #
1951 # Output the function table.
1952 #
1953 oOut.write( '\n'
1954 + '\n'
1955 + '/*\n'
1956 + ' * Function table running parallel to g_apfnIemThreadedFunctions and friends.\n'
1957 + ' */\n'
1958 + 'const PFNIEMNATIVERECOMPFUNC g_apfnIemNativeRecompileFunctions[kIemThreadedFunc_End] =\n'
1959 + '{\n'
1960 + ' /*Invalid*/ NULL,'
1961 + '\n'
1962 + ' /*\n'
1963 + ' * Predefined.\n'
1964 + ' */\n'
1965 );
1966 for sFuncNm, _, fHaveRecompFunc in self.katBltIns:
1967 if fHaveRecompFunc:
1968 oOut.write(' iemNativeRecompFunc_BltIn_%s,\n' % (sFuncNm,))
1969 else:
1970 oOut.write(' NULL, /*BltIn_%s*/\n' % (sFuncNm,))
1971
1972 iThreadedFunction = 1 + len(self.katBltIns);
1973 for sVariation in ThreadedFunctionVariation.kasVariationsEmitOrder:
1974 oOut.write( ' /*\n'
1975 + ' * Variation: ' + ThreadedFunctionVariation.kdVariationNames[sVariation] + '\n'
1976 + ' */\n');
1977 for oThreadedFunction in self.aoThreadedFuncs:
1978 oVariation = oThreadedFunction.dVariations.get(sVariation, None);
1979 if oVariation:
1980 iThreadedFunction += 1;
1981 assert oVariation.iEnumValue == iThreadedFunction;
1982 sName = oVariation.getNativeFunctionName();
1983 if oVariation.oNativeRecomp and oVariation.oNativeRecomp.isRecompilable():
1984 oOut.write(' /*%4u*/ %s,\n' % (iThreadedFunction, sName,));
1985 else:
1986 oOut.write(' /*%4u*/ NULL /*%s*/,\n' % (iThreadedFunction, sName,));
1987
1988 oOut.write( '};\n'
1989 + '\n');
1990 return True;
1991
1992
1993 def getThreadedFunctionByIndex(self, idx):
1994 """
1995 Returns a ThreadedFunction object for the given index. If the index is
1996 out of bounds, a dummy is returned.
1997 """
1998 if idx < len(self.aoThreadedFuncs):
1999 return self.aoThreadedFuncs[idx];
2000 return ThreadedFunction.dummyInstance();
2001
2002 def generateModifiedInput(self, oOut, idxFile):
2003 """
2004 Generates the combined modified input source/header file.
2005 Returns success indicator.
2006 """
2007 #
2008 # File header and assert assumptions.
2009 #
2010 oOut.write('\n'.join(self.generateLicenseHeader()));
2011 oOut.write('AssertCompile((IEM_F_MODE_X86_FLAT_OR_PRE_386_MASK | IEM_F_MODE_CPUMODE_MASK) == 7);\n');
2012
2013 #
2014 # Iterate all parsers (input files) and output the ones related to the
2015 # file set given by idxFile.
2016 #
2017 for idxParser, oParser in enumerate(self.aoParsers): # type: int, IEMAllInstPython.SimpleParser
2018 # Is this included in the file set?
2019 sSrcBaseFile = os.path.basename(oParser.sSrcFile).lower();
2020 fInclude = -1;
2021 for aoInfo in iai.g_aaoAllInstrFilesAndDefaultMapAndSet:
2022 if sSrcBaseFile == aoInfo[0].lower():
2023 fInclude = aoInfo[2] in (-1, idxFile);
2024 break;
2025 if fInclude is not True:
2026 assert fInclude is False;
2027 continue;
2028
2029 # Output it.
2030 oOut.write("\n\n/* ****** BEGIN %s ******* */\n" % (oParser.sSrcFile,));
2031
2032 iThreadedFunction = self.aidxFirstFunctions[idxParser];
2033 oThreadedFunction = self.getThreadedFunctionByIndex(iThreadedFunction);
2034 iLine = 0;
2035 while iLine < len(oParser.asLines):
2036 sLine = oParser.asLines[iLine];
2037 iLine += 1; # iBeginLine and iEndLine are 1-based.
2038
2039 # Can we pass it thru?
2040 if ( iLine not in [oThreadedFunction.oMcBlock.iBeginLine, oThreadedFunction.oMcBlock.iEndLine]
2041 or oThreadedFunction.oMcBlock.sSrcFile != oParser.sSrcFile):
2042 oOut.write(sLine);
2043 #
2044 # Single MC block. Just extract it and insert the replacement.
2045 #
2046 elif oThreadedFunction.oMcBlock.iBeginLine != oThreadedFunction.oMcBlock.iEndLine:
2047 assert sLine.count('IEM_MC_') - sLine.count('IEM_MC_F_') == 1, 'sLine="%s"' % (sLine,);
2048 oOut.write(sLine[:oThreadedFunction.oMcBlock.offBeginLine]);
2049 sModified = oThreadedFunction.generateInputCode().strip();
2050 oOut.write(sModified);
2051
2052 iLine = oThreadedFunction.oMcBlock.iEndLine;
2053 sLine = oParser.asLines[iLine - 1];
2054 assert sLine.count('IEM_MC_') - sLine.count('IEM_MC_F_') == 1 or len(oThreadedFunction.oMcBlock.aoStmts) == 1;
2055 oOut.write(sLine[oThreadedFunction.oMcBlock.offAfterEnd : ]);
2056
2057 # Advance
2058 iThreadedFunction += 1;
2059 oThreadedFunction = self.getThreadedFunctionByIndex(iThreadedFunction);
2060 #
2061 # Macro expansion line that have sublines and may contain multiple MC blocks.
2062 #
2063 else:
2064 offLine = 0;
2065 while iLine == oThreadedFunction.oMcBlock.iBeginLine:
2066 oOut.write(sLine[offLine : oThreadedFunction.oMcBlock.offBeginLine]);
2067
2068 sModified = oThreadedFunction.generateInputCode().strip();
2069 assert ( sModified.startswith('IEM_MC_BEGIN')
2070 or (sModified.find('IEM_MC_DEFER_TO_CIMPL_') > 0 and sModified.strip().startswith('{\n'))
2071 or sModified.startswith('pVCpu->iem.s.fEndTb = true')
2072 ), 'sModified="%s"' % (sModified,);
2073 oOut.write(sModified);
2074
2075 offLine = oThreadedFunction.oMcBlock.offAfterEnd;
2076
2077 # Advance
2078 iThreadedFunction += 1;
2079 oThreadedFunction = self.getThreadedFunctionByIndex(iThreadedFunction);
2080
2081 # Last line segment.
2082 if offLine < len(sLine):
2083 oOut.write(sLine[offLine : ]);
2084
2085 oOut.write("/* ****** END %s ******* */\n" % (oParser.sSrcFile,));
2086
2087 return True;
2088
2089 def generateModifiedInput1(self, oOut):
2090 """
2091 Generates the combined modified input source/header file, part 1.
2092 Returns success indicator.
2093 """
2094 return self.generateModifiedInput(oOut, 1);
2095
2096 def generateModifiedInput2(self, oOut):
2097 """
2098 Generates the combined modified input source/header file, part 2.
2099 Returns success indicator.
2100 """
2101 return self.generateModifiedInput(oOut, 2);
2102
2103 def generateModifiedInput3(self, oOut):
2104 """
2105 Generates the combined modified input source/header file, part 3.
2106 Returns success indicator.
2107 """
2108 return self.generateModifiedInput(oOut, 3);
2109
2110 def generateModifiedInput4(self, oOut):
2111 """
2112 Generates the combined modified input source/header file, part 4.
2113 Returns success indicator.
2114 """
2115 return self.generateModifiedInput(oOut, 4);
2116
2117
2118 #
2119 # Main
2120 #
2121
2122 def main(self, asArgs):
2123 """
2124 C-like main function.
2125 Returns exit code.
2126 """
2127
2128 #
2129 # Parse arguments
2130 #
2131 sScriptDir = os.path.dirname(__file__);
2132 oParser = argparse.ArgumentParser(add_help = False);
2133 oParser.add_argument('asInFiles',
2134 metavar = 'input.cpp.h',
2135 nargs = '*',
2136 default = [os.path.join(sScriptDir, aoInfo[0])
2137 for aoInfo in iai.g_aaoAllInstrFilesAndDefaultMapAndSet],
2138 help = "Selection of VMMAll/IEMAllInst*.cpp.h files to use as input.");
2139 oParser.add_argument('--out-thrd-funcs-hdr',
2140 metavar = 'file-thrd-funcs.h',
2141 dest = 'sOutFileThrdFuncsHdr',
2142 action = 'store',
2143 default = '-',
2144 help = 'The output header file for the threaded functions.');
2145 oParser.add_argument('--out-thrd-funcs-cpp',
2146 metavar = 'file-thrd-funcs.cpp',
2147 dest = 'sOutFileThrdFuncsCpp',
2148 action = 'store',
2149 default = '-',
2150 help = 'The output C++ file for the threaded functions.');
2151 oParser.add_argument('--out-n8ve-funcs-hdr',
2152 metavar = 'file-n8tv-funcs.h',
2153 dest = 'sOutFileN8veFuncsHdr',
2154 action = 'store',
2155 default = '-',
2156 help = 'The output header file for the native recompiler functions.');
2157 oParser.add_argument('--out-n8ve-funcs-cpp',
2158 metavar = 'file-n8tv-funcs.cpp',
2159 dest = 'sOutFileN8veFuncsCpp',
2160 action = 'store',
2161 default = '-',
2162 help = 'The output C++ file for the native recompiler functions.');
2163 oParser.add_argument('--native-arch',
2164 metavar = 'arch',
2165 dest = 'sNativeRecompilerArch',
2166 action = 'store',
2167 default = None,
2168 help = 'The host architecture for the native recompiler. No default as it enables/disables '
2169 + 'generating the files related to native recompilation.');
2170 oParser.add_argument('--out-mod-input1',
2171 metavar = 'file-instr.cpp.h',
2172 dest = 'sOutFileModInput1',
2173 action = 'store',
2174 default = '-',
2175 help = 'The output C++/header file for modified input instruction files part 1.');
2176 oParser.add_argument('--out-mod-input2',
2177 metavar = 'file-instr.cpp.h',
2178 dest = 'sOutFileModInput2',
2179 action = 'store',
2180 default = '-',
2181 help = 'The output C++/header file for modified input instruction files part 2.');
2182 oParser.add_argument('--out-mod-input3',
2183 metavar = 'file-instr.cpp.h',
2184 dest = 'sOutFileModInput3',
2185 action = 'store',
2186 default = '-',
2187 help = 'The output C++/header file for modified input instruction files part 3.');
2188 oParser.add_argument('--out-mod-input4',
2189 metavar = 'file-instr.cpp.h',
2190 dest = 'sOutFileModInput4',
2191 action = 'store',
2192 default = '-',
2193 help = 'The output C++/header file for modified input instruction files part 4.');
2194 oParser.add_argument('--help', '-h', '-?',
2195 action = 'help',
2196 help = 'Display help and exit.');
2197 oParser.add_argument('--version', '-V',
2198 action = 'version',
2199 version = 'r%s (IEMAllThreadedPython.py), r%s (IEMAllInstPython.py)'
2200 % (__version__.split()[1], iai.__version__.split()[1],),
2201 help = 'Displays the version/revision of the script and exit.');
2202 self.oOptions = oParser.parse_args(asArgs[1:]);
2203 print("oOptions=%s" % (self.oOptions,));
2204
2205 #
2206 # Process the instructions specified in the IEM sources.
2207 #
2208 if self.processInputFiles(self.oOptions.sNativeRecompilerArch):
2209 #
2210 # Generate the output files.
2211 #
2212 aaoOutputFiles = (
2213 ( self.oOptions.sOutFileThrdFuncsHdr, self.generateThreadedFunctionsHeader ),
2214 ( self.oOptions.sOutFileThrdFuncsCpp, self.generateThreadedFunctionsSource ),
2215 ( self.oOptions.sOutFileN8veFuncsHdr, self.generateNativeFunctionsHeader ),
2216 ( self.oOptions.sOutFileN8veFuncsCpp, self.generateNativeFunctionsSource ),
2217 ( self.oOptions.sOutFileModInput1, self.generateModifiedInput1 ),
2218 ( self.oOptions.sOutFileModInput2, self.generateModifiedInput2 ),
2219 ( self.oOptions.sOutFileModInput3, self.generateModifiedInput3 ),
2220 ( self.oOptions.sOutFileModInput4, self.generateModifiedInput4 ),
2221 );
2222 fRc = True;
2223 for sOutFile, fnGenMethod in aaoOutputFiles:
2224 if sOutFile == '-':
2225 fRc = fnGenMethod(sys.stdout) and fRc;
2226 else:
2227 try:
2228 oOut = open(sOutFile, 'w'); # pylint: disable=consider-using-with,unspecified-encoding
2229 except Exception as oXcpt:
2230 print('error! Failed open "%s" for writing: %s' % (sOutFile, oXcpt,), file = sys.stderr);
2231 return 1;
2232 fRc = fnGenMethod(oOut) and fRc;
2233 oOut.close();
2234 if fRc:
2235 return 0;
2236
2237 return 1;
2238
2239
2240if __name__ == '__main__':
2241 sys.exit(IEMThreadedGenerator().main(sys.argv));
2242
Note: See TracBrowser for help on using the repository browser.

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