#include "jit.h" #include "i_system.h" // To do: get cmake to define these.. #define ASMJIT_BUILD_EMBED #define ASMJIT_STATIC #include #include #include class AsmJitException : public std::exception { public: AsmJitException(asmjit::Error error, const char *message) noexcept : error(error), message(message) { } const char* what() const noexcept override { return message.GetChars(); } asmjit::Error error; FString message; }; class ThrowingErrorHandler : public asmjit::ErrorHandler { public: bool handleError(asmjit::Error err, const char *message, asmjit::CodeEmitter *origin) override { throw AsmJitException(err, message); } }; static asmjit::JitRuntime *jit; static int jitRefCount = 0; asmjit::JitRuntime *JitGetRuntime() { if (!jit) jit = new asmjit::JitRuntime; jitRefCount++; return jit; } void JitCleanUp(VMScriptFunction *func) { jitRefCount--; if (jitRefCount == 0) { delete jit; jit = nullptr; } } #define A (pc[0].a) #define B (pc[0].b) #define C (pc[0].c) #define Cs (pc[0].cs) #define BC (pc[0].i16u) #define BCs (pc[0].i16) #define ABCs (pc[0].i24) #define JMPOFS(x) ((x)->i24) #define KC (konstd[C]) #define RC (regD[C]) #define PA (regA[A]) #define PB (regA[B]) #define ASSERTD(x) assert((unsigned)(x) < sfunc->NumRegD) #define ASSERTF(x) assert((unsigned)(x) < sfunc->NumRegF) #define ASSERTA(x) assert((unsigned)(x) < sfunc->NumRegA) #define ASSERTS(x) assert((unsigned)(x) < sfunc->NumRegS) #define ASSERTKD(x) assert(sfunc != NULL && (unsigned)(x) < sfunc->NumKonstD) #define ASSERTKF(x) assert(sfunc != NULL && (unsigned)(x) < sfunc->NumKonstF) #define ASSERTKA(x) assert(sfunc != NULL && (unsigned)(x) < sfunc->NumKonstA) #define ASSERTKS(x) assert(sfunc != NULL && (unsigned)(x) < sfunc->NumKonstS) // [pbeta] TODO: VM aborts #define NULL_POINTER_CHECK(a,o,x) #define BINARY_OP_INT(op,out,r,l) \ { \ auto tmp = cc.newInt32(); \ cc.mov(tmp, r); \ cc.op(tmp, l); \ cc.mov(out, tmp); \ } static bool CanJit(VMScriptFunction *sfunc) { int size = sfunc->CodeSize; for (int i = 0; i < size; i++) { const VMOP *pc = sfunc->Code + i; VM_UBYTE op = pc->op; int a = pc->a; switch (op) { default: return false; case OP_NOP: case OP_LI: case OP_LK: case OP_LKF: //case OP_LKS: case OP_LKP: case OP_LK_R: case OP_LKF_R: //case OP_LKS_R: //case OP_LKP_R: case OP_LB: case OP_LB_R: case OP_LH: case OP_LH_R: case OP_LW: case OP_LW_R: case OP_LBU: case OP_LBU_R: case OP_LHU: case OP_LHU_R: case OP_LSP: case OP_LSP_R: case OP_LDP: case OP_LDP_R: case OP_LV2: case OP_LV2_R: case OP_LV3: case OP_LV3_R: case OP_SB: case OP_SB_R: case OP_SH: case OP_SH_R: case OP_SW: case OP_SW_R: case OP_SSP: case OP_SSP_R: case OP_SDP: case OP_SDP_R: case OP_SV2: case OP_SV2_R: case OP_SV3: case OP_SV3_R: case OP_MOVE: case OP_MOVEF: //case OP_MOVES: //case OP_MOVEA: case OP_MOVEV2: case OP_MOVEV3: break; case OP_RET: if (B != REGT_NIL) { int regtype = B; int regnum = C; switch (regtype & REGT_TYPE) { case REGT_STRING: case REGT_POINTER: return false; } } break; case OP_RETI: case OP_SLL_RR: case OP_SLL_RI: case OP_SLL_KR: case OP_SRL_RR: case OP_SRL_RI: case OP_SRL_KR: case OP_SRA_RR: case OP_SRA_RI: case OP_SRA_KR: case OP_ADD_RR: case OP_ADD_RK: case OP_ADDI: case OP_SUB_RR: case OP_SUB_RK: case OP_SUB_KR: case OP_MUL_RR: case OP_MUL_RK: case OP_DIV_RR: case OP_DIV_RK: case OP_DIV_KR: case OP_DIVU_RR: case OP_DIVU_RK: case OP_DIVU_KR: //case OP_MOD_RR: //case OP_MOD_RK: //case OP_MOD_KR: //case OP_MODU_RR: //case OP_MODU_RK: //case OP_MODU_KR: case OP_AND_RR: case OP_AND_RK: case OP_OR_RR: case OP_OR_RK: case OP_XOR_RR: case OP_XOR_RK: //case OP_MIN_RR: //case OP_MIN_RK: //case OP_MAX_RR: //case OP_MAX_RK: //case OP_ABS: case OP_NEG: case OP_NOT: case OP_EQ_R: case OP_EQ_K: case OP_LT_RR: case OP_LT_RK: case OP_LT_KR: case OP_LE_RR: case OP_LE_RK: case OP_LE_KR: case OP_LTU_RR: case OP_LTU_RK: case OP_LTU_KR: case OP_LEU_RR: case OP_LEU_RK: case OP_LEU_KR: case OP_ADDF_RR: case OP_ADDF_RK: case OP_SUBF_RR: case OP_SUBF_RK: case OP_SUBF_KR: case OP_MULF_RR: case OP_MULF_RK: case OP_DIVF_RR: case OP_DIVF_RK: case OP_DIVF_KR: case OP_EQF_R: case OP_EQF_K: case OP_LTF_RR: case OP_LTF_RK: case OP_LTF_KR: case OP_LEF_RR: case OP_LEF_RK: case OP_LEF_KR: case OP_NEGV2: case OP_ADDV2_RR: case OP_SUBV2_RR: case OP_DOTV2_RR: case OP_MULVF2_RR: case OP_MULVF2_RK: case OP_DIVVF2_RR: case OP_DIVVF2_RK: case OP_LENV2: // case OP_EQV2_R: // case OP_EQV2_K: case OP_NEGV3: case OP_ADDV3_RR: case OP_SUBV3_RR: case OP_DOTV3_RR: case OP_CROSSV_RR: case OP_MULVF3_RR: case OP_MULVF3_RK: case OP_DIVVF3_RR: case OP_DIVVF3_RK: case OP_LENV3: //case OP_EQV3_R: //case OP_EQV3_K: case OP_ADDA_RR: case OP_ADDA_RK: case OP_SUBA: break; } } return true; } template void emitComparisonOpcode(asmjit::X86Compiler& cc, const TArray& labels, const VMOP* pc, int i, Func compFunc) { using namespace asmjit; auto tmp = cc.newInt32(); compFunc(tmp); bool check = static_cast(A & CMP_CHECK); cc.test(tmp, tmp); if (check) cc.je (labels[i + 2]); else cc.jne(labels[i + 2]); cc.jmp(labels[i + 2 + JMPOFS(pc + 1)]); } static int64_t ToMemAddress(const void *d) { return (int64_t)(ptrdiff_t)d; } static void CallSqrt(asmjit::X86Compiler& cc, const asmjit::X86Xmm &a, const asmjit::X86Xmm &b) { using namespace asmjit; typedef double(*FuncPtr)(double); auto call = cc.call(ToMemAddress(reinterpret_cast(static_cast(g_sqrt))), FuncSignature1()); call->setRet(0, a); call->setArg(0, b); } static void EmitThrowException(asmjit::X86Compiler& cc, asmjit::X86Gp exceptInfo, const VMOP* pc, EVMAbortException reason) { using namespace asmjit; // Update JitExceptionInfo struct cc.mov(x86::dword_ptr(exceptInfo, 0 * 4), (int32_t)reason); #ifdef ASMJIT_ARCH_X64 cc.mov(x86::qword_ptr(exceptInfo, 4 * 4), ToMemAddress(pc)); #else cc.mov(x86::dword_ptr(exceptInfo, 4 * 4), ToMemAddress(pc)); #endif // Return from function X86Gp vReg = cc.newInt32(); cc.mov(vReg, 0); cc.ret(vReg); } static void EmitThrowException(asmjit::X86Compiler& cc, asmjit::X86Gp exceptInfo, const VMOP* pc, EVMAbortException reason, asmjit::X86Gp arg1) { using namespace asmjit; // Update JitExceptionInfo struct cc.mov(x86::dword_ptr(exceptInfo, 0 * 4), (int32_t)reason); cc.mov(x86::dword_ptr(exceptInfo, 1 * 4), arg1); #ifdef ASMJIT_ARCH_X64 cc.mov(x86::qword_ptr(exceptInfo, 4 * 4), ToMemAddress(pc)); #else cc.mov(x86::dword_ptr(exceptInfo, 4 * 4), ToMemAddress(pc)); #endif // Return from function X86Gp vReg = cc.newInt32(); cc.mov(vReg, 0); cc.ret(vReg); } JitFuncPtr JitCompile(VMScriptFunction *sfunc) { #if 0 // For debugging if (strcmp(sfunc->Name.GetChars(), "EmptyFunction") != 0) return nullptr; #else if (!CanJit(sfunc)) return nullptr; #endif using namespace asmjit; try { auto *jit = JitGetRuntime(); ThrowingErrorHandler errorHandler; //FileLogger logger(stdout); CodeHolder code; code.init(jit->getCodeInfo()); code.setErrorHandler(&errorHandler); //code.setLogger(&logger); X86Compiler cc(&code); X86Gp stack = cc.newIntPtr("stack"); // VMFrameStack *stack X86Gp vmregs = cc.newIntPtr("vmregs"); // void *vmregs X86Gp ret = cc.newIntPtr("ret"); // VMReturn *ret X86Gp numret = cc.newInt32("numret"); // int numret X86Gp exceptInfo = cc.newIntPtr("exceptinfo"); // JitExceptionInfo *exceptInfo cc.addFunc(FuncSignature5()); cc.setArg(0, stack); cc.setArg(1, vmregs); cc.setArg(2, ret); cc.setArg(3, numret); cc.setArg(4, exceptInfo); const int *konstd = sfunc->KonstD; const double *konstf = sfunc->KonstF; const FString *konsts = sfunc->KonstS; const FVoidObj *konsta = sfunc->KonstA; TArray regD(sfunc->NumRegD, true); TArray regF(sfunc->NumRegF, true); TArray regA(sfunc->NumRegA, true); //TArray regS(sfunc->NumRegS, true); X86Gp initreg = cc.newIntPtr(); if (sfunc->NumRegD > 0) { cc.mov(initreg, x86::ptr(vmregs, 0)); for (int i = 0; i < sfunc->NumRegD; i++) { regD[i] = cc.newInt32(); cc.mov(regD[i], x86::dword_ptr(initreg, i * 4)); } } if (sfunc->NumRegF > 0) { cc.mov(initreg, x86::ptr(vmregs, sizeof(void*))); for (int i = 0; i < sfunc->NumRegF; i++) { regF[i] = cc.newXmmSd (); cc.movsd(regF[i], x86::qword_ptr(initreg, i * 8)); } } /*if (sfunc->NumRegS > 0) { cc.mov(initreg, x86::ptr(vmregs, sizeof(void*) * 2)); for (int i = 0; i < sfunc->NumRegS; i++) { regS[i] = cc.newGpd(); } }*/ if (sfunc->NumRegA > 0) { cc.mov(initreg, x86::ptr(vmregs, sizeof(void*) * 3)); for (int i = 0; i < sfunc->NumRegA; i++) { regA[i] = cc.newIntPtr(); cc.mov(regA[i], x86::ptr(initreg, i * 4)); } } /* typedef void(*FuncPtr)(); cc.call((ptrdiff_t)(FuncPtr)[] { Printf("c++ from asmjit\n"); }, FuncSignature0()); */ int size = sfunc->CodeSize; TArray