VirtualBox

Changeset 100111 in vbox for trunk/src/VBox/VMM/VMMAll


Ignore:
Timestamp:
Jun 7, 2023 11:39:47 PM (21 months ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
157804
Message:

VMM/IEM: Working on parsing the IEM_MC_DEFER_TO_CIMPL_[0-9]_RET() macro invocations and treat them as a special McBlock case. bugref:10369

Location:
trunk/src/VBox/VMM/VMMAll
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/VMM/VMMAll/IEMAllInstructionsPython.py

    r100089 r100111  
    19181918class McBlock(object):
    19191919    """
    1920     Microcode block (IEM_MC_BEGIN ... IEM_MC_END).
     1920    Microcode block (IEM_MC_BEGIN ... IEM_MC_END, IEM_MC_DEFER_TO_CIMPL_x_RET).
    19211921    """
    19221922
    19231923    def __init__(self, sSrcFile, iBeginLine, offBeginLine, oFunction, iInFunction, cchIndent = None):
    1924         self.sSrcFile     = sSrcFile;                           ##< The source file containing the block.
    1925         self.iBeginLine   = iBeginLine;                         ##< The line with the IEM_MC_BEGIN statement.
    1926         self.offBeginLine = offBeginLine;                       ##< The offset of the IEM_MC_BEGIN statement within the line.
    1927         self.iEndLine     = -1;                                 ##< The line with the IEM_MC_END statement.
    1928         self.offEndLine   = 0;                                  ##< The offset of the IEM_MC_END statement within the line.
    1929         self.oFunction    = oFunction;                          ##< The function the block resides in.
    1930         self.sFunction    = oFunction.sName;                    ##< The name of the function the block resides in. DEPRECATED.
    1931         self.iInFunction  = iInFunction;                        ##< The block number wihtin the function.
     1924        ## The source file containing the block.
     1925        self.sSrcFile     = sSrcFile;
     1926        ## The line with the IEM_MC_BEGIN/IEM_MC_DEFER_TO_CIMPL_X_RET statement.
     1927        self.iBeginLine   = iBeginLine;
     1928        ## The offset of the IEM_MC_BEGIN/IEM_MC_DEFER_TO_CIMPL_X_RET statement within the line.
     1929        self.offBeginLine = offBeginLine;
     1930        ## The line with the IEM_MC_END statement / last line of IEM_MC_DEFER_TO_CIMPL_X_RET.
     1931        self.iEndLine     = -1;
     1932        ## The offset of the IEM_MC_END statement within the line / semicolon offset for defer-to.
     1933        self.offEndLine   = 0;
     1934        ## The offset following the IEM_MC_END/IEM_MC_DEFER_TO_CIMPL_X_RET semicolon.
     1935        self.offAfterEnd  = 0;
     1936        ## The function the block resides in.
     1937        self.oFunction    = oFunction;
     1938        ## The name of the function the block resides in. DEPRECATED.
     1939        self.sFunction    = oFunction.sName;
     1940        ## The block number within the function.
     1941        self.iInFunction  = iInFunction;
    19321942        self.cchIndent    = cchIndent if cchIndent else offBeginLine;
    19331943        self.asLines      = []              # type: list(str)   ##< The raw lines the block is made up of.
     
    19351945        self.aoStmts      = []              # type: list(McStmt)
    19361946
    1937     def complete(self, iEndLine, offEndLine, asLines):
     1947    def complete(self, iEndLine, offEndLine, offAfterEnd, asLines):
    19381948        """
    19391949        Completes the microcode block.
     
    19421952        self.iEndLine     = iEndLine;
    19431953        self.offEndLine   = offEndLine;
     1954        self.offAfterEnd  = offAfterEnd;
    19441955        self.asLines      = asLines;
    19451956
     
    24322443    def decode(self):
    24332444        """
    2434         Decodes the block, populating self.aoStmts.
     2445        Decodes the block, populating self.aoStmts if necessary.
    24352446        Returns the statement list.
    24362447        Raises ParserException on failure.
    24372448        """
    2438         self.aoStmts = self.decodeCode(''.join(self.asLines));
     2449        if not self.aoStmts:
     2450            self.aoStmts = self.decodeCode(''.join(self.asLines));
    24392451        return self.aoStmts;
    24402452
     
    29152927        self.oReHashDefine3 = re.compile('(?s)\A\s*([A-Za-z_][A-Za-z0-9_]*)[^(]\s*(.*)\Z');        ##< Simple, no arguments.
    29162928        self.oReHashUndef   = re.compile('^\s*#\s*undef\s+(.*)$');
    2917         self.oReMcBeginEnd  = re.compile(r'\bIEM_MC_(BEGIN|END)\s*\(');
    2918 
     2929        self.oReMcBeginEnd  = re.compile(r'\bIEM_MC_(BEGIN|END|DEFER_TO_CIMPL_[0-5]_RET)\s*\(');
    29192930        self.fDebug         = True;
    29202931        self.fDebugMc       = False;
     
    46014612        while asLines[-1].strip() == '':
    46024613            asLines.pop();
    4603         sFinal      = asLines[-1];
    4604         offFinalEnd = sFinal.find('IEM_MC_END');
     4614        sFinal        = asLines[-1];
     4615        offFinalEnd   = sFinal.find('IEM_MC_END');
     4616        offEndInFinal = offFinalEnd;
    46054617        if offFinalEnd < 0: self.raiseError('bogus IEM_MC_END: Not in final line: %s' % (sFinal,));
    46064618        offFinalEnd += len('IEM_MC_END');
     
    46264638        # Complete and discard the current block.
    46274639        #
    4628         self.oCurMcBlock.complete(self.iLine, offEndStatementInLine, asLines);
     4640        self.oCurMcBlock.complete(self.iLine, offEndStatementInLine,
     4641                                  offEndStatementInLine + offFinalEnd - offEndInFinal, asLines);
    46294642        self.oCurMcBlock = None;
     4643        return True;
     4644
     4645    def workerIemMcDeferToCImplXRet(self, sCode, offBeginStatementInCodeStr, offBeginStatementInLine, cParams):
     4646        """
     4647        Process a IEM_MC_DEFER_TO_CIMPL_[0-5]_RET macro invocation.
     4648        """
     4649        if self.fDebugMc:
     4650            self.debug('IEM_MC_DEFER_TO_CIMPL_%d_RET on %s off %s' % (cParams, self.iLine, offBeginStatementInLine,));
     4651            #self.debug('%s<eos>' % (sCode,));
     4652
     4653        # Check preconditions.
     4654        if not self.oCurFunction:
     4655            self.raiseError('IEM_MC_DEFER_TO_CIMPL_x_RET w/o current function (%s)' % (sCode,));
     4656        if self.oCurMcBlock:
     4657            self.raiseError('IEM_MC_DEFER_TO_CIMPL_x_RET inside IEM_MC_BEGIN blocki starting at line %u'
     4658                            % (self.oCurMcBlock.iBeginLine,));
     4659
     4660        # Figure out the indent level the block starts at, adjusting for expanded multiline macros.
     4661        cchIndent = offBeginStatementInCodeStr;
     4662        offPrevNewline = sCode.rfind('\n', 0, offBeginStatementInCodeStr);
     4663        if offPrevNewline >= 0:
     4664            cchIndent -= offPrevNewline + 1;
     4665        #self.debug('cchIndent=%s offPrevNewline=%s sFunc=%s' % (cchIndent, offPrevNewline, self.oCurFunction.sName));
     4666
     4667        # Start a new block.
     4668        oMcBlock = McBlock(self.sSrcFile, self.iLine, offBeginStatementInLine,
     4669                           self.oCurFunction, self.iMcBlockInFunc, cchIndent);
     4670
     4671        # Parse the statment (first call may advance self.iLine!).
     4672        offAfter, asArgs = self.findAndParseMacroInvocationEx(sCode[offBeginStatementInCodeStr:],
     4673                                                              'IEM_MC_DEFER_TO_CIMPL_%d_RET' % (cParams,));
     4674        if asArgs is None:
     4675            self.raiseError('IEM_MC_DEFER_TO_CIMPL_x_RET: Closing parenthesis not found!');
     4676        if len(asArgs) != cParams + 3:
     4677            self.raiseError('IEM_MC_DEFER_TO_CIMPL_%d_RET: findAndParseMacroInvocationEx returns %s args, expected %s!'
     4678                            % (cParams, len(asArgs), cParams + 3,));
     4679
     4680        oMcBlock.aoStmts = [McStmtCall(asArgs[0], asArgs[1:], 1),];
     4681
     4682        ## @todo complete this after some sleep...
     4683        _  = offAfter;
     4684        #asLines =
     4685
     4686        # Complete the block.
     4687        #oMcBlock.complete(self.iLine, offBeginStatementInCodeStr);
     4688
     4689        #g_aoMcBlocks.append(oMcBlock);
     4690        #self.cTotalMcBlocks += 1;
     4691        #self.iMcBlockInFunc += 1;
    46304692        return True;
    46314693
     
    47814843                    if oMatch.group(1) == 'END':
    47824844                        self.workerIemMcEnd(offLine + oMatch.start());
     4845                    elif oMatch.group(1) == 'BEGIN':
     4846                        self.workerIemMcBegin(sCode, oMatch.start(), offLine + oMatch.start());
    47834847                    else:
    4784                         self.workerIemMcBegin(sCode, oMatch.start(), offLine + oMatch.start());
     4848                        self.workerIemMcDeferToCImplXRet(sCode, oMatch.start(), offLine + oMatch.start(),
     4849                                                         int(oMatch.group(1)[len('DEFER_TO_CIMPL_')]));
    47854850                return True;
    47864851
     
    49525017        """
    49535018        Parses the given file.
     5019
    49545020        Returns number or errors.
    49555021        Raises exception on fatal trouble.
     
    49575023        #self.debug('Parsing %s' % (self.sSrcFile,));
    49585024
     5025        #
     5026        # Loop thru the lines.
     5027        #
     5028        # Please mind that self.iLine may be updated by checkCodeForMacro and
     5029        # other worker methods.
     5030        #
    49595031        while self.iLine < len(self.asLines):
    49605032            sLine = self.asLines[self.iLine];
  • trunk/src/VBox/VMM/VMMAll/IEMAllThreadedPython.py

    r100096 r100111  
    171171        ksVariation_64_Addr32,
    172172    );
     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    };
    173196    ## @}
    174197
     
    187210        ## List/tree of statements for the threaded function.
    188211        self.aoStmtsForThreadedFunction = [] # type: list(McStmt)
     212
     213        ## Function enum number, for verification. Set by generateThreadedFunctionsHeader.
     214        self.iEnumValue     = -1;
    189215
    190216    def getIndexName(self):
     
    770796        ## Variations for this block. There is at least one.
    771797        self.aoVariations   = []            # type: list(ThreadedFunctionVariation)
     798        ## Variation dictionary containing the same as aoVariations.
     799        self.dVariations    = {}            # type: dict(str,ThreadedFunctionVariation)
    772800        ## Dictionary of local variables (IEM_MC_LOCAL[_CONST]) and call arguments (IEM_MC_ARG*).
    773801        self.dVariables     = {}            # type: dict(str,McStmtVar)
     
    829857                                 for sVar in ThreadedFunctionVariation.kasVariationsWithoutAddress];
    830858
     859        # Dictionary variant of the list.
     860        self.dVariations = { oVar.sVariation: oVar for oVar in self.aoVariations };
     861
    831862        # Continue the analysis on each variation.
    832863        for oVariation in self.aoVariations:
     
    846877
    847878        # Currently only have variations for address mode.
    848         dByVari = { oVar.sVariation: oVar for oVar in self.aoVariations };
     879        dByVari = self.dVariations;
    849880
    850881        sExecMask = 'IEM_F_MODE_CPUMODE_MASK';
     
    10801111            '    kIemThreadedFunc_Invalid = 0,',
    10811112        ];
    1082         for oThreadedFunction in self.aoThreadedFuncs:
    1083             for oVariation in oThreadedFunction.aoVariations:
    1084                 asLines.append('    ' + oVariation.getIndexName() + ',');
     1113        iThreadedFunction = 0;
     1114        for sVariation in ThreadedFunctionVariation.kasVariationsEmitOrder:
     1115            asLines += [
     1116                    '',
     1117                    '    /*',
     1118                    '     * Variation: ' + ThreadedFunctionVariation.kdVariationNames[sVariation] + '',
     1119                    '     */',
     1120            ];
     1121            for oThreadedFunction in self.aoThreadedFuncs:
     1122                oVariation = oThreadedFunction.dVariations.get(sVariation, None);
     1123                if oVariation:
     1124                    iThreadedFunction += 1;
     1125                    oVariation.iEnumValue = iThreadedFunction;
     1126                    asLines.append('    ' + oVariation.getIndexName() + ',');
    10851127        asLines += [
    10861128            '    kIemThreadedFunc_End',
     
    11311173        # Emit the function definitions.
    11321174        #
    1133         for oThreadedFunction in self.aoThreadedFuncs:
    1134             oMcBlock = oThreadedFunction.oMcBlock;
    1135             for oVariation in oThreadedFunction.aoVariations:
    1136                 # Function header
    1137                 oOut.write(  '\n'
    1138                            + '\n'
    1139                            + '/**\n'
    1140                            + ' * %s at line %s offset %s in %s%s\n'
    1141                               % (oMcBlock.sFunction, oMcBlock.iBeginLine, oMcBlock.offBeginLine,
    1142                                  os.path.split(oMcBlock.sSrcFile)[1],
    1143                                  ' (macro expansion)' if oMcBlock.iBeginLine == oMcBlock.iEndLine else '')
    1144                            + ' */\n'
    1145                            + 'static IEM_DECL_IMPL_DEF(VBOXSTRICTRC, ' + oVariation.getFunctionName() + ',\n'
    1146                            + '                         ' + sParamList
    1147                            + '{\n');
    1148 
    1149                 aasVars = [];
    1150                 for aoRefs in oVariation.dParamRefs.values():
    1151                     oRef  = aoRefs[0];
    1152                     if oRef.sType[0] != 'P':
    1153                         cBits = g_kdTypeInfo[oRef.sType][0];
    1154                         sType = g_kdTypeInfo[oRef.sType][2];
    1155                     else:
    1156                         cBits = 64;
    1157                         sType = oRef.sType;
    1158 
    1159                     sTypeDecl = sType + ' const';
    1160 
    1161                     if cBits == 64:
    1162                         assert oRef.offNewParam == 0;
    1163                         if sType == 'uint64_t':
    1164                             sUnpack = 'uParam%s;' % (oRef.iNewParam,);
     1175        for sVariation in ThreadedFunctionVariation.kasVariationsEmitOrder:
     1176            sVarName = ThreadedFunctionVariation.kdVariationNames[sVariation];
     1177            oOut.write(  '\n'
     1178                       + '\n'
     1179                       + '\n'
     1180                       + '\n'
     1181                       + '/*' + '*' * 128 + '\n'
     1182                       + '*   Variation: ' + sVarName + ' ' * (129 - len(sVarName) - 15) + '*\n'
     1183                       + '*' * 128 + '*/\n');
     1184
     1185            for oThreadedFunction in self.aoThreadedFuncs:
     1186                oVariation = oThreadedFunction.dVariations.get(sVariation, None);
     1187                if oVariation:
     1188                    oMcBlock = oThreadedFunction.oMcBlock;
     1189
     1190                    # Function header
     1191                    oOut.write(  '\n'
     1192                               + '\n'
     1193                               + '/**\n'
     1194                               + ' * %s at line %s offset %s in %s%s\n'
     1195                                  % (oMcBlock.sFunction, oMcBlock.iBeginLine, oMcBlock.offBeginLine,
     1196                                     os.path.split(oMcBlock.sSrcFile)[1],
     1197                                     ' (macro expansion)' if oMcBlock.iBeginLine == oMcBlock.iEndLine else '')
     1198                               + ' */\n'
     1199                               + 'static IEM_DECL_IMPL_DEF(VBOXSTRICTRC, ' + oVariation.getFunctionName() + ',\n'
     1200                               + '                         ' + sParamList
     1201                               + '{\n');
     1202
     1203                    aasVars = [];
     1204                    for aoRefs in oVariation.dParamRefs.values():
     1205                        oRef  = aoRefs[0];
     1206                        if oRef.sType[0] != 'P':
     1207                            cBits = g_kdTypeInfo[oRef.sType][0];
     1208                            sType = g_kdTypeInfo[oRef.sType][2];
    11651209                        else:
    1166                             sUnpack = '(%s)uParam%s;' % (sType, oRef.iNewParam,);
    1167                     elif oRef.offNewParam == 0:
    1168                         sUnpack = '(%s)(uParam%s & %s);' % (sType, oRef.iNewParam, self.ksBitsToIntMask[cBits]);
    1169                     else:
    1170                         sUnpack = '(%s)((uParam%s >> %s) & %s);' \
    1171                                 % (sType, oRef.iNewParam, oRef.offNewParam, self.ksBitsToIntMask[cBits]);
    1172 
    1173                     sComment = '/* %s - %s ref%s */' % (oRef.sOrgRef, len(aoRefs), 's' if len(aoRefs) != 1 else '',);
    1174 
    1175                     aasVars.append([ '%s:%02u' % (oRef.iNewParam, oRef.offNewParam),
    1176                                      sTypeDecl, oRef.sNewName, sUnpack, sComment ]);
    1177                 acchVars = [0, 0, 0, 0, 0];
    1178                 for asVar in aasVars:
    1179                     for iCol, sStr in enumerate(asVar):
    1180                         acchVars[iCol] = max(acchVars[iCol], len(sStr));
    1181                 sFmt = '    %%-%ss %%-%ss = %%-%ss %%s\n' % (acchVars[1], acchVars[2], acchVars[3]);
    1182                 for asVar in sorted(aasVars):
    1183                     oOut.write(sFmt % (asVar[1], asVar[2], asVar[3], asVar[4],));
    1184 
    1185                 # RT_NOREF for unused parameters.
    1186                 if oVariation.cMinParams < g_kcThreadedParams:
    1187                     oOut.write('    RT_NOREF('
    1188                                + ', '.join(['uParam%u' % (i,) for i in range(oVariation.cMinParams, g_kcThreadedParams)])
    1189                                + ');\n');
    1190 
    1191                 # Now for the actual statements.
    1192                 oOut.write(iai.McStmt.renderCodeForList(oVariation.aoStmtsForThreadedFunction, cchIndent = 4));
    1193 
    1194                 oOut.write('}\n');
     1210                            cBits = 64;
     1211                            sType = oRef.sType;
     1212
     1213                        sTypeDecl = sType + ' const';
     1214
     1215                        if cBits == 64:
     1216                            assert oRef.offNewParam == 0;
     1217                            if sType == 'uint64_t':
     1218                                sUnpack = 'uParam%s;' % (oRef.iNewParam,);
     1219                            else:
     1220                                sUnpack = '(%s)uParam%s;' % (sType, oRef.iNewParam,);
     1221                        elif oRef.offNewParam == 0:
     1222                            sUnpack = '(%s)(uParam%s & %s);' % (sType, oRef.iNewParam, self.ksBitsToIntMask[cBits]);
     1223                        else:
     1224                            sUnpack = '(%s)((uParam%s >> %s) & %s);' \
     1225                                    % (sType, oRef.iNewParam, oRef.offNewParam, self.ksBitsToIntMask[cBits]);
     1226
     1227                        sComment = '/* %s - %s ref%s */' % (oRef.sOrgRef, len(aoRefs), 's' if len(aoRefs) != 1 else '',);
     1228
     1229                        aasVars.append([ '%s:%02u' % (oRef.iNewParam, oRef.offNewParam),
     1230                                         sTypeDecl, oRef.sNewName, sUnpack, sComment ]);
     1231                    acchVars = [0, 0, 0, 0, 0];
     1232                    for asVar in aasVars:
     1233                        for iCol, sStr in enumerate(asVar):
     1234                            acchVars[iCol] = max(acchVars[iCol], len(sStr));
     1235                    sFmt = '    %%-%ss %%-%ss = %%-%ss %%s\n' % (acchVars[1], acchVars[2], acchVars[3]);
     1236                    for asVar in sorted(aasVars):
     1237                        oOut.write(sFmt % (asVar[1], asVar[2], asVar[3], asVar[4],));
     1238
     1239                    # RT_NOREF for unused parameters.
     1240                    if oVariation.cMinParams < g_kcThreadedParams:
     1241                        oOut.write('    RT_NOREF('
     1242                                   + ', '.join(['uParam%u' % (i,) for i in range(oVariation.cMinParams, g_kcThreadedParams)])
     1243                                   + ');\n');
     1244
     1245                    # Now for the actual statements.
     1246                    oOut.write(iai.McStmt.renderCodeForList(oVariation.aoStmtsForThreadedFunction, cchIndent = 4));
     1247
     1248                    oOut.write('}\n');
    11951249
    11961250
     
    12071261                   + '    /*Invalid*/ NULL, \n');
    12081262        iThreadedFunction = 0;
    1209         for oThreadedFunction in self.aoThreadedFuncs:
    1210             for oVariation in oThreadedFunction.aoVariations:
    1211                 iThreadedFunction += 1;
    1212                 oOut.write('    /*%4u*/ %s,\n' % (iThreadedFunction, oVariation.getFunctionName(),));
     1263        for sVariation in ThreadedFunctionVariation.kasVariationsEmitOrder:
     1264            oOut.write(  '\n'
     1265                       + '    /*\n'
     1266                       + '     * Variation: ' + ThreadedFunctionVariation.kdVariationNames[sVariation] + '\n'
     1267                       + '     */\n');
     1268            for oThreadedFunction in self.aoThreadedFuncs:
     1269                oVariation = oThreadedFunction.dVariations.get(sVariation, None);
     1270                if oVariation:
     1271                    iThreadedFunction += 1;
     1272                    assert oVariation.iEnumValue == iThreadedFunction;
     1273                    oOut.write('    /*%4u*/ %s,\n' % (iThreadedFunction, oVariation.getFunctionName(),));
    12131274        oOut.write('};\n');
    12141275
     
    12231284            return self.aoThreadedFuncs[idx];
    12241285        return ThreadedFunction.dummyInstance();
    1225 
    1226     def findEndOfMcEndStmt(self, sLine, offEndStmt):
    1227         """
    1228         Helper that returns the line offset following the 'IEM_MC_END();'.
    1229         """
    1230         assert sLine[offEndStmt:].startswith('IEM_MC_END');
    1231         off = sLine.find(';', offEndStmt + len('IEM_MC_END'));
    1232         assert off > 0, 'sLine="%s"' % (sLine, );
    1233         return off + 1 if off > 0 else 99999998;
    12341286
    12351287    def generateModifiedInput(self, oOut):
     
    12741326                    sLine = oParser.asLines[iLine - 1];
    12751327                    assert sLine.count('IEM_MC_') == 1;
    1276                     oOut.write(sLine[self.findEndOfMcEndStmt(sLine, oThreadedFunction.oMcBlock.offEndLine) : ]);
     1328                    oOut.write(sLine[oThreadedFunction.oMcBlock.offAfterEnd : ]);
    12771329
    12781330                    # Advance
     
    12881340
    12891341                        sModified = oThreadedFunction.generateInputCode().strip();
    1290                         assert sModified.startswith('IEM_MC_BEGIN'), 'sModified="%s"' % (sModified,);
     1342                        assert (   sModified.startswith('IEM_MC_BEGIN')
     1343                                or sModified.startswith('IEM_MC_DEFER_TO_CIMPL_')), 'sModified="%s"' % (sModified,);
    12911344                        oOut.write(sModified);
    12921345
    1293                         offLine = self.findEndOfMcEndStmt(sLine, oThreadedFunction.oMcBlock.offEndLine);
     1346                        offLine = oThreadedFunction.oMcBlock.offAfterEnd;
    12941347
    12951348                        # Advance
Note: See TracChangeset for help on using the changeset viewer.

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