mirror of
https://github.com/asmjit/asmjit.git
synced 2025-12-17 12:34:35 +03:00
[Bug] Fixed AArch64 calling convention handling on Apple platforms
The standard AArch64 calling convention specifies that the minimum size of each argument passed via stack is 8 bytes. However, Apple doesn't follow this convention and only aligns stack to 8 bytes when an argument has 8 bytes or more. This makes it incompatible with the standard calling convention as two consecutive 32-bit integers would occupy only 8 bytes instead of 16 when passed via stack.
This commit is contained in:
@@ -41,6 +41,7 @@ static RegType regTypeFromFpOrVecTypeId(TypeId typeId) noexcept {
|
|||||||
|
|
||||||
ASMJIT_FAVOR_SIZE Error initCallConv(CallConv& cc, CallConvId ccId, const Environment& environment) noexcept {
|
ASMJIT_FAVOR_SIZE Error initCallConv(CallConv& cc, CallConvId ccId, const Environment& environment) noexcept {
|
||||||
cc.setArch(environment.arch());
|
cc.setArch(environment.arch());
|
||||||
|
cc.setStrategy(environment.isDarwin() ? CallConvStrategy::kAArch64Apple : CallConvStrategy::kDefault);
|
||||||
|
|
||||||
cc.setSaveRestoreRegSize(RegGroup::kGp, 8);
|
cc.setSaveRestoreRegSize(RegGroup::kGp, 8);
|
||||||
cc.setSaveRestoreRegSize(RegGroup::kVec, 8);
|
cc.setSaveRestoreRegSize(RegGroup::kVec, 8);
|
||||||
@@ -68,7 +69,7 @@ ASMJIT_FAVOR_SIZE Error initCallConv(CallConv& cc, CallConvId ccId, const Enviro
|
|||||||
return kErrorOk;
|
return kErrorOk;
|
||||||
}
|
}
|
||||||
|
|
||||||
ASMJIT_FAVOR_SIZE Error initFuncDetail(FuncDetail& func, const FuncSignature& signature, uint32_t registerSize) noexcept {
|
ASMJIT_FAVOR_SIZE Error initFuncDetail(FuncDetail& func, const FuncSignature& signature) noexcept {
|
||||||
DebugUtils::unused(signature);
|
DebugUtils::unused(signature);
|
||||||
|
|
||||||
const CallConv& cc = func.callConv();
|
const CallConv& cc = func.callConv();
|
||||||
@@ -77,6 +78,13 @@ ASMJIT_FAVOR_SIZE Error initFuncDetail(FuncDetail& func, const FuncSignature& si
|
|||||||
uint32_t i;
|
uint32_t i;
|
||||||
uint32_t argCount = func.argCount();
|
uint32_t argCount = func.argCount();
|
||||||
|
|
||||||
|
// Minimum stack size of a single argument passed via stack. The standard AArch64 calling convention
|
||||||
|
// specifies 8 bytes, so each function argument would occupy at least 8 bytes even if it needs less.
|
||||||
|
// However, Apple has decided to not follow this rule and function argument can occupy less, for
|
||||||
|
// example two consecutive 32-bit arguments would occupy 8 bytes total, instead of 16 as specified
|
||||||
|
// by ARM.
|
||||||
|
uint32_t minStackArgSize = cc.strategy() == CallConvStrategy::kAArch64Apple ? 4u : 8u;
|
||||||
|
|
||||||
if (func.hasRet()) {
|
if (func.hasRet()) {
|
||||||
for (uint32_t valueIndex = 0; valueIndex < Globals::kMaxValuePack; valueIndex++) {
|
for (uint32_t valueIndex = 0; valueIndex < Globals::kMaxValuePack; valueIndex++) {
|
||||||
TypeId typeId = func._rets[valueIndex].typeId();
|
TypeId typeId = func._rets[valueIndex].typeId();
|
||||||
@@ -119,7 +127,8 @@ ASMJIT_FAVOR_SIZE Error initFuncDetail(FuncDetail& func, const FuncSignature& si
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch (cc.strategy()) {
|
switch (cc.strategy()) {
|
||||||
case CallConvStrategy::kDefault: {
|
case CallConvStrategy::kDefault:
|
||||||
|
case CallConvStrategy::kAArch64Apple: {
|
||||||
uint32_t gpzPos = 0;
|
uint32_t gpzPos = 0;
|
||||||
uint32_t vecPos = 0;
|
uint32_t vecPos = 0;
|
||||||
|
|
||||||
@@ -140,7 +149,9 @@ ASMJIT_FAVOR_SIZE Error initFuncDetail(FuncDetail& func, const FuncSignature& si
|
|||||||
gpzPos++;
|
gpzPos++;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
uint32_t size = Support::max<uint32_t>(TypeUtils::sizeOf(typeId), registerSize);
|
uint32_t size = Support::max<uint32_t>(TypeUtils::sizeOf(typeId), minStackArgSize);
|
||||||
|
if (size >= 8)
|
||||||
|
stackOffset = Support::alignUp(stackOffset, 8);
|
||||||
arg.assignStackOffset(int32_t(stackOffset));
|
arg.assignStackOffset(int32_t(stackOffset));
|
||||||
stackOffset += size;
|
stackOffset += size;
|
||||||
}
|
}
|
||||||
@@ -164,7 +175,9 @@ ASMJIT_FAVOR_SIZE Error initFuncDetail(FuncDetail& func, const FuncSignature& si
|
|||||||
vecPos++;
|
vecPos++;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
uint32_t size = TypeUtils::sizeOf(typeId);
|
uint32_t size = Support::max<uint32_t>(TypeUtils::sizeOf(typeId), minStackArgSize);
|
||||||
|
if (size >= 8)
|
||||||
|
stackOffset = Support::alignUp(stackOffset, 8);
|
||||||
arg.assignStackOffset(int32_t(stackOffset));
|
arg.assignStackOffset(int32_t(stackOffset));
|
||||||
stackOffset += size;
|
stackOffset += size;
|
||||||
}
|
}
|
||||||
@@ -178,7 +191,7 @@ ASMJIT_FAVOR_SIZE Error initFuncDetail(FuncDetail& func, const FuncSignature& si
|
|||||||
return DebugUtils::errored(kErrorInvalidState);
|
return DebugUtils::errored(kErrorInvalidState);
|
||||||
}
|
}
|
||||||
|
|
||||||
func._argStackSize = stackOffset;
|
func._argStackSize = Support::alignUp(stackOffset, 8u);
|
||||||
return kErrorOk;
|
return kErrorOk;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ namespace FuncInternal {
|
|||||||
Error initCallConv(CallConv& cc, CallConvId ccId, const Environment& environment) noexcept;
|
Error initCallConv(CallConv& cc, CallConvId ccId, const Environment& environment) noexcept;
|
||||||
|
|
||||||
//! Initialize `FuncDetail` (AArch64 specific).
|
//! Initialize `FuncDetail` (AArch64 specific).
|
||||||
Error initFuncDetail(FuncDetail& func, const FuncSignature& signature, uint32_t registerSize) noexcept;
|
Error initFuncDetail(FuncDetail& func, const FuncSignature& signature) noexcept;
|
||||||
|
|
||||||
} // {FuncInternal}
|
} // {FuncInternal}
|
||||||
|
|
||||||
|
|||||||
@@ -612,7 +612,9 @@ void ARMRAPass::onInit() noexcept {
|
|||||||
// make unavailable all registers that are special and cannot be used in general.
|
// make unavailable all registers that are special and cannot be used in general.
|
||||||
bool hasFP = _func->frame().hasPreservedFP();
|
bool hasFP = _func->frame().hasPreservedFP();
|
||||||
|
|
||||||
if (hasFP)
|
// Apple ABI requires that the frame-pointer register is not changed by leaf functions and properly updated
|
||||||
|
// by non-leaf functions. So, let's make this register unavailable as it's just not safe to update it.
|
||||||
|
if (hasFP || cc()->environment().isDarwin())
|
||||||
makeUnavailable(RegGroup::kGp, Gp::kIdFp);
|
makeUnavailable(RegGroup::kGp, Gp::kIdFp);
|
||||||
|
|
||||||
makeUnavailable(RegGroup::kGp, Gp::kIdSp);
|
makeUnavailable(RegGroup::kGp, Gp::kIdSp);
|
||||||
|
|||||||
@@ -126,6 +126,8 @@ enum class PlatformABI : uint8_t {
|
|||||||
kAndroid,
|
kAndroid,
|
||||||
//! Cygwin ABI.
|
//! Cygwin ABI.
|
||||||
kCygwin,
|
kCygwin,
|
||||||
|
//! Darwin ABI.
|
||||||
|
kDarwin,
|
||||||
|
|
||||||
//! Maximum value of `PlatformABI`.
|
//! Maximum value of `PlatformABI`.
|
||||||
kMaxValue,
|
kMaxValue,
|
||||||
@@ -142,6 +144,8 @@ enum class PlatformABI : uint8_t {
|
|||||||
kGNU
|
kGNU
|
||||||
#elif defined(__ANDROID__)
|
#elif defined(__ANDROID__)
|
||||||
kAndroid
|
kAndroid
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
kDarwin
|
||||||
#else
|
#else
|
||||||
kUnknown
|
kUnknown
|
||||||
#endif
|
#endif
|
||||||
@@ -399,6 +403,8 @@ public:
|
|||||||
ASMJIT_INLINE_NODEBUG bool isMSVC() const noexcept { return _platformABI == PlatformABI::kMSVC; }
|
ASMJIT_INLINE_NODEBUG bool isMSVC() const noexcept { return _platformABI == PlatformABI::kMSVC; }
|
||||||
//! Tests whether the ABI is GNU.
|
//! Tests whether the ABI is GNU.
|
||||||
ASMJIT_INLINE_NODEBUG bool isGNU() const noexcept { return _platformABI == PlatformABI::kGNU; }
|
ASMJIT_INLINE_NODEBUG bool isGNU() const noexcept { return _platformABI == PlatformABI::kGNU; }
|
||||||
|
//! Tests whether the ABI is GNU.
|
||||||
|
ASMJIT_INLINE_NODEBUG bool isDarwin() const noexcept { return _platformABI == PlatformABI::kDarwin; }
|
||||||
|
|
||||||
//! Returns a calculated stack alignment for this environment.
|
//! Returns a calculated stack alignment for this environment.
|
||||||
ASMJIT_API uint32_t stackAlignment() const noexcept;
|
ASMJIT_API uint32_t stackAlignment() const noexcept;
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ ASMJIT_FAVOR_SIZE Error FuncDetail::init(const FuncSignature& signature, const E
|
|||||||
|
|
||||||
#if !defined(ASMJIT_NO_AARCH64)
|
#if !defined(ASMJIT_NO_AARCH64)
|
||||||
if (environment.isFamilyAArch64())
|
if (environment.isFamilyAArch64())
|
||||||
return a64::FuncInternal::initFuncDetail(*this, signature, registerSize);
|
return a64::FuncInternal::initFuncDetail(*this, signature);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// We should never bubble here as if `cc.init()` succeeded then there has to be an implementation for the current
|
// We should never bubble here as if `cc.init()` succeeded then there has to be an implementation for the current
|
||||||
|
|||||||
@@ -119,6 +119,8 @@ enum class CallConvStrategy : uint8_t {
|
|||||||
kX64Windows = 1,
|
kX64Windows = 1,
|
||||||
//! Windows 64-bit __vectorcall register assignment strategy.
|
//! Windows 64-bit __vectorcall register assignment strategy.
|
||||||
kX64VectorCall = 2,
|
kX64VectorCall = 2,
|
||||||
|
//! Apple's AArch64 calling convention (differs compared to AArch64 calling convention used by Linux).
|
||||||
|
kAArch64Apple = 3,
|
||||||
|
|
||||||
//! Maximum value of `CallConvStrategy`.
|
//! Maximum value of `CallConvStrategy`.
|
||||||
kMaxValue = kX64VectorCall
|
kMaxValue = kX64VectorCall
|
||||||
|
|||||||
@@ -327,7 +327,8 @@ ASMJIT_FAVOR_SIZE Error initFuncDetail(FuncDetail& func, const FuncSignature& si
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch (cc.strategy()) {
|
switch (cc.strategy()) {
|
||||||
case CallConvStrategy::kDefault: {
|
case CallConvStrategy::kDefault:
|
||||||
|
default: {
|
||||||
uint32_t gpzPos = 0;
|
uint32_t gpzPos = 0;
|
||||||
uint32_t vecPos = 0;
|
uint32_t vecPos = 0;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user