// Copyright 2019, 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. // Test infrastructure. // // Tests are functions which accept no parameters and have no return values. // The testing code should not perform an explicit return once completed. For // example to test the mov immediate instruction a very simple test would be: // // TEST(mov_x0_one) { // SETUP(); // // START(); // __ mov(x0, 1); // END(); // // if (CAN_RUN()) { // RUN(); // // ASSERT_EQUAL_64(1, x0); // } // } // // Within a START ... END block all registers but sp can be modified. sp has to // be explicitly saved/restored. The END() macro replaces the function return // so it may appear multiple times in a test if the test has multiple exit // points. // // Tests requiring specific CPU features should specify exactly what they // require using SETUP_WITH_FEATURES(...) instead of SETUP(). // // Once the test has been run all integer and floating point registers as well // as flags are accessible through a RegisterDump instance, see // utils-aarch64.cc for more info on RegisterDump. // // We provide some helper assert to handle common cases: // // ASSERT_EQUAL_32(int32_t, int_32t) // ASSERT_EQUAL_FP32(float, float) // ASSERT_EQUAL_32(int32_t, W register) // ASSERT_EQUAL_FP32(float, S register) // ASSERT_EQUAL_64(int64_t, int_64t) // ASSERT_EQUAL_FP64(double, double) // ASSERT_EQUAL_64(int64_t, X register) // ASSERT_EQUAL_64(X register, X register) // ASSERT_EQUAL_FP64(double, D register) // // e.g. ASSERT_EQUAL_64(0.5, d30); // // If more advanced computation is required before the assert then access the // RegisterDump named core directly: // // ASSERT_EQUAL_64(0x1234, core->reg_x0() & 0xffff); namespace vixl { namespace aarch64 { #define __ masm. #define TEST(name) TEST_(AARCH64_ASM_##name) #ifdef VIXL_INCLUDE_SIMULATOR_AARCH64 // Run tests with the simulator. #define SETUP() \ MacroAssembler masm; \ SETUP_COMMON(); \ SETUP_COMMON_SIM() #define SETUP_WITH_FEATURES(...) \ MacroAssembler masm; \ SETUP_COMMON(); \ SETUP_COMMON_SIM(); \ masm.SetCPUFeatures(CPUFeatures(__VA_ARGS__)); \ simulator.SetCPUFeatures(CPUFeatures(__VA_ARGS__)) #define SETUP_CUSTOM(size, pic) \ MacroAssembler masm(size + CodeBuffer::kDefaultCapacity, pic); \ SETUP_COMMON(); \ SETUP_COMMON_SIM() #define SETUP_CUSTOM_SIM(...) \ MacroAssembler masm; \ SETUP_COMMON(); \ Simulator simulator(&simulator_decoder, stdout, __VA_ARGS__); \ simulator.SetColouredTrace(Test::coloured_trace()); \ simulator.SetCPUFeatures(CPUFeatures::None()) #define SETUP_COMMON() \ bool queried_can_run = false; \ bool printed_sve_lane_warning = false; \ /* Avoid unused-variable warnings in case a test never calls RUN(). */ \ USE(queried_can_run); \ USE(printed_sve_lane_warning); \ masm.SetCPUFeatures(CPUFeatures::None()); \ masm.SetGenerateSimulatorCode(true); \ Decoder simulator_decoder; \ RegisterDump core; \ ptrdiff_t offset_after_infrastructure_start; \ ptrdiff_t offset_before_infrastructure_end #define SETUP_COMMON_SIM() \ Simulator simulator(&simulator_decoder); \ simulator.SetColouredTrace(Test::coloured_trace()); \ simulator.SetCPUFeatures(CPUFeatures::None()) #define START() \ masm.Reset(); \ simulator.ResetState(); \ { \ SimulationCPUFeaturesScope cpu(&masm, kInfrastructureCPUFeatures); \ __ PushCalleeSavedRegisters(); \ } \ { \ int trace_parameters = 0; \ if (Test::trace_reg()) trace_parameters |= LOG_STATE; \ if (Test::trace_write()) trace_parameters |= LOG_WRITE; \ if (Test::trace_sim()) trace_parameters |= LOG_DISASM; \ if (Test::trace_branch()) trace_parameters |= LOG_BRANCH; \ if (trace_parameters != 0) { \ __ Trace(static_cast(trace_parameters), TRACE_ENABLE); \ } \ } \ offset_after_infrastructure_start = masm.GetCursorOffset(); \ /* Avoid unused-variable warnings in case a test never calls RUN(). */ \ USE(offset_after_infrastructure_start) #define END() \ offset_before_infrastructure_end = masm.GetCursorOffset(); \ /* Avoid unused-variable warnings in case a test never calls RUN(). */ \ USE(offset_before_infrastructure_end); \ __ Trace(LOG_ALL, TRACE_DISABLE); \ { \ SimulationCPUFeaturesScope cpu(&masm, kInfrastructureCPUFeatures); \ core.Dump(&masm); \ __ PopCalleeSavedRegisters(); \ } \ __ Ret(); \ masm.FinalizeCode() #define RUN() \ RUN_WITHOUT_SEEN_FEATURE_CHECK(); \ { \ /* We expect the test to use all of the features it requested, plus the */ \ /* features that the instructure code requires. */ \ CPUFeatures const& expected = \ simulator.GetCPUFeatures()->With(CPUFeatures::kNEON); \ CPUFeatures const& seen = simulator.GetSeenFeatures(); \ /* This gives three broad categories of features that we care about: */ \ /* 1. Things both expected and seen. */ \ /* 2. Things seen, but not expected. The simulator catches these. */ \ /* 3. Things expected, but not seen. We check these here. */ \ /* In a valid, passing test, categories 2 and 3 should be empty. */ \ if (seen != expected) { \ /* The Simulator should have caught anything in category 2 already. */ \ VIXL_ASSERT(expected.Has(seen)); \ /* Anything left is category 3: things expected, but not seen. This */ \ /* is not necessarily a bug in VIXL itself, but indicates that the */ \ /* test is less strict than it could be. */ \ CPUFeatures missing = expected.Without(seen); \ VIXL_ASSERT(missing.Count() > 0); \ std::cout << "Error: expected to see CPUFeatures { " << missing \ << " }\n"; \ VIXL_ABORT(); \ } \ } #define RUN_WITHOUT_SEEN_FEATURE_CHECK() \ DISASSEMBLE(); \ VIXL_ASSERT(QUERIED_CAN_RUN()); \ VIXL_ASSERT(CAN_RUN()); \ simulator.RunFrom(masm.GetBuffer()->GetStartAddress()) #else // ifdef VIXL_INCLUDE_SIMULATOR_AARCH64. #define SETUP() \ MacroAssembler masm; \ SETUP_COMMON() #define SETUP_WITH_FEATURES(...) \ MacroAssembler masm; \ SETUP_COMMON(); \ masm.SetCPUFeatures(CPUFeatures(__VA_ARGS__)) #define SETUP_CUSTOM(size, pic) \ size_t buffer_size = size + CodeBuffer::kDefaultCapacity; \ MacroAssembler masm(buffer_size, pic); \ SETUP_COMMON() #define SETUP_COMMON() \ bool queried_can_run = false; \ bool printed_sve_lane_warning = false; \ /* Avoid unused-variable warnings in case a test never calls RUN(). */ \ USE(queried_can_run); \ USE(printed_sve_lane_warning); \ masm.SetCPUFeatures(CPUFeatures::None()); \ masm.SetGenerateSimulatorCode(false); \ RegisterDump core; \ CPU::SetUp(); \ ptrdiff_t offset_after_infrastructure_start; \ ptrdiff_t offset_before_infrastructure_end #define START() \ masm.Reset(); \ { \ CPUFeaturesScope cpu(&masm, kInfrastructureCPUFeatures); \ __ PushCalleeSavedRegisters(); \ } \ offset_after_infrastructure_start = masm.GetCursorOffset(); \ /* Avoid unused-variable warnings in case a test never calls RUN(). */ \ USE(offset_after_infrastructure_start) #define END() \ offset_before_infrastructure_end = masm.GetCursorOffset(); \ /* Avoid unused-variable warnings in case a test never calls RUN(). */ \ USE(offset_before_infrastructure_end); \ { \ CPUFeaturesScope cpu(&masm, kInfrastructureCPUFeatures); \ core.Dump(&masm); \ __ PopCalleeSavedRegisters(); \ } \ __ Ret(); \ masm.FinalizeCode() // Execute the generated code from the memory area. #define RUN() \ DISASSEMBLE(); \ VIXL_ASSERT(QUERIED_CAN_RUN()); \ VIXL_ASSERT(CAN_RUN()); \ masm.GetBuffer()->SetExecutable(); \ ExecuteMemory(masm.GetBuffer()->GetStartAddress(), \ masm.GetSizeOfCodeGenerated()); \ masm.GetBuffer()->SetWritable() // This just provides compatibility with VIXL_INCLUDE_SIMULATOR_AARCH64 builds. // We cannot run seen-feature checks when running natively. #define RUN_WITHOUT_SEEN_FEATURE_CHECK() RUN() #endif // ifdef VIXL_INCLUDE_SIMULATOR_AARCH64. #define CAN_RUN() CanRun(*masm.GetCPUFeatures(), &queried_can_run) #define QUERIED_CAN_RUN() (queried_can_run) #define DISASSEMBLE() \ if (Test::disassemble()) { \ PrintDisassembler disasm(stdout); \ CodeBuffer* buffer = masm.GetBuffer(); \ Instruction* start = buffer->GetOffsetAddress( \ offset_after_infrastructure_start); \ Instruction* end = buffer->GetOffsetAddress( \ offset_before_infrastructure_end); \ \ if (Test::disassemble_infrastructure()) { \ Instruction* infra_start = buffer->GetStartAddress(); \ printf("# Infrastructure code (prologue)\n"); \ disasm.DisassembleBuffer(infra_start, start); \ printf("# Test code\n"); \ } else { \ printf( \ "# Warning: Omitting infrastructure code. " \ "Use --disassemble to see it.\n"); \ } \ \ disasm.DisassembleBuffer(start, end); \ \ if (Test::disassemble_infrastructure()) { \ printf("# Infrastructure code (epilogue)\n"); \ Instruction* infra_end = buffer->GetEndAddress(); \ disasm.DisassembleBuffer(end, infra_end); \ } \ } #define ASSERT_EQUAL_NZCV(expected) \ VIXL_CHECK(EqualNzcv(expected, core.flags_nzcv())) #define ASSERT_EQUAL_REGISTERS(expected) \ VIXL_CHECK(EqualRegisters(&expected, &core)) #define ASSERT_EQUAL_FP16(expected, result) \ VIXL_CHECK(EqualFP16(expected, &core, result)) #define ASSERT_EQUAL_32(expected, result) \ VIXL_CHECK(Equal32(static_cast(expected), &core, result)) #define ASSERT_EQUAL_FP32(expected, result) \ VIXL_CHECK(EqualFP32(expected, &core, result)) #define ASSERT_EQUAL_64(expected, result) \ VIXL_CHECK(Equal64(expected, &core, result)) #define ASSERT_NOT_EQUAL_64(expected, result) \ VIXL_CHECK(NotEqual64(expected, &core, result)) #define ASSERT_EQUAL_FP64(expected, result) \ VIXL_CHECK(EqualFP64(expected, &core, result)) #define ASSERT_EQUAL_128(expected_h, expected_l, result) \ VIXL_CHECK(Equal128(expected_h, expected_l, &core, result)) #define ASSERT_LITERAL_POOL_SIZE(expected) \ VIXL_CHECK((expected + kInstructionSize) == (masm.GetLiteralPoolSize())) #define ASSERT_EQUAL_SVE_LANE(expected, result, lane) \ VIXL_CHECK(EqualSVELane(expected, &core, result, lane)); // If `expected` is scalar, check that every lane of `result` matches it. // If `expected` is an array of N expected values, check that the first N // lanes on `result` match. The rightmost (highest-indexed) array element maps // to the lowest-numbered lane. #define ASSERT_EQUAL_SVE(expected, result) \ VIXL_CHECK(EqualSVE(expected, &core, result, &printed_sve_lane_warning)) #define ASSERT_EQUAL_MEMORY(expected, result, ...) \ VIXL_CHECK(EqualMemory(reinterpret_cast(expected), \ reinterpret_cast(result), \ __VA_ARGS__)) #define MUST_FAIL_WITH_MESSAGE(code, message) \ { \ bool aborted = false; \ try { \ code; \ } catch (const std::runtime_error& e) { \ const char* expected_error = message; \ size_t error_length = strlen(expected_error); \ if (strncmp(expected_error, e.what(), error_length) == 0) { \ aborted = true; \ } else { \ printf("Mismatch in error message.\n"); \ printf("Expected: %s\n", expected_error); \ printf("Found: %s\n", e.what()); \ } \ } \ VIXL_CHECK(aborted); \ } } // namespace aarch64 } // namespace vixl