summaryrefslogtreecommitdiff
path: root/Source/JavaScriptCore/llint/LowLevelInterpreter.cpp
diff options
context:
space:
mode:
authorSimon Hausmann <simon.hausmann@nokia.com>2012-09-10 19:10:20 +0200
committerSimon Hausmann <simon.hausmann@nokia.com>2012-09-10 19:10:20 +0200
commit284837daa07b29d6a63a748544a90b1f5842ac5c (patch)
treeecd258180bde91fe741e0cfd2638beb3c6da7e8e /Source/JavaScriptCore/llint/LowLevelInterpreter.cpp
parent2e2ba8ff45915f40ed3e014101269c175f2a89a0 (diff)
downloadqtwebkit-284837daa07b29d6a63a748544a90b1f5842ac5c.tar.gz
Imported WebKit commit 68645295d2e3e09af2c942f092556f06aa5f8b0d (http://svn.webkit.org/repository/webkit/trunk@128073)
New snapshot
Diffstat (limited to 'Source/JavaScriptCore/llint/LowLevelInterpreter.cpp')
-rw-r--r--Source/JavaScriptCore/llint/LowLevelInterpreter.cpp431
1 files changed, 431 insertions, 0 deletions
diff --git a/Source/JavaScriptCore/llint/LowLevelInterpreter.cpp b/Source/JavaScriptCore/llint/LowLevelInterpreter.cpp
index b95a50082..ebfdadfdb 100644
--- a/Source/JavaScriptCore/llint/LowLevelInterpreter.cpp
+++ b/Source/JavaScriptCore/llint/LowLevelInterpreter.cpp
@@ -31,8 +31,439 @@
#include "LLIntOfflineAsmConfig.h"
#include <wtf/InlineASM.h>
+#if ENABLE(LLINT_C_LOOP)
+#include "CodeBlock.h"
+#include "LLIntCLoop.h"
+#include "LLIntSlowPaths.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;
+}
+#endif // USE(JSVALUE32_64)
+
+} // namespace LLint
+
+
+//============================================================================
+// 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->globalData().topCallFrame == callFrame);
+
+ // Define the pseudo registers used by the LLINT C Loop backend:
+ union CLoopRegister {
+ intptr_t i;
+ uintptr_t u;
+ int32_t i32;
+ uint32_t u32;
+ int8_t i8;
+ uint8_t u8;
+ int8_t* i8p;
+ void* vp;
+ ExecState* execState;
+ void* instruction;
+ NativeFunction nativeFunc;
+#if USE(JSVALUE64)
+ int64_t i64;
+ EncodedJSValue encodedJSValue;
+ double castToDouble;
+#endif
+ Opcode opcode;
+ };
+ union CLoopDoubleRegister {
+ double d;
+#if USE(JSVALUE64)
+ void* castToVoidPtr;
+#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;
+
+#if COMPILER(MSVC)
+ // 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;
+#endif
+
+ // 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->globalData = &callFrame->globalData();
+
+ // Set up an alias for the globalData ptr in the JITStackFrame:
+ JSGlobalData* &globalData = sp->globalData;
+
+ 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: \
+ 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 = globalData->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 globalData->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_LLINT_NATIVE_HELPER(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)