VirtualBox

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

Last change on this file since 101722 was 101722, checked in by vboxsync, 13 months ago

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