]> git.saurik.com Git - apple/javascriptcore.git/blobdiff - llint/LowLevelInterpreter.cpp
JavaScriptCore-1218.33.tar.gz
[apple/javascriptcore.git] / llint / LowLevelInterpreter.cpp
index b95a50082e9f258919cf126033d055f8fa532eec..a616ce9f0c7c93226cf954734cfab4cddfba726d 100644 (file)
 #include "LLIntOfflineAsmConfig.h"
 #include <wtf/InlineASM.h>
 
+#if ENABLE(LLINT_C_LOOP)
+#include "CodeBlock.h"
+#include "LLIntCLoop.h"
+#include "LLIntSlowPaths.h"
+#include "Operations.h"
+#include "VMInspector.h"
+#include <wtf/Assertions.h>
+#include <wtf/MathExtras.h>
+
+using namespace JSC::LLInt;
+
+// LLInt C Loop opcodes
+// ====================
+// In the implementation of the C loop, the LLint trampoline glue functions
+// (e.g. llint_program_prologue, llint_eval_prologue, etc) are addressed as
+// if they are bytecode handlers. That means the names of the trampoline
+// functions will be added to the OpcodeID list via the
+// FOR_EACH_LLINT_OPCODE_EXTENSION() macro that FOR_EACH_OPCODE_ID()
+// includes.
+//
+// In addition, some JIT trampoline functions which are needed by LLInt
+// (e.g. getHostCallReturnValue, ctiOpThrowNotCaught) are also added as
+// bytecodes, and the CLoop will provide bytecode handlers for them.
+//
+// In the CLoop, we can only dispatch indirectly to these bytecodes
+// (including the LLInt and JIT extensions). All other dispatches
+// (i.e. goto's) must be to a known label (i.e. local / global labels).
+
+
+// How are the opcodes named?
+// ==========================
+// Here is a table to show examples of how each of the manifestation of the
+// opcodes are named:
+//
+//   Type:                        Opcode            Trampoline Glue
+//                                ======            ===============
+//   [In the llint .asm files]
+//   llint labels:                llint_op_enter    llint_program_prologue
+//
+//   OpcodeID:                    op_enter          llint_program
+//                                [in Opcode.h]     [in LLIntOpcode.h]
+//
+//   When using a switch statement dispatch in the CLoop, each "opcode" is
+//   a case statement:
+//   Opcode:                      case op_enter:    case llint_program_prologue:
+//
+//   When using a computed goto dispatch in the CLoop, each opcode is a label:
+//   Opcode:                      op_enter:         llint_program_prologue:
+
+
+//============================================================================
+// Define the opcode dispatch mechanism when using the C loop:
+//
+
+// These are for building a C Loop interpreter:
+#define OFFLINE_ASM_BEGIN
+#define OFFLINE_ASM_END
+
+
+#define OFFLINE_ASM_OPCODE_LABEL(opcode) DEFINE_OPCODE(opcode)
+#if ENABLE(COMPUTED_GOTO_OPCODES)
+    #define OFFLINE_ASM_GLUE_LABEL(label)  label:
+#else
+    #define OFFLINE_ASM_GLUE_LABEL(label)  case label: label:
+#endif
+
+#define OFFLINE_ASM_LOCAL_LABEL(label)   label:
+
+
+//============================================================================
+// Some utilities:
+//
+
+namespace JSC {
+namespace LLInt {
+
+#if USE(JSVALUE32_64)
+static double Ints2Double(uint32_t lo, uint32_t hi)
+{
+    union {
+        double dval;
+        uint64_t ival64;
+    } u;
+    u.ival64 = (static_cast<uint64_t>(hi) << 32) | lo;
+    return u.dval;
+}
+
+static void Double2Ints(double val, uint32_t& lo, uint32_t& hi)
+{
+    union {
+        double dval;
+        uint64_t ival64;
+    } u;
+    u.dval = val;
+    hi = static_cast<uint32_t>(u.ival64 >> 32);
+    lo = static_cast<uint32_t>(u.ival64);
+}
+#endif // USE(JSVALUE32_64)
+
+} // namespace LLint
+
+
+//============================================================================
+// CLoopRegister is the storage for an emulated CPU register.
+// It defines the policy of how ints smaller than intptr_t are packed into the
+// pseudo register, as well as hides endianness differences.
+
+struct CLoopRegister {
+    union {
+        intptr_t i;
+        uintptr_t u;
+#if USE(JSVALUE64)
+#if CPU(BIG_ENDIAN)
+        struct {
+            int32_t i32padding;
+            int32_t i32;
+        };
+        struct {
+            uint32_t u32padding;
+            uint32_t u32;
+        };
+        struct {
+            int8_t i8padding[7];
+            int8_t i8;
+        };
+        struct {
+            uint8_t u8padding[7];
+            uint8_t u8;
+        };
+#else // !CPU(BIG_ENDIAN)
+        struct {
+            int32_t i32;
+            int32_t i32padding;
+        };
+        struct {
+            uint32_t u32;
+            uint32_t u32padding;
+        };
+        struct {
+            int8_t i8;
+            int8_t i8padding[7];
+        };
+        struct {
+            uint8_t u8;
+            uint8_t u8padding[7];
+        };
+#endif // !CPU(BIG_ENDIAN)
+#else // !USE(JSVALUE64)
+        int32_t i32;
+        uint32_t u32;
+
+#if CPU(BIG_ENDIAN)
+        struct {
+            int8_t i8padding[3];
+            int8_t i8;
+        };
+        struct {
+            uint8_t u8padding[3];
+            uint8_t u8;
+        };
+
+#else // !CPU(BIG_ENDIAN)
+        struct {
+            int8_t i8;
+            int8_t i8padding[3];
+        };
+        struct {
+            uint8_t u8;
+            uint8_t u8padding[3];
+        };
+#endif // !CPU(BIG_ENDIAN)
+#endif // !USE(JSVALUE64)
+
+        int8_t* i8p;
+        void* vp;
+        ExecState* execState;
+        void* instruction;
+        NativeFunction nativeFunc;
+#if USE(JSVALUE64)
+        int64_t i64;
+        uint64_t u64;
+        EncodedJSValue encodedJSValue;
+        double castToDouble;
+#endif
+        Opcode opcode;
+    };
+
+#if USE(JSVALUE64)
+    inline void clearHighWord() { i32padding = 0; }
+#else
+    inline void clearHighWord() { }
+#endif
+};
+
+//============================================================================
+// The llint C++ interpreter loop:
+//
+
+JSValue CLoop::execute(CallFrame* callFrame, OpcodeID bootstrapOpcodeId,
+                       bool isInitializationPass)
+{
+    #define CAST reinterpret_cast
+    #define SIGN_BIT32(x) ((x) & 0x80000000)
+
+    // One-time initialization of our address tables. We have to put this code
+    // here because our labels are only in scope inside this function. The
+    // caller (or one of its ancestors) is responsible for ensuring that this
+    // is only called once during the initialization of the VM before threads
+    // are at play.
+    if (UNLIKELY(isInitializationPass)) {
+#if ENABLE(COMPUTED_GOTO_OPCODES)
+        Opcode* opcodeMap = LLInt::opcodeMap();
+        #define OPCODE_ENTRY(__opcode, length) \
+            opcodeMap[__opcode] = bitwise_cast<void*>(&&__opcode);
+        FOR_EACH_OPCODE_ID(OPCODE_ENTRY)
+        #undef OPCODE_ENTRY
+
+        #define LLINT_OPCODE_ENTRY(__opcode, length) \
+            opcodeMap[__opcode] = bitwise_cast<void*>(&&__opcode);
+
+        FOR_EACH_LLINT_NATIVE_HELPER(LLINT_OPCODE_ENTRY)
+        #undef LLINT_OPCODE_ENTRY
+#endif
+        // Note: we can only set the exceptionInstructions after we have
+        // initialized the opcodeMap above. This is because getCodePtr()
+        // can depend on the opcodeMap.
+        Instruction* exceptionInstructions = LLInt::exceptionInstructions();
+        for (int i = 0; i < maxOpcodeLength + 1; ++i)
+            exceptionInstructions[i].u.pointer =
+                LLInt::getCodePtr(llint_throw_from_slow_path_trampoline);
+
+        return JSValue();
+    }
+
+    ASSERT(callFrame->vm().topCallFrame == callFrame);
+
+    // Define the pseudo registers used by the LLINT C Loop backend:
+    ASSERT(sizeof(CLoopRegister) == sizeof(intptr_t));
+
+    union CLoopDoubleRegister {
+        double d;
+#if USE(JSVALUE64)
+        int64_t castToInt64;
+#endif
+    };
+
+    // The CLoop llint backend is initially based on the ARMv7 backend, and
+    // then further enhanced with a few instructions from the x86 backend to
+    // support building for X64 targets. Hence, the shape of the generated
+    // code and the usage convention of registers will look a lot like the
+    // ARMv7 backend's.
+    //
+    // For example, on a 32-bit build:
+    // 1. Outgoing args will be set up as follows:
+    //    arg1 in t0 (r0 on ARM)
+    //    arg2 in t1 (r1 on ARM)
+    // 2. 32 bit return values will be in t0 (r0 on ARM).
+    // 3. 64 bit return values (e.g. doubles) will be in t0,t1 (r0,r1 on ARM).
+    //
+    // But instead of naming these simulator registers based on their ARM
+    // counterparts, we'll name them based on their original llint asm names.
+    // This will make it easier to correlate the generated code with the
+    // original llint asm code.
+    //
+    // On a 64-bit build, it more like x64 in that the registers are 64 bit.
+    // Hence:
+    // 1. Outgoing args are still the same: arg1 in t0, arg2 in t1, etc.
+    // 2. 32 bit result values will be in the low 32-bit of t0.
+    // 3. 64 bit result values will be in t0.
+
+    CLoopRegister t0, t1, t2, t3;
+#if USE(JSVALUE64)
+    CLoopRegister rBasePC, tagTypeNumber, tagMask;
+#endif
+    CLoopRegister rRetVPC;
+    CLoopDoubleRegister d0, d1;
+
+    // Keep the compiler happy. We don't really need this, but the compiler
+    // will complain. This makes the warning go away.
+    t0.i = 0;
+    t1.i = 0;
+
+    // Instantiate the pseudo JIT stack frame used by the LLINT C Loop backend:
+    JITStackFrame jitStackFrame;
+
+    // The llint expects the native stack pointer, sp, to be pointing to the
+    // jitStackFrame (which is the simulation of the native stack frame):
+    JITStackFrame* const sp = &jitStackFrame;
+    sp->vm = &callFrame->vm();
+
+    // Set up an alias for the vm ptr in the JITStackFrame:
+    VM* &vm = sp->vm;
+
+    CodeBlock* codeBlock = callFrame->codeBlock();
+    Instruction* vPC;
+
+    // rPC is an alias for vPC. Set up the alias:
+    CLoopRegister& rPC = *CAST<CLoopRegister*>(&vPC);
+
+#if USE(JSVALUE32_64)
+    vPC = codeBlock->instructions().begin();
+#else // USE(JSVALUE64)
+    vPC = 0;
+    rBasePC.vp = codeBlock->instructions().begin();
+
+    // For the ASM llint, JITStubs takes care of this initialization. We do
+    // it explicitly here for the C loop:
+    tagTypeNumber.i = 0xFFFF000000000000;
+    tagMask.i = 0xFFFF000000000002;
+#endif // USE(JSVALUE64)
+
+    // cfr is an alias for callFrame. Set up this alias:
+    CLoopRegister& cfr = *CAST<CLoopRegister*>(&callFrame);
+
+    // Simulate a native return PC which should never be used:
+    rRetVPC.i = 0xbbadbeef;
+
+    // Interpreter variables for value passing between opcodes and/or helpers:
+    NativeFunction nativeFunc = 0;
+    JSValue functionReturnValue;
+    Opcode opcode;
+
+    opcode = LLInt::getOpcode(bootstrapOpcodeId);
+
+    #if ENABLE(OPCODE_STATS)
+        #define RECORD_OPCODE_STATS(__opcode) \
+            OpcodeStats::recordInstruction(__opcode)
+    #else
+        #define RECORD_OPCODE_STATS(__opcode)
+    #endif
+
+    #if USE(JSVALUE32_64)
+        #define FETCH_OPCODE() vPC->u.opcode
+    #else // USE(JSVALUE64)
+        #define FETCH_OPCODE() *bitwise_cast<Opcode*>(rBasePC.i8p + rPC.i * 8)
+    #endif // USE(JSVALUE64)
+
+    #define NEXT_INSTRUCTION() \
+        do {                         \
+            opcode = FETCH_OPCODE(); \
+            DISPATCH_OPCODE();       \
+        } while (false)
+
+#if ENABLE(COMPUTED_GOTO_OPCODES)
+
+    //========================================================================
+    // Loop dispatch mechanism using computed goto statements:
+
+    #define DISPATCH_OPCODE() goto *opcode
+
+    #define DEFINE_OPCODE(__opcode) \
+        __opcode: \
+            RECORD_OPCODE_STATS(__opcode);
+
+    // Dispatch to the current PC's bytecode:
+    DISPATCH_OPCODE();
+
+#else // !ENABLE(COMPUTED_GOTO_OPCODES)
+    //========================================================================
+    // Loop dispatch mechanism using a C switch statement:
+
+    #define DISPATCH_OPCODE() goto dispatchOpcode
+
+    #define DEFINE_OPCODE(__opcode) \
+        case __opcode: \
+        __opcode: \
+            RECORD_OPCODE_STATS(__opcode);
+
+    // Dispatch to the current PC's bytecode:
+    dispatchOpcode:
+    switch (opcode)
+
+#endif // !ENABLE(COMPUTED_GOTO_OPCODES)
+
+    //========================================================================
+    // Bytecode handlers:
+    {
+        // This is the file generated by offlineasm, which contains all of the
+        // bytecode handlers for the interpreter, as compiled from
+        // LowLevelInterpreter.asm and its peers.
+
+        #include "LLIntAssembly.h"
+
+        // In the ASM llint, getHostCallReturnValue() is a piece of glue
+        // function provided by the JIT (see dfg/DFGOperations.cpp).
+        // We simulate it here with a pseduo-opcode handler.
+        OFFLINE_ASM_GLUE_LABEL(getHostCallReturnValue)
+        {
+            // The ASM part pops the frame:
+            callFrame = callFrame->callerFrame();
+
+            // The part in getHostCallReturnValueWithExecState():
+            JSValue result = vm->hostCallReturnValue;
+#if USE(JSVALUE32_64)
+            t1.i = result.tag();
+            t0.i = result.payload();
+#else
+            t0.encodedJSValue = JSValue::encode(result);
+#endif
+            goto doReturnHelper;
+        }
+
+        OFFLINE_ASM_GLUE_LABEL(ctiOpThrowNotCaught)
+        {
+            return vm->exception;
+        }
+
+#if !ENABLE(COMPUTED_GOTO_OPCODES)
+    default:
+        ASSERT(false);
+#endif
+
+    } // END bytecode handler cases.
+
+    //========================================================================
+    // Bytecode helpers:
+
+    doReturnHelper: {
+        ASSERT(!!callFrame);
+        if (callFrame->hasHostCallFrameFlag()) {
+#if USE(JSVALUE32_64)
+            return JSValue(t1.i, t0.i); // returning JSValue(tag, payload);
+#else
+            return JSValue::decode(t0.encodedJSValue);
+#endif
+        }
+
+        // The normal ASM llint call implementation returns to the caller as
+        // recorded in rRetVPC, and the caller would fetch the return address
+        // from ArgumentCount.tag() (see the dispatchAfterCall() macro used in
+        // the callTargetFunction() macro in the llint asm files).
+        //
+        // For the C loop, we don't have the JIT stub to this work for us.
+        // So, we need to implement the equivalent of dispatchAfterCall() here
+        // before dispatching to the PC.
+
+        vPC = callFrame->currentVPC();
+
+#if USE(JSVALUE64)
+        // Based on LowLevelInterpreter64.asm's dispatchAfterCall():
+
+        // When returning from a native trampoline call, unlike the assembly
+        // LLInt, we can't simply return to the caller. In our case, we grab
+        // the caller's VPC and resume execution there. However, the caller's
+        // VPC returned by callFrame->currentVPC() is in the form of the real
+        // address of the target bytecode, but the 64-bit llint expects the
+        // VPC to be a bytecode offset. Hence, we need to map it back to a
+        // bytecode offset before we dispatch via the usual dispatch mechanism
+        // i.e. NEXT_INSTRUCTION():
+
+        codeBlock = callFrame->codeBlock();
+        ASSERT(codeBlock);
+        rPC.vp = callFrame->currentVPC();
+        rPC.i = rPC.i8p - reinterpret_cast<int8_t*>(codeBlock->instructions().begin());
+        rPC.i >>= 3;
+
+        rBasePC.vp = codeBlock->instructions().begin();
+#endif // USE(JSVALUE64)
+
+        NEXT_INSTRUCTION();
+
+    } // END doReturnHelper.
+
+
+    // Keep the compiler happy so that it doesn't complain about unused
+    // labels for the LLInt trampoline glue. The labels are automatically
+    // emitted by label macros above, and some of them are referenced by
+    // the llint generated code. Since we can't tell ahead of time which
+    // will be referenced and which will be not, we'll just passify the
+    // compiler on all such labels:
+    #define LLINT_OPCODE_ENTRY(__opcode, length) \
+        UNUSED_LABEL(__opcode);
+        FOR_EACH_OPCODE_ID(LLINT_OPCODE_ENTRY);
+    #undef LLINT_OPCODE_ENTRY
+
+
+    #undef NEXT_INSTRUCTION
+    #undef DEFINE_OPCODE
+    #undef CHECK_FOR_TIMEOUT
+    #undef CAST
+    #undef SIGN_BIT32
+
+} // Interpreter::llintCLoopExecute()
+
+} // namespace JSC
+
+#else // !ENABLE(LLINT_C_LOOP)
+
+//============================================================================
+// Define the opcode dispatch mechanism when using an ASM loop:
+//
+
+// These are for building an interpreter from generated assembly code:
+#define OFFLINE_ASM_BEGIN   asm (
+#define OFFLINE_ASM_END     );
+
+#define OFFLINE_ASM_OPCODE_LABEL(__opcode) OFFLINE_ASM_GLOBAL_LABEL(llint_##__opcode)
+#define OFFLINE_ASM_GLUE_LABEL(__opcode)   OFFLINE_ASM_GLOBAL_LABEL(__opcode)
+
+#if CPU(ARM_THUMB2)
+#define OFFLINE_ASM_GLOBAL_LABEL(label)          \
+    ".globl " SYMBOL_STRING(label) "\n"          \
+    HIDE_SYMBOL(label) "\n"                      \
+    ".thumb\n"                                   \
+    ".thumb_func " THUMB_FUNC_PARAM(label) "\n"  \
+    SYMBOL_STRING(label) ":\n"
+#else
+#define OFFLINE_ASM_GLOBAL_LABEL(label)         \
+    ".globl " SYMBOL_STRING(label) "\n"         \
+    HIDE_SYMBOL(label) "\n"                     \
+    SYMBOL_STRING(label) ":\n"
+#endif
+
+#define OFFLINE_ASM_LOCAL_LABEL(label)   LOCAL_LABEL_STRING(label) ":\n"
+
 // This is a file generated by offlineasm, which contains all of the assembly code
 // for the interpreter, as compiled from LowLevelInterpreter.asm.
 #include "LLIntAssembly.h"
 
+#endif // !ENABLE(LLINT_C_LOOP)
+
 #endif // ENABLE(LLINT)