// [AsmJit] // Complete x86/x64 JIT and Remote Assembler for C++. // // [License] // Zlib - See LICENSE.md file in the package. // [Dependencies] #include #include #include #include #include "./asmjit.h" #include "./asmjit_test_misc.h" using namespace asmjit; // ============================================================================ // [MyErrorHandler] // ============================================================================ class MyErrorHandler : public ErrorHandler { public: virtual bool handleError(Error err, const char* message, CodeEmitter* origin) { fprintf(stderr, "ERROR: %s\n", message); return false; } }; // ============================================================================ // [X86Test] // ============================================================================ //! Interface used to test CodeCompiler. class X86Test { public: X86Test(const char* name = NULL) { _name.setString(name); } virtual ~X86Test() {} ASMJIT_INLINE const char* getName() const { return _name.getData(); } virtual void compile(X86Compiler& c) = 0; virtual bool run(void* func, StringBuilder& result, StringBuilder& expect) = 0; StringBuilder _name; }; // ============================================================================ // [X86TestManager] // ============================================================================ class X86TestManager { public: // -------------------------------------------------------------------------- // [Construction / Destruction] // -------------------------------------------------------------------------- X86TestManager(); ~X86TestManager(); // -------------------------------------------------------------------------- // [Methods] // -------------------------------------------------------------------------- inline Error add(X86Test* test) { return _tests.append(&_zoneHeap, test); } int run(); // -------------------------------------------------------------------------- // [Members] // -------------------------------------------------------------------------- Zone _zone; ZoneHeap _zoneHeap; ZoneVector _tests; int _returnCode; int _binSize; bool _verbose; StringBuilder _output; }; X86TestManager::X86TestManager() : _zone(8096 - Zone::kZoneOverhead), _zoneHeap(&_zone), _returnCode(0), _binSize(0), _verbose(false) {} X86TestManager::~X86TestManager() { size_t i; size_t count = _tests.getLength(); for (i = 0; i < count; i++) { X86Test* test = _tests[i]; delete test; } } int X86TestManager::run() { size_t i; size_t count = _tests.getLength(); FILE* file = stdout; #if !defined(ASMJIT_DISABLE_LOGGING) FileLogger fileLogger(file); fileLogger.addOptions(Logger::kOptionBinaryForm); StringLogger stringLogger; stringLogger.addOptions(Logger::kOptionBinaryForm); #endif // ASMJIT_DISABLE_LOGGING MyErrorHandler errorHandler; for (i = 0; i < count; i++) { JitRuntime runtime; CodeHolder code; code.init(runtime.getCodeInfo()); code.setErrorHandler(&errorHandler); #if !defined(ASMJIT_DISABLE_LOGGING) if (_verbose) { fprintf(file, "\n"); code.setLogger(&fileLogger); } else { stringLogger.clearString(); code.setLogger(&stringLogger); } #endif // ASMJIT_DISABLE_LOGGING X86Compiler cc(&code); X86Test* test = _tests[i]; test->compile(cc); Error err = cc.finalize(); void* func; if (err == kErrorOk) err = runtime.add(&func, &code); if (_verbose) fflush(file); if (err == kErrorOk) { StringBuilder result; StringBuilder expect; if (test->run(func, result, expect)) { fprintf(file, "[Success] %s.\n", test->getName()); } else { #if !defined(ASMJIT_DISABLE_LOGGING) if (!_verbose) fprintf(file, "\n%s", stringLogger.getString()); #endif // ASMJIT_DISABLE_LOGGING fprintf(file, "-------------------------------------------------------------------------------\n"); fprintf(file, "[Failure] %s.\n", test->getName()); fprintf(file, "-------------------------------------------------------------------------------\n"); fprintf(file, "Result : %s\n", result.getData()); fprintf(file, "Expected: %s\n", expect.getData()); fprintf(file, "===============================================================================\n"); _returnCode = 1; } runtime.release(func); } else { #if !defined(ASMJIT_DISABLE_LOGGING) if (!_verbose) fprintf(file, "%s\n", stringLogger.getString()); #endif // ASMJIT_DISABLE_LOGGING fprintf(file, "-------------------------------------------------------------------------------\n"); fprintf(file, "[Failure] %s (%s).\n", test->getName(), DebugUtils::errorAsString(err)); fprintf(file, "===============================================================================\n"); _returnCode = 1; } fflush(file); } fputs("\n", file); fputs(_output.getData(), file); fflush(file); return _returnCode; } // ============================================================================ // [X86Test_AlignBase] // ============================================================================ class X86Test_AlignBase : public X86Test { public: X86Test_AlignBase(uint32_t numArgs, uint32_t numVars, uint32_t alignment, bool naked) : _numArgs(numArgs), _numVars(numVars), _alignment(alignment), _naked(naked) { _name.setFormat("[Align] NumArgs=%u NumVars=%u Alignment=%u Naked=%c", numArgs, numVars, alignment, naked ? 'Y' : 'N'); } static void add(X86TestManager& mgr) { for (uint32_t i = 0; i <= 8; i++) { for (uint32_t j = 0; j <= 4; j++) { for (uint32_t a = 16; a <= 32; a += 16) { mgr.add(new X86Test_AlignBase(i, j, a, false)); mgr.add(new X86Test_AlignBase(i, j, a, true)); } } } } virtual void compile(X86Compiler& cc) { switch (_numArgs) { case 0: cc.addFunc(FuncSignature0(CallConv::kIdHost)); break; case 1: cc.addFunc(FuncSignature1(CallConv::kIdHost)); break; case 2: cc.addFunc(FuncSignature2(CallConv::kIdHost)); break; case 3: cc.addFunc(FuncSignature3(CallConv::kIdHost)); break; case 4: cc.addFunc(FuncSignature4(CallConv::kIdHost)); break; case 5: cc.addFunc(FuncSignature5(CallConv::kIdHost)); break; case 6: cc.addFunc(FuncSignature6(CallConv::kIdHost)); break; case 7: cc.addFunc(FuncSignature7(CallConv::kIdHost)); break; case 8: cc.addFunc(FuncSignature8(CallConv::kIdHost)); break; } if (!_naked) cc.getFunc()->getFrameInfo().enablePreservedFP(); X86Gp gpVar = cc.newIntPtr("gpVar"); X86Gp gpSum = cc.newInt32("gpSum"); X86Mem stack = cc.newStack(_alignment, _alignment); // Alloc, use and spill preserved registers. if (_numVars) { uint32_t gpCount = cc.getGpCount(); uint32_t varIndex = 0; uint32_t physId = 0; uint32_t regMask = 0x1; uint32_t preservedMask = cc.getFunc()->getDetail().getPreservedRegs(Reg::kKindGp); do { if ((preservedMask & regMask) != 0 && (physId != X86Gp::kIdSp && physId != X86Gp::kIdBp)) { X86Gp tmp = cc.newInt32("gpTmp%u", physId); cc.alloc(tmp, physId); cc.xor_(tmp, tmp); cc.spill(tmp); varIndex++; } physId++; regMask <<= 1; } while (varIndex < _numVars && physId < gpCount); } // Do a sum of arguments to verify a possible relocation when misaligned. if (_numArgs) { cc.xor_(gpSum, gpSum); for (uint32_t argIndex = 0; argIndex < _numArgs; argIndex++) { X86Gp gpArg = cc.newInt32("gpArg%u", argIndex); cc.setArg(argIndex, gpArg); cc.add(gpSum, gpArg); } } // Check alignment of xmmVar (has to be 16). cc.lea(gpVar, stack); cc.and_(gpVar, _alignment - 1); // Add a sum of arguments to check whether they are correct. if (_numArgs) cc.or_(gpVar.r32(), gpSum); cc.ret(gpVar); cc.endFunc(); } virtual bool run(void* _func, StringBuilder& result, StringBuilder& expect) { typedef int (*Func0)(); typedef int (*Func1)(int); typedef int (*Func2)(int, int); typedef int (*Func3)(int, int, int); typedef int (*Func4)(int, int, int, int); typedef int (*Func5)(int, int, int, int, int); typedef int (*Func6)(int, int, int, int, int, int); typedef int (*Func7)(int, int, int, int, int, int, int); typedef int (*Func8)(int, int, int, int, int, int, int, int); unsigned int resultRet = 0; unsigned int expectRet = 0; switch (_numArgs) { case 0: resultRet = ptr_as_func(_func)(); expectRet = 0; break; case 1: resultRet = ptr_as_func(_func)(1); expectRet = 1; break; case 2: resultRet = ptr_as_func(_func)(1, 2); expectRet = 1 + 2; break; case 3: resultRet = ptr_as_func(_func)(1, 2, 3); expectRet = 1 + 2 + 3; break; case 4: resultRet = ptr_as_func(_func)(1, 2, 3, 4); expectRet = 1 + 2 + 3 + 4; break; case 5: resultRet = ptr_as_func(_func)(1, 2, 3, 4, 5); expectRet = 1 + 2 + 3 + 4 + 5; break; case 6: resultRet = ptr_as_func(_func)(1, 2, 3, 4, 5, 6); expectRet = 1 + 2 + 3 + 4 + 5 + 6; break; case 7: resultRet = ptr_as_func(_func)(1, 2, 3, 4, 5, 6, 7); expectRet = 1 + 2 + 3 + 4 + 5 + 6 + 7; break; case 8: resultRet = ptr_as_func(_func)(1, 2, 3, 4, 5, 6, 7, 8); expectRet = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8; break; } result.setFormat("ret={%u, %u}", resultRet >> 28, resultRet & 0x0FFFFFFFU); expect.setFormat("ret={%u, %u}", expectRet >> 28, expectRet & 0x0FFFFFFFU); return resultRet == expectRet; } uint32_t _numArgs; uint32_t _numVars; uint32_t _alignment; bool _naked; }; // ============================================================================ // [X86Test_AlignNone] // ============================================================================ class X86Test_AlignNone : public X86Test { public: X86Test_AlignNone() : X86Test("[Align] None") {} static void add(X86TestManager& mgr) { mgr.add(new X86Test_AlignNone()); } virtual void compile(X86Compiler& cc) { cc.addFunc(FuncSignature0(CallConv::kIdHost)); cc.align(kAlignCode, 0); cc.align(kAlignCode, 1); cc.endFunc(); } virtual bool run(void* _func, StringBuilder& result, StringBuilder& expect) { typedef void (*Func)(void); Func func = ptr_as_func(_func); func(); return true; } }; // ============================================================================ // [X86Test_JumpCross] // ============================================================================ class X86Test_JumpCross : public X86Test { public: X86Test_JumpCross() : X86Test("[Jump] Cross jump") {} static void add(X86TestManager& mgr) { mgr.add(new X86Test_JumpCross()); } virtual void compile(X86Compiler& cc) { cc.addFunc(FuncSignature0(CallConv::kIdHost)); Label L1 = cc.newLabel(); Label L2 = cc.newLabel(); Label L3 = cc.newLabel(); cc.jmp(L2); cc.bind(L1); cc.jmp(L3); cc.bind(L2); cc.jmp(L1); cc.bind(L3); cc.ret(); cc.endFunc(); } virtual bool run(void* _func, StringBuilder& result, StringBuilder& expect) { typedef void (*Func)(void); Func func = ptr_as_func(_func); func(); return true; } }; // ============================================================================ // [X86Test_JumpMany] // ============================================================================ class X86Test_JumpMany : public X86Test { public: X86Test_JumpMany() : X86Test("[Jump] Many jumps") {} static void add(X86TestManager& mgr) { mgr.add(new X86Test_JumpMany()); } virtual void compile(X86Compiler& cc) { cc.addFunc(FuncSignature0(CallConv::kIdHost)); for (uint32_t i = 0; i < 1000; i++) { Label L = cc.newLabel(); cc.jmp(L); cc.bind(L); } X86Gp ret = cc.newInt32("ret"); cc.xor_(ret, ret); cc.ret(ret); cc.endFunc(); } virtual bool run(void* _func, StringBuilder& result, StringBuilder& expect) { typedef int (*Func)(void); Func func = ptr_as_func(_func); int resultRet = func(); int expectRet = 0; result.setFormat("ret={%d}", resultRet); expect.setFormat("ret={%d}", expectRet); return resultRet == expectRet; } }; // ============================================================================ // [X86Test_JumpUnreachable1] // ============================================================================ class X86Test_JumpUnreachable1 : public X86Test { public: X86Test_JumpUnreachable1() : X86Test("[Jump] Unreachable #1") {} static void add(X86TestManager& mgr) { mgr.add(new X86Test_JumpUnreachable1()); } virtual void compile(X86Compiler& cc) { cc.addFunc(FuncSignature0(CallConv::kIdHost)); Label L_1 = cc.newLabel(); Label L_2 = cc.newLabel(); Label L_3 = cc.newLabel(); Label L_4 = cc.newLabel(); Label L_5 = cc.newLabel(); Label L_6 = cc.newLabel(); Label L_7 = cc.newLabel(); X86Gp v0 = cc.newUInt32("v0"); X86Gp v1 = cc.newUInt32("v1"); cc.bind(L_2); cc.bind(L_3); cc.jmp(L_1); cc.bind(L_5); cc.mov(v0, 0); cc.bind(L_6); cc.jmp(L_3); cc.mov(v1, 1); cc.jmp(L_1); cc.bind(L_4); cc.jmp(L_2); cc.bind(L_7); cc.add(v0, v1); cc.align(kAlignCode, 16); cc.bind(L_1); cc.ret(); cc.endFunc(); } virtual bool run(void* _func, StringBuilder& result, StringBuilder& expect) { typedef void (*Func)(void); Func func = ptr_as_func(_func); func(); result.appendString("ret={}"); expect.appendString("ret={}"); return true; } }; // ============================================================================ // [X86Test_JumpUnreachable2] // ============================================================================ class X86Test_JumpUnreachable2 : public X86Test { public: X86Test_JumpUnreachable2() : X86Test("[Jump] Unreachable #2") {} static void add(X86TestManager& mgr) { mgr.add(new X86Test_JumpUnreachable2()); } virtual void compile(X86Compiler& cc) { cc.addFunc(FuncSignature0(CallConv::kIdHost)); Label L_1 = cc.newLabel(); Label L_2 = cc.newLabel(); X86Gp v0 = cc.newUInt32("v0"); X86Gp v1 = cc.newUInt32("v1"); cc.jmp(L_1); cc.bind(L_2); cc.mov(v0, 1); cc.mov(v1, 2); cc.cmp(v0, v1); cc.jz(L_2); cc.jmp(L_1); cc.bind(L_1); cc.ret(); cc.endFunc(); } virtual bool run(void* _func, StringBuilder& result, StringBuilder& expect) { typedef void (*Func)(void); Func func = ptr_as_func(_func); func(); result.appendString("ret={}"); expect.appendString("ret={}"); return true; } }; // ============================================================================ // [X86Test_AllocBase] // ============================================================================ class X86Test_AllocBase : public X86Test { public: X86Test_AllocBase() : X86Test("[Alloc] Base") {} static void add(X86TestManager& mgr) { mgr.add(new X86Test_AllocBase()); } virtual void compile(X86Compiler& cc) { cc.addFunc(FuncSignature0(CallConv::kIdHost)); X86Gp v0 = cc.newInt32("v0"); X86Gp v1 = cc.newInt32("v1"); X86Gp v2 = cc.newInt32("v2"); X86Gp v3 = cc.newInt32("v3"); X86Gp v4 = cc.newInt32("v4"); cc.xor_(v0, v0); cc.mov(v1, 1); cc.mov(v2, 2); cc.mov(v3, 3); cc.mov(v4, 4); cc.add(v0, v1); cc.add(v0, v2); cc.add(v0, v3); cc.add(v0, v4); cc.ret(v0); cc.endFunc(); } virtual bool run(void* _func, StringBuilder& result, StringBuilder& expect) { typedef int (*Func)(void); Func func = ptr_as_func(_func); int resultRet = func(); int expectRet = 1 + 2 + 3 + 4; result.setFormat("ret=%d", resultRet); expect.setFormat("ret=%d", expectRet); return resultRet == expectRet; } }; // ============================================================================ // [X86Test_AllocManual] // ============================================================================ class X86Test_AllocManual : public X86Test { public: X86Test_AllocManual() : X86Test("[Alloc] Manual alloc/spill") {} static void add(X86TestManager& mgr) { mgr.add(new X86Test_AllocManual()); } virtual void compile(X86Compiler& cc) { cc.addFunc(FuncSignature0(CallConv::kIdHost)); X86Gp v0 = cc.newInt32("v0"); X86Gp v1 = cc.newInt32("v1"); X86Gp cnt = cc.newInt32("cnt"); cc.xor_(v0, v0); cc.xor_(v1, v1); cc.spill(v0); cc.spill(v1); Label L = cc.newLabel(); cc.mov(cnt, 32); cc.bind(L); cc.inc(v1); cc.add(v0, v1); cc.dec(cnt); cc.jnz(L); cc.ret(v0); cc.endFunc(); } virtual bool run(void* _func, StringBuilder& result, StringBuilder& expect) { typedef int (*Func)(void); Func func = ptr_as_func(_func); int resultRet = func(); int expectRet = 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + 20 + 21 + 22 + 23 + 24 + 25 + 26 + 27 + 28 + 29 + 30 + 31 + 32; result.setFormat("ret=%d", resultRet); expect.setFormat("ret=%d", expectRet); return resultRet == expectRet; } }; // ============================================================================ // [X86Test_AllocUseMem] // ============================================================================ class X86Test_AllocUseMem : public X86Test { public: X86Test_AllocUseMem() : X86Test("[Alloc] Alloc/use mem") {} static void add(X86TestManager& mgr) { mgr.add(new X86Test_AllocUseMem()); } virtual void compile(X86Compiler& cc) { cc.addFunc(FuncSignature2(CallConv::kIdHost)); X86Gp iIdx = cc.newInt32("iIdx"); X86Gp iEnd = cc.newInt32("iEnd"); X86Gp aIdx = cc.newInt32("aIdx"); X86Gp aEnd = cc.newInt32("aEnd"); Label L_1 = cc.newLabel(); cc.setArg(0, aIdx); cc.setArg(1, aEnd); cc.mov(iIdx, aIdx); cc.mov(iEnd, aEnd); cc.spill(iEnd); cc.bind(L_1); cc.inc(iIdx); cc.cmp(iIdx, iEnd.m()); cc.jne(L_1); cc.ret(iIdx); cc.endFunc(); } virtual bool run(void* _func, StringBuilder& result, StringBuilder& expect) { typedef int (*Func)(int, int); Func func = ptr_as_func(_func); int resultRet = func(10, 20); int expectRet = 20; result.setFormat("ret=%d", resultRet); expect.setFormat("ret=%d", expectRet); return resultRet == expectRet; } }; // ============================================================================ // [X86Test_AllocMany1] // ============================================================================ class X86Test_AllocMany1 : public X86Test { public: X86Test_AllocMany1() : X86Test("[Alloc] Many #1") {} enum { kCount = 8 }; static void add(X86TestManager& mgr) { mgr.add(new X86Test_AllocMany1()); } virtual void compile(X86Compiler& cc) { cc.addFunc(FuncSignature2(CallConv::kIdHost)); X86Gp a0 = cc.newIntPtr("a0"); X86Gp a1 = cc.newIntPtr("a1"); cc.setArg(0, a0); cc.setArg(1, a1); // Create some variables. X86Gp t = cc.newInt32("t"); X86Gp x[kCount]; uint32_t i; for (i = 0; i < kCount; i++) { x[i] = cc.newInt32("x%u", i); } // Setup variables (use mov with reg/imm to se if register allocator works). for (i = 0; i < kCount; i++) { cc.mov(x[i], static_cast(i + 1)); } // Make sum (addition). cc.xor_(t, t); for (i = 0; i < kCount; i++) { cc.add(t, x[i]); } // Store result to a given pointer in first argument. cc.mov(x86::dword_ptr(a0), t); // Clear t. cc.xor_(t, t); // Make sum (subtraction). for (i = 0; i < kCount; i++) { cc.sub(t, x[i]); } // Store result to a given pointer in second argument. cc.mov(x86::dword_ptr(a1), t); // End of function. cc.endFunc(); } virtual bool run(void* _func, StringBuilder& result, StringBuilder& expect) { typedef void (*Func)(int*, int*); Func func = ptr_as_func(_func); int resultX; int resultY; int expectX = 36; int expectY = -36; func(&resultX, &resultY); result.setFormat("ret={x=%d, y=%d}", resultX, resultY); expect.setFormat("ret={x=%d, y=%d}", expectX, expectY); return resultX == expectX && resultY == expectY; } }; // ============================================================================ // [X86Test_AllocMany2] // ============================================================================ class X86Test_AllocMany2 : public X86Test { public: X86Test_AllocMany2() : X86Test("[Alloc] Many #2") {} static void add(X86TestManager& mgr) { mgr.add(new X86Test_AllocMany2()); } virtual void compile(X86Compiler& cc) { cc.addFunc(FuncSignature1(CallConv::kIdHost)); X86Gp var[32]; X86Gp a = cc.newIntPtr("a"); cc.setArg(0, a); int i; for (i = 0; i < ASMJIT_ARRAY_SIZE(var); i++) { var[i] = cc.newInt32("var[%d]", i); } for (i = 0; i < ASMJIT_ARRAY_SIZE(var); i++) { cc.xor_(var[i], var[i]); } X86Gp v0 = cc.newInt32("v0"); Label L = cc.newLabel(); cc.mov(v0, 32); cc.bind(L); for (i = 0; i < ASMJIT_ARRAY_SIZE(var); i++) { cc.add(var[i], i); } cc.dec(v0); cc.jnz(L); for (i = 0; i < ASMJIT_ARRAY_SIZE(var); i++) { cc.mov(x86::dword_ptr(a, i * 4), var[i]); } cc.endFunc(); } virtual bool run(void* _func, StringBuilder& result, StringBuilder& expect) { typedef void (*Func)(int*); Func func = ptr_as_func(_func); int i; int resultBuf[32]; int expectBuf[32]; for (i = 0; i < ASMJIT_ARRAY_SIZE(resultBuf); i++) { expectBuf[i] = i * 32; } bool success = true; func(resultBuf); for (i = 0; i < ASMJIT_ARRAY_SIZE(resultBuf); i++) { result.appendFormat("%d", resultBuf[i]); expect.appendFormat("%d", expectBuf[1]); success &= (resultBuf[i] == expectBuf[i]); } return success; } }; // ============================================================================ // [X86Test_AllocImul1] // ============================================================================ class X86Test_AllocImul1 : public X86Test { public: X86Test_AllocImul1() : X86Test("[Alloc] IMUL #1") {} static void add(X86TestManager& mgr) { mgr.add(new X86Test_AllocImul1()); } virtual void compile(X86Compiler& cc) { cc.addFunc(FuncSignature4(CallConv::kIdHost)); X86Gp dstHi = cc.newIntPtr("dstHi"); X86Gp dstLo = cc.newIntPtr("dstLo"); X86Gp vHi = cc.newInt32("vHi"); X86Gp vLo = cc.newInt32("vLo"); X86Gp src = cc.newInt32("src"); cc.setArg(0, dstHi); cc.setArg(1, dstLo); cc.setArg(2, vLo); cc.setArg(3, src); cc.imul(vHi, vLo, src); cc.mov(x86::dword_ptr(dstHi), vHi); cc.mov(x86::dword_ptr(dstLo), vLo); cc.endFunc(); } virtual bool run(void* _func, StringBuilder& result, StringBuilder& expect) { typedef void (*Func)(int*, int*, int, int); Func func = ptr_as_func(_func); int v0 = 4; int v1 = 4; int resultHi; int resultLo; int expectHi = 0; int expectLo = v0 * v1; func(&resultHi, &resultLo, v0, v1); result.setFormat("hi=%d, lo=%d", resultHi, resultLo); expect.setFormat("hi=%d, lo=%d", expectHi, expectLo); return resultHi == expectHi && resultLo == expectLo; } }; // ============================================================================ // [X86Test_AllocImul2] // ============================================================================ class X86Test_AllocImul2 : public X86Test { public: X86Test_AllocImul2() : X86Test("[Alloc] IMUL #2") {} static void add(X86TestManager& mgr) { mgr.add(new X86Test_AllocImul2()); } virtual void compile(X86Compiler& cc) { cc.addFunc(FuncSignature2(CallConv::kIdHost)); X86Gp dst = cc.newIntPtr("dst"); X86Gp src = cc.newIntPtr("src"); cc.setArg(0, dst); cc.setArg(1, src); for (unsigned int i = 0; i < 4; i++) { X86Gp x = cc.newInt32("x"); X86Gp y = cc.newInt32("y"); X86Gp hi = cc.newInt32("hi"); cc.mov(x, x86::dword_ptr(src, 0)); cc.mov(y, x86::dword_ptr(src, 4)); cc.imul(hi, x, y); cc.add(x86::dword_ptr(dst, 0), hi); cc.add(x86::dword_ptr(dst, 4), x); } cc.endFunc(); } virtual bool run(void* _func, StringBuilder& result, StringBuilder& expect) { typedef void (*Func)(int*, const int*); Func func = ptr_as_func(_func); int src[2] = { 4, 9 }; int resultRet[2] = { 0, 0 }; int expectRet[2] = { 0, (4 * 9) * 4 }; func(resultRet, src); result.setFormat("ret={%d, %d}", resultRet[0], resultRet[1]); expect.setFormat("ret={%d, %d}", expectRet[0], expectRet[1]); return resultRet[0] == expectRet[0] && resultRet[1] == expectRet[1]; } }; // ============================================================================ // [X86Test_AllocIdiv1] // ============================================================================ class X86Test_AllocIdiv1 : public X86Test { public: X86Test_AllocIdiv1() : X86Test("[Alloc] IDIV #1") {} static void add(X86TestManager& mgr) { mgr.add(new X86Test_AllocIdiv1()); } virtual void compile(X86Compiler& cc) { cc.addFunc(FuncSignature2(CallConv::kIdHost)); X86Gp a = cc.newInt32("a"); X86Gp b = cc.newInt32("b"); X86Gp dummy = cc.newInt32("dummy"); cc.setArg(0, a); cc.setArg(1, b); cc.xor_(dummy, dummy); cc.idiv(dummy, a, b); cc.ret(a); cc.endFunc(); } virtual bool run(void* _func, StringBuilder& result, StringBuilder& expect) { typedef int (*Func)(int, int); Func func = ptr_as_func(_func); int v0 = 2999; int v1 = 245; int resultRet = func(v0, v1); int expectRet = 2999 / 245; result.setFormat("result=%d", resultRet); expect.setFormat("result=%d", expectRet); return resultRet == expectRet; } }; // ============================================================================ // [X86Test_AllocSetz] // ============================================================================ class X86Test_AllocSetz : public X86Test { public: X86Test_AllocSetz() : X86Test("[Alloc] SETZ") {} static void add(X86TestManager& mgr) { mgr.add(new X86Test_AllocSetz()); } virtual void compile(X86Compiler& cc) { cc.addFunc(FuncSignature3(CallConv::kIdHost)); X86Gp src0 = cc.newInt32("src0"); X86Gp src1 = cc.newInt32("src1"); X86Gp dst0 = cc.newIntPtr("dst0"); cc.setArg(0, src0); cc.setArg(1, src1); cc.setArg(2, dst0); cc.cmp(src0, src1); cc.setz(x86::byte_ptr(dst0)); cc.endFunc(); } virtual bool run(void* _func, StringBuilder& result, StringBuilder& expect) { typedef void (*Func)(int, int, char*); Func func = ptr_as_func(_func); char resultBuf[4]; char expectBuf[4] = { 1, 0, 0, 1 }; func(0, 0, &resultBuf[0]); // We are expecting 1 (0 == 0). func(0, 1, &resultBuf[1]); // We are expecting 0 (0 != 1). func(1, 0, &resultBuf[2]); // We are expecting 0 (1 != 0). func(1, 1, &resultBuf[3]); // We are expecting 1 (1 == 1). result.setFormat("out={%d, %d, %d, %d}", resultBuf[0], resultBuf[1], resultBuf[2], resultBuf[3]); expect.setFormat("out={%d, %d, %d, %d}", expectBuf[0], expectBuf[1], expectBuf[2], expectBuf[3]); return resultBuf[0] == expectBuf[0] && resultBuf[1] == expectBuf[1] && resultBuf[2] == expectBuf[2] && resultBuf[3] == expectBuf[3] ; } }; // ============================================================================ // [X86Test_AllocShlRor] // ============================================================================ class X86Test_AllocShlRor : public X86Test { public: X86Test_AllocShlRor() : X86Test("[Alloc] SHL/ROR") {} static void add(X86TestManager& mgr) { mgr.add(new X86Test_AllocShlRor()); } virtual void compile(X86Compiler& cc) { cc.addFunc(FuncSignature4(CallConv::kIdHost)); X86Gp dst = cc.newIntPtr("dst"); X86Gp var = cc.newInt32("var"); X86Gp vShlParam = cc.newInt32("vShlParam"); X86Gp vRorParam = cc.newInt32("vRorParam"); cc.setArg(0, dst); cc.setArg(1, var); cc.setArg(2, vShlParam); cc.setArg(3, vRorParam); cc.shl(var, vShlParam); cc.ror(var, vRorParam); cc.mov(x86::dword_ptr(dst), var); cc.endFunc(); } virtual bool run(void* _func, StringBuilder& result, StringBuilder& expect) { typedef void (*Func)(int*, int, int, int); Func func = ptr_as_func(_func); int v0 = 0x000000FF; int resultRet; int expectRet = 0x0000FF00; func(&resultRet, v0, 16, 8); result.setFormat("ret=%d", resultRet); expect.setFormat("ret=%d", expectRet); return resultRet == expectRet; } }; // ============================================================================ // [X86Test_AllocGpLo] // ============================================================================ class X86Test_AllocGpLo : public X86Test { public: X86Test_AllocGpLo() : X86Test("[Alloc] GPB-LO") {} enum { kCount = 32 }; static void add(X86TestManager& mgr) { mgr.add(new X86Test_AllocGpLo()); } virtual void compile(X86Compiler& cc) { cc.addFunc(FuncSignature1(CallConv::kIdHost)); X86Gp rPtr = cc.newUIntPtr("rPtr"); X86Gp rSum = cc.newUInt32("rSum"); cc.setArg(0, rPtr); X86Gp rVar[kCount]; uint32_t i; for (i = 0; i < kCount; i++) { rVar[i] = cc.newUInt32("rVar[%u]", i); } // Init pseudo-regs with values from our array. for (i = 0; i < kCount; i++) { cc.mov(rVar[i], x86::dword_ptr(rPtr, i * 4)); } for (i = 2; i < kCount; i++) { // Add and truncate to 8 bit; no purpose, just mess with jit. cc.add (rVar[i ], rVar[i-1]); cc.movzx(rVar[i ], rVar[i ].r8()); cc.movzx(rVar[i-2], rVar[i-1].r8()); cc.movzx(rVar[i-1], rVar[i-2].r8()); } // Sum up all computed values. cc.mov(rSum, 0); for (i = 0; i < kCount; i++) { cc.add(rSum, rVar[i]); } // Return the sum. cc.ret(rSum); cc.endFunc(); } virtual bool run(void* _func, StringBuilder& result, StringBuilder& expect) { typedef int (*Func)(uint32_t*); Func func = ptr_as_func(_func); unsigned int i; uint32_t buf[kCount]; uint32_t resultRet; uint32_t expectRet; expectRet = 0; for (i = 0; i < kCount; i++) { buf[i] = 1; } for (i = 2; i < kCount; i++) { buf[i ]+= buf[i-1]; buf[i ] = buf[i ] & 0xFF; buf[i-2] = buf[i-1] & 0xFF; buf[i-1] = buf[i-2] & 0xFF; } for (i = 0; i < kCount; i++) { expectRet += buf[i]; } for (i = 0; i < kCount; i++) { buf[i] = 1; } resultRet = func(buf); result.setFormat("ret=%d", resultRet); expect.setFormat("ret=%d", expectRet); return resultRet == expectRet; } }; // ============================================================================ // [X86Test_AllocRepMovsb] // ============================================================================ class X86Test_AllocRepMovsb : public X86Test { public: X86Test_AllocRepMovsb() : X86Test("[Alloc] REP MOVS") {} static void add(X86TestManager& mgr) { mgr.add(new X86Test_AllocRepMovsb()); } virtual void compile(X86Compiler& cc) { cc.addFunc(FuncSignature3(CallConv::kIdHost)); X86Gp dst = cc.newIntPtr("dst"); X86Gp src = cc.newIntPtr("src"); X86Gp cnt = cc.newIntPtr("cnt"); cc.setArg(0, dst); cc.setArg(1, src); cc.setArg(2, cnt); cc.rep(cnt).movs(x86::byte_ptr(dst), x86::byte_ptr(src)); cc.endFunc(); } virtual bool run(void* _func, StringBuilder& result, StringBuilder& expect) { typedef void (*Func)(void*, void*, size_t); Func func = ptr_as_func(_func); char dst[20] = { 0 }; char src[20] = "Hello AsmJit!"; func(dst, src, strlen(src) + 1); result.setFormat("ret=\"%s\"", dst); expect.setFormat("ret=\"%s\"", src); return result == expect; } }; // ============================================================================ // [X86Test_AllocIfElse1] // ============================================================================ class X86Test_AllocIfElse1 : public X86Test { public: X86Test_AllocIfElse1() : X86Test("[Alloc] If-Else #1") {} static void add(X86TestManager& mgr) { mgr.add(new X86Test_AllocIfElse1()); } virtual void compile(X86Compiler& cc) { cc.addFunc(FuncSignature2(CallConv::kIdHost)); X86Gp v1 = cc.newInt32("v1"); X86Gp v2 = cc.newInt32("v2"); Label L_1 = cc.newLabel(); Label L_2 = cc.newLabel(); cc.setArg(0, v1); cc.setArg(1, v2); cc.cmp(v1, v2); cc.jg(L_1); cc.mov(v1, 1); cc.jmp(L_2); cc.bind(L_1); cc.mov(v1, 2); cc.bind(L_2); cc.ret(v1); cc.endFunc(); } virtual bool run(void* _func, StringBuilder& result, StringBuilder& expect) { typedef int (*Func)(int, int); Func func = ptr_as_func(_func); int a = func(0, 1); int b = func(1, 0); result.appendFormat("ret={%d, %d}", a, b); result.appendFormat("ret={%d, %d}", 1, 2); return a == 1 && b == 2; } }; // ============================================================================ // [X86Test_AllocIfElse2] // ============================================================================ class X86Test_AllocIfElse2 : public X86Test { public: X86Test_AllocIfElse2() : X86Test("[Alloc] If-Else #2") {} static void add(X86TestManager& mgr) { mgr.add(new X86Test_AllocIfElse2()); } virtual void compile(X86Compiler& cc) { cc.addFunc(FuncSignature2(CallConv::kIdHost)); X86Gp v1 = cc.newInt32("v1"); X86Gp v2 = cc.newInt32("v2"); Label L_1 = cc.newLabel(); Label L_2 = cc.newLabel(); Label L_3 = cc.newLabel(); Label L_4 = cc.newLabel(); cc.setArg(0, v1); cc.setArg(1, v2); cc.jmp(L_1); cc.bind(L_2); cc.jmp(L_4); cc.bind(L_1); cc.cmp(v1, v2); cc.jg(L_3); cc.mov(v1, 1); cc.jmp(L_2); cc.bind(L_3); cc.mov(v1, 2); cc.jmp(L_2); cc.bind(L_4); cc.ret(v1); cc.endFunc(); } virtual bool run(void* _func, StringBuilder& result, StringBuilder& expect) { typedef int (*Func)(int, int); Func func = ptr_as_func(_func); int a = func(0, 1); int b = func(1, 0); result.appendFormat("ret={%d, %d}", a, b); result.appendFormat("ret={%d, %d}", 1, 2); return a == 1 && b == 2; } }; // ============================================================================ // [X86Test_AllocIfElse3] // ============================================================================ class X86Test_AllocIfElse3 : public X86Test { public: X86Test_AllocIfElse3() : X86Test("[Alloc] If-Else #3") {} static void add(X86TestManager& mgr) { mgr.add(new X86Test_AllocIfElse3()); } virtual void compile(X86Compiler& cc) { cc.addFunc(FuncSignature2(CallConv::kIdHost)); X86Gp v1 = cc.newInt32("v1"); X86Gp v2 = cc.newInt32("v2"); X86Gp counter = cc.newInt32("counter"); Label L_1 = cc.newLabel(); Label L_Loop = cc.newLabel(); Label L_Exit = cc.newLabel(); cc.setArg(0, v1); cc.setArg(1, v2); cc.cmp(v1, v2); cc.jg(L_1); cc.mov(counter, 0); cc.bind(L_Loop); cc.mov(v1, counter); cc.inc(counter); cc.cmp(counter, 1); cc.jle(L_Loop); cc.jmp(L_Exit); cc.bind(L_1); cc.mov(v1, 2); cc.bind(L_Exit); cc.ret(v1); cc.endFunc(); } virtual bool run(void* _func, StringBuilder& result, StringBuilder& expect) { typedef int (*Func)(int, int); Func func = ptr_as_func(_func); int a = func(0, 1); int b = func(1, 0); result.appendFormat("ret={%d, %d}", a, b); result.appendFormat("ret={%d, %d}", 1, 2); return a == 1 && b == 2; } }; // ============================================================================ // [X86Test_AllocIfElse4] // ============================================================================ class X86Test_AllocIfElse4 : public X86Test { public: X86Test_AllocIfElse4() : X86Test("[Alloc] If-Else #4") {} static void add(X86TestManager& mgr) { mgr.add(new X86Test_AllocIfElse4()); } virtual void compile(X86Compiler& cc) { cc.addFunc(FuncSignature2(CallConv::kIdHost)); X86Gp v1 = cc.newInt32("v1"); X86Gp v2 = cc.newInt32("v2"); X86Gp counter = cc.newInt32("counter"); Label L_1 = cc.newLabel(); Label L_Loop1 = cc.newLabel(); Label L_Loop2 = cc.newLabel(); Label L_Exit = cc.newLabel(); cc.mov(counter, 0); cc.setArg(0, v1); cc.setArg(1, v2); cc.cmp(v1, v2); cc.jg(L_1); cc.bind(L_Loop1); cc.mov(v1, counter); cc.inc(counter); cc.cmp(counter, 1); cc.jle(L_Loop1); cc.jmp(L_Exit); cc.bind(L_1); cc.bind(L_Loop2); cc.mov(v1, counter); cc.inc(counter); cc.cmp(counter, 2); cc.jle(L_Loop2); cc.bind(L_Exit); cc.ret(v1); cc.endFunc(); } virtual bool run(void* _func, StringBuilder& result, StringBuilder& expect) { typedef int (*Func)(int, int); Func func = ptr_as_func(_func); int a = func(0, 1); int b = func(1, 0); result.appendFormat("ret={%d, %d}", a, b); result.appendFormat("ret={%d, %d}", 1, 2); return a == 1 && b == 2; } }; // ============================================================================ // [X86Test_AllocInt8] // ============================================================================ class X86Test_AllocInt8 : public X86Test { public: X86Test_AllocInt8() : X86Test("[Alloc] Int8") {} static void add(X86TestManager& mgr) { mgr.add(new X86Test_AllocInt8()); } virtual void compile(X86Compiler& cc) { X86Gp x = cc.newInt8("x"); X86Gp y = cc.newInt32("y"); cc.addFunc(FuncSignature1(CallConv::kIdHost)); cc.setArg(0, x); cc.movsx(y, x); cc.ret(y); cc.endFunc(); } virtual bool run(void* _func, StringBuilder& result, StringBuilder& expect) { typedef int (*Func)(char); Func func = ptr_as_func(_func); int resultRet = func(-13); int expectRet = -13; result.setFormat("ret=%d", resultRet); expect.setFormat("ret=%d", expectRet); return resultRet == expectRet; } }; // ============================================================================ // [X86Test_AllocArgsIntPtr] // ============================================================================ class X86Test_AllocArgsIntPtr : public X86Test { public: X86Test_AllocArgsIntPtr() : X86Test("[Alloc] Args IntPtr") {} static void add(X86TestManager& mgr) { mgr.add(new X86Test_AllocArgsIntPtr()); } virtual void compile(X86Compiler& cc) { cc.addFunc(FuncSignature8(CallConv::kIdHost)); uint32_t i; X86Gp var[8]; for (i = 0; i < 8; i++) { var[i] = cc.newIntPtr("var%u", i); cc.setArg(i, var[i]); } for (i = 0; i < 8; i++) { cc.add(var[i], static_cast(i + 1)); } // Move some data into buffer provided by arguments so we can verify if it // really works without looking into assembler output. for (i = 0; i < 8; i++) { cc.add(x86::byte_ptr(var[i]), static_cast(i + 1)); } cc.endFunc(); } virtual bool run(void* _func, StringBuilder& result, StringBuilder& expect) { typedef void (*Func)(void*, void*, void*, void*, void*, void*, void*, void*); Func func = ptr_as_func(_func); uint8_t resultBuf[9] = { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; uint8_t expectBuf[9] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 }; func(resultBuf, resultBuf, resultBuf, resultBuf, resultBuf, resultBuf, resultBuf, resultBuf); result.setFormat("buf={%d, %d, %d, %d, %d, %d, %d, %d, %d}", resultBuf[0], resultBuf[1], resultBuf[2], resultBuf[3], resultBuf[4], resultBuf[5], resultBuf[6], resultBuf[7], resultBuf[8]); expect.setFormat("buf={%d, %d, %d, %d, %d, %d, %d, %d, %d}", expectBuf[0], expectBuf[1], expectBuf[2], expectBuf[3], expectBuf[4], expectBuf[5], expectBuf[6], expectBuf[7], expectBuf[8]); return result == expect; } }; // ============================================================================ // [X86Test_AllocArgsFloat] // ============================================================================ class X86Test_AllocArgsFloat : public X86Test { public: X86Test_AllocArgsFloat() : X86Test("[Alloc] Args Float") {} static void add(X86TestManager& mgr) { mgr.add(new X86Test_AllocArgsFloat()); } virtual void compile(X86Compiler& cc) { cc.addFunc(FuncSignature8(CallConv::kIdHost)); uint32_t i; X86Gp p = cc.newIntPtr("p"); X86Xmm xv[7]; for (i = 0; i < 7; i++) { xv[i] = cc.newXmmSs("xv%u", i); cc.setArg(i, xv[i]); } cc.setArg(7, p); cc.addss(xv[0], xv[1]); cc.addss(xv[0], xv[2]); cc.addss(xv[0], xv[3]); cc.addss(xv[0], xv[4]); cc.addss(xv[0], xv[5]); cc.addss(xv[0], xv[6]); cc.movss(x86::ptr(p), xv[0]); cc.endFunc(); } virtual bool run(void* _func, StringBuilder& result, StringBuilder& expect) { typedef void (*Func)(float, float, float, float, float, float, float, float*); Func func = ptr_as_func(_func); float resultRet; float expectRet = 1.0f + 2.0f + 3.0f + 4.0f + 5.0f + 6.0f + 7.0f; func(1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, &resultRet); result.setFormat("ret={%g}", resultRet); expect.setFormat("ret={%g}", expectRet); return resultRet == expectRet; } }; // ============================================================================ // [X86Test_AllocArgsDouble] // ============================================================================ class X86Test_AllocArgsDouble : public X86Test { public: X86Test_AllocArgsDouble() : X86Test("[Alloc] Args Double") {} static void add(X86TestManager& mgr) { mgr.add(new X86Test_AllocArgsDouble()); } virtual void compile(X86Compiler& cc) { cc.addFunc(FuncSignature8(CallConv::kIdHost)); uint32_t i; X86Gp p = cc.newIntPtr("p"); X86Xmm xv[7]; for (i = 0; i < 7; i++) { xv[i] = cc.newXmmSd("xv%u", i); cc.setArg(i, xv[i]); } cc.setArg(7, p); cc.addsd(xv[0], xv[1]); cc.addsd(xv[0], xv[2]); cc.addsd(xv[0], xv[3]); cc.addsd(xv[0], xv[4]); cc.addsd(xv[0], xv[5]); cc.addsd(xv[0], xv[6]); cc.movsd(x86::ptr(p), xv[0]); cc.endFunc(); } virtual bool run(void* _func, StringBuilder& result, StringBuilder& expect) { typedef void (*Func)(double, double, double, double, double, double, double, double*); Func func = ptr_as_func(_func); double resultRet; double expectRet = 1.0 + 2.0 + 3.0 + 4.0 + 5.0 + 6.0 + 7.0; func(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, &resultRet); result.setFormat("ret={%g}", resultRet); expect.setFormat("ret={%g}", expectRet); return resultRet == expectRet; } }; // ============================================================================ // [X86Test_AllocRetFloat] // ============================================================================ class X86Test_AllocRetFloat : public X86Test { public: X86Test_AllocRetFloat() : X86Test("[Alloc] Ret Float") {} static void add(X86TestManager& mgr) { mgr.add(new X86Test_AllocRetFloat()); } virtual void compile(X86Compiler& cc) { cc.addFunc(FuncSignature2(CallConv::kIdHost)); X86Xmm a = cc.newXmmSs("a"); X86Xmm b = cc.newXmmSs("b"); cc.setArg(0, a); cc.setArg(1, b); cc.addss(a, b); cc.ret(a); cc.endFunc(); } virtual bool run(void* _func, StringBuilder& result, StringBuilder& expect) { typedef float (*Func)(float, float); Func func = ptr_as_func(_func); float resultRet = func(1.0f, 2.0f); float expectRet = 1.0f + 2.0f; result.setFormat("ret={%g}", resultRet); expect.setFormat("ret={%g}", expectRet); return resultRet == expectRet; } }; // ============================================================================ // [X86Test_AllocRetDouble] // ============================================================================ class X86Test_AllocRetDouble : public X86Test { public: X86Test_AllocRetDouble() : X86Test("[Alloc] Ret Double") {} static void add(X86TestManager& mgr) { mgr.add(new X86Test_AllocRetDouble()); } virtual void compile(X86Compiler& cc) { cc.addFunc(FuncSignature2(CallConv::kIdHost)); X86Xmm a = cc.newXmmSd("a"); X86Xmm b = cc.newXmmSd("b"); cc.setArg(0, a); cc.setArg(1, b); cc.addsd(a, b); cc.ret(a); cc.endFunc(); } virtual bool run(void* _func, StringBuilder& result, StringBuilder& expect) { typedef double (*Func)(double, double); Func func = ptr_as_func(_func); double resultRet = func(1.0, 2.0); double expectRet = 1.0 + 2.0; result.setFormat("ret={%g}", resultRet); expect.setFormat("ret={%g}", expectRet); return resultRet == expectRet; } }; // ============================================================================ // [X86Test_AllocStack1] // ============================================================================ class X86Test_AllocStack1 : public X86Test { public: X86Test_AllocStack1() : X86Test("[Alloc] Stack #1") {} enum { kSize = 256 }; static void add(X86TestManager& mgr) { mgr.add(new X86Test_AllocStack1()); } virtual void compile(X86Compiler& cc) { cc.addFunc(FuncSignature0(CallConv::kIdHost)); X86Mem stack = cc.newStack(kSize, 1); stack.setSize(1); X86Gp i = cc.newIntPtr("i"); X86Gp a = cc.newInt32("a"); X86Gp b = cc.newInt32("b"); Label L_1 = cc.newLabel(); Label L_2 = cc.newLabel(); // Fill stack by sequence [0, 1, 2, 3 ... 255]. cc.xor_(i, i); X86Mem stackWithIndex = stack.clone(); stackWithIndex.setIndex(i, 0); cc.bind(L_1); cc.mov(stackWithIndex, i.r8()); cc.inc(i); cc.cmp(i, 255); cc.jle(L_1); // Sum sequence in stack. cc.xor_(i, i); cc.xor_(a, a); cc.bind(L_2); cc.movzx(b, stackWithIndex); cc.add(a, b); cc.inc(i); cc.cmp(i, 255); cc.jle(L_2); cc.ret(a); cc.endFunc(); } virtual bool run(void* _func, StringBuilder& result, StringBuilder& expect) { typedef int (*Func)(void); Func func = ptr_as_func(_func); int resultRet = func(); int expectRet = 32640; result.setInt(resultRet); expect.setInt(expectRet); return resultRet == expectRet; } }; // ============================================================================ // [X86Test_AllocStack2] // ============================================================================ class X86Test_AllocStack2 : public X86Test { public: X86Test_AllocStack2() : X86Test("[Alloc] Stack #2") {} enum { kSize = 256 }; static void add(X86TestManager& mgr) { mgr.add(new X86Test_AllocStack2()); } virtual void compile(X86Compiler& cc) { cc.addFunc(FuncSignature0(CallConv::kIdHost)); const int kTokenSize = 32; X86Mem s1 = cc.newStack(kTokenSize, 32); X86Mem s2 = cc.newStack(kTokenSize, 32); X86Gp p1 = cc.newIntPtr("p1"); X86Gp p2 = cc.newIntPtr("p2"); X86Gp ret = cc.newInt32("ret"); Label L_Exit = cc.newLabel(); static const char token[kTokenSize] = "-+:|abcdefghijklmnopqrstuvwxyz|"; CCFuncCall* call; cc.lea(p1, s1); cc.lea(p2, s2); // Try to corrupt the stack if wrongly allocated. call = cc.call(imm_ptr((void*)memcpy), FuncSignature3(CallConv::kIdHostCDecl)); call->setArg(0, p1); call->setArg(1, imm_ptr(token)); call->setArg(2, imm(kTokenSize)); call->setRet(0, p1); call = cc.call(imm_ptr((void*)memcpy), FuncSignature3(CallConv::kIdHostCDecl)); call->setArg(0, p2); call->setArg(1, imm_ptr(token)); call->setArg(2, imm(kTokenSize)); call->setRet(0, p2); call = cc.call(imm_ptr((void*)memcmp), FuncSignature3(CallConv::kIdHostCDecl)); call->setArg(0, p1); call->setArg(1, p2); call->setArg(2, imm(kTokenSize)); call->setRet(0, ret); // This should be 0 on success, however, if both `p1` and `p2` were // allocated in the same address this check will still pass. cc.cmp(ret, 0); cc.jnz(L_Exit); // Checks whether `p1` and `p2` are different (must be). cc.xor_(ret, ret); cc.cmp(p1, p2); cc.setz(ret.r8()); cc.bind(L_Exit); cc.ret(ret); cc.endFunc(); } virtual bool run(void* _func, StringBuilder& result, StringBuilder& expect) { typedef int (*Func)(void); Func func = ptr_as_func(_func); int resultRet = func(); int expectRet = 0; // Must be zero, stack addresses must be different. result.setInt(resultRet); expect.setInt(expectRet); return resultRet == expectRet; } }; // ============================================================================ // [X86Test_AllocMemcpy] // ============================================================================ class X86Test_AllocMemcpy : public X86Test { public: X86Test_AllocMemcpy() : X86Test("[Alloc] Memcpy") {} enum { kCount = 32 }; static void add(X86TestManager& mgr) { mgr.add(new X86Test_AllocMemcpy()); } virtual void compile(X86Compiler& cc) { X86Gp dst = cc.newIntPtr("dst"); X86Gp src = cc.newIntPtr("src"); X86Gp cnt = cc.newUIntPtr("cnt"); Label L_Loop = cc.newLabel(); // Create base labels we use Label L_Exit = cc.newLabel(); // in our function. cc.addFunc(FuncSignature3(CallConv::kIdHost)); cc.setArg(0, dst); cc.setArg(1, src); cc.setArg(2, cnt); cc.alloc(dst); // Allocate all registers now, cc.alloc(src); // because we want to keep them cc.alloc(cnt); // in physical registers only. cc.test(cnt, cnt); // Exit if length is zero. cc.jz(L_Exit); cc.bind(L_Loop); // Bind the loop label here. X86Gp tmp = cc.newInt32("tmp"); // Copy a single dword (4 bytes). cc.mov(tmp, x86::dword_ptr(src)); cc.mov(x86::dword_ptr(dst), tmp); cc.add(src, 4); // Increment dst/src pointers. cc.add(dst, 4); cc.dec(cnt); // Loop until cnt isn't zero. cc.jnz(L_Loop); cc.bind(L_Exit); // Bind the exit label here. cc.endFunc(); // End of function. } virtual bool run(void* _func, StringBuilder& result, StringBuilder& expect) { typedef void (*Func)(uint32_t*, const uint32_t*, size_t); Func func = ptr_as_func(_func); uint32_t i; uint32_t dstBuffer[kCount]; uint32_t srcBuffer[kCount]; for (i = 0; i < kCount; i++) { dstBuffer[i] = 0; srcBuffer[i] = i; } func(dstBuffer, srcBuffer, kCount); result.setString("buf={"); expect.setString("buf={"); for (i = 0; i < kCount; i++) { if (i != 0) { result.appendString(", "); expect.appendString(", "); } result.appendFormat("%u", static_cast(dstBuffer[i])); expect.appendFormat("%u", static_cast(srcBuffer[i])); } result.appendString("}"); expect.appendString("}"); return result == expect; } }; // ============================================================================ // [X86Test_AllocAlphaBlend] // ============================================================================ class X86Test_AllocAlphaBlend : public X86Test { public: X86Test_AllocAlphaBlend() : X86Test("[Alloc] AlphaBlend") {} enum { kCount = 17 }; static void add(X86TestManager& mgr) { mgr.add(new X86Test_AllocAlphaBlend()); } static uint32_t blendSrcOver(uint32_t d, uint32_t s) { uint32_t saInv = ~s >> 24; uint32_t d_20 = (d ) & 0x00FF00FF; uint32_t d_31 = (d >> 8) & 0x00FF00FF; d_20 *= saInv; d_31 *= saInv; d_20 = ((d_20 + ((d_20 >> 8) & 0x00FF00FFU) + 0x00800080U) & 0xFF00FF00U) >> 8; d_31 = ((d_31 + ((d_31 >> 8) & 0x00FF00FFU) + 0x00800080U) & 0xFF00FF00U); return d_20 + d_31 + s; } virtual void compile(X86Compiler& cc) { asmtest::generateAlphaBlend(cc); } virtual bool run(void* _func, StringBuilder& result, StringBuilder& expect) { typedef void (*Func)(void*, const void*, size_t); Func func = ptr_as_func(_func); static const uint32_t dstConstData[] = { 0x00000000, 0x10101010, 0x20100804, 0x30200003, 0x40204040, 0x5000004D, 0x60302E2C, 0x706F6E6D, 0x807F4F2F, 0x90349001, 0xA0010203, 0xB03204AB, 0xC023AFBD, 0xD0D0D0C0, 0xE0AABBCC, 0xFFFFFFFF, 0xF8F4F2F1 }; static const uint32_t srcConstData[] = { 0xE0E0E0E0, 0xA0008080, 0x341F1E1A, 0xFEFEFEFE, 0x80302010, 0x49490A0B, 0x998F7798, 0x00000000, 0x01010101, 0xA0264733, 0xBAB0B1B9, 0xFF000000, 0xDAB0A0C1, 0xE0BACFDA, 0x99887766, 0xFFFFFF80, 0xEE0A5FEC }; uint32_t _dstBuffer[kCount + 3]; uint32_t _srcBuffer[kCount + 3]; // Has to be aligned. uint32_t* dstBuffer = (uint32_t*)Utils::alignTo((intptr_t)_dstBuffer, 16); uint32_t* srcBuffer = (uint32_t*)Utils::alignTo((intptr_t)_srcBuffer, 16); ::memcpy(dstBuffer, dstConstData, sizeof(dstConstData)); ::memcpy(srcBuffer, srcConstData, sizeof(srcConstData)); uint32_t i; uint32_t expBuffer[kCount]; for (i = 0; i < kCount; i++) { expBuffer[i] = blendSrcOver(dstBuffer[i], srcBuffer[i]); } func(dstBuffer, srcBuffer, kCount); result.setString("buf={"); expect.setString("buf={"); for (i = 0; i < kCount; i++) { if (i != 0) { result.appendString(", "); expect.appendString(", "); } result.appendFormat("%08X", static_cast(dstBuffer[i])); expect.appendFormat("%08X", static_cast(expBuffer[i])); } result.appendString("}"); expect.appendString("}"); return result == expect; } }; // ============================================================================ // [X86Test_CallBase] // ============================================================================ class X86Test_CallBase : public X86Test { public: X86Test_CallBase() : X86Test("[Call] CDecl") {} static void add(X86TestManager& mgr) { mgr.add(new X86Test_CallBase()); } virtual void compile(X86Compiler& cc) { X86Gp v0 = cc.newInt32("v0"); X86Gp v1 = cc.newInt32("v1"); X86Gp v2 = cc.newInt32("v2"); cc.addFunc(FuncSignature3(CallConv::kIdHost)); cc.setArg(0, v0); cc.setArg(1, v1); cc.setArg(2, v2); // Just do something. cc.shl(v0, 1); cc.shl(v1, 1); cc.shl(v2, 1); // Call a function. X86Gp fn = cc.newIntPtr("fn"); cc.mov(fn, imm_ptr(calledFunc)); CCFuncCall* call = cc.call(fn, FuncSignature3(CallConv::kIdHost)); call->setArg(0, v2); call->setArg(1, v1); call->setArg(2, v0); call->setRet(0, v0); cc.ret(v0); cc.endFunc(); } virtual bool run(void* _func, StringBuilder& result, StringBuilder& expect) { typedef int (*Func)(int, int, int); Func func = ptr_as_func(_func); int resultRet = func(3, 2, 1); int expectRet = 36; result.setFormat("ret=%d", resultRet); expect.setFormat("ret=%d", expectRet); return resultRet == expectRet; } static int calledFunc(int a, int b, int c) { return (a + b) * c; } }; // ============================================================================ // [X86Test_CallFast] // ============================================================================ class X86Test_CallFast : public X86Test { public: X86Test_CallFast() : X86Test("[Call] Fastcall") {} static void add(X86TestManager& mgr) { mgr.add(new X86Test_CallFast()); } virtual void compile(X86Compiler& cc) { X86Gp var = cc.newInt32("var"); X86Gp fn = cc.newIntPtr("fn"); cc.addFunc(FuncSignature1(CallConv::kIdHost)); cc.setArg(0, var); cc.mov(fn, imm_ptr(calledFunc)); CCFuncCall* call; call = cc.call(fn, FuncSignature1(CallConv::kIdHostFastCall)); call->setArg(0, var); call->setRet(0, var); call = cc.call(fn, FuncSignature1(CallConv::kIdHostFastCall)); call->setArg(0, var); call->setRet(0, var); cc.ret(var); cc.endFunc(); } virtual bool run(void* _func, StringBuilder& result, StringBuilder& expect) { typedef int (*Func)(int); Func func = ptr_as_func(_func); int resultRet = func(9); int expectRet = (9 * 9) * (9 * 9); result.setFormat("ret=%d", resultRet); expect.setFormat("ret=%d", expectRet); return resultRet == expectRet; } // Function that is called inside the generated one. Because this test is // mainly about register arguments, we need to use the fastcall calling // convention when running 32-bit. static int ASMJIT_FASTCALL calledFunc(int a) { return a * a; } }; // ============================================================================ // [X86Test_CallManyArgs] // ============================================================================ class X86Test_CallManyArgs : public X86Test { public: X86Test_CallManyArgs() : X86Test("[Call] Many Args") {} static void add(X86TestManager& mgr) { mgr.add(new X86Test_CallManyArgs()); } static int calledFunc(int a, int b, int c, int d, int e, int f, int g, int h, int i, int j) { return (a * b * c * d * e) + (f * g * h * i * j); } virtual void compile(X86Compiler& cc) { cc.addFunc(FuncSignature0(CallConv::kIdHost)); // Prepare. X86Gp fn = cc.newIntPtr("fn"); X86Gp va = cc.newInt32("va"); X86Gp vb = cc.newInt32("vb"); X86Gp vc = cc.newInt32("vc"); X86Gp vd = cc.newInt32("vd"); X86Gp ve = cc.newInt32("ve"); X86Gp vf = cc.newInt32("vf"); X86Gp vg = cc.newInt32("vg"); X86Gp vh = cc.newInt32("vh"); X86Gp vi = cc.newInt32("vi"); X86Gp vj = cc.newInt32("vj"); cc.mov(fn, imm_ptr(calledFunc)); cc.mov(va, 0x03); cc.mov(vb, 0x12); cc.mov(vc, 0xA0); cc.mov(vd, 0x0B); cc.mov(ve, 0x2F); cc.mov(vf, 0x02); cc.mov(vg, 0x0C); cc.mov(vh, 0x12); cc.mov(vi, 0x18); cc.mov(vj, 0x1E); // Call function. CCFuncCall* call = cc.call(fn, FuncSignature10(CallConv::kIdHost)); call->setArg(0, va); call->setArg(1, vb); call->setArg(2, vc); call->setArg(3, vd); call->setArg(4, ve); call->setArg(5, vf); call->setArg(6, vg); call->setArg(7, vh); call->setArg(8, vi); call->setArg(9, vj); call->setRet(0, va); cc.ret(va); cc.endFunc(); } virtual bool run(void* _func, StringBuilder& result, StringBuilder& expect) { typedef int (*Func)(void); Func func = ptr_as_func(_func); int resultRet = func(); int expectRet = calledFunc(0x03, 0x12, 0xA0, 0x0B, 0x2F, 0x02, 0x0C, 0x12, 0x18, 0x1E); result.setFormat("ret=%d", resultRet); expect.setFormat("ret=%d", expectRet); return resultRet == expectRet; } }; // ============================================================================ // [X86Test_CallDuplicateArgs] // ============================================================================ class X86Test_CallDuplicateArgs : public X86Test { public: X86Test_CallDuplicateArgs() : X86Test("[Call] Duplicate Args") {} static void add(X86TestManager& mgr) { mgr.add(new X86Test_CallDuplicateArgs()); } static int calledFunc(int a, int b, int c, int d, int e, int f, int g, int h, int i, int j) { return (a * b * c * d * e) + (f * g * h * i * j); } virtual void compile(X86Compiler& cc) { cc.addFunc(FuncSignature0(CallConv::kIdHost)); // Prepare. X86Gp fn = cc.newIntPtr("fn"); X86Gp a = cc.newInt32("a"); cc.mov(fn, imm_ptr(calledFunc)); cc.mov(a, 3); // Call function. CCFuncCall* call = cc.call(fn, FuncSignature10(CallConv::kIdHost)); call->setArg(0, a); call->setArg(1, a); call->setArg(2, a); call->setArg(3, a); call->setArg(4, a); call->setArg(5, a); call->setArg(6, a); call->setArg(7, a); call->setArg(8, a); call->setArg(9, a); call->setRet(0, a); cc.ret(a); cc.endFunc(); } virtual bool run(void* _func, StringBuilder& result, StringBuilder& expect) { typedef int (*Func)(void); Func func = ptr_as_func(_func); int resultRet = func(); int expectRet = calledFunc(3, 3, 3, 3, 3, 3, 3, 3, 3, 3); result.setFormat("ret=%d", resultRet); expect.setFormat("ret=%d", expectRet); return resultRet == expectRet; } }; // ============================================================================ // [X86Test_CallImmArgs] // ============================================================================ class X86Test_CallImmArgs : public X86Test { public: X86Test_CallImmArgs() : X86Test("[Call] Imm Args") {} static void add(X86TestManager& mgr) { mgr.add(new X86Test_CallImmArgs()); } virtual void compile(X86Compiler& cc) { cc.addFunc(FuncSignature0(CallConv::kIdHost)); // Prepare. X86Gp fn = cc.newIntPtr("fn"); X86Gp rv = cc.newInt32("rv"); cc.mov(fn, imm_ptr(X86Test_CallManyArgs::calledFunc)); // Call function. CCFuncCall* call = cc.call(fn, FuncSignature10(CallConv::kIdHost)); call->setArg(0, imm(0x03)); call->setArg(1, imm(0x12)); call->setArg(2, imm(0xA0)); call->setArg(3, imm(0x0B)); call->setArg(4, imm(0x2F)); call->setArg(5, imm(0x02)); call->setArg(6, imm(0x0C)); call->setArg(7, imm(0x12)); call->setArg(8, imm(0x18)); call->setArg(9, imm(0x1E)); call->setRet(0, rv); cc.ret(rv); cc.endFunc(); } virtual bool run(void* _func, StringBuilder& result, StringBuilder& expect) { typedef int (*Func)(void); Func func = ptr_as_func(_func); int resultRet = func(); int expectRet = X86Test_CallManyArgs::calledFunc(0x03, 0x12, 0xA0, 0x0B, 0x2F, 0x02, 0x0C, 0x12, 0x18, 0x1E); result.setFormat("ret=%d", resultRet); expect.setFormat("ret=%d", expectRet); return resultRet == expectRet; } }; // ============================================================================ // [X86Test_CallPtrArgs] // ============================================================================ class X86Test_CallPtrArgs : public X86Test { public: X86Test_CallPtrArgs() : X86Test("[Call] Ptr Args") {} static void add(X86TestManager& mgr) { mgr.add(new X86Test_CallPtrArgs()); } static int calledFunc(void* a, void* b, void* c, void* d, void* e, void* f, void* g, void* h, void* i, void* j) { return static_cast((intptr_t)a) + static_cast((intptr_t)b) + static_cast((intptr_t)c) + static_cast((intptr_t)d) + static_cast((intptr_t)e) + static_cast((intptr_t)f) + static_cast((intptr_t)g) + static_cast((intptr_t)h) + static_cast((intptr_t)i) + static_cast((intptr_t)j) ; } virtual void compile(X86Compiler& cc) { cc.addFunc(FuncSignature0(CallConv::kIdHost)); // Prepare. X86Gp fn = cc.newIntPtr("fn"); X86Gp rv = cc.newInt32("rv"); cc.mov(fn, imm_ptr(calledFunc)); // Call function. CCFuncCall* call = cc.call(fn, FuncSignature10(CallConv::kIdHost)); call->setArg(0, imm(0x01)); call->setArg(1, imm(0x02)); call->setArg(2, imm(0x03)); call->setArg(3, imm(0x04)); call->setArg(4, imm(0x05)); call->setArg(5, imm(0x06)); call->setArg(6, imm(0x07)); call->setArg(7, imm(0x08)); call->setArg(8, imm(0x09)); call->setArg(9, imm(0x0A)); call->setRet(0, rv); cc.ret(rv); cc.endFunc(); } virtual bool run(void* _func, StringBuilder& result, StringBuilder& expect) { typedef int (*Func)(void); Func func = ptr_as_func(_func); int resultRet = func(); int expectRet = 55; result.setFormat("ret=%d", resultRet); expect.setFormat("ret=%d", expectRet); return resultRet == expectRet; } }; // ============================================================================ // [X86Test_CallFloatAsXmmRet] // ============================================================================ class X86Test_CallFloatAsXmmRet : public X86Test { public: X86Test_CallFloatAsXmmRet() : X86Test("[Call] Float As Xmm Ret") {} static void add(X86TestManager& mgr) { mgr.add(new X86Test_CallFloatAsXmmRet()); } static float calledFunc(float a, float b) { return a * b; } virtual void compile(X86Compiler& cc) { cc.addFunc(FuncSignature2(CallConv::kIdHost)); X86Xmm a = cc.newXmmSs("a"); X86Xmm b = cc.newXmmSs("b"); X86Xmm ret = cc.newXmmSs("ret"); cc.setArg(0, a); cc.setArg(1, b); // Prepare. X86Gp fn = cc.newIntPtr("fn"); cc.mov(fn, imm_ptr(calledFunc)); // Call function. CCFuncCall* call = cc.call(fn, FuncSignature2(CallConv::kIdHost)); call->setArg(0, a); call->setArg(1, b); call->setRet(0, ret); cc.ret(ret); cc.endFunc(); } virtual bool run(void* _func, StringBuilder& result, StringBuilder& expect) { typedef float (*Func)(float, float); Func func = ptr_as_func(_func); float resultRet = func(15.5f, 2.0f); float expectRet = calledFunc(15.5f, 2.0f); result.setFormat("ret=%g", resultRet); expect.setFormat("ret=%g", expectRet); return resultRet == expectRet; } }; // ============================================================================ // [X86Test_CallDoubleAsXmmRet] // ============================================================================ class X86Test_CallDoubleAsXmmRet : public X86Test { public: X86Test_CallDoubleAsXmmRet() : X86Test("[Call] Double As Xmm Ret") {} static void add(X86TestManager& mgr) { mgr.add(new X86Test_CallDoubleAsXmmRet()); } static double calledFunc(double a, double b) { return a * b; } virtual void compile(X86Compiler& cc) { cc.addFunc(FuncSignature2(CallConv::kIdHost)); X86Xmm a = cc.newXmmSd("a"); X86Xmm b = cc.newXmmSd("b"); X86Xmm ret = cc.newXmmSd("ret"); cc.setArg(0, a); cc.setArg(1, b); X86Gp fn = cc.newIntPtr("fn"); cc.mov(fn, imm_ptr(calledFunc)); CCFuncCall* call = cc.call(fn, FuncSignature2(CallConv::kIdHost)); call->setArg(0, a); call->setArg(1, b); call->setRet(0, ret); cc.ret(ret); cc.endFunc(); } virtual bool run(void* _func, StringBuilder& result, StringBuilder& expect) { typedef double (*Func)(double, double); Func func = ptr_as_func(_func); double resultRet = func(15.5, 2.0); double expectRet = calledFunc(15.5, 2.0); result.setFormat("ret=%g", resultRet); expect.setFormat("ret=%g", expectRet); return resultRet == expectRet; } }; // ============================================================================ // [X86Test_CallConditional] // ============================================================================ class X86Test_CallConditional : public X86Test { public: X86Test_CallConditional() : X86Test("[Call] Conditional") {} static void add(X86TestManager& mgr) { mgr.add(new X86Test_CallConditional()); } virtual void compile(X86Compiler& cc) { X86Gp x = cc.newInt32("x"); X86Gp y = cc.newInt32("y"); X86Gp op = cc.newInt32("op"); CCFuncCall* call; X86Gp result; cc.addFunc(FuncSignature3(CallConv::kIdHost)); cc.setArg(0, x); cc.setArg(1, y); cc.setArg(2, op); Label opAdd = cc.newLabel(); Label opMul = cc.newLabel(); cc.cmp(op, 0); cc.jz(opAdd); cc.cmp(op, 1); cc.jz(opMul); result = cc.newInt32("result_0"); cc.mov(result, 0); cc.ret(result); cc.bind(opAdd); result = cc.newInt32("result_1"); call = cc.call((uint64_t)calledFuncAdd, FuncSignature2(CallConv::kIdHost)); call->setArg(0, x); call->setArg(1, y); call->setRet(0, result); cc.ret(result); cc.bind(opMul); result = cc.newInt32("result_2"); call = cc.call((uint64_t)calledFuncMul, FuncSignature2(CallConv::kIdHost)); call->setArg(0, x); call->setArg(1, y); call->setRet(0, result); cc.ret(result); cc.endFunc(); } virtual bool run(void* _func, StringBuilder& result, StringBuilder& expect) { typedef int (*Func)(int, int, int); Func func = ptr_as_func(_func); int arg1 = 4; int arg2 = 8; int resultAdd = func(arg1, arg2, 0); int expectAdd = calledFuncAdd(arg1, arg2); int resultMul = func(arg1, arg2, 1); int expectMul = calledFuncMul(arg1, arg2); result.setFormat("ret={add=%d, mul=%d}", resultAdd, resultMul); expect.setFormat("ret={add=%d, mul=%d}", expectAdd, expectMul); return (resultAdd == expectAdd) && (resultMul == expectMul); } static int calledFuncAdd(int x, int y) { return x + y; } static int calledFuncMul(int x, int y) { return x * y; } }; // ============================================================================ // [X86Test_CallMultiple] // ============================================================================ class X86Test_CallMultiple : public X86Test { public: X86Test_CallMultiple() : X86Test("[Call] Multiple") {} static void add(X86TestManager& mgr) { mgr.add(new X86Test_CallMultiple()); } static int ASMJIT_FASTCALL calledFunc(int* pInt, int index) { return pInt[index]; } virtual void compile(X86Compiler& cc) { unsigned int i; X86Gp buf = cc.newIntPtr("buf"); X86Gp acc0 = cc.newInt32("acc0"); X86Gp acc1 = cc.newInt32("acc1"); cc.addFunc(FuncSignature1(CallConv::kIdHost)); cc.setArg(0, buf); cc.mov(acc0, 0); cc.mov(acc1, 0); for (i = 0; i < 4; i++) { X86Gp ret = cc.newInt32("ret"); X86Gp ptr = cc.newIntPtr("ptr"); X86Gp idx = cc.newInt32("idx"); CCFuncCall* call; cc.mov(ptr, buf); cc.mov(idx, static_cast(i)); call = cc.call((uint64_t)calledFunc, FuncSignature2(CallConv::kIdHostFastCall)); call->setArg(0, ptr); call->setArg(1, idx); call->setRet(0, ret); cc.add(acc0, ret); cc.mov(ptr, buf); cc.mov(idx, static_cast(i)); call = cc.call((uint64_t)calledFunc, FuncSignature2(CallConv::kIdHostFastCall)); call->setArg(0, ptr); call->setArg(1, idx); call->setRet(0, ret); cc.sub(acc1, ret); } cc.add(acc0, acc1); cc.ret(acc0); cc.endFunc(); } virtual bool run(void* _func, StringBuilder& result, StringBuilder& expect) { typedef int (*Func)(int*); Func func = ptr_as_func(_func); int buffer[4] = { 127, 87, 23, 17 }; int resultRet = func(buffer); int expectRet = 0; result.setFormat("ret=%d", resultRet); expect.setFormat("ret=%d", expectRet); return resultRet == expectRet; } }; // ============================================================================ // [X86Test_CallRecursive] // ============================================================================ class X86Test_CallRecursive : public X86Test { public: X86Test_CallRecursive() : X86Test("[Call] Recursive") {} static void add(X86TestManager& mgr) { mgr.add(new X86Test_CallRecursive()); } virtual void compile(X86Compiler& cc) { X86Gp val = cc.newInt32("val"); Label skip = cc.newLabel(); CCFunc* func = cc.addFunc(FuncSignature1(CallConv::kIdHost)); cc.setArg(0, val); cc.cmp(val, 1); cc.jle(skip); X86Gp tmp = cc.newInt32("tmp"); cc.mov(tmp, val); cc.dec(tmp); CCFuncCall* call = cc.call(func->getLabel(), FuncSignature1(CallConv::kIdHost)); call->setArg(0, tmp); call->setRet(0, tmp); cc.mul(cc.newInt32(), val, tmp); cc.bind(skip); cc.ret(val); cc.endFunc(); } virtual bool run(void* _func, StringBuilder& result, StringBuilder& expect) { typedef int (*Func)(int); Func func = ptr_as_func(_func); int resultRet = func(5); int expectRet = 1 * 2 * 3 * 4 * 5; result.setFormat("ret=%d", resultRet); expect.setFormat("ret=%d", expectRet); return resultRet == expectRet; } }; // ============================================================================ // [X86Test_CallMisc1] // ============================================================================ class X86Test_CallMisc1 : public X86Test { public: X86Test_CallMisc1() : X86Test("[Call] Misc #1") {} static void add(X86TestManager& mgr) { mgr.add(new X86Test_CallMisc1()); } static void dummy(int a, int b) {} virtual void compile(X86Compiler& cc) { X86Gp val = cc.newInt32("val"); Label skip = cc.newLabel(); CCFunc* func = cc.addFunc(FuncSignature2(CallConv::kIdHost)); X86Gp a = cc.newInt32("a"); X86Gp b = cc.newInt32("b"); X86Gp r = cc.newInt32("r"); cc.setArg(0, a); cc.setArg(1, b); cc.alloc(a, x86::eax); cc.alloc(b, x86::ebx); CCFuncCall* call = cc.call(imm_ptr(dummy), FuncSignature2(CallConv::kIdHost)); call->setArg(0, a); call->setArg(1, b); cc.lea(r, x86::ptr(a, b)); cc.ret(r); cc.endFunc(); } virtual bool run(void* _func, StringBuilder& result, StringBuilder& expect) { typedef int (*Func)(int, int); Func func = ptr_as_func(_func); int resultRet = func(44, 199); int expectRet = 243; result.setFormat("ret=%d", resultRet); expect.setFormat("ret=%d", expectRet); return resultRet == expectRet; } }; // ============================================================================ // [X86Test_CallMisc2] // ============================================================================ class X86Test_CallMisc2 : public X86Test { public: X86Test_CallMisc2() : X86Test("[Call] Misc #2") {} static void add(X86TestManager& mgr) { mgr.add(new X86Test_CallMisc2()); } virtual void compile(X86Compiler& cc) { CCFunc* func = cc.addFunc(FuncSignature1(CallConv::kIdHost)); X86Gp p = cc.newIntPtr("p"); X86Gp fn = cc.newIntPtr("fn"); X86Xmm arg = cc.newXmmSd("arg"); X86Xmm ret = cc.newXmmSd("ret"); cc.setArg(0, p); cc.movsd(arg, x86::ptr(p)); cc.mov(fn, imm_ptr(op)); CCFuncCall* call = cc.call(fn, FuncSignature1(CallConv::kIdHost)); call->setArg(0, arg); call->setRet(0, ret); cc.ret(ret); cc.endFunc(); } virtual bool run(void* _func, StringBuilder& result, StringBuilder& expect) { typedef double (*Func)(const double*); Func func = ptr_as_func(_func); double arg = 2; double resultRet = func(&arg); double expectRet = op(arg); result.setFormat("ret=%g", resultRet); expect.setFormat("ret=%g", expectRet); return resultRet == expectRet; } static double op(double a) { return a * a; } }; // ============================================================================ // [X86Test_CallMisc3] // ============================================================================ class X86Test_CallMisc3 : public X86Test { public: X86Test_CallMisc3() : X86Test("[Call] Misc #3") {} static void add(X86TestManager& mgr) { mgr.add(new X86Test_CallMisc3()); } virtual void compile(X86Compiler& cc) { CCFunc* func = cc.addFunc(FuncSignature1(CallConv::kIdHost)); X86Gp p = cc.newIntPtr("p"); X86Gp fn = cc.newIntPtr("fn"); X86Xmm arg = cc.newXmmSd("arg"); X86Xmm ret = cc.newXmmSd("ret"); cc.setArg(0, p); cc.movsd(arg, x86::ptr(p)); cc.mov(fn, imm_ptr(op)); CCFuncCall* call = cc.call(fn, FuncSignature1(CallConv::kIdHost)); call->setArg(0, arg); call->setRet(0, ret); cc.xorps(arg, arg); cc.subsd(arg, ret); cc.ret(arg); cc.endFunc(); } virtual bool run(void* _func, StringBuilder& result, StringBuilder& expect) { typedef double (*Func)(const double*); Func func = ptr_as_func(_func); double arg = 2; double resultRet = func(&arg); double expectRet = -op(arg); result.setFormat("ret=%g", resultRet); expect.setFormat("ret=%g", expectRet); return resultRet == expectRet; } static double op(double a) { return a * a; } }; // ============================================================================ // [X86Test_CallMisc4] // ============================================================================ class X86Test_CallMisc4 : public X86Test { public: X86Test_CallMisc4() : X86Test("[Call] Misc #4") {} static void add(X86TestManager& mgr) { mgr.add(new X86Test_CallMisc4()); } virtual void compile(X86Compiler& cc) { FuncSignatureX funcPrototype; funcPrototype.setCallConv(CallConv::kIdHost); funcPrototype.setRet(TypeId::kF64); CCFunc* func = cc.addFunc(funcPrototype); FuncSignatureX callPrototype; callPrototype.setCallConv(CallConv::kIdHost); callPrototype.setRet(TypeId::kF64); CCFuncCall* call = cc.call(imm_ptr(calledFunc), callPrototype); X86Xmm ret = cc.newXmmSd("ret"); call->setRet(0, ret); cc.ret(ret); cc.endFunc(); } virtual bool run(void* _func, StringBuilder& result, StringBuilder& expect) { typedef double (*Func)(void); Func func = ptr_as_func(_func); double resultRet = func(); double expectRet = 3.14; result.setFormat("ret=%g", resultRet); expect.setFormat("ret=%g", expectRet); return resultRet == expectRet; } static double calledFunc() { return 3.14; } }; // ============================================================================ // [X86Test_CallMisc5] // ============================================================================ // The register allocator should clobber the register used by the `call` itself. class X86Test_CallMisc5 : public X86Test { public: X86Test_CallMisc5() : X86Test("[Call] Misc #5") {} static void add(X86TestManager& mgr) { mgr.add(new X86Test_CallMisc5()); } virtual void compile(X86Compiler& cc) { CCFunc* func = cc.addFunc(FuncSignature0(CallConv::kIdHost)); X86Gp pFn = cc.newIntPtr("pFn"); X86Gp vars[16]; uint32_t i, regCount = cc.getGpCount(); ASMJIT_ASSERT(regCount <= ASMJIT_ARRAY_SIZE(vars)); cc.mov(pFn, imm_ptr(calledFunc)); cc.spill(pFn); for (i = 0; i < regCount; i++) { if (i == X86Gp::kIdBp || i == X86Gp::kIdSp) continue; vars[i] = cc.newInt32("v%u", static_cast(i)); cc.alloc(vars[i], i); cc.mov(vars[i], 1); } CCFuncCall* call = cc.call(pFn, FuncSignature0(CallConv::kIdHost)); for (i = 1; i < regCount; i++) { if (vars[i].isValid()) cc.add(vars[0], vars[i]); } cc.ret(vars[0]); cc.endFunc(); } virtual bool run(void* _func, StringBuilder& result, StringBuilder& expect) { typedef int (*Func)(void); Func func = ptr_as_func(_func); int resultRet = func(); int expectRet = sizeof(void*) == 4 ? 6 : 14; result.setFormat("ret=%d", resultRet); expect.setFormat("ret=%d", expectRet); return resultRet == expectRet; } static void calledFunc() {} }; // ============================================================================ // [X86Test_MiscConstPool] // ============================================================================ class X86Test_MiscConstPool : public X86Test { public: X86Test_MiscConstPool() : X86Test("[Misc] ConstPool #1") {} static void add(X86TestManager& mgr) { mgr.add(new X86Test_MiscConstPool()); } virtual void compile(X86Compiler& cc) { cc.addFunc(FuncSignature0(CallConv::kIdHost)); X86Gp v0 = cc.newInt32("v0"); X86Gp v1 = cc.newInt32("v1"); X86Mem c0 = cc.newInt32Const(kConstScopeLocal, 200); X86Mem c1 = cc.newInt32Const(kConstScopeLocal, 33); cc.mov(v0, c0); cc.mov(v1, c1); cc.add(v0, v1); cc.ret(v0); cc.endFunc(); } virtual bool run(void* _func, StringBuilder& result, StringBuilder& expect) { typedef int (*Func)(void); Func func = ptr_as_func(_func); int resultRet = func(); int expectRet = 233; result.setFormat("ret=%d", resultRet); expect.setFormat("ret=%d", expectRet); return resultRet == expectRet; } }; // ============================================================================ // [X86Test_MiscMultiRet] // ============================================================================ struct X86Test_MiscMultiRet : public X86Test { X86Test_MiscMultiRet() : X86Test("[Misc] MultiRet") {} static void add(X86TestManager& mgr) { mgr.add(new X86Test_MiscMultiRet()); } virtual void compile(X86Compiler& cc) { cc.addFunc(FuncSignature3(CallConv::kIdHost)); X86Gp op = cc.newInt32("op"); X86Gp a = cc.newInt32("a"); X86Gp b = cc.newInt32("b"); Label L_Zero = cc.newLabel(); Label L_Add = cc.newLabel(); Label L_Sub = cc.newLabel(); Label L_Mul = cc.newLabel(); Label L_Div = cc.newLabel(); cc.setArg(0, op); cc.setArg(1, a); cc.setArg(2, b); cc.cmp(op, 0); cc.jz(L_Add); cc.cmp(op, 1); cc.jz(L_Sub); cc.cmp(op, 2); cc.jz(L_Mul); cc.cmp(op, 3); cc.jz(L_Div); cc.bind(L_Zero); cc.xor_(a, a); cc.ret(a); cc.bind(L_Add); cc.add(a, b); cc.ret(a); cc.bind(L_Sub); cc.sub(a, b); cc.ret(a); cc.bind(L_Mul); cc.imul(a, b); cc.ret(a); cc.bind(L_Div); cc.cmp(b, 0); cc.jz(L_Zero); X86Gp zero = cc.newInt32("zero"); cc.xor_(zero, zero); cc.idiv(zero, a, b); cc.ret(a); cc.endFunc(); } virtual bool run(void* _func, StringBuilder& result, StringBuilder& expect) { typedef int (*Func)(int, int, int); Func func = ptr_as_func(_func); int a = 44; int b = 3; int r0 = func(0, a, b); int r1 = func(1, a, b); int r2 = func(2, a, b); int r3 = func(3, a, b); int e0 = a + b; int e1 = a - b; int e2 = a * b; int e3 = a / b; result.setFormat("ret={%d %d %d %d}", r0, r1, r2, r3); expect.setFormat("ret={%d %d %d %d}", e0, e1, e2, e3); return result.eq(expect); } }; // ============================================================================ // [X86Test_MiscMultiFunc] // ============================================================================ class X86Test_MiscMultiFunc : public X86Test { public: X86Test_MiscMultiFunc() : X86Test("[Misc] MultiFunc") {} static void add(X86TestManager& mgr) { mgr.add(new X86Test_MiscMultiFunc()); } virtual void compile(X86Compiler& cc) { CCFunc* f1 = cc.newFunc(FuncSignature2(CallConv::kIdHost)); CCFunc* f2 = cc.newFunc(FuncSignature2(CallConv::kIdHost)); { X86Gp a = cc.newInt32("a"); X86Gp b = cc.newInt32("b"); cc.addFunc(f1); cc.setArg(0, a); cc.setArg(1, b); CCFuncCall* call = cc.call(f2->getLabel(), FuncSignature2(CallConv::kIdHost)); call->setArg(0, a); call->setArg(1, b); call->setRet(0, a); cc.ret(a); cc.endFunc(); } { X86Gp a = cc.newInt32("a"); X86Gp b = cc.newInt32("b"); cc.addFunc(f2); cc.setArg(0, a); cc.setArg(1, b); cc.add(a, b); cc.ret(a); cc.endFunc(); } } virtual bool run(void* _func, StringBuilder& result, StringBuilder& expect) { typedef int (*Func)(int, int); Func func = ptr_as_func(_func); int resultRet = func(56, 22); int expectRet = 56 + 22; result.setFormat("ret=%d", resultRet); expect.setFormat("ret=%d", expectRet); return result.eq(expect); } }; // ============================================================================ // [X86Test_MiscFastEval] // ============================================================================ class X86Test_MiscFastEval : public X86Test { public: X86Test_MiscFastEval() : X86Test("[Misc] FastEval (CConv)") {} static void add(X86TestManager& mgr) { mgr.add(new X86Test_MiscFastEval()); } virtual void compile(X86Compiler& cc) { FuncSignature5 funcSig(CallConv::kIdHostCDecl); FuncSignature2 fastSig(CallConv::kIdHostFastEval2); CCFunc* func = cc.newFunc(funcSig); CCFunc* fast = cc.newFunc(fastSig); { X86Gp aPtr = cc.newIntPtr("aPtr"); X86Gp bPtr = cc.newIntPtr("bPtr"); X86Gp cPtr = cc.newIntPtr("cPtr"); X86Gp dPtr = cc.newIntPtr("dPtr"); X86Gp pOut = cc.newIntPtr("pOut"); X86Xmm aXmm = cc.newXmm("aXmm"); X86Xmm bXmm = cc.newXmm("bXmm"); X86Xmm cXmm = cc.newXmm("cXmm"); X86Xmm dXmm = cc.newXmm("dXmm"); cc.addFunc(func); cc.setArg(0, aPtr); cc.setArg(1, bPtr); cc.setArg(2, cPtr); cc.setArg(3, dPtr); cc.setArg(4, pOut); cc.movups(aXmm, x86::ptr(aPtr)); cc.movups(bXmm, x86::ptr(bPtr)); cc.movups(cXmm, x86::ptr(cPtr)); cc.movups(dXmm, x86::ptr(dPtr)); X86Xmm xXmm = cc.newXmm("xXmm"); X86Xmm yXmm = cc.newXmm("yXmm"); CCFuncCall* call1 = cc.call(fast->getLabel(), fastSig); call1->setArg(0, aXmm); call1->setArg(1, bXmm); call1->setRet(0, xXmm); CCFuncCall* call2 = cc.call(fast->getLabel(), fastSig); call2->setArg(0, cXmm); call2->setArg(1, dXmm); call2->setRet(0, yXmm); cc.pmullw(xXmm, yXmm); cc.movups(x86::ptr(pOut), xXmm); cc.endFunc(); } { X86Xmm aXmm = cc.newXmm("aXmm"); X86Xmm bXmm = cc.newXmm("bXmm"); cc.addFunc(fast); cc.setArg(0, aXmm); cc.setArg(1, bXmm); cc.paddw(aXmm, bXmm); cc.ret(aXmm); cc.endFunc(); } } virtual bool run(void* _func, StringBuilder& result, StringBuilder& expect) { typedef void (*Func)(const void*, const void*, const void*, const void*, void*); Func func = ptr_as_func(_func); int16_t a[8] = { 0, 1, 2, 3, 4, 5, 6, 7 }; int16_t b[8] = { 7, 6, 5, 4, 3, 2, 1, 0 }; int16_t c[8] = { 1, 3, 9, 7, 5, 4, 2, 1 }; int16_t d[8] = { 2, 0,-6,-4,-2,-1, 1, 2 }; int16_t o[8]; int oExp = 7 * 3; func(a, b, c, d, o); result.setFormat("ret={%02X %02X %02X %02X %02X %02X %02X %02X}", o[0], o[1], o[2], o[3], o[4], o[5], o[6], o[7]); expect.setFormat("ret={%02X %02X %02X %02X %02X %02X %02X %02X}", oExp, oExp, oExp, oExp, oExp, oExp, oExp, oExp); return result == expect; } }; // ============================================================================ // [X86Test_MiscUnfollow] // ============================================================================ // Global (I didn't find a better way to test this). static jmp_buf globalJmpBuf; class X86Test_MiscUnfollow : public X86Test { public: X86Test_MiscUnfollow() : X86Test("[Misc] Unfollow") {} static void add(X86TestManager& mgr) { mgr.add(new X86Test_MiscUnfollow()); } virtual void compile(X86Compiler& cc) { // NOTE: Fastcall calling convention is the most appropriate here, as all // arguments will be passed by registers and there won't be any stack // misalignment when we call the `handler()`. This was failing on OSX // when targeting 32-bit. cc.addFunc(FuncSignature2(CallConv::kIdHostFastCall)); X86Gp a = cc.newInt32("a"); X86Gp b = cc.newIntPtr("b"); Label tramp = cc.newLabel(); cc.setArg(0, a); cc.setArg(1, b); cc.cmp(a, 0); cc.jz(tramp); cc.ret(a); cc.bind(tramp); cc.unfollow().jmp(b); cc.endFunc(); } virtual bool run(void* _func, StringBuilder& result, StringBuilder& expect) { typedef int (ASMJIT_FASTCALL *Func)(int, void*); Func func = ptr_as_func(_func); int resultRet = 0; int expectRet = 1; if (!setjmp(globalJmpBuf)) resultRet = func(0, (void*)handler); else resultRet = 1; result.setFormat("ret={%d}", resultRet); expect.setFormat("ret={%d}", expectRet); return resultRet == expectRet; } static void ASMJIT_FASTCALL handler() { longjmp(globalJmpBuf, 1); } }; // ============================================================================ // [X86Test_Bug100] // ============================================================================ class X86Test_Bug100 : public X86Test { public: X86Test_Bug100() : X86Test("[Alloc] Bug#100") {} static void add(X86TestManager& mgr) { mgr.add(new X86Test_Bug100()); } virtual void compile(X86Compiler& cc) { cc.addFunc(FuncSignature4(CallConv::kIdHost)); Label L2 = cc.newLabel(); Label L3 = cc.newLabel(); Label L4 = cc.newLabel(); X86Gp dst = cc.newIntPtr("dst"); X86Gp v0 = cc.newU32("v0"); X86Gp v1 = cc.newU32("v1"); X86Gp v2 = cc.newU32("v2"); cc.setArg(0, dst); cc.setArg(1, v0); cc.setArg(2, v1); cc.setArg(3, v2); cc.cmp(v0, 65535); cc.jne(L2); cc.cmp(v0, v1); cc.je(L3); cc.mov(v0, v2); cc.jmp(cc.getFunc()->getExitLabel()); cc.bind(L3); cc.bind(L4); cc.mov(v2, v1); cc.cmp(v1, 65535); cc.jne(L2); cc.mov(v0, 128); cc.bind(L2); cc.mov(x86::ptr(dst), v0); cc.endFunc(); } virtual bool run(void* _func, StringBuilder& result, StringBuilder& expect) { // TODO: This test is not complete. // typedef void (*Func)(void*, const void*, size_t); // Func func = ptr_as_func(_func); return result == expect; } }; // ============================================================================ // [CmdLine] // ============================================================================ class CmdLine { public: CmdLine(int argc, char** argv) : _argc(argc), _argv(argv) {} bool hasArg(const char* arg) { for (int i = 1; i < _argc; i++) { if (::strcmp(_argv[i], arg) == 0) return true; } return false; } // -------------------------------------------------------------------------- // [Members] // -------------------------------------------------------------------------- int _argc; char** _argv; }; // ============================================================================ // [Main] // ============================================================================ #define ADD_TEST(CLASS) CLASS::add(testMgr) int main(int argc, char* argv[]) { X86TestManager testMgr; CmdLine cmd(argc, argv); if (cmd.hasArg("--verbose")) testMgr._verbose = true; // Align. ADD_TEST(X86Test_AlignBase); ADD_TEST(X86Test_AlignNone); // Jump. ADD_TEST(X86Test_JumpCross); ADD_TEST(X86Test_JumpMany); ADD_TEST(X86Test_JumpUnreachable1); ADD_TEST(X86Test_JumpUnreachable2); // Alloc. ADD_TEST(X86Test_AllocBase); ADD_TEST(X86Test_AllocManual); ADD_TEST(X86Test_AllocUseMem); ADD_TEST(X86Test_AllocMany1); ADD_TEST(X86Test_AllocMany2); ADD_TEST(X86Test_AllocImul1); ADD_TEST(X86Test_AllocImul2); ADD_TEST(X86Test_AllocIdiv1); ADD_TEST(X86Test_AllocSetz); ADD_TEST(X86Test_AllocShlRor); ADD_TEST(X86Test_AllocGpLo); ADD_TEST(X86Test_AllocRepMovsb); ADD_TEST(X86Test_AllocIfElse1); ADD_TEST(X86Test_AllocIfElse2); ADD_TEST(X86Test_AllocIfElse3); ADD_TEST(X86Test_AllocIfElse4); ADD_TEST(X86Test_AllocInt8); ADD_TEST(X86Test_AllocArgsIntPtr); ADD_TEST(X86Test_AllocArgsFloat); ADD_TEST(X86Test_AllocArgsDouble); ADD_TEST(X86Test_AllocRetFloat); ADD_TEST(X86Test_AllocRetDouble); ADD_TEST(X86Test_AllocStack1); ADD_TEST(X86Test_AllocStack2); ADD_TEST(X86Test_AllocMemcpy); ADD_TEST(X86Test_AllocAlphaBlend); // Call. ADD_TEST(X86Test_CallBase); ADD_TEST(X86Test_CallFast); ADD_TEST(X86Test_CallManyArgs); ADD_TEST(X86Test_CallDuplicateArgs); ADD_TEST(X86Test_CallImmArgs); ADD_TEST(X86Test_CallPtrArgs); ADD_TEST(X86Test_CallFloatAsXmmRet); ADD_TEST(X86Test_CallDoubleAsXmmRet); ADD_TEST(X86Test_CallConditional); ADD_TEST(X86Test_CallMultiple); ADD_TEST(X86Test_CallRecursive); ADD_TEST(X86Test_CallMisc1); ADD_TEST(X86Test_CallMisc2); ADD_TEST(X86Test_CallMisc3); ADD_TEST(X86Test_CallMisc4); ADD_TEST(X86Test_CallMisc5); // Misc. ADD_TEST(X86Test_MiscConstPool); ADD_TEST(X86Test_MiscMultiRet); ADD_TEST(X86Test_MiscMultiFunc); ADD_TEST(X86Test_MiscFastEval); ADD_TEST(X86Test_MiscUnfollow); // Bugs. ADD_TEST(X86Test_Bug100); return testMgr.run(); }