Add class to help the platform unwind JIT code.
authorErik Verbruggen <erik.verbruggen@me.com>
Thu, 21 Feb 2013 13:30:35 +0000 (14:30 +0100)
committerSimon Hausmann <simon.hausmann@digia.com>
Wed, 27 Feb 2013 12:50:00 +0000 (13:50 +0100)
Currently only x86_64 and x86 is supported.

Change-Id: I80fe60543b71e7073a0666d5ebb10144a75a488c
Reviewed-by: Lars Knoll <lars.knoll@digia.com>
src/v4/qmljs_engine.cpp
src/v4/qmljs_engine.h
src/v4/qv4isel_llvm_p.h
src/v4/qv4isel_masm.cpp
src/v4/qv4unwindhelper.cpp [new file with mode: 0644]
src/v4/qv4unwindhelper.h [new file with mode: 0644]
src/v4/qv4unwindhelper_p-dw2.h [new file with mode: 0644]
src/v4/v4.pro

index 9e10efd..f4d523d 100644 (file)
@@ -57,6 +57,7 @@
 #include <qv4jsonobject.h>
 #include <qv4stringobject.h>
 #include <qv4identifier.h>
+#include <qv4unwindhelper.h>
 
 namespace QQmlJS {
 namespace VM {
@@ -70,6 +71,7 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory)
     , exception(Value::nullValue())
 {
     MemoryManager::GCBlocker gcBlocker(memoryManager);
+    unwindHelper = UnwindHelper::create();
 
     memoryManager->setExecutionEngine(this);
 
@@ -233,6 +235,9 @@ ExecutionEngine::~ExecutionEngine()
     delete globalObject.asObject();
     rootContext->destroy();
     delete rootContext;
+    if (unwindHelper)
+        unwindHelper->deregisterFunctions(functions);
+    delete unwindHelper;
     qDeleteAll(functions);
     delete memoryManager;
 }
index 70a7fd9..6d9682a 100644 (file)
@@ -76,6 +76,7 @@ struct ArgumentsObject;
 struct ExecutionContext;
 struct ExecutionEngine;
 class MemoryManager;
+class UnwindHelper;
 
 struct ObjectPrototype;
 struct StringPrototype;
@@ -101,6 +102,7 @@ class RegExp;
 struct Q_V4_EXPORT ExecutionEngine
 {
     MemoryManager *memoryManager;
+    UnwindHelper *unwindHelper;
     EvalISelFactory *iselFactory;
     ExecutionContext *current;
     ExecutionContext *rootContext;
index 9b4aa36..08d1966 100644 (file)
@@ -94,6 +94,7 @@ public: // methods from InstructionSelection:
     virtual void callBuiltinForeachIteratorObject(IR::Temp *arg, IR::Temp *result);
     virtual void callBuiltinForeachNextPropertyname(IR::Temp *arg, IR::Temp *result);
     virtual void callBuiltinPushWithScope(IR::Temp *arg);
+    virtual void callBuiltinPushCatchScope(const QString &exceptionVarName){}
     virtual void callBuiltinPopScope();
     virtual void callBuiltinDeclareVar(bool deletable, const QString &name);
     virtual void callBuiltinDefineGetterSetter(IR::Temp *object, const QString &name, IR::Temp *getter, IR::Temp *setter);
index 0e29a0f..83c718e 100644 (file)
@@ -44,6 +44,7 @@
 #include "qv4object.h"
 #include "qv4functionobject.h"
 #include "qv4regexpobject.h"
+#include "qv4unwindhelper.h"
 
 #include <assembler/LinkBuffer.h>
 #include <WTFStubs.h>
@@ -483,6 +484,7 @@ void InstructionSelection::run(VM::Function *vmFunction, IR::Function *function)
     _as->poke(Assembler::ScratchRegister);
 #endif
     _as->ret();
+    // TODO: add a label and a nop, so we can determine the exact function length
 
     _as->link(_vmFunction);
 
@@ -491,6 +493,9 @@ void InstructionSelection::run(VM::Function *vmFunction, IR::Function *function)
         memcpy(_vmFunction->lookups, _lookups.constData(), _lookups.size()*sizeof(Lookup));
     }
 
+    if (engine()->unwindHelper)
+        engine()->unwindHelper->registerFunction(_vmFunction);
+
     qSwap(_vmFunction, vmFunction);
     qSwap(_function, function);
     qSwap(_lookups, lookups);
diff --git a/src/v4/qv4unwindhelper.cpp b/src/v4/qv4unwindhelper.cpp
new file mode 100644 (file)
index 0000000..4d78474
--- /dev/null
@@ -0,0 +1,37 @@
+#include <qv4unwindhelper.h>
+
+#include <wtf/Platform.h>
+
+#if CPU(X86_64) && OS(LINUX)
+#  define USE_DW2_HELPER
+#elif CPU(X86) && OS(LINUX)
+#  define USE_DW2_HELPER
+#elif OS(WINDOWS)
+    // SJLJ will unwind on Windows
+#  define USE_NULL_HELPER
+#elif OS(IOS)
+    // SJLJ will unwind on iOS
+#  define USE_NULL_HELPER
+#elif OS(ANDROID)
+#  warning "TODO!"
+#  define USE_NULL_HELPER
+#else
+#  warning "Unsupported/untested platform!"
+#  define USE_NULL_HELPER
+#endif
+
+#ifdef USE_DW2_HELPER
+#  include <qv4unwindhelper_p-dw2.h>
+#endif // USE_DW2_HELPER
+
+#ifdef USE_NULL_HELPER
+using namespace QQmlJS::VM;
+UnwindHelper *UnwindHelper::create() { return 0; }
+UnwindHelper::UnwindHelper() {}
+UnwindHelper::~UnwindHelper() {}
+void UnwindHelper::registerFunction(Function *function);
+void UnwindHelper::registerFunctions(QVector<Function *> functions);
+void UnwindHelper::deregisterFunction(Function *function);
+void UnwindHelper::deregisterFunctions(QVector<Function *> functions);
+#endif // USE_NULL_HELPER
+
diff --git a/src/v4/qv4unwindhelper.h b/src/v4/qv4unwindhelper.h
new file mode 100644 (file)
index 0000000..bf70a01
--- /dev/null
@@ -0,0 +1,36 @@
+#ifndef QV4UNWINDHELPER_H
+#define QV4UNWINDHELPER_H
+
+#include <QtCore/QVector>
+
+namespace QQmlJS {
+namespace VM {
+
+struct Function;
+
+class UnwindHelper
+{
+    Q_DISABLE_COPY(UnwindHelper)
+
+private:
+    UnwindHelper();
+
+public:
+    ~UnwindHelper();
+
+    static UnwindHelper *create();
+
+    void registerFunction(Function *function);
+    void registerFunctions(QVector<Function *> functions);
+    void deregisterFunction(Function *function);
+    void deregisterFunctions(QVector<Function *> functions);
+
+private:
+    struct Private;
+    Private *p;
+};
+
+} // VM namespace
+} // QQmlJS namespace
+
+#endif // QV4UNWINDHELPER_H
diff --git a/src/v4/qv4unwindhelper_p-dw2.h b/src/v4/qv4unwindhelper_p-dw2.h
new file mode 100644 (file)
index 0000000..302e173
--- /dev/null
@@ -0,0 +1,152 @@
+#ifndef QV4UNWINDHELPER_PDW2_H
+#define QV4UNWINDHELPER_PDW2_H
+
+#include <qv4unwindhelper.h>
+#include <qv4functionobject.h>
+#include <wtf/Platform.h>
+
+#include <QtCore/QHash>
+
+extern "C" void __register_frame(void*);
+extern "C" void __deregister_frame(void*);
+
+namespace QQmlJS {
+namespace VM {
+
+namespace {
+#if CPU(X86_64)
+// Generated by fdegen
+static const unsigned char cie_fde_data[] = {
+    0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+    0x1, 0x0, 0x8, 0x78, 0x10, 0xc, 0x7, 0x8,
+    0x90, 0x1, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0,
+    0x18, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+    0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+    0x0, 0x0, 0x0, 0x0, 0x41, 0x13, 0x7e, 0x86,
+    0x2, 0x43, 0xd, 0x6, 0x8c, 0x3, 0x8e, 0x4,
+    0x0, 0x0, 0x0, 0x0
+};
+static const int fde_offset = 20;
+static const int initial_location_offset = 28;
+static const int address_range_offset = 36;
+#elif CPU(X86) && OS(LINUX)
+static const unsigned char cie_fde_data[] = {
+    0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+    0x1, 0x0, 0x4, 0x7c, 0x8, 0xc, 0x4, 0x4,
+    0x88, 0x1, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0,
+    0x18, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+    0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+    0x0, 0x0, 0x0, 0x0, 0x41, 0x13, 0x7e, 0x85,
+    0x2, 0x43, 0xd, 0x5, 0x86, 0x3, 0x87, 0x4,
+    0x0, 0x0, 0x0, 0x0,
+};
+static const int fde_offset = 20;
+static const int initial_location_offset = 28;
+static const int address_range_offset = 32;
+#endif
+} // anonymous namespace
+
+namespace {
+struct EHInfo {
+    unsigned char *cie_and_fde;
+    unsigned char *fde;
+
+    EHInfo(unsigned char *cie_and_fde, unsigned char *fde)
+        : cie_and_fde(cie_and_fde), fde(fde)
+    {
+        Q_ASSERT(cie_and_fde);
+        Q_ASSERT(fde);
+
+        __register_frame(fde);
+    }
+
+    ~EHInfo()
+    {
+        Q_ASSERT(cie_and_fde);
+        Q_ASSERT(fde);
+
+        __deregister_frame(fde);
+
+        delete[] cie_and_fde;
+    }
+};
+} // anonymous namespace
+
+struct Function;
+
+struct UnwindHelper::Private
+{
+    QHash<Function *, EHInfo *> ehInfoForFunction;
+};
+
+UnwindHelper *UnwindHelper::create()
+{
+    return new UnwindHelper;
+}
+
+UnwindHelper::UnwindHelper()
+    : p(new Private)
+{}
+
+UnwindHelper::~UnwindHelper()
+{
+    foreach (Function *f, p->ehInfoForFunction.keys())
+        deregisterFunction(f);
+    delete p;
+}
+
+void UnwindHelper::registerFunctions(QVector<Function *> functions)
+{
+    foreach (Function *f, functions) registerFunction(f);
+}
+
+void UnwindHelper::deregisterFunction(Function *function)
+{
+    QHash<Function *, EHInfo *>::iterator i = p->ehInfoForFunction.find(function);
+    if (i == p->ehInfoForFunction.end())
+        return;
+
+    delete i.value();
+    p->ehInfoForFunction.erase(i);
+}
+
+void UnwindHelper::deregisterFunctions(QVector<Function *> functions)
+{
+    foreach (Function *f, functions) deregisterFunction(f);
+}
+
+namespace {
+void writeIntPtrValue(unsigned char *addr, intptr_t val)
+{
+    addr[0] = (val >>  0) & 0xff;
+    addr[1] = (val >>  8) & 0xff;
+    addr[2] = (val >> 16) & 0xff;
+    addr[3] = (val >> 24) & 0xff;
+#if QT_POINTER_SIZE == 8
+    addr[4] = (val >> 32) & 0xff;
+    addr[5] = (val >> 40) & 0xff;
+    addr[6] = (val >> 48) & 0xff;
+    addr[7] = (val >> 56) & 0xff;
+#endif
+}
+} // anonymous namespace
+
+void UnwindHelper::registerFunction(Function *function)
+{
+    unsigned char *cie_and_fde = new unsigned char[sizeof(cie_fde_data)];
+    memcpy(cie_and_fde, cie_fde_data, sizeof(cie_fde_data));
+
+    intptr_t ptr = static_cast<char *>(function->codeRef.code().executableAddress()) - static_cast<char *>(0);
+    writeIntPtrValue(cie_and_fde + initial_location_offset, ptr);
+
+    size_t len = function->codeRef.size();
+    writeIntPtrValue(cie_and_fde + address_range_offset, len);
+
+    p->ehInfoForFunction.insert(function, new EHInfo(cie_and_fde,
+                                                     cie_and_fde + fde_offset));
+}
+
+} // VM namespace
+} // QQmlJS namespace
+
+#endif // QV4UNWINDHELPER_PDW2_H
index 2197ae3..a96d063 100644 (file)
@@ -47,7 +47,8 @@ SOURCES += \
     qv4stringobject.cpp \
     qv4string.cpp \
     qv4objectiterator.cpp \
-    qv4regexp.cpp
+    qv4regexp.cpp \
+    qv4unwindhelper.cpp
 
 HEADERS += \
     qv4global.h \
@@ -85,7 +86,9 @@ HEADERS += \
     qv4string.h \
     qv4propertydescriptor.h \
     qv4objectiterator.h \
-    qv4regexp.h
+    qv4regexp.h \
+    qv4unwindhelper.h \
+    qv4unwindhelper_p-dw2.h
 
 llvm-libs {