// Copyright 2016, VIXL authors // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // * Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // * Neither the name of ARM Limited nor the names of its contributors may be // used to endorse or promote products derived from this software without // specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <stdlib.h> #include "test-runner.h" #ifdef VIXL_INCLUDE_TARGET_AARCH32 #include "aarch32/macro-assembler-aarch32.h" #endif #ifdef VIXL_INCLUDE_TARGET_AARCH64 #include "aarch64/macro-assembler-aarch64.h" #endif #define STRINGIFY(x) #x #define TEST_AARCH32(Name) \ namespace aarch32 { \ void Test_##Name##_AArch32_Impl(); \ } \ void Test_##Name##_AArch32() { aarch32::Test_##Name##_AArch32_Impl(); } \ Test test_##Name##_AArch32(STRINGIFY(AARCH32_SCRATCH_##Name), \ &Test_##Name##_AArch32); \ void aarch32::Test_##Name##_AArch32_Impl() #define TEST_AARCH64(Name) \ namespace aarch64 { \ void Test_##Name##_AArch64_Impl(); \ } \ void Test_##Name##_AArch64() { aarch64::Test_##Name##_AArch64_Impl(); } \ Test test_##Name##_AArch64(STRINGIFY(AARCH64_SCRATCH_##Name), \ &Test_##Name##_AArch64); \ void aarch64::Test_##Name##_AArch64_Impl() #define SETUP() MacroAssembler masm #define TEARDOWN() #define __ masm. namespace vixl { // UseScratchRegisterScopes must be able to nest perfectly. That is, they may // nest, but nested scopes must not outlive less-nested scopes. template <typename MacroAssembler, typename UseScratchRegisterScope> class PerfectNestingTestHelper { public: explicit PerfectNestingTestHelper(MacroAssembler* masm) : masm_(masm) { uint16_t seed[3] = {4, 5, 6}; seed48(seed); } void Run() { UseScratchRegisterScope* top_scope = masm_->GetCurrentScratchRegisterScope(); int descendents = 0; while (descendents < kMinimumDescendentScopeCount) descendents += Run(0); VIXL_CHECK(masm_->GetCurrentScratchRegisterScope() == top_scope); } private: int Run(int depth) { // As the depth increases, the probability of recursion decreases. // At depth = kDepthLimit, we never recurse. int max_children = static_cast<int>(std::abs(mrand48()) % kDepthLimit); int children = std::max(0, max_children - depth); int descendents = children; while (children-- > 0) { UseScratchRegisterScope scope(masm_); VIXL_CHECK(masm_->GetCurrentScratchRegisterScope() == &scope); descendents += Run(depth + 1); VIXL_CHECK(masm_->GetCurrentScratchRegisterScope() == &scope); } return descendents; } MacroAssembler* masm_; static const int kDepthLimit = 12; static const int kMinimumDescendentScopeCount = 10000; }; #ifdef VIXL_INCLUDE_TARGET_AARCH32 TEST_AARCH32(perfect_nesting) { SETUP(); PerfectNestingTestHelper<MacroAssembler, UseScratchRegisterScope>(&masm) .Run(); TEARDOWN(); } #endif // VIXL_INCLUDE_TARGET_AARCH32 #ifdef VIXL_INCLUDE_TARGET_AARCH64 TEST_AARCH64(perfect_nesting) { SETUP(); PerfectNestingTestHelper<MacroAssembler, UseScratchRegisterScope>(&masm) .Run(); TEARDOWN(); } #endif // VIXL_INCLUDE_TARGET_AARCH64 #ifdef VIXL_INCLUDE_TARGET_AARCH32 TEST_AARCH32(v_registers) { SETUP(); { UseScratchRegisterScope temps(&masm); temps.Include(VRegisterList(q0, q1, q2, q3)); // This test assumes that low-numbered registers are allocated first. The // implementation is allowed to use a different strategy; if it does, the // test will need to be updated. // TODO: Write more flexible (and thorough) tests. VIXL_CHECK(q0.Is(temps.AcquireQ())); VIXL_CHECK(!temps.IsAvailable(q0)); VIXL_CHECK(!temps.IsAvailable(d0)); VIXL_CHECK(!temps.IsAvailable(d1)); VIXL_CHECK(!temps.IsAvailable(s0)); VIXL_CHECK(!temps.IsAvailable(s1)); VIXL_CHECK(!temps.IsAvailable(s2)); VIXL_CHECK(!temps.IsAvailable(s3)); VIXL_CHECK(d2.Is(temps.AcquireV(64))); VIXL_CHECK(!temps.IsAvailable(q1)); VIXL_CHECK(!temps.IsAvailable(d2)); VIXL_CHECK(temps.IsAvailable(d3)); VIXL_CHECK(!temps.IsAvailable(s4)); VIXL_CHECK(!temps.IsAvailable(s5)); VIXL_CHECK(temps.IsAvailable(s6)); VIXL_CHECK(temps.IsAvailable(s7)); VIXL_CHECK(s6.Is(temps.AcquireS())); VIXL_CHECK(!temps.IsAvailable(d3)); VIXL_CHECK(!temps.IsAvailable(s6)); VIXL_CHECK(temps.IsAvailable(s7)); VIXL_CHECK(q2.Is(temps.AcquireV(128))); VIXL_CHECK(!temps.IsAvailable(q2)); VIXL_CHECK(!temps.IsAvailable(d4)); VIXL_CHECK(!temps.IsAvailable(d5)); VIXL_CHECK(!temps.IsAvailable(s8)); VIXL_CHECK(!temps.IsAvailable(s9)); VIXL_CHECK(!temps.IsAvailable(s10)); VIXL_CHECK(!temps.IsAvailable(s11)); VIXL_CHECK(temps.IsAvailable(s7)); VIXL_CHECK(d6.Is(temps.AcquireD())); VIXL_CHECK(!temps.IsAvailable(q3)); VIXL_CHECK(!temps.IsAvailable(d6)); VIXL_CHECK(temps.IsAvailable(d7)); VIXL_CHECK(!temps.IsAvailable(s12)); VIXL_CHECK(!temps.IsAvailable(s13)); VIXL_CHECK(temps.IsAvailable(s14)); VIXL_CHECK(temps.IsAvailable(s15)); VIXL_CHECK(temps.IsAvailable(s7)); VIXL_CHECK(s7.Is(temps.AcquireS())); } TEARDOWN(); } #endif // VIXL_INCLUDE_TARGET_AARCH32 #ifdef VIXL_INCLUDE_TARGET_AARCH32 TEST_AARCH32(include_exclude) { SETUP(); { UseScratchRegisterScope temps(&masm); temps.Include(r0, r1, r2, r3); temps.Include(s0, s1, d1, q1); VIXL_CHECK(temps.IsAvailable(r0)); VIXL_CHECK(temps.IsAvailable(r1)); VIXL_CHECK(temps.IsAvailable(r2)); VIXL_CHECK(temps.IsAvailable(r3)); VIXL_CHECK(temps.IsAvailable(s0)); VIXL_CHECK(temps.IsAvailable(s1)); VIXL_CHECK(temps.IsAvailable(d1)); VIXL_CHECK(temps.IsAvailable(s2)); VIXL_CHECK(temps.IsAvailable(s3)); VIXL_CHECK(temps.IsAvailable(q1)); VIXL_CHECK(temps.IsAvailable(d2)); VIXL_CHECK(temps.IsAvailable(d3)); VIXL_CHECK(temps.IsAvailable(s4)); VIXL_CHECK(temps.IsAvailable(s5)); VIXL_CHECK(temps.IsAvailable(s6)); VIXL_CHECK(temps.IsAvailable(s7)); // Test local exclusion. { UseScratchRegisterScope local_temps(&masm); local_temps.Exclude(r1, r2); local_temps.Exclude(s1, q1); VIXL_CHECK(temps.IsAvailable(r0)); VIXL_CHECK(!temps.IsAvailable(r1)); VIXL_CHECK(!temps.IsAvailable(r2)); VIXL_CHECK(temps.IsAvailable(r3)); VIXL_CHECK(temps.IsAvailable(s0)); VIXL_CHECK(!temps.IsAvailable(s1)); VIXL_CHECK(temps.IsAvailable(d1)); VIXL_CHECK(temps.IsAvailable(s2)); VIXL_CHECK(temps.IsAvailable(s3)); VIXL_CHECK(!temps.IsAvailable(q1)); VIXL_CHECK(!temps.IsAvailable(d2)); VIXL_CHECK(!temps.IsAvailable(d3)); VIXL_CHECK(!temps.IsAvailable(s4)); VIXL_CHECK(!temps.IsAvailable(s5)); VIXL_CHECK(!temps.IsAvailable(s6)); VIXL_CHECK(!temps.IsAvailable(s7)); } // This time, exclude part of included registers, making sure the entire // register gets excluded. { UseScratchRegisterScope local_temps(&masm); local_temps.Exclude(s2, d3); VIXL_CHECK(temps.IsAvailable(r0)); VIXL_CHECK(temps.IsAvailable(r1)); VIXL_CHECK(temps.IsAvailable(r2)); VIXL_CHECK(temps.IsAvailable(r3)); VIXL_CHECK(temps.IsAvailable(s0)); VIXL_CHECK(temps.IsAvailable(s1)); // Excluding s2 should exclude d1 but not s3. VIXL_CHECK(!temps.IsAvailable(d1)); VIXL_CHECK(!temps.IsAvailable(s2)); VIXL_CHECK(temps.IsAvailable(s3)); // Excluding d3 should exclude q1, s7 and s6 but not d2, s5, s4. VIXL_CHECK(!temps.IsAvailable(q1)); VIXL_CHECK(temps.IsAvailable(d2)); VIXL_CHECK(!temps.IsAvailable(d3)); VIXL_CHECK(temps.IsAvailable(s4)); VIXL_CHECK(temps.IsAvailable(s5)); VIXL_CHECK(!temps.IsAvailable(s6)); VIXL_CHECK(!temps.IsAvailable(s7)); } // Make sure the initial state was restored. VIXL_CHECK(temps.IsAvailable(r0)); VIXL_CHECK(temps.IsAvailable(r1)); VIXL_CHECK(temps.IsAvailable(r2)); VIXL_CHECK(temps.IsAvailable(r3)); VIXL_CHECK(temps.IsAvailable(s0)); VIXL_CHECK(temps.IsAvailable(s1)); VIXL_CHECK(temps.IsAvailable(d1)); VIXL_CHECK(temps.IsAvailable(s2)); VIXL_CHECK(temps.IsAvailable(s3)); VIXL_CHECK(temps.IsAvailable(q1)); VIXL_CHECK(temps.IsAvailable(d2)); VIXL_CHECK(temps.IsAvailable(d3)); VIXL_CHECK(temps.IsAvailable(s4)); VIXL_CHECK(temps.IsAvailable(s5)); VIXL_CHECK(temps.IsAvailable(s6)); VIXL_CHECK(temps.IsAvailable(s7)); } TEARDOWN(); } #endif // VIXL_INCLUDE_TARGET_AARCH32 } // namespace vixl