Commit a83607d6 authored by peastman's avatar peastman
Browse files

Preliminary implementation of JIT compilation for CompiledExpressions

parent 4c19a401
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Guard]
#ifndef _ASMJIT_BASE_CPUTICKS_H
#define _ASMJIT_BASE_CPUTICKS_H
// [Dependencies - AsmJit]
#include "../base/globals.h"
// [Api-Begin]
#include "../apibegin.h"
namespace asmjit {
//! \addtogroup asmjit_base_util
//! \{
// ============================================================================
// [asmjit::CpuTicks]
// ============================================================================
//! CPU ticks utilities.
struct CpuTicks {
//! Get the current CPU ticks for benchmarking (1ms resolution).
static ASMJIT_API uint32_t now();
};
//! \}
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
// [Guard]
#endif // _ASMJIT_BASE_CPUTICKS_H
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Export]
#define ASMJIT_EXPORTS
// [Dependencies - AsmJit]
#include "../base/error.h"
#include "../base/intutil.h"
// [Api-Begin]
#include "../apibegin.h"
namespace asmjit {
// ============================================================================
// [asmjit::ErrorHandler - Construction / Destruction]
// ============================================================================
ErrorHandler::ErrorHandler() {}
ErrorHandler::~ErrorHandler() {}
// ============================================================================
// [asmjit::ErrorHandler - Interface]
// ============================================================================
ErrorHandler* ErrorHandler::addRef() const {
return const_cast<ErrorHandler*>(this);
}
void ErrorHandler::release() {}
// ============================================================================
// [asmjit::ErrorUtil - AsString]
// ============================================================================
#if !defined(ASMJIT_DISABLE_NAMES)
static const char errorMessages[] = {
"Ok\0"
"No heap memory\0"
"No virtual memory\0"
"Invalid argument\0"
"Invalid state\0"
"No code generated\0"
"Code too large\0"
"Label already bound\0"
"Unknown instruction\0"
"Illegal instruction\0"
"Illegal addressing\0"
"Illegal displacement\0"
"Overlapped arguments\0"
"Unknown error\0"
};
static const char* findPackedString(const char* p, uint32_t id, uint32_t maxId) {
uint32_t i = 0;
if (id > maxId)
id = maxId;
while (i < id) {
while (p[0])
p++;
p++;
}
return p;
}
const char* ErrorUtil::asString(Error e) {
return findPackedString(errorMessages, e, kErrorCount);
}
#endif // ASMJIT_DISABLE_NAMES
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Guard]
#ifndef _ASMJIT_BASE_ERROR_H
#define _ASMJIT_BASE_ERROR_H
// [Api-Begin]
#include "../apibegin.h"
// [Dependencies - AsmJit]
#include "../base/globals.h"
namespace asmjit {
//! \addtogroup asmjit_base_general
//! \{
// ============================================================================
// [asmjit::kError]
// ============================================================================
//! AsmJit error codes.
ASMJIT_ENUM(kError) {
//! No error (success).
//!
//! This is default state and state you want.
kErrorOk = 0,
//! Heap memory allocation failed.
kErrorNoHeapMemory = 1,
//! Virtual memory allocation failed.
kErrorNoVirtualMemory = 2,
//! Invalid argument.
kErrorInvalidArgument = 3,
//! Invalid state.
kErrorInvalidState = 4,
//! No code generated.
//!
//! Returned by runtime if the code-generator contains no code.
kErrorNoCodeGenerated = 5,
//! Code generated is too large to fit in memory reserved.
//!
//! Returned by `StaticRuntime` in case that the code generated is too large
//! to fit in the memory already reserved for it.
kErrorCodeTooLarge = 6,
//! Label is already bound.
kErrorLabelAlreadyBound = 7,
//! Unknown instruction (an instruction ID is out of bounds or instruction
//! name is invalid).
kErrorUnknownInst = 8,
//! Illegal instruction.
//!
//! This status code can also be returned in X64 mode if AH, BH, CH or DH
//! registers have been used together with a REX prefix. The instruction
//! is not encodable in such case.
//!
//! Example of raising `kErrorIllegalInst` error.
//!
//! ~~~
//! // Invalid address size.
//! a.mov(dword_ptr(eax), al);
//!
//! // Undecodable instruction - AH used with R10, however R10 can only be
//! // encoded by using REX prefix, which conflicts with AH.
//! a.mov(byte_ptr(r10), ah);
//! ~~~
//!
//! \note In debug mode assertion is raised instead of returning an error.
kErrorIllegalInst = 9,
//! Illegal (unencodable) addressing used.
kErrorIllegalAddresing = 10,
//! Illegal (unencodable) displacement used.
//!
//! X86/X64
//! -------
//!
//! Short form of jump instruction has been used, but the displacement is out
//! of bounds.
kErrorIllegalDisplacement = 11,
//! A variable has been assigned more than once to a function argument (Compiler).
kErrorOverlappedArgs = 12,
//! Count of AsmJit error codes.
kErrorCount = 13
};
// ============================================================================
// [asmjit::Error]
// ============================================================================
//! AsmJit error type (unsigned integer).
typedef uint32_t Error;
// ============================================================================
// [asmjit::ErrorHandler]
// ============================================================================
//! Error handler.
//!
//! Error handler can be used to override the default behavior of `CodeGen`
//! error handling and propagation. See `handleError` on how to override it.
//!
//! Please note that `addRef` and `release` functions are used, but there is
//! no reference counting implemented by default, reimplement to change the
//! default behavior.
struct ASMJIT_VCLASS ErrorHandler {
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! Create a new `ErrorHandler` instance.
ASMJIT_API ErrorHandler();
//! Destroy the `ErrorHandler` instance.
ASMJIT_API virtual ~ErrorHandler();
// --------------------------------------------------------------------------
// [Interface]
// --------------------------------------------------------------------------
//! Reference this error handler.
//!
//! \note This member function is provided for convenience. The default
//! implementation does nothing. If you are working in environment where
//! multiple `ErrorHandler` instances are used by a different code generators
//! you may provide your own functionality for reference counting. In that
//! case `addRef()` and `release()` functions should be overridden.
ASMJIT_API virtual ErrorHandler* addRef() const;
//! Release this error handler.
//!
//! \note This member function is provided for convenience. See `addRef()`
//! for more detailed information related to reference counting.
ASMJIT_API virtual void release();
//! Error handler (pure).
//!
//! Error handler is called when an error happened. An error can happen in
//! many places, but error handler is mostly used by `Assembler` and
//! `Compiler` classes to report anything that may cause incorrect code
//! generation. There are multiple ways how the error handler can be used
//! and each has it's pros/cons.
//!
//! AsmJit library doesn't use exceptions and can be compiled with or without
//! exception handling support. Even if the AsmJit library is compiled without
//! exceptions it is exception-safe and handleError() can report an incoming
//! error by throwing an exception of any type. It's guaranteed that the
//! exception won't be catched by AsmJit and will be propagated to the code
//! calling AsmJit `Assembler` or `Compiler` methods. Alternative to
//! throwing an exception is using `setjmp()` and `longjmp()` pair available
//! in the standard C library.
//!
//! If the exception or setjmp() / longjmp() mechanism is used, the state of
//! the `BaseAssember` or `Compiler` is unchanged and if it's possible the
//! execution (instruction serialization) can continue. However if the error
//! happened during any phase that translates or modifies the stored code
//! (for example relocation done by `Assembler` or analysis/translation
//! done by `Compiler`) the execution can't continue and the error will
//! be also stored in `Assembler` or `Compiler`.
//!
//! Finally, if no exceptions nor setjmp() / longjmp() mechanisms were used,
//! you can still implement a compatible handling by returning from your
//! error handler. Returning `true` means that error was reported and AsmJit
//! should continue execution, but `false` sets the rror immediately to the
//! `Assembler` or `Compiler` and execution shouldn't continue (this
//! is the default behavior in case no error handler is used).
virtual bool handleError(Error code, const char* message) = 0;
};
// ============================================================================
// [asmjit::ErrorUtil]
// ============================================================================
//! Error utilities.
struct ErrorUtil {
#if !defined(ASMJIT_DISABLE_NAMES)
//! Get printable version of AsmJit `kError` code.
static ASMJIT_API const char* asString(Error code);
#endif // ASMJIT_DISABLE_NAMES
};
//! \}
// ============================================================================
// [ASMJIT_PROPAGATE_ERROR]
// ============================================================================
//! \internal
//!
//! Used by AsmJit to return the `_Exp_` result if it's an error.
#define ASMJIT_PROPAGATE_ERROR(_Exp_) \
do { \
::asmjit::Error errval_ = (_Exp_); \
if (errval_ != ::asmjit::kErrorOk) \
return errval_; \
} while (0)
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
// [Guard]
#endif // _ASMJIT_BASE_ERROR_H
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Export]
#define ASMJIT_EXPORTS
// [Dependencies - AsmJit]
#include "../base/globals.h"
// [Api-Begin]
#include "../apibegin.h"
namespace asmjit {
// ============================================================================
// [asmjit::Assert]
// ============================================================================
void assertionFailed(const char* exp, const char* file, int line) {
::fprintf(stderr, "Assertion failed: %s\n, file %s, line %d\n", exp, file, line);
::abort();
}
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Guard]
#ifndef _ASMJIT_BASE_GLOBALS_H
#define _ASMJIT_BASE_GLOBALS_H
// [Dependencies - AsmJit]
#include "../build.h"
// [Api-Begin]
#include "../apibegin.h"
namespace asmjit {
//! \addtogroup asmjit_base_general
//! \{
// ============================================================================
// [asmjit::Ptr / SignedPtr]
// ============================================================================
//! 64-bit unsigned pointer, compatible with JIT and non-JIT generators.
//!
//! This is the preferred pointer type to use with AsmJit library. It has a
//! capability to hold any pointer for any architecture making it an ideal
//! candidate for cross-platform code generation.
typedef uint64_t Ptr;
//! 64-bit signed pointer, like \ref Ptr, but made signed.
typedef int64_t SignedPtr;
// ============================================================================
// [asmjit::kGlobals]
// ============================================================================
//! Invalid index
//!
//! Invalid index is the last possible index that is never used in practice. In
//! AsmJit it is used exclusively with strings to indicate the the length of the
//! string is not known and has to be determined.
static const size_t kInvalidIndex = ~static_cast<size_t>(0);
//! Invalid base address.
static const Ptr kNoBaseAddress = static_cast<Ptr>(static_cast<SignedPtr>(-1));
//! Global constants.
ASMJIT_ENUM(kGlobals) {
//! Invalid value or operand id.
kInvalidValue = 0xFFFFFFFF,
//! Invalid register index.
kInvalidReg = 0xFF,
//! Invalid variable type.
kInvalidVar = 0xFF,
//! Host memory allocator overhead.
//!
//! The overhead is decremented from all zone allocators so the operating
//! system doesn't have allocate extra virtual page to keep tract of the
//! requested memory block.
//!
//! The number is actually a guess.
kMemAllocOverhead = sizeof(intptr_t) * 4,
//! Memory grow threshold.
//!
//! After the grow threshold is reached the capacity won't be doubled
//! anymore.
kMemAllocGrowMax = 8192 * 1024
};
// ============================================================================
// [asmjit::kArch]
// ============================================================================
//! Architecture.
ASMJIT_ENUM(kArch) {
//! No/Unknown architecture.
kArchNone = 0,
//! X86 architecture.
kArchX86 = 1,
//! X64 architecture, also called AMD64.
kArchX64 = 2,
//! Arm architecture.
kArchArm = 4,
#if defined(ASMJIT_HOST_X86)
kArchHost = kArchX86,
#endif // ASMJIT_HOST_X86
#if defined(ASMJIT_HOST_X64)
kArchHost = kArchX64,
#endif // ASMJIT_HOST_X64
#if defined(ASMJIT_HOST_ARM)
kArchHost = kArchArm,
#endif // ASMJIT_HOST_ARM
//! Whether the host is 64-bit.
kArchHost64Bit = sizeof(intptr_t) >= 8
};
//! \}
// ============================================================================
// [asmjit::Init / NoInit]
// ============================================================================
#if !defined(ASMJIT_DOCGEN)
struct _Init {};
static const _Init Init = {};
struct _NoInit {};
static const _NoInit NoInit = {};
#endif // !ASMJIT_DOCGEN
// ============================================================================
// [asmjit::Assert]
// ============================================================================
//! \addtogroup asmjit_base_general
//! \{
//! Called in debug build on assertion failure.
//!
//! \param exp Expression that failed.
//! \param file Source file name where it happened.
//! \param line Line in the source file.
//!
//! If you have problems with assertions put a breakpoint at assertionFailed()
//! function (asmjit/base/globals.cpp) and check the call stack to locate the
//! failing code.
ASMJIT_API void assertionFailed(const char* exp, const char* file, int line);
#if defined(ASMJIT_DEBUG)
#define ASMJIT_ASSERT(_Exp_) \
do { \
if (!(_Exp_)) ::asmjit::assertionFailed(#_Exp_, __FILE__, __LINE__); \
} while (0)
#else
#define ASMJIT_ASSERT(_Exp_) ASMJIT_NOP()
#endif // DEBUG
//! \}
} // asmjit namespace
// ============================================================================
// [asmjit_cast<>]
// ============================================================================
//! \addtogroup asmjit_base_util
//! \{
//! Cast used to cast pointer to function. It's like reinterpret_cast<>,
//! but uses internally C style cast to work with MinGW.
//!
//! If you are using single compiler and `reinterpret_cast<>` works for you,
//! there is no reason to use `asmjit_cast<>`. If you are writing
//! cross-platform software with various compiler support, consider using
//! `asmjit_cast<>` instead of `reinterpret_cast<>`.
template<typename T, typename Z>
static ASMJIT_INLINE T asmjit_cast(Z* p) { return (T)p; }
//! \}
// [Api-End]
#include "../apiend.h"
// [Guard]
#endif // _ASMJIT_BASE_GLOBALS_H
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Export]
#define ASMJIT_EXPORTS
// [Dependencies - AsmJit]
#include "../base/intutil.h"
// [Api-Begin]
#include "../apibegin.h"
namespace asmjit {
#if defined(ASMJIT_TEST)
UNIT(base_intutil) {
uint32_t i;
INFO("IntTraits<>.");
EXPECT(IntTraits<signed char>::kIsSigned,
"IntTraits<signed char> should report signed.");
EXPECT(IntTraits<unsigned char>::kIsUnsigned,
"IntTraits<unsigned char> should report unsigned.");
EXPECT(IntTraits<signed short>::kIsSigned,
"IntTraits<signed short> should report signed.");
EXPECT(IntTraits<unsigned short>::kIsUnsigned,
"IntTraits<unsigned short> should report unsigned.");
EXPECT(IntTraits<int>::kIsSigned,
"IntTraits<int> should report signed.");
EXPECT(IntTraits<unsigned int>::kIsUnsigned,
"IntTraits<unsigned int> should report unsigned.");
EXPECT(IntTraits<long>::kIsSigned,
"IntTraits<long> should report signed.");
EXPECT(IntTraits<unsigned long>::kIsUnsigned,
"IntTraits<unsigned long> should report unsigned.");
EXPECT(IntTraits<intptr_t>::kIsSigned,
"IntTraits<intptr_t> should report signed.");
EXPECT(IntTraits<uintptr_t>::kIsUnsigned,
"IntTraits<uintptr_t> should report unsigned.");
EXPECT(IntTraits<intptr_t>::kIsIntPtr,
"IntTraits<intptr_t> should report intptr_t type.");
EXPECT(IntTraits<uintptr_t>::kIsIntPtr,
"IntTraits<uintptr_t> should report intptr_t type.");
INFO("IntUtil::iMin()/iMax().");
EXPECT(IntUtil::iMin<int>(0, -1) == -1,
"IntUtil::iMin<int> should return a minimum value.");
EXPECT(IntUtil::iMin<int>(-1, -2) == -2,
"IntUtil::iMin<int> should return a minimum value.");
EXPECT(IntUtil::iMin<int>(1, 2) == 1,
"IntUtil::iMin<int> should return a minimum value.");
EXPECT(IntUtil::iMax<int>(0, -1) == 0,
"IntUtil::iMax<int> should return a maximum value.");
EXPECT(IntUtil::iMax<int>(-1, -2) == -1,
"IntUtil::iMax<int> should return a maximum value.");
EXPECT(IntUtil::iMax<int>(1, 2) == 2,
"IntUtil::iMax<int> should return a maximum value.");
INFO("IntUtil::inInterval().");
EXPECT(IntUtil::inInterval<int>(11, 10, 20) == true,
"IntUtil::inInterval<int> should return true if inside.");
EXPECT(IntUtil::inInterval<int>(101, 10, 20) == false,
"IntUtil::inInterval<int> should return false if outside.");
INFO("IntUtil::isInt8().");
EXPECT(IntUtil::isInt8(-128) == true,
"IntUtil::isInt8<> should return true if inside.");
EXPECT(IntUtil::isInt8(127) == true,
"IntUtil::isInt8<> should return true if inside.");
EXPECT(IntUtil::isInt8(-129) == false,
"IntUtil::isInt8<> should return false if outside.");
EXPECT(IntUtil::isInt8(128) == false,
"IntUtil::isInt8<> should return false if outside.");
INFO("IntUtil::isUInt8().");
EXPECT(IntUtil::isUInt8(255) == true,
"IntUtil::isUInt8<> should return true if inside.");
EXPECT(IntUtil::isUInt8(256) == false,
"IntUtil::isUInt8<> should return false if outside.");
EXPECT(IntUtil::isUInt8(-1) == false,
"IntUtil::isUInt8<> should return false if negative.");
INFO("IntUtil::isInt16().");
EXPECT(IntUtil::isInt16(-32768) == true,
"IntUtil::isInt16<> should return true if inside.");
EXPECT(IntUtil::isInt16(32767) == true,
"IntUtil::isInt16<> should return true if inside.");
EXPECT(IntUtil::isInt16(-32769) == false,
"IntUtil::isInt16<> should return false if outside.");
EXPECT(IntUtil::isInt16(32768) == false,
"IntUtil::isInt16<> should return false if outside.");
INFO("IntUtil::isUInt16().");
EXPECT(IntUtil::isUInt16(65535) == true,
"IntUtil::isUInt16<> should return true if inside.");
EXPECT(IntUtil::isUInt16(65536) == false,
"IntUtil::isUInt16<> should return false if outside.");
EXPECT(IntUtil::isUInt16(-1) == false,
"IntUtil::isUInt16<> should return false if negative.");
INFO("IntUtil::isInt32().");
EXPECT(IntUtil::isInt32(2147483647) == true,
"IntUtil::isInt32<int> should return true if inside.");
EXPECT(IntUtil::isInt32(-2147483647 - 1) == true,
"IntUtil::isInt32<int> should return true if inside.");
EXPECT(IntUtil::isInt32(ASMJIT_UINT64_C(2147483648)) == false,
"IntUtil::isInt32<int> should return false if outside.");
EXPECT(IntUtil::isInt32(ASMJIT_UINT64_C(0xFFFFFFFF)) == false,
"IntUtil::isInt32<int> should return false if outside.");
EXPECT(IntUtil::isInt32(ASMJIT_UINT64_C(0xFFFFFFFF) + 1) == false,
"IntUtil::isInt32<int> should return false if outside.");
INFO("IntUtil::isUInt32().");
EXPECT(IntUtil::isUInt32(ASMJIT_UINT64_C(0xFFFFFFFF)) == true,
"IntUtil::isUInt32<int> should return true if inside.");
EXPECT(IntUtil::isUInt32(ASMJIT_UINT64_C(0xFFFFFFFF) + 1) == false,
"IntUtil::isUInt32<int> should return false if outside.");
EXPECT(IntUtil::isUInt32(-1) == false,
"IntUtil::isUInt32<int> should return false if negative.");
INFO("IntUtil::isPower2().");
for (i = 0; i < 64; i++) {
EXPECT(IntUtil::isPowerOf2(static_cast<uint64_t>(1) << i) == true,
"IntUtil::isPower2() didn't report power of 2.");
EXPECT(IntUtil::isPowerOf2((static_cast<uint64_t>(1) << i) ^ 0x001101) == false,
"IntUtil::isPower2() didn't report not power of 2.");
}
INFO("IntUtil::mask().");
for (i = 0; i < 32; i++) {
EXPECT(IntUtil::mask(i) == (1 << i),
"IntUtil::mask(%u) should return %X.", i, (1 << i));
}
INFO("IntUtil::bits().");
for (i = 0; i < 32; i++) {
uint32_t expectedBits = 0;
for (uint32_t b = 0; b < i; b++)
expectedBits |= static_cast<uint32_t>(1) << b;
EXPECT(IntUtil::bits(i) == expectedBits,
"IntUtil::bits(%u) should return %X.", i, expectedBits);
}
INFO("IntUtil::hasBit().");
for (i = 0; i < 32; i++) {
EXPECT(IntUtil::hasBit((1 << i), i) == true,
"IntUtil::hasBit(%X, %u) should return true.", (1 << i), i);
}
INFO("IntUtil::bitCount().");
for (i = 0; i < 32; i++) {
EXPECT(IntUtil::bitCount((1 << i)) == 1,
"IntUtil::bitCount(%X) should return true.", (1 << i));
}
EXPECT(IntUtil::bitCount(0x000000F0) == 4, "");
EXPECT(IntUtil::bitCount(0x10101010) == 4, "");
EXPECT(IntUtil::bitCount(0xFF000000) == 8, "");
EXPECT(IntUtil::bitCount(0xFFFFFFF7) == 31, "");
EXPECT(IntUtil::bitCount(0x7FFFFFFF) == 31, "");
INFO("IntUtil::findFirstBit().");
for (i = 0; i < 32; i++) {
EXPECT(IntUtil::findFirstBit((1 << i)) == i,
"IntUtil::findFirstBit(%X) should return %u.", (1 << i), i);
}
INFO("IntUtil::isAligned().");
EXPECT(IntUtil::isAligned<size_t>(0xFFFF, 4) == false, "");
EXPECT(IntUtil::isAligned<size_t>(0xFFF4, 4) == true , "");
EXPECT(IntUtil::isAligned<size_t>(0xFFF8, 8) == true , "");
EXPECT(IntUtil::isAligned<size_t>(0xFFF0, 16) == true , "");
INFO("IntUtil::alignTo().");
EXPECT(IntUtil::alignTo<size_t>(0xFFFF, 4) == 0x10000, "");
EXPECT(IntUtil::alignTo<size_t>(0xFFF4, 4) == 0x0FFF4, "");
EXPECT(IntUtil::alignTo<size_t>(0xFFF8, 8) == 0x0FFF8, "");
EXPECT(IntUtil::alignTo<size_t>(0xFFF0, 16) == 0x0FFF0, "");
EXPECT(IntUtil::alignTo<size_t>(0xFFF0, 32) == 0x10000, "");
INFO("IntUtil::alignToPowerOf2().");
EXPECT(IntUtil::alignToPowerOf2<size_t>(0xFFFF) == 0x10000, "");
EXPECT(IntUtil::alignToPowerOf2<size_t>(0xF123) == 0x10000, "");
EXPECT(IntUtil::alignToPowerOf2<size_t>(0x0F00) == 0x01000, "");
EXPECT(IntUtil::alignToPowerOf2<size_t>(0x0100) == 0x00100, "");
EXPECT(IntUtil::alignToPowerOf2<size_t>(0x1001) == 0x02000, "");
INFO("IntUtil::deltaTo().");
EXPECT(IntUtil::deltaTo<size_t>(0xFFFF, 4) == 1, "");
EXPECT(IntUtil::deltaTo<size_t>(0xFFF4, 4) == 0, "");
EXPECT(IntUtil::deltaTo<size_t>(0xFFF8, 8) == 0, "");
EXPECT(IntUtil::deltaTo<size_t>(0xFFF0, 16) == 0, "");
EXPECT(IntUtil::deltaTo<size_t>(0xFFF0, 32) == 16, "");
}
#endif // ASMJIT_TEST
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Guard]
#ifndef _ASMJIT_BASE_INTUTIL_H
#define _ASMJIT_BASE_INTUTIL_H
// [Dependencies - AsmJit]
#include "../base/globals.h"
#if defined(_MSC_VER)
#pragma intrinsic(_BitScanForward)
#endif // ASMJIT_OS_WINDOWS
// [Api-Begin]
#include "../apibegin.h"
namespace asmjit {
//! \addtogroup asmjit_base_util
//! \{
// ============================================================================
// [asmjit::IntTraits]
// ============================================================================
//! \internal
template<typename T>
struct IntTraits {
enum {
kIsSigned = static_cast<T>(~static_cast<T>(0)) < static_cast<T>(0),
kIsUnsigned = !kIsSigned,
kIs8Bit = sizeof(T) == 1,
kIs16Bit = sizeof(T) == 2,
kIs32Bit = sizeof(T) == 4,
kIs64Bit = sizeof(T) == 8,
kIsIntPtr = sizeof(T) == sizeof(intptr_t)
};
};
// ============================================================================
// [asmjit::IntUtil]
// ============================================================================
//! Integer utilities.
struct IntUtil {
// --------------------------------------------------------------------------
// [Float <-> Int]
// --------------------------------------------------------------------------
//! \internal
union Float {
int32_t i;
float f;
};
//! \internal
union Double {
int64_t i;
double d;
};
//! Bit-cast `float` to 32-bit integer.
static ASMJIT_INLINE int32_t floatAsInt(float f) { Float m; m.f = f; return m.i; }
//! Bit-cast 32-bit integer to `float`.
static ASMJIT_INLINE float intAsFloat(int32_t i) { Float m; m.i = i; return m.f; }
//! Bit-cast `double` to 64-bit integer.
static ASMJIT_INLINE int64_t doubleAsInt(double d) { Double m; m.d = d; return m.i; }
//! Bit-cast 64-bit integer to `double`.
static ASMJIT_INLINE double intAsDouble(int64_t i) { Double m; m.i = i; return m.d; }
// --------------------------------------------------------------------------
// [AsmJit - Pack / Unpack]
// --------------------------------------------------------------------------
//! Pack two 8-bit integer and one 16-bit integer into a 32-bit integer as it
//! is an array of `{u0,u1,w2}`.
static ASMJIT_INLINE uint32_t pack32_2x8_1x16(uint32_t u0, uint32_t u1, uint32_t w2) {
#if defined(ASMJIT_HOST_LE)
return u0 + (u1 << 8) + (w2 << 16);
#else
return (u0 << 24) + (u1 << 16) + (w2);
#endif // ASMJIT_HOST
}
//! Pack four 8-bit integer into a 32-bit integer as it is an array of `{u0,u1,u2,u3}`.
static ASMJIT_INLINE uint32_t pack32_4x8(uint32_t u0, uint32_t u1, uint32_t u2, uint32_t u3) {
#if defined(ASMJIT_HOST_LE)
return u0 + (u1 << 8) + (u2 << 16) + (u3 << 24);
#else
return (u0 << 24) + (u1 << 16) + (u2 << 8) + u3;
#endif // ASMJIT_HOST
}
//! Pack two 32-bit integer into a 64-bit integer as it is an array of `{u0,u1}`.
static ASMJIT_INLINE uint64_t pack64_2x32(uint32_t u0, uint32_t u1) {
#if defined(ASMJIT_HOST_LE)
return (static_cast<uint64_t>(u1) << 32) + u0;
#else
return (static_cast<uint64_t>(u0) << 32) + u1;
#endif // ASMJIT_HOST
}
// --------------------------------------------------------------------------
// [AsmJit - Min/Max]
// --------------------------------------------------------------------------
// NOTE: Because some environments declare min() and max() as macros, it has
// been decided to use different name so we never collide with them.
//! Get minimum value of `a` and `b`.
template<typename T>
static ASMJIT_INLINE T iMin(const T& a, const T& b) { return a < b ? a : b; }
//! Get maximum value of `a` and `b`.
template<typename T>
static ASMJIT_INLINE T iMax(const T& a, const T& b) { return a > b ? a : b; }
// --------------------------------------------------------------------------
// [AsmJit - MaxUInt]
// --------------------------------------------------------------------------
//! Get maximum unsigned value of `T`.
template<typename T>
static ASMJIT_INLINE T maxUInt() { return ~T(0); }
// --------------------------------------------------------------------------
// [AsmJit - InInterval]
// --------------------------------------------------------------------------
//! Get whether `x` is greater or equal than `start` and less or equal than `end`.
template<typename T>
static ASMJIT_INLINE bool inInterval(const T& x, const T& start, const T& end) {
return x >= start && x <= end;
}
// --------------------------------------------------------------------------
// [AsmJit - IsInt/IsUInt]
// --------------------------------------------------------------------------
//! Get whether the given integer `x` can be casted to 8-bit signed integer.
template<typename T>
static ASMJIT_INLINE bool isInt8(T x) {
if (IntTraits<T>::kIsSigned)
return sizeof(T) <= sizeof(int8_t) ? true : x >= T(-128) && x <= T(127);
else
return x <= T(127);
}
//! Get whether the given integer `x` can be casted to 8-bit unsigned integer.
template<typename T>
static ASMJIT_INLINE bool isUInt8(T x) {
if (IntTraits<T>::kIsSigned)
return x >= T(0) && (sizeof(T) <= sizeof(uint8_t) ? true : x <= T(255));
else
return sizeof(T) <= sizeof(uint8_t) ? true : x <= T(255);
}
//! Get whether the given integer `x` can be casted to 16-bit signed integer.
template<typename T>
static ASMJIT_INLINE bool isInt16(T x) {
if (IntTraits<T>::kIsSigned)
return sizeof(T) <= sizeof(int16_t) ? true : x >= T(-32768) && x <= T(32767);
else
return x >= T(0) && (sizeof(T) <= sizeof(int16_t) ? true : x <= T(32767));
}
//! Get whether the given integer `x` can be casted to 16-bit unsigned integer.
template<typename T>
static ASMJIT_INLINE bool isUInt16(T x) {
if (IntTraits<T>::kIsSigned)
return x >= T(0) && (sizeof(T) <= sizeof(uint16_t) ? true : x <= T(65535));
else
return sizeof(T) <= sizeof(uint16_t) ? true : x <= T(65535);
}
//! Get whether the given integer `x` can be casted to 32-bit signed integer.
template<typename T>
static ASMJIT_INLINE bool isInt32(T x) {
if (IntTraits<T>::kIsSigned)
return sizeof(T) <= sizeof(int32_t) ? true : x >= T(-2147483647) - 1 && x <= T(2147483647);
else
return x >= T(0) && (sizeof(T) <= sizeof(int32_t) ? true : x <= T(2147483647));
}
//! Get whether the given integer `x` can be casted to 32-bit unsigned integer.
template<typename T>
static ASMJIT_INLINE bool isUInt32(T x) {
if (IntTraits<T>::kIsSigned)
return x >= T(0) && (sizeof(T) <= sizeof(uint32_t) ? true : x <= T(4294967295U));
else
return sizeof(T) <= sizeof(uint32_t) ? true : x <= T(4294967295U);
}
// --------------------------------------------------------------------------
// [AsmJit - IsPowerOf2]
// --------------------------------------------------------------------------
//! Get whether the `n` value is a power of two (only one bit is set).
template<typename T>
static ASMJIT_INLINE bool isPowerOf2(T n) {
return n != 0 && (n & (n - 1)) == 0;
}
// --------------------------------------------------------------------------
// [AsmJit - Mask]
// --------------------------------------------------------------------------
//! Generate a bit-mask that has `x` bit set.
static ASMJIT_INLINE uint32_t mask(uint32_t x) {
ASMJIT_ASSERT(x < 32);
return (1U << x);
}
//! Generate a bit-mask that has `x0` and `x1` bits set.
static ASMJIT_INLINE uint32_t mask(uint32_t x0, uint32_t x1) {
return mask(x0) | mask(x1);
}
//! Generate a bit-mask that has `x0`, `x1` and `x2` bits set.
static ASMJIT_INLINE uint32_t mask(uint32_t x0, uint32_t x1, uint32_t x2) {
return mask(x0) | mask(x1) | mask(x2);
}
//! Generate a bit-mask that has `x0`, `x1`, `x2` and `x3` bits set.
static ASMJIT_INLINE uint32_t mask(uint32_t x0, uint32_t x1, uint32_t x2, uint32_t x3) {
return mask(x0) | mask(x1) | mask(x2) | mask(x3);
}
//! Generate a bit-mask that has `x0`, `x1`, `x2`, `x3` and `x4` bits set.
static ASMJIT_INLINE uint32_t mask(uint32_t x0, uint32_t x1, uint32_t x2, uint32_t x3, uint32_t x4) {
return mask(x0) | mask(x1) | mask(x2) | mask(x3) |
mask(x4) ;
}
//! Generate a bit-mask that has `x0`, `x1`, `x2`, `x3`, `x4` and `x5` bits set.
static ASMJIT_INLINE uint32_t mask(uint32_t x0, uint32_t x1, uint32_t x2, uint32_t x3, uint32_t x4, uint32_t x5) {
return mask(x0) | mask(x1) | mask(x2) | mask(x3) |
mask(x4) | mask(x5) ;
}
//! Generate a bit-mask that has `x0`, `x1`, `x2`, `x3`, `x4`, `x5` and `x6` bits set.
static ASMJIT_INLINE uint32_t mask(uint32_t x0, uint32_t x1, uint32_t x2, uint32_t x3, uint32_t x4, uint32_t x5, uint32_t x6) {
return mask(x0) | mask(x1) | mask(x2) | mask(x3) |
mask(x4) | mask(x5) | mask(x6) ;
}
//! Generate a bit-mask that has `x0`, `x1`, `x2`, `x3`, `x4`, `x5`, `x6` and `x7` bits set.
static ASMJIT_INLINE uint32_t mask(uint32_t x0, uint32_t x1, uint32_t x2, uint32_t x3, uint32_t x4, uint32_t x5, uint32_t x6, uint32_t x7) {
return mask(x0) | mask(x1) | mask(x2) | mask(x3) |
mask(x4) | mask(x5) | mask(x6) | mask(x7) ;
}
//! Generate a bit-mask that has `x0`, `x1`, `x2`, `x3`, `x4`, `x5`, `x6`, `x7` and `x8` bits set.
static ASMJIT_INLINE uint32_t mask(uint32_t x0, uint32_t x1, uint32_t x2, uint32_t x3, uint32_t x4, uint32_t x5, uint32_t x6, uint32_t x7, uint32_t x8) {
return mask(x0) | mask(x1) | mask(x2) | mask(x3) |
mask(x4) | mask(x5) | mask(x6) | mask(x7) |
mask(x8) ;
}
//! Generate a bit-mask that has `x0`, `x1`, `x2`, `x3`, `x4`, `x5`, `x6`, `x7`, `x8` and `x9` bits set.
static ASMJIT_INLINE uint32_t mask(uint32_t x0, uint32_t x1, uint32_t x2, uint32_t x3, uint32_t x4, uint32_t x5, uint32_t x6, uint32_t x7, uint32_t x8, uint32_t x9) {
return mask(x0) | mask(x1) | mask(x2) | mask(x3) |
mask(x4) | mask(x5) | mask(x6) | mask(x7) |
mask(x8) | mask(x9) ;
}
// --------------------------------------------------------------------------
// [AsmJit - Bits]
// --------------------------------------------------------------------------
//! Generate a bit-mask that has `x` most significant bits set.
static ASMJIT_INLINE uint32_t bits(uint32_t x) {
// Shifting more bits that the type has has undefined behavior. Everything
// we need is that application shouldn't crash because of that, but the
// content of register after shift is not defined. So in case that the
// requested shift is too large for the type we correct this undefined
// behavior by setting all bits to ones (this is why we generate an overflow
// mask).
uint32_t overflow = static_cast<uint32_t>(
-static_cast<int32_t>(x >= sizeof(uint32_t) * 8));
return ((static_cast<uint32_t>(1) << x) - 1U) | overflow;
}
// --------------------------------------------------------------------------
// [AsmJit - HasBit]
// --------------------------------------------------------------------------
//! Get whether `x` has bit `n` set.
static ASMJIT_INLINE bool hasBit(uint32_t x, uint32_t n) {
return static_cast<bool>((x >> n) & 0x1);
}
// --------------------------------------------------------------------------
// [AsmJit - BitCount]
// --------------------------------------------------------------------------
//! Get count of bits in `x`.
//!
//! Taken from http://graphics.stanford.edu/~seander/bithacks.html .
static ASMJIT_INLINE uint32_t bitCount(uint32_t x) {
x = x - ((x >> 1) & 0x55555555U);
x = (x & 0x33333333U) + ((x >> 2) & 0x33333333U);
return (((x + (x >> 4)) & 0x0F0F0F0FU) * 0x01010101U) >> 24;
}
// --------------------------------------------------------------------------
// [AsmJit - FindFirstBit]
// --------------------------------------------------------------------------
//! \internal
static ASMJIT_INLINE uint32_t findFirstBitSlow(uint32_t mask) {
// This is a reference (slow) implementation of findFirstBit(), used when
// we don't have compiler support for this task. The implementation speed
// has been improved to check for 2 bits per iteration.
uint32_t i = 1;
while (mask != 0) {
uint32_t two = mask & 0x3;
if (two != 0x0)
return i - (two & 0x1);
i += 2;
mask >>= 2;
}
return 0xFFFFFFFFU;
}
//! Find a first bit in `mask`.
static ASMJIT_INLINE uint32_t findFirstBit(uint32_t mask) {
#if defined(_MSC_VER)
DWORD i;
if (_BitScanForward(&i, mask)) {
ASMJIT_ASSERT(findFirstBitSlow(mask) == i);
return static_cast<uint32_t>(i);
}
return 0xFFFFFFFFU;
#else
return findFirstBitSlow(mask);
#endif
}
// --------------------------------------------------------------------------
// [AsmJit - Misc]
// --------------------------------------------------------------------------
static ASMJIT_INLINE uint32_t keepNOnesFromRight(uint32_t mask, uint32_t nBits) {
uint32_t m = 0x1;
do {
nBits -= (mask & m) == 0;
m <<= 1;
if (nBits == 0) {
m -= 1;
mask &= m;
break;
}
} while (m);
return mask;
}
static ASMJIT_INLINE uint32_t indexNOnesFromRight(uint8_t* dst, uint32_t mask, uint32_t nBits) {
uint32_t totalBits = nBits;
uint8_t i = 0;
uint32_t m = 0x1;
do {
if (mask & m) {
*dst++ = i;
if (--nBits == 0)
break;
}
m <<= 1;
i++;
} while (m);
return totalBits - nBits;
}
// --------------------------------------------------------------------------
// [AsmJit - Alignment]
// --------------------------------------------------------------------------
template<typename T>
static ASMJIT_INLINE bool isAligned(T base, T alignment) {
return (base % alignment) == 0;
}
//! Align `base` to `alignment`.
template<typename T>
static ASMJIT_INLINE T alignTo(T base, T alignment) {
return (base + (alignment - 1)) & ~(alignment - 1);
}
template<typename T>
static ASMJIT_INLINE T alignToPowerOf2(T base) {
// Implementation is from "Hacker's Delight" by Henry S. Warren, Jr.
base -= 1;
#if defined(_MSC_VER)
# pragma warning(push)
# pragma warning(disable: 4293)
#endif // _MSC_VER
base = base | (base >> 1);
base = base | (base >> 2);
base = base | (base >> 4);
// 8/16/32 constants are multiplied by the condition to prevent a compiler
// complaining about the 'shift count >= type width' (GCC).
if (sizeof(T) >= 2) base = base | (base >> ( 8 * (sizeof(T) >= 2))); // Base >> 8.
if (sizeof(T) >= 4) base = base | (base >> (16 * (sizeof(T) >= 4))); // Base >> 16.
if (sizeof(T) >= 8) base = base | (base >> (32 * (sizeof(T) >= 8))); // Base >> 32.
#if defined(_MSC_VER)
# pragma warning(pop)
#endif // _MSC_VER
return base + 1;
}
//! Get delta required to align `base` to `alignment`.
template<typename T>
static ASMJIT_INLINE T deltaTo(T base, T alignment) {
return alignTo(base, alignment) - base;
}
};
// ============================================================================
// [asmjit::UInt64]
// ============================================================================
union UInt64 {
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
ASMJIT_INLINE UInt64 fromUInt64(uint64_t val) {
UInt64 data;
data.setUInt64(val);
return data;
}
ASMJIT_INLINE UInt64 fromUInt64(const UInt64& val) {
UInt64 data;
data.setUInt64(val);
return data;
}
// --------------------------------------------------------------------------
// [Reset]
// --------------------------------------------------------------------------
ASMJIT_INLINE void reset() {
if (kArchHost64Bit) {
u64 = 0;
}
else {
u32[0] = 0;
u32[1] = 0;
}
}
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
ASMJIT_INLINE uint64_t getUInt64() const {
return u64;
}
ASMJIT_INLINE UInt64& setUInt64(uint64_t val) {
u64 = val;
return *this;
}
ASMJIT_INLINE UInt64& setUInt64(const UInt64& val) {
if (kArchHost64Bit) {
u64 = val.u64;
}
else {
u32[0] = val.u32[0];
u32[1] = val.u32[1];
}
return *this;
}
ASMJIT_INLINE UInt64& setPacked_2x32(uint32_t u0, uint32_t u1) {
if (kArchHost64Bit) {
u64 = IntUtil::pack64_2x32(u0, u1);
}
else {
u32[0] = u0;
u32[1] = u1;
}
return *this;
}
// --------------------------------------------------------------------------
// [Add]
// --------------------------------------------------------------------------
ASMJIT_INLINE UInt64& add(uint64_t val) {
u64 += val;
return *this;
}
ASMJIT_INLINE UInt64& add(const UInt64& val) {
if (kArchHost64Bit) {
u64 += val.u64;
}
else {
u32[0] += val.u32[0];
u32[1] += val.u32[1];
}
return *this;
}
// --------------------------------------------------------------------------
// [Sub]
// --------------------------------------------------------------------------
ASMJIT_INLINE UInt64& sub(uint64_t val) {
u64 -= val;
return *this;
}
ASMJIT_INLINE UInt64& sub(const UInt64& val) {
if (kArchHost64Bit) {
u64 -= val.u64;
}
else {
u32[0] -= val.u32[0];
u32[1] -= val.u32[1];
}
return *this;
}
// --------------------------------------------------------------------------
// [And]
// --------------------------------------------------------------------------
ASMJIT_INLINE UInt64& and_(uint64_t val) {
u64 &= val;
return *this;
}
ASMJIT_INLINE UInt64& and_(const UInt64& val) {
if (kArchHost64Bit) {
u64 &= val.u64;
}
else {
u32[0] &= val.u32[0];
u32[1] &= val.u32[1];
}
return *this;
}
// --------------------------------------------------------------------------
// [Or]
// --------------------------------------------------------------------------
ASMJIT_INLINE UInt64& or_(uint64_t val) {
u64 |= val;
return *this;
}
ASMJIT_INLINE UInt64& or_(const UInt64& val) {
if (kArchHost64Bit) {
u64 |= val.u64;
}
else {
u32[0] |= val.u32[0];
u32[1] |= val.u32[1];
}
return *this;
}
// --------------------------------------------------------------------------
// [Xor]
// --------------------------------------------------------------------------
ASMJIT_INLINE UInt64& xor_(uint64_t val) {
u64 ^= val;
return *this;
}
ASMJIT_INLINE UInt64& xor_(const UInt64& val) {
if (kArchHost64Bit) {
u64 ^= val.u64;
}
else {
u32[0] ^= val.u32[0];
u32[1] ^= val.u32[1];
}
return *this;
}
// --------------------------------------------------------------------------
// [Del]
// --------------------------------------------------------------------------
ASMJIT_INLINE UInt64& del(uint64_t val) {
u64 &= ~val;
return *this;
}
ASMJIT_INLINE UInt64& del(const UInt64& val) {
if (kArchHost64Bit) {
u64 &= ~val.u64;
}
else {
u32[0] &= ~val.u32[0];
u32[1] &= ~val.u32[1];
}
return *this;
}
// --------------------------------------------------------------------------
// [Eq]
// --------------------------------------------------------------------------
ASMJIT_INLINE bool isZero() const {
return kArchHost64Bit ? u64 == 0 : (u32[0] | u32[1]) == 0;
}
ASMJIT_INLINE bool isNonZero() const {
return kArchHost64Bit ? u64 != 0 : (u32[0] | u32[1]) != 0;
}
ASMJIT_INLINE bool eq(uint64_t val) const {
return u64 == val;
}
ASMJIT_INLINE bool eq(const UInt64& val) const {
return kArchHost64Bit ? u64 == val.u64 : (u32[0] == val.u32[0]) & (u32[1] == val.u32[1]);
}
// --------------------------------------------------------------------------
// [Operator Overload]
// --------------------------------------------------------------------------
ASMJIT_INLINE UInt64& operator+=(uint64_t val) { return add(val); }
ASMJIT_INLINE UInt64& operator+=(const UInt64& val) { return add(val); }
ASMJIT_INLINE UInt64& operator-=(uint64_t val) { return sub(val); }
ASMJIT_INLINE UInt64& operator-=(const UInt64& val) { return sub(val); }
ASMJIT_INLINE UInt64& operator&=(uint64_t val) { return and_(val); }
ASMJIT_INLINE UInt64& operator&=(const UInt64& val) { return and_(val); }
ASMJIT_INLINE UInt64& operator|=(uint64_t val) { return or_(val); }
ASMJIT_INLINE UInt64& operator|=(const UInt64& val) { return or_(val); }
ASMJIT_INLINE UInt64& operator^=(uint64_t val) { return xor_(val); }
ASMJIT_INLINE UInt64& operator^=(const UInt64& val) { return xor_(val); }
ASMJIT_INLINE bool operator==(uint64_t val) const { return eq(val); }
ASMJIT_INLINE bool operator==(const UInt64& val) const { return eq(val); }
ASMJIT_INLINE bool operator!=(uint64_t val) const { return !eq(val); }
ASMJIT_INLINE bool operator!=(const UInt64& val) const { return !eq(val); }
ASMJIT_INLINE bool operator<(uint64_t val) const { return u64 < val; }
ASMJIT_INLINE bool operator<(const UInt64& val) const { return u64 < val.u64; }
ASMJIT_INLINE bool operator<=(uint64_t val) const { return u64 <= val; }
ASMJIT_INLINE bool operator<=(const UInt64& val) const { return u64 <= val.u64; }
ASMJIT_INLINE bool operator>(uint64_t val) const { return u64 > val; }
ASMJIT_INLINE bool operator>(const UInt64& val) const { return u64 > val.u64; }
ASMJIT_INLINE bool operator>=(uint64_t val) const { return u64 >= val; }
ASMJIT_INLINE bool operator>=(const UInt64& val) const { return u64 >= val.u64; }
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
uint64_t u64;
uint32_t u32[2];
uint16_t u16[4];
uint8_t u8[8];
struct {
#if defined(ASMJIT_HOST_LE)
uint32_t lo, hi;
#else
uint32_t hi, lo;
#endif // ASMJIT_HOST_LE
};
};
//! \}
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
// [Guard]
#endif // _ASMJIT_BASE_INTUTIL_H
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Guard]
#ifndef _ASMJIT_BASE_LOCK_H
#define _ASMJIT_BASE_LOCK_H
// [Dependencies - AsmJit]
#include "../build.h"
// [Dependencies - Posix]
#if defined(ASMJIT_OS_POSIX)
# include <pthread.h>
#endif // ASMJIT_OS_POSIX
// [Api-Begin]
#include "../apibegin.h"
namespace asmjit {
//! \addtogroup asmjit_base_util
//! \{
// ============================================================================
// [asmjit::Lock]
// ============================================================================
//! Lock - used in thread-safe code for locking.
struct Lock {
ASMJIT_NO_COPY(Lock)
// --------------------------------------------------------------------------
// [Windows]
// --------------------------------------------------------------------------
#if defined(ASMJIT_OS_WINDOWS)
typedef CRITICAL_SECTION Handle;
//! Create a new `Lock` instance.
ASMJIT_INLINE Lock() { InitializeCriticalSection(&_handle); }
//! Destroy the `Lock` instance.
ASMJIT_INLINE ~Lock() { DeleteCriticalSection(&_handle); }
//! Lock.
ASMJIT_INLINE void lock() { EnterCriticalSection(&_handle); }
//! Unlock.
ASMJIT_INLINE void unlock() { LeaveCriticalSection(&_handle); }
#endif // ASMJIT_OS_WINDOWS
// --------------------------------------------------------------------------
// [Posix]
// --------------------------------------------------------------------------
#if defined(ASMJIT_OS_POSIX)
typedef pthread_mutex_t Handle;
//! Create a new `Lock` instance.
ASMJIT_INLINE Lock() { pthread_mutex_init(&_handle, NULL); }
//! Destroy the `Lock` instance.
ASMJIT_INLINE ~Lock() { pthread_mutex_destroy(&_handle); }
//! Lock.
ASMJIT_INLINE void lock() { pthread_mutex_lock(&_handle); }
//! Unlock.
ASMJIT_INLINE void unlock() { pthread_mutex_unlock(&_handle); }
#endif // ASMJIT_OS_POSIX
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
//! Get handle.
ASMJIT_INLINE Handle& getHandle() {
return _handle;
}
//! \overload
ASMJIT_INLINE const Handle& getHandle() const {
return _handle;
}
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
//! Handle.
Handle _handle;
};
// ============================================================================
// [asmjit::AutoLock]
// ============================================================================
//! Scoped lock.
struct AutoLock {
ASMJIT_NO_COPY(AutoLock)
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! Autolock `target`, scoped.
ASMJIT_INLINE AutoLock(Lock& target) : _target(target) {
_target.lock();
}
//! Autounlock `target`.
ASMJIT_INLINE ~AutoLock() {
_target.unlock();
}
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
//! Pointer to target (lock).
Lock& _target;
};
//! \}
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
// [Guard]
#endif // _ASMJIT_BASE_LOCK_H
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Export]
#define ASMJIT_EXPORTS
// [Guard]
#include "../build.h"
#if !defined(ASMJIT_DISABLE_LOGGER)
// [Dependencies - AsmJit]
#include "../base/intutil.h"
#include "../base/logger.h"
#include "../base/string.h"
// [Dependencies - C]
#include <stdarg.h>
// [Api-Begin]
#include "../apibegin.h"
namespace asmjit {
// ============================================================================
// [asmjit::Logger - Construction / Destruction]
// ============================================================================
Logger::Logger() {
_options = 0;
::memset(_indentation, 0, ASMJIT_ARRAY_SIZE(_indentation));
}
Logger::~Logger() {}
// ============================================================================
// [asmjit::Logger - Logging]
// ============================================================================
void Logger::logFormat(uint32_t style, const char* fmt, ...) {
char buf[1024];
size_t len;
va_list ap;
va_start(ap, fmt);
len = vsnprintf(buf, 1023, fmt, ap);
va_end(ap);
logString(style, buf, len);
}
void Logger::logBinary(uint32_t style, const void* data, size_t size) {
static const char prefix[] = ".data ";
static const char hex[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
const uint8_t* s = static_cast<const uint8_t*>(data);
size_t i = size;
char buffer[128];
::memcpy(buffer, prefix, ASMJIT_ARRAY_SIZE(prefix) - 1);
while (i) {
uint32_t n = static_cast<uint32_t>(IntUtil::iMin<size_t>(i, 16));
char* p = buffer + ASMJIT_ARRAY_SIZE(prefix) - 1;
i -= n;
do {
uint32_t c = s[0];
p[0] = hex[c >> 4];
p[1] = hex[c & 15];
p += 2;
s += 1;
} while (--n);
*p++ = '\n';
logString(style, buffer, (size_t)(p - buffer));
}
}
// ============================================================================
// [asmjit::Logger - LogBinary]
// ============================================================================
void Logger::setOption(uint32_t id, bool value) {
if (id >= kLoggerOptionCount)
return;
uint32_t mask = 1 << id;
if (value)
_options |= mask;
else
_options &= ~mask;
}
// ============================================================================
// [asmjit::Logger - Indentation]
// ============================================================================
void Logger::setIndentation(const char* indentation) {
::memset(_indentation, 0, ASMJIT_ARRAY_SIZE(_indentation));
if (!indentation)
return;
size_t length = StringUtil::nlen(indentation, ASMJIT_ARRAY_SIZE(_indentation) - 1);
::memcpy(_indentation, indentation, length);
}
// ============================================================================
// [asmjit::FileLogger - Construction / Destruction]
// ============================================================================
FileLogger::FileLogger(FILE* stream) : _stream(NULL) {
setStream(stream);
}
FileLogger::~FileLogger() {}
// ============================================================================
// [asmjit::FileLogger - Accessors]
// ============================================================================
//! Set file stream.
void FileLogger::setStream(FILE* stream) {
_stream = stream;
}
// ============================================================================
// [asmjit::FileLogger - Logging]
// ============================================================================
void FileLogger::logString(uint32_t style, const char* buf, size_t len) {
if (!_stream)
return;
if (len == kInvalidIndex)
len = strlen(buf);
fwrite(buf, 1, len, _stream);
}
// ============================================================================
// [asmjit::StringLogger - Construction / Destruction]
// ============================================================================
StringLogger::StringLogger() {}
StringLogger::~StringLogger() {}
// ============================================================================
// [asmjit::StringLogger - Logging]
// ============================================================================
void StringLogger::logString(uint32_t style, const char* buf, size_t len) {
_stringBuilder.appendString(buf, len);
}
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
// [Guard]
#endif // !ASMJIT_DISABLE_LOGGER
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Guard]
#ifndef _ASMJIT_BASE_LOGGER_H
#define _ASMJIT_BASE_LOGGER_H
#include "../build.h"
#if !defined(ASMJIT_DISABLE_LOGGER)
// [Dependencies - AsmJit]
#include "../base/string.h"
// [Dependencies - C]
#include <stdarg.h>
// [Api-Begin]
#include "../apibegin.h"
namespace asmjit {
//! \addtogroup asmjit_base_util
//! \{
// ============================================================================
// [asmjit::kLoggerOption]
// ============================================================================
//! Logger options.
ASMJIT_ENUM(kLoggerOption) {
//! Whether to output instructions also in binary form.
kLoggerOptionBinaryForm = 0,
//! Whether to output immediates as hexadecimal numbers.
kLoggerOptionHexImmediate = 1,
//! Whether to output displacements as hexadecimal numbers.
kLoggerOptionHexDisplacement = 2,
//! Count of logger options.
kLoggerOptionCount = 3
};
// ============================================================================
// [asmjit::kLoggerStyle]
// ============================================================================
//! Logger style.
ASMJIT_ENUM(kLoggerStyle) {
kLoggerStyleDefault = 0,
kLoggerStyleDirective = 1,
kLoggerStyleLabel = 2,
kLoggerStyleData = 3,
kLoggerStyleComment = 4,
kLoggerStyleCount = 5
};
// ============================================================================
// [asmjit::Logger]
// ============================================================================
//! Abstract logging class.
//!
//! This class can be inherited and reimplemented to fit into your logging
//! subsystem. When reimplementing use `Logger::log()` method to log into
//! a custom stream.
//!
//! This class also contain `_enabled` member that can be used to enable
//! or disable logging.
struct ASMJIT_VCLASS Logger {
ASMJIT_NO_COPY(Logger)
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! Create a `Logger` instance.
ASMJIT_API Logger();
//! Destroy the `Logger` instance.
ASMJIT_API virtual ~Logger();
// --------------------------------------------------------------------------
// [Logging]
// --------------------------------------------------------------------------
//! Log output.
virtual void logString(uint32_t style, const char* buf, size_t len = kInvalidIndex) = 0;
//! Log formatter message (like sprintf) sending output to `logString()` method.
ASMJIT_API void logFormat(uint32_t style, const char* fmt, ...);
//! Log binary data.
ASMJIT_API void logBinary(uint32_t style, const void* data, size_t size);
// --------------------------------------------------------------------------
// [Options]
// --------------------------------------------------------------------------
//! Get all logger options as a single integer.
ASMJIT_INLINE uint32_t getOptions() const {
return _options;
}
//! Get the given logger option.
ASMJIT_INLINE bool getOption(uint32_t id) const {
ASMJIT_ASSERT(id < kLoggerOptionCount);
return static_cast<bool>((_options >> id) & 0x1);
}
//! Set the given logger option.
ASMJIT_API void setOption(uint32_t id, bool value);
// --------------------------------------------------------------------------
// [Indentation]
// --------------------------------------------------------------------------
//! Get indentation.
ASMJIT_INLINE const char* getIndentation() const {
return _indentation;
}
//! Set indentation.
ASMJIT_API void setIndentation(const char* indentation);
//! Reset indentation.
ASMJIT_INLINE void resetIndentation() {
setIndentation(NULL);
}
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
//! Options, see `kLoggerOption`.
uint32_t _options;
//! Indentation.
char _indentation[12];
};
// ============================================================================
// [asmjit::FileLogger]
// ============================================================================
//! Logger that can log to standard C `FILE*` stream.
struct ASMJIT_VCLASS FileLogger : public Logger {
ASMJIT_NO_COPY(FileLogger)
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! Create a new `FileLogger` that logs to a `FILE` stream.
ASMJIT_API FileLogger(FILE* stream = NULL);
//! Destroy the `FileLogger`.
ASMJIT_API virtual ~FileLogger();
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
//! Get `FILE*` stream.
//!
//! \note Return value can be `NULL`.
ASMJIT_INLINE FILE* getStream() const { return _stream; }
//! Set `FILE*` stream, can be set to `NULL` to disable logging, although
//! the `CodeGen` will still call `logString` even if there is no stream.
ASMJIT_API void setStream(FILE* stream);
// --------------------------------------------------------------------------
// [Logging]
// --------------------------------------------------------------------------
ASMJIT_API virtual void logString(uint32_t style, const char* buf, size_t len = kInvalidIndex);
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
//! C file stream.
FILE* _stream;
};
// ============================================================================
// [asmjit::StringLogger]
// ============================================================================
//! String logger.
struct ASMJIT_VCLASS StringLogger : public Logger {
ASMJIT_NO_COPY(StringLogger)
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! Create new `StringLogger`.
ASMJIT_API StringLogger();
//! Destroy the `StringLogger`.
ASMJIT_API virtual ~StringLogger();
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
//! Get <code>char*</code> pointer which represents the resulting
//! string.
//!
//! The pointer is owned by `StringLogger`, it can't be modified or freed.
ASMJIT_INLINE const char* getString() const {
return _stringBuilder.getData();
}
//! Clear the resulting string.
ASMJIT_INLINE void clearString() {
_stringBuilder.clear();
}
// --------------------------------------------------------------------------
// [Logging]
// --------------------------------------------------------------------------
ASMJIT_API virtual void logString(uint32_t style, const char* buf, size_t len = kInvalidIndex);
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
//! Output.
StringBuilder _stringBuilder;
};
//! \}
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
// [Guard]
#endif // !ASMJIT_DISABLE_LOGGER
#endif // _ASMJIT_BASE_LOGGER_H
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Export]
#define ASMJIT_EXPORTS
// [Dependencies - AsmJit]
#include "../base/globals.h"
// [Api-Begin]
#include "../apibegin.h"
namespace asmjit {
// ============================================================================
// [asmjit::Operand]
// ============================================================================
// Prevent static initialization.
struct Operand {
uint8_t op;
uint8_t size;
uint8_t reserved_2_1;
uint8_t reserved_3_1;
uint32_t id;
uint64_t reserved_8_8;
};
ASMJIT_VAR const Operand noOperand;
const Operand noOperand = { 0, 0, 0, 0, kInvalidValue, 0 };
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Guard]
#ifndef _ASMJIT_BASE_OPERAND_H
#define _ASMJIT_BASE_OPERAND_H
// [Dependencies - AsmJit]
#include "../base/intutil.h"
// [Api-Begin]
#include "../apibegin.h"
namespace asmjit {
// ============================================================================
// [Forward Declarations]
// ============================================================================
struct Assembler;
struct Compiler;
//! \addtogroup asmjit_base_general
//! \{
// ============================================================================
// [asmjit::kOperandType]
// ============================================================================
//! Operand types that can be encoded in `Operand`.
ASMJIT_ENUM(kOperandType) {
//! Invalid operand, used only internally (not initialized Operand).
kOperandTypeNone = 0,
//! Operand is a register.
kOperandTypeReg = 1,
//! Operand is a variable.
kOperandTypeVar = 2,
//! Operand is a memory.
kOperandTypeMem = 3,
//! Operand is an immediate value.
kOperandTypeImm = 4,
//! Operand is a label.
kOperandTypeLabel = 5
};
// ============================================================================
// [asmjit::kOperandId]
// ============================================================================
//! Operand id masks used to determine the operand type.
ASMJIT_ENUM(kOperandId) {
//! Operand id refers to `Var`.
kOperandIdVar = 0x80000000U,
//! Operand id to real index mask.
kOperandIdNum = 0x7FFFFFFFU
};
// ============================================================================
// [asmjit::kRegClass]
// ============================================================================
//! Register class.
ASMJIT_ENUM(kRegClass) {
//! Gp register class, compatible with all architectures.
kRegClassGp = 0
};
// ============================================================================
// [asmjit::kSize]
// ============================================================================
//! Common size of registers and pointers.
ASMJIT_ENUM(kSize) {
//! 1 byte size (BYTE).
kSizeByte = 1,
//! 2 bytes size (WORD).
kSizeWord = 2,
//! 4 bytes size (DWORD).
kSizeDWord = 4,
//! 8 bytes size (QWORD).
kSizeQWord = 8,
//! 10 bytes size (TWORD).
kSizeTWord = 10,
//! 16 bytes size (OWORD / DQWORD).
kSizeOWord = 16,
//! 32 bytes size (YWORD / QQWORD).
kSizeYWord = 32
};
// ============================================================================
// [asmjit::kMemType]
// ============================================================================
//! Type of memory operand.
ASMJIT_ENUM(kMemType) {
//! Memory operand is a combination of base register and optional index register
//! and displacement.
//!
//! The `Assembler` interprets `kMemTypeBaseIndex` and `kMemTypeStackIndex`
//! types the same way, but `Compiler` interprets `kMemTypeBaseIndex` as
//! `[base + index]` and `kMemTypeStackIndex` as `[stack(base) + index]`.
kMemTypeBaseIndex = 0,
//! Memory operand is a combination of variable's memory location,
//! optional index register and displacement.
//!
//! The `Assembler` interprets `kMemTypeBaseIndex` and `kMemTypeStackIndex`
//! types in the same way, but `Compiler` interprets `kMemTypeBaseIndex` as
//! `[base + index]` and `kMemTypeStackIndex` as `[stack(base) + index]`.
kMemTypeStackIndex = 1,
//! Memory operand refers to the memory location specified by a label.
kMemTypeLabel = 2,
//! Memory operand is an absolute memory location.
//!
//! Supported mostly by x86, truncated to a 32-bit value when running in
//! 64-bit mode (x64).
kMemTypeAbsolute = 3
};
// ============================================================================
// [asmjit::Operand]
// ============================================================================
//! Operand can contain register, memory location, immediate, or label.
struct Operand {
// --------------------------------------------------------------------------
// [Structs]
// --------------------------------------------------------------------------
//! \internal
//!
//! Base operand data.
struct BaseOp {
//! Type of operand, see `kOperandType`.
uint8_t op;
//! Size of operand (register, address, immediate, or variable).
uint8_t size;
//! \internal
uint8_t reserved_2_1;
//! \internal
uint8_t reserved_3_1;
//! Operand id, identifier used by `Assembler` and `Compiler`.
//!
//! \note Uninitialized operand has always set id to `kInvalidValue`.
uint32_t id;
//! \internal
uint32_t reserved_8_4;
//! \internal
uint32_t reserved_12_4;
};
//! \internal
//!
//! Register or Variable operand data.
struct VRegOp {
//! Type of operand, `kOperandTypeReg`.
uint8_t op;
//! Size of register or variable.
uint8_t size;
union {
//! Register code = (type << 8) | index.
uint16_t code;
//! Register type and index access.
struct {
#if defined(ASMJIT_HOST_LE)
//! Register index.
uint8_t index;
//! Register type.
uint8_t type;
#else
//! Register type.
uint8_t type;
//! Register index.
uint8_t index;
#endif // ASMJIT_HOST
};
};
//! Variable id, used by `Compiler` to identify variables.
uint32_t id;
union {
struct {
//! Variable type.
uint32_t vType;
//! \internal
uint32_t reserved_12_4;
};
//! \internal
//!
//! This is not needed or used, it's just to force compiler to always
//! align this struct to 8-bytes (so the struct is compatible to others
//! when it comes to alignment). It should fix VS linker warning as well.
uint64_t reserved8_8;
};
};
//! \internal
//!
//! Memory or Variable operand data.
struct VMemOp {
//! Type of operand, `kOperandTypeMem`.
uint8_t op;
//! Size of the pointer in bytes.
uint8_t size;
//! Type of the memory operand, see `kMemType`.
uint8_t type;
//! X86/X64 layout:
//! - segment [3 bits], see `kX86Seg`.
//! - shift [2 bits], index register shift (0 to 3).
uint8_t flags;
//! Base register, variable or label id.
uint32_t base;
//! Index register or variable.
uint32_t index;
//! 32-bit displacement or absolute address.
int32_t displacement;
};
//! \internal
//!
//! Immediate operand data.
struct ImmOp {
//! Type of operand, `kOperandTypeImm`.
uint8_t op;
//! Size of immediate (or 0 to autodetect).
uint8_t size;
//! \internal
uint8_t reserved_2_1;
//! \internal
uint8_t reserved_3_1;
//! Operand id, always set to `kInvalidValue` (immediates don't have IDs).
uint32_t id;
union {
//! 8x8-bit signed immediate values.
int8_t _i8[8];
//! 8x8-bit unsigned immediate values.
uint8_t _u8[8];
//! 4x16-bit signed immediate values.
int16_t _i16[4];
//! 4x16-bit unsigned immediate values.
uint16_t _u16[4];
//! 2x32-bit signed immediate values.
int32_t _i32[2];
//! 2x32-bit unsigned immediate values.
uint32_t _u32[2];
//! 1x64-bit signed immediate value.
int64_t _i64[1];
//! 1x64-bit unsigned immediate value.
uint64_t _u64[1];
//! 2x SP-FP values.
float _f32[2];
//! 1x DP-FP value.
double _f64[1];
} value;
};
//! \internal
//!
//! Label operand data.
struct LabelOp {
//! Type of operand, `kOperandTypeLabel`.
uint8_t op;
//! Always zero, labels don't have size.
uint8_t size;
//! \internal
uint8_t reserved_2_1;
//! \internal
uint8_t reserved_3_1;
//! Operand id (`kInvalidValue` if the label is not initialized by code
//! generator).
uint32_t id;
//! \internal
uint32_t reserved_8_4;
//! \internal
uint32_t reserved_12_4;
};
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! Create an uninitialized operand.
ASMJIT_INLINE Operand() {
_init_packed_op_sz_b0_b1_id(kOperandTypeNone, 0, 0, 0, kInvalidValue);
_init_packed_d2_d3(0, 0);
}
//! Create a reference to `other` operand.
ASMJIT_INLINE Operand(const Operand& other) {
_init(other);
}
explicit ASMJIT_INLINE Operand(const _NoInit&) {}
// --------------------------------------------------------------------------
// [Operand]
// --------------------------------------------------------------------------
//! Clone `Operand`.
ASMJIT_INLINE Operand clone() const {
return Operand(*this);
}
// --------------------------------------------------------------------------
// [Init & Copy]
// --------------------------------------------------------------------------
//! \internal
//!
//! Initialize operand to `other` (used by constructors).
ASMJIT_INLINE void _init(const Operand& other) {
::memcpy(this, &other, sizeof(Operand));
}
ASMJIT_INLINE void _init_packed_op_sz_b0_b1_id(uint32_t op, uint32_t sz, uint32_t r0, uint32_t r1, uint32_t id) {
// This hack is not for performance, but to decrease the size of the binary
// generated when constructing AsmJit operands (mostly for third parties).
// Some compilers are not able to join four BYTE writes to a single DWORD
// write. Because the 'a', 'b', 'c' and 'd' variables are usually compile
// time constants the compiler can do a really nice job if they are joined
// by using bitwise operations.
_packed[0].setPacked_2x32(IntUtil::pack32_4x8(op, sz, r0, r1), id);
}
ASMJIT_INLINE void _init_packed_op_sz_w0_id(uint32_t op, uint32_t sz, uint32_t w0, uint32_t id) {
_packed[0].setPacked_2x32(IntUtil::pack32_2x8_1x16(op, sz, w0), id);
}
ASMJIT_INLINE void _init_packed_d0_d1(uint32_t u0, uint32_t u1) {
_packed[0].setPacked_2x32(u0, u1);
}
ASMJIT_INLINE void _init_packed_d2_d3(uint32_t u2, uint32_t u3) {
_packed[1].setPacked_2x32(u2, u3);
}
//! \internal
//!
//! Initialize operand to `other` (used by assign operators).
ASMJIT_INLINE void _copy(const Operand& other) {
::memcpy(this, &other, sizeof(Operand));
}
// --------------------------------------------------------------------------
// [Data]
// --------------------------------------------------------------------------
template<typename T>
ASMJIT_INLINE T& getData() {
return reinterpret_cast<T&>(_base);
}
template<typename T>
ASMJIT_INLINE const T& getData() const {
return reinterpret_cast<const T&>(_base);
}
// --------------------------------------------------------------------------
// [Type]
// --------------------------------------------------------------------------
//! Get type of the operand, see `kOperandType`.
ASMJIT_INLINE uint32_t getOp() const { return _base.op; }
//! Get whether the operand is none - `kOperandTypeNone`.
ASMJIT_INLINE bool isNone() const { return (_base.op == kOperandTypeNone); }
//! Get whether the operand is a register - `kOperandTypeReg`.
ASMJIT_INLINE bool isReg() const { return (_base.op == kOperandTypeReg); }
//! Get whether the operand is a variable - `kOperandTypeVar`.
ASMJIT_INLINE bool isVar() const { return (_base.op == kOperandTypeVar); }
//! Get whether the operand is a memory address - `kOperandTypeMem`.
ASMJIT_INLINE bool isMem() const { return (_base.op == kOperandTypeMem); }
//! Get whether the operand is an immediate value - `kOperandTypeImm`.
ASMJIT_INLINE bool isImm() const { return (_base.op == kOperandTypeImm); }
//! Get whether the operand is a label - `kOperandTypeLabel`.
ASMJIT_INLINE bool isLabel() const { return (_base.op == kOperandTypeLabel); }
// --------------------------------------------------------------------------
// [Type - Combined]
// --------------------------------------------------------------------------
//! Get register type.
ASMJIT_INLINE uint32_t getRegType() const {
return _vreg.type;
}
//! Get register index.
ASMJIT_INLINE uint32_t getRegIndex() const {
return _vreg.index;
}
//! Get whether the operand is register of `type`.
ASMJIT_INLINE bool isRegType(uint32_t type) const {
return (_packed[0].u32[0] & IntUtil::pack32_2x8_1x16(0xFF, 0, 0xFF00)) == IntUtil::pack32_2x8_1x16(kOperandTypeReg, 0, (type << 8));
}
//! Get whether the operand is register and of `type` and `index`.
ASMJIT_INLINE bool isRegCode(uint32_t type, uint32_t index) const {
return (_packed[0].u32[0] & IntUtil::pack32_2x8_1x16(0xFF, 0, 0xFFFF)) == IntUtil::pack32_2x8_1x16(kOperandTypeReg, 0, (type << 8) + index);
}
//! Get whether the operand is a register or memory.
ASMJIT_INLINE bool isRegOrMem() const {
ASMJIT_ASSERT(kOperandTypeReg == 1);
ASMJIT_ASSERT(kOperandTypeMem == 3);
return (static_cast<uint32_t>(_base.op) | 0x2U) == 0x3U;
}
//! Get whether the operand is variable or memory.
ASMJIT_INLINE bool isVarOrMem() const {
ASMJIT_ASSERT(kOperandTypeVar == 2);
ASMJIT_ASSERT(kOperandTypeMem == 3);
return (static_cast<uint32_t>(_base.op) - 2U) <= 1;
}
// --------------------------------------------------------------------------
// [Size]
// --------------------------------------------------------------------------
//! Get size of the operand in bytes.
ASMJIT_INLINE uint32_t getSize() const {
return _base.size;
}
// --------------------------------------------------------------------------
// [Id]
// --------------------------------------------------------------------------
//! Get operand id.
//!
//! Operand id's are used internally by `Assembler` and `Compiler`.
//!
//! There is no way to change or remove operand id. Unneeded operands can be
//! simply reassigned by `operator=`.
ASMJIT_INLINE uint32_t getId() const {
return _base.id;
}
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
union {
//! Base data.
BaseOp _base;
//! Register or variable data.
VRegOp _vreg;
//! Memory data.
VMemOp _vmem;
//! Immediate data.
ImmOp _imm;
//! Label data.
LabelOp _label;
//! Packed operand as two 64-bit integers.
UInt64 _packed[2];
};
};
// ============================================================================
// [asmjit::OperandUtil]
// ============================================================================
//! Operand utilities.
struct OperandUtil {
//! Make variable id.
static ASMJIT_INLINE uint32_t makeVarId(uint32_t id) {
return id | kOperandIdVar;
}
//! Make label id.
static ASMJIT_INLINE uint32_t makeLabelId(uint32_t id) {
return id;
}
//! Strip variable id bit so it becomes a pure index to `VarData[]` array.
static ASMJIT_INLINE uint32_t stripVarId(uint32_t id) {
return id & 0x7FFFFFFFU;
}
//! Get whether the id refers to `Var`.
//!
//! \note The function will never return `true` if the id is `kInvalidValue`.
//! The trick is to compare a given id to -1 (kInvalidValue) so we check both
//! using only one comparison.
static ASMJIT_INLINE bool isVarId(uint32_t id) {
return static_cast<int32_t>(id) < -1;
}
//! Get whether the id refers to `Label`.
//!
//! \note The function will never return `true` if the id is `kInvalidValue`.
static ASMJIT_INLINE bool isLabelId(uint32_t id) {
return static_cast<int32_t>(id) >= 0;
}
};
// ============================================================================
// [asmjit::Reg]
// ============================================================================
//! Base class for all register operands.
struct Reg : public Operand {
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! Create a dummy base register.
ASMJIT_INLINE Reg() : Operand(NoInit) {
_init_packed_op_sz_w0_id(kOperandTypeReg, 0, (kInvalidReg << 8) + kInvalidReg, kInvalidValue);
_init_packed_d2_d3(kInvalidVar, 0);
}
//! Create a new base register.
ASMJIT_INLINE Reg(uint32_t type, uint32_t index, uint32_t size) : Operand(NoInit) {
_init_packed_op_sz_w0_id(kOperandTypeReg, size, (type << 8) + index, kInvalidValue);
_init_packed_d2_d3(kInvalidVar, 0);
}
//! Create a new reference to `other`.
ASMJIT_INLINE Reg(const Reg& other) : Operand(other) {}
//! Create a new reference to `other` and change the index to `index`.
ASMJIT_INLINE Reg(const Reg& other, uint32_t index) : Operand(other) {
_vreg.index = static_cast<uint8_t>(index);
}
explicit ASMJIT_INLINE Reg(const _NoInit&) : Operand(NoInit) {}
// --------------------------------------------------------------------------
// [Reg Specific]
// --------------------------------------------------------------------------
//! Clone `Reg` operand.
ASMJIT_INLINE Reg clone() const {
return Reg(*this);
}
//! Get whether register code is equal to `type`.
ASMJIT_INLINE bool isRegType(uint32_t type) const {
return _vreg.type == type;
}
//! Get whether register code is equal to `type`.
ASMJIT_INLINE bool isRegCode(uint32_t code) const {
return _vreg.code == code;
}
//! Get whether register code is equal to `type`.
ASMJIT_INLINE bool isRegCode(uint32_t type, uint32_t index) const {
return _vreg.code == (type << 8) + index;
}
//! Get register code that equals to '(type << 8) + index'.
ASMJIT_INLINE uint32_t getRegCode() const {
return _vreg.code;
}
//! Get register type.
ASMJIT_INLINE uint32_t getRegType() const {
return _vreg.type;
}
//! Get register index.
ASMJIT_INLINE uint32_t getRegIndex() const {
return _vreg.index;
}
#define ASMJIT_REG_OP(_Type_) \
ASMJIT_INLINE _Type_ clone() const { \
return _Type_(*this); \
} \
\
/*! Set register `size`. */ \
ASMJIT_INLINE _Type_& setSize(uint32_t size) { \
_vreg.size = static_cast<uint8_t>(size); \
return *this; \
} \
\
/*! Set register `code`. */ \
ASMJIT_INLINE _Type_& setCode(uint32_t code) { \
_vreg.code = static_cast<uint16_t>(code); \
return *this; \
} \
\
/*! Set register `type` and `index`. */ \
ASMJIT_INLINE _Type_& setCode(uint32_t type, uint32_t index) { \
_vreg.type = static_cast<uint8_t>(type); \
_vreg.index = static_cast<uint8_t>(index); \
return *this; \
} \
\
/*! Set register `type`. */ \
ASMJIT_INLINE _Type_& setType(uint32_t type) { \
_vreg.type = static_cast<uint8_t>(type); \
return *this; \
} \
\
/*! Set register `index`. */ \
ASMJIT_INLINE _Type_& setIndex(uint32_t index) { \
_vreg.index = static_cast<uint8_t>(index); \
return *this; \
} \
\
ASMJIT_INLINE _Type_& operator=(const _Type_& other) { _copy(other); return *this; } \
\
ASMJIT_INLINE bool operator==(const _Type_& other) const { return _packed[0].u32[0] == other._packed[0].u32[0]; } \
ASMJIT_INLINE bool operator!=(const _Type_& other) const { return !operator==(other); }
};
// ============================================================================
// [asmjit::BaseMem]
// ============================================================================
//! Base class for all memory operands.
struct BaseMem : public Operand {
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
ASMJIT_INLINE BaseMem() : Operand(NoInit) {
reset();
}
ASMJIT_INLINE BaseMem(const BaseMem& other) : Operand(other) {}
explicit ASMJIT_INLINE BaseMem(const _NoInit&) : Operand(NoInit) {}
// --------------------------------------------------------------------------
// [BaseMem Specific]
// --------------------------------------------------------------------------
//! Clone `BaseMem` operand.
ASMJIT_INLINE BaseMem clone() const {
return BaseMem(*this);
}
//! Reset `BaseMem` operand.
ASMJIT_INLINE void reset() {
_init_packed_op_sz_b0_b1_id(kOperandTypeMem, 0, kMemTypeBaseIndex, 0, kInvalidValue);
_init_packed_d2_d3(kInvalidValue, 0);
}
//! Get the type of the memory operand, see `kMemType`.
ASMJIT_INLINE uint32_t getMemType() const {
return _vmem.type;
}
//! Get whether the type of the memory operand is either `kMemTypeBaseIndex`
//! or `kMemTypeStackIndex`.
ASMJIT_INLINE bool isBaseIndexType() const {
return _vmem.type <= kMemTypeStackIndex;
}
//! Get whether the memory operand has base register.
ASMJIT_INLINE bool hasBase() const {
return _vmem.base != kInvalidValue;
}
//! Get memory operand base id, or `kInvalidValue`.
ASMJIT_INLINE uint32_t getBase() const {
return _vmem.base;
}
//! Set memory operand size.
ASMJIT_INLINE BaseMem& setSize(uint32_t size) {
_vmem.size = static_cast<uint8_t>(size);
return *this;
}
//! Get memory operand relative displacement.
ASMJIT_INLINE int32_t getDisplacement() const {
return _vmem.displacement;
}
//! Set memory operand relative displacement.
ASMJIT_INLINE BaseMem& setDisplacement(int32_t disp) {
_vmem.displacement = disp;
return *this;
}
// --------------------------------------------------------------------------
// [Operator Overload]
// --------------------------------------------------------------------------
ASMJIT_INLINE BaseMem& operator=(const BaseMem& other) {
_copy(other);
return *this;
}
ASMJIT_INLINE bool operator==(const BaseMem& other) const {
return (_packed[0] == other._packed[0]) & (_packed[1] == other._packed[1]);
}
ASMJIT_INLINE bool operator!=(const BaseMem& other) const {
return !(*this == other);
}
};
// ============================================================================
// [asmjit::Imm]
// ============================================================================
//! Immediate operand.
//!
//! Immediate operand is usually part of instruction itself. It's inlined after
//! or before the instruction opcode. Immediates can be only signed or unsigned
//! integers.
//!
//! To create immediate operand use `imm()` or `imm_u()` non-members or `Imm`
//! constructors.
struct Imm : public Operand {
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! Create a new immediate value (initial value is 0).
Imm() : Operand(NoInit) {
_init_packed_op_sz_b0_b1_id(kOperandTypeImm, 0, 0, 0, kInvalidValue);
_imm.value._i64[0] = 0;
}
//! Create a new signed immediate value, assigning the value to `val`.
explicit Imm(int64_t val) : Operand(NoInit) {
_init_packed_op_sz_b0_b1_id(kOperandTypeImm, 0, 0, 0, kInvalidValue);
_imm.value._i64[0] = val;
}
//! Create a new immediate value from `other`.
ASMJIT_INLINE Imm(const Imm& other) : Operand(other) {}
explicit ASMJIT_INLINE Imm(const _NoInit&) : Operand(NoInit) {}
// --------------------------------------------------------------------------
// [Immediate Specific]
// --------------------------------------------------------------------------
//! Clone `Imm` operand.
ASMJIT_INLINE Imm clone() const {
return Imm(*this);
}
//! Get whether the immediate can be casted to 8-bit signed integer.
ASMJIT_INLINE bool isInt8() const { return IntUtil::isInt8(_imm.value._i64[0]); }
//! Get whether the immediate can be casted to 8-bit unsigned integer.
ASMJIT_INLINE bool isUInt8() const { return IntUtil::isUInt8(_imm.value._i64[0]); }
//! Get whether the immediate can be casted to 16-bit signed integer.
ASMJIT_INLINE bool isInt16() const { return IntUtil::isInt16(_imm.value._i64[0]); }
//! Get whether the immediate can be casted to 16-bit unsigned integer.
ASMJIT_INLINE bool isUInt16() const { return IntUtil::isUInt16(_imm.value._i64[0]); }
//! Get whether the immediate can be casted to 32-bit signed integer.
ASMJIT_INLINE bool isInt32() const { return IntUtil::isInt32(_imm.value._i64[0]); }
//! Get whether the immediate can be casted to 32-bit unsigned integer.
ASMJIT_INLINE bool isUInt32() const { return IntUtil::isUInt32(_imm.value._i64[0]); }
//! Get immediate value as 8-bit signed integer.
ASMJIT_INLINE int8_t getInt8() const { return _imm.value._i8[_ASMJIT_HOST_INDEX(8, 0)]; }
//! Get immediate value as 8-bit unsigned integer.
ASMJIT_INLINE uint8_t getUInt8() const { return _imm.value._u8[_ASMJIT_HOST_INDEX(8, 0)]; }
//! Get immediate value as 16-bit signed integer.
ASMJIT_INLINE int16_t getInt16() const { return _imm.value._i16[_ASMJIT_HOST_INDEX(4, 0)]; }
//! Get immediate value as 16-bit unsigned integer.
ASMJIT_INLINE uint16_t getUInt16() const { return _imm.value._u16[_ASMJIT_HOST_INDEX(4, 0)]; }
//! Get immediate value as 32-bit signed integer.
ASMJIT_INLINE int32_t getInt32() const { return _imm.value._i32[_ASMJIT_HOST_INDEX(2, 0)]; }
//! Get immediate value as 32-bit unsigned integer.
ASMJIT_INLINE uint32_t getUInt32() const { return _imm.value._u32[_ASMJIT_HOST_INDEX(2, 0)]; }
//! Get immediate value as 64-bit signed integer.
ASMJIT_INLINE int64_t getInt64() const { return _imm.value._i64[0]; }
//! Get immediate value as 64-bit unsigned integer.
ASMJIT_INLINE uint64_t getUInt64() const { return _imm.value._u64[0]; }
//! Get immediate value as `intptr_t`.
ASMJIT_INLINE intptr_t getIntPtr() const {
if (sizeof(intptr_t) == sizeof(int64_t))
return static_cast<intptr_t>(getInt64());
else
return static_cast<intptr_t>(getInt32());
}
//! Get immediate value as `uintptr_t`.
ASMJIT_INLINE uintptr_t getUIntPtr() const {
if (sizeof(uintptr_t) == sizeof(uint64_t))
return static_cast<uintptr_t>(getUInt64());
else
return static_cast<uintptr_t>(getUInt32());
}
//! Get low 32-bit signed integer.
ASMJIT_INLINE int32_t getInt32Lo() const { return _imm.value._i32[_ASMJIT_HOST_INDEX(2, 0)]; }
//! Get low 32-bit signed integer.
ASMJIT_INLINE uint32_t getUInt32Lo() const { return _imm.value._u32[_ASMJIT_HOST_INDEX(2, 0)]; }
//! Get high 32-bit signed integer.
ASMJIT_INLINE int32_t getInt32Hi() const { return _imm.value._i32[_ASMJIT_HOST_INDEX(2, 1)]; }
//! Get high 32-bit signed integer.
ASMJIT_INLINE uint32_t getUInt32Hi() const { return _imm.value._u32[_ASMJIT_HOST_INDEX(2, 1)]; }
//! Set immediate value to 8-bit signed integer `val`.
ASMJIT_INLINE Imm& setInt8(int8_t val) {
if (kArchHost64Bit) {
_imm.value._i64[0] = static_cast<int64_t>(val);
}
else {
int32_t val32 = static_cast<int32_t>(val);
_imm.value._i32[_ASMJIT_HOST_INDEX(2, 0)] = val32;
_imm.value._i32[_ASMJIT_HOST_INDEX(2, 1)] = val32 >> 31;
}
return *this;
}
//! Set immediate value to 8-bit unsigned integer `val`.
ASMJIT_INLINE Imm& setUInt8(uint8_t val) {
if (kArchHost64Bit) {
_imm.value._u64[0] = static_cast<uint64_t>(val);
}
else {
_imm.value._u32[_ASMJIT_HOST_INDEX(2, 0)] = static_cast<uint32_t>(val);
_imm.value._u32[_ASMJIT_HOST_INDEX(2, 1)] = 0;
}
return *this;
}
//! Set immediate value to 16-bit signed integer `val`.
ASMJIT_INLINE Imm& setInt16(int16_t val) {
if (kArchHost64Bit) {
_imm.value._i64[0] = static_cast<int64_t>(val);
}
else {
int32_t val32 = static_cast<int32_t>(val);
_imm.value._i32[_ASMJIT_HOST_INDEX(2, 0)] = val32;
_imm.value._i32[_ASMJIT_HOST_INDEX(2, 1)] = val32 >> 31;
}
return *this;
}
//! Set immediate value to 16-bit unsigned integer `val`.
ASMJIT_INLINE Imm& setUInt16(uint16_t val) {
if (kArchHost64Bit) {
_imm.value._u64[0] = static_cast<uint64_t>(val);
}
else {
_imm.value._u32[_ASMJIT_HOST_INDEX(2, 0)] = static_cast<uint32_t>(val);
_imm.value._u32[_ASMJIT_HOST_INDEX(2, 1)] = 0;
}
return *this;
}
//! Set immediate value to 32-bit signed integer `val`.
ASMJIT_INLINE Imm& setInt32(int32_t val) {
if (kArchHost64Bit) {
_imm.value._i64[0] = static_cast<int64_t>(val);
}
else {
_imm.value._i32[_ASMJIT_HOST_INDEX(2, 0)] = val;
_imm.value._i32[_ASMJIT_HOST_INDEX(2, 1)] = val >> 31;
}
return *this;
}
//! Set immediate value to 32-bit unsigned integer `val`.
ASMJIT_INLINE Imm& setUInt32(uint32_t val) {
if (kArchHost64Bit) {
_imm.value._u64[0] = static_cast<uint64_t>(val);
}
else {
_imm.value._u32[_ASMJIT_HOST_INDEX(2, 0)] = val;
_imm.value._u32[_ASMJIT_HOST_INDEX(2, 1)] = 0;
}
return *this;
}
//! Set immediate value to 64-bit signed integer `val`.
ASMJIT_INLINE Imm& setInt64(int64_t val) {
_imm.value._i64[0] = val;
return *this;
}
//! Set immediate value to 64-bit unsigned integer `val`.
ASMJIT_INLINE Imm& setUInt64(uint64_t val) {
_imm.value._u64[0] = val;
return *this;
}
//! Set immediate value to intptr_t `val`.
ASMJIT_INLINE Imm& setIntPtr(intptr_t val) {
_imm.value._i64[0] = static_cast<int64_t>(val);
return *this;
}
//! Set immediate value to uintptr_t `val`.
ASMJIT_INLINE Imm& setUIntPtr(uintptr_t val) {
_imm.value._u64[0] = static_cast<uint64_t>(val);
return *this;
}
//! Set immediate value as unsigned type to `val`.
ASMJIT_INLINE Imm& setPtr(void* p) { return setIntPtr((intptr_t)p); }
// --------------------------------------------------------------------------
// [Float]
// --------------------------------------------------------------------------
ASMJIT_INLINE Imm& setFloat(float f) {
_imm.value._f32[_ASMJIT_HOST_INDEX(2, 0)] = f;
_imm.value._u32[_ASMJIT_HOST_INDEX(2, 1)] = 0;
return *this;
}
ASMJIT_INLINE Imm& setDouble(double d) {
_imm.value._f64[0] = d;
return *this;
}
// --------------------------------------------------------------------------
// [Truncate]
// --------------------------------------------------------------------------
ASMJIT_INLINE Imm& truncateTo8Bits() {
if (kArchHost64Bit) {
_imm.value._u64[0] &= static_cast<uint64_t>(0x000000FFU);
}
else {
_imm.value._u32[_ASMJIT_HOST_INDEX(2, 0)] &= 0x000000FFU;
_imm.value._u32[_ASMJIT_HOST_INDEX(2, 1)] = 0;
}
return *this;
}
ASMJIT_INLINE Imm& truncateTo16Bits() {
if (kArchHost64Bit) {
_imm.value._u64[0] &= static_cast<uint64_t>(0x0000FFFFU);
}
else {
_imm.value._u32[_ASMJIT_HOST_INDEX(2, 0)] &= 0x0000FFFFU;
_imm.value._u32[_ASMJIT_HOST_INDEX(2, 1)] = 0;
}
return *this;
}
ASMJIT_INLINE Imm& truncateTo32Bits() {
_imm.value._u32[_ASMJIT_HOST_INDEX(2, 1)] = 0;
return *this;
}
// --------------------------------------------------------------------------
// [Operator Overload]
// --------------------------------------------------------------------------
//! Assign `other` to the immediate operand.
ASMJIT_INLINE Imm& operator=(const Imm& other) {
_copy(other);
return *this;
}
};
// ============================================================================
// [asmjit::Label]
// ============================================================================
//! Label (jump target or data location).
//!
//! Label represents a location in code typically used as jump targets, but may
//! be also reference data or static variables. Label has to be explicitly
//! created by a code-generator by calling `CodeGen::newLabel()` where `CodeGen`
//! is your code generator, which derives from `Assembler` or `Compiler`.
//!
//! Example of using labels:
//!
//! ~~~
//! // Create Assembler/Compiler.
//! host::Assembler a;
//!
//! // Create Label instance.
//! Label L_1(a);
//!
//! // ... your code ...
//!
//! // Using label.
//! a.jump(L_1);
//!
//! // ... your code ...
//!
//! // Bind label to the current position, see `CodeGen::bind()`.
//! a.bind(L_1);
//! ~~~
struct Label : public Operand {
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! Create new, unassociated label.
ASMJIT_INLINE Label() : Operand(NoInit) {
reset();
}
explicit ASMJIT_INLINE Label(uint32_t id) : Operand(NoInit) {
_init_packed_op_sz_b0_b1_id(kOperandTypeLabel, 0, 0, 0, id);
_init_packed_d2_d3(0, 0);
}
//! Create new initialized label.
explicit ASMJIT_INLINE Label(Assembler& a);
//! Create new initialized label.
explicit ASMJIT_INLINE Label(Compiler& c);
//! Create reference to another label.
ASMJIT_INLINE Label(const Label& other) : Operand(other) {}
explicit ASMJIT_INLINE Label(const _NoInit&) : Operand(NoInit) {}
// --------------------------------------------------------------------------
// [Reset]
// --------------------------------------------------------------------------
ASMJIT_INLINE void reset() {
_init_packed_op_sz_b0_b1_id(kOperandTypeLabel, 0, 0, 0, kInvalidValue);
_init_packed_d2_d3(0, 0);
}
// --------------------------------------------------------------------------
// [Label Specific]
// --------------------------------------------------------------------------
//! Get whether the label has been initialized by `Assembler` or `Compiler`.
ASMJIT_INLINE bool isInitialized() const {
return _label.id != kInvalidValue;
}
// --------------------------------------------------------------------------
// [Operator Overload]
// --------------------------------------------------------------------------
ASMJIT_INLINE Label& operator=(const Label& other) { _copy(other); return *this; }
ASMJIT_INLINE bool operator==(const Label& other) const { return _base.id == other._base.id; }
ASMJIT_INLINE bool operator!=(const Label& other) const { return _base.id != other._base.id; }
};
// ============================================================================
// [asmjit::Operand - Globals]
// ============================================================================
//! No operand, can be used to reset an operand by assignment or to refer to an
//! operand that doesn't exist.
ASMJIT_VAR const Operand noOperand;
//! Create signed immediate value operand.
static ASMJIT_INLINE Imm imm(int64_t val) {
return Imm(val);
}
//! Create unsigned immediate value operand.
static ASMJIT_INLINE Imm imm_u(uint64_t val) {
return Imm(static_cast<int64_t>(val));
}
//! Create void* pointer immediate value operand.
static ASMJIT_INLINE Imm imm_ptr(void* p) {
return Imm(static_cast<int64_t>((intptr_t)p));
}
//! \}
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
// [Guard]
#endif // _ASMJIT_BASE_OPERAND_H
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Export]
#define ASMJIT_EXPORTS
// [Dependencies - AsmJit]
#include "../base/assembler.h"
#include "../base/cpuinfo.h"
#include "../base/error.h"
#include "../base/runtime.h"
// [Api-Begin]
#include "../apibegin.h"
namespace asmjit {
// ============================================================================
// [asmjit::Runtime - Construction / Destruction]
// ============================================================================
Runtime::Runtime() {
_sizeLimit = 0;
_runtimeType = kRuntimeTypeNone;
_allocType = kVMemAllocFreeable;
::memset(_reserved, 0, sizeof(_reserved));
_baseAddress = kNoBaseAddress;
}
Runtime::~Runtime() {}
// ============================================================================
// [asmjit::HostRuntime - Construction / Destruction]
// ============================================================================
HostRuntime::HostRuntime() {
_runtimeType = kRuntimeTypeJit;
}
HostRuntime::~HostRuntime() {}
// ============================================================================
// [asmjit::HostRuntime - Interface]
// ============================================================================
const CpuInfo* HostRuntime::getCpuInfo() {
return CpuInfo::getHost();
}
uint32_t HostRuntime::getStackAlignment() {
uint32_t alignment = sizeof(intptr_t);
#if defined(ASMJIT_HOST_X86)
// Modern Linux, APPLE and UNIX guarantees 16-byte stack alignment, but I'm
// not sure about all other UNIX operating systems, because 16-byte alignment
// is addition to an older specification.
# if (defined(__linux__) || \
defined(__linux) || \
defined(__unix__) || \
defined(__FreeBSD__) || \
defined(__NetBSD__) || \
defined(__OpenBSD__) || \
defined(__DARWIN__) || \
defined(__APPLE__) )
alignment = 16;
# endif
#elif defined(ASMJIT_HOST_X64)
alignment = 16;
#endif
return alignment;
}
void HostRuntime::flush(void* p, size_t size) {
// Only useful on non-x86 architectures.
#if !defined(ASMJIT_HOST_X86) && !defined(ASMJIT_HOST_X64)
// Windows has built-in support in kernel32.dll.
#if defined(ASMJIT_OS_WINDOWS)
::FlushInstructionCache(_memMgr.getProcessHandle(), p, size);
#endif // ASMJIT_OS_WINDOWS
#endif // !ASMJIT_HOST_X86 && !ASMJIT_HOST_X64
}
// ============================================================================
// [asmjit::StaticRuntime - Construction / Destruction]
// ============================================================================
StaticRuntime::StaticRuntime(void* baseAddress, size_t sizeLimit) {
_sizeLimit = sizeLimit;
_baseAddress = static_cast<Ptr>((uintptr_t)baseAddress);
}
StaticRuntime::~StaticRuntime() {}
// ============================================================================
// [asmjit::StaticRuntime - Interface]
// ============================================================================
Error StaticRuntime::add(void** dst, Assembler* assembler) {
size_t codeSize = assembler->getCodeSize();
size_t sizeLimit = _sizeLimit;
if (codeSize == 0) {
*dst = NULL;
return kErrorNoCodeGenerated;
}
if (sizeLimit != 0 && sizeLimit < codeSize) {
*dst = NULL;
return kErrorCodeTooLarge;
}
Ptr baseAddress = _baseAddress;
uint8_t* p = static_cast<uint8_t*>((void*)static_cast<uintptr_t>(baseAddress));
// Since the base address is known the `relocSize` returned should be equal
// to `codeSize`. It's better to fail if they don't match instead of passsing
// silently.
size_t relocSize = assembler->relocCode(p, baseAddress);
if (relocSize == 0 || codeSize != relocSize) {
*dst = NULL;
return kErrorInvalidState;
}
_baseAddress += codeSize;
if (sizeLimit)
sizeLimit -= codeSize;
flush(p, codeSize);
*dst = p;
return kErrorOk;
}
Error StaticRuntime::release(void* p) {
// There is nothing to release as `StaticRuntime` doesn't manage any memory.
ASMJIT_UNUSED(p);
return kErrorOk;
}
// ============================================================================
// [asmjit::JitRuntime - Construction / Destruction]
// ============================================================================
JitRuntime::JitRuntime() {}
JitRuntime::~JitRuntime() {}
// ============================================================================
// [asmjit::JitRuntime - Interface]
// ============================================================================
Error JitRuntime::add(void** dst, Assembler* assembler) {
size_t codeSize = assembler->getCodeSize();
if (codeSize == 0) {
*dst = NULL;
return kErrorNoCodeGenerated;
}
void* p = _memMgr.alloc(codeSize, getAllocType());
if (p == NULL) {
*dst = NULL;
return kErrorNoVirtualMemory;
}
// Relocate the code and release the unused memory back to `VMemMgr`.
size_t relocSize = assembler->relocCode(p);
if (relocSize < codeSize) {
_memMgr.shrink(p, relocSize);
}
flush(p, relocSize);
*dst = p;
return kErrorOk;
}
Error JitRuntime::release(void* p) {
return _memMgr.release(p);
}
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Guard]
#ifndef _ASMJIT_BASE_RUNTIME_H
#define _ASMJIT_BASE_RUNTIME_H
// [Dependencies - AsmJit]
#include "../base/error.h"
#include "../base/vmem.h"
// [Api-Begin]
#include "../apibegin.h"
namespace asmjit {
// ============================================================================
// [Forward Declarations]
// ============================================================================
struct Assembler;
struct CpuInfo;
//! \addtogroup asmjit_base_general
//! \{
// ============================================================================
// [asmjit::kRuntimeType]
// ============================================================================
ASMJIT_ENUM(kRuntimeType) {
kRuntimeTypeNone = 0,
kRuntimeTypeJit = 1,
kRuntimeTypeRemote = 2
};
// ============================================================================
// [asmjit::Runtime]
// ============================================================================
//! Base runtime.
struct ASMJIT_VCLASS Runtime {
ASMJIT_NO_COPY(Runtime)
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! Create a `Runtime` instance.
ASMJIT_API Runtime();
//! Destroy the `Runtime` instance.
ASMJIT_API virtual ~Runtime();
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
//! Get runtime type.
ASMJIT_INLINE uint32_t getRuntimeType() const {
return _runtimeType;
}
//! Get whether the runtime has a base address.
//!
//! \sa \ref getBaseAddress()
ASMJIT_INLINE bool hasBaseAddress() const {
return _baseAddress == kNoBaseAddress;
}
//! Get the base address.
ASMJIT_INLINE Ptr getBaseAddress() const {
return _baseAddress;
}
// --------------------------------------------------------------------------
// [Interface]
// --------------------------------------------------------------------------
//! Get CPU information.
virtual const CpuInfo* getCpuInfo() = 0;
//! Get stack alignment of target runtime.
virtual uint32_t getStackAlignment() = 0;
//! Allocate a memory needed for a code generated by `assembler` and
//! relocate it to the target location.
//!
//! The beginning of the memory allocated for the function is returned in
//! `dst`. Returns Status code as \ref kError, on failure `dst` is set to
//! `NULL`.
virtual Error add(void** dst, Assembler* assembler) = 0;
//! Release memory allocated by `add`.
virtual Error release(void* p) = 0;
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
//! Maximum size of the code that can be added to the runtime (0=unlimited).
size_t _sizeLimit;
//! Base address (-1 means no base address).
Ptr _baseAddress;
//! Type of the runtime.
uint8_t _runtimeType;
//! Type of the allocation.
uint8_t _allocType;
//! \internal
uint8_t _reserved[sizeof(intptr_t) - 2];
};
// ============================================================================
// [asmjit::HostRuntime]
// ============================================================================
//! Base runtime for JIT code generation.
struct ASMJIT_VCLASS HostRuntime : public Runtime {
ASMJIT_NO_COPY(HostRuntime)
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! Create a `HostRuntime` instance.
ASMJIT_API HostRuntime();
//! Destroy the `HostRuntime` instance.
ASMJIT_API virtual ~HostRuntime();
// --------------------------------------------------------------------------
// [Interface]
// --------------------------------------------------------------------------
ASMJIT_API virtual const CpuInfo* getCpuInfo();
ASMJIT_API virtual uint32_t getStackAlignment();
//! Flush an instruction cache.
//!
//! This member function is called after the code has been copied to the
//! destination buffer. It is only useful for JIT code generation as it
//! causes a flush of the processor cache.
//!
//! Flushing is basically a NOP under X86/X64, but is needed by architectures
//! that do not have a transparent instruction cache.
//!
//! This function can also be overridden to improve compatibility with tools
//! such as Valgrind, however, it's not an official part of AsmJit.
ASMJIT_API virtual void flush(void* p, size_t size);
};
// ============================================================================
// [asmjit::StaticRuntime]
// ============================================================================
//! JIT static runtime.
//!
//! JIT static runtime can be used to generate code to a memory location that
//! is known.
struct ASMJIT_VCLASS StaticRuntime : public HostRuntime {
ASMJIT_NO_COPY(StaticRuntime)
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! Create a `StaticRuntime` instance.
//!
//! The `address` specifies a fixed target address, which will be used as a
//! base address for relocation, and `sizeLimit` specified the maximum size
//! of a code that can be copied to it. If there is no limit `sizeLimit`
//! should be zero.
ASMJIT_API StaticRuntime(void* baseAddress, size_t sizeLimit = 0);
//! Destroy the `StaticRuntime` instance.
ASMJIT_API virtual ~StaticRuntime();
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
//! Get the base address.
ASMJIT_INLINE Ptr getBaseAddress() const {
return _baseAddress;
}
//! Get the maximum size of the code that can be relocated to the target
//! address or zero if unlimited.
ASMJIT_INLINE size_t getSizeLimit() const {
return _sizeLimit;
}
// --------------------------------------------------------------------------
// [Interface]
// --------------------------------------------------------------------------
ASMJIT_API virtual Error add(void** dst, Assembler* assembler);
ASMJIT_API virtual Error release(void* p);
};
// ============================================================================
// [asmjit::JitRuntime]
// ============================================================================
//! JIT runtime.
struct ASMJIT_VCLASS JitRuntime : public HostRuntime {
ASMJIT_NO_COPY(JitRuntime)
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! Create a `JitRuntime` instance.
ASMJIT_API JitRuntime();
//! Destroy the `JitRuntime` instance.
ASMJIT_API virtual ~JitRuntime();
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
//! Get the type of allocation.
ASMJIT_INLINE uint32_t getAllocType() const {
return _allocType;
}
//! Set the type of allocation.
ASMJIT_INLINE void setAllocType(uint32_t allocType) {
_allocType = allocType;
}
//! Get the virtual memory manager.
ASMJIT_INLINE VMemMgr* getMemMgr() const {
return const_cast<VMemMgr*>(&_memMgr);
}
// --------------------------------------------------------------------------
// [Interface]
// --------------------------------------------------------------------------
ASMJIT_API virtual Error add(void** dst, Assembler* assembler);
ASMJIT_API virtual Error release(void* p);
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
//! Virtual memory manager.
VMemMgr _memMgr;
};
//! \}
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
// [Guard]
#endif // _ASMJIT_BASE_RUNTIME_H
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Export]
#define ASMJIT_EXPORTS
// [Dependencies - AsmJit]
#include "../base/intutil.h"
#include "../base/string.h"
// [Api-Begin]
#include "../apibegin.h"
namespace asmjit {
// Should be placed in read-only memory.
static const char StringBuilder_empty[4] = { 0 };
// ============================================================================
// [asmjit::StringBuilder - Construction / Destruction]
// ============================================================================
StringBuilder::StringBuilder() :
_data(const_cast<char*>(StringBuilder_empty)),
_length(0),
_capacity(0),
_canFree(false) {}
StringBuilder::~StringBuilder() {
if (_canFree)
ASMJIT_FREE(_data);
}
// ============================================================================
// [asmjit::StringBuilder - Prepare / Reserve]
// ============================================================================
char* StringBuilder::prepare(uint32_t op, size_t len) {
// --------------------------------------------------------------------------
// [Set]
// --------------------------------------------------------------------------
if (op == kStringOpSet) {
// We don't care here, but we can't return a NULL pointer since it indicates
// failure in memory allocation.
if (len == 0) {
if (_data != StringBuilder_empty)
_data[0] = 0;
_length = 0;
return _data;
}
if (_capacity < len) {
if (len >= IntUtil::maxUInt<size_t>() - sizeof(intptr_t) * 2)
return NULL;
size_t to = IntUtil::alignTo<size_t>(len, sizeof(intptr_t));
if (to < 256 - sizeof(intptr_t))
to = 256 - sizeof(intptr_t);
char* newData = static_cast<char*>(ASMJIT_ALLOC(to + sizeof(intptr_t)));
if (newData == NULL) {
clear();
return NULL;
}
if (_canFree)
ASMJIT_FREE(_data);
_data = newData;
_capacity = to + sizeof(intptr_t) - 1;
_canFree = true;
}
_data[len] = 0;
_length = len;
ASMJIT_ASSERT(_length <= _capacity);
return _data;
}
// --------------------------------------------------------------------------
// [Append]
// --------------------------------------------------------------------------
else {
// We don't care here, but we can't return a NULL pointer since it indicates
// failure in memory allocation.
if (len == 0)
return _data + _length;
// Overflow.
if (IntUtil::maxUInt<size_t>() - sizeof(intptr_t) * 2 - _length < len)
return NULL;
size_t after = _length + len;
if (_capacity < after) {
size_t to = _capacity;
if (to < 256)
to = 256;
while (to < 1024 * 1024 && to < after)
to *= 2;
if (to < after) {
to = after;
if (to < (IntUtil::maxUInt<size_t>() - 1024 * 32))
to = IntUtil::alignTo<size_t>(to, 1024 * 32);
}
to = IntUtil::alignTo<size_t>(to, sizeof(intptr_t));
char* newData = static_cast<char*>(ASMJIT_ALLOC(to + sizeof(intptr_t)));
if (newData == NULL)
return NULL;
::memcpy(newData, _data, _length);
if (_canFree)
ASMJIT_FREE(_data);
_data = newData;
_capacity = to + sizeof(intptr_t) - 1;
_canFree = true;
}
char* ret = _data + _length;
_data[after] = 0;
_length = after;
ASMJIT_ASSERT(_length <= _capacity);
return ret;
}
}
bool StringBuilder::reserve(size_t to) {
if (_capacity >= to)
return true;
if (to >= IntUtil::maxUInt<size_t>() - sizeof(intptr_t) * 2)
return false;
to = IntUtil::alignTo<size_t>(to, sizeof(intptr_t));
char* newData = static_cast<char*>(ASMJIT_ALLOC(to + sizeof(intptr_t)));
if (newData == NULL)
return false;
::memcpy(newData, _data, _length + 1);
if (_canFree)
ASMJIT_FREE(_data);
_data = newData;
_capacity = to + sizeof(intptr_t) - 1;
_canFree = true;
return true;
}
// ============================================================================
// [asmjit::StringBuilder - Clear]
// ============================================================================
void StringBuilder::clear() {
if (_data != StringBuilder_empty)
_data[0] = 0;
_length = 0;
}
// ============================================================================
// [asmjit::StringBuilder - Methods]
// ============================================================================
bool StringBuilder::_opString(uint32_t op, const char* str, size_t len) {
if (len == kInvalidIndex)
len = str != NULL ? ::strlen(str) : static_cast<size_t>(0);
char* p = prepare(op, len);
if (p == NULL)
return false;
::memcpy(p, str, len);
return true;
}
bool StringBuilder::_opChar(uint32_t op, char c) {
char* p = prepare(op, 1);
if (p == NULL)
return false;
*p = c;
return true;
}
bool StringBuilder::_opChars(uint32_t op, char c, size_t len) {
char* p = prepare(op, len);
if (p == NULL)
return false;
::memset(p, c, len);
return true;
}
static const char StringBuilder_numbers[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
bool StringBuilder::_opNumber(uint32_t op, uint64_t i, uint32_t base, size_t width, uint32_t flags) {
if (base < 2 || base > 36)
base = 10;
char buf[128];
char* p = buf + ASMJIT_ARRAY_SIZE(buf);
uint64_t orig = i;
char sign = '\0';
// --------------------------------------------------------------------------
// [Sign]
// --------------------------------------------------------------------------
if ((flags & kStringFormatSigned) != 0 && static_cast<int64_t>(i) < 0) {
i = static_cast<uint64_t>(-static_cast<int64_t>(i));
sign = '-';
}
else if ((flags & kStringFormatShowSign) != 0) {
sign = '+';
}
else if ((flags & kStringFormatShowSpace) != 0) {
sign = ' ';
}
// --------------------------------------------------------------------------
// [Number]
// --------------------------------------------------------------------------
do {
uint64_t d = i / base;
uint64_t r = i % base;
*--p = StringBuilder_numbers[r];
i = d;
} while (i);
size_t numberLength = (size_t)(buf + ASMJIT_ARRAY_SIZE(buf) - p);
// --------------------------------------------------------------------------
// [Alternate Form]
// --------------------------------------------------------------------------
if ((flags & kStringFormatAlternate) != 0) {
if (base == 8) {
if (orig != 0)
*--p = '0';
}
if (base == 16) {
*--p = 'x';
*--p = '0';
}
}
// --------------------------------------------------------------------------
// [Width]
// --------------------------------------------------------------------------
if (sign != 0)
*--p = sign;
if (width > 256)
width = 256;
if (width <= numberLength)
width = 0;
else
width -= numberLength;
// --------------------------------------------------------------------------
// Write]
// --------------------------------------------------------------------------
size_t prefixLength = (size_t)(buf + ASMJIT_ARRAY_SIZE(buf) - p) - numberLength;
char* data = prepare(op, prefixLength + width + numberLength);
if (data == NULL)
return false;
::memcpy(data, p, prefixLength);
data += prefixLength;
::memset(data, '0', width);
data += width;
::memcpy(data, p + prefixLength, numberLength);
return true;
}
bool StringBuilder::_opHex(uint32_t op, const void* data, size_t len) {
if (len >= IntUtil::maxUInt<size_t>() / 2)
return false;
char* dst = prepare(op, len * 2);
if (dst == NULL)
return false;
const char* src = static_cast<const char*>(data);
for (size_t i = 0; i < len; i++, dst += 2, src += 1)
{
dst[0] = StringBuilder_numbers[(src[0] >> 4) & 0xF];
dst[1] = StringBuilder_numbers[(src[0] ) & 0xF];
}
return true;
}
bool StringBuilder::_opVFormat(uint32_t op, const char* fmt, va_list ap) {
char buf[1024];
vsnprintf(buf, ASMJIT_ARRAY_SIZE(buf), fmt, ap);
buf[ASMJIT_ARRAY_SIZE(buf) - 1] = '\0';
return _opString(op, buf);
}
bool StringBuilder::setFormat(const char* fmt, ...) {
bool result;
va_list ap;
va_start(ap, fmt);
result = _opVFormat(kStringOpSet, fmt, ap);
va_end(ap);
return result;
}
bool StringBuilder::appendFormat(const char* fmt, ...) {
bool result;
va_list ap;
va_start(ap, fmt);
result = _opVFormat(kStringOpAppend, fmt, ap);
va_end(ap);
return result;
}
bool StringBuilder::eq(const char* str, size_t len) const {
const char* aData = _data;
const char* bData = str;
size_t aLength = _length;
size_t bLength = len;
if (bLength == kInvalidIndex) {
size_t i;
for (i = 0; i < aLength; i++) {
if (aData[i] != bData[i] || bData[i] == 0)
return false;
}
return bData[i] == 0;
}
else {
if (aLength != bLength)
return false;
return ::memcmp(aData, bData, aLength) == 0;
}
}
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Guard]
#ifndef _ASMJIT_BASE_STRING_H
#define _ASMJIT_BASE_STRING_H
// [Dependencies - AsmJit]
#include "../base/globals.h"
// [Dependencies - C]
#include <stdarg.h>
// [Api-Begin]
#include "../apibegin.h"
namespace asmjit {
//! \addtogroup asmjit_base_util
//! \{
// ============================================================================
// [asmjit::kStringOp]
// ============================================================================
//! \internal
//!
//! String operation.
ASMJIT_ENUM(kStringOp) {
//! Replace the current string by a given content.
kStringOpSet = 0,
//! Append a given content to the current string.
kStringOpAppend = 1
};
// ============================================================================
// [asmjit::kStringFormat]
// ============================================================================
//! \internal
//!
//! String format flags.
ASMJIT_ENUM(kStringFormat) {
kStringFormatShowSign = 0x00000001,
kStringFormatShowSpace = 0x00000002,
kStringFormatAlternate = 0x00000004,
kStringFormatSigned = 0x80000000
};
// ============================================================================
// [asmjit::StringUtil]
// ============================================================================
//! String utilities.
struct StringUtil {
static ASMJIT_INLINE size_t nlen(const char* s, size_t maxlen) {
size_t i;
for (i = 0; i < maxlen; i++)
if (!s[i])
break;
return i;
}
};
// ============================================================================
// [asmjit::StringBuilder]
// ============================================================================
//! String builder.
//!
//! String builder was designed to be able to build a string using append like
//! operation to append numbers, other strings, or signle characters. It can
//! allocate it's own buffer or use a buffer created on the stack.
//!
//! String builder contains method specific to AsmJit functionality, used for
//! logging or HTML output.
struct StringBuilder {
ASMJIT_NO_COPY(StringBuilder)
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
ASMJIT_API StringBuilder();
ASMJIT_API ~StringBuilder();
ASMJIT_INLINE StringBuilder(const _NoInit&) {}
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
//! Get string builder capacity.
ASMJIT_INLINE size_t getCapacity() const { return _capacity; }
//! Get length.
ASMJIT_INLINE size_t getLength() const { return _length; }
//! Get null-terminated string data.
ASMJIT_INLINE char* getData() { return _data; }
//! Get null-terminated string data (const).
ASMJIT_INLINE const char* getData() const { return _data; }
// --------------------------------------------------------------------------
// [Prepare / Reserve]
// --------------------------------------------------------------------------
//! Prepare to set/append.
ASMJIT_API char* prepare(uint32_t op, size_t len);
//! Reserve `to` bytes in string builder.
ASMJIT_API bool reserve(size_t to);
// --------------------------------------------------------------------------
// [Clear]
// --------------------------------------------------------------------------
//! Clear the content in String builder.
ASMJIT_API void clear();
// --------------------------------------------------------------------------
// [Op]
// --------------------------------------------------------------------------
ASMJIT_API bool _opString(uint32_t op, const char* str, size_t len = kInvalidIndex);
ASMJIT_API bool _opVFormat(uint32_t op, const char* fmt, va_list ap);
ASMJIT_API bool _opChar(uint32_t op, char c);
ASMJIT_API bool _opChars(uint32_t op, char c, size_t len);
ASMJIT_API bool _opNumber(uint32_t op, uint64_t i, uint32_t base = 0, size_t width = 0, uint32_t flags = 0);
ASMJIT_API bool _opHex(uint32_t op, const void* data, size_t len);
// --------------------------------------------------------------------------
// [Set]
// --------------------------------------------------------------------------
//! Replace the current content by `str` of `len`.
ASMJIT_INLINE bool setString(const char* str, size_t len = kInvalidIndex) {
return _opString(kStringOpSet, str, len);
}
//! Replace the current content by formatted string `fmt`.
ASMJIT_INLINE bool setVFormat(const char* fmt, va_list ap) {
return _opVFormat(kStringOpSet, fmt, ap);
}
//! Replace the current content by formatted string `fmt`.
ASMJIT_API bool setFormat(const char* fmt, ...);
//! Replace the current content by `c` character.
ASMJIT_INLINE bool setChar(char c) {
return _opChar(kStringOpSet, c);
}
//! Replace the current content by `c` of `len`.
ASMJIT_INLINE bool setChars(char c, size_t len) {
return _opChars(kStringOpSet, c, len);
}
//! Replace the current content by formatted integer `i`.
ASMJIT_INLINE bool setInt(uint64_t i, uint32_t base = 0, size_t width = 0, uint32_t flags = 0) {
return _opNumber(kStringOpSet, i, base, width, flags | kStringFormatSigned);
}
//! Replace the current content by formatted integer `i`.
ASMJIT_INLINE bool setUInt(uint64_t i, uint32_t base = 0, size_t width = 0, uint32_t flags = 0) {
return _opNumber(kStringOpSet, i, base, width, flags);
}
//! Replace the current content by the given `data` converted to a HEX string.
ASMJIT_INLINE bool setHex(const void* data, size_t len) {
return _opHex(kStringOpSet, data, len);
}
// --------------------------------------------------------------------------
// [Append]
// --------------------------------------------------------------------------
//! Append `str` of `len`.
ASMJIT_INLINE bool appendString(const char* str, size_t len = kInvalidIndex) {
return _opString(kStringOpAppend, str, len);
}
//! Append a formatted string `fmt` to the current content.
ASMJIT_INLINE bool appendVFormat(const char* fmt, va_list ap) {
return _opVFormat(kStringOpAppend, fmt, ap);
}
//! Append a formatted string `fmt` to the current content.
ASMJIT_API bool appendFormat(const char* fmt, ...);
//! Append `c` character.
ASMJIT_INLINE bool appendChar(char c) {
return _opChar(kStringOpAppend, c);
}
//! Append `c` of `len`.
ASMJIT_INLINE bool appendChars(char c, size_t len) {
return _opChars(kStringOpAppend, c, len);
}
//! Append `i`.
ASMJIT_INLINE bool appendInt(int64_t i, uint32_t base = 0, size_t width = 0, uint32_t flags = 0) {
return _opNumber(kStringOpAppend, static_cast<uint64_t>(i), base, width, flags | kStringFormatSigned);
}
//! Append `i`.
ASMJIT_INLINE bool appendUInt(uint64_t i, uint32_t base = 0, size_t width = 0, uint32_t flags = 0) {
return _opNumber(kStringOpAppend, i, base, width, flags);
}
//! Append the given `data` converted to a HEX string.
ASMJIT_INLINE bool appendHex(const void* data, size_t len) {
return _opHex(kStringOpAppend, data, len);
}
// --------------------------------------------------------------------------
// [_Append]
// --------------------------------------------------------------------------
//! Append `str` of `len`, inlined, without buffer overflow check.
ASMJIT_INLINE void _appendString(const char* str, size_t len = kInvalidIndex) {
// len should be a constant if we are inlining.
if (len == kInvalidIndex) {
char* p = &_data[_length];
while (*str) {
ASMJIT_ASSERT(p < _data + _capacity);
*p++ = *str++;
}
*p = '\0';
_length = (size_t)(p - _data);
}
else {
ASMJIT_ASSERT(_capacity - _length >= len);
char* p = &_data[_length];
char* pEnd = p + len;
while (p < pEnd)
*p++ = *str++;
*p = '\0';
_length += len;
}
}
//! Append `c` character, inlined, without buffer overflow check.
ASMJIT_INLINE void _appendChar(char c) {
ASMJIT_ASSERT(_capacity - _length >= 1);
_data[_length] = c;
_length++;
_data[_length] = '\0';
}
//! Append `c` of `len`, inlined, without buffer overflow check.
ASMJIT_INLINE void _appendChars(char c, size_t len) {
ASMJIT_ASSERT(_capacity - _length >= len);
char* p = &_data[_length];
char* pEnd = p + len;
while (p < pEnd)
*p++ = c;
*p = '\0';
_length += len;
}
ASMJIT_INLINE void _appendUInt32(uint32_t i) {
char buf_[32];
char* pEnd = buf_ + ASMJIT_ARRAY_SIZE(buf_);
char* pBuf = pEnd;
do {
uint32_t d = i / 10;
uint32_t r = i % 10;
*--pBuf = static_cast<uint8_t>(r + '0');
i = d;
} while (i);
ASMJIT_ASSERT(_capacity - _length >= (size_t)(pEnd - pBuf));
char* p = &_data[_length];
do {
*p++ = *pBuf;
} while (++pBuf != pEnd);
*p = '\0';
_length = (size_t)(p - _data);
}
// --------------------------------------------------------------------------
// [Eq]
// --------------------------------------------------------------------------
//! Check for equality with other `str` of `len`.
ASMJIT_API bool eq(const char* str, size_t len = kInvalidIndex) const;
//! Check for equality with `other`.
ASMJIT_INLINE bool eq(const StringBuilder& other) const {
return eq(other._data);
}
// --------------------------------------------------------------------------
// [Operator Overload]
// --------------------------------------------------------------------------
ASMJIT_INLINE bool operator==(const StringBuilder& other) const { return eq(other); }
ASMJIT_INLINE bool operator!=(const StringBuilder& other) const { return !eq(other); }
ASMJIT_INLINE bool operator==(const char* str) const { return eq(str); }
ASMJIT_INLINE bool operator!=(const char* str) const { return !eq(str); }
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
//! String data.
char* _data;
//! Length.
size_t _length;
//! Capacity.
size_t _capacity;
//! Whether the string can be freed.
size_t _canFree;
};
// ============================================================================
// [asmjit::StringBuilderT]
// ============================================================================
//! \internal
template<size_t N>
struct StringBuilderT : public StringBuilder {
ASMJIT_NO_COPY(StringBuilderT<N>)
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
ASMJIT_INLINE StringBuilderT() : StringBuilder(NoInit) {
_data = _embeddedData;
_data[0] = 0;
_length = 0;
_capacity = N;
_canFree = false;
}
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
//! Embedded data.
char _embeddedData[static_cast<size_t>(
N + 1 + sizeof(intptr_t)) & ~static_cast<size_t>(sizeof(intptr_t) - 1)];
};
//! \}
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
// [Guard]
#endif // _ASMJIT_BASE_STRING_H
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Guard]
#ifndef _ASMJIT_BASE_VECTYPES_H
#define _ASMJIT_BASE_VECTYPES_H
// [Dependencies - AsmJit]
#include "../base/globals.h"
// [Api-Begin]
#include "../apibegin.h"
namespace asmjit {
//! \addtogroup asmjit_base_util
//! \{
// ============================================================================
// [asmjit::Vec64]
// ============================================================================
//! 64-bit vector register data.
union Vec64 {
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! Set all eight 8-bit signed integers.
static ASMJIT_INLINE Vec64 fromSb(
int8_t x0, int8_t x1, int8_t x2, int8_t x3, int8_t x4, int8_t x5, int8_t x6, int8_t x7)
{
Vec64 self;
self.setSb(x0, x1, x2, x3, x4, x5, x6, x7);
return self;
}
//! Set all eight 8-bit signed integers.
static ASMJIT_INLINE Vec64 fromSb(
int8_t x0)
{
Vec64 self;
self.setSb(x0);
return self;
}
//! Set all eight 8-bit unsigned integers.
static ASMJIT_INLINE Vec64 fromUb(
uint8_t x0, uint8_t x1, uint8_t x2, uint8_t x3, uint8_t x4, uint8_t x5, uint8_t x6, uint8_t x7)
{
Vec64 self;
self.setUb(x0, x1, x2, x3, x4, x5, x6, x7);
return self;
}
//! Set all eight 8-bit unsigned integers.
static ASMJIT_INLINE Vec64 fromUb(
uint8_t x0)
{
Vec64 self;
self.setUb(x0);
return self;
}
//! Set all four 16-bit signed integers.
static ASMJIT_INLINE Vec64 fromSw(
int16_t x0, int16_t x1, int16_t x2, int16_t x3)
{
Vec64 self;
self.setSw(x0, x1, x2, x3);
return self;
}
//! Set all four 16-bit signed integers.
static ASMJIT_INLINE Vec64 fromSw(
int16_t x0)
{
Vec64 self;
self.setSw(x0);
return self;
}
//! Set all four 16-bit unsigned integers.
static ASMJIT_INLINE Vec64 fromUw(
uint16_t x0, uint16_t x1, uint16_t x2, uint16_t x3)
{
Vec64 self;
self.setUw(x0, x1, x2, x3);
return self;
}
//! Set all four 16-bit unsigned integers.
static ASMJIT_INLINE Vec64 fromUw(
uint16_t x0)
{
Vec64 self;
self.setUw(x0);
return self;
}
//! Set all two 32-bit signed integers.
static ASMJIT_INLINE Vec64 fromSd(
int32_t x0, int32_t x1)
{
Vec64 self;
self.setSd(x0, x1);
return self;
}
//! Set all two 32-bit signed integers.
static ASMJIT_INLINE Vec64 fromSd(
int32_t x0)
{
Vec64 self;
self.setSd(x0);
return self;
}
//! Set all two 32-bit unsigned integers.
static ASMJIT_INLINE Vec64 fromUd(
uint32_t x0, uint32_t x1)
{
Vec64 self;
self.setUd(x0, x1);
return self;
}
//! Set all two 32-bit unsigned integers.
static ASMJIT_INLINE Vec64 fromUd(
uint32_t x0)
{
Vec64 self;
self.setUd(x0);
return self;
}
//! Set 64-bit signed integer.
static ASMJIT_INLINE Vec64 fromSq(
int64_t x0)
{
Vec64 self;
self.setSq(x0);
return self;
}
//! Set 64-bit unsigned integer.
static ASMJIT_INLINE Vec64 fromUq(
uint64_t x0)
{
Vec64 self;
self.setUq(x0);
return self;
}
//! Set all two SP-FP values.
static ASMJIT_INLINE Vec64 fromSf(
float x0, float x1)
{
Vec64 self;
self.setSf(x0, x1);
return self;
}
//! Set all two SP-FP values.
static ASMJIT_INLINE Vec64 fromSf(
float x0)
{
Vec64 self;
self.setSf(x0);
return self;
}
//! Set all two SP-FP values.
static ASMJIT_INLINE Vec64 fromDf(
double x0)
{
Vec64 self;
self.setDf(x0);
return self;
}
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
//! Set all eight 8-bit signed integers.
ASMJIT_INLINE void setSb(
int8_t x0, int8_t x1, int8_t x2, int8_t x3, int8_t x4, int8_t x5, int8_t x6, int8_t x7)
{
sb[0] = x0; sb[1] = x1; sb[2] = x2; sb[3] = x3;
sb[4] = x4; sb[5] = x5; sb[6] = x6; sb[7] = x7;
}
//! Set all eight 8-bit signed integers.
ASMJIT_INLINE void setSb(
int8_t x0)
{
setUb(static_cast<uint8_t>(x0));
}
//! Set all eight 8-bit unsigned integers.
ASMJIT_INLINE void setUb(
uint8_t x0, uint8_t x1, uint8_t x2, uint8_t x3, uint8_t x4, uint8_t x5, uint8_t x6, uint8_t x7)
{
ub[0] = x0; ub[1] = x1; ub[2] = x2; ub[3] = x3;
ub[4] = x4; ub[5] = x5; ub[6] = x6; ub[7] = x7;
}
//! Set all eight 8-bit unsigned integers.
ASMJIT_INLINE void setUb(
uint8_t x0)
{
if (kArchHost64Bit) {
uint64_t t = static_cast<uint64_t>(x0) * ASMJIT_UINT64_C(0x0101010101010101);
uq[0] = t;
}
else {
uint32_t t = static_cast<uint32_t>(x0) * static_cast<uint32_t>(0x01010101U);
ud[0] = t;
ud[1] = t;
}
}
//! Set all four 16-bit signed integers.
ASMJIT_INLINE void setSw(
int16_t x0, int16_t x1, int16_t x2, int16_t x3)
{
sw[0] = x0; sw[1] = x1; sw[2] = x2; sw[3] = x3;
}
//! Set all four 16-bit signed integers.
ASMJIT_INLINE void setSw(
int16_t x0)
{
setUw(static_cast<uint16_t>(x0));
}
//! Set all four 16-bit unsigned integers.
ASMJIT_INLINE void setUw(
uint16_t x0, uint16_t x1, uint16_t x2, uint16_t x3)
{
uw[0] = x0; uw[1] = x1; uw[2] = x2; uw[3] = x3;
}
//! Set all four 16-bit unsigned integers.
ASMJIT_INLINE void setUw(
uint16_t x0)
{
if (kArchHost64Bit) {
uint64_t t = static_cast<uint64_t>(x0) * ASMJIT_UINT64_C(0x0001000100010001);
uq[0] = t;
}
else {
uint32_t t = static_cast<uint32_t>(x0) * static_cast<uint32_t>(0x00010001U);
ud[0] = t;
ud[1] = t;
}
}
//! Set all two 32-bit signed integers.
ASMJIT_INLINE void setSd(
int32_t x0, int32_t x1)
{
sd[0] = x0; sd[1] = x1;
}
//! Set all two 32-bit signed integers.
ASMJIT_INLINE void setSd(
int32_t x0)
{
sd[0] = x0; sd[1] = x0;
}
//! Set all two 32-bit unsigned integers.
ASMJIT_INLINE void setUd(
uint32_t x0, uint32_t x1)
{
ud[0] = x0; ud[1] = x1;
}
//! Set all two 32-bit unsigned integers.
ASMJIT_INLINE void setUd(
uint32_t x0)
{
ud[0] = x0; ud[1] = x0;
}
//! Set 64-bit signed integer.
ASMJIT_INLINE void setSq(
int64_t x0)
{
sq[0] = x0;
}
//! Set 64-bit unsigned integer.
ASMJIT_INLINE void setUq(
uint64_t x0)
{
uq[0] = x0;
}
//! Set all two SP-FP values.
ASMJIT_INLINE void setSf(
float x0, float x1)
{
sf[0] = x0; sf[1] = x1;
}
//! Set all two SP-FP values.
ASMJIT_INLINE void setSf(
float x0)
{
sf[0] = x0; sf[1] = x0;
}
//! Set all two SP-FP values.
ASMJIT_INLINE void setDf(
double x0)
{
df[0] = x0;
}
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
//! Array of eight 8-bit signed integers.
int8_t sb[8];
//! Array of eight 8-bit unsigned integers.
uint8_t ub[8];
//! Array of four 16-bit signed integers.
int16_t sw[4];
//! Array of four 16-bit unsigned integers.
uint16_t uw[4];
//! Array of two 32-bit signed integers.
int32_t sd[2];
//! Array of two 32-bit unsigned integers.
uint32_t ud[2];
//! Array of one 64-bit signed integer.
int64_t sq[1];
//! Array of one 64-bit unsigned integer.
uint64_t uq[1];
//! Array of two SP-FP values.
float sf[2];
//! Array of one DP-FP value.
double df[1];
};
// ============================================================================
// [asmjit::Vec128]
// ============================================================================
//! 128-bit vector register data.
union Vec128 {
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! Set all sixteen 8-bit signed integers.
static ASMJIT_INLINE Vec128 fromSb(
int8_t x0 , int8_t x1 , int8_t x2 , int8_t x3 ,
int8_t x4 , int8_t x5 , int8_t x6 , int8_t x7 ,
int8_t x8 , int8_t x9 , int8_t x10, int8_t x11,
int8_t x12, int8_t x13, int8_t x14, int8_t x15)
{
Vec128 self;
self.setSb(x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15);
return self;
}
//! Set all sixteen 8-bit signed integers.
static ASMJIT_INLINE Vec128 fromSb(
int8_t x0)
{
Vec128 self;
self.setSb(x0);
return self;
}
//! Set all sixteen 8-bit unsigned integers.
static ASMJIT_INLINE Vec128 fromUb(
uint8_t x0 , uint8_t x1 , uint8_t x2 , uint8_t x3 ,
uint8_t x4 , uint8_t x5 , uint8_t x6 , uint8_t x7 ,
uint8_t x8 , uint8_t x9 , uint8_t x10, uint8_t x11,
uint8_t x12, uint8_t x13, uint8_t x14, uint8_t x15)
{
Vec128 self;
self.setUb(x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15);
return self;
}
//! Set all sixteen 8-bit unsigned integers.
static ASMJIT_INLINE Vec128 fromUb(
uint8_t x0)
{
Vec128 self;
self.setUb(x0);
return self;
}
//! Set all eight 16-bit signed integers.
static ASMJIT_INLINE Vec128 fromSw(
int16_t x0, int16_t x1, int16_t x2, int16_t x3, int16_t x4, int16_t x5, int16_t x6, int16_t x7)
{
Vec128 self;
self.setSw(x0, x1, x2, x3, x4, x5, x6, x7);
return self;
}
//! Set all eight 16-bit signed integers.
static ASMJIT_INLINE Vec128 fromSw(
int16_t x0)
{
Vec128 self;
self.setSw(x0);
return self;
}
//! Set all eight 16-bit unsigned integers.
static ASMJIT_INLINE Vec128 fromUw(
uint16_t x0, uint16_t x1, uint16_t x2, uint16_t x3, uint16_t x4, uint16_t x5, uint16_t x6, uint16_t x7)
{
Vec128 self;
self.setUw(x0, x1, x2, x3, x4, x5, x6, x7);
return self;
}
//! Set all eight 16-bit unsigned integers.
static ASMJIT_INLINE Vec128 fromUw(
uint16_t x0)
{
Vec128 self;
self.setUw(x0);
return self;
}
//! Set all four 32-bit signed integers.
static ASMJIT_INLINE Vec128 fromSd(
int32_t x0, int32_t x1, int32_t x2, int32_t x3)
{
Vec128 self;
self.setSd(x0, x1, x2, x3);
return self;
}
//! Set all four 32-bit signed integers.
static ASMJIT_INLINE Vec128 fromSd(
int32_t x0)
{
Vec128 self;
self.setSd(x0);
return self;
}
//! Set all four 32-bit unsigned integers.
static ASMJIT_INLINE Vec128 fromUd(
uint32_t x0, uint32_t x1, uint32_t x2, uint32_t x3)
{
Vec128 self;
self.setUd(x0, x1, x2, x3);
return self;
}
//! Set all four 32-bit unsigned integers.
static ASMJIT_INLINE Vec128 fromUd(
uint32_t x0)
{
Vec128 self;
self.setUd(x0);
return self;
}
//! Set all two 64-bit signed integers.
static ASMJIT_INLINE Vec128 fromSq(
int64_t x0, int64_t x1)
{
Vec128 self;
self.setSq(x0, x1);
return self;
}
//! Set all two 64-bit signed integers.
static ASMJIT_INLINE Vec128 fromSq(
int64_t x0)
{
Vec128 self;
self.setSq(x0);
return self;
}
//! Set all two 64-bit unsigned integers.
static ASMJIT_INLINE Vec128 fromUq(
uint64_t x0, uint64_t x1)
{
Vec128 self;
self.setUq(x0, x1);
return self;
}
//! Set all two 64-bit unsigned integers.
static ASMJIT_INLINE Vec128 fromUq(
uint64_t x0)
{
Vec128 self;
self.setUq(x0);
return self;
}
//! Set all four SP-FP floats.
static ASMJIT_INLINE Vec128 fromSf(
float x0, float x1, float x2, float x3)
{
Vec128 self;
self.setSf(x0, x1, x2, x3);
return self;
}
//! Set all four SP-FP floats.
static ASMJIT_INLINE Vec128 fromSf(
float x0)
{
Vec128 self;
self.setSf(x0);
return self;
}
//! Set all two DP-FP floats.
static ASMJIT_INLINE Vec128 fromDf(
double x0, double x1)
{
Vec128 self;
self.setDf(x0, x1);
return self;
}
//! Set all two DP-FP floats.
static ASMJIT_INLINE Vec128 fromDf(
double x0)
{
Vec128 self;
self.setDf(x0);
return self;
}
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
//! Set all sixteen 8-bit signed integers.
ASMJIT_INLINE void setSb(
int8_t x0 , int8_t x1 , int8_t x2 , int8_t x3 ,
int8_t x4 , int8_t x5 , int8_t x6 , int8_t x7 ,
int8_t x8 , int8_t x9 , int8_t x10, int8_t x11,
int8_t x12, int8_t x13, int8_t x14, int8_t x15)
{
sb[0 ] = x0 ; sb[1 ] = x1 ; sb[2 ] = x2 ; sb[3 ] = x3 ;
sb[4 ] = x4 ; sb[5 ] = x5 ; sb[6 ] = x6 ; sb[7 ] = x7 ;
sb[8 ] = x8 ; sb[9 ] = x9 ; sb[10] = x10; sb[11] = x11;
sb[12] = x12; sb[13] = x13; sb[14] = x14; sb[15] = x15;
}
//! Set all sixteen 8-bit signed integers.
ASMJIT_INLINE void setSb(
int8_t x0)
{
setUb(static_cast<uint8_t>(x0));
}
//! Set all sixteen 8-bit unsigned integers.
ASMJIT_INLINE void setUb(
uint8_t x0 , uint8_t x1 , uint8_t x2 , uint8_t x3 ,
uint8_t x4 , uint8_t x5 , uint8_t x6 , uint8_t x7 ,
uint8_t x8 , uint8_t x9 , uint8_t x10, uint8_t x11,
uint8_t x12, uint8_t x13, uint8_t x14, uint8_t x15)
{
ub[0 ] = x0 ; ub[1 ] = x1 ; ub[2 ] = x2 ; ub[3 ] = x3 ;
ub[4 ] = x4 ; ub[5 ] = x5 ; ub[6 ] = x6 ; ub[7 ] = x7 ;
ub[8 ] = x8 ; ub[9 ] = x9 ; ub[10] = x10; ub[11] = x11;
ub[12] = x12; ub[13] = x13; ub[14] = x14; ub[15] = x15;
}
//! Set all sixteen 8-bit unsigned integers.
ASMJIT_INLINE void setUb(
uint8_t x0)
{
if (kArchHost64Bit) {
uint64_t t = static_cast<uint64_t>(x0) * ASMJIT_UINT64_C(0x0101010101010101);
uq[0] = t;
uq[1] = t;
}
else {
uint32_t t = static_cast<uint32_t>(x0) * static_cast<uint32_t>(0x01010101U);
ud[0] = t;
ud[1] = t;
ud[2] = t;
ud[3] = t;
}
}
//! Set all eight 16-bit signed integers.
ASMJIT_INLINE void setSw(
int16_t x0, int16_t x1, int16_t x2, int16_t x3, int16_t x4, int16_t x5, int16_t x6, int16_t x7)
{
sw[0] = x0; sw[1] = x1; sw[2] = x2; sw[3] = x3;
sw[4] = x4; sw[5] = x5; sw[6] = x6; sw[7] = x7;
}
//! Set all eight 16-bit signed integers.
ASMJIT_INLINE void setSw(
int16_t x0)
{
setUw(static_cast<uint16_t>(x0));
}
//! Set all eight 16-bit unsigned integers.
ASMJIT_INLINE void setUw(
uint16_t x0, uint16_t x1, uint16_t x2, uint16_t x3, uint16_t x4, uint16_t x5, uint16_t x6, uint16_t x7)
{
uw[0] = x0; uw[1] = x1; uw[2] = x2; uw[3] = x3;
uw[4] = x4; uw[5] = x5; uw[6] = x6; uw[7] = x7;
}
//! Set all eight 16-bit unsigned integers.
ASMJIT_INLINE void setUw(
uint16_t x0)
{
if (kArchHost64Bit) {
uint64_t t = static_cast<uint64_t>(x0) * ASMJIT_UINT64_C(0x0001000100010001);
uq[0] = t;
uq[1] = t;
}
else {
uint32_t t = static_cast<uint32_t>(x0) * static_cast<uint32_t>(0x00010001U);
ud[0] = t;
ud[1] = t;
ud[2] = t;
ud[3] = t;
}
}
//! Set all four 32-bit signed integers.
ASMJIT_INLINE void setSd(
int32_t x0, int32_t x1, int32_t x2, int32_t x3)
{
sd[0] = x0; sd[1] = x1; sd[2] = x2; sd[3] = x3;
}
//! Set all four 32-bit signed integers.
ASMJIT_INLINE void setSd(
int32_t x0)
{
setUd(static_cast<uint32_t>(x0));
}
//! Set all four 32-bit unsigned integers.
ASMJIT_INLINE void setUd(
uint32_t x0, uint32_t x1, uint32_t x2, uint32_t x3)
{
ud[0] = x0; ud[1] = x1; ud[2] = x2; ud[3] = x3;
}
//! Set all four 32-bit unsigned integers.
ASMJIT_INLINE void setUd(
uint32_t x0)
{
if (kArchHost64Bit) {
uint64_t t = (static_cast<uint64_t>(x0) << 32) + x0;
uq[0] = t;
uq[1] = t;
}
else {
ud[0] = x0;
ud[1] = x0;
ud[2] = x0;
ud[3] = x0;
}
}
//! Set all two 64-bit signed integers.
ASMJIT_INLINE void setSq(
int64_t x0, int64_t x1)
{
sq[0] = x0; sq[1] = x1;
}
//! Set all two 64-bit signed integers.
ASMJIT_INLINE void setSq(
int64_t x0)
{
sq[0] = x0; sq[1] = x0;
}
//! Set all two 64-bit unsigned integers.
ASMJIT_INLINE void setUq(
uint64_t x0, uint64_t x1)
{
uq[0] = x0; uq[1] = x1;
}
//! Set all two 64-bit unsigned integers.
ASMJIT_INLINE void setUq(
uint64_t x0)
{
uq[0] = x0; uq[1] = x0;
}
//! Set all four SP-FP floats.
ASMJIT_INLINE void setSf(
float x0, float x1, float x2, float x3)
{
sf[0] = x0; sf[1] = x1; sf[2] = x2; sf[3] = x3;
}
//! Set all four SP-FP floats.
ASMJIT_INLINE void setSf(
float x0)
{
sf[0] = x0; sf[1] = x0; sf[2] = x0; sf[3] = x0;
}
//! Set all two DP-FP floats.
ASMJIT_INLINE void setDf(
double x0, double x1)
{
df[0] = x0; df[1] = x1;
}
//! Set all two DP-FP floats.
ASMJIT_INLINE void setDf(
double x0)
{
df[0] = x0; df[1] = x0;
}
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
//! Array of sixteen 8-bit signed integers.
int8_t sb[16];
//! Array of sixteen 8-bit unsigned integers.
uint8_t ub[16];
//! Array of eight 16-bit signed integers.
int16_t sw[8];
//! Array of eight 16-bit unsigned integers.
uint16_t uw[8];
//! Array of four 32-bit signed integers.
int32_t sd[4];
//! Array of four 32-bit unsigned integers.
uint32_t ud[4];
//! Array of two 64-bit signed integers.
int64_t sq[2];
//! Array of two 64-bit unsigned integers.
uint64_t uq[2];
//! Array of four 32-bit single precision floating points.
float sf[4];
//! Array of two 64-bit double precision floating points.
double df[2];
};
// ============================================================================
// [asmjit::Vec256]
// ============================================================================
//! 256-bit vector register data.
union Vec256 {
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! Set all thirty two 8-bit signed integers.
static ASMJIT_INLINE Vec256 fromSb(
int8_t x0 , int8_t x1 , int8_t x2 , int8_t x3 ,
int8_t x4 , int8_t x5 , int8_t x6 , int8_t x7 ,
int8_t x8 , int8_t x9 , int8_t x10, int8_t x11,
int8_t x12, int8_t x13, int8_t x14, int8_t x15,
int8_t x16, int8_t x17, int8_t x18, int8_t x19,
int8_t x20, int8_t x21, int8_t x22, int8_t x23,
int8_t x24, int8_t x25, int8_t x26, int8_t x27,
int8_t x28, int8_t x29, int8_t x30, int8_t x31)
{
Vec256 self;
self.setSb(
x0, x1 , x2 , x3 , x4 , x5 , x6 , x7 , x8 , x9 , x10, x11, x12, x13, x14, x15,
x16, x17, x18, x19, x20, x21, x22, x23, x24, x25, x26, x27, x28, x29, x30, x31);
return self;
}
//! Set all thirty two 8-bit signed integers.
static ASMJIT_INLINE Vec256 fromSb(
int8_t x0)
{
Vec256 self;
self.setSb(x0);
return self;
}
//! Set all thirty two 8-bit unsigned integers.
static ASMJIT_INLINE Vec256 fromUb(
uint8_t x0 , uint8_t x1 , uint8_t x2 , uint8_t x3 ,
uint8_t x4 , uint8_t x5 , uint8_t x6 , uint8_t x7 ,
uint8_t x8 , uint8_t x9 , uint8_t x10, uint8_t x11,
uint8_t x12, uint8_t x13, uint8_t x14, uint8_t x15,
uint8_t x16, uint8_t x17, uint8_t x18, uint8_t x19,
uint8_t x20, uint8_t x21, uint8_t x22, uint8_t x23,
uint8_t x24, uint8_t x25, uint8_t x26, uint8_t x27,
uint8_t x28, uint8_t x29, uint8_t x30, uint8_t x31)
{
Vec256 self;
self.setUb(
x0, x1 , x2 , x3 , x4 , x5 , x6 , x7 , x8 , x9 , x10, x11, x12, x13, x14, x15,
x16, x17, x18, x19, x20, x21, x22, x23, x24, x25, x26, x27, x28, x29, x30, x31);
return self;
}
//! Set all thirty two 8-bit unsigned integers.
static ASMJIT_INLINE Vec256 fromUb(
uint8_t x0)
{
Vec256 self;
self.setUb(x0);
return self;
}
//! Set all sixteen 16-bit signed integers.
static ASMJIT_INLINE Vec256 fromSw(
int16_t x0, int16_t x1, int16_t x2 , int16_t x3 , int16_t x4 , int16_t x5 , int16_t x6 , int16_t x7 ,
int16_t x8, int16_t x9, int16_t x10, int16_t x11, int16_t x12, int16_t x13, int16_t x14, int16_t x15)
{
Vec256 self;
self.setSw(x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15);
return self;
}
//! Set all sixteen 16-bit signed integers.
static ASMJIT_INLINE Vec256 fromSw(
int16_t x0)
{
Vec256 self;
self.setSw(x0);
return self;
}
//! Set all sixteen 16-bit unsigned integers.
static ASMJIT_INLINE Vec256 fromUw(
uint16_t x0, uint16_t x1, uint16_t x2 , uint16_t x3 , uint16_t x4 , uint16_t x5 , uint16_t x6 , uint16_t x7 ,
uint16_t x8, uint16_t x9, uint16_t x10, uint16_t x11, uint16_t x12, uint16_t x13, uint16_t x14, uint16_t x15)
{
Vec256 self;
self.setUw(x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15);
return self;
}
//! Set all sixteen 16-bit unsigned integers.
static ASMJIT_INLINE Vec256 fromUw(
uint16_t x0)
{
Vec256 self;
self.setUw(x0);
return self;
}
//! Set all eight 32-bit signed integers.
static ASMJIT_INLINE Vec256 fromSd(
int32_t x0, int32_t x1, int32_t x2, int32_t x3,
int32_t x4, int32_t x5, int32_t x6, int32_t x7)
{
Vec256 self;
self.setSd(x0, x1, x2, x3, x4, x5, x6, x7);
return self;
}
//! Set all eight 32-bit signed integers.
static ASMJIT_INLINE Vec256 fromSd(
int32_t x0)
{
Vec256 self;
self.setSd(x0);
return self;
}
//! Set all eight 32-bit unsigned integers.
static ASMJIT_INLINE Vec256 fromUd(
uint32_t x0, uint32_t x1, uint32_t x2, uint32_t x3,
uint32_t x4, uint32_t x5, uint32_t x6, uint32_t x7)
{
Vec256 self;
self.setUd(x0, x1, x2, x3, x4, x5, x6, x7);
return self;
}
//! Set all eight 32-bit unsigned integers.
static ASMJIT_INLINE Vec256 fromUd(
uint32_t x0)
{
Vec256 self;
self.setUd(x0);
return self;
}
//! Set all four 64-bit signed integers.
static ASMJIT_INLINE Vec256 fromSq(
int64_t x0, int64_t x1, int64_t x2, int64_t x3)
{
Vec256 self;
self.setSq(x0, x1, x2, x3);
return self;
}
//! Set all four 64-bit signed integers.
static ASMJIT_INLINE Vec256 fromSq(
int64_t x0)
{
Vec256 self;
self.setSq(x0);
return self;
}
//! Set all four 64-bit unsigned integers.
static ASMJIT_INLINE Vec256 fromUq(
uint64_t x0, uint64_t x1, uint64_t x2, uint64_t x3)
{
Vec256 self;
self.setUq(x0, x1, x2, x3);
return self;
}
//! Set all four 64-bit unsigned integers.
static ASMJIT_INLINE Vec256 fromUq(
uint64_t x0)
{
Vec256 self;
self.setUq(x0);
return self;
}
//! Set all eight SP-FP floats.
static ASMJIT_INLINE Vec256 fromSf(
float x0, float x1, float x2, float x3,
float x4, float x5, float x6, float x7)
{
Vec256 self;
self.setSf(x0, x1, x2, x3, x4, x5, x6, x7);
return self;
}
//! Set all eight SP-FP floats.
static ASMJIT_INLINE Vec256 fromSf(
float x0)
{
Vec256 self;
self.setSf(x0);
return self;
}
//! Set all four DP-FP floats.
static ASMJIT_INLINE Vec256 fromDf(
double x0, double x1, double x2, double x3)
{
Vec256 self;
self.setDf(x0, x1, x2, x3);
return self;
}
//! Set all four DP-FP floats.
static ASMJIT_INLINE Vec256 fromDf(
double x0)
{
Vec256 self;
self.setDf(x0);
return self;
}
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
//! Set all thirty two 8-bit signed integers.
ASMJIT_INLINE void setSb(
int8_t x0 , int8_t x1 , int8_t x2 , int8_t x3 ,
int8_t x4 , int8_t x5 , int8_t x6 , int8_t x7 ,
int8_t x8 , int8_t x9 , int8_t x10, int8_t x11,
int8_t x12, int8_t x13, int8_t x14, int8_t x15,
int8_t x16, int8_t x17, int8_t x18, int8_t x19,
int8_t x20, int8_t x21, int8_t x22, int8_t x23,
int8_t x24, int8_t x25, int8_t x26, int8_t x27,
int8_t x28, int8_t x29, int8_t x30, int8_t x31)
{
sb[0 ] = x0 ; sb[1 ] = x1 ; sb[2 ] = x2 ; sb[3 ] = x3 ;
sb[4 ] = x4 ; sb[5 ] = x5 ; sb[6 ] = x6 ; sb[7 ] = x7 ;
sb[8 ] = x8 ; sb[9 ] = x9 ; sb[10] = x10; sb[11] = x11;
sb[12] = x12; sb[13] = x13; sb[14] = x14; sb[15] = x15;
sb[16] = x16; sb[17] = x17; sb[18] = x18; sb[19] = x19;
sb[20] = x20; sb[21] = x21; sb[22] = x22; sb[23] = x23;
sb[24] = x24; sb[25] = x25; sb[26] = x26; sb[27] = x27;
sb[28] = x28; sb[29] = x29; sb[30] = x30; sb[31] = x31;
}
//! Set all thirty two 8-bit signed integers.
ASMJIT_INLINE void setSb(
int8_t x0)
{
setUb(static_cast<uint8_t>(x0));
}
//! Set all thirty two 8-bit unsigned integers.
ASMJIT_INLINE void setUb(
uint8_t x0 , uint8_t x1 , uint8_t x2 , uint8_t x3 ,
uint8_t x4 , uint8_t x5 , uint8_t x6 , uint8_t x7 ,
uint8_t x8 , uint8_t x9 , uint8_t x10, uint8_t x11,
uint8_t x12, uint8_t x13, uint8_t x14, uint8_t x15,
uint8_t x16, uint8_t x17, uint8_t x18, uint8_t x19,
uint8_t x20, uint8_t x21, uint8_t x22, uint8_t x23,
uint8_t x24, uint8_t x25, uint8_t x26, uint8_t x27,
uint8_t x28, uint8_t x29, uint8_t x30, uint8_t x31)
{
ub[0 ] = x0 ; ub[1 ] = x1 ; ub[2 ] = x2 ; ub[3 ] = x3 ;
ub[4 ] = x4 ; ub[5 ] = x5 ; ub[6 ] = x6 ; ub[7 ] = x7 ;
ub[8 ] = x8 ; ub[9 ] = x9 ; ub[10] = x10; ub[11] = x11;
ub[12] = x12; ub[13] = x13; ub[14] = x14; ub[15] = x15;
ub[16] = x16; ub[17] = x17; ub[18] = x18; ub[19] = x19;
ub[20] = x20; ub[21] = x21; ub[22] = x22; ub[23] = x23;
ub[24] = x24; ub[25] = x25; ub[26] = x26; ub[27] = x27;
ub[28] = x28; ub[29] = x29; ub[30] = x30; ub[31] = x31;
}
//! Set all thirty two 8-bit unsigned integers.
ASMJIT_INLINE void setUb(
uint8_t x0)
{
if (kArchHost64Bit) {
uint64_t t = static_cast<uint64_t>(x0)* ASMJIT_UINT64_C(0x0101010101010101);
uq[0] = t;
uq[1] = t;
uq[2] = t;
uq[3] = t;
}
else {
uint32_t t = static_cast<uint32_t>(x0)* static_cast<uint32_t>(0x01010101U);
ud[0] = t;
ud[1] = t;
ud[2] = t;
ud[3] = t;
ud[4] = t;
ud[5] = t;
ud[6] = t;
ud[7] = t;
}
}
//! Set all sixteen 16-bit signed integers.
ASMJIT_INLINE void setSw(
int16_t x0, int16_t x1, int16_t x2, int16_t x3, int16_t x4, int16_t x5, int16_t x6, int16_t x7,
int16_t x8, int16_t x9, int16_t x10, int16_t x11, int16_t x12, int16_t x13, int16_t x14, int16_t x15)
{
sw[0 ] = x0 ; sw[1 ] = x1 ; sw[2 ] = x2 ; sw[3 ] = x3 ;
sw[4 ] = x4 ; sw[5 ] = x5 ; sw[6 ] = x6 ; sw[7 ] = x7 ;
sw[8 ] = x8 ; sw[9 ] = x9 ; sw[10] = x10; sw[11] = x11;
sw[12] = x12; sw[13] = x13; sw[14] = x14; sw[15] = x15;
}
//! Set all sixteen 16-bit signed integers.
ASMJIT_INLINE void setSw(
int16_t x0)
{
setUw(static_cast<uint16_t>(x0));
}
//! Set all sixteen 16-bit unsigned integers.
ASMJIT_INLINE void setUw(
uint16_t x0, uint16_t x1, uint16_t x2 , uint16_t x3 , uint16_t x4 , uint16_t x5 , uint16_t x6 , uint16_t x7 ,
uint16_t x8, uint16_t x9, uint16_t x10, uint16_t x11, uint16_t x12, uint16_t x13, uint16_t x14, uint16_t x15)
{
uw[0 ] = x0 ; uw[1 ] = x1 ; uw[2 ] = x2 ; uw[3 ] = x3 ;
uw[4 ] = x4 ; uw[5 ] = x5 ; uw[6 ] = x6 ; uw[7 ] = x7 ;
uw[8 ] = x8 ; uw[9 ] = x9 ; uw[10] = x10; uw[11] = x11;
uw[12] = x12; uw[13] = x13; uw[14] = x14; uw[15] = x15;
}
//! Set all eight 16-bit unsigned integers.
ASMJIT_INLINE void setUw(
uint16_t x0)
{
if (kArchHost64Bit) {
uint64_t t = static_cast<uint64_t>(x0)* ASMJIT_UINT64_C(0x0001000100010001);
uq[0] = t;
uq[1] = t;
uq[2] = t;
uq[3] = t;
}
else {
uint32_t t = static_cast<uint32_t>(x0)* static_cast<uint32_t>(0x00010001U);
ud[0] = t;
ud[1] = t;
ud[2] = t;
ud[3] = t;
ud[4] = t;
ud[5] = t;
ud[6] = t;
ud[7] = t;
}
}
//! Set all eight 32-bit signed integers.
ASMJIT_INLINE void setSd(
int32_t x0, int32_t x1, int32_t x2, int32_t x3,
int32_t x4, int32_t x5, int32_t x6, int32_t x7)
{
sd[0] = x0; sd[1] = x1; sd[2] = x2; sd[3] = x3;
sd[4] = x4; sd[5] = x5; sd[6] = x6; sd[7] = x7;
}
//! Set all eight 32-bit signed integers.
ASMJIT_INLINE void setSd(
int32_t x0)
{
setUd(static_cast<uint32_t>(x0));
}
//! Set all eight 32-bit unsigned integers.
ASMJIT_INLINE void setUd(
uint32_t x0, uint32_t x1, uint32_t x2, uint32_t x3,
uint32_t x4, uint32_t x5, uint32_t x6, uint32_t x7)
{
ud[0] = x0; ud[1] = x1; ud[2] = x2; ud[3] = x3;
ud[4] = x4; ud[5] = x5; ud[6] = x6; ud[7] = x7;
}
//! Set all eight 32-bit unsigned integers.
ASMJIT_INLINE void setUd(
uint32_t x0)
{
if (kArchHost64Bit) {
uint64_t t = (static_cast<uint64_t>(x0) << 32) + x0;
uq[0] = t;
uq[1] = t;
uq[2] = t;
uq[3] = t;
}
else {
ud[0] = x0;
ud[1] = x0;
ud[2] = x0;
ud[3] = x0;
ud[4] = x0;
ud[5] = x0;
ud[6] = x0;
ud[7] = x0;
}
}
//! Set all four 64-bit signed integers.
ASMJIT_INLINE void setSq(
int64_t x0, int64_t x1, int64_t x2, int64_t x3)
{
sq[0] = x0; sq[1] = x1; sq[2] = x2; sq[3] = x3;
}
//! Set all four 64-bit signed integers.
ASMJIT_INLINE void setSq(
int64_t x0)
{
sq[0] = x0; sq[1] = x0; sq[2] = x0; sq[3] = x0;
}
//! Set all four 64-bit unsigned integers.
ASMJIT_INLINE void setUq(
uint64_t x0, uint64_t x1, uint64_t x2, uint64_t x3)
{
uq[0] = x0; uq[1] = x1; uq[2] = x2; uq[3] = x3;
}
//! Set all four 64-bit unsigned integers.
ASMJIT_INLINE void setUq(
uint64_t x0)
{
uq[0] = x0; uq[1] = x0; uq[2] = x0; uq[3] = x0;
}
//! Set all eight SP-FP floats.
ASMJIT_INLINE void setSf(
float x0, float x1, float x2, float x3,
float x4, float x5, float x6, float x7)
{
sf[0] = x0; sf[1] = x1; sf[2] = x2; sf[3] = x3;
sf[4] = x4; sf[5] = x5; sf[6] = x6; sf[7] = x7;
}
//! Set all eight SP-FP floats.
ASMJIT_INLINE void setSf(
float x0)
{
sf[0] = x0; sf[1] = x0; sf[2] = x0; sf[3] = x0;
sf[4] = x0; sf[5] = x0; sf[6] = x0; sf[7] = x0;
}
//! Set all four DP-FP floats.
ASMJIT_INLINE void setDf(
double x0, double x1, double x2, double x3)
{
df[0] = x0; df[1] = x1; df[2] = x2; df[3] = x3;
}
//! Set all four DP-FP floats.
ASMJIT_INLINE void setDf(
double x0)
{
df[0] = x0; df[1] = x0; df[2] = x0; df[3] = x0;
}
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
//! Array of thirty two 8-bit signed integers.
int8_t sb[32];
//! Array of thirty two 8-bit unsigned integers.
uint8_t ub[32];
//! Array of sixteen 16-bit signed integers.
int16_t sw[16];
//! Array of sixteen 16-bit unsigned integers.
uint16_t uw[16];
//! Array of eight 32-bit signed integers.
int32_t sd[8];
//! Array of eight 32-bit unsigned integers.
uint32_t ud[8];
//! Array of four 64-bit signed integers.
int64_t sq[4];
//! Array of four 64-bit unsigned integers.
uint64_t uq[4];
//! Array of eight 32-bit single precision floating points.
float sf[8];
//! Array of four 64-bit double precision floating points.
double df[4];
};
//! \}
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
// [Guard]
#endif // _ASMJIT_BASE_VECTYPES_H
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Export]
#define ASMJIT_EXPORTS
// [Dependencies - AsmJit]
#include "../base/error.h"
#include "../base/globals.h"
#include "../base/intutil.h"
#include "../base/lock.h"
#include "../base/vmem.h"
// [Dependencies - Posix]
#if defined(ASMJIT_OS_POSIX)
# include <sys/types.h>
# include <sys/mman.h>
# include <unistd.h>
#endif // ASMJIT_OS_POSIX
// [Api-Begin]
#include "../apibegin.h"
// This file contains implementation of virtual memory management for AsmJit
// library. The initial concept is to keep this implementation simple but
// efficient. There are several goals I decided to write implementation myself.
//
// Goals:
//
// - Granularity of allocated blocks is different than granularity for a typical
// C malloc. It is at least 64-bytes so Assembler/Compiler can guarantee the
// alignment required. Alignment requirements can grow in the future, but at
// the moment 64 bytes is safe (we may jump to 128 bytes if necessary or make
// it configurable).
//
// - Keep memory manager information outside of the allocated virtual memory
// pages, because these pages allow executing of machine code and there should
// not be data required to keep track of these blocks. Another reason is that
// some environments (i.e. iOS) allow to generate and run JIT code, but this
// code has to be set to [Executable, but not Writable].
//
// - Keep implementation simple and easy to follow.
//
// Implementation is based on bit arrays and binary trees. Bit arrays contain
// information related to allocated and unused blocks of memory. The size of
// a block is described by `MemNode::density`. Count of blocks is stored in
// `MemNode::blocks`. For example if density is 64 and count of blocks is 20,
// memory node contains 64*20 bytes of memory and smallest possible allocation
// (and also alignment) is 64 bytes. So density is also related to memory
// alignment. Binary trees (RB) are used to enable fast lookup into all addresses
// allocated by memory manager instance. This is used mainly by `VMemPrivate::release()`.
//
// Bit array looks like this (empty = unused, X = used) - Size of block 64:
//
// -------------------------------------------------------------------------
// | |X|X| | | | | |X|X|X|X|X|X| | | | | | | | | | | | |X| | | | |X|X|X| | |
// -------------------------------------------------------------------------
// (Maximum continuous block)
//
// These bits show that there are 12 allocated blocks (X) of 64 bytes, so total
// size allocated is 768 bytes. Maximum count of continuous memory is 12 * 64.
namespace asmjit {
// ============================================================================
// [asmjit::VMemUtil - Windows]
// ============================================================================
// Windows specific implementation using `VirtualAllocEx` and `VirtualFree`.
#if defined(ASMJIT_OS_WINDOWS)
struct VMemLocal {
// AsmJit allows to pass a `NULL` handle to `VMemUtil`. This function is just
// a convenient way to convert such handle to the current process one.
ASMJIT_INLINE HANDLE getSafeProcessHandle(HANDLE hParam) const {
return hParam != NULL ? hParam : hProcess;
}
size_t pageSize;
size_t pageGranularity;
HANDLE hProcess;
};
static VMemLocal vMemLocal;
static const VMemLocal& vMemGet() {
VMemLocal& vMem = vMemLocal;
if (!vMem.hProcess) {
SYSTEM_INFO info;
::GetSystemInfo(&info);
vMem.pageSize = IntUtil::alignToPowerOf2<uint32_t>(info.dwPageSize);
vMem.pageGranularity = info.dwAllocationGranularity;
vMem.hProcess = ::GetCurrentProcess();
}
return vMem;
};
size_t VMemUtil::getPageSize() {
const VMemLocal& vMem = vMemGet();
return vMem.pageSize;
}
size_t VMemUtil::getPageGranularity() {
const VMemLocal& vMem = vMemGet();
return vMem.pageGranularity;
}
void* VMemUtil::alloc(size_t length, size_t* allocated, uint32_t flags) {
return allocProcessMemory(static_cast<HANDLE>(0), length, allocated, flags);
}
void* VMemUtil::allocProcessMemory(HANDLE hProcess, size_t length, size_t* allocated, uint32_t flags) {
if (length == 0)
return NULL;
const VMemLocal& vMem = vMemGet();
hProcess = vMem.getSafeProcessHandle(hProcess);
// VirtualAlloc rounds allocated size to a page size automatically.
size_t mSize = IntUtil::alignTo(length, vMem.pageSize);
// Windows XP SP2 / Vista allow Data Excution Prevention (DEP).
DWORD protectFlags = 0;
if (flags & kVMemFlagExecutable)
protectFlags |= (flags & kVMemFlagWritable) ? PAGE_EXECUTE_READWRITE : PAGE_EXECUTE_READ;
else
protectFlags |= (flags & kVMemFlagWritable) ? PAGE_READWRITE : PAGE_READONLY;
LPVOID mBase = ::VirtualAllocEx(hProcess, NULL, mSize, MEM_COMMIT | MEM_RESERVE, protectFlags);
if (mBase == NULL)
return NULL;
ASMJIT_ASSERT(IntUtil::isAligned<size_t>(
reinterpret_cast<size_t>(mBase), vMem.pageSize));
if (allocated != NULL)
*allocated = mSize;
return mBase;
}
Error VMemUtil::release(void* addr, size_t length) {
return releaseProcessMemory(static_cast<HANDLE>(0), addr, length);
}
Error VMemUtil::releaseProcessMemory(HANDLE hProcess, void* addr, size_t /* length */) {
hProcess = vMemGet().getSafeProcessHandle(hProcess);
if (!::VirtualFreeEx(hProcess, addr, 0, MEM_RELEASE))
return kErrorInvalidState;
return kErrorOk;
}
#endif // ASMJIT_OS_WINDOWS
// ============================================================================
// [asmjit::VMemUtil - Posix]
// ============================================================================
// Posix specific implementation using `mmap` and `munmap`.
#if defined(ASMJIT_OS_POSIX)
// MacOS uses MAP_ANON instead of MAP_ANONYMOUS.
#if !defined(MAP_ANONYMOUS)
# define MAP_ANONYMOUS MAP_ANON
#endif // MAP_ANONYMOUS
struct VMemLocal {
size_t pageSize;
size_t pageGranularity;
};
static VMemLocal vMemLocal;
static const VMemLocal& vMemGet() {
VMemLocal& vMem = vMemLocal;
if (!vMem.pageSize) {
size_t pageSize = ::getpagesize();
vMem.pageSize = pageSize;
vMem.pageGranularity = IntUtil::iMax<size_t>(pageSize, 65536);
}
return vMem;
};
size_t VMemUtil::getPageSize() {
const VMemLocal& vMem = vMemGet();
return vMem.pageSize;
}
size_t VMemUtil::getPageGranularity() {
const VMemLocal& vMem = vMemGet();
return vMem.pageGranularity;
}
void* VMemUtil::alloc(size_t length, size_t* allocated, uint32_t flags) {
const VMemLocal& vMem = vMemGet();
size_t msize = IntUtil::alignTo<size_t>(length, vMem.pageSize);
int protection = PROT_READ;
if (flags & kVMemFlagWritable ) protection |= PROT_WRITE;
if (flags & kVMemFlagExecutable) protection |= PROT_EXEC;
void* mbase = ::mmap(NULL, msize, protection, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (mbase == MAP_FAILED)
return NULL;
if (allocated != NULL)
*allocated = msize;
return mbase;
}
Error VMemUtil::release(void* addr, size_t length) {
if (::munmap(addr, length) != 0)
return kErrorInvalidState;
return kErrorOk;
}
#endif // ASMJIT_OS_POSIX
// ============================================================================
// [asmjit::VMemMgr - BitOps]
// ============================================================================
#define M_DIV(x, y) ((x) / (y))
#define M_MOD(x, y) ((x) % (y))
//! \internal
enum {
kBitsPerEntity = (sizeof(size_t) * 8)
};
//! \internal
//!
//! Set `len` bits in `buf` starting at `index` bit index.
static void _SetBits(size_t* buf, size_t index, size_t len) {
if (len == 0)
return;
size_t i = index / kBitsPerEntity; // size_t[]
size_t j = index % kBitsPerEntity; // size_t[][] bit index
// How many bytes process in the first group.
size_t c = kBitsPerEntity - j;
if (c > len)
c = len;
// Offset.
buf += i;
*buf++ |= ((~(size_t)0) >> (kBitsPerEntity - c)) << j;
len -= c;
while (len >= kBitsPerEntity) {
*buf++ = ~(size_t)0;
len -= kBitsPerEntity;
}
if (len)
*buf |= ((~(size_t)0) >> (kBitsPerEntity - len));
}
// ============================================================================
// [asmjit::VMemMgr::TypeDefs]
// ============================================================================
typedef VMemMgr::RbNode RbNode;
typedef VMemMgr::MemNode MemNode;
typedef VMemMgr::PermanentNode PermanentNode;
// ============================================================================
// [asmjit::VMemMgr::RbNode]
// ============================================================================
//! \internal
//!
//! Base red-black tree node.
struct VMemMgr::RbNode {
// Implementation is based on article by Julienne Walker (Public Domain),
// including C code and original comments. Thanks for the excellent article.
// Left[0] and right[1] nodes.
RbNode* node[2];
// Virtual memory address.
uint8_t* mem;
// Whether the node is RED.
uint32_t red;
};
//! \internal
//!
//! Get whether the node is red (NULL or node with red flag).
static ASMJIT_INLINE bool rbIsRed(RbNode* node) {
return node != NULL && node->red;
}
//! \internal
//!
//! Check whether the RB tree is valid.
static int rbAssert(RbNode* root) {
if (root == NULL)
return 1;
RbNode* ln = root->node[0];
RbNode* rn = root->node[1];
// Red violation.
ASMJIT_ASSERT( !(rbIsRed(root) && (rbIsRed(ln) || rbIsRed(rn))) );
int lh = rbAssert(ln);
int rh = rbAssert(rn);
// Invalid btree.
ASMJIT_ASSERT(ln == NULL || ln->mem < root->mem);
ASMJIT_ASSERT(rn == NULL || rn->mem > root->mem);
// Black violation.
ASMJIT_ASSERT( !(lh != 0 && rh != 0 && lh != rh) );
// Only count black links.
if (lh != 0 && rh != 0)
return rbIsRed(root) ? lh : lh + 1;
else
return 0;
}
//! \internal
//!
//! Single rotation.
static ASMJIT_INLINE RbNode* rbRotateSingle(RbNode* root, int dir) {
RbNode* save = root->node[!dir];
root->node[!dir] = save->node[dir];
save->node[dir] = root;
root->red = 1;
save->red = 0;
return save;
}
//! \internal
//!
//! Double rotation.
static ASMJIT_INLINE RbNode* rbRotateDouble(RbNode* root, int dir) {
root->node[!dir] = rbRotateSingle(root->node[!dir], !dir);
return rbRotateSingle(root, dir);
}
// ============================================================================
// [asmjit::VMemMgr::MemNode]
// ============================================================================
struct VMemMgr::MemNode : public RbNode {
// --------------------------------------------------------------------------
// [Helpers]
// --------------------------------------------------------------------------
// Get available space.
ASMJIT_INLINE size_t getAvailable() const {
return size - used;
}
ASMJIT_INLINE void fillData(MemNode* other) {
mem = other->mem;
size = other->size;
used = other->used;
blocks = other->blocks;
density = other->density;
largestBlock = other->largestBlock;
baUsed = other->baUsed;
baCont = other->baCont;
}
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
MemNode* prev; // Prev node in list.
MemNode* next; // Next node in list.
size_t size; // How many bytes contain this node.
size_t used; // How many bytes are used in this node.
size_t blocks; // How many blocks are here.
size_t density; // Minimum count of allocated bytes in this node (also alignment).
size_t largestBlock; // Contains largest block that can be allocated.
size_t* baUsed; // Contains bits about used blocks (0 = unused, 1 = used).
size_t* baCont; // Contains bits about continuous blocks (0 = stop , 1 = continue).
};
// ============================================================================
// [asmjit::VMemMgr::PermanentNode]
// ============================================================================
//! \internal
//!
//! Permanent node.
struct VMemMgr::PermanentNode {
// --------------------------------------------------------------------------
// [Helpers]
// --------------------------------------------------------------------------
//! Get available space.
ASMJIT_INLINE size_t getAvailable() const {
return size - used;
}
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
PermanentNode* prev; // Pointer to prev chunk or NULL.
uint8_t* mem; // Base pointer (virtual memory address).
size_t size; // Count of bytes allocated.
size_t used; // Count of bytes used.
};
// ============================================================================
// [asmjit::VMemMgr - Private]
// ============================================================================
//! \internal
//!
//! Helper to avoid `#ifdef`s in the code.
ASMJIT_INLINE uint8_t* vMemMgrAllocVMem(VMemMgr* self, size_t size, size_t* vSize) {
uint32_t flags = kVMemFlagWritable | kVMemFlagExecutable;
#if !defined(ASMJIT_OS_WINDOWS)
return static_cast<uint8_t*>(VMemUtil::alloc(size, vSize, flags));
#else
return static_cast<uint8_t*>(VMemUtil::allocProcessMemory(self->_hProcess, size, vSize, flags));
#endif
}
//! \internal
//!
//! Helper to avoid `#ifdef`s in the code.
ASMJIT_INLINE Error vMemMgrReleaseVMem(VMemMgr* self, void* p, size_t vSize) {
#if !defined(ASMJIT_OS_WINDOWS)
return VMemUtil::release(p, vSize);
#else
return VMemUtil::releaseProcessMemory(self->_hProcess, p, vSize);
#endif
}
//! \internal
//!
//! Check whether the Red-Black tree is valid.
static bool vMemMgrCheckTree(VMemMgr* self) {
return rbAssert(self->_root) > 0;
}
//! \internal
//!
//! Alloc virtual memory including a heap memory needed for `MemNode` data.
//!
//! Returns set-up `MemNode*` or NULL if allocation failed.
static MemNode* vMemMgrCreateNode(VMemMgr* self, size_t size, size_t density) {
size_t vSize;
uint8_t* vmem = vMemMgrAllocVMem(self, size, &vSize);
// Out of memory.
if (vmem == NULL)
return NULL;
size_t blocks = (vSize / density);
size_t bsize = (((blocks + 7) >> 3) + sizeof(size_t) - 1) & ~(size_t)(sizeof(size_t) - 1);
MemNode* node = static_cast<MemNode*>(ASMJIT_ALLOC(sizeof(MemNode)));
uint8_t* data = static_cast<uint8_t*>(ASMJIT_ALLOC(bsize * 2));
// Out of memory.
if (node == NULL || data == NULL) {
vMemMgrReleaseVMem(self, vmem, vSize);
if (node) ASMJIT_FREE(node);
if (data) ASMJIT_FREE(data);
return NULL;
}
// Initialize RbNode data.
node->node[0] = NULL;
node->node[1] = NULL;
node->mem = vmem;
node->red = 1;
// Initialize MemNode data.
node->prev = NULL;
node->next = NULL;
node->size = vSize;
node->used = 0;
node->blocks = blocks;
node->density = density;
node->largestBlock = vSize;
::memset(data, 0, bsize * 2);
node->baUsed = reinterpret_cast<size_t*>(data);
node->baCont = reinterpret_cast<size_t*>(data + bsize);
return node;
}
static void vMemMgrInsertNode(VMemMgr* self, MemNode* node) {
if (self->_root == NULL) {
// Empty tree case.
self->_root = node;
}
else {
// False tree root.
RbNode head = { 0 };
// Grandparent & parent.
RbNode* g = NULL;
RbNode* t = &head;
// Iterator & parent.
RbNode* p = NULL;
RbNode* q = t->node[1] = self->_root;
int dir = 0, last;
// Search down the tree.
for (;;) {
if (q == NULL) {
// Insert new node at the bottom.
q = node;
p->node[dir] = node;
}
else if (rbIsRed(q->node[0]) && rbIsRed(q->node[1])) {
// Color flip.
q->red = 1;
q->node[0]->red = 0;
q->node[1]->red = 0;
}
// Fix red violation.
if (rbIsRed(q) && rbIsRed(p)) {
int dir2 = t->node[1] == g;
t->node[dir2] = q == p->node[last] ? rbRotateSingle(g, !last) : rbRotateDouble(g, !last);
}
// Stop if found.
if (q == node)
break;
last = dir;
dir = q->mem < node->mem;
// Update helpers.
if (g != NULL)
t = g;
g = p;
p = q;
q = q->node[dir];
}
// Update root.
self->_root = static_cast<MemNode*>(head.node[1]);
}
// Make root black.
self->_root->red = 0;
// Link with others.
node->prev = self->_last;
if (self->_first == NULL) {
self->_first = node;
self->_last = node;
self->_optimal = node;
}
else {
node->prev = self->_last;
self->_last->next = node;
self->_last = node;
}
}
//! \internal
//!
//! Remove node from Red-Black tree.
//!
//! Returns node that should be freed, but it doesn't have to be necessarily
//! the `node` passed.
static MemNode* vMemMgrRemoveNode(VMemMgr* self, MemNode* node) {
// False tree root.
RbNode head = { 0 };
// Helpers.
RbNode* q = &head;
RbNode* p = NULL;
RbNode* g = NULL;
// Found item.
RbNode* f = NULL;
int dir = 1;
// Set up.
q->node[1] = self->_root;
// Search and push a red down.
while (q->node[dir] != NULL) {
int last = dir;
// Update helpers.
g = p;
p = q;
q = q->node[dir];
dir = q->mem < node->mem;
// Save found node.
if (q == node)
f = q;
// Push the red node down.
if (!rbIsRed(q) && !rbIsRed(q->node[dir])) {
if (rbIsRed(q->node[!dir])) {
p = p->node[last] = rbRotateSingle(q, dir);
}
else if (!rbIsRed(q->node[!dir])) {
RbNode* s = p->node[!last];
if (s != NULL) {
if (!rbIsRed(s->node[!last]) && !rbIsRed(s->node[last])) {
// Color flip.
p->red = 0;
s->red = 1;
q->red = 1;
}
else {
int dir2 = g->node[1] == p;
if (rbIsRed(s->node[last]))
g->node[dir2] = rbRotateDouble(p, last);
else if (rbIsRed(s->node[!last]))
g->node[dir2] = rbRotateSingle(p, last);
// Ensure correct coloring.
q->red = g->node[dir2]->red = 1;
g->node[dir2]->node[0]->red = 0;
g->node[dir2]->node[1]->red = 0;
}
}
}
}
}
// Replace and remove.
ASMJIT_ASSERT(f != NULL);
ASMJIT_ASSERT(f != &head);
ASMJIT_ASSERT(q != &head);
if (f != q) {
ASMJIT_ASSERT(f != &head);
static_cast<MemNode*>(f)->fillData(static_cast<MemNode*>(q));
}
p->node[p->node[1] == q] = q->node[q->node[0] == NULL];
// Update root and make it black.
self->_root = static_cast<MemNode*>(head.node[1]);
if (self->_root != NULL)
self->_root->red = 0;
// Unlink.
MemNode* next = static_cast<MemNode*>(q)->next;
MemNode* prev = static_cast<MemNode*>(q)->prev;
if (prev)
prev->next = next;
else
self->_first = next;
if (next)
next->prev = prev;
else
self->_last = prev;
if (self->_optimal == q)
self->_optimal = prev ? prev : next;
return static_cast<MemNode*>(q);
}
static MemNode* vMemMgrFindNodeByPtr(VMemMgr* self, uint8_t* mem) {
MemNode* node = self->_root;
while (node != NULL) {
uint8_t* nodeMem = node->mem;
// Go left.
if (mem < nodeMem) {
node = static_cast<MemNode*>(node->node[0]);
continue;
}
// Go right.
uint8_t* nodeEnd = nodeMem + node->size;
if (mem >= nodeEnd) {
node = static_cast<MemNode*>(node->node[1]);
continue;
}
// Match.
break;
}
return node;
}
static void* vMemMgrAllocPermanent(VMemMgr* self, size_t vSize) {
static const size_t permanentAlignment = 32;
static const size_t permanentNodeSize = 32768;
vSize = IntUtil::alignTo<size_t>(vSize, permanentAlignment);
AutoLock locked(self->_lock);
PermanentNode* node = self->_permanent;
// Try to find space in allocated chunks.
while (node && vSize > node->getAvailable())
node = node->prev;
// Or allocate new node.
if (node == NULL) {
size_t nodeSize = permanentNodeSize;
if (nodeSize < vSize)
nodeSize = vSize;
node = static_cast<PermanentNode*>(ASMJIT_ALLOC(sizeof(PermanentNode)));
// Out of memory.
if (node == NULL)
return NULL;
node->mem = vMemMgrAllocVMem(self, nodeSize, &node->size);
// Out of memory.
if (node->mem == NULL) {
ASMJIT_FREE(node);
return NULL;
}
node->used = 0;
node->prev = self->_permanent;
self->_permanent = node;
}
// Finally, copy function code to our space we reserved for.
uint8_t* result = node->mem + node->used;
// Update Statistics.
node->used += vSize;
self->_usedBytes += vSize;
// Code can be null to only reserve space for code.
return static_cast<void*>(result);
}
static void* vMemMgrAllocFreeable(VMemMgr* self, size_t vSize) {
// Current index.
size_t i;
// How many we need to be freed.
size_t need;
size_t minVSize;
// Align to 32 bytes by default.
vSize = IntUtil::alignTo<size_t>(vSize, 32);
if (vSize == 0)
return NULL;
AutoLock locked(self->_lock);
MemNode* node = self->_optimal;
minVSize = self->_blockSize;
// Try to find memory block in existing nodes.
while (node) {
// Skip this node?
if ((node->getAvailable() < vSize) || (node->largestBlock < vSize && node->largestBlock != 0)) {
MemNode* next = node->next;
if (node->getAvailable() < minVSize && node == self->_optimal && next)
self->_optimal = next;
node = next;
continue;
}
size_t* up = node->baUsed; // Current ubits address.
size_t ubits; // Current ubits[0] value.
size_t bit; // Current bit mask.
size_t blocks = node->blocks; // Count of blocks in node.
size_t cont = 0; // How many bits are currently freed in find loop.
size_t maxCont = 0; // Largest continuous block (bits count).
size_t j;
need = M_DIV((vSize + node->density - 1), node->density);
i = 0;
// Try to find node that is large enough.
while (i < blocks) {
ubits = *up++;
// Fast skip used blocks.
if (ubits == ~(size_t)0) {
if (cont > maxCont)
maxCont = cont;
cont = 0;
i += kBitsPerEntity;
continue;
}
size_t max = kBitsPerEntity;
if (i + max > blocks)
max = blocks - i;
for (j = 0, bit = 1; j < max; bit <<= 1) {
j++;
if ((ubits & bit) == 0) {
if (++cont == need) {
i += j;
i -= cont;
goto _Found;
}
continue;
}
if (cont > maxCont) maxCont = cont;
cont = 0;
}
i += kBitsPerEntity;
}
// Because we traversed the entire node, we can set largest node size that
// will be used to cache next traversing.
node->largestBlock = maxCont * node->density;
node = node->next;
}
// If we are here, we failed to find existing memory block and we must
// allocate a new one.
{
size_t blockSize = self->_blockSize;
if (blockSize < vSize)
blockSize = vSize;
node = vMemMgrCreateNode(self, blockSize, self->_blockDensity);
if (node == NULL)
return NULL;
// Update binary tree.
vMemMgrInsertNode(self, node);
ASMJIT_ASSERT(vMemMgrCheckTree(self));
// Alloc first node at start.
i = 0;
need = (vSize + node->density - 1) / node->density;
// Update statistics.
self->_allocatedBytes += node->size;
}
_Found:
// Update bits.
_SetBits(node->baUsed, i, need);
_SetBits(node->baCont, i, need - 1);
// Update statistics.
{
size_t u = need * node->density;
node->used += u;
node->largestBlock = 0;
self->_usedBytes += u;
}
// And return pointer to allocated memory.
uint8_t* result = node->mem + i * node->density;
ASMJIT_ASSERT(result >= node->mem && result <= node->mem + node->size - vSize);
return result;
}
//! \internal
//!
//! Reset the whole `VMemMgr` instance, freeing all heap memory allocated an
//! virtual memory allocated unless `keepVirtualMemory` is true (and this is
//! only used when writing data to a remote process).
static void vMemMgrReset(VMemMgr* self, bool keepVirtualMemory) {
MemNode* node = self->_first;
while (node != NULL) {
MemNode* next = node->next;
if (!keepVirtualMemory)
vMemMgrReleaseVMem(self, node->mem, node->size);
ASMJIT_FREE(node->baUsed);
ASMJIT_FREE(node);
node = next;
}
self->_allocatedBytes = 0;
self->_usedBytes = 0;
self->_root = NULL;
self->_first = NULL;
self->_last = NULL;
self->_optimal = NULL;
}
// ============================================================================
// [asmjit::VMemMgr - Construction / Destruction]
// ============================================================================
#if !defined(ASMJIT_OS_WINDOWS)
VMemMgr::VMemMgr()
#else
VMemMgr::VMemMgr(HANDLE hProcess) :
_hProcess(vMemGet().getSafeProcessHandle(hProcess))
#endif // ASMJIT_OS_WINDOWS
{
_blockSize = VMemUtil::getPageGranularity();
_blockDensity = 64;
_allocatedBytes = 0;
_usedBytes = 0;
_root = NULL;
_first = NULL;
_last = NULL;
_optimal = NULL;
_permanent = NULL;
_keepVirtualMemory = false;
}
VMemMgr::~VMemMgr() {
// Freeable memory cleanup - Also frees the virtual memory if configured to.
vMemMgrReset(this, _keepVirtualMemory);
// Permanent memory cleanup - Never frees the virtual memory.
PermanentNode* node = _permanent;
while (node) {
PermanentNode* prev = node->prev;
ASMJIT_FREE(node);
node = prev;
}
}
// ============================================================================
// [asmjit::VMemMgr - Reset]
// ============================================================================
void VMemMgr::reset() {
vMemMgrReset(this, false);
}
// ============================================================================
// [asmjit::VMemMgr - Alloc / Release]
// ============================================================================
void* VMemMgr::alloc(size_t size, uint32_t type) {
if (type == kVMemAllocPermanent)
return vMemMgrAllocPermanent(this, size);
else
return vMemMgrAllocFreeable(this, size);
}
Error VMemMgr::release(void* p) {
if (p == NULL)
return kErrorOk;
AutoLock locked(_lock);
MemNode* node = vMemMgrFindNodeByPtr(this, static_cast<uint8_t*>(p));
if (node == NULL)
return kErrorInvalidArgument;
size_t offset = (size_t)((uint8_t*)p - (uint8_t*)node->mem);
size_t bitpos = M_DIV(offset, node->density);
size_t i = (bitpos / kBitsPerEntity);
size_t* up = node->baUsed + i; // Current ubits address.
size_t* cp = node->baCont + i; // Current cbits address.
size_t ubits = *up; // Current ubits[0] value.
size_t cbits = *cp; // Current cbits[0] value.
size_t bit = (size_t)1 << (bitpos % kBitsPerEntity);
size_t cont = 0;
bool stop;
for (;;) {
stop = (cbits & bit) == 0;
ubits &= ~bit;
cbits &= ~bit;
bit <<= 1;
cont++;
if (stop || bit == 0) {
*up = ubits;
*cp = cbits;
if (stop)
break;
ubits = *++up;
cbits = *++cp;
bit = 1;
}
}
// If the freed block is fully allocated node then it's needed to
// update 'optimal' pointer in memory manager.
if (node->used == node->size) {
MemNode* cur = _optimal;
do {
cur = cur->prev;
if (cur == node) {
_optimal = node;
break;
}
} while (cur);
}
// Statistics.
cont *= node->density;
if (node->largestBlock < cont)
node->largestBlock = cont;
node->used -= cont;
_usedBytes -= cont;
// If page is empty, we can free it.
if (node->used == 0) {
// Free memory associated with node (this memory is not accessed
// anymore so it's safe).
vMemMgrReleaseVMem(this, node->mem, node->size);
ASMJIT_FREE(node->baUsed);
node->baUsed = NULL;
node->baCont = NULL;
// Statistics.
_allocatedBytes -= node->size;
// Remove node. This function can return different node than
// passed into, but data is copied into previous node if needed.
ASMJIT_FREE(vMemMgrRemoveNode(this, node));
ASMJIT_ASSERT(vMemMgrCheckTree(this));
}
return kErrorOk;
}
Error VMemMgr::shrink(void* p, size_t used) {
if (p == NULL)
return kErrorOk;
if (used == 0)
return release(p);
AutoLock locked(_lock);
MemNode* node = vMemMgrFindNodeByPtr(this, (uint8_t*)p);
if (node == NULL)
return kErrorInvalidArgument;
size_t offset = (size_t)((uint8_t*)p - (uint8_t*)node->mem);
size_t bitpos = M_DIV(offset, node->density);
size_t i = (bitpos / kBitsPerEntity);
size_t* up = node->baUsed + i; // Current ubits address.
size_t* cp = node->baCont + i; // Current cbits address.
size_t ubits = *up; // Current ubits[0] value.
size_t cbits = *cp; // Current cbits[0] value.
size_t bit = (size_t)1 << (bitpos % kBitsPerEntity);
size_t cont = 0;
size_t usedBlocks = (used + node->density - 1) / node->density;
bool stop;
// Find the first block we can mark as free.
for (;;) {
stop = (cbits & bit) == 0;
if (stop)
return kErrorOk;
if (++cont == usedBlocks)
break;
bit <<= 1;
if (bit == 0) {
ubits = *++up;
cbits = *++cp;
bit = 1;
}
}
// Free the tail blocks.
cont = ~(size_t)0;
goto _EnterFreeLoop;
for (;;) {
stop = (cbits & bit) == 0;
ubits &= ~bit;
_EnterFreeLoop:
cbits &= ~bit;
bit <<= 1;
cont++;
if (stop || bit == 0) {
*up = ubits;
*cp = cbits;
if (stop)
break;
ubits = *++up;
cbits = *++cp;
bit = 1;
}
}
// Statistics.
cont *= node->density;
if (node->largestBlock < cont)
node->largestBlock = cont;
node->used -= cont;
_usedBytes -= cont;
return kErrorOk;
}
// ============================================================================
// [asmjit::VMem - Test]
// ============================================================================
#if defined(ASMJIT_TEST)
static void VMemTest_fill(void* a, void* b, int i) {
int pattern = rand() % 256;
*(int *)a = i;
*(int *)b = i;
::memset((char*)a + sizeof(int), pattern, i - sizeof(int));
::memset((char*)b + sizeof(int), pattern, i - sizeof(int));
}
static void VMemTest_verify(void* a, void* b) {
int ai = *(int*)a;
int bi = *(int*)b;
EXPECT(ai == bi,
"The length of 'a' (%d) and 'b' (%d) should be same", ai, bi);
EXPECT(::memcmp(a, b, ai) == 0,
"Pattern (%p) doesn't match", a);
}
static void VMemTest_stats(VMemMgr& memmgr) {
INFO("Used : %u", static_cast<unsigned int>(memmgr.getUsedBytes()));
INFO("Allocated: %u", static_cast<unsigned int>(memmgr.getAllocatedBytes()));
}
static void VMemTest_shuffle(void **a, void **b, size_t count) {
for (size_t i = 0; i < count; ++i) {
size_t si = (size_t)rand() % count;
void *ta = a[i];
void *tb = b[i];
a[i] = a[si];
b[i] = b[si];
a[si] = ta;
b[si] = tb;
}
}
UNIT(base_vmem) {
VMemMgr memmgr;
// Should be predictible.
srand(100);
int i;
int kCount = 200000;
INFO("Memory alloc/free test - %d allocations.", static_cast<int>(kCount));
void** a = (void**)ASMJIT_ALLOC(sizeof(void*) * kCount);
void** b = (void**)ASMJIT_ALLOC(sizeof(void*) * kCount);
EXPECT(a != NULL && b != NULL,
"Couldn't allocate %u bytes on heap.", kCount * 2);
INFO("Allocating virtual memory...");
for (i = 0; i < kCount; i++) {
int r = (rand() % 1000) + 4;
a[i] = memmgr.alloc(r);
EXPECT(a[i] != NULL,
"Couldn't allocate %d bytes of virtual memory", r);
::memset(a[i], 0, r);
}
VMemTest_stats(memmgr);
INFO("Freeing virtual memory...");
for (i = 0; i < kCount; i++) {
EXPECT(memmgr.release(a[i]) == kErrorOk,
"Failed to free %p.", b[i]);
}
VMemTest_stats(memmgr);
INFO("Verified alloc/free test - %d allocations.", static_cast<int>(kCount));
for (i = 0; i < kCount; i++) {
int r = (rand() % 1000) + 4;
a[i] = memmgr.alloc(r);
EXPECT(a[i] != NULL,
"Couldn't allocate %d bytes of virtual memory.", r);
b[i] = ASMJIT_ALLOC(r);
EXPECT(b[i] != NULL,
"Couldn't allocate %d bytes on heap.", r);
VMemTest_fill(a[i], b[i], r);
}
VMemTest_stats(memmgr);
INFO("Shuffling...");
VMemTest_shuffle(a, b, kCount);
INFO("Verify and free...");
for (i = 0; i < kCount / 2; i++) {
VMemTest_verify(a[i], b[i]);
EXPECT(memmgr.release(a[i]) == kErrorOk,
"Failed to free %p.", a[i]);
ASMJIT_FREE(b[i]);
}
VMemTest_stats(memmgr);
INFO("Alloc again.");
for (i = 0; i < kCount / 2; i++) {
int r = (rand() % 1000) + 4;
a[i] = memmgr.alloc(r);
EXPECT(a[i] != NULL,
"Couldn't allocate %d bytes of virtual memory.", r);
b[i] = ASMJIT_ALLOC(r);
EXPECT(b[i] != NULL,
"Couldn't allocate %d bytes on heap.");
VMemTest_fill(a[i], b[i], r);
}
VMemTest_stats(memmgr);
INFO("Verify and free...");
for (i = 0; i < kCount; i++) {
VMemTest_verify(a[i], b[i]);
EXPECT(memmgr.release(a[i]) == kErrorOk,
"Failed to free %p.", a[i]);
ASMJIT_FREE(b[i]);
}
VMemTest_stats(memmgr);
ASMJIT_FREE(a);
ASMJIT_FREE(b);
}
#endif // ASMJIT_TEST
} // asmjit namespace
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Guard]
#ifndef _ASMJIT_BASE_VMEM_H
#define _ASMJIT_BASE_VMEM_H
// [Dependencies]
#include "../base/error.h"
#include "../base/lock.h"
// [Api-Begin]
#include "../apibegin.h"
namespace asmjit {
//! \addtogroup asmjit_base_util
//! \{
// ============================================================================
// [asmjit::kVMemAlloc]
// ============================================================================
//! Type of virtual memory allocation, see `VMemMgr::alloc()`.
ASMJIT_ENUM(kVMemAlloc) {
//! Normal memory allocation, has to be freed by `VMemMgr::release()`.
kVMemAllocFreeable = 0,
//! Allocate permanent memory, can't be freed.
kVMemAllocPermanent = 1
};
// ============================================================================
// [asmjit::kVMemFlags]
// ============================================================================
//! Type of virtual memory allocation, see `VMemMgr::alloc()`.
ASMJIT_ENUM(kVMemFlags) {
//! Memory is writable.
kVMemFlagWritable = 0x00000001,
//! Memory is executable.
kVMemFlagExecutable = 0x00000002
};
// ============================================================================
// [asmjit::VMemUtil]
// ============================================================================
//! Virtual memory utilities.
//!
//! Defines functions that provide facility to allocate and free memory that is
//! executable in a platform independent manner. If both the processor and host
//! operating system support data-execution-prevention then the only way how to
//! run machine code is to allocate it to a memory that has marked as executable.
//! VMemUtil is just unified interface to platform dependent APIs.
//!
//! `VirtualAlloc()` function is used on Windows operating system and `mmap()`
//! on POSIX. `VirtualAlloc()` and `mmap()` documentation provide a detailed
//! overview on how to use a platform specific APIs.
struct VMemUtil {
//! Get a size/alignment of a single virtual memory page.
static ASMJIT_API size_t getPageSize();
//! Get a recommended granularity for a single `alloc` call.
static ASMJIT_API size_t getPageGranularity();
//! Allocate virtual memory.
//!
//! Pages are readable/writeable, but they are not guaranteed to be
//! executable unless 'canExecute' is true. Returns the address of
//! allocated memory, or NULL on failure.
static ASMJIT_API void* alloc(size_t length, size_t* allocated, uint32_t flags);
#if defined(ASMJIT_OS_WINDOWS)
//! Allocate virtual memory of `hProcess`.
//!
//! \note This function is Windows specific.
static ASMJIT_API void* allocProcessMemory(HANDLE hProcess, size_t length, size_t* allocated, uint32_t flags);
#endif // ASMJIT_OS_WINDOWS
//! Free memory allocated by `alloc()`.
static ASMJIT_API Error release(void* addr, size_t length);
#if defined(ASMJIT_OS_WINDOWS)
//! Release virtual memory of `hProcess`.
//!
//! \note This function is Windows specific.
static ASMJIT_API Error releaseProcessMemory(HANDLE hProcess, void* addr, size_t length);
#endif // ASMJIT_OS_WINDOWS
};
// ============================================================================
// [asmjit::VMemMgr]
// ============================================================================
//! Reference implementation of memory manager that uses `VMemUtil` to allocate
//! chunks of virtual memory and bit arrays to manage it.
struct VMemMgr {
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
#if !defined(ASMJIT_OS_WINDOWS)
//! Create a `VMemMgr` instance.
ASMJIT_API VMemMgr();
#else
//! Create a `VMemMgr` instance.
//!
//! \note When running on Windows it's possible to specify a `hProcess` to
//! be used for memory allocation. This allows to allocate memory of remote
//! process.
ASMJIT_API VMemMgr(HANDLE hProcess = static_cast<HANDLE>(0));
#endif // ASMJIT_OS_WINDOWS
//! Destroy the `VMemMgr` instance and free all blocks.
ASMJIT_API ~VMemMgr();
// --------------------------------------------------------------------------
// [Reset]
// --------------------------------------------------------------------------
//! Free all allocated memory.
ASMJIT_API void reset();
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
#if defined(ASMJIT_OS_WINDOWS)
//! Get the handle of the process memory manager is bound to.
ASMJIT_INLINE HANDLE getProcessHandle() const {
return _hProcess;
}
#endif // ASMJIT_OS_WINDOWS
//! Get how many bytes are currently allocated.
ASMJIT_INLINE size_t getAllocatedBytes() const {
return _allocatedBytes;
}
//! Get how many bytes are currently used.
ASMJIT_INLINE size_t getUsedBytes() const {
return _usedBytes;
}
//! Get whether to keep allocated memory after the `VMemMgr` is destroyed.
//!
//! \sa \ref setKeepVirtualMemory.
ASMJIT_INLINE bool getKeepVirtualMemory() const {
return _keepVirtualMemory;
}
//! Set whether to keep allocated memory after memory manager is
//! destroyed.
//!
//! This method is usable when patching code of remote process. You need to
//! allocate process memory, store generated assembler into it and patch the
//! method you want to redirect (into your code). This method affects only
//! VMemMgr destructor. After destruction all internal
//! structures are freed, only the process virtual memory remains.
//!
//! \note Memory allocated with kVMemAllocPermanent is always kept.
//!
//! \sa \ref getKeepVirtualMemory.
ASMJIT_INLINE void setKeepVirtualMemory(bool keepVirtualMemory) {
_keepVirtualMemory = keepVirtualMemory;
}
// --------------------------------------------------------------------------
// [Alloc / Release]
// --------------------------------------------------------------------------
//! Allocate a `size` bytes of virtual memory.
//!
//! Note that if you are implementing your own virtual memory manager then you
//! can quitly ignore type of allocation. This is mainly for AsmJit to memory
//! manager that allocated memory will be never freed.
ASMJIT_API void* alloc(size_t size, uint32_t type = kVMemAllocFreeable);
//! Free previously allocated memory at a given `address`.
ASMJIT_API Error release(void* p);
//! Free extra memory allocated with `p`.
ASMJIT_API Error shrink(void* p, size_t used);
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
#if defined(ASMJIT_OS_WINDOWS)
//! Process passed to `VirtualAllocEx` and `VirtualFree`.
HANDLE _hProcess;
#endif // ASMJIT_OS_WINDOWS
//! Lock to enable thread-safe functionality.
Lock _lock;
//! Default block size.
size_t _blockSize;
//! Default block density.
size_t _blockDensity;
// Whether to keep virtual memory after destroy.
bool _keepVirtualMemory;
//! How many bytes are currently allocated.
size_t _allocatedBytes;
//! How many bytes are currently used.
size_t _usedBytes;
//! \internal
//! \{
struct RbNode;
struct MemNode;
struct PermanentNode;
// Memory nodes root.
MemNode* _root;
// Memory nodes list.
MemNode* _first;
MemNode* _last;
MemNode* _optimal;
// Permanent memory.
PermanentNode* _permanent;
//! \}
};
//! \}
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
// [Guard]
#endif // _ASMJIT_BASE_VMEM_H
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Export]
#define ASMJIT_EXPORTS
// [Dependencies - AsmJit]
#include "../base/intutil.h"
#include "../base/zone.h"
// [Dependencies - C]
#include <stdarg.h>
// [Api-Begin]
#include "../apibegin.h"
namespace asmjit {
//! Zero size block used by `Zone` that doesn't have any memory allocated.
static const Zone::Block Zone_zeroBlock = {
NULL, NULL, NULL, NULL, { 0 }
};
// ============================================================================
// [asmjit::Zone - Construction / Destruction]
// ============================================================================
Zone::Zone(size_t blockSize) {
_block = const_cast<Zone::Block*>(&Zone_zeroBlock);
_blockSize = blockSize;
}
Zone::~Zone() {
reset(true);
}
// ============================================================================
// [asmjit::Zone - Reset]
// ============================================================================
void Zone::reset(bool releaseMemory) {
Block* cur = _block;
// Can't be altered.
if (cur == &Zone_zeroBlock)
return;
if (releaseMemory) {
// Since cur can be in the middle of the double-linked list, we have to
// traverse to both directions `prev` and `next` separately.
Block* next = cur->next;
do {
Block* prev = cur->prev;
ASMJIT_FREE(cur);
cur = prev;
} while (cur != NULL);
cur = next;
while (cur != NULL) {
next = cur->next;
ASMJIT_FREE(cur);
cur = next;
}
_block = const_cast<Zone::Block*>(&Zone_zeroBlock);
}
else {
while (cur->prev != NULL)
cur = cur->prev;
cur->pos = cur->data;
_block = cur;
}
}
// ============================================================================
// [asmjit::Zone - Alloc]
// ============================================================================
void* Zone::_alloc(size_t size) {
Block* curBlock = _block;
size_t blockSize = IntUtil::iMax<size_t>(_blockSize, size);
// The `_alloc()` method can only be called if there is not enough space
// in the current block, see `alloc()` implementation for more details.
ASMJIT_ASSERT(curBlock == &Zone_zeroBlock || curBlock->getRemainingSize() < size);
// If the `Zone` has been reset the current block doesn't have to be the
// last one. Check if there is a block that can be used instead of allocating
// a new one. If there is a `next` block it's completely unused, we don't have
// to check for remaining bytes.
Block* next = curBlock->next;
if (next != NULL && next->getBlockSize() >= size) {
next->pos = next->data + size;
_block = next;
return static_cast<void*>(next->data);
}
// Prevent arithmetic overflow.
if (blockSize > ~static_cast<size_t>(0) - sizeof(Block))
return NULL;
Block* newBlock = static_cast<Block*>(ASMJIT_ALLOC(sizeof(Block) - sizeof(void*) + blockSize));
if (newBlock == NULL)
return NULL;
newBlock->pos = newBlock->data + size;
newBlock->end = newBlock->data + blockSize;
newBlock->prev = NULL;
newBlock->next = NULL;
if (curBlock != &Zone_zeroBlock) {
newBlock->prev = curBlock;
curBlock->next = newBlock;
// Does only happen if there is a next block, but the requested memory
// can't fit into it. In this case a new buffer is allocated and inserted
// between the current block and the next one.
if (next != NULL) {
newBlock->next = next;
next->prev = newBlock;
}
}
_block = newBlock;
return static_cast<void*>(newBlock->data);
}
void* Zone::allocZeroed(size_t size) {
void* p = alloc(size);
if (p != NULL)
::memset(p, 0, size);
return p;
}
void* Zone::dup(const void* data, size_t size) {
if (data == NULL)
return NULL;
if (size == 0)
return NULL;
void* m = alloc(size);
if (m == NULL)
return NULL;
::memcpy(m, data, size);
return m;
}
char* Zone::sdup(const char* str) {
if (str == NULL)
return NULL;
size_t len = ::strlen(str);
if (len == 0)
return NULL;
// Include NULL terminator and limit string length.
if (++len > 256)
len = 256;
char* m = static_cast<char*>(alloc(len));
if (m == NULL)
return NULL;
::memcpy(m, str, len);
m[len - 1] = '\0';
return m;
}
char* Zone::sformat(const char* fmt, ...) {
if (fmt == NULL)
return NULL;
char buf[512];
size_t len;
va_list ap;
va_start(ap, fmt);
len = vsnprintf(buf, ASMJIT_ARRAY_SIZE(buf) - 1, fmt, ap);
buf[len++] = 0;
va_end(ap);
return static_cast<char*>(dup(buf, len));
}
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment