Changeset 37386 in webkit


Ignore:
Timestamp:
Oct 7, 2008 1:27:50 PM (16 years ago)
Author:
barraclough@apple.com
Message:

2008-10-07 Gavin Barraclough <barraclough@apple.com>

Reviewed by Oliver Hunt.

Move callframe initialization into JIT code, again.


As a part of the restructuring the second result from functions is now
returned in edx, allowing the new value of 'r' to be returned via a
register, and stored to the stack from JIT code, too.

4.5% progression on v8-tests. (3% in their harness)

  • VM/CTI.cpp: (JSC::): (JSC::CTI::emitCall): (JSC::CTI::compileOpCall): (JSC::CTI::privateCompileMainPass): (JSC::CTI::privateCompileSlowCases): (JSC::CTI::privateCompile):
  • VM/CTI.h: (JSC::CallRecord::CallRecord):
  • VM/Machine.cpp: (JSC::Machine::cti_op_call_JSFunction): (JSC::Machine::cti_op_construct_JSConstruct): (JSC::Machine::cti_op_resolve_func): (JSC::Machine::cti_op_post_inc): (JSC::Machine::cti_op_resolve_with_base): (JSC::Machine::cti_op_post_dec):
  • VM/Machine.h:
  • kjs/JSFunction.h:
  • kjs/ScopeChain.h:
Location:
trunk/JavaScriptCore
Files:
7 edited

Legend:

Unmodified
Added
Removed
  • trunk/JavaScriptCore/ChangeLog

    r37383 r37386  
     12008-10-07  Gavin Barraclough  <barraclough@apple.com>
     2
     3        Reviewed by Oliver Hunt.
     4
     5        Move callframe initialization into JIT code, again.
     6       
     7        As a part of the restructuring the second result from functions is now
     8        returned in edx, allowing the new value of 'r' to be returned via a
     9        register, and stored to the stack from JIT code, too.
     10
     11        4.5% progression on v8-tests. (3% in their harness)
     12
     13        * VM/CTI.cpp:
     14        (JSC::):
     15        (JSC::CTI::emitCall):
     16        (JSC::CTI::compileOpCall):
     17        (JSC::CTI::privateCompileMainPass):
     18        (JSC::CTI::privateCompileSlowCases):
     19        (JSC::CTI::privateCompile):
     20        * VM/CTI.h:
     21        (JSC::CallRecord::CallRecord):
     22        * VM/Machine.cpp:
     23        (JSC::Machine::cti_op_call_JSFunction):
     24        (JSC::Machine::cti_op_construct_JSConstruct):
     25        (JSC::Machine::cti_op_resolve_func):
     26        (JSC::Machine::cti_op_post_inc):
     27        (JSC::Machine::cti_op_resolve_with_base):
     28        (JSC::Machine::cti_op_post_dec):
     29        * VM/Machine.h:
     30        * kjs/JSFunction.h:
     31        * kjs/ScopeChain.h:
     32
    1332008-10-07  Mark Rowe  <mrowe@apple.com>
    234
  • trunk/JavaScriptCore/VM/CTI.cpp

    r37381 r37386  
    3131#include "CodeBlock.h"
    3232#include "JSArray.h"
     33#include "JSFunction.h"
    3334#include "Machine.h"
    3435#include "wrec/WREC.h"
     
    8384    "subl $0x24, %esp" "\n"
    8485    "movl $512, %esi" "\n"
     86    "movl 0x38(%esp), %edi" "\n" // Ox38 = 0x0E * 4, 0x0E = CTI_ARGS_r
    8587    "call *0x30(%esp)" "\n" // Ox30 = 0x0C * 4, 0x0C = CTI_ARGS_code
    8688    "addl $0x24, %esp" "\n"
     
    112114            mov esi, 512;
    113115            mov [esp], esp;
     116            mov edi, [esp + 0x38];
    114117            call [esp + 0x30];
    115118            add esp, 0x24;
     
    363366}
    364367
     368ALWAYS_INLINE X86Assembler::JmpSrc CTI::emitCall(unsigned opcodeIndex, CTIHelper_2 helper)
     369{
     370#if ENABLE(SAMPLING_TOOL)
     371    m_jit.movl_i32m(1, &inCalledCode);
     372#endif
     373    m_jit.emitRestoreArgumentReference();
     374    X86Assembler::JmpSrc call = m_jit.emitCall();
     375    m_calls.append(CallRecord(call, helper, opcodeIndex));
     376#if ENABLE(SAMPLING_TOOL)
     377    m_jit.movl_i32m(0, &inCalledCode);
     378#endif
     379
     380    return call;
     381}
     382
    365383ALWAYS_INLINE void CTI::emitJumpSlowCaseIfNotJSCell(X86Assembler::RegisterID reg, unsigned opcodeIndex)
    366384{
     
    461479#endif
    462480
     481void CTI::compileOpCallInitializeCallFrame(unsigned callee, unsigned argCount)
     482{
     483    emitGetArg(callee, X86::ecx); // Load callee JSFunction into ecx
     484    m_jit.movl_rm(X86::eax, RegisterFile::CodeBlock * static_cast<int>(sizeof(Register)), X86::edx); // callee CodeBlock was returned in eax
     485    m_jit.movl_i32m(reinterpret_cast<unsigned>(nullJSValue), RegisterFile::OptionalCalleeArguments * static_cast<int>(sizeof(Register)), X86::edx);
     486    m_jit.movl_rm(X86::ecx, RegisterFile::Callee * static_cast<int>(sizeof(Register)), X86::edx);
     487
     488    m_jit.movl_mr(OBJECT_OFFSET(JSFunction, m_scopeChain) + OBJECT_OFFSET(ScopeChain, m_node), X86::ecx, X86::ecx); // newScopeChain
     489    m_jit.movl_i32m(argCount, RegisterFile::ArgumentCount * static_cast<int>(sizeof(Register)), X86::edx);
     490    m_jit.movl_rm(X86::edi, RegisterFile::CallerRegisters * static_cast<int>(sizeof(Register)), X86::edx);
     491    m_jit.movl_rm(X86::ecx, RegisterFile::ScopeChain * static_cast<int>(sizeof(Register)), X86::edx);
     492}
     493
    463494void CTI::compileOpCall(Instruction* instruction, unsigned i, CompileOpCallType type)
    464495{
    465496    int dst = instruction[i + 1].u.operand;
     497    int callee = instruction[i + 2].u.operand;
    466498    int firstArg = instruction[i + 4].u.operand;
    467499    int argCount = instruction[i + 5].u.operand;
     
    494526    X86Assembler::JmpSrc wasEval;
    495527    if (type == OpCallEval) {
    496         emitGetPutArg(instruction[i + 2].u.operand, 0, X86::ecx);
     528        emitGetPutArg(callee, 0, X86::ecx);
    497529        emitCall(i, Machine::cti_op_call_eval);
    498 
    499         emitGetCTIParam(CTI_ARGS_r, X86::edi); // edi := r
    500530
    501531        m_jit.cmpl_i32r(reinterpret_cast<unsigned>(JSImmediate::impossibleValue()), X86::eax);
     
    503533
    504534        // this sets up the first arg to op_cti_call (func), and explicitly leaves the value in ecx (checked just below).
    505         emitGetArg(instruction[i + 2].u.operand, X86::ecx);
     535        emitGetArg(callee, X86::ecx);
    506536    } else {
    507537        // this sets up the first arg to op_cti_call (func), and explicitly leaves the value in ecx (checked just below).
    508         emitGetPutArg(instruction[i + 2].u.operand, 0, X86::ecx);
     538        emitGetPutArg(callee, 0, X86::ecx);
    509539    }
    510540
     
    523553
    524554    // This handles JSFunctions
    525     emitCall(i, ((type == OpConstruct) ? Machine::cti_op_construct_JSConstruct : Machine::cti_op_call_JSFunction));
     555    emitCall(i, (type == OpConstruct) ? Machine::cti_op_construct_JSConstruct : Machine::cti_op_call_JSFunction);
     556
     557    compileOpCallInitializeCallFrame(callee, argCount);
     558
     559    // load ctiCode from the new codeBlock.
     560    m_jit.movl_mr(OBJECT_OFFSET(CodeBlock, ctiCode), X86::eax, X86::eax);
     561
     562    // Setup the new value of 'r' in edi, and on the stack, too.
     563    emitPutCTIParam(X86::edx, CTI_ARGS_r);
     564    m_jit.movl_rr(X86::edx, X86::edi);
    526565
    527566    // Check the ctiCode has been generated - if not, this is handled in a slow case.
     
    12801319            emitCall(i, Machine::cti_op_resolve_func);
    12811320            emitPutResult(instruction[i + 1].u.operand);
    1282             emitGetCTIParam(CTI_ARGS_2ndResult, X86::eax);
    1283             emitPutResult(instruction[i + 2].u.operand);
     1321            emitPutResult(instruction[i + 2].u.operand, X86::edx);
    12841322            i += 4;
    12851323            break;
     
    15651603            emitCall(i, Machine::cti_op_resolve_with_base);
    15661604            emitPutResult(instruction[i + 1].u.operand);
    1567             emitGetCTIParam(CTI_ARGS_2ndResult, X86::eax);
    1568             emitPutResult(instruction[i + 2].u.operand);
     1605            emitPutResult(instruction[i + 2].u.operand, X86::edx);
    15691606            i += 4;
    15701607            break;
     
    23282365            emitCall(i, Machine::cti_op_post_inc);
    23292366            emitPutResult(instruction[i + 1].u.operand);
    2330             emitGetCTIParam(CTI_ARGS_2ndResult, X86::eax);
    2331             emitPutResult(srcDst);
     2367            emitPutResult(srcDst, X86::edx);
    23322368            i += 3;
    23332369            break;
     
    23842420            emitCall(i, Machine::cti_op_post_dec);
    23852421            emitPutResult(instruction[i + 1].u.operand);
    2386             emitGetCTIParam(CTI_ARGS_2ndResult, X86::eax);
    2387             emitPutResult(srcDst);
     2422            emitPutResult(srcDst, X86::edx);
    23882423            i += 3;
    23892424            break;
     
    25002535    // Could use a popl_m, but would need to offset the following instruction if so.
    25012536    m_jit.popl_r(X86::ecx);
    2502     emitGetCTIParam(CTI_ARGS_r, X86::edi); // edi := r
    25032537    emitPutToCallFrameHeader(X86::ecx, RegisterFile::ReturnPC);
    25042538
  • trunk/JavaScriptCore/VM/CTI.h

    r37316 r37386  
    4646#endif
    4747
    48 #define CTI_ARGS_2ndResult 0x08
    49 
    5048#define CTI_ARGS_code 0x0C
    5149#define CTI_ARGS_registerFile 0x0D
     
    6361
    6462#define ARG_setR(newR) (*(Register**)&(ARGS)[CTI_ARGS_r] = newR)
    65 #define ARG_set2ndResult(new2ndResult) (*(JSValue**)&(ARGS)[CTI_ARGS_2ndResult] = new2ndResult)
    6663
    6764#define ARG_src1 ((JSValue*)((ARGS)[1]))
     
    109106    struct OperandTypes;
    110107
     108    struct VoidPtrPair { void* first; void* second; };
     109
    111110    typedef JSValue* (*CTIHelper_j)(CTI_ARGS);
    112111    typedef JSPropertyNameIterator* (*CTIHelper_p)(CTI_ARGS);
     
    114113    typedef void* (*CTIHelper_s)(CTI_ARGS);
    115114    typedef int (*CTIHelper_b)(CTI_ARGS);
     115    typedef VoidPtrPair (*CTIHelper_2)(CTI_ARGS);
    116116
    117117    struct CallRecord {
     
    153153       
    154154        CallRecord(X86Assembler::JmpSrc f, CTIHelper_b t, unsigned i)
     155            : from(f)
     156            , to((void*)t)
     157            , opcodeIndex(i)
     158        {
     159        }
     160
     161        CallRecord(X86Assembler::JmpSrc f, CTIHelper_2 t, unsigned i)
    155162            : from(f)
    156163            , to((void*)t)
     
    351358        enum CompileOpCallType { OpCallNormal, OpCallEval, OpConstruct };
    352359        void compileOpCall(Instruction* instruction, unsigned i, CompileOpCallType type = OpCallNormal);
     360        void compileOpCallInitializeCallFrame(unsigned callee, unsigned argCount);
    353361        enum CompileOpStrictEqType { OpStrictEq, OpNStrictEq };
    354362        void compileOpStrictEq(Instruction* instruction, unsigned i, CompileOpStrictEqType type);
     
    393401        X86Assembler::JmpSrc emitCall(unsigned opcodeIndex, CTIHelper_j);
    394402        X86Assembler::JmpSrc emitCall(unsigned opcodeIndex, CTIHelper_p);
    395         X86Assembler::JmpSrc emitCall(unsigned opcodeIndex, CTIHelper_b);
    396403        X86Assembler::JmpSrc emitCall(unsigned opcodeIndex, CTIHelper_v);
    397404        X86Assembler::JmpSrc emitCall(unsigned opcodeIndex, CTIHelper_s);
    398        
     405        X86Assembler::JmpSrc emitCall(unsigned opcodeIndex, CTIHelper_b);
     406        X86Assembler::JmpSrc emitCall(unsigned opcodeIndex, CTIHelper_2);
     407
    399408        void emitGetVariableObjectRegister(X86Assembler::RegisterID variableObject, int index, X86Assembler::RegisterID dst);
    400409        void emitPutVariableObjectRegister(X86Assembler::RegisterID src, X86Assembler::RegisterID variableObject, int index);
  • trunk/JavaScriptCore/VM/Machine.cpp

    r37381 r37386  
    42364236        return 0; \
    42374237    } while (0)
     4238#define VM_THROW_EXCEPTION_2() \
     4239    do { \
     4240        VM_THROW_EXCEPTION_AT_END(); \
     4241        VoidPtrPair pair = { (void*)0, (void*)0, }; \
     4242        return pair; \
     4243    } while (0)
    42384244#define VM_THROW_EXCEPTION_AT_END() \
    42394245    do { \
     
    42674273        } \
    42684274    } while (0)
     4275#define VM_CHECK_EXCEPTION_2() \
     4276    do { \
     4277        if (UNLIKELY(ARG_globalData->exception != 0)) { \
     4278            VM_THROW_EXCEPTION_2(); \
     4279        } \
     4280    } while (0)
    42694281
    42704282JSValue* Machine::cti_op_convert_this(CTI_ARGS)
     
    45614573}
    45624574
    4563 void* Machine::cti_op_call_JSFunction(CTI_ARGS)
     4575VoidPtrPair Machine::cti_op_call_JSFunction(CTI_ARGS)
    45644576{
    45654577#ifndef NDEBUG
     
    45774589    if (UNLIKELY(!r)) {
    45784590        ARG_globalData->exception = createStackOverflowError(CallFrame::create(ARG_r));
    4579         VM_THROW_EXCEPTION();
    4580     }
    4581 
    4582     r[RegisterFile::CodeBlock] = newCodeBlock;
    4583     r[RegisterFile::ScopeChain] = callDataScopeChain;
    4584     r[RegisterFile::CallerRegisters] = ARG_r;
    4585     // RegisterFile::ReturnPC is set by callee
    4586     // RegisterFile::ReturnValueRegister is set by caller
    4587     r[RegisterFile::ArgumentCount] = ARG_int3; // original argument count (for the sake of the "arguments" object)
    4588     r[RegisterFile::Callee] = ARG_src1;
    4589     r[RegisterFile::OptionalCalleeArguments] = nullJSValue;
    4590 
    4591     ARG_setR(r);
    4592 
    4593     return newCodeBlock->ctiCode;
     4591        VM_THROW_EXCEPTION_2();
     4592    }
     4593
     4594    VoidPtrPair pair = { newCodeBlock, r };
     4595    return pair;
    45944596}
    45954597
     
    47434745}
    47444746
    4745 void* Machine::cti_op_construct_JSConstruct(CTI_ARGS)
     4747VoidPtrPair Machine::cti_op_construct_JSConstruct(CTI_ARGS)
    47464748{
    47474749    RegisterFile* registerFile = ARG_registerFile;
    47484750    Register* r = ARG_r;
    47494751
    4750     JSValue* constrVal = ARG_src1;
     4752    JSFunction* constructor = static_cast<JSFunction*>(ARG_src1);
    47514753    JSValue* constrProtoVal = ARG_src2;
    47524754    int firstArg = ARG_int3;
     
    47564758#ifndef NDEBUG
    47574759    ConstructData constructData;
    4758     ASSERT(constrVal->getConstructData(constructData) == ConstructTypeJS);
     4760    ASSERT(ARG_src1->getConstructData(constructData) == ConstructTypeJS);
    47594761#endif
    4760 
    4761     JSFunction* constructor = static_cast<JSFunction*>(constrVal);
    47624762
    47634763    if (*ARG_profilerReference)
     
    47804780    if (UNLIKELY(!r)) {
    47814781        ARG_globalData->exception = createStackOverflowError(CallFrame::create(ARG_r));
    4782         VM_THROW_EXCEPTION();
    4783     }
    4784 
    4785     r[RegisterFile::CodeBlock] = newCodeBlock;
    4786     r[RegisterFile::ScopeChain] = callDataScopeChain;
    4787     r[RegisterFile::CallerRegisters] = ARG_r;
    4788     // RegisterFile::ReturnPC is set by callee
    4789     // RegisterFile::ReturnValueRegister is set by caller
    4790     r[RegisterFile::ArgumentCount] = argCount; // original argument count (for the sake of the "arguments" object)
    4791     r[RegisterFile::Callee] = constructor;
    4792     r[RegisterFile::OptionalCalleeArguments] = nullJSValue;
    4793 
    4794     ARG_setR(r);
    4795     return newCodeBlock->ctiCode;
     4782        VM_THROW_EXCEPTION_2();
     4783    }
     4784
     4785    VoidPtrPair pair = { newCodeBlock, r };
     4786    return pair;
    47964787}
    47974788
     
    48654856}
    48664857
    4867 JSValue* Machine::cti_op_resolve_func(CTI_ARGS)
     4858VoidPtrPair Machine::cti_op_resolve_func(CTI_ARGS)
    48684859{
    48694860    ExecState* exec = ARG_exec;
     
    48954886            VM_CHECK_EXCEPTION_AT_END();
    48964887
    4897             ARG_set2ndResult(result);
    4898             return thisObj;
     4888            VoidPtrPair pair = { thisObj, result };
     4889            return pair;
    48994890        }
    49004891        ++iter;
     
    49054896    unsigned vPCIndex = codeBlock->ctiReturnAddressVPCMap.get(CTI_RETURN_ADDRESS);
    49064897    exec->setException(createUndefinedVariableError(exec, ident, codeBlock->instructions.begin() + vPCIndex, codeBlock));
    4907     VM_THROW_EXCEPTION();
     4898    VM_THROW_EXCEPTION_2();
    49084899}
    49094900
     
    51395130}
    51405131
    5141 JSValue* Machine::cti_op_post_inc(CTI_ARGS)
     5132VoidPtrPair Machine::cti_op_post_inc(CTI_ARGS)
    51425133{
    51435134    JSValue* v = ARG_src1;
     
    51465137
    51475138    JSValue* number = v->toJSNumber(exec);
    5148     VM_CHECK_EXCEPTION();
    5149     ARG_set2ndResult(jsNumber(ARG_globalData, number->uncheckedGetNumber() + 1));
    5150     return number;
     5139    VM_CHECK_EXCEPTION_2();
     5140
     5141    VoidPtrPair pair = { number, jsNumber(ARG_globalData, number->uncheckedGetNumber() + 1) };
     5142    return pair;
    51515143}
    51525144
     
    52305222}
    52315223
    5232 JSValue* Machine::cti_op_resolve_with_base(CTI_ARGS)
     5224VoidPtrPair Machine::cti_op_resolve_with_base(CTI_ARGS)
    52335225{
    52345226    ExecState* exec = ARG_exec;
     
    52515243            JSValue* result = slot.getValue(exec, ident);
    52525244            VM_CHECK_EXCEPTION_AT_END();
    5253             ARG_set2ndResult(result);
    5254             return base;
     5245
     5246            VoidPtrPair pair = { base, result };
     5247            return pair;
    52555248        }
    52565249        ++iter;
     
    52615254    unsigned vPCIndex = codeBlock->ctiReturnAddressVPCMap.get(CTI_RETURN_ADDRESS);
    52625255    exec->setException(createUndefinedVariableError(exec, ident, codeBlock->instructions.begin() + vPCIndex, codeBlock));
    5263     VM_THROW_EXCEPTION();
     5256    VM_THROW_EXCEPTION_2();
    52645257}
    52655258
     
    53025295}
    53035296
    5304 JSValue* Machine::cti_op_post_dec(CTI_ARGS)
     5297VoidPtrPair Machine::cti_op_post_dec(CTI_ARGS)
    53055298{
    53065299    JSValue* v = ARG_src1;
     
    53095302
    53105303    JSValue* number = v->toJSNumber(exec);
    5311     VM_CHECK_EXCEPTION();
    5312 
    5313     ARG_set2ndResult(jsNumber(ARG_globalData, number->uncheckedGetNumber() - 1));
    5314     return number;
     5304    VM_CHECK_EXCEPTION_2();
     5305
     5306    VoidPtrPair pair = { number, jsNumber(ARG_globalData, number->uncheckedGetNumber() - 1) };
     5307    return pair;
    53155308}
    53165309
  • trunk/JavaScriptCore/VM/Machine.h

    r37366 r37386  
    162162        static JSValue* SFX_CALL cti_op_mul(CTI_ARGS);
    163163        static JSValue* SFX_CALL cti_op_new_func(CTI_ARGS);
    164         static void* SFX_CALL cti_op_call_JSFunction(CTI_ARGS);
     164        static VoidPtrPair SFX_CALL cti_op_call_JSFunction(CTI_ARGS);
    165165        static JSValue* SFX_CALL cti_op_call_NotJSFunction(CTI_ARGS);
    166166        static void SFX_CALL cti_op_create_arguments(CTI_ARGS);
     
    172172        static JSValue* SFX_CALL cti_op_resolve(CTI_ARGS);
    173173        static JSValue* SFX_CALL cti_op_resolve_global(CTI_ARGS);
    174         static void* SFX_CALL cti_op_construct_JSConstruct(CTI_ARGS);
     174        static VoidPtrPair SFX_CALL cti_op_construct_JSConstruct(CTI_ARGS);
    175175        static JSValue* SFX_CALL cti_op_construct_NotJSConstruct(CTI_ARGS);
    176176        static JSValue* SFX_CALL cti_op_get_by_val(CTI_ARGS);
    177         static JSValue* SFX_CALL cti_op_resolve_func(CTI_ARGS);
     177        static VoidPtrPair SFX_CALL cti_op_resolve_func(CTI_ARGS);
    178178        static JSValue* SFX_CALL cti_op_sub(CTI_ARGS);
    179179        static void SFX_CALL cti_op_put_by_val(CTI_ARGS);
     
    189189        static JSValue* SFX_CALL cti_op_not(CTI_ARGS);
    190190        static int SFX_CALL cti_op_jtrue(CTI_ARGS);
    191         static JSValue* SFX_CALL cti_op_post_inc(CTI_ARGS);
     191        static VoidPtrPair SFX_CALL cti_op_post_inc(CTI_ARGS);
    192192        static JSValue* SFX_CALL cti_op_eq(CTI_ARGS);
    193193        static JSValue* SFX_CALL cti_op_lshift(CTI_ARGS);
     
    195195        static JSValue* SFX_CALL cti_op_rshift(CTI_ARGS);
    196196        static JSValue* SFX_CALL cti_op_bitnot(CTI_ARGS);
    197         static JSValue* SFX_CALL cti_op_resolve_with_base(CTI_ARGS);
     197        static VoidPtrPair SFX_CALL cti_op_resolve_with_base(CTI_ARGS);
    198198        static JSValue* SFX_CALL cti_op_new_func_exp(CTI_ARGS);
    199199        static JSValue* SFX_CALL cti_op_mod(CTI_ARGS);
    200200        static JSValue* SFX_CALL cti_op_less(CTI_ARGS);
    201201        static JSValue* SFX_CALL cti_op_neq(CTI_ARGS);
    202         static JSValue* SFX_CALL cti_op_post_dec(CTI_ARGS);
     202        static VoidPtrPair SFX_CALL cti_op_post_dec(CTI_ARGS);
    203203        static JSValue* SFX_CALL cti_op_urshift(CTI_ARGS);
    204204        static JSValue* SFX_CALL cti_op_bitxor(CTI_ARGS);
  • trunk/JavaScriptCore/kjs/JSFunction.h

    r37184 r37386  
    3939
    4040    class JSFunction : public InternalFunction {
     41        friend class CTI;
    4142        friend class Machine;
    4243
  • trunk/JavaScriptCore/kjs/ScopeChain.h

    r37259 r37386  
    154154
    155155    class ScopeChain {
     156        friend class CTI;
    156157    public:
    157158        ScopeChain(NoScopeChain)
Note: See TracChangeset for help on using the changeset viewer.