123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556 |
- /*
- * Copyright (C) 2012 Apple Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- #include "config.h"
- #include "LowLevelInterpreter.h"
- #if ENABLE(LLINT)
- #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)
|