VirtualBox

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

Last change on this file since 101189 was 101182, checked in by vboxsync, 15 months ago

VMM/IEM: Eliminated a parameter for three more builtin threaded functions. bugref:10370

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