summaryrefslogtreecommitdiff
path: root/lib/AsmJit/CompilerX86X64.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/AsmJit/CompilerX86X64.cpp')
-rw-r--r--lib/AsmJit/CompilerX86X64.cpp7812
1 files changed, 7812 insertions, 0 deletions
diff --git a/lib/AsmJit/CompilerX86X64.cpp b/lib/AsmJit/CompilerX86X64.cpp
new file mode 100644
index 0000000..b3f9e28
--- /dev/null
+++ b/lib/AsmJit/CompilerX86X64.cpp
@@ -0,0 +1,7812 @@
+// AsmJit - Complete JIT Assembler for C++ Language.
+
+// Copyright (c) 2008-2010, Petr Kobalicek <kobalicek.petr@gmail.com>
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+
+// We are using sprintf() here.
+#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
+#define _CRT_SECURE_NO_WARNINGS
+#endif // _MSC_VER
+
+// [Dependencies]
+#include "Assembler.h"
+#include "CodeGenerator.h"
+#include "Compiler.h"
+#include "CpuInfo.h"
+#include "Logger.h"
+#include "Util_p.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+// [Api-Begin]
+#include "ApiBegin.h"
+
+namespace AsmJit {
+
+// ============================================================================
+// [Helpers - Logging]
+// ============================================================================
+
+// Defined in AssemblerX86X64.cpp.
+ASMJIT_HIDDEN char* dumpRegister(char* buf, uint32_t type, uint32_t index) ASMJIT_NOTHROW;
+ASMJIT_HIDDEN char* dumpOperand(char* buf, const Operand* op) ASMJIT_NOTHROW;
+
+// ============================================================================
+// [Helpers - Variables]
+// ============================================================================
+
+struct VariableInfo
+{
+ enum CLASS_INFO
+ {
+ CLASS_NONE = 0x00,
+ CLASS_GP = 0x01,
+ CLASS_X87 = 0x02,
+ CLASS_MM = 0x04,
+ CLASS_XMM = 0x08,
+ };
+
+ enum FLAGS
+ {
+ FLAG_SP_FP = 0x10,
+ FLAG_DP_FP = 0x20,
+ FLAG_VECTOR = 0x40
+ };
+
+ uint32_t code;
+ uint8_t size;
+ uint8_t clazz;
+ uint8_t flags;
+ uint8_t reserved_0;
+ char name[8];
+};
+
+#define C(c) VariableInfo::CLASS_##c
+#define F(f) VariableInfo::FLAG_##f
+static const VariableInfo variableInfo[] =
+{
+ /* 0 */ { REG_TYPE_GPD , 4 , C(GP) , 0 , 0, "GP.D" },
+ /* 1 */ { REG_TYPE_GPQ , 8 , C(GP) , 0 , 0, "GP.Q" },
+ /* 2 */ { REG_TYPE_X87 , 4 , C(X87), F(SP_FP) , 0, "X87" },
+ /* 3 */ { REG_TYPE_X87 , 4 , C(X87), F(SP_FP) , 0, "X87.1F" },
+ /* 4 */ { REG_TYPE_X87 , 8 , C(X87), F(DP_FP) , 0, "X87.1D" },
+ /* 5 */ { REG_TYPE_MM , 8 , C(MM) , F(VECTOR), 0, "MM" },
+ /* 6 */ { REG_TYPE_XMM , 16, C(XMM), 0 , 0, "XMM" },
+ /* 7 */ { REG_TYPE_XMM , 4 , C(XMM), F(SP_FP) , 0, "XMM.1F" },
+ /* 8 */ { REG_TYPE_XMM , 8 , C(XMM), F(DP_FP) , 0, "XMM.1D" },
+ /* 9 */ { REG_TYPE_XMM , 16, C(XMM), F(SP_FP) | F(VECTOR), 0, "XMM.4F" },
+ /* 10 */ { REG_TYPE_XMM , 16, C(XMM), F(DP_FP) | F(VECTOR), 0, "XMM.2D" }
+};
+#undef F
+#undef C
+
+static uint32_t getVariableClass(uint32_t type)
+{
+ ASMJIT_ASSERT(type < ASMJIT_ARRAY_SIZE(variableInfo));
+ return variableInfo[type].clazz;
+}
+
+static uint32_t getVariableSize(uint32_t type)
+{
+ ASMJIT_ASSERT(type < ASMJIT_ARRAY_SIZE(variableInfo));
+ return variableInfo[type].size;
+}
+
+static uint32_t getVariableRegisterCode(uint32_t type, uint32_t index)
+{
+ ASMJIT_ASSERT(type < ASMJIT_ARRAY_SIZE(variableInfo));
+ return variableInfo[type].code | index;
+}
+
+static bool isVariableInteger(uint32_t type)
+{
+ ASMJIT_ASSERT(type < ASMJIT_ARRAY_SIZE(variableInfo));
+ return (variableInfo[type].clazz & VariableInfo::CLASS_GP) != 0;
+}
+
+static bool isVariableFloat(uint32_t type)
+{
+ ASMJIT_ASSERT(type < ASMJIT_ARRAY_SIZE(variableInfo));
+ return (variableInfo[type].flags & (VariableInfo::FLAG_SP_FP | VariableInfo::FLAG_DP_FP)) != 0;
+}
+
+static GPVar GPVarFromData(VarData* vdata)
+{
+ GPVar var;
+ var._var.id = vdata->id;
+ var._var.size = vdata->size;
+ var._var.registerCode = variableInfo[vdata->type].code;
+ var._var.variableType = vdata->type;
+ return var;
+}
+
+static MMVar MMVarFromData(VarData* vdata)
+{
+ MMVar var;
+ var._var.id = vdata->id;
+ var._var.size = vdata->size;
+ var._var.registerCode = variableInfo[vdata->type].code;
+ var._var.variableType = vdata->type;
+ return var;
+}
+
+static XMMVar XMMVarFromData(VarData* vdata)
+{
+ XMMVar var;
+ var._var.id = vdata->id;
+ var._var.size = vdata->size;
+ var._var.registerCode = variableInfo[vdata->type].code;
+ var._var.variableType = vdata->type;
+ return var;
+}
+
+// ============================================================================
+// [Helpers - Emittables]
+// ============================================================================
+
+static void delAll(Emittable* first) ASMJIT_NOTHROW
+{
+ Emittable* cur = first;
+
+ while (cur)
+ {
+ Emittable* next = cur->getNext();
+ cur->~Emittable();
+ cur = next;
+ }
+}
+
+// ============================================================================
+// [Helpers - Compiler]
+// ============================================================================
+
+template<typename T>
+inline T* Compiler_newObject(CompilerCore* self) ASMJIT_NOTHROW
+{
+ void* addr = self->getZone().zalloc(sizeof(T));
+ return new(addr) T(reinterpret_cast<Compiler*>(self));
+}
+
+template<typename T, typename P1>
+inline T* Compiler_newObject(CompilerCore* self, P1 p1) ASMJIT_NOTHROW
+{
+ void* addr = self->getZone().zalloc(sizeof(T));
+ return new(addr) T(reinterpret_cast<Compiler*>(self), p1);
+}
+
+template<typename T, typename P1, typename P2>
+inline T* Compiler_newObject(CompilerCore* self, P1 p1, P2 p2) ASMJIT_NOTHROW
+{
+ void* addr = self->getZone().zalloc(sizeof(T));
+ return new(addr) T(reinterpret_cast<Compiler*>(self), p1, p2);
+}
+
+template<typename T, typename P1, typename P2, typename P3>
+inline T* Compiler_newObject(CompilerCore* self, P1 p1, P2 p2, P3 p3) ASMJIT_NOTHROW
+{
+ void* addr = self->getZone().zalloc(sizeof(T));
+ return new(addr) T(reinterpret_cast<Compiler*>(self), p1, p2, p3);
+}
+
+template<typename T, typename P1, typename P2, typename P3, typename P4>
+inline T* Compiler_newObject(CompilerCore* self, P1 p1, P2 p2, P3 p3, P4 p4) ASMJIT_NOTHROW
+{
+ void* addr = self->getZone().zalloc(sizeof(T));
+ return new(addr) T(reinterpret_cast<Compiler*>(self), p1, p2, p3, p4);
+}
+
+template<typename T, typename P1, typename P2, typename P3, typename P4, typename P5>
+inline T* Compiler_newObject(CompilerCore* self, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) ASMJIT_NOTHROW
+{
+ void* addr = self->getZone().zalloc(sizeof(T));
+ return new(addr) T(reinterpret_cast<Compiler*>(self), p1, p2, p3, p4, p5);
+}
+
+// ============================================================================
+// [AsmJit::FunctionPrototype]
+// ============================================================================
+
+FunctionPrototype::FunctionPrototype() ASMJIT_NOTHROW
+{
+ // Safe defaults.
+ _clear();
+}
+
+FunctionPrototype::~FunctionPrototype() ASMJIT_NOTHROW
+{
+}
+
+void FunctionPrototype::setPrototype(
+ uint32_t callingConvention,
+ const uint32_t* arguments,
+ uint32_t argumentsCount,
+ uint32_t returnValue) ASMJIT_NOTHROW
+{
+ _setCallingConvention(callingConvention);
+
+ if (argumentsCount > 32) argumentsCount = 32;
+ _setPrototype(arguments, argumentsCount, returnValue);
+}
+
+uint32_t FunctionPrototype::findArgumentByRegisterCode(uint32_t regCode) const ASMJIT_NOTHROW
+{
+ uint32_t type = regCode & REG_TYPE_MASK;
+ uint32_t idx = regCode & REG_INDEX_MASK;
+
+ uint32_t clazz;
+ uint32_t i;
+
+ switch (type)
+ {
+ case REG_TYPE_GPD:
+ case REG_TYPE_GPQ:
+ clazz = VariableInfo::CLASS_GP;
+ break;
+
+ case REG_TYPE_MM:
+ clazz = VariableInfo::CLASS_MM;
+ break;
+
+ case REG_TYPE_XMM:
+ clazz = VariableInfo::CLASS_XMM;
+ break;
+
+ default:
+ return INVALID_VALUE;
+ }
+
+ for (i = 0; i < _argumentsCount; i++)
+ {
+ const Argument& arg = _arguments[i];
+ if ((getVariableClass(arg.variableType) & clazz) != 0 && (arg.registerIndex == idx))
+ return i;
+ }
+
+ return INVALID_VALUE;
+}
+
+void FunctionPrototype::_clear() ASMJIT_NOTHROW
+{
+ _callingConvention = CALL_CONV_NONE;
+ _calleePopsStack = false;
+
+ _argumentsCount = 0;
+ _argumentsDirection = ARGUMENT_DIR_RIGHT_TO_LEFT;
+ _argumentsStackSize = 0;
+
+ _returnValue = INVALID_VALUE;
+
+ Util::memset32(_argumentsGPList , INVALID_VALUE, ASMJIT_ARRAY_SIZE(_argumentsGPList ));
+ Util::memset32(_argumentsXMMList, INVALID_VALUE, ASMJIT_ARRAY_SIZE(_argumentsXMMList));
+
+ _argumentsGP = 0;
+ _argumentsMM = 0;
+ _argumentsXMM = 0;
+
+ _preservedGP = 0;
+ _preservedMM = 0;
+ _preservedXMM = 0;
+
+ _passedGP = 0;
+ _passedMM = 0;
+ _passedXMM = 0;
+}
+
+void FunctionPrototype::_setCallingConvention(uint32_t callingConvention) ASMJIT_NOTHROW
+{
+ // Safe defaults.
+ _clear();
+
+ _callingConvention = callingConvention;
+
+ // --------------------------------------------------------------------------
+ // [X86 Calling Conventions]
+ // --------------------------------------------------------------------------
+
+#if defined(ASMJIT_X86)
+ _preservedGP = (1 << REG_INDEX_EBX) |
+ (1 << REG_INDEX_ESP) |
+ (1 << REG_INDEX_EBP) |
+ (1 << REG_INDEX_ESI) |
+ (1 << REG_INDEX_EDI) ;
+ _preservedXMM = 0;
+
+ switch (_callingConvention)
+ {
+ case CALL_CONV_CDECL:
+ break;
+
+ case CALL_CONV_STDCALL:
+ _calleePopsStack = true;
+ break;
+
+ case CALL_CONV_MSTHISCALL:
+ _calleePopsStack = true;
+ _argumentsGPList[0] = REG_INDEX_ECX;
+
+ _argumentsGP = (1 << REG_INDEX_ECX);
+ break;
+
+ case CALL_CONV_MSFASTCALL:
+ _calleePopsStack = true;
+ _argumentsGPList[0] = REG_INDEX_ECX;
+ _argumentsGPList[1] = REG_INDEX_EDX;
+
+ _argumentsGP = (1 << REG_INDEX_ECX) |
+ (1 << REG_INDEX_EDX) ;
+ break;
+
+ case CALL_CONV_BORLANDFASTCALL:
+ _calleePopsStack = true;
+ _argumentsDirection = ARGUMENT_DIR_LEFT_TO_RIGHT;
+ _argumentsGPList[0] = REG_INDEX_EAX;
+ _argumentsGPList[1] = REG_INDEX_EDX;
+ _argumentsGPList[2] = REG_INDEX_ECX;
+
+ _argumentsGP = (1 << REG_INDEX_EAX) |
+ (1 << REG_INDEX_EDX) |
+ (1 << REG_INDEX_ECX) ;
+ break;
+
+ case CALL_CONV_GCCFASTCALL:
+ _calleePopsStack = true;
+ _argumentsGPList[0] = REG_INDEX_ECX;
+ _argumentsGPList[1] = REG_INDEX_EDX;
+
+ _argumentsGP = (1 << REG_INDEX_ECX) |
+ (1 << REG_INDEX_EDX) ;
+ break;
+
+ case CALL_CONV_GCCREGPARM_1:
+ _calleePopsStack = false;
+ _argumentsGPList[0] = REG_INDEX_EAX;
+
+ _argumentsGP = (1 << REG_INDEX_EAX) ;
+ break;
+
+ case CALL_CONV_GCCREGPARM_2:
+ _calleePopsStack = false;
+ _argumentsGPList[0] = REG_INDEX_EAX;
+ _argumentsGPList[1] = REG_INDEX_EDX;
+
+ _argumentsGP = (1 << REG_INDEX_EAX) |
+ (1 << REG_INDEX_EDX) ;
+ break;
+
+ case CALL_CONV_GCCREGPARM_3:
+ _calleePopsStack = false;
+ _argumentsGPList[0] = REG_INDEX_EAX;
+ _argumentsGPList[1] = REG_INDEX_EDX;
+ _argumentsGPList[2] = REG_INDEX_ECX;
+
+ _argumentsGP = (1 << REG_INDEX_EAX) |
+ (1 << REG_INDEX_EDX) |
+ (1 << REG_INDEX_ECX) ;
+ break;
+
+ default:
+ // Illegal calling convention.
+ ASMJIT_ASSERT(0);
+ }
+#endif // ASMJIT_X86
+
+ // --------------------------------------------------------------------------
+ // [X64 Calling Conventions]
+ // --------------------------------------------------------------------------
+
+#if defined(ASMJIT_X64)
+ switch (_callingConvention)
+ {
+ case CALL_CONV_X64W:
+ _argumentsGPList[0] = REG_INDEX_RCX;
+ _argumentsGPList[1] = REG_INDEX_RDX;
+ _argumentsGPList[2] = REG_INDEX_R8;
+ _argumentsGPList[3] = REG_INDEX_R9;
+
+ _argumentsXMMList[0] = REG_INDEX_XMM0;
+ _argumentsXMMList[1] = REG_INDEX_XMM1;
+ _argumentsXMMList[2] = REG_INDEX_XMM2;
+ _argumentsXMMList[3] = REG_INDEX_XMM3;
+
+ _argumentsGP = (1 << REG_INDEX_RCX ) |
+ (1 << REG_INDEX_RDX ) |
+ (1 << REG_INDEX_R8 ) |
+ (1 << REG_INDEX_R9 ) ;
+
+ _argumentsXMM = (1 << REG_INDEX_XMM0 ) |
+ (1 << REG_INDEX_XMM1 ) |
+ (1 << REG_INDEX_XMM2 ) |
+ (1 << REG_INDEX_XMM3 ) ;
+
+ _preservedGP = (1 << REG_INDEX_RBX ) |
+ (1 << REG_INDEX_RSP ) |
+ (1 << REG_INDEX_RBP ) |
+ (1 << REG_INDEX_RSI ) |
+ (1 << REG_INDEX_RDI ) |
+ (1 << REG_INDEX_R12 ) |
+ (1 << REG_INDEX_R13 ) |
+ (1 << REG_INDEX_R14 ) |
+ (1 << REG_INDEX_R15 ) ;
+
+ _preservedXMM = (1 << REG_INDEX_XMM6 ) |
+ (1 << REG_INDEX_XMM7 ) |
+ (1 << REG_INDEX_XMM8 ) |
+ (1 << REG_INDEX_XMM9 ) |
+ (1 << REG_INDEX_XMM10) |
+ (1 << REG_INDEX_XMM11) |
+ (1 << REG_INDEX_XMM12) |
+ (1 << REG_INDEX_XMM13) |
+ (1 << REG_INDEX_XMM14) |
+ (1 << REG_INDEX_XMM15) ;
+ break;
+
+ case CALL_CONV_X64U:
+ _argumentsGPList[0] = REG_INDEX_RDI;
+ _argumentsGPList[1] = REG_INDEX_RSI;
+ _argumentsGPList[2] = REG_INDEX_RDX;
+ _argumentsGPList[3] = REG_INDEX_RCX;
+ _argumentsGPList[4] = REG_INDEX_R8;
+ _argumentsGPList[5] = REG_INDEX_R9;
+
+ _argumentsXMMList[0] = REG_INDEX_XMM0;
+ _argumentsXMMList[1] = REG_INDEX_XMM1;
+ _argumentsXMMList[2] = REG_INDEX_XMM2;
+ _argumentsXMMList[3] = REG_INDEX_XMM3;
+ _argumentsXMMList[4] = REG_INDEX_XMM4;
+ _argumentsXMMList[5] = REG_INDEX_XMM5;
+ _argumentsXMMList[6] = REG_INDEX_XMM6;
+ _argumentsXMMList[7] = REG_INDEX_XMM7;
+
+ _argumentsGP = (1 << REG_INDEX_RDI ) |
+ (1 << REG_INDEX_RSI ) |
+ (1 << REG_INDEX_RDX ) |
+ (1 << REG_INDEX_RCX ) |
+ (1 << REG_INDEX_R8 ) |
+ (1 << REG_INDEX_R9 ) ;
+
+ _argumentsXMM = (1 << REG_INDEX_XMM0 ) |
+ (1 << REG_INDEX_XMM1 ) |
+ (1 << REG_INDEX_XMM2 ) |
+ (1 << REG_INDEX_XMM3 ) |
+ (1 << REG_INDEX_XMM4 ) |
+ (1 << REG_INDEX_XMM5 ) |
+ (1 << REG_INDEX_XMM6 ) |
+ (1 << REG_INDEX_XMM7 ) ;
+
+ _preservedGP = (1 << REG_INDEX_RBX ) |
+ (1 << REG_INDEX_RSP ) |
+ (1 << REG_INDEX_RBP ) |
+ (1 << REG_INDEX_R12 ) |
+ (1 << REG_INDEX_R13 ) |
+ (1 << REG_INDEX_R14 ) |
+ (1 << REG_INDEX_R15 ) ;
+ break;
+
+ default:
+ // Illegal calling convention.
+ ASMJIT_ASSERT(0);
+ }
+#endif // ASMJIT_X64
+}
+
+void FunctionPrototype::_setPrototype(
+ const uint32_t* argumentsData,
+ uint32_t argumentsCount,
+ uint32_t returnValue) ASMJIT_NOTHROW
+{
+ ASMJIT_ASSERT(argumentsCount <= 32);
+
+ int32_t i;
+
+ int32_t posGP = 0;
+ int32_t posXMM = 0;
+ int32_t stackOffset = 0;
+
+ _returnValue = returnValue;
+
+ for (i = 0; i < (sysint_t)argumentsCount; i++)
+ {
+ Argument& a = _arguments[i];
+ a.variableType = argumentsData[i];
+ a.registerIndex = INVALID_VALUE;
+ a.stackOffset = INVALID_VALUE;
+ }
+
+ _argumentsCount = (uint32_t)argumentsCount;
+ if (_argumentsCount == 0) return;
+
+ // --------------------------------------------------------------------------
+ // [X86 Calling Conventions (32-bit)]
+ // --------------------------------------------------------------------------
+
+#if defined(ASMJIT_X86)
+ // Register arguments (Integer), always left-to-right.
+ for (i = 0; i != argumentsCount; i++)
+ {
+ Argument& a = _arguments[i];
+ if (isVariableInteger(a.variableType) && posGP < 16 && _argumentsGPList[posGP] != INVALID_VALUE)
+ {
+ a.registerIndex = _argumentsGPList[posGP++];
+ _passedGP |= Util::maskFromIndex(a.registerIndex);
+ }
+ }
+
+ // Stack arguments.
+ bool ltr = _argumentsDirection == ARGUMENT_DIR_LEFT_TO_RIGHT;
+ sysint_t istart = ltr ? 0 : (sysint_t)argumentsCount - 1;
+ sysint_t iend = ltr ? (sysint_t)argumentsCount : -1;
+ sysint_t istep = ltr ? 1 : -1;
+
+ for (i = istart; i != iend; i += istep)
+ {
+ Argument& a = _arguments[i];
+ if (a.registerIndex != INVALID_VALUE) continue;
+
+ if (isVariableInteger(a.variableType))
+ {
+ stackOffset -= 4;
+ a.stackOffset = stackOffset;
+ }
+ else if (isVariableFloat(a.variableType))
+ {
+ int32_t size = (int32_t)variableInfo[a.variableType].size;
+ stackOffset -= size;
+ a.stackOffset = stackOffset;
+ }
+ }
+#endif // ASMJIT_X86
+
+ // --------------------------------------------------------------------------
+ // [X64 Calling Conventions (64-bit)]
+ // --------------------------------------------------------------------------
+
+#if defined(ASMJIT_X64)
+ // Windows 64-bit specific.
+ if (_callingConvention == CALL_CONV_X64W)
+ {
+ sysint_t max = argumentsCount < 4 ? argumentsCount : 4;
+
+ // Register arguments (Integer / FP), always left to right.
+ for (i = 0; i != max; i++)
+ {
+ Argument& a = _arguments[i];
+
+ if (isVariableInteger(a.variableType))
+ {
+ a.registerIndex = _argumentsGPList[i];
+ _passedGP |= Util::maskFromIndex(a.registerIndex);
+ }
+ else if (isVariableFloat(a.variableType))
+ {
+ a.registerIndex = _argumentsXMMList[i];
+ _passedXMM |= Util::maskFromIndex(a.registerIndex);
+ }
+ }
+
+ // Stack arguments (always right-to-left).
+ for (i = argumentsCount - 1; i != -1; i--)
+ {
+ Argument& a = _arguments[i];
+ if (a.isAssigned()) continue;
+
+ if (isVariableInteger(a.variableType))
+ {
+ stackOffset -= 8; // Always 8 bytes.
+ a.stackOffset = stackOffset;
+ }
+ else if (isVariableFloat(a.variableType))
+ {
+ int32_t size = (int32_t)variableInfo[a.variableType].size;
+ stackOffset -= size;
+ a.stackOffset = stackOffset;
+ }
+ }
+
+ // 32 bytes shadow space (X64W calling convention specific).
+ stackOffset -= 4 * 8;
+ }
+ // Linux/Unix 64-bit (AMD64 calling convention).
+ else
+ {
+ // Register arguments (Integer), always left to right.
+ for (i = 0; i != argumentsCount; i++)
+ {
+ Argument& a = _arguments[i];
+ if (isVariableInteger(a.variableType) && posGP < 32 && _argumentsGPList[posGP] != INVALID_VALUE)
+ {
+ a.registerIndex = _argumentsGPList[posGP++];
+ _passedGP |= Util::maskFromIndex(a.registerIndex);
+ }
+ }
+
+ // Register arguments (FP), always left to right.
+ for (i = 0; i != argumentsCount; i++)
+ {
+ Argument& a = _arguments[i];
+ if (isVariableFloat(a.variableType))
+ {
+ a.registerIndex = _argumentsXMMList[posXMM++];
+ _passedXMM |= Util::maskFromIndex(a.registerIndex);
+ }
+ }
+
+ // Stack arguments.
+ for (i = argumentsCount - 1; i != -1; i--)
+ {
+ Argument& a = _arguments[i];
+ if (a.isAssigned()) continue;
+
+ if (isVariableInteger(a.variableType))
+ {
+ stackOffset -= 8;
+ a.stackOffset = stackOffset;
+ }
+ else if (isVariableFloat(a.variableType))
+ {
+ int32_t size = (int32_t)variableInfo[a.variableType].size;
+
+ stackOffset -= size;
+ a.stackOffset = stackOffset;
+ }
+ }
+ }
+#endif // ASMJIT_X64
+
+ // Modify stack offset (all function parameters will be in positive stack
+ // offset that is never zero).
+ for (i = 0; i < (sysint_t)argumentsCount; i++)
+ {
+ if (_arguments[i].registerIndex == INVALID_VALUE)
+ _arguments[i].stackOffset += sizeof(sysint_t) - stackOffset;
+ }
+
+ _argumentsStackSize = (uint32_t)(-stackOffset);
+}
+
+void FunctionPrototype::_setReturnValue(uint32_t valueId) ASMJIT_NOTHROW
+{
+ // TODO.
+}
+
+// ============================================================================
+// [AsmJit::EVariableHint]
+// ============================================================================
+
+EVariableHint::EVariableHint(Compiler* c, VarData* vdata, uint32_t hintId, uint32_t hintValue) ASMJIT_NOTHROW :
+ Emittable(c, EMITTABLE_VARIABLE_HINT),
+ _vdata(vdata),
+ _hintId(hintId),
+ _hintValue(hintValue)
+{
+ ASMJIT_ASSERT(_vdata != NULL);
+}
+
+EVariableHint::~EVariableHint() ASMJIT_NOTHROW
+{
+}
+
+void EVariableHint::prepare(CompilerContext& cc) ASMJIT_NOTHROW
+{
+ _offset = cc._currentOffset;
+
+ // First emittable (begin of variable scope).
+ if (_vdata->firstEmittable == NULL) _vdata->firstEmittable = this;
+
+ Emittable* oldLast = _vdata->lastEmittable;
+
+ // Last emittable (end of variable scope).
+ _vdata->lastEmittable = this;
+
+ switch (_hintId)
+ {
+ case VARIABLE_HINT_ALLOC:
+ case VARIABLE_HINT_SPILL:
+ case VARIABLE_HINT_SAVE:
+ if (!cc._isActive(_vdata)) cc._addActive(_vdata);
+ break;
+ case VARIABLE_HINT_SAVE_AND_UNUSE:
+ if (!cc._isActive(_vdata)) cc._addActive(_vdata);
+ break;
+ case VARIABLE_HINT_UNUSE:
+ if (oldLast) oldLast->_tryUnuseVar(_vdata);
+ break;
+ }
+}
+
+Emittable* EVariableHint::translate(CompilerContext& cc) ASMJIT_NOTHROW
+{
+ switch (_hintId)
+ {
+ case VARIABLE_HINT_ALLOC:
+ cc.allocVar(_vdata, _hintValue, VARIABLE_ALLOC_READWRITE);
+ break;
+ case VARIABLE_HINT_SPILL:
+ if (_vdata->state == VARIABLE_STATE_REGISTER)
+ cc.spillVar(_vdata);
+ break;
+ case VARIABLE_HINT_SAVE:
+ case VARIABLE_HINT_SAVE_AND_UNUSE:
+ if (_vdata->state == VARIABLE_STATE_REGISTER && _vdata->changed)
+ {
+ cc.emitSaveVar(_vdata, _vdata->registerIndex);
+ _vdata->changed = false;
+ }
+ if (_hintId == VARIABLE_HINT_SAVE_AND_UNUSE) goto unuse;
+ break;
+ case VARIABLE_HINT_UNUSE:
+unuse:
+ cc.unuseVar(_vdata, VARIABLE_STATE_UNUSED);
+ goto end;
+ }
+
+ cc._unuseVarOnEndOfScope(this, _vdata);
+
+end:
+ return translated();
+}
+
+int EVariableHint::getMaxSize() const ASMJIT_NOTHROW
+{
+ // Variable hint is NOP, but it can generate other emittables which can do
+ // something.
+ return 0;
+}
+
+// ============================================================================
+// [AsmJit::EInstruction]
+// ============================================================================
+
+EInstruction::EInstruction(Compiler* c, uint32_t code, Operand* operandsData, uint32_t operandsCount) ASMJIT_NOTHROW :
+ Emittable(c, EMITTABLE_INSTRUCTION)
+{
+ _code = code;
+ _emitOptions = c->_emitOptions;
+ // Each created instruction takes emit options and clears it.
+ c->_emitOptions = 0;
+
+ _operands = operandsData;
+ _operandsCount = operandsCount;
+ _memOp = NULL;
+
+ _variables = NULL;
+ _variablesCount = 0;
+
+ uint32_t i;
+ for (i = 0; i < operandsCount; i++)
+ {
+ if (_operands[i].isMem())
+ {
+ _memOp = reinterpret_cast<Mem*>(&_operands[i]);
+ break;
+ }
+ }
+
+ const InstructionDescription* id = &instructionDescription[_code];
+ _isSpecial = id->isSpecial();
+ _isFPU = id->isFPU();
+ _isGPBLoUsed = false;
+ _isGPBHiUsed = false;
+
+ if (_isSpecial)
+ {
+ // ${SPECIAL_INSTRUCTION_HANDLING_BEGIN}
+ switch (_code)
+ {
+ case INST_CPUID:
+ // Special...
+ break;
+
+ case INST_CBW:
+ case INST_CDQE:
+ case INST_CWDE:
+ // Special...
+ break;
+
+ case INST_CMPXCHG:
+ case INST_CMPXCHG8B:
+#if defined(ASMJIT_X64)
+ case INST_CMPXCHG16B:
+#endif // ASMJIT_X64
+ // Special...
+ break;
+
+#if defined(ASMJIT_X86)
+ case INST_DAA:
+ case INST_DAS:
+ // Special...
+ break;
+#endif // ASMJIT_X86
+
+ case INST_IMUL:
+ switch (operandsCount)
+ {
+ case 2:
+ // IMUL dst, src is not special instruction.
+ _isSpecial = false;
+ break;
+ case 3:
+ if (!(_operands[0].isVar() && _operands[1].isVar() && _operands[2].isVarMem()))
+ {
+ // Only IMUL dst_lo, dst_hi, reg/mem is special, all others not.
+ _isSpecial = false;
+ }
+ break;
+ }
+ break;
+ case INST_MUL:
+ case INST_IDIV:
+ case INST_DIV:
+ // Special...
+ break;
+
+ case INST_MOV_PTR:
+ // Special...
+ break;
+
+ case INST_LAHF:
+ case INST_SAHF:
+ // Special...
+ break;
+
+ case INST_MASKMOVQ:
+ case INST_MASKMOVDQU:
+ // Special...
+ break;
+
+ case INST_ENTER:
+ case INST_LEAVE:
+ // Special...
+ break;
+
+ case INST_RET:
+ // Special...
+ break;
+
+ case INST_MONITOR:
+ case INST_MWAIT:
+ // Special...
+ break;
+
+ case INST_POP:
+ case INST_POPAD:
+ case INST_POPFD:
+ case INST_POPFQ:
+ // Special...
+ break;
+
+ case INST_PUSH:
+ case INST_PUSHAD:
+ case INST_PUSHFD:
+ case INST_PUSHFQ:
+ // Special...
+ break;
+
+ case INST_RCL:
+ case INST_RCR:
+ case INST_ROL:
+ case INST_ROR:
+ case INST_SAL:
+ case INST_SAR:
+ case INST_SHL:
+ case INST_SHR:
+ // Rot instruction is special only if last operand is variable (register).
+ _isSpecial = _operands[1].isVar();
+ break;
+
+ case INST_SHLD:
+ case INST_SHRD:
+ // Shld/Shrd instruction is special only if last operand is variable (register).
+ _isSpecial = _operands[2].isVar();
+ break;
+
+ case INST_RDTSC:
+ case INST_RDTSCP:
+ // Special...
+ break;
+
+ case INST_REP_LODSB:
+ case INST_REP_LODSD:
+ case INST_REP_LODSQ:
+ case INST_REP_LODSW:
+ case INST_REP_MOVSB:
+ case INST_REP_MOVSD:
+ case INST_REP_MOVSQ:
+ case INST_REP_MOVSW:
+ case INST_REP_STOSB:
+ case INST_REP_STOSD:
+ case INST_REP_STOSQ:
+ case INST_REP_STOSW:
+ case INST_REPE_CMPSB:
+ case INST_REPE_CMPSD:
+ case INST_REPE_CMPSQ:
+ case INST_REPE_CMPSW:
+ case INST_REPE_SCASB:
+ case INST_REPE_SCASD:
+ case INST_REPE_SCASQ:
+ case INST_REPE_SCASW:
+ case INST_REPNE_CMPSB:
+ case INST_REPNE_CMPSD:
+ case INST_REPNE_CMPSQ:
+ case INST_REPNE_CMPSW:
+ case INST_REPNE_SCASB:
+ case INST_REPNE_SCASD:
+ case INST_REPNE_SCASQ:
+ case INST_REPNE_SCASW:
+ // Special...
+ break;
+
+ default:
+ ASMJIT_ASSERT(0);
+ }
+ // ${SPECIAL_INSTRUCTION_HANDLING_END}
+ }
+}
+
+EInstruction::~EInstruction() ASMJIT_NOTHROW
+{
+}
+
+void EInstruction::prepare(CompilerContext& cc) ASMJIT_NOTHROW
+{
+#define __GET_VARIABLE(__vardata__) \
+ { \
+ VarData* _candidate = __vardata__; \
+ \
+ for (var = cur; ;) \
+ { \
+ if (var == _variables) \
+ { \
+ var = cur++; \
+ var->vdata = _candidate; \
+ var->vflags = 0; \
+ var->regMask = 0xFFFFFFFF; \
+ break; \
+ } \
+ \
+ var--; \
+ \
+ if (var->vdata == _candidate) \
+ { \
+ break; \
+ } \
+ } \
+ \
+ ASMJIT_ASSERT(var != NULL); \
+ }
+
+ _offset = cc._currentOffset;
+
+ const InstructionDescription* id = &instructionDescription[_code];
+
+ uint32_t i, len = _operandsCount;
+ uint32_t variablesCount = 0;
+
+ for (i = 0; i < len; i++)
+ {
+ Operand& o = _operands[i];
+
+ if (o.isVar())
+ {
+ ASMJIT_ASSERT(o.getId() != INVALID_VALUE);
+ VarData* vdata = _compiler->_getVarData(o.getId());
+ ASMJIT_ASSERT(vdata != NULL);
+
+ if (reinterpret_cast<BaseVar*>(&o)->isGPVar())
+ {
+ if (reinterpret_cast<GPVar*>(&o)->isGPBLo()) { _isGPBLoUsed = true; vdata->registerGPBLoCount++; };
+ if (reinterpret_cast<GPVar*>(&o)->isGPBHi()) { _isGPBHiUsed = true; vdata->registerGPBHiCount++; };
+ }
+
+ if (vdata->workOffset != _offset)
+ {
+ if (!cc._isActive(vdata)) cc._addActive(vdata);
+
+ vdata->workOffset = _offset;
+ variablesCount++;
+ }
+ }
+ else if (o.isMem())
+ {
+ if ((o.getId() & OPERAND_ID_TYPE_MASK) == OPERAND_ID_TYPE_VAR)
+ {
+ VarData* vdata = _compiler->_getVarData(o.getId());
+ ASMJIT_ASSERT(vdata != NULL);
+
+ cc._markMemoryUsed(vdata);
+
+ if (vdata->workOffset != _offset)
+ {
+ if (!cc._isActive(vdata)) cc._addActive(vdata);
+
+ vdata->workOffset = _offset;
+ variablesCount++;
+ }
+ }
+ else if ((o._mem.base & OPERAND_ID_TYPE_MASK) == OPERAND_ID_TYPE_VAR)
+ {
+ VarData* vdata = _compiler->_getVarData(o._mem.base);
+ ASMJIT_ASSERT(vdata != NULL);
+
+ if (vdata->workOffset != _offset)
+ {
+ if (!cc._isActive(vdata)) cc._addActive(vdata);
+
+ vdata->workOffset = _offset;
+ variablesCount++;
+ }
+ }
+
+ if ((o._mem.index & OPERAND_ID_TYPE_MASK) == OPERAND_ID_TYPE_VAR)
+ {
+ VarData* vdata = _compiler->_getVarData(o._mem.index);
+ ASMJIT_ASSERT(vdata != NULL);
+
+ if (vdata->workOffset != _offset)
+ {
+ if (!cc._isActive(vdata)) cc._addActive(vdata);
+
+ vdata->workOffset = _offset;
+ variablesCount++;
+ }
+ }
+ }
+ }
+
+ if (!variablesCount)
+ {
+ cc._currentOffset++;
+ return;
+ }
+
+ _variables = reinterpret_cast<VarAllocRecord*>(_compiler->getZone().zalloc(sizeof(VarAllocRecord) * variablesCount));
+ if (!_variables)
+ {
+ _compiler->setError(ERROR_NO_HEAP_MEMORY);
+ cc._currentOffset++;
+ return;
+ }
+
+ _variablesCount = variablesCount;
+
+ VarAllocRecord* cur = _variables;
+ VarAllocRecord* var = NULL;
+
+ bool _isGPBUsed = _isGPBLoUsed | _isGPBHiUsed;
+ uint32_t gpRestrictMask = Util::maskUpToIndex(REG_NUM_GP);
+
+#if defined(ASMJIT_X64)
+ if (_isGPBHiUsed)
+ {
+ gpRestrictMask &= Util::maskFromIndex(REG_INDEX_EAX) |
+ Util::maskFromIndex(REG_INDEX_EBX) |
+ Util::maskFromIndex(REG_INDEX_ECX) |
+ Util::maskFromIndex(REG_INDEX_EDX) |
+ Util::maskFromIndex(REG_INDEX_EBP) |
+ Util::maskFromIndex(REG_INDEX_ESI) |
+ Util::maskFromIndex(REG_INDEX_EDI) ;
+ }
+#endif // ASMJIT_X64
+
+ for (i = 0; i < len; i++)
+ {
+ Operand& o = _operands[i];
+
+ if (o.isVar())
+ {
+ VarData* vdata = _compiler->_getVarData(o.getId());
+ ASMJIT_ASSERT(vdata != NULL);
+
+ __GET_VARIABLE(vdata)
+ var->vflags |= VARIABLE_ALLOC_REGISTER;
+
+ if (_isGPBUsed)
+ {
+#if defined(ASMJIT_X86)
+ if (reinterpret_cast<GPVar*>(&o)->isGPB())
+ {
+ var->regMask &= Util::maskFromIndex(REG_INDEX_EAX) |
+ Util::maskFromIndex(REG_INDEX_EBX) |
+ Util::maskFromIndex(REG_INDEX_ECX) |
+ Util::maskFromIndex(REG_INDEX_EDX) ;
+ }
+#else
+ // Restrict all BYTE registers to RAX/RBX/RCX/RDX if HI BYTE register
+ // is used (REX prefix makes HI BYTE addressing unencodable).
+ if (_isGPBHiUsed)
+ {
+ if (reinterpret_cast<GPVar*>(&o)->isGPB())
+ {
+ var->regMask &= Util::maskFromIndex(REG_INDEX_EAX) |
+ Util::maskFromIndex(REG_INDEX_EBX) |
+ Util::maskFromIndex(REG_INDEX_ECX) |
+ Util::maskFromIndex(REG_INDEX_EDX) ;
+ }
+ }
+#endif // ASMJIT_X86/X64
+ }
+
+ if (isSpecial())
+ {
+ // ${SPECIAL_INSTRUCTION_HANDLING_BEGIN}
+ switch (_code)
+ {
+ case INST_CPUID:
+ switch (i)
+ {
+ case 0:
+ vdata->registerRWCount++;
+ var->vflags |= VARIABLE_ALLOC_READWRITE | VARIABLE_ALLOC_SPECIAL;
+ var->regMask = Util::maskFromIndex(REG_INDEX_EAX);
+ gpRestrictMask &= ~var->regMask;
+ break;
+ case 1:
+ vdata->registerWriteCount++;
+ var->vflags |= VARIABLE_ALLOC_WRITE | VARIABLE_ALLOC_SPECIAL;
+ var->regMask = Util::maskFromIndex(REG_INDEX_EBX);
+ gpRestrictMask &= ~var->regMask;
+ break;
+ case 2:
+ vdata->registerWriteCount++;
+ var->vflags |= VARIABLE_ALLOC_WRITE | VARIABLE_ALLOC_SPECIAL;
+ var->regMask = Util::maskFromIndex(REG_INDEX_ECX);
+ gpRestrictMask &= ~var->regMask;
+ break;
+ case 3:
+ vdata->registerWriteCount++;
+ var->vflags |= VARIABLE_ALLOC_WRITE | VARIABLE_ALLOC_SPECIAL;
+ var->regMask = Util::maskFromIndex(REG_INDEX_EDX);
+ gpRestrictMask &= ~var->regMask;
+ break;
+
+ default:
+ ASMJIT_ASSERT(0);
+ }
+ break;
+
+ case INST_CBW:
+ case INST_CDQE:
+ case INST_CWDE:
+ switch (i)
+ {
+ case 0:
+ vdata->registerRWCount++;
+ var->vflags |= VARIABLE_ALLOC_READWRITE | VARIABLE_ALLOC_SPECIAL;
+ var->regMask = Util::maskFromIndex(REG_INDEX_EAX);
+ gpRestrictMask &= ~var->regMask;
+ break;
+
+ default:
+ ASMJIT_ASSERT(0);
+ }
+ break;
+
+ case INST_CMPXCHG:
+ switch (i)
+ {
+ case 0:
+ vdata->registerRWCount++;
+ var->vflags |= VARIABLE_ALLOC_READWRITE | VARIABLE_ALLOC_SPECIAL;
+ var->regMask = Util::maskFromIndex(REG_INDEX_EAX);
+ gpRestrictMask &= ~var->regMask;
+ break;
+ case 1:
+ vdata->registerRWCount++;
+ var->vflags |= VARIABLE_ALLOC_READWRITE;
+ break;
+ case 2:
+ vdata->registerReadCount++;
+ var->vflags |= VARIABLE_ALLOC_READ;
+ break;
+
+ default:
+ ASMJIT_ASSERT(0);
+ }
+ break;
+
+ case INST_CMPXCHG8B:
+#if defined(ASMJIT_X64)
+ case INST_CMPXCHG16B:
+#endif // ASMJIT_X64
+ switch (i)
+ {
+ case 0:
+ vdata->registerRWCount++;
+ var->vflags |= VARIABLE_ALLOC_READWRITE | VARIABLE_ALLOC_SPECIAL;
+ var->regMask = Util::maskFromIndex(REG_INDEX_EDX);
+ gpRestrictMask &= ~var->regMask;
+ break;
+ case 1:
+ vdata->registerRWCount++;
+ var->vflags |= VARIABLE_ALLOC_READWRITE | VARIABLE_ALLOC_SPECIAL;
+ var->regMask = Util::maskFromIndex(REG_INDEX_EAX);
+ gpRestrictMask &= ~var->regMask;
+ break;
+ case 2:
+ vdata->registerReadCount++;
+ var->vflags |= VARIABLE_ALLOC_READ | VARIABLE_ALLOC_SPECIAL;
+ var->regMask = Util::maskFromIndex(REG_INDEX_ECX);
+ gpRestrictMask &= ~var->regMask;
+ break;
+ case 3:
+ vdata->registerReadCount++;
+ var->vflags |= VARIABLE_ALLOC_READ | VARIABLE_ALLOC_SPECIAL;
+ var->regMask = Util::maskFromIndex(REG_INDEX_EBX);
+ gpRestrictMask &= ~var->regMask;
+ break;
+
+ default:
+ ASMJIT_ASSERT(0);
+ }
+ break;
+
+#if defined(ASMJIT_X86)
+ case INST_DAA:
+ case INST_DAS:
+ ASMJIT_ASSERT(i == 0);
+ vdata->registerRWCount++;
+ var->vflags |= VARIABLE_ALLOC_READWRITE | VARIABLE_ALLOC_SPECIAL;
+ var->regMask = Util::maskFromIndex(REG_INDEX_EAX);
+ gpRestrictMask &= ~var->regMask;
+ break;
+#endif // ASMJIT_X86
+
+ case INST_IMUL:
+ case INST_MUL:
+ case INST_IDIV:
+ case INST_DIV:
+ switch (i)
+ {
+ case 0:
+ vdata->registerRWCount++;
+ var->vflags |= VARIABLE_ALLOC_READWRITE | VARIABLE_ALLOC_SPECIAL;
+ var->regMask = Util::maskFromIndex(REG_INDEX_EAX);
+ gpRestrictMask &= ~var->regMask;
+ break;
+ case 1:
+ vdata->registerWriteCount++;
+ var->vflags |= VARIABLE_ALLOC_WRITE | VARIABLE_ALLOC_SPECIAL;
+ var->regMask = Util::maskFromIndex(REG_INDEX_EDX);
+ gpRestrictMask &= ~var->regMask;
+ break;
+ case 2:
+ vdata->registerReadCount++;
+ var->vflags |= VARIABLE_ALLOC_READ;
+ break;
+
+ default:
+ ASMJIT_ASSERT(0);
+ }
+ break;
+
+ case INST_MOV_PTR:
+ switch (i)
+ {
+ case 0:
+ vdata->registerWriteCount++;
+ var->vflags |= VARIABLE_ALLOC_WRITE | VARIABLE_ALLOC_SPECIAL;
+ var->regMask = Util::maskFromIndex(REG_INDEX_EAX);
+ gpRestrictMask &= ~var->regMask;
+ break;
+ case 1:
+ vdata->registerReadCount++;
+ var->vflags |= VARIABLE_ALLOC_READ | VARIABLE_ALLOC_SPECIAL;
+ var->regMask = Util::maskFromIndex(REG_INDEX_EAX);
+ gpRestrictMask &= ~var->regMask;
+ break;
+ default:
+ ASMJIT_ASSERT(0);
+ }
+ break;
+
+ case INST_LAHF:
+ ASMJIT_ASSERT(i == 0);
+ vdata->registerWriteCount++;
+ var->vflags |= VARIABLE_ALLOC_WRITE | VARIABLE_ALLOC_SPECIAL;
+ var->regMask = Util::maskFromIndex(REG_INDEX_EAX);
+ gpRestrictMask &= ~var->regMask;
+ break;
+
+ case INST_SAHF:
+ ASMJIT_ASSERT(i == 0);
+ vdata->registerReadCount++;
+ var->vflags |= VARIABLE_ALLOC_READ | VARIABLE_ALLOC_SPECIAL;
+ var->regMask = Util::maskFromIndex(REG_INDEX_EAX);
+ gpRestrictMask &= ~var->regMask;
+ break;
+
+ case INST_MASKMOVQ:
+ case INST_MASKMOVDQU:
+ switch (i)
+ {
+ case 0:
+ vdata->registerReadCount++;
+ var->vflags |= VARIABLE_ALLOC_READ | VARIABLE_ALLOC_SPECIAL;
+ var->regMask = Util::maskFromIndex(REG_INDEX_EDI);
+ gpRestrictMask &= ~var->regMask;
+ break;
+ case 1:
+ case 2:
+ vdata->registerReadCount++;
+ var->vflags |= VARIABLE_ALLOC_READ;
+ break;
+ }
+ break;
+
+ case INST_ENTER:
+ case INST_LEAVE:
+ // TODO: SPECIAL INSTRUCTION.
+ break;
+
+ case INST_RET:
+ // TODO: SPECIAL INSTRUCTION.
+ break;
+
+ case INST_MONITOR:
+ case INST_MWAIT:
+ // TODO: MONITOR/MWAIT (COMPILER).
+ break;
+
+ case INST_POP:
+ // TODO: SPECIAL INSTRUCTION.
+ break;
+
+ case INST_POPAD:
+ case INST_POPFD:
+ case INST_POPFQ:
+ // TODO: SPECIAL INSTRUCTION.
+ break;
+
+ case INST_PUSH:
+ // TODO: SPECIAL INSTRUCTION.
+ break;
+
+ case INST_PUSHAD:
+ case INST_PUSHFD:
+ case INST_PUSHFQ:
+ // TODO: SPECIAL INSTRUCTION.
+ break;
+
+ case INST_RCL:
+ case INST_RCR:
+ case INST_ROL:
+ case INST_ROR:
+ case INST_SAL:
+ case INST_SAR:
+ case INST_SHL:
+ case INST_SHR:
+ switch (i)
+ {
+ case 0:
+ vdata->registerRWCount++;
+ var->vflags |= VARIABLE_ALLOC_READWRITE;
+ break;
+ case 1:
+ vdata->registerReadCount++;
+ var->vflags |= VARIABLE_ALLOC_READ | VARIABLE_ALLOC_SPECIAL;
+ var->regMask = Util::maskFromIndex(REG_INDEX_ECX);
+ gpRestrictMask &= ~var->regMask;
+ break;
+
+ default:
+ ASMJIT_ASSERT(0);
+ }
+ break;
+
+ case INST_SHLD:
+ case INST_SHRD:
+ switch (i)
+ {
+ case 0:
+ vdata->registerRWCount++;
+ var->vflags |= VARIABLE_ALLOC_READWRITE;
+ break;
+ case 1:
+ vdata->registerReadCount++;
+ var->vflags |= VARIABLE_ALLOC_READ;
+ break;
+ case 2:
+ vdata->registerReadCount++;
+ var->vflags |= VARIABLE_ALLOC_READ | VARIABLE_ALLOC_SPECIAL;
+ var->regMask = Util::maskFromIndex(REG_INDEX_ECX);
+ gpRestrictMask &= ~var->regMask;
+ break;
+
+ default:
+ ASMJIT_ASSERT(0);
+ }
+ break;
+
+ case INST_RDTSC:
+ case INST_RDTSCP:
+ switch (i)
+ {
+ case 0:
+ vdata->registerWriteCount++;
+ var->vflags |= VARIABLE_ALLOC_WRITE | VARIABLE_ALLOC_SPECIAL;
+ var->regMask = Util::maskFromIndex(REG_INDEX_EDX);
+ gpRestrictMask &= ~var->regMask;
+ break;
+ case 1:
+ vdata->registerWriteCount++;
+ var->vflags |= VARIABLE_ALLOC_WRITE | VARIABLE_ALLOC_SPECIAL;
+ var->regMask = Util::maskFromIndex(REG_INDEX_EAX);
+ gpRestrictMask &= ~var->regMask;
+ break;
+ case 2:
+ ASMJIT_ASSERT(_code == INST_RDTSCP);
+ vdata->registerWriteCount++;
+ var->vflags |= VARIABLE_ALLOC_WRITE | VARIABLE_ALLOC_SPECIAL;
+ var->regMask = Util::maskFromIndex(REG_INDEX_ECX);
+ gpRestrictMask &= ~var->regMask;
+ break;
+
+ default:
+ ASMJIT_ASSERT(0);
+ }
+ break;
+
+ case INST_REP_LODSB:
+ case INST_REP_LODSD:
+ case INST_REP_LODSQ:
+ case INST_REP_LODSW:
+ switch (i)
+ {
+ case 0:
+ vdata->registerWriteCount++;
+ var->vflags |= VARIABLE_ALLOC_WRITE | VARIABLE_ALLOC_SPECIAL;
+ var->regMask = Util::maskFromIndex(REG_INDEX_EAX);
+ gpRestrictMask &= ~var->regMask;
+ break;
+ case 1:
+ vdata->registerReadCount++;
+ var->vflags |= VARIABLE_ALLOC_READ | VARIABLE_ALLOC_SPECIAL;
+ var->regMask = Util::maskFromIndex(REG_INDEX_ESI);
+ gpRestrictMask &= ~var->regMask;
+ break;
+ case 2:
+ vdata->registerRWCount++;
+ var->vflags |= VARIABLE_ALLOC_READWRITE | VARIABLE_ALLOC_SPECIAL;
+ var->regMask = Util::maskFromIndex(REG_INDEX_ECX);
+ gpRestrictMask &= ~var->regMask;
+ break;
+ default:
+ ASMJIT_ASSERT(0);
+ }
+ break;
+
+ case INST_REP_MOVSB:
+ case INST_REP_MOVSD:
+ case INST_REP_MOVSQ:
+ case INST_REP_MOVSW:
+ case INST_REPE_CMPSB:
+ case INST_REPE_CMPSD:
+ case INST_REPE_CMPSQ:
+ case INST_REPE_CMPSW:
+ case INST_REPNE_CMPSB:
+ case INST_REPNE_CMPSD:
+ case INST_REPNE_CMPSQ:
+ case INST_REPNE_CMPSW:
+ switch (i)
+ {
+ case 0:
+ vdata->registerReadCount++;
+ var->vflags |= VARIABLE_ALLOC_READ | VARIABLE_ALLOC_SPECIAL;
+ var->regMask = Util::maskFromIndex(REG_INDEX_EDI);
+ gpRestrictMask &= ~var->regMask;
+ break;
+ case 1:
+ vdata->registerReadCount++;
+ var->vflags |= VARIABLE_ALLOC_READ | VARIABLE_ALLOC_SPECIAL;
+ var->regMask = Util::maskFromIndex(REG_INDEX_ESI);
+ gpRestrictMask &= ~var->regMask;
+ break;
+ case 2:
+ vdata->registerRWCount++;
+ var->vflags |= VARIABLE_ALLOC_READWRITE | VARIABLE_ALLOC_SPECIAL;
+ var->regMask = Util::maskFromIndex(REG_INDEX_ECX);
+ gpRestrictMask &= ~var->regMask;
+ break;
+ default:
+ ASMJIT_ASSERT(0);
+ }
+ break;
+
+ case INST_REP_STOSB:
+ case INST_REP_STOSD:
+ case INST_REP_STOSQ:
+ case INST_REP_STOSW:
+ switch (i)
+ {
+ case 0:
+ vdata->registerReadCount++;
+ var->vflags |= VARIABLE_ALLOC_READ | VARIABLE_ALLOC_SPECIAL;
+ var->regMask = Util::maskFromIndex(REG_INDEX_EDI);
+ gpRestrictMask &= ~var->regMask;
+ break;
+ case 1:
+ vdata->registerReadCount++;
+ var->vflags |= VARIABLE_ALLOC_READ | VARIABLE_ALLOC_SPECIAL;
+ var->regMask = Util::maskFromIndex(REG_INDEX_EAX);
+ gpRestrictMask &= ~var->regMask;
+ break;
+ case 2:
+ vdata->registerRWCount++;
+ var->vflags |= VARIABLE_ALLOC_READWRITE | VARIABLE_ALLOC_SPECIAL;
+ var->regMask = Util::maskFromIndex(REG_INDEX_ECX);
+ gpRestrictMask &= ~var->regMask;
+ break;
+ default:
+ ASMJIT_ASSERT(0);
+ }
+ break;
+
+ case INST_REPE_SCASB:
+ case INST_REPE_SCASD:
+ case INST_REPE_SCASQ:
+ case INST_REPE_SCASW:
+ case INST_REPNE_SCASB:
+ case INST_REPNE_SCASD:
+ case INST_REPNE_SCASQ:
+ case INST_REPNE_SCASW:
+ switch (i)
+ {
+ case 0:
+ vdata->registerReadCount++;
+ var->vflags |= VARIABLE_ALLOC_READ | VARIABLE_ALLOC_SPECIAL;
+ var->regMask = Util::maskFromIndex(REG_INDEX_EDI);
+ gpRestrictMask &= ~var->regMask;
+ break;
+ case 1:
+ vdata->registerReadCount++;
+ var->vflags |= VARIABLE_ALLOC_READ | VARIABLE_ALLOC_SPECIAL;
+ var->regMask = Util::maskFromIndex(REG_INDEX_EAX);
+ gpRestrictMask &= ~var->regMask;
+ break;
+ case 2:
+ vdata->registerRWCount++;
+ var->vflags |= VARIABLE_ALLOC_READWRITE | VARIABLE_ALLOC_SPECIAL;
+ var->regMask = Util::maskFromIndex(REG_INDEX_ECX);
+ gpRestrictMask &= ~var->regMask;
+ break;
+ default:
+ ASMJIT_ASSERT(0);
+ }
+ break;
+
+ default:
+ ASMJIT_ASSERT(0);
+ }
+ // ${SPECIAL_INSTRUCTION_HANDLING_END}
+ }
+ else
+ {
+ if (i == 0)
+ {
+ // CMP/TEST instruction.
+ if (id->code == INST_CMP || id->code == INST_TEST)
+ {
+ // Read-only case.
+ vdata->registerReadCount++;
+ var->vflags |= VARIABLE_ALLOC_READ;
+ }
+ // MOV/MOVSS/MOVSD instructions.
+ //
+ // If instruction is MOV (source replaces the destination) or
+ // MOVSS/MOVSD and source operand is memory location then register
+ // allocator should know that previous destination value is lost
+ // (write only operation).
+ else if ((id->isMov()) ||
+ ((id->code == INST_MOVSS || id->code == INST_MOVSD) /* && _operands[1].isMem() */) ||
+ (id->code == INST_IMUL && _operandsCount == 3 && !isSpecial()))
+ {
+ // Write-only case.
+ vdata->registerWriteCount++;
+ var->vflags |= VARIABLE_ALLOC_WRITE;
+ }
+ else if (id->code == INST_LEA)
+ {
+ // Write.
+ vdata->registerWriteCount++;
+ var->vflags |= VARIABLE_ALLOC_WRITE;
+ }
+ else
+ {
+ // Read/Write.
+ vdata->registerRWCount++;
+ var->vflags |= VARIABLE_ALLOC_READWRITE;
+ }
+ }
+ else
+ {
+ // Second, third, ... operands are read-only.
+ vdata->registerReadCount++;
+ var->vflags |= VARIABLE_ALLOC_READ;
+ }
+
+ if (!_memOp && i < 2 && (id->oflags[i] & InstructionDescription::O_MEM) != 0)
+ {
+ var->vflags |= VARIABLE_ALLOC_MEMORY;
+ }
+ }
+
+ // If variable must be in specific register we could add some hint to allocator.
+ if (var->vflags & VARIABLE_ALLOC_SPECIAL)
+ {
+ vdata->prefRegisterMask |= Util::maskFromIndex(var->regMask);
+ cc._newRegisterHomeIndex(vdata, Util::findFirstBit(var->regMask));
+ }
+ }
+ else if (o.isMem())
+ {
+ if ((o.getId() & OPERAND_ID_TYPE_MASK) == OPERAND_ID_TYPE_VAR)
+ {
+ VarData* vdata = _compiler->_getVarData(o.getId());
+ ASMJIT_ASSERT(vdata != NULL);
+
+ __GET_VARIABLE(vdata)
+
+ if (i == 0)
+ {
+ // If variable is MOV instruction type (source replaces the destination)
+ // or variable is MOVSS/MOVSD instruction then register allocator should
+ // know that previous destination value is lost (write only operation).
+ if (id->isMov() || ((id->code == INST_MOVSS || id->code == INST_MOVSD)))
+ {
+ // Write only case.
+ vdata->memoryWriteCount++;
+ }
+ else
+ {
+ vdata->memoryRWCount++;
+ }
+ }
+ else
+ {
+ vdata->memoryReadCount++;
+ }
+ }
+ else if ((o._mem.base & OPERAND_ID_TYPE_MASK) == OPERAND_ID_TYPE_VAR)
+ {
+ VarData* vdata = _compiler->_getVarData(reinterpret_cast<Mem&>(o).getBase());
+ ASMJIT_ASSERT(vdata != NULL);
+
+ __GET_VARIABLE(vdata)
+ vdata->registerReadCount++;
+ var->vflags |= VARIABLE_ALLOC_REGISTER | VARIABLE_ALLOC_READ;
+ var->regMask &= gpRestrictMask;
+ }
+
+ if ((o._mem.index & OPERAND_ID_TYPE_MASK) == OPERAND_ID_TYPE_VAR)
+ {
+ VarData* vdata = _compiler->_getVarData(reinterpret_cast<Mem&>(o).getIndex());
+ ASMJIT_ASSERT(vdata != NULL);
+
+ __GET_VARIABLE(vdata)
+ vdata->registerReadCount++;
+ var->vflags |= VARIABLE_ALLOC_REGISTER | VARIABLE_ALLOC_READ;
+ var->regMask &= gpRestrictMask;
+ }
+ }
+ }
+
+ // Traverse all variables and update firstEmittable / lastEmittable. This
+ // function is called from iterator that scans emittables using forward
+ // direction so we can use this knowledge to optimize the process.
+ //
+ // Similar to ECall::prepare().
+ for (i = 0; i < _variablesCount; i++)
+ {
+ VarData* v = _variables[i].vdata;
+
+ // Update GP register allocator restrictions.
+ if (isVariableInteger(v->type))
+ {
+ if (_variables[i].regMask == 0xFFFFFFFF) _variables[i].regMask &= gpRestrictMask;
+ }
+
+ // Update first/last emittable (begin of variable scope).
+ if (v->firstEmittable == NULL) v->firstEmittable = this;
+ v->lastEmittable = this;
+ }
+
+ // There are some instructions that can be used to clear register or to set
+ // register to some value (ideal case is all zeros or all ones).
+ //
+ // xor/pxor reg, reg ; Set all bits in reg to 0.
+ // sub/psub reg, reg ; Set all bits in reg to 0.
+ // andn reg, reg ; Set all bits in reg to 0.
+ // pcmpgt reg, reg ; Set all bits in reg to 0.
+ // pcmpeq reg, reg ; Set all bits in reg to 1.
+
+ if (_variablesCount == 1 &&
+ _operandsCount > 1 &&
+ _operands[0].isVar() &&
+ _operands[1].isVar() &&
+ !_memOp)
+ {
+ switch (_code)
+ {
+ // XOR Instructions.
+ case INST_XOR:
+ case INST_XORPD:
+ case INST_XORPS:
+ case INST_PXOR:
+
+ // ANDN Instructions.
+ case INST_PANDN:
+
+ // SUB Instructions.
+ case INST_SUB:
+ case INST_PSUBB:
+ case INST_PSUBW:
+ case INST_PSUBD:
+ case INST_PSUBQ:
+ case INST_PSUBSB:
+ case INST_PSUBSW:
+ case INST_PSUBUSB:
+ case INST_PSUBUSW:
+
+ // PCMPEQ Instructions.
+ case INST_PCMPEQB:
+ case INST_PCMPEQW:
+ case INST_PCMPEQD:
+ case INST_PCMPEQQ:
+
+ // PCMPGT Instructions.
+ case INST_PCMPGTB:
+ case INST_PCMPGTW:
+ case INST_PCMPGTD:
+ case INST_PCMPGTQ:
+ // Clear the read flag. This prevents variable alloc/spill.
+ _variables[0].vflags = VARIABLE_ALLOC_WRITE;
+ _variables[0].vdata->registerReadCount--;
+ break;
+ }
+ }
+ cc._currentOffset++;
+
+#undef __GET_VARIABLE
+}
+
+Emittable* EInstruction::translate(CompilerContext& cc) ASMJIT_NOTHROW
+{
+ uint32_t i;
+ uint32_t variablesCount = _variablesCount;
+
+ if (variablesCount > 0)
+ {
+ // These variables are used by the instruction and we set current offset
+ // to their work offsets -> getSpillCandidate never return the variable
+ // used this instruction.
+ for (i = 0; i < variablesCount; i++)
+ {
+ _variables->vdata->workOffset = cc._currentOffset;
+ }
+
+ // Alloc variables used by the instruction (special first).
+ for (i = 0; i < variablesCount; i++)
+ {
+ VarAllocRecord& r = _variables[i];
+ // Alloc variables with specific register first.
+ if ((r.vflags & VARIABLE_ALLOC_SPECIAL) != 0)
+ cc.allocVar(r.vdata, r.regMask, r.vflags);
+ }
+
+ for (i = 0; i < variablesCount; i++)
+ {
+ VarAllocRecord& r = _variables[i];
+ // Alloc variables without specific register last.
+ if ((r.vflags & VARIABLE_ALLOC_SPECIAL) == 0)
+ cc.allocVar(r.vdata, r.regMask, r.vflags);
+ }
+
+ cc.translateOperands(_operands, _operandsCount);
+ }
+
+ if (_memOp && (_memOp->getId() & OPERAND_ID_TYPE_MASK) == OPERAND_ID_TYPE_VAR)
+ {
+ VarData* vdata = _compiler->_getVarData(_memOp->getId());
+ ASMJIT_ASSERT(vdata != NULL);
+
+ switch (vdata->state)
+ {
+ case VARIABLE_STATE_UNUSED:
+ vdata->state = VARIABLE_STATE_MEMORY;
+ break;
+ case VARIABLE_STATE_REGISTER:
+ vdata->changed = false;
+ cc.unuseVar(vdata, VARIABLE_STATE_MEMORY);
+ break;
+ }
+ }
+
+ for (i = 0; i < variablesCount; i++)
+ {
+ cc._unuseVarOnEndOfScope(this, &_variables[i]);
+ }
+
+ return translated();
+}
+
+void EInstruction::emit(Assembler& a) ASMJIT_NOTHROW
+{
+ a._comment = _comment;
+ a._emitOptions = _emitOptions;
+
+ if (isSpecial())
+ {
+ // ${SPECIAL_INSTRUCTION_HANDLING_BEGIN}
+ switch (_code)
+ {
+ case INST_CPUID:
+ a._emitInstruction(_code);
+ return;
+
+ case INST_CBW:
+ case INST_CDQE:
+ case INST_CWDE:
+ a._emitInstruction(_code);
+ return;
+
+ case INST_CMPXCHG:
+ a._emitInstruction(_code, &_operands[1], &_operands[2]);
+ return;
+
+ case INST_CMPXCHG8B:
+#if defined(ASMJIT_X64)
+ case INST_CMPXCHG16B:
+#endif // ASMJIT_X64
+ a._emitInstruction(_code, &_operands[4]);
+ return;
+
+#if defined(ASMJIT_X86)
+ case INST_DAA:
+ case INST_DAS:
+ a._emitInstruction(_code);
+ return;
+#endif // ASMJIT_X86
+
+ case INST_IMUL:
+ case INST_MUL:
+ case INST_IDIV:
+ case INST_DIV:
+ // INST dst_lo (implicit), dst_hi (implicit), src (explicit)
+ ASMJIT_ASSERT(_operandsCount == 3);
+ a._emitInstruction(_code, &_operands[2]);
+ return;
+
+ case INST_MOV_PTR:
+ break;
+
+ case INST_LAHF:
+ case INST_SAHF:
+ a._emitInstruction(_code);
+ return;
+
+ case INST_MASKMOVQ:
+ case INST_MASKMOVDQU:
+ a._emitInstruction(_code, &_operands[1], &_operands[2]);
+ return;
+
+ case INST_ENTER:
+ case INST_LEAVE:
+ // TODO: SPECIAL INSTRUCTION.
+ break;
+
+ case INST_RET:
+ // TODO: SPECIAL INSTRUCTION.
+ break;
+
+ case INST_MONITOR:
+ case INST_MWAIT:
+ // TODO: MONITOR/MWAIT (COMPILER).
+ break;
+
+ case INST_POP:
+ case INST_POPAD:
+ case INST_POPFD:
+ case INST_POPFQ:
+ // TODO: SPECIAL INSTRUCTION.
+ break;
+
+ case INST_PUSH:
+ case INST_PUSHAD:
+ case INST_PUSHFD:
+ case INST_PUSHFQ:
+ // TODO: SPECIAL INSTRUCTION.
+ break;
+
+ case INST_RCL:
+ case INST_RCR:
+ case INST_ROL:
+ case INST_ROR:
+ case INST_SAL:
+ case INST_SAR:
+ case INST_SHL:
+ case INST_SHR:
+ a._emitInstruction(_code, &_operands[0], &cl);
+ return;
+
+ case INST_SHLD:
+ case INST_SHRD:
+ a._emitInstruction(_code, &_operands[0], &_operands[1], &cl);
+ return;
+
+ case INST_RDTSC:
+ case INST_RDTSCP:
+ a._emitInstruction(_code);
+ return;
+
+ case INST_REP_LODSB:
+ case INST_REP_LODSD:
+ case INST_REP_LODSQ:
+ case INST_REP_LODSW:
+ case INST_REP_MOVSB:
+ case INST_REP_MOVSD:
+ case INST_REP_MOVSQ:
+ case INST_REP_MOVSW:
+ case INST_REP_STOSB:
+ case INST_REP_STOSD:
+ case INST_REP_STOSQ:
+ case INST_REP_STOSW:
+ case INST_REPE_CMPSB:
+ case INST_REPE_CMPSD:
+ case INST_REPE_CMPSQ:
+ case INST_REPE_CMPSW:
+ case INST_REPE_SCASB:
+ case INST_REPE_SCASD:
+ case INST_REPE_SCASQ:
+ case INST_REPE_SCASW:
+ case INST_REPNE_CMPSB:
+ case INST_REPNE_CMPSD:
+ case INST_REPNE_CMPSQ:
+ case INST_REPNE_CMPSW:
+ case INST_REPNE_SCASB:
+ case INST_REPNE_SCASD:
+ case INST_REPNE_SCASQ:
+ case INST_REPNE_SCASW:
+ a._emitInstruction(_code);
+ return;
+
+ default:
+ ASMJIT_ASSERT(0);
+ }
+ // ${SPECIAL_INSTRUCTION_HANDLING_END}
+ }
+
+ switch (_operandsCount)
+ {
+ case 0:
+ a._emitInstruction(_code);
+ break;
+ case 1:
+ a._emitInstruction(_code, &_operands[0]);
+ break;
+ case 2:
+ a._emitInstruction(_code, &_operands[0], &_operands[1]);
+ break;
+ case 3:
+ a._emitInstruction(_code, &_operands[0], &_operands[1], &_operands[2]);
+ break;
+ default:
+ ASMJIT_ASSERT(0);
+ break;
+ }
+}
+
+int EInstruction::getMaxSize() const ASMJIT_NOTHROW
+{
+ // TODO: Do something more exact.
+ return 15;
+}
+
+bool EInstruction::_tryUnuseVar(VarData* v) ASMJIT_NOTHROW
+{
+ for (uint32_t i = 0; i < _variablesCount; i++)
+ {
+ if (_variables[i].vdata == v)
+ {
+ _variables[i].vflags |= VARIABLE_ALLOC_UNUSE_AFTER_USE;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+ETarget* EInstruction::getJumpTarget() const ASMJIT_NOTHROW
+{
+ return NULL;
+}
+
+// ============================================================================
+// [AsmJit::EJmp]
+// ============================================================================
+
+EJmp::EJmp(Compiler* c, uint32_t code, Operand* operandsData, uint32_t operandsCount) ASMJIT_NOTHROW :
+ EInstruction(c, code, operandsData, operandsCount)
+{
+ _jumpTarget = _compiler->_getTarget(_operands[0].getId());
+ _jumpTarget->_jumpsCount++;
+
+ _jumpNext = _jumpTarget->_from;
+ _jumpTarget->_from = this;
+
+ // The 'jmp' is always taken, conditional jump can contain hint, we detect it.
+ _isTaken = (getCode() == INST_JMP) ||
+ (operandsCount > 1 &&
+ operandsData[1].isImm() &&
+ reinterpret_cast<Imm*>(&operandsData[1])->getValue() == HINT_TAKEN);
+}
+
+EJmp::~EJmp() ASMJIT_NOTHROW
+{
+}
+
+void EJmp::prepare(CompilerContext& cc) ASMJIT_NOTHROW
+{
+ _offset = cc._currentOffset;
+
+ // Update _isTaken to true if this is conditional backward jump. This behavior
+ // can be overriden by using HINT_NOT_TAKEN when using the instruction.
+ if (getCode() != INST_JMP &&
+ _operandsCount == 1 &&
+ _jumpTarget->getOffset() < getOffset())
+ {
+ _isTaken = true;
+ }
+
+ // Now patch all variables where jump location is in the active range.
+ if (_jumpTarget->getOffset() != INVALID_VALUE && cc._active)
+ {
+ VarData* first = cc._active;
+ VarData* var = first;
+ uint32_t jumpOffset = _jumpTarget->getOffset();
+
+ do {
+ if (var->firstEmittable)
+ {
+ ASMJIT_ASSERT(var->lastEmittable != NULL);
+ uint32_t start = var->firstEmittable->getOffset();
+ uint32_t end = var->lastEmittable->getOffset();
+
+ if (jumpOffset >= start && jumpOffset <= end) var->lastEmittable = this;
+ }
+ var = var->nextActive;
+ } while (var != first);
+ }
+
+ cc._currentOffset++;
+}
+
+Emittable* EJmp::translate(CompilerContext& cc) ASMJIT_NOTHROW
+{
+ // Translate using EInstruction.
+ Emittable* ret = EInstruction::translate(cc);
+
+ // We jump with emittable if its INST_JUMP (not based on condiiton) and it
+ // points into yet unknown location.
+ if (_code == INST_JMP && !_jumpTarget->isTranslated())
+ {
+ cc.addBackwardCode(this);
+ ret = _jumpTarget;
+ }
+ else
+ {
+ _state = cc._saveState();
+ if (_jumpTarget->isTranslated())
+ {
+ _doJump(cc);
+ }
+ else
+ {
+ // State is not known, so we need to call _doJump() later. Compiler will
+ // do it for us.
+ cc.addForwardJump(this);
+ _jumpTarget->_state = _state;
+ }
+
+ // Mark next code as unrecheable, cleared by a next label (ETarget).
+ if (_code == INST_JMP) cc._unrecheable = 1;
+ }
+
+ // Need to traverse over all active variables and unuse them if their scope ends
+ // here.
+ if (cc._active)
+ {
+ VarData* first = cc._active;
+ VarData* var = first;
+
+ do {
+ cc._unuseVarOnEndOfScope(this, var);
+ var = var->nextActive;
+ } while (var != first);
+ }
+
+ return ret;
+}
+
+void EJmp::emit(Assembler& a) ASMJIT_NOTHROW
+{
+ static const uint MAXIMUM_SHORT_JMP_SIZE = 127;
+
+ // Try to minimize size of jump using SHORT jump (8-bit displacement) by
+ // traversing into the target and calculating the maximum code size. We
+ // end when code size reaches MAXIMUM_SHORT_JMP_SIZE.
+ if (!(_emitOptions & EMIT_OPTION_SHORT_JUMP) && getJumpTarget()->getOffset() > getOffset())
+ {
+ // Calculate the code size.
+ uint codeSize = 0;
+ Emittable* cur = this->getNext();
+ Emittable* target = getJumpTarget();
+
+ while (cur)
+ {
+ if (cur == target)
+ {
+ // Target found, we can tell assembler to generate short form of jump.
+ _emitOptions |= EMIT_OPTION_SHORT_JUMP;
+ goto end;
+ }
+
+ int s = cur->getMaxSize();
+ if (s == -1) break;
+
+ codeSize += (uint)s;
+ if (codeSize > MAXIMUM_SHORT_JMP_SIZE) break;
+
+ cur = cur->getNext();
+ }
+ }
+
+end:
+ EInstruction::emit(a);
+}
+
+void EJmp::_doJump(CompilerContext& cc) ASMJIT_NOTHROW
+{
+ // The state have to be already known. The _doJump() method is called by
+ // translate() or by Compiler in case that it's forward jump.
+ ASMJIT_ASSERT(_jumpTarget->getState());
+
+ if (getCode() == INST_JMP || (isTaken() && _jumpTarget->getOffset() < getOffset()))
+ {
+ // Instruction type is JMP or conditional jump that should be taken (likely).
+ // We can set state here instead of jumping out, setting state and jumping
+ // to _jumpTarget.
+ //
+ // NOTE: We can't use this technique if instruction is forward conditional
+ // jump. The reason is that when generating code we can't change state here,
+ // because next instruction depends to it.
+ cc._restoreState(_jumpTarget->getState(), _jumpTarget->getOffset());
+ }
+ else
+ {
+ // Instruction type is JMP or conditional jump that should be not normally
+ // taken. If we need add code that will switch between different states we
+ // add it after the end of function body (after epilog, using 'ExtraBlock').
+ Compiler* compiler = cc.getCompiler();
+
+ Emittable* ext = cc.getExtraBlock();
+ Emittable* old = compiler->setCurrentEmittable(ext);
+
+ cc._restoreState(_jumpTarget->getState(), _jumpTarget->getOffset());
+
+ if (compiler->getCurrentEmittable() != ext)
+ {
+ // Add the jump to the target.
+ compiler->jmp(_jumpTarget->_label);
+ ext = compiler->getCurrentEmittable();
+
+ // The cc._restoreState() method emitted some instructions so we need to
+ // patch the jump.
+ Label L = compiler->newLabel();
+ compiler->setCurrentEmittable(cc.getExtraBlock());
+ compiler->bind(L);
+
+ // Finally, patch the jump target.
+ ASMJIT_ASSERT(_operandsCount > 0);
+ _operands[0] = L; // Operand part (Label).
+ _jumpTarget = compiler->_getTarget(L.getId()); // Emittable part (ETarget).
+ }
+
+ cc.setExtraBlock(ext);
+ compiler->setCurrentEmittable(old);
+
+ // Assign state back.
+ cc._assignState(_state);
+ }
+}
+
+ETarget* EJmp::getJumpTarget() const ASMJIT_NOTHROW
+{
+ return _jumpTarget;
+}
+
+// ============================================================================
+// [AsmJit::EFunction]
+// ============================================================================
+
+EFunction::EFunction(Compiler* c) ASMJIT_NOTHROW : Emittable(c, EMITTABLE_FUNCTION)
+{
+ _argumentVariables = NULL;
+ Util::memset32(_hints, INVALID_VALUE, ASMJIT_ARRAY_SIZE(_hints));
+
+ // Stack is always aligned to 16-bytes when using 64-bit OS.
+ _isStackAlignedByOsTo16Bytes = CompilerUtil::isStack16ByteAligned();
+
+ // Manual aligning is autodetected by prepare() method.
+ _isStackAlignedByFnTo16Bytes = false;
+
+ // Just clear to safe defaults.
+ _isNaked = false;
+ _isEspAdjusted = false;
+ _isCaller = false;
+
+ _pePushPop = true;
+ _emitEMMS = false;
+ _emitSFence = false;
+ _emitLFence = false;
+
+ _finished = false;
+
+ _modifiedAndPreservedGP = 0;
+ _modifiedAndPreservedMM = 0;
+ _modifiedAndPreservedXMM = 0;
+
+ _pePushPopStackSize = 0;
+ _peMovStackSize = 0;
+ _peAdjustStackSize = 0;
+
+ _memStackSize = 0;
+ _memStackSize16 = 0;
+
+ _functionCallStackSize = 0;
+
+ _entryLabel = c->newLabel();
+ _exitLabel = c->newLabel();
+
+ _prolog = Compiler_newObject<EProlog>(c, this);
+ _epilog = Compiler_newObject<EEpilog>(c, this);
+ _end = Compiler_newObject<EFunctionEnd>(c);
+}
+
+EFunction::~EFunction() ASMJIT_NOTHROW
+{
+}
+
+void EFunction::prepare(CompilerContext& cc) ASMJIT_NOTHROW
+{
+ _offset = cc._currentOffset++;
+}
+
+int EFunction::getMaxSize() const ASMJIT_NOTHROW
+{
+ // EFunction is NOP.
+ return 0;
+}
+
+void EFunction::setPrototype(
+ uint32_t callingConvention,
+ const uint32_t* arguments,
+ uint32_t argumentsCount,
+ uint32_t returnValue) ASMJIT_NOTHROW
+{
+ _functionPrototype.setPrototype(callingConvention, arguments, argumentsCount, returnValue);
+}
+
+void EFunction::setHint(uint32_t hint, uint32_t value) ASMJIT_NOTHROW
+{
+ ASMJIT_ASSERT(hint < ASMJIT_ARRAY_SIZE(_hints));
+ _hints[hint] = value;
+}
+
+void EFunction::_createVariables() ASMJIT_NOTHROW
+{
+ uint32_t i, count = _functionPrototype.getArgumentsCount();
+ if (count == 0) return;
+
+ _argumentVariables = reinterpret_cast<VarData**>(_compiler->getZone().zalloc(count * sizeof(VarData*)));
+ if (_argumentVariables == NULL)
+ {
+ _compiler->setError(ERROR_NO_HEAP_MEMORY);
+ return;
+ }
+
+ char argNameStorage[64];
+ char* argName = NULL;
+
+ bool debug = _compiler->getLogger() != NULL;
+ if (debug) argName = argNameStorage;
+
+ for (i = 0; i < count; i++)
+ {
+ FunctionPrototype::Argument& a = _functionPrototype.getArguments()[i];
+ if (debug) snprintf(argName, ASMJIT_ARRAY_SIZE(argNameStorage), "arg_%u", i);
+
+ uint32_t size = getVariableSize(a.variableType);
+ VarData* vdata = _compiler->_newVarData(argName, a.variableType, size);
+
+ if (a.registerIndex != (uint32_t)INVALID_VALUE)
+ {
+ vdata->isRegArgument = true;
+ vdata->registerIndex = a.registerIndex;
+ }
+
+ if (a.stackOffset != (int32_t)INVALID_VALUE)
+ {
+ vdata->isMemArgument = true;
+ vdata->homeMemoryOffset = a.stackOffset;
+ }
+
+ _argumentVariables[i] = vdata;
+ }
+}
+
+void EFunction::_prepareVariables(Emittable* first) ASMJIT_NOTHROW
+{
+ uint32_t i, count = _functionPrototype.getArgumentsCount();
+ if (count == 0) return;
+
+ for (i = 0; i < count; i++)
+ {
+ VarData* vdata = _argumentVariables[i];
+
+ // This is where variable scope starts.
+ vdata->firstEmittable = first;
+ // If this will not be changed then it will be deallocated immediately.
+ vdata->lastEmittable = first;
+ }
+}
+
+void EFunction::_allocVariables(CompilerContext& cc) ASMJIT_NOTHROW
+{
+ uint32_t i, count = _functionPrototype.getArgumentsCount();
+ if (count == 0) return;
+
+ for (i = 0; i < count; i++)
+ {
+ VarData* vdata = _argumentVariables[i];
+
+ if (vdata->firstEmittable != NULL ||
+ vdata->isRegArgument ||
+ vdata->isMemArgument)
+ {
+ // Variable is used.
+ if (vdata->registerIndex != INVALID_VALUE)
+ {
+ vdata->state = VARIABLE_STATE_REGISTER;
+ // If variable is in register -> mark it as changed so it will not be
+ // lost by first spill.
+ vdata->changed = true;
+ cc._allocatedVariable(vdata);
+ }
+ else if (vdata->isMemArgument)
+ {
+ vdata->state = VARIABLE_STATE_MEMORY;
+ }
+ }
+ else
+ {
+ // Variable is not used.
+ vdata->registerIndex = INVALID_VALUE;
+ }
+ }
+}
+
+void EFunction::_preparePrologEpilog(CompilerContext& cc) ASMJIT_NOTHROW
+{
+ const CpuInfo* cpuInfo = getCpuInfo();
+
+ _pePushPop = true;
+ _emitEMMS = false;
+ _emitSFence = false;
+ _emitLFence = false;
+
+ uint32_t accessibleMemoryBelowStack = 0;
+ if (_functionPrototype.getCallingConvention() == CALL_CONV_X64U)
+ accessibleMemoryBelowStack = 128;
+
+ if (_isCaller && (cc._memBytesTotal > 0 || _isStackAlignedByOsTo16Bytes))
+ _isEspAdjusted = true;
+
+ if (cc._memBytesTotal > accessibleMemoryBelowStack)
+ _isEspAdjusted = true;
+
+ if (_hints[FUNCTION_HINT_NAKED] != INVALID_VALUE)
+ _isNaked = (bool)_hints[FUNCTION_HINT_NAKED];
+
+ if (_hints[FUNCTION_HINT_PUSH_POP_SEQUENCE] != INVALID_VALUE)
+ _pePushPop = (bool)_hints[FUNCTION_HINT_PUSH_POP_SEQUENCE];
+
+ if (_hints[FUNCTION_HINT_EMMS] != INVALID_VALUE)
+ _emitEMMS = (bool)_hints[FUNCTION_HINT_EMMS];
+
+ if (_hints[FUNCTION_HINT_SFENCE] != INVALID_VALUE)
+ _emitSFence = (bool)_hints[FUNCTION_HINT_SFENCE];
+
+ if (_hints[FUNCTION_HINT_LFENCE] != INVALID_VALUE)
+ _emitLFence = (bool)_hints[FUNCTION_HINT_LFENCE];
+
+ if (!_isStackAlignedByOsTo16Bytes && !_isNaked && (cc._mem16BlocksCount > 0))
+ {
+ // Have to align stack to 16-bytes.
+ _isStackAlignedByFnTo16Bytes = true;
+ _isEspAdjusted = true;
+ }
+
+ _modifiedAndPreservedGP = cc._modifiedGPRegisters & _functionPrototype.getPreservedGP() & ~Util::maskFromIndex(REG_INDEX_ESP);
+ _modifiedAndPreservedMM = cc._modifiedMMRegisters & _functionPrototype.getPreservedMM();
+ _modifiedAndPreservedXMM = cc._modifiedXMMRegisters & _functionPrototype.getPreservedXMM();
+
+ _movDqaInstruction = (_isStackAlignedByOsTo16Bytes || !_isNaked) ? INST_MOVDQA : INST_MOVDQU;
+
+ // Prolog & Epilog stack size.
+ {
+ int32_t memGP = Util::bitCount(_modifiedAndPreservedGP) * sizeof(sysint_t);
+ int32_t memMM = Util::bitCount(_modifiedAndPreservedMM) * 8;
+ int32_t memXMM = Util::bitCount(_modifiedAndPreservedXMM) * 16;
+
+ if (_pePushPop)
+ {
+ _pePushPopStackSize = memGP;
+ _peMovStackSize = memXMM + Util::alignTo16(memMM);
+ }
+ else
+ {
+ _pePushPopStackSize = 0;
+ _peMovStackSize = memXMM + Util::alignTo16(memMM + memGP);
+ }
+ }
+
+ if (_isStackAlignedByFnTo16Bytes)
+ {
+ _peAdjustStackSize += Util::deltaTo16(_pePushPopStackSize);
+ }
+ else
+ {
+ int32_t v = 16 - sizeof(sysint_t);
+ if (!_isNaked) v -= sizeof(sysint_t);
+
+ v -= _pePushPopStackSize & 15;
+ if (v < 0) v += 16;
+ _peAdjustStackSize = v;
+
+ //_peAdjustStackSize += Util::deltaTo16(_pePushPopStackSize + v);
+ }
+
+ // Memory stack size.
+ _memStackSize = cc._memBytesTotal;
+ _memStackSize16 = Util::alignTo16(_memStackSize);
+
+ if (_isNaked)
+ {
+ cc._argumentsBaseReg = REG_INDEX_ESP;
+ cc._argumentsBaseOffset = (_isEspAdjusted)
+ ? (_functionCallStackSize + _memStackSize16 + _peMovStackSize + _pePushPopStackSize + _peAdjustStackSize)
+ : (_pePushPopStackSize);
+ }
+ else
+ {
+ cc._argumentsBaseReg = REG_INDEX_EBP;
+ cc._argumentsBaseOffset = sizeof(sysint_t);
+ }
+
+ cc._variablesBaseReg = REG_INDEX_ESP;
+ cc._variablesBaseOffset = _functionCallStackSize;
+ if (!_isEspAdjusted)
+ cc._variablesBaseOffset = -_memStackSize16 - _peMovStackSize - _peAdjustStackSize;
+}
+
+void EFunction::_dumpFunction(CompilerContext& cc) ASMJIT_NOTHROW
+{
+ Logger* logger = _compiler->getLogger();
+ ASMJIT_ASSERT(logger != NULL);
+
+ uint32_t i;
+ char _buf[1024];
+ char* p;
+
+ // Log function prototype.
+ {
+ uint32_t argumentsCount = _functionPrototype.getArgumentsCount();
+ bool first = true;
+
+ logger->logString("; Function Prototype:\n");
+ logger->logString(";\n");
+
+ for (i = 0; i < argumentsCount; i++)
+ {
+ const FunctionPrototype::Argument& a = _functionPrototype.getArguments()[i];
+ VarData* vdata = _argumentVariables[i];
+
+ if (first)
+ {
+ logger->logString("; IDX| Type | Sz | Home |\n");
+ logger->logString("; ---+----------+----+----------------+\n");
+ }
+
+ char* memHome = memHome = _buf;
+
+ if (a.registerIndex != INVALID_VALUE)
+ {
+ BaseReg regOp(a.registerIndex | REG_TYPE_GPN, 0);
+ dumpOperand(memHome, &regOp)[0] = '\0';
+ }
+ else
+ {
+ Mem memOp;
+ memOp._mem.base = REG_INDEX_ESP;
+ memOp._mem.displacement = a.stackOffset;
+ dumpOperand(memHome, &memOp)[0] = '\0';
+ }
+
+ logger->logFormat("; %-3u| %-9s| %-3u| %-15s|\n",
+ // Argument index.
+ i,
+ // Argument type.
+ vdata->type < _VARIABLE_TYPE_COUNT ? variableInfo[vdata->type].name : "invalid",
+ // Argument size.
+ vdata->size,
+ // Argument memory home.
+ memHome
+ );
+
+ first = false;
+ }
+ logger->logString(";\n");
+ }
+
+ // Log variables.
+ {
+ uint32_t variablesCount = (uint32_t)_compiler->_varData.getLength();
+ bool first = true;
+
+ logger->logString("; Variables:\n");
+ logger->logString(";\n");
+
+ for (i = 0; i < variablesCount; i++)
+ {
+ VarData* vdata = _compiler->_varData[i];
+
+ // If this variable is not related to this function then skip it.
+ if (vdata->scope != this) continue;
+
+ // Get some information about variable type.
+ const VariableInfo& vinfo = variableInfo[vdata->type];
+
+ if (first)
+ {
+ logger->logString("; ID | Type | Sz | Home | Register Access | Memory Access |\n");
+ logger->logString("; ---+----------+----+----------------+-------------------+-------------------+\n");
+ }
+
+ char* memHome = (char*)"[None]";
+ if (vdata->homeMemoryData != NULL)
+ {
+ VarMemBlock* memBlock = reinterpret_cast<VarMemBlock*>(vdata->homeMemoryData);
+ memHome = _buf;
+
+ Mem memOp;
+ if (vdata->isMemArgument)
+ {
+ const FunctionPrototype::Argument& a = _functionPrototype.getArguments()[i];
+
+ memOp._mem.base = cc._argumentsBaseReg;
+ memOp._mem.displacement += cc._argumentsBaseOffset;
+ memOp._mem.displacement += a.stackOffset;
+ }
+ else
+ {
+ memOp._mem.base = cc._variablesBaseReg;
+ memOp._mem.displacement += cc._variablesBaseOffset;
+ memOp._mem.displacement += memBlock->offset;
+ }
+ dumpOperand(memHome, &memOp)[0] = '\0';
+ }
+
+ logger->logFormat("; %-3u| %-9s| %-3u| %-15s| r=%-4uw=%-4ux=%-4u| r=%-4uw=%-4ux=%-4u|\n",
+ // Variable id.
+ (uint)(i & OPERAND_ID_VALUE_MASK),
+ // Variable type.
+ vdata->type < _VARIABLE_TYPE_COUNT ? vinfo.name : "invalid",
+ // Variable size.
+ vdata->size,
+ // Variable memory home.
+ memHome,
+ // Register access count.
+ (unsigned int)vdata->registerReadCount,
+ (unsigned int)vdata->registerWriteCount,
+ (unsigned int)vdata->registerRWCount,
+ // Memory access count.
+ (unsigned int)vdata->memoryReadCount,
+ (unsigned int)vdata->memoryWriteCount,
+ (unsigned int)vdata->memoryRWCount
+ );
+ first = false;
+ }
+ logger->logString(";\n");
+ }
+
+ // Log modified registers.
+ {
+ p = _buf;
+
+ uint32_t r;
+ uint32_t modifiedRegisters = 0;
+
+ for (r = 0; r < 3; r++)
+ {
+ bool first = true;
+ uint32_t regs;
+ uint32_t type;
+
+ switch (r)
+ {
+ case 0:
+ regs = cc._modifiedGPRegisters;
+ type = REG_TYPE_GPN;
+ p = Util::mycpy(p, "; GP : ");
+ break;
+ case 1:
+ regs = cc._modifiedMMRegisters;
+ type = REG_TYPE_MM;
+ p = Util::mycpy(p, "; MM : ");
+ break;
+ case 2:
+ regs = cc._modifiedXMMRegisters;
+ type = REG_TYPE_XMM;
+ p = Util::mycpy(p, "; XMM: ");
+ break;
+ default:
+ ASMJIT_ASSERT(0);
+ }
+
+ for (i = 0; i < REG_NUM_BASE; i++)
+ {
+ if ((regs & Util::maskFromIndex(i)) != 0)
+ {
+ if (!first) { *p++ = ','; *p++ = ' '; }
+ p = dumpRegister(p, type, i);
+ first = false;
+ modifiedRegisters++;
+ }
+ }
+ *p++ = '\n';
+ }
+ *p = '\0';
+
+ logger->logFormat("; Modified registers (%u):\n", (unsigned int)modifiedRegisters);
+ logger->logString(_buf);
+ }
+
+ logger->logString("\n");
+}
+
+void EFunction::_emitProlog(CompilerContext& cc) ASMJIT_NOTHROW
+{
+ uint32_t i, mask;
+ uint32_t preservedGP = _modifiedAndPreservedGP;
+ uint32_t preservedMM = _modifiedAndPreservedMM;
+ uint32_t preservedXMM = _modifiedAndPreservedXMM;
+
+ int32_t stackSubtract =
+ _functionCallStackSize +
+ _memStackSize16 +
+ _peMovStackSize +
+ _peAdjustStackSize;
+ int32_t nspPos;
+
+ if (_compiler->getLogger())
+ {
+ // Here function prolog starts.
+ _compiler->comment("Prolog");
+ }
+
+ // Emit standard prolog entry code (but don't do it if function is set to be
+ // naked).
+ //
+ // Also see the _prologEpilogStackAdjust variable. If function is naked (so
+ // prolog and epilog will not contain "push ebp" and "mov ebp, esp", we need
+ // to adjust stack by 8 bytes in 64-bit mode (this will give us that stack
+ // will remain aligned to 16 bytes).
+ if (!_isNaked)
+ {
+ _compiler->emit(INST_PUSH, nbp);
+ _compiler->emit(INST_MOV, nbp, nsp);
+ }
+
+ // Align manually stack-pointer to 16-bytes.
+ if (_isStackAlignedByFnTo16Bytes)
+ {
+ ASMJIT_ASSERT(!_isNaked);
+ _compiler->emit(INST_AND, nsp, imm(-16));
+ }
+
+ // Save GP registers using PUSH/POP.
+ if (preservedGP && _pePushPop)
+ {
+ for (i = 0, mask = 1; i < REG_NUM_GP; i++, mask <<= 1)
+ {
+ if (preservedGP & mask) _compiler->emit(INST_PUSH, gpn(i));
+ }
+ }
+
+ if (_isEspAdjusted)
+ {
+ nspPos = _memStackSize16;
+ if (stackSubtract) _compiler->emit(INST_SUB, nsp, imm(stackSubtract));
+ }
+ else
+ {
+ nspPos = -(_peMovStackSize + _peAdjustStackSize);
+ //if (_pePushPop) nspPos += Util::bitCount(preservedGP) * sizeof(sysint_t);
+ }
+
+ // Save XMM registers using MOVDQA/MOVDQU.
+ if (preservedXMM)
+ {
+ for (i = 0, mask = 1; i < REG_NUM_XMM; i++, mask <<= 1)
+ {
+ if (preservedXMM & mask)
+ {
+ _compiler->emit(_movDqaInstruction, dqword_ptr(nsp, nspPos), xmm(i));
+ nspPos += 16;
+ }
+ }
+ }
+
+ // Save MM registers using MOVQ.
+ if (preservedMM)
+ {
+ for (i = 0, mask = 1; i < 8; i++, mask <<= 1)
+ {
+ if (preservedMM & mask)
+ {
+ _compiler->emit(INST_MOVQ, qword_ptr(nsp, nspPos), mm(i));
+ nspPos += 8;
+ }
+ }
+ }
+
+ // Save GP registers using MOV.
+ if (preservedGP && !_pePushPop)
+ {
+ for (i = 0, mask = 1; i < REG_NUM_GP; i++, mask <<= 1)
+ {
+ if (preservedGP & mask)
+ {
+ _compiler->emit(INST_MOV, sysint_ptr(nsp, nspPos), gpn(i));
+ nspPos += sizeof(sysint_t);
+ }
+ }
+ }
+
+ if (_compiler->getLogger())
+ {
+ _compiler->comment("Body");
+ }
+}
+
+void EFunction::_emitEpilog(CompilerContext& cc) ASMJIT_NOTHROW
+{
+ const CpuInfo* cpuInfo = getCpuInfo();
+
+ uint32_t i, mask;
+ uint32_t preservedGP = _modifiedAndPreservedGP;
+ uint32_t preservedMM = _modifiedAndPreservedMM;
+ uint32_t preservedXMM = _modifiedAndPreservedXMM;
+
+ int32_t stackAdd =
+ _functionCallStackSize +
+ _memStackSize16 +
+ _peMovStackSize +
+ _peAdjustStackSize;
+ int32_t nspPos;
+
+ nspPos = (_isEspAdjusted)
+ ? (_memStackSize16)
+ : -(_peMovStackSize + _peAdjustStackSize);
+
+ if (_compiler->getLogger())
+ {
+ _compiler->comment("Epilog");
+ }
+
+ // Restore XMM registers using MOVDQA/MOVDQU.
+ if (preservedXMM)
+ {
+ for (i = 0, mask = 1; i < REG_NUM_XMM; i++, mask <<= 1)
+ {
+ if (preservedXMM & mask)
+ {
+ _compiler->emit(_movDqaInstruction, xmm(i), dqword_ptr(nsp, nspPos));
+ nspPos += 16;
+ }
+ }
+ }
+
+ // Restore MM registers using MOVQ.
+ if (preservedMM)
+ {
+ for (i = 0, mask = 1; i < 8; i++, mask <<= 1)
+ {
+ if (preservedMM & mask)
+ {
+ _compiler->emit(INST_MOVQ, mm(i), qword_ptr(nsp, nspPos));
+ nspPos += 8;
+ }
+ }
+ }
+
+ // Restore GP registers using MOV.
+ if (preservedGP && !_pePushPop)
+ {
+ for (i = 0, mask = 1; i < REG_NUM_GP; i++, mask <<= 1)
+ {
+ if (preservedGP & mask)
+ {
+ _compiler->emit(INST_MOV, gpn(i), sysint_ptr(nsp, nspPos));
+ nspPos += sizeof(sysint_t);
+ }
+ }
+ }
+
+ if (_isEspAdjusted && stackAdd != 0)
+ _compiler->emit(INST_ADD, nsp, imm(stackAdd));
+
+ // Restore GP registers using POP.
+ if (preservedGP && _pePushPop)
+ {
+ for (i = REG_NUM_GP - 1, mask = 1 << i; (int32_t)i >= 0; i--, mask >>= 1)
+ {
+ if (preservedGP & mask)
+ {
+ _compiler->emit(INST_POP, gpn(i));
+ }
+ }
+ }
+
+ // Emit Emms.
+ if (_emitEMMS) _compiler->emit(INST_EMMS);
+
+ // Emit SFence / LFence / MFence.
+ if ( _emitSFence && _emitLFence) _compiler->emit(INST_MFENCE); // MFence == SFence & LFence.
+ if ( _emitSFence && !_emitLFence) _compiler->emit(INST_SFENCE); // Only SFence.
+ if (!_emitSFence && _emitLFence) _compiler->emit(INST_LFENCE); // Only LFence.
+
+ // Emit standard epilog leave code (if needed).
+ if (!_isNaked)
+ {
+ if (cpuInfo->vendorId == CPU_VENDOR_AMD)
+ {
+ // AMD seems to prefer LEAVE instead of MOV/POP sequence.
+ _compiler->emit(INST_LEAVE);
+ }
+ else
+ {
+ _compiler->emit(INST_MOV, nsp, nbp);
+ _compiler->emit(INST_POP, nbp);
+ }
+ }
+
+ // Emit return using correct instruction.
+ if (_functionPrototype.getCalleePopsStack())
+ _compiler->emit(INST_RET, imm((int16_t)_functionPrototype.getArgumentsStackSize()));
+ else
+ _compiler->emit(INST_RET);
+}
+
+void EFunction::reserveStackForFunctionCall(int32_t size)
+{
+ size = Util::alignTo16(size);
+
+ if (size > _functionCallStackSize) _functionCallStackSize = size;
+ _isCaller = true;
+}
+
+// ============================================================================
+// [AsmJit::EProlog]
+// ============================================================================
+
+EProlog::EProlog(Compiler* c, EFunction* f) ASMJIT_NOTHROW :
+ Emittable(c, EMITTABLE_PROLOG),
+ _function(f)
+{
+}
+
+EProlog::~EProlog() ASMJIT_NOTHROW
+{
+}
+
+void EProlog::prepare(CompilerContext& cc) ASMJIT_NOTHROW
+{
+ _offset = cc._currentOffset++;
+ _function->_prepareVariables(this);
+}
+
+Emittable* EProlog::translate(CompilerContext& cc) ASMJIT_NOTHROW
+{
+ _function->_allocVariables(cc);
+ return translated();
+}
+
+// ============================================================================
+// [AsmJit::EEpilog]
+// ============================================================================
+
+EEpilog::EEpilog(Compiler* c, EFunction* f) ASMJIT_NOTHROW :
+ Emittable(c, EMITTABLE_EPILOG),
+ _function(f)
+{
+}
+
+EEpilog::~EEpilog() ASMJIT_NOTHROW
+{
+}
+
+void EEpilog::prepare(CompilerContext& cc) ASMJIT_NOTHROW
+{
+ _offset = cc._currentOffset++;
+}
+
+Emittable* EEpilog::translate(CompilerContext& cc) ASMJIT_NOTHROW
+{
+ return translated();
+}
+
+// ============================================================================
+// [AsmJit::ECall]
+// ============================================================================
+
+ECall::ECall(Compiler* c, EFunction* caller, const Operand* target) ASMJIT_NOTHROW :
+ Emittable(c, EMITTABLE_CALL),
+ _caller(caller),
+ _target(*target),
+ _args(NULL),
+ _gpParams(0),
+ _mmParams(0),
+ _xmmParams(0),
+ _variablesCount(0),
+ _variables(NULL)
+{
+}
+
+ECall::~ECall() ASMJIT_NOTHROW
+{
+ memset(_argumentToVarRecord, 0, sizeof(VarCallRecord*) * FUNC_MAX_ARGS);
+}
+
+void ECall::prepare(CompilerContext& cc) ASMJIT_NOTHROW
+{
+ // Prepare is similar to EInstruction::prepare(). We collect unique variables
+ // and update statistics, but we don't use standard alloc/free register calls.
+ //
+ // The calling function is also unique in variable allocator point of view,
+ // because we need to alloc some variables that may be destroyed be the
+ // callee (okay, may not, but this is not guaranteed).
+ _offset = cc._currentOffset;
+
+ // Tell EFunction that another function will be called inside. It needs this
+ // information to reserve stack for the call and to mark esp adjustable.
+ getCaller()->reserveStackForFunctionCall(
+ (int32_t)getPrototype().getArgumentsStackSize());
+
+ uint32_t i;
+ uint32_t argumentsCount = getPrototype().getArgumentsCount();
+ uint32_t operandsCount = argumentsCount;
+ uint32_t variablesCount = 0;
+
+ // Create registers used as arguments mask.
+ for (i = 0; i < argumentsCount; i++)
+ {
+ const FunctionPrototype::Argument& fArg = getPrototype().getArguments()[i];
+
+ if (fArg.registerIndex != INVALID_VALUE)
+ {
+ switch (fArg.variableType)
+ {
+ case VARIABLE_TYPE_GPD:
+ case VARIABLE_TYPE_GPQ:
+ _gpParams |= Util::maskFromIndex(fArg.registerIndex);
+ break;
+ case VARIABLE_TYPE_MM:
+ _mmParams |= Util::maskFromIndex(fArg.registerIndex);
+ break;
+ case VARIABLE_TYPE_XMM:
+ case VARIABLE_TYPE_XMM_1F:
+ case VARIABLE_TYPE_XMM_4F:
+ case VARIABLE_TYPE_XMM_1D:
+ case VARIABLE_TYPE_XMM_2D:
+ _xmmParams |= Util::maskFromIndex(fArg.registerIndex);
+ break;
+ default:
+ ASMJIT_ASSERT(0);
+ }
+ }
+ else
+ {
+ cc.getFunction()->mustAdjustEsp();
+ }
+ }
+
+ // Call address.
+ operandsCount++;
+
+ // The first and the second return value.
+ if (!_ret[0].isNone()) operandsCount++;
+ if (!_ret[1].isNone()) operandsCount++;
+
+#define __GET_VARIABLE(__vardata__) \
+ { \
+ VarData* _candidate = __vardata__; \
+ \
+ for (var = cur; ;) \
+ { \
+ if (var == _variables) \
+ { \
+ var = cur++; \
+ var->vdata = _candidate; \
+ break; \
+ } \
+ \
+ var--; \
+ \
+ if (var->vdata == _candidate) \
+ { \
+ break; \
+ } \
+ } \
+ \
+ ASMJIT_ASSERT(var != NULL); \
+ }
+
+ for (i = 0; i < operandsCount; i++)
+ {
+ Operand& o = (i < argumentsCount)
+ ? (_args[i])
+ : (i == argumentsCount ? _target : _ret[i - argumentsCount - 1]);
+
+ if (o.isVar())
+ {
+ ASMJIT_ASSERT(o.getId() != INVALID_VALUE);
+ VarData* vdata = _compiler->_getVarData(o.getId());
+ ASMJIT_ASSERT(vdata != NULL);
+
+ if (vdata->workOffset == _offset) continue;
+ if (!cc._isActive(vdata)) cc._addActive(vdata);
+
+ vdata->workOffset = _offset;
+ variablesCount++;
+ }
+ else if (o.isMem())
+ {
+ if ((o.getId() & OPERAND_ID_TYPE_MASK) == OPERAND_ID_TYPE_VAR)
+ {
+ VarData* vdata = _compiler->_getVarData(o.getId());
+ ASMJIT_ASSERT(vdata != NULL);
+
+ cc._markMemoryUsed(vdata);
+ if (!cc._isActive(vdata)) cc._addActive(vdata);
+
+ continue;
+ }
+ else if ((o._mem.base & OPERAND_ID_TYPE_MASK) == OPERAND_ID_TYPE_VAR)
+ {
+ VarData* vdata = _compiler->_getVarData(o._mem.base);
+ ASMJIT_ASSERT(vdata != NULL);
+
+ if (vdata->workOffset == _offset) continue;
+ if (!cc._isActive(vdata)) cc._addActive(vdata);
+
+ vdata->workOffset = _offset;
+ variablesCount++;
+ }
+
+ if ((o._mem.index & OPERAND_ID_TYPE_MASK) == OPERAND_ID_TYPE_VAR)
+ {
+ VarData* vdata = _compiler->_getVarData(o._mem.index);
+ ASMJIT_ASSERT(vdata != NULL);
+
+ if (vdata->workOffset == _offset) continue;
+ if (!cc._isActive(vdata)) cc._addActive(vdata);
+
+ vdata->workOffset = _offset;
+ variablesCount++;
+ }
+ }
+ }
+
+ // Traverse all active variables and set their firstCallable pointer to this
+ // call. This information can be used to choose between the preserved-first
+ // and preserved-last register allocation.
+ if (cc._active)
+ {
+ VarData* first = cc._active;
+ VarData* active = first;
+ do {
+ if (active->firstCallable == NULL) active->firstCallable = this;
+ active = active->nextActive;
+ } while (active != first);
+ }
+
+ if (!variablesCount)
+ {
+ cc._currentOffset++;
+ return;
+ }
+
+ _variables = reinterpret_cast<VarCallRecord*>(_compiler->getZone().zalloc(sizeof(VarCallRecord) * variablesCount));
+ if (!_variables)
+ {
+ _compiler->setError(ERROR_NO_HEAP_MEMORY);
+ cc._currentOffset++;
+ return;
+ }
+
+ _variablesCount = variablesCount;
+ memset(_variables, 0, sizeof(VarCallRecord) * variablesCount);
+
+ VarCallRecord* cur = _variables;
+ VarCallRecord* var = NULL;
+
+ for (i = 0; i < operandsCount; i++)
+ {
+ Operand& o = (i < argumentsCount)
+ ? (_args[i])
+ : (i == argumentsCount ? _target : _ret[i - argumentsCount - 1]);
+
+ if (o.isVar())
+ {
+ VarData* vdata = _compiler->_getVarData(o.getId());
+ ASMJIT_ASSERT(vdata != NULL);
+
+ __GET_VARIABLE(vdata)
+ _argumentToVarRecord[i] = var;
+
+ if (i < argumentsCount)
+ {
+ const FunctionPrototype::Argument& fArg = getPrototype().getArguments()[i];
+
+ if (fArg.registerIndex != INVALID_VALUE)
+ {
+ cc._newRegisterHomeIndex(vdata, fArg.registerIndex);
+
+ switch (fArg.variableType)
+ {
+ case VARIABLE_TYPE_GPD:
+ case VARIABLE_TYPE_GPQ:
+ var->flags |= VarCallRecord::FLAG_IN_GP;
+ var->inCount++;
+ break;
+
+ case VARIABLE_TYPE_MM:
+ var->flags |= VarCallRecord::FLAG_IN_MM;
+ var->inCount++;
+ break;
+
+ case VARIABLE_TYPE_XMM:
+ case VARIABLE_TYPE_XMM_1F:
+ case VARIABLE_TYPE_XMM_4F:
+ case VARIABLE_TYPE_XMM_1D:
+ case VARIABLE_TYPE_XMM_2D:
+ var->flags |= VarCallRecord::FLAG_IN_XMM;
+ var->inCount++;
+ break;
+
+ default:
+ ASMJIT_ASSERT(0);
+ }
+ }
+ else
+ {
+ var->inCount++;
+ }
+
+ vdata->registerReadCount++;
+ }
+ else if (i == argumentsCount)
+ {
+ uint32_t mask = ~getPrototype().getPreservedGP() &
+ ~getPrototype().getPassedGP() &
+ Util::maskUpToIndex(REG_NUM_GP);
+
+ cc._newRegisterHomeIndex(vdata, Util::findFirstBit(mask));
+ cc._newRegisterHomeMask(vdata, mask);
+
+ var->flags |= VarCallRecord::FLAG_CALL_OPERAND_REG;
+ vdata->registerReadCount++;
+ }
+ else
+ {
+ switch (vdata->type)
+ {
+ case VARIABLE_TYPE_GPD:
+ case VARIABLE_TYPE_GPQ:
+ if (i == argumentsCount+1)
+ var->flags |= VarCallRecord::FLAG_OUT_EAX;
+ else
+ var->flags |= VarCallRecord::FLAG_OUT_EDX;
+ break;
+
+ case VARIABLE_TYPE_X87:
+ case VARIABLE_TYPE_X87_1F:
+ case VARIABLE_TYPE_X87_1D:
+#if defined(ASMJIT_X86)
+ if (i == argumentsCount+1)
+ var->flags |= VarCallRecord::FLAG_OUT_ST0;
+ else
+ var->flags |= VarCallRecord::FLAG_OUT_ST1;
+#else
+ if (i == argumentsCount+1)
+ var->flags |= VarCallRecord::FLAG_OUT_XMM0;
+ else
+ var->flags |= VarCallRecord::FLAG_OUT_XMM1;
+#endif
+ break;
+
+ case VARIABLE_TYPE_MM:
+ var->flags |= VarCallRecord::FLAG_OUT_MM0;
+ break;
+
+ case VARIABLE_TYPE_XMM:
+ case VARIABLE_TYPE_XMM_4F:
+ case VARIABLE_TYPE_XMM_2D:
+ if (i == argumentsCount+1)
+ var->flags |= VarCallRecord::FLAG_OUT_XMM0;
+ else
+ var->flags |= VarCallRecord::FLAG_OUT_XMM1;
+ break;
+
+ case VARIABLE_TYPE_XMM_1F:
+ case VARIABLE_TYPE_XMM_1D:
+#if defined(ASMJIT_X86)
+ if (i == argumentsCount+1)
+ var->flags |= VarCallRecord::FLAG_OUT_ST0;
+ else
+ var->flags |= VarCallRecord::FLAG_OUT_ST1;
+#else
+ if (i == argumentsCount+1)
+ var->flags |= VarCallRecord::FLAG_OUT_XMM0;
+ else
+ var->flags |= VarCallRecord::FLAG_OUT_XMM1;
+#endif
+ break;
+
+ default:
+ ASMJIT_ASSERT(0);
+ }
+
+ vdata->registerWriteCount++;
+ }
+ }
+ else if (o.isMem())
+ {
+ ASMJIT_ASSERT(i == argumentsCount);
+
+ if ((o.getId() & OPERAND_ID_TYPE_MASK) == OPERAND_ID_TYPE_VAR)
+ {
+ VarData* vdata = _compiler->_getVarData(o.getId());
+ ASMJIT_ASSERT(vdata != NULL);
+
+ vdata->memoryReadCount++;
+ }
+ else if ((o._mem.base & OPERAND_ID_TYPE_MASK) == OPERAND_ID_TYPE_VAR)
+ {
+ VarData* vdata = _compiler->_getVarData(reinterpret_cast<Mem&>(o).getBase());
+ ASMJIT_ASSERT(vdata != NULL);
+
+ vdata->registerReadCount++;
+
+ __GET_VARIABLE(vdata)
+ var->flags |= VarCallRecord::FLAG_CALL_OPERAND_REG | VarCallRecord::FLAG_CALL_OPERAND_MEM;
+ }
+
+ if ((o._mem.index & OPERAND_ID_TYPE_MASK) == OPERAND_ID_TYPE_VAR)
+ {
+ VarData* vdata = _compiler->_getVarData(reinterpret_cast<Mem&>(o).getIndex());
+ ASMJIT_ASSERT(vdata != NULL);
+
+ vdata->registerReadCount++;
+
+ __GET_VARIABLE(vdata)
+ var->flags |= VarCallRecord::FLAG_CALL_OPERAND_REG | VarCallRecord::FLAG_CALL_OPERAND_MEM;
+ }
+ }
+ }
+
+ // Traverse all variables and update firstEmittable / lastEmittable. This
+ // function is called from iterator that scans emittables using forward
+ // direction so we can use this knowledge to optimize the process.
+ //
+ // Same code is in EInstruction::prepare().
+ for (i = 0; i < _variablesCount; i++)
+ {
+ VarData* v = _variables[i].vdata;
+
+ // First emittable (begin of variable scope).
+ if (v->firstEmittable == NULL) v->firstEmittable = this;
+
+ // Last emittable (end of variable scope).
+ v->lastEmittable = this;
+ }
+
+ cc._currentOffset++;
+
+#undef __GET_VARIABLE
+}
+
+Emittable* ECall::translate(CompilerContext& cc) ASMJIT_NOTHROW
+{
+ uint32_t i;
+ uint32_t preserved, mask;
+
+ uint32_t temporaryGpReg;
+ uint32_t temporaryXmmReg;
+
+ uint32_t offset = cc._currentOffset;
+ Compiler* compiler = cc.getCompiler();
+
+ // Constants.
+ const FunctionPrototype::Argument* targs = getPrototype().getArguments();
+
+ uint32_t argumentsCount = getPrototype().getArgumentsCount();
+ uint32_t variablesCount = _variablesCount;
+
+ // Processed arguments.
+ uint8_t processed[FUNC_MAX_ARGS] = { 0 };
+
+ compiler->comment("Function Call");
+
+ // These variables are used by the instruction and we set current offset
+ // to their work offsets -> The getSpillCandidate() method never returns
+ // the variable used by this instruction.
+ for (i = 0; i < variablesCount; i++)
+ {
+ _variables[i].vdata->workOffset = offset;
+
+ // Init back-reference to VarCallRecord.
+ _variables[i].vdata->tempPtr = &_variables[i];
+ }
+
+ // --------------------------------------------------------------------------
+ // STEP 1:
+ //
+ // Spill variables which are not used by the function call and have to
+ // be destroyed. These registers may be used by callee.
+ // --------------------------------------------------------------------------
+
+ preserved = getPrototype().getPreservedGP();
+ for (i = 0, mask = 1; i < REG_NUM_GP; i++, mask <<= 1)
+ {
+ VarData* vdata = cc._state.gp[i];
+ if (vdata && vdata->workOffset != offset && (preserved & mask) == 0)
+ {
+ cc.spillGPVar(vdata);
+ }
+ }
+
+ preserved = getPrototype().getPreservedMM();
+ for (i = 0, mask = 1; i < REG_NUM_MM; i++, mask <<= 1)
+ {
+ VarData* vdata = cc._state.mm[i];
+ if (vdata && vdata->workOffset != offset && (preserved & mask) == 0)
+ {
+ cc.spillMMVar(vdata);
+ }
+ }
+
+ preserved = getPrototype().getPreservedXMM();
+ for (i = 0, mask = 1; i < REG_NUM_XMM; i++, mask <<= 1)
+ {
+ VarData* vdata = cc._state.xmm[i];
+ if (vdata && vdata->workOffset != offset && (preserved & mask) == 0)
+ {
+ cc.spillXMMVar(vdata);
+ }
+ }
+
+ // --------------------------------------------------------------------------
+ // STEP 2:
+ //
+ // Move all arguments to the stack which all already in registers.
+ // --------------------------------------------------------------------------
+
+ for (i = 0; i < argumentsCount; i++)
+ {
+ if (processed[i]) continue;
+
+ const FunctionPrototype::Argument& argType = targs[i];
+ if (argType.registerIndex != INVALID_VALUE) continue;
+
+ Operand& operand = _args[i];
+
+ if (operand.isVar())
+ {
+ VarCallRecord* rec = _argumentToVarRecord[i];
+ VarData* vdata = compiler->_getVarData(operand.getId());
+
+ if (vdata->registerIndex != INVALID_VALUE)
+ {
+ _moveAllocatedVariableToStack(cc,
+ vdata, argType);
+
+ rec->inDone++;
+ processed[i] = true;
+ }
+ }
+ }
+
+ // --------------------------------------------------------------------------
+ // STEP 3:
+ //
+ // Spill all non-preserved variables we moved to stack in STEP #2.
+ // --------------------------------------------------------------------------
+
+ for (i = 0; i < argumentsCount; i++)
+ {
+ VarCallRecord* rec = _argumentToVarRecord[i];
+ if (!rec || processed[i]) continue;
+
+ if (rec->inDone >= rec->inCount)
+ {
+ VarData* vdata = rec->vdata;
+ if (vdata->registerIndex == INVALID_VALUE) continue;
+
+ if (rec->outCount)
+ {
+ // Variable will be rewritten by function return value, it's not needed
+ // to spill it. It will be allocated again by ECall.
+ cc.unuseVar(rec->vdata, VARIABLE_STATE_UNUSED);
+ }
+ else
+ {
+ switch (vdata->type)
+ {
+ case VARIABLE_TYPE_GPD:
+ case VARIABLE_TYPE_GPQ:
+ if ((getPrototype().getPreservedGP() & Util::maskFromIndex(vdata->registerIndex)) == 0)
+ cc.spillGPVar(vdata);
+ break;
+ case VARIABLE_TYPE_MM:
+ if ((getPrototype().getPreservedMM() & Util::maskFromIndex(vdata->registerIndex)) == 0)
+ cc.spillMMVar(vdata);
+ break;
+ case VARIABLE_TYPE_XMM:
+ case VARIABLE_TYPE_XMM_1F:
+ case VARIABLE_TYPE_XMM_1D:
+ case VARIABLE_TYPE_XMM_4F:
+ case VARIABLE_TYPE_XMM_2D:
+ if ((getPrototype().getPreservedXMM() & Util::maskFromIndex(vdata->registerIndex)) == 0)
+ cc.spillXMMVar(vdata);
+ break;
+ }
+ }
+ }
+ }
+
+ // --------------------------------------------------------------------------
+ // STEP 4:
+ //
+ // Get temporary register that we can use to pass input function arguments.
+ // Now it's safe to do, because the non-needed variables should be spilled.
+ // --------------------------------------------------------------------------
+
+ temporaryGpReg = _findTemporaryGpRegister(cc);
+ temporaryXmmReg = _findTemporaryXmmRegister(cc);
+
+ // If failed to get temporary register then we need just to pick one.
+ if (temporaryGpReg == INVALID_VALUE)
+ {
+ // TODO.
+ }
+ if (temporaryXmmReg == INVALID_VALUE)
+ {
+ // TODO.
+ }
+
+ // --------------------------------------------------------------------------
+ // STEP 5:
+ //
+ // Move all remaining arguments to the stack (we can use temporary register).
+ // or allocate it to the primary register. Also move immediates.
+ // --------------------------------------------------------------------------
+
+ for (i = 0; i < argumentsCount; i++)
+ {
+ if (processed[i]) continue;
+
+ const FunctionPrototype::Argument& argType = targs[i];
+ if (argType.registerIndex != INVALID_VALUE) continue;
+
+ Operand& operand = _args[i];
+
+ if (operand.isVar())
+ {
+ VarCallRecord* rec = _argumentToVarRecord[i];
+ VarData* vdata = compiler->_getVarData(operand.getId());
+
+ _moveSpilledVariableToStack(cc,
+ vdata, argType,
+ temporaryGpReg, temporaryXmmReg);
+
+ rec->inDone++;
+ processed[i] = true;
+ }
+ else if (operand.isImm())
+ {
+ // TODO.
+ }
+ }
+
+ // --------------------------------------------------------------------------
+ // STEP 6:
+ //
+ // Allocate arguments to registers.
+ // --------------------------------------------------------------------------
+
+ bool didWork;
+
+ do {
+ didWork = false;
+
+ for (i = 0; i < argumentsCount; i++)
+ {
+ if (processed[i]) continue;
+
+ VarCallRecord* rsrc = _argumentToVarRecord[i];
+
+ Operand& osrc = _args[i];
+ ASMJIT_ASSERT(osrc.isVar());
+ VarData* vsrc = compiler->_getVarData(osrc.getId());
+
+ const FunctionPrototype::Argument& srcArgType = targs[i];
+ VarData* vdst = _getOverlappingVariable(cc, srcArgType);
+
+ if (vsrc == vdst)
+ {
+ rsrc->inDone++;
+ processed[i] = true;
+
+ didWork = true;
+ continue;
+ }
+ else if (vdst != NULL)
+ {
+ VarCallRecord* rdst = reinterpret_cast<VarCallRecord*>(vdst->tempPtr);
+
+ if (rdst->inDone >= rdst->inCount && (rdst->flags & VarCallRecord::FLAG_CALL_OPERAND_REG) == 0)
+ {
+ // Safe to spill.
+ if (rdst->outCount || vdst->lastEmittable == this)
+ cc.unuseVar(vdst, VARIABLE_STATE_UNUSED);
+ else
+ cc.spillVar(vdst);
+ vdst = NULL;
+ }
+ else
+ {
+ uint32_t x = getPrototype().findArgumentByRegisterCode(
+ getVariableRegisterCode(vsrc->type, vsrc->registerIndex));
+ bool doSpill = true;
+
+ if ((getVariableClass(vdst->type) & VariableInfo::CLASS_GP) != 0)
+ {
+ // Try to emit mov to register which is possible for call() operand.
+ if (x == INVALID_VALUE && (rdst->flags & VarCallRecord::FLAG_CALL_OPERAND_REG) != 0)
+ {
+ uint32_t rIndex;
+ uint32_t rBit;
+
+ // The mask which contains registers which are not-preserved
+ // (these that might be clobbered by the callee) and which are
+ // not used to pass function arguments. Each register contained
+ // in this mask is ideal to be used by call() instruction.
+ uint32_t possibleMask = ~getPrototype().getPreservedGP() &
+ ~getPrototype().getPassedGP() &
+ Util::maskUpToIndex(REG_NUM_GP);
+
+ if (possibleMask != 0)
+ {
+ for (rIndex = 0, rBit = 1; rIndex < REG_NUM_GP; rIndex++, rBit <<= 1)
+ {
+ if ((possibleMask & rBit) != 0)
+ {
+ if (cc._state.gp[rIndex] == NULL)
+ {
+ // This is the best possible solution, the register is
+ // free. We do not need to continue with this loop, the
+ // rIndex will be used by the call().
+ break;
+ }
+ else
+ {
+ // Wait until the register is freed or try to find another.
+ doSpill = false;
+ didWork = true;
+ }
+ }
+ }
+ }
+ else
+ {
+ // Try to find a register which is free and which is not used
+ // to pass a function argument.
+ possibleMask = getPrototype().getPreservedGP();
+
+ for (rIndex = 0, rBit = 1; rIndex < REG_NUM_GP; rIndex++, rBit <<= 1)
+ {
+ if ((possibleMask & rBit) != 0)
+ {
+ // Found one.
+ if (cc._state.gp[rIndex] == NULL) break;
+ }
+ }
+ }
+
+ if (rIndex < REG_NUM_GP)
+ {
+ if (temporaryGpReg == vsrc->registerIndex) temporaryGpReg = rIndex;
+ compiler->emit(INST_MOV, gpn(rIndex), gpn(vsrc->registerIndex));
+
+ cc._state.gp[vsrc->registerIndex] = NULL;
+ cc._state.gp[rIndex] = vsrc;
+
+ vsrc->registerIndex = rIndex;
+ cc._allocatedGPRegister(rIndex);
+
+ doSpill = false;
+ didWork = true;
+ }
+ }
+ // Emit xchg instead of spill/alloc if possible.
+ else if (x != INVALID_VALUE)
+ {
+ const FunctionPrototype::Argument& dstArgType = targs[x];
+ if (getVariableClass(dstArgType.variableType) == getVariableClass(srcArgType.variableType))
+ {
+ uint32_t dstIndex = vdst->registerIndex;
+ uint32_t srcIndex = vsrc->registerIndex;
+
+ if (srcIndex == dstArgType.registerIndex)
+ {
+#if defined(ASMJIT_X64)
+ if (vdst->type != VARIABLE_TYPE_GPD || vsrc->type != VARIABLE_TYPE_GPD)
+ compiler->emit(INST_XCHG, gpq(dstIndex), gpq(srcIndex));
+ else
+#endif
+ compiler->emit(INST_XCHG, gpd(dstIndex), gpd(srcIndex));
+
+ cc._state.gp[srcIndex] = vdst;
+ cc._state.gp[dstIndex] = vsrc;
+
+ vdst->registerIndex = srcIndex;
+ vsrc->registerIndex = dstIndex;
+
+ rdst->inDone++;
+ rsrc->inDone++;
+
+ processed[i] = true;
+ processed[x] = true;
+
+ doSpill = false;
+ }
+ }
+ }
+ }
+
+ if (doSpill)
+ {
+ cc.spillVar(vdst);
+ vdst = NULL;
+ }
+ }
+ }
+
+ if (vdst == NULL)
+ {
+ VarCallRecord* rec = reinterpret_cast<VarCallRecord*>(vsrc->tempPtr);
+
+ _moveSrcVariableToRegister(cc, vsrc, srcArgType);
+
+ switch (srcArgType.variableType)
+ {
+ case VARIABLE_TYPE_GPD:
+ case VARIABLE_TYPE_GPQ:
+ cc._markGPRegisterModified(srcArgType.registerIndex);
+ break;
+ case VARIABLE_TYPE_MM:
+ cc._markMMRegisterModified(srcArgType.registerIndex);
+ break;
+ case VARIABLE_TYPE_XMM:
+ case VARIABLE_TYPE_XMM_1F:
+ case VARIABLE_TYPE_XMM_1D:
+ case VARIABLE_TYPE_XMM_4F:
+ case VARIABLE_TYPE_XMM_2D:
+ cc._markMMRegisterModified(srcArgType.registerIndex);
+ break;
+ }
+
+ rec->inDone++;
+ processed[i] = true;
+ }
+ }
+ } while (didWork);
+
+ // --------------------------------------------------------------------------
+ // STEP 7:
+ //
+ // Allocate operand used by CALL instruction.
+ // --------------------------------------------------------------------------
+
+ for (i = 0; i < variablesCount; i++)
+ {
+ VarCallRecord& r = _variables[i];
+ if ((r.flags & VarCallRecord::FLAG_CALL_OPERAND_REG) &&
+ (r.vdata->registerIndex == INVALID_VALUE))
+ {
+ // If the register is not allocated and the call form is 'call reg' then
+ // it's possible to keep it in memory.
+ if ((r.flags & VarCallRecord::FLAG_CALL_OPERAND_MEM) == 0)
+ {
+ _target = GPVarFromData(r.vdata).m();
+ break;
+ }
+
+ if (temporaryGpReg == INVALID_VALUE)
+ temporaryGpReg = _findTemporaryGpRegister(cc);
+
+ cc.allocGPVar(r.vdata,
+ Util::maskFromIndex(temporaryGpReg),
+ VARIABLE_ALLOC_REGISTER | VARIABLE_ALLOC_READ);
+ }
+ }
+
+ cc.translateOperands(&_target, 1);
+
+ // --------------------------------------------------------------------------
+ // STEP 8:
+ //
+ // Spill all preserved variables.
+ // --------------------------------------------------------------------------
+
+ preserved = getPrototype().getPreservedGP();
+ for (i = 0, mask = 1; i < REG_NUM_GP; i++, mask <<= 1)
+ {
+ VarData* vdata = cc._state.gp[i];
+ if (vdata && (preserved & mask) == 0)
+ {
+ VarCallRecord* rec = reinterpret_cast<VarCallRecord*>(vdata->tempPtr);
+ if (rec && (rec->outCount || rec->flags & VarCallRecord::FLAG_UNUSE_AFTER_USE || vdata->lastEmittable == this))
+ cc.unuseVar(vdata, VARIABLE_STATE_UNUSED);
+ else
+ cc.spillGPVar(vdata);
+ }
+ }
+
+ preserved = getPrototype().getPreservedMM();
+ for (i = 0, mask = 1; i < REG_NUM_MM; i++, mask <<= 1)
+ {
+ VarData* vdata = cc._state.mm[i];
+ if (vdata && (preserved & mask) == 0)
+ {
+ VarCallRecord* rec = reinterpret_cast<VarCallRecord*>(vdata->tempPtr);
+ if (rec && (rec->outCount || vdata->lastEmittable == this))
+ cc.unuseVar(vdata, VARIABLE_STATE_UNUSED);
+ else
+ cc.spillMMVar(vdata);
+ }
+ }
+
+ preserved = getPrototype().getPreservedXMM();
+ for (i = 0, mask = 1; i < REG_NUM_XMM; i++, mask <<= 1)
+ {
+ VarData* vdata = cc._state.xmm[i];
+ if (vdata && (preserved & mask) == 0)
+ {
+ VarCallRecord* rec = reinterpret_cast<VarCallRecord*>(vdata->tempPtr);
+ if (rec && (rec->outCount || vdata->lastEmittable == this))
+ cc.unuseVar(vdata, VARIABLE_STATE_UNUSED);
+ else
+ cc.spillXMMVar(vdata);
+ }
+ }
+
+ // --------------------------------------------------------------------------
+ // STEP 9:
+ //
+ // Emit CALL instruction.
+ // --------------------------------------------------------------------------
+
+ compiler->emit(INST_CALL, _target);
+
+ // Restore the stack offset.
+ if (getPrototype().getCalleePopsStack())
+ {
+ int32_t s = (int32_t)getPrototype().getArgumentsStackSize();
+ if (s) compiler->emit(INST_SUB, nsp, imm(s));
+ }
+
+ // --------------------------------------------------------------------------
+ // STEP 10:
+ //
+ // Prepare others for return value(s) and cleanup.
+ // --------------------------------------------------------------------------
+
+ // Clear temp data, see AsmJit::VarData::temp why it's needed.
+ for (i = 0; i < variablesCount; i++)
+ {
+ VarCallRecord* rec = &_variables[i];
+ VarData* vdata = rec->vdata;
+
+ if (rec->flags & (VarCallRecord::FLAG_OUT_EAX | VarCallRecord::FLAG_OUT_EDX))
+ {
+ if (getVariableClass(vdata->type) & VariableInfo::CLASS_GP)
+ {
+ cc.allocGPVar(vdata,
+ Util::maskFromIndex((rec->flags & VarCallRecord::FLAG_OUT_EAX) != 0
+ ? REG_INDEX_EAX
+ : REG_INDEX_EDX),
+ VARIABLE_ALLOC_REGISTER | VARIABLE_ALLOC_WRITE);
+ vdata->changed = true;
+ }
+ }
+
+ if (rec->flags & (VarCallRecord::FLAG_OUT_MM0))
+ {
+ if (getVariableClass(vdata->type) & VariableInfo::CLASS_MM)
+ {
+ cc.allocMMVar(vdata, Util::maskFromIndex(REG_INDEX_MM0),
+ VARIABLE_ALLOC_REGISTER | VARIABLE_ALLOC_WRITE);
+ vdata->changed = true;
+ }
+ }
+
+ if (rec->flags & (VarCallRecord::FLAG_OUT_XMM0 | VarCallRecord::FLAG_OUT_XMM1))
+ {
+ if (getVariableClass(vdata->type) & VariableInfo::CLASS_XMM)
+ {
+ cc.allocXMMVar(vdata,
+ Util::maskFromIndex((rec->flags & VarCallRecord::FLAG_OUT_XMM0) != 0
+ ? REG_INDEX_XMM0
+ : REG_INDEX_XMM1),
+ VARIABLE_ALLOC_REGISTER | VARIABLE_ALLOC_WRITE);
+ vdata->changed = true;
+ }
+ }
+
+ if (rec->flags & (VarCallRecord::FLAG_OUT_ST0 | VarCallRecord::FLAG_OUT_ST1))
+ {
+ if (getVariableClass(vdata->type) & VariableInfo::CLASS_XMM)
+ {
+ Mem mem(cc._getVarMem(vdata));
+ cc.unuseVar(vdata, VARIABLE_STATE_MEMORY);
+
+ switch (vdata->type)
+ {
+ case VARIABLE_TYPE_XMM_1F:
+ case VARIABLE_TYPE_XMM_4F:
+ {
+ mem.setSize(4);
+ compiler->emit(INST_FSTP, mem);
+ break;
+ }
+ case VARIABLE_TYPE_XMM_1D:
+ case VARIABLE_TYPE_XMM_2D:
+ {
+ mem.setSize(8);
+ compiler->emit(INST_FSTP, mem);
+ break;
+ }
+ default:
+ {
+ compiler->comment("*** WARNING: Can't convert float return value to untyped XMM\n");
+ break;
+ }
+ }
+ }
+ }
+
+ // Cleanup.
+ vdata->tempPtr = NULL;
+ }
+
+ for (i = 0; i < variablesCount; i++)
+ {
+ cc._unuseVarOnEndOfScope(this, &_variables[i]);
+ }
+
+ return translated();
+}
+
+int ECall::getMaxSize() const ASMJIT_NOTHROW
+{
+ // TODO: Not optimal.
+ return 15;
+}
+
+bool ECall::_tryUnuseVar(VarData* v) ASMJIT_NOTHROW
+{
+ for (uint32_t i = 0; i < _variablesCount; i++)
+ {
+ if (_variables[i].vdata == v)
+ {
+ _variables[i].flags |= VarCallRecord::FLAG_UNUSE_AFTER_USE;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+uint32_t ECall::_findTemporaryGpRegister(CompilerContext& cc) ASMJIT_NOTHROW
+{
+ uint32_t i;
+ uint32_t mask;
+
+ uint32_t passedGP = getPrototype().getPassedGP();
+ uint32_t candidate = INVALID_VALUE;
+
+ // Find all registers used to pass function arguments. We shouldn't use these
+ // if possible.
+ for (i = 0, mask = 1; i < REG_NUM_GP; i++, mask <<= 1)
+ {
+ if (cc._state.gp[i] == NULL)
+ {
+ // If this register is used to pass arguments to function, we will mark
+ // it and use it only if there is no other one.
+ if ((passedGP & mask) != 0)
+ candidate = i;
+ else
+ return i;
+ }
+ }
+
+ return candidate;
+}
+
+uint32_t ECall::_findTemporaryXmmRegister(CompilerContext& cc) ASMJIT_NOTHROW
+{
+ uint32_t i;
+ uint32_t mask;
+
+ uint32_t passedXMM = getPrototype().getPassedXMM();
+ uint32_t candidate = INVALID_VALUE;
+
+ // Find all registers used to pass function arguments. We shouldn't use these
+ // if possible.
+ for (i = 0, mask = 1; i < REG_NUM_XMM; i++, mask <<= 1)
+ {
+ if (cc._state.xmm[i] == NULL)
+ {
+ // If this register is used to pass arguments to function, we will mark
+ // it and use it only if there is no other one.
+ if ((passedXMM & mask) != 0)
+ candidate = i;
+ else
+ return i;
+ }
+ }
+
+ return candidate;
+}
+
+VarData* ECall::_getOverlappingVariable(CompilerContext& cc,
+ const FunctionPrototype::Argument& argType) const ASMJIT_NOTHROW
+{
+ ASMJIT_ASSERT(argType.variableType != INVALID_VALUE);
+
+ switch (argType.variableType)
+ {
+ case VARIABLE_TYPE_GPD:
+ case VARIABLE_TYPE_GPQ:
+ return cc._state.gp[argType.registerIndex];
+ case VARIABLE_TYPE_MM:
+ return cc._state.mm[argType.registerIndex];
+ case VARIABLE_TYPE_XMM:
+ case VARIABLE_TYPE_XMM_1F:
+ case VARIABLE_TYPE_XMM_1D:
+ case VARIABLE_TYPE_XMM_4F:
+ case VARIABLE_TYPE_XMM_2D:
+ return cc._state.xmm[argType.registerIndex];
+ }
+
+ return NULL;
+}
+
+void ECall::_moveAllocatedVariableToStack(CompilerContext& cc, VarData* vdata, const FunctionPrototype::Argument& argType) ASMJIT_NOTHROW
+{
+ ASMJIT_ASSERT(argType.registerIndex == INVALID_VALUE);
+ ASMJIT_ASSERT(vdata->registerIndex != INVALID_VALUE);
+
+ Compiler* compiler = cc.getCompiler();
+
+ uint32_t src = vdata->registerIndex;
+ Mem dst = ptr(nsp, -(int)sizeof(sysint_t) + argType.stackOffset);
+
+ switch (vdata->type)
+ {
+ case VARIABLE_TYPE_GPD:
+ switch (argType.variableType)
+ {
+ case VARIABLE_TYPE_GPD:
+ compiler->emit(INST_MOV, dst, gpd(src));
+ return;
+#if defined(ASMJIT_X64)
+ case VARIABLE_TYPE_GPQ:
+ case VARIABLE_TYPE_MM:
+ compiler->emit(INST_MOV, dst, gpq(src));
+ return;
+#endif // ASMJIT_X64
+ }
+ break;
+
+#if defined(ASMJIT_X64)
+ case VARIABLE_TYPE_GPQ:
+ switch (argType.variableType)
+ {
+ case VARIABLE_TYPE_GPD:
+ compiler->emit(INST_MOV, dst, gpd(src));
+ return;
+ case VARIABLE_TYPE_GPQ:
+ compiler->emit(INST_MOV, dst, gpq(src));
+ return;
+ case VARIABLE_TYPE_MM:
+ compiler->emit(INST_MOVQ, dst, gpq(src));
+ return;
+ }
+ break;
+#endif // ASMJIT_X64
+
+ case VARIABLE_TYPE_MM:
+ switch (argType.variableType)
+ {
+ case VARIABLE_TYPE_GPD:
+ case VARIABLE_TYPE_X87_1F:
+ case VARIABLE_TYPE_XMM_1F:
+ compiler->emit(INST_MOVD, dst, mm(src));
+ return;
+ case VARIABLE_TYPE_GPQ:
+ case VARIABLE_TYPE_MM:
+ case VARIABLE_TYPE_X87_1D:
+ case VARIABLE_TYPE_XMM_1D:
+ compiler->emit(INST_MOVQ, dst, mm(src));
+ return;
+ }
+ break;
+
+ // We allow incompatible types here, because the called can convert them
+ // to correct format before function is called.
+
+ case VARIABLE_TYPE_XMM:
+ case VARIABLE_TYPE_XMM_4F:
+ case VARIABLE_TYPE_XMM_2D:
+ switch (argType.variableType)
+ {
+ case VARIABLE_TYPE_XMM:
+ compiler->emit(INST_MOVDQU, dst, xmm(src));
+ return;
+ case VARIABLE_TYPE_XMM_1F:
+ case VARIABLE_TYPE_XMM_4F:
+ compiler->emit(INST_MOVUPS, dst, xmm(src));
+ return;
+ case VARIABLE_TYPE_XMM_1D:
+ case VARIABLE_TYPE_XMM_2D:
+ compiler->emit(INST_MOVUPD, dst, xmm(src));
+ return;
+ }
+ break;
+
+ case VARIABLE_TYPE_XMM_1F:
+ switch (argType.variableType)
+ {
+ case VARIABLE_TYPE_X87_1F:
+ case VARIABLE_TYPE_XMM:
+ case VARIABLE_TYPE_XMM_1F:
+ case VARIABLE_TYPE_XMM_4F:
+ case VARIABLE_TYPE_XMM_1D:
+ case VARIABLE_TYPE_XMM_2D:
+ compiler->emit(INST_MOVSS, dst, xmm(src));
+ return;
+ }
+ break;
+
+ case VARIABLE_TYPE_XMM_1D:
+ switch (argType.variableType)
+ {
+ case VARIABLE_TYPE_X87_1D:
+ case VARIABLE_TYPE_XMM:
+ case VARIABLE_TYPE_XMM_1F:
+ case VARIABLE_TYPE_XMM_4F:
+ case VARIABLE_TYPE_XMM_1D:
+ case VARIABLE_TYPE_XMM_2D:
+ compiler->emit(INST_MOVSD, dst, xmm(src));
+ return;
+ }
+ break;
+ }
+
+ compiler->setError(ERROR_INCOMPATIBLE_ARGUMENT);
+}
+
+void ECall::_moveSpilledVariableToStack(CompilerContext& cc,
+ VarData* vdata, const FunctionPrototype::Argument& argType,
+ uint32_t temporaryGpReg,
+ uint32_t temporaryXmmReg) ASMJIT_NOTHROW
+{
+ ASMJIT_ASSERT(argType.registerIndex == INVALID_VALUE);
+ ASMJIT_ASSERT(vdata->registerIndex == INVALID_VALUE);
+
+ Compiler* compiler = cc.getCompiler();
+
+ Mem src = cc._getVarMem(vdata);
+ Mem dst = ptr(nsp, -(int)sizeof(sysint_t) + argType.stackOffset);
+
+ switch (vdata->type)
+ {
+ case VARIABLE_TYPE_GPD:
+ switch (argType.variableType)
+ {
+ case VARIABLE_TYPE_GPD:
+ compiler->emit(INST_MOV, gpd(temporaryGpReg), src);
+ compiler->emit(INST_MOV, dst, gpd(temporaryGpReg));
+ return;
+#if defined(ASMJIT_X64)
+ case VARIABLE_TYPE_GPQ:
+ case VARIABLE_TYPE_MM:
+ compiler->emit(INST_MOV, gpd(temporaryGpReg), src);
+ compiler->emit(INST_MOV, dst, gpq(temporaryGpReg));
+ return;
+#endif // ASMJIT_X64
+ }
+ break;
+
+#if defined(ASMJIT_X64)
+ case VARIABLE_TYPE_GPQ:
+ switch (argType.variableType)
+ {
+ case VARIABLE_TYPE_GPD:
+ compiler->emit(INST_MOV, gpd(temporaryGpReg), src);
+ compiler->emit(INST_MOV, dst, gpd(temporaryGpReg));
+ return;
+ case VARIABLE_TYPE_GPQ:
+ case VARIABLE_TYPE_MM:
+ compiler->emit(INST_MOV, gpq(temporaryGpReg), src);
+ compiler->emit(INST_MOV, dst, gpq(temporaryGpReg));
+ return;
+ }
+ break;
+#endif // ASMJIT_X64
+
+ case VARIABLE_TYPE_MM:
+ switch (argType.variableType)
+ {
+ case VARIABLE_TYPE_GPD:
+ case VARIABLE_TYPE_X87_1F:
+ case VARIABLE_TYPE_XMM_1F:
+ compiler->emit(INST_MOV, gpd(temporaryGpReg), src);
+ compiler->emit(INST_MOV, dst, gpd(temporaryGpReg));
+ return;
+ case VARIABLE_TYPE_GPQ:
+ case VARIABLE_TYPE_MM:
+ case VARIABLE_TYPE_X87_1D:
+ case VARIABLE_TYPE_XMM_1D:
+ // TODO
+ return;
+ }
+ break;
+
+ // We allow incompatible types here, because the called can convert them
+ // to correct format before function is called.
+
+ case VARIABLE_TYPE_XMM:
+ case VARIABLE_TYPE_XMM_4F:
+ case VARIABLE_TYPE_XMM_2D:
+ switch (argType.variableType)
+ {
+ case VARIABLE_TYPE_XMM:
+ compiler->emit(INST_MOVDQU, xmm(temporaryXmmReg), src);
+ compiler->emit(INST_MOVDQU, dst, xmm(temporaryXmmReg));
+ return;
+ case VARIABLE_TYPE_XMM_1F:
+ case VARIABLE_TYPE_XMM_4F:
+ compiler->emit(INST_MOVUPS, xmm(temporaryXmmReg), src);
+ compiler->emit(INST_MOVUPS, dst, xmm(temporaryXmmReg));
+ return;
+ case VARIABLE_TYPE_XMM_1D:
+ case VARIABLE_TYPE_XMM_2D:
+ compiler->emit(INST_MOVUPD, xmm(temporaryXmmReg), src);
+ compiler->emit(INST_MOVUPD, dst, xmm(temporaryXmmReg));
+ return;
+ }
+ break;
+
+ case VARIABLE_TYPE_XMM_1F:
+ switch (argType.variableType)
+ {
+ case VARIABLE_TYPE_X87_1F:
+ case VARIABLE_TYPE_XMM:
+ case VARIABLE_TYPE_XMM_1F:
+ case VARIABLE_TYPE_XMM_4F:
+ case VARIABLE_TYPE_XMM_1D:
+ case VARIABLE_TYPE_XMM_2D:
+ compiler->emit(INST_MOVSS, xmm(temporaryXmmReg), src);
+ compiler->emit(INST_MOVSS, dst, xmm(temporaryXmmReg));
+ return;
+ }
+ break;
+
+ case VARIABLE_TYPE_XMM_1D:
+ switch (argType.variableType)
+ {
+ case VARIABLE_TYPE_X87_1D:
+ case VARIABLE_TYPE_XMM:
+ case VARIABLE_TYPE_XMM_1F:
+ case VARIABLE_TYPE_XMM_4F:
+ case VARIABLE_TYPE_XMM_1D:
+ case VARIABLE_TYPE_XMM_2D:
+ compiler->emit(INST_MOVSD, xmm(temporaryXmmReg), src);
+ compiler->emit(INST_MOVSD, dst, xmm(temporaryXmmReg));
+ return;
+ }
+ break;
+ }
+
+ compiler->setError(ERROR_INCOMPATIBLE_ARGUMENT);
+}
+
+void ECall::_moveSrcVariableToRegister(CompilerContext& cc,
+ VarData* vdata, const FunctionPrototype::Argument& argType) ASMJIT_NOTHROW
+{
+ uint32_t dst = argType.registerIndex;
+ uint32_t src = vdata->registerIndex;
+
+ Compiler* compiler = cc.getCompiler();
+
+ if (src != INVALID_VALUE)
+ {
+ switch (argType.variableType)
+ {
+ case VARIABLE_TYPE_GPD:
+ switch (vdata->type)
+ {
+ case VARIABLE_TYPE_GPD:
+#if defined(ASMJIT_X64)
+ case VARIABLE_TYPE_GPQ:
+#endif // ASMJIT_X64
+ compiler->emit(INST_MOV, gpd(dst), gpd(src));
+ return;
+ case VARIABLE_TYPE_MM:
+ compiler->emit(INST_MOVD, gpd(dst), mm(src));
+ return;
+ }
+ break;
+
+#if defined(ASMJIT_X64)
+ case VARIABLE_TYPE_GPQ:
+ switch (vdata->type)
+ {
+ case VARIABLE_TYPE_GPD:
+ compiler->emit(INST_MOV, gpd(dst), gpd(src));
+ return;
+ case VARIABLE_TYPE_GPQ:
+ compiler->emit(INST_MOV, gpq(dst), gpq(src));
+ return;
+ case VARIABLE_TYPE_MM:
+ compiler->emit(INST_MOVQ, gpq(dst), mm(src));
+ return;
+ }
+ break;
+#endif // ASMJIT_X64
+
+ case VARIABLE_TYPE_MM:
+ switch (vdata->type)
+ {
+ case VARIABLE_TYPE_GPD:
+ compiler->emit(INST_MOVD, gpd(dst), gpd(src));
+ return;
+#if defined(ASMJIT_X64)
+ case VARIABLE_TYPE_GPQ:
+ compiler->emit(INST_MOVQ, gpq(dst), gpq(src));
+ return;
+#endif // ASMJIT_X64
+ case VARIABLE_TYPE_MM:
+ compiler->emit(INST_MOVQ, mm(dst), mm(src));
+ return;
+ }
+ break;
+
+ case VARIABLE_TYPE_XMM:
+ case VARIABLE_TYPE_XMM_4F:
+ case VARIABLE_TYPE_XMM_2D:
+ switch (vdata->type)
+ {
+ case VARIABLE_TYPE_GPD:
+ compiler->emit(INST_MOVD, xmm(dst), gpd(src));
+ return;
+#if defined(ASMJIT_X64)
+ case VARIABLE_TYPE_GPQ:
+ compiler->emit(INST_MOVQ, xmm(dst), gpq(src));
+ return;
+#endif // ASMJIT_X64
+ case VARIABLE_TYPE_MM:
+ compiler->emit(INST_MOVQ, xmm(dst), mm(src));
+ return;
+ case VARIABLE_TYPE_XMM:
+ case VARIABLE_TYPE_XMM_1F:
+ case VARIABLE_TYPE_XMM_4F:
+ case VARIABLE_TYPE_XMM_1D:
+ case VARIABLE_TYPE_XMM_2D:
+ compiler->emit(INST_MOVDQA, xmm(dst), xmm(src));
+ return;
+ }
+ break;
+
+ case VARIABLE_TYPE_XMM_1F:
+ switch (vdata->type)
+ {
+ case VARIABLE_TYPE_MM:
+ compiler->emit(INST_MOVQ, xmm(dst), mm(src));
+ return;
+
+ case VARIABLE_TYPE_XMM:
+ compiler->emit(INST_MOVDQA, xmm(dst), xmm(src));
+ return;
+ case VARIABLE_TYPE_XMM_1F:
+ case VARIABLE_TYPE_XMM_4F:
+ compiler->emit(INST_MOVSS, xmm(dst), xmm(src));
+ return;
+ case VARIABLE_TYPE_XMM_1D:
+ case VARIABLE_TYPE_XMM_2D:
+ compiler->emit(INST_CVTSD2SS, xmm(dst), xmm(src));
+ return;
+ }
+ break;
+
+ case VARIABLE_TYPE_XMM_1D:
+ switch (vdata->type)
+ {
+ case VARIABLE_TYPE_MM:
+ compiler->emit(INST_MOVQ, xmm(dst), mm(src));
+ return;
+
+ case VARIABLE_TYPE_XMM:
+ compiler->emit(INST_MOVDQA, xmm(dst), xmm(src));
+ return;
+ case VARIABLE_TYPE_XMM_1F:
+ case VARIABLE_TYPE_XMM_4F:
+ compiler->emit(INST_CVTSS2SD, xmm(dst), xmm(src));
+ return;
+ case VARIABLE_TYPE_XMM_1D:
+ case VARIABLE_TYPE_XMM_2D:
+ compiler->emit(INST_MOVSD, xmm(dst), xmm(src));
+ return;
+ }
+ break;
+ }
+ }
+ else
+ {
+ Mem mem = cc._getVarMem(vdata);
+
+ switch (argType.variableType)
+ {
+ case VARIABLE_TYPE_GPD:
+ switch (vdata->type)
+ {
+ case VARIABLE_TYPE_GPD:
+#if defined(ASMJIT_X64)
+ case VARIABLE_TYPE_GPQ:
+#endif // ASMJIT_X64
+ compiler->emit(INST_MOV, gpd(dst), mem);
+ return;
+ case VARIABLE_TYPE_MM:
+ compiler->emit(INST_MOVD, gpd(dst), mem);
+ return;
+ }
+ break;
+
+#if defined(ASMJIT_X64)
+ case VARIABLE_TYPE_GPQ:
+ switch (vdata->type)
+ {
+ case VARIABLE_TYPE_GPD:
+ compiler->emit(INST_MOV, gpd(dst), mem);
+ return;
+ case VARIABLE_TYPE_GPQ:
+ compiler->emit(INST_MOV, gpq(dst), mem);
+ return;
+ case VARIABLE_TYPE_MM:
+ compiler->emit(INST_MOVQ, gpq(dst), mem);
+ return;
+ }
+ break;
+#endif // ASMJIT_X64
+
+ case VARIABLE_TYPE_MM:
+ switch (vdata->type)
+ {
+ case VARIABLE_TYPE_GPD:
+ compiler->emit(INST_MOVD, gpd(dst), mem);
+ return;
+#if defined(ASMJIT_X64)
+ case VARIABLE_TYPE_GPQ:
+ compiler->emit(INST_MOVQ, gpq(dst), mem);
+ return;
+#endif // ASMJIT_X64
+ case VARIABLE_TYPE_MM:
+ compiler->emit(INST_MOVQ, mm(dst), mem);
+ return;
+ }
+ break;
+
+ case VARIABLE_TYPE_XMM:
+ case VARIABLE_TYPE_XMM_4F:
+ case VARIABLE_TYPE_XMM_2D:
+ switch (vdata->type)
+ {
+ case VARIABLE_TYPE_GPD:
+ compiler->emit(INST_MOVD, xmm(dst), mem);
+ return;
+#if defined(ASMJIT_X64)
+ case VARIABLE_TYPE_GPQ:
+ compiler->emit(INST_MOVQ, xmm(dst), mem);
+ return;
+#endif // ASMJIT_X64
+ case VARIABLE_TYPE_MM:
+ compiler->emit(INST_MOVQ, xmm(dst), mem);
+ return;
+ case VARIABLE_TYPE_XMM:
+ case VARIABLE_TYPE_XMM_1F:
+ case VARIABLE_TYPE_XMM_4F:
+ case VARIABLE_TYPE_XMM_1D:
+ case VARIABLE_TYPE_XMM_2D:
+ compiler->emit(INST_MOVDQA, xmm(dst), mem);
+ return;
+ }
+ break;
+
+ case VARIABLE_TYPE_XMM_1F:
+ switch (vdata->type)
+ {
+ case VARIABLE_TYPE_MM:
+ compiler->emit(INST_MOVQ, xmm(dst), mem);
+ return;
+
+ case VARIABLE_TYPE_XMM:
+ compiler->emit(INST_MOVDQA, xmm(dst), mem);
+ return;
+ case VARIABLE_TYPE_XMM_1F:
+ case VARIABLE_TYPE_XMM_4F:
+ compiler->emit(INST_MOVSS, xmm(dst), mem);
+ return;
+ case VARIABLE_TYPE_XMM_1D:
+ case VARIABLE_TYPE_XMM_2D:
+ compiler->emit(INST_CVTSD2SS, xmm(dst), mem);
+ return;
+ }
+ break;
+
+ case VARIABLE_TYPE_XMM_1D:
+ switch (vdata->type)
+ {
+ case VARIABLE_TYPE_MM:
+ compiler->emit(INST_MOVQ, xmm(dst), mem);
+ return;
+
+ case VARIABLE_TYPE_XMM:
+ compiler->emit(INST_MOVDQA, xmm(dst), mem);
+ return;
+ case VARIABLE_TYPE_XMM_1F:
+ case VARIABLE_TYPE_XMM_4F:
+ compiler->emit(INST_CVTSS2SD, xmm(dst), mem);
+ return;
+ case VARIABLE_TYPE_XMM_1D:
+ case VARIABLE_TYPE_XMM_2D:
+ compiler->emit(INST_MOVSD, xmm(dst), mem);
+ return;
+ }
+ break;
+ }
+ }
+
+ compiler->setError(ERROR_INCOMPATIBLE_ARGUMENT);
+}
+
+// Prototype & Arguments Management.
+void ECall::_setPrototype(
+ uint32_t callingConvention,
+ const uint32_t* arguments,
+ uint32_t argumentsCount,
+ uint32_t returnValue) ASMJIT_NOTHROW
+{
+ _functionPrototype.setPrototype(callingConvention, arguments, argumentsCount, returnValue);
+
+ _args = reinterpret_cast<Operand*>(
+ getCompiler()->getZone().zalloc(sizeof(Operand) * argumentsCount));
+ memset(_args, 0, sizeof(Operand) * argumentsCount);
+}
+
+bool ECall::setArgument(uint32_t i, const BaseVar& var) ASMJIT_NOTHROW
+{
+ ASMJIT_ASSERT(i < _functionPrototype.getArgumentsCount());
+ if (i >= _functionPrototype.getArgumentsCount()) return false;
+
+ _args[i] = var;
+ return true;
+}
+
+bool ECall::setArgument(uint32_t i, const Imm& imm) ASMJIT_NOTHROW
+{
+ ASMJIT_ASSERT(i < _functionPrototype.getArgumentsCount());
+ if (i >= _functionPrototype.getArgumentsCount()) return false;
+
+ _args[i] = imm;
+ return true;
+}
+
+bool ECall::setReturn(const Operand& first, const Operand& second) ASMJIT_NOTHROW
+{
+ _ret[0] = first;
+ _ret[1] = second;
+
+ return true;
+}
+
+// ============================================================================
+// [AsmJit::ERet]
+// ============================================================================
+
+ERet::ERet(Compiler* c, EFunction* function, const Operand* first, const Operand* second) ASMJIT_NOTHROW :
+ Emittable(c, EMITTABLE_RET),
+ _function(function)
+{
+ if (first ) _ret[0] = *first;
+ if (second) _ret[1] = *second;
+
+/*
+ // TODO:?
+
+ // Check whether the return value is compatible.
+ uint32_t retValType = function->getPrototype().getReturnValue();
+ bool valid = false;
+
+ switch (retValType)
+ {
+ case VARIABLE_TYPE_GPD:
+ case VARIABLE_TYPE_GPQ:
+ if ((_ret[0].isVar() && (reinterpret_cast<const BaseVar&>(_ret[0]).isGPVar())) ||
+ (_ret[0].isImm()))
+ {
+ valid = true;
+ }
+ break;
+
+ case VARIABLE_TYPE_X87:
+ case VARIABLE_TYPE_X87_1F:
+ case VARIABLE_TYPE_X87_1D:
+ if ((_ret[0].isVar() && (reinterpret_cast<const BaseVar&>(_ret[0]).isX87Var() ||
+ reinterpret_cast<const BaseVar&>(_ret[0]).isXMMVar() )) )
+ {
+ valid = true;
+ }
+ break;
+
+ case VARIABLE_TYPE_MM:
+ break;
+
+ case INVALID_VALUE:
+ if (_ret[0].isNone() &&
+ _ret[1].isNone())
+ {
+ valid = true;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ // Incompatible return value.
+ if (!valid)
+ {
+ c->setError(ERROR_INCOMPATIBLE_RETURN_VALUE);
+ }
+*/
+}
+
+ERet::~ERet() ASMJIT_NOTHROW
+{
+}
+
+void ERet::prepare(CompilerContext& cc) ASMJIT_NOTHROW
+{
+ _offset = cc._currentOffset;
+
+ uint32_t retValType = getFunction()->getPrototype().getReturnValue();
+ if (retValType != INVALID_VALUE)
+ {
+ uint32_t i;
+ for (i = 0; i < 2; i++)
+ {
+ Operand& o = _ret[i];
+
+ if (o.isVar())
+ {
+ ASMJIT_ASSERT(o.getId() != INVALID_VALUE);
+ VarData* vdata = _compiler->_getVarData(o.getId());
+ ASMJIT_ASSERT(vdata != NULL);
+
+ // First emittable (begin of variable scope).
+ if (vdata->firstEmittable == NULL) vdata->firstEmittable = this;
+
+ // Last emittable (end of variable scope).
+ vdata->lastEmittable = this;
+
+ if (vdata->workOffset == _offset) continue;
+ if (!cc._isActive(vdata)) cc._addActive(vdata);
+
+ vdata->workOffset = _offset;
+ vdata->registerReadCount++;
+
+ if (isVariableInteger(vdata->type) && isVariableInteger(retValType))
+ {
+ cc._newRegisterHomeIndex(vdata, (i == 0) ? REG_INDEX_EAX : REG_INDEX_EDX);
+ }
+ }
+ }
+ }
+
+ cc._currentOffset++;
+}
+
+Emittable* ERet::translate(CompilerContext& cc) ASMJIT_NOTHROW
+{
+ Compiler* compiler = cc.getCompiler();
+
+ // Check whether the return value is compatible.
+ uint32_t retValType = getFunction()->getPrototype().getReturnValue();
+ uint32_t i;
+
+ switch (retValType)
+ {
+ case VARIABLE_TYPE_GPD:
+ case VARIABLE_TYPE_GPQ:
+ for (i = 0; i < 2; i++)
+ {
+ uint32_t dsti = (i == 0) ? REG_INDEX_EAX : REG_INDEX_EDX;
+ uint32_t srci;
+
+ if (_ret[i].isVar())
+ {
+ if (reinterpret_cast<const BaseVar&>(_ret[i]).isGPVar())
+ {
+ VarData* vdata = compiler->_getVarData(_ret[i].getId());
+ ASMJIT_ASSERT(vdata != NULL);
+
+ srci = vdata->registerIndex;
+ if (srci == INVALID_VALUE)
+ compiler->emit(INST_MOV, gpn(dsti), cc._getVarMem(vdata));
+ else if (dsti != srci)
+ compiler->emit(INST_MOV, gpn(dsti), gpn(srci));
+ }
+ }
+ else if (_ret[i].isImm())
+ {
+ compiler->emit(INST_MOV, gpn(dsti), _ret[i]);
+ }
+ }
+ break;
+
+ case VARIABLE_TYPE_X87:
+ case VARIABLE_TYPE_X87_1F:
+ case VARIABLE_TYPE_X87_1D:
+ // There is case that we need to return two values (Unix-ABI specific):
+ // - FLD #2
+ //- FLD #1
+ i = 2;
+ do {
+ i--;
+ uint32_t dsti = i;
+ uint32_t srci;
+
+ if (_ret[i].isVar())
+ {
+ if (reinterpret_cast<const BaseVar&>(_ret[i]).isX87Var())
+ {
+ // TODO: X87.
+ }
+ else if (reinterpret_cast<const BaseVar&>(_ret[i]).isXMMVar())
+ {
+ VarData* vdata = compiler->_getVarData(_ret[i].getId());
+ ASMJIT_ASSERT(vdata != NULL);
+
+ srci = vdata->registerIndex;
+ if (srci != INVALID_VALUE) cc.saveXMMVar(vdata);
+
+ switch (vdata->type)
+ {
+ case VARIABLE_TYPE_XMM_1F:
+ case VARIABLE_TYPE_XMM_4F:
+ compiler->emit(INST_FLD, _baseVarMem(reinterpret_cast<BaseVar&>(_ret[i]), 4));
+ break;
+ case VARIABLE_TYPE_XMM_1D:
+ case VARIABLE_TYPE_XMM_2D:
+ compiler->emit(INST_FLD, _baseVarMem(reinterpret_cast<BaseVar&>(_ret[i]), 8));
+ break;
+ }
+ }
+ }
+ } while (i != 0);
+ break;
+
+ case VARIABLE_TYPE_MM:
+ for (i = 0; i < 2; i++)
+ {
+ uint32_t dsti = i;
+ uint32_t srci;
+
+ if (_ret[i].isVar())
+ {
+ if (reinterpret_cast<const BaseVar&>(_ret[i]).isGPVar())
+ {
+ VarData* vdata = compiler->_getVarData(_ret[i].getId());
+ ASMJIT_ASSERT(vdata != NULL);
+
+ srci = vdata->registerIndex;
+ uint32_t inst = _ret[i].isRegType(REG_TYPE_GPQ) ? INST_MOVQ : INST_MOVD;
+
+ if (srci == INVALID_VALUE)
+ compiler->emit(inst, mm(dsti), cc._getVarMem(vdata));
+ else
+#if defined(ASMJIT_X86)
+ compiler->emit(inst, mm(dsti), gpd(srci));
+#else
+ compiler->emit(inst, mm(dsti), _ret[i].isRegType(REG_TYPE_GPQ) ? gpq(srci) : gpd(srci));
+#endif
+ }
+ else if (reinterpret_cast<const BaseVar&>(_ret[i]).isMMVar())
+ {
+ VarData* vdata = compiler->_getVarData(_ret[i].getId());
+ ASMJIT_ASSERT(vdata != NULL);
+
+ srci = vdata->registerIndex;
+ uint32_t inst = INST_MOVQ;
+
+ if (srci == INVALID_VALUE)
+ compiler->emit(inst, mm(dsti), cc._getVarMem(vdata));
+ else if (dsti != srci)
+ compiler->emit(inst, mm(dsti), mm(srci));
+ }
+ else if (reinterpret_cast<const BaseVar&>(_ret[i]).isXMMVar())
+ {
+ VarData* vdata = compiler->_getVarData(_ret[i].getId());
+ ASMJIT_ASSERT(vdata != NULL);
+
+ srci = vdata->registerIndex;
+ uint32_t inst = INST_MOVQ;
+ if (reinterpret_cast<const BaseVar&>(_ret[i]).getVariableType() == VARIABLE_TYPE_XMM_1F) inst = INST_MOVD;
+
+ if (srci == INVALID_VALUE)
+ compiler->emit(inst, mm(dsti), cc._getVarMem(vdata));
+ else
+ compiler->emit(inst, mm(dsti), xmm(srci));
+ }
+ }
+ }
+ break;
+
+ case VARIABLE_TYPE_XMM:
+ case VARIABLE_TYPE_XMM_4F:
+ case VARIABLE_TYPE_XMM_2D:
+ for (i = 0; i < 2; i++)
+ {
+ uint32_t dsti = i;
+ uint32_t srci;
+
+ if (_ret[i].isVar())
+ {
+ if (reinterpret_cast<const BaseVar&>(_ret[i]).isGPVar())
+ {
+ VarData* vdata = compiler->_getVarData(_ret[i].getId());
+ ASMJIT_ASSERT(vdata != NULL);
+
+ srci = vdata->registerIndex;
+ uint32_t inst = _ret[i].isRegType(REG_TYPE_GPQ) ? INST_MOVQ : INST_MOVD;
+
+ if (srci == INVALID_VALUE)
+ compiler->emit(inst, xmm(dsti), cc._getVarMem(vdata));
+ else
+#if defined(ASMJIT_X86)
+ compiler->emit(inst, xmm(dsti), gpd(srci));
+#else
+ compiler->emit(inst, xmm(dsti), _ret[i].isRegType(REG_TYPE_GPQ) ? gpq(srci) : gpd(srci));
+#endif
+ }
+ else if (reinterpret_cast<const BaseVar&>(_ret[i]).isX87Var())
+ {
+ // TODO: X87.
+ }
+ else if (reinterpret_cast<const BaseVar&>(_ret[i]).isMMVar())
+ {
+ VarData* vdata = compiler->_getVarData(_ret[i].getId());
+ ASMJIT_ASSERT(vdata != NULL);
+
+ srci = vdata->registerIndex;
+ if (srci == INVALID_VALUE)
+ compiler->emit(INST_MOVQ, xmm(dsti), cc._getVarMem(vdata));
+ else
+ compiler->emit(INST_MOVQ, xmm(dsti), mm(srci));
+ }
+ else if (reinterpret_cast<const BaseVar&>(_ret[i]).isXMMVar())
+ {
+ VarData* vdata = compiler->_getVarData(_ret[i].getId());
+ ASMJIT_ASSERT(vdata != NULL);
+
+ srci = vdata->registerIndex;
+ if (srci == INVALID_VALUE)
+ compiler->emit(INST_MOVDQA, xmm(dsti), cc._getVarMem(vdata));
+ else if (dsti != srci)
+ compiler->emit(INST_MOVDQA, xmm(dsti), xmm(srci));
+ }
+ }
+ }
+ break;
+
+ case VARIABLE_TYPE_XMM_1F:
+ for (i = 0; i < 2; i++)
+ {
+ uint32_t dsti = i;
+ uint32_t srci;
+
+ if (_ret[i].isVar())
+ {
+ if (reinterpret_cast<const BaseVar&>(_ret[i]).isX87Var())
+ {
+ // TODO: X87.
+ }
+ else if (reinterpret_cast<const BaseVar&>(_ret[i]).isXMMVar())
+ {
+ VarData* vdata = compiler->_getVarData(_ret[i].getId());
+ ASMJIT_ASSERT(vdata != NULL);
+
+ srci = vdata->registerIndex;
+ switch (vdata->type)
+ {
+ case VARIABLE_TYPE_XMM:
+ if (srci == INVALID_VALUE)
+ compiler->emit(INST_MOVDQA, xmm(dsti), cc._getVarMem(vdata));
+ else if (dsti != srci)
+ compiler->emit(INST_MOVDQA, xmm(dsti), xmm(srci));
+ break;
+ case VARIABLE_TYPE_XMM_1F:
+ case VARIABLE_TYPE_XMM_4F:
+ if (srci == INVALID_VALUE)
+ compiler->emit(INST_MOVSS, xmm(dsti), cc._getVarMem(vdata));
+ else
+ compiler->emit(INST_MOVSS, xmm(dsti), xmm(srci));
+ break;
+ case VARIABLE_TYPE_XMM_1D:
+ case VARIABLE_TYPE_XMM_2D:
+ if (srci == INVALID_VALUE)
+ compiler->emit(INST_CVTSD2SS, xmm(dsti), cc._getVarMem(vdata));
+ else if (dsti != srci)
+ compiler->emit(INST_CVTSD2SS, xmm(dsti), xmm(srci));
+ break;
+ }
+ }
+ }
+ }
+ break;
+
+ case VARIABLE_TYPE_XMM_1D:
+ for (i = 0; i < 2; i++)
+ {
+ uint32_t dsti = i;
+ uint32_t srci;
+
+ if (_ret[i].isVar())
+ {
+ if (reinterpret_cast<const BaseVar&>(_ret[i]).isX87Var())
+ {
+ // TODO: X87.
+ }
+ else if (reinterpret_cast<const BaseVar&>(_ret[i]).isXMMVar())
+ {
+ VarData* vdata = compiler->_getVarData(_ret[i].getId());
+ ASMJIT_ASSERT(vdata != NULL);
+
+ srci = vdata->registerIndex;
+ switch (vdata->type)
+ {
+ case VARIABLE_TYPE_XMM:
+ if (srci == INVALID_VALUE)
+ compiler->emit(INST_MOVDQA, xmm(dsti), cc._getVarMem(vdata));
+ else if (dsti != srci)
+ compiler->emit(INST_MOVDQA, xmm(dsti), xmm(srci));
+ break;
+ case VARIABLE_TYPE_XMM_1F:
+ case VARIABLE_TYPE_XMM_4F:
+ if (srci == INVALID_VALUE)
+ compiler->emit(INST_CVTSS2SD, xmm(dsti), cc._getVarMem(vdata));
+ else
+ compiler->emit(INST_CVTSS2SD, xmm(dsti), xmm(srci));
+ break;
+ case VARIABLE_TYPE_XMM_1D:
+ case VARIABLE_TYPE_XMM_2D:
+ if (srci == INVALID_VALUE)
+ compiler->emit(INST_MOVSD, xmm(dsti), cc._getVarMem(vdata));
+ else
+ compiler->emit(INST_MOVSD, xmm(dsti), xmm(srci));
+ break;
+ }
+ }
+ }
+ }
+ break;
+
+ case INVALID_VALUE:
+ default:
+ break;
+ }
+
+ if (shouldEmitJumpToEpilog())
+ {
+ cc._unrecheable = 1;
+ }
+
+ for (i = 0; i < 2; i++)
+ {
+ if (_ret[i].isVar())
+ {
+ VarData* vdata = compiler->_getVarData(_ret[i].getId());
+ cc._unuseVarOnEndOfScope(this, vdata);
+ }
+ }
+
+ return translated();
+}
+
+void ERet::emit(Assembler& a) ASMJIT_NOTHROW
+{
+ if (shouldEmitJumpToEpilog())
+ {
+ a.jmp(getFunction()->getExitLabel());
+ }
+}
+
+int ERet::getMaxSize() const ASMJIT_NOTHROW
+{
+ return shouldEmitJumpToEpilog() ? 15 : 0;
+}
+
+bool ERet::shouldEmitJumpToEpilog() const ASMJIT_NOTHROW
+{
+ // Iterate over next emittables. If we found emittable that emits real
+ // instruction then we must return @c true.
+ Emittable* e = this->getNext();
+
+ while (e)
+ {
+ switch (e->getType())
+ {
+ // Non-interesting emittables.
+ case EMITTABLE_COMMENT:
+ case EMITTABLE_DUMMY:
+ case EMITTABLE_ALIGN:
+ case EMITTABLE_BLOCK:
+ case EMITTABLE_VARIABLE_HINT:
+ case EMITTABLE_TARGET:
+ break;
+
+ // Interesting emittables.
+ case EMITTABLE_EMBEDDED_DATA:
+ case EMITTABLE_INSTRUCTION:
+ case EMITTABLE_JUMP_TABLE:
+ case EMITTABLE_CALL:
+ case EMITTABLE_RET:
+ return true;
+
+ // These emittables shouldn't be here. We are inside function, after
+ // prolog.
+ case EMITTABLE_FUNCTION:
+ case EMITTABLE_PROLOG:
+ break;
+
+ // Stop station, we can't go forward from here.
+ case EMITTABLE_EPILOG:
+ return false;
+ }
+ e = e->getNext();
+ }
+
+ return false;
+}
+
+// ============================================================================
+// [AsmJit::CompilerContext - Construction / Destruction]
+// ============================================================================
+
+CompilerContext::CompilerContext(Compiler* compiler) ASMJIT_NOTHROW :
+ _zone(8192 - sizeof(Zone::Chunk) - 32)
+{
+ _compiler = compiler;
+ _clear();
+
+ _emitComments = compiler->getLogger() != NULL;
+}
+
+CompilerContext::~CompilerContext() ASMJIT_NOTHROW
+{
+}
+
+// ============================================================================
+// [AsmJit::CompilerContext - Clear]
+// ============================================================================
+
+void CompilerContext::_clear() ASMJIT_NOTHROW
+{
+ _zone.clear();
+ _function = NULL;
+
+ _start = NULL;
+ _stop = NULL;
+
+ _state.clear();
+ _active = NULL;
+
+ _forwardJumps = NULL;
+
+ _currentOffset = 0;
+ _unrecheable = 0;
+
+ _modifiedGPRegisters = 0;
+ _modifiedMMRegisters = 0;
+ _modifiedXMMRegisters = 0;
+
+ _allocableEBP = false;
+
+ _adjustESP = 0;
+
+ _argumentsBaseReg = INVALID_VALUE; // Used by patcher.
+ _argumentsBaseOffset = 0; // Used by patcher.
+ _argumentsActualDisp = 0; // Used by translate().
+
+ _variablesBaseReg = INVALID_VALUE; // Used by patcher.
+ _variablesBaseOffset = 0; // Used by patcher.
+ _variablesActualDisp = 0; // Used by translate()
+
+ _memUsed = NULL;
+ _memFree = NULL;
+
+ _mem4BlocksCount = 0;
+ _mem8BlocksCount = 0;
+ _mem16BlocksCount = 0;
+
+ _memBytesTotal = 0;
+
+ _backCode.clear();
+ _backPos = 0;
+}
+
+// ============================================================================
+// [AsmJit::CompilerContext - Construction / Destruction]
+// ============================================================================
+
+void CompilerContext::allocVar(VarData* vdata, uint32_t regMask, uint32_t vflags) ASMJIT_NOTHROW
+{
+ switch (vdata->type)
+ {
+ case VARIABLE_TYPE_GPD:
+#if defined(ASMJIT_X64)
+ case VARIABLE_TYPE_GPQ:
+#endif // ASMJIT_X64
+ allocGPVar(vdata, regMask, vflags);
+ break;
+
+ case VARIABLE_TYPE_X87:
+ case VARIABLE_TYPE_X87_1F:
+ case VARIABLE_TYPE_X87_1D:
+ // TODO: X87 VARIABLES NOT IMPLEMENTED.
+ break;
+
+ case VARIABLE_TYPE_MM:
+ allocMMVar(vdata, regMask, vflags);
+ break;
+
+ case VARIABLE_TYPE_XMM:
+ case VARIABLE_TYPE_XMM_1F:
+ case VARIABLE_TYPE_XMM_4F:
+ case VARIABLE_TYPE_XMM_1D:
+ case VARIABLE_TYPE_XMM_2D:
+ allocXMMVar(vdata, regMask, vflags);
+ break;
+ }
+
+ _postAlloc(vdata, vflags);
+}
+
+void CompilerContext::saveVar(VarData* vdata) ASMJIT_NOTHROW
+{
+ switch (vdata->type)
+ {
+ case VARIABLE_TYPE_GPD:
+#if defined(ASMJIT_X64)
+ case VARIABLE_TYPE_GPQ:
+#endif // ASMJIT_X64
+ saveGPVar(vdata);
+ break;
+
+ case VARIABLE_TYPE_X87:
+ case VARIABLE_TYPE_X87_1F:
+ case VARIABLE_TYPE_X87_1D:
+ // TODO: X87 VARIABLES NOT IMPLEMENTED.
+ break;
+
+ case VARIABLE_TYPE_MM:
+ saveMMVar(vdata);
+ break;
+
+ case VARIABLE_TYPE_XMM:
+ case VARIABLE_TYPE_XMM_1F:
+ case VARIABLE_TYPE_XMM_4F:
+ case VARIABLE_TYPE_XMM_1D:
+ case VARIABLE_TYPE_XMM_2D:
+ saveXMMVar(vdata);
+ break;
+ }
+}
+
+void CompilerContext::spillVar(VarData* vdata) ASMJIT_NOTHROW
+{
+ switch (vdata->type)
+ {
+ case VARIABLE_TYPE_GPD:
+#if defined(ASMJIT_X64)
+ case VARIABLE_TYPE_GPQ:
+#endif // ASMJIT_X64
+ spillGPVar(vdata);
+ break;
+
+ case VARIABLE_TYPE_X87:
+ case VARIABLE_TYPE_X87_1F:
+ case VARIABLE_TYPE_X87_1D:
+ // TODO: X87 VARIABLES NOT IMPLEMENTED.
+ break;
+
+ case VARIABLE_TYPE_MM:
+ spillMMVar(vdata);
+ break;
+
+ case VARIABLE_TYPE_XMM:
+ case VARIABLE_TYPE_XMM_1F:
+ case VARIABLE_TYPE_XMM_4F:
+ case VARIABLE_TYPE_XMM_1D:
+ case VARIABLE_TYPE_XMM_2D:
+ spillXMMVar(vdata);
+ break;
+ }
+}
+
+void CompilerContext::unuseVar(VarData* vdata, uint32_t toState) ASMJIT_NOTHROW
+{
+ ASMJIT_ASSERT(toState != VARIABLE_STATE_REGISTER);
+
+ if (vdata->state == VARIABLE_STATE_REGISTER)
+ {
+ uint32_t registerIndex = vdata->registerIndex;
+ switch (vdata->type)
+ {
+ case VARIABLE_TYPE_GPD:
+#if defined(ASMJIT_X64)
+ case VARIABLE_TYPE_GPQ:
+#endif // ASMJIT_X64
+ _state.gp[registerIndex] = NULL;
+ _freedGPRegister(registerIndex);
+ break;
+
+ case VARIABLE_TYPE_X87:
+ case VARIABLE_TYPE_X87_1F:
+ case VARIABLE_TYPE_X87_1D:
+ // TODO: X87 VARIABLES NOT IMPLEMENTED.
+ break;
+
+ case VARIABLE_TYPE_MM:
+ _state.mm[registerIndex] = NULL;
+ _freedMMRegister(registerIndex);
+ break;
+
+ case VARIABLE_TYPE_XMM:
+ case VARIABLE_TYPE_XMM_1F:
+ case VARIABLE_TYPE_XMM_4F:
+ case VARIABLE_TYPE_XMM_1D:
+ case VARIABLE_TYPE_XMM_2D:
+ _state.xmm[registerIndex] = NULL;
+ _freedXMMRegister(registerIndex);
+ break;
+ }
+ }
+
+ vdata->state = toState;
+ vdata->changed = false;
+ vdata->registerIndex = INVALID_VALUE;
+}
+
+void CompilerContext::allocGPVar(VarData* vdata, uint32_t regMask, uint32_t vflags) ASMJIT_NOTHROW
+{
+ // Fix the regMask (0 or full bit-array means that any register may be used).
+ if (regMask == 0) regMask = Util::maskUpToIndex(REG_NUM_GP);
+ regMask &= Util::maskUpToIndex(REG_NUM_GP);
+
+ // Working variables.
+ uint32_t i;
+ uint32_t mask;
+
+ // Last register code (aka home).
+ uint32_t home = vdata->homeRegisterIndex;
+ // New register code.
+ uint32_t idx = INVALID_VALUE;
+
+ // Preserved GP variables.
+ uint32_t preservedGP = vdata->scope->getPrototype().getPreservedGP();
+
+ // Spill candidate.
+ VarData* spillCandidate = NULL;
+
+ // Whether to alloc the non-preserved variables first.
+ bool nonPreservedFirst = true;
+ if (getFunction()->_isCaller)
+ {
+ nonPreservedFirst = vdata->firstCallable == NULL ||
+ vdata->firstCallable->getOffset() >= vdata->lastEmittable->getOffset();
+ }
+
+ // --------------------------------------------------------------------------
+ // [Already Allocated]
+ // --------------------------------------------------------------------------
+
+ // Go away if variable is already allocated.
+ if (vdata->state == VARIABLE_STATE_REGISTER)
+ {
+ uint32_t oldIndex = vdata->registerIndex;
+
+ // Already allocated in the right register.
+ if (Util::maskFromIndex(oldIndex) & regMask) return;
+
+ // Try to find unallocated register first.
+ mask = regMask & ~_state.usedGP;
+ if (mask != 0)
+ {
+ idx = Util::findFirstBit(
+ (nonPreservedFirst && (mask & ~preservedGP) != 0) ? mask & ~preservedGP : mask);
+ }
+ // Then find the allocated and later exchange.
+ else
+ {
+ idx = Util::findFirstBit(regMask & _state.usedGP);
+ }
+ ASMJIT_ASSERT(idx != INVALID_VALUE);
+
+ VarData* other = _state.gp[idx];
+ emitExchangeVar(vdata, idx, vflags, other);
+
+ _state.gp[oldIndex] = other;
+ _state.gp[idx ] = vdata;
+
+ if (other)
+ other->registerIndex = oldIndex;
+ else
+ _freedGPRegister(oldIndex);
+
+ // Update VarData.
+ vdata->state = VARIABLE_STATE_REGISTER;
+ vdata->registerIndex = idx;
+ vdata->homeRegisterIndex = idx;
+
+ _allocatedGPRegister(idx);
+ return;
+ }
+
+ // --------------------------------------------------------------------------
+ // [Find Unused GP]
+ // --------------------------------------------------------------------------
+
+ // If regMask contains restricted registers which may be used then everything
+ // is handled in this block.
+ if (regMask != Util::maskUpToIndex(REG_NUM_GP))
+ {
+ // Try to find unallocated register first.
+ mask = regMask & ~_state.usedGP;
+ if (mask != 0)
+ {
+ idx = Util::findFirstBit(
+ (nonPreservedFirst && (mask & ~preservedGP) != 0) ? (mask & ~preservedGP) : mask);
+ ASMJIT_ASSERT(idx != INVALID_VALUE);
+ }
+ // Then find the allocated and later spill.
+ else
+ {
+ idx = Util::findFirstBit(regMask & _state.usedGP);
+ ASMJIT_ASSERT(idx != INVALID_VALUE);
+
+ // Spill register we need.
+ spillCandidate = _state.gp[idx];
+
+ // Jump to spill part of allocation.
+ goto L_Spill;
+ }
+ }
+
+ // Home register code.
+ if (idx == INVALID_VALUE && home != INVALID_VALUE)
+ {
+ if ((_state.usedGP & (1U << home)) == 0) idx = home;
+ }
+
+ // We start from 1, because EAX/RAX register is sometimes explicitly
+ // needed. So we trying to prevent reallocation in near future.
+ if (idx == INVALID_VALUE)
+ {
+ for (i = 1, mask = (1 << i); i < REG_NUM_GP; i++, mask <<= 1)
+ {
+ if ((_state.usedGP & mask) == 0 && (i != REG_INDEX_EBP || _allocableEBP) && (i != REG_INDEX_ESP))
+ {
+ // Convenience to alloc non-preserved first or non-preserved last.
+ if (nonPreservedFirst)
+ {
+ if (idx != INVALID_VALUE && (preservedGP & mask) != 0) continue;
+ idx = i;
+ // If current register is preserved, we should try to find different
+ // one that is not. This can save one push / pop in prolog / epilog.
+ if ((preservedGP & mask) == 0) break;
+ }
+ else
+ {
+ if (idx != INVALID_VALUE && (preservedGP & mask) == 0) continue;
+ idx = i;
+ // The opposite.
+ if ((preservedGP & mask) != 0) break;
+ }
+ }
+ }
+ }
+
+ // If not found, try EAX/RAX.
+ if (idx == INVALID_VALUE && (_state.usedGP & 1) == 0)
+ {
+ idx = REG_INDEX_EAX;
+ }
+
+ // --------------------------------------------------------------------------
+ // [Spill]
+ // --------------------------------------------------------------------------
+
+ // If register is still not found, spill other variable.
+ if (idx == INVALID_VALUE)
+ {
+ if (spillCandidate == NULL)
+ {
+ spillCandidate = _getSpillCandidateGP();
+ }
+
+ // Spill candidate not found?
+ if (spillCandidate == NULL)
+ {
+ _compiler->setError(ERROR_NOT_ENOUGH_REGISTERS);
+ return;
+ }
+
+L_Spill:
+
+ // Prevented variables can't be spilled. _getSpillCandidate() never returns
+ // prevented variables, but when jumping to L_spill it can happen.
+ if (spillCandidate->workOffset == _currentOffset)
+ {
+ _compiler->setError(ERROR_REGISTERS_OVERLAP);
+ return;
+ }
+
+ idx = spillCandidate->registerIndex;
+ spillGPVar(spillCandidate);
+ }
+
+ // --------------------------------------------------------------------------
+ // [Alloc]
+ // --------------------------------------------------------------------------
+
+ if (vdata->state == VARIABLE_STATE_MEMORY && (vflags & VARIABLE_ALLOC_READ) != 0)
+ {
+ emitLoadVar(vdata, idx);
+ }
+
+ // Update VarData.
+ vdata->state = VARIABLE_STATE_REGISTER;
+ vdata->registerIndex = idx;
+ vdata->homeRegisterIndex = idx;
+
+ // Update StateData.
+ _allocatedVariable(vdata);
+}
+
+void CompilerContext::saveGPVar(VarData* vdata) ASMJIT_NOTHROW
+{
+ // Can't save variable that isn't allocated.
+ ASMJIT_ASSERT(vdata->state == VARIABLE_STATE_REGISTER);
+ ASMJIT_ASSERT(vdata->registerIndex != INVALID_VALUE);
+
+ uint32_t idx = vdata->registerIndex;
+ emitSaveVar(vdata, idx);
+
+ // Update VarData.
+ vdata->changed = false;
+}
+
+void CompilerContext::spillGPVar(VarData* vdata) ASMJIT_NOTHROW
+{
+ // Can't spill variable that isn't allocated.
+ ASMJIT_ASSERT(vdata->state == VARIABLE_STATE_REGISTER);
+ ASMJIT_ASSERT(vdata->registerIndex != INVALID_VALUE);
+
+ uint32_t idx = vdata->registerIndex;
+
+ if (vdata->changed) emitSaveVar(vdata, idx);
+
+ // Update VarData.
+ vdata->registerIndex = INVALID_VALUE;
+ vdata->state = VARIABLE_STATE_MEMORY;
+ vdata->changed = false;
+
+ // Update StateData.
+ _state.gp[idx] = NULL;
+ _freedGPRegister(idx);
+}
+
+void CompilerContext::allocMMVar(VarData* vdata, uint32_t regMask, uint32_t vflags) ASMJIT_NOTHROW
+{
+ // Fix the regMask (0 or full bit-array means that any register may be used).
+ if (regMask == 0) regMask = Util::maskUpToIndex(REG_NUM_MM);
+ regMask &= Util::maskUpToIndex(REG_NUM_MM);
+
+ // Working variables.
+ uint32_t i;
+ uint32_t mask;
+
+ // Last register code (aka home).
+ uint32_t home = vdata->homeRegisterIndex;
+ // New register code.
+ uint32_t idx = INVALID_VALUE;
+
+ // Preserved MM variables.
+ //
+ // NOTE: Currently MM variables are not preserved and there is no calling
+ // convention known to me that does that. But on the other side it's possible
+ // to write such calling convention.
+ uint32_t preservedMM = vdata->scope->getPrototype().getPreservedMM();
+
+ // Spill candidate.
+ VarData* spillCandidate = NULL;
+
+ // Whether to alloc non-preserved first or last.
+ bool nonPreservedFirst = true;
+ if (this->getFunction()->_isCaller)
+ {
+ nonPreservedFirst = vdata->firstCallable == NULL ||
+ vdata->firstCallable->getOffset() >= vdata->lastEmittable->getOffset();
+ }
+
+ // --------------------------------------------------------------------------
+ // [Already Allocated]
+ // --------------------------------------------------------------------------
+
+ // Go away if variable is already allocated.
+ if (vdata->state == VARIABLE_STATE_REGISTER)
+ {
+ uint32_t oldIndex = vdata->registerIndex;
+
+ // Already allocated in the right register.
+ if (Util::maskFromIndex(oldIndex) & regMask) return;
+
+ // Try to find unallocated register first.
+ mask = regMask & ~_state.usedMM;
+ if (mask != 0)
+ {
+ idx = Util::findFirstBit(
+ (nonPreservedFirst && (mask & ~preservedMM) != 0) ? mask & ~preservedMM : mask);
+ }
+ // Then find the allocated and later exchange.
+ else
+ {
+ idx = Util::findFirstBit(regMask & _state.usedMM);
+ }
+ ASMJIT_ASSERT(idx != INVALID_VALUE);
+
+ VarData* other = _state.mm[idx];
+ if (other) spillMMVar(other);
+
+ emitMoveVar(vdata, idx, vflags);
+ _freedMMRegister(oldIndex);
+ _state.mm[idx] = vdata;
+
+ // Update VarData.
+ vdata->state = VARIABLE_STATE_REGISTER;
+ vdata->registerIndex = idx;
+ vdata->homeRegisterIndex = idx;
+
+ _allocatedMMRegister(idx);
+ return;
+ }
+
+ // --------------------------------------------------------------------------
+ // [Find Unused MM]
+ // --------------------------------------------------------------------------
+
+ // If regMask contains restricted registers which may be used then everything
+ // is handled in this block.
+ if (regMask != Util::maskUpToIndex(REG_NUM_MM))
+ {
+ // Try to find unallocated register first.
+ mask = regMask & ~_state.usedMM;
+ if (mask != 0)
+ {
+ idx = Util::findFirstBit(
+ (nonPreservedFirst && (mask & ~preservedMM) != 0) ? mask & ~preservedMM : mask);
+ ASMJIT_ASSERT(idx != INVALID_VALUE);
+ }
+ // Then find the allocated and later spill.
+ else
+ {
+ idx = Util::findFirstBit(regMask & _state.usedMM);
+ ASMJIT_ASSERT(idx != INVALID_VALUE);
+
+ // Spill register we need.
+ spillCandidate = _state.mm[idx];
+
+ // Jump to spill part of allocation.
+ goto L_Spill;
+ }
+ }
+
+ // Home register code.
+ if (idx == INVALID_VALUE && home != INVALID_VALUE)
+ {
+ if ((_state.usedMM & (1U << home)) == 0) idx = home;
+ }
+
+ if (idx == INVALID_VALUE)
+ {
+ for (i = 0, mask = (1 << i); i < REG_NUM_MM; i++, mask <<= 1)
+ {
+ if ((_state.usedMM & mask) == 0)
+ {
+ // Convenience to alloc non-preserved first or non-preserved last.
+ if (nonPreservedFirst)
+ {
+ if (idx != INVALID_VALUE && (preservedMM & mask) != 0) continue;
+ idx = i;
+ // If current register is preserved, we should try to find different
+ // one that is not. This can save one push / pop in prolog / epilog.
+ if ((preservedMM & mask) == 0) break;
+ }
+ else
+ {
+ if (idx != INVALID_VALUE && (preservedMM & mask) == 0) continue;
+ idx = i;
+ // The opposite.
+ if ((preservedMM & mask) != 0) break;
+ }
+ }
+ }
+ }
+
+ // --------------------------------------------------------------------------
+ // [Spill]
+ // --------------------------------------------------------------------------
+
+ // If register is still not found, spill other variable.
+ if (idx == INVALID_VALUE)
+ {
+ if (spillCandidate == NULL) spillCandidate = _getSpillCandidateMM();
+
+ // Spill candidate not found?
+ if (spillCandidate == NULL)
+ {
+ _compiler->setError(ERROR_NOT_ENOUGH_REGISTERS);
+ return;
+ }
+
+L_Spill:
+
+ // Prevented variables can't be spilled. _getSpillCandidate() never returns
+ // prevented variables, but when jumping to L_spill it can happen.
+ if (spillCandidate->workOffset == _currentOffset)
+ {
+ _compiler->setError(ERROR_REGISTERS_OVERLAP);
+ return;
+ }
+
+ idx = spillCandidate->registerIndex;
+ spillMMVar(spillCandidate);
+ }
+
+ // --------------------------------------------------------------------------
+ // [Alloc]
+ // --------------------------------------------------------------------------
+
+ if (vdata->state == VARIABLE_STATE_MEMORY && (vflags & VARIABLE_ALLOC_READ) != 0)
+ {
+ emitLoadVar(vdata, idx);
+ }
+
+ // Update VarData.
+ vdata->state = VARIABLE_STATE_REGISTER;
+ vdata->registerIndex = idx;
+ vdata->homeRegisterIndex = idx;
+
+ // Update StateData.
+ _allocatedVariable(vdata);
+}
+
+void CompilerContext::saveMMVar(VarData* vdata) ASMJIT_NOTHROW
+{
+ // Can't save variable that isn't allocated.
+ ASMJIT_ASSERT(vdata->state == VARIABLE_STATE_REGISTER);
+ ASMJIT_ASSERT(vdata->registerIndex != INVALID_VALUE);
+
+ uint32_t idx = vdata->registerIndex;
+ emitSaveVar(vdata, idx);
+
+ // Update VarData.
+ vdata->changed = false;
+}
+
+void CompilerContext::spillMMVar(VarData* vdata) ASMJIT_NOTHROW
+{
+ // Can't spill variable that isn't allocated.
+ ASMJIT_ASSERT(vdata->state == VARIABLE_STATE_REGISTER);
+ ASMJIT_ASSERT(vdata->registerIndex != INVALID_VALUE);
+
+ uint32_t idx = vdata->registerIndex;
+
+ if (vdata->changed) emitSaveVar(vdata, idx);
+
+ // Update VarData.
+ vdata->registerIndex = INVALID_VALUE;
+ vdata->state = VARIABLE_STATE_MEMORY;
+ vdata->changed = false;
+
+ // Update StateData.
+ _state.mm[idx] = NULL;
+ _freedMMRegister(idx);
+}
+
+void CompilerContext::allocXMMVar(VarData* vdata, uint32_t regMask, uint32_t vflags) ASMJIT_NOTHROW
+{
+ // Fix the regMask (0 or full bit-array means that any register may be used).
+ if (regMask == 0) regMask = Util::maskUpToIndex(REG_NUM_XMM);
+ regMask &= Util::maskUpToIndex(REG_NUM_XMM);
+
+ // Working variables.
+ uint32_t i;
+ uint32_t mask;
+
+ // Last register code (aka home).
+ uint32_t home = vdata->homeRegisterIndex;
+ // New register code.
+ uint32_t idx = INVALID_VALUE;
+
+ // Preserved XMM variables.
+ uint32_t preservedXMM = vdata->scope->getPrototype().getPreservedXMM();
+
+ // Spill candidate.
+ VarData* spillCandidate = NULL;
+
+ // Whether to alloc non-preserved first or last.
+ bool nonPreservedFirst = true;
+ if (this->getFunction()->_isCaller)
+ {
+ nonPreservedFirst = vdata->firstCallable == NULL ||
+ vdata->firstCallable->getOffset() >= vdata->lastEmittable->getOffset();
+ }
+
+ // --------------------------------------------------------------------------
+ // [Already Allocated]
+ // --------------------------------------------------------------------------
+
+ // Go away if variable is already allocated.
+ if (vdata->state == VARIABLE_STATE_REGISTER)
+ {
+ uint32_t oldIndex = vdata->registerIndex;
+
+ // Already allocated in the right register.
+ if (Util::maskFromIndex(oldIndex) & regMask) return;
+
+ // Try to find unallocated register first.
+ mask = regMask & ~_state.usedXMM;
+ if (mask != 0)
+ {
+ idx = Util::findFirstBit(
+ (nonPreservedFirst && (mask & ~preservedXMM) != 0) ? mask & ~preservedXMM : mask);
+ }
+ // Then find the allocated and later exchange.
+ else
+ {
+ idx = Util::findFirstBit(regMask & _state.usedXMM);
+ }
+ ASMJIT_ASSERT(idx != INVALID_VALUE);
+
+ VarData* other = _state.xmm[idx];
+ if (other) spillXMMVar(other);
+
+ emitMoveVar(vdata, idx, vflags);
+ _freedXMMRegister(oldIndex);
+ _state.xmm[idx] = vdata;
+
+ // Update VarData.
+ vdata->state = VARIABLE_STATE_REGISTER;
+ vdata->registerIndex = idx;
+ vdata->homeRegisterIndex = idx;
+
+ _allocatedXMMRegister(idx);
+ return;
+ }
+
+ // --------------------------------------------------------------------------
+ // [Find Unused XMM]
+ // --------------------------------------------------------------------------
+
+ // If regMask contains restricted registers which may be used then everything
+ // is handled in this block.
+ if (regMask != Util::maskUpToIndex(REG_NUM_XMM))
+ {
+ // Try to find unallocated register first.
+ mask = regMask & ~_state.usedXMM;
+ if (mask != 0)
+ {
+ idx = Util::findFirstBit(
+ (nonPreservedFirst && (mask & ~preservedXMM) != 0) ? mask & ~preservedXMM : mask);
+ ASMJIT_ASSERT(idx != INVALID_VALUE);
+ }
+ // Then find the allocated and later spill.
+ else
+ {
+ idx = Util::findFirstBit(regMask & _state.usedXMM);
+ ASMJIT_ASSERT(idx != INVALID_VALUE);
+
+ // Spill register we need.
+ spillCandidate = _state.xmm[idx];
+
+ // Jump to spill part of allocation.
+ goto L_Spill;
+ }
+ }
+
+ // Home register code.
+ if (idx == INVALID_VALUE && home != INVALID_VALUE)
+ {
+ if ((_state.usedXMM & (1U << home)) == 0) idx = home;
+ }
+
+ if (idx == INVALID_VALUE)
+ {
+ for (i = 0, mask = (1 << i); i < REG_NUM_XMM; i++, mask <<= 1)
+ {
+ if ((_state.usedXMM & mask) == 0)
+ {
+ // Convenience to alloc non-preserved first or non-preserved last.
+ if (nonPreservedFirst)
+ {
+ if (idx != INVALID_VALUE && (preservedXMM & mask) != 0) continue;
+ idx = i;
+ // If current register is preserved, we should try to find different
+ // one that is not. This can save one push / pop in prolog / epilog.
+ if ((preservedXMM & mask) == 0) break;
+ }
+ else
+ {
+ if (idx != INVALID_VALUE && (preservedXMM & mask) == 0) continue;
+ idx = i;
+ // The opposite.
+ if ((preservedXMM & mask) != 0) break;
+ }
+ }
+ }
+ }
+
+ // --------------------------------------------------------------------------
+ // [Spill]
+ // --------------------------------------------------------------------------
+
+ // If register is still not found, spill other variable.
+ if (idx == INVALID_VALUE)
+ {
+ if (spillCandidate == NULL) spillCandidate = _getSpillCandidateXMM();
+
+ // Spill candidate not found?
+ if (spillCandidate == NULL)
+ {
+ _compiler->setError(ERROR_NOT_ENOUGH_REGISTERS);
+ return;
+ }
+
+L_Spill:
+
+ // Prevented variables can't be spilled. _getSpillCandidate() never returns
+ // prevented variables, but when jumping to L_spill it can happen.
+ if (spillCandidate->workOffset == _currentOffset)
+ {
+ _compiler->setError(ERROR_REGISTERS_OVERLAP);
+ return;
+ }
+
+ idx = spillCandidate->registerIndex;
+ spillXMMVar(spillCandidate);
+ }
+
+ // --------------------------------------------------------------------------
+ // [Alloc]
+ // --------------------------------------------------------------------------
+
+ if (vdata->state == VARIABLE_STATE_MEMORY && (vflags & VARIABLE_ALLOC_READ) != 0)
+ {
+ emitLoadVar(vdata, idx);
+ }
+
+ // Update VarData.
+ vdata->state = VARIABLE_STATE_REGISTER;
+ vdata->registerIndex = idx;
+ vdata->homeRegisterIndex = idx;
+
+ // Update StateData.
+ _allocatedVariable(vdata);
+}
+
+void CompilerContext::saveXMMVar(VarData* vdata) ASMJIT_NOTHROW
+{
+ // Can't save variable that isn't allocated.
+ ASMJIT_ASSERT(vdata->state == VARIABLE_STATE_REGISTER);
+ ASMJIT_ASSERT(vdata->registerIndex != INVALID_VALUE);
+
+ uint32_t idx = vdata->registerIndex;
+ emitSaveVar(vdata, idx);
+
+ // Update VarData.
+ vdata->changed = false;
+}
+
+void CompilerContext::spillXMMVar(VarData* vdata) ASMJIT_NOTHROW
+{
+ // Can't spill variable that isn't allocated.
+ ASMJIT_ASSERT(vdata->state == VARIABLE_STATE_REGISTER);
+ ASMJIT_ASSERT(vdata->registerIndex != INVALID_VALUE);
+
+ uint32_t idx = vdata->registerIndex;
+
+ if (vdata->changed) emitSaveVar(vdata, idx);
+
+ // Update VarData.
+ vdata->registerIndex = INVALID_VALUE;
+ vdata->state = VARIABLE_STATE_MEMORY;
+ vdata->changed = false;
+
+ // Update StateData.
+ _state.xmm[idx] = NULL;
+ _freedXMMRegister(idx);
+}
+
+void CompilerContext::emitLoadVar(VarData* vdata, uint32_t regIndex) ASMJIT_NOTHROW
+{
+ Mem m = _getVarMem(vdata);
+
+ switch (vdata->type)
+ {
+ case VARIABLE_TYPE_GPD:
+ _compiler->emit(INST_MOV, gpd(regIndex), m);
+ if (_emitComments) goto addComment;
+ break;
+#if defined(ASMJIT_X64)
+ case VARIABLE_TYPE_GPQ:
+ _compiler->emit(INST_MOV, gpq(regIndex), m);
+ if (_emitComments) goto addComment;
+ break;
+#endif // ASMJIT_X64
+
+ case VARIABLE_TYPE_X87:
+ case VARIABLE_TYPE_X87_1F:
+ case VARIABLE_TYPE_X87_1D:
+ // TODO: X87 VARIABLES NOT IMPLEMENTED.
+ break;
+
+ case VARIABLE_TYPE_MM:
+ _compiler->emit(INST_MOVQ, mm(regIndex), m);
+ if (_emitComments) goto addComment;
+ break;
+
+ case VARIABLE_TYPE_XMM:
+ _compiler->emit(INST_MOVDQA, xmm(regIndex), m);
+ if (_emitComments) goto addComment;
+ break;
+ case VARIABLE_TYPE_XMM_1F:
+ _compiler->emit(INST_MOVSS, xmm(regIndex), m);
+ if (_emitComments) goto addComment;
+ break;
+ case VARIABLE_TYPE_XMM_1D:
+ _compiler->emit(INST_MOVSD, xmm(regIndex), m);
+ if (_emitComments) goto addComment;
+ break;
+ case VARIABLE_TYPE_XMM_4F:
+ _compiler->emit(INST_MOVAPS, xmm(regIndex), m);
+ if (_emitComments) goto addComment;
+ break;
+ case VARIABLE_TYPE_XMM_2D:
+ _compiler->emit(INST_MOVAPD, xmm(regIndex), m);
+ if (_emitComments) goto addComment;
+ break;
+ }
+ return;
+
+addComment:
+ _compiler->getCurrentEmittable()->setCommentF("Alloc %s", vdata->name);
+}
+
+void CompilerContext::emitSaveVar(VarData* vdata, uint32_t regIndex) ASMJIT_NOTHROW
+{
+ // Caller must ensure that variable is allocated.
+ ASMJIT_ASSERT(regIndex != INVALID_VALUE);
+
+ Mem m = _getVarMem(vdata);
+
+ switch (vdata->type)
+ {
+ case VARIABLE_TYPE_GPD:
+ _compiler->emit(INST_MOV, m, gpd(regIndex));
+ if (_emitComments) goto addComment;
+ break;
+#if defined(ASMJIT_X64)
+ case VARIABLE_TYPE_GPQ:
+ _compiler->emit(INST_MOV, m, gpq(regIndex));
+ if (_emitComments) goto addComment;
+ break;
+#endif // ASMJIT_X64
+
+ case VARIABLE_TYPE_X87:
+ case VARIABLE_TYPE_X87_1F:
+ case VARIABLE_TYPE_X87_1D:
+ // TODO: X87 VARIABLES NOT IMPLEMENTED.
+ break;
+
+ case VARIABLE_TYPE_MM:
+ _compiler->emit(INST_MOVQ, m, mm(regIndex));
+ if (_emitComments) goto addComment;
+ break;
+
+ case VARIABLE_TYPE_XMM:
+ _compiler->emit(INST_MOVDQA, m, xmm(regIndex));
+ if (_emitComments) goto addComment;
+ break;
+ case VARIABLE_TYPE_XMM_1F:
+ _compiler->emit(INST_MOVSS, m, xmm(regIndex));
+ if (_emitComments) goto addComment;
+ break;
+ case VARIABLE_TYPE_XMM_1D:
+ _compiler->emit(INST_MOVSD, m, xmm(regIndex));
+ if (_emitComments) goto addComment;
+ break;
+ case VARIABLE_TYPE_XMM_4F:
+ _compiler->emit(INST_MOVAPS, m, xmm(regIndex));
+ if (_emitComments) goto addComment;
+ break;
+ case VARIABLE_TYPE_XMM_2D:
+ _compiler->emit(INST_MOVAPD, m, xmm(regIndex));
+ if (_emitComments) goto addComment;
+ break;
+ }
+ return;
+
+addComment:
+ _compiler->getCurrentEmittable()->setCommentF("Spill %s", vdata->name);
+}
+
+void CompilerContext::emitMoveVar(VarData* vdata, uint32_t regIndex, uint32_t vflags) ASMJIT_NOTHROW
+{
+ // Caller must ensure that variable is allocated.
+ ASMJIT_ASSERT(vdata->registerIndex != INVALID_VALUE);
+
+ if ((vflags & VARIABLE_ALLOC_READ) == 0) return;
+
+ switch (vdata->type)
+ {
+ case VARIABLE_TYPE_GPD:
+ _compiler->emit(INST_MOV, gpd(regIndex), gpd(vdata->registerIndex));
+ break;
+#if defined(ASMJIT_X64)
+ case VARIABLE_TYPE_GPQ:
+ _compiler->emit(INST_MOV, gpq(regIndex), gpq(vdata->registerIndex));
+ break;
+#endif // ASMJIT_X64
+
+ case VARIABLE_TYPE_X87:
+ case VARIABLE_TYPE_X87_1F:
+ case VARIABLE_TYPE_X87_1D:
+ // TODO: X87 VARIABLES NOT IMPLEMENTED.
+ break;
+
+ case VARIABLE_TYPE_MM:
+ _compiler->emit(INST_MOVQ, mm(regIndex), mm(vdata->registerIndex));
+ break;
+
+ case VARIABLE_TYPE_XMM:
+ _compiler->emit(INST_MOVDQA, xmm(regIndex), xmm(vdata->registerIndex));
+ break;
+ case VARIABLE_TYPE_XMM_1F:
+ _compiler->emit(INST_MOVSS, xmm(regIndex), xmm(vdata->registerIndex));
+ break;
+ case VARIABLE_TYPE_XMM_1D:
+ _compiler->emit(INST_MOVSD, xmm(regIndex), xmm(vdata->registerIndex));
+ break;
+ case VARIABLE_TYPE_XMM_4F:
+ _compiler->emit(INST_MOVAPS, xmm(regIndex), xmm(vdata->registerIndex));
+ break;
+ case VARIABLE_TYPE_XMM_2D:
+ _compiler->emit(INST_MOVAPD, xmm(regIndex), xmm(vdata->registerIndex));
+ break;
+ }
+}
+
+void CompilerContext::emitExchangeVar(VarData* vdata, uint32_t regIndex, uint32_t vflags, VarData* other) ASMJIT_NOTHROW
+{
+ // Caller must ensure that variable is allocated.
+ ASMJIT_ASSERT(vdata->registerIndex != INVALID_VALUE);
+
+ // If other is not valid then we can just emit MOV (or other similar instruction).
+ if (other == NULL)
+ {
+ emitMoveVar(vdata, regIndex, vflags);
+ return;
+ }
+
+ // If we need to alloc for write-only operation then we can move other
+ // variable away instead of exchanging them.
+ if ((vflags & VARIABLE_ALLOC_READ) == 0)
+ {
+ emitMoveVar(other, vdata->registerIndex, VARIABLE_ALLOC_READ);
+ return;
+ }
+
+ switch (vdata->type)
+ {
+ case VARIABLE_TYPE_GPD:
+ _compiler->emit(INST_XCHG, gpd(regIndex), gpd(vdata->registerIndex));
+ break;
+#if defined(ASMJIT_X64)
+ case VARIABLE_TYPE_GPQ:
+ _compiler->emit(INST_XCHG, gpq(regIndex), gpq(vdata->registerIndex));
+ break;
+#endif // ASMJIT_X64
+
+ case VARIABLE_TYPE_X87:
+ case VARIABLE_TYPE_X87_1F:
+ case VARIABLE_TYPE_X87_1D:
+ // TODO: X87 VARIABLES NOT IMPLEMENTED.
+ break;
+
+ // NOTE: MM and XMM registers shoudln't be exchanged using this way, it's
+ // correct, but it sucks.
+
+ case VARIABLE_TYPE_MM:
+ {
+ MMReg a = mm(regIndex);
+ MMReg b = mm(vdata->registerIndex);
+
+ _compiler->emit(INST_PXOR, a, b);
+ _compiler->emit(INST_PXOR, b, a);
+ _compiler->emit(INST_PXOR, a, b);
+ break;
+ }
+
+ case VARIABLE_TYPE_XMM_1F:
+ case VARIABLE_TYPE_XMM_4F:
+ {
+ XMMReg a = xmm(regIndex);
+ XMMReg b = xmm(vdata->registerIndex);
+
+ _compiler->emit(INST_XORPS, a, b);
+ _compiler->emit(INST_XORPS, b, a);
+ _compiler->emit(INST_XORPS, a, b);
+ break;
+ }
+
+ case VARIABLE_TYPE_XMM_1D:
+ case VARIABLE_TYPE_XMM_2D:
+ {
+ XMMReg a = xmm(regIndex);
+ XMMReg b = xmm(vdata->registerIndex);
+
+ _compiler->emit(INST_XORPD, a, b);
+ _compiler->emit(INST_XORPD, b, a);
+ _compiler->emit(INST_XORPD, a, b);
+ break;
+ }
+
+ case VARIABLE_TYPE_XMM:
+ {
+ XMMReg a = xmm(regIndex);
+ XMMReg b = xmm(vdata->registerIndex);
+
+ _compiler->emit(INST_PXOR, a, b);
+ _compiler->emit(INST_PXOR, b, a);
+ _compiler->emit(INST_PXOR, a, b);
+ break;
+ }
+ }
+}
+
+void CompilerContext::_postAlloc(VarData* vdata, uint32_t vflags) ASMJIT_NOTHROW
+{
+ if (vflags & VARIABLE_ALLOC_WRITE) vdata->changed = true;
+}
+
+void CompilerContext::_markMemoryUsed(VarData* vdata) ASMJIT_NOTHROW
+{
+ if (vdata->homeMemoryData != NULL) return;
+
+ VarMemBlock* mem = _allocMemBlock(vdata->size);
+ if (!mem) return;
+
+ vdata->homeMemoryData = mem;
+}
+
+Mem CompilerContext::_getVarMem(VarData* vdata) ASMJIT_NOTHROW
+{
+ Mem m;
+ m._mem.id = vdata->id;
+ if (!vdata->isMemArgument) m._mem.displacement = _adjustESP;
+
+ _markMemoryUsed(vdata);
+ return m;
+}
+
+static int32_t getSpillScore(VarData* v, uint32_t currentOffset)
+{
+ int32_t score = 0;
+
+ ASMJIT_ASSERT(v->lastEmittable != NULL);
+ uint32_t lastOffset = v->lastEmittable->getOffset();
+
+ if (lastOffset >= currentOffset)
+ score += (int32_t)(lastOffset - currentOffset);
+
+ // Each write access decreases probability of spill.
+ score -= (int32_t)v->registerWriteCount + (int32_t)v->registerRWCount;
+ // Each read-only access increases probability of spill.
+ score += (int32_t)v->registerReadCount;
+
+ // Each memory access increases probability of spill.
+ score += (int32_t)v->memoryWriteCount + (int32_t)v->memoryRWCount;
+ score += (int32_t)v->memoryReadCount;
+
+ return score;
+}
+
+VarData* CompilerContext::_getSpillCandidateGP() ASMJIT_NOTHROW
+{
+ return _getSpillCandidateGeneric(_state.gp, REG_NUM_GP);
+}
+
+VarData* CompilerContext::_getSpillCandidateMM() ASMJIT_NOTHROW
+{
+ return _getSpillCandidateGeneric(_state.mm, REG_NUM_MM);
+}
+
+VarData* CompilerContext::_getSpillCandidateXMM() ASMJIT_NOTHROW
+{
+ return _getSpillCandidateGeneric(_state.xmm, REG_NUM_XMM);
+}
+
+VarData* CompilerContext::_getSpillCandidateGeneric(VarData** varArray, uint32_t count) ASMJIT_NOTHROW
+{
+ uint32_t i;
+
+ VarData* candidate = NULL;
+ uint32_t candidatePriority = 0;
+ int32_t candidateScore = 0;
+
+ uint32_t currentOffset = _compiler->getCurrentEmittable()->getOffset();
+
+ for (i = 0; i < count; i++)
+ {
+ // Get variable.
+ VarData* vdata = varArray[i];
+
+ // Never spill variables needed for next instruction.
+ if (vdata == NULL || vdata->workOffset == _currentOffset) continue;
+
+ uint32_t variablePriority = vdata->priority;
+ int32_t variableScore = getSpillScore(vdata, currentOffset);
+
+ if ((candidate == NULL) ||
+ (variablePriority > candidatePriority) ||
+ (variablePriority == candidatePriority && variableScore > candidateScore))
+ {
+ candidate = vdata;
+ candidatePriority = variablePriority;
+ candidateScore = variableScore;
+ }
+ }
+
+ return candidate;
+}
+
+void CompilerContext::_addActive(VarData* vdata) ASMJIT_NOTHROW
+{
+ // Never call with variable that is already in active list.
+ ASMJIT_ASSERT(vdata->nextActive == NULL);
+ ASMJIT_ASSERT(vdata->prevActive == NULL);
+
+ if (_active == NULL)
+ {
+ vdata->nextActive = vdata;
+ vdata->prevActive = vdata;
+
+ _active = vdata;
+ }
+ else
+ {
+ VarData* vlast = _active->prevActive;
+
+ vlast->nextActive = vdata;
+ _active->prevActive = vdata;
+
+ vdata->nextActive = _active;
+ vdata->prevActive = vlast;
+ }
+}
+
+void CompilerContext::_freeActive(VarData* vdata) ASMJIT_NOTHROW
+{
+ VarData* next = vdata->nextActive;
+ VarData* prev = vdata->prevActive;
+
+ if (prev == next)
+ {
+ _active = NULL;
+ }
+ else
+ {
+ if (_active == vdata) _active = next;
+ prev->nextActive = next;
+ next->prevActive = prev;
+ }
+
+ vdata->nextActive = NULL;
+ vdata->prevActive = NULL;
+}
+
+void CompilerContext::_freeAllActive() ASMJIT_NOTHROW
+{
+ if (_active == NULL) return;
+
+ VarData* cur = _active;
+ for (;;)
+ {
+ VarData* next = cur->nextActive;
+ cur->nextActive = NULL;
+ cur->prevActive = NULL;
+ if (next == _active) break;
+ }
+
+ _active = NULL;
+}
+
+void CompilerContext::_allocatedVariable(VarData* vdata) ASMJIT_NOTHROW
+{
+ uint32_t idx = vdata->registerIndex;
+
+ switch (vdata->type)
+ {
+ case VARIABLE_TYPE_GPD:
+ case VARIABLE_TYPE_GPQ:
+ _state.gp[idx] = vdata;
+ _allocatedGPRegister(idx);
+ break;
+
+ case VARIABLE_TYPE_MM:
+ _state.mm[idx] = vdata;
+ _allocatedMMRegister(idx);
+ break;
+
+ case VARIABLE_TYPE_XMM:
+ case VARIABLE_TYPE_XMM_1F:
+ case VARIABLE_TYPE_XMM_4F:
+ case VARIABLE_TYPE_XMM_1D:
+ case VARIABLE_TYPE_XMM_2D:
+ _state.xmm[idx] = vdata;
+ _allocatedXMMRegister(idx);
+ break;
+
+ default:
+ ASMJIT_ASSERT(0);
+ break;
+ }
+}
+
+void CompilerContext::translateOperands(Operand* operands, uint32_t count) ASMJIT_NOTHROW
+{
+ uint32_t i;
+
+ // Translate variables to registers.
+ for (i = 0; i < count; i++)
+ {
+ Operand& o = operands[i];
+
+ if (o.isVar())
+ {
+ VarData* vdata = _compiler->_getVarData(o.getId());
+ ASMJIT_ASSERT(vdata != NULL);
+
+ o._reg.op = OPERAND_REG;
+ o._reg.code |= vdata->registerIndex;
+ }
+ else if (o.isMem())
+ {
+ if ((o.getId() & OPERAND_ID_TYPE_MASK) == OPERAND_ID_TYPE_VAR)
+ {
+ // Memory access. We just increment here actual displacement.
+ VarData* vdata = _compiler->_getVarData(o.getId());
+ ASMJIT_ASSERT(vdata != NULL);
+
+ o._mem.displacement += vdata->isMemArgument
+ ? _argumentsActualDisp
+ : _variablesActualDisp;
+ // NOTE: This is not enough, variable position will be patched later
+ // by CompilerContext::_patchMemoryOperands().
+ }
+ else if ((o._mem.base & OPERAND_ID_TYPE_MASK) == OPERAND_ID_TYPE_VAR)
+ {
+ VarData* vdata = _compiler->_getVarData(o._mem.base);
+ ASMJIT_ASSERT(vdata != NULL);
+
+ o._mem.base = vdata->registerIndex;
+ }
+
+ if ((o._mem.index & OPERAND_ID_TYPE_MASK) == OPERAND_ID_TYPE_VAR)
+ {
+ VarData* vdata = _compiler->_getVarData(o._mem.index);
+ ASMJIT_ASSERT(vdata != NULL);
+
+ o._mem.index = vdata->registerIndex;
+ }
+ }
+ }
+}
+
+void CompilerContext::addBackwardCode(EJmp* from) ASMJIT_NOTHROW
+{
+ _backCode.append(from);
+}
+
+void CompilerContext::addForwardJump(EJmp* inst) ASMJIT_NOTHROW
+{
+ ForwardJumpData* j =
+ reinterpret_cast<ForwardJumpData*>(_zone.zalloc(sizeof(ForwardJumpData)));
+ if (j == NULL) { _compiler->setError(ERROR_NO_HEAP_MEMORY); return; }
+
+ j->inst = inst;
+ j->state = _saveState();
+ j->next = _forwardJumps;
+ _forwardJumps = j;
+}
+
+StateData* CompilerContext::_saveState() ASMJIT_NOTHROW
+{
+ // Get count of variables stored in memory.
+ uint32_t memVarsCount = 0;
+ VarData* cur = _active;
+ if (cur)
+ {
+ do {
+ if (cur->state == VARIABLE_STATE_MEMORY) memVarsCount++;
+ cur = cur->nextActive;
+ } while (cur != _active);
+ }
+
+ // Alloc StateData structure (using zone allocator) and copy current state into it.
+ StateData* state = _compiler->_newStateData(memVarsCount);
+ memcpy(state, &_state, sizeof(StateData));
+
+ // Clear changed flags.
+ state->changedGP = 0;
+ state->changedMM = 0;
+ state->changedXMM = 0;
+
+ uint i;
+ uint mask;
+
+ // Save variables stored in REGISTERs and CHANGE flag.
+ for (i = 0, mask = 1; i < REG_NUM_GP; i++, mask <<= 1)
+ {
+ if (state->gp[i] && state->gp[i]->changed) state->changedGP |= mask;
+ }
+
+ for (i = 0, mask = 1; i < REG_NUM_MM; i++, mask <<= 1)
+ {
+ if (state->mm[i] && state->mm[i]->changed) state->changedMM |= mask;
+ }
+
+ for (i = 0, mask = 1; i < REG_NUM_XMM; i++, mask <<= 1)
+ {
+ if (state->xmm[i] && state->xmm[i]->changed) state->changedXMM |= mask;
+ }
+
+ // Save variables stored in MEMORY.
+ state->memVarsCount = memVarsCount;
+ memVarsCount = 0;
+
+ cur = _active;
+ if (cur)
+ {
+ do {
+ if (cur->state == VARIABLE_STATE_MEMORY) state->memVarsData[memVarsCount++] = cur;
+ cur = cur->nextActive;
+ } while (cur != _active);
+ }
+
+ // Finished.
+ return state;
+}
+
+void CompilerContext::_assignState(StateData* state) ASMJIT_NOTHROW
+{
+ Compiler* compiler = getCompiler();
+
+ memcpy(&_state, state, sizeof(StateData));
+ _state.memVarsCount = 0;
+
+ uint i, mask;
+ VarData* vdata;
+
+ // Unuse all variables first.
+ vdata = _active;
+ if (vdata)
+ {
+ do {
+ vdata->state = VARIABLE_STATE_UNUSED;
+ vdata = vdata->nextActive;
+ } while (vdata != _active);
+ }
+
+ // Assign variables stored in memory which are not unused.
+ for (i = 0; i < state->memVarsCount; i++)
+ {
+ state->memVarsData[i]->state = VARIABLE_STATE_MEMORY;
+ }
+
+ // Assign allocated variables.
+ for (i = 0, mask = 1; i < REG_NUM_GP; i++, mask <<= 1)
+ {
+ if ((vdata = _state.gp[i]) != NULL)
+ {
+ vdata->state = VARIABLE_STATE_REGISTER;
+ vdata->registerIndex = i;
+ vdata->changed = (_state.changedGP & mask) != 0;
+ }
+ }
+
+ for (i = 0, mask = 1; i < REG_NUM_MM; i++, mask <<= 1)
+ {
+ if ((vdata = _state.mm[i]) != NULL)
+ {
+ vdata->state = VARIABLE_STATE_REGISTER;
+ vdata->registerIndex = i;
+ vdata->changed = (_state.changedMM & mask) != 0;
+ }
+ }
+
+ for (i = 0, mask = 1; i < REG_NUM_XMM; i++, mask <<= 1)
+ {
+ if ((vdata = _state.xmm[i]) != NULL)
+ {
+ vdata->state = VARIABLE_STATE_REGISTER;
+ vdata->registerIndex = i;
+ vdata->changed = (_state.changedXMM & mask) != 0;
+ }
+ }
+}
+
+void CompilerContext::_restoreState(StateData* state, uint32_t targetOffset) ASMJIT_NOTHROW
+{
+ // 16 + 8 + 16 = GP + MMX + XMM registers.
+ static const uint STATE_REGS_COUNT = 16 + 8 + 16;
+
+ StateData* fromState = &_state;
+ StateData* toState = state;
+
+ // No change, rare...
+ if (fromState == toState) return;
+
+ uint base;
+ uint i;
+
+ // --------------------------------------------------------------------------
+ // Set target state to all variables. vdata->tempInt is target state in this
+ // function.
+ // --------------------------------------------------------------------------
+
+ {
+ // UNUSED.
+ VarData* vdata = _active;
+ if (vdata)
+ {
+ do {
+ vdata->tempInt = VARIABLE_STATE_UNUSED;
+ vdata = vdata->nextActive;
+ } while (vdata != _active);
+ }
+
+ // MEMORY.
+ for (i = 0; i < toState->memVarsCount; i++)
+ {
+ toState->memVarsData[i]->tempInt = VARIABLE_STATE_MEMORY;
+ }
+
+ // REGISTER.
+ for (i = 0; i < StateData::NUM_REGS; i++)
+ {
+ if ((vdata = toState->regs[i]) != NULL) vdata->tempInt = VARIABLE_STATE_REGISTER;
+ }
+ }
+
+ // --------------------------------------------------------------------------
+ // [GP-Registers Switch]
+ // --------------------------------------------------------------------------
+
+ // TODO.
+#if 0
+ for (i = 0; i < REG_NUM_GP; i++)
+ {
+ VarData* fromVar = fromState->gp[i];
+ VarData* toVar = toState->gp[i];
+
+ if (fromVar != toVar)
+ {
+ if (fromVar != NULL)
+ {
+ if (toVar != NULL)
+ {
+ if (fromState->gp[to
+ }
+ else
+ {
+ // It is possible that variable that was saved in state currently not
+ // exists (tempInt is target scope!).
+ if (fromVar->tempInt == VARIABLE_STATE_UNUSED)
+ {
+ unuseVar(fromVar, VARIABLE_STATE_UNUSED);
+ }
+ else
+ {
+ spillVar(fromVar);
+ }
+ }
+ }
+ }
+ else if (fromVar != NULL)
+ {
+ uint32_t mask = Util::maskFromIndex(i);
+ // Variables are the same, we just need to compare changed flags.
+ if ((fromState->changedGP & mask) && !(toState->changedGP & mask)) saveVar(fromVar);
+ }
+ }
+#endif
+
+ // Spill.
+ for (base = 0, i = 0; i < STATE_REGS_COUNT; i++)
+ {
+ // Change the base offset (from base offset the register index can be
+ // calculated).
+ if (i == 16 || i == 16 + 8) base = i;
+ uint32_t regIndex = i - base;
+
+ VarData* fromVar = fromState->regs[i];
+ VarData* toVar = toState->regs[i];
+
+ if (fromVar != toVar)
+ {
+
+ // Spill the register.
+ if (fromVar != NULL)
+ {
+ // It is possible that variable that was saved in state currently not
+ // exists (tempInt is target scope!).
+ if (fromVar->tempInt == VARIABLE_STATE_UNUSED)
+ {
+ unuseVar(fromVar, VARIABLE_STATE_UNUSED);
+ }
+ else
+ {
+ spillVar(fromVar);
+ }
+ }
+ }
+ else if (fromVar != NULL)
+ {
+ uint32_t mask = Util::maskFromIndex(regIndex);
+ // Variables are the same, we just need to compare changed flags.
+ if ((fromState->changedGP & mask) && !(toState->changedGP & mask))
+ {
+ saveVar(fromVar);
+ }
+ }
+ }
+
+ // Alloc.
+ for (base = 0, i = 0; i < STATE_REGS_COUNT; i++)
+ {
+ if (i == 16 || i == 24) base = i;
+
+ VarData* fromVar = fromState->regs[i];
+ VarData* toVar = toState->regs[i];
+
+ if (fromVar != toVar)
+ {
+ uint32_t regIndex = i - base;
+
+ // Alloc register
+ if (toVar != NULL)
+ {
+ allocVar(toVar, Util::maskFromIndex(regIndex), VARIABLE_ALLOC_READ);
+ }
+ }
+
+ // TODO:
+ //if (toVar)
+ //{
+ // toVar->changed = to->changed;
+ //}
+ }
+
+ // --------------------------------------------------------------------------
+ // Update used masks.
+ // --------------------------------------------------------------------------
+
+ _state.usedGP = state->usedGP;
+ _state.usedMM = state->usedMM;
+ _state.usedXMM = state->usedXMM;
+
+ // --------------------------------------------------------------------------
+ // Update changed masks and cleanup.
+ // --------------------------------------------------------------------------
+
+ {
+ VarData* vdata = _active;
+ if (vdata)
+ {
+ do {
+ if (vdata->tempInt != VARIABLE_STATE_REGISTER)
+ {
+ vdata->state = (int)vdata->tempInt;
+ vdata->changed = false;
+ }
+
+ vdata->tempInt = 0;
+ vdata = vdata->nextActive;
+ } while (vdata != _active);
+ }
+ }
+}
+
+VarMemBlock* CompilerContext::_allocMemBlock(uint32_t size) ASMJIT_NOTHROW
+{
+ ASMJIT_ASSERT(size != 0);
+
+ // First try to find mem blocks.
+ VarMemBlock* mem = _memFree;
+ VarMemBlock* prev = NULL;
+
+ while (mem)
+ {
+ VarMemBlock* next = mem->nextFree;
+
+ if (mem->size == size)
+ {
+ if (prev)
+ prev->nextFree = next;
+ else
+ _memFree = next;
+
+ mem->nextFree = NULL;
+ return mem;
+ }
+
+ prev = mem;
+ mem = next;
+ }
+
+ // Never mind, create new.
+ mem = reinterpret_cast<VarMemBlock*>(_zone.zalloc(sizeof(VarMemBlock)));
+ if (!mem)
+ {
+ _compiler->setError(ERROR_NO_HEAP_MEMORY);
+ return NULL;
+ }
+
+ mem->offset = 0;
+ mem->size = size;
+
+ mem->nextUsed = _memUsed;
+ mem->nextFree = NULL;
+
+ _memUsed = mem;
+
+ switch (size)
+ {
+ case 16: _mem16BlocksCount++; break;
+ case 8: _mem8BlocksCount++; break;
+ case 4: _mem4BlocksCount++; break;
+ }
+
+ return mem;
+}
+
+void CompilerContext::_freeMemBlock(VarMemBlock* mem) ASMJIT_NOTHROW
+{
+ // Add mem to free blocks.
+ mem->nextFree = _memFree;
+ _memFree = mem;
+}
+
+void CompilerContext::_allocMemoryOperands() ASMJIT_NOTHROW
+{
+ VarMemBlock* mem;
+
+ // Variables are allocated in this order:
+ // 1. 16-byte variables.
+ // 2. 8-byte variables.
+ // 3. 4-byte variables.
+ // 4. All others.
+
+ uint32_t start16 = 0;
+ uint32_t start8 = start16 + _mem16BlocksCount * 16;
+ uint32_t start4 = start8 + _mem8BlocksCount * 8;
+ uint32_t startX = (start4 + _mem4BlocksCount * 4 + 15) & ~15;
+
+ for (mem = _memUsed; mem; mem = mem->nextUsed)
+ {
+ uint32_t size = mem->size;
+ uint32_t offset;
+
+ switch (size)
+ {
+ case 16:
+ offset = start16;
+ start16 += 16;
+ break;
+
+ case 8:
+ offset = start8;
+ start8 += 8;
+ break;
+
+ case 4:
+ offset = start4;
+ start4 += 4;
+ break;
+
+ default:
+ // Align to 16 bytes if size is 16 or more.
+ if (size >= 16)
+ {
+ size = (size + 15) & ~15;
+ startX = (startX + 15) & ~15;
+ }
+ offset = startX;
+ startX += size;
+ break;
+ }
+
+ mem->offset = (int32_t)offset;
+ _memBytesTotal += size;
+ }
+}
+
+void CompilerContext::_patchMemoryOperands(Emittable* start, Emittable* stop) ASMJIT_NOTHROW
+{
+ Emittable* cur;
+
+ for (cur = start;; cur = cur->getNext())
+ {
+ if (cur->getType() == EMITTABLE_INSTRUCTION)
+ {
+ Mem* mem = reinterpret_cast<EInstruction*>(cur)->_memOp;
+
+ if (mem && (mem->_mem.id & OPERAND_ID_TYPE_MASK) == OPERAND_ID_TYPE_VAR)
+ {
+ VarData* vdata = _compiler->_getVarData(mem->_mem.id);
+ ASMJIT_ASSERT(vdata != NULL);
+
+ if (vdata->isMemArgument)
+ {
+ mem->_mem.base = _argumentsBaseReg;
+ mem->_mem.displacement += vdata->homeMemoryOffset;
+ mem->_mem.displacement += _argumentsBaseOffset;
+ }
+ else
+ {
+ VarMemBlock* mb = reinterpret_cast<VarMemBlock*>(vdata->homeMemoryData);
+ ASMJIT_ASSERT(mb != NULL);
+
+ mem->_mem.base = _variablesBaseReg;
+ mem->_mem.displacement += mb->offset;
+ mem->_mem.displacement += _variablesBaseOffset;
+ }
+ }
+ }
+ if (cur == stop) break;
+ }
+}
+
+// ============================================================================
+// [AsmJit::CompilerUtil]
+// ============================================================================
+
+bool CompilerUtil::isStack16ByteAligned()
+{
+ // Stack is always aligned to 16-bytes when using 64-bit OS.
+ bool result = (sizeof(sysuint_t) == 8);
+
+ // Modern Linux, APPLE and UNIX guarantees stack alignment to 16 bytes by
+ // default. I'm really not sure about all UNIX operating systems, because
+ // 16-byte alignment is an addition to the older specification.
+#if (defined(__linux__) || \
+ defined(__linux) || \
+ defined(linux) || \
+ defined(__unix__) || \
+ defined(__FreeBSD__) || \
+ defined(__NetBSD__) || \
+ defined(__OpenBSD__) || \
+ defined(__DARWIN__) || \
+ defined(__APPLE__) )
+ result = true;
+#endif // __linux__
+
+ return result;
+}
+
+// ============================================================================
+// [AsmJit::CompilerCore - Construction / Destruction]
+// ============================================================================
+
+CompilerCore::CompilerCore(CodeGenerator* codeGenerator) ASMJIT_NOTHROW :
+ _codeGenerator(codeGenerator != NULL ? codeGenerator : CodeGenerator::getGlobal()),
+ _zone(16384 - sizeof(Zone::Chunk) - 32),
+ _logger(NULL),
+ _error(0),
+ _properties((1 << PROPERTY_OPTIMIZE_ALIGN)),
+ _emitOptions(0),
+ _finished(false),
+ _first(NULL),
+ _last(NULL),
+ _current(NULL),
+ _function(NULL),
+ _varNameId(0),
+ _cc(NULL)
+{
+}
+
+CompilerCore::~CompilerCore() ASMJIT_NOTHROW
+{
+ free();
+}
+
+// ============================================================================
+// [AsmJit::CompilerCore - Logging]
+// ============================================================================
+
+void CompilerCore::setLogger(Logger* logger) ASMJIT_NOTHROW
+{
+ _logger = logger;
+}
+
+// ============================================================================
+// [AsmJit::CompilerCore - Error Handling]
+// ============================================================================
+
+void CompilerCore::setError(uint32_t error) ASMJIT_NOTHROW
+{
+ _error = error;
+ if (_error == ERROR_NONE) return;
+
+ if (_logger)
+ {
+ _logger->logFormat("*** COMPILER ERROR: %s (%u).\n",
+ getErrorCodeAsString(error),
+ (unsigned int)error);
+ }
+}
+
+// ============================================================================
+// [AsmJit::CompilerCore - Properties]
+// ============================================================================
+
+uint32_t CompilerCore::getProperty(uint32_t propertyId)
+{
+ return (_properties & (1 << propertyId)) != 0;
+}
+
+void CompilerCore::setProperty(uint32_t propertyId, uint32_t value)
+{
+ if (value)
+ _properties |= (1 << propertyId);
+ else
+ _properties &= ~(1 << propertyId);
+}
+
+// ============================================================================
+// [AsmJit::CompilerCore - Buffer]
+// ============================================================================
+
+void CompilerCore::clear() ASMJIT_NOTHROW
+{
+ _finished = false;
+
+ delAll(_first);
+ _first = NULL;
+ _last = NULL;
+ _current = NULL;
+
+ _zone.freeAll();
+ _targetData.clear();
+ _varData.clear();
+
+ _cc = NULL;
+
+ if (_error) setError(ERROR_NONE);
+}
+
+void CompilerCore::free() ASMJIT_NOTHROW
+{
+ clear();
+
+ _targetData.free();
+ _varData.free();
+}
+
+// ============================================================================
+// [AsmJit::CompilerCore - Emittables]
+// ============================================================================
+
+void CompilerCore::addEmittable(Emittable* emittable) ASMJIT_NOTHROW
+{
+ ASMJIT_ASSERT(emittable != NULL);
+ ASMJIT_ASSERT(emittable->_prev == NULL);
+ ASMJIT_ASSERT(emittable->_next == NULL);
+
+ if (_current == NULL)
+ {
+ if (!_first)
+ {
+ _first = emittable;
+ _last = emittable;
+ }
+ else
+ {
+ emittable->_next = _first;
+ _first->_prev = emittable;
+ _first = emittable;
+ }
+ }
+ else
+ {
+ Emittable* prev = _current;
+ Emittable* next = _current->_next;
+
+ emittable->_prev = prev;
+ emittable->_next = next;
+
+ prev->_next = emittable;
+ if (next)
+ next->_prev = emittable;
+ else
+ _last = emittable;
+ }
+
+ _current = emittable;
+}
+
+void CompilerCore::addEmittableAfter(Emittable* emittable, Emittable* ref) ASMJIT_NOTHROW
+{
+ ASMJIT_ASSERT(emittable != NULL);
+ ASMJIT_ASSERT(emittable->_prev == NULL);
+ ASMJIT_ASSERT(emittable->_next == NULL);
+ ASMJIT_ASSERT(ref != NULL);
+
+ Emittable* prev = ref;
+ Emittable* next = ref->_next;
+
+ emittable->_prev = prev;
+ emittable->_next = next;
+
+ prev->_next = emittable;
+ if (next)
+ next->_prev = emittable;
+ else
+ _last = emittable;
+}
+
+void CompilerCore::removeEmittable(Emittable* emittable) ASMJIT_NOTHROW
+{
+ Emittable* prev = emittable->_prev;
+ Emittable* next = emittable->_next;
+
+ if (_first == emittable) { _first = next; } else { prev->_next = next; }
+ if (_last == emittable) { _last = prev; } else { next->_prev = prev; }
+
+ emittable->_prev = NULL;
+ emittable->_next = NULL;
+
+ if (emittable == _current) _current = prev;
+}
+
+Emittable* CompilerCore::setCurrentEmittable(Emittable* current) ASMJIT_NOTHROW
+{
+ Emittable* old = _current;
+ _current = current;
+ return old;
+}
+
+// ============================================================================
+// [AsmJit::CompilerCore - Logging]
+// ============================================================================
+
+void CompilerCore::comment(const char* fmt, ...) ASMJIT_NOTHROW
+{
+ char buf[128];
+ char* p = buf;
+
+ if (fmt)
+ {
+ *p++ = ';';
+ *p++ = ' ';
+
+ va_list ap;
+ va_start(ap, fmt);
+ p += vsnprintf(p, 100, fmt, ap);
+ va_end(ap);
+ }
+
+ *p++ = '\n';
+ *p = '\0';
+
+ addEmittable(Compiler_newObject<EComment>(this, buf));
+}
+
+// ============================================================================
+// [AsmJit::CompilerCore - Function Builder]
+// ============================================================================
+
+EFunction* CompilerCore::newFunction_(
+ uint32_t callingConvention,
+ const uint32_t* arguments,
+ uint32_t argumentsCount,
+ uint32_t returnValue) ASMJIT_NOTHROW
+{
+ ASMJIT_ASSERT(_function == NULL);
+ EFunction* f = _function = Compiler_newObject<EFunction>(this);
+
+ f->setPrototype(callingConvention, arguments, argumentsCount, returnValue);
+ addEmittable(f);
+
+ bind(f->_entryLabel);
+ addEmittable(f->_prolog);
+
+ _varNameId = 0;
+
+ f->_createVariables();
+ return f;
+}
+
+EFunction* CompilerCore::endFunction() ASMJIT_NOTHROW
+{
+ ASMJIT_ASSERT(_function != NULL);
+ EFunction* f = _function;
+
+ bind(f->_exitLabel);
+ addEmittable(f->_epilog);
+ addEmittable(f->_end);
+
+ f->_finished = true;
+ _function = NULL;
+
+ return f;
+}
+
+// ============================================================================
+// [AsmJit::CompilerCore - EmitInstruction]
+// ============================================================================
+
+void CompilerCore::_emitInstruction(uint32_t code) ASMJIT_NOTHROW
+{
+ EInstruction* e = newInstruction(code, NULL, 0);
+ if (!e) return;
+
+ addEmittable(e);
+ if (_cc) { e->_offset = _cc->_currentOffset; e->prepare(*_cc); }
+}
+
+void CompilerCore::_emitInstruction(uint32_t code, const Operand* o0) ASMJIT_NOTHROW
+{
+ Operand* operands = reinterpret_cast<Operand*>(_zone.zalloc(1 * sizeof(Operand)));
+ if (!operands) return;
+
+ operands[0] = *o0;
+
+ EInstruction* e = newInstruction(code, operands, 1);
+ if (!e) return;
+
+ addEmittable(e);
+ if (_cc) { e->_offset = _cc->_currentOffset; e->prepare(*_cc); }
+}
+
+void CompilerCore::_emitInstruction(uint32_t code, const Operand* o0, const Operand* o1) ASMJIT_NOTHROW
+{
+ Operand* operands = reinterpret_cast<Operand*>(_zone.zalloc(2 * sizeof(Operand)));
+ if (!operands) return;
+
+ operands[0] = *o0;
+ operands[1] = *o1;
+
+ EInstruction* e = newInstruction(code, operands, 2);
+ if (!e) return;
+
+ addEmittable(e);
+ if (_cc) { e->_offset = _cc->_currentOffset; e->prepare(*_cc); }
+}
+
+void CompilerCore::_emitInstruction(uint32_t code, const Operand* o0, const Operand* o1, const Operand* o2) ASMJIT_NOTHROW
+{
+ Operand* operands = reinterpret_cast<Operand*>(_zone.zalloc(3 * sizeof(Operand)));
+ if (!operands) return;
+
+ operands[0] = *o0;
+ operands[1] = *o1;
+ operands[2] = *o2;
+
+ EInstruction* e = newInstruction(code, operands, 3);
+ if (!e) return;
+
+ addEmittable(e);
+ if (_cc) { e->_offset = _cc->_currentOffset; e->prepare(*_cc); }
+}
+
+void CompilerCore::_emitInstruction(uint32_t code, const Operand* o0, const Operand* o1, const Operand* o2, const Operand* o3) ASMJIT_NOTHROW
+{
+ Operand* operands = reinterpret_cast<Operand*>(_zone.zalloc(4 * sizeof(Operand)));
+ if (!operands) return;
+
+ operands[0] = *o0;
+ operands[1] = *o1;
+ operands[2] = *o2;
+ operands[3] = *o3;
+
+ EInstruction* e = newInstruction(code, operands, 4);
+ if (!e) return;
+
+ addEmittable(e);
+ if (_cc) { e->_offset = _cc->_currentOffset; e->prepare(*_cc); }
+}
+
+void CompilerCore::_emitInstruction(uint32_t code, const Operand* o0, const Operand* o1, const Operand* o2, const Operand* o3, const Operand* o4) ASMJIT_NOTHROW
+{
+ Operand* operands = reinterpret_cast<Operand*>(_zone.zalloc(5 * sizeof(Operand)));
+ if (!operands) return;
+
+ operands[0] = *o0;
+ operands[1] = *o1;
+ operands[2] = *o2;
+ operands[3] = *o3;
+ operands[4] = *o4;
+
+ EInstruction* e = newInstruction(code, operands, 5);
+ if (!e) return;
+
+ addEmittable(e);
+ if (_cc) { e->_offset = _cc->_currentOffset; e->prepare(*_cc); }
+}
+
+void CompilerCore::_emitJcc(uint32_t code, const Label* label, uint32_t hint) ASMJIT_NOTHROW
+{
+ if (!hint)
+ {
+ _emitInstruction(code, label);
+ }
+ else
+ {
+ Imm imm(hint);
+ _emitInstruction(code, label, &imm);
+ }
+}
+
+ECall* CompilerCore::_emitCall(const Operand* o0) ASMJIT_NOTHROW
+{
+ EFunction* fn = getFunction();
+ if (!fn) { setError(ERROR_NO_FUNCTION); return NULL; }
+
+ ECall* eCall = Compiler_newObject<ECall>(this, fn, o0);
+ if (!eCall) { setError(ERROR_NO_HEAP_MEMORY); return NULL; }
+
+ addEmittable(eCall);
+ return eCall;
+}
+
+void CompilerCore::_emitReturn(const Operand* first, const Operand* second) ASMJIT_NOTHROW
+{
+ EFunction* fn = getFunction();
+ if (!fn) { setError(ERROR_NO_FUNCTION); return; }
+
+ ERet* eRet = Compiler_newObject<ERet>(this, fn, first, second);
+ if (!eRet) { setError(ERROR_NO_HEAP_MEMORY); return; }
+
+ addEmittable(eRet);
+}
+
+// ============================================================================
+// [AsmJit::CompilerCore - Embed]
+// ============================================================================
+
+void CompilerCore::embed(const void* data, sysuint_t size) ASMJIT_NOTHROW
+{
+ // Align length to 16 bytes.
+ sysuint_t alignedSize = (size + 15) & ~15;
+
+ EData* e =
+ new(_zone.zalloc(sizeof(EData) - sizeof(void*) + alignedSize))
+ EData(reinterpret_cast<Compiler*>(this), data, size);
+ addEmittable(e);
+}
+
+// ============================================================================
+// [AsmJit::CompilerCore - Align]
+// ============================================================================
+
+void CompilerCore::align(uint32_t m) ASMJIT_NOTHROW
+{
+ addEmittable(Compiler_newObject<EAlign>(this, m));
+}
+
+// ============================================================================
+// [AsmJit::CompilerCore - Label]
+// ============================================================================
+
+Label CompilerCore::newLabel() ASMJIT_NOTHROW
+{
+ Label label;
+ label._base.id = (uint32_t)_targetData.getLength() | OPERAND_ID_TYPE_LABEL;
+
+ ETarget* target = Compiler_newObject<ETarget>(this, label);
+ _targetData.append(target);
+
+ return label;
+}
+
+void CompilerCore::bind(const Label& label) ASMJIT_NOTHROW
+{
+ uint32_t id = label.getId() & OPERAND_ID_VALUE_MASK;
+ ASMJIT_ASSERT(id != INVALID_VALUE);
+ ASMJIT_ASSERT(id < _targetData.getLength());
+
+ addEmittable(_targetData[id]);
+}
+
+// ============================================================================
+// [AsmJit::CompilerCore - Variables]
+// ============================================================================
+
+VarData* CompilerCore::_newVarData(const char* name, uint32_t type, uint32_t size) ASMJIT_NOTHROW
+{
+ VarData* vdata = reinterpret_cast<VarData*>(_zone.zalloc(sizeof(VarData)));
+ if (vdata == NULL) return NULL;
+
+ char nameBuffer[32];
+ if (name == NULL)
+ {
+ sprintf(nameBuffer, "var_%d", _varNameId);
+ name = nameBuffer;
+ _varNameId++;
+ }
+
+ vdata->scope = getFunction();
+ vdata->firstEmittable = NULL;
+ vdata->firstCallable = NULL;
+ vdata->lastEmittable = NULL;
+
+ vdata->name = _zone.zstrdup(name);
+ vdata->id = (uint32_t)_varData.getLength() | OPERAND_ID_TYPE_VAR;
+ vdata->type = type;
+ vdata->size = size;
+
+ vdata->homeRegisterIndex = INVALID_VALUE;
+ vdata->prefRegisterMask = 0;
+
+ vdata->homeMemoryData = NULL;
+
+ vdata->registerIndex = INVALID_VALUE;
+ vdata->workOffset = INVALID_VALUE;
+
+ vdata->nextActive = NULL;
+ vdata->prevActive = NULL;
+
+ vdata->priority = 10;
+ vdata->calculated = false;
+ vdata->isRegArgument = false;
+ vdata->isMemArgument = false;
+
+ vdata->state = VARIABLE_STATE_UNUSED;
+ vdata->changed = false;
+ vdata->saveOnUnuse = false;
+
+ vdata->registerReadCount = 0;
+ vdata->registerWriteCount = 0;
+ vdata->registerRWCount = 0;
+
+ vdata->registerGPBLoCount = 0;
+ vdata->registerGPBHiCount = 0;
+
+ vdata->memoryReadCount = 0;
+ vdata->memoryWriteCount = 0;
+ vdata->memoryRWCount = 0;
+
+ vdata->tempPtr = NULL;
+
+ _varData.append(vdata);
+ return vdata;
+}
+
+GPVar CompilerCore::newGP(uint32_t variableType, const char* name) ASMJIT_NOTHROW
+{
+ ASMJIT_ASSERT((variableType < _VARIABLE_TYPE_COUNT) &&
+ (variableInfo[variableType].clazz & VariableInfo::CLASS_GP) != 0);
+
+#if defined(ASMJIT_X86)
+ if (variableInfo[variableType].size > 4)
+ {
+ variableType = VARIABLE_TYPE_GPD;
+ if (_logger)
+ {
+ _logger->logString("*** COMPILER WARNING: Translated QWORD variable to DWORD, FIX YOUR CODE! ***\n");
+ }
+ }
+#endif // ASMJIT_X86
+
+ VarData* vdata = _newVarData(name, variableType, variableInfo[variableType].size);
+ return GPVarFromData(vdata);
+}
+
+GPVar CompilerCore::argGP(uint32_t index) ASMJIT_NOTHROW
+{
+ GPVar var;
+ EFunction* f = getFunction();
+
+ if (f)
+ {
+ const FunctionPrototype& prototype = f->getPrototype();
+ if (index < prototype.getArgumentsCount())
+ {
+ VarData* vdata = getFunction()->_argumentVariables[index];
+
+ var._var.id = vdata->id;
+ var._var.size = vdata->size;
+ var._var.registerCode = variableInfo[vdata->type].code;
+ var._var.variableType = vdata->type;
+ }
+ }
+
+ return var;
+}
+
+MMVar CompilerCore::newMM(uint32_t variableType, const char* name) ASMJIT_NOTHROW
+{
+ ASMJIT_ASSERT((variableType < _VARIABLE_TYPE_COUNT) &&
+ (variableInfo[variableType].clazz & VariableInfo::CLASS_MM) != 0);
+
+ VarData* vdata = _newVarData(name, variableType, 8);
+ return MMVarFromData(vdata);
+}
+
+MMVar CompilerCore::argMM(uint32_t index) ASMJIT_NOTHROW
+{
+ MMVar var;
+ EFunction* f = getFunction();
+
+ if (f)
+ {
+ const FunctionPrototype& prototype = f->getPrototype();
+ if (prototype.getArgumentsCount() < index)
+ {
+ VarData* vdata = getFunction()->_argumentVariables[index];
+
+ var._var.id = vdata->id;
+ var._var.size = vdata->size;
+ var._var.registerCode = variableInfo[vdata->type].code;
+ var._var.variableType = vdata->type;
+ }
+ }
+
+ return var;
+}
+
+XMMVar CompilerCore::newXMM(uint32_t variableType, const char* name) ASMJIT_NOTHROW
+{
+ ASMJIT_ASSERT((variableType < _VARIABLE_TYPE_COUNT) &&
+ (variableInfo[variableType].clazz & VariableInfo::CLASS_XMM) != 0);
+
+ VarData* vdata = _newVarData(name, variableType, 16);
+ return XMMVarFromData(vdata);
+}
+
+XMMVar CompilerCore::argXMM(uint32_t index) ASMJIT_NOTHROW
+{
+ XMMVar var;
+ EFunction* f = getFunction();
+
+ if (f)
+ {
+ const FunctionPrototype& prototype = f->getPrototype();
+ if (prototype.getArgumentsCount() < index)
+ {
+ VarData* vdata = getFunction()->_argumentVariables[index];
+
+ var._var.id = vdata->id;
+ var._var.size = vdata->size;
+ var._var.registerCode = variableInfo[vdata->type].code;
+ var._var.variableType = vdata->type;
+ }
+ }
+
+ return var;
+}
+
+void CompilerCore::_vhint(BaseVar& var, uint32_t hintId, uint32_t hintValue) ASMJIT_NOTHROW
+{
+ if (var.getId() == INVALID_VALUE) return;
+
+ VarData* vdata = _getVarData(var.getId());
+ ASMJIT_ASSERT(vdata != NULL);
+
+ EVariableHint* e = Compiler_newObject<EVariableHint>(this, vdata, hintId, hintValue);
+ addEmittable(e);
+}
+
+void CompilerCore::alloc(BaseVar& var) ASMJIT_NOTHROW
+{
+ _vhint(var, VARIABLE_HINT_ALLOC, INVALID_VALUE);
+}
+
+void CompilerCore::alloc(BaseVar& var, uint32_t regIndex) ASMJIT_NOTHROW
+{
+ _vhint(var, VARIABLE_HINT_ALLOC, regIndex);
+}
+
+void CompilerCore::alloc(BaseVar& var, const BaseReg& reg) ASMJIT_NOTHROW
+{
+ _vhint(var, VARIABLE_HINT_ALLOC, reg.getRegIndex());
+}
+
+void CompilerCore::save(BaseVar& var) ASMJIT_NOTHROW
+{
+ _vhint(var, VARIABLE_HINT_SAVE, INVALID_VALUE);
+}
+
+void CompilerCore::spill(BaseVar& var) ASMJIT_NOTHROW
+{
+ _vhint(var, VARIABLE_HINT_SPILL, INVALID_VALUE);
+}
+
+void CompilerCore::unuse(BaseVar& var) ASMJIT_NOTHROW
+{
+ _vhint(var, VARIABLE_HINT_UNUSE, INVALID_VALUE);
+}
+
+uint32_t CompilerCore::getPriority(BaseVar& var) const ASMJIT_NOTHROW
+{
+ if (var.getId() == INVALID_VALUE) return INVALID_VALUE;
+
+ VarData* vdata = _getVarData(var.getId());
+ ASMJIT_ASSERT(vdata != NULL);
+
+ return vdata->priority;
+}
+
+void CompilerCore::setPriority(BaseVar& var, uint32_t priority) ASMJIT_NOTHROW
+{
+ if (var.getId() == INVALID_VALUE) return;
+
+ VarData* vdata = _getVarData(var.getId());
+ ASMJIT_ASSERT(vdata != NULL);
+
+ if (priority > 100) priority = 100;
+ vdata->priority = (uint8_t)priority;
+}
+
+bool CompilerCore::getSaveOnUnuse(BaseVar& var) const ASMJIT_NOTHROW
+{
+ if (var.getId() == INVALID_VALUE) return false;
+
+ VarData* vdata = _getVarData(var.getId());
+ ASMJIT_ASSERT(vdata != NULL);
+
+ return (bool)vdata->saveOnUnuse;
+}
+
+void CompilerCore::setSaveOnUnuse(BaseVar& var, bool value) ASMJIT_NOTHROW
+{
+ if (var.getId() == INVALID_VALUE) return;
+
+ VarData* vdata = _getVarData(var.getId());
+ ASMJIT_ASSERT(vdata != NULL);
+
+ vdata->saveOnUnuse = value;
+}
+
+void CompilerCore::rename(BaseVar& var, const char* name) ASMJIT_NOTHROW
+{
+ if (var.getId() == INVALID_VALUE) return;
+
+ VarData* vdata = _getVarData(var.getId());
+ ASMJIT_ASSERT(vdata != NULL);
+
+ vdata->name = _zone.zstrdup(name);
+}
+
+// ============================================================================
+// [AsmJit::CompilerCore - State]
+// ============================================================================
+
+StateData* CompilerCore::_newStateData(uint32_t memVarsCount) ASMJIT_NOTHROW
+{
+ StateData* state = reinterpret_cast<StateData*>(_zone.zalloc(sizeof(StateData) + memVarsCount * sizeof(void*)));
+ return state;
+}
+
+// ============================================================================
+// [AsmJit::CompilerCore - Make]
+// ============================================================================
+
+void* CompilerCore::make() ASMJIT_NOTHROW
+{
+ Assembler a(_codeGenerator);
+ a._properties = _properties;
+ a.setLogger(_logger);
+
+ serialize(a);
+
+ if (this->getError())
+ {
+ return NULL;
+ }
+
+ if (a.getError())
+ {
+ setError(a.getError());
+ return NULL;
+ }
+
+ void* result = a.make();
+ if (_logger)
+ {
+ _logger->logFormat("*** COMPILER SUCCESS - Wrote %u bytes, code: %u, trampolines: %u.\n\n",
+ (unsigned int)a.getCodeSize(),
+ (unsigned int)a.getOffset(),
+ (unsigned int)a.getTrampolineSize());
+ }
+ return result;
+}
+
+void CompilerCore::serialize(Assembler& a) ASMJIT_NOTHROW
+{
+ // Context.
+ CompilerContext cc(reinterpret_cast<Compiler*>(this));
+
+ Emittable* start = _first;
+ Emittable* stop = NULL;
+
+ // Register all labels.
+ a.registerLabels(_targetData.getLength());
+
+ // Make code.
+ for (;;)
+ {
+ _cc = NULL;
+
+ // ------------------------------------------------------------------------
+ // Find a function.
+ for (;;)
+ {
+ if (start == NULL) return;
+ if (start->getType() == EMITTABLE_FUNCTION)
+ break;
+ else
+ start->emit(a);
+
+ start = start->getNext();
+ }
+ // ------------------------------------------------------------------------
+
+ // ------------------------------------------------------------------------
+ // Setup code generation context.
+ Emittable* cur;
+
+ cc._function = reinterpret_cast<EFunction*>(start);
+ cc._start = start;
+ cc._stop = stop = cc._function->getEnd();
+ cc._extraBlock = stop->getPrev();
+
+ // Detect whether the function generation was finished.
+ if (!cc._function->_finished || cc._function->getEnd()->getPrev() == NULL)
+ {
+ setError(ERROR_INCOMPLETE_FUNCTION);
+ return;
+ }
+ // ------------------------------------------------------------------------
+
+ // ------------------------------------------------------------------------
+ // Step 1:
+ // - Assign/increment offset to each emittable.
+ // - Extract variables from instructions.
+ // - Prepare variables for register allocator:
+ // - Update read(r) / write(w) / overwrite(x) statistics.
+ // - Update register / memory usage statistics.
+ // - Find scope (first / last emittable) of variables.
+ for (cur = start; ; cur = cur->getNext())
+ {
+ cur->prepare(cc);
+ if (cur == stop) break;
+ }
+ // ------------------------------------------------------------------------
+
+ // We set compiler context also to Compiler so new emitted instructions
+ // can call prepare() to itself.
+ _cc = &cc;
+
+ // ------------------------------------------------------------------------
+ // Step 2:
+ // - Translate special instructions (imul, cmpxchg8b, ...).
+ // - Alloc registers.
+ // - Translate forward jumps.
+ // - Alloc memory operands (variables related).
+ // - Emit function prolog.
+ // - Emit function epilog.
+ // - Patch memory operands (variables related).
+ // - Dump function prototype and variable statistics (if enabled).
+
+ // Translate special instructions and run alloc registers.
+ cur = start;
+
+ do {
+ do {
+ // Assign current offset for each emittable back to CompilerContext.
+ cc._currentOffset = cur->_offset;
+ // Assign previous emittable to compiler so each variable spill/alloc will
+ // be emitted before.
+ _current = cur->getPrev();
+
+ cur = cur->translate(cc);
+ } while (cur);
+
+ cc._unrecheable = true;
+
+ sysuint_t len = cc._backCode.getLength();
+ while (cc._backPos < len)
+ {
+ cur = cc._backCode[cc._backPos++]->getNext();
+ if (!cur->isTranslated()) break;
+
+ cur = NULL;
+ }
+ } while (cur);
+
+ // Translate forward jumps.
+ {
+ ForwardJumpData* j = cc._forwardJumps;
+ while (j)
+ {
+ cc._assignState(j->state);
+ _current = j->inst->getPrev();
+ j->inst->_doJump(cc);
+ j = j->next;
+ }
+ }
+
+ // Alloc memory operands (variables related).
+ cc._allocMemoryOperands();
+
+ // Emit function prolog / epilog.
+ cc._function->_preparePrologEpilog(cc);
+
+ _current = cc._function->_prolog;
+ cc._function->_emitProlog(cc);
+
+ _current = cc._function->_epilog;
+ cc._function->_emitEpilog(cc);
+
+ // Patch memory operands (variables related).
+ _current = _last;
+ cc._patchMemoryOperands(start, stop);
+
+ // Dump function prototype and variable statistics (if enabled).
+ if (_logger)
+ {
+ cc._function->_dumpFunction(cc);
+ }
+ // ------------------------------------------------------------------------
+
+ // ------------------------------------------------------------------------
+ // Hack: need to register labels that was created by the Step 2.
+ if (a._labelData.getLength() < _targetData.getLength())
+ {
+ a.registerLabels(_targetData.getLength() - a._labelData.getLength());
+ }
+
+ Emittable* extraBlock = cc._extraBlock;
+
+ // Step 3:
+ // - Emit instructions to Assembler stream.
+ for (cur = start; ; cur = cur->getNext())
+ {
+ cur->emit(a);
+ if (cur == extraBlock) break;
+ }
+ // ------------------------------------------------------------------------
+
+ // ------------------------------------------------------------------------
+ // Step 4:
+ // - Emit everything else (post action).
+ for (cur = start; ; cur = cur->getNext())
+ {
+ cur->post(a);
+ if (cur == extraBlock) break;
+ }
+ // ------------------------------------------------------------------------
+
+ start = extraBlock->getNext();
+ cc._clear();
+ }
+}
+
+// ============================================================================
+// [AsmJit::Compiler - Construction / Destruction]
+// ============================================================================
+
+Compiler::Compiler(CodeGenerator* codeGenerator) ASMJIT_NOTHROW :
+ CompilerIntrinsics(codeGenerator)
+{
+}
+
+Compiler::~Compiler() ASMJIT_NOTHROW
+{
+}
+
+} // AsmJit namespace
+
+// [Api-End]
+#include "ApiEnd.h"