diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 000000000..def0869eb --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,19 @@ +FROM mcr.microsoft.com/devcontainers/cpp:1-ubuntu-24.04 + +ARG REINSTALL_CMAKE_VERSION_FROM_SOURCE="none" + +# Optionally install the cmake for vcpkg +COPY ./reinstall-cmake.sh /tmp/ + +RUN if [ "${REINSTALL_CMAKE_VERSION_FROM_SOURCE}" != "none" ]; then \ + chmod +x /tmp/reinstall-cmake.sh && /tmp/reinstall-cmake.sh ${REINSTALL_CMAKE_VERSION_FROM_SOURCE}; \ + fi \ + && rm -f /tmp/reinstall-cmake.sh + +# [Optional] Uncomment this section to install additional vcpkg ports. +# RUN su vscode -c "${VCPKG_ROOT}/vcpkg install " + +# [Optional] Uncomment this section to install additional packages. +RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ + && apt-get -y install --no-install-recommends clangd shellcheck tcc \ + && apt-get autoremove -y && apt-get clean -y && rm -rf /var/lib/apt/lists/* \ No newline at end of file diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 000000000..a9d9ef89b --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,60 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the +// README at: https://github.com/devcontainers/templates/tree/main/src/cpp +{ + "name": "QuickJS-ng Devcontainer", + "build": { + "dockerfile": "Dockerfile" + }, + + // Features to add to the dev container. More info: https://containers.dev/features. + "features": {}, + + // Use 'forwardPorts' to make a list of ports inside the container available locally. + // "forwardPorts": [], + "postCreateCommand": "git submodule update --init", + + // Configure tool-specific properties. + "customizations": { + // Configure properties specific to VS Code. + "vscode": { + "settings": { + "workbench.colorTheme": "Visual Studio Dark - C++", + "workbench.preferredDarkColorTheme": "Visual Studio Dark - C++", + "workbench.preferredHighContrastColorTheme": "Default High Contrast", + "workbench.preferredLightColorTheme": "Visual Studio Light - C++", + "window.autoDetectColorScheme": true, + "[cpp]": { + "editor.defaultFormatter": "llvm-vs-code-extensions.vscode-clangd", + "editor.formatOnSave": true + } + }, + "extensions": [ + "bierner.markdown-preview-github-styles", + "cheshirekow.cmake-format", + "davidanson.vscode-markdownlint", + "eamodio.gitlens", + "esbenp.prettier-vscode", + "franneck94.vscode-c-cpp-dev-extension-pack", + "fredericbonnet.cmake-test-adapter", + "hbenl.vscode-test-explorer", + "josetr.cmake-language-support-vscode", + "llvm-vs-code-extensions.vscode-clangd", + "matepek.vscode-catch2-test-adapter", + "mhutchie.git-graph", + "ms-vscode.cmake-tools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.cpptools-themes", + "ms-vscode.cpptools", + "ms-vscode.remote-explorer", + "ms-vscode.test-adapter-converter", + "redhat.vscode-yaml", + "solomonkinard.workspace-include-what-you-use", + "sr-team.vscode-clangd-cmake", + "streetsidesoftware.code-spell-checker", + "timonwong.shellcheck", + "vadimcn.vscode-lldb", + "xaver.clang-format" + ] + } + } +} diff --git a/.devcontainer/reinstall-cmake.sh b/.devcontainer/reinstall-cmake.sh new file mode 100644 index 000000000..408b81d22 --- /dev/null +++ b/.devcontainer/reinstall-cmake.sh @@ -0,0 +1,59 @@ +#!/usr/bin/env bash +#------------------------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information. +#------------------------------------------------------------------------------------------------------------- +# +set -e + +CMAKE_VERSION=${1:-"none"} + +if [ "${CMAKE_VERSION}" = "none" ]; then + echo "No CMake version specified, skipping CMake reinstallation" + exit 0 +fi + +# Cleanup temporary directory and associated files when exiting the script. +cleanup() { + EXIT_CODE=$? + set +e + if [[ -n "${TMP_DIR}" ]]; then + echo "Executing cleanup of tmp files" + rm -Rf "${TMP_DIR}" + fi + exit $EXIT_CODE +} +trap cleanup EXIT + + +echo "Installing CMake..." +apt-get -y purge --auto-remove cmake +mkdir -p /opt/cmake + +architecture=$(dpkg --print-architecture) +case "${architecture}" in + arm64) + ARCH=aarch64 ;; + amd64) + ARCH=x86_64 ;; + *) + echo "Unsupported architecture ${architecture}." + exit 1 + ;; +esac + +CMAKE_BINARY_NAME="cmake-${CMAKE_VERSION}-linux-${ARCH}.sh" +CMAKE_CHECKSUM_NAME="cmake-${CMAKE_VERSION}-SHA-256.txt" +TMP_DIR=$(mktemp -d -t cmake-XXXXXXXXXX) + +echo "${TMP_DIR}" +cd "${TMP_DIR}" + +curl -sSL "https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/${CMAKE_BINARY_NAME}" -O +curl -sSL "https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/${CMAKE_CHECKSUM_NAME}" -O + +sha256sum -c --ignore-missing "${CMAKE_CHECKSUM_NAME}" +sh "${TMP_DIR}/${CMAKE_BINARY_NAME}" --prefix=/opt/cmake --skip-license + +ln -s /opt/cmake/bin/cmake /usr/local/bin/cmake +ln -s /opt/cmake/bin/ctest /usr/local/bin/ctest diff --git a/.gitignore b/.gitignore index ff10fcd14..1b39788f3 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ CMakeUserPresets.json fuzz .vscode/ microbench*.txt +.cache/ \ No newline at end of file diff --git a/.gitmodules b/.gitmodules index 91828e05a..63bd7dda8 100644 --- a/.gitmodules +++ b/.gitmodules @@ -3,3 +3,9 @@ url = https://github.com/tc39/test262 shallow = true update = none +[submodule "sljit"] + path = sljit + url = https://github.com/zherczeg/sljit +[submodule "zydis"] + path = zydis + url = https://github.com/zyantific/zydis diff --git a/CMakeLists.txt b/CMakeLists.txt index 91c6c683e..63d84c476 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -149,6 +149,7 @@ xoption(QJS_ENABLE_ASAN "Enable AddressSanitizer (ASan)" OFF) xoption(QJS_ENABLE_MSAN "Enable MemorySanitizer (MSan)" OFF) xoption(QJS_ENABLE_TSAN "Enable ThreadSanitizer (TSan)" OFF) xoption(QJS_ENABLE_UBSAN "Enable UndefinedBehaviorSanitizer (UBSan)" OFF) +xoption(QJS_ENABLE_SLJIT "Enable sljit" ON) if(QJS_ENABLE_ASAN) message(STATUS "Building with ASan") @@ -279,6 +280,39 @@ if(EMSCRIPTEN) target_link_libraries(qjs_wasm m) endif() +if (QJS_ENABLE_SLJIT) + +if(CMAKE_BUILD_TYPE MATCHES "Debug") +xoption(QJS_SLJIT_DEBUG "sljit debug mode" ON) +xoption(QJS_SLJIT_VERBOSE "sljit verbose mode" ON) +else() +xoption(QJS_SLJIT_DEBUG "sljit debug mode" OFF) +xoption(QJS_SLJIT_VERBOSE "sljit verbose mode" OFF) +endif() + +file(GLOB SLJIT_SRC + ${CMAKE_CURRENT_SOURCE_DIR}/sljit/sljit_src/sljitLir.c +) + +add_library(sljit ${SLJIT_SRC}) +target_include_directories(sljit PUBLIC + $ + $ +) +target_link_libraries(qjs PRIVATE sljit) + +if (QJS_SLJIT_DEBUG) + target_compile_definitions(sljit PUBLIC SLJIT_DEBUG=1) +endif() + +if (QJS_SLJIT_VERBOSE) + target_compile_definitions(sljit PUBLIC SLJIT_VERBOSE=1) + add_subdirectory(zydis) + target_link_libraries(qjs PRIVATE Zydis::Zydis) + target_compile_options(Zycore PRIVATE -Wno-format-nonliteral) +endif() + +endif() # QuickJS bytecode compiler # @@ -441,4 +475,11 @@ if(NOT IOS) install(EXPORT qjsConfig DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/quickjs) install(FILES LICENSE DESTINATION ${CMAKE_INSTALL_DOCDIR}) install(DIRECTORY examples DESTINATION ${CMAKE_INSTALL_DOCDIR}) + + if(QJS_ENABLE_SLJIT) + install(TARGETS sljit EXPORT qjsConfig + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) + endif() endif() diff --git a/quickjs.c b/quickjs.c index fb42ff8da..50e95dcac 100644 --- a/quickjs.c +++ b/quickjs.c @@ -73,6 +73,15 @@ #define CONFIG_ATOMICS #endif + +#if defined(QJS_ENABLE_SLJIT) +#include + +#ifdef QJS_SLJIT_VERBOSE +#include +#endif +#endif + #ifndef __GNUC__ #define __extension__ #endif @@ -650,6 +659,18 @@ typedef enum JSFunctionKindEnum { JS_FUNC_ASYNC_GENERATOR = (JS_FUNC_GENERATOR | JS_FUNC_ASYNC), } JSFunctionKindEnum; + +struct JitAux { + JSValue *stack_buf; + JSValue *var_buf; + JSValue *arg_buf; + JSValue *sp; + JSVarRef **var_refs; + JSStackFrame *sf; + JSObject *p; + JSContext *caller_ctx; +}; + typedef struct JSFunctionBytecode { JSGCObjectHeader header; /* must come first */ uint8_t is_strict_mode : 1; @@ -685,8 +706,16 @@ typedef struct JSFunctionBytecode { int pc2line_len; uint8_t *pc2line_buf; char *source; +#ifdef QJS_ENABLE_SLJIT + JSValue (*jitcode)(JSContext *ctx, JSValueConst func_obj, + JSValueConst this_obj, JSValueConst new_target, + int argc, JSValue *argv, int flags, + struct JitAux *aux); +#endif } JSFunctionBytecode; + + typedef struct JSBoundFunction { JSValue func_obj; JSValue this_val; @@ -5748,8 +5777,17 @@ static void js_free_value_rt(JSRuntime *rt, JSValue v) } } break; + case JS_TAG_FUNCTION_BYTECODE: + { +#ifdef QJS_ENABLE_SLJIT + JSFunctionBytecode *b = JS_GetFunctionBytecode(v); + if (b && b->jitcode) { + sljit_free_code(b->jitcode, NULL); + b->jitcode = NULL; + } +#endif + } case JS_TAG_OBJECT: - case JS_TAG_FUNCTION_BYTECODE: { JSGCObjectHeader *p = JS_VALUE_GET_PTR(v); if (rt->gc_phase != JS_GC_PHASE_REMOVE_CYCLES) { @@ -16473,6 +16511,8 @@ static bool needs_backtrace(JSValue exc) return !find_own_property1(p, JS_ATOM_stack); } +static void js_jit(JSContext *ctx, JSFunctionBytecode *b); + /* argv[] is modified if (flags & JS_CALL_FLAG_COPY_ARGV) = 0. */ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, JSValueConst this_obj, JSValueConst new_target, @@ -16604,6 +16644,31 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, print_func_name(b); #endif +#ifdef QJS_ENABLE_SLJIT +jit: + if (!b->jitcode) { + js_jit(ctx, b); + goto jit; + } else { + // struct JitAux aux = { + // .caller_ctx = caller_ctx, + // .var_refs = var_refs, + // .var_buf = var_buf, + // .arg_buf = arg_buf, + // .sf = sf, + // .sp = sp, + // .p = p, + // }; + // ret_val = b->jitcode(ctx, func_obj, this_obj, new_target, + // argc, argv, flags, &aux); + // sp = aux.sp; + // if (JS_IsException(ret_val)) + // goto exception; + // // TODO handle b->func_kind != JS_FUNC_NORMAL + // goto done; + } +#endif + restart: for(;;) { int call_argc; @@ -33886,6 +33951,485 @@ static int add_module_variables(JSContext *ctx, JSFunctionDef *fd) return 0; } +#ifdef QJS_ENABLE_SLJIT + +typedef struct jit_aux { + JSValueConst func_obj; + JSValueConst this_obj; + JSValueConst new_target; + int argc; + JSValueConst *argv; + int flags; + + JSObject *p; + JSStackFrame sf_s, *sf; + int arg_allocated_size; + JSValue *local_buf, *stack_buf, *var_buf, *arg_buf, ret_val, *pval; + JSVarRef **var_refs; + size_t alloca_size; +} *jit_aux; + +typedef void (*jit_execute_func_t)( + JSContext *ctx, // S0 + JSFunctionBytecode *b, // S1 + JSValue **rsp, // S2 + jit_aux *aux // S3 +); + +static void js_jit_move_js_value(struct sljit_compiler *c, JSValue val) { + // R0 = *rsp = sp; + sljit_emit_op1(c, SLJIT_MOV, SLJIT_R0, 0, SLJIT_MEM1(SLJIT_S2), 0); + // *(*R0 + offsetof(u)) = val.u; + sljit_emit_op1(c, SLJIT_MOV, SLJIT_MEM1(SLJIT_R0), SLJIT_OFFSETOF(JSValue, u), SLJIT_IMM, (sljit_up)val.u.ptr); + // *(*R0 + offsetof(tag)) = val.tag; + sljit_emit_op1(c, SLJIT_MOV, SLJIT_MEM1(SLJIT_R0), SLJIT_OFFSETOF(JSValue, tag), SLJIT_IMM, val.tag); +} + +static void js_jit_sp_add(struct sljit_compiler *c, int count) { + if (count < 0) { + // (*rsp) = (*rsp) + count + sljit_emit_op2(c, SLJIT_ADD, SLJIT_MEM1(SLJIT_S2), 0, SLJIT_MEM1(SLJIT_S2), 0, SLJIT_IMM, count); + } else { + // (*rsp) = (*rsp) - count + sljit_emit_op2(c, SLJIT_SUB, SLJIT_MEM1(SLJIT_S2), 0, SLJIT_MEM1(SLJIT_S2), 0, SLJIT_IMM, -count); + } +} + +static void js_jit_JS_FreeValue_helper(JSContext *ctx, JSValue *val) { + JS_FreeValue(ctx, *val); +} + +static void js_jit_free_value_stack(struct sljit_compiler *c, int stack_num) { + // R0 = ctx + sljit_emit_op1(c, SLJIT_MOV, SLJIT_R0, 0, SLJIT_S0, 0); + // R1 = *rsp + stack_num + sljit_emit_op1(c, SLJIT_MOV, SLJIT_R1, 0, SLJIT_MEM1(SLJIT_S2), stack_num); + // js_jit_JS_FreeValue_helper(R0, R1) -> js_jit_JS_FreeValue_helper(ctx, *rsp + stack_num) + sljit_emit_icall(c, SLJIT_CALL, SLJIT_ARGS2V(P, P), SLJIT_IMM, SLJIT_FUNC_ADDR(js_jit_JS_FreeValue_helper)); +} + +static void js_jit_jsvalue_copy(JSValue *dst, JSValue *src) { + *dst = *src; +} + +static void js_jit_jsvalue_copy_dup_src(JSValue *dst, JSValue *src) { + *dst = js_dup(*src); +} + +#undef SWITCH +#undef CASE +#undef DEFAULT +#undef BREAK +static void js_jit(JSContext *ctx, JSFunctionBytecode *b) { + uint8_t *pc; + int opcode; + + pc = b->byte_code_buf; + + struct sljit_compiler *c = sljit_create_compiler(NULL); + sljit_emit_enter(c, 0, SLJIT_ARGS4V(P, P, P, P), 4, 3, 0); +#define SWITCH(pc) switch (opcode = *pc++) +#define CASE(op) case op +#define DEFAULT default +#define BREAK break + + for(;;) { + SWITCH(pc) { + CASE(OP_push_i32): + js_jit_move_js_value(c, js_int32(get_u32(pc))); + js_jit_sp_add(c, 1); + pc += 4; + BREAK; + CASE(OP_push_bigint_i32): + js_jit_move_js_value(c, __JS_NewShortBigInt(ctx, (int)get_u32(pc))); + js_jit_sp_add(c, 1); + pc += 4; + BREAK; + CASE(OP_push_const): + js_jit_move_js_value(c, js_dup(b->cpool[get_u32(pc)])); + js_jit_sp_add(c, 1); + + pc += 4; + BREAK; + CASE(OP_push_minus1): + CASE(OP_push_0): + CASE(OP_push_1): + CASE(OP_push_2): + CASE(OP_push_3): + CASE(OP_push_4): + CASE(OP_push_5): + CASE(OP_push_6): + CASE(OP_push_7): + js_jit_move_js_value(c, js_int32(opcode - OP_push_0)); + js_jit_sp_add(c, 1); + BREAK; + CASE(OP_push_i8): + js_jit_move_js_value(c, js_int32(get_i8(pc))); + js_jit_sp_add(c, 1); + pc += 1; + BREAK; + CASE(OP_push_i16): + js_jit_move_js_value(c, js_int32(get_i16(pc))); + js_jit_sp_add(c, 1); + pc += 2; + BREAK; + CASE(OP_push_const8): + js_jit_move_js_value(c, js_dup(b->cpool[*pc++])); + js_jit_sp_add(c, 1); + BREAK; + // CASE(OP_fclosure8): + // *sp++ = js_closure(ctx, js_dup(b->cpool[*pc++]), var_refs, sf); + // if (unlikely(JS_IsException(sp[-1]))) + // goto exception; + // BREAK; + CASE(OP_push_empty_string): + js_jit_move_js_value(c, JS_AtomToString(ctx, JS_ATOM_empty_string)); + js_jit_sp_add(c, 1); + BREAK; + // CASE(OP_get_length): + // { + // JSValue val; + + // sf->cur_pc = pc; + // val = JS_GetProperty(ctx, sp[-1], JS_ATOM_length); + // if (unlikely(JS_IsException(val))) + // goto exception; + // JS_FreeValue(ctx, sp[-1]); + // sp[-1] = val; + // } + // BREAK; + CASE(OP_push_atom_value): + js_jit_move_js_value(c, JS_AtomToString(ctx, get_u32(pc))); + js_jit_sp_add(c, 1); + pc += 4; + BREAK; + CASE(OP_undefined): + js_jit_move_js_value(c, JS_UNDEFINED); + js_jit_sp_add(c, 1); + BREAK; + CASE(OP_null): + js_jit_move_js_value(c, JS_NULL); + js_jit_sp_add(c, 1); + BREAK; + // CASE(OP_push_this): + // /* OP_push_this is only called at the start of a function */ + // { + // JSValue val; + // if (!b->is_strict_mode) { + // uint32_t tag = JS_VALUE_GET_TAG(this_obj); + // if (likely(tag == JS_TAG_OBJECT)) + // goto normal_this; + // if (tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED) { + // val = js_dup(ctx->global_obj); + // } else { + // val = JS_ToObject(ctx, this_obj); + // if (JS_IsException(val)) + // goto exception; + // } + // } else { + // normal_this: + // val = js_dup(this_obj); + // } + // *sp++ = val; + // } + // BREAK; + CASE(OP_push_false): + js_jit_move_js_value(c, JS_FALSE); + js_jit_sp_add(c, 1); + BREAK; + CASE(OP_push_true): + js_jit_move_js_value(c, JS_TRUE); + js_jit_sp_add(c, 1); + BREAK; + CASE(OP_nop): + BREAK; + // CASE(OP_object): + // *sp++ = JS_NewObject(ctx); + // if (unlikely(JS_IsException(sp[-1]))) + // goto exception; + // BREAK; + // CASE(OP_special_object): + // { + // int arg = *pc++; + // switch(arg) { + // case OP_SPECIAL_OBJECT_ARGUMENTS: + // *sp++ = js_build_arguments(ctx, argc, argv); + // if (unlikely(JS_IsException(sp[-1]))) + // goto exception; + // break; + // case OP_SPECIAL_OBJECT_MAPPED_ARGUMENTS: + // *sp++ = js_build_mapped_arguments(ctx, argc, argv, + // sf, min_int(argc, b->arg_count)); + // if (unlikely(JS_IsException(sp[-1]))) + // goto exception; + // break; + // case OP_SPECIAL_OBJECT_THIS_FUNC: + // *sp++ = js_dup(sf->cur_func); + // break; + // case OP_SPECIAL_OBJECT_NEW_TARGET: + // *sp++ = js_dup(new_target); + // break; + // case OP_SPECIAL_OBJECT_HOME_OBJECT: + // { + // JSObject *p1; + // p1 = p->u.func.home_object; + // if (unlikely(!p1)) + // *sp++ = JS_UNDEFINED; + // else + // *sp++ = js_dup(JS_MKPTR(JS_TAG_OBJECT, p1)); + // } + // break; + // case OP_SPECIAL_OBJECT_VAR_OBJECT: + // *sp++ = JS_NewObjectProto(ctx, JS_NULL); + // if (unlikely(JS_IsException(sp[-1]))) + // goto exception; + // break; + // case OP_SPECIAL_OBJECT_IMPORT_META: + // *sp++ = js_import_meta(ctx); + // if (unlikely(JS_IsException(sp[-1]))) + // goto exception; + // break; + // default: + // abort(); + // } + // } + // BREAK; + // CASE(OP_rest): + // { + // int i, n, first = get_u16(pc); + // pc += 2; + // i = min_int(first, argc); + // n = argc - i; + // *sp++ = js_create_array(ctx, n, &argv[i]); + // if (unlikely(JS_IsException(sp[-1]))) + // goto exception; + // } + // BREAK; + + CASE(OP_drop): + // JS_FreeValue(ctx, sp[-1]); + js_jit_free_value_stack(c, -1); + // sp--; + js_jit_sp_add(c, -1); + BREAK; +#define COPY_BASE(sp_reg, dst_idx, src_idx, fn) do { \ + sljit_emit_op1(c, SLJIT_MOV, sp_reg, 0, SLJIT_MEM1(SLJIT_S2), 0); \ + sljit_emit_op1(c, SLJIT_MOV, SLJIT_R0, 0, SLJIT_MEM1(sp_reg), dst_idx); \ + sljit_emit_op1(c, SLJIT_MOV, SLJIT_R1, 0, SLJIT_MEM1(sp_reg), src_idx); \ + sljit_emit_icall(c, SLJIT_CALL, SLJIT_ARGS2V(P, P), SLJIT_IMM, SLJIT_FUNC_ADDR(fn)); \ +} while (0) + + + CASE(OP_nip): + // JS_FreeValue(ctx, sp[-2]); + js_jit_free_value_stack(c, -2); + // sp[-2] = sp[-1]; -> js_jit_jsvalue_copy(&sp[-2], &sp[-1]); + COPY_BASE(SLJIT_R3, -2, -1, js_jit_jsvalue_copy); + // sp--; + js_jit_sp_add(c, -1); + BREAK; + CASE(OP_nip1): /* a b c -> b c */ + // JS_FreeValue(ctx, sp[-3]); + js_jit_free_value_stack(c, -3); + // sp[-3] = sp[-2]; + COPY_BASE(SLJIT_R3, -3, -2, js_jit_jsvalue_copy); + // sp[-2] = sp[-1]; + COPY_BASE(SLJIT_R3, -2, -1, js_jit_jsvalue_copy); + // sp--; + js_jit_sp_add(c, -1); + BREAK; + CASE(OP_dup): + // sp[0] = js_dup(sp[-1]); -> js_jit_jsvalue_copy_dup_src(&sp[0], &sp[-1]); + COPY_BASE(SLJIT_R3, 0, -1, js_jit_jsvalue_copy_dup_src); + // sp++; + js_jit_sp_add(c, 1); + BREAK; + CASE(OP_dup2): /* a b -> a b a b */ + // sp[0] = js_dup(sp[-2]); + COPY_BASE(SLJIT_R3, 0, -2, js_jit_jsvalue_copy_dup_src); + // sp[1] = js_dup(sp[-1]); + COPY_BASE(SLJIT_R3, 1, -1, js_jit_jsvalue_copy_dup_src); + // sp += 2; + js_jit_sp_add(c, 2); + BREAK; + CASE(OP_dup3): /* a b c -> a b c a b c */ + // sp[0] = js_dup(sp[-3]); -> js_jit_jsvalue_copy_dup_src(&sp[0], &sp[-3]); + COPY_BASE(SLJIT_R3, 0, -3, js_jit_jsvalue_copy_dup_src); + + // sp[1] = js_dup(sp[-2]); -> js_jit_jsvalue_copy_dup_src(&sp[1], &sp[-2]); + COPY_BASE(SLJIT_R3, 1, -2, js_jit_jsvalue_copy_dup_src); + // sp[2] = js_dup(sp[-1]); -> js_jit_jsvalue_copy_dup_src(&sp[2], &sp[-1]); + COPY_BASE(SLJIT_R3, 2, -1, js_jit_jsvalue_copy_dup_src); + // sp += 3; + js_jit_sp_add(c, 3); + BREAK; + CASE(OP_dup1): /* a b -> a a b */ + // sp[0] = sp[-1]; + COPY_BASE(SLJIT_R3, 0, -1, js_jit_jsvalue_copy); + // sp[-1] = js_dup(sp[-2]); + COPY_BASE(SLJIT_R3, -1, -2, js_jit_jsvalue_copy_dup_src); + // sp++; + js_jit_sp_add(c, 1); + BREAK; + CASE(OP_insert2): /* obj a -> a obj a (dup_x1) */ + // sp[0] = sp[-1]; + COPY_BASE(SLJIT_R3, 0, -1, js_jit_jsvalue_copy); + // sp[-1] = sp[-2]; + COPY_BASE(SLJIT_R3, -1, -2, js_jit_jsvalue_copy); + // sp[-2] = js_dup(sp[0]); + COPY_BASE(SLJIT_R3, -2, 0, js_jit_jsvalue_copy_dup_src); + // sp++ + js_jit_sp_add(c, 1); + BREAK; + CASE(OP_insert3): /* obj prop a -> a obj prop a (dup_x2) */ + // sp[0] = sp[-1]; + COPY_BASE(SLJIT_R3, 0, -1, js_jit_jsvalue_copy); + // sp[-1] = sp[-2]; + COPY_BASE(SLJIT_R3, -1, -2, js_jit_jsvalue_copy); + // sp[-2] = sp[-3]; + COPY_BASE(SLJIT_R3, -2, -3, js_jit_jsvalue_copy); + // sp[-3] = js_dup(sp[0]); + COPY_BASE(SLJIT_R3, -3, 0, js_jit_jsvalue_copy_dup_src); + // sp++; + js_jit_sp_add(c, 1); + BREAK; + CASE(OP_insert4): /* this obj prop a -> a this obj prop a */ + // sp[0] = sp[-1]; + COPY_BASE(SLJIT_R3, 0, -1, js_jit_jsvalue_copy); + // sp[-1] = sp[-2]; + COPY_BASE(SLJIT_R3, -1, -2, js_jit_jsvalue_copy); + // sp[-2] = sp[-3]; + COPY_BASE(SLJIT_R3, -2, -3, js_jit_jsvalue_copy); + // sp[-3] = sp[-4]; + COPY_BASE(SLJIT_R3, -3, -4, js_jit_jsvalue_copy); + // sp[-4] = js_dup(sp[0]); + COPY_BASE(SLJIT_R3, -4, 0, js_jit_jsvalue_copy_dup_src); + // sp++; + js_jit_sp_add(c, 1); + BREAK; + // CASE(OP_perm3): /* obj a b -> a obj b (213) */ + // { + // JSValue tmp; + // tmp = sp[-2]; + // sp[-2] = sp[-3]; + // sp[-3] = tmp; + // } + // BREAK; + // CASE(OP_rot3l): /* x a b -> a b x (231) */ + // { + // JSValue tmp; + // tmp = sp[-3]; + // sp[-3] = sp[-2]; + // sp[-2] = sp[-1]; + // sp[-1] = tmp; + // } + // BREAK; + // CASE(OP_rot4l): /* x a b c -> a b c x */ + // { + // JSValue tmp; + // tmp = sp[-4]; + // sp[-4] = sp[-3]; + // sp[-3] = sp[-2]; + // sp[-2] = sp[-1]; + // sp[-1] = tmp; + // } + // BREAK; + // CASE(OP_rot5l): /* x a b c d -> a b c d x */ + // { + // JSValue tmp; + // tmp = sp[-5]; + // sp[-5] = sp[-4]; + // sp[-4] = sp[-3]; + // sp[-3] = sp[-2]; + // sp[-2] = sp[-1]; + // sp[-1] = tmp; + // } + // BREAK; + // CASE(OP_rot3r): /* a b x -> x a b (312) */ + // { + // JSValue tmp; + // tmp = sp[-1]; + // sp[-1] = sp[-2]; + // sp[-2] = sp[-3]; + // sp[-3] = tmp; + // } + // BREAK; + // CASE(OP_perm4): /* obj prop a b -> a obj prop b */ + // { + // JSValue tmp; + // tmp = sp[-2]; + // sp[-2] = sp[-3]; + // sp[-3] = sp[-4]; + // sp[-4] = tmp; + // } + // BREAK; + // CASE(OP_perm5): /* this obj prop a b -> a this obj prop b */ + // { + // JSValue tmp; + // tmp = sp[-2]; + // sp[-2] = sp[-3]; + // sp[-3] = sp[-4]; + // sp[-4] = sp[-5]; + // sp[-5] = tmp; + // } + // BREAK; + // CASE(OP_swap): /* a b -> b a */ + // { + // JSValue tmp; + // tmp = sp[-2]; + // sp[-2] = sp[-1]; + // sp[-1] = tmp; + // } + // BREAK; + // CASE(OP_swap2): /* a b c d -> c d a b */ + // { + // JSValue tmp1, tmp2; + // tmp1 = sp[-4]; + // tmp2 = sp[-3]; + // sp[-4] = sp[-2]; + // sp[-3] = sp[-1]; + // sp[-2] = tmp1; + // sp[-1] = tmp2; + // } + // BREAK; + CASE(OP_invalid): + DEFAULT: + goto end; + BREAK; + + } + } +end: + sljit_emit_return_void(c); + b->jitcode = sljit_generate_code(c, 0, NULL); +#if QJS_SLJIT_VERBOSE + printf("-- FUNCTION DUMP START --\n"); + size_t len = sljit_get_generated_code_size(c); + // The runtime address (instruction pointer) was chosen arbitrarily here in order to better + // visualize relative addressing. In your actual program, set this to e.g. the memory address + // that the code being disassembled was read from. + ZyanU64 runtime_address = (ZyanU64) b->jitcode; + + // Loop over the instructions in our buffer. + ZyanUSize offset = 0; + ZydisDisassembledInstruction instruction; + while (ZYAN_SUCCESS(ZydisDisassembleIntel( + ZYDIS_MACHINE_MODE_LONG_64, + runtime_address, + (const void*) (runtime_address + offset), + len - offset, + &instruction + ))) { + printf("%016" PRIX64 " %s\n", runtime_address, instruction.text); + offset += instruction.info.length; + runtime_address += instruction.info.length; + } + printf("-- FUNCTION DUMP END --\n"); +#endif + sljit_free_compiler(c); +} +#endif + /* create a function object from a function definition. The function definition is freed. All the child functions are also created. It must be done this way to resolve all the variables. */ diff --git a/sljit b/sljit new file mode 160000 index 000000000..7779da89d --- /dev/null +++ b/sljit @@ -0,0 +1 @@ +Subproject commit 7779da89dc7e00a540712fcb859b762d4eca2a26 diff --git a/zydis b/zydis new file mode 160000 index 000000000..48e750650 --- /dev/null +++ b/zydis @@ -0,0 +1 @@ +Subproject commit 48e7506503dd2b72ee6667eeeed0164e40cdd7e8