diff --git a/.gitignore b/.gitignore index f4c769df4a2..5ef035d17b9 100644 --- a/.gitignore +++ b/.gitignore @@ -25,5 +25,3 @@ /build2 /build_vc2019-64 /build_vc2019-32 -.kdev4 -*.kdev4 \ No newline at end of file diff --git a/libraries/asmjit/CMakeLists.txt b/libraries/asmjit/CMakeLists.txt index 118570c3a51..6b7636ebe98 100644 --- a/libraries/asmjit/CMakeLists.txt +++ b/libraries/asmjit/CMakeLists.txt @@ -1,22 +1,17 @@ cmake_minimum_required(VERSION 2.8.7) + #make_release_only() -project(asmjit CXX) +project(asmjit C) set(ASMJITNAME asmjit) -add_definitions(-DASMJIT_STATIC) -if(CMAKE_VERSION VERSION_LESS "3.1") - if("${CMAKE_CXX_COMPILER_ID}" MATCHES "^(GNU|Clang|AppleClang)$") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") - endif() -else() - set(CMAKE_CXX_STANDARD 11) -endif() +add_definitions(-DASMJIT_BUILD_EMBED) +add_definitions(-DASMJIT_STATIC) if(MSVC) - set(CMAKE_DEBUG_POSTFIX "d") - add_definitions(-D_CRT_NONSTDC_NO_DEPRECATE) + set(CMAKE_DEBUG_POSTFIX "d") + add_definitions(-D_CRT_NONSTDC_NO_DEPRECATE) endif() if(APPLE) @@ -26,127 +21,92 @@ endif() include_directories(${CMAKE_CURRENT_SOURCE_DIR}) -set(ASMJIT_SRCS +set(ASMJIT_PUBLIC_HDRS + asmjit/arm.h asmjit/asmjit.h - - asmjit/core.h - asmjit/core/build.h - asmjit/core/arch.cpp - asmjit/core/arch.h - asmjit/core/assembler.cpp - asmjit/core/assembler.h - asmjit/core/builder.cpp - asmjit/core/builder.h - asmjit/core/callconv.cpp - asmjit/core/callconv.h - asmjit/core/codebufferwriter_p.h - asmjit/core/codeholder.cpp - asmjit/core/codeholder.h - asmjit/core/compiler.cpp - asmjit/core/compiler.h - asmjit/core/constpool.cpp - asmjit/core/constpool.h - asmjit/core/cpuinfo.cpp - asmjit/core/cpuinfo.h - asmjit/core/datatypes.h - asmjit/core/emitter.cpp - asmjit/core/emitter.h - asmjit/core/features.h - asmjit/core/func.cpp - asmjit/core/func.h - asmjit/core/globals.cpp - asmjit/core/globals.h - asmjit/core/inst.cpp - asmjit/core/inst.h - asmjit/core/jitallocator.cpp - asmjit/core/jitallocator.h - asmjit/core/jitruntime.cpp - asmjit/core/jitruntime.h - asmjit/core/logging.cpp - asmjit/core/logging.h - asmjit/core/misc_p.h - asmjit/core/operand.cpp - asmjit/core/operand.h - asmjit/core/osutils.cpp - asmjit/core/osutils.h - asmjit/core/raassignment_p.h - asmjit/core/rabuilders_p.h - asmjit/core/radefs_p.h - asmjit/core/ralocal.cpp - asmjit/core/ralocal_p.h - asmjit/core/rapass.cpp - asmjit/core/rapass_p.h - asmjit/core/rastack.cpp - asmjit/core/rastack_p.h - asmjit/core/string.cpp - asmjit/core/string.h - asmjit/core/support.cpp - asmjit/core/support.h - asmjit/core/target.cpp - asmjit/core/target.h - asmjit/core/type.cpp - asmjit/core/type.h - asmjit/core/virtmem.cpp - asmjit/core/virtmem.h - asmjit/core/zone.cpp - asmjit/core/zone.h - asmjit/core/zonehash.cpp - asmjit/core/zonehash.h - asmjit/core/zonelist.cpp - asmjit/core/zonelist.h - asmjit/core/zonestack.cpp - asmjit/core/zonestack.h - asmjit/core/zonestring.h - asmjit/core/zonetree.cpp - asmjit/core/zonetree.h - asmjit/core/zonevector.cpp - asmjit/core/zonevector.h - - asmjit/x86.h - asmjit/x86/x86assembler.cpp - asmjit/x86/x86assembler.h - asmjit/x86/x86builder.cpp - asmjit/x86/x86builder.h - asmjit/x86/x86callconv.cpp - asmjit/x86/x86callconv_p.h - asmjit/x86/x86compiler.cpp - asmjit/x86/x86compiler.h - asmjit/x86/x86emitter.h - asmjit/x86/x86features.cpp - asmjit/x86/x86features.h - asmjit/x86/x86globals.h - asmjit/x86/x86instapi.cpp - asmjit/x86/x86instapi_p.h - asmjit/x86/x86instdb.cpp - asmjit/x86/x86instdb.h - asmjit/x86/x86instdb_p.h - asmjit/x86/x86internal.cpp - asmjit/x86/x86internal_p.h - asmjit/x86/x86logging.cpp - asmjit/x86/x86logging_p.h - asmjit/x86/x86opcode_p.h - asmjit/x86/x86operand.cpp - asmjit/x86/x86operand.h - asmjit/x86/x86rapass.cpp - asmjit/x86/x86rapass_p.h + asmjit/asmjit_apibegin.h + asmjit/asmjit_apiend.h + asmjit/asmjit_build.h + asmjit/base.h + asmjit/base/arch.h + asmjit/base/assembler.h + asmjit/base/codebuilder.h + asmjit/base/codecompiler.h + asmjit/base/codeemitter.h + asmjit/base/codeholder.h + asmjit/base/constpool.h + asmjit/base/cpuinfo.h + asmjit/base/func.h + asmjit/base/globals.h + asmjit/base/inst.h + asmjit/base/logging.h + asmjit/base/misc_p.h + asmjit/base/operand.h + asmjit/base/osutils.h + asmjit/base/regalloc_p.h + asmjit/base/runtime.h + asmjit/base/simdtypes.h + asmjit/base/string.h + asmjit/base/utils.h + asmjit/base/vmem.h + asmjit/base/zone.h + asmjit/x86.h + asmjit/x86/x86assembler.h + asmjit/x86/x86builder.h + asmjit/x86/x86compiler.h + asmjit/x86/x86emitter.h + asmjit/x86/x86globals.h + asmjit/x86/x86inst.h + asmjit/x86/x86instimpl_p.h + asmjit/x86/x86internal_p.h + asmjit/x86/x86logging_p.h + asmjit/x86/x86misc.h + asmjit/x86/x86operand.h + asmjit/x86/x86regalloc_p.h +) +set(ASMJIT_SRCS + asmjit/base/arch.cpp + asmjit/base/assembler.cpp + asmjit/base/codebuilder.cpp + asmjit/base/codecompiler.cpp + asmjit/base/codeemitter.cpp + asmjit/base/codeholder.cpp + asmjit/base/constpool.cpp + asmjit/base/cpuinfo.cpp + asmjit/base/func.cpp + asmjit/base/globals.cpp + asmjit/base/inst.cpp + asmjit/base/logging.cpp + asmjit/base/operand.cpp + asmjit/base/osutils.cpp + asmjit/base/regalloc.cpp + asmjit/base/runtime.cpp + asmjit/base/string.cpp + asmjit/base/utils.cpp + asmjit/base/vmem.cpp + asmjit/base/zone.cpp + asmjit/x86/x86assembler.cpp + asmjit/x86/x86builder.cpp + asmjit/x86/x86compiler.cpp + asmjit/x86/x86inst.cpp + asmjit/x86/x86instimpl.cpp + asmjit/x86/x86internal.cpp + asmjit/x86/x86logging.cpp + asmjit/x86/x86operand.cpp + asmjit/x86/x86operand_regs.cpp + asmjit/x86/x86regalloc.cpp ) -set(ASMJIT_PUBLIC_HDRS "") -foreach(_src_file ${ASMJIT_SRCS}) - if ("${_src_file}" MATCHES "\\.h$" AND NOT "${_src_file}" MATCHES "_p\\.h$") - list(APPEND ASMJIT_PUBLIC_HDRS ${_src_file}) - endif() -endforeach() +add_library(${ASMJITNAME} STATIC ${ASMJIT_SRCS} ${ASMJIT_PUBLIC_HDRS}) -add_library(${ASMJITNAME} STATIC ${ASMJIT_SRCS}) set_target_properties(${ASMJITNAME} PROPERTIES OUTPUT_NAME asmjit) -if(NOT SKIP_INSTALL_LIBRARIES AND NOT SKIP_INSTALL_ALL) - install(TARGETS ${ASMJITNAME} - RUNTIME DESTINATION bin - ARCHIVE DESTINATION lib - LIBRARY DESTINATION lib) +if(NOT SKIP_INSTALL_LIBRARIES AND NOT SKIP_INSTALL_ALL ) + install(TARGETS ${ASMJITNAME} + RUNTIME DESTINATION bin + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib ) endif() -if(NOT SKIP_INSTALL_HEADERS AND NOT SKIP_INSTALL_ALL) - install(FILES ${ASMJIT_PUBLIC_HDRS} DESTINATION include) +if(NOT SKIP_INSTALL_HEADERS AND NOT SKIP_INSTALL_ALL ) + install(FILES ${ASMJIT_PUBLIC_HDRS} DESTINATION include) endif() diff --git a/libraries/asmjit/asmjit/arm.h b/libraries/asmjit/asmjit/arm.h new file mode 100644 index 00000000000..0a916d9cd99 --- /dev/null +++ b/libraries/asmjit/asmjit/arm.h @@ -0,0 +1,21 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Guard] +#ifndef _ASMJIT_ARM_H +#define _ASMJIT_ARM_H + +// [Dependencies] +#include "./base.h" + +#include "./arm/armassembler.h" +#include "./arm/armbuilder.h" +#include "./arm/armcompiler.h" +#include "./arm/arminst.h" +#include "./arm/armoperand.h" + +// [Guard] +#endif // _ASMJIT_ARM_H diff --git a/libraries/asmjit/asmjit/asmjit.h b/libraries/asmjit/asmjit/asmjit.h index 44281295d2e..ead90f0c257 100644 --- a/libraries/asmjit/asmjit/asmjit.h +++ b/libraries/asmjit/asmjit/asmjit.h @@ -1,41 +1,47 @@ // [AsmJit] -// Machine Code Generation for C++. +// Complete x86/x64 JIT and Remote Assembler for C++. // // [License] // Zlib - See LICENSE.md file in the package. +// [Guard] #ifndef _ASMJIT_ASMJIT_H #define _ASMJIT_ASMJIT_H -//! \mainpage API Reference +// ============================================================================ +// [asmjit_mainpage] +// ============================================================================ + +//! \mainpage //! -//! AsmJit C++ API reference documentation generated by Doxygen. +//! AsmJit - Complete x86/x64 JIT and Remote Assembler for C++. //! //! Introduction provided by the project page at https://github.com/asmjit/asmjit. + +//! \defgroup asmjit_base AsmJit Base API (architecture independent) //! -//! \section main_groups Groups -//! -//! The documentation is split into the following groups: -//! -//! $$DOCS_GROUP_OVERVIEW$$ -//! -//! \section main_other Other Pages +//! \brief Backend Neutral API. + +//! \defgroup asmjit_x86 AsmJit X86/X64 API //! -//! - Class List - List of classes sorted alphabetically -//! - AsmJit Namespace - List of symbols provided by `asmjit` namespace +//! \brief X86/X64 Backend API. -//! \namespace asmjit +//! \defgroup asmjit_arm AsmJit ARM32/ARM64 API //! -//! Root namespace used by AsmJit. +//! \brief ARM32/ARM64 Backend API. -#include "./core.h" +// [Dependencies] +#include "./base.h" -#ifdef ASMJIT_BUILD_X86 - #include "./x86.h" -#endif +// [X86/X64] +#if defined(ASMJIT_BUILD_X86) +#include "./x86.h" +#endif // ASMJIT_BUILD_X86 -#ifdef ASMJIT_BUILD_ARM - #include "./arm.h" -#endif +// [ARM32/ARM64] +#if defined(ASMJIT_BUILD_ARM) +#include "./arm.h" +#endif // ASMJIT_BUILD_ARM +// [Guard] #endif // _ASMJIT_ASMJIT_H diff --git a/libraries/asmjit/asmjit/asmjit_apibegin.h b/libraries/asmjit/asmjit/asmjit_apibegin.h new file mode 100644 index 00000000000..58d16dbaef6 --- /dev/null +++ b/libraries/asmjit/asmjit/asmjit_apibegin.h @@ -0,0 +1,117 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Dependencies] +#if !defined(_ASMJIT_BUILD_H) +# include "./build.h" +#endif // !_ASMJIT_BUILD_H + +// [Guard] +#if !defined(ASMJIT_API_SCOPE) +# define ASMJIT_API_SCOPE +#else +# error "[asmjit] api-scope is already active, previous scope not closed by asmjit_apiend.h?" +#endif // ASMJIT_API_SCOPE + +// ============================================================================ +// [C++ Support] +// ============================================================================ + +// [NoExcept] +#if !ASMJIT_CC_HAS_NOEXCEPT && !defined(noexcept) +# define noexcept ASMJIT_NOEXCEPT +# define ASMJIT_UNDEF_NOEXCEPT +#endif // !ASMJIT_CC_HAS_NOEXCEPT && !noexcept + +// [NullPtr] +#if !ASMJIT_CC_HAS_NULLPTR && !defined(nullptr) +# define nullptr NULL +# define ASMJIT_UNDEF_NULLPTR +#endif // !ASMJIT_CC_HAS_NULLPTR && !nullptr + +// [Override] +#if !ASMJIT_CC_HAS_OVERRIDE && !defined(override) +# define override +# define ASMJIT_UNDEF_OVERRIDE +#endif // !ASMJIT_CC_HAS_OVERRIDE && !override + +// ============================================================================ +// [Compiler Support] +// ============================================================================ + +// [Clang] +#if ASMJIT_CC_CLANG +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wc++11-extensions" +# pragma clang diagnostic ignored "-Wconstant-logical-operand" +# pragma clang diagnostic ignored "-Wunnamed-type-template-args" +#endif // ASMJIT_CC_CLANG + +// [GCC] +#if ASMJIT_CC_GCC +# pragma GCC diagnostic push +#endif // ASMJIT_CC_GCC + +// [MSC] +#if ASMJIT_CC_MSC +# pragma warning(push) +# pragma warning(disable: 4127) // conditional expression is constant +# pragma warning(disable: 4201) // nameless struct/union +# pragma warning(disable: 4244) // '+=' : conversion from 'int' to 'x', possible loss of data +# pragma warning(disable: 4251) // struct needs to have dll-interface to be used by clients of struct ... +# pragma warning(disable: 4275) // non dll-interface struct ... used as base for dll-interface struct +# pragma warning(disable: 4355) // this used in base member initializer list +# pragma warning(disable: 4480) // specifying underlying type for enum +# pragma warning(disable: 4800) // forcing value to bool 'true' or 'false' +# if _MSC_VER < 1900 +# if !defined(vsnprintf) +# define ASMJIT_UNDEF_VSNPRINTF +# define vsnprintf _vsnprintf +# endif // !vsnprintf +# if !defined(snprintf) +# define ASMJIT_UNDEF_SNPRINTF +# define snprintf _snprintf +# endif // !snprintf +# endif +#endif // ASMJIT_CC_MSC + +// ============================================================================ +// [Custom Macros] +// ============================================================================ + +// [ASMJIT_NON...] +#if ASMJIT_CC_HAS_DELETE_FUNCTION +#define ASMJIT_NONCONSTRUCTIBLE(...) \ +private: \ + __VA_ARGS__() = delete; \ + __VA_ARGS__(const __VA_ARGS__& other) = delete; \ + __VA_ARGS__& operator=(const __VA_ARGS__& other) = delete; \ +public: +#define ASMJIT_NONCOPYABLE(...) \ +private: \ + __VA_ARGS__(const __VA_ARGS__& other) = delete; \ + __VA_ARGS__& operator=(const __VA_ARGS__& other) = delete; \ +public: +#else +#define ASMJIT_NONCONSTRUCTIBLE(...) \ +private: \ + inline __VA_ARGS__(); \ + inline __VA_ARGS__(const __VA_ARGS__& other); \ + inline __VA_ARGS__& operator=(const __VA_ARGS__& other); \ +public: +#define ASMJIT_NONCOPYABLE(...) \ +private: \ + inline __VA_ARGS__(const __VA_ARGS__& other); \ + inline __VA_ARGS__& operator=(const __VA_ARGS__& other); \ +public: +#endif // ASMJIT_CC_HAS_DELETE_FUNCTION + +// [ASMJIT_ENUM] +#if defined(_MSC_VER) && _MSC_VER >= 1400 +# define ASMJIT_ENUM(NAME) enum NAME : uint32_t +#else +# define ASMJIT_ENUM(NAME) enum NAME +#endif diff --git a/libraries/asmjit/asmjit/asmjit_apiend.h b/libraries/asmjit/asmjit/asmjit_apiend.h new file mode 100644 index 00000000000..a51630b5a24 --- /dev/null +++ b/libraries/asmjit/asmjit/asmjit_apiend.h @@ -0,0 +1,74 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Guard] +#if defined(ASMJIT_API_SCOPE) +# undef ASMJIT_API_SCOPE +#else +# error "[asmjit] api-scope not active, forgot to include asmjit_apibegin.h?" +#endif // ASMJIT_API_SCOPE + +// ============================================================================ +// [C++ Support] +// ============================================================================ + +// [NoExcept] +#if defined(ASMJIT_UNDEF_NOEXCEPT) +# undef noexcept +# undef ASMJIT_UNDEF_NOEXCEPT +#endif // ASMJIT_UNDEF_NOEXCEPT + +// [NullPtr] +#if defined(ASMJIT_UNDEF_NULLPTR) +# undef nullptr +# undef ASMJIT_UNDEF_NULLPTR +#endif // ASMJIT_UNDEF_NULLPTR + +// [Override] +#if defined(ASMJIT_UNDEF_OVERRIDE) +# undef override +# undef ASMJIT_UNDEF_OVERRIDE +#endif // ASMJIT_UNDEF_OVERRIDE + +// ============================================================================ +// [Compiler Support] +// ============================================================================ + +// [Clang] +#if ASMJIT_CC_CLANG +# pragma clang diagnostic pop +#endif // ASMJIT_CC_CLANG + +// [GCC] +#if ASMJIT_CC_GCC +# pragma GCC diagnostic pop +#endif // ASMJIT_CC_GCC + +// [MSC] +#if ASMJIT_CC_MSC +# pragma warning(pop) +# if _MSC_VER < 1900 +# if defined(ASMJIT_UNDEF_VSNPRINTF) +# undef vsnprintf +# undef ASMJIT_UNDEF_VSNPRINTF +# endif // ASMJIT_UNDEF_VSNPRINTF +# if defined(ASMJIT_UNDEF_SNPRINTF) +# undef snprintf +# undef ASMJIT_UNDEF_SNPRINTF +# endif // ASMJIT_UNDEF_SNPRINTF +# endif +#endif // ASMJIT_CC_MSC + +// ============================================================================ +// [Custom Macros] +// ============================================================================ + +// [ASMJIT_NON...] +#undef ASMJIT_NONCONSTRUCTIBLE +#undef ASMJIT_NONCOPYABLE + +// [ASMJIT_ENUM] +#undef ASMJIT_ENUM diff --git a/libraries/asmjit/asmjit/asmjit_build.h b/libraries/asmjit/asmjit/asmjit_build.h new file mode 100644 index 00000000000..77b151ac3a1 --- /dev/null +++ b/libraries/asmjit/asmjit/asmjit_build.h @@ -0,0 +1,949 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Guard] +#ifndef _ASMJIT_BUILD_H +#define _ASMJIT_BUILD_H + +// ============================================================================ +// [asmjit::Build - Configuration] +// ============================================================================ + +// AsmJit is by default compiled only for a host processor for the purpose of +// JIT code generation. Both Assembler and CodeCompiler emitters are compiled +// by default. Preprocessor macros can be used to change the default behavior. + +// External Config File +// -------------------- +// +// Define in case your configuration is generated in an external file to be +// included. + +#if defined(ASMJIT_CONFIG_FILE) +# include ASMJIT_CONFIG_FILE +#endif // ASMJIT_CONFIG_FILE + +// AsmJit Static Builds and Embedding +// ---------------------------------- +// +// These definitions can be used to enable static library build. Embed is used +// when AsmJit's source code is embedded directly in another project, implies +// static build as well. +// +// #define ASMJIT_EMBED // Asmjit is embedded (implies ASMJIT_STATIC). +// #define ASMJIT_STATIC // Define to enable static-library build. + +// AsmJit Build Modes +// ------------------ +// +// These definitions control the build mode and tracing support. The build mode +// should be auto-detected at compile time, but it's possible to override it in +// case that the auto-detection fails. +// +// Tracing is a feature that is never compiled by default and it's only used to +// debug AsmJit itself. +// +// #define ASMJIT_DEBUG // Define to enable debug-mode. +// #define ASMJIT_RELEASE // Define to enable release-mode. + +// AsmJit Build Backends +// --------------------- +// +// These definitions control which backends to compile. If none of these is +// defined AsmJit will use host architecture by default (for JIT code generation). +// +// #define ASMJIT_BUILD_X86 // Define to enable X86 and X64 code-generation. +// #define ASMJIT_BUILD_ARM // Define to enable ARM32 and ARM64 code-generation. +// #define ASMJIT_BUILD_HOST // Define to enable host instruction set. + +// AsmJit Build Features +// --------------------- +// +// Flags can be defined to disable standard features. These are handy especially +// when building AsmJit statically and some features are not needed or unwanted +// (like CodeCompiler). +// +// AsmJit features are enabled by default. +// #define ASMJIT_DISABLE_COMPILER // Disable CodeCompiler (completely). +// #define ASMJIT_DISABLE_LOGGING // Disable logging and formatting (completely). +// #define ASMJIT_DISABLE_TEXT // Disable everything that contains text +// // representation (instructions, errors, ...). +// #define ASMJIT_DISABLE_VALIDATION // Disable Validation (completely). + +// Prevent compile-time errors caused by misconfiguration. +#if defined(ASMJIT_DISABLE_TEXT) && !defined(ASMJIT_DISABLE_LOGGING) +# error "[asmjit] ASMJIT_DISABLE_TEXT requires ASMJIT_DISABLE_LOGGING to be defined." +#endif // ASMJIT_DISABLE_TEXT && !ASMJIT_DISABLE_LOGGING + +// Detect ASMJIT_DEBUG and ASMJIT_RELEASE if not forced from outside. +#if !defined(ASMJIT_DEBUG) && !defined(ASMJIT_RELEASE) +# if !defined(NDEBUG) +# define ASMJIT_DEBUG +# else +# define ASMJIT_RELEASE +# endif +#endif + +// ASMJIT_EMBED implies ASMJIT_STATIC. +#if defined(ASMJIT_EMBED) && !defined(ASMJIT_STATIC) +# define ASMJIT_STATIC +#endif + +// ============================================================================ +// [asmjit::Build - VERSION] +// ============================================================================ + +// [@VERSION{@] +#define ASMJIT_VERSION_MAJOR 1 +#define ASMJIT_VERSION_MINOR 0 +#define ASMJIT_VERSION_PATCH 0 +#define ASMJIT_VERSION_STRING "1.0.0" +// [@VERSION}@] + +// ============================================================================ +// [asmjit::Build - WIN32] +// ============================================================================ + +// [@WIN32_CRT_NO_DEPRECATE{@] +#if defined(_MSC_VER) && defined(ASMJIT_EXPORTS) +# if !defined(_CRT_SECURE_NO_DEPRECATE) +# define _CRT_SECURE_NO_DEPRECATE +# endif +# if !defined(_CRT_SECURE_NO_WARNINGS) +# define _CRT_SECURE_NO_WARNINGS +# endif +#endif +// [@WIN32_CRT_NO_DEPRECATE}@] + +// [@WIN32_LEAN_AND_MEAN{@] +#if (defined(_WIN32) || defined(_WINDOWS)) && !defined(_WINDOWS_) +# if !defined(WIN32_LEAN_AND_MEAN) +# define WIN32_LEAN_AND_MEAN +# define ASMJIT_UNDEF_WIN32_LEAN_AND_MEAN +# endif +# if !defined(NOMINMAX) +# define NOMINMAX +# define ASMJIT_UNDEF_NOMINMAX +# endif +# include +# if defined(ASMJIT_UNDEF_NOMINMAX) +# undef NOMINMAX +# undef ASMJIT_UNDEF_NOMINMAX +# endif +# if defined(ASMJIT_UNDEF_WIN32_LEAN_AND_MEAN) +# undef WIN32_LEAN_AND_MEAN +# undef ASMJIT_UNDEF_WIN32_LEAN_AND_MEAN +# endif +#endif +// [@WIN32_LEAN_AND_MEAN}@] + +// ============================================================================ +// [asmjit::Build - OS] +// ============================================================================ + +// [@OS{@] +#if defined(_WIN32) || defined(_WINDOWS) +#define ASMJIT_OS_WINDOWS (1) +#else +#define ASMJIT_OS_WINDOWS (0) +#endif + +#if defined(__APPLE__) +# include +# define ASMJIT_OS_MAC (TARGET_OS_MAC) +# define ASMJIT_OS_IOS (TARGET_OS_IPHONE) +#else +# define ASMJIT_OS_MAC (0) +# define ASMJIT_OS_IOS (0) +#endif + +#if defined(__ANDROID__) +# define ASMJIT_OS_ANDROID (1) +#else +# define ASMJIT_OS_ANDROID (0) +#endif + +#if defined(__linux__) || defined(__ANDROID__) +# define ASMJIT_OS_LINUX (1) +#else +# define ASMJIT_OS_LINUX (0) +#endif + +#if defined(__DragonFly__) +# define ASMJIT_OS_DRAGONFLYBSD (1) +#else +# define ASMJIT_OS_DRAGONFLYBSD (0) +#endif + +#if defined(__FreeBSD__) +# define ASMJIT_OS_FREEBSD (1) +#else +# define ASMJIT_OS_FREEBSD (0) +#endif + +#if defined(__NetBSD__) +# define ASMJIT_OS_NETBSD (1) +#else +# define ASMJIT_OS_NETBSD (0) +#endif + +#if defined(__OpenBSD__) +# define ASMJIT_OS_OPENBSD (1) +#else +# define ASMJIT_OS_OPENBSD (0) +#endif + +#if defined(__QNXNTO__) +# define ASMJIT_OS_QNX (1) +#else +# define ASMJIT_OS_QNX (0) +#endif + +#if defined(__sun) +# define ASMJIT_OS_SOLARIS (1) +#else +# define ASMJIT_OS_SOLARIS (0) +#endif + +#if defined(__CYGWIN__) +# define ASMJIT_OS_CYGWIN (1) +#else +# define ASMJIT_OS_CYGWIN (0) +#endif + +#define ASMJIT_OS_BSD ( \ + ASMJIT_OS_FREEBSD || \ + ASMJIT_OS_DRAGONFLYBSD || \ + ASMJIT_OS_NETBSD || \ + ASMJIT_OS_OPENBSD || \ + ASMJIT_OS_MAC) +#define ASMJIT_OS_POSIX (!ASMJIT_OS_WINDOWS) +// [@OS}@] + +// ============================================================================ +// [asmjit::Build - ARCH] +// ============================================================================ + +// [@ARCH{@] +// \def ASMJIT_ARCH_ARM32 +// True if the target architecture is a 32-bit ARM. +// +// \def ASMJIT_ARCH_ARM64 +// True if the target architecture is a 64-bit ARM. +// +// \def ASMJIT_ARCH_X86 +// True if the target architecture is a 32-bit X86/IA32 +// +// \def ASMJIT_ARCH_X64 +// True if the target architecture is a 64-bit X64/AMD64 +// +// \def ASMJIT_ARCH_LE +// True if the target architecture is little endian. +// +// \def ASMJIT_ARCH_BE +// True if the target architecture is big endian. +// +// \def ASMJIT_ARCH_64BIT +// True if the target architecture is 64-bit. + +#if (defined(_M_X64 ) || defined(__x86_64) || defined(__x86_64__) || \ + defined(_M_AMD64) || defined(__amd64 ) || defined(__amd64__ )) +# define ASMJIT_ARCH_X64 1 +#else +# define ASMJIT_ARCH_X64 0 +#endif + +#if (defined(_M_IX86 ) || defined(__X86__ ) || defined(__i386 ) || \ + defined(__IA32__) || defined(__I86__ ) || defined(__i386__) || \ + defined(__i486__) || defined(__i586__) || defined(__i686__)) +# define ASMJIT_ARCH_X86 (!ASMJIT_ARCH_X64) +#else +# define ASMJIT_ARCH_X86 0 +#endif + +#if defined(__aarch64__) +# define ASMJIT_ARCH_ARM64 1 +#else +# define ASMJIT_ARCH_ARM64 0 +#endif + +#if (defined(_M_ARM ) || defined(__arm ) || defined(__thumb__ ) || \ + defined(_M_ARMT ) || defined(__arm__ ) || defined(__thumb2__)) +# define ASMJIT_ARCH_ARM32 (!ASMJIT_ARCH_ARM64) +#else +# define ASMJIT_ARCH_ARM32 0 +#endif + +#define ASMJIT_ARCH_LE ( \ + ASMJIT_ARCH_X86 || \ + ASMJIT_ARCH_X64 || \ + ASMJIT_ARCH_ARM32 || \ + ASMJIT_ARCH_ARM64 ) +#define ASMJIT_ARCH_BE (!(ASMJIT_ARCH_LE)) +#define ASMJIT_ARCH_64BIT (ASMJIT_ARCH_X64 || ASMJIT_ARCH_ARM64) +// [@ARCH}@] + +// [@ARCH_UNALIGNED_RW{@] +// \def ASMJIT_ARCH_UNALIGNED_16 +// True if the target architecture allows unaligned 16-bit reads and writes. +// +// \def ASMJIT_ARCH_UNALIGNED_32 +// True if the target architecture allows unaligned 32-bit reads and writes. +// +// \def ASMJIT_ARCH_UNALIGNED_64 +// True if the target architecture allows unaligned 64-bit reads and writes. + +#define ASMJIT_ARCH_UNALIGNED_16 (ASMJIT_ARCH_X86 || ASMJIT_ARCH_X64) +#define ASMJIT_ARCH_UNALIGNED_32 (ASMJIT_ARCH_X86 || ASMJIT_ARCH_X64) +#define ASMJIT_ARCH_UNALIGNED_64 (ASMJIT_ARCH_X86 || ASMJIT_ARCH_X64) +// [@ARCH_UNALIGNED_RW}@] + +// ============================================================================ +// [asmjit::Build - CC] +// ============================================================================ + +// [@CC{@] +// \def ASMJIT_CC_CLANG +// Non-zero if the detected C++ compiler is CLANG (contains normalized CLANG version). +// +// \def ASMJIT_CC_CODEGEAR +// Non-zero if the detected C++ compiler is CODEGEAR or BORLAND (version not normalized). +// +// \def ASMJIT_CC_INTEL +// Non-zero if the detected C++ compiler is INTEL (version not normalized). +// +// \def ASMJIT_CC_GCC +// Non-zero if the detected C++ compiler is GCC (contains normalized GCC version). +// +// \def ASMJIT_CC_MSC +// Non-zero if the detected C++ compiler is MSC (contains normalized MSC version). +// +// \def ASMJIT_CC_MINGW +// Non-zero if the detected C++ compiler is MINGW32 (set to 32) or MINGW64 (set to 64). + +#define ASMJIT_CC_CLANG 0 +#define ASMJIT_CC_CODEGEAR 0 +#define ASMJIT_CC_GCC 0 +#define ASMJIT_CC_INTEL 0 +#define ASMJIT_CC_MSC 0 + +// Intel masquerades as GCC, so check for it first. +#if defined(__INTEL_COMPILER) +# undef ASMJIT_CC_INTEL +# define ASMJIT_CC_INTEL __INTEL_COMPILER +#elif defined(__CODEGEARC__) +# undef ASMJIT_CC_CODEGEAR +# define ASMJIT_CC_CODEGEAR (__CODEGEARC__) +#elif defined(__BORLANDC__) +# undef ASMJIT_CC_CODEGEAR +# define ASMJIT_CC_CODEGEAR (__BORLANDC__) +#elif defined(__clang__) && defined(__clang_minor__) +# undef ASMJIT_CC_CLANG +# define ASMJIT_CC_CLANG (__clang_major__ * 10000000 + __clang_minor__ * 100000 + __clang_patchlevel__) +#elif defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__) +# undef ASMJIT_CC_GCC +# define ASMJIT_CC_GCC (__GNUC__ * 10000000 + __GNUC_MINOR__ * 100000 + __GNUC_PATCHLEVEL__) +#elif defined(_MSC_VER) && defined(_MSC_FULL_VER) +# undef ASMJIT_CC_MSC +# if _MSC_VER == _MSC_FULL_VER / 10000 +# define ASMJIT_CC_MSC (_MSC_VER * 100000 + (_MSC_FULL_VER % 10000)) +# else +# define ASMJIT_CC_MSC (_MSC_VER * 100000 + (_MSC_FULL_VER % 100000)) +# endif +#else +# error "[asmjit] Unable to detect the C/C++ compiler." +#endif + +#if ASMJIT_CC_INTEL && (defined(__GNUC__) || defined(__clang__)) +# define ASMJIT_CC_INTEL_COMPAT_MODE 1 +# else +# define ASMJIT_CC_INTEL_COMPAT_MODE 0 +#endif + +#define ASMJIT_CC_CODEGEAR_EQ(x, y) (ASMJIT_CC_CODEGEAR == (((x) << 8) + (y))) +#define ASMJIT_CC_CODEGEAR_GE(x, y) (ASMJIT_CC_CODEGEAR >= (((x) << 8) + (y))) + +#define ASMJIT_CC_CLANG_EQ(x, y, z) (ASMJIT_CC_CLANG == ((x) * 10000000 + (y) * 100000 + (z))) +#define ASMJIT_CC_CLANG_GE(x, y, z) (ASMJIT_CC_CLANG >= ((x) * 10000000 + (y) * 100000 + (z))) + +#define ASMJIT_CC_GCC_EQ(x, y, z) (ASMJIT_CC_GCC == ((x) * 10000000 + (y) * 100000 + (z))) +#define ASMJIT_CC_GCC_GE(x, y, z) (ASMJIT_CC_GCC >= ((x) * 10000000 + (y) * 100000 + (z))) + +#define ASMJIT_CC_INTEL_EQ(x, y) (ASMJIT_CC_INTEL == (((x) * 100) + (y))) +#define ASMJIT_CC_INTEL_GE(x, y) (ASMJIT_CC_INTEL >= (((x) * 100) + (y))) + +#define ASMJIT_CC_MSC_EQ(x, y, z) (ASMJIT_CC_MSC == ((x) * 10000000 + (y) * 100000 + (z))) +#define ASMJIT_CC_MSC_GE(x, y, z) (ASMJIT_CC_MSC >= ((x) * 10000000 + (y) * 100000 + (z))) + +#if defined(__MINGW64__) +# define ASMJIT_CC_MINGW 64 +#elif defined(__MINGW32__) +# define ASMJIT_CC_MINGW 32 +#else +# define ASMJIT_CC_MINGW 0 +#endif + +#if defined(__cplusplus) +# if __cplusplus >= 201103L +# define ASMJIT_CC_CXX_VERSION __cplusplus +# elif defined(__GXX_EXPERIMENTAL_CXX0X__) || ASMJIT_CC_MSC_GE(18, 0, 0) || ASMJIT_CC_INTEL_GE(14, 0) +# define ASMJIT_CC_CXX_VERSION 201103L +# else +# define ASMJIT_CC_CXX_VERSION 199711L +# endif +#endif + +#if !defined(ASMJIT_CC_CXX_VERSION) +# define ASMJIT_CC_CXX_VERSION 0 +#endif +// [@CC}@] + +// [@CC_FEATURES{@] +#if ASMJIT_CC_CLANG +# define ASMJIT_CC_HAS_ATTRIBUTE (1) +# define ASMJIT_CC_HAS_ATTRIBUTE_ALIGNED (__has_attribute(__aligned__)) +# define ASMJIT_CC_HAS_ATTRIBUTE_ALWAYS_INLINE (__has_attribute(__always_inline__)) +# define ASMJIT_CC_HAS_ATTRIBUTE_NOINLINE (__has_attribute(__noinline__)) +# define ASMJIT_CC_HAS_ATTRIBUTE_NORETURN (__has_attribute(__noreturn__)) +# define ASMJIT_CC_HAS_ATTRIBUTE_OPTIMIZE (__has_attribute(__optimize__)) +# define ASMJIT_CC_HAS_BUILTIN_ASSUME (__has_builtin(__builtin_assume)) +# define ASMJIT_CC_HAS_BUILTIN_ASSUME_ALIGNED (__has_builtin(__builtin_assume_aligned)) +# define ASMJIT_CC_HAS_BUILTIN_EXPECT (__has_builtin(__builtin_expect)) +# define ASMJIT_CC_HAS_BUILTIN_UNREACHABLE (__has_builtin(__builtin_unreachable)) +# define ASMJIT_CC_HAS_ALIGNAS (__has_extension(__cxx_alignas__)) +# define ASMJIT_CC_HAS_ALIGNOF (__has_extension(__cxx_alignof__)) +# define ASMJIT_CC_HAS_CONSTEXPR (__has_extension(__cxx_constexpr__)) +# define ASMJIT_CC_HAS_DECLTYPE (__has_extension(__cxx_decltype__)) +# define ASMJIT_CC_HAS_DEFAULT_FUNCTION (__has_extension(__cxx_defaulted_functions__)) +# define ASMJIT_CC_HAS_DELETE_FUNCTION (__has_extension(__cxx_deleted_functions__)) +# define ASMJIT_CC_HAS_FINAL (__has_extension(__cxx_override_control__)) +# define ASMJIT_CC_HAS_INITIALIZER_LIST (__has_extension(__cxx_generalized_initializers__)) +# define ASMJIT_CC_HAS_LAMBDA (__has_extension(__cxx_lambdas__)) +# define ASMJIT_CC_HAS_NATIVE_CHAR (1) +# define ASMJIT_CC_HAS_NATIVE_WCHAR_T (1) +# define ASMJIT_CC_HAS_NATIVE_CHAR16_T (__has_extension(__cxx_unicode_literals__)) +# define ASMJIT_CC_HAS_NATIVE_CHAR32_T (__has_extension(__cxx_unicode_literals__)) +# define ASMJIT_CC_HAS_NOEXCEPT (__has_extension(__cxx_noexcept__)) +# define ASMJIT_CC_HAS_NULLPTR (__has_extension(__cxx_nullptr__)) +# define ASMJIT_CC_HAS_OVERRIDE (__has_extension(__cxx_override_control__)) +# define ASMJIT_CC_HAS_RVALUE (__has_extension(__cxx_rvalue_references__)) +# define ASMJIT_CC_HAS_STATIC_ASSERT (__has_extension(__cxx_static_assert__)) +# define ASMJIT_CC_HAS_VARIADIC_TEMPLATES (__has_extension(__cxx_variadic_templates__)) +#endif + +#if ASMJIT_CC_CODEGEAR +# define ASMJIT_CC_HAS_DECLSPEC_ALIGN (ASMJIT_CC_CODEGEAR >= 0x0610) +# define ASMJIT_CC_HAS_DECLSPEC_FORCEINLINE (0) +# define ASMJIT_CC_HAS_DECLSPEC_NOINLINE (0) +# define ASMJIT_CC_HAS_DECLSPEC_NORETURN (ASMJIT_CC_CODEGEAR >= 0x0610) +# define ASMJIT_CC_HAS_ALIGNAS (0) +# define ASMJIT_CC_HAS_ALIGNOF (0) +# define ASMJIT_CC_HAS_CONSTEXPR (0) +# define ASMJIT_CC_HAS_DECLTYPE (ASMJIT_CC_CODEGEAR >= 0x0610) +# define ASMJIT_CC_HAS_DEFAULT_FUNCTION (0) +# define ASMJIT_CC_HAS_DELETE_FUNCTION (0) +# define ASMJIT_CC_HAS_FINAL (0) +# define ASMJIT_CC_HAS_INITIALIZER_LIST (0) +# define ASMJIT_CC_HAS_LAMBDA (0) +# define ASMJIT_CC_HAS_NATIVE_CHAR (1) +# define ASMJIT_CC_HAS_NATIVE_WCHAR_T (1) +# define ASMJIT_CC_HAS_NATIVE_CHAR16_T (0) +# define ASMJIT_CC_HAS_NATIVE_CHAR32_T (0) +# define ASMJIT_CC_HAS_NOEXCEPT (0) +# define ASMJIT_CC_HAS_NULLPTR (0) +# define ASMJIT_CC_HAS_OVERRIDE (0) +# define ASMJIT_CC_HAS_RVALUE (ASMJIT_CC_CODEGEAR >= 0x0610) +# define ASMJIT_CC_HAS_STATIC_ASSERT (ASMJIT_CC_CODEGEAR >= 0x0610) +# define ASMJIT_CC_HAS_VARIADIC_TEMPLATES (0) +#endif + +#if ASMJIT_CC_GCC +# define ASMJIT_CC_HAS_ATTRIBUTE (1) +# define ASMJIT_CC_HAS_ATTRIBUTE_ALIGNED (ASMJIT_CC_GCC_GE(2, 7, 0)) +# define ASMJIT_CC_HAS_ATTRIBUTE_ALWAYS_INLINE (ASMJIT_CC_GCC_GE(4, 4, 0) && !ASMJIT_CC_MINGW) +# define ASMJIT_CC_HAS_ATTRIBUTE_NOINLINE (ASMJIT_CC_GCC_GE(3, 4, 0) && !ASMJIT_CC_MINGW) +# define ASMJIT_CC_HAS_ATTRIBUTE_NORETURN (ASMJIT_CC_GCC_GE(2, 5, 0)) +# define ASMJIT_CC_HAS_ATTRIBUTE_OPTIMIZE (ASMJIT_CC_GCC_GE(4, 4, 0)) +# define ASMJIT_CC_HAS_BUILTIN_ASSUME (0) +# define ASMJIT_CC_HAS_BUILTIN_ASSUME_ALIGNED (ASMJIT_CC_GCC_GE(4, 7, 0)) +# define ASMJIT_CC_HAS_BUILTIN_EXPECT (1) +# define ASMJIT_CC_HAS_BUILTIN_UNREACHABLE (ASMJIT_CC_GCC_GE(4, 5, 0) && ASMJIT_CC_CXX_VERSION >= 201103L) +# define ASMJIT_CC_HAS_ALIGNAS (ASMJIT_CC_GCC_GE(4, 8, 0) && ASMJIT_CC_CXX_VERSION >= 201103L) +# define ASMJIT_CC_HAS_ALIGNOF (ASMJIT_CC_GCC_GE(4, 8, 0) && ASMJIT_CC_CXX_VERSION >= 201103L) +# define ASMJIT_CC_HAS_CONSTEXPR (ASMJIT_CC_GCC_GE(4, 6, 0) && ASMJIT_CC_CXX_VERSION >= 201103L) +# define ASMJIT_CC_HAS_DECLTYPE (ASMJIT_CC_GCC_GE(4, 3, 0) && ASMJIT_CC_CXX_VERSION >= 201103L) +# define ASMJIT_CC_HAS_DEFAULT_FUNCTION (ASMJIT_CC_GCC_GE(4, 4, 0) && ASMJIT_CC_CXX_VERSION >= 201103L) +# define ASMJIT_CC_HAS_DELETE_FUNCTION (ASMJIT_CC_GCC_GE(4, 4, 0) && ASMJIT_CC_CXX_VERSION >= 201103L) +# define ASMJIT_CC_HAS_FINAL (ASMJIT_CC_GCC_GE(4, 7, 0) && ASMJIT_CC_CXX_VERSION >= 201103L) +# define ASMJIT_CC_HAS_INITIALIZER_LIST (ASMJIT_CC_GCC_GE(4, 4, 0) && ASMJIT_CC_CXX_VERSION >= 201103L) +# define ASMJIT_CC_HAS_LAMBDA (ASMJIT_CC_GCC_GE(4, 5, 0) && ASMJIT_CC_CXX_VERSION >= 201103L) +# define ASMJIT_CC_HAS_NATIVE_CHAR (1) +# define ASMJIT_CC_HAS_NATIVE_WCHAR_T (1) +# define ASMJIT_CC_HAS_NATIVE_CHAR16_T (ASMJIT_CC_GCC_GE(4, 5, 0) && ASMJIT_CC_CXX_VERSION >= 201103L) +# define ASMJIT_CC_HAS_NATIVE_CHAR32_T (ASMJIT_CC_GCC_GE(4, 5, 0) && ASMJIT_CC_CXX_VERSION >= 201103L) +# define ASMJIT_CC_HAS_NOEXCEPT (ASMJIT_CC_GCC_GE(4, 6, 0) && ASMJIT_CC_CXX_VERSION >= 201103L) +# define ASMJIT_CC_HAS_NULLPTR (ASMJIT_CC_GCC_GE(4, 6, 0) && ASMJIT_CC_CXX_VERSION >= 201103L) +# define ASMJIT_CC_HAS_OVERRIDE (ASMJIT_CC_GCC_GE(4, 7, 0) && ASMJIT_CC_CXX_VERSION >= 201103L) +# define ASMJIT_CC_HAS_RVALUE (ASMJIT_CC_GCC_GE(4, 3, 0) && ASMJIT_CC_CXX_VERSION >= 201103L) +# define ASMJIT_CC_HAS_STATIC_ASSERT (ASMJIT_CC_GCC_GE(4, 3, 0) && ASMJIT_CC_CXX_VERSION >= 201103L) +# define ASMJIT_CC_HAS_VARIADIC_TEMPLATES (ASMJIT_CC_GCC_GE(4, 3, 0) && ASMJIT_CC_CXX_VERSION >= 201103L) +#endif + +#if ASMJIT_CC_INTEL +# define ASMJIT_CC_HAS_ATTRIBUTE (ASMJIT_CC_INTEL_COMPAT_MODE) +# define ASMJIT_CC_HAS_ATTRIBUTE_ALIGNED (ASMJIT_CC_INTEL_COMPAT_MODE) +# define ASMJIT_CC_HAS_ATTRIBUTE_ALWAYS_INLINE (ASMJIT_CC_INTEL_COMPAT_MODE) +# define ASMJIT_CC_HAS_ATTRIBUTE_NOINLINE (ASMJIT_CC_INTEL_COMPAT_MODE) +# define ASMJIT_CC_HAS_ATTRIBUTE_NORETURN (ASMJIT_CC_INTEL_COMPAT_MODE) +# define ASMJIT_CC_HAS_ATTRIBUTE_OPTIMIZE (ASMJIT_CC_INTEL_COMPAT_MODE) +# define ASMJIT_CC_HAS_BUILTIN_EXPECT (ASMJIT_CC_INTEL_COMPAT_MODE) +# define ASMJIT_CC_HAS_DECLSPEC_ALIGN (ASMJIT_CC_INTEL_COMPAT_MODE == 0) +# define ASMJIT_CC_HAS_DECLSPEC_FORCEINLINE (ASMJIT_CC_INTEL_COMPAT_MODE == 0) +# define ASMJIT_CC_HAS_DECLSPEC_NOINLINE (ASMJIT_CC_INTEL_COMPAT_MODE == 0) +# define ASMJIT_CC_HAS_DECLSPEC_NORETURN (ASMJIT_CC_INTEL_COMPAT_MODE == 0) +# define ASMJIT_CC_HAS_ASSUME (1) +# define ASMJIT_CC_HAS_ASSUME_ALIGNED (1) +# define ASMJIT_CC_HAS_ALIGNAS (ASMJIT_CC_INTEL >= 1500) +# define ASMJIT_CC_HAS_ALIGNOF (ASMJIT_CC_INTEL >= 1500) +# define ASMJIT_CC_HAS_CONSTEXPR (ASMJIT_CC_INTEL >= 1400) +# define ASMJIT_CC_HAS_DECLTYPE (ASMJIT_CC_INTEL >= 1200) +# define ASMJIT_CC_HAS_DEFAULT_FUNCTION (ASMJIT_CC_INTEL >= 1200) +# define ASMJIT_CC_HAS_DELETE_FUNCTION (ASMJIT_CC_INTEL >= 1200) +# define ASMJIT_CC_HAS_FINAL (ASMJIT_CC_INTEL >= 1400) +# define ASMJIT_CC_HAS_INITIALIZER_LIST (ASMJIT_CC_INTEL >= 1400) +# define ASMJIT_CC_HAS_LAMBDA (ASMJIT_CC_INTEL >= 1200) +# define ASMJIT_CC_HAS_NATIVE_CHAR (1) +# define ASMJIT_CC_HAS_NATIVE_WCHAR_T (1) +# define ASMJIT_CC_HAS_NATIVE_CHAR16_T (ASMJIT_CC_INTEL >= 1400 || (ASMJIT_CC_INTEL_COMPAT_MODE > 0 && ASMJIT_CC_INTEL >= 1206)) +# define ASMJIT_CC_HAS_NATIVE_CHAR32_T (ASMJIT_CC_INTEL >= 1400 || (ASMJIT_CC_INTEL_COMPAT_MODE > 0 && ASMJIT_CC_INTEL >= 1206)) +# define ASMJIT_CC_HAS_NOEXCEPT (ASMJIT_CC_INTEL >= 1400) +# define ASMJIT_CC_HAS_NULLPTR (ASMJIT_CC_INTEL >= 1206) +# define ASMJIT_CC_HAS_OVERRIDE (ASMJIT_CC_INTEL >= 1400) +# define ASMJIT_CC_HAS_RVALUE (ASMJIT_CC_INTEL >= 1110) +# define ASMJIT_CC_HAS_STATIC_ASSERT (ASMJIT_CC_INTEL >= 1110) +# define ASMJIT_CC_HAS_VARIADIC_TEMPLATES (ASMJIT_CC_INTEL >= 1206) +#endif + +#if ASMJIT_CC_MSC +# define ASMJIT_CC_HAS_DECLSPEC_ALIGN (1) +# define ASMJIT_CC_HAS_DECLSPEC_FORCEINLINE (1) +# define ASMJIT_CC_HAS_DECLSPEC_NOINLINE (1) +# define ASMJIT_CC_HAS_DECLSPEC_NORETURN (1) +# define ASMJIT_CC_HAS_ASSUME (1) +# define ASMJIT_CC_HAS_ASSUME_ALIGNED (0) +# define ASMJIT_CC_HAS_ALIGNAS (ASMJIT_CC_MSC_GE(19, 0, 0)) +# define ASMJIT_CC_HAS_ALIGNOF (ASMJIT_CC_MSC_GE(19, 0, 0)) +# define ASMJIT_CC_HAS_CONSTEXPR (ASMJIT_CC_MSC_GE(19, 0, 0)) +# define ASMJIT_CC_HAS_DECLTYPE (ASMJIT_CC_MSC_GE(16, 0, 0)) +# define ASMJIT_CC_HAS_DEFAULT_FUNCTION (ASMJIT_CC_MSC_GE(18, 0, 0)) +# define ASMJIT_CC_HAS_DELETE_FUNCTION (ASMJIT_CC_MSC_GE(18, 0, 0)) +# define ASMJIT_CC_HAS_FINAL (ASMJIT_CC_MSC_GE(14, 0, 0)) +# define ASMJIT_CC_HAS_INITIALIZER_LIST (ASMJIT_CC_MSC_GE(18, 0, 0)) +# define ASMJIT_CC_HAS_LAMBDA (ASMJIT_CC_MSC_GE(16, 0, 0)) +# define ASMJIT_CC_HAS_NATIVE_CHAR (1) +# if defined(_NATIVE_WCHAR_T_DEFINED) +# define ASMJIT_CC_HAS_NATIVE_WCHAR_T (1) +# else +# define ASMJIT_CC_HAS_NATIVE_WCHAR_T (0) +# endif +# define ASMJIT_CC_HAS_NATIVE_CHAR16_T (ASMJIT_CC_MSC_GE(19, 0, 0)) +# define ASMJIT_CC_HAS_NATIVE_CHAR32_T (ASMJIT_CC_MSC_GE(19, 0, 0)) +# define ASMJIT_CC_HAS_NOEXCEPT (ASMJIT_CC_MSC_GE(19, 0, 0)) +# define ASMJIT_CC_HAS_NULLPTR (ASMJIT_CC_MSC_GE(16, 0, 0)) +# define ASMJIT_CC_HAS_OVERRIDE (ASMJIT_CC_MSC_GE(14, 0, 0)) +# define ASMJIT_CC_HAS_RVALUE (ASMJIT_CC_MSC_GE(16, 0, 0)) +# define ASMJIT_CC_HAS_STATIC_ASSERT (ASMJIT_CC_MSC_GE(16, 0, 0)) +# define ASMJIT_CC_HAS_VARIADIC_TEMPLATES (ASMJIT_CC_MSC_GE(18, 0, 0)) +#endif + +// Fixup some vendor specific keywords. +#if !defined(ASMJIT_CC_HAS_ASSUME) +# define ASMJIT_CC_HAS_ASSUME (0) +#endif +#if !defined(ASMJIT_CC_HAS_ASSUME_ALIGNED) +# define ASMJIT_CC_HAS_ASSUME_ALIGNED (0) +#endif + +// Fixup compilers that don't support '__attribute__'. +#if !defined(ASMJIT_CC_HAS_ATTRIBUTE) +# define ASMJIT_CC_HAS_ATTRIBUTE (0) +#endif +#if !defined(ASMJIT_CC_HAS_ATTRIBUTE_ALIGNED) +# define ASMJIT_CC_HAS_ATTRIBUTE_ALIGNED (0) +#endif +#if !defined(ASMJIT_CC_HAS_ATTRIBUTE_ALWAYS_INLINE) +# define ASMJIT_CC_HAS_ATTRIBUTE_ALWAYS_INLINE (0) +#endif +#if !defined(ASMJIT_CC_HAS_ATTRIBUTE_NOINLINE) +# define ASMJIT_CC_HAS_ATTRIBUTE_NOINLINE (0) +#endif +#if !defined(ASMJIT_CC_HAS_ATTRIBUTE_NORETURN) +# define ASMJIT_CC_HAS_ATTRIBUTE_NORETURN (0) +#endif +#if !defined(ASMJIT_CC_HAS_ATTRIBUTE_OPTIMIZE) +# define ASMJIT_CC_HAS_ATTRIBUTE_OPTIMIZE (0) +#endif + +// Fixup compilers that don't support '__builtin?'. +#if !defined(ASMJIT_CC_HAS_BUILTIN_ASSUME) +# define ASMJIT_CC_HAS_BUILTIN_ASSUME (0) +#endif +#if !defined(ASMJIT_CC_HAS_BUILTIN_ASSUME_ALIGNED) +# define ASMJIT_CC_HAS_BUILTIN_ASSUME_ALIGNED (0) +#endif +#if !defined(ASMJIT_CC_HAS_BUILTIN_EXPECT) +# define ASMJIT_CC_HAS_BUILTIN_EXPECT (0) +#endif +#if !defined(ASMJIT_CC_HAS_BUILTIN_UNREACHABLE) +# define ASMJIT_CC_HAS_BUILTIN_UNREACHABLE (0) +#endif + +// Fixup compilers that don't support 'declspec'. +#if !defined(ASMJIT_CC_HAS_DECLSPEC_ALIGN) +# define ASMJIT_CC_HAS_DECLSPEC_ALIGN (0) +#endif +#if !defined(ASMJIT_CC_HAS_DECLSPEC_FORCEINLINE) +# define ASMJIT_CC_HAS_DECLSPEC_FORCEINLINE (0) +#endif +#if !defined(ASMJIT_CC_HAS_DECLSPEC_NOINLINE) +# define ASMJIT_CC_HAS_DECLSPEC_NOINLINE (0) +#endif +#if !defined(ASMJIT_CC_HAS_DECLSPEC_NORETURN) +# define ASMJIT_CC_HAS_DECLSPEC_NORETURN (0) +#endif +// [@CC_FEATURES}@] + +// [@CC_API{@] +// \def ASMJIT_API +// The decorated function is asmjit API and should be exported. +#if !defined(ASMJIT_API) +# if defined(ASMJIT_STATIC) +# define ASMJIT_API +# elif ASMJIT_OS_WINDOWS +# if (ASMJIT_CC_GCC || ASMJIT_CC_CLANG) && !ASMJIT_CC_MINGW +# if defined(ASMJIT_EXPORTS) +# define ASMJIT_API __attribute__((__dllexport__)) +# else +# define ASMJIT_API __attribute__((__dllimport__)) +# endif +# else +# if defined(ASMJIT_EXPORTS) +# define ASMJIT_API __declspec(dllexport) +# else +# define ASMJIT_API __declspec(dllimport) +# endif +# endif +# else +# if ASMJIT_CC_CLANG || ASMJIT_CC_GCC_GE(4, 0, 0) || ASMJIT_CC_INTEL +# define ASMJIT_API __attribute__((__visibility__("default"))) +# endif +# endif +#endif +// [@CC_API}@] + +// [@CC_VARAPI{@] +// \def ASMJIT_VARAPI +// The decorated variable is part of asmjit API and is exported. +#if !defined(ASMJIT_VARAPI) +# define ASMJIT_VARAPI extern ASMJIT_API +#endif +// [@CC_VARAPI}@] + +// [@CC_VIRTAPI{@] +// \def ASMJIT_VIRTAPI +// The decorated class has a virtual table and is part of asmjit API. +// +// This is basically a workaround. When using MSVC and marking class as DLL +// export everything gets exported, which is unwanted in most projects. MSVC +// automatically exports typeinfo and vtable if at least one symbol of the +// class is exported. However, GCC has some strange behavior that even if +// one or more symbol is exported it doesn't export typeinfo unless the +// class itself is decorated with "visibility(default)" (i.e. asmjit_API). +#if (ASMJIT_CC_GCC || ASMJIT_CC_CLANG) && !ASMJIT_OS_WINDOWS +# define ASMJIT_VIRTAPI ASMJIT_API +#else +# define ASMJIT_VIRTAPI +#endif +// [@CC_VIRTAPI}@] + +// [@CC_INLINE{@] +// \def ASMJIT_INLINE +// Always inline the decorated function. +#if ASMJIT_CC_HAS_ATTRIBUTE_ALWAYS_INLINE +# define ASMJIT_INLINE inline __attribute__((__always_inline__)) +#elif ASMJIT_CC_HAS_DECLSPEC_FORCEINLINE +# define ASMJIT_INLINE __forceinline +#else +# define ASMJIT_INLINE inline +#endif +// [@CC_INLINE}@] + +// [@CC_NOINLINE{@] +// \def ASMJIT_NOINLINE +// Never inline the decorated function. +#if ASMJIT_CC_HAS_ATTRIBUTE_NOINLINE +# define ASMJIT_NOINLINE __attribute__((__noinline__)) +#elif ASMJIT_CC_HAS_DECLSPEC_NOINLINE +# define ASMJIT_NOINLINE __declspec(noinline) +#else +# define ASMJIT_NOINLINE +#endif +// [@CC_NOINLINE}@] + +// [@CC_NORETURN{@] +// \def ASMJIT_NORETURN +// The decorated function never returns (exit, assertion failure, etc...). +#if ASMJIT_CC_HAS_ATTRIBUTE_NORETURN +# define ASMJIT_NORETURN __attribute__((__noreturn__)) +#elif ASMJIT_CC_HAS_DECLSPEC_NORETURN +# define ASMJIT_NORETURN __declspec(noreturn) +#else +# define ASMJIT_NORETURN +#endif +// [@CC_NORETURN}@] + +// [@CC_CDECL{@] +// \def ASMJIT_CDECL +// Standard C function calling convention decorator (__cdecl). +#if ASMJIT_ARCH_X86 +# if ASMJIT_CC_HAS_ATTRIBUTE +# define ASMJIT_CDECL __attribute__((__cdecl__)) +# else +# define ASMJIT_CDECL __cdecl +# endif +#else +# define ASMJIT_CDECL +#endif +// [@CC_CDECL}@] + +// [@CC_STDCALL{@] +// \def ASMJIT_STDCALL +// StdCall function calling convention decorator (__stdcall). +#if ASMJIT_ARCH_X86 +# if ASMJIT_CC_HAS_ATTRIBUTE +# define ASMJIT_STDCALL __attribute__((__stdcall__)) +# else +# define ASMJIT_STDCALL __stdcall +# endif +#else +# define ASMJIT_STDCALL +#endif +// [@CC_STDCALL}@] + +// [@CC_FASTCALL{@] +// \def ASMJIT_FASTCALL +// FastCall function calling convention decorator (__fastcall). +#if ASMJIT_ARCH_X86 +# if ASMJIT_CC_HAS_ATTRIBUTE +# define ASMJIT_FASTCALL __attribute__((__fastcall__)) +# else +# define ASMJIT_FASTCALL __fastcall +# endif +#else +# define ASMJIT_FASTCALL +#endif +// [@CC_FASTCALL}@] + +// [@CC_REGPARM{@] +// \def ASMJIT_REGPARM(n) +// A custom calling convention which passes n arguments in registers. +#if ASMJIT_ARCH_X86 && ASMJIT_CC_HAS_ATTRIBUTE +# define ASMJIT_REGPARM(n) __attribute__((__regparm__(n))) +#else +# define ASMJIT_REGPARM(n) +#endif +// [@CC_REGPARM}@] + +// [@CC_NOEXCEPT{@] +// \def ASMJIT_NOEXCEPT +// The decorated function never throws an exception (noexcept). +#if ASMJIT_CC_HAS_NOEXCEPT +# define ASMJIT_NOEXCEPT noexcept +#else +# define ASMJIT_NOEXCEPT +#endif +// [@CC_NOEXCEPT}@] + +// [@CC_NOP{@] +// \def ASMJIT_NOP +// No operation. +#if !defined(ASMJIT_NOP) +# define ASMJIT_NOP ((void)0) +#endif +// [@CC_NOP}@] + +// [@CC_ASSUME{@] +// \def ASMJIT_ASSUME(exp) +// Assume that the expression exp is always true. +#if ASMJIT_CC_HAS_ASSUME +# define ASMJIT_ASSUME(exp) __assume(exp) +#elif ASMJIT_CC_HAS_BUILTIN_ASSUME +# define ASMJIT_ASSUME(exp) __builtin_assume(exp) +#elif ASMJIT_CC_HAS_BUILTIN_UNREACHABLE +# define ASMJIT_ASSUME(exp) do { if (!(exp)) __builtin_unreachable(); } while (0) +#else +# define ASMJIT_ASSUME(exp) ((void)0) +#endif +// [@CC_ASSUME}@] + +// [@CC_ASSUME_ALIGNED{@] +// \def ASMJIT_ASSUME_ALIGNED(p, alignment) +// Assume that the pointer 'p' is aligned to at least 'alignment' bytes. +#if ASMJIT_CC_HAS_ASSUME_ALIGNED +# define ASMJIT_ASSUME_ALIGNED(p, alignment) __assume_aligned(p, alignment) +#elif ASMJIT_CC_HAS_BUILTIN_ASSUME_ALIGNED +# define ASMJIT_ASSUME_ALIGNED(p, alignment) p = __builtin_assume_aligned(p, alignment) +#else +# define ASMJIT_ASSUME_ALIGNED(p, alignment) ((void)0) +#endif +// [@CC_ASSUME_ALIGNED}@] + +// [@CC_EXPECT{@] +// \def ASMJIT_LIKELY(exp) +// Expression exp is likely to be true. +// +// \def ASMJIT_UNLIKELY(exp) +// Expression exp is likely to be false. +#if ASMJIT_CC_HAS_BUILTIN_EXPECT +# define ASMJIT_LIKELY(exp) __builtin_expect(!!(exp), 1) +# define ASMJIT_UNLIKELY(exp) __builtin_expect(!!(exp), 0) +#else +# define ASMJIT_LIKELY(exp) (exp) +# define ASMJIT_UNLIKELY(exp) (exp) +#endif +// [@CC_EXPECT}@] + +// [@CC_FALLTHROUGH{@] +// \def ASMJIT_FALLTHROUGH +// The code falls through annotation (switch / case). +#if ASMJIT_CC_CLANG && __cplusplus >= 201103L +# define ASMJIT_FALLTHROUGH [[clang::fallthrough]] +#else +# define ASMJIT_FALLTHROUGH (void)0 +#endif +// [@CC_FALLTHROUGH}@] + +// [@CC_UNUSED{@] +// \def ASMJIT_UNUSED(x) +// Mark a variable x as unused. +#define ASMJIT_UNUSED(x) (void)(x) +// [@CC_UNUSED}@] + +// [@CC_OFFSET_OF{@] +// \def ASMJIT_OFFSET_OF(x, y). +// Get the offset of a member y of a struct x at compile-time. +#define ASMJIT_OFFSET_OF(x, y) ((int)(intptr_t)((const char*)&((const x*)0x1)->y) - 1) +// [@CC_OFFSET_OF}@] + +// [@CC_ARRAY_SIZE{@] +// \def ASMJIT_ARRAY_SIZE(x) +// Get the array size of x at compile-time. +#define ASMJIT_ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) +// [@CC_ARRAY_SIZE}@] + +// ============================================================================ +// [asmjit::Build - STDTYPES] +// ============================================================================ + +// [@STDTYPES{@] +#if defined(__MINGW32__) || defined(__MINGW64__) +# include +#endif +#if defined(_MSC_VER) && (_MSC_VER < 1600) +# include +# if !defined(ASMJIT_SUPPRESS_STD_TYPES) +# if (_MSC_VER < 1300) +typedef signed char int8_t; +typedef signed short int16_t; +typedef signed int int32_t; +typedef signed __int64 int64_t; +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +typedef unsigned __int64 uint64_t; +# else +typedef __int8 int8_t; +typedef __int16 int16_t; +typedef __int32 int32_t; +typedef __int64 int64_t; +typedef unsigned __int8 uint8_t; +typedef unsigned __int16 uint16_t; +typedef unsigned __int32 uint32_t; +typedef unsigned __int64 uint64_t; +# endif +# endif +#else +# include +# include +#endif +// [@STDTYPES}@] + +// ============================================================================ +// [asmjit::Build - Dependencies] +// ============================================================================ + +#include +#include +#include +#include + +#include +#include + +#if ASMJIT_OS_POSIX +# include +#endif // ASMJIT_OS_POSIX + +// ============================================================================ +// [asmjit::Build - Additional] +// ============================================================================ + +// Build host architecture if no architecture is selected. +#if !defined(ASMJIT_BUILD_HOST) && \ + !defined(ASMJIT_BUILD_X86) && \ + !defined(ASMJIT_BUILD_ARM) +# define ASMJIT_BUILD_HOST +#endif + +// Detect host architecture if building only for host. +#if defined(ASMJIT_BUILD_HOST) +# if (ASMJIT_ARCH_X86 || ASMJIT_ARCH_X64) && !defined(ASMJIT_BUILD_X86) +# define ASMJIT_BUILD_X86 +# endif // ASMJIT_ARCH_X86 +#endif // ASMJIT_BUILD_HOST + +#if ASMJIT_CC_MSC +# define ASMJIT_UINT64_C(x) x##ui64 +#else +# define ASMJIT_UINT64_C(x) x##ull +#endif + +#if ASMJIT_ARCH_LE +# define ASMJIT_PACK32_4x8(A, B, C, D) ((A) + ((B) << 8) + ((C) << 16) + ((D) << 24)) +#else +# define ASMJIT_PACK32_4x8(A, B, C, D) ((D) + ((C) << 8) + ((B) << 16) + ((A) << 24)) +#endif + +// Internal macros that are only used when building AsmJit itself. +#if defined(ASMJIT_EXPORTS) +# if !defined(ASMJIT_DEBUG) && ASMJIT_CC_HAS_ATTRIBUTE_OPTIMIZE +# define ASMJIT_FAVOR_SIZE __attribute__((__optimize__("Os"))) +# else +# define ASMJIT_FAVOR_SIZE +# endif +#endif // ASMJIT_EXPORTS + +// ============================================================================ +// [asmjit::Build - Test] +// ============================================================================ + +// Include a unit testing package if this is a `asmjit_test` build. +#if defined(ASMJIT_TEST) +# include "../../test/broken.h" +#endif // ASMJIT_TEST + +// [Guard] +#endif // _ASMJIT_BUILD_H diff --git a/libraries/asmjit/asmjit/base.h b/libraries/asmjit/asmjit/base.h new file mode 100644 index 00000000000..70b7e82f6ba --- /dev/null +++ b/libraries/asmjit/asmjit/base.h @@ -0,0 +1,34 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Guard] +#ifndef _ASMJIT_BASE_H +#define _ASMJIT_BASE_H + +// [Dependencies] +#include "./base/arch.h" +#include "./base/assembler.h" +#include "./base/codebuilder.h" +#include "./base/codecompiler.h" +#include "./base/codeemitter.h" +#include "./base/codeholder.h" +#include "./base/constpool.h" +#include "./base/cpuinfo.h" +#include "./base/func.h" +#include "./base/globals.h" +#include "./base/inst.h" +#include "./base/logging.h" +#include "./base/operand.h" +#include "./base/osutils.h" +#include "./base/runtime.h" +#include "./base/simdtypes.h" +#include "./base/string.h" +#include "./base/utils.h" +#include "./base/vmem.h" +#include "./base/zone.h" + +// [Guard] +#endif // _ASMJIT_BASE_H diff --git a/libraries/asmjit/asmjit/base/arch.cpp b/libraries/asmjit/asmjit/base/arch.cpp new file mode 100644 index 00000000000..2e849c67a1d --- /dev/null +++ b/libraries/asmjit/asmjit/base/arch.cpp @@ -0,0 +1,161 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Export] +#define ASMJIT_EXPORTS + +// [Dependencies] +#include "../base/arch.h" + +#if defined(ASMJIT_BUILD_X86) +#include "../x86/x86operand.h" +#endif // ASMJIT_BUILD_X86 + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +// ============================================================================ +// [asmjit::ArchInfo] +// ============================================================================ + +static const uint32_t archInfoTable[] = { + // <-------------+---------------------+-----------------------+-------+ + // | Type | SubType | GPInfo| + // <-------------+---------------------+-----------------------+-------+ + ASMJIT_PACK32_4x8(ArchInfo::kTypeNone , ArchInfo::kSubTypeNone, 0, 0), + ASMJIT_PACK32_4x8(ArchInfo::kTypeX86 , ArchInfo::kSubTypeNone, 4, 8), + ASMJIT_PACK32_4x8(ArchInfo::kTypeX64 , ArchInfo::kSubTypeNone, 8, 16), + ASMJIT_PACK32_4x8(ArchInfo::kTypeX32 , ArchInfo::kSubTypeNone, 8, 16), + ASMJIT_PACK32_4x8(ArchInfo::kTypeA32 , ArchInfo::kSubTypeNone, 4, 16), + ASMJIT_PACK32_4x8(ArchInfo::kTypeA64 , ArchInfo::kSubTypeNone, 8, 32) +}; + +ASMJIT_FAVOR_SIZE void ArchInfo::init(uint32_t type, uint32_t subType) noexcept { + uint32_t index = type < ASMJIT_ARRAY_SIZE(archInfoTable) ? type : uint32_t(0); + + // Make sure the `archInfoTable` array is correctly indexed. + _signature = archInfoTable[index]; + ASMJIT_ASSERT(_type == index); + + // Even if the architecture is not known we setup its type and sub-type, + // however, such architecture is not really useful. + _type = type; + _subType = subType; +} + +// ============================================================================ +// [asmjit::ArchUtils] +// ============================================================================ + +ASMJIT_FAVOR_SIZE Error ArchUtils::typeIdToRegInfo(uint32_t archType, uint32_t& typeIdInOut, RegInfo& regInfo) noexcept { + uint32_t typeId = typeIdInOut; + + // Zero the signature so it's clear in case that typeId is not invalid. + regInfo._signature = 0; + +#if defined(ASMJIT_BUILD_X86) + if (ArchInfo::isX86Family(archType)) { + // Passed RegType instead of TypeId? + if (typeId <= Reg::kRegMax) + typeId = x86OpData.archRegs.regTypeToTypeId[typeId]; + + if (ASMJIT_UNLIKELY(!TypeId::isValid(typeId))) + return DebugUtils::errored(kErrorInvalidTypeId); + + // First normalize architecture dependent types. + if (TypeId::isAbstract(typeId)) { + if (typeId == TypeId::kIntPtr) + typeId = (archType == ArchInfo::kTypeX86) ? TypeId::kI32 : TypeId::kI64; + else + typeId = (archType == ArchInfo::kTypeX86) ? TypeId::kU32 : TypeId::kU64; + } + + // Type size helps to construct all kinds of registers. If the size is zero + // then the TypeId is invalid. + uint32_t size = TypeId::sizeOf(typeId); + if (ASMJIT_UNLIKELY(!size)) + return DebugUtils::errored(kErrorInvalidTypeId); + + if (ASMJIT_UNLIKELY(typeId == TypeId::kF80)) + return DebugUtils::errored(kErrorInvalidUseOfF80); + + uint32_t regType = 0; + + switch (typeId) { + case TypeId::kI8: + case TypeId::kU8: + regType = X86Reg::kRegGpbLo; + break; + + case TypeId::kI16: + case TypeId::kU16: + regType = X86Reg::kRegGpw; + break; + + case TypeId::kI32: + case TypeId::kU32: + regType = X86Reg::kRegGpd; + break; + + case TypeId::kI64: + case TypeId::kU64: + if (archType == ArchInfo::kTypeX86) + return DebugUtils::errored(kErrorInvalidUseOfGpq); + + regType = X86Reg::kRegGpq; + break; + + // F32 and F64 are always promoted to use vector registers. + case TypeId::kF32: + typeId = TypeId::kF32x1; + regType = X86Reg::kRegXmm; + break; + + case TypeId::kF64: + typeId = TypeId::kF64x1; + regType = X86Reg::kRegXmm; + break; + + // Mask registers {k}. + case TypeId::kMask8: + case TypeId::kMask16: + case TypeId::kMask32: + case TypeId::kMask64: + regType = X86Reg::kRegK; + break; + + // MMX registers. + case TypeId::kMmx32: + case TypeId::kMmx64: + regType = X86Reg::kRegMm; + break; + + // XMM|YMM|ZMM registers. + default: + if (size <= 16) + regType = X86Reg::kRegXmm; + else if (size == 32) + regType = X86Reg::kRegYmm; + else + regType = X86Reg::kRegZmm; + break; + } + + typeIdInOut = typeId; + regInfo._signature = x86OpData.archRegs.regInfo[regType].getSignature(); + return kErrorOk; + } +#endif // ASMJIT_BUILD_X86 + + return DebugUtils::errored(kErrorInvalidArch); +} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" diff --git a/libraries/asmjit/asmjit/base/arch.h b/libraries/asmjit/asmjit/base/arch.h new file mode 100644 index 00000000000..e03c6af4e0a --- /dev/null +++ b/libraries/asmjit/asmjit/base/arch.h @@ -0,0 +1,199 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Guard] +#ifndef _ASMJIT_BASE_ARCH_H +#define _ASMJIT_BASE_ARCH_H + +// [Dependencies] +#include "../base/globals.h" +#include "../base/operand.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +//! \addtogroup asmjit_base +//! \{ + +// ============================================================================ +// [asmjit::ArchInfo] +// ============================================================================ + +class ArchInfo { +public: + //! Architecture type. + ASMJIT_ENUM(Type) { + kTypeNone = 0, //!< No/Unknown architecture. + + // X86 architectures. + kTypeX86 = 1, //!< X86 architecture (32-bit). + kTypeX64 = 2, //!< X64 architecture (64-bit) (AMD64). + kTypeX32 = 3, //!< X32 architecture (DEAD-END). + + // ARM architectures. + kTypeA32 = 4, //!< ARM 32-bit architecture (AArch32/ARM/THUMB). + kTypeA64 = 5, //!< ARM 64-bit architecture (AArch64). + + //! Architecture detected at compile-time (architecture of the host). + kTypeHost = ASMJIT_ARCH_X86 ? kTypeX86 : + ASMJIT_ARCH_X64 ? kTypeX64 : + ASMJIT_ARCH_ARM32 ? kTypeA32 : + ASMJIT_ARCH_ARM64 ? kTypeA64 : kTypeNone + }; + + //! Architecture sub-type or execution mode. + ASMJIT_ENUM(SubType) { + kSubTypeNone = 0, //!< Default mode (or no specific mode). + + // X86 sub-types. + kSubTypeX86_AVX = 1, //!< Code generation uses AVX by default (VEC instructions). + kSubTypeX86_AVX2 = 2, //!< Code generation uses AVX2 by default (VEC instructions). + kSubTypeX86_AVX512 = 3, //!< Code generation uses AVX-512F by default (+32 vector regs). + kSubTypeX86_AVX512VL = 4, //!< Code generation uses AVX-512F-VL by default (+VL extensions). + + // ARM sub-types. + kSubTypeA32_Thumb = 8, //!< THUMB|THUMB2 sub-type (only ARM in 32-bit mode). + +#if (ASMJIT_ARCH_X86 || ASMJIT_ARCH_X64) && defined(__AVX512VL__) + kSubTypeHost = kSubTypeX86_AVX512VL +#elif (ASMJIT_ARCH_X86 || ASMJIT_ARCH_X64) && defined(__AVX512F__) + kSubTypeHost = kSubTypeX86_AVX512 +#elif (ASMJIT_ARCH_X86 || ASMJIT_ARCH_X64) && defined(__AVX2__) + kSubTypeHost = kSubTypeX86_AVX2 +#elif (ASMJIT_ARCH_X86 || ASMJIT_ARCH_X64) && defined(__AVX__) + kSubTypeHost = kSubTypeX86_AVX +#elif (ASMJIT_ARCH_ARM32) && (defined(_M_ARMT) || defined(__thumb__) || defined(__thumb2__)) + kSubTypeHost = kSubTypeA32_Thumb +#else + kSubTypeHost = 0 +#endif + }; + + // -------------------------------------------------------------------------- + // [Utilities] + // -------------------------------------------------------------------------- + + static ASMJIT_INLINE bool isX86Family(uint32_t archType) noexcept { return archType >= kTypeX86 && archType <= kTypeX32; } + static ASMJIT_INLINE bool isArmFamily(uint32_t archType) noexcept { return archType >= kTypeA32 && archType <= kTypeA64; } + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE ArchInfo() noexcept : _signature(0) {} + ASMJIT_INLINE ArchInfo(const ArchInfo& other) noexcept : _signature(other._signature) {} + explicit ASMJIT_INLINE ArchInfo(uint32_t type, uint32_t subType = kSubTypeNone) noexcept { init(type, subType); } + + ASMJIT_INLINE static ArchInfo host() noexcept { return ArchInfo(kTypeHost, kSubTypeHost); } + + // -------------------------------------------------------------------------- + // [Init / Reset] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE bool isInitialized() const noexcept { return _type != kTypeNone; } + + ASMJIT_API void init(uint32_t type, uint32_t subType = kSubTypeNone) noexcept; + ASMJIT_INLINE void reset() noexcept { _signature = 0; } + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get if the architecture is 32-bit. + ASMJIT_INLINE bool is32Bit() const noexcept { return _gpSize == 4; } + //! Get if the architecture is 64-bit. + ASMJIT_INLINE bool is64Bit() const noexcept { return _gpSize == 8; } + + //! Get architecture type, see \ref Type. + ASMJIT_INLINE uint32_t getType() const noexcept { return _type; } + + //! Get architecture sub-type, see \ref SubType. + //! + //! X86 & X64 + //! --------- + //! + //! Architecture subtype describe the highest instruction-set level that can + //! be used. + //! + //! ARM32 + //! ----- + //! + //! Architecture mode means the instruction encoding to be used when generating + //! machine code, thus mode can be used to force generation of THUMB and THUMB2 + //! encoding or regular ARM encoding. + //! + //! ARM64 + //! ----- + //! + //! No meaning yet. + ASMJIT_INLINE uint32_t getSubType() const noexcept { return _subType; } + + //! Get if the architecture is X86, X64, or X32. + ASMJIT_INLINE bool isX86Family() const noexcept { return isX86Family(_type); } + //! Get if the architecture is ARM32 or ARM64. + ASMJIT_INLINE bool isArmFamily() const noexcept { return isArmFamily(_type); } + + //! Get a size of a general-purpose register. + ASMJIT_INLINE uint32_t getGpSize() const noexcept { return _gpSize; } + //! Get number of general-purpose registers. + ASMJIT_INLINE uint32_t getGpCount() const noexcept { return _gpCount; } + + // -------------------------------------------------------------------------- + // [Operator Overload] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE const ArchInfo& operator=(const ArchInfo& other) noexcept { _signature = other._signature; return *this; } + ASMJIT_INLINE bool operator==(const ArchInfo& other) const noexcept { return _signature == other._signature; } + ASMJIT_INLINE bool operator!=(const ArchInfo& other) const noexcept { return _signature != other._signature; } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + union { + struct { + uint8_t _type; //!< Architecture type. + uint8_t _subType; //!< Architecture sub-type. + uint8_t _gpSize; //!< Default size of a general purpose register. + uint8_t _gpCount; //!< Count of all general purpose registers. + }; + uint32_t _signature; //!< Architecture signature (32-bit int). + }; +}; + +// ============================================================================ +// [asmjit::ArchRegs] +// ============================================================================ + +//! Information about all architecture registers. +struct ArchRegs { + //! Register information and signatures indexed by \ref Reg::Type. + RegInfo regInfo[Reg::kRegMax + 1]; + //! Count (maximum) of registers per \ref Reg::Type. + uint8_t regCount[Reg::kRegMax + 1]; + //! Converts RegType to TypeId, see \ref TypeId::Id. + uint8_t regTypeToTypeId[Reg::kRegMax + 1]; +}; + +// ============================================================================ +// [asmjit::ArchUtils] +// ============================================================================ + +struct ArchUtils { + ASMJIT_API static Error typeIdToRegInfo(uint32_t archType, uint32_t& typeIdInOut, RegInfo& regInfo) noexcept; +}; + +//! \} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" + +// [Guard] +#endif // _ASMJIT_BASE_ARCH_H diff --git a/libraries/asmjit/asmjit/base/assembler.cpp b/libraries/asmjit/asmjit/base/assembler.cpp new file mode 100644 index 00000000000..79a26665115 --- /dev/null +++ b/libraries/asmjit/asmjit/base/assembler.cpp @@ -0,0 +1,447 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Export] +#define ASMJIT_EXPORTS + +// [Dependencies] +#include "../base/assembler.h" +#include "../base/constpool.h" +#include "../base/utils.h" +#include "../base/vmem.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +// ============================================================================ +// [asmjit::Assembler - Construction / Destruction] +// ============================================================================ + +Assembler::Assembler() noexcept + : CodeEmitter(kTypeAssembler), + _section(nullptr), + _bufferData(nullptr), + _bufferEnd(nullptr), + _bufferPtr(nullptr), + _op4(), + _op5() {} + +Assembler::~Assembler() noexcept { + if (_code) sync(); +} + +// ============================================================================ +// [asmjit::Assembler - Events] +// ============================================================================ + +Error Assembler::onAttach(CodeHolder* code) noexcept { + // Attach to the end of the .text section. + _section = code->_sections[0]; + uint8_t* p = _section->_buffer._data; + + _bufferData = p; + _bufferEnd = p + _section->_buffer._capacity; + _bufferPtr = p + _section->_buffer._length; + + _op4.reset(); + _op5.reset(); + + return Base::onAttach(code); +} + +Error Assembler::onDetach(CodeHolder* code) noexcept { + _section = nullptr; + _bufferData = nullptr; + _bufferEnd = nullptr; + _bufferPtr = nullptr; + + _op4.reset(); + _op5.reset(); + + return Base::onDetach(code); +} + +// ============================================================================ +// [asmjit::Assembler - Code-Generation] +// ============================================================================ + +Error Assembler::_emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, const Operand_& o4, const Operand_& o5) { + _op4 = o4; + _op5 = o5; + _options |= kOptionOp4Op5Used; + return _emit(instId, o0, o1, o2, o3); +} + +Error Assembler::_emitOpArray(uint32_t instId, const Operand_* opArray, size_t opCount) { + const Operand_* op = opArray; + switch (opCount) { + case 0: return _emit(instId, _none, _none, _none, _none); + case 1: return _emit(instId, op[0], _none, _none, _none); + case 2: return _emit(instId, op[0], op[1], _none, _none); + case 3: return _emit(instId, op[0], op[1], op[2], _none); + case 4: return _emit(instId, op[0], op[1], op[2], op[3]); + + case 5: + _op4 = op[4]; + _op5.reset(); + _options |= kOptionOp4Op5Used; + return _emit(instId, op[0], op[1], op[2], op[3]); + + case 6: + _op4 = op[4]; + _op5 = op[5]; + _options |= kOptionOp4Op5Used; + return _emit(instId, op[0], op[1], op[2], op[3]); + + default: + return DebugUtils::errored(kErrorInvalidArgument); + } +} + +// ============================================================================ +// [asmjit::Assembler - Sync] +// ============================================================================ + +void Assembler::sync() noexcept { + ASMJIT_ASSERT(_code != nullptr); // Only called by CodeHolder, so we must be attached. + ASMJIT_ASSERT(_section != nullptr); // One section must always be active, no matter what. + ASMJIT_ASSERT(_bufferData == _section->_buffer._data); // `_bufferStart` is a shortcut to `_section->buffer.data`. + + // Update only if the current offset is greater than the section length. + size_t offset = (size_t)(_bufferPtr - _bufferData); + if (_section->getBuffer().getLength() < offset) + _section->_buffer._length = offset; +} + +// ============================================================================ +// [asmjit::Assembler - Code-Buffer] +// ============================================================================ + +Error Assembler::setOffset(size_t offset) { + if (_lastError) return _lastError; + + size_t length = std::max(_section->getBuffer().getLength(), getOffset()); + if (ASMJIT_UNLIKELY(offset > length)) + return setLastError(DebugUtils::errored(kErrorInvalidArgument)); + + // If the `Assembler` generated any code the `_bufferPtr` may be higher than + // the section length stored in `CodeHolder` as it doesn't update it each + // time it generates machine code. This is the same as calling `sync()`. + if (_section->_buffer._length < length) + _section->_buffer._length = length; + + _bufferPtr = _bufferData + offset; + return kErrorOk; +} + +// ============================================================================ +// [asmjit::Assembler - Comment] +// ============================================================================ + +Error Assembler::comment(const char* s, size_t len) { + if (_lastError) return _lastError; + +#if !defined(ASMJIT_DISABLE_LOGGING) + if (_globalOptions & kOptionLoggingEnabled) { + Logger* logger = _code->getLogger(); + logger->log(s, len); + logger->log("\n", 1); + return kErrorOk; + } +#else + ASMJIT_UNUSED(s); + ASMJIT_UNUSED(len); +#endif + + return kErrorOk; +} + +// ============================================================================ +// [asmjit::Assembler - Building Blocks] +// ============================================================================ + +Label Assembler::newLabel() { + uint32_t id = 0; + if (!_lastError) { + ASMJIT_ASSERT(_code != nullptr); + Error err = _code->newLabelId(id); + if (ASMJIT_UNLIKELY(err)) setLastError(err); + } + return Label(id); +} + +Label Assembler::newNamedLabel(const char* name, size_t nameLength, uint32_t type, uint32_t parentId) { + uint32_t id = 0; + if (!_lastError) { + ASMJIT_ASSERT(_code != nullptr); + Error err = _code->newNamedLabelId(id, name, nameLength, type, parentId); + if (ASMJIT_UNLIKELY(err)) setLastError(err); + } + return Label(id); +} + +Error Assembler::bind(const Label& label) { + if (_lastError) return _lastError; + ASMJIT_ASSERT(_code != nullptr); + + LabelEntry* le = _code->getLabelEntry(label); + if (ASMJIT_UNLIKELY(!le)) + return setLastError(DebugUtils::errored(kErrorInvalidLabel)); + + // Label can be bound only once. + if (ASMJIT_UNLIKELY(le->isBound())) + return setLastError(DebugUtils::errored(kErrorLabelAlreadyBound)); + +#if !defined(ASMJIT_DISABLE_LOGGING) + if (_globalOptions & kOptionLoggingEnabled) { + StringBuilderTmp<256> sb; + if (le->hasName()) + sb.setFormat("%s:", le->getName()); + else + sb.setFormat("L%u:", Operand::unpackId(label.getId())); + + size_t binSize = 0; + if (!_code->_logger->hasOption(Logger::kOptionBinaryForm)) + binSize = Globals::kInvalidIndex; + + Logging::formatLine(sb, nullptr, binSize, 0, 0, getInlineComment()); + _code->_logger->log(sb.getData(), sb.getLength()); + } +#endif // !ASMJIT_DISABLE_LOGGING + + Error err = kErrorOk; + size_t pos = getOffset(); + + LabelLink* link = le->_links; + LabelLink* prev = nullptr; + + while (link) { + intptr_t offset = link->offset; + uint32_t relocId = link->relocId; + + if (relocId != RelocEntry::kInvalidId) { + // Adjust relocation data. + RelocEntry* re = _code->_relocations[relocId]; + re->_data += static_cast(pos); + } + else { + // Not using relocId, this means that we are overwriting a real + // displacement in the CodeBuffer. + int32_t patchedValue = static_cast( + static_cast(pos) - offset + link->rel); + + // Size of the value we are going to patch. Only BYTE/DWORD is allowed. + uint32_t size = _bufferData[offset]; + if (size == 4) + Utils::writeI32u(_bufferData + offset, static_cast(patchedValue)); + else if (size == 1 && Utils::isInt8(patchedValue)) + _bufferData[offset] = static_cast(patchedValue & 0xFF); + else + err = DebugUtils::errored(kErrorInvalidDisplacement); + } + + prev = link->prev; + _code->_unresolvedLabelsCount--; + _code->_baseHeap.release(link, sizeof(LabelLink)); + + link = prev; + } + + // Set as bound. + le->_sectionId = _section->getId(); + le->_offset = pos; + le->_links = nullptr; + resetInlineComment(); + + if (err != kErrorOk) + return setLastError(err); + + return kErrorOk; +} + +Error Assembler::embed(const void* data, uint32_t size) { + if (_lastError) return _lastError; + + if (getRemainingSpace() < size) { + Error err = _code->growBuffer(&_section->_buffer, size); + if (ASMJIT_UNLIKELY(err != kErrorOk)) return setLastError(err); + } + + ::memcpy(_bufferPtr, data, size); + _bufferPtr += size; + +#if !defined(ASMJIT_DISABLE_LOGGING) + if (_globalOptions & kOptionLoggingEnabled) + _code->_logger->logBinary(data, size); +#endif // !ASMJIT_DISABLE_LOGGING + + return kErrorOk; +} + +Error Assembler::embedLabel(const Label& label) { + if (_lastError) return _lastError; + ASMJIT_ASSERT(_code != nullptr); + + RelocEntry* re; + LabelEntry* le = _code->getLabelEntry(label); + + if (ASMJIT_UNLIKELY(!le)) + return setLastError(DebugUtils::errored(kErrorInvalidLabel)); + + Error err; + uint32_t gpSize = getGpSize(); + + if (getRemainingSpace() < gpSize) { + err = _code->growBuffer(&_section->_buffer, gpSize); + if (ASMJIT_UNLIKELY(err)) return setLastError(err); + } + +#if !defined(ASMJIT_DISABLE_LOGGING) + if (_globalOptions & kOptionLoggingEnabled) + _code->_logger->logf(gpSize == 4 ? ".dd L%u\n" : ".dq L%u\n", Operand::unpackId(label.getId())); +#endif // !ASMJIT_DISABLE_LOGGING + + err = _code->newRelocEntry(&re, RelocEntry::kTypeRelToAbs, gpSize); + if (ASMJIT_UNLIKELY(err)) return setLastError(err); + + re->_sourceSectionId = _section->getId(); + re->_sourceOffset = static_cast(getOffset()); + + if (le->isBound()) { + re->_targetSectionId = le->getSectionId(); + re->_data = static_cast(static_cast(le->getOffset())); + } + else { + LabelLink* link = _code->newLabelLink(le, _section->getId(), getOffset(), 0); + if (ASMJIT_UNLIKELY(!link)) + return setLastError(DebugUtils::errored(kErrorNoHeapMemory)); + link->relocId = re->getId(); + } + + // Emit dummy DWORD/QWORD depending on the address size. + ::memset(_bufferPtr, 0, gpSize); + _bufferPtr += gpSize; + + return kErrorOk; +} + +Error Assembler::embedConstPool(const Label& label, const ConstPool& pool) { + if (_lastError) return _lastError; + + if (!isLabelValid(label)) + return DebugUtils::errored(kErrorInvalidLabel); + + ASMJIT_PROPAGATE(align(kAlignData, static_cast(pool.getAlignment()))); + ASMJIT_PROPAGATE(bind(label)); + + size_t size = pool.getSize(); + if (getRemainingSpace() < size) { + Error err = _code->growBuffer(&_section->_buffer, size); + if (ASMJIT_UNLIKELY(err)) return setLastError(err); + } + + uint8_t* p = _bufferPtr; + pool.fill(p); + +#if !defined(ASMJIT_DISABLE_LOGGING) + if (_globalOptions & kOptionLoggingEnabled) + _code->_logger->logBinary(p, size); +#endif // !ASMJIT_DISABLE_LOGGING + + _bufferPtr += size; + return kErrorOk; +} + +// ============================================================================ +// [asmjit::Assembler - Emit-Helpers] +// ============================================================================ + +#if !defined(ASMJIT_DISABLE_LOGGING) +void Assembler::_emitLog( + uint32_t instId, uint32_t options, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, + uint32_t relSize, uint32_t imLen, uint8_t* afterCursor) { + + Logger* logger = _code->getLogger(); + ASMJIT_ASSERT(logger != nullptr); + ASMJIT_ASSERT(options & CodeEmitter::kOptionLoggingEnabled); + + StringBuilderTmp<256> sb; + uint32_t logOptions = logger->getOptions(); + + uint8_t* beforeCursor = _bufferPtr; + intptr_t emittedSize = (intptr_t)(afterCursor - beforeCursor); + + sb.appendString(logger->getIndentation()); + + Operand_ opArray[6]; + opArray[0].copyFrom(o0); + opArray[1].copyFrom(o1); + opArray[2].copyFrom(o2); + opArray[3].copyFrom(o3); + + if (options & kOptionOp4Op5Used) { + opArray[4].copyFrom(_op4); + opArray[5].copyFrom(_op5); + } + else { + opArray[4].reset(); + opArray[5].reset(); + } + + Logging::formatInstruction( + sb, logOptions, + this, getArchType(), + Inst::Detail(instId, options, _extraReg), opArray, 6); + + if ((logOptions & Logger::kOptionBinaryForm) != 0) + Logging::formatLine(sb, _bufferPtr, emittedSize, relSize, imLen, getInlineComment()); + else + Logging::formatLine(sb, nullptr, Globals::kInvalidIndex, 0, 0, getInlineComment()); + + logger->log(sb.getData(), sb.getLength()); +} + +Error Assembler::_emitFailed( + Error err, + uint32_t instId, uint32_t options, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3) { + + StringBuilderTmp<256> sb; + sb.appendString(DebugUtils::errorAsString(err)); + sb.appendString(": "); + + Operand_ opArray[6]; + opArray[0].copyFrom(o0); + opArray[1].copyFrom(o1); + opArray[2].copyFrom(o2); + opArray[3].copyFrom(o3); + + if (options & kOptionOp4Op5Used) { + opArray[4].copyFrom(_op4); + opArray[5].copyFrom(_op5); + } + else { + opArray[4].reset(); + opArray[5].reset(); + } + + Logging::formatInstruction( + sb, 0, + this, getArchType(), + Inst::Detail(instId, options, _extraReg), opArray, 6); + + resetOptions(); + resetExtraReg(); + resetInlineComment(); + return setLastError(err, sb.getData()); +} +#endif + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" diff --git a/libraries/asmjit/asmjit/base/assembler.h b/libraries/asmjit/asmjit/base/assembler.h new file mode 100644 index 00000000000..55fbb142e3d --- /dev/null +++ b/libraries/asmjit/asmjit/base/assembler.h @@ -0,0 +1,154 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Guard] +#ifndef _ASMJIT_BASE_ASSEMBLER_H +#define _ASMJIT_BASE_ASSEMBLER_H + +// [Dependencies] +#include "../base/codeemitter.h" +#include "../base/codeholder.h" +#include "../base/operand.h" +#include "../base/simdtypes.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +//! \addtogroup asmjit_base +//! \{ + +// ============================================================================ +// [asmjit::Assembler] +// ============================================================================ + +//! Base assembler. +//! +//! This class implements a base interface that is used by architecture +//! specific assemblers. +//! +//! \sa CodeCompiler. +class ASMJIT_VIRTAPI Assembler : public CodeEmitter { +public: + ASMJIT_NONCOPYABLE(Assembler) + typedef CodeEmitter Base; + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a new `Assembler` instance. + ASMJIT_API Assembler() noexcept; + //! Destroy the `Assembler` instance. + ASMJIT_API virtual ~Assembler() noexcept; + + // -------------------------------------------------------------------------- + // [Events] + // -------------------------------------------------------------------------- + + ASMJIT_API Error onAttach(CodeHolder* code) noexcept override; + ASMJIT_API Error onDetach(CodeHolder* code) noexcept override; + + // -------------------------------------------------------------------------- + // [Code-Generation] + // -------------------------------------------------------------------------- + + using CodeEmitter::_emit; + + ASMJIT_API Error _emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, const Operand_& o4, const Operand_& o5) override; + ASMJIT_API Error _emitOpArray(uint32_t instId, const Operand_* opArray, size_t opCount) override; + + // -------------------------------------------------------------------------- + // [Code-Buffer] + // -------------------------------------------------------------------------- + + //! Called by \ref CodeHolder::sync(). + ASMJIT_API virtual void sync() noexcept; + + //! Get the capacity of the current CodeBuffer. + ASMJIT_INLINE size_t getBufferCapacity() const noexcept { return (size_t)(_bufferEnd - _bufferData); } + //! Get the number of remaining bytes in the current CodeBuffer. + ASMJIT_INLINE size_t getRemainingSpace() const noexcept { return (size_t)(_bufferEnd - _bufferPtr); } + + //! Get the current position in the CodeBuffer. + ASMJIT_INLINE size_t getOffset() const noexcept { return (size_t)(_bufferPtr - _bufferData); } + //! Set the current position in the CodeBuffer to `offset`. + //! + //! NOTE: The `offset` cannot be outside of the buffer length (even if it's + //! within buffer's capacity). + ASMJIT_API Error setOffset(size_t offset); + + //! Get start of the CodeBuffer of the current section. + ASMJIT_INLINE uint8_t* getBufferData() const noexcept { return _bufferData; } + //! Get end (first invalid byte) of the current section. + ASMJIT_INLINE uint8_t* getBufferEnd() const noexcept { return _bufferEnd; } + //! Get pointer in the CodeBuffer of the current section. + ASMJIT_INLINE uint8_t* getBufferPtr() const noexcept { return _bufferPtr; } + + // -------------------------------------------------------------------------- + // [Code-Generation] + // -------------------------------------------------------------------------- + + ASMJIT_API Label newLabel() override; + ASMJIT_API Label newNamedLabel( + const char* name, + size_t nameLength = Globals::kInvalidIndex, + uint32_t type = Label::kTypeGlobal, + uint32_t parentId = 0) override; + ASMJIT_API Error bind(const Label& label) override; + ASMJIT_API Error embed(const void* data, uint32_t size) override; + ASMJIT_API Error embedLabel(const Label& label) override; + ASMJIT_API Error embedConstPool(const Label& label, const ConstPool& pool) override; + ASMJIT_API Error comment(const char* s, size_t len = Globals::kInvalidIndex) override; + + // -------------------------------------------------------------------------- + // [Emit-Helpers] + // -------------------------------------------------------------------------- + +protected: +#if !defined(ASMJIT_DISABLE_LOGGING) + void _emitLog( + uint32_t instId, uint32_t options, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, + uint32_t relSize, uint32_t imLen, uint8_t* afterCursor); + + Error _emitFailed( + Error err, + uint32_t instId, uint32_t options, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3); +#else + ASMJIT_INLINE Error _emitFailed( + uint32_t err, + uint32_t instId, uint32_t options, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3) { + + resetOptions(); + resetInlineComment(); + return setLastError(err); + } +#endif + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + +public: + SectionEntry* _section; //!< Current section where the assembling happens. + uint8_t* _bufferData; //!< Start of the CodeBuffer of the current section. + uint8_t* _bufferEnd; //!< End (first invalid byte) of the current section. + uint8_t* _bufferPtr; //!< Pointer in the CodeBuffer of the current section. + + Operand_ _op4; //!< 5th operand data, used only temporarily. + Operand_ _op5; //!< 6th operand data, used only temporarily. +}; + +//! \} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" + +// [Guard] +#endif // _ASMJIT_BASE_ASSEMBLER_H diff --git a/libraries/asmjit/asmjit/base/codebuilder.cpp b/libraries/asmjit/asmjit/base/codebuilder.cpp new file mode 100644 index 00000000000..1f0024833e8 --- /dev/null +++ b/libraries/asmjit/asmjit/base/codebuilder.cpp @@ -0,0 +1,584 @@ +// [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 "../asmjit_build.h" +#if !defined(ASMJIT_DISABLE_BUILDER) + +// [Dependencies] +#include "../base/codebuilder.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +// ============================================================================ +// [asmjit::CodeBuilder - Construction / Destruction] +// ============================================================================ + +CodeBuilder::CodeBuilder() noexcept + : CodeEmitter(kTypeBuilder), + _cbBaseZone(32768 - Zone::kZoneOverhead), + _cbDataZone(16384 - Zone::kZoneOverhead), + _cbPassZone(32768 - Zone::kZoneOverhead), + _cbHeap(&_cbBaseZone), + _cbPasses(), + _cbLabels(), + _firstNode(nullptr), + _lastNode(nullptr), + _cursor(nullptr), + _position(0), + _nodeFlags(0) {} +CodeBuilder::~CodeBuilder() noexcept {} + +// ============================================================================ +// [asmjit::CodeBuilder - Events] +// ============================================================================ + +Error CodeBuilder::onAttach(CodeHolder* code) noexcept { + return Base::onAttach(code); +} + +Error CodeBuilder::onDetach(CodeHolder* code) noexcept { + _cbPasses.reset(); + _cbLabels.reset(); + _cbHeap.reset(&_cbBaseZone); + + _cbBaseZone.reset(false); + _cbDataZone.reset(false); + _cbPassZone.reset(false); + + _position = 0; + _nodeFlags = 0; + + _firstNode = nullptr; + _lastNode = nullptr; + _cursor = nullptr; + + return Base::onDetach(code); +} + +// ============================================================================ +// [asmjit::CodeBuilder - Node-Factory] +// ============================================================================ + +Error CodeBuilder::getCBLabel(CBLabel** pOut, uint32_t id) noexcept { + if (_lastError) return _lastError; + ASMJIT_ASSERT(_code != nullptr); + + size_t index = Operand::unpackId(id); + if (ASMJIT_UNLIKELY(index >= _code->getLabelsCount())) + return DebugUtils::errored(kErrorInvalidLabel); + + if (index >= _cbLabels.getLength()) + ASMJIT_PROPAGATE(_cbLabels.resize(&_cbHeap, index + 1)); + + CBLabel* node = _cbLabels[index]; + if (!node) { + node = newNodeT(id); + if (ASMJIT_UNLIKELY(!node)) + return DebugUtils::errored(kErrorNoHeapMemory); + _cbLabels[index] = node; + } + + *pOut = node; + return kErrorOk; +} + +Error CodeBuilder::registerLabelNode(CBLabel* node) noexcept { + if (_lastError) return _lastError; + ASMJIT_ASSERT(_code != nullptr); + + // Don't call setLastError() from here, we are noexcept and we are called + // by `newLabelNode()` and `newFuncNode()`, which are noexcept as well. + uint32_t id; + ASMJIT_PROPAGATE(_code->newLabelId(id)); + size_t index = Operand::unpackId(id); + + // We just added one label so it must be true. + ASMJIT_ASSERT(_cbLabels.getLength() < index + 1); + ASMJIT_PROPAGATE(_cbLabels.resize(&_cbHeap, index + 1)); + + _cbLabels[index] = node; + node->_id = id; + return kErrorOk; +} + +CBLabel* CodeBuilder::newLabelNode() noexcept { + CBLabel* node = newNodeT(); + if (!node || registerLabelNode(node) != kErrorOk) + return nullptr; + return node; +} + +CBAlign* CodeBuilder::newAlignNode(uint32_t mode, uint32_t alignment) noexcept { + return newNodeT(mode, alignment); +} + +CBData* CodeBuilder::newDataNode(const void* data, uint32_t size) noexcept { + if (size > CBData::kInlineBufferSize) { + void* cloned = _cbDataZone.alloc(size); + if (!cloned) return nullptr; + + if (data) ::memcpy(cloned, data, size); + data = cloned; + } + + return newNodeT(const_cast(data), size); +} + +CBConstPool* CodeBuilder::newConstPool() noexcept { + CBConstPool* node = newNodeT(); + if (!node || registerLabelNode(node) != kErrorOk) + return nullptr; + return node; +} + +CBComment* CodeBuilder::newCommentNode(const char* s, size_t len) noexcept { + if (s) { + if (len == Globals::kInvalidIndex) len = ::strlen(s); + if (len > 0) { + s = static_cast(_cbDataZone.dup(s, len, true)); + if (!s) return nullptr; + } + } + + return newNodeT(s); +} + +// ============================================================================ +// [asmjit::CodeBuilder - Code-Emitter] +// ============================================================================ + +Label CodeBuilder::newLabel() { + uint32_t id = kInvalidValue; + + if (!_lastError) { + CBLabel* node = newNodeT(id); + if (ASMJIT_UNLIKELY(!node)) { + setLastError(DebugUtils::errored(kErrorNoHeapMemory)); + } + else { + Error err = registerLabelNode(node); + if (ASMJIT_UNLIKELY(err)) + setLastError(err); + else + id = node->getId(); + } + } + + return Label(id); +} + +Label CodeBuilder::newNamedLabel(const char* name, size_t nameLength, uint32_t type, uint32_t parentId) { + uint32_t id = kInvalidValue; + + if (!_lastError) { + CBLabel* node = newNodeT(id); + if (ASMJIT_UNLIKELY(!node)) { + setLastError(DebugUtils::errored(kErrorNoHeapMemory)); + } + else { + Error err = _code->newNamedLabelId(id, name, nameLength, type, parentId); + if (ASMJIT_UNLIKELY(err)) + setLastError(err); + else + id = node->getId(); + } + } + + return Label(id); +} + +Error CodeBuilder::bind(const Label& label) { + if (_lastError) return _lastError; + + CBLabel* node; + Error err = getCBLabel(&node, label); + if (ASMJIT_UNLIKELY(err)) + return setLastError(err); + + addNode(node); + return kErrorOk; +} + +Error CodeBuilder::align(uint32_t mode, uint32_t alignment) { + if (_lastError) return _lastError; + + CBAlign* node = newAlignNode(mode, alignment); + if (ASMJIT_UNLIKELY(!node)) + return setLastError(DebugUtils::errored(kErrorNoHeapMemory)); + + addNode(node); + return kErrorOk; +} + +Error CodeBuilder::embed(const void* data, uint32_t size) { + if (_lastError) return _lastError; + + CBData* node = newDataNode(data, size); + if (ASMJIT_UNLIKELY(!node)) + return setLastError(DebugUtils::errored(kErrorNoHeapMemory)); + + addNode(node); + return kErrorOk; +} + +Error CodeBuilder::embedLabel(const Label& label) { + if (_lastError) return _lastError; + + CBLabelData* node = newNodeT(label.getId()); + if (ASMJIT_UNLIKELY(!node)) + return setLastError(DebugUtils::errored(kErrorNoHeapMemory)); + + addNode(node); + return kErrorOk; +} + +Error CodeBuilder::embedConstPool(const Label& label, const ConstPool& pool) { + if (_lastError) return _lastError; + + if (!isLabelValid(label)) + return setLastError(DebugUtils::errored(kErrorInvalidLabel)); + + ASMJIT_PROPAGATE(align(kAlignData, static_cast(pool.getAlignment()))); + ASMJIT_PROPAGATE(bind(label)); + + CBData* node = newDataNode(nullptr, static_cast(pool.getSize())); + if (ASMJIT_UNLIKELY(!node)) + return setLastError(DebugUtils::errored(kErrorNoHeapMemory)); + + pool.fill(node->getData()); + addNode(node); + return kErrorOk; +} + +Error CodeBuilder::comment(const char* s, size_t len) { + if (_lastError) return _lastError; + + CBComment* node = newCommentNode(s, len); + if (ASMJIT_UNLIKELY(!node)) + return setLastError(DebugUtils::errored(kErrorNoHeapMemory)); + + addNode(node); + return kErrorOk; +} + +// ============================================================================ +// [asmjit::CodeBuilder - Node-Management] +// ============================================================================ + +CBNode* CodeBuilder::addNode(CBNode* node) noexcept { + ASMJIT_ASSERT(node); + ASMJIT_ASSERT(node->_prev == nullptr); + ASMJIT_ASSERT(node->_next == nullptr); + + if (!_cursor) { + if (!_firstNode) { + _firstNode = node; + _lastNode = node; + } + else { + node->_next = _firstNode; + _firstNode->_prev = node; + _firstNode = node; + } + } + else { + CBNode* prev = _cursor; + CBNode* next = _cursor->_next; + + node->_prev = prev; + node->_next = next; + + prev->_next = node; + if (next) + next->_prev = node; + else + _lastNode = node; + } + + _cursor = node; + return node; +} + +CBNode* CodeBuilder::addAfter(CBNode* node, CBNode* ref) noexcept { + ASMJIT_ASSERT(node); + ASMJIT_ASSERT(ref); + + ASMJIT_ASSERT(node->_prev == nullptr); + ASMJIT_ASSERT(node->_next == nullptr); + + CBNode* prev = ref; + CBNode* next = ref->_next; + + node->_prev = prev; + node->_next = next; + + prev->_next = node; + if (next) + next->_prev = node; + else + _lastNode = node; + + return node; +} + +CBNode* CodeBuilder::addBefore(CBNode* node, CBNode* ref) noexcept { + ASMJIT_ASSERT(node != nullptr); + ASMJIT_ASSERT(node->_prev == nullptr); + ASMJIT_ASSERT(node->_next == nullptr); + ASMJIT_ASSERT(ref != nullptr); + + CBNode* prev = ref->_prev; + CBNode* next = ref; + + node->_prev = prev; + node->_next = next; + + next->_prev = node; + if (prev) + prev->_next = node; + else + _firstNode = node; + + return node; +} + +static ASMJIT_INLINE void CodeBuilder_nodeRemoved(CodeBuilder* self, CBNode* node_) noexcept { + if (node_->isJmpOrJcc()) { + CBJump* node = static_cast(node_); + CBLabel* label = node->getTarget(); + + if (label) { + // Disconnect. + CBJump** pPrev = &label->_from; + for (;;) { + ASMJIT_ASSERT(*pPrev != nullptr); + + CBJump* current = *pPrev; + if (!current) break; + + if (current == node) { + *pPrev = node->_jumpNext; + break; + } + + pPrev = ¤t->_jumpNext; + } + + label->subNumRefs(); + } + } +} + +CBNode* CodeBuilder::removeNode(CBNode* node) noexcept { + CBNode* prev = node->_prev; + CBNode* next = node->_next; + + if (_firstNode == node) + _firstNode = next; + else + prev->_next = next; + + if (_lastNode == node) + _lastNode = prev; + else + next->_prev = prev; + + node->_prev = nullptr; + node->_next = nullptr; + + if (_cursor == node) + _cursor = prev; + CodeBuilder_nodeRemoved(this, node); + + return node; +} + +void CodeBuilder::removeNodes(CBNode* first, CBNode* last) noexcept { + if (first == last) { + removeNode(first); + return; + } + + CBNode* prev = first->_prev; + CBNode* next = last->_next; + + if (_firstNode == first) + _firstNode = next; + else + prev->_next = next; + + if (_lastNode == last) + _lastNode = prev; + else + next->_prev = prev; + + CBNode* node = first; + for (;;) { + CBNode* next = node->getNext(); + ASMJIT_ASSERT(next != nullptr); + + node->_prev = nullptr; + node->_next = nullptr; + + if (_cursor == node) + _cursor = prev; + CodeBuilder_nodeRemoved(this, node); + + if (node == last) + break; + node = next; + } +} + +CBNode* CodeBuilder::setCursor(CBNode* node) noexcept { + CBNode* old = _cursor; + _cursor = node; + return old; +} + +// ============================================================================ +// [asmjit::CodeBuilder - Passes] +// ============================================================================ + +ASMJIT_FAVOR_SIZE CBPass* CodeBuilder::getPassByName(const char* name) const noexcept { + for (size_t i = 0, len = _cbPasses.getLength(); i < len; i++) { + CBPass* pass = _cbPasses[i]; + if (::strcmp(pass->getName(), name) == 0) + return pass; + } + + return nullptr; +} + +ASMJIT_FAVOR_SIZE Error CodeBuilder::addPass(CBPass* pass) noexcept { + if (ASMJIT_UNLIKELY(pass == nullptr)) { + // Since this is directly called by `addPassT()` we treat `null` argument + // as out-of-memory condition. Otherwise it would be API misuse. + return DebugUtils::errored(kErrorNoHeapMemory); + } + else if (ASMJIT_UNLIKELY(pass->_cb)) { + // Kind of weird, but okay... + if (pass->_cb == this) + return kErrorOk; + return DebugUtils::errored(kErrorInvalidState); + } + + ASMJIT_PROPAGATE(_cbPasses.append(&_cbHeap, pass)); + pass->_cb = this; + return kErrorOk; +} + +ASMJIT_FAVOR_SIZE Error CodeBuilder::deletePass(CBPass* pass) noexcept { + if (ASMJIT_UNLIKELY(pass == nullptr)) + return DebugUtils::errored(kErrorInvalidArgument); + + if (pass->_cb != nullptr) { + if (pass->_cb != this) + return DebugUtils::errored(kErrorInvalidState); + + size_t index = _cbPasses.indexOf(pass); + ASMJIT_ASSERT(index != Globals::kInvalidIndex); + + pass->_cb = nullptr; + _cbPasses.removeAt(index); + } + + pass->~CBPass(); + return kErrorOk; +} + +// ============================================================================ +// [asmjit::CodeBuilder - Serialization] +// ============================================================================ + +Error CodeBuilder::serialize(CodeEmitter* dst) { + Error err = kErrorOk; + CBNode* node_ = getFirstNode(); + + do { + dst->setInlineComment(node_->getInlineComment()); + + switch (node_->getType()) { + case CBNode::kNodeAlign: { + CBAlign* node = static_cast(node_); + err = dst->align(node->getMode(), node->getAlignment()); + break; + } + + case CBNode::kNodeData: { + CBData* node = static_cast(node_); + err = dst->embed(node->getData(), node->getSize()); + break; + } + + case CBNode::kNodeFunc: + case CBNode::kNodeLabel: { + CBLabel* node = static_cast(node_); + err = dst->bind(node->getLabel()); + break; + } + + case CBNode::kNodeLabelData: { + CBLabelData* node = static_cast(node_); + err = dst->embedLabel(node->getLabel()); + break; + } + + case CBNode::kNodeConstPool: { + CBConstPool* node = static_cast(node_); + err = dst->embedConstPool(node->getLabel(), node->getConstPool()); + break; + } + + case CBNode::kNodeInst: + case CBNode::kNodeFuncCall: { + CBInst* node = node_->as(); + dst->setOptions(node->getOptions()); + dst->setExtraReg(node->getExtraReg()); + err = dst->emitOpArray(node->getInstId(), node->getOpArray(), node->getOpCount()); + break; + } + + case CBNode::kNodeComment: { + CBComment* node = static_cast(node_); + err = dst->comment(node->getInlineComment()); + break; + } + + default: + break; + } + + if (err) break; + node_ = node_->getNext(); + } while (node_); + + return err; +} + +// ============================================================================ +// [asmjit::CBPass] +// ============================================================================ + +CBPass::CBPass(const char* name) noexcept + : _cb(nullptr), + _name(name) {} +CBPass::~CBPass() noexcept {} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" + +// [Guard] +#endif // !ASMJIT_DISABLE_BUILDER diff --git a/libraries/asmjit/asmjit/base/codebuilder.h b/libraries/asmjit/asmjit/base/codebuilder.h new file mode 100644 index 00000000000..231dd8449fd --- /dev/null +++ b/libraries/asmjit/asmjit/base/codebuilder.h @@ -0,0 +1,915 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Guard] +#ifndef _ASMJIT_BASE_CODEBUILDER_H +#define _ASMJIT_BASE_CODEBUILDER_H + +#include "../asmjit_build.h" +#if !defined(ASMJIT_DISABLE_BUILDER) + +// [Dependencies] +#include "../base/assembler.h" +#include "../base/codeholder.h" +#include "../base/constpool.h" +#include "../base/inst.h" +#include "../base/operand.h" +#include "../base/utils.h" +#include "../base/zone.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +// ============================================================================ +// [Forward Declarations] +// ============================================================================ + +class CBNode; +class CBPass; + +class CBAlign; +class CBComment; +class CBConstPool; +class CBData; +class CBInst; +class CBJump; +class CBLabel; +class CBLabelData; +class CBSentinel; + +//! \addtogroup asmjit_base +//! \{ + +// ============================================================================ +// [asmjit::CodeBuilder] +// ============================================================================ + +class ASMJIT_VIRTAPI CodeBuilder : public CodeEmitter { +public: + ASMJIT_NONCOPYABLE(CodeBuilder) + typedef CodeEmitter Base; + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a new `CodeBuilder` instance. + ASMJIT_API CodeBuilder() noexcept; + //! Destroy the `CodeBuilder` instance. + ASMJIT_API virtual ~CodeBuilder() noexcept; + + // -------------------------------------------------------------------------- + // [Events] + // -------------------------------------------------------------------------- + + ASMJIT_API virtual Error onAttach(CodeHolder* code) noexcept override; + ASMJIT_API virtual Error onDetach(CodeHolder* code) noexcept override; + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get a vector of CBPass objects that will be executed by `process()`. + ASMJIT_INLINE const ZoneVector& getPasses() const noexcept { return _cbPasses; } + + //! Get a vector of CBLabel nodes. + //! + //! NOTE: If a label of some index is not associated with `CodeBuilder` it + //! would be null, so always check for nulls if you iterate over the vector. + ASMJIT_INLINE const ZoneVector& getLabels() const noexcept { return _cbLabels; } + + //! Get the first node. + ASMJIT_INLINE CBNode* getFirstNode() const noexcept { return _firstNode; } + //! Get the last node. + ASMJIT_INLINE CBNode* getLastNode() const noexcept { return _lastNode; } + + // -------------------------------------------------------------------------- + // [Node-Management] + // -------------------------------------------------------------------------- + + //! \internal + template + ASMJIT_INLINE T* newNodeT() noexcept { return new(_cbHeap.alloc(sizeof(T))) T(this); } + + //! \internal + template + ASMJIT_INLINE T* newNodeT(P0 p0) noexcept { return new(_cbHeap.alloc(sizeof(T))) T(this, p0); } + + //! \internal + template + ASMJIT_INLINE T* newNodeT(P0 p0, P1 p1) noexcept { return new(_cbHeap.alloc(sizeof(T))) T(this, p0, p1); } + + //! \internal + template + ASMJIT_INLINE T* newNodeT(P0 p0, P1 p1, P2 p2) noexcept { return new(_cbHeap.alloc(sizeof(T))) T(this, p0, p1, p2); } + + ASMJIT_API Error registerLabelNode(CBLabel* node) noexcept; + //! Get `CBLabel` by `id`. + ASMJIT_API Error getCBLabel(CBLabel** pOut, uint32_t id) noexcept; + //! Get `CBLabel` by `label`. + ASMJIT_INLINE Error getCBLabel(CBLabel** pOut, const Label& label) noexcept { return getCBLabel(pOut, label.getId()); } + + //! Create a new \ref CBLabel node. + ASMJIT_API CBLabel* newLabelNode() noexcept; + //! Create a new \ref CBAlign node. + ASMJIT_API CBAlign* newAlignNode(uint32_t mode, uint32_t alignment) noexcept; + //! Create a new \ref CBData node. + ASMJIT_API CBData* newDataNode(const void* data, uint32_t size) noexcept; + //! Create a new \ref CBConstPool node. + ASMJIT_API CBConstPool* newConstPool() noexcept; + //! Create a new \ref CBComment node. + ASMJIT_API CBComment* newCommentNode(const char* s, size_t len) noexcept; + + // -------------------------------------------------------------------------- + // [Code-Emitter] + // -------------------------------------------------------------------------- + + ASMJIT_API virtual Label newLabel() override; + ASMJIT_API virtual Label newNamedLabel(const char* name, size_t nameLength = Globals::kInvalidIndex, uint32_t type = Label::kTypeGlobal, uint32_t parentId = kInvalidValue) override; + ASMJIT_API virtual Error bind(const Label& label) override; + ASMJIT_API virtual Error align(uint32_t mode, uint32_t alignment) override; + ASMJIT_API virtual Error embed(const void* data, uint32_t size) override; + ASMJIT_API virtual Error embedLabel(const Label& label) override; + ASMJIT_API virtual Error embedConstPool(const Label& label, const ConstPool& pool) override; + ASMJIT_API virtual Error comment(const char* s, size_t len = Globals::kInvalidIndex) override; + + // -------------------------------------------------------------------------- + // [Node-Management] + // -------------------------------------------------------------------------- + + //! Add `node` after the current and set current to `node`. + ASMJIT_API CBNode* addNode(CBNode* node) noexcept; + //! Insert `node` after `ref`. + ASMJIT_API CBNode* addAfter(CBNode* node, CBNode* ref) noexcept; + //! Insert `node` before `ref`. + ASMJIT_API CBNode* addBefore(CBNode* node, CBNode* ref) noexcept; + //! Remove `node`. + ASMJIT_API CBNode* removeNode(CBNode* node) noexcept; + //! Remove multiple nodes. + ASMJIT_API void removeNodes(CBNode* first, CBNode* last) noexcept; + + //! Get current node. + //! + //! \note If this method returns null it means that nothing has been + //! emitted yet. + ASMJIT_INLINE CBNode* getCursor() const noexcept { return _cursor; } + //! Set the current node without returning the previous node. + ASMJIT_INLINE void _setCursor(CBNode* node) noexcept { _cursor = node; } + //! Set the current node to `node` and return the previous one. + ASMJIT_API CBNode* setCursor(CBNode* node) noexcept; + + // -------------------------------------------------------------------------- + // [Passes] + // -------------------------------------------------------------------------- + + template + ASMJIT_INLINE T* newPassT() noexcept { return new(_cbBaseZone.alloc(sizeof(T))) T(); } + template + ASMJIT_INLINE T* newPassT(P0 p0) noexcept { return new(_cbBaseZone.alloc(sizeof(T))) T(p0); } + template + ASMJIT_INLINE T* newPassT(P0 p0, P1 p1) noexcept { return new(_cbBaseZone.alloc(sizeof(T))) T(p0, p1); } + + template + ASMJIT_INLINE Error addPassT() noexcept { return addPass(newPassT()); } + template + ASMJIT_INLINE Error addPassT(P0 p0) noexcept { return addPass(newPassT(p0)); } + template + ASMJIT_INLINE Error addPassT(P0 p0, P1 p1) noexcept { return addPass(newPassT(p0, p1)); } + + //! Get a `CBPass` by name. + ASMJIT_API CBPass* getPassByName(const char* name) const noexcept; + //! Add `pass` to the list of passes. + ASMJIT_API Error addPass(CBPass* pass) noexcept; + //! Remove `pass` from the list of passes and delete it. + ASMJIT_API Error deletePass(CBPass* pass) noexcept; + + // -------------------------------------------------------------------------- + // [Serialization] + // -------------------------------------------------------------------------- + + ASMJIT_API virtual Error serialize(CodeEmitter* dst); + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + Zone _cbBaseZone; //!< Base zone used to allocate nodes and `CBPass`. + Zone _cbDataZone; //!< Data zone used to allocate data and names. + Zone _cbPassZone; //!< Zone passed to `CBPass::process()`. + ZoneHeap _cbHeap; //!< ZoneHeap that uses `_cbBaseZone`. + + ZoneVector _cbPasses; //!< Array of `CBPass` objects. + ZoneVector _cbLabels; //!< Maps label indexes to `CBLabel` nodes. + + CBNode* _firstNode; //!< First node of the current section. + CBNode* _lastNode; //!< Last node of the current section. + CBNode* _cursor; //!< Current node (cursor). + + uint32_t _position; //!< Flow-id assigned to each new node. + uint32_t _nodeFlags; //!< Flags assigned to each new node. +}; + +// ============================================================================ +// [asmjit::CBPass] +// ============================================================================ + +//! `CodeBuilder` pass used to code transformations, analysis, and lowering. +class ASMJIT_VIRTAPI CBPass { +public: + ASMJIT_NONCOPYABLE(CBPass); + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + ASMJIT_API CBPass(const char* name) noexcept; + ASMJIT_API virtual ~CBPass() noexcept; + + // -------------------------------------------------------------------------- + // [Interface] + // -------------------------------------------------------------------------- + + //! Process the code stored in CodeBuffer `cb`. + //! + //! This is the only function that is called by the `CodeBuilder` to process + //! the code. It passes the CodeBuilder itself (`cb`) and also a zone memory + //! allocator `zone`, which will be reset after the `process()` returns. The + //! allocator should be used for all allocations as it's fast and everything + //! it allocates will be released at once when `process()` returns. + virtual Error process(Zone* zone) noexcept = 0; + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE const CodeBuilder* cb() const noexcept { return _cb; } + ASMJIT_INLINE const char* getName() const noexcept { return _name; } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + CodeBuilder* _cb; //!< CodeBuilder this pass is assigned to. + const char* _name; //!< Name of the pass. +}; + +// ============================================================================ +// [asmjit::CBNode] +// ============================================================================ + +//! Node (CodeBuilder). +//! +//! Every node represents a building-block used by \ref CodeBuilder. It can be +//! instruction, data, label, comment, directive, or any other high-level +//! representation that can be transformed to the building blocks mentioned. +//! Every class that inherits \ref CodeBuilder can define its own nodes that it +//! can lower to basic nodes. +class CBNode { +public: + ASMJIT_NONCOPYABLE(CBNode) + + // -------------------------------------------------------------------------- + // [Type] + // -------------------------------------------------------------------------- + + //! Type of \ref CBNode. + ASMJIT_ENUM(NodeType) { + kNodeNone = 0, //!< Invalid node (internal, don't use). + + // [CodeBuilder] + kNodeInst = 1, //!< Node is \ref CBInst or \ref CBJump. + kNodeData = 2, //!< Node is \ref CBData. + kNodeAlign = 3, //!< Node is \ref CBAlign. + kNodeLabel = 4, //!< Node is \ref CBLabel. + kNodeLabelData = 5, //!< Node is \ref CBLabelData. + kNodeConstPool = 6, //!< Node is \ref CBConstPool. + kNodeComment = 7, //!< Node is \ref CBComment. + kNodeSentinel = 8, //!< Node is \ref CBSentinel. + + // [CodeCompiler] + kNodeFunc = 16, //!< Node is \ref CCFunc (considered as \ref CBLabel by \ref CodeBuilder). + kNodeFuncExit = 17, //!< Node is \ref CCFuncRet. + kNodeFuncCall = 18, //!< Node is \ref CCFuncCall. + kNodePushArg = 19, //!< Node is \ref CCPushArg. + kNodeHint = 20, //!< Node is \ref CCHint. + + // [UserDefined] + kNodeUser = 32 //!< First id of a user-defined node. + }; + + // -------------------------------------------------------------------------- + // [Flags] + // -------------------------------------------------------------------------- + + ASMJIT_ENUM(Flags) { + //! The node has been translated by the CodeCompiler. + kFlagIsTranslated = 0x0001, + //! If the node can be safely removed (has no effect). + kFlagIsRemovable = 0x0004, + //! If the node is informative only and can be safely removed. + kFlagIsInformative = 0x0008, + + //! If the `CBInst` is a jump. + kFlagIsJmp = 0x0010, + //! If the `CBInst` is a conditional jump. + kFlagIsJcc = 0x0020, + + //! If the `CBInst` is an unconditional jump or conditional jump that is + //! likely to be taken. + kFlagIsTaken = 0x0040, + + //! If the `CBNode` will return from a function. + //! + //! This flag is used by both `CBSentinel` and `CCFuncRet`. + kFlagIsRet = 0x0080, + + //! Whether the instruction is special. + kFlagIsSpecial = 0x0100, + + //! Whether the instruction is an FPU instruction. + kFlagIsFp = 0x0200 + }; + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a new \ref CBNode - always use \ref CodeBuilder to allocate nodes. + ASMJIT_INLINE CBNode(CodeBuilder* cb, uint32_t type) noexcept { + _prev = nullptr; + _next = nullptr; + _type = static_cast(type); + _opCount = 0; + _flags = static_cast(cb->_nodeFlags); + _position = cb->_position; + _inlineComment = nullptr; + _passData = nullptr; + } + //! Destroy the `CBNode` instance (NEVER CALLED). + ASMJIT_INLINE ~CBNode() noexcept {} + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + template + ASMJIT_INLINE T* as() noexcept { return static_cast(this); } + template + ASMJIT_INLINE const T* as() const noexcept { return static_cast(this); } + + //! Get previous node in the compiler stream. + ASMJIT_INLINE CBNode* getPrev() const noexcept { return _prev; } + //! Get next node in the compiler stream. + ASMJIT_INLINE CBNode* getNext() const noexcept { return _next; } + + //! Get the node type, see \ref Type. + ASMJIT_INLINE uint32_t getType() const noexcept { return _type; } + //! Get the node flags. + ASMJIT_INLINE uint32_t getFlags() const noexcept { return _flags; } + + //! Get whether the instruction has flag `flag`. + ASMJIT_INLINE bool hasFlag(uint32_t flag) const noexcept { return (static_cast(_flags) & flag) != 0; } + //! Set node flags to `flags`. + ASMJIT_INLINE void setFlags(uint32_t flags) noexcept { _flags = static_cast(flags); } + //! Add instruction `flags`. + ASMJIT_INLINE void orFlags(uint32_t flags) noexcept { _flags |= static_cast(flags); } + //! And instruction `flags`. + ASMJIT_INLINE void andFlags(uint32_t flags) noexcept { _flags &= static_cast(flags); } + //! Clear instruction `flags`. + ASMJIT_INLINE void andNotFlags(uint32_t flags) noexcept { _flags &= ~static_cast(flags); } + + //! Get whether the node has been translated. + ASMJIT_INLINE bool isTranslated() const noexcept { return hasFlag(kFlagIsTranslated); } + + //! Get whether the node is removable if it's in unreachable code block. + ASMJIT_INLINE bool isRemovable() const noexcept { return hasFlag(kFlagIsRemovable); } + //! Get whether the node is informative only (comment, hint). + ASMJIT_INLINE bool isInformative() const noexcept { return hasFlag(kFlagIsInformative); } + + //! Whether the node is `CBLabel`. + ASMJIT_INLINE bool isLabel() const noexcept { return _type == kNodeLabel; } + //! Whether the `CBInst` node is an unconditional jump. + ASMJIT_INLINE bool isJmp() const noexcept { return hasFlag(kFlagIsJmp); } + //! Whether the `CBInst` node is a conditional jump. + ASMJIT_INLINE bool isJcc() const noexcept { return hasFlag(kFlagIsJcc); } + //! Whether the `CBInst` node is a conditional/unconditional jump. + ASMJIT_INLINE bool isJmpOrJcc() const noexcept { return hasFlag(kFlagIsJmp | kFlagIsJcc); } + //! Whether the `CBInst` node is a return. + ASMJIT_INLINE bool isRet() const noexcept { return hasFlag(kFlagIsRet); } + + //! Get whether the node is `CBInst` and the instruction is special. + ASMJIT_INLINE bool isSpecial() const noexcept { return hasFlag(kFlagIsSpecial); } + //! Get whether the node is `CBInst` and the instruction uses x87-FPU. + ASMJIT_INLINE bool isFp() const noexcept { return hasFlag(kFlagIsFp); } + + ASMJIT_INLINE bool hasPosition() const noexcept { return _position != 0; } + //! Get flow index. + ASMJIT_INLINE uint32_t getPosition() const noexcept { return _position; } + //! Set flow index. + ASMJIT_INLINE void setPosition(uint32_t position) noexcept { _position = position; } + + //! Get if the node has an inline comment. + ASMJIT_INLINE bool hasInlineComment() const noexcept { return _inlineComment != nullptr; } + //! Get an inline comment string. + ASMJIT_INLINE const char* getInlineComment() const noexcept { return _inlineComment; } + //! Set an inline comment string to `s`. + ASMJIT_INLINE void setInlineComment(const char* s) noexcept { _inlineComment = s; } + //! Set an inline comment string to null. + ASMJIT_INLINE void resetInlineComment() noexcept { _inlineComment = nullptr; } + + //! Get if the node has associated work-data. + ASMJIT_INLINE bool hasPassData() const noexcept { return _passData != nullptr; } + //! Get work-data - data used during processing & transformations. + template + ASMJIT_INLINE T* getPassData() const noexcept { return (T*)_passData; } + //! Set work-data to `data`. + template + ASMJIT_INLINE void setPassData(T* data) noexcept { _passData = (void*)data; } + //! Reset work-data to null. + ASMJIT_INLINE void resetPassData() noexcept { _passData = nullptr; } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + CBNode* _prev; //!< Previous node. + CBNode* _next; //!< Next node. + + uint8_t _type; //!< Node type, see \ref NodeType. + uint8_t _opCount; //!< Count of operands or zero. + uint16_t _flags; //!< Flags, different meaning for every type of the node. + uint32_t _position; //!< Flow index. + + const char* _inlineComment; //!< Inline comment or null if not used. + void* _passData; //!< Data used exclusively by the current `CBPass`. +}; + +// ============================================================================ +// [asmjit::CBInst] +// ============================================================================ + +//! Instruction (CodeBuilder). +//! +//! Wraps an instruction with its options and operands. +class CBInst : public CBNode { +public: + ASMJIT_NONCOPYABLE(CBInst) + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a new `CBInst` instance. + ASMJIT_INLINE CBInst(CodeBuilder* cb, uint32_t instId, uint32_t options, Operand* opArray, uint32_t opCount) noexcept + : CBNode(cb, kNodeInst) { + + orFlags(kFlagIsRemovable); + _instDetail.instId = static_cast(instId); + _instDetail.options = options; + + _opCount = static_cast(opCount); + _opArray = opArray; + + _updateMemOp(); + } + + //! Destroy the `CBInst` instance (NEVER CALLED). + ASMJIT_INLINE ~CBInst() noexcept {} + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE Inst::Detail& getInstDetail() noexcept { return _instDetail; } + ASMJIT_INLINE const Inst::Detail& getInstDetail() const noexcept { return _instDetail; } + + //! Get the instruction id, see \ref Inst::Id. + ASMJIT_INLINE uint32_t getInstId() const noexcept { return _instDetail.instId; } + //! Set the instruction id to `instId`, see \ref Inst::Id. + ASMJIT_INLINE void setInstId(uint32_t instId) noexcept { _instDetail.instId = instId; } + + //! Whether the instruction is either a jump or a conditional jump likely to be taken. + ASMJIT_INLINE bool isTaken() const noexcept { return hasFlag(kFlagIsTaken); } + + //! Get emit options. + ASMJIT_INLINE uint32_t getOptions() const noexcept { return _instDetail.options; } + //! Set emit options. + ASMJIT_INLINE void setOptions(uint32_t options) noexcept { _instDetail.options = options; } + //! Add emit options. + ASMJIT_INLINE void addOptions(uint32_t options) noexcept { _instDetail.options |= options; } + //! Mask emit options. + ASMJIT_INLINE void andOptions(uint32_t options) noexcept { _instDetail.options &= options; } + //! Clear emit options. + ASMJIT_INLINE void delOptions(uint32_t options) noexcept { _instDetail.options &= ~options; } + + //! Get if the node has an extra register operand. + ASMJIT_INLINE bool hasExtraReg() const noexcept { return _instDetail.hasExtraReg(); } + //! Get extra register operand. + ASMJIT_INLINE RegOnly& getExtraReg() noexcept { return _instDetail.extraReg; } + //! \overload + ASMJIT_INLINE const RegOnly& getExtraReg() const noexcept { return _instDetail.extraReg; } + //! Set extra register operand to `reg`. + ASMJIT_INLINE void setExtraReg(const Reg& reg) noexcept { _instDetail.extraReg.init(reg); } + //! Set extra register operand to `reg`. + ASMJIT_INLINE void setExtraReg(const RegOnly& reg) noexcept { _instDetail.extraReg.init(reg); } + //! Reset extra register operand. + ASMJIT_INLINE void resetExtraReg() noexcept { _instDetail.extraReg.reset(); } + + //! Get operands count. + ASMJIT_INLINE uint32_t getOpCount() const noexcept { return _opCount; } + //! Get operands list. + ASMJIT_INLINE Operand* getOpArray() noexcept { return _opArray; } + //! \overload + ASMJIT_INLINE const Operand* getOpArray() const noexcept { return _opArray; } + + //! Get whether the instruction contains a memory operand. + ASMJIT_INLINE bool hasMemOp() const noexcept { return _memOpIndex != 0xFF; } + //! Get memory operand. + //! + //! NOTE: Can only be called if the instruction has such operand, + //! see `hasMemOp()`. + ASMJIT_INLINE Mem* getMemOp() const noexcept { + ASMJIT_ASSERT(hasMemOp()); + return static_cast(&_opArray[_memOpIndex]); + } + //! \overload + template + ASMJIT_INLINE T* getMemOp() const noexcept { + ASMJIT_ASSERT(hasMemOp()); + return static_cast(&_opArray[_memOpIndex]); + } + + //! Set memory operand index, `0xFF` means no memory operand. + ASMJIT_INLINE void setMemOpIndex(uint32_t index) noexcept { _memOpIndex = static_cast(index); } + //! Reset memory operand index to `0xFF` (no operand). + ASMJIT_INLINE void resetMemOpIndex() noexcept { _memOpIndex = 0xFF; } + + // -------------------------------------------------------------------------- + // [Utils] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE void _updateMemOp() noexcept { + Operand* opArray = getOpArray(); + uint32_t opCount = getOpCount(); + + uint32_t i; + for (i = 0; i < opCount; i++) + if (opArray[i].isMem()) + goto Update; + i = 0xFF; + +Update: + setMemOpIndex(i); + } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + Inst::Detail _instDetail; //!< Instruction id, options, and extra register. + uint8_t _memOpIndex; //!< \internal + uint8_t _reserved[7]; //!< \internal + Operand* _opArray; //!< Instruction operands. +}; + +// ============================================================================ +// [asmjit::CBInstEx] +// ============================================================================ + +struct CBInstEx : public CBInst { + Operand _op4; + Operand _op5; +}; + +// ============================================================================ +// [asmjit::CBJump] +// ============================================================================ + +//! Asm jump (conditional or direct). +//! +//! Extension of `CBInst` node, which stores more information about the jump. +class CBJump : public CBInst { +public: + ASMJIT_NONCOPYABLE(CBJump) + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE CBJump(CodeBuilder* cb, uint32_t instId, uint32_t options, Operand* opArray, uint32_t opCount) noexcept + : CBInst(cb, instId, options, opArray, opCount), + _target(nullptr), + _jumpNext(nullptr) {} + ASMJIT_INLINE ~CBJump() noexcept {} + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE CBLabel* getTarget() const noexcept { return _target; } + ASMJIT_INLINE CBJump* getJumpNext() const noexcept { return _jumpNext; } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + CBLabel* _target; //!< Target node. + CBJump* _jumpNext; //!< Next jump to the same target in a single linked-list. +}; + +// ============================================================================ +// [asmjit::CBData] +// ============================================================================ + +//! Asm data (CodeBuilder). +//! +//! Wraps `.data` directive. The node contains data that will be placed at the +//! node's position in the assembler stream. The data is considered to be RAW; +//! no analysis nor byte-order conversion is performed on RAW data. +class CBData : public CBNode { +public: + ASMJIT_NONCOPYABLE(CBData) + enum { kInlineBufferSize = static_cast(64 - sizeof(CBNode) - 4) }; + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a new `CBData` instance. + ASMJIT_INLINE CBData(CodeBuilder* cb, void* data, uint32_t size) noexcept : CBNode(cb, kNodeData) { + if (size <= kInlineBufferSize) { + if (data) ::memcpy(_buf, data, size); + } + else { + _externalPtr = static_cast(data); + } + _size = size; + } + + //! Destroy the `CBData` instance (NEVER CALLED). + ASMJIT_INLINE ~CBData() noexcept {} + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get size of the data. + uint32_t getSize() const noexcept { return _size; } + //! Get pointer to the data. + uint8_t* getData() const noexcept { return _size <= kInlineBufferSize ? const_cast(_buf) : _externalPtr; } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + union { + struct { + uint8_t _buf[kInlineBufferSize]; //!< Embedded data buffer. + uint32_t _size; //!< Size of the data. + }; + struct { + uint8_t* _externalPtr; //!< Pointer to external data. + }; + }; +}; + +// ============================================================================ +// [asmjit::CBAlign] +// ============================================================================ + +//! Align directive (CodeBuilder). +//! +//! Wraps `.align` directive. +class CBAlign : public CBNode { +public: + ASMJIT_NONCOPYABLE(CBAlign) + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a new `CBAlign` instance. + ASMJIT_INLINE CBAlign(CodeBuilder* cb, uint32_t mode, uint32_t alignment) noexcept + : CBNode(cb, kNodeAlign), + _mode(mode), + _alignment(alignment) {} + //! Destroy the `CBAlign` instance (NEVER CALLED). + ASMJIT_INLINE ~CBAlign() noexcept {} + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get align mode. + ASMJIT_INLINE uint32_t getMode() const noexcept { return _mode; } + //! Set align mode. + ASMJIT_INLINE void setMode(uint32_t mode) noexcept { _mode = mode; } + + //! Get align offset in bytes. + ASMJIT_INLINE uint32_t getAlignment() const noexcept { return _alignment; } + //! Set align offset in bytes to `offset`. + ASMJIT_INLINE void setAlignment(uint32_t alignment) noexcept { _alignment = alignment; } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + uint32_t _mode; //!< Align mode, see \ref AlignMode. + uint32_t _alignment; //!< Alignment (in bytes). +}; + +// ============================================================================ +// [asmjit::CBLabel] +// ============================================================================ + +//! Label (CodeBuilder). +class CBLabel : public CBNode { +public: + ASMJIT_NONCOPYABLE(CBLabel) + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a new `CBLabel` instance. + ASMJIT_INLINE CBLabel(CodeBuilder* cb, uint32_t id = kInvalidValue) noexcept + : CBNode(cb, kNodeLabel), + _id(id), + _numRefs(0), + _from(nullptr) {} + //! Destroy the `CBLabel` instance (NEVER CALLED). + ASMJIT_INLINE ~CBLabel() noexcept {} + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get the label id. + ASMJIT_INLINE uint32_t getId() const noexcept { return _id; } + //! Get the label as `Label` operand. + ASMJIT_INLINE Label getLabel() const noexcept { return Label(_id); } + + //! Get first jmp instruction. + ASMJIT_INLINE CBJump* getFrom() const noexcept { return _from; } + + //! Get number of jumps to this target. + ASMJIT_INLINE uint32_t getNumRefs() const noexcept { return _numRefs; } + //! Set number of jumps to this target. + ASMJIT_INLINE void setNumRefs(uint32_t i) noexcept { _numRefs = i; } + + //! Add number of jumps to this target. + ASMJIT_INLINE void addNumRefs(uint32_t i = 1) noexcept { _numRefs += i; } + //! Subtract number of jumps to this target. + ASMJIT_INLINE void subNumRefs(uint32_t i = 1) noexcept { _numRefs -= i; } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + uint32_t _id; //!< Label id. + uint32_t _numRefs; //!< Count of jumps here. + CBJump* _from; //!< Linked-list of nodes that can jump here. +}; + +// ============================================================================ +// [asmjit::CBLabelData] +// ============================================================================ + +class CBLabelData : public CBNode { +public: + ASMJIT_NONCOPYABLE(CBLabelData) + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a new `CBLabelData` instance. + ASMJIT_INLINE CBLabelData(CodeBuilder* cb, uint32_t id = kInvalidValue) noexcept + : CBNode(cb, kNodeLabelData), + _id(id) {} + + //! Destroy the `CBLabelData` instance (NEVER CALLED). + ASMJIT_INLINE ~CBLabelData() noexcept {} + + // -------------------------------------------------------------------------- + // [Interface] + // -------------------------------------------------------------------------- + + //! Get the label id. + ASMJIT_INLINE uint32_t getId() const noexcept { return _id; } + //! Get the label as `Label` operand. + ASMJIT_INLINE Label getLabel() const noexcept { return Label(_id); } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + uint32_t _id; +}; + +// ============================================================================ +// [asmjit::CBConstPool] +// ============================================================================ + +class CBConstPool : public CBLabel { +public: + ASMJIT_NONCOPYABLE(CBConstPool) + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a new `CBConstPool` instance. + ASMJIT_INLINE CBConstPool(CodeBuilder* cb, uint32_t id = kInvalidValue) noexcept + : CBLabel(cb, id), + _constPool(&cb->_cbBaseZone) { _type = kNodeConstPool; } + + //! Destroy the `CBConstPool` instance (NEVER CALLED). + ASMJIT_INLINE ~CBConstPool() noexcept {} + + // -------------------------------------------------------------------------- + // [Interface] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE ConstPool& getConstPool() noexcept { return _constPool; } + ASMJIT_INLINE const ConstPool& getConstPool() const noexcept { return _constPool; } + + //! Get whether the constant-pool is empty. + ASMJIT_INLINE bool isEmpty() const noexcept { return _constPool.isEmpty(); } + //! Get the size of the constant-pool in bytes. + ASMJIT_INLINE size_t getSize() const noexcept { return _constPool.getSize(); } + //! Get minimum alignment. + ASMJIT_INLINE size_t getAlignment() const noexcept { return _constPool.getAlignment(); } + + //! See \ref ConstPool::add(). + ASMJIT_INLINE Error add(const void* data, size_t size, size_t& dstOffset) noexcept { + return _constPool.add(data, size, dstOffset); + } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + ConstPool _constPool; +}; + +// ============================================================================ +// [asmjit::CBComment] +// ============================================================================ + +//! Comment (CodeBuilder). +class CBComment : public CBNode { +public: + ASMJIT_NONCOPYABLE(CBComment) + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a new `CBComment` instance. + ASMJIT_INLINE CBComment(CodeBuilder* cb, const char* comment) noexcept : CBNode(cb, kNodeComment) { + orFlags(kFlagIsRemovable | kFlagIsInformative); + _inlineComment = comment; + } + + //! Destroy the `CBComment` instance (NEVER CALLED). + ASMJIT_INLINE ~CBComment() noexcept {} +}; + +// ============================================================================ +// [asmjit::CBSentinel] +// ============================================================================ + +//! Sentinel (CodeBuilder). +//! +//! Sentinel is a marker that is completely ignored by the code builder. It's +//! used to remember a position in a code as it never gets removed by any pass. +class CBSentinel : public CBNode { +public: + ASMJIT_NONCOPYABLE(CBSentinel) + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a new `CBSentinel` instance. + ASMJIT_INLINE CBSentinel(CodeBuilder* cb) noexcept : CBNode(cb, kNodeSentinel) {} + //! Destroy the `CBSentinel` instance (NEVER CALLED). + ASMJIT_INLINE ~CBSentinel() noexcept {} +}; + +//! \} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" + +// [Guard] +#endif // !ASMJIT_DISABLE_BUILDER +#endif // _ASMJIT_BASE_CODEBUILDER_H diff --git a/libraries/asmjit/asmjit/base/codecompiler.cpp b/libraries/asmjit/asmjit/base/codecompiler.cpp new file mode 100644 index 00000000000..582e94a9056 --- /dev/null +++ b/libraries/asmjit/asmjit/base/codecompiler.cpp @@ -0,0 +1,573 @@ +// [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 "../asmjit_build.h" +#if !defined(ASMJIT_DISABLE_COMPILER) + +// [Dependencies] +#include "../base/assembler.h" +#include "../base/codecompiler.h" +#include "../base/cpuinfo.h" +#include "../base/logging.h" +#include "../base/regalloc_p.h" +#include "../base/utils.h" +#include + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +// ============================================================================ +// [Constants] +// ============================================================================ + +static const char noName[1] = { '\0' }; + +// ============================================================================ +// [asmjit::CCFuncCall - Arg / Ret] +// ============================================================================ + +bool CCFuncCall::_setArg(uint32_t i, const Operand_& op) noexcept { + if ((i & ~kFuncArgHi) >= _funcDetail.getArgCount()) + return false; + + _args[i] = op; + return true; +} + +bool CCFuncCall::_setRet(uint32_t i, const Operand_& op) noexcept { + if (i >= 2) + return false; + + _ret[i] = op; + return true; +} + +// ============================================================================ +// [asmjit::CodeCompiler - Construction / Destruction] +// ============================================================================ + +CodeCompiler::CodeCompiler() noexcept + : CodeBuilder(), + _func(nullptr), + _vRegZone(4096 - Zone::kZoneOverhead), + _vRegArray(), + _localConstPool(nullptr), + _globalConstPool(nullptr) { + + _type = kTypeCompiler; +} +CodeCompiler::~CodeCompiler() noexcept {} + +// ============================================================================ +// [asmjit::CodeCompiler - Events] +// ============================================================================ + +Error CodeCompiler::onAttach(CodeHolder* code) noexcept { + return Base::onAttach(code); +} + +Error CodeCompiler::onDetach(CodeHolder* code) noexcept { + _func = nullptr; + + _localConstPool = nullptr; + _globalConstPool = nullptr; + + _vRegArray.reset(); + _vRegZone.reset(false); + + return Base::onDetach(code); +} + +// ============================================================================ +// [asmjit::CodeCompiler - Node-Factory] +// ============================================================================ + +CCHint* CodeCompiler::newHintNode(Reg& r, uint32_t hint, uint32_t value) noexcept { + if (!r.isVirtReg()) return nullptr; + + VirtReg* vr = getVirtReg(r); + return newNodeT(vr, hint, value); +} + +// ============================================================================ +// [asmjit::CodeCompiler - Func] +// ============================================================================ + +CCFunc* CodeCompiler::newFunc(const FuncSignature& sign) noexcept { + Error err; + + CCFunc* func = newNodeT(); + if (!func) goto _NoMemory; + + err = registerLabelNode(func); + if (ASMJIT_UNLIKELY(err)) { + // TODO: Calls setLastError, maybe rethink noexcept? + setLastError(err); + return nullptr; + } + + // Create helper nodes. + func->_exitNode = newLabelNode(); + func->_end = newNodeT(); + + if (!func->_exitNode || !func->_end) + goto _NoMemory; + + // Function prototype. + err = func->getDetail().init(sign); + if (err != kErrorOk) { + setLastError(err); + return nullptr; + } + + // If the CodeInfo guarantees higher alignment honor it. + if (_codeInfo.getStackAlignment() > func->_funcDetail._callConv.getNaturalStackAlignment()) + func->_funcDetail._callConv.setNaturalStackAlignment(_codeInfo.getStackAlignment()); + + // Allocate space for function arguments. + func->_args = nullptr; + if (func->getArgCount() != 0) { + func->_args = _cbHeap.allocT(func->getArgCount() * sizeof(VirtReg*)); + if (!func->_args) goto _NoMemory; + + ::memset(func->_args, 0, func->getArgCount() * sizeof(VirtReg*)); + } + + return func; + +_NoMemory: + setLastError(DebugUtils::errored(kErrorNoHeapMemory)); + return nullptr; +} + +CCFunc* CodeCompiler::addFunc(CCFunc* func) { + ASMJIT_ASSERT(_func == nullptr); + _func = func; + + addNode(func); // Function node. + CBNode* cursor = getCursor(); // {CURSOR}. + addNode(func->getExitNode()); // Function exit label. + addNode(func->getEnd()); // Function end marker. + + _setCursor(cursor); + return func; +} + +CCFunc* CodeCompiler::addFunc(const FuncSignature& sign) { + CCFunc* func = newFunc(sign); + + if (!func) { + setLastError(DebugUtils::errored(kErrorNoHeapMemory)); + return nullptr; + } + + return addFunc(func); +} + +CBSentinel* CodeCompiler::endFunc() { + CCFunc* func = getFunc(); + if (!func) { + // TODO: + return nullptr; + } + + // Add the local constant pool at the end of the function (if exists). + if (_localConstPool) { + setCursor(func->getEnd()->getPrev()); + addNode(_localConstPool); + _localConstPool = nullptr; + } + + // Mark as finished. + func->_isFinished = true; + _func = nullptr; + + CBSentinel* end = func->getEnd(); + setCursor(end); + return end; +} + +// ============================================================================ +// [asmjit::CodeCompiler - Ret] +// ============================================================================ + +CCFuncRet* CodeCompiler::newRet(const Operand_& o0, const Operand_& o1) noexcept { + CCFuncRet* node = newNodeT(o0, o1); + if (!node) { + setLastError(DebugUtils::errored(kErrorNoHeapMemory)); + return nullptr; + } + return node; +} + +CCFuncRet* CodeCompiler::addRet(const Operand_& o0, const Operand_& o1) noexcept { + CCFuncRet* node = newRet(o0, o1); + if (!node) return nullptr; + return static_cast(addNode(node)); +} + +// ============================================================================ +// [asmjit::CodeCompiler - Call] +// ============================================================================ + +CCFuncCall* CodeCompiler::newCall(uint32_t instId, const Operand_& o0, const FuncSignature& sign) noexcept { + Error err; + uint32_t nArgs; + + CCFuncCall* node = _cbHeap.allocT(sizeof(CCFuncCall) + sizeof(Operand)); + Operand* opArray = reinterpret_cast(reinterpret_cast(node) + sizeof(CCFuncCall)); + + if (ASMJIT_UNLIKELY(!node)) + goto _NoMemory; + + opArray[0].copyFrom(o0); + new (node) CCFuncCall(this, instId, 0, opArray, 1); + + if ((err = node->getDetail().init(sign)) != kErrorOk) { + setLastError(err); + return nullptr; + } + + // If there are no arguments skip the allocation. + if ((nArgs = sign.getArgCount()) == 0) + return node; + + node->_args = static_cast(_cbHeap.alloc(nArgs * sizeof(Operand))); + if (!node->_args) goto _NoMemory; + + ::memset(node->_args, 0, nArgs * sizeof(Operand)); + return node; + +_NoMemory: + setLastError(DebugUtils::errored(kErrorNoHeapMemory)); + return nullptr; +} + +CCFuncCall* CodeCompiler::addCall(uint32_t instId, const Operand_& o0, const FuncSignature& sign) noexcept { + CCFuncCall* node = newCall(instId, o0, sign); + if (!node) return nullptr; + return static_cast(addNode(node)); +} + +// ============================================================================ +// [asmjit::CodeCompiler - Vars] +// ============================================================================ + +Error CodeCompiler::setArg(uint32_t argIndex, const Reg& r) { + CCFunc* func = getFunc(); + + if (!func) + return setLastError(DebugUtils::errored(kErrorInvalidState)); + + if (!isVirtRegValid(r)) + return setLastError(DebugUtils::errored(kErrorInvalidVirtId)); + + VirtReg* vr = getVirtReg(r); + func->setArg(argIndex, vr); + + return kErrorOk; +} + +// ============================================================================ +// [asmjit::CodeCompiler - Hint] +// ============================================================================ + +Error CodeCompiler::_hint(Reg& r, uint32_t hint, uint32_t value) { + if (!r.isVirtReg()) return kErrorOk; + + CCHint* node = newHintNode(r, hint, value); + if (!node) return setLastError(DebugUtils::errored(kErrorNoHeapMemory)); + + addNode(node); + return kErrorOk; +} + +// ============================================================================ +// [asmjit::CodeCompiler - Vars] +// ============================================================================ + +VirtReg* CodeCompiler::newVirtReg(uint32_t typeId, uint32_t signature, const char* name) noexcept { + size_t index = _vRegArray.getLength(); + if (ASMJIT_UNLIKELY(index > Operand::kPackedIdCount)) + return nullptr; + + VirtReg* vreg; + if (_vRegArray.willGrow(&_cbHeap, 1) != kErrorOk || !(vreg = _vRegZone.allocZeroedT())) + return nullptr; + + vreg->_id = Operand::packId(static_cast(index)); + vreg->_regInfo._signature = signature; + vreg->_name = noName; + +#if !defined(ASMJIT_DISABLE_LOGGING) + if (name && name[0] != '\0') + vreg->_name = static_cast(_cbDataZone.dup(name, ::strlen(name), true)); +#endif // !ASMJIT_DISABLE_LOGGING + + vreg->_size = TypeId::sizeOf(typeId); + vreg->_typeId = typeId; + vreg->_alignment = static_cast(std::min(vreg->_size, 64)); + vreg->_priority = 10; + + // The following are only used by `RAPass`. + vreg->_raId = kInvalidValue; + vreg->_state = VirtReg::kStateNone; + vreg->_physId = Globals::kInvalidRegId; + + _vRegArray.appendUnsafe(vreg); + return vreg; +} + +Error CodeCompiler::_newReg(Reg& out, uint32_t typeId, const char* name) { + RegInfo regInfo; + + Error err = ArchUtils::typeIdToRegInfo(getArchType(), typeId, regInfo); + if (ASMJIT_UNLIKELY(err)) return setLastError(err); + + VirtReg* vReg = newVirtReg(typeId, regInfo.getSignature(), name); + if (ASMJIT_UNLIKELY(!vReg)) { + out.reset(); + return setLastError(DebugUtils::errored(kErrorNoHeapMemory)); + } + + out._initReg(regInfo.getSignature(), vReg->getId()); + return kErrorOk; +} + +Error CodeCompiler::_newReg(Reg& out, uint32_t typeId, const char* nameFmt, va_list ap) { + StringBuilderTmp<256> sb; + sb.appendFormatVA(nameFmt, ap); + return _newReg(out, typeId, sb.getData()); +} + +Error CodeCompiler::_newReg(Reg& out, const Reg& ref, const char* name) { + RegInfo regInfo; + uint32_t typeId; + + if (isVirtRegValid(ref)) { + VirtReg* vRef = getVirtReg(ref); + typeId = vRef->getTypeId(); + + // NOTE: It's possible to cast one register type to another if it's the + // same register kind. However, VirtReg always contains the TypeId that + // was used to create the register. This means that in some cases we may + // end up having different size of `ref` and `vRef`. In such case we + // adjust the TypeId to match the `ref` register type instead of the + // original register type, which should be the expected behavior. + uint32_t typeSize = TypeId::sizeOf(typeId); + uint32_t refSize = ref.getSize(); + + if (typeSize != refSize) { + if (TypeId::isInt(typeId)) { + // GP register - change TypeId to match `ref`, but keep sign of `vRef`. + switch (refSize) { + case 1: typeId = TypeId::kI8 | (typeId & 1); break; + case 2: typeId = TypeId::kI16 | (typeId & 1); break; + case 4: typeId = TypeId::kI32 | (typeId & 1); break; + case 8: typeId = TypeId::kI64 | (typeId & 1); break; + default: typeId = TypeId::kVoid; break; + } + } + else if (TypeId::isMmx(typeId)) { + // MMX register - always use 64-bit. + typeId = TypeId::kMmx64; + } + else if (TypeId::isMask(typeId)) { + // Mask register - change TypeId to match `ref` size. + switch (refSize) { + case 1: typeId = TypeId::kMask8; break; + case 2: typeId = TypeId::kMask16; break; + case 4: typeId = TypeId::kMask32; break; + case 8: typeId = TypeId::kMask64; break; + default: typeId = TypeId::kVoid; break; + } + } + else { + // VEC register - change TypeId to match `ref` size, keep vector metadata. + uint32_t elementTypeId = TypeId::elementOf(typeId); + + switch (refSize) { + case 16: typeId = TypeId::_kVec128Start + (elementTypeId - TypeId::kI8); break; + case 32: typeId = TypeId::_kVec256Start + (elementTypeId - TypeId::kI8); break; + case 64: typeId = TypeId::_kVec512Start + (elementTypeId - TypeId::kI8); break; + default: typeId = TypeId::kVoid; break; + } + } + + if (typeId == TypeId::kVoid) + return setLastError(DebugUtils::errored(kErrorInvalidState)); + } + } + else { + typeId = ref.getType(); + } + + Error err = ArchUtils::typeIdToRegInfo(getArchType(), typeId, regInfo); + if (ASMJIT_UNLIKELY(err)) return setLastError(err); + + VirtReg* vReg = newVirtReg(typeId, regInfo.getSignature(), name); + if (ASMJIT_UNLIKELY(!vReg)) { + out.reset(); + return setLastError(DebugUtils::errored(kErrorNoHeapMemory)); + } + + out._initReg(regInfo.getSignature(), vReg->getId()); + return kErrorOk; +} + +Error CodeCompiler::_newReg(Reg& out, const Reg& ref, const char* nameFmt, va_list ap) { + StringBuilderTmp<256> sb; + sb.appendFormatVA(nameFmt, ap); + return _newReg(out, ref, sb.getData()); +} + +Error CodeCompiler::_newStack(Mem& out, uint32_t size, uint32_t alignment, const char* name) { + if (size == 0) + return setLastError(DebugUtils::errored(kErrorInvalidArgument)); + + if (alignment == 0) alignment = 1; + if (!Utils::isPowerOf2(alignment)) + return setLastError(DebugUtils::errored(kErrorInvalidArgument)); + + if (alignment > 64) alignment = 64; + + VirtReg* vReg = newVirtReg(0, 0, name); + if (ASMJIT_UNLIKELY(!vReg)) { + out.reset(); + return setLastError(DebugUtils::errored(kErrorNoHeapMemory)); + } + + vReg->_size = size; + vReg->_isStack = true; + vReg->_alignment = static_cast(alignment); + + // Set the memory operand to GPD/GPQ and its id to VirtReg. + out = Mem(Init, _nativeGpReg.getType(), vReg->getId(), Reg::kRegNone, kInvalidValue, 0, 0, Mem::kSignatureMemRegHomeFlag); + return kErrorOk; +} + +Error CodeCompiler::_newConst(Mem& out, uint32_t scope, const void* data, size_t size) { + CBConstPool** pPool; + if (scope == kConstScopeLocal) + pPool = &_localConstPool; + else if (scope == kConstScopeGlobal) + pPool = &_globalConstPool; + else + return setLastError(DebugUtils::errored(kErrorInvalidArgument)); + + if (!*pPool && !(*pPool = newConstPool())) + return setLastError(DebugUtils::errored(kErrorNoHeapMemory)); + + CBConstPool* pool = *pPool; + size_t off; + + Error err = pool->add(data, size, off); + if (ASMJIT_UNLIKELY(err)) return setLastError(err); + + out = Mem(Init, + Label::kLabelTag, // Base type. + pool->getId(), // Base id. + 0, // Index type. + kInvalidValue, // Index id. + static_cast(off), // Offset. + static_cast(size), // Size. + 0); // Flags. + return kErrorOk; +} + +Error CodeCompiler::alloc(Reg& reg) { + if (!reg.isVirtReg()) return kErrorOk; + return _hint(reg, CCHint::kHintAlloc, kInvalidValue); +} + +Error CodeCompiler::alloc(Reg& reg, uint32_t physId) { + if (!reg.isVirtReg()) return kErrorOk; + return _hint(reg, CCHint::kHintAlloc, physId); +} + +Error CodeCompiler::alloc(Reg& reg, const Reg& physReg) { + if (!reg.isVirtReg()) return kErrorOk; + return _hint(reg, CCHint::kHintAlloc, physReg.getId()); +} + +Error CodeCompiler::save(Reg& reg) { + if (!reg.isVirtReg()) return kErrorOk; + return _hint(reg, CCHint::kHintSave, kInvalidValue); +} + +Error CodeCompiler::spill(Reg& reg) { + if (!reg.isVirtReg()) return kErrorOk; + return _hint(reg, CCHint::kHintSpill, kInvalidValue); +} + +Error CodeCompiler::unuse(Reg& reg) { + if (!reg.isVirtReg()) return kErrorOk; + return _hint(reg, CCHint::kHintUnuse, kInvalidValue); +} + +uint32_t CodeCompiler::getPriority(Reg& reg) const { + if (!reg.isVirtReg()) return 0; + return getVirtRegById(reg.getId())->getPriority(); +} + +void CodeCompiler::setPriority(Reg& reg, uint32_t priority) { + if (!reg.isVirtReg()) return; + if (priority > 255) priority = 255; + + VirtReg* vreg = getVirtRegById(reg.getId()); + if (vreg) vreg->_priority = static_cast(priority); +} + +bool CodeCompiler::getSaveOnUnuse(Reg& reg) const { + if (!reg.isVirtReg()) return false; + + VirtReg* vreg = getVirtRegById(reg.getId()); + return static_cast(vreg->_saveOnUnuse); +} + +void CodeCompiler::setSaveOnUnuse(Reg& reg, bool value) { + if (!reg.isVirtReg()) return; + + VirtReg* vreg = getVirtRegById(reg.getId()); + if (!vreg) return; + + vreg->_saveOnUnuse = value; +} + +void CodeCompiler::rename(Reg& reg, const char* fmt, ...) { + if (!reg.isVirtReg()) return; + + VirtReg* vreg = getVirtRegById(reg.getId()); + if (!vreg) return; + + vreg->_name = noName; + if (fmt && fmt[0] != '\0') { + char buf[64]; + + va_list ap; + va_start(ap, fmt); + + vsnprintf(buf, ASMJIT_ARRAY_SIZE(buf), fmt, ap); + buf[ASMJIT_ARRAY_SIZE(buf) - 1] = '\0'; + + vreg->_name = static_cast(_cbDataZone.dup(buf, ::strlen(buf), true)); + va_end(ap); + } +} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" + +// [Guard] +#endif // !ASMJIT_DISABLE_COMPILER diff --git a/libraries/asmjit/asmjit/base/codecompiler.h b/libraries/asmjit/asmjit/base/codecompiler.h new file mode 100644 index 00000000000..44b9644b71b --- /dev/null +++ b/libraries/asmjit/asmjit/base/codecompiler.h @@ -0,0 +1,738 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Guard] +#ifndef _ASMJIT_BASE_CODECOMPILER_H +#define _ASMJIT_BASE_CODECOMPILER_H + +#include "../asmjit_build.h" +#if !defined(ASMJIT_DISABLE_COMPILER) + +// [Dependencies] +#include "../base/assembler.h" +#include "../base/codebuilder.h" +#include "../base/constpool.h" +#include "../base/func.h" +#include "../base/operand.h" +#include "../base/utils.h" +#include "../base/zone.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +// ============================================================================ +// [Forward Declarations] +// ============================================================================ + +struct VirtReg; +struct TiedReg; +struct RAState; +struct RACell; + +//! \addtogroup asmjit_base +//! \{ + +// ============================================================================ +// [asmjit::ConstScope] +// ============================================================================ + +//! Scope of the constant. +ASMJIT_ENUM(ConstScope) { + //! Local constant, always embedded right after the current function. + kConstScopeLocal = 0, + //! Global constant, embedded at the end of the currently compiled code. + kConstScopeGlobal = 1 +}; + +// ============================================================================ +// [asmjit::VirtReg] +// ============================================================================ + +//! Virtual register data (CodeCompiler). +struct VirtReg { + //! A state of a virtual register (used during register allocation). + ASMJIT_ENUM(State) { + kStateNone = 0, //!< Not allocated, not used. + kStateReg = 1, //!< Allocated in register. + kStateMem = 2 //!< Allocated in memory or spilled. + }; + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get the virtual-register id. + ASMJIT_INLINE uint32_t getId() const noexcept { return _id; } + //! Get virtual-register's name. + ASMJIT_INLINE const char* getName() const noexcept { return _name; } + + //! Get a physical register type. + ASMJIT_INLINE uint32_t getType() const noexcept { return _regInfo.getType(); } + //! Get a physical register kind. + ASMJIT_INLINE uint32_t getKind() const noexcept { return _regInfo.getKind(); } + //! Get a physical register size. + ASMJIT_INLINE uint32_t getRegSize() const noexcept { return _regInfo.getSize(); } + //! Get a register signature of this virtual register. + ASMJIT_INLINE uint32_t getSignature() const noexcept { return _regInfo.getSignature(); } + + //! Get a register's type-id, see \ref TypeId. + ASMJIT_INLINE uint32_t getTypeId() const noexcept { return _typeId; } + + //! Get virtual-register's size. + ASMJIT_INLINE uint32_t getSize() const noexcept { return _size; } + //! Get virtual-register's alignment. + ASMJIT_INLINE uint32_t getAlignment() const noexcept { return _alignment; } + + //! Get the virtual-register priority, used by compiler to decide which variable to spill. + ASMJIT_INLINE uint32_t getPriority() const noexcept { return _priority; } + //! Set the virtual-register priority. + ASMJIT_INLINE void setPriority(uint32_t priority) noexcept { + ASMJIT_ASSERT(priority <= 0xFF); + _priority = static_cast(priority); + } + + //! Get variable state, only used by `RAPass`. + ASMJIT_INLINE uint32_t getState() const noexcept { return _state; } + //! Set variable state, only used by `RAPass`. + ASMJIT_INLINE void setState(uint32_t state) { + ASMJIT_ASSERT(state <= 0xFF); + _state = static_cast(state); + } + + //! Get register index. + ASMJIT_INLINE uint32_t getPhysId() const noexcept { return _physId; } + //! Set register index. + ASMJIT_INLINE void setPhysId(uint32_t physId) { + ASMJIT_ASSERT(physId <= Globals::kInvalidRegId); + _physId = static_cast(physId); + } + //! Reset register index. + ASMJIT_INLINE void resetPhysId() { + _physId = static_cast(Globals::kInvalidRegId); + } + + //! Get home registers mask. + ASMJIT_INLINE uint32_t getHomeMask() const { return _homeMask; } + //! Add a home register index to the home registers mask. + ASMJIT_INLINE void addHomeId(uint32_t physId) { _homeMask |= Utils::mask(physId); } + + ASMJIT_INLINE bool isFixed() const noexcept { return static_cast(_isFixed); } + + //! Get whether the VirtReg is only memory allocated on the stack. + ASMJIT_INLINE bool isStack() const noexcept { return static_cast(_isStack); } + + //! Get whether to save variable when it's unused (spill). + ASMJIT_INLINE bool saveOnUnuse() const noexcept { return static_cast(_saveOnUnuse); } + + //! Get whether the variable was changed. + ASMJIT_INLINE bool isModified() const noexcept { return static_cast(_modified); } + //! Set whether the variable was changed. + ASMJIT_INLINE void setModified(bool modified) noexcept { _modified = modified; } + + //! Get home memory offset. + ASMJIT_INLINE int32_t getMemOffset() const noexcept { return _memOffset; } + //! Set home memory offset. + ASMJIT_INLINE void setMemOffset(int32_t offset) noexcept { _memOffset = offset; } + + //! Get home memory cell. + ASMJIT_INLINE RACell* getMemCell() const noexcept { return _memCell; } + //! Set home memory cell. + ASMJIT_INLINE void setMemCell(RACell* cell) noexcept { _memCell = cell; } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + uint32_t _id; //!< Virtual register id. + RegInfo _regInfo; //!< Physical register info & signature. + const char* _name; //!< Virtual name (user provided). + uint32_t _size; //!< Virtual size (can be smaller than `regInfo._size`). + uint8_t _typeId; //!< Type-id. + uint8_t _alignment; //!< Register's natural alignment (for spilling). + uint8_t _priority; //!< Allocation priority (hint for RAPass that can be ignored). + uint8_t _isFixed : 1; //!< True if this is a fixed register, never reallocated. + uint8_t _isStack : 1; //!< True if the virtual register is only used as a stack. + uint8_t _isMaterialized : 1; //!< Register is constant that is easily created by a single instruction. + uint8_t _saveOnUnuse : 1; //!< Save on unuse (at end of the variable scope). + + // ------------------------------------------------------------------------- + // The following members are used exclusively by RAPass. They are initialized + // when the VirtReg is created and then changed during RAPass. + // ------------------------------------------------------------------------- + + uint32_t _raId; //!< Register allocator work-id (used by RAPass). + int32_t _memOffset; //!< Home memory offset. + uint32_t _homeMask; //!< Mask of all registers variable has been allocated to. + + uint8_t _state; //!< Variable state (connected with actual `RAState)`. + uint8_t _physId; //!< Actual register index (only used by `RAPass)`, during translate. + uint8_t _modified; //!< Whether variable was changed (connected with actual `RAState)`. + + RACell* _memCell; //!< Home memory cell, used by `RAPass` (initially nullptr). + + //! Temporary link to TiedReg* used by the `RAPass` used in + //! various phases, but always set back to nullptr when finished. + //! + //! This temporary data is designed to be used by algorithms that need to + //! store some data into variables themselves during compilation. But it's + //! expected that after variable is compiled & translated the data is set + //! back to zero/null. Initial value is nullptr. + TiedReg* _tied; +}; + +// ============================================================================ +// [asmjit::CCHint] +// ============================================================================ + +//! Hint for register allocator (CodeCompiler). +class CCHint : public CBNode { +public: + ASMJIT_NONCOPYABLE(CCHint) + + //! Hint type. + ASMJIT_ENUM(Hint) { + //! Alloc to physical reg. + kHintAlloc = 0, + //! Spill to memory. + kHintSpill = 1, + //! Save if modified. + kHintSave = 2, + //! Save if modified and mark it as unused. + kHintSaveAndUnuse = 3, + //! Mark as unused. + kHintUnuse = 4 + }; + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a new `CCHint` instance. + ASMJIT_INLINE CCHint(CodeBuilder* cb, VirtReg* vreg, uint32_t hint, uint32_t value) noexcept : CBNode(cb, kNodeHint) { + orFlags(kFlagIsRemovable | kFlagIsInformative); + _vreg = vreg; + _hint = hint; + _value = value; + } + + //! Destroy the `CCHint` instance (NEVER CALLED). + ASMJIT_INLINE ~CCHint() noexcept {} + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get variable. + ASMJIT_INLINE VirtReg* getVReg() const noexcept { return _vreg; } + + //! Get hint it, see \ref Hint. + ASMJIT_INLINE uint32_t getHint() const noexcept { return _hint; } + //! Set hint it, see \ref Hint. + ASMJIT_INLINE void setHint(uint32_t hint) noexcept { _hint = hint; } + + //! Get hint value. + ASMJIT_INLINE uint32_t getValue() const noexcept { return _value; } + //! Set hint value. + ASMJIT_INLINE void setValue(uint32_t value) noexcept { _value = value; } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + //! Variable. + VirtReg* _vreg; + //! Hint id. + uint32_t _hint; + //! Value. + uint32_t _value; +}; + +// ============================================================================ +// [asmjit::CCFunc] +// ============================================================================ + +//! Function entry (CodeCompiler). +class CCFunc : public CBLabel { +public: + ASMJIT_NONCOPYABLE(CCFunc) + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a new `CCFunc` instance. + //! + //! Always use `CodeCompiler::addFunc()` to create \ref CCFunc. + ASMJIT_INLINE CCFunc(CodeBuilder* cb) noexcept + : CBLabel(cb), + _funcDetail(), + _frameInfo(), + _exitNode(nullptr), + _end(nullptr), + _args(nullptr), + _isFinished(false) { + + _type = kNodeFunc; + } + + //! Destroy the `CCFunc` instance (NEVER CALLED). + ASMJIT_INLINE ~CCFunc() noexcept {} + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get function exit `CBLabel`. + ASMJIT_INLINE CBLabel* getExitNode() const noexcept { return _exitNode; } + //! Get function exit label. + ASMJIT_INLINE Label getExitLabel() const noexcept { return _exitNode->getLabel(); } + + //! Get "End of Func" sentinel. + ASMJIT_INLINE CBSentinel* getEnd() const noexcept { return _end; } + + //! Get function declaration. + ASMJIT_INLINE FuncDetail& getDetail() noexcept { return _funcDetail; } + //! Get function declaration. + ASMJIT_INLINE const FuncDetail& getDetail() const noexcept { return _funcDetail; } + + //! Get function declaration. + ASMJIT_INLINE FuncFrameInfo& getFrameInfo() noexcept { return _frameInfo; } + //! Get function declaration. + ASMJIT_INLINE const FuncFrameInfo& getFrameInfo() const noexcept { return _frameInfo; } + + //! Get arguments count. + ASMJIT_INLINE uint32_t getArgCount() const noexcept { return _funcDetail.getArgCount(); } + //! Get returns count. + ASMJIT_INLINE uint32_t getRetCount() const noexcept { return _funcDetail.getRetCount(); } + + //! Get arguments list. + ASMJIT_INLINE VirtReg** getArgs() const noexcept { return _args; } + + //! Get argument at `i`. + ASMJIT_INLINE VirtReg* getArg(uint32_t i) const noexcept { + ASMJIT_ASSERT(i < getArgCount()); + return _args[i]; + } + + //! Set argument at `i`. + ASMJIT_INLINE void setArg(uint32_t i, VirtReg* vreg) noexcept { + ASMJIT_ASSERT(i < getArgCount()); + _args[i] = vreg; + } + + //! Reset argument at `i`. + ASMJIT_INLINE void resetArg(uint32_t i) noexcept { + ASMJIT_ASSERT(i < getArgCount()); + _args[i] = nullptr; + } + + ASMJIT_INLINE uint32_t getAttributes() const noexcept { return _frameInfo.getAttributes(); } + ASMJIT_INLINE void addAttributes(uint32_t attrs) noexcept { _frameInfo.addAttributes(attrs); } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + FuncDetail _funcDetail; //!< Function detail. + FuncFrameInfo _frameInfo; //!< Function frame information. + + CBLabel* _exitNode; //!< Function exit. + CBSentinel* _end; //!< Function end. + + VirtReg** _args; //!< Arguments array as `VirtReg`. + + //! Function was finished by `Compiler::endFunc()`. + uint8_t _isFinished; +}; + +// ============================================================================ +// [asmjit::CCFuncRet] +// ============================================================================ + +//! Function return (CodeCompiler). +class CCFuncRet : public CBNode { +public: + ASMJIT_NONCOPYABLE(CCFuncRet) + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a new `CCFuncRet` instance. + ASMJIT_INLINE CCFuncRet(CodeBuilder* cb, const Operand_& o0, const Operand_& o1) noexcept : CBNode(cb, kNodeFuncExit) { + orFlags(kFlagIsRet); + _ret[0].copyFrom(o0); + _ret[1].copyFrom(o1); + } + + //! Destroy the `CCFuncRet` instance (NEVER CALLED). + ASMJIT_INLINE ~CCFuncRet() noexcept {} + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get the first return operand. + ASMJIT_INLINE Operand& getFirst() noexcept { return static_cast(_ret[0]); } + //! \overload + ASMJIT_INLINE const Operand& getFirst() const noexcept { return static_cast(_ret[0]); } + + //! Get the second return operand. + ASMJIT_INLINE Operand& getSecond() noexcept { return static_cast(_ret[1]); } + //! \overload + ASMJIT_INLINE const Operand& getSecond() const noexcept { return static_cast(_ret[1]); } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + //! Return operands. + Operand_ _ret[2]; +}; + +// ============================================================================ +// [asmjit::CCFuncCall] +// ============================================================================ + +//! Function call (CodeCompiler). +class CCFuncCall : public CBInst { +public: + ASMJIT_NONCOPYABLE(CCFuncCall) + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a new `CCFuncCall` instance. + ASMJIT_INLINE CCFuncCall(CodeBuilder* cb, uint32_t instId, uint32_t options, Operand* opArray, uint32_t opCount) noexcept + : CBInst(cb, instId, options, opArray, opCount), + _funcDetail(), + _args(nullptr) { + + _type = kNodeFuncCall; + _ret[0].reset(); + _ret[1].reset(); + orFlags(kFlagIsRemovable); + } + + //! Destroy the `CCFuncCall` instance (NEVER CALLED). + ASMJIT_INLINE ~CCFuncCall() noexcept {} + + // -------------------------------------------------------------------------- + // [Signature] + // -------------------------------------------------------------------------- + + //! Set function signature. + ASMJIT_INLINE Error setSignature(const FuncSignature& sign) noexcept { + return _funcDetail.init(sign); + } + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get function declaration. + ASMJIT_INLINE FuncDetail& getDetail() noexcept { return _funcDetail; } + //! Get function declaration. + ASMJIT_INLINE const FuncDetail& getDetail() const noexcept { return _funcDetail; } + + //! Get target operand. + ASMJIT_INLINE Operand& getTarget() noexcept { return static_cast(_opArray[0]); } + //! \overload + ASMJIT_INLINE const Operand& getTarget() const noexcept { return static_cast(_opArray[0]); } + + //! Get return at `i`. + ASMJIT_INLINE Operand& getRet(uint32_t i = 0) noexcept { + ASMJIT_ASSERT(i < 2); + return static_cast(_ret[i]); + } + //! \overload + ASMJIT_INLINE const Operand& getRet(uint32_t i = 0) const noexcept { + ASMJIT_ASSERT(i < 2); + return static_cast(_ret[i]); + } + + //! Get argument at `i`. + ASMJIT_INLINE Operand& getArg(uint32_t i) noexcept { + ASMJIT_ASSERT(i < kFuncArgCountLoHi); + return static_cast(_args[i]); + } + //! \overload + ASMJIT_INLINE const Operand& getArg(uint32_t i) const noexcept { + ASMJIT_ASSERT(i < kFuncArgCountLoHi); + return static_cast(_args[i]); + } + + //! Set argument at `i` to `op`. + ASMJIT_API bool _setArg(uint32_t i, const Operand_& op) noexcept; + //! Set return at `i` to `op`. + ASMJIT_API bool _setRet(uint32_t i, const Operand_& op) noexcept; + + //! Set argument at `i` to `reg`. + ASMJIT_INLINE bool setArg(uint32_t i, const Reg& reg) noexcept { return _setArg(i, reg); } + //! Set argument at `i` to `imm`. + ASMJIT_INLINE bool setArg(uint32_t i, const Imm& imm) noexcept { return _setArg(i, imm); } + + //! Set return at `i` to `var`. + ASMJIT_INLINE bool setRet(uint32_t i, const Reg& reg) noexcept { return _setRet(i, reg); } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + FuncDetail _funcDetail; //!< Function detail. + Operand_ _ret[2]; //!< Return. + Operand_* _args; //!< Arguments. +}; + +// ============================================================================ +// [asmjit::CCPushArg] +// ============================================================================ + +//! Push argument before a function call (CodeCompiler). +class CCPushArg : public CBNode { +public: + ASMJIT_NONCOPYABLE(CCPushArg) + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a new `CCPushArg` instance. + ASMJIT_INLINE CCPushArg(CodeBuilder* cb, CCFuncCall* call, VirtReg* src, VirtReg* cvt) noexcept + : CBNode(cb, kNodePushArg), + _call(call), + _src(src), + _cvt(cvt), + _args(0) { + orFlags(kFlagIsRemovable); + } + + //! Destroy the `CCPushArg` instance. + ASMJIT_INLINE ~CCPushArg() noexcept {} + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get the associated function-call. + ASMJIT_INLINE CCFuncCall* getCall() const noexcept { return _call; } + //! Get source variable. + ASMJIT_INLINE VirtReg* getSrcReg() const noexcept { return _src; } + //! Get conversion variable. + ASMJIT_INLINE VirtReg* getCvtReg() const noexcept { return _cvt; } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + CCFuncCall* _call; //!< Associated `CCFuncCall`. + VirtReg* _src; //!< Source variable. + VirtReg* _cvt; //!< Temporary variable used for conversion (or null). + uint32_t _args; //!< Affected arguments bit-array. +}; + +// ============================================================================ +// [asmjit::CodeCompiler] +// ============================================================================ + +//! Code emitter that uses virtual registers and performs register allocation. +//! +//! Compiler is a high-level code-generation tool that provides register +//! allocation and automatic handling of function calling conventions. It was +//! primarily designed for merging multiple parts of code into a function +//! without worrying about registers and function calling conventions. +//! +//! CodeCompiler can be used, with a minimum effort, to handle 32-bit and 64-bit +//! code at the same time. +//! +//! CodeCompiler is based on CodeBuilder and contains all the features it +//! provides. It means that the code it stores can be modified (removed, added, +//! injected) and analyzed. When the code is finalized the compiler can emit +//! the code into an Assembler to translate the abstract representation into a +//! machine code. +class ASMJIT_VIRTAPI CodeCompiler : public CodeBuilder { +public: + ASMJIT_NONCOPYABLE(CodeCompiler) + typedef CodeBuilder Base; + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a new `CodeCompiler` instance. + ASMJIT_API CodeCompiler() noexcept; + //! Destroy the `CodeCompiler` instance. + ASMJIT_API virtual ~CodeCompiler() noexcept; + + // -------------------------------------------------------------------------- + // [Events] + // -------------------------------------------------------------------------- + + ASMJIT_API virtual Error onAttach(CodeHolder* code) noexcept override; + ASMJIT_API virtual Error onDetach(CodeHolder* code) noexcept override; + + // -------------------------------------------------------------------------- + // [Node-Factory] + // -------------------------------------------------------------------------- + + //! \internal + //! + //! Create a new `CCHint`. + ASMJIT_API CCHint* newHintNode(Reg& reg, uint32_t hint, uint32_t value) noexcept; + + // -------------------------------------------------------------------------- + // [Func] + // -------------------------------------------------------------------------- + + //! Get the current function. + ASMJIT_INLINE CCFunc* getFunc() const noexcept { return _func; } + + //! Create a new `CCFunc`. + ASMJIT_API CCFunc* newFunc(const FuncSignature& sign) noexcept; + //! Add a function `node` to the stream. + ASMJIT_API CCFunc* addFunc(CCFunc* func); + //! Add a new function. + ASMJIT_API CCFunc* addFunc(const FuncSignature& sign); + //! Emit a sentinel that marks the end of the current function. + ASMJIT_API CBSentinel* endFunc(); + + // -------------------------------------------------------------------------- + // [Ret] + // -------------------------------------------------------------------------- + + //! Create a new `CCFuncRet`. + ASMJIT_API CCFuncRet* newRet(const Operand_& o0, const Operand_& o1) noexcept; + //! Add a new `CCFuncRet`. + ASMJIT_API CCFuncRet* addRet(const Operand_& o0, const Operand_& o1) noexcept; + + // -------------------------------------------------------------------------- + // [Call] + // -------------------------------------------------------------------------- + + //! Create a new `CCFuncCall`. + ASMJIT_API CCFuncCall* newCall(uint32_t instId, const Operand_& o0, const FuncSignature& sign) noexcept; + //! Add a new `CCFuncCall`. + ASMJIT_API CCFuncCall* addCall(uint32_t instId, const Operand_& o0, const FuncSignature& sign) noexcept; + + // -------------------------------------------------------------------------- + // [Args] + // -------------------------------------------------------------------------- + + //! Set a function argument at `argIndex` to `reg`. + ASMJIT_API Error setArg(uint32_t argIndex, const Reg& reg); + + // -------------------------------------------------------------------------- + // [Hint] + // -------------------------------------------------------------------------- + + //! Emit a new hint (purely informational node). + ASMJIT_API Error _hint(Reg& reg, uint32_t hint, uint32_t value); + + // -------------------------------------------------------------------------- + // [VirtReg / Stack] + // -------------------------------------------------------------------------- + + //! Create a new virtual register representing the given `vti` and `signature`. + //! + //! This function accepts either register type representing a machine-specific + //! register, like `X86Reg`, or RegTag representation, which represents + //! machine independent register, and from the machine-specific register + //! is deduced. + ASMJIT_API VirtReg* newVirtReg(uint32_t typeId, uint32_t signature, const char* name) noexcept; + + ASMJIT_API Error _newReg(Reg& out, uint32_t typeId, const char* name); + ASMJIT_API Error _newReg(Reg& out, uint32_t typeId, const char* nameFmt, va_list ap); + + ASMJIT_API Error _newReg(Reg& out, const Reg& ref, const char* name); + ASMJIT_API Error _newReg(Reg& out, const Reg& ref, const char* nameFmt, va_list ap); + + ASMJIT_API Error _newStack(Mem& out, uint32_t size, uint32_t alignment, const char* name); + ASMJIT_API Error _newConst(Mem& out, uint32_t scope, const void* data, size_t size); + + // -------------------------------------------------------------------------- + // [VirtReg] + // -------------------------------------------------------------------------- + + //! Get whether the virtual register `r` is valid. + ASMJIT_INLINE bool isVirtRegValid(const Reg& reg) const noexcept { + return isVirtRegValid(reg.getId()); + } + //! \overload + ASMJIT_INLINE bool isVirtRegValid(uint32_t id) const noexcept { + size_t index = Operand::unpackId(id); + return index < _vRegArray.getLength(); + } + + //! Get \ref VirtReg associated with the given `r`. + ASMJIT_INLINE VirtReg* getVirtReg(const Reg& reg) const noexcept { + return getVirtRegById(reg.getId()); + } + //! Get \ref VirtReg associated with the given `id`. + ASMJIT_INLINE VirtReg* getVirtRegById(uint32_t id) const noexcept { + ASMJIT_ASSERT(id != kInvalidValue); + size_t index = Operand::unpackId(id); + + ASMJIT_ASSERT(index < _vRegArray.getLength()); + return _vRegArray[index]; + } + + //! Get an array of all virtual registers managed by CodeCompiler. + ASMJIT_INLINE const ZoneVector& getVirtRegArray() const noexcept { return _vRegArray; } + + //! Alloc a virtual register `reg`. + ASMJIT_API Error alloc(Reg& reg); + //! Alloc a virtual register `reg` using `physId` as a register id. + ASMJIT_API Error alloc(Reg& reg, uint32_t physId); + //! Alloc a virtual register `reg` using `ref` as a register operand. + ASMJIT_API Error alloc(Reg& reg, const Reg& ref); + //! Spill a virtual register `reg`. + ASMJIT_API Error spill(Reg& reg); + //! Save a virtual register `reg` if the status is `modified` at this point. + ASMJIT_API Error save(Reg& reg); + //! Unuse a virtual register `reg`. + ASMJIT_API Error unuse(Reg& reg); + + //! Get priority of a virtual register `reg`. + ASMJIT_API uint32_t getPriority(Reg& reg) const; + //! Set priority of variable `reg` to `priority`. + ASMJIT_API void setPriority(Reg& reg, uint32_t priority); + + //! Get save-on-unuse `reg` property. + ASMJIT_API bool getSaveOnUnuse(Reg& reg) const; + //! Set save-on-unuse `reg` property to `value`. + ASMJIT_API void setSaveOnUnuse(Reg& reg, bool value); + + //! Rename variable `reg` to `name`. + //! + //! NOTE: Only new name will appear in the logger. + ASMJIT_API void rename(Reg& reg, const char* fmt, ...); + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + CCFunc* _func; //!< Current function. + + Zone _vRegZone; //!< Allocates \ref VirtReg objects. + ZoneVector _vRegArray; //!< Stores array of \ref VirtReg pointers. + + CBConstPool* _localConstPool; //!< Local constant pool, flushed at the end of each function. + CBConstPool* _globalConstPool; //!< Global constant pool, flushed at the end of the compilation. +}; + +//! \} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" + +// [Guard] +#endif // !ASMJIT_DISABLE_COMPILER +#endif // _ASMJIT_BASE_CODECOMPILER_H diff --git a/libraries/asmjit/asmjit/base/codeemitter.cpp b/libraries/asmjit/asmjit/base/codeemitter.cpp new file mode 100644 index 00000000000..48a4c9a21c0 --- /dev/null +++ b/libraries/asmjit/asmjit/base/codeemitter.cpp @@ -0,0 +1,236 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Export] +#define ASMJIT_EXPORTS + +// [Dependencies] +#include "../base/assembler.h" +#include "../base/utils.h" +#include "../base/vmem.h" + +#if defined(ASMJIT_BUILD_X86) +#include "../x86/x86inst.h" +#endif // ASMJIT_BUILD_X86 + +#if defined(ASMJIT_BUILD_ARM) +#include "../arm/arminst.h" +#endif // ASMJIT_BUILD_ARM + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +// ============================================================================ +// [asmjit::CodeEmitter - Construction / Destruction] +// ============================================================================ + +CodeEmitter::CodeEmitter(uint32_t type) noexcept + : _codeInfo(), + _code(nullptr), + _nextEmitter(nullptr), + _type(static_cast(type)), + _destroyed(false), + _finalized(false), + _reserved(false), + _lastError(kErrorNotInitialized), + _privateData(0), + _globalHints(0), + _globalOptions(kOptionMaybeFailureCase), + _options(0), + _extraReg(), + _inlineComment(nullptr), + _none(), + _nativeGpReg(), + _nativeGpArray(nullptr) {} + +CodeEmitter::~CodeEmitter() noexcept { + if (_code) { + _destroyed = true; + _code->detach(this); + } +} + +// ============================================================================ +// [asmjit::CodeEmitter - Events] +// ============================================================================ + +Error CodeEmitter::onAttach(CodeHolder* code) noexcept { + _codeInfo = code->getCodeInfo(); + _lastError = kErrorOk; + + _globalHints = code->getGlobalHints(); + _globalOptions = code->getGlobalOptions(); + + return kErrorOk; +} + +Error CodeEmitter::onDetach(CodeHolder* code) noexcept { + _codeInfo.reset(); + _finalized = false; + _lastError = kErrorNotInitialized; + + _privateData = 0; + _globalHints = 0; + _globalOptions = kOptionMaybeFailureCase; + + _options = 0; + _extraReg.reset(); + _inlineComment = nullptr; + + _nativeGpReg.reset(); + _nativeGpArray = nullptr; + + return kErrorOk; +} + +// ============================================================================ +// [asmjit::CodeEmitter - Code-Generation] +// ============================================================================ + +Error CodeEmitter::_emitOpArray(uint32_t instId, const Operand_* opArray, size_t opCount) { + const Operand_* op = opArray; + switch (opCount) { + case 0: return _emit(instId, _none, _none, _none, _none); + case 1: return _emit(instId, op[0], _none, _none, _none); + case 2: return _emit(instId, op[0], op[1], _none, _none); + case 3: return _emit(instId, op[0], op[1], op[2], _none); + case 4: return _emit(instId, op[0], op[1], op[2], op[3]); + case 5: return _emit(instId, op[0], op[1], op[2], op[3], op[4], _none); + case 6: return _emit(instId, op[0], op[1], op[2], op[3], op[4], op[5]); + + default: + return DebugUtils::errored(kErrorInvalidArgument); + } +} + +// ============================================================================ +// [asmjit::CodeEmitter - Finalize] +// ============================================================================ + +Label CodeEmitter::getLabelByName(const char* name, size_t nameLength, uint32_t parentId) noexcept { + return Label(_code ? _code->getLabelIdByName(name, nameLength, parentId) : static_cast(0)); +} + +// ============================================================================ +// [asmjit::CodeEmitter - Finalize] +// ============================================================================ + +Error CodeEmitter::finalize() { + // Finalization does nothing by default, overridden by `CodeBuilder`. + return kErrorOk; +} + +// ============================================================================ +// [asmjit::CodeEmitter - Error Handling] +// ============================================================================ + +Error CodeEmitter::setLastError(Error error, const char* message) { + // This is fatal, CodeEmitter can't set error without being attached to `CodeHolder`. + ASMJIT_ASSERT(_code != nullptr); + + // Special case used to reset the last error. + if (error == kErrorOk) { + _lastError = kErrorOk; + _globalOptions &= ~kOptionMaybeFailureCase; + return kErrorOk; + } + + if (!message) + message = DebugUtils::errorAsString(error); + + // Logging is skipped if the error is handled by `ErrorHandler`. + ErrorHandler* handler = _code->_errorHandler; + if (handler && handler->handleError(error, message, this)) + return error; + + // The handler->handleError() function may throw an exception or longjmp() + // to terminate the execution of `setLastError()`. This is the reason why + // we have delayed changing the `_error` member until now. + _lastError = error; + _globalOptions |= kOptionMaybeFailureCase; + + return error; +} + +// ============================================================================ +// [asmjit::CodeEmitter - Helpers] +// ============================================================================ + +bool CodeEmitter::isLabelValid(uint32_t id) const noexcept { + size_t index = Operand::unpackId(id); + return _code && index < _code->_labels.getLength(); +} + +Error CodeEmitter::commentf(const char* fmt, ...) { + Error err = _lastError; + if (err) return err; + +#if !defined(ASMJIT_DISABLE_LOGGING) + if (_globalOptions & kOptionLoggingEnabled) { + va_list ap; + va_start(ap, fmt); + err = _code->_logger->logv(fmt, ap); + va_end(ap); + } +#else + ASMJIT_UNUSED(fmt); +#endif + + return err; +} + +Error CodeEmitter::commentv(const char* fmt, va_list ap) { + Error err = _lastError; + if (err) return err; + +#if !defined(ASMJIT_DISABLE_LOGGING) + if (_globalOptions & kOptionLoggingEnabled) + err = _code->_logger->logv(fmt, ap); +#else + ASMJIT_UNUSED(fmt); + ASMJIT_UNUSED(ap); +#endif + + return err; +} + +// ============================================================================ +// [asmjit::CodeEmitter - Emit] +// ============================================================================ + +#define OP const Operand_& + +Error CodeEmitter::emit(uint32_t instId) { return _emit(instId, _none, _none, _none, _none); } +Error CodeEmitter::emit(uint32_t instId, OP o0) { return _emit(instId, o0, _none, _none, _none); } +Error CodeEmitter::emit(uint32_t instId, OP o0, OP o1) { return _emit(instId, o0, o1, _none, _none); } +Error CodeEmitter::emit(uint32_t instId, OP o0, OP o1, OP o2) { return _emit(instId, o0, o1, o2, _none); } +Error CodeEmitter::emit(uint32_t instId, OP o0, OP o1, OP o2, OP o3) { return _emit(instId, o0, o1, o2, o3); } +Error CodeEmitter::emit(uint32_t instId, OP o0, OP o1, OP o2, OP o3, OP o4) { return _emit(instId, o0, o1, o2, o3, o4, _none); } +Error CodeEmitter::emit(uint32_t instId, OP o0, OP o1, OP o2, OP o3, OP o4, OP o5) { return _emit(instId, o0, o1, o2, o3, o4, o5); } + +Error CodeEmitter::emit(uint32_t instId, int o0) { return _emit(instId, Imm(o0), _none, _none, _none); } +Error CodeEmitter::emit(uint32_t instId, OP o0, int o1) { return _emit(instId, o0, Imm(o1), _none, _none); } +Error CodeEmitter::emit(uint32_t instId, OP o0, OP o1, int o2) { return _emit(instId, o0, o1, Imm(o2), _none); } +Error CodeEmitter::emit(uint32_t instId, OP o0, OP o1, OP o2, int o3) { return _emit(instId, o0, o1, o2, Imm(o3)); } +Error CodeEmitter::emit(uint32_t instId, OP o0, OP o1, OP o2, OP o3, int o4) { return _emit(instId, o0, o1, o2, o3, Imm(o4), _none); } +Error CodeEmitter::emit(uint32_t instId, OP o0, OP o1, OP o2, OP o3, OP o4, int o5) { return _emit(instId, o0, o1, o2, o3, o4, Imm(o5)); } + +Error CodeEmitter::emit(uint32_t instId, int64_t o0) { return _emit(instId, Imm(o0), _none, _none, _none); } +Error CodeEmitter::emit(uint32_t instId, OP o0, int64_t o1) { return _emit(instId, o0, Imm(o1), _none, _none); } +Error CodeEmitter::emit(uint32_t instId, OP o0, OP o1, int64_t o2) { return _emit(instId, o0, o1, Imm(o2), _none); } +Error CodeEmitter::emit(uint32_t instId, OP o0, OP o1, OP o2, int64_t o3) { return _emit(instId, o0, o1, o2, Imm(o3)); } + +Error CodeEmitter::emit(uint32_t instId, OP o0, OP o1, OP o2, OP o3, int64_t o4) { return _emit(instId, o0, o1, o2, o3, Imm(o4), _none); } +Error CodeEmitter::emit(uint32_t instId, OP o0, OP o1, OP o2, OP o3, OP o4, int64_t o5) { return _emit(instId, o0, o1, o2, o3, o4, Imm(o5)); } + +#undef OP + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" diff --git a/libraries/asmjit/asmjit/base/codeemitter.h b/libraries/asmjit/asmjit/base/codeemitter.h new file mode 100644 index 00000000000..93a2de36fff --- /dev/null +++ b/libraries/asmjit/asmjit/base/codeemitter.h @@ -0,0 +1,499 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Guard] +#ifndef _ASMJIT_BASE_CODEEMITTER_H +#define _ASMJIT_BASE_CODEEMITTER_H + +// [Dependencies] +#include "../base/arch.h" +#include "../base/codeholder.h" +#include "../base/operand.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +//! \addtogroup asmjit_base +//! \{ + +// ============================================================================ +// [Forward Declarations] +// ============================================================================ + +class ConstPool; + +// ============================================================================ +// [asmjit::CodeEmitter] +// ============================================================================ + +//! Provides a base foundation to emit code - specialized by \ref Assembler and +//! \ref CodeBuilder. +class ASMJIT_VIRTAPI CodeEmitter { +public: + //! CodeEmitter type. + ASMJIT_ENUM(Type) { + kTypeNone = 0, + kTypeAssembler = 1, + kTypeBuilder = 2, + kTypeCompiler = 3, + kTypeCount = 4 + }; + + //! CodeEmitter hints - global settings that affect machine-code generation. + ASMJIT_ENUM(Hints) { + //! Emit optimized code-alignment sequences. + //! + //! Default `true`. + //! + //! X86/X64 Specific + //! ---------------- + //! + //! Default align sequence used by X86/X64 architecture is one-byte (0x90) + //! opcode that is often shown by disassemblers as nop. However there are + //! more optimized align sequences for 2-11 bytes that may execute faster. + //! If this feature is enabled AsmJit will generate specialized sequences + //! for alignment between 2 to 11 bytes. + kHintOptimizedAlign = 0x00000001U, + + //! Emit jump-prediction hints. + //! + //! Default `false`. + //! + //! X86/X64 Specific + //! ---------------- + //! + //! Jump prediction is usually based on the direction of the jump. If the + //! jump is backward it is usually predicted as taken; and if the jump is + //! forward it is usually predicted as not-taken. The reason is that loops + //! generally use backward jumps and conditions usually use forward jumps. + //! However this behavior can be overridden by using instruction prefixes. + //! If this option is enabled these hints will be emitted. + //! + //! This feature is disabled by default, because the only processor that + //! used to take into consideration prediction hints was P4. Newer processors + //! implement heuristics for branch prediction that ignores any static hints. + kHintPredictedJumps = 0x00000002U + }; + + //! CodeEmitter options that are merged with instruction options. + ASMJIT_ENUM(Options) { + //! Reserved, used to check for errors in `Assembler::_emit()`. In addition, + //! if an emitter is in error state it will have `kOptionMaybeFailureCase` + //! set + kOptionMaybeFailureCase = 0x00000001U, + + //! Perform a strict validation before the instruction is emitted. + kOptionStrictValidation = 0x00000002U, + + //! Logging is enabled and `CodeHolder::getLogger()` should return a valid + //! \ref Logger pointer. + kOptionLoggingEnabled = 0x00000004U, + + //! Mask of all internal options that are not used to represent instruction + //! options, but are used to instrument Assembler and CodeBuilder. These + //! options are internal and should not be used outside of AsmJit itself. + //! + //! NOTE: Reserved options should never appear in `CBInst` options. + kOptionReservedMask = 0x00000007U, + + //! Used only by Assembler to mark `_op4` and `_op5` are used. + kOptionOp4Op5Used = 0x00000008U, + + //! Prevents following a jump during compilation (CodeCompiler). + kOptionUnfollow = 0x00000010U, + + //! Overwrite the destination operand (CodeCompiler). + //! + //! Hint that is important for register liveness analysis. It tells the + //! compiler that the destination operand will be overwritten now or by + //! adjacent instructions. CodeCompiler knows when a register is completely + //! overwritten by a single instruction, for example you don't have to + //! mark "movaps" or "pxor x, x", however, if a pair of instructions is + //! used and the first of them doesn't completely overwrite the content + //! of the destination, CodeCompiler fails to mark that register as dead. + //! + //! X86/X64 Specific + //! ---------------- + //! + //! - All instructions that always overwrite at least the size of the + //! register the virtual-register uses , for example "mov", "movq", + //! "movaps" don't need the overwrite option to be used - conversion, + //! shuffle, and other miscellaneous instructions included. + //! + //! - All instructions that clear the destination register if all operands + //! are the same, for example "xor x, x", "pcmpeqb x x", etc... + //! + //! - Consecutive instructions that partially overwrite the variable until + //! there is no old content require the `overwrite()` to be used. Some + //! examples (not always the best use cases thought): + //! + //! - `movlps xmm0, ?` followed by `movhps xmm0, ?` and vice versa + //! - `movlpd xmm0, ?` followed by `movhpd xmm0, ?` and vice versa + //! - `mov al, ?` followed by `and ax, 0xFF` + //! - `mov al, ?` followed by `mov ah, al` + //! - `pinsrq xmm0, ?, 0` followed by `pinsrq xmm0, ?, 1` + //! + //! - If allocated variable is used temporarily for scalar operations. For + //! example if you allocate a full vector like `X86Compiler::newXmm()` + //! and then use that vector for scalar operations you should use + //! `overwrite()` directive: + //! + //! - `sqrtss x, y` - only LO element of `x` is changed, if you don't use + //! HI elements, use `X86Compiler.overwrite().sqrtss(x, y)`. + kOptionOverwrite = 0x00000020U + }; + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + ASMJIT_API CodeEmitter(uint32_t type) noexcept; + ASMJIT_API virtual ~CodeEmitter() noexcept; + + // -------------------------------------------------------------------------- + // [Events] + // -------------------------------------------------------------------------- + + //! Called after the \ref CodeEmitter was attached to the \ref CodeHolder. + virtual Error onAttach(CodeHolder* code) noexcept = 0; + //! Called after the \ref CodeEmitter was detached from the \ref CodeHolder. + virtual Error onDetach(CodeHolder* code) noexcept = 0; + + // -------------------------------------------------------------------------- + // [Code-Generation] + // -------------------------------------------------------------------------- + + //! Emit instruction having max 4 operands. + virtual Error _emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3) = 0; + //! Emit instruction having max 6 operands. + virtual Error _emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, const Operand_& o4, const Operand_& o5) = 0; + //! Emit instruction having operands stored in array. + virtual Error _emitOpArray(uint32_t instId, const Operand_* opArray, size_t opCount); + + //! Create a new label. + virtual Label newLabel() = 0; + //! Create a new named label. + virtual Label newNamedLabel( + const char* name, + size_t nameLength = Globals::kInvalidIndex, + uint32_t type = Label::kTypeGlobal, + uint32_t parentId = 0) = 0; + + //! Get a label by name. + //! + //! Returns invalid Label in case that the name is invalid or label was not found. + //! + //! NOTE: This function doesn't trigger ErrorHandler in case the name is + //! invalid or no such label exist. You must always check the validity of the + //! \ref Label returned. + ASMJIT_API Label getLabelByName( + const char* name, + size_t nameLength = Globals::kInvalidIndex, + uint32_t parentId = 0) noexcept; + + //! Bind the `label` to the current position of the current section. + //! + //! NOTE: Attempt to bind the same label multiple times will return an error. + virtual Error bind(const Label& label) = 0; + + //! Align to the `alignment` specified. + //! + //! The sequence that is used to fill the gap between the aligned location + //! and the current location depends on the align `mode`, see \ref AlignMode. + virtual Error align(uint32_t mode, uint32_t alignment) = 0; + + //! Embed raw data into the code-buffer. + virtual Error embed(const void* data, uint32_t size) = 0; + + //! Embed absolute label address as data (4 or 8 bytes). + virtual Error embedLabel(const Label& label) = 0; + + //! Embed a constant pool into the code-buffer in the following steps: + //! 1. Align by using kAlignData to the minimum `pool` alignment. + //! 2. Bind `label` so it's bound to an aligned location. + //! 3. Emit constant pool data. + virtual Error embedConstPool(const Label& label, const ConstPool& pool) = 0; + + //! Emit a comment string `s` with an optional `len` parameter. + virtual Error comment(const char* s, size_t len = Globals::kInvalidIndex) = 0; + + // -------------------------------------------------------------------------- + // [Code-Generation Status] + // -------------------------------------------------------------------------- + + //! Get if the CodeEmitter is initialized (i.e. attached to a \ref CodeHolder). + ASMJIT_INLINE bool isInitialized() const noexcept { return _code != nullptr; } + + ASMJIT_API virtual Error finalize(); + + // -------------------------------------------------------------------------- + // [Code Information] + // -------------------------------------------------------------------------- + + //! Get information about the code, see \ref CodeInfo. + ASMJIT_INLINE const CodeInfo& getCodeInfo() const noexcept { return _codeInfo; } + //! Get \ref CodeHolder this CodeEmitter is attached to. + ASMJIT_INLINE CodeHolder* getCode() const noexcept { return _code; } + + //! Get information about the architecture, see \ref ArchInfo. + ASMJIT_INLINE const ArchInfo& getArchInfo() const noexcept { return _codeInfo.getArchInfo(); } + + //! Get if the target architecture is 32-bit. + ASMJIT_INLINE bool is32Bit() const noexcept { return getArchInfo().is32Bit(); } + //! Get if the target architecture is 64-bit. + ASMJIT_INLINE bool is64Bit() const noexcept { return getArchInfo().is64Bit(); } + + //! Get the target architecture type. + ASMJIT_INLINE uint32_t getArchType() const noexcept { return getArchInfo().getType(); } + //! Get the target architecture sub-type. + ASMJIT_INLINE uint32_t getArchSubType() const noexcept { return getArchInfo().getSubType(); } + //! Get the target architecture's GP register size (4 or 8 bytes). + ASMJIT_INLINE uint32_t getGpSize() const noexcept { return getArchInfo().getGpSize(); } + //! Get the number of target GP registers. + ASMJIT_INLINE uint32_t getGpCount() const noexcept { return getArchInfo().getGpCount(); } + + // -------------------------------------------------------------------------- + // [Code-Emitter Type] + // -------------------------------------------------------------------------- + + //! Get the type of this CodeEmitter, see \ref Type. + ASMJIT_INLINE uint32_t getType() const noexcept { return _type; } + + ASMJIT_INLINE bool isAssembler() const noexcept { return _type == kTypeAssembler; } + ASMJIT_INLINE bool isCodeBuilder() const noexcept { return _type == kTypeBuilder; } + ASMJIT_INLINE bool isCodeCompiler() const noexcept { return _type == kTypeCompiler; } + + // -------------------------------------------------------------------------- + // [Global Information] + // -------------------------------------------------------------------------- + + //! Get global hints. + ASMJIT_INLINE uint32_t getGlobalHints() const noexcept { return _globalHints; } + + //! Get global options. + //! + //! Global options are merged with instruction options before the instruction + //! is encoded. These options have some bits reserved that are used for error + //! checking, logging, and strict validation. Other options are globals that + //! affect each instruction, for example if VEX3 is set globally, it will all + //! instructions, even those that don't have such option set. + ASMJIT_INLINE uint32_t getGlobalOptions() const noexcept { return _globalOptions; } + + // -------------------------------------------------------------------------- + // [Error Handling] + // -------------------------------------------------------------------------- + + //! Get if the object is in error state. + //! + //! Error state means that it does not consume anything unless the error + //! state is reset by calling `resetLastError()`. Use `getLastError()` to + //! get the last error that put the object into the error state. + ASMJIT_INLINE bool isInErrorState() const noexcept { return _lastError != kErrorOk; } + + //! Get the last error code. + ASMJIT_INLINE Error getLastError() const noexcept { return _lastError; } + //! Set the last error code and propagate it through the error handler. + ASMJIT_API Error setLastError(Error error, const char* message = nullptr); + //! Clear the last error code and return `kErrorOk`. + ASMJIT_INLINE Error resetLastError() noexcept { return setLastError(kErrorOk); } + + // -------------------------------------------------------------------------- + // [Accessors That Affect the Next Instruction] + // -------------------------------------------------------------------------- + + //! Get options of the next instruction. + ASMJIT_INLINE uint32_t getOptions() const noexcept { return _options; } + //! Set options of the next instruction. + ASMJIT_INLINE void setOptions(uint32_t options) noexcept { _options = options; } + //! Add options of the next instruction. + ASMJIT_INLINE void addOptions(uint32_t options) noexcept { _options |= options; } + //! Reset options of the next instruction. + ASMJIT_INLINE void resetOptions() noexcept { _options = 0; } + + //! Get if the extra register operand is valid. + ASMJIT_INLINE bool hasExtraReg() const noexcept { return _extraReg.isValid(); } + //! Get an extra operand that will be used by the next instruction (architecture specific). + ASMJIT_INLINE const RegOnly& getExtraReg() const noexcept { return _extraReg; } + //! Set an extra operand that will be used by the next instruction (architecture specific). + ASMJIT_INLINE void setExtraReg(const Reg& reg) noexcept { _extraReg.init(reg); } + //! Set an extra operand that will be used by the next instruction (architecture specific). + ASMJIT_INLINE void setExtraReg(const RegOnly& reg) noexcept { _extraReg.init(reg); } + //! Reset an extra operand that will be used by the next instruction (architecture specific). + ASMJIT_INLINE void resetExtraReg() noexcept { _extraReg.reset(); } + + //! Get annotation of the next instruction. + ASMJIT_INLINE const char* getInlineComment() const noexcept { return _inlineComment; } + //! Set annotation of the next instruction. + //! + //! NOTE: This string is set back to null by `_emit()`, but until that it has + //! to remain valid as `CodeEmitter` is not required to make a copy of it (and + //! it would be slow to do that for each instruction). + ASMJIT_INLINE void setInlineComment(const char* s) noexcept { _inlineComment = s; } + //! Reset annotation of the next instruction to null. + ASMJIT_INLINE void resetInlineComment() noexcept { _inlineComment = nullptr; } + + // -------------------------------------------------------------------------- + // [Helpers] + // -------------------------------------------------------------------------- + + //! Get if the `label` is valid (i.e. registered). + ASMJIT_INLINE bool isLabelValid(const Label& label) const noexcept { + return isLabelValid(label.getId()); + } + + //! Get if the label `id` is valid (i.e. registered). + ASMJIT_API bool isLabelValid(uint32_t id) const noexcept; + + //! Emit a formatted string `fmt`. + ASMJIT_API Error commentf(const char* fmt, ...); + //! Emit a formatted string `fmt` (va_list version). + ASMJIT_API Error commentv(const char* fmt, va_list ap); + + // -------------------------------------------------------------------------- + // [Emit] + // -------------------------------------------------------------------------- + + // NOTE: These `emit()` helpers are designed to address a code-bloat generated + // by C++ compilers to call a function having many arguments. Each parameter to + // `_emit()` requires code to pass it, which means that if we default to 4 + // operand parameters in `_emit()` and instId the C++ compiler would have to + // generate a virtual function call having 5 parameters, which is quite a lot. + // Since by default asm instructions have 2 to 3 operands it's better to + // introduce helpers that pass those and fill all the remaining with `_none`. + + //! Emit an instruction. + ASMJIT_API Error emit(uint32_t instId); + //! \overload + ASMJIT_API Error emit(uint32_t instId, const Operand_& o0); + //! \overload + ASMJIT_API Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1); + //! \overload + ASMJIT_API Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2); + //! \overload + ASMJIT_API Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3); + //! \overload + ASMJIT_API Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, const Operand_& o4); + //! \overload + ASMJIT_API Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, const Operand_& o4, const Operand_& o5); + + //! Emit an instruction that has a 32-bit signed immediate operand. + ASMJIT_API Error emit(uint32_t instId, int o0); + //! \overload + ASMJIT_API Error emit(uint32_t instId, const Operand_& o0, int o1); + //! \overload + ASMJIT_API Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, int o2); + //! \overload + ASMJIT_API Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, int o3); + //! \overload + ASMJIT_API Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, int o4); + //! \overload + ASMJIT_API Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, const Operand_& o4, int o5); + + //! Emit an instruction that has a 64-bit signed immediate operand. + ASMJIT_API Error emit(uint32_t instId, int64_t o0); + //! \overload + ASMJIT_API Error emit(uint32_t instId, const Operand_& o0, int64_t o1); + //! \overload + ASMJIT_API Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, int64_t o2); + //! \overload + ASMJIT_API Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, int64_t o3); + //! \overload + ASMJIT_API Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, int64_t o4); + //! \overload + ASMJIT_API Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, const Operand_& o4, int64_t o5); + + //! \overload + ASMJIT_INLINE Error emit(uint32_t instId, unsigned int o0) { + return emit(instId, static_cast(o0)); + } + //! \overload + ASMJIT_INLINE Error emit(uint32_t instId, const Operand_& o0, unsigned int o1) { + return emit(instId, o0, static_cast(o1)); + } + //! \overload + ASMJIT_INLINE Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, unsigned int o2) { + return emit(instId, o0, o1, static_cast(o2)); + } + //! \overload + ASMJIT_INLINE Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, unsigned int o3) { + return emit(instId, o0, o1, o2, static_cast(o3)); + } + //! \overload + ASMJIT_INLINE Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, unsigned int o4) { + return emit(instId, o0, o1, o2, o3, static_cast(o4)); + } + //! \overload + ASMJIT_INLINE Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, const Operand_& o4, unsigned int o5) { + return emit(instId, o0, o1, o2, o3, o4, static_cast(o5)); + } + + //! \overload + ASMJIT_INLINE Error emit(uint32_t instId, uint64_t o0) { + return emit(instId, static_cast(o0)); + } + //! \overload + ASMJIT_INLINE Error emit(uint32_t instId, const Operand_& o0, uint64_t o1) { + return emit(instId, o0, static_cast(o1)); + } + //! \overload + ASMJIT_INLINE Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, uint64_t o2) { + return emit(instId, o0, o1, static_cast(o2)); + } + //! \overload + ASMJIT_INLINE Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, uint64_t o3) { + return emit(instId, o0, o1, o2, static_cast(o3)); + } + //! \overload + ASMJIT_INLINE Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, uint64_t o4) { + return emit(instId, o0, o1, o2, o3, static_cast(o4)); + } + //! \overload + ASMJIT_INLINE Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, const Operand_& o4, uint64_t o5) { + return emit(instId, o0, o1, o2, o3, o4, static_cast(o5)); + } + + ASMJIT_INLINE Error emitOpArray(uint32_t instId, const Operand_* opArray, size_t opCount) { + return _emitOpArray(instId, opArray, opCount); + } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + CodeInfo _codeInfo; //!< Basic information about the code (matches CodeHolder::_codeInfo). + CodeHolder* _code; //!< CodeHolder the CodeEmitter is attached to. + CodeEmitter* _nextEmitter; //!< Linked list of `CodeEmitter`s attached to the same \ref CodeHolder. + + uint8_t _type; //!< See CodeEmitter::Type. + uint8_t _destroyed; //!< Set by ~CodeEmitter() before calling `_code->detach()`. + uint8_t _finalized; //!< True if the CodeEmitter is finalized (CodeBuilder & CodeCompiler). + uint8_t _reserved; //!< \internal + Error _lastError; //!< Last error code. + + uint32_t _privateData; //!< Internal private data used freely by any CodeEmitter. + uint32_t _globalHints; //!< Global hints, always in sync with CodeHolder. + uint32_t _globalOptions; //!< Global options, combined with `_options` before used by each instruction. + + uint32_t _options; //!< Used to pass instruction options (affects the next instruction). + RegOnly _extraReg; //!< Extra register (op-mask {k} on AVX-512) (affects the next instruction). + const char* _inlineComment; //!< Inline comment of the next instruction (affects the next instruction). + + Operand_ _none; //!< Used to pass unused operands to `_emit()` instead of passing null. + Reg _nativeGpReg; //!< Native GP register with zero id. + const Reg* _nativeGpArray; //!< Array of native registers indexed from zero. +}; + +//! \} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" + +// [Guard] +#endif // _ASMJIT_BASE_CODEEMITTER_H diff --git a/libraries/asmjit/asmjit/base/codeholder.cpp b/libraries/asmjit/asmjit/base/codeholder.cpp new file mode 100644 index 00000000000..282f01289ca --- /dev/null +++ b/libraries/asmjit/asmjit/base/codeholder.cpp @@ -0,0 +1,696 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Export] +#define ASMJIT_EXPORTS + +// [Dependencies] +#include "../base/assembler.h" +#include "../base/utils.h" +#include "../base/vmem.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +// ============================================================================ +// [asmjit::ErrorHandler] +// ============================================================================ + +ErrorHandler::ErrorHandler() noexcept {} +ErrorHandler::~ErrorHandler() noexcept {} + +// ============================================================================ +// [asmjit::CodeHolder - Utilities] +// ============================================================================ + +static void CodeHolder_setGlobalOption(CodeHolder* self, uint32_t clear, uint32_t add) noexcept { + // Modify global options of `CodeHolder` itself. + self->_globalOptions = (self->_globalOptions & ~clear) | add; + + // Modify all global options of all `CodeEmitter`s attached. + CodeEmitter* emitter = self->_emitters; + while (emitter) { + emitter->_globalOptions = (emitter->_globalOptions & ~clear) | add; + emitter = emitter->_nextEmitter; + } +} + +static void CodeHolder_resetInternal(CodeHolder* self, bool releaseMemory) noexcept { + // Detach all `CodeEmitter`s. + while (self->_emitters) + self->detach(self->_emitters); + + // Reset everything into its construction state. + self->_codeInfo.reset(); + self->_globalHints = 0; + self->_globalOptions = 0; + self->_logger = nullptr; + self->_errorHandler = nullptr; + + self->_unresolvedLabelsCount = 0; + self->_trampolinesSize = 0; + + // Reset all sections. + size_t numSections = self->_sections.getLength(); + for (size_t i = 0; i < numSections; i++) { + SectionEntry* section = self->_sections[i]; + if (section->_buffer.hasData() && !section->_buffer.isExternal()) + Internal::releaseMemory(section->_buffer._data); + section->_buffer._data = nullptr; + section->_buffer._capacity = 0; + } + + // Reset zone allocator and all containers using it. + ZoneHeap* heap = &self->_baseHeap; + + self->_namedLabels.reset(heap); + self->_relocations.reset(); + self->_labels.reset(); + self->_sections.reset(); + + heap->reset(&self->_baseZone); + self->_baseZone.reset(releaseMemory); +} + +// ============================================================================ +// [asmjit::CodeHolder - Construction / Destruction] +// ============================================================================ + +CodeHolder::CodeHolder() noexcept + : _codeInfo(), + _globalHints(0), + _globalOptions(0), + _emitters(nullptr), + _cgAsm(nullptr), + _logger(nullptr), + _errorHandler(nullptr), + _unresolvedLabelsCount(0), + _trampolinesSize(0), + _baseZone(16384 - Zone::kZoneOverhead), + _dataZone(16384 - Zone::kZoneOverhead), + _baseHeap(&_baseZone), + _namedLabels(&_baseHeap) {} + +CodeHolder::~CodeHolder() noexcept { + CodeHolder_resetInternal(this, true); +} + +// ============================================================================ +// [asmjit::CodeHolder - Init / Reset] +// ============================================================================ + +Error CodeHolder::init(const CodeInfo& info) noexcept { + // Cannot reinitialize if it's locked or there is one or more CodeEmitter + // attached. + if (isInitialized()) + return DebugUtils::errored(kErrorAlreadyInitialized); + + // If we are just initializing there should be no emitters attached). + ASMJIT_ASSERT(_emitters == nullptr); + + // Create the default section and insert it to the `_sections` array. + Error err = _sections.willGrow(&_baseHeap); + if (err == kErrorOk) { + SectionEntry* se = _baseZone.allocZeroedT(); + if (ASMJIT_LIKELY(se)) { + se->_flags = SectionEntry::kFlagExec | SectionEntry::kFlagConst; + se->_setDefaultName('.', 't', 'e', 'x', 't'); + _sections.appendUnsafe(se); + } + else { + err = DebugUtils::errored(kErrorNoHeapMemory); + } + } + + if (ASMJIT_UNLIKELY(err)) { + _baseZone.reset(false); + return err; + } + else { + _codeInfo = info; + return kErrorOk; + } +} + +void CodeHolder::reset(bool releaseMemory) noexcept { + CodeHolder_resetInternal(this, releaseMemory); +} + +// ============================================================================ +// [asmjit::CodeHolder - Attach / Detach] +// ============================================================================ + +Error CodeHolder::attach(CodeEmitter* emitter) noexcept { + // Catch a possible misuse of the API. + if (!emitter) + return DebugUtils::errored(kErrorInvalidArgument); + + uint32_t type = emitter->getType(); + if (type == CodeEmitter::kTypeNone || type >= CodeEmitter::kTypeCount) + return DebugUtils::errored(kErrorInvalidState); + + // This is suspicious, but don't fail if `emitter` matches. + if (emitter->_code != nullptr) { + if (emitter->_code == this) return kErrorOk; + return DebugUtils::errored(kErrorInvalidState); + } + + // Special case - attach `Assembler`. + CodeEmitter** pSlot = nullptr; + if (type == CodeEmitter::kTypeAssembler) { + if (_cgAsm) + return DebugUtils::errored(kErrorSlotOccupied); + pSlot = reinterpret_cast(&_cgAsm); + } + + Error err = emitter->onAttach(this); + if (err != kErrorOk) return err; + + // Add to a single-linked list of `CodeEmitter`s. + emitter->_nextEmitter = _emitters; + _emitters = emitter; + if (pSlot) *pSlot = emitter; + + // Establish the connection. + emitter->_code = this; + return kErrorOk; +} + +Error CodeHolder::detach(CodeEmitter* emitter) noexcept { + if (!emitter) + return DebugUtils::errored(kErrorInvalidArgument); + + if (emitter->_code != this) + return DebugUtils::errored(kErrorInvalidState); + + uint32_t type = emitter->getType(); + Error err = kErrorOk; + + // NOTE: We always detach if we were asked to, if error happens during + // `emitter->onDetach()` we just propagate it, but the CodeEmitter will + // be detached. + if (!emitter->_destroyed) { + if (type == CodeEmitter::kTypeAssembler) + static_cast(emitter)->sync(); + err = emitter->onDetach(this); + } + + // Special case - detach `Assembler`. + if (type == CodeEmitter::kTypeAssembler) + _cgAsm = nullptr; + + // Remove from a single-linked list of `CodeEmitter`s. + CodeEmitter** pPrev = &_emitters; + for (;;) { + ASMJIT_ASSERT(*pPrev != nullptr); + CodeEmitter* cur = *pPrev; + + if (cur == emitter) { + *pPrev = emitter->_nextEmitter; + break; + } + + pPrev = &cur->_nextEmitter; + } + + emitter->_code = nullptr; + emitter->_nextEmitter = nullptr; + + return err; +} + +// ============================================================================ +// [asmjit::CodeHolder - Sync] +// ============================================================================ + +void CodeHolder::sync() noexcept { + if (_cgAsm) _cgAsm->sync(); +} + +// ============================================================================ +// [asmjit::CodeHolder - Result Information] +// ============================================================================ + +size_t CodeHolder::getCodeSize() const noexcept { + // Reflect all changes first. + const_cast(this)->sync(); + + // TODO: Support sections. + return _sections[0]->_buffer._length + getTrampolinesSize(); +} + +// ============================================================================ +// [asmjit::CodeHolder - Logging & Error Handling] +// ============================================================================ + +#if !defined(ASMJIT_DISABLE_LOGGING) +void CodeHolder::setLogger(Logger* logger) noexcept { + uint32_t opt = 0; + if (logger) opt = CodeEmitter::kOptionLoggingEnabled; + + _logger = logger; + CodeHolder_setGlobalOption(this, CodeEmitter::kOptionLoggingEnabled, opt); +} +#endif // !ASMJIT_DISABLE_LOGGING + +Error CodeHolder::setErrorHandler(ErrorHandler* handler) noexcept { + _errorHandler = handler; + return kErrorOk; +} + +// ============================================================================ +// [asmjit::CodeHolder - Sections] +// ============================================================================ + +static Error CodeHolder_reserveInternal(CodeHolder* self, CodeBuffer* cb, size_t n) noexcept { + uint8_t* oldData = cb->_data; + uint8_t* newData; + + if (oldData && !cb->isExternal()) + newData = static_cast(Internal::reallocMemory(oldData, n)); + else + newData = static_cast(Internal::allocMemory(n)); + + if (ASMJIT_UNLIKELY(!newData)) + return DebugUtils::errored(kErrorNoHeapMemory); + + cb->_data = newData; + cb->_capacity = n; + + // Update the `Assembler` pointers if attached. Maybe we should introduce an + // event for this, but since only one Assembler can be attached at a time it + // should not matter how these pointers are updated. + Assembler* a = self->_cgAsm; + if (a && &a->_section->_buffer == cb) { + size_t offset = a->getOffset(); + + a->_bufferData = newData; + a->_bufferEnd = newData + n; + a->_bufferPtr = newData + offset; + } + + return kErrorOk; +} + +Error CodeHolder::growBuffer(CodeBuffer* cb, size_t n) noexcept { + // This is most likely called by `Assembler` so `sync()` shouldn't be needed, + // however, if this is called by the user and the currently attached Assembler + // did generate some code we could lose that, so sync now and make sure the + // section length is updated. + if (_cgAsm) _cgAsm->sync(); + + // Now the length of the section must be valid. + size_t length = cb->getLength(); + if (ASMJIT_UNLIKELY(n > IntTraits::maxValue() - length)) + return DebugUtils::errored(kErrorNoHeapMemory); + + // We can now check if growing the buffer is really necessary. It's unlikely + // that this function is called while there is still room for `n` bytes. + size_t capacity = cb->getCapacity(); + size_t required = cb->getLength() + n; + if (ASMJIT_UNLIKELY(required <= capacity)) return kErrorOk; + + if (cb->isFixedSize()) + return DebugUtils::errored(kErrorCodeTooLarge); + + if (capacity < 8096) + capacity = 8096; + else + capacity += Globals::kAllocOverhead; + + do { + size_t old = capacity; + if (capacity < Globals::kAllocThreshold) + capacity *= 2; + else + capacity += Globals::kAllocThreshold; + + if (capacity < Globals::kAllocThreshold) + capacity *= 2; + else + capacity += Globals::kAllocThreshold; + + // Overflow. + if (ASMJIT_UNLIKELY(old > capacity)) + return DebugUtils::errored(kErrorNoHeapMemory); + } while (capacity - Globals::kAllocOverhead < required); + + return CodeHolder_reserveInternal(this, cb, capacity - Globals::kAllocOverhead); +} + +Error CodeHolder::reserveBuffer(CodeBuffer* cb, size_t n) noexcept { + size_t capacity = cb->getCapacity(); + if (n <= capacity) return kErrorOk; + + if (cb->isFixedSize()) + return DebugUtils::errored(kErrorCodeTooLarge); + + // We must sync, as mentioned in `growBuffer()` as well. + if (_cgAsm) _cgAsm->sync(); + + return CodeHolder_reserveInternal(this, cb, n); +} + +// ============================================================================ +// [asmjit::CodeHolder - Labels & Symbols] +// ============================================================================ + +namespace { + +//! \internal +//! +//! Only used to lookup a label from `_namedLabels`. +class LabelByName { +public: + ASMJIT_INLINE LabelByName(const char* name, size_t nameLength, uint32_t hVal) noexcept + : name(name), + nameLength(static_cast(nameLength)) {} + + ASMJIT_INLINE bool matches(const LabelEntry* entry) const noexcept { + return static_cast(entry->getNameLength()) == nameLength && + ::memcmp(entry->getName(), name, nameLength) == 0; + } + + const char* name; + uint32_t nameLength; + uint32_t hVal; +}; + +// Returns a hash of `name` and fixes `nameLength` if it's `Globals::kInvalidIndex`. +static uint32_t CodeHolder_hashNameAndFixLen(const char* name, size_t& nameLength) noexcept { + uint32_t hVal = 0; + if (nameLength == Globals::kInvalidIndex) { + size_t i = 0; + for (;;) { + uint8_t c = static_cast(name[i]); + if (!c) break; + hVal = Utils::hashRound(hVal, c); + i++; + } + nameLength = i; + } + else { + for (size_t i = 0; i < nameLength; i++) { + uint8_t c = static_cast(name[i]); + if (ASMJIT_UNLIKELY(!c)) return DebugUtils::errored(kErrorInvalidLabelName); + hVal = Utils::hashRound(hVal, c); + } + } + return hVal; +} + +} // anonymous namespace + +LabelLink* CodeHolder::newLabelLink(LabelEntry* le, uint32_t sectionId, size_t offset, intptr_t rel) noexcept { + LabelLink* link = _baseHeap.allocT(); + if (ASMJIT_UNLIKELY(!link)) return nullptr; + + link->prev = le->_links; + le->_links = link; + + link->sectionId = sectionId; + link->relocId = RelocEntry::kInvalidId; + link->offset = offset; + link->rel = rel; + + _unresolvedLabelsCount++; + return link; +} + +Error CodeHolder::newLabelId(uint32_t& idOut) noexcept { + idOut = 0; + + size_t index = _labels.getLength(); + if (ASMJIT_LIKELY(index >= Operand::kPackedIdCount)) + return DebugUtils::errored(kErrorLabelIndexOverflow); + + ASMJIT_PROPAGATE(_labels.willGrow(&_baseHeap)); + LabelEntry* le = _baseHeap.allocZeroedT(); + + if (ASMJIT_UNLIKELY(!le)) + return DebugUtils::errored(kErrorNoHeapMemory);; + + uint32_t id = Operand::packId(static_cast(index)); + le->_setId(id); + le->_parentId = 0; + le->_sectionId = SectionEntry::kInvalidId; + le->_offset = 0; + + _labels.appendUnsafe(le); + idOut = id; + return kErrorOk; +} + +Error CodeHolder::newNamedLabelId(uint32_t& idOut, const char* name, size_t nameLength, uint32_t type, uint32_t parentId) noexcept { + idOut = 0; + uint32_t hVal = CodeHolder_hashNameAndFixLen(name, nameLength); + + if (ASMJIT_UNLIKELY(nameLength == 0)) + return DebugUtils::errored(kErrorInvalidLabelName); + + if (ASMJIT_UNLIKELY(nameLength > Globals::kMaxLabelLength)) + return DebugUtils::errored(kErrorLabelNameTooLong); + + switch (type) { + case Label::kTypeLocal: + if (ASMJIT_UNLIKELY(Operand::unpackId(parentId) >= _labels.getLength())) + return DebugUtils::errored(kErrorInvalidParentLabel); + + hVal ^= parentId; + break; + + case Label::kTypeGlobal: + if (ASMJIT_UNLIKELY(parentId != 0)) + return DebugUtils::errored(kErrorNonLocalLabelCantHaveParent); + + break; + + default: + return DebugUtils::errored(kErrorInvalidArgument); + } + + // Don't allow to insert duplicates. Local labels allow duplicates that have + // different id, this is already accomplished by having a different hashes + // between the same label names having different parent labels. + LabelEntry* le = _namedLabels.get(LabelByName(name, nameLength, hVal)); + if (ASMJIT_UNLIKELY(le)) + return DebugUtils::errored(kErrorLabelAlreadyDefined); + + Error err = kErrorOk; + size_t index = _labels.getLength(); + + if (ASMJIT_UNLIKELY(index >= Operand::kPackedIdCount)) + return DebugUtils::errored(kErrorLabelIndexOverflow); + + ASMJIT_PROPAGATE(_labels.willGrow(&_baseHeap)); + le = _baseHeap.allocZeroedT(); + + if (ASMJIT_UNLIKELY(!le)) + return DebugUtils::errored(kErrorNoHeapMemory); + + uint32_t id = Operand::packId(static_cast(index)); + le->_hVal = hVal; + le->_setId(id); + le->_type = static_cast(type); + le->_parentId = 0; + le->_sectionId = SectionEntry::kInvalidId; + le->_offset = 0; + + if (le->_name.mustEmbed(nameLength)) { + le->_name.setEmbedded(name, nameLength); + } + else { + char* nameExternal = static_cast(_dataZone.dup(name, nameLength, true)); + if (ASMJIT_UNLIKELY(!nameExternal)) + return DebugUtils::errored(kErrorNoHeapMemory); + le->_name.setExternal(nameExternal, nameLength); + } + + _labels.appendUnsafe(le); + _namedLabels.put(le); + + idOut = id; + return err; +} + +uint32_t CodeHolder::getLabelIdByName(const char* name, size_t nameLength, uint32_t parentId) noexcept { + uint32_t hVal = CodeHolder_hashNameAndFixLen(name, nameLength); + if (ASMJIT_UNLIKELY(!nameLength)) return 0; + + LabelEntry* le = _namedLabels.get(LabelByName(name, nameLength, hVal)); + return le ? le->getId() : static_cast(0); +} + +// ============================================================================ +// [asmjit::CodeEmitter - Relocations] +// ============================================================================ + +//! Encode MOD byte. +static ASMJIT_INLINE uint32_t x86EncodeMod(uint32_t m, uint32_t o, uint32_t rm) noexcept { + return (m << 6) | (o << 3) | rm; +} + +Error CodeHolder::newRelocEntry(RelocEntry** dst, uint32_t type, uint32_t size) noexcept { + ASMJIT_PROPAGATE(_relocations.willGrow(&_baseHeap)); + + size_t index = _relocations.getLength(); + if (ASMJIT_UNLIKELY(index > size_t(0xFFFFFFFFU))) + return DebugUtils::errored(kErrorRelocIndexOverflow); + + RelocEntry* re = _baseHeap.allocZeroedT(); + if (ASMJIT_UNLIKELY(!re)) + return DebugUtils::errored(kErrorNoHeapMemory); + + re->_id = static_cast(index); + re->_type = static_cast(type); + re->_size = static_cast(size); + re->_sourceSectionId = SectionEntry::kInvalidId; + re->_targetSectionId = SectionEntry::kInvalidId; + _relocations.appendUnsafe(re); + + *dst = re; + return kErrorOk; +} + +// TODO: Support multiple sections, this only relocates the first. +// TODO: This should go to Runtime as it's responsible for relocating the +// code, CodeHolder should just hold it. +size_t CodeHolder::relocate(void* _dst, uint64_t baseAddress) const noexcept { + SectionEntry* section = _sections[0]; + ASMJIT_ASSERT(section != nullptr); + + uint8_t* dst = static_cast(_dst); + if (baseAddress == Globals::kNoBaseAddress) + baseAddress = static_cast((uintptr_t)dst); + +#if !defined(ASMJIT_DISABLE_LOGGING) + Logger* logger = getLogger(); +#endif // ASMJIT_DISABLE_LOGGING + + size_t minCodeSize = section->getBuffer().getLength(); // Minimum code size. + size_t maxCodeSize = getCodeSize(); // Includes all possible trampolines. + + // We will copy the exact size of the generated code. Extra code for trampolines + // is generated on-the-fly by the relocator (this code doesn't exist at the moment). + ::memcpy(dst, section->_buffer._data, minCodeSize); + + // Trampoline offset from the beginning of dst/baseAddress. + size_t trampOffset = minCodeSize; + + // Relocate all recorded locations. + size_t numRelocs = _relocations.getLength(); + const RelocEntry* const* reArray = _relocations.getData(); + + for (size_t i = 0; i < numRelocs; i++) { + const RelocEntry* re = reArray[i]; + + // Possibly deleted or optimized out relocation entry. + if (re->getType() == RelocEntry::kTypeNone) + continue; + + uint64_t ptr = re->getData(); + size_t codeOffset = static_cast(re->getSourceOffset()); + + // Make sure that the `RelocEntry` is correct, we don't want to write + // out of bounds in `dst`. + if (ASMJIT_UNLIKELY(codeOffset + re->getSize() > maxCodeSize)) + return DebugUtils::errored(kErrorInvalidRelocEntry); + + // Whether to use trampoline, can be only used if relocation type is `kRelocTrampoline`. + bool useTrampoline = false; + + switch (re->getType()) { + case RelocEntry::kTypeAbsToAbs: { + break; + } + + case RelocEntry::kTypeRelToAbs: { + ptr += baseAddress; + break; + } + + case RelocEntry::kTypeAbsToRel: { + ptr -= baseAddress + re->getSourceOffset() + re->getSize(); + break; + } + + case RelocEntry::kTypeTrampoline: { + if (re->getSize() != 4) + return DebugUtils::errored(kErrorInvalidRelocEntry); + + ptr -= baseAddress + re->getSourceOffset() + re->getSize(); + if (!Utils::isInt32(static_cast(ptr))) { + ptr = (uint64_t)trampOffset - re->getSourceOffset() - re->getSize(); + useTrampoline = true; + } + break; + } + + default: + return DebugUtils::errored(kErrorInvalidRelocEntry); + } + + switch (re->getSize()) { + case 1: + Utils::writeU8(dst + codeOffset, static_cast(ptr & 0xFFU)); + break; + + case 4: + Utils::writeU32u(dst + codeOffset, static_cast(ptr & 0xFFFFFFFFU)); + break; + + case 8: + Utils::writeU64u(dst + codeOffset, ptr); + break; + + default: + return DebugUtils::errored(kErrorInvalidRelocEntry); + } + + // Handle the trampoline case. + if (useTrampoline) { + // Bytes that replace [REX, OPCODE] bytes. + uint32_t byte0 = 0xFF; + uint32_t byte1 = dst[codeOffset - 1]; + + if (byte1 == 0xE8) { + // Patch CALL/MOD byte to FF/2 (-> 0x15). + byte1 = x86EncodeMod(0, 2, 5); + } + else if (byte1 == 0xE9) { + // Patch JMP/MOD byte to FF/4 (-> 0x25). + byte1 = x86EncodeMod(0, 4, 5); + } + else { + return DebugUtils::errored(kErrorInvalidRelocEntry); + } + + // Patch `jmp/call` instruction. + ASMJIT_ASSERT(codeOffset >= 2); + dst[codeOffset - 2] = static_cast(byte0); + dst[codeOffset - 1] = static_cast(byte1); + + // Store absolute address and advance the trampoline pointer. + Utils::writeU64u(dst + trampOffset, re->getData()); + trampOffset += 8; + +#if !defined(ASMJIT_DISABLE_LOGGING) + if (logger) + logger->logf("[reloc] dq 0x%016llX ; Trampoline\n", re->getData()); +#endif // !ASMJIT_DISABLE_LOGGING + } + } + + // If there are no trampolines this is the same as `minCodeSize`. + return trampOffset; +} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" diff --git a/libraries/asmjit/asmjit/base/codeholder.h b/libraries/asmjit/asmjit/base/codeholder.h new file mode 100644 index 00000000000..f753ecc3786 --- /dev/null +++ b/libraries/asmjit/asmjit/base/codeholder.h @@ -0,0 +1,748 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Guard] +#ifndef _ASMJIT_BASE_CODEHOLDER_H +#define _ASMJIT_BASE_CODEHOLDER_H + +// [Dependencies] +#include "../base/arch.h" +#include "../base/func.h" +#include "../base/logging.h" +#include "../base/operand.h" +#include "../base/simdtypes.h" +#include "../base/utils.h" +#include "../base/zone.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +//! \addtogroup asmjit_base +//! \{ + +// ============================================================================ +// [Forward Declarations] +// ============================================================================ + +class Assembler; +class CodeEmitter; +class CodeHolder; + +// ============================================================================ +// [asmjit::AlignMode] +// ============================================================================ + +//! Align mode. +ASMJIT_ENUM(AlignMode) { + kAlignCode = 0, //!< Align executable code. + kAlignData = 1, //!< Align non-executable code. + kAlignZero = 2, //!< Align by a sequence of zeros. + kAlignCount //!< Count of alignment modes. +}; + +// ============================================================================ +// [asmjit::ErrorHandler] +// ============================================================================ + +//! Error handler can be used to override the default behavior of error handling +//! available to all classes that inherit \ref CodeEmitter. See \ref handleError(). +class ASMJIT_VIRTAPI ErrorHandler { +public: + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a new `ErrorHandler` instance. + ASMJIT_API ErrorHandler() noexcept; + //! Destroy the `ErrorHandler` instance. + ASMJIT_API virtual ~ErrorHandler() noexcept; + + // -------------------------------------------------------------------------- + // [Handle Error] + // -------------------------------------------------------------------------- + + //! Error handler (abstract). + //! + //! Error handler is called after an error happened and before it's propagated + //! to the caller. There are multiple ways how the error handler can be used: + //! + //! 1. Returning `true` or `false` from `handleError()`. If `true` is returned + //! it means that the error was reported and AsmJit can continue execution. + //! The reported error still be propagated to the caller, but won't put the + //! CodeEmitter into an error state (it won't set last-error). However, + //! returning `false` means that the error cannot be handled - in such case + //! it stores the error, which can be then retrieved by using `getLastError()`. + //! Returning `false` is the default behavior when no error handler is present. + //! To put the assembler into a non-error state again a `resetLastError()` must + //! be called. + //! + //! 2. Throwing an exception. AsmJit doesn't use exceptions and is completely + //! exception-safe, but you can throw exception from your error handler if + //! this way is the preferred way of handling errors in your project. Throwing + //! an exception acts virtually as returning `true` as AsmJit won't be able + //! to store the error because the exception changes execution path. + //! + //! 3. Using plain old C's `setjmp()` and `longjmp()`. Asmjit always puts + //! `CodeEmitter` to a consistent state before calling the `handleError()` + //! so `longjmp()` can be used without any issues to cancel the code + //! generation if an error occurred. There is no difference between + //! exceptions and longjmp() from AsmJit's perspective. + virtual bool handleError(Error err, const char* message, CodeEmitter* origin) = 0; +}; + +// ============================================================================ +// [asmjit::CodeInfo] +// ============================================================================ + +//! Basic information about a code (or target). It describes its architecture, +//! code generation mode (or optimization level), and base address. +class CodeInfo { +public: + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE CodeInfo() noexcept + : _archInfo(), + _stackAlignment(0), + _cdeclCallConv(CallConv::kIdNone), + _stdCallConv(CallConv::kIdNone), + _fastCallConv(CallConv::kIdNone), + _baseAddress(Globals::kNoBaseAddress) {} + ASMJIT_INLINE CodeInfo(const CodeInfo& other) noexcept { init(other); } + + explicit ASMJIT_INLINE CodeInfo(uint32_t archType, uint32_t archMode = 0, uint64_t baseAddress = Globals::kNoBaseAddress) noexcept + : _archInfo(archType, archMode), + _packedMiscInfo(0), + _baseAddress(baseAddress) {} + + // -------------------------------------------------------------------------- + // [Init / Reset] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE bool isInitialized() const noexcept { + return _archInfo._type != ArchInfo::kTypeNone; + } + + ASMJIT_INLINE void init(const CodeInfo& other) noexcept { + _archInfo = other._archInfo; + _packedMiscInfo = other._packedMiscInfo; + _baseAddress = other._baseAddress; + } + + ASMJIT_INLINE void init(uint32_t archType, uint32_t archMode = 0, uint64_t baseAddress = Globals::kNoBaseAddress) noexcept { + _archInfo.init(archType, archMode); + _packedMiscInfo = 0; + _baseAddress = baseAddress; + } + + ASMJIT_INLINE void reset() noexcept { + _archInfo.reset(); + _stackAlignment = 0; + _cdeclCallConv = CallConv::kIdNone; + _stdCallConv = CallConv::kIdNone; + _fastCallConv = CallConv::kIdNone; + _baseAddress = Globals::kNoBaseAddress; + } + + // -------------------------------------------------------------------------- + // [Architecture Information] + // -------------------------------------------------------------------------- + + //! Get architecture information, see \ref ArchInfo. + ASMJIT_INLINE const ArchInfo& getArchInfo() const noexcept { return _archInfo; } + + //! Get architecture type, see \ref ArchInfo::Type. + ASMJIT_INLINE uint32_t getArchType() const noexcept { return _archInfo.getType(); } + //! Get architecture sub-type, see \ref ArchInfo::SubType. + ASMJIT_INLINE uint32_t getArchSubType() const noexcept { return _archInfo.getSubType(); } + //! Get a size of a GP register of the architecture the code is using. + ASMJIT_INLINE uint32_t getGpSize() const noexcept { return _archInfo.getGpSize(); } + //! Get number of GP registers available of the architecture the code is using. + ASMJIT_INLINE uint32_t getGpCount() const noexcept { return _archInfo.getGpCount(); } + + // -------------------------------------------------------------------------- + // [High-Level Information] + // -------------------------------------------------------------------------- + + //! Get a natural stack alignment that must be honored (or 0 if not known). + ASMJIT_INLINE uint32_t getStackAlignment() const noexcept { return _stackAlignment; } + //! Set a natural stack alignment that must be honored. + ASMJIT_INLINE void setStackAlignment(uint8_t sa) noexcept { _stackAlignment = static_cast(sa); } + + ASMJIT_INLINE uint32_t getCdeclCallConv() const noexcept { return _cdeclCallConv; } + ASMJIT_INLINE void setCdeclCallConv(uint32_t cc) noexcept { _cdeclCallConv = static_cast(cc); } + + ASMJIT_INLINE uint32_t getStdCallConv() const noexcept { return _stdCallConv; } + ASMJIT_INLINE void setStdCallConv(uint32_t cc) noexcept { _stdCallConv = static_cast(cc); } + + ASMJIT_INLINE uint32_t getFastCallConv() const noexcept { return _fastCallConv; } + ASMJIT_INLINE void setFastCallConv(uint32_t cc) noexcept { _fastCallConv = static_cast(cc); } + + // -------------------------------------------------------------------------- + // [Addressing Information] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE bool hasBaseAddress() const noexcept { return _baseAddress != Globals::kNoBaseAddress; } + ASMJIT_INLINE uint64_t getBaseAddress() const noexcept { return _baseAddress; } + ASMJIT_INLINE void setBaseAddress(uint64_t p) noexcept { _baseAddress = p; } + ASMJIT_INLINE void resetBaseAddress() noexcept { _baseAddress = Globals::kNoBaseAddress; } + + // -------------------------------------------------------------------------- + // [Operator Overload] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE CodeInfo& operator=(const CodeInfo& other) noexcept { init(other); return *this; } + ASMJIT_INLINE bool operator==(const CodeInfo& other) const noexcept { return ::memcmp(this, &other, sizeof(*this)) == 0; } + ASMJIT_INLINE bool operator!=(const CodeInfo& other) const noexcept { return ::memcmp(this, &other, sizeof(*this)) != 0; } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + ArchInfo _archInfo; //!< Architecture information. + + union { + struct { + uint8_t _stackAlignment; //!< Natural stack alignment (ARCH+OS). + uint8_t _cdeclCallConv; //!< Default CDECL calling convention. + uint8_t _stdCallConv; //!< Default STDCALL calling convention. + uint8_t _fastCallConv; //!< Default FASTCALL calling convention. + }; + uint32_t _packedMiscInfo; //!< \internal + }; + + uint64_t _baseAddress; //!< Base address. +}; + +// ============================================================================ +// [asmjit::CodeBuffer] +// ============================================================================ + +//! Code or data buffer. +struct CodeBuffer { + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE bool hasData() const noexcept { return _data != nullptr; } + ASMJIT_INLINE uint8_t* getData() noexcept { return _data; } + ASMJIT_INLINE const uint8_t* getData() const noexcept { return _data; } + + ASMJIT_INLINE size_t getLength() const noexcept { return _length; } + ASMJIT_INLINE size_t getCapacity() const noexcept { return _capacity; } + + ASMJIT_INLINE bool isExternal() const noexcept { return _isExternal; } + ASMJIT_INLINE bool isFixedSize() const noexcept { return _isFixedSize; } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + uint8_t* _data; //!< The content of the buffer (data). + size_t _length; //!< Number of bytes of `data` used. + size_t _capacity; //!< Buffer capacity (in bytes). + bool _isExternal; //!< True if this is external buffer. + bool _isFixedSize; //!< True if this buffer cannot grow. +}; + +// ============================================================================ +// [asmjit::SectionEntry] +// ============================================================================ + +//! Section entry. +class SectionEntry { +public: + ASMJIT_ENUM(Id) { + kInvalidId = 0xFFFFFFFFU //!< Invalid section id. + }; + + //! Section flags. + ASMJIT_ENUM(Flags) { + kFlagExec = 0x00000001U, //!< Executable (.text sections). + kFlagConst = 0x00000002U, //!< Read-only (.text and .data sections). + kFlagZero = 0x00000004U, //!< Zero initialized by the loader (BSS). + kFlagInfo = 0x00000008U, //!< Info / comment flag. + kFlagImplicit = 0x80000000U //!< Section created implicitly (can be deleted by the Runtime). + }; + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE uint32_t getId() const noexcept { return _id; } + ASMJIT_INLINE const char* getName() const noexcept { return _name; } + + ASMJIT_INLINE void _setDefaultName( + char c0 = 0, char c1 = 0, char c2 = 0, char c3 = 0, + char c4 = 0, char c5 = 0, char c6 = 0, char c7 = 0) noexcept { + _nameAsU32[0] = Utils::pack32_4x8(c0, c1, c2, c3); + _nameAsU32[1] = Utils::pack32_4x8(c4, c5, c6, c7); + } + + ASMJIT_INLINE uint32_t getFlags() const noexcept { return _flags; } + ASMJIT_INLINE bool hasFlag(uint32_t flag) const noexcept { return (_flags & flag) != 0; } + ASMJIT_INLINE void addFlags(uint32_t flags) noexcept { _flags |= flags; } + ASMJIT_INLINE void clearFlags(uint32_t flags) noexcept { _flags &= ~flags; } + + ASMJIT_INLINE uint32_t getAlignment() const noexcept { return _alignment; } + ASMJIT_INLINE void setAlignment(uint32_t alignment) noexcept { _alignment = alignment; } + + ASMJIT_INLINE size_t getPhysicalSize() const noexcept { return _buffer.getLength(); } + + ASMJIT_INLINE size_t getVirtualSize() const noexcept { return _virtualSize; } + ASMJIT_INLINE void setVirtualSize(uint32_t size) noexcept { _virtualSize = size; } + + ASMJIT_INLINE CodeBuffer& getBuffer() noexcept { return _buffer; } + ASMJIT_INLINE const CodeBuffer& getBuffer() const noexcept { return _buffer; } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + uint32_t _id; //!< Section id. + uint32_t _flags; //!< Section flags. + uint32_t _alignment; //!< Section alignment requirements (0 if no requirements). + uint32_t _virtualSize; //!< Virtual size of the section (zero initialized mostly). + union { + char _name[36]; //!< Section name (max 35 characters, PE allows max 8). + uint32_t _nameAsU32[36 / 4]; //!< Section name as `uint32_t[]` (only optimization). + }; + CodeBuffer _buffer; //!< Code or data buffer. +}; + +// ============================================================================ +// [asmjit::LabelLink] +// ============================================================================ + +//! Data structure used to link labels. +struct LabelLink { + LabelLink* prev; //!< Previous link (single-linked list). + uint32_t sectionId; //!< Section id. + uint32_t relocId; //!< Relocation id or RelocEntry::kInvalidId. + size_t offset; //!< Label offset relative to the start of the section. + intptr_t rel; //!< Inlined rel8/rel32. +}; + +// ============================================================================ +// [asmjit::LabelEntry] +// ============================================================================ + +//! Label entry. +//! +//! Contains the following properties: +//! * Label id - This is the only thing that is set to the `Label` operand. +//! * Label name - Optional, used mostly to create executables and libraries. +//! * Label type - Type of the label, default `Label::kTypeAnonymous`. +//! * Label parent id - Derived from many assemblers that allow to define a +//! local label that falls under a global label. This allows to define +//! many labels of the same name that have different parent (global) label. +//! * Offset - offset of the label bound by `Assembler`. +//! * Links - single-linked list that contains locations of code that has +//! to be patched when the label gets bound. Every use of unbound label +//! adds one link to `_links` list. +//! * HVal - Hash value of label's name and optionally parentId. +//! * HashNext - Hash-table implementation detail. +class LabelEntry : public ZoneHashNode { +public: + // NOTE: Label id is stored in `_customData`, which is provided by ZoneHashNode + // to fill a padding that a C++ compiler targeting 64-bit CPU will add to align + // the structure to 64-bits. + + //! Get label id. + ASMJIT_INLINE uint32_t getId() const noexcept { return _customData; } + //! Set label id (internal, used only by \ref CodeHolder). + ASMJIT_INLINE void _setId(uint32_t id) noexcept { _customData = id; } + + //! Get label type, see \ref Label::Type. + ASMJIT_INLINE uint32_t getType() const noexcept { return _type; } + //! Get label flags, returns 0 at the moment. + ASMJIT_INLINE uint32_t getFlags() const noexcept { return _flags; } + + ASMJIT_INLINE bool hasParent() const noexcept { return _parentId != 0; } + //! Get label's parent id. + ASMJIT_INLINE uint32_t getParentId() const noexcept { return _parentId; } + + //! Get label's section id where it's bound to (or `SectionEntry::kInvalidId` if it's not bound yet). + ASMJIT_INLINE uint32_t getSectionId() const noexcept { return _sectionId; } + + //! Get if the label has name. + ASMJIT_INLINE bool hasName() const noexcept { return !_name.isEmpty(); } + + //! Get the label's name. + //! + //! NOTE: Local labels will return their local name without their parent + //! part, for example ".L1". + ASMJIT_INLINE const char* getName() const noexcept { return _name.getData(); } + + //! Get length of label's name. + //! + //! NOTE: Label name is always null terminated, so you can use `strlen()` to + //! get it, however, it's also cached in `LabelEntry`, so if you want to know + //! the length the easiest way is to use `LabelEntry::getNameLength()`. + ASMJIT_INLINE size_t getNameLength() const noexcept { return _name.getLength(); } + + //! Get if the label is bound. + ASMJIT_INLINE bool isBound() const noexcept { return _sectionId != SectionEntry::kInvalidId; } + //! Get the label offset (only useful if the label is bound). + ASMJIT_INLINE intptr_t getOffset() const noexcept { return _offset; } + + //! Get the hash-value of label's name and its parent label (if any). + //! + //! Label hash is calculated as `HASH(Name) ^ ParentId`. The hash function + //! is implemented in `Utils::hashString()` and `Utils::hashRound()`. + ASMJIT_INLINE uint32_t getHVal() const noexcept { return _hVal; } + + // ------------------------------------------------------------------------ + // [Members] + // ------------------------------------------------------------------------ + + // Let's round the size of `LabelEntry` to 64 bytes (as ZoneHeap has 32 + // bytes granularity anyway). This gives `_name` the remaining space, which + // is roughly 16 bytes on 64-bit and 28 bytes on 32-bit architectures. + enum { kNameBytes = 64 - (sizeof(ZoneHashNode) + 16 + sizeof(intptr_t) + sizeof(LabelLink*)) }; + + uint8_t _type; //!< Label type, see Label::Type. + uint8_t _flags; //!< Must be zero. + uint16_t _reserved16; //!< Reserved. + uint32_t _parentId; //!< Label parent id or zero. + uint32_t _sectionId; //!< Section id or `SectionEntry::kInvalidId`. + uint32_t _reserved32; //!< Reserved. + intptr_t _offset; //!< Label offset. + LabelLink* _links; //!< Label links. + SmallString _name; //!< Label name. +}; + +// ============================================================================ +// [asmjit::RelocEntry] +// ============================================================================ + +//! Relocation entry. +struct RelocEntry { + ASMJIT_ENUM(Id) { + kInvalidId = 0xFFFFFFFFU //!< Invalid relocation id. + }; + + //! Relocation type. + ASMJIT_ENUM(Type) { + kTypeNone = 0, //!< Deleted entry (no relocation). + kTypeAbsToAbs = 1, //!< Relocate absolute to absolute. + kTypeRelToAbs = 2, //!< Relocate relative to absolute. + kTypeAbsToRel = 3, //!< Relocate absolute to relative. + kTypeTrampoline = 4 //!< Relocate absolute to relative or use trampoline. + }; + + // ------------------------------------------------------------------------ + // [Accessors] + // ------------------------------------------------------------------------ + + ASMJIT_INLINE uint32_t getId() const noexcept { return _id; } + + ASMJIT_INLINE uint32_t getType() const noexcept { return _type; } + ASMJIT_INLINE uint32_t getSize() const noexcept { return _size; } + + ASMJIT_INLINE uint32_t getSourceSectionId() const noexcept { return _sourceSectionId; } + ASMJIT_INLINE uint32_t getTargetSectionId() const noexcept { return _targetSectionId; } + + ASMJIT_INLINE uint64_t getSourceOffset() const noexcept { return _sourceOffset; } + ASMJIT_INLINE uint64_t getData() const noexcept { return _data; } + + // ------------------------------------------------------------------------ + // [Members] + // ------------------------------------------------------------------------ + + uint32_t _id; //!< Relocation id. + uint8_t _type; //!< Type of the relocation. + uint8_t _size; //!< Size of the relocation (1, 2, 4 or 8 bytes). + uint8_t _reserved[2]; //!< Reserved. + uint32_t _sourceSectionId; //!< Source section id. + uint32_t _targetSectionId; //!< Destination section id. + uint64_t _sourceOffset; //!< Source offset (relative to start of the section). + uint64_t _data; //!< Relocation data (target offset, target address, etc). +}; + +// ============================================================================ +// [asmjit::CodeHolder] +// ============================================================================ + +//! Contains basic information about the target architecture plus its settings, +//! and holds code & data (including sections, labels, and relocation information). +//! CodeHolder can store both binary and intermediate representation of assembly, +//! which can be generated by \ref Assembler and/or \ref CodeBuilder. +//! +//! NOTE: CodeHolder has ability to attach an \ref ErrorHandler, however, this +//! error handler is not triggered by CodeHolder itself, it's only used by the +//! attached code generators. +class CodeHolder { +public: + ASMJIT_NONCOPYABLE(CodeHolder) + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create an uninitialized CodeHolder (you must init() it before it can be used). + ASMJIT_API CodeHolder() noexcept; + //! Destroy the CodeHolder. + ASMJIT_API ~CodeHolder() noexcept; + + // -------------------------------------------------------------------------- + // [Init / Reset] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE bool isInitialized() const noexcept { return _codeInfo.isInitialized(); } + + //! Initialize to CodeHolder to hold code described by `codeInfo`. + ASMJIT_API Error init(const CodeInfo& info) noexcept; + //! Detach all code-generators attached and reset the \ref CodeHolder. + ASMJIT_API void reset(bool releaseMemory = false) noexcept; + + // -------------------------------------------------------------------------- + // [Attach / Detach] + // -------------------------------------------------------------------------- + + //! Attach a \ref CodeEmitter to this \ref CodeHolder. + ASMJIT_API Error attach(CodeEmitter* emitter) noexcept; + //! Detach a \ref CodeEmitter from this \ref CodeHolder. + ASMJIT_API Error detach(CodeEmitter* emitter) noexcept; + + // -------------------------------------------------------------------------- + // [Sync] + // -------------------------------------------------------------------------- + + //! Synchronize all states of all `CodeEmitter`s associated with the CodeHolder. + //! This is required as some code generators don't sync every time they do + //! something - for example \ref Assembler generally syncs when it needs to + //! reallocate the \ref CodeBuffer, but not each time it encodes instruction + //! or directive. + ASMJIT_API void sync() noexcept; + + // -------------------------------------------------------------------------- + // [Code-Information] + // -------------------------------------------------------------------------- + + //! Get code/target information, see \ref CodeInfo. + ASMJIT_INLINE const CodeInfo& getCodeInfo() const noexcept { return _codeInfo; } + //! Get architecture information, see \ref ArchInfo. + ASMJIT_INLINE const ArchInfo& getArchInfo() const noexcept { return _codeInfo.getArchInfo(); } + + //! Get the target's architecture type. + ASMJIT_INLINE uint32_t getArchType() const noexcept { return getArchInfo().getType(); } + //! Get the target's architecture sub-type. + ASMJIT_INLINE uint32_t getArchSubType() const noexcept { return getArchInfo().getSubType(); } + + //! Get if a static base-address is set. + ASMJIT_INLINE bool hasBaseAddress() const noexcept { return _codeInfo.hasBaseAddress(); } + //! Get a static base-address (uint64_t). + ASMJIT_INLINE uint64_t getBaseAddress() const noexcept { return _codeInfo.getBaseAddress(); } + + // -------------------------------------------------------------------------- + // [Global Information] + // -------------------------------------------------------------------------- + + //! Get global hints, internally propagated to all `CodeEmitter`s attached. + ASMJIT_INLINE uint32_t getGlobalHints() const noexcept { return _globalHints; } + //! Get global options, internally propagated to all `CodeEmitter`s attached. + ASMJIT_INLINE uint32_t getGlobalOptions() const noexcept { return _globalOptions; } + + // -------------------------------------------------------------------------- + // [Result Information] + // -------------------------------------------------------------------------- + + //! Get the size code & data of all sections. + ASMJIT_API size_t getCodeSize() const noexcept; + + //! Get size of all possible trampolines. + //! + //! Trampolines are needed to successfully generate relative jumps to absolute + //! addresses. This value is only non-zero if jmp of call instructions were + //! used with immediate operand (this means jumping or calling an absolute + //! address directly). + ASMJIT_INLINE size_t getTrampolinesSize() const noexcept { return _trampolinesSize; } + + // -------------------------------------------------------------------------- + // [Logging & Error Handling] + // -------------------------------------------------------------------------- + +#if !defined(ASMJIT_DISABLE_LOGGING) + //! Get if a logger attached. + ASMJIT_INLINE bool hasLogger() const noexcept { return _logger != nullptr; } + //! Get the attached logger. + ASMJIT_INLINE Logger* getLogger() const noexcept { return _logger; } + //! Attach a `logger` to CodeHolder and propagate it to all attached `CodeEmitter`s. + ASMJIT_API void setLogger(Logger* logger) noexcept; + //! Reset the logger (does nothing if not attached). + ASMJIT_INLINE void resetLogger() noexcept { setLogger(nullptr); } +#endif // !ASMJIT_DISABLE_LOGGING + + //! Get if error-handler is attached. + ASMJIT_INLINE bool hasErrorHandler() const noexcept { return _errorHandler != nullptr; } + //! Get the error-handler. + ASMJIT_INLINE ErrorHandler* getErrorHandler() const noexcept { return _errorHandler; } + //! Set the error handler, will affect all attached `CodeEmitter`s. + ASMJIT_API Error setErrorHandler(ErrorHandler* handler) noexcept; + //! Reset the error handler (does nothing if not attached). + ASMJIT_INLINE void resetErrorHandler() noexcept { setErrorHandler(nullptr); } + + // -------------------------------------------------------------------------- + // [Sections] + // -------------------------------------------------------------------------- + + //! Get array of `SectionEntry*` records. + ASMJIT_INLINE const ZoneVector& getSections() const noexcept { return _sections; } + + //! Get a section entry of the given index. + ASMJIT_INLINE SectionEntry* getSectionEntry(size_t index) const noexcept { return _sections[index]; } + + ASMJIT_API Error growBuffer(CodeBuffer* cb, size_t n) noexcept; + ASMJIT_API Error reserveBuffer(CodeBuffer* cb, size_t n) noexcept; + + // -------------------------------------------------------------------------- + // [Labels & Symbols] + // -------------------------------------------------------------------------- + + //! Create a new anonymous label and return its id in `idOut`. + //! + //! Returns `Error`, does not report error to \ref ErrorHandler. + ASMJIT_API Error newLabelId(uint32_t& idOut) noexcept; + + //! Create a new named label label-type `type`. + //! + //! Returns `Error`, does not report error to \ref ErrorHandler. + ASMJIT_API Error newNamedLabelId(uint32_t& idOut, const char* name, size_t nameLength, uint32_t type, uint32_t parentId) noexcept; + + //! Get a label id by name. + ASMJIT_API uint32_t getLabelIdByName(const char* name, size_t nameLength = Globals::kInvalidIndex, uint32_t parentId = 0) noexcept; + + //! Create a new label-link used to store information about yet unbound labels. + //! + //! Returns `null` if the allocation failed. + ASMJIT_API LabelLink* newLabelLink(LabelEntry* le, uint32_t sectionId, size_t offset, intptr_t rel) noexcept; + + //! Get array of `LabelEntry*` records. + ASMJIT_INLINE const ZoneVector& getLabelEntries() const noexcept { return _labels; } + + //! Get number of labels created. + ASMJIT_INLINE size_t getLabelsCount() const noexcept { return _labels.getLength(); } + + //! Get number of label references, which are unresolved at the moment. + ASMJIT_INLINE size_t getUnresolvedLabelsCount() const noexcept { return _unresolvedLabelsCount; } + + //! Get if the `label` is valid (i.e. created by `newLabelId()`). + ASMJIT_INLINE bool isLabelValid(const Label& label) const noexcept { + return isLabelValid(label.getId()); + } + //! Get if the label having `id` is valid (i.e. created by `newLabelId()`). + ASMJIT_INLINE bool isLabelValid(uint32_t labelId) const noexcept { + size_t index = Operand::unpackId(labelId); + return index < _labels.getLength(); + } + + //! Get if the `label` is already bound. + //! + //! Returns `false` if the `label` is not valid. + ASMJIT_INLINE bool isLabelBound(const Label& label) const noexcept { + return isLabelBound(label.getId()); + } + //! \overload + ASMJIT_INLINE bool isLabelBound(uint32_t id) const noexcept { + size_t index = Operand::unpackId(id); + return index < _labels.getLength() && _labels[index]->isBound(); + } + + //! Get a `label` offset or -1 if the label is not yet bound. + ASMJIT_INLINE intptr_t getLabelOffset(const Label& label) const noexcept { + return getLabelOffset(label.getId()); + } + //! \overload + ASMJIT_INLINE intptr_t getLabelOffset(uint32_t id) const noexcept { + ASMJIT_ASSERT(isLabelValid(id)); + return _labels[Operand::unpackId(id)]->getOffset(); + } + + //! Get information about the given `label`. + ASMJIT_INLINE LabelEntry* getLabelEntry(const Label& label) const noexcept { + return getLabelEntry(label.getId()); + } + //! Get information about a label having the given `id`. + ASMJIT_INLINE LabelEntry* getLabelEntry(uint32_t id) const noexcept { + size_t index = static_cast(Operand::unpackId(id)); + return index < _labels.getLength() ? _labels[index] : static_cast(nullptr); + } + + // -------------------------------------------------------------------------- + // [Relocations] + // -------------------------------------------------------------------------- + + //! Create a new relocation entry of type `type` and size `size`. + //! + //! Additional fields can be set after the relocation entry was created. + ASMJIT_API Error newRelocEntry(RelocEntry** dst, uint32_t type, uint32_t size) noexcept; + + //! Get if the code contains relocations. + ASMJIT_INLINE bool hasRelocations() const noexcept { return !_relocations.isEmpty(); } + //! Get array of `RelocEntry*` records. + ASMJIT_INLINE const ZoneVector& getRelocEntries() const noexcept { return _relocations; } + + ASMJIT_INLINE RelocEntry* getRelocEntry(uint32_t id) const noexcept { return _relocations[id]; } + + //! Relocate the code to `baseAddress` and copy it to `dst`. + //! + //! \param dst Contains the location where the relocated code should be + //! copied. The pointer can be address returned by virtual memory allocator + //! or any other address that has sufficient space. + //! + //! \param baseAddress Base address used for relocation. `JitRuntime` always + //! sets the `baseAddress` to be the same as `dst`. + //! + //! \return The number bytes actually used. If the code emitter reserved + //! space for possible trampolines, but didn't use it, the number of bytes + //! used can actually be less than the expected worst case. Virtual memory + //! allocator can shrink the memory it allocated initially. + //! + //! A given buffer will be overwritten, to get the number of bytes required, + //! use `getCodeSize()`. + ASMJIT_API size_t relocate(void* dst, uint64_t baseAddress = Globals::kNoBaseAddress) const noexcept; + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + CodeInfo _codeInfo; //!< Basic information about the code (architecture and other info). + + uint32_t _globalHints; //!< Global hints, propagated to all `CodeEmitter`s. + uint32_t _globalOptions; //!< Global options, propagated to all `CodeEmitter`s. + + CodeEmitter* _emitters; //!< Linked-list of all attached `CodeEmitter`s. + Assembler* _cgAsm; //!< Attached \ref Assembler (only one at a time). + + Logger* _logger; //!< Attached \ref Logger, used by all consumers. + ErrorHandler* _errorHandler; //!< Attached \ref ErrorHandler. + + uint32_t _unresolvedLabelsCount; //!< Count of label references which were not resolved. + uint32_t _trampolinesSize; //!< Size of all possible trampolines. + + Zone _baseZone; //!< Base zone (used to allocate core structures). + Zone _dataZone; //!< Data zone (used to allocate extra data like label names). + ZoneHeap _baseHeap; //!< Zone allocator, used to manage internal containers. + + ZoneVector _sections; //!< Section entries. + ZoneVector _labels; //!< Label entries (each label is stored here). + ZoneVector _relocations; //!< Relocation entries. + ZoneHash _namedLabels; //!< Label name -> LabelEntry (only named labels). +}; + +//! \} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" + +// [Guard] +#endif // _ASMJIT_BASE_CODEHOLDER_H diff --git a/libraries/asmjit/asmjit/base/constpool.cpp b/libraries/asmjit/asmjit/base/constpool.cpp new file mode 100644 index 00000000000..799abd1c2a9 --- /dev/null +++ b/libraries/asmjit/asmjit/base/constpool.cpp @@ -0,0 +1,511 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Export] +#define ASMJIT_EXPORTS + +// [Dependencies] +#include "../base/constpool.h" +#include "../base/utils.h" + +#include + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +// Binary tree code is based on Julienne Walker's "Andersson Binary Trees" +// article and implementation. However, only three operations are implemented - +// get, insert and traverse. + +// ============================================================================ +// [asmjit::ConstPool::Tree - Ops] +// ============================================================================ + +//! \internal +//! +//! Remove left horizontal links. +static ASMJIT_INLINE ConstPool::Node* ConstPoolTree_skewNode(ConstPool::Node* node) noexcept { + ConstPool::Node* link = node->_link[0]; + uint32_t level = node->_level; + + if (level != 0 && link && link->_level == level) { + node->_link[0] = link->_link[1]; + link->_link[1] = node; + + node = link; + } + + return node; +} + +//! \internal +//! +//! Remove consecutive horizontal links. +static ASMJIT_INLINE ConstPool::Node* ConstPoolTree_splitNode(ConstPool::Node* node) noexcept { + ConstPool::Node* link = node->_link[1]; + uint32_t level = node->_level; + + if (level != 0 && link && link->_link[1] && link->_link[1]->_level == level) { + node->_link[1] = link->_link[0]; + link->_link[0] = node; + + node = link; + node->_level++; + } + + return node; +} + +ConstPool::Node* ConstPool::Tree::get(const void* data) noexcept { + ConstPool::Node* node = _root; + size_t dataSize = _dataSize; + + while (node) { + int c = ::memcmp(node->getData(), data, dataSize); + if (c == 0) + return node; + node = node->_link[c < 0]; + } + + return nullptr; +} + +void ConstPool::Tree::put(ConstPool::Node* newNode) noexcept { + size_t dataSize = _dataSize; + _length++; + + if (!_root) { + _root = newNode; + return; + } + + ConstPool::Node* node = _root; + ConstPool::Node* stack[kHeightLimit]; + + unsigned int top = 0; + unsigned int dir; + + // Find a spot and save the stack. + for (;;) { + stack[top++] = node; + dir = ::memcmp(node->getData(), newNode->getData(), dataSize) < 0; + + ConstPool::Node* link = node->_link[dir]; + if (!link) break; + + node = link; + } + + // Link and rebalance. + node->_link[dir] = newNode; + + while (top > 0) { + // Which child? + node = stack[--top]; + + if (top != 0) { + dir = stack[top - 1]->_link[1] == node; + } + + node = ConstPoolTree_skewNode(node); + node = ConstPoolTree_splitNode(node); + + // Fix the parent. + if (top != 0) + stack[top - 1]->_link[dir] = node; + else + _root = node; + } +} + +// ============================================================================ +// [asmjit::ConstPool - Construction / Destruction] +// ============================================================================ + +ConstPool::ConstPool(Zone* zone) noexcept { reset(zone); } +ConstPool::~ConstPool() noexcept {} + +// ============================================================================ +// [asmjit::ConstPool - Reset] +// ============================================================================ + +void ConstPool::reset(Zone* zone) noexcept { + _zone = zone; + + size_t dataSize = 1; + for (size_t i = 0; i < ASMJIT_ARRAY_SIZE(_tree); i++) { + _tree[i].reset(); + _tree[i].setDataSize(dataSize); + _gaps[i] = nullptr; + dataSize <<= 1; + } + + _gapPool = nullptr; + _size = 0; + _alignment = 0; +} + +// ============================================================================ +// [asmjit::ConstPool - Ops] +// ============================================================================ + +static ASMJIT_INLINE ConstPool::Gap* ConstPool_allocGap(ConstPool* self) noexcept { + ConstPool::Gap* gap = self->_gapPool; + if (!gap) return self->_zone->allocT(); + + self->_gapPool = gap->_next; + return gap; +} + +static ASMJIT_INLINE void ConstPool_freeGap(ConstPool* self, ConstPool::Gap* gap) noexcept { + gap->_next = self->_gapPool; + self->_gapPool = gap; +} + +static void ConstPool_addGap(ConstPool* self, size_t offset, size_t length) noexcept { + ASMJIT_ASSERT(length > 0); + + while (length > 0) { + size_t gapIndex; + size_t gapLength; + + gapIndex = ConstPool::kIndex16; + if (length >= 16 && Utils::isAligned(offset, 16)) { + gapLength = 16; + } + else if (length >= 8 && Utils::isAligned(offset, 8)) { + gapIndex = ConstPool::kIndex8; + gapLength = 8; + } + else if (length >= 4 && Utils::isAligned(offset, 4)) { + gapIndex = ConstPool::kIndex4; + gapLength = 4; + } + else if (length >= 2 && Utils::isAligned(offset, 2)) { + gapIndex = ConstPool::kIndex2; + gapLength = 2; + } + else { + gapIndex = ConstPool::kIndex1; + gapLength = 1; + } + + // We don't have to check for errors here, if this failed nothing really + // happened (just the gap won't be visible) and it will fail again at + // place where checking will cause kErrorNoHeapMemory. + ConstPool::Gap* gap = ConstPool_allocGap(self); + if (!gap) return; + + gap->_next = self->_gaps[gapIndex]; + self->_gaps[gapIndex] = gap; + + gap->_offset = offset; + gap->_length = gapLength; + + offset += gapLength; + length -= gapLength; + } +} + +Error ConstPool::add(const void* data, size_t size, size_t& dstOffset) noexcept { + size_t treeIndex; + + if (size == 32) + treeIndex = kIndex32; + else if (size == 16) + treeIndex = kIndex16; + else if (size == 8) + treeIndex = kIndex8; + else if (size == 4) + treeIndex = kIndex4; + else if (size == 2) + treeIndex = kIndex2; + else if (size == 1) + treeIndex = kIndex1; + else + return DebugUtils::errored(kErrorInvalidArgument); + + ConstPool::Node* node = _tree[treeIndex].get(data); + if (node) { + dstOffset = node->_offset; + return kErrorOk; + } + + // Before incrementing the current offset try if there is a gap that can + // be used for the requested data. + size_t offset = ~static_cast(0); + size_t gapIndex = treeIndex; + + while (gapIndex != kIndexCount - 1) { + ConstPool::Gap* gap = _gaps[treeIndex]; + + // Check if there is a gap. + if (gap) { + size_t gapOffset = gap->_offset; + size_t gapLength = gap->_length; + + // Destroy the gap for now. + _gaps[treeIndex] = gap->_next; + ConstPool_freeGap(this, gap); + + offset = gapOffset; + ASMJIT_ASSERT(Utils::isAligned(offset, size)); + + gapLength -= size; + if (gapLength > 0) + ConstPool_addGap(this, gapOffset, gapLength); + } + + gapIndex++; + } + + if (offset == ~static_cast(0)) { + // Get how many bytes have to be skipped so the address is aligned accordingly + // to the 'size'. + size_t diff = Utils::alignDiff(_size, size); + + if (diff != 0) { + ConstPool_addGap(this, _size, diff); + _size += diff; + } + + offset = _size; + _size += size; + } + + // Add the initial node to the right index. + node = ConstPool::Tree::_newNode(_zone, data, size, offset, false); + if (!node) return DebugUtils::errored(kErrorNoHeapMemory); + + _tree[treeIndex].put(node); + _alignment = std::max(_alignment, size); + + dstOffset = offset; + + // Now create a bunch of shared constants that are based on the data pattern. + // We stop at size 4, it probably doesn't make sense to split constants down + // to 1 byte. + size_t pCount = 1; + while (size > 4) { + size >>= 1; + pCount <<= 1; + + ASMJIT_ASSERT(treeIndex != 0); + treeIndex--; + + const uint8_t* pData = static_cast(data); + for (size_t i = 0; i < pCount; i++, pData += size) { + node = _tree[treeIndex].get(pData); + if (node) continue; + + node = ConstPool::Tree::_newNode(_zone, pData, size, offset + (i * size), true); + _tree[treeIndex].put(node); + } + } + + return kErrorOk; +} + +// ============================================================================ +// [asmjit::ConstPool - Reset] +// ============================================================================ + +struct ConstPoolFill { + ASMJIT_INLINE ConstPoolFill(uint8_t* dst, size_t dataSize) noexcept : + _dst(dst), + _dataSize(dataSize) {} + + ASMJIT_INLINE void visit(const ConstPool::Node* node) noexcept { + if (!node->_shared) + ::memcpy(_dst + node->_offset, node->getData(), _dataSize); + } + + uint8_t* _dst; + size_t _dataSize; +}; + +void ConstPool::fill(void* dst) const noexcept { + // Clears possible gaps, asmjit should never emit garbage to the output. + ::memset(dst, 0, _size); + + ConstPoolFill filler(static_cast(dst), 1); + for (size_t i = 0; i < ASMJIT_ARRAY_SIZE(_tree); i++) { + _tree[i].iterate(filler); + filler._dataSize <<= 1; + } +} + +// ============================================================================ +// [asmjit::ConstPool - Test] +// ============================================================================ + +#if defined(ASMJIT_TEST) +UNIT(base_constpool) { + Zone zone(32384 - Zone::kZoneOverhead); + ConstPool pool(&zone); + + uint32_t i; + uint32_t kCount = 1000000; + + INFO("Adding %u constants to the pool.", kCount); + { + size_t prevOffset; + size_t curOffset; + uint64_t c = ASMJIT_UINT64_C(0x0101010101010101); + + EXPECT(pool.add(&c, 8, prevOffset) == kErrorOk, + "pool.add() - Returned error"); + EXPECT(prevOffset == 0, + "pool.add() - First constant should have zero offset"); + + for (i = 1; i < kCount; i++) { + c++; + EXPECT(pool.add(&c, 8, curOffset) == kErrorOk, + "pool.add() - Returned error"); + EXPECT(prevOffset + 8 == curOffset, + "pool.add() - Returned incorrect curOffset"); + EXPECT(pool.getSize() == (i + 1) * 8, + "pool.getSize() - Reported incorrect size"); + prevOffset = curOffset; + } + + EXPECT(pool.getAlignment() == 8, + "pool.getAlignment() - Expected 8-byte alignment"); + } + + INFO("Retrieving %u constants from the pool.", kCount); + { + uint64_t c = ASMJIT_UINT64_C(0x0101010101010101); + + for (i = 0; i < kCount; i++) { + size_t offset; + EXPECT(pool.add(&c, 8, offset) == kErrorOk, + "pool.add() - Returned error"); + EXPECT(offset == i * 8, + "pool.add() - Should have reused constant"); + c++; + } + } + + INFO("Checking if the constants were split into 4-byte patterns"); + { + uint32_t c = 0x01010101; + for (i = 0; i < kCount; i++) { + size_t offset; + EXPECT(pool.add(&c, 4, offset) == kErrorOk, + "pool.add() - Returned error"); + EXPECT(offset == i * 8, + "pool.add() - Should reuse existing constant"); + c++; + } + } + + INFO("Adding 2 byte constant to misalign the current offset"); + { + uint16_t c = 0xFFFF; + size_t offset; + + EXPECT(pool.add(&c, 2, offset) == kErrorOk, + "pool.add() - Returned error"); + EXPECT(offset == kCount * 8, + "pool.add() - Didn't return expected position"); + EXPECT(pool.getAlignment() == 8, + "pool.getAlignment() - Expected 8-byte alignment"); + } + + INFO("Adding 8 byte constant to check if pool gets aligned again"); + { + uint64_t c = ASMJIT_UINT64_C(0xFFFFFFFFFFFFFFFF); + size_t offset; + + EXPECT(pool.add(&c, 8, offset) == kErrorOk, + "pool.add() - Returned error"); + EXPECT(offset == kCount * 8 + 8, + "pool.add() - Didn't return aligned offset"); + } + + INFO("Adding 2 byte constant to verify the gap is filled"); + { + uint16_t c = 0xFFFE; + size_t offset; + + EXPECT(pool.add(&c, 2, offset) == kErrorOk, + "pool.add() - Returned error"); + EXPECT(offset == kCount * 8 + 2, + "pool.add() - Didn't fill the gap"); + EXPECT(pool.getAlignment() == 8, + "pool.getAlignment() - Expected 8-byte alignment"); + } + + INFO("Checking reset functionality"); + { + pool.reset(&zone); + zone.reset(); + + EXPECT(pool.getSize() == 0, + "pool.getSize() - Expected pool size to be zero"); + EXPECT(pool.getAlignment() == 0, + "pool.getSize() - Expected pool alignment to be zero"); + } + + INFO("Checking pool alignment when combined constants are added"); + { + uint8_t bytes[32] = { 0 }; + size_t offset; + + pool.add(bytes, 1, offset); + + EXPECT(pool.getSize() == 1, + "pool.getSize() - Expected pool size to be 1 byte"); + EXPECT(pool.getAlignment() == 1, + "pool.getSize() - Expected pool alignment to be 1 byte"); + EXPECT(offset == 0, + "pool.getSize() - Expected offset returned to be zero"); + + pool.add(bytes, 2, offset); + + EXPECT(pool.getSize() == 4, + "pool.getSize() - Expected pool size to be 4 bytes"); + EXPECT(pool.getAlignment() == 2, + "pool.getSize() - Expected pool alignment to be 2 bytes"); + EXPECT(offset == 2, + "pool.getSize() - Expected offset returned to be 2"); + + pool.add(bytes, 4, offset); + + EXPECT(pool.getSize() == 8, + "pool.getSize() - Expected pool size to be 8 bytes"); + EXPECT(pool.getAlignment() == 4, + "pool.getSize() - Expected pool alignment to be 4 bytes"); + EXPECT(offset == 4, + "pool.getSize() - Expected offset returned to be 4"); + + pool.add(bytes, 4, offset); + + EXPECT(pool.getSize() == 8, + "pool.getSize() - Expected pool size to be 8 bytes"); + EXPECT(pool.getAlignment() == 4, + "pool.getSize() - Expected pool alignment to be 4 bytes"); + EXPECT(offset == 4, + "pool.getSize() - Expected offset returned to be 8"); + + pool.add(bytes, 32, offset); + EXPECT(pool.getSize() == 64, + "pool.getSize() - Expected pool size to be 64 bytes"); + EXPECT(pool.getAlignment() == 32, + "pool.getSize() - Expected pool alignment to be 32 bytes"); + EXPECT(offset == 32, + "pool.getSize() - Expected offset returned to be 32"); + } +} +#endif // ASMJIT_TEST + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" diff --git a/libraries/asmjit/asmjit/base/constpool.h b/libraries/asmjit/asmjit/base/constpool.h new file mode 100644 index 00000000000..945ea647bbc --- /dev/null +++ b/libraries/asmjit/asmjit/base/constpool.h @@ -0,0 +1,257 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Guard] +#ifndef _ASMJIT_BASE_CONSTPOOL_H +#define _ASMJIT_BASE_CONSTPOOL_H + +// [Dependencies] +#include "../base/zone.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +//! \addtogroup asmjit_base +//! \{ + +// ============================================================================ +// [asmjit::ConstPool] +// ============================================================================ + +//! Constant pool. +class ConstPool { +public: + ASMJIT_NONCOPYABLE(ConstPool) + + enum { + kIndex1 = 0, + kIndex2 = 1, + kIndex4 = 2, + kIndex8 = 3, + kIndex16 = 4, + kIndex32 = 5, + kIndexCount = 6 + }; + + // -------------------------------------------------------------------------- + // [Gap] + // -------------------------------------------------------------------------- + + //! \internal + //! + //! Zone-allocated const-pool gap. + struct Gap { + Gap* _next; //!< Pointer to the next gap + size_t _offset; //!< Offset of the gap. + size_t _length; //!< Remaining bytes of the gap (basically a gap size). + }; + + // -------------------------------------------------------------------------- + // [Node] + // -------------------------------------------------------------------------- + + //! \internal + //! + //! Zone-allocated const-pool node. + struct Node { + ASMJIT_INLINE void* getData() const noexcept { + return static_cast(const_cast(this) + 1); + } + + Node* _link[2]; //!< Left/Right nodes. + uint32_t _level : 31; //!< Horizontal level for balance. + uint32_t _shared : 1; //!< If this constant is shared with another. + uint32_t _offset; //!< Data offset from the beginning of the pool. + }; + + // -------------------------------------------------------------------------- + // [Tree] + // -------------------------------------------------------------------------- + + //! \internal + //! + //! Zone-allocated const-pool tree. + struct Tree { + enum { + //! Maximum tree height == log2(1 << 64). + kHeightLimit = 64 + }; + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE Tree(size_t dataSize = 0) noexcept + : _root(nullptr), + _length(0), + _dataSize(dataSize) {} + ASMJIT_INLINE ~Tree() {} + + // -------------------------------------------------------------------------- + // [Reset] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE void reset() noexcept { + _root = nullptr; + _length = 0; + } + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE bool isEmpty() const noexcept { return _length == 0; } + ASMJIT_INLINE size_t getLength() const noexcept { return _length; } + + ASMJIT_INLINE void setDataSize(size_t dataSize) noexcept { + ASMJIT_ASSERT(isEmpty()); + _dataSize = dataSize; + } + + // -------------------------------------------------------------------------- + // [Ops] + // -------------------------------------------------------------------------- + + ASMJIT_API Node* get(const void* data) noexcept; + ASMJIT_API void put(Node* node) noexcept; + + // -------------------------------------------------------------------------- + // [Iterate] + // -------------------------------------------------------------------------- + + template + ASMJIT_INLINE void iterate(Visitor& visitor) const noexcept { + Node* node = const_cast(_root); + if (!node) return; + + Node* stack[kHeightLimit]; + size_t top = 0; + + for (;;) { + Node* left = node->_link[0]; + if (left != nullptr) { + ASMJIT_ASSERT(top != kHeightLimit); + stack[top++] = node; + + node = left; + continue; + } + +Visit: + visitor.visit(node); + node = node->_link[1]; + if (node != nullptr) + continue; + + if (top == 0) + return; + + node = stack[--top]; + goto Visit; + } + } + + // -------------------------------------------------------------------------- + // [Helpers] + // -------------------------------------------------------------------------- + + static ASMJIT_INLINE Node* _newNode(Zone* zone, const void* data, size_t size, size_t offset, bool shared) noexcept { + Node* node = zone->allocT(sizeof(Node) + size); + if (ASMJIT_UNLIKELY(!node)) return nullptr; + + node->_link[0] = nullptr; + node->_link[1] = nullptr; + node->_level = 1; + node->_shared = shared; + node->_offset = static_cast(offset); + + ::memcpy(node->getData(), data, size); + return node; + } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + Node* _root; //!< Root of the tree + size_t _length; //!< Length of the tree (count of nodes). + size_t _dataSize; //!< Size of the data. + }; + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + ASMJIT_API ConstPool(Zone* zone) noexcept; + ASMJIT_API ~ConstPool() noexcept; + + // -------------------------------------------------------------------------- + // [Reset] + // -------------------------------------------------------------------------- + + ASMJIT_API void reset(Zone* zone) noexcept; + + // -------------------------------------------------------------------------- + // [Ops] + // -------------------------------------------------------------------------- + + //! Get whether the constant-pool is empty. + ASMJIT_INLINE bool isEmpty() const noexcept { return _size == 0; } + //! Get the size of the constant-pool in bytes. + ASMJIT_INLINE size_t getSize() const noexcept { return _size; } + //! Get minimum alignment. + ASMJIT_INLINE size_t getAlignment() const noexcept { return _alignment; } + + //! Add a constant to the constant pool. + //! + //! The constant must have known size, which is 1, 2, 4, 8, 16 or 32 bytes. + //! The constant is added to the pool only if it doesn't not exist, otherwise + //! cached value is returned. + //! + //! AsmJit is able to subdivide added constants, so for example if you add + //! 8-byte constant 0x1122334455667788 it will create the following slots: + //! + //! 8-byte: 0x1122334455667788 + //! 4-byte: 0x11223344, 0x55667788 + //! + //! The reason is that when combining MMX/SSE/AVX code some patterns are used + //! frequently. However, AsmJit is not able to reallocate a constant that has + //! been already added. For example if you try to add 4-byte constant and then + //! 8-byte constant having the same 4-byte pattern as the previous one, two + //! independent slots will be generated by the pool. + ASMJIT_API Error add(const void* data, size_t size, size_t& dstOffset) noexcept; + + // -------------------------------------------------------------------------- + // [Fill] + // -------------------------------------------------------------------------- + + //! Fill the destination with the constants from the pool. + ASMJIT_API void fill(void* dst) const noexcept; + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + Zone* _zone; //!< Zone allocator. + Tree _tree[kIndexCount]; //!< Tree per size. + Gap* _gaps[kIndexCount]; //!< Gaps per size. + Gap* _gapPool; //!< Gaps pool + + size_t _size; //!< Size of the pool (in bytes). + size_t _alignment; //!< Required pool alignment. +}; + +//! \} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" + +// [Guard] +#endif // _ASMJIT_BASE_CONSTPOOL_H diff --git a/libraries/asmjit/asmjit/base/cpuinfo.cpp b/libraries/asmjit/asmjit/base/cpuinfo.cpp new file mode 100644 index 00000000000..c8421735d2e --- /dev/null +++ b/libraries/asmjit/asmjit/base/cpuinfo.cpp @@ -0,0 +1,674 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Export] +#define ASMJIT_EXPORTS + +// [Dependencies] +#include "../base/cpuinfo.h" +#include "../base/utils.h" + +#if ASMJIT_OS_POSIX +# include +# include +# include +#endif // ASMJIT_OS_POSIX + +#if ASMJIT_ARCH_X86 || ASMJIT_ARCH_X64 +# if ASMJIT_CC_MSC_GE(14, 0, 0) + # include // Required by `__cpuid()` and `_xgetbv()`. +# endif // _MSC_VER >= 1400 +#endif + +#if ASMJIT_ARCH_ARM32 || ASMJIT_ARCH_ARM64 +# if ASMJIT_OS_LINUX +# include // Required by `getauxval()`. +# endif +#endif + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +// ============================================================================ +// [asmjit::CpuInfo - Detect ARM] +// ============================================================================ + +// ARM information has to be retrieved by the OS (this is how ARM was designed). +#if ASMJIT_ARCH_ARM32 || ASMJIT_ARCH_ARM64 + +#if ASMJIT_ARCH_ARM32 +static ASMJIT_INLINE void armPopulateBaselineA32Features(CpuInfo* cpuInfo) noexcept { + cpuInfo->_archInfo.init(ArchInfo::kTypeA32); +} +#endif // ASMJIT_ARCH_ARM32 + +#if ASMJIT_ARCH_ARM64 +static ASMJIT_INLINE void armPopulateBaselineA64Features(CpuInfo* cpuInfo) noexcept { + cpuInfo->_archInfo.init(ArchInfo::kTypeA64); + + // Thumb (including all variations) is supported on A64 (but not accessible from A64). + cpuInfo->addFeature(CpuInfo::kArmFeatureTHUMB); + cpuInfo->addFeature(CpuInfo::kArmFeatureTHUMB2); + + // A64 is based on ARMv8 and newer. + cpuInfo->addFeature(CpuInfo::kArmFeatureV6); + cpuInfo->addFeature(CpuInfo::kArmFeatureV7); + cpuInfo->addFeature(CpuInfo::kArmFeatureV8); + + // A64 comes with these features by default. + cpuInfo->addFeature(CpuInfo::kArmFeatureVFPv2); + cpuInfo->addFeature(CpuInfo::kArmFeatureVFPv3); + cpuInfo->addFeature(CpuInfo::kArmFeatureVFPv4); + cpuInfo->addFeature(CpuInfo::kArmFeatureEDSP); + cpuInfo->addFeature(CpuInfo::kArmFeatureASIMD); + cpuInfo->addFeature(CpuInfo::kArmFeatureIDIVA); + cpuInfo->addFeature(CpuInfo::kArmFeatureIDIVT); +} +#endif // ASMJIT_ARCH_ARM64 + +#if ASMJIT_OS_WINDOWS +//! \internal +//! +//! Detect ARM CPU features on Windows. +//! +//! The detection is based on `IsProcessorFeaturePresent()` API call. +static ASMJIT_INLINE void armDetectCpuInfoOnWindows(CpuInfo* cpuInfo) noexcept { +#if ASMJIT_ARCH_ARM32 + armPopulateBaselineA32Features(cpuInfo); + + // Windows for ARM requires at least ARMv7 with DSP extensions. + cpuInfo->addFeature(CpuInfo::kArmFeatureV6); + cpuInfo->addFeature(CpuInfo::kArmFeatureV7); + cpuInfo->addFeature(CpuInfo::kArmFeatureEDSP); + + // Windows for ARM requires VFPv3. + cpuInfo->addFeature(CpuInfo::kArmFeatureVFPv2); + cpuInfo->addFeature(CpuInfo::kArmFeatureVFPv3); + + // Windows for ARM requires and uses THUMB2. + cpuInfo->addFeature(CpuInfo::kArmFeatureTHUMB); + cpuInfo->addFeature(CpuInfo::kArmFeatureTHUMB2); +#else + armPopulateBaselineA64Features(cpuInfo); +#endif + + // Windows for ARM requires ASIMD. + cpuInfo->addFeature(CpuInfo::kArmFeatureASIMD); + + // Detect additional CPU features by calling `IsProcessorFeaturePresent()`. + struct WinPFPMapping { + uint32_t pfpId; + uint32_t featureId; + }; + + static const WinPFPMapping mapping[] = { + { PF_ARM_FMAC_INSTRUCTIONS_AVAILABLE , CpuInfo::kArmFeatureVFPv4 }, + { PF_ARM_VFP_32_REGISTERS_AVAILABLE , CpuInfo::kArmFeatureVFP_D32 }, + { PF_ARM_DIVIDE_INSTRUCTION_AVAILABLE, CpuInfo::kArmFeatureIDIVT }, + { PF_ARM_64BIT_LOADSTORE_ATOMIC , CpuInfo::kArmFeatureAtomics64 } + }; + + for (uint32_t i = 0; i < ASMJIT_ARRAY_SIZE(mapping); i++) + if (::IsProcessorFeaturePresent(mapping[i].pfpId)) + cpuInfo->addFeature(mapping[i].featureId); +} +#endif // ASMJIT_OS_WINDOWS + +#if ASMJIT_OS_LINUX +struct LinuxHWCapMapping { + uint32_t hwcapMask; + uint32_t featureId; +}; + +static void armDetectHWCaps(CpuInfo* cpuInfo, unsigned long type, const LinuxHWCapMapping* mapping, size_t length) noexcept { + unsigned long mask = getauxval(type); + + for (size_t i = 0; i < length; i++) + if ((mask & mapping[i].hwcapMask) == mapping[i].hwcapMask) + cpuInfo->addFeature(mapping[i].featureId); +} + +//! \internal +//! +//! Detect ARM CPU features on Linux. +//! +//! The detection is based on `getauxval()`. +ASMJIT_FAVOR_SIZE static void armDetectCpuInfoOnLinux(CpuInfo* cpuInfo) noexcept { +#if ASMJIT_ARCH_ARM32 + armPopulateBaselineA32Features(cpuInfo); + + // `AT_HWCAP` provides ARMv7 (and less) related flags. + static const LinuxHWCapMapping hwCapMapping[] = { + { /* HWCAP_VFP */ (1 << 6), CpuInfo::kArmFeatureVFPv2 }, + { /* HWCAP_EDSP */ (1 << 7), CpuInfo::kArmFeatureEDSP }, + { /* HWCAP_NEON */ (1 << 12), CpuInfo::kArmFeatureASIMD }, + { /* HWCAP_VFPv3 */ (1 << 13), CpuInfo::kArmFeatureVFPv3 }, + { /* HWCAP_VFPv4 */ (1 << 16), CpuInfo::kArmFeatureVFPv4 }, + { /* HWCAP_IDIVA */ (1 << 17), CpuInfo::kArmFeatureIDIVA }, + { /* HWCAP_IDIVT */ (1 << 18), CpuInfo::kArmFeatureIDIVT }, + { /* HWCAP_VFPD32 */ (1 << 19), CpuInfo::kArmFeatureVFP_D32 } + }; + armDetectHWCaps(cpuInfo, AT_HWCAP, hwCapMapping, ASMJIT_ARRAY_SIZE(hwCapMapping)); + + // VFPv3 implies VFPv2. + if (cpuInfo->hasFeature(CpuInfo::kArmFeatureVFPv3)) { + cpuInfo->addFeature(CpuInfo::kArmFeatureVFPv2); + } + + // VFPv2 implies ARMv6. + if (cpuInfo->hasFeature(CpuInfo::kArmFeatureVFPv2)) { + cpuInfo->addFeature(CpuInfo::kArmFeatureV6); + } + + // VFPv3 or ASIMD implies ARMv7. + if (cpuInfo->hasFeature(CpuInfo::kArmFeatureVFPv3) || + cpuInfo->hasFeature(CpuInfo::kArmFeatureASIMD)) { + cpuInfo->addFeature(CpuInfo::kArmFeatureV7); + } + + // `AT_HWCAP2` provides ARMv8+ related flags. + static const LinuxHWCapMapping hwCap2Mapping[] = { + { /* HWCAP2_AES */ (1 << 0), CpuInfo::kArmFeatureAES }, + { /* HWCAP2_PMULL */ (1 << 1), CpuInfo::kArmFeaturePMULL }, + { /* HWCAP2_SHA1 */ (1 << 2), CpuInfo::kArmFeatureSHA1 }, + { /* HWCAP2_SHA2 */ (1 << 3), CpuInfo::kArmFeatureSHA256 }, + { /* HWCAP2_CRC32 */ (1 << 4), CpuInfo::kArmFeatureCRC32 } + }; + armDetectHWCaps(cpuInfo, AT_HWCAP2, hwCap2Mapping, ASMJIT_ARRAY_SIZE(hwCap2Mapping)); + + if (cpuInfo->hasFeature(CpuInfo::kArmFeatureAES ) || + cpuInfo->hasFeature(CpuInfo::kArmFeatureCRC32 ) || + cpuInfo->hasFeature(CpuInfo::kArmFeaturePMULL ) || + cpuInfo->hasFeature(CpuInfo::kArmFeatureSHA1 ) || + cpuInfo->hasFeature(CpuInfo::kArmFeatureSHA256)) { + cpuInfo->addFeature(CpuInfo::kArmFeatureV8); + } +#else + armPopulateBaselineA64Features(cpuInfo); + + // `AT_HWCAP` provides ARMv8+ related flags. + static const LinuxHWCapMapping hwCapMapping[] = { + { /* HWCAP_ASIMD */ (1 << 1), CpuInfo::kArmFeatureASIMD }, + { /* HWCAP_AES */ (1 << 3), CpuInfo::kArmFeatureAES }, + { /* HWCAP_CRC32 */ (1 << 7), CpuInfo::kArmFeatureCRC32 }, + { /* HWCAP_PMULL */ (1 << 4), CpuInfo::kArmFeaturePMULL }, + { /* HWCAP_SHA1 */ (1 << 5), CpuInfo::kArmFeatureSHA1 }, + { /* HWCAP_SHA2 */ (1 << 6), CpuInfo::kArmFeatureSHA256 }, + { /* HWCAP_ATOMICS */ (1 << 8), CpuInfo::kArmFeatureAtomics64 } + }; + armDetectHWCaps(cpuInfo, AT_HWCAP, hwCapMapping, ASMJIT_ARRAY_SIZE(hwCapMapping)); + + // `AT_HWCAP2` is not used at the moment. +#endif +} +#endif // ASMJIT_OS_LINUX + +ASMJIT_FAVOR_SIZE static void armDetectCpuInfo(CpuInfo* cpuInfo) noexcept { +#if ASMJIT_OS_WINDOWS + armDetectCpuInfoOnWindows(cpuInfo); +#elif ASMJIT_OS_LINUX + armDetectCpuInfoOnLinux(cpuInfo); +#else +# error "[asmjit] armDetectCpuInfo() - Unsupported OS." +#endif +} +#endif // ASMJIT_ARCH_ARM32 || ASMJIT_ARCH_ARM64 + +// ============================================================================ +// [asmjit::CpuInfo - Detect X86] +// ============================================================================ + +#if ASMJIT_ARCH_X86 || ASMJIT_ARCH_X64 + +//! \internal +//! +//! X86 CPUID result. +struct CpuIdResult { + uint32_t eax, ebx, ecx, edx; +}; + +//! \internal +//! +//! Content of XCR register, result of XGETBV instruction. +struct XGetBVResult { + uint32_t eax, edx; +}; + +#if ASMJIT_CC_MSC && !ASMJIT_CC_MSC_GE(15, 0, 30729) && ASMJIT_ARCH_X64 +//! \internal +//! +//! HACK: VS2008 or less, 64-bit mode - `__cpuidex` doesn't exist! However, +//! 64-bit calling convention specifies the first parameter to be passed by +//! ECX, so we may be lucky if compiler doesn't move the register, otherwise +//! the result would be wrong. +static void ASMJIT_NOINLINE void x86CallCpuIdWorkaround(uint32_t inEcx, uint32_t inEax, CpuIdResult* result) noexcept { + __cpuid(reinterpret_cast(result), inEax); +} +#endif + +//! \internal +//! +//! Wrapper to call `cpuid` instruction. +static void ASMJIT_INLINE x86CallCpuId(CpuIdResult* result, uint32_t inEax, uint32_t inEcx = 0) noexcept { +#if ASMJIT_CC_MSC && ASMJIT_CC_MSC_GE(15, 0, 30729) + __cpuidex(reinterpret_cast(result), inEax, inEcx); +#elif ASMJIT_CC_MSC && ASMJIT_ARCH_X64 + x86CallCpuIdWorkaround(inEcx, inEax, result); +#elif ASMJIT_CC_MSC && ASMJIT_ARCH_X86 + uint32_t paramEax = inEax; + uint32_t paramEcx = inEcx; + uint32_t* out = reinterpret_cast(result); + + __asm { + mov eax, paramEax + mov ecx, paramEcx + mov edi, out + cpuid + mov dword ptr[edi + 0], eax + mov dword ptr[edi + 4], ebx + mov dword ptr[edi + 8], ecx + mov dword ptr[edi + 12], edx + } +#elif (ASMJIT_CC_GCC || ASMJIT_CC_CLANG) && ASMJIT_ARCH_X86 + __asm__ __volatile__( + "mov %%ebx, %%edi\n" + "cpuid\n" + "xchg %%edi, %%ebx\n" + : "=a"(result->eax), + "=D"(result->ebx), + "=c"(result->ecx), + "=d"(result->edx) + : "a"(inEax), + "c"(inEcx)); +#elif (ASMJIT_CC_GCC || ASMJIT_CC_CLANG || ASMJIT_CC_INTEL) && ASMJIT_ARCH_X64 + __asm__ __volatile__( + "mov %%rbx, %%rdi\n" + "cpuid\n" + "xchg %%rdi, %%rbx\n" + : "=a"(result->eax), + "=D"(result->ebx), + "=c"(result->ecx), + "=d"(result->edx) + : "a"(inEax), + "c"(inEcx)); +#else +# error "[asmjit] x86CallCpuid() - Unsupported compiler." +#endif +} + +//! \internal +//! +//! Wrapper to call `xgetbv` instruction. +static ASMJIT_INLINE void x86CallXGetBV(XGetBVResult* result, uint32_t inEcx) noexcept { +#if ASMJIT_CC_MSC_GE(16, 0, 40219) // 2010SP1+ + uint64_t value = _xgetbv(inEcx); + result->eax = static_cast(value & 0xFFFFFFFFU); + result->edx = static_cast(value >> 32); +#elif ASMJIT_CC_GCC || ASMJIT_CC_CLANG + uint32_t outEax; + uint32_t outEdx; + + // Replaced, because the world is not perfect: + // __asm__ __volatile__("xgetbv" : "=a"(outEax), "=d"(outEdx) : "c"(inEcx)); + __asm__ __volatile__(".byte 0x0F, 0x01, 0xd0" : "=a"(outEax), "=d"(outEdx) : "c"(inEcx)); + + result->eax = outEax; + result->edx = outEdx; +#else + result->eax = 0; + result->edx = 0; +#endif +} + +//! \internal +//! +//! Map a 12-byte vendor string returned by `cpuid` into a `CpuInfo::Vendor` ID. +static ASMJIT_INLINE uint32_t x86GetCpuVendorID(const char* vendorString) noexcept { + struct VendorData { + uint32_t id; + char text[12]; + }; + + static const VendorData vendorList[] = { + { CpuInfo::kVendorIntel , { 'G', 'e', 'n', 'u', 'i', 'n', 'e', 'I', 'n', 't', 'e', 'l' } }, + { CpuInfo::kVendorAMD , { 'A', 'u', 't', 'h', 'e', 'n', 't', 'i', 'c', 'A', 'M', 'D' } }, + { CpuInfo::kVendorVIA , { 'V', 'I', 'A', 0 , 'V', 'I', 'A', 0 , 'V', 'I', 'A', 0 } }, + { CpuInfo::kVendorVIA , { 'C', 'e', 'n', 't', 'a', 'u', 'r', 'H', 'a', 'u', 'l', 's' } } + }; + + uint32_t dw0 = reinterpret_cast(vendorString)[0]; + uint32_t dw1 = reinterpret_cast(vendorString)[1]; + uint32_t dw2 = reinterpret_cast(vendorString)[2]; + + for (uint32_t i = 0; i < ASMJIT_ARRAY_SIZE(vendorList); i++) { + if (dw0 == reinterpret_cast(vendorList[i].text)[0] && + dw1 == reinterpret_cast(vendorList[i].text)[1] && + dw2 == reinterpret_cast(vendorList[i].text)[2]) + return vendorList[i].id; + } + + return CpuInfo::kVendorNone; +} + +static ASMJIT_INLINE void x86SimplifyBrandString(char* s) noexcept { + // Used to always clear the current character to ensure that the result + // doesn't contain garbage after the new zero terminator. + char* d = s; + + char prev = 0; + char curr = s[0]; + s[0] = '\0'; + + for (;;) { + if (curr == 0) + break; + + if (curr == ' ') { + if (prev == '@' || s[1] == ' ' || s[1] == '@') + goto L_Skip; + } + + d[0] = curr; + d++; + prev = curr; + +L_Skip: + curr = *++s; + s[0] = '\0'; + } + + d[0] = '\0'; +} + +ASMJIT_FAVOR_SIZE static void x86DetectCpuInfo(CpuInfo* cpuInfo) noexcept { + uint32_t i, maxId; + + CpuIdResult regs; + XGetBVResult xcr0 = { 0, 0 }; + + cpuInfo->_archInfo.init(ArchInfo::kTypeHost); + cpuInfo->addFeature(CpuInfo::kX86FeatureI486); + + // -------------------------------------------------------------------------- + // [CPUID EAX=0x0] + // -------------------------------------------------------------------------- + + // Get vendor string/id. + x86CallCpuId(®s, 0x0); + + maxId = regs.eax; + ::memcpy(cpuInfo->_vendorString + 0, ®s.ebx, 4); + ::memcpy(cpuInfo->_vendorString + 4, ®s.edx, 4); + ::memcpy(cpuInfo->_vendorString + 8, ®s.ecx, 4); + cpuInfo->_vendorId = x86GetCpuVendorID(cpuInfo->_vendorString); + + // -------------------------------------------------------------------------- + // [CPUID EAX=0x1] + // -------------------------------------------------------------------------- + + if (maxId >= 0x1) { + // Get feature flags in ECX/EDX and family/model in EAX. + x86CallCpuId(®s, 0x1); + + // Fill family and model fields. + cpuInfo->_family = (regs.eax >> 8) & 0x0F; + cpuInfo->_model = (regs.eax >> 4) & 0x0F; + cpuInfo->_stepping = (regs.eax ) & 0x0F; + + // Use extended family and model fields. + if (cpuInfo->_family == 0x0F) { + cpuInfo->_family += ((regs.eax >> 20) & 0xFF); + cpuInfo->_model += ((regs.eax >> 16) & 0x0F) << 4; + } + + cpuInfo->_x86Data._processorType = ((regs.eax >> 12) & 0x03); + cpuInfo->_x86Data._brandIndex = ((regs.ebx ) & 0xFF); + cpuInfo->_x86Data._flushCacheLineSize = ((regs.ebx >> 8) & 0xFF) * 8; + cpuInfo->_x86Data._maxLogicalProcessors = ((regs.ebx >> 16) & 0xFF); + + if (regs.ecx & 0x00000001U) cpuInfo->addFeature(CpuInfo::kX86FeatureSSE3); + if (regs.ecx & 0x00000002U) cpuInfo->addFeature(CpuInfo::kX86FeaturePCLMULQDQ); + if (regs.ecx & 0x00000008U) cpuInfo->addFeature(CpuInfo::kX86FeatureMONITOR); + if (regs.ecx & 0x00000200U) cpuInfo->addFeature(CpuInfo::kX86FeatureSSSE3); + if (regs.ecx & 0x00002000U) cpuInfo->addFeature(CpuInfo::kX86FeatureCMPXCHG16B); + if (regs.ecx & 0x00080000U) cpuInfo->addFeature(CpuInfo::kX86FeatureSSE4_1); + if (regs.ecx & 0x00100000U) cpuInfo->addFeature(CpuInfo::kX86FeatureSSE4_2); + if (regs.ecx & 0x00400000U) cpuInfo->addFeature(CpuInfo::kX86FeatureMOVBE); + if (regs.ecx & 0x00800000U) cpuInfo->addFeature(CpuInfo::kX86FeaturePOPCNT); + if (regs.ecx & 0x02000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureAESNI); + if (regs.ecx & 0x04000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureXSAVE); + if (regs.ecx & 0x08000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureOSXSAVE); + if (regs.ecx & 0x40000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureRDRAND); + if (regs.edx & 0x00000010U) cpuInfo->addFeature(CpuInfo::kX86FeatureRDTSC); + if (regs.edx & 0x00000020U) cpuInfo->addFeature(CpuInfo::kX86FeatureMSR); + if (regs.edx & 0x00000100U) cpuInfo->addFeature(CpuInfo::kX86FeatureCMPXCHG8B); + if (regs.edx & 0x00008000U) cpuInfo->addFeature(CpuInfo::kX86FeatureCMOV); + if (regs.edx & 0x00080000U) cpuInfo->addFeature(CpuInfo::kX86FeatureCLFLUSH); + if (regs.edx & 0x00800000U) cpuInfo->addFeature(CpuInfo::kX86FeatureMMX); + if (regs.edx & 0x01000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureFXSR); + if (regs.edx & 0x02000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureSSE) + .addFeature(CpuInfo::kX86FeatureMMX2); + if (regs.edx & 0x04000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureSSE) + .addFeature(CpuInfo::kX86FeatureSSE2); + if (regs.edx & 0x10000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureMT); + + // Get the content of XCR0 if supported by CPU and enabled by OS. + if ((regs.ecx & 0x0C000000U) == 0x0C000000U) { + x86CallXGetBV(&xcr0, 0); + } + + // Detect AVX+. + if (regs.ecx & 0x10000000U) { + // - XCR0[2:1] == 11b + // XMM & YMM states need to be enabled by OS. + if ((xcr0.eax & 0x00000006U) == 0x00000006U) { + cpuInfo->addFeature(CpuInfo::kX86FeatureAVX); + + if (regs.ecx & 0x00001000U) cpuInfo->addFeature(CpuInfo::kX86FeatureFMA); + if (regs.ecx & 0x20000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureF16C); + } + } + } + + // -------------------------------------------------------------------------- + // [CPUID EAX=0x7] + // -------------------------------------------------------------------------- + + // Detect new features if the processor supports CPUID-07. + bool maybeMPX = false; + + if (maxId >= 0x7) { + x86CallCpuId(®s, 0x7); + + if (regs.ebx & 0x00000001U) cpuInfo->addFeature(CpuInfo::kX86FeatureFSGSBASE); + if (regs.ebx & 0x00000008U) cpuInfo->addFeature(CpuInfo::kX86FeatureBMI); + if (regs.ebx & 0x00000010U) cpuInfo->addFeature(CpuInfo::kX86FeatureHLE); + if (regs.ebx & 0x00000080U) cpuInfo->addFeature(CpuInfo::kX86FeatureSMEP); + if (regs.ebx & 0x00000100U) cpuInfo->addFeature(CpuInfo::kX86FeatureBMI2); + if (regs.ebx & 0x00000200U) cpuInfo->addFeature(CpuInfo::kX86FeatureERMS); + if (regs.ebx & 0x00000800U) cpuInfo->addFeature(CpuInfo::kX86FeatureRTM); + if (regs.ebx & 0x00004000U) maybeMPX = true; + if (regs.ebx & 0x00040000U) cpuInfo->addFeature(CpuInfo::kX86FeatureRDSEED); + if (regs.ebx & 0x00080000U) cpuInfo->addFeature(CpuInfo::kX86FeatureADX); + if (regs.ebx & 0x00100000U) cpuInfo->addFeature(CpuInfo::kX86FeatureSMAP); + if (regs.ebx & 0x00400000U) cpuInfo->addFeature(CpuInfo::kX86FeaturePCOMMIT); + if (regs.ebx & 0x00800000U) cpuInfo->addFeature(CpuInfo::kX86FeatureCLFLUSHOPT); + if (regs.ebx & 0x01000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureCLWB); + if (regs.ebx & 0x20000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureSHA); + if (regs.ecx & 0x00000001U) cpuInfo->addFeature(CpuInfo::kX86FeaturePREFETCHWT1); + + // TSX is supported if at least one of `HLE` and `RTM` is supported. + if (regs.ebx & 0x00000810U) cpuInfo->addFeature(CpuInfo::kX86FeatureTSX); + + // Detect AVX2. + if (cpuInfo->hasFeature(CpuInfo::kX86FeatureAVX)) { + if (regs.ebx & 0x00000020U) cpuInfo->addFeature(CpuInfo::kX86FeatureAVX2); + } + + // Detect AVX-512+. + if (regs.ebx & 0x00010000U) { + // - XCR0[2:1] == 11b + // XMM/YMM states need to be enabled by OS. + // - XCR0[7:5] == 111b + // Upper 256-bit of ZMM0-XMM15 and ZMM16-ZMM31 need to be enabled by the OS. + if ((xcr0.eax & 0x000000E6U) == 0x000000E6U) { + cpuInfo->addFeature(CpuInfo::kX86FeatureAVX512_F); + + if (regs.ebx & 0x00020000U) cpuInfo->addFeature(CpuInfo::kX86FeatureAVX512_DQ); + if (regs.ebx & 0x00200000U) cpuInfo->addFeature(CpuInfo::kX86FeatureAVX512_IFMA); + if (regs.ebx & 0x04000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureAVX512_PFI); + if (regs.ebx & 0x08000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureAVX512_ERI); + if (regs.ebx & 0x10000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureAVX512_CDI); + if (regs.ebx & 0x40000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureAVX512_BW); + if (regs.ebx & 0x80000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureAVX512_VL); + if (regs.ecx & 0x00000002U) cpuInfo->addFeature(CpuInfo::kX86FeatureAVX512_VBMI); + if (regs.ecx & 0x00004000U) cpuInfo->addFeature(CpuInfo::kX86FeatureAVX512_VPOPCNTDQ); + if (regs.edx & 0x00000004U) cpuInfo->addFeature(CpuInfo::kX86FeatureAVX512_4VNNIW); + if (regs.edx & 0x00000008U) cpuInfo->addFeature(CpuInfo::kX86FeatureAVX512_4FMAPS); + } + } + } + + // -------------------------------------------------------------------------- + // [CPUID EAX=0xD] + // -------------------------------------------------------------------------- + + if (maxId >= 0xD) { + x86CallCpuId(®s, 0xD, 0); + + // Both CPUID result and XCR0 has to be enabled to have support for MPX. + if (((regs.eax & xcr0.eax) & 0x00000018U) == 0x00000018U && maybeMPX) + cpuInfo->addFeature(CpuInfo::kX86FeatureMPX); + + x86CallCpuId(®s, 0xD, 1); + if (regs.eax & 0x00000001U) cpuInfo->addFeature(CpuInfo::kX86FeatureXSAVEOPT); + if (regs.eax & 0x00000002U) cpuInfo->addFeature(CpuInfo::kX86FeatureXSAVEC); + if (regs.eax & 0x00000008U) cpuInfo->addFeature(CpuInfo::kX86FeatureXSAVES); + } + + // -------------------------------------------------------------------------- + // [CPUID EAX=0x80000000...maxId] + // -------------------------------------------------------------------------- + + // The highest EAX that we understand. + uint32_t kHighestProcessedEAX = 0x80000008U; + + // Several CPUID calls are required to get the whole branc string. It's easy + // to copy one DWORD at a time instead of performing a byte copy. + uint32_t* brand = reinterpret_cast(cpuInfo->_brandString); + + i = maxId = 0x80000000U; + do { + x86CallCpuId(®s, i); + switch (i) { + case 0x80000000U: + maxId = std::min(regs.eax, kHighestProcessedEAX); + break; + + case 0x80000001U: + if (regs.ecx & 0x00000001U) cpuInfo->addFeature(CpuInfo::kX86FeatureLAHFSAHF); + if (regs.ecx & 0x00000020U) cpuInfo->addFeature(CpuInfo::kX86FeatureLZCNT); + if (regs.ecx & 0x00000040U) cpuInfo->addFeature(CpuInfo::kX86FeatureSSE4A); + if (regs.ecx & 0x00000080U) cpuInfo->addFeature(CpuInfo::kX86FeatureMSSE); + if (regs.ecx & 0x00000100U) cpuInfo->addFeature(CpuInfo::kX86FeaturePREFETCHW); + if (regs.ecx & 0x00200000U) cpuInfo->addFeature(CpuInfo::kX86FeatureTBM); + if (regs.edx & 0x00100000U) cpuInfo->addFeature(CpuInfo::kX86FeatureNX); + if (regs.edx & 0x00200000U) cpuInfo->addFeature(CpuInfo::kX86FeatureFXSROPT); + if (regs.edx & 0x00400000U) cpuInfo->addFeature(CpuInfo::kX86FeatureMMX2); + if (regs.edx & 0x08000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureRDTSCP); + if (regs.edx & 0x40000000U) cpuInfo->addFeature(CpuInfo::kX86Feature3DNOW2) + .addFeature(CpuInfo::kX86FeatureMMX2); + if (regs.edx & 0x80000000U) cpuInfo->addFeature(CpuInfo::kX86Feature3DNOW); + + if (cpuInfo->hasFeature(CpuInfo::kX86FeatureAVX)) { + if (regs.ecx & 0x00000800U) cpuInfo->addFeature(CpuInfo::kX86FeatureXOP); + if (regs.ecx & 0x00010000U) cpuInfo->addFeature(CpuInfo::kX86FeatureFMA4); + } + + // These seem to be only supported by AMD. + if (cpuInfo->getVendorId() == CpuInfo::kVendorAMD) { + if (regs.ecx & 0x00000010U) cpuInfo->addFeature(CpuInfo::kX86FeatureALTMOVCR8); + } + break; + + case 0x80000002U: + case 0x80000003U: + case 0x80000004U: + *brand++ = regs.eax; + *brand++ = regs.ebx; + *brand++ = regs.ecx; + *brand++ = regs.edx; + + // Go directly to the last one. + if (i == 0x80000004U) i = 0x80000008U - 1; + break; + + case 0x80000008U: + if (regs.ebx & 0x00000001U) cpuInfo->addFeature(CpuInfo::kX86FeatureCLZERO); + break; + } + } while (++i <= maxId); + + // Simplify CPU brand string by removing unnecessary spaces. + x86SimplifyBrandString(cpuInfo->_brandString); +} +#endif // ASMJIT_ARCH_X86 || ASMJIT_ARCH_X64 + +// ============================================================================ +// [asmjit::CpuInfo - Detect - HWThreadsCount] +// ============================================================================ + +static ASMJIT_INLINE uint32_t cpuDetectHWThreadsCount() noexcept { +#if ASMJIT_OS_WINDOWS + SYSTEM_INFO info; + ::GetSystemInfo(&info); + return info.dwNumberOfProcessors; +#elif ASMJIT_OS_POSIX && defined(_SC_NPROCESSORS_ONLN) + long res = ::sysconf(_SC_NPROCESSORS_ONLN); + if (res <= 0) return 1; + return static_cast(res); +#else + return 1; +#endif +} + +// ============================================================================ +// [asmjit::CpuInfo - Detect] +// ============================================================================ + +ASMJIT_FAVOR_SIZE void CpuInfo::detect() noexcept { + reset(); + +#if ASMJIT_ARCH_ARM32 || ASMJIT_ARCH_ARM64 + armDetectCpuInfo(this); +#endif // ASMJIT_ARCH_ARM32 || ASMJIT_ARCH_ARM64 + +#if ASMJIT_ARCH_X86 || ASMJIT_ARCH_X64 + x86DetectCpuInfo(this); +#endif // ASMJIT_ARCH_X86 || ASMJIT_ARCH_X64 + + _hwThreadsCount = cpuDetectHWThreadsCount(); +} + +// ============================================================================ +// [asmjit::CpuInfo - GetHost] +// ============================================================================ + +struct HostCpuInfo : public CpuInfo { + ASMJIT_INLINE HostCpuInfo() noexcept : CpuInfo() { detect(); } +}; + +const CpuInfo& CpuInfo::getHost() noexcept { + static HostCpuInfo host; + return host; +} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" diff --git a/libraries/asmjit/asmjit/base/cpuinfo.h b/libraries/asmjit/asmjit/base/cpuinfo.h new file mode 100644 index 00000000000..268d37e8dd3 --- /dev/null +++ b/libraries/asmjit/asmjit/base/cpuinfo.h @@ -0,0 +1,373 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Guard] +#ifndef _ASMJIT_BASE_CPUINFO_H +#define _ASMJIT_BASE_CPUINFO_H + +// [Dependencies] +#include "../base/arch.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +//! \addtogroup asmjit_base +//! \{ + +// ============================================================================ +// [asmjit::CpuFeatures] +// ============================================================================ + +class CpuFeatures { +public: + typedef uintptr_t BitWord; + + enum { + kMaxFeatures = 128, + kBitWordSize = static_cast(sizeof(BitWord)) * 8, + kNumBitWords = kMaxFeatures / kBitWordSize + }; + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE CpuFeatures() noexcept { reset(); } + ASMJIT_INLINE CpuFeatures(const CpuFeatures& other) noexcept { init(other); } + + // -------------------------------------------------------------------------- + // [Init / Reset] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE void init(const CpuFeatures& other) noexcept { ::memcpy(this, &other, sizeof(*this)); } + ASMJIT_INLINE void reset() noexcept { ::memset(this, 0, sizeof(*this)); } + + // -------------------------------------------------------------------------- + // [Ops] + // -------------------------------------------------------------------------- + + //! Get all features as `BitWord` array. + ASMJIT_INLINE BitWord* getBits() noexcept { return _bits; } + //! Get all features as `BitWord` array (const). + ASMJIT_INLINE const BitWord* getBits() const noexcept { return _bits; } + + //! Get if feature `feature` is present. + ASMJIT_INLINE bool has(uint32_t feature) const noexcept { + ASMJIT_ASSERT(feature < kMaxFeatures); + + uint32_t idx = feature / kBitWordSize; + uint32_t bit = feature % kBitWordSize; + + return static_cast((_bits[idx] >> bit) & 0x1); + } + + //! Get if all features as defined by `other` are present. + ASMJIT_INLINE bool hasAll(const CpuFeatures& other) const noexcept { + for (uint32_t i = 0; i < kNumBitWords; i++) + if ((_bits[i] & other._bits[i]) != other._bits[i]) + return false; + return true; + } + + //! Add a CPU `feature`. + ASMJIT_INLINE CpuFeatures& add(uint32_t feature) noexcept { + ASMJIT_ASSERT(feature < kMaxFeatures); + + uint32_t idx = feature / kBitWordSize; + uint32_t bit = feature % kBitWordSize; + + _bits[idx] |= static_cast(1) << bit; + return *this; + } + + //! Remove a CPU `feature`. + ASMJIT_INLINE CpuFeatures& remove(uint32_t feature) noexcept { + ASMJIT_ASSERT(feature < kMaxFeatures); + + uint32_t idx = feature / kBitWordSize; + uint32_t bit = feature % kBitWordSize; + + _bits[idx] &= ~(static_cast(1) << bit); + return *this; + } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + BitWord _bits[kNumBitWords]; +}; + +// ============================================================================ +// [asmjit::CpuInfo] +// ============================================================================ + +//! CPU information. +class CpuInfo { +public: + //! CPU vendor ID. + ASMJIT_ENUM(Vendor) { + kVendorNone = 0, //!< Generic or unknown. + kVendorIntel = 1, //!< Intel vendor. + kVendorAMD = 2, //!< AMD vendor. + kVendorVIA = 3 //!< VIA vendor. + }; + + //! ARM/ARM64 CPU features. + ASMJIT_ENUM(ArmFeatures) { + kArmFeatureV6 = 1, //!< ARMv6 instruction set. + kArmFeatureV7, //!< ARMv7 instruction set. + kArmFeatureV8, //!< ARMv8 instruction set. + kArmFeatureTHUMB, //!< CPU provides THUMB v1 instruction set (THUMB mode). + kArmFeatureTHUMB2, //!< CPU provides THUMB v2 instruction set (THUMB mode). + kArmFeatureVFPv2, //!< CPU provides VFPv2 instruction set. + kArmFeatureVFPv3, //!< CPU provides VFPv3 instruction set. + kArmFeatureVFPv4, //!< CPU provides VFPv4 instruction set. + kArmFeatureVFP_D32, //!< CPU provides 32 VFP-D (64-bit) registers. + kArmFeatureEDSP, //!< CPU provides EDSP extensions. + kArmFeatureASIMD, //!< CPU provides 'Advanced SIMD'. + kArmFeatureIDIVA, //!< CPU provides hardware SDIV and UDIV (ARM mode). + kArmFeatureIDIVT, //!< CPU provides hardware SDIV and UDIV (THUMB mode). + kArmFeatureAES, //!< CPU provides AES instructions (ARM64 only). + kArmFeatureCRC32, //!< CPU provides CRC32 instructions. + kArmFeaturePMULL, //!< CPU provides PMULL instructions (ARM64 only). + kArmFeatureSHA1, //!< CPU provides SHA1 instructions. + kArmFeatureSHA256, //!< CPU provides SHA256 instructions. + kArmFeatureAtomics64, //!< CPU provides 64-bit load/store atomics (ARM64 only). + + kArmFeaturesCount //!< Count of ARM/ARM64 CPU features. + }; + + //! X86/X64 CPU features. + ASMJIT_ENUM(X86Features) { + kX86FeatureI486 = 1, //!< CPU is at least I486. + kX86FeatureNX, //!< CPU has Not-Execute-Bit. + kX86FeatureMT, //!< CPU has multi-threading. + kX86FeatureALTMOVCR8, //!< CPU supports `LOCK MOV CR8` (AMD CPUs). + kX86FeatureCMOV, //!< CPU has CMOV. + kX86FeatureCMPXCHG8B, //!< CPU has CMPXCHG8B. + kX86FeatureCMPXCHG16B, //!< CPU has CMPXCHG16B (x64). + kX86FeatureMSR, //!< CPU has RDMSR/WRMSR. + kX86FeatureRDTSC, //!< CPU has RDTSC. + kX86FeatureRDTSCP, //!< CPU has RDTSCP. + kX86FeatureCLFLUSH, //!< CPU has CLFUSH. + kX86FeatureCLFLUSHOPT, //!< CPU has CLFUSHOPT. + kX86FeatureCLWB, //!< CPU has CLWB. + kX86FeatureCLZERO, //!< CPU has CLZERO. + kX86FeaturePCOMMIT, //!< CPU has PCOMMIT. + kX86FeaturePREFETCHW, //!< CPU has PREFETCHW. + kX86FeaturePREFETCHWT1, //!< CPU has PREFETCHWT1. + kX86FeatureLAHFSAHF, //!< CPU has LAHF/SAHF. + kX86FeatureFXSR, //!< CPU has FXSAVE/FXRSTOR. + kX86FeatureFXSROPT, //!< CPU has FXSAVE/FXRSTOR (optimized). + kX86FeatureMMX, //!< CPU has MMX. + kX86FeatureMMX2, //!< CPU has extended MMX. + kX86Feature3DNOW, //!< CPU has 3DNOW. + kX86Feature3DNOW2, //!< CPU has 3DNOW2 (enhanced). + kX86FeatureGEODE, //!< CPU has GEODE extensions (few additions to 3DNOW). + kX86FeatureSSE, //!< CPU has SSE. + kX86FeatureSSE2, //!< CPU has SSE2. + kX86FeatureSSE3, //!< CPU has SSE3. + kX86FeatureSSSE3, //!< CPU has SSSE3. + kX86FeatureSSE4A, //!< CPU has SSE4.A. + kX86FeatureSSE4_1, //!< CPU has SSE4.1. + kX86FeatureSSE4_2, //!< CPU has SSE4.2. + kX86FeatureMSSE, //!< CPU has Misaligned SSE (MSSE). + kX86FeatureMONITOR, //!< CPU has MONITOR and MWAIT. + kX86FeatureMOVBE, //!< CPU has MOVBE. + kX86FeaturePOPCNT, //!< CPU has POPCNT. + kX86FeatureLZCNT, //!< CPU has LZCNT. + kX86FeatureAESNI, //!< CPU has AESNI. + kX86FeaturePCLMULQDQ, //!< CPU has PCLMULQDQ. + kX86FeatureRDRAND, //!< CPU has RDRAND. + kX86FeatureRDSEED, //!< CPU has RDSEED. + kX86FeatureSMAP, //!< CPU has SMAP (supervisor-mode access prevention). + kX86FeatureSMEP, //!< CPU has SMEP (supervisor-mode execution prevention). + kX86FeatureSHA, //!< CPU has SHA-1 and SHA-256. + kX86FeatureXSAVE, //!< CPU has XSAVE support (XSAVE/XRSTOR, XSETBV/XGETBV, and XCR). + kX86FeatureXSAVEC, //!< CPU has XSAVEC support (XSAVEC). + kX86FeatureXSAVES, //!< CPU has XSAVES support (XSAVES/XRSTORS). + kX86FeatureXSAVEOPT, //!< CPU has XSAVEOPT support (XSAVEOPT/XSAVEOPT64). + kX86FeatureOSXSAVE, //!< CPU has XSAVE enabled by OS. + kX86FeatureAVX, //!< CPU has AVX. + kX86FeatureAVX2, //!< CPU has AVX2. + kX86FeatureF16C, //!< CPU has F16C. + kX86FeatureFMA, //!< CPU has FMA. + kX86FeatureFMA4, //!< CPU has FMA4. + kX86FeatureXOP, //!< CPU has XOP. + kX86FeatureBMI, //!< CPU has BMI (bit manipulation instructions #1). + kX86FeatureBMI2, //!< CPU has BMI2 (bit manipulation instructions #2). + kX86FeatureADX, //!< CPU has ADX (multi-precision add-carry instruction extensions). + kX86FeatureTBM, //!< CPU has TBM (trailing bit manipulation). + kX86FeatureMPX, //!< CPU has MPX (memory protection extensions). + kX86FeatureHLE, //!< CPU has HLE. + kX86FeatureRTM, //!< CPU has RTM. + kX86FeatureTSX, //!< CPU has TSX. + kX86FeatureERMS, //!< CPU has ERMS (enhanced REP MOVSB/STOSB). + kX86FeatureFSGSBASE, //!< CPU has FSGSBASE. + kX86FeatureAVX512_F, //!< CPU has AVX512-F (foundation). + kX86FeatureAVX512_CDI, //!< CPU has AVX512-CDI (conflict detection). + kX86FeatureAVX512_PFI, //!< CPU has AVX512-PFI (prefetch instructions). + kX86FeatureAVX512_ERI, //!< CPU has AVX512-ERI (exponential and reciprocal). + kX86FeatureAVX512_DQ, //!< CPU has AVX512-DQ (DWORD/QWORD). + kX86FeatureAVX512_BW, //!< CPU has AVX512-BW (BYTE/WORD). + kX86FeatureAVX512_VL, //!< CPU has AVX512-VL (vector length extensions). + kX86FeatureAVX512_IFMA, //!< CPU has AVX512-IFMA (integer fused-multiply-add using 52-bit precision). + kX86FeatureAVX512_VBMI, //!< CPU has AVX512-VBMI (vector byte manipulation). + kX86FeatureAVX512_VPOPCNTDQ, //!< CPU has AVX512-VPOPCNTDQ (VPOPCNT[D|Q] instructions). + kX86FeatureAVX512_4VNNIW, //!< CPU has AVX512-VNNIW (vector NN instructions word variable precision). + kX86FeatureAVX512_4FMAPS, //!< CPU has AVX512-FMAPS (FMA packed single). + + kX86FeaturesCount //!< Count of X86/X64 CPU features. + }; + + // -------------------------------------------------------------------------- + // [ArmInfo] + // -------------------------------------------------------------------------- + + struct ArmData { + }; + + // -------------------------------------------------------------------------- + // [X86Info] + // -------------------------------------------------------------------------- + + struct X86Data { + uint32_t _processorType; //!< Processor type. + uint32_t _brandIndex; //!< Brand index. + uint32_t _flushCacheLineSize; //!< Flush cache line size (in bytes). + uint32_t _maxLogicalProcessors; //!< Maximum number of addressable IDs for logical processors. + }; + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE CpuInfo() noexcept { reset(); } + ASMJIT_INLINE CpuInfo(const CpuInfo& other) noexcept { init(other); } + + // -------------------------------------------------------------------------- + // [Init / Reset] + // -------------------------------------------------------------------------- + + //! Initialize CpuInfo to the given architecture, see \ArchInfo. + ASMJIT_INLINE void initArch(uint32_t archType, uint32_t archMode = 0) noexcept { + _archInfo.init(archType, archMode); + } + + ASMJIT_INLINE void init(const CpuInfo& other) noexcept { ::memcpy(this, &other, sizeof(*this)); } + ASMJIT_INLINE void reset() noexcept { ::memset(this, 0, sizeof(*this)); } + + // -------------------------------------------------------------------------- + // [Detect] + // -------------------------------------------------------------------------- + + ASMJIT_API void detect() noexcept; + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get generic architecture information. + ASMJIT_INLINE const ArchInfo& getArchInfo() const noexcept { return _archInfo; } + //! Get CPU architecture type, see \ArchInfo::Type. + ASMJIT_INLINE uint32_t getArchType() const noexcept { return _archInfo.getType(); } + //! Get CPU architecture sub-type, see \ArchInfo::SubType. + ASMJIT_INLINE uint32_t getArchSubType() const noexcept { return _archInfo.getSubType(); } + + //! Get CPU vendor ID. + ASMJIT_INLINE uint32_t getVendorId() const noexcept { return _vendorId; } + //! Get CPU family ID. + ASMJIT_INLINE uint32_t getFamily() const noexcept { return _family; } + //! Get CPU model ID. + ASMJIT_INLINE uint32_t getModel() const noexcept { return _model; } + //! Get CPU stepping. + ASMJIT_INLINE uint32_t getStepping() const noexcept { return _stepping; } + + //! Get number of hardware threads available. + ASMJIT_INLINE uint32_t getHwThreadsCount() const noexcept { + return _hwThreadsCount; + } + + //! Get all CPU features. + ASMJIT_INLINE const CpuFeatures& getFeatures() const noexcept { return _features; } + //! Get whether CPU has a `feature`. + ASMJIT_INLINE bool hasFeature(uint32_t feature) const noexcept { return _features.has(feature); } + //! Add a CPU `feature`. + ASMJIT_INLINE CpuInfo& addFeature(uint32_t feature) noexcept { _features.add(feature); return *this; } + + //! Get CPU vendor string. + ASMJIT_INLINE const char* getVendorString() const noexcept { return _vendorString; } + //! Get CPU brand string. + ASMJIT_INLINE const char* getBrandString() const noexcept { return _brandString; } + + // -------------------------------------------------------------------------- + // [Accessors - ARM] + // -------------------------------------------------------------------------- + + // -------------------------------------------------------------------------- + // [Accessors - X86] + // -------------------------------------------------------------------------- + + //! Get processor type. + ASMJIT_INLINE uint32_t getX86ProcessorType() const noexcept { + return _x86Data._processorType; + } + + //! Get brand index. + ASMJIT_INLINE uint32_t getX86BrandIndex() const noexcept { + return _x86Data._brandIndex; + } + + //! Get flush cache line size. + ASMJIT_INLINE uint32_t getX86FlushCacheLineSize() const noexcept { + return _x86Data._flushCacheLineSize; + } + + //! Get maximum logical processors count. + ASMJIT_INLINE uint32_t getX86MaxLogicalProcessors() const noexcept { + return _x86Data._maxLogicalProcessors; + } + + // -------------------------------------------------------------------------- + // [Statics] + // -------------------------------------------------------------------------- + + //! Get the host CPU information. + ASMJIT_API static const CpuInfo& getHost() noexcept; + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + ArchInfo _archInfo; //!< CPU architecture information. + uint32_t _vendorId; //!< CPU vendor id, see \ref Vendor. + uint32_t _family; //!< CPU family ID. + uint32_t _model; //!< CPU model ID. + uint32_t _stepping; //!< CPU stepping. + uint32_t _hwThreadsCount; //!< Number of hardware threads. + CpuFeatures _features; //!< CPU features. + char _vendorString[16]; //!< CPU vendor string. + char _brandString[64]; //!< CPU brand string. + + // Architecture specific data. + union { + ArmData _armData; + X86Data _x86Data; + }; +}; + +//! \} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" + +// [Guard] +#endif // _ASMJIT_BASE_CPUINFO_H diff --git a/libraries/asmjit/asmjit/base/func.cpp b/libraries/asmjit/asmjit/base/func.cpp new file mode 100644 index 00000000000..52107655584 --- /dev/null +++ b/libraries/asmjit/asmjit/base/func.cpp @@ -0,0 +1,186 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Export] +#define ASMJIT_EXPORTS + +// [Dependencies] +#include "../base/arch.h" +#include "../base/func.h" + +#if defined(ASMJIT_BUILD_X86) +#include "../x86/x86internal_p.h" +#include "../x86/x86operand.h" +#endif // ASMJIT_BUILD_X86 + +#if defined(ASMJIT_BUILD_ARM) +#include "../arm/arminternal_p.h" +#include "../arm/armoperand.h" +#endif // ASMJIT_BUILD_ARM + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +// ============================================================================ +// [asmjit::CallConv - Init / Reset] +// ============================================================================ + +ASMJIT_FAVOR_SIZE Error CallConv::init(uint32_t ccId) noexcept { + reset(); + +#if defined(ASMJIT_BUILD_X86) + if (CallConv::isX86Family(ccId)) + return X86Internal::initCallConv(*this, ccId); +#endif // ASMJIT_BUILD_X86 + +#if defined(ASMJIT_BUILD_ARM) + if (CallConv::isArmFamily(ccId)) + return ArmInternal::initCallConv(*this, ccId); +#endif // ASMJIT_BUILD_ARM + + return DebugUtils::errored(kErrorInvalidArgument); +} + +// ============================================================================ +// [asmjit::FuncDetail - Init / Reset] +// ============================================================================ + +ASMJIT_FAVOR_SIZE Error FuncDetail::init(const FuncSignature& sign) { + uint32_t ccId = sign.getCallConv(); + CallConv& cc = _callConv; + + uint32_t argCount = sign.getArgCount(); + if (ASMJIT_UNLIKELY(argCount > kFuncArgCount)) + return DebugUtils::errored(kErrorInvalidArgument); + + ASMJIT_PROPAGATE(cc.init(ccId)); + + uint32_t gpSize = (cc.getArchType() == ArchInfo::kTypeX86) ? 4 : 8; + uint32_t deabstractDelta = TypeId::deabstractDeltaOfSize(gpSize); + + const uint8_t* args = sign.getArgs(); + for (uint32_t i = 0; i < argCount; i++) { + Value& arg = _args[i]; + arg.initTypeId(TypeId::deabstract(args[i], deabstractDelta)); + } + _argCount = static_cast(argCount); + + uint32_t ret = sign.getRet(); + if (ret != TypeId::kVoid) { + _rets[0].initTypeId(TypeId::deabstract(ret, deabstractDelta)); + _retCount = 1; + } + +#if defined(ASMJIT_BUILD_X86) + if (CallConv::isX86Family(ccId)) + return X86Internal::initFuncDetail(*this, sign, gpSize); +#endif // ASMJIT_BUILD_X86 + +#if defined(ASMJIT_BUILD_ARM) + if (CallConv::isArmFamily(ccId)) + return ArmInternal::initFuncDetail(*this, sign, gpSize); +#endif // ASMJIT_BUILD_ARM + + // We should never bubble here as if `cc.init()` succeeded then there has to + // be an implementation for the current architecture. However, stay safe. + return DebugUtils::errored(kErrorInvalidArgument); +} + +// ============================================================================ +// [asmjit::FuncFrameLayout - Init / Reset] +// ============================================================================ + +ASMJIT_FAVOR_SIZE Error FuncFrameLayout::init(const FuncDetail& func, const FuncFrameInfo& ffi) noexcept { + uint32_t ccId = func.getCallConv().getId(); + +#if defined(ASMJIT_BUILD_X86) + if (CallConv::isX86Family(ccId)) + return X86Internal::initFrameLayout(*this, func, ffi); +#endif // ASMJIT_BUILD_X86 + +#if defined(ASMJIT_BUILD_ARM) + if (CallConv::isArmFamily(ccId)) + return ArmInternal::initFrameLayout(*this, func, ffi); +#endif // ASMJIT_BUILD_ARM + + return DebugUtils::errored(kErrorInvalidArgument); +} + +// ============================================================================ +// [asmjit::FuncArgsMapper] +// ============================================================================ + +ASMJIT_FAVOR_SIZE Error FuncArgsMapper::updateFrameInfo(FuncFrameInfo& ffi) const noexcept { + const FuncDetail* func = getFuncDetail(); + if (!func) return DebugUtils::errored(kErrorInvalidState); + + uint32_t ccId = func->getCallConv().getId(); + +#if defined(ASMJIT_BUILD_X86) + if (CallConv::isX86Family(ccId)) + return X86Internal::argsToFrameInfo(*this, ffi); +#endif // ASMJIT_BUILD_X86 + +#if defined(ASMJIT_BUILD_ARM) + if (CallConv::isArmFamily(ccId)) + return ArmInternal::argsToFrameInfo(*this, ffi); +#endif // ASMJIT_BUILD_X86 + + return DebugUtils::errored(kErrorInvalidArch); +} + +// ============================================================================ +// [asmjit::FuncUtils] +// ============================================================================ + +ASMJIT_FAVOR_SIZE Error FuncUtils::emitProlog(CodeEmitter* emitter, const FuncFrameLayout& layout) { +#if defined(ASMJIT_BUILD_X86) + if (emitter->getArchInfo().isX86Family()) + return X86Internal::emitProlog(static_cast(emitter), layout); +#endif // ASMJIT_BUILD_X86 + +#if defined(ASMJIT_BUILD_ARM) + if (emitter->getArchInfo().isArmFamily()) + return ArmInternal::emitProlog(static_cast(emitter), layout); +#endif // ASMJIT_BUILD_ARM + + return DebugUtils::errored(kErrorInvalidArch); +} + +ASMJIT_FAVOR_SIZE Error FuncUtils::emitEpilog(CodeEmitter* emitter, const FuncFrameLayout& layout) { +#if defined(ASMJIT_BUILD_X86) + if (emitter->getArchInfo().isX86Family()) + return X86Internal::emitEpilog(static_cast(emitter), layout); +#endif // ASMJIT_BUILD_X86 + +#if defined(ASMJIT_BUILD_ARM) + if (emitter->getArchInfo().isArmFamily()) + return ArmInternal::emitEpilog(static_cast(emitter), layout); +#endif // ASMJIT_BUILD_ARM + + return DebugUtils::errored(kErrorInvalidArch); +} + +ASMJIT_FAVOR_SIZE Error FuncUtils::allocArgs(CodeEmitter* emitter, const FuncFrameLayout& layout, const FuncArgsMapper& args) { +#if defined(ASMJIT_BUILD_X86) + if (emitter->getArchInfo().isX86Family()) + return X86Internal::allocArgs(static_cast(emitter), layout, args); +#endif // ASMJIT_BUILD_X86 + +#if defined(ASMJIT_BUILD_ARM) + if (emitter->getArchInfo().isArmFamily()) + return ArmInternal::allocArgs(static_cast(emitter), layout, args); +#endif // ASMJIT_BUILD_ARM + + return DebugUtils::errored(kErrorInvalidArch); +} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" diff --git a/libraries/asmjit/asmjit/base/func.h b/libraries/asmjit/asmjit/base/func.h new file mode 100644 index 00000000000..c9ab0529d2f --- /dev/null +++ b/libraries/asmjit/asmjit/base/func.h @@ -0,0 +1,1296 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Guard] +#ifndef _ASMJIT_BASE_FUNC_H +#define _ASMJIT_BASE_FUNC_H + +#include "../asmjit_build.h" + +// [Dependencies] +#include "../base/arch.h" +#include "../base/operand.h" +#include "../base/utils.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +//! \addtogroup asmjit_base +//! \{ + +// ============================================================================ +// [Forward Declarations] +// ============================================================================ + +class CodeEmitter; + +// ============================================================================ +// [asmjit::CallConv] +// ============================================================================ + +//! Function calling convention. +//! +//! Function calling convention is a scheme that defines how function parameters +//! are passed and how function returns its result. AsmJit defines a variety of +//! architecture and OS specific calling conventions and also provides a compile +//! time detection to make JIT code-generation easier. +struct CallConv { + //! Calling convention id. + ASMJIT_ENUM(Id) { + //! None or invalid (can't be used). + kIdNone = 0, + + // ------------------------------------------------------------------------ + // [Universal] + // ------------------------------------------------------------------------ + + // TODO: To make this possible we need to know target ARCH and ABI. + + /* + + // Universal calling conventions are applicable to any target and are + // converted to target dependent conventions at runtime. The purpose of + // these conventions is to make using functions less target dependent. + + kIdCDecl = 1, + kIdStdCall = 2, + kIdFastCall = 3, + + //! AsmJit specific calling convention designed for calling functions + //! inside a multimedia code like that don't use many registers internally, + //! but are long enough to be called and not inlined. These functions are + //! usually used to calculate trigonometric functions, logarithms, etc... + kIdFastEval2 = 10, + kIdFastEval3 = 11, + kIdFastEval4 = 12, + */ + + // ------------------------------------------------------------------------ + // [X86] + // ------------------------------------------------------------------------ + + //! X86 `__cdecl` calling convention (used by C runtime and libraries). + kIdX86CDecl = 16, + //! X86 `__stdcall` calling convention (used mostly by WinAPI). + kIdX86StdCall = 17, + //! X86 `__thiscall` calling convention (MSVC/Intel). + kIdX86MsThisCall = 18, + //! X86 `__fastcall` convention (MSVC/Intel). + kIdX86MsFastCall = 19, + //! X86 `__fastcall` convention (GCC and Clang). + kIdX86GccFastCall = 20, + //! X86 `regparm(1)` convention (GCC and Clang). + kIdX86GccRegParm1 = 21, + //! X86 `regparm(2)` convention (GCC and Clang). + kIdX86GccRegParm2 = 22, + //! X86 `regparm(3)` convention (GCC and Clang). + kIdX86GccRegParm3 = 23, + + kIdX86FastEval2 = 29, + kIdX86FastEval3 = 30, + kIdX86FastEval4 = 31, + + //! X64 calling convention defined by WIN64-ABI. + //! + //! Links: + //! * . + kIdX86Win64 = 32, + //! X64 calling convention used by Unix platforms (SYSV/AMD64-ABI). + kIdX86SysV64 = 33, + + kIdX64FastEval2 = 45, + kIdX64FastEval3 = 46, + kIdX64FastEval4 = 47, + + // ------------------------------------------------------------------------ + // [ARM] + // ------------------------------------------------------------------------ + + //! Legacy calling convention, floating point arguments are passed via GP registers. + kIdArm32SoftFP = 48, + //! Modern calling convention, uses VFP registers to pass floating point arguments. + kIdArm32HardFP = 49, + + // ------------------------------------------------------------------------ + // [Internal] + // ------------------------------------------------------------------------ + + _kIdX86Start = 16, //!< \internal + _kIdX86End = 31, //!< \internal + + _kIdX64Start = 32, //!< \internal + _kIdX64End = 47, //!< \internal + + _kIdArmStart = 48, //!< \internal + _kIdArmEnd = 49, //!< \internal + + // ------------------------------------------------------------------------ + // [Host] + // ------------------------------------------------------------------------ + +#if defined(ASMJIT_DOCGEN) + //! Default calling convention based on the current C++ compiler's settings. + //! + //! NOTE: This should be always the same as `kIdHostCDecl`, but some + //! compilers allow to override the default calling convention. Overriding + //! is not detected at the moment. + kIdHost = DETECTED_AT_COMPILE_TIME, + + //! Default CDECL calling convention based on the current C++ compiler's settings. + kIdHostCDecl = DETECTED_AT_COMPILE_TIME, + + //! Default STDCALL calling convention based on the current C++ compiler's settings. + //! + //! NOTE: If not defined by the host then it's the same as `kIdHostCDecl`. + kIdHostStdCall = DETECTED_AT_COMPILE_TIME, + + //! Compatibility for `__fastcall` calling convention. + //! + //! NOTE: If not defined by the host then it's the same as `kIdHostCDecl`. + kIdHostFastCall = DETECTED_AT_COMPILE_TIME +#elif ASMJIT_ARCH_X86 + kIdHost = kIdX86CDecl, + kIdHostCDecl = kIdX86CDecl, + kIdHostStdCall = kIdX86StdCall, + kIdHostFastCall = ASMJIT_CC_MSC ? kIdX86MsFastCall : + ASMJIT_CC_GCC ? kIdX86GccFastCall : + ASMJIT_CC_CLANG ? kIdX86GccFastCall : kIdNone, + kIdHostFastEval2 = kIdX86FastEval2, + kIdHostFastEval3 = kIdX86FastEval3, + kIdHostFastEval4 = kIdX86FastEval4 +#elif ASMJIT_ARCH_X64 + kIdHost = ASMJIT_OS_WINDOWS ? kIdX86Win64 : kIdX86SysV64, + kIdHostCDecl = kIdHost, // Doesn't exist, redirected to host. + kIdHostStdCall = kIdHost, // Doesn't exist, redirected to host. + kIdHostFastCall = kIdHost, // Doesn't exist, redirected to host. + kIdHostFastEval2 = kIdX64FastEval2, + kIdHostFastEval3 = kIdX64FastEval3, + kIdHostFastEval4 = kIdX64FastEval4 +#elif ASMJIT_ARCH_ARM32 +# if defined(__SOFTFP__) + kIdHost = kIdArm32SoftFP, +# else + kIdHost = kIdArm32HardFP, +# endif + // These don't exist on ARM. + kIdHostCDecl = kIdHost, // Doesn't exist, redirected to host. + kIdHostStdCall = kIdHost, // Doesn't exist, redirected to host. + kIdHostFastCall = kIdHost // Doesn't exist, redirected to host. +#else +# error "[asmjit] Couldn't determine the target's calling convention." +#endif + }; + + //! Calling convention algorithm. + //! + //! This is AsmJit specific. It basically describes how should AsmJit convert + //! the function arguments defined by `FuncSignature` into register ids or + //! stack offsets. The default algorithm is a standard algorithm that assigns + //! registers first, and then assigns stack. The Win64 algorithm does register + //! shadowing as defined by `WIN64` calling convention - it applies to 64-bit + //! calling conventions only. + ASMJIT_ENUM(Algorithm) { + kAlgorithmDefault = 0, //!< Default algorithm (cross-platform). + kAlgorithmWin64 = 1 //!< WIN64 specific algorithm. + }; + + //! Calling convention flags. + ASMJIT_ENUM(Flags) { + kFlagCalleePopsStack = 0x01, //!< Callee is responsible for cleaning up the stack. + kFlagPassFloatsByVec = 0x02, //!< Pass F32 and F64 arguments by VEC128 register. + kFlagVectorCall = 0x04, //!< This is a '__vectorcall' calling convention. + kFlagIndirectVecArgs = 0x08 //!< Pass vector arguments indirectly (as a pointer). + }; + + //! Internal limits of AsmJit/CallConv. + ASMJIT_ENUM(Limits) { + kMaxVRegKinds = Globals::kMaxVRegKinds, + kNumRegArgsPerKind = 8 + }; + + //! Passed registers' order. + union RegOrder { + uint8_t id[kNumRegArgsPerKind]; //!< Passed registers, ordered. + uint32_t packed[(kNumRegArgsPerKind + 3) / 4]; + }; + + // -------------------------------------------------------------------------- + // [Utilities] + // -------------------------------------------------------------------------- + + static ASMJIT_INLINE bool isX86Family(uint32_t ccId) noexcept { return ccId >= _kIdX86Start && ccId <= _kIdX64End; } + static ASMJIT_INLINE bool isArmFamily(uint32_t ccId) noexcept { return ccId >= _kIdArmStart && ccId <= _kIdArmEnd; } + + // -------------------------------------------------------------------------- + // [Init / Reset] + // -------------------------------------------------------------------------- + + ASMJIT_API Error init(uint32_t ccId) noexcept; + + ASMJIT_INLINE void reset() noexcept { + ::memset(this, 0, sizeof(*this)); + ::memset(_passedOrder, 0xFF, sizeof(_passedOrder)); + } + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get calling convention id, see \ref Id. + ASMJIT_INLINE uint32_t getId() const noexcept { return _id; } + //! Set calling convention id, see \ref Id. + ASMJIT_INLINE void setId(uint32_t id) noexcept { _id = static_cast(id); } + + //! Get architecture type. + ASMJIT_INLINE uint32_t getArchType() const noexcept { return _archType; } + //! Set architecture type. + ASMJIT_INLINE void setArchType(uint32_t archType) noexcept { _archType = static_cast(archType); } + + //! Get calling convention algorithm, see \ref Algorithm. + ASMJIT_INLINE uint32_t getAlgorithm() const noexcept { return _algorithm; } + //! Set calling convention algorithm, see \ref Algorithm. + ASMJIT_INLINE void setAlgorithm(uint32_t algorithm) noexcept { _algorithm = static_cast(algorithm); } + + //! Get if the calling convention has the given `flag` set. + ASMJIT_INLINE bool hasFlag(uint32_t flag) const noexcept { return (_flags & flag) != 0; } + //! Get calling convention flags, see \ref Flags. + ASMJIT_INLINE uint32_t getFlags() const noexcept { return _flags; } + //! Add calling convention flags, see \ref Flags. + ASMJIT_INLINE void setFlags(uint32_t flag) noexcept { _flags = flag; }; + //! Add calling convention flags, see \ref Flags. + ASMJIT_INLINE void addFlags(uint32_t flag) noexcept { _flags |= flag; }; + + //! Get a natural stack alignment. + ASMJIT_INLINE uint32_t getNaturalStackAlignment() const noexcept { return _naturalStackAlignment; } + + //! Set a natural stack alignment. + //! + //! This function can be used to override the default stack alignment in case + //! that you know that it's alignment is different. For example it allows to + //! implement custom calling conventions that guarantee higher stack alignment. + ASMJIT_INLINE void setNaturalStackAlignment(uint32_t value) noexcept { + ASMJIT_ASSERT(value < 256); + _naturalStackAlignment = static_cast(value); + } + + //! Get if this calling convention specifies 'SpillZone'. + ASMJIT_INLINE bool hasSpillZone() const noexcept { return _spillZoneSize != 0; } + //! Get size of 'SpillZone'. + ASMJIT_INLINE uint32_t getSpillZoneSize() const noexcept { return _spillZoneSize; } + //! Set size of 'SpillZone'. + ASMJIT_INLINE void setSpillZoneSize(uint32_t size) noexcept { _spillZoneSize = static_cast(size); } + + //! Get if this calling convention specifies 'RedZone'. + ASMJIT_INLINE bool hasRedZone() const noexcept { return _redZoneSize != 0; } + //! Get size of 'RedZone'. + ASMJIT_INLINE uint32_t getRedZoneSize() const noexcept { return _redZoneSize; } + //! Set size of 'RedZone'. + ASMJIT_INLINE void setRedZoneSize(uint32_t size) noexcept { _redZoneSize = static_cast(size); } + + ASMJIT_INLINE const uint8_t* getPassedOrder(uint32_t kind) const noexcept { + ASMJIT_ASSERT(kind < kMaxVRegKinds); + return _passedOrder[kind].id; + } + + ASMJIT_INLINE uint32_t getPassedRegs(uint32_t kind) const noexcept { + ASMJIT_ASSERT(kind < kMaxVRegKinds); + return _passedRegs[kind]; + } + + ASMJIT_INLINE void _setPassedPacked(uint32_t kind, uint32_t p0, uint32_t p1) noexcept { + ASMJIT_ASSERT(kind < kMaxVRegKinds); + + _passedOrder[kind].packed[0] = p0; + _passedOrder[kind].packed[1] = p1; + } + + ASMJIT_INLINE void setPassedToNone(uint32_t kind) noexcept { + ASMJIT_ASSERT(kind < kMaxVRegKinds); + + _setPassedPacked(kind, ASMJIT_PACK32_4x8(0xFF, 0xFF, 0xFF, 0xFF), + ASMJIT_PACK32_4x8(0xFF, 0xFF, 0xFF, 0xFF)); + _passedRegs[kind] = 0; + } + + ASMJIT_INLINE void setPassedOrder(uint32_t kind, uint32_t a0, uint32_t a1 = 0xFF, uint32_t a2 = 0xFF, uint32_t a3 = 0xFF, uint32_t a4 = 0xFF, uint32_t a5 = 0xFF, uint32_t a6 = 0xFF, uint32_t a7 = 0xFF) noexcept { + ASMJIT_ASSERT(kind < kMaxVRegKinds); + + _setPassedPacked(kind, ASMJIT_PACK32_4x8(a0, a1, a2, a3), + ASMJIT_PACK32_4x8(a4, a5, a6, a7)); + + // NOTE: This should always be called with all arguments known at compile + // time, so even if it looks scary it should be translated to a single + // instruction. + _passedRegs[kind] = (a0 != 0xFF ? 1U << a0 : 0U) | + (a1 != 0xFF ? 1U << a1 : 0U) | + (a2 != 0xFF ? 1U << a2 : 0U) | + (a3 != 0xFF ? 1U << a3 : 0U) | + (a4 != 0xFF ? 1U << a4 : 0U) | + (a5 != 0xFF ? 1U << a5 : 0U) | + (a6 != 0xFF ? 1U << a6 : 0U) | + (a7 != 0xFF ? 1U << a7 : 0U) ; + } + + ASMJIT_INLINE uint32_t getPreservedRegs(uint32_t kind) const noexcept { + ASMJIT_ASSERT(kind < kMaxVRegKinds); + return _preservedRegs[kind]; + } + + + ASMJIT_INLINE void setPreservedRegs(uint32_t kind, uint32_t regs) noexcept { + ASMJIT_ASSERT(kind < kMaxVRegKinds); + _preservedRegs[kind] = regs; + } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + uint8_t _id; //!< Calling convention id, see \ref Id. + uint8_t _archType; //!< Architecture type (see \ref ArchInfo::Type). + uint8_t _algorithm; //!< Calling convention algorithm. + uint8_t _flags; //!< Calling convention flags. + + uint8_t _naturalStackAlignment; //!< Natural stack alignment as defined by OS/ABI. + uint8_t _spillZoneSize; //!< Spill zone size (WIN64 == 32 bytes). + uint16_t _redZoneSize; //!< Red zone size (AMD64 == 128 bytes). + + RegOrder _passedOrder[kMaxVRegKinds]; //!< Passed registers' order, per kind. + uint32_t _passedRegs[kMaxVRegKinds]; //!< Mask of all passed registers, per kind. + uint32_t _preservedRegs[kMaxVRegKinds];//!< Mask of all preserved registers, per kind. +}; + +// ============================================================================ +// [asmjit::FuncArgIndex] +// ============================================================================ + +//! Function argument index (lo/hi). +ASMJIT_ENUM(FuncArgIndex) { + //! Maximum number of function arguments supported by AsmJit. + kFuncArgCount = 16, + //! Extended maximum number of arguments (used internally). + kFuncArgCountLoHi = kFuncArgCount * 2, + + //! Index to the LO part of function argument (default). + //! + //! This value is typically omitted and added only if there is HI argument + //! accessed. + kFuncArgLo = 0, + + //! Index to the HI part of function argument. + //! + //! HI part of function argument depends on target architecture. On x86 it's + //! typically used to transfer 64-bit integers (they form a pair of 32-bit + //! integers). + kFuncArgHi = kFuncArgCount +}; + +// ============================================================================ +// [asmjit::FuncSignature] +// ============================================================================ + +//! Function signature. +//! +//! Contains information about function return type, count of arguments and +//! their TypeIds. Function signature is a low level structure which doesn't +//! contain platform specific or calling convention specific information. +struct FuncSignature { + enum { + //! Doesn't have variable number of arguments (`...`). + kNoVarArgs = 0xFF + }; + + // -------------------------------------------------------------------------- + // [Init / Reset] + // -------------------------------------------------------------------------- + + //! Initialize the function signature. + ASMJIT_INLINE void init(uint32_t ccId, uint32_t ret, const uint8_t* args, uint32_t argCount) noexcept { + ASMJIT_ASSERT(ccId <= 0xFF); + ASMJIT_ASSERT(argCount <= 0xFF); + + _callConv = static_cast(ccId); + _argCount = static_cast(argCount); + _vaIndex = kNoVarArgs; + _ret = ret; + _args = args; + } + + ASMJIT_INLINE void reset() noexcept { + memset(this, 0, sizeof(*this)); + } + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get the function's calling convention. + ASMJIT_INLINE uint32_t getCallConv() const noexcept { return _callConv; } + + //! Get if the function has variable number of arguments (...). + ASMJIT_INLINE bool hasVarArgs() const noexcept { return _vaIndex != kNoVarArgs; } + //! Get the variable arguments (...) index, `kNoVarArgs` if none. + ASMJIT_INLINE uint32_t getVAIndex() const noexcept { return _vaIndex; } + + //! Get the number of function arguments. + ASMJIT_INLINE uint32_t getArgCount() const noexcept { return _argCount; } + + ASMJIT_INLINE bool hasRet() const noexcept { return _ret != TypeId::kVoid; } + //! Get the return value type. + ASMJIT_INLINE uint32_t getRet() const noexcept { return _ret; } + + //! Get the type of the argument at index `i`. + ASMJIT_INLINE uint32_t getArg(uint32_t i) const noexcept { + ASMJIT_ASSERT(i < _argCount); + return _args[i]; + } + //! Get the array of function arguments' types. + ASMJIT_INLINE const uint8_t* getArgs() const noexcept { return _args; } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + uint8_t _callConv; //!< Calling convention id. + uint8_t _argCount; //!< Count of arguments. + uint8_t _vaIndex; //!< Index to a first vararg or `kNoVarArgs`. + uint8_t _ret; //!< TypeId of a return value. + const uint8_t* _args; //!< TypeIds of function arguments. +}; + +// ============================================================================ +// [asmjit::FuncSignatureT] +// ============================================================================ + +//! \internal +#define T(TYPE) TypeIdOf::kTypeId + +//! Static function signature (no arguments). +template +class FuncSignature0 : public FuncSignature { +public: + ASMJIT_INLINE FuncSignature0(uint32_t ccId = CallConv::kIdHost) noexcept { + init(ccId, T(RET), nullptr, 0); + } +}; + +//! Static function signature (1 argument). +template +class FuncSignature1 : public FuncSignature { +public: + ASMJIT_INLINE FuncSignature1(uint32_t ccId = CallConv::kIdHost) noexcept { + static const uint8_t args[] = { T(A0) }; + init(ccId, T(RET), args, ASMJIT_ARRAY_SIZE(args)); + } +}; + +//! Static function signature (2 arguments). +template +class FuncSignature2 : public FuncSignature { +public: + ASMJIT_INLINE FuncSignature2(uint32_t ccId = CallConv::kIdHost) noexcept { + static const uint8_t args[] = { T(A0), T(A1) }; + init(ccId, T(RET), args, ASMJIT_ARRAY_SIZE(args)); + } +}; + +//! Static function signature (3 arguments). +template +class FuncSignature3 : public FuncSignature { +public: + ASMJIT_INLINE FuncSignature3(uint32_t ccId = CallConv::kIdHost) noexcept { + static const uint8_t args[] = { T(A0), T(A1), T(A2) }; + init(ccId, T(RET), args, ASMJIT_ARRAY_SIZE(args)); + } +}; + +//! Static function signature (4 arguments). +template +class FuncSignature4 : public FuncSignature { +public: + ASMJIT_INLINE FuncSignature4(uint32_t ccId = CallConv::kIdHost) noexcept { + static const uint8_t args[] = { T(A0), T(A1), T(A2), T(A3) }; + init(ccId, T(RET), args, ASMJIT_ARRAY_SIZE(args)); + } +}; + +//! Static function signature (5 arguments). +template +class FuncSignature5 : public FuncSignature { +public: + ASMJIT_INLINE FuncSignature5(uint32_t ccId = CallConv::kIdHost) noexcept { + static const uint8_t args[] = { T(A0), T(A1), T(A2), T(A3), T(A4) }; + init(ccId, T(RET), args, ASMJIT_ARRAY_SIZE(args)); + } +}; + +//! Static function signature (6 arguments). +template +class FuncSignature6 : public FuncSignature { +public: + ASMJIT_INLINE FuncSignature6(uint32_t ccId = CallConv::kIdHost) noexcept { + static const uint8_t args[] = { T(A0), T(A1), T(A2), T(A3), T(A4), T(A5) }; + init(ccId, T(RET), args, ASMJIT_ARRAY_SIZE(args)); + } +}; + +//! Static function signature (7 arguments). +template +class FuncSignature7 : public FuncSignature { +public: + ASMJIT_INLINE FuncSignature7(uint32_t ccId = CallConv::kIdHost) noexcept { + static const uint8_t args[] = { T(A0), T(A1), T(A2), T(A3), T(A4), T(A5), T(A6) }; + init(ccId, T(RET), args, ASMJIT_ARRAY_SIZE(args)); + } +}; + +//! Static function signature (8 arguments). +template +class FuncSignature8 : public FuncSignature { +public: + ASMJIT_INLINE FuncSignature8(uint32_t ccId = CallConv::kIdHost) noexcept { + static const uint8_t args[] = { T(A0), T(A1), T(A2), T(A3), T(A4), T(A5), T(A6), T(A7) }; + init(ccId, T(RET), args, ASMJIT_ARRAY_SIZE(args)); + } +}; + +//! Static function signature (9 arguments). +template +class FuncSignature9 : public FuncSignature { +public: + ASMJIT_INLINE FuncSignature9(uint32_t ccId = CallConv::kIdHost) noexcept { + static const uint8_t args[] = { T(A0), T(A1), T(A2), T(A3), T(A4), T(A5), T(A6), T(A7), T(A8) }; + init(ccId, T(RET), args, ASMJIT_ARRAY_SIZE(args)); + } +}; + +//! Static function signature (10 arguments). +template +class FuncSignature10 : public FuncSignature { +public: + ASMJIT_INLINE FuncSignature10(uint32_t ccId = CallConv::kIdHost) noexcept { + static const uint8_t args[] = { T(A0), T(A1), T(A2), T(A3), T(A4), T(A5), T(A6), T(A7), T(A8), T(A9) }; + init(ccId, T(RET), args, ASMJIT_ARRAY_SIZE(args)); + } +}; + +#if ASMJIT_CC_HAS_VARIADIC_TEMPLATES +//! Static function signature (variadic). +template +class FuncSignatureT : public FuncSignature { +public: + ASMJIT_INLINE FuncSignatureT(uint32_t ccId = CallConv::kIdHost) noexcept { + static const uint8_t args[] = { (T(ARGS))... }; + init(ccId, T(RET), args, ASMJIT_ARRAY_SIZE(args)); + } +}; +#endif // ASMJIT_CC_HAS_VARIADIC_TEMPLATES + +#undef T + +// ============================================================================ +// [asmjit::FuncSignatureX] +// ============================================================================ + +//! Dynamic function signature. +class FuncSignatureX : public FuncSignature { +public: + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE FuncSignatureX(uint32_t ccId = CallConv::kIdHost) noexcept { + init(ccId, TypeId::kVoid, _builderArgList, 0); + } + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE void setCallConv(uint32_t ccId) noexcept { + ASMJIT_ASSERT(ccId <= 0xFF); + _callConv = static_cast(ccId); + } + + //! Set the return type to `retType`. + ASMJIT_INLINE void setRet(uint32_t retType) noexcept { _ret = retType; } + //! Set the return type based on `T`. + template + ASMJIT_INLINE void setRetT() noexcept { setRet(TypeIdOf::kTypeId); } + + //! Set the argument at index `i` to the `type` + ASMJIT_INLINE void setArg(uint32_t i, uint32_t type) noexcept { + ASMJIT_ASSERT(i < _argCount); + _builderArgList[i] = type; + } + //! Set the argument at index `i` to the type based on `T`. + template + ASMJIT_INLINE void setArgT(uint32_t i) noexcept { setArg(i, TypeIdOf::kTypeId); } + + //! Append an argument of `type` to the function prototype. + ASMJIT_INLINE void addArg(uint32_t type) noexcept { + ASMJIT_ASSERT(_argCount < kFuncArgCount); + _builderArgList[_argCount++] = static_cast(type); + } + //! Append an argument of type based on `T` to the function prototype. + template + ASMJIT_INLINE void addArgT() noexcept { addArg(TypeIdOf::kTypeId); } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + uint8_t _builderArgList[kFuncArgCount]; +}; + +// ============================================================================ +// [asmjit::FuncDetail] +// ============================================================================ + +//! Function detail - CallConv and expanded FuncSignature. +//! +//! Function details is architecture and OS dependent representation of function. +//! It contains calling convention and expanded function signature so all +//! arguments have assigned either register type & id or stack address. +class FuncDetail { +public: + ASMJIT_ENUM(Limits) { + kMaxVRegKinds = Globals::kMaxVRegKinds + }; + + //! Argument or return value as defined by `FuncSignature`, but with register + //! or stack address (and other metadata) assigned. + struct Value { + ASMJIT_ENUM(Parts) { + kTypeIdShift = 24, + kTypeIdMask = 0xFF000000U, + + kRegTypeShift = 8, + kRegTypeMask = 0x0000FF00U, + + kRegIdShift = 0, + kRegIdMask = 0x000000FFU, + + kStackOffsetShift = 0, + kStackOffsetMask = 0x0000FFFFU, + + kIsByReg = 0x00010000U, + kIsByStack = 0x00020000U, + kIsIndirect = 0x00040000U + }; + + //! Get if this value is initialized (i.e. contains a valid data). + ASMJIT_INLINE bool isInitialized() const noexcept { return _value != 0; } + //! Initialize this in/out by a given `typeId`. + ASMJIT_INLINE void initTypeId(uint32_t typeId) noexcept { _value = typeId << kTypeIdShift; } + //! Initialize this in/out by a given `typeId`, `regType`, and `regId`. + ASMJIT_INLINE void initReg(uint32_t typeId, uint32_t regType, uint32_t regId) noexcept { + _value = (typeId << kTypeIdShift) | (regType << kRegTypeShift) | (regId << kRegIdShift) | kIsByReg; + } + //! Initialize this in/out by a given `typeId` and `offset`. + ASMJIT_INLINE void initStack(uint32_t typeId, uint32_t stackOffset) noexcept { + _value = (typeId << kTypeIdShift) | (stackOffset << kStackOffsetShift) | kIsByStack; + } + //! Reset the value to its uninitialized and unassigned state. + ASMJIT_INLINE void reset() noexcept { _value = 0; } + + ASMJIT_INLINE void assignToReg(uint32_t regType, uint32_t regId) noexcept { + ASMJIT_ASSERT(!isAssigned()); + _value |= (regType << kRegTypeShift) | (regId << kRegIdShift) | kIsByReg; + } + + ASMJIT_INLINE void assignToStack(int32_t offset) noexcept { + ASMJIT_ASSERT(!isAssigned()); + _value |= (offset << kStackOffsetShift) | kIsByStack; + } + + //! Get if this argument is passed by register. + ASMJIT_INLINE bool byReg() const noexcept { return (_value & kIsByReg) != 0; } + //! Get if this argument is passed by stack. + ASMJIT_INLINE bool byStack() const noexcept { return (_value & kIsByStack) != 0; } + //! Get if this argument is passed by register. + ASMJIT_INLINE bool isAssigned() const noexcept { return (_value & (kIsByReg | kIsByStack)) != 0; } + //! Get if this argument is passed through a pointer (used by WIN64 to pass XMM|YMM|ZMM). + ASMJIT_INLINE bool isIndirect() const noexcept { return (_value & kIsIndirect) != 0; } + + //! Get virtual type of this argument or return value. + ASMJIT_INLINE uint32_t getTypeId() const noexcept { return _value >> kTypeIdShift; } + //! Get a register type of the register used to pass the argument or return the value. + ASMJIT_INLINE uint32_t getRegType() const noexcept { return (_value & kRegTypeMask) >> kRegTypeShift; } + //! Get a physical id of the register used to pass the argument or return the value. + ASMJIT_INLINE uint32_t getRegId() const noexcept { return (_value & kRegIdMask) >> kRegIdShift; } + //! Get a stack offset of this argument (always positive). + ASMJIT_INLINE int32_t getStackOffset() const noexcept { return (_value & kStackOffsetMask) >> kStackOffsetShift; } + + uint32_t _value; + }; + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE FuncDetail() noexcept { reset(); } + ASMJIT_INLINE FuncDetail(const FuncDetail& other) noexcept { + ::memcpy(this, &other, sizeof(*this)); + } + + // -------------------------------------------------------------------------- + // [Init / Reset] + // -------------------------------------------------------------------------- + + //! Initialize this `FuncDetail` to the given signature. + ASMJIT_API Error init(const FuncSignature& sign); + ASMJIT_INLINE void reset() noexcept { ::memset(this, 0, sizeof(*this)); } + + // -------------------------------------------------------------------------- + // [Accessors - Calling Convention] + // -------------------------------------------------------------------------- + + //! Get the function's calling convention, see `CallConv`. + ASMJIT_INLINE const CallConv& getCallConv() const noexcept { return _callConv; } + + //! Get CallConv flags, see \ref CallConv::Flags. + ASMJIT_INLINE uint32_t getFlags() const noexcept { return _callConv.getFlags(); } + //! Check if a CallConv `flag` is set, see \ref CallConv::Flags. + ASMJIT_INLINE bool hasFlag(uint32_t ccFlag) const noexcept { return _callConv.hasFlag(ccFlag); } + + // -------------------------------------------------------------------------- + // [Accessors - Arguments and Return] + // -------------------------------------------------------------------------- + + //! Get count of function return values. + ASMJIT_INLINE uint32_t getRetCount() const noexcept { return _retCount; } + //! Get the number of function arguments. + ASMJIT_INLINE uint32_t getArgCount() const noexcept { return _argCount; } + + //! Get whether the function has a return value. + ASMJIT_INLINE bool hasRet() const noexcept { return _retCount != 0; } + //! Get function return value. + ASMJIT_INLINE Value& getRet(size_t index = 0) noexcept { + ASMJIT_ASSERT(index < ASMJIT_ARRAY_SIZE(_rets)); + return _rets[index]; + } + //! Get function return value (const). + ASMJIT_INLINE const Value& getRet(size_t index = 0) const noexcept { + ASMJIT_ASSERT(index < ASMJIT_ARRAY_SIZE(_rets)); + return _rets[index]; + } + + //! Get function arguments array. + ASMJIT_INLINE Value* getArgs() noexcept { return _args; } + //! Get function arguments array (const). + ASMJIT_INLINE const Value* getArgs() const noexcept { return _args; } + + ASMJIT_INLINE bool hasArg(size_t index) const noexcept { + ASMJIT_ASSERT(index < kFuncArgCountLoHi); + return _args[index].isInitialized(); + } + + //! Get function argument at index `index`. + ASMJIT_INLINE Value& getArg(size_t index) noexcept { + ASMJIT_ASSERT(index < kFuncArgCountLoHi); + return _args[index]; + } + + //! Get function argument at index `index`. + ASMJIT_INLINE const Value& getArg(size_t index) const noexcept { + ASMJIT_ASSERT(index < kFuncArgCountLoHi); + return _args[index]; + } + + ASMJIT_INLINE void resetArg(size_t index) noexcept { + ASMJIT_ASSERT(index < kFuncArgCountLoHi); + _args[index].reset(); + } + + //! Get if the function passes one or more argument by stack. + ASMJIT_INLINE bool hasStackArgs() const noexcept { return _argStackSize != 0; } + //! Get stack size needed for function arguments passed on the stack. + ASMJIT_INLINE uint32_t getArgStackSize() const noexcept { return _argStackSize; } + + ASMJIT_INLINE uint32_t getNaturalStackAlignment() const noexcept { return _callConv.getNaturalStackAlignment(); } + ASMJIT_INLINE uint32_t getSpillZoneSize() const noexcept { return _callConv.getSpillZoneSize(); } + ASMJIT_INLINE uint32_t getRedZoneSize() const noexcept { return _callConv.getRedZoneSize(); } + + ASMJIT_INLINE uint32_t getPassedRegs(uint32_t kind) const noexcept { return _callConv.getPassedRegs(kind); } + ASMJIT_INLINE uint32_t getPreservedRegs(uint32_t kind) const noexcept { return _callConv.getPreservedRegs(kind); } + + ASMJIT_INLINE uint32_t getUsedRegs(uint32_t kind) const noexcept { + ASMJIT_ASSERT(kind < kMaxVRegKinds); + return _usedRegs[kind]; + } + + ASMJIT_INLINE void addUsedRegs(uint32_t kind, uint32_t regs) noexcept { + ASMJIT_ASSERT(kind < kMaxVRegKinds); + _usedRegs[kind] |= regs; + } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + CallConv _callConv; //!< Calling convention. + uint8_t _argCount; //!< Number of function arguments. + uint8_t _retCount; //!< Number of function return values. + uint32_t _usedRegs[kMaxVRegKinds]; //!< Registers that contains arguments (signature dependent). + uint32_t _argStackSize; //!< Size of arguments passed by stack. + Value _rets[2]; //!< Function return values. + Value _args[kFuncArgCountLoHi]; //!< Function arguments. +}; + +// ============================================================================ +// [asmjit::FuncFrameInfo] +// ============================================================================ + +//! Function-frame information. +//! +//! This structure can be used to create a function frame in a cross-platform +//! way. It contains information about the function's stack to be used and +//! registers to be saved and restored. Based on this information in can +//! calculate the optimal layout of a function as \ref FuncFrameLayout. +struct FuncFrameInfo { + ASMJIT_ENUM(Limits) { + kMaxVRegKinds = Globals::kMaxVRegKinds + }; + + //! Attributes. + //! + //! Attributes are designed in a way that all are initially false, and user + //! or function-frame finalizer sets them when necessary. Architecture-specific + //! attributes are prefixed with the architecture name. + ASMJIT_ENUM(Attributes) { + kAttrPreserveFP = 0x00000001U, //!< Preserve frame pointer (EBP|RBP). + kAttrCompactPE = 0x00000002U, //!< Use smaller, but possibly slower prolog/epilog. + kAttrHasCalls = 0x00000004U, //!< Function calls other functions (is not leaf). + + kX86AttrAlignedVecSR = 0x00010000U, //!< Use aligned save/restore of VEC regs. + kX86AttrMmxCleanup = 0x00020000U, //!< Emit EMMS instruction in epilog (X86). + kX86AttrAvxCleanup = 0x00040000U, //!< Emit VZEROUPPER instruction in epilog (X86). + kX86AttrAvxEnabled = 0x00080000U //!< Use AVX instead of SSE for all operations (X86). + }; + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE FuncFrameInfo() noexcept { reset(); } + + ASMJIT_INLINE FuncFrameInfo(const FuncFrameInfo& other) noexcept { + ::memcpy(this, &other, sizeof(*this)); + } + + // -------------------------------------------------------------------------- + // [Init / Reset] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE void reset() noexcept { + ::memset(this, 0, sizeof(*this)); + _stackArgsRegId = Globals::kInvalidRegId; + } + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get frame-info flags, see \ref Attributes. + ASMJIT_INLINE uint32_t getAttributes() const noexcept { return _attributes; } + //! Check if a frame-info `flag` is set, see \ref Attributes. + ASMJIT_INLINE bool hasAttribute(uint32_t attr) const noexcept { return (_attributes & attr) != 0; } + //! Add `flags` to the frame-info, see \ref Attributes. + ASMJIT_INLINE void addAttributes(uint32_t attrs) noexcept { _attributes |= attrs; } + //! Clear `flags` from the frame-info, see \ref Attributes. + ASMJIT_INLINE void clearAttributes(uint32_t attrs) noexcept { _attributes &= ~attrs; } + + //! Get if the function preserves frame pointer (EBP|ESP on X86). + ASMJIT_INLINE bool hasPreservedFP() const noexcept { return (_attributes & kAttrPreserveFP) != 0; } + //! Enable preserved frame pointer. + ASMJIT_INLINE void enablePreservedFP() noexcept { _attributes |= kAttrPreserveFP; } + //! Disable preserved frame pointer. + ASMJIT_INLINE void disablePreservedFP() noexcept { _attributes &= ~kAttrPreserveFP; } + + //! Get if the function prolog and epilog should be compacted (as small as possible). + ASMJIT_INLINE bool hasCompactPE() const noexcept { return (_attributes & kAttrCompactPE) != 0; } + //! Enable compact prolog/epilog. + ASMJIT_INLINE void enableCompactPE() noexcept { _attributes |= kAttrCompactPE; } + //! Disable compact prolog/epilog. + ASMJIT_INLINE void disableCompactPE() noexcept { _attributes &= ~kAttrCompactPE; } + + //! Get if the function calls other functions. + ASMJIT_INLINE bool hasCalls() const noexcept { return (_attributes & kAttrHasCalls) != 0; } + //! Set `kFlagHasCalls` to true. + ASMJIT_INLINE void enableCalls() noexcept { _attributes |= kAttrHasCalls; } + //! Set `kFlagHasCalls` to false. + ASMJIT_INLINE void disableCalls() noexcept { _attributes &= ~kAttrHasCalls; } + + //! Get if the function contains MMX cleanup - 'emms' instruction in epilog. + ASMJIT_INLINE bool hasMmxCleanup() const noexcept { return (_attributes & kX86AttrMmxCleanup) != 0; } + //! Enable MMX cleanup. + ASMJIT_INLINE void enableMmxCleanup() noexcept { _attributes |= kX86AttrMmxCleanup; } + //! Disable MMX cleanup. + ASMJIT_INLINE void disableMmxCleanup() noexcept { _attributes &= ~kX86AttrMmxCleanup; } + + //! Get if the function contains AVX cleanup - 'vzeroupper' instruction in epilog. + ASMJIT_INLINE bool hasAvxCleanup() const noexcept { return (_attributes & kX86AttrAvxCleanup) != 0; } + //! Enable AVX cleanup. + ASMJIT_INLINE void enableAvxCleanup() noexcept { _attributes |= kX86AttrAvxCleanup; } + //! Disable AVX cleanup. + ASMJIT_INLINE void disableAvxCleanup() noexcept { _attributes &= ~kX86AttrAvxCleanup; } + + //! Get if the function contains AVX cleanup - 'vzeroupper' instruction in epilog. + ASMJIT_INLINE bool isAvxEnabled() const noexcept { return (_attributes & kX86AttrAvxEnabled) != 0; } + //! Enable AVX cleanup. + ASMJIT_INLINE void enableAvx() noexcept { _attributes |= kX86AttrAvxEnabled; } + //! Disable AVX cleanup. + ASMJIT_INLINE void disableAvx() noexcept { _attributes &= ~kX86AttrAvxEnabled; } + + //! Get which registers (by `kind`) are saved/restored in prolog/epilog, respectively. + ASMJIT_INLINE uint32_t getDirtyRegs(uint32_t kind) const noexcept { + ASMJIT_ASSERT(kind < kMaxVRegKinds); + return _dirtyRegs[kind]; + } + + //! Set which registers (by `kind`) are saved/restored in prolog/epilog, respectively. + ASMJIT_INLINE void setDirtyRegs(uint32_t kind, uint32_t regs) noexcept { + ASMJIT_ASSERT(kind < kMaxVRegKinds); + _dirtyRegs[kind] = regs; + } + + //! Add registers (by `kind`) to saved/restored registers. + ASMJIT_INLINE void addDirtyRegs(uint32_t kind, uint32_t regs) noexcept { + ASMJIT_ASSERT(kind < kMaxVRegKinds); + _dirtyRegs[kind] |= regs; + } + + ASMJIT_INLINE void setAllDirty() noexcept { + _dirtyRegs[0] = 0xFFFFFFFFU; + _dirtyRegs[1] = 0xFFFFFFFFU; + _dirtyRegs[2] = 0xFFFFFFFFU; + _dirtyRegs[3] = 0xFFFFFFFFU; + } + + ASMJIT_INLINE void setAllDirty(uint32_t kind) noexcept { + ASMJIT_ASSERT(kind < kMaxVRegKinds); + _dirtyRegs[kind] = 0xFFFFFFFFU; + } + + //! Get stack-frame size used by the function. + ASMJIT_INLINE uint32_t getStackFrameSize() const noexcept { return _stackFrameSize; } + //! Get call-frame size used by the function. + ASMJIT_INLINE uint32_t getCallFrameSize() const noexcept { return _callFrameSize; } + + //! Get minimum stack-frame alignment required by the function. + ASMJIT_INLINE uint32_t getStackFrameAlignment() const noexcept { return _stackFrameAlignment; } + //! Get minimum call-frame alignment required by the function. + ASMJIT_INLINE uint32_t getCallFrameAlignment() const noexcept { return _callFrameAlignment; } + + ASMJIT_INLINE void setStackFrameSize(uint32_t size) noexcept { _stackFrameSize = size; } + ASMJIT_INLINE void setCallFrameSize(uint32_t size) noexcept { _callFrameSize = size; } + + ASMJIT_INLINE void setStackFrameAlignment(uint32_t value) noexcept { + ASMJIT_ASSERT(value < 256); + _stackFrameAlignment = static_cast(value); + } + + ASMJIT_INLINE void setCallFrameAlignment(uint32_t value) noexcept { + ASMJIT_ASSERT(value < 256); + _callFrameAlignment = static_cast(value); + } + + ASMJIT_INLINE void mergeStackFrameSize(uint32_t size) noexcept { _stackFrameSize = std::max(_stackFrameSize, size); } + ASMJIT_INLINE void mergeCallFrameSize(uint32_t size) noexcept { _callFrameSize = std::max(_callFrameSize, size); } + + ASMJIT_INLINE void mergeStackFrameAlignment(uint32_t value) noexcept { + ASMJIT_ASSERT(value < 256); + _stackFrameAlignment = static_cast(std::max(_stackFrameAlignment, value)); + } + + ASMJIT_INLINE void mergeCallFrameAlignment(uint32_t value) noexcept { + ASMJIT_ASSERT(value < 256); + _callFrameAlignment = static_cast(std::max(_callFrameAlignment, value)); + } + + ASMJIT_INLINE bool hasStackArgsRegId() const noexcept { + return _stackArgsRegId != Globals::kInvalidRegId; + } + ASMJIT_INLINE uint32_t getStackArgsRegId() const noexcept { return _stackArgsRegId; } + ASMJIT_INLINE void setStackArgsRegId(uint32_t regId) { _stackArgsRegId = regId; } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + uint32_t _attributes; //!< Function attributes. + uint32_t _dirtyRegs[kMaxVRegKinds]; //!< Registers used by the function. + + uint8_t _stackFrameAlignment; //!< Minimum alignment of stack-frame. + uint8_t _callFrameAlignment; //!< Minimum alignment of call-frame. + uint8_t _stackArgsRegId; //!< Register that holds base-address to arguments passed by stack. + + uint32_t _stackFrameSize; //!< Size of a stack-frame used by the function. + uint32_t _callFrameSize; //!< Size of a call-frame (not part of _stackFrameSize). +}; + +// ============================================================================ +// [asmjit::FuncFrameLayout] +// ============================================================================ + +//! Function-frame layout. +//! +//! Function layout is used directly by prolog and epilog insertion helpers. It +//! contains only information necessary to insert proper prolog and epilog, and +//! should be always calculated from \ref FuncDetail and \ref FuncFrameInfo, where +//! \ref FuncDetail defines function's calling convention and signature, and \ref +//! FuncFrameInfo specifies how much stack is used, and which registers are dirty. +struct FuncFrameLayout { + ASMJIT_ENUM(Limits) { + kMaxVRegKinds = Globals::kMaxVRegKinds + }; + + // -------------------------------------------------------------------------- + // [Init / Reset] + // -------------------------------------------------------------------------- + + ASMJIT_API Error init(const FuncDetail& func, const FuncFrameInfo& ffi) noexcept; + ASMJIT_INLINE void reset() noexcept { ::memset(this, 0, sizeof(*this)); } + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE bool hasPreservedFP() const noexcept { return static_cast(_preservedFP); } + ASMJIT_INLINE bool hasDsaSlotUsed() const noexcept { return static_cast(_dsaSlotUsed); } + ASMJIT_INLINE bool hasAlignedVecSR() const noexcept { return static_cast(_alignedVecSR); } + ASMJIT_INLINE bool hasDynamicAlignment() const noexcept { return static_cast(_dynamicAlignment); } + + ASMJIT_INLINE bool hasMmxCleanup() const noexcept { return static_cast(_mmxCleanup); } + ASMJIT_INLINE bool hasAvxCleanup() const noexcept { return static_cast(_avxCleanup); } + ASMJIT_INLINE bool isAvxEnabled() const noexcept { return static_cast(_avxEnabled); } + + ASMJIT_INLINE uint32_t getSavedRegs(uint32_t kind) const noexcept { + ASMJIT_ASSERT(kind < kMaxVRegKinds); + return _savedRegs[kind]; + } + + //! Get stack size. + ASMJIT_INLINE uint32_t getStackSize() const noexcept { return _stackSize; } + //! Get stack alignment. + ASMJIT_INLINE uint32_t getStackAlignment() const noexcept { return _stackAlignment; } + //! Get the offset needed to access the function's stack (it skips call-stack). + ASMJIT_INLINE uint32_t getStackBaseOffset() const noexcept { return _stackBaseOffset; } + + //! Get stack size required to save GP registers. + ASMJIT_INLINE uint32_t getGpStackSize() const noexcept { return _gpStackSize; } + //! Get stack size required to save VEC registers. + ASMJIT_INLINE uint32_t getVecStackSize() const noexcept { return _vecStackSize; } + + ASMJIT_INLINE uint32_t getGpStackOffset() const noexcept { return _gpStackOffset; } + ASMJIT_INLINE uint32_t getVecStackOffset() const noexcept { return _vecStackOffset; } + + ASMJIT_INLINE uint32_t getStackArgsRegId() const noexcept { return _stackArgsRegId; } + ASMJIT_INLINE uint32_t getStackArgsOffset() const noexcept { return _stackArgsOffset; } + + ASMJIT_INLINE bool hasStackAdjustment() const noexcept { return _stackAdjustment != 0; } + ASMJIT_INLINE uint32_t getStackAdjustment() const noexcept { return _stackAdjustment; } + + ASMJIT_INLINE bool hasCalleeStackCleanup() const noexcept { return _calleeStackCleanup != 0; } + ASMJIT_INLINE uint32_t getCalleeStackCleanup() const noexcept { return _calleeStackCleanup; } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + uint8_t _stackAlignment; //!< Final stack alignment of the functions. + uint8_t _stackBaseRegId; //!< GP register that holds address of base stack address. + uint8_t _stackArgsRegId; //!< GP register that holds address of the first argument passed by stack. + + uint32_t _savedRegs[kMaxVRegKinds]; //!< Registers that will be saved/restored in prolog/epilog. + + uint32_t _preservedFP : 1; //!< Function preserves frame-pointer. + uint32_t _dsaSlotUsed : 1; //!< True if `_dsaSlot` contains a valid memory slot/offset. + uint32_t _alignedVecSR : 1; //!< Use instructions that perform aligned ops to save/restore XMM regs. + uint32_t _dynamicAlignment : 1; //!< Function must dynamically align the stack. + + uint32_t _mmxCleanup : 1; //!< Emit 'emms' in epilog (X86). + uint32_t _avxCleanup : 1; //!< Emit 'vzeroupper' in epilog (X86). + uint32_t _avxEnabled : 1; //!< Use AVX instead of SSE for SIMD saves/restores (X86). + + uint32_t _stackSize; //!< Stack size (sum of function's stack and call stack). + uint32_t _stackBaseOffset; //!< Stack offset (non-zero if kFlagHasCalls is set). + uint32_t _stackAdjustment; //!< Stack adjustment in prolog/epilog. + uint32_t _stackArgsOffset; //!< Offset to the first argument passed by stack of _stackArgsRegId. + + uint32_t _dsaSlot; //!< Memory slot where the prolog inserter stores previous (unaligned) ESP. + uint16_t _calleeStackCleanup; //!< How many bytes the callee should add to the stack (X86 STDCALL). + uint16_t _gpStackSize; //!< Stack size required to save GP regs. + uint16_t _vecStackSize; //!< Stack size required to save VEC regs. + uint32_t _gpStackOffset; //!< Offset where saved GP regs are stored. + uint32_t _vecStackOffset; //!< Offset where saved GP regs are stored. +}; + +// ============================================================================ +// [asmjit::FuncArgsMapper] +// ============================================================================ + +//! Assign a physical register to each function argument. +//! +//! This is used to specify where each function argument should be shuffled +//! or allocated (in case it's passed by stack). +class FuncArgsMapper { +public: + struct Value { + // NOTE: The layout is compatible with FuncDetail::Value except stack. + ASMJIT_ENUM(Parts) { + kTypeIdShift = 24, + kTypeIdMask = 0xFF000000U, + + kRegTypeShift = 8, + kRegTypeMask = 0x0000FF00U, + + kRegIdShift = 0, + kRegIdMask = 0x000000FFU, + + kIsAssigned = 0x00010000U + }; + + //! Get if this value is initialized (i.e. contains a valid data). + ASMJIT_INLINE bool isAssigned() const noexcept { return _value != 0; } + //! Initialize this in/out by a given `typeId`, `regType`, and `regId`. + ASMJIT_INLINE void assign(uint32_t typeId, uint32_t regType, uint32_t regId) noexcept { + _value = (typeId << kTypeIdShift) | (regType << kRegTypeShift) | (regId << kRegIdShift) | kIsAssigned; + } + //! Reset the value to its unassigned state. + ASMJIT_INLINE void reset() noexcept { _value = 0; } + + //! Get virtual type of this argument or return value. + ASMJIT_INLINE uint32_t getTypeId() const noexcept { return _value >> kTypeIdShift; } + //! Get a register type of the register used to pass the argument or return the value. + ASMJIT_INLINE uint32_t getRegType() const noexcept { return (_value & kRegTypeMask) >> kRegTypeShift; } + //! Get a physical id of the register used to pass the argument or return the value. + ASMJIT_INLINE uint32_t getRegId() const noexcept { return (_value & kRegIdMask) >> kRegIdShift; } + + uint32_t _value; + }; + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + explicit ASMJIT_INLINE FuncArgsMapper(const FuncDetail* fd) noexcept { reset(fd); } + ASMJIT_INLINE FuncArgsMapper(const FuncArgsMapper& other) noexcept { + ::memcpy(this, &other, sizeof(*this)); + } + + // -------------------------------------------------------------------------- + // [Reset] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE void reset(const FuncDetail* fd = nullptr) noexcept { + _funcDetail = fd; + ::memset(_args, 0, sizeof(_args)); + } + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE const FuncDetail* getFuncDetail() const noexcept { return _funcDetail; } + ASMJIT_INLINE void setFuncDetail(const FuncDetail* fd) noexcept { _funcDetail = fd; } + + ASMJIT_INLINE Value& getArg(size_t index) noexcept { + ASMJIT_ASSERT(index < ASMJIT_ARRAY_SIZE(_args)); + return _args[index]; + } + ASMJIT_INLINE const Value& getArg(size_t index) const noexcept { + ASMJIT_ASSERT(index < ASMJIT_ARRAY_SIZE(_args)); + return _args[index]; + } + + ASMJIT_INLINE bool isAssigned(size_t index) const noexcept { + ASMJIT_ASSERT(index < ASMJIT_ARRAY_SIZE(_args)); + return _args[index].isAssigned(); + } + + ASMJIT_INLINE void assign(size_t index, const Reg& reg, uint32_t typeId = TypeId::kVoid) noexcept { + // Not designed for virtual registers. + ASMJIT_ASSERT(index < ASMJIT_ARRAY_SIZE(_args)); + ASMJIT_ASSERT(reg.isPhysReg()); + + _args[index].assign(typeId, reg.getType(), reg.getId()); + } + + // NOTE: All `assignAll()` methods are shortcuts to assign all arguments at + // once, however, since registers are passed all at once these initializers + // don't provide any way to pass TypeId and/or to keep any argument between + // the arguments passed uninitialized. + ASMJIT_INLINE void assignAll(const Reg& a0) noexcept { + assign(0, a0); + } + ASMJIT_INLINE void assignAll(const Reg& a0, const Reg& a1) noexcept { + assign(0, a0); assign(1, a1); + } + ASMJIT_INLINE void assignAll(const Reg& a0, const Reg& a1, const Reg& a2) noexcept { + assign(0, a0); assign(1, a1); assign(2, a2); + } + ASMJIT_INLINE void assignAll(const Reg& a0, const Reg& a1, const Reg& a2, const Reg& a3) noexcept { + assign(0, a0); assign(1, a1); assign(2, a2); assign(3, a3); + } + ASMJIT_INLINE void assignAll(const Reg& a0, const Reg& a1, const Reg& a2, const Reg& a3, const Reg& a4) noexcept { + assign(0, a0); assign(1, a1); assign(2, a2); assign(3, a3); + assign(4, a4); + } + ASMJIT_INLINE void assignAll(const Reg& a0, const Reg& a1, const Reg& a2, const Reg& a3, const Reg& a4, const Reg& a5) noexcept { + assign(0, a0); assign(1, a1); assign(2, a2); assign(3, a3); + assign(4, a4); assign(5, a5); + } + ASMJIT_INLINE void assignAll(const Reg& a0, const Reg& a1, const Reg& a2, const Reg& a3, const Reg& a4, const Reg& a5, const Reg& a6) noexcept { + assign(0, a0); assign(1, a1); assign(2, a2); assign(3, a3); + assign(4, a4); assign(5, a5); assign(6, a6); + } + ASMJIT_INLINE void assignAll(const Reg& a0, const Reg& a1, const Reg& a2, const Reg& a3, const Reg& a4, const Reg& a5, const Reg& a6, const Reg& a7) noexcept { + assign(0, a0); assign(1, a1); assign(2, a2); assign(3, a3); + assign(4, a4); assign(5, a5); assign(6, a6); assign(7, a7); + } + + // -------------------------------------------------------------------------- + // [Utilities] + // -------------------------------------------------------------------------- + + //! Update `FuncFrameInfo` accordingly to FuncArgsMapper. + //! + //! This method must be called if you use `FuncArgsMapper` and you plan to + //! use `FuncUtils::allocArgs()` to remap all arguments after the prolog is + //! inserted. + ASMJIT_API Error updateFrameInfo(FuncFrameInfo& ffi) const noexcept; + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + const FuncDetail* _funcDetail; //!< Function detail. + Value _args[kFuncArgCountLoHi]; //!< Mapping of each function argument. +}; + +// ============================================================================ +// [asmjit::FuncUtils] +// ============================================================================ + +struct FuncUtils { + ASMJIT_API static Error emitProlog(CodeEmitter* emitter, const FuncFrameLayout& layout); + ASMJIT_API static Error emitEpilog(CodeEmitter* emitter, const FuncFrameLayout& layout); + ASMJIT_API static Error allocArgs(CodeEmitter* emitter, const FuncFrameLayout& layout, const FuncArgsMapper& args); +}; + +//! \} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" + +// [Guard] +#endif // _ASMJIT_BASE_FUNC_H diff --git a/libraries/asmjit/asmjit/base/globals.cpp b/libraries/asmjit/asmjit/base/globals.cpp new file mode 100644 index 00000000000..b4612e5fa37 --- /dev/null +++ b/libraries/asmjit/asmjit/base/globals.cpp @@ -0,0 +1,118 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Export] +#define ASMJIT_EXPORTS + +// [Dependencies] +#include "../base/globals.h" +#include "../base/utils.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +// ============================================================================ +// [asmjit::DebugUtils] +// ============================================================================ + +#if !defined(ASMJIT_DISABLE_TEXT) +static const char errorMessages[] = + "Ok\0" + "No heap memory\0" + "No virtual memory\0" + "Invalid argument\0" + "Invalid state\0" + "Invalid architecture\0" + "Not initialized\0" + "Already initialized\0" + "Feature not enabled\0" + "Slot occupied\0" + "No code generated\0" + "Code too large\0" + "Invalid label\0" + "Label index overflow\0" + "Label already bound\0" + "Label already defined\0" + "Label name too long\0" + "Invalid label name\0" + "Invalid parent label\0" + "Non-local label can't have parent\0" + "Relocation index overflow\0" + "Invalid relocation entry\0" + "Invalid instruction\0" + "Invalid register type\0" + "Invalid register kind\0" + "Invalid register's physical id\0" + "Invalid register's virtual id\0" + "Invalid prefix combination\0" + "Invalid lock prefix\0" + "Invalid xacquire prefix\0" + "Invalid xrelease prefix\0" + "Invalid rep prefix\0" + "Invalid rex prefix\0" + "Invalid mask, expected {k}\0" + "Invalid use of {k}\0" + "Invalid use of {k}{z}\0" + "Invalid broadcast {1tox}\0" + "Invalid {er} or {sae} option\0" + "Invalid address\0" + "Invalid address index\0" + "Invalid address scale\0" + "Invalid use of 64-bit address\0" + "Invalid displacement\0" + "Invalid segment\0" + "Invalid immediate value\0" + "Invalid operand size\0" + "Ambiguous operand size\0" + "Operand size mismatch\0" + "Invalid type-info\0" + "Invalid use of a low 8-bit GPB register\0" + "Invalid use of a 64-bit GPQ register in 32-bit mode\0" + "Invalid use of an 80-bit float\0" + "Not consecutive registers\0" + "No more physical registers\0" + "Overlapped registers\0" + "Overlapping register and arguments base-address register\0" + "Unknown error\0"; +#endif // ASMJIT_DISABLE_TEXT + +ASMJIT_FAVOR_SIZE const char* DebugUtils::errorAsString(Error err) noexcept { +#if !defined(ASMJIT_DISABLE_TEXT) + return Utils::findPackedString(errorMessages, std::min(err, kErrorCount)); +#else + static const char noMessage[] = ""; + return noMessage; +#endif +} + +ASMJIT_FAVOR_SIZE void DebugUtils::debugOutput(const char* str) noexcept { +#if ASMJIT_OS_WINDOWS + ::OutputDebugStringA(str); +#else + ::fputs(str, stderr); +#endif +} + +ASMJIT_FAVOR_SIZE void DebugUtils::assertionFailed(const char* file, int line, const char* msg) noexcept { + char str[1024]; + + snprintf(str, 1024, + "[asmjit] Assertion failed at %s (line %d):\n" + "[asmjit] %s\n", file, line, msg); + + // Support buggy `snprintf` implementations. + str[1023] = '\0'; + + debugOutput(str); + ::abort(); +} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" diff --git a/libraries/asmjit/asmjit/base/globals.h b/libraries/asmjit/asmjit/base/globals.h new file mode 100644 index 00000000000..74c725159a5 --- /dev/null +++ b/libraries/asmjit/asmjit/base/globals.h @@ -0,0 +1,341 @@ +// [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] +#include "../asmjit_build.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +//! \addtogroup asmjit_base +//! \{ + +// ============================================================================ +// [asmjit::Globals] +// ============================================================================ + +enum { kInvalidValue = 0xFFFFFFFFU }; + +//! AsmJit globals. +namespace Globals { + +//! 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(0); + +//! Invalid base address. +static const uint64_t kNoBaseAddress = ~static_cast(0); + +//! Global definitions. +ASMJIT_ENUM(Defs) { + //! Invalid register id. + kInvalidRegId = 0xFF, + + //! Host memory allocator overhead. + kAllocOverhead = static_cast(sizeof(intptr_t) * 4), + //! Aggressive growing strategy threshold. + kAllocThreshold = 8192 * 1024 +}; + +ASMJIT_ENUM(Limits) { + //! Count of register kinds that are important to Function API and CodeCompiler. + //! The target architecture can define more register kinds for special registers, + //! but these will never map to virtual registers and will never be used to pass + //! and return function arguments and function return values, respectively. + kMaxVRegKinds = 4, + + //! Maximum number of physical registers of all kinds of all supported + //! architectures. This is only important for \ref CodeCompiler and its + //! \ref RAPass (register allocator pass). + //! + //! NOTE: The distribution of these registers is architecture specific. + kMaxPhysRegs = 64, + + //! Maximum alignment. + kMaxAlignment = 64, + + //! Maximum label or symbol length in bytes (take into consideration that a + //! single UTF-8 character can take more than single byte to encode it). + kMaxLabelLength = 2048 +}; + +} // Globals namespace + +// ============================================================================ +// [asmjit::Error] +// ============================================================================ + +//! AsmJit error type (uint32_t). +typedef uint32_t Error; + +//! AsmJit error codes. +ASMJIT_ENUM(ErrorCode) { + //! No error (success). + //! + //! This is default state and state you want. + kErrorOk = 0, + + //! Heap memory allocation failed. + kErrorNoHeapMemory, + + //! Virtual memory allocation failed. + kErrorNoVirtualMemory, + + //! Invalid argument. + kErrorInvalidArgument, + + //! Invalid state. + //! + //! If this error is returned it means that either you are doing something + //! wrong or AsmJit caught itself by doing something wrong. This error should + //! not be underestimated. + kErrorInvalidState, + + //! Invalid or incompatible architecture. + kErrorInvalidArch, + + //! The object is not initialized. + kErrorNotInitialized, + //! The object is already initialized. + kErrorAlreadyInitialized, + + //! Built-in feature was disabled at compile time and it's not available. + kErrorFeatureNotEnabled, + + //! CodeHolder can't have attached more than one \ref Assembler at a time. + kErrorSlotOccupied, + + //! No code generated. + //! + //! Returned by runtime if the \ref CodeHolder contains no code. + kErrorNoCodeGenerated, + //! Code generated is larger than allowed. + kErrorCodeTooLarge, + + //! Attempt to use uninitialized label. + kErrorInvalidLabel, + //! Label index overflow - a single `Assembler` instance can hold more than + //! 2 billion labels (2147483391 to be exact). If there is an attempt to + //! create more labels this error is returned. + kErrorLabelIndexOverflow, + //! Label is already bound. + kErrorLabelAlreadyBound, + //! Label is already defined (named labels). + kErrorLabelAlreadyDefined, + //! Label name is too long. + kErrorLabelNameTooLong, + //! Label must always be local if it's anonymous (without a name). + kErrorInvalidLabelName, + //! Parent id passed to `CodeHolder::newNamedLabelId()` was invalid. + kErrorInvalidParentLabel, + //! Parent id specified for a non-local (global) label. + kErrorNonLocalLabelCantHaveParent, + + //! Relocation index overflow. + kErrorRelocIndexOverflow, + //! Invalid relocation entry. + kErrorInvalidRelocEntry, + + //! Invalid instruction. + kErrorInvalidInstruction, + //! Invalid register type. + kErrorInvalidRegType, + //! Invalid register kind. + kErrorInvalidRegKind, + //! Invalid register's physical id. + kErrorInvalidPhysId, + //! Invalid register's virtual id. + kErrorInvalidVirtId, + //! Invalid prefix combination. + kErrorInvalidPrefixCombination, + //! Invalid LOCK prefix. + kErrorInvalidLockPrefix, + //! Invalid XACQUIRE prefix. + kErrorInvalidXAcquirePrefix, + //! Invalid XACQUIRE prefix. + kErrorInvalidXReleasePrefix, + //! Invalid REP prefix. + kErrorInvalidRepPrefix, + //! Invalid REX prefix. + kErrorInvalidRexPrefix, + //! Invalid mask register (not 'k'). + kErrorInvalidKMaskReg, + //! Invalid {k} use (not supported by the instruction). + kErrorInvalidKMaskUse, + //! Invalid {k}{z} use (not supported by the instruction). + kErrorInvalidKZeroUse, + //! Invalid broadcast - Currently only related to invalid use of AVX-512 {1tox}. + kErrorInvalidBroadcast, + //! Invalid 'embedded-rounding' {er} or 'suppress-all-exceptions' {sae} (AVX-512). + kErrorInvalidEROrSAE, + //! Invalid address used (not encodable). + kErrorInvalidAddress, + //! Invalid index register used in memory address (not encodable). + kErrorInvalidAddressIndex, + //! Invalid address scale (not encodable). + kErrorInvalidAddressScale, + //! Invalid use of 64-bit address. + kErrorInvalidAddress64Bit, + //! Invalid displacement (not encodable). + kErrorInvalidDisplacement, + //! Invalid segment (X86). + kErrorInvalidSegment, + + //! Invalid immediate (out of bounds on X86 and invalid pattern on ARM). + kErrorInvalidImmediate, + + //! Invalid operand size. + kErrorInvalidOperandSize, + //! Ambiguous operand size (memory has zero size while it's required to determine the operation type. + kErrorAmbiguousOperandSize, + //! Mismatching operand size (size of multiple operands doesn't match the operation size). + kErrorOperandSizeMismatch, + + //! Invalid TypeId. + kErrorInvalidTypeId, + //! Invalid use of a 8-bit GPB-HIGH register. + kErrorInvalidUseOfGpbHi, + //! Invalid use of a 64-bit GPQ register in 32-bit mode. + kErrorInvalidUseOfGpq, + //! Invalid use of an 80-bit float (TypeId::kF80). + kErrorInvalidUseOfF80, + //! Some registers in the instruction muse be consecutive (some ARM and AVX512 neural-net instructions). + kErrorNotConsecutiveRegs, + + //! AsmJit requires a physical register, but no one is available. + kErrorNoMorePhysRegs, + //! A variable has been assigned more than once to a function argument (CodeCompiler). + kErrorOverlappedRegs, + //! Invalid register to hold stack arguments offset. + kErrorOverlappingStackRegWithRegArg, + + //! Count of AsmJit error codes. + kErrorCount +}; + +// ============================================================================ +// [asmjit::Internal] +// ============================================================================ + +namespace Internal { + +#if defined(ASMJIT_CUSTOM_ALLOC) && \ + defined(ASMJIT_CUSTOM_REALLOC) && \ + defined(ASMJIT_CUSTOM_FREE) +static ASMJIT_INLINE void* allocMemory(size_t size) noexcept { return ASMJIT_CUSTOM_ALLOC(size); } +static ASMJIT_INLINE void* reallocMemory(void* p, size_t size) noexcept { return ASMJIT_CUSTOM_REALLOC(p, size); } +static ASMJIT_INLINE void releaseMemory(void* p) noexcept { ASMJIT_CUSTOM_FREE(p); } +#elif !defined(ASMJIT_CUSTOM_ALLOC) && \ + !defined(ASMJIT_CUSTOM_REALLOC) && \ + !defined(ASMJIT_CUSTOM_FREE) +static ASMJIT_INLINE void* allocMemory(size_t size) noexcept { return ::malloc(size); } +static ASMJIT_INLINE void* reallocMemory(void* p, size_t size) noexcept { return ::realloc(p, size); } +static ASMJIT_INLINE void releaseMemory(void* p) noexcept { ::free(p); } +#else +# error "[asmjit] You must provide either none or all of ASMJIT_CUSTOM_[ALLOC|REALLOC|FREE]" +#endif + +//! Cast designed to cast between function and void* pointers. +template +static ASMJIT_INLINE Dst ptr_cast(Src p) noexcept { return (Dst)p; } + +} // Internal namespace + +template +static ASMJIT_INLINE Func ptr_as_func(void* func) noexcept { return Internal::ptr_cast(func); } + +template +static ASMJIT_INLINE void* func_as_ptr(Func func) noexcept { return Internal::ptr_cast(func); } + +// ============================================================================ +// [asmjit::DebugUtils] +// ============================================================================ + +namespace DebugUtils { + +//! Returns the error `err` passed. +//! +//! Provided for debugging purposes. Putting a breakpoint inside `errored` can +//! help with tracing the origin of any error reported / returned by AsmJit. +static ASMJIT_INLINE Error errored(Error err) noexcept { return err; } + +//! Get a printable version of `asmjit::Error` code. +ASMJIT_API const char* errorAsString(Error err) noexcept; + +//! Called to output debugging message(s). +ASMJIT_API void debugOutput(const char* str) noexcept; + +//! Called on assertion failure. +//! +//! \param file Source file name where it happened. +//! \param line Line in the source file. +//! \param msg Message to display. +//! +//! 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 ASMJIT_NORETURN assertionFailed(const char* file, int line, const char* msg) noexcept; + +#if defined(ASMJIT_DEBUG) +# define ASMJIT_ASSERT(exp) \ + do { \ + if (ASMJIT_LIKELY(exp)) \ + break; \ + ::asmjit::DebugUtils::assertionFailed(__FILE__, __LINE__, #exp); \ + } while (0) +# define ASMJIT_NOT_REACHED() \ + do { \ + ::asmjit::DebugUtils::assertionFailed(__FILE__, __LINE__, \ + "ASMJIT_NOT_REACHED has been reached"); \ + ASMJIT_ASSUME(0); \ + } while (0) +#else +# define ASMJIT_ASSERT(exp) ASMJIT_NOP +# define ASMJIT_NOT_REACHED() ASMJIT_ASSUME(0) +#endif // DEBUG + +//! \internal +//! +//! Used by AsmJit to propagate a possible `Error` produced by `...` to the caller. +#define ASMJIT_PROPAGATE(...) \ + do { \ + ::asmjit::Error _err = __VA_ARGS__; \ + if (ASMJIT_UNLIKELY(_err)) \ + return _err; \ + } while (0) + +} // DebugUtils namespace + +// ============================================================================ +// [asmjit::Init / NoInit] +// ============================================================================ + +#if !defined(ASMJIT_DOCGEN) +struct _Init {}; +static const _Init Init = {}; + +struct _NoInit {}; +static const _NoInit NoInit = {}; +#endif // !ASMJIT_DOCGEN + +//! \} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" + +// [Guard] +#endif // _ASMJIT_BASE_GLOBALS_H diff --git a/libraries/asmjit/asmjit/base/inst.cpp b/libraries/asmjit/asmjit/base/inst.cpp new file mode 100644 index 00000000000..cc5ff39eac6 --- /dev/null +++ b/libraries/asmjit/asmjit/base/inst.cpp @@ -0,0 +1,77 @@ +// [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 "../asmjit_build.h" +#if defined(ASMJIT_BUILD_X86) + +// [Dependencies] +#include "../base/arch.h" +#include "../base/inst.h" + +#if defined(ASMJIT_BUILD_X86) +# include "../x86/x86instimpl_p.h" +#endif // ASMJIT_BUILD_X86 + +#if defined(ASMJIT_BUILD_ARM) +# include "../arm/arminstimpl_p.h" +#endif // ASMJIT_BUILD_ARM + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +// ============================================================================ +// [asmjit::Inst - Validate] +// ============================================================================ + +#if !defined(ASMJIT_DISABLE_VALIDATION) +Error Inst::validate(uint32_t archType, const Detail& detail, const Operand_* operands, uint32_t count) noexcept { + #if defined(ASMJIT_BUILD_X86) + if (ArchInfo::isX86Family(archType)) + return X86InstImpl::validate(archType, detail, operands, count); + #endif + + #if defined(ASMJIT_BUILD_ARM) + if (ArchInfo::isArmFamily(archType)) + return ArmInstImpl::validate(archType, detail, operands, count); + #endif + + return DebugUtils::errored(kErrorInvalidArch); +} +#endif + +// ============================================================================ +// [asmjit::Inst - CheckFeatures] +// ============================================================================ + +#if !defined(ASMJIT_DISABLE_EXTENSIONS) +Error Inst::checkFeatures(uint32_t archType, const Detail& detail, const Operand_* operands, uint32_t count, CpuFeatures& out) noexcept { + #if defined(ASMJIT_BUILD_X86) + if (ArchInfo::isX86Family(archType)) + return X86InstImpl::checkFeatures(archType, detail, operands, count, out); + #endif + + #if defined(ASMJIT_BUILD_ARM) + if (ArchInfo::isArmFamily(archType)) + return ArmInstImpl::checkFeatures(archType, detail, operands, count, out); + #endif + + return DebugUtils::errored(kErrorInvalidArch); +} +#endif // !defined(ASMJIT_DISABLE_EXTENSIONS) + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" + +// [Guard] +#endif // ASMJIT_BUILD_X86 diff --git a/libraries/asmjit/asmjit/base/inst.h b/libraries/asmjit/asmjit/base/inst.h new file mode 100644 index 00000000000..7bb210a8ba8 --- /dev/null +++ b/libraries/asmjit/asmjit/base/inst.h @@ -0,0 +1,108 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Guard] +#ifndef _ASMJIT_BASE_INST_H +#define _ASMJIT_BASE_INST_H + +// [Dependencies] +#include "../base/cpuinfo.h" +#include "../base/operand.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +//! \addtogroup asmjit_base +//! \{ + +// ============================================================================ +// [asmjit::Inst] +// ============================================================================ + +//! Definitions and utilities related to instructions used by all architectures. +struct Inst { + ASMJIT_ENUM(Id) { + kIdNone = 0 //!< Invalid or uninitialized instruction id. + }; + + //! Describes an instruction's jump type, if any. + ASMJIT_ENUM(JumpType) { + kJumpTypeNone = 0, //!< Instruction doesn't jump (regular instruction). + kJumpTypeDirect = 1, //!< Instruction is a unconditional (direct) jump. + kJumpTypeConditional = 2, //!< Instruction is a conditional jump. + kJumpTypeCall = 3, //!< Instruction is a function call. + kJumpTypeReturn = 4 //!< Instruction is a function return. + }; + + // -------------------------------------------------------------------------- + // [Detail] + // -------------------------------------------------------------------------- + + //! Instruction id, options, and extraReg packed in a single structure. This + //! structure exists to simplify analysis and validation API that requires a + //! lot of information about the instruction to be processed. + class Detail { + public: + ASMJIT_INLINE Detail() noexcept + : instId(0), + options(0), + extraReg() {} + + explicit ASMJIT_INLINE Detail(uint32_t instId, uint32_t options = 0) noexcept + : instId(instId), + options(options), + extraReg() {} + + ASMJIT_INLINE Detail(uint32_t instId, uint32_t options, const RegOnly& reg) noexcept + : instId(instId), + options(options), + extraReg(reg) {} + + ASMJIT_INLINE Detail(uint32_t instId, uint32_t options, const Reg& reg) noexcept + : instId(instId), + options(options) { extraReg.init(reg); } + + // ------------------------------------------------------------------------ + // [Accessors] + // ------------------------------------------------------------------------ + + ASMJIT_INLINE bool hasExtraReg() const noexcept { return extraReg.isValid(); } + + // ------------------------------------------------------------------------ + // [Members] + // ------------------------------------------------------------------------ + + uint32_t instId; + uint32_t options; + RegOnly extraReg; + }; + + // -------------------------------------------------------------------------- + // [API] + // -------------------------------------------------------------------------- + +#if !defined(ASMJIT_DISABLE_VALIDATION) + //! Validate the given instruction. + ASMJIT_API static Error validate(uint32_t archType, const Detail& detail, const Operand_* operands, uint32_t count) noexcept; +#endif // !ASMJIT_DISABLE_VALIDATION + +#if !defined(ASMJIT_DISABLE_EXTENSIONS) + //! Check CPU features required to execute the given instruction. + ASMJIT_API static Error checkFeatures(uint32_t archType, const Detail& detail, const Operand_* operands, uint32_t count, CpuFeatures& out) noexcept; +#endif // !defined(ASMJIT_DISABLE_EXTENSIONS) +}; + +//! \} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" + +// [Guard] +#endif // _ASMJIT_BASE_INST_H diff --git a/libraries/asmjit/asmjit/base/logging.cpp b/libraries/asmjit/asmjit/base/logging.cpp new file mode 100644 index 00000000000..efb4475255b --- /dev/null +++ b/libraries/asmjit/asmjit/base/logging.cpp @@ -0,0 +1,497 @@ +// [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 "../asmjit_build.h" +#if !defined(ASMJIT_DISABLE_LOGGING) + +// [Dependencies] +#include "../base/codeholder.h" +#include "../base/codeemitter.h" +#include "../base/logging.h" +#include "../base/utils.h" + +#if !defined(ASMJIT_DISABLE_BUILDER) +# include "../base/codebuilder.h" +#endif // !ASMJIT_DISABLE_BUILDER + +#if !defined(ASMJIT_DISABLE_COMPILER) +# include "../base/codecompiler.h" +#else +namespace asmjit { class VirtReg; } +#endif // !ASMJIT_DISABLE_COMPILER + +#if defined(ASMJIT_BUILD_X86) +# include "../x86/x86logging_p.h" +#endif // ASMJIT_BUILD_X86 + +#if defined(ASMJIT_BUILD_ARM) +# include "../arm/armlogging_p.h" +#endif // ASMJIT_BUILD_ARM + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +// ============================================================================ +// [asmjit::Logger - Construction / Destruction] +// ============================================================================ + +Logger::Logger() noexcept { + _options = 0; + ::memset(_indentation, 0, ASMJIT_ARRAY_SIZE(_indentation)); +} +Logger::~Logger() noexcept {} + +// ============================================================================ +// [asmjit::Logger - Logging] +// ============================================================================ + +Error Logger::logf(const char* fmt, ...) noexcept { + Error err; + + va_list ap; + va_start(ap, fmt); + err = logv(fmt, ap); + va_end(ap); + + return err; +} + +Error Logger::logv(const char* fmt, va_list ap) noexcept { + char buf[1024]; + size_t len = vsnprintf(buf, sizeof(buf), fmt, ap); + + if (len >= sizeof(buf)) + len = sizeof(buf) - 1; + return log(buf, len); +} + +Error Logger::logBinary(const void* data, size_t size) noexcept { + 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(data); + size_t i = size; + + char buffer[128]; + ::memcpy(buffer, prefix, ASMJIT_ARRAY_SIZE(prefix) - 1); + + while (i) { + uint32_t n = static_cast(std::min(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'; + ASMJIT_PROPAGATE(log(buffer, (size_t)(p - buffer))); + } + + return kErrorOk; +} + +// ============================================================================ +// [asmjit::Logger - Indentation] +// ============================================================================ + +void Logger::setIndentation(const char* indentation) noexcept { + ::memset(_indentation, 0, ASMJIT_ARRAY_SIZE(_indentation)); + if (!indentation) + return; + + size_t length = Utils::strLen(indentation, ASMJIT_ARRAY_SIZE(_indentation) - 1); + ::memcpy(_indentation, indentation, length); +} + +// ============================================================================ +// [asmjit::FileLogger - Construction / Destruction] +// ============================================================================ + +FileLogger::FileLogger(FILE* stream) noexcept : _stream(nullptr) { setStream(stream); } +FileLogger::~FileLogger() noexcept {} + +// ============================================================================ +// [asmjit::FileLogger - Logging] +// ============================================================================ + +Error FileLogger::_log(const char* buf, size_t len) noexcept { + if (!_stream) + return kErrorOk; + + if (len == Globals::kInvalidIndex) + len = strlen(buf); + + fwrite(buf, 1, len, _stream); + return kErrorOk; +} + +// ============================================================================ +// [asmjit::StringLogger - Construction / Destruction] +// ============================================================================ + +StringLogger::StringLogger() noexcept {} +StringLogger::~StringLogger() noexcept {} + +// ============================================================================ +// [asmjit::StringLogger - Logging] +// ============================================================================ + +Error StringLogger::_log(const char* buf, size_t len) noexcept { + return _stringBuilder.appendString(buf, len); +} + +// ============================================================================ +// [asmjit::Logging] +// ============================================================================ + +Error Logging::formatLabel( + StringBuilder& sb, + uint32_t logOptions, + const CodeEmitter* emitter, + uint32_t labelId) noexcept { + + const LabelEntry* le = emitter->getCode()->getLabelEntry(labelId); + if (ASMJIT_UNLIKELY(!le)) + return sb.appendFormat("InvalidLabel[Id=%u]", static_cast(labelId)); + + if (le->hasName()) { + if (le->hasParent()) { + uint32_t parentId = le->getParentId(); + const LabelEntry* pe = emitter->getCode()->getLabelEntry(parentId); + + if (ASMJIT_UNLIKELY(!pe)) + ASMJIT_PROPAGATE(sb.appendFormat("InvalidLabel[Id=%u]", static_cast(labelId))); + else if (ASMJIT_UNLIKELY(!pe->hasName())) + ASMJIT_PROPAGATE(sb.appendFormat("L%u", Operand::unpackId(parentId))); + else + ASMJIT_PROPAGATE(sb.appendString(pe->getName())); + + ASMJIT_PROPAGATE(sb.appendChar('.')); + } + return sb.appendString(le->getName()); + } + else { + return sb.appendFormat("L%u", Operand::unpackId(labelId)); + } +} + +Error Logging::formatRegister( + StringBuilder& sb, + uint32_t logOptions, + const CodeEmitter* emitter, + uint32_t archType, + uint32_t regType, + uint32_t regId) noexcept { + +#if defined(ASMJIT_BUILD_X86) + return X86Logging::formatRegister(sb, logOptions, emitter, archType, regType, regId); +#endif // ASMJIT_BUILD_X86 + +#if defined(ASMJIT_BUILD_ARM) + return ArmLogging::formatRegister(sb, logOptions, emitter, archType, regType, regId); +#endif // ASMJIT_BUILD_ARM + + return kErrorInvalidArch; +} + +Error Logging::formatOperand( + StringBuilder& sb, + uint32_t logOptions, + const CodeEmitter* emitter, + uint32_t archType, + const Operand_& op) noexcept { + +#if defined(ASMJIT_BUILD_X86) + return X86Logging::formatOperand(sb, logOptions, emitter, archType, op); +#endif // ASMJIT_BUILD_X86 + +#if defined(ASMJIT_BUILD_ARM) + return ArmLogging::formatOperand(sb, logOptions, emitter, archType, op); +#endif // ASMJIT_BUILD_ARM + + return kErrorInvalidArch; +} + +Error Logging::formatInstruction( + StringBuilder& sb, + uint32_t logOptions, + const CodeEmitter* emitter, + uint32_t archType, + const Inst::Detail& detail, const Operand_* opArray, uint32_t opCount) noexcept { + +#if defined(ASMJIT_BUILD_X86) + return X86Logging::formatInstruction(sb, logOptions, emitter, archType, detail, opArray, opCount); +#endif // ASMJIT_BUILD_X86 + +#if defined(ASMJIT_BUILD_ARM) + return ArmLogging::formatInstruction(sb, logOptions, emitter, archType, detail, opArray, opCount); +#endif // ASMJIT_BUILD_ARM + + return kErrorInvalidArch; +} + +#if !defined(ASMJIT_DISABLE_BUILDER) +static Error formatTypeId(StringBuilder& sb, uint32_t typeId) noexcept { + if (typeId == TypeId::kVoid) + return sb.appendString("void"); + + if (!TypeId::isValid(typeId)) + return sb.appendString("unknown"); + + const char* typeName = "unknown"; + uint32_t typeSize = TypeId::sizeOf(typeId); + + uint32_t elementId = TypeId::elementOf(typeId); + switch (elementId) { + case TypeId::kIntPtr : typeName = "intptr" ; break; + case TypeId::kUIntPtr: typeName = "uintptr"; break; + case TypeId::kI8 : typeName = "i8" ; break; + case TypeId::kU8 : typeName = "u8" ; break; + case TypeId::kI16 : typeName = "i16" ; break; + case TypeId::kU16 : typeName = "u16" ; break; + case TypeId::kI32 : typeName = "i32" ; break; + case TypeId::kU32 : typeName = "u32" ; break; + case TypeId::kI64 : typeName = "i64" ; break; + case TypeId::kU64 : typeName = "u64" ; break; + case TypeId::kF32 : typeName = "f32" ; break; + case TypeId::kF64 : typeName = "f64" ; break; + case TypeId::kF80 : typeName = "f80" ; break; + case TypeId::kMask8 : typeName = "mask8" ; break; + case TypeId::kMask16 : typeName = "mask16" ; break; + case TypeId::kMask32 : typeName = "mask32" ; break; + case TypeId::kMask64 : typeName = "mask64" ; break; + case TypeId::kMmx32 : typeName = "mmx32" ; break; + case TypeId::kMmx64 : typeName = "mmx64" ; break; + } + + uint32_t elementSize = TypeId::sizeOf(elementId); + if (typeSize > elementSize) { + unsigned int numElements = typeSize / elementSize; + return sb.appendFormat("%sx%u", typeName, numElements); + } + else { + return sb.appendString(typeName); + } +} + +static Error formatFuncDetailValue( + StringBuilder& sb, + uint32_t logOptions, + const CodeEmitter* emitter, + FuncDetail::Value value) noexcept { + + uint32_t typeId = value.getTypeId(); + ASMJIT_PROPAGATE(formatTypeId(sb, typeId)); + + if (value.byReg()) { + ASMJIT_PROPAGATE(sb.appendChar(':')); + ASMJIT_PROPAGATE(Logging::formatRegister(sb, logOptions, emitter, emitter->getArchType(), value.getRegType(), value.getRegId())); + } + + if (value.byStack()) { + ASMJIT_PROPAGATE(sb.appendFormat(":[%d]", static_cast(value.getStackOffset()))); + } + + return kErrorOk; +} + +static Error formatFuncRets( + StringBuilder& sb, + uint32_t logOptions, + const CodeEmitter* emitter, + const FuncDetail& fd, + VirtReg* const* vRegs) noexcept { + + if (!fd.hasRet()) + return sb.appendString("void"); + + for (uint32_t i = 0; i < fd.getRetCount(); i++) { + if (i) ASMJIT_PROPAGATE(sb.appendString(", ")); + ASMJIT_PROPAGATE(formatFuncDetailValue(sb, logOptions, emitter, fd.getRet(i))); + +#if !defined(ASMJIT_DISABLE_COMPILER) + if (vRegs) + ASMJIT_PROPAGATE(sb.appendFormat(" {%s}", vRegs[i]->getName())); +#endif // !ASMJIT_DISABLE_COMPILER + } + + return kErrorOk; +} + +static Error formatFuncArgs( + StringBuilder& sb, + uint32_t logOptions, + const CodeEmitter* emitter, + const FuncDetail& fd, + VirtReg* const* vRegs) noexcept { + + for (uint32_t i = 0; i < fd.getArgCount(); i++) { + if (i) ASMJIT_PROPAGATE(sb.appendString(", ")); + ASMJIT_PROPAGATE(formatFuncDetailValue(sb, logOptions, emitter, fd.getArg(i))); + +#if !defined(ASMJIT_DISABLE_COMPILER) + if (vRegs) + ASMJIT_PROPAGATE(sb.appendFormat(" {%s}", vRegs[i]->getName())); +#endif // !ASMJIT_DISABLE_COMPILER + } + + return kErrorOk; +} + +Error Logging::formatNode( + StringBuilder& sb, + uint32_t logOptions, + const CodeBuilder* cb, + const CBNode* node_) noexcept { + + if (node_->hasPosition()) + ASMJIT_PROPAGATE(sb.appendFormat("<%04u> ", node_->getPosition())); + + switch (node_->getType()) { + case CBNode::kNodeInst: { + const CBInst* node = node_->as(); + ASMJIT_PROPAGATE( + Logging::formatInstruction(sb, logOptions, cb, + cb->getArchType(), + node->getInstDetail(), node->getOpArray(), node->getOpCount())); + break; + } + + case CBNode::kNodeLabel: { + const CBLabel* node = node_->as(); + ASMJIT_PROPAGATE(sb.appendFormat("L%u:", Operand::unpackId(node->getId()))); + break; + } + + case CBNode::kNodeData: { + const CBData* node = node_->as(); + ASMJIT_PROPAGATE(sb.appendFormat(".embed (%u bytes)", node->getSize())); + break; + } + + case CBNode::kNodeAlign: { + const CBAlign* node = node_->as(); + ASMJIT_PROPAGATE( + sb.appendFormat(".align %u (%s)", + node->getAlignment(), + node->getMode() == kAlignCode ? "code" : "data")); + break; + } + + case CBNode::kNodeComment: { + const CBComment* node = node_->as(); + ASMJIT_PROPAGATE(sb.appendFormat("; %s", node->getInlineComment())); + break; + } + + case CBNode::kNodeSentinel: { + ASMJIT_PROPAGATE(sb.appendString("[sentinel]")); + break; + } + +#if !defined(ASMJIT_DISABLE_COMPILER) + case CBNode::kNodeFunc: { + const CCFunc* node = node_->as(); + ASMJIT_PROPAGATE(formatLabel(sb, logOptions, cb, node->getId())); + + ASMJIT_PROPAGATE(sb.appendString(": [")); + ASMJIT_PROPAGATE(formatFuncRets(sb, logOptions, cb, node->getDetail(), nullptr)); + ASMJIT_PROPAGATE(sb.appendString("]")); + + ASMJIT_PROPAGATE(sb.appendString("(")); + ASMJIT_PROPAGATE(formatFuncArgs(sb, logOptions, cb, node->getDetail(), node->getArgs())); + ASMJIT_PROPAGATE(sb.appendString(")")); + break; + } + + case CBNode::kNodeFuncExit: { + ASMJIT_PROPAGATE(sb.appendString("[ret]")); + break; + } + + case CBNode::kNodeFuncCall: { + const CCFuncCall* node = node_->as(); + ASMJIT_PROPAGATE( + Logging::formatInstruction(sb, logOptions, cb, + cb->getArchType(), + node->getInstDetail(), node->getOpArray(), node->getOpCount())); + break; + } +#endif // !ASMJIT_DISABLE_COMPILER + + default: { + ASMJIT_PROPAGATE(sb.appendFormat("[unknown (type=%u)]", node_->getType())); + break; + } + } + + return kErrorOk; +} +#endif // !ASMJIT_DISABLE_BUILDER + +Error Logging::formatLine(StringBuilder& sb, const uint8_t* binData, size_t binLen, size_t dispLen, size_t imLen, const char* comment) noexcept { + size_t currentLen = sb.getLength(); + size_t commentLen = comment ? Utils::strLen(comment, kMaxCommentLength) : 0; + + ASMJIT_ASSERT(binLen >= dispLen); + + if ((binLen != 0 && binLen != Globals::kInvalidIndex) || commentLen) { + size_t align = kMaxInstLength; + char sep = ';'; + + for (size_t i = (binLen == Globals::kInvalidIndex); i < 2; i++) { + size_t begin = sb.getLength(); + + // Append align. + if (currentLen < align) + ASMJIT_PROPAGATE(sb.appendChars(' ', align - currentLen)); + + // Append separator. + if (sep) { + ASMJIT_PROPAGATE(sb.appendChar(sep)); + ASMJIT_PROPAGATE(sb.appendChar(' ')); + } + + // Append binary data or comment. + if (i == 0) { + ASMJIT_PROPAGATE(sb.appendHex(binData, binLen - dispLen - imLen)); + ASMJIT_PROPAGATE(sb.appendChars('.', dispLen * 2)); + ASMJIT_PROPAGATE(sb.appendHex(binData + binLen - imLen, imLen)); + if (commentLen == 0) break; + } + else { + ASMJIT_PROPAGATE(sb.appendString(comment, commentLen)); + } + + currentLen += sb.getLength() - begin; + align += kMaxBinaryLength; + sep = '|'; + } + } + + return sb.appendChar('\n'); +} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" + +// [Guard] +#endif // !ASMJIT_DISABLE_LOGGING diff --git a/libraries/asmjit/asmjit/base/logging.h b/libraries/asmjit/asmjit/base/logging.h new file mode 100644 index 00000000000..609f1880369 --- /dev/null +++ b/libraries/asmjit/asmjit/base/logging.h @@ -0,0 +1,288 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Guard] +#ifndef _ASMJIT_BASE_LOGGING_H +#define _ASMJIT_BASE_LOGGING_H + +// [Dependencies] +#include "../base/inst.h" +#include "../base/string.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +//! \addtogroup asmjit_base +//! \{ + +#if !defined(ASMJIT_DISABLE_LOGGING) + +// ============================================================================ +// [Forward Declarations] +// ============================================================================ + +class CodeEmitter; +class Reg; +struct Operand_; + +#if !defined(ASMJIT_DISABLE_BUILDER) +class CodeBuilder; +class CBNode; +#endif // !ASMJIT_DISABLE_BUILDER + +// ============================================================================ +// [asmjit::Logger] +// ============================================================================ + +//! Abstract logging interface and helpers. +//! +//! 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. +//! +//! There are two \ref Logger implementations offered by AsmJit: +//! - \ref FileLogger - allows to log into a `FILE*` stream. +//! - \ref StringLogger - logs into a \ref StringBuilder. +class ASMJIT_VIRTAPI Logger { +public: + ASMJIT_NONCOPYABLE(Logger) + + // -------------------------------------------------------------------------- + // [Options] + // -------------------------------------------------------------------------- + + //! Logger options. + ASMJIT_ENUM(Options) { + kOptionBinaryForm = 0x00000001, //! Output instructions also in binary form. + kOptionImmExtended = 0x00000002, //! Output a meaning of some immediates. + kOptionHexImmediate = 0x00000004, //! Output constants in hexadecimal form. + kOptionHexDisplacement = 0x00000008 //! Output displacements in hexadecimal form. + }; + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a `Logger` instance. + ASMJIT_API Logger() noexcept; + //! Destroy the `Logger` instance. + ASMJIT_API virtual ~Logger() noexcept; + + // -------------------------------------------------------------------------- + // [Logging] + // -------------------------------------------------------------------------- + + //! Log `str` - must be reimplemented. + virtual Error _log(const char* str, size_t len) noexcept = 0; + + //! Log a string `str`, which is either null terminated or having `len` length. + ASMJIT_INLINE Error log(const char* str, size_t len = Globals::kInvalidIndex) noexcept { return _log(str, len); } + //! Log a content of a `StringBuilder` `str`. + ASMJIT_INLINE Error log(const StringBuilder& str) noexcept { return _log(str.getData(), str.getLength()); } + + //! Format the message by using `sprintf()` and then send to `log()`. + ASMJIT_API Error logf(const char* fmt, ...) noexcept; + //! Format the message by using `vsprintf()` and then send to `log()`. + ASMJIT_API Error logv(const char* fmt, va_list ap) noexcept; + //! Log binary data. + ASMJIT_API Error logBinary(const void* data, size_t size) noexcept; + + // -------------------------------------------------------------------------- + // [Options] + // -------------------------------------------------------------------------- + + //! Get all logger options as a single integer. + ASMJIT_INLINE uint32_t getOptions() const noexcept { return _options; } + //! Get the given logger option. + ASMJIT_INLINE bool hasOption(uint32_t option) const noexcept { return (_options & option) != 0; } + ASMJIT_INLINE void addOptions(uint32_t options) noexcept { _options |= options; } + ASMJIT_INLINE void clearOptions(uint32_t options) noexcept { _options &= ~options; } + + // -------------------------------------------------------------------------- + // [Indentation] + // -------------------------------------------------------------------------- + + //! Get indentation. + ASMJIT_INLINE const char* getIndentation() const noexcept { return _indentation; } + //! Set indentation. + ASMJIT_API void setIndentation(const char* indentation) noexcept; + //! Reset indentation. + ASMJIT_INLINE void resetIndentation() noexcept { setIndentation(nullptr); } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + //! Options, see \ref LoggerOption. + uint32_t _options; + + //! Indentation. + char _indentation[12]; +}; + +// ============================================================================ +// [asmjit::FileLogger] +// ============================================================================ + +//! Logger that can log to a `FILE*` stream. +class ASMJIT_VIRTAPI FileLogger : public Logger { +public: + ASMJIT_NONCOPYABLE(FileLogger) + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a new `FileLogger` that logs to a `FILE` stream. + ASMJIT_API FileLogger(FILE* stream = nullptr) noexcept; + //! Destroy the `FileLogger`. + ASMJIT_API virtual ~FileLogger() noexcept; + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get the logging out put stream or null. + ASMJIT_INLINE FILE* getStream() const noexcept { return _stream; } + + //! Set the logging output stream to `stream` or null. + //! + //! NOTE: If the `stream` is null it will disable logging, but it won't + //! stop calling `log()` unless the logger is detached from the + //! \ref Assembler. + ASMJIT_INLINE void setStream(FILE* stream) noexcept { _stream = stream; } + + // -------------------------------------------------------------------------- + // [Logging] + // -------------------------------------------------------------------------- + + ASMJIT_API Error _log(const char* buf, size_t len = Globals::kInvalidIndex) noexcept override; + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + //! C file stream. + FILE* _stream; +}; + +// ============================================================================ +// [asmjit::StringLogger] +// ============================================================================ + +//! Logger that stores everything in an internal string buffer. +class ASMJIT_VIRTAPI StringLogger : public Logger { +public: + ASMJIT_NONCOPYABLE(StringLogger) + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create new `StringLogger`. + ASMJIT_API StringLogger() noexcept; + //! Destroy the `StringLogger`. + ASMJIT_API virtual ~StringLogger() noexcept; + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get `char*` 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 noexcept { return _stringBuilder.getData(); } + //! Clear the resulting string. + ASMJIT_INLINE void clearString() noexcept { _stringBuilder.clear(); } + + //! Get the length of the string returned by `getString()`. + ASMJIT_INLINE size_t getLength() const noexcept { return _stringBuilder.getLength(); } + + // -------------------------------------------------------------------------- + // [Logging] + // -------------------------------------------------------------------------- + + ASMJIT_API Error _log(const char* buf, size_t len = Globals::kInvalidIndex) noexcept override; + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + //! Output string. + StringBuilder _stringBuilder; +}; + +// ============================================================================ +// [asmjit::Logging] +// ============================================================================ + +struct Logging { + ASMJIT_API static Error formatRegister( + StringBuilder& sb, + uint32_t logOptions, + const CodeEmitter* emitter, + uint32_t archType, + uint32_t regType, + uint32_t regId) noexcept; + + ASMJIT_API static Error formatLabel( + StringBuilder& sb, + uint32_t logOptions, + const CodeEmitter* emitter, + uint32_t labelId) noexcept; + + ASMJIT_API static Error formatOperand( + StringBuilder& sb, + uint32_t logOptions, + const CodeEmitter* emitter, + uint32_t archType, + const Operand_& op) noexcept; + + ASMJIT_API static Error formatInstruction( + StringBuilder& sb, + uint32_t logOptions, + const CodeEmitter* emitter, + uint32_t archType, + const Inst::Detail& detail, const Operand_* opArray, uint32_t opCount) noexcept; + +#if !defined(ASMJIT_DISABLE_BUILDER) + ASMJIT_API static Error formatNode( + StringBuilder& sb, + uint32_t logOptions, + const CodeBuilder* cb, + const CBNode* node_) noexcept; +#endif // !ASMJIT_DISABLE_BUILDER + +// Only used by AsmJit internals, not available to users. +#if defined(ASMJIT_EXPORTS) + enum { + // Has to be big to be able to hold all metadata compiler can assign to a + // single instruction. + kMaxCommentLength = 512, + kMaxInstLength = 40, + kMaxBinaryLength = 26 + }; + + static Error formatLine( + StringBuilder& sb, + const uint8_t* binData, size_t binLen, size_t dispLen, size_t imLen, const char* comment) noexcept; +#endif // ASMJIT_EXPORTS +}; +#else +class Logger; +#endif // !ASMJIT_DISABLE_LOGGING + +//! \} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" + +// [Guard] +#endif // _ASMJIT_BASE_LOGGER_H diff --git a/libraries/asmjit/asmjit/base/misc_p.h b/libraries/asmjit/asmjit/base/misc_p.h new file mode 100644 index 00000000000..5024f1c70f3 --- /dev/null +++ b/libraries/asmjit/asmjit/base/misc_p.h @@ -0,0 +1,74 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Guard] +#ifndef _ASMJIT_BASE_MISC_P_H +#define _ASMJIT_BASE_MISC_P_H + +// [Dependencies] +#include "../asmjit_build.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +//! \addtogroup asmjit_base +//! \{ + +//! \internal +//! +//! Macro used to populate a table with 16 elements starting at `I`. +#define ASMJIT_TABLE_16(DEF, I) DEF(I + 0), DEF(I + 1), DEF(I + 2), DEF(I + 3), \ + DEF(I + 4), DEF(I + 5), DEF(I + 6), DEF(I + 7), \ + DEF(I + 8), DEF(I + 9), DEF(I + 10), DEF(I + 11), \ + DEF(I + 12), DEF(I + 13), DEF(I + 14), DEF(I + 15) + +#define ASMJIT_TABLE_T_8(TABLE, VALUE, I) \ + TABLE< I + 0 >::VALUE, TABLE< I + 1 >::VALUE, \ + TABLE< I + 2 >::VALUE, TABLE< I + 3 >::VALUE, \ + TABLE< I + 4 >::VALUE, TABLE< I + 5 >::VALUE, \ + TABLE< I + 6 >::VALUE, TABLE< I + 7 >::VALUE + +#define ASMJIT_TABLE_T_16(TABLE, VALUE, I) \ + ASMJIT_TABLE_T_8(TABLE, VALUE, I), \ + ASMJIT_TABLE_T_8(TABLE, VALUE, I + 8) + +#define ASMJIT_TABLE_T_32(TABLE, VALUE, I) \ + ASMJIT_TABLE_T_16(TABLE, VALUE, I), \ + ASMJIT_TABLE_T_16(TABLE, VALUE, I + 16) + +#define ASMJIT_TABLE_T_64(TABLE, VALUE, I) \ + ASMJIT_TABLE_T_32(TABLE, VALUE, I), \ + ASMJIT_TABLE_T_32(TABLE, VALUE, I + 32) + +#define ASMJIT_TABLE_T_128(TABLE, VALUE, I) \ + ASMJIT_TABLE_T_64(TABLE, VALUE, I), \ + ASMJIT_TABLE_T_64(TABLE, VALUE, I + 64) + +#define ASMJIT_TABLE_T_256(TABLE, VALUE, I) \ + ASMJIT_TABLE_T_128(TABLE, VALUE, I), \ + ASMJIT_TABLE_T_128(TABLE, VALUE, I + 128) + +#define ASMJIT_TABLE_T_512(TABLE, VALUE, I) \ + ASMJIT_TABLE_T_256(TABLE, VALUE, I), \ + ASMJIT_TABLE_T_256(TABLE, VALUE, I + 256) + +#define ASMJIT_TABLE_T_1024(TABLE, VALUE, I) \ + ASMJIT_TABLE_T_512(TABLE, VALUE, I), \ + ASMJIT_TABLE_T_512(TABLE, VALUE, I + 512) + +//! \} + +} // asmjit namespace + +//! \} + +// [Api-End] +#include "../asmjit_apiend.h" + +// [Guard] +#endif // _ASMJIT_BASE_MISC_P_H diff --git a/libraries/asmjit/asmjit/base/operand.cpp b/libraries/asmjit/asmjit/base/operand.cpp new file mode 100644 index 00000000000..09eeea8932c --- /dev/null +++ b/libraries/asmjit/asmjit/base/operand.cpp @@ -0,0 +1,209 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Export] +#define ASMJIT_EXPORTS + +// [Dependencies] +#include "../base/operand.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +// ============================================================================ +// [asmjit::TypeId] +// ============================================================================ + +template +struct TypeIdSizeOf_T { + enum { + kValue = (ID == TypeId::kI8 ) ? 1 : + (ID == TypeId::kU8 ) ? 1 : + (ID == TypeId::kI16 ) ? 2 : + (ID == TypeId::kU16 ) ? 2 : + (ID == TypeId::kI32 ) ? 4 : + (ID == TypeId::kU32 ) ? 4 : + (ID == TypeId::kI64 ) ? 8 : + (ID == TypeId::kU64 ) ? 8 : + (ID == TypeId::kF32 ) ? 4 : + (ID == TypeId::kF64 ) ? 8 : + (ID == TypeId::kF80 ) ? 10 : + (ID == TypeId::kMask8 ) ? 1 : + (ID == TypeId::kMask16) ? 2 : + (ID == TypeId::kMask32) ? 4 : + (ID == TypeId::kMask64) ? 8 : + (ID == TypeId::kMmx32 ) ? 4 : + (ID == TypeId::kMmx64 ) ? 8 : + (ID >= TypeId::_kVec32Start && ID <= TypeId::_kVec32End ) ? 4 : + (ID >= TypeId::_kVec64Start && ID <= TypeId::_kVec64End ) ? 8 : + (ID >= TypeId::_kVec128Start && ID <= TypeId::_kVec128End) ? 16 : + (ID >= TypeId::_kVec256Start && ID <= TypeId::_kVec256End) ? 32 : + (ID >= TypeId::_kVec512Start && ID <= TypeId::_kVec512End) ? 64 : 0 + }; +}; + +template +struct TypeIdElementOf_T { + enum { + kValue = (ID == TypeId::kMask8 ) ? TypeId::kU8 : + (ID == TypeId::kMask16) ? TypeId::kU16 : + (ID == TypeId::kMask32) ? TypeId::kU32 : + (ID == TypeId::kMask64) ? TypeId::kU64 : + (ID == TypeId::kMmx32 ) ? TypeId::kI32 : + (ID == TypeId::kMmx64 ) ? TypeId::kI64 : + (ID >= TypeId::kI8 && ID <= TypeId::kF80 ) ? ID : + (ID >= TypeId::_kVec32Start && ID <= TypeId::_kVec32End ) ? ID - TypeId::_kVec32Start + TypeId::kI8 : + (ID >= TypeId::_kVec64Start && ID <= TypeId::_kVec64End ) ? ID - TypeId::_kVec64Start + TypeId::kI8 : + (ID >= TypeId::_kVec128Start && ID <= TypeId::_kVec128End) ? ID - TypeId::_kVec128Start + TypeId::kI8 : + (ID >= TypeId::_kVec256Start && ID <= TypeId::_kVec256End) ? ID - TypeId::_kVec256Start + TypeId::kI8 : + (ID >= TypeId::_kVec512Start && ID <= TypeId::_kVec512End) ? ID - TypeId::_kVec512Start + TypeId::kI8 : 0 + }; +}; + +#define R(TMPL, I) TMPL::kValue, TMPL::kValue, \ + TMPL::kValue, TMPL::kValue, \ + TMPL::kValue, TMPL::kValue, \ + TMPL::kValue, TMPL::kValue, \ + TMPL::kValue, TMPL::kValue, \ + TMPL::kValue, TMPL::kValue, \ + TMPL::kValue, TMPL::kValue, \ + TMPL::kValue, TMPL::kValue +ASMJIT_API const TypeId::Info TypeId::_info = { + // SizeOf[128] + { + R(TypeIdSizeOf_T, 0), R(TypeIdSizeOf_T, 16), + R(TypeIdSizeOf_T, 32), R(TypeIdSizeOf_T, 48), + R(TypeIdSizeOf_T, 64), R(TypeIdSizeOf_T, 80), + R(TypeIdSizeOf_T, 96), R(TypeIdSizeOf_T, 112) + }, + + // ElementOf[128] + { + R(TypeIdElementOf_T, 0), R(TypeIdElementOf_T, 16), + R(TypeIdElementOf_T, 32), R(TypeIdElementOf_T, 48), + R(TypeIdElementOf_T, 64), R(TypeIdElementOf_T, 80), + R(TypeIdElementOf_T, 96), R(TypeIdElementOf_T, 112) + } +}; +#undef R + +// ============================================================================ +// [asmjit::Operand - Test] +// ============================================================================ + +#if defined(ASMJIT_TEST) +UNIT(base_operand) { + INFO("Checking operand sizes"); + EXPECT(sizeof(Operand) == 16); + EXPECT(sizeof(Reg) == 16); + EXPECT(sizeof(Mem) == 16); + EXPECT(sizeof(Imm) == 16); + EXPECT(sizeof(Label) == 16); + + INFO("Checking basic functionality of Operand"); + Operand a, b; + Operand dummy; + + EXPECT(a.isNone() == true); + EXPECT(a.isReg() == false); + EXPECT(a.isMem() == false); + EXPECT(a.isImm() == false); + EXPECT(a.isLabel() == false); + EXPECT(a == b); + + EXPECT(a._any.reserved8_4 == 0, "Default constructed Operand should zero its 'reserved8_4' field"); + EXPECT(a._any.reserved12_4 == 0, "Default constructed Operand should zero its 'reserved12_4' field"); + + INFO("Checking basic functionality of Label"); + Label label; + EXPECT(label.isValid() == false); + EXPECT(label.getId() == 0); + + INFO("Checking basic functionality of Reg"); + EXPECT(Reg().isValid() == false, + "Default constructed Reg() should not be valid"); + EXPECT(Reg()._any.reserved8_4 == 0, + "A default constructed Reg() should zero its 'reserved8_4' field"); + EXPECT(Reg()._any.reserved12_4 == 0, + "A default constructed Reg() should zero its 'reserved12_4' field"); + + EXPECT(Reg().isReg() == false, + "Default constructed register should not isReg()"); + EXPECT(dummy.as().isValid() == false, + "Default constructed Operand casted to Reg should not be valid"); + + // Create some register (not specific to any architecture). + uint32_t rSig = Operand::kOpReg | (1 << Operand::kSignatureRegTypeShift) | + (2 << Operand::kSignatureRegKindShift) | + (8 << Operand::kSignatureSizeShift ) ; + Reg r1(Reg::fromSignature(rSig, 5)); + + EXPECT(r1.isValid() == true); + EXPECT(r1.isReg() == true); + EXPECT(r1.isReg(1) == true); + EXPECT(r1.isPhysReg() == true); + EXPECT(r1.isVirtReg() == false); + EXPECT(r1.getSignature() == rSig); + EXPECT(r1.getType() == 1); + EXPECT(r1.getKind() == 2); + EXPECT(r1.getSize() == 8); + EXPECT(r1.getId() == 5); + EXPECT(r1.isReg(1, 5) == true); // RegType and Id. + + EXPECT(r1._any.reserved8_4 == 0, "Reg should have 'reserved8_4' zero"); + EXPECT(r1._any.reserved12_4 == 0, "Reg should have 'reserved12_4' zero"); + + // The same type of register having different id. + Reg r2(r1, 6); + EXPECT(r2.isValid() == true); + EXPECT(r2.isReg() == true); + EXPECT(r2.isReg(1) == true); + EXPECT(r2.isPhysReg() == true); + EXPECT(r2.isVirtReg() == false); + EXPECT(r2.getSignature() == rSig); + EXPECT(r2.getType() == r1.getType()); + EXPECT(r2.getKind() == r1.getKind()); + EXPECT(r2.getSize() == r1.getSize()); + EXPECT(r2.getId() == 6); + EXPECT(r2.isReg(1, 6) == true); + + r1.reset(); + EXPECT(!r1.isValid(), + "Reset register should not be valid"); + EXPECT(!r1.isReg(), + "Reset register should not isReg()"); + + INFO("Checking basic functionality of Mem"); + Mem m; + EXPECT(m.isMem() , "Default constructed Mem() should isMem()"); + EXPECT(m == Mem() , "Two default constructed Mem() operands should be equal"); + EXPECT(m.hasBase() == false , "Default constructed Mem() should not have base specified"); + EXPECT(m.hasIndex() == false , "Default constructed Mem() should not have index specified"); + EXPECT(m.has64BitOffset() == true , "Default constructed Mem() should report 64-bit offset"); + EXPECT(m.getOffset() == 0 , "Default constructed Mem() should have be zero offset / address"); + + m.setOffset(-1); + EXPECT(m.getOffsetLo32() == -1 , "Memory operand must hold a 32-bit offset"); + EXPECT(m.getOffset() == -1 , "32-bit offset must be sign extended to 64 bits"); + + int64_t x = int64_t(ASMJIT_UINT64_C(0xFF00FF0000000001)); + m.setOffset(x); + EXPECT(m.getOffset() == x , "Memory operand must hold a 64-bit offset"); + EXPECT(m.getOffsetLo32() == 1 , "Memory operand must return correct low offset DWORD"); + EXPECT(m.getOffsetHi32() == 0xFF00FF00, "Memory operand must return correct high offset DWORD"); + + INFO("Checking basic functionality of Imm"); + EXPECT(Imm(-1).getInt64() == int64_t(-1), + "Immediate values should by default sign-extend to 64-bits"); +} +#endif // ASMJIT_TEST + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" diff --git a/libraries/asmjit/asmjit/base/operand.h b/libraries/asmjit/asmjit/base/operand.h new file mode 100644 index 00000000000..5d9fb8205f7 --- /dev/null +++ b/libraries/asmjit/asmjit/base/operand.h @@ -0,0 +1,1574 @@ +// [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] +#include "../base/utils.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +#ifdef _MSC_VER +#pragma warning(disable: 4804) // warning C4804: '~': unsafe use of type 'bool' in operation +#endif + +namespace asmjit { + +//! \addtogroup asmjit_base +//! \{ + +// ============================================================================ +// [asmjit::Operand_] +// ============================================================================ + +//! Constructor-less \ref Operand. +//! +//! Contains no initialization code and can be used safely to define an array +//! of operands that won't be initialized. This is a \ref Operand compatible +//! data structure designed to be statically initialized or `static const`. +struct Operand_ { + // -------------------------------------------------------------------------- + // [Operand Type] + // -------------------------------------------------------------------------- + + //! Operand types that can be encoded in \ref Operand. + ASMJIT_ENUM(OpType) { + kOpNone = 0, //!< Not an operand or not initialized. + kOpReg = 1, //!< Operand is a register. + kOpMem = 2, //!< Operand is a memory. + kOpImm = 3, //!< Operand is an immediate value. + kOpLabel = 4 //!< Operand is a label. + }; + + // -------------------------------------------------------------------------- + // [Operand Signature (Bits)] + // -------------------------------------------------------------------------- + + ASMJIT_ENUM(SignatureBits) { + // Operand type (3 least significant bits). + // |........|........|........|.....XXX| + kSignatureOpShift = 0, + kSignatureOpBits = 0x07U, + kSignatureOpMask = kSignatureOpBits << kSignatureOpShift, + + // Operand size (8 most significant bits). + // |XXXXXXXX|........|........|........| + kSignatureSizeShift = 24, + kSignatureSizeBits = 0xFFU, + kSignatureSizeMask = kSignatureSizeBits << kSignatureSizeShift, + + // Register type (5 bits). + // |........|........|........|XXXXX...| + kSignatureRegTypeShift = 3, + kSignatureRegTypeBits = 0x1FU, + kSignatureRegTypeMask = kSignatureRegTypeBits << kSignatureRegTypeShift, + + // Register kind (4 bits). + // |........|........|....XXXX|........| + kSignatureRegKindShift = 8, + kSignatureRegKindBits = 0x0FU, + kSignatureRegKindMask = kSignatureRegKindBits << kSignatureRegKindShift, + + // Memory base type (5 bits). + // |........|........|........|XXXXX...| + kSignatureMemBaseTypeShift = 3, + kSignatureMemBaseTypeBits = 0x1FU, + kSignatureMemBaseTypeMask = kSignatureMemBaseTypeBits << kSignatureMemBaseTypeShift, + + // Memory index type (5 bits). + // |........|........|...XXXXX|........| + kSignatureMemIndexTypeShift = 8, + kSignatureMemIndexTypeBits = 0x1FU, + kSignatureMemIndexTypeMask = kSignatureMemIndexTypeBits << kSignatureMemIndexTypeShift, + + // Memory base+index combined (10 bits). + // |........|........|...XXXXX|XXXXX...| + kSignatureMemBaseIndexShift = 3, + kSignatureMemBaseIndexBits = 0x3FFU, + kSignatureMemBaseIndexMask = kSignatureMemBaseIndexBits << kSignatureMemBaseIndexShift, + + // Memory should be encoded as absolute immediate (X86|X64). + // |........|........|.XX.....|........| + kSignatureMemAddrTypeShift = 13, + kSignatureMemAddrTypeBits = 0x03U, + kSignatureMemAddrTypeMask = kSignatureMemAddrTypeBits << kSignatureMemAddrTypeShift, + + // This memory operand represents a function argument's stack location (CodeCompiler) + // |........|........|.X......|........| + kSignatureMemArgHomeShift = 15, + kSignatureMemArgHomeBits = 0x01U, + kSignatureMemArgHomeFlag = kSignatureMemArgHomeBits << kSignatureMemArgHomeShift, + + // This memory operand represents a virtual register's home-slot (CodeCompiler). + // |........|........|X.......|........| + kSignatureMemRegHomeShift = 16, + kSignatureMemRegHomeBits = 0x01U, + kSignatureMemRegHomeFlag = kSignatureMemRegHomeBits << kSignatureMemRegHomeShift + }; + + // -------------------------------------------------------------------------- + // [Operand Id] + // -------------------------------------------------------------------------- + + //! Operand id helpers useful for id <-> index translation. + ASMJIT_ENUM(PackedId) { + //! Minimum valid packed-id. + kPackedIdMin = 0x00000100U, + //! Maximum valid packed-id. + kPackedIdMax = 0xFFFFFFFFU, + //! Count of valid packed-ids. + kPackedIdCount = kPackedIdMax - kPackedIdMin + 1 + }; + + // -------------------------------------------------------------------------- + // [Operand Utilities] + // -------------------------------------------------------------------------- + + //! Get if the given `id` is a valid packed-id that can be used by Operand. + //! Packed ids are those equal or greater than `kPackedIdMin` and lesser or + //! equal to `kPackedIdMax`. This concept was created to support virtual + //! registers and to make them distinguishable from physical ones. It allows + //! a single uint32_t to contain either physical register id or virtual + //! register id represented as `packed-id`. This concept is used also for + //! labels to make the API consistent. + static ASMJIT_INLINE bool isPackedId(uint32_t id) noexcept { return id - kPackedIdMin < kPackedIdCount; } + //! Convert a real-id into a packed-id that can be stored in Operand. + static ASMJIT_INLINE uint32_t packId(uint32_t id) noexcept { return id + kPackedIdMin; } + //! Convert a packed-id back to real-id. + static ASMJIT_INLINE uint32_t unpackId(uint32_t id) noexcept { return id - kPackedIdMin; } + + // -------------------------------------------------------------------------- + // [Operand Data] + // -------------------------------------------------------------------------- + + //! Any operand. + struct AnyData { + uint32_t signature; //!< Type of the operand (see \ref OpType) and other data. + uint32_t id; //!< Operand id or `0`. + uint32_t reserved8_4; //!< \internal + uint32_t reserved12_4; //!< \internal + }; + + //! Register operand data. + struct RegData { + uint32_t signature; //!< Type of the operand (always \ref kOpReg) and other data. + uint32_t id; //!< Physical or virtual register id. + uint32_t reserved8_4; //!< \internal + uint32_t reserved12_4; //!< \internal + }; + + //! Memory operand data. + struct MemData { + uint32_t signature; //!< Type of the operand (always \ref kOpMem) and other data. + uint32_t index; //!< INDEX register id or `0`. + + // [BASE + OFF32] vs just [OFF64]. + union { + uint64_t offset64; //!< 64-bit offset, combining low and high 32-bit parts. + struct { +#if ASMJIT_ARCH_LE + uint32_t offsetLo32; //!< 32-bit low offset part. + uint32_t base; //!< 32-bit high offset part or BASE. +#else + uint32_t base; //!< 32-bit high offset part or BASE. + uint32_t offsetLo32; //!< 32-bit low offset part. +#endif + }; + }; + }; + + //! Immediate operand data. + struct ImmData { + uint32_t signature; //!< Type of the operand (always \ref kOpImm) and other data. + uint32_t id; //!< Immediate id (always `0`). + UInt64 value; //!< Immediate value. + }; + + //! Label operand data. + struct LabelData { + uint32_t signature; //!< Type of the operand (always \ref kOpLabel) and other data. + uint32_t id; //!< Label id (`0` if not initialized). + uint32_t reserved8_4; //!< \internal + uint32_t reserved12_4; //!< \internal + }; + + // -------------------------------------------------------------------------- + // [Init & Copy] + // -------------------------------------------------------------------------- + + //! \internal + //! + //! Initialize the operand to `other` (used by constructors). + ASMJIT_INLINE void _init(const Operand_& other) noexcept { ::memcpy(this, &other, sizeof(Operand_)); } + + //! \internal + ASMJIT_INLINE void _initReg(uint32_t signature, uint32_t rd) { + _init_packed_d0_d1(signature, rd); + _init_packed_d2_d3(0, 0); + } + + //! \internal + ASMJIT_INLINE void _init_packed_d0_d1(uint32_t d0, uint32_t d1) noexcept { _packed[0].setPacked_2x32(d0, d1); } + //! \internal + ASMJIT_INLINE void _init_packed_d2_d3(uint32_t d2, uint32_t d3) noexcept { _packed[1].setPacked_2x32(d2, d3); } + + //! \internal + //! + //! Initialize the operand from `other` (used by operator overloads). + ASMJIT_INLINE void copyFrom(const Operand_& other) noexcept { ::memcpy(this, &other, sizeof(Operand_)); } + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get if the operand matches the given signature `sign`. + ASMJIT_INLINE bool hasSignature(uint32_t signature) const noexcept { return _signature == signature; } + + //! Get if the operand matches a signature of the `other` operand. + ASMJIT_INLINE bool hasSignature(const Operand_& other) const noexcept { + return _signature == other.getSignature(); + } + + //! Get a 32-bit operand signature. + //! + //! Signature is first 4 bytes of the operand data. It's used mostly for + //! operand checking as it's much faster to check 4 bytes at once than having + //! to check these bytes individually. + ASMJIT_INLINE uint32_t getSignature() const noexcept { return _signature; } + + //! Set the operand signature (see \ref getSignature). + //! + //! Improper use of `setSignature()` can lead to hard-to-debug errors. + ASMJIT_INLINE void setSignature(uint32_t signature) noexcept { _signature = signature; } + + ASMJIT_INLINE bool _hasSignatureData(uint32_t bits) const noexcept { return (_signature & bits) != 0; } + + //! \internal + //! + //! Unpacks information from operand's signature. + ASMJIT_INLINE uint32_t _getSignatureData(uint32_t bits, uint32_t shift) const noexcept { return (_signature >> shift) & bits; } + + //! \internal + //! + //! Packs information to operand's signature. + ASMJIT_INLINE void _setSignatureData(uint32_t value, uint32_t bits, uint32_t shift) noexcept { + ASMJIT_ASSERT(value <= bits); + _signature = (_signature & ~(bits << shift)) | (value << shift); + } + + ASMJIT_INLINE void _addSignatureData(uint32_t data) noexcept { _signature |= data; } + + //! Clears specified bits in operand's signature. + ASMJIT_INLINE void _clearSignatureData(uint32_t bits, uint32_t shift) noexcept { _signature &= ~(bits << shift); } + + //! Get type of the operand, see \ref OpType. + ASMJIT_INLINE uint32_t getOp() const noexcept { return _getSignatureData(kSignatureOpBits, kSignatureOpShift); } + //! Get if the operand is none (\ref kOpNone). + ASMJIT_INLINE bool isNone() const noexcept { return getOp() == 0; } + //! Get if the operand is a register (\ref kOpReg). + ASMJIT_INLINE bool isReg() const noexcept { return getOp() == kOpReg; } + //! Get if the operand is a memory location (\ref kOpMem). + ASMJIT_INLINE bool isMem() const noexcept { return getOp() == kOpMem; } + //! Get if the operand is an immediate (\ref kOpImm). + ASMJIT_INLINE bool isImm() const noexcept { return getOp() == kOpImm; } + //! Get if the operand is a label (\ref kOpLabel). + ASMJIT_INLINE bool isLabel() const noexcept { return getOp() == kOpLabel; } + + //! Get if the operand is a physical register. + ASMJIT_INLINE bool isPhysReg() const noexcept { return isReg() && _reg.id < Globals::kInvalidRegId; } + //! Get if the operand is a virtual register. + ASMJIT_INLINE bool isVirtReg() const noexcept { return isReg() && isPackedId(_reg.id); } + + //! Get if the operand specifies a size (i.e. the size is not zero). + ASMJIT_INLINE bool hasSize() const noexcept { return _hasSignatureData(kSignatureSizeMask); } + //! Get if the size of the operand matches `size`. + ASMJIT_INLINE bool hasSize(uint32_t size) const noexcept { return getSize() == size; } + + //! Get size of the operand (in bytes). + //! + //! The value returned depends on the operand type: + //! * None - Should always return zero size. + //! * Reg - Should always return the size of the register. If the register + //! size depends on architecture (like \ref X86CReg and \ref X86DReg) + //! the size returned should be the greatest possible (so it should + //! return 64-bit size in such case). + //! * Mem - Size is optional and will be in most cases zero. + //! * Imm - Should always return zero size. + //! * Label - Should always return zero size. + ASMJIT_INLINE uint32_t getSize() const noexcept { return _getSignatureData(kSignatureSizeBits, kSignatureSizeShift); } + + //! Get the operand id. + //! + //! The value returned should be interpreted accordingly to the operand type: + //! * None - Should be `0`. + //! * Reg - Physical or virtual register id. + //! * Mem - Multiple meanings - BASE address (register or label id), or + //! high value of a 64-bit absolute address. + //! * Imm - Should be `0`. + //! * Label - Label id if it was created by using `newLabel()` or `0` + //! if the label is invalid or uninitialized. + ASMJIT_INLINE uint32_t getId() const noexcept { return _any.id; } + + //! Get if the operand is 100% equal to `other`. + ASMJIT_INLINE bool isEqual(const Operand_& other) const noexcept { + return (_packed[0] == other._packed[0]) & + (_packed[1] == other._packed[1]) ; + } + + //! Get if the operand is a register matching `rType`. + ASMJIT_INLINE bool isReg(uint32_t rType) const noexcept { + const uint32_t kMsk = (kSignatureOpBits << kSignatureOpShift) | (kSignatureRegTypeBits << kSignatureRegTypeShift); + const uint32_t kSgn = (kOpReg << kSignatureOpShift) | (rType << kSignatureRegTypeShift); + return (_signature & kMsk) == kSgn; + } + + //! Get whether the operand is register and of `type` and `id`. + ASMJIT_INLINE bool isReg(uint32_t rType, uint32_t rId) const noexcept { + return isReg(rType) && getId() == rId; + } + + //! Get whether the operand is a register or memory. + ASMJIT_INLINE bool isRegOrMem() const noexcept { + ASMJIT_ASSERT(kOpMem - kOpReg == 1); + return Utils::inInterval(getOp(), kOpReg, kOpMem); + } + + //! Cast this operand to `T` type. + template + ASMJIT_INLINE T& as() noexcept { return static_cast(*this); } + //! Cast this operand to `T` type (const). + template + ASMJIT_INLINE const T& as() const noexcept { return static_cast(*this); } + + // -------------------------------------------------------------------------- + // [Reset] + // -------------------------------------------------------------------------- + + //! Reset the `Operand` to none. + //! + //! None operand is defined the following way: + //! - Its signature is zero (kOpNone, and the rest zero as well). + //! - Its id is `0`. + //! - The reserved8_4 field is set to `0`. + //! - The reserved12_4 field is set to zero. + //! + //! In other words, reset operands have all members set to zero. Reset operand + //! must match the Operand state right after its construction. Alternatively, + //! if you have an array of operands, you can simply use `memset()`. + //! + //! ``` + //! using namespace asmjit; + //! + //! Operand a; + //! Operand b; + //! assert(a == b); + //! + //! b = x86::eax; + //! assert(a != b); + //! + //! b.reset(); + //! assert(a == b); + //! + //! memset(&b, 0, sizeof(Operand)); + //! assert(a == b); + //! ``` + ASMJIT_INLINE void reset() noexcept { + _init_packed_d0_d1(kOpNone, 0); + _init_packed_d2_d3(0, 0); + } + + // -------------------------------------------------------------------------- + // [Operator Overload] + // -------------------------------------------------------------------------- + + template + ASMJIT_INLINE bool operator==(const T& other) const noexcept { return isEqual(other); } + template + ASMJIT_INLINE bool operator!=(const T& other) const noexcept { return !isEqual(other); } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + union { + AnyData _any; //!< Generic data. + RegData _reg; //!< Physical or virtual register data. + MemData _mem; //!< Memory address data. + ImmData _imm; //!< Immediate value data. + LabelData _label; //!< Label data. + + uint32_t _signature; //!< Operand signature (first 32-bits). + UInt64 _packed[2]; //!< Operand packed into two 64-bit integers. + }; +}; + +// ============================================================================ +// [asmjit::Operand] +// ============================================================================ + +//! Operand can contain register, memory location, immediate, or label. +class Operand : public Operand_ { +public: + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create an uninitialized operand. + ASMJIT_INLINE Operand() noexcept { reset(); } + //! Create a reference to `other` operand. + ASMJIT_INLINE Operand(const Operand& other) noexcept { _init(other); } + //! Create a reference to `other` operand. + explicit ASMJIT_INLINE Operand(const Operand_& other) noexcept { _init(other); } + //! Create a completely uninitialized operand (dangerous). + explicit ASMJIT_INLINE Operand(const _NoInit&) noexcept {} + + // -------------------------------------------------------------------------- + // [Clone] + // -------------------------------------------------------------------------- + + //! Clone the `Operand`. + ASMJIT_INLINE Operand clone() const noexcept { return Operand(*this); } + + ASMJIT_INLINE Operand& operator=(const Operand_& other) noexcept { copyFrom(other); return *this; } +}; + +// ============================================================================ +// [asmjit::Label] +// ============================================================================ + +//! Label (jump target or data location). +//! +//! Label represents a location in code typically used as a jump target, but +//! may be also a reference to some data or a static variable. Label has to be +//! explicitly created by CodeEmitter. +//! +//! Example of using labels: +//! +//! ~~~ +//! // Create a CodeEmitter (for example X86Assembler). +//! X86Assembler a; +//! +//! // Create Label instance. +//! Label L1 = a.newLabel(); +//! +//! // ... your code ... +//! +//! // Using label. +//! a.jump(L1); +//! +//! // ... your code ... +//! +//! // Bind label to the current position, see `CodeEmitter::bind()`. +//! a.bind(L1); +//! ~~~ +class Label : public Operand { +public: + //! Type of the Label. + enum Type { + kTypeAnonymous = 0, //!< Anonymous (unnamed) label. + kTypeLocal = 1, //!< Local label (always has parentId). + kTypeGlobal = 2, //!< Global label (never has parentId). + kTypeCount = 3 //!< Number of label types. + }; + + // TODO: Find a better place, find a better name. + enum { + //! Label tag is used as a sub-type, forming a unique signature across all + //! operand types as 0x1 is never associated with any register (reg-type). + //! This means that a memory operand's BASE register can be constructed + //! from virtually any operand (register vs. label) by just assigning its + //! type (reg type or label-tag) and operand id. + kLabelTag = 0x1 + }; + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create new, unassociated label. + ASMJIT_INLINE Label() noexcept : Operand(NoInit) { reset(); } + //! Create a reference to another label. + ASMJIT_INLINE Label(const Label& other) noexcept : Operand(other) {} + + explicit ASMJIT_INLINE Label(uint32_t id) noexcept : Operand(NoInit) { + _init_packed_d0_d1(kOpLabel, id); + _init_packed_d2_d3(0, 0); + } + + explicit ASMJIT_INLINE Label(const _NoInit&) noexcept : Operand(NoInit) {} + + // -------------------------------------------------------------------------- + // [Reset] + // -------------------------------------------------------------------------- + + // TODO: I think that if operand is reset it shouldn't say it's a Label, it + // should be none like all other operands. + ASMJIT_INLINE void reset() noexcept { + _init_packed_d0_d1(kOpLabel, 0); + _init_packed_d2_d3(0, 0); + } + + // -------------------------------------------------------------------------- + // [Label Specific] + // -------------------------------------------------------------------------- + + //! Get if the label was created by CodeEmitter and has an assigned id. + ASMJIT_INLINE bool isValid() const noexcept { return _label.id != 0; } + //! Set label id. + ASMJIT_INLINE void setId(uint32_t id) { _label.id = id; } + + // -------------------------------------------------------------------------- + // [Operator Overload] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE Label& operator=(const Label& other) noexcept { copyFrom(other); return *this; } +}; + +// ============================================================================ +// [asmjit::Reg] +// ============================================================================ + +#define ASMJIT_DEFINE_REG_TRAITS(TRAITS_T, REG_T, TYPE, KIND, SIZE, COUNT, TYPE_ID) \ +template<> \ +struct TRAITS_T < TYPE > { \ + typedef REG_T Reg; \ + \ + enum { \ + kValid = 1, \ + kCount = COUNT, \ + kTypeId = TYPE_ID, \ + \ + kType = TYPE, \ + kKind = KIND, \ + kSize = SIZE, \ + kSignature = (Operand::kOpReg << Operand::kSignatureOpShift ) | \ + (kType << Operand::kSignatureRegTypeShift) | \ + (kKind << Operand::kSignatureRegKindShift) | \ + (kSize << Operand::kSignatureSizeShift ) \ + }; \ +} \ + +#define ASMJIT_DEFINE_ABSTRACT_REG(REG_T, BASE_T) \ +public: \ + /*! Default constructor doesn't setup anything, it's like `Operand()`. */ \ + ASMJIT_INLINE REG_T() ASMJIT_NOEXCEPT \ + : BASE_T() {} \ + \ + /*! Copy the `other` REG_T register operand. */ \ + ASMJIT_INLINE REG_T(const REG_T& other) ASMJIT_NOEXCEPT \ + : BASE_T(other) {} \ + \ + /*! Copy the `other` REG_T register operand having its id set to `rId` */ \ + ASMJIT_INLINE REG_T(const Reg& other, uint32_t rId) ASMJIT_NOEXCEPT \ + : BASE_T(other, rId) {} \ + \ + /*! Create a REG_T register operand based on `signature` and `rId`. */ \ + ASMJIT_INLINE REG_T(const _Init& init, uint32_t signature, uint32_t rId) ASMJIT_NOEXCEPT \ + : BASE_T(init, signature, rId) {} \ + \ + /*! Create a completely uninitialized REG_T register operand (garbage). */ \ + explicit ASMJIT_INLINE REG_T(const _NoInit&) ASMJIT_NOEXCEPT \ + : BASE_T(NoInit) {} \ + \ + /*! Clone the register operand. */ \ + ASMJIT_INLINE REG_T clone() const ASMJIT_NOEXCEPT { return REG_T(*this); } \ + \ + /*! Create a new register from register type and id. */ \ + static ASMJIT_INLINE REG_T fromTypeAndId(uint32_t rType, uint32_t rId) ASMJIT_NOEXCEPT { \ + return REG_T(Init, signatureOf(rType), rId); \ + } \ + \ + /*! Create a new register from signature and id. */ \ + static ASMJIT_INLINE REG_T fromSignature(uint32_t signature, uint32_t rId) ASMJIT_NOEXCEPT { \ + return REG_T(Init, signature, rId); \ + } \ + \ + ASMJIT_INLINE REG_T& operator=(const REG_T& other) ASMJIT_NOEXCEPT { \ + copyFrom(other); return *this; \ + } + +#define ASMJIT_DEFINE_FINAL_REG(REG_T, BASE_T, TRAITS_T) \ + ASMJIT_DEFINE_ABSTRACT_REG(REG_T, BASE_T) \ + \ + /*! Create a REG_T register with `id`. */ \ + explicit ASMJIT_INLINE REG_T(uint32_t rId) ASMJIT_NOEXCEPT \ + : BASE_T(Init, kSignature, rId) {} \ + \ + enum { \ + kThisType = TRAITS_T::kType, \ + kThisKind = TRAITS_T::kKind, \ + kThisSize = TRAITS_T::kSize, \ + kSignature = TRAITS_T::kSignature \ + }; + +//! Structure that contains core register information. +//! +//! This information is compatible with operand's signature (32-bit integer) +//! and `RegInfo` just provides easy way to access it. +struct RegInfo { + ASMJIT_INLINE uint32_t getSignature() const noexcept { + return _signature; + } + + ASMJIT_INLINE uint32_t getOp() const noexcept { + return (_signature >> Operand::kSignatureOpShift) & Operand::kSignatureOpBits; + } + + ASMJIT_INLINE uint32_t getType() const noexcept { + return (_signature >> Operand::kSignatureRegTypeShift) & Operand::kSignatureRegTypeBits; + } + + ASMJIT_INLINE uint32_t getKind() const noexcept { + return (_signature >> Operand::kSignatureRegKindShift) & Operand::kSignatureRegKindBits; + } + + ASMJIT_INLINE uint32_t getSize() const noexcept { + return (_signature >> Operand::kSignatureSizeShift) & Operand::kSignatureSizeBits; + } + + uint32_t _signature; +}; + +//! Physical/Virtual register operand. +class Reg : public Operand { +public: + //! Architecture neutral register types. + //! + //! These must be reused by any platform that contains that types. All GP + //! and VEC registers are also allowed by design to be part of a BASE|INDEX + //! of a memory operand. + ASMJIT_ENUM(RegType) { + kRegNone = 0, //!< No register - unused, invalid, multiple meanings. + // (1 is used as a LabelTag) + kRegGp8Lo = 2, //!< 8-bit low general purpose register (X86). + kRegGp8Hi = 3, //!< 8-bit high general purpose register (X86). + kRegGp16 = 4, //!< 16-bit general purpose register (X86). + kRegGp32 = 5, //!< 32-bit general purpose register (X86|ARM). + kRegGp64 = 6, //!< 64-bit general purpose register (X86|ARM). + kRegVec32 = 7, //!< 32-bit view of a vector register (ARM). + kRegVec64 = 8, //!< 64-bit view of a vector register (ARM). + kRegVec128 = 9, //!< 128-bit view of a vector register (X86|ARM). + kRegVec256 = 10, //!< 256-bit view of a vector register (X86). + kRegVec512 = 11, //!< 512-bit view of a vector register (X86). + kRegVec1024 = 12, //!< 1024-bit view of a vector register (future). + kRegVec2048 = 13, //!< 2048-bit view of a vector register (future). + kRegIP = 14, //!< Universal id of IP/PC register (if separate). + kRegCustom = 15, //!< Start of platform dependent register types (must be honored). + kRegMax = 31 //!< Maximum possible register id of all architectures. + }; + + //! Architecture neutral register kinds. + ASMJIT_ENUM(Kind) { + kKindGp = 0, //!< General purpose register (X86|ARM). + kKindVec = 1, //!< Vector register (X86|ARM). + kKindMax = 15 //!< Maximum possible register kind of all architectures. + }; + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a dummy register operand. + ASMJIT_INLINE Reg() noexcept : Operand() {} + //! Create a new register operand which is the same as `other` . + ASMJIT_INLINE Reg(const Reg& other) noexcept : Operand(other) {} + //! Create a new register operand compatible with `other`, but with a different `rId`. + ASMJIT_INLINE Reg(const Reg& other, uint32_t rId) noexcept : Operand(NoInit) { + _init_packed_d0_d1(other._signature, rId); + _packed[1] = other._packed[1]; + } + + //! Create a register initialized to `signature` and `rId`. + ASMJIT_INLINE Reg(const _Init&, uint32_t signature, uint32_t rId) noexcept : Operand(NoInit) { + _initReg(signature, rId); + } + explicit ASMJIT_INLINE Reg(const _NoInit&) noexcept : Operand(NoInit) {} + + //! Create a new register based on `signature` and `rId`. + static ASMJIT_INLINE Reg fromSignature(uint32_t signature, uint32_t rId) noexcept { return Reg(Init, signature, rId); } + + // -------------------------------------------------------------------------- + // [Reg Specific] + // -------------------------------------------------------------------------- + + //! Get if the register is valid (either virtual or physical). + ASMJIT_INLINE bool isValid() const noexcept { return _signature != 0; } + //! Get if this is a physical register. + ASMJIT_INLINE bool isPhysReg() const noexcept { return _reg.id < Globals::kInvalidRegId; } + //! Get if this is a virtual register (used by \ref CodeCompiler). + ASMJIT_INLINE bool isVirtReg() const noexcept { return isPackedId(_reg.id); } + + //! Get if this register is the same as `other`. + //! + //! This is just an optimization. Registers by default only use the first + //! 8 bytes of the Operand, so this method takes advantage of this knowledge + //! and only compares these 8 bytes. If both operands were created correctly + //! then `isEqual()` and `isSame()` should give the same answer, however, if + //! some operands contains a garbage or other metadata in the upper 8 bytes + //! then `isSame()` may return `true` in cases where `isEqual()` returns + //! false. However. no such case is known at the moment. + ASMJIT_INLINE bool isSame(const Reg& other) const noexcept { return _packed[0] == other._packed[0]; } + + //! Get if the register type matches `rType` - same as `isReg(rType)`, provided for convenience. + ASMJIT_INLINE bool isType(uint32_t rType) const noexcept { return (_signature & kSignatureRegTypeMask) == (rType << kSignatureRegTypeShift); } + //! Get if the register kind matches `rKind`. + ASMJIT_INLINE bool isKind(uint32_t rKind) const noexcept { return (_signature & kSignatureRegKindMask) == (rKind << kSignatureRegKindShift); } + + //! Get if the register is a general purpose register (any size). + ASMJIT_INLINE bool isGp() const noexcept { return isKind(kKindGp); } + //! Get if the register is a vector register. + ASMJIT_INLINE bool isVec() const noexcept { return isKind(kKindVec); } + + using Operand_::isReg; + + //! Same as `isType()`, provided for convenience. + ASMJIT_INLINE bool isReg(uint32_t rType) const noexcept { return isType(rType); } + //! Get if the register type matches `type` and register id matches `rId`. + ASMJIT_INLINE bool isReg(uint32_t rType, uint32_t rId) const noexcept { return isType(rType) && getId() == rId; } + + //! Get the register type. + ASMJIT_INLINE uint32_t getType() const noexcept { return _getSignatureData(kSignatureRegTypeBits, kSignatureRegTypeShift); } + //! Get the register kind. + ASMJIT_INLINE uint32_t getKind() const noexcept { return _getSignatureData(kSignatureRegKindBits, kSignatureRegKindShift); } + + //! Clone the register operand. + ASMJIT_INLINE Reg clone() const noexcept { return Reg(*this); } + + //! Cast this register to `RegT` by also changing its signature. + //! + //! NOTE: Improper use of `cloneAs()` can lead to hard-to-debug errors. + template + ASMJIT_INLINE RegT cloneAs() const noexcept { return RegT(Init, RegT::kSignature, getId()); } + + //! Cast this register to `other` by also changing its signature. + //! + //! NOTE: Improper use of `cloneAs()` can lead to hard-to-debug errors. + template + ASMJIT_INLINE RegT cloneAs(const RegT& other) const noexcept { return RegT(Init, other.getSignature(), getId()); } + + //! Set the register id to `id`. + ASMJIT_INLINE void setId(uint32_t rId) noexcept { _reg.id = rId; } + + //! Set a 32-bit operand signature based on traits of `RegT`. + template + ASMJIT_INLINE void setSignatureT() noexcept { _signature = RegT::kSignature; } + + //! Set register's `signature` and `rId`. + ASMJIT_INLINE void setSignatureAndId(uint32_t signature, uint32_t rId) noexcept { + _signature = signature; + _reg.id = rId; + } + + // -------------------------------------------------------------------------- + // [Reg Statics] + // -------------------------------------------------------------------------- + + static ASMJIT_INLINE bool isGp(const Operand_& op) noexcept { + // Check operand type and register kind. Not interested in register type and size. + const uint32_t kSgn = (kOpReg << kSignatureOpShift ) | + (kKindGp << kSignatureRegKindShift) ; + return (op.getSignature() & (kSignatureOpMask | kSignatureRegKindMask)) == kSgn; + } + + //! Get if the `op` operand is either a low or high 8-bit GPB register. + static ASMJIT_INLINE bool isVec(const Operand_& op) noexcept { + // Check operand type and register kind. Not interested in register type and size. + const uint32_t kSgn = (kOpReg << kSignatureOpShift ) | + (kKindVec << kSignatureRegKindShift) ; + return (op.getSignature() & (kSignatureOpMask | kSignatureRegKindMask)) == kSgn; + } + + static ASMJIT_INLINE bool isGp(const Operand_& op, uint32_t rId) noexcept { return isGp(op) & (op.getId() == rId); } + static ASMJIT_INLINE bool isVec(const Operand_& op, uint32_t rId) noexcept { return isVec(op) & (op.getId() == rId); } +}; + +// ============================================================================ +// [asmjit::RegOnly] +// ============================================================================ + +//! RegOnly is 8-byte version of `Reg` that only allows to store either `Reg` +//! or nothing. This class was designed to decrease the space consumed by each +//! extra "operand" in `CodeEmitter` and `CBInst` classes. +struct RegOnly { + // -------------------------------------------------------------------------- + // [Init / Reset] + // -------------------------------------------------------------------------- + + //! Initialize the `RegOnly` instance to hold register `signature` and `id`. + ASMJIT_INLINE void init(uint32_t signature, uint32_t id) noexcept { + _signature = signature; + _id = id; + } + + ASMJIT_INLINE void init(const Reg& reg) noexcept { init(reg.getSignature(), reg.getId()); } + ASMJIT_INLINE void init(const RegOnly& reg) noexcept { init(reg.getSignature(), reg.getId()); } + + //! Reset the `RegOnly` to none. + ASMJIT_INLINE void reset() noexcept { init(0, 0); } + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get if the `ExtraReg` is none (same as calling `Operand_::isNone()`). + ASMJIT_INLINE bool isNone() const noexcept { return _signature == 0; } + //! Get if the register is valid (either virtual or physical). + ASMJIT_INLINE bool isValid() const noexcept { return _signature != 0; } + + //! Get if this is a physical register. + ASMJIT_INLINE bool isPhysReg() const noexcept { return _id < Globals::kInvalidRegId; } + //! Get if this is a virtual register (used by \ref CodeCompiler). + ASMJIT_INLINE bool isVirtReg() const noexcept { return Operand::isPackedId(_id); } + + //! Get register signature or 0. + ASMJIT_INLINE uint32_t getSignature() const noexcept { return _signature; } + //! Get register id or 0. + ASMJIT_INLINE uint32_t getId() const noexcept { return _id; } + + //! \internal + //! + //! Unpacks information from operand's signature. + ASMJIT_INLINE uint32_t _getSignatureData(uint32_t bits, uint32_t shift) const noexcept { return (_signature >> shift) & bits; } + + //! Get the register type. + ASMJIT_INLINE uint32_t getType() const noexcept { return _getSignatureData(Operand::kSignatureRegTypeBits, Operand::kSignatureRegTypeShift); } + //! Get the register kind. + ASMJIT_INLINE uint32_t getKind() const noexcept { return _getSignatureData(Operand::kSignatureRegKindBits, Operand::kSignatureRegKindShift); } + + // -------------------------------------------------------------------------- + // [ToReg] + // -------------------------------------------------------------------------- + + //! Convert back to `RegT` operand. + template + ASMJIT_INLINE RegT toReg() const noexcept { return RegT(Init, _signature, _id); } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + //! Type of the operand, either `kOpNone` or `kOpReg`. + uint32_t _signature; + //! Physical or virtual register id. + uint32_t _id; +}; + +// ============================================================================ +// [asmjit::Mem] +// ============================================================================ + +//! Base class for all memory operands. +//! +//! NOTE: It's tricky to pack all possible cases that define a memory operand +//! into just 16 bytes. The `Mem` splits data into the following parts: +//! +//! BASE - Base register or label - requires 36 bits total. 4 bits are used +//! to encode the type of the BASE operand (label vs. register type) and +//! the remaining 32 bits define the BASE id, which can be a physical or +//! virtual register index. If BASE type is zero, which is never used as +//! a register-type and label doesn't use it as well then BASE field +//! contains a high DWORD of a possible 64-bit absolute address, which is +//! possible on X64. +//! +//! INDEX - Index register (or theoretically Label, which doesn't make sense). +//! Encoding is similar to BASE - it also requires 36 bits and splits the +//! encoding to INDEX type (4 bits defining the register type) and id (32-bits). +//! +//! OFFSET - A relative offset of the address. Basically if BASE is specified +//! the relative displacement adjusts BASE and an optional INDEX. if BASE is +//! not specified then the OFFSET should be considered as ABSOLUTE address +//! (at least on X86/X64). In that case its low 32 bits are stored in +//! DISPLACEMENT field and the remaining high 32 bits are stored in BASE. +//! +//! OTHER FIELDS - There is rest 8 bits that can be used for whatever purpose. +//! The X86Mem operand uses these bits to store segment override +//! prefix and index shift (scale). +class Mem : public Operand { +public: + enum AddrType { + kAddrTypeDefault = 0, + kAddrTypeAbs = 1, + kAddrTypeRel = 2, + kAddrTypeWrt = 3 + }; + + // Shortcuts. + enum SignatureMem { + kSignatureMemAbs = kAddrTypeAbs << kSignatureMemAddrTypeShift, + kSignatureMemRel = kAddrTypeRel << kSignatureMemAddrTypeShift, + kSignatureMemWrt = kAddrTypeWrt << kSignatureMemAddrTypeShift + }; + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Construct a default `Mem` operand, that points to [0]. + ASMJIT_INLINE Mem() noexcept : Operand(NoInit) { reset(); } + ASMJIT_INLINE Mem(const Mem& other) noexcept : Operand(other) {} + + ASMJIT_INLINE Mem(const _Init&, + uint32_t baseType, uint32_t baseId, + uint32_t indexType, uint32_t indexId, + int32_t off, uint32_t size, uint32_t flags) noexcept : Operand(NoInit) { + + uint32_t signature = (baseType << kSignatureMemBaseTypeShift ) | + (indexType << kSignatureMemIndexTypeShift) | + (size << kSignatureSizeShift ) ; + + _init_packed_d0_d1(kOpMem | signature | flags, indexId); + _mem.base = baseId; + _mem.offsetLo32 = static_cast(off); + } + explicit ASMJIT_INLINE Mem(const _NoInit&) noexcept : Operand(NoInit) {} + + // -------------------------------------------------------------------------- + // [Mem Specific] + // -------------------------------------------------------------------------- + + //! Clone `Mem` operand. + ASMJIT_INLINE Mem clone() const noexcept { return Mem(*this); } + + //! Reset the memory operand - after reset the memory points to [0]. + ASMJIT_INLINE void reset() noexcept { + _init_packed_d0_d1(kOpMem, 0); + _init_packed_d2_d3(0, 0); + } + + ASMJIT_INLINE bool hasAddrType() const noexcept { return _hasSignatureData(kSignatureMemAddrTypeMask); } + ASMJIT_INLINE uint32_t getAddrType() const noexcept { return _getSignatureData(kSignatureMemAddrTypeBits, kSignatureMemAddrTypeShift); } + ASMJIT_INLINE void setAddrType(uint32_t addrType) noexcept { return _setSignatureData(addrType, kSignatureMemAddrTypeBits, kSignatureMemAddrTypeShift); } + ASMJIT_INLINE void resetAddrType() noexcept { return _clearSignatureData(kSignatureMemAddrTypeBits, kSignatureMemAddrTypeShift); } + + ASMJIT_INLINE bool isAbs() const noexcept { return getAddrType() == kAddrTypeAbs; } + ASMJIT_INLINE bool isRel() const noexcept { return getAddrType() == kAddrTypeRel; } + ASMJIT_INLINE bool isWrt() const noexcept { return getAddrType() == kAddrTypeWrt; } + + ASMJIT_INLINE void setAbs() noexcept { setAddrType(kAddrTypeAbs); } + ASMJIT_INLINE void setRel() noexcept { setAddrType(kAddrTypeRel); } + ASMJIT_INLINE void setWrt() noexcept { setAddrType(kAddrTypeWrt); } + + ASMJIT_INLINE bool isArgHome() const noexcept { return _hasSignatureData(kSignatureMemArgHomeFlag); } + ASMJIT_INLINE bool isRegHome() const noexcept { return _hasSignatureData(kSignatureMemRegHomeFlag); } + + ASMJIT_INLINE void setArgHome() noexcept { _signature |= kSignatureMemArgHomeFlag; } + ASMJIT_INLINE void setRegHome() noexcept { _signature |= kSignatureMemRegHomeFlag; } + + ASMJIT_INLINE void clearArgHome() noexcept { _signature &= ~kSignatureMemArgHomeFlag; } + ASMJIT_INLINE void clearRegHome() noexcept { _signature &= ~kSignatureMemRegHomeFlag; } + + //! Get if the memory operand has a BASE register or label specified. + ASMJIT_INLINE bool hasBase() const noexcept { return (_signature & kSignatureMemBaseTypeMask) != 0; } + //! Get if the memory operand has an INDEX register specified. + ASMJIT_INLINE bool hasIndex() const noexcept { return (_signature & kSignatureMemIndexTypeMask) != 0; } + //! Get whether the memory operand has BASE and INDEX register. + ASMJIT_INLINE bool hasBaseOrIndex() const noexcept { return (_signature & kSignatureMemBaseIndexMask) != 0; } + //! Get whether the memory operand has BASE and INDEX register. + ASMJIT_INLINE bool hasBaseAndIndex() const noexcept { return (_signature & kSignatureMemBaseTypeMask) != 0 && (_signature & kSignatureMemIndexTypeMask) != 0; } + + //! Get if the BASE operand is a register (registers start after `kLabelTag`). + ASMJIT_INLINE bool hasBaseReg() const noexcept { return (_signature & kSignatureMemBaseTypeMask) > (Label::kLabelTag << kSignatureMemBaseTypeShift); } + //! Get if the BASE operand is a label. + ASMJIT_INLINE bool hasBaseLabel() const noexcept { return (_signature & kSignatureMemBaseTypeMask) == (Label::kLabelTag << kSignatureMemBaseTypeShift); } + //! Get if the INDEX operand is a register (registers start after `kLabelTag`). + ASMJIT_INLINE bool hasIndexReg() const noexcept { return (_signature & kSignatureMemIndexTypeMask) > (Label::kLabelTag << kSignatureMemIndexTypeShift); } + + //! Get type of a BASE register (0 if this memory operand doesn't use the BASE register). + //! + //! NOTE: If the returned type is one (a value never associated to a register + //! type) the BASE is not register, but it's a label. One equals to `kLabelTag`. + //! You should always check `hasBaseLabel()` before using `getBaseId()` result. + ASMJIT_INLINE uint32_t getBaseType() const noexcept { return _getSignatureData(kSignatureMemBaseTypeBits, kSignatureMemBaseTypeShift); } + //! Get type of an INDEX register (0 if this memory operand doesn't use the INDEX register). + ASMJIT_INLINE uint32_t getIndexType() const noexcept { return _getSignatureData(kSignatureMemIndexTypeBits, kSignatureMemIndexTypeShift); } + + //! Get both BASE (4:0 bits) and INDEX (9:5 bits) types combined into a single integer. + //! + //! This is used internally for BASE+INDEX validation. + ASMJIT_INLINE uint32_t getBaseIndexType() const noexcept { return _getSignatureData(kSignatureMemBaseIndexBits, kSignatureMemBaseIndexShift); } + + //! Get id of the BASE register or label (if the BASE was specified as label). + ASMJIT_INLINE uint32_t getBaseId() const noexcept { return _mem.base; } + //! Get id of the INDEX register. + ASMJIT_INLINE uint32_t getIndexId() const noexcept { return _mem.index; } + + ASMJIT_INLINE void _setBase(uint32_t rType, uint32_t rId) noexcept { + _setSignatureData(rType, kSignatureMemBaseTypeBits, kSignatureMemBaseTypeShift); + _mem.base = rId; + } + + ASMJIT_INLINE void _setIndex(uint32_t rType, uint32_t rId) noexcept { + _setSignatureData(rType, kSignatureMemIndexTypeBits, kSignatureMemIndexTypeShift); + _mem.index = rId; + } + + ASMJIT_INLINE void setBase(const Reg& base) noexcept { return _setBase(base.getType(), base.getId()); } + ASMJIT_INLINE void setIndex(const Reg& index) noexcept { return _setIndex(index.getType(), index.getId()); } + + //! Reset the memory operand's BASE register / label. + ASMJIT_INLINE void resetBase() noexcept { _setBase(0, 0); } + //! Reset the memory operand's INDEX register. + ASMJIT_INLINE void resetIndex() noexcept { _setIndex(0, 0); } + + //! Set memory operand size. + ASMJIT_INLINE void setSize(uint32_t size) noexcept { + _setSignatureData(size, kSignatureSizeBits, kSignatureSizeShift); + } + + ASMJIT_INLINE bool hasOffset() const noexcept { + int32_t lo = static_cast(_mem.offsetLo32); + int32_t hi = static_cast(_mem.base) & -static_cast(getBaseType() == 0); + return (lo | hi) != 0; + } + + //! Get if the memory operand has 64-bit offset or absolute address. + //! + //! If this is true then `hasBase()` must always report false. + ASMJIT_INLINE bool has64BitOffset() const noexcept { return getBaseType() == 0; } + + //! Get a 64-bit offset or absolute address. + ASMJIT_INLINE int64_t getOffset() const noexcept { + return has64BitOffset() + ? static_cast(_mem.offset64) + : static_cast(static_cast(_mem.offsetLo32)); // Sign-Extend. + } + + //! Get a lower part of a 64-bit offset or absolute address. + ASMJIT_INLINE int32_t getOffsetLo32() const noexcept { return static_cast(_mem.offsetLo32); } + //! Get a higher part of a 64-bit offset or absolute address. + //! + //! NOTE: This function is UNSAFE and returns garbage if `has64BitOffset()` + //! returns false. Never use it blindly without checking it. + ASMJIT_INLINE int32_t getOffsetHi32() const noexcept { return static_cast(_mem.base); } + + //! Set a 64-bit offset or an absolute address to `offset`. + //! + //! NOTE: This functions attempts to set both high and low parts of a 64-bit + //! offset, however, if the operand has a BASE register it will store only the + //! low 32 bits of the offset / address as there is no way to store both BASE + //! and 64-bit offset, and there is currently no architecture that has such + //! capability targeted by AsmJit. + ASMJIT_INLINE void setOffset(int64_t offset) noexcept { + if (has64BitOffset()) + _mem.offset64 = static_cast(offset); + else + _mem.offsetLo32 = static_cast(offset & 0xFFFFFFFF); + } + //! Adjust the offset by a 64-bit `off`. + ASMJIT_INLINE void addOffset(int64_t off) noexcept { + if (has64BitOffset()) + _mem.offset64 += static_cast(off); + else + _mem.offsetLo32 += static_cast(off & 0xFFFFFFFF); + } + //! Reset the memory offset to zero. + ASMJIT_INLINE void resetOffset() noexcept { setOffset(0); } + + //! Set a low 32-bit offset to `off`. + ASMJIT_INLINE void setOffsetLo32(int32_t off) noexcept { + _mem.offsetLo32 = static_cast(off); + } + //! Adjust the offset by `off`. + //! + //! NOTE: This is a fast function that doesn't use the HI 32-bits of a + //! 64-bit offset. Use it only if you know that there is a BASE register + //! and the offset is only 32 bits anyway. + ASMJIT_INLINE void addOffsetLo32(int32_t off) noexcept { + _mem.offsetLo32 += static_cast(off); + } + //! Reset the memory offset to zero. + ASMJIT_INLINE void resetOffsetLo32() noexcept { setOffsetLo32(0); } + + // -------------------------------------------------------------------------- + // [Operator Overload] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE Mem& operator=(const Mem& other) noexcept { copyFrom(other); return *this; } +}; + +// ============================================================================ +// [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. +class Imm : public Operand { +public: + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a new immediate value (initial value is 0). + Imm() noexcept : Operand(NoInit) { + _init_packed_d0_d1(kOpImm, 0); + _imm.value.i64 = 0; + } + + //! Create a new signed immediate value, assigning the value to `val`. + explicit Imm(int64_t val) noexcept : Operand(NoInit) { + _init_packed_d0_d1(kOpImm, 0); + _imm.value.i64 = val; + } + + //! Create a new immediate value from `other`. + ASMJIT_INLINE Imm(const Imm& other) noexcept : Operand(other) {} + + explicit ASMJIT_INLINE Imm(const _NoInit&) noexcept : Operand(NoInit) {} + + // -------------------------------------------------------------------------- + // [Immediate Specific] + // -------------------------------------------------------------------------- + + //! Clone `Imm` operand. + ASMJIT_INLINE Imm clone() const noexcept { return Imm(*this); } + + //! Get whether the immediate can be casted to 8-bit signed integer. + ASMJIT_INLINE bool isInt8() const noexcept { return Utils::isInt8(_imm.value.i64); } + //! Get whether the immediate can be casted to 8-bit unsigned integer. + ASMJIT_INLINE bool isUInt8() const noexcept { return Utils::isUInt8(_imm.value.i64); } + + //! Get whether the immediate can be casted to 16-bit signed integer. + ASMJIT_INLINE bool isInt16() const noexcept { return Utils::isInt16(_imm.value.i64); } + //! Get whether the immediate can be casted to 16-bit unsigned integer. + ASMJIT_INLINE bool isUInt16() const noexcept { return Utils::isUInt16(_imm.value.i64); } + + //! Get whether the immediate can be casted to 32-bit signed integer. + ASMJIT_INLINE bool isInt32() const noexcept { return Utils::isInt32(_imm.value.i64); } + //! Get whether the immediate can be casted to 32-bit unsigned integer. + ASMJIT_INLINE bool isUInt32() const noexcept { return Utils::isUInt32(_imm.value.i64); } + + //! Get immediate value as 8-bit signed integer. + ASMJIT_INLINE int8_t getInt8() const noexcept { return static_cast(_imm.value.i32Lo & 0xFF); } + //! Get immediate value as 8-bit unsigned integer. + ASMJIT_INLINE uint8_t getUInt8() const noexcept { return static_cast(_imm.value.u32Lo & 0xFFU); } + //! Get immediate value as 16-bit signed integer. + ASMJIT_INLINE int16_t getInt16() const noexcept { return static_cast(_imm.value.i32Lo & 0xFFFF);} + //! Get immediate value as 16-bit unsigned integer. + ASMJIT_INLINE uint16_t getUInt16() const noexcept { return static_cast(_imm.value.u32Lo & 0xFFFFU);} + + //! Get immediate value as 32-bit signed integer. + ASMJIT_INLINE int32_t getInt32() const noexcept { return _imm.value.i32Lo; } + //! Get low 32-bit signed integer. + ASMJIT_INLINE int32_t getInt32Lo() const noexcept { return _imm.value.i32Lo; } + //! Get high 32-bit signed integer. + ASMJIT_INLINE int32_t getInt32Hi() const noexcept { return _imm.value.i32Hi; } + + //! Get immediate value as 32-bit unsigned integer. + ASMJIT_INLINE uint32_t getUInt32() const noexcept { return _imm.value.u32Lo; } + //! Get low 32-bit signed integer. + ASMJIT_INLINE uint32_t getUInt32Lo() const noexcept { return _imm.value.u32Lo; } + //! Get high 32-bit signed integer. + ASMJIT_INLINE uint32_t getUInt32Hi() const noexcept { return _imm.value.u32Hi; } + + //! Get immediate value as 64-bit signed integer. + ASMJIT_INLINE int64_t getInt64() const noexcept { return _imm.value.i64; } + //! Get immediate value as 64-bit unsigned integer. + ASMJIT_INLINE uint64_t getUInt64() const noexcept { return _imm.value.u64; } + + //! Get immediate value as `intptr_t`. + ASMJIT_INLINE intptr_t getIntPtr() const noexcept { + if (sizeof(intptr_t) == sizeof(int64_t)) + return static_cast(getInt64()); + else + return static_cast(getInt32()); + } + + //! Get immediate value as `uintptr_t`. + ASMJIT_INLINE uintptr_t getUIntPtr() const noexcept { + if (sizeof(uintptr_t) == sizeof(uint64_t)) + return static_cast(getUInt64()); + else + return static_cast(getUInt32()); + } + + //! Set immediate value to 8-bit signed integer `val`. + ASMJIT_INLINE void setInt8(int8_t val) noexcept { _imm.value.i64 = static_cast(val); } + //! Set immediate value to 8-bit unsigned integer `val`. + ASMJIT_INLINE void setUInt8(uint8_t val) noexcept { _imm.value.u64 = static_cast(val); } + + //! Set immediate value to 16-bit signed integer `val`. + ASMJIT_INLINE void setInt16(int16_t val) noexcept { _imm.value.i64 = static_cast(val); } + //! Set immediate value to 16-bit unsigned integer `val`. + ASMJIT_INLINE void setUInt16(uint16_t val) noexcept { _imm.value.u64 = static_cast(val); } + + //! Set immediate value to 32-bit signed integer `val`. + ASMJIT_INLINE void setInt32(int32_t val) noexcept { _imm.value.i64 = static_cast(val); } + //! Set immediate value to 32-bit unsigned integer `val`. + ASMJIT_INLINE void setUInt32(uint32_t val) noexcept { _imm.value.u64 = static_cast(val); } + + //! Set immediate value to 64-bit signed integer `val`. + ASMJIT_INLINE void setInt64(int64_t val) noexcept { _imm.value.i64 = val; } + //! Set immediate value to 64-bit unsigned integer `val`. + ASMJIT_INLINE void setUInt64(uint64_t val) noexcept { _imm.value.u64 = val; } + //! Set immediate value to intptr_t `val`. + ASMJIT_INLINE void setIntPtr(intptr_t val) noexcept { _imm.value.i64 = static_cast(val); } + //! Set immediate value to uintptr_t `val`. + ASMJIT_INLINE void setUIntPtr(uintptr_t val) noexcept { _imm.value.u64 = static_cast(val); } + + //! Set immediate value as unsigned type to `val`. + ASMJIT_INLINE void setPtr(void* p) noexcept { setIntPtr((uint64_t)p); } + //! Set immediate value to `val`. + template + ASMJIT_INLINE void setValue(T val) noexcept { setIntPtr((int64_t)val); } + + // -------------------------------------------------------------------------- + // [Float] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE void setFloat(float f) noexcept { + _imm.value.f32Lo = f; + _imm.value.u32Hi = 0; + } + + ASMJIT_INLINE void setDouble(double d) noexcept { + _imm.value.f64 = d; + } + + // -------------------------------------------------------------------------- + // [Truncate] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE void truncateTo8Bits() noexcept { + if (ASMJIT_ARCH_64BIT) { + _imm.value.u64 &= static_cast(0x000000FFU); + } + else { + _imm.value.u32Lo &= 0x000000FFU; + _imm.value.u32Hi = 0; + } + } + + ASMJIT_INLINE void truncateTo16Bits() noexcept { + if (ASMJIT_ARCH_64BIT) { + _imm.value.u64 &= static_cast(0x0000FFFFU); + } + else { + _imm.value.u32Lo &= 0x0000FFFFU; + _imm.value.u32Hi = 0; + } + } + + ASMJIT_INLINE void truncateTo32Bits() noexcept { _imm.value.u32Hi = 0; } + + // -------------------------------------------------------------------------- + // [Operator Overload] + // -------------------------------------------------------------------------- + + //! Assign `other` to the immediate operand. + ASMJIT_INLINE Imm& operator=(const Imm& other) noexcept { copyFrom(other); return *this; } +}; + +//! Create a signed immediate operand. +static ASMJIT_INLINE Imm imm(int64_t val) noexcept { return Imm(val); } +//! Create an unsigned immediate operand. +static ASMJIT_INLINE Imm imm_u(uint64_t val) noexcept { return Imm(static_cast(val)); } +//! Create an immediate operand from `p`. +template +static ASMJIT_INLINE Imm imm_ptr(T p) noexcept { return Imm(static_cast((intptr_t)p)); } + +// ============================================================================ +// [asmjit::TypeId] +// ============================================================================ + +//! Type-id. +//! +//! This is an additional information that can be used to describe a physical +//! or virtual register. it's used mostly by CodeCompiler to describe register +//! representation (the kind of data stored in the register and the width used) +//! and it's also used by APIs that allow to describe and work with function +//! signatures. +struct TypeId { + // -------------------------------------------------------------------------- + // [Id] + // -------------------------------------------------------------------------- + + enum Id { + kVoid = 0, + + _kIntStart = 32, + _kIntEnd = 41, + + kIntPtr = 32, + kUIntPtr = 33, + + kI8 = 34, + kU8 = 35, + kI16 = 36, + kU16 = 37, + kI32 = 38, + kU32 = 39, + kI64 = 40, + kU64 = 41, + + _kFloatStart = 42, + _kFloatEnd = 44, + + kF32 = 42, + kF64 = 43, + kF80 = 44, + + _kMaskStart = 45, + _kMaskEnd = 48, + + kMask8 = 45, + kMask16 = 46, + kMask32 = 47, + kMask64 = 48, + + _kMmxStart = 49, + _kMmxEnd = 50, + + kMmx32 = 49, + kMmx64 = 50, + + _kVec32Start = 51, + _kVec32End = 60, + + kI8x4 = 51, + kU8x4 = 52, + kI16x2 = 53, + kU16x2 = 54, + kI32x1 = 55, + kU32x1 = 56, + kF32x1 = 59, + + _kVec64Start = 61, + _kVec64End = 70, + + kI8x8 = 61, + kU8x8 = 62, + kI16x4 = 63, + kU16x4 = 64, + kI32x2 = 65, + kU32x2 = 66, + kI64x1 = 67, + kU64x1 = 68, + kF32x2 = 69, + kF64x1 = 70, + + _kVec128Start = 71, + _kVec128End = 80, + + kI8x16 = 71, + kU8x16 = 72, + kI16x8 = 73, + kU16x8 = 74, + kI32x4 = 75, + kU32x4 = 76, + kI64x2 = 77, + kU64x2 = 78, + kF32x4 = 79, + kF64x2 = 80, + + _kVec256Start = 81, + _kVec256End = 90, + + kI8x32 = 81, + kU8x32 = 82, + kI16x16 = 83, + kU16x16 = 84, + kI32x8 = 85, + kU32x8 = 86, + kI64x4 = 87, + kU64x4 = 88, + kF32x8 = 89, + kF64x4 = 90, + + _kVec512Start = 91, + _kVec512End = 100, + + kI8x64 = 91, + kU8x64 = 92, + kI16x32 = 93, + kU16x32 = 94, + kI32x16 = 95, + kU32x16 = 96, + kI64x8 = 97, + kU64x8 = 98, + kF32x16 = 99, + kF64x8 = 100, + + kCount = 101 + }; + + // -------------------------------------------------------------------------- + // [TypeName - Used by Templates] + // -------------------------------------------------------------------------- + + struct Int8 {}; //!< int8_t as C++ type-name. + struct UInt8 {}; //!< uint8_t as C++ type-name. + struct Int16 {}; //!< int16_t as C++ type-name. + struct UInt16 {}; //!< uint16_t as C++ type-name. + struct Int32 {}; //!< int32_t as C++ type-name. + struct UInt32 {}; //!< uint32_t as C++ type-name. + struct Int64 {}; //!< int64_t as C++ type-name. + struct UInt64 {}; //!< uint64_t as C++ type-name. + struct IntPtr {}; //!< intptr_t as C++ type-name. + struct UIntPtr {}; //!< uintptr_t as C++ type-name. + struct Float {}; //!< float as C++ type-name. + struct Double {}; //!< double as C++ type-name. + struct MmxReg {}; //!< MMX register as C++ type-name. + struct Vec128 {}; //!< SIMD128/XMM register as C++ type-name. + struct Vec256 {}; //!< SIMD256/YMM register as C++ type-name. + struct Vec512 {}; //!< SIMD512/ZMM register as C++ type-name. + + // -------------------------------------------------------------------------- + // [Utilities] + // -------------------------------------------------------------------------- + + struct Info { + uint8_t sizeOf[128]; + uint8_t elementOf[128]; + }; + + ASMJIT_API static const Info _info; + + static ASMJIT_INLINE bool isVoid(uint32_t typeId) noexcept { return typeId == 0; } + static ASMJIT_INLINE bool isValid(uint32_t typeId) noexcept { return typeId >= _kIntStart && typeId <= _kVec512End; } + static ASMJIT_INLINE bool isAbstract(uint32_t typeId) noexcept { return typeId >= kIntPtr && typeId <= kUIntPtr; } + static ASMJIT_INLINE bool isInt(uint32_t typeId) noexcept { return typeId >= _kIntStart && typeId <= _kIntEnd; } + static ASMJIT_INLINE bool isGpb(uint32_t typeId) noexcept { return typeId >= kI8 && typeId <= kU8; } + static ASMJIT_INLINE bool isGpw(uint32_t typeId) noexcept { return typeId >= kI16 && typeId <= kU16; } + static ASMJIT_INLINE bool isGpd(uint32_t typeId) noexcept { return typeId >= kI32 && typeId <= kU32; } + static ASMJIT_INLINE bool isGpq(uint32_t typeId) noexcept { return typeId >= kI64 && typeId <= kU64; } + static ASMJIT_INLINE bool isFloat(uint32_t typeId) noexcept { return typeId >= _kFloatStart && typeId <= _kFloatEnd; } + static ASMJIT_INLINE bool isMask(uint32_t typeId) noexcept { return typeId >= _kMaskStart && typeId <= _kMaskEnd; } + static ASMJIT_INLINE bool isMmx(uint32_t typeId) noexcept { return typeId >= _kMmxStart && typeId <= _kMmxEnd; } + + static ASMJIT_INLINE bool isVec(uint32_t typeId) noexcept { return typeId >= _kVec32Start && typeId <= _kVec512End; } + static ASMJIT_INLINE bool isVec32(uint32_t typeId) noexcept { return typeId >= _kVec32Start && typeId <= _kVec32End; } + static ASMJIT_INLINE bool isVec64(uint32_t typeId) noexcept { return typeId >= _kVec64Start && typeId <= _kVec64End; } + static ASMJIT_INLINE bool isVec128(uint32_t typeId) noexcept { return typeId >= _kVec128Start && typeId <= _kVec128End; } + static ASMJIT_INLINE bool isVec256(uint32_t typeId) noexcept { return typeId >= _kVec256Start && typeId <= _kVec256End; } + static ASMJIT_INLINE bool isVec512(uint32_t typeId) noexcept { return typeId >= _kVec512Start && typeId <= _kVec512End; } + + static ASMJIT_INLINE uint32_t sizeOf(uint32_t typeId) noexcept { + ASMJIT_ASSERT(typeId < ASMJIT_ARRAY_SIZE(_info.sizeOf)); + return _info.sizeOf[typeId]; + } + + static ASMJIT_INLINE uint32_t elementOf(uint32_t typeId) noexcept { + ASMJIT_ASSERT(typeId < ASMJIT_ARRAY_SIZE(_info.elementOf)); + return _info.elementOf[typeId]; + } + + //! Get an offset to convert a `kIntPtr` and `kUIntPtr` TypeId into a + //! type that matches `gpSize` (general-purpose register size). If you + //! find such TypeId it's then only about adding the offset to it. + //! + //! For example: + //! ~~~ + //! uint32_t gpSize = '4' or '8'; + //! uint32_t deabstractDelta = TypeId::deabstractDeltaOfSize(gpSize); + //! + //! uint32_t typeId = 'some type-id'; + //! + //! // Normalize some typeId into a non-abstract typeId. + //! if (TypeId::isAbstract(typeId)) typeId += deabstractDelta; + //! + //! // The same, but by using TypeId::deabstract() function. + //! typeId = TypeId::deabstract(typeId, deabstractDelta); + //! ~~~ + static ASMJIT_INLINE uint32_t deabstractDeltaOfSize(uint32_t gpSize) noexcept { + return gpSize >= 8 ? kI64 - kIntPtr : kI32 - kIntPtr; + } + + static ASMJIT_INLINE uint32_t deabstract(uint32_t typeId, uint32_t deabstractDelta) noexcept { + return TypeId::isAbstract(typeId) ? typeId += deabstractDelta : typeId; + } +}; + +//! TypeIdOf<> template allows to get a TypeId of a C++ type. +template struct TypeIdOf { + // Don't provide anything if not specialized. +}; +template struct TypeIdOf { + enum { kTypeId = TypeId::kUIntPtr }; +}; + +template +struct TypeIdOfInt { + enum { + kSignatureed = int(~static_cast(0) < static_cast(0)), + kTypeId = (sizeof(T) == 1) ? (int)(kSignatureed ? TypeId::kI8 : TypeId::kU8 ) : + (sizeof(T) == 2) ? (int)(kSignatureed ? TypeId::kI16 : TypeId::kU16) : + (sizeof(T) == 4) ? (int)(kSignatureed ? TypeId::kI32 : TypeId::kU32) : + (sizeof(T) == 8) ? (int)(kSignatureed ? TypeId::kI64 : TypeId::kU64) : (int)TypeId::kVoid + }; +}; + +#define ASMJIT_DEFINE_TYPE_ID(T, TYPE_ID) \ + template<> \ + struct TypeIdOf { enum { kTypeId = TYPE_ID}; } + +ASMJIT_DEFINE_TYPE_ID(signed char , TypeIdOfInt< signed char >::kTypeId); +ASMJIT_DEFINE_TYPE_ID(unsigned char , TypeIdOfInt< unsigned char >::kTypeId); +ASMJIT_DEFINE_TYPE_ID(short , TypeIdOfInt< short >::kTypeId); +ASMJIT_DEFINE_TYPE_ID(unsigned short , TypeIdOfInt< unsigned short >::kTypeId); +ASMJIT_DEFINE_TYPE_ID(int , TypeIdOfInt< int >::kTypeId); +ASMJIT_DEFINE_TYPE_ID(unsigned int , TypeIdOfInt< unsigned int >::kTypeId); +ASMJIT_DEFINE_TYPE_ID(long , TypeIdOfInt< long >::kTypeId); +ASMJIT_DEFINE_TYPE_ID(unsigned long , TypeIdOfInt< unsigned long >::kTypeId); +#if ASMJIT_CC_MSC && !ASMJIT_CC_MSC_GE(16, 0, 0) +ASMJIT_DEFINE_TYPE_ID(__int64 , TypeIdOfInt< __int64 >::kTypeId); +ASMJIT_DEFINE_TYPE_ID(unsigned __int64 , TypeIdOfInt< unsigned __int64 >::kTypeId); +#else +ASMJIT_DEFINE_TYPE_ID(long long , TypeIdOfInt< long long >::kTypeId); +ASMJIT_DEFINE_TYPE_ID(unsigned long long, TypeIdOfInt< unsigned long long >::kTypeId); +#endif +ASMJIT_DEFINE_TYPE_ID(bool , TypeIdOfInt< bool >::kTypeId); +#if ASMJIT_CC_HAS_NATIVE_CHAR +ASMJIT_DEFINE_TYPE_ID(char , TypeIdOfInt< char >::kTypeId); +#endif +#if ASMJIT_CC_HAS_NATIVE_CHAR16_T +ASMJIT_DEFINE_TYPE_ID(char16_t , TypeIdOfInt< char16_t >::kTypeId); +#endif +#if ASMJIT_CC_HAS_NATIVE_CHAR32_T +ASMJIT_DEFINE_TYPE_ID(char32_t , TypeIdOfInt< char32_t >::kTypeId); +#endif +#if ASMJIT_CC_HAS_NATIVE_WCHAR_T +ASMJIT_DEFINE_TYPE_ID(wchar_t , TypeIdOfInt< wchar_t >::kTypeId); +#endif + +ASMJIT_DEFINE_TYPE_ID(void , TypeId::kVoid); +ASMJIT_DEFINE_TYPE_ID(float , TypeId::kF32); +ASMJIT_DEFINE_TYPE_ID(double , TypeId::kF64); + +ASMJIT_DEFINE_TYPE_ID(TypeId::Int8 , TypeId::kI8); +ASMJIT_DEFINE_TYPE_ID(TypeId::UInt8 , TypeId::kU8); +ASMJIT_DEFINE_TYPE_ID(TypeId::Int16 , TypeId::kI16); +ASMJIT_DEFINE_TYPE_ID(TypeId::UInt16 , TypeId::kU16); +ASMJIT_DEFINE_TYPE_ID(TypeId::Int32 , TypeId::kI32); +ASMJIT_DEFINE_TYPE_ID(TypeId::UInt32 , TypeId::kU32); +ASMJIT_DEFINE_TYPE_ID(TypeId::Int64 , TypeId::kI64); +ASMJIT_DEFINE_TYPE_ID(TypeId::UInt64 , TypeId::kU64); +ASMJIT_DEFINE_TYPE_ID(TypeId::IntPtr , TypeId::kIntPtr); +ASMJIT_DEFINE_TYPE_ID(TypeId::UIntPtr , TypeId::kUIntPtr); +ASMJIT_DEFINE_TYPE_ID(TypeId::Float , TypeId::kF32); +ASMJIT_DEFINE_TYPE_ID(TypeId::Double , TypeId::kF64); +ASMJIT_DEFINE_TYPE_ID(TypeId::MmxReg , TypeId::kMmx64); +ASMJIT_DEFINE_TYPE_ID(TypeId::Vec128 , TypeId::kI32x4); +ASMJIT_DEFINE_TYPE_ID(TypeId::Vec256 , TypeId::kI32x8); +ASMJIT_DEFINE_TYPE_ID(TypeId::Vec512 , TypeId::kI32x16); + +//! \} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" + +// [Guard] +#endif // _ASMJIT_BASE_OPERAND_H diff --git a/libraries/asmjit/asmjit/base/osutils.cpp b/libraries/asmjit/asmjit/base/osutils.cpp new file mode 100644 index 00000000000..08ddd7dfd27 --- /dev/null +++ b/libraries/asmjit/asmjit/base/osutils.cpp @@ -0,0 +1,228 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Export] +#define ASMJIT_EXPORTS + +// [Dependencies] +#include "../base/osutils.h" +#include "../base/utils.h" + +#if ASMJIT_OS_POSIX +# include +# include +# include +# include +#endif // ASMJIT_OS_POSIX + +#if ASMJIT_OS_MAC +# include +#endif // ASMJIT_OS_MAC + +#if ASMJIT_OS_WINDOWS +# if defined(_MSC_VER) && _MSC_VER >= 1400 +# include +# else +# define _InterlockedCompareExchange InterlockedCompareExchange +# endif // _MSC_VER +#endif // ASMJIT_OS_WINDOWS + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +// ============================================================================ +// [asmjit::OSUtils - Virtual Memory] +// ============================================================================ + +// Windows specific implementation using `VirtualAllocEx` and `VirtualFree`. +#if ASMJIT_OS_WINDOWS +static ASMJIT_NOINLINE const VMemInfo& OSUtils_GetVMemInfo() noexcept { + static VMemInfo vmi; + + if (ASMJIT_UNLIKELY(!vmi.hCurrentProcess)) { + SYSTEM_INFO info; + ::GetSystemInfo(&info); + + vmi.pageSize = Utils::alignToPowerOf2(info.dwPageSize); + vmi.pageGranularity = info.dwAllocationGranularity; + vmi.hCurrentProcess = ::GetCurrentProcess(); + } + + return vmi; +}; + +VMemInfo OSUtils::getVirtualMemoryInfo() noexcept { return OSUtils_GetVMemInfo(); } + +void* OSUtils::allocVirtualMemory(size_t size, size_t* allocated, uint32_t flags) noexcept { + return allocProcessMemory(static_cast(0), size, allocated, flags); +} + +Error OSUtils::releaseVirtualMemory(void* p, size_t size) noexcept { + return releaseProcessMemory(static_cast(0), p, size); +} + +void* OSUtils::allocProcessMemory(HANDLE hProcess, size_t size, size_t* allocated, uint32_t flags) noexcept { + if (size == 0) + return nullptr; + + const VMemInfo& vmi = OSUtils_GetVMemInfo(); + if (!hProcess) hProcess = vmi.hCurrentProcess; + + // VirtualAllocEx rounds the allocated size to a page size automatically, + // but we need the `alignedSize` so we can store the real allocated size + // into `allocated` output. + size_t alignedSize = Utils::alignTo(size, vmi.pageSize); + + // Windows XP SP2 / Vista+ allow data-execution-prevention (DEP). + DWORD protectFlags = 0; + + if (flags & kVMExecutable) + protectFlags |= (flags & kVMWritable) ? PAGE_EXECUTE_READWRITE : PAGE_EXECUTE_READ; + else + protectFlags |= (flags & kVMWritable) ? PAGE_READWRITE : PAGE_READONLY; + + LPVOID mBase = ::VirtualAllocEx(hProcess, nullptr, alignedSize, MEM_COMMIT | MEM_RESERVE, protectFlags); + if (ASMJIT_UNLIKELY(!mBase)) return nullptr; + + ASMJIT_ASSERT(Utils::isAligned(reinterpret_cast(mBase), vmi.pageSize)); + if (allocated) *allocated = alignedSize; + return mBase; +} + +Error OSUtils::releaseProcessMemory(HANDLE hProcess, void* p, size_t size) noexcept { + const VMemInfo& vmi = OSUtils_GetVMemInfo(); + if (!hProcess) hProcess = vmi.hCurrentProcess; + + if (ASMJIT_UNLIKELY(!::VirtualFreeEx(hProcess, p, 0, MEM_RELEASE))) + return DebugUtils::errored(kErrorInvalidState); + + return kErrorOk; +} +#endif // ASMJIT_OS_WINDOWS + +// Posix specific implementation using `mmap()` and `munmap()`. +#if ASMJIT_OS_POSIX + +// Mac uses MAP_ANON instead of MAP_ANONYMOUS. +#if !defined(MAP_ANONYMOUS) +# define MAP_ANONYMOUS MAP_ANON +#endif // MAP_ANONYMOUS + +static const VMemInfo& OSUtils_GetVMemInfo() noexcept { + static VMemInfo vmi; + if (ASMJIT_UNLIKELY(!vmi.pageSize)) { + size_t pageSize = ::getpagesize(); + vmi.pageSize = pageSize; + vmi.pageGranularity = std::max(pageSize, 65536); + } + return vmi; +}; + +VMemInfo OSUtils::getVirtualMemoryInfo() noexcept { return OSUtils_GetVMemInfo(); } + +void* OSUtils::allocVirtualMemory(size_t size, size_t* allocated, uint32_t flags) noexcept { + const VMemInfo& vmi = OSUtils_GetVMemInfo(); + + size_t alignedSize = Utils::alignTo(size, vmi.pageSize); + int protection = PROT_READ; + + if (flags & kVMWritable ) protection |= PROT_WRITE; + if (flags & kVMExecutable) protection |= PROT_EXEC; + + void* mbase = ::mmap(nullptr, alignedSize, protection, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (ASMJIT_UNLIKELY(mbase == MAP_FAILED)) return nullptr; + + if (allocated) *allocated = alignedSize; + return mbase; +} + +Error OSUtils::releaseVirtualMemory(void* p, size_t size) noexcept { + if (ASMJIT_UNLIKELY(::munmap(p, size) != 0)) + return DebugUtils::errored(kErrorInvalidState); + + return kErrorOk; +} +#endif // ASMJIT_OS_POSIX + +// ============================================================================ +// [asmjit::OSUtils - GetTickCount] +// ============================================================================ + +#if ASMJIT_OS_WINDOWS +static ASMJIT_INLINE uint32_t OSUtils_calcHiRes(const LARGE_INTEGER& now, double freq) noexcept { + return static_cast( + (int64_t)(double(now.QuadPart) / freq) & 0xFFFFFFFF); +} + +uint32_t OSUtils::getTickCount() noexcept { + static volatile uint32_t _hiResTicks; + static volatile double _hiResFreq; + + do { + uint32_t hiResOk = _hiResTicks; + LARGE_INTEGER qpf, now; + + // If for whatever reason this fails, bail to `GetTickCount()`. + if (!::QueryPerformanceCounter(&now)) break; + + // Expected - if we ran through this at least once `hiResTicks` will be + // either 1 or 0xFFFFFFFF. If it's '1' then the Hi-Res counter is available + // and `QueryPerformanceCounter()` can be used. + if (hiResOk == 1) return OSUtils_calcHiRes(now, _hiResFreq); + + // Hi-Res counter is not available, bail to `GetTickCount()`. + if (hiResOk != 0) break; + + // Detect availability of Hi-Res counter, if not available, bail to `GetTickCount()`. + if (!::QueryPerformanceFrequency(&qpf)) { + _InterlockedCompareExchange((LONG*)&_hiResTicks, 0xFFFFFFFF, 0); + break; + } + + double freq = double(qpf.QuadPart) / 1000.0; + _hiResFreq = freq; + + _InterlockedCompareExchange((LONG*)&_hiResTicks, 1, 0); + return OSUtils_calcHiRes(now, freq); + } while (0); + + return ::GetTickCount(); +} +#elif ASMJIT_OS_MAC +uint32_t OSUtils::getTickCount() noexcept { + static mach_timebase_info_data_t _machTime; + + // See Apple's QA1398. + if (ASMJIT_UNLIKELY(_machTime.denom == 0) || mach_timebase_info(&_machTime) != KERN_SUCCESS) + return 0; + + // `mach_absolute_time()` returns nanoseconds, we want milliseconds. + uint64_t t = mach_absolute_time() / 1000000; + + t = t * _machTime.numer / _machTime.denom; + return static_cast(t & 0xFFFFFFFFU); +} +#elif defined(_POSIX_MONOTONIC_CLOCK) && _POSIX_MONOTONIC_CLOCK >= 0 +uint32_t OSUtils::getTickCount() noexcept { + struct timespec ts; + + if (ASMJIT_UNLIKELY(clock_gettime(CLOCK_MONOTONIC, &ts) != 0)) + return 0; + + uint64_t t = (uint64_t(ts.tv_sec ) * 1000) + (uint64_t(ts.tv_nsec) / 1000000); + return static_cast(t & 0xFFFFFFFFU); +} +#else +#error "[asmjit] OSUtils::getTickCount() is not implemented for your target OS." +uint32_t OSUtils::getTickCount() noexcept { return 0; } +#endif + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" diff --git a/libraries/asmjit/asmjit/base/osutils.h b/libraries/asmjit/asmjit/base/osutils.h new file mode 100644 index 00000000000..ccf6bee26d8 --- /dev/null +++ b/libraries/asmjit/asmjit/base/osutils.h @@ -0,0 +1,178 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Guard] +#ifndef _ASMJIT_BASE_OSUTILS_H +#define _ASMJIT_BASE_OSUTILS_H + +// [Dependencies] +#include "../base/globals.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +//! \addtogroup asmjit_base +//! \{ + +// ============================================================================ +// [asmjit::VMemInfo] +// ============================================================================ + +//! Information about OS virtual memory. +struct VMemInfo { +#if ASMJIT_OS_WINDOWS + HANDLE hCurrentProcess; //!< Handle of the current process (Windows). +#endif // ASMJIT_OS_WINDOWS + size_t pageSize; //!< Virtual memory page size. + size_t pageGranularity; //!< Virtual memory page granularity. +}; + +// ============================================================================ +// [asmjit::OSUtils] +// ============================================================================ + +//! OS utilities. +//! +//! Virtual Memory +//! -------------- +//! +//! Provides functions to allocate and release virtual memory that is required +//! to execute dynamically generated code. If both processor and host OS support +//! data-execution-prevention (DEP) then the only way to run machine code is to +//! allocate virtual memory that has `OSUtils::kVMExecutable` flag enabled. All +//! functions provides by OSUtils use internally platform specific API. +//! +//! Benchmarking +//! ------------ +//! +//! OSUtils also provide a function `getTickCount()` that can be used for +//! benchmarking purposes. It's similar to Windows-only `GetTickCount()`, but +//! it's cross-platform and tries to be the most reliable platform specific +//! calls to make the result usable. +struct OSUtils { + // -------------------------------------------------------------------------- + // [Virtual Memory] + // -------------------------------------------------------------------------- + + //! Virtual memory flags. + ASMJIT_ENUM(VMFlags) { + kVMWritable = 0x00000001U, //!< Virtual memory is writable. + kVMExecutable = 0x00000002U //!< Virtual memory is executable. + }; + + ASMJIT_API static VMemInfo getVirtualMemoryInfo() noexcept; + + //! Allocate virtual memory. + ASMJIT_API static void* allocVirtualMemory(size_t size, size_t* allocated, uint32_t flags) noexcept; + //! Release virtual memory previously allocated by \ref allocVirtualMemory(). + ASMJIT_API static Error releaseVirtualMemory(void* p, size_t size) noexcept; + +#if ASMJIT_OS_WINDOWS + //! Allocate virtual memory of `hProcess` (Windows). + ASMJIT_API static void* allocProcessMemory(HANDLE hProcess, size_t size, size_t* allocated, uint32_t flags) noexcept; + + //! Release virtual memory of `hProcess` (Windows). + ASMJIT_API static Error releaseProcessMemory(HANDLE hProcess, void* p, size_t size) noexcept; +#endif // ASMJIT_OS_WINDOWS + + // -------------------------------------------------------------------------- + // [GetTickCount] + // -------------------------------------------------------------------------- + + //! Get the current CPU tick count, used for benchmarking (1ms resolution). + ASMJIT_API static uint32_t getTickCount() noexcept; +}; + +// ============================================================================ +// [asmjit::Lock] +// ============================================================================ + +//! \internal +//! +//! Lock. +struct Lock { + ASMJIT_NONCOPYABLE(Lock) + + // -------------------------------------------------------------------------- + // [Windows] + // -------------------------------------------------------------------------- + +#if ASMJIT_OS_WINDOWS + typedef CRITICAL_SECTION Handle; + + //! Create a new `Lock` instance. + ASMJIT_INLINE Lock() noexcept { InitializeCriticalSection(&_handle); } + //! Destroy the `Lock` instance. + ASMJIT_INLINE ~Lock() noexcept { DeleteCriticalSection(&_handle); } + + //! Lock. + ASMJIT_INLINE void lock() noexcept { EnterCriticalSection(&_handle); } + //! Unlock. + ASMJIT_INLINE void unlock() noexcept { LeaveCriticalSection(&_handle); } +#endif // ASMJIT_OS_WINDOWS + + // -------------------------------------------------------------------------- + // [Posix] + // -------------------------------------------------------------------------- + +#if ASMJIT_OS_POSIX + typedef pthread_mutex_t Handle; + + //! Create a new `Lock` instance. + ASMJIT_INLINE Lock() noexcept { pthread_mutex_init(&_handle, nullptr); } + //! Destroy the `Lock` instance. + ASMJIT_INLINE ~Lock() noexcept { pthread_mutex_destroy(&_handle); } + + //! Lock. + ASMJIT_INLINE void lock() noexcept { pthread_mutex_lock(&_handle); } + //! Unlock. + ASMJIT_INLINE void unlock() noexcept { pthread_mutex_unlock(&_handle); } +#endif // ASMJIT_OS_POSIX + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + //! Native handle. + Handle _handle; +}; + +// ============================================================================ +// [asmjit::AutoLock] +// ============================================================================ + +//! \internal +//! +//! Scoped lock. +struct AutoLock { + ASMJIT_NONCOPYABLE(AutoLock) + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE AutoLock(Lock& target) noexcept : _target(target) { _target.lock(); } + ASMJIT_INLINE ~AutoLock() noexcept { _target.unlock(); } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + //! Reference to the `Lock`. + Lock& _target; +}; + +//! \} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" + +// [Guard] +#endif // _ASMJIT_BASE_OSUTILS_H diff --git a/libraries/asmjit/asmjit/base/regalloc.cpp b/libraries/asmjit/asmjit/base/regalloc.cpp new file mode 100644 index 00000000000..cbdfd85e969 --- /dev/null +++ b/libraries/asmjit/asmjit/base/regalloc.cpp @@ -0,0 +1,594 @@ +// [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 "../asmjit_build.h" +#if !defined(ASMJIT_DISABLE_COMPILER) + +// [Dependencies] +#include "../base/regalloc_p.h" +#include "../base/utils.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +// ============================================================================ +// [asmjit::RAPass - Construction / Destruction] +// ============================================================================ + +RAPass::RAPass() noexcept : + CBPass("RA"), + _varMapToVaListOffset(0) {} +RAPass::~RAPass() noexcept {} + +// ============================================================================ +// [asmjit::RAPass - Interface] +// ============================================================================ + +Error RAPass::process(Zone* zone) noexcept { + _zone = zone; + _heap.reset(zone); + _emitComments = (cb()->getGlobalOptions() & CodeEmitter::kOptionLoggingEnabled) != 0; + + Error err = kErrorOk; + CBNode* node = cc()->getFirstNode(); + if (!node) return err; + + do { + if (node->getType() == CBNode::kNodeFunc) { + CCFunc* func = static_cast(node); + node = func->getEnd(); + + err = compile(func); + if (err) break; + } + + // Find a function by skipping all nodes that are not `kNodeFunc`. + do { + node = node->getNext(); + } while (node && node->getType() != CBNode::kNodeFunc); + } while (node); + + _heap.reset(nullptr); + _zone = nullptr; + return err; +} + +Error RAPass::compile(CCFunc* func) noexcept { + ASMJIT_PROPAGATE(prepare(func)); + + Error err; + do { + err = fetch(); + if (err) break; + + err = removeUnreachableCode(); + if (err) break; + + err = livenessAnalysis(); + if (err) break; + +#if !defined(ASMJIT_DISABLE_LOGGING) + if (cc()->getGlobalOptions() & CodeEmitter::kOptionLoggingEnabled) { + err = annotate(); + if (err) break; + } +#endif // !ASMJIT_DISABLE_LOGGING + + err = translate(); + } while (false); + + cleanup(); + + // We alter the compiler cursor, because it doesn't make sense to reference + // it after compilation - some nodes may disappear and it's forbidden to add + // new code after the compilation is done. + cc()->_setCursor(nullptr); + return err; +} + +Error RAPass::prepare(CCFunc* func) noexcept { + CBNode* end = func->getEnd(); + + _func = func; + _stop = end->getNext(); + + _unreachableList.reset(); + _returningList.reset(); + _jccList.reset(); + _contextVd.reset(); + + _memVarCells = nullptr; + _memStackCells = nullptr; + + _mem1ByteVarsUsed = 0; + _mem2ByteVarsUsed = 0; + _mem4ByteVarsUsed = 0; + _mem8ByteVarsUsed = 0; + _mem16ByteVarsUsed = 0; + _mem32ByteVarsUsed = 0; + _mem64ByteVarsUsed = 0; + _memStackCellsUsed = 0; + + _memMaxAlign = 0; + _memVarTotal = 0; + _memStackTotal = 0; + _memAllTotal = 0; + _annotationLength = 12; + + return kErrorOk; +} + +void RAPass::cleanup() noexcept { + VirtReg** virtArray = _contextVd.getData(); + size_t virtCount = _contextVd.getLength(); + + for (size_t i = 0; i < virtCount; i++) { + VirtReg* vreg = virtArray[i]; + vreg->_raId = kInvalidValue; + vreg->resetPhysId(); + } + + _contextVd.reset(); +} + +// ============================================================================ +// [asmjit::RAPass - Mem] +// ============================================================================ + +static ASMJIT_INLINE uint32_t RAGetDefaultAlignment(uint32_t size) { + if (size > 32) + return 64; + else if (size > 16) + return 32; + else if (size > 8) + return 16; + else if (size > 4) + return 8; + else if (size > 2) + return 4; + else if (size > 1) + return 2; + else + return 1; +} + +RACell* RAPass::_newVarCell(VirtReg* vreg) { + ASMJIT_ASSERT(vreg->_memCell == nullptr); + + RACell* cell; + uint32_t size = vreg->getSize(); + + if (vreg->isStack()) { + cell = _newStackCell(size, vreg->getAlignment()); + if (ASMJIT_UNLIKELY(!cell)) return nullptr; + } + else { + cell = static_cast(_zone->alloc(sizeof(RACell))); + if (!cell) goto _NoMemory; + + cell->next = _memVarCells; + cell->offset = 0; + cell->size = size; + cell->alignment = size; + + _memVarCells = cell; + _memMaxAlign = std::max(_memMaxAlign, size); + _memVarTotal += size; + + switch (size) { + case 1: _mem1ByteVarsUsed++ ; break; + case 2: _mem2ByteVarsUsed++ ; break; + case 4: _mem4ByteVarsUsed++ ; break; + case 8: _mem8ByteVarsUsed++ ; break; + case 16: _mem16ByteVarsUsed++; break; + case 32: _mem32ByteVarsUsed++; break; + case 64: _mem64ByteVarsUsed++; break; + + default: + ASMJIT_NOT_REACHED(); + } + } + + vreg->_memCell = cell; + return cell; + +_NoMemory: + cc()->setLastError(DebugUtils::errored(kErrorNoHeapMemory)); + return nullptr; +} + +RACell* RAPass::_newStackCell(uint32_t size, uint32_t alignment) { + RACell* cell = static_cast(_zone->alloc(sizeof(RACell))); + if (ASMJIT_UNLIKELY(!cell)) return nullptr; + + if (alignment == 0) + alignment = RAGetDefaultAlignment(size); + + if (alignment > 64) + alignment = 64; + + ASMJIT_ASSERT(Utils::isPowerOf2(alignment)); + size = Utils::alignTo(size, alignment); + + // Insert it sorted according to the alignment and size. + { + RACell** pPrev = &_memStackCells; + RACell* cur = *pPrev; + + while (cur && ((cur->alignment > alignment) || (cur->alignment == alignment && cur->size > size))) { + pPrev = &cur->next; + cur = *pPrev; + } + + cell->next = cur; + cell->offset = 0; + cell->size = size; + cell->alignment = alignment; + + *pPrev = cell; + _memStackCellsUsed++; + + _memMaxAlign = std::max(_memMaxAlign, alignment); + _memStackTotal += size; + } + + return cell; +} + +Error RAPass::resolveCellOffsets() { + RACell* varCell = _memVarCells; + RACell* stackCell = _memStackCells; + + uint32_t pos64 = 0; + uint32_t pos32 = pos64 + _mem64ByteVarsUsed * 64; + uint32_t pos16 = pos32 + _mem32ByteVarsUsed * 32; + uint32_t pos8 = pos16 + _mem16ByteVarsUsed * 16; + uint32_t pos4 = pos8 + _mem8ByteVarsUsed * 8 ; + uint32_t pos2 = pos4 + _mem4ByteVarsUsed * 4 ; + uint32_t pos1 = pos2 + _mem2ByteVarsUsed * 2 ; + + // Assign home slots. + while (varCell) { + uint32_t size = varCell->size; + uint32_t offset = 0; + + switch (size) { + case 1: offset = pos1 ; pos1 += 1 ; break; + case 2: offset = pos2 ; pos2 += 2 ; break; + case 4: offset = pos4 ; pos4 += 4 ; break; + case 8: offset = pos8 ; pos8 += 8 ; break; + case 16: offset = pos16; pos16 += 16; break; + case 32: offset = pos32; pos32 += 32; break; + case 64: offset = pos64; pos64 += 64; break; + + default: + ASMJIT_NOT_REACHED(); + } + + varCell->offset = static_cast(offset); + varCell = varCell->next; + } + + // Assign stack slots. + uint32_t stackPos = pos1 + _mem1ByteVarsUsed; + while (stackCell) { + uint32_t size = stackCell->size; + uint32_t alignment = stackCell->alignment; + ASMJIT_ASSERT(alignment != 0 && Utils::isPowerOf2(alignment)); + + stackPos = Utils::alignTo(stackPos, alignment); + stackCell->offset = stackPos; + stackCell = stackCell->next; + + stackPos += size; + } + + _memAllTotal = stackPos; + return kErrorOk; +} + +// ============================================================================ +// [asmjit::RAPass - RemoveUnreachableCode] +// ============================================================================ + +Error RAPass::removeUnreachableCode() { + ZoneList::Link* link = _unreachableList.getFirst(); + CBNode* stop = getStop(); + + while (link) { + CBNode* node = link->getValue(); + if (node && node->getPrev() && node != stop) { + // Locate all unreachable nodes. + CBNode* first = node; + do { + if (node->hasPassData()) break; + node = node->getNext(); + } while (node != stop); + + // Remove unreachable nodes that are neither informative nor directives. + if (node != first) { + CBNode* end = node; + node = first; + + // NOTE: The strategy is as follows: + // 1. The algorithm removes everything until it finds a first label. + // 2. After the first label is found it removes only removable nodes. + bool removeEverything = true; + do { + CBNode* next = node->getNext(); + bool remove = node->isRemovable(); + + if (!remove) { + if (node->isLabel()) + removeEverything = false; + remove = removeEverything; + } + + if (remove) + cc()->removeNode(node); + + node = next; + } while (node != end); + } + } + + link = link->getNext(); + } + + return kErrorOk; +} + +// ============================================================================ +// [asmjit::RAPass - Liveness Analysis] +// ============================================================================ + +//! \internal +struct LivenessTarget { + LivenessTarget* prev; //!< Previous target. + CBLabel* node; //!< Target node. + CBJump* from; //!< Jumped from. +}; + +Error RAPass::livenessAnalysis() { + uint32_t bLen = static_cast( + ((_contextVd.getLength() + RABits::kEntityBits - 1) / RABits::kEntityBits)); + + // No variables. + if (bLen == 0) + return kErrorOk; + + CCFunc* func = getFunc(); + CBJump* from = nullptr; + + LivenessTarget* ltCur = nullptr; + LivenessTarget* ltUnused = nullptr; + + ZoneList::Link* retPtr = _returningList.getFirst(); + ASMJIT_ASSERT(retPtr != nullptr); + + CBNode* node = retPtr->getValue(); + RAData* wd; + + size_t varMapToVaListOffset = _varMapToVaListOffset; + RABits* bCur = newBits(bLen); + if (ASMJIT_UNLIKELY(!bCur)) goto NoMem; + + // Allocate bits for code visited first time. +Visit: + for (;;) { + wd = node->getPassData(); + if (wd->liveness) { + if (bCur->_addBitsDelSource(wd->liveness, bCur, bLen)) + goto Patch; + else + goto Done; + } + + RABits* bTmp = copyBits(bCur, bLen); + if (!bTmp) goto NoMem; + + wd = node->getPassData(); + wd->liveness = bTmp; + + uint32_t tiedTotal = wd->tiedTotal; + TiedReg* tiedArray = reinterpret_cast(((uint8_t*)wd) + varMapToVaListOffset); + + for (uint32_t i = 0; i < tiedTotal; i++) { + TiedReg* tied = &tiedArray[i]; + VirtReg* vreg = tied->vreg; + + uint32_t flags = tied->flags; + uint32_t raId = vreg->_raId; + + if ((flags & TiedReg::kWAll) && !(flags & TiedReg::kRAll)) { + // Write-Only. + bTmp->setBit(raId); + bCur->delBit(raId); + } + else { + // Read-Only or Read/Write. + bTmp->setBit(raId); + bCur->setBit(raId); + } + } + + if (node->getType() == CBNode::kNodeLabel) + goto Target; + + if (node == func) + goto Done; + + ASMJIT_ASSERT(node->getPrev()); + node = node->getPrev(); + } + + // Patch already generated liveness bits. +Patch: + for (;;) { + ASMJIT_ASSERT(node->hasPassData()); + ASMJIT_ASSERT(node->getPassData()->liveness != nullptr); + + RABits* bNode = node->getPassData()->liveness; + if (!bNode->_addBitsDelSource(bCur, bLen)) goto Done; + if (node->getType() == CBNode::kNodeLabel) goto Target; + + if (node == func) goto Done; + node = node->getPrev(); + } + +Target: + if (static_cast(node)->getNumRefs() != 0) { + // Push a new LivenessTarget onto the stack if needed. + if (!ltCur || ltCur->node != node) { + // Allocate a new LivenessTarget object (from pool or zone). + LivenessTarget* ltTmp = ltUnused; + + if (ltTmp) { + ltUnused = ltUnused->prev; + } + else { + ltTmp = _zone->allocT( + sizeof(LivenessTarget) - sizeof(RABits) + bLen * sizeof(uintptr_t)); + if (!ltTmp) goto NoMem; + } + + // Initialize and make current - ltTmp->from will be set later on. + ltTmp->prev = ltCur; + ltTmp->node = static_cast(node); + ltCur = ltTmp; + + from = static_cast(node)->getFrom(); + ASMJIT_ASSERT(from != nullptr); + } + else { + from = ltCur->from; + goto JumpNext; + } + + // Visit/Patch. + do { + ltCur->from = from; + bCur->copyBits(node->getPassData()->liveness, bLen); + + if (!from->getPassData()->liveness) { + node = from; + goto Visit; + } + + // Issue #25: Moved 'JumpNext' here since it's important to patch + // code again if there are more live variables than before. +JumpNext: + if (bCur->delBits(from->getPassData()->liveness, bLen)) { + node = from; + goto Patch; + } + + from = from->getJumpNext(); + } while (from); + + // Pop the current LivenessTarget from the stack. + { + LivenessTarget* ltTmp = ltCur; + ltCur = ltCur->prev; + ltTmp->prev = ltUnused; + ltUnused = ltTmp; + } + } + + bCur->copyBits(node->getPassData()->liveness, bLen); + node = node->getPrev(); + if (node->isJmp() || !node->hasPassData()) goto Done; + + wd = node->getPassData(); + if (!wd->liveness) goto Visit; + if (bCur->delBits(wd->liveness, bLen)) goto Patch; + +Done: + if (ltCur) { + node = ltCur->node; + from = ltCur->from; + + goto JumpNext; + } + + retPtr = retPtr->getNext(); + if (retPtr) { + node = retPtr->getValue(); + goto Visit; + } + + return kErrorOk; + +NoMem: + return DebugUtils::errored(kErrorNoHeapMemory); +} + +// ============================================================================ +// [asmjit::RAPass - Annotate] +// ============================================================================ + +Error RAPass::formatInlineComment(StringBuilder& dst, CBNode* node) { +#if !defined(ASMJIT_DISABLE_LOGGING) + RAData* wd = node->getPassData(); + + if (node->hasInlineComment()) + dst.appendString(node->getInlineComment()); + + if (wd && wd->liveness) { + if (dst.getLength() < _annotationLength) + dst.appendChars(' ', _annotationLength - dst.getLength()); + + uint32_t vdCount = static_cast(_contextVd.getLength()); + size_t offset = dst.getLength() + 1; + + dst.appendChar('['); + dst.appendChars(' ', vdCount); + dst.appendChar(']'); + RABits* liveness = wd->liveness; + + uint32_t i; + for (i = 0; i < vdCount; i++) { + if (liveness->getBit(i)) + dst.getData()[offset + i] = '.'; + } + + uint32_t tiedTotal = wd->tiedTotal; + TiedReg* tiedArray = reinterpret_cast(((uint8_t*)wd) + _varMapToVaListOffset); + + for (i = 0; i < tiedTotal; i++) { + TiedReg* tied = &tiedArray[i]; + VirtReg* vreg = tied->vreg; + uint32_t flags = tied->flags; + + char c = 'u'; + if ( (flags & TiedReg::kRAll) && !(flags & TiedReg::kWAll)) c = 'r'; + if (!(flags & TiedReg::kRAll) && (flags & TiedReg::kWAll)) c = 'w'; + if ( (flags & TiedReg::kRAll) && (flags & TiedReg::kWAll)) c = 'x'; + // Uppercase if unused. + if ( (flags & TiedReg::kUnuse)) c -= 'a' - 'A'; + + ASMJIT_ASSERT(offset + vreg->_raId < dst.getLength()); + dst._data[offset + vreg->_raId] = c; + } + } +#endif // !ASMJIT_DISABLE_LOGGING + + return kErrorOk; +} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" + +// [Guard] +#endif // !ASMJIT_DISABLE_COMPILER diff --git a/libraries/asmjit/asmjit/base/regalloc_p.h b/libraries/asmjit/asmjit/base/regalloc_p.h new file mode 100644 index 00000000000..53c7aebe618 --- /dev/null +++ b/libraries/asmjit/asmjit/base/regalloc_p.h @@ -0,0 +1,568 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Guard] +#ifndef _ASMJIT_BASE_REGALLOC_P_H +#define _ASMJIT_BASE_REGALLOC_P_H + +#include "../asmjit_build.h" +#if !defined(ASMJIT_DISABLE_COMPILER) + +// [Dependencies] +#include "../base/codecompiler.h" +#include "../base/zone.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +//! \addtogroup asmjit_base +//! \{ + +// ============================================================================ +// [asmjit::TiedReg] +// ============================================================================ + +//! Tied register (CodeCompiler) +//! +//! Tied register is used to describe one ore more register operands that share +//! the same virtual register. Tied register contains all the data that is +//! essential for register allocation. +struct TiedReg { + //! Flags. + ASMJIT_ENUM(Flags) { + kRReg = 0x00000001U, //!< Register read. + kWReg = 0x00000002U, //!< Register write. + kXReg = 0x00000003U, //!< Register read-write. + + kRMem = 0x00000004U, //!< Memory read. + kWMem = 0x00000008U, //!< Memory write. + kXMem = 0x0000000CU, //!< Memory read-write. + + kRDecide = 0x00000010U, //!< RA can decide between reg/mem read. + kWDecide = 0x00000020U, //!< RA can decide between reg/mem write. + kXDecide = 0x00000030U, //!< RA can decide between reg/mem read-write. + + kRFunc = 0x00000100U, //!< Function argument passed in register. + kWFunc = 0x00000200U, //!< Function return value passed into register. + kXFunc = 0x00000300U, //!< Function argument and return value. + kRCall = 0x00000400U, //!< Function call operand. + + kSpill = 0x00000800U, //!< Variable should be spilled. + kUnuse = 0x00001000U, //!< Variable should be unused at the end of the instruction/node. + + kRAll = kRReg | kRMem | kRDecide | kRFunc | kRCall, //!< All in-flags. + kWAll = kWReg | kWMem | kWDecide | kWFunc, //!< All out-flags. + + kRDone = 0x00400000U, //!< Already allocated on the input. + kWDone = 0x00800000U, //!< Already allocated on the output. + + kX86GpbLo = 0x10000000U, + kX86GpbHi = 0x20000000U, + kX86Fld4 = 0x40000000U, + kX86Fld8 = 0x80000000U + }; + + // -------------------------------------------------------------------------- + // [Init / Reset] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE void init(VirtReg* vreg, uint32_t flags = 0, uint32_t inRegs = 0, uint32_t allocableRegs = 0) noexcept { + this->vreg = vreg; + this->flags = flags; + this->refCount = 0; + this->inPhysId = Globals::kInvalidRegId; + this->outPhysId = Globals::kInvalidRegId; + this->reserved = 0; + this->inRegs = inRegs; + this->allocableRegs = allocableRegs; + } + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get whether the variable has to be allocated in a specific input register. + ASMJIT_INLINE uint32_t hasInPhysId() const { return inPhysId != Globals::kInvalidRegId; } + //! Get whether the variable has to be allocated in a specific output register. + ASMJIT_INLINE uint32_t hasOutPhysId() const { return outPhysId != Globals::kInvalidRegId; } + + //! Set the input register index. + ASMJIT_INLINE void setInPhysId(uint32_t index) { inPhysId = static_cast(index); } + //! Set the output register index. + ASMJIT_INLINE void setOutPhysId(uint32_t index) { outPhysId = static_cast(index); } + + // -------------------------------------------------------------------------- + // [Operator Overload] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE TiedReg& operator=(const TiedReg& other) { + ::memcpy(this, &other, sizeof(TiedReg)); + return *this; + } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + //! Pointer to the associated \ref VirtReg. + VirtReg* vreg; + //! Tied flags. + uint32_t flags; + + union { + struct { + //! How many times the variable is used by the instruction/node. + uint8_t refCount; + //! Input register index or `kInvalidReg` if it's not given. + //! + //! Even if the input register index is not given (i.e. it may by any + //! register), register allocator should assign an index that will be + //! used to persist a variable into this specific index. It's helpful + //! in situations where one variable has to be allocated in multiple + //! registers to determine the register which will be persistent. + uint8_t inPhysId; + //! Output register index or `kInvalidReg` if it's not given. + //! + //! Typically `kInvalidReg` if variable is only used on input. + uint8_t outPhysId; + //! \internal + uint8_t reserved; + }; + + //! \internal + //! + //! Packed data #0. + uint32_t packed; + }; + + //! Mandatory input registers. + //! + //! Mandatory input registers are required by the instruction even if + //! there are duplicates. This schema allows us to allocate one variable + //! in one or more register when needed. Required mostly by instructions + //! that have implicit register operands (imul, cpuid, ...) and function + //! call. + uint32_t inRegs; + + //! Allocable input registers. + //! + //! Optional input registers is a mask of all allocable registers for a given + //! variable where we have to pick one of them. This mask is usually not used + //! when _inRegs is set. If both masks are used then the register + //! allocator tries first to find an intersection between these and allocates + //! an extra slot if not found. + uint32_t allocableRegs; +}; + +// ============================================================================ +// [asmjit::RABits] +// ============================================================================ + +//! Fixed size bit-array. +//! +//! Used by variable liveness analysis. +struct RABits { + // -------------------------------------------------------------------------- + // [Enums] + // -------------------------------------------------------------------------- + + enum { + kEntitySize = static_cast(sizeof(uintptr_t)), + kEntityBits = kEntitySize * 8 + }; + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE uintptr_t getBit(uint32_t index) const noexcept { + return (data[index / kEntityBits] >> (index % kEntityBits)) & 1; + } + + ASMJIT_INLINE void setBit(uint32_t index) noexcept { + data[index / kEntityBits] |= static_cast(1) << (index % kEntityBits); + } + + ASMJIT_INLINE void delBit(uint32_t index) noexcept { + data[index / kEntityBits] &= ~(static_cast(1) << (index % kEntityBits)); + } + + // -------------------------------------------------------------------------- + // [Interface] + // -------------------------------------------------------------------------- + + //! Copy bits from `s0`, returns `true` if at least one bit is set in `s0`. + ASMJIT_INLINE bool copyBits(const RABits* s0, uint32_t len) noexcept { + uintptr_t r = 0; + for (uint32_t i = 0; i < len; i++) { + uintptr_t t = s0->data[i]; + data[i] = t; + r |= t; + } + return r != 0; + } + + ASMJIT_INLINE bool addBits(const RABits* s0, uint32_t len) noexcept { + return addBits(this, s0, len); + } + + ASMJIT_INLINE bool addBits(const RABits* s0, const RABits* s1, uint32_t len) noexcept { + uintptr_t r = 0; + for (uint32_t i = 0; i < len; i++) { + uintptr_t t = s0->data[i] | s1->data[i]; + data[i] = t; + r |= t; + } + return r != 0; + } + + ASMJIT_INLINE bool andBits(const RABits* s1, uint32_t len) noexcept { + return andBits(this, s1, len); + } + + ASMJIT_INLINE bool andBits(const RABits* s0, const RABits* s1, uint32_t len) noexcept { + uintptr_t r = 0; + for (uint32_t i = 0; i < len; i++) { + uintptr_t t = s0->data[i] & s1->data[i]; + data[i] = t; + r |= t; + } + return r != 0; + } + + ASMJIT_INLINE bool delBits(const RABits* s1, uint32_t len) noexcept { + return delBits(this, s1, len); + } + + ASMJIT_INLINE bool delBits(const RABits* s0, const RABits* s1, uint32_t len) noexcept { + uintptr_t r = 0; + for (uint32_t i = 0; i < len; i++) { + uintptr_t t = s0->data[i] & ~s1->data[i]; + data[i] = t; + r |= t; + } + return r != 0; + } + + ASMJIT_INLINE bool _addBitsDelSource(RABits* s1, uint32_t len) noexcept { + return _addBitsDelSource(this, s1, len); + } + + ASMJIT_INLINE bool _addBitsDelSource(const RABits* s0, RABits* s1, uint32_t len) noexcept { + uintptr_t r = 0; + for (uint32_t i = 0; i < len; i++) { + uintptr_t a = s0->data[i]; + uintptr_t b = s1->data[i]; + + this->data[i] = a | b; + b &= ~a; + + s1->data[i] = b; + r |= b; + } + return r != 0; + } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + uintptr_t data[1]; +}; + +// ============================================================================ +// [asmjit::RACell] +// ============================================================================ + +//! Register allocator's (RA) memory cell. +struct RACell { + RACell* next; //!< Next active cell. + int32_t offset; //!< Cell offset, relative to base-offset. + uint32_t size; //!< Cell size. + uint32_t alignment; //!< Cell alignment. +}; + +// ============================================================================ +// [asmjit::RAData] +// ============================================================================ + +//! Register allocator's (RA) data associated with each \ref CBNode. +struct RAData { + ASMJIT_INLINE RAData(uint32_t tiedTotal) noexcept + : liveness(nullptr), + state(nullptr), + tiedTotal(tiedTotal) {} + + RABits* liveness; //!< Liveness bits (populated by liveness-analysis). + RAState* state; //!< Optional saved \ref RAState. + uint32_t tiedTotal; //!< Total count of \ref TiedReg regs. +}; + +// ============================================================================ +// [asmjit::RAState] +// ============================================================================ + +//! Variables' state. +struct RAState {}; + +// ============================================================================ +// [asmjit::RAPass] +// ============================================================================ + +//! \internal +//! +//! Register allocator pipeline used by \ref CodeCompiler. +struct RAPass : public CBPass { +public: + ASMJIT_NONCOPYABLE(RAPass) + + typedef void (ASMJIT_CDECL* TraceNodeFunc)(RAPass* self, CBNode* node_, const char* prefix); + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + RAPass() noexcept; + virtual ~RAPass() noexcept; + + // -------------------------------------------------------------------------- + // [Interface] + // -------------------------------------------------------------------------- + + virtual Error process(Zone* zone) noexcept override; + + //! Run the register allocator for a given function `func`. + virtual Error compile(CCFunc* func) noexcept; + + //! Called by `compile()` to prepare the register allocator to process the + //! given function. It should reset and set-up everything (i.e. no states + //! from a previous compilation should prevail). + virtual Error prepare(CCFunc* func) noexcept; + + //! Called after `compile()` to clean everything up, no matter if it + //! succeeded or failed. + virtual void cleanup() noexcept; + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get the associated `CodeCompiler`. + ASMJIT_INLINE CodeCompiler* cc() const noexcept { return static_cast(_cb); } + + //! Get function. + ASMJIT_INLINE CCFunc* getFunc() const noexcept { return _func; } + //! Get stop node. + ASMJIT_INLINE CBNode* getStop() const noexcept { return _stop; } + + // -------------------------------------------------------------------------- + // [State] + // -------------------------------------------------------------------------- + + //! Get current state. + ASMJIT_INLINE RAState* getState() const { return _state; } + + //! Load current state from `target` state. + virtual void loadState(RAState* src) = 0; + + //! Save current state, returning new `RAState` instance. + virtual RAState* saveState() = 0; + + //! Change the current state to `target` state. + virtual void switchState(RAState* src) = 0; + + //! Change the current state to the intersection of two states `a` and `b`. + virtual void intersectStates(RAState* a, RAState* b) = 0; + + // -------------------------------------------------------------------------- + // [Context] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE Error assignRAId(VirtReg* vreg) noexcept { + // Likely as a single virtual register would be mostly used more than once, + // this means that each virtual register will hit one bad case (doesn't + // have id) and then all likely cases. + if (ASMJIT_LIKELY(vreg->_raId != kInvalidValue)) return kErrorOk; + + uint32_t raId = static_cast(_contextVd.getLength()); + ASMJIT_PROPAGATE(_contextVd.append(&_heap, vreg)); + + vreg->_raId = raId; + return kErrorOk; + } + + // -------------------------------------------------------------------------- + // [Mem] + // -------------------------------------------------------------------------- + + RACell* _newVarCell(VirtReg* vreg); + RACell* _newStackCell(uint32_t size, uint32_t alignment); + + ASMJIT_INLINE RACell* getVarCell(VirtReg* vreg) { + RACell* cell = vreg->getMemCell(); + return cell ? cell : _newVarCell(vreg); + } + + virtual Error resolveCellOffsets(); + + // -------------------------------------------------------------------------- + // [Bits] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE RABits* newBits(uint32_t len) { + return static_cast( + _zone->allocZeroed(static_cast(len) * RABits::kEntitySize)); + } + + ASMJIT_INLINE RABits* copyBits(const RABits* src, uint32_t len) { + return static_cast( + _zone->dup(src, static_cast(len) * RABits::kEntitySize)); + } + + // -------------------------------------------------------------------------- + // [Fetch] + // -------------------------------------------------------------------------- + + //! Fetch. + //! + //! Fetch iterates over all nodes and gathers information about all variables + //! used. The process generates information required by register allocator, + //! variable liveness analysis and translator. + virtual Error fetch() = 0; + + // -------------------------------------------------------------------------- + // [Unreachable Code] + // -------------------------------------------------------------------------- + + //! Add unreachable-flow data to the unreachable flow list. + ASMJIT_INLINE Error addUnreachableNode(CBNode* node) { + ZoneList::Link* link = _zone->allocT::Link>(); + if (!link) return DebugUtils::errored(kErrorNoHeapMemory); + + link->setValue(node); + _unreachableList.append(link); + + return kErrorOk; + } + + //! Remove unreachable code. + virtual Error removeUnreachableCode(); + + // -------------------------------------------------------------------------- + // [Code-Flow] + // -------------------------------------------------------------------------- + + //! Add returning node (i.e. node that returns and where liveness analysis + //! should start). + ASMJIT_INLINE Error addReturningNode(CBNode* node) { + ZoneList::Link* link = _zone->allocT::Link>(); + if (!link) return DebugUtils::errored(kErrorNoHeapMemory); + + link->setValue(node); + _returningList.append(link); + + return kErrorOk; + } + + //! Add jump-flow data to the jcc flow list. + ASMJIT_INLINE Error addJccNode(CBNode* node) { + ZoneList::Link* link = _zone->allocT::Link>(); + if (!link) return DebugUtils::errored(kErrorNoHeapMemory); + + link->setValue(node); + _jccList.append(link); + + return kErrorOk; + } + + // -------------------------------------------------------------------------- + // [Analyze] + // -------------------------------------------------------------------------- + + //! Perform variable liveness analysis. + //! + //! Analysis phase iterates over nodes in reverse order and generates a bit + //! array describing variables that are alive at every node in the function. + //! When the analysis start all variables are assumed dead. When a read or + //! read/write operations of a variable is detected the variable becomes + //! alive; when only write operation is detected the variable becomes dead. + //! + //! When a label is found all jumps to that label are followed and analysis + //! repeats until all variables are resolved. + virtual Error livenessAnalysis(); + + // -------------------------------------------------------------------------- + // [Annotate] + // -------------------------------------------------------------------------- + + virtual Error annotate() = 0; + virtual Error formatInlineComment(StringBuilder& dst, CBNode* node); + + // -------------------------------------------------------------------------- + // [Translate] + // -------------------------------------------------------------------------- + + //! Translate code by allocating registers and handling state changes. + virtual Error translate() = 0; + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + Zone* _zone; //!< Zone passed to `process()`. + ZoneHeap _heap; //!< ZoneHeap that uses `_zone`. + + CCFunc* _func; //!< Function being processed. + CBNode* _stop; //!< Stop node. + + //! \internal + //! + //! Offset (how many bytes to add) to `VarMap` to get `TiedReg` array. Used + //! by liveness analysis shared across all backends. This is needed because + //! `VarMap` is a base class for a specialized version that liveness analysis + //! doesn't use, it just needs `TiedReg` array. + uint32_t _varMapToVaListOffset; + + uint8_t _emitComments; //!< Whether to emit comments. + + ZoneList _unreachableList; //!< Unreachable nodes. + ZoneList _returningList; //!< Returning nodes. + ZoneList _jccList; //!< Jump nodes. + + ZoneVector _contextVd; //!< All variables used by the current function. + RACell* _memVarCells; //!< Memory used to spill variables. + RACell* _memStackCells; //!< Memory used to allocate memory on the stack. + + uint32_t _mem1ByteVarsUsed; //!< Count of 1-byte cells. + uint32_t _mem2ByteVarsUsed; //!< Count of 2-byte cells. + uint32_t _mem4ByteVarsUsed; //!< Count of 4-byte cells. + uint32_t _mem8ByteVarsUsed; //!< Count of 8-byte cells. + uint32_t _mem16ByteVarsUsed; //!< Count of 16-byte cells. + uint32_t _mem32ByteVarsUsed; //!< Count of 32-byte cells. + uint32_t _mem64ByteVarsUsed; //!< Count of 64-byte cells. + uint32_t _memStackCellsUsed; //!< Count of stack memory cells. + + uint32_t _memMaxAlign; //!< Maximum memory alignment used by the function. + uint32_t _memVarTotal; //!< Count of bytes used by variables. + uint32_t _memStackTotal; //!< Count of bytes used by stack. + uint32_t _memAllTotal; //!< Count of bytes used by variables and stack after alignment. + + uint32_t _annotationLength; //!< Default length of an annotated instruction. + RAState* _state; //!< Current RA state. +}; + +//! \} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" + +// [Guard] +#endif // !ASMJIT_DISABLE_COMPILER +#endif // _ASMJIT_BASE_REGALLOC_P_H diff --git a/libraries/asmjit/asmjit/base/runtime.cpp b/libraries/asmjit/asmjit/base/runtime.cpp new file mode 100644 index 00000000000..f074885a383 --- /dev/null +++ b/libraries/asmjit/asmjit/base/runtime.cpp @@ -0,0 +1,147 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Export] +#define ASMJIT_EXPORTS + +// [Dependencies] +#include "../base/assembler.h" +#include "../base/cpuinfo.h" +#include "../base/runtime.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +static ASMJIT_INLINE void hostFlushInstructionCache(const void* p, size_t size) noexcept { + // Only useful on non-x86 architectures. +#if !ASMJIT_ARCH_X86 && !ASMJIT_ARCH_X64 +# if ASMJIT_OS_WINDOWS + // Windows has a built-in support in kernel32.dll. + ::FlushInstructionCache(_memMgr.getProcessHandle(), p, size); +# endif // ASMJIT_OS_WINDOWS +#else + ASMJIT_UNUSED(p); + ASMJIT_UNUSED(size); +#endif // !ASMJIT_ARCH_X86 && !ASMJIT_ARCH_X64 +} + +static ASMJIT_INLINE uint32_t hostDetectNaturalStackAlignment() noexcept { + // Alignment is assumed to match the pointer-size by default. + uint32_t alignment = sizeof(intptr_t); + + // X86 & X64 + // --------- + // + // - 32-bit X86 requires stack to be aligned to 4 bytes. Modern Linux, Mac + // and UNIX guarantees 16-byte stack alignment even on 32-bit. I'm not + // sure about all other UNIX operating systems, because 16-byte alignment + //! is addition to an older specification. + // - 64-bit X86 requires stack to be aligned to at least 16 bytes. +#if ASMJIT_ARCH_X86 || ASMJIT_ARCH_X64 + int kIsModernOS = ASMJIT_OS_LINUX || // Linux & ANDROID. + ASMJIT_OS_MAC || // OSX and iOS. + ASMJIT_OS_BSD ; // BSD variants. + alignment = ASMJIT_ARCH_X64 || kIsModernOS ? 16 : 4; +#endif + + // ARM32 & ARM64 + // ------------- + // + // - 32-bit ARM requires stack to be aligned to 8 bytes. + // - 64-bit ARM requires stack to be aligned to 16 bytes. +#if ASMJIT_ARCH_ARM32 || ASMJIT_ARCH_ARM64 + alignment = ASMJIT_ARCH_ARM32 ? 8 : 16; +#endif + + return alignment; +} + + +// ============================================================================ +// [asmjit::Runtime - Construction / Destruction] +// ============================================================================ + +Runtime::Runtime() noexcept + : _codeInfo(), + _runtimeType(kRuntimeNone), + _allocType(VMemMgr::kAllocFreeable) {} +Runtime::~Runtime() noexcept {} + +// ============================================================================ +// [asmjit::HostRuntime - Construction / Destruction] +// ============================================================================ + +HostRuntime::HostRuntime() noexcept { + _runtimeType = kRuntimeJit; + + // Setup the CodeInfo of this Runtime. + _codeInfo._archInfo = CpuInfo::getHost().getArchInfo(); + _codeInfo._stackAlignment = static_cast(hostDetectNaturalStackAlignment()); + _codeInfo._cdeclCallConv = CallConv::kIdHostCDecl; + _codeInfo._stdCallConv = CallConv::kIdHostStdCall; + _codeInfo._fastCallConv = CallConv::kIdHostFastCall; +} +HostRuntime::~HostRuntime() noexcept {} + +// ============================================================================ +// [asmjit::HostRuntime - Interface] +// ============================================================================ + +void HostRuntime::flush(const void* p, size_t size) noexcept { + hostFlushInstructionCache(p, size); +} + +// ============================================================================ +// [asmjit::JitRuntime - Construction / Destruction] +// ============================================================================ + +JitRuntime::JitRuntime() noexcept {} +JitRuntime::~JitRuntime() noexcept {} + +// ============================================================================ +// [asmjit::JitRuntime - Interface] +// ============================================================================ + +Error JitRuntime::_add(void** dst, CodeHolder* code) noexcept { + size_t codeSize = code->getCodeSize(); + if (ASMJIT_UNLIKELY(codeSize == 0)) { + *dst = nullptr; + return DebugUtils::errored(kErrorNoCodeGenerated); + } + + void* p = _memMgr.alloc(codeSize, getAllocType()); + if (ASMJIT_UNLIKELY(!p)) { + *dst = nullptr; + return DebugUtils::errored(kErrorNoVirtualMemory); + } + + // Relocate the code and release the unused memory back to `VMemMgr`. + size_t relocSize = code->relocate(p); + if (ASMJIT_UNLIKELY(relocSize == 0)) { + *dst = nullptr; + _memMgr.release(p); + return DebugUtils::errored(kErrorInvalidState); + } + + if (relocSize < codeSize) + _memMgr.shrink(p, relocSize); + + flush(p, relocSize); + *dst = p; + + return kErrorOk; +} + +Error JitRuntime::_release(void* p) noexcept { + return _memMgr.release(p); +} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" diff --git a/libraries/asmjit/asmjit/base/runtime.h b/libraries/asmjit/asmjit/base/runtime.h new file mode 100644 index 00000000000..730b6a8b4ba --- /dev/null +++ b/libraries/asmjit/asmjit/base/runtime.h @@ -0,0 +1,198 @@ +// [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] +#include "../base/codeholder.h" +#include "../base/vmem.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +// ============================================================================ +// [Forward Declarations] +// ============================================================================ + +class CodeHolder; + +//! \addtogroup asmjit_base +//! \{ + +// ============================================================================ +// [asmjit::Runtime] +// ============================================================================ + +//! Base runtime. +class ASMJIT_VIRTAPI Runtime { +public: + ASMJIT_NONCOPYABLE(Runtime) + + ASMJIT_ENUM(RuntimeType) { + kRuntimeNone = 0, + kRuntimeJit = 1, + kRuntimeRemote = 2 + }; + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a `Runtime` instance. + ASMJIT_API Runtime() noexcept; + //! Destroy the `Runtime` instance. + ASMJIT_API virtual ~Runtime() noexcept; + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get CodeInfo of this runtime. + //! + //! CodeInfo can be used to setup a CodeHolder in case you plan to generate a + //! code compatible and executable by this Runtime. + ASMJIT_INLINE const CodeInfo& getCodeInfo() const noexcept { return _codeInfo; } + + //! Get the Runtime's architecture type, see \ref ArchInfo::Type. + ASMJIT_INLINE uint32_t getArchType() const noexcept { return _codeInfo.getArchType(); } + //! Get the Runtime's architecture sub-type, see \ref ArchInfo::SubType. + ASMJIT_INLINE uint32_t getArchSubType() const noexcept { return _codeInfo.getArchSubType(); } + + //! Get the runtime type, see \ref Type. + ASMJIT_INLINE uint32_t getRuntimeType() const noexcept { return _runtimeType; } + + // -------------------------------------------------------------------------- + // [Interface] + // -------------------------------------------------------------------------- + + // NOTE: To allow passing function pointers to `add()` and `release()` the + // virtual methods are prefixed with `_` and called from templates. + + template + ASMJIT_INLINE Error add(Func* dst, CodeHolder* code) noexcept { + return _add(Internal::ptr_cast(dst), code); + } + + template + ASMJIT_INLINE Error release(Func dst) noexcept { + return _release(Internal::ptr_cast(dst)); + } + + //! Allocate a memory needed for a code stored in the \ref CodeHolder and + //! relocate it to the target location. + //! + //! The beginning of the memory allocated for the function is returned in + //! `dst`. If failed the \ref Error code is returned and `dst` is set to null + //! (this means that you don't have to set it to null before calling `add()`). + virtual Error _add(void** dst, CodeHolder* code) noexcept = 0; + + //! Release `p` allocated by `add()`. + virtual Error _release(void* p) noexcept = 0; + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + CodeInfo _codeInfo; //!< Basic information about the Runtime's code. + uint8_t _runtimeType; //!< Type of the runtime. + uint8_t _allocType; //!< Type of the allocator the Runtime uses. + uint8_t _reserved[6]; //!< \internal +}; + +// ============================================================================ +// [asmjit::HostRuntime] +// ============================================================================ + +//! Runtime designed to be used in the same process the code is generated in. +class ASMJIT_VIRTAPI HostRuntime : public Runtime { +public: + ASMJIT_NONCOPYABLE(HostRuntime) + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a `HostRuntime` instance. + ASMJIT_API HostRuntime() noexcept; + //! Destroy the `HostRuntime` instance. + ASMJIT_API virtual ~HostRuntime() noexcept; + + // -------------------------------------------------------------------------- + // [Interface] + // -------------------------------------------------------------------------- + + //! 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's cache. + //! + //! Flushing is basically a NOP under X86/X64, but is needed by architectures + //! that do not have a transparent instruction cache like ARM. + //! + //! 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(const void* p, size_t size) noexcept; +}; + +// ============================================================================ +// [asmjit::JitRuntime] +// ============================================================================ + +//! Runtime designed to store and execute code generated at runtime (JIT). +class ASMJIT_VIRTAPI JitRuntime : public HostRuntime { +public: + ASMJIT_NONCOPYABLE(JitRuntime) + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a `JitRuntime` instance. + ASMJIT_API JitRuntime() noexcept; + //! Destroy the `JitRuntime` instance. + ASMJIT_API virtual ~JitRuntime() noexcept; + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get the type of allocation. + ASMJIT_INLINE uint32_t getAllocType() const noexcept { return _allocType; } + //! Set the type of allocation. + ASMJIT_INLINE void setAllocType(uint32_t allocType) noexcept { _allocType = allocType; } + + //! Get the virtual memory manager. + ASMJIT_INLINE VMemMgr* getMemMgr() const noexcept { return const_cast(&_memMgr); } + + // -------------------------------------------------------------------------- + // [Interface] + // -------------------------------------------------------------------------- + + ASMJIT_API Error _add(void** dst, CodeHolder* code) noexcept override; + ASMJIT_API Error _release(void* p) noexcept override; + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + //! Virtual memory manager. + VMemMgr _memMgr; +}; + +//! \} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" + +// [Guard] +#endif // _ASMJIT_BASE_RUNTIME_H diff --git a/libraries/asmjit/asmjit/core/datatypes.h b/libraries/asmjit/asmjit/base/simdtypes.h similarity index 54% rename from libraries/asmjit/asmjit/core/datatypes.h rename to libraries/asmjit/asmjit/base/simdtypes.h index b262cab9137..5c1c75a7ac1 100644 --- a/libraries/asmjit/asmjit/core/datatypes.h +++ b/libraries/asmjit/asmjit/base/simdtypes.h @@ -1,17 +1,22 @@ // [AsmJit] -// Machine Code Generation for C++. +// Complete x86/x64 JIT and Remote Assembler for C++. // // [License] // Zlib - See LICENSE.md file in the package. -#ifndef _ASMJIT_CORE_DATATYPES_H -#define _ASMJIT_CORE_DATATYPES_H +// [Guard] +#ifndef _ASMJIT_BASE_SIMDTYPES_H +#define _ASMJIT_BASE_SIMDTYPES_H -#include "../core/globals.h" +// [Dependencies] +#include "../base/globals.h" -ASMJIT_BEGIN_NAMESPACE +// [Api-Begin] +#include "../asmjit_apibegin.h" -//! \addtogroup asmjit_support +namespace asmjit { + +//! \addtogroup asmjit_base //! \{ // ============================================================================ @@ -20,47 +25,26 @@ ASMJIT_BEGIN_NAMESPACE //! 64-bit data useful for creating SIMD constants. union Data64 { - //! 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]; + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- - //! Array of two SP-FP values. - float sf[2]; - //! Array of one DP-FP value. - double df[1]; - - //! \name Construction & Destruction - //! \{ - - //! Sets all eight 8-bit signed integers. - static inline Data64 fromI8(int8_t x0) noexcept { + //! Set all eight 8-bit signed integers. + static ASMJIT_INLINE Data64 fromI8(int8_t x0) noexcept { Data64 self; self.setI8(x0); return self; } - //! Sets all eight 8-bit unsigned integers. - static inline Data64 fromU8(uint8_t x0) noexcept { + //! Set all eight 8-bit unsigned integers. + static ASMJIT_INLINE Data64 fromU8(uint8_t x0) noexcept { Data64 self; self.setU8(x0); return self; } - //! Sets all eight 8-bit signed integers. - static inline Data64 fromI8( + //! Set all eight 8-bit signed integers. + static ASMJIT_INLINE Data64 fromI8( int8_t x0, int8_t x1, int8_t x2, int8_t x3, int8_t x4, int8_t x5, int8_t x6, int8_t x7) noexcept { Data64 self; @@ -68,8 +52,8 @@ union Data64 { return self; } - //! Sets all eight 8-bit unsigned integers. - static inline Data64 fromU8( + //! Set all eight 8-bit unsigned integers. + static ASMJIT_INLINE Data64 fromU8( uint8_t x0, uint8_t x1, uint8_t x2, uint8_t x3, uint8_t x4, uint8_t x5, uint8_t x6, uint8_t x7) noexcept { Data64 self; @@ -77,208 +61,233 @@ union Data64 { return self; } - //! Sets all four 16-bit signed integers. - static inline Data64 fromI16(int16_t x0) noexcept { + //! Set all four 16-bit signed integers. + static ASMJIT_INLINE Data64 fromI16(int16_t x0) noexcept { Data64 self; self.setI16(x0); return self; } - //! Sets all four 16-bit unsigned integers. - static inline Data64 fromU16(uint16_t x0) noexcept { + //! Set all four 16-bit unsigned integers. + static ASMJIT_INLINE Data64 fromU16(uint16_t x0) noexcept { Data64 self; self.setU16(x0); return self; } - //! Sets all four 16-bit signed integers. - static inline Data64 fromI16(int16_t x0, int16_t x1, int16_t x2, int16_t x3) noexcept { + //! Set all four 16-bit signed integers. + static ASMJIT_INLINE Data64 fromI16(int16_t x0, int16_t x1, int16_t x2, int16_t x3) noexcept { Data64 self; self.setI16(x0, x1, x2, x3); return self; } - //! Sets all four 16-bit unsigned integers. - static inline Data64 fromU16(uint16_t x0, uint16_t x1, uint16_t x2, uint16_t x3) noexcept { + //! Set all four 16-bit unsigned integers. + static ASMJIT_INLINE Data64 fromU16(uint16_t x0, uint16_t x1, uint16_t x2, uint16_t x3) noexcept { Data64 self; self.setU16(x0, x1, x2, x3); return self; } - //! Sets all two 32-bit signed integers. - static inline Data64 fromI32(int32_t x0) noexcept { + //! Set all two 32-bit signed integers. + static ASMJIT_INLINE Data64 fromI32(int32_t x0) noexcept { Data64 self; self.setI32(x0); return self; } - //! Sets all two 32-bit unsigned integers. - static inline Data64 fromU32(uint32_t x0) noexcept { + //! Set all two 32-bit unsigned integers. + static ASMJIT_INLINE Data64 fromU32(uint32_t x0) noexcept { Data64 self; self.setU32(x0); return self; } - //! Sets all two 32-bit signed integers. - static inline Data64 fromI32(int32_t x0, int32_t x1) noexcept { + //! Set all two 32-bit signed integers. + static ASMJIT_INLINE Data64 fromI32(int32_t x0, int32_t x1) noexcept { Data64 self; self.setI32(x0, x1); return self; } - //! Sets all two 32-bit unsigned integers. - static inline Data64 fromU32(uint32_t x0, uint32_t x1) noexcept { + //! Set all two 32-bit unsigned integers. + static ASMJIT_INLINE Data64 fromU32(uint32_t x0, uint32_t x1) noexcept { Data64 self; self.setU32(x0, x1); return self; } - //! Sets 64-bit signed integer. - static inline Data64 fromI64(int64_t x0) noexcept { + //! Set 64-bit signed integer. + static ASMJIT_INLINE Data64 fromI64(int64_t x0) noexcept { Data64 self; self.setI64(x0); return self; } - //! Sets 64-bit unsigned integer. - static inline Data64 fromU64(uint64_t x0) noexcept { + //! Set 64-bit unsigned integer. + static ASMJIT_INLINE Data64 fromU64(uint64_t x0) noexcept { Data64 self; self.setU64(x0); return self; } - //! Sets all two SP-FP values. - static inline Data64 fromF32(float x0) noexcept { + //! Set all two SP-FP values. + static ASMJIT_INLINE Data64 fromF32(float x0) noexcept { Data64 self; self.setF32(x0); return self; } - //! Sets all two SP-FP values. - static inline Data64 fromF32(float x0, float x1) noexcept { + //! Set all two SP-FP values. + static ASMJIT_INLINE Data64 fromF32(float x0, float x1) noexcept { Data64 self; self.setF32(x0, x1); return self; } - //! Sets all two SP-FP values. - static inline Data64 fromF64(double x0) noexcept { + //! Set all two SP-FP values. + static ASMJIT_INLINE Data64 fromF64(double x0) noexcept { Data64 self; self.setF64(x0); return self; } - //! \} + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- - //! \name Accessors - //! \{ - - //! Sets all eight 8-bit signed integers. - inline void setI8(int8_t x0) noexcept { - setU8(uint8_t(x0)); + //! Set all eight 8-bit signed integers. + ASMJIT_INLINE void setI8(int8_t x0) noexcept { + setU8(static_cast(x0)); } - //! Sets all eight 8-bit unsigned integers. - inline void setU8(uint8_t x0) noexcept { - if (ASMJIT_ARCH_BITS >= 64) { - uint64_t xq = uint64_t(x0) * 0x0101010101010101u; + //! Set all eight 8-bit unsigned integers. + ASMJIT_INLINE void setU8(uint8_t x0) noexcept { + if (ASMJIT_ARCH_64BIT) { + uint64_t xq = static_cast(x0) * ASMJIT_UINT64_C(0x0101010101010101); uq[0] = xq; } else { - uint32_t xd = uint32_t(x0) * 0x01010101u; + uint32_t xd = static_cast(x0) * static_cast(0x01010101U); ud[0] = xd; ud[1] = xd; } } - //! Sets all eight 8-bit signed integers. - inline void setI8( + //! Set all eight 8-bit signed integers. + ASMJIT_INLINE void setI8( int8_t x0, int8_t x1, int8_t x2, int8_t x3, int8_t x4, int8_t x5, int8_t x6, int8_t x7) noexcept { sb[0] = x0; sb[1] = x1; sb[2] = x2; sb[3] = x3; sb[4] = x4; sb[5] = x5; sb[6] = x6; sb[7] = x7; } - //! Sets all eight 8-bit unsigned integers. - inline void setU8( + //! Set all eight 8-bit unsigned integers. + ASMJIT_INLINE void setU8( uint8_t x0, uint8_t x1, uint8_t x2, uint8_t x3, uint8_t x4, uint8_t x5, uint8_t x6, uint8_t x7) noexcept { ub[0] = x0; ub[1] = x1; ub[2] = x2; ub[3] = x3; ub[4] = x4; ub[5] = x5; ub[6] = x6; ub[7] = x7; } - //! Sets all four 16-bit signed integers. - inline void setI16(int16_t x0) noexcept { - setU16(uint16_t(x0)); + //! Set all four 16-bit signed integers. + ASMJIT_INLINE void setI16(int16_t x0) noexcept { + setU16(static_cast(x0)); } - //! Sets all four 16-bit unsigned integers. - inline void setU16(uint16_t x0) noexcept { - if (ASMJIT_ARCH_BITS >= 64) { - uint64_t xq = uint64_t(x0) * 0x0001000100010001u; + //! Set all four 16-bit unsigned integers. + ASMJIT_INLINE void setU16(uint16_t x0) noexcept { + if (ASMJIT_ARCH_64BIT) { + uint64_t xq = static_cast(x0) * ASMJIT_UINT64_C(0x0001000100010001); uq[0] = xq; } else { - uint32_t xd = uint32_t(x0) * 0x00010001u; + uint32_t xd = static_cast(x0) * static_cast(0x00010001U); ud[0] = xd; ud[1] = xd; } } - //! Sets all four 16-bit signed integers. - inline void setI16(int16_t x0, int16_t x1, int16_t x2, int16_t x3) noexcept { + //! Set all four 16-bit signed integers. + ASMJIT_INLINE void setI16(int16_t x0, int16_t x1, int16_t x2, int16_t x3) noexcept { sw[0] = x0; sw[1] = x1; sw[2] = x2; sw[3] = x3; } - //! Sets all four 16-bit unsigned integers. - inline void setU16(uint16_t x0, uint16_t x1, uint16_t x2, uint16_t x3) noexcept { + //! Set all four 16-bit unsigned integers. + ASMJIT_INLINE void setU16(uint16_t x0, uint16_t x1, uint16_t x2, uint16_t x3) noexcept { uw[0] = x0; uw[1] = x1; uw[2] = x2; uw[3] = x3; } - //! Sets all two 32-bit signed integers. - inline void setI32(int32_t x0) noexcept { + //! Set all two 32-bit signed integers. + ASMJIT_INLINE void setI32(int32_t x0) noexcept { sd[0] = x0; sd[1] = x0; } - //! Sets all two 32-bit unsigned integers. - inline void setU32(uint32_t x0) noexcept { + //! Set all two 32-bit unsigned integers. + ASMJIT_INLINE void setU32(uint32_t x0) noexcept { ud[0] = x0; ud[1] = x0; } - //! Sets all two 32-bit signed integers. - inline void setI32(int32_t x0, int32_t x1) noexcept { + //! Set all two 32-bit signed integers. + ASMJIT_INLINE void setI32(int32_t x0, int32_t x1) noexcept { sd[0] = x0; sd[1] = x1; } - //! Sets all two 32-bit unsigned integers. - inline void setU32(uint32_t x0, uint32_t x1) noexcept { + //! Set all two 32-bit unsigned integers. + ASMJIT_INLINE void setU32(uint32_t x0, uint32_t x1) noexcept { ud[0] = x0; ud[1] = x1; } - //! Sets 64-bit signed integer. - inline void setI64(int64_t x0) noexcept { + //! Set 64-bit signed integer. + ASMJIT_INLINE void setI64(int64_t x0) noexcept { sq[0] = x0; } - //! Sets 64-bit unsigned integer. - inline void setU64(uint64_t x0) noexcept { + //! Set 64-bit unsigned integer. + ASMJIT_INLINE void setU64(uint64_t x0) noexcept { uq[0] = x0; } - //! Sets all two SP-FP values. - inline void setF32(float x0) noexcept { + //! Set all two SP-FP values. + ASMJIT_INLINE void setF32(float x0) noexcept { sf[0] = x0; sf[1] = x0; } - //! Sets all two SP-FP values. - inline void setF32(float x0, float x1) noexcept { + //! Set all two SP-FP values. + ASMJIT_INLINE void setF32(float x0, float x1) noexcept { sf[0] = x0; sf[1] = x1; } - //! Sets all two SP-FP values. - inline void setF64(double x0) noexcept { + //! Set all two SP-FP values. + ASMJIT_INLINE void setF64(double x0) noexcept { 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]; }; // ============================================================================ @@ -287,47 +296,26 @@ union Data64 { //! 128-bit data useful for creating SIMD constants. union Data128 { - //! 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]; + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- - //! \name Construction & Destruction - //! \{ - - //! Sets all sixteen 8-bit signed integers. - static inline Data128 fromI8(int8_t x0) noexcept { + //! Set all sixteen 8-bit signed integers. + static ASMJIT_INLINE Data128 fromI8(int8_t x0) noexcept { Data128 self; self.setI8(x0); return self; } - //! Sets all sixteen 8-bit unsigned integers. - static inline Data128 fromU8(uint8_t x0) noexcept { + //! Set all sixteen 8-bit unsigned integers. + static ASMJIT_INLINE Data128 fromU8(uint8_t x0) noexcept { Data128 self; self.setU8(x0); return self; } - //! Sets all sixteen 8-bit signed integers. - static inline Data128 fromI8( + //! Set all sixteen 8-bit signed integers. + static ASMJIT_INLINE Data128 fromI8( 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, @@ -338,8 +326,8 @@ union Data128 { return self; } - //! Sets all sixteen 8-bit unsigned integers. - static inline Data128 fromU8( + //! Set all sixteen 8-bit unsigned integers. + static ASMJIT_INLINE Data128 fromU8( 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, @@ -350,22 +338,22 @@ union Data128 { return self; } - //! Sets all eight 16-bit signed integers. - static inline Data128 fromI16(int16_t x0) noexcept { + //! Set all eight 16-bit signed integers. + static ASMJIT_INLINE Data128 fromI16(int16_t x0) noexcept { Data128 self; self.setI16(x0); return self; } - //! Sets all eight 16-bit unsigned integers. - static inline Data128 fromU16(uint16_t x0) noexcept { + //! Set all eight 16-bit unsigned integers. + static ASMJIT_INLINE Data128 fromU16(uint16_t x0) noexcept { Data128 self; self.setU16(x0); return self; } - //! Sets all eight 16-bit signed integers. - static inline Data128 fromI16( + //! Set all eight 16-bit signed integers. + static ASMJIT_INLINE Data128 fromI16( int16_t x0, int16_t x1, int16_t x2, int16_t x3, int16_t x4, int16_t x5, int16_t x6, int16_t x7) noexcept { Data128 self; @@ -373,8 +361,8 @@ union Data128 { return self; } - //! Sets all eight 16-bit unsigned integers. - static inline Data128 fromU16( + //! Set all eight 16-bit unsigned integers. + static ASMJIT_INLINE Data128 fromU16( uint16_t x0, uint16_t x1, uint16_t x2, uint16_t x3, uint16_t x4, uint16_t x5, uint16_t x6, uint16_t x7) noexcept { Data128 self; @@ -382,109 +370,108 @@ union Data128 { return self; } - //! Sets all four 32-bit signed integers. - static inline Data128 fromI32(int32_t x0) noexcept { + //! Set all four 32-bit signed integers. + static ASMJIT_INLINE Data128 fromI32(int32_t x0) noexcept { Data128 self; self.setI32(x0); return self; } - //! Sets all four 32-bit unsigned integers. - static inline Data128 fromU32(uint32_t x0) noexcept { + //! Set all four 32-bit unsigned integers. + static ASMJIT_INLINE Data128 fromU32(uint32_t x0) noexcept { Data128 self; self.setU32(x0); return self; } - //! Sets all four 32-bit signed integers. - static inline Data128 fromI32(int32_t x0, int32_t x1, int32_t x2, int32_t x3) noexcept { + //! Set all four 32-bit signed integers. + static ASMJIT_INLINE Data128 fromI32(int32_t x0, int32_t x1, int32_t x2, int32_t x3) noexcept { Data128 self; self.setI32(x0, x1, x2, x3); return self; } - //! Sets all four 32-bit unsigned integers. - static inline Data128 fromU32(uint32_t x0, uint32_t x1, uint32_t x2, uint32_t x3) noexcept { + //! Set all four 32-bit unsigned integers. + static ASMJIT_INLINE Data128 fromU32(uint32_t x0, uint32_t x1, uint32_t x2, uint32_t x3) noexcept { Data128 self; self.setU32(x0, x1, x2, x3); return self; } - //! Sets all two 64-bit signed integers. - static inline Data128 fromI64(int64_t x0) noexcept { + //! Set all two 64-bit signed integers. + static ASMJIT_INLINE Data128 fromI64(int64_t x0) noexcept { Data128 self; self.setI64(x0); return self; } - //! Sets all two 64-bit unsigned integers. - static inline Data128 fromU64(uint64_t x0) noexcept { + //! Set all two 64-bit unsigned integers. + static ASMJIT_INLINE Data128 fromU64(uint64_t x0) noexcept { Data128 self; self.setU64(x0); return self; } - //! Sets all two 64-bit signed integers. - static inline Data128 fromI64(int64_t x0, int64_t x1) noexcept { + //! Set all two 64-bit signed integers. + static ASMJIT_INLINE Data128 fromI64(int64_t x0, int64_t x1) noexcept { Data128 self; self.setI64(x0, x1); return self; } - //! Sets all two 64-bit unsigned integers. - static inline Data128 fromU64(uint64_t x0, uint64_t x1) noexcept { + //! Set all two 64-bit unsigned integers. + static ASMJIT_INLINE Data128 fromU64(uint64_t x0, uint64_t x1) noexcept { Data128 self; self.setU64(x0, x1); return self; } - //! Sets all four SP-FP floats. - static inline Data128 fromF32(float x0) noexcept { + //! Set all four SP-FP floats. + static ASMJIT_INLINE Data128 fromF32(float x0) noexcept { Data128 self; self.setF32(x0); return self; } - //! Sets all four SP-FP floats. - static inline Data128 fromF32(float x0, float x1, float x2, float x3) noexcept { + //! Set all four SP-FP floats. + static ASMJIT_INLINE Data128 fromF32(float x0, float x1, float x2, float x3) noexcept { Data128 self; self.setF32(x0, x1, x2, x3); return self; } - //! Sets all two DP-FP floats. - static inline Data128 fromF64(double x0) noexcept { + //! Set all two DP-FP floats. + static ASMJIT_INLINE Data128 fromF64(double x0) noexcept { Data128 self; self.setF64(x0); return self; } - //! Sets all two DP-FP floats. - static inline Data128 fromF64(double x0, double x1) noexcept { + //! Set all two DP-FP floats. + static ASMJIT_INLINE Data128 fromF64(double x0, double x1) noexcept { Data128 self; self.setF64(x0, x1); return self; } - //! \} - - //! \name Accessors - //! \{ + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- - //! Sets all sixteen 8-bit signed integers. - inline void setI8(int8_t x0) noexcept { - setU8(uint8_t(x0)); + //! Set all sixteen 8-bit signed integers. + ASMJIT_INLINE void setI8(int8_t x0) noexcept { + setU8(static_cast(x0)); } - //! Sets all sixteen 8-bit unsigned integers. - inline void setU8(uint8_t x0) noexcept { - if (ASMJIT_ARCH_BITS >= 64) { - uint64_t xq = uint64_t(x0) * 0x0101010101010101u; + //! Set all sixteen 8-bit unsigned integers. + ASMJIT_INLINE void setU8(uint8_t x0) noexcept { + if (ASMJIT_ARCH_64BIT) { + uint64_t xq = static_cast(x0) * ASMJIT_UINT64_C(0x0101010101010101); uq[0] = xq; uq[1] = xq; } else { - uint32_t xd = uint32_t(x0) * 0x01010101u; + uint32_t xd = static_cast(x0) * static_cast(0x01010101U); ud[0] = xd; ud[1] = xd; ud[2] = xd; @@ -492,8 +479,8 @@ union Data128 { } } - //! Sets all sixteen 8-bit signed integers. - inline void setI8( + //! Set all sixteen 8-bit signed integers. + ASMJIT_INLINE void setI8( 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, @@ -505,8 +492,8 @@ union Data128 { sb[12] = x12; sb[13] = x13; sb[14] = x14; sb[15] = x15; } - //! Sets all sixteen 8-bit unsigned integers. - inline void setU8( + //! Set all sixteen 8-bit unsigned integers. + ASMJIT_INLINE void setU8( 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, @@ -518,20 +505,20 @@ union Data128 { ub[12] = x12; ub[13] = x13; ub[14] = x14; ub[15] = x15; } - //! Sets all eight 16-bit signed integers. - inline void setI16(int16_t x0) noexcept { - setU16(uint16_t(x0)); + //! Set all eight 16-bit signed integers. + ASMJIT_INLINE void setI16(int16_t x0) noexcept { + setU16(static_cast(x0)); } - //! Sets all eight 16-bit unsigned integers. - inline void setU16(uint16_t x0) noexcept { - if (ASMJIT_ARCH_BITS >= 64) { - uint64_t xq = uint64_t(x0) * 0x0001000100010001u; + //! Set all eight 16-bit unsigned integers. + ASMJIT_INLINE void setU16(uint16_t x0) noexcept { + if (ASMJIT_ARCH_64BIT) { + uint64_t xq = static_cast(x0) * ASMJIT_UINT64_C(0x0001000100010001); uq[0] = xq; uq[1] = xq; } else { - uint32_t xd = uint32_t(x0) * 0x00010001u; + uint32_t xd = static_cast(x0) * static_cast(0x00010001U); ud[0] = xd; ud[1] = xd; ud[2] = xd; @@ -539,31 +526,31 @@ union Data128 { } } - //! Sets all eight 16-bit signed integers. - inline void setI16( + //! Set all eight 16-bit signed integers. + ASMJIT_INLINE void setI16( int16_t x0, int16_t x1, int16_t x2, int16_t x3, int16_t x4, int16_t x5, int16_t x6, int16_t x7) noexcept { sw[0] = x0; sw[1] = x1; sw[2] = x2; sw[3] = x3; sw[4] = x4; sw[5] = x5; sw[6] = x6; sw[7] = x7; } - //! Sets all eight 16-bit unsigned integers. - inline void setU16( + //! Set all eight 16-bit unsigned integers. + ASMJIT_INLINE void setU16( uint16_t x0, uint16_t x1, uint16_t x2, uint16_t x3, uint16_t x4, uint16_t x5, uint16_t x6, uint16_t x7) noexcept { uw[0] = x0; uw[1] = x1; uw[2] = x2; uw[3] = x3; uw[4] = x4; uw[5] = x5; uw[6] = x6; uw[7] = x7; } - //! Sets all four 32-bit signed integers. - inline void setI32(int32_t x0) noexcept { - setU32(uint32_t(x0)); + //! Set all four 32-bit signed integers. + ASMJIT_INLINE void setI32(int32_t x0) noexcept { + setU32(static_cast(x0)); } - //! Sets all four 32-bit unsigned integers. - inline void setU32(uint32_t x0) noexcept { - if (ASMJIT_ARCH_BITS >= 64) { - uint64_t t = (uint64_t(x0) << 32) + x0; + //! Set all four 32-bit unsigned integers. + ASMJIT_INLINE void setU32(uint32_t x0) noexcept { + if (ASMJIT_ARCH_64BIT) { + uint64_t t = (static_cast(x0) << 32) + x0; uq[0] = t; uq[1] = t; } @@ -575,55 +562,81 @@ union Data128 { } } - //! Sets all four 32-bit signed integers. - inline void setI32(int32_t x0, int32_t x1, int32_t x2, int32_t x3) noexcept { + //! Set all four 32-bit signed integers. + ASMJIT_INLINE void setI32(int32_t x0, int32_t x1, int32_t x2, int32_t x3) noexcept { sd[0] = x0; sd[1] = x1; sd[2] = x2; sd[3] = x3; } - //! Sets all four 32-bit unsigned integers. - inline void setU32(uint32_t x0, uint32_t x1, uint32_t x2, uint32_t x3) noexcept { + //! Set all four 32-bit unsigned integers. + ASMJIT_INLINE void setU32(uint32_t x0, uint32_t x1, uint32_t x2, uint32_t x3) noexcept { ud[0] = x0; ud[1] = x1; ud[2] = x2; ud[3] = x3; } - //! Sets all two 64-bit signed integers. - inline void setI64(int64_t x0) noexcept { + //! Set all two 64-bit signed integers. + ASMJIT_INLINE void setI64(int64_t x0) noexcept { sq[0] = x0; sq[1] = x0; } - //! Sets all two 64-bit unsigned integers. - inline void setU64(uint64_t x0) noexcept { + //! Set all two 64-bit unsigned integers. + ASMJIT_INLINE void setU64(uint64_t x0) noexcept { uq[0] = x0; uq[1] = x0; } - //! Sets all two 64-bit signed integers. - inline void setI64(int64_t x0, int64_t x1) noexcept { + //! Set all two 64-bit signed integers. + ASMJIT_INLINE void setI64(int64_t x0, int64_t x1) noexcept { sq[0] = x0; sq[1] = x1; } - //! Sets all two 64-bit unsigned integers. - inline void setU64(uint64_t x0, uint64_t x1) noexcept { + //! Set all two 64-bit unsigned integers. + ASMJIT_INLINE void setU64(uint64_t x0, uint64_t x1) noexcept { uq[0] = x0; uq[1] = x1; } - //! Sets all four SP-FP floats. - inline void setF32(float x0) noexcept { + //! Set all four SP-FP floats. + ASMJIT_INLINE void setF32(float x0) noexcept { sf[0] = x0; sf[1] = x0; sf[2] = x0; sf[3] = x0; } - //! Sets all four SP-FP floats. - inline void setF32(float x0, float x1, float x2, float x3) noexcept { + //! Set all four SP-FP floats. + ASMJIT_INLINE void setF32(float x0, float x1, float x2, float x3) noexcept { sf[0] = x0; sf[1] = x1; sf[2] = x2; sf[3] = x3; } - //! Sets all two DP-FP floats. - inline void setF64(double x0) noexcept { + //! Set all two DP-FP floats. + ASMJIT_INLINE void setF64(double x0) noexcept { df[0] = x0; df[1] = x0; } - //! Sets all two DP-FP floats. - inline void setF64(double x0, double x1) noexcept { + //! Set all two DP-FP floats. + ASMJIT_INLINE void setF64(double x0, double x1) noexcept { df[0] = x0; df[1] = x1; } + + // -------------------------------------------------------------------------- + // [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]; }; // ============================================================================ @@ -632,47 +645,26 @@ union Data128 { //! 256-bit data useful for creating SIMD constants. union Data256 { - //! 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]; + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- - //! \name Construction & Destruction - //! \{ - - //! Sets all thirty two 8-bit signed integers. - static inline Data256 fromI8(int8_t x0) noexcept { + //! Set all thirty two 8-bit signed integers. + static ASMJIT_INLINE Data256 fromI8(int8_t x0) noexcept { Data256 self; self.setI8(x0); return self; } - //! Sets all thirty two 8-bit unsigned integers. - static inline Data256 fromU8(uint8_t x0) noexcept { + //! Set all thirty two 8-bit unsigned integers. + static ASMJIT_INLINE Data256 fromU8(uint8_t x0) noexcept { Data256 self; self.setU8(x0); return self; } - //! Sets all thirty two 8-bit signed integers. - static inline Data256 fromI8( + //! Set all thirty two 8-bit signed integers. + static ASMJIT_INLINE Data256 fromI8( 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, @@ -689,8 +681,8 @@ union Data256 { return self; } - //! Sets all thirty two 8-bit unsigned integers. - static inline Data256 fromU8( + //! Set all thirty two 8-bit unsigned integers. + static ASMJIT_INLINE Data256 fromU8( 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, @@ -707,22 +699,22 @@ union Data256 { return self; } - //! Sets all sixteen 16-bit signed integers. - static inline Data256 fromI16(int16_t x0) noexcept { + //! Set all sixteen 16-bit signed integers. + static ASMJIT_INLINE Data256 fromI16(int16_t x0) noexcept { Data256 self; self.setI16(x0); return self; } - //! Sets all sixteen 16-bit unsigned integers. - static inline Data256 fromU16(uint16_t x0) noexcept { + //! Set all sixteen 16-bit unsigned integers. + static ASMJIT_INLINE Data256 fromU16(uint16_t x0) noexcept { Data256 self; self.setU16(x0); return self; } - //! Sets all sixteen 16-bit signed integers. - static inline Data256 fromI16( + //! Set all sixteen 16-bit signed integers. + static ASMJIT_INLINE Data256 fromI16( 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) noexcept { @@ -731,8 +723,8 @@ union Data256 { return self; } - //! Sets all sixteen 16-bit unsigned integers. - static inline Data256 fromU16( + //! Set all sixteen 16-bit unsigned integers. + static ASMJIT_INLINE Data256 fromU16( 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) noexcept { @@ -741,22 +733,22 @@ union Data256 { return self; } - //! Sets all eight 32-bit signed integers. - static inline Data256 fromI32(int32_t x0) noexcept { + //! Set all eight 32-bit signed integers. + static ASMJIT_INLINE Data256 fromI32(int32_t x0) noexcept { Data256 self; self.setI32(x0); return self; } - //! Sets all eight 32-bit unsigned integers. - static inline Data256 fromU32(uint32_t x0) noexcept { + //! Set all eight 32-bit unsigned integers. + static ASMJIT_INLINE Data256 fromU32(uint32_t x0) noexcept { Data256 self; self.setU32(x0); return self; } - //! Sets all eight 32-bit signed integers. - static inline Data256 fromI32( + //! Set all eight 32-bit signed integers. + static ASMJIT_INLINE Data256 fromI32( int32_t x0, int32_t x1, int32_t x2, int32_t x3, int32_t x4, int32_t x5, int32_t x6, int32_t x7) noexcept { @@ -765,8 +757,8 @@ union Data256 { return self; } - //! Sets all eight 32-bit unsigned integers. - static inline Data256 fromU32( + //! Set all eight 32-bit unsigned integers. + static ASMJIT_INLINE Data256 fromU32( uint32_t x0, uint32_t x1, uint32_t x2, uint32_t x3, uint32_t x4, uint32_t x5, uint32_t x6, uint32_t x7) noexcept { @@ -775,43 +767,43 @@ union Data256 { return self; } - //! Sets all four 64-bit signed integers. - static inline Data256 fromI64(int64_t x0) noexcept { + //! Set all four 64-bit signed integers. + static ASMJIT_INLINE Data256 fromI64(int64_t x0) noexcept { Data256 self; self.setI64(x0); return self; } - //! Sets all four 64-bit unsigned integers. - static inline Data256 fromU64(uint64_t x0) noexcept { + //! Set all four 64-bit unsigned integers. + static ASMJIT_INLINE Data256 fromU64(uint64_t x0) noexcept { Data256 self; self.setU64(x0); return self; } - //! Sets all four 64-bit signed integers. - static inline Data256 fromI64(int64_t x0, int64_t x1, int64_t x2, int64_t x3) noexcept { + //! Set all four 64-bit signed integers. + static ASMJIT_INLINE Data256 fromI64(int64_t x0, int64_t x1, int64_t x2, int64_t x3) noexcept { Data256 self; self.setI64(x0, x1, x2, x3); return self; } - //! Sets all four 64-bit unsigned integers. - static inline Data256 fromU64(uint64_t x0, uint64_t x1, uint64_t x2, uint64_t x3) noexcept { + //! Set all four 64-bit unsigned integers. + static ASMJIT_INLINE Data256 fromU64(uint64_t x0, uint64_t x1, uint64_t x2, uint64_t x3) noexcept { Data256 self; self.setU64(x0, x1, x2, x3); return self; } - //! Sets all eight SP-FP floats. - static inline Data256 fromF32(float x0) noexcept { + //! Set all eight SP-FP floats. + static ASMJIT_INLINE Data256 fromF32(float x0) noexcept { Data256 self; self.setF32(x0); return self; } - //! Sets all eight SP-FP floats. - static inline Data256 fromF32( + //! Set all eight SP-FP floats. + static ASMJIT_INLINE Data256 fromF32( float x0, float x1, float x2, float x3, float x4, float x5, float x6, float x7) noexcept { @@ -820,41 +812,40 @@ union Data256 { return self; } - //! Sets all four DP-FP floats. - static inline Data256 fromF64(double x0) noexcept { + //! Set all four DP-FP floats. + static ASMJIT_INLINE Data256 fromF64(double x0) noexcept { Data256 self; self.setF64(x0); return self; } - //! Sets all four DP-FP floats. - static inline Data256 fromF64(double x0, double x1, double x2, double x3) noexcept { + //! Set all four DP-FP floats. + static ASMJIT_INLINE Data256 fromF64(double x0, double x1, double x2, double x3) noexcept { Data256 self; self.setF64(x0, x1, x2, x3); return self; } - //! \} - - //! \name Accessors - //! \{ + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- - //! Sets all thirty two 8-bit signed integers. - inline void setI8(int8_t x0) noexcept { - setU8(uint8_t(x0)); + //! Set all thirty two 8-bit signed integers. + ASMJIT_INLINE void setI8(int8_t x0) noexcept { + setU8(static_cast(x0)); } - //! Sets all thirty two 8-bit unsigned integers. - inline void setU8(uint8_t x0) noexcept { - if (ASMJIT_ARCH_BITS >= 64) { - uint64_t xq = uint64_t(x0) * 0x0101010101010101u; + //! Set all thirty two 8-bit unsigned integers. + ASMJIT_INLINE void setU8(uint8_t x0) noexcept { + if (ASMJIT_ARCH_64BIT) { + uint64_t xq = static_cast(x0) * ASMJIT_UINT64_C(0x0101010101010101); uq[0] = xq; uq[1] = xq; uq[2] = xq; uq[3] = xq; } else { - uint32_t xd = uint32_t(x0) * 0x01010101u; + uint32_t xd = static_cast(x0) * static_cast(0x01010101U); ud[0] = xd; ud[1] = xd; ud[2] = xd; @@ -866,8 +857,8 @@ union Data256 { } } - //! Sets all thirty two 8-bit signed integers. - inline void setI8( + //! Set all thirty two 8-bit signed integers. + ASMJIT_INLINE void setI8( 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, @@ -887,8 +878,8 @@ union Data256 { sb[28] = x28; sb[29] = x29; sb[30] = x30; sb[31] = x31; } - //! Sets all thirty two 8-bit unsigned integers. - inline void setU8( + //! Set all thirty two 8-bit unsigned integers. + ASMJIT_INLINE void setU8( 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, @@ -908,22 +899,22 @@ union Data256 { ub[28] = x28; ub[29] = x29; ub[30] = x30; ub[31] = x31; } - //! Sets all sixteen 16-bit signed integers. - inline void setI16(int16_t x0) noexcept { - setU16(uint16_t(x0)); + //! Set all sixteen 16-bit signed integers. + ASMJIT_INLINE void setI16(int16_t x0) noexcept { + setU16(static_cast(x0)); } - //! Sets all eight 16-bit unsigned integers. - inline void setU16(uint16_t x0) noexcept { - if (ASMJIT_ARCH_BITS >= 64) { - uint64_t xq = uint64_t(x0) * 0x0001000100010001u; + //! Set all eight 16-bit unsigned integers. + ASMJIT_INLINE void setU16(uint16_t x0) noexcept { + if (ASMJIT_ARCH_64BIT) { + uint64_t xq = static_cast(x0) * ASMJIT_UINT64_C(0x0001000100010001); uq[0] = xq; uq[1] = xq; uq[2] = xq; uq[3] = xq; } else { - uint32_t xd = uint32_t(x0) * 0x00010001u; + uint32_t xd = static_cast(x0) * static_cast(0x00010001U); ud[0] = xd; ud[1] = xd; ud[2] = xd; @@ -935,8 +926,8 @@ union Data256 { } } - //! Sets all sixteen 16-bit signed integers. - inline void setI16( + //! Set all sixteen 16-bit signed integers. + ASMJIT_INLINE void setI16( 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) noexcept { @@ -946,8 +937,8 @@ union Data256 { sw[12] = x12; sw[13] = x13; sw[14] = x14; sw[15] = x15; } - //! Sets all sixteen 16-bit unsigned integers. - inline void setU16( + //! Set all sixteen 16-bit unsigned integers. + ASMJIT_INLINE void setU16( 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) noexcept { @@ -957,15 +948,15 @@ union Data256 { uw[12] = x12; uw[13] = x13; uw[14] = x14; uw[15] = x15; } - //! Sets all eight 32-bit signed integers. - inline void setI32(int32_t x0) noexcept { - setU32(uint32_t(x0)); + //! Set all eight 32-bit signed integers. + ASMJIT_INLINE void setI32(int32_t x0) noexcept { + setU32(static_cast(x0)); } - //! Sets all eight 32-bit unsigned integers. - inline void setU32(uint32_t x0) noexcept { - if (ASMJIT_ARCH_BITS >= 64) { - uint64_t xq = (uint64_t(x0) << 32) + x0; + //! Set all eight 32-bit unsigned integers. + ASMJIT_INLINE void setU32(uint32_t x0) noexcept { + if (ASMJIT_ARCH_64BIT) { + uint64_t xq = (static_cast(x0) << 32) + x0; uq[0] = xq; uq[1] = xq; uq[2] = xq; @@ -983,8 +974,8 @@ union Data256 { } } - //! Sets all eight 32-bit signed integers. - inline void setI32( + //! Set all eight 32-bit signed integers. + ASMJIT_INLINE void setI32( int32_t x0, int32_t x1, int32_t x2, int32_t x3, int32_t x4, int32_t x5, int32_t x6, int32_t x7) noexcept { @@ -992,8 +983,8 @@ union Data256 { sd[4] = x4; sd[5] = x5; sd[6] = x6; sd[7] = x7; } - //! Sets all eight 32-bit unsigned integers. - inline void setU32( + //! Set all eight 32-bit unsigned integers. + ASMJIT_INLINE void setU32( uint32_t x0, uint32_t x1, uint32_t x2, uint32_t x3, uint32_t x4, uint32_t x5, uint32_t x6, uint32_t x7) noexcept { @@ -1001,34 +992,34 @@ union Data256 { ud[4] = x4; ud[5] = x5; ud[6] = x6; ud[7] = x7; } - //! Sets all four 64-bit signed integers. - inline void setI64(int64_t x0) noexcept { + //! Set all four 64-bit signed integers. + ASMJIT_INLINE void setI64(int64_t x0) noexcept { sq[0] = x0; sq[1] = x0; sq[2] = x0; sq[3] = x0; } - //! Sets all four 64-bit unsigned integers. - inline void setU64(uint64_t x0) noexcept { + //! Set all four 64-bit unsigned integers. + ASMJIT_INLINE void setU64(uint64_t x0) noexcept { uq[0] = x0; uq[1] = x0; uq[2] = x0; uq[3] = x0; } - //! Sets all four 64-bit signed integers. - inline void setI64(int64_t x0, int64_t x1, int64_t x2, int64_t x3) noexcept { + //! Set all four 64-bit signed integers. + ASMJIT_INLINE void setI64(int64_t x0, int64_t x1, int64_t x2, int64_t x3) noexcept { sq[0] = x0; sq[1] = x1; sq[2] = x2; sq[3] = x3; } - //! Sets all four 64-bit unsigned integers. - inline void setU64(uint64_t x0, uint64_t x1, uint64_t x2, uint64_t x3) noexcept { + //! Set all four 64-bit unsigned integers. + ASMJIT_INLINE void setU64(uint64_t x0, uint64_t x1, uint64_t x2, uint64_t x3) noexcept { uq[0] = x0; uq[1] = x1; uq[2] = x2; uq[3] = x3; } - //! Sets all eight SP-FP floats. - inline void setF32(float x0) noexcept { + //! Set all eight SP-FP floats. + ASMJIT_INLINE void setF32(float x0) noexcept { sf[0] = x0; sf[1] = x0; sf[2] = x0; sf[3] = x0; sf[4] = x0; sf[5] = x0; sf[6] = x0; sf[7] = x0; } - //! Sets all eight SP-FP floats. - inline void setF32( + //! Set all eight SP-FP floats. + ASMJIT_INLINE void setF32( float x0, float x1, float x2, float x3, float x4, float x5, float x6, float x7) noexcept { @@ -1036,21 +1027,49 @@ union Data256 { sf[4] = x4; sf[5] = x5; sf[6] = x6; sf[7] = x7; } - //! Sets all four DP-FP floats. - inline void setF64(double x0) noexcept { + //! Set all four DP-FP floats. + ASMJIT_INLINE void setF64(double x0) noexcept { df[0] = x0; df[1] = x0; df[2] = x0; df[3] = x0; } - //! Sets all four DP-FP floats. - inline void setF64(double x0, double x1, double x2, double x3) noexcept { + //! Set all four DP-FP floats. + ASMJIT_INLINE void setF64(double x0, double x1, double x2, double x3) noexcept { df[0] = x0; df[1] = x1; df[2] = x2; df[3] = x3; } - //! \} + // -------------------------------------------------------------------------- + // [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_END_NAMESPACE +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" -#endif // _ASMJIT_CORE_DATATYPES_H +// [Guard] +#endif // _ASMJIT_BASE_SIMDTYPES_H diff --git a/libraries/asmjit/asmjit/base/string.cpp b/libraries/asmjit/asmjit/base/string.cpp new file mode 100644 index 00000000000..4d0a837c169 --- /dev/null +++ b/libraries/asmjit/asmjit/base/string.cpp @@ -0,0 +1,353 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Export] +#define ASMJIT_EXPORTS + +// [Dependencies] +#include "../base/string.h" +#include "../base/utils.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +// ============================================================================ +// [asmjit::StringBuilder - Construction / Destruction] +// ============================================================================ + +// Should be placed in read-only memory. +static const char StringBuilder_empty[4] = { 0 }; + +StringBuilder::StringBuilder() noexcept + : _data(const_cast(StringBuilder_empty)), + _length(0), + _capacity(0), + _canFree(false) {} + +StringBuilder::~StringBuilder() noexcept { + if (_canFree) + Internal::releaseMemory(_data); +} + +// ============================================================================ +// [asmjit::StringBuilder - Prepare / Reserve] +// ============================================================================ + +ASMJIT_FAVOR_SIZE char* StringBuilder::prepare(uint32_t op, size_t len) noexcept { + 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 >= IntTraits::maxValue() - sizeof(intptr_t) * 2) + return nullptr; + + size_t to = Utils::alignTo(len, sizeof(intptr_t)); + if (to < 256 - sizeof(intptr_t)) + to = 256 - sizeof(intptr_t); + + char* newData = static_cast(Internal::allocMemory(to + sizeof(intptr_t))); + if (!newData) { + clear(); + return nullptr; + } + + if (_canFree) + Internal::releaseMemory(_data); + + _data = newData; + _capacity = to + sizeof(intptr_t) - 1; + _canFree = true; + } + + _data[len] = 0; + _length = len; + + ASMJIT_ASSERT(_length <= _capacity); + return _data; + } + else { + // We don't care here, but we can't return a null pointer since it indicates + // failure of memory allocation. + if (len == 0) + return _data + _length; + + // Overflow. + if (IntTraits::maxValue() - sizeof(intptr_t) * 2 - _length < len) + return nullptr; + + 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 < (IntTraits::maxValue() - 1024 * 32)) + to = Utils::alignTo(to, 1024 * 32); + } + + to = Utils::alignTo(to, sizeof(intptr_t)); + char* newData = static_cast(Internal::allocMemory(to + sizeof(intptr_t))); + if (!newData) return nullptr; + + ::memcpy(newData, _data, _length); + if (_canFree) + Internal::releaseMemory(_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; + } +} + +ASMJIT_FAVOR_SIZE Error StringBuilder::reserve(size_t to) noexcept { + if (_capacity >= to) + return kErrorOk; + + if (to >= IntTraits::maxValue() - sizeof(intptr_t) * 2) + return DebugUtils::errored(kErrorNoHeapMemory); + + to = Utils::alignTo(to, sizeof(intptr_t)); + char* newData = static_cast(Internal::allocMemory(to + sizeof(intptr_t))); + + if (!newData) + return DebugUtils::errored(kErrorNoHeapMemory); + + ::memcpy(newData, _data, _length + 1); + if (_canFree) + Internal::releaseMemory(_data); + + _data = newData; + _capacity = to + sizeof(intptr_t) - 1; + _canFree = true; + return kErrorOk; +} + +// ============================================================================ +// [asmjit::StringBuilder - Clear] +// ============================================================================ + +void StringBuilder::clear() noexcept { + if (_data != StringBuilder_empty) + _data[0] = 0; + _length = 0; +} + +// ============================================================================ +// [asmjit::StringBuilder - Methods] +// ============================================================================ + +Error StringBuilder::_opString(uint32_t op, const char* str, size_t len) noexcept { + if (len == Globals::kInvalidIndex) + len = str ? ::strlen(str) : static_cast(0); + + char* p = prepare(op, len); + if (!p) return DebugUtils::errored(kErrorNoHeapMemory); + + ::memcpy(p, str, len); + return kErrorOk; +} + +Error StringBuilder::_opChar(uint32_t op, char c) noexcept { + char* p = prepare(op, 1); + if (!p) return DebugUtils::errored(kErrorNoHeapMemory); + + *p = c; + return kErrorOk; +} + +Error StringBuilder::_opChars(uint32_t op, char c, size_t n) noexcept { + char* p = prepare(op, n); + if (!p) return DebugUtils::errored(kErrorNoHeapMemory); + + ::memset(p, c, n); + return kErrorOk; +} + +static const char StringBuilder_numbers[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + +Error StringBuilder::_opNumber(uint32_t op, uint64_t i, uint32_t base, size_t width, uint32_t flags) noexcept { + 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(i) < 0) { + i = static_cast(-static_cast(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) + return DebugUtils::errored(kErrorNoHeapMemory); + + ::memcpy(data, p, prefixLength); + data += prefixLength; + + ::memset(data, '0', width); + data += width; + + ::memcpy(data, p + prefixLength, numberLength); + return kErrorOk; +} + +Error StringBuilder::_opHex(uint32_t op, const void* data, size_t len) noexcept { + char* dst; + + if (len >= IntTraits::maxValue() / 2 || !(dst = prepare(op, len * 2))) + return DebugUtils::errored(kErrorNoHeapMemory);; + + const char* src = static_cast(data); + for (size_t i = 0; i < len; i++, dst += 2, src++) { + dst[0] = StringBuilder_numbers[(src[0] >> 4) & 0xF]; + dst[1] = StringBuilder_numbers[(src[0] ) & 0xF]; + } + return kErrorOk; +} + +Error StringBuilder::_opVFormat(uint32_t op, const char* fmt, va_list ap) noexcept { + char buf[1024]; + + vsnprintf(buf, ASMJIT_ARRAY_SIZE(buf), fmt, ap); + buf[ASMJIT_ARRAY_SIZE(buf) - 1] = '\0'; + + return _opString(op, buf); +} + +Error StringBuilder::setFormat(const char* fmt, ...) noexcept { + bool result; + + va_list ap; + va_start(ap, fmt); + result = _opVFormat(kStringOpSet, fmt, ap); + va_end(ap); + + return result; +} + +Error StringBuilder::appendFormat(const char* fmt, ...) noexcept { + 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 noexcept { + const char* aData = _data; + const char* bData = str; + + size_t aLength = _length; + size_t bLength = len; + + if (bLength == Globals::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 "../asmjit_apiend.h" diff --git a/libraries/asmjit/asmjit/base/string.h b/libraries/asmjit/asmjit/base/string.h new file mode 100644 index 00000000000..8d1ef16639f --- /dev/null +++ b/libraries/asmjit/asmjit/base/string.h @@ -0,0 +1,289 @@ +// [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] +#include "../base/globals.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +//! \addtogroup asmjit_base +//! \{ + +// ============================================================================ +// [asmjit::SmallString] +// ============================================================================ + +//! Small string is a template that helps to create strings that can be either +//! statically allocated if they are small, or externally allocated in case +//! their length exceed the limit. The `WholeSize` represents the size of the +//! whole `SmallString` structure, based on that size the maximum size of the +//! internal buffer is determined. +template +class SmallString { +public: + enum { kMaxEmbeddedLength = WholeSize - 5 }; + + ASMJIT_INLINE SmallString() noexcept { reset(); } + ASMJIT_INLINE void reset() noexcept { ::memset(this, 0, sizeof(*this)); } + + ASMJIT_INLINE bool isEmpty() const noexcept { return _length == 0; } + ASMJIT_INLINE bool isEmbedded() const noexcept { return _length <= kMaxEmbeddedLength; } + ASMJIT_INLINE bool mustEmbed(size_t len) const noexcept { return len <= kMaxEmbeddedLength; } + + ASMJIT_INLINE uint32_t getLength() const noexcept { return _length; } + ASMJIT_INLINE char* getData() const noexcept { + return _length <= kMaxEmbeddedLength ? const_cast(_embedded) : _external[1]; + } + + ASMJIT_INLINE void setEmbedded(const char* data, size_t len) noexcept { + ASMJIT_ASSERT(len <= kMaxEmbeddedLength); + + _length = static_cast(len); + ::memcpy(_embedded, data, len); + _embedded[len] = '\0'; + } + + ASMJIT_INLINE void setExternal(const char* data, size_t len) noexcept { + ASMJIT_ASSERT(len > kMaxEmbeddedLength); + ASMJIT_ASSERT(len <= ~static_cast(0)); + + _length = static_cast(len); + _external[1] = const_cast(data); + } + + union { + struct { + uint32_t _length; + char _embedded[WholeSize - 4]; + }; + char* _external[2]; + }; +}; + +// ============================================================================ +// [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. +class StringBuilder { +public: + ASMJIT_NONCOPYABLE(StringBuilder) + + //! \internal + //! + //! String operation. + ASMJIT_ENUM(OpType) { + kStringOpSet = 0, //!< Replace the current string by a given content. + kStringOpAppend = 1 //!< Append a given content to the current string. + }; + + //! \internal + //! + //! String format flags. + ASMJIT_ENUM(StringFormatFlags) { + kStringFormatShowSign = 0x00000001, + kStringFormatShowSpace = 0x00000002, + kStringFormatAlternate = 0x00000004, + kStringFormatSigned = 0x80000000 + }; + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + ASMJIT_API StringBuilder() noexcept; + ASMJIT_API ~StringBuilder() noexcept; + + ASMJIT_INLINE StringBuilder(const _NoInit&) noexcept {} + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get string builder capacity. + ASMJIT_INLINE size_t getCapacity() const noexcept { return _capacity; } + //! Get length. + ASMJIT_INLINE size_t getLength() const noexcept { return _length; } + + //! Get null-terminated string data. + ASMJIT_INLINE char* getData() noexcept { return _data; } + //! Get null-terminated string data (const). + ASMJIT_INLINE const char* getData() const noexcept { return _data; } + + // -------------------------------------------------------------------------- + // [Prepare / Reserve] + // -------------------------------------------------------------------------- + + //! Prepare to set/append. + ASMJIT_API char* prepare(uint32_t op, size_t len) noexcept; + + //! Reserve `to` bytes in string builder. + ASMJIT_API Error reserve(size_t to) noexcept; + + // -------------------------------------------------------------------------- + // [Clear] + // -------------------------------------------------------------------------- + + //! Clear the content in String builder. + ASMJIT_API void clear() noexcept; + + // -------------------------------------------------------------------------- + // [Op] + // -------------------------------------------------------------------------- + + ASMJIT_API Error _opString(uint32_t op, const char* str, size_t len = Globals::kInvalidIndex) noexcept; + ASMJIT_API Error _opVFormat(uint32_t op, const char* fmt, va_list ap) noexcept; + ASMJIT_API Error _opChar(uint32_t op, char c) noexcept; + ASMJIT_API Error _opChars(uint32_t op, char c, size_t n) noexcept; + ASMJIT_API Error _opNumber(uint32_t op, uint64_t i, uint32_t base = 0, size_t width = 0, uint32_t flags = 0) noexcept; + ASMJIT_API Error _opHex(uint32_t op, const void* data, size_t len) noexcept; + + // -------------------------------------------------------------------------- + // [Set] + // -------------------------------------------------------------------------- + + //! Replace the current string with `str` having `len` characters (or `kInvalidIndex` if it's null terminated). + ASMJIT_INLINE Error setString(const char* str, size_t len = Globals::kInvalidIndex) noexcept { return _opString(kStringOpSet, str, len); } + //! Replace the current content by a formatted string `fmt`. + ASMJIT_API Error setFormat(const char* fmt, ...) noexcept; + //! Replace the current content by a formatted string `fmt` (va_list version). + ASMJIT_INLINE Error setFormatVA(const char* fmt, va_list ap) noexcept { return _opVFormat(kStringOpSet, fmt, ap); } + + //! Replace the current content by a single `c` character. + ASMJIT_INLINE Error setChar(char c) noexcept { return _opChar(kStringOpSet, c); } + //! Replace the current content by `c` character `n` times. + ASMJIT_INLINE Error setChars(char c, size_t n) noexcept { return _opChars(kStringOpSet, c, n); } + + //! Replace the current content by a formatted integer `i` (signed). + ASMJIT_INLINE Error setInt(uint64_t i, uint32_t base = 0, size_t width = 0, uint32_t flags = 0) noexcept { + return _opNumber(kStringOpSet, i, base, width, flags | kStringFormatSigned); + } + + //! Replace the current content by a formatted integer `i` (unsigned). + ASMJIT_INLINE Error setUInt(uint64_t i, uint32_t base = 0, size_t width = 0, uint32_t flags = 0) noexcept { + return _opNumber(kStringOpSet, i, base, width, flags); + } + + //! Replace the current content by the given `data` converted to a HEX string. + ASMJIT_INLINE Error setHex(const void* data, size_t len) noexcept { + return _opHex(kStringOpSet, data, len); + } + + // -------------------------------------------------------------------------- + // [Append] + // -------------------------------------------------------------------------- + + //! Append string `str` having `len` characters (or `kInvalidIndex` if it's null terminated). + ASMJIT_INLINE Error appendString(const char* str, size_t len = Globals::kInvalidIndex) noexcept { return _opString(kStringOpAppend, str, len); } + //! Append a formatted string `fmt`. + ASMJIT_API Error appendFormat(const char* fmt, ...) noexcept; + //! Append a formatted string `fmt` (va_list version). + ASMJIT_INLINE Error appendFormatVA(const char* fmt, va_list ap) noexcept { return _opVFormat(kStringOpAppend, fmt, ap); } + + //! Append a single `c` character. + ASMJIT_INLINE Error appendChar(char c) noexcept { return _opChar(kStringOpAppend, c); } + //! Append `c` character `n` times. + ASMJIT_INLINE Error appendChars(char c, size_t n) noexcept { return _opChars(kStringOpAppend, c, n); } + + //! Append `i`. + ASMJIT_INLINE Error appendInt(int64_t i, uint32_t base = 0, size_t width = 0, uint32_t flags = 0) noexcept { + return _opNumber(kStringOpAppend, static_cast(i), base, width, flags | kStringFormatSigned); + } + + //! Append `i`. + ASMJIT_INLINE Error appendUInt(uint64_t i, uint32_t base = 0, size_t width = 0, uint32_t flags = 0) noexcept { + return _opNumber(kStringOpAppend, i, base, width, flags); + } + + //! Append the given `data` converted to a HEX string. + ASMJIT_INLINE Error appendHex(const void* data, size_t len) noexcept { + return _opHex(kStringOpAppend, data, len); + } + + // -------------------------------------------------------------------------- + // [Eq] + // -------------------------------------------------------------------------- + + //! Check for equality with other `str` of length `len`. + ASMJIT_API bool eq(const char* str, size_t len = Globals::kInvalidIndex) const noexcept; + //! Check for equality with `other`. + ASMJIT_INLINE bool eq(const StringBuilder& other) const noexcept { return eq(other._data, other._length); } + + // -------------------------------------------------------------------------- + // [Operator Overload] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE bool operator==(const StringBuilder& other) const noexcept { return eq(other); } + ASMJIT_INLINE bool operator!=(const StringBuilder& other) const noexcept { return !eq(other); } + + ASMJIT_INLINE bool operator==(const char* str) const noexcept { return eq(str); } + ASMJIT_INLINE bool operator!=(const char* str) const noexcept { return !eq(str); } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + char* _data; //!< String data. + size_t _length; //!< String length. + size_t _capacity; //!< String capacity. + size_t _canFree; //!< If the string data can be freed. +}; + +// ============================================================================ +// [asmjit::StringBuilderTmp] +// ============================================================================ + +//! Temporary string builder, has statically allocated `N` bytes. +template +class StringBuilderTmp : public StringBuilder { +public: + ASMJIT_NONCOPYABLE(StringBuilderTmp) + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE StringBuilderTmp() noexcept : StringBuilder(NoInit) { + _data = _embeddedData; + _data[0] = 0; + + _length = 0; + _capacity = N; + _canFree = false; + } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + //! Embedded data. + char _embeddedData[static_cast( + N + 1 + sizeof(intptr_t)) & ~static_cast(sizeof(intptr_t) - 1)]; +}; + +//! \} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" + +// [Guard] +#endif // _ASMJIT_BASE_STRING_H diff --git a/libraries/asmjit/asmjit/base/utils.cpp b/libraries/asmjit/asmjit/base/utils.cpp new file mode 100644 index 00000000000..91e01700fe5 --- /dev/null +++ b/libraries/asmjit/asmjit/base/utils.cpp @@ -0,0 +1,176 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Export] +#define ASMJIT_EXPORTS + +// [Dependencies] +#include "../base/utils.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +// ============================================================================ +// [asmjit::Utils - Unit] +// ============================================================================ + +#if defined(ASMJIT_TEST) +UNIT(base_utils) { + uint32_t i; + + INFO("IntTraits<>"); + EXPECT(IntTraits::kIsSigned,"IntTraits should report signed"); + EXPECT(IntTraits::kIsSigned, "IntTraits should report signed"); + EXPECT(IntTraits::kIsSigned, "IntTraits should report signed"); + EXPECT(IntTraits::kIsSigned, "IntTraits should report signed"); + + EXPECT(IntTraits::kIsUnsigned, "IntTraits should report unsigned"); + EXPECT(IntTraits::kIsUnsigned, "IntTraits should report unsigned"); + EXPECT(IntTraits::kIsUnsigned, "IntTraits should report unsigned"); + EXPECT(IntTraits::kIsUnsigned, "IntTraits should report unsigned"); + + EXPECT(IntTraits::kIsSigned, "IntTraits should report signed"); + EXPECT(IntTraits::kIsUnsigned, "IntTraits should report unsigned"); + + EXPECT(IntTraits::kIsIntPtr, "IntTraits should report intptr_t type"); + EXPECT(IntTraits::kIsIntPtr, "IntTraits should report intptr_t type"); + + INFO("Utils::inInterval()"); + EXPECT(Utils::inInterval(11 , 10, 20) == true , "Utils::inInterval should return true if inside"); + EXPECT(Utils::inInterval(101, 10, 20) == false, "Utils::inInterval should return false if outside"); + + INFO("Utils::isInt8()"); + EXPECT(Utils::isInt8(-128) == true , "Utils::isInt8<> should return true if inside"); + EXPECT(Utils::isInt8( 127) == true , "Utils::isInt8<> should return true if inside"); + EXPECT(Utils::isInt8(-129) == false, "Utils::isInt8<> should return false if outside"); + EXPECT(Utils::isInt8( 128) == false, "Utils::isInt8<> should return false if outside"); + + INFO("Utils::isInt16()"); + EXPECT(Utils::isInt16(-32768) == true , "Utils::isInt16<> should return true if inside"); + EXPECT(Utils::isInt16( 32767) == true , "Utils::isInt16<> should return true if inside"); + EXPECT(Utils::isInt16(-32769) == false, "Utils::isInt16<> should return false if outside"); + EXPECT(Utils::isInt16( 32768) == false, "Utils::isInt16<> should return false if outside"); + + INFO("Utils::isInt32()"); + EXPECT(Utils::isInt32( 2147483647 ) == true, "Utils::isInt32 should return true if inside"); + EXPECT(Utils::isInt32(-2147483647 - 1) == true, "Utils::isInt32 should return true if inside"); + EXPECT(Utils::isInt32(ASMJIT_UINT64_C(2147483648)) == false, "Utils::isInt32 should return false if outside"); + EXPECT(Utils::isInt32(ASMJIT_UINT64_C(0xFFFFFFFF)) == false, "Utils::isInt32 should return false if outside"); + EXPECT(Utils::isInt32(ASMJIT_UINT64_C(0xFFFFFFFF) + 1) == false, "Utils::isInt32 should return false if outside"); + + INFO("Utils::isUInt8()"); + EXPECT(Utils::isUInt8(0) == true , "Utils::isUInt8<> should return true if inside"); + EXPECT(Utils::isUInt8(255) == true , "Utils::isUInt8<> should return true if inside"); + EXPECT(Utils::isUInt8(256) == false, "Utils::isUInt8<> should return false if outside"); + EXPECT(Utils::isUInt8(-1) == false, "Utils::isUInt8<> should return false if negative"); + + INFO("Utils::isUInt12()"); + EXPECT(Utils::isUInt12(0) == true , "Utils::isUInt12<> should return true if inside"); + EXPECT(Utils::isUInt12(4095) == true , "Utils::isUInt12<> should return true if inside"); + EXPECT(Utils::isUInt12(4096) == false, "Utils::isUInt12<> should return false if outside"); + EXPECT(Utils::isUInt12(-1) == false, "Utils::isUInt12<> should return false if negative"); + + INFO("Utils::isUInt16()"); + EXPECT(Utils::isUInt16(0) == true , "Utils::isUInt16<> should return true if inside"); + EXPECT(Utils::isUInt16(65535) == true , "Utils::isUInt16<> should return true if inside"); + EXPECT(Utils::isUInt16(65536) == false, "Utils::isUInt16<> should return false if outside"); + EXPECT(Utils::isUInt16(-1) == false, "Utils::isUInt16<> should return false if negative"); + + INFO("Utils::isUInt32()"); + EXPECT(Utils::isUInt32(ASMJIT_UINT64_C(0xFFFFFFFF)) == true, "Utils::isUInt32 should return true if inside"); + EXPECT(Utils::isUInt32(ASMJIT_UINT64_C(0xFFFFFFFF) + 1) == false, "Utils::isUInt32 should return false if outside"); + EXPECT(Utils::isUInt32(-1) == false, "Utils::isUInt32 should return false if negative"); + + INFO("Utils::isPower2()"); + for (i = 0; i < 64; i++) { + EXPECT(Utils::isPowerOf2(static_cast(1) << i) == true, + "Utils::isPower2() didn't report power of 2"); + EXPECT(Utils::isPowerOf2((static_cast(1) << i) ^ 0x001101) == false, + "Utils::isPower2() didn't report not power of 2"); + } + + INFO("Utils::mask()"); + for (i = 0; i < 32; i++) { + EXPECT(Utils::mask(i) == (1 << i), + "Utils::mask(%u) should return %X", i, (1 << i)); + } + + INFO("Utils::bits()"); + for (i = 0; i < 32; i++) { + uint32_t expectedBits = 0; + + for (uint32_t b = 0; b < i; b++) + expectedBits |= static_cast(1) << b; + + EXPECT(Utils::bits(i) == expectedBits, + "Utils::bits(%u) should return %X", i, expectedBits); + } + + INFO("Utils::hasBit()"); + for (i = 0; i < 32; i++) { + EXPECT(Utils::hasBit((1 << i), i) == true, + "Utils::hasBit(%X, %u) should return true", (1 << i), i); + } + + INFO("Utils::bitCount()"); + for (i = 0; i < 32; i++) { + EXPECT(Utils::bitCount((1 << i)) == 1, + "Utils::bitCount(%X) should return true", (1 << i)); + } + EXPECT(Utils::bitCount(0x000000F0) == 4, ""); + EXPECT(Utils::bitCount(0x10101010) == 4, ""); + EXPECT(Utils::bitCount(0xFF000000) == 8, ""); + EXPECT(Utils::bitCount(0xFFFFFFF7) == 31, ""); + EXPECT(Utils::bitCount(0x7FFFFFFF) == 31, ""); + + INFO("Utils::findFirstBit()"); + for (i = 0; i < 32; i++) { + EXPECT(Utils::findFirstBit((1 << i)) == i, + "Utils::findFirstBit(%X) should return %u", (1 << i), i); + } + + INFO("Utils::keepNOnesFromRight()"); + EXPECT(Utils::keepNOnesFromRight(0xF, 1) == 0x1, ""); + EXPECT(Utils::keepNOnesFromRight(0xF, 2) == 0x3, ""); + EXPECT(Utils::keepNOnesFromRight(0xF, 3) == 0x7, ""); + EXPECT(Utils::keepNOnesFromRight(0x5, 2) == 0x5, ""); + EXPECT(Utils::keepNOnesFromRight(0xD, 2) == 0x5, ""); + + INFO("Utils::isAligned()"); + EXPECT(Utils::isAligned(0xFFFF, 4) == false, ""); + EXPECT(Utils::isAligned(0xFFF4, 4) == true , ""); + EXPECT(Utils::isAligned(0xFFF8, 8) == true , ""); + EXPECT(Utils::isAligned(0xFFF0, 16) == true , ""); + + INFO("Utils::alignTo()"); + EXPECT(Utils::alignTo(0xFFFF, 4) == 0x10000, ""); + EXPECT(Utils::alignTo(0xFFF4, 4) == 0x0FFF4, ""); + EXPECT(Utils::alignTo(0xFFF8, 8) == 0x0FFF8, ""); + EXPECT(Utils::alignTo(0xFFF0, 16) == 0x0FFF0, ""); + EXPECT(Utils::alignTo(0xFFF0, 32) == 0x10000, ""); + + INFO("Utils::alignToPowerOf2()"); + EXPECT(Utils::alignToPowerOf2(0xFFFF) == 0x10000, ""); + EXPECT(Utils::alignToPowerOf2(0xF123) == 0x10000, ""); + EXPECT(Utils::alignToPowerOf2(0x0F00) == 0x01000, ""); + EXPECT(Utils::alignToPowerOf2(0x0100) == 0x00100, ""); + EXPECT(Utils::alignToPowerOf2(0x1001) == 0x02000, ""); + + INFO("Utils::alignDiff()"); + EXPECT(Utils::alignDiff(0xFFFF, 4) == 1, ""); + EXPECT(Utils::alignDiff(0xFFF4, 4) == 0, ""); + EXPECT(Utils::alignDiff(0xFFF8, 8) == 0, ""); + EXPECT(Utils::alignDiff(0xFFF0, 16) == 0, ""); + EXPECT(Utils::alignDiff(0xFFF0, 32) == 16, ""); +} +#endif // ASMJIT_TEST + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" diff --git a/libraries/asmjit/asmjit/base/utils.h b/libraries/asmjit/asmjit/base/utils.h new file mode 100644 index 00000000000..b926ceaebf1 --- /dev/null +++ b/libraries/asmjit/asmjit/base/utils.h @@ -0,0 +1,1358 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Guard] +#ifndef _ASMJIT_BASE_UTILS_H +#define _ASMJIT_BASE_UTILS_H + +// [Dependencies] +#include "../base/globals.h" + +#if ASMJIT_CC_MSC_GE(14, 0, 0) +# include +#endif // ASMJIT_OS_WINDOWS + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +//! \addtogroup asmjit_base +//! \{ + +// ============================================================================ +// [asmjit::IntTraits] +// ============================================================================ + +//! \internal +//! \{ +template +struct IntTraitsPrivate {}; // Let it fail if not specialized! + +template<> struct IntTraitsPrivate<1, 0> { typedef int IntType; typedef int8_t SignedType; typedef uint8_t UnsignedType; }; +template<> struct IntTraitsPrivate<1, 1> { typedef int IntType; typedef int8_t SignedType; typedef uint8_t UnsignedType; }; + +template<> struct IntTraitsPrivate<2, 0> { typedef int IntType; typedef int16_t SignedType; typedef uint16_t UnsignedType; }; +template<> struct IntTraitsPrivate<2, 1> { typedef int IntType; typedef int16_t SignedType; typedef uint16_t UnsignedType; }; + +template<> struct IntTraitsPrivate<4, 0> { typedef int64_t IntType; typedef int32_t SignedType; typedef uint32_t UnsignedType; }; +template<> struct IntTraitsPrivate<4, 1> { typedef int IntType; typedef int32_t SignedType; typedef uint32_t UnsignedType; }; + +template<> struct IntTraitsPrivate<8, 0> { typedef int64_t IntType; typedef int64_t SignedType; typedef uint64_t UnsignedType; }; +template<> struct IntTraitsPrivate<8, 1> { typedef int64_t IntType; typedef int64_t SignedType; typedef uint64_t UnsignedType; }; + +//! \internal +template +struct IntTraits { + enum { + kIsSigned = static_cast(~static_cast(0)) < static_cast(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) + }; + + typedef typename IntTraitsPrivate::IntType IntType; + typedef typename IntTraitsPrivate::SignedType SignedType; + typedef typename IntTraitsPrivate::UnsignedType UnsignedType; + + //! Get a minimum value of `T`. + static ASMJIT_INLINE T minValue() noexcept { + return kIsSigned ? T((~static_cast(0) >> 1) + static_cast(1)) : T(0); + } + + //! Get a maximum value of `T`. + static ASMJIT_INLINE T maxValue() noexcept { + return kIsSigned ? T(~static_cast(0) >> 1) : ~T(0); + } +}; + +//! \} + +// ============================================================================ +// [asmjit::Utils] +// ============================================================================ + +//! AsmJit utilities - integer, string, etc... +namespace Utils { + // -------------------------------------------------------------------------- + // [Float <-> Int] + // -------------------------------------------------------------------------- + + //! \internal + union FloatBits { + int32_t i; + float f; + }; + + //! \internal + union DoubleBits { + int64_t i; + double d; + }; + + //! Bit-cast `float` to a 32-bit integer. + static ASMJIT_INLINE int32_t floatAsInt(float f) noexcept { FloatBits m; m.f = f; return m.i; } + //! Bit-cast 32-bit integer to `float`. + static ASMJIT_INLINE float intAsFloat(int32_t i) noexcept { FloatBits m; m.i = i; return m.f; } + + //! Bit-cast `double` to a 64-bit integer. + static ASMJIT_INLINE int64_t doubleAsInt(double d) noexcept { DoubleBits m; m.d = d; return m.i; } + //! Bit-cast 64-bit integer to `double`. + static ASMJIT_INLINE double intAsDouble(int64_t i) noexcept { DoubleBits m; m.i = i; return m.d; } + + // -------------------------------------------------------------------------- + // [Pack / Unpack] + // -------------------------------------------------------------------------- + + //! Pack four 8-bit integer into a 32-bit integer as it is an array of `{b0,b1,b2,b3}`. + static ASMJIT_INLINE uint32_t pack32_4x8(uint32_t b0, uint32_t b1, uint32_t b2, uint32_t b3) noexcept { + return ASMJIT_PACK32_4x8(b0, b1, b2, b3); + } + + //! 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) noexcept { + return ASMJIT_ARCH_LE ? (static_cast(u1) << 32) + u0 + : (static_cast(u0) << 32) + u1; + } + + // -------------------------------------------------------------------------- + // [Position of byte (in bit-shift)] + // -------------------------------------------------------------------------- + + static ASMJIT_INLINE uint32_t byteShiftOfDWordStruct(uint32_t index) noexcept { + if (ASMJIT_ARCH_LE) + return index * 8; + else + return (sizeof(uint32_t) - 1 - index) * 8; + } + + // -------------------------------------------------------------------------- + // [Lower/Upper] + // -------------------------------------------------------------------------- + + template + static ASMJIT_INLINE T toLower(T c) noexcept { return c ^ (static_cast(c >= T('A') && c <= T('Z')) << 5); } + template + static ASMJIT_INLINE T toUpper(T c) noexcept { return c ^ (static_cast(c >= T('a') && c <= T('z')) << 5); } + + // -------------------------------------------------------------------------- + // [Hash] + // -------------------------------------------------------------------------- + + // \internal + static ASMJIT_INLINE uint32_t hashRound(uint32_t hash, uint32_t c) noexcept { return hash * 65599 + c; } + + // Get a hash of the given string `str` of `len` length. Length must be valid + // as this function doesn't check for a null terminator and allows it in the + // middle of the string. + static ASMJIT_INLINE uint32_t hashString(const char* str, size_t len) noexcept { + uint32_t hVal = 0; + for (uint32_t i = 0; i < len; i++) + hVal = hashRound(hVal, str[i]); + return hVal; + } + + // -------------------------------------------------------------------------- + // [Swap] + // -------------------------------------------------------------------------- + + template + static ASMJIT_INLINE void swap(T& a, T& b) noexcept { + T tmp = a; + a = b; + b = tmp; + } + + // -------------------------------------------------------------------------- + // [InInterval] + // -------------------------------------------------------------------------- + + //! Get whether `x` is greater than or equal to `a` and lesses than or equal to `b`. + template + static ASMJIT_INLINE bool inInterval(T x, T a, T b) noexcept { + return x >= a && x <= b; + } + + // -------------------------------------------------------------------------- + // [AsInt] + // -------------------------------------------------------------------------- + + //! Map an integer `x` of type `T` to `int` or `int64_t` depending on the + //! type. Used internally by AsmJit to dispatch arguments that can be of + //! arbitrary integer type into a function argument that is either `int` or + //! `int64_t`. + template + static ASMJIT_INLINE typename IntTraits::IntType asInt(T x) noexcept { + return static_cast::IntType>(x); + } + + // -------------------------------------------------------------------------- + // [IsInt / IsUInt] + // -------------------------------------------------------------------------- + + //! Get whether the given integer `x` can be casted to a 4-bit signed integer. + template + static ASMJIT_INLINE bool isInt4(T x) noexcept { + typedef typename IntTraits::SignedType SignedType; + typedef typename IntTraits::UnsignedType UnsignedType; + + if (IntTraits::kIsSigned) + return inInterval(SignedType(x), -8, 7); + else + return UnsignedType(x) <= UnsignedType(7U); + } + + //! Get whether the given integer `x` can be casted to an 8-bit signed integer. + template + static ASMJIT_INLINE bool isInt8(T x) noexcept { + typedef typename IntTraits::SignedType SignedType; + typedef typename IntTraits::UnsignedType UnsignedType; + + if (IntTraits::kIsSigned) + return sizeof(T) <= 1 || inInterval(SignedType(x), -128, 127); + else + return UnsignedType(x) <= UnsignedType(127U); + } + + //! Get whether the given integer `x` can be casted to a 16-bit signed integer. + template + static ASMJIT_INLINE bool isInt16(T x) noexcept { + typedef typename IntTraits::SignedType SignedType; + typedef typename IntTraits::UnsignedType UnsignedType; + + if (IntTraits::kIsSigned) + return sizeof(T) <= 2 || inInterval(SignedType(x), -32768, 32767); + else + return sizeof(T) <= 1 || UnsignedType(x) <= UnsignedType(32767U); + } + + //! Get whether the given integer `x` can be casted to a 32-bit signed integer. + template + static ASMJIT_INLINE bool isInt32(T x) noexcept { + typedef typename IntTraits::SignedType SignedType; + typedef typename IntTraits::UnsignedType UnsignedType; + + if (IntTraits::kIsSigned) + return sizeof(T) <= 4 || inInterval(SignedType(x), -2147483647 - 1, 2147483647); + else + return sizeof(T) <= 2 || UnsignedType(x) <= UnsignedType(2147483647U); + } + + //! Get whether the given integer `x` can be casted to a 4-bit unsigned integer. + template + static ASMJIT_INLINE bool isUInt4(T x) noexcept { + typedef typename IntTraits::UnsignedType UnsignedType; + + if (IntTraits::kIsSigned) + return x >= T(0) && x <= T(15); + else + return UnsignedType(x) <= UnsignedType(15U); + } + + //! Get whether the given integer `x` can be casted to an 8-bit unsigned integer. + template + static ASMJIT_INLINE bool isUInt8(T x) noexcept { + typedef typename IntTraits::UnsignedType UnsignedType; + + if (IntTraits::kIsSigned) + return x >= T(0) && (sizeof(T) <= 1 ? true : x <= T(255)); + else + return sizeof(T) <= 1 || UnsignedType(x) <= UnsignedType(255U); + } + + //! Get whether the given integer `x` can be casted to a 12-bit unsigned integer (ARM specific). + template + static ASMJIT_INLINE bool isUInt12(T x) noexcept { + typedef typename IntTraits::UnsignedType UnsignedType; + + if (IntTraits::kIsSigned) + return x >= T(0) && (sizeof(T) <= 1 ? true : x <= T(4095)); + else + return sizeof(T) <= 1 || UnsignedType(x) <= UnsignedType(4095U); + } + + //! Get whether the given integer `x` can be casted to a 16-bit unsigned integer. + template + static ASMJIT_INLINE bool isUInt16(T x) noexcept { + typedef typename IntTraits::UnsignedType UnsignedType; + + if (IntTraits::kIsSigned) + return x >= T(0) && (sizeof(T) <= 2 ? true : x <= T(65535)); + else + return sizeof(T) <= 2 || UnsignedType(x) <= UnsignedType(65535U); + } + + //! Get whether the given integer `x` can be casted to a 32-bit unsigned integer. + template + static ASMJIT_INLINE bool isUInt32(T x) noexcept { + typedef typename IntTraits::UnsignedType UnsignedType; + + if (IntTraits::kIsSigned) + return x >= T(0) && (sizeof(T) <= 4 ? true : x <= T(4294967295U)); + else + return sizeof(T) <= 4 || UnsignedType(x) <= UnsignedType(4294967295U); + } + + // -------------------------------------------------------------------------- + // [IsPowerOf2] + // -------------------------------------------------------------------------- + + //! Get whether the `n` value is a power of two (only one bit is set). + template + static ASMJIT_INLINE bool isPowerOf2(T n) noexcept { + return n != 0 && (n & (n - 1)) == 0; + } + + // -------------------------------------------------------------------------- + // [Mask] + // -------------------------------------------------------------------------- + + //! Generate a bit-mask that has `x` bit set. + static ASMJIT_INLINE uint32_t mask(uint32_t x) noexcept { + ASMJIT_ASSERT(x < 32); + return static_cast(1) << x; + } + + //! Generate a bit-mask that has `x0` and `x1` bits set. + static ASMJIT_INLINE uint32_t mask(uint32_t x0, uint32_t x1) noexcept { + 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) noexcept { + return mask(x0, 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) noexcept { + return mask(x0, x1) | mask(x2, 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) noexcept { + return mask(x0, x1) | mask(x2, 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) noexcept { + return mask(x0, x1) | mask(x2, x3) | mask(x4, 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) noexcept { + return mask(x0, x1) | mask(x2, x3) | mask(x4, 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) noexcept { + return mask(x0, x1) | mask(x2, x3) | mask(x4, x5) | mask(x6, 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) noexcept { + return mask(x0, x1) | mask(x2, x3) | mask(x4, x5) | mask(x6, 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) noexcept { + return mask(x0, x1) | mask(x2, x3) | mask(x4, x5) | mask(x6, x7) | mask(x8, x9); + } + + // -------------------------------------------------------------------------- + // [Bits] + // -------------------------------------------------------------------------- + + //! Generate a bit-mask that has `x` least significant bits set. + static ASMJIT_INLINE uint32_t bits(uint32_t x) noexcept { + // Shifting more bits than the type has results in undefined behavior. In + // such case asmjit trashes the result by ORing with `overflow` mask, which + // discards the undefined value returned by the shift. + uint32_t overflow = static_cast( + -static_cast(x >= sizeof(uint32_t) * 8)); + + return ((static_cast(1) << x) - 1U) | overflow; + } + + // -------------------------------------------------------------------------- + // [HasBit] + // -------------------------------------------------------------------------- + + //! Get whether `x` has bit `n` set. + template + static ASMJIT_INLINE bool hasBit(T x, Index n) noexcept { + return (x & (static_cast(1) << n)) != 0; + } + + // -------------------------------------------------------------------------- + // [BitCount] + // -------------------------------------------------------------------------- + + static ASMJIT_INLINE uint32_t bitCountSlow(uint32_t x) noexcept { + // From: http://graphics.stanford.edu/~seander/bithacks.html + x = x - ((x >> 1) & 0x55555555U); + x = (x & 0x33333333U) + ((x >> 2) & 0x33333333U); + return (((x + (x >> 4)) & 0x0F0F0F0FU) * 0x01010101U) >> 24; + } + + //! Get count of bits in `x`. + static ASMJIT_INLINE uint32_t bitCount(uint32_t x) noexcept { +#if ASMJIT_CC_GCC || ASMJIT_CC_CLANG + return __builtin_popcount(x); +#else + return bitCountSlow(x); +#endif + } + + // -------------------------------------------------------------------------- + // [FirstBitOf] + // -------------------------------------------------------------------------- + + template + struct FirstBitOfTImpl { + enum { + _shift = (In & ASMJIT_UINT64_C(0x0000FFFFFFFFFFFF)) == 0 ? 48 : + (In & ASMJIT_UINT64_C(0x00000000FFFFFFFF)) == 0 ? 32 : + (In & ASMJIT_UINT64_C(0x000000000000FFFF)) == 0 ? 16 : 0, + + kValue = ((In >> _shift) & 0x0001) ? (_shift + 0) : + ((In >> _shift) & 0x0002) ? (_shift + 1) : + ((In >> _shift) & 0x0004) ? (_shift + 2) : + ((In >> _shift) & 0x0008) ? (_shift + 3) : + ((In >> _shift) & 0x0010) ? (_shift + 4) : + ((In >> _shift) & 0x0020) ? (_shift + 5) : + ((In >> _shift) & 0x0040) ? (_shift + 6) : + ((In >> _shift) & 0x0080) ? (_shift + 7) : + ((In >> _shift) & 0x0100) ? (_shift + 8) : + ((In >> _shift) & 0x0200) ? (_shift + 9) : + ((In >> _shift) & 0x0400) ? (_shift + 10) : + ((In >> _shift) & 0x0800) ? (_shift + 11) : + ((In >> _shift) & 0x1000) ? (_shift + 12) : + ((In >> _shift) & 0x2000) ? (_shift + 13) : + ((In >> _shift) & 0x4000) ? (_shift + 14) : + ((In >> _shift) & 0x8000) ? (_shift + 15) : 0 + }; + }; + + template<> + struct FirstBitOfTImpl<0> {}; + + template + static ASMJIT_INLINE uint32_t firstBitOfT() noexcept { return FirstBitOfTImpl::kValue; } + + // -------------------------------------------------------------------------- + // [FindFirstBit] + // -------------------------------------------------------------------------- + + //! \internal + static ASMJIT_INLINE uint32_t findFirstBitSlow(uint32_t mask) noexcept { + // This is a reference (slow) implementation of `findFirstBit()`, used when + // we don't have a C++ compiler support. 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) noexcept { +#if ASMJIT_CC_MSC_GE(14, 0, 0) && (ASMJIT_ARCH_X86 || ASMJIT_ARCH_ARM32 || \ + ASMJIT_ARCH_X64 || ASMJIT_ARCH_ARM64) + DWORD i; + if (_BitScanForward(&i, mask)) + return static_cast(i); + else + return 0xFFFFFFFFU; +#elif ASMJIT_CC_GCC_GE(3, 4, 6) || ASMJIT_CC_CLANG + if (mask) + return __builtin_ctz(mask); + else + return 0xFFFFFFFFU; +#else + return findFirstBitSlow(mask); +#endif + } + + // -------------------------------------------------------------------------- + // [Misc] + // -------------------------------------------------------------------------- + + static ASMJIT_INLINE uint32_t keepNOnesFromRight(uint32_t mask, uint32_t nBits) noexcept { + 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) noexcept { + 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; + } + + // -------------------------------------------------------------------------- + // [Alignment] + // -------------------------------------------------------------------------- + + template + static ASMJIT_INLINE bool isAligned(X base, Y alignment) noexcept { + typedef typename IntTraitsPrivate::UnsignedType U; + return ((U)base % (U)alignment) == 0; + } + + template + static ASMJIT_INLINE X alignTo(X x, Y alignment) noexcept { + typedef typename IntTraitsPrivate::UnsignedType U; + return (X)( ((U)x + (U)(alignment - 1)) & ~(static_cast(alignment) - 1) ); + } + + //! Get delta required to align `base` to `alignment`. + template + static ASMJIT_INLINE X alignDiff(X base, Y alignment) noexcept { + return alignTo(base, alignment) - base; + } + + template + static ASMJIT_INLINE T alignToPowerOf2(T base) noexcept { + // 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; + } + + // -------------------------------------------------------------------------- + // [String] + // -------------------------------------------------------------------------- + + static ASMJIT_INLINE size_t strLen(const char* s, size_t maxlen) noexcept { + size_t i; + for (i = 0; i < maxlen; i++) + if (!s[i]) + break; + return i; + } + + static ASMJIT_INLINE const char* findPackedString(const char* p, uint32_t id) noexcept { + uint32_t i = 0; + while (i < id) { + while (p[0]) + p++; + p++; + i++; + } + return p; + } + + //! \internal + //! + //! Compare two instruction names. + //! + //! `a` is a null terminated instruction name from `???InstDB::nameData[]` table. + //! `b` is a non-null terminated instruction name passed to `???Inst::getIdByName()`. + static ASMJIT_INLINE int cmpInstName(const char* a, const char* b, size_t len) noexcept { + for (size_t i = 0; i < len; i++) { + int c = static_cast(static_cast(a[i])) - + static_cast(static_cast(b[i])) ; + if (c != 0) return c; + } + + return static_cast(a[len]); + } + + // -------------------------------------------------------------------------- + // [BSwap] + // -------------------------------------------------------------------------- + + static ASMJIT_INLINE uint32_t byteswap32(uint32_t x) noexcept { +#if ASMJIT_CC_MSC + return static_cast(_byteswap_ulong(x)); +#elif ASMJIT_CC_GCC_GE(4, 3, 0) || ASMJIT_CC_CLANG_GE(2, 6, 0) + return __builtin_bswap32(x); +#else + uint32_t y = x & 0x00FFFF00U; + x = (x << 24) + (x >> 24); + y = (y << 8) + (y >> 8); + return x + (y & 0x00FFFF00U); +#endif + } + + // -------------------------------------------------------------------------- + // [ReadMem] + // -------------------------------------------------------------------------- + + static ASMJIT_INLINE uint32_t readU8(const void* p) noexcept { + return static_cast(static_cast(p)[0]); + } + + static ASMJIT_INLINE int32_t readI8(const void* p) noexcept { + return static_cast(static_cast(p)[0]); + } + + template + static ASMJIT_INLINE uint32_t readU16xLE(const void* p) noexcept { + ASMJIT_ASSUME_ALIGNED(p, Alignment > 1 ? Alignment : 1U); + if (ASMJIT_ARCH_LE && (ASMJIT_ARCH_UNALIGNED_16 || Alignment >= 2)) { + return static_cast(static_cast(p)[0]); + } + else { + uint32_t x = static_cast(static_cast(p)[0]); + uint32_t y = static_cast(static_cast(p)[1]); + return x + (y << 8); + } + } + + template + static ASMJIT_INLINE uint32_t readU16xBE(const void* p) noexcept { + ASMJIT_ASSUME_ALIGNED(p, Alignment > 1 ? Alignment : 1U); + if (ASMJIT_ARCH_BE && (ASMJIT_ARCH_UNALIGNED_16 || Alignment >= 2)) { + return static_cast(static_cast(p)[0]); + } + else { + uint32_t x = static_cast(static_cast(p)[0]); + uint32_t y = static_cast(static_cast(p)[1]); + return (x << 8) + y; + } + } + + template + static ASMJIT_INLINE uint32_t readU16x(const void* p) noexcept { + return ASMJIT_ARCH_LE ? readU16xLE(p) : readU16xBE(p); + } + + template + static ASMJIT_INLINE int32_t readI16xLE(const void* p) noexcept { + ASMJIT_ASSUME_ALIGNED(p, Alignment > 1 ? Alignment : 1U); + if (ASMJIT_ARCH_LE && (ASMJIT_ARCH_UNALIGNED_16 || Alignment >= 2)) { + return static_cast(static_cast(p)[0]); + } + else { + int32_t x = static_cast(static_cast(p)[0]); + int32_t y = static_cast(static_cast(p)[1]); + return x + (y << 8); + } + } + + template + static ASMJIT_INLINE int32_t readI16xBE(const void* p) noexcept { + ASMJIT_ASSUME_ALIGNED(p, Alignment > 1 ? Alignment : 1U); + if (ASMJIT_ARCH_BE && (ASMJIT_ARCH_UNALIGNED_16 || Alignment >= 2)) { + return static_cast(static_cast(p)[0]); + } + else { + int32_t x = static_cast(static_cast(p)[0]); + int32_t y = static_cast(static_cast(p)[1]); + return (x << 8) + y; + } + } + + template + static ASMJIT_INLINE int32_t readI16x(const void* p) noexcept { + return ASMJIT_ARCH_LE ? readI16xLE(p) : readI16xBE(p); + } + + static ASMJIT_INLINE uint32_t readU16aLE(const void* p) noexcept { return readU16xLE<2>(p); } + static ASMJIT_INLINE uint32_t readU16uLE(const void* p) noexcept { return readU16xLE<0>(p); } + + static ASMJIT_INLINE uint32_t readU16aBE(const void* p) noexcept { return readU16xBE<2>(p); } + static ASMJIT_INLINE uint32_t readU16uBE(const void* p) noexcept { return readU16xBE<0>(p); } + + static ASMJIT_INLINE uint32_t readU16a(const void* p) noexcept { return readU16x<2>(p); } + static ASMJIT_INLINE uint32_t readU16u(const void* p) noexcept { return readU16x<0>(p); } + + static ASMJIT_INLINE int32_t readI16aLE(const void* p) noexcept { return readI16xLE<2>(p); } + static ASMJIT_INLINE int32_t readI16uLE(const void* p) noexcept { return readI16xLE<0>(p); } + + static ASMJIT_INLINE int32_t readI16aBE(const void* p) noexcept { return readI16xBE<2>(p); } + static ASMJIT_INLINE int32_t readI16uBE(const void* p) noexcept { return readI16xBE<0>(p); } + + static ASMJIT_INLINE int32_t readI16a(const void* p) noexcept { return readI16x<2>(p); } + static ASMJIT_INLINE int32_t readI16u(const void* p) noexcept { return readI16x<0>(p); } + + template + static ASMJIT_INLINE uint32_t readU32xLE(const void* p) noexcept { + ASMJIT_ASSUME_ALIGNED(p, Alignment > 1 ? Alignment : 1U); + if (ASMJIT_ARCH_UNALIGNED_32 || Alignment >= 4) { + uint32_t x = static_cast(p)[0]; + return ASMJIT_ARCH_LE ? x : byteswap32(x); + } + else { + uint32_t x = readU16xLE(static_cast(p) + 0); + uint32_t y = readU16xLE(static_cast(p) + 2); + return x + (y << 16); + } + } + + template + static ASMJIT_INLINE uint32_t readU32xBE(const void* p) noexcept { + ASMJIT_ASSUME_ALIGNED(p, Alignment > 1 ? Alignment : 1U); + if (ASMJIT_ARCH_UNALIGNED_32 || Alignment >= 4) { + uint32_t x = static_cast(p)[0]; + return ASMJIT_ARCH_BE ? x : byteswap32(x); + } + else { + uint32_t x = readU16xBE(static_cast(p) + 0); + uint32_t y = readU16xBE(static_cast(p) + 2); + return (x << 16) + y; + } + } + + template + static ASMJIT_INLINE uint32_t readU32x(const void* p) noexcept { + return ASMJIT_ARCH_LE ? readU32xLE(p) : readU32xBE(p); + } + + template + static ASMJIT_INLINE int32_t readI32xLE(const void* p) noexcept { + return static_cast(readU32xLE(p)); + } + + template + static ASMJIT_INLINE int32_t readI32xBE(const void* p) noexcept { + return static_cast(readU32xBE(p)); + } + + template + static ASMJIT_INLINE int32_t readI32x(const void* p) noexcept { + return ASMJIT_ARCH_LE ? readI32xLE(p) : readI32xBE(p); + } + + static ASMJIT_INLINE uint32_t readU32a(const void* p) noexcept { return readU32x<4>(p); } + static ASMJIT_INLINE uint32_t readU32u(const void* p) noexcept { return readU32x<0>(p); } + + static ASMJIT_INLINE uint32_t readU32aLE(const void* p) noexcept { return readU32xLE<4>(p); } + static ASMJIT_INLINE uint32_t readU32uLE(const void* p) noexcept { return readU32xLE<0>(p); } + + static ASMJIT_INLINE uint32_t readU32aBE(const void* p) noexcept { return readU32xBE<4>(p); } + static ASMJIT_INLINE uint32_t readU32uBE(const void* p) noexcept { return readU32xBE<0>(p); } + + static ASMJIT_INLINE int32_t readI32a(const void* p) noexcept { return readI32x<4>(p); } + static ASMJIT_INLINE int32_t readI32u(const void* p) noexcept { return readI32x<0>(p); } + + static ASMJIT_INLINE int32_t readI32aLE(const void* p) noexcept { return readI32xLE<4>(p); } + static ASMJIT_INLINE int32_t readI32uLE(const void* p) noexcept { return readI32xLE<0>(p); } + + static ASMJIT_INLINE int32_t readI32aBE(const void* p) noexcept { return readI32xBE<4>(p); } + static ASMJIT_INLINE int32_t readI32uBE(const void* p) noexcept { return readI32xBE<0>(p); } + + template + static ASMJIT_INLINE uint64_t readU64xLE(const void* p) noexcept { + ASMJIT_ASSUME_ALIGNED(p, Alignment > 1 ? Alignment : 1U); + if (ASMJIT_ARCH_LE && (ASMJIT_ARCH_UNALIGNED_64 || Alignment >= 8)) { + return static_cast(p)[0]; + } + else { + uint32_t x = readU32xLE(static_cast(p) + 0); + uint32_t y = readU32xLE(static_cast(p) + 4); + return static_cast(x) + (static_cast(y) << 32); + } + } + + template + static ASMJIT_INLINE uint64_t readU64xBE(const void* p) noexcept { + ASMJIT_ASSUME_ALIGNED(p, Alignment > 1 ? Alignment : 1U); + if (ASMJIT_ARCH_BE && (ASMJIT_ARCH_UNALIGNED_64 || Alignment >= 8)) { + return static_cast(p)[0]; + } + else { + uint32_t x = readU32xLE(static_cast(p) + 0); + uint32_t y = readU32xLE(static_cast(p) + 4); + return (static_cast(x) << 32) + static_cast(y); + } + } + + template + static ASMJIT_INLINE uint64_t readU64x(const void* p) noexcept { + return ASMJIT_ARCH_LE ? readU64xLE(p) : readU64xBE(p); + } + + template + static ASMJIT_INLINE int64_t readI64xLE(const void* p) noexcept { + return static_cast(readU64xLE(p)); + } + + template + static ASMJIT_INLINE int64_t readI64xBE(const void* p) noexcept { + return static_cast(readU64xBE(p)); + } + + template + static ASMJIT_INLINE int64_t readI64x(const void* p) noexcept { + return ASMJIT_ARCH_LE ? readI64xLE(p) : readI64xBE(p); + } + + static ASMJIT_INLINE uint64_t readU64a(const void* p) noexcept { return readU64x<8>(p); } + static ASMJIT_INLINE uint64_t readU64u(const void* p) noexcept { return readU64x<0>(p); } + + static ASMJIT_INLINE uint64_t readU64aLE(const void* p) noexcept { return readU64xLE<8>(p); } + static ASMJIT_INLINE uint64_t readU64uLE(const void* p) noexcept { return readU64xLE<0>(p); } + + static ASMJIT_INLINE uint64_t readU64aBE(const void* p) noexcept { return readU64xBE<8>(p); } + static ASMJIT_INLINE uint64_t readU64uBE(const void* p) noexcept { return readU64xBE<0>(p); } + + static ASMJIT_INLINE int64_t readI64a(const void* p) noexcept { return readI64x<8>(p); } + static ASMJIT_INLINE int64_t readI64u(const void* p) noexcept { return readI64x<0>(p); } + + static ASMJIT_INLINE int64_t readI64aLE(const void* p) noexcept { return readI64xLE<8>(p); } + static ASMJIT_INLINE int64_t readI64uLE(const void* p) noexcept { return readI64xLE<0>(p); } + + static ASMJIT_INLINE int64_t readI64aBE(const void* p) noexcept { return readI64xBE<8>(p); } + static ASMJIT_INLINE int64_t readI64uBE(const void* p) noexcept { return readI64xBE<0>(p); } + + // -------------------------------------------------------------------------- + // [WriteMem] + // -------------------------------------------------------------------------- + + static ASMJIT_INLINE void writeU8(void* p, uint32_t x) noexcept { + static_cast(p)[0] = static_cast(x & 0xFFU); + } + + static ASMJIT_INLINE void writeI8(void* p, int32_t x) noexcept { + static_cast(p)[0] = static_cast(x & 0xFF); + } + + template + static ASMJIT_INLINE void writeU16xLE(void* p, uint32_t x) noexcept { + ASMJIT_ASSUME_ALIGNED(p, Alignment > 1 ? Alignment : 1U); + if (ASMJIT_ARCH_LE && (ASMJIT_ARCH_UNALIGNED_16 || Alignment >= 2)) { + static_cast(p)[0] = static_cast(x & 0xFFFFU); + } + else { + static_cast(p)[0] = static_cast((x ) & 0xFFU); + static_cast(p)[1] = static_cast((x >> 8) & 0xFFU); + } + } + + template + static ASMJIT_INLINE void writeU16xBE(void* p, uint32_t x) noexcept { + ASMJIT_ASSUME_ALIGNED(p, Alignment > 1 ? Alignment : 1U); + if (ASMJIT_ARCH_BE && (ASMJIT_ARCH_UNALIGNED_16 || Alignment >= 2)) { + static_cast(p)[0] = static_cast(x & 0xFFFFU); + } + else { + static_cast(p)[0] = static_cast((x >> 8) & 0xFFU); + static_cast(p)[1] = static_cast((x ) & 0xFFU); + } + } + + template + static ASMJIT_INLINE void writeU16x(void* p, uint32_t x) noexcept { + if (ASMJIT_ARCH_LE) + writeU16xLE(p, x); + else + writeU16xBE(p, x); + } + + template + static ASMJIT_INLINE void writeI16xLE(void* p, int32_t x) noexcept { + writeU16xLE(p, static_cast(x)); + } + + template + static ASMJIT_INLINE void writeI16xBE(void* p, int32_t x) noexcept { + writeU16xBE(p, static_cast(x)); + } + + template + static ASMJIT_INLINE void writeI16x(void* p, int32_t x) noexcept { + writeU16x(p, static_cast(x)); + } + + static ASMJIT_INLINE void writeU16aLE(void* p, uint32_t x) noexcept { writeU16xLE<2>(p, x); } + static ASMJIT_INLINE void writeU16uLE(void* p, uint32_t x) noexcept { writeU16xLE<0>(p, x); } + + static ASMJIT_INLINE void writeU16aBE(void* p, uint32_t x) noexcept { writeU16xBE<2>(p, x); } + static ASMJIT_INLINE void writeU16uBE(void* p, uint32_t x) noexcept { writeU16xBE<0>(p, x); } + + static ASMJIT_INLINE void writeU16a(void* p, uint32_t x) noexcept { writeU16x<2>(p, x); } + static ASMJIT_INLINE void writeU16u(void* p, uint32_t x) noexcept { writeU16x<0>(p, x); } + + static ASMJIT_INLINE void writeI16aLE(void* p, int32_t x) noexcept { writeI16xLE<2>(p, x); } + static ASMJIT_INLINE void writeI16uLE(void* p, int32_t x) noexcept { writeI16xLE<0>(p, x); } + + static ASMJIT_INLINE void writeI16aBE(void* p, int32_t x) noexcept { writeI16xBE<2>(p, x); } + static ASMJIT_INLINE void writeI16uBE(void* p, int32_t x) noexcept { writeI16xBE<0>(p, x); } + + static ASMJIT_INLINE void writeI16a(void* p, int32_t x) noexcept { writeI16x<2>(p, x); } + static ASMJIT_INLINE void writeI16u(void* p, int32_t x) noexcept { writeI16x<0>(p, x); } + + template + static ASMJIT_INLINE void writeU32xLE(void* p, uint32_t x) noexcept { + ASMJIT_ASSUME_ALIGNED(p, Alignment > 1 ? Alignment : 1U); + if (ASMJIT_ARCH_UNALIGNED_32 || Alignment >= 4) { + static_cast(p)[0] = ASMJIT_ARCH_LE ? x : byteswap32(x); + } + else { + writeU16xLE(static_cast(p) + 0, x >> 16); + writeU16xLE(static_cast(p) + 2, x); + } + } + + template + static ASMJIT_INLINE void writeU32xBE(void* p, uint32_t x) noexcept { + ASMJIT_ASSUME_ALIGNED(p, Alignment > 1 ? Alignment : 1U); + if (ASMJIT_ARCH_UNALIGNED_32 || Alignment >= 4) { + static_cast(p)[0] = ASMJIT_ARCH_BE ? x : byteswap32(x); + } + else { + writeU16xBE(static_cast(p) + 0, x); + writeU16xBE(static_cast(p) + 2, x >> 16); + } + } + + template + static ASMJIT_INLINE void writeU32x(void* p, uint32_t x) noexcept { + if (ASMJIT_ARCH_LE) + writeU32xLE(p, x); + else + writeU32xBE(p, x); + } + + template + static ASMJIT_INLINE void writeI32xLE(void* p, int32_t x) noexcept { + writeU32xLE(p, static_cast(x)); + } + + template + static ASMJIT_INLINE void writeI32xBE(void* p, int32_t x) noexcept { + writeU32xBE(p, static_cast(x)); + } + + template + static ASMJIT_INLINE void writeI32x(void* p, int32_t x) noexcept { + writeU32x(p, static_cast(x)); + } + + static ASMJIT_INLINE void writeU32aLE(void* p, uint32_t x) noexcept { writeU32xLE<4>(p, x); } + static ASMJIT_INLINE void writeU32uLE(void* p, uint32_t x) noexcept { writeU32xLE<0>(p, x); } + + static ASMJIT_INLINE void writeU32aBE(void* p, uint32_t x) noexcept { writeU32xBE<4>(p, x); } + static ASMJIT_INLINE void writeU32uBE(void* p, uint32_t x) noexcept { writeU32xBE<0>(p, x); } + + static ASMJIT_INLINE void writeU32a(void* p, uint32_t x) noexcept { writeU32x<4>(p, x); } + static ASMJIT_INLINE void writeU32u(void* p, uint32_t x) noexcept { writeU32x<0>(p, x); } + + static ASMJIT_INLINE void writeI32aLE(void* p, int32_t x) noexcept { writeI32xLE<4>(p, x); } + static ASMJIT_INLINE void writeI32uLE(void* p, int32_t x) noexcept { writeI32xLE<0>(p, x); } + + static ASMJIT_INLINE void writeI32aBE(void* p, int32_t x) noexcept { writeI32xBE<4>(p, x); } + static ASMJIT_INLINE void writeI32uBE(void* p, int32_t x) noexcept { writeI32xBE<0>(p, x); } + + static ASMJIT_INLINE void writeI32a(void* p, int32_t x) noexcept { writeI32x<4>(p, x); } + static ASMJIT_INLINE void writeI32u(void* p, int32_t x) noexcept { writeI32x<0>(p, x); } + + template + static ASMJIT_INLINE void writeU64xLE(void* p, uint64_t x) noexcept { + ASMJIT_ASSUME_ALIGNED(p, Alignment > 1 ? Alignment : 1U); + if (ASMJIT_ARCH_LE && (ASMJIT_ARCH_UNALIGNED_64 || Alignment >= 8)) { + static_cast(p)[0] = x; + } + else { + writeU32xLE(static_cast(p) + 0, static_cast(x >> 32)); + writeU32xLE(static_cast(p) + 4, static_cast(x & 0xFFFFFFFFU)); + } + } + + template + static ASMJIT_INLINE void writeU64xBE(void* p, uint64_t x) noexcept { + ASMJIT_ASSUME_ALIGNED(p, Alignment > 1 ? Alignment : 1U); + if (ASMJIT_ARCH_BE && (ASMJIT_ARCH_UNALIGNED_64 || Alignment >= 8)) { + static_cast(p)[0] = x; + } + else { + writeU32xBE(static_cast(p) + 0, static_cast(x & 0xFFFFFFFFU)); + writeU32xBE(static_cast(p) + 4, static_cast(x >> 32)); + } + } + + template + static ASMJIT_INLINE void writeU64x(void* p, uint64_t x) noexcept { + if (ASMJIT_ARCH_LE) + writeU64xLE(p, x); + else + writeU64xBE(p, x); + } + + template + static ASMJIT_INLINE void writeI64xLE(void* p, int64_t x) noexcept { + writeU64xLE(p, static_cast(x)); + } + + template + static ASMJIT_INLINE void writeI64xBE(void* p, int64_t x) noexcept { + writeU64xBE(p, static_cast(x)); + } + + template + static ASMJIT_INLINE void writeI64x(void* p, int64_t x) noexcept { + writeU64x(p, static_cast(x)); + } + + static ASMJIT_INLINE void writeU64aLE(void* p, uint64_t x) noexcept { writeU64xLE<8>(p, x); } + static ASMJIT_INLINE void writeU64uLE(void* p, uint64_t x) noexcept { writeU64xLE<0>(p, x); } + + static ASMJIT_INLINE void writeU64aBE(void* p, uint64_t x) noexcept { writeU64xBE<8>(p, x); } + static ASMJIT_INLINE void writeU64uBE(void* p, uint64_t x) noexcept { writeU64xBE<0>(p, x); } + + static ASMJIT_INLINE void writeU64a(void* p, uint64_t x) noexcept { writeU64x<8>(p, x); } + static ASMJIT_INLINE void writeU64u(void* p, uint64_t x) noexcept { writeU64x<0>(p, x); } + + static ASMJIT_INLINE void writeI64aLE(void* p, int64_t x) noexcept { writeI64xLE<8>(p, x); } + static ASMJIT_INLINE void writeI64uLE(void* p, int64_t x) noexcept { writeI64xLE<0>(p, x); } + + static ASMJIT_INLINE void writeI64aBE(void* p, int64_t x) noexcept { writeI64xBE<8>(p, x); } + static ASMJIT_INLINE void writeI64uBE(void* p, int64_t x) noexcept { writeI64xBE<0>(p, x); } + + static ASMJIT_INLINE void writeI64a(void* p, int64_t x) noexcept { writeI64x<8>(p, x); } + static ASMJIT_INLINE void writeI64u(void* p, int64_t x) noexcept { writeI64x<0>(p, x); } +} // Utils namespace + +// ============================================================================ +// [asmjit::UInt64] +// ============================================================================ + +union UInt64 { + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE UInt64 fromUInt64(uint64_t val) noexcept { + UInt64 data; + data.setUInt64(val); + return data; + } + + ASMJIT_INLINE UInt64 fromUInt64(const UInt64& val) noexcept { + UInt64 data; + data.setUInt64(val); + return data; + } + + // -------------------------------------------------------------------------- + // [Reset] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE void reset() noexcept { + if (ASMJIT_ARCH_64BIT) { + u64 = 0; + } + else { + u32[0] = 0; + u32[1] = 0; + } + } + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE uint64_t getUInt64() const noexcept { + return u64; + } + + ASMJIT_INLINE UInt64& setUInt64(uint64_t val) noexcept { + u64 = val; + return *this; + } + + ASMJIT_INLINE UInt64& setUInt64(const UInt64& val) noexcept { + if (ASMJIT_ARCH_64BIT) { + 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) noexcept { + if (ASMJIT_ARCH_64BIT) { + u64 = Utils::pack64_2x32(u0, u1); + } + else { + u32[0] = u0; + u32[1] = u1; + } + return *this; + } + + // -------------------------------------------------------------------------- + // [Add] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE UInt64& add(uint64_t val) noexcept { + u64 += val; + return *this; + } + + ASMJIT_INLINE UInt64& add(const UInt64& val) noexcept { + if (ASMJIT_ARCH_64BIT) { + u64 += val.u64; + } + else { + u32[0] += val.u32[0]; + u32[1] += val.u32[1]; + } + return *this; + } + + // -------------------------------------------------------------------------- + // [Sub] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE UInt64& sub(uint64_t val) noexcept { + u64 -= val; + return *this; + } + + ASMJIT_INLINE UInt64& sub(const UInt64& val) noexcept { + if (ASMJIT_ARCH_64BIT) { + u64 -= val.u64; + } + else { + u32[0] -= val.u32[0]; + u32[1] -= val.u32[1]; + } + return *this; + } + + // -------------------------------------------------------------------------- + // [And] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE UInt64& and_(uint64_t val) noexcept { + u64 &= val; + return *this; + } + + ASMJIT_INLINE UInt64& and_(const UInt64& val) noexcept { + if (ASMJIT_ARCH_64BIT) { + u64 &= val.u64; + } + else { + u32[0] &= val.u32[0]; + u32[1] &= val.u32[1]; + } + return *this; + } + + // -------------------------------------------------------------------------- + // [AndNot] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE UInt64& andNot(uint64_t val) noexcept { + u64 &= ~val; + return *this; + } + + ASMJIT_INLINE UInt64& andNot(const UInt64& val) noexcept { + if (ASMJIT_ARCH_64BIT) { + u64 &= ~val.u64; + } + else { + u32[0] &= ~val.u32[0]; + u32[1] &= ~val.u32[1]; + } + return *this; + } + + // -------------------------------------------------------------------------- + // [Or] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE UInt64& or_(uint64_t val) noexcept { + u64 |= val; + return *this; + } + + ASMJIT_INLINE UInt64& or_(const UInt64& val) noexcept { + if (ASMJIT_ARCH_64BIT) { + u64 |= val.u64; + } + else { + u32[0] |= val.u32[0]; + u32[1] |= val.u32[1]; + } + return *this; + } + + // -------------------------------------------------------------------------- + // [Xor] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE UInt64& xor_(uint64_t val) noexcept { + u64 ^= val; + return *this; + } + + ASMJIT_INLINE UInt64& xor_(const UInt64& val) noexcept { + if (ASMJIT_ARCH_64BIT) { + u64 ^= val.u64; + } + else { + u32[0] ^= val.u32[0]; + u32[1] ^= val.u32[1]; + } + return *this; + } + + // -------------------------------------------------------------------------- + // [Eq] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE bool isZero() const noexcept { + if (ASMJIT_ARCH_64BIT) + return u64 == 0; + else + return (u32[0] | u32[1]) == 0; + } + + ASMJIT_INLINE bool isNonZero() const noexcept { + if (ASMJIT_ARCH_64BIT) + return u64 != 0; + else + return (u32[0] | u32[1]) != 0; + } + + ASMJIT_INLINE bool eq(uint64_t val) const noexcept { + return u64 == val; + } + + ASMJIT_INLINE bool eq(const UInt64& val) const noexcept { + if (ASMJIT_ARCH_64BIT) + return u64 == val.u64; + else + return u32[0] == val.u32[0] && u32[1] == val.u32[1]; + } + + // -------------------------------------------------------------------------- + // [Operator Overload] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE UInt64& operator+=(uint64_t val) noexcept { return add(val); } + ASMJIT_INLINE UInt64& operator+=(const UInt64& val) noexcept { return add(val); } + + ASMJIT_INLINE UInt64& operator-=(uint64_t val) noexcept { return sub(val); } + ASMJIT_INLINE UInt64& operator-=(const UInt64& val) noexcept { return sub(val); } + + ASMJIT_INLINE UInt64& operator&=(uint64_t val) noexcept { return and_(val); } + ASMJIT_INLINE UInt64& operator&=(const UInt64& val) noexcept { return and_(val); } + + ASMJIT_INLINE UInt64& operator|=(uint64_t val) noexcept { return or_(val); } + ASMJIT_INLINE UInt64& operator|=(const UInt64& val) noexcept { return or_(val); } + + ASMJIT_INLINE UInt64& operator^=(uint64_t val) noexcept { return xor_(val); } + ASMJIT_INLINE UInt64& operator^=(const UInt64& val) noexcept { return xor_(val); } + + ASMJIT_INLINE bool operator==(uint64_t val) const noexcept { return eq(val); } + ASMJIT_INLINE bool operator==(const UInt64& val) const noexcept { return eq(val); } + + ASMJIT_INLINE bool operator!=(uint64_t val) const noexcept { return !eq(val); } + ASMJIT_INLINE bool operator!=(const UInt64& val) const noexcept { return !eq(val); } + + ASMJIT_INLINE bool operator<(uint64_t val) const noexcept { return u64 < val; } + ASMJIT_INLINE bool operator<(const UInt64& val) const noexcept { return u64 < val.u64; } + + ASMJIT_INLINE bool operator<=(uint64_t val) const noexcept { return u64 <= val; } + ASMJIT_INLINE bool operator<=(const UInt64& val) const noexcept { return u64 <= val.u64; } + + ASMJIT_INLINE bool operator>(uint64_t val) const noexcept { return u64 > val; } + ASMJIT_INLINE bool operator>(const UInt64& val) const noexcept { return u64 > val.u64; } + + ASMJIT_INLINE bool operator>=(uint64_t val) const noexcept { return u64 >= val; } + ASMJIT_INLINE bool operator>=(const UInt64& val) const noexcept { return u64 >= val.u64; } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + int8_t i8[8]; //!< 8-bit signed integer (8x). + uint8_t u8[8]; //!< 8-bit unsigned integer (8x). + + int16_t i16[4]; //!< 16-bit signed integer (4x). + uint16_t u16[4]; //!< 16-bit unsigned integer (4x). + + int32_t i32[2]; //!< 32-bit signed integer (2x). + uint32_t u32[2]; //!< 32-bit unsigned integer (2x). + + int64_t i64; //!< 64-bit signed integer. + uint64_t u64; //!< 64-bit unsigned integer. + + float f32[2]; //!< 32-bit floating point (2x). + double f64; //!< 64-bit floating point. + +#if ASMJIT_ARCH_LE + struct { float f32Lo, f32Hi; }; + struct { int32_t i32Lo, i32Hi; }; + struct { uint32_t u32Lo, u32Hi; }; +#else + struct { float f32Hi, f32Lo; }; + struct { int32_t i32Hi, i32Lo; }; + struct { uint32_t u32Hi, u32Lo; }; +#endif // ASMJIT_ARCH_LE +}; + +//! \} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" + +// [Guard] +#endif // _ASMJIT_BASE_UTILS_H diff --git a/libraries/asmjit/asmjit/base/vmem.cpp b/libraries/asmjit/asmjit/base/vmem.cpp new file mode 100644 index 00000000000..553032a5b1d --- /dev/null +++ b/libraries/asmjit/asmjit/base/vmem.cpp @@ -0,0 +1,1077 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Export] +#define ASMJIT_EXPORTS + +// [Dependencies] +#include "../base/osutils.h" +#include "../base/utils.h" +#include "../base/vmem.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +// This file contains implementation of virtual memory management for AsmJit +// library. There are several goals I decided to write implementation myself: +// +// - Granularity of allocated blocks is different than granularity for a typical +// C malloc. It is at least 64-bytes so CodeEmitter can guarantee the alignment +// up to 64 bytes, which is the size of a cache-line and it's also required by +// AVX-512 aligned loads and stores. 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 machine code execution and there should +// be not 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 the 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::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) noexcept { + 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. + + RbNode* node[2]; //!< Left[0] and right[1] nodes. + uint8_t* mem; //!< Virtual memory address. + uint32_t red; //!< Node color (red vs. black). +}; + +//! \internal +//! +//! Get if the node is red (nullptr or node with red flag). +static ASMJIT_INLINE bool rbIsRed(RbNode* node) noexcept { + return node && node->red; +} + +//! \internal +//! +//! Check whether the RB tree is valid. +static int rbAssert(RbNode* root) noexcept { + if (!root) 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 == nullptr || ln->mem < root->mem); + ASMJIT_ASSERT(rn == nullptr || 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) noexcept { + 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) noexcept { + root->node[!dir] = rbRotateSingle(root->node[!dir], !dir); + return rbRotateSingle(root, dir); +} + +// ============================================================================ +// [asmjit::VMemMgr::MemNode] +// ============================================================================ + +struct VMemMgr::MemNode : public RbNode { + ASMJIT_INLINE void init(MemNode* other) noexcept { + mem = other->mem; + + size = other->size; + used = other->used; + blocks = other->blocks; + density = other->density; + largestBlock = other->largestBlock; + + baUsed = other->baUsed; + baCont = other->baCont; + } + + // Get available space. + ASMJIT_INLINE size_t getAvailable() const noexcept { return size - used; } + + 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 { + //! Get available space. + ASMJIT_INLINE size_t getAvailable() const noexcept { return size - used; } + + PermanentNode* prev; // Pointer to prev chunk or nullptr. + 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) noexcept { + uint32_t flags = OSUtils::kVMWritable | OSUtils::kVMExecutable; +#if !ASMJIT_OS_WINDOWS + return static_cast(OSUtils::allocVirtualMemory(size, vSize, flags)); +#else + return static_cast(OSUtils::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) noexcept { +#if !ASMJIT_OS_WINDOWS + return OSUtils::releaseVirtualMemory(p, vSize); +#else + return OSUtils::releaseProcessMemory(self->_hProcess, p, vSize); +#endif +} + +//! \internal +//! +//! Check whether the Red-Black tree is valid. +static bool vMemMgrCheckTree(VMemMgr* self) noexcept { + return rbAssert(self->_root) > 0; +} + +//! \internal +//! +//! Alloc virtual memory including a heap memory needed for `MemNode` data. +//! +//! Returns set-up `MemNode*` or nullptr if allocation failed. +static MemNode* vMemMgrCreateNode(VMemMgr* self, size_t size, size_t density) noexcept { + size_t vSize; + uint8_t* vmem = vMemMgrAllocVMem(self, size, &vSize); + if (!vmem) return nullptr; + + 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(Internal::allocMemory(sizeof(MemNode))); + uint8_t* data = static_cast(Internal::allocMemory(bsize * 2)); + + // Out of memory. + if (!node || !data) { + vMemMgrReleaseVMem(self, vmem, vSize); + if (node) Internal::releaseMemory(node); + if (data) Internal::releaseMemory(data); + return nullptr; + } + + // Initialize RbNode data. + node->node[0] = nullptr; + node->node[1] = nullptr; + node->mem = vmem; + node->red = 1; + + // Initialize MemNode data. + node->prev = nullptr; + node->next = nullptr; + + node->size = vSize; + node->used = 0; + node->blocks = blocks; + node->density = density; + node->largestBlock = vSize; + + ::memset(data, 0, bsize * 2); + node->baUsed = reinterpret_cast(data); + node->baCont = reinterpret_cast(data + bsize); + + return node; +} + +static void vMemMgrInsertNode(VMemMgr* self, MemNode* node) noexcept { + if (!self->_root) { + // Empty tree case. + self->_root = node; + } + else { + // False tree root. + RbNode head = { { nullptr, nullptr }, 0, 0 }; + + // Grandparent & parent. + RbNode* g = nullptr; + RbNode* t = &head; + + // Iterator & parent. + RbNode* p = nullptr; + RbNode* q = t->node[1] = self->_root; + + int dir = 0; + int last = 0; // Not needed to initialize, but makes some tools happy. + + // Search down the tree. + for (;;) { + if (!q) { + // 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) t = g; + + g = p; + p = q; + q = q->node[dir]; + } + + // Update root. + self->_root = static_cast(head.node[1]); + } + + // Make root black. + self->_root->red = 0; + + // Link with others. + node->prev = self->_last; + + if (!self->_first) { + 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) noexcept { + // False tree root. + RbNode head = { { nullptr, nullptr }, 0, 0 }; + + // Helpers. + RbNode* q = &head; + RbNode* p = nullptr; + RbNode* g = nullptr; + + // Found item. + RbNode* f = nullptr; + int dir = 1; + + // Set up. + q->node[1] = self->_root; + + // Search and push a red down. + while (q->node[dir]) { + 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) { + 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 != nullptr); + ASMJIT_ASSERT(f != &head); + ASMJIT_ASSERT(q != &head); + + if (f != q) { + ASMJIT_ASSERT(f != &head); + static_cast(f)->init(static_cast(q)); + } + + p->node[p->node[1] == q] = q->node[q->node[0] == nullptr]; + + // Update root and make it black. + self->_root = static_cast(head.node[1]); + if (self->_root) self->_root->red = 0; + + // Unlink. + MemNode* next = static_cast(q)->next; + MemNode* prev = static_cast(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(q); +} + +static MemNode* vMemMgrFindNodeByPtr(VMemMgr* self, uint8_t* mem) noexcept { + MemNode* node = self->_root; + while (node) { + uint8_t* nodeMem = node->mem; + + // Go left. + if (mem < nodeMem) { + node = static_cast(node->node[0]); + continue; + } + + // Go right. + uint8_t* nodeEnd = nodeMem + node->size; + if (mem >= nodeEnd) { + node = static_cast(node->node[1]); + continue; + } + + // Match. + break; + } + return node; +} + +static void* vMemMgrAllocPermanent(VMemMgr* self, size_t vSize) noexcept { + static const size_t permanentAlignment = 32; + static const size_t permanentNodeSize = 32768; + + vSize = Utils::alignTo(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) { + size_t nodeSize = permanentNodeSize; + if (nodeSize < vSize) nodeSize = vSize; + + node = static_cast(Internal::allocMemory(sizeof(PermanentNode))); + if (!node) return nullptr; + + node->mem = vMemMgrAllocVMem(self, nodeSize, &node->size); + if (!node->mem) { + Internal::releaseMemory(node); + return nullptr; + } + + 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(result); +} + +static void* vMemMgrAllocFreeable(VMemMgr* self, size_t vSize) noexcept { + // 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 = Utils::alignTo(vSize, 32); + if (vSize == 0) + return nullptr; + + 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 L_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) return nullptr; + + // 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; + } + +L_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) noexcept { + MemNode* node = self->_first; + + while (node) { + MemNode* next = node->next; + + if (!keepVirtualMemory) + vMemMgrReleaseVMem(self, node->mem, node->size); + + Internal::releaseMemory(node->baUsed); + Internal::releaseMemory(node); + + node = next; + } + + self->_allocatedBytes = 0; + self->_usedBytes = 0; + + self->_root = nullptr; + self->_first = nullptr; + self->_last = nullptr; + self->_optimal = nullptr; +} + +// ============================================================================ +// [asmjit::VMemMgr - Construction / Destruction] +// ============================================================================ + +#if !ASMJIT_OS_WINDOWS +VMemMgr::VMemMgr() noexcept { +#else +VMemMgr::VMemMgr(HANDLE hProcess) noexcept { +#endif + + VMemInfo vm = OSUtils::getVirtualMemoryInfo(); + +#if ASMJIT_OS_WINDOWS + _hProcess = hProcess ? hProcess : vm.hCurrentProcess; +#endif // ASMJIT_OS_WINDOWS + + _blockSize = vm.pageGranularity; + _blockDensity = 64; + + _allocatedBytes = 0; + _usedBytes = 0; + + _root = nullptr; + _first = nullptr; + _last = nullptr; + _optimal = nullptr; + + _permanent = nullptr; + _keepVirtualMemory = false; +} + +VMemMgr::~VMemMgr() noexcept { + // 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; + Internal::releaseMemory(node); + node = prev; + } +} + +// ============================================================================ +// [asmjit::VMemMgr - Reset] +// ============================================================================ + +void VMemMgr::reset() noexcept { + vMemMgrReset(this, false); +} + +// ============================================================================ +// [asmjit::VMemMgr - Alloc / Release] +// ============================================================================ + +void* VMemMgr::alloc(size_t size, uint32_t type) noexcept { + if (type == kAllocPermanent) + return vMemMgrAllocPermanent(this, size); + else + return vMemMgrAllocFreeable(this, size); +} + +Error VMemMgr::release(void* p) noexcept { + if (!p) return kErrorOk; + + AutoLock locked(_lock); + MemNode* node = vMemMgrFindNodeByPtr(this, static_cast(p)); + if (!node) return DebugUtils::errored(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); + Internal::releaseMemory(node->baUsed); + + node->baUsed = nullptr; + node->baCont = nullptr; + + // Statistics. + _allocatedBytes -= node->size; + + // Remove node. This function can return different node than + // passed into, but data is copied into previous node if needed. + Internal::releaseMemory(vMemMgrRemoveNode(this, node)); + ASMJIT_ASSERT(vMemMgrCheckTree(this)); + } + + return kErrorOk; +} + +Error VMemMgr::shrink(void* p, size_t used) noexcept { + if (!p) return kErrorOk; + if (used == 0) + return release(p); + + AutoLock locked(_lock); + MemNode* node = vMemMgrFindNodeByPtr(this, (uint8_t*)p); + if (!node) return DebugUtils::errored(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) noexcept { + 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) noexcept { + 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) noexcept { + INFO("Used : %u", static_cast(memmgr.getUsedBytes())); + INFO("Allocated: %u", static_cast(memmgr.getAllocatedBytes())); +} + +static void VMemTest_shuffle(void** a, void** b, size_t count) noexcept { + 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(kCount)); + + void** a = (void**)Internal::allocMemory(sizeof(void*) * kCount); + void** b = (void**)Internal::allocMemory(sizeof(void*) * kCount); + + EXPECT(a != nullptr && b != nullptr, + "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] != nullptr, + "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(kCount)); + for (i = 0; i < kCount; i++) { + int r = (rand() % 1000) + 4; + + a[i] = memmgr.alloc(r); + EXPECT(a[i] != nullptr, + "Couldn't allocate %d bytes of virtual memory", r); + + b[i] = Internal::allocMemory(r); + EXPECT(b[i] != nullptr, + "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]); + Internal::releaseMemory(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] != nullptr, + "Couldn't allocate %d bytes of virtual memory", r); + + b[i] = Internal::allocMemory(r); + EXPECT(b[i] != nullptr, + "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]); + Internal::releaseMemory(b[i]); + } + VMemTest_stats(memmgr); + + Internal::releaseMemory(a); + Internal::releaseMemory(b); +} +#endif // ASMJIT_TEST + +} // asmjit namespace diff --git a/libraries/asmjit/asmjit/base/vmem.h b/libraries/asmjit/asmjit/base/vmem.h new file mode 100644 index 00000000000..6a1a51339e3 --- /dev/null +++ b/libraries/asmjit/asmjit/base/vmem.h @@ -0,0 +1,154 @@ +// [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/globals.h" +#include "../base/osutils.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +//! \addtogroup asmjit_base +//! \{ + +// ============================================================================ +// [asmjit::VMemMgr] +// ============================================================================ + +//! Reference implementation of memory manager that uses `VMemUtil` to allocate +//! chunks of virtual memory and bit arrays to manage it. +class VMemMgr { +public: + //! Type of virtual memory allocation, see `VMemMgr::alloc()`. + ASMJIT_ENUM(AllocType) { + //! Normal memory allocation, has to be freed by `VMemMgr::release()`. + kAllocFreeable = 0, + //! Allocate permanent memory, can't be freed. + kAllocPermanent = 1 + }; + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + +#if !ASMJIT_OS_WINDOWS + //! Create a `VMemMgr` instance. + ASMJIT_API VMemMgr() noexcept; +#else + //! Create a `VMemMgr` instance. + //! + //! NOTE: When running on Windows it's possible to specify a `hProcess` to + //! be used for memory allocation. Using `hProcess` allows to allocate memory + //! of a remote process. + ASMJIT_API VMemMgr(HANDLE hProcess = static_cast(0)) noexcept; +#endif // ASMJIT_OS_WINDOWS + + //! Destroy the `VMemMgr` instance and free all blocks. + ASMJIT_API ~VMemMgr() noexcept; + + // -------------------------------------------------------------------------- + // [Reset] + // -------------------------------------------------------------------------- + + //! Free all allocated memory. + ASMJIT_API void reset() noexcept; + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + +#if ASMJIT_OS_WINDOWS + //! Get the handle of the process memory manager is bound to. + ASMJIT_INLINE HANDLE getProcessHandle() const noexcept { return _hProcess; } +#endif // ASMJIT_OS_WINDOWS + + //! Get how many bytes are currently allocated. + ASMJIT_INLINE size_t getAllocatedBytes() const noexcept { return _allocatedBytes; } + //! Get how many bytes are currently used. + ASMJIT_INLINE size_t getUsedBytes() const noexcept { return _usedBytes; } + + //! Get whether to keep allocated memory after the `VMemMgr` is destroyed. + //! + //! \sa \ref setKeepVirtualMemory. + ASMJIT_INLINE bool getKeepVirtualMemory() const noexcept { return _keepVirtualMemory; } + //! Set whether to keep allocated memory after the 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 kAllocPermanent is always kept. + //! + //! \sa \ref getKeepVirtualMemory. + ASMJIT_INLINE void setKeepVirtualMemory(bool val) noexcept { _keepVirtualMemory = val; } + + // -------------------------------------------------------------------------- + // [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 = kAllocFreeable) noexcept; + //! Free previously allocated memory at a given `address`. + ASMJIT_API Error release(void* p) noexcept; + //! Free extra memory allocated with `p`. + ASMJIT_API Error shrink(void* p, size_t used) noexcept; + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + +#if ASMJIT_OS_WINDOWS + HANDLE _hProcess; //!< Process passed to `VirtualAllocEx` and `VirtualFree`. +#endif // ASMJIT_OS_WINDOWS + Lock _lock; //!< Lock to enable thread-safe functionality. + + size_t _blockSize; //!< Default block size. + size_t _blockDensity; //!< Default block density. + bool _keepVirtualMemory; //!< Keep virtual memory after destroyed. + + size_t _allocatedBytes; //!< How many bytes are currently allocated. + size_t _usedBytes; //!< How many bytes are currently used. + + //! \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 "../asmjit_apiend.h" + +// [Guard] +#endif // _ASMJIT_BASE_VMEM_H diff --git a/libraries/asmjit/asmjit/base/zone.cpp b/libraries/asmjit/asmjit/base/zone.cpp new file mode 100644 index 00000000000..6dd535de80b --- /dev/null +++ b/libraries/asmjit/asmjit/base/zone.cpp @@ -0,0 +1,962 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Export] +#define ASMJIT_EXPORTS + +// [Dependencies] +#include "../base/utils.h" +#include "../base/zone.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +//! Zero size block used by `Zone` that doesn't have any memory allocated. +static const Zone::Block Zone_zeroBlock = { nullptr, nullptr, 0, { 0 } }; + +static ASMJIT_INLINE uint32_t Zone_getAlignmentOffsetFromAlignment(uint32_t x) noexcept { + switch (x) { + default: return 0; + case 0 : return 0; + case 1 : return 0; + case 2 : return 1; + case 4 : return 2; + case 8 : return 3; + case 16: return 4; + case 32: return 5; + case 64: return 6; + } +} + +// ============================================================================ +// [asmjit::Zone - Construction / Destruction] +// ============================================================================ + +Zone::Zone(uint32_t blockSize, uint32_t blockAlignment) noexcept + : _ptr(nullptr), + _end(nullptr), + _block(const_cast(&Zone_zeroBlock)), + _blockSize(blockSize), + _blockAlignmentShift(Zone_getAlignmentOffsetFromAlignment(blockAlignment)) {} + +Zone::~Zone() noexcept { + reset(true); +} + +// ============================================================================ +// [asmjit::Zone - Reset] +// ============================================================================ + +void Zone::reset(bool releaseMemory) noexcept { + 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; + Internal::releaseMemory(cur); + cur = prev; + } while (cur); + + cur = next; + while (cur) { + next = cur->next; + Internal::releaseMemory(cur); + cur = next; + } + + _ptr = nullptr; + _end = nullptr; + _block = const_cast(&Zone_zeroBlock); + } + else { + while (cur->prev) + cur = cur->prev; + + _ptr = cur->data; + _end = _ptr + cur->size; + _block = cur; + } +} + +// ============================================================================ +// [asmjit::Zone - Alloc] +// ============================================================================ + +void* Zone::_alloc(size_t size) noexcept { + Block* curBlock = _block; + uint8_t* p; + + size_t blockSize = std::max(_blockSize, size); + size_t blockAlignment = getBlockAlignment(); + + // 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 || getRemainingSize() < size); + + // If the `Zone` has been cleared 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 && next->size >= size) { + p = Utils::alignTo(next->data, blockAlignment); + + _block = next; + _ptr = p + size; + _end = next->data + next->size; + + return static_cast(p); + } + + // Prevent arithmetic overflow. + if (ASMJIT_UNLIKELY(blockSize > (~static_cast(0) - sizeof(Block) - blockAlignment))) + return nullptr; + + blockSize += blockAlignment; + Block* newBlock = static_cast(Internal::allocMemory(sizeof(Block) + blockSize)); + + if (ASMJIT_UNLIKELY(!newBlock)) + return nullptr; + + // Align the pointer to `blockAlignment` and adjust the size of this block + // accordingly. It's the same as using `blockAlignment - Utils::alignDiff()`, + // just written differently. + p = Utils::alignTo(newBlock->data, blockAlignment); + newBlock->prev = nullptr; + newBlock->next = nullptr; + newBlock->size = blockSize; + + 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) { + newBlock->next = next; + next->prev = newBlock; + } + } + + _block = newBlock; + _ptr = p + size; + _end = newBlock->data + blockSize; + + return static_cast(p); +} + +void* Zone::allocZeroed(size_t size) noexcept { + void* p = alloc(size); + if (ASMJIT_UNLIKELY(!p)) return p; + return ::memset(p, 0, size); +} + +void* Zone::dup(const void* data, size_t size, bool nullTerminate) noexcept { + if (ASMJIT_UNLIKELY(!data || !size)) return nullptr; + + ASMJIT_ASSERT(size != IntTraits::maxValue()); + uint8_t* m = allocT(size + nullTerminate); + if (ASMJIT_UNLIKELY(!m)) return nullptr; + + ::memcpy(m, data, size); + if (nullTerminate) m[size] = '\0'; + + return static_cast(m); +} + +char* Zone::sformat(const char* fmt, ...) noexcept { + if (ASMJIT_UNLIKELY(!fmt)) return nullptr; + + 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(dup(buf, len)); +} + +// ============================================================================ +// [asmjit::ZoneHeap - Helpers] +// ============================================================================ + +static bool ZoneHeap_hasDynamicBlock(ZoneHeap* self, ZoneHeap::DynamicBlock* block) noexcept { + ZoneHeap::DynamicBlock* cur = self->_dynamicBlocks; + while (cur) { + if (cur == block) + return true; + cur = cur->next; + } + return false; +} + +// ============================================================================ +// [asmjit::ZoneHeap - Init / Reset] +// ============================================================================ + +void ZoneHeap::reset(Zone* zone) noexcept { + // Free dynamic blocks. + DynamicBlock* block = _dynamicBlocks; + while (block) { + DynamicBlock* next = block->next; + Internal::releaseMemory(block); + block = next; + } + + // Zero the entire class and initialize to the given `zone`. + ::memset(this, 0, sizeof(*this)); + _zone = zone; +} + +// ============================================================================ +// [asmjit::ZoneHeap - Alloc / Release] +// ============================================================================ + +void* ZoneHeap::_alloc(size_t size, size_t& allocatedSize) noexcept { + ASMJIT_ASSERT(isInitialized()); + + // We use our memory pool only if the requested block is of a reasonable size. + uint32_t slot; + if (_getSlotIndex(size, slot, allocatedSize)) { + // Slot reuse. + uint8_t* p = reinterpret_cast(_slots[slot]); + size = allocatedSize; + + if (p) { + _slots[slot] = reinterpret_cast(p)->next; + //printf("ALLOCATED %p of size %d (SLOT %d)\n", p, int(size), slot); + return p; + } + + // So use Zone to allocate a new chunk for us. But before we use it, we + // check if there is enough room for the new chunk in zone, and if not, + // we redistribute the remaining memory in Zone's current block into slots. + Zone* zone = _zone; + p = Utils::alignTo(zone->getCursor(), kBlockAlignment); + size_t remain = (p <= zone->getEnd()) ? (size_t)(zone->getEnd() - p) : size_t(0); + + if (ASMJIT_LIKELY(remain >= size)) { + zone->setCursor(p + size); + //printf("ALLOCATED %p of size %d (SLOT %d)\n", p, int(size), slot); + return p; + } + else { + // Distribute the remaining memory to suitable slots. + if (remain >= kLoGranularity) { + do { + size_t distSize = std::min(remain, kLoMaxSize); + uint32_t distSlot = static_cast((distSize - kLoGranularity) / kLoGranularity); + ASMJIT_ASSERT(distSlot < kLoCount); + + reinterpret_cast(p)->next = _slots[distSlot]; + _slots[distSlot] = reinterpret_cast(p); + + p += distSize; + remain -= distSize; + } while (remain >= kLoGranularity); + zone->setCursor(p); + } + + p = static_cast(zone->_alloc(size)); + if (ASMJIT_UNLIKELY(!p)) { + allocatedSize = 0; + return nullptr; + } + + //printf("ALLOCATED %p of size %d (SLOT %d)\n", p, int(size), slot); + return p; + } + } + else { + // Allocate a dynamic block. + size_t overhead = sizeof(DynamicBlock) + sizeof(DynamicBlock*) + kBlockAlignment; + + // Handle a possible overflow. + if (ASMJIT_UNLIKELY(overhead >= ~static_cast(0) - size)) + return nullptr; + + void* p = Internal::allocMemory(size + overhead); + if (ASMJIT_UNLIKELY(!p)) { + allocatedSize = 0; + return nullptr; + } + + // Link as first in `_dynamicBlocks` double-linked list. + DynamicBlock* block = static_cast(p); + DynamicBlock* next = _dynamicBlocks; + + if (next) + next->prev = block; + + block->prev = nullptr; + block->next = next; + _dynamicBlocks = block; + + // Align the pointer to the guaranteed alignment and store `DynamicBlock` + // at the end of the memory block, so `_releaseDynamic()` can find it. + p = Utils::alignTo(static_cast(p) + sizeof(DynamicBlock) + sizeof(DynamicBlock*), kBlockAlignment); + reinterpret_cast(p)[-1] = block; + + allocatedSize = size; + //printf("ALLOCATED DYNAMIC %p of size %d\n", p, int(size)); + return p; + } +} + +void* ZoneHeap::_allocZeroed(size_t size, size_t& allocatedSize) noexcept { + ASMJIT_ASSERT(isInitialized()); + + void* p = _alloc(size, allocatedSize); + if (ASMJIT_UNLIKELY(!p)) return p; + return ::memset(p, 0, allocatedSize); +} + +void ZoneHeap::_releaseDynamic(void* p, size_t size) noexcept { + ASMJIT_ASSERT(isInitialized()); + //printf("RELEASING DYNAMIC %p of size %d\n", p, int(size)); + + // Pointer to `DynamicBlock` is stored at [-1]. + DynamicBlock* block = reinterpret_cast(p)[-1]; + ASMJIT_ASSERT(ZoneHeap_hasDynamicBlock(this, block)); + + // Unlink and free. + DynamicBlock* prev = block->prev; + DynamicBlock* next = block->next; + + if (prev) + prev->next = next; + else + _dynamicBlocks = next; + + if (next) + next->prev = prev; + + Internal::releaseMemory(block); +} + +// ============================================================================ +// [asmjit::ZoneVectorBase - Helpers] +// ============================================================================ + +Error ZoneVectorBase::_grow(ZoneHeap* heap, size_t sizeOfT, size_t n) noexcept { + size_t threshold = Globals::kAllocThreshold / sizeOfT; + size_t capacity = _capacity; + size_t after = _length; + + if (ASMJIT_UNLIKELY(IntTraits::maxValue() - n < after)) + return DebugUtils::errored(kErrorNoHeapMemory); + + after += n; + if (capacity >= after) + return kErrorOk; + + // ZoneVector is used as an array to hold short-lived data structures used + // during code generation. The growing strategy is simple - use small capacity + // at the beginning (very good for ZoneHeap) and then grow quicker to prevent + // successive reallocations. + if (capacity < 4) + capacity = 4; + else if (capacity < 8) + capacity = 8; + else if (capacity < 16) + capacity = 16; + else if (capacity < 64) + capacity = 64; + else if (capacity < 256) + capacity = 256; + + while (capacity < after) { + if (capacity < threshold) + capacity *= 2; + else + capacity += threshold; + } + + return _reserve(heap, sizeOfT, capacity); +} + +Error ZoneVectorBase::_reserve(ZoneHeap* heap, size_t sizeOfT, size_t n) noexcept { + size_t oldCapacity = _capacity; + if (oldCapacity >= n) return kErrorOk; + + size_t nBytes = n * sizeOfT; + if (ASMJIT_UNLIKELY(nBytes < n)) + return DebugUtils::errored(kErrorNoHeapMemory); + + size_t allocatedBytes; + uint8_t* newData = static_cast(heap->alloc(nBytes, allocatedBytes)); + + if (ASMJIT_UNLIKELY(!newData)) + return DebugUtils::errored(kErrorNoHeapMemory); + + void* oldData = _data; + if (_length) + ::memcpy(newData, oldData, _length * sizeOfT); + + if (oldData) + heap->release(oldData, oldCapacity * sizeOfT); + + _capacity = allocatedBytes / sizeOfT; + ASMJIT_ASSERT(_capacity >= n); + + _data = newData; + return kErrorOk; +} + +Error ZoneVectorBase::_resize(ZoneHeap* heap, size_t sizeOfT, size_t n) noexcept { + size_t length = _length; + if (_capacity < n) { + ASMJIT_PROPAGATE(_grow(heap, sizeOfT, n - length)); + ASMJIT_ASSERT(_capacity >= n); + } + + if (length < n) + ::memset(static_cast(_data) + length * sizeOfT, 0, (n - length) * sizeOfT); + + _length = n; + return kErrorOk; +} + +// ============================================================================ +// [asmjit::ZoneBitVector - Ops] +// ============================================================================ + +Error ZoneBitVector::_resize(ZoneHeap* heap, size_t newLength, size_t idealCapacity, bool newBitsValue) noexcept { + ASMJIT_ASSERT(idealCapacity >= newLength); + + if (newLength <= _length) { + // The size after the resize is lesser than or equal to the current length. + size_t idx = newLength / kBitsPerWord; + size_t bit = newLength % kBitsPerWord; + + // Just set all bits outside of the new length in the last word to zero. + // There is a case that there are not bits to set if `bit` is zero. This + // happens when `newLength` is a multiply of `kBitsPerWord` like 64, 128, + // and so on. In that case don't change anything as that would mean settings + // bits outside of the `_length`. + if (bit) + _data[idx] &= (static_cast(1) << bit) - 1U; + + _length = newLength; + return kErrorOk; + } + + size_t oldLength = _length; + BitWord* data = _data; + + if (newLength > _capacity) { + // Realloc needed... Calculate the minimum capacity (in bytes) requied. + size_t minimumCapacityInBits = Utils::alignTo(idealCapacity, kBitsPerWord); + size_t allocatedCapacity; + + if (ASMJIT_UNLIKELY(minimumCapacityInBits < newLength)) + return DebugUtils::errored(kErrorNoHeapMemory); + + // Normalize to bytes. + size_t minimumCapacity = minimumCapacityInBits / 8; + BitWord* newData = static_cast(heap->alloc(minimumCapacity, allocatedCapacity)); + + if (ASMJIT_UNLIKELY(!newData)) + return DebugUtils::errored(kErrorNoHeapMemory); + + // `allocatedCapacity` now contains number in bytes, we need bits. + size_t allocatedCapacityInBits = allocatedCapacity * 8; + + // Arithmetic overflow should normally not happen. If it happens we just + // change the `allocatedCapacityInBits` to the `minimumCapacityInBits` as + // this value is still safe to be used to call `_heap->release(...)`. + if (ASMJIT_UNLIKELY(allocatedCapacityInBits < allocatedCapacity)) + allocatedCapacityInBits = minimumCapacityInBits; + + if (oldLength) + ::memcpy(newData, data, _wordsPerBits(oldLength)); + + if (data) + heap->release(data, _capacity / 8); + data = newData; + + _data = data; + _capacity = allocatedCapacityInBits; + } + + // Start (of the old length) and end (of the new length) bits + size_t idx = oldLength / kBitsPerWord; + size_t startBit = oldLength % kBitsPerWord; + size_t endBit = newLength % kBitsPerWord; + + // Set new bits to either 0 or 1. The `pattern` is used to set multiple + // bits per bit-word and contains either all zeros or all ones. + BitWord pattern = _patternFromBit(newBitsValue); + + // First initialize the last bit-word of the old length. + if (startBit) { + size_t nBits = 0; + + if (idx == (newLength / kBitsPerWord)) { + // The number of bit-words is the same after the resize. In that case + // we need to set only bits necessary in the current last bit-word. + ASMJIT_ASSERT(startBit < endBit); + nBits = endBit - startBit; + } + else { + // There is be more bit-words after the resize. In that case we don't + // have to be extra careful about the last bit-word of the old length. + nBits = kBitsPerWord - startBit; + } + + data[idx++] |= pattern << nBits; + } + + // Initialize all bit-words after the last bit-word of the old length. + size_t endIdx = _wordsPerBits(newLength); + endIdx -= static_cast(endIdx * kBitsPerWord == newLength); + + while (idx <= endIdx) + data[idx++] = pattern; + + // Clear unused bits of the last bit-word. + if (endBit) + data[endIdx] &= (static_cast(1) << endBit) - 1; + + _length = newLength; + return kErrorOk; +} + +Error ZoneBitVector::_append(ZoneHeap* heap, bool value) noexcept { + size_t kThreshold = Globals::kAllocThreshold * 8; + size_t newLength = _length + 1; + size_t idealCapacity = _capacity; + + if (idealCapacity < 128) + idealCapacity = 128; + else if (idealCapacity <= kThreshold) + idealCapacity *= 2; + else + idealCapacity += kThreshold; + + if (ASMJIT_UNLIKELY(idealCapacity < _capacity)) { + // It's technically impossible that `_length + 1` overflows. + idealCapacity = newLength; + ASMJIT_ASSERT(idealCapacity > _capacity); + } + + return _resize(heap, newLength, idealCapacity, value); +} + +Error ZoneBitVector::fill(size_t from, size_t to, bool value) noexcept { + if (ASMJIT_UNLIKELY(from >= to)) { + if (from > to) + return DebugUtils::errored(kErrorInvalidArgument); + else + return kErrorOk; + } + + ASMJIT_ASSERT(from <= _length); + ASMJIT_ASSERT(to <= _length); + + // This is very similar to `ZoneBitVector::_fill()`, however, since we + // actually set bits that are already part of the container we need to + // special case filiing to zeros and ones. + size_t idx = from / kBitsPerWord; + size_t startBit = from % kBitsPerWord; + + size_t endIdx = to / kBitsPerWord; + size_t endBit = to % kBitsPerWord; + + BitWord* data = _data; + ASMJIT_ASSERT(data != nullptr); + + // Special case for non-zero `startBit`. + if (startBit) { + if (idx == endIdx) { + ASMJIT_ASSERT(startBit < endBit); + + size_t nBits = endBit - startBit; + BitWord mask = ((static_cast(1) << nBits) - 1) << startBit; + + if (value) + data[idx] |= mask; + else + data[idx] &= ~mask; + return kErrorOk; + } + else { + BitWord mask = (static_cast(0) - 1) << startBit; + + if (value) + data[idx++] |= mask; + else + data[idx++] &= ~mask; + } + } + + // Fill all bits in case there is a gap between the current `idx` and `endIdx`. + if (idx < endIdx) { + BitWord pattern = _patternFromBit(value); + do { + data[idx++] = pattern; + } while (idx < endIdx); + } + + // Special case for non-zero `endBit`. + if (endBit) { + BitWord mask = ((static_cast(1) << endBit) - 1); + if (value) + data[endIdx] |= mask; + else + data[endIdx] &= ~mask; + } + + return kErrorOk; +} + +// ============================================================================ +// [asmjit::ZoneStackBase - Init / Reset] +// ============================================================================ + +Error ZoneStackBase::_init(ZoneHeap* heap, size_t middleIndex) noexcept { + ZoneHeap* oldHeap = _heap; + + if (oldHeap) { + Block* block = _block[kSideLeft]; + while (block) { + Block* next = block->getNext(); + oldHeap->release(block, kBlockSize); + block = next; + } + + _heap = nullptr; + _block[kSideLeft] = nullptr; + _block[kSideRight] = nullptr; + } + + + if (heap) { + Block* block = static_cast(heap->alloc(kBlockSize)); + if (ASMJIT_UNLIKELY(!block)) + return DebugUtils::errored(kErrorNoHeapMemory); + + block->_link[kSideLeft] = nullptr; + block->_link[kSideRight] = nullptr; + block->_start = (uint8_t*)block + middleIndex; + block->_end = (uint8_t*)block + middleIndex; + + _heap = heap; + _block[kSideLeft] = block; + _block[kSideRight] = block; + } + + return kErrorOk; +} + +// ============================================================================ +// [asmjit::ZoneStackBase - Ops] +// ============================================================================ + +Error ZoneStackBase::_prepareBlock(uint32_t side, size_t initialIndex) noexcept { + ASMJIT_ASSERT(isInitialized()); + + Block* prev = _block[side]; + ASMJIT_ASSERT(!prev->isEmpty()); + + Block* block = _heap->allocT(kBlockSize); + if (ASMJIT_UNLIKELY(!block)) + return DebugUtils::errored(kErrorNoHeapMemory); + + block->_link[ side] = nullptr; + block->_link[!side] = prev; + block->_start = (uint8_t*)block + initialIndex; + block->_end = (uint8_t*)block + initialIndex; + + prev->_link[side] = block; + _block[side] = block; + + return kErrorOk; +} + +void ZoneStackBase::_cleanupBlock(uint32_t side, size_t middleIndex) noexcept { + Block* block = _block[side]; + ASMJIT_ASSERT(block->isEmpty()); + + Block* prev = block->_link[!side]; + if (prev) { + ASMJIT_ASSERT(prev->_link[side] == block); + _heap->release(block, kBlockSize); + + prev->_link[side] = nullptr; + _block[side] = prev; + } + else if (_block[!side] == prev && prev->isEmpty()) { + // If the container becomes empty center both pointers in the remaining block. + prev->_start = (uint8_t*)prev + middleIndex; + prev->_end = (uint8_t*)prev + middleIndex; + } +} + +// ============================================================================ +// [asmjit::ZoneHashBase - Utilities] +// ============================================================================ + +static uint32_t ZoneHash_getClosestPrime(uint32_t x) noexcept { + static const uint32_t primeTable[] = { + 23, 53, 193, 389, 769, 1543, 3079, 6151, 12289, 24593 + }; + + size_t i = 0; + uint32_t p; + + do { + if ((p = primeTable[i]) > x) + break; + } while (++i < ASMJIT_ARRAY_SIZE(primeTable)); + + return p; +} + +// ============================================================================ +// [asmjit::ZoneHashBase - Reset] +// ============================================================================ + +void ZoneHashBase::reset(ZoneHeap* heap) noexcept { + ZoneHashNode** oldData = _data; + if (oldData != _embedded) + _heap->release(oldData, _bucketsCount * sizeof(ZoneHashNode*)); + + _heap = heap; + _size = 0; + _bucketsCount = 1; + _bucketsGrow = 1; + _data = _embedded; + _embedded[0] = nullptr; +} + +// ============================================================================ +// [asmjit::ZoneHashBase - Rehash] +// ============================================================================ + +void ZoneHashBase::_rehash(uint32_t newCount) noexcept { + ASMJIT_ASSERT(isInitialized()); + + ZoneHashNode** oldData = _data; + ZoneHashNode** newData = reinterpret_cast( + _heap->allocZeroed(static_cast(newCount) * sizeof(ZoneHashNode*))); + + // We can still store nodes into the table, but it will degrade. + if (ASMJIT_UNLIKELY(newData == nullptr)) + return; + + uint32_t i; + uint32_t oldCount = _bucketsCount; + + for (i = 0; i < oldCount; i++) { + ZoneHashNode* node = oldData[i]; + while (node) { + ZoneHashNode* next = node->_hashNext; + uint32_t hMod = node->_hVal % newCount; + + node->_hashNext = newData[hMod]; + newData[hMod] = node; + + node = next; + } + } + + // 90% is the maximum occupancy, can't overflow since the maximum capacity + // is limited to the last prime number stored in the prime table. + if (oldData != _embedded) + _heap->release(oldData, _bucketsCount * sizeof(ZoneHashNode*)); + + _bucketsCount = newCount; + _bucketsGrow = newCount * 9 / 10; + + _data = newData; +} + +// ============================================================================ +// [asmjit::ZoneHashBase - Ops] +// ============================================================================ + +ZoneHashNode* ZoneHashBase::_put(ZoneHashNode* node) noexcept { + uint32_t hMod = node->_hVal % _bucketsCount; + ZoneHashNode* next = _data[hMod]; + + node->_hashNext = next; + _data[hMod] = node; + + if (++_size >= _bucketsGrow && next) { + uint32_t newCapacity = ZoneHash_getClosestPrime(_bucketsCount); + if (newCapacity != _bucketsCount) + _rehash(newCapacity); + } + + return node; +} + +ZoneHashNode* ZoneHashBase::_del(ZoneHashNode* node) noexcept { + uint32_t hMod = node->_hVal % _bucketsCount; + + ZoneHashNode** pPrev = &_data[hMod]; + ZoneHashNode* p = *pPrev; + + while (p) { + if (p == node) { + *pPrev = p->_hashNext; + return node; + } + + pPrev = &p->_hashNext; + p = *pPrev; + } + + return nullptr; +} + +// ============================================================================ +// [asmjit::Zone - Test] +// ============================================================================ + +#if defined(ASMJIT_TEST) +UNIT(base_zonevector) { + Zone zone(8096 - Zone::kZoneOverhead); + ZoneHeap heap(&zone); + + int i; + int kMax = 100000; + + ZoneVector vec; + + INFO("ZoneVector basic tests"); + EXPECT(vec.append(&heap, 0) == kErrorOk); + EXPECT(vec.isEmpty() == false); + EXPECT(vec.getLength() == 1); + EXPECT(vec.getCapacity() >= 1); + EXPECT(vec.indexOf(0) == 0); + EXPECT(vec.indexOf(-11) == Globals::kInvalidIndex); + + vec.clear(); + EXPECT(vec.isEmpty()); + EXPECT(vec.getLength() == 0); + EXPECT(vec.indexOf(0) == Globals::kInvalidIndex); + + for (i = 0; i < kMax; i++) { + EXPECT(vec.append(&heap, i) == kErrorOk); + } + EXPECT(vec.isEmpty() == false); + EXPECT(vec.getLength() == static_cast(kMax)); + EXPECT(vec.indexOf(kMax - 1) == static_cast(kMax - 1)); +} + +UNIT(base_ZoneBitVector) { + Zone zone(8096 - Zone::kZoneOverhead); + ZoneHeap heap(&zone); + + size_t i, count; + size_t kMaxCount = 100; + + ZoneBitVector vec; + EXPECT(vec.isEmpty()); + EXPECT(vec.getLength() == 0); + + INFO("ZoneBitVector::resize()"); + for (count = 1; count < kMaxCount; count++) { + vec.clear(); + EXPECT(vec.resize(&heap, count, false) == kErrorOk); + EXPECT(vec.getLength() == count); + + for (i = 0; i < count; i++) + EXPECT(vec.getAt(i) == false); + + vec.clear(); + EXPECT(vec.resize(&heap, count, true) == kErrorOk); + EXPECT(vec.getLength() == count); + + for (i = 0; i < count; i++) + EXPECT(vec.getAt(i) == true); + } + + INFO("ZoneBitVector::fill()"); + for (count = 1; count < kMaxCount; count += 2) { + vec.clear(); + EXPECT(vec.resize(&heap, count) == kErrorOk); + EXPECT(vec.getLength() == count); + + for (i = 0; i < (count + 1) / 2; i++) { + bool value = static_cast(i & 1); + EXPECT(vec.fill(i, count - i, value) == kErrorOk); + } + + for (i = 0; i < count; i++) { + EXPECT(vec.getAt(i) == static_cast(i & 1)); + } + } +} + +UNIT(base_zonestack) { + Zone zone(8096 - Zone::kZoneOverhead); + ZoneHeap heap(&zone); + ZoneStack stack; + + INFO("ZoneStack contains %d elements per one Block", ZoneStack::kNumBlockItems); + + EXPECT(stack.init(&heap) == kErrorOk); + EXPECT(stack.isEmpty(), "Stack must be empty after `init()`"); + + EXPECT(stack.append(42) == kErrorOk); + EXPECT(!stack.isEmpty() , "Stack must not be empty after an item has been appended"); + EXPECT(stack.pop() == 42, "Stack.pop() must return the item that has been appended last"); + EXPECT(stack.isEmpty() , "Stack must be empty after the last element has been removed"); + + EXPECT(stack.prepend(43) == kErrorOk); + EXPECT(!stack.isEmpty() , "Stack must not be empty after an item has been prepended"); + EXPECT(stack.popFirst() == 43, "Stack.popFirst() must return the item that has been prepended last"); + EXPECT(stack.isEmpty() , "Stack must be empty after the last element has been removed"); + + int i; + int iMin =-100; + int iMax = 100000; + + INFO("Adding items from %d to %d to the stack", iMin, iMax); + for (i = 1; i <= iMax; i++) stack.append(i); + for (i = 0; i >= iMin; i--) stack.prepend(i); + + INFO("Validating popFirst()"); + for (i = iMin; i <= iMax; i++) { + int item = stack.popFirst(); + EXPECT(i == item, "Item '%d' didn't match the item '%d' popped", i, item); + } + EXPECT(stack.isEmpty()); + + INFO("Adding items from %d to %d to the stack", iMin, iMax); + for (i = 0; i >= iMin; i--) stack.prepend(i); + for (i = 1; i <= iMax; i++) stack.append(i); + + INFO("Validating pop()"); + for (i = iMax; i >= iMin; i--) { + int item = stack.pop(); + EXPECT(i == item, "Item '%d' didn't match the item '%d' popped", i, item); + } + EXPECT(stack.isEmpty()); +} +#endif // ASMJIT_TEST + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" diff --git a/libraries/asmjit/asmjit/base/zone.h b/libraries/asmjit/asmjit/base/zone.h new file mode 100644 index 00000000000..5a461a29cf0 --- /dev/null +++ b/libraries/asmjit/asmjit/base/zone.h @@ -0,0 +1,1329 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Guard] +#ifndef _ASMJIT_BASE_ZONE_H +#define _ASMJIT_BASE_ZONE_H + +// [Dependencies] +#include "../base/utils.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +//! \addtogroup asmjit_base +//! \{ + +// ============================================================================ +// [asmjit::Zone] +// ============================================================================ + +//! Memory zone. +//! +//! Zone is an incremental memory allocator that allocates memory by simply +//! incrementing a pointer. It allocates blocks of memory by using standard +//! C `malloc`, but divides these blocks into smaller segments requested by +//! calling `Zone::alloc()` and friends. +//! +//! Zone has no function to release the allocated memory. It has to be released +//! all at once by calling `reset()`. If you need a more friendly allocator that +//! also supports `release()`, consider using \ref Zone with \ref ZoneHeap. +class Zone { +public: + //! \internal + //! + //! A single block of memory. + struct Block { + Block* prev; //!< Link to the previous block. + Block* next; //!< Link to the next block. + size_t size; //!< Size of the block. + uint8_t data[sizeof(void*)]; //!< Data. + }; + + enum { + //! Zone allocator overhead. + kZoneOverhead = Globals::kAllocOverhead + static_cast(sizeof(Block)) + }; + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a new instance of `Zone` allocator. + //! + //! The `blockSize` parameter describes the default size of the block. If the + //! `size` parameter passed to `alloc()` is greater than the default size + //! `Zone` will allocate and use a larger block, but it will not change the + //! default `blockSize`. + //! + //! It's not required, but it's good practice to set `blockSize` to a + //! reasonable value that depends on the usage of `Zone`. Greater block sizes + //! are generally safer and perform better than unreasonably low values. + ASMJIT_API Zone(uint32_t blockSize, uint32_t blockAlignment = 0) noexcept; + + //! Destroy the `Zone` instance. + //! + //! This will destroy the `Zone` instance and release all blocks of memory + //! allocated by it. It performs implicit `reset(true)`. + ASMJIT_API ~Zone() noexcept; + + // -------------------------------------------------------------------------- + // [Reset] + // -------------------------------------------------------------------------- + + //! Reset the `Zone` invalidating all blocks allocated. + //! + //! If `releaseMemory` is true all buffers will be released to the system. + ASMJIT_API void reset(bool releaseMemory = false) noexcept; + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get the default block size. + ASMJIT_INLINE uint32_t getBlockSize() const noexcept { return _blockSize; } + //! Get the default block alignment. + ASMJIT_INLINE uint32_t getBlockAlignment() const noexcept { return (uint32_t)1 << _blockAlignmentShift; } + //! Get remaining size of the current block. + ASMJIT_INLINE size_t getRemainingSize() const noexcept { return (size_t)(_end - _ptr); } + + //! Get the current zone cursor (dangerous). + //! + //! This is a function that can be used to get exclusive access to the current + //! block's memory buffer. + ASMJIT_INLINE uint8_t* getCursor() noexcept { return _ptr; } + //! Get the end of the current zone block, only useful if you use `getCursor()`. + ASMJIT_INLINE uint8_t* getEnd() noexcept { return _end; } + + //! Set the current zone cursor to `p` (must match the current block). + //! + //! This is a counterpart of `getZoneCursor()`. + ASMJIT_INLINE void setCursor(uint8_t* p) noexcept { + ASMJIT_ASSERT(p >= _ptr && p <= _end); + _ptr = p; + } + + // -------------------------------------------------------------------------- + // [Alloc] + // -------------------------------------------------------------------------- + + //! Allocate `size` bytes of memory. + //! + //! Pointer returned is valid until the `Zone` instance is destroyed or reset + //! by calling `reset()`. If you plan to make an instance of C++ from the + //! given pointer use placement `new` and `delete` operators: + //! + //! ~~~ + //! using namespace asmjit; + //! + //! class Object { ... }; + //! + //! // Create Zone with default block size of approximately 65536 bytes. + //! Zone zone(65536 - Zone::kZoneOverhead); + //! + //! // Create your objects using zone object allocating, for example: + //! Object* obj = static_cast( zone.alloc(sizeof(Object)) ); + // + //! if (!obj) { + //! // Handle out of memory error. + //! } + //! + //! // Placement `new` and `delete` operators can be used to instantiate it. + //! new(obj) Object(); + //! + //! // ... lifetime of your objects ... + //! + //! // To destroy the instance (if required). + //! obj->~Object(); + //! + //! // Reset or destroy `Zone`. + //! zone.reset(); + //! ~~~ + ASMJIT_INLINE void* alloc(size_t size) noexcept { + uint8_t* ptr = _ptr; + size_t remainingBytes = (size_t)(_end - ptr); + + if (ASMJIT_UNLIKELY(remainingBytes < size)) + return _alloc(size); + + _ptr += size; + ASMJIT_ASSERT(_ptr <= _end); + + return static_cast(ptr); + } + + //! Allocate `size` bytes without any checks. + //! + //! Can only be called if `getRemainingSize()` returns size at least equal + //! to `size`. + ASMJIT_INLINE void* allocNoCheck(size_t size) noexcept { + ASMJIT_ASSERT((size_t)(_end - _ptr) >= size); + + uint8_t* ptr = _ptr; + _ptr += size; + return static_cast(ptr); + } + + //! Allocate `size` bytes of zeroed memory. + //! + //! See \ref alloc() for more details. + ASMJIT_API void* allocZeroed(size_t size) noexcept; + + //! Like `alloc()`, but the return pointer is casted to `T*`. + template + ASMJIT_INLINE T* allocT(size_t size = sizeof(T)) noexcept { + return static_cast(alloc(size)); + } + + //! Like `allocNoCheck()`, but the return pointer is casted to `T*`. + template + ASMJIT_INLINE T* allocNoCheckT(size_t size = sizeof(T)) noexcept { + return static_cast(allocNoCheck(size)); + } + + //! Like `allocZeroed()`, but the return pointer is casted to `T*`. + template + ASMJIT_INLINE T* allocZeroedT(size_t size = sizeof(T)) noexcept { + return static_cast(allocZeroed(size)); + } + + //! Like `new(std::nothrow) T(...)`, but allocated by `Zone`. + template + ASMJIT_INLINE T* newT() noexcept { + void* p = alloc(sizeof(T)); + if (ASMJIT_UNLIKELY(!p)) + return nullptr; + return new(p) T(); + } + //! Like `new(std::nothrow) T(...)`, but allocated by `Zone`. + template + ASMJIT_INLINE T* newT(P1 p1) noexcept { + void* p = alloc(sizeof(T)); + if (ASMJIT_UNLIKELY(!p)) + return nullptr; + return new(p) T(p1); + } + + //! \internal + ASMJIT_API void* _alloc(size_t size) noexcept; + + //! Helper to duplicate data. + ASMJIT_API void* dup(const void* data, size_t size, bool nullTerminate = false) noexcept; + + //! Helper to duplicate formatted string, maximum length is 256 bytes. + ASMJIT_API char* sformat(const char* str, ...) noexcept; + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + uint8_t* _ptr; //!< Pointer in the current block's buffer. + uint8_t* _end; //!< End of the current block's buffer. + Block* _block; //!< Current block. + +#if ASMJIT_ARCH_64BIT + uint32_t _blockSize; //!< Default size of a newly allocated block. + uint32_t _blockAlignmentShift; //!< Minimum alignment of each block. +#else + uint32_t _blockSize : 29; //!< Default size of a newly allocated block. + uint32_t _blockAlignmentShift : 3; //!< Minimum alignment of each block. +#endif +}; + +// ============================================================================ +// [asmjit::ZoneHeap] +// ============================================================================ + +//! Zone-based memory allocator that uses an existing \ref Zone and provides +//! a `release()` functionality on top of it. It uses \ref Zone only for chunks +//! that can be pooled, and uses libc `malloc()` for chunks that are large. +//! +//! The advantage of ZoneHeap is that it can allocate small chunks of memory +//! really fast, and these chunks, when released, will be reused by consecutive +//! calls to `alloc()`. Also, since ZoneHeap uses \ref Zone, you can turn any +//! \ref Zone into a \ref ZoneHeap, and use it in your \ref Pass when necessary. +//! +//! ZoneHeap is used by AsmJit containers to make containers having only +//! few elements fast (and lightweight) and to allow them to grow and use +//! dynamic blocks when require more storage. +class ZoneHeap { + ASMJIT_NONCOPYABLE(ZoneHeap) + + enum { + // In short, we pool chunks of these sizes: + // [32, 64, 96, 128, 192, 256, 320, 384, 448, 512] + + //! How many bytes per a low granularity pool (has to be at least 16). + kLoGranularity = 32, + //! Number of slots of a low granularity pool. + kLoCount = 4, + //! Maximum size of a block that can be allocated in a low granularity pool. + kLoMaxSize = kLoGranularity * kLoCount, + + //! How many bytes per a high granularity pool. + kHiGranularity = 64, + //! Number of slots of a high granularity pool. + kHiCount = 6, + //! Maximum size of a block that can be allocated in a high granularity pool. + kHiMaxSize = kLoMaxSize + kHiGranularity * kHiCount, + + //! Alignment of every pointer returned by `alloc()`. + kBlockAlignment = kLoGranularity + }; + + //! Single-linked list used to store unused chunks. + struct Slot { + //! Link to a next slot in a single-linked list. + Slot* next; + }; + + //! A block of memory that has been allocated dynamically and is not part of + //! block-list used by the allocator. This is used to keep track of all these + //! blocks so they can be freed by `reset()` if not freed explicitly. + struct DynamicBlock { + DynamicBlock* prev; + DynamicBlock* next; + }; + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a new `ZoneHeap`. + //! + //! NOTE: To use it, you must first `init()` it. + ASMJIT_INLINE ZoneHeap() noexcept { + ::memset(this, 0, sizeof(*this)); + } + //! Create a new `ZoneHeap` initialized to use `zone`. + explicit ASMJIT_INLINE ZoneHeap(Zone* zone) noexcept { + ::memset(this, 0, sizeof(*this)); + _zone = zone; + } + //! Destroy the `ZoneHeap`. + ASMJIT_INLINE ~ZoneHeap() noexcept { reset(); } + + // -------------------------------------------------------------------------- + // [Init / Reset] + // -------------------------------------------------------------------------- + + //! Get if the `ZoneHeap` is initialized (i.e. has `Zone`). + ASMJIT_INLINE bool isInitialized() const noexcept { return _zone != nullptr; } + + //! Convenience method to initialize the `ZoneHeap` with `zone`. + //! + //! It's the same as calling `reset(zone)`. + ASMJIT_INLINE void init(Zone* zone) noexcept { reset(zone); } + + //! Reset this `ZoneHeap` and also forget about the current `Zone` which + //! is attached (if any). Reset optionally attaches a new `zone` passed, or + //! keeps the `ZoneHeap` in an uninitialized state, if `zone` is null. + ASMJIT_API void reset(Zone* zone = nullptr) noexcept; + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get the `Zone` the `ZoneHeap` is using, or null if it's not initialized. + ASMJIT_INLINE Zone* getZone() const noexcept { return _zone; } + + // -------------------------------------------------------------------------- + // [Utilities] + // -------------------------------------------------------------------------- + + //! \internal + //! + //! Get the slot index to be used for `size`. Returns `true` if a valid slot + //! has been written to `slot` and `allocatedSize` has been filled with slot + //! exact size (`allocatedSize` can be equal or slightly greater than `size`). + static ASMJIT_INLINE bool _getSlotIndex(size_t size, uint32_t& slot) noexcept { + ASMJIT_ASSERT(size > 0); + if (size > kHiMaxSize) + return false; + + if (size <= kLoMaxSize) + slot = static_cast((size - 1) / kLoGranularity); + else + slot = static_cast((size - kLoMaxSize - 1) / kHiGranularity) + kLoCount; + + return true; + } + + //! \overload + static ASMJIT_INLINE bool _getSlotIndex(size_t size, uint32_t& slot, size_t& allocatedSize) noexcept { + ASMJIT_ASSERT(size > 0); + if (size > kHiMaxSize) + return false; + + if (size <= kLoMaxSize) { + slot = static_cast((size - 1) / kLoGranularity); + allocatedSize = Utils::alignTo(size, kLoGranularity); + } + else { + slot = static_cast((size - kLoMaxSize - 1) / kHiGranularity) + kLoCount; + allocatedSize = Utils::alignTo(size, kHiGranularity); + } + + return true; + } + + // -------------------------------------------------------------------------- + // [Alloc / Release] + // -------------------------------------------------------------------------- + + ASMJIT_API void* _alloc(size_t size, size_t& allocatedSize) noexcept; + ASMJIT_API void* _allocZeroed(size_t size, size_t& allocatedSize) noexcept; + ASMJIT_API void _releaseDynamic(void* p, size_t size) noexcept; + + //! Allocate `size` bytes of memory, ideally from an available pool. + //! + //! NOTE: `size` can't be zero, it will assert in debug mode in such case. + ASMJIT_INLINE void* alloc(size_t size) noexcept { + ASMJIT_ASSERT(isInitialized()); + size_t allocatedSize; + return _alloc(size, allocatedSize); + } + + //! Like `alloc(size)`, but provides a second argument `allocatedSize` that + //! provides a way to know how big the block returned actually is. This is + //! useful for containers to prevent growing too early. + ASMJIT_INLINE void* alloc(size_t size, size_t& allocatedSize) noexcept { + ASMJIT_ASSERT(isInitialized()); + return _alloc(size, allocatedSize); + } + + //! Like `alloc()`, but the return pointer is casted to `T*`. + template + ASMJIT_INLINE T* allocT(size_t size = sizeof(T)) noexcept { + return static_cast(alloc(size)); + } + + //! Like `alloc(size)`, but returns zeroed memory. + ASMJIT_INLINE void* allocZeroed(size_t size) noexcept { + ASMJIT_ASSERT(isInitialized()); + + size_t allocatedSize; + return _allocZeroed(size, allocatedSize); + } + + //! Like `alloc(size, allocatedSize)`, but returns zeroed memory. + ASMJIT_INLINE void* allocZeroed(size_t size, size_t& allocatedSize) noexcept { + ASMJIT_ASSERT(isInitialized()); + + return _allocZeroed(size, allocatedSize); + } + + //! Like `allocZeroed()`, but the return pointer is casted to `T*`. + template + ASMJIT_INLINE T* allocZeroedT(size_t size = sizeof(T)) noexcept { + return static_cast(allocZeroed(size)); + } + + //! Release the memory previously allocated by `alloc()`. The `size` argument + //! has to be the same as used to call `alloc()` or `allocatedSize` returned + //! by `alloc()`. + ASMJIT_INLINE void release(void* p, size_t size) noexcept { + ASMJIT_ASSERT(isInitialized()); + + ASMJIT_ASSERT(p != nullptr); + ASMJIT_ASSERT(size != 0); + + uint32_t slot; + if (_getSlotIndex(size, slot)) { + //printf("RELEASING %p of size %d (SLOT %u)\n", p, int(size), slot); + static_cast(p)->next = static_cast(_slots[slot]); + _slots[slot] = static_cast(p); + } + else { + _releaseDynamic(p, size); + } + } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + Zone* _zone; //!< Zone used to allocate memory that fits into slots. + Slot* _slots[kLoCount + kHiCount]; //!< Indexed slots containing released memory. + DynamicBlock* _dynamicBlocks; //!< Dynamic blocks for larger allocations (no slots). +}; + +// ============================================================================ +// [asmjit::ZoneList] +// ============================================================================ + +//! \internal +template +class ZoneList { +public: + ASMJIT_NONCOPYABLE(ZoneList) + + // -------------------------------------------------------------------------- + // [Link] + // -------------------------------------------------------------------------- + + //! ZoneList node. + struct Link { + //! Get next node. + ASMJIT_INLINE Link* getNext() const noexcept { return _next; } + //! Get value. + ASMJIT_INLINE T getValue() const noexcept { return _value; } + //! Set value to `value`. + ASMJIT_INLINE void setValue(const T& value) noexcept { _value = value; } + + Link* _next; + T _value; + }; + + // -------------------------------------------------------------------------- + // [Appender] + // -------------------------------------------------------------------------- + + //! Specialized appender that takes advantage of ZoneList structure. You must + //! initialize it and then call done(). + struct Appender { + ASMJIT_INLINE Appender(ZoneList& list) noexcept { init(list); } + + ASMJIT_INLINE void init(ZoneList& list) noexcept { + pPrev = &list._first; + } + + ASMJIT_INLINE void done(ZoneList& list) noexcept { + list._last = *pPrev; + *pPrev = nullptr; + } + + ASMJIT_INLINE void append(Link* node) noexcept { + *pPrev = node; + pPrev = &node->_next; + } + + Link** pPrev; + }; + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE ZoneList() noexcept : _first(nullptr), _last(nullptr) {} + ASMJIT_INLINE ~ZoneList() noexcept {} + + // -------------------------------------------------------------------------- + // [Data] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE bool isEmpty() const noexcept { return _first != nullptr; } + ASMJIT_INLINE Link* getFirst() const noexcept { return _first; } + ASMJIT_INLINE Link* getLast() const noexcept { return _last; } + + // -------------------------------------------------------------------------- + // [Ops] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE void reset() noexcept { + _first = nullptr; + _last = nullptr; + } + + ASMJIT_INLINE void prepend(Link* link) noexcept { + link->_next = _first; + if (!_first) _last = link; + _first = link; + } + + ASMJIT_INLINE void append(Link* link) noexcept { + link->_next = nullptr; + if (!_first) + _first = link; + else + _last->_next = link; + _last = link; + } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + Link* _first; + Link* _last; +}; + +// ============================================================================ +// [asmjit::ZoneVectorBase] +// ============================================================================ + +//! \internal +class ZoneVectorBase { +public: + ASMJIT_NONCOPYABLE(ZoneVectorBase) + +protected: + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a new instance of `ZoneVectorBase`. + explicit ASMJIT_INLINE ZoneVectorBase() noexcept + : _data(nullptr), + _length(0), + _capacity(0) {} + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + +public: + //! Get if the vector is empty. + ASMJIT_INLINE bool isEmpty() const noexcept { return _length == 0; } + //! Get vector length. + ASMJIT_INLINE size_t getLength() const noexcept { return _length; } + //! Get vector capacity. + ASMJIT_INLINE size_t getCapacity() const noexcept { return _capacity; } + + // -------------------------------------------------------------------------- + // [Ops] + // -------------------------------------------------------------------------- + + //! Makes the vector empty (won't change the capacity or data pointer). + ASMJIT_INLINE void clear() noexcept { _length = 0; } + //! Reset the vector data and set its `length` to zero. + ASMJIT_INLINE void reset() noexcept { + _data = nullptr; + _length = 0; + _capacity = 0; + } + + //! Truncate the vector to at most `n` items. + ASMJIT_INLINE void truncate(size_t n) noexcept { + _length = std::min(_length, n); + } + + // -------------------------------------------------------------------------- + // [Memory Management] + // -------------------------------------------------------------------------- + +protected: + ASMJIT_INLINE void _release(ZoneHeap* heap, size_t sizeOfT) noexcept { + if (_data != nullptr) { + heap->release(_data, _capacity * sizeOfT); + reset(); + } + } + + ASMJIT_API Error _grow(ZoneHeap* heap, size_t sizeOfT, size_t n) noexcept; + ASMJIT_API Error _resize(ZoneHeap* heap, size_t sizeOfT, size_t n) noexcept; + ASMJIT_API Error _reserve(ZoneHeap* heap, size_t sizeOfT, size_t n) noexcept; + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + +public: + void* _data; //!< Vector data. + size_t _length; //!< Length of the vector. + size_t _capacity; //!< Capacity of the vector. +}; + +// ============================================================================ +// [asmjit::ZoneVector] +// ============================================================================ + +//! Template used to store and manage array of Zone allocated data. +//! +//! This template has these advantages over other std::vector<>: +//! - Always non-copyable (designed to be non-copyable, we want it). +//! - No copy-on-write (some implementations of STL can use it). +//! - Optimized for working only with POD types. +//! - Uses ZoneHeap, thus small vectors are basically for free. +template +class ZoneVector : public ZoneVectorBase { +public: + ASMJIT_NONCOPYABLE(ZoneVector) + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a new instance of `ZoneVector`. + explicit ASMJIT_INLINE ZoneVector() noexcept : ZoneVectorBase() {} + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get data. + ASMJIT_INLINE T* getData() noexcept { return static_cast(_data); } + //! \overload + ASMJIT_INLINE const T* getData() const noexcept { return static_cast(_data); } + + // -------------------------------------------------------------------------- + // [Ops] + // -------------------------------------------------------------------------- + + //! Prepend `item` to the vector. + Error prepend(ZoneHeap* heap, const T& item) noexcept { + if (ASMJIT_UNLIKELY(_length == _capacity)) + ASMJIT_PROPAGATE(grow(heap, 1)); + + ::memmove(static_cast(_data) + 1, _data, _length * sizeof(T)); + ::memcpy(_data, &item, sizeof(T)); + + _length++; + return kErrorOk; + } + + //! Insert an `item` at the specified `index`. + Error insert(ZoneHeap* heap, size_t index, const T& item) noexcept { + ASMJIT_ASSERT(index <= _length); + + if (ASMJIT_UNLIKELY(_length == _capacity)) + ASMJIT_PROPAGATE(grow(heap, 1)); + + T* dst = static_cast(_data) + index; + ::memmove(dst + 1, dst, _length - index); + ::memcpy(dst, &item, sizeof(T)); + + _length++; + return kErrorOk; + } + + //! Append `item` to the vector. + Error append(ZoneHeap* heap, const T& item) noexcept { + if (ASMJIT_UNLIKELY(_length == _capacity)) + ASMJIT_PROPAGATE(grow(heap, 1)); + + ::memcpy(static_cast(_data) + _length, &item, sizeof(T)); + + _length++; + return kErrorOk; + } + + Error concat(ZoneHeap* heap, const ZoneVector& other) noexcept { + size_t count = other._length; + if (_capacity - _length < count) + ASMJIT_PROPAGATE(grow(heap, count)); + + ::memcpy(static_cast(_data) + _length, other._data, count * sizeof(T)); + + _length += count; + return kErrorOk; + } + + //! Prepend `item` to the vector (unsafe case). + //! + //! Can only be used together with `willGrow()`. If `willGrow(N)` returns + //! `kErrorOk` then N elements can be added to the vector without checking + //! if there is a place for them. Used mostly internally. + ASMJIT_INLINE void prependUnsafe(const T& item) noexcept { + ASMJIT_ASSERT(_length < _capacity); + T* data = static_cast(_data); + + if (_length) + ::memmove(data + 1, data, _length * sizeof(T)); + + ::memcpy(data, &item, sizeof(T)); + _length++; + } + + //! Append `item` to the vector (unsafe case). + //! + //! Can only be used together with `willGrow()`. If `willGrow(N)` returns + //! `kErrorOk` then N elements can be added to the vector without checking + //! if there is a place for them. Used mostly internally. + ASMJIT_INLINE void appendUnsafe(const T& item) noexcept { + ASMJIT_ASSERT(_length < _capacity); + + ::memcpy(static_cast(_data) + _length, &item, sizeof(T)); + _length++; + } + + //! Concatenate all items of `other` at the end of the vector. + ASMJIT_INLINE void concatUnsafe(const ZoneVector& other) noexcept { + size_t count = other._length; + ASMJIT_ASSERT(_capacity - _length >= count); + + ::memcpy(static_cast(_data) + _length, other._data, count * sizeof(T)); + _length += count; + } + + //! Get index of `val` or `kInvalidIndex` if not found. + ASMJIT_INLINE size_t indexOf(const T& val) const noexcept { + const T* data = static_cast(_data); + size_t length = _length; + + for (size_t i = 0; i < length; i++) + if (data[i] == val) + return i; + + return Globals::kInvalidIndex; + } + + //! Get whether the vector contains `val`. + ASMJIT_INLINE bool contains(const T& val) const noexcept { + return indexOf(val) != Globals::kInvalidIndex; + } + + //! Remove item at index `i`. + ASMJIT_INLINE void removeAt(size_t i) noexcept { + ASMJIT_ASSERT(i < _length); + + T* data = static_cast(_data) + i; + _length--; + ::memmove(data, data + 1, _length - i); + } + + //! Swap this pod-vector with `other`. + ASMJIT_INLINE void swap(ZoneVector& other) noexcept { + Utils::swap(_length, other._length); + Utils::swap(_capacity, other._capacity); + Utils::swap(_data, other._data); + } + + //! Get item at index `i` (const). + ASMJIT_INLINE const T& getAt(size_t i) const noexcept { + ASMJIT_ASSERT(i < _length); + return getData()[i]; + } + + //! Get item at index `i`. + ASMJIT_INLINE T& operator[](size_t i) noexcept { + ASMJIT_ASSERT(i < _length); + return getData()[i]; + } + + //! Get item at index `i`. + ASMJIT_INLINE const T& operator[](size_t i) const noexcept { + ASMJIT_ASSERT(i < _length); + return getData()[i]; + } + + // -------------------------------------------------------------------------- + // [Memory Management] + // -------------------------------------------------------------------------- + + //! Release the memory held by `ZoneVector` back to the `heap`. + ASMJIT_INLINE void release(ZoneHeap* heap) noexcept { _release(heap, sizeof(T)); } + + //! Called to grow the buffer to fit at least `n` elements more. + ASMJIT_INLINE Error grow(ZoneHeap* heap, size_t n) noexcept { return ZoneVectorBase::_grow(heap, sizeof(T), n); } + + //! Resize the vector to hold `n` elements. + //! + //! If `n` is greater than the current length then the additional elements' + //! content will be initialized to zero. If `n` is less than the current + //! length then the vector will be truncated to exactly `n` elements. + ASMJIT_INLINE Error resize(ZoneHeap* heap, size_t n) noexcept { return ZoneVectorBase::_resize(heap, sizeof(T), n); } + + //! Realloc internal array to fit at least `n` items. + ASMJIT_INLINE Error reserve(ZoneHeap* heap, size_t n) noexcept { return ZoneVectorBase::_reserve(heap, sizeof(T), n); } + + ASMJIT_INLINE Error willGrow(ZoneHeap* heap, size_t n = 1) noexcept { + return _capacity - _length < n ? grow(heap, n) : static_cast(kErrorOk); + } +}; + +// ============================================================================ +// [asmjit::ZoneBitVector] +// ============================================================================ + +class ZoneBitVector { +public: + ASMJIT_NONCOPYABLE(ZoneBitVector) + + //! Storage used to store a pack of bits (should by compatible with a machine word). + typedef uintptr_t BitWord; + enum { kBitsPerWord = static_cast(sizeof(BitWord)) * 8 }; + + static ASMJIT_INLINE size_t _wordsPerBits(size_t nBits) noexcept { + return ((nBits + kBitsPerWord) / kBitsPerWord) - 1; + } + + // Return all bits zero if 0 and all bits set if 1. + static ASMJIT_INLINE BitWord _patternFromBit(bool bit) noexcept { + BitWord bitAsWord = static_cast(bit); + ASMJIT_ASSERT(bitAsWord == 0 || bitAsWord == 1); + return static_cast(0) - bitAsWord; + } + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + explicit ASMJIT_INLINE ZoneBitVector() noexcept : + _data(nullptr), + _length(0), + _capacity(0) {} + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get if the bit-vector is empty (has no bits). + ASMJIT_INLINE bool isEmpty() const noexcept { return _length == 0; } + //! Get a length of this bit-vector (in bits). + ASMJIT_INLINE size_t getLength() const noexcept { return _length; } + //! Get a capacity of this bit-vector (in bits). + ASMJIT_INLINE size_t getCapacity() const noexcept { return _capacity; } + + //! Get data. + ASMJIT_INLINE BitWord* getData() noexcept { return _data; } + //! \overload + ASMJIT_INLINE const BitWord* getData() const noexcept { return _data; } + + // -------------------------------------------------------------------------- + // [Ops] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE void clear() noexcept { + _length = 0; + } + + ASMJIT_INLINE void reset() noexcept { + _data = nullptr; + _length = 0; + _capacity = 0; + } + + ASMJIT_INLINE void truncate(size_t newLength) noexcept { + _length = std::min(_length, newLength); + _clearUnusedBits(); + } + + ASMJIT_INLINE bool getAt(size_t index) const noexcept { + ASMJIT_ASSERT(index < _length); + + size_t idx = index / kBitsPerWord; + size_t bit = index % kBitsPerWord; + return static_cast((_data[idx] >> bit) & 1); + } + + ASMJIT_INLINE void setAt(size_t index, bool value) noexcept { + ASMJIT_ASSERT(index < _length); + + size_t idx = index / kBitsPerWord; + size_t bit = index % kBitsPerWord; + if (value) + _data[idx] |= static_cast(1) << bit; + else + _data[idx] &= ~(static_cast(1) << bit); + } + + ASMJIT_INLINE void toggleAt(size_t index) noexcept { + ASMJIT_ASSERT(index < _length); + + size_t idx = index / kBitsPerWord; + size_t bit = index % kBitsPerWord; + _data[idx] ^= static_cast(1) << bit; + } + + ASMJIT_INLINE Error append(ZoneHeap* heap, bool value) noexcept { + size_t index = _length; + if (ASMJIT_UNLIKELY(index >= _capacity)) + return _append(heap, value); + + size_t idx = index / kBitsPerWord; + size_t bit = index % kBitsPerWord; + + if (bit == 0) + _data[idx] = static_cast(value) << bit; + else + _data[idx] |= static_cast(value) << bit; + + _length++; + return kErrorOk; + } + + ASMJIT_API Error fill(size_t fromIndex, size_t toIndex, bool value) noexcept; + + ASMJIT_INLINE void and_(const ZoneBitVector& other) noexcept { + BitWord* dst = _data; + const BitWord* src = other._data; + + size_t numWords = (std::min(_length, other._length) + kBitsPerWord - 1) / kBitsPerWord; + for (size_t i = 0; i < numWords; i++) + dst[i] = dst[i] & src[i]; + _clearUnusedBits(); + } + + ASMJIT_INLINE void andNot(const ZoneBitVector& other) noexcept { + BitWord* dst = _data; + const BitWord* src = other._data; + + size_t numWords = (std::min(_length, other._length) + kBitsPerWord - 1) / kBitsPerWord; + for (size_t i = 0; i < numWords; i++) + dst[i] = dst[i] & ~src[i]; + _clearUnusedBits(); + } + + ASMJIT_INLINE void or_(const ZoneBitVector& other) noexcept { + BitWord* dst = _data; + const BitWord* src = other._data; + + size_t numWords = (std::min(_length, other._length) + kBitsPerWord - 1) / kBitsPerWord; + for (size_t i = 0; i < numWords; i++) + dst[i] = dst[i] | src[i]; + _clearUnusedBits(); + } + + ASMJIT_INLINE void _clearUnusedBits() noexcept { + size_t idx = _length / kBitsPerWord; + size_t bit = _length % kBitsPerWord; + + if (!bit) return; + _data[idx] &= (static_cast(1) << bit) - 1U; + } + + // -------------------------------------------------------------------------- + // [Memory Management] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE void release(ZoneHeap* heap) noexcept { + if (_data != nullptr) { + heap->release(_data, _capacity / 8); + reset(); + } + } + + ASMJIT_INLINE Error resize(ZoneHeap* heap, size_t newLength, bool newBitsValue = false) noexcept { + return _resize(heap, newLength, newLength, newBitsValue); + } + + ASMJIT_API Error _resize(ZoneHeap* heap, size_t newLength, size_t idealCapacity, bool newBitsValue) noexcept; + ASMJIT_API Error _append(ZoneHeap* heap, bool value) noexcept; + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + BitWord* _data; //!< Bits. + size_t _length; //!< Length of the bit-vector (in bits). + size_t _capacity; //!< Capacity of the bit-vector (in bits). +}; + +// ============================================================================ +// [asmjit::ZoneStackBase] +// ============================================================================ + +class ZoneStackBase { +public: + enum Side { + kSideLeft = 0, + kSideRight = 1 + }; + + enum { + kBlockSize = ZoneHeap::kHiMaxSize + }; + + struct Block { + ASMJIT_INLINE Block* getPrev() const noexcept { return _link[kSideLeft]; } + ASMJIT_INLINE void setPrev(Block* block) noexcept { _link[kSideLeft] = block; } + + ASMJIT_INLINE Block* getNext() const noexcept { return _link[kSideRight]; } + ASMJIT_INLINE void setNext(Block* block) noexcept { _link[kSideRight] = block; } + + template + ASMJIT_INLINE T* getStart() const noexcept { return static_cast(_start); } + template + ASMJIT_INLINE void setStart(T* start) noexcept { _start = static_cast(start); } + + template + ASMJIT_INLINE T* getEnd() const noexcept { return static_cast(_end); } + template + ASMJIT_INLINE void setEnd(T* end) noexcept { _end = static_cast(end); } + + ASMJIT_INLINE bool isEmpty() const noexcept { return _start == _end; } + + template + ASMJIT_INLINE T* getData() const noexcept { + return static_cast(static_cast((uint8_t*)this + sizeof(Block))); + } + + template + ASMJIT_INLINE bool canPrepend() const noexcept { + return _start > getData(); + } + + template + ASMJIT_INLINE bool canAppend() const noexcept { + size_t kNumBlockItems = (kBlockSize - sizeof(Block)) / sizeof(T); + size_t kBlockEnd = sizeof(Block) + kNumBlockItems * sizeof(T); + return (uintptr_t)_end - (uintptr_t)this < kBlockEnd; + } + + Block* _link[2]; //!< Next and previous blocks. + void* _start; //!< Pointer to the start of the array. + void* _end; //!< Pointer to the end of the array. + }; + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE ZoneStackBase() noexcept { + _heap = nullptr; + _block[0] = nullptr; + _block[1] = nullptr; + } + ASMJIT_INLINE ~ZoneStackBase() noexcept { reset(); } + + // -------------------------------------------------------------------------- + // [Init / Reset] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE bool isInitialized() const noexcept { return _heap != nullptr; } + ASMJIT_API Error _init(ZoneHeap* heap, size_t middleIndex) noexcept; + ASMJIT_INLINE Error reset() noexcept { return _init(nullptr, 0); } + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get a `ZoneHeap` attached to this container. + ASMJIT_INLINE ZoneHeap* getHeap() const noexcept { return _heap; } + + ASMJIT_INLINE bool isEmpty() const noexcept { + ASMJIT_ASSERT(isInitialized()); + return _block[0] == _block[1] && _block[0]->isEmpty(); + } + + // -------------------------------------------------------------------------- + // [Ops] + // -------------------------------------------------------------------------- + + ASMJIT_API Error _prepareBlock(uint32_t side, size_t initialIndex) noexcept; + ASMJIT_API void _cleanupBlock(uint32_t side, size_t middleIndex) noexcept; + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + ZoneHeap* _heap; //!< ZoneHeap used to allocate data. + Block* _block[2]; //!< First and last blocks. +}; + +// ============================================================================ +// [asmjit::ZoneStack] +// ============================================================================ + +template +class ZoneStack : public ZoneStackBase { +public: + enum { + kNumBlockItems = static_cast((kBlockSize - sizeof(Block)) / sizeof(T)), + kStartBlockIndex = static_cast(sizeof(Block)), + kMidBlockIndex = static_cast(kStartBlockIndex + (kNumBlockItems / 2) * sizeof(T)), + kEndBlockIndex = static_cast(kStartBlockIndex + kNumBlockItems * sizeof(T)) + }; + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE ZoneStack() noexcept {} + ASMJIT_INLINE ~ZoneStack() noexcept {} + + // -------------------------------------------------------------------------- + // [Init / Reset] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE Error init(ZoneHeap* heap) noexcept { return _init(heap, kMidBlockIndex); } + + // -------------------------------------------------------------------------- + // [Ops] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE Error prepend(T item) noexcept { + ASMJIT_ASSERT(isInitialized()); + Block* block = _block[kSideLeft]; + + if (!block->canPrepend()) { + ASMJIT_PROPAGATE(_prepareBlock(kSideLeft, kEndBlockIndex)); + block = _block[kSideLeft]; + } + + T* ptr = block->getStart() - 1; + ASMJIT_ASSERT(ptr >= block->getData() && ptr < block->getData() + kNumBlockItems); + *ptr = item; + block->setStart(ptr); + return kErrorOk; + } + + ASMJIT_INLINE Error append(T item) noexcept { + ASMJIT_ASSERT(isInitialized()); + Block* block = _block[kSideRight]; + + if (!block->canAppend()) { + ASMJIT_PROPAGATE(_prepareBlock(kSideRight, kStartBlockIndex)); + block = _block[kSideRight]; + } + + T* ptr = block->getEnd(); + ASMJIT_ASSERT(ptr >= block->getData() && ptr < block->getData() + kNumBlockItems); + + *ptr++ = item; + block->setEnd(ptr); + return kErrorOk; + } + + ASMJIT_INLINE T popFirst() noexcept { + ASMJIT_ASSERT(isInitialized()); + ASMJIT_ASSERT(!isEmpty()); + + Block* block = _block[kSideLeft]; + ASMJIT_ASSERT(!block->isEmpty()); + + T* ptr = block->getStart(); + T item = *ptr++; + + block->setStart(ptr); + if (block->isEmpty()) + _cleanupBlock(kSideLeft, kMidBlockIndex); + + return item; + } + + ASMJIT_INLINE T pop() noexcept { + ASMJIT_ASSERT(isInitialized()); + ASMJIT_ASSERT(!isEmpty()); + + Block* block = _block[kSideRight]; + ASMJIT_ASSERT(!block->isEmpty()); + + T* ptr = block->getEnd(); + T item = *--ptr; + + block->setEnd(ptr); + if (block->isEmpty()) + _cleanupBlock(kSideRight, kMidBlockIndex); + + return item; + } +}; + +// ============================================================================ +// [asmjit::ZoneHashNode] +// ============================================================================ + +//! Node used by \ref ZoneHash<> template. +//! +//! You must provide function `bool eq(const Key& key)` in order to make +//! `ZoneHash::get()` working. +class ZoneHashNode { +public: + ASMJIT_INLINE ZoneHashNode(uint32_t hVal = 0) noexcept + : _hashNext(nullptr), + _hVal(hVal) {} + + //! Next node in the chain, null if it terminates the chain. + ZoneHashNode* _hashNext; + //! Key hash. + uint32_t _hVal; + //! Should be used by Node that inherits ZoneHashNode, it aligns ZoneHashNode. + uint32_t _customData; +}; + +// ============================================================================ +// [asmjit::ZoneHashBase] +// ============================================================================ + +class ZoneHashBase { +public: + ASMJIT_NONCOPYABLE(ZoneHashBase) + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE ZoneHashBase(ZoneHeap* heap) noexcept { + _heap = heap; + _size = 0; + _bucketsCount = 1; + _bucketsGrow = 1; + _data = _embedded; + _embedded[0] = nullptr; + } + ASMJIT_INLINE ~ZoneHashBase() noexcept { reset(nullptr); } + + // -------------------------------------------------------------------------- + // [Reset] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE bool isInitialized() const noexcept { return _heap != nullptr; } + ASMJIT_API void reset(ZoneHeap* heap) noexcept; + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get a `ZoneHeap` attached to this container. + ASMJIT_INLINE ZoneHeap* getHeap() const noexcept { return _heap; } + + ASMJIT_INLINE size_t getSize() const noexcept { return _size; } + + // -------------------------------------------------------------------------- + // [Ops] + // -------------------------------------------------------------------------- + + ASMJIT_API void _rehash(uint32_t newCount) noexcept; + ASMJIT_API ZoneHashNode* _put(ZoneHashNode* node) noexcept; + ASMJIT_API ZoneHashNode* _del(ZoneHashNode* node) noexcept; + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + ZoneHeap* _heap; //!< ZoneHeap used to allocate data. + size_t _size; //!< Count of records inserted into the hash table. + uint32_t _bucketsCount; //!< Count of hash buckets. + uint32_t _bucketsGrow; //!< When buckets array should grow. + + ZoneHashNode** _data; //!< Buckets data. + ZoneHashNode* _embedded[1]; //!< Embedded data, used by empty hash tables. +}; + +// ============================================================================ +// [asmjit::ZoneHash] +// ============================================================================ + +//! Low-level hash table specialized for storing string keys and POD values. +//! +//! This hash table allows duplicates to be inserted (the API is so low +//! level that it's up to you if you allow it or not, as you should first +//! `get()` the node and then modify it or insert a new node by using `put()`, +//! depending on the intention). +template +class ZoneHash : public ZoneHashBase { +public: + explicit ASMJIT_INLINE ZoneHash(ZoneHeap* heap = nullptr) noexcept + : ZoneHashBase(heap) {} + ASMJIT_INLINE ~ZoneHash() noexcept {} + + template + ASMJIT_INLINE Node* get(const Key& key) const noexcept { + uint32_t hMod = key.hVal % _bucketsCount; + Node* node = static_cast(_data[hMod]); + + while (node && !key.matches(node)) + node = static_cast(node->_hashNext); + return node; + } + + ASMJIT_INLINE Node* put(Node* node) noexcept { return static_cast(_put(node)); } + ASMJIT_INLINE Node* del(Node* node) noexcept { return static_cast(_del(node)); } +}; + +//! \} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" + +// [Guard] +#endif // _ASMJIT_BASE_ZONE_H diff --git a/libraries/asmjit/asmjit/core.h b/libraries/asmjit/asmjit/core.h deleted file mode 100644 index 4aaa5791e2d..00000000000 --- a/libraries/asmjit/asmjit/core.h +++ /dev/null @@ -1,85 +0,0 @@ -// [AsmJit] -// Machine Code Generation for C++. -// -// [License] -// Zlib - See LICENSE.md file in the package. - -#ifndef _ASMJIT_CORE_H -#define _ASMJIT_CORE_H - -//! \defgroup asmjit_core Core -//! \brief Core API. -//! -//! API that provides classes and functions not specific to any architecture. - -//! \defgroup asmjit_builder Builder -//! \brief Builder API. -//! -//! Both Builder and Compiler are emitters that emit everything to a representation -//! that allows further processing. The code stored in such representation is -//! completely safe to be patched, simplified, reordered, obfuscated, removed, -//! injected, analyzed, or processed some other way. Each instruction, label, -//! directive, or other building block is stored as \ref BaseNode (or derived -//! class like \ref InstNode or \ref LabelNode) and contains all the information -//! necessary to pass that node later to the Assembler. - -//! \defgroup asmjit_compiler Compiler -//! \brief Compiler API. -//! -//! Compiler tool is built on top of a \ref asmjit_builder API and adds register -//! allocation and support for defining and calling functions into it. At the -//! moment it's the easiest way to generate some code as most architecture and -//! OS specific stuff is properly abstracted, however, abstractions also mean -//! that not everything is possible with the Compiler. - -//! \defgroup asmjit_func Function -//! \brief Function API. - -//! \defgroup asmjit_jit JIT -//! \brief JIT API and Virtual Memory Management. - -//! \defgroup asmjit_zone Zone -//! \brief Zone allocator and zone allocated containers. - -//! \defgroup asmjit_support Support -//! \brief Support API. - -//! \cond INTERNAL -//! \defgroup asmjit_ra RA -//! \brief Register allocator internals. -//! \endcond - -#include "./core/globals.h" - -#include "./core/arch.h" -#include "./core/assembler.h" -#include "./core/builder.h" -#include "./core/callconv.h" -#include "./core/codeholder.h" -#include "./core/compiler.h" -#include "./core/constpool.h" -#include "./core/cpuinfo.h" -#include "./core/datatypes.h" -#include "./core/emitter.h" -#include "./core/features.h" -#include "./core/func.h" -#include "./core/inst.h" -#include "./core/jitallocator.h" -#include "./core/jitruntime.h" -#include "./core/logging.h" -#include "./core/operand.h" -#include "./core/osutils.h" -#include "./core/string.h" -#include "./core/support.h" -#include "./core/target.h" -#include "./core/type.h" -#include "./core/virtmem.h" -#include "./core/zone.h" -#include "./core/zonehash.h" -#include "./core/zonelist.h" -#include "./core/zonetree.h" -#include "./core/zonestack.h" -#include "./core/zonestring.h" -#include "./core/zonevector.h" - -#endif // _ASMJIT_CORE_H diff --git a/libraries/asmjit/asmjit/core/arch.cpp b/libraries/asmjit/asmjit/core/arch.cpp deleted file mode 100644 index 0f94a53792b..00000000000 --- a/libraries/asmjit/asmjit/core/arch.cpp +++ /dev/null @@ -1,160 +0,0 @@ -// [AsmJit] -// Machine Code Generation for C++. -// -// [License] -// Zlib - See LICENSE.md file in the package. - -#define ASMJIT_EXPORTS - -#include "../core/arch.h" -#include "../core/support.h" -#include "../core/type.h" - -#ifdef ASMJIT_BUILD_X86 - #include "../x86/x86operand.h" -#endif - -#ifdef ASMJIT_BUILD_ARM - #include "../arm/armoperand.h" -#endif - -ASMJIT_BEGIN_NAMESPACE - -// ============================================================================ -// [asmjit::ArchInfo] -// ============================================================================ - -// NOTE: Keep `const constexpr` otherwise MSC would not compile this code correctly. -static const constexpr uint32_t archInfoTable[] = { - // <--------------------+---------------------+-------------------+-------+ - // | Type | SubType | GPInfo| - // <--------------------+---------------------+-------------------+-------+ - Support::bytepack32_4x8(ArchInfo::kIdNone , ArchInfo::kSubIdNone, 0, 0), - Support::bytepack32_4x8(ArchInfo::kIdX86 , ArchInfo::kSubIdNone, 4, 8), - Support::bytepack32_4x8(ArchInfo::kIdX64 , ArchInfo::kSubIdNone, 8, 16), - Support::bytepack32_4x8(ArchInfo::kIdA32 , ArchInfo::kSubIdNone, 4, 16), - Support::bytepack32_4x8(ArchInfo::kIdA64 , ArchInfo::kSubIdNone, 8, 32) -}; - -ASMJIT_FAVOR_SIZE void ArchInfo::init(uint32_t id, uint32_t subId) noexcept { - uint32_t index = id < ASMJIT_ARRAY_SIZE(archInfoTable) ? id : uint32_t(0); - - // Make sure the `archInfoTable` array is correctly indexed. - _signature = archInfoTable[index]; - ASMJIT_ASSERT(_id == index); - - // Even if the architecture is not known we setup its id and sub-id, - // however, such architecture is not really useful. - _id = uint8_t(id); - _subId = uint8_t(subId); -} - -// ============================================================================ -// [asmjit::ArchUtils] -// ============================================================================ - -ASMJIT_FAVOR_SIZE Error ArchUtils::typeIdToRegInfo(uint32_t archId, uint32_t& typeIdInOut, RegInfo& regInfo) noexcept { - uint32_t typeId = typeIdInOut; - - // Zero the signature so it's clear in case that typeId is not invalid. - regInfo._signature = 0; - - // TODO: Move to X86 backend. - #ifdef ASMJIT_BUILD_X86 - if (ArchInfo::isX86Family(archId)) { - // Passed RegType instead of TypeId? - if (typeId <= BaseReg::kTypeMax) - typeId = x86::opData.archRegs.regTypeToTypeId[typeId]; - - if (ASMJIT_UNLIKELY(!Type::isValid(typeId))) - return DebugUtils::errored(kErrorInvalidTypeId); - - // First normalize architecture dependent types. - if (Type::isAbstract(typeId)) { - if (typeId == Type::kIdIntPtr) - typeId = (archId == ArchInfo::kIdX86) ? Type::kIdI32 : Type::kIdI64; - else - typeId = (archId == ArchInfo::kIdX86) ? Type::kIdU32 : Type::kIdU64; - } - - // Type size helps to construct all groupss of registers. If the size is zero - // then the TypeId is invalid. - uint32_t size = Type::sizeOf(typeId); - if (ASMJIT_UNLIKELY(!size)) - return DebugUtils::errored(kErrorInvalidTypeId); - - if (ASMJIT_UNLIKELY(typeId == Type::kIdF80)) - return DebugUtils::errored(kErrorInvalidUseOfF80); - - uint32_t regType = 0; - - switch (typeId) { - case Type::kIdI8: - case Type::kIdU8: - regType = x86::Reg::kTypeGpbLo; - break; - - case Type::kIdI16: - case Type::kIdU16: - regType = x86::Reg::kTypeGpw; - break; - - case Type::kIdI32: - case Type::kIdU32: - regType = x86::Reg::kTypeGpd; - break; - - case Type::kIdI64: - case Type::kIdU64: - if (archId == ArchInfo::kIdX86) - return DebugUtils::errored(kErrorInvalidUseOfGpq); - - regType = x86::Reg::kTypeGpq; - break; - - // F32 and F64 are always promoted to use vector registers. - case Type::kIdF32: - typeId = Type::kIdF32x1; - regType = x86::Reg::kTypeXmm; - break; - - case Type::kIdF64: - typeId = Type::kIdF64x1; - regType = x86::Reg::kTypeXmm; - break; - - // Mask registers {k}. - case Type::kIdMask8: - case Type::kIdMask16: - case Type::kIdMask32: - case Type::kIdMask64: - regType = x86::Reg::kTypeKReg; - break; - - // MMX registers. - case Type::kIdMmx32: - case Type::kIdMmx64: - regType = x86::Reg::kTypeMm; - break; - - // XMM|YMM|ZMM registers. - default: - if (size <= 16) - regType = x86::Reg::kTypeXmm; - else if (size == 32) - regType = x86::Reg::kTypeYmm; - else - regType = x86::Reg::kTypeZmm; - break; - } - - typeIdInOut = typeId; - regInfo._signature = x86::opData.archRegs.regInfo[regType].signature(); - return kErrorOk; - } - #endif - - return DebugUtils::errored(kErrorInvalidArch); -} - -ASMJIT_END_NAMESPACE diff --git a/libraries/asmjit/asmjit/core/arch.h b/libraries/asmjit/asmjit/core/arch.h deleted file mode 100644 index fc1bcaa6485..00000000000 --- a/libraries/asmjit/asmjit/core/arch.h +++ /dev/null @@ -1,187 +0,0 @@ -// [AsmJit] -// Machine Code Generation for C++. -// -// [License] -// Zlib - See LICENSE.md file in the package. - -#ifndef _ASMJIT_CORE_ARCH_H -#define _ASMJIT_CORE_ARCH_H - -#include "../core/globals.h" -#include "../core/operand.h" - -ASMJIT_BEGIN_NAMESPACE - -//! \addtogroup asmjit_core -//! \{ - -// ============================================================================ -// [asmjit::ArchInfo] -// ============================================================================ - -class ArchInfo { -public: - union { - struct { - //! Architecture id. - uint8_t _id; - //! Architecture sub-id. - uint8_t _subId; - //! Default size of a general purpose register. - uint8_t _gpSize; - //! Count of all general purpose registers. - uint8_t _gpCount; - }; - //! Architecture signature (32-bit int). - uint32_t _signature; - }; - - //! Architecture id. - enum Id : uint32_t { - kIdNone = 0, //!< No/Unknown architecture. - - // X86 architectures. - kIdX86 = 1, //!< X86 architecture (32-bit). - kIdX64 = 2, //!< X64 architecture (64-bit) (AMD64). - - // ARM architectures. - kIdA32 = 3, //!< ARM 32-bit architecture (AArch32/ARM/THUMB). - kIdA64 = 4, //!< ARM 64-bit architecture (AArch64). - - //! Architecture detected at compile-time (architecture of the host). - kIdHost = ASMJIT_ARCH_X86 == 32 ? kIdX86 : - ASMJIT_ARCH_X86 == 64 ? kIdX64 : - ASMJIT_ARCH_ARM == 32 ? kIdA32 : - ASMJIT_ARCH_ARM == 64 ? kIdA64 : kIdNone - }; - - //! Architecture sub-type or execution mode. - enum SubType : uint32_t { - kSubIdNone = 0, //!< Default mode (or no specific mode). - - // X86 sub-types. - kSubIdX86_AVX = 1, //!< Code generation uses AVX by default (VEC instructions). - kSubIdX86_AVX2 = 2, //!< Code generation uses AVX2 by default (VEC instructions). - kSubIdX86_AVX512 = 3, //!< Code generation uses AVX-512F by default (+32 vector regs). - kSubIdX86_AVX512VL = 4, //!< Code generation uses AVX-512F-VL by default (+VL extensions). - - // ARM sub-types. - kSubIdA32_Thumb = 8, //!< THUMB|THUMBv2 sub-type (only ARM in 32-bit mode). - - #if (ASMJIT_ARCH_X86) && defined(__AVX512VL__) - kSubIdHost = kSubIdX86_AVX512VL - #elif (ASMJIT_ARCH_X86) && defined(__AVX512F__) - kSubIdHost = kSubIdX86_AVX512 - #elif (ASMJIT_ARCH_X86) && defined(__AVX2__) - kSubIdHost = kSubIdX86_AVX2 - #elif (ASMJIT_ARCH_X86) && defined(__AVX__) - kSubIdHost = kSubIdX86_AVX - #elif (ASMJIT_ARCH_ARM == 32) && (defined(_M_ARMT) || defined(__thumb__) || defined(__thumb2__)) - kSubIdHost = kSubIdA32_Thumb - #else - kSubIdHost = 0 - #endif - }; - - //! \name Construction & Destruction - //! \{ - - inline ArchInfo() noexcept : _signature(0) {} - inline ArchInfo(const ArchInfo& other) noexcept : _signature(other._signature) {} - inline explicit ArchInfo(uint32_t type, uint32_t subType = kSubIdNone) noexcept { init(type, subType); } - inline explicit ArchInfo(Globals::NoInit_) noexcept {} - - inline static ArchInfo host() noexcept { return ArchInfo(kIdHost, kSubIdHost); } - - inline bool isInitialized() const noexcept { return _id != kIdNone; } - - ASMJIT_API void init(uint32_t type, uint32_t subType = kSubIdNone) noexcept; - inline void reset() noexcept { _signature = 0; } - - //! \} - - //! \name Overloaded Operators - //! \{ - - inline ArchInfo& operator=(const ArchInfo& other) noexcept = default; - - inline bool operator==(const ArchInfo& other) const noexcept { return _signature == other._signature; } - inline bool operator!=(const ArchInfo& other) const noexcept { return _signature != other._signature; } - - //! \} - - //! \name Accessors - //! \{ - - //! Returns the architecture id, see `Id`. - inline uint32_t archId() const noexcept { return _id; } - - //! Returns the architecture sub-id, see `SubType`. - //! - //! X86 & X64 - //! --------- - //! - //! Architecture subtype describe the highest instruction-set level that can - //! be used. - //! - //! A32 & A64 - //! --------- - //! - //! Architecture mode means the instruction encoding to be used when generating - //! machine code, thus mode can be used to force generation of THUMB and THUMBv2 - //! encoding or regular ARM encoding. - inline uint32_t archSubId() const noexcept { return _subId; } - - //! Tests whether this architecture is 32-bit. - inline bool is32Bit() const noexcept { return _gpSize == 4; } - //! Tests whether this architecture is 64-bit. - inline bool is64Bit() const noexcept { return _gpSize == 8; } - - //! Tests whether this architecture is X86, X64. - inline bool isX86Family() const noexcept { return isX86Family(_id); } - //! Tests whether this architecture is ARM32 or ARM64. - inline bool isArmFamily() const noexcept { return isArmFamily(_id); } - - //! Returns the native size of a general-purpose register. - inline uint32_t gpSize() const noexcept { return _gpSize; } - //! Returns number of general-purpose registers. - inline uint32_t gpCount() const noexcept { return _gpCount; } - - //! \} - - //! \name Static Functions - //! \{ - - static inline bool isX86Family(uint32_t archId) noexcept { return archId >= kIdX86 && archId <= kIdX64; } - static inline bool isArmFamily(uint32_t archId) noexcept { return archId >= kIdA32 && archId <= kIdA64; } - - //! \} -}; - -// ============================================================================ -// [asmjit::ArchRegs] -// ============================================================================ - -//! Information about all architecture registers. -struct ArchRegs { - //! Register information and signatures indexed by `BaseReg::RegType`. - RegInfo regInfo[BaseReg::kTypeMax + 1]; - //! Count (maximum) of registers per `BaseReg::RegType`. - uint8_t regCount[BaseReg::kTypeMax + 1]; - //! Converts RegType to TypeId, see `Type::Id`. - uint8_t regTypeToTypeId[BaseReg::kTypeMax + 1]; -}; - -// ============================================================================ -// [asmjit::ArchUtils] -// ============================================================================ - -struct ArchUtils { - ASMJIT_API static Error typeIdToRegInfo(uint32_t archId, uint32_t& typeIdInOut, RegInfo& regInfo) noexcept; -}; - -//! \} - -ASMJIT_END_NAMESPACE - -#endif // _ASMJIT_CORE_ARCH_H diff --git a/libraries/asmjit/asmjit/core/assembler.cpp b/libraries/asmjit/asmjit/core/assembler.cpp deleted file mode 100644 index 9e7eaf23784..00000000000 --- a/libraries/asmjit/asmjit/core/assembler.cpp +++ /dev/null @@ -1,491 +0,0 @@ -// [AsmJit] -// Machine Code Generation for C++. -// -// [License] -// Zlib - See LICENSE.md file in the package. - -#define ASMJIT_EXPORTS - -#include "../core/assembler.h" -#include "../core/codebufferwriter_p.h" -#include "../core/constpool.h" -#include "../core/logging.h" -#include "../core/support.h" - -ASMJIT_BEGIN_NAMESPACE - -// ============================================================================ -// [asmjit::BaseAssembler - Construction / Destruction] -// ============================================================================ - -BaseAssembler::BaseAssembler() noexcept - : BaseEmitter(kTypeAssembler), - _section(nullptr), - _bufferData(nullptr), - _bufferEnd(nullptr), - _bufferPtr(nullptr), - _op4(), - _op5() {} -BaseAssembler::~BaseAssembler() noexcept {} - -// ============================================================================ -// [asmjit::BaseAssembler - Buffer Management] -// ============================================================================ - -Error BaseAssembler::setOffset(size_t offset) { - if (ASMJIT_UNLIKELY(!_code)) - return DebugUtils::errored(kErrorNotInitialized); - - size_t size = Support::max(_section->bufferSize(), this->offset()); - if (ASMJIT_UNLIKELY(offset > size)) - return reportError(DebugUtils::errored(kErrorInvalidArgument)); - - _bufferPtr = _bufferData + offset; - return kErrorOk; -} - -// ============================================================================ -// [asmjit::BaseAssembler - Logging] -// ============================================================================ - -#ifndef ASMJIT_NO_LOGGING -static void BaseAssembler_logLabel(BaseAssembler* self, const Label& label) noexcept { - Logger* logger = self->_code->_logger; - - StringTmp<512> sb; - size_t binSize = logger->hasFlag(FormatOptions::kFlagMachineCode) ? size_t(0) : std::numeric_limits::max(); - - sb.appendChars(' ', logger->indentation(FormatOptions::kIndentationLabel)); - Logging::formatLabel(sb, logger->flags(), self, label.id()); - sb.appendChar(':'); - Logging::formatLine(sb, nullptr, binSize, 0, 0, self->_inlineComment); - logger->log(sb.data(), sb.size()); -} -#endif - -// ============================================================================ -// [asmjit::BaseAssembler - Section Management] -// ============================================================================ - -static void BaseAssembler_initSection(BaseAssembler* self, Section* section) noexcept { - uint8_t* p = section->_buffer._data; - - self->_section = section; - self->_bufferData = p; - self->_bufferPtr = p + section->_buffer._size; - self->_bufferEnd = p + section->_buffer._capacity; -} - -Error BaseAssembler::section(Section* section) { - if (ASMJIT_UNLIKELY(!_code)) - return reportError(DebugUtils::errored(kErrorNotInitialized)); - - if (!_code->isSectionValid(section->id()) || _code->_sections[section->id()] != section) - return reportError(DebugUtils::errored(kErrorInvalidSection)); - - #ifndef ASMJIT_NO_LOGGING - if (hasEmitterOption(kOptionLoggingEnabled)) - _code->_logger->logf(".section %s {#%u}\n", section->name(), section->id()); - #endif - - BaseAssembler_initSection(this, section); - return kErrorOk; -} - -// ============================================================================ -// [asmjit::BaseAssembler - Label Management] -// ============================================================================ - -Label BaseAssembler::newLabel() { - uint32_t labelId = Globals::kInvalidId; - if (ASMJIT_LIKELY(_code)) { - LabelEntry* le; - Error err = _code->newLabelEntry(&le); - if (ASMJIT_UNLIKELY(err)) - reportError(err); - labelId = le->id(); - } - return Label(labelId); -} - -Label BaseAssembler::newNamedLabel(const char* name, size_t nameSize, uint32_t type, uint32_t parentId) { - uint32_t labelId = Globals::kInvalidId; - if (ASMJIT_LIKELY(_code)) { - LabelEntry* le; - Error err = _code->newNamedLabelEntry(&le, name, nameSize, type, parentId); - if (ASMJIT_UNLIKELY(err)) - reportError(err); - labelId = le->id(); - } - return Label(labelId); -} - -Error BaseAssembler::bind(const Label& label) { - if (ASMJIT_UNLIKELY(!_code)) - return DebugUtils::errored(kErrorNotInitialized); - - Error err = _code->bindLabel(label, _section->id(), offset()); - - #ifndef ASMJIT_NO_LOGGING - if (hasEmitterOption(kOptionLoggingEnabled)) - BaseAssembler_logLabel(this, label); - #endif - - resetInlineComment(); - if (err) - return reportError(err); - - return kErrorOk; -} - -// ============================================================================ -// [asmjit::BaseAssembler - Emit (Low-Level)] -// ============================================================================ - -Error BaseAssembler::_emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, const Operand_& o4, const Operand_& o5) { - _op4 = o4; - _op5 = o5; - _instOptions |= BaseInst::kOptionOp4Op5Used; - return _emit(instId, o0, o1, o2, o3); -} - -Error BaseAssembler::_emitOpArray(uint32_t instId, const Operand_* operands, size_t count) { - const Operand_* o0 = &operands[0]; - const Operand_* o1 = &operands[1]; - const Operand_* o2 = &operands[2]; - const Operand_* o3 = &operands[3]; - - switch (count) { - case 0: o0 = &Globals::none; ASMJIT_FALLTHROUGH; - case 1: o1 = &Globals::none; ASMJIT_FALLTHROUGH; - case 2: o2 = &Globals::none; ASMJIT_FALLTHROUGH; - case 3: o3 = &Globals::none; ASMJIT_FALLTHROUGH; - case 4: - return _emit(instId, *o0, *o1, *o2, *o3); - - case 5: - _op4 = operands[4]; - _op5.reset(); - _instOptions |= BaseInst::kOptionOp4Op5Used; - return _emit(instId, *o0, *o1, *o2, *o3); - - case 6: - _op4 = operands[4]; - _op5 = operands[5]; - _instOptions |= BaseInst::kOptionOp4Op5Used; - return _emit(instId, *o0, *o1, *o2, *o3); - - default: - return DebugUtils::errored(kErrorInvalidArgument); - } -} - -#ifndef ASMJIT_NO_LOGGING -void BaseAssembler::_emitLog( - uint32_t instId, uint32_t options, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, - uint32_t relSize, uint32_t immSize, uint8_t* afterCursor) { - - Logger* logger = _code->logger(); - ASMJIT_ASSERT(logger != nullptr); - ASMJIT_ASSERT(options & BaseEmitter::kOptionLoggingEnabled); - - StringTmp<256> sb; - uint32_t flags = logger->flags(); - - uint8_t* beforeCursor = _bufferPtr; - intptr_t emittedSize = (intptr_t)(afterCursor - beforeCursor); - - Operand_ operands[Globals::kMaxOpCount]; - operands[0].copyFrom(o0); - operands[1].copyFrom(o1); - operands[2].copyFrom(o2); - operands[3].copyFrom(o3); - - if (options & BaseInst::kOptionOp4Op5Used) { - operands[4].copyFrom(_op4); - operands[5].copyFrom(_op5); - } - else { - operands[4].reset(); - operands[5].reset(); - } - - sb.appendChars(' ', logger->indentation(FormatOptions::kIndentationCode)); - Logging::formatInstruction(sb, flags, this, archId(), BaseInst(instId, options, _extraReg), operands, Globals::kMaxOpCount); - - if ((flags & FormatOptions::kFlagMachineCode) != 0) - Logging::formatLine(sb, _bufferPtr, size_t(emittedSize), relSize, immSize, inlineComment()); - else - Logging::formatLine(sb, nullptr, std::numeric_limits::max(), 0, 0, inlineComment()); - logger->log(sb); -} - -Error BaseAssembler::_emitFailed( - Error err, - uint32_t instId, uint32_t options, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3) { - - StringTmp<256> sb; - sb.appendString(DebugUtils::errorAsString(err)); - sb.appendString(": "); - - Operand_ operands[Globals::kMaxOpCount]; - operands[0].copyFrom(o0); - operands[1].copyFrom(o1); - operands[2].copyFrom(o2); - operands[3].copyFrom(o3); - - if (options & BaseInst::kOptionOp4Op5Used) { - operands[4].copyFrom(_op4); - operands[5].copyFrom(_op5); - } - else { - operands[4].reset(); - operands[5].reset(); - } - - Logging::formatInstruction(sb, 0, this, archId(), BaseInst(instId, options, _extraReg), operands, Globals::kMaxOpCount); - resetInstOptions(); - resetExtraReg(); - resetInlineComment(); - return reportError(err, sb.data()); -} -#endif - -// ============================================================================ -// [asmjit::BaseAssembler - Embed] -// ============================================================================ - -struct DataSizeByPower { - char str[4]; -}; - -static const DataSizeByPower dataSizeByPowerTable[] = { - { "db" }, - { "dw" }, - { "dd" }, - { "dq" } -}; - -Error BaseAssembler::embed(const void* data, uint32_t dataSize) { - if (ASMJIT_UNLIKELY(!_code)) - return DebugUtils::errored(kErrorNotInitialized); - - if (dataSize == 0) - return DebugUtils::errored(kErrorInvalidArgument); - - CodeBufferWriter writer(this); - ASMJIT_PROPAGATE(writer.ensureSpace(this, dataSize)); - - writer.emitData(data, dataSize); - - #ifndef ASMJIT_NO_LOGGING - if (ASMJIT_UNLIKELY(hasEmitterOption(kOptionLoggingEnabled))) - _code->_logger->logBinary(data, dataSize); - #endif - - writer.done(this); - return kErrorOk; -} - -Error BaseAssembler::embedLabel(const Label& label) { - if (ASMJIT_UNLIKELY(!_code)) - return DebugUtils::errored(kErrorNotInitialized); - - ASMJIT_ASSERT(_code != nullptr); - RelocEntry* re; - LabelEntry* le = _code->labelEntry(label); - - if (ASMJIT_UNLIKELY(!le)) - return reportError(DebugUtils::errored(kErrorInvalidLabel)); - - uint32_t dataSize = gpSize(); - ASMJIT_ASSERT(dataSize <= 8); - - CodeBufferWriter writer(this); - ASMJIT_PROPAGATE(writer.ensureSpace(this, dataSize)); - - #ifndef ASMJIT_NO_LOGGING - if (ASMJIT_UNLIKELY(hasEmitterOption(kOptionLoggingEnabled))) { - StringTmp<256> sb; - sb.appendFormat(".%s ", dataSizeByPowerTable[Support::ctz(dataSize)].str); - Logging::formatLabel(sb, 0, this, label.id()); - sb.appendChar('\n'); - _code->_logger->log(sb); - } - #endif - - // TODO: Does it make sense to calculate the address here if everything is known? - /* - if (_code->hasBaseAddress() && currentSection() == _code->textSection() && le->isBound()) { - uint64_t addr = _code->baseAddress() + _code->textSection()->offset() + le->offset(); - writer.emitValueLE(addr, dataSize); - } - */ - - Error err = _code->newRelocEntry(&re, RelocEntry::kTypeRelToAbs, dataSize); - if (ASMJIT_UNLIKELY(err)) - return reportError(err); - - re->_sourceSectionId = _section->id(); - re->_sourceOffset = offset(); - - if (le->isBound()) { - re->_targetSectionId = le->section()->id(); - re->_payload = le->offset(); - } - else { - LabelLink* link = _code->newLabelLink(le, _section->id(), offset(), 0); - if (ASMJIT_UNLIKELY(!link)) - return reportError(DebugUtils::errored(kErrorOutOfMemory)); - link->relocId = re->id(); - } - - // Emit dummy DWORD/QWORD depending on the data size. - writer.emitZeros(dataSize); - writer.done(this); - - return kErrorOk; -} - -Error BaseAssembler::embedLabelDelta(const Label& label, const Label& base, uint32_t dataSize) { - if (ASMJIT_UNLIKELY(!_code)) - return DebugUtils::errored(kErrorNotInitialized); - - LabelEntry* labelEntry = _code->labelEntry(label); - LabelEntry* baseEntry = _code->labelEntry(base); - - if (ASMJIT_UNLIKELY(!labelEntry || !baseEntry)) - return reportError(DebugUtils::errored(kErrorInvalidLabel)); - - if (dataSize == 0) - dataSize = gpSize(); - - if (ASMJIT_UNLIKELY(!Support::isPowerOf2(dataSize) || dataSize > 8)) - return reportError(DebugUtils::errored(kErrorInvalidOperandSize)); - - CodeBufferWriter writer(this); - ASMJIT_PROPAGATE(writer.ensureSpace(this, dataSize)); - - #ifndef ASMJIT_NO_LOGGING - if (ASMJIT_UNLIKELY(hasEmitterOption(kOptionLoggingEnabled))) { - StringTmp<256> sb; - sb.appendFormat(".%s (", dataSizeByPowerTable[Support::ctz(dataSize)].str); - Logging::formatLabel(sb, 0, this, label.id()); - sb.appendString(" - "); - Logging::formatLabel(sb, 0, this, base.id()); - sb.appendString(")\n"); - _code->_logger->log(sb); - } - #endif - - // If both labels are bound within the same section it means the delta can be calculated now. - if (labelEntry->isBound() && baseEntry->isBound() && labelEntry->section() == baseEntry->section()) { - uint64_t delta = labelEntry->offset() - baseEntry->offset(); - writer.emitValueLE(delta, dataSize); - } - else { - RelocEntry* re; - Error err = _code->newRelocEntry(&re, RelocEntry::kTypeExpression, dataSize); - if (ASMJIT_UNLIKELY(err)) - return reportError(err); - - Expression* exp = _code->_zone.newT(); - if (ASMJIT_UNLIKELY(!exp)) - return reportError(DebugUtils::errored(kErrorOutOfMemory)); - - exp->reset(); - exp->opType = Expression::kOpSub; - exp->setValueAsLabel(0, labelEntry); - exp->setValueAsLabel(1, baseEntry); - - re->_sourceSectionId = _section->id(); - re->_sourceOffset = offset(); - re->_payload = (uint64_t)(uintptr_t)exp; - - writer.emitZeros(dataSize); - } - - writer.done(this); - return kErrorOk; -} - -Error BaseAssembler::embedConstPool(const Label& label, const ConstPool& pool) { - if (ASMJIT_UNLIKELY(!_code)) - return DebugUtils::errored(kErrorNotInitialized); - - if (ASMJIT_UNLIKELY(!isLabelValid(label))) - return DebugUtils::errored(kErrorInvalidLabel); - - ASMJIT_PROPAGATE(align(kAlignData, uint32_t(pool.alignment()))); - ASMJIT_PROPAGATE(bind(label)); - - size_t size = pool.size(); - CodeBufferWriter writer(this); - ASMJIT_PROPAGATE(writer.ensureSpace(this, size)); - - pool.fill(writer.cursor()); - - #ifndef ASMJIT_NO_LOGGING - if (ASMJIT_UNLIKELY(hasEmitterOption(kOptionLoggingEnabled))) - _code->_logger->logBinary(writer.cursor(), size); - #endif - - writer.advance(size); - writer.done(this); - - return kErrorOk; -} - -// ============================================================================ -// [asmjit::BaseAssembler - Comment] -// ============================================================================ - -Error BaseAssembler::comment(const char* data, size_t size) { - if (ASMJIT_UNLIKELY(!_code)) - return DebugUtils::errored(kErrorNotInitialized); - - #ifndef ASMJIT_NO_LOGGING - if (hasEmitterOption(kOptionLoggingEnabled)) { - Logger* logger = _code->logger(); - logger->log(data, size); - logger->log("\n", 1); - return kErrorOk; - } - #else - ASMJIT_UNUSED(data); - ASMJIT_UNUSED(size); - #endif - - return kErrorOk; -} - -// ============================================================================ -// [asmjit::BaseAssembler - Events] -// ============================================================================ - -Error BaseAssembler::onAttach(CodeHolder* code) noexcept { - ASMJIT_PROPAGATE(Base::onAttach(code)); - - // Attach to the end of the .text section. - BaseAssembler_initSection(this, code->_sections[0]); - - // And reset everything that is used temporarily. - _op4.reset(); - _op5.reset(); - - return kErrorOk; -} - -Error BaseAssembler::onDetach(CodeHolder* code) noexcept { - _section = nullptr; - _bufferData = nullptr; - _bufferEnd = nullptr; - _bufferPtr = nullptr; - - _op4.reset(); - _op5.reset(); - - return Base::onDetach(code); -} - -ASMJIT_END_NAMESPACE diff --git a/libraries/asmjit/asmjit/core/assembler.h b/libraries/asmjit/asmjit/core/assembler.h deleted file mode 100644 index c3bc6cad990..00000000000 --- a/libraries/asmjit/asmjit/core/assembler.h +++ /dev/null @@ -1,165 +0,0 @@ -// [AsmJit] -// Machine Code Generation for C++. -// -// [License] -// Zlib - See LICENSE.md file in the package. - -#ifndef _ASMJIT_CORE_ASSEMBLER_H -#define _ASMJIT_CORE_ASSEMBLER_H - -#include "../core/codeholder.h" -#include "../core/datatypes.h" -#include "../core/emitter.h" -#include "../core/operand.h" - -ASMJIT_BEGIN_NAMESPACE - -//! \addtogroup asmjit_core -//! \{ - -// ============================================================================ -// [asmjit::BaseAssembler] -// ============================================================================ - -//! Base encoder (assembler). -class ASMJIT_VIRTAPI BaseAssembler : public BaseEmitter { -public: - ASMJIT_NONCOPYABLE(BaseAssembler) - typedef BaseEmitter Base; - - //! Current section where the assembling happens. - Section* _section; - //! Start of the CodeBuffer of the current section. - uint8_t* _bufferData; - //! End (first invalid byte) of the current section. - uint8_t* _bufferEnd; - //! Pointer in the CodeBuffer of the current section. - uint8_t* _bufferPtr; - //! 5th operand data, used only temporarily. - Operand_ _op4; - //! 6th operand data, used only temporarily. - Operand_ _op5; - - //! \name Construction & Destruction - //! \{ - - //! Creates a new `BaseAssembler` instance. - ASMJIT_API BaseAssembler() noexcept; - //! Destroys the `BaseAssembler` instance. - ASMJIT_API virtual ~BaseAssembler() noexcept; - - //! \} - - //! \name Code-Buffer Management - //! \{ - - //! Returns the capacity of the current CodeBuffer. - inline size_t bufferCapacity() const noexcept { return (size_t)(_bufferEnd - _bufferData); } - //! Returns the number of remaining bytes in the current CodeBuffer. - inline size_t remainingSpace() const noexcept { return (size_t)(_bufferEnd - _bufferPtr); } - - //! Returns the current position in the CodeBuffer. - inline size_t offset() const noexcept { return (size_t)(_bufferPtr - _bufferData); } - //! Sets the current position in the CodeBuffer to `offset`. - //! - //! \note The `offset` cannot be outside of the buffer size (even if it's - //! within buffer's capacity). - ASMJIT_API Error setOffset(size_t offset); - - //! Returns the start of the CodeBuffer in the current section. - inline uint8_t* bufferData() const noexcept { return _bufferData; } - //! Returns the end (first invalid byte) in the current section. - inline uint8_t* bufferEnd() const noexcept { return _bufferEnd; } - //! Returns the current pointer in the CodeBuffer in the current section. - inline uint8_t* bufferPtr() const noexcept { return _bufferPtr; } - - //! \} - - //! \name Section Management - //! \{ - - inline Section* currentSection() const noexcept { return _section; } - - ASMJIT_API Error section(Section* section) override; - - //! \} - - //! \name Label Management - //! \{ - - ASMJIT_API Label newLabel() override; - ASMJIT_API Label newNamedLabel(const char* name, size_t nameSize = SIZE_MAX, uint32_t type = Label::kTypeGlobal, uint32_t parentId = Globals::kInvalidId) override; - ASMJIT_API Error bind(const Label& label) override; - - //! \} - - //! \cond INTERNAL - //! \name Emit - //! \{ - - using BaseEmitter::_emit; - - ASMJIT_API Error _emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, const Operand_& o4, const Operand_& o5) override; - ASMJIT_API Error _emitOpArray(uint32_t instId, const Operand_* operands, size_t count) override; - -protected: - #ifndef ASMJIT_NO_LOGGING - void _emitLog( - uint32_t instId, uint32_t options, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, - uint32_t relSize, uint32_t immSize, uint8_t* afterCursor); - - Error _emitFailed( - Error err, - uint32_t instId, uint32_t options, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3); - #else - inline Error _emitFailed( - uint32_t err, - uint32_t instId, uint32_t options, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3) { - - ASMJIT_UNUSED(instId); - ASMJIT_UNUSED(options); - ASMJIT_UNUSED(o0); - ASMJIT_UNUSED(o1); - ASMJIT_UNUSED(o2); - ASMJIT_UNUSED(o3); - - resetInstOptions(); - resetInlineComment(); - return reportError(err); - } - #endif -public: - //! \} - //! \endcond - - //! \name Embed - //! \{ - - ASMJIT_API Error embed(const void* data, uint32_t dataSize) override; - ASMJIT_API Error embedLabel(const Label& label) override; - ASMJIT_API Error embedLabelDelta(const Label& label, const Label& base, uint32_t dataSize) override; - ASMJIT_API Error embedConstPool(const Label& label, const ConstPool& pool) override; - - //! \} - - //! \name Comment - //! \{ - - ASMJIT_API Error comment(const char* data, size_t size = SIZE_MAX) override; - - //! \} - - //! \name Events - //! \{ - - ASMJIT_API Error onAttach(CodeHolder* code) noexcept override; - ASMJIT_API Error onDetach(CodeHolder* code) noexcept override; - - //! \} -}; - -//! \} - -ASMJIT_END_NAMESPACE - -#endif // _ASMJIT_CORE_ASSEMBLER_H diff --git a/libraries/asmjit/asmjit/core/build.h b/libraries/asmjit/asmjit/core/build.h deleted file mode 100644 index 894fdeffa0b..00000000000 --- a/libraries/asmjit/asmjit/core/build.h +++ /dev/null @@ -1,573 +0,0 @@ -// [AsmJit] -// Machine Code Generation for C++. -// -// [License] -// Zlib - See LICENSE.md file in the package. - -#ifndef _ASMJIT_CORE_BUILD_H -#define _ASMJIT_CORE_BUILD_H - -// ============================================================================ -// [asmjit::Version] -// ============================================================================ - -#define ASMJIT_LIBRARY_VERSION 0x010200 /* 1.2.0 */ - -// ============================================================================ -// [asmjit::Options] -// ============================================================================ - -// AsmJit Static Builds and Embedding -// ---------------------------------- -// -// These definitions can be used to enable static library build. Embed is used -// when AsmJit's source code is embedded directly in another project, implies -// static build as well. -// -// #define ASMJIT_EMBED // Asmjit is embedded (implies ASMJIT_BUILD_STATIC). -// #define ASMJIT_STATIC // Enable static-library build. - -// AsmJit Build Mode -// ----------------- -// -// These definitions control the build mode and tracing support. The build mode -// should be auto-detected at compile time, but it's possible to override it in -// case that the auto-detection fails. -// -// Tracing is a feature that is never compiled by default and it's only used to -// debug AsmJit itself. -// -// #define ASMJIT_BUILD_DEBUG // Always use debug-mode (ASMJIT_ASSERT enabled). -// #define ASMJIT_BUILD_RELEASE // Always use release-mode (ASMJIT_ASSERT disabled). - -// AsmJit Build Backends -// --------------------- -// -// These definitions control which backends to compile. If none of these is -// defined AsmJit will use host architecture by default (for JIT code generation). -// -// #define ASMJIT_BUILD_X86 // Enable X86 targets (X86 and X86_64). -// #define ASMJIT_BUILD_ARM // Enable ARM targets (ARM and AArch64). -// #define ASMJIT_BUILD_HOST // Enable targets based on target arch (default). - -// AsmJit Build Options -// -------------------- -// -// Flags can be defined to disable standard features. These are handy especially -// when building AsmJit statically and some features are not needed or unwanted -// (like BaseCompiler). -// -// AsmJit features are enabled by default. -// #define ASMJIT_NO_BUILDER // Disable Builder (completely). -// #define ASMJIT_NO_COMPILER // Disable Compiler (completely). -// #define ASMJIT_NO_JIT // Disable JIT memory manager and JitRuntime. -// #define ASMJIT_NO_LOGGING // Disable logging and formatting (completely). -// #define ASMJIT_NO_TEXT // Disable everything that contains text -// // representation (instructions, errors, ...). -// #define ASMJIT_NO_VALIDATION // Disable validation API and options. -// #define ASMJIT_NO_INTROSPECTION // Disable API related to instruction database -// // (validation, cpu features, rw-info, etc). - -// Prevent compile-time errors caused by misconfiguration. -#if defined(ASMJIT_NO_TEXT) && !defined(ASMJIT_NO_LOGGING) - #pragma "ASMJIT_NO_TEXT can only be defined when ASMJIT_NO_LOGGING is defined." - #undef ASMJIT_NO_TEXT -#endif - -#if defined(ASMJIT_NO_INTROSPECTION) && !defined(ASMJIT_NO_COMPILER) - #pragma message("ASMJIT_NO_INTROSPECTION can only be defined when ASMJIT_NO_COMPILER is defined") - #undef ASMJIT_NO_INTROSPECTION -#endif - -// ============================================================================ -// [asmjit::Dependencies] -// ============================================================================ - -// We really want std-types as globals. -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#if defined(_WIN32) - #ifndef WIN32_LEAN_AND_MEAN - #define WIN32_LEAN_AND_MEAN - #define ASMJIT_UNDEF_WIN32_LEAN_AND_MEAN - #endif - #ifndef NOMINMAX - #define NOMINMAX - #define ASMJIT_UNDEF_NOMINMAX - #endif - #include - #ifdef ASMJIT_UNDEF_WIN32_LEAN_AND_MEAN - #undef WIN32_LEAN_AND_MEAN - #undef ASMJIT_UNDEF_WIN32_LEAN_AND_MEAN - #endif - #ifdef ASMJIT_UNDEF_NOMINMAX - #undef NOMINMAX - #undef ASMJIT_UNDEF_NOMINMAX - #endif -#else - #include -#endif - -// ============================================================================ -// [asmjit::Build - Globals - Deprecated] -// ============================================================================ - -// DEPRECATED: Will be removed in the future. -#if defined(ASMJIT_BUILD_EMBED) || defined(ASMJIT_BUILD_STATIC) - #if defined(ASMJIT_BUILD_EMBED) - #pragma message("'ASMJIT_BUILD_EMBED' is deprecated, use 'ASMJIT_STATIC'") - #endif - #if defined(ASMJIT_BUILD_STATIC) - #pragma message("'ASMJIT_BUILD_STATIC' is deprecated, use 'ASMJIT_STATIC'") - #endif - - #if !defined(ASMJIT_STATIC) - #define ASMJIT_STATIC - #endif -#endif - -// ============================================================================ -// [asmjit::Build - Globals - Build Mode] -// ============================================================================ - -// Detect ASMJIT_BUILD_DEBUG and ASMJIT_BUILD_RELEASE if not defined. -#if !defined(ASMJIT_BUILD_DEBUG) && !defined(ASMJIT_BUILD_RELEASE) - #if !defined(NDEBUG) - #define ASMJIT_BUILD_DEBUG - #else - #define ASMJIT_BUILD_RELEASE - #endif -#endif - -// ============================================================================ -// [asmjit::Build - Globals - Target Architecture] -// ============================================================================ - -#if defined(_M_X64) || defined(__x86_64__) - #define ASMJIT_ARCH_X86 64 -#elif defined(_M_IX86) || defined(__X86__) || defined(__i386__) - #define ASMJIT_ARCH_X86 32 -#else - #define ASMJIT_ARCH_X86 0 -#endif - -#if defined(__arm64__) || defined(__aarch64__) -# define ASMJIT_ARCH_ARM 64 -#elif defined(_M_ARM) || defined(_M_ARMT) || defined(__arm__) || defined(__thumb__) || defined(__thumb2__) - #define ASMJIT_ARCH_ARM 32 -#else - #define ASMJIT_ARCH_ARM 0 -#endif - -#if defined(_MIPS_ARCH_MIPS64) || defined(__mips64) - #define ASMJIT_ARCH_MIPS 64 -#elif defined(_MIPS_ARCH_MIPS32) || defined(_M_MRX000) || defined(__mips__) - #define ASMJIT_ARCH_MIPS 32 -#else - #define ASMJIT_ARCH_MIPS 0 -#endif - -#define ASMJIT_ARCH_BITS (ASMJIT_ARCH_X86 | ASMJIT_ARCH_ARM | ASMJIT_ARCH_MIPS) -#if ASMJIT_ARCH_BITS == 0 - #undef ASMJIT_ARCH_BITS - #if defined (__LP64__) || defined(_LP64) - #define ASMJIT_ARCH_BITS 64 - #else - #define ASMJIT_ARCH_BITS 32 - #endif -#endif - -#if (defined(__ARMEB__)) || \ - (defined(__MIPSEB__)) || \ - (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)) - #define ASMJIT_ARCH_LE 0 - #define ASMJIT_ARCH_BE 1 -#else - #define ASMJIT_ARCH_LE 1 - #define ASMJIT_ARCH_BE 0 -#endif - -// Build host architecture if no architecture is selected. -#if !defined(ASMJIT_BUILD_HOST) && \ - !defined(ASMJIT_BUILD_X86) && \ - !defined(ASMJIT_BUILD_ARM) - #define ASMJIT_BUILD_HOST -#endif - -// Detect host architecture if building only for host. -#if ASMJIT_ARCH_X86 && defined(ASMJIT_BUILD_HOST) && !defined(ASMJIT_BUILD_X86) - #define ASMJIT_BUILD_X86 -#endif - -#if ASMJIT_ARCH_ARM && defined(ASMJIT_BUILD_HOST) && !defined(ASMJIT_BUILD_ARM) - #define ASMJIT_BUILD_ARM -#endif - -// ============================================================================ -// [asmjit::Build - Globals - C++ Compiler and Features Detection] -// ============================================================================ - -#define ASMJIT_CXX_CLANG 0 -#define ASMJIT_CXX_GNU 0 -#define ASMJIT_CXX_INTEL 0 -#define ASMJIT_CXX_MSC 0 -#define ASMJIT_CXX_MAKE_VER(MAJOR, MINOR, PATCH) ((MAJOR) * 10000000 + (MINOR) * 100000 + (PATCH)) - -// Intel Compiler [pretends to be GNU or MSC, so it must be checked first]: -// - https://software.intel.com/en-us/articles/c0x-features-supported-by-intel-c-compiler -// - https://software.intel.com/en-us/articles/c14-features-supported-by-intel-c-compiler -// - https://software.intel.com/en-us/articles/c17-features-supported-by-intel-c-compiler -#if defined(__INTEL_COMPILER) - - #undef ASMJIT_CXX_INTEL - #define ASMJIT_CXX_INTEL ASMJIT_CXX_MAKE_VER(__INTEL_COMPILER / 100, (__INTEL_COMPILER / 10) % 10, __INTEL_COMPILER % 10) - -// MSC Compiler: -// - https://msdn.microsoft.com/en-us/library/hh567368.aspx -// -// Version List: -// - 16.00.0 == VS2010 -// - 17.00.0 == VS2012 -// - 18.00.0 == VS2013 -// - 19.00.0 == VS2015 -// - 19.10.0 == VS2017 -#elif defined(_MSC_VER) && defined(_MSC_FULL_VER) - - #undef ASMJIT_CXX_MSC - #if _MSC_VER == _MSC_FULL_VER / 10000 - #define ASMJIT_CXX_MSC ASMJIT_CXX_MAKE_VER(_MSC_VER / 100, _MSC_VER % 100, _MSC_FULL_VER % 10000) - #else - #define ASMJIT_CXX_MSC ASMJIT_CXX_MAKE_VER(_MSC_VER / 100, (_MSC_FULL_VER / 100000) % 100, _MSC_FULL_VER % 100000) - #endif - - // SEVERE: VS2015 handles constexpr's incorrectly in case a struct contains a - // union. There is no workaround known other than rewriting the whole - // code. VS2017 has a similar bug, but it can be workarounded. - #if ASMJIT_CXX_MSC < ASMJIT_CXX_MAKE_VER(19, 10, 0) - #error "[asmjit] At least VS2017 is required due to a severe bug in VS2015's constexpr implementation" - #endif - -// Clang Compiler [Pretends to be GNU, so it must be checked before]: -// - https://clang.llvm.org/cxx_status.html -#elif defined(__clang_major__) && defined(__clang_minor__) && defined(__clang_patchlevel__) - - #undef ASMJIT_CXX_CLANG - #define ASMJIT_CXX_CLANG ASMJIT_CXX_MAKE_VER(__clang_major__, __clang_minor__, __clang_patchlevel__) - -// GNU Compiler: -// - https://gcc.gnu.org/projects/cxx-status.html -#elif defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__) - - #undef ASMJIT_CXX_GNU - #define ASMJIT_CXX_GNU ASMJIT_CXX_MAKE_VER(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) - -#endif - -// Compiler features detection macros. -#if ASMJIT_CXX_CLANG && defined(__has_builtin) - #define ASMJIT_CXX_HAS_BUILTIN(NAME, CHECK) (__has_builtin(NAME)) -#else - #define ASMJIT_CXX_HAS_BUILTIN(NAME, CHECK) (!(!(CHECK))) -#endif - -#if ASMJIT_CXX_CLANG && defined(__has_extension) - #define ASMJIT_CXX_HAS_FEATURE(NAME, CHECK) (__has_extension(NAME)) -#elif ASMJIT_CXX_CLANG && defined(__has_feature) - #define ASMJIT_CXX_HAS_FEATURE(NAME, CHECK) (__has_feature(NAME)) -#else - #define ASMJIT_CXX_HAS_FEATURE(NAME, CHECK) (!(!(CHECK))) -#endif - -#if ASMJIT_CXX_CLANG && defined(__has_attribute) - #define ASMJIT_CXX_HAS_ATTRIBUTE(NAME, CHECK) (__has_attribute(NAME)) -#else - #define ASMJIT_CXX_HAS_ATTRIBUTE(NAME, CHECK) (!(!(CHECK))) -#endif - -#if ASMJIT_CXX_CLANG && defined(__has_cpp_attribute) - #define ASMJIT_CXX_HAS_CPP_ATTRIBUTE(NAME, CHECK) (__has_cpp_attribute(NAME)) -#else - #define ASMJIT_CXX_HAS_CPP_ATTRIBUTE(NAME, CHECK) (!(!(CHECK))) -#endif - -// Compiler features by vendor. -#if defined(_MSC_VER) && !defined(_NATIVE_WCHAR_T_DEFINED) - #define ASMJIT_CXX_HAS_NATIVE_WCHAR_T 0 -#else - #define ASMJIT_CXX_HAS_NATIVE_WCHAR_T 1 -#endif - -#if ASMJIT_CXX_HAS_FEATURE(cxx_unicode_literals, ( \ - (ASMJIT_CXX_INTEL >= ASMJIT_CXX_MAKE_VER(14, 0, 0)) || \ - (ASMJIT_CXX_MSC >= ASMJIT_CXX_MAKE_VER(19, 0, 0)) || \ - (ASMJIT_CXX_GNU >= ASMJIT_CXX_MAKE_VER(4 , 5, 0) && __cplusplus >= 201103L) )) - #define ASMJIT_CXX_HAS_UNICODE_LITERALS 1 -#else - #define ASMJIT_CXX_HAS_UNICODE_LITERALS 0 -#endif - -// ============================================================================ -// [asmjit::Build - Globals - API Decorators & Language Extensions] -// ============================================================================ - -// API (Export / Import). -#if !defined(ASMJIT_STATIC) - #if defined(_WIN32) && (defined(_MSC_VER) || defined(__MINGW32__)) - #if defined(ASMJIT_EXPORTS) - #define ASMJIT_API __declspec(dllexport) - #else - #define ASMJIT_API __declspec(dllimport) - #endif - #elif defined(_WIN32) && defined(__GNUC__) - #if defined(ASMJIT_EXPORTS) - #define ASMJIT_API __attribute__((__dllexport__)) - #else - #define ASMJIT_API __attribute__((__dllimport__)) - #endif - #elif defined(__GNUC__) - #define ASMJIT_API __attribute__((__visibility__("default"))) - #endif -#endif - -#if !defined(ASMJIT_API) - #define ASMJIT_API -#endif - -#if !defined(ASMJIT_VARAPI) - #define ASMJIT_VARAPI extern ASMJIT_API -#endif - -// This is basically a workaround. When using MSVC and marking class as DLL -// export everything gets exported, which is unwanted in most projects. MSVC -// automatically exports typeinfo and vtable if at least one symbol of the -// class is exported. However, GCC has some strange behavior that even if -// one or more symbol is exported it doesn't export typeinfo unless the -// class itself is decorated with "visibility(default)" (i.e. ASMJIT_API). -#if !defined(_WIN32) && defined(__GNUC__) - #define ASMJIT_VIRTAPI ASMJIT_API -#else - #define ASMJIT_VIRTAPI -#endif - -// Function attributes. -#if !defined(ASMJIT_BUILD_DEBUG) && defined(__GNUC__) - #define ASMJIT_INLINE inline __attribute__((__always_inline__)) -#elif !defined(ASMJIT_BUILD_DEBUG) && defined(_MSC_VER) - #define ASMJIT_INLINE __forceinline -#else - #define ASMJIT_INLINE inline -#endif - -#if defined(__GNUC__) - #define ASMJIT_NOINLINE __attribute__((__noinline__)) - #define ASMJIT_NORETURN __attribute__((__noreturn__)) -#elif defined(_MSC_VER) - #define ASMJIT_NOINLINE __declspec(noinline) - #define ASMJIT_NORETURN __declspec(noreturn) -#else - #define ASMJIT_NOINLINE - #define ASMJIT_NORETURN -#endif - -// Calling conventions. -#if ASMJIT_ARCH_X86 == 32 && defined(__GNUC__) - #define ASMJIT_CDECL __attribute__((__cdecl__)) - #define ASMJIT_STDCALL __attribute__((__stdcall__)) - #define ASMJIT_FASTCALL __attribute__((__fastcall__)) - #define ASMJIT_REGPARM(N) __attribute__((__regparm__(N))) -#elif ASMJIT_ARCH_X86 == 32 && defined(_MSC_VER) - #define ASMJIT_CDECL __cdecl - #define ASMJIT_STDCALL __stdcall - #define ASMJIT_FASTCALL __fastcall - #define ASMJIT_REGPARM(N) -#else - #define ASMJIT_CDECL - #define ASMJIT_STDCALL - #define ASMJIT_FASTCALL - #define ASMJIT_REGPARM(N) -#endif - -// Type alignment (not allowed by C++11 'alignas' keyword). -#if defined(__GNUC__) - #define ASMJIT_ALIGN_TYPE(TYPE, N) __attribute__((__aligned__(N))) TYPE -#elif defined(_MSC_VER) - #define ASMJIT_ALIGN_TYPE(TYPE, N) __declspec(align(N)) TYPE -#else - #define ASMJIT_ALIGN_TYPE(TYPE, N) TYPE -#endif - -// Annotations. -#if defined(__GNUC__) - #define ASMJIT_LIKELY(...) __builtin_expect(!!(__VA_ARGS__), 1) - #define ASMJIT_UNLIKELY(...) __builtin_expect(!!(__VA_ARGS__), 0) -#else - #define ASMJIT_LIKELY(...) (__VA_ARGS__) - #define ASMJIT_UNLIKELY(...) (__VA_ARGS__) -#endif - -#if defined(__clang__) && __cplusplus >= 201103L - #define ASMJIT_FALLTHROUGH [[clang::fallthrough]] -#elif ASMJIT_CXX_GNU >= ASMJIT_CXX_MAKE_VER(7, 0, 0) - #define ASMJIT_FALLTHROUGH __attribute__((__fallthrough__)) -#else - #define ASMJIT_FALLTHROUGH ((void)0) /* fallthrough */ -#endif - -#define ASMJIT_UNUSED(X) (void)(X) - -// Utilities. -#define ASMJIT_OFFSET_OF(STRUCT, MEMBER) ((int)(intptr_t)((const char*)&((const STRUCT*)0x100)->MEMBER) - 0x100) -#define ASMJIT_ARRAY_SIZE(X) uint32_t(sizeof(X) / sizeof(X[0])) - -#if ASMJIT_CXX_HAS_ATTRIBUTE(attribute_deprecated_with_message, ASMJIT_CXX_GNU >= ASMJIT_CXX_MAKE_VER(4, 5, 0)) - #define ASMJIT_DEPRECATED(DECL, MESSAGE) DECL __attribute__((__deprecated__(MESSAGE))) -#elif ASMJIT_MSC - #define ASMJIT_DEPRECATED(DECL, MESSAGE) __declspec(deprecated(MESSAGE)) DECL -#else - #define ASMJIT_DEPRECATED(DECL, MESSAGE) DECL -#endif - -#if ASMJIT_CXX_HAS_ATTRIBUTE(no_sanitize, 0) - #define ASMJIT_ATTRIBUTE_NO_SANITIZE_UNDEF __attribute__((__no_sanitize__("undefined"))) -#elif ASMJIT_CXX_GNU >= ASMJIT_CXX_MAKE_VER(4, 9, 0) - #define ASMJIT_ATTRIBUTE_NO_SANITIZE_UNDEF __attribute__((__no_sanitize_undefined__)) -#else - #define ASMJIT_ATTRIBUTE_NO_SANITIZE_UNDEF -#endif - -// ============================================================================ -// [asmjit::Build - Globals - Begin-Namespace / End-Namespace] -// ============================================================================ - -#if defined(__clang__) - #define ASMJIT_BEGIN_NAMESPACE \ - namespace asmjit { \ - _Pragma("clang diagnostic push") \ - _Pragma("clang diagnostic ignored \"-Wconstant-logical-operand\"") \ - _Pragma("clang diagnostic ignored \"-Wunnamed-type-template-args\"") - #define ASMJIT_END_NAMESPACE \ - _Pragma("clang diagnostic pop") \ - } -#elif ASMJIT_CXX_GNU >= ASMJIT_CXX_MAKE_VER(4, 0, 0) && \ - ASMJIT_CXX_GNU < ASMJIT_CXX_MAKE_VER(5, 0, 0) - #define ASMJIT_BEGIN_NAMESPACE \ - namespace asmjit { \ - _Pragma("GCC diagnostic push") \ - _Pragma("GCC diagnostic ignored \"-Wmissing-field-initializers\"") - #define ASMJIT_END_NAMESPACE \ - _Pragma("GCC diagnostic pop") \ - } -#elif ASMJIT_CXX_GNU >= ASMJIT_CXX_MAKE_VER(8, 0, 0) - #define ASMJIT_BEGIN_NAMESPACE \ - namespace asmjit { \ - _Pragma("GCC diagnostic push") \ - _Pragma("GCC diagnostic ignored \"-Wclass-memaccess\"") - #define ASMJIT_END_NAMESPACE \ - _Pragma("GCC diagnostic pop") \ - } -#elif defined(_MSC_VER) && !defined(__INTEL_COMPILER) - #define ASMJIT_BEGIN_NAMESPACE \ - namespace asmjit { \ - __pragma(warning(push)) \ - __pragma(warning(disable: 4127)) /* conditional expression is constant*/\ - __pragma(warning(disable: 4201)) /* nameless struct/union */ - #define ASMJIT_END_NAMESPACE \ - __pragma(warning(pop)) \ - } -#endif - -#if !defined(ASMJIT_BEGIN_NAMESPACE) && !defined(ASMJIT_END_NAMESPACE) - #define ASMJIT_BEGIN_NAMESPACE namespace asmjit { - #define ASMJIT_END_NAMESPACE } -#endif - -#define ASMJIT_BEGIN_SUB_NAMESPACE(NAMESPACE) \ - ASMJIT_BEGIN_NAMESPACE \ - namespace NAMESPACE { - -#define ASMJIT_END_SUB_NAMESPACE \ - } \ - ASMJIT_END_NAMESPACE - -// ============================================================================ -// [asmjit::Build - Globals - Utilities] -// ============================================================================ - -#define ASMJIT_NONCOPYABLE(...) \ - private: \ - __VA_ARGS__(const __VA_ARGS__& other) = delete; \ - __VA_ARGS__& operator=(const __VA_ARGS__& other) = delete; \ - public: - -#define ASMJIT_NONCONSTRUCTIBLE(...) \ - private: \ - __VA_ARGS__() = delete; \ - __VA_ARGS__(const __VA_ARGS__& other) = delete; \ - __VA_ARGS__& operator=(const __VA_ARGS__& other) = delete; \ - public: - -// ============================================================================ -// [asmjit::Build - Globals - Build-Only] -// ============================================================================ - -// Internal macros that are only used when building AsmJit itself. -#ifdef ASMJIT_EXPORTS - #if !defined(ASMJIT_BUILD_DEBUG) && ASMJIT_CXX_GNU >= ASMJIT_CXX_MAKE_VER(4, 4, 0) - #define ASMJIT_FAVOR_SIZE __attribute__((__optimize__("Os"))) - #define ASMJIT_FAVOR_SPEED __attribute__((__optimize__("O3"))) - #elif ASMJIT_CXX_HAS_ATTRIBUTE(__minsize__, 0) - #define ASMJIT_FAVOR_SIZE __attribute__((__minsize__)) - #define ASMJIT_FAVOR_SPEED - #else - #define ASMJIT_FAVOR_SIZE - #define ASMJIT_FAVOR_SPEED - #endif - - // Only turn-off these warnings when building asmjit itself. - #ifdef _MSC_VER - #ifndef _CRT_SECURE_NO_DEPRECATE - #define _CRT_SECURE_NO_DEPRECATE - #endif - #ifndef _CRT_SECURE_NO_WARNINGS - #define _CRT_SECURE_NO_WARNINGS - #endif - #endif -#endif - -// ============================================================================ -// [asmjit::Build - Globals - Cleanup] -// ============================================================================ - -// Undefine everything that is not used by AsmJit outside of `build.h` and that -// is considered private. -#undef ASMJIT_CXX_CLANG -#undef ASMJIT_CXX_GNU -#undef ASMJIT_CXX_INTEL -#undef ASMJIT_CXX_MSC -#undef ASMJIT_CXX_MAKE_VER - -// ============================================================================ -// [asmjit::Build - Globals - Unit Testing Boilerplate] -// ============================================================================ - -// IDE: Make sure '#ifdef'ed unit tests are properly highlighted. -#if defined(__INTELLISENSE__) && !defined(ASMJIT_TEST) - #define ASMJIT_TEST -#endif - -// IDE: Make sure '#ifdef'ed unit tests are not disabled by IDE. -#if defined(ASMJIT_TEST) - #include "../../../test/broken.h" -#endif - -#endif // _ASMJIT_CORE_BUILD_H diff --git a/libraries/asmjit/asmjit/core/builder.cpp b/libraries/asmjit/asmjit/core/builder.cpp deleted file mode 100644 index 541d0532f2a..00000000000 --- a/libraries/asmjit/asmjit/core/builder.cpp +++ /dev/null @@ -1,995 +0,0 @@ -// [AsmJit] -// Machine Code Generation for C++. -// -// [License] -// Zlib - See LICENSE.md file in the package. - -#define ASMJIT_EXPORTS - -#include "../core/build.h" -#ifndef ASMJIT_NO_BUILDER - -#include "../core/builder.h" -#include "../core/logging.h" -#include "../core/support.h" - -ASMJIT_BEGIN_NAMESPACE - -// ============================================================================ -// [asmjit::PostponedErrorHandler (Internal)] -// ============================================================================ - -//! Postponed error handler that never throws. Used as a temporal error handler -//! to run passes. If error occurs, the caller is notified and will call the -//! real error handler, that can throw. -class PostponedErrorHandler : public ErrorHandler { -public: - void handleError(Error err, const char* message, BaseEmitter* origin) override { - ASMJIT_UNUSED(err); - ASMJIT_UNUSED(origin); - - _message.assignString(message); - } - - StringTmp<128> _message; -}; - -// ============================================================================ -// [asmjit::BaseBuilder - Construction / Destruction] -// ============================================================================ - -BaseBuilder::BaseBuilder() noexcept - : BaseEmitter(kTypeBuilder), - _codeZone(32768 - Zone::kBlockOverhead), - _dataZone(16384 - Zone::kBlockOverhead), - _passZone(65536 - Zone::kBlockOverhead), - _allocator(&_codeZone), - _passes(), - _labelNodes(), - _cursor(nullptr), - _firstNode(nullptr), - _lastNode(nullptr), - _nodeFlags(0) {} -BaseBuilder::~BaseBuilder() noexcept {} - -// ============================================================================ -// [asmjit::BaseBuilder - Node Management] -// ============================================================================ - -LabelNode* BaseBuilder::newLabelNode() noexcept { - LabelNode* node = newNodeT(); - if (!node || registerLabelNode(node) != kErrorOk) - return nullptr; - return node; -} - -AlignNode* BaseBuilder::newAlignNode(uint32_t alignMode, uint32_t alignment) noexcept { - return newNodeT(alignMode, alignment); -} - -EmbedDataNode* BaseBuilder::newEmbedDataNode(const void* data, uint32_t size) noexcept { - if (size > EmbedDataNode::kInlineBufferSize) { - void* cloned = _dataZone.alloc(size); - if (ASMJIT_UNLIKELY(!cloned)) - return nullptr; - - if (data) - memcpy(cloned, data, size); - data = cloned; - } - - return newNodeT(const_cast(data), size); -} - -ConstPoolNode* BaseBuilder::newConstPoolNode() noexcept { - ConstPoolNode* node = newNodeT(); - if (!node || registerLabelNode(node) != kErrorOk) - return nullptr; - return node; -} - -CommentNode* BaseBuilder::newCommentNode(const char* data, size_t size) noexcept { - if (data) { - if (size == SIZE_MAX) - size = strlen(data); - - if (size > 0) { - data = static_cast(_dataZone.dup(data, size, true)); - if (!data) return nullptr; - } - } - - return newNodeT(data); -} - -InstNode* BaseBuilder::newInstNode(uint32_t instId, uint32_t instOptions, const Operand_& o0) noexcept { - uint32_t opCount = 1; - uint32_t opCapacity = InstNode::capacityOfOpCount(opCount); - ASMJIT_ASSERT(opCapacity >= 4); - - InstNode* node = _allocator.allocT(InstNode::nodeSizeOfOpCapacity(opCapacity)); - if (ASMJIT_UNLIKELY(!node)) - return nullptr; - - node = new(node) InstNode(this, instId, instOptions, opCount, opCapacity); - node->setOp(0, o0); - for (uint32_t i = opCount; i < opCapacity; i++) node->resetOp(i); - return node; -} - -InstNode* BaseBuilder::newInstNode(uint32_t instId, uint32_t instOptions, const Operand_& o0, const Operand_& o1) noexcept { - uint32_t opCount = 2; - uint32_t opCapacity = InstNode::capacityOfOpCount(opCount); - ASMJIT_ASSERT(opCapacity >= 4); - - InstNode* node = _allocator.allocT(InstNode::nodeSizeOfOpCapacity(opCapacity)); - if (ASMJIT_UNLIKELY(!node)) - return nullptr; - - node = new(node) InstNode(this, instId, instOptions, opCount, opCapacity); - node->setOp(0, o0); - node->setOp(1, o1); - for (uint32_t i = opCount; i < opCapacity; i++) node->resetOp(i); - return node; -} - -InstNode* BaseBuilder::newInstNode(uint32_t instId, uint32_t instOptions, const Operand_& o0, const Operand_& o1, const Operand_& o2) noexcept { - uint32_t opCount = 3; - uint32_t opCapacity = InstNode::capacityOfOpCount(opCount); - ASMJIT_ASSERT(opCapacity >= 4); - - InstNode* node = _allocator.allocT(InstNode::nodeSizeOfOpCapacity(opCapacity)); - if (ASMJIT_UNLIKELY(!node)) - return nullptr; - - node = new(node) InstNode(this, instId, instOptions, opCount, opCapacity); - node->setOp(0, o0); - node->setOp(1, o1); - node->setOp(2, o2); - for (uint32_t i = opCount; i < opCapacity; i++) node->resetOp(i); - return node; -} - -InstNode* BaseBuilder::newInstNode(uint32_t instId, uint32_t instOptions, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3) noexcept { - uint32_t opCount = 4; - uint32_t opCapacity = InstNode::capacityOfOpCount(opCount); - ASMJIT_ASSERT(opCapacity >= 4); - - InstNode* node = _allocator.allocT(InstNode::nodeSizeOfOpCapacity(opCapacity)); - if (ASMJIT_UNLIKELY(!node)) - return nullptr; - - node = new(node) InstNode(this, instId, instOptions, opCount, opCapacity); - node->setOp(0, o0); - node->setOp(1, o1); - node->setOp(2, o2); - node->setOp(3, o3); - for (uint32_t i = opCount; i < opCapacity; i++) node->resetOp(i); - return node; -} - -InstNode* BaseBuilder::newInstNodeRaw(uint32_t instId, uint32_t instOptions, uint32_t opCount) noexcept { - uint32_t opCapacity = InstNode::capacityOfOpCount(opCount); - ASMJIT_ASSERT(opCapacity >= 4); - - InstNode* node = _allocator.allocT(InstNode::nodeSizeOfOpCapacity(opCapacity)); - if (ASMJIT_UNLIKELY(!node)) - return nullptr; - return new(node) InstNode(this, instId, instOptions, opCount, opCapacity); -} - -BaseNode* BaseBuilder::addNode(BaseNode* node) noexcept { - ASMJIT_ASSERT(node); - ASMJIT_ASSERT(!node->_prev); - ASMJIT_ASSERT(!node->_next); - ASMJIT_ASSERT(!node->isActive()); - - if (!_cursor) { - if (!_firstNode) { - _firstNode = node; - _lastNode = node; - } - else { - node->_next = _firstNode; - _firstNode->_prev = node; - _firstNode = node; - } - } - else { - BaseNode* prev = _cursor; - BaseNode* next = _cursor->next(); - - node->_prev = prev; - node->_next = next; - - prev->_next = node; - if (next) - next->_prev = node; - else - _lastNode = node; - } - - node->addFlags(BaseNode::kFlagIsActive); - if (node->isSection()) - _dirtySectionLinks = true; - - _cursor = node; - return node; -} - -BaseNode* BaseBuilder::addAfter(BaseNode* node, BaseNode* ref) noexcept { - ASMJIT_ASSERT(node); - ASMJIT_ASSERT(ref); - - ASMJIT_ASSERT(!node->_prev); - ASMJIT_ASSERT(!node->_next); - - BaseNode* prev = ref; - BaseNode* next = ref->next(); - - node->_prev = prev; - node->_next = next; - - node->addFlags(BaseNode::kFlagIsActive); - if (node->isSection()) - _dirtySectionLinks = true; - - prev->_next = node; - if (next) - next->_prev = node; - else - _lastNode = node; - - return node; -} - -BaseNode* BaseBuilder::addBefore(BaseNode* node, BaseNode* ref) noexcept { - ASMJIT_ASSERT(node != nullptr); - ASMJIT_ASSERT(!node->_prev); - ASMJIT_ASSERT(!node->_next); - ASMJIT_ASSERT(!node->isActive()); - ASMJIT_ASSERT(ref != nullptr); - ASMJIT_ASSERT(ref->isActive()); - - BaseNode* prev = ref->prev(); - BaseNode* next = ref; - - node->_prev = prev; - node->_next = next; - - node->addFlags(BaseNode::kFlagIsActive); - if (node->isSection()) - _dirtySectionLinks = true; - - next->_prev = node; - if (prev) - prev->_next = node; - else - _firstNode = node; - - return node; -} - -BaseNode* BaseBuilder::removeNode(BaseNode* node) noexcept { - if (!node->isActive()) - return node; - - BaseNode* prev = node->prev(); - BaseNode* next = node->next(); - - if (_firstNode == node) - _firstNode = next; - else - prev->_next = next; - - if (_lastNode == node) - _lastNode = prev; - else - next->_prev = prev; - - node->_prev = nullptr; - node->_next = nullptr; - node->clearFlags(BaseNode::kFlagIsActive); - if (node->isSection()) - _dirtySectionLinks = true; - - if (_cursor == node) - _cursor = prev; - - return node; -} - -void BaseBuilder::removeNodes(BaseNode* first, BaseNode* last) noexcept { - if (first == last) { - removeNode(first); - return; - } - - if (!first->isActive()) - return; - - BaseNode* prev = first->prev(); - BaseNode* next = last->next(); - - if (_firstNode == first) - _firstNode = next; - else - prev->_next = next; - - if (_lastNode == last) - _lastNode = prev; - else - next->_prev = prev; - - BaseNode* node = first; - uint32_t didRemoveSection = false; - - for (;;) { - next = node->next(); - ASMJIT_ASSERT(next != nullptr); - - node->_prev = nullptr; - node->_next = nullptr; - node->clearFlags(BaseNode::kFlagIsActive); - didRemoveSection |= uint32_t(node->isSection()); - - if (_cursor == node) - _cursor = prev; - - if (node == last) - break; - node = next; - } - - if (didRemoveSection) - _dirtySectionLinks = true; -} - -BaseNode* BaseBuilder::setCursor(BaseNode* node) noexcept { - BaseNode* old = _cursor; - _cursor = node; - return old; -} - -// ============================================================================ -// [asmjit::BaseBuilder - Section] -// ============================================================================ - -Error BaseBuilder::sectionNodeOf(SectionNode** pOut, uint32_t sectionId) noexcept { - if (ASMJIT_UNLIKELY(!_code)) - return DebugUtils::errored(kErrorNotInitialized); - - if (ASMJIT_UNLIKELY(!_code->isSectionValid(sectionId))) - return DebugUtils::errored(kErrorInvalidSection); - - if (sectionId >= _sectionNodes.size()) - ASMJIT_PROPAGATE(_sectionNodes.resize(&_allocator, sectionId + 1)); - - SectionNode* node = _sectionNodes[sectionId]; - if (!node) { - node = newNodeT(sectionId); - if (ASMJIT_UNLIKELY(!node)) - return DebugUtils::errored(kErrorOutOfMemory); - _sectionNodes[sectionId] = node; - } - - *pOut = node; - return kErrorOk; -} - -Error BaseBuilder::section(Section* section) { - SectionNode* node; - Error err = sectionNodeOf(&node, section->id()); - - if (ASMJIT_UNLIKELY(err)) - return reportError(err); - - if (!node->isActive()) { - // Insert the section at the end if it was not part of the code. - addAfter(node, lastNode()); - _cursor = node; - } - else { - // This is a bit tricky. We cache section links to make sure that - // switching sections doesn't involve traversal in linked-list unless - // the position of the section has changed. - if (hasDirtySectionLinks()) - updateSectionLinks(); - - if (node->_nextSection) - _cursor = node->_nextSection->_prev; - else - _cursor = _lastNode; - } - - return kErrorOk; -} - -void BaseBuilder::updateSectionLinks() noexcept { - if (!_dirtySectionLinks) - return; - - BaseNode* node_ = _firstNode; - SectionNode* currentSection = nullptr; - - while (node_) { - if (node_->isSection()) { - if (currentSection) - currentSection->_nextSection = node_->as(); - currentSection = node_->as(); - } - node_ = node_->next(); - } - - if (currentSection) - currentSection->_nextSection = nullptr; - - _dirtySectionLinks = false; -} - -// ============================================================================ -// [asmjit::BaseBuilder - Labels] -// ============================================================================ - -Error BaseBuilder::labelNodeOf(LabelNode** pOut, uint32_t labelId) noexcept { - if (ASMJIT_UNLIKELY(!_code)) - return DebugUtils::errored(kErrorNotInitialized); - - uint32_t index = labelId; - if (ASMJIT_UNLIKELY(index >= _code->labelCount())) - return DebugUtils::errored(kErrorInvalidLabel); - - if (index >= _labelNodes.size()) - ASMJIT_PROPAGATE(_labelNodes.resize(&_allocator, index + 1)); - - LabelNode* node = _labelNodes[index]; - if (!node) { - node = newNodeT(labelId); - if (ASMJIT_UNLIKELY(!node)) - return DebugUtils::errored(kErrorOutOfMemory); - _labelNodes[index] = node; - } - - *pOut = node; - return kErrorOk; -} - -Error BaseBuilder::registerLabelNode(LabelNode* node) noexcept { - if (ASMJIT_UNLIKELY(!_code)) - return DebugUtils::errored(kErrorNotInitialized); - - // Don't call `reportError()` from here, we are noexcept and we are called - // by `newLabelNode()` and `newFuncNode()`, which are noexcept as well. - LabelEntry* le; - ASMJIT_PROPAGATE(_code->newLabelEntry(&le)); - uint32_t labelId = le->id(); - - // We just added one label so it must be true. - ASMJIT_ASSERT(_labelNodes.size() < labelId + 1); - ASMJIT_PROPAGATE(_labelNodes.resize(&_allocator, labelId + 1)); - - _labelNodes[labelId] = node; - node->_id = labelId; - - return kErrorOk; -} - -static Error BaseBuilder_newLabelInternal(BaseBuilder* self, uint32_t labelId) noexcept { - ASMJIT_ASSERT(self->_labelNodes.size() < labelId + 1); - LabelNode* node = self->newNodeT(labelId); - - if (ASMJIT_UNLIKELY(!node)) - return DebugUtils::errored(kErrorOutOfMemory); - - ASMJIT_PROPAGATE(self->_labelNodes.resize(&self->_allocator, labelId + 1)); - self->_labelNodes[labelId] = node; - node->_id = labelId; - return kErrorOk; -} - -Label BaseBuilder::newLabel() { - uint32_t labelId = Globals::kInvalidId; - if (_code) { - LabelEntry* le; - Error err = _code->newLabelEntry(&le); - if (ASMJIT_UNLIKELY(err)) { - reportError(err); - } - else { - err = BaseBuilder_newLabelInternal(this, le->id()); - if (ASMJIT_UNLIKELY(err)) - reportError(err); - else - labelId = le->id(); - } - } - return Label(labelId); -} - -Label BaseBuilder::newNamedLabel(const char* name, size_t nameSize, uint32_t type, uint32_t parentId) { - uint32_t labelId = Globals::kInvalidId; - if (_code) { - LabelEntry* le; - Error err = _code->newNamedLabelEntry(&le, name, nameSize, type, parentId); - if (ASMJIT_UNLIKELY(err)) { - reportError(err); - } - else { - err = BaseBuilder_newLabelInternal(this, le->id()); - if (ASMJIT_UNLIKELY(err)) - reportError(err); - else - labelId = le->id(); - } - } - return Label(labelId); -} - -Error BaseBuilder::bind(const Label& label) { - LabelNode* node; - Error err = labelNodeOf(&node, label); - - if (ASMJIT_UNLIKELY(err)) - return reportError(err); - - addNode(node); - return kErrorOk; -} - -// ============================================================================ -// [asmjit::BaseBuilder - Passes] -// ============================================================================ - -ASMJIT_FAVOR_SIZE Pass* BaseBuilder::passByName(const char* name) const noexcept { - for (Pass* pass : _passes) - if (strcmp(pass->name(), name) == 0) - return pass; - return nullptr; -} - -ASMJIT_FAVOR_SIZE Error BaseBuilder::addPass(Pass* pass) noexcept { - if (ASMJIT_UNLIKELY(!_code)) - return DebugUtils::errored(kErrorNotInitialized); - - if (ASMJIT_UNLIKELY(pass == nullptr)) { - // Since this is directly called by `addPassT()` we treat `null` argument - // as out-of-memory condition. Otherwise it would be API misuse. - return DebugUtils::errored(kErrorOutOfMemory); - } - else if (ASMJIT_UNLIKELY(pass->_cb)) { - // Kinda weird, but okay... - if (pass->_cb == this) - return kErrorOk; - return DebugUtils::errored(kErrorInvalidState); - } - - ASMJIT_PROPAGATE(_passes.append(&_allocator, pass)); - pass->_cb = this; - return kErrorOk; -} - -ASMJIT_FAVOR_SIZE Error BaseBuilder::deletePass(Pass* pass) noexcept { - if (ASMJIT_UNLIKELY(!_code)) - return DebugUtils::errored(kErrorNotInitialized); - - if (ASMJIT_UNLIKELY(pass == nullptr)) - return DebugUtils::errored(kErrorInvalidArgument); - - if (pass->_cb != nullptr) { - if (pass->_cb != this) - return DebugUtils::errored(kErrorInvalidState); - - uint32_t index = _passes.indexOf(pass); - ASMJIT_ASSERT(index != Globals::kNotFound); - - pass->_cb = nullptr; - _passes.removeAt(index); - } - - pass->~Pass(); - return kErrorOk; -} - -Error BaseBuilder::runPasses() { - if (ASMJIT_UNLIKELY(!_code)) - return DebugUtils::errored(kErrorNotInitialized); - - if (_passes.empty()) - return kErrorOk; - - Logger* logger = code()->logger(); - ErrorHandler* prev = errorHandler(); - PostponedErrorHandler postponed; - - Error err = kErrorOk; - setErrorHandler(&postponed); - - for (Pass* pass : _passes) { - _passZone.reset(); - err = pass->run(&_passZone, logger); - if (err) break; - } - _passZone.reset(); - setErrorHandler(prev); - - if (ASMJIT_UNLIKELY(err)) - return reportError(err, !postponed._message.empty() ? postponed._message.data() : nullptr); - - return kErrorOk; -} - -// ============================================================================ -// [asmjit::BaseBuilder - Emit] -// ============================================================================ - -Error BaseBuilder::_emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3) { - uint32_t opCount = 4; - - if (o3.isNone()) { - opCount = 3; - if (o2.isNone()) { - opCount = 2; - if (o1.isNone()) { - opCount = 1; - if (o0.isNone()) - opCount = 0; - } - } - } - - uint32_t options = instOptions() | globalInstOptions(); - if (options & BaseInst::kOptionReserved) { - if (ASMJIT_UNLIKELY(!_code)) - return DebugUtils::errored(kErrorNotInitialized); - - // Strict validation. - #ifndef ASMJIT_NO_VALIDATION - if (hasEmitterOption(kOptionStrictValidation)) { - Operand_ opArray[4]; - opArray[0].copyFrom(o0); - opArray[1].copyFrom(o1); - opArray[2].copyFrom(o2); - opArray[3].copyFrom(o3); - - Error err = InstAPI::validate(archId(), BaseInst(instId, options, _extraReg), opArray, opCount); - if (ASMJIT_UNLIKELY(err)) { - resetInstOptions(); - resetExtraReg(); - resetInlineComment(); - return reportError(err); - } - } - #endif - - // Clear options that should never be part of `InstNode`. - options &= ~BaseInst::kOptionReserved; - } - - uint32_t opCapacity = InstNode::capacityOfOpCount(opCount); - ASMJIT_ASSERT(opCapacity >= 4); - - InstNode* node = _allocator.allocT(InstNode::nodeSizeOfOpCapacity(opCapacity)); - if (ASMJIT_UNLIKELY(!node)) { - resetInstOptions(); - resetExtraReg(); - resetInlineComment(); - return reportError(DebugUtils::errored(kErrorOutOfMemory)); - } - - node = new(node) InstNode(this, instId, options, opCount, opCapacity); - node->setExtraReg(extraReg()); - node->setOp(0, o0); - node->setOp(1, o1); - node->setOp(2, o2); - node->setOp(3, o3); - - for (uint32_t i = 4; i < InstNode::kBaseOpCapacity; i++) - node->resetOp(i); - - const char* comment = inlineComment(); - if (comment) - node->setInlineComment(static_cast(_dataZone.dup(comment, strlen(comment), true))); - - resetInstOptions(); - resetExtraReg(); - resetInlineComment(); - - addNode(node); - return kErrorOk; -} - -Error BaseBuilder::_emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, const Operand_& o4, const Operand_& o5) { - uint32_t opCount = Globals::kMaxOpCount; - if (o5.isNone()) { - opCount = 5; - if (o4.isNone()) - return _emit(instId, o0, o1, o2, o3); - } - - uint32_t options = instOptions() | globalInstOptions(); - if (ASMJIT_UNLIKELY(options & BaseInst::kOptionReserved)) { - if (ASMJIT_UNLIKELY(!_code)) - return DebugUtils::errored(kErrorNotInitialized); - - // Strict validation. - #ifndef ASMJIT_NO_VALIDATION - if (hasEmitterOption(kOptionStrictValidation)) { - Operand_ opArray[Globals::kMaxOpCount]; - opArray[0].copyFrom(o0); - opArray[1].copyFrom(o1); - opArray[2].copyFrom(o2); - opArray[3].copyFrom(o3); - opArray[4].copyFrom(o4); - opArray[5].copyFrom(o5); - - Error err = InstAPI::validate(archId(), BaseInst(instId, options, _extraReg), opArray, opCount); - if (ASMJIT_UNLIKELY(err)) { - resetInstOptions(); - resetExtraReg(); - resetInlineComment(); - return reportError(err); - } - } - #endif - - // Clear options that should never be part of `InstNode`. - options &= ~BaseInst::kOptionReserved; - } - - uint32_t opCapacity = InstNode::capacityOfOpCount(opCount); - ASMJIT_ASSERT(opCapacity >= opCount); - - InstNode* node = _allocator.allocT(InstNode::nodeSizeOfOpCapacity(opCapacity)); - if (ASMJIT_UNLIKELY(!node)) { - resetInstOptions(); - resetExtraReg(); - resetInlineComment(); - return reportError(DebugUtils::errored(kErrorOutOfMemory)); - } - - node = new(node) InstNode(this, instId, options, opCount, opCapacity); - node->setExtraReg(extraReg()); - node->setOp(0, o0); - node->setOp(1, o1); - node->setOp(2, o2); - node->setOp(3, o3); - node->setOp(4, o4); - - if (opCapacity > 5) - node->setOp(5, o5); - - const char* comment = inlineComment(); - if (comment) - node->setInlineComment(static_cast(_dataZone.dup(comment, strlen(comment), true))); - - resetInstOptions(); - resetExtraReg(); - resetInlineComment(); - - addNode(node); - return kErrorOk; -} - -// ============================================================================ -// [asmjit::BaseBuilder - Align] -// ============================================================================ - -Error BaseBuilder::align(uint32_t alignMode, uint32_t alignment) { - if (ASMJIT_UNLIKELY(!_code)) - return DebugUtils::errored(kErrorNotInitialized); - - AlignNode* node = newAlignNode(alignMode, alignment); - if (ASMJIT_UNLIKELY(!node)) - return reportError(DebugUtils::errored(kErrorOutOfMemory)); - - addNode(node); - return kErrorOk; -} - -// ============================================================================ -// [asmjit::BaseBuilder - Embed] -// ============================================================================ - -Error BaseBuilder::embed(const void* data, uint32_t dataSize) { - if (ASMJIT_UNLIKELY(!_code)) - return DebugUtils::errored(kErrorNotInitialized); - - EmbedDataNode* node = newEmbedDataNode(data, dataSize); - if (ASMJIT_UNLIKELY(!node)) - return reportError(DebugUtils::errored(kErrorOutOfMemory)); - - addNode(node); - return kErrorOk; -} - -Error BaseBuilder::embedLabel(const Label& label) { - if (ASMJIT_UNLIKELY(!_code)) - return DebugUtils::errored(kErrorNotInitialized); - - EmbedLabelNode* node = newNodeT(label.id()); - if (ASMJIT_UNLIKELY(!node)) - return reportError(DebugUtils::errored(kErrorOutOfMemory)); - - addNode(node); - return kErrorOk; -} - -Error BaseBuilder::embedLabelDelta(const Label& label, const Label& base, uint32_t dataSize) { - if (ASMJIT_UNLIKELY(!_code)) - return DebugUtils::errored(kErrorNotInitialized); - - EmbedLabelDeltaNode* node = newNodeT(label.id(), base.id(), dataSize); - if (ASMJIT_UNLIKELY(!node)) - return reportError(DebugUtils::errored(kErrorOutOfMemory)); - - addNode(node); - return kErrorOk; -} - -Error BaseBuilder::embedConstPool(const Label& label, const ConstPool& pool) { - if (ASMJIT_UNLIKELY(!_code)) - return DebugUtils::errored(kErrorNotInitialized); - - if (!isLabelValid(label)) - return reportError(DebugUtils::errored(kErrorInvalidLabel)); - - ASMJIT_PROPAGATE(align(kAlignData, uint32_t(pool.alignment()))); - ASMJIT_PROPAGATE(bind(label)); - - EmbedDataNode* node = newEmbedDataNode(nullptr, uint32_t(pool.size())); - if (ASMJIT_UNLIKELY(!node)) - return reportError(DebugUtils::errored(kErrorOutOfMemory)); - - pool.fill(node->data()); - addNode(node); - return kErrorOk; -} - -// ============================================================================ -// [asmjit::BaseBuilder - Comment] -// ============================================================================ - -Error BaseBuilder::comment(const char* data, size_t size) { - if (ASMJIT_UNLIKELY(!_code)) - return DebugUtils::errored(kErrorNotInitialized); - - CommentNode* node = newCommentNode(data, size); - if (ASMJIT_UNLIKELY(!node)) - return reportError(DebugUtils::errored(kErrorOutOfMemory)); - - addNode(node); - return kErrorOk; -} - -// ============================================================================ -// [asmjit::BaseBuilder - Serialize] -// ============================================================================ - -Error BaseBuilder::serialize(BaseEmitter* dst) { - Error err = kErrorOk; - BaseNode* node_ = _firstNode; - - do { - dst->setInlineComment(node_->inlineComment()); - - if (node_->isInst()) { - InstNode* node = node_->as(); - err = dst->emitInst(node->baseInst(), node->operands(), node->opCount()); - } - else if (node_->isLabel()) { - if (node_->isConstPool()) { - ConstPoolNode* node = node_->as(); - err = dst->embedConstPool(node->label(), node->constPool()); - } - else { - LabelNode* node = node_->as(); - err = dst->bind(node->label()); - } - } - else if (node_->isAlign()) { - AlignNode* node = node_->as(); - err = dst->align(node->alignMode(), node->alignment()); - } - else if (node_->isEmbedData()) { - EmbedDataNode* node = node_->as(); - err = dst->embed(node->data(), node->size()); - } - else if (node_->isEmbedLabel()) { - EmbedLabelNode* node = node_->as(); - err = dst->embedLabel(node->label()); - } - else if (node_->isEmbedLabelDelta()) { - EmbedLabelDeltaNode* node = node_->as(); - err = dst->embedLabelDelta(node->label(), node->baseLabel(), node->dataSize()); - } - else if (node_->isSection()) { - SectionNode* node = node_->as(); - err = dst->section(_code->sectionById(node->id())); - } - else if (node_->isComment()) { - CommentNode* node = node_->as(); - err = dst->comment(node->inlineComment()); - } - - if (err) break; - node_ = node_->next(); - } while (node_); - - return err; -} - -// ============================================================================ -// [asmjit::BaseBuilder - Logging] -// ============================================================================ - -#ifndef ASMJIT_NO_LOGGING -Error BaseBuilder::dump(String& sb, uint32_t flags) const noexcept { - BaseNode* node = _firstNode; - while (node) { - ASMJIT_PROPAGATE(Logging::formatNode(sb, flags, this, node)); - sb.appendChar('\n'); - node = node->next(); - } - - return kErrorOk; -} -#endif - -// ============================================================================ -// [asmjit::BaseBuilder - Events] -// ============================================================================ - -Error BaseBuilder::onAttach(CodeHolder* code) noexcept { - ASMJIT_PROPAGATE(Base::onAttach(code)); - - SectionNode* initialSection; - Error err = sectionNodeOf(&initialSection, 0); - - if (!err) - err = _passes.willGrow(&_allocator, 8); - - if (ASMJIT_UNLIKELY(err)) { - onDetach(code); - return err; - } - - - _cursor = initialSection; - _firstNode = initialSection; - _lastNode = initialSection; - initialSection->setFlags(BaseNode::kFlagIsActive); - - return kErrorOk; -} - -Error BaseBuilder::onDetach(CodeHolder* code) noexcept { - _passes.reset(); - _sectionNodes.reset(); - _labelNodes.reset(); - - _allocator.reset(&_codeZone); - _codeZone.reset(); - _dataZone.reset(); - _passZone.reset(); - - _nodeFlags = 0; - - _cursor = nullptr; - _firstNode = nullptr; - _lastNode = nullptr; - - return Base::onDetach(code); -} - -// ============================================================================ -// [asmjit::Pass - Construction / Destruction] -// ============================================================================ - -Pass::Pass(const char* name) noexcept - : _cb(nullptr), - _name(name) {} -Pass::~Pass() noexcept {} - -ASMJIT_END_NAMESPACE - -#endif // !ASMJIT_NO_BUILDER diff --git a/libraries/asmjit/asmjit/core/builder.h b/libraries/asmjit/asmjit/core/builder.h deleted file mode 100644 index aaa488995a4..00000000000 --- a/libraries/asmjit/asmjit/core/builder.h +++ /dev/null @@ -1,1278 +0,0 @@ -// [AsmJit] -// Machine Code Generation for C++. -// -// [License] -// Zlib - See LICENSE.md file in the package. - -#ifndef _ASMJIT_CORE_BUILDER_H -#define _ASMJIT_CORE_BUILDER_H - -#include "../core/build.h" -#ifndef ASMJIT_NO_BUILDER - -#include "../core/assembler.h" -#include "../core/codeholder.h" -#include "../core/constpool.h" -#include "../core/inst.h" -#include "../core/operand.h" -#include "../core/string.h" -#include "../core/support.h" -#include "../core/zone.h" -#include "../core/zonevector.h" - -ASMJIT_BEGIN_NAMESPACE - -//! \addtogroup asmjit_builder -//! \{ - -// ============================================================================ -// [Forward Declarations] -// ============================================================================ - -class BaseBuilder; -class Pass; - -class BaseNode; -class InstNode; -class SectionNode; -class LabelNode; -class AlignNode; -class EmbedDataNode; -class EmbedLabelNode; -class ConstPoolNode; -class CommentNode; -class SentinelNode; -class LabelDeltaNode; - -// ============================================================================ -// [asmjit::BaseBuilder] -// ============================================================================ - -class ASMJIT_VIRTAPI BaseBuilder : public BaseEmitter { -public: - ASMJIT_NONCOPYABLE(BaseBuilder) - typedef BaseEmitter Base; - - //! Base zone used to allocate nodes and passes. - Zone _codeZone; - //! Data zone used to allocate data and names. - Zone _dataZone; - //! Pass zone, passed to `Pass::run()`. - Zone _passZone; - //! Allocator that uses `_codeZone`. - ZoneAllocator _allocator; - - //! Array of `Pass` objects. - ZoneVector _passes; - //! Maps section indexes to `LabelNode` nodes. - ZoneVector _sectionNodes; - //! Maps label indexes to `LabelNode` nodes. - ZoneVector _labelNodes; - - //! Current node (cursor). - BaseNode* _cursor; - //! First node of the current section. - BaseNode* _firstNode; - //! Last node of the current section. - BaseNode* _lastNode; - - //! Flags assigned to each new node. - uint32_t _nodeFlags; - //! The sections links are dirty (used internally). - bool _dirtySectionLinks; - - //! \name Construction & Destruction - //! \{ - - //! Creates a new `BaseBuilder` instance. - ASMJIT_API BaseBuilder() noexcept; - //! Destroys the `BaseBuilder` instance. - ASMJIT_API virtual ~BaseBuilder() noexcept; - - //! \} - - //! \name Node Management - //! \{ - - //! Returns the first node. - inline BaseNode* firstNode() const noexcept { return _firstNode; } - //! Returns the last node. - inline BaseNode* lastNode() const noexcept { return _lastNode; } - - //! Allocates and instantiates a new node of type `T` and returns its instance. - //! If the allocation fails `nullptr` is returned. - //! - //! The template argument `T` must be a type that is extends \ref BaseNode. - //! - //! \remarks The pointer returned (if non-null) is owned by the Builder or - //! Compiler. When the Builder/Compiler is destroyed it destroys all nodes - //! it created so no manual memory management is required. - template - inline T* newNodeT() noexcept { - return _allocator.newT(this); - } - - //! \overload - template - inline T* newNodeT(ArgsT&&... args) noexcept { - return _allocator.newT(this, std::forward(args)...); - } - - //! Creates a new `LabelNode`. - ASMJIT_API LabelNode* newLabelNode() noexcept; - //! Creates a new `AlignNode`. - ASMJIT_API AlignNode* newAlignNode(uint32_t alignMode, uint32_t alignment) noexcept; - //! Creates a new `EmbedDataNode`. - ASMJIT_API EmbedDataNode* newEmbedDataNode(const void* data, uint32_t size) noexcept; - //! Creates a new `ConstPoolNode`. - ASMJIT_API ConstPoolNode* newConstPoolNode() noexcept; - //! Creates a new `CommentNode`. - ASMJIT_API CommentNode* newCommentNode(const char* data, size_t size) noexcept; - - ASMJIT_API InstNode* newInstNode(uint32_t instId, uint32_t instOptions, const Operand_& o0) noexcept; - ASMJIT_API InstNode* newInstNode(uint32_t instId, uint32_t instOptions, const Operand_& o0, const Operand_& o1) noexcept; - ASMJIT_API InstNode* newInstNode(uint32_t instId, uint32_t instOptions, const Operand_& o0, const Operand_& o1, const Operand_& o2) noexcept; - ASMJIT_API InstNode* newInstNode(uint32_t instId, uint32_t instOptions, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3) noexcept; - ASMJIT_API InstNode* newInstNodeRaw(uint32_t instId, uint32_t instOptions, uint32_t opCount) noexcept; - - //! Adds `node` after the current and sets the current node to the given `node`. - ASMJIT_API BaseNode* addNode(BaseNode* node) noexcept; - //! Inserts the given `node` after `ref`. - ASMJIT_API BaseNode* addAfter(BaseNode* node, BaseNode* ref) noexcept; - //! Inserts the given `node` before `ref`. - ASMJIT_API BaseNode* addBefore(BaseNode* node, BaseNode* ref) noexcept; - //! Removes the given `node`. - ASMJIT_API BaseNode* removeNode(BaseNode* node) noexcept; - //! Removes multiple nodes. - ASMJIT_API void removeNodes(BaseNode* first, BaseNode* last) noexcept; - - //! Returns the cursor. - //! - //! When the Builder/Compiler is created it automatically creates a '.text' - //! \ref SectionNode, which will be the initial one. When instructions are - //! added they are always added after the cursor and the cursor is changed - //! to be that newly added node. Use `setCursor()` to change where new nodes - //! are inserted. - inline BaseNode* cursor() const noexcept { return _cursor; } - - //! Sets the current node to `node` and return the previous one. - ASMJIT_API BaseNode* setCursor(BaseNode* node) noexcept; - - //! Sets the current node without returning the previous node. - //! - //! Only use this function if you are concerned about performance and want - //! this inlined (for example if you set the cursor in a loop, etc...). - inline void _setCursor(BaseNode* node) noexcept { _cursor = node; } - - //! \} - - //! \name Section Management - //! \{ - - //! Returns a vector of SectionNode objects. - //! - //! \note If a section of some id is not associated with the Builder/Compiler - //! it would be null, so always check for nulls if you iterate over the vector. - inline const ZoneVector& sectionNodes() const noexcept { return _sectionNodes; } - - //! Tests whether the `SectionNode` of the given `sectionId` was registered. - inline bool hasRegisteredSectionNode(uint32_t sectionId) const noexcept { - return sectionId < _sectionNodes.size() && _sectionNodes[sectionId] != nullptr; - } - - //! Returns or creates a `SectionNode` that matches the given `sectionId`. - //! - //! \remarks This function will either get the existing `SectionNode` or create - //! it in case it wasn't created before. You can check whether a section has a - //! registered `SectionNode` by using `BaseBuilder::hasRegisteredSectionNode()`. - ASMJIT_API Error sectionNodeOf(SectionNode** pOut, uint32_t sectionId) noexcept; - - ASMJIT_API Error section(Section* section) override; - - //! Returns whether the section links of active section nodes are dirty. You can - //! update these links by calling `updateSectionLinks()` in such case. - inline bool hasDirtySectionLinks() const noexcept { return _dirtySectionLinks; } - - //! Updates links of all active section nodes. - ASMJIT_API void updateSectionLinks() noexcept; - - //! \} - - //! \name Label Management - //! \{ - - //! Returns a vector of LabelNode nodes. - //! - //! \note If a label of some id is not associated with the Builder/Compiler - //! it would be null, so always check for nulls if you iterate over the vector. - inline const ZoneVector& labelNodes() const noexcept { return _labelNodes; } - - //! Tests whether the `LabelNode` of the given `labelId` was registered. - inline bool hasRegisteredLabelNode(uint32_t labelId) const noexcept { - return labelId < _labelNodes.size() && _labelNodes[labelId] != nullptr; - } - - //! \overload - inline bool hasRegisteredLabelNode(const Label& label) const noexcept { - return hasRegisteredLabelNode(label.id()); - } - - //! Gets or creates a `LabelNode` that matches the given `labelId`. - //! - //! \remarks This function will either get the existing `LabelNode` or create - //! it in case it wasn't created before. You can check whether a label has a - //! registered `LabelNode` by using `BaseBuilder::hasRegisteredLabelNode()`. - ASMJIT_API Error labelNodeOf(LabelNode** pOut, uint32_t labelId) noexcept; - - //! \overload - inline Error labelNodeOf(LabelNode** pOut, const Label& label) noexcept { - return labelNodeOf(pOut, label.id()); - } - - //! Registers this label node [Internal]. - //! - //! This function is used internally to register a newly created `LabelNode` - //! with this instance of Builder/Compiler. Use `labelNodeOf()` functions to - //! get back `LabelNode` from a label or its identifier. - ASMJIT_API Error registerLabelNode(LabelNode* node) noexcept; - - ASMJIT_API Label newLabel() override; - ASMJIT_API Label newNamedLabel(const char* name, size_t nameSize = SIZE_MAX, uint32_t type = Label::kTypeGlobal, uint32_t parentId = Globals::kInvalidId) override; - ASMJIT_API Error bind(const Label& label) override; - - //! \} - - //! \name Passes - //! \{ - - //! Returns a vector of `Pass` instances that will be executed by `runPasses()`. - inline const ZoneVector& passes() const noexcept { return _passes; } - - //! Allocates and instantiates a new pass of type `T` and returns its instance. - //! If the allocation fails `nullptr` is returned. - //! - //! The template argument `T` must be a type that is extends \ref Pass. - //! - //! \remarks The pointer returned (if non-null) is owned by the Builder or - //! Compiler. When the Builder/Compiler is destroyed it destroys all passes - //! it created so no manual memory management is required. - template - inline T* newPassT() noexcept { return _codeZone.newT(); } - - //! \overload - template - inline T* newPassT(ArgsT&&... args) noexcept { return _codeZone.newT(std::forward(args)...); } - - template - inline Error addPassT() noexcept { return addPass(newPassT()); } - - template - inline Error addPassT(ArgsT&&... args) noexcept { return addPass(newPassT(std::forward(args)...)); } - - //! Returns `Pass` by name. - ASMJIT_API Pass* passByName(const char* name) const noexcept; - //! Adds `pass` to the list of passes. - ASMJIT_API Error addPass(Pass* pass) noexcept; - //! Removes `pass` from the list of passes and delete it. - ASMJIT_API Error deletePass(Pass* pass) noexcept; - - //! Runs all passes in order. - ASMJIT_API Error runPasses(); - - //! \} - - //! \name Emit - //! \{ - - ASMJIT_API Error _emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3) override; - ASMJIT_API Error _emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, const Operand_& o4, const Operand_& o5) override; - - //! \} - - //! \name Align - //! \{ - - ASMJIT_API Error align(uint32_t alignMode, uint32_t alignment) override; - - //! \} - - //! \name Embed - //! \{ - - ASMJIT_API Error embed(const void* data, uint32_t dataSize) override; - ASMJIT_API Error embedLabel(const Label& label) override; - ASMJIT_API Error embedLabelDelta(const Label& label, const Label& base, uint32_t dataSize) override; - ASMJIT_API Error embedConstPool(const Label& label, const ConstPool& pool) override; - - //! \} - - //! \name Comment - //! \{ - - ASMJIT_API Error comment(const char* data, size_t size = SIZE_MAX) override; - - //! \} - - //! \name Serialization - //! \{ - - //! Serializes everything the given emitter `dst`. - //! - //! Although not explicitly required the emitter will most probably be of - //! Assembler type. The reason is that there is no known use of serializing - //! nodes held by Builder/Compiler into another Builder-like emitter. - ASMJIT_API Error serialize(BaseEmitter* dst); - - //! \} - - //! \name Logging - //! \{ - - #ifndef ASMJIT_NO_LOGGING - ASMJIT_API Error dump(String& sb, uint32_t flags = 0) const noexcept; - #endif - - //! \} - - //! \name Events - //! \{ - - ASMJIT_API Error onAttach(CodeHolder* code) noexcept override; - ASMJIT_API Error onDetach(CodeHolder* code) noexcept override; - - //! \} -}; - -// ============================================================================ -// [asmjit::BaseNode] -// ============================================================================ - -//! Base node. -//! -//! Every node represents a building-block used by `BaseBuilder`. It can be -//! instruction, data, label, comment, directive, or any other high-level -//! representation that can be transformed to the building blocks mentioned. -//! Every class that inherits `BaseBuilder` can define its own nodes that it -//! can lower to basic nodes. -class BaseNode { -public: - ASMJIT_NONCOPYABLE(BaseNode) - - union { - struct { - //! Previous node. - BaseNode* _prev; - //! Next node. - BaseNode* _next; - }; - //! Links (previous and next nodes). - BaseNode* _links[2]; - }; - - //! Data shared between all types of nodes. - struct AnyData { - //! Node type, see \ref NodeType. - uint8_t _nodeType; - //! Node flags, see \ref Flags. - uint8_t _nodeFlags; - //! Not used by BaseNode. - uint8_t _reserved0; - //! Not used by BaseNode. - uint8_t _reserved1; - }; - - struct InstData { - //! Node type, see \ref NodeType. - uint8_t _nodeType; - //! Node flags, see \ref Flags. - uint8_t _nodeFlags; - //! Instruction operands count (used). - uint8_t _opCount; - //! Instruction operands capacity (allocated). - uint8_t _opCapacity; - }; - - struct SentinelData { - //! Node type, see \ref NodeType. - uint8_t _nodeType; - //! Node flags, see \ref Flags. - uint8_t _nodeFlags; - //! Sentinel type. - uint8_t _sentinelType; - //! Not used by BaseNode. - uint8_t _reserved1; - }; - - union { - AnyData _any; - InstData _inst; - SentinelData _sentinel; - }; - - //! Node position in code (should be unique). - uint32_t _position; - - //! Value reserved for AsmJit users never touched by AsmJit itself. - union { - uint64_t _userDataU64; - void* _userDataPtr; - }; - - //! Data used exclusively by the current `Pass`. - void* _passData; - - //! Inline comment/annotation or nullptr if not used. - const char* _inlineComment; - - //! Type of `BaseNode`. - enum NodeType : uint32_t { - //! Invalid node (internal, don't use). - kNodeNone = 0, - - // [BaseBuilder] - - //! Node is `InstNode` or `InstExNode`. - kNodeInst = 1, - //! Node is `SectionNode`. - kNodeSection = 2, - //! Node is `LabelNode`. - kNodeLabel = 3, - //! Node is `AlignNode`. - kNodeAlign = 4, - //! Node is `EmbedDataNode`. - kNodeEmbedData = 5, - //! Node is `EmbedLabelNode`. - kNodeEmbedLabel = 6, - //! Node is `EmbedLabelDeltaNode`. - kNodeEmbedLabelDelta = 7, - //! Node is `ConstPoolNode`. - kNodeConstPool = 8, - //! Node is `CommentNode`. - kNodeComment = 9, - //! Node is `SentinelNode`. - kNodeSentinel = 10, - - // [BaseCompiler] - - //! Node is `FuncNode` (acts as LabelNode). - kNodeFunc = 16, - //! Node is `FuncRetNode` (acts as InstNode). - kNodeFuncRet = 17, - //! Node is `FuncCallNode` (acts as InstNode). - kNodeFuncCall = 18, - - // [UserDefined] - - //! First id of a user-defined node. - kNodeUser = 32 - }; - - //! Node flags, specify what the node is and/or does. - enum Flags : uint32_t { - kFlagIsCode = 0x01u, //!< Node is code that can be executed (instruction, label, align, etc...). - kFlagIsData = 0x02u, //!< Node is data that cannot be executed (data, const-pool, etc...). - kFlagIsInformative = 0x04u, //!< Node is informative, can be removed and ignored. - kFlagIsRemovable = 0x08u, //!< Node can be safely removed if unreachable. - kFlagHasNoEffect = 0x10u, //!< Node does nothing when executed (label, align, explicit nop). - kFlagActsAsInst = 0x20u, //!< Node is an instruction or acts as it. - kFlagActsAsLabel = 0x40u, //!< Node is a label or acts as it. - kFlagIsActive = 0x80u //!< Node is active (part of the code). - }; - - //! \name Construction & Destruction - //! \{ - - //! Creates a new `BaseNode` - always use `BaseBuilder` to allocate nodes. - ASMJIT_INLINE BaseNode(BaseBuilder* cb, uint32_t type, uint32_t flags = 0) noexcept { - _prev = nullptr; - _next = nullptr; - _any._nodeType = uint8_t(type); - _any._nodeFlags = uint8_t(flags | cb->_nodeFlags); - _any._reserved0 = 0; - _any._reserved1 = 0; - _position = 0; - _userDataU64 = 0; - _passData = nullptr; - _inlineComment = nullptr; - } - - //! \} - - //! \name Accessors - //! \{ - - //! Casts this node to `T*`. - template - inline T* as() noexcept { return static_cast(this); } - //! Casts this node to `const T*`. - template - inline const T* as() const noexcept { return static_cast(this); } - - //! Returns previous node or `nullptr` if this node is either first or not - //! part of Builder/Compiler node-list. - inline BaseNode* prev() const noexcept { return _prev; } - //! Returns next node or `nullptr` if this node is either last or not part - //! of Builder/Compiler node-list. - inline BaseNode* next() const noexcept { return _next; } - - //! Returns the type of the node, see `NodeType`. - inline uint32_t type() const noexcept { return _any._nodeType; } - - //! Sets the type of the node, see `NodeType` (internal). - //! - //! \remarks You should never set a type of a node to anything else than the - //! initial value. This function is only provided for users that use custom - //! nodes and need to change the type either during construction or later. - inline void setType(uint32_t type) noexcept { _any._nodeType = uint8_t(type); } - - //! Tests whether this node is either `InstNode` or extends it. - inline bool isInst() const noexcept { return hasFlag(kFlagActsAsInst); } - //! Tests whether this node is `SectionNode`. - inline bool isSection() const noexcept { return type() == kNodeSection; } - //! Tests whether this node is either `LabelNode` or extends it. - inline bool isLabel() const noexcept { return hasFlag(kFlagActsAsLabel); } - //! Tests whether this node is `AlignNode`. - inline bool isAlign() const noexcept { return type() == kNodeAlign; } - //! Tests whether this node is `EmbedDataNode`. - inline bool isEmbedData() const noexcept { return type() == kNodeEmbedData; } - //! Tests whether this node is `EmbedLabelNode`. - inline bool isEmbedLabel() const noexcept { return type() == kNodeEmbedLabel; } - //! Tests whether this node is `EmbedLabelDeltaNode`. - inline bool isEmbedLabelDelta() const noexcept { return type() == kNodeEmbedLabelDelta; } - //! Tests whether this node is `ConstPoolNode`. - inline bool isConstPool() const noexcept { return type() == kNodeConstPool; } - //! Tests whether this node is `CommentNode`. - inline bool isComment() const noexcept { return type() == kNodeComment; } - //! Tests whether this node is `SentinelNode`. - inline bool isSentinel() const noexcept { return type() == kNodeSentinel; } - - //! Tests whether this node is `FuncNode`. - inline bool isFunc() const noexcept { return type() == kNodeFunc; } - //! Tests whether this node is `FuncRetNode`. - inline bool isFuncRet() const noexcept { return type() == kNodeFuncRet; } - //! Tests whether this node is `FuncCallNode`. - inline bool isFuncCall() const noexcept { return type() == kNodeFuncCall; } - - //! Returns the node flags, see \ref Flags. - inline uint32_t flags() const noexcept { return _any._nodeFlags; } - //! Tests whether the node has the given `flag` set. - inline bool hasFlag(uint32_t flag) const noexcept { return (uint32_t(_any._nodeFlags) & flag) != 0; } - //! Replaces node flags with `flags`. - inline void setFlags(uint32_t flags) noexcept { _any._nodeFlags = uint8_t(flags); } - //! Adds the given `flags` to node flags. - inline void addFlags(uint32_t flags) noexcept { _any._nodeFlags = uint8_t(_any._nodeFlags | flags); } - //! Clears the given `flags` from node flags. - inline void clearFlags(uint32_t flags) noexcept { _any._nodeFlags = uint8_t(_any._nodeFlags & (flags ^ 0xFF)); } - - //! Tests whether the node is code that can be executed. - inline bool isCode() const noexcept { return hasFlag(kFlagIsCode); } - //! Tests whether the node is data that cannot be executed. - inline bool isData() const noexcept { return hasFlag(kFlagIsData); } - //! Tests whether the node is informative only (is never encoded like comment, etc...). - inline bool isInformative() const noexcept { return hasFlag(kFlagIsInformative); } - //! Tests whether the node is removable if it's in an unreachable code block. - inline bool isRemovable() const noexcept { return hasFlag(kFlagIsRemovable); } - //! Tests whether the node has no effect when executed (label, .align, nop, ...). - inline bool hasNoEffect() const noexcept { return hasFlag(kFlagHasNoEffect); } - //! Tests whether the node is part of the code. - inline bool isActive() const noexcept { return hasFlag(kFlagIsActive); } - - //! Tests whether the node has a position assigned. - //! - //! \remarks Returns `true` if node position is non-zero. - inline bool hasPosition() const noexcept { return _position != 0; } - //! Returns node position. - inline uint32_t position() const noexcept { return _position; } - //! Sets node position. - //! - //! Node position is a 32-bit unsigned integer that is used by Compiler to - //! track where the node is relatively to the start of the function. It doesn't - //! describe a byte position in a binary, instead it's just a pseudo position - //! used by liveness analysis and other tools around Compiler. - //! - //! If you don't use Compiler then you may use `position()` and `setPosition()` - //! freely for your own purposes if the 32-bit value limit is okay for you. - inline void setPosition(uint32_t position) noexcept { _position = position; } - - //! Returns user data casted to `T*`. - //! - //! User data is decicated to be used only by AsmJit users and not touched - //! by the library. The data has a pointer size so you can either store a - //! pointer or `intptr_t` value through `setUserDataAsIntPtr()`. - template - inline T* userDataAsPtr() const noexcept { return static_cast(_userDataPtr); } - //! Returns user data casted to `int64_t`. - inline int64_t userDataAsInt64() const noexcept { return int64_t(_userDataU64); } - //! Returns user data casted to `uint64_t`. - inline uint64_t userDataAsUInt64() const noexcept { return _userDataU64; } - - //! Sets user data to `data`. - template - inline void setUserDataAsPtr(T* data) noexcept { _userDataPtr = static_cast(data); } - //! Sets used data to the given 64-bit signed `value`. - inline void setUserDataAsInt64(int64_t value) noexcept { _userDataU64 = uint64_t(value); } - //! Sets used data to the given 64-bit unsigned `value`. - inline void setUserDataAsUInt64(uint64_t value) noexcept { _userDataU64 = value; } - - //! Resets user data to zero / nullptr. - inline void resetUserData() noexcept { _userDataU64 = 0; } - - //! Tests whether the node has an associated pass data. - inline bool hasPassData() const noexcept { return _passData != nullptr; } - //! Returns the node pass data - data used during processing & transformations. - template - inline T* passData() const noexcept { return (T*)_passData; } - //! Sets the node pass data to `data`. - template - inline void setPassData(T* data) noexcept { _passData = (void*)data; } - //! Resets the node pass data to nullptr. - inline void resetPassData() noexcept { _passData = nullptr; } - - //! Tests whether the node has an inline comment/annotation. - inline bool hasInlineComment() const noexcept { return _inlineComment != nullptr; } - //! Returns an inline comment/annotation string. - inline const char* inlineComment() const noexcept { return _inlineComment; } - //! Sets an inline comment/annotation string to `s`. - inline void setInlineComment(const char* s) noexcept { _inlineComment = s; } - //! Resets an inline comment/annotation string to nullptr. - inline void resetInlineComment() noexcept { _inlineComment = nullptr; } - - //! \} -}; - -// ============================================================================ -// [asmjit::InstNode] -// ============================================================================ - -//! Instruction node. -//! -//! Wraps an instruction with its options and operands. -class InstNode : public BaseNode { -public: - ASMJIT_NONCOPYABLE(InstNode) - - enum : uint32_t { - //! Count of embedded operands per `InstNode` that are always allocated as - //! a part of the instruction. Minimum embedded operands is 4, but in 32-bit - //! more pointers are smaller and we can embed 5. The rest (up to 6 operands) - //! is always stored in `InstExNode`. - kBaseOpCapacity = uint32_t((128 - sizeof(BaseNode) - sizeof(BaseInst)) / sizeof(Operand_)) - }; - - //! Base instruction data. - BaseInst _baseInst; - //! First 4 or 5 operands (indexed from 0). - Operand_ _opArray[kBaseOpCapacity]; - - //! \name Construction & Destruction - //! \{ - - //! Creates a new `InstNode` instance. - ASMJIT_INLINE InstNode(BaseBuilder* cb, uint32_t instId, uint32_t options, uint32_t opCount, uint32_t opCapacity = kBaseOpCapacity) noexcept - : BaseNode(cb, kNodeInst, kFlagIsCode | kFlagIsRemovable | kFlagActsAsInst), - _baseInst(instId, options) { - _inst._opCapacity = uint8_t(opCapacity); - _inst._opCount = uint8_t(opCount); - } - - //! Reset all built-in operands, including `extraReg`. - inline void _resetOps() noexcept { - _baseInst.resetExtraReg(); - for (uint32_t i = 0, count = opCapacity(); i < count; i++) - _opArray[i].reset(); - } - - //! \} - - //! \name Accessors - //! \{ - - inline BaseInst& baseInst() noexcept { return _baseInst; } - inline const BaseInst& baseInst() const noexcept { return _baseInst; } - - //! Returns the instruction id, see `BaseInst::Id`. - inline uint32_t id() const noexcept { return _baseInst.id(); } - //! Sets the instruction id to `id`, see `BaseInst::Id`. - inline void setId(uint32_t id) noexcept { _baseInst.setId(id); } - - //! Returns instruction options. - inline uint32_t instOptions() const noexcept { return _baseInst.options(); } - //! Sets instruction options. - inline void setInstOptions(uint32_t options) noexcept { _baseInst.setOptions(options); } - //! Adds instruction options. - inline void addInstOptions(uint32_t options) noexcept { _baseInst.addOptions(options); } - //! Clears instruction options. - inline void clearInstOptions(uint32_t options) noexcept { _baseInst.clearOptions(options); } - - //! Tests whether the node has an extra register operand. - inline bool hasExtraReg() const noexcept { return _baseInst.hasExtraReg(); } - //! Returns extra register operand. - inline RegOnly& extraReg() noexcept { return _baseInst.extraReg(); } - //! \overload - inline const RegOnly& extraReg() const noexcept { return _baseInst.extraReg(); } - //! Sets extra register operand to `reg`. - inline void setExtraReg(const BaseReg& reg) noexcept { _baseInst.setExtraReg(reg); } - //! Sets extra register operand to `reg`. - inline void setExtraReg(const RegOnly& reg) noexcept { _baseInst.setExtraReg(reg); } - //! Resets extra register operand. - inline void resetExtraReg() noexcept { _baseInst.resetExtraReg(); } - - //! Returns operands count. - inline uint32_t opCount() const noexcept { return _inst._opCount; } - //! Returns operands capacity. - inline uint32_t opCapacity() const noexcept { return _inst._opCapacity; } - - //! Sets operands count. - inline void setOpCount(uint32_t opCount) noexcept { _inst._opCount = uint8_t(opCount); } - - //! Returns operands array. - inline Operand* operands() noexcept { return (Operand*)_opArray; } - //! Returns operands array (const). - inline const Operand* operands() const noexcept { return (const Operand*)_opArray; } - - inline Operand& opType(uint32_t index) noexcept { - ASMJIT_ASSERT(index < opCapacity()); - return _opArray[index].as(); - } - - inline const Operand& opType(uint32_t index) const noexcept { - ASMJIT_ASSERT(index < opCapacity()); - return _opArray[index].as(); - } - - inline void setOp(uint32_t index, const Operand_& op) noexcept { - ASMJIT_ASSERT(index < opCapacity()); - _opArray[index].copyFrom(op); - } - - inline void resetOp(uint32_t index) noexcept { - ASMJIT_ASSERT(index < opCapacity()); - _opArray[index].reset(); - } - - //! \} - - //! \name Utilities - //! \{ - - inline bool hasOpType(uint32_t opType) const noexcept { - for (uint32_t i = 0, count = opCount(); i < count; i++) - if (_opArray[i].opType() == opType) - return true; - return false; - } - - inline bool hasRegOp() const noexcept { return hasOpType(Operand::kOpReg); } - inline bool hasMemOp() const noexcept { return hasOpType(Operand::kOpMem); } - inline bool hasImmOp() const noexcept { return hasOpType(Operand::kOpImm); } - inline bool hasLabelOp() const noexcept { return hasOpType(Operand::kOpLabel); } - - inline uint32_t indexOfOpType(uint32_t opType) const noexcept { - uint32_t i = 0; - uint32_t count = opCount(); - - while (i < count) { - if (_opArray[i].opType() == opType) - break; - i++; - } - - return i; - } - - inline uint32_t indexOfMemOp() const noexcept { return indexOfOpType(Operand::kOpMem); } - inline uint32_t indexOfImmOp() const noexcept { return indexOfOpType(Operand::kOpImm); } - inline uint32_t indexOfLabelOp() const noexcept { return indexOfOpType(Operand::kOpLabel); } - - //! \} - - //! \name Rewriting - //! \{ - - inline uint32_t* _getRewriteArray() noexcept { return &_baseInst._extraReg._id; } - inline const uint32_t* _getRewriteArray() const noexcept { return &_baseInst._extraReg._id; } - - ASMJIT_INLINE uint32_t getRewriteIndex(const uint32_t* id) const noexcept { - const uint32_t* array = _getRewriteArray(); - ASMJIT_ASSERT(array <= id); - - size_t index = (size_t)(id - array); - ASMJIT_ASSERT(index < 32); - - return uint32_t(index); - } - - ASMJIT_INLINE void rewriteIdAtIndex(uint32_t index, uint32_t id) noexcept { - uint32_t* array = _getRewriteArray(); - array[index] = id; - } - - //! \} - - //! \name Static Functions - //! \{ - - static inline uint32_t capacityOfOpCount(uint32_t opCount) noexcept { - return opCount <= kBaseOpCapacity ? kBaseOpCapacity : Globals::kMaxOpCount; - } - - static inline size_t nodeSizeOfOpCapacity(uint32_t opCapacity) noexcept { - size_t base = sizeof(InstNode) - kBaseOpCapacity * sizeof(Operand); - return base + opCapacity * sizeof(Operand); - } - - //! \} -}; - -// ============================================================================ -// [asmjit::InstExNode] -// ============================================================================ - -//! Instruction node with maximum number of operands.. -//! -//! This node is created automatically by Builder/Compiler in case that the -//! required number of operands exceeds the default capacity of `InstNode`. -class InstExNode : public InstNode { -public: - ASMJIT_NONCOPYABLE(InstExNode) - - //! Continued `_opArray[]` to hold up to `kMaxOpCount` operands. - Operand_ _opArrayEx[Globals::kMaxOpCount - kBaseOpCapacity]; - - //! \name Construction & Destruction - //! \{ - - //! Creates a new `InstExNode` instance. - inline InstExNode(BaseBuilder* cb, uint32_t instId, uint32_t options, uint32_t opCapacity = Globals::kMaxOpCount) noexcept - : InstNode(cb, instId, options, opCapacity) {} - - //! \} -}; - -// ============================================================================ -// [asmjit::SectionNode] -// ============================================================================ - -//! Section node. -class SectionNode : public BaseNode { -public: - ASMJIT_NONCOPYABLE(SectionNode) - - //! Section id. - uint32_t _id; - - //! Next section node that follows this section. - //! - //! This link is only valid when the section is active (is part of the code) - //! and when `Builder::hasDirtySectionLinks()` returns `false`. If you intend - //! to use this field you should always call `Builder::updateSectionLinks()` - //! before you do so. - SectionNode* _nextSection; - - //! \name Construction & Destruction - //! \{ - - //! Creates a new `SectionNode` instance. - inline SectionNode(BaseBuilder* cb, uint32_t id = 0) noexcept - : BaseNode(cb, kNodeSection, kFlagHasNoEffect), - _id(id), - _nextSection(nullptr) {} - - //! \} - - //! \name Accessors - //! \{ - - //! Returns the section id. - inline uint32_t id() const noexcept { return _id; } - - //! \} -}; - -// ============================================================================ -// [asmjit::LabelNode] -// ============================================================================ - -//! Label node. -class LabelNode : public BaseNode { -public: - ASMJIT_NONCOPYABLE(LabelNode) - - uint32_t _id; - - //! \name Construction & Destruction - //! \{ - - //! Creates a new `LabelNode` instance. - inline LabelNode(BaseBuilder* cb, uint32_t id = 0) noexcept - : BaseNode(cb, kNodeLabel, kFlagHasNoEffect | kFlagActsAsLabel), - _id(id) {} - - //! \} - - //! \name Accessors - //! \{ - - //! Returns the id of the label. - inline uint32_t id() const noexcept { return _id; } - //! Returns the label as `Label` operand. - inline Label label() const noexcept { return Label(_id); } - - //! \} -}; - -// ============================================================================ -// [asmjit::AlignNode] -// ============================================================================ - -//! Align directive (BaseBuilder). -//! -//! Wraps `.align` directive. -class AlignNode : public BaseNode { -public: - ASMJIT_NONCOPYABLE(AlignNode) - - //! Align mode, see `AlignMode`. - uint32_t _alignMode; - //! Alignment (in bytes). - uint32_t _alignment; - - //! \name Construction & Destruction - //! \{ - - //! Creates a new `AlignNode` instance. - inline AlignNode(BaseBuilder* cb, uint32_t alignMode, uint32_t alignment) noexcept - : BaseNode(cb, kNodeAlign, kFlagIsCode | kFlagHasNoEffect), - _alignMode(alignMode), - _alignment(alignment) {} - - //! \} - - //! \name Accessors - //! \{ - - //! Returns align mode. - inline uint32_t alignMode() const noexcept { return _alignMode; } - //! Sets align mode to `alignMode`. - inline void setAlignMode(uint32_t alignMode) noexcept { _alignMode = alignMode; } - - //! Returns align offset in bytes. - inline uint32_t alignment() const noexcept { return _alignment; } - //! Sets align offset in bytes to `offset`. - inline void setAlignment(uint32_t alignment) noexcept { _alignment = alignment; } - - //! \} -}; - -// ============================================================================ -// [asmjit::EmbedDataNode] -// ============================================================================ - -//! Embed data node. -//! -//! Wraps `.data` directive. The node contains data that will be placed at the -//! node's position in the assembler stream. The data is considered to be RAW; -//! no analysis nor byte-order conversion is performed on RAW data. -class EmbedDataNode : public BaseNode { -public: - ASMJIT_NONCOPYABLE(EmbedDataNode) - - enum : uint32_t { - kInlineBufferSize = uint32_t(64 - sizeof(BaseNode) - 4) - }; - - union { - struct { - //! Embedded data buffer. - uint8_t _buf[kInlineBufferSize]; - //! Size of the data. - uint32_t _size; - }; - struct { - //! Pointer to external data. - uint8_t* _externalPtr; - }; - }; - - //! \name Construction & Destruction - //! \{ - - //! Creates a new `EmbedDataNode` instance. - inline EmbedDataNode(BaseBuilder* cb, void* data, uint32_t size) noexcept - : BaseNode(cb, kNodeEmbedData, kFlagIsData) { - - if (size <= kInlineBufferSize) { - if (data) - memcpy(_buf, data, size); - } - else { - _externalPtr = static_cast(data); - } - _size = size; - } - - //! \} - - //! \name Accessors - //! \{ - - //! Returns pointer to the data. - inline uint8_t* data() const noexcept { return _size <= kInlineBufferSize ? const_cast(_buf) : _externalPtr; } - //! Returns size of the data. - inline uint32_t size() const noexcept { return _size; } - - //! \} -}; - -// ============================================================================ -// [asmjit::EmbedLabelNode] -// ============================================================================ - -//! Label data node. -class EmbedLabelNode : public BaseNode { -public: - ASMJIT_NONCOPYABLE(EmbedLabelNode) - - uint32_t _id; - - //! \name Construction & Destruction - //! \{ - - //! Creates a new `EmbedLabelNode` instance. - inline EmbedLabelNode(BaseBuilder* cb, uint32_t id = 0) noexcept - : BaseNode(cb, kNodeEmbedLabel, kFlagIsData), - _id(id) {} - - //! \} - - //! \name Accessors - //! \{ - - //! Returns the id of the label. - inline uint32_t id() const noexcept { return _id; } - //! Sets the label id (use with caution, improper use can break a lot of things). - inline void setId(uint32_t id) noexcept { _id = id; } - - //! Returns the label as `Label` operand. - inline Label label() const noexcept { return Label(_id); } - //! Sets the label id from `label` operand. - inline void setLabel(const Label& label) noexcept { setId(label.id()); } - - //! \} -}; - -// ============================================================================ -// [asmjit::EmbedLabelDeltaNode] -// ============================================================================ - -//! Label data node. -class EmbedLabelDeltaNode : public BaseNode { -public: - ASMJIT_NONCOPYABLE(EmbedLabelDeltaNode) - - uint32_t _id; - uint32_t _baseId; - uint32_t _dataSize; - - //! \name Construction & Destruction - //! \{ - - //! Creates a new `EmbedLabelDeltaNode` instance. - inline EmbedLabelDeltaNode(BaseBuilder* cb, uint32_t id = 0, uint32_t baseId = 0, uint32_t dataSize = 0) noexcept - : BaseNode(cb, kNodeEmbedLabelDelta, kFlagIsData), - _id(id), - _baseId(baseId), - _dataSize(dataSize) {} - - //! \} - - //! \name Accessors - //! \{ - - //! Returns the id of the label. - inline uint32_t id() const noexcept { return _id; } - //! Sets the label id. - inline void setId(uint32_t id) noexcept { _id = id; } - //! Returns the label as `Label` operand. - inline Label label() const noexcept { return Label(_id); } - //! Sets the label id from `label` operand. - inline void setLabel(const Label& label) noexcept { setId(label.id()); } - - //! Returns the id of the base label. - inline uint32_t baseId() const noexcept { return _baseId; } - //! Sets the base label id. - inline void setBaseId(uint32_t baseId) noexcept { _baseId = baseId; } - //! Returns the base label as `Label` operand. - inline Label baseLabel() const noexcept { return Label(_baseId); } - //! Sets the base label id from `label` operand. - inline void setBaseLabel(const Label& baseLabel) noexcept { setBaseId(baseLabel.id()); } - - inline uint32_t dataSize() const noexcept { return _dataSize; } - inline void setDataSize(uint32_t dataSize) noexcept { _dataSize = dataSize; } - - //! \} -}; - -// ============================================================================ -// [asmjit::ConstPoolNode] -// ============================================================================ - -//! A node that wraps `ConstPool`. -class ConstPoolNode : public LabelNode { -public: - ASMJIT_NONCOPYABLE(ConstPoolNode) - - ConstPool _constPool; - - //! \name Construction & Destruction - //! \{ - - //! Creates a new `ConstPoolNode` instance. - inline ConstPoolNode(BaseBuilder* cb, uint32_t id = 0) noexcept - : LabelNode(cb, id), - _constPool(&cb->_codeZone) { - - setType(kNodeConstPool); - addFlags(kFlagIsData); - clearFlags(kFlagIsCode | kFlagHasNoEffect); - } - - //! \} - - //! \name Accessors - //! \{ - - //! Tests whether the constant-pool is empty. - inline bool empty() const noexcept { return _constPool.empty(); } - //! Returns the size of the constant-pool in bytes. - inline size_t size() const noexcept { return _constPool.size(); } - //! Returns minimum alignment. - inline size_t alignment() const noexcept { return _constPool.alignment(); } - - //! Returns the wrapped `ConstPool` instance. - inline ConstPool& constPool() noexcept { return _constPool; } - //! Returns the wrapped `ConstPool` instance (const). - inline const ConstPool& constPool() const noexcept { return _constPool; } - - //! \} - - //! \name Utilities - //! \{ - - //! See `ConstPool::add()`. - inline Error add(const void* data, size_t size, size_t& dstOffset) noexcept { - return _constPool.add(data, size, dstOffset); - } - - //! \} -}; - -// ============================================================================ -// [asmjit::CommentNode] -// ============================================================================ - -//! Comment node. -class CommentNode : public BaseNode { -public: - ASMJIT_NONCOPYABLE(CommentNode) - - //! \name Construction & Destruction - //! \{ - - //! Creates a new `CommentNode` instance. - inline CommentNode(BaseBuilder* cb, const char* comment) noexcept - : BaseNode(cb, kNodeComment, kFlagIsInformative | kFlagHasNoEffect | kFlagIsRemovable) { - _inlineComment = comment; - } - - //! \} -}; - -// ============================================================================ -// [asmjit::SentinelNode] -// ============================================================================ - -//! Sentinel node. -//! -//! Sentinel is a marker that is completely ignored by the code builder. It's -//! used to remember a position in a code as it never gets removed by any pass. -class SentinelNode : public BaseNode { -public: - ASMJIT_NONCOPYABLE(SentinelNode) - - //! Type of the sentinel (purery informative purpose). - enum SentinelType : uint32_t { - kSentinelUnknown = 0u, - kSentinelFuncEnd = 1u - }; - - //! \name Construction & Destruction - //! \{ - - //! Creates a new `SentinelNode` instance. - inline SentinelNode(BaseBuilder* cb, uint32_t sentinelType = kSentinelUnknown) noexcept - : BaseNode(cb, kNodeSentinel, kFlagIsInformative | kFlagHasNoEffect) { - - _sentinel._sentinelType = uint8_t(sentinelType); - } - - //! \} - - //! \name Accessors - //! \{ - - inline uint32_t sentinelType() const noexcept { return _sentinel._sentinelType; } - inline void setSentinelType(uint32_t type) noexcept { _sentinel._sentinelType = uint8_t(type); } - - //! \} -}; - -// ============================================================================ -// [asmjit::Pass] -// ============================================================================ - -//! Pass can be used to implement code transformations, analysis, and lowering. -class ASMJIT_VIRTAPI Pass { -public: - ASMJIT_BASE_CLASS(Pass) - ASMJIT_NONCOPYABLE(Pass) - - //! BaseBuilder this pass is assigned to. - BaseBuilder* _cb; - //! Name of the pass. - const char* _name; - - //! \name Construction & Destruction - //! \{ - - ASMJIT_API Pass(const char* name) noexcept; - ASMJIT_API virtual ~Pass() noexcept; - - //! \} - - //! \name Accessors - //! \{ - - inline const BaseBuilder* cb() const noexcept { return _cb; } - inline const char* name() const noexcept { return _name; } - - //! \} - - //! \name Pass Interface - //! \{ - - //! Processes the code stored in Builder or Compiler. - //! - //! This is the only function that is called by the `BaseBuilder` to process - //! the code. It passes `zone`, which will be reset after the `run()` finishes. - virtual Error run(Zone* zone, Logger* logger) noexcept = 0; - - //! \} -}; - -//! \} - -ASMJIT_END_NAMESPACE - -#endif // !ASMJIT_NO_BUILDER -#endif // _ASMJIT_CORE_BUILDER_H diff --git a/libraries/asmjit/asmjit/core/callconv.cpp b/libraries/asmjit/asmjit/core/callconv.cpp deleted file mode 100644 index b9d6d652677..00000000000 --- a/libraries/asmjit/asmjit/core/callconv.cpp +++ /dev/null @@ -1,43 +0,0 @@ -// [AsmJit] -// Machine Code Generation for C++. -// -// [License] -// Zlib - See LICENSE.md file in the package. - -#define ASMJIT_EXPORTS - -#include "../core/arch.h" -#include "../core/func.h" -#include "../core/type.h" - -#ifdef ASMJIT_BUILD_X86 - #include "../x86/x86callconv_p.h" -#endif - -#ifdef ASMJIT_BUILD_ARM - #include "../arm/armcallconv_p.h" -#endif - -ASMJIT_BEGIN_NAMESPACE - -// ============================================================================ -// [asmjit::CallConv - Init / Reset] -// ============================================================================ - -ASMJIT_FAVOR_SIZE Error CallConv::init(uint32_t ccId) noexcept { - reset(); - - #ifdef ASMJIT_BUILD_X86 - if (CallConv::isX86Family(ccId)) - return x86::CallConvInternal::init(*this, ccId); - #endif - - #ifdef ASMJIT_BUILD_ARM - if (CallConv::isArmFamily(ccId)) - return arm::CallConvInternal::init(*this, ccId); - #endif - - return DebugUtils::errored(kErrorInvalidArgument); -} - -ASMJIT_END_NAMESPACE diff --git a/libraries/asmjit/asmjit/core/callconv.h b/libraries/asmjit/asmjit/core/callconv.h deleted file mode 100644 index 723f8129b4a..00000000000 --- a/libraries/asmjit/asmjit/core/callconv.h +++ /dev/null @@ -1,394 +0,0 @@ -// [AsmJit] -// Machine Code Generation for C++. -// -// [License] -// Zlib - See LICENSE.md file in the package. - -#ifndef _ASMJIT_CORE_CALLCONV_H -#define _ASMJIT_CORE_CALLCONV_H - -#include "../core/arch.h" -#include "../core/operand.h" -#include "../core/support.h" - -ASMJIT_BEGIN_NAMESPACE - -//! \addtogroup asmjit_func -//! \{ - -// ============================================================================ -// [asmjit::CallConv] -// ============================================================================ - -//! Function calling convention. -//! -//! Function calling convention is a scheme that defines how function parameters -//! are passed and how function returns its result. AsmJit defines a variety of -//! architecture and OS specific calling conventions and also provides a compile -//! time detection to make the code-generation easier. -struct CallConv { - //! Calling convention id, see `Id`. - uint8_t _id; - //! Architecture id (see `ArchInfo::Id`). - uint8_t _archId; - //! Register assignment strategy. - uint8_t _strategy; - //! Flags. - uint8_t _flags; - - //! Red zone size (AMD64 == 128 bytes). - uint8_t _redZoneSize; - //! Spill zone size (WIN64 == 32 bytes). - uint8_t _spillZoneSize; - //! Natural stack alignment as defined by OS/ABI. - uint8_t _naturalStackAlignment; - uint8_t _reserved[1]; - - //! Mask of all passed registers, per group. - uint32_t _passedRegs[BaseReg::kGroupVirt]; - //! Mask of all preserved registers, per group. - uint32_t _preservedRegs[BaseReg::kGroupVirt]; - - //! Internal limits of AsmJit's CallConv. - enum Limits : uint32_t { - kMaxRegArgsPerGroup = 16 - }; - - //! Passed registers' order. - union RegOrder { - //! Passed registers, ordered. - uint8_t id[kMaxRegArgsPerGroup]; - uint32_t packed[(kMaxRegArgsPerGroup + 3) / 4]; - }; - - //! Passed registers' order, per group. - RegOrder _passedOrder[BaseReg::kGroupVirt]; - - //! Calling convention id. - enum Id : uint32_t { - //! None or invalid (can't be used). - kIdNone = 0, - - // ------------------------------------------------------------------------ - // [Universal] - // ------------------------------------------------------------------------ - - // TODO: To make this possible we need to know target ARCH and ABI. - - /* - - // Universal calling conventions are applicable to any target and are - // converted to target dependent conventions at runtime. The purpose of - // these conventions is to make using functions less target dependent. - - kIdCDecl = 1, - kIdStdCall = 2, - kIdFastCall = 3, - - //! AsmJit specific calling convention designed for calling functions - //! inside a multimedia code that don't use many registers internally, - //! but are long enough to be called and not inlined. These functions are - //! usually used to calculate trigonometric functions, logarithms, etc... - kIdLightCall2 = 10, - kIdLightCall3 = 11, - kIdLightCall4 = 12, - */ - - // ------------------------------------------------------------------------ - // [X86] - // ------------------------------------------------------------------------ - - //! X86 `__cdecl` calling convention (used by C runtime and libraries). - kIdX86CDecl = 16, - //! X86 `__stdcall` calling convention (used mostly by WinAPI). - kIdX86StdCall = 17, - //! X86 `__thiscall` calling convention (MSVC/Intel). - kIdX86MsThisCall = 18, - //! X86 `__fastcall` convention (MSVC/Intel). - kIdX86MsFastCall = 19, - //! X86 `__fastcall` convention (GCC and Clang). - kIdX86GccFastCall = 20, - //! X86 `regparm(1)` convention (GCC and Clang). - kIdX86GccRegParm1 = 21, - //! X86 `regparm(2)` convention (GCC and Clang). - kIdX86GccRegParm2 = 22, - //! X86 `regparm(3)` convention (GCC and Clang). - kIdX86GccRegParm3 = 23, - - kIdX86LightCall2 = 29, - kIdX86LightCall3 = 30, - kIdX86LightCall4 = 31, - - //! X64 calling convention - WIN64-ABI. - kIdX86Win64 = 32, - //! X64 calling convention - SystemV / AMD64-ABI. - kIdX86SysV64 = 33, - - kIdX64LightCall2 = 45, - kIdX64LightCall3 = 46, - kIdX64LightCall4 = 47, - - // ------------------------------------------------------------------------ - // [ARM] - // ------------------------------------------------------------------------ - - //! Legacy calling convention, floating point arguments are passed via GP registers. - kIdArm32SoftFP = 48, - //! Modern calling convention, uses VFP registers to pass floating point arguments. - kIdArm32HardFP = 49, - - // ------------------------------------------------------------------------ - // [Internal] - // ------------------------------------------------------------------------ - - //! \cond INTERNAL - - _kIdX86Start = 16, - _kIdX86End = 31, - - _kIdX64Start = 32, - _kIdX64End = 47, - - _kIdArmStart = 48, - _kIdArmEnd = 49, - - //! \endcond - - // ------------------------------------------------------------------------ - // [Host] - // ------------------------------------------------------------------------ - - #if defined(ASMJIT_DOCGEN) - - //! Default calling convention based on the current C++ compiler's settings. - //! - //! \note This should be always the same as `kIdHostCDecl`, but some - //! compilers allow to override the default calling convention. Overriding - //! is not detected at the moment. - kIdHost = DETECTED_AT_COMPILE_TIME, - - //! Default CDECL calling convention based on the current C++ compiler's settings. - kIdHostCDecl = DETECTED_AT_COMPILE_TIME, - - //! Default STDCALL calling convention based on the current C++ compiler's settings. - //! - //! \note If not defined by the host then it's the same as `kIdHostCDecl`. - kIdHostStdCall = DETECTED_AT_COMPILE_TIME, - - //! Compatibility for `__fastcall` calling convention. - //! - //! \note If not defined by the host then it's the same as `kIdHostCDecl`. - kIdHostFastCall = DETECTED_AT_COMPILE_TIME - - #elif ASMJIT_ARCH_X86 == 32 - - kIdHost = kIdX86CDecl, - kIdHostCDecl = kIdX86CDecl, - kIdHostStdCall = kIdX86StdCall, - - #if defined(_MSC_VER) - kIdHostFastCall = kIdX86MsFastCall, - #elif defined(__GNUC__) - kIdHostFastCall = kIdX86GccFastCall, - #else - kIdHostFastCall = kIdHost, - #endif - - kIdHostLightCall2 = kIdX86LightCall2, - kIdHostLightCall3 = kIdX86LightCall3, - kIdHostLightCall4 = kIdX86LightCall4 - - #elif ASMJIT_ARCH_X86 == 64 - - #if defined(_WIN32) - kIdHost = kIdX86Win64, - #else - kIdHost = kIdX86SysV64, - #endif - - kIdHostCDecl = kIdHost, // Doesn't exist, redirected to host. - kIdHostStdCall = kIdHost, // Doesn't exist, redirected to host. - kIdHostFastCall = kIdHost, // Doesn't exist, redirected to host. - - kIdHostLightCall2 = kIdX64LightCall2, - kIdHostLightCall3 = kIdX64LightCall3, - kIdHostLightCall4 = kIdX64LightCall4 - - #elif ASMJIT_ARCH_ARM == 32 - - #if defined(__SOFTFP__) - kIdHost = kIdArm32SoftFP, - #else - kIdHost = kIdArm32HardFP, - #endif - // These don't exist on ARM. - kIdHostCDecl = kIdHost, // Doesn't exist, redirected to host. - kIdHostStdCall = kIdHost, // Doesn't exist, redirected to host. - kIdHostFastCall = kIdHost // Doesn't exist, redirected to host. - - #else - - kIdHost = kIdNone, - kIdHostCDecl = kIdHost, - kIdHostStdCall = kIdHost, - kIdHostFastCall = kIdHost - - #endif - }; - - //! Strategy used to assign registers to function arguments. - //! - //! This is AsmJit specific. It basically describes how AsmJit should convert - //! the function arguments defined by `FuncSignature` into register IDs and - //! stack offsets. The default strategy `kStrategyDefault` assigns registers - //! and then stack whereas `kStrategyWin64` strategy does register shadowing - //! as defined by WIN64 calling convention - it applies to 64-bit calling - //! conventions only. - enum Strategy : uint32_t { - kStrategyDefault = 0, //!< Default register assignment strategy. - kStrategyWin64 = 1 //!< WIN64 specific register assignment strategy. - }; - - //! Calling convention flags. - enum Flags : uint32_t { - kFlagCalleePopsStack = 0x01, //!< Callee is responsible for cleaning up the stack. - kFlagPassFloatsByVec = 0x02, //!< Pass F32 and F64 arguments by VEC128 register. - kFlagVectorCall = 0x04, //!< This is a '__vectorcall' calling convention. - kFlagIndirectVecArgs = 0x08 //!< Pass vector arguments indirectly (as a pointer). - }; - - //! \name Construction & Destruction - //! \{ - - ASMJIT_API Error init(uint32_t ccId) noexcept; - - inline void reset() noexcept { - memset(this, 0, sizeof(*this)); - memset(_passedOrder, 0xFF, sizeof(_passedOrder)); - } - - //! \} - - //! \name Accessors - //! \{ - - //! Returns the calling convention id, see `Id`. - inline uint32_t id() const noexcept { return _id; } - //! Sets the calling convention id, see `Id`. - inline void setId(uint32_t id) noexcept { _id = uint8_t(id); } - - //! Returns the calling function architecture id. - inline uint32_t archId() const noexcept { return _archId; } - //! Sets the calling function architecture id. - inline void setArchType(uint32_t archId) noexcept { _archId = uint8_t(archId); } - - //! Returns the strategy used to assign registers to arguments, see `Strategy`. - inline uint32_t strategy() const noexcept { return _strategy; } - //! Sets the strategy used to assign registers to arguments, see `Strategy`. - inline void setStrategy(uint32_t strategy) noexcept { _strategy = uint8_t(strategy); } - - //! Tests whether the calling convention has the given `flag` set. - inline bool hasFlag(uint32_t flag) const noexcept { return (uint32_t(_flags) & flag) != 0; } - //! Returns the calling convention flags, see `Flags`. - inline uint32_t flags() const noexcept { return _flags; } - //! Adds the calling convention flags, see `Flags`. - inline void setFlags(uint32_t flag) noexcept { _flags = uint8_t(flag); }; - //! Adds the calling convention flags, see `Flags`. - inline void addFlags(uint32_t flags) noexcept { _flags = uint8_t(_flags | flags); }; - - //! Tests whether this calling convention specifies 'RedZone'. - inline bool hasRedZone() const noexcept { return _redZoneSize != 0; } - //! Tests whether this calling convention specifies 'SpillZone'. - inline bool hasSpillZone() const noexcept { return _spillZoneSize != 0; } - - //! Returns size of 'RedZone'. - inline uint32_t redZoneSize() const noexcept { return _redZoneSize; } - //! Returns size of 'SpillZone'. - inline uint32_t spillZoneSize() const noexcept { return _spillZoneSize; } - - //! Sets size of 'RedZone'. - inline void setRedZoneSize(uint32_t size) noexcept { _redZoneSize = uint8_t(size); } - //! Sets size of 'SpillZone'. - inline void setSpillZoneSize(uint32_t size) noexcept { _spillZoneSize = uint8_t(size); } - - //! Returns a natural stack alignment. - inline uint32_t naturalStackAlignment() const noexcept { return _naturalStackAlignment; } - //! Sets a natural stack alignment. - //! - //! This function can be used to override the default stack alignment in case - //! that you know that it's alignment is different. For example it allows to - //! implement custom calling conventions that guarantee higher stack alignment. - inline void setNaturalStackAlignment(uint32_t value) noexcept { _naturalStackAlignment = uint8_t(value); } - - inline const uint8_t* passedOrder(uint32_t group) const noexcept { - ASMJIT_ASSERT(group < BaseReg::kGroupVirt); - return _passedOrder[group].id; - } - - inline uint32_t passedRegs(uint32_t group) const noexcept { - ASMJIT_ASSERT(group < BaseReg::kGroupVirt); - return _passedRegs[group]; - } - - inline void _setPassedPacked(uint32_t group, uint32_t p0, uint32_t p1, uint32_t p2, uint32_t p3) noexcept { - ASMJIT_ASSERT(group < BaseReg::kGroupVirt); - - _passedOrder[group].packed[0] = p0; - _passedOrder[group].packed[1] = p1; - _passedOrder[group].packed[2] = p2; - _passedOrder[group].packed[3] = p3; - } - - inline void setPassedToNone(uint32_t group) noexcept { - ASMJIT_ASSERT(group < BaseReg::kGroupVirt); - - _setPassedPacked(group, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu); - _passedRegs[group] = 0u; - } - - inline void setPassedOrder(uint32_t group, uint32_t a0, uint32_t a1 = 0xFF, uint32_t a2 = 0xFF, uint32_t a3 = 0xFF, uint32_t a4 = 0xFF, uint32_t a5 = 0xFF, uint32_t a6 = 0xFF, uint32_t a7 = 0xFF) noexcept { - ASMJIT_ASSERT(group < BaseReg::kGroupVirt); - - // NOTE: This should always be called with all arguments known at compile time, - // so even if it looks scary it should be translated into few instructions. - _setPassedPacked(group, Support::bytepack32_4x8(a0, a1, a2, a3), - Support::bytepack32_4x8(a4, a5, a6, a7), - 0xFFFFFFFFu, - 0xFFFFFFFFu); - - _passedRegs[group] = (a0 != 0xFF ? 1u << a0 : 0u) | - (a1 != 0xFF ? 1u << a1 : 0u) | - (a2 != 0xFF ? 1u << a2 : 0u) | - (a3 != 0xFF ? 1u << a3 : 0u) | - (a4 != 0xFF ? 1u << a4 : 0u) | - (a5 != 0xFF ? 1u << a5 : 0u) | - (a6 != 0xFF ? 1u << a6 : 0u) | - (a7 != 0xFF ? 1u << a7 : 0u) ; - } - - inline uint32_t preservedRegs(uint32_t group) const noexcept { - ASMJIT_ASSERT(group < BaseReg::kGroupVirt); - return _preservedRegs[group]; - } - - inline void setPreservedRegs(uint32_t group, uint32_t regs) noexcept { - ASMJIT_ASSERT(group < BaseReg::kGroupVirt); - _preservedRegs[group] = regs; - } - - //! \} - - //! \name Static Functions - //! \{ - - static inline bool isX86Family(uint32_t ccId) noexcept { return ccId >= _kIdX86Start && ccId <= _kIdX64End; } - static inline bool isArmFamily(uint32_t ccId) noexcept { return ccId >= _kIdArmStart && ccId <= _kIdArmEnd; } - - //! \} -}; - -//! \} - -ASMJIT_END_NAMESPACE - -#endif // _ASMJIT_CORE_CALLCONV_H diff --git a/libraries/asmjit/asmjit/core/codebufferwriter_p.h b/libraries/asmjit/asmjit/core/codebufferwriter_p.h deleted file mode 100644 index d6f44fc92b7..00000000000 --- a/libraries/asmjit/asmjit/core/codebufferwriter_p.h +++ /dev/null @@ -1,171 +0,0 @@ -// [AsmJit] -// Machine Code Generation for C++. -// -// [License] -// Zlib - See LICENSE.md file in the package. - -#ifndef _ASMJIT_CORE_CODEBUFFERWRITER_P_H -#define _ASMJIT_CORE_CODEBUFFERWRITER_P_H - -#include "../core/assembler.h" -#include "../core/support.h" - -ASMJIT_BEGIN_NAMESPACE - -//! \cond INTERNAL -//! \addtogroup asmjit_core -//! \{ - -// ============================================================================ -// [asmjit::CodeBufferWriter] -// ============================================================================ - -//! Helper that is used to write into a `CodeBuffer` held by `BaseAssembler`. -class CodeBufferWriter { -public: - uint8_t* _cursor; - - ASMJIT_INLINE explicit CodeBufferWriter(BaseAssembler* a) noexcept - : _cursor(a->_bufferPtr) {} - - ASMJIT_INLINE Error ensureSpace(BaseAssembler* a, size_t n) noexcept { - size_t remainingSpace = (size_t)(a->_bufferEnd - _cursor); - if (ASMJIT_UNLIKELY(remainingSpace < n)) { - CodeBuffer& buffer = a->_section->_buffer; - Error err = a->_code->growBuffer(&buffer, n); - if (ASMJIT_UNLIKELY(err)) - return a->reportError(err); - _cursor = a->_bufferPtr; - } - return kErrorOk; - } - - ASMJIT_INLINE uint8_t* cursor() const noexcept { return _cursor; } - ASMJIT_INLINE void setCursor(uint8_t* cursor) noexcept { _cursor = cursor; } - ASMJIT_INLINE void advance(size_t n) noexcept { _cursor += n; } - - ASMJIT_INLINE size_t offsetFrom(uint8_t* from) const noexcept { - ASMJIT_ASSERT(_cursor >= from); - return (size_t)(_cursor - from); - } - - template - ASMJIT_INLINE void emit8(T val) noexcept { - typedef typename std::make_unsigned::type U; - _cursor[0] = uint8_t(U(val) & U(0xFF)); - _cursor++; - } - - template - ASMJIT_INLINE void emit8If(T val, Y cond) noexcept { - typedef typename std::make_unsigned::type U; - ASMJIT_ASSERT(size_t(cond) <= 1u); - - _cursor[0] = uint8_t(U(val) & U(0xFF)); - _cursor += size_t(cond); - } - - template - ASMJIT_INLINE void emit16uLE(T val) noexcept { - typedef typename std::make_unsigned::type U; - Support::writeU16uLE(_cursor, uint32_t(U(val) & 0xFFFFu)); - _cursor += 2; - } - - template - ASMJIT_INLINE void emit16uBE(T val) noexcept { - typedef typename std::make_unsigned::type U; - Support::writeU16uBE(_cursor, uint32_t(U(val) & 0xFFFFu)); - _cursor += 2; - } - - template - ASMJIT_INLINE void emit32uLE(T val) noexcept { - typedef typename std::make_unsigned::type U; - Support::writeU32uLE(_cursor, uint32_t(U(val) & 0xFFFFFFFFu)); - _cursor += 4; - } - - template - ASMJIT_INLINE void emit32uBE(T val) noexcept { - typedef typename std::make_unsigned::type U; - Support::writeU32uBE(_cursor, uint32_t(U(val) & 0xFFFFFFFFu)); - _cursor += 4; - } - - ASMJIT_INLINE void emitData(const void* data, size_t size) noexcept { - ASMJIT_ASSERT(size != 0); - memcpy(_cursor, data, size); - _cursor += size; - } - - template - ASMJIT_INLINE void emitValueLE(const T& value, size_t size) noexcept { - typedef typename std::make_unsigned::type U; - ASMJIT_ASSERT(size <= sizeof(T)); - - U v = U(value); - for (uint32_t i = 0; i < size; i++) { - _cursor[i] = uint8_t(v & 0xFFu); - v >>= 8; - } - _cursor += size; - } - - template - ASMJIT_INLINE void emitValueBE(const T& value, size_t size) noexcept { - typedef typename std::make_unsigned::type U; - ASMJIT_ASSERT(size <= sizeof(T)); - - U v = U(value); - for (uint32_t i = 0; i < size; i++) { - _cursor[i] = uint8_t(v >> (sizeof(T) - 8)); - v <<= 8; - } - _cursor += size; - } - - ASMJIT_INLINE void emitZeros(size_t size) noexcept { - ASMJIT_ASSERT(size != 0); - memset(_cursor, 0, size); - _cursor += size; - } - - ASMJIT_INLINE void remove8(uint8_t* where) noexcept { - ASMJIT_ASSERT(where < _cursor); - - uint8_t* p = where; - while (++p != _cursor) - p[-1] = p[0]; - _cursor--; - } - - template - ASMJIT_INLINE void insert8(uint8_t* where, T val) noexcept { - uint8_t* p = _cursor; - - while (p != where) { - p[0] = p[-1]; - p--; - } - - *p = uint8_t(val & 0xFF); - _cursor++; - } - - ASMJIT_INLINE void done(BaseAssembler* a) noexcept { - CodeBuffer& buffer = a->_section->_buffer; - size_t newSize = (size_t)(_cursor - a->_bufferData); - ASMJIT_ASSERT(newSize <= buffer.capacity()); - - a->_bufferPtr = _cursor; - buffer._size = Support::max(buffer._size, newSize); - } -}; - -//! \} -//! \endcond - -ASMJIT_END_NAMESPACE - -#endif // _ASMJIT_CORE_CODEBUFFERWRITER_P_H diff --git a/libraries/asmjit/asmjit/core/codeholder.cpp b/libraries/asmjit/asmjit/core/codeholder.cpp deleted file mode 100644 index 4f98d8884f2..00000000000 --- a/libraries/asmjit/asmjit/core/codeholder.cpp +++ /dev/null @@ -1,1109 +0,0 @@ -// [AsmJit] -// Machine Code Generation for C++. -// -// [License] -// Zlib - See LICENSE.md file in the package. - -#define ASMJIT_EXPORTS - -#include "../core/assembler.h" -#include "../core/logging.h" -#include "../core/support.h" - -ASMJIT_BEGIN_NAMESPACE - -// ============================================================================ -// [Globals] -// ============================================================================ - -static const char CodeHolder_addrTabName[] = ".addrtab"; - -//! Encode MOD byte. -static inline uint32_t x86EncodeMod(uint32_t m, uint32_t o, uint32_t rm) noexcept { - return (m << 6) | (o << 3) | rm; -} - -// ============================================================================ -// [asmjit::LabelLinkIterator] -// ============================================================================ - -class LabelLinkIterator { -public: - ASMJIT_INLINE LabelLinkIterator(LabelEntry* le) noexcept { reset(le); } - - ASMJIT_INLINE explicit operator bool() const noexcept { return isValid(); } - ASMJIT_INLINE bool isValid() const noexcept { return _link != nullptr; } - - ASMJIT_INLINE LabelLink* link() const noexcept { return _link; } - ASMJIT_INLINE LabelLink* operator->() const noexcept { return _link; } - - ASMJIT_INLINE void reset(LabelEntry* le) noexcept { - _pPrev = &le->_links; - _link = *_pPrev; - } - - ASMJIT_INLINE void next() noexcept { - _pPrev = &_link->next; - _link = *_pPrev; - } - - ASMJIT_INLINE void resolveAndNext(CodeHolder* code) noexcept { - LabelLink* linkToDelete = _link; - - _link = _link->next; - *_pPrev = _link; - - code->_unresolvedLinkCount--; - code->_allocator.release(linkToDelete, sizeof(LabelLink)); - } - - LabelLink** _pPrev; - LabelLink* _link; -}; - -// ============================================================================ -// [asmjit::ErrorHandler] -// ============================================================================ - -ErrorHandler::ErrorHandler() noexcept {} -ErrorHandler::~ErrorHandler() noexcept {} - -// ============================================================================ -// [asmjit::CodeHolder - Utilities] -// ============================================================================ - -static void CodeHolder_resetInternal(CodeHolder* self, uint32_t resetPolicy) noexcept { - uint32_t i; - const ZoneVector& emitters = self->emitters(); - - i = emitters.size(); - while (i) - self->detach(emitters[--i]); - - // Reset everything into its construction state. - self->_codeInfo.reset(); - self->_emitterOptions = 0; - self->_logger = nullptr; - self->_errorHandler = nullptr; - - // Reset all sections. - uint32_t numSections = self->_sections.size(); - for (i = 0; i < numSections; i++) { - Section* section = self->_sections[i]; - if (section->_buffer.data() && !section->_buffer.isExternal()) - ::free(section->_buffer._data); - section->_buffer._data = nullptr; - section->_buffer._capacity = 0; - } - - // Reset zone allocator and all containers using it. - ZoneAllocator* allocator = self->allocator(); - - self->_emitters.reset(); - self->_namedLabels.reset(); - self->_relocations.reset(); - self->_labelEntries.reset(); - self->_sections.reset(); - - self->_unresolvedLinkCount = 0; - self->_addressTableSection = nullptr; - self->_addressTableEntries.reset(); - - allocator->reset(&self->_zone); - self->_zone.reset(resetPolicy); -} - -static void CodeHolder_modifyEmitterOptions(CodeHolder* self, uint32_t clear, uint32_t add) noexcept { - uint32_t oldOpt = self->_emitterOptions; - uint32_t newOpt = (oldOpt & ~clear) | add; - - if (oldOpt == newOpt) - return; - - // Modify emitter options of `CodeHolder` itself. - self->_emitterOptions = newOpt; - - // Modify emitter options of all attached emitters. - for (BaseEmitter* emitter : self->emitters()) { - emitter->_emitterOptions = (emitter->_emitterOptions & ~clear) | add; - emitter->onUpdateGlobalInstOptions(); - } -} - -// ============================================================================ -// [asmjit::CodeHolder - Construction / Destruction] -// ============================================================================ - -CodeHolder::CodeHolder() noexcept - : _codeInfo(), - _emitterOptions(0), - _logger(nullptr), - _errorHandler(nullptr), - _zone(16384 - Zone::kBlockOverhead), - _allocator(&_zone), - _unresolvedLinkCount(0), - _addressTableSection(nullptr) {} - -CodeHolder::~CodeHolder() noexcept { - CodeHolder_resetInternal(this, Globals::kResetHard); -} - -// ============================================================================ -// [asmjit::CodeHolder - Init / Reset] -// ============================================================================ - -inline void CodeHolder_setSectionDefaultName( - Section* section, - char c0 = 0, char c1 = 0, char c2 = 0, char c3 = 0, - char c4 = 0, char c5 = 0, char c6 = 0, char c7 = 0) noexcept { - - section->_name.u32[0] = Support::bytepack32_4x8(uint8_t(c0), uint8_t(c1), uint8_t(c2), uint8_t(c3)); - section->_name.u32[1] = Support::bytepack32_4x8(uint8_t(c4), uint8_t(c5), uint8_t(c6), uint8_t(c7)); -} - -Error CodeHolder::init(const CodeInfo& info) noexcept { - // Cannot reinitialize if it's locked or there is one or more emitter attached. - if (isInitialized()) - return DebugUtils::errored(kErrorAlreadyInitialized); - - // If we are just initializing there should be no emitters attached. - ASMJIT_ASSERT(_emitters.empty()); - - // Create the default section and insert it to the `_sections` array. - Error err = _sections.willGrow(&_allocator); - if (err == kErrorOk) { - Section* section = _allocator.allocZeroedT
(); - if (ASMJIT_LIKELY(section)) { - section->_flags = Section::kFlagExec | Section::kFlagConst; - CodeHolder_setSectionDefaultName(section, '.', 't', 'e', 'x', 't'); - _sections.appendUnsafe(section); - } - else { - err = DebugUtils::errored(kErrorOutOfMemory); - } - } - - if (ASMJIT_UNLIKELY(err)) { - _zone.reset(); - return err; - } - else { - _codeInfo = info; - return kErrorOk; - } -} - -void CodeHolder::reset(uint32_t resetPolicy) noexcept { - CodeHolder_resetInternal(this, resetPolicy); -} - -// ============================================================================ -// [asmjit::CodeHolder - Attach / Detach] -// ============================================================================ - -Error CodeHolder::attach(BaseEmitter* emitter) noexcept { - // Catch a possible misuse of the API. - if (ASMJIT_UNLIKELY(!emitter)) - return DebugUtils::errored(kErrorInvalidArgument); - - // Invalid emitter, this should not be possible. - uint32_t type = emitter->emitterType(); - if (ASMJIT_UNLIKELY(type == BaseEmitter::kTypeNone || type >= BaseEmitter::kTypeCount)) - return DebugUtils::errored(kErrorInvalidState); - - // This is suspicious, but don't fail if `emitter` is already attached - // to this code holder. This is not error, but it's not recommended. - if (emitter->_code != nullptr) { - if (emitter->_code == this) - return kErrorOk; - return DebugUtils::errored(kErrorInvalidState); - } - - // Reserve the space now as we cannot fail after `onAttach()` succeeded. - ASMJIT_PROPAGATE(_emitters.willGrow(&_allocator, 1)); - ASMJIT_PROPAGATE(emitter->onAttach(this)); - - // Connect CodeHolder <-> BaseEmitter. - ASMJIT_ASSERT(emitter->_code == this); - _emitters.appendUnsafe(emitter); - - return kErrorOk; -} - -Error CodeHolder::detach(BaseEmitter* emitter) noexcept { - if (ASMJIT_UNLIKELY(!emitter)) - return DebugUtils::errored(kErrorInvalidArgument); - - if (ASMJIT_UNLIKELY(emitter->_code != this)) - return DebugUtils::errored(kErrorInvalidState); - - // NOTE: We always detach if we were asked to, if error happens during - // `emitter->onDetach()` we just propagate it, but the BaseEmitter will - // be detached. - Error err = kErrorOk; - if (!emitter->isDestroyed()) - err = emitter->onDetach(this); - - // Disconnect CodeHolder <-> BaseEmitter. - uint32_t index = _emitters.indexOf(emitter); - ASMJIT_ASSERT(index != Globals::kNotFound); - - _emitters.removeAt(index); - emitter->_code = nullptr; - - return err; -} - -// ============================================================================ -// [asmjit::CodeHolder - Emitter Options] -// ============================================================================ - -static constexpr uint32_t kEmitterOptionsFilter = ~uint32_t(BaseEmitter::kOptionLoggingEnabled); - -void CodeHolder::addEmitterOptions(uint32_t options) noexcept { - CodeHolder_modifyEmitterOptions(this, 0, options & kEmitterOptionsFilter); -} - -void CodeHolder::clearEmitterOptions(uint32_t options) noexcept { - CodeHolder_modifyEmitterOptions(this, options & kEmitterOptionsFilter, 0); -} - -// ============================================================================ -// [asmjit::CodeHolder - Logging & Error Handling] -// ============================================================================ - -void CodeHolder::setLogger(Logger* logger) noexcept { - #ifndef ASMJIT_NO_LOGGING - _logger = logger; - uint32_t option = !logger ? uint32_t(0) : uint32_t(BaseEmitter::kOptionLoggingEnabled); - CodeHolder_modifyEmitterOptions(this, BaseEmitter::kOptionLoggingEnabled, option); - #else - ASMJIT_UNUSED(logger); - #endif -} - -// ============================================================================ -// [asmjit::CodeHolder - Code Buffer] -// ============================================================================ - -static Error CodeHolder_reserveInternal(CodeHolder* self, CodeBuffer* cb, size_t n) noexcept { - uint8_t* oldData = cb->_data; - uint8_t* newData; - - if (oldData && !cb->isExternal()) - newData = static_cast(::realloc(oldData, n)); - else - newData = static_cast(::malloc(n)); - - if (ASMJIT_UNLIKELY(!newData)) - return DebugUtils::errored(kErrorOutOfMemory); - - cb->_data = newData; - cb->_capacity = n; - - // Update pointers used by assemblers, if attached. - for (BaseEmitter* emitter : self->emitters()) { - if (emitter->isAssembler()) { - BaseAssembler* a = static_cast(emitter); - if (&a->_section->_buffer == cb) { - size_t offset = a->offset(); - - a->_bufferData = newData; - a->_bufferEnd = newData + n; - a->_bufferPtr = newData + offset; - } - } - } - - return kErrorOk; -} - -Error CodeHolder::growBuffer(CodeBuffer* cb, size_t n) noexcept { - // The size of the section must be valid. - size_t size = cb->size(); - if (ASMJIT_UNLIKELY(n > std::numeric_limits::max() - size)) - return DebugUtils::errored(kErrorOutOfMemory); - - // We can now check if growing the buffer is really necessary. It's unlikely - // that this function is called while there is still room for `n` bytes. - size_t capacity = cb->capacity(); - size_t required = cb->size() + n; - if (ASMJIT_UNLIKELY(required <= capacity)) - return kErrorOk; - - if (cb->isFixed()) - return DebugUtils::errored(kErrorTooLarge); - - size_t kInitialCapacity = 8096; - if (capacity < kInitialCapacity) - capacity = kInitialCapacity; - else - capacity += Globals::kAllocOverhead; - - do { - size_t old = capacity; - if (capacity < Globals::kGrowThreshold) - capacity *= 2; - else - capacity += Globals::kGrowThreshold; - - // Overflow. - if (ASMJIT_UNLIKELY(old > capacity)) - return DebugUtils::errored(kErrorOutOfMemory); - } while (capacity - Globals::kAllocOverhead < required); - - return CodeHolder_reserveInternal(this, cb, capacity - Globals::kAllocOverhead); -} - -Error CodeHolder::reserveBuffer(CodeBuffer* cb, size_t n) noexcept { - size_t capacity = cb->capacity(); - if (n <= capacity) return kErrorOk; - - if (cb->isFixed()) - return DebugUtils::errored(kErrorTooLarge); - - return CodeHolder_reserveInternal(this, cb, n); -} - -// ============================================================================ -// [asmjit::CodeHolder - Sections] -// ============================================================================ - -Error CodeHolder::newSection(Section** sectionOut, const char* name, size_t nameSize, uint32_t flags, uint32_t alignment) noexcept { - *sectionOut = nullptr; - - if (nameSize == SIZE_MAX) - nameSize = strlen(name); - - if (alignment == 0) - alignment = 1; - - if (ASMJIT_UNLIKELY(!Support::isPowerOf2(alignment))) - return DebugUtils::errored(kErrorInvalidArgument); - - if (ASMJIT_UNLIKELY(nameSize > Globals::kMaxSectionNameSize)) - return DebugUtils::errored(kErrorInvalidSectionName); - - uint32_t sectionId = _sections.size(); - if (ASMJIT_UNLIKELY(sectionId == Globals::kInvalidId)) - return DebugUtils::errored(kErrorTooManySections); - - ASMJIT_PROPAGATE(_sections.willGrow(&_allocator)); - Section* section = _allocator.allocZeroedT
(); - - if (ASMJIT_UNLIKELY(!section)) - return DebugUtils::errored(kErrorOutOfMemory); - - section->_id = sectionId; - section->_flags = flags; - section->_alignment = alignment; - memcpy(section->_name.str, name, nameSize); - _sections.appendUnsafe(section); - - *sectionOut = section; - return kErrorOk; -} - -Section* CodeHolder::sectionByName(const char* name, size_t nameSize) const noexcept { - if (nameSize == SIZE_MAX) - nameSize = strlen(name); - - // This could be also put in a hash-table similarly like we do with labels, - // however it's questionable as the number of sections should be pretty low - // in general. Create an issue if this becomes a problem. - if (ASMJIT_UNLIKELY(nameSize <= Globals::kMaxSectionNameSize)) { - for (Section* section : _sections) - if (memcmp(section->_name.str, name, nameSize) == 0 && section->_name.str[nameSize] == '\0') - return section; - } - - return nullptr; -} - -Section* CodeHolder::ensureAddressTableSection() noexcept { - if (_addressTableSection) - return _addressTableSection; - - newSection(&_addressTableSection, CodeHolder_addrTabName, sizeof(CodeHolder_addrTabName) - 1, 0, _codeInfo.gpSize()); - return _addressTableSection; -} - -Error CodeHolder::addAddressToAddressTable(uint64_t address) noexcept { - AddressTableEntry* entry = _addressTableEntries.get(address); - if (entry) - return kErrorOk; - - Section* section = ensureAddressTableSection(); - if (ASMJIT_UNLIKELY(!section)) - return DebugUtils::errored(kErrorOutOfMemory); - - entry = _zone.newT(address); - if (ASMJIT_UNLIKELY(!entry)) - return DebugUtils::errored(kErrorOutOfMemory); - - _addressTableEntries.insert(entry); - section->_virtualSize += _codeInfo.gpSize(); - - return kErrorOk; -} - -// ============================================================================ -// [asmjit::CodeHolder - Labels / Symbols] -// ============================================================================ - -//! Only used to lookup a label from `_namedLabels`. -class LabelByName { -public: - inline LabelByName(const char* key, size_t keySize, uint32_t hashCode) noexcept - : _key(key), - _keySize(uint32_t(keySize)), - _hashCode(hashCode) {} - - inline uint32_t hashCode() const noexcept { return _hashCode; } - - inline bool matches(const LabelEntry* entry) const noexcept { - return entry->nameSize() == _keySize && ::memcmp(entry->name(), _key, _keySize) == 0; - } - - const char* _key; - uint32_t _keySize; - uint32_t _hashCode; -}; - -// Returns a hash of `name` and fixes `nameSize` if it's `SIZE_MAX`. -static uint32_t CodeHolder_hashNameAndGetSize(const char* name, size_t& nameSize) noexcept { - uint32_t hashCode = 0; - if (nameSize == SIZE_MAX) { - size_t i = 0; - for (;;) { - uint8_t c = uint8_t(name[i]); - if (!c) break; - hashCode = Support::hashRound(hashCode, c); - i++; - } - nameSize = i; - } - else { - for (size_t i = 0; i < nameSize; i++) { - uint8_t c = uint8_t(name[i]); - if (ASMJIT_UNLIKELY(!c)) return DebugUtils::errored(kErrorInvalidLabelName); - hashCode = Support::hashRound(hashCode, c); - } - } - return hashCode; -} - -static bool CodeHolder_writeDisplacement(void* dst, int64_t displacement, uint32_t displacementSize) { - if (displacementSize == 4 && Support::isInt32(displacement)) { - Support::writeI32uLE(dst, int32_t(displacement)); - return true; - } - else if (displacementSize == 1 && Support::isInt8(displacement)) { - Support::writeI8(dst, int8_t(displacement)); - return true; - } - - return false; -} - -LabelLink* CodeHolder::newLabelLink(LabelEntry* le, uint32_t sectionId, size_t offset, intptr_t rel) noexcept { - LabelLink* link = _allocator.allocT(); - if (ASMJIT_UNLIKELY(!link)) return nullptr; - - link->next = le->_links; - le->_links = link; - - link->sectionId = sectionId; - link->relocId = Globals::kInvalidId; - link->offset = offset; - link->rel = rel; - - _unresolvedLinkCount++; - return link; -} - -Error CodeHolder::newLabelEntry(LabelEntry** entryOut) noexcept { - *entryOut = 0; - - uint32_t labelId = _labelEntries.size(); - if (ASMJIT_UNLIKELY(labelId == Globals::kInvalidId)) - return DebugUtils::errored(kErrorTooManyLabels); - - ASMJIT_PROPAGATE(_labelEntries.willGrow(&_allocator)); - LabelEntry* le = _allocator.allocZeroedT(); - - if (ASMJIT_UNLIKELY(!le)) - return DebugUtils::errored(kErrorOutOfMemory); - - le->_setId(labelId); - le->_parentId = Globals::kInvalidId; - le->_offset = 0; - _labelEntries.appendUnsafe(le); - - *entryOut = le; - return kErrorOk; -} - -Error CodeHolder::newNamedLabelEntry(LabelEntry** entryOut, const char* name, size_t nameSize, uint32_t type, uint32_t parentId) noexcept { - *entryOut = 0; - uint32_t hashCode = CodeHolder_hashNameAndGetSize(name, nameSize); - - if (ASMJIT_UNLIKELY(nameSize == 0)) - return DebugUtils::errored(kErrorInvalidLabelName); - - if (ASMJIT_UNLIKELY(nameSize > Globals::kMaxLabelNameSize)) - return DebugUtils::errored(kErrorLabelNameTooLong); - - switch (type) { - case Label::kTypeLocal: - if (ASMJIT_UNLIKELY(parentId >= _labelEntries.size())) - return DebugUtils::errored(kErrorInvalidParentLabel); - - hashCode ^= parentId; - break; - - case Label::kTypeGlobal: - if (ASMJIT_UNLIKELY(parentId != Globals::kInvalidId)) - return DebugUtils::errored(kErrorNonLocalLabelCantHaveParent); - - break; - - default: - return DebugUtils::errored(kErrorInvalidArgument); - } - - // Don't allow to insert duplicates. Local labels allow duplicates that have - // different id, this is already accomplished by having a different hashes - // between the same label names having different parent labels. - LabelEntry* le = _namedLabels.get(LabelByName(name, nameSize, hashCode)); - if (ASMJIT_UNLIKELY(le)) - return DebugUtils::errored(kErrorLabelAlreadyDefined); - - Error err = kErrorOk; - uint32_t labelId = _labelEntries.size(); - - if (ASMJIT_UNLIKELY(labelId == Globals::kInvalidId)) - return DebugUtils::errored(kErrorTooManyLabels); - - ASMJIT_PROPAGATE(_labelEntries.willGrow(&_allocator)); - le = _allocator.allocZeroedT(); - - if (ASMJIT_UNLIKELY(!le)) - return DebugUtils::errored(kErrorOutOfMemory); - - le->_hashCode = hashCode; - le->_setId(labelId); - le->_type = uint8_t(type); - le->_parentId = Globals::kInvalidId; - le->_offset = 0; - ASMJIT_PROPAGATE(le->_name.setData(&_zone, name, nameSize)); - - _labelEntries.appendUnsafe(le); - _namedLabels.insert(allocator(), le); - - *entryOut = le; - return err; -} - -uint32_t CodeHolder::labelIdByName(const char* name, size_t nameSize, uint32_t parentId) noexcept { - // TODO: Finalize - parent id is not used here? - ASMJIT_UNUSED(parentId); - - uint32_t hashCode = CodeHolder_hashNameAndGetSize(name, nameSize); - if (ASMJIT_UNLIKELY(!nameSize)) return 0; - - LabelEntry* le = _namedLabels.get(LabelByName(name, nameSize, hashCode)); - return le ? le->id() : uint32_t(Globals::kInvalidId); -} - -ASMJIT_API Error CodeHolder::resolveUnresolvedLinks() noexcept { - if (!hasUnresolvedLinks()) - return kErrorOk; - - Error err = kErrorOk; - for (LabelEntry* le : labelEntries()) { - if (!le->isBound()) - continue; - - LabelLinkIterator link(le); - if (link) { - Support::FastUInt8 of = 0; - Section* toSection = le->section(); - uint64_t toOffset = Support::addOverflow(toSection->offset(), le->offset(), &of); - - do { - uint32_t linkSectionId = link->sectionId; - if (link->relocId == Globals::kInvalidId) { - Section* fromSection = sectionById(linkSectionId); - size_t linkOffset = link->offset; - - CodeBuffer& buf = _sections[linkSectionId]->buffer(); - ASMJIT_ASSERT(linkOffset < buf.size()); - - // Calculate the offset relative to the start of the virtual base. - uint64_t fromOffset = Support::addOverflow(fromSection->offset(), linkOffset, &of); - int64_t displacement = int64_t(toOffset - fromOffset + uint64_t(int64_t(link->rel))); - - if (!of) { - ASMJIT_ASSERT(size_t(linkOffset) < buf.size()); - - // Size of the value we are going to patch. Only BYTE/DWORD is allowed. - uint32_t displacementSize = buf._data[linkOffset]; - ASMJIT_ASSERT(buf.size() - size_t(linkOffset) >= displacementSize); - - // Overwrite a real displacement in the CodeBuffer. - if (CodeHolder_writeDisplacement(buf._data + linkOffset, displacement, displacementSize)) { - link.resolveAndNext(this); - continue; - } - } - - err = DebugUtils::errored(kErrorInvalidDisplacement); - // Falls through to `link.next()`. - } - - link.next(); - } while (link); - } - } - - return err; -} - -ASMJIT_API Error CodeHolder::bindLabel(const Label& label, uint32_t toSectionId, uint64_t toOffset) noexcept { - LabelEntry* le = labelEntry(label); - if (ASMJIT_UNLIKELY(!le)) - return DebugUtils::errored(kErrorInvalidLabel); - - if (ASMJIT_UNLIKELY(toSectionId > _sections.size())) - return DebugUtils::errored(kErrorInvalidSection); - - // Label can be bound only once. - if (ASMJIT_UNLIKELY(le->isBound())) - return DebugUtils::errored(kErrorLabelAlreadyBound); - - // Bind the label. - Section* section = _sections[toSectionId]; - le->_section = section; - le->_offset = toOffset; - - Error err = kErrorOk; - CodeBuffer& buf = section->buffer(); - - // Fix all links to this label we have collected so far if they are within - // the same section. We ignore any inter-section links as these have to be - // fixed later. - LabelLinkIterator link(le); - while (link) { - uint32_t linkSectionId = link->sectionId; - size_t linkOffset = link->offset; - - uint32_t relocId = link->relocId; - if (relocId != Globals::kInvalidId) { - // Adjust relocation data only. - RelocEntry* re = _relocations[relocId]; - re->_payload += toOffset; - re->_targetSectionId = toSectionId; - } - else { - if (linkSectionId != toSectionId) { - link.next(); - continue; - } - - ASMJIT_ASSERT(linkOffset < buf.size()); - int64_t displacement = int64_t(toOffset - uint64_t(linkOffset) + uint64_t(int64_t(link->rel))); - - // Size of the value we are going to patch. Only BYTE/DWORD is allowed. - uint32_t displacementSize = buf._data[linkOffset]; - ASMJIT_ASSERT(buf.size() - size_t(linkOffset) >= displacementSize); - - // Overwrite a real displacement in the CodeBuffer. - if (!CodeHolder_writeDisplacement(buf._data + linkOffset, displacement, displacementSize)) { - err = DebugUtils::errored(kErrorInvalidDisplacement); - link.next(); - continue; - } - } - - link.resolveAndNext(this); - } - - return err; -} - -// ============================================================================ -// [asmjit::BaseEmitter - Relocations] -// ============================================================================ - -Error CodeHolder::newRelocEntry(RelocEntry** dst, uint32_t relocType, uint32_t valueSize) noexcept { - ASMJIT_PROPAGATE(_relocations.willGrow(&_allocator)); - - uint32_t relocId = _relocations.size(); - if (ASMJIT_UNLIKELY(relocId == Globals::kInvalidId)) - return DebugUtils::errored(kErrorTooManyRelocations); - - RelocEntry* re = _allocator.allocZeroedT(); - if (ASMJIT_UNLIKELY(!re)) - return DebugUtils::errored(kErrorOutOfMemory); - - re->_id = relocId; - re->_relocType = uint8_t(relocType); - re->_valueSize = uint8_t(valueSize); - re->_sourceSectionId = Globals::kInvalidId; - re->_targetSectionId = Globals::kInvalidId; - _relocations.appendUnsafe(re); - - *dst = re; - return kErrorOk; -} - -// ============================================================================ -// [asmjit::BaseEmitter - Expression Evaluation] -// ============================================================================ - -static Error CodeHolder_evaluateExpression(CodeHolder* self, Expression* exp, uint64_t* out) noexcept { - uint64_t value[2]; - for (size_t i = 0; i < 2; i++) { - uint64_t v; - switch (exp->valueType[i]) { - case Expression::kValueNone: { - v = 0; - break; - } - - case Expression::kValueConstant: { - v = exp->value[i].constant; - break; - } - - case Expression::kValueLabel: { - LabelEntry* le = exp->value[i].label; - if (!le->isBound()) - return DebugUtils::errored(kErrorExpressionLabelNotBound); - v = le->section()->offset() + le->offset(); - break; - } - - case Expression::kValueExpression: { - Expression* nested = exp->value[i].expression; - ASMJIT_PROPAGATE(CodeHolder_evaluateExpression(self, nested, &v)); - break; - } - - default: - return DebugUtils::errored(kErrorInvalidState); - } - - value[i] = v; - } - - uint64_t result; - uint64_t& a = value[0]; - uint64_t& b = value[1]; - - switch (exp->opType) { - case Expression::kOpAdd: - result = a + b; - break; - - case Expression::kOpSub: - result = a - b; - break; - - case Expression::kOpMul: - result = a * b; - break; - - case Expression::kOpSll: - result = (b > 63) ? uint64_t(0) : uint64_t(a << b); - break; - - case Expression::kOpSrl: - result = (b > 63) ? uint64_t(0) : uint64_t(a >> b); - break; - - case Expression::kOpSra: - result = Support::sar(a, Support::min(b, 63)); - break; - - default: - return DebugUtils::errored(kErrorInvalidState); - } - - *out = result; - return kErrorOk; -} - -// ============================================================================ -// [asmjit::BaseEmitter - Utilities] -// ============================================================================ - -Error CodeHolder::flatten() noexcept { - uint64_t offset = 0; - for (Section* section : _sections) { - uint64_t realSize = section->realSize(); - if (realSize) { - uint64_t alignedOffset = Support::alignUp(offset, section->alignment()); - if (ASMJIT_UNLIKELY(alignedOffset < offset)) - return DebugUtils::errored(kErrorTooLarge); - - Support::FastUInt8 of = 0; - offset = Support::addOverflow(alignedOffset, realSize, &of); - - if (ASMJIT_UNLIKELY(of)) - return DebugUtils::errored(kErrorTooLarge); - } - } - - // Now we know that we can assign offsets of all sections properly. - Section* prev = nullptr; - offset = 0; - for (Section* section : _sections) { - uint64_t realSize = section->realSize(); - if (realSize) - offset = Support::alignUp(offset, section->alignment()); - section->_offset = offset; - - // Make sure the previous section extends a bit to cover the alignment. - if (prev) - prev->_virtualSize = offset - prev->_offset; - - prev = section; - offset += realSize; - } - - return kErrorOk; -} - -size_t CodeHolder::codeSize() const noexcept { - Support::FastUInt8 of = 0; - uint64_t offset = 0; - - for (Section* section : _sections) { - uint64_t realSize = section->realSize(); - - if (realSize) { - uint64_t alignedOffset = Support::alignUp(offset, section->alignment()); - ASMJIT_ASSERT(alignedOffset >= offset); - offset = Support::addOverflow(alignedOffset, realSize, &of); - } - } - - // TODO: Not nice, maybe changing `codeSize()` to return `uint64_t` instead? - if ((sizeof(uint64_t) > sizeof(size_t) && offset > SIZE_MAX) || of) - return SIZE_MAX; - - return size_t(offset); -} - -Error CodeHolder::relocateToBase(uint64_t baseAddress) noexcept { - // Base address must be provided. - if (ASMJIT_UNLIKELY(baseAddress == Globals::kNoBaseAddress)) - return DebugUtils::errored(kErrorInvalidArgument); - - _codeInfo.setBaseAddress(baseAddress); - uint32_t gpSize = _codeInfo.gpSize(); - - Section* addressTableSection = _addressTableSection; - uint32_t addressTableEntryCount = 0; - uint8_t* addressTableEntryData = nullptr; - - if (addressTableSection) { - ASMJIT_PROPAGATE( - reserveBuffer(&addressTableSection->_buffer, size_t(addressTableSection->virtualSize()))); - addressTableEntryData = addressTableSection->_buffer.data(); - } - - // Relocate all recorded locations. - for (const RelocEntry* re : _relocations) { - // Possibly deleted or optimized-out entry. - if (re->relocType() == RelocEntry::kTypeNone) - continue; - - Section* sourceSection = sectionById(re->sourceSectionId()); - Section* targetSection = nullptr; - - if (re->targetSectionId() != Globals::kInvalidId) - targetSection = sectionById(re->targetSectionId()); - - uint64_t value = re->payload(); - uint64_t sectionOffset = sourceSection->offset(); - uint64_t sourceOffset = re->sourceOffset(); - - // Make sure that the `RelocEntry` doesn't go out of bounds. - size_t regionSize = re->leadingSize() + re->valueSize() + re->trailingSize(); - if (ASMJIT_UNLIKELY(re->sourceOffset() >= sourceSection->bufferSize() || - sourceSection->bufferSize() - size_t(re->sourceOffset()) < regionSize)) - return DebugUtils::errored(kErrorInvalidRelocEntry); - - uint8_t* buffer = sourceSection->data(); - size_t valueOffset = size_t(re->sourceOffset()) + re->leadingSize(); - - switch (re->relocType()) { - case RelocEntry::kTypeExpression: { - Expression* expression = (Expression*)(uintptr_t(value)); - ASMJIT_PROPAGATE(CodeHolder_evaluateExpression(this, expression, &value)); - break; - } - - case RelocEntry::kTypeAbsToAbs: { - break; - } - - case RelocEntry::kTypeRelToAbs: { - // Value is currently a relative offset from the start of its section. - // We have to convert it to an absolute offset (including base address). - if (ASMJIT_UNLIKELY(!targetSection)) - return DebugUtils::errored(kErrorInvalidRelocEntry); - - //value += baseAddress + sectionOffset + sourceOffset + regionSize; - value += baseAddress + targetSection->offset(); - break; - } - - case RelocEntry::kTypeAbsToRel: { - value -= baseAddress + sectionOffset + sourceOffset + regionSize; - if (gpSize > 4 && !Support::isInt32(int64_t(value))) - return DebugUtils::errored(kErrorRelocOffsetOutOfRange); - break; - } - - case RelocEntry::kTypeX64AddressEntry: { - if (re->valueSize() != 4 || re->leadingSize() < 2) - return DebugUtils::errored(kErrorInvalidRelocEntry); - - // First try whether a relative 32-bit displacement would work. - value -= baseAddress + sectionOffset + sourceOffset + regionSize; - if (!Support::isInt32(int64_t(value))) { - // Relative 32-bit displacement is not possible, use '.addrtab' section. - AddressTableEntry* atEntry = _addressTableEntries.get(re->payload()); - if (ASMJIT_UNLIKELY(!atEntry)) - return DebugUtils::errored(kErrorInvalidRelocEntry); - - // Cannot be null as we have just matched the `AddressTableEntry`. - ASMJIT_ASSERT(addressTableSection != nullptr); - - if (!atEntry->hasAssignedSlot()) - atEntry->_slot = addressTableEntryCount++; - - size_t atEntryIndex = size_t(atEntry->slot()) * gpSize; - uint64_t addrSrc = sectionOffset + sourceOffset + regionSize; - uint64_t addrDst = addressTableSection->offset() + uint64_t(atEntryIndex); - - value = addrDst - addrSrc; - if (!Support::isInt32(int64_t(value))) - return DebugUtils::errored(kErrorRelocOffsetOutOfRange); - - // Bytes that replace [REX, OPCODE] bytes. - uint32_t byte0 = 0xFF; - uint32_t byte1 = buffer[valueOffset - 1]; - - if (byte1 == 0xE8) { - // Patch CALL/MOD byte to FF /2 (-> 0x15). - byte1 = x86EncodeMod(0, 2, 5); - } - else if (byte1 == 0xE9) { - // Patch JMP/MOD byte to FF /4 (-> 0x25). - byte1 = x86EncodeMod(0, 4, 5); - } - else { - return DebugUtils::errored(kErrorInvalidRelocEntry); - } - - // Patch `jmp/call` instruction. - buffer[valueOffset - 2] = uint8_t(byte0); - buffer[valueOffset - 1] = uint8_t(byte1); - - Support::writeU64uLE(addressTableEntryData + atEntryIndex, re->payload()); - } - break; - } - - default: - return DebugUtils::errored(kErrorInvalidRelocEntry); - } - - switch (re->valueSize()) { - case 1: - Support::writeU8(buffer + valueOffset, uint32_t(value & 0xFFu)); - break; - - case 2: - Support::writeU16uLE(buffer + valueOffset, uint32_t(value & 0xFFFFu)); - break; - - case 4: - Support::writeU32uLE(buffer + valueOffset, uint32_t(value & 0xFFFFFFFFu)); - break; - - case 8: - Support::writeU64uLE(buffer + valueOffset, value); - break; - - default: - return DebugUtils::errored(kErrorInvalidRelocEntry); - } - } - - // Fixup the virtual size of the address table if it's the last section. - if (_sections.last() == addressTableSection) { - size_t addressTableSize = addressTableEntryCount * gpSize; - addressTableSection->_buffer._size = addressTableSize; - addressTableSection->_virtualSize = addressTableSize; - } - - return kErrorOk; -} - -Error CodeHolder::copySectionData(void* dst, size_t dstSize, uint32_t sectionId, uint32_t options) noexcept { - if (ASMJIT_UNLIKELY(!isSectionValid(sectionId))) - return DebugUtils::errored(kErrorInvalidSection); - - Section* section = sectionById(sectionId); - size_t bufferSize = section->bufferSize(); - - if (ASMJIT_UNLIKELY(dstSize < bufferSize)) - return DebugUtils::errored(kErrorInvalidArgument); - - memcpy(dst, section->data(), bufferSize); - - if (bufferSize < dstSize && (options & kCopyWithPadding)) { - size_t paddingSize = dstSize - bufferSize; - memset(static_cast(dst) + bufferSize, 0, paddingSize); - } - - return kErrorOk; -} - -Error CodeHolder::copyFlattenedData(void* dst, size_t dstSize, uint32_t options) noexcept { - size_t end = 0; - for (Section* section : _sections) { - if (section->offset() > dstSize) - return DebugUtils::errored(kErrorInvalidArgument); - - size_t bufferSize = section->bufferSize(); - size_t offset = size_t(section->offset()); - - if (ASMJIT_UNLIKELY(dstSize - offset < bufferSize)) - return DebugUtils::errored(kErrorInvalidArgument); - - uint8_t* dstTarget = static_cast(dst) + offset; - size_t paddingSize = 0; - memcpy(dstTarget, section->data(), bufferSize); - - if ((options & kCopyWithPadding) && bufferSize < section->virtualSize()) { - paddingSize = Support::min(dstSize - offset, size_t(section->virtualSize())) - bufferSize; - memset(dstTarget + bufferSize, 0, paddingSize); - } - - end = Support::max(end, offset + bufferSize + paddingSize); - } - - // TODO: `end` is not used atm, we need an option to also pad anything beyond - // the code in case that the destination was much larger (for example page-size). - - return kErrorOk; -} - -ASMJIT_END_NAMESPACE diff --git a/libraries/asmjit/asmjit/core/codeholder.h b/libraries/asmjit/asmjit/core/codeholder.h deleted file mode 100644 index 3275fdbec41..00000000000 --- a/libraries/asmjit/asmjit/core/codeholder.h +++ /dev/null @@ -1,887 +0,0 @@ -// [AsmJit] -// Machine Code Generation for C++. -// -// [License] -// Zlib - See LICENSE.md file in the package. - -#ifndef _ASMJIT_CORE_CODEHOLDER_H -#define _ASMJIT_CORE_CODEHOLDER_H - -#include "../core/arch.h" -#include "../core/datatypes.h" -#include "../core/operand.h" -#include "../core/string.h" -#include "../core/support.h" -#include "../core/target.h" -#include "../core/zone.h" -#include "../core/zonehash.h" -#include "../core/zonestring.h" -#include "../core/zonetree.h" -#include "../core/zonevector.h" - -ASMJIT_BEGIN_NAMESPACE - -//! \addtogroup asmjit_core -//! \{ - -// ============================================================================ -// [Forward Declarations] -// ============================================================================ - -class BaseEmitter; -class CodeHolder; -class LabelEntry; -class Logger; - -// ============================================================================ -// [asmjit::AlignMode] -// ============================================================================ - -//! Align mode. -enum AlignMode : uint32_t { - kAlignCode = 0, //!< Align executable code. - kAlignData = 1, //!< Align non-executable code. - kAlignZero = 2, //!< Align by a sequence of zeros. - kAlignCount = 3 //!< Count of alignment modes. -}; - -// ============================================================================ -// [asmjit::ErrorHandler] -// ============================================================================ - -//! Error handler can be used to override the default behavior of error handling -//! available to all classes that inherit `BaseEmitter`. -//! -//! Override `ErrorHandler::handleError()` to implement your own error handler. -class ASMJIT_VIRTAPI ErrorHandler { -public: - ASMJIT_BASE_CLASS(ErrorHandler) - - // -------------------------------------------------------------------------- - // [Construction / Destruction] - // -------------------------------------------------------------------------- - - //! Creates a new `ErrorHandler` instance. - ASMJIT_API ErrorHandler() noexcept; - //! Destroys the `ErrorHandler` instance. - ASMJIT_API virtual ~ErrorHandler() noexcept; - - // -------------------------------------------------------------------------- - // [Handle Error] - // -------------------------------------------------------------------------- - - //! Error handler (must be reimplemented). - //! - //! Error handler is called after an error happened and before it's propagated - //! to the caller. There are multiple ways how the error handler can be used: - //! - //! 1. User-based error handling without throwing exception or using C's - //! `longjmp()`. This is for users that don't use exceptions and want - //! customized error handling. - //! - //! 2. Throwing an exception. AsmJit doesn't use exceptions and is completely - //! exception-safe, but you can throw exception from your error handler if - //! this way is the preferred way of handling errors in your project. - //! - //! 3. Using plain old C's `setjmp()` and `longjmp()`. Asmjit always puts - //! `BaseEmitter` to a consistent state before calling `handleError()` - //! so `longjmp()` can be used without any issues to cancel the code - //! generation if an error occurred. There is no difference between - //! exceptions and `longjmp()` from AsmJit's perspective, however, - //! never jump outside of `CodeHolder` and `BaseEmitter` scope as you - //! would leak memory. - virtual void handleError(Error err, const char* message, BaseEmitter* origin) = 0; -}; - -// ============================================================================ -// [asmjit::CodeBuffer] -// ============================================================================ - -//! Code or data buffer. -struct CodeBuffer { - //! The content of the buffer (data). - uint8_t* _data; - //! Number of bytes of `data` used. - size_t _size; - //! Buffer capacity (in bytes). - size_t _capacity; - //! Buffer flags. - uint32_t _flags; - - enum Flags : uint32_t { - //! Buffer is external (not allocated by asmjit). - kFlagIsExternal = 0x00000001u, - //! Buffer is fixed (cannot be reallocated). - kFlagIsFixed = 0x00000002u - }; - - //! \name Accessors - //! \{ - - inline uint32_t flags() const noexcept { return _flags; } - inline bool hasFlag(uint32_t flag) const noexcept { return (_flags & flag) != 0; } - - inline bool isAllocated() const noexcept { return _data != nullptr; } - inline bool isFixed() const noexcept { return hasFlag(kFlagIsFixed); } - inline bool isExternal() const noexcept { return hasFlag(kFlagIsExternal); } - - inline uint8_t* data() noexcept { return _data; } - inline const uint8_t* data() const noexcept { return _data; } - - inline bool empty() const noexcept { return !_size; } - inline size_t size() const noexcept { return _size; } - inline size_t capacity() const noexcept { return _capacity; } - - //! \} -}; - -// ============================================================================ -// [asmjit::Section] -// ============================================================================ - -//! Section entry. -class Section { -public: - //! Section id. - uint32_t _id; - //! Section flags. - uint32_t _flags; - //! Section alignment requirements (0 if no requirements). - uint32_t _alignment; - //! Reserved for future use (padding). - uint32_t _reserved; - //! Offset of this section from base-address. - uint64_t _offset; - //! Virtual size of the section (zero initialized sections). - uint64_t _virtualSize; - //! Section name (max 35 characters, PE allows max 8). - FixedString _name; - //! Code or data buffer. - CodeBuffer _buffer; - - //! Section flags. - enum Flags : uint32_t { - kFlagExec = 0x00000001u, //!< Executable (.text sections). - kFlagConst = 0x00000002u, //!< Read-only (.text and .data sections). - kFlagZero = 0x00000004u, //!< Zero initialized by the loader (BSS). - kFlagInfo = 0x00000008u, //!< Info / comment flag. - kFlagImplicit = 0x80000000u //!< Section created implicitly and can be deleted by `Target`. - }; - - //! \name Accessors - //! \{ - - inline uint32_t id() const noexcept { return _id; } - inline const char* name() const noexcept { return _name.str; } - - inline uint8_t* data() noexcept { return _buffer.data(); } - inline const uint8_t* data() const noexcept { return _buffer.data(); } - - inline uint32_t flags() const noexcept { return _flags; } - inline bool hasFlag(uint32_t flag) const noexcept { return (_flags & flag) != 0; } - inline void addFlags(uint32_t flags) noexcept { _flags |= flags; } - inline void clearFlags(uint32_t flags) noexcept { _flags &= ~flags; } - - inline uint32_t alignment() const noexcept { return _alignment; } - inline void setAlignment(uint32_t alignment) noexcept { _alignment = alignment; } - - inline uint64_t offset() const noexcept { return _offset; } - inline void setOffset(uint64_t offset) noexcept { _offset = offset; } - - //! Returns the virtual size of the section. - //! - //! Virtual size is initially zero and is never changed by AsmJit. It's normal - //! if virtual size is smaller than size returned by `bufferSize()` as the buffer - //! stores real data emitted by assemblers or appended by users. - //! - //! Use `realSize()` to get the real and final size of this section. - inline uint64_t virtualSize() const noexcept { return _virtualSize; } - //! Sets the virtual size of the section. - inline void setVirtualSize(uint64_t virtualSize) noexcept { _virtualSize = virtualSize; } - - //! Returns the buffer size of the section. - inline size_t bufferSize() const noexcept { return _buffer.size(); } - //! Returns the real size of the section calculated from virtual and buffer sizes. - inline uint64_t realSize() const noexcept { return Support::max(virtualSize(), bufferSize()); } - - //! Returns the `CodeBuffer` used by this section. - inline CodeBuffer& buffer() noexcept { return _buffer; } - //! Returns the `CodeBuffer` used by this section (const). - inline const CodeBuffer& buffer() const noexcept { return _buffer; } - - //! \} -}; - -// ============================================================================ -// [asmjit::LabelLink] -// ============================================================================ - -//! Data structure used to link either unbound labels or cross-section links. -struct LabelLink { - //! Next link (single-linked list). - LabelLink* next; - //! Section id where the label is bound. - uint32_t sectionId; - //! Relocation id or Globals::kInvalidId. - uint32_t relocId; - //! Label offset relative to the start of the section. - size_t offset; - //! Inlined rel8/rel32. - intptr_t rel; -}; - -// ============================================================================ -// [asmjit::Expression] -// ============================================================================ - -struct Expression { - enum OpType : uint8_t { - kOpAdd = 0, - kOpSub = 1, - kOpMul = 2, - kOpSll = 3, - kOpSrl = 4, - kOpSra = 5 - }; - - enum ValueType : uint8_t { - kValueNone = 0, - kValueConstant = 1, - kValueLabel = 2, - kValueExpression = 3 - }; - - union Value { - uint64_t constant; - Expression* expression; - LabelEntry* label; - }; - - uint8_t opType; - uint8_t valueType[2]; - uint8_t reserved[5]; - Value value[2]; - - inline void reset() noexcept { memset(this, 0, sizeof(*this)); } - - inline void setValueAsConstant(size_t index, uint64_t constant) noexcept { - valueType[index] = kValueConstant; - value[index].constant = constant; - } - - inline void setValueAsLabel(size_t index, LabelEntry* label) noexcept { - valueType[index] = kValueLabel; - value[index].label = label; - } - - inline void setValueAsExpression(size_t index, Expression* expression) noexcept { - valueType[index] = kValueLabel; - value[index].expression = expression; - } -}; - -// ============================================================================ -// [asmjit::LabelEntry] -// ============================================================================ - -//! Label entry. -//! -//! Contains the following properties: -//! * Label id - This is the only thing that is set to the `Label` operand. -//! * Label name - Optional, used mostly to create executables and libraries. -//! * Label type - Type of the label, default `Label::kTypeAnonymous`. -//! * Label parent id - Derived from many assemblers that allow to define a -//! local label that falls under a global label. This allows to define -//! many labels of the same name that have different parent (global) label. -//! * Offset - offset of the label bound by `Assembler`. -//! * Links - single-linked list that contains locations of code that has -//! to be patched when the label gets bound. Every use of unbound label -//! adds one link to `_links` list. -//! * HVal - Hash value of label's name and optionally parentId. -//! * HashNext - Hash-table implementation detail. -class LabelEntry : public ZoneHashNode { -public: - // Let's round the size of `LabelEntry` to 64 bytes (as `ZoneAllocator` has - // granularity of 32 bytes anyway). This gives `_name` the remaining space, - // which is should be 16 bytes on 64-bit and 28 bytes on 32-bit architectures. - static constexpr uint32_t kStaticNameSize = - 64 - (sizeof(ZoneHashNode) + 8 + sizeof(Section*) + sizeof(size_t) + sizeof(LabelLink*)); - - //! Label type, see `Label::LabelType`. - uint8_t _type; - //! Must be zero. - uint8_t _flags; - //! Reserved. - uint16_t _reserved16; - //! Label parent id or zero. - uint32_t _parentId; - //! Label offset relative to the start of the `_section`. - uint64_t _offset; - //! Section where the label was bound. - Section* _section; - //! Label links. - LabelLink* _links; - //! Label name. - ZoneString _name; - - //! \name Accessors - //! \{ - - // NOTE: Label id is stored in `_customData`, which is provided by ZoneHashNode - // to fill a padding that a C++ compiler targeting 64-bit CPU will add to align - // the structure to 64-bits. - - //! Returns label id. - inline uint32_t id() const noexcept { return _customData; } - //! Sets label id (internal, used only by `CodeHolder`). - inline void _setId(uint32_t id) noexcept { _customData = id; } - - //! Returns label type, see `Label::LabelType`. - inline uint32_t type() const noexcept { return _type; } - //! Returns label flags, returns 0 at the moment. - inline uint32_t flags() const noexcept { return _flags; } - - //! Tests whether the label has a parent label. - inline bool hasParent() const noexcept { return _parentId != Globals::kInvalidId; } - //! Returns label's parent id. - inline uint32_t parentId() const noexcept { return _parentId; } - - //! Returns the section where the label was bound. - //! - //! If the label was not yet bound the return value is `nullptr`. - inline Section* section() const noexcept { return _section; } - - //! Tests whether the label has name. - inline bool hasName() const noexcept { return !_name.empty(); } - - //! Returns the label's name. - //! - //! \note Local labels will return their local name without their parent - //! part, for example ".L1". - inline const char* name() const noexcept { return _name.data(); } - - //! Returns size of label's name. - //! - //! \note Label name is always null terminated, so you can use `strlen()` to - //! get it, however, it's also cached in `LabelEntry` itself, so if you want - //! to know the size the fastest way is to call `LabelEntry::nameSize()`. - inline uint32_t nameSize() const noexcept { return _name.size(); } - - //! Returns links associated with this label. - inline LabelLink* links() const noexcept { return _links; } - - //! Tests whether the label is bound. - inline bool isBound() const noexcept { return _section != nullptr; } - //! Tests whether the label is bound to a the given `sectionId`. - inline bool isBoundTo(Section* section) const noexcept { return _section == section; } - - //! Returns the label offset (only useful if the label is bound). - inline uint64_t offset() const noexcept { return _offset; } - - //! Returns the hash-value of label's name and its parent label (if any). - //! - //! Label hash is calculated as `HASH(Name) ^ ParentId`. The hash function - //! is implemented in `Support::hashString()` and `Support::hashRound()`. - inline uint32_t hashCode() const noexcept { return _hashCode; } - - //! \} -}; - -// ============================================================================ -// [asmjit::RelocEntry] -// ============================================================================ - -//! Relocation entry. -//! -//! We describe relocation data in the following way: -//! -//! ``` -//! +- Start of the buffer +- End of the data -//! | |*PATCHED*| | or instruction -//! |xxxxxxxxxxxxxxxxxxxxxx|LeadSize|ValueSize|TrailSize|xxxxxxxxxxxxxxxxxxxx-> -//! | -//! +- Source offset -//! ``` -struct RelocEntry { - //! Relocation id. - uint32_t _id; - //! Type of the relocation. - uint8_t _relocType; - //! Size of the relocation data/value (1, 2, 4 or 8 bytes). - uint8_t _valueSize; - //! Number of bytes after `_sourceOffset` to reach the value to be patched. - uint8_t _leadingSize; - //! Number of bytes after `_sourceOffset + _valueSize` to reach end of the - //! instruction. - uint8_t _trailingSize; - //! Source section id. - uint32_t _sourceSectionId; - //! Target section id. - uint32_t _targetSectionId; - //! Source offset (relative to start of the section). - uint64_t _sourceOffset; - //! Payload (target offset, target address, expression, etc). - uint64_t _payload; - - //! Relocation type. - enum RelocType : uint32_t { - //! None/deleted (no relocation). - kTypeNone = 0, - //! Expression evaluation, `_payload` is pointer to `Expression`. - kTypeExpression = 1, - //! Relocate absolute to absolute. - kTypeAbsToAbs = 2, - //! Relocate relative to absolute. - kTypeRelToAbs = 3, - //! Relocate absolute to relative. - kTypeAbsToRel = 4, - //! Relocate absolute to relative or use trampoline. - kTypeX64AddressEntry = 5 - }; - - //! \name Accessors - //! \{ - - inline uint32_t id() const noexcept { return _id; } - - inline uint32_t relocType() const noexcept { return _relocType; } - inline uint32_t valueSize() const noexcept { return _valueSize; } - - inline uint32_t leadingSize() const noexcept { return _leadingSize; } - inline uint32_t trailingSize() const noexcept { return _trailingSize; } - - inline uint32_t sourceSectionId() const noexcept { return _sourceSectionId; } - inline uint32_t targetSectionId() const noexcept { return _targetSectionId; } - - inline uint64_t sourceOffset() const noexcept { return _sourceOffset; } - inline uint64_t payload() const noexcept { return _payload; } - - Expression* payloadAsExpression() const noexcept { - return reinterpret_cast(uintptr_t(_payload)); - } - - //! \} -}; - -// ============================================================================ -// [asmjit::AddressTableEntry] -// ============================================================================ - -class AddressTableEntry : public ZoneTreeNodeT { -public: - ASMJIT_NONCOPYABLE(AddressTableEntry) - - uint64_t _address; - uint32_t _slot; - - //! \name Construction & Destruction - //! \{ - - inline explicit AddressTableEntry(uint64_t address) noexcept - : _address(address), - _slot(0xFFFFFFFFu) {} - - //! \} - - //! \name Accessors - //! \{ - - inline uint64_t address() const noexcept { return _address; } - inline uint32_t slot() const noexcept { return _slot; } - - inline bool hasAssignedSlot() const noexcept { return _slot != 0xFFFFFFFFu; } - - inline bool operator<(const AddressTableEntry& other) const noexcept { return _address < other._address; } - inline bool operator>(const AddressTableEntry& other) const noexcept { return _address > other._address; } - - inline bool operator<(uint64_t queryAddress) const noexcept { return _address < queryAddress; } - inline bool operator>(uint64_t queryAddress) const noexcept { return _address > queryAddress; } - - //! \} -}; - -// ============================================================================ -// [asmjit::CodeHolder] -// ============================================================================ - -//! Contains basic information about the target architecture plus its settings, -//! and holds code & data (including sections, labels, and relocation information). -//! CodeHolder can store both binary and intermediate representation of assembly, -//! which can be generated by `BaseAssembler` and/or `BaseBuilder`. -//! -//! \note `CodeHolder` has ability to attach an `ErrorHandler`, however, the -//! error handler is not triggered by `CodeHolder` itself, it's only used by -//! emitters attached to `CodeHolder`. -class CodeHolder { -public: - ASMJIT_NONCOPYABLE(CodeHolder) - - //! Basic information about the code (architecture and other info). - CodeInfo _codeInfo; - //! Emitter options, propagated to all emitters when changed. - uint32_t _emitterOptions; - - //! Attached `Logger`, used by all consumers. - Logger* _logger; - //! Attached `ErrorHandler`. - ErrorHandler* _errorHandler; - - //! Code zone (used to allocate core structures). - Zone _zone; - //! Zone allocator, used to manage internal containers. - ZoneAllocator _allocator; - - //! Attached code emitters. - ZoneVector _emitters; - //! Section entries. - ZoneVector _sections; - //! Label entries. - ZoneVector _labelEntries; - //! Relocation entries. - ZoneVector _relocations; - //! Label name -> LabelEntry (only named labels). - ZoneHash _namedLabels; - - //! Count of label links, which are not resolved. - size_t _unresolvedLinkCount; - //! Pointer to an address table section (or null if this section doesn't exist). - Section* _addressTableSection; - //! Address table entries. - ZoneTree _addressTableEntries; - - //! \name Construction & Destruction - //! \{ - - //! Creates an uninitialized CodeHolder (you must init() it before it can be used). - ASMJIT_API CodeHolder() noexcept; - //! Destroys the CodeHolder. - ASMJIT_API ~CodeHolder() noexcept; - - inline bool isInitialized() const noexcept { return _codeInfo.isInitialized(); } - - //! Initializes CodeHolder to hold code described by `codeInfo`. - ASMJIT_API Error init(const CodeInfo& info) noexcept; - //! Detaches all code-generators attached and resets the `CodeHolder`. - ASMJIT_API void reset(uint32_t resetPolicy = Globals::kResetSoft) noexcept; - - //! \} - - //! \name Attach & Detach - //! \{ - - //! Attaches an emitter to this `CodeHolder`. - ASMJIT_API Error attach(BaseEmitter* emitter) noexcept; - //! Detaches an emitter from this `CodeHolder`. - ASMJIT_API Error detach(BaseEmitter* emitter) noexcept; - - //! \} - - //! \name Allocators - //! \{ - - inline ZoneAllocator* allocator() const noexcept { return const_cast(&_allocator); } - - //! \} - - //! \name Code Emitter - //! \{ - - inline const ZoneVector& emitters() const noexcept { return _emitters; } - - //! Returns global emitter options, internally propagated to all attached emitters. - inline uint32_t emitterOptions() const noexcept { return _emitterOptions; } - - //! Enables the given global emitter `options` and propagates the resulting - //! options to all attached emitters. - ASMJIT_API void addEmitterOptions(uint32_t options) noexcept; - - //! Disables the given global emitter `options` and propagates the resulting - //! options to all attached emitters. - ASMJIT_API void clearEmitterOptions(uint32_t options) noexcept; - - //! \} - - //! \name Code & Architecture - //! \{ - - //! Returns the target architecture information, see `ArchInfo`. - inline const ArchInfo& archInfo() const noexcept { return _codeInfo.archInfo(); } - //! Returns the target code information, see `CodeInfo`. - inline const CodeInfo& codeInfo() const noexcept { return _codeInfo; } - - //! Returns the target architecture id. - inline uint32_t archId() const noexcept { return archInfo().archId(); } - //! Returns the target architecture sub-id. - inline uint32_t archSubId() const noexcept { return archInfo().archSubId(); } - - //! Tests whether a static base-address is set. - inline bool hasBaseAddress() const noexcept { return _codeInfo.hasBaseAddress(); } - //! Returns a static base-address (uint64_t). - inline uint64_t baseAddress() const noexcept { return _codeInfo.baseAddress(); } - - //! \} - - //! \name Logging & Error Handling - //! \{ - - //! Returns the attached logger. - inline Logger* logger() const noexcept { return _logger; } - //! Attaches a `logger` to CodeHolder and propagates it to all attached emitters. - ASMJIT_API void setLogger(Logger* logger) noexcept; - //! Resets the logger to none. - inline void resetLogger() noexcept { setLogger(nullptr); } - - //! Tests whether the global error handler is attached. - inline bool hasErrorHandler() const noexcept { return _errorHandler != nullptr; } - //! Returns the global error handler. - inline ErrorHandler* errorHandler() const noexcept { return _errorHandler; } - //! Sets the global error handler. - inline void setErrorHandler(ErrorHandler* handler) noexcept { _errorHandler = handler; } - //! Resets the global error handler to none. - inline void resetErrorHandler() noexcept { setErrorHandler(nullptr); } - - //! \} - - //! \name Code Buffer - //! \{ - - ASMJIT_API Error growBuffer(CodeBuffer* cb, size_t n) noexcept; - ASMJIT_API Error reserveBuffer(CodeBuffer* cb, size_t n) noexcept; - - //! \} - - //! \name Sections - //! \{ - - //! Returns an array of `Section*` records. - inline const ZoneVector& sections() const noexcept { return _sections; } - //! Returns the number of sections. - inline uint32_t sectionCount() const noexcept { return _sections.size(); } - - //! Tests whether the given `sectionId` is valid. - inline bool isSectionValid(uint32_t sectionId) const noexcept { return sectionId < _sections.size(); } - - //! Creates a new section and return its pointer in `sectionOut`. - //! - //! Returns `Error`, does not report a possible error to `ErrorHandler`. - ASMJIT_API Error newSection(Section** sectionOut, const char* name, size_t nameSize = SIZE_MAX, uint32_t flags = 0, uint32_t alignment = 1) noexcept; - - //! Returns a section entry of the given index. - inline Section* sectionById(uint32_t sectionId) const noexcept { return _sections[sectionId]; } - - //! Returns section-id that matches the given `name`. - //! - //! If there is no such section `Section::kInvalidId` is returned. - ASMJIT_API Section* sectionByName(const char* name, size_t nameSize = SIZE_MAX) const noexcept; - - //! Returns '.text' section (section that commonly represents code). - //! - //! \note Text section is always the first section in `CodeHolder::sections()` array. - inline Section* textSection() const noexcept { return _sections[0]; } - - //! Tests whether '.addrtab' section exists. - inline bool hasAddressTable() const noexcept { return _addressTableSection != nullptr; } - - //! Returns '.addrtab' section. - //! - //! This section is used exclusively by AsmJit to store absolute 64-bit - //! addresses that cannot be encoded in instructions like 'jmp' or 'call'. - inline Section* addressTableSection() const noexcept { return _addressTableSection; } - - //! Ensures that '.addrtab' section exists (creates it if it doesn't) and - //! returns it. Can return `nullptr` on out of memory condition. - ASMJIT_API Section* ensureAddressTableSection() noexcept; - - //! Used to add an address to an address table. - //! - //! This implicitly calls `ensureAddressTableSection()` and then creates - //! `AddressTableEntry` that is inserted to `_addressTableEntries`. If the - //! address already exists this operation does nothing as the same addresses - //! use the same slot. - //! - //! This function should be considered internal as it's used by assemblers to - //! insert an absolute address into the address table. Inserting address into - //! address table without creating a particula relocation entry makes no sense. - ASMJIT_API Error addAddressToAddressTable(uint64_t address) noexcept; - - //! \} - - //! \name Labels & Symbols - //! \{ - - //! Returns array of `LabelEntry*` records. - inline const ZoneVector& labelEntries() const noexcept { return _labelEntries; } - - //! Returns number of labels created. - inline uint32_t labelCount() const noexcept { return _labelEntries.size(); } - - //! Tests whether the label having `id` is valid (i.e. created by `newLabelEntry()`). - inline bool isLabelValid(uint32_t labelId) const noexcept { - return labelId < _labelEntries.size(); - } - - //! Tests whether the `label` is valid (i.e. created by `newLabelEntry()`). - inline bool isLabelValid(const Label& label) const noexcept { - return label.id() < _labelEntries.size(); - } - - //! \overload - inline bool isLabelBound(uint32_t labelId) const noexcept { - return isLabelValid(labelId) && _labelEntries[labelId]->isBound(); - } - - //! Tests whether the `label` is already bound. - //! - //! Returns `false` if the `label` is not valid. - inline bool isLabelBound(const Label& label) const noexcept { - return isLabelBound(label.id()); - } - - //! Returns LabelEntry of the given label `id`. - inline LabelEntry* labelEntry(uint32_t labelId) const noexcept { - return isLabelValid(labelId) ? _labelEntries[labelId] : static_cast(nullptr); - } - - //! Returns LabelEntry of the given `label`. - inline LabelEntry* labelEntry(const Label& label) const noexcept { - return labelEntry(label.id()); - } - - //! Returns offset of a `Label` by its `labelId`. - //! - //! The offset returned is relative to the start of the section. Zero offset - //! is returned for unbound labels, which is their initial offset value. - inline uint64_t labelOffset(uint32_t labelId) const noexcept { - ASMJIT_ASSERT(isLabelValid(labelId)); - return _labelEntries[labelId]->offset(); - } - - //! \overload - inline uint64_t labelOffset(const Label& label) const noexcept { - return labelOffset(label.id()); - } - - //! Returns offset of a label by it's `labelId` relative to the base offset. - //! - //! \remarks The offset of the section where the label is bound must be valid - //! in order to use this function, otherwise the value returned will not be - //! reliable. - inline uint64_t labelOffsetFromBase(uint32_t labelId) const noexcept { - ASMJIT_ASSERT(isLabelValid(labelId)); - const LabelEntry* le = _labelEntries[labelId]; - return (le->isBound() ? le->section()->offset() : uint64_t(0)) + le->offset(); - } - - //! \overload - inline uint64_t labelOffsetFromBase(const Label& label) const noexcept { - return labelOffsetFromBase(label.id()); - } - - //! Creates a new anonymous label and return its id in `idOut`. - //! - //! Returns `Error`, does not report error to `ErrorHandler`. - ASMJIT_API Error newLabelEntry(LabelEntry** entryOut) noexcept; - - //! Creates a new named label label-type `type`. - //! - //! Returns `Error`, does not report a possible error to `ErrorHandler`. - ASMJIT_API Error newNamedLabelEntry(LabelEntry** entryOut, const char* name, size_t nameSize, uint32_t type, uint32_t parentId = Globals::kInvalidId) noexcept; - - //! Returns a label id by name. - ASMJIT_API uint32_t labelIdByName(const char* name, size_t nameSize = SIZE_MAX, uint32_t parentId = Globals::kInvalidId) noexcept; - - inline Label labelByName(const char* name, size_t nameSize = SIZE_MAX, uint32_t parentId = Globals::kInvalidId) noexcept { - return Label(labelIdByName(name, nameSize, parentId)); - } - - //! Tests whether there are any unresolved label links. - inline bool hasUnresolvedLinks() const noexcept { return _unresolvedLinkCount != 0; } - //! Returns the number of label links, which are unresolved. - inline size_t unresolvedLinkCount() const noexcept { return _unresolvedLinkCount; } - - //! Creates a new label-link used to store information about yet unbound labels. - //! - //! Returns `null` if the allocation failed. - ASMJIT_API LabelLink* newLabelLink(LabelEntry* le, uint32_t sectionId, size_t offset, intptr_t rel) noexcept; - - //! Resolves cross-section links (`LabelLink`) associated with each label that - //! was used as a destination in code of a different section. It's only useful - //! to people that use multiple sections as it will do nothing if the code only - //! contains a single section in which cross-section links are not possible. - ASMJIT_API Error resolveUnresolvedLinks() noexcept; - - //! Binds a label to a given `sectionId` and `offset` (relative to start of the section). - //! - //! This function is generally used by `BaseAssembler::bind()` to do the heavy lifting. - ASMJIT_API Error bindLabel(const Label& label, uint32_t sectionId, uint64_t offset) noexcept; - - //! \} - - //! \name Relocations - //! \{ - - //! Tests whether the code contains relocation entries. - inline bool hasRelocEntries() const noexcept { return !_relocations.empty(); } - //! Returns array of `RelocEntry*` records. - inline const ZoneVector& relocEntries() const noexcept { return _relocations; } - - //! Returns a RelocEntry of the given `id`. - inline RelocEntry* relocEntry(uint32_t id) const noexcept { return _relocations[id]; } - - //! Creates a new relocation entry of type `relocType` and size `valueSize`. - //! - //! Additional fields can be set after the relocation entry was created. - ASMJIT_API Error newRelocEntry(RelocEntry** dst, uint32_t relocType, uint32_t valueSize) noexcept; - - //! \} - - //! \name Utilities - //! \{ - - //! Flattens all sections by recalculating their offsets, starting at 0. - //! - //! \note This should never be called more than once. - ASMJIT_API Error flatten() noexcept; - - //! Returns computed the size of code & data of all sections. - //! - //! \note All sections will be iterated over and the code size returned - //! would represent the minimum code size of all combined sections after - //! applying minimum alignment. Code size may decrease after calling - //! `flatten()` and `relocateToBase()`. - ASMJIT_API size_t codeSize() const noexcept; - - //! Relocates the code to the given `baseAddress`. - //! - //! \param baseAddress Absolute base address where the code will be relocated - //! to. Please note that nothing is copied to such base address, it's just an - //! absolute value used by the relocator to resolve all stored relocations. - //! - //! \note This should never be called more than once. - ASMJIT_API Error relocateToBase(uint64_t baseAddress) noexcept; - - //! Options that can be used with \ref copySectionData(). - enum CopyOptions : uint32_t { - //! If virtual size of the section is larger than the size of its buffer - //! then all bytes between buffer size and virtual size will be zeroed. - kCopyWithPadding = 0x1 - }; - - //! Copies a single section into `dst`. - ASMJIT_API Error copySectionData(void* dst, size_t dstSize, uint32_t sectionId, uint32_t options = 0) noexcept; - - //! Copies all sections into `dst`. - //! - //! This should only be used if the data was flattened and there are no gaps - //! between the sections. The `dstSize` is always checked and the copy will - //! never write anything outside the provided buffer. - ASMJIT_API Error copyFlattenedData(void* dst, size_t dstSize, uint32_t options = 0) noexcept; - - //! \} -}; - -//! \} - -ASMJIT_END_NAMESPACE - -#endif // _ASMJIT_CORE_CODEHOLDER_H diff --git a/libraries/asmjit/asmjit/core/compiler.cpp b/libraries/asmjit/asmjit/core/compiler.cpp deleted file mode 100644 index 69f1f86602a..00000000000 --- a/libraries/asmjit/asmjit/core/compiler.cpp +++ /dev/null @@ -1,556 +0,0 @@ -// [AsmJit] -// Machine Code Generation for C++. -// -// [License] -// Zlib - See LICENSE.md file in the package. - -#define ASMJIT_EXPORTS - -#include "../core/build.h" -#ifndef ASMJIT_NO_COMPILER - -#include "../core/assembler.h" -#include "../core/compiler.h" -#include "../core/cpuinfo.h" -#include "../core/logging.h" -#include "../core/rapass_p.h" -#include "../core/support.h" -#include "../core/type.h" - -ASMJIT_BEGIN_NAMESPACE - -// ============================================================================ -// [asmjit::GlobalConstPoolPass] -// ============================================================================ - -class GlobalConstPoolPass : public Pass { - ASMJIT_NONCOPYABLE(GlobalConstPoolPass) - typedef Pass Base; - - GlobalConstPoolPass() noexcept : Pass("GlobalConstPoolPass") {} - - Error run(Zone* zone, Logger* logger) noexcept override { - ASMJIT_UNUSED(zone); - ASMJIT_UNUSED(logger); - - // Flush the global constant pool. - BaseCompiler* compiler = static_cast(_cb); - if (compiler->_globalConstPool) { - compiler->addAfter(compiler->_globalConstPool, compiler->lastNode()); - compiler->_globalConstPool = nullptr; - } - return kErrorOk; - } -}; - -// ============================================================================ -// [asmjit::FuncCallNode - Arg / Ret] -// ============================================================================ - -bool FuncCallNode::_setArg(uint32_t i, const Operand_& op) noexcept { - if ((i & ~kFuncArgHi) >= _funcDetail.argCount()) - return false; - - _args[i] = op; - return true; -} - -bool FuncCallNode::_setRet(uint32_t i, const Operand_& op) noexcept { - if (i >= 2) - return false; - - _rets[i] = op; - return true; -} - -// ============================================================================ -// [asmjit::BaseCompiler - Construction / Destruction] -// ============================================================================ - -BaseCompiler::BaseCompiler() noexcept - : BaseBuilder(), - _func(nullptr), - _vRegZone(4096 - Zone::kBlockOverhead), - _vRegArray(), - _localConstPool(nullptr), - _globalConstPool(nullptr) { - - _type = kTypeCompiler; -} -BaseCompiler::~BaseCompiler() noexcept {} - -// ============================================================================ -// [asmjit::BaseCompiler - Function API] -// ============================================================================ - -FuncNode* BaseCompiler::newFunc(const FuncSignature& sign) noexcept { - Error err; - - FuncNode* func = newNodeT(); - if (ASMJIT_UNLIKELY(!func)) { - reportError(DebugUtils::errored(kErrorOutOfMemory)); - return nullptr; - } - - err = registerLabelNode(func); - if (ASMJIT_UNLIKELY(err)) { - // TODO: Calls reportError, maybe rethink noexcept? - reportError(err); - return nullptr; - } - - // Create helper nodes. - func->_exitNode = newLabelNode(); - func->_end = newNodeT(SentinelNode::kSentinelFuncEnd); - - if (ASMJIT_UNLIKELY(!func->_exitNode || !func->_end)) { - reportError(DebugUtils::errored(kErrorOutOfMemory)); - return nullptr; - } - - // Initialize the function info. - err = func->detail().init(sign); - if (ASMJIT_UNLIKELY(err)) { - reportError(err); - return nullptr; - } - - // If the Target guarantees greater stack alignment than required by the - // calling convention then override it as we can prevent having to perform - // dynamic stack alignment - if (func->_funcDetail._callConv.naturalStackAlignment() < _codeInfo.stackAlignment()) - func->_funcDetail._callConv.setNaturalStackAlignment(_codeInfo.stackAlignment()); - - // Initialize the function frame. - err = func->_frame.init(func->_funcDetail); - if (ASMJIT_UNLIKELY(err)) { - reportError(err); - return nullptr; - } - - // Allocate space for function arguments. - func->_args = nullptr; - if (func->argCount() != 0) { - func->_args = _allocator.allocT(func->argCount() * sizeof(VirtReg*)); - if (ASMJIT_UNLIKELY(!func->_args)) { - reportError(DebugUtils::errored(kErrorOutOfMemory)); - return nullptr; - } - - memset(func->_args, 0, func->argCount() * sizeof(VirtReg*)); - } - - return func; -} - -FuncNode* BaseCompiler::addFunc(FuncNode* func) { - ASMJIT_ASSERT(_func == nullptr); - _func = func; - - addNode(func); // Function node. - BaseNode* prev = cursor(); // {CURSOR}. - addNode(func->exitNode()); // Function exit label. - addNode(func->endNode()); // Function end marker. - - _setCursor(prev); - return func; -} - -FuncNode* BaseCompiler::addFunc(const FuncSignature& sign) { - FuncNode* func = newFunc(sign); - - if (!func) { - reportError(DebugUtils::errored(kErrorOutOfMemory)); - return nullptr; - } - - return addFunc(func); -} - -Error BaseCompiler::endFunc() { - FuncNode* func = _func; - if (ASMJIT_UNLIKELY(!func)) - return reportError(DebugUtils::errored(kErrorInvalidState)); - - // Add the local constant pool at the end of the function (if exists). - if (_localConstPool) { - setCursor(func->endNode()->prev()); - addNode(_localConstPool); - _localConstPool = nullptr; - } - - // Mark as finished. - _func = nullptr; - - SentinelNode* end = func->endNode(); - setCursor(end); - return kErrorOk; -} - -Error BaseCompiler::setArg(uint32_t argIndex, const BaseReg& r) { - FuncNode* func = _func; - - if (ASMJIT_UNLIKELY(!func)) - return reportError(DebugUtils::errored(kErrorInvalidState)); - - if (ASMJIT_UNLIKELY(!isVirtRegValid(r))) - return reportError(DebugUtils::errored(kErrorInvalidVirtId)); - - VirtReg* vReg = virtRegByReg(r); - func->setArg(argIndex, vReg); - - return kErrorOk; -} - -FuncRetNode* BaseCompiler::newRet(const Operand_& o0, const Operand_& o1) noexcept { - FuncRetNode* node = newNodeT(); - if (!node) { - reportError(DebugUtils::errored(kErrorOutOfMemory)); - return nullptr; - } - - node->setOp(0, o0); - node->setOp(1, o1); - node->setOpCount(!o1.isNone() ? 2u : !o0.isNone() ? 1u : 0u); - - return node; -} - -FuncRetNode* BaseCompiler::addRet(const Operand_& o0, const Operand_& o1) noexcept { - FuncRetNode* node = newRet(o0, o1); - if (!node) return nullptr; - return addNode(node)->as(); -} - -// ============================================================================ -// [asmjit::BaseCompiler - Call] -// ============================================================================ - -FuncCallNode* BaseCompiler::newCall(uint32_t instId, const Operand_& o0, const FuncSignature& sign) noexcept { - FuncCallNode* node = newNodeT(instId, 0u); - if (ASMJIT_UNLIKELY(!node)) { - reportError(DebugUtils::errored(kErrorOutOfMemory)); - return nullptr; - } - - node->setOpCount(1); - node->setOp(0, o0); - node->resetOp(1); - node->resetOp(2); - node->resetOp(3); - - Error err = node->detail().init(sign); - if (ASMJIT_UNLIKELY(err)) { - reportError(err); - return nullptr; - } - - // If there are no arguments skip the allocation. - uint32_t nArgs = sign.argCount(); - if (!nArgs) return node; - - node->_args = static_cast(_allocator.alloc(nArgs * sizeof(Operand))); - if (!node->_args) { - reportError(DebugUtils::errored(kErrorOutOfMemory)); - return nullptr; - } - - memset(node->_args, 0, nArgs * sizeof(Operand)); - return node; -} - -FuncCallNode* BaseCompiler::addCall(uint32_t instId, const Operand_& o0, const FuncSignature& sign) noexcept { - FuncCallNode* node = newCall(instId, o0, sign); - if (!node) return nullptr; - return addNode(node)->as(); -} - -// ============================================================================ -// [asmjit::BaseCompiler - Vars] -// ============================================================================ - -static void CodeCompiler_assignGenericName(BaseCompiler* self, VirtReg* vReg) { - uint32_t index = unsigned(Operand::virtIdToIndex(vReg->_id)); - - char buf[64]; - int size = snprintf(buf, ASMJIT_ARRAY_SIZE(buf), "%%%u", unsigned(index)); - - ASMJIT_ASSERT(size > 0 && size < int(ASMJIT_ARRAY_SIZE(buf))); - vReg->_name.setData(&self->_dataZone, buf, unsigned(size)); -} - -VirtReg* BaseCompiler::newVirtReg(uint32_t typeId, uint32_t signature, const char* name) noexcept { - uint32_t index = _vRegArray.size(); - if (ASMJIT_UNLIKELY(index >= uint32_t(Operand::kVirtIdCount))) - return nullptr; - - if (_vRegArray.willGrow(&_allocator) != kErrorOk) - return nullptr; - - VirtReg* vReg = _vRegZone.allocZeroedT(); - if (ASMJIT_UNLIKELY(!vReg)) return nullptr; - - uint32_t size = Type::sizeOf(typeId); - uint32_t alignment = Support::min(size, 64); - - vReg = new(vReg) VirtReg(Operand::indexToVirtId(index), signature, size, alignment, typeId); - - #ifndef ASMJIT_NO_LOGGING - if (name && name[0] != '\0') - vReg->_name.setData(&_dataZone, name, SIZE_MAX); - else - CodeCompiler_assignGenericName(this, vReg); - #endif - - _vRegArray.appendUnsafe(vReg); - return vReg; -} - -Error BaseCompiler::_newReg(BaseReg& out, uint32_t typeId, const char* name) { - RegInfo regInfo; - - Error err = ArchUtils::typeIdToRegInfo(archId(), typeId, regInfo); - if (ASMJIT_UNLIKELY(err)) return reportError(err); - - VirtReg* vReg = newVirtReg(typeId, regInfo.signature(), name); - if (ASMJIT_UNLIKELY(!vReg)) { - out.reset(); - return reportError(DebugUtils::errored(kErrorOutOfMemory)); - } - - out._initReg(regInfo.signature(), vReg->id()); - return kErrorOk; -} - -Error BaseCompiler::_newReg(BaseReg& out, uint32_t typeId, const char* fmt, va_list ap) { - StringTmp<256> sb; - sb.appendVFormat(fmt, ap); - return _newReg(out, typeId, sb.data()); -} - -Error BaseCompiler::_newReg(BaseReg& out, const BaseReg& ref, const char* name) { - RegInfo regInfo; - uint32_t typeId; - - if (isVirtRegValid(ref)) { - VirtReg* vRef = virtRegByReg(ref); - typeId = vRef->typeId(); - - // NOTE: It's possible to cast one register type to another if it's the - // same register group. However, VirtReg always contains the TypeId that - // was used to create the register. This means that in some cases we may - // end up having different size of `ref` and `vRef`. In such case we - // adjust the TypeId to match the `ref` register type instead of the - // original register type, which should be the expected behavior. - uint32_t typeSize = Type::sizeOf(typeId); - uint32_t refSize = ref.size(); - - if (typeSize != refSize) { - if (Type::isInt(typeId)) { - // GP register - change TypeId to match `ref`, but keep sign of `vRef`. - switch (refSize) { - case 1: typeId = Type::kIdI8 | (typeId & 1); break; - case 2: typeId = Type::kIdI16 | (typeId & 1); break; - case 4: typeId = Type::kIdI32 | (typeId & 1); break; - case 8: typeId = Type::kIdI64 | (typeId & 1); break; - default: typeId = Type::kIdVoid; break; - } - } - else if (Type::isMmx(typeId)) { - // MMX register - always use 64-bit. - typeId = Type::kIdMmx64; - } - else if (Type::isMask(typeId)) { - // Mask register - change TypeId to match `ref` size. - switch (refSize) { - case 1: typeId = Type::kIdMask8; break; - case 2: typeId = Type::kIdMask16; break; - case 4: typeId = Type::kIdMask32; break; - case 8: typeId = Type::kIdMask64; break; - default: typeId = Type::kIdVoid; break; - } - } - else { - // VEC register - change TypeId to match `ref` size, keep vector metadata. - uint32_t elementTypeId = Type::baseOf(typeId); - - switch (refSize) { - case 16: typeId = Type::_kIdVec128Start + (elementTypeId - Type::kIdI8); break; - case 32: typeId = Type::_kIdVec256Start + (elementTypeId - Type::kIdI8); break; - case 64: typeId = Type::_kIdVec512Start + (elementTypeId - Type::kIdI8); break; - default: typeId = Type::kIdVoid; break; - } - } - - if (typeId == Type::kIdVoid) - return reportError(DebugUtils::errored(kErrorInvalidState)); - } - } - else { - typeId = ref.type(); - } - - Error err = ArchUtils::typeIdToRegInfo(archId(), typeId, regInfo); - if (ASMJIT_UNLIKELY(err)) return reportError(err); - - VirtReg* vReg = newVirtReg(typeId, regInfo.signature(), name); - if (ASMJIT_UNLIKELY(!vReg)) { - out.reset(); - return reportError(DebugUtils::errored(kErrorOutOfMemory)); - } - - out._initReg(regInfo.signature(), vReg->id()); - return kErrorOk; -} - -Error BaseCompiler::_newReg(BaseReg& out, const BaseReg& ref, const char* fmt, va_list ap) { - StringTmp<256> sb; - sb.appendVFormat(fmt, ap); - return _newReg(out, ref, sb.data()); -} - -Error BaseCompiler::_newStack(BaseMem& out, uint32_t size, uint32_t alignment, const char* name) { - if (size == 0) - return reportError(DebugUtils::errored(kErrorInvalidArgument)); - - if (alignment == 0) - alignment = 1; - - if (!Support::isPowerOf2(alignment)) - return reportError(DebugUtils::errored(kErrorInvalidArgument)); - - if (alignment > 64) - alignment = 64; - - VirtReg* vReg = newVirtReg(0, 0, name); - if (ASMJIT_UNLIKELY(!vReg)) { - out.reset(); - return reportError(DebugUtils::errored(kErrorOutOfMemory)); - } - - vReg->_virtSize = size; - vReg->_isStack = true; - vReg->_alignment = uint8_t(alignment); - - // Set the memory operand to GPD/GPQ and its id to VirtReg. - out = BaseMem(BaseMem::Decomposed { _gpRegInfo.type(), vReg->id(), BaseReg::kTypeNone, 0, 0, 0, BaseMem::kSignatureMemRegHomeFlag }); - return kErrorOk; -} - -Error BaseCompiler::_newConst(BaseMem& out, uint32_t scope, const void* data, size_t size) { - ConstPoolNode** pPool; - if (scope == ConstPool::kScopeLocal) - pPool = &_localConstPool; - else if (scope == ConstPool::kScopeGlobal) - pPool = &_globalConstPool; - else - return reportError(DebugUtils::errored(kErrorInvalidArgument)); - - ConstPoolNode* pool = *pPool; - if (!pool) { - pool = newConstPoolNode(); - if (ASMJIT_UNLIKELY(!pool)) - return reportError(DebugUtils::errored(kErrorOutOfMemory)); - *pPool = pool; - } - - size_t off; - Error err = pool->add(data, size, off); - - if (ASMJIT_UNLIKELY(err)) - return reportError(err); - - out = BaseMem(BaseMem::Decomposed { - Label::kLabelTag, // Base type. - pool->id(), // Base id. - 0, // Index type. - 0, // Index id. - int32_t(off), // Offset. - uint32_t(size), // Size. - 0 // Flags. - }); - return kErrorOk; -} - -void BaseCompiler::rename(BaseReg& reg, const char* fmt, ...) { - if (!reg.isVirtReg()) return; - - VirtReg* vReg = virtRegById(reg.id()); - if (!vReg) return; - - if (fmt && fmt[0] != '\0') { - char buf[128]; - va_list ap; - - va_start(ap, fmt); - vsnprintf(buf, ASMJIT_ARRAY_SIZE(buf), fmt, ap); - va_end(ap); - - vReg->_name.setData(&_dataZone, buf, SIZE_MAX); - } - else { - CodeCompiler_assignGenericName(this, vReg); - } -} - -// ============================================================================ -// [asmjit::BaseCompiler - Events] -// ============================================================================ - -Error BaseCompiler::onAttach(CodeHolder* code) noexcept { - ASMJIT_PROPAGATE(Base::onAttach(code)); - - Error err = addPassT(); - if (ASMJIT_UNLIKELY(err)) { - onDetach(code); - return err; - } - - return kErrorOk; -} - -Error BaseCompiler::onDetach(CodeHolder* code) noexcept { - _func = nullptr; - _localConstPool = nullptr; - _globalConstPool = nullptr; - - _vRegArray.reset(); - _vRegZone.reset(); - - return Base::onDetach(code); -} - -// ============================================================================ -// [asmjit::FuncPass - Construction / Destruction] -// ============================================================================ - -FuncPass::FuncPass(const char* name) noexcept - : Pass(name) {} - -// ============================================================================ -// [asmjit::FuncPass - Run] -// ============================================================================ - -Error FuncPass::run(Zone* zone, Logger* logger) noexcept { - BaseNode* node = cb()->firstNode(); - if (!node) return kErrorOk; - - do { - if (node->type() == BaseNode::kNodeFunc) { - FuncNode* func = node->as(); - node = func->endNode(); - ASMJIT_PROPAGATE(runOnFunction(zone, logger, func)); - } - - // Find a function by skipping all nodes that are not `kNodeFunc`. - do { - node = node->next(); - } while (node && node->type() != BaseNode::kNodeFunc); - } while (node); - - return kErrorOk; -} - -ASMJIT_END_NAMESPACE - -#endif // !ASMJIT_NO_COMPILER diff --git a/libraries/asmjit/asmjit/core/compiler.h b/libraries/asmjit/asmjit/core/compiler.h deleted file mode 100644 index a59d2bc28b2..00000000000 --- a/libraries/asmjit/asmjit/core/compiler.h +++ /dev/null @@ -1,563 +0,0 @@ -// [AsmJit] -// Machine Code Generation for C++. -// -// [License] -// Zlib - See LICENSE.md file in the package. - -#ifndef _ASMJIT_CORE_COMPILER_H -#define _ASMJIT_CORE_COMPILER_H - -#include "../core/build.h" -#ifndef ASMJIT_NO_COMPILER - -#include "../core/assembler.h" -#include "../core/builder.h" -#include "../core/constpool.h" -#include "../core/func.h" -#include "../core/inst.h" -#include "../core/operand.h" -#include "../core/support.h" -#include "../core/zone.h" -#include "../core/zonevector.h" - -ASMJIT_BEGIN_NAMESPACE - -// ============================================================================ -// [Forward Declarations] -// ============================================================================ - -struct RATiedReg; -class RAWorkReg; - -class FuncNode; -class FuncRetNode; -class FuncCallNode; - -//! \addtogroup asmjit_compiler -//! \{ - -// ============================================================================ -// [asmjit::VirtReg] -// ============================================================================ - -//! Virtual register data (BaseCompiler). -class VirtReg { -public: - ASMJIT_NONCOPYABLE(VirtReg) - - //! Virtual register id. - uint32_t _id; - //! Virtual register info (signature). - RegInfo _info; - //! Virtual register size (can be smaller than `regInfo._size`). - uint32_t _virtSize; - //! Virtual register alignment (for spilling). - uint8_t _alignment; - //! Type-id. - uint8_t _typeId; - //! Virtual register weight for alloc/spill decisions. - uint8_t _weight; - //! True if this is a fixed register, never reallocated. - uint8_t _isFixed : 1; - //! True if the virtual register is only used as a stack (never accessed as register). - uint8_t _isStack : 1; - uint8_t _reserved : 6; - - //! Virtual register name (user provided or automatically generated). - ZoneString<16> _name; - - // ------------------------------------------------------------------------- - // The following members are used exclusively by RAPass. They are initialized - // when the VirtReg is created to NULL pointers and then changed during RAPass - // execution. RAPass sets them back to NULL before it returns. - // ------------------------------------------------------------------------- - - //! Reference to `RAWorkReg`, used during register allocation. - RAWorkReg* _workReg; - - //! \name Construction & Destruction - //! \{ - - inline VirtReg(uint32_t id, uint32_t signature, uint32_t virtSize, uint32_t alignment, uint32_t typeId) noexcept - : _id(id), - _virtSize(virtSize), - _alignment(uint8_t(alignment)), - _typeId(uint8_t(typeId)), - _weight(1), - _isFixed(false), - _isStack(false), - _reserved(0), - _name(), - _workReg(nullptr) { _info._signature = signature; } - - //! \} - - //! \name Accessors - //! \{ - - //! Returns the virtual register id. - inline uint32_t id() const noexcept { return _id; } - - //! Returns the virtual register name. - inline const char* name() const noexcept { return _name.data(); } - //! Returns the size of the virtual register name. - inline uint32_t nameSize() const noexcept { return _name.size(); } - - //! Returns a register information that wraps the register signature. - inline const RegInfo& info() const noexcept { return _info; } - //! Returns a virtual register type (maps to the physical register type as well). - inline uint32_t type() const noexcept { return _info.type(); } - //! Returns a virtual register group (maps to the physical register group as well). - inline uint32_t group() const noexcept { return _info.group(); } - - //! Returns a real size of the register this virtual register maps to. - //! - //! For example if this is a 128-bit SIMD register used for a scalar single - //! precision floating point value then its virtSize would be 4, however, the - //! `regSize` would still say 16 (128-bits), because it's the smallest size - //! of that register type. - inline uint32_t regSize() const noexcept { return _info.size(); } - - //! Returns a register signature of this virtual register. - inline uint32_t signature() const noexcept { return _info.signature(); } - - //! Returns the virtual register size. - //! - //! The virtual register size describes how many bytes the virtual register - //! needs to store its content. It can be smaller than the physical register - //! size, see `regSize()`. - inline uint32_t virtSize() const noexcept { return _virtSize; } - - //! Returns the virtual register alignment. - inline uint32_t alignment() const noexcept { return _alignment; } - - //! Returns the virtual register type id, see `Type::Id`. - inline uint32_t typeId() const noexcept { return _typeId; } - - //! Returns the virtual register weight - the register allocator can use it - //! as explicit hint for alloc/spill decisions. - inline uint32_t weight() const noexcept { return _weight; } - //! Sets the virtual register weight (0 to 255) - the register allocator can - //! use it as explicit hint for alloc/spill decisions and initial bin-packing. - inline void setWeight(uint32_t weight) noexcept { _weight = uint8_t(weight); } - - //! Returns whether the virtual register is always allocated to a fixed - //! physical register (and never reallocated). - //! - //! \note This is only used for special purposes and it's mostly internal. - inline bool isFixed() const noexcept { return bool(_isFixed); } - - //! Returns whether the virtual register is indeed a stack that only uses - //! the virtual register id for making it accessible. - //! - //! \note It's an error if a stack is accessed as a register. - inline bool isStack() const noexcept { return bool(_isStack); } - - inline bool hasWorkReg() const noexcept { return _workReg != nullptr; } - inline RAWorkReg* workReg() const noexcept { return _workReg; } - inline void setWorkReg(RAWorkReg* workReg) noexcept { _workReg = workReg; } - inline void resetWorkReg() noexcept { _workReg = nullptr; } - - //! \} -}; - -// ============================================================================ -// [asmjit::BaseCompiler] -// ============================================================================ - -//! Code emitter that uses virtual registers and performs register allocation. -//! -//! Compiler is a high-level code-generation tool that provides register -//! allocation and automatic handling of function calling conventions. It was -//! primarily designed for merging multiple parts of code into a function -//! without worrying about registers and function calling conventions. -//! -//! BaseCompiler can be used, with a minimum effort, to handle 32-bit and 64-bit -//! code at the same time. -//! -//! BaseCompiler is based on BaseBuilder and contains all the features it -//! provides. It means that the code it stores can be modified (removed, added, -//! injected) and analyzed. When the code is finalized the compiler can emit -//! the code into an Assembler to translate the abstract representation into a -//! machine code. -class ASMJIT_VIRTAPI BaseCompiler : public BaseBuilder { -public: - ASMJIT_NONCOPYABLE(BaseCompiler) - typedef BaseBuilder Base; - - //! Current function. - FuncNode* _func; - //! Allocates `VirtReg` objects. - Zone _vRegZone; - //! Stores array of `VirtReg` pointers. - ZoneVector _vRegArray; - - //! Local constant pool, flushed at the end of each function. - ConstPoolNode* _localConstPool; - //! Global constant pool, flushed by `finalize()`. - ConstPoolNode* _globalConstPool; - - //! \name Construction & Destruction - //! \{ - - //! Creates a new `BaseCompiler` instance. - ASMJIT_API BaseCompiler() noexcept; - //! Destroys the `BaseCompiler` instance. - ASMJIT_API virtual ~BaseCompiler() noexcept; - - //! \} - - //! \name Function API - //! \{ - - //! Returns the current function. - inline FuncNode* func() const noexcept { return _func; } - - //! Creates a new `FuncNode`. - ASMJIT_API FuncNode* newFunc(const FuncSignature& sign) noexcept; - //! Adds a function `node` to the stream. - ASMJIT_API FuncNode* addFunc(FuncNode* func); - //! Adds a new function. - ASMJIT_API FuncNode* addFunc(const FuncSignature& sign); - //! Emits a sentinel that marks the end of the current function. - ASMJIT_API Error endFunc(); - - //! Sets a function argument at `argIndex` to `reg`. - ASMJIT_API Error setArg(uint32_t argIndex, const BaseReg& reg); - - //! Creates a new `FuncRetNode`. - ASMJIT_API FuncRetNode* newRet(const Operand_& o0, const Operand_& o1) noexcept; - //! Adds a new `FuncRetNode`. - ASMJIT_API FuncRetNode* addRet(const Operand_& o0, const Operand_& o1) noexcept; - - //! \} - - //! \name Function Calls - //! \{ - - //! Creates a new `FuncCallNode`. - ASMJIT_API FuncCallNode* newCall(uint32_t instId, const Operand_& o0, const FuncSignature& sign) noexcept; - //! Adds a new `FuncCallNode`. - ASMJIT_API FuncCallNode* addCall(uint32_t instId, const Operand_& o0, const FuncSignature& sign) noexcept; - - //! \} - - //! \name Virtual Registers - //! \{ - - //! Creates a new virtual register representing the given `typeId` and `signature`. - ASMJIT_API VirtReg* newVirtReg(uint32_t typeId, uint32_t signature, const char* name) noexcept; - - ASMJIT_API Error _newReg(BaseReg& out, uint32_t typeId, const char* name = nullptr); - ASMJIT_API Error _newReg(BaseReg& out, uint32_t typeId, const char* fmt, va_list ap); - - ASMJIT_API Error _newReg(BaseReg& out, const BaseReg& ref, const char* name = nullptr); - ASMJIT_API Error _newReg(BaseReg& out, const BaseReg& ref, const char* fmt, va_list ap); - - //! Tests whether the given `id` is a valid virtual register id. - inline bool isVirtIdValid(uint32_t id) const noexcept { - uint32_t index = Operand::virtIdToIndex(id); - return index < _vRegArray.size(); - } - //! Tests whether the given `reg` is a virtual register having a valid id. - inline bool isVirtRegValid(const BaseReg& reg) const noexcept { - return isVirtIdValid(reg.id()); - } - - //! Returns `VirtReg` associated with the given `id`. - inline VirtReg* virtRegById(uint32_t id) const noexcept { - ASMJIT_ASSERT(isVirtIdValid(id)); - return _vRegArray[Operand::virtIdToIndex(id)]; - } - //! Returns `VirtReg` associated with the given `reg`. - inline VirtReg* virtRegByReg(const BaseReg& reg) const noexcept { return virtRegById(reg.id()); } - //! Returns `VirtReg` associated with the given `index`. - inline VirtReg* virtRegByIndex(uint32_t index) const noexcept { return _vRegArray[index]; } - - //! Returns an array of all virtual registers managed by the Compiler. - inline const ZoneVector& virtRegs() const noexcept { return _vRegArray; } - - //! \name Stack - //! \{ - - ASMJIT_API Error _newStack(BaseMem& out, uint32_t size, uint32_t alignment, const char* name = nullptr); - - //! \} - - //! \name Constants - //! \{ - - ASMJIT_API Error _newConst(BaseMem& out, uint32_t scope, const void* data, size_t size); - - //! \} - - //! \name Miscellaneous - //! \{ - - //! Rename the given virtual register `reg` to a formatted string `fmt`. - //! - //! \note Only new name will appear in the logger. - ASMJIT_API void rename(BaseReg& reg, const char* fmt, ...); - - //! \} - - // TODO: These should be removed - inline void alloc(BaseReg& reg) { ASMJIT_UNUSED(reg); } - inline void spill(BaseReg& reg) { ASMJIT_UNUSED(reg); } - - //! \name Events - //! \{ - - ASMJIT_API Error onAttach(CodeHolder* code) noexcept override; - ASMJIT_API Error onDetach(CodeHolder* code) noexcept override; - - //! \} -}; - -// ============================================================================ -// [asmjit::FuncNode] -// ============================================================================ - -//! Function entry (BaseCompiler). -class FuncNode : public LabelNode { -public: - ASMJIT_NONCOPYABLE(FuncNode) - - //! Function detail. - FuncDetail _funcDetail; - //! Function frame. - FuncFrame _frame; - //! Function exit(label). - LabelNode* _exitNode; - //! Function end (sentinel). - SentinelNode* _end; - //! Arguments array as `VirtReg`. - VirtReg** _args; - - //! \name Construction & Destruction - //! \{ - - //! Creates a new `FuncNode` instance. - //! - //! Always use `BaseCompiler::addFunc()` to create `FuncNode`. - ASMJIT_INLINE FuncNode(BaseBuilder* cb) noexcept - : LabelNode(cb), - _funcDetail(), - _frame(), - _exitNode(nullptr), - _end(nullptr), - _args(nullptr) { - setType(kNodeFunc); - } - - //! \} - - //! \{ - //! \name Accessors - - //! Returns function exit `LabelNode`. - inline LabelNode* exitNode() const noexcept { return _exitNode; } - //! Returns function exit label. - inline Label exitLabel() const noexcept { return _exitNode->label(); } - - //! Returns "End of Func" sentinel. - inline SentinelNode* endNode() const noexcept { return _end; } - - //! Returns function declaration. - inline FuncDetail& detail() noexcept { return _funcDetail; } - //! Returns function declaration. - inline const FuncDetail& detail() const noexcept { return _funcDetail; } - - //! Returns function frame. - inline FuncFrame& frame() noexcept { return _frame; } - //! Returns function frame. - inline const FuncFrame& frame() const noexcept { return _frame; } - - //! Returns arguments count. - inline uint32_t argCount() const noexcept { return _funcDetail.argCount(); } - //! Returns returns count. - inline uint32_t retCount() const noexcept { return _funcDetail.retCount(); } - - //! Returns arguments list. - inline VirtReg** args() const noexcept { return _args; } - - //! Returns argument at `i`. - inline VirtReg* arg(uint32_t i) const noexcept { - ASMJIT_ASSERT(i < argCount()); - return _args[i]; - } - - //! Sets argument at `i`. - inline void setArg(uint32_t i, VirtReg* vReg) noexcept { - ASMJIT_ASSERT(i < argCount()); - _args[i] = vReg; - } - - //! Resets argument at `i`. - inline void resetArg(uint32_t i) noexcept { - ASMJIT_ASSERT(i < argCount()); - _args[i] = nullptr; - } - - inline uint32_t attributes() const noexcept { return _frame.attributes(); } - inline void addAttributes(uint32_t attrs) noexcept { _frame.addAttributes(attrs); } - - //! \} -}; - -// ============================================================================ -// [asmjit::FuncRetNode] -// ============================================================================ - -//! Function return (BaseCompiler). -class FuncRetNode : public InstNode { -public: - ASMJIT_NONCOPYABLE(FuncRetNode) - - //! \name Construction & Destruction - //! \{ - - //! Creates a new `FuncRetNode` instance. - inline FuncRetNode(BaseBuilder* cb) noexcept : InstNode(cb, BaseInst::kIdAbstract, 0, 0) { - _any._nodeType = kNodeFuncRet; - } - - //! \} -}; - -// ============================================================================ -// [asmjit::FuncCallNode] -// ============================================================================ - -//! Function call (BaseCompiler). -class FuncCallNode : public InstNode { -public: - ASMJIT_NONCOPYABLE(FuncCallNode) - - //! Function detail. - FuncDetail _funcDetail; - //! Returns. - Operand_ _rets[2]; - //! Arguments. - Operand_* _args; - - //! \name Construction & Destruction - //! \{ - - //! Creates a new `FuncCallNode` instance. - inline FuncCallNode(BaseBuilder* cb, uint32_t instId, uint32_t options) noexcept - : InstNode(cb, instId, options, kBaseOpCapacity), - _funcDetail(), - _args(nullptr) { - setType(kNodeFuncCall); - _resetOps(); - _rets[0].reset(); - _rets[1].reset(); - addFlags(kFlagIsRemovable); - } - - //! \} - - //! \name Accessors - //! \{ - - //! Sets the function signature. - inline Error setSignature(const FuncSignature& sign) noexcept { - return _funcDetail.init(sign); - } - - //! Returns the function detail. - inline FuncDetail& detail() noexcept { return _funcDetail; } - //! Returns the function detail. - inline const FuncDetail& detail() const noexcept { return _funcDetail; } - - //! Returns the target operand. - inline Operand& target() noexcept { return _opArray[0].as(); } - //! \overload - inline const Operand& target() const noexcept { return _opArray[0].as(); } - - //! Returns the number of function arguments. - inline uint32_t argCount() const noexcept { return _funcDetail.argCount(); } - //! Returns the number of function return values. - inline uint32_t retCount() const noexcept { return _funcDetail.retCount(); } - - //! Returns the return value at `i`. - inline Operand& ret(uint32_t i = 0) noexcept { - ASMJIT_ASSERT(i < 2); - return _rets[i].as(); - } - //! \overload - inline const Operand& ret(uint32_t i = 0) const noexcept { - ASMJIT_ASSERT(i < 2); - return _rets[i].as(); - } - - //! Returns the function argument at `i`. - inline Operand& arg(uint32_t i) noexcept { - ASMJIT_ASSERT(i < kFuncArgCountLoHi); - return _args[i].as(); - } - //! \overload - inline const Operand& arg(uint32_t i) const noexcept { - ASMJIT_ASSERT(i < kFuncArgCountLoHi); - return _args[i].as(); - } - - //! Sets the function argument at `i` to `op`. - ASMJIT_API bool _setArg(uint32_t i, const Operand_& op) noexcept; - //! Sets the function return value at `i` to `op`. - ASMJIT_API bool _setRet(uint32_t i, const Operand_& op) noexcept; - - //! Sets the function argument at `i` to `reg`. - inline bool setArg(uint32_t i, const BaseReg& reg) noexcept { return _setArg(i, reg); } - //! Sets the function argument at `i` to `imm`. - inline bool setArg(uint32_t i, const Imm& imm) noexcept { return _setArg(i, imm); } - - //! Sets the function return value at `i` to `var`. - inline bool setRet(uint32_t i, const BaseReg& reg) noexcept { return _setRet(i, reg); } - - //! \} -}; - -// ============================================================================ -// [asmjit::FuncPass] -// ============================================================================ - -class ASMJIT_VIRTAPI FuncPass : public Pass { -public: - ASMJIT_NONCOPYABLE(FuncPass) - typedef Pass Base; - - //! \name Construction & Destruction - //! \{ - - ASMJIT_API FuncPass(const char* name) noexcept; - - //! \} - - //! \name Accessors - //! \{ - - //! Returns the associated `BaseCompiler`. - inline BaseCompiler* cc() const noexcept { return static_cast(_cb); } - - //! \} - - //! \name Run - //! \{ - - //! Calls `runOnFunction()` on each `FuncNode` node found. - ASMJIT_API Error run(Zone* zone, Logger* logger) noexcept override; - - //! Called once per `FuncNode`. - virtual Error runOnFunction(Zone* zone, Logger* logger, FuncNode* func) noexcept = 0; - - //! \} -}; - -//! \} - -ASMJIT_END_NAMESPACE - -#endif // !ASMJIT_NO_COMPILER -#endif // _ASMJIT_CORE_COMPILER_H diff --git a/libraries/asmjit/asmjit/core/constpool.cpp b/libraries/asmjit/asmjit/core/constpool.cpp deleted file mode 100644 index 2c5a513bf19..00000000000 --- a/libraries/asmjit/asmjit/core/constpool.cpp +++ /dev/null @@ -1,359 +0,0 @@ -// [AsmJit] -// Machine Code Generation for C++. -// -// [License] -// Zlib - See LICENSE.md file in the package. - -#define ASMJIT_EXPORTS - -#include "../core/constpool.h" -#include "../core/support.h" - -ASMJIT_BEGIN_NAMESPACE - -// ============================================================================ -// [asmjit::ConstPool - Construction / Destruction] -// ============================================================================ - -ConstPool::ConstPool(Zone* zone) noexcept { reset(zone); } -ConstPool::~ConstPool() noexcept {} - -// ============================================================================ -// [asmjit::ConstPool - Reset] -// ============================================================================ - -void ConstPool::reset(Zone* zone) noexcept { - _zone = zone; - - size_t dataSize = 1; - for (size_t i = 0; i < ASMJIT_ARRAY_SIZE(_tree); i++) { - _tree[i].reset(); - _tree[i].setDataSize(dataSize); - _gaps[i] = nullptr; - dataSize <<= 1; - } - - _gapPool = nullptr; - _size = 0; - _alignment = 0; -} - -// ============================================================================ -// [asmjit::ConstPool - Ops] -// ============================================================================ - -static ASMJIT_INLINE ConstPool::Gap* ConstPool_allocGap(ConstPool* self) noexcept { - ConstPool::Gap* gap = self->_gapPool; - if (!gap) - return self->_zone->allocT(); - - self->_gapPool = gap->_next; - return gap; -} - -static ASMJIT_INLINE void ConstPool_freeGap(ConstPool* self, ConstPool::Gap* gap) noexcept { - gap->_next = self->_gapPool; - self->_gapPool = gap; -} - -static void ConstPool_addGap(ConstPool* self, size_t offset, size_t size) noexcept { - ASMJIT_ASSERT(size > 0); - - while (size > 0) { - size_t gapIndex; - size_t gapSize; - - if (size >= 16 && Support::isAligned(offset, 16)) { - gapIndex = ConstPool::kIndex16; - gapSize = 16; - } - else if (size >= 8 && Support::isAligned(offset, 8)) { - gapIndex = ConstPool::kIndex8; - gapSize = 8; - } - else if (size >= 4 && Support::isAligned(offset, 4)) { - gapIndex = ConstPool::kIndex4; - gapSize = 4; - } - else if (size >= 2 && Support::isAligned(offset, 2)) { - gapIndex = ConstPool::kIndex2; - gapSize = 2; - } - else { - gapIndex = ConstPool::kIndex1; - gapSize = 1; - } - - // We don't have to check for errors here, if this failed nothing really - // happened (just the gap won't be visible) and it will fail again at - // place where the same check would generate `kErrorOutOfMemory` error. - ConstPool::Gap* gap = ConstPool_allocGap(self); - if (!gap) - return; - - gap->_next = self->_gaps[gapIndex]; - self->_gaps[gapIndex] = gap; - - gap->_offset = offset; - gap->_size = gapSize; - - offset += gapSize; - size -= gapSize; - } -} - -Error ConstPool::add(const void* data, size_t size, size_t& dstOffset) noexcept { - size_t treeIndex; - - if (size == 32) - treeIndex = kIndex32; - else if (size == 16) - treeIndex = kIndex16; - else if (size == 8) - treeIndex = kIndex8; - else if (size == 4) - treeIndex = kIndex4; - else if (size == 2) - treeIndex = kIndex2; - else if (size == 1) - treeIndex = kIndex1; - else - return DebugUtils::errored(kErrorInvalidArgument); - - ConstPool::Node* node = _tree[treeIndex].get(data); - if (node) { - dstOffset = node->_offset; - return kErrorOk; - } - - // Before incrementing the current offset try if there is a gap that can - // be used for the requested data. - size_t offset = ~size_t(0); - size_t gapIndex = treeIndex; - - while (gapIndex != kIndexCount - 1) { - ConstPool::Gap* gap = _gaps[treeIndex]; - - // Check if there is a gap. - if (gap) { - size_t gapOffset = gap->_offset; - size_t gapSize = gap->_size; - - // Destroy the gap for now. - _gaps[treeIndex] = gap->_next; - ConstPool_freeGap(this, gap); - - offset = gapOffset; - ASMJIT_ASSERT(Support::isAligned(offset, size)); - - gapSize -= size; - if (gapSize > 0) - ConstPool_addGap(this, gapOffset, gapSize); - } - - gapIndex++; - } - - if (offset == ~size_t(0)) { - // Get how many bytes have to be skipped so the address is aligned accordingly - // to the 'size'. - size_t diff = Support::alignUpDiff(_size, size); - - if (diff != 0) { - ConstPool_addGap(this, _size, diff); - _size += diff; - } - - offset = _size; - _size += size; - } - - // Add the initial node to the right index. - node = ConstPool::Tree::_newNode(_zone, data, size, offset, false); - if (!node) return DebugUtils::errored(kErrorOutOfMemory); - - _tree[treeIndex].insert(node); - _alignment = Support::max(_alignment, size); - - dstOffset = offset; - - // Now create a bunch of shared constants that are based on the data pattern. - // We stop at size 4, it probably doesn't make sense to split constants down - // to 1 byte. - size_t pCount = 1; - while (size > 4) { - size >>= 1; - pCount <<= 1; - - ASMJIT_ASSERT(treeIndex != 0); - treeIndex--; - - const uint8_t* pData = static_cast(data); - for (size_t i = 0; i < pCount; i++, pData += size) { - node = _tree[treeIndex].get(pData); - if (node) continue; - - node = ConstPool::Tree::_newNode(_zone, pData, size, offset + (i * size), true); - _tree[treeIndex].insert(node); - } - } - - return kErrorOk; -} - -// ============================================================================ -// [asmjit::ConstPool - Reset] -// ============================================================================ - -struct ConstPoolFill { - inline ConstPoolFill(uint8_t* dst, size_t dataSize) noexcept : - _dst(dst), - _dataSize(dataSize) {} - - inline void operator()(const ConstPool::Node* node) noexcept { - if (!node->_shared) - memcpy(_dst + node->_offset, node->data(), _dataSize); - } - - uint8_t* _dst; - size_t _dataSize; -}; - -void ConstPool::fill(void* dst) const noexcept { - // Clears possible gaps, asmjit should never emit garbage to the output. - memset(dst, 0, _size); - - ConstPoolFill filler(static_cast(dst), 1); - for (size_t i = 0; i < ASMJIT_ARRAY_SIZE(_tree); i++) { - _tree[i].forEach(filler); - filler._dataSize <<= 1; - } -} - -// ============================================================================ -// [asmjit::ConstPool - Unit] -// ============================================================================ - -#if defined(ASMJIT_TEST) -UNIT(asmjit_core_const_pool) { - Zone zone(32384 - Zone::kBlockOverhead); - ConstPool pool(&zone); - - uint32_t i; - uint32_t kCount = BrokenAPI::hasArg("--quick") ? 1000 : 1000000; - - INFO("Adding %u constants to the pool.", kCount); - { - size_t prevOffset; - size_t curOffset; - uint64_t c = 0x0101010101010101u; - - EXPECT(pool.add(&c, 8, prevOffset) == kErrorOk); - EXPECT(prevOffset == 0); - - for (i = 1; i < kCount; i++) { - c++; - EXPECT(pool.add(&c, 8, curOffset) == kErrorOk); - EXPECT(prevOffset + 8 == curOffset); - EXPECT(pool.size() == (i + 1) * 8); - prevOffset = curOffset; - } - - EXPECT(pool.alignment() == 8); - } - - INFO("Retrieving %u constants from the pool.", kCount); - { - uint64_t c = 0x0101010101010101u; - - for (i = 0; i < kCount; i++) { - size_t offset; - EXPECT(pool.add(&c, 8, offset) == kErrorOk); - EXPECT(offset == i * 8); - c++; - } - } - - INFO("Checking if the constants were split into 4-byte patterns"); - { - uint32_t c = 0x01010101; - for (i = 0; i < kCount; i++) { - size_t offset; - EXPECT(pool.add(&c, 4, offset) == kErrorOk); - EXPECT(offset == i * 8); - c++; - } - } - - INFO("Adding 2 byte constant to misalign the current offset"); - { - uint16_t c = 0xFFFF; - size_t offset; - - EXPECT(pool.add(&c, 2, offset) == kErrorOk); - EXPECT(offset == kCount * 8); - EXPECT(pool.alignment() == 8); - } - - INFO("Adding 8 byte constant to check if pool gets aligned again"); - { - uint64_t c = 0xFFFFFFFFFFFFFFFFu; - size_t offset; - - EXPECT(pool.add(&c, 8, offset) == kErrorOk); - EXPECT(offset == kCount * 8 + 8); - } - - INFO("Adding 2 byte constant to verify the gap is filled"); - { - uint16_t c = 0xFFFE; - size_t offset; - - EXPECT(pool.add(&c, 2, offset) == kErrorOk); - EXPECT(offset == kCount * 8 + 2); - EXPECT(pool.alignment() == 8); - } - - INFO("Checking reset functionality"); - { - pool.reset(&zone); - zone.reset(); - - EXPECT(pool.size() == 0); - EXPECT(pool.alignment() == 0); - } - - INFO("Checking pool alignment when combined constants are added"); - { - uint8_t bytes[32] = { 0 }; - size_t offset; - - pool.add(bytes, 1, offset); - EXPECT(pool.size() == 1); - EXPECT(pool.alignment() == 1); - EXPECT(offset == 0); - - pool.add(bytes, 2, offset); - EXPECT(pool.size() == 4); - EXPECT(pool.alignment() == 2); - EXPECT(offset == 2); - - pool.add(bytes, 4, offset); - EXPECT(pool.size() == 8); - EXPECT(pool.alignment() == 4); - EXPECT(offset == 4); - - pool.add(bytes, 4, offset); - EXPECT(pool.size() == 8); - EXPECT(pool.alignment() == 4); - EXPECT(offset == 4); - - pool.add(bytes, 32, offset); - EXPECT(pool.size() == 64); - EXPECT(pool.alignment() == 32); - EXPECT(offset == 32); - } -} -#endif - -ASMJIT_END_NAMESPACE diff --git a/libraries/asmjit/asmjit/core/constpool.h b/libraries/asmjit/asmjit/core/constpool.h deleted file mode 100644 index 0ff200ec4b3..00000000000 --- a/libraries/asmjit/asmjit/core/constpool.h +++ /dev/null @@ -1,240 +0,0 @@ -// [AsmJit] -// Machine Code Generation for C++. -// -// [License] -// Zlib - See LICENSE.md file in the package. - -#ifndef _ASMJIT_CORE_CONSTPOOL_H -#define _ASMJIT_CORE_CONSTPOOL_H - -#include "../core/support.h" -#include "../core/zone.h" -#include "../core/zonetree.h" - -ASMJIT_BEGIN_NAMESPACE - -//! \addtogroup asmjit_core -//! \{ - -// ============================================================================ -// [asmjit::ConstPool] -// ============================================================================ - -//! Constant pool. -class ConstPool { -public: - ASMJIT_NONCOPYABLE(ConstPool) - - //! Constant pool scope. - enum Scope : uint32_t { - //! Local constant, always embedded right after the current function. - kScopeLocal = 0, - //! Global constant, embedded at the end of the currently compiled code. - kScopeGlobal = 1 - }; - - //! \cond INTERNAL - - //! Index of a given size in const-pool table. - enum Index : uint32_t { - kIndex1 = 0, - kIndex2 = 1, - kIndex4 = 2, - kIndex8 = 3, - kIndex16 = 4, - kIndex32 = 5, - kIndexCount = 6 - }; - - //! Zone-allocated const-pool gap created by two differently aligned constants. - struct Gap { - Gap* _next; //!< Pointer to the next gap - size_t _offset; //!< Offset of the gap. - size_t _size; //!< Remaining bytes of the gap (basically a gap size). - }; - - //! Zone-allocated const-pool node. - class Node : public ZoneTreeNodeT { - public: - ASMJIT_NONCOPYABLE(Node) - - inline Node(size_t offset, bool shared) noexcept - : ZoneTreeNodeT(), - _shared(shared), - _offset(uint32_t(offset)) {} - - inline void* data() const noexcept { - return static_cast(const_cast(this) + 1); - } - - uint32_t _shared : 1; //!< If this constant is shared with another. - uint32_t _offset; //!< Data offset from the beginning of the pool. - }; - - //! Data comparer used internally. - class Compare { - public: - inline Compare(size_t dataSize) noexcept - : _dataSize(dataSize) {} - - inline int operator()(const Node& a, const Node& b) const noexcept { - return ::memcmp(a.data(), b.data(), _dataSize); - } - - inline int operator()(const Node& a, const void* data) const noexcept { - return ::memcmp(a.data(), data, _dataSize); - } - - size_t _dataSize; - }; - - //! Zone-allocated const-pool tree. - struct Tree { - inline explicit Tree(size_t dataSize = 0) noexcept - : _tree(), - _size(0), - _dataSize(dataSize) {} - - inline void reset() noexcept { - _tree.reset(); - _size = 0; - } - - inline bool empty() const noexcept { return _size == 0; } - inline size_t size() const noexcept { return _size; } - - inline void setDataSize(size_t dataSize) noexcept { - ASMJIT_ASSERT(empty()); - _dataSize = dataSize; - } - - inline Node* get(const void* data) noexcept { - Compare cmp(_dataSize); - return _tree.get(data, cmp); - } - - inline void insert(Node* node) noexcept { - Compare cmp(_dataSize); - _tree.insert(node, cmp); - _size++; - } - - template - inline void forEach(Visitor& visitor) const noexcept { - Node* node = _tree.root(); - if (!node) return; - - Node* stack[Globals::kMaxTreeHeight]; - size_t top = 0; - - for (;;) { - Node* left = node->left(); - if (left != nullptr) { - ASMJIT_ASSERT(top != Globals::kMaxTreeHeight); - stack[top++] = node; - - node = left; - continue; - } - - for (;;) { - visitor(node); - node = node->right(); - - if (node != nullptr) - break; - - if (top == 0) - return; - - node = stack[--top]; - } - } - } - - static inline Node* _newNode(Zone* zone, const void* data, size_t size, size_t offset, bool shared) noexcept { - Node* node = zone->allocT(sizeof(Node) + size); - if (ASMJIT_UNLIKELY(!node)) return nullptr; - - node = new(node) Node(offset, shared); - memcpy(node->data(), data, size); - return node; - } - - //! RB tree. - ZoneTree _tree; - //! Size of the tree (number of nodes). - size_t _size; - //! Size of the data. - size_t _dataSize; - }; - - //! \endcond - - //! Zone allocator. - Zone* _zone; - //! Tree per size. - Tree _tree[kIndexCount]; - //! Gaps per size. - Gap* _gaps[kIndexCount]; - //! Gaps pool - Gap* _gapPool; - - //! Size of the pool (in bytes). - size_t _size; - //! Required pool alignment. - size_t _alignment; - - //! \name Construction & Destruction - //! \{ - - ASMJIT_API ConstPool(Zone* zone) noexcept; - ASMJIT_API ~ConstPool() noexcept; - - ASMJIT_API void reset(Zone* zone) noexcept; - - //! \} - - //! \name Accessors - //! \{ - - //! Tests whether the constant-pool is empty. - inline bool empty() const noexcept { return _size == 0; } - //! Returns the size of the constant-pool in bytes. - inline size_t size() const noexcept { return _size; } - //! Returns minimum alignment. - inline size_t alignment() const noexcept { return _alignment; } - - //! \} - - //! \name Utilities - //! \{ - - //! Adds a constant to the constant pool. - //! - //! The constant must have known size, which is 1, 2, 4, 8, 16 or 32 bytes. - //! The constant is added to the pool only if it doesn't not exist, otherwise - //! cached value is returned. - //! - //! AsmJit is able to subdivide added constants, so for example if you add - //! 8-byte constant 0x1122334455667788 it will create the following slots: - //! - //! 8-byte: 0x1122334455667788 - //! 4-byte: 0x11223344, 0x55667788 - //! - //! The reason is that when combining MMX/SSE/AVX code some patterns are used - //! frequently. However, AsmJit is not able to reallocate a constant that has - //! been already added. For example if you try to add 4-byte constant and then - //! 8-byte constant having the same 4-byte pattern as the previous one, two - //! independent slots will be generated by the pool. - ASMJIT_API Error add(const void* data, size_t size, size_t& dstOffset) noexcept; - - //! Fills the destination with the content of this constant pool. - ASMJIT_API void fill(void* dst) const noexcept; -}; - -//! \} - -ASMJIT_END_NAMESPACE - -#endif // _ASMJIT_CORE_CONSTPOOL_H diff --git a/libraries/asmjit/asmjit/core/cpuinfo.cpp b/libraries/asmjit/asmjit/core/cpuinfo.cpp deleted file mode 100644 index 3f41576aa6f..00000000000 --- a/libraries/asmjit/asmjit/core/cpuinfo.cpp +++ /dev/null @@ -1,81 +0,0 @@ -// [AsmJit] -// Machine Code Generation for C++. -// -// [License] -// Zlib - See LICENSE.md file in the package. - -#define ASMJIT_EXPORTS - -#include "../core/cpuinfo.h" - -#if !defined(_WIN32) - #include - #include - #include -#endif - -ASMJIT_BEGIN_NAMESPACE - -// ============================================================================ -// [asmjit::CpuInfo - Detect - CPU NumThreads] -// ============================================================================ - -#if defined(_WIN32) -static inline uint32_t detectHWThreadCount() noexcept { - SYSTEM_INFO info; - ::GetSystemInfo(&info); - return info.dwNumberOfProcessors; -} -#elif defined(_SC_NPROCESSORS_ONLN) -static inline uint32_t detectHWThreadCount() noexcept { - long res = ::sysconf(_SC_NPROCESSORS_ONLN); - return res <= 0 ? uint32_t(1) : uint32_t(res); -} -#else -static inline uint32_t detectHWThreadCount() noexcept { - return 1; -} -#endif - -// ============================================================================ -// [asmjit::CpuInfo - Detect - CPU Features] -// ============================================================================ - -#if defined(ASMJIT_BUILD_X86) && ASMJIT_ARCH_X86 -namespace x86 { void detectCpu(CpuInfo& cpu) noexcept; } -#endif - -#if defined(ASMJIT_BUILD_ARM) && ASMJIT_ARCH_ARM -namespace arm { void detectCpu(CpuInfo& cpu) noexcept; } -#endif - -// ============================================================================ -// [asmjit::CpuInfo - Detect - Static Initializer] -// ============================================================================ - -static uint32_t cpuInfoInitialized; -static CpuInfo cpuInfoGlobal(Globals::NoInit); - -const CpuInfo& CpuInfo::host() noexcept { - // This should never cause a problem as the resulting information should - // always be the same. - if (!cpuInfoInitialized) { - CpuInfo cpuInfoLocal; - - #if defined(ASMJIT_BUILD_X86) && ASMJIT_ARCH_X86 - x86::detectCpu(cpuInfoLocal); - #endif - - #if defined(ASMJIT_BUILD_ARM) && ASMJIT_ARCH_ARM - arm::detectCpu(cpuInfoLocal); - #endif - - cpuInfoLocal._hwThreadCount = detectHWThreadCount(); - cpuInfoGlobal = cpuInfoLocal; - cpuInfoInitialized = 1; - } - - return cpuInfoGlobal; -} - -ASMJIT_END_NAMESPACE diff --git a/libraries/asmjit/asmjit/core/cpuinfo.h b/libraries/asmjit/asmjit/core/cpuinfo.h deleted file mode 100644 index cf9228fc2dd..00000000000 --- a/libraries/asmjit/asmjit/core/cpuinfo.h +++ /dev/null @@ -1,135 +0,0 @@ -// [AsmJit] -// Machine Code Generation for C++. -// -// [License] -// Zlib - See LICENSE.md file in the package. - -#ifndef _ASMJIT_CORE_CPUINFO_H -#define _ASMJIT_CORE_CPUINFO_H - -#include "../core/arch.h" -#include "../core/features.h" -#include "../core/globals.h" -#include "../core/string.h" - -ASMJIT_BEGIN_NAMESPACE - -//! \addtogroup asmjit_support -//! \{ - -// ============================================================================ -// [asmjit::CpuInfo] -// ============================================================================ - -//! CPU information. -class CpuInfo { -public: - //! CPU architecture information. - ArchInfo _archInfo; - //! CPU family ID. - uint32_t _familyId; - //! CPU model ID. - uint32_t _modelId; - //! CPU brand ID. - uint32_t _brandId; - //! CPU stepping. - uint32_t _stepping; - //! Processor type. - uint32_t _processorType; - //! Maximum number of addressable IDs for logical processors. - uint32_t _maxLogicalProcessors; - //! Cache line size (in bytes). - uint32_t _cacheLineSize; - //! Number of hardware threads. - uint32_t _hwThreadCount; - - //! CPU vendor string. - FixedString<16> _vendor; - //! CPU brand string. - FixedString<64> _brand; - //! CPU features. - BaseFeatures _features; - - //! \name Construction & Destruction - //! \{ - - inline CpuInfo() noexcept { reset(); } - inline CpuInfo(const CpuInfo& other) noexcept = default; - - inline explicit CpuInfo(Globals::NoInit_) noexcept - : _archInfo(Globals::NoInit), - _features(Globals::NoInit) {}; - - //! Returns the host CPU information. - ASMJIT_API static const CpuInfo& host() noexcept; - - //! Initializes CpuInfo to the given architecture, see `ArchInfo`. - inline void initArch(uint32_t archId, uint32_t archMode = 0) noexcept { - _archInfo.init(archId, archMode); - } - - inline void reset() noexcept { memset(this, 0, sizeof(*this)); } - - //! \} - - //! \name Overloaded Operators - //! \{ - - inline CpuInfo& operator=(const CpuInfo& other) noexcept = default; - - //! \} - - //! \name Accessors - //! \{ - - //! Returns the CPU architecture information. - inline const ArchInfo& archInfo() const noexcept { return _archInfo; } - //! Returns the CPU architecture id, see `ArchInfo::Id`. - inline uint32_t archId() const noexcept { return _archInfo.archId(); } - //! Returns the CPU architecture sub-id, see `ArchInfo::SubId`. - inline uint32_t archSubId() const noexcept { return _archInfo.archSubId(); } - - //! Returns the CPU family ID. - inline uint32_t familyId() const noexcept { return _familyId; } - //! Returns the CPU model ID. - inline uint32_t modelId() const noexcept { return _modelId; } - //! Returns the CPU brand id. - inline uint32_t brandId() const noexcept { return _brandId; } - //! Returns the CPU stepping. - inline uint32_t stepping() const noexcept { return _stepping; } - //! Returns the processor type. - inline uint32_t processorType() const noexcept { return _processorType; } - //! Returns the number of maximum logical processors. - inline uint32_t maxLogicalProcessors() const noexcept { return _maxLogicalProcessors; } - - //! Returns the size of a cache line flush. - inline uint32_t cacheLineSize() const noexcept { return _cacheLineSize; } - //! Returns number of hardware threads available. - inline uint32_t hwThreadCount() const noexcept { return _hwThreadCount; } - - //! Returns the CPU vendor. - inline const char* vendor() const noexcept { return _vendor.str; } - //! Tests whether the CPU vendor is equal to `s`. - inline bool isVendor(const char* s) const noexcept { return _vendor.eq(s); } - - //! Returns the CPU brand string. - inline const char* brand() const noexcept { return _brand.str; } - - //! Returns all CPU features as `BaseFeatures`, cast to your arch-specific class - //! if needed. - template - inline const T& features() const noexcept { return _features.as(); } - - //! Tests whether the CPU has the given `feature`. - inline bool hasFeature(uint32_t featureId) const noexcept { return _features.has(featureId); } - //! Adds the given CPU `feature` to the list of this CpuInfo features. - inline CpuInfo& addFeature(uint32_t featureId) noexcept { _features.add(featureId); return *this; } - - //! \} -}; - -//! \} - -ASMJIT_END_NAMESPACE - -#endif // _ASMJIT_CORE_CPUINFO_H diff --git a/libraries/asmjit/asmjit/core/emitter.cpp b/libraries/asmjit/asmjit/core/emitter.cpp deleted file mode 100644 index f3190202e53..00000000000 --- a/libraries/asmjit/asmjit/core/emitter.cpp +++ /dev/null @@ -1,257 +0,0 @@ -// [AsmJit] -// Machine Code Generation for C++. -// -// [License] -// Zlib - See LICENSE.md file in the package. - -#define ASMJIT_EXPORTS - -#include "../core/logging.h" -#include "../core/support.h" - -#ifdef ASMJIT_BUILD_X86 - #include "../x86/x86internal_p.h" - #include "../x86/x86instdb_p.h" -#endif // ASMJIT_BUILD_X86 - -#ifdef ASMJIT_BUILD_ARM - #include "../arm/arminternal_p.h" - #include "../arm/arminstdb.h" -#endif // ASMJIT_BUILD_ARM - -ASMJIT_BEGIN_NAMESPACE - -// ============================================================================ -// [asmjit::BaseEmitter - Construction / Destruction] -// ============================================================================ - -BaseEmitter::BaseEmitter(uint32_t type) noexcept - : _type(uint8_t(type)), - _reserved(0), - _flags(0), - _emitterOptions(0), - _code(nullptr), - _errorHandler(nullptr), - _codeInfo(), - _gpRegInfo(), - _privateData(0), - _instOptions(0), - _globalInstOptions(BaseInst::kOptionReserved), - _extraReg(), - _inlineComment(nullptr) {} - -BaseEmitter::~BaseEmitter() noexcept { - if (_code) { - _addFlags(kFlagDestroyed); - _code->detach(this); - } -} - -// ============================================================================ -// [asmjit::BaseEmitter - Code-Generation] -// ============================================================================ - -Error BaseEmitter::_emitOpArray(uint32_t instId, const Operand_* operands, size_t count) { - const Operand_* op = operands; - const Operand& none_ = Globals::none; - - switch (count) { - case 0: return _emit(instId, none_, none_, none_, none_); - case 1: return _emit(instId, op[0], none_, none_, none_); - case 2: return _emit(instId, op[0], op[1], none_, none_); - case 3: return _emit(instId, op[0], op[1], op[2], none_); - case 4: return _emit(instId, op[0], op[1], op[2], op[3]); - case 5: return _emit(instId, op[0], op[1], op[2], op[3], op[4], none_); - case 6: return _emit(instId, op[0], op[1], op[2], op[3], op[4], op[5]); - default: return DebugUtils::errored(kErrorInvalidArgument); - } -} - -// ============================================================================ -// [asmjit::BaseEmitter - Finalize] -// ============================================================================ - -Label BaseEmitter::labelByName(const char* name, size_t nameSize, uint32_t parentId) noexcept { - return Label(_code ? _code->labelIdByName(name, nameSize, parentId) : uint32_t(Globals::kInvalidId)); -} - -// ============================================================================ -// [asmjit::BaseEmitter - Finalize] -// ============================================================================ - -Error BaseEmitter::finalize() { - // Does nothing by default, overridden by `BaseBuilder` and `BaseCompiler`. - return kErrorOk; -} - -// ============================================================================ -// [asmjit::BaseEmitter - Error Handling] -// ============================================================================ - -Error BaseEmitter::reportError(Error err, const char* message) { - ErrorHandler* handler = errorHandler(); - if (!handler) { - if (code()) - handler = code()->errorHandler(); - } - - if (handler) { - if (!message) - message = DebugUtils::errorAsString(err); - handler->handleError(err, message, this); - } - - return err; -} - -// ============================================================================ -// [asmjit::BaseEmitter - Label Management] -// ============================================================================ - -bool BaseEmitter::isLabelValid(uint32_t labelId) const noexcept { - return _code && labelId < _code->labelCount(); -} - -// ============================================================================ -// [asmjit::BaseEmitter - Emit (High-Level)] -// ============================================================================ - -ASMJIT_FAVOR_SIZE Error BaseEmitter::emitProlog(const FuncFrame& frame) { - if (ASMJIT_UNLIKELY(!_code)) - return DebugUtils::errored(kErrorNotInitialized); - - #ifdef ASMJIT_BUILD_X86 - if (archInfo().isX86Family()) - return x86::X86Internal::emitProlog(as(), frame); - #endif - - #ifdef ASMJIT_BUILD_ARM - if (archInfo().isArmFamily()) - return arm::ArmInternal::emitProlog(as(), frame); - #endif - - return DebugUtils::errored(kErrorInvalidArch); -} - -ASMJIT_FAVOR_SIZE Error BaseEmitter::emitEpilog(const FuncFrame& frame) { - if (ASMJIT_UNLIKELY(!_code)) - return DebugUtils::errored(kErrorNotInitialized); - - #ifdef ASMJIT_BUILD_X86 - if (archInfo().isX86Family()) - return x86::X86Internal::emitEpilog(as(), frame); - #endif - - #ifdef ASMJIT_BUILD_ARM - if (archInfo().isArmFamily()) - return arm::ArmInternal::emitEpilog(as(), frame); - #endif - - return DebugUtils::errored(kErrorInvalidArch); -} - -ASMJIT_FAVOR_SIZE Error BaseEmitter::emitArgsAssignment(const FuncFrame& frame, const FuncArgsAssignment& args) { - if (ASMJIT_UNLIKELY(!_code)) - return DebugUtils::errored(kErrorNotInitialized); - - #ifdef ASMJIT_BUILD_X86 - if (archInfo().isX86Family()) - return x86::X86Internal::emitArgsAssignment(as(), frame, args); - #endif - - #ifdef ASMJIT_BUILD_ARM - if (archInfo().isArmFamily()) - return arm::ArmInternal::emitArgsAssignment(as(), frame, args); - #endif - - return DebugUtils::errored(kErrorInvalidArch); -} - -// ============================================================================ -// [asmjit::BaseEmitter - Comment] -// ============================================================================ - -Error BaseEmitter::commentf(const char* fmt, ...) { - if (ASMJIT_UNLIKELY(!_code)) - return DebugUtils::errored(kErrorNotInitialized); - - #ifndef ASMJIT_NO_LOGGING - StringTmp<1024> sb; - - va_list ap; - va_start(ap, fmt); - Error err = sb.appendVFormat(fmt, ap); - va_end(ap); - - if (ASMJIT_UNLIKELY(err)) - return err; - - return comment(sb.data(), sb.size()); - #else - ASMJIT_UNUSED(fmt); - return kErrorOk; - #endif -} - -Error BaseEmitter::commentv(const char* fmt, va_list ap) { - if (ASMJIT_UNLIKELY(!_code)) - return DebugUtils::errored(kErrorNotInitialized); - - #ifndef ASMJIT_NO_LOGGING - StringTmp<1024> sb; - - Error err = sb.appendVFormat(fmt, ap); - if (ASMJIT_UNLIKELY(err)) - return err; - - return comment(sb.data(), sb.size()); - #else - ASMJIT_UNUSED(fmt); - ASMJIT_UNUSED(ap); - return kErrorOk; - #endif -} - -// ============================================================================ -// [asmjit::BaseEmitter - Events] -// ============================================================================ - -Error BaseEmitter::onAttach(CodeHolder* code) noexcept { - _code = code; - _codeInfo = code->codeInfo(); - _emitterOptions = code->emitterOptions(); - - onUpdateGlobalInstOptions(); - return kErrorOk; -} - -Error BaseEmitter::onDetach(CodeHolder* code) noexcept { - ASMJIT_UNUSED(code); - - _flags = 0; - _emitterOptions = 0; - _errorHandler = nullptr; - - _codeInfo.reset(); - _gpRegInfo.reset(); - _privateData = 0; - - _instOptions = 0; - _globalInstOptions = BaseInst::kOptionReserved; - _extraReg.reset(); - _inlineComment = nullptr; - - return kErrorOk; -} - -void BaseEmitter::onUpdateGlobalInstOptions() noexcept { - constexpr uint32_t kCriticalEmitterOptions = - kOptionLoggingEnabled | - kOptionStrictValidation ; - - _globalInstOptions &= ~BaseInst::kOptionReserved; - if ((_emitterOptions & kCriticalEmitterOptions) != 0) - _globalInstOptions |= BaseInst::kOptionReserved; -} - -ASMJIT_END_NAMESPACE diff --git a/libraries/asmjit/asmjit/core/emitter.h b/libraries/asmjit/asmjit/core/emitter.h deleted file mode 100644 index a139634eb0d..00000000000 --- a/libraries/asmjit/asmjit/core/emitter.h +++ /dev/null @@ -1,532 +0,0 @@ -// [AsmJit] -// Machine Code Generation for C++. -// -// [License] -// Zlib - See LICENSE.md file in the package. - -#ifndef _ASMJIT_CORE_EMITTER_H -#define _ASMJIT_CORE_EMITTER_H - -#include "../core/arch.h" -#include "../core/inst.h" -#include "../core/operand.h" -#include "../core/codeholder.h" - -ASMJIT_BEGIN_NAMESPACE - -//! \addtogroup asmjit_core -//! \{ - -// ============================================================================ -// [Forward Declarations] -// ============================================================================ - -class ConstPool; -class FuncFrame; -class FuncArgsAssignment; - -// ============================================================================ -// [asmjit::BaseEmitter] -// ============================================================================ - -//! Provides a base foundation to emit code - specialized by `Assembler` and -//! `BaseBuilder`. -class ASMJIT_VIRTAPI BaseEmitter { -public: - ASMJIT_BASE_CLASS(BaseEmitter) - - //! See `EmitterType`. - uint8_t _type; - //! Reserved for future use. - uint8_t _reserved; - //! See \ref BaseEmitter::Flags. - uint16_t _flags; - //! Emitter options, always in sync with CodeHolder. - uint32_t _emitterOptions; - - //! CodeHolder the BaseEmitter is attached to. - CodeHolder* _code; - //! Attached `ErrorHandler`. - ErrorHandler* _errorHandler; - - //! Basic information about the code (matches CodeHolder::_codeInfo). - CodeInfo _codeInfo; - //! Native GP register signature and signature related information. - RegInfo _gpRegInfo; - //! Internal private data used freely by any emitter. - uint32_t _privateData; - - //! Next instruction options (affects the next instruction). - uint32_t _instOptions; - //! Global Instruction options (combined with `_instOptions` by `emit...()`). - uint32_t _globalInstOptions; - //! Extra register (op-mask {k} on AVX-512) (affects the next instruction). - RegOnly _extraReg; - //! Inline comment of the next instruction (affects the next instruction). - const char* _inlineComment; - - //! Emitter type. - enum EmitterType : uint32_t { - kTypeNone = 0, - kTypeAssembler = 1, - kTypeBuilder = 2, - kTypeCompiler = 3, - kTypeCount = 4 - }; - - //! Emitter flags. - enum Flags : uint32_t { - //! The emitter was finalized. - kFlagFinalized = 0x4000u, - //! The emitter was destroyed. - kFlagDestroyed = 0x8000u - }; - - //! Emitter options. - enum Options : uint32_t { - //! Logging is enabled, `BaseEmitter::logger()` must return a valid logger. - //! This option is set automatically by the emitter if the logger is present. - //! User code should never alter this value. - //! - //! Default `false`. - kOptionLoggingEnabled = 0x00000001u, - - //! Stricly validate each instruction before it's emitted. - //! - //! Default `false`. - kOptionStrictValidation = 0x00000002u, - - //! Emit instructions that are optimized for size, if possible. - //! - //! Default `false`. - //! - //! X86 Specific - //! ------------ - //! - //! When this option is set it the assembler will try to fix instructions - //! if possible into operation equivalent instructions that take less bytes - //! by taking advantage of implicit zero extension. For example instruction - //! like `mov r64, imm` and `and r64, imm` can be translated to `mov r32, imm` - //! and `and r32, imm` when the immediate constant is lesser than `2^31`. - kOptionOptimizedForSize = 0x00000004u, - - //! Emit optimized code-alignment sequences. - //! - //! Default `false`. - //! - //! X86 Specific - //! ------------ - //! - //! Default align sequence used by X86 architecture is one-byte (0x90) - //! opcode that is often shown by disassemblers as NOP. However there are - //! more optimized align sequences for 2-11 bytes that may execute faster - //! on certain CPUs. If this feature is enabled AsmJit will generate - //! specialized sequences for alignment between 2 to 11 bytes. - kOptionOptimizedAlign = 0x00000008u, - - //! Emit jump-prediction hints. - //! - //! Default `false`. - //! - //! X86 Specific - //! ------------ - //! - //! Jump prediction is usually based on the direction of the jump. If the - //! jump is backward it is usually predicted as taken; and if the jump is - //! forward it is usually predicted as not-taken. The reason is that loops - //! generally use backward jumps and conditions usually use forward jumps. - //! However this behavior can be overridden by using instruction prefixes. - //! If this option is enabled these hints will be emitted. - //! - //! This feature is disabled by default, because the only processor that - //! used to take into consideration prediction hints was P4. Newer processors - //! implement heuristics for branch prediction and ignore static hints. This - //! means that this feature can be used for annotation purposes. - kOptionPredictedJumps = 0x00000010u - }; - - //! \name Construction & Destruction - //! \{ - - ASMJIT_API explicit BaseEmitter(uint32_t type) noexcept; - ASMJIT_API virtual ~BaseEmitter() noexcept; - - //! \} - - //! \name Cast - //! \{ - - template - inline T* as() noexcept { return reinterpret_cast(this); } - - template - inline const T* as() const noexcept { return reinterpret_cast(this); } - - //! \} - - //! \name Emitter Type & Flags - //! \{ - - //! Returns the type of this emitter, see `EmitterType`. - inline uint32_t emitterType() const noexcept { return _type; } - //! Returns emitter flags , see `Flags`. - inline uint32_t emitterFlags() const noexcept { return _flags; } - - //! Tests whether the emitter inherits from `BaseAssembler`. - inline bool isAssembler() const noexcept { return _type == kTypeAssembler; } - //! Tests whether the emitter inherits from `BaseBuilder`. - //! - //! \note Both Builder and Compiler emitters would return `true`. - inline bool isBuilder() const noexcept { return _type >= kTypeBuilder; } - //! Tests whether the emitter inherits from `BaseCompiler`. - inline bool isCompiler() const noexcept { return _type == kTypeCompiler; } - - //! Tests whether the emitter has the given `flag` enabled. - inline bool hasFlag(uint32_t flag) const noexcept { return (_flags & flag) != 0; } - //! Tests whether the emitter is finalized. - inline bool isFinalized() const noexcept { return hasFlag(kFlagFinalized); } - //! Tests whether the emitter is destroyed (only used during destruction). - inline bool isDestroyed() const noexcept { return hasFlag(kFlagDestroyed); } - - inline void _addFlags(uint32_t flags) noexcept { _flags = uint16_t(_flags | flags); } - inline void _clearFlags(uint32_t flags) noexcept { _flags = uint16_t(_flags & ~flags); } - - //! \} - - //! \name Target Information - //! \{ - - //! Returns the CodeHolder this emitter is attached to. - inline CodeHolder* code() const noexcept { return _code; } - //! Returns an information about the code, see `CodeInfo`. - inline const CodeInfo& codeInfo() const noexcept { return _codeInfo; } - //! Returns an information about the architecture, see `ArchInfo`. - inline const ArchInfo& archInfo() const noexcept { return _codeInfo.archInfo(); } - - //! Tests whether the target architecture is 32-bit. - inline bool is32Bit() const noexcept { return archInfo().is32Bit(); } - //! Tests whether the target architecture is 64-bit. - inline bool is64Bit() const noexcept { return archInfo().is64Bit(); } - - //! Returns the target architecture type. - inline uint32_t archId() const noexcept { return archInfo().archId(); } - //! Returns the target architecture sub-type. - inline uint32_t archSubId() const noexcept { return archInfo().archSubId(); } - //! Returns the target architecture's GP register size (4 or 8 bytes). - inline uint32_t gpSize() const noexcept { return archInfo().gpSize(); } - //! Returns the number of target GP registers. - inline uint32_t gpCount() const noexcept { return archInfo().gpCount(); } - - //! \} - - //! \name Initialization & Finalization - //! \{ - - //! Tests whether the BaseEmitter is initialized (i.e. attached to the `CodeHolder`). - inline bool isInitialized() const noexcept { return _code != nullptr; } - - ASMJIT_API virtual Error finalize(); - - //! \} - - //! \name Emitter Options - //! \{ - - //! Tests whether the `option` is present in emitter options. - inline bool hasEmitterOption(uint32_t option) const noexcept { return (_emitterOptions & option) != 0; } - //! Returns the emitter options. - inline uint32_t emitterOptions() const noexcept { return _emitterOptions; } - - // TODO: Deprecate and remove, CodeHolder::addEmitterOptions() is the way. - inline void addEmitterOptions(uint32_t options) noexcept { - _emitterOptions |= options; - onUpdateGlobalInstOptions(); - } - - inline void clearEmitterOptions(uint32_t options) noexcept { - _emitterOptions &= ~options; - onUpdateGlobalInstOptions(); - } - - //! Returns the global instruction options. - //! - //! Default instruction options are merged with instruction options before the - //! instruction is encoded. These options have some bits reserved that are used - //! for error handling, logging, and strict validation. Other options are globals that - //! affect each instruction, for example if VEX3 is set globally, it will all - //! instructions, even those that don't have such option set. - inline uint32_t globalInstOptions() const noexcept { return _globalInstOptions; } - - //! \} - - //! \name Error Handling - //! \{ - - //! Tests whether the local error handler is attached. - inline bool hasErrorHandler() const noexcept { return _errorHandler != nullptr; } - //! Returns the local error handler. - inline ErrorHandler* errorHandler() const noexcept { return _errorHandler; } - //! Sets the local error handler. - inline void setErrorHandler(ErrorHandler* handler) noexcept { _errorHandler = handler; } - //! Resets the local error handler (does nothing if not attached). - inline void resetErrorHandler() noexcept { setErrorHandler(nullptr); } - - //! Handles the given error in the following way: - //! 1. Gets either Emitter's (preferred) or CodeHolder's ErrorHandler. - //! 2. If exists, calls `ErrorHandler::handleError(error, message, this)`. - //! 3. Returns the given `err` if ErrorHandler haven't thrown. - ASMJIT_API Error reportError(Error err, const char* message = nullptr); - - //! \} - - //! \name Instruction Options - //! \{ - - //! Returns options of the next instruction. - inline uint32_t instOptions() const noexcept { return _instOptions; } - //! Returns options of the next instruction. - inline void setInstOptions(uint32_t options) noexcept { _instOptions = options; } - //! Adds options of the next instruction. - inline void addInstOptions(uint32_t options) noexcept { _instOptions |= options; } - //! Resets options of the next instruction. - inline void resetInstOptions() noexcept { _instOptions = 0; } - - //! Tests whether the extra register operand is valid. - inline bool hasExtraReg() const noexcept { return _extraReg.isReg(); } - //! Returns an extra operand that will be used by the next instruction (architecture specific). - inline const RegOnly& extraReg() const noexcept { return _extraReg; } - //! Sets an extra operand that will be used by the next instruction (architecture specific). - inline void setExtraReg(const BaseReg& reg) noexcept { _extraReg.init(reg); } - //! Sets an extra operand that will be used by the next instruction (architecture specific). - inline void setExtraReg(const RegOnly& reg) noexcept { _extraReg.init(reg); } - //! Resets an extra operand that will be used by the next instruction (architecture specific). - inline void resetExtraReg() noexcept { _extraReg.reset(); } - - //! Returns comment/annotation of the next instruction. - inline const char* inlineComment() const noexcept { return _inlineComment; } - //! Sets comment/annotation of the next instruction. - //! - //! \note This string is set back to null by `_emit()`, but until that it has - //! to remain valid as the Emitter is not required to make a copy of it (and - //! it would be slow to do that for each instruction). - inline void setInlineComment(const char* s) noexcept { _inlineComment = s; } - //! Resets the comment/annotation to nullptr. - inline void resetInlineComment() noexcept { _inlineComment = nullptr; } - - //! \} - - //! \name Sections - //! \{ - - virtual Error section(Section* section) = 0; - - //! \} - - //! \name Labels - //! \{ - - //! Creates a new label. - virtual Label newLabel() = 0; - //! Creates a new named label. - virtual Label newNamedLabel(const char* name, size_t nameSize = SIZE_MAX, uint32_t type = Label::kTypeGlobal, uint32_t parentId = Globals::kInvalidId) = 0; - - //! Returns `Label` by `name`. - //! - //! Returns invalid Label in case that the name is invalid or label was not found. - //! - //! \note This function doesn't trigger ErrorHandler in case the name is invalid - //! or no such label exist. You must always check the validity of the `Label` returned. - ASMJIT_API Label labelByName(const char* name, size_t nameSize = SIZE_MAX, uint32_t parentId = Globals::kInvalidId) noexcept; - - //! Binds the `label` to the current position of the current section. - //! - //! \note Attempt to bind the same label multiple times will return an error. - virtual Error bind(const Label& label) = 0; - - //! Tests whether the label `id` is valid (i.e. registered). - ASMJIT_API bool isLabelValid(uint32_t labelId) const noexcept; - //! Tests whether the `label` is valid (i.e. registered). - inline bool isLabelValid(const Label& label) const noexcept { return isLabelValid(label.id()); } - - //! \} - - //! \name Emit - //! \{ - - // NOTE: These `emit()` helpers are designed to address a code-bloat generated - // by C++ compilers to call a function having many arguments. Each parameter to - // `_emit()` requires some code to pass it, which means that if we default to 4 - // operand parameters in `_emit()` and instId the C++ compiler would have to - // generate a virtual function call having 5 parameters, which is quite a lot. - // Since by default asm instructions have 2 to 3 operands it's better to - // introduce helpers that pass those and fill out the remaining operands. - - #define OP const Operand_& - #define NONE Globals::none - - //! Emits an instruction. - ASMJIT_NOINLINE Error emit(uint32_t instId) { return _emit(instId, NONE, NONE, NONE, NONE); } - //! \overload - ASMJIT_NOINLINE Error emit(uint32_t instId, OP o0) { return _emit(instId, o0, NONE, NONE, NONE); } - //! \overload - ASMJIT_NOINLINE Error emit(uint32_t instId, OP o0, OP o1) { return _emit(instId, o0, o1, NONE, NONE); } - //! \overload - ASMJIT_NOINLINE Error emit(uint32_t instId, OP o0, OP o1, OP o2) { return _emit(instId, o0, o1, o2, NONE); } - //! \overload - inline Error emit(uint32_t instId, OP o0, OP o1, OP o2, OP o3) { return _emit(instId, o0, o1, o2, o3); } - //! \overload - inline Error emit(uint32_t instId, OP o0, OP o1, OP o2, OP o3, OP o4) { return _emit(instId, o0, o1, o2, o3, o4, NONE); } - //! \overload - inline Error emit(uint32_t instId, OP o0, OP o1, OP o2, OP o3, OP o4, OP o5) { return _emit(instId, o0, o1, o2, o3, o4, o5); } - - //! \overload - ASMJIT_NOINLINE Error emit(uint32_t instId, int o0) { return _emit(instId, Imm(o0), NONE, NONE, NONE); } - //! \overload - ASMJIT_NOINLINE Error emit(uint32_t instId, OP o0, int o1) { return _emit(instId, o0, Imm(o1), NONE, NONE); } - //! \overload - ASMJIT_NOINLINE Error emit(uint32_t instId, OP o0, OP o1, int o2) { return _emit(instId, o0, o1, Imm(o2), NONE); } - //! \overload - ASMJIT_NOINLINE Error emit(uint32_t instId, OP o0, OP o1, OP o2, int o3) { return _emit(instId, o0, o1, o2, Imm(o3)); } - //! \overload - ASMJIT_NOINLINE Error emit(uint32_t instId, OP o0, OP o1, OP o2, OP o3, int o4) { return _emit(instId, o0, o1, o2, o3, Imm(o4), NONE); } - //! \overload - ASMJIT_NOINLINE Error emit(uint32_t instId, OP o0, OP o1, OP o2, OP o3, OP o4, int o5) { return _emit(instId, o0, o1, o2, o3, o4, Imm(o5)); } - - //! \overload - ASMJIT_NOINLINE Error emit(uint32_t instId, int64_t o0) { return _emit(instId, Imm(o0), NONE, NONE, NONE); } - //! \overload - ASMJIT_NOINLINE Error emit(uint32_t instId, OP o0, int64_t o1) { return _emit(instId, o0, Imm(o1), NONE, NONE); } - //! \overload - ASMJIT_NOINLINE Error emit(uint32_t instId, OP o0, OP o1, int64_t o2) { return _emit(instId, o0, o1, Imm(o2), NONE); } - //! \overload - ASMJIT_NOINLINE Error emit(uint32_t instId, OP o0, OP o1, OP o2, int64_t o3) { return _emit(instId, o0, o1, o2, Imm(o3)); } - //! \overload - ASMJIT_NOINLINE Error emit(uint32_t instId, OP o0, OP o1, OP o2, OP o3, int64_t o4) { return _emit(instId, o0, o1, o2, o3, Imm(o4), NONE); } - //! \overload - ASMJIT_NOINLINE Error emit(uint32_t instId, OP o0, OP o1, OP o2, OP o3, OP o4, int64_t o5) { return _emit(instId, o0, o1, o2, o3, o4, Imm(o5)); } - - //! \overload - inline Error emit(uint32_t instId, unsigned int o0) { return emit(instId, int64_t(o0)); } - //! \overload - inline Error emit(uint32_t instId, OP o0, unsigned int o1) { return emit(instId, o0, int64_t(o1)); } - //! \overload - inline Error emit(uint32_t instId, OP o0, OP o1, unsigned int o2) { return emit(instId, o0, o1, int64_t(o2)); } - //! \overload - inline Error emit(uint32_t instId, OP o0, OP o1, OP o2, unsigned int o3) { return emit(instId, o0, o1, o2, int64_t(o3)); } - //! \overload - inline Error emit(uint32_t instId, OP o0, OP o1, OP o2, OP o3, unsigned int o4) { return emit(instId, o0, o1, o2, o3, int64_t(o4)); } - //! \overload - inline Error emit(uint32_t instId, OP o0, OP o1, OP o2, OP o3, OP o4, unsigned int o5) { return emit(instId, o0, o1, o2, o3, o4, int64_t(o5)); } - - //! \overload - inline Error emit(uint32_t instId, uint64_t o0) { return emit(instId, int64_t(o0)); } - //! \overload - inline Error emit(uint32_t instId, OP o0, uint64_t o1) { return emit(instId, o0, int64_t(o1)); } - //! \overload - inline Error emit(uint32_t instId, OP o0, OP o1, uint64_t o2) { return emit(instId, o0, o1, int64_t(o2)); } - //! \overload - inline Error emit(uint32_t instId, OP o0, OP o1, OP o2, uint64_t o3) { return emit(instId, o0, o1, o2, int64_t(o3)); } - //! \overload - inline Error emit(uint32_t instId, OP o0, OP o1, OP o2, OP o3, uint64_t o4) { return emit(instId, o0, o1, o2, o3, int64_t(o4)); } - //! \overload - inline Error emit(uint32_t instId, OP o0, OP o1, OP o2, OP o3, OP o4, uint64_t o5) { return emit(instId, o0, o1, o2, o3, o4, int64_t(o5)); } - - #undef NONE - #undef OP - - inline Error emitOpArray(uint32_t instId, const Operand_* operands, size_t count) { return _emitOpArray(instId, operands, count); } - - inline Error emitInst(const BaseInst& inst, const Operand_* operands, size_t count) { - setInstOptions(inst.options()); - setExtraReg(inst.extraReg()); - return _emitOpArray(inst.id(), operands, count); - } - - //! \cond INTERNAL - //! Emits instruction having max 4 operands. - virtual Error _emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3) = 0; - //! Emits instruction having max 6 operands. - virtual Error _emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, const Operand_& o4, const Operand_& o5) = 0; - //! Emits instruction having operands stored in array. - virtual Error _emitOpArray(uint32_t instId, const Operand_* operands, size_t count); - //! \endcond - - //! \} - - //! \name Emit Utilities - //! \{ - - ASMJIT_API Error emitProlog(const FuncFrame& layout); - ASMJIT_API Error emitEpilog(const FuncFrame& layout); - ASMJIT_API Error emitArgsAssignment(const FuncFrame& layout, const FuncArgsAssignment& args); - - //! \} - - //! \name Align - //! \{ - - //! Aligns the current CodeBuffer to the `alignment` specified. - //! - //! The sequence that is used to fill the gap between the aligned location - //! and the current location depends on the align `mode`, see `AlignMode`. - virtual Error align(uint32_t alignMode, uint32_t alignment) = 0; - - //! \} - - //! \name Embed - //! \{ - - //! Embeds raw data into the CodeBuffer. - virtual Error embed(const void* data, uint32_t dataSize) = 0; - - //! Embeds an absolute label address as data (4 or 8 bytes). - virtual Error embedLabel(const Label& label) = 0; - - //! Embeds a delta (distance) between the `label` and `base` calculating it - //! as `label - base`. This function was designed to make it easier to embed - //! lookup tables where each index is a relative distance of two labels. - virtual Error embedLabelDelta(const Label& label, const Label& base, uint32_t dataSize) = 0; - - //! Embeds a constant pool at the current offset by performing the following: - //! 1. Aligns by using kAlignData to the minimum `pool` alignment. - //! 2. Binds the ConstPool label so it's bound to an aligned location. - //! 3. Emits ConstPool content. - virtual Error embedConstPool(const Label& label, const ConstPool& pool) = 0; - - //! \} - - //! \name Comment - //! \{ - - //! Emits a comment stored in `data` with an optional `size` parameter. - virtual Error comment(const char* data, size_t size = SIZE_MAX) = 0; - - //! Emits a formatted comment specified by `fmt` and variable number of arguments. - ASMJIT_API Error commentf(const char* fmt, ...); - //! Emits a formatted comment specified by `fmt` and `ap`. - ASMJIT_API Error commentv(const char* fmt, va_list ap); - - //! \} - - //! \name Events - //! \{ - - //! Called after the emitter was attached to `CodeHolder`. - virtual Error onAttach(CodeHolder* code) noexcept = 0; - //! Called after the emitter was detached from `CodeHolder`. - virtual Error onDetach(CodeHolder* code) noexcept = 0; - - //! Called to update `_globalInstOptions` based on `_emitterOptions`. - //! - //! This function should only touch one bit `BaseInst::kOptionReserved`, which - //! is used to handle errors and special-cases in a way that minimizes branching. - ASMJIT_API void onUpdateGlobalInstOptions() noexcept; - - //! \} -}; - -//! \} - -ASMJIT_END_NAMESPACE - -#endif // _ASMJIT_CORE_EMITTER_H diff --git a/libraries/asmjit/asmjit/core/features.h b/libraries/asmjit/asmjit/core/features.h deleted file mode 100644 index f22b71adaf6..00000000000 --- a/libraries/asmjit/asmjit/core/features.h +++ /dev/null @@ -1,145 +0,0 @@ -// [AsmJit] -// Machine Code Generation for C++. -// -// [License] -// Zlib - See LICENSE.md file in the package. - -#ifndef _ASMJIT_CORE_FEATURES_H -#define _ASMJIT_CORE_FEATURES_H - -#include "../core/globals.h" -#include "../core/support.h" - -ASMJIT_BEGIN_NAMESPACE - -//! \addtogroup asmjit_core -//! \{ - -// ============================================================================ -// [asmjit::BaseFeatures] -// ============================================================================ - -class BaseFeatures { -public: - typedef Support::BitWord BitWord; - - enum : uint32_t { - kMaxFeatures = 128, - kNumBitWords = kMaxFeatures / Support::kBitWordSizeInBits - }; - - BitWord _bits[kNumBitWords]; - - //! \name Construction & Destruction - //! \{ - - inline BaseFeatures() noexcept { reset(); } - inline BaseFeatures(const BaseFeatures& other) noexcept = default; - inline explicit BaseFeatures(Globals::NoInit_) noexcept {} - - inline void reset() noexcept { - for (size_t i = 0; i < kNumBitWords; i++) - _bits[i] = 0; - } - - //! \} - - //! \name Overloaded Operators - //! \{ - - inline BaseFeatures& operator=(const BaseFeatures& other) noexcept = default; - - inline bool operator==(const BaseFeatures& other) noexcept { return eq(other); } - inline bool operator!=(const BaseFeatures& other) noexcept { return !eq(other); } - - //! \} - - //! \name Cast - //! \{ - - template - inline T& as() noexcept { return static_cast(*this); } - - template - inline const T& as() const noexcept { return static_cast(*this); } - - //! \} - - //! \name Accessors - //! \{ - - //! Returns all features as `BitWord` array. - inline BitWord* bits() noexcept { return _bits; } - //! Returns all features as `BitWord` array (const). - inline const BitWord* bits() const noexcept { return _bits; } - - //! Tests whether the feature `featureId` is present. - inline bool has(uint32_t featureId) const noexcept { - ASMJIT_ASSERT(featureId < kMaxFeatures); - - uint32_t idx = featureId / Support::kBitWordSizeInBits; - uint32_t bit = featureId % Support::kBitWordSizeInBits; - - return bool((_bits[idx] >> bit) & 0x1); - } - - //! Tests whether all features as defined by `other` are present. - inline bool hasAll(const BaseFeatures& other) const noexcept { - for (uint32_t i = 0; i < kNumBitWords; i++) - if ((_bits[i] & other._bits[i]) != other._bits[i]) - return false; - return true; - } - - //! \} - - //! \name Utilities - //! \{ - - //! Adds the given CPU `featureId` to the list of features. - inline void add(uint32_t featureId) noexcept { - ASMJIT_ASSERT(featureId < kMaxFeatures); - - uint32_t idx = featureId / Support::kBitWordSizeInBits; - uint32_t bit = featureId % Support::kBitWordSizeInBits; - - _bits[idx] |= BitWord(1) << bit; - } - - template - inline void add(uint32_t featureId, ArgsT... otherIds) noexcept { - add(featureId); - add(otherIds...); - } - - //! Removes the given CPU `featureId` from the list of features. - inline void remove(uint32_t featureId) noexcept { - ASMJIT_ASSERT(featureId < kMaxFeatures); - - uint32_t idx = featureId / Support::kBitWordSizeInBits; - uint32_t bit = featureId % Support::kBitWordSizeInBits; - - _bits[idx] &= ~(BitWord(1) << bit); - } - - template - inline void remove(uint32_t featureId, ArgsT... otherIds) noexcept { - remove(featureId); - remove(otherIds...); - } - - inline bool eq(const BaseFeatures& other) const noexcept { - for (size_t i = 0; i < kNumBitWords; i++) - if (_bits[i] != other._bits[i]) - return false; - return true; - } - - //! \} -}; - -//! \} - -ASMJIT_END_NAMESPACE - -#endif // _ASMJIT_CORE_FEATURES_H diff --git a/libraries/asmjit/asmjit/core/func.cpp b/libraries/asmjit/asmjit/core/func.cpp deleted file mode 100644 index 248b291097d..00000000000 --- a/libraries/asmjit/asmjit/core/func.cpp +++ /dev/null @@ -1,128 +0,0 @@ -// [AsmJit] -// Machine Code Generation for C++. -// -// [License] -// Zlib - See LICENSE.md file in the package. - -#define ASMJIT_EXPORTS - -#include "../core/arch.h" -#include "../core/func.h" -#include "../core/type.h" - -#ifdef ASMJIT_BUILD_X86 - #include "../x86/x86internal_p.h" - #include "../x86/x86operand.h" -#endif - -#ifdef ASMJIT_BUILD_ARM - #include "../arm/arminternal_p.h" - #include "../arm/armoperand.h" -#endif - -ASMJIT_BEGIN_NAMESPACE - -// ============================================================================ -// [asmjit::FuncDetail - Init / Reset] -// ============================================================================ - -ASMJIT_FAVOR_SIZE Error FuncDetail::init(const FuncSignature& sign) { - uint32_t ccId = sign.callConv(); - CallConv& cc = _callConv; - - uint32_t argCount = sign.argCount(); - if (ASMJIT_UNLIKELY(argCount > Globals::kMaxFuncArgs)) - return DebugUtils::errored(kErrorInvalidArgument); - - ASMJIT_PROPAGATE(cc.init(ccId)); - - uint32_t gpSize = (cc.archId() == ArchInfo::kIdX86) ? 4 : 8; - uint32_t deabstractDelta = Type::deabstractDeltaOfSize(gpSize); - - const uint8_t* args = sign.args(); - for (uint32_t i = 0; i < argCount; i++) { - FuncValue& arg = _args[i]; - arg.initTypeId(Type::deabstract(args[i], deabstractDelta)); - } - _argCount = uint8_t(argCount); - _vaIndex = uint8_t(sign.vaIndex()); - - uint32_t ret = sign.ret(); - if (ret != Type::kIdVoid) { - _rets[0].initTypeId(Type::deabstract(ret, deabstractDelta)); - _retCount = 1; - } - - #ifdef ASMJIT_BUILD_X86 - if (CallConv::isX86Family(ccId)) - return x86::X86Internal::initFuncDetail(*this, sign, gpSize); - #endif - - #ifdef ASMJIT_BUILD_ARM - if (CallConv::isArmFamily(ccId)) - return arm::ArmInternal::initFuncDetail(*this, sign, gpSize); - #endif - - // We should never bubble here as if `cc.init()` succeeded then there has to - // be an implementation for the current architecture. However, stay safe. - return DebugUtils::errored(kErrorInvalidArgument); -} - -// ============================================================================ -// [asmjit::FuncFrame - Init / Reset / Finalize] -// ============================================================================ - -ASMJIT_FAVOR_SIZE Error FuncFrame::init(const FuncDetail& func) noexcept { - uint32_t ccId = func.callConv().id(); - - #ifdef ASMJIT_BUILD_X86 - if (CallConv::isX86Family(ccId)) - return x86::X86Internal::initFuncFrame(*this, func); - #endif - - #ifdef ASMJIT_BUILD_ARM - if (CallConv::isArmFamily(ccId)) - return arm::ArmInternal::initFuncFrame(*this, func); - #endif - - return DebugUtils::errored(kErrorInvalidArgument); -} - -ASMJIT_FAVOR_SIZE Error FuncFrame::finalize() noexcept { - #ifdef ASMJIT_BUILD_X86 - if (ArchInfo::isX86Family(archId())) - return x86::X86Internal::finalizeFuncFrame(*this); - #endif - - #ifdef ASMJIT_BUILD_ARM - if (ArchInfo::isArmFamily(archId())) - return arm::ArmInternal::finalizeFuncFrame(*this); - #endif - - return DebugUtils::errored(kErrorInvalidArgument); -} - -// ============================================================================ -// [asmjit::FuncArgsAssignment] -// ============================================================================ - -ASMJIT_FAVOR_SIZE Error FuncArgsAssignment::updateFuncFrame(FuncFrame& frame) const noexcept { - const FuncDetail* func = funcDetail(); - if (!func) return DebugUtils::errored(kErrorInvalidState); - - uint32_t ccId = func->callConv().id(); - - #ifdef ASMJIT_BUILD_X86 - if (CallConv::isX86Family(ccId)) - return x86::X86Internal::argsToFuncFrame(*this, frame); - #endif - - #ifdef ASMJIT_BUILD_ARM - if (CallConv::isArmFamily(ccId)) - return arm::ArmInternal::argsToFuncFrame(*this, frame); - #endif - - return DebugUtils::errored(kErrorInvalidArch); -} - -ASMJIT_END_NAMESPACE diff --git a/libraries/asmjit/asmjit/core/func.h b/libraries/asmjit/asmjit/core/func.h deleted file mode 100644 index 5d8d64e69ea..00000000000 --- a/libraries/asmjit/asmjit/core/func.h +++ /dev/null @@ -1,949 +0,0 @@ -// [AsmJit] -// Machine Code Generation for C++. -// -// [License] -// Zlib - See LICENSE.md file in the package. - -#ifndef _ASMJIT_CORE_FUNC_H -#define _ASMJIT_CORE_FUNC_H - -#include "../core/arch.h" -#include "../core/callconv.h" -#include "../core/operand.h" -#include "../core/type.h" -#include "../core/support.h" - -ASMJIT_BEGIN_NAMESPACE - -//! \addtogroup asmjit_func -//! \{ - -// ============================================================================ -// [asmjit::FuncArgIndex] -// ============================================================================ - -//! Function argument index (lo/hi). -enum FuncArgIndex : uint32_t { - //! Maximum number of function arguments supported by AsmJit. - kFuncArgCount = Globals::kMaxFuncArgs, - //! Extended maximum number of arguments (used internally). - kFuncArgCountLoHi = kFuncArgCount * 2, - - //! Index to the LO part of function argument (default). - //! - //! This value is typically omitted and added only if there is HI argument - //! accessed. - kFuncArgLo = 0, - - //! Index to the HI part of function argument. - //! - //! HI part of function argument depends on target architecture. On x86 it's - //! typically used to transfer 64-bit integers (they form a pair of 32-bit - //! integers). - kFuncArgHi = kFuncArgCount -}; - -// ============================================================================ -// [asmjit::FuncSignature] -// ============================================================================ - -//! Function signature. -//! -//! Contains information about function return type, count of arguments and -//! their TypeIds. Function signature is a low level structure which doesn't -//! contain platform specific or calling convention specific information. -struct FuncSignature { - //! Calling convention id. - uint8_t _callConv; - //! Count of arguments. - uint8_t _argCount; - //! Index of a first VA or `kNoVarArgs`. - uint8_t _vaIndex; - //! Return value TypeId. - uint8_t _ret; - //! Function arguments TypeIds. - const uint8_t* _args; - - enum : uint8_t { - //! Doesn't have variable number of arguments (`...`). - kNoVarArgs = 0xFF - }; - - //! \name Initializtion & Reset - //! \{ - - //! Initializes the function signature. - inline void init(uint32_t ccId, uint32_t vaIndex, uint32_t ret, const uint8_t* args, uint32_t argCount) noexcept { - ASMJIT_ASSERT(ccId <= 0xFF); - ASMJIT_ASSERT(argCount <= 0xFF); - - _callConv = uint8_t(ccId); - _argCount = uint8_t(argCount); - _vaIndex = uint8_t(vaIndex); - _ret = uint8_t(ret); - _args = args; - } - - inline void reset() noexcept { memset(this, 0, sizeof(*this)); } - - //! \} - - //! \name Accessors - //! \{ - - //! Returns the calling convention. - inline uint32_t callConv() const noexcept { return _callConv; } - //! Sets the calling convention to `ccId`; - inline void setCallConv(uint32_t ccId) noexcept { _callConv = uint8_t(ccId); } - - //! Tests whether the function has variable number of arguments (...). - inline bool hasVarArgs() const noexcept { return _vaIndex != kNoVarArgs; } - //! Returns the variable arguments (...) index, `kNoVarArgs` if none. - inline uint32_t vaIndex() const noexcept { return _vaIndex; } - //! Sets the variable arguments (...) index to `index`. - inline void setVaIndex(uint32_t index) noexcept { _vaIndex = uint8_t(index); } - //! Resets the variable arguments index (making it a non-va function). - inline void resetVaIndex() noexcept { _vaIndex = kNoVarArgs; } - - //! Returns the number of function arguments. - inline uint32_t argCount() const noexcept { return _argCount; } - - inline bool hasRet() const noexcept { return _ret != Type::kIdVoid; } - //! Returns the return value type. - inline uint32_t ret() const noexcept { return _ret; } - - //! Returns the type of the argument at index `i`. - inline uint32_t arg(uint32_t i) const noexcept { - ASMJIT_ASSERT(i < _argCount); - return _args[i]; - } - //! Returns the array of function arguments' types. - inline const uint8_t* args() const noexcept { return _args; } - - //! \} -}; - -// ============================================================================ -// [asmjit::FuncSignatureT] -// ============================================================================ - -template -class FuncSignatureT : public FuncSignature { -public: - inline FuncSignatureT(uint32_t ccId = CallConv::kIdHost, uint32_t vaIndex = kNoVarArgs) noexcept { - static const uint8_t ret_args[] = { (uint8_t(Type::IdOfT::kTypeId))... }; - init(ccId, vaIndex, ret_args[0], ret_args + 1, uint32_t(ASMJIT_ARRAY_SIZE(ret_args) - 1)); - } -}; - -// ============================================================================ -// [asmjit::FuncSignatureBuilder] -// ============================================================================ - -//! Function signature builder. -class FuncSignatureBuilder : public FuncSignature { -public: - uint8_t _builderArgList[kFuncArgCount]; - - //! \name Initializtion & Reset - //! \{ - - inline FuncSignatureBuilder(uint32_t ccId = CallConv::kIdHost, uint32_t vaIndex = kNoVarArgs) noexcept { - init(ccId, vaIndex, Type::kIdVoid, _builderArgList, 0); - } - - //! \} - - //! \name Accessors - //! \{ - - //! Sets the return type to `retType`. - inline void setRet(uint32_t retType) noexcept { _ret = uint8_t(retType); } - //! Sets the return type based on `T`. - template - inline void setRetT() noexcept { setRet(Type::IdOfT::kTypeId); } - - //! Sets the argument at index `index` to `argType`. - inline void setArg(uint32_t index, uint32_t argType) noexcept { - ASMJIT_ASSERT(index < _argCount); - _builderArgList[index] = uint8_t(argType); - } - //! Sets the argument at index `i` to the type based on `T`. - template - inline void setArgT(uint32_t index) noexcept { setArg(index, Type::IdOfT::kTypeId); } - - //! Appends an argument of `type` to the function prototype. - inline void addArg(uint32_t type) noexcept { - ASMJIT_ASSERT(_argCount < kFuncArgCount); - _builderArgList[_argCount++] = uint8_t(type); - } - //! Appends an argument of type based on `T` to the function prototype. - template - inline void addArgT() noexcept { addArg(Type::IdOfT::kTypeId); } - - //! \} -}; - -// ============================================================================ -// [asmjit::FuncValue] -// ============================================================================ - -//! Argument or return value as defined by `FuncSignature`, but with register -//! or stack address (and other metadata) assigned to it. -struct FuncValue { - uint32_t _data; - - enum Parts : uint32_t { - kTypeIdShift = 0, //!< TypeId shift. - kTypeIdMask = 0x000000FFu, //!< TypeId mask. - - kFlagIsReg = 0x00000100u, //!< Passed by register. - kFlagIsStack = 0x00000200u, //!< Passed by stack. - kFlagIsIndirect = 0x00000400u, //!< Passed indirectly by reference (internally a pointer). - kFlagIsDone = 0x00000800u, //!< Used internally by arguments allocator. - - kStackOffsetShift = 12, //!< Stack offset shift. - kStackOffsetMask = 0xFFFFF000u, //!< Stack offset mask (must occupy MSB bits). - - kRegIdShift = 16, //!< RegId shift. - kRegIdMask = 0x00FF0000u, //!< RegId mask. - - kRegTypeShift = 24, //!< RegType shift. - kRegTypeMask = 0xFF000000u //!< RegType mask. - }; - - //! \name Initializtion & Reset - //! \{ - - // These initialize the whole `FuncValue` to either register or stack. Useful - // when you know all of these properties and wanna just set it up. - - //! Initializes the `typeId` of this `FuncValue`. - inline void initTypeId(uint32_t typeId) noexcept { - _data = typeId << kTypeIdShift; - } - - inline void initReg(uint32_t regType, uint32_t regId, uint32_t typeId, uint32_t flags = 0) noexcept { - _data = (regType << kRegTypeShift) | (regId << kRegIdShift) | (typeId << kTypeIdShift) | kFlagIsReg | flags; - } - - inline void initStack(int32_t offset, uint32_t typeId) noexcept { - _data = (uint32_t(offset) << kStackOffsetShift) | (typeId << kTypeIdShift) | kFlagIsStack; - } - - //! Resets the value to its unassigned state. - inline void reset() noexcept { _data = 0; } - - //! \} - - //! \name Assign - //! \{ - - // These initialize only part of `FuncValue`, useful when building `FuncValue` - // incrementally. The caller should first init the type-id by caliing `initTypeId` - // and then continue building either register or stack. - - inline void assignRegData(uint32_t regType, uint32_t regId) noexcept { - ASMJIT_ASSERT((_data & (kRegTypeMask | kRegIdMask)) == 0); - _data |= (regType << kRegTypeShift) | (regId << kRegIdShift) | kFlagIsReg; - } - - inline void assignStackOffset(int32_t offset) noexcept { - ASMJIT_ASSERT((_data & kStackOffsetMask) == 0); - _data |= (uint32_t(offset) << kStackOffsetShift) | kFlagIsStack; - } - - //! \} - - //! \name Accessors - //! \{ - - inline void _replaceValue(uint32_t mask, uint32_t value) noexcept { _data = (_data & ~mask) | value; } - - //! Tests whether the `FuncValue` has a flag `flag` set. - inline bool hasFlag(uint32_t flag) const noexcept { return (_data & flag) != 0; } - //! Adds `flags` to `FuncValue`. - inline void addFlags(uint32_t flags) noexcept { _data |= flags; } - //! Clears `flags` of `FuncValue`. - inline void clearFlags(uint32_t flags) noexcept { _data &= ~flags; } - - //! Tests whether the value is initialized (i.e. contains a valid data). - inline bool isInitialized() const noexcept { return _data != 0; } - //! Tests whether the argument is passed by register. - inline bool isReg() const noexcept { return hasFlag(kFlagIsReg); } - //! Tests whether the argument is passed by stack. - inline bool isStack() const noexcept { return hasFlag(kFlagIsStack); } - //! Tests whether the argument is passed by register. - inline bool isAssigned() const noexcept { return hasFlag(kFlagIsReg | kFlagIsStack); } - //! Tests whether the argument is passed through a pointer (used by WIN64 to pass XMM|YMM|ZMM). - inline bool isIndirect() const noexcept { return hasFlag(kFlagIsIndirect); } - - //! Tests whether the argument was already processed (used internally). - inline bool isDone() const noexcept { return hasFlag(kFlagIsDone); } - - //! Returns a register type of the register used to pass function argument or return value. - inline uint32_t regType() const noexcept { return (_data & kRegTypeMask) >> kRegTypeShift; } - //! Sets a register type of the register used to pass function argument or return value. - inline void setRegType(uint32_t regType) noexcept { _replaceValue(kRegTypeMask, regType << kRegTypeShift); } - - //! Returns a physical id of the register used to pass function argument or return value. - inline uint32_t regId() const noexcept { return (_data & kRegIdMask) >> kRegIdShift; } - //! Sets a physical id of the register used to pass function argument or return value. - inline void setRegId(uint32_t regId) noexcept { _replaceValue(kRegIdMask, regId << kRegIdShift); } - - //! Returns a stack offset of this argument. - inline int32_t stackOffset() const noexcept { return int32_t(_data & kStackOffsetMask) >> kStackOffsetShift; } - //! Sets a stack offset of this argument. - inline void setStackOffset(int32_t offset) noexcept { _replaceValue(kStackOffsetMask, uint32_t(offset) << kStackOffsetShift); } - - //! Tests whether the argument or return value has associated `Type::Id`. - inline bool hasTypeId() const noexcept { return (_data & kTypeIdMask) != 0; } - //! Returns a TypeId of this argument or return value. - inline uint32_t typeId() const noexcept { return (_data & kTypeIdMask) >> kTypeIdShift; } - //! Sets a TypeId of this argument or return value. - inline void setTypeId(uint32_t typeId) noexcept { _replaceValue(kTypeIdMask, typeId << kTypeIdShift); } - - //! \} -}; - -// ============================================================================ -// [asmjit::FuncDetail] -// ============================================================================ - -//! Function detail - CallConv and expanded FuncSignature. -//! -//! Function detail is architecture and OS dependent representation of a function. -//! It contains calling convention and expanded function signature so all -//! arguments have assigned either register type & id or stack address. -class FuncDetail { -public: - //! Calling convention. - CallConv _callConv; - //! Number of function arguments. - uint8_t _argCount; - //! Number of function return values. - uint8_t _retCount; - //! Variable arguments index of `kNoVarArgs`. - uint8_t _vaIndex; - //! Reserved for future use. - uint8_t _reserved; - //! Registers that contains arguments. - uint32_t _usedRegs[BaseReg::kGroupVirt]; - //! Size of arguments passed by stack. - uint32_t _argStackSize; - //! Function return values. - FuncValue _rets[2]; - //! Function arguments. - FuncValue _args[kFuncArgCountLoHi]; - - enum : uint8_t { - //! Doesn't have variable number of arguments (`...`). - kNoVarArgs = 0xFF - }; - - //! \name Construction & Destruction - //! \{ - - inline FuncDetail() noexcept { reset(); } - inline FuncDetail(const FuncDetail& other) noexcept = default; - - //! Initializes this `FuncDetail` to the given signature. - ASMJIT_API Error init(const FuncSignature& sign); - inline void reset() noexcept { memset(this, 0, sizeof(*this)); } - - //! \} - - //! \name Accessors - //! \{ - - //! Returns the function's calling convention, see `CallConv`. - inline const CallConv& callConv() const noexcept { return _callConv; } - - //! Returns the associated calling convention flags, see `CallConv::Flags`. - inline uint32_t flags() const noexcept { return _callConv.flags(); } - //! Checks whether a CallConv `flag` is set, see `CallConv::Flags`. - inline bool hasFlag(uint32_t ccFlag) const noexcept { return _callConv.hasFlag(ccFlag); } - - //! Returns count of function return values. - inline uint32_t retCount() const noexcept { return _retCount; } - //! Returns the number of function arguments. - inline uint32_t argCount() const noexcept { return _argCount; } - - //! Tests whether the function has a return value. - inline bool hasRet() const noexcept { return _retCount != 0; } - //! Returns function return value associated with the given `index`. - inline FuncValue& ret(uint32_t index = 0) noexcept { - ASMJIT_ASSERT(index < ASMJIT_ARRAY_SIZE(_rets)); - return _rets[index]; - } - //! Returns function return value associated with the given `index` (const). - inline const FuncValue& ret(uint32_t index = 0) const noexcept { - ASMJIT_ASSERT(index < ASMJIT_ARRAY_SIZE(_rets)); - return _rets[index]; - } - - //! Returns function arguments array. - inline FuncValue* args() noexcept { return _args; } - //! Returns function arguments array (const). - inline const FuncValue* args() const noexcept { return _args; } - - inline bool hasArg(uint32_t index) const noexcept { - ASMJIT_ASSERT(index < kFuncArgCountLoHi); - return _args[index].isInitialized(); - } - - //! Returns function argument at the given `index`. - inline FuncValue& arg(uint32_t index) noexcept { - ASMJIT_ASSERT(index < kFuncArgCountLoHi); - return _args[index]; - } - - //! Returnsfunction argument at the given index `index` (const). - inline const FuncValue& arg(uint32_t index) const noexcept { - ASMJIT_ASSERT(index < kFuncArgCountLoHi); - return _args[index]; - } - - inline void resetArg(uint32_t index) noexcept { - ASMJIT_ASSERT(index < kFuncArgCountLoHi); - _args[index].reset(); - } - - inline bool hasVarArgs() const noexcept { return _vaIndex != kNoVarArgs; } - inline uint32_t vaIndex() const noexcept { return _vaIndex; } - - //! Tests whether the function passes one or more argument by stack. - inline bool hasStackArgs() const noexcept { return _argStackSize != 0; } - //! Returns stack size needed for function arguments passed on the stack. - inline uint32_t argStackSize() const noexcept { return _argStackSize; } - - inline uint32_t redZoneSize() const noexcept { return _callConv.redZoneSize(); } - inline uint32_t spillZoneSize() const noexcept { return _callConv.spillZoneSize(); } - inline uint32_t naturalStackAlignment() const noexcept { return _callConv.naturalStackAlignment(); } - - inline uint32_t passedRegs(uint32_t group) const noexcept { return _callConv.passedRegs(group); } - inline uint32_t preservedRegs(uint32_t group) const noexcept { return _callConv.preservedRegs(group); } - - inline uint32_t usedRegs(uint32_t group) const noexcept { - ASMJIT_ASSERT(group < BaseReg::kGroupVirt); - return _usedRegs[group]; - } - - inline void addUsedRegs(uint32_t group, uint32_t regs) noexcept { - ASMJIT_ASSERT(group < BaseReg::kGroupVirt); - _usedRegs[group] |= regs; - } - - //! \} -}; - -// ============================================================================ -// [asmjit::FuncFrame] -// ============================================================================ - -//! Function frame. -//! -//! Function frame is used directly by prolog and epilog insertion (PEI) utils. -//! It provides information necessary to insert a proper and ABI comforming -//! prolog and epilog. Function frame calculation is based on `CallConv` and -//! other function attributes. -//! -//! Function Frame Structure -//! ------------------------ -//! -//! Various properties can contribute to the size and structure of the function -//! frame. The function frame in most cases won't use all of the properties -//! illustrated (for example Spill Zone and Red Zone are never used together). -//! -//! +-----------------------------+ -//! | Arguments Passed by Stack | -//! +-----------------------------+ -//! | Spill Zone | -//! +-----------------------------+ <- Stack offset (args) starts from here. -//! | Return Address if Pushed | -//! +-----------------------------+ <- Stack pointer (SP) upon entry. -//! | Save/Restore Stack. | -//! +-----------------------------+-----------------------------+ -//! | Local Stack | | -//! +-----------------------------+ Final Stack | -//! | Call Stack | | -//! +-----------------------------+-----------------------------+ <- SP after prolog. -//! | Red Zone | -//! +-----------------------------+ -class FuncFrame { -public: - enum Tag : uint32_t { - kTagInvalidOffset = 0xFFFFFFFFu //!< Tag used to inform that some offset is invalid. - }; - - //! Attributes are designed in a way that all are initially false, and user - //! or FuncFrame finalizer adds them when necessary. - enum Attributes : uint32_t { - kAttrHasVarArgs = 0x00000001u, //!< Function has variable number of arguments. - kAttrHasPreservedFP = 0x00000010u, //!< Preserve frame pointer (don't omit FP). - kAttrHasFuncCalls = 0x00000020u, //!< Function calls other functions (is not leaf). - - kAttrX86AvxEnabled = 0x00010000u, //!< Use AVX instead of SSE for all operations (X86). - kAttrX86AvxCleanup = 0x00020000u, //!< Emit VZEROUPPER instruction in epilog (X86). - kAttrX86MmxCleanup = 0x00040000u, //!< Emit EMMS instruction in epilog (X86). - - kAttrAlignedVecSR = 0x40000000u, //!< Function has aligned save/restore of vector registers. - kAttrIsFinalized = 0x80000000u //!< FuncFrame is finalized and can be used by PEI. - }; - - //! Function attributes. - uint32_t _attributes; - - //! Architecture ID. - uint8_t _archId; - //! SP register ID (to access call stack and local stack). - uint8_t _spRegId; - //! SA register ID (to access stack arguments). - uint8_t _saRegId; - - //! Red zone size (copied from CallConv). - uint8_t _redZoneSize; - //! Spill zone size (copied from CallConv). - uint8_t _spillZoneSize; - //! Natural stack alignment (copied from CallConv). - uint8_t _naturalStackAlignment; - //! Minimum stack alignment to turn on dynamic alignment. - uint8_t _minDynamicAlignment; - - //! Call stack alignment. - uint8_t _callStackAlignment; - //! Local stack alignment. - uint8_t _localStackAlignment; - //! Final stack alignment. - uint8_t _finalStackAlignment; - - //! Adjustment of the stack before returning (X86-STDCALL). - uint16_t _calleeStackCleanup; - - //! Call stack size. - uint32_t _callStackSize; - //! Local stack size. - uint32_t _localStackSize; - //! Final stack size (sum of call stack and local stack). - uint32_t _finalStackSize; - - //! Local stack offset (non-zero only if call stack is used). - uint32_t _localStackOffset; - //! Offset relative to SP that contains previous SP (before alignment). - uint32_t _daOffset; - //! Offset of the first stack argument relative to SP. - uint32_t _saOffsetFromSP; - //! Offset of the first stack argument relative to SA (_saRegId or FP). - uint32_t _saOffsetFromSA; - - //! Local stack adjustment in prolog/epilog. - uint32_t _stackAdjustment; - - //! Registers that are dirty. - uint32_t _dirtyRegs[BaseReg::kGroupVirt]; - //! Registers that must be preserved (copied from CallConv). - uint32_t _preservedRegs[BaseReg::kGroupVirt]; - - //! Final stack size required to save GP regs. - uint16_t _gpSaveSize; - //! Final Stack size required to save other than GP regs. - uint16_t _nonGpSaveSize; - //! Final offset where saved GP regs are stored. - uint32_t _gpSaveOffset; - //! Final offset where saved other than GP regs are stored. - uint32_t _nonGpSaveOffset; - - //! \name Construction & Destruction - //! \{ - - inline FuncFrame() noexcept { reset(); } - inline FuncFrame(const FuncFrame& other) noexcept = default; - - ASMJIT_API Error init(const FuncDetail& func) noexcept; - - inline void reset() noexcept { - memset(this, 0, sizeof(FuncFrame)); - _spRegId = BaseReg::kIdBad; - _saRegId = BaseReg::kIdBad; - _daOffset = kTagInvalidOffset; - } - - //! \} - - //! \name Accessors - //! \{ - - //! Returns the target architecture of the function frame. - inline uint32_t archId() const noexcept { return _archId; } - - //! Returns function frame attributes, see `Attributes`. - inline uint32_t attributes() const noexcept { return _attributes; } - //! Checks whether the FuncFame contains an attribute `attr`. - inline bool hasAttribute(uint32_t attr) const noexcept { return (_attributes & attr) != 0; } - //! Adds attributes `attrs` to the FuncFrame. - inline void addAttributes(uint32_t attrs) noexcept { _attributes |= attrs; } - //! Clears attributes `attrs` from the FrameFrame. - inline void clearAttributes(uint32_t attrs) noexcept { _attributes &= ~attrs; } - - //! Tests whether the function has variable number of arguments. - inline bool hasVarArgs() const noexcept { return hasAttribute(kAttrHasVarArgs); } - //! Sets the variable arguments flag. - inline void setVarArgs() noexcept { addAttributes(kAttrHasVarArgs); } - //! Resets variable arguments flag. - inline void resetVarArgs() noexcept { clearAttributes(kAttrHasVarArgs); } - - //! Tests whether the function preserves frame pointer (EBP|ESP on X86). - inline bool hasPreservedFP() const noexcept { return hasAttribute(kAttrHasPreservedFP); } - //! Enables preserved frame pointer. - inline void setPreservedFP() noexcept { addAttributes(kAttrHasPreservedFP); } - //! Disables preserved frame pointer. - inline void resetPreservedFP() noexcept { clearAttributes(kAttrHasPreservedFP); } - - //! Tests whether the function calls other functions. - inline bool hasFuncCalls() const noexcept { return hasAttribute(kAttrHasFuncCalls); } - //! Sets `kFlagHasCalls` to true. - inline void setFuncCalls() noexcept { addAttributes(kAttrHasFuncCalls); } - //! Sets `kFlagHasCalls` to false. - inline void resetFuncCalls() noexcept { clearAttributes(kAttrHasFuncCalls); } - - //! Tests whether the function contains AVX cleanup - 'vzeroupper' instruction in epilog. - inline bool hasAvxCleanup() const noexcept { return hasAttribute(kAttrX86AvxCleanup); } - //! Enables AVX cleanup. - inline void setAvxCleanup() noexcept { addAttributes(kAttrX86AvxCleanup); } - //! Disables AVX cleanup. - inline void resetAvxCleanup() noexcept { clearAttributes(kAttrX86AvxCleanup); } - - //! Tests whether the function contains AVX cleanup - 'vzeroupper' instruction in epilog. - inline bool isAvxEnabled() const noexcept { return hasAttribute(kAttrX86AvxEnabled); } - //! Enables AVX cleanup. - inline void setAvxEnabled() noexcept { addAttributes(kAttrX86AvxEnabled); } - //! Disables AVX cleanup. - inline void resetAvxEnabled() noexcept { clearAttributes(kAttrX86AvxEnabled); } - - //! Tests whether the function contains MMX cleanup - 'emms' instruction in epilog. - inline bool hasMmxCleanup() const noexcept { return hasAttribute(kAttrX86MmxCleanup); } - //! Enables MMX cleanup. - inline void setMmxCleanup() noexcept { addAttributes(kAttrX86MmxCleanup); } - //! Disables MMX cleanup. - inline void resetMmxCleanup() noexcept { clearAttributes(kAttrX86MmxCleanup); } - - //! Tests whether the function uses call stack. - inline bool hasCallStack() const noexcept { return _callStackSize != 0; } - //! Tests whether the function uses local stack. - inline bool hasLocalStack() const noexcept { return _localStackSize != 0; } - //! Tests whether vector registers can be saved and restored by using aligned reads and writes. - inline bool hasAlignedVecSR() const noexcept { return hasAttribute(kAttrAlignedVecSR); } - //! Tests whether the function has to align stack dynamically. - inline bool hasDynamicAlignment() const noexcept { return _finalStackAlignment >= _minDynamicAlignment; } - - //! Tests whether the calling convention specifies 'RedZone'. - inline bool hasRedZone() const noexcept { return _redZoneSize != 0; } - //! Tests whether the calling convention specifies 'SpillZone'. - inline bool hasSpillZone() const noexcept { return _spillZoneSize != 0; } - - //! Returns the size of 'RedZone'. - inline uint32_t redZoneSize() const noexcept { return _redZoneSize; } - //! Returns the size of 'SpillZone'. - inline uint32_t spillZoneSize() const noexcept { return _spillZoneSize; } - //! Returns natural stack alignment (guaranteed stack alignment upon entry). - inline uint32_t naturalStackAlignment() const noexcept { return _naturalStackAlignment; } - //! Returns natural stack alignment (guaranteed stack alignment upon entry). - inline uint32_t minDynamicAlignment() const noexcept { return _minDynamicAlignment; } - - //! Tests whether the callee must adjust SP before returning (X86-STDCALL only) - inline bool hasCalleeStackCleanup() const noexcept { return _calleeStackCleanup != 0; } - //! Returns home many bytes of the stack the the callee must adjust before returning (X86-STDCALL only) - inline uint32_t calleeStackCleanup() const noexcept { return _calleeStackCleanup; } - - //! Returns call stack alignment. - inline uint32_t callStackAlignment() const noexcept { return _callStackAlignment; } - //! Returns local stack alignment. - inline uint32_t localStackAlignment() const noexcept { return _localStackAlignment; } - //! Returns final stack alignment (the maximum value of call, local, and natural stack alignments). - inline uint32_t finalStackAlignment() const noexcept { return _finalStackAlignment; } - - //! Sets call stack alignment. - //! - //! \note This also updates the final stack alignment. - inline void setCallStackAlignment(uint32_t alignment) noexcept { - _callStackAlignment = uint8_t(alignment); - _finalStackAlignment = Support::max(_naturalStackAlignment, _callStackAlignment, _localStackAlignment); - } - - //! Sets local stack alignment. - //! - //! \note This also updates the final stack alignment. - inline void setLocalStackAlignment(uint32_t value) noexcept { - _localStackAlignment = uint8_t(value); - _finalStackAlignment = Support::max(_naturalStackAlignment, _callStackAlignment, _localStackAlignment); - } - - //! Combines call stack alignment with `alignment`, updating it to the greater value. - //! - //! \note This also updates the final stack alignment. - inline void updateCallStackAlignment(uint32_t alignment) noexcept { - _callStackAlignment = uint8_t(Support::max(_callStackAlignment, alignment)); - _finalStackAlignment = Support::max(_finalStackAlignment, _callStackAlignment); - } - - //! Combines local stack alignment with `alignment`, updating it to the greater value. - //! - //! \note This also updates the final stack alignment. - inline void updateLocalStackAlignment(uint32_t alignment) noexcept { - _localStackAlignment = uint8_t(Support::max(_localStackAlignment, alignment)); - _finalStackAlignment = Support::max(_finalStackAlignment, _localStackAlignment); - } - - //! Returns call stack size. - inline uint32_t callStackSize() const noexcept { return _callStackSize; } - //! Returns local stack size. - inline uint32_t localStackSize() const noexcept { return _localStackSize; } - - //! Sets call stack size. - inline void setCallStackSize(uint32_t size) noexcept { _callStackSize = size; } - //! Sets local stack size. - inline void setLocalStackSize(uint32_t size) noexcept { _localStackSize = size; } - - //! Combines call stack size with `size`, updating it to the greater value. - inline void updateCallStackSize(uint32_t size) noexcept { _callStackSize = Support::max(_callStackSize, size); } - //! Combines local stack size with `size`, updating it to the greater value. - inline void updateLocalStackSize(uint32_t size) noexcept { _localStackSize = Support::max(_localStackSize, size); } - - //! Returns final stack size (only valid after the FuncFrame is finalized). - inline uint32_t finalStackSize() const noexcept { return _finalStackSize; } - - //! Returns an offset to access the local stack (non-zero only if call stack is used). - inline uint32_t localStackOffset() const noexcept { return _localStackOffset; } - - //! Tests whether the function prolog/epilog requires a memory slot for storing unaligned SP. - inline bool hasDAOffset() const noexcept { return _daOffset != kTagInvalidOffset; } - //! Returns a memory offset used to store DA (dynamic alignment) slot (relative to SP). - inline uint32_t daOffset() const noexcept { return _daOffset; } - - inline uint32_t saOffset(uint32_t regId) const noexcept { - return regId == _spRegId ? saOffsetFromSP() - : saOffsetFromSA(); - } - - inline uint32_t saOffsetFromSP() const noexcept { return _saOffsetFromSP; } - inline uint32_t saOffsetFromSA() const noexcept { return _saOffsetFromSA; } - - //! Returns mask of registers of the given register `group` that are modified - //! by the function. The engine would then calculate which registers must be - //! saved & restored by the function by using the data provided by the calling - //! convention. - inline uint32_t dirtyRegs(uint32_t group) const noexcept { - ASMJIT_ASSERT(group < BaseReg::kGroupVirt); - return _dirtyRegs[group]; - } - - //! Sets which registers (as a mask) are modified by the function. - //! - //! \remarks Please note that this will completely overwrite the existing - //! register mask, use `addDirtyRegs()` to modify the existing register - //! mask. - inline void setDirtyRegs(uint32_t group, uint32_t regs) noexcept { - ASMJIT_ASSERT(group < BaseReg::kGroupVirt); - _dirtyRegs[group] = regs; - } - - //! Adds which registers (as a mask) are modified by the function. - inline void addDirtyRegs(uint32_t group, uint32_t regs) noexcept { - ASMJIT_ASSERT(group < BaseReg::kGroupVirt); - _dirtyRegs[group] |= regs; - } - - //! \overload - inline void addDirtyRegs(const BaseReg& reg) noexcept { - ASMJIT_ASSERT(reg.id() < Globals::kMaxPhysRegs); - addDirtyRegs(reg.group(), Support::bitMask(reg.id())); - } - - //! \overload - template - ASMJIT_INLINE void addDirtyRegs(const BaseReg& reg, ArgsT&&... args) noexcept { - addDirtyRegs(reg); - addDirtyRegs(std::forward(args)...); - } - - inline void setAllDirty() noexcept { - _dirtyRegs[0] = 0xFFFFFFFFu; - _dirtyRegs[1] = 0xFFFFFFFFu; - _dirtyRegs[2] = 0xFFFFFFFFu; - _dirtyRegs[3] = 0xFFFFFFFFu; - } - - inline void setAllDirty(uint32_t group) noexcept { - ASMJIT_ASSERT(group < BaseReg::kGroupVirt); - _dirtyRegs[group] = 0xFFFFFFFFu; - } - - //! Returns a calculated mask of registers of the given `group` that will be - //! saved and restored in the function's prolog and epilog, respectively. The - //! register mask is calculated from both `dirtyRegs` (provided by user) and - //! `preservedMask` (provided by the calling convention). - inline uint32_t savedRegs(uint32_t group) const noexcept { - ASMJIT_ASSERT(group < BaseReg::kGroupVirt); - return _dirtyRegs[group] & _preservedRegs[group]; - } - - //! Returns the mask of preserved registers of the given register `group`. - //! - //! Preserved registers are those that must survive the function call - //! unmodified. The function can only modify preserved registers it they - //! are saved and restored in funciton's prolog and epilog, respectively. - inline uint32_t preservedRegs(uint32_t group) const noexcept { - ASMJIT_ASSERT(group < BaseReg::kGroupVirt); - return _preservedRegs[group]; - } - - inline bool hasSARegId() const noexcept { return _saRegId != BaseReg::kIdBad; } - inline uint32_t saRegId() const noexcept { return _saRegId; } - inline void setSARegId(uint32_t regId) { _saRegId = uint8_t(regId); } - inline void resetSARegId() { setSARegId(BaseReg::kIdBad); } - - //! Returns stack size required to save GP registers. - inline uint32_t gpSaveSize() const noexcept { return _gpSaveSize; } - //! Returns stack size required to save other than GP registers (MM, XMM|YMM|ZMM, K, VFP, etc...). - inline uint32_t nonGpSaveSize() const noexcept { return _nonGpSaveSize; } - - //! Returns an offset to the stack where general purpose registers are saved. - inline uint32_t gpSaveOffset() const noexcept { return _gpSaveOffset; } - //! Returns an offset to the stack where other than GP registers are saved. - inline uint32_t nonGpSaveOffset() const noexcept { return _nonGpSaveOffset; } - - //! Tests whether the functions contains stack adjustment. - inline bool hasStackAdjustment() const noexcept { return _stackAdjustment != 0; } - //! Returns function's stack adjustment used in function's prolog and epilog. - //! - //! If the returned value is zero it means that the stack is not adjusted. - //! This can mean both that the stack is not used and/or the stack is only - //! adjusted by instructions that pust/pop registers into/from stack. - inline uint32_t stackAdjustment() const noexcept { return _stackAdjustment; } - - //! \} - - //! \name Finaliztion - //! \{ - - ASMJIT_API Error finalize() noexcept; - - //! \} -}; - -// ============================================================================ -// [asmjit::FuncArgsAssignment] -// ============================================================================ - -//! A helper class that can be used to assign a physical register for each -//! function argument. Use with `BaseEmitter::emitArgsAssignment()`. -class FuncArgsAssignment { -public: - //! Function detail. - const FuncDetail* _funcDetail; - //! Register that can be used to access arguments passed by stack. - uint8_t _saRegId; - //! Reserved for future use. - uint8_t _reserved[3]; - //! Mapping of each function argument. - FuncValue _args[kFuncArgCountLoHi]; - - //! \name Construction & Destruction - //! \{ - - inline explicit FuncArgsAssignment(const FuncDetail* fd = nullptr) noexcept { reset(fd); } - - inline FuncArgsAssignment(const FuncArgsAssignment& other) noexcept { - memcpy(this, &other, sizeof(*this)); - } - - inline void reset(const FuncDetail* fd = nullptr) noexcept { - _funcDetail = fd; - _saRegId = uint8_t(BaseReg::kIdBad); - memset(_reserved, 0, sizeof(_reserved)); - memset(_args, 0, sizeof(_args)); - } - - //! \} - - //! \name Accessors - //! \{ - - inline const FuncDetail* funcDetail() const noexcept { return _funcDetail; } - inline void setFuncDetail(const FuncDetail* fd) noexcept { _funcDetail = fd; } - - inline bool hasSARegId() const noexcept { return _saRegId != BaseReg::kIdBad; } - inline uint32_t saRegId() const noexcept { return _saRegId; } - inline void setSARegId(uint32_t regId) { _saRegId = uint8_t(regId); } - inline void resetSARegId() { _saRegId = uint8_t(BaseReg::kIdBad); } - - inline FuncValue& arg(uint32_t index) noexcept { - ASMJIT_ASSERT(index < ASMJIT_ARRAY_SIZE(_args)); - return _args[index]; - } - inline const FuncValue& arg(uint32_t index) const noexcept { - ASMJIT_ASSERT(index < ASMJIT_ARRAY_SIZE(_args)); - return _args[index]; - } - - inline bool isAssigned(uint32_t argIndex) const noexcept { - ASMJIT_ASSERT(argIndex < ASMJIT_ARRAY_SIZE(_args)); - return _args[argIndex].isAssigned(); - } - - inline void assignReg(uint32_t argIndex, const BaseReg& reg, uint32_t typeId = Type::kIdVoid) noexcept { - ASMJIT_ASSERT(argIndex < ASMJIT_ARRAY_SIZE(_args)); - ASMJIT_ASSERT(reg.isPhysReg()); - _args[argIndex].initReg(reg.type(), reg.id(), typeId); - } - - inline void assignReg(uint32_t argIndex, uint32_t regType, uint32_t regId, uint32_t typeId = Type::kIdVoid) noexcept { - ASMJIT_ASSERT(argIndex < ASMJIT_ARRAY_SIZE(_args)); - _args[argIndex].initReg(regType, regId, typeId); - } - - inline void assignStack(uint32_t argIndex, int32_t offset, uint32_t typeId = Type::kIdVoid) { - ASMJIT_ASSERT(argIndex < ASMJIT_ARRAY_SIZE(_args)); - _args[argIndex].initStack(offset, typeId); - } - - // NOTE: All `assignAll()` methods are shortcuts to assign all arguments at - // once, however, since registers are passed all at once these initializers - // don't provide any way to pass TypeId and/or to keep any argument between - // the arguments passed unassigned. - inline void _assignAllInternal(uint32_t argIndex, const BaseReg& reg) noexcept { - assignReg(argIndex, reg); - } - - template - inline void _assignAllInternal(uint32_t argIndex, const BaseReg& reg, ArgsT&&... args) noexcept { - assignReg(argIndex, reg); - _assignAllInternal(argIndex + 1, std::forward(args)...); - } - - template - inline void assignAll(ArgsT&&... args) noexcept { - _assignAllInternal(0, std::forward(args)...); - } - - //! \} - - //! \name Utilities - //! \{ - - //! Update `FuncFrame` based on function's arguments assignment. - //! - //! \note You MUST call this in orher to use `BaseEmitter::emitArgsAssignment()`, - //! otherwise the FuncFrame would not contain the information necessary to - //! assign all arguments into the registers and/or stack specified. - ASMJIT_API Error updateFuncFrame(FuncFrame& frame) const noexcept; - - //! \} -}; - -//! \} - -ASMJIT_END_NAMESPACE - -#endif // _ASMJIT_CORE_FUNC_H - diff --git a/libraries/asmjit/asmjit/core/globals.cpp b/libraries/asmjit/asmjit/core/globals.cpp deleted file mode 100644 index 5a16de41b26..00000000000 --- a/libraries/asmjit/asmjit/core/globals.cpp +++ /dev/null @@ -1,115 +0,0 @@ -// [AsmJit] -// Machine Code Generation for C++. -// -// [License] -// Zlib - See LICENSE.md file in the package. - -#define ASMJIT_EXPORTS - -#include "../core/globals.h" -#include "../core/support.h" - -ASMJIT_BEGIN_NAMESPACE - -// ============================================================================ -// [asmjit::DebugUtils] -// ============================================================================ - -ASMJIT_FAVOR_SIZE const char* DebugUtils::errorAsString(Error err) noexcept { -#ifndef ASMJIT_NO_TEXT - static const char errorMessages[] = - "Ok\0" - "Out of memory\0" - "Invalid argument\0" - "Invalid state\0" - "Invalid architecture\0" - "Not initialized\0" - "Already initialized\0" - "Feature not enabled\0" - "Too many handles or file descriptors\0" - "Too large (code or memory request)\0" - "No code generated\0" - "Invalid directive\0" - "Invalid label\0" - "Too many labels\0" - "Label already bound\0" - "Label already defined\0" - "Label name too long\0" - "Invalid label name\0" - "Invalid parent label\0" - "Non-local label can't have parent\0" - "Invalid section\0" - "Too many sections\0" - "Invalid section name\0" - "Too many relocations\0" - "Invalid relocation entry\0" - "Relocation offset out of range\0" - "Invalid assignment\0" - "Invalid instruction\0" - "Invalid register type\0" - "Invalid register group\0" - "Invalid register physical id\0" - "Invalid register virtual id\0" - "Invalid prefix combination\0" - "Invalid lock prefix\0" - "Invalid xacquire prefix\0" - "Invalid xrelease prefix\0" - "Invalid rep prefix\0" - "Invalid rex prefix\0" - "Invalid {...} register \0" - "Invalid use of {k}\0" - "Invalid use of {k}{z}\0" - "Invalid broadcast {1tox}\0" - "Invalid {er} or {sae} option\0" - "Invalid address\0" - "Invalid address index\0" - "Invalid address scale\0" - "Invalid use of 64-bit address or offset\0" - "Invalid use of 64-bit address or offset that requires 32-bit zero-extension\0" - "Invalid displacement\0" - "Invalid segment\0" - "Invalid immediate value\0" - "Invalid operand size\0" - "Ambiguous operand size\0" - "Operand size mismatch\0" - "Invalid option\0" - "Option already defined\0" - "Invalid type-info\0" - "Invalid use of a low 8-bit GPB register\0" - "Invalid use of a 64-bit GPQ register in 32-bit mode\0" - "Invalid use of an 80-bit float\0" - "Not consecutive registers\0" - "No more physical registers\0" - "Overlapped registers\0" - "Overlapping register and arguments base-address register\0" - "Unbound label cannot be evaluated by expression\0" - "Arithmetic overflow during expression evaluation\0" - "Unknown error\0"; - return Support::findPackedString(errorMessages, Support::min(err, kErrorCount)); -#else - ASMJIT_UNUSED(err); - static const char noMessage[] = ""; - return noMessage; -#endif -} - -ASMJIT_FAVOR_SIZE void DebugUtils::debugOutput(const char* str) noexcept { -#if defined(_WIN32) - ::OutputDebugStringA(str); -#else - ::fputs(str, stderr); -#endif -} - -ASMJIT_FAVOR_SIZE void DebugUtils::assertionFailed(const char* file, int line, const char* msg) noexcept { - char str[1024]; - - snprintf(str, 1024, - "[asmjit] Assertion failed at %s (line %d):\n" - "[asmjit] %s\n", file, line, msg); - - debugOutput(str); - ::abort(); -} - -ASMJIT_END_NAMESPACE diff --git a/libraries/asmjit/asmjit/core/globals.h b/libraries/asmjit/asmjit/core/globals.h deleted file mode 100644 index f03ede54c2a..00000000000 --- a/libraries/asmjit/asmjit/core/globals.h +++ /dev/null @@ -1,404 +0,0 @@ -// [AsmJit] -// Machine Code Generation for C++. -// -// [License] -// Zlib - See LICENSE.md file in the package. - -#ifndef _ASMJIT_CORE_GLOBALS_H -#define _ASMJIT_CORE_GLOBALS_H - -#include "../core/build.h" - -ASMJIT_BEGIN_NAMESPACE - -// ============================================================================ -// [asmjit::Support] -// ============================================================================ - -//! \cond INTERNAL -//! \addtogroup Support -//! \{ -namespace Support { - //! Cast designed to cast between function and void* pointers. - template - static inline Dst ptr_cast_impl(Src p) noexcept { return (Dst)p; } -} // {Support} - -#if defined(ASMJIT_NO_STDCXX) -namespace Support { - ASMJIT_INLINE void* operatorNew(size_t n) noexcept { return malloc(n); } - ASMJIT_INLINE void operatorDelete(void* p) noexcept { if (p) free(p); } -} // {Support} - -#define ASMJIT_BASE_CLASS(TYPE) \ - ASMJIT_INLINE void* operator new(size_t n) noexcept { \ - return Support::operatorNew(n); \ - } \ - \ - ASMJIT_INLINE void operator delete(void* p) noexcept { \ - Support::operatorDelete(p); \ - } \ - \ - ASMJIT_INLINE void* operator new(size_t, void* p) noexcept { return p; } \ - ASMJIT_INLINE void operator delete(void*, void*) noexcept {} -#else -#define ASMJIT_BASE_CLASS(TYPE) -#endif - -//! \} -//! \endcond - -// ============================================================================ -// [asmjit::Globals] -// ============================================================================ - -//! \addtogroup asmjit_core -//! \{ - -//! Contains typedefs, constants, and variables used globally by AsmJit. -namespace Globals { - -// ============================================================================ -// [asmjit::Globals::] -// ============================================================================ - -//! Host memory allocator overhead. -constexpr uint32_t kAllocOverhead = uint32_t(sizeof(intptr_t) * 4); - -//! Host memory allocator alignment. -constexpr uint32_t kAllocAlignment = 8; - -//! Aggressive growing strategy threshold. -constexpr uint32_t kGrowThreshold = 1024 * 1024 * 16; - -//! Maximum height of RB-Tree is: -//! -//! `2 * log2(n + 1)`. -//! -//! Size of RB node is at least two pointers (without data), -//! so a theoretical architecture limit would be: -//! -//! `2 * log2(addressableMemorySize / sizeof(Node) + 1)` -//! -//! Which yields 30 on 32-bit arch and 61 on 64-bit arch. -//! The final value was adjusted by +1 for safety reasons. -constexpr uint32_t kMaxTreeHeight = (ASMJIT_ARCH_BITS == 32 ? 30 : 61) + 1; - -//! Maximum number of operands per a single instruction. -constexpr uint32_t kMaxOpCount = 6; - -// TODO: Use this one. -constexpr uint32_t kMaxFuncArgs = 16; - -//! Maximum number of physical registers AsmJit can use per register group. -constexpr uint32_t kMaxPhysRegs = 32; - -//! Maximum alignment. -constexpr uint32_t kMaxAlignment = 64; - -//! Maximum label or symbol size in bytes. -constexpr uint32_t kMaxLabelNameSize = 2048; - -//! Maximum section name size. -constexpr uint32_t kMaxSectionNameSize = 35; - -//! Maximum size of comment. -constexpr uint32_t kMaxCommentSize = 1024; - -//! Invalid identifier. -constexpr uint32_t kInvalidId = 0xFFFFFFFFu; - -//! Returned by `indexOf()` and similar when working with containers that use 32-bit index/size. -constexpr uint32_t kNotFound = 0xFFFFFFFFu; - -//! Invalid base address. -constexpr uint64_t kNoBaseAddress = ~uint64_t(0); - -// ============================================================================ -// [asmjit::Globals::ResetPolicy] -// ============================================================================ - -//! Reset policy used by most `reset()` functions. -enum ResetPolicy : uint32_t { - //! Soft reset, doesn't deallocate memory (default). - kResetSoft = 0, - //! Hard reset, releases all memory used, if any. - kResetHard = 1 -}; - -// ============================================================================ -// [asmjit::Globals::Link] -// ============================================================================ - -enum Link : uint32_t { - kLinkLeft = 0, - kLinkRight = 1, - - kLinkPrev = 0, - kLinkNext = 1, - - kLinkFirst = 0, - kLinkLast = 1, - - kLinkCount = 2 -}; - -struct Init_ {}; -struct NoInit_ {}; - -static const constexpr Init_ Init {}; -static const constexpr NoInit_ NoInit {}; - -} // {Globals} - -// ============================================================================ -// [asmjit::Error] -// ============================================================================ - -//! AsmJit error type (uint32_t). -typedef uint32_t Error; - -//! AsmJit error codes. -enum ErrorCode : uint32_t { - //! No error (success). - kErrorOk = 0, - - //! Out of memory. - kErrorOutOfMemory, - - //! Invalid argument. - kErrorInvalidArgument, - - //! Invalid state. - //! - //! If this error is returned it means that either you are doing something - //! wrong or AsmJit caught itself by doing something wrong. This error should - //! never be ignored. - kErrorInvalidState, - - //! Invalid or incompatible architecture. - kErrorInvalidArch, - - //! The object is not initialized. - kErrorNotInitialized, - //! The object is already initialized. - kErrorAlreadyInitialized, - - //! Built-in feature was disabled at compile time and it's not available. - kErrorFeatureNotEnabled, - - //! Too many handles (Windows) or file descriptors (Unix/Posix). - kErrorTooManyHandles, - //! Code generated is larger than allowed. - kErrorTooLarge, - - //! No code generated. - //! - //! Returned by runtime if the `CodeHolder` contains no code. - kErrorNoCodeGenerated, - - //! Invalid directive. - kErrorInvalidDirective, - //! Attempt to use uninitialized label. - kErrorInvalidLabel, - //! Label index overflow - a single `Assembler` instance can hold almost - //! 2^32 (4 billion) labels. If there is an attempt to create more labels - //! then this error is returned. - kErrorTooManyLabels, - //! Label is already bound. - kErrorLabelAlreadyBound, - //! Label is already defined (named labels). - kErrorLabelAlreadyDefined, - //! Label name is too long. - kErrorLabelNameTooLong, - //! Label must always be local if it's anonymous (without a name). - kErrorInvalidLabelName, - //! Parent id passed to `CodeHolder::newNamedLabelId()` was invalid. - kErrorInvalidParentLabel, - //! Parent id specified for a non-local (global) label. - kErrorNonLocalLabelCantHaveParent, - - //! Invalid section. - kErrorInvalidSection, - //! Too many sections (section index overflow). - kErrorTooManySections, - //! Invalid section name (most probably too long). - kErrorInvalidSectionName, - - //! Relocation index overflow (too many relocations). - kErrorTooManyRelocations, - //! Invalid relocation entry. - kErrorInvalidRelocEntry, - //! Reloc entry contains address that is out of range (unencodable). - kErrorRelocOffsetOutOfRange, - - //! Invalid assignment to a register, function argument, or function return value. - kErrorInvalidAssignment, - //! Invalid instruction. - kErrorInvalidInstruction, - //! Invalid register type. - kErrorInvalidRegType, - //! Invalid register group. - kErrorInvalidRegGroup, - //! Invalid register's physical id. - kErrorInvalidPhysId, - //! Invalid register's virtual id. - kErrorInvalidVirtId, - //! Invalid prefix combination. - kErrorInvalidPrefixCombination, - //! Invalid LOCK prefix. - kErrorInvalidLockPrefix, - //! Invalid XACQUIRE prefix. - kErrorInvalidXAcquirePrefix, - //! Invalid XRELEASE prefix. - kErrorInvalidXReleasePrefix, - //! Invalid REP prefix. - kErrorInvalidRepPrefix, - //! Invalid REX prefix. - kErrorInvalidRexPrefix, - //! Invalid {...} register. - kErrorInvalidExtraReg, - //! Invalid {k} use (not supported by the instruction). - kErrorInvalidKMaskUse, - //! Invalid {k}{z} use (not supported by the instruction). - kErrorInvalidKZeroUse, - //! Invalid broadcast - Currently only related to invalid use of AVX-512 {1tox}. - kErrorInvalidBroadcast, - //! Invalid 'embedded-rounding' {er} or 'suppress-all-exceptions' {sae} (AVX-512). - kErrorInvalidEROrSAE, - //! Invalid address used (not encodable). - kErrorInvalidAddress, - //! Invalid index register used in memory address (not encodable). - kErrorInvalidAddressIndex, - //! Invalid address scale (not encodable). - kErrorInvalidAddressScale, - //! Invalid use of 64-bit address. - kErrorInvalidAddress64Bit, - //! Invalid use of 64-bit address that require 32-bit zero-extension (X64). - kErrorInvalidAddress64BitZeroExtension, - //! Invalid displacement (not encodable). - kErrorInvalidDisplacement, - //! Invalid segment (X86). - kErrorInvalidSegment, - - //! Invalid immediate (out of bounds on X86 and invalid pattern on ARM). - kErrorInvalidImmediate, - - //! Invalid operand size. - kErrorInvalidOperandSize, - //! Ambiguous operand size (memory has zero size while it's required to determine the operation type. - kErrorAmbiguousOperandSize, - //! Mismatching operand size (size of multiple operands doesn't match the operation size). - kErrorOperandSizeMismatch, - - //! Invalid option. - kErrorInvalidOption, - //! Option already defined. - kErrorOptionAlreadyDefined, - - //! Invalid TypeId. - kErrorInvalidTypeId, - //! Invalid use of a 8-bit GPB-HIGH register. - kErrorInvalidUseOfGpbHi, - //! Invalid use of a 64-bit GPQ register in 32-bit mode. - kErrorInvalidUseOfGpq, - //! Invalid use of an 80-bit float (Type::kIdF80). - kErrorInvalidUseOfF80, - //! Some registers in the instruction muse be consecutive (some ARM and AVX512 neural-net instructions). - kErrorNotConsecutiveRegs, - - //! AsmJit requires a physical register, but no one is available. - kErrorNoMorePhysRegs, - //! A variable has been assigned more than once to a function argument (BaseCompiler). - kErrorOverlappedRegs, - //! Invalid register to hold stack arguments offset. - kErrorOverlappingStackRegWithRegArg, - - //! Unbound label cannot be evaluated by expression. - kErrorExpressionLabelNotBound, - //! Arithmetic overflow during expression evaluation. - kErrorExpressionOverflow, - - //! Count of AsmJit error codes. - kErrorCount -}; - -// ============================================================================ -// [asmjit::ByteOrder] -// ============================================================================ - -//! Byte order. -namespace ByteOrder { - enum : uint32_t { - kLE = 0, - kBE = 1, - kNative = ASMJIT_ARCH_LE ? kLE : kBE, - kSwapped = ASMJIT_ARCH_LE ? kBE : kLE - }; -} - -// ============================================================================ -// [asmjit::ptr_as_func / func_as_ptr] -// ============================================================================ - -template -static inline Func ptr_as_func(void* func) noexcept { return Support::ptr_cast_impl(func); } -template -static inline void* func_as_ptr(Func func) noexcept { return Support::ptr_cast_impl(func); } - -// ============================================================================ -// [asmjit::DebugUtils] -// ============================================================================ - -//! Debugging utilities. -namespace DebugUtils { - -//! Returns the error `err` passed. -//! -//! Provided for debugging purposes. Putting a breakpoint inside `errored` can -//! help with tracing the origin of any error reported / returned by AsmJit. -static constexpr Error errored(Error err) noexcept { return err; } - -//! Returns a printable version of `asmjit::Error` code. -ASMJIT_API const char* errorAsString(Error err) noexcept; - -//! Called to output debugging message(s). -ASMJIT_API void debugOutput(const char* str) noexcept; - -//! Called on assertion failure. -//! -//! \param file Source file name where it happened. -//! \param line Line in the source file. -//! \param msg Message to display. -//! -//! If you have problems with assertions put a breakpoint at assertionFailed() -//! function (asmjit/core/globals.cpp) and check the call stack to locate the -//! failing code. -ASMJIT_API void ASMJIT_NORETURN assertionFailed(const char* file, int line, const char* msg) noexcept; - -#if defined(ASMJIT_BUILD_DEBUG) -#define ASMJIT_ASSERT(EXP) \ - do { \ - if (ASMJIT_LIKELY(EXP)) \ - break; \ - ::asmjit::DebugUtils::assertionFailed(__FILE__, __LINE__, #EXP); \ - } while (0) -#else -#define ASMJIT_ASSERT(EXP) ((void)0) -#endif - -//! Used by AsmJit to propagate a possible `Error` produced by `...` to the caller. -#define ASMJIT_PROPAGATE(...) \ - do { \ - ::asmjit::Error _err = __VA_ARGS__; \ - if (ASMJIT_UNLIKELY(_err)) \ - return _err; \ - } while (0) - -} // {DebugUtils} - -//! \} - -ASMJIT_END_NAMESPACE - -#endif // _ASMJIT_CORE_GLOBALS_H diff --git a/libraries/asmjit/asmjit/core/inst.cpp b/libraries/asmjit/asmjit/core/inst.cpp deleted file mode 100644 index f5a2bf90b69..00000000000 --- a/libraries/asmjit/asmjit/core/inst.cpp +++ /dev/null @@ -1,124 +0,0 @@ -// [AsmJit] -// Machine Code Generation for C++. -// -// [License] -// Zlib - See LICENSE.md file in the package. - -#define ASMJIT_EXPORTS - -#include "../core/build.h" -#ifdef ASMJIT_BUILD_X86 - -#include "../core/arch.h" -#include "../core/inst.h" - -#ifdef ASMJIT_BUILD_X86 - #include "../x86/x86instapi_p.h" -#endif - -#ifdef ASMJIT_BUILD_ARM - #include "../arm/arminstapi_p.h" -#endif - -ASMJIT_BEGIN_NAMESPACE - -// ============================================================================ -// [asmjit::InstAPI - Text] -// ============================================================================ - -#ifndef ASMJIT_NO_TEXT -Error InstAPI::instIdToString(uint32_t archId, uint32_t instId, String& output) noexcept { -#ifdef ASMJIT_BUILD_X86 - if (ArchInfo::isX86Family(archId)) - return x86::InstInternal::instIdToString(archId, instId, output); -#endif - -#ifdef ASMJIT_BUILD_ARM - if (ArchInfo::isArmFamily(archId)) - return arm::InstInternal::instIdToString(archId, instId, output); -#endif - - return DebugUtils::errored(kErrorInvalidArch); -} - -uint32_t InstAPI::stringToInstId(uint32_t archId, const char* s, size_t len) noexcept { -#ifdef ASMJIT_BUILD_X86 - if (ArchInfo::isX86Family(archId)) - return x86::InstInternal::stringToInstId(archId, s, len); -#endif - -#ifdef ASMJIT_BUILD_ARM - if (ArchInfo::isArmFamily(archId)) - return arm::InstInternal::stringToInstId(archId, s, len); -#endif - - return 0; -} -#endif // !ASMJIT_NO_TEXT - -// ============================================================================ -// [asmjit::InstAPI - Validate] -// ============================================================================ - -#ifndef ASMJIT_NO_VALIDATION -Error InstAPI::validate(uint32_t archId, const BaseInst& inst, const Operand_* operands, uint32_t opCount) noexcept { -#ifdef ASMJIT_BUILD_X86 - if (ArchInfo::isX86Family(archId)) - return x86::InstInternal::validate(archId, inst, operands, opCount); -#endif - -#ifdef ASMJIT_BUILD_ARM - if (ArchInfo::isArmFamily(archId)) - return arm::InstInternal::validate(archId, inst, operands, opCount); -#endif - - return DebugUtils::errored(kErrorInvalidArch); -} -#endif // !ASMJIT_NO_VALIDATION - -// ============================================================================ -// [asmjit::InstAPI - QueryRWInfo] -// ============================================================================ - -#ifndef ASMJIT_NO_INTROSPECTION -Error InstAPI::queryRWInfo(uint32_t archId, const BaseInst& inst, const Operand_* operands, uint32_t opCount, InstRWInfo& out) noexcept { - if (ASMJIT_UNLIKELY(opCount > 6)) - return DebugUtils::errored(kErrorInvalidArgument); - -#ifdef ASMJIT_BUILD_X86 - if (ArchInfo::isX86Family(archId)) - return x86::InstInternal::queryRWInfo(archId, inst, operands, opCount, out); -#endif - -#ifdef ASMJIT_BUILD_ARM - if (ArchInfo::isArmFamily(archId)) - return arm::InstInternal::queryRWInfo(archId, inst, operands, opCount, out); -#endif - - return DebugUtils::errored(kErrorInvalidArch); -} -#endif // !ASMJIT_NO_INTROSPECTION - -// ============================================================================ -// [asmjit::InstAPI - QueryFeatures] -// ============================================================================ - -#ifndef ASMJIT_NO_INTROSPECTION -Error InstAPI::queryFeatures(uint32_t archId, const BaseInst& inst, const Operand_* operands, uint32_t opCount, BaseFeatures& out) noexcept { -#ifdef ASMJIT_BUILD_X86 - if (ArchInfo::isX86Family(archId)) - return x86::InstInternal::queryFeatures(archId, inst, operands, opCount, out); -#endif - -#ifdef ASMJIT_BUILD_ARM - if (ArchInfo::isArmFamily(archId)) - return arm::InstInternal::queryFeatures(archId, inst, operands, opCount, out); -#endif - - return DebugUtils::errored(kErrorInvalidArch); -} -#endif // !ASMJIT_NO_INTROSPECTION - -ASMJIT_END_NAMESPACE - -#endif // ASMJIT_BUILD_X86 diff --git a/libraries/asmjit/asmjit/core/inst.h b/libraries/asmjit/asmjit/core/inst.h deleted file mode 100644 index 9605b8b3d1f..00000000000 --- a/libraries/asmjit/asmjit/core/inst.h +++ /dev/null @@ -1,448 +0,0 @@ -// [AsmJit] -// Machine Code Generation for C++. -// -// [License] -// Zlib - See LICENSE.md file in the package. - -#ifndef _ASMJIT_CORE_INST_H -#define _ASMJIT_CORE_INST_H - -#include "../core/cpuinfo.h" -#include "../core/operand.h" -#include "../core/string.h" -#include "../core/support.h" - -ASMJIT_BEGIN_NAMESPACE - -//! \addtogroup asmjit_core -//! \{ - -// ============================================================================ -// [asmjit::InstInfo] -// ============================================================================ - -// TODO: Finalize instruction info and make more x86::InstDB methods/structs private. - -/* - -struct InstInfo { - //! Architecture agnostic attributes. - enum Attributes : uint32_t { - - - }; - - //! Instruction attributes. - uint32_t _attributes; - - inline void reset() noexcept { memset(this, 0, sizeof(*this)); } - - inline uint32_t attributes() const noexcept { return _attributes; } - inline bool hasAttribute(uint32_t attr) const noexcept { return (_attributes & attr) != 0; } -}; - -//! Gets attributes of the given instruction. -ASMJIT_API Error queryCommonInfo(uint32_t archId, uint32_t instId, InstInfo& out) noexcept; - -*/ - -// ============================================================================ -// [asmjit::InstRWInfo / OpRWInfo] -// ============================================================================ - -//! Read/Write information related to a single operand, used by `InstRWInfo`. -struct OpRWInfo { - //! Read/Write flags, see `OpRWInfo::Flags`. - uint32_t _opFlags; - //! Physical register index, if required. - uint8_t _physId; - //! Size of a possible memory operand that can replace a register operand. - uint8_t _rmSize; - //! Reserved for future use. - uint8_t _reserved[2]; - //! Read bit-mask where each bit represents one byte read from Reg/Mem. - uint64_t _readByteMask; - //! Write bit-mask where each bit represents one byte written to Reg/Mem. - uint64_t _writeByteMask; - //! Zero/Sign extend bit-mask where each bit represents one byte written to Reg/Mem. - uint64_t _extendByteMask; - - //! Flags describe how the operand is accessed and some additional information. - enum Flags : uint32_t { - //! Operand is read. - //! - //! \note This flag must be `0x00000001`. - kRead = 0x00000001u, - - //! Operand is written. - //! - //! \note This flag must be `0x00000002`. - kWrite = 0x00000002u, - - //! Operand is both read and written. - //! - //! \note This combination of flags must be `0x00000003`. - kRW = 0x00000003u, - - //! Register operand can be replaced by a memory operand. - kRegMem = 0x00000004u, - - //! The `extendByteMask()` represents a zero extension. - kZExt = 0x00000010u, - - //! Register operand must use `physId()`. - kRegPhysId = 0x00000100u, - //! Base register of a memory operand must use `physId()`. - kMemPhysId = 0x00000200u, - - //! This memory operand is only used to encode registers and doesn't access memory. - //! - //! X86 Specific - //! ------------ - //! - //! Instructions that use such feature include BNDLDX, BNDSTX, and LEA. - kMemFake = 0x000000400u, - - //! Base register of the memory operand will be read. - kMemBaseRead = 0x00001000u, - //! Base register of the memory operand will be written. - kMemBaseWrite = 0x00002000u, - //! Base register of the memory operand will be read & written. - kMemBaseRW = 0x00003000u, - - //! Index register of the memory operand will be read. - kMemIndexRead = 0x00004000u, - //! Index register of the memory operand will be written. - kMemIndexWrite = 0x00008000u, - //! Index register of the memory operand will be read & written. - kMemIndexRW = 0x0000C000u, - - //! Base register of the memory operand will be modified before the operation. - kMemBasePreModify = 0x00010000u, - //! Base register of the memory operand will be modified after the operation. - kMemBasePostModify = 0x00020000u - }; - - static_assert(kRead == 0x1, "OpRWInfo::kRead flag must be 0x1"); - static_assert(kWrite == 0x2, "OpRWInfo::kWrite flag must be 0x2"); - static_assert(kRegMem == 0x4, "OpRWInfo::kRegMem flag must be 0x4"); - - //! \name Reset - //! \{ - - inline void reset() noexcept { memset(this, 0, sizeof(*this)); } - inline void reset(uint32_t opFlags, uint32_t regSize, uint32_t physId = BaseReg::kIdBad) noexcept { - _opFlags = opFlags; - _physId = uint8_t(physId); - _rmSize = uint8_t((opFlags & kRegMem) ? regSize : uint32_t(0)); - _resetReserved(); - - uint64_t mask = Support::lsbMask(regSize); - _readByteMask = opFlags & kRead ? mask : uint64_t(0); - _writeByteMask = opFlags & kWrite ? mask : uint64_t(0); - _extendByteMask = 0; - } - - inline void _resetReserved() noexcept { - memset(_reserved, 0, sizeof(_reserved)); - } - - //! \} - - //! \name Operand Flags - //! \{ - - inline uint32_t opFlags() const noexcept { return _opFlags; } - inline bool hasOpFlag(uint32_t flag) const noexcept { return (_opFlags & flag) != 0; } - - inline void addOpFlags(uint32_t flags) noexcept { _opFlags |= flags; } - inline void clearOpFlags(uint32_t flags) noexcept { _opFlags &= ~flags; } - - inline bool isRead() const noexcept { return hasOpFlag(kRead); } - inline bool isWrite() const noexcept { return hasOpFlag(kWrite); } - inline bool isReadWrite() const noexcept { return (_opFlags & kRW) == kRW; } - inline bool isReadOnly() const noexcept { return (_opFlags & kRW) == kRead; } - inline bool isWriteOnly() const noexcept { return (_opFlags & kRW) == kWrite; } - inline bool isRm() const noexcept { return hasOpFlag(kRegMem); } - inline bool isZExt() const noexcept { return hasOpFlag(kZExt); } - - //! \} - - //! \name Physical Register ID - //! \{ - - inline uint32_t physId() const noexcept { return _physId; } - inline bool hasPhysId() const noexcept { return _physId != BaseReg::kIdBad; } - inline void setPhysId(uint32_t physId) noexcept { _physId = uint8_t(physId); } - - //! \} - - //! \name Reg/Mem - //! \{ - - inline uint32_t rmSize() const noexcept { return _rmSize; } - inline void setRmSize(uint32_t rmSize) noexcept { _rmSize = uint8_t(rmSize); } - - //! \} - - //! \name Read & Write Masks - //! \{ - - inline uint64_t readByteMask() const noexcept { return _readByteMask; } - inline uint64_t writeByteMask() const noexcept { return _writeByteMask; } - inline uint64_t extendByteMask() const noexcept { return _extendByteMask; } - - inline void setReadByteMask(uint64_t mask) noexcept { _readByteMask = mask; } - inline void setWriteByteMask(uint64_t mask) noexcept { _writeByteMask = mask; } - inline void setExtendByteMask(uint64_t mask) noexcept { _extendByteMask = mask; } - - //! \} -}; - -//! Read/Write information of an instruction. -struct InstRWInfo { - //! Instruction flags. - uint32_t _instFlags; - //! Mask of flags read. - uint32_t _readFlags; - //! Mask of flags written. - uint32_t _writeFlags; - //! Count of operands. - uint8_t _opCount; - //! CPU feature required for replacing register operand with memory operand. - uint8_t _rmFeature; - //! Reserved for future use. - uint8_t _reserved[19]; - //! Read/Write onfo of extra register (rep{} or kz{}). - OpRWInfo _extraReg; - //! Read/Write info of instruction operands. - OpRWInfo _operands[Globals::kMaxOpCount]; - - inline void reset() noexcept { memset(this, 0, sizeof(*this)); } - - inline uint32_t instFlags() const noexcept { return _instFlags; } - inline bool hasInstFlag(uint32_t flag) const noexcept { return (_instFlags & flag) != 0; } - - inline uint32_t opCount() const noexcept { return _opCount; } - - inline uint32_t readFlags() const noexcept { return _readFlags; } - inline uint32_t writeFlags() const noexcept { return _writeFlags; } - - //! Returns the CPU feature required to replace a register operand with memory - //! operand. If the returned feature is zero (none) then this instruction - //! either doesn't provide memory operand combination or there is no extra - //! CPU feature required. - //! - //! X86 Specific - //! ------------ - //! - //! Some AVX+ instructions may require extra features for replacing registers - //! with memory operands, for example VPSLLDQ instruction only supports - //! 'reg/reg/imm' combination on AVX/AVX2 capable CPUs and requires AVX-512 for - //! 'reg/mem/imm' combination. - inline uint32_t rmFeature() const noexcept { return _rmFeature; } - - inline const OpRWInfo& extraReg() const noexcept { return _extraReg; } - inline const OpRWInfo* operands() const noexcept { return _operands; } - - inline const OpRWInfo& operand(size_t index) const noexcept { - ASMJIT_ASSERT(index < Globals::kMaxOpCount); - return _operands[index]; - } -}; - -// ============================================================================ -// [asmjit::BaseInst] -// ============================================================================ - -//! Instruction id, options, and extraReg in a single structure. This structure -//! exists mainly to simplify analysis and validation API that requires `BaseInst` -//! and `Operand[]` array. -class BaseInst { -public: - //! Instruction id. - uint32_t _id; - //! Instruction options. - uint32_t _options; - //! Extra register used by instruction (either REP register or AVX-512 selector). - RegOnly _extraReg; - - enum Id : uint32_t { - //! Invalid or uninitialized instruction id. - kIdNone = 0x00000000u, - //! Abstract instruction (BaseBuilder and BaseCompiler). - kIdAbstract = 0x80000000u - }; - - enum Options : uint32_t { - //! Used internally by emitters for handling errors and rare cases. - kOptionReserved = 0x00000001u, - - //! Used only by Assembler to mark that `_op4` and `_op5` are used (internal). - kOptionOp4Op5Used = 0x00000002u, - - //! Prevents following a jump during compilation (BaseCompiler). - kOptionUnfollow = 0x00000010u, - - //! Overwrite the destination operand(s) (BaseCompiler). - //! - //! Hint that is important for register liveness analysis. It tells the - //! compiler that the destination operand will be overwritten now or by - //! adjacent instructions. BaseCompiler knows when a register is completely - //! overwritten by a single instruction, for example you don't have to - //! mark "movaps" or "pxor x, x", however, if a pair of instructions is - //! used and the first of them doesn't completely overwrite the content - //! of the destination, BaseCompiler fails to mark that register as dead. - //! - //! X86 Specific - //! ------------ - //! - //! - All instructions that always overwrite at least the size of the - //! register the virtual-register uses , for example "mov", "movq", - //! "movaps" don't need the overwrite option to be used - conversion, - //! shuffle, and other miscellaneous instructions included. - //! - //! - All instructions that clear the destination register if all operands - //! are the same, for example "xor x, x", "pcmpeqb x x", etc... - //! - //! - Consecutive instructions that partially overwrite the variable until - //! there is no old content require `BaseCompiler::overwrite()` to be used. - //! Some examples (not always the best use cases thought): - //! - //! - `movlps xmm0, ?` followed by `movhps xmm0, ?` and vice versa - //! - `movlpd xmm0, ?` followed by `movhpd xmm0, ?` and vice versa - //! - `mov al, ?` followed by `and ax, 0xFF` - //! - `mov al, ?` followed by `mov ah, al` - //! - `pinsrq xmm0, ?, 0` followed by `pinsrq xmm0, ?, 1` - //! - //! - If allocated variable is used temporarily for scalar operations. For - //! example if you allocate a full vector like `x86::Compiler::newXmm()` - //! and then use that vector for scalar operations you should use - //! `overwrite()` directive: - //! - //! - `sqrtss x, y` - only LO element of `x` is changed, if you don't - //! use HI elements, use `compiler.overwrite().sqrtss(x, y)`. - kOptionOverwrite = 0x00000020u, - - //! Emit short-form of the instruction. - kOptionShortForm = 0x00000040u, - //! Emit long-form of the instruction. - kOptionLongForm = 0x00000080u, - - //! Conditional jump is likely to be taken. - kOptionTaken = 0x00000100u, - //! Conditional jump is unlikely to be taken. - kOptionNotTaken = 0x00000200u - }; - - //! Control type. - enum ControlType : uint32_t { - //! No control type (doesn't jump). - kControlNone = 0u, - //! Unconditional jump. - kControlJump = 1u, - //! Conditional jump (branch). - kControlBranch = 2u, - //! Function call. - kControlCall = 3u, - //! Function return. - kControlReturn = 4u - }; - - //! \name Construction & Destruction - //! \{ - - inline explicit BaseInst(uint32_t id = 0, uint32_t options = 0) noexcept - : _id(id), - _options(options), - _extraReg() {} - - inline BaseInst(uint32_t id, uint32_t options, const RegOnly& extraReg) noexcept - : _id(id), - _options(options), - _extraReg(extraReg) {} - - inline BaseInst(uint32_t id, uint32_t options, const BaseReg& extraReg) noexcept - : _id(id), - _options(options), - _extraReg { extraReg.signature(), extraReg.id() } {} - - //! \} - - //! \name Instruction ID - //! \{ - - inline uint32_t id() const noexcept { return _id; } - inline void setId(uint32_t id) noexcept { _id = id; } - inline void resetId() noexcept { _id = 0; } - - //! \} - - //! \name Instruction Options - //! \{ - - inline uint32_t options() const noexcept { return _options; } - inline void setOptions(uint32_t options) noexcept { _options = options; } - inline void addOptions(uint32_t options) noexcept { _options |= options; } - inline void clearOptions(uint32_t options) noexcept { _options &= ~options; } - inline void resetOptions() noexcept { _options = 0; } - - //! \} - - //! \name Extra Register - //! \{ - - inline bool hasExtraReg() const noexcept { return _extraReg.isReg(); } - inline RegOnly& extraReg() noexcept { return _extraReg; } - inline const RegOnly& extraReg() const noexcept { return _extraReg; } - inline void setExtraReg(const BaseReg& reg) noexcept { _extraReg.init(reg); } - inline void setExtraReg(const RegOnly& reg) noexcept { _extraReg.init(reg); } - inline void resetExtraReg() noexcept { _extraReg.reset(); } - - //! \} -}; - -// ============================================================================ -// [asmjit::InstAPI] -// ============================================================================ - -//! Instruction API. -namespace InstAPI { - -#ifndef ASMJIT_NO_TEXT -//! Appends the name of the instruction specified by `instId` and `instOptions` -//! into the `output` string. -//! -//! \note Instruction options would only affect instruction prefix & suffix, -//! other options would be ignored. If `instOptions` is zero then only raw -//! instruction name (without any additional text) will be appended. -ASMJIT_API Error instIdToString(uint32_t archId, uint32_t instId, String& output) noexcept; - -//! Parses an instruction name in the given string `s`. Length is specified -//! by `len` argument, which can be `SIZE_MAX` if `s` is known to be null -//! terminated. -//! -//! The output is stored in `instId`. -ASMJIT_API uint32_t stringToInstId(uint32_t archId, const char* s, size_t len) noexcept; -#endif // !ASMJIT_NO_TEXT - -#ifndef ASMJIT_NO_VALIDATION -//! Validates the given instruction. -ASMJIT_API Error validate(uint32_t archId, const BaseInst& inst, const Operand_* operands, uint32_t opCount) noexcept; -#endif // !ASMJIT_NO_VALIDATION - -#ifndef ASMJIT_NO_INTROSPECTION -//! Gets Read/Write information of the given instruction. -ASMJIT_API Error queryRWInfo(uint32_t archId, const BaseInst& inst, const Operand_* operands, uint32_t opCount, InstRWInfo& out) noexcept; - -//! Gets CPU features required by the given instruction. -ASMJIT_API Error queryFeatures(uint32_t archId, const BaseInst& inst, const Operand_* operands, uint32_t opCount, BaseFeatures& out) noexcept; -#endif // !ASMJIT_NO_INTROSPECTION - -} // {InstAPI} - -//! \} - -ASMJIT_END_NAMESPACE - -#endif // _ASMJIT_CORE_INST_H diff --git a/libraries/asmjit/asmjit/core/jitallocator.cpp b/libraries/asmjit/asmjit/core/jitallocator.cpp deleted file mode 100644 index 76166d2a7c4..00000000000 --- a/libraries/asmjit/asmjit/core/jitallocator.cpp +++ /dev/null @@ -1,1137 +0,0 @@ -// [AsmJit] -// Machine Code Generation for C++. -// -// [License] -// Zlib - See LICENSE.md file in the package. - -#define ASMJIT_EXPORTS - -#include "../core/build.h" -#ifndef ASMJIT_NO_JIT - -#include "../core/arch.h" -#include "../core/jitallocator.h" -#include "../core/osutils.h" -#include "../core/support.h" -#include "../core/virtmem.h" -#include "../core/zone.h" -#include "../core/zonelist.h" -#include "../core/zonetree.h" - -ASMJIT_BEGIN_NAMESPACE - -// ============================================================================ -// [asmjit::JitAllocator - Constants] -// ============================================================================ - -enum JitAllocatorConstants : uint32_t { - //! Number of pools to use when `JitAllocator::kOptionUseMultiplePools` is set. - //! - //! Each pool increases granularity twice to make memory management more - //! efficient. Ideal number of pools appears to be 3 to 4 as it distributes - //! small and large functions properly. - kJitAllocatorMultiPoolCount = 3, - - //! Minimum granularity (and the default granularity for pool #0). - kJitAllocatorBaseGranularity = 64, - - //! Maximum block size (16MB). - kJitAllocatorMaxBlockSize = 1024 * 1024 * 16 -}; - -static inline uint32_t JitAllocator_defaultFillPattern() noexcept { - // X86 and X86_64 - 4x 'int3' instruction. - if (ASMJIT_ARCH_X86) - return 0xCCCCCCCCu; - - // Unknown... - return 0u; -} - -// ============================================================================ -// [asmjit::JitAllocator - BitFlipIterator] -// ============================================================================ - -//! BitWord[] iterator used by `JitAllocator` that can flip the search pattern -//! during iteration. -template -class BitFlipIterator { -public: - ASMJIT_INLINE BitFlipIterator(const T* data, size_t numBitWords, size_t start = 0, T xorMask = 0) noexcept { - init(data, numBitWords, start, xorMask); - } - - ASMJIT_INLINE void init(const T* data, size_t numBitWords, size_t start = 0, T xorMask = 0) noexcept { - const T* ptr = data + (start / Support::bitSizeOf()); - size_t idx = Support::alignDown(start, Support::bitSizeOf()); - size_t end = numBitWords * Support::bitSizeOf(); - - T bitWord = T(0); - if (idx < end) { - bitWord = (*ptr++ ^ xorMask) & (Support::allOnes() << (start % Support::bitSizeOf())); - while (!bitWord && (idx += Support::bitSizeOf()) < end) - bitWord = *ptr++ ^ xorMask; - } - - _ptr = ptr; - _idx = idx; - _end = end; - _current = bitWord; - _xorMask = xorMask; - } - - ASMJIT_INLINE bool hasNext() const noexcept { - return _current != T(0); - } - - ASMJIT_INLINE size_t next() noexcept { - T bitWord = _current; - ASMJIT_ASSERT(bitWord != T(0)); - - uint32_t bit = Support::ctz(bitWord); - bitWord ^= T(1u) << bit; - - size_t n = _idx + bit; - while (!bitWord && (_idx += Support::bitSizeOf()) < _end) - bitWord = *_ptr++ ^ _xorMask; - - _current = bitWord; - return n; - } - - ASMJIT_INLINE size_t nextAndFlip() noexcept { - T bitWord = _current; - ASMJIT_ASSERT(bitWord != T(0)); - - uint32_t bit = Support::ctz(bitWord); - bitWord ^= Support::allOnes() << bit; - _xorMask ^= Support::allOnes(); - - size_t n = _idx + bit; - while (!bitWord && (_idx += Support::bitSizeOf()) < _end) - bitWord = *_ptr++ ^ _xorMask; - - _current = bitWord; - return n; - } - - ASMJIT_INLINE size_t peekNext() const noexcept { - ASMJIT_ASSERT(_current != T(0)); - return _idx + Support::ctz(_current); - } - - const T* _ptr; - size_t _idx; - size_t _end; - T _current; - T _xorMask; -}; - -// ============================================================================ -// [asmjit::JitAllocator - Pool] -// ============================================================================ - -class JitAllocatorBlock; - -class JitAllocatorPool { -public: - ASMJIT_NONCOPYABLE(JitAllocatorPool) - - inline JitAllocatorPool(uint32_t granularity) noexcept - : blocks(), - cursor(nullptr), - blockCount(0), - granularity(uint16_t(granularity)), - granularityLog2(uint8_t(Support::ctz(granularity))), - emptyBlockCount(0), - totalAreaSize(0), - totalAreaUsed(0), - totalOverheadBytes(0) {} - - inline void reset() noexcept { - blocks.reset(); - cursor = nullptr; - blockCount = 0; - totalAreaSize = 0; - totalAreaUsed = 0; - totalOverheadBytes = 0; - } - - inline size_t byteSizeFromAreaSize(uint32_t areaSize) const noexcept { return size_t(areaSize) * granularity; } - inline uint32_t areaSizeFromByteSize(size_t size) const noexcept { return uint32_t((size + granularity - 1) >> granularityLog2); } - - inline size_t bitWordCountFromAreaSize(uint32_t areaSize) const noexcept { - using namespace Support; - return alignUp(areaSize, kBitWordSizeInBits) / kBitWordSizeInBits; - } - - //! Double linked list of blocks. - ZoneList blocks; - //! Where to start looking first. - JitAllocatorBlock* cursor; - - //! Count of blocks. - uint32_t blockCount; - //! Allocation granularity. - uint16_t granularity; - //! Log2(granularity). - uint8_t granularityLog2; - //! Count of empty blocks (either 0 or 1 as we won't keep more blocks empty). - uint8_t emptyBlockCount; - - //! Number of bits reserved across all blocks. - size_t totalAreaSize; - //! Number of bits used across all blocks. - size_t totalAreaUsed; - //! Overhead of all blocks (in bytes). - size_t totalOverheadBytes; -}; - -// ============================================================================ -// [asmjit::JitAllocator - Block] -// ============================================================================ - -class JitAllocatorBlock : public ZoneTreeNodeT, - public ZoneListNode { -public: - ASMJIT_NONCOPYABLE(JitAllocatorBlock) - - enum Flags : uint32_t { - //! Block is empty. - kFlagEmpty = 0x00000001u, - //! Block is dirty (largestUnusedArea, searchStart, searchEnd). - kFlagDirty = 0x00000002u, - //! Block is dual-mapped. - kFlagDualMapped = 0x00000004u - }; - - inline JitAllocatorBlock( - JitAllocatorPool* pool, - VirtMem::DualMapping mapping, - size_t blockSize, - uint32_t blockFlags, - Support::BitWord* usedBitVector, - Support::BitWord* stopBitVector, - uint32_t areaSize) noexcept - : ZoneTreeNodeT(), - pool(pool), - mapping(mapping), - blockSize(blockSize), - flags(blockFlags), - areaSize(areaSize), - areaUsed(0), - largestUnusedArea(areaSize), - searchStart(0), - searchEnd(areaSize), - usedBitVector(usedBitVector), - stopBitVector(stopBitVector) {} - - inline uint8_t* roPtr() const noexcept { return static_cast(mapping.ro); } - inline uint8_t* rwPtr() const noexcept { return static_cast(mapping.rw); } - - inline bool hasFlag(uint32_t f) const noexcept { return (flags & f) != 0; } - inline void addFlags(uint32_t f) noexcept { flags |= f; } - inline void clearFlags(uint32_t f) noexcept { flags &= ~f; } - - inline uint32_t areaAvailable() const noexcept { return areaSize - areaUsed; } - - inline void increaseUsedArea(uint32_t value) noexcept { - areaUsed += value; - pool->totalAreaUsed += value; - } - - inline void decreaseUsedArea(uint32_t value) noexcept { - areaUsed -= value; - pool->totalAreaUsed -= value; - } - - // RBTree default CMP uses '<' and '>' operators. - inline bool operator<(const JitAllocatorBlock& other) const noexcept { return roPtr() < other.roPtr(); } - inline bool operator>(const JitAllocatorBlock& other) const noexcept { return roPtr() > other.roPtr(); } - - // Special implementation for querying blocks by `key`, which must be in `[BlockPtr, BlockPtr + BlockSize)` range. - inline bool operator<(const uint8_t* key) const noexcept { return roPtr() + blockSize <= key; } - inline bool operator>(const uint8_t* key) const noexcept { return roPtr() > key; } - - //! Link to the pool that owns this block. - JitAllocatorPool* pool; - //! Virtual memory mapping - either single mapping (both pointers equal) or - //! dual mapping, where one pointer is Read+Execute and the second Read+Write. - VirtMem::DualMapping mapping; - //! Virtual memory size (block size) [bytes]. - size_t blockSize; - - //! Block flags. - uint32_t flags; - //! Size of the whole block area (bit-vector size). - uint32_t areaSize; - //! Used area (number of bits in bit-vector used). - uint32_t areaUsed; - //! The largest unused continuous area in the bit-vector (or `areaSize` to initiate rescan). - uint32_t largestUnusedArea; - //! Start of a search range (for unused bits). - uint32_t searchStart; - //! End of a search range (for unused bits). - uint32_t searchEnd; - - //! Used bit-vector (0 = unused, 1 = used). - Support::BitWord* usedBitVector; - //! Stop bit-vector (0 = don't care, 1 = stop). - Support::BitWord* stopBitVector; -}; - -// ============================================================================ -// [asmjit::JitAllocator - PrivateImpl] -// ============================================================================ - -class JitAllocatorPrivateImpl : public JitAllocator::Impl { -public: - inline JitAllocatorPrivateImpl(JitAllocatorPool* pools, size_t poolCount) noexcept - : JitAllocator::Impl {}, - pools(pools), - poolCount(poolCount) {} - inline ~JitAllocatorPrivateImpl() noexcept {} - - //! Lock for thread safety. - mutable Lock lock; - //! System page size (also a minimum block size). - uint32_t pageSize; - - //! Blocks from all pools in RBTree. - ZoneTree tree; - //! Allocator pools. - JitAllocatorPool* pools; - //! Number of allocator pools. - size_t poolCount; -}; - -static const JitAllocator::Impl JitAllocatorImpl_none {}; -static const JitAllocator::CreateParams JitAllocatorParams_none {}; - -// ============================================================================ -// [asmjit::JitAllocator - Utilities] -// ============================================================================ - -static inline JitAllocatorPrivateImpl* JitAllocatorImpl_new(const JitAllocator::CreateParams* params) noexcept { - VirtMem::Info vmInfo = VirtMem::info(); - - if (!params) - params = &JitAllocatorParams_none; - - uint32_t options = params->options; - uint32_t blockSize = params->blockSize; - uint32_t granularity = params->granularity; - uint32_t fillPattern = params->fillPattern; - - // Setup pool count to [1..3]. - size_t poolCount = 1; - if (options & JitAllocator::kOptionUseMultiplePools) - poolCount = kJitAllocatorMultiPoolCount;; - - // Setup block size [64kB..256MB]. - if (blockSize < 64 * 1024 || blockSize > 256 * 1024 * 1024 || !Support::isPowerOf2(blockSize)) - blockSize = vmInfo.pageGranularity; - - // Setup granularity [64..256]. - if (granularity < 64 || granularity > 256 || !Support::isPowerOf2(granularity)) - granularity = kJitAllocatorBaseGranularity; - - // Setup fill-pattern. - if (!(options & JitAllocator::kOptionCustomFillPattern)) - fillPattern = JitAllocator_defaultFillPattern(); - - size_t size = sizeof(JitAllocatorPrivateImpl) + sizeof(JitAllocatorPool) * poolCount; - void* p = ::malloc(size); - if (ASMJIT_UNLIKELY(!p)) - return nullptr; - - JitAllocatorPool* pools = reinterpret_cast((uint8_t*)p + sizeof(JitAllocatorPrivateImpl)); - JitAllocatorPrivateImpl* impl = new(p) JitAllocatorPrivateImpl(pools, poolCount); - - impl->options = options; - impl->blockSize = blockSize; - impl->granularity = granularity; - impl->fillPattern = fillPattern; - impl->pageSize = vmInfo.pageSize; - - for (size_t poolId = 0; poolId < poolCount; poolId++) - new(&pools[poolId]) JitAllocatorPool(granularity << poolId); - - return impl; -} - -static inline void JitAllocatorImpl_destroy(JitAllocatorPrivateImpl* impl) noexcept { - impl->~JitAllocatorPrivateImpl(); - ::free(impl); -} - -static inline size_t JitAllocatorImpl_sizeToPoolId(const JitAllocatorPrivateImpl* impl, size_t size) noexcept { - size_t poolId = impl->poolCount - 1; - size_t granularity = size_t(impl->granularity) << poolId; - - while (poolId) { - if (Support::alignUp(size, granularity) == size) - break; - poolId--; - granularity >>= 1; - } - - return poolId; -} - -static inline size_t JitAllocatorImpl_bitVectorSizeToByteSize(uint32_t areaSize) noexcept { - using Support::kBitWordSizeInBits; - return ((areaSize + kBitWordSizeInBits - 1u) / kBitWordSizeInBits) * sizeof(Support::BitWord); -} - -static inline size_t JitAllocatorImpl_calculateIdealBlockSize(JitAllocatorPrivateImpl* impl, JitAllocatorPool* pool, size_t allocationSize) noexcept { - JitAllocatorBlock* last = pool->blocks.last(); - size_t blockSize = last ? last->blockSize : size_t(impl->blockSize); - - if (blockSize < kJitAllocatorMaxBlockSize) - blockSize *= 2u; - - if (allocationSize > blockSize) { - blockSize = Support::alignUp(allocationSize, impl->blockSize); - if (ASMJIT_UNLIKELY(blockSize < allocationSize)) - return 0; // Overflown. - } - - return blockSize; -} - -ASMJIT_FAVOR_SPEED static void JitAllocatorImpl_fillPattern(void* mem, uint32_t pattern, size_t sizeInBytes) noexcept { - size_t n = sizeInBytes / 4u; - uint32_t* p = static_cast(mem); - - for (size_t i = 0; i < n; i++) - p[i] = pattern; -} - -// Allocate a new `JitAllocatorBlock` for the given `blockSize`. -// -// NOTE: The block doesn't have `kFlagEmpty` flag set, because the new block -// is only allocated when it's actually needed, so it would be cleared anyway. -static JitAllocatorBlock* JitAllocatorImpl_newBlock(JitAllocatorPrivateImpl* impl, JitAllocatorPool* pool, size_t blockSize) noexcept { - using Support::BitWord; - using Support::kBitWordSizeInBits; - - uint32_t areaSize = uint32_t((blockSize + pool->granularity - 1) >> pool->granularityLog2); - uint32_t numBitWords = (areaSize + kBitWordSizeInBits - 1u) / kBitWordSizeInBits; - - JitAllocatorBlock* block = static_cast(::malloc(sizeof(JitAllocatorBlock))); - BitWord* bitWords = nullptr; - VirtMem::DualMapping virtMem {}; - Error err = kErrorOutOfMemory; - - if (block != nullptr) - bitWords = static_cast(::malloc(size_t(numBitWords) * 2 * sizeof(BitWord))); - - uint32_t blockFlags = 0; - if (bitWords != nullptr) { - if (impl->options & JitAllocator::kOptionUseDualMapping) { - err = VirtMem::allocDualMapping(&virtMem, blockSize, VirtMem::kAccessReadWrite | VirtMem::kAccessExecute); - blockFlags |= JitAllocatorBlock::kFlagDualMapped; - } - else { - err = VirtMem::alloc(&virtMem.ro, blockSize, VirtMem::kAccessReadWrite | VirtMem::kAccessExecute); - virtMem.rw = virtMem.ro; - } - } - - // Out of memory. - if (ASMJIT_UNLIKELY(!block || !bitWords || err != kErrorOk)) { - if (bitWords) ::free(bitWords); - if (block) ::free(block); - return nullptr; - } - - // Fill the memory if the secure mode is enabled. - if (impl->options & JitAllocator::kOptionFillUnusedMemory) - JitAllocatorImpl_fillPattern(virtMem.rw, impl->fillPattern, blockSize); - - memset(bitWords, 0, size_t(numBitWords) * 2 * sizeof(BitWord)); - return new(block) JitAllocatorBlock(pool, virtMem, blockSize, blockFlags, bitWords, bitWords + numBitWords, areaSize); -} - -static void JitAllocatorImpl_deleteBlock(JitAllocatorPrivateImpl* impl, JitAllocatorBlock* block) noexcept { - ASMJIT_UNUSED(impl); - - if (block->flags & JitAllocatorBlock::kFlagDualMapped) - VirtMem::releaseDualMapping(&block->mapping, block->blockSize); - else - VirtMem::release(block->mapping.ro, block->blockSize); - - ::free(block->usedBitVector); - ::free(block); -} - -static void JitAllocatorImpl_insertBlock(JitAllocatorPrivateImpl* impl, JitAllocatorBlock* block) noexcept { - JitAllocatorPool* pool = block->pool; - - if (!pool->cursor) - pool->cursor = block; - - // Add to RBTree and List. - impl->tree.insert(block); - pool->blocks.append(block); - - // Update statistics. - pool->blockCount++; - pool->totalAreaSize += block->areaSize; - pool->totalOverheadBytes += sizeof(JitAllocatorBlock) + JitAllocatorImpl_bitVectorSizeToByteSize(block->areaSize) * 2u; -} - -static void JitAllocatorImpl_removeBlock(JitAllocatorPrivateImpl* impl, JitAllocatorBlock* block) noexcept { - JitAllocatorPool* pool = block->pool; - - // Remove from RBTree and List. - if (pool->cursor == block) - pool->cursor = block->hasPrev() ? block->prev() : block->next(); - - impl->tree.remove(block); - pool->blocks.unlink(block); - - // Update statistics. - pool->blockCount--; - pool->totalAreaSize -= block->areaSize; - pool->totalOverheadBytes -= sizeof(JitAllocatorBlock) + JitAllocatorImpl_bitVectorSizeToByteSize(block->areaSize) * 2u; -} - -static void JitAllocatorImpl_wipeOutBlock(JitAllocatorPrivateImpl* impl, JitAllocatorBlock* block) noexcept { - JitAllocatorPool* pool = block->pool; - - if (block->hasFlag(JitAllocatorBlock::kFlagEmpty)) - return; - - uint32_t areaSize = block->areaSize; - uint32_t granularity = pool->granularity; - size_t numBitWords = pool->bitWordCountFromAreaSize(areaSize); - - if (impl->options & JitAllocator::kOptionFillUnusedMemory) { - BitFlipIterator it(block->usedBitVector, numBitWords); - - while (it.hasNext()) { - uint32_t start = uint32_t(it.nextAndFlip()); - uint32_t end = areaSize; - - if (it.hasNext()) - end = uint32_t(it.nextAndFlip()); - - JitAllocatorImpl_fillPattern(block->rwPtr() + start * granularity, impl->fillPattern, (end - start) * granularity); - } - } - - memset(block->usedBitVector, 0, size_t(numBitWords) * sizeof(Support::BitWord)); - memset(block->stopBitVector, 0, size_t(numBitWords) * sizeof(Support::BitWord)); - - block->areaUsed = 0; - block->largestUnusedArea = areaSize; - block->searchStart = 0; - block->searchEnd = areaSize; - block->addFlags(JitAllocatorBlock::kFlagEmpty); - block->clearFlags(JitAllocatorBlock::kFlagDirty); -} - -// ============================================================================ -// [asmjit::JitAllocator - Construction / Destruction] -// ============================================================================ - -JitAllocator::JitAllocator(const CreateParams* params) noexcept { - _impl = JitAllocatorImpl_new(params); - if (ASMJIT_UNLIKELY(!_impl)) - _impl = const_cast(&JitAllocatorImpl_none); -} - -JitAllocator::~JitAllocator() noexcept { - if (_impl == &JitAllocatorImpl_none) - return; - - reset(Globals::kResetHard); - JitAllocatorImpl_destroy(static_cast(_impl)); -} - -// ============================================================================ -// [asmjit::JitAllocator - Reset] -// ============================================================================ - -void JitAllocator::reset(uint32_t resetPolicy) noexcept { - if (_impl == &JitAllocatorImpl_none) - return; - - JitAllocatorPrivateImpl* impl = static_cast(_impl); - impl->tree.reset(); - size_t poolCount = impl->poolCount; - - for (size_t poolId = 0; poolId < poolCount; poolId++) { - JitAllocatorPool& pool = impl->pools[poolId]; - JitAllocatorBlock* block = pool.blocks.first(); - - JitAllocatorBlock* blockToKeep = nullptr; - if (resetPolicy != Globals::kResetHard && !(impl->options & kOptionImmediateRelease)) { - blockToKeep = block; - block = block->next(); - } - - while (block) { - JitAllocatorBlock* next = block->next(); - JitAllocatorImpl_deleteBlock(impl, block); - block = next; - } - - pool.reset(); - - if (blockToKeep) { - blockToKeep->_listNodes[0] = nullptr; - blockToKeep->_listNodes[1] = nullptr; - JitAllocatorImpl_wipeOutBlock(impl, blockToKeep); - JitAllocatorImpl_insertBlock(impl, blockToKeep); - pool.emptyBlockCount = 1; - } - } -} - -// ============================================================================ -// [asmjit::JitAllocator - Statistics] -// ============================================================================ - -JitAllocator::Statistics JitAllocator::statistics() const noexcept { - Statistics statistics; - statistics.reset(); - - if (ASMJIT_LIKELY(_impl != &JitAllocatorImpl_none)) { - JitAllocatorPrivateImpl* impl = static_cast(_impl); - ScopedLock locked(impl->lock); - - size_t poolCount = impl->poolCount; - for (size_t poolId = 0; poolId < poolCount; poolId++) { - const JitAllocatorPool& pool = impl->pools[poolId]; - statistics._blockCount += size_t(pool.blockCount); - statistics._reservedSize += size_t(pool.totalAreaSize) * pool.granularity; - statistics._usedSize += size_t(pool.totalAreaUsed) * pool.granularity; - statistics._overheadSize += size_t(pool.totalOverheadBytes); - } - } - - return statistics; -} - -// ============================================================================ -// [asmjit::JitAllocator - Alloc / Release] -// ============================================================================ - -Error JitAllocator::alloc(void** roPtrOut, void** rwPtrOut, size_t size) noexcept { - if (ASMJIT_UNLIKELY(_impl == &JitAllocatorImpl_none)) - return DebugUtils::errored(kErrorNotInitialized); - - JitAllocatorPrivateImpl* impl = static_cast(_impl); - constexpr uint32_t kNoIndex = std::numeric_limits::max(); - - *roPtrOut = nullptr; - *rwPtrOut = nullptr; - - // Align to the minimum granularity by default. - size = Support::alignUp(size, impl->granularity); - if (ASMJIT_UNLIKELY(size == 0)) - return DebugUtils::errored(kErrorInvalidArgument); - - if (ASMJIT_UNLIKELY(size > std::numeric_limits::max() / 2)) - return DebugUtils::errored(kErrorTooLarge); - - ScopedLock locked(impl->lock); - JitAllocatorPool* pool = &impl->pools[JitAllocatorImpl_sizeToPoolId(impl, size)]; - - uint32_t areaIndex = kNoIndex; - uint32_t areaSize = uint32_t(pool->areaSizeFromByteSize(size)); - - // Try to find the requested memory area in existing blocks. - JitAllocatorBlock* block = pool->blocks.first(); - if (block) { - JitAllocatorBlock* initial = block; - do { - JitAllocatorBlock* next = block->hasNext() ? block->next() : pool->blocks.first(); - if (block->areaAvailable() >= areaSize) { - if (block->hasFlag(JitAllocatorBlock::kFlagDirty) || block->largestUnusedArea >= areaSize) { - uint32_t blockAreaSize = block->areaSize; - uint32_t searchStart = block->searchStart; - uint32_t searchEnd = block->searchEnd; - - BitFlipIterator it( - block->usedBitVector, - pool->bitWordCountFromAreaSize(searchEnd), - searchStart, - Support::allOnes()); - - // If there is unused area available then there has to be at least one match. - ASMJIT_ASSERT(it.hasNext()); - - uint32_t bestArea = blockAreaSize; - uint32_t largestArea = 0; - uint32_t holeIndex = uint32_t(it.peekNext()); - uint32_t holeEnd = holeIndex; - - searchStart = holeIndex; - do { - holeIndex = uint32_t(it.nextAndFlip()); - if (holeIndex >= searchEnd) break; - - holeEnd = it.hasNext() ? Support::min(searchEnd, uint32_t(it.nextAndFlip())) : searchEnd; - uint32_t holeSize = holeEnd - holeIndex; - - if (holeSize >= areaSize && bestArea >= holeSize) { - largestArea = Support::max(largestArea, bestArea); - bestArea = holeSize; - areaIndex = holeIndex; - } - else { - largestArea = Support::max(largestArea, holeSize); - } - } while (it.hasNext()); - searchEnd = holeEnd; - - // Because we have traversed the entire block, we can now mark the - // largest unused area that can be used to cache the next traversal. - block->searchStart = searchStart; - block->searchEnd = searchEnd; - block->largestUnusedArea = largestArea; - block->clearFlags(JitAllocatorBlock::kFlagDirty); - - if (areaIndex != kNoIndex) { - if (searchStart == areaIndex) - block->searchStart += areaSize; - break; - } - } - } - - block = next; - } while (block != initial); - } - - // Allocate a new block if there is no region of a required width. - if (areaIndex == kNoIndex) { - size_t blockSize = JitAllocatorImpl_calculateIdealBlockSize(impl, pool, size); - if (ASMJIT_UNLIKELY(!blockSize)) - return DebugUtils::errored(kErrorOutOfMemory); - - block = JitAllocatorImpl_newBlock(impl, pool, blockSize); - - if (ASMJIT_UNLIKELY(!block)) - return DebugUtils::errored(kErrorOutOfMemory); - - JitAllocatorImpl_insertBlock(impl, block); - areaIndex = 0; - block->searchStart = areaSize; - block->largestUnusedArea = block->areaSize - areaSize; - } - - // Update statistics. - block->increaseUsedArea(areaSize); - - // Handle special cases. - if (block->hasFlag(JitAllocatorBlock::kFlagEmpty)) { - pool->emptyBlockCount--; - block->clearFlags(JitAllocatorBlock::kFlagEmpty); - } - - if (block->areaAvailable() == 0) { - // The whole block is filled. - block->searchStart = block->areaSize; - block->searchEnd = 0; - block->largestUnusedArea = 0; - block->clearFlags(JitAllocatorBlock::kFlagDirty); - } - - // Mark the newly allocated space as occupied and also the sentinel. - Support::bitVectorFill(block->usedBitVector, areaIndex, areaSize); - Support::bitVectorSetBit(block->stopBitVector, areaIndex + areaSize - 1, true); - - // Return a pointer to the allocated memory. - size_t offset = pool->byteSizeFromAreaSize(areaIndex); - ASMJIT_ASSERT(offset <= block->blockSize - size); - - *roPtrOut = block->roPtr() + offset; - *rwPtrOut = block->rwPtr() + offset; - return kErrorOk; -} - -Error JitAllocator::release(void* ro) noexcept { - if (ASMJIT_UNLIKELY(_impl == &JitAllocatorImpl_none)) - return DebugUtils::errored(kErrorNotInitialized); - - if (ASMJIT_UNLIKELY(!ro)) - return DebugUtils::errored(kErrorInvalidArgument); - - JitAllocatorPrivateImpl* impl = static_cast(_impl); - ScopedLock locked(impl->lock); - - JitAllocatorBlock* block = impl->tree.get(static_cast(ro)); - if (ASMJIT_UNLIKELY(!block)) - return DebugUtils::errored(kErrorInvalidState); - - // Offset relative to the start of the block. - JitAllocatorPool* pool = block->pool; - size_t offset = (size_t)((uint8_t*)ro - block->roPtr()); - - // The first bit representing the allocated area and its size. - uint32_t areaIndex = uint32_t(offset >> pool->granularityLog2); - uint32_t areaLast = uint32_t(Support::bitVectorIndexOf(block->stopBitVector, areaIndex, true)); - uint32_t areaSize = areaLast - areaIndex + 1; - - // Update the search region and statistics. - block->searchStart = Support::min(block->searchStart, areaIndex); - block->searchEnd = Support::max(block->searchEnd, areaLast + 1); - block->addFlags(JitAllocatorBlock::kFlagDirty); - block->decreaseUsedArea(areaSize); - - // Clear all occupied bits and also the sentinel. - Support::bitVectorClear(block->usedBitVector, areaIndex, areaSize); - Support::bitVectorSetBit(block->stopBitVector, areaLast, false); - - // Fill the released memory if the secure mode is enabled. - if (impl->options & kOptionFillUnusedMemory) - JitAllocatorImpl_fillPattern(block->rwPtr() + areaIndex * pool->granularity, impl->fillPattern, areaSize * pool->granularity); - - // Release the whole block if it became empty. - if (block->areaUsed == 0) { - if (pool->emptyBlockCount || (impl->options & kOptionImmediateRelease)) { - JitAllocatorImpl_removeBlock(impl, block); - JitAllocatorImpl_deleteBlock(impl, block); - } - else { - pool->emptyBlockCount++; - block->largestUnusedArea = areaSize; - block->searchStart = 0; - block->searchEnd = areaSize; - block->addFlags(JitAllocatorBlock::kFlagEmpty); - block->clearFlags(JitAllocatorBlock::kFlagDirty); - } - } - - return kErrorOk; -} - -Error JitAllocator::shrink(void* ro, size_t newSize) noexcept { - if (ASMJIT_UNLIKELY(_impl == &JitAllocatorImpl_none)) - return DebugUtils::errored(kErrorNotInitialized); - - if (ASMJIT_UNLIKELY(!ro)) - return DebugUtils::errored(kErrorInvalidArgument); - - if (ASMJIT_UNLIKELY(newSize == 0)) - return release(ro); - - JitAllocatorPrivateImpl* impl = static_cast(_impl); - ScopedLock locked(impl->lock); - JitAllocatorBlock* block = impl->tree.get(static_cast(ro)); - - if (ASMJIT_UNLIKELY(!block)) - return DebugUtils::errored(kErrorInvalidArgument); - - // Offset relative to the start of the block. - JitAllocatorPool* pool = block->pool; - size_t offset = (size_t)((uint8_t*)ro - block->roPtr()); - - // The first bit representing the allocated area and its size. - uint32_t areaIndex = uint32_t(offset >> pool->granularityLog2); - uint32_t areaOldSize = uint32_t(Support::bitVectorIndexOf(block->stopBitVector, areaIndex, true)) + 1 - areaIndex; - uint32_t areaNewSize = pool->areaSizeFromByteSize(newSize); - - if (ASMJIT_UNLIKELY(areaNewSize > areaOldSize)) - return DebugUtils::errored(kErrorInvalidState); - - uint32_t areaDiff = areaOldSize - areaNewSize; - if (!areaDiff) - return kErrorOk; - - // Update the search region and statistics. - block->searchStart = Support::min(block->searchStart, areaIndex + areaNewSize); - block->searchEnd = Support::max(block->searchEnd, areaIndex + areaOldSize); - block->addFlags(JitAllocatorBlock::kFlagDirty); - block->decreaseUsedArea(areaDiff); - - // Unmark the released space and move the sentinel. - Support::bitVectorClear(block->usedBitVector, areaIndex + areaNewSize, areaDiff); - Support::bitVectorSetBit(block->stopBitVector, areaIndex + areaOldSize - 1, false); - Support::bitVectorSetBit(block->stopBitVector, areaIndex + areaNewSize - 1, true); - - // Fill released memory if the secure mode is enabled. - if (impl->options & kOptionFillUnusedMemory) - JitAllocatorImpl_fillPattern( - block->rwPtr() + (areaIndex + areaOldSize) * pool->granularity, - fillPattern(), - areaDiff * pool->granularity); - - return kErrorOk; -} - -// ============================================================================ -// [asmjit::JitAllocator - Unit] -// ============================================================================ - -#if defined(ASMJIT_TEST) -// A pseudo random number generator based on a paper by Sebastiano Vigna: -// http://vigna.di.unimi.it/ftp/papers/xorshiftplus.pdf -class Random { -public: - // Constants suggested as `23/18/5`. - enum Steps : uint32_t { - kStep1_SHL = 23, - kStep2_SHR = 18, - kStep3_SHR = 5 - }; - - inline explicit Random(uint64_t seed = 0) noexcept { reset(seed); } - inline Random(const Random& other) noexcept = default; - - inline void reset(uint64_t seed = 0) noexcept { - // The number is arbitrary, it means nothing. - constexpr uint64_t kZeroSeed = 0x1F0A2BE71D163FA0u; - - // Generate the state data by using splitmix64. - for (uint32_t i = 0; i < 2; i++) { - seed += 0x9E3779B97F4A7C15u; - uint64_t x = seed; - x = (x ^ (x >> 30)) * 0xBF58476D1CE4E5B9u; - x = (x ^ (x >> 27)) * 0x94D049BB133111EBu; - x = (x ^ (x >> 31)); - _state[i] = x != 0 ? x : kZeroSeed; - } - } - - inline uint32_t nextUInt32() noexcept { - return uint32_t(nextUInt64() >> 32); - } - - inline uint64_t nextUInt64() noexcept { - uint64_t x = _state[0]; - uint64_t y = _state[1]; - - x ^= x << kStep1_SHL; - y ^= y >> kStep3_SHR; - x ^= x >> kStep2_SHR; - x ^= y; - - _state[0] = y; - _state[1] = x; - return x + y; - } - - uint64_t _state[2]; -}; - -// Helper class to verify that JitAllocator doesn't return addresses that overlap. -class JitAllocatorWrapper { -public: - explicit inline JitAllocatorWrapper(const JitAllocator::CreateParams* params) noexcept - : _zone(1024 * 1024), - _heap(&_zone), - _allocator(params) {} - - // Address to a memory region of a given size. - class Range { - public: - inline Range(uint8_t* addr, size_t size) noexcept - : addr(addr), - size(size) {} - uint8_t* addr; - size_t size; - }; - - // Based on JitAllocator::Block, serves our purpose well... - class Record : public ZoneTreeNodeT, - public Range { - public: - inline Record(uint8_t* addr, size_t size) - : ZoneTreeNodeT(), - Range(addr, size) {} - - inline bool operator<(const Record& other) const noexcept { return addr < other.addr; } - inline bool operator>(const Record& other) const noexcept { return addr > other.addr; } - - inline bool operator<(const uint8_t* key) const noexcept { return addr + size <= key; } - inline bool operator>(const uint8_t* key) const noexcept { return addr > key; } - }; - - void _insert(void* p_, size_t size) noexcept { - uint8_t* p = static_cast(p_); - uint8_t* pEnd = p + size - 1; - - Record* record; - - record = _records.get(p); - if (record) - EXPECT(record == nullptr, - "Address [%p:%p] collides with a newly allocated [%p:%p]\n", record->addr, record->addr + record->size, p, p + size); - - record = _records.get(pEnd); - if (record) - EXPECT(record == nullptr, - "Address [%p:%p] collides with a newly allocated [%p:%p]\n", record->addr, record->addr + record->size, p, p + size); - - record = _heap.newT(p, size); - EXPECT(record != nullptr, - "Out of memory, cannot allocate 'Record'"); - - _records.insert(record); - } - - void _remove(void* p) noexcept { - Record* record = _records.get(static_cast(p)); - EXPECT(record != nullptr, - "Address [%p] doesn't exist\n", p); - - _records.remove(record); - _heap.release(record, sizeof(Record)); - } - - void* alloc(size_t size) noexcept { - void* roPtr; - void* rwPtr; - - Error err = _allocator.alloc(&roPtr, &rwPtr, size); - EXPECT(err == kErrorOk, - "JitAllocator failed to allocate '%u' bytes\n", unsigned(size)); - - _insert(roPtr, size); - return roPtr; - } - - void release(void* p) noexcept { - _remove(p); - EXPECT(_allocator.release(p) == kErrorOk, - "JitAllocator failed to release '%p'\n", p); - } - - Zone _zone; - ZoneAllocator _heap; - ZoneTree _records; - JitAllocator _allocator; -}; - -static void JitAllocatorTest_shuffle(void** ptrArray, size_t count, Random& prng) noexcept { - for (size_t i = 0; i < count; ++i) - std::swap(ptrArray[i], ptrArray[size_t(prng.nextUInt32() % count)]); -} - -static void JitAllocatorTest_usage(JitAllocator& allocator) noexcept { - JitAllocator::Statistics stats = allocator.statistics(); - INFO(" Block Count : %9llu [Blocks]" , (unsigned long long)(stats.blockCount())); - INFO(" Reserved (VirtMem): %9llu [Bytes]" , (unsigned long long)(stats.reservedSize())); - INFO(" Used (VirtMem): %9llu [Bytes] (%.1f%%)", (unsigned long long)(stats.usedSize()), stats.usedSizeAsPercent()); - INFO(" Overhead (HeapMem): %9llu [Bytes] (%.1f%%)", (unsigned long long)(stats.overheadSize()), stats.overheadSizeAsPercent()); -} - -UNIT(asmjit_jit_allocator) { - size_t kCount = BrokenAPI::hasArg("--quick") ? 1000 : 100000; - - struct TestParams { - const char* name; - uint32_t options; - uint32_t blockSize; - uint32_t granularity; - }; - - #define OPT(OPTION) JitAllocator::OPTION - static TestParams testParams[] = { - { "Default", 0, 0, 0 }, - { "16MB blocks", 0, 16 * 1024 * 1024, 0 }, - { "256B granularity", 0, 0, 256 }, - { "kOptionUseDualMapping", OPT(kOptionUseDualMapping), 0, 0 }, - { "kOptionUseMultiplePools", OPT(kOptionUseMultiplePools), 0, 0 }, - { "kOptionFillUnusedMemory", OPT(kOptionFillUnusedMemory), 0, 0 }, - { "kOptionImmediateRelease", OPT(kOptionImmediateRelease), 0, 0 }, - { "kOptionUseDualMapping | kOptionFillUnusedMemory", OPT(kOptionUseDualMapping) | OPT(kOptionFillUnusedMemory), 0, 0 } - }; - #undef OPT - - INFO("BitFlipIterator"); - { - static const uint32_t bits[] = { 0x80000000u, 0x80000000u, 0x00000000u, 0x80000000u }; - BitFlipIterator it(bits, ASMJIT_ARRAY_SIZE(bits)); - - EXPECT(it.hasNext()); - EXPECT(it.nextAndFlip() == 31); - EXPECT(it.hasNext()); - EXPECT(it.nextAndFlip() == 32); - EXPECT(it.hasNext()); - EXPECT(it.nextAndFlip() == 63); - EXPECT(it.hasNext()); - EXPECT(it.nextAndFlip() == 64); - EXPECT(it.hasNext()); - EXPECT(it.nextAndFlip() == 127); - EXPECT(!it.hasNext()); - } - - INFO("BitFlipIterator"); - { - static const uint64_t bits[] = { 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFF, 0, 0 }; - BitFlipIterator it(bits, ASMJIT_ARRAY_SIZE(bits)); - - EXPECT(it.hasNext()); - EXPECT(it.nextAndFlip() == 0); - EXPECT(it.hasNext()); - EXPECT(it.nextAndFlip() == 128); - EXPECT(!it.hasNext()); - } - - for (uint32_t testId = 0; testId < ASMJIT_ARRAY_SIZE(testParams); testId++) { - INFO("Testing JitAllocator: %s", testParams[testId].name); - - JitAllocator::CreateParams params {}; - params.options = testParams[testId].options; - params.blockSize = testParams[testId].blockSize; - params.granularity = testParams[testId].granularity; - - JitAllocatorWrapper wrapper(¶ms); - Random prng(100); - - size_t i; - - INFO(" Memory alloc/release test - %d allocations", kCount); - - void** ptrArray = (void**)::malloc(sizeof(void*) * size_t(kCount)); - EXPECT(ptrArray != nullptr, - "Couldn't allocate '%u' bytes for pointer-array", unsigned(sizeof(void*) * size_t(kCount))); - - INFO(" Allocating virtual memory..."); - for (i = 0; i < kCount; i++) - ptrArray[i] = wrapper.alloc((prng.nextUInt32() % 1024) + 8); - JitAllocatorTest_usage(wrapper._allocator); - - INFO(" Releasing virtual memory..."); - for (i = 0; i < kCount; i++) - wrapper.release(ptrArray[i]); - JitAllocatorTest_usage(wrapper._allocator); - - INFO(" Allocating virtual memory...", kCount); - for (i = 0; i < kCount; i++) - ptrArray[i] = wrapper.alloc((prng.nextUInt32() % 1024) + 8); - JitAllocatorTest_usage(wrapper._allocator); - - INFO(" Shuffling..."); - JitAllocatorTest_shuffle(ptrArray, unsigned(kCount), prng); - - INFO(" Releasing 50%% blocks..."); - for (i = 0; i < kCount / 2; i++) - wrapper.release(ptrArray[i]); - JitAllocatorTest_usage(wrapper._allocator); - - INFO(" Allocating 50%% blocks again..."); - for (i = 0; i < kCount / 2; i++) - ptrArray[i] = wrapper.alloc((prng.nextUInt32() % 1024) + 8); - JitAllocatorTest_usage(wrapper._allocator); - - INFO(" Releasing virtual memory..."); - for (i = 0; i < kCount; i++) - wrapper.release(ptrArray[i]); - JitAllocatorTest_usage(wrapper._allocator); - - ::free(ptrArray); - } -} -#endif - -ASMJIT_END_NAMESPACE - -#endif diff --git a/libraries/asmjit/asmjit/core/jitallocator.h b/libraries/asmjit/asmjit/core/jitallocator.h deleted file mode 100644 index e5531f82c44..00000000000 --- a/libraries/asmjit/asmjit/core/jitallocator.h +++ /dev/null @@ -1,261 +0,0 @@ -// [AsmJit] -// Machine Code Generation for C++. -// -// [License] -// Zlib - See LICENSE.md file in the package. - -#ifndef _ASMJIT_CORE_JITALLOCATOR_H -#define _ASMJIT_CORE_JITALLOCATOR_H - -#include "../core/build.h" -#ifndef ASMJIT_NO_JIT - -#include "../core/globals.h" -#include "../core/virtmem.h" - -ASMJIT_BEGIN_NAMESPACE - -//! \addtogroup asmjit_jit -//! \{ - -// ============================================================================ -// [asmjit::JitAllocator] -// ============================================================================ - -//! A simple implementation of memory manager that uses `asmjit::VirtMem` -//! functions to manage virtual memory for JIT compiled code. -//! -//! Implementation notes: -//! -//! - Granularity of allocated blocks is different than granularity for a typical -//! C malloc. In addition, the allocator can use several memory pools having a -//! different granularity to minimize the maintenance overhead. Multiple pools -//! feature requires `kFlagUseMultiplePools` flag to be set. -//! -//! - The allocator doesn't store any information in executable memory, instead, -//! the implementation uses two bit-vectors to manage allocated memory of each -//! allocator-block. The first bit-vector called 'used' is used to track used -//! memory (where each bit represents memory size defined by granularity) and -//! the second bit vector called 'stop' is used as a sentinel to mark where -//! the allocated area ends. -//! -//! - Internally, the allocator also uses RB tree to keep track of all blocks -//! across all pools. Each inserted block is added to the tree so it can be -//! matched fast during `release()` and `shrink()`. -class JitAllocator { -public: - ASMJIT_NONCOPYABLE(JitAllocator) - - struct Impl { - //! Allocator options, see \ref JitAllocator::Options. - uint32_t options; - //! Base block size (0 if the allocator is not initialized). - uint32_t blockSize; - //! Base granularity (0 if the allocator is not initialized). - uint32_t granularity; - //! A pattern that is used to fill unused memory if secure mode is enabled. - uint32_t fillPattern; - }; - - //! Allocator implementation (private). - Impl* _impl; - - enum Options : uint32_t { - //! Enables the use of an anonymous memory-mapped memory that is mapped into - //! two buffers having a different pointer. The first buffer has read and - //! execute permissions and the second buffer has read+write permissions. - //! - //! See \ref VirtMem::allocDualMapping() for more details about this feature. - kOptionUseDualMapping = 0x00000001u, - - //! Enables the use of multiple pools with increasing granularity instead of - //! a single pool. This flag would enable 3 internal pools in total having - //! 64, 128, and 256 bytes granularity. - //! - //! This feature is only recommended for users that generate a lot of code - //! and would like to minimize the overhead of `JitAllocator` itself by - //! having blocks of different allocation granularities. Using this feature - //! only for few allocations won't pay off as the allocator may need to - //! create more blocks initially before it can take the advantage of - //! variable block granularity. - kOptionUseMultiplePools = 0x00000002u, - - //! Always fill reserved memory by a fill-pattern. - //! - //! Causes a new block to be cleared by the fill pattern and freshly - //! released memory to be cleared before making it ready for another use. - kOptionFillUnusedMemory = 0x00000004u, - - //! When this flag is set the allocator would immediately release unused - //! blocks during `release()` or `reset()`. When this flag is not set the - //! allocator would keep one empty block in each pool to prevent excessive - //! virtual memory allocations and deallocations in border cases, which - //! involve constantly allocating and deallocating a single block caused - //! by repetitive calling `alloc()` and `release()` when the allocator has - //! either no blocks or have all blocks fully occupied. - kOptionImmediateRelease = 0x00000008u, - - //! Use a custom fill pattern, must be combined with `kFlagFillUnusedMemory`. - kOptionCustomFillPattern = 0x10000000u - }; - - //! \name Construction & Destruction - //! \{ - - //! Parameters that can be passed to `JitAllocator` constructor. - //! - //! Use it like this: - //! - //! ``` - //! // Zero initialize (zero means the default value) and change what you need. - //! JitAllocator::CreateParams params {}; - //! params.blockSize = 1024 * 1024; - //! - //! // Create the allocator. - //! JitAllocator allocator(¶ms); - //! ``` - struct CreateParams { - // Reset the content of `CreateParams`. - inline void reset() noexcept { memset(this, 0, sizeof(*this)); } - - //! Allocator options, see \ref JitAllocator::Options. - //! - //! No options are used by default. - uint32_t options; - - //! Base size of a single block in bytes (default 64kB). - //! - //! \remarks Block size must be equal or greater to page size and must be - //! power of 2. If the input is not valid then the default block size will - //! be used instead. - uint32_t blockSize; - - //! Base granularity (and also natural alignment) of allocations in bytes - //! (default 64). - //! - //! Since the `JitAllocator` uses bit-arrays to mark used memory the - //! granularity also specifies how many bytes correspond to a single bit in - //! such bit-array. Higher granularity means more waste of virtual memory - //! (as it increases the natural alignment), but smaller bit-arrays as less - //! bits would be required per a single block. - uint32_t granularity; - - //! Patter to use to fill unused memory. - //! - //! Only used if \ref kOptionCustomFillPattern is set. - uint32_t fillPattern; - }; - - //! Creates a `JitAllocator` instance. - explicit ASMJIT_API JitAllocator(const CreateParams* params = nullptr) noexcept; - //! Destroys the `JitAllocator` instance and release all blocks held. - ASMJIT_API ~JitAllocator() noexcept; - - inline bool isInitialized() const noexcept { return _impl->blockSize == 0; } - - //! Free all allocated memory - makes all pointers returned by `alloc()` invalid. - //! - //! \remarks This function is not thread-safe as it's designed to be used when - //! nobody else is using allocator. The reason is that there is no point of - //1 calling `reset()` when the allocator is still in use. - ASMJIT_API void reset(uint32_t resetPolicy = Globals::kResetSoft) noexcept; - - //! \} - - //! \name Accessors - //! \{ - - //! Returns allocator options, see `Flags`. - inline uint32_t options() const noexcept { return _impl->options; } - //! Tests whether the allocator has the given `option` set. - inline bool hasOption(uint32_t option) const noexcept { return (_impl->options & option) != 0; } - - //! Returns a base block size (a minimum size of block that the allocator would allocate). - inline uint32_t blockSize() const noexcept { return _impl->blockSize; } - //! Returns granularity of the allocator. - inline uint32_t granularity() const noexcept { return _impl->granularity; } - //! Returns pattern that is used to fill unused memory if `kFlagUseFillPattern` is set. - inline uint32_t fillPattern() const noexcept { return _impl->fillPattern; } - - //! \} - - //! \name Alloc & Release - //! \{ - - //! Allocate `size` bytes of virtual memory. - //! - //! \remarks This function is thread-safe. - ASMJIT_API Error alloc(void** roPtrOut, void** rwPtrOut, size_t size) noexcept; - - //! Release a memory returned by `alloc()`. - //! - //! \remarks This function is thread-safe. - ASMJIT_API Error release(void* ro) noexcept; - - //! Free extra memory allocated with `p` by restricting it to `newSize` size. - //! - //! \remarks This function is thread-safe. - ASMJIT_API Error shrink(void* ro, size_t newSize) noexcept; - - //! \} - - //! \name Statistics - //! \{ - - //! Statistics about `JitAllocator`. - struct Statistics { - inline void reset() noexcept { - _blockCount = 0; - _usedSize = 0; - _reservedSize = 0; - _overheadSize = 0; - } - - //! Returns count of blocks managed by `JitAllocator` at the moment. - inline size_t blockCount() const noexcept { return _blockCount; } - - //! Returns how many bytes are currently used. - inline size_t usedSize() const noexcept { return _usedSize; } - //! Returns the number of bytes unused by the allocator at the moment. - inline size_t unusedSize() const noexcept { return _reservedSize - _usedSize; } - //! Returns the total number of bytes bytes reserved by the allocator (sum of sizes of all blocks). - inline size_t reservedSize() const noexcept { return _reservedSize; } - //! Returns the number of bytes the allocator needs to manage the allocated memory. - inline size_t overheadSize() const noexcept { return _overheadSize; } - - inline double usedSizeAsPercent() const noexcept { - return (double(usedSize()) / (double(reservedSize()) + 1e-16)) * 100.0; - } - - inline double unusedSizeAsPercent() const noexcept { - return (double(unusedSize()) / (double(reservedSize()) + 1e-16)) * 100.0; - } - - inline double overheadSizeAsPercent() const noexcept { - return (double(overheadSize()) / (double(reservedSize()) + 1e-16)) * 100.0; - } - - //! Number of blocks `JitAllocator` maintains. - size_t _blockCount; - //! How many bytes are currently used / allocated. - size_t _usedSize; - //! How many bytes are currently reserved by the allocator. - size_t _reservedSize; - //! Allocation overhead (in bytes) required to maintain all blocks. - size_t _overheadSize; - }; - - //! Returns JIT allocator statistics. - //! - //! \remarks This function is thread-safe. - ASMJIT_API Statistics statistics() const noexcept; - - //! \} -}; - -//! \} - -ASMJIT_END_NAMESPACE - -#endif -#endif diff --git a/libraries/asmjit/asmjit/core/jitruntime.cpp b/libraries/asmjit/asmjit/core/jitruntime.cpp deleted file mode 100644 index f8d23c9aa99..00000000000 --- a/libraries/asmjit/asmjit/core/jitruntime.cpp +++ /dev/null @@ -1,142 +0,0 @@ -// [AsmJit] -// Machine Code Generation for C++. -// -// [License] -// Zlib - See LICENSE.md file in the package. - -#define ASMJIT_EXPORTS - -#include "../core/build.h" -#ifndef ASMJIT_NO_JIT - -#include "../core/cpuinfo.h" -#include "../core/jitruntime.h" - -ASMJIT_BEGIN_NAMESPACE - -// ============================================================================ -// [asmjit::JitRuntime - Utilities] -// ============================================================================ - -// Only useful on non-x86 architectures. -static inline void JitRuntime_flushInstructionCache(const void* p, size_t size) noexcept { -#if defined(_WIN32) && !ASMJIT_ARCH_X86 - // Windows has a built-in support in `kernel32.dll`. - ::FlushInstructionCache(::GetCurrentProcess(), p, size); -#else - ASMJIT_UNUSED(p); - ASMJIT_UNUSED(size); -#endif -} - -// X86 Target -// ---------- -// -// - 32-bit - Linux, OSX, BSD, and apparently also Haiku guarantee 16-byte -// stack alignment. Other operating systems are assumed to have -// 4-byte alignment by default for safety reasons. -// - 64-bit - stack must be aligned to 16 bytes. -// -// ARM Target -// ---------- -// -// - 32-bit - Stack must be aligned to 8 bytes. -// - 64-bit - Stack must be aligned to 16 bytes (hardware requirement). -static inline uint32_t JitRuntime_detectNaturalStackAlignment() noexcept { -#if ASMJIT_ARCH_BITS == 64 || \ - defined(__APPLE__ ) || \ - defined(__DragonFly__) || \ - defined(__HAIKU__ ) || \ - defined(__FreeBSD__ ) || \ - defined(__NetBSD__ ) || \ - defined(__OpenBSD__ ) || \ - defined(__bsdi__ ) || \ - defined(__linux__ ) - return 16; -#elif ASMJIT_ARCH_ARM - return 8; -#else - return uint32_t(sizeof(uintptr_t)); -#endif -} - -// ============================================================================ -// [asmjit::JitRuntime - Construction / Destruction] -// ============================================================================ - -JitRuntime::JitRuntime(const JitAllocator::CreateParams* params) noexcept - : _allocator(params) { - - // Setup target properties. - _targetType = kTargetJit; - _codeInfo._archInfo = CpuInfo::host().archInfo(); - _codeInfo._stackAlignment = uint8_t(JitRuntime_detectNaturalStackAlignment()); - _codeInfo._cdeclCallConv = CallConv::kIdHostCDecl; - _codeInfo._stdCallConv = CallConv::kIdHostStdCall; - _codeInfo._fastCallConv = CallConv::kIdHostFastCall; -} -JitRuntime::~JitRuntime() noexcept {} - -// ============================================================================ -// [asmjit::JitRuntime - Interface] -// ============================================================================ - -Error JitRuntime::_add(void** dst, CodeHolder* code) noexcept { - *dst = nullptr; - - ASMJIT_PROPAGATE(code->flatten()); - ASMJIT_PROPAGATE(code->resolveUnresolvedLinks()); - - size_t estimatedCodeSize = code->codeSize(); - if (ASMJIT_UNLIKELY(estimatedCodeSize == 0)) - return DebugUtils::errored(kErrorNoCodeGenerated); - - uint8_t* ro; - uint8_t* rw; - ASMJIT_PROPAGATE(_allocator.alloc((void**)&ro, (void**)&rw, estimatedCodeSize)); - - // Relocate the code. - Error err = code->relocateToBase(uintptr_t((void*)ro)); - if (ASMJIT_UNLIKELY(err)) { - _allocator.release(ro); - return err; - } - - // Recalculate the final code size and shrink the memory we allocated for it - // in case that some relocations didn't require records in an address table. - size_t codeSize = code->codeSize(); - - for (Section* section : code->_sections) { - size_t offset = size_t(section->offset()); - size_t bufferSize = size_t(section->bufferSize()); - size_t virtualSize = size_t(section->virtualSize()); - - ASMJIT_ASSERT(offset + bufferSize <= codeSize); - memcpy(rw + offset, section->data(), bufferSize); - - if (virtualSize > bufferSize) { - ASMJIT_ASSERT(offset + virtualSize <= codeSize); - memset(rw + offset + bufferSize, 0, virtualSize - bufferSize); - } - } - - if (codeSize < estimatedCodeSize) - _allocator.shrink(ro, codeSize); - - flush(ro, codeSize); - *dst = ro; - - return kErrorOk; -} - -Error JitRuntime::_release(void* p) noexcept { - return _allocator.release(p); -} - -void JitRuntime::flush(const void* p, size_t size) noexcept { - JitRuntime_flushInstructionCache(p, size); -} - -ASMJIT_END_NAMESPACE - -#endif diff --git a/libraries/asmjit/asmjit/core/jitruntime.h b/libraries/asmjit/asmjit/core/jitruntime.h deleted file mode 100644 index 242d1bba333..00000000000 --- a/libraries/asmjit/asmjit/core/jitruntime.h +++ /dev/null @@ -1,109 +0,0 @@ -// [AsmJit] -// Machine Code Generation for C++. -// -// [License] -// Zlib - See LICENSE.md file in the package. - -#ifndef _ASMJIT_CORE_JITRUNTIME_H -#define _ASMJIT_CORE_JITRUNTIME_H - -#include "../core/build.h" -#ifndef ASMJIT_NO_JIT - -#include "../core/codeholder.h" -#include "../core/jitallocator.h" -#include "../core/target.h" - -ASMJIT_BEGIN_NAMESPACE - -class CodeHolder; - -//! \addtogroup asmjit_jit -//! \{ - -// ============================================================================ -// [asmjit::JitRuntime] -// ============================================================================ - -//! JIT execution runtime is a special `Target` that is designed to store and -//! execute the generated code. -class ASMJIT_VIRTAPI JitRuntime : public Target { -public: - ASMJIT_NONCOPYABLE(JitRuntime) - - //! Virtual memory allocator. - JitAllocator _allocator; - - //! \name Construction & Destruction - //! \{ - - //! Creates a `JitRuntime` instance. - explicit ASMJIT_API JitRuntime(const JitAllocator::CreateParams* params = nullptr) noexcept; - //! Destroys the `JitRuntime` instance. - ASMJIT_API virtual ~JitRuntime() noexcept; - - inline void reset(uint32_t resetPolicy = Globals::kResetSoft) noexcept { - _allocator.reset(resetPolicy); - } - - //! \} - - //! \name Accessors - //! \{ - - //! Returns the associated `JitAllocator`. - inline JitAllocator* allocator() const noexcept { return const_cast(&_allocator); } - - //! \} - - //! \name Utilities - //! \{ - - // NOTE: To allow passing function pointers to `add()` and `release()` the - // virtual methods are prefixed with `_` and called from templates instead. - - //! Allocates memory needed for a code stored in the `CodeHolder` and relocates - //! the code to the pointer allocated. - //! - //! The beginning of the memory allocated for the function is returned in `dst`. - //! If failed `Error` code is returned and `dst` is explicitly set to `nullptr` - //! (this means that you don't have to set it to null before calling `add()`). - template - inline Error add(Func* dst, CodeHolder* code) noexcept { - return _add(Support::ptr_cast_impl(dst), code); - } - - //! Releases `p` which was obtained by calling `add()`. - template - inline Error release(Func p) noexcept { - return _release(Support::ptr_cast_impl(p)); - } - - //! Type-unsafe version of `add()`. - ASMJIT_API virtual Error _add(void** dst, CodeHolder* code) noexcept; - - //! Type-unsafe version of `release()`. - ASMJIT_API virtual Error _release(void* p) noexcept; - - //! Flushes 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's cache. - //! - //! Flushing is basically a NOP under X86, but is needed by architectures - //! that do not have a transparent instruction cache like ARM. - //! - //! 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(const void* p, size_t size) noexcept; - - //! \} -}; - -//! \} - -ASMJIT_END_NAMESPACE - -#endif -#endif diff --git a/libraries/asmjit/asmjit/core/logging.cpp b/libraries/asmjit/asmjit/core/logging.cpp deleted file mode 100644 index 6039a47712c..00000000000 --- a/libraries/asmjit/asmjit/core/logging.cpp +++ /dev/null @@ -1,515 +0,0 @@ -// [AsmJit] -// Machine Code Generation for C++. -// -// [License] -// Zlib - See LICENSE.md file in the package. - -#define ASMJIT_EXPORTS - -#include "../core/build.h" -#ifndef ASMJIT_NO_LOGGING - -#include "../core/builder.h" -#include "../core/codeholder.h" -#include "../core/compiler.h" -#include "../core/emitter.h" -#include "../core/logging.h" -#include "../core/string.h" -#include "../core/support.h" -#include "../core/type.h" - -#ifdef ASMJIT_BUILD_X86 - #include "../x86/x86logging_p.h" -#endif - -#ifdef ASMJIT_BUILD_ARM - #include "../arm/armlogging_p.h" -#endif - -ASMJIT_BEGIN_NAMESPACE - -#if defined(ASMJIT_NO_COMPILER) -class VirtReg; -#endif - -// ============================================================================ -// [asmjit::Logger - Construction / Destruction] -// ============================================================================ - -Logger::Logger() noexcept - : _options() {} -Logger::~Logger() noexcept {} - -// ============================================================================ -// [asmjit::Logger - Logging] -// ============================================================================ - -Error Logger::logf(const char* fmt, ...) noexcept { - Error err; - va_list ap; - - va_start(ap, fmt); - err = logv(fmt, ap); - va_end(ap); - - return err; -} - -Error Logger::logv(const char* fmt, va_list ap) noexcept { - StringTmp<2048> sb; - ASMJIT_PROPAGATE(sb.appendVFormat(fmt, ap)); - return log(sb); -} - -Error Logger::logBinary(const void* data, size_t size) noexcept { - static const char prefix[] = "db "; - - StringTmp<256> sb; - sb.appendString(prefix, ASMJIT_ARRAY_SIZE(prefix) - 1); - - size_t i = size; - const uint8_t* s = static_cast(data); - - while (i) { - uint32_t n = uint32_t(Support::min(i, 16)); - sb.truncate(ASMJIT_ARRAY_SIZE(prefix) - 1); - sb.appendHex(s, n); - sb.appendChar('\n'); - ASMJIT_PROPAGATE(log(sb)); - s += n; - i -= n; - } - - return kErrorOk; -} - -// ============================================================================ -// [asmjit::FileLogger - Construction / Destruction] -// ============================================================================ - -FileLogger::FileLogger(FILE* file) noexcept - : _file(nullptr) { setFile(file); } -FileLogger::~FileLogger() noexcept {} - -// ============================================================================ -// [asmjit::FileLogger - Logging] -// ============================================================================ - -Error FileLogger::_log(const char* data, size_t size) noexcept { - if (!_file) - return kErrorOk; - - if (size == SIZE_MAX) - size = strlen(data); - - fwrite(data, 1, size, _file); - return kErrorOk; -} - -// ============================================================================ -// [asmjit::StringLogger - Construction / Destruction] -// ============================================================================ - -StringLogger::StringLogger() noexcept {} -StringLogger::~StringLogger() noexcept {} - -// ============================================================================ -// [asmjit::StringLogger - Logging] -// ============================================================================ - -Error StringLogger::_log(const char* data, size_t size) noexcept { - return _content.appendString(data, size); -} - -// ============================================================================ -// [asmjit::Logging] -// ============================================================================ - -Error Logging::formatLabel( - String& sb, - uint32_t flags, - const BaseEmitter* emitter, - uint32_t labelId) noexcept { - - ASMJIT_UNUSED(flags); - - const LabelEntry* le = emitter->code()->labelEntry(labelId); - if (ASMJIT_UNLIKELY(!le)) - return sb.appendFormat("InvalidLabel[Id=%u]", labelId); - - if (le->hasName()) { - if (le->hasParent()) { - uint32_t parentId = le->parentId(); - const LabelEntry* pe = emitter->code()->labelEntry(parentId); - - if (ASMJIT_UNLIKELY(!pe)) - ASMJIT_PROPAGATE(sb.appendFormat("InvalidLabel[Id=%u]", labelId)); - else if (ASMJIT_UNLIKELY(!pe->hasName())) - ASMJIT_PROPAGATE(sb.appendFormat("L%u", parentId)); - else - ASMJIT_PROPAGATE(sb.appendString(pe->name())); - - ASMJIT_PROPAGATE(sb.appendChar('.')); - } - return sb.appendString(le->name()); - } - else { - return sb.appendFormat("L%u", labelId); - } -} - -Error Logging::formatRegister( - String& sb, - uint32_t flags, - const BaseEmitter* emitter, - uint32_t archId, - uint32_t regType, - uint32_t regId) noexcept { - - #ifdef ASMJIT_BUILD_X86 - if (ArchInfo::isX86Family(archId)) - return x86::LoggingInternal::formatRegister(sb, flags, emitter, archId, regType, regId); - #endif - - #ifdef ASMJIT_BUILD_ARM - if (ArchInfo::isArmFamily(archId)) - return arm::LoggingInternal::formatRegister(sb, flags, emitter, archId, regType, regId); - #endif - - return kErrorInvalidArch; -} - -Error Logging::formatOperand( - String& sb, - uint32_t flags, - const BaseEmitter* emitter, - uint32_t archId, - const Operand_& op) noexcept { - - #ifdef ASMJIT_BUILD_X86 - if (ArchInfo::isX86Family(archId)) - return x86::LoggingInternal::formatOperand(sb, flags, emitter, archId, op); - #endif - - #ifdef ASMJIT_BUILD_ARM - if (ArchInfo::isArmFamily(archId)) - return arm::LoggingInternal::formatOperand(sb, flags, emitter, archId, op); - #endif - - return kErrorInvalidArch; -} - -Error Logging::formatInstruction( - String& sb, - uint32_t flags, - const BaseEmitter* emitter, - uint32_t archId, - const BaseInst& inst, const Operand_* operands, uint32_t opCount) noexcept { - - #ifdef ASMJIT_BUILD_X86 - if (ArchInfo::isX86Family(archId)) - return x86::LoggingInternal::formatInstruction(sb, flags, emitter, archId, inst, operands, opCount); - #endif - - #ifdef ASMJIT_BUILD_ARM - if (ArchInfo::isArmFamily(archId)) - return arm::LoggingInternal::formatInstruction(sb, flags, emitter, archId, inst, operands, opCount); - #endif - - return kErrorInvalidArch; -} - -Error Logging::formatTypeId(String& sb, uint32_t typeId) noexcept { - if (typeId == Type::kIdVoid) - return sb.appendString("void"); - - if (!Type::isValid(typeId)) - return sb.appendString("unknown"); - - const char* typeName = "unknown"; - uint32_t typeSize = Type::sizeOf(typeId); - - uint32_t baseId = Type::baseOf(typeId); - switch (baseId) { - case Type::kIdIntPtr : typeName = "iptr" ; break; - case Type::kIdUIntPtr: typeName = "uptr" ; break; - case Type::kIdI8 : typeName = "i8" ; break; - case Type::kIdU8 : typeName = "u8" ; break; - case Type::kIdI16 : typeName = "i16" ; break; - case Type::kIdU16 : typeName = "u16" ; break; - case Type::kIdI32 : typeName = "i32" ; break; - case Type::kIdU32 : typeName = "u32" ; break; - case Type::kIdI64 : typeName = "i64" ; break; - case Type::kIdU64 : typeName = "u64" ; break; - case Type::kIdF32 : typeName = "f32" ; break; - case Type::kIdF64 : typeName = "f64" ; break; - case Type::kIdF80 : typeName = "f80" ; break; - case Type::kIdMask8 : typeName = "mask8" ; break; - case Type::kIdMask16 : typeName = "mask16"; break; - case Type::kIdMask32 : typeName = "mask32"; break; - case Type::kIdMask64 : typeName = "mask64"; break; - case Type::kIdMmx32 : typeName = "mmx32" ; break; - case Type::kIdMmx64 : typeName = "mmx64" ; break; - } - - uint32_t baseSize = Type::sizeOf(baseId); - if (typeSize > baseSize) { - uint32_t count = typeSize / baseSize; - return sb.appendFormat("%sx%u", typeName, unsigned(count)); - } - else { - return sb.appendString(typeName); - } - -} - -#ifndef ASMJIT_NO_BUILDER -static Error formatFuncValue(String& sb, uint32_t flags, const BaseEmitter* emitter, FuncValue value) noexcept { - uint32_t typeId = value.typeId(); - ASMJIT_PROPAGATE(Logging::formatTypeId(sb, typeId)); - - if (value.isReg()) { - ASMJIT_PROPAGATE(sb.appendChar('@')); - ASMJIT_PROPAGATE(Logging::formatRegister(sb, flags, emitter, emitter->archId(), value.regType(), value.regId())); - } - - if (value.isStack()) { - ASMJIT_PROPAGATE(sb.appendFormat("@[%d]", int(value.stackOffset()))); - } - - return kErrorOk; -} - -static Error formatFuncRets( - String& sb, - uint32_t flags, - const BaseEmitter* emitter, - const FuncDetail& fd, - VirtReg* const* vRegs) noexcept { - - if (!fd.hasRet()) - return sb.appendString("void"); - - for (uint32_t i = 0; i < fd.retCount(); i++) { - if (i) ASMJIT_PROPAGATE(sb.appendString(", ")); - ASMJIT_PROPAGATE(formatFuncValue(sb, flags, emitter, fd.ret(i))); - - #ifndef ASMJIT_NO_COMPILER - if (vRegs) { - static const char nullRet[] = ""; - ASMJIT_PROPAGATE(sb.appendFormat(" %s", vRegs[i] ? vRegs[i]->name() : nullRet)); - } - #endif - } - - return kErrorOk; -} - -static Error formatFuncArgs( - String& sb, - uint32_t flags, - const BaseEmitter* emitter, - const FuncDetail& fd, - VirtReg* const* vRegs) noexcept { - - uint32_t count = fd.argCount(); - if (!count) - return sb.appendString("void"); - - for (uint32_t i = 0; i < count; i++) { - if (i) ASMJIT_PROPAGATE(sb.appendString(", ")); - ASMJIT_PROPAGATE(formatFuncValue(sb, flags, emitter, fd.arg(i))); - - #ifndef ASMJIT_NO_COMPILER - if (vRegs) { - static const char nullArg[] = ""; - ASMJIT_PROPAGATE(sb.appendFormat(" %s", vRegs[i] ? vRegs[i]->name() : nullArg)); - } - #endif - } - - return kErrorOk; -} - -Error Logging::formatNode( - String& sb, - uint32_t flags, - const BaseBuilder* cb, - const BaseNode* node_) noexcept { - - if (node_->hasPosition() && (flags & FormatOptions::kFlagPositions) != 0) - ASMJIT_PROPAGATE(sb.appendFormat("<%05u> ", node_->position())); - - switch (node_->type()) { - case BaseNode::kNodeInst: { - const InstNode* node = node_->as(); - ASMJIT_PROPAGATE( - Logging::formatInstruction(sb, flags, cb, - cb->archId(), - node->baseInst(), node->operands(), node->opCount())); - break; - } - - case BaseNode::kNodeSection: { - const SectionNode* node = node_->as(); - if (cb->_code->isSectionValid(node->id())) { - const Section* section = cb->_code->sectionById(node->id()); - ASMJIT_PROPAGATE(sb.appendFormat(".section %s", section->name())); - } - break; - } - - case BaseNode::kNodeLabel: { - const LabelNode* node = node_->as(); - ASMJIT_PROPAGATE(formatLabel(sb, flags, cb, node->id())); - ASMJIT_PROPAGATE(sb.appendString(":")); - break; - } - - case BaseNode::kNodeAlign: { - const AlignNode* node = node_->as(); - ASMJIT_PROPAGATE( - sb.appendFormat(".align %u (%s)", - node->alignment(), - node->alignMode() == kAlignCode ? "code" : "data")); - break; - } - - case BaseNode::kNodeEmbedData: { - const EmbedDataNode* node = node_->as(); - ASMJIT_PROPAGATE(sb.appendFormat(".embed (%u bytes)", node->size())); - break; - } - - case BaseNode::kNodeEmbedLabel: { - const EmbedLabelNode* node = node_->as(); - ASMJIT_PROPAGATE(sb.appendString(".label ")); - ASMJIT_PROPAGATE(formatLabel(sb, flags, cb, node->id())); - break; - } - - case BaseNode::kNodeEmbedLabelDelta: { - const EmbedLabelDeltaNode* node = node_->as(); - ASMJIT_PROPAGATE(sb.appendString(".label (")); - ASMJIT_PROPAGATE(formatLabel(sb, flags, cb, node->id())); - ASMJIT_PROPAGATE(sb.appendString(" - ")); - ASMJIT_PROPAGATE(formatLabel(sb, flags, cb, node->baseId())); - ASMJIT_PROPAGATE(sb.appendString(")")); - break; - } - - case BaseNode::kNodeComment: { - const CommentNode* node = node_->as(); - ASMJIT_PROPAGATE(sb.appendFormat("; %s", node->inlineComment())); - break; - } - - case BaseNode::kNodeSentinel: { - const SentinelNode* node = node_->as(); - const char* sentinelName = nullptr; - - switch (node->sentinelType()) { - case SentinelNode::kSentinelFuncEnd: - sentinelName = "[FuncEnd]"; - break; - - default: - sentinelName = "[Sentinel]"; - break; - } - - ASMJIT_PROPAGATE(sb.appendString(sentinelName)); - break; - } - - #ifndef ASMJIT_NO_COMPILER - case BaseNode::kNodeFunc: { - const FuncNode* node = node_->as(); - - ASMJIT_PROPAGATE(formatLabel(sb, flags, cb, node->id())); - ASMJIT_PROPAGATE(sb.appendString(": ")); - - ASMJIT_PROPAGATE(formatFuncRets(sb, flags, cb, node->detail(), nullptr)); - ASMJIT_PROPAGATE(sb.appendString(" Func(")); - ASMJIT_PROPAGATE(formatFuncArgs(sb, flags, cb, node->detail(), node->args())); - ASMJIT_PROPAGATE(sb.appendString(")")); - break; - } - - case BaseNode::kNodeFuncRet: { - const FuncRetNode* node = node_->as(); - ASMJIT_PROPAGATE(sb.appendString("[FuncRet]")); - - for (uint32_t i = 0; i < 2; i++) { - const Operand_& op = node->_opArray[i]; - if (!op.isNone()) { - ASMJIT_PROPAGATE(sb.appendString(i == 0 ? " " : ", ")); - ASMJIT_PROPAGATE(formatOperand(sb, flags, cb, cb->archId(), op)); - } - } - break; - } - - case BaseNode::kNodeFuncCall: { - const FuncCallNode* node = node_->as(); - ASMJIT_PROPAGATE( - Logging::formatInstruction(sb, flags, cb, - cb->archId(), - node->baseInst(), node->operands(), node->opCount())); - break; - } - #endif - - default: { - ASMJIT_PROPAGATE(sb.appendFormat("[User:%u]", node_->type())); - break; - } - } - - return kErrorOk; -} -#endif - -Error Logging::formatLine(String& sb, const uint8_t* binData, size_t binSize, size_t dispSize, size_t immSize, const char* comment) noexcept { - size_t currentSize = sb.size(); - size_t commentSize = comment ? Support::strLen(comment, Globals::kMaxCommentSize) : 0; - - ASMJIT_ASSERT(binSize >= dispSize); - const size_t kNoBinSize = std::numeric_limits::max(); - - if ((binSize != 0 && binSize != kNoBinSize) || commentSize) { - size_t align = kMaxInstLineSize; - char sep = ';'; - - for (size_t i = (binSize == kNoBinSize); i < 2; i++) { - size_t begin = sb.size(); - ASMJIT_PROPAGATE(sb.padEnd(align)); - - if (sep) { - ASMJIT_PROPAGATE(sb.appendChar(sep)); - ASMJIT_PROPAGATE(sb.appendChar(' ')); - } - - // Append binary data or comment. - if (i == 0) { - ASMJIT_PROPAGATE(sb.appendHex(binData, binSize - dispSize - immSize)); - ASMJIT_PROPAGATE(sb.appendChars('.', dispSize * 2)); - ASMJIT_PROPAGATE(sb.appendHex(binData + binSize - immSize, immSize)); - if (commentSize == 0) break; - } - else { - ASMJIT_PROPAGATE(sb.appendString(comment, commentSize)); - } - - currentSize += sb.size() - begin; - align += kMaxBinarySize; - sep = '|'; - } - } - - return sb.appendChar('\n'); -} - -ASMJIT_END_NAMESPACE - -#endif diff --git a/libraries/asmjit/asmjit/core/logging.h b/libraries/asmjit/asmjit/core/logging.h deleted file mode 100644 index 16e8b97943a..00000000000 --- a/libraries/asmjit/asmjit/core/logging.h +++ /dev/null @@ -1,338 +0,0 @@ -// [AsmJit] -// Machine Code Generation for C++. -// -// [License] -// Zlib - See LICENSE.md file in the package. - -#ifndef _ASMJIT_CORE_LOGGING_H -#define _ASMJIT_CORE_LOGGING_H - -#include "../core/inst.h" -#include "../core/string.h" - -ASMJIT_BEGIN_NAMESPACE - -//! \addtogroup asmjit_core -//! \{ - -#ifndef ASMJIT_NO_LOGGING - -// ============================================================================ -// [Forward Declarations] -// ============================================================================ - -class BaseEmitter; -class BaseReg; -class Logger; -struct Operand_; - -#ifndef ASMJIT_NO_BUILDER -class BaseBuilder; -class BaseNode; -#endif - -// ============================================================================ -// [asmjit::FormatOptions] -// ============================================================================ - -class FormatOptions { -public: - uint32_t _flags; - uint8_t _indentation[4]; - - enum Flags : uint32_t { - //!< Show also binary form of each logged instruction (assembler). - kFlagMachineCode = 0x00000001u, - //!< Show a text explanation of some immediate values. - kFlagExplainImms = 0x00000002u, - //!< Use hexadecimal notation of immediate values. - kFlagHexImms = 0x00000004u, - //!< Use hexadecimal notation of address offsets. - kFlagHexOffsets = 0x00000008u, - //!< Show casts between virtual register types (compiler). - kFlagRegCasts = 0x00000010u, - //!< Show positions associated with nodes (compiler). - kFlagPositions = 0x00000020u, - //!< Annotate nodes that are lowered by passes. - kFlagAnnotations = 0x00000040u, - - // TODO: These must go, keep this only for formatting. - //!< Show an additional output from passes. - kFlagDebugPasses = 0x00000080u, - //!< Show an additional output from RA. - kFlagDebugRA = 0x00000100u - }; - - enum IndentationType : uint32_t { - //! Indentation used for instructions and directives. - kIndentationCode = 0u, - //! Indentation used for labels and function nodes. - kIndentationLabel = 1u, - //! Indentation used for comments (not inline comments). - kIndentationComment = 2u, - kIndentationReserved = 3u - }; - - //! \name Construction & Destruction - //! \{ - - constexpr FormatOptions() noexcept - : _flags(0), - _indentation { 0, 0, 0, 0 } {} - - constexpr FormatOptions(const FormatOptions& other) noexcept = default; - inline FormatOptions& operator=(const FormatOptions& other) noexcept = default; - - inline void reset() noexcept { - _flags = 0; - _indentation[0] = 0; - _indentation[1] = 0; - _indentation[2] = 0; - _indentation[3] = 0; - } - - //! \} - - //! \name Accessors - //! \{ - - constexpr uint32_t flags() const noexcept { return _flags; } - constexpr bool hasFlag(uint32_t flag) const noexcept { return (_flags & flag) != 0; } - inline void setFlags(uint32_t flags) noexcept { _flags = flags; } - inline void addFlags(uint32_t flags) noexcept { _flags |= flags; } - inline void clearFlags(uint32_t flags) noexcept { _flags &= ~flags; } - - constexpr uint8_t indentation(uint32_t type) const noexcept { return _indentation[type]; } - inline void setIndentation(uint32_t type, uint32_t n) noexcept { _indentation[type] = uint8_t(n); } - inline void resetIndentation(uint32_t type) noexcept { _indentation[type] = uint8_t(0); } - - //! \} -}; - -// ============================================================================ -// [asmjit::Logger] -// ============================================================================ - -//! Abstract logging interface and helpers. -//! -//! 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. -//! -//! There are two `Logger` implementations offered by AsmJit: -//! - `FileLogger` - allows to log into `FILE*`. -//! - `StringLogger` - logs into a `String`. -class ASMJIT_VIRTAPI Logger { -public: - ASMJIT_BASE_CLASS(Logger) - ASMJIT_NONCOPYABLE(Logger) - - //! Format options. - FormatOptions _options; - - //! \name Construction & Destruction - //! \{ - - //! Creates a `Logger` instance. - ASMJIT_API Logger() noexcept; - //! Destroys the `Logger` instance. - ASMJIT_API virtual ~Logger() noexcept; - - //! \} - - //! \name Format Options - //! \{ - - inline FormatOptions& options() noexcept { return _options; } - inline const FormatOptions& options() const noexcept { return _options; } - - inline uint32_t flags() const noexcept { return _options.flags(); } - inline bool hasFlag(uint32_t flag) const noexcept { return _options.hasFlag(flag); } - inline void setFlags(uint32_t flags) noexcept { _options.setFlags(flags); } - inline void addFlags(uint32_t flags) noexcept { _options.addFlags(flags); } - inline void clearFlags(uint32_t flags) noexcept { _options.clearFlags(flags); } - - inline uint32_t indentation(uint32_t type) const noexcept { return _options.indentation(type); } - inline void setIndentation(uint32_t type, uint32_t n) noexcept { _options.setIndentation(type, n); } - inline void resetIndentation(uint32_t type) noexcept { _options.resetIndentation(type); } - - //! \} - - //! \name Logging Interface - //! \{ - - //! Logs `str` - must be reimplemented. - virtual Error _log(const char* data, size_t size) noexcept = 0; - - //! Logs string `str`, which is either null terminated or having size `size`. - inline Error log(const char* data, size_t size = SIZE_MAX) noexcept { return _log(data, size); } - //! Logs content of a string `str`. - inline Error log(const String& str) noexcept { return _log(str.data(), str.size()); } - - //! Formats the message by using `snprintf()` and then sends the result - //! to `log()`. - ASMJIT_API Error logf(const char* fmt, ...) noexcept; - - //! Formats the message by using `vsnprintf()` and then sends the result - //! to `log()`. - ASMJIT_API Error logv(const char* fmt, va_list ap) noexcept; - - //! Logs binary data. - ASMJIT_API Error logBinary(const void* data, size_t size) noexcept; - - //! \} -}; - -// ============================================================================ -// [asmjit::FileLogger] -// ============================================================================ - -//! Logger that can log to a `FILE*`. -class ASMJIT_VIRTAPI FileLogger : public Logger { -public: - ASMJIT_NONCOPYABLE(FileLogger) - - FILE* _file; - - //! \name Construction & Destruction - //! \{ - - //! Creates a new `FileLogger` that logs to `FILE*`. - ASMJIT_API FileLogger(FILE* file = nullptr) noexcept; - //! Destroys the `FileLogger`. - ASMJIT_API virtual ~FileLogger() noexcept; - - //! \} - - //! \name Accessors - //! \{ - - //! Returns the logging output stream or null if the logger has no output - //! stream. - inline FILE* file() const noexcept { return _file; } - - //! Sets the logging output stream to `stream` or null. - //! - //! \note If the `file` is null the logging will be disabled. When a logger - //! is attached to `CodeHolder` or any emitter the logging API will always - //! be called regardless of the output file. This means that if you really - //! want to disable logging at emitter level you must not attach a logger - //! to it. - inline void setFile(FILE* file) noexcept { _file = file; } - - //! \} - - ASMJIT_API Error _log(const char* data, size_t size = SIZE_MAX) noexcept override; -}; - -// ============================================================================ -// [asmjit::StringLogger] -// ============================================================================ - -//! Logger that stores everything in an internal string buffer. -class ASMJIT_VIRTAPI StringLogger : public Logger { -public: - ASMJIT_NONCOPYABLE(StringLogger) - - //! Logger data as string. - String _content; - - //! \name Construction & Destruction - //! \{ - - //! Create new `StringLogger`. - ASMJIT_API StringLogger() noexcept; - //! Destroys the `StringLogger`. - ASMJIT_API virtual ~StringLogger() noexcept; - - //! \} - - //! \name Logger Data Accessors - //! \{ - - //! Returns aggregated logger data as `char*` pointer. - //! - //! The pointer is owned by `StringLogger`, it can't be modified or freed. - inline const char* data() const noexcept { return _content.data(); } - //! Returns size of the data returned by `data()`. - inline size_t dataSize() const noexcept { return _content.size(); } - - //! \} - - //! \name Logger Data Manipulation - //! \{ - - //! Clears the accumulated logger data. - inline void clear() noexcept { _content.clear(); } - - //! \} - - ASMJIT_API Error _log(const char* data, size_t size = SIZE_MAX) noexcept override; -}; - -// ============================================================================ -// [asmjit::Logging] -// ============================================================================ - -struct Logging { - ASMJIT_API static Error formatRegister( - String& sb, - uint32_t flags, - const BaseEmitter* emitter, - uint32_t archId, - uint32_t regType, - uint32_t regId) noexcept; - - ASMJIT_API static Error formatLabel( - String& sb, - uint32_t flags, - const BaseEmitter* emitter, - uint32_t labelId) noexcept; - - ASMJIT_API static Error formatOperand( - String& sb, - uint32_t flags, - const BaseEmitter* emitter, - uint32_t archId, - const Operand_& op) noexcept; - - ASMJIT_API static Error formatInstruction( - String& sb, - uint32_t flags, - const BaseEmitter* emitter, - uint32_t archId, - const BaseInst& inst, const Operand_* operands, uint32_t opCount) noexcept; - - ASMJIT_API static Error formatTypeId( - String& sb, - uint32_t typeId) noexcept; - - #ifndef ASMJIT_NO_BUILDER - ASMJIT_API static Error formatNode( - String& sb, - uint32_t flags, - const BaseBuilder* cb, - const BaseNode* node_) noexcept; - #endif - - // Only used by AsmJit internals, not available to users. - #if defined(ASMJIT_EXPORTS) - enum { - // Has to be big to be able to hold all metadata compiler can assign to a - // single instruction. - kMaxInstLineSize = 44, - kMaxBinarySize = 26 - }; - - static Error formatLine( - String& sb, - const uint8_t* binData, size_t binSize, size_t dispSize, size_t immSize, const char* comment) noexcept; - #endif -}; -#endif - -//! \} - -ASMJIT_END_NAMESPACE - -#endif // _ASMJIT_CORE_LOGGER_H diff --git a/libraries/asmjit/asmjit/core/misc_p.h b/libraries/asmjit/asmjit/core/misc_p.h deleted file mode 100644 index f8548f141b7..00000000000 --- a/libraries/asmjit/asmjit/core/misc_p.h +++ /dev/null @@ -1,32 +0,0 @@ -// [AsmJit] -// Machine Code Generation for C++. -// -// [License] -// Zlib - See LICENSE.md file in the package. - -#ifndef _ASMJIT_CORE_MISC_P_H -#define _ASMJIT_CORE_MISC_P_H - -#include "../core/build.h" - -ASMJIT_BEGIN_NAMESPACE - -//! \cond INTERNAL -//! \addtogroup asmjit_support -//! \{ - -#define ASMJIT_LOOKUP_TABLE_8(T, I) T((I)), T((I+1)), T((I+2)), T((I+3)), T((I+4)), T((I+5)), T((I+6)), T((I+7)) -#define ASMJIT_LOOKUP_TABLE_16(T, I) ASMJIT_LOOKUP_TABLE_8(T, I), ASMJIT_LOOKUP_TABLE_8(T, I + 8) -#define ASMJIT_LOOKUP_TABLE_32(T, I) ASMJIT_LOOKUP_TABLE_16(T, I), ASMJIT_LOOKUP_TABLE_16(T, I + 16) -#define ASMJIT_LOOKUP_TABLE_64(T, I) ASMJIT_LOOKUP_TABLE_32(T, I), ASMJIT_LOOKUP_TABLE_32(T, I + 32) -#define ASMJIT_LOOKUP_TABLE_128(T, I) ASMJIT_LOOKUP_TABLE_64(T, I), ASMJIT_LOOKUP_TABLE_64(T, I + 64) -#define ASMJIT_LOOKUP_TABLE_256(T, I) ASMJIT_LOOKUP_TABLE_128(T, I), ASMJIT_LOOKUP_TABLE_128(T, I + 128) -#define ASMJIT_LOOKUP_TABLE_512(T, I) ASMJIT_LOOKUP_TABLE_256(T, I), ASMJIT_LOOKUP_TABLE_256(T, I + 256) -#define ASMJIT_LOOKUP_TABLE_1024(T, I) ASMJIT_LOOKUP_TABLE_512(T, I), ASMJIT_LOOKUP_TABLE_512(T, I + 512) - -//! \} -//! \endcond - -ASMJIT_END_NAMESPACE - -#endif // _ASMJIT_CORE_MISC_P_H diff --git a/libraries/asmjit/asmjit/core/operand.cpp b/libraries/asmjit/asmjit/core/operand.cpp deleted file mode 100644 index 6156d6f455b..00000000000 --- a/libraries/asmjit/asmjit/core/operand.cpp +++ /dev/null @@ -1,113 +0,0 @@ -// [AsmJit] -// Machine Code Generation for C++. -// -// [License] -// Zlib - See LICENSE.md file in the package. - -#define ASMJIT_EXPORTS - -#include "../core/operand.h" - -ASMJIT_BEGIN_NAMESPACE - -// ============================================================================ -// [asmjit::Operand - Unit] -// ============================================================================ - -#if defined(ASMJIT_TEST) -UNIT(asmjit_core_operand) { - INFO("Checking operand sizes"); - EXPECT(sizeof(Operand) == 16); - EXPECT(sizeof(BaseReg) == 16); - EXPECT(sizeof(BaseMem) == 16); - EXPECT(sizeof(Imm) == 16); - EXPECT(sizeof(Label) == 16); - - INFO("Checking basic functionality of Operand"); - Operand a, b; - Operand dummy; - - EXPECT(a.isNone() == true); - EXPECT(a.isReg() == false); - EXPECT(a.isMem() == false); - EXPECT(a.isImm() == false); - EXPECT(a.isLabel() == false); - EXPECT(a == b); - EXPECT(a._data64 == 0); - - INFO("Checking basic functionality of Label"); - Label label; - EXPECT(label.isValid() == false); - EXPECT(label.id() == Globals::kInvalidId); - - INFO("Checking basic functionality of BaseReg"); - EXPECT(BaseReg().isReg() == true); - EXPECT(BaseReg().isValid() == false); - EXPECT(BaseReg()._data64 == 0); - EXPECT(dummy.as().isValid() == false); - - // Create some register (not specific to any architecture). - uint32_t rSig = Operand::kOpReg | (1 << Operand::kSignatureRegTypeShift ) | - (2 << Operand::kSignatureRegGroupShift) | - (8 << Operand::kSignatureSizeShift ) ; - BaseReg r1(rSig, 5); - - EXPECT(r1.isValid() == true); - EXPECT(r1.isReg() == true); - EXPECT(r1.isReg(1) == true); - EXPECT(r1.isPhysReg() == true); - EXPECT(r1.isVirtReg() == false); - EXPECT(r1.signature() == rSig); - EXPECT(r1.type() == 1); - EXPECT(r1.group() == 2); - EXPECT(r1.size() == 8); - EXPECT(r1.id() == 5); - EXPECT(r1.isReg(1, 5) == true); // RegType and Id. - EXPECT(r1._data64 == 0); - - // The same type of register having different id. - BaseReg r2(r1, 6); - EXPECT(r2.isValid() == true); - EXPECT(r2.isReg() == true); - EXPECT(r2.isReg(1) == true); - EXPECT(r2.isPhysReg() == true); - EXPECT(r2.isVirtReg() == false); - EXPECT(r2.signature() == rSig); - EXPECT(r2.type() == r1.type()); - EXPECT(r2.group() == r1.group()); - EXPECT(r2.size() == r1.size()); - EXPECT(r2.id() == 6); - EXPECT(r2.isReg(1, 6) == true); - - r1.reset(); - EXPECT(!r1.isReg()); - EXPECT(!r1.isValid()); - - INFO("Checking basic functionality of BaseMem"); - BaseMem m; - EXPECT(m.isMem()); - EXPECT(m == BaseMem()); - EXPECT(m.hasBase() == false); - EXPECT(m.hasIndex() == false); - EXPECT(m.hasOffset() == false); - EXPECT(m.isOffset64Bit() == true); - EXPECT(m.offset() == 0); - - m.setOffset(-1); - EXPECT(m.offsetLo32() == -1); - EXPECT(m.offset() == -1); - - int64_t x = int64_t(0xFF00FF0000000001u); - int32_t xHi = int32_t(0xFF00FF00u); - - m.setOffset(x); - EXPECT(m.offset() == x); - EXPECT(m.offsetLo32() == 1); - EXPECT(m.offsetHi32() == xHi); - - INFO("Checking basic functionality of Imm"); - EXPECT(Imm(-1).i64() == int64_t(-1)); -} -#endif - -ASMJIT_END_NAMESPACE diff --git a/libraries/asmjit/asmjit/core/operand.h b/libraries/asmjit/asmjit/core/operand.h deleted file mode 100644 index 27610dca883..00000000000 --- a/libraries/asmjit/asmjit/core/operand.h +++ /dev/null @@ -1,1316 +0,0 @@ -// [AsmJit] -// Machine Code Generation for C++. -// -// [License] -// Zlib - See LICENSE.md file in the package. - -#ifndef _ASMJIT_CORE_OPERAND_H -#define _ASMJIT_CORE_OPERAND_H - -#include "../core/support.h" - -ASMJIT_BEGIN_NAMESPACE - -// ============================================================================ -// [Macros] -// ============================================================================ - -//! Adds a template specialization for `REG_TYPE` into the local `RegTraits`. -#define ASMJIT_DEFINE_REG_TRAITS(REG, REG_TYPE, GROUP, SIZE, COUNT, TYPE_ID) \ -template<> \ -struct RegTraits { \ - typedef REG RegT; \ - \ - static constexpr uint32_t kValid = 1; \ - static constexpr uint32_t kCount = COUNT; \ - static constexpr uint32_t kTypeId = TYPE_ID; \ - \ - static constexpr uint32_t kType = REG_TYPE; \ - static constexpr uint32_t kGroup = GROUP; \ - static constexpr uint32_t kSize = SIZE; \ - \ - static constexpr uint32_t kSignature = \ - (Operand::kOpReg << Operand::kSignatureOpShift ) | \ - (kType << Operand::kSignatureRegTypeShift ) | \ - (kGroup << Operand::kSignatureRegGroupShift) | \ - (kSize << Operand::kSignatureSizeShift ) ; \ -} - -//! Adds constructors and member functions to a class that implements abstract -//! register. Abstract register is register that doesn't have type or signature -//! yet, it's a base class like `x86::Reg` or `arm::Reg`. -#define ASMJIT_DEFINE_ABSTRACT_REG(REG, BASE) \ -public: \ - /*! Default constructor that only setups basics. */ \ - constexpr REG() noexcept \ - : BASE(kSignature, kIdBad) {} \ - \ - /*! Makes a copy of the `other` register operand. */ \ - constexpr REG(const REG& other) noexcept \ - : BASE(other) {} \ - \ - /*! Makes a copy of the `other` register having id set to `rId` */ \ - constexpr REG(const BaseReg& other, uint32_t rId) noexcept \ - : BASE(other, rId) {} \ - \ - /*! Creates a register based on `signature` and `rId`. */ \ - constexpr REG(uint32_t signature, uint32_t rId) noexcept \ - : BASE(signature, rId) {} \ - \ - /*! Creates a completely uninitialized REG register operand (garbage). */ \ - inline explicit REG(Globals::NoInit_) noexcept \ - : BASE(Globals::NoInit) {} \ - \ - /*! Creates a new register from register type and id. */ \ - static inline REG fromTypeAndId(uint32_t rType, uint32_t rId) noexcept { \ - return REG(signatureOf(rType), rId); \ - } \ - \ - /*! Clones the register operand. */ \ - constexpr REG clone() const noexcept { return REG(*this); } \ - \ - inline REG& operator=(const REG& other) noexcept = default; - -//! Adds constructors and member functions to a class that implements final -//! register. Final registers MUST HAVE a valid signature. -#define ASMJIT_DEFINE_FINAL_REG(REG, BASE, TRAITS) \ -public: \ - static constexpr uint32_t kThisType = TRAITS::kType; \ - static constexpr uint32_t kThisGroup = TRAITS::kGroup; \ - static constexpr uint32_t kThisSize = TRAITS::kSize; \ - static constexpr uint32_t kSignature = TRAITS::kSignature; \ - \ - ASMJIT_DEFINE_ABSTRACT_REG(REG, BASE) \ - \ - /*! Creates a register operand having its id set to `rId`. */ \ - constexpr explicit REG(uint32_t rId) noexcept \ - : BASE(kSignature, rId) {} - -//! \addtogroup asmjit_core -//! \{ - -// ============================================================================ -// [asmjit::Operand_] -// ============================================================================ - -//! Constructor-less `Operand`. -//! -//! Contains no initialization code and can be used safely to define an array -//! of operands that won't be initialized. This is an `Operand` compatible -//! data structure designed to be statically initialized, static const, or to -//! be used by the user to define an array of operands without having them -//! default initialized. -//! -//! The key difference between `Operand` and `Operand_`: -//! -//! ``` -//! Operand_ xArray[10]; // Not initialized, contains garbage. -//! Operand yArray[10]; // All operands initialized to none. -//! ``` -struct Operand_ { - //! Operand's signature that provides operand type and additional information. - uint32_t _signature; - //! Either base id as used by memory operand or any id as used by others. - uint32_t _baseId; - - //! Memory operand data. - struct MemData { - //! Index register id. - uint32_t indexId; - //! Low part of 64-bit offset (or 32-bit offset). - uint32_t offsetLo32; - }; - - //! Additional data used by some operands. - union { - //! 32-bit data (used either by immediate or as a 32-bit view). - uint32_t _data32[2]; - //! 64-bit data (used either by immediate or as a 64-bit view). - uint64_t _data64; - - //! Memory address data. - MemData _mem; - }; - - //! Operand types that can be encoded in `Operand`. - enum OpType : uint32_t { - //! Not an operand or not initialized. - kOpNone = 0, - //! Operand is a register. - kOpReg = 1, - //! Operand is a memory. - kOpMem = 2, - //! Operand is an immediate value. - kOpImm = 3, - //! Operand is a label. - kOpLabel = 4 - }; - static_assert(kOpMem == kOpReg + 1, "asmjit::Operand requires `kOpMem` to be `kOpReg+1`."); - - // \cond INTERNAL - enum SignatureBits : uint32_t { - // Operand type (3 least significant bits). - // |........|........|........|.....XXX| - kSignatureOpShift = 0, - kSignatureOpMask = 0x07u << kSignatureOpShift, - - // Register type (5 bits). - // |........|........|........|XXXXX...| - kSignatureRegTypeShift = 3, - kSignatureRegTypeMask = 0x1Fu << kSignatureRegTypeShift, - - // Register group (4 bits). - // |........|........|....XXXX|........| - kSignatureRegGroupShift = 8, - kSignatureRegGroupMask = 0x0Fu << kSignatureRegGroupShift, - - // Memory base type (5 bits). - // |........|........|........|XXXXX...| - kSignatureMemBaseTypeShift = 3, - kSignatureMemBaseTypeMask = 0x1Fu << kSignatureMemBaseTypeShift, - - // Memory index type (5 bits). - // |........|........|...XXXXX|........| - kSignatureMemIndexTypeShift = 8, - kSignatureMemIndexTypeMask = 0x1Fu << kSignatureMemIndexTypeShift, - - // Memory base+index combined (10 bits). - // |........|........|...XXXXX|XXXXX...| - kSignatureMemBaseIndexShift = 3, - kSignatureMemBaseIndexMask = 0x3FFu << kSignatureMemBaseIndexShift, - - // Memory address type (2 bits). - // |........|........|.XX.....|........| - kSignatureMemAddrTypeShift = 13, - kSignatureMemAddrTypeMask = 0x03u << kSignatureMemAddrTypeShift, - - // This memory operand represents a home-slot or stack (BaseCompiler). - // |........|........|X.......|........| - kSignatureMemRegHomeShift = 15, - kSignatureMemRegHomeFlag = 0x01u << kSignatureMemRegHomeShift, - - // Operand size (8 most significant bits). - // |XXXXXXXX|........|........|........| - kSignatureSizeShift = 24, - kSignatureSizeMask = 0xFFu << kSignatureSizeShift - }; - //! \endcond - - //! \cond INTERNAL - //! Constants useful for VirtId <-> Index translation. - enum VirtIdConstants : uint32_t { - //! Minimum valid packed-id. - kVirtIdMin = 256, - //! Maximum valid packed-id, excludes Globals::kInvalidId. - kVirtIdMax = Globals::kInvalidId - 1, - //! Count of valid packed-ids. - kVirtIdCount = uint32_t(kVirtIdMax - kVirtIdMin + 1) - }; - - //! Tests whether the given `id` is a valid virtual register id. Since AsmJit - //! supports both physical and virtual registers it must be able to distinguish - //! between these two. The idea is that physical registers are always limited - //! in size, so virtual identifiers start from `kVirtIdMin` and end at - //! `kVirtIdMax`. - static ASMJIT_INLINE bool isVirtId(uint32_t id) noexcept { return id - kVirtIdMin < uint32_t(kVirtIdCount); } - //! Converts a real-id into a packed-id that can be stored in Operand. - static ASMJIT_INLINE uint32_t indexToVirtId(uint32_t id) noexcept { return id + kVirtIdMin; } - //! Converts a packed-id back to real-id. - static ASMJIT_INLINE uint32_t virtIdToIndex(uint32_t id) noexcept { return id - kVirtIdMin; } - //! \endcond - - //! \name Construction & Destruction - //! \{ - - //! \cond INTERNAL - //! Initializes a `BaseReg` operand from `signature` and register `id`. - inline void _initReg(uint32_t signature, uint32_t id) noexcept { - _signature = signature; - _baseId = id; - _data64 = 0; - } - - //! Initializes the operand from `other` (used by operator overloads). - inline void copyFrom(const Operand_& other) noexcept { memcpy(this, &other, sizeof(Operand_)); } - //! \endcond - - //! Resets the `Operand` to none. - //! - //! None operand is defined the following way: - //! - Its signature is zero (kOpNone, and the rest zero as well). - //! - Its id is `0`. - //! - The reserved8_4 field is set to `0`. - //! - The reserved12_4 field is set to zero. - //! - //! In other words, reset operands have all members set to zero. Reset operand - //! must match the Operand state right after its construction. Alternatively, - //! if you have an array of operands, you can simply use `memset()`. - //! - //! ``` - //! using namespace asmjit; - //! - //! Operand a; - //! Operand b; - //! assert(a == b); - //! - //! b = x86::eax; - //! assert(a != b); - //! - //! b.reset(); - //! assert(a == b); - //! - //! memset(&b, 0, sizeof(Operand)); - //! assert(a == b); - //! ``` - inline void reset() noexcept { - _signature = 0; - _baseId = 0; - _data64 = 0; - } - - //! \} - - //! \name Operator Overloads - //! \{ - - constexpr bool operator==(const Operand_& other) const noexcept { return isEqual(other); } - constexpr bool operator!=(const Operand_& other) const noexcept { return !isEqual(other); } - - //! \} - - //! \name Cast - //! \{ - - //! Casts this operand to `T` type. - template - inline T& as() noexcept { return static_cast(*this); } - - //! Casts this operand to `T` type (const). - template - inline const T& as() const noexcept { return static_cast(*this); } - - //! \} - - //! \name Accessors - //! \{ - - //! Tests whether the operand matches the given signature `sign`. - constexpr bool hasSignature(uint32_t signature) const noexcept { return _signature == signature; } - //! Tests whether the operand matches the signature of the `other` operand. - constexpr bool hasSignature(const Operand_& other) const noexcept { return _signature == other.signature(); } - - //! Returns operand signature as unsigned 32-bit integer. - //! - //! Signature is first 4 bytes of the operand data. It's used mostly for - //! operand checking as it's much faster to check 4 bytes at once than having - //! to check these bytes individually. - constexpr uint32_t signature() const noexcept { return _signature; } - - //! Sets the operand signature, see `signature()`. - //! - //! \note Improper use of `setSignature()` can lead to hard-to-debug errors. - inline void setSignature(uint32_t signature) noexcept { _signature = signature; } - - //! \cond INTERNAL - template - constexpr bool _hasSignaturePart() const noexcept { - return (_signature & mask) != 0; - } - - template - constexpr uint32_t _getSignaturePart() const noexcept { - return (_signature >> Support::constCtz(mask)) & (mask >> Support::constCtz(mask)); - } - - template - inline void _setSignaturePart(uint32_t value) noexcept { - ASMJIT_ASSERT((value & ~(mask >> Support::constCtz(mask))) == 0); - _signature = (_signature & ~mask) | (value << Support::constCtz(mask)); - } - //! \endcond - - //! Returns the type of the operand, see `OpType`. - constexpr uint32_t opType() const noexcept { return _getSignaturePart(); } - //! Tests whether the operand is none (`kOpNone`). - constexpr bool isNone() const noexcept { return _signature == 0; } - //! Tests whether the operand is a register (`kOpReg`). - constexpr bool isReg() const noexcept { return opType() == kOpReg; } - //! Tests whether the operand is a memory location (`kOpMem`). - constexpr bool isMem() const noexcept { return opType() == kOpMem; } - //! Tests whether the operand is an immediate (`kOpImm`). - constexpr bool isImm() const noexcept { return opType() == kOpImm; } - //! Tests whether the operand is a label (`kOpLabel`). - constexpr bool isLabel() const noexcept { return opType() == kOpLabel; } - - //! Tests whether the operand is a physical register. - constexpr bool isPhysReg() const noexcept { return isReg() && _baseId < 0xFFu; } - //! Tests whether the operand is a virtual register. - constexpr bool isVirtReg() const noexcept { return isReg() && _baseId > 0xFFu; } - - //! Tests whether the operand specifies a size (i.e. the size is not zero). - constexpr bool hasSize() const noexcept { return _hasSignaturePart(); } - //! Tests whether the size of the operand matches `size`. - constexpr bool hasSize(uint32_t s) const noexcept { return size() == s; } - - //! Returns the size of the operand in bytes. - //! - //! The value returned depends on the operand type: - //! * None - Should always return zero size. - //! * Reg - Should always return the size of the register. If the register - //! size depends on architecture (like `x86::CReg` and `x86::DReg`) - //! the size returned should be the greatest possible (so it should - //! return 64-bit size in such case). - //! * Mem - Size is optional and will be in most cases zero. - //! * Imm - Should always return zero size. - //! * Label - Should always return zero size. - constexpr uint32_t size() const noexcept { return _getSignaturePart(); } - - //! Returns the operand id. - //! - //! The value returned should be interpreted accordingly to the operand type: - //! * None - Should be `0`. - //! * Reg - Physical or virtual register id. - //! * Mem - Multiple meanings - BASE address (register or label id), or - //! high value of a 64-bit absolute address. - //! * Imm - Should be `0`. - //! * Label - Label id if it was created by using `newLabel()` or - //! `Globals::kInvalidId` if the label is invalid or not - //! initialized. - constexpr uint32_t id() const noexcept { return _baseId; } - - //! Tests whether the operand is 100% equal to `other`. - constexpr bool isEqual(const Operand_& other) const noexcept { - return (_signature == other._signature) & - (_baseId == other._baseId ) & - (_data64 == other._data64 ) ; - } - - //! Tests whether the operand is a register matching `rType`. - constexpr bool isReg(uint32_t rType) const noexcept { - return (_signature & (kSignatureOpMask | kSignatureRegTypeMask)) == - ((kOpReg << kSignatureOpShift) | (rType << kSignatureRegTypeShift)); - } - - //! Tests whether the operand is register and of `rType` and `rId`. - constexpr bool isReg(uint32_t rType, uint32_t rId) const noexcept { - return isReg(rType) && id() == rId; - } - - //! Tests whether the operand is a register or memory. - constexpr bool isRegOrMem() const noexcept { - return Support::isBetween(opType(), kOpReg, kOpMem); - } - - //! \} -}; - -// ============================================================================ -// [asmjit::Operand] -// ============================================================================ - -//! Operand can contain register, memory location, immediate, or label. -class Operand : public Operand_ { -public: - //! \name Construction & Destruction - //! \{ - - //! Creates `kOpNone` operand having all members initialized to zero. - constexpr Operand() noexcept - : Operand_{ kOpNone, 0u, {{ 0u, 0u }}} {} - - //! Creates a cloned `other` operand. - constexpr Operand(const Operand& other) noexcept = default; - - //! Creates a cloned `other` operand. - constexpr explicit Operand(const Operand_& other) - : Operand_(other) {} - - //! Creates an operand initialized to raw `[u0, u1, u2, u3]` values. - constexpr Operand(Globals::Init_, uint32_t u0, uint32_t u1, uint32_t u2, uint32_t u3) noexcept - : Operand_{ u0, u1, {{ u2, u3 }}} {} - - //! Creates an uninitialized operand (dangerous). - inline explicit Operand(Globals::NoInit_) noexcept {} - - //! \} - - //! \name Operator Overloads - //! \{ - - inline Operand& operator=(const Operand& other) noexcept = default; - inline Operand& operator=(const Operand_& other) noexcept { return operator=(static_cast(other)); } - - //! \} - - //! \name Utilities - //! \{ - - //! Clones this operand and returns its copy. - constexpr Operand clone() const noexcept { return Operand(*this); } - - //! \} -}; - -static_assert(sizeof(Operand) == 16, "asmjit::Operand must be exactly 16 bytes long"); - -namespace Globals { - //! A default-constructed operand of `Operand_::kOpNone` type. - static constexpr const Operand none; -} - -// ============================================================================ -// [asmjit::Label] -// ============================================================================ - -//! Label (jump target or data location). -//! -//! Label represents a location in code typically used as a jump target, but -//! may be also a reference to some data or a static variable. Label has to be -//! explicitly created by BaseEmitter. -//! -//! Example of using labels: -//! -//! ``` -//! // Create some emitter (for example x86::Assembler). -//! x86::Assembler a; -//! -//! // Create Label instance. -//! Label L1 = a.newLabel(); -//! -//! // ... your code ... -//! -//! // Using label. -//! a.jump(L1); -//! -//! // ... your code ... -//! -//! // Bind label to the current position, see `BaseEmitter::bind()`. -//! a.bind(L1); -//! ``` -class Label : public Operand { -public: - //! Type of the Label. - enum LabelType : uint32_t { - //! Anonymous (unnamed) label. - kTypeAnonymous = 0, - //! Local label (always has parentId). - kTypeLocal = 1, - //! Global label (never has parentId). - kTypeGlobal = 2, - //! Number of label types. - kTypeCount = 3 - }; - - // TODO: Find a better place, find a better name. - enum { - //! Label tag is used as a sub-type, forming a unique signature across all - //! operand types as 0x1 is never associated with any register (reg-type). - //! This means that a memory operand's BASE register can be constructed - //! from virtually any operand (register vs. label) by just assigning its - //! type (reg type or label-tag) and operand id. - kLabelTag = 0x1 - }; - - //! \name Construction & Destruction - //! \{ - - //! Creates a label operand without ID (you must set the ID to make it valid). - constexpr Label() noexcept - : Operand(Globals::Init, kOpLabel, Globals::kInvalidId, 0, 0) {} - - //! Creates a cloned label operand of `other` . - constexpr Label(const Label& other) noexcept - : Operand(other) {} - - //! Creates a label operand of the given `id`. - constexpr explicit Label(uint32_t id) noexcept - : Operand(Globals::Init, kOpLabel, id, 0, 0) {} - - inline explicit Label(Globals::NoInit_) noexcept - : Operand(Globals::NoInit) {} - - //! Resets the label, will reset all properties and set its ID to `Globals::kInvalidId`. - inline void reset() noexcept { - _signature = kOpLabel; - _baseId = Globals::kInvalidId; - _data64 = 0; - } - - //! \} - - //! \name Overloaded Operators - //! \{ - - inline Label& operator=(const Label& other) noexcept = default; - - //! \} - - //! \name Accessors - //! \{ - - //! Tests whether the label was created by CodeHolder and/or an attached emitter. - constexpr bool isValid() const noexcept { return _baseId != Globals::kInvalidId; } - //! Sets the label `id`. - inline void setId(uint32_t id) noexcept { _baseId = id; } - - //! \} -}; - -// ============================================================================ -// [asmjit::BaseRegTraits] -// ============================================================================ - -//! \cond INTERNAL -//! Default register traits. -struct BaseRegTraits { - //! RegType is not valid by default. - static constexpr uint32_t kValid = 0; - //! Count of registers (0 if none). - static constexpr uint32_t kCount = 0; - //! Everything is void by default. - static constexpr uint32_t kTypeId = 0; - - //! Zero type by default. - static constexpr uint32_t kType = 0; - //! Zero group by default. - static constexpr uint32_t kGroup = 0; - //! No size by default. - static constexpr uint32_t kSize = 0; - - //! Empty signature by default. - static constexpr uint32_t kSignature = Operand::kOpReg; -}; -//! \endcond - -// ============================================================================ -// [asmjit::BaseReg] -// ============================================================================ - -//! Structure that allows to extract a register information based on the signature. -//! -//! This information is compatible with operand's signature (32-bit integer) -//! and `RegInfo` just provides easy way to access it. -struct RegInfo { - inline void reset() noexcept { _signature = 0; } - inline void setSignature(uint32_t signature) noexcept { _signature = signature; } - - template - constexpr uint32_t _getSignaturePart() const noexcept { - return (_signature >> Support::constCtz(mask)) & (mask >> Support::constCtz(mask)); - } - - constexpr bool isValid() const noexcept { return _signature != 0; } - constexpr uint32_t signature() const noexcept { return _signature; } - constexpr uint32_t opType() const noexcept { return _getSignaturePart(); } - constexpr uint32_t group() const noexcept { return _getSignaturePart(); } - constexpr uint32_t type() const noexcept { return _getSignaturePart(); } - constexpr uint32_t size() const noexcept { return _getSignaturePart(); } - - uint32_t _signature; -}; - -//! Physical/Virtual register operand. -class BaseReg : public Operand { -public: - //! Architecture neutral register types. - //! - //! These must be reused by any platform that contains that types. All GP - //! and VEC registers are also allowed by design to be part of a BASE|INDEX - //! of a memory operand. - enum RegType : uint32_t { - //! No register - unused, invalid, multiple meanings. - kTypeNone = 0, - - // (1 is used as a LabelTag) - - //! 8-bit low general purpose register (X86). - kTypeGp8Lo = 2, - //! 8-bit high general purpose register (X86). - kTypeGp8Hi = 3, - //! 16-bit general purpose register (X86). - kTypeGp16 = 4, - //! 32-bit general purpose register (X86|ARM). - kTypeGp32 = 5, - //! 64-bit general purpose register (X86|ARM). - kTypeGp64 = 6, - //! 32-bit view of a vector register (ARM). - kTypeVec32 = 7, - //! 64-bit view of a vector register (ARM). - kTypeVec64 = 8, - //! 128-bit view of a vector register (X86|ARM). - kTypeVec128 = 9, - //! 256-bit view of a vector register (X86). - kTypeVec256 = 10, - //! 512-bit view of a vector register (X86). - kTypeVec512 = 11, - //! 1024-bit view of a vector register (future). - kTypeVec1024 = 12, - //! Other0 register, should match `kOther0` group. - kTypeOther0 = 13, - //! Other1 register, should match `kOther1` group. - kTypeOther1 = 14, - //! Universal id of IP/PC register (if separate). - kTypeIP = 15, - //! Start of platform dependent register types (must be honored). - kTypeCustom = 16, - //! Maximum possible register id of all architectures. - kTypeMax = 31 - }; - - //! Register group (architecture neutral), and some limits. - enum RegGroup : uint32_t { - //! General purpose register group compatible with all backends. - kGroupGp = 0, - //! Vector register group compatible with all backends. - kGroupVec = 1, - //! Group that is architecture dependent. - kGroupOther0 = 2, - //! Group that is architecture dependent. - kGroupOther1 = 3, - //! Count of register groups used by virtual registers. - kGroupVirt = 4, - //! Count of register groups used by physical registers. - kGroupCount = 16 - }; - - enum Id : uint32_t { - //! None or any register (mostly internal). - kIdBad = 0xFFu - }; - - static constexpr uint32_t kSignature = kOpReg; - - //! \name Construction & Destruction - //! \{ - - //! Creates a dummy register operand. - constexpr BaseReg() noexcept - : Operand(Globals::Init, kSignature, kIdBad, 0, 0) {} - - //! Creates a new register operand which is the same as `other` . - constexpr BaseReg(const BaseReg& other) noexcept - : Operand(other) {} - - //! Creates a new register operand compatible with `other`, but with a different `rId`. - constexpr BaseReg(const BaseReg& other, uint32_t rId) noexcept - : Operand(Globals::Init, other._signature, rId, 0, 0) {} - - //! Creates a register initialized to `signature` and `rId`. - constexpr BaseReg(uint32_t signature, uint32_t rId) noexcept - : Operand(Globals::Init, signature, rId, 0, 0) {} - - inline explicit BaseReg(Globals::NoInit_) noexcept - : Operand(Globals::NoInit) {} - - //! \} - - //! \name Overloaded Operators - //! \{ - - inline BaseReg& operator=(const BaseReg& other) noexcept = default; - - //! \} - - //! \name Accessors - //! \{ - - //! Tests whether this register is the same as `other`. - //! - //! This is just an optimization. Registers by default only use the first - //! 8 bytes of the Operand, so this method takes advantage of this knowledge - //! and only compares these 8 bytes. If both operands were created correctly - //! then `isEqual()` and `isSame()` should give the same answer, however, if - //! some one of the two operand contains a garbage or other metadata in the - //! upper 8 bytes then `isSame()` may return `true` in cases where `isEqual()` - //! returns false. - constexpr bool isSame(const BaseReg& other) const noexcept { - return (_signature == other._signature) & - (_baseId == other._baseId ) ; - } - - //! Tests whether the register is valid (either virtual or physical). - constexpr bool isValid() const noexcept { return (_signature != 0) & (_baseId != kIdBad); } - - //! Tests whether this is a physical register. - constexpr bool isPhysReg() const noexcept { return _baseId < kIdBad; } - //! Tests whether this is a virtual register. - constexpr bool isVirtReg() const noexcept { return _baseId > kIdBad; } - - //! Tests whether the register type matches `type` - same as `isReg(type)`, provided for convenience. - constexpr bool isType(uint32_t type) const noexcept { return (_signature & kSignatureRegTypeMask) == (type << kSignatureRegTypeShift); } - //! Tests whether the register group matches `group`. - constexpr bool isGroup(uint32_t group) const noexcept { return (_signature & kSignatureRegGroupMask) == (group << kSignatureRegGroupShift); } - - //! Tests whether the register is a general purpose register (any size). - constexpr bool isGp() const noexcept { return isGroup(kGroupGp); } - //! Tests whether the register is a vector register. - constexpr bool isVec() const noexcept { return isGroup(kGroupVec); } - - using Operand_::isReg; - - //! Same as `isType()`, provided for convenience. - constexpr bool isReg(uint32_t rType) const noexcept { return isType(rType); } - //! Tests whether the register type matches `type` and register id matches `rId`. - constexpr bool isReg(uint32_t rType, uint32_t rId) const noexcept { return isType(rType) && id() == rId; } - - //! Returns the type of the register. - constexpr uint32_t type() const noexcept { return _getSignaturePart(); } - //! Returns the register group. - constexpr uint32_t group() const noexcept { return _getSignaturePart(); } - - //! Clones the register operand. - constexpr BaseReg clone() const noexcept { return BaseReg(*this); } - - //! Casts this register to `RegT` by also changing its signature. - //! - //! \note Improper use of `cloneAs()` can lead to hard-to-debug errors. - template - constexpr RegT cloneAs() const noexcept { return RegT(RegT::kSignature, id()); } - - //! Casts this register to `other` by also changing its signature. - //! - //! \note Improper use of `cloneAs()` can lead to hard-to-debug errors. - template - constexpr RegT cloneAs(const RegT& other) const noexcept { return RegT(other.signature(), id()); } - - //! Sets the register id to `rId`. - inline void setId(uint32_t rId) noexcept { _baseId = rId; } - - //! Sets a 32-bit operand signature based on traits of `RegT`. - template - inline void setSignatureT() noexcept { _signature = RegT::kSignature; } - - //! Sets the register `signature` and `rId`. - inline void setSignatureAndId(uint32_t signature, uint32_t rId) noexcept { - _signature = signature; - _baseId = rId; - } - - //! \} - - //! \name Static Functions - //! \{ - - static inline bool isGp(const Operand_& op) noexcept { - // Check operand type and register group. Not interested in register type and size. - const uint32_t kSgn = (kOpReg << kSignatureOpShift ) | - (kGroupGp << kSignatureRegGroupShift) ; - return (op.signature() & (kSignatureOpMask | kSignatureRegGroupMask)) == kSgn; - } - - //! Tests whether the `op` operand is either a low or high 8-bit GPB register. - static inline bool isVec(const Operand_& op) noexcept { - // Check operand type and register group. Not interested in register type and size. - const uint32_t kSgn = (kOpReg << kSignatureOpShift ) | - (kGroupVec << kSignatureRegGroupShift) ; - return (op.signature() & (kSignatureOpMask | kSignatureRegGroupMask)) == kSgn; - } - - static inline bool isGp(const Operand_& op, uint32_t rId) noexcept { return isGp(op) & (op.id() == rId); } - static inline bool isVec(const Operand_& op, uint32_t rId) noexcept { return isVec(op) & (op.id() == rId); } - - //! \} -}; - -// ============================================================================ -// [asmjit::RegOnly] -// ============================================================================ - -//! RegOnly is 8-byte version of `BaseReg` that allows to store either register -//! or nothing. -//! -//! This class was designed to decrease the space consumed by each extra "operand" -//! in `BaseEmitter` and `InstNode` classes. -struct RegOnly { - //! Type of the operand, either `kOpNone` or `kOpReg`. - uint32_t _signature; - //! Physical or virtual register id. - uint32_t _id; - - //! \name Construction & Destruction - //! \{ - - //! Initializes the `RegOnly` instance to hold register `signature` and `id`. - inline void init(uint32_t signature, uint32_t id) noexcept { - _signature = signature; - _id = id; - } - - inline void init(const BaseReg& reg) noexcept { init(reg.signature(), reg.id()); } - inline void init(const RegOnly& reg) noexcept { init(reg.signature(), reg.id()); } - - //! Resets the `RegOnly` members to zeros (none). - inline void reset() noexcept { init(0, 0); } - - //! \} - - //! \name Accessors - //! \{ - - //! Tests whether this ExtraReg is none (same as calling `Operand_::isNone()`). - constexpr bool isNone() const noexcept { return _signature == 0; } - //! Tests whether the register is valid (either virtual or physical). - constexpr bool isReg() const noexcept { return _signature != 0; } - - //! Tests whether this is a physical register. - constexpr bool isPhysReg() const noexcept { return _id < BaseReg::kIdBad; } - //! Tests whether this is a virtual register (used by `BaseCompiler`). - constexpr bool isVirtReg() const noexcept { return _id > BaseReg::kIdBad; } - - //! Returns the register signature or 0 if no register is assigned. - constexpr uint32_t signature() const noexcept { return _signature; } - //! Returns the register id. - //! - //! \note Always check whether the register is assigned before using the - //! returned identifier as non-assigned `RegOnly` instance would return - //! zero id, which is still a valid register id. - constexpr uint32_t id() const noexcept { return _id; } - - //! Sets the register id. - inline void setId(uint32_t id) noexcept { _id = id; } - - //! \cond INTERNAL - //! - //! Extracts information from operand's signature. - template - constexpr uint32_t _getSignaturePart() const noexcept { - return (_signature >> Support::constCtz(mask)) & (mask >> Support::constCtz(mask)); - } - //! \endcond - - //! Returns the type of the register. - constexpr uint32_t type() const noexcept { return _getSignaturePart(); } - //! Returns the register group. - constexpr uint32_t group() const noexcept { return _getSignaturePart(); } - - //! \} - - //! \name Utilities - //! \{ - - //! Converts this ExtraReg to a real `RegT` operand. - template - constexpr RegT toReg() const noexcept { return RegT(_signature, _id); } - - //! \} -}; - -// ============================================================================ -// [asmjit::BaseMem] -// ============================================================================ - -//! Base class for all memory operands. -//! -//! \note It's tricky to pack all possible cases that define a memory operand -//! into just 16 bytes. The `BaseMem` splits data into the following parts: -//! -//! BASE - Base register or label - requires 36 bits total. 4 bits are used to -//! encode the type of the BASE operand (label vs. register type) and -//! the remaining 32 bits define the BASE id, which can be a physical or -//! virtual register index. If BASE type is zero, which is never used as -//! a register-type and label doesn't use it as well then BASE field -//! contains a high DWORD of a possible 64-bit absolute address, which is -//! possible on X64. -//! -//! INDEX - Index register (or theoretically Label, which doesn't make sense). -//! Encoding is similar to BASE - it also requires 36 bits and splits -//! the encoding to INDEX type (4 bits defining the register type) and -//! id (32-bits). -//! -//! OFFSET - A relative offset of the address. Basically if BASE is specified -//! the relative displacement adjusts BASE and an optional INDEX. if -//! BASE is not specified then the OFFSET should be considered as ABSOLUTE -//! address (at least on X86). In that case its low 32 bits are stored in -//! DISPLACEMENT field and the remaining high 32 bits are stored in BASE. -//! -//! OTHER - There is rest 8 bits that can be used for whatever purpose. The -//! x86::Mem operand uses these bits to store segment override prefix and -//! index shift (scale). -class BaseMem : public Operand { -public: - enum AddrType : uint32_t { - kAddrTypeDefault = 0, - kAddrTypeAbs = 1, - kAddrTypeRel = 2 - }; - - // Shortcuts. - enum SignatureMem : uint32_t { - kSignatureMemAbs = kAddrTypeAbs << kSignatureMemAddrTypeShift, - kSignatureMemRel = kAddrTypeRel << kSignatureMemAddrTypeShift - }; - - //! \cond INTERNAL - //! Used internally to construct `BaseMem` operand from decomposed data. - struct Decomposed { - uint32_t baseType; - uint32_t baseId; - uint32_t indexType; - uint32_t indexId; - int32_t offset; - uint32_t size; - uint32_t flags; - }; - //! \endcond - - //! \name Construction & Destruction - //! \{ - - //! Creates a default `BaseMem` operand, that points to [0]. - constexpr BaseMem() noexcept - : Operand(Globals::Init, kOpMem, 0, 0, 0) {} - - //! Creates a `BaseMem` operand that is a clone of `other`. - constexpr BaseMem(const BaseMem& other) noexcept - : Operand(other) {} - - //! \cond INTERNAL - - //! Creates a `BaseMem` operand from 4 integers as used by `Operand_` struct. - constexpr BaseMem(Globals::Init_, uint32_t u0, uint32_t u1, uint32_t u2, uint32_t u3) noexcept - : Operand(Globals::Init, u0, u1, u2, u3) {} - - constexpr BaseMem(const Decomposed& d) noexcept - : Operand(Globals::Init, - kOpMem | (d.baseType << kSignatureMemBaseTypeShift ) - | (d.indexType << kSignatureMemIndexTypeShift) - | (d.size << kSignatureSizeShift ) - | d.flags, - d.baseId, - d.indexId, - uint32_t(d.offset)) {} - - //! \endcond - - //! Creates a completely uninitialized `BaseMem` operand. - inline explicit BaseMem(Globals::NoInit_) noexcept - : Operand(Globals::NoInit) {} - - //! Resets the memory operand - after the reset the memory points to [0]. - inline void reset() noexcept { - _signature = kOpMem; - _baseId = 0; - _data64 = 0; - } - - //! \} - - //! \name Overloaded Operators - //! \{ - - inline BaseMem& operator=(const BaseMem& other) noexcept { copyFrom(other); return *this; } - - //! \} - - //! \name Accessors - //! \{ - - //! Clones the memory operand. - constexpr BaseMem clone() const noexcept { return BaseMem(*this); } - - constexpr uint32_t addrType() const noexcept { return _getSignaturePart(); } - inline void setAddrType(uint32_t addrType) noexcept { _setSignaturePart(addrType); } - inline void resetAddrType() noexcept { _setSignaturePart(0); } - - constexpr bool isAbs() const noexcept { return addrType() == kAddrTypeAbs; } - inline void setAbs() noexcept { setAddrType(kAddrTypeAbs); } - - constexpr bool isRel() const noexcept { return addrType() == kAddrTypeRel; } - inline void setRel() noexcept { setAddrType(kAddrTypeRel); } - - constexpr bool isRegHome() const noexcept { return _hasSignaturePart(); } - inline void setRegHome() noexcept { _signature |= kSignatureMemRegHomeFlag; } - inline void clearRegHome() noexcept { _signature &= ~kSignatureMemRegHomeFlag; } - - //! Tests whether the memory operand has a BASE register or label specified. - constexpr bool hasBase() const noexcept { return (_signature & kSignatureMemBaseTypeMask) != 0; } - //! Tests whether the memory operand has an INDEX register specified. - constexpr bool hasIndex() const noexcept { return (_signature & kSignatureMemIndexTypeMask) != 0; } - //! Tests whether the memory operand has BASE and INDEX register. - constexpr bool hasBaseOrIndex() const noexcept { return (_signature & kSignatureMemBaseIndexMask) != 0; } - //! Tests whether the memory operand has BASE and INDEX register. - constexpr bool hasBaseAndIndex() const noexcept { return (_signature & kSignatureMemBaseTypeMask) != 0 && (_signature & kSignatureMemIndexTypeMask) != 0; } - - //! Tests whether the BASE operand is a register (registers start after `kLabelTag`). - constexpr bool hasBaseReg() const noexcept { return (_signature & kSignatureMemBaseTypeMask) > (Label::kLabelTag << kSignatureMemBaseTypeShift); } - //! Tests whether the BASE operand is a label. - constexpr bool hasBaseLabel() const noexcept { return (_signature & kSignatureMemBaseTypeMask) == (Label::kLabelTag << kSignatureMemBaseTypeShift); } - //! Tests whether the INDEX operand is a register (registers start after `kLabelTag`). - constexpr bool hasIndexReg() const noexcept { return (_signature & kSignatureMemIndexTypeMask) > (Label::kLabelTag << kSignatureMemIndexTypeShift); } - - //! Returns the type of the BASE register (0 if this memory operand doesn't - //! use the BASE register). - //! - //! \note If the returned type is one (a value never associated to a register - //! type) the BASE is not register, but it's a label. One equals to `kLabelTag`. - //! You should always check `hasBaseLabel()` before using `baseId()` result. - constexpr uint32_t baseType() const noexcept { return _getSignaturePart(); } - - //! Returns the type of an INDEX register (0 if this memory operand doesn't - //! use the INDEX register). - constexpr uint32_t indexType() const noexcept { return _getSignaturePart(); } - - //! This is used internally for BASE+INDEX validation. - constexpr uint32_t baseAndIndexTypes() const noexcept { return _getSignaturePart(); } - - //! Returns both BASE (4:0 bits) and INDEX (9:5 bits) types combined into a - //! single value. - //! - //! \remarks Returns id of the BASE register or label (if the BASE was - //! specified as label). - constexpr uint32_t baseId() const noexcept { return _baseId; } - - //! Returns the id of the INDEX register. - constexpr uint32_t indexId() const noexcept { return _mem.indexId; } - - //! Sets the id of the BASE register (without modifying its type). - inline void setBaseId(uint32_t rId) noexcept { _baseId = rId; } - //! Sets the id of the INDEX register (without modifying its type). - inline void setIndexId(uint32_t rId) noexcept { _mem.indexId = rId; } - - //! Sets the base register to type and id of the given `base` operand. - inline void setBase(const BaseReg& base) noexcept { return _setBase(base.type(), base.id()); } - //! Sets the index register to type and id of the given `index` operand. - inline void setIndex(const BaseReg& index) noexcept { return _setIndex(index.type(), index.id()); } - - inline void _setBase(uint32_t rType, uint32_t rId) noexcept { - _setSignaturePart(rType); - _baseId = rId; - } - - inline void _setIndex(uint32_t rType, uint32_t rId) noexcept { - _setSignaturePart(rType); - _mem.indexId = rId; - } - - //! Resets the memory operand's BASE register or label. - inline void resetBase() noexcept { _setBase(0, 0); } - //! Resets the memory operand's INDEX register. - inline void resetIndex() noexcept { _setIndex(0, 0); } - - //! Sets the memory operand size (in bytes). - inline void setSize(uint32_t size) noexcept { _setSignaturePart(size); } - - //! Tests whether the memory operand has a 64-bit offset or absolute address. - //! - //! If this is true then `hasBase()` must always report false. - constexpr bool isOffset64Bit() const noexcept { return baseType() == 0; } - - //! Tests whether the memory operand has a non-zero offset or absolute address. - constexpr bool hasOffset() const noexcept { - return (_mem.offsetLo32 | uint32_t(_baseId & Support::bitMaskFromBool(isOffset64Bit()))) != 0; - } - - //! Returns either relative offset or absolute address as 64-bit integer. - constexpr int64_t offset() const noexcept { - return isOffset64Bit() ? int64_t(uint64_t(_mem.offsetLo32) | (uint64_t(_baseId) << 32)) - : int64_t(int32_t(_mem.offsetLo32)); // Sign extend 32-bit offset. - } - - //! Returns a 32-bit low part of a 64-bit offset or absolute address. - constexpr int32_t offsetLo32() const noexcept { return int32_t(_mem.offsetLo32); } - //! Returns a 32-but high part of a 64-bit offset or absolute address. - //! - //! \note This function is UNSAFE and returns garbage if `isOffset64Bit()` - //! returns false. Never use it blindly without checking it first. - constexpr int32_t offsetHi32() const noexcept { return int32_t(_baseId); } - - //! Sets a 64-bit offset or an absolute address to `offset`. - //! - //! \note This functions attempts to set both high and low parts of a 64-bit - //! offset, however, if the operand has a BASE register it will store only the - //! low 32 bits of the offset / address as there is no way to store both BASE - //! and 64-bit offset, and there is currently no architecture that has such - //! capability targeted by AsmJit. - inline void setOffset(int64_t offset) noexcept { - uint32_t lo = uint32_t(uint64_t(offset) & 0xFFFFFFFFu); - uint32_t hi = uint32_t(uint64_t(offset) >> 32); - uint32_t hiMsk = Support::bitMaskFromBool(isOffset64Bit()); - - _mem.offsetLo32 = lo; - _baseId = (hi & hiMsk) | (_baseId & ~hiMsk); - } - //! Sets a low 32-bit offset to `offset` (don't use without knowing how BaseMem works). - inline void setOffsetLo32(int32_t offset) noexcept { _mem.offsetLo32 = uint32_t(offset); } - - //! Adjusts the offset by `offset`. - //! - //! \note This is a fast function that doesn't use the HI 32-bits of a - //! 64-bit offset. Use it only if you know that there is a BASE register - //! and the offset is only 32 bits anyway. - - //! Adjusts the offset by a 64-bit `offset`. - inline void addOffset(int64_t offset) noexcept { - if (isOffset64Bit()) { - int64_t result = offset + int64_t(uint64_t(_mem.offsetLo32) | (uint64_t(_baseId) << 32)); - _mem.offsetLo32 = uint32_t(uint64_t(result) & 0xFFFFFFFFu); - _baseId = uint32_t(uint64_t(result) >> 32); - } - else { - _mem.offsetLo32 += uint32_t(uint64_t(offset) & 0xFFFFFFFFu); - } - } - - //! Adds `offset` to a low 32-bit offset part (don't use without knowing how - //! BaseMem works). - inline void addOffsetLo32(int32_t offset) noexcept { _mem.offsetLo32 += uint32_t(offset); } - - //! Resets the memory offset to zero. - inline void resetOffset() noexcept { setOffset(0); } - - //! Resets the lo part of the memory offset to zero (don't use without knowing - //! how BaseMem works). - inline void resetOffsetLo32() noexcept { setOffsetLo32(0); } - - //! \} -}; - -// ============================================================================ -// [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 an immediate operand use `asmjit::imm()` helper, which can be used -//! with any type, not just the default 64-bit int. -class Imm : public Operand { -public: - //! \name Construction & Destruction - //! \{ - - //! Creates a new immediate value (initial value is 0). - constexpr Imm() noexcept - : Operand(Globals::Init, kOpImm, 0, 0, 0) {} - - //! Creates a new immediate value from `other`. - constexpr Imm(const Imm& other) noexcept - : Operand(other) {} - - //! Creates a new signed immediate value, assigning the value to `val`. - constexpr explicit Imm(int64_t val) noexcept - : Operand(Globals::Init, kOpImm, 0, Support::unpackU32At0(val), Support::unpackU32At1(val)) {} - - inline explicit Imm(Globals::NoInit_) noexcept - : Operand(Globals::NoInit) {} - - //! \} - - //! \name Overloaded Operators - //! \{ - - //! Assigns the value of the `other` operand to this immediate. - inline Imm& operator=(const Imm& other) noexcept { copyFrom(other); return *this; } - - //! \} - - //! \name Accessors - //! \{ - - //! Tests whether the immediate can be casted to 8-bit signed integer. - constexpr bool isInt8() const noexcept { return Support::isInt8(int64_t(_data64)); } - //! Tests whether the immediate can be casted to 8-bit unsigned integer. - constexpr bool isUInt8() const noexcept { return Support::isUInt8(int64_t(_data64)); } - //! Tests whether the immediate can be casted to 16-bit signed integer. - constexpr bool isInt16() const noexcept { return Support::isInt16(int64_t(_data64)); } - //! Tests whether the immediate can be casted to 16-bit unsigned integer. - constexpr bool isUInt16() const noexcept { return Support::isUInt16(int64_t(_data64)); } - //! Tests whether the immediate can be casted to 32-bit signed integer. - constexpr bool isInt32() const noexcept { return Support::isInt32(int64_t(_data64)); } - //! Tests whether the immediate can be casted to 32-bit unsigned integer. - constexpr bool isUInt32() const noexcept { return Support::isUInt32(int64_t(_data64)); } - - //! Returns immediate value as 8-bit signed integer, possibly cropped. - constexpr int8_t i8() const noexcept { return int8_t(_data64 & 0xFFu); } - //! Returns immediate value as 8-bit unsigned integer, possibly cropped. - constexpr uint8_t u8() const noexcept { return uint8_t(_data64 & 0xFFu); } - //! Returns immediate value as 16-bit signed integer, possibly cropped. - constexpr int16_t i16() const noexcept { return int16_t(_data64 & 0xFFFFu);} - //! Returns immediate value as 16-bit unsigned integer, possibly cropped. - constexpr uint16_t u16() const noexcept { return uint16_t(_data64 & 0xFFFFu);} - //! Returns immediate value as 32-bit signed integer, possibly cropped. - constexpr int32_t i32() const noexcept { return int32_t(_data64 & 0xFFFFFFFFu); } - //! Returns low 32-bit signed integer. - constexpr int32_t i32Lo() const noexcept { return int32_t(_data64 & 0xFFFFFFFFu); } - //! Returns high 32-bit signed integer. - constexpr int32_t i32Hi() const noexcept { return int32_t(_data64 >> 32); } - //! Returns immediate value as 32-bit unsigned integer, possibly cropped. - constexpr uint32_t u32() const noexcept { return uint32_t(_data64 & 0xFFFFFFFFu); } - //! Returns low 32-bit signed integer. - constexpr uint32_t u32Lo() const noexcept { return uint32_t(_data64 & 0xFFFFFFFFu); } - //! Returns high 32-bit signed integer. - constexpr uint32_t u32Hi() const noexcept { return uint32_t(_data64 >> 32); } - //! Returns immediate value as 64-bit signed integer. - constexpr int64_t i64() const noexcept { return int64_t(_data64); } - //! Returns immediate value as 64-bit unsigned integer. - constexpr uint64_t u64() const noexcept { return _data64; } - //! Returns immediate value as `intptr_t`, possibly cropped if size of `intptr_t` is 32 bits. - constexpr intptr_t iptr() const noexcept { return (sizeof(intptr_t) == sizeof(int64_t)) ? intptr_t(_data64) : intptr_t(i32()); } - //! Returns immediate value as `uintptr_t`, possibly cropped if size of `uintptr_t` is 32 bits. - constexpr uintptr_t uptr() const noexcept { return (sizeof(uintptr_t) == sizeof(uint64_t)) ? uintptr_t(_data64) : uintptr_t(u32()); } - - //! Sets immediate value to 8-bit signed integer `val`. - inline void setI8(int8_t val) noexcept { _data64 = uint64_t(int64_t(val)); } - //! Sets immediate value to 8-bit unsigned integer `val`. - inline void setU8(uint8_t val) noexcept { _data64 = uint64_t(val); } - //! Sets immediate value to 16-bit signed integer `val`. - inline void setI16(int16_t val) noexcept { _data64 = uint64_t(int64_t(val)); } - //! Sets immediate value to 16-bit unsigned integer `val`. - inline void setU16(uint16_t val) noexcept { _data64 = uint64_t(val); } - //! Sets immediate value to 32-bit signed integer `val`. - inline void setI32(int32_t val) noexcept { _data64 = uint64_t(int64_t(val)); } - //! Sets immediate value to 32-bit unsigned integer `val`. - inline void setU32(uint32_t val) noexcept { _data64 = uint64_t(val); } - //! Sets immediate value to 64-bit signed integer `val`. - inline void setI64(int64_t val) noexcept { _data64 = uint64_t(val); } - //! Sets immediate value to 64-bit unsigned integer `val`. - inline void setU64(uint64_t val) noexcept { _data64 = val; } - //! Sets immediate value to intptr_t `val`. - inline void setIPtr(intptr_t val) noexcept { _data64 = uint64_t(int64_t(val)); } - //! Sets immediate value to uintptr_t `val`. - inline void setUPtr(uintptr_t val) noexcept { _data64 = uint64_t(val); } - - //! Sets immediate value to `val`. - template - inline void setValue(T val) noexcept { setI64(int64_t(Support::asNormalized(val))); } - - inline void setDouble(double d) noexcept { - _data64 = Support::bitCast(d); - } - - //! \} - - //! \name Utilities - //! \{ - - //! Clones the immediate operand. - constexpr Imm clone() const noexcept { return Imm(*this); } - - inline void signExtend8Bits() noexcept { _data64 = uint64_t(int64_t(i8())); } - inline void signExtend16Bits() noexcept { _data64 = uint64_t(int64_t(i16())); } - inline void signExtend32Bits() noexcept { _data64 = uint64_t(int64_t(i32())); } - - inline void zeroExtend8Bits() noexcept { _data64 &= 0x000000FFu; } - inline void zeroExtend16Bits() noexcept { _data64 &= 0x0000FFFFu; } - inline void zeroExtend32Bits() noexcept { _data64 &= 0xFFFFFFFFu; } - - //! \} -}; - -//! Creates a new immediate operand. -//! -//! Using `imm(x)` is much nicer than using `Imm(x)` as this is a template -//! which can accept any integer including pointers and function pointers. -template -static constexpr Imm imm(T val) noexcept { - return Imm(std::is_signed::value ? int64_t(val) : int64_t(uint64_t(val))); -} - -//! \} - -ASMJIT_END_NAMESPACE - -#endif // _ASMJIT_CORE_OPERAND_H diff --git a/libraries/asmjit/asmjit/core/osutils.cpp b/libraries/asmjit/asmjit/core/osutils.cpp deleted file mode 100644 index 3115101a15c..00000000000 --- a/libraries/asmjit/asmjit/core/osutils.cpp +++ /dev/null @@ -1,90 +0,0 @@ -// [AsmJit] -// Machine Code Generation for C++. -// -// [License] -// Zlib - See LICENSE.md file in the package. - -#define ASMJIT_EXPORTS - -#include "../core/osutils.h" -#include "../core/support.h" - -#if defined(_WIN32) - #include -#elif defined(__APPLE__) - #include -#else - #include - #include -#endif - -ASMJIT_BEGIN_NAMESPACE - -// ============================================================================ -// [asmjit::OSUtils - GetTickCount] -// ============================================================================ - -uint32_t OSUtils::getTickCount() noexcept { -#if defined(_WIN32) - enum HiResStatus : uint32_t { - kHiResUnknown = 0, - kHiResAvailable = 1, - kHiResNotAvailable = 2 - }; - - static std::atomic _hiResStatus(kHiResUnknown); - static volatile double _hiResFreq(0); - - uint32_t status = _hiResStatus.load(); - LARGE_INTEGER now, qpf; - - if (status != kHiResNotAvailable && ::QueryPerformanceCounter(&now)) { - double freq = _hiResFreq; - if (status == kHiResUnknown) { - // Detects the availability of high resolution counter. - if (::QueryPerformanceFrequency(&qpf)) { - freq = double(qpf.QuadPart) / 1000.0; - _hiResFreq = freq; - _hiResStatus.compare_exchange_strong(status, kHiResAvailable); - status = kHiResAvailable; - } - else { - // High resolution not available. - _hiResStatus.compare_exchange_strong(status, kHiResNotAvailable); - } - } - - if (status == kHiResAvailable) - return uint32_t(uint64_t(int64_t(double(now.QuadPart) / freq)) & 0xFFFFFFFFu); - } - - // Bail to `GetTickCount()` if we cannot use high resolution. - return ::GetTickCount(); -#elif defined(__APPLE__) - // See Apple's QA1398. - static mach_timebase_info_data_t _machTime; - - uint32_t denom = _machTime.denom; - if (ASMJIT_UNLIKELY(!denom)) { - if (mach_timebase_info(&_machTime) != KERN_SUCCESS || !(denom = _machTime.denom)) - return 0; - } - - // `mach_absolute_time()` returns nanoseconds, we want milliseconds. - uint64_t t = mach_absolute_time() / 1000000u; - t = (t * _machTime.numer) / _machTime.denom; - return uint32_t(t & 0xFFFFFFFFu); -#elif defined(_POSIX_MONOTONIC_CLOCK) && _POSIX_MONOTONIC_CLOCK >= 0 - struct timespec ts; - if (ASMJIT_UNLIKELY(clock_gettime(CLOCK_MONOTONIC, &ts) != 0)) - return 0; - - uint64_t t = (uint64_t(ts.tv_sec ) * 1000u) + (uint64_t(ts.tv_nsec) / 1000000u); - return uint32_t(t & 0xFFFFFFFFu); -#else - #pragma message("asmjit::OSUtils::getTickCount() doesn't have implementation for the target OS.") - return 0; -#endif -} - -ASMJIT_END_NAMESPACE diff --git a/libraries/asmjit/asmjit/core/osutils.h b/libraries/asmjit/asmjit/core/osutils.h deleted file mode 100644 index 4b822314f91..00000000000 --- a/libraries/asmjit/asmjit/core/osutils.h +++ /dev/null @@ -1,96 +0,0 @@ -// [AsmJit] -// Machine Code Generation for C++. -// -// [License] -// Zlib - See LICENSE.md file in the package. - -#ifndef _ASMJIT_CORE_OSUTILS_H -#define _ASMJIT_CORE_OSUTILS_H - -#include "../core/globals.h" - -ASMJIT_BEGIN_NAMESPACE - -//! \addtogroup asmjit_support -//! \{ - -// ============================================================================ -// [asmjit::OSUtils] -// ============================================================================ - -//! Operating system utilities. -namespace OSUtils { - //! Gets the current CPU tick count, used for benchmarking (1ms resolution). - ASMJIT_API uint32_t getTickCount() noexcept; -}; - -// ============================================================================ -// [asmjit::Lock] -// ============================================================================ - -//! \cond INTERNAL - -//! Lock. -class Lock { -public: - ASMJIT_NONCOPYABLE(Lock) - - #if defined(_WIN32) - - typedef CRITICAL_SECTION Handle; - Handle _handle; - - inline Lock() noexcept { InitializeCriticalSection(&_handle); } - inline ~Lock() noexcept { DeleteCriticalSection(&_handle); } - - inline void lock() noexcept { EnterCriticalSection(&_handle); } - inline void unlock() noexcept { LeaveCriticalSection(&_handle); } - - #elif !defined(__EMSCRIPTEN__) - - typedef pthread_mutex_t Handle; - Handle _handle; - - inline Lock() noexcept { pthread_mutex_init(&_handle, nullptr); } - inline ~Lock() noexcept { pthread_mutex_destroy(&_handle); } - - inline void lock() noexcept { pthread_mutex_lock(&_handle); } - inline void unlock() noexcept { pthread_mutex_unlock(&_handle); } - - #else - - // Browser or other unsupported OS. - inline Lock() noexcept {} - inline ~Lock() noexcept {} - - inline void lock() noexcept {} - inline void unlock() noexcept {} - - #endif -}; - -//! \endcond - -// ============================================================================ -// [asmjit::ScopedLock] -// ============================================================================ - -//! \cond INTERNAL - -//! Scoped lock. -struct ScopedLock { - ASMJIT_NONCOPYABLE(ScopedLock) - - Lock& _target; - - inline ScopedLock(Lock& target) noexcept : _target(target) { _target.lock(); } - inline ~ScopedLock() noexcept { _target.unlock(); } -}; - -//! \endcond - -//! \} - -ASMJIT_END_NAMESPACE - -#endif // _ASMJIT_CORE_OSUTILS_H diff --git a/libraries/asmjit/asmjit/core/raassignment_p.h b/libraries/asmjit/asmjit/core/raassignment_p.h deleted file mode 100644 index 664e8997fb7..00000000000 --- a/libraries/asmjit/asmjit/core/raassignment_p.h +++ /dev/null @@ -1,384 +0,0 @@ -// [AsmJit] -// Machine Code Generation for C++. -// -// [License] -// Zlib - See LICENSE.md file in the package. - -#ifndef _ASMJIT_CORE_RAASSIGNMENT_P_H -#define _ASMJIT_CORE_RAASSIGNMENT_P_H - -#include "../core/build.h" -#ifndef ASMJIT_NO_COMPILER - -#include "../core/radefs_p.h" - -ASMJIT_BEGIN_NAMESPACE - -//! \cond INTERNAL -//! \addtogroup asmjit_ra -//! \{ - -// ============================================================================ -// [asmjit::RAAssignment] -// ============================================================================ - -class RAAssignment { - ASMJIT_NONCOPYABLE(RAAssignment) - -public: - enum Ids : uint32_t { - kPhysNone = 0xFF, - kWorkNone = RAWorkReg::kIdNone - }; - - enum DirtyBit : uint32_t { - kClean = 0, - kDirty = 1 - }; - - struct Layout { - inline void reset() noexcept { - physIndex.reset(); - physCount.reset(); - physTotal = 0; - workCount = 0; - workRegs = nullptr; - } - - RARegIndex physIndex; //!< Index of architecture registers per group. - RARegCount physCount; //!< Count of architecture registers per group. - uint32_t physTotal; //!< Count of physical registers of all groups. - uint32_t workCount; //!< Count of work registers. - const RAWorkRegs* workRegs; //!< WorkRegs data (vector). - }; - - struct PhysToWorkMap { - static inline size_t sizeOf(uint32_t count) noexcept { - return sizeof(PhysToWorkMap) - sizeof(uint32_t) + size_t(count) * sizeof(uint32_t); - } - - inline void reset(uint32_t count) noexcept { - assigned.reset(); - dirty.reset(); - - for (uint32_t i = 0; i < count; i++) - workIds[i] = kWorkNone; - } - - inline void copyFrom(const PhysToWorkMap* other, uint32_t count) noexcept { - size_t size = sizeOf(count); - memcpy(this, other, size); - } - - RARegMask assigned; //!< Assigned registers (each bit represents one physical reg). - RARegMask dirty; //!< Dirty registers (spill slot out of sync or no spill slot). - uint32_t workIds[1 /* ... */]; //!< PhysReg to WorkReg mapping. - }; - - struct WorkToPhysMap { - static inline size_t sizeOf(uint32_t count) noexcept { - return size_t(count) * sizeof(uint8_t); - } - - inline void reset(uint32_t count) noexcept { - for (uint32_t i = 0; i < count; i++) - physIds[i] = kPhysNone; - } - - inline void copyFrom(const WorkToPhysMap* other, uint32_t count) noexcept { - size_t size = sizeOf(count); - if (ASMJIT_LIKELY(size)) - memcpy(this, other, size); - } - - uint8_t physIds[1 /* ... */]; //!< WorkReg to PhysReg mapping - }; - - //! Physical registers layout. - Layout _layout; - //! WorkReg to PhysReg mapping. - WorkToPhysMap* _workToPhysMap; - //! PhysReg to WorkReg mapping and assigned/dirty bits. - PhysToWorkMap* _physToWorkMap; - //! Optimization to translate PhysRegs to WorkRegs faster. - uint32_t* _physToWorkIds[BaseReg::kGroupVirt]; - - //! \name Construction & Destruction - //! \{ - - inline RAAssignment() noexcept { - _layout.reset(); - resetMaps(); - } - - inline void initLayout(const RARegCount& physCount, const RAWorkRegs& workRegs) noexcept { - // Layout must be initialized before data. - ASMJIT_ASSERT(_physToWorkMap == nullptr); - ASMJIT_ASSERT(_workToPhysMap == nullptr); - - _layout.physIndex.buildIndexes(physCount); - _layout.physCount = physCount; - _layout.physTotal = uint32_t(_layout.physIndex[BaseReg::kGroupVirt - 1]) + - uint32_t(_layout.physCount[BaseReg::kGroupVirt - 1]) ; - _layout.workCount = workRegs.size(); - _layout.workRegs = &workRegs; - } - - inline void initMaps(PhysToWorkMap* physToWorkMap, WorkToPhysMap* workToPhysMap) noexcept { - _physToWorkMap = physToWorkMap; - _workToPhysMap = workToPhysMap; - for (uint32_t group = 0; group < BaseReg::kGroupVirt; group++) - _physToWorkIds[group] = physToWorkMap->workIds + _layout.physIndex.get(group); - } - - inline void resetMaps() noexcept { - _physToWorkMap = nullptr; - _workToPhysMap = nullptr; - for (uint32_t group = 0; group < BaseReg::kGroupVirt; group++) - _physToWorkIds[group] = nullptr; - } - - //! \} - - //! \name Accessors - //! \{ - - inline PhysToWorkMap* physToWorkMap() const noexcept { return _physToWorkMap; } - inline WorkToPhysMap* workToPhysMap() const noexcept { return _workToPhysMap; } - - inline RARegMask& assigned() noexcept { return _physToWorkMap->assigned; } - inline const RARegMask& assigned() const noexcept { return _physToWorkMap->assigned; } - inline uint32_t assigned(uint32_t group) const noexcept { return _physToWorkMap->assigned[group]; } - - inline RARegMask& dirty() noexcept { return _physToWorkMap->dirty; } - inline const RARegMask& dirty() const noexcept { return _physToWorkMap->dirty; } - inline uint32_t dirty(uint32_t group) const noexcept { return _physToWorkMap->dirty[group]; } - - inline uint32_t workToPhysId(uint32_t group, uint32_t workId) const noexcept { - ASMJIT_UNUSED(group); - ASMJIT_ASSERT(workId != kWorkNone); - ASMJIT_ASSERT(workId < _layout.workCount); - return _workToPhysMap->physIds[workId]; - } - - inline uint32_t physToWorkId(uint32_t group, uint32_t physId) const noexcept { - ASMJIT_ASSERT(physId < Globals::kMaxPhysRegs); - return _physToWorkIds[group][physId]; - } - - inline bool isPhysAssigned(uint32_t group, uint32_t physId) const noexcept { - ASMJIT_ASSERT(physId < Globals::kMaxPhysRegs); - return Support::bitTest(_physToWorkMap->assigned[group], physId); - } - - inline bool isPhysDirty(uint32_t group, uint32_t physId) const noexcept { - ASMJIT_ASSERT(physId < Globals::kMaxPhysRegs); - return Support::bitTest(_physToWorkMap->dirty[group], physId); - } - - //! \} - - //! \name Assignment - //! \{ - - // These are low-level allocation helpers that are used to update the current - // mappings between physical and virt/work registers and also to update masks - // that represent allocated and dirty registers. These functions don't emit - // any code; they are only used to update and keep all mappings in sync. - - //! Assign [VirtReg/WorkReg] to a physical register. - ASMJIT_INLINE void assign(uint32_t group, uint32_t workId, uint32_t physId, uint32_t dirty) noexcept { - ASMJIT_ASSERT(workToPhysId(group, workId) == kPhysNone); - ASMJIT_ASSERT(physToWorkId(group, physId) == kWorkNone); - ASMJIT_ASSERT(!isPhysAssigned(group, physId)); - ASMJIT_ASSERT(!isPhysDirty(group, physId)); - - _workToPhysMap->physIds[workId] = uint8_t(physId); - _physToWorkIds[group][physId] = workId; - - uint32_t regMask = Support::bitMask(physId); - _physToWorkMap->assigned[group] |= regMask; - _physToWorkMap->dirty[group] |= regMask & Support::bitMaskFromBool(dirty); - - verify(); - } - - //! Reassign [VirtReg/WorkReg] to `dstPhysId` from `srcPhysId`. - ASMJIT_INLINE void reassign(uint32_t group, uint32_t workId, uint32_t dstPhysId, uint32_t srcPhysId) noexcept { - ASMJIT_ASSERT(dstPhysId != srcPhysId); - ASMJIT_ASSERT(workToPhysId(group, workId) == srcPhysId); - ASMJIT_ASSERT(physToWorkId(group, srcPhysId) == workId); - ASMJIT_ASSERT(isPhysAssigned(group, srcPhysId) == true); - ASMJIT_ASSERT(isPhysAssigned(group, dstPhysId) == false); - - _workToPhysMap->physIds[workId] = uint8_t(dstPhysId); - _physToWorkIds[group][srcPhysId] = kWorkNone; - _physToWorkIds[group][dstPhysId] = workId; - - uint32_t srcMask = Support::bitMask(srcPhysId); - uint32_t dstMask = Support::bitMask(dstPhysId); - - uint32_t dirty = (_physToWorkMap->dirty[group] & srcMask) != 0; - uint32_t regMask = dstMask | srcMask; - - _physToWorkMap->assigned[group] ^= regMask; - _physToWorkMap->dirty[group] ^= regMask & Support::bitMaskFromBool(dirty); - - verify(); - } - - ASMJIT_INLINE void swap(uint32_t group, uint32_t aWorkId, uint32_t aPhysId, uint32_t bWorkId, uint32_t bPhysId) noexcept { - ASMJIT_ASSERT(aPhysId != bPhysId); - ASMJIT_ASSERT(workToPhysId(group, aWorkId) == aPhysId); - ASMJIT_ASSERT(workToPhysId(group, bWorkId) == bPhysId); - ASMJIT_ASSERT(physToWorkId(group, aPhysId) == aWorkId); - ASMJIT_ASSERT(physToWorkId(group, bPhysId) == bWorkId); - ASMJIT_ASSERT(isPhysAssigned(group, aPhysId)); - ASMJIT_ASSERT(isPhysAssigned(group, bPhysId)); - - _workToPhysMap->physIds[aWorkId] = uint8_t(bPhysId); - _workToPhysMap->physIds[bWorkId] = uint8_t(aPhysId); - _physToWorkIds[group][aPhysId] = bWorkId; - _physToWorkIds[group][bPhysId] = aWorkId; - - uint32_t aMask = Support::bitMask(aPhysId); - uint32_t bMask = Support::bitMask(bPhysId); - - uint32_t flipMask = Support::bitMaskFromBool( - ((_physToWorkMap->dirty[group] & aMask) != 0) ^ - ((_physToWorkMap->dirty[group] & bMask) != 0)); - - uint32_t regMask = aMask | bMask; - _physToWorkMap->dirty[group] ^= regMask & flipMask; - - verify(); - } - - //! Unassign [VirtReg/WorkReg] from a physical register. - ASMJIT_INLINE void unassign(uint32_t group, uint32_t workId, uint32_t physId) noexcept { - ASMJIT_ASSERT(physId < Globals::kMaxPhysRegs); - ASMJIT_ASSERT(workToPhysId(group, workId) == physId); - ASMJIT_ASSERT(physToWorkId(group, physId) == workId); - ASMJIT_ASSERT(isPhysAssigned(group, physId)); - - _workToPhysMap->physIds[workId] = kPhysNone; - _physToWorkIds[group][physId] = kWorkNone; - - uint32_t regMask = Support::bitMask(physId); - _physToWorkMap->assigned[group] &= ~regMask; - _physToWorkMap->dirty[group] &= ~regMask; - - verify(); - } - - inline void makeClean(uint32_t group, uint32_t workId, uint32_t physId) noexcept { - ASMJIT_UNUSED(workId); - - uint32_t regMask = Support::bitMask(physId); - _physToWorkMap->dirty[group] &= ~regMask; - } - - inline void makeDirty(uint32_t group, uint32_t workId, uint32_t physId) noexcept { - ASMJIT_UNUSED(workId); - - uint32_t regMask = Support::bitMask(physId); - _physToWorkMap->dirty[group] |= regMask; - } - - //! \} - - //! \name Utilities - //! \{ - - inline void swap(RAAssignment& other) noexcept { - std::swap(_workToPhysMap, other._workToPhysMap); - std::swap(_physToWorkMap, other._physToWorkMap); - - for (uint32_t group = 0; group < BaseReg::kGroupVirt; group++) - std::swap(_physToWorkIds[group], other._physToWorkIds[group]); - } - - inline void copyFrom(const PhysToWorkMap* physToWorkMap, const WorkToPhysMap* workToPhysMap) noexcept { - memcpy(_physToWorkMap, physToWorkMap, PhysToWorkMap::sizeOf(_layout.physTotal)); - memcpy(_workToPhysMap, workToPhysMap, WorkToPhysMap::sizeOf(_layout.workCount)); - } - - inline void copyFrom(const RAAssignment& other) noexcept { - copyFrom(other.physToWorkMap(), other.workToPhysMap()); - } - - // Not really useful outside of debugging. - bool equals(const RAAssignment& other) const noexcept { - // Layout should always match. - if (_layout.physIndex != other._layout.physIndex || - _layout.physCount != other._layout.physCount || - _layout.physTotal != other._layout.physTotal || - _layout.workCount != other._layout.workCount || - _layout.workRegs != other._layout.workRegs) - return false; - - uint32_t physTotal = _layout.physTotal; - uint32_t workCount = _layout.workCount; - - for (uint32_t physId = 0; physId < physTotal; physId++) { - uint32_t thisWorkId = _physToWorkMap->workIds[physId]; - uint32_t otherWorkId = other._physToWorkMap->workIds[physId]; - if (thisWorkId != otherWorkId) - return false; - } - - for (uint32_t workId = 0; workId < workCount; workId++) { - uint32_t thisPhysId = _workToPhysMap->physIds[workId]; - uint32_t otherPhysId = other._workToPhysMap->physIds[workId]; - if (thisPhysId != otherPhysId) - return false; - } - - if (_physToWorkMap->assigned != other._physToWorkMap->assigned || - _physToWorkMap->dirty != other._physToWorkMap->dirty ) - return false; - - return true; - } - -#if defined(ASMJIT_BUILD_DEBUG) - ASMJIT_NOINLINE void verify() noexcept { - // Verify WorkToPhysMap. - { - for (uint32_t workId = 0; workId < _layout.workCount; workId++) { - uint32_t physId = _workToPhysMap->physIds[workId]; - if (physId != kPhysNone) { - const RAWorkReg* workReg = _layout.workRegs->at(workId); - uint32_t group = workReg->group(); - ASMJIT_ASSERT(_physToWorkIds[group][physId] == workId); - } - } - } - - // Verify PhysToWorkMap. - { - for (uint32_t group = 0; group < BaseReg::kGroupVirt; group++) { - uint32_t physCount = _layout.physCount[group]; - for (uint32_t physId = 0; physId < physCount; physId++) { - uint32_t workId = _physToWorkIds[group][physId]; - if (workId != kWorkNone) { - ASMJIT_ASSERT(_workToPhysMap->physIds[workId] == physId); - } - } - } - } - } -#else - inline void verify() noexcept {} -#endif - - //! \} -}; - -//! \} -//! \endcond - -ASMJIT_END_NAMESPACE - -#endif // !ASMJIT_NO_COMPILER -#endif // _ASMJIT_CORE_RAASSIGNMENT_P_H diff --git a/libraries/asmjit/asmjit/core/rabuilders_p.h b/libraries/asmjit/asmjit/core/rabuilders_p.h deleted file mode 100644 index cb1eac89068..00000000000 --- a/libraries/asmjit/asmjit/core/rabuilders_p.h +++ /dev/null @@ -1,420 +0,0 @@ -// [AsmJit] -// Machine Code Generation for C++. -// -// [License] -// Zlib - See LICENSE.md file in the package. - -#ifndef _ASMJIT_CORE_RABUILDERS_P_H -#define _ASMJIT_CORE_RABUILDERS_P_H - -#include "../core/build.h" -#ifndef ASMJIT_NO_COMPILER - -#include "../core/rapass_p.h" - -ASMJIT_BEGIN_NAMESPACE - -//! \cond INTERNAL -//! \addtogroup asmjit_ra -//! \{ - -// ============================================================================ -// [asmjit::RACFGBuilder] -// ============================================================================ - -template -class RACFGBuilder { -public: - RAPass* _pass; - BaseCompiler* _cc; - RABlock* _curBlock; - RABlock* _retBlock; - - // NOTE: This is a bit hacky. There are some nodes which are processed twice - // (see `onBeforeCall()` and `onBeforeRet()`) as they can insert some nodes - // around them. Since we don't have any flags to mark these we just use their - // position that is [at that time] unassigned. - static constexpr uint32_t kNodePositionDidOnBefore = 0xFFFFFFFFu; - - inline RACFGBuilder(RAPass* pass) noexcept - : _pass(pass), - _cc(pass->cc()), - _curBlock(nullptr), - _retBlock(nullptr) {} - - inline BaseCompiler* cc() const noexcept { return _cc; } - - Error run() noexcept { - #ifndef ASMJIT_NO_LOGGING - Logger* logger = _pass->debugLogger(); - uint32_t flags = FormatOptions::kFlagPositions; - RABlock* lastPrintedBlock = nullptr; - StringTmp<512> sb; - #endif - - ASMJIT_RA_LOG_FORMAT("[RAPass::BuildCFG]\n"); - - FuncNode* func = _pass->func(); - BaseNode* node = nullptr; - - // Create entry and exit blocks. - _retBlock = _pass->newBlockOrExistingAt(func->exitNode(), &node); - if (ASMJIT_UNLIKELY(!_retBlock)) - return DebugUtils::errored(kErrorOutOfMemory); - ASMJIT_PROPAGATE(_pass->addExitBlock(_retBlock)); - - if (node != func) { - _curBlock = _pass->newBlock(); - if (ASMJIT_UNLIKELY(!_curBlock)) - return DebugUtils::errored(kErrorOutOfMemory); - } - else { - // Function that has no code at all. - _curBlock = _retBlock; - } - - ASMJIT_PROPAGATE(_pass->addBlock(_curBlock)); - - RARegsStats blockRegStats; - blockRegStats.reset(); - RAInstBuilder ib; - - bool hasCode = false; - uint32_t exitLabelId = func->exitNode()->id(); - - ASMJIT_RA_LOG_COMPLEX({ - flags |= logger->flags(); - - Logging::formatNode(sb, flags, cc(), func); - logger->logf(" %s\n", sb.data()); - - lastPrintedBlock = _curBlock; - logger->logf(" {#%u}\n", lastPrintedBlock->blockId()); - }); - - node = func->next(); - if (ASMJIT_UNLIKELY(!node)) - return DebugUtils::errored(kErrorInvalidState); - - _curBlock->setFirst(node); - _curBlock->setLast(node); - - for (;;) { - BaseNode* next = node->next(); - ASMJIT_ASSERT(node->position() == 0 || node->position() == kNodePositionDidOnBefore); - - if (node->isInst()) { - if (ASMJIT_UNLIKELY(!_curBlock)) { - // If this code is unreachable then it has to be removed. - ASMJIT_RA_LOG_COMPLEX({ - sb.clear(); - Logging::formatNode(sb, flags, cc(), node); - logger->logf(" %s\n", sb.data()); - }); - cc()->removeNode(node); - node = next; - continue; - } - else { - // Handle `InstNode`, `FuncCallNode`, and `FuncRetNode`. All of - // these share the `InstNode` interface and contain operands. - hasCode = true; - - if (node->type() != BaseNode::kNodeInst) { - if (node->position() != kNodePositionDidOnBefore) { - // Call and Reg are complicated as they may insert some surrounding - // code around them. The simplest approach is to get the previous - // node, call the `onBefore()` handlers and then check whether - // anything changed and restart if so. By restart we mean that the - // current `node` would go back to the first possible inserted node - // by `onBeforeCall()` or `onBeforeRet()`. - BaseNode* prev = node->prev(); - if (node->type() == BaseNode::kNodeFuncCall) { - ASMJIT_PROPAGATE(static_cast(this)->onBeforeCall(node->as())); - } - else if (node->type() == BaseNode::kNodeFuncRet) { - ASMJIT_PROPAGATE(static_cast(this)->onBeforeRet(node->as())); - } - - if (prev != node->prev()) { - // If this was the first node in the block and something was - // inserted before it then we have to update the first block. - if (_curBlock->first() == node) - _curBlock->setFirst(prev->next()); - - node->setPosition(kNodePositionDidOnBefore); - node = prev->next(); - - // `onBeforeCall()` and `onBeforeRet()` can only insert instructions. - ASMJIT_ASSERT(node->isInst()); - } - - // Necessary if something was inserted after `node`, but nothing before. - next = node->next(); - } - else { - // Change the position back to its original value. - node->setPosition(0); - } - } - - InstNode* inst = node->as(); - ASMJIT_RA_LOG_COMPLEX({ - sb.clear(); - Logging::formatNode(sb, flags, cc(), node); - logger->logf(" %s\n", sb.data()); - }); - - uint32_t controlType = BaseInst::kControlNone; - ib.reset(); - ASMJIT_PROPAGATE(static_cast(this)->onInst(inst, controlType, ib)); - - if (node->type() != BaseNode::kNodeInst) { - if (node->type() == BaseNode::kNodeFuncCall) { - ASMJIT_PROPAGATE(static_cast(this)->onCall(inst->as(), ib)); - } - else if (node->type() == BaseNode::kNodeFuncRet) { - ASMJIT_PROPAGATE(static_cast(this)->onRet(inst->as(), ib)); - controlType = BaseInst::kControlReturn; - } - } - - ASMJIT_PROPAGATE(_pass->assignRAInst(inst, _curBlock, ib)); - blockRegStats.combineWith(ib._stats); - - if (controlType != BaseInst::kControlNone) { - // Support for conditional and unconditional jumps. - if (controlType == BaseInst::kControlJump || controlType == BaseInst::kControlBranch) { - _curBlock->setLast(node); - _curBlock->addFlags(RABlock::kFlagHasTerminator); - _curBlock->makeConstructed(blockRegStats); - - if (!(inst->instOptions() & BaseInst::kOptionUnfollow)) { - // Jmp/Jcc/Call/Loop/etc... - uint32_t opCount = inst->opCount(); - const Operand* opArray = inst->operands(); - - // The last operand must be label (this supports also instructions - // like jecx in explicit form). - if (ASMJIT_UNLIKELY(opCount == 0 || !opArray[opCount - 1].isLabel())) - return DebugUtils::errored(kErrorInvalidState); - - LabelNode* cbLabel; - ASMJIT_PROPAGATE(cc()->labelNodeOf(&cbLabel, opArray[opCount - 1].as