summaryrefslogtreecommitdiffstatshomepage
path: root/3rdparty/asmjit/src/asmjit/core/func.cpp
diff options
context:
space:
mode:
Diffstat (limited to '3rdparty/asmjit/src/asmjit/core/func.cpp')
-rw-r--r--3rdparty/asmjit/src/asmjit/core/func.cpp323
1 files changed, 240 insertions, 83 deletions
diff --git a/3rdparty/asmjit/src/asmjit/core/func.cpp b/3rdparty/asmjit/src/asmjit/core/func.cpp
index 514ae9db4e1..a8a6d3ab932 100644
--- a/3rdparty/asmjit/src/asmjit/core/func.cpp
+++ b/3rdparty/asmjit/src/asmjit/core/func.cpp
@@ -1,49 +1,49 @@
-// AsmJit - Machine code generation for C++
+// This file is part of AsmJit project <https://asmjit.com>
//
-// * Official AsmJit Home Page: https://asmjit.com
-// * Official Github Repository: https://github.com/asmjit/asmjit
-//
-// Copyright (c) 2008-2020 The AsmJit Authors
-//
-// This software is provided 'as-is', without any express or implied
-// warranty. In no event will the authors be held liable for any damages
-// arising from the use of this software.
-//
-// Permission is granted to anyone to use this software for any purpose,
-// including commercial applications, and to alter it and redistribute it
-// freely, subject to the following restrictions:
-//
-// 1. The origin of this software must not be misrepresented; you must not
-// claim that you wrote the original software. If you use this software
-// in a product, an acknowledgment in the product documentation would be
-// appreciated but is not required.
-// 2. Altered source versions must be plainly marked as such, and must not be
-// misrepresented as being the original software.
-// 3. This notice may not be removed or altered from any source distribution.
+// See asmjit.h or LICENSE.md for license and copyright information
+// SPDX-License-Identifier: Zlib
#include "../core/api-build_p.h"
-#include "../core/arch.h"
+#include "../core/archtraits.h"
#include "../core/func.h"
+#include "../core/operand.h"
#include "../core/type.h"
+#include "../core/funcargscontext_p.h"
-#ifdef ASMJIT_BUILD_X86
- #include "../x86/x86internal_p.h"
- #include "../x86/x86operand.h"
+#if !defined(ASMJIT_NO_X86)
+ #include "../x86/x86func_p.h"
#endif
-#ifdef ASMJIT_BUILD_ARM
- #include "../arm/arminternal_p.h"
- #include "../arm/armoperand.h"
+#if !defined(ASMJIT_NO_AARCH64)
+ #include "../arm/a64func_p.h"
#endif
ASMJIT_BEGIN_NAMESPACE
-// ============================================================================
-// [asmjit::FuncDetail - Init / Reset]
-// ============================================================================
+// CallConv - Initialization & Reset
+// =================================
+
+ASMJIT_FAVOR_SIZE Error CallConv::init(CallConvId ccId, const Environment& environment) noexcept {
+ reset();
+
+#if !defined(ASMJIT_NO_X86)
+ if (environment.isFamilyX86())
+ return x86::FuncInternal::initCallConv(*this, ccId, environment);
+#endif
+
+#if !defined(ASMJIT_NO_AARCH64)
+ if (environment.isFamilyAArch64())
+ return a64::FuncInternal::initCallConv(*this, ccId, environment);
+#endif
+
+ return DebugUtils::errored(kErrorInvalidArgument);
+}
+
+// FuncDetail - Init / Reset
+// =========================
ASMJIT_FAVOR_SIZE Error FuncDetail::init(const FuncSignature& signature, const Environment& environment) noexcept {
- uint32_t ccId = signature.callConv();
+ CallConvId ccId = signature.callConvId();
uint32_t argCount = signature.argCount();
if (ASMJIT_UNLIKELY(argCount > Globals::kMaxFuncArgs))
@@ -53,91 +53,248 @@ ASMJIT_FAVOR_SIZE Error FuncDetail::init(const FuncSignature& signature, const E
ASMJIT_PROPAGATE(cc.init(ccId, environment));
uint32_t registerSize = Environment::registerSizeFromArch(cc.arch());
- uint32_t deabstractDelta = Type::deabstractDeltaOfSize(registerSize);
+ uint32_t deabstractDelta = TypeUtils::deabstractDeltaOfSize(registerSize);
- const uint8_t* args = signature.args();
- for (uint32_t i = 0; i < argCount; i++) {
- FuncValue& arg = _args[i];
- arg.initTypeId(Type::deabstract(args[i], deabstractDelta));
+ const TypeId* signatureArgs = signature.args();
+ for (uint32_t argIndex = 0; argIndex < argCount; argIndex++) {
+ FuncValuePack& argPack = _args[argIndex];
+ argPack[0].initTypeId(TypeUtils::deabstract(signatureArgs[argIndex], deabstractDelta));
}
+
_argCount = uint8_t(argCount);
_vaIndex = uint8_t(signature.vaIndex());
- uint32_t ret = signature.ret();
- if (ret != Type::kIdVoid) {
- _rets[0].initTypeId(Type::deabstract(ret, deabstractDelta));
- _retCount = 1;
- }
+ TypeId ret = signature.ret();
+ if (ret != TypeId::kVoid)
+ _rets[0].initTypeId(TypeUtils::deabstract(ret, deabstractDelta));
-#ifdef ASMJIT_BUILD_X86
+#if !defined(ASMJIT_NO_X86)
if (environment.isFamilyX86())
- return x86::X86Internal::initFuncDetail(*this, signature, registerSize);
+ return x86::FuncInternal::initFuncDetail(*this, signature, registerSize);
#endif
-#ifdef ASMJIT_BUILD_ARM
- if (environment.isFamilyARM())
- return arm::ArmInternal::initFuncDetail(*this, signature, registerSize);
+#if !defined(ASMJIT_NO_AARCH64)
+ if (environment.isFamilyAArch64())
+ return a64::FuncInternal::initFuncDetail(*this, signature);
#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.
+ // 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]
-// ============================================================================
+// FuncFrame - Init
+// ================
ASMJIT_FAVOR_SIZE Error FuncFrame::init(const FuncDetail& func) noexcept {
-#ifdef ASMJIT_BUILD_X86
- if (Environment::isFamilyX86(func.callConv().arch()))
- return x86::X86Internal::initFuncFrame(*this, func);
-#endif
+ Arch arch = func.callConv().arch();
+ if (!Environment::isValidArch(arch))
+ return DebugUtils::errored(kErrorInvalidArch);
-#ifdef ASMJIT_BUILD_ARM
- if (Environment::isFamilyARM(func.callConv().arch()))
- return arm::ArmInternal::initFuncFrame(*this, func);
-#endif
+ const ArchTraits& archTraits = ArchTraits::byArch(arch);
- return DebugUtils::errored(kErrorInvalidArgument);
+ // Initializing FuncFrame means making a copy of some properties of `func`. Properties like `_localStackSize` will
+ // be set by the user before the frame is finalized.
+ reset();
+
+ _arch = arch;
+ _spRegId = uint8_t(archTraits.spRegId());
+ _saRegId = uint8_t(BaseReg::kIdBad);
+
+ uint32_t naturalStackAlignment = func.callConv().naturalStackAlignment();
+ uint32_t minDynamicAlignment = Support::max<uint32_t>(naturalStackAlignment, 16);
+
+ if (minDynamicAlignment == naturalStackAlignment)
+ minDynamicAlignment <<= 1;
+
+ _naturalStackAlignment = uint8_t(naturalStackAlignment);
+ _minDynamicAlignment = uint8_t(minDynamicAlignment);
+ _redZoneSize = uint8_t(func.redZoneSize());
+ _spillZoneSize = uint8_t(func.spillZoneSize());
+ _finalStackAlignment = uint8_t(_naturalStackAlignment);
+
+ if (func.hasFlag(CallConvFlags::kCalleePopsStack)) {
+ _calleeStackCleanup = uint16_t(func.argStackSize());
+ }
+
+ // Initial masks of dirty and preserved registers.
+ for (RegGroup group : RegGroupVirtValues{}) {
+ _dirtyRegs[group] = func.usedRegs(group);
+ _preservedRegs[group] = func.preservedRegs(group);
+ }
+
+ // Exclude stack pointer - this register is never included in saved GP regs.
+ _preservedRegs[RegGroup::kGp] &= ~Support::bitMask(archTraits.spRegId());
+
+ // The size and alignment of save/restore area of registers for each virtual register group
+ _saveRestoreRegSize = func.callConv()._saveRestoreRegSize;
+ _saveRestoreAlignment = func.callConv()._saveRestoreAlignment;
+
+ return kErrorOk;
}
+// FuncFrame - Finalize
+// ====================
+
ASMJIT_FAVOR_SIZE Error FuncFrame::finalize() noexcept {
-#ifdef ASMJIT_BUILD_X86
- if (Environment::isFamilyX86(arch()))
- return x86::X86Internal::finalizeFuncFrame(*this);
-#endif
+ if (!Environment::isValidArch(arch()))
+ return DebugUtils::errored(kErrorInvalidArch);
-#ifdef ASMJIT_BUILD_ARM
- if (Environment::isFamilyARM(arch()))
- return arm::ArmInternal::finalizeFuncFrame(*this);
-#endif
+ const ArchTraits& archTraits = ArchTraits::byArch(arch());
- return DebugUtils::errored(kErrorInvalidArgument);
+ uint32_t registerSize = _saveRestoreRegSize[RegGroup::kGp];
+ uint32_t vectorSize = _saveRestoreRegSize[RegGroup::kVec];
+ uint32_t returnAddressSize = archTraits.hasLinkReg() ? 0u : registerSize;
+
+ // The final stack alignment must be updated accordingly to call and local stack alignments.
+ uint32_t stackAlignment = _finalStackAlignment;
+ ASMJIT_ASSERT(stackAlignment == Support::max(_naturalStackAlignment,
+ _callStackAlignment,
+ _localStackAlignment));
+
+ bool hasFP = hasPreservedFP();
+ bool hasDA = hasDynamicAlignment();
+
+ uint32_t kSp = archTraits.spRegId();
+ uint32_t kFp = archTraits.fpRegId();
+ uint32_t kLr = archTraits.linkRegId();
+
+ // Make frame pointer dirty if the function uses it.
+ if (hasFP) {
+ _dirtyRegs[RegGroup::kGp] |= Support::bitMask(kFp);
+
+ // Currently required by ARM, if this works differently across architectures we would have to generalize most
+ // likely in CallConv.
+ if (kLr != BaseReg::kIdBad)
+ _dirtyRegs[RegGroup::kGp] |= Support::bitMask(kLr);
+ }
+
+ // These two are identical if the function doesn't align its stack dynamically.
+ uint32_t saRegId = _saRegId;
+ if (saRegId == BaseReg::kIdBad)
+ saRegId = kSp;
+
+ // Fix stack arguments base-register from SP to FP in case it was not picked before and the function performs
+ // dynamic stack alignment.
+ if (hasDA && saRegId == kSp)
+ saRegId = kFp;
+
+ // Mark as dirty any register but SP if used as SA pointer.
+ if (saRegId != kSp)
+ _dirtyRegs[RegGroup::kGp] |= Support::bitMask(saRegId);
+
+ _spRegId = uint8_t(kSp);
+ _saRegId = uint8_t(saRegId);
+
+ // Setup stack size used to save preserved registers.
+ uint32_t saveRestoreSizes[2] {};
+ for (RegGroup group : RegGroupVirtValues{})
+ saveRestoreSizes[size_t(!archTraits.hasInstPushPop(group))]
+ += Support::alignUp(Support::popcnt(savedRegs(group)) * saveRestoreRegSize(group), saveRestoreAlignment(group));
+
+ _pushPopSaveSize = uint16_t(saveRestoreSizes[0]);
+ _extraRegSaveSize = uint16_t(saveRestoreSizes[1]);
+
+ uint32_t v = 0; // The beginning of the stack frame relative to SP after prolog.
+ v += callStackSize(); // Count 'callStackSize' <- This is used to call functions.
+ v = Support::alignUp(v, stackAlignment); // Align to function's stack alignment.
+
+ _localStackOffset = v; // Store 'localStackOffset' <- Function's local stack starts here.
+ v += localStackSize(); // Count 'localStackSize' <- Function's local stack ends here.
+
+ // If the function's stack must be aligned, calculate the alignment necessary to store vector registers, and set
+ // `FuncAttributes::kAlignedVecSR` to inform PEI that it can use instructions that perform aligned stores/loads.
+ if (stackAlignment >= vectorSize && _extraRegSaveSize) {
+ addAttributes(FuncAttributes::kAlignedVecSR);
+ v = Support::alignUp(v, vectorSize); // Align 'extraRegSaveOffset'.
+ }
+
+ _extraRegSaveOffset = v; // Store 'extraRegSaveOffset' <- Non-GP save/restore starts here.
+ v += _extraRegSaveSize; // Count 'extraRegSaveSize' <- Non-GP save/restore ends here.
+
+ // Calculate if dynamic alignment (DA) slot (stored as offset relative to SP) is required and its offset.
+ if (hasDA && !hasFP) {
+ _daOffset = v; // Store 'daOffset' <- DA pointer would be stored here.
+ v += registerSize; // Count 'daOffset'.
+ }
+ else {
+ _daOffset = FuncFrame::kTagInvalidOffset;
+ }
+
+ // Link Register
+ // -------------
+ //
+ // The stack is aligned after the function call as the return address is stored in a link register. Some
+ // architectures may require to always have aligned stack after PUSH/POP operation, which is represented
+ // by ArchTraits::stackAlignmentConstraint().
+ //
+ // No Link Register (X86/X64)
+ // --------------------------
+ //
+ // The return address should be stored after GP save/restore regs. It has the same size as `registerSize`
+ // (basically the native register/pointer size). We don't adjust it now as `v` now contains the exact size
+ // that the function requires to adjust (call frame + stack frame, vec stack size). The stack (if we consider
+ // this size) is misaligned now, as it's always aligned before the function call - when `call()` is executed
+ // it pushes the current EIP|RIP onto the stack, and misaligns it by 12 or 8 bytes (depending on the
+ // architecture). So count number of bytes needed to align it up to the function's CallFrame (the beginning).
+ if (v || hasFuncCalls() || !returnAddressSize)
+ v += Support::alignUpDiff(v + pushPopSaveSize() + returnAddressSize, stackAlignment);
+
+ _pushPopSaveOffset = v; // Store 'pushPopSaveOffset' <- Function's push/pop save/restore starts here.
+ _stackAdjustment = v; // Store 'stackAdjustment' <- SA used by 'add SP, SA' and 'sub SP, SA'.
+ v += _pushPopSaveSize; // Count 'pushPopSaveSize' <- Function's push/pop save/restore ends here.
+ _finalStackSize = v; // Store 'finalStackSize' <- Final stack used by the function.
+
+ if (!archTraits.hasLinkReg())
+ v += registerSize; // Count 'ReturnAddress' <- As CALL pushes onto stack.
+
+ // If the function performs dynamic stack alignment then the stack-adjustment must be aligned.
+ if (hasDA)
+ _stackAdjustment = Support::alignUp(_stackAdjustment, stackAlignment);
+
+ // Calculate where the function arguments start relative to SP.
+ _saOffsetFromSP = hasDA ? FuncFrame::kTagInvalidOffset : v;
+
+ // Calculate where the function arguments start relative to FP or user-provided register.
+ _saOffsetFromSA = hasFP ? returnAddressSize + registerSize // Return address + frame pointer.
+ : returnAddressSize + _pushPopSaveSize; // Return address + all push/pop regs.
+
+ return kErrorOk;
}
-// ============================================================================
-// [asmjit::FuncArgsAssignment]
-// ============================================================================
+// FuncArgsAssignment - UpdateFuncFrame
+// ====================================
ASMJIT_FAVOR_SIZE Error FuncArgsAssignment::updateFuncFrame(FuncFrame& frame) const noexcept {
- uint32_t arch = frame.arch();
+ Arch arch = frame.arch();
const FuncDetail* func = funcDetail();
if (!func)
return DebugUtils::errored(kErrorInvalidState);
-#ifdef ASMJIT_BUILD_X86
- if (Environment::isFamilyX86(arch))
- return x86::X86Internal::argsToFuncFrame(*this, frame);
-#endif
+ RAConstraints constraints;
+ ASMJIT_PROPAGATE(constraints.init(arch));
-#ifdef ASMJIT_BUILD_ARM
- if (Environment::isFamilyARM(arch))
- return arm::ArmInternal::argsToFuncFrame(*this, frame);
-#endif
+ FuncArgsContext ctx;
+ ASMJIT_PROPAGATE(ctx.initWorkData(frame, *this, &constraints));
+ ASMJIT_PROPAGATE(ctx.markDstRegsDirty(frame));
+ ASMJIT_PROPAGATE(ctx.markScratchRegs(frame));
+ ASMJIT_PROPAGATE(ctx.markStackArgsReg(frame));
+ return kErrorOk;
+}
+
+// Func API - Tests
+// ================
- return DebugUtils::errored(kErrorInvalidArch);
+#if defined(ASMJIT_TEST)
+UNIT(func_signature) {
+ FuncSignature signature;
+ signature.setRetT<int8_t>();
+ signature.addArgT<int16_t>();
+ signature.addArg(TypeId::kInt32);
+
+ EXPECT_EQ(signature, FuncSignature::build<int8_t, int16_t, int32_t>());
}
+#endif
ASMJIT_END_NAMESPACE