diff --git a/.travis.yml b/.travis.yml index 347df59..b64f9eb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -57,6 +57,33 @@ matrix: - sourceline: "ppa:ubuntu-toolchain-r/test" packages: [clang++-9] + - name: "Linux Clang Default [64-bit] [REL] [NoBuilder]" + env: BUILD_MATRIX="BUILD_TYPE=Release && CC=clang-9 && CXX=clang++-9" EXTRA_OPTIONS="-DASMJIT_NO_BUILDER=1" + os: linux + addons: + apt: + sources: + - sourceline: "ppa:ubuntu-toolchain-r/test" + packages: [clang++-9] + + - name: "Linux Clang Default [64-bit] [REL] [NoCompiler]" + env: BUILD_MATRIX="BUILD_TYPE=Release && CC=clang-9 && CXX=clang++-9" EXTRA_OPTIONS="-DASMJIT_NO_COMPILER=1" + os: linux + addons: + apt: + sources: + - sourceline: "ppa:ubuntu-toolchain-r/test" + packages: [clang++-9] + + - name: "Linux Clang Default [64-bit] [REL] [NoLogging]" + env: BUILD_MATRIX="BUILD_TYPE=Release && CC=clang-9 && CXX=clang++-9" EXTRA_OPTIONS="-DASMJIT_NO_LOGGING=1" + os: linux + addons: + apt: + sources: + - sourceline: "ppa:ubuntu-toolchain-r/test" + packages: [clang++-9] + - name: "Linux GCC 4.8 [32-bit] [DBG]" env: BUILD_MATRIX="BUILD_TYPE=Debug && CC=gcc-4.8 && CXX=g++-4.8" CXXFLAGS=-m32 LDFLAGS=-m32 os: linux @@ -269,5 +296,8 @@ script: - eval "$RUN_CMD ./asmjit_test_unit --quick" - eval "$RUN_CMD ./asmjit_test_opcode > /dev/null" - eval "$RUN_CMD ./asmjit_test_x86_asm" - - eval "$RUN_CMD ./asmjit_test_x86_cc" - eval "$RUN_CMD ./asmjit_test_x86_sections" + - | + if [ -f ./asmjit_test_x86_cc ]; then + eval "$RUN_CMD ./asmjit_test_x86_cc" + fi diff --git a/CMakeLists.txt b/CMakeLists.txt index 88e3f6a..7b6c90b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -464,7 +464,6 @@ if (NOT ASMJIT_EMBED) foreach(_target asmjit_test_opcode asmjit_test_x86_asm - asmjit_test_x86_cc asmjit_test_x86_sections) asmjit_add_target(${_target} TEST SOURCES test/${_target}.cpp @@ -474,6 +473,15 @@ if (NOT ASMJIT_EMBED) CFLAGS_REL ${ASMJIT_PRIVATE_CFLAGS_REL}) endforeach() + if (NOT (ASMJIT_NO_BUILDER OR ASMJIT_NO_COMPILER)) + asmjit_add_target(asmjit_test_x86_cc TEST + SOURCES test/asmjit_test_x86_cc.cpp + LIBRARIES AsmJit::AsmJit + CFLAGS ${ASMJIT_PRIVATE_CFLAGS} + CFLAGS_DBG ${ASMJIT_PRIVATE_CFLAGS_DBG} + CFLAGS_REL ${ASMJIT_PRIVATE_CFLAGS_REL}) + endif() + foreach(_target asmjit_bench_x86) asmjit_add_target(${_target} EXECUTABLE SOURCES test/${_target}.cpp diff --git a/README.md b/README.md index 57df043..24c726b 100644 --- a/README.md +++ b/README.md @@ -1460,6 +1460,87 @@ static void exampleUseOfConstPool(x86::Compiler& cc) { } ``` +### Jump Tables + +**Compiler** supports `jmp` instruction with reg/mem operand, which is a commonly used pattern to implement indirect jumps within a function, for example to implement `switch()` statement in a programming languages. By default AsmJit assumes that every basic block can be a possible jump target as it's unable to deduce targets from instruction's operands. This is a very pessimistic default that should be avoided if possible as it's costly and very unfriendly to liveness analysis and register allocation. So instead of relying on such pessimistic default, use **JumpAnnotation** to annotate indirect jumps: + +```c++ +#include + +using namespace asmjit; + +static void exampleUseOfIndirectJump(x86::Compiler& cc) { + cc.addFunc(FuncSignatureT(CallConv::kIdHost)); + + // Function arguments + x86::Xmm a = cc.newXmmSs("a"); + x86::Xmm b = cc.newXmmSs("b"); + x86::Gp op = cc.newUInt32("op"); + + x86::Gp target = cc.newIntPtr("target"); + x86::Gp offset = cc.newIntPtr("offset"); + + Label L_Table = cc.newLabel(); + Label L_Add = cc.newLabel(); + Label L_Sub = cc.newLabel(); + Label L_Mul = cc.newLabel(); + Label L_Div = cc.newLabel(); + Label L_End = cc.newLabel(); + + cc.setArg(0, a); + cc.setArg(1, b); + cc.setArg(2, op); + + // Jump annotation is a building block that allows to annotate all + // possible targets where `jmp()` can jump. It then drives the CFG + // contruction and liveness analysis, which impacts register allocation. + JumpAnnotation* annotation = cc.newJumpAnnotation(); + annotation->addLabel(L_Add); + annotation->addLabel(L_Sub); + annotation->addLabel(L_Mul); + annotation->addLabel(L_Div); + + // Most likely not the common indirect jump approach, but it + // doesn't really matter how final address is calculated. The + // most important path using JumpAnnotation with `jmp()`. + cc.lea(offset, x86::ptr(L_Table)); + if (cc.is64Bit()) + cc.movsxd(target, x86::dword_ptr(offset, op.cloneAs(offset), 2)); + else + cc.mov(target, x86::dword_ptr(offset, op.cloneAs(offset), 2)); + cc.add(target, offset); + cc.jmp(target, annotation); + + // Acts like a switch() statement in C. + cc.bind(L_Add); + cc.addss(a, b); + cc.jmp(L_End); + + cc.bind(L_Sub); + cc.subss(a, b); + cc.jmp(L_End); + + cc.bind(L_Mul); + cc.mulss(a, b); + cc.jmp(L_End); + + cc.bind(L_Div); + cc.divss(a, b); + + cc.bind(L_End); + cc.ret(a); + + cc.endFunc(); + + // Relative int32_t offsets of `L_XXX - L_Table`. + cc.bind(L_Table); + cc.embedLabelDelta(L_Add, L_Table, 4); + cc.embedLabelDelta(L_Sub, L_Table, 4); + cc.embedLabelDelta(L_Mul, L_Table, 4); + cc.embedLabelDelta(L_Div, L_Table, 4); +} +``` + Advanced Features ----------------- diff --git a/src/asmjit/core/api-build_p.h b/src/asmjit/core/api-build_p.h index 796c995..714107c 100644 --- a/src/asmjit/core/api-build_p.h +++ b/src/asmjit/core/api-build_p.h @@ -26,6 +26,16 @@ #define ASMJIT_EXPORTS +// 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 + // Dependencies only required for asmjit build, but never exposed through public headers. #ifdef _WIN32 #ifndef WIN32_LEAN_AND_MEAN @@ -37,8 +47,23 @@ #include #endif +// ============================================================================ +// [asmjit::Build - Globals - Build-Only] +// ============================================================================ + #include "./api-config.h" +#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 + // Make sure '#ifdef'ed unit tests are properly highlighted in IDE. #if !defined(ASMJIT_TEST) && defined(__INTELLISENSE__) #define ASMJIT_TEST diff --git a/src/asmjit/core/api-config.h b/src/asmjit/core/api-config.h index a70dc73..de083ff 100644 --- a/src/asmjit/core/api-config.h +++ b/src/asmjit/core/api-config.h @@ -82,9 +82,14 @@ // #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 +// #define ASMJIT_NO_INTROSPECTION // Disable API related to instruction database. // // (validation, cpu features, rw-info, etc). +// ASMJIT_NO_BUILDER implies ASMJIT_NO_COMPILER. +#if defined(ASMJIT_NO_BUILDER) && !defined(ASMJIT_NO_COMPILER) + #define ASMJIT_NO_COMPILER +#endif + // 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." @@ -421,8 +426,6 @@ #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])) @@ -514,44 +517,17 @@ __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 +// Try to cleanup things not used in other public headers. +#ifndef ASMJIT_EXPORTS + #undef ASMJIT_CXX_CLANG + #undef ASMJIT_CXX_GNU + #undef ASMJIT_CXX_INTEL + #undef ASMJIT_CXX_MSC + #undef ASMJIT_CXX_MAKE_VER +#endif #endif // ASMJIT_CORE_API_CONFIG_H_INCLUDED diff --git a/src/asmjit/core/arch.cpp b/src/asmjit/core/arch.cpp index 933858b..97fca9d 100644 --- a/src/asmjit/core/arch.cpp +++ b/src/asmjit/core/arch.cpp @@ -76,7 +76,7 @@ ASMJIT_FAVOR_SIZE Error ArchUtils::typeIdToRegInfo(uint32_t archId, uint32_t& ty regInfo._signature = 0; // TODO: Move to X86 backend. - #ifdef ASMJIT_BUILD_X86 +#ifdef ASMJIT_BUILD_X86 if (ArchInfo::isX86Family(archId)) { // Passed RegType instead of TypeId? if (typeId <= BaseReg::kTypeMax) @@ -168,7 +168,7 @@ ASMJIT_FAVOR_SIZE Error ArchUtils::typeIdToRegInfo(uint32_t archId, uint32_t& ty regInfo._signature = x86::opData.archRegs.regInfo[regType].signature(); return kErrorOk; } - #endif +#endif return DebugUtils::errored(kErrorInvalidArch); } diff --git a/src/asmjit/core/arch.h b/src/asmjit/core/arch.h index 53471e5..b0a27fd 100644 --- a/src/asmjit/core/arch.h +++ b/src/asmjit/core/arch.h @@ -85,19 +85,19 @@ public: // ARM sub-types. kSubIdA32_Thumb = 8, //!< THUMB|THUMBv2 sub-type (only ARM in 32-bit mode). - #if (ASMJIT_ARCH_X86) && defined(__AVX512VL__) +#if (ASMJIT_ARCH_X86) && defined(__AVX512VL__) kSubIdHost = kSubIdX86_AVX512VL - #elif (ASMJIT_ARCH_X86) && defined(__AVX512F__) +#elif (ASMJIT_ARCH_X86) && defined(__AVX512F__) kSubIdHost = kSubIdX86_AVX512 - #elif (ASMJIT_ARCH_X86) && defined(__AVX2__) +#elif (ASMJIT_ARCH_X86) && defined(__AVX2__) kSubIdHost = kSubIdX86_AVX2 - #elif (ASMJIT_ARCH_X86) && defined(__AVX__) +#elif (ASMJIT_ARCH_X86) && defined(__AVX__) kSubIdHost = kSubIdX86_AVX - #elif (ASMJIT_ARCH_ARM == 32) && (defined(_M_ARMT) || defined(__thumb__) || defined(__thumb2__)) +#elif (ASMJIT_ARCH_ARM == 32) && (defined(_M_ARMT) || defined(__thumb__) || defined(__thumb2__)) kSubIdHost = kSubIdA32_Thumb - #else +#else kSubIdHost = 0 - #endif +#endif }; //! \name Construction & Destruction diff --git a/src/asmjit/core/assembler.cpp b/src/asmjit/core/assembler.cpp index ff4c303..35c39ab 100644 --- a/src/asmjit/core/assembler.cpp +++ b/src/asmjit/core/assembler.cpp @@ -99,10 +99,10 @@ Error BaseAssembler::section(Section* section) { if (!_code->isSectionValid(section->id()) || _code->_sections[section->id()] != section) return reportError(DebugUtils::errored(kErrorInvalidSection)); - #ifndef ASMJIT_NO_LOGGING +#ifndef ASMJIT_NO_LOGGING if (hasEmitterOption(kOptionLoggingEnabled)) _code->_logger->logf(".section %s {#%u}\n", section->name(), section->id()); - #endif +#endif BaseAssembler_initSection(this, section); return kErrorOk; @@ -142,10 +142,10 @@ Error BaseAssembler::bind(const Label& label) { Error err = _code->bindLabel(label, _section->id(), offset()); - #ifndef ASMJIT_NO_LOGGING +#ifndef ASMJIT_NO_LOGGING if (hasEmitterOption(kOptionLoggingEnabled)) BaseAssembler_logLabel(this, label); - #endif +#endif resetInlineComment(); if (err) @@ -277,6 +277,7 @@ Error BaseAssembler::_emitFailed( // [asmjit::BaseAssembler - Embed] // ============================================================================ +#ifndef ASMJIT_NO_LOGGING struct DataSizeByPower { char str[4]; }; @@ -287,6 +288,7 @@ static const DataSizeByPower dataSizeByPowerTable[] = { { "dd" }, { "dq" } }; +#endif Error BaseAssembler::embed(const void* data, uint32_t dataSize) { if (ASMJIT_UNLIKELY(!_code)) @@ -300,10 +302,10 @@ Error BaseAssembler::embed(const void* data, uint32_t dataSize) { writer.emitData(data, dataSize); - #ifndef ASMJIT_NO_LOGGING +#ifndef ASMJIT_NO_LOGGING if (ASMJIT_UNLIKELY(hasEmitterOption(kOptionLoggingEnabled))) _code->_logger->logBinary(data, dataSize); - #endif +#endif writer.done(this); return kErrorOk; @@ -326,7 +328,7 @@ Error BaseAssembler::embedLabel(const Label& label) { CodeBufferWriter writer(this); ASMJIT_PROPAGATE(writer.ensureSpace(this, dataSize)); - #ifndef ASMJIT_NO_LOGGING +#ifndef ASMJIT_NO_LOGGING if (ASMJIT_UNLIKELY(hasEmitterOption(kOptionLoggingEnabled))) { StringTmp<256> sb; sb.appendFormat(".%s ", dataSizeByPowerTable[Support::ctz(dataSize)].str); @@ -334,7 +336,7 @@ Error BaseAssembler::embedLabel(const Label& label) { sb.appendChar('\n'); _code->_logger->log(sb); } - #endif +#endif // TODO: Does it make sense to calculate the address here if everything is known? /* @@ -388,7 +390,7 @@ Error BaseAssembler::embedLabelDelta(const Label& label, const Label& base, uint CodeBufferWriter writer(this); ASMJIT_PROPAGATE(writer.ensureSpace(this, dataSize)); - #ifndef ASMJIT_NO_LOGGING +#ifndef ASMJIT_NO_LOGGING if (ASMJIT_UNLIKELY(hasEmitterOption(kOptionLoggingEnabled))) { StringTmp<256> sb; sb.appendFormat(".%s (", dataSizeByPowerTable[Support::ctz(dataSize)].str); @@ -398,7 +400,7 @@ Error BaseAssembler::embedLabelDelta(const Label& label, const Label& base, uint sb.appendString(")\n"); _code->_logger->log(sb); } - #endif +#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()) { @@ -447,10 +449,10 @@ Error BaseAssembler::embedConstPool(const Label& label, const ConstPool& pool) { pool.fill(writer.cursor()); - #ifndef ASMJIT_NO_LOGGING +#ifndef ASMJIT_NO_LOGGING if (ASMJIT_UNLIKELY(hasEmitterOption(kOptionLoggingEnabled))) _code->_logger->logBinary(writer.cursor(), size); - #endif +#endif writer.advance(size); writer.done(this); @@ -466,17 +468,16 @@ Error BaseAssembler::comment(const char* data, size_t size) { if (ASMJIT_UNLIKELY(!_code)) return DebugUtils::errored(kErrorNotInitialized); - #ifndef ASMJIT_NO_LOGGING +#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 +#else + DebugUtils::unused(data, size); +#endif return kErrorOk; } diff --git a/src/asmjit/core/assembler.h b/src/asmjit/core/assembler.h index 3f380c5..fd2c1c3 100644 --- a/src/asmjit/core/assembler.h +++ b/src/asmjit/core/assembler.h @@ -120,7 +120,7 @@ public: ASMJIT_API Error _emitOpArray(uint32_t instId, const Operand_* operands, size_t count) override; protected: - #ifndef ASMJIT_NO_LOGGING +#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); @@ -128,23 +128,17 @@ protected: Error _emitFailed( Error err, uint32_t instId, uint32_t options, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3); - #else +#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); - + DebugUtils::unused(instId, options, o0, o1, o2, o3); resetInstOptions(); resetInlineComment(); return reportError(err); } - #endif +#endif public: //! \} //! \endcond diff --git a/src/asmjit/core/builder.cpp b/src/asmjit/core/builder.cpp index 49397aa..35d7127 100644 --- a/src/asmjit/core/builder.cpp +++ b/src/asmjit/core/builder.cpp @@ -40,9 +40,7 @@ ASMJIT_BEGIN_NAMESPACE class PostponedErrorHandler : public ErrorHandler { public: void handleError(Error err, const char* message, BaseEmitter* origin) override { - ASMJIT_UNUSED(err); - ASMJIT_UNUSED(origin); - + DebugUtils::unused(err, origin); _message.assignString(message); } @@ -128,7 +126,7 @@ InstNode* BaseBuilder::newInstNode(uint32_t instId, uint32_t instOptions, const node = new(node) InstNode(this, instId, instOptions, opCount, opCapacity); node->setOp(0, o0); - for (uint32_t i = opCount; i < opCapacity; i++) node->resetOp(i); + node->resetOps(opCount, opCapacity); return node; } @@ -144,7 +142,7 @@ InstNode* BaseBuilder::newInstNode(uint32_t instId, uint32_t instOptions, const 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); + node->resetOps(opCount, opCapacity); return node; } @@ -161,7 +159,7 @@ InstNode* BaseBuilder::newInstNode(uint32_t instId, uint32_t instOptions, const node->setOp(0, o0); node->setOp(1, o1); node->setOp(2, o2); - for (uint32_t i = opCount; i < opCapacity; i++) node->resetOp(i); + node->resetOps(opCount, opCapacity); return node; } @@ -179,7 +177,7 @@ InstNode* BaseBuilder::newInstNode(uint32_t instId, uint32_t instOptions, const node->setOp(1, o1); node->setOp(2, o2); node->setOp(3, o3); - for (uint32_t i = opCount; i < opCapacity; i++) node->resetOp(i); + node->resetOps(opCount, opCapacity); return node; } @@ -657,8 +655,8 @@ Error BaseBuilder::_emit(uint32_t instId, const Operand_& o0, const Operand_& o1 if (ASMJIT_UNLIKELY(!_code)) return DebugUtils::errored(kErrorNotInitialized); +#ifndef ASMJIT_NO_VALIDATION // Strict validation. - #ifndef ASMJIT_NO_VALIDATION if (hasEmitterOption(kOptionStrictValidation)) { Operand_ opArray[4]; opArray[0].copyFrom(o0); @@ -674,7 +672,7 @@ Error BaseBuilder::_emit(uint32_t instId, const Operand_& o0, const Operand_& o1 return reportError(err); } } - #endif +#endif // Clear options that should never be part of `InstNode`. options &= ~BaseInst::kOptionReserved; @@ -684,10 +682,13 @@ Error BaseBuilder::_emit(uint32_t instId, const Operand_& o0, const Operand_& o1 ASMJIT_ASSERT(opCapacity >= 4); InstNode* node = _allocator.allocT(InstNode::nodeSizeOfOpCapacity(opCapacity)); + const char* comment = inlineComment(); + + resetInstOptions(); + resetInlineComment(); + if (ASMJIT_UNLIKELY(!node)) { - resetInstOptions(); resetExtraReg(); - resetInlineComment(); return reportError(DebugUtils::errored(kErrorOutOfMemory)); } @@ -697,19 +698,13 @@ Error BaseBuilder::_emit(uint32_t instId, const Operand_& o0, const Operand_& o1 node->setOp(1, o1); node->setOp(2, o2); node->setOp(3, o3); + node->resetOps(4, opCapacity); - 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); + resetExtraReg(); return kErrorOk; } @@ -726,8 +721,8 @@ Error BaseBuilder::_emit(uint32_t instId, const Operand_& o0, const Operand_& o1 if (ASMJIT_UNLIKELY(!_code)) return DebugUtils::errored(kErrorNotInitialized); +#ifndef ASMJIT_NO_VALIDATION // Strict validation. - #ifndef ASMJIT_NO_VALIDATION if (hasEmitterOption(kOptionStrictValidation)) { Operand_ opArray[Globals::kMaxOpCount]; opArray[0].copyFrom(o0); @@ -745,7 +740,7 @@ Error BaseBuilder::_emit(uint32_t instId, const Operand_& o0, const Operand_& o1 return reportError(err); } } - #endif +#endif // Clear options that should never be part of `InstNode`. options &= ~BaseInst::kOptionReserved; @@ -755,10 +750,13 @@ Error BaseBuilder::_emit(uint32_t instId, const Operand_& o0, const Operand_& o1 ASMJIT_ASSERT(opCapacity >= opCount); InstNode* node = _allocator.allocT(InstNode::nodeSizeOfOpCapacity(opCapacity)); + const char* comment = inlineComment(); + + resetInstOptions(); + resetInlineComment(); + if (ASMJIT_UNLIKELY(!node)) { - resetInstOptions(); resetExtraReg(); - resetInlineComment(); return reportError(DebugUtils::errored(kErrorOutOfMemory)); } @@ -773,15 +771,11 @@ Error BaseBuilder::_emit(uint32_t instId, const Operand_& o0, const Operand_& o1 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); + resetExtraReg(); return kErrorOk; } diff --git a/src/asmjit/core/builder.h b/src/asmjit/core/builder.h index a375771..c6d3599 100644 --- a/src/asmjit/core/builder.h +++ b/src/asmjit/core/builder.h @@ -61,6 +61,9 @@ class CommentNode; class SentinelNode; class LabelDeltaNode; +// Only used by Compiler infrastructure. +class JumpAnnotation; + // ============================================================================ // [asmjit::BaseBuilder] // ============================================================================ @@ -345,9 +348,9 @@ public: //! \name Logging //! \{ - #ifndef ASMJIT_NO_LOGGING +#ifndef ASMJIT_NO_LOGGING ASMJIT_API Error dump(String& sb, uint32_t flags = 0) const noexcept; - #endif +#endif //! \} @@ -471,6 +474,8 @@ public: // [BaseCompiler] + //! Node is `JumpNode` (acts as InstNode). + kNodeJump = 15, //! Node is `FuncNode` (acts as LabelNode). kNodeFunc = 16, //! Node is `FuncRetNode` (acts as InstNode). @@ -767,6 +772,11 @@ public: _opArray[index].reset(); } + inline void resetOps(uint32_t start, uint32_t end) noexcept { + for (uint32_t i = start; i < end; i++) + _opArray[i].reset(); + } + //! \} //! \name Utilities diff --git a/src/asmjit/core/callconv.cpp b/src/asmjit/core/callconv.cpp index 3c0ac9e..5d915d0 100644 --- a/src/asmjit/core/callconv.cpp +++ b/src/asmjit/core/callconv.cpp @@ -43,15 +43,15 @@ ASMJIT_BEGIN_NAMESPACE ASMJIT_FAVOR_SIZE Error CallConv::init(uint32_t ccId) noexcept { reset(); - #ifdef ASMJIT_BUILD_X86 +#ifdef ASMJIT_BUILD_X86 if (CallConv::isX86Family(ccId)) return x86::CallConvInternal::init(*this, ccId); - #endif +#endif - #ifdef ASMJIT_BUILD_ARM +#ifdef ASMJIT_BUILD_ARM if (CallConv::isArmFamily(ccId)) return arm::CallConvInternal::init(*this, ccId); - #endif +#endif return DebugUtils::errored(kErrorInvalidArgument); } diff --git a/src/asmjit/core/callconv.h b/src/asmjit/core/callconv.h index 9d63b84..f3dc385 100644 --- a/src/asmjit/core/callconv.h +++ b/src/asmjit/core/callconv.h @@ -175,7 +175,7 @@ struct CallConv { // [Host] // ------------------------------------------------------------------------ - #if defined(ASMJIT_DOCGEN) +#if defined(ASMJIT_DOCGEN) //! Default calling convention based on the current C++ compiler's settings. //! @@ -197,31 +197,31 @@ struct CallConv { //! \note If not defined by the host then it's the same as `kIdHostCDecl`. kIdHostFastCall = DETECTED_AT_COMPILE_TIME - #elif ASMJIT_ARCH_X86 == 32 +#elif ASMJIT_ARCH_X86 == 32 kIdHost = kIdX86CDecl, kIdHostCDecl = kIdX86CDecl, kIdHostStdCall = kIdX86StdCall, - #if defined(_MSC_VER) +# if defined(_MSC_VER) kIdHostFastCall = kIdX86MsFastCall, - #elif defined(__GNUC__) +# elif defined(__GNUC__) kIdHostFastCall = kIdX86GccFastCall, - #else +# else kIdHostFastCall = kIdHost, - #endif +# endif kIdHostLightCall2 = kIdX86LightCall2, kIdHostLightCall3 = kIdX86LightCall3, kIdHostLightCall4 = kIdX86LightCall4 - #elif ASMJIT_ARCH_X86 == 64 +#elif ASMJIT_ARCH_X86 == 64 - #if defined(_WIN32) +# if defined(_WIN32) kIdHost = kIdX86Win64, - #else +# else kIdHost = kIdX86SysV64, - #endif +# endif kIdHostCDecl = kIdHost, // Doesn't exist, redirected to host. kIdHostStdCall = kIdHost, // Doesn't exist, redirected to host. @@ -231,26 +231,26 @@ struct CallConv { kIdHostLightCall3 = kIdX64LightCall3, kIdHostLightCall4 = kIdX64LightCall4 - #elif ASMJIT_ARCH_ARM == 32 +#elif ASMJIT_ARCH_ARM == 32 - #if defined(__SOFTFP__) +# if defined(__SOFTFP__) kIdHost = kIdArm32SoftFP, - #else +# else kIdHost = kIdArm32HardFP, - #endif +# 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 +#else kIdHost = kIdNone, kIdHostCDecl = kIdHost, kIdHostStdCall = kIdHost, kIdHostFastCall = kIdHost - #endif +#endif }; //! Strategy used to assign registers to function arguments. diff --git a/src/asmjit/core/codeholder.cpp b/src/asmjit/core/codeholder.cpp index eddd496..93c9a99 100644 --- a/src/asmjit/core/codeholder.cpp +++ b/src/asmjit/core/codeholder.cpp @@ -289,13 +289,13 @@ void CodeHolder::clearEmitterOptions(uint32_t options) noexcept { // ============================================================================ void CodeHolder::setLogger(Logger* logger) noexcept { - #ifndef ASMJIT_NO_LOGGING +#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 +#else + DebugUtils::unused(logger); +#endif } // ============================================================================ @@ -623,7 +623,7 @@ Error CodeHolder::newNamedLabelEntry(LabelEntry** entryOut, const char* name, si 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); + DebugUtils::unused(parentId); uint32_t hashCode = CodeHolder_hashNameAndGetSize(name, nameSize); if (ASMJIT_UNLIKELY(!nameSize)) return 0; diff --git a/src/asmjit/core/compiler.cpp b/src/asmjit/core/compiler.cpp index 1053ee9..13dbf54 100644 --- a/src/asmjit/core/compiler.cpp +++ b/src/asmjit/core/compiler.cpp @@ -46,8 +46,7 @@ class GlobalConstPoolPass : public Pass { GlobalConstPoolPass() noexcept : Pass("GlobalConstPoolPass") {} Error run(Zone* zone, Logger* logger) noexcept override { - ASMJIT_UNUSED(zone); - ASMJIT_UNUSED(logger); + DebugUtils::unused(zone, logger); // Flush the global constant pool. BaseCompiler* compiler = static_cast(_cb); @@ -285,7 +284,7 @@ FuncCallNode* BaseCompiler::addCall(uint32_t instId, const Operand_& o0, const F // [asmjit::BaseCompiler - Vars] // ============================================================================ -static void CodeCompiler_assignGenericName(BaseCompiler* self, VirtReg* vReg) { +static void BaseCompiler_assignGenericName(BaseCompiler* self, VirtReg* vReg) { uint32_t index = unsigned(Operand::virtIdToIndex(vReg->_id)); char buf[64]; @@ -311,12 +310,14 @@ VirtReg* BaseCompiler::newVirtReg(uint32_t typeId, uint32_t signature, const cha vReg = new(vReg) VirtReg(Operand::indexToVirtId(index), signature, size, alignment, typeId); - #ifndef ASMJIT_NO_LOGGING +#ifndef ASMJIT_NO_LOGGING if (name && name[0] != '\0') vReg->_name.setData(&_dataZone, name, SIZE_MAX); else - CodeCompiler_assignGenericName(this, vReg); - #endif + BaseCompiler_assignGenericName(this, vReg); +#else + DebugUtils::unused(name); +#endif _vRegArray.appendUnsafe(vReg); return vReg; @@ -544,10 +545,67 @@ void BaseCompiler::rename(const BaseReg& reg, const char* fmt, ...) { vReg->_name.setData(&_dataZone, buf, SIZE_MAX); } else { - CodeCompiler_assignGenericName(this, vReg); + BaseCompiler_assignGenericName(this, vReg); } } +// ============================================================================ +// [asmjit::BaseCompiler - Jump Annotations] +// ============================================================================ + +JumpNode* BaseCompiler::newJumpNode(uint32_t instId, uint32_t instOptions, const Operand_& o0, JumpAnnotation* annotation) noexcept { + uint32_t opCount = 1; + JumpNode* node = _allocator.allocT(); + if (ASMJIT_UNLIKELY(!node)) + return nullptr; + + node = new(node) JumpNode(this, instId, instOptions, opCount, annotation); + node->setOp(0, o0); + node->resetOps(opCount, JumpNode::kBaseOpCapacity); + return node; +} + +Error BaseCompiler::emitAnnotatedJump(uint32_t instId, const Operand_& o0, JumpAnnotation* annotation) { + uint32_t options = instOptions() | globalInstOptions(); + const char* comment = inlineComment(); + + JumpNode* node = newJumpNode(instId, options, o0, annotation); + + resetInstOptions(); + resetInlineComment(); + + if (ASMJIT_UNLIKELY(!node)) { + resetExtraReg(); + return reportError(DebugUtils::errored(kErrorOutOfMemory)); + } + + node->setExtraReg(extraReg()); + if (comment) + node->setInlineComment(static_cast(_dataZone.dup(comment, strlen(comment), true))); + + addNode(node); + resetExtraReg(); + return kErrorOk; +} + +JumpAnnotation* BaseCompiler::newJumpAnnotation() { + if (_jumpAnnotations.grow(&_allocator, 1) != kErrorOk) { + reportError(DebugUtils::errored(kErrorOutOfMemory)); + return nullptr; + } + + uint32_t id = _jumpAnnotations.size(); + JumpAnnotation* jumpAnnotation = _allocator.newT(this, id); + + if (!jumpAnnotation) { + reportError(DebugUtils::errored(kErrorOutOfMemory)); + return nullptr; + } + + _jumpAnnotations.appendUnsafe(jumpAnnotation); + return jumpAnnotation; +} + // ============================================================================ // [asmjit::BaseCompiler - Events] // ============================================================================ diff --git a/src/asmjit/core/compiler.h b/src/asmjit/core/compiler.h index 83eb992..32b2a8b 100644 --- a/src/asmjit/core/compiler.h +++ b/src/asmjit/core/compiler.h @@ -46,6 +46,9 @@ ASMJIT_BEGIN_NAMESPACE struct RATiedReg; class RAWorkReg; +class JumpAnnotation; + +class JumpNode; class FuncNode; class FuncRetNode; class FuncCallNode; @@ -208,6 +211,8 @@ public: Zone _vRegZone; //! Stores array of `VirtReg` pointers. ZoneVector _vRegArray; + //! Stores jump annotations. + ZoneVector _jumpAnnotations; //! Local constant pool, flushed at the end of each function. ConstPoolNode* _localConstPool; @@ -326,9 +331,26 @@ public: //! \} + //! \name Jump Annotations + //! \{ + + inline const ZoneVector& jumpAnnotations() const noexcept { + return _jumpAnnotations; + } + + ASMJIT_API JumpNode* newJumpNode(uint32_t instId, uint32_t instOptions, const Operand_& o0, JumpAnnotation* annotation) noexcept; + ASMJIT_API Error emitAnnotatedJump(uint32_t instId, const Operand_& o0, JumpAnnotation* annotation); + + //! Returns a new `JumpAnnotation` instance, which can be used to aggregate + //! possible targets of a jump where the target is not a label, for example + //! to implement jump tables. + ASMJIT_API JumpAnnotation* newJumpAnnotation(); + + //! \} + // TODO: These should be removed - inline void alloc(BaseReg& reg) { ASMJIT_UNUSED(reg); } - inline void spill(BaseReg& reg) { ASMJIT_UNUSED(reg); } + inline void alloc(BaseReg& reg) { DebugUtils::unused(reg); } + inline void spill(BaseReg& reg) { DebugUtils::unused(reg); } //! \name Events //! \{ @@ -339,6 +361,70 @@ public: //! \} }; +// ============================================================================ +// [asmjit::JumpAnnotation] +// ============================================================================ + +class JumpAnnotation { +public: + ASMJIT_NONCOPYABLE(JumpAnnotation) + + BaseCompiler* _compiler; + uint32_t _annotationId; + ZoneVector _labelIds; + + inline JumpAnnotation(BaseCompiler* compiler, uint32_t annotationId) noexcept + : _compiler(compiler), + _annotationId(annotationId) {} + + inline BaseCompiler* compiler() const noexcept { return _compiler; } + inline uint32_t annotationId() const noexcept { return _annotationId; } + const ZoneVector& labelIds() const noexcept { return _labelIds; } + + inline bool hasLabel(const Label& label) const noexcept { return hasLabelId(label.id()); } + inline bool hasLabelId(uint32_t labelId) const noexcept { return _labelIds.contains(labelId); } + + inline Error addLabel(const Label& label) noexcept { return addLabelId(label.id()); } + inline Error addLabelId(uint32_t labelId) noexcept { return _labelIds.append(&_compiler->_allocator, labelId); } +}; + +// ============================================================================ +// [asmjit::JumpNode] +// ============================================================================ + +//! Jump instruction with \ref JumpAnnotation. +//! +//! \note This node should be only used to represent jump where the jump target +//! cannot be deduced by examining instruction operands. For example if the jump +//! target is register or memory location. This pattern is often used to perform +//! indirect jumps that use jump table, e.g. to implement `switch{}` statement. +class JumpNode : public InstNode { +public: + ASMJIT_NONCOPYABLE(JumpNode) + + JumpAnnotation* _annotation; + + //! \name Construction & Destruction + //! \{ + + ASMJIT_INLINE JumpNode(BaseCompiler* cc, uint32_t instId, uint32_t options, uint32_t opCount, JumpAnnotation* annotation) noexcept + : InstNode(cc, instId, options, opCount, kBaseOpCapacity), + _annotation(annotation) { + setType(kNodeJump); + } + + //! \} + + //! \name Accessors + //! \{ + + inline bool hasAnnotation() const noexcept { return _annotation != nullptr; } + inline JumpAnnotation* annotation() const noexcept { return _annotation; } + inline void setAnnotation(JumpAnnotation* annotation) noexcept { _annotation = annotation; } + + //! \} +}; + // ============================================================================ // [asmjit::FuncNode] // ============================================================================ diff --git a/src/asmjit/core/cpuinfo.cpp b/src/asmjit/core/cpuinfo.cpp index ca8a20f..edc7d17 100644 --- a/src/asmjit/core/cpuinfo.cpp +++ b/src/asmjit/core/cpuinfo.cpp @@ -78,13 +78,13 @@ const CpuInfo& CpuInfo::host() noexcept { if (!cpuInfoInitialized) { CpuInfo cpuInfoLocal; - #if defined(ASMJIT_BUILD_X86) && ASMJIT_ARCH_X86 +#if defined(ASMJIT_BUILD_X86) && ASMJIT_ARCH_X86 x86::detectCpu(cpuInfoLocal); - #endif +#endif - #if defined(ASMJIT_BUILD_ARM) && ASMJIT_ARCH_ARM +#if defined(ASMJIT_BUILD_ARM) && ASMJIT_ARCH_ARM arm::detectCpu(cpuInfoLocal); - #endif +#endif cpuInfoLocal._hwThreadCount = detectHWThreadCount(); cpuInfoGlobal = cpuInfoLocal; diff --git a/src/asmjit/core/emitter.cpp b/src/asmjit/core/emitter.cpp index 5c6c8a6..ebf8c17 100644 --- a/src/asmjit/core/emitter.cpp +++ b/src/asmjit/core/emitter.cpp @@ -136,15 +136,15 @@ ASMJIT_FAVOR_SIZE Error BaseEmitter::emitProlog(const FuncFrame& frame) { if (ASMJIT_UNLIKELY(!_code)) return DebugUtils::errored(kErrorNotInitialized); - #ifdef ASMJIT_BUILD_X86 +#ifdef ASMJIT_BUILD_X86 if (archInfo().isX86Family()) return x86::X86Internal::emitProlog(as(), frame); - #endif +#endif - #ifdef ASMJIT_BUILD_ARM +#ifdef ASMJIT_BUILD_ARM if (archInfo().isArmFamily()) return arm::ArmInternal::emitProlog(as(), frame); - #endif +#endif return DebugUtils::errored(kErrorInvalidArch); } @@ -153,15 +153,15 @@ ASMJIT_FAVOR_SIZE Error BaseEmitter::emitEpilog(const FuncFrame& frame) { if (ASMJIT_UNLIKELY(!_code)) return DebugUtils::errored(kErrorNotInitialized); - #ifdef ASMJIT_BUILD_X86 +#ifdef ASMJIT_BUILD_X86 if (archInfo().isX86Family()) return x86::X86Internal::emitEpilog(as(), frame); - #endif +#endif - #ifdef ASMJIT_BUILD_ARM +#ifdef ASMJIT_BUILD_ARM if (archInfo().isArmFamily()) return arm::ArmInternal::emitEpilog(as(), frame); - #endif +#endif return DebugUtils::errored(kErrorInvalidArch); } @@ -170,15 +170,15 @@ ASMJIT_FAVOR_SIZE Error BaseEmitter::emitArgsAssignment(const FuncFrame& frame, if (ASMJIT_UNLIKELY(!_code)) return DebugUtils::errored(kErrorNotInitialized); - #ifdef ASMJIT_BUILD_X86 +#ifdef ASMJIT_BUILD_X86 if (archInfo().isX86Family()) return x86::X86Internal::emitArgsAssignment(as(), frame, args); - #endif +#endif - #ifdef ASMJIT_BUILD_ARM +#ifdef ASMJIT_BUILD_ARM if (archInfo().isArmFamily()) return arm::ArmInternal::emitArgsAssignment(as(), frame, args); - #endif +#endif return DebugUtils::errored(kErrorInvalidArch); } @@ -191,7 +191,7 @@ Error BaseEmitter::commentf(const char* fmt, ...) { if (ASMJIT_UNLIKELY(!_code)) return DebugUtils::errored(kErrorNotInitialized); - #ifndef ASMJIT_NO_LOGGING +#ifndef ASMJIT_NO_LOGGING StringTmp<1024> sb; va_list ap; @@ -203,17 +203,17 @@ Error BaseEmitter::commentf(const char* fmt, ...) { return err; return comment(sb.data(), sb.size()); - #else - ASMJIT_UNUSED(fmt); +#else + DebugUtils::unused(fmt); return kErrorOk; - #endif +#endif } Error BaseEmitter::commentv(const char* fmt, va_list ap) { if (ASMJIT_UNLIKELY(!_code)) return DebugUtils::errored(kErrorNotInitialized); - #ifndef ASMJIT_NO_LOGGING +#ifndef ASMJIT_NO_LOGGING StringTmp<1024> sb; Error err = sb.appendVFormat(fmt, ap); @@ -221,11 +221,10 @@ Error BaseEmitter::commentv(const char* fmt, va_list ap) { return err; return comment(sb.data(), sb.size()); - #else - ASMJIT_UNUSED(fmt); - ASMJIT_UNUSED(ap); +#else + DebugUtils::unused(fmt, ap); return kErrorOk; - #endif +#endif } // ============================================================================ @@ -242,7 +241,7 @@ Error BaseEmitter::onAttach(CodeHolder* code) noexcept { } Error BaseEmitter::onDetach(CodeHolder* code) noexcept { - ASMJIT_UNUSED(code); + DebugUtils::unused(code); _flags = 0; _emitterOptions = 0; diff --git a/src/asmjit/core/func.cpp b/src/asmjit/core/func.cpp index 9b1ad31..79eab2e 100644 --- a/src/asmjit/core/func.cpp +++ b/src/asmjit/core/func.cpp @@ -69,15 +69,15 @@ ASMJIT_FAVOR_SIZE Error FuncDetail::init(const FuncSignature& sign) { _retCount = 1; } - #ifdef ASMJIT_BUILD_X86 +#ifdef ASMJIT_BUILD_X86 if (CallConv::isX86Family(ccId)) return x86::X86Internal::initFuncDetail(*this, sign, gpSize); - #endif +#endif - #ifdef ASMJIT_BUILD_ARM +#ifdef ASMJIT_BUILD_ARM if (CallConv::isArmFamily(ccId)) return arm::ArmInternal::initFuncDetail(*this, sign, gpSize); - #endif +#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. @@ -91,29 +91,29 @@ ASMJIT_FAVOR_SIZE Error FuncDetail::init(const FuncSignature& sign) { ASMJIT_FAVOR_SIZE Error FuncFrame::init(const FuncDetail& func) noexcept { uint32_t ccId = func.callConv().id(); - #ifdef ASMJIT_BUILD_X86 +#ifdef ASMJIT_BUILD_X86 if (CallConv::isX86Family(ccId)) return x86::X86Internal::initFuncFrame(*this, func); - #endif +#endif - #ifdef ASMJIT_BUILD_ARM +#ifdef ASMJIT_BUILD_ARM if (CallConv::isArmFamily(ccId)) return arm::ArmInternal::initFuncFrame(*this, func); - #endif +#endif return DebugUtils::errored(kErrorInvalidArgument); } ASMJIT_FAVOR_SIZE Error FuncFrame::finalize() noexcept { - #ifdef ASMJIT_BUILD_X86 +#ifdef ASMJIT_BUILD_X86 if (ArchInfo::isX86Family(archId())) return x86::X86Internal::finalizeFuncFrame(*this); - #endif +#endif - #ifdef ASMJIT_BUILD_ARM +#ifdef ASMJIT_BUILD_ARM if (ArchInfo::isArmFamily(archId())) return arm::ArmInternal::finalizeFuncFrame(*this); - #endif +#endif return DebugUtils::errored(kErrorInvalidArgument); } @@ -128,15 +128,15 @@ ASMJIT_FAVOR_SIZE Error FuncArgsAssignment::updateFuncFrame(FuncFrame& frame) co uint32_t ccId = func->callConv().id(); - #ifdef ASMJIT_BUILD_X86 +#ifdef ASMJIT_BUILD_X86 if (CallConv::isX86Family(ccId)) return x86::X86Internal::argsToFuncFrame(*this, frame); - #endif +#endif - #ifdef ASMJIT_BUILD_ARM +#ifdef ASMJIT_BUILD_ARM if (CallConv::isArmFamily(ccId)) return arm::ArmInternal::argsToFuncFrame(*this, frame); - #endif +#endif return DebugUtils::errored(kErrorInvalidArch); } diff --git a/src/asmjit/core/globals.cpp b/src/asmjit/core/globals.cpp index 1fa3bd7..426fce8 100644 --- a/src/asmjit/core/globals.cpp +++ b/src/asmjit/core/globals.cpp @@ -103,7 +103,7 @@ ASMJIT_FAVOR_SIZE const char* DebugUtils::errorAsString(Error err) noexcept { "Unknown error\0"; return Support::findPackedString(errorMessages, Support::min(err, kErrorCount)); #else - ASMJIT_UNUSED(err); + DebugUtils::unused(err); static const char noMessage[] = ""; return noMessage; #endif diff --git a/src/asmjit/core/globals.h b/src/asmjit/core/globals.h index 3402f37..6373b7e 100644 --- a/src/asmjit/core/globals.h +++ b/src/asmjit/core/globals.h @@ -370,6 +370,10 @@ static inline void* func_as_ptr(Func func) noexcept { return Support::ptr_cast_i //! Debugging utilities. namespace DebugUtils { +//! Used to silence warnings about unused arguments or variables. +template +static ASMJIT_INLINE void unused(Args&&...) noexcept {} + //! Returns the error `err` passed. //! //! Provided for debugging purposes. Putting a breakpoint inside `errored` can diff --git a/src/asmjit/core/inst.h b/src/asmjit/core/inst.h index 59a9f73..9167182 100644 --- a/src/asmjit/core/inst.h +++ b/src/asmjit/core/inst.h @@ -296,9 +296,13 @@ public: kOptionReserved = 0x00000001u, //! Used only by Assembler to mark that `_op4` and `_op5` are used (internal). + //! + //! TODO: This should be removed in the future. kOptionOp4Op5Used = 0x00000002u, //! Prevents following a jump during compilation (BaseCompiler). + //! + //! TODO: This should be renamed to kOptionNoReturn. kOptionUnfollow = 0x00000010u, //! Overwrite the destination operand(s) (BaseCompiler). diff --git a/src/asmjit/core/jitallocator.cpp b/src/asmjit/core/jitallocator.cpp index 5547239..a8ca0c3 100644 --- a/src/asmjit/core/jitallocator.cpp +++ b/src/asmjit/core/jitallocator.cpp @@ -470,7 +470,7 @@ static JitAllocatorBlock* JitAllocatorImpl_newBlock(JitAllocatorPrivateImpl* imp } static void JitAllocatorImpl_deleteBlock(JitAllocatorPrivateImpl* impl, JitAllocatorBlock* block) noexcept { - ASMJIT_UNUSED(impl); + DebugUtils::unused(impl); if (block->flags & JitAllocatorBlock::kFlagDualMapped) VirtMem::releaseDualMapping(&block->mapping, block->blockSize); diff --git a/src/asmjit/core/jitruntime.cpp b/src/asmjit/core/jitruntime.cpp index 0f12e69..625cc3d 100644 --- a/src/asmjit/core/jitruntime.cpp +++ b/src/asmjit/core/jitruntime.cpp @@ -39,8 +39,7 @@ static inline void JitRuntime_flushInstructionCache(const void* p, size_t size) // Windows has a built-in support in `kernel32.dll`. ::FlushInstructionCache(::GetCurrentProcess(), p, size); #else - ASMJIT_UNUSED(p); - ASMJIT_UNUSED(size); + DebugUtils::unused(p, size); #endif } diff --git a/src/asmjit/core/logging.cpp b/src/asmjit/core/logging.cpp index 40e9aaf..7e10af2 100644 --- a/src/asmjit/core/logging.cpp +++ b/src/asmjit/core/logging.cpp @@ -146,7 +146,7 @@ Error Logging::formatLabel( const BaseEmitter* emitter, uint32_t labelId) noexcept { - ASMJIT_UNUSED(flags); + DebugUtils::unused(flags); const LabelEntry* le = emitter->code()->labelEntry(labelId); if (ASMJIT_UNLIKELY(!le)) @@ -181,15 +181,15 @@ Error Logging::formatRegister( uint32_t regType, uint32_t regId) noexcept { - #ifdef ASMJIT_BUILD_X86 +#ifdef ASMJIT_BUILD_X86 if (ArchInfo::isX86Family(archId)) return x86::LoggingInternal::formatRegister(sb, flags, emitter, archId, regType, regId); - #endif +#endif - #ifdef ASMJIT_BUILD_ARM +#ifdef ASMJIT_BUILD_ARM if (ArchInfo::isArmFamily(archId)) return arm::LoggingInternal::formatRegister(sb, flags, emitter, archId, regType, regId); - #endif +#endif return kErrorInvalidArch; } @@ -201,15 +201,15 @@ Error Logging::formatOperand( uint32_t archId, const Operand_& op) noexcept { - #ifdef ASMJIT_BUILD_X86 +#ifdef ASMJIT_BUILD_X86 if (ArchInfo::isX86Family(archId)) return x86::LoggingInternal::formatOperand(sb, flags, emitter, archId, op); - #endif +#endif - #ifdef ASMJIT_BUILD_ARM +#ifdef ASMJIT_BUILD_ARM if (ArchInfo::isArmFamily(archId)) return arm::LoggingInternal::formatOperand(sb, flags, emitter, archId, op); - #endif +#endif return kErrorInvalidArch; } @@ -221,15 +221,15 @@ Error Logging::formatInstruction( uint32_t archId, const BaseInst& inst, const Operand_* operands, uint32_t opCount) noexcept { - #ifdef ASMJIT_BUILD_X86 +#ifdef ASMJIT_BUILD_X86 if (ArchInfo::isX86Family(archId)) return x86::LoggingInternal::formatInstruction(sb, flags, emitter, archId, inst, operands, opCount); - #endif +#endif - #ifdef ASMJIT_BUILD_ARM +#ifdef ASMJIT_BUILD_ARM if (ArchInfo::isArmFamily(archId)) return arm::LoggingInternal::formatInstruction(sb, flags, emitter, archId, inst, operands, opCount); - #endif +#endif return kErrorInvalidArch; } @@ -309,12 +309,14 @@ static Error formatFuncRets( if (i) ASMJIT_PROPAGATE(sb.appendString(", ")); ASMJIT_PROPAGATE(formatFuncValue(sb, flags, emitter, fd.ret(i))); - #ifndef ASMJIT_NO_COMPILER +#ifndef ASMJIT_NO_COMPILER if (vRegs) { static const char nullRet[] = ""; ASMJIT_PROPAGATE(sb.appendFormat(" %s", vRegs[i] ? vRegs[i]->name() : nullRet)); } - #endif +#else + DebugUtils::unused(vRegs); +#endif } return kErrorOk; @@ -335,12 +337,14 @@ static Error formatFuncArgs( if (i) ASMJIT_PROPAGATE(sb.appendString(", ")); ASMJIT_PROPAGATE(formatFuncValue(sb, flags, emitter, fd.arg(i))); - #ifndef ASMJIT_NO_COMPILER +#ifndef ASMJIT_NO_COMPILER if (vRegs) { static const char nullArg[] = ""; ASMJIT_PROPAGATE(sb.appendFormat(" %s", vRegs[i] ? vRegs[i]->name() : nullArg)); } - #endif +#else + DebugUtils::unused(vRegs); +#endif } return kErrorOk; @@ -356,7 +360,8 @@ Error Logging::formatNode( ASMJIT_PROPAGATE(sb.appendFormat("<%05u> ", node_->position())); switch (node_->type()) { - case BaseNode::kNodeInst: { + case BaseNode::kNodeInst: + case BaseNode::kNodeJump: { const InstNode* node = node_->as(); ASMJIT_PROPAGATE( Logging::formatInstruction(sb, flags, cb, @@ -437,7 +442,7 @@ Error Logging::formatNode( break; } - #ifndef ASMJIT_NO_COMPILER +#ifndef ASMJIT_NO_COMPILER case BaseNode::kNodeFunc: { const FuncNode* node = node_->as(); @@ -473,7 +478,7 @@ Error Logging::formatNode( node->baseInst(), node->operands(), node->opCount())); break; } - #endif +#endif default: { ASMJIT_PROPAGATE(sb.appendFormat("[User:%u]", node_->type())); diff --git a/src/asmjit/core/logging.h b/src/asmjit/core/logging.h index a098c6b..468e3a1 100644 --- a/src/asmjit/core/logging.h +++ b/src/asmjit/core/logging.h @@ -58,25 +58,25 @@ public: uint8_t _indentation[4]; enum Flags : uint32_t { - //!< Show also binary form of each logged instruction (assembler). + //! Show also binary form of each logged instruction (assembler). kFlagMachineCode = 0x00000001u, - //!< Show a text explanation of some immediate values. + //! Show a text explanation of some immediate values. kFlagExplainImms = 0x00000002u, - //!< Use hexadecimal notation of immediate values. + //! Use hexadecimal notation of immediate values. kFlagHexImms = 0x00000004u, - //!< Use hexadecimal notation of address offsets. + //! Use hexadecimal notation of address offsets. kFlagHexOffsets = 0x00000008u, - //!< Show casts between virtual register types (compiler). + //! Show casts between virtual register types (compiler). kFlagRegCasts = 0x00000010u, - //!< Show positions associated with nodes (compiler). + //! Show positions associated with nodes (compiler). kFlagPositions = 0x00000020u, - //!< Annotate nodes that are lowered by passes. + //! Annotate nodes that are lowered by passes. kFlagAnnotations = 0x00000040u, // TODO: These must go, keep this only for formatting. - //!< Show an additional output from passes. + //! Show an additional output from passes. kFlagDebugPasses = 0x00000080u, - //!< Show an additional output from RA. + //! Show an additional output from RA. kFlagDebugRA = 0x00000100u }; @@ -324,16 +324,16 @@ struct Logging { String& sb, uint32_t typeId) noexcept; - #ifndef ASMJIT_NO_BUILDER +#ifndef ASMJIT_NO_BUILDER ASMJIT_API static Error formatNode( String& sb, uint32_t flags, const BaseBuilder* cb, const BaseNode* node_) noexcept; - #endif +#endif // Only used by AsmJit internals, not available to users. - #ifdef ASMJIT_EXPORTS +#ifdef ASMJIT_EXPORTS enum { // Has to be big to be able to hold all metadata compiler can assign to a // single instruction. @@ -344,7 +344,7 @@ struct Logging { static Error formatLine( String& sb, const uint8_t* binData, size_t binSize, size_t dispSize, size_t immSize, const char* comment) noexcept; - #endif +#endif }; #endif diff --git a/src/asmjit/core/raassignment_p.h b/src/asmjit/core/raassignment_p.h index c1afe8a..2618afd 100644 --- a/src/asmjit/core/raassignment_p.h +++ b/src/asmjit/core/raassignment_p.h @@ -172,7 +172,7 @@ public: 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); + DebugUtils::unused(group); ASMJIT_ASSERT(workId != kWorkNone); ASMJIT_ASSERT(workId < _layout.workCount); return _workToPhysMap->physIds[workId]; @@ -289,15 +289,13 @@ public: } inline void makeClean(uint32_t group, uint32_t workId, uint32_t physId) noexcept { - ASMJIT_UNUSED(workId); - + DebugUtils::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); - + DebugUtils::unused(workId); uint32_t regMask = Support::bitMask(physId); _physToWorkMap->dirty[group] |= regMask; } diff --git a/src/asmjit/core/rabuilders_p.h b/src/asmjit/core/rabuilders_p.h index a24c1ce..6f400ad 100644 --- a/src/asmjit/core/rabuilders_p.h +++ b/src/asmjit/core/rabuilders_p.h @@ -44,8 +44,26 @@ class RACFGBuilder { public: RAPass* _pass; BaseCompiler* _cc; + RABlock* _curBlock; RABlock* _retBlock; + FuncNode* _funcNode; + RARegsStats _blockRegStats; + uint32_t _exitLabelId; + ZoneVector _sharedAssignmentsMap; + + // Only used by logging, it's fine to be here to prevent more #ifdefs... + bool _hasCode; + RABlock* _lastLoggedBlock; + +#ifndef ASMJIT_NO_LOGGING + Logger* _logger; + uint32_t _logFlags; + StringTmp<512> _sb; +#endif + + static constexpr uint32_t kRootIndentation = 2; + static constexpr uint32_t kCodeIndentation = 4; // NOTE: This is a bit hacky. There are some nodes which are processed twice // (see `onBeforeCall()` and `onBeforeRet()`) as they can insert some nodes @@ -57,241 +75,264 @@ public: : _pass(pass), _cc(pass->cc()), _curBlock(nullptr), - _retBlock(nullptr) {} + _retBlock(nullptr), + _funcNode(nullptr), + _blockRegStats{}, + _exitLabelId(Globals::kInvalidId), + _hasCode(false), + _lastLoggedBlock(nullptr) { +#ifndef ASMJIT_NO_LOGGING + _logger = _pass->debugLogger(); + _logFlags = FormatOptions::kFlagPositions; + + if (_logger) + _logFlags |= _logger->flags(); +#endif + } inline BaseCompiler* cc() const noexcept { return _cc; } + // -------------------------------------------------------------------------- + // [Run] + // -------------------------------------------------------------------------- + + //! Called per function by an architecture-specific CFG builder. Error run() noexcept { - #ifndef ASMJIT_NO_LOGGING - Logger* logger = _pass->debugLogger(); - uint32_t flags = FormatOptions::kFlagPositions; - RABlock* lastPrintedBlock = nullptr; - StringTmp<512> sb; - #endif + log("[RAPass::BuildCFG]\n"); + ASMJIT_PROPAGATE(prepare()); - ASMJIT_RA_LOG_FORMAT("[RAPass::BuildCFG]\n"); + logNode(_funcNode, kRootIndentation); + logBlock(_curBlock, kRootIndentation); - 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(); + BaseNode* node = _funcNode->next(); if (ASMJIT_UNLIKELY(!node)) return DebugUtils::errored(kErrorInvalidState); _curBlock->setFirst(node); _curBlock->setLast(node); + RAInstBuilder ib; + ZoneVector blocksWithUnknownJumps; + for (;;) { BaseNode* next = node->next(); ASMJIT_ASSERT(node->position() == 0 || node->position() == kNodePositionDidOnBefore); if (node->isInst()) { + // Instruction | Jump | Invoke | Return + // ------------------------------------ + + // Handle `InstNode`, `FuncCallNode`, and `FuncRetNode`. All of them + // share the same interface that provides operands that have read/write + // semantics. 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); + // Unreachable code has to be removed, we cannot allocate registers + // in such code as we cannot do proper liveness analysis in such case. + 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())); - } + _hasCode = true; - 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()); + if (node->isFuncCall() || node->isFuncRet()) { + 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(); - node->setPosition(kNodePositionDidOnBefore); - node = prev->next(); + if (node->type() == BaseNode::kNodeFuncCall) + ASMJIT_PROPAGATE(static_cast(this)->onBeforeCall(node->as())); + else + ASMJIT_PROPAGATE(static_cast(this)->onBeforeRet(node->as())); - // `onBeforeCall()` and `onBeforeRet()` can only insert instructions. - ASMJIT_ASSERT(node->isInst()); - } + 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()); - // Necessary if something was inserted after `node`, but nothing before. - next = node->next(); + node->setPosition(kNodePositionDidOnBefore); + node = prev->next(); + + // `onBeforeCall()` and `onBeforeRet()` can only insert instructions. + ASMJIT_ASSERT(node->isInst()); } - else { - // Change the position back to its original value. - node->setPosition(0); + + // 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(); + logNode(inst, kCodeIndentation); + + uint32_t controlType = BaseInst::kControlNone; + ib.reset(); + ASMJIT_PROPAGATE(static_cast(this)->onInst(inst, controlType, ib)); + + if (node->isFuncCall()) { + ASMJIT_PROPAGATE(static_cast(this)->onCall(inst->as(), ib)); + } + + if (node->isFuncRet()) { + ASMJIT_PROPAGATE(static_cast(this)->onRet(inst->as(), ib)); + controlType = BaseInst::kControlReturn; + } + + if (controlType == BaseInst::kControlJump) { + uint32_t fixedRegCount = 0; + for (RATiedReg& tiedReg : ib) { + RAWorkReg* workReg = _pass->workRegById(tiedReg.workId()); + if (workReg->group() == BaseReg::kGroupGp) { + uint32_t useId = tiedReg.useId(); + if (useId == BaseReg::kIdBad) { + useId = _pass->_scratchRegIndexes[fixedRegCount++]; + tiedReg.setUseId(useId); + } + _curBlock->addExitScratchGpRegs(Support::bitMask(useId)); } } + } - InstNode* inst = node->as(); - ASMJIT_RA_LOG_COMPLEX({ - sb.clear(); - Logging::formatNode(sb, flags, cc(), node); - logger->logf(" %s\n", sb.data()); - }); + ASMJIT_PROPAGATE(_pass->assignRAInst(inst, _curBlock, ib)); + _blockRegStats.combineWith(ib._stats); - uint32_t controlType = BaseInst::kControlNone; - ib.reset(); - ASMJIT_PROPAGATE(static_cast(this)->onInst(inst, controlType, ib)); + 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 (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; - } - } + if (!(inst->instOptions() & BaseInst::kOptionUnfollow)) { + // Jmp/Jcc/Call/Loop/etc... + uint32_t opCount = inst->opCount(); + const Operand* opArray = inst->operands(); - ASMJIT_PROPAGATE(_pass->assignRAInst(inst, _curBlock, ib)); - blockRegStats.combineWith(ib._stats); + // Cannot jump anywhere without operands. + if (ASMJIT_UNLIKELY(!opCount)) + return DebugUtils::errored(kErrorInvalidState); - 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 (opArray[opCount - 1].isLabel()) { + // Labels are easy for constructing the control flow. + LabelNode* labelNode; + ASMJIT_PROPAGATE(cc()->labelNodeOf(&labelNode, opArray[opCount - 1].as