VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMAll/IEMAllThreadedPython.py@ 100148

Last change on this file since 100148 was 100148, checked in by vboxsync, 22 months ago

VMM/IEM: Made the python scripts pick up and deal with the IEM_MC_DEFER_TO_CIMPL_[0-5]_RET short-hand macros. bugref:10369

  • Property svn:eol-style set to LF
  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
File size: 70.7 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3# $Id: IEMAllThreadedPython.py 100148 2023-06-10 19:44:02Z vboxsync $
4# pylint: disable=invalid-name
5
6"""
7Annotates and generates threaded functions from IEMAllInstructions*.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: 100148 $"
35
36# Standard python imports.
37import copy;
38import datetime;
39import os;
40import re;
41import sys;
42import argparse;
43
44import IEMAllInstructionsPython 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, 'uint8_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 ## @note Effective operand size is generally handled in the decoder, at present
133 ## we only do variations on addressing and memory accessing.
134 ## @todo Blocks without addressing should have 64-bit and 32-bit PC update
135 ## variations to reduce code size (see iemRegAddToRip).
136 ## @{
137 ksVariation_Default = ''; ##< No variations - only used by IEM_MC_DEFER_TO_CIMPL_X_RET.
138 ksVariation_16 = '_16'; ##< 16-bit mode code (386+).
139 ksVariation_16_Addr32 = '_16_Addr32'; ##< 16-bit mode code (386+), address size prefixed to 32-bit addressing.
140 ksVariation_16_Pre386 = '_16_Pre386'; ##< 16-bit mode code, pre-386 CPU target.
141 ksVariation_32 = '_32'; ##< 32-bit mode code (386+).
142 ksVariation_32_Flat = '_32_Flat'; ##< 32-bit mode code (386+) with CS, DS, E,S and SS flat and 4GB wide.
143 ksVariation_32_Addr16 = '_32_Addr16'; ##< 32-bit mode code (386+), address size prefixed to 16-bit addressing.
144 ksVariation_64 = '_64'; ##< 64-bit mode code.
145 ksVariation_64_Addr32 = '_64_Addr32'; ##< 64-bit mode code, address size prefixed to 32-bit addressing.
146 kasVariations = (
147 ksVariation_Default,
148 ksVariation_16,
149 ksVariation_16_Addr32,
150 ksVariation_16_Pre386,
151 ksVariation_32,
152 ksVariation_32_Flat,
153 ksVariation_32_Addr16,
154 ksVariation_64,
155 ksVariation_64_Addr32,
156 );
157 kasVariationsWithoutAddress = (
158 ksVariation_16,
159 ksVariation_16_Pre386,
160 ksVariation_32,
161 ksVariation_64,
162 );
163 kasVariationsWithAddress = (
164 ksVariation_16,
165 ksVariation_16_Addr32,
166 ksVariation_16_Pre386,
167 ksVariation_32,
168 ksVariation_32_Flat,
169 ksVariation_32_Addr16,
170 ksVariation_64,
171 ksVariation_64_Addr32,
172 );
173 kasVariationsEmitOrder = (
174 ksVariation_Default,
175 ksVariation_64,
176 ksVariation_32_Flat,
177 ksVariation_32,
178 ksVariation_16,
179 ksVariation_16_Addr32,
180 ksVariation_16_Pre386,
181 ksVariation_32_Addr16,
182 ksVariation_64_Addr32,
183 );
184 kdVariationNames = {
185 ksVariation_Default: 'defer-to-cimpl',
186 ksVariation_16: '16-bit',
187 ksVariation_16_Addr32: '16-bit w/ address prefix (Addr32)',
188 ksVariation_16_Pre386: '16-bit on pre-386 CPU',
189 ksVariation_32: '32-bit',
190 ksVariation_32_Flat: '32-bit flat and wide open CS, SS, DS and ES',
191 ksVariation_32_Addr16: '32-bit w/ address prefix (Addr16)',
192 ksVariation_64: '64-bit',
193 ksVariation_64_Addr32: '64-bit w/ address prefix (Addr32)',
194
195 };
196 ## @}
197
198 def __init__(self, oThreadedFunction, sVariation = ksVariation_Default):
199 self.oParent = oThreadedFunction # type: ThreadedFunction
200 ##< ksVariation_Xxxx.
201 self.sVariation = sVariation
202
203 ## Threaded function parameter references.
204 self.aoParamRefs = [] # type: list(ThreadedParamRef)
205 ## Unique parameter references.
206 self.dParamRefs = {} # type: dict(str,list(ThreadedParamRef))
207 ## Minimum number of parameters to the threaded function.
208 self.cMinParams = 0;
209
210 ## List/tree of statements for the threaded function.
211 self.aoStmtsForThreadedFunction = [] # type: list(McStmt)
212
213 ## Function enum number, for verification. Set by generateThreadedFunctionsHeader.
214 self.iEnumValue = -1;
215
216 def getIndexName(self):
217 sName = self.oParent.oMcBlock.sFunction;
218 if sName.startswith('iemOp_'):
219 sName = sName[len('iemOp_'):];
220 if self.oParent.oMcBlock.iInFunction == 0:
221 return 'kIemThreadedFunc_%s%s' % ( sName, self.sVariation, );
222 return 'kIemThreadedFunc_%s_%s%s' % ( sName, self.oParent.oMcBlock.iInFunction, self.sVariation, );
223
224 def getFunctionName(self):
225 sName = self.oParent.oMcBlock.sFunction;
226 if sName.startswith('iemOp_'):
227 sName = sName[len('iemOp_'):];
228 if self.oParent.oMcBlock.iInFunction == 0:
229 return 'iemThreadedFunc_%s%s' % ( sName, self.sVariation, );
230 return 'iemThreadedFunc_%s_%s%s' % ( sName, self.oParent.oMcBlock.iInFunction, self.sVariation, );
231
232 #
233 # Analysis and code morphing.
234 #
235
236 def raiseProblem(self, sMessage):
237 """ Raises a problem. """
238 self.oParent.raiseProblem(sMessage);
239
240 def analyzeReferenceToType(self, sRef):
241 """
242 Translates a variable or structure reference to a type.
243 Returns type name.
244 Raises exception if unable to figure it out.
245 """
246 ch0 = sRef[0];
247 if ch0 == 'u':
248 if sRef.startswith('u32'):
249 return 'uint32_t';
250 if sRef.startswith('u8') or sRef == 'uReg':
251 return 'uint8_t';
252 if sRef.startswith('u64'):
253 return 'uint64_t';
254 if sRef.startswith('u16'):
255 return 'uint16_t';
256 elif ch0 == 'b':
257 return 'uint8_t';
258 elif ch0 == 'f':
259 return 'bool';
260 elif ch0 == 'i':
261 if sRef.startswith('i8'):
262 return 'int8_t';
263 if sRef.startswith('i16'):
264 return 'int16_t';
265 if sRef.startswith('i32'):
266 return 'int32_t';
267 if sRef.startswith('i64'):
268 return 'int64_t';
269 if sRef in ('iReg', 'iGReg', 'iSegReg', 'iSrcReg', 'iDstReg', 'iCrReg'):
270 return 'uint8_t';
271 elif ch0 == 'p':
272 if sRef.find('-') < 0:
273 return 'uintptr_t';
274 if sRef.startswith('pVCpu->iem.s.'):
275 sField = sRef[len('pVCpu->iem.s.') : ];
276 if sField in g_kdIemFieldToType:
277 if g_kdIemFieldToType[sField][0]:
278 return g_kdIemFieldToType[sField][0];
279 elif ch0 == 'G' and sRef.startswith('GCPtr'):
280 return 'uint64_t';
281 elif ch0 == 'e':
282 if sRef == 'enmEffOpSize':
283 return 'IEMMODE';
284 elif ch0 == 'o':
285 if sRef.startswith('off32'):
286 return 'uint32_t';
287 elif sRef == 'cbFrame': # enter
288 return 'uint16_t';
289 elif sRef == 'cShift': ## @todo risky
290 return 'uint8_t';
291
292 self.raiseProblem('Unknown reference: %s' % (sRef,));
293 return None; # Shut up pylint 2.16.2.
294
295 def analyzeCallToType(self, sFnRef):
296 """
297 Determins the type of an indirect function call.
298 """
299 assert sFnRef[0] == 'p';
300
301 #
302 # Simple?
303 #
304 if sFnRef.find('-') < 0:
305 oDecoderFunction = self.oParent.oMcBlock.oFunction;
306
307 # Try the argument list of the function defintion macro invocation first.
308 iArg = 2;
309 while iArg < len(oDecoderFunction.asDefArgs):
310 if sFnRef == oDecoderFunction.asDefArgs[iArg]:
311 return oDecoderFunction.asDefArgs[iArg - 1];
312 iArg += 1;
313
314 # Then check out line that includes the word and looks like a variable declaration.
315 oRe = re.compile(' +(P[A-Z0-9_]+|const +IEMOP[A-Z0-9_]+ *[*]) +(const |) *' + sFnRef + ' *(;|=)');
316 for sLine in oDecoderFunction.asLines:
317 oMatch = oRe.match(sLine);
318 if oMatch:
319 if not oMatch.group(1).startswith('const'):
320 return oMatch.group(1);
321 return 'PC' + oMatch.group(1)[len('const ') : -1].strip();
322
323 #
324 # Deal with the pImpl->pfnXxx:
325 #
326 elif sFnRef.startswith('pImpl->pfn'):
327 sMember = sFnRef[len('pImpl->') : ];
328 sBaseType = self.analyzeCallToType('pImpl');
329 offBits = sMember.rfind('U') + 1;
330 if sBaseType == 'PCIEMOPBINSIZES': return 'PFNIEMAIMPLBINU' + sMember[offBits:];
331 if sBaseType == 'PCIEMOPUNARYSIZES': return 'PFNIEMAIMPLUNARYU' + sMember[offBits:];
332 if sBaseType == 'PCIEMOPSHIFTSIZES': return 'PFNIEMAIMPLSHIFTU' + sMember[offBits:];
333 if sBaseType == 'PCIEMOPSHIFTDBLSIZES': return 'PFNIEMAIMPLSHIFTDBLU' + sMember[offBits:];
334 if sBaseType == 'PCIEMOPMULDIVSIZES': return 'PFNIEMAIMPLMULDIVU' + sMember[offBits:];
335 if sBaseType == 'PCIEMOPMEDIAF3': return 'PFNIEMAIMPLMEDIAF3U' + sMember[offBits:];
336 if sBaseType == 'PCIEMOPMEDIAOPTF3': return 'PFNIEMAIMPLMEDIAOPTF3U' + sMember[offBits:];
337 if sBaseType == 'PCIEMOPMEDIAOPTF2': return 'PFNIEMAIMPLMEDIAOPTF2U' + sMember[offBits:];
338 if sBaseType == 'PCIEMOPMEDIAOPTF3IMM8': return 'PFNIEMAIMPLMEDIAOPTF3U' + sMember[offBits:] + 'IMM8';
339 if sBaseType == 'PCIEMOPBLENDOP': return 'PFNIEMAIMPLAVXBLENDU' + sMember[offBits:];
340
341 self.raiseProblem('Unknown call reference: %s::%s (%s)' % (sBaseType, sMember, sFnRef,));
342
343 self.raiseProblem('Unknown call reference: %s' % (sFnRef,));
344 return None; # Shut up pylint 2.16.2.
345
346 def analyze8BitGRegStmt(self, oStmt):
347 """
348 Gets the 8-bit general purpose register access details of the given statement.
349 ASSUMES the statement is one accessing an 8-bit GREG.
350 """
351 idxReg = 0;
352 if ( oStmt.sName.find('_FETCH_') > 0
353 or oStmt.sName.find('_REF_') > 0
354 or oStmt.sName.find('_TO_LOCAL') > 0):
355 idxReg = 1;
356
357 sRegRef = oStmt.asParams[idxReg];
358 sOrgExpr = '((%s) < 4 || (pVCpu->iem.s.fPrefixes & IEM_OP_PRF_REX) ? (%s) : (%s) | 16)' % (sRegRef, sRegRef, sRegRef);
359
360 if sRegRef.find('IEM_GET_MODRM_RM') > 0: sStdRef = 'bRmRm8Ex';
361 elif sRegRef.find('IEM_GET_MODRM_REG') > 0: sStdRef = 'bRmReg8Ex';
362 else: sStdRef = 'bOther8Ex';
363
364 return (idxReg, sOrgExpr, sStdRef);
365
366
367 def analyzeMorphStmtForThreaded(self, aoStmts, iParamRef = 0):
368 """
369 Transforms (copy) the statements into those for the threaded function.
370
371 Returns list/tree of statements (aoStmts is not modified) and the new
372 iParamRef value.
373 """
374 #
375 # We'll be traversing aoParamRefs in parallel to the statements, so we
376 # must match the traversal in analyzeFindThreadedParamRefs exactly.
377 #
378 #print('McBlock at %s:%s' % (os.path.split(self.oMcBlock.sSrcFile)[1], self.oMcBlock.iBeginLine,));
379 aoThreadedStmts = [];
380 for oStmt in aoStmts:
381 # Skip C++ statements that is purely related to decoding.
382 if not oStmt.isCppStmt() or not oStmt.fDecode:
383 # Copy the statement. Make a deep copy to make sure we've got our own
384 # copies of all instance variables, even if a bit overkill at the moment.
385 oNewStmt = copy.deepcopy(oStmt);
386 aoThreadedStmts.append(oNewStmt);
387 #print('oNewStmt %s %s' % (oNewStmt.sName, len(oNewStmt.asParams),));
388
389 # If the statement has parameter references, process the relevant parameters.
390 # We grab the references relevant to this statement and apply them in reserve order.
391 if iParamRef < len(self.aoParamRefs) and self.aoParamRefs[iParamRef].oStmt == oStmt:
392 iParamRefFirst = iParamRef;
393 while True:
394 iParamRef += 1;
395 if iParamRef >= len(self.aoParamRefs) or self.aoParamRefs[iParamRef].oStmt != oStmt:
396 break;
397
398 #print('iParamRefFirst=%s iParamRef=%s' % (iParamRefFirst, iParamRef));
399 for iCurRef in range(iParamRef - 1, iParamRefFirst - 1, -1):
400 oCurRef = self.aoParamRefs[iCurRef];
401 if oCurRef.iParam is not None:
402 assert oCurRef.oStmt == oStmt;
403 #print('iCurRef=%s iParam=%s sOrgRef=%s' % (iCurRef, oCurRef.iParam, oCurRef.sOrgRef));
404 sSrcParam = oNewStmt.asParams[oCurRef.iParam];
405 assert ( sSrcParam[oCurRef.offParam : oCurRef.offParam + len(oCurRef.sOrgRef)] == oCurRef.sOrgRef
406 or oCurRef.fCustomRef), \
407 'offParam=%s sOrgRef=%s iParam=%s oStmt.sName=%s sSrcParam=%s<eos>' \
408 % (oCurRef.offParam, oCurRef.sOrgRef, oCurRef.iParam, oStmt.sName, sSrcParam);
409 oNewStmt.asParams[oCurRef.iParam] = sSrcParam[0 : oCurRef.offParam] \
410 + oCurRef.sNewName \
411 + sSrcParam[oCurRef.offParam + len(oCurRef.sOrgRef) : ];
412
413 # Morph IEM_MC_CALC_RM_EFF_ADDR into IEM_MC_CALC_RM_EFF_ADDR_THREADED ...
414 if oNewStmt.sName == 'IEM_MC_CALC_RM_EFF_ADDR':
415 assert self.sVariation != self.ksVariation_Default;
416 oNewStmt.sName = 'IEM_MC_CALC_RM_EFF_ADDR_THREADED' + self.sVariation.upper();
417 assert len(oNewStmt.asParams) == 3;
418 if self.sVariation in (self.ksVariation_16, self.ksVariation_16_Pre386, self.ksVariation_32_Addr16):
419 oNewStmt.asParams = [
420 oNewStmt.asParams[0], oNewStmt.asParams[1], self.dParamRefs['u16Disp'][0].sNewName,
421 ];
422 elif self.sVariation in (self.ksVariation_32, self.ksVariation_32_Flat, self.ksVariation_16_Addr32):
423 oNewStmt.asParams = [
424 oNewStmt.asParams[0], oNewStmt.asParams[1], self.dParamRefs['bSib'][0].sNewName,
425 self.dParamRefs['u32Disp'][0].sNewName,
426 ];
427 else:
428 oNewStmt.asParams = [
429 oNewStmt.asParams[0], self.dParamRefs['bRmEx'][0].sNewName, self.dParamRefs['bSib'][0].sNewName,
430 self.dParamRefs['u32Disp'][0].sNewName, self.dParamRefs['cbInstr'][0].sNewName,
431 ];
432 # ... and IEM_MC_ADVANCE_RIP_AND_FINISH into *_THREADED and maybe *_LM64/_NOT64 ...
433 elif oNewStmt.sName in ('IEM_MC_ADVANCE_RIP_AND_FINISH', 'IEM_MC_REL_JMP_S8_AND_FINISH',
434 'IEM_MC_REL_JMP_S16_AND_FINISH', 'IEM_MC_REL_JMP_S32_AND_FINISH'):
435 oNewStmt.asParams.append(self.dParamRefs['cbInstr'][0].sNewName);
436 if ( oNewStmt.sName in ('IEM_MC_REL_JMP_S8_AND_FINISH', )
437 and self.sVariation != self.ksVariation_16_Pre386):
438 oNewStmt.asParams.append(self.dParamRefs['pVCpu->iem.s.enmEffOpSize'][0].sNewName);
439 oNewStmt.sName += '_THREADED';
440 if self.sVariation in (self.ksVariation_64, self.ksVariation_64_Addr32):
441 oNewStmt.sName += '_PC64';
442 elif self.sVariation == self.ksVariation_16_Pre386:
443 oNewStmt.sName += '_PC16';
444 elif self.sVariation != self.ksVariation_Default:
445 oNewStmt.sName += '_PC32';
446
447 # ... and IEM_MC_*_GREG_U8 into *_THREADED w/ reworked index taking REX into account
448 elif oNewStmt.sName.startswith('IEM_MC_') and oNewStmt.sName.find('_GREG_U8') > 0:
449 (idxReg, _, sStdRef) = self.analyze8BitGRegStmt(oNewStmt);
450 oNewStmt.asParams[idxReg] = self.dParamRefs[sStdRef][0].sNewName;
451 oNewStmt.sName += '_THREADED';
452
453 # ... and IEM_MC_CALL_CIMPL_[0-5] and IEM_MC_DEFER_TO_CIMPL_[0-5]_RET into *_THREADED ...
454 elif oNewStmt.sName.startswith('IEM_MC_CALL_CIMPL_') or oNewStmt.sName.startswith('IEM_MC_DEFER_TO_CIMPL_'):
455 oNewStmt.sName += '_THREADED';
456 oNewStmt.asParams.insert(0, self.dParamRefs['cbInstr'][0].sNewName);
457
458 # Process branches of conditionals recursively.
459 if isinstance(oStmt, iai.McStmtCond):
460 (oNewStmt.aoIfBranch, iParamRef) = self.analyzeMorphStmtForThreaded(oStmt.aoIfBranch, iParamRef);
461 if oStmt.aoElseBranch:
462 (oNewStmt.aoElseBranch, iParamRef) = self.analyzeMorphStmtForThreaded(oStmt.aoElseBranch, iParamRef);
463
464 return (aoThreadedStmts, iParamRef);
465
466 def analyzeConsolidateThreadedParamRefs(self):
467 """
468 Consolidate threaded function parameter references into a dictionary
469 with lists of the references to each variable/field.
470 """
471 # Gather unique parameters.
472 self.dParamRefs = {};
473 for oRef in self.aoParamRefs:
474 if oRef.sStdRef not in self.dParamRefs:
475 self.dParamRefs[oRef.sStdRef] = [oRef,];
476 else:
477 self.dParamRefs[oRef.sStdRef].append(oRef);
478
479 # Generate names for them for use in the threaded function.
480 dParamNames = {};
481 for sName, aoRefs in self.dParamRefs.items():
482 # Morph the reference expression into a name.
483 if sName.startswith('IEM_GET_MODRM_REG'): sName = 'bModRmRegP';
484 elif sName.startswith('IEM_GET_MODRM_RM'): sName = 'bModRmRmP';
485 elif sName.startswith('IEM_GET_MODRM_REG_8'): sName = 'bModRmReg8P';
486 elif sName.startswith('IEM_GET_MODRM_RM_8'): sName = 'bModRmRm8P';
487 elif sName.startswith('IEM_GET_EFFECTIVE_VVVV'): sName = 'bEffVvvvP';
488 elif sName.find('.') >= 0 or sName.find('->') >= 0:
489 sName = sName[max(sName.rfind('.'), sName.rfind('>')) + 1 : ] + 'P';
490 else:
491 sName += 'P';
492
493 # Ensure it's unique.
494 if sName in dParamNames:
495 for i in range(10):
496 if sName + str(i) not in dParamNames:
497 sName += str(i);
498 break;
499 dParamNames[sName] = True;
500
501 # Update all the references.
502 for oRef in aoRefs:
503 oRef.sNewName = sName;
504
505 # Organize them by size too for the purpose of optimize them.
506 dBySize = {} # type: dict(str,str)
507 for sStdRef, aoRefs in self.dParamRefs.items():
508 if aoRefs[0].sType[0] != 'P':
509 cBits = g_kdTypeInfo[aoRefs[0].sType][0];
510 assert(cBits <= 64);
511 else:
512 cBits = 64;
513
514 if cBits not in dBySize:
515 dBySize[cBits] = [sStdRef,]
516 else:
517 dBySize[cBits].append(sStdRef);
518
519 # Pack the parameters as best as we can, starting with the largest ones
520 # and ASSUMING a 64-bit parameter size.
521 self.cMinParams = 0;
522 offNewParam = 0;
523 for cBits in sorted(dBySize.keys(), reverse = True):
524 for sStdRef in dBySize[cBits]:
525 if offNewParam == 0 or offNewParam + cBits > 64:
526 self.cMinParams += 1;
527 offNewParam = cBits;
528 else:
529 offNewParam += cBits;
530 assert(offNewParam <= 64);
531
532 for oRef in self.dParamRefs[sStdRef]:
533 oRef.iNewParam = self.cMinParams - 1;
534 oRef.offNewParam = offNewParam - cBits;
535
536 # Currently there are a few that requires 4 parameters, list these so we can figure out why:
537 if self.cMinParams >= 4:
538 print('debug: cMinParams=%s cRawParams=%s - %s:%d'
539 % (self.cMinParams, len(self.dParamRefs), self.oParent.oMcBlock.sSrcFile, self.oParent.oMcBlock.iBeginLine,));
540
541 return True;
542
543 ksHexDigits = '0123456789abcdefABCDEF';
544
545 def analyzeFindThreadedParamRefs(self, aoStmts):
546 """
547 Scans the statements for things that have to passed on to the threaded
548 function (populates self.aoParamRefs).
549 """
550 for oStmt in aoStmts:
551 # Some statements we can skip alltogether.
552 if isinstance(oStmt, iai.McCppPreProc):
553 continue;
554 if oStmt.isCppStmt() and oStmt.fDecode:
555 continue;
556
557 if isinstance(oStmt, iai.McStmtVar):
558 if oStmt.sConstValue is None:
559 continue;
560 aiSkipParams = { 0: True, 1: True, 3: True };
561 else:
562 aiSkipParams = {};
563
564 # Several statements have implicit parameters and some have different parameters.
565 if oStmt.sName in ('IEM_MC_ADVANCE_RIP_AND_FINISH', 'IEM_MC_REL_JMP_S8_AND_FINISH', 'IEM_MC_REL_JMP_S16_AND_FINISH',
566 'IEM_MC_REL_JMP_S32_AND_FINISH', 'IEM_MC_CALL_CIMPL_0', 'IEM_MC_CALL_CIMPL_1',
567 'IEM_MC_CALL_CIMPL_2', 'IEM_MC_CALL_CIMPL_3', 'IEM_MC_CALL_CIMPL_4', 'IEM_MC_CALL_CIMPL_5',
568 'IEM_MC_DEFER_TO_CIMPL_0_RET', 'IEM_MC_DEFER_TO_CIMPL_1_RET', 'IEM_MC_DEFER_TO_CIMPL_2_RET',
569 'IEM_MC_DEFER_TO_CIMPL_3_RET', 'IEM_MC_DEFER_TO_CIMPL_4_RET', 'IEM_MC_DEFER_TO_CIMPL_5_RET', ):
570 self.aoParamRefs.append(ThreadedParamRef('IEM_GET_INSTR_LEN(pVCpu)', 'uint4_t', oStmt, sStdRef = 'cbInstr'));
571
572 if ( oStmt.sName in ('IEM_MC_REL_JMP_S8_AND_FINISH',)
573 and self.sVariation != self.ksVariation_16_Pre386):
574 self.aoParamRefs.append(ThreadedParamRef('pVCpu->iem.s.enmEffOpSize', 'IEMMODE', oStmt));
575
576 if oStmt.sName == 'IEM_MC_CALC_RM_EFF_ADDR':
577 # This is being pretty presumptive about bRm always being the RM byte...
578 if self.sVariation in (self.ksVariation_16, self.ksVariation_16_Pre386, self.ksVariation_32_Addr16):
579 self.aoParamRefs.append(ThreadedParamRef('bRm', 'uint8_t', oStmt));
580 self.aoParamRefs.append(ThreadedParamRef('(uint16_t)uEffAddrInfo' ,
581 'uint16_t', oStmt, sStdRef = 'u16Disp'));
582 elif self.sVariation in (self.ksVariation_32, self.ksVariation_32_Flat, self.ksVariation_16_Addr32):
583 self.aoParamRefs.append(ThreadedParamRef('bRm', 'uint8_t', oStmt));
584 self.aoParamRefs.append(ThreadedParamRef('(uint8_t)(uEffAddrInfo >> 32)',
585 'uint8_t', oStmt, sStdRef = 'bSib'));
586 self.aoParamRefs.append(ThreadedParamRef('(uint32_t)uEffAddrInfo',
587 'uint32_t', oStmt, sStdRef = 'u32Disp'));
588 else:
589 assert self.sVariation in (self.ksVariation_64, self.ksVariation_64_Addr32);
590 self.aoParamRefs.append(ThreadedParamRef('IEM_GET_MODRM_EX(pVCpu, bRm)',
591 'uint8_t', oStmt, sStdRef = 'bRmEx'));
592 self.aoParamRefs.append(ThreadedParamRef('(uint8_t)(uEffAddrInfo >> 32)',
593 'uint8_t', oStmt, sStdRef = 'bSib'));
594 self.aoParamRefs.append(ThreadedParamRef('(uint32_t)uEffAddrInfo',
595 'uint32_t', oStmt, sStdRef = 'u32Disp'));
596 self.aoParamRefs.append(ThreadedParamRef('IEM_GET_INSTR_LEN(pVCpu)',
597 'uint4_t', oStmt, sStdRef = 'cbInstr'));
598 assert len(oStmt.asParams) == 3;
599 assert oStmt.asParams[1].startswith('bRm');
600 aiSkipParams[1] = True; # Skip the bRm parameter
601
602 # 8-bit register accesses needs to have their index argument reworked to take REX into account.
603 if oStmt.sName.startswith('IEM_MC_') and oStmt.sName.find('_GREG_U8') > 0:
604 (idxReg, sOrgRef, sStdRef) = self.analyze8BitGRegStmt(oStmt);
605 self.aoParamRefs.append(ThreadedParamRef(sOrgRef, 'uint4_t', oStmt, idxReg, sStdRef = sStdRef));
606 aiSkipParams[idxReg] = True; # Skip the parameter below.
607
608 # Inspect the target of calls to see if we need to pass down a
609 # function pointer or function table pointer for it to work.
610 if isinstance(oStmt, iai.McStmtCall):
611 if oStmt.sFn[0] == 'p':
612 self.aoParamRefs.append(ThreadedParamRef(oStmt.sFn, self.analyzeCallToType(oStmt.sFn), oStmt, oStmt.idxFn));
613 elif ( oStmt.sFn[0] != 'i'
614 and not oStmt.sFn.startswith('IEMTARGETCPU_EFL_BEHAVIOR_SELECT')
615 and not oStmt.sFn.startswith('IEM_SELECT_HOST_OR_FALLBACK') ):
616 self.raiseProblem('Bogus function name in %s: %s' % (oStmt.sName, oStmt.sFn,));
617 aiSkipParams[oStmt.idxFn] = True;
618
619 # Skip the hint parameter (first) for IEM_MC_CALL_CIMPL_X.
620 if oStmt.sName.startswith('IEM_MC_CALL_CIMPL_'):
621 assert oStmt.idxFn == 1;
622 aiSkipParams[0] = True;
623
624
625 # Check all the parameters for bogus references.
626 for iParam, sParam in enumerate(oStmt.asParams):
627 if iParam not in aiSkipParams and sParam not in self.oParent.dVariables:
628 # The parameter may contain a C expression, so we have to try
629 # extract the relevant bits, i.e. variables and fields while
630 # ignoring operators and parentheses.
631 offParam = 0;
632 while offParam < len(sParam):
633 # Is it the start of an C identifier? If so, find the end, but don't stop on field separators (->, .).
634 ch = sParam[offParam];
635 if ch.isalpha() or ch == '_':
636 offStart = offParam;
637 offParam += 1;
638 while offParam < len(sParam):
639 ch = sParam[offParam];
640 if not ch.isalnum() and ch != '_' and ch != '.':
641 if ch != '-' or sParam[offParam + 1] != '>':
642 # Special hack for the 'CTX_SUFF(pVM)' bit in pVCpu->CTX_SUFF(pVM)->xxxx:
643 if ( ch == '('
644 and sParam[offStart : offParam + len('(pVM)->')] == 'pVCpu->CTX_SUFF(pVM)->'):
645 offParam += len('(pVM)->') - 1;
646 else:
647 break;
648 offParam += 1;
649 offParam += 1;
650 sRef = sParam[offStart : offParam];
651
652 # For register references, we pass the full register indexes instead as macros
653 # like IEM_GET_MODRM_REG implicitly references pVCpu->iem.s.uRexReg and the
654 # threaded function will be more efficient if we just pass the register index
655 # as a 4-bit param.
656 if ( sRef.startswith('IEM_GET_MODRM')
657 or sRef.startswith('IEM_GET_EFFECTIVE_VVVV') ):
658 offParam = iai.McBlock.skipSpacesAt(sParam, offParam, len(sParam));
659 if sParam[offParam] != '(':
660 self.raiseProblem('Expected "(" following %s in "%s"' % (sRef, oStmt.renderCode(),));
661 (asMacroParams, offCloseParam) = iai.McBlock.extractParams(sParam, offParam);
662 if asMacroParams is None:
663 self.raiseProblem('Unable to find ")" for %s in "%s"' % (sRef, oStmt.renderCode(),));
664 offParam = offCloseParam + 1;
665 self.aoParamRefs.append(ThreadedParamRef(sParam[offStart : offParam], 'uint8_t',
666 oStmt, iParam, offStart));
667
668 # We can skip known variables.
669 elif sRef in self.oParent.dVariables:
670 pass;
671
672 # Skip certain macro invocations.
673 elif sRef in ('IEM_GET_HOST_CPU_FEATURES',
674 'IEM_GET_GUEST_CPU_FEATURES',
675 'IEM_IS_GUEST_CPU_AMD',
676 'IEM_IS_16BIT_CODE',
677 'IEM_IS_32BIT_CODE',
678 'IEM_IS_64BIT_CODE',
679 ):
680 offParam = iai.McBlock.skipSpacesAt(sParam, offParam, len(sParam));
681 if sParam[offParam] != '(':
682 self.raiseProblem('Expected "(" following %s in "%s"' % (sRef, oStmt.renderCode(),));
683 (asMacroParams, offCloseParam) = iai.McBlock.extractParams(sParam, offParam);
684 if asMacroParams is None:
685 self.raiseProblem('Unable to find ")" for %s in "%s"' % (sRef, oStmt.renderCode(),));
686 offParam = offCloseParam + 1;
687
688 # Skip any dereference following it, unless it's a predicate like IEM_IS_GUEST_CPU_AMD.
689 if sRef not in ('IEM_IS_GUEST_CPU_AMD',
690 'IEM_IS_16BIT_CODE',
691 'IEM_IS_32BIT_CODE',
692 'IEM_IS_64BIT_CODE',
693 ):
694 offParam = iai.McBlock.skipSpacesAt(sParam, offParam, len(sParam));
695 if offParam + 2 <= len(sParam) and sParam[offParam : offParam + 2] == '->':
696 offParam = iai.McBlock.skipSpacesAt(sParam, offParam + 2, len(sParam));
697 while offParam < len(sParam) and (sParam[offParam].isalnum() or sParam[offParam] in '_.'):
698 offParam += 1;
699
700 # Skip constants, globals, types (casts), sizeof and macros.
701 elif ( sRef.startswith('IEM_OP_PRF_')
702 or sRef.startswith('IEM_ACCESS_')
703 or sRef.startswith('IEMINT_')
704 or sRef.startswith('X86_GREG_')
705 or sRef.startswith('X86_SREG_')
706 or sRef.startswith('X86_EFL_')
707 or sRef.startswith('X86_FSW_')
708 or sRef.startswith('X86_FCW_')
709 or sRef.startswith('X86_XCPT_')
710 or sRef.startswith('IEMMODE_')
711 or sRef.startswith('IEM_F_')
712 or sRef.startswith('IEM_CIMPL_F_')
713 or sRef.startswith('g_')
714 or sRef.startswith('iemAImpl_')
715 or sRef in ( 'int8_t', 'int16_t', 'int32_t',
716 'INT8_C', 'INT16_C', 'INT32_C', 'INT64_C',
717 'UINT8_C', 'UINT16_C', 'UINT32_C', 'UINT64_C',
718 'UINT8_MAX', 'UINT16_MAX', 'UINT32_MAX', 'UINT64_MAX',
719 'INT8_MAX', 'INT16_MAX', 'INT32_MAX', 'INT64_MAX',
720 'INT8_MIN', 'INT16_MIN', 'INT32_MIN', 'INT64_MIN',
721 'sizeof', 'NOREF', 'RT_NOREF', 'IEMMODE_64BIT',
722 'RT_BIT_32', 'true', 'false', 'NIL_RTGCPTR',) ):
723 pass;
724
725 # Skip certain macro invocations.
726 # Any variable (non-field) and decoder fields in IEMCPU will need to be parameterized.
727 elif ( ( '.' not in sRef
728 and '-' not in sRef
729 and sRef not in ('pVCpu', ) )
730 or iai.McBlock.koReIemDecoderVars.search(sRef) is not None):
731 self.aoParamRefs.append(ThreadedParamRef(sRef, self.analyzeReferenceToType(sRef),
732 oStmt, iParam, offStart));
733 # Number.
734 elif ch.isdigit():
735 if ( ch == '0'
736 and offParam + 2 <= len(sParam)
737 and sParam[offParam + 1] in 'xX'
738 and sParam[offParam + 2] in self.ksHexDigits ):
739 offParam += 2;
740 while offParam < len(sParam) and sParam[offParam] in self.ksHexDigits:
741 offParam += 1;
742 else:
743 while offParam < len(sParam) and sParam[offParam].isdigit():
744 offParam += 1;
745 # Comment?
746 elif ( ch == '/'
747 and offParam + 4 <= len(sParam)
748 and sParam[offParam + 1] == '*'):
749 offParam += 2;
750 offNext = sParam.find('*/', offParam);
751 if offNext < offParam:
752 self.raiseProblem('Unable to find "*/" in "%s" ("%s")' % (sRef, oStmt.renderCode(),));
753 offParam = offNext + 2;
754 # Whatever else.
755 else:
756 offParam += 1;
757
758 # Traverse the branches of conditionals.
759 if isinstance(oStmt, iai.McStmtCond):
760 self.analyzeFindThreadedParamRefs(oStmt.aoIfBranch);
761 self.analyzeFindThreadedParamRefs(oStmt.aoElseBranch);
762 return True;
763
764 def analyzeVariation(self, aoStmts):
765 """
766 2nd part of the analysis, done on each variation.
767
768 The variations may differ in parameter requirements and will end up with
769 slightly different MC sequences. Thus this is done on each individually.
770
771 Returns dummy True - raises exception on trouble.
772 """
773 # Now scan the code for variables and field references that needs to
774 # be passed to the threaded function because they are related to the
775 # instruction decoding.
776 self.analyzeFindThreadedParamRefs(aoStmts);
777 self.analyzeConsolidateThreadedParamRefs();
778
779 # Morph the statement stream for the block into what we'll be using in the threaded function.
780 (self.aoStmtsForThreadedFunction, iParamRef) = self.analyzeMorphStmtForThreaded(aoStmts);
781 if iParamRef != len(self.aoParamRefs):
782 raise Exception('iParamRef=%s, expected %s!' % (iParamRef, len(self.aoParamRefs),));
783
784 return True;
785
786 def emitThreadedCallStmts(self, cchIndent):
787 """
788 Produces a generic C++ statment that emits a call to the thread function variation.
789 """
790 sCode = 'IEM_MC2_EMIT_CALL_%s(%s' % (self.cMinParams, self.getIndexName(), );
791 for iParam in range(self.cMinParams):
792 asFrags = [];
793 for aoRefs in self.dParamRefs.values():
794 oRef = aoRefs[0];
795 if oRef.iNewParam == iParam:
796 if oRef.offNewParam == 0:
797 asFrags.append('(uint64_t)(' + oRef.sOrgRef + ')');
798 else:
799 asFrags.append('((uint64_t)(%s) << %s)' % (oRef.sOrgRef, oRef.offNewParam));
800 assert asFrags;
801 sCode += ', ' + ' | '.join(asFrags);
802 sCode += ');';
803 return [iai.McCppGeneric(sCode, cchIndent = cchIndent),];
804
805
806class ThreadedFunction(object):
807 """
808 A threaded function.
809 """
810
811 def __init__(self, oMcBlock):
812 self.oMcBlock = oMcBlock # type: IEMAllInstructionsPython.McBlock
813 ## Variations for this block. There is at least one.
814 self.aoVariations = [] # type: list(ThreadedFunctionVariation)
815 ## Variation dictionary containing the same as aoVariations.
816 self.dVariations = {} # type: dict(str,ThreadedFunctionVariation)
817 ## Dictionary of local variables (IEM_MC_LOCAL[_CONST]) and call arguments (IEM_MC_ARG*).
818 self.dVariables = {} # type: dict(str,McStmtVar)
819
820 @staticmethod
821 def dummyInstance():
822 """ Gets a dummy instance. """
823 return ThreadedFunction(iai.McBlock('null', 999999999, 999999999,
824 iai.DecoderFunction('null', 999999999, 'nil', ('','')), 999999999));
825
826 def raiseProblem(self, sMessage):
827 """ Raises a problem. """
828 raise Exception('%s:%s: error: %s' % (self.oMcBlock.sSrcFile, self.oMcBlock.iBeginLine, sMessage, ));
829
830 def analyzeFindVariablesAndCallArgs(self, aoStmts):
831 """ Scans the statements for MC variables and call arguments. """
832 for oStmt in aoStmts:
833 if isinstance(oStmt, iai.McStmtVar):
834 if oStmt.sVarName in self.dVariables:
835 raise Exception('Variable %s is defined more than once!' % (oStmt.sVarName,));
836 self.dVariables[oStmt.sVarName] = oStmt.sVarName;
837
838 # There shouldn't be any variables or arguments declared inside if/
839 # else blocks, but scan them too to be on the safe side.
840 if isinstance(oStmt, iai.McStmtCond):
841 cBefore = len(self.dVariables);
842 self.analyzeFindVariablesAndCallArgs(oStmt.aoIfBranch);
843 self.analyzeFindVariablesAndCallArgs(oStmt.aoElseBranch);
844 if len(self.dVariables) != cBefore:
845 raise Exception('Variables/arguments defined in conditional branches!');
846 return True;
847
848 def analyze(self):
849 """
850 Analyzes the code, identifying the number of parameters it requires and such.
851
852 Returns dummy True - raises exception on trouble.
853 """
854
855 # Decode the block into a list/tree of McStmt objects.
856 aoStmts = self.oMcBlock.decode();
857
858 # Scan the statements for local variables and call arguments (self.dVariables).
859 self.analyzeFindVariablesAndCallArgs(aoStmts);
860
861 # Create variations if needed.
862 if iai.McStmt.findStmtByNames(aoStmts,
863 { 'IEM_MC_DEFER_TO_CIMPL_0_RET': True,
864 'IEM_MC_DEFER_TO_CIMPL_1_RET': True,
865 'IEM_MC_DEFER_TO_CIMPL_2_RET': True,
866 'IEM_MC_DEFER_TO_CIMPL_3_RET': True, }):
867 self.aoVariations = [ThreadedFunctionVariation(self, ThreadedFunctionVariation.ksVariation_Default),];
868
869 elif iai.McStmt.findStmtByNames(aoStmts, {'IEM_MC_CALC_RM_EFF_ADDR' : True,}):
870 self.aoVariations = [ThreadedFunctionVariation(self, sVar)
871 for sVar in ThreadedFunctionVariation.kasVariationsWithAddress];
872 else:
873 self.aoVariations = [ThreadedFunctionVariation(self, sVar)
874 for sVar in ThreadedFunctionVariation.kasVariationsWithoutAddress];
875
876 # Dictionary variant of the list.
877 self.dVariations = { oVar.sVariation: oVar for oVar in self.aoVariations };
878
879 # Continue the analysis on each variation.
880 for oVariation in self.aoVariations:
881 oVariation.analyzeVariation(aoStmts);
882
883 return True;
884
885 def emitThreadedCallStmts(self):
886 """
887 Worker for morphInputCode that returns a list of statements that emits
888 the call to the threaded functions for the block.
889 """
890 # Special case for only default variation:
891 if len(self.aoVariations) == 1:
892 assert self.aoVariations[0].sVariation == ThreadedFunctionVariation.ksVariation_Default;
893 return self.aoVariations[0].emitThreadedCallStmts(0);
894
895 # Currently only have variations for address mode.
896 dByVari = self.dVariations;
897
898 sExecMask = 'IEM_F_MODE_CPUMODE_MASK';
899 if ( ThreadedFunctionVariation.ksVariation_64_Addr32 in dByVari
900 or ThreadedFunctionVariation.ksVariation_32_Addr16 in dByVari
901 or ThreadedFunctionVariation.ksVariation_32_Flat in dByVari
902 or ThreadedFunctionVariation.ksVariation_16_Addr32 in dByVari):
903 sExecMask = '(IEM_F_MODE_CPUMODE_MASK | IEM_F_MODE_X86_FLAT_OR_PRE_386_MASK)';
904 aoStmts = [
905 iai.McCppGeneric('switch (pVCpu->iem.s.fExec & %s)' % (sExecMask,)),
906 iai.McCppGeneric('{'),
907 ];
908
909 if ThreadedFunctionVariation.ksVariation_64_Addr32 in dByVari:
910 aoStmts.extend([
911 iai.McCppGeneric('case IEMMODE_64BIT:', cchIndent = 4),
912 iai.McCppCond('RT_LIKELY(pVCpu->iem.s.enmEffAddrMode == IEMMODE_64BIT)', fDecode = True, cchIndent = 8,
913 aoIfBranch = dByVari[ThreadedFunctionVariation.ksVariation_64].emitThreadedCallStmts(0),
914 aoElseBranch = dByVari[ThreadedFunctionVariation.ksVariation_64_Addr32].emitThreadedCallStmts(0)),
915 iai.McCppGeneric('break;', cchIndent = 8),
916 ]);
917 elif ThreadedFunctionVariation.ksVariation_64 in dByVari:
918 aoStmts.append(iai.McCppGeneric('case IEMMODE_64BIT:', cchIndent = 4));
919 aoStmts.extend(dByVari[ThreadedFunctionVariation.ksVariation_64].emitThreadedCallStmts(8));
920 aoStmts.append(iai.McCppGeneric('break;', cchIndent = 8));
921
922 if ThreadedFunctionVariation.ksVariation_32_Addr16 in dByVari:
923 aoStmts.extend([
924 iai.McCppGeneric('case IEMMODE_32BIT | IEM_F_MODE_X86_FLAT_OR_PRE_386_MASK:', cchIndent = 4),
925 iai.McCppCond('RT_LIKELY(pVCpu->iem.s.enmEffAddrMode == IEMMODE_32BIT)', fDecode = True, cchIndent = 8,
926 aoIfBranch = dByVari[ThreadedFunctionVariation.ksVariation_32_Flat].emitThreadedCallStmts(0),
927 aoElseBranch = dByVari[ThreadedFunctionVariation.ksVariation_32_Addr16].emitThreadedCallStmts(0)),
928 iai.McCppGeneric('break;', cchIndent = 8),
929 iai.McCppGeneric('case IEMMODE_32BIT:', cchIndent = 4),
930 iai.McCppCond('RT_LIKELY(pVCpu->iem.s.enmEffAddrMode == IEMMODE_32BIT)', fDecode = True, cchIndent = 8,
931 aoIfBranch = dByVari[ThreadedFunctionVariation.ksVariation_32].emitThreadedCallStmts(0),
932 aoElseBranch = dByVari[ThreadedFunctionVariation.ksVariation_32_Addr16].emitThreadedCallStmts(0)),
933 iai.McCppGeneric('break;', cchIndent = 8),
934 ]);
935 elif ThreadedFunctionVariation.ksVariation_32 in dByVari:
936 aoStmts.append(iai.McCppGeneric('case IEMMODE_32BIT:', cchIndent = 4));
937 aoStmts.extend(dByVari[ThreadedFunctionVariation.ksVariation_32].emitThreadedCallStmts(8));
938 aoStmts.append(iai.McCppGeneric('break;', cchIndent = 8));
939
940 if ThreadedFunctionVariation.ksVariation_16_Addr32 in dByVari:
941 aoStmts.extend([
942 iai.McCppGeneric('case IEMMODE_16BIT:', cchIndent = 4),
943 iai.McCppCond('RT_LIKELY(pVCpu->iem.s.enmEffAddrMode == IEMMODE_16BIT)', fDecode = True, cchIndent = 8,
944 aoIfBranch = dByVari[ThreadedFunctionVariation.ksVariation_16].emitThreadedCallStmts(0),
945 aoElseBranch = dByVari[ThreadedFunctionVariation.ksVariation_16_Addr32].emitThreadedCallStmts(0)),
946 iai.McCppGeneric('break;', cchIndent = 8),
947 ]);
948 elif ThreadedFunctionVariation.ksVariation_16 in dByVari:
949 aoStmts.append(iai.McCppGeneric('case IEMMODE_16BIT:', cchIndent = 4));
950 aoStmts.extend(dByVari[ThreadedFunctionVariation.ksVariation_16].emitThreadedCallStmts(8));
951 aoStmts.append(iai.McCppGeneric('break;', cchIndent = 8));
952
953 if ThreadedFunctionVariation.ksVariation_16_Pre386 in dByVari:
954 aoStmts.append(iai.McCppGeneric('case IEMMODE_16BIT | IEM_F_MODE_X86_FLAT_OR_PRE_386_MASK:', cchIndent = 4));
955 aoStmts.extend(dByVari[ThreadedFunctionVariation.ksVariation_16_Pre386].emitThreadedCallStmts(8));
956 aoStmts.append(iai.McCppGeneric('break;', cchIndent = 8));
957
958 aoStmts.extend([
959 iai.McCppGeneric('IEM_NOT_REACHED_DEFAULT_CASE_RET();', cchIndent = 4),
960 iai.McCppGeneric('}'),
961 ]);
962
963 return aoStmts;
964
965 def morphInputCode(self, aoStmts, fCallEmitted = False, cDepth = 0):
966 """
967 Adjusts (& copies) the statements for the input/decoder so it will emit
968 calls to the right threaded functions for each block.
969
970 Returns list/tree of statements (aoStmts is not modified) and updated
971 fCallEmitted status.
972 """
973 #print('McBlock at %s:%s' % (os.path.split(self.oMcBlock.sSrcFile)[1], self.oMcBlock.iBeginLine,));
974 aoDecoderStmts = [];
975 for oStmt in aoStmts:
976 # Copy the statement. Make a deep copy to make sure we've got our own
977 # copies of all instance variables, even if a bit overkill at the moment.
978 oNewStmt = copy.deepcopy(oStmt);
979 aoDecoderStmts.append(oNewStmt);
980 #print('oNewStmt %s %s' % (oNewStmt.sName, len(oNewStmt.asParams),));
981
982 # If we haven't emitted the threaded function call yet, look for
983 # statements which it would naturally follow or preceed.
984 if not fCallEmitted:
985 if not oStmt.isCppStmt():
986 if ( oStmt.sName.startswith('IEM_MC_MAYBE_RAISE_') \
987 or (oStmt.sName.endswith('_AND_FINISH') and oStmt.sName.startswith('IEM_MC_'))
988 or oStmt.sName.startswith('IEM_MC_CALL_CIMPL_')
989 or oStmt.sName.startswith('IEM_MC_DEFER_TO_CIMPL_')
990 or oStmt.sName in ('IEM_MC_RAISE_DIVIDE_ERROR',)):
991 aoDecoderStmts.pop();
992 aoDecoderStmts.extend(self.emitThreadedCallStmts());
993 aoDecoderStmts.append(oNewStmt);
994 fCallEmitted = True;
995 elif ( oStmt.fDecode
996 and ( oStmt.asParams[0].find('IEMOP_HLP_DONE_') >= 0
997 or oStmt.asParams[0].find('IEMOP_HLP_DECODED_') >= 0)):
998 aoDecoderStmts.extend(self.emitThreadedCallStmts());
999 fCallEmitted = True;
1000
1001 # Process branches of conditionals recursively.
1002 if isinstance(oStmt, iai.McStmtCond):
1003 (oNewStmt.aoIfBranch, fCallEmitted1) = self.morphInputCode(oStmt.aoIfBranch, fCallEmitted, cDepth + 1);
1004 if oStmt.aoElseBranch:
1005 (oNewStmt.aoElseBranch, fCallEmitted2) = self.morphInputCode(oStmt.aoElseBranch, fCallEmitted, cDepth + 1);
1006 else:
1007 fCallEmitted2 = False;
1008 fCallEmitted = fCallEmitted or (fCallEmitted1 and fCallEmitted2);
1009
1010 if not fCallEmitted and cDepth == 0:
1011 self.raiseProblem('Unable to insert call to threaded function.');
1012
1013 return (aoDecoderStmts, fCallEmitted);
1014
1015
1016 def generateInputCode(self):
1017 """
1018 Modifies the input code.
1019 """
1020 cchIndent = (self.oMcBlock.cchIndent + 3) // 4 * 4;
1021
1022 if len(self.oMcBlock.aoStmts) == 1:
1023 # IEM_MC_DEFER_TO_CIMPL_X_RET - need to wrap in {} to make it safe to insert into random code.
1024 sCode = iai.McStmt.renderCodeForList(self.morphInputCode(self.oMcBlock.aoStmts)[0],
1025 cchIndent = cchIndent).replace('\n', ' /* gen */\n', 1);
1026 sCode = ' ' * (min(cchIndent, 2) - 2) + '{\n' \
1027 + sCode \
1028 + ' ' * (min(cchIndent, 2) - 2) + '}\n';
1029 return sCode;
1030
1031 # IEM_MC_BEGIN/END block
1032 assert len(self.oMcBlock.asLines) > 2, "asLines=%s" % (self.oMcBlock.asLines,);
1033 return iai.McStmt.renderCodeForList(self.morphInputCode(self.oMcBlock.aoStmts)[0],
1034 cchIndent = cchIndent).replace('\n', ' /* gen */\n', 1);
1035
1036
1037class IEMThreadedGenerator(object):
1038 """
1039 The threaded code generator & annotator.
1040 """
1041
1042 def __init__(self):
1043 self.aoThreadedFuncs = [] # type: list(ThreadedFunction)
1044 self.oOptions = None # type: argparse.Namespace
1045 self.aoParsers = [] # type: list(IEMAllInstructionsPython.SimpleParser)
1046
1047 #
1048 # Processing.
1049 #
1050
1051 def processInputFiles(self):
1052 """
1053 Process the input files.
1054 """
1055
1056 # Parse the files.
1057 self.aoParsers = iai.parseFiles(self.oOptions.asInFiles);
1058
1059 # Create threaded functions for the MC blocks.
1060 self.aoThreadedFuncs = [ThreadedFunction(oMcBlock) for oMcBlock in iai.g_aoMcBlocks];
1061
1062 # Analyze the threaded functions.
1063 dRawParamCounts = {};
1064 dMinParamCounts = {};
1065 for oThreadedFunction in self.aoThreadedFuncs:
1066 oThreadedFunction.analyze();
1067 for oVariation in oThreadedFunction.aoVariations:
1068 dRawParamCounts[len(oVariation.dParamRefs)] = dRawParamCounts.get(len(oVariation.dParamRefs), 0) + 1;
1069 dMinParamCounts[oVariation.cMinParams] = dMinParamCounts.get(oVariation.cMinParams, 0) + 1;
1070 print('debug: param count distribution, raw and optimized:', file = sys.stderr);
1071 for cCount in sorted({cBits: True for cBits in list(dRawParamCounts.keys()) + list(dMinParamCounts.keys())}.keys()):
1072 print('debug: %s params: %4s raw, %4s min'
1073 % (cCount, dRawParamCounts.get(cCount, 0), dMinParamCounts.get(cCount, 0)),
1074 file = sys.stderr);
1075
1076 return True;
1077
1078 #
1079 # Output
1080 #
1081
1082 def generateLicenseHeader(self):
1083 """
1084 Returns the lines for a license header.
1085 """
1086 return [
1087 '/*',
1088 ' * Autogenerated by $Id: IEMAllThreadedPython.py 100148 2023-06-10 19:44:02Z vboxsync $ ',
1089 ' * Do not edit!',
1090 ' */',
1091 '',
1092 '/*',
1093 ' * Copyright (C) 2023-' + str(datetime.date.today().year) + ' Oracle and/or its affiliates.',
1094 ' *',
1095 ' * This file is part of VirtualBox base platform packages, as',
1096 ' * available from https://www.virtualbox.org.',
1097 ' *',
1098 ' * This program is free software; you can redistribute it and/or',
1099 ' * modify it under the terms of the GNU General Public License',
1100 ' * as published by the Free Software Foundation, in version 3 of the',
1101 ' * License.',
1102 ' *',
1103 ' * This program is distributed in the hope that it will be useful, but',
1104 ' * WITHOUT ANY WARRANTY; without even the implied warranty of',
1105 ' * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU',
1106 ' * General Public License for more details.',
1107 ' *',
1108 ' * You should have received a copy of the GNU General Public License',
1109 ' * along with this program; if not, see <https://www.gnu.org/licenses>.',
1110 ' *',
1111 ' * The contents of this file may alternatively be used under the terms',
1112 ' * of the Common Development and Distribution License Version 1.0',
1113 ' * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included',
1114 ' * in the VirtualBox distribution, in which case the provisions of the',
1115 ' * CDDL are applicable instead of those of the GPL.',
1116 ' *',
1117 ' * You may elect to license modified versions of this file under the',
1118 ' * terms and conditions of either the GPL or the CDDL or both.',
1119 ' *',
1120 ' * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0',
1121 ' */',
1122 '',
1123 '',
1124 '',
1125 ];
1126
1127
1128 def generateThreadedFunctionsHeader(self, oOut):
1129 """
1130 Generates the threaded functions header file.
1131 Returns success indicator.
1132 """
1133
1134 asLines = self.generateLicenseHeader();
1135
1136 # Generate the threaded function table indexes.
1137 asLines += [
1138 'typedef enum IEMTHREADEDFUNCS',
1139 '{',
1140 ' kIemThreadedFunc_Invalid = 0,',
1141 ];
1142 iThreadedFunction = 0;
1143 for sVariation in ThreadedFunctionVariation.kasVariationsEmitOrder:
1144 asLines += [
1145 '',
1146 ' /*',
1147 ' * Variation: ' + ThreadedFunctionVariation.kdVariationNames[sVariation] + '',
1148 ' */',
1149 ];
1150 for oThreadedFunction in self.aoThreadedFuncs:
1151 oVariation = oThreadedFunction.dVariations.get(sVariation, None);
1152 if oVariation:
1153 iThreadedFunction += 1;
1154 oVariation.iEnumValue = iThreadedFunction;
1155 asLines.append(' ' + oVariation.getIndexName() + ',');
1156 asLines += [
1157 ' kIemThreadedFunc_End',
1158 '} IEMTHREADEDFUNCS;',
1159 '',
1160 ];
1161
1162 # Prototype the function table.
1163 sFnType = 'typedef IEM_DECL_IMPL_TYPE(VBOXSTRICTRC, FNIEMTHREADEDFUNC, (PVMCPU pVCpu';
1164 for iParam in range(g_kcThreadedParams):
1165 sFnType += ', uint64_t uParam' + str(iParam);
1166 sFnType += '));'
1167
1168 asLines += [
1169 sFnType,
1170 'typedef FNIEMTHREADEDFUNC *PFNIEMTHREADEDFUNC;',
1171 '',
1172 'extern const PFNIEMTHREADEDFUNC g_apfnIemThreadedFunctions[kIemThreadedFunc_End];',
1173 ];
1174
1175 oOut.write('\n'.join(asLines));
1176 return True;
1177
1178 ksBitsToIntMask = {
1179 1: "UINT64_C(0x1)",
1180 2: "UINT64_C(0x3)",
1181 4: "UINT64_C(0xf)",
1182 8: "UINT64_C(0xff)",
1183 16: "UINT64_C(0xffff)",
1184 32: "UINT64_C(0xffffffff)",
1185 };
1186 def generateThreadedFunctionsSource(self, oOut):
1187 """
1188 Generates the threaded functions source file.
1189 Returns success indicator.
1190 """
1191
1192 asLines = self.generateLicenseHeader();
1193 oOut.write('\n'.join(asLines));
1194
1195 # Prepare the fixed bits.
1196 sParamList = '(PVMCPU pVCpu';
1197 for iParam in range(g_kcThreadedParams):
1198 sParamList += ', uint64_t uParam' + str(iParam);
1199 sParamList += '))\n';
1200
1201 #
1202 # Emit the function definitions.
1203 #
1204 for sVariation in ThreadedFunctionVariation.kasVariationsEmitOrder:
1205 sVarName = ThreadedFunctionVariation.kdVariationNames[sVariation];
1206 oOut.write( '\n'
1207 + '\n'
1208 + '\n'
1209 + '\n'
1210 + '/*' + '*' * 128 + '\n'
1211 + '* Variation: ' + sVarName + ' ' * (129 - len(sVarName) - 15) + '*\n'
1212 + '*' * 128 + '*/\n');
1213
1214 for oThreadedFunction in self.aoThreadedFuncs:
1215 oVariation = oThreadedFunction.dVariations.get(sVariation, None);
1216 if oVariation:
1217 oMcBlock = oThreadedFunction.oMcBlock;
1218
1219 # Function header
1220 oOut.write( '\n'
1221 + '\n'
1222 + '/**\n'
1223 + ' * %s at line %s offset %s in %s%s\n'
1224 % (oMcBlock.sFunction, oMcBlock.iBeginLine, oMcBlock.offBeginLine,
1225 os.path.split(oMcBlock.sSrcFile)[1],
1226 ' (macro expansion)' if oMcBlock.iBeginLine == oMcBlock.iEndLine else '')
1227 + ' */\n'
1228 + 'static IEM_DECL_IMPL_DEF(VBOXSTRICTRC, ' + oVariation.getFunctionName() + ',\n'
1229 + ' ' + sParamList
1230 + '{\n');
1231
1232 aasVars = [];
1233 for aoRefs in oVariation.dParamRefs.values():
1234 oRef = aoRefs[0];
1235 if oRef.sType[0] != 'P':
1236 cBits = g_kdTypeInfo[oRef.sType][0];
1237 sType = g_kdTypeInfo[oRef.sType][2];
1238 else:
1239 cBits = 64;
1240 sType = oRef.sType;
1241
1242 sTypeDecl = sType + ' const';
1243
1244 if cBits == 64:
1245 assert oRef.offNewParam == 0;
1246 if sType == 'uint64_t':
1247 sUnpack = 'uParam%s;' % (oRef.iNewParam,);
1248 else:
1249 sUnpack = '(%s)uParam%s;' % (sType, oRef.iNewParam,);
1250 elif oRef.offNewParam == 0:
1251 sUnpack = '(%s)(uParam%s & %s);' % (sType, oRef.iNewParam, self.ksBitsToIntMask[cBits]);
1252 else:
1253 sUnpack = '(%s)((uParam%s >> %s) & %s);' \
1254 % (sType, oRef.iNewParam, oRef.offNewParam, self.ksBitsToIntMask[cBits]);
1255
1256 sComment = '/* %s - %s ref%s */' % (oRef.sOrgRef, len(aoRefs), 's' if len(aoRefs) != 1 else '',);
1257
1258 aasVars.append([ '%s:%02u' % (oRef.iNewParam, oRef.offNewParam),
1259 sTypeDecl, oRef.sNewName, sUnpack, sComment ]);
1260 acchVars = [0, 0, 0, 0, 0];
1261 for asVar in aasVars:
1262 for iCol, sStr in enumerate(asVar):
1263 acchVars[iCol] = max(acchVars[iCol], len(sStr));
1264 sFmt = ' %%-%ss %%-%ss = %%-%ss %%s\n' % (acchVars[1], acchVars[2], acchVars[3]);
1265 for asVar in sorted(aasVars):
1266 oOut.write(sFmt % (asVar[1], asVar[2], asVar[3], asVar[4],));
1267
1268 # RT_NOREF for unused parameters.
1269 if oVariation.cMinParams < g_kcThreadedParams:
1270 oOut.write(' RT_NOREF('
1271 + ', '.join(['uParam%u' % (i,) for i in range(oVariation.cMinParams, g_kcThreadedParams)])
1272 + ');\n');
1273
1274 # Now for the actual statements.
1275 oOut.write(iai.McStmt.renderCodeForList(oVariation.aoStmtsForThreadedFunction, cchIndent = 4));
1276
1277 oOut.write('}\n');
1278
1279
1280 #
1281 # Emit the function table.
1282 #
1283 oOut.write( '\n'
1284 + '\n'
1285 + '/**\n'
1286 + ' * Function table.\n'
1287 + ' */\n'
1288 + 'const PFNIEMTHREADEDFUNC g_apfnIemThreadedFunctions[kIemThreadedFunc_End] =\n'
1289 + '{\n'
1290 + ' /*Invalid*/ NULL, \n');
1291 iThreadedFunction = 0;
1292 for sVariation in ThreadedFunctionVariation.kasVariationsEmitOrder:
1293 oOut.write( '\n'
1294 + ' /*\n'
1295 + ' * Variation: ' + ThreadedFunctionVariation.kdVariationNames[sVariation] + '\n'
1296 + ' */\n');
1297 for oThreadedFunction in self.aoThreadedFuncs:
1298 oVariation = oThreadedFunction.dVariations.get(sVariation, None);
1299 if oVariation:
1300 iThreadedFunction += 1;
1301 assert oVariation.iEnumValue == iThreadedFunction;
1302 oOut.write(' /*%4u*/ %s,\n' % (iThreadedFunction, oVariation.getFunctionName(),));
1303 oOut.write('};\n');
1304
1305 return True;
1306
1307 def getThreadedFunctionByIndex(self, idx):
1308 """
1309 Returns a ThreadedFunction object for the given index. If the index is
1310 out of bounds, a dummy is returned.
1311 """
1312 if idx < len(self.aoThreadedFuncs):
1313 return self.aoThreadedFuncs[idx];
1314 return ThreadedFunction.dummyInstance();
1315
1316 def generateModifiedInput(self, oOut):
1317 """
1318 Generates the combined modified input source/header file.
1319 Returns success indicator.
1320 """
1321 #
1322 # File header.
1323 #
1324 oOut.write('\n'.join(self.generateLicenseHeader()));
1325
1326 #
1327 # ASSUMING that g_aoMcBlocks/self.aoThreadedFuncs are in self.aoParsers
1328 # order, we iterate aoThreadedFuncs in parallel to the lines from the
1329 # parsers and apply modifications where required.
1330 #
1331 iThreadedFunction = 0;
1332 oThreadedFunction = self.getThreadedFunctionByIndex(0);
1333 for oParser in self.aoParsers: # type: IEMAllInstructionsPython.SimpleParser
1334 oOut.write("\n\n/* ****** BEGIN %s ******* */\n" % (oParser.sSrcFile,));
1335
1336 iLine = 0;
1337 while iLine < len(oParser.asLines):
1338 sLine = oParser.asLines[iLine];
1339 iLine += 1; # iBeginLine and iEndLine are 1-based.
1340
1341 # Can we pass it thru?
1342 if ( iLine not in [oThreadedFunction.oMcBlock.iBeginLine, oThreadedFunction.oMcBlock.iEndLine]
1343 or oThreadedFunction.oMcBlock.sSrcFile != oParser.sSrcFile):
1344 oOut.write(sLine);
1345 #
1346 # Single MC block. Just extract it and insert the replacement.
1347 #
1348 elif oThreadedFunction.oMcBlock.iBeginLine != oThreadedFunction.oMcBlock.iEndLine:
1349 assert sLine.count('IEM_MC_') == 1;
1350 oOut.write(sLine[:oThreadedFunction.oMcBlock.offBeginLine]);
1351 sModified = oThreadedFunction.generateInputCode().strip();
1352 oOut.write(sModified);
1353
1354 iLine = oThreadedFunction.oMcBlock.iEndLine;
1355 sLine = oParser.asLines[iLine - 1];
1356 assert sLine.count('IEM_MC_') == 1 or len(oThreadedFunction.oMcBlock.aoStmts) == 1;
1357 oOut.write(sLine[oThreadedFunction.oMcBlock.offAfterEnd : ]);
1358
1359 # Advance
1360 iThreadedFunction += 1;
1361 oThreadedFunction = self.getThreadedFunctionByIndex(iThreadedFunction);
1362 #
1363 # Macro expansion line that have sublines and may contain multiple MC blocks.
1364 #
1365 else:
1366 offLine = 0;
1367 while iLine == oThreadedFunction.oMcBlock.iBeginLine:
1368 oOut.write(sLine[offLine : oThreadedFunction.oMcBlock.offBeginLine]);
1369
1370 sModified = oThreadedFunction.generateInputCode().strip();
1371 assert ( sModified.startswith('IEM_MC_BEGIN')
1372 or (sModified.find('IEM_MC_DEFER_TO_CIMPL_') > 0 and sModified.strip().startswith('{\n'))
1373 ), 'sModified="%s"' % (sModified,);
1374 oOut.write(sModified);
1375
1376 offLine = oThreadedFunction.oMcBlock.offAfterEnd;
1377
1378 # Advance
1379 iThreadedFunction += 1;
1380 oThreadedFunction = self.getThreadedFunctionByIndex(iThreadedFunction);
1381
1382 # Last line segment.
1383 if offLine < len(sLine):
1384 oOut.write(sLine[offLine : ]);
1385
1386 oOut.write("/* ****** END %s ******* */\n" % (oParser.sSrcFile,));
1387
1388 return True;
1389
1390 #
1391 # Main
1392 #
1393
1394 def main(self, asArgs):
1395 """
1396 C-like main function.
1397 Returns exit code.
1398 """
1399
1400 #
1401 # Parse arguments
1402 #
1403 sScriptDir = os.path.dirname(__file__);
1404 oParser = argparse.ArgumentParser(add_help = False);
1405 oParser.add_argument('asInFiles', metavar = 'input.cpp.h', nargs = '*',
1406 default = [os.path.join(sScriptDir, asFM[0]) for asFM in iai.g_aasAllInstrFilesAndDefaultMap],
1407 help = "Selection of VMMAll/IEMAllInstructions*.cpp.h files to use as input.");
1408 oParser.add_argument('--out-funcs-hdr', metavar = 'file-funcs.h', dest = 'sOutFileFuncsHdr', action = 'store',
1409 default = '-', help = 'The output header file for the functions.');
1410 oParser.add_argument('--out-funcs-cpp', metavar = 'file-funcs.cpp', dest = 'sOutFileFuncsCpp', action = 'store',
1411 default = '-', help = 'The output C++ file for the functions.');
1412 oParser.add_argument('--out-mod-input', metavar = 'file-instr.cpp.h', dest = 'sOutFileModInput', action = 'store',
1413 default = '-', help = 'The output C++/header file for the modified input instruction files.');
1414 oParser.add_argument('--help', '-h', '-?', action = 'help', help = 'Display help and exit.');
1415 oParser.add_argument('--version', '-V', action = 'version',
1416 version = 'r%s (IEMAllThreadedPython.py), r%s (IEMAllInstructionsPython.py)'
1417 % (__version__.split()[1], iai.__version__.split()[1],),
1418 help = 'Displays the version/revision of the script and exit.');
1419 self.oOptions = oParser.parse_args(asArgs[1:]);
1420 print("oOptions=%s" % (self.oOptions,));
1421
1422 #
1423 # Process the instructions specified in the IEM sources.
1424 #
1425 if self.processInputFiles():
1426 #
1427 # Generate the output files.
1428 #
1429 aaoOutputFiles = (
1430 ( self.oOptions.sOutFileFuncsHdr, self.generateThreadedFunctionsHeader ),
1431 ( self.oOptions.sOutFileFuncsCpp, self.generateThreadedFunctionsSource ),
1432 ( self.oOptions.sOutFileModInput, self.generateModifiedInput ),
1433 );
1434 fRc = True;
1435 for sOutFile, fnGenMethod in aaoOutputFiles:
1436 if sOutFile == '-':
1437 fRc = fnGenMethod(sys.stdout) and fRc;
1438 else:
1439 try:
1440 oOut = open(sOutFile, 'w'); # pylint: disable=consider-using-with,unspecified-encoding
1441 except Exception as oXcpt:
1442 print('error! Failed open "%s" for writing: %s' % (sOutFile, oXcpt,), file = sys.stderr);
1443 return 1;
1444 fRc = fnGenMethod(oOut) and fRc;
1445 oOut.close();
1446 if fRc:
1447 return 0;
1448
1449 return 1;
1450
1451
1452if __name__ == '__main__':
1453 sys.exit(IEMThreadedGenerator().main(sys.argv));
1454
Note: See TracBrowser for help on using the repository browser.

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