From 4f4016690277eafb92a740de08e027cb0716cd73 Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Mon, 22 Feb 2021 18:17:53 -0800 Subject: [PATCH 1/4] Implements unaligned atomic memory ops for ARMv8.1+ This takes the existing unaligned CAS handling code and makes it more robust to handle both true unaligned CAS and unaligned atomic memory ops. Specifically it inserts some lambda helpers to calculate the Desired and Expected values inside of the CAS loops to account for CAS and memory operation. --- .../Interface/Core/ArchHelpers/Arm64.cpp | 1416 ++++++++++------- .../Source/Interface/Core/ArchHelpers/Arm64.h | 16 + 2 files changed, 877 insertions(+), 555 deletions(-) diff --git a/External/FEXCore/Source/Interface/Core/ArchHelpers/Arm64.cpp b/External/FEXCore/Source/Interface/Core/ArchHelpers/Arm64.cpp index bb9bdcdd9..cda87abe4 100644 --- a/External/FEXCore/Source/Interface/Core/ArchHelpers/Arm64.cpp +++ b/External/FEXCore/Source/Interface/Core/ArchHelpers/Arm64.cpp @@ -1,3 +1,5 @@ +#include "Interface/Core/ArchHelpers/Arm64.h" + #include #include @@ -219,6 +221,612 @@ bool HandleCASPAL(void *_mcontext, void *_info, uint32_t Instr) { return false; } +static +std::tuple DoCAS16( + uint16_t DesiredSrc, + uint16_t ExpectedSrc, + uint64_t Addr, + std::function ExpectedFunction, + std::function DesiredFunction) { + // 16 bit + uint64_t AlignmentMask = 0b1111; + if ((Addr & AlignmentMask) == 15) { + // Address crosses over 16byte or 64byte threshold + // Need a dual 8bit CAS loop + uint64_t AddrUpper = Addr + 1; + + uint8_t ActualUpper{}; + uint8_t ActualLower{}; + // Careful ordering here + ActualUpper = LoadAcquire8(AddrUpper); + ActualLower = LoadAcquire8(Addr); + + uint16_t Actual = ActualUpper; + Actual <<= 8; + Actual |= ActualLower; + + uint16_t Desired = DesiredFunction(Actual, DesiredSrc); + uint8_t DesiredLower = Desired; + uint8_t DesiredUpper = Desired >> 8; + + uint16_t Expected = ExpectedFunction(Actual, ExpectedSrc); + uint8_t ExpectedLower = Expected; + uint8_t ExpectedUpper = Expected >> 8; + + if (ActualUpper == ExpectedUpper && + ActualLower == ExpectedLower) { + if (StoreCAS8(ExpectedUpper, DesiredUpper, AddrUpper)) { + if (StoreCAS8(ExpectedLower, DesiredLower, Addr)) { + // Stored successfully + return std::make_tuple(Expected, true); + } + else { + // CAS managed to tear, we can't really solve this + // Continue down the path to let the guest know values weren't expected + } + } + + ActualLower = ExpectedLower; + ActualUpper = ExpectedUpper; + } + + // If the bits changed that we were wanting to change then we have failed and can return + // We need to extract the bits and return them in EXPECTED + uint16_t FailedResult = ActualUpper; + FailedResult <<= 8; + FailedResult |= ActualLower; + + // CAS failed but handled successfully + return std::make_tuple(FailedResult, false); + } + else { + AlignmentMask = 0b111; + if ((Addr & AlignmentMask) == 7) { + // Crosses 8byte boundary + // Needs 128bit CAS + // Fits within a 16byte region + uint64_t Alignment = Addr & 0b1111; + Addr &= ~0b1111ULL; + std::atomic<__uint128_t> *Atomic128 = reinterpret_cast*>(Addr); + + __uint128_t Mask = 0xFFFF; + Mask <<= Alignment * 8; + __uint128_t NegMask = ~Mask; + __uint128_t TmpExpected{}; + __uint128_t TmpDesired{}; + + while (1) { + TmpExpected = Atomic128->load(); + + __uint128_t Desired = DesiredFunction(TmpExpected >> (Alignment * 8), DesiredSrc); + Desired <<= Alignment * 8; + + __uint128_t Expected = ExpectedFunction(TmpExpected >> (Alignment * 8), ExpectedSrc); + Expected <<= Alignment * 8; + + // Set up expected + TmpExpected &= NegMask; + TmpExpected |= Expected; + + // Set up desired + TmpDesired = TmpExpected; + TmpDesired &= NegMask; + TmpDesired |= Desired; + + bool CASResult = Atomic128->compare_exchange_strong(TmpExpected, TmpDesired); + if (CASResult) { + // Successful, so we are done + return std::make_tuple(Expected >> (Alignment * 8), true); + } + else { + // Not successful + // Now we need to check the results to see if we need to try again + __uint128_t FailedResultOurBits = TmpExpected & Mask; + __uint128_t FailedResultNotOurBits = TmpExpected & NegMask; + + __uint128_t FailedDesiredOurBits = TmpDesired & Mask; + __uint128_t FailedDesiredNotOurBits = TmpDesired & NegMask; + if ((FailedResultNotOurBits ^ FailedDesiredNotOurBits) != 0) { + // If the bits changed that weren't part of our regular CAS then we need to try again + continue; + } + if ((FailedResultOurBits ^ FailedDesiredOurBits) != 0) { + // If the bits changed that we were wanting to change then we have failed and can return + // We need to extract the bits and return them in EXPECTED + uint16_t FailedResult = FailedResultOurBits >> (Alignment * 8); + LogMan::Msg::D("Expected 0x%04x, Desired 0x%04x, Result 0x%04x", (uint16_t)(Expected >> (Alignment * 8)), DesiredSrc, FailedResult); + // CAS failed but handled successfully + return std::make_tuple(FailedResult, false); + } + } + } + } + else { + AlignmentMask = 0b11; + if ((Addr & AlignmentMask) == 3) { + // Crosses 4byte boundary + // Needs 64bit CAS + uint64_t Alignment = Addr & AlignmentMask; + Addr &= ~AlignmentMask; + + uint64_t Mask = 0xFFFF; + Mask <<= Alignment * 8; + + uint64_t NegMask = ~Mask; + + uint64_t TmpExpected{}; + uint64_t TmpDesired{}; + + std::atomic *Atomic = reinterpret_cast*>(Addr); + while (1) { + TmpExpected = Atomic->load(); + + uint64_t Desired = DesiredFunction(TmpExpected >> (Alignment * 8), DesiredSrc); + Desired <<= Alignment * 8; + + uint64_t Expected = ExpectedFunction(TmpExpected >> (Alignment * 8), ExpectedSrc); + Expected <<= Alignment * 8; + + // Set up expected + TmpExpected &= NegMask; + TmpExpected |= Expected; + + // Set up desired + TmpDesired = TmpExpected; + TmpDesired &= NegMask; + TmpDesired |= Desired; + + bool CASResult = Atomic->compare_exchange_strong(TmpExpected, TmpDesired); + if (CASResult) { + // Successful, so we are done + return std::make_tuple(Expected >> (Alignment * 8), true); + } + else { + // Not successful + // Now we need to check the results to see if we can try again + uint64_t FailedResultOurBits = TmpExpected & Mask; + uint64_t FailedResultNotOurBits = TmpExpected & NegMask; + + uint64_t FailedDesiredOurBits = TmpDesired & Mask; + uint64_t FailedDesiredNotOurBits = TmpDesired & NegMask; + + if ((FailedResultNotOurBits ^ FailedDesiredNotOurBits) != 0) { + // If the bits changed that weren't part of our regular CAS then we need to try again + continue; + } + if ((FailedResultOurBits ^ FailedDesiredOurBits) != 0) { + // If the bits changed that we were wanting to change then we have failed and can return + // We need to extract the bits and return them in EXPECTED + uint16_t FailedResult = FailedResultOurBits >> (Alignment * 8); + // CAS failed but handled successfully + return std::make_tuple(FailedResult, false); + } + } + } + } + else { + // Fits within 4byte boundary + // Only needs 32bit CAS + // Only alignment offset will be 1 here + uint64_t Alignment = Addr & AlignmentMask; + Addr &= ~AlignmentMask; + + uint32_t Mask = 0xFFFF; + Mask <<= Alignment * 8; + + uint32_t NegMask = ~Mask; + + uint32_t TmpExpected{}; + uint32_t TmpDesired{}; + + std::atomic *Atomic = reinterpret_cast*>(Addr); + while (1) { + TmpExpected = Atomic->load(); + + + uint32_t Desired = DesiredFunction(TmpExpected >> (Alignment * 8), DesiredSrc); + Desired <<= Alignment * 8; + + uint32_t Expected = ExpectedFunction(TmpExpected >> (Alignment * 8), ExpectedSrc); + Expected <<= Alignment * 8; + + // Set up expected + TmpExpected &= NegMask; + TmpExpected |= Expected; + + // Set up desired + TmpDesired = TmpExpected; + TmpDesired &= NegMask; + TmpDesired |= Desired; + + bool CASResult = Atomic->compare_exchange_strong(TmpExpected, TmpDesired); + if (CASResult) { + // Successful, so we are done + return std::make_tuple(Expected >> (Alignment * 8), true); + } + else { + // Not successful + // Now we need to check the results to see if we can try again + uint32_t FailedResultOurBits = TmpExpected & Mask; + uint32_t FailedResultNotOurBits = TmpExpected & NegMask; + + uint32_t FailedDesiredOurBits = TmpDesired & Mask; + uint32_t FailedDesiredNotOurBits = TmpDesired & NegMask; + + if ((FailedResultNotOurBits ^ FailedDesiredNotOurBits) != 0) { + // If the bits changed that weren't part of our regular CAS then we need to try again + continue; + } + if ((FailedResultOurBits ^ FailedDesiredOurBits) != 0) { + // If the bits changed that we were wanting to change then we have failed and can return + // We need to extract the bits and return them in EXPECTED + uint16_t FailedResult = FailedResultOurBits >> (Alignment * 8); + // CAS failed but handled successfully + return std::make_tuple(FailedResult, false); + } + } + } + } + } + } +} + +static +std::tuple DoCAS32( + uint32_t DesiredSrc, + uint32_t ExpectedSrc, + uint64_t Addr, + std::function ExpectedFunction, + std::function DesiredFunction) { + // 32 bit + uint64_t AlignmentMask = 0b1111; + if ((Addr & AlignmentMask) > 12) { + // Address crosses over 16byte threshold + // Needs dual 4 byte CAS loop + uint64_t Alignment = Addr & 0b11; + Addr &= ~0b11; + + uint64_t AddrUpper = Addr + 4; + + uint64_t Mask = ~0U; + Mask <<= Alignment * 8; + uint64_t NegMask = ~Mask; + + // Careful ordering here + while (1) { + uint64_t LoadOrderUpper = LoadAcquire32(AddrUpper); + LoadOrderUpper <<= 32; + uint64_t TmpActual = LoadOrderUpper | LoadAcquire32(Addr); + + uint64_t Desired = DesiredFunction(TmpActual >> (Alignment * 8), DesiredSrc); + uint64_t Expected = ExpectedFunction(TmpActual >> (Alignment * 8), ExpectedSrc); + + uint64_t TmpExpected = TmpActual; + TmpExpected &= NegMask; + TmpExpected |= Expected << (Alignment * 8); + + uint64_t TmpDesired = TmpExpected; + TmpDesired &= NegMask; + TmpDesired |= Desired << (Alignment * 8); + + if (TmpExpected == TmpActual) { + uint32_t TmpExpectedLower = TmpExpected; + uint32_t TmpExpectedUpper = TmpExpected >> 32; + + uint32_t TmpDesiredLower = TmpDesired; + uint32_t TmpDesiredUpper = TmpDesired >> 32; + + if (StoreCAS32(TmpExpectedUpper, TmpDesiredUpper, AddrUpper)) { + if (StoreCAS32(TmpExpectedLower, TmpDesiredLower, Addr)) { + // Stored successfully + return std::make_tuple(Expected, true); + } + else { + // CAS managed to tear, we can't really solve this + // Continue down the path to let the guest know values weren't expected + } + } + + TmpExpected = TmpExpectedUpper; + TmpExpected <<= 32; + TmpExpected |= TmpExpectedLower; + } + else { + // Mismatch up front + TmpExpected = TmpActual; + } + + // Not successful + // Now we need to check the results to see if we need to try again + uint64_t FailedResultOurBits = TmpExpected & Mask; + uint64_t FailedResultNotOurBits = TmpExpected & NegMask; + + uint64_t FailedDesiredOurBits = TmpDesired & Mask; + uint64_t FailedDesiredNotOurBits = TmpDesired & NegMask; + if ((FailedResultNotOurBits ^ FailedDesiredNotOurBits) != 0) { + // If the bits changed that weren't part of our regular CAS then we need to try again + continue; + } + if ((FailedResultOurBits ^ FailedDesiredOurBits) != 0) { + // If the bits changed that we were wanting to change then we have failed and can return + // We need to extract the bits and return them in EXPECTED + uint32_t FailedResult = FailedResultOurBits >> (Alignment * 8); + // CAS failed but handled successfully + return std::make_tuple(FailedResult, false); + } + } + } + else { + AlignmentMask = 0b111; + if ((Addr & AlignmentMask) >= 5) { + // Crosses 8byte boundary + // Needs 128bit CAS + // Fits within a 16byte region + uint64_t Alignment = Addr & 0b1111; + Addr &= ~0b1111ULL; + std::atomic<__uint128_t> *Atomic128 = reinterpret_cast*>(Addr); + + __uint128_t Mask = ~0U; + Mask <<= Alignment * 8; + __uint128_t NegMask = ~Mask; + __uint128_t TmpExpected{}; + __uint128_t TmpDesired{}; + + while (1) { + __uint128_t TmpActual = Atomic128->load(); + + __uint128_t Desired = DesiredFunction(TmpActual >> (Alignment * 8), DesiredSrc); + __uint128_t Expected = ExpectedFunction(TmpActual >> (Alignment * 8), ExpectedSrc); + + // Set up expected + TmpExpected = TmpActual; + TmpExpected &= NegMask; + TmpExpected |= Expected << (Alignment * 8); + + // Set up desired + TmpDesired = TmpExpected; + TmpDesired &= NegMask; + TmpDesired |= Desired << (Alignment * 8); + + bool CASResult = Atomic128->compare_exchange_strong(TmpExpected, TmpDesired); + if (CASResult) { + // Stored successfully + return std::make_tuple(Expected, true); + } + else { + // Not successful + // Now we need to check the results to see if we need to try again + __uint128_t FailedResultOurBits = TmpExpected & Mask; + __uint128_t FailedResultNotOurBits = TmpExpected & NegMask; + + __uint128_t FailedDesiredOurBits = TmpDesired & Mask; + __uint128_t FailedDesiredNotOurBits = TmpDesired & NegMask; + if ((FailedResultNotOurBits ^ FailedDesiredNotOurBits) != 0) { + // If the bits changed that weren't part of our regular CAS then we need to try again + continue; + } + if ((FailedResultOurBits ^ FailedDesiredOurBits) != 0) { + // If the bits changed that we were wanting to change then we have failed and can return + // We need to extract the bits and return them in EXPECTED + uint32_t FailedResult = FailedResultOurBits >> (Alignment * 8); + // CAS failed but handled successfully + return std::make_tuple(FailedResult, false); + } + } + } + } + else { + // Fits within 8byte boundary + // Only needs 64bit CAS + // Alignments can be [1,5) + uint64_t Alignment = Addr & AlignmentMask; + Addr &= ~AlignmentMask; + + uint64_t Mask = ~0U; + Mask <<= Alignment * 8; + + uint64_t NegMask = ~Mask; + + uint64_t TmpExpected{}; + uint64_t TmpDesired{}; + + std::atomic *Atomic = reinterpret_cast*>(Addr); + while (1) { + uint64_t TmpActual = Atomic->load(); + + uint64_t Desired = DesiredFunction(TmpActual >> (Alignment * 8), DesiredSrc); + uint64_t Expected = ExpectedFunction(TmpActual >> (Alignment * 8), ExpectedSrc); + + // Set up expected + TmpExpected = TmpActual; + TmpExpected &= NegMask; + TmpExpected |= Expected << (Alignment * 8); + + // Set up desired + TmpDesired = TmpExpected; + TmpDesired &= NegMask; + TmpDesired |= Desired << (Alignment * 8); + + bool CASResult = Atomic->compare_exchange_strong(TmpExpected, TmpDesired); + if (CASResult) { + // Stored successfully + return std::make_tuple(Expected, true); + } + else { + // Not successful + // Now we need to check the results to see if we can try again + uint64_t FailedResultOurBits = TmpExpected & Mask; + uint64_t FailedResultNotOurBits = TmpExpected & NegMask; + + uint64_t FailedDesiredOurBits = TmpDesired & Mask; + uint64_t FailedDesiredNotOurBits = TmpDesired & NegMask; + + if ((FailedResultNotOurBits ^ FailedDesiredNotOurBits) != 0) { + // If the bits changed that weren't part of our regular CAS then we need to try again + continue; + } + if ((FailedResultOurBits ^ FailedDesiredOurBits) != 0) { + // If the bits changed that we were wanting to change then we have failed and can return + // We need to extract the bits and return them in EXPECTED + uint32_t FailedResult = FailedResultOurBits >> (Alignment * 8); + // CAS failed but handled successfully + return std::make_tuple(FailedResult, false); + } + } + } + } + } +} + +static +std::tuple DoCAS64( + uint64_t DesiredSrc, + uint64_t ExpectedSrc, + uint64_t Addr, + std::function ExpectedFunction, + std::function DesiredFunction) { + // 64bit + uint64_t AlignmentMask = 0b1111; + if ((Addr & AlignmentMask) > 8) { + uint64_t Alignment = Addr & 0b111; + Addr &= ~0b111ULL; + uint64_t AddrUpper = Addr + 8; + + // Crosses a 16byte boundary + // Need to do 256bit atomic, but since that doesn't exist we need to do a dual CAS loop + __uint128_t Mask = ~0ULL; + Mask <<= Alignment * 8; + __uint128_t NegMask = ~Mask; + __uint128_t TmpExpected{}; + __uint128_t TmpDesired{}; + + while (1) { + __uint128_t LoadOrderUpper = LoadAcquire64(AddrUpper); + LoadOrderUpper <<= 64; + __uint128_t TmpActual = LoadOrderUpper | LoadAcquire64(Addr); + + __uint128_t Desired = DesiredFunction(TmpActual >> (Alignment * 8), DesiredSrc); + __uint128_t Expected = ExpectedFunction(TmpActual >> (Alignment * 8), ExpectedSrc); + + // Set up expected + TmpExpected = TmpActual; + TmpExpected &= NegMask; + TmpExpected |= Expected << (Alignment * 8); + + // Set up desired + TmpDesired = TmpExpected; + TmpDesired &= NegMask; + TmpDesired |= Desired << (Alignment * 8); + + uint64_t TmpExpectedLower = TmpExpected; + uint64_t TmpExpectedUpper = TmpExpected >> 64; + + uint64_t TmpDesiredLower = TmpDesired; + uint64_t TmpDesiredUpper = TmpDesired >> 64; + + if (TmpExpected == TmpActual) { + if (StoreCAS64(TmpExpectedUpper, TmpDesiredUpper, AddrUpper)) { + if (StoreCAS64(TmpExpectedLower, TmpDesiredLower, Addr)) { + // Stored successfully + return std::make_tuple(Expected, true); + } + else { + // CAS managed to tear, we can't really solve this + // Continue down the path to let the guest know values weren't expected + } + } + + TmpExpected = TmpExpectedUpper; + TmpExpected <<= 64; + TmpExpected |= TmpExpectedLower; + } + else { + // Mismatch up front + TmpExpected = TmpActual; + } + + // Not successful + // Now we need to check the results to see if we need to try again + __uint128_t FailedResultOurBits = TmpExpected & Mask; + __uint128_t FailedResultNotOurBits = TmpExpected & NegMask; + + __uint128_t FailedDesiredOurBits = TmpDesired & Mask; + __uint128_t FailedDesiredNotOurBits = TmpDesired & NegMask; + if ((FailedResultNotOurBits ^ FailedDesiredNotOurBits) != 0) { + // If the bits changed that weren't part of our regular CAS then we need to try again + continue; + } + if ((FailedResultOurBits ^ FailedDesiredOurBits) != 0) { + // If the bits changed that we were wanting to change then we have failed and can return + // We need to extract the bits and return them in EXPECTED + uint64_t FailedResult = FailedResultOurBits >> (Alignment * 8); + // CAS failed but handled successfully + return std::make_tuple(FailedResult, false); + } + } + } + else { + // Fits within a 16byte region + uint64_t Alignment = Addr & AlignmentMask; + Addr &= ~AlignmentMask; + std::atomic<__uint128_t> *Atomic128 = reinterpret_cast*>(Addr); + + __uint128_t Mask = ~0ULL; + Mask <<= Alignment * 8; + __uint128_t NegMask = ~Mask; + __uint128_t TmpExpected{}; + __uint128_t TmpDesired{}; + + while (1) { + __uint128_t TmpActual = Atomic128->load(); + + __uint128_t Desired = DesiredFunction(TmpActual >> (Alignment * 8), DesiredSrc); + __uint128_t Expected = ExpectedFunction(TmpActual >> (Alignment * 8), ExpectedSrc); + + // Set up expected + TmpExpected = TmpActual; + TmpExpected &= NegMask; + TmpExpected |= Expected << (Alignment * 8); + + // Set up desired + TmpDesired = TmpExpected; + TmpDesired &= NegMask; + TmpDesired |= Desired << (Alignment * 8); + + bool CASResult = Atomic128->compare_exchange_strong(TmpExpected, TmpDesired); + if (CASResult) { + // Stored successfully + return std::make_tuple(Expected, true); + } + else { + // Not successful + // Now we need to check the results to see if we need to try again + __uint128_t FailedResultOurBits = TmpExpected & Mask; + __uint128_t FailedResultNotOurBits = TmpExpected & NegMask; + + __uint128_t FailedDesiredOurBits = TmpDesired & Mask; + __uint128_t FailedDesiredNotOurBits = TmpDesired & NegMask; + if ((FailedResultNotOurBits ^ FailedDesiredNotOurBits) != 0) { + // If the bits changed that weren't part of our regular CAS then we need to try again + continue; + } + if ((FailedResultOurBits ^ FailedDesiredOurBits) != 0) { + // If the bits changed that we were wanting to change then we have failed and can return + // We need to extract the bits and return them in EXPECTED + uint64_t FailedResult = FailedResultOurBits >> (Alignment * 8); + // CAS failed but handled successfully + return std::make_tuple(FailedResult, false); + } + + // If we got here, that means the CAS failed + // NotOurBits didn't change and bits we cared about didn't change + ERROR_AND_DIE("Impossible"); + } + } + } + +} + bool HandleCASAL(void *_mcontext, void *_info, uint32_t Instr) { mcontext_t* mcontext = reinterpret_cast(_mcontext); siginfo_t* info = reinterpret_cast(_info); @@ -242,597 +850,295 @@ bool HandleCASAL(void *_mcontext, void *_info, uint32_t Instr) { // AMD will tear instead // Both cross-cacheline and cross 16byte both need dual CAS loops that can tear // ARMv8.4 LSE2 solves all atomic issues except cross-cacheline + // ARM's TME extension solves the cross-cacheline problem // 8bit can't be unaligned // Only need to handle 16, 32, 64 if (Size == 2) { - // 16 bit - uint64_t AlignmentMask = 0b1111; - if ((Addr & AlignmentMask) == 15) { - // Address crosses over 16byte or 64byte threshold - // Need a dual 8bit CAS loop - uint64_t AddrUpper = Addr + 1; + auto Res = DoCAS16( + mcontext->regs[DesiredReg], + mcontext->regs[ExpectedReg], + Addr, + [](uint16_t, uint16_t Expected) -> uint16_t { + // Expected is just Expected + return Expected; + }, + [](uint16_t, uint16_t Desired) -> uint16_t { + // Desired is just Desired + return Desired; + }); - uint16_t Desired = mcontext->regs[DesiredReg]; - uint16_t Expected = mcontext->regs[ExpectedReg]; - - uint8_t DesiredLower = Desired; - uint8_t DesiredUpper = Desired >> 8; - - uint8_t ExpectedLower = Expected; - uint8_t ExpectedUpper = Expected >> 8; - - uint8_t ActualUpper{}; - uint8_t ActualLower{}; - // Careful ordering here - ActualUpper = LoadAcquire8(AddrUpper); - ActualLower = LoadAcquire8(Addr); - if (ActualUpper == ExpectedUpper && - ActualLower == ExpectedLower) { - if (StoreCAS8(ExpectedUpper, DesiredUpper, AddrUpper)) { - if (StoreCAS8(ExpectedLower, DesiredLower, Addr)) { - // Stored successfully - return true; - } - else { - // CAS managed to tear, we can't really solve this - // Continue down the path to let the guest know values weren't expected - } - } - - ActualLower = ExpectedLower; - ActualUpper = ExpectedUpper; - } - - // If the bits changed that we were wanting to change then we have failed and can return - // We need to extract the bits and return them in EXPECTED - uint16_t FailedResult = ActualUpper; - FailedResult <<= 8; - FailedResult |= ActualLower; - mcontext->regs[ExpectedReg] = FailedResult; - return true; - } - else { - AlignmentMask = 0b111; - if ((Addr & AlignmentMask) == 7) { - // Crosses 8byte boundary - // Needs 128bit CAS - // Fits within a 16byte region - uint64_t Alignment = Addr & 0b1111; - Addr &= ~0b1111ULL; - std::atomic<__uint128_t> *Atomic128 = reinterpret_cast*>(Addr); - - __uint128_t Mask = ~0U; - Mask <<= Alignment * 8; - __uint128_t NegMask = ~Mask; - __uint128_t TmpExpected{}; - __uint128_t TmpDesired{}; - - __uint128_t Desired = mcontext->regs[DesiredReg] & 0xFFFF; - Desired <<= Alignment * 8; - - __uint128_t Expected = mcontext->regs[ExpectedReg] & 0xFFFF; - Expected <<= Alignment * 8; - - while (1) { - TmpExpected = Atomic128->load(); - - // Set up expected - TmpExpected &= NegMask; - TmpExpected |= Expected; - - // Set up desired - TmpDesired = TmpExpected; - TmpDesired &= NegMask; - TmpDesired |= Desired; - - bool CASResult = Atomic128->compare_exchange_strong(TmpExpected, TmpDesired); - if (CASResult) { - // Successful, so we are done - return true; - } - else { - // Not successful - // Now we need to check the results to see if we need to try again - __uint128_t FailedResultOurBits = TmpExpected & Mask; - __uint128_t FailedResultNotOurBits = TmpExpected & NegMask; - - __uint128_t FailedDesiredOurBits = TmpDesired & Mask; - __uint128_t FailedDesiredNotOurBits = TmpDesired & NegMask; - if ((FailedResultNotOurBits ^ FailedDesiredNotOurBits) != 0) { - // If the bits changed that weren't part of our regular CAS then we need to try again - continue; - } - if ((FailedResultOurBits ^ FailedDesiredOurBits) != 0) { - // If the bits changed that we were wanting to change then we have failed and can return - // We need to extract the bits and return them in EXPECTED - uint32_t FailedResult = FailedResultOurBits >> (Alignment * 8); - mcontext->regs[ExpectedReg] = FailedResult; - return true; - } - } - } - } - else { - AlignmentMask = 0b11; - if ((Addr & AlignmentMask) == 3) { - // Crosses 4byte boundary - // Needs 64bit CAS - uint64_t Alignment = Addr & AlignmentMask; - Addr &= ~AlignmentMask; - - uint64_t Mask = 0xFFFF; - Mask <<= Alignment * 8; - - uint64_t NegMask = ~Mask; - - uint64_t TmpExpected{}; - uint64_t TmpDesired{}; - - uint64_t Desired = mcontext->regs[DesiredReg] & 0xFFFF; - Desired <<= Alignment * 8; - - uint64_t Expected = mcontext->regs[ExpectedReg] & 0xFFFF; - Expected <<= Alignment * 8; - - std::atomic *Atomic = reinterpret_cast*>(Addr); - while (1) { - TmpExpected = Atomic->load(); - - // Set up expected - TmpExpected &= NegMask; - TmpExpected |= Expected; - - // Set up desired - TmpDesired = TmpExpected; - TmpDesired &= NegMask; - TmpDesired |= Desired; - - bool CASResult = Atomic->compare_exchange_strong(TmpExpected, TmpDesired); - if (CASResult) { - // Successful, so we are done - return true; - } - else { - // Not successful - // Now we need to check the results to see if we can try again - uint64_t FailedResultOurBits = TmpExpected & Mask; - uint64_t FailedResultNotOurBits = TmpExpected & NegMask; - - uint64_t FailedDesiredOurBits = TmpDesired & Mask; - uint64_t FailedDesiredNotOurBits = TmpDesired & NegMask; - - if ((FailedResultNotOurBits ^ FailedDesiredNotOurBits) != 0) { - // If the bits changed that weren't part of our regular CAS then we need to try again - continue; - } - if ((FailedResultOurBits ^ FailedDesiredOurBits) != 0) { - // If the bits changed that we were wanting to change then we have failed and can return - // We need to extract the bits and return them in EXPECTED - uint16_t FailedResult = FailedResultOurBits >> (Alignment * 8); - mcontext->regs[ExpectedReg] = FailedResult; - return true; - } - } - } - } - else { - // Fits within 4byte boundary - // Only needs 32bit CAS - // Only alignment offset will be 1 here - uint64_t Alignment = Addr & AlignmentMask; - Addr &= ~AlignmentMask; - - uint32_t Mask = 0xFFFF; - Mask <<= Alignment * 8; - - uint32_t NegMask = ~Mask; - - uint32_t TmpExpected{}; - uint32_t TmpDesired{}; - - uint32_t Desired = mcontext->regs[DesiredReg] & 0xFFFF; - Desired <<= Alignment * 8; - - uint32_t Expected = mcontext->regs[ExpectedReg] & 0xFFFF; - Expected <<= Alignment * 8; - - std::atomic *Atomic = reinterpret_cast*>(Addr); - while (1) { - TmpExpected = Atomic->load(); - - // Set up expected - TmpExpected &= NegMask; - TmpExpected |= Expected; - - // Set up desired - TmpDesired = TmpExpected; - TmpDesired &= NegMask; - TmpDesired |= Desired; - - bool CASResult = Atomic->compare_exchange_strong(TmpExpected, TmpDesired); - if (CASResult) { - // Successful, so we are done - return true; - } - else { - // Not successful - // Now we need to check the results to see if we can try again - uint32_t FailedResultOurBits = TmpExpected & Mask; - uint32_t FailedResultNotOurBits = TmpExpected & NegMask; - - uint32_t FailedDesiredOurBits = TmpDesired & Mask; - uint32_t FailedDesiredNotOurBits = TmpDesired & NegMask; - - if ((FailedResultNotOurBits ^ FailedDesiredNotOurBits) != 0) { - // If the bits changed that weren't part of our regular CAS then we need to try again - continue; - } - if ((FailedResultOurBits ^ FailedDesiredOurBits) != 0) { - // If the bits changed that we were wanting to change then we have failed and can return - // We need to extract the bits and return them in EXPECTED - uint16_t FailedResult = FailedResultOurBits >> (Alignment * 8); - mcontext->regs[ExpectedReg] = FailedResult; - return true; - } - } - } - } - } + // Regardless of pass or fail + // We set the result register if it isn't a zero register + if (ExpectedReg != 31) { + mcontext->regs[ExpectedReg] = std::get<0>(Res); } + return true; } else if (Size == 4) { - // 32bit - uint64_t AlignmentMask = 0b1111; - if ((Addr & AlignmentMask) > 12) { - // Address crosses over 16byte threshold - // Needs dual 4 byte CAS loop - uint64_t Alignment = Addr & 0b11; - Addr &= ~0b11; + auto Res = DoCAS32( + mcontext->regs[DesiredReg], + mcontext->regs[ExpectedReg], + Addr, + [](uint32_t, uint32_t Expected) -> uint32_t { + // Expected is just Expected + return Expected; + }, + [](uint32_t, uint32_t Desired) -> uint32_t { + // Desired is just Desired + return Desired; + }); - uint64_t AddrUpper = Addr + 4; - - uint64_t Desired = mcontext->regs[DesiredReg] & ~0U; - uint64_t Expected = mcontext->regs[ExpectedReg] & ~0U; - - Desired <<= Alignment * 8; - Expected <<= Alignment * 8; - - uint64_t Mask = ~0U; - Mask <<= Alignment * 8; - uint64_t NegMask = ~Mask; - - // Careful ordering here - while (1) { - uint64_t LoadOrderUpper = LoadAcquire32(AddrUpper); - LoadOrderUpper <<= 32; - uint64_t TmpActual = LoadOrderUpper | LoadAcquire32(Addr); - - uint64_t TmpExpected = TmpActual; - TmpExpected &= NegMask; - TmpExpected |= Expected; - - uint64_t TmpDesired = TmpExpected; - TmpDesired &= NegMask; - TmpDesired |= Desired; - - if (TmpExpected == TmpActual) { - uint32_t TmpExpectedLower = TmpExpected; - uint32_t TmpExpectedUpper = TmpExpected >> 32; - - uint32_t TmpDesiredLower = TmpDesired; - uint32_t TmpDesiredUpper = TmpDesired >> 32; - - if (StoreCAS32(TmpExpectedUpper, TmpDesiredUpper, AddrUpper)) { - if (StoreCAS32(TmpExpectedLower, TmpDesiredLower, Addr)) { - // Stored successfully - return true; - } - else { - // CAS managed to tear, we can't really solve this - // Continue down the path to let the guest know values weren't expected - } - } - - TmpExpected = TmpExpectedUpper; - TmpExpected <<= 32; - TmpExpected |= TmpExpectedLower; - } - else { - // Mismatch up front - TmpExpected = TmpActual; - } - - // Not successful - // Now we need to check the results to see if we need to try again - uint64_t FailedResultOurBits = TmpExpected & Mask; - uint64_t FailedResultNotOurBits = TmpExpected & NegMask; - - uint64_t FailedDesiredOurBits = TmpDesired & Mask; - uint64_t FailedDesiredNotOurBits = TmpDesired & NegMask; - if ((FailedResultNotOurBits ^ FailedDesiredNotOurBits) != 0) { - // If the bits changed that weren't part of our regular CAS then we need to try again - continue; - } - if ((FailedResultOurBits ^ FailedDesiredOurBits) != 0) { - // If the bits changed that we were wanting to change then we have failed and can return - // We need to extract the bits and return them in EXPECTED - uint32_t FailedResult = FailedResultOurBits >> (Alignment * 8); - mcontext->regs[ExpectedReg] = FailedResult; - return true; - } - } - } - else { - AlignmentMask = 0b111; - if ((Addr & AlignmentMask) >= 5) { - // Crosses 8byte boundary - // Needs 128bit CAS - // Fits within a 16byte region - uint64_t Alignment = Addr & 0b1111; - Addr &= ~0b1111ULL; - std::atomic<__uint128_t> *Atomic128 = reinterpret_cast*>(Addr); - - __uint128_t Mask = ~0U; - Mask <<= Alignment * 8; - __uint128_t NegMask = ~Mask; - __uint128_t TmpExpected{}; - __uint128_t TmpDesired{}; - - __uint128_t Desired = mcontext->regs[DesiredReg] & ~0U; - Desired <<= Alignment * 8; - - __uint128_t Expected = mcontext->regs[ExpectedReg] & ~0U; - Expected <<= Alignment * 8; - - while (1) { - TmpExpected = Atomic128->load(); - - // Set up expected - TmpExpected &= NegMask; - TmpExpected |= Expected; - - // Set up desired - TmpDesired = TmpExpected; - TmpDesired &= NegMask; - TmpDesired |= Desired; - - bool CASResult = Atomic128->compare_exchange_strong(TmpExpected, TmpDesired); - if (CASResult) { - // Successful, so we are done - return true; - } - else { - // Not successful - // Now we need to check the results to see if we need to try again - __uint128_t FailedResultOurBits = TmpExpected & Mask; - __uint128_t FailedResultNotOurBits = TmpExpected & NegMask; - - __uint128_t FailedDesiredOurBits = TmpDesired & Mask; - __uint128_t FailedDesiredNotOurBits = TmpDesired & NegMask; - if ((FailedResultNotOurBits ^ FailedDesiredNotOurBits) != 0) { - // If the bits changed that weren't part of our regular CAS then we need to try again - continue; - } - if ((FailedResultOurBits ^ FailedDesiredOurBits) != 0) { - // If the bits changed that we were wanting to change then we have failed and can return - // We need to extract the bits and return them in EXPECTED - uint32_t FailedResult = FailedResultOurBits >> (Alignment * 8); - mcontext->regs[ExpectedReg] = FailedResult; - return true; - } - } - } - } - else { - // Fits within 8byte boundary - // Only needs 64bit CAS - // Alignments can be [1,5) - uint64_t Alignment = Addr & AlignmentMask; - Addr &= ~AlignmentMask; - - uint64_t Mask = ~0U; - Mask <<= Alignment * 8; - - uint64_t NegMask = ~Mask; - - uint64_t TmpExpected{}; - uint64_t TmpDesired{}; - - uint64_t Desired = mcontext->regs[DesiredReg] & ~0U; - Desired <<= Alignment * 8; - - uint64_t Expected = mcontext->regs[ExpectedReg] & ~0U; - Expected <<= Alignment * 8; - - std::atomic *Atomic = reinterpret_cast*>(Addr); - while (1) { - TmpExpected = Atomic->load(); - - // Set up expected - TmpExpected &= NegMask; - TmpExpected |= Expected; - - // Set up desired - TmpDesired = TmpExpected; - TmpDesired &= NegMask; - TmpDesired |= Desired; - - bool CASResult = Atomic->compare_exchange_strong(TmpExpected, TmpDesired); - if (CASResult) { - // Successful, so we are done - return true; - } - else { - // Not successful - // Now we need to check the results to see if we can try again - uint64_t FailedResultOurBits = TmpExpected & Mask; - uint64_t FailedResultNotOurBits = TmpExpected & NegMask; - - uint64_t FailedDesiredOurBits = TmpDesired & Mask; - uint64_t FailedDesiredNotOurBits = TmpDesired & NegMask; - - if ((FailedResultNotOurBits ^ FailedDesiredNotOurBits) != 0) { - // If the bits changed that weren't part of our regular CAS then we need to try again - continue; - } - if ((FailedResultOurBits ^ FailedDesiredOurBits) != 0) { - // If the bits changed that we were wanting to change then we have failed and can return - // We need to extract the bits and return them in EXPECTED - uint32_t FailedResult = FailedResultOurBits >> (Alignment * 8); - mcontext->regs[ExpectedReg] = FailedResult; - return true; - } - } - } - } + // Regardless of pass or fail + // We set the result register if it isn't a zero register + if (ExpectedReg != 31) { + mcontext->regs[ExpectedReg] = std::get<0>(Res); } + return true; } else if (Size == 8) { - // 64bit - uint64_t AlignmentMask = 0b1111; - if ((Addr & AlignmentMask) > 8) { - uint64_t Alignment = Addr & 0b111; - Addr &= ~0b111ULL; - uint64_t AddrUpper = Addr + 8; + auto Res = DoCAS64( + mcontext->regs[DesiredReg], + mcontext->regs[ExpectedReg], + Addr, + [](uint64_t, uint64_t Expected) -> uint64_t { + // Expected is just Expected + return Expected; + }, + [](uint64_t, uint64_t Desired) -> uint64_t { + // Desired is just Desired + return Desired; + }); - // Crosses a 16byte boundary - // Need to do 256bit atomic, but since that doesn't exist we need to do a dual CAS loop - __uint128_t Mask = ~0ULL; - Mask <<= Alignment * 8; - __uint128_t NegMask = ~Mask; - __uint128_t TmpExpected{}; - __uint128_t TmpDesired{}; + // Regardless of pass or fail + // We set the result register if it isn't a zero register + if (ExpectedReg != 31) { + mcontext->regs[ExpectedReg] = std::get<0>(Res); + } + return true; + } - __uint128_t Desired = mcontext->regs[DesiredReg]; - Desired <<= Alignment * 8; + return false; +} - __uint128_t Expected = mcontext->regs[ExpectedReg]; - Expected <<= Alignment * 8; +bool HandleAtomicMemOp(void *_mcontext, void *_info, uint32_t Instr) { + mcontext_t* mcontext = reinterpret_cast(_mcontext); + siginfo_t* info = reinterpret_cast(_info); - while (1) { - __uint128_t LoadOrderUpper = LoadAcquire64(AddrUpper); - LoadOrderUpper <<= 64; - __uint128_t TmpActual = LoadOrderUpper | LoadAcquire64(Addr); + if (info->si_code != BUS_ADRALN) { + // This only handles alignment problems + return false; + } - // Set up expected - TmpExpected = TmpActual; - TmpExpected &= NegMask; - TmpExpected |= Expected; + uint32_t Size = 1 << (Instr >> 30); + uint32_t ResultReg = Instr & 0b11111; + uint32_t SourceReg = (Instr >> 16) & 0b11111; + uint32_t AddressReg = (Instr >> 5) & 0b11111; - // Set up desired - TmpDesired = TmpExpected; - TmpDesired &= NegMask; - TmpDesired |= Desired; + uint64_t Addr = mcontext->regs[AddressReg]; - uint64_t TmpExpectedLower = TmpExpected; - uint64_t TmpExpectedUpper = TmpExpected >> 64; + uint8_t Op = (Instr >> 12) & 0xF; - uint64_t TmpDesiredLower = TmpDesired; - uint64_t TmpDesiredUpper = TmpDesired >> 64; + if (Size == 2) { + auto NOPExpected = [](uint16_t SrcVal, uint16_t) -> uint16_t { + return SrcVal; + }; - if (TmpExpected == TmpActual) { - if (StoreCAS64(TmpExpectedUpper, TmpDesiredUpper, AddrUpper)) { - if (StoreCAS64(TmpExpectedLower, TmpDesiredLower, Addr)) { - // Stored successfully - return true; - } - else { - // CAS managed to tear, we can't really solve this - // Continue down the path to let the guest know values weren't expected - } - } + auto ADDDesired = [](uint16_t SrcVal, uint16_t Desired) -> uint16_t { + return SrcVal + Desired; + }; - TmpExpected = TmpExpectedUpper; - TmpExpected <<= 64; - TmpExpected |= TmpExpectedLower; - } - else { - // Mismatch up front - TmpExpected = TmpActual; - } + auto CLRDesired = [](uint16_t SrcVal, uint16_t Desired) -> uint16_t { + return SrcVal & ~Desired; + }; - // Not successful - // Now we need to check the results to see if we need to try again - __uint128_t FailedResultOurBits = TmpExpected & Mask; - __uint128_t FailedResultNotOurBits = TmpExpected & NegMask; + auto EORDesired = [](uint16_t SrcVal, uint16_t Desired) -> uint16_t { + return SrcVal ^ Desired; + }; - __uint128_t FailedDesiredOurBits = TmpDesired & Mask; - __uint128_t FailedDesiredNotOurBits = TmpDesired & NegMask; - if ((FailedResultNotOurBits ^ FailedDesiredNotOurBits) != 0) { - // If the bits changed that weren't part of our regular CAS then we need to try again - continue; - } - if ((FailedResultOurBits ^ FailedDesiredOurBits) != 0) { - // If the bits changed that we were wanting to change then we have failed and can return - // We need to extract the bits and return them in EXPECTED - uint64_t FailedResult = FailedResultOurBits >> (Alignment * 8); - mcontext->regs[ExpectedReg] = FailedResult; - return true; - } + auto SETDesired = [](uint16_t SrcVal, uint16_t Desired) -> uint16_t { + return SrcVal | Desired; + }; + + auto SWAPDesired = [](uint16_t SrcVal, uint16_t Desired) -> uint16_t { + return Desired; + }; + + std::function DesiredFunction; + + switch (Op) { + case ATOMIC_ADD_OP: + DesiredFunction = ADDDesired; + break; + case ATOMIC_CLR_OP: + DesiredFunction = CLRDesired; + break; + case ATOMIC_EOR_OP: + DesiredFunction = EORDesired; + break; + case ATOMIC_SET_OP: + DesiredFunction = SETDesired; + break; + case ATOMIC_SWAP_OP: + DesiredFunction = SWAPDesired; + break; + default: + LogMan::Msg::E("Unhandled JIT SIGBUS Atomic mem op 0x%02x", Op); + return false; + break; + } + + bool Passed = false; + while (!Passed) { + auto Res = DoCAS16( + mcontext->regs[SourceReg], + 0, // Unused + Addr, + NOPExpected, + DesiredFunction); + Passed = std::get<1>(Res); + // If we passed and our destination register is not zero + // Then we need to update the result register with what was in memory + if (Passed && + ResultReg != 31) { + mcontext->regs[ResultReg] = std::get<0>(Res); } } - else { - // Fits within a 16byte region - uint64_t Alignment = Addr & AlignmentMask; - Addr &= ~AlignmentMask; - std::atomic<__uint128_t> *Atomic128 = reinterpret_cast*>(Addr); + return true; + } + else if (Size == 4) { + auto NOPExpected = [](uint32_t SrcVal, uint32_t) -> uint32_t { + return SrcVal; + }; - __uint128_t Mask = ~0ULL; - Mask <<= Alignment * 8; - __uint128_t NegMask = ~Mask; - __uint128_t TmpExpected{}; - __uint128_t TmpDesired{}; + auto ADDDesired = [](uint32_t SrcVal, uint32_t Desired) -> uint32_t { + return SrcVal + Desired; + }; - __uint128_t Desired = mcontext->regs[DesiredReg]; - Desired <<= Alignment * 8; + auto CLRDesired = [](uint32_t SrcVal, uint32_t Desired) -> uint32_t { + return SrcVal & ~Desired; + }; - __uint128_t Expected = mcontext->regs[ExpectedReg]; - Expected <<= Alignment * 8; + auto EORDesired = [](uint32_t SrcVal, uint32_t Desired) -> uint32_t { + return SrcVal ^ Desired; + }; - while (1) { - TmpExpected = Atomic128->load(); + auto SETDesired = [](uint32_t SrcVal, uint32_t Desired) -> uint32_t { + return SrcVal | Desired; + }; - // Set up expected - TmpExpected &= NegMask; - TmpExpected |= Expected; + auto SWAPDesired = [](uint32_t SrcVal, uint32_t Desired) -> uint32_t { + return Desired; + }; - // Set up desired - TmpDesired = TmpExpected; - TmpDesired &= NegMask; - TmpDesired |= Desired; + std::function DesiredFunction; - bool CASResult = Atomic128->compare_exchange_strong(TmpExpected, TmpDesired); - if (CASResult) { - // Successful, so we are done - return true; - } - else { - // Not successful - // Now we need to check the results to see if we need to try again - __uint128_t FailedResultOurBits = TmpExpected & Mask; - __uint128_t FailedResultNotOurBits = TmpExpected & NegMask; + switch (Op) { + case ATOMIC_ADD_OP: + DesiredFunction = ADDDesired; + break; + case ATOMIC_CLR_OP: + DesiredFunction = CLRDesired; + break; + case ATOMIC_EOR_OP: + DesiredFunction = EORDesired; + break; + case ATOMIC_SET_OP: + DesiredFunction = SETDesired; + break; + case ATOMIC_SWAP_OP: + DesiredFunction = SWAPDesired; + break; + default: + LogMan::Msg::E("Unhandled JIT SIGBUS Atomic mem op 0x%02x", Op); + return false; + break; + } - __uint128_t FailedDesiredOurBits = TmpDesired & Mask; - __uint128_t FailedDesiredNotOurBits = TmpDesired & NegMask; - if ((FailedResultNotOurBits ^ FailedDesiredNotOurBits) != 0) { - // If the bits changed that weren't part of our regular CAS then we need to try again - continue; - } - if ((FailedResultOurBits ^ FailedDesiredOurBits) != 0) { - // If the bits changed that we were wanting to change then we have failed and can return - // We need to extract the bits and return them in EXPECTED - uint64_t FailedResult = FailedResultOurBits >> (Alignment * 8); - mcontext->regs[ExpectedReg] = FailedResult; - return true; - } - - // If we got here, that means the CAS failed - // NotOurBits didn't change and bits we cared about didn't change - ERROR_AND_DIE("Impossible"); - } + bool Passed = false; + while (!Passed) { + auto Res = DoCAS32( + mcontext->regs[SourceReg], + 0, // Unused + Addr, + NOPExpected, + DesiredFunction); + Passed = std::get<1>(Res); + // If we passed and our destination register is not zero + // Then we need to update the result register with what was in memory + if (Passed && + ResultReg != 31) { + mcontext->regs[ResultReg] = std::get<0>(Res); } } + return true; + } + else if (Size == 8) { + auto NOPExpected = [](uint64_t SrcVal, uint64_t) -> uint64_t { + return SrcVal; + }; + + auto ADDDesired = [](uint64_t SrcVal, uint64_t Desired) -> uint64_t { + return SrcVal + Desired; + }; + + auto CLRDesired = [](uint64_t SrcVal, uint64_t Desired) -> uint64_t { + return SrcVal & ~Desired; + }; + + auto EORDesired = [](uint64_t SrcVal, uint64_t Desired) -> uint64_t { + return SrcVal ^ Desired; + }; + + auto SETDesired = [](uint64_t SrcVal, uint64_t Desired) -> uint64_t { + return SrcVal | Desired; + }; + + auto SWAPDesired = [](uint64_t SrcVal, uint64_t Desired) -> uint64_t { + return Desired; + }; + + std::function DesiredFunction; + + switch (Op) { + case ATOMIC_ADD_OP: + DesiredFunction = ADDDesired; + break; + case ATOMIC_CLR_OP: + DesiredFunction = CLRDesired; + break; + case ATOMIC_EOR_OP: + DesiredFunction = EORDesired; + break; + case ATOMIC_SET_OP: + DesiredFunction = SETDesired; + break; + case ATOMIC_SWAP_OP: + DesiredFunction = SWAPDesired; + break; + default: + LogMan::Msg::E("Unhandled JIT SIGBUS Atomic mem op 0x%02x", Op); + return false; + break; + } + + bool Passed = false; + while (!Passed) { + auto Res = DoCAS64( + mcontext->regs[SourceReg], + 0, // Unused + Addr, + NOPExpected, + DesiredFunction); + Passed = std::get<1>(Res); + // If we passed and our destination register is not zero + // Then we need to update the result register with what was in memory + if (Passed && + ResultReg != 31) { + mcontext->regs[ResultReg] = std::get<0>(Res); + } + } + return true; } return false; diff --git a/External/FEXCore/Source/Interface/Core/ArchHelpers/Arm64.h b/External/FEXCore/Source/Interface/Core/ArchHelpers/Arm64.h index 2519ce156..ed9d4432e 100644 --- a/External/FEXCore/Source/Interface/Core/ArchHelpers/Arm64.h +++ b/External/FEXCore/Source/Interface/Core/ArchHelpers/Arm64.h @@ -9,6 +9,22 @@ namespace FEXCore::ArchHelpers::Arm64 { constexpr uint32_t CASAL_MASK = 0x3F'E0'FC'00; constexpr uint32_t CASAL_INST = 0x08'E0'FC'00; + constexpr uint32_t ATOMIC_MEM_MASK = 0x3B200C00; + constexpr uint32_t ATOMIC_MEM_INST = 0x38200000; + + // Load ops are 4 bits + // Acquire and release bits are independent on the instruction + constexpr uint32_t ATOMIC_ADD_OP = 0b0000; + constexpr uint32_t ATOMIC_CLR_OP = 0b0001; + constexpr uint32_t ATOMIC_EOR_OP = 0b0010; + constexpr uint32_t ATOMIC_SET_OP = 0b0011; + constexpr uint32_t ATOMIC_SMAX_OP = 0b0100; + constexpr uint32_t ATOMIC_SMIN_OP = 0b0101; + constexpr uint32_t ATOMIC_UMAX_OP = 0b0110; + constexpr uint32_t ATOMIC_UMIN_OP = 0b0111; + constexpr uint32_t ATOMIC_SWAP_OP = 0b1000; + bool HandleCASPAL(void *_mcontext, void *_info, uint32_t Instr); bool HandleCASAL(void *_mcontext, void *_info, uint32_t Instr); + bool HandleAtomicMemOp(void *_mcontext, void *_info, uint32_t Instr); } From 55fc47e0b7e54b7f958531850b00b90af8c1bd62 Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Mon, 22 Feb 2021 18:22:10 -0800 Subject: [PATCH 2/4] Passes SIGBUS handler to unaligned atomic op handler for ARMv8.1 ARMv8.0 still unsupported here. This allows us to support unaligned atomic ops for every atomic x86 op that we support. --- .../Interface/Core/Interpreter/InterpreterCore.cpp | 12 ++++++++++++ .../FEXCore/Source/Interface/Core/JIT/Arm64/JIT.cpp | 13 ++++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/External/FEXCore/Source/Interface/Core/Interpreter/InterpreterCore.cpp b/External/FEXCore/Source/Interface/Core/Interpreter/InterpreterCore.cpp index 84f6f66b8..527bed200 100644 --- a/External/FEXCore/Source/Interface/Core/Interpreter/InterpreterCore.cpp +++ b/External/FEXCore/Source/Interface/Core/Interpreter/InterpreterCore.cpp @@ -72,6 +72,18 @@ bool InterpreterCore::HandleSIGBUS(int Signal, void *info, void *ucontext) { return false; } } + else if ((Instr & FEXCore::ArchHelpers::Arm64::ATOMIC_MEM_MASK) == FEXCore::ArchHelpers::Arm64::ATOMIC_MEM_INST) { // Atomic memory op + if (FEXCore::ArchHelpers::Arm64::HandleAtomicMemOp(_mcontext, info, Instr)) { + // Skip this instruction now + _mcontext->pc += 4; + return true; + } + else { + uint8_t Op = (PC[0] >> 12) & 0xF; + LogMan::Msg::E("Unhandled JIT SIGBUS Atomic mem op 0x%02x: PC: %p Instruction: 0x%08x\n", Op, PC, PC[0]); + return false; + } + } #endif return false; } diff --git a/External/FEXCore/Source/Interface/Core/JIT/Arm64/JIT.cpp b/External/FEXCore/Source/Interface/Core/JIT/Arm64/JIT.cpp index d9ea891c8..dfe114e18 100644 --- a/External/FEXCore/Source/Interface/Core/JIT/Arm64/JIT.cpp +++ b/External/FEXCore/Source/Interface/Core/JIT/Arm64/JIT.cpp @@ -685,7 +685,18 @@ bool JITCore::HandleSIGBUS(int Signal, void *info, void *ucontext) { return false; } } - + else if ((Instr & FEXCore::ArchHelpers::Arm64::ATOMIC_MEM_MASK) == FEXCore::ArchHelpers::Arm64::ATOMIC_MEM_INST) { // Atomic memory op + if (FEXCore::ArchHelpers::Arm64::HandleAtomicMemOp(_mcontext, info, Instr)) { + // Skip this instruction now + _mcontext->pc += 4; + return true; + } + else { + uint8_t Op = (PC[0] >> 12) & 0xF; + LogMan::Msg::E("Unhandled JIT SIGBUS Atomic mem op 0x%02x: PC: %p Instruction: 0x%08x\n", Op, PC, PC[0]); + return false; + } + } else { LogMan::Msg::E("Unhandled JIT SIGBUS: PC: %p Instruction: 0x%08x\n", PC, PC[0]); return false; From f848957e177359caa520c0bbdfce879a8d130418 Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Mon, 22 Feb 2021 18:25:25 -0800 Subject: [PATCH 3/4] Implements unit tests for the new unaligned atomics Tests all the unaligned atomic ops we support now --- unittests/ASM/Primary/Primary_01_Atomic16.asm | 52 +++++++++++++++ unittests/ASM/Primary/Primary_01_Atomic32.asm | 49 ++++++++++++++ unittests/ASM/Primary/Primary_01_Atomic64.asm | 46 +++++++++++++ unittests/ASM/Primary/Primary_09_Atomic16.asm | 52 +++++++++++++++ unittests/ASM/Primary/Primary_09_Atomic32.asm | 49 ++++++++++++++ unittests/ASM/Primary/Primary_09_Atomic64.asm | 46 +++++++++++++ unittests/ASM/Primary/Primary_23_Atomic16.asm | 52 +++++++++++++++ unittests/ASM/Primary/Primary_23_Atomic32.asm | 49 ++++++++++++++ unittests/ASM/Primary/Primary_23_Atomic64.asm | 46 +++++++++++++ unittests/ASM/Primary/Primary_29_Atomic16.asm | 52 +++++++++++++++ unittests/ASM/Primary/Primary_29_Atomic32.asm | 49 ++++++++++++++ unittests/ASM/Primary/Primary_29_Atomic64.asm | 46 +++++++++++++ unittests/ASM/Primary/Primary_31_Atomic16.asm | 52 +++++++++++++++ unittests/ASM/Primary/Primary_31_Atomic32.asm | 49 ++++++++++++++ unittests/ASM/Primary/Primary_31_Atomic64.asm | 46 +++++++++++++ unittests/ASM/Primary/Primary_87_Atomic16.asm | 65 +++++++++++++++++++ unittests/ASM/Primary/Primary_87_Atomic32.asm | 59 +++++++++++++++++ unittests/ASM/Primary/Primary_87_Atomic64.asm | 53 +++++++++++++++ .../ASM/Primary/Primary_FF_0_Atomic16.asm | 50 ++++++++++++++ .../ASM/Primary/Primary_FF_0_Atomic32.asm | 49 ++++++++++++++ .../ASM/Primary/Primary_FF_0_Atomic64.asm | 46 +++++++++++++ .../ASM/Primary/Primary_FF_1_Atomic16.asm | 52 +++++++++++++++ .../ASM/Primary/Primary_FF_1_Atomic32.asm | 49 ++++++++++++++ .../ASM/Primary/Primary_FF_1_Atomic64.asm | 46 +++++++++++++ unittests/ASM/TwoByte/0F_C0_Atomic16.asm | 52 +++++++++++++++ unittests/ASM/TwoByte/0F_C0_Atomic32.asm | 49 ++++++++++++++ unittests/ASM/TwoByte/0F_C0_Atomic64.asm | 46 +++++++++++++ 27 files changed, 1351 insertions(+) create mode 100644 unittests/ASM/Primary/Primary_01_Atomic16.asm create mode 100644 unittests/ASM/Primary/Primary_01_Atomic32.asm create mode 100644 unittests/ASM/Primary/Primary_01_Atomic64.asm create mode 100644 unittests/ASM/Primary/Primary_09_Atomic16.asm create mode 100644 unittests/ASM/Primary/Primary_09_Atomic32.asm create mode 100644 unittests/ASM/Primary/Primary_09_Atomic64.asm create mode 100644 unittests/ASM/Primary/Primary_23_Atomic16.asm create mode 100644 unittests/ASM/Primary/Primary_23_Atomic32.asm create mode 100644 unittests/ASM/Primary/Primary_23_Atomic64.asm create mode 100644 unittests/ASM/Primary/Primary_29_Atomic16.asm create mode 100644 unittests/ASM/Primary/Primary_29_Atomic32.asm create mode 100644 unittests/ASM/Primary/Primary_29_Atomic64.asm create mode 100644 unittests/ASM/Primary/Primary_31_Atomic16.asm create mode 100644 unittests/ASM/Primary/Primary_31_Atomic32.asm create mode 100644 unittests/ASM/Primary/Primary_31_Atomic64.asm create mode 100644 unittests/ASM/Primary/Primary_87_Atomic16.asm create mode 100644 unittests/ASM/Primary/Primary_87_Atomic32.asm create mode 100644 unittests/ASM/Primary/Primary_87_Atomic64.asm create mode 100644 unittests/ASM/Primary/Primary_FF_0_Atomic16.asm create mode 100644 unittests/ASM/Primary/Primary_FF_0_Atomic32.asm create mode 100644 unittests/ASM/Primary/Primary_FF_0_Atomic64.asm create mode 100644 unittests/ASM/Primary/Primary_FF_1_Atomic16.asm create mode 100644 unittests/ASM/Primary/Primary_FF_1_Atomic32.asm create mode 100644 unittests/ASM/Primary/Primary_FF_1_Atomic64.asm create mode 100644 unittests/ASM/TwoByte/0F_C0_Atomic16.asm create mode 100644 unittests/ASM/TwoByte/0F_C0_Atomic32.asm create mode 100644 unittests/ASM/TwoByte/0F_C0_Atomic64.asm diff --git a/unittests/ASM/Primary/Primary_01_Atomic16.asm b/unittests/ASM/Primary/Primary_01_Atomic16.asm new file mode 100644 index 000000000..b08ee0594 --- /dev/null +++ b/unittests/ASM/Primary/Primary_01_Atomic16.asm @@ -0,0 +1,52 @@ +%ifdef CONFIG +{ + "RegData": { + "RAX": "0x4242434446464848", + "RBX": "0x4242434445464748", + "RCX": "0x4142434445464748", + "RDX": "0x4142434445464748", + "RSI": "0x4242434445464748", + "RDI": "0x4142434445464748" + } +} +%endif + +mov r15, 0xe0000000 + +mov rax, 0x4142434445464748 +mov [r15 + 8 * 0], rax +mov [r15 + 8 * 1], rax +mov [r15 + 8 * 2], rax +mov [r15 + 8 * 3], rax +mov [r15 + 8 * 4], rax +mov [r15 + 8 * 5], rax +mov [r15 + 8 * 6], rax +mov [r15 + 8 * 7], rax +mov [r15 + 8 * 8], rax +mov [r15 + 8 * 9], rax + +mov rax, 1 + +; Test 1 byte offset within 4byte boundary +lock add word [r15 + 8 * 0 + 1], ax + +; Test 3 byte offset across 4byte boundary +lock add word [r15 + 8 * 0 + 3], ax + +; Test 7 byte offset across 8byte boundary +lock add word [r15 + 8 * 0 + 7], ax + +; Test 15 byte offset across 16byte boundary +lock add word [r15 + 8 * 0 + 15], ax + +; Test 63 byte offset across cacheline boundary +lock add word [r15 + 8 * 0 + 63], ax + +mov rax, qword [r15 + 8 * 0] +mov rbx, qword [r15 + 8 * 1] +mov rcx, qword [r15 + 8 * 2] +mov rdx, qword [r15 + 8 * 3] +mov rsi, qword [r15 + 8 * 7] +mov rdi, qword [r15 + 8 * 8] + +hlt diff --git a/unittests/ASM/Primary/Primary_01_Atomic32.asm b/unittests/ASM/Primary/Primary_01_Atomic32.asm new file mode 100644 index 000000000..83291239d --- /dev/null +++ b/unittests/ASM/Primary/Primary_01_Atomic32.asm @@ -0,0 +1,49 @@ +%ifdef CONFIG +{ + "RegData": { + "RAX": "0x4242434446464748", + "RBX": "0x4242434445464748", + "RCX": "0x4142434445464748", + "RDX": "0x4142434445464748", + "RSI": "0x4242434445464748", + "RDI": "0x4142434445464748" + } +} +%endif + +mov r15, 0xe0000000 + +mov rax, 0x4142434445464748 +mov [r15 + 8 * 0], rax +mov [r15 + 8 * 1], rax +mov [r15 + 8 * 2], rax +mov [r15 + 8 * 3], rax +mov [r15 + 8 * 4], rax +mov [r15 + 8 * 5], rax +mov [r15 + 8 * 6], rax +mov [r15 + 8 * 7], rax +mov [r15 + 8 * 8], rax +mov [r15 + 8 * 9], rax + +mov rax, 1 + +; Test 3 byte offset across 4byte boundary +lock add dword [r15 + 8 * 0 + 3], eax + +; Test 7 byte offset across 8byte boundary +lock add dword [r15 + 8 * 0 + 7], eax + +; Test 15 byte offset across 16byte boundary +lock add dword [r15 + 8 * 0 + 15], eax + +; Test 63 byte offset across cacheline boundary +lock add dword [r15 + 8 * 0 + 63], eax + +mov rax, qword [r15 + 8 * 0] +mov rbx, qword [r15 + 8 * 1] +mov rcx, qword [r15 + 8 * 2] +mov rdx, qword [r15 + 8 * 3] +mov rsi, qword [r15 + 8 * 7] +mov rdi, qword [r15 + 8 * 8] + +hlt diff --git a/unittests/ASM/Primary/Primary_01_Atomic64.asm b/unittests/ASM/Primary/Primary_01_Atomic64.asm new file mode 100644 index 000000000..c18ae720c --- /dev/null +++ b/unittests/ASM/Primary/Primary_01_Atomic64.asm @@ -0,0 +1,46 @@ +%ifdef CONFIG +{ + "RegData": { + "RAX": "0x4242434445464748", + "RBX": "0x4242434445464748", + "RCX": "0x4142434445464748", + "RDX": "0x4142434445464748", + "RSI": "0x4242434445464748", + "RDI": "0x4142434445464748" + } +} +%endif + +mov r15, 0xe0000000 + +mov rax, 0x4142434445464748 +mov [r15 + 8 * 0], rax +mov [r15 + 8 * 1], rax +mov [r15 + 8 * 2], rax +mov [r15 + 8 * 3], rax +mov [r15 + 8 * 4], rax +mov [r15 + 8 * 5], rax +mov [r15 + 8 * 6], rax +mov [r15 + 8 * 7], rax +mov [r15 + 8 * 8], rax +mov [r15 + 8 * 9], rax + +mov rax, 1 + +; Test 7 byte offset across 8byte boundary +lock add qword [r15 + 8 * 0 + 7], rax + +; Test 15 byte offset across 16byte boundary +lock add qword [r15 + 8 * 0 + 15], rax + +; Test 63 byte offset across cacheline boundary +lock add qword [r15 + 8 * 0 + 63], rax + +mov rax, qword [r15 + 8 * 0] +mov rbx, qword [r15 + 8 * 1] +mov rcx, qword [r15 + 8 * 2] +mov rdx, qword [r15 + 8 * 3] +mov rsi, qword [r15 + 8 * 7] +mov rdi, qword [r15 + 8 * 8] + +hlt diff --git a/unittests/ASM/Primary/Primary_09_Atomic16.asm b/unittests/ASM/Primary/Primary_09_Atomic16.asm new file mode 100644 index 000000000..d892cb5be --- /dev/null +++ b/unittests/ASM/Primary/Primary_09_Atomic16.asm @@ -0,0 +1,52 @@ +%ifdef CONFIG +{ + "RegData": { + "RAX": "0x4142434445464748", + "RBX": "0x4142434445464748", + "RCX": "0x4142434445464748", + "RDX": "0x4142434445464748", + "RSI": "0x4142434445464748", + "RDI": "0x4142434445464748" + } +} +%endif + +mov r15, 0xe0000000 + +mov rax, 0x4142434445464748 +mov [r15 + 8 * 0], rax +mov [r15 + 8 * 1], rax +mov [r15 + 8 * 2], rax +mov [r15 + 8 * 3], rax +mov [r15 + 8 * 4], rax +mov [r15 + 8 * 5], rax +mov [r15 + 8 * 6], rax +mov [r15 + 8 * 7], rax +mov [r15 + 8 * 8], rax +mov [r15 + 8 * 9], rax + +mov rax, 1 + +; Test 1 byte offset within 4byte boundary +lock or word [r15 + 8 * 0 + 1], ax + +; Test 3 byte offset across 4byte boundary +lock or word [r15 + 8 * 0 + 3], ax + +; Test 7 byte offset across 8byte boundary +lock or word [r15 + 8 * 0 + 7], ax + +; Test 15 byte offset across 16byte boundary +lock or word [r15 + 8 * 0 + 15], ax + +; Test 63 byte offset across cacheline boundary +lock or word [r15 + 8 * 0 + 63], ax + +mov rax, qword [r15 + 8 * 0] +mov rbx, qword [r15 + 8 * 1] +mov rcx, qword [r15 + 8 * 2] +mov rdx, qword [r15 + 8 * 3] +mov rsi, qword [r15 + 8 * 7] +mov rdi, qword [r15 + 8 * 8] + +hlt diff --git a/unittests/ASM/Primary/Primary_09_Atomic32.asm b/unittests/ASM/Primary/Primary_09_Atomic32.asm new file mode 100644 index 000000000..6773b0896 --- /dev/null +++ b/unittests/ASM/Primary/Primary_09_Atomic32.asm @@ -0,0 +1,49 @@ +%ifdef CONFIG +{ + "RegData": { + "RAX": "0x4142434445464748", + "RBX": "0x4142434445464748", + "RCX": "0x4142434445464748", + "RDX": "0x4142434445464748", + "RSI": "0x4142434445464748", + "RDI": "0x4142434445464748" + } +} +%endif + +mov r15, 0xe0000000 + +mov rax, 0x4142434445464748 +mov [r15 + 8 * 0], rax +mov [r15 + 8 * 1], rax +mov [r15 + 8 * 2], rax +mov [r15 + 8 * 3], rax +mov [r15 + 8 * 4], rax +mov [r15 + 8 * 5], rax +mov [r15 + 8 * 6], rax +mov [r15 + 8 * 7], rax +mov [r15 + 8 * 8], rax +mov [r15 + 8 * 9], rax + +mov rax, 1 + +; Test 3 byte offset across 4byte boundary +lock or dword [r15 + 8 * 0 + 3], eax + +; Test 7 byte offset across 8byte boundary +lock or dword [r15 + 8 * 0 + 7], eax + +; Test 15 byte offset across 16byte boundary +lock or dword [r15 + 8 * 0 + 15], eax + +; Test 63 byte offset across cacheline boundary +lock or dword [r15 + 8 * 0 + 63], eax + +mov rax, qword [r15 + 8 * 0] +mov rbx, qword [r15 + 8 * 1] +mov rcx, qword [r15 + 8 * 2] +mov rdx, qword [r15 + 8 * 3] +mov rsi, qword [r15 + 8 * 7] +mov rdi, qword [r15 + 8 * 8] + +hlt diff --git a/unittests/ASM/Primary/Primary_09_Atomic64.asm b/unittests/ASM/Primary/Primary_09_Atomic64.asm new file mode 100644 index 000000000..0b5dc732c --- /dev/null +++ b/unittests/ASM/Primary/Primary_09_Atomic64.asm @@ -0,0 +1,46 @@ +%ifdef CONFIG +{ + "RegData": { + "RAX": "0x4142434445464748", + "RBX": "0x4142434445464748", + "RCX": "0x4142434445464748", + "RDX": "0x4142434445464748", + "RSI": "0x4142434445464748", + "RDI": "0x4142434445464748" + } +} +%endif + +mov r15, 0xe0000000 + +mov rax, 0x4142434445464748 +mov [r15 + 8 * 0], rax +mov [r15 + 8 * 1], rax +mov [r15 + 8 * 2], rax +mov [r15 + 8 * 3], rax +mov [r15 + 8 * 4], rax +mov [r15 + 8 * 5], rax +mov [r15 + 8 * 6], rax +mov [r15 + 8 * 7], rax +mov [r15 + 8 * 8], rax +mov [r15 + 8 * 9], rax + +mov rax, 1 + +; Test 7 byte offset across 8byte boundary +lock or qword [r15 + 8 * 0 + 7], rax + +; Test 15 byte offset across 16byte boundary +lock or qword [r15 + 8 * 0 + 15], rax + +; Test 63 byte offset across cacheline boundary +lock or qword [r15 + 8 * 0 + 63], rax + +mov rax, qword [r15 + 8 * 0] +mov rbx, qword [r15 + 8 * 1] +mov rcx, qword [r15 + 8 * 2] +mov rdx, qword [r15 + 8 * 3] +mov rsi, qword [r15 + 8 * 7] +mov rdi, qword [r15 + 8 * 8] + +hlt diff --git a/unittests/ASM/Primary/Primary_23_Atomic16.asm b/unittests/ASM/Primary/Primary_23_Atomic16.asm new file mode 100644 index 000000000..f532d871f --- /dev/null +++ b/unittests/ASM/Primary/Primary_23_Atomic16.asm @@ -0,0 +1,52 @@ +%ifdef CONFIG +{ + "RegData": { + "RAX": "0x0142430001000148", + "RBX": "0x0142434445464700", + "RCX": "0x4142434445464700", + "RDX": "0x4142434445464748", + "RSI": "0x0142434445464748", + "RDI": "0x4142434445464700" + } +} +%endif + +mov r15, 0xe0000000 + +mov rax, 0x4142434445464748 +mov [r15 + 8 * 0], rax +mov [r15 + 8 * 1], rax +mov [r15 + 8 * 2], rax +mov [r15 + 8 * 3], rax +mov [r15 + 8 * 4], rax +mov [r15 + 8 * 5], rax +mov [r15 + 8 * 6], rax +mov [r15 + 8 * 7], rax +mov [r15 + 8 * 8], rax +mov [r15 + 8 * 9], rax + +mov rax, 1 + +; Test 1 byte offset within 4byte boundary +lock and word [r15 + 8 * 0 + 1], ax + +; Test 3 byte offset across 4byte boundary +lock and word [r15 + 8 * 0 + 3], ax + +; Test 7 byte offset across 8byte boundary +lock and word [r15 + 8 * 0 + 7], ax + +; Test 15 byte offset across 16byte boundary +lock and word [r15 + 8 * 0 + 15], ax + +; Test 63 byte offset across cacheline boundary +lock and word [r15 + 8 * 0 + 63], ax + +mov rax, qword [r15 + 8 * 0] +mov rbx, qword [r15 + 8 * 1] +mov rcx, qword [r15 + 8 * 2] +mov rdx, qword [r15 + 8 * 3] +mov rsi, qword [r15 + 8 * 7] +mov rdi, qword [r15 + 8 * 8] + +hlt diff --git a/unittests/ASM/Primary/Primary_23_Atomic32.asm b/unittests/ASM/Primary/Primary_23_Atomic32.asm new file mode 100644 index 000000000..40f04df35 --- /dev/null +++ b/unittests/ASM/Primary/Primary_23_Atomic32.asm @@ -0,0 +1,49 @@ +%ifdef CONFIG +{ + "RegData": { + "RAX": "0x0100000001464748", + "RBX": "0x0142434445000000", + "RCX": "0x4142434445000000", + "RDX": "0x4142434445464748", + "RSI": "0x0142434445464748", + "RDI": "0x4142434445000000" + } +} +%endif + +mov r15, 0xe0000000 + +mov rax, 0x4142434445464748 +mov [r15 + 8 * 0], rax +mov [r15 + 8 * 1], rax +mov [r15 + 8 * 2], rax +mov [r15 + 8 * 3], rax +mov [r15 + 8 * 4], rax +mov [r15 + 8 * 5], rax +mov [r15 + 8 * 6], rax +mov [r15 + 8 * 7], rax +mov [r15 + 8 * 8], rax +mov [r15 + 8 * 9], rax + +mov rax, 1 + +; Test 3 byte offset across 4byte boundary +lock and dword [r15 + 8 * 0 + 3], eax + +; Test 7 byte offset across 8byte boundary +lock and dword [r15 + 8 * 0 + 7], eax + +; Test 15 byte offset across 16byte boundary +lock and dword [r15 + 8 * 0 + 15], eax + +; Test 63 byte offset across cacheline boundary +lock and dword [r15 + 8 * 0 + 63], eax + +mov rax, qword [r15 + 8 * 0] +mov rbx, qword [r15 + 8 * 1] +mov rcx, qword [r15 + 8 * 2] +mov rdx, qword [r15 + 8 * 3] +mov rsi, qword [r15 + 8 * 7] +mov rdi, qword [r15 + 8 * 8] + +hlt diff --git a/unittests/ASM/Primary/Primary_23_Atomic64.asm b/unittests/ASM/Primary/Primary_23_Atomic64.asm new file mode 100644 index 000000000..de360b4ec --- /dev/null +++ b/unittests/ASM/Primary/Primary_23_Atomic64.asm @@ -0,0 +1,46 @@ +%ifdef CONFIG +{ + "RegData": { + "RAX": "0x0142434445464748", + "RBX": "0x0100000000000000", + "RCX": "0x4100000000000000", + "RDX": "0x4142434445464748", + "RSI": "0x0142434445464748", + "RDI": "0x4100000000000000" + } +} +%endif + +mov r15, 0xe0000000 + +mov rax, 0x4142434445464748 +mov [r15 + 8 * 0], rax +mov [r15 + 8 * 1], rax +mov [r15 + 8 * 2], rax +mov [r15 + 8 * 3], rax +mov [r15 + 8 * 4], rax +mov [r15 + 8 * 5], rax +mov [r15 + 8 * 6], rax +mov [r15 + 8 * 7], rax +mov [r15 + 8 * 8], rax +mov [r15 + 8 * 9], rax + +mov rax, 1 + +; Test 7 byte offset across 8byte boundary +lock and qword [r15 + 8 * 0 + 7], rax + +; Test 15 byte offset across 16byte boundary +lock and qword [r15 + 8 * 0 + 15], rax + +; Test 63 byte offset across cacheline boundary +lock and qword [r15 + 8 * 0 + 63], rax + +mov rax, qword [r15 + 8 * 0] +mov rbx, qword [r15 + 8 * 1] +mov rcx, qword [r15 + 8 * 2] +mov rdx, qword [r15 + 8 * 3] +mov rsi, qword [r15 + 8 * 7] +mov rdi, qword [r15 + 8 * 8] + +hlt diff --git a/unittests/ASM/Primary/Primary_29_Atomic16.asm b/unittests/ASM/Primary/Primary_29_Atomic16.asm new file mode 100644 index 000000000..5bd4cfe5d --- /dev/null +++ b/unittests/ASM/Primary/Primary_29_Atomic16.asm @@ -0,0 +1,52 @@ +%ifdef CONFIG +{ + "RegData": { + "RAX": "0x4042434444464648", + "RBX": "0x4042434445464748", + "RCX": "0x4142434445464748", + "RDX": "0x4142434445464748", + "RSI": "0x4042434445464748", + "RDI": "0x4142434445464748" + } +} +%endif + +mov r15, 0xe0000000 + +mov rax, 0x4142434445464748 +mov [r15 + 8 * 0], rax +mov [r15 + 8 * 1], rax +mov [r15 + 8 * 2], rax +mov [r15 + 8 * 3], rax +mov [r15 + 8 * 4], rax +mov [r15 + 8 * 5], rax +mov [r15 + 8 * 6], rax +mov [r15 + 8 * 7], rax +mov [r15 + 8 * 8], rax +mov [r15 + 8 * 9], rax + +mov rax, 1 + +; Test 1 byte offset within 4byte boundary +lock sub word [r15 + 8 * 0 + 1], ax + +; Test 3 byte offset across 4byte boundary +lock sub word [r15 + 8 * 0 + 3], ax + +; Test 7 byte offset across 8byte boundary +lock sub word [r15 + 8 * 0 + 7], ax + +; Test 15 byte offset across 16byte boundary +lock sub word [r15 + 8 * 0 + 15], ax + +; Test 63 byte offset across cacheline boundary +lock sub word [r15 + 8 * 0 + 63], ax + +mov rax, qword [r15 + 8 * 0] +mov rbx, qword [r15 + 8 * 1] +mov rcx, qword [r15 + 8 * 2] +mov rdx, qword [r15 + 8 * 3] +mov rsi, qword [r15 + 8 * 7] +mov rdi, qword [r15 + 8 * 8] + +hlt diff --git a/unittests/ASM/Primary/Primary_29_Atomic32.asm b/unittests/ASM/Primary/Primary_29_Atomic32.asm new file mode 100644 index 000000000..cc511da81 --- /dev/null +++ b/unittests/ASM/Primary/Primary_29_Atomic32.asm @@ -0,0 +1,49 @@ +%ifdef CONFIG +{ + "RegData": { + "RAX": "0x4042434444464748", + "RBX": "0x4042434445464748", + "RCX": "0x4142434445464748", + "RDX": "0x4142434445464748", + "RSI": "0x4042434445464748", + "RDI": "0x4142434445464748" + } +} +%endif + +mov r15, 0xe0000000 + +mov rax, 0x4142434445464748 +mov [r15 + 8 * 0], rax +mov [r15 + 8 * 1], rax +mov [r15 + 8 * 2], rax +mov [r15 + 8 * 3], rax +mov [r15 + 8 * 4], rax +mov [r15 + 8 * 5], rax +mov [r15 + 8 * 6], rax +mov [r15 + 8 * 7], rax +mov [r15 + 8 * 8], rax +mov [r15 + 8 * 9], rax + +mov rax, 1 + +; Test 3 byte offset across 4byte boundary +lock sub dword [r15 + 8 * 0 + 3], eax + +; Test 7 byte offset across 8byte boundary +lock sub dword [r15 + 8 * 0 + 7], eax + +; Test 15 byte offset across 16byte boundary +lock sub dword [r15 + 8 * 0 + 15], eax + +; Test 63 byte offset across cacheline boundary +lock sub dword [r15 + 8 * 0 + 63], eax + +mov rax, qword [r15 + 8 * 0] +mov rbx, qword [r15 + 8 * 1] +mov rcx, qword [r15 + 8 * 2] +mov rdx, qword [r15 + 8 * 3] +mov rsi, qword [r15 + 8 * 7] +mov rdi, qword [r15 + 8 * 8] + +hlt diff --git a/unittests/ASM/Primary/Primary_29_Atomic64.asm b/unittests/ASM/Primary/Primary_29_Atomic64.asm new file mode 100644 index 000000000..3612478ec --- /dev/null +++ b/unittests/ASM/Primary/Primary_29_Atomic64.asm @@ -0,0 +1,46 @@ +%ifdef CONFIG +{ + "RegData": { + "RAX": "0x4042434445464748", + "RBX": "0x4042434445464748", + "RCX": "0x4142434445464748", + "RDX": "0x4142434445464748", + "RSI": "0x4042434445464748", + "RDI": "0x4142434445464748" + } +} +%endif + +mov r15, 0xe0000000 + +mov rax, 0x4142434445464748 +mov [r15 + 8 * 0], rax +mov [r15 + 8 * 1], rax +mov [r15 + 8 * 2], rax +mov [r15 + 8 * 3], rax +mov [r15 + 8 * 4], rax +mov [r15 + 8 * 5], rax +mov [r15 + 8 * 6], rax +mov [r15 + 8 * 7], rax +mov [r15 + 8 * 8], rax +mov [r15 + 8 * 9], rax + +mov rax, 1 + +; Test 7 byte offset across 8byte boundary +lock sub qword [r15 + 8 * 0 + 7], rax + +; Test 15 byte offset across 16byte boundary +lock sub qword [r15 + 8 * 0 + 15], rax + +; Test 63 byte offset across cacheline boundary +lock sub qword [r15 + 8 * 0 + 63], rax + +mov rax, qword [r15 + 8 * 0] +mov rbx, qword [r15 + 8 * 1] +mov rcx, qword [r15 + 8 * 2] +mov rdx, qword [r15 + 8 * 3] +mov rsi, qword [r15 + 8 * 7] +mov rdi, qword [r15 + 8 * 8] + +hlt diff --git a/unittests/ASM/Primary/Primary_31_Atomic16.asm b/unittests/ASM/Primary/Primary_31_Atomic16.asm new file mode 100644 index 000000000..a2839c0f7 --- /dev/null +++ b/unittests/ASM/Primary/Primary_31_Atomic16.asm @@ -0,0 +1,52 @@ +%ifdef CONFIG +{ + "RegData": { + "RAX": "0x4042434444464648", + "RBX": "0x4042434445464748", + "RCX": "0x4142434445464748", + "RDX": "0x4142434445464748", + "RSI": "0x4042434445464748", + "RDI": "0x4142434445464748" + } +} +%endif + +mov r15, 0xe0000000 + +mov rax, 0x4142434445464748 +mov [r15 + 8 * 0], rax +mov [r15 + 8 * 1], rax +mov [r15 + 8 * 2], rax +mov [r15 + 8 * 3], rax +mov [r15 + 8 * 4], rax +mov [r15 + 8 * 5], rax +mov [r15 + 8 * 6], rax +mov [r15 + 8 * 7], rax +mov [r15 + 8 * 8], rax +mov [r15 + 8 * 9], rax + +mov rax, 1 + +; Test 1 byte offset within 4byte boundary +lock xor word [r15 + 8 * 0 + 1], ax + +; Test 3 byte offset across 4byte boundary +lock xor word [r15 + 8 * 0 + 3], ax + +; Test 7 byte offset across 8byte boundary +lock xor word [r15 + 8 * 0 + 7], ax + +; Test 15 byte offset across 16byte boundary +lock xor word [r15 + 8 * 0 + 15], ax + +; Test 63 byte offset across cacheline boundary +lock xor word [r15 + 8 * 0 + 63], ax + +mov rax, qword [r15 + 8 * 0] +mov rbx, qword [r15 + 8 * 1] +mov rcx, qword [r15 + 8 * 2] +mov rdx, qword [r15 + 8 * 3] +mov rsi, qword [r15 + 8 * 7] +mov rdi, qword [r15 + 8 * 8] + +hlt diff --git a/unittests/ASM/Primary/Primary_31_Atomic32.asm b/unittests/ASM/Primary/Primary_31_Atomic32.asm new file mode 100644 index 000000000..35fadcfc0 --- /dev/null +++ b/unittests/ASM/Primary/Primary_31_Atomic32.asm @@ -0,0 +1,49 @@ +%ifdef CONFIG +{ + "RegData": { + "RAX": "0x4042434444464748", + "RBX": "0x4042434445464748", + "RCX": "0x4142434445464748", + "RDX": "0x4142434445464748", + "RSI": "0x4042434445464748", + "RDI": "0x4142434445464748" + } +} +%endif + +mov r15, 0xe0000000 + +mov rax, 0x4142434445464748 +mov [r15 + 8 * 0], rax +mov [r15 + 8 * 1], rax +mov [r15 + 8 * 2], rax +mov [r15 + 8 * 3], rax +mov [r15 + 8 * 4], rax +mov [r15 + 8 * 5], rax +mov [r15 + 8 * 6], rax +mov [r15 + 8 * 7], rax +mov [r15 + 8 * 8], rax +mov [r15 + 8 * 9], rax + +mov rax, 1 + +; Test 3 byte offset across 4byte boundary +lock xor dword [r15 + 8 * 0 + 3], eax + +; Test 7 byte offset across 8byte boundary +lock xor dword [r15 + 8 * 0 + 7], eax + +; Test 15 byte offset across 16byte boundary +lock xor dword [r15 + 8 * 0 + 15], eax + +; Test 63 byte offset across cacheline boundary +lock xor dword [r15 + 8 * 0 + 63], eax + +mov rax, qword [r15 + 8 * 0] +mov rbx, qword [r15 + 8 * 1] +mov rcx, qword [r15 + 8 * 2] +mov rdx, qword [r15 + 8 * 3] +mov rsi, qword [r15 + 8 * 7] +mov rdi, qword [r15 + 8 * 8] + +hlt diff --git a/unittests/ASM/Primary/Primary_31_Atomic64.asm b/unittests/ASM/Primary/Primary_31_Atomic64.asm new file mode 100644 index 000000000..e268d35de --- /dev/null +++ b/unittests/ASM/Primary/Primary_31_Atomic64.asm @@ -0,0 +1,46 @@ +%ifdef CONFIG +{ + "RegData": { + "RAX": "0x4042434445464748", + "RBX": "0x4042434445464748", + "RCX": "0x4142434445464748", + "RDX": "0x4142434445464748", + "RSI": "0x4042434445464748", + "RDI": "0x4142434445464748" + } +} +%endif + +mov r15, 0xe0000000 + +mov rax, 0x4142434445464748 +mov [r15 + 8 * 0], rax +mov [r15 + 8 * 1], rax +mov [r15 + 8 * 2], rax +mov [r15 + 8 * 3], rax +mov [r15 + 8 * 4], rax +mov [r15 + 8 * 5], rax +mov [r15 + 8 * 6], rax +mov [r15 + 8 * 7], rax +mov [r15 + 8 * 8], rax +mov [r15 + 8 * 9], rax + +mov rax, 1 + +; Test 7 byte offset across 8byte boundary +lock xor qword [r15 + 8 * 0 + 7], rax + +; Test 15 byte offset across 16byte boundary +lock xor qword [r15 + 8 * 0 + 15], rax + +; Test 63 byte offset across cacheline boundary +lock xor qword [r15 + 8 * 0 + 63], rax + +mov rax, qword [r15 + 8 * 0] +mov rbx, qword [r15 + 8 * 1] +mov rcx, qword [r15 + 8 * 2] +mov rdx, qword [r15 + 8 * 3] +mov rsi, qword [r15 + 8 * 7] +mov rdi, qword [r15 + 8 * 8] + +hlt diff --git a/unittests/ASM/Primary/Primary_87_Atomic16.asm b/unittests/ASM/Primary/Primary_87_Atomic16.asm new file mode 100644 index 000000000..df6c376b7 --- /dev/null +++ b/unittests/ASM/Primary/Primary_87_Atomic16.asm @@ -0,0 +1,65 @@ +%ifdef CONFIG +{ + "RegData": { + "RAX": "0x0142430001000148", + "RBX": "0x0142434445464700", + "RCX": "0x4142434445464700", + "RDX": "0x4142434445464748", + "RSI": "0x0142434445464748", + "RDI": "0x4142434445464700", + "R14": "0x0000000000004647", + "R13": "0x0000000000004445", + "R12": "0x0000000000004841", + "R11": "0x0000000000004841", + "R10": "0x0000000000004841" + } +} +%endif + +mov r15, 0xe0000000 + +mov rax, 0x4142434445464748 +mov [r15 + 8 * 0], rax +mov [r15 + 8 * 1], rax +mov [r15 + 8 * 2], rax +mov [r15 + 8 * 3], rax +mov [r15 + 8 * 4], rax +mov [r15 + 8 * 5], rax +mov [r15 + 8 * 6], rax +mov [r15 + 8 * 7], rax +mov [r15 + 8 * 8], rax +mov [r15 + 8 * 9], rax + +; Test 1 byte offset within 4byte boundary +mov rax, 1 +lock xchg word [r15 + 8 * 0 + 1], ax +mov r14, rax + +; Test 3 byte offset across 4byte boundary +mov rax, 1 +lock xchg word [r15 + 8 * 0 + 3], ax +mov r13, rax + +; Test 7 byte offset across 8byte boundary +mov rax, 1 +lock xchg word [r15 + 8 * 0 + 7], ax +mov r12, rax + +; Test 15 byte offset across 16byte boundary +mov rax, 1 +lock xchg word [r15 + 8 * 0 + 15], ax +mov r11, rax + +; Test 63 byte offset across cacheline boundary +mov rax, 1 +lock xchg word [r15 + 8 * 0 + 63], ax +mov r10, rax + +mov rax, qword [r15 + 8 * 0] +mov rbx, qword [r15 + 8 * 1] +mov rcx, qword [r15 + 8 * 2] +mov rdx, qword [r15 + 8 * 3] +mov rsi, qword [r15 + 8 * 7] +mov rdi, qword [r15 + 8 * 8] + +hlt diff --git a/unittests/ASM/Primary/Primary_87_Atomic32.asm b/unittests/ASM/Primary/Primary_87_Atomic32.asm new file mode 100644 index 000000000..186e925a4 --- /dev/null +++ b/unittests/ASM/Primary/Primary_87_Atomic32.asm @@ -0,0 +1,59 @@ +%ifdef CONFIG +{ + "RegData": { + "RAX": "0x0100000001464748", + "RBX": "0x0142434445000000", + "RCX": "0x4142434445000000", + "RDX": "0x4142434445464748", + "RSI": "0x0142434445464748", + "RDI": "0x4142434445000000", + "R13": "0x0000000042434445", + "R12": "0x0000000046474841", + "R11": "0x0000000046474841", + "R10": "0x0000000046474841" + } +} +%endif + +mov r15, 0xe0000000 + +mov rax, 0x4142434445464748 +mov [r15 + 8 * 0], rax +mov [r15 + 8 * 1], rax +mov [r15 + 8 * 2], rax +mov [r15 + 8 * 3], rax +mov [r15 + 8 * 4], rax +mov [r15 + 8 * 5], rax +mov [r15 + 8 * 6], rax +mov [r15 + 8 * 7], rax +mov [r15 + 8 * 8], rax +mov [r15 + 8 * 9], rax + +; Test 3 byte offset across 4byte boundary +mov rax, 1 +lock xchg dword [r15 + 8 * 0 + 3], eax +mov r13, rax + +; Test 7 byte offset across 8byte boundary +mov rax, 1 +lock xchg dword [r15 + 8 * 0 + 7], eax +mov r12, rax + +; Test 15 byte offset across 16byte boundary +mov rax, 1 +lock xchg dword [r15 + 8 * 0 + 15], eax +mov r11, rax + +; Test 63 byte offset across cacheline boundary +mov rax, 1 +lock xchg dword [r15 + 8 * 0 + 63], eax +mov r10, rax + +mov rax, qword [r15 + 8 * 0] +mov rbx, qword [r15 + 8 * 1] +mov rcx, qword [r15 + 8 * 2] +mov rdx, qword [r15 + 8 * 3] +mov rsi, qword [r15 + 8 * 7] +mov rdi, qword [r15 + 8 * 8] + +hlt diff --git a/unittests/ASM/Primary/Primary_87_Atomic64.asm b/unittests/ASM/Primary/Primary_87_Atomic64.asm new file mode 100644 index 000000000..e887bd305 --- /dev/null +++ b/unittests/ASM/Primary/Primary_87_Atomic64.asm @@ -0,0 +1,53 @@ +%ifdef CONFIG +{ + "RegData": { + "RAX": "0x0142434445464748", + "RBX": "0x0100000000000000", + "RCX": "0x4100000000000000", + "RDX": "0x4142434445464748", + "RSI": "0x0142434445464748", + "RDI": "0x4100000000000000", + "R13": "0x4243444546474841", + "R12": "0x4243444546474841", + "R11": "0x4243444546474841" + } +} +%endif + +mov r15, 0xe0000000 + +mov rax, 0x4142434445464748 +mov [r15 + 8 * 0], rax +mov [r15 + 8 * 1], rax +mov [r15 + 8 * 2], rax +mov [r15 + 8 * 3], rax +mov [r15 + 8 * 4], rax +mov [r15 + 8 * 5], rax +mov [r15 + 8 * 6], rax +mov [r15 + 8 * 7], rax +mov [r15 + 8 * 8], rax +mov [r15 + 8 * 9], rax + +; Test 7 byte offset across 8byte boundary +mov rax, 1 +lock xchg qword [r15 + 8 * 0 + 7], rax +mov r13, rax + +; Test 15 byte offset across 16byte boundary +mov rax, 1 +lock xchg qword [r15 + 8 * 0 + 15], rax +mov r12, rax + +; Test 63 byte offset across cacheline boundary +mov rax, 1 +lock xchg qword [r15 + 8 * 0 + 63], rax +mov r11, rax + +mov rax, qword [r15 + 8 * 0] +mov rbx, qword [r15 + 8 * 1] +mov rcx, qword [r15 + 8 * 2] +mov rdx, qword [r15 + 8 * 3] +mov rsi, qword [r15 + 8 * 7] +mov rdi, qword [r15 + 8 * 8] + +hlt diff --git a/unittests/ASM/Primary/Primary_FF_0_Atomic16.asm b/unittests/ASM/Primary/Primary_FF_0_Atomic16.asm new file mode 100644 index 000000000..05d2e61d3 --- /dev/null +++ b/unittests/ASM/Primary/Primary_FF_0_Atomic16.asm @@ -0,0 +1,50 @@ +%ifdef CONFIG +{ + "RegData": { + "RAX": "0x4242434446464848", + "RBX": "0x4242434445464748", + "RCX": "0x4142434445464748", + "RDX": "0x4142434445464748", + "RSI": "0x4242434445464748", + "RDI": "0x4142434445464748" + } +} +%endif + +mov r15, 0xe0000000 + +mov rax, 0x4142434445464748 +mov [r15 + 8 * 0], rax +mov [r15 + 8 * 1], rax +mov [r15 + 8 * 2], rax +mov [r15 + 8 * 3], rax +mov [r15 + 8 * 4], rax +mov [r15 + 8 * 5], rax +mov [r15 + 8 * 6], rax +mov [r15 + 8 * 7], rax +mov [r15 + 8 * 8], rax +mov [r15 + 8 * 9], rax + +; Test 1 byte offset within 4byte boundary +lock inc word [r15 + 8 * 0 + 1] + +; Test 3 byte offset across 4byte boundary +lock inc word [r15 + 8 * 0 + 3] + +; Test 7 byte offset across 8byte boundary +lock inc word [r15 + 8 * 0 + 7] + +; Test 15 byte offset across 16byte boundary +lock inc word [r15 + 8 * 0 + 15] + +; Test 63 byte offset across cacheline boundary +lock inc word [r15 + 8 * 0 + 63] + +mov rax, qword [r15 + 8 * 0] +mov rbx, qword [r15 + 8 * 1] +mov rcx, qword [r15 + 8 * 2] +mov rdx, qword [r15 + 8 * 3] +mov rsi, qword [r15 + 8 * 7] +mov rdi, qword [r15 + 8 * 8] + +hlt diff --git a/unittests/ASM/Primary/Primary_FF_0_Atomic32.asm b/unittests/ASM/Primary/Primary_FF_0_Atomic32.asm new file mode 100644 index 000000000..f15da0077 --- /dev/null +++ b/unittests/ASM/Primary/Primary_FF_0_Atomic32.asm @@ -0,0 +1,49 @@ +%ifdef CONFIG +{ + "RegData": { + "RAX": "0x4242434446464748", + "RBX": "0x4242434445464748", + "RCX": "0x4142434445464748", + "RDX": "0x4142434445464748", + "RSI": "0x4242434445464748", + "RDI": "0x4142434445464748" + } +} +%endif + +mov r15, 0xe0000000 + +mov rax, 0x4142434445464748 +mov [r15 + 8 * 0], rax +mov [r15 + 8 * 1], rax +mov [r15 + 8 * 2], rax +mov [r15 + 8 * 3], rax +mov [r15 + 8 * 4], rax +mov [r15 + 8 * 5], rax +mov [r15 + 8 * 6], rax +mov [r15 + 8 * 7], rax +mov [r15 + 8 * 8], rax +mov [r15 + 8 * 9], rax + +mov rax, 1 + +; Test 3 byte offset across 4byte boundary +lock inc dword [r15 + 8 * 0 + 3] + +; Test 7 byte offset across 8byte boundary +lock inc dword [r15 + 8 * 0 + 7] + +; Test 15 byte offset across 16byte boundary +lock inc dword [r15 + 8 * 0 + 15] + +; Test 63 byte offset across cacheline boundary +lock inc dword [r15 + 8 * 0 + 63] + +mov rax, qword [r15 + 8 * 0] +mov rbx, qword [r15 + 8 * 1] +mov rcx, qword [r15 + 8 * 2] +mov rdx, qword [r15 + 8 * 3] +mov rsi, qword [r15 + 8 * 7] +mov rdi, qword [r15 + 8 * 8] + +hlt diff --git a/unittests/ASM/Primary/Primary_FF_0_Atomic64.asm b/unittests/ASM/Primary/Primary_FF_0_Atomic64.asm new file mode 100644 index 000000000..099452942 --- /dev/null +++ b/unittests/ASM/Primary/Primary_FF_0_Atomic64.asm @@ -0,0 +1,46 @@ +%ifdef CONFIG +{ + "RegData": { + "RAX": "0x4242434445464748", + "RBX": "0x4242434445464748", + "RCX": "0x4142434445464748", + "RDX": "0x4142434445464748", + "RSI": "0x4242434445464748", + "RDI": "0x4142434445464748" + } +} +%endif + +mov r15, 0xe0000000 + +mov rax, 0x4142434445464748 +mov [r15 + 8 * 0], rax +mov [r15 + 8 * 1], rax +mov [r15 + 8 * 2], rax +mov [r15 + 8 * 3], rax +mov [r15 + 8 * 4], rax +mov [r15 + 8 * 5], rax +mov [r15 + 8 * 6], rax +mov [r15 + 8 * 7], rax +mov [r15 + 8 * 8], rax +mov [r15 + 8 * 9], rax + +mov rax, 1 + +; Test 7 byte offset across 8byte boundary +lock inc qword [r15 + 8 * 0 + 7] + +; Test 15 byte offset across 16byte boundary +lock inc qword [r15 + 8 * 0 + 15] + +; Test 63 byte offset across cacheline boundary +lock inc qword [r15 + 8 * 0 + 63] + +mov rax, qword [r15 + 8 * 0] +mov rbx, qword [r15 + 8 * 1] +mov rcx, qword [r15 + 8 * 2] +mov rdx, qword [r15 + 8 * 3] +mov rsi, qword [r15 + 8 * 7] +mov rdi, qword [r15 + 8 * 8] + +hlt diff --git a/unittests/ASM/Primary/Primary_FF_1_Atomic16.asm b/unittests/ASM/Primary/Primary_FF_1_Atomic16.asm new file mode 100644 index 000000000..6670f736c --- /dev/null +++ b/unittests/ASM/Primary/Primary_FF_1_Atomic16.asm @@ -0,0 +1,52 @@ +%ifdef CONFIG +{ + "RegData": { + "RAX": "0x4042434444464648", + "RBX": "0x4042434445464748", + "RCX": "0x4142434445464748", + "RDX": "0x4142434445464748", + "RSI": "0x4042434445464748", + "RDI": "0x4142434445464748" + } +} +%endif + +mov r15, 0xe0000000 + +mov rax, 0x4142434445464748 +mov [r15 + 8 * 0], rax +mov [r15 + 8 * 1], rax +mov [r15 + 8 * 2], rax +mov [r15 + 8 * 3], rax +mov [r15 + 8 * 4], rax +mov [r15 + 8 * 5], rax +mov [r15 + 8 * 6], rax +mov [r15 + 8 * 7], rax +mov [r15 + 8 * 8], rax +mov [r15 + 8 * 9], rax + +mov rax, 1 + +; Test 1 byte offset within 4byte boundary +lock dec word [r15 + 8 * 0 + 1] + +; Test 3 byte offset across 4byte boundary +lock dec word [r15 + 8 * 0 + 3] + +; Test 7 byte offset across 8byte boundary +lock dec word [r15 + 8 * 0 + 7] + +; Test 15 byte offset across 16byte boundary +lock dec word [r15 + 8 * 0 + 15] + +; Test 63 byte offset across cacheline boundary +lock dec word [r15 + 8 * 0 + 63] + +mov rax, qword [r15 + 8 * 0] +mov rbx, qword [r15 + 8 * 1] +mov rcx, qword [r15 + 8 * 2] +mov rdx, qword [r15 + 8 * 3] +mov rsi, qword [r15 + 8 * 7] +mov rdi, qword [r15 + 8 * 8] + +hlt diff --git a/unittests/ASM/Primary/Primary_FF_1_Atomic32.asm b/unittests/ASM/Primary/Primary_FF_1_Atomic32.asm new file mode 100644 index 000000000..40ed1888c --- /dev/null +++ b/unittests/ASM/Primary/Primary_FF_1_Atomic32.asm @@ -0,0 +1,49 @@ +%ifdef CONFIG +{ + "RegData": { + "RAX": "0x4042434444464748", + "RBX": "0x4042434445464748", + "RCX": "0x4142434445464748", + "RDX": "0x4142434445464748", + "RSI": "0x4042434445464748", + "RDI": "0x4142434445464748" + } +} +%endif + +mov r15, 0xe0000000 + +mov rax, 0x4142434445464748 +mov [r15 + 8 * 0], rax +mov [r15 + 8 * 1], rax +mov [r15 + 8 * 2], rax +mov [r15 + 8 * 3], rax +mov [r15 + 8 * 4], rax +mov [r15 + 8 * 5], rax +mov [r15 + 8 * 6], rax +mov [r15 + 8 * 7], rax +mov [r15 + 8 * 8], rax +mov [r15 + 8 * 9], rax + +mov rax, 1 + +; Test 3 byte offset across 4byte boundary +lock dec dword [r15 + 8 * 0 + 3] + +; Test 7 byte offset across 8byte boundary +lock dec dword [r15 + 8 * 0 + 7] + +; Test 15 byte offset across 16byte boundary +lock dec dword [r15 + 8 * 0 + 15] + +; Test 63 byte offset across cacheline boundary +lock dec dword [r15 + 8 * 0 + 63] + +mov rax, qword [r15 + 8 * 0] +mov rbx, qword [r15 + 8 * 1] +mov rcx, qword [r15 + 8 * 2] +mov rdx, qword [r15 + 8 * 3] +mov rsi, qword [r15 + 8 * 7] +mov rdi, qword [r15 + 8 * 8] + +hlt diff --git a/unittests/ASM/Primary/Primary_FF_1_Atomic64.asm b/unittests/ASM/Primary/Primary_FF_1_Atomic64.asm new file mode 100644 index 000000000..719c06a1c --- /dev/null +++ b/unittests/ASM/Primary/Primary_FF_1_Atomic64.asm @@ -0,0 +1,46 @@ +%ifdef CONFIG +{ + "RegData": { + "RAX": "0x4042434445464748", + "RBX": "0x4042434445464748", + "RCX": "0x4142434445464748", + "RDX": "0x4142434445464748", + "RSI": "0x4042434445464748", + "RDI": "0x4142434445464748" + } +} +%endif + +mov r15, 0xe0000000 + +mov rax, 0x4142434445464748 +mov [r15 + 8 * 0], rax +mov [r15 + 8 * 1], rax +mov [r15 + 8 * 2], rax +mov [r15 + 8 * 3], rax +mov [r15 + 8 * 4], rax +mov [r15 + 8 * 5], rax +mov [r15 + 8 * 6], rax +mov [r15 + 8 * 7], rax +mov [r15 + 8 * 8], rax +mov [r15 + 8 * 9], rax + +mov rax, 1 + +; Test 7 byte offset across 8byte boundary +lock dec qword [r15 + 8 * 0 + 7] + +; Test 15 byte offset across 16byte boundary +lock dec qword [r15 + 8 * 0 + 15] + +; Test 63 byte offset across cacheline boundary +lock dec qword [r15 + 8 * 0 + 63] + +mov rax, qword [r15 + 8 * 0] +mov rbx, qword [r15 + 8 * 1] +mov rcx, qword [r15 + 8 * 2] +mov rdx, qword [r15 + 8 * 3] +mov rsi, qword [r15 + 8 * 7] +mov rdi, qword [r15 + 8 * 8] + +hlt diff --git a/unittests/ASM/TwoByte/0F_C0_Atomic16.asm b/unittests/ASM/TwoByte/0F_C0_Atomic16.asm new file mode 100644 index 000000000..f94f0e3c5 --- /dev/null +++ b/unittests/ASM/TwoByte/0F_C0_Atomic16.asm @@ -0,0 +1,52 @@ +%ifdef CONFIG +{ + "RegData": { + "RAX": "0x8642438a8c464848", + "RBX": "0x824243444546478c", + "RCX": "0x4142434445464790", + "RDX": "0x4142434445464748", + "RSI": "0x8242434445464748", + "RDI": "0x4142434445464790" + } +} +%endif + +mov r15, 0xe0000000 + +mov rax, 0x4142434445464748 +mov [r15 + 8 * 0], rax +mov [r15 + 8 * 1], rax +mov [r15 + 8 * 2], rax +mov [r15 + 8 * 3], rax +mov [r15 + 8 * 4], rax +mov [r15 + 8 * 5], rax +mov [r15 + 8 * 6], rax +mov [r15 + 8 * 7], rax +mov [r15 + 8 * 8], rax +mov [r15 + 8 * 9], rax + +mov rax, 1 + +; Test 1 byte offset within 4byte boundary +lock xadd word [r15 + 8 * 0 + 1], ax + +; Test 3 byte offset across 4byte boundary +lock xadd word [r15 + 8 * 0 + 3], ax + +; Test 7 byte offset across 8byte boundary +lock xadd word [r15 + 8 * 0 + 7], ax + +; Test 15 byte offset across 16byte boundary +lock xadd word [r15 + 8 * 0 + 15], ax + +; Test 63 byte offset across cacheline boundary +lock xadd word [r15 + 8 * 0 + 63], ax + +mov rax, qword [r15 + 8 * 0] +mov rbx, qword [r15 + 8 * 1] +mov rcx, qword [r15 + 8 * 2] +mov rdx, qword [r15 + 8 * 3] +mov rsi, qword [r15 + 8 * 7] +mov rdi, qword [r15 + 8 * 8] + +hlt diff --git a/unittests/ASM/TwoByte/0F_C0_Atomic32.asm b/unittests/ASM/TwoByte/0F_C0_Atomic32.asm new file mode 100644 index 000000000..2e1ca03ff --- /dev/null +++ b/unittests/ASM/TwoByte/0F_C0_Atomic32.asm @@ -0,0 +1,49 @@ +%ifdef CONFIG +{ + "RegData": { + "RAX": "0x8642434446464748", + "RBX": "0x8242434445888a8c", + "RCX": "0x41424344458c8e90", + "RDX": "0x4142434445464748", + "RSI": "0x8242434445464748", + "RDI": "0x41424344458c8e90" + } +} +%endif + +mov r15, 0xe0000000 + +mov rax, 0x4142434445464748 +mov [r15 + 8 * 0], rax +mov [r15 + 8 * 1], rax +mov [r15 + 8 * 2], rax +mov [r15 + 8 * 3], rax +mov [r15 + 8 * 4], rax +mov [r15 + 8 * 5], rax +mov [r15 + 8 * 6], rax +mov [r15 + 8 * 7], rax +mov [r15 + 8 * 8], rax +mov [r15 + 8 * 9], rax + +mov rax, 1 + +; Test 3 byte offset across 4byte boundary +lock xadd dword [r15 + 8 * 0 + 3], eax + +; Test 7 byte offset across 8byte boundary +lock xadd dword [r15 + 8 * 0 + 7], eax + +; Test 15 byte offset across 16byte boundary +lock xadd dword [r15 + 8 * 0 + 15], eax + +; Test 63 byte offset across cacheline boundary +lock xadd dword [r15 + 8 * 0 + 63], eax + +mov rax, qword [r15 + 8 * 0] +mov rbx, qword [r15 + 8 * 1] +mov rcx, qword [r15 + 8 * 2] +mov rdx, qword [r15 + 8 * 3] +mov rsi, qword [r15 + 8 * 7] +mov rdi, qword [r15 + 8 * 8] + +hlt diff --git a/unittests/ASM/TwoByte/0F_C0_Atomic64.asm b/unittests/ASM/TwoByte/0F_C0_Atomic64.asm new file mode 100644 index 000000000..ea119ad39 --- /dev/null +++ b/unittests/ASM/TwoByte/0F_C0_Atomic64.asm @@ -0,0 +1,46 @@ +%ifdef CONFIG +{ + "RegData": { + "RAX": "0x4242434445464748", + "RBX": "0x8242434445464748", + "RCX": "0x418486888a8c8e90", + "RDX": "0x4142434445464748", + "RSI": "0x8242434445464748", + "RDI": "0x418486888a8c8e90" + } +} +%endif + +mov r15, 0xe0000000 + +mov rax, 0x4142434445464748 +mov [r15 + 8 * 0], rax +mov [r15 + 8 * 1], rax +mov [r15 + 8 * 2], rax +mov [r15 + 8 * 3], rax +mov [r15 + 8 * 4], rax +mov [r15 + 8 * 5], rax +mov [r15 + 8 * 6], rax +mov [r15 + 8 * 7], rax +mov [r15 + 8 * 8], rax +mov [r15 + 8 * 9], rax + +mov rax, 1 + +; Test 7 byte offset across 8byte boundary +lock xadd qword [r15 + 8 * 0 + 7], rax + +; Test 15 byte offset across 16byte boundary +lock xadd qword [r15 + 8 * 0 + 15], rax + +; Test 63 byte offset across cacheline boundary +lock xadd qword [r15 + 8 * 0 + 63], rax + +mov rax, qword [r15 + 8 * 0] +mov rbx, qword [r15 + 8 * 1] +mov rcx, qword [r15 + 8 * 2] +mov rdx, qword [r15 + 8 * 3] +mov rsi, qword [r15 + 8 * 7] +mov rdi, qword [r15 + 8 * 8] + +hlt From f1349becb4af50609ac74590f602845abdc64ae7 Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Mon, 22 Feb 2021 18:33:17 -0800 Subject: [PATCH 4/4] Disables the unaligned atomic memory op tests These don't run on the armv8.0 runner --- unittests/ASM/Disabled_Tests | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/unittests/ASM/Disabled_Tests b/unittests/ASM/Disabled_Tests index f03e3cbf7..ba0376f78 100644 --- a/unittests/ASM/Disabled_Tests +++ b/unittests/ASM/Disabled_Tests @@ -34,3 +34,32 @@ Test_TwoByte/0F_B0_7.asm Test_Secondary/09_XX_01_7.asm Test_Secondary/09_XX_01_8.asm Test_Secondary/09_XX_01_9.asm + +# Doesn't support unaligned atomic memory ops on armv8.0 +Test_Primary/Primary_01_Atomic16.asm +Test_Primary/Primary_01_Atomic32.asm +Test_Primary/Primary_01_Atomic64.asm +Test_Primary/Primary_09_Atomic16.asm +Test_Primary/Primary_09_Atomic32.asm +Test_Primary/Primary_09_Atomic64.asm +Test_Primary/Primary_23_Atomic16.asm +Test_Primary/Primary_23_Atomic32.asm +Test_Primary/Primary_23_Atomic64.asm +Test_Primary/Primary_29_Atomic16.asm +Test_Primary/Primary_29_Atomic32.asm +Test_Primary/Primary_29_Atomic64.asm +Test_Primary/Primary_31_Atomic16.asm +Test_Primary/Primary_31_Atomic32.asm +Test_Primary/Primary_31_Atomic64.asm +Test_Primary/Primary_87_Atomic16.asm +Test_Primary/Primary_87_Atomic32.asm +Test_Primary/Primary_87_Atomic64.asm +Test_Primary/Primary_FF_0_Atomic16.asm +Test_Primary/Primary_FF_0_Atomic32.asm +Test_Primary/Primary_FF_0_Atomic64.asm +Test_Primary/Primary_FF_1_Atomic16.asm +Test_Primary/Primary_FF_1_Atomic32.asm +Test_Primary/Primary_FF_1_Atomic64.asm +Test_TwoByte/0F_C0_Atomic16.asm +Test_TwoByte/0F_C0_Atomic32.asm +Test_TwoByte/0F_C0_Atomic64.asm