| // 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 <sstream> |
| #include <string> |
| #include <list> |
| |
| #include "test-runner.h" |
| #include "test-utils.h" |
| |
| #include "aarch32/macro-assembler-aarch32.h" |
| #include "aarch32/disasm-aarch32.h" |
| |
| #ifdef VIXL_NEGATIVE_TESTING |
| #include <stdexcept> |
| #endif |
| |
| namespace vixl { |
| namespace aarch32 { |
| |
| #define __ masm. |
| #define TEST(name) TEST_(AARCH32_DISASM_##name) |
| |
| #ifdef VIXL_INCLUDE_TARGET_T32 |
| #define TEST_T32(name) TEST_(AARCH32_DISASM_##name) |
| #else |
| #define TEST_T32(name) void Test##name() |
| #endif |
| |
| #ifdef VIXL_INCLUDE_TARGET_A32 |
| #define TEST_A32(name) TEST_(AARCH32_DISASM_##name) |
| #else |
| #define TEST_A32(name) void Test##name() |
| #endif |
| |
| #define BUF_SIZE (4096) |
| |
| #define SETUP() \ |
| MacroAssembler masm(BUF_SIZE); |
| |
| #define CLEANUP() |
| |
| #ifdef VIXL_NEGATIVE_TESTING |
| #define START_COMPARE() \ |
| { \ |
| try { \ |
| int32_t start = masm.GetCursorOffset(); |
| |
| #define END_COMPARE_CHECK_SIZE(EXP,SIZE) \ |
| int32_t end = masm.GetCursorOffset(); \ |
| masm.FinalizeCode(); \ |
| std::ostringstream ss; \ |
| TestDisassembler disassembler(ss, 0); \ |
| if (masm.IsUsingT32()) { \ |
| disassembler.DisassembleT32(*masm.GetBuffer(), start, end); \ |
| } else { \ |
| disassembler.DisassembleA32(*masm.GetBuffer(), start, end); \ |
| } \ |
| masm.GetBuffer()->Reset(); \ |
| if (Test::disassemble()) { \ |
| printf("----\n"); \ |
| printf("%s", ss.str().c_str()); \ |
| } \ |
| if (std::string(EXP) != ss.str()) { \ |
| printf("\n%s:%d:%s\nFound:\n%sExpected:\n%s", __FILE__, __LINE__, \ |
| masm.IsUsingT32() ? "T32" : "A32", ss.str().c_str(), EXP); \ |
| abort(); \ |
| } \ |
| if ((SIZE) != -1 && ((end - start) != (SIZE))) { \ |
| printf("\nExpected %d bits, found %d bits\n", \ |
| 8 * (SIZE), 8 * (end - start)); \ |
| abort(); \ |
| } \ |
| } catch (std::runtime_error e) { \ |
| const char *msg = e.what(); \ |
| printf("\n%s:%d:%s\nFound:\n%sExpected:\n%s", __FILE__, __LINE__, \ |
| masm.IsUsingT32() ? "T32" : "A32", msg, EXP); \ |
| abort(); \ |
| } \ |
| } |
| #else |
| #define START_COMPARE() \ |
| { \ |
| int32_t start = masm.GetCursorOffset(); |
| |
| #define END_COMPARE_CHECK_SIZE(EXP,SIZE) \ |
| int32_t end = masm.GetCursorOffset(); \ |
| masm.FinalizeCode(); \ |
| std::ostringstream ss; \ |
| TestDisassembler disassembler(ss, 0); \ |
| if (masm.IsUsingT32()) { \ |
| disassembler.DisassembleT32(*masm.GetBuffer(), start, end); \ |
| } else { \ |
| disassembler.DisassembleA32(*masm.GetBuffer(), start, end); \ |
| } \ |
| masm.GetBuffer()->Reset(); \ |
| if (Test::disassemble()) { \ |
| printf("----\n"); \ |
| printf("%s", ss.str().c_str()); \ |
| } \ |
| if (std::string(EXP) != ss.str()) { \ |
| printf("\n%s:%d:%s\nFound:\n%sExpected:\n%s", __FILE__, __LINE__, \ |
| masm.IsUsingT32() ? "T32" : "A32", ss.str().c_str(), EXP); \ |
| abort(); \ |
| } \ |
| if ((SIZE) != -1 && ((end - start) != (SIZE))) { \ |
| printf("\nExpected %d bits, found %d bits\n", \ |
| 8 * (SIZE), 8 * (end - start)); \ |
| abort(); \ |
| } \ |
| } |
| #endif |
| |
| #define END_COMPARE(EXP) END_COMPARE_CHECK_SIZE(EXP,-1) |
| |
| #ifdef VIXL_INCLUDE_TARGET_A32 |
| #define COMPARE_A32(ASM, EXP) \ |
| masm.UseA32(); \ |
| START_COMPARE() \ |
| masm.ASM; \ |
| END_COMPARE(EXP) |
| #else |
| #define COMPARE_A32(ASM, EXP) |
| #endif |
| |
| #ifdef VIXL_INCLUDE_TARGET_T32 |
| #define COMPARE_T32(ASM, EXP) \ |
| masm.UseT32(); \ |
| START_COMPARE() \ |
| masm.ASM; \ |
| END_COMPARE(EXP) |
| #else |
| #define COMPARE_T32(ASM, EXP) |
| #endif |
| |
| #ifdef VIXL_INCLUDE_TARGET_T32 |
| #define COMPARE_T32_CHECK_SIZE(ASM, EXP, SIZE) \ |
| masm.UseT32(); \ |
| START_COMPARE() \ |
| masm.ASM; \ |
| END_COMPARE_CHECK_SIZE(EXP, SIZE) |
| #else |
| #define COMPARE_T32_CHECK_SIZE(ASM, EXP, SIZE) |
| #endif |
| |
| #define COMPARE_BOTH(ASM, EXP) \ |
| COMPARE_A32(ASM, EXP) \ |
| COMPARE_T32(ASM, EXP) |
| |
| #ifdef VIXL_NEGATIVE_TESTING |
| #define NEGATIVE_TEST(ASM, EXP, TEMPORARILY_ACCEPTED) \ |
| { \ |
| try { \ |
| int32_t start = masm.GetCursorOffset(); \ |
| ASM \ |
| int32_t end = masm.GetCursorOffset(); \ |
| masm.FinalizeCode(); \ |
| if (!TEMPORARILY_ACCEPTED) { \ |
| std::ostringstream ss; \ |
| PrintDisassembler disassembler(ss, 0); \ |
| if (masm.IsUsingT32()) { \ |
| disassembler.DisassembleT32Buffer( \ |
| masm.GetBuffer()->GetOffsetAddress<uint16_t*>(start), end); \ |
| } else { \ |
| disassembler.DisassembleA32Buffer( \ |
| masm.GetBuffer()->GetOffsetAddress<uint32_t*>(start), end); \ |
| } \ |
| printf("\n%s:%d:%s\nNo exception raised.\n", \ |
| __FILE__, \ |
| __LINE__, \ |
| masm.IsUsingT32() ? "T32" : "A32"); \ |
| printf("Found:\n%sExpected:\n%s", ss.str().c_str(), EXP); \ |
| abort(); \ |
| } \ |
| } catch (std::runtime_error e) { \ |
| const char *msg = e.what(); \ |
| size_t exp_len = strlen(EXP); \ |
| if (TEMPORARILY_ACCEPTED) { \ |
| printf("\nNegative MacroAssembler test that was temporarily " \ |
| "assembling a deprecated or unpredictable instruction is now " \ |
| "correctly raising an exception. Please update the " \ |
| "test to reflect this.\n"); \ |
| printf("at: %s:%d:%s\n", \ |
| __FILE__, \ |
| __LINE__, \ |
| masm.IsUsingT32() ? "T32" : "A32"); \ |
| abort(); \ |
| } else if (std::strncmp(EXP, msg, exp_len) != 0) { \ |
| printf("\n%s:%d:%s\nFound:\n%sExpected:\n%s...", \ |
| __FILE__, \ |
| __LINE__, \ |
| masm.IsUsingT32() ? "T32" : "A32", msg, EXP); \ |
| abort(); \ |
| } \ |
| } \ |
| } |
| |
| #ifdef VIXL_INCLUDE_TARGET_A32 |
| #define MUST_FAIL_TEST_A32(ASM, EXP) \ |
| masm.UseA32(); \ |
| NEGATIVE_TEST({ masm.ASM; }, EXP, false) \ |
| masm.GetBuffer()->Reset(); |
| #else |
| #define MUST_FAIL_TEST_A32(ASM, EXP) |
| #endif |
| |
| #ifdef VIXL_INCLUDE_TARGET_T32 |
| #define MUST_FAIL_TEST_T32(ASM, EXP) \ |
| masm.UseT32(); \ |
| NEGATIVE_TEST({ masm.ASM; }, EXP, false) \ |
| masm.GetBuffer()->Reset(); |
| #else |
| #define MUST_FAIL_TEST_T32(ASM, EXP) |
| #endif |
| |
| #define MUST_FAIL_TEST_BOTH(ASM, EXP) \ |
| MUST_FAIL_TEST_A32(ASM, EXP) \ |
| MUST_FAIL_TEST_T32(ASM, EXP) |
| |
| #ifdef VIXL_INCLUDE_TARGET_A32 |
| #define MUST_FAIL_TEST_A32_BLOCK(ASM, EXP) \ |
| masm.UseA32(); \ |
| NEGATIVE_TEST(ASM, EXP, false) \ |
| masm.GetBuffer()->Reset(); |
| #else |
| #define MUST_FAIL_TEST_A32_BLOCK(ASM, EXP) |
| #endif |
| |
| #ifdef VIXL_INCLUDE_TARGET_T32 |
| #define MUST_FAIL_TEST_T32_BLOCK(ASM, EXP) \ |
| masm.UseT32(); \ |
| NEGATIVE_TEST(ASM, EXP, false) \ |
| masm.GetBuffer()->Reset(); |
| #else |
| #define MUST_FAIL_TEST_T32_BLOCK(ASM, EXP) |
| #endif |
| |
| #define MUST_FAIL_TEST_BOTH_BLOCK(ASM, EXP) \ |
| MUST_FAIL_TEST_A32_BLOCK(ASM, EXP) \ |
| MUST_FAIL_TEST_T32_BLOCK(ASM, EXP) |
| #else |
| // Skip negative tests. |
| #define MUST_FAIL_TEST_A32(ASM, EXP) \ |
| printf("Skipping negative tests. To enable them, build with 'negative_testing=on'.\n"); |
| #define MUST_FAIL_TEST_T32(ASM, EXP) \ |
| printf("Skipping negative tests. To enable them, build with 'negative_testing=on'.\n"); |
| #define MUST_FAIL_TEST_BOTH(ASM, EXP) \ |
| printf("Skipping negative tests. To enable them, build with 'negative_testing=on'.\n"); |
| #define MUST_FAIL_TEST_A32_BLOCK(ASM, EXP) \ |
| printf("Skipping negative tests. To enable them, build with 'negative_testing=on'.\n"); |
| #define MUST_FAIL_TEST_T32_BLOCK(ASM, EXP) \ |
| printf("Skipping negative tests. To enable them, build with 'negative_testing=on'.\n"); |
| #define MUST_FAIL_TEST_BOTH_BLOCK(ASM, EXP) \ |
| printf("Skipping negative tests. To enable them, build with 'negative_testing=on'.\n"); |
| #endif |
| |
| #ifdef VIXL_NEGATIVE_TESTING |
| #ifdef VIXL_INCLUDE_TARGET_A32 |
| #define SHOULD_FAIL_TEST_A32(ASM) \ |
| masm.UseA32(); \ |
| NEGATIVE_TEST({ masm.ASM; }, "", true) \ |
| masm.GetBuffer()->Reset(); |
| #else |
| #define SHOULD_FAIL_TEST_A32(ASM) |
| #endif |
| |
| #ifdef VIXL_INCLUDE_TARGET_T32 |
| #define SHOULD_FAIL_TEST_T32(ASM) \ |
| masm.UseT32(); \ |
| NEGATIVE_TEST({ masm.ASM; }, "", true) \ |
| masm.GetBuffer()->Reset(); |
| #else |
| #define SHOULD_FAIL_TEST_T32(ASM) |
| #endif |
| |
| #define SHOULD_FAIL_TEST_BOTH(ASM) \ |
| SHOULD_FAIL_TEST_A32(ASM) \ |
| SHOULD_FAIL_TEST_T32(ASM) |
| #else |
| #define SHOULD_FAIL_TEST_A32(ASM) \ |
| printf("Skipping negative tests. To enable them, build with 'negative_testing=on'.\n"); |
| #define SHOULD_FAIL_TEST_T32(ASM) \ |
| printf("Skipping negative tests. To enable them, build with 'negative_testing=on'.\n"); |
| #define SHOULD_FAIL_TEST_BOTH(ASM) \ |
| printf("Skipping negative tests. To enable them, build with 'negative_testing=on'.\n"); |
| #endif |
| |
| class TestDisassembler : public PrintDisassembler { |
| public: |
| TestDisassembler(std::ostream& os, uint32_t pc) // NOLINT(runtime/references) |
| : PrintDisassembler(os, pc) { |
| } |
| |
| virtual void PrintCodeAddress(uint32_t code_address) VIXL_OVERRIDE { |
| USE(code_address); |
| } |
| |
| virtual void PrintOpcode16(uint32_t opcode) VIXL_OVERRIDE { |
| USE(opcode); |
| } |
| |
| virtual void PrintOpcode32(uint32_t opcode) VIXL_OVERRIDE { |
| USE(opcode); |
| } |
| |
| void DisassembleA32(const CodeBuffer& buffer, ptrdiff_t start, |
| ptrdiff_t end) { |
| DisassembleA32Buffer(buffer.GetOffsetAddress<const uint32_t*>(start), |
| end - start); |
| } |
| |
| void DisassembleT32(const CodeBuffer& buffer, ptrdiff_t start, |
| ptrdiff_t end) { |
| DisassembleT32Buffer(buffer.GetOffsetAddress<const uint16_t*>(start), |
| end - start); |
| } |
| }; |
| |
| |
| TEST_T32(t32_disassembler_limit1) { |
| SETUP(); |
| |
| masm.UseT32(); |
| START_COMPARE() |
| masm.Add(r9, r10, r11); |
| masm.GetBuffer()->Emit16(kLowestT32_32Opcode >> 16); |
| END_COMPARE("add r9, r10, r11\n" |
| "?\n"); |
| |
| CLEANUP(); |
| } |
| |
| |
| TEST_T32(t32_disassembler_limit2) { |
| SETUP(); |
| |
| masm.UseT32(); |
| START_COMPARE() |
| masm.Add(r9, r10, r11); |
| masm.Add(r0, r0, r1); |
| END_COMPARE("add r9, r10, r11\n" |
| "add r0, r1\n"); |
| |
| CLEANUP(); |
| } |
| |
| |
| TEST(macro_assembler_orn) { |
| SETUP(); |
| |
| // - Identities. |
| |
| COMPARE_BOTH(Orn(r0, r1, 0), |
| "mvn r0, #0\n"); |
| COMPARE_BOTH(Orn(r0, r0, 0xffffffff), |
| ""); |
| |
| // - Immediate form. This form does not need macro-assembler support |
| // for T32. |
| |
| // Use r0 as the temporary register. |
| COMPARE_A32(Orn(r0, r1, 1), |
| "mvn r0, #1\n" |
| "orr r0, r1, r0\n"); |
| // Use ip as the temporary register. |
| COMPARE_A32(Orns(r0, r0, 1), |
| "mvn ip, #1\n" |
| "orrs r0, ip\n"); |
| |
| // - Too large immediate form. |
| COMPARE_BOTH(Orn(r0, r1, 0x00ffffff), |
| "orr r0, r1, #0xff000000\n"); |
| COMPARE_BOTH(Orn(r0, r1, 0xff00ffff), |
| "orr r0, r1, #0xff0000\n"); |
| COMPARE_BOTH(Orns(r0, r1, 0x00ffffff), |
| "orrs r0, r1, #0xff000000\n"); |
| |
| COMPARE_A32(Orns(r0, r1, 0xabcd2345), |
| "mov ip, #9029\n" |
| "movt ip, #43981\n" |
| "mvn r0, ip\n" |
| "orrs r0, r1, r0\n"); |
| COMPARE_T32(Orn(r0, r1, 0xabcd2345), |
| "mov r0, #9029\n" |
| "movt r0, #43981\n" |
| "orn r0, r1, r0\n"); |
| |
| // - Plain register form. This form does not need macro-assembler |
| // support for T32. |
| |
| // Use r0 as the temporary register. |
| COMPARE_A32(Orn(r0, r1, r2), |
| "mvn r0, r2\n" |
| "orr r0, r1, r0\n"); |
| // Use ip as the temporary register. |
| COMPARE_A32(Orn(r0, r0, r1), |
| "mvn ip, r1\n" |
| "orr r0, ip\n"); |
| // Use r0 as the temporary register. |
| COMPARE_A32(Orn(r0, r1, r0), |
| "mvn r0, r0\n" |
| "orr r0, r1, r0\n"); |
| // Use ip as the temporary register. |
| COMPARE_A32(Orn(r0, r0, r0), |
| "mvn ip, r0\n" |
| "orr r0, ip\n"); |
| |
| // - Shifted register form. This form does not need macro-assembler |
| // support for T32. |
| |
| // Use r0 as the temporary register. |
| COMPARE_A32(Orn(r0, r1, Operand(r2, LSL, 1)), |
| "mvn r0, r2, lsl #1\n" |
| "orr r0, r1, r0\n"); |
| // Use ip as the temporary register. |
| COMPARE_A32(Orns(r0, r0, Operand(r2, LSR, 2)), |
| "mvn ip, r2, lsr #2\n" |
| "orrs r0, ip\n"); |
| |
| // - Register shifted register form. |
| |
| // Use r0 as the temporary register. |
| COMPARE_A32(Orn(r0, r1, Operand(r2, LSL, r3)), |
| "mvn r0, r2, lsl r3\n" |
| "orr r0, r1, r0\n"); |
| COMPARE_T32(Orn(r0, r1, Operand(r2, LSL, r3)), |
| "lsl r0, r2, r3\n" |
| "orn r0, r1, r0\n"); |
| // Use ip as the temporary register. |
| COMPARE_A32(Orns(r0, r0, Operand(r2, LSR, r3)), |
| "mvn ip, r2, lsr r3\n" |
| "orrs r0, ip\n"); |
| COMPARE_T32(Orns(r0, r0, Operand(r2, LSR, r3)), |
| "lsr ip, r2, r3\n" |
| "orns r0, ip\n"); |
| // Use ip as the temporary register. |
| COMPARE_A32(Orn(r0, r0, Operand(r0, ASR, r3)), |
| "mvn ip, r0, asr r3\n" |
| "orr r0, ip\n"); |
| COMPARE_T32(Orn(r0, r0, Operand(r0, ASR, r3)), |
| "asr ip, r0, r3\n" |
| "orn r0, ip\n"); |
| CLEANUP(); |
| } |
| |
| |
| TEST(macro_assembler_t32_rsc) { |
| SETUP(); |
| |
| // - Immediate form. We can always re-use `rn`. |
| |
| // No need for temporay registers. |
| COMPARE_T32(Rsc(r0, r1, 1), |
| "mvn r0, r1\n" |
| "adc r0, #1\n"); |
| // No need for temporay registers. |
| COMPARE_T32(Rscs(r0, r0, 2), |
| "mvn r0, r0\n" |
| "adcs r0, #2\n"); |
| |
| // - Too large immediate form. |
| |
| // TODO: optimize this. |
| COMPARE_A32(Rsc(r0, r1, 0x00ffffff), |
| "mvn r0, #4278190080\n" |
| "rsc r0, r1, r0\n"); |
| COMPARE_T32(Rscs(r0, r1, 0x00ffffff), |
| "mvn r0, r1\n" |
| "mvn ip, #4278190080\n" |
| "adcs r0, ip\n"); |
| COMPARE_A32(Rsc(r0, r0, 0x00ffffff), |
| "mvn ip, #4278190080\n" |
| "rsc r0, ip\n"); |
| COMPARE_T32(Rscs(r0, r0, 0x00ffffff), |
| "mvn r0, r0\n" |
| "mvn ip, #4278190080\n" |
| "adcs r0, ip\n"); |
| |
| COMPARE_A32(Rsc(r0, r1, 0xabcd2345), |
| "mov r0, #9029\n" |
| "movt r0, #43981\n" |
| "rsc r0, r1, r0\n"); |
| COMPARE_T32(Rscs(r0, r1, 0xabcd2345), |
| "mvn r0, r1\n" |
| "mov ip, #56506\n" |
| "movt ip, #21554\n" |
| "sbcs r0, ip\n"); |
| COMPARE_A32(Rsc(r0, r0, 0xabcd2345), |
| "mov ip, #9029\n" |
| "movt ip, #43981\n" |
| "rsc r0, ip\n"); |
| COMPARE_T32(Rscs(r0, r0, 0xabcd2345), |
| "mvn r0, r0\n" |
| "mov ip, #56506\n" |
| "movt ip, #21554\n" |
| "sbcs r0, ip\n"); |
| |
| // - Plain register form. |
| |
| // No need for temporary registers. |
| COMPARE_T32(Rscs(r0, r1, r2), |
| "mvn r0, r1\n" |
| "adcs r0, r2\n"); |
| // Use r0 as the temporary register. |
| COMPARE_T32(Rscs(r0, r1, r1), |
| "mvn r0, r1\n" |
| "adcs r0, r1\n"); |
| // Use ip as the temporary register. |
| COMPARE_T32(Rscs(r0, r0, r0), |
| "mvn ip, r0\n" |
| "adcs r0, ip, r0\n"); |
| |
| // - Shifted register form. |
| |
| // No need for temporay registers. |
| COMPARE_T32(Rsc(r0, r1, Operand(r2, LSL, 1)), |
| "mvn r0, r1\n" |
| "adc r0, r2, lsl #1\n"); |
| // Use ip as the temporary register. |
| COMPARE_T32(Rscs(r0, r1, Operand(r0, LSR, 2)), |
| "mvn ip, r1\n" |
| "adcs r0, ip, r0, lsr #2\n"); |
| // Use r0 as the temporary register. |
| COMPARE_T32(Rsc(r0, r1, Operand(r1, ASR, 3)), |
| "mvn r0, r1\n" |
| "adc r0, r1, asr #3\n"); |
| // Use ip as the temporary register. |
| COMPARE_T32(Rscs(r0, r0, Operand(r0, ROR, 4)), |
| "mvn ip, r0\n" |
| "adcs r0, ip, r0, ror #4\n"); |
| |
| // - Register shifted register form. The macro-assembler handles this form in |
| // two steps. First, a shift instruction is generated from the operand. And |
| // finally the operation is reduced to its plain register form. |
| |
| COMPARE_T32(Rsc(r0, r1, Operand(r2, LSL, r3)), |
| "lsl r0, r2, r3\n" |
| "mvn ip, r1\n" |
| "adc r0, ip, r0\n"); |
| // Use r0 and ip as the temporary register. |
| COMPARE_T32(Rscs(r0, r1, Operand(r1, LSR, r3)), |
| "lsr r0, r1, r3\n" |
| "mvn ip, r1\n" |
| "adcs r0, ip, r0\n"); |
| // Use ip and r0 as the temporary register. |
| COMPARE_T32(Rsc(r0, r0, Operand(r2, ASR, r3)), |
| "asr ip, r2, r3\n" |
| "mvn r0, r0\n" |
| "adc r0, ip\n"); |
| // Use ip and r0 as the temporary register. |
| COMPARE_T32(Rscs(r0, r0, Operand(r0, ROR, r3)), |
| "ror ip, r0, r3\n" |
| "mvn r0, r0\n" |
| "adcs r0, ip\n"); |
| // Use ip and r0 as the temporary register. |
| COMPARE_T32(Rsc(r0, r0, Operand(r0, LSL, r0)), |
| "lsl ip, r0, r0\n" |
| "mvn r0, r0\n" |
| "adc r0, ip\n"); |
| |
| CLEANUP(); |
| } |
| |
| |
| TEST(macro_assembler_t32_register_shift_register) { |
| SETUP(); |
| |
| COMPARE_T32(Adc(r0, r1, Operand(r2, LSL, r3)), |
| "lsl r0, r2, r3\n" |
| "adc r0, r1, r0\n"); |
| COMPARE_T32(Adcs(r0, r0, Operand(r2, LSR, r3)), |
| "lsr ip, r2, r3\n" |
| "adcs r0, ip\n"); |
| COMPARE_T32(Add(r0, r0, Operand(r0, ASR, r3)), |
| "asr ip, r0, r3\n" |
| "add r0, ip\n"); |
| COMPARE_T32(Adds(r0, r0, Operand(r0, ROR, r0)), |
| "ror ip, r0, r0\n" |
| "adds r0, ip\n"); |
| |
| CLEANUP(); |
| } |
| |
| |
| TEST(macro_assembler_big_offset) { |
| SETUP(); |
| |
| COMPARE_BOTH(Ldr(r0, MemOperand(r1, 0xfff123)), |
| "add r0, r1, #1044480\n" // #0xff000 |
| "add r0, #15728640\n" // #0x00f00000 |
| "ldr r0, [r0, #291]\n"); // #0x123 |
| COMPARE_BOTH(Ldr(r0, MemOperand(r1, 0xff123)), |
| "add r0, r1, #1044480\n" // #0xff000 |
| "ldr r0, [r0, #291]\n"); // #0x123 |
| COMPARE_BOTH(Ldr(r0, MemOperand(r1, -0xff123)), |
| "sub r0, r1, #1048576\n" // #0x100000 |
| "ldr r0, [r0, #3805]\n"); // #0xedd |
| |
| COMPARE_A32(Ldr(r0, MemOperand(r1, 0xfff123, PreIndex)), |
| "add r1, #1044480\n" // #0xff000 |
| "add r1, #15728640\n" // #0x00f00000 |
| "ldr r0, [r1, #291]!\n"); // #0x123 |
| COMPARE_A32(Ldr(r0, MemOperand(r1, 0xff123, PreIndex)), |
| "add r1, #1044480\n" // #0xff000 |
| "ldr r0, [r1, #291]!\n"); // #0x123 |
| COMPARE_A32(Ldr(r0, MemOperand(r1, -0xff123, PreIndex)), |
| "sub r1, #1048576\n" // #0x100000 |
| "ldr r0, [r1, #3805]!\n"); // #0xedd |
| |
| COMPARE_T32(Ldr(r0, MemOperand(r1, 0xfff12, PreIndex)), |
| "add r1, #65280\n" // #0xff00 |
| "add r1, #983040\n" // #0x000f0000 |
| "ldr r0, [r1, #18]!\n"); // #0x12 |
| COMPARE_T32(Ldr(r0, MemOperand(r1, 0xff12, PreIndex)), |
| "add r1, #65280\n" // #0xff00 |
| "ldr r0, [r1, #18]!\n"); // #0x12 |
| COMPARE_T32(Ldr(r0, MemOperand(r1, -0xff12, PreIndex)), |
| "sub r1, #65536\n" // #0x10000 |
| "ldr r0, [r1, #238]!\n"); // #0xee |
| |
| COMPARE_A32(Ldr(r0, MemOperand(r1, 0xfff123, PostIndex)), |
| "ldr r0, [r1], #291\n" // #0x123 |
| "add r1, #1044480\n" // #0xff000 |
| "add r1, #15728640\n"); // #0x00f00000 |
| COMPARE_A32(Ldr(r0, MemOperand(r1, 0xff123, PostIndex)), |
| "ldr r0, [r1], #291\n" // #0x123 |
| "add r1, #1044480\n"); // #0xff000 |
| COMPARE_A32(Ldr(r0, MemOperand(r1, -0xff123, PostIndex)), |
| "ldr r0, [r1], #3805\n" // #0xedd |
| "sub r1, #1048576\n"); // #0x100000 |
| |
| COMPARE_T32(Ldr(r0, MemOperand(r1, 0xfff12, PostIndex)), |
| "ldr r0, [r1], #18\n" // #0x12 |
| "add r1, #65280\n" // #0xff00 |
| "add r1, #983040\n"); // #0x000f0000 |
| COMPARE_T32(Ldr(r0, MemOperand(r1, 0xff12, PostIndex)), |
| "ldr r0, [r1], #18\n" // #0x12 |
| "add r1, #65280\n"); // #0xff00 |
| COMPARE_T32(Ldr(r0, MemOperand(r1, -0xff12, PostIndex)), |
| "ldr r0, [r1], #238\n" // #0xee |
| "sub r1, #65536\n"); // #0x10000 |
| |
| COMPARE_A32(Ldrh(r0, MemOperand(r1, 0xfff123)), |
| "add r0, r1, #61696\n" // #0xf100 |
| "add r0, #16711680\n" // #0x00ff0000 |
| "ldrh r0, [r0, #35]\n"); // #0x23 |
| COMPARE_T32(Ldrh(r0, MemOperand(r1, 0xfff123)), |
| "add r0, r1, #1044480\n" // #0xff000 |
| "add r0, #15728640\n" // #0x00f00000 |
| "ldrh r0, [r0, #291]\n"); // #0x123 |
| |
| COMPARE_A32(Ldrh(r0, MemOperand(r1, 0xff123)), |
| "add r0, r1, #61696\n" // #0xf100 |
| "add r0, #983040\n" // #0x000f0000 |
| "ldrh r0, [r0, #35]\n"); // #0x23 |
| COMPARE_T32(Ldrh(r0, MemOperand(r1, 0xff123)), |
| "add r0, r1, #1044480\n" // #0xff000 |
| "ldrh r0, [r0, #291]\n"); // #0x123 |
| COMPARE_A32(Ldrh(r0, MemOperand(r1, -0xff123)), |
| "sub r0, r1, #61952\n" // #0xf200 |
| "sub r0, #983040\n" // #0x000f0000 |
| "ldrh r0, [r0, #221]\n"); // #0xdd |
| COMPARE_T32(Ldrh(r0, MemOperand(r1, -0xff123)), |
| "sub r0, r1, #1048576\n" // #0x100000 |
| "ldrh r0, [r0, #3805]\n"); // #0xedd |
| |
| MUST_FAIL_TEST_BOTH(Ldr(r0, MemOperand(r0, 0xfff12, PreIndex)), |
| "Ill-formed 'ldr' instruction.\n"); |
| MUST_FAIL_TEST_BOTH(Ldr(r0, MemOperand(r0, 0xfff12, PostIndex)), |
| "Ill-formed 'ldr' instruction.\n"); |
| CLEANUP(); |
| } |
| |
| |
| TEST(macro_assembler_load) { |
| SETUP(); |
| |
| // Register base and offset that we can encode in both A1 and T1. |
| COMPARE_BOTH(Ldr(r0, MemOperand(r1, r8, Offset)), |
| "ldr r0, [r1, r8]\n"); |
| |
| // Negative register offset. Use the destination as a scratch register, |
| // regardless of the values of the base and offset register. |
| COMPARE_T32(Ldr(r0, MemOperand(r0, minus, r0, Offset)), |
| "sub r0, r0\n" |
| "ldr r0, [r0]\n"); |
| |
| COMPARE_T32(Ldr(r0, MemOperand(r0, minus, r1, Offset)), |
| "sub r0, r1\n" |
| "ldr r0, [r0]\n"); |
| |
| COMPARE_T32(Ldr(r0, MemOperand(r1, minus, r0, Offset)), |
| "sub r0, r1, r0\n" |
| "ldr r0, [r0]\n"); |
| |
| COMPARE_T32(Ldr(r0, MemOperand(r1, minus, r2, Offset)), |
| "sub r0, r1, r2\n" |
| "ldr r0, [r0]\n"); |
| |
| // Pre-index negative offset. |
| COMPARE_T32(Ldr(r0, MemOperand(r1, minus, r2, PreIndex)), |
| "sub r1, r2\n" |
| "ldr r0, [r1]\n"); |
| |
| // Post-index negative offset. |
| COMPARE_T32(Ldr(r0, MemOperand(r1, minus, r2, PostIndex)), |
| "ldr r0, [r1]\n" |
| "sub r1, r2\n"); |
| |
| // SP is allowed as base, offset and destination. |
| COMPARE_BOTH(Ldr(sp, MemOperand(sp, sp, Offset)), |
| "ldr sp, [sp, sp]\n"); |
| |
| // PC is allowed as destination - make sure it is not used as a temporary |
| // register. |
| COMPARE_BOTH(Ldr(pc, MemOperand(r0, r0, Offset)), |
| "ldr pc, [r0, r0]\n"); |
| COMPARE_A32(Ldr(pc, MemOperand(r0, r0, PreIndex)), |
| "ldr pc, [r0, r0]!\n"); |
| COMPARE_T32(Ldr(pc, MemOperand(r0, r0, PreIndex)), |
| "add r0, r0\n" |
| "ldr pc, [r0]\n"); |
| COMPARE_A32(Ldr(pc, MemOperand(r0, r0, PostIndex)), |
| "ldr pc, [r0], r0\n"); |
| COMPARE_T32(Ldr(pc, MemOperand(r0, r0, PostIndex)), |
| "ldr pc, [r0]\n" |
| "add r0, r0\n"); |
| |
| // PC is allowed as register base in the offset variant only for A32. |
| COMPARE_A32(Ldr(r0, MemOperand(pc, r0, Offset)), |
| "ldr r0, [pc, r0]\n"); |
| MUST_FAIL_TEST_T32(Ldr(r0, MemOperand(pc, r0, Offset)), |
| "The MacroAssembler does not convert loads and stores with" |
| " a PC base register for T32.\n"); |
| |
| // PC is not allowed as register base in the pre-index and post-index variants. |
| MUST_FAIL_TEST_T32(Ldr(r0, MemOperand(pc, r0, PreIndex)), |
| "The MacroAssembler does not convert loads and stores " |
| "with a PC base register in pre-index or post-index mode.\n"); |
| MUST_FAIL_TEST_T32(Ldr(r0, MemOperand(pc, r0, PostIndex)), |
| "The MacroAssembler does not convert loads and stores " |
| "with a PC base register in pre-index or post-index mode.\n"); |
| |
| // We don't convert loads with PC as the register offset. |
| MUST_FAIL_TEST_T32(Ldr(r0, MemOperand(r0, minus, pc, Offset)), |
| "The MacroAssembler does not convert loads and stores " |
| "with a PC offset register.\n"); |
| MUST_FAIL_TEST_T32(Ldr(r0, MemOperand(r0, pc, PreIndex)), |
| "The MacroAssembler does not convert loads and stores " |
| "with a PC offset register.\n"); |
| MUST_FAIL_TEST_T32(Ldr(r0, MemOperand(r0, pc, PostIndex)), |
| "The MacroAssembler does not convert loads and stores " |
| "with a PC offset register.\n"); |
| |
| // TODO: PC should not be allowed as register offset (unpredictable). |
| SHOULD_FAIL_TEST_BOTH(Ldr(r0, MemOperand(r0, Sign(plus), pc, Offset))); |
| |
| // TODO: PC should not be allowed as register base in A32 with pre-index |
| // and post-index (unpredictable). |
| SHOULD_FAIL_TEST_A32(Ldr(r0, MemOperand(pc, r0, PreIndex))); |
| SHOULD_FAIL_TEST_A32(Ldr(r0, MemOperand(pc, r0, PostIndex))); |
| |
| // TODO: load with the same register used as base and as destination |
| // should fail to assemble (unpredictable). |
| SHOULD_FAIL_TEST_A32(Ldr(r0, MemOperand(r0, r1, PreIndex))); |
| SHOULD_FAIL_TEST_A32(Ldr(r0, MemOperand(r0, r1, PostIndex))); |
| MUST_FAIL_TEST_T32(Ldr(r0, MemOperand(r0, r1, PreIndex)), |
| "Ill-formed 'ldr' instruction.\n"); |
| MUST_FAIL_TEST_T32(Ldr(r0, MemOperand(r0, r1, PostIndex)), |
| "Ill-formed 'ldr' instruction.\n"); |
| |
| CLEANUP(); |
| } |
| |
| |
| TEST(macro_assembler_store) { |
| SETUP(); |
| |
| // Register base and offset that we can encode in both A1 and T1. |
| COMPARE_BOTH(Str(r0, MemOperand(r1, r8, Offset)), |
| "str r0, [r1, r8]\n"); |
| |
| // Negative register offset. |
| COMPARE_T32(Str(r0, MemOperand(r0, minus, r0, Offset)), |
| "sub ip, r0, r0\n" |
| "str r0, [ip]\n"); |
| |
| COMPARE_T32(Str(r0, MemOperand(r0, minus, r1, Offset)), |
| "sub ip, r0, r1\n" |
| "str r0, [ip]\n"); |
| |
| COMPARE_T32(Str(r0, MemOperand(r1, minus, r0, Offset)), |
| "sub ip, r1, r0\n" |
| "str r0, [ip]\n"); |
| |
| COMPARE_T32(Str(r0, MemOperand(r1, minus, r2, Offset)), |
| "sub ip, r1, r2\n" |
| "str r0, [ip]\n"); |
| |
| // Pre-index negative offset. |
| COMPARE_T32(Str(r0, MemOperand(r1, minus, r2, PreIndex)), |
| "sub r1, r2\n" |
| "str r0, [r1]\n"); |
| |
| // Post-index negative offset. |
| COMPARE_T32(Str(r0, MemOperand(r1, minus, r2, PostIndex)), |
| "str r0, [r1]\n" |
| "sub r1, r2\n"); |
| |
| // SP is allowed as base, offset and source. |
| COMPARE_BOTH(Str(sp, MemOperand(sp, sp, Offset)), |
| "str sp, [sp, sp]\n"); |
| |
| // TODO: PC is allowed as the value we are storing for A32, but |
| // should not be allowed for T32 (unpredictable). |
| COMPARE_A32(Str(pc, MemOperand(r0, r0, Offset)), |
| "str pc, [r0, r0]\n"); |
| COMPARE_A32(Str(pc, MemOperand(r0, r0, PreIndex)), |
| "str pc, [r0, r0]!\n"); |
| COMPARE_A32(Str(pc, MemOperand(r0, r0, PostIndex)), |
| "str pc, [r0], r0\n"); |
| SHOULD_FAIL_TEST_T32(Str(pc, MemOperand(r0, r0, Offset))); |
| SHOULD_FAIL_TEST_T32(Str(pc, MemOperand(r0, r0, PreIndex))); |
| SHOULD_FAIL_TEST_T32(Str(pc, MemOperand(r0, r0, PostIndex))); |
| |
| // PC is allowed as register base in the offset variant only for A32. |
| COMPARE_A32(Str(r0, MemOperand(pc, r0, Offset)), |
| "str r0, [pc, r0]\n"); |
| MUST_FAIL_TEST_T32(Str(r0, MemOperand(pc, r0, Offset)), |
| "The MacroAssembler does not convert loads and stores with" |
| " a PC base register for T32.\n"); |
| |
| // PC is not allowed as register base in the pre-index and post-index variants. |
| MUST_FAIL_TEST_T32(Str(r0, MemOperand(pc, r0, PreIndex)), |
| "The MacroAssembler does not convert loads and stores " |
| "with a PC base register in pre-index or post-index mode.\n"); |
| MUST_FAIL_TEST_T32(Str(r0, MemOperand(pc, r0, PostIndex)), |
| "The MacroAssembler does not convert loads and stores " |
| "with a PC base register in pre-index or post-index mode.\n"); |
| |
| // We don't convert loads with PC as the register offset. |
| MUST_FAIL_TEST_T32(Str(r0, MemOperand(r0, minus, pc, Offset)), |
| "The MacroAssembler does not convert loads and stores " |
| "with a PC offset register.\n"); |
| MUST_FAIL_TEST_T32(Str(r0, MemOperand(r0, pc, PreIndex)), |
| "The MacroAssembler does not convert loads and stores " |
| "with a PC offset register.\n"); |
| MUST_FAIL_TEST_T32(Str(r0, MemOperand(r0, pc, PostIndex)), |
| "The MacroAssembler does not convert loads and stores " |
| "with a PC offset register.\n"); |
| |
| // TODO: PC should not be allowed as register offset (unpredictable). |
| SHOULD_FAIL_TEST_BOTH(Str(r0, MemOperand(r0, Sign(plus), pc, Offset))); |
| |
| // TODO: PC should not be allowed as register base in A32 with pre-index |
| // and post-index (unpredictable). |
| SHOULD_FAIL_TEST_A32(Str(r0, MemOperand(pc, r0, PreIndex))); |
| SHOULD_FAIL_TEST_A32(Str(r0, MemOperand(pc, r0, PostIndex))); |
| |
| // TODO: store with the same register used as base and as source |
| // should fail to assemble (unpredictable). |
| SHOULD_FAIL_TEST_A32(Str(r0, MemOperand(r0, r1, PreIndex))); |
| SHOULD_FAIL_TEST_A32(Str(r0, MemOperand(r0, r1, PostIndex))); |
| MUST_FAIL_TEST_T32(Str(r0, MemOperand(r0, r1, PreIndex)), |
| "Ill-formed 'str' instruction.\n"); |
| MUST_FAIL_TEST_T32(Str(r0, MemOperand(r0, r1, PostIndex)), |
| "Ill-formed 'str' instruction.\n"); |
| |
| CLEANUP(); |
| } |
| |
| |
| TEST(macro_assembler_ldrd) { |
| SETUP(); |
| |
| // - Tests with no offset. |
| |
| COMPARE_BOTH(Ldrd(r0, r1, MemOperand(r3)), |
| "ldrd r0, r1, [r3]\n"); |
| // Destination registers need to start with a even numbered register on A32. |
| MUST_FAIL_TEST_A32(Ldrd(r1, r2, MemOperand(r3)), |
| "Unpredictable instruction.\n"); |
| COMPARE_T32(Ldrd(r1, r2, MemOperand(r3)), |
| "ldrd r1, r2, [r3]\n"); |
| // Registers need to be adjacent on A32. |
| MUST_FAIL_TEST_A32(Ldrd(r0, r2, MemOperand(r1)), |
| "Ill-formed 'ldrd' instruction.\n"); |
| COMPARE_T32(Ldrd(r0, r2, MemOperand(r1)), |
| "ldrd r0, r2, [r1]\n"); |
| |
| COMPARE_BOTH(Ldrd(r0, r1, MemOperand(r2)), |
| "ldrd r0, r1, [r2]\n"); |
| |
| // - Tests with immediate offsets. |
| |
| COMPARE_A32(Ldrd(r0, r1, MemOperand(r2, 1020)), |
| "add r0, r2, #1020\n" |
| "ldrd r0, r1, [r0]\n"); |
| COMPARE_T32(Ldrd(r0, r1, MemOperand(r2, 1020)), |
| "ldrd r0, r1, [r2, #1020]\n"); |
| COMPARE_A32(Ldrd(r0, r1, MemOperand(r2, -1020)), |
| "sub r0, r2, #1020\n" |
| "ldrd r0, r1, [r0]\n"); |
| COMPARE_T32(Ldrd(r0, r1, MemOperand(r2, -1020)), |
| "ldrd r0, r1, [r2, #-1020]\n"); |
| |
| COMPARE_A32(Ldrd(r0, r1, MemOperand(r2, 0xabcc)), |
| "add r0, r2, #43776\n" |
| "ldrd r0, r1, [r0, #204]\n"); |
| COMPARE_T32(Ldrd(r0, r1, MemOperand(r2, 0xabcc)), |
| "add r0, r2, #43008\n" |
| "ldrd r0, r1, [r0, #972]\n"); |
| COMPARE_A32(Ldrd(r0, r1, MemOperand(r2, -0xabcc)), |
| "sub r0, r2, #44032\n" |
| "ldrd r0, r1, [r0, #52]\n"); |
| COMPARE_T32(Ldrd(r0, r1, MemOperand(r2, -0xabcc)), |
| "sub r0, r2, #44032\n" |
| "ldrd r0, r1, [r0, #52]\n"); |
| COMPARE_A32(Ldrd(r0, r1, MemOperand(r2, 0xabcdec)), |
| "add r0, r2, #52480\n" |
| "add r0, #11206656\n" |
| "ldrd r0, r1, [r0, #236]\n"); |
| COMPARE_T32(Ldrd(r0, r1, MemOperand(r2, 0xabcdec)), |
| "add r0, r2, #248832\n" |
| "add r0, #11010048\n" |
| "ldrd r0, r1, [r0, #492]\n"); |
| COMPARE_A32(Ldrd(r0, r1, MemOperand(r2, -0xabcdec)), |
| "sub r0, r2, #52736\n" |
| "sub r0, #11206656\n" |
| "ldrd r0, r1, [r0, #20]\n"); |
| COMPARE_T32(Ldrd(r0, r1, MemOperand(r2, -0xabcdec)), |
| "sub r0, r2, #774144\n" |
| "sub r0, #10485760\n" |
| "ldrd r0, r1, [r0, #532]\n"); |
| |
| COMPARE_A32(Ldrd(r0, r1, MemOperand(r0, 0xabcc)), |
| "add r1, r0, #43776\n" |
| "ldrd r0, r1, [r1, #204]\n"); |
| COMPARE_T32(Ldrd(r0, r1, MemOperand(r0, 0xabcc)), |
| "add r1, r0, #43008\n" |
| "ldrd r0, r1, [r1, #972]\n"); |
| COMPARE_A32(Ldrd(r0, r1, MemOperand(r0, -0xabcc)), |
| "sub r1, r0, #44032\n" |
| "ldrd r0, r1, [r1, #52]\n"); |
| COMPARE_T32(Ldrd(r0, r1, MemOperand(r0, -0xabcc)), |
| "sub r1, r0, #44032\n" |
| "ldrd r0, r1, [r1, #52]\n"); |
| COMPARE_A32(Ldrd(r0, r1, MemOperand(r0, 0xabcdec)), |
| "add r1, r0, #52480\n" |
| "add r1, #11206656\n" |
| "ldrd r0, r1, [r1, #236]\n"); |
| COMPARE_T32(Ldrd(r0, r1, MemOperand(r0, 0xabcdec)), |
| "add r1, r0, #248832\n" |
| "add r1, #11010048\n" |
| "ldrd r0, r1, [r1, #492]\n"); |
| COMPARE_A32(Ldrd(r0, r1, MemOperand(r0, -0xabcdec)), |
| "sub r1, r0, #52736\n" |
| "sub r1, #11206656\n" |
| "ldrd r0, r1, [r1, #20]\n"); |
| COMPARE_T32(Ldrd(r0, r1, MemOperand(r0, -0xabcdec)), |
| "sub r1, r0, #774144\n" |
| "sub r1, #10485760\n" |
| "ldrd r0, r1, [r1, #532]\n"); |
| |
| COMPARE_A32(Ldrd(r0, r1, MemOperand(r1, 0xabcc)), |
| "add r0, r1, #43776\n" |
| "ldrd r0, r1, [r0, #204]\n"); |
| COMPARE_T32(Ldrd(r0, r1, MemOperand(r1, 0xabcc)), |
| "add r0, r1, #43008\n" |
| "ldrd r0, r1, [r0, #972]\n"); |
| COMPARE_A32(Ldrd(r0, r1, MemOperand(r1, -0xabcc)), |
| "sub r0, r1, #44032\n" |
| "ldrd r0, r1, [r0, #52]\n"); |
| COMPARE_T32(Ldrd(r0, r1, MemOperand(r1, -0xabcc)), |
| "sub r0, r1, #44032\n" |
| "ldrd r0, r1, [r0, #52]\n"); |
| COMPARE_A32(Ldrd(r0, r1, MemOperand(r1, 0xabcdec)), |
| "add r0, r1, #52480\n" |
| "add r0, #11206656\n" |
| "ldrd r0, r1, [r0, #236]\n"); |
| COMPARE_T32(Ldrd(r0, r1, MemOperand(r1, 0xabcdec)), |
| "add r0, r1, #248832\n" |
| "add r0, #11010048\n" |
| "ldrd r0, r1, [r0, #492]\n"); |
| COMPARE_A32(Ldrd(r0, r1, MemOperand(r1, -0xabcdec)), |
| "sub r0, r1, #52736\n" |
| "sub r0, #11206656\n" |
| "ldrd r0, r1, [r0, #20]\n"); |
| COMPARE_T32(Ldrd(r0, r1, MemOperand(r1, -0xabcdec)), |
| "sub r0, r1, #774144\n" |
| "sub r0, #10485760\n" |
| "ldrd r0, r1, [r0, #532]\n"); |
| |
| COMPARE_A32(Ldrd(r0, r1, MemOperand(r2, 0xabcc, PostIndex)), |
| "ldrd r0, r1, [r2], #204\n" |
| "add r2, #43776\n"); |
| COMPARE_T32(Ldrd(r0, r1, MemOperand(r2, 0xabcc, PostIndex)), |
| "ldrd r0, r1, [r2], #972\n" |
| "add r2, #43008\n"); |
| COMPARE_A32(Ldrd(r0, r1, MemOperand(r2, -0xabcc, PostIndex)), |
| "ldrd r0, r1, [r2], #52\n" |
| "sub r2, #44032\n"); |
| COMPARE_T32(Ldrd(r0, r1, MemOperand(r2, -0xabcc, PostIndex)), |
| "ldrd r0, r1, [r2], #52\n" |
| "sub r2, #44032\n"); |
| COMPARE_A32(Ldrd(r0, r1, MemOperand(r2, 0xabcdec, PostIndex)), |
| "ldrd r0, r1, [r2], #236\n" |
| "add r2, #52480\n" |
| "add r2, #11206656\n"); |
| COMPARE_T32(Ldrd(r0, r1, MemOperand(r2, 0xabcdec, PostIndex)), |
| "ldrd r0, r1, [r2], #492\n" |
| "add r2, #248832\n" |
| "add r2, #11010048\n"); |
| COMPARE_A32(Ldrd(r0, r1, MemOperand(r2, -0xabcdec, PostIndex)), |
| "ldrd r0, r1, [r2], #20\n" |
| "sub r2, #52736\n" |
| "sub r2, #11206656\n"); |
| COMPARE_T32(Ldrd(r0, r1, MemOperand(r2, -0xabcdec, PostIndex)), |
| "ldrd r0, r1, [r2], #532\n" |
| "sub r2, #774144\n" |
| "sub r2, #10485760\n"); |
| |
| // PostIndex with the same register as base and destination is invalid. |
| MUST_FAIL_TEST_BOTH(Ldrd(r0, r1, MemOperand(r0, 0xabcd, PostIndex)), |
| "Ill-formed 'ldrd' instruction.\n"); |
| MUST_FAIL_TEST_BOTH(Ldrd(r0, r1, MemOperand(r1, 0xabcdef, PostIndex)), |
| "Ill-formed 'ldrd' instruction.\n"); |
| |
| COMPARE_A32(Ldrd(r0, r1, MemOperand(r2, 0xabcc, PreIndex)), |
| "add r2, #43776\n" |
| "ldrd r0, r1, [r2, #204]!\n"); |
| COMPARE_T32(Ldrd(r0, r1, MemOperand(r2, 0xabcc, PreIndex)), |
| "add r2, #43008\n" |
| "ldrd r0, r1, [r2, #972]!\n"); |
| COMPARE_A32(Ldrd(r0, r1, MemOperand(r2, -0xabcc, PreIndex)), |
| "sub r2, #44032\n" |
| "ldrd r0, r1, [r2, #52]!\n"); |
| COMPARE_T32(Ldrd(r0, r1, MemOperand(r2, -0xabcc, PreIndex)), |
| "sub r2, #44032\n" |
| "ldrd r0, r1, [r2, #52]!\n"); |
| COMPARE_A32(Ldrd(r0, r1, MemOperand(r2, 0xabcdec, PreIndex)), |
| "add r2, #52480\n" |
| "add r2, #11206656\n" |
| "ldrd r0, r1, [r2, #236]!\n"); |
| COMPARE_T32(Ldrd(r0, r1, MemOperand(r2, 0xabcdec, PreIndex)), |
| "add r2, #248832\n" |
| "add r2, #11010048\n" |
| "ldrd r0, r1, [r2, #492]!\n"); |
| COMPARE_A32(Ldrd(r0, r1, MemOperand(r2, -0xabcdec, PreIndex)), |
| "sub r2, #52736\n" |
| "sub r2, #11206656\n" |
| "ldrd r0, r1, [r2, #20]!\n"); |
| COMPARE_T32(Ldrd(r0, r1, MemOperand(r2, -0xabcdec, PreIndex)), |
| "sub r2, #774144\n" |
| "sub r2, #10485760\n" |
| "ldrd r0, r1, [r2, #532]!\n"); |
| |
| // - Tests with register offsets. |
| |
| COMPARE_A32(Ldrd(r0, r1, MemOperand(r2, r3)), |
| "ldrd r0, r1, [r2, r3]\n"); |
| COMPARE_T32(Ldrd(r0, r1, MemOperand(r2, r3)), |
| "add r0, r2, r3\n" |
| "ldrd r0, r1, [r0]\n"); |
| |
| COMPARE_A32(Ldrd(r0, r1, MemOperand(r2, minus, r3)), |
| "ldrd r0, r1, [r2, -r3]\n"); |
| COMPARE_T32(Ldrd(r0, r1, MemOperand(r2, minus, r3)), |
| "sub r0, r2, r3\n" |
| "ldrd r0, r1, [r0]\n"); |
| |
| COMPARE_A32(Ldrd(r0, r1, MemOperand(r2, r3, PostIndex)), |
| "ldrd r0, r1, [r2], r3\n"); |
| COMPARE_T32(Ldrd(r0, r1, MemOperand(r2, r3, PostIndex)), |
| "ldrd r0, r1, [r2]\n" |
| "add r2, r3\n"); |
| |
| COMPARE_A32(Ldrd(r0, r1, MemOperand(r2, minus, r3, PostIndex)), |
| "ldrd r0, r1, [r2], -r3\n"); |
| COMPARE_T32(Ldrd(r0, r1, MemOperand(r2, minus, r3, PostIndex)), |
| "ldrd r0, r1, [r2]\n" |
| "sub r2, r3\n"); |
| |
| COMPARE_A32(Ldrd(r0, r1, MemOperand(r2, r3, PreIndex)), |
| "ldrd r0, r1, [r2, r3]!\n"); |
| COMPARE_T32(Ldrd(r0, r1, MemOperand(r2, r3, PreIndex)), |
| "add r2, r3\n" |
| "ldrd r0, r1, [r2]\n"); |
| |
| COMPARE_A32(Ldrd(r0, r1, MemOperand(r2, minus, r3, PreIndex)), |
| "ldrd r0, r1, [r2, -r3]!\n"); |
| COMPARE_T32(Ldrd(r0, r1, MemOperand(r2, minus, r3, PreIndex)), |
| "sub r2, r3\n" |
| "ldrd r0, r1, [r2]\n"); |
| |
| // - We do not support register shifted base register operands with LDRD. |
| |
| MUST_FAIL_TEST_BOTH(Ldrd(r0, r1, MemOperand(r2, r3, LSL, 4)), |
| "Ill-formed 'ldrd' instruction.\n"); |
| |
| // First register is odd - rejected by the Assembler. |
| MUST_FAIL_TEST_A32(Ldrd(r1, r2, MemOperand(r0)), |
| "Unpredictable instruction.\n"); |
| MUST_FAIL_TEST_A32(Ldrd(r1, r2, MemOperand(r0, r0, PreIndex)), |
| "Unpredictable instruction.\n"); |
| // First register is odd - rejected by the MacroAssembler. |
| MUST_FAIL_TEST_A32(Ldrd(r1, r2, MemOperand(r0, 0xabcd, PreIndex)), |
| "Ill-formed 'ldrd' instruction.\n"); |
| |
| // First register is lr - rejected by the Assembler. |
| MUST_FAIL_TEST_A32(Ldrd(lr, pc, MemOperand(r0)), |
| "Unpredictable instruction.\n"); |
| MUST_FAIL_TEST_A32(Ldrd(lr, pc, MemOperand(r0, r0, PreIndex)), |
| "Unpredictable instruction.\n"); |
| // First register is lr - rejected by the MacroAssembler. |
| MUST_FAIL_TEST_A32(Ldrd(lr, pc, MemOperand(r0, 0xabcd, PreIndex)), |
| "Ill-formed 'ldrd' instruction.\n"); |
| |
| // Non-adjacent registers. |
| MUST_FAIL_TEST_A32(Ldrd(r0, r2, MemOperand(r0)), |
| "Ill-formed 'ldrd' instruction.\n"); |
| |
| CLEANUP(); |
| } |
| |
| TEST(macro_assembler_strd) { |
| SETUP(); |
| |
| // - Tests with no offset. |
| |
| COMPARE_BOTH(Strd(r0, r1, MemOperand(r3)), |
| "strd r0, r1, [r3]\n"); |
| // Destination registers need to start with a even numbered register on A32. |
| MUST_FAIL_TEST_A32(Strd(r1, r2, MemOperand(r3)), |
| "Unpredictable instruction.\n"); |
| COMPARE_T32(Strd(r1, r2, MemOperand(r3)), |
| "strd r1, r2, [r3]\n"); |
| // Registers need to be adjacent on A32. |
| MUST_FAIL_TEST_A32(Strd(r0, r2, MemOperand(r1)), |
| "Ill-formed 'strd' instruction.\n"); |
| COMPARE_T32(Strd(r0, r2, MemOperand(r1)), |
| "strd r0, r2, [r1]\n"); |
| |
| COMPARE_BOTH(Strd(r0, r1, MemOperand(r2)), |
| "strd r0, r1, [r2]\n"); |
| |
| // - Tests with immediate offsets. |
| |
| COMPARE_A32(Strd(r0, r1, MemOperand(r2, 1020)), |
| "add ip, r2, #1020\n" |
| "strd r0, r1, [ip]\n"); |
| COMPARE_T32(Strd(r0, r1, MemOperand(r2, 1020)), |
| "strd r0, r1, [r2, #1020]\n"); |
| COMPARE_A32(Strd(r0, r1, MemOperand(r2, -1020)), |
| "sub ip, r2, #1020\n" |
| "strd r0, r1, [ip]\n"); |
| COMPARE_T32(Strd(r0, r1, MemOperand(r2, -1020)), |
| "strd r0, r1, [r2, #-1020]\n"); |
| |
| COMPARE_A32(Strd(r0, r1, MemOperand(r2, 0xabcc)), |
| "add ip, r2, #43776\n" |
| "strd r0, r1, [ip, #204]\n"); |
| COMPARE_T32(Strd(r0, r1, MemOperand(r2, 0xabcc)), |
| "add ip, r2, #43008\n" |
| "strd r0, r1, [ip, #972]\n"); |
| COMPARE_A32(Strd(r0, r1, MemOperand(r2, -0xabcc)), |
| "sub ip, r2, #44032\n" |
| "strd r0, r1, [ip, #52]\n"); |
| COMPARE_T32(Strd(r0, r1, MemOperand(r2, -0xabcc)), |
| "sub ip, r2, #44032\n" |
| "strd r0, r1, [ip, #52]\n"); |
| COMPARE_A32(Strd(r0, r1, MemOperand(r2, 0xabcdec)), |
| "add ip, r2, #52480\n" |
| "add ip, #11206656\n" |
| "strd r0, r1, [ip, #236]\n"); |
| COMPARE_T32(Strd(r0, r1, MemOperand(r2, 0xabcdec)), |
| "add ip, r2, #248832\n" |
| "add ip, #11010048\n" |
| "strd r0, r1, [ip, #492]\n"); |
| COMPARE_A32(Strd(r0, r1, MemOperand(r2, -0xabcdec)), |
| "sub ip, r2, #52736\n" |
| "sub ip, #11206656\n" |
| "strd r0, r1, [ip, #20]\n"); |
| COMPARE_T32(Strd(r0, r1, MemOperand(r2, -0xabcdec)), |
| "sub ip, r2, #774144\n" |
| "sub ip, #10485760\n" |
| "strd r0, r1, [ip, #532]\n"); |
| |
| COMPARE_A32(Strd(r0, r1, MemOperand(r0, 0xabcc)), |
| "add ip, r0, #43776\n" |
| "strd r0, r1, [ip, #204]\n"); |
| COMPARE_T32(Strd(r0, r1, MemOperand(r0, 0xabcc)), |
| "add ip, r0, #43008\n" |
| "strd r0, r1, [ip, #972]\n"); |
| COMPARE_A32(Strd(r0, r1, MemOperand(r0, -0xabcc)), |
| "sub ip, r0, #44032\n" |
| "strd r0, r1, [ip, #52]\n"); |
| COMPARE_T32(Strd(r0, r1, MemOperand(r0, -0xabcc)), |
| "sub ip, r0, #44032\n" |
| "strd r0, r1, [ip, #52]\n"); |
| COMPARE_A32(Strd(r0, r1, MemOperand(r0, 0xabcdec)), |
| "add ip, r0, #52480\n" |
| "add ip, #11206656\n" |
| "strd r0, r1, [ip, #236]\n"); |
| COMPARE_T32(Strd(r0, r1, MemOperand(r0, 0xabcdec)), |
| "add ip, r0, #248832\n" |
| "add ip, #11010048\n" |
| "strd r0, r1, [ip, #492]\n"); |
| COMPARE_A32(Strd(r0, r1, MemOperand(r0, -0xabcdec)), |
| "sub ip, r0, #52736\n" |
| "sub ip, #11206656\n" |
| "strd r0, r1, [ip, #20]\n"); |
| COMPARE_T32(Strd(r0, r1, MemOperand(r0, -0xabcdec)), |
| "sub ip, r0, #774144\n" |
| "sub ip, #10485760\n" |
| "strd r0, r1, [ip, #532]\n"); |
| |
| COMPARE_A32(Strd(r0, r1, MemOperand(r1, 0xabcc)), |
| "add ip, r1, #43776\n" |
| "strd r0, r1, [ip, #204]\n"); |
| COMPARE_T32(Strd(r0, r1, MemOperand(r1, 0xabcc)), |
| "add ip, r1, #43008\n" |
| "strd r0, r1, [ip, #972]\n"); |
| COMPARE_A32(Strd(r0, r1, MemOperand(r1, -0xabcc)), |
| "sub ip, r1, #44032\n" |
| "strd r0, r1, [ip, #52]\n"); |
| COMPARE_T32(Strd(r0, r1, MemOperand(r1, -0xabcc)), |
| "sub ip, r1, #44032\n" |
| "strd r0, r1, [ip, #52]\n"); |
| COMPARE_A32(Strd(r0, r1, MemOperand(r1, 0xabcdec)), |
| "add ip, r1, #52480\n" |
| "add ip, #11206656\n" |
| "strd r0, r1, [ip, #236]\n"); |
| COMPARE_T32(Strd(r0, r1, MemOperand(r1, 0xabcdec)), |
| "add ip, r1, #248832\n" |
| "add ip, #11010048\n" |
| "strd r0, r1, [ip, #492]\n"); |
| COMPARE_A32(Strd(r0, r1, MemOperand(r1, -0xabcdec)), |
| "sub ip, r1, #52736\n" |
| "sub ip, #11206656\n" |
| "strd r0, r1, [ip, #20]\n"); |
| COMPARE_T32(Strd(r0, r1, MemOperand(r1, -0xabcdec)), |
| "sub ip, r1, #774144\n" |
| "sub ip, #10485760\n" |
| "strd r0, r1, [ip, #532]\n"); |
| |
| COMPARE_A32(Strd(r0, r1, MemOperand(r2, 0xabcc, PostIndex)), |
| "strd r0, r1, [r2], #204\n" |
| "add r2, #43776\n"); |
| COMPARE_T32(Strd(r0, r1, MemOperand(r2, 0xabcc, PostIndex)), |
| "strd r0, r1, [r2], #972\n" |
| "add r2, #43008\n"); |
| COMPARE_A32(Strd(r0, r1, MemOperand(r2, -0xabcc, PostIndex)), |
| "strd r0, r1, [r2], #52\n" |
| "sub r2, #44032\n"); |
| COMPARE_T32(Strd(r0, r1, MemOperand(r2, -0xabcc, PostIndex)), |
| "strd r0, r1, [r2], #52\n" |
| "sub r2, #44032\n"); |
| COMPARE_A32(Strd(r0, r1, MemOperand(r2, 0xabcdec, PostIndex)), |
| "strd r0, r1, [r2], #236\n" |
| "add r2, #52480\n" |
| "add r2, #11206656\n"); |
| COMPARE_T32(Strd(r0, r1, MemOperand(r2, 0xabcdec, PostIndex)), |
| "strd r0, r1, [r2], #492\n" |
| "add r2, #248832\n" |
| "add r2, #11010048\n"); |
| COMPARE_A32(Strd(r0, r1, MemOperand(r2, -0xabcdec, PostIndex)), |
| "strd r0, r1, [r2], #20\n" |
| "sub r2, #52736\n" |
| "sub r2, #11206656\n"); |
| COMPARE_T32(Strd(r0, r1, MemOperand(r2, -0xabcdec, PostIndex)), |
| "strd r0, r1, [r2], #532\n" |
| "sub r2, #774144\n" |
| "sub r2, #10485760\n"); |
| |
| // PostIndex with the same register as base and source is invalid. |
| MUST_FAIL_TEST_BOTH(Strd(r0, r1, MemOperand(r0, 0xabcd, PostIndex)), |
| "Ill-formed 'strd' instruction.\n"); |
| MUST_FAIL_TEST_BOTH(Strd(r0, r1, MemOperand(r1, 0xabcdef, PostIndex)), |
| "Ill-formed 'strd' instruction.\n"); |
| |
| COMPARE_A32(Strd(r0, r1, MemOperand(r2, 0xabcc, PreIndex)), |
| "add r2, #43776\n" |
| "strd r0, r1, [r2, #204]!\n"); |
| COMPARE_T32(Strd(r0, r1, MemOperand(r2, 0xabcc, PreIndex)), |
| "add r2, #43008\n" |
| "strd r0, r1, [r2, #972]!\n"); |
| COMPARE_A32(Strd(r0, r1, MemOperand(r2, -0xabcc, PreIndex)), |
| "sub r2, #44032\n" |
| "strd r0, r1, [r2, #52]!\n"); |
| COMPARE_T32(Strd(r0, r1, MemOperand(r2, -0xabcc, PreIndex)), |
| "sub r2, #44032\n" |
| "strd r0, r1, [r2, #52]!\n"); |
| COMPARE_A32(Strd(r0, r1, MemOperand(r2, 0xabcdec, PreIndex)), |
| "add r2, #52480\n" |
| "add r2, #11206656\n" |
| "strd r0, r1, [r2, #236]!\n"); |
| COMPARE_T32(Strd(r0, r1, MemOperand(r2, 0xabcdec, PreIndex)), |
| "add r2, #248832\n" |
| "add r2, #11010048\n" |
| "strd r0, r1, [r2, #492]!\n"); |
| COMPARE_A32(Strd(r0, r1, MemOperand(r2, -0xabcdec, PreIndex)), |
| "sub r2, #52736\n" |
| "sub r2, #11206656\n" |
| "strd r0, r1, [r2, #20]!\n"); |
| COMPARE_T32(Strd(r0, r1, MemOperand(r2, -0xabcdec, PreIndex)), |
| "sub r2, #774144\n" |
| "sub r2, #10485760\n" |
| "strd r0, r1, [r2, #532]!\n"); |
| |
| // - Tests with register offsets. |
| |
| COMPARE_A32(Strd(r0, r1, MemOperand(r2, r3)), |
| "strd r0, r1, [r2, r3]\n"); |
| COMPARE_T32(Strd(r0, r1, MemOperand(r2, r3)), |
| "add ip, r2, r3\n" |
| "strd r0, r1, [ip]\n"); |
| |
| COMPARE_A32(Strd(r0, r1, MemOperand(r2, minus, r3)), |
| "strd r0, r1, [r2, -r3]\n"); |
| COMPARE_T32(Strd(r0, r1, MemOperand(r2, minus, r3)), |
| "sub ip, r2, r3\n" |
| "strd r0, r1, [ip]\n"); |
| |
| COMPARE_A32(Strd(r0, r1, MemOperand(r2, r3, PostIndex)), |
| "strd r0, r1, [r2], r3\n"); |
| COMPARE_T32(Strd(r0, r1, MemOperand(r2, r3, PostIndex)), |
| "strd r0, r1, [r2]\n" |
| "add r2, r3\n"); |
| |
| COMPARE_A32(Strd(r0, r1, MemOperand(r2, minus, r3, PostIndex)), |
| "strd r0, r1, [r2], -r3\n"); |
| COMPARE_T32(Strd(r0, r1, MemOperand(r2, minus, r3, PostIndex)), |
| "strd r0, r1, [r2]\n" |
| "sub r2, r3\n"); |
| |
| COMPARE_A32(Strd(r0, r1, MemOperand(r2, r3, PreIndex)), |
| "strd r0, r1, [r2, r3]!\n"); |
| COMPARE_T32(Strd(r0, r1, MemOperand(r2, r3, PreIndex)), |
| "add r2, r3\n" |
| "strd r0, r1, [r2]\n"); |
| |
| COMPARE_A32(Strd(r0, r1, MemOperand(r2, minus, r3, PreIndex)), |
| "strd r0, r1, [r2, -r3]!\n"); |
| COMPARE_T32(Strd(r0, r1, MemOperand(r2, minus, r3, PreIndex)), |
| "sub r2, r3\n" |
| "strd r0, r1, [r2]\n"); |
| |
| // - We do not support register shifted base register operands with LDRD. |
| |
| MUST_FAIL_TEST_BOTH(Strd(r0, r1, MemOperand(r2, r3, LSL, 4)), |
| "Ill-formed 'strd' instruction.\n"); |
| |
| // First register is odd - rejected by the Assembler. |
| MUST_FAIL_TEST_A32(Strd(r1, r2, MemOperand(r0)), |
| "Unpredictable instruction.\n"); |
| MUST_FAIL_TEST_A32(Strd(r1, r2, MemOperand(r0, r0, PreIndex)), |
| "Unpredictable instruction.\n"); |
| // First register is odd - rejected by the MacroAssembler. |
| MUST_FAIL_TEST_A32(Strd(r1, r2, MemOperand(r0, 0xabcd, PreIndex)), |
| "Ill-formed 'strd' instruction.\n"); |
| |
| // First register is lr - rejected by the Assembler. |
| MUST_FAIL_TEST_A32(Strd(lr, pc, MemOperand(r0)), |
| "Unpredictable instruction.\n"); |
| MUST_FAIL_TEST_A32(Strd(lr, pc, MemOperand(r0, r0, PreIndex)), |
| "Unpredictable instruction.\n"); |
| // First register is lr - rejected by the MacroAssembler. |
| MUST_FAIL_TEST_A32(Strd(lr, pc, MemOperand(r0, 0xabcd, PreIndex)), |
| "Ill-formed 'strd' instruction.\n"); |
| |
| // Non-adjacent registers. |
| MUST_FAIL_TEST_A32(Strd(r0, r2, MemOperand(r0)), |
| "Ill-formed 'strd' instruction.\n"); |
| |
| CLEANUP(); |
| } |
| |
| |
| TEST(macro_assembler_wide_immediate) { |
| SETUP(); |
| |
| COMPARE_BOTH(Adc(r0, r1, 0xbadbeef), |
| "mov r0, #48879\n" |
| "movt r0, #2989\n" |
| "adc r0, r1, r0\n"); |
| |
| COMPARE_BOTH(Add(r0, r0, 0xbadbeef), |
| "mov ip, #48879\n" |
| "movt ip, #2989\n" |
| "add r0, ip\n"); |
| |
| COMPARE_BOTH(Mov(r0, 0xbadbeef), |
| "mov r0, #48879\n" |
| "movt r0, #2989\n"); |
| COMPARE_A32(Mov(eq, r0, 0xbadbeef), |
| "moveq r0, #48879\n" |
| "movteq r0, #2989\n"); |
| COMPARE_T32(Mov(eq, r0, 0xbadbeef), |
| "bne 0x0000000a\n" |
| "mov r0, #48879\n" |
| "movt r0, #2989\n"); |
| |
| COMPARE_BOTH(Movs(r0, 0xbadbeef), |
| "mov r0, #48879\n" |
| "movt r0, #2989\n" |
| "tst r0, r0\n"); |
| COMPARE_A32(Movs(eq, r0, 0xbadbeef), |
| "moveq r0, #48879\n" |
| "movteq r0, #2989\n" |
| "tsteq r0, r0\n"); |
| COMPARE_T32(Movs(eq, r0, 0xbadbeef), |
| "bne 0x0000000c\n" |
| "mov r0, #48879\n" |
| "movt r0, #2989\n" |
| "tst r0, r0\n"); |
| COMPARE_A32(Movs(pc, 0x1), |
| "movs pc, #1\n"); |
| MUST_FAIL_TEST_T32(Movs(pc, 0x1), "Unpredictable instruction.\n"); |
| MUST_FAIL_TEST_BOTH(Movs(pc, 0xbadbeed), |
| "Ill-formed 'movs' instruction.\n"); |
| |
| COMPARE_BOTH(Mov(pc, 0xbadbeef), |
| "mov ip, #48879\n" |
| "movt ip, #2989\n" |
| "bx ip\n"); |
| COMPARE_A32(Mov(eq, pc, 0xbadbeef), |
| "mov ip, #48879\n" |
| "movt ip, #2989\n" |
| "bxeq ip\n"); |
| COMPARE_T32(Mov(eq, pc, 0xbadbeef), |
| "bne 0x0000000c\n" |
| "mov ip, #48879\n" |
| "movt ip, #2989\n" |
| "bx ip\n"); |
| |
| CLEANUP(); |
| } |
| |
| |
| TEST(macro_assembler_And) { |
| SETUP(); |
| |
| // Identities. |
| COMPARE_BOTH(And(r0, r1, 0), |
| "mov r0, #0\n"); |
| COMPARE_BOTH(And(r0, r0, 0xffffffff), |
| ""); |
| CLEANUP(); |
| } |
| |
| |
| TEST(macro_assembler_Bic) { |
| SETUP(); |
| |
| // Identities. |
| COMPARE_BOTH(Bic(r0, r1, 0xffffffff), |
| "mov r0, #0\n"); |
| COMPARE_BOTH(Bic(r0, r0, 0), |
| ""); |
| CLEANUP(); |
| } |
| |
| |
| TEST(macro_assembler_Orr) { |
| SETUP(); |
| |
| // Identities. |
| COMPARE_BOTH(Orr(r0, r1, 0xffffffff), |
| "mvn r0, #0\n"); |
| COMPARE_BOTH(Orr(r0, r0, 0), |
| ""); |
| CLEANUP(); |
| } |
| |
| |
| TEST(macro_assembler_InstructionCondSizeRROp) { |
| SETUP(); |
| |
| // Special case for Orr <-> Orn correspondance. |
| |
| COMPARE_T32(Orr(r0, r1, 0x00ffffff), |
| "orn r0, r1, #0xff000000\n"); |
| COMPARE_T32(Orrs(r0, r1, 0x00ffffff), |
| "orns r0, r1, #0xff000000\n"); |
| |
| // Encodable immediates. |
| |
| COMPARE_A32(Add(r0, r1, -1), |
| "sub r0, r1, #1\n"); |
| COMPARE_A32(Adds(r0, r1, -1), |
| "subs r0, r1, #1\n"); |
| // 0xffffffff is encodable in a T32 ADD. |
| COMPARE_T32(Add(r0, r1, -1), |
| "add r0, r1, #4294967295\n"); |
| COMPARE_T32(Adds(r0, r1, -1), |
| "adds r0, r1, #4294967295\n"); |
| |
| COMPARE_BOTH(Add(r0, r1, -4), |
| "sub r0, r1, #4\n"); |
| COMPARE_BOTH(Adds(r0, r1, -4), |
| "subs r0, r1, #4\n"); |
| |
| COMPARE_BOTH(Adc(r0, r1, -2), |
| "sbc r0, r1, #1\n"); |
| COMPARE_BOTH(Adcs(r0, r1, -2), |
| "sbcs r0, r1, #1\n"); |
| |
| COMPARE_A32(Sub(r0, r1, -1), |
| "add r0, r1, #1\n"); |
| COMPARE_A32(Subs(r0, r1, -1), |
| "adds r0, r1, #1\n"); |
| // 0xffffffff is encodable in a T32 SUB. |
| COMPARE_T32(Sub(r0, r1, -1), |
| "sub r0, r1, #4294967295\n"); |
| COMPARE_T32(Subs(r0, r1, -1), |
| "subs r0, r1, #4294967295\n"); |
| |
| COMPARE_BOTH(Sub(r0, r1, -4), |
| "add r0, r1, #4\n"); |
| COMPARE_BOTH(Subs(r0, r1, -4), |
| "adds r0, r1, #4\n"); |
| |
| COMPARE_BOTH(Sbc(r0, r1, -5), |
| "adc r0, r1, #4\n"); |
| COMPARE_BOTH(Sbcs(r0, r1, -5), |
| "adcs r0, r1, #4\n"); |
| |
| // Non-encodable immediates |
| |
| COMPARE_BOTH(Adc(r0, r1, 0xabcd), |
| "mov r0, #43981\n" |
| "adc r0, r1, r0\n"); |
| |
| COMPARE_BOTH(Adc(r0, r1, -0xabcd), |
| "mov r0, #43980\n" // This represents #0xabcd - 1. |
| "sbc r0, r1, r0\n"); |
| |
| COMPARE_BOTH(Adc(r0, r1, 0x1234abcd), |
| "mov r0, #43981\n" |
| "movt r0, #4660\n" |
| "adc r0, r1, r0\n"); |
| |
| COMPARE_BOTH(Adc(r0, r1, -0x1234abcd), |
| "mov r0, #43980\n" // This represents #0x1234abcd - 1. |
| "movt r0, #4660\n" |
| "sbc r0, r1, r0\n"); |
| |
| // Non-encodable immediates with the same source and destination registers. |
| |
| COMPARE_BOTH(Sbc(r0, r0, 0xabcd), |
| "mov ip, #43981\n" |
| "sbc r0, ip\n"); |
| |
| COMPARE_BOTH(Sbc(r0, r0, -0xabcd), |
| "mov ip, #43980\n" // This represents #0xabcd - 1. |
| "adc r0, ip\n"); |
| |
| COMPARE_BOTH(Sbc(r0, r0, 0x1234abcd), |
| "mov ip, #43981\n" |
| "movt ip, #4660\n" |
| "sbc r0, ip\n"); |
| |
| COMPARE_BOTH(Sbc(r0, r0, -0x1234abcd), |
| "mov ip, #43980\n" // This represents #0x1234abcd - 1. |
| "movt ip, #4660\n" |
| "adc r0, ip\n"); |
| |
| |
| // Test that we can pass a register shifted register operand in T32. |
| |
| COMPARE_T32(Adc(r0, r1, Operand(r2, LSL, r3)), |
| "lsl r0, r2, r3\n" |
| "adc r0, r1, r0\n"); |
| |
| COMPARE_T32(Add(r3, r2, Operand(r2, ASR, r3)), |
| "asr r3, r2, r3\n" |
| "add r3, r2, r3\n"); |
| |
| COMPARE_T32(Ands(r3, r2, Operand(r2, LSR, r2)), |
| "lsr r3, r2, r2\n" |
| "ands r3, r2, r3\n"); |
| |
| COMPARE_T32(Asr(r2, r2, Operand(r2, ROR, r2)), |
| "ror ip, r2, r2\n" |
| "asr r2, ip\n"); |
| |
| COMPARE_T32(Asr(r2, r2, Operand(r2, ROR, r2)), |
| "ror ip, r2, r2\n" |
| "asr r2, ip\n"); |
| |
| |
| CLEANUP(); |
| } |
| |
| |
| TEST(macro_assembler_InstructionCondRO) { |
| SETUP(); |
| |
| COMPARE_BOTH(Teq(r0, 0xbad), |
| "mov ip, #2989\n" |
| "teq r0, ip\n"); |
| COMPARE_BOTH(Teq(r0, 0xbadbeef), |
| "mov ip, #48879\n" |
| "movt ip, #2989\n" |
| "teq r0, ip\n"); |
| MUST_FAIL_TEST_T32(Teq(r0, Operand(r1, LSL, r2)), |
| "Ill-formed 'teq' instruction.\n"); |
| |
| CLEANUP(); |
| } |
| |
| |
| TEST(macro_assembler_too_large_immediate) { |
| SETUP(); |
| |
| // Attempting to use a 17-bit immediate with movt. |
| MUST_FAIL_TEST_BOTH(Movt(r0, 0x10000), "`Movt` expects a 16-bit immediate."); |
| |
| CLEANUP(); |
| } |
| |
| TEST(macro_assembler_Cbz) { |
| SETUP(); |
| |
| #ifdef VIXL_INCLUDE_TARGET_A32 |
| // Cbz/Cbnz are not available in A32 mode. |
| // Make sure GetArchitectureStatePCOffset() returns the correct value. |
| __ UseA32(); |
| Label label_64(__ GetCursorOffset() + __ GetArchitectureStatePCOffset(), 64); |
| MUST_FAIL_TEST_A32(Cbz(r0, &label_64), "Cbz is only available for T32.\n"); |
| MUST_FAIL_TEST_A32(Cbnz(r0, &label_64), "Cbnz is only available for T32.\n"); |
| #endif |
| |
| #ifdef VIXL_INCLUDE_TARGET_T32 |
| // Make sure GetArchitectureStatePCOffset() returns the correct value. |
| __ UseT32(); |
| // Largest encodable offset. |
| Label label_126(__ GetCursorOffset() + __ GetArchitectureStatePCOffset(), 126); |
| COMPARE_T32(Cbz(r0, &label_126), |
| "cbz r0, 0x00000082\n"); |
| COMPARE_T32(Cbnz(r0, &label_126), |
| "cbnz r0, 0x00000082\n"); |
| |
| // Offset cannot be encoded. |
| Label label_128(__ GetCursorOffset() + __ GetArchitectureStatePCOffset(), 128); |
| COMPARE_T32(Cbz(r0, &label_128), |
| "cbnz r0, 0x00000004\n" |
| "b 0x00000084\n"); |
| COMPARE_T32(Cbnz(r0, &label_128), |
| "cbz r0, 0x00000004\n" |
| "b 0x00000084\n"); |
| |
| // Offset that cannot be encoded and needs 32-bit branch instruction. |
| Label label_8192(__ GetCursorOffset() + __ GetArchitectureStatePCOffset(), 8192); |
| COMPARE_T32(Cbz(r0, &label_8192), |
| "cbnz r0, 0x00000006\n" |
| "b 0x00002004\n"); |
| COMPARE_T32(Cbnz(r0, &label_8192), |
| "cbz r0, 0x00000006\n" |
| "b 0x00002004\n"); |
| |
| // Negative offset. |
| Label label_neg(__ GetCursorOffset() + __ GetArchitectureStatePCOffset(), -8); |
| COMPARE_T32(Cbz(r0, &label_neg), |
| "cbnz r0, 0x00000004\n" |
| "b 0xfffffffc\n"); |
| COMPARE_T32(Cbnz(r0, &label_neg), |
| "cbz r0, 0x00000004\n" |
| "b 0xfffffffc\n"); |
| |
| // Large negative offset. |
| Label label_neg128(__ GetCursorOffset() + __ GetArchitectureStatePCOffset(), -128); |
| COMPARE_T32(Cbz(r0, &label_neg128), |
| "cbnz r0, 0x00000004\n" |
| "b 0xffffff84\n"); |
| COMPARE_T32(Cbnz(r0, &label_neg128), |
| "cbz r0, 0x00000004\n" |
| "b 0xffffff84\n"); |
| #endif |
| |
| CLEANUP(); |
| } |
| |
| |
| #define TEST_VMEMOP(MACRO_OP, STRING_OP, DST_REG) \ |
| SETUP(); \ |
| \ |
| COMPARE_T32(MACRO_OP(DST_REG, MemOperand(r8, 1024)), \ |
| "add ip, r8, #1024\n" \ |
| STRING_OP # DST_REG ", [ip]\n"); \ |
| COMPARE_T32(MACRO_OP(DST_REG, MemOperand(r8, 1371)), \ |
| "add ip, r8, #1371\n" \ |
| STRING_OP # DST_REG ", [ip]\n"); \ |
| COMPARE_T32(MACRO_OP(DST_REG, MemOperand(r8, 4113)), \ |
| "add ip, r8, #17\n" \ |
| "add ip, #4096\n" \ |
| STRING_OP # DST_REG ", [ip]\n"); \ |
| COMPARE_T32(MACRO_OP(DST_REG, MemOperand(r8, 65808)), \ |
| "add ip, r8, #272\n" \ |
| "add ip, #65536\n" \ |
| STRING_OP # DST_REG ", [ip]\n"); \ |
| \ |
| COMPARE_T32(MACRO_OP(DST_REG, MemOperand(r8, -1024)), \ |
| "sub ip, r8, #1024\n" \ |
| STRING_OP # DST_REG ", [ip]\n"); \ |
| COMPARE_T32(MACRO_OP(DST_REG, MemOperand(r8, -1371)), \ |
| "sub ip, r8, #1371\n" \ |
| STRING_OP # DST_REG ", [ip]\n"); \ |
| COMPARE_T32(MACRO_OP(DST_REG, MemOperand(r8, -4113)), \ |
| "sub ip, r8, #17\n" \ |
| "sub ip, #4096\n" \ |
| STRING_OP # DST_REG ", [ip]\n"); \ |
| COMPARE_T32(MACRO_OP(DST_REG, MemOperand(r8, -65808)), \ |
| "sub ip, r8, #272\n" \ |
| "sub ip, #65536\n" \ |
| STRING_OP # DST_REG ", [ip]\n"); \ |
| \ |
| COMPARE_T32(MACRO_OP(DST_REG, MemOperand(r9, 0, PreIndex)), \ |
| STRING_OP # DST_REG ", [r9]\n"); \ |
| COMPARE_T32(MACRO_OP(DST_REG, MemOperand(r9, 137, PreIndex)), \ |
| "add r9, #137\n" \ |
| STRING_OP # DST_REG ", [r9]\n"); \ |
| COMPARE_T32(MACRO_OP(DST_REG, MemOperand(r9, 4110, PreIndex)), \ |
| "add r9, #14\n" \ |
| "add r9, #4096\n" \ |
| STRING_OP # DST_REG ", [r9]\n"); \ |
| COMPARE_T32(MACRO_OP(DST_REG, MemOperand(r9, 65623, PreIndex)), \ |
| "add r9, #87\n" \ |
| "add r9, #65536\n" \ |
| STRING_OP # DST_REG ", [r9]\n"); \ |
| \ |
| COMPARE_T32(MACRO_OP(DST_REG, MemOperand(r9, -137, PreIndex)), \ |
| "sub r9, #137\n" \ |
| STRING_OP # DST_REG ", [r9]\n"); \ |
| COMPARE_T32(MACRO_OP(DST_REG, MemOperand(r9, -4110, PreIndex)), \ |
| "sub r9, #14\n" \ |
| "sub r9, #4096\n" \ |
| STRING_OP # DST_REG ", [r9]\n"); \ |
| COMPARE_T32(MACRO_OP(DST_REG, MemOperand(r9, -65623, PreIndex)), \ |
| "sub r9, #87\n" \ |
| "sub r9, #65536\n" \ |
| STRING_OP # DST_REG ", [r9]\n"); \ |
| \ |
| COMPARE_T32(MACRO_OP(DST_REG, MemOperand(r10, 0, PostIndex)), \ |
| STRING_OP # DST_REG ", [r10]\n"); \ |
| COMPARE_T32(MACRO_OP(DST_REG, MemOperand(r10, 137, PostIndex)), \ |
| STRING_OP # DST_REG ", [r10]\n" \ |
| "add r10, #137\n"); \ |
| COMPARE_T32(MACRO_OP(DST_REG, MemOperand(r10, 4110, PostIndex)), \ |
| STRING_OP # DST_REG ", [r10]\n" \ |
| "add r10, #14\n" \ |
| "add r10, #4096\n"); \ |
| COMPARE_T32(MACRO_OP(DST_REG, MemOperand(r10, 65623, PostIndex)), \ |
| STRING_OP # DST_REG ", [r10]\n" \ |
| "add r10, #87\n" \ |
| "add r10, #65536\n"); \ |
| \ |
| COMPARE_T32(MACRO_OP(DST_REG, MemOperand(r10, -137, PostIndex)), \ |
| STRING_OP # DST_REG ", [r10]\n" \ |
| "sub r10, #137\n"); \ |
| COMPARE_T32(MACRO_OP(DST_REG, MemOperand(r10, -4110, PostIndex)), \ |
| STRING_OP # DST_REG ", [r10]\n" \ |
| "sub r10, #14\n" \ |
| "sub r10, #4096\n"); \ |
| COMPARE_T32(MACRO_OP(DST_REG, MemOperand(r10, -65623, PostIndex)), \ |
| STRING_OP # DST_REG ", [r10]\n" \ |
| "sub r10, #87\n" \ |
| "sub r10, #65536\n"); \ |
| CLEANUP(); |
| |
| TEST(macro_assembler_T32_Vldr_d) { |
| TEST_VMEMOP(Vldr, "vldr ", d0); |
| } |
| |
| TEST(macro_assembler_T32_Vstr_d) { |
| TEST_VMEMOP(Vstr, "vstr ", d1); |
| } |
| |
| TEST(macro_assembler_T32_Vldr_s) { |
| TEST_VMEMOP(Vldr, "vldr ", s2); |
| } |
| |
| TEST(macro_assembler_T32_Vstr_s) { |
| TEST_VMEMOP(Vstr, "vstr ", s3); |
| } |
| |
| #undef TEST_VMEMOP |
| |
| #define TEST_VMEMOP(MACRO_OP, STRING_OP, DST_REG) \ |
| SETUP(); \ |
| \ |
| COMPARE_A32(MACRO_OP(DST_REG, MemOperand(r8, 137)), \ |
| "add ip, r8, #137\n" \ |
| STRING_OP # DST_REG ", [ip]\n"); \ |
| COMPARE_A32(MACRO_OP(DST_REG, MemOperand(r8, 274)), \ |
| "add ip, r8, #18\n" \ |
| "add ip, #256\n" \ |
| STRING_OP # DST_REG ", [ip]\n"); \ |
| COMPARE_A32(MACRO_OP(DST_REG, MemOperand(r8, 65623)), \ |
| "add ip, r8, #87\n" \ |
| "add ip, #65536\n" \ |
| STRING_OP # DST_REG ", [ip]\n"); \ |
| \ |
| COMPARE_A32(MACRO_OP(DST_REG, MemOperand(r8, -137)), \ |
| "sub ip, r8, #137\n" \ |
| STRING_OP # DST_REG ", [ip]\n"); \ |
| COMPARE_A32(MACRO_OP(DST_REG, MemOperand(r8, -274)), \ |
| "sub ip, r8, #18\n" \ |
| "sub ip, #256\n" \ |
| STRING_OP # DST_REG ", [ip]\n"); \ |
| COMPARE_A32(MACRO_OP(DST_REG, MemOperand(r8, -65623)), \ |
| "sub ip, r8, #87\n" \ |
| "sub ip, #65536\n" \ |
| STRING_OP # DST_REG ", [ip]\n"); \ |
| \ |
| COMPARE_A32(MACRO_OP(DST_REG, MemOperand(r9, 0, PreIndex)), \ |
| STRING_OP # DST_REG ", [r9]\n"); \ |
| COMPARE_A32(MACRO_OP(DST_REG, MemOperand(r9, 137, PreIndex)), \ |
| "add r9, #137\n" \ |
| STRING_OP # DST_REG ", [r9]\n"); \ |
| COMPARE_A32(MACRO_OP(DST_REG, MemOperand(r9, 274, PreIndex)), \ |
| "add r9, #18\n" \ |
| "add r9, #256\n" \ |
| STRING_OP # DST_REG ", [r9]\n"); \ |
| COMPARE_A32(MACRO_OP(DST_REG, MemOperand(r9, 65623, PreIndex)), \ |
| "add r9, #87\n" \ |
| "add r9, #65536\n" \ |
| STRING_OP # DST_REG ", [r9]\n"); \ |
| \ |
| COMPARE_A32(MACRO_OP(DST_REG, MemOperand(r9, -137, PreIndex)), \ |
| "sub r9, #137\n" \ |
| STRING_OP # DST_REG ", [r9]\n"); \ |
| COMPARE_A32(MACRO_OP(DST_REG, MemOperand(r9, -274, PreIndex)), \ |
| "sub r9, #18\n" \ |
| "sub r9, #256\n" \ |
| STRING_OP # DST_REG ", [r9]\n"); \ |
| COMPARE_A32(MACRO_OP(DST_REG, MemOperand(r9, -65623, PreIndex)), \ |
| "sub r9, #87\n" \ |
| "sub r9, #65536\n" \ |
| STRING_OP # DST_REG ", [r9]\n"); \ |
| \ |
| COMPARE_A32(MACRO_OP(DST_REG, MemOperand(r10, 0, PostIndex)), \ |
| STRING_OP # DST_REG ", [r10]\n"); \ |
| COMPARE_A32(MACRO_OP(DST_REG, MemOperand(r10, 137, PostIndex)), \ |
| STRING_OP # DST_REG ", [r10]\n" \ |
| "add r10, #137\n"); \ |
| COMPARE_A32(MACRO_OP(DST_REG, MemOperand(r10, 274, PostIndex)), \ |
| STRING_OP # DST_REG ", [r10]\n" \ |
| "add r10, #18\n" \ |
| "add r10, #256\n"); \ |
| COMPARE_A32(MACRO_OP(DST_REG, MemOperand(r10, 65623, PostIndex)), \ |
| STRING_OP # DST_REG ", [r10]\n" \ |
| "add r10, #87\n" \ |
| "add r10, #65536\n"); \ |
| \ |
| COMPARE_A32(MACRO_OP(DST_REG, MemOperand(r10, -137, PostIndex)), \ |
| STRING_OP # DST_REG ", [r10]\n" \ |
| "sub r10, #137\n"); \ |
| COMPARE_A32(MACRO_OP(DST_REG, MemOperand(r10, -274, PostIndex)), \ |
| STRING_OP # DST_REG ", [r10]\n" \ |
| "sub r10, #18\n" \ |
| "sub r10, #256\n"); \ |
| COMPARE_A32(MACRO_OP(DST_REG, MemOperand(r10, -65623, PostIndex)), \ |
| STRING_OP # DST_REG ", [r10]\n" \ |
| "sub r10, #87\n" \ |
| "sub r10, #65536\n"); \ |
| CLEANUP(); |
| |
| |
| TEST(macro_assembler_A32_Vldr_d) { |
| TEST_VMEMOP(Vldr, "vldr ", d0); |
| } |
| |
| TEST(macro_assembler_A32_Vstr_d) { |
| TEST_VMEMOP(Vstr, "vstr ", d1); |
| } |
| |
| TEST(macro_assembler_A32_Vldr_s) { |
| TEST_VMEMOP(Vldr, "vldr ", s2); |
| } |
| |
| TEST(macro_assembler_A32_Vstr_s) { |
| TEST_VMEMOP(Vstr, "vstr ", s3); |
| } |
| |
| #undef TEST_VMEMOP |
| |
| TEST(macro_assembler_Vldr_Vstr_negative) { |
| SETUP(); |
| |
| MUST_FAIL_TEST_BOTH(Vldr(s0, MemOperand(pc, 1, PreIndex)), |
| "The MacroAssembler does not convert vldr or vstr" |
| " with a PC base register.\n"); |
| |
| MUST_FAIL_TEST_BOTH(Vldr(s0, MemOperand(pc, r0, PreIndex)), |
| "Ill-formed 'vldr' instruction.\n"); |
| |
| MUST_FAIL_TEST_BOTH(Vstr(s0, MemOperand(pc, 1, PreIndex)), |
| "The MacroAssembler does not convert vldr or vstr" |
| " with a PC base register.\n"); |
| |
| MUST_FAIL_TEST_BOTH(Vstr(s0, MemOperand(pc, r0, PreIndex)), |
| "Ill-formed 'vstr' instruction.\n"); |
| |
| MUST_FAIL_TEST_BOTH(Vldr(d0, MemOperand(pc, 1, PreIndex)), |
| "The MacroAssembler does not convert vldr or vstr" |
| " with a PC base register.\n"); |
| |
| MUST_FAIL_TEST_BOTH(Vldr(d0, MemOperand(pc, r0, PreIndex)), |
| "Ill-formed 'vldr' instruction.\n"); |
| |
| MUST_FAIL_TEST_BOTH(Vstr(d0, MemOperand(pc, 1, PreIndex)), |
| "The MacroAssembler does not convert vldr or vstr" |
| " with a PC base register.\n"); |
| |
| MUST_FAIL_TEST_BOTH(Vstr(d0, MemOperand(pc, r0, PreIndex)), |
| "Ill-formed 'vstr' instruction.\n"); |
| CLEANUP(); |
| } |
| |
| #define TEST_SHIFT_T32(Inst, name, offset) \ |
| COMPARE_T32(Inst(r0, Operand(r1, LSL, r2)), \ |
| "lsl ip, r1, r2\n" \ |
| name " r0, ip\n"); \ |
| COMPARE_T32(Inst(r0, Operand(r1, LSR, r2)), \ |
| "lsr ip, r1, r2\n" \ |
| name " r0, ip\n"); \ |
| COMPARE_T32(Inst(r0, Operand(r1, ASR, r2)), \ |
| "asr ip, r1, r2\n" \ |
| name " r0, ip\n"); \ |
| COMPARE_T32(Inst(r0, Operand(r1, ROR, r2)), \ |
| "ror ip, r1, r2\n" \ |
| name " r0, ip\n"); \ |
| COMPARE_T32(Inst(eq, r0, Operand(r1, LSL, r2)),\ |
| "bne "#offset"\n" \ |
| "lsl ip, r1, r2\n" \ |
| name " r0, ip\n"); \ |
| COMPARE_T32(Inst(le, r0, Operand(r1, LSL, r2)),\ |
| "bgt "#offset"\n" \ |
| "lsl ip, r1, r2\n" \ |
| name " r0, ip\n"); |
| |
| #define TEST_MOV_SHIFT_T32(Inst, s, offset) \ |
| COMPARE_T32(Inst(r0, Operand(r1, LSL, r2)), \ |
| "lsl" s " r0, r1, r2\n"); \ |
| COMPARE_T32(Inst(r0, Operand(r1, LSR, r2)), \ |
| "lsr" s " r0, r1, r2\n"); \ |
| COMPARE_T32(Inst(r0, Operand(r1, ASR, r2)), \ |
| "asr" s " r0, r1, r2\n"); \ |
| COMPARE_T32(Inst(r0, Operand(r1, ROR, r2)), \ |
| "ror" s " r0, r1, r2\n"); \ |
| COMPARE_T32(Inst(eq, r0, Operand(r1, LSL, r2)),\ |
| "bne "#offset"\n" \ |
| "lsl" s " r0, r1, r2\n"); \ |
| COMPARE_T32(Inst(le, r0, Operand(r1, LSL, r2)),\ |
| "bgt "#offset"\n" \ |
| "lsl" s " r0, r1, r2\n"); |
| |
| #define TEST_WIDE_IMMEDIATE(Inst, name, offset) \ |
| COMPARE_BOTH(Inst(r0, 0xbadbeef), \ |
| "mov ip, #48879\n" \ |
| "movt ip, #2989\n" \ |
| name " r0, ip\n"); \ |
| COMPARE_A32(Inst(eq, r0, 0xbadbeef), \ |
| "moveq ip, #48879\n" \ |
| "movteq ip, #2989\n" \ |
| name "eq r0, ip\n"); \ |
| COMPARE_T32(Inst(eq, r0, 0xbadbeef), \ |
| "bne "#offset"\n" \ |
| "mov ip, #48879\n" \ |
| "movt ip, #2989\n" \ |
| name " r0, ip\n"); |
| |
| #define TEST_WIDE_IMMEDIATE_PC(Inst, name, offset) \ |
| COMPARE_A32(Inst(pc, 0xbadbeef), \ |
| "mov ip, #48879\n" \ |
| "movt ip, #2989\n" \ |
| name " pc, ip\n"); \ |
| COMPARE_A32(Inst(eq, pc, 0xbadbeef), \ |
| "moveq ip, #48879\n" \ |
| "movteq ip, #2989\n" \ |
| name "eq pc, ip\n"); \ |
| MUST_FAIL_TEST_T32(Inst(pc, 0xbadbeef), \ |
| "Ill-formed '" name "' instruction.\n"); \ |
| MUST_FAIL_TEST_T32(Inst(eq, pc, 0xbadbeef), \ |
| "Ill-formed '" name "' instruction.\n"); \ |
| |
| TEST(macro_assembler_InstructionCondSizeROp) { |
| SETUP(); |
| |
| // T32 register shifted register. |
| TEST_SHIFT_T32(Cmn,"cmn", 0x0000000a) |
| TEST_SHIFT_T32(Cmp,"cmp", 0x00000008) |
| TEST_SHIFT_T32(Mvn,"mvn", 0x0000000a) |
| TEST_SHIFT_T32(Mvns,"mvns", 0x0000000a) |
| TEST_SHIFT_T32(Sxtb,"sxtb", 0x0000000a) |
| TEST_SHIFT_T32(Sxth,"sxth", 0x0000000a) |
| TEST_SHIFT_T32(Tst,"tst", 0x0000000a) |
| TEST_SHIFT_T32(Uxtb,"uxtb", 0x0000000a) |
| TEST_SHIFT_T32(Uxth,"uxth", 0x0000000a) |
| |
| TEST_MOV_SHIFT_T32(Mov, "", 0x00000006) |
| TEST_MOV_SHIFT_T32(Movs, "s", 0x00000006) |
| |
| MUST_FAIL_TEST_BOTH(Movs(pc, r0), |
| "Unpredictable instruction.\n"); |
| MUST_FAIL_TEST_BOTH(Movs(pc, Operand(r0, LSL, 0x4)), |
| "Unpredictable instruction.\n"); |
| MUST_FAIL_TEST_BOTH(Movs(pc, Operand(r0, ASR, r2)), |
| "Unpredictable instruction.\n"); |
| |
| // Wide immediates (Mov and Movs are tested in "macro_assembler_wide_immediate"). |
| TEST_WIDE_IMMEDIATE(Cmp, "cmp", 0x0000000c); |
| TEST_WIDE_IMMEDIATE(Cmn, "cmn", 0x0000000e); |
| TEST_WIDE_IMMEDIATE(Tst, "tst", 0x0000000e); |
| TEST_WIDE_IMMEDIATE_PC(Cmp, "cmp", 0x0000000c); |
| TEST_WIDE_IMMEDIATE_PC(Cmn, "cmn", 0x0000000e); |
| TEST_WIDE_IMMEDIATE_PC(Tst, "tst", 0x0000000e); |
| |
| // For Mvn and Mvns, we don't allow PC as a destination. |
| TEST_WIDE_IMMEDIATE(Mvn, "mvn", 0x0000000e); |
| TEST_WIDE_IMMEDIATE(Mvns, "mvns", 0x0000000e); |
| MUST_FAIL_TEST_BOTH(Mvn(pc, 0xbadbeef), "Ill-formed 'mvn' instruction.\n"); |
| MUST_FAIL_TEST_BOTH(Mvn(eq, pc, 0xbadbeef), "Ill-formed 'mvn' instruction.\n"); |
| MUST_FAIL_TEST_BOTH(Mvns(pc, 0xbadbeef), "Ill-formed 'mvns' instruction.\n"); |
| MUST_FAIL_TEST_BOTH(Mvns(eq, pc, 0xbadbeef), "Ill-formed 'mvns' instruction.\n"); |
| |
| MUST_FAIL_TEST_BOTH(Sxtb(r0, 0x1), "Ill-formed 'sxtb' instruction.\n"); |
| MUST_FAIL_TEST_BOTH(Sxth(r0, 0x1), "Ill-formed 'sxth' instruction.\n"); |
| MUST_FAIL_TEST_BOTH(Uxtb(r0, 0x1), "Ill-formed 'uxtb' instruction.\n"); |
| MUST_FAIL_TEST_BOTH(Uxth(r0, 0x1), "Ill-formed 'uxth' instruction.\n"); |
| |
| CLEANUP(); |
| } |
| |
| #undef TEST_SHIFT_T32 |
| #undef TEST_MOV_SHIFT_T32 |
| #undef TEST_WIDE_IMMEDIATE |
| #undef TEST_WIDE_IMMEDIATE_PC |
| |
| TEST(macro_assembler_Msr) { |
| SETUP(); |
| |
| // Msr with immediate for T32. |
| COMPARE_T32(Msr(APSR_nzcvq, 0x0), |
| "mov ip, #0\n" |
| "msr APSR_nzcvq, ip\n"); |
| |
| // Wide immediate. |
| COMPARE_BOTH(Msr(APSR_nzcvq, 0xbadbeef), |
| "mov ip, #48879\n" |
| "movt ip, #2989\n" |
| "msr APSR_nzcvq, ip\n"); |
| |
| // Other types of operands are not handled. |
| MUST_FAIL_TEST_BOTH(Msr(APSR_nzcvq, Operand(r0, LSR, r1)), |
| "Ill-formed 'msr' instruction.\n"); |
| CLEANUP(); |
| } |
| |
| |
| TEST(macro_assembler_Vmov_imm) { |
| SETUP(); |
| |
| COMPARE_BOTH(Vmov(s0, 0.0f), |
| "mov ip, #0\n" |
| "vmov s0, ip\n"); |
| COMPARE_BOTH(Vmov(s1, 1.0f), |
| "vmov.f32 s1, #1\n"); |
| COMPARE_BOTH(Vmov(s2, RawbitsToFloat(0x0000db6c)), |
| "mov ip, #56172\n" |
| "vmov s2, ip\n"); |
| COMPARE_BOTH(Vmov(s3, RawbitsToFloat(0x327b23c6)), |
| "mov ip, #9158\n" |
| "movt ip, #12923\n" |
| "vmov s3, ip\n"); |
| COMPARE_BOTH(Vmov(s4, RawbitsToFloat(0xffcc7fff)), |
| "mvn ip, #3375104\n" |
| "vmov s4, ip\n"); |
| COMPARE_BOTH(Vmov(s5, RawbitsToFloat(0xb72df575)), |
| "mov ip, #62837\n" |
| "movt ip, #46893\n" |
| "vmov s5, ip\n"); |
| |
| COMPARE_BOTH(Vmov(d6, 0.0), |
| "vmov.i64 d6, #0x0000000000000000\n"); |
| COMPARE_BOTH(Vmov(d7, 1.0), |
| "vmov.f64 d7, #1\n"); |
| COMPARE_BOTH(Vmov(d8, RawbitsToDouble(0x000000000000af8e)), |
| "mov ip, #44942\n" |
| "vdup.32 d8, ip\n" |
| "mov ip, #0\n" |
| "vmov.32 d8[1], ip\n"); |
| COMPARE_BOTH(Vmov(d9, RawbitsToDouble(0x000070210000af8e)), |
| "mov ip, #44942\n" |
| "vdup.32 d9, ip\n" |
| "mov ip, #28705\n" |
| "vmov.32 d9[1], ip\n"); |
| COMPARE_BOTH(Vmov(d10, RawbitsToDouble(0x7021000000000000)), |
| "mov ip, #0\n" |
| "vdup.32 d10, ip\n" |
| "mov ip, #0\n" |
| "movt ip, #28705\n" |
| "vmov.32 d10[1], ip\n"); |
| COMPARE_BOTH(Vmov(d11, RawbitsToDouble(0x7021da4b0000af8e)), |
| "mov ip, #44942\n" |
| "vdup.32 d11, ip\n" |
| "mov ip, #55883\n" |
| "movt ip, #28705\n" |
| "vmov.32 d11[1], ip\n"); |
| COMPARE_BOTH(Vmov(d12, RawbitsToDouble(0x0cff553204ec4a3f)), |
| "mov ip, #19007\n" |
| "movt ip, #1260\n" |
| "vdup.32 d12, ip\n" |
| "mov ip, #21810\n" |
| "movt ip, #3327\n" |
| "vmov.32 d12[1], ip\n"); |
| COMPARE_BOTH(Vmov(d13, RawbitsToDouble(0xa2037ad20000f592)), |
| "mov ip, #62866\n" |
| "vdup.32 d13, ip\n" |
| "mov ip, #31442\n" |
| "movt ip, #41475\n" |
| "vmov.32 d13[1], ip\n"); |
| COMPARE_BOTH(Vmov(d14, RawbitsToDouble(0xe62556c325a59470)), |
| "mov ip, #38000\n" |
| "movt ip, #9637\n" |
| "vdup.32 d14, ip\n" |
| "mov ip, #22211\n" |
| "movt ip, #58917\n" |
| "vmov.32 d14[1], ip\n"); |
| CLEANUP(); |
| } |
| |
| TEST(macro_assembler_PushRegisterList) { |
| SETUP(); |
| |
| // Allow the test to use all registers. |
| UseScratchRegisterScope temps(&masm); |
| temps.ExcludeAll(); |
| |
| COMPARE_BOTH(Push(RegisterList(0x1111)), |
| "push {r0,r4,r8,ip}\n"); |
| |
| COMPARE_BOTH(Push(RegisterList(0x1fff)), |
| "push {r0,r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11,ip}\n"); |
| |
| COMPARE_BOTH(Push(RegisterList(0x5fff)), |
| "push {r0,r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11,ip,lr}\n"); |
| |
| COMPARE_A32(Push(ne, RegisterList(0x1fff)), |
| "pushne {r0,r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11,ip}\n"); |
| |
| COMPARE_T32(Push(ne, RegisterList(0x1fff)), |
| "beq 0x00000006\n" |
| "push {r0,r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11,ip}\n"); |
| |
| COMPARE_A32(Push(RegisterList(sp)), "stmdb sp!, {sp}\n"); |
| |
| // TODO: Clarify behaviour of MacroAssembler vs Assembler with respect to |
| // deprecated and unpredictable instructions. The tests reflect the |
| // current behaviour and will need to be updated. |
| |
| // Deprecated, but accepted: |
| SHOULD_FAIL_TEST_A32(Push(RegisterList(pc))); |
| // Whereas we don't accept the single-register version: |
| MUST_FAIL_TEST_A32(Push(pc), "Unpredictable instruction.\n"); |
| |
| // For T32, pushing the PC is allowed: |
| COMPARE_T32(Push(pc), "push {pc}\n"); |
| |
| // Accepted, but stores UNKNOWN value for the SP: |
| SHOULD_FAIL_TEST_A32(Push(RegisterList(r0, sp))); |
| |
| // The following use the T1 and A1 encodings for T32 and A32 respectively, and |
| // hence have different preferred disassembly. |
| COMPARE_T32(Push(RegisterList(r0)), "push {r0}\n"); |
| COMPARE_A32(Push(RegisterList(r0)), "stmdb sp!, {r0}\n"); |
| COMPARE_T32(Push(RegisterList(r7)), "push {r7}\n"); |
| COMPARE_A32(Push(RegisterList(r7)), "stmdb sp!, {r7}\n"); |
| COMPARE_T32(Push(RegisterList(lr)), "push {lr}\n"); |
| COMPARE_A32(Push(RegisterList(lr)), "stmdb sp!, {lr}\n"); |
| |
| // T2 and A1 encodings, with the same preferred disassembly: |
| COMPARE_BOTH(Push(RegisterList(r8)), "stmdb sp!, {r8}\n"); |
| |
| // Cannot push the sp and pc in T32 when using a register list. |
| MUST_FAIL_TEST_T32(Push(RegisterList(sp)), "Ill-formed 'push' instruction.\n"); |
| MUST_FAIL_TEST_T32(Push(RegisterList(pc)), "Ill-formed 'push' instruction.\n"); |
| |
| CLEANUP(); |
| } |
| |
| TEST(macro_assembler_PopRegisterList) { |
| SETUP(); |
| |
| // Allow the test to use all registers. |
| UseScratchRegisterScope temps(&masm); |
| temps.ExcludeAll(); |
| |
| COMPARE_BOTH(Pop(RegisterList(0x1111)), |
| "pop {r0,r4,r8,ip}\n"); |
| |
| COMPARE_BOTH(Pop(RegisterList(0x1fff)), |
| "pop {r0,r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11,ip}\n"); |
| |
| COMPARE_BOTH(Pop(RegisterList(0x5fff)), |
| "pop {r0,r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11,ip,lr}\n"); |
| |
| COMPARE_A32(Pop(ne, RegisterList(0x1fff)), |
| "popne {r0,r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11,ip}\n"); |
| |
| COMPARE_T32(Pop(ne, RegisterList(0x1fff)), |
| "beq 0x00000006\n" |
| "pop {r0,r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11,ip}\n"); |
| |
| // TODO: Accepted, but value of SP after the instruction is UNKNOWN: |
| SHOULD_FAIL_TEST_A32(Pop(RegisterList(sp))); |
| |
| // Cannot pop the sp in T32 when using a register list. |
| MUST_FAIL_TEST_T32(Pop(RegisterList(sp)), "Ill-formed 'pop' instruction.\n"); |
| |
| // The following use the T1 and A1 encodings for T32 and A32 respectively, and |
| // hence have different preferred disassembly. |
| COMPARE_T32(Pop(RegisterList(pc)), "pop {pc}\n"); |
| COMPARE_A32(Pop(RegisterList(pc)), "ldm sp!, {pc}\n"); |
| COMPARE_T32(Pop(RegisterList(r0)), "pop {r0}\n"); |
| COMPARE_A32(Pop(RegisterList(r0)), "ldm sp!, {r0}\n"); |
| COMPARE_T32(Pop(RegisterList(r7)), "pop {r7}\n"); |
| COMPARE_A32(Pop(RegisterList(r7)), "ldm sp!, {r7}\n"); |
| |
| // T2 and A1 encodings, with the same preferred disassembly: |
| COMPARE_BOTH(Pop(RegisterList(r8)), "ldm sp!, {r8}\n"); |
| COMPARE_BOTH(Pop(RegisterList(lr)), "ldm sp!, {lr}\n"); |
| |
| // TODO: Pushing both the lr and pc should not be allowed by the |
| // MacroAssembler (deprecated for A32, for T32 they shouldn't both |
| // be in the list). |
| SHOULD_FAIL_TEST_BOTH(Pop(RegisterList(lr, pc))); |
| |
| CLEANUP(); |
| } |
| |
| |
| TEST(macro_assembler_unpredictable) { |
| SETUP(); |
| |
| // ADC, ADCS (immediate). |
| COMPARE_A32(Adc(pc, r0, 1), "adc pc, r0, #1\n"); |
| COMPARE_A32(Adc(r0, pc, 1), "adc r0, pc, #1\n"); |
| MUST_FAIL_TEST_T32(Adc(pc, r0, 1), |
| "Unpredictable instruction.\n"); |
| MUST_FAIL_TEST_T32(Adc(r0, pc, 1), |
| "Unpredictable instruction.\n"); |
| COMPARE_A32(Adcs(pc, r0, 1), "adcs pc, r0, #1\n"); |
| COMPARE_A32(Adcs(r0, pc, 1), "adcs r0, pc, #1\n"); |
| MUST_FAIL_TEST_T32(Adcs(pc, r0, 1), |
| "Unpredictable instruction.\n"); |
| MUST_FAIL_TEST_T32(Adcs(r0, pc, 1), |
| "Unpredictable instruction.\n"); |
| |
| // ADC, ADCS (register). |
| COMPARE_A32(Adc(pc, r0, r1), "adc pc, r0, r1\n"); |
| COMPARE_A32(Adc(r0, pc, r1), "adc r0, pc, r1\n"); |
| COMPARE_A32(Adc(r0, r1, pc), "adc r0, r1, pc\n"); |
| MUST_FAIL_TEST_T32(Adc(pc, r0, r1), |
| "Unpredictable instruction.\n"); |
| MUST_FAIL_TEST_T32(Adc(r0, pc, r1), |
| "Unpredictable instruction.\n"); |
| MUST_FAIL_TEST_T32(Adc(r0, r1, pc), |
| "Unpredictable instruction.\n"); |
| COMPARE_A32(Adcs(pc, r0, r1), "adcs pc, r0, r1\n"); |
| COMPARE_A32(Adcs(r0, pc, r1), "adcs r0, pc, r1\n"); |
| COMPARE_A32(Adcs(r0, r1, pc), "adcs r0, r1, pc\n"); |
| MUST_FAIL_TEST_T32(Adcs(pc, r0, r1), |
| "Unpredictable instruction.\n"); |
| MUST_FAIL_TEST_T32(Adcs(r0, pc, r1), |
| "Unpredictable instruction.\n"); |
| MUST_FAIL_TEST_T32(Adcs(r0, r1, pc), |
| "Unpredictable instruction.\n"); |
| |
| // ADC, ADCS (register-shifted register). |
| MUST_FAIL_TEST_A32(Adc(pc, r0, Operand(r1, LSL, r2)), |
| "Unpredictable instruction.\n"); |
| MUST_FAIL_TEST_A32(Adc(r0, pc, Operand(r1, LSL, r2)), |
| "Unpredictable instruction.\n"); |
| MUST_FAIL_TEST_A32(Adc(r0, r1, Operand(pc, LSL, r2)), |
| "Unpredictable instruction.\n"); |
| MUST_FAIL_TEST_A32(Adc(r0, r1, Operand(r2, LSL, pc)), |
| "Unpredictable instruction.\n"); |
| MUST_FAIL_TEST_A32(Adcs(pc, r0, Operand(r1, LSL, r2)), |
| "Unpredictable instruction.\n"); |
| MUST_FAIL_TEST_A32(Adcs(r0, pc, Operand(r1, LSL, r2)), |
| "Unpredictable instruction.\n"); |
| MUST_FAIL_TEST_A32(Adcs(r0, r1, Operand(pc, LSL, r2)), |
| "Unpredictable instruction.\n"); |
| MUST_FAIL_TEST_A32(Adcs(r0, r1, Operand(r2, LSL, pc)), |
| "Unpredictable instruction.\n"); |
| |
| // ADD (immediate, to PC). |
| COMPARE_A32(Add(r0, pc, 1), "adr r0, 0x00000009\n"); |
| COMPARE_T32(Add(r0, pc, 1), "adr r0, 0x00000005\n"); |
| COMPARE_A32(Add(pc, pc, 1), "adr pc, 0x00000009\n"); |
| MUST_FAIL_TEST_T32(Add(pc, pc, 1), |
| "Unpredictable instruction.\n"); |
| |
| // ADD, ADDS (immediate). |
| COMPARE_A32(Add(pc, r0, 1), "add pc, r0, #1\n"); |
| MUST_FAIL_TEST_T32(Add(pc, r0, 1), |
| "Unpredictable instruction.\n"); |
| MUST_FAIL_TEST_T32(Add(pc, r0, 0x123), |
| "Unpredictable instruction.\n"); |
| COMPARE_A32(Adds(pc, r0, 1), "adds pc, r0, #1\n"); |
| COMPARE_A32(Adds(r0, pc, 1), "adds r0, pc, #1\n"); |
| // TODO: Try to make these error messages more consistent. |
| MUST_FAIL_TEST_T32(Adds(r0, pc, 1), |
| "Unpredictable instruction.\n"); |
| MUST_FAIL_TEST_T32(Adds(r0, pc, 0x123), |
| "Ill-formed 'adds' instruction.\n"); |
| |
| // ADD, ADDS (register). |
| COMPARE_A32(Add(pc, r0, r1), "add pc, r0, r1\n"); |
| COMPARE_A32(Add(r0, pc, r1), "add r0, pc, r1\n"); |
| COMPARE_A32(Add(r0, r1, pc), "add r0, r1, pc\n"); |
| COMPARE_T32(Add(r0, r0, pc), "add r0, pc\n"); |
| COMPARE_T32(Add(pc, pc, r0), "add pc, r0\n"); |
| MUST_FAIL_TEST_T32(Add(pc, pc, pc), |
| "Unpredictable instruction.\n"); |
| MUST_FAIL_TEST_T32(Add(pc, r0, r1), |
| "Unpredictable instruction.\n"); |
| MUST_FAIL_TEST_T32(Add(r0, pc, r1), |
| "Unpredictable instruction.\n"); |
| MUST_FAIL_TEST_T32(Add(r0, r1, pc), |
| "Unpredictable instruction.\n"); |
| COMPARE_A32(Adds(pc, r0, r1), "adds pc, r0, r1\n"); |
| COMPARE_A32(Adds(r0, pc, r1), "adds r0, pc, r1\n"); |
| COMPARE_A32(Adds(r0, r1, pc), "adds r0, r1, pc\n"); |
| MUST_FAIL_TEST_T32(Adds(r0, pc, r1), |
| "Unpredictable instruction.\n"); |
| MUST_FAIL_TEST_T32(Adds(r0, r1, pc), |
| "Unpredictable instruction.\n"); |
| |
| // ADD, ADDS (register-shifted register) |
| MUST_FAIL_TEST_A32(Add(pc, r0, Operand(r1, LSL, r2)), |
| "Unpredictable instruction.\n"); |
| MUST_FAIL_TEST_A32(Add(r0, pc, Operand(r1, LSL, r2)), |
| "Unpredictable instruction.\n"); |
| MUST_FAIL_TEST_A32(Add(r0, r1, Operand(pc, LSL, r2)), |
| "Unpredictable instruction.\n"); |
| MUST_FAIL_TEST_A32(Add(r0, r1, Operand(r2, LSL, pc)), |
| "Unpredictable instruction.\n"); |
| COMPARE_A32(Add(pc, sp, 1), "add pc, sp, #1\n"); |
| MUST_FAIL_TEST_T32(Add(pc, sp, 1), |
| "Unpredictable instruction.\n"); |
| MUST_FAIL_TEST_A32(Adds(pc, r0, Operand(r1, LSL, r2)), |
| "Unpredictable instruction.\n"); |
| MUST_FAIL_TEST_A32(Adds(r0, pc, Operand(r1, LSL, r2)), |
| "Unpredictable instruction.\n"); |
| MUST_FAIL_TEST_A32(Adds(r0, r1, Operand(pc, LSL, r2)), |
| "Unpredictable instruction.\n"); |
| MUST_FAIL_TEST_A32(Adds(r0, r1, Operand(r2, LSL, pc)), |
| "Unpredictable instruction.\n"); |
| |
| // ADD, ADDS (SP plus immediate). |
| COMPARE_A32(Add(pc, sp, 1), "add pc, sp, #1\n"); |
| MUST_FAIL_TEST_T32(Add(pc, sp, 1), |
| "Unpredictable instruction.\n"); |
| COMPARE_A32(Adds(pc, sp, 1), "adds pc, sp, #1\n"); |
| MUST_FAIL_TEST_T32(Adds(pc, sp, 1), |
| "Ill-formed 'adds' instruction.\n"); |
| |
| // ADD, ADDS (SP plus register). |
| COMPARE_A32(Add(pc, sp, r0), "add pc, sp, r0\n"); |
| MUST_FAIL_TEST_T32(Add(pc, sp, r0), |
| "Unpredictable instruction.\n"); |
| COMPARE_A32(Add(r0, sp, pc), "add r0, sp, pc\n"); |
| MUST_FAIL_TEST_T32(Add(r0, sp, pc), |
| "Unpredictable instruction.\n"); |
| COMPARE_BOTH(Add(pc, sp, pc), "add pc, sp, pc\n"); |
| COMPARE_BOTH(Add(sp, sp, pc), "add sp, pc\n"); |
| COMPARE_A32(Adds(pc, sp, r0), "adds pc, sp, r0\n"); |
| MUST_FAIL_TEST_T32(Adds(pc, sp, r0), |
| "Ill-formed 'adds' instruction.\n"); |
| COMPARE_A32(Adds(r0, sp, pc), "adds r0, sp, pc\n"); |
| MUST_FAIL_TEST_T32(Adds(r0, sp, pc), |
| "Unpredictable instruction.\n"); |
| COMPARE_A32(Adds(pc, sp, pc), "adds pc, sp, pc\n"); |
| MUST_FAIL_TEST_T32(Adds(pc, sp, pc), |
| "Ill-formed 'adds' instruction.\n"); |
| COMPARE_A32(Adds(sp, sp, pc), "adds sp, pc\n"); |
| MUST_FAIL_TEST_T32(Adds(sp, sp, pc), |
| "Unpredictable instruction.\n"); |
| |
| // ADR. |
| { |
| Label label; |
| masm.Bind(&label); |
| COMPARE_A32(Adr(pc, &label), "adr pc, 0x00000000\n"); |
| MUST_FAIL_TEST_T32(Adr(pc, &label), |
| "Unpredictable instruction.\n"); |
| } |
| |
| // CLZ. |
| MUST_FAIL_TEST_BOTH(Clz(pc, r0), "Unpredictable instruction.\n"); |
| MUST_FAIL_TEST_BOTH(Clz(r0, pc), "Unpredictable instruction.\n"); |
| |
| // MOV, MOVS (immediate). |
| COMPARE_A32(Mov(pc, 1), "mov pc, #1\n"); |
| MUST_FAIL_TEST_T32(Mov(pc, 1), "Unpredictable instruction.\n"); |
| MUST_FAIL_TEST_T32(Mov(pc, 0xfff), "Unpredictable instruction.\n"); |
| COMPARE_A32(Mov(pc, 0xf000), "mov pc, #61440\n"); |
| MUST_FAIL_TEST_T32(Mov(pc, 0xf000), "Unpredictable instruction.\n"); |
| COMPARE_A32(Movs(pc, 1), "movs pc, #1\n"); |
| MUST_FAIL_TEST_T32(Movs(pc, 1), "Unpredictable instruction.\n"); |
| MUST_FAIL_TEST_BOTH(Movs(pc, 0xfff), |
| "Ill-formed 'movs' instruction.\n"); |
| COMPARE_A32(Movs(pc, 0xf000), "movs pc, #61440\n"); |
| MUST_FAIL_TEST_T32(Movs(pc, 0xf000), "Unpredictable instruction.\n"); |
| |
| // MOV, MOVS (register). |
| COMPARE_BOTH(Mov(pc, r0), "mov pc, r0\n"); |
| COMPARE_BOTH(Mov(r0, pc), "mov r0, pc\n"); |
| MUST_FAIL_TEST_BOTH(Movs(pc, r0), "Unpredictable instruction.\n"); |
| COMPARE_A32(Movs(r0, pc), "movs r0, pc\n"); |
| MUST_FAIL_TEST_T32(Movs(r0, pc), "Unpredictable instruction.\n"); |
| |
| // MOV, MOVS (register-shifted register). |
| MUST_FAIL_TEST_BOTH(Mov(pc, Operand(r0, ASR, r1)), |
| "Unpredictable instruction.\n"); |
| MUST_FAIL_TEST_BOTH(Mov(r0, Operand(pc, ASR, r1)), |
| "Unpredictable instruction.\n"); |
| MUST_FAIL_TEST_BOTH(Mov(r0, Operand(r1, ASR, pc)), |
| "Unpredictable instruction.\n"); |
| MUST_FAIL_TEST_BOTH(Movs(pc, Operand(r0, ASR, r1)), |
| "Unpredictable instruction.\n"); |
| MUST_FAIL_TEST_BOTH(Movs(r0, Operand(pc, ASR, r1)), |
| "Unpredictable instruction.\n"); |
| MUST_FAIL_TEST_BOTH(Movs(r0, Operand(r1, ASR, pc)), |
| "Unpredictable instruction.\n"); |
| |
| CLEANUP(); |
| } |
| |
| |
| TEST(macro_assembler_pc_rel_A32) { |
| SETUP(); |
| // Simple cases alias adr. |
| COMPARE_A32(Add(r0, pc, -8), "adr r0, 0x00000000\n"); |
| COMPARE_A32(Add(r0, pc, 255), "adr r0, 0x00000107\n"); |
| COMPARE_A32(Add(r0, pc, 256), "adr r0, 0x00000108\n"); |
| COMPARE_A32(Add(r0, pc, 1024), "adr r0, 0x00000408\n"); |
| COMPARE_A32(Add(r0, pc, -9), "adr r0, 0xffffffff\n"); |
| COMPARE_A32(Add(r0, pc, -1024), "adr r0, 0xfffffc08\n"); |
| COMPARE_A32(Add(r0, pc, UINT32_C(0x80000000)), "adr r0, 0x80000008\n"); |
| COMPARE_A32(Add(r0, pc, -0x7fffffff), "adr r0, 0x80000009\n"); |
| |
| COMPARE_A32(Sub(r0, pc, 8), "adr r0, 0x00000000\n"); |
| COMPARE_A32(Sub(r0, pc, -255), "adr r0, 0x00000107\n"); |
| COMPARE_A32(Sub(r0, pc, -256), "adr r0, 0x00000108\n"); |
| COMPARE_A32(Sub(r0, pc, -1024), "adr r0, 0x00000408\n"); |
| COMPARE_A32(Sub(r0, pc, 9), "adr r0, 0xffffffff\n"); |
| COMPARE_A32(Sub(r0, pc, 1024), "adr r0, 0xfffffc08\n"); |
| COMPARE_A32(Sub(r0, pc, UINT32_C(0x80000000)), "adr r0, 0x80000008\n"); |
| COMPARE_A32(Sub(r0, pc, -0x7fffffff), "adr r0, 0x80000007\n"); |
| |
| // Cases out of range. |
| // Only negative offsets are supported, because the proper behaviour for |
| // positive offsets is not clear. |
| MUST_FAIL_TEST_A32(Add(r0, pc, 1025), "Ill-formed 'add' instruction.\n"); |
| MUST_FAIL_TEST_A32(Add(r0, pc, 0xffff), "Ill-formed 'add' instruction.\n"); |
| MUST_FAIL_TEST_A32(Add(r0, pc, 0x10001), "Ill-formed 'add' instruction.\n"); |
| MUST_FAIL_TEST_A32(Add(r0, pc, 0x12345678), "Ill-formed 'add' instruction.\n"); |
| MUST_FAIL_TEST_A32(Add(r0, pc, 0x7fffffff), "Ill-formed 'add' instruction.\n"); |
| COMPARE_A32(Add(r0, pc, -1025), "adr r0, 0x00000007\n" |
| "sub r0, #1024\n"); |
| COMPARE_A32(Add(r0, pc, -0xffff), "adr r0, 0xffffff09\n" |
| "sub r0, #65280\n"); |
| COMPARE_A32(Add(r0, pc, -0x10001), "adr r0, 0x00000007\n" |
| "sub r0, #65536\n"); |
| COMPARE_A32(Add(r0, pc, -0x2345678), "adr r0, 0xfffffd90\n" |
| "sub r0, #21504\n" |
| "sub r0, #36962304\n"); |
| COMPARE_A32(Add(r0, pc, -0x12345678), "adr r0, 0xfffffd90\n" |
| "mov ip, #21504\n" |
| "movt ip, #4660\n" |
| "sub r0, ip\n"); |
| |
| MUST_FAIL_TEST_A32(Sub(r0, pc, -1025), "Ill-formed 'add' instruction.\n"); |
| MUST_FAIL_TEST_A32(Sub(r0, pc, -0xffff), "Ill-formed 'add' instruction.\n"); |
| MUST_FAIL_TEST_A32(Sub(r0, pc, -0x10001), "Ill-formed 'add' instruction.\n"); |
| MUST_FAIL_TEST_A32(Sub(r0, pc, -0x12345678), "Ill-formed 'add' instruction.\n"); |
| COMPARE_A32(Sub(r0, pc, 1025), "adr r0, 0x00000007\n" |
| "sub r0, #1024\n"); |
| COMPARE_A32(Sub(r0, pc, 0xffff), "adr r0, 0xffffff09\n" |
| "sub r0, #65280\n"); |
| COMPARE_A32(Sub(r0, pc, 0x10001), "adr r0, 0x00000007\n" |
| "sub r0, #65536\n"); |
| COMPARE_A32(Sub(r0, pc, 0x2345678), "adr r0, 0xfffffd90\n" |
| "sub r0, #21504\n" |
| "sub r0, #36962304\n"); |
| COMPARE_A32(Sub(r0, pc, 0x12345678), "adr r0, 0xfffffd90\n" |
| "mov ip, #21504\n" |
| "movt ip, #4660\n" |
| "sub r0, ip\n"); |
| COMPARE_A32(Sub(r0, pc, 0x7fffffff), "adr r0, 0xffffff09\n" |
| "add r0, #256\n" |
| "add r0, #2147483648\n"); |
| |
| CLEANUP(); |
| } |
| |
| |
| TEST(macro_assembler_pc_rel_T32) { |
| SETUP(); |
| // Simple cases alias adr. |
| COMPARE_T32(Add(r0, pc, -4), "adr r0, 0x00000000\n"); // T1 |
| COMPARE_T32(Add(r0, pc, 1020), "adr r0, 0x00000400\n"); // T1 |
| COMPARE_T32(Add(r0, pc, -5), "adr r0, 0xffffffff\n"); // T2 |
| COMPARE_T32(Add(r0, pc, -4095), "adr r0, 0xfffff005\n"); // T2 |
| COMPARE_T32(Add(r0, pc, -3), "adr r0, 0x00000001\n"); // T3 |
| COMPARE_T32(Add(r0, pc, 1021), "adr r0, 0x00000401\n"); // T3 |
| COMPARE_T32(Add(r0, pc, 1019), "adr r0, 0x000003ff\n"); // T3 |
| COMPARE_T32(Add(r0, pc, 4095), "adr r0, 0x00001003\n"); // T3 |
| |
| COMPARE_T32(Sub(r0, pc, 4), "adr r0, 0x00000000\n"); // T1 |
| COMPARE_T32(Sub(r0, pc, -1020), "adr r0, 0x00000400\n"); // T1 |
| COMPARE_T32(Sub(r0, pc, 5), "adr r0, 0xffffffff\n"); // T2 |
| COMPARE_T32(Sub(r0, pc, 4095), "adr r0, 0xfffff005\n"); // T2 |
| COMPARE_T32(Sub(r0, pc, 3), "adr r0, 0x00000001\n"); // T3 |
| COMPARE_T32(Sub(r0, pc, -1021), "adr r0, 0x00000401\n"); // T3 |
| COMPARE_T32(Sub(r0, pc, -1019), "adr r0, 0x000003ff\n"); // T3 |
| COMPARE_T32(Sub(r0, pc, -4095), "adr r0, 0x00001003\n"); // T3 |
| |
| // Cases out of range. |
| // Only negative offsets are supported, because the proper behaviour for |
| // positive offsets is not clear. |
| |
| MUST_FAIL_TEST_T32(Add(r0, pc, 4096), "Unpredictable instruction.\n"); |
| |
| // TODO: This case is incorrect; the instruction is unpredictable. The test |
| // must be updated once the bug is fixed. |
| COMPARE_T32(Add(r0, pc, -4096), "sub r0, pc, #4096\n"); |
| |
| MUST_FAIL_TEST_T32(Add(r0, pc, 0xffff), "Ill-formed 'add' instruction.\n"); |
| MUST_FAIL_TEST_T32(Add(r0, pc, 0x10002), "Ill-formed 'add' instruction.\n"); |
| MUST_FAIL_TEST_T32(Add(r0, pc, 0x12345678), "Ill-formed 'add' instruction.\n"); |
| MUST_FAIL_TEST_T32(Add(r0, pc, 0x7fffffff), "Ill-formed 'add' instruction.\n"); |
| COMPARE_T32(Add(r0, pc, -0x12345678), "mov r0, pc\n" |
| "mov ip, #22136\n" |
| "movt ip, #4660\n" |
| "sub r0, ip\n"); |
| COMPARE_T32(Add(r0, pc, -0x7fffffff), "mov r0, pc\n" |
| "add r0, #1\n" |
| "add r0, #2147483648\n"); |
| |
| // TODO: This test aborts in the Assembler (with unpredictable instruction |
| // errors) before the MacroAssembler gets a chance to do something |
| // predictable. |
| // COMPARE_T32(Sub(r0, pc, -4096), "mov r0, pc\n" |
| // "add r0, #4096\n"); |
| |
| // TODO: This case is incorrect; the instruction is unpredictable. The test |
| // must be updated once the bug is fixed. |
| COMPARE_T32(Sub(r0, pc, 4096), "sub r0, pc, #4096\n"); |
| |
| MUST_FAIL_TEST_T32(Sub(r0, pc, -0xffff), "Ill-formed 'add' instruction.\n"); |
| MUST_FAIL_TEST_T32(Sub(r0, pc, -0x10002), "Ill-formed 'add' instruction.\n"); |
| MUST_FAIL_TEST_T32(Sub(r0, pc, -0x12345678), "Ill-formed 'add' instruction.\n"); |
| MUST_FAIL_TEST_T32(Sub(r0, pc, -0x7fffffff), "Ill-formed 'add' instruction.\n"); |
| COMPARE_T32(Sub(r0, pc, 0x12345678), "mov r0, pc\n" |
| "mov ip, #22136\n" |
| "movt ip, #4660\n" |
| "sub r0, ip\n"); |
| COMPARE_T32(Sub(r0, pc, 0x7fffffff), "mov r0, pc\n" |
| "add r0, #1\n" |
| "add r0, #2147483648\n"); |
| CLEANUP(); |
| } |
| |
| |
| TEST(macro_assembler_unsupported) { |
| SETUP(); |
| |
| MUST_FAIL_TEST_BOTH(Sxtab(r0, r1, Operand(r2, ROR, 1)), |
| "Ill-formed 'sxtab' instruction.\n"); |
| MUST_FAIL_TEST_BOTH(Sxtab16(r0, r1, Operand(r0, ASR, 2)), |
| "Ill-formed 'sxtab16' instruction.\n"); |
| MUST_FAIL_TEST_BOTH(Sxtah(r0, r1, Operand(r0, LSL, r1)), |
| "Ill-formed 'sxtah' instruction.\n"); |
| MUST_FAIL_TEST_BOTH(Uxtab(r0, r1, Operand(r0, LSR, r2)), |
| "Ill-formed 'uxtab' instruction.\n"); |
| MUST_FAIL_TEST_BOTH(Uxtab16(r0, r1, Operand(r0, ROR, 1)), |
| "Ill-formed 'uxtab16' instruction.\n"); |
| MUST_FAIL_TEST_BOTH(Uxtah(r0, r1, Operand(r0, ASR, 2)), |
| "Ill-formed 'uxtah' instruction.\n"); |
| MUST_FAIL_TEST_BOTH(Pkhbt(r0, r1, Operand(r0, LSL, r1)), |
| "Ill-formed 'pkhbt' instruction.\n"); |
| MUST_FAIL_TEST_BOTH(Pkhtb(r0, r1, Operand(r0, LSR, r2)), |
| "Ill-formed 'pkhtb' instruction.\n"); |
| |
| MUST_FAIL_TEST_BOTH(Pld(MemOperand(r0, 1, PreIndex)), |
| "Ill-formed 'pld' instruction.\n"); |
| MUST_FAIL_TEST_BOTH(Pldw(MemOperand(r0, 1, PostIndex)), |
| "Ill-formed 'pldw' instruction.\n"); |
| MUST_FAIL_TEST_BOTH(Pli(MemOperand(r0, 1, PreIndex)), |
| "Ill-formed 'pli' instruction.\n"); |
| |
| MUST_FAIL_TEST_BOTH(Pld(MemOperand(r0, r0, PreIndex)), |
| "Ill-formed 'pld' instruction.\n"); |
| MUST_FAIL_TEST_BOTH(Pldw(MemOperand(r0, r1, PostIndex)), |
| "Ill-formed 'pldw' instruction.\n"); |
| MUST_FAIL_TEST_BOTH(Pli(MemOperand(r0, r2, PreIndex)), |
| "Ill-formed 'pli' instruction.\n"); |
| |
| MUST_FAIL_TEST_BOTH(Pld(MemOperand(r0, r0, LSL, 1, PreIndex)), |
| "Ill-formed 'pld' instruction.\n"); |
| MUST_FAIL_TEST_BOTH(Pldw(MemOperand(r0, r1, LSR, 2, PostIndex)), |
| "Ill-formed 'pldw' instruction.\n"); |
| MUST_FAIL_TEST_BOTH(Pli(MemOperand(r0, r2, ASR, 3, PreIndex)), |
| "Ill-formed 'pli' instruction.\n"); |
| |
| MUST_FAIL_TEST_BOTH(Lda(r0, MemOperand(r0, 1)), |
| "Ill-formed 'lda' instruction.\n"); |
| MUST_FAIL_TEST_BOTH(Ldab(r0, MemOperand(r0, 1)), |
| "Ill-formed 'ldab' instruction.\n"); |
| MUST_FAIL_TEST_BOTH(Ldaex(r0, MemOperand(r0, 1)), |
| "Ill-formed 'ldaex' instruction.\n"); |
| MUST_FAIL_TEST_BOTH(Ldaexb(r0, MemOperand(r0, 1)), |
| "Ill-formed 'ldaexb' instruction.\n"); |
| MUST_FAIL_TEST_BOTH(Ldaexh(r0, MemOperand(r0, 1)), |
| "Ill-formed 'ldaexh' instruction.\n"); |
| MUST_FAIL_TEST_BOTH(Ldah(r0, MemOperand(r0, 1)), |
| "Ill-formed 'ldah' instruction.\n"); |
| MUST_FAIL_TEST_BOTH(Ldrex(r0, MemOperand(r0, 1)), |
| "Ill-formed 'ldrex' instruction.\n"); |
| MUST_FAIL_TEST_BOTH(Ldrexb(r0, MemOperand(r0, 1)), |
| "Ill-formed 'ldrexb' instruction.\n"); |
| MUST_FAIL_TEST_BOTH(Ldrexh(r0, MemOperand(r0, 1)), |
| "Ill-formed 'ldrexh' instruction.\n"); |
| MUST_FAIL_TEST_BOTH(Stl(r0, MemOperand(r0, 1)), |
| "Ill-formed 'stl' instruction.\n"); |
| MUST_FAIL_TEST_BOTH(Stlb(r0, MemOperand(r0, 1)), |
| "Ill-formed 'stlb' instruction.\n"); |
| MUST_FAIL_TEST_BOTH(Stlh(r0, MemOperand(r0, 1)), |
| "Ill-formed 'stlh' instruction.\n"); |
| |
| MUST_FAIL_TEST_BOTH(Ldaexd(r0, r1, MemOperand(r0, 1)), |
| "Ill-formed 'ldaexd' instruction.\n"); |
| MUST_FAIL_TEST_BOTH(Ldrexd(r0, r1, MemOperand(r0, 1)), |
| "Ill-formed 'ldrexd' instruction.\n"); |
| MUST_FAIL_TEST_BOTH(Stlex(r0, r1, MemOperand(r0, 1)), |
| "Ill-formed 'stlex' instruction.\n"); |
| MUST_FAIL_TEST_BOTH(Stlexb(r0, r1, MemOperand(r0, 1)), |
| "Ill-formed 'stlexb' instruction.\n"); |
| MUST_FAIL_TEST_BOTH(Stlexh(r0, r1, MemOperand(r0, 1)), |
| "Ill-formed 'stlexh' instruction.\n"); |
| MUST_FAIL_TEST_BOTH(Strex(r0, r1, MemOperand(r0, 1)), |
| "Ill-formed 'strex' instruction.\n"); |
| MUST_FAIL_TEST_BOTH(Strexb(r0, r1, MemOperand(r0, 1)), |
| "Ill-formed 'strexb' instruction.\n"); |
| MUST_FAIL_TEST_BOTH(Strexh(r0, r1, MemOperand(r0, 1)), |
| "Ill-formed 'strexh' instruction.\n"); |
| |
| MUST_FAIL_TEST_BOTH(Stlexd(r0, r1, r2, MemOperand(r0, 1)), |
| "Ill-formed 'stlexd' instruction.\n"); |
| MUST_FAIL_TEST_BOTH(Strexd(r0, r1, r2, MemOperand(r0, 1)), |
| "Ill-formed 'strexd' instruction.\n"); |
| |
| CLEANUP(); |
| } |
| |
| TEST(macro_assembler_Vmov_neon_immediate) { |
| SETUP(); |
| |
| // Move 8, 16 and 32-bit immediates into D registers, duplicated across the |
| // destination. |
| COMPARE_BOTH(Vmov(I8, d0, 0xac), |
| "vmov.i8 d0, #172\n"); |
| |
| COMPARE_BOTH(Vmov(I16, d0, 0xa4), |
| "vmov.i16 d0, #164\n"); |
| COMPARE_BOTH(Vmov(I16, d0, 0x9797), |
| "vmov.i8 d0, #151\n"); |
| COMPARE_BOTH(Vmov(I16, d0, 0x9ef6), |
| "mov ip, #40694\n" |
| "vdup.16 d0, ip\n"); |
| |
| COMPARE_BOTH(Vmov(I32, d0, 0x6d0000), |
| "vmov.i32 d0, #7143424\n"); |
| COMPARE_BOTH(Vmov(I32, d0, 0x15ffffff), |
| "vmvn.i32 d0, #3925868544\n"); |
| COMPARE_BOTH(Vmov(I32, d0, 0x74747474), |
| "vmov.i8 d0, #116\n"); |
| COMPARE_BOTH(Vmov(I32, d0, 0xff0000ff), |
| "vmov.i64 d0, #0xff0000ffff0000ff\n"); |
| COMPARE_BOTH(Vmov(I32, d0, 0x1ecb9ef6), |
| "mov ip, #40694\n" |
| "movt ip, #7883\n" |
| "vdup.32 d0, ip\n"); |
| COMPARE_BOTH(Vmov(I32, d0, 0x006d0000), |
| "vmov.i32 d0, #7143424\n"); |
| COMPARE_BOTH(Vmov(I32, d0, 0x00004da6), |
| "mov ip, #19878\n" |
| "vdup.32 d0, ip\n"); |
| COMPARE_BOTH(Vmov(I32, d0, 0xffffff55), |
| "vmvn.i32 d0, #170\n"); |
| COMPARE_BOTH(Vmov(I32, d0, 0xffff55ff), |
| "vmvn.i32 d0, #43520\n"); |
| COMPARE_BOTH(Vmov(I32, d0, 0xff55ffff), |
| "vmvn.i32 d0, #11141120\n"); |
| |
| COMPARE_BOTH(Vmov(I64, d0, UINT64_C(0xa5a5a5a5a5a5a5a5)), |
| "vmov.i8 d0, #165\n"); |
| COMPARE_BOTH(Vmov(I64, d0, UINT64_C(0x0a01248315ffffff)), |
| "mvn ip, #3925868544\n" |
| "vdup.32 d0, ip\n" |
| "mov ip, #9347\n" |
| "movt ip, #2561\n" |
| "vmov.32 d0[1], ip\n"); |
| COMPARE_BOTH(Vmov(I64, d0, UINT64_C(0x6fe1a7a779e33af2)), |
| "mov ip, #15090\n" |
| "movt ip, #31203\n" |
| "vdup.32 d0, ip\n" |
| "mov ip, #42919\n" |
| "movt ip, #28641\n" |
| "vmov.32 d0[1], ip\n"); |
| COMPARE_BOTH(Vmov(I64, d0, UINT64_C(0x2efa8b440000c1da)), |
| "mov ip, #49626\n" |
| "vdup.32 d0, ip\n" |
| "mov ip, #35652\n" |
| "movt ip, #12026\n" |
| "vmov.32 d0[1], ip\n"); |
| COMPARE_BOTH(Vmov(I64, d0, UINT64_C(0x00008bb75c3036fd)), |
| "mov ip, #14077\n" |
| "movt ip, #23600\n" |
| "vdup.32 d0, ip\n" |
| "mov ip, #35767\n" |
| "vmov.32 d0[1], ip\n"); |
| |
| COMPARE_BOTH(Vmov(F32, d0, 0.5), |
| "vmov.f32 d0, #0.5\n"); |
| COMPARE_BOTH(Vmov(F32, d0, 1.1), |
| "mov ip, #52429\n" |
| "movt ip, #16268\n" |
| "vdup.32 d0, ip\n"); |
| |
| COMPARE_T32(Vmov(I64, d0, UINT64_C(0x2fff2fff3e2869e7)), |
| "mov ip, #27111\n" |
| "movt ip, #15912\n" |
| "vdup.32 d0, ip\n" |
| "mvn ip, #3489714176\n" |
| "vmov.32 d0[1], ip\n"); |
| |
| COMPARE_A32(Vmov(I32, d0, 0x0ffffffa), |
| "mvn ip, #4026531845\n" |
| "vdup.32 d0, ip\n"); |
| COMPARE_A32(Vmov(I64, d0, UINT64_C(0x65ffffff16a0ef46)), |
| "mov ip, #61254\n" |
| "movt ip, #5792\n" |
| "vdup.32 d0, ip\n" |
| "mvn ip, #2583691264\n" |
| "vmov.32 d0[1], ip\n"); |
| |
| // Move 8, 16 and 32-bit immediates into Q registers, duplicated across the |
| // destination. |
| COMPARE_BOTH(Vmov(I8, q0, 0xac), |
| "vmov.i8 q0, #172\n"); |
| |
| COMPARE_BOTH(Vmov(I16, q0, 0xa4), |
| "vmov.i16 q0, #164\n"); |
| COMPARE_BOTH(Vmov(I16, q0, 0x9797), |
| "vmov.i8 q0, #151\n"); |
| COMPARE_BOTH(Vmov(I16, q0, 0x9ef6), |
| "mov ip, #40694\n" |
| "vdup.16 q0, ip\n"); |
| |
| COMPARE_BOTH(Vmov(I32, q0, 0x6d0000), |
| "vmov.i32 q0, #7143424\n"); |
| COMPARE_BOTH(Vmov(I32, q0, 0x15ffffff), |
| "vmvn.i32 q0, #3925868544\n"); |
| COMPARE_BOTH(Vmov(I32, q0, 0x74747474), |
| "vmov.i8 q0, #116\n"); |
| COMPARE_BOTH(Vmov(I32, q0, 0xff0000ff), |
| "vmov.i64 q0, #0xff0000ffff0000ff\n"); |
| COMPARE_BOTH(Vmov(I32, q0, 0x1ecb9ef6), |
| "mov ip, #40694\n" |
| "movt ip, #7883\n" |
| "vdup.32 q0, ip\n"); |
| COMPARE_BOTH(Vmov(I32, q0, 0x006d0000), |
| "vmov.i32 q0, #7143424\n"); |
| COMPARE_BOTH(Vmov(I32, q0, 0x00004da6), |
| "mov ip, #19878\n" |
| "vdup.32 q0, ip\n"); |
| |
| COMPARE_BOTH(Vmov(I64, q0, UINT64_C(0xa5a5a5a5a5a5a5a5)), |
| "vmov.i8 q0, #165\n"); |
| COMPARE_BOTH(Vmov(I64, q0, UINT64_C(0x0a01248315ffffff)), |
| "mvn ip, #3925868544\n" |
| "vdup.32 q0, ip\n" |
| "mov ip, #9347\n" |
| "movt ip, #2561\n" |
| "vmov.32 d0[1], ip\n" |
| "vmov.f64 d1, d0\n"); |
| COMPARE_BOTH(Vmov(I64, q0, UINT64_C(0x6fe1a7a779e33af2)), |
| "mov ip, #15090\n" |
| "movt ip, #31203\n" |
| "vdup.32 q0, ip\n" |
| "mov ip, #42919\n" |
| "movt ip, #28641\n" |
| "vmov.32 d0[1], ip\n" |
| "vmov.f64 d1, d0\n"); |
| COMPARE_BOTH(Vmov(I64, q0, UINT64_C(0x2efa8b440000c1da)), |
| "mov ip, #49626\n" |
| "vdup.32 q0, ip\n" |
| "mov ip, #35652\n" |
| "movt ip, #12026\n" |
| "vmov.32 d0[1], ip\n" |
| "vmov.f64 d1, d0\n"); |
| COMPARE_BOTH(Vmov(I64, q0, UINT64_C(0x00008bb75c3036fd)), |
| "mov ip, #14077\n" |
| "movt ip, #23600\n" |
| "vdup.32 q0, ip\n" |
| "mov ip, #35767\n" |
| "vmov.32 d0[1], ip\n" |
| "vmov.f64 d1, d0\n"); |
| |
| COMPARE_BOTH(Vmov(F32, q0, 0.5), |
| "vmov.f32 q0, #0.5\n"); |
| COMPARE_BOTH(Vmov(F32, q0, 1.1), |
| "mov ip, #52429\n" |
| "movt ip, #16268\n" |
| "vdup.32 q0, ip\n"); |
| COMPARE_BOTH(Vmov(F64, q0, 0.5), |
| "vmov.f64 d0, #0.5\n" |
| "vmov.f64 d1, d0\n"); |
| COMPARE_BOTH(Vmov(F64, q0, 1.1), |
| "mov ip, #39322\n" |
| "movt ip, #39321\n" |
| "vdup.32 d0, ip\n" |
| "mov ip, #39321\n" |
| "movt ip, #16369\n" |
| "vmov.32 d0[1], ip\n" |
| "vmov.f64 d1, d0\n"); |
| |
| COMPARE_T32(Vmov(I64, q0, UINT64_C(0x2fff2fff3e2869e7)), |
| "mov ip, #27111\n" |
| "movt ip, #15912\n" |
| "vdup.32 q0, ip\n" |
| "mvn ip, #3489714176\n" |
| "vmov.32 d0[1], ip\n" |
| "vmov.f64 d1, d0\n"); |
| |
| COMPARE_A32(Vmov(I32, q0, 0x0ffffffa), |
| "mvn ip, #4026531845\n" |
| "vdup.32 q0, ip\n"); |
| COMPARE_A32(Vmov(I64, q0, UINT64_C(0x65ffffff16a0ef46)), |
| "mov ip, #61254\n" |
| "movt ip, #5792\n" |
| "vdup.32 q0, ip\n" |
| "mvn ip, #2583691264\n" |
| "vmov.32 d0[1], ip\n" |
| "vmov.f64 d1, d0\n"); |
| CLEANUP(); |
| } |
| |
| TEST(macro_assembler_T32_IT) { |
| SETUP(); |
| |
| // ADC (register) T1 |
| COMPARE_T32(Adc(eq, r0, r0, r1), |
| "it eq\n" |
| "adceq r0, r1\n"); |
| |
| COMPARE_T32(Adc(eq, r0, r1, r2), |
| "bne 0x00000006\n" |
| "adc r0, r1, r2\n"); |
| |
| // ADD (immediate) T1 |
| COMPARE_T32(Add(eq, r0, r1, 0x1), |
| "it eq\n" |
| "addeq r0, r1, #1\n"); |
| |
| COMPARE_T32(Add(eq, r0, r1, 0x8), |
| "bne 0x00000006\n" |
| "add r0, r1, #8\n"); |
| |
| // ADD (immediate) T2 |
| COMPARE_T32(Add(eq, r0, r0, 0xff), |
| "it eq\n" |
| "addeq r0, #255\n"); |
| |
| // ADD (register) T1 |
| COMPARE_T32(Add(eq, r0, r1, r7), |
| "it eq\n" |
| "addeq r0, r1, r7\n"); |
| |
| // ADD (register) T2 |
| COMPARE_T32(Add(eq, r5, r5, r8), |
| "it eq\n" |
| "addeq r5, r8\n"); |
| |
| // ADD (SP plus immediate) T1 |
| COMPARE_T32(Add(eq, r7, sp, 1020), |
| "it eq\n" |
| "addeq r7, sp, #1020\n"); |
| |
| COMPARE_T32(Add(eq, r7, sp, 1), |
| "bne 0x00000006\n" |
| "add r7, sp, #1\n"); |
| |
| COMPARE_T32(Add(eq, r7, sp, 1024), |
| "bne 0x00000006\n" |
| "add r7, sp, #1024\n"); |
| |
| COMPARE_T32(Add(eq, sp, sp, 32), |
| "bne 0x00000004\n" |
| "add sp, #32\n"); |
| |
| // ADD (SP plus register) T1 |
| COMPARE_T32(Add(eq, r7, sp, r7), |
| "it eq\n" |
| "addeq r7, sp, r7\n"); |
| |
| // ADD (SP plus register) T2 |
| COMPARE_T32(Add(eq, sp, sp, r10), |
| "it eq\n" |
| "addeq sp, r10\n"); |
| |
| COMPARE_T32(Add(eq, r5, r5, sp), |
| "bne 0x00000006\n" |
| "add.w r5, sp\n"); |
| |
| // AND (register) T1 |
| COMPARE_T32(And(eq, r7, r7, r0), |
| "it eq\n" |
| "andeq r7, r0\n"); |
| |
| COMPARE_T32(And(eq, r8, r8, r0), |
| "bne 0x00000006\n" |
| "and r8, r0\n"); |
| |
| // ASR (immediate) T2 |
| COMPARE_T32(Asr(eq, r0, r1, 16), |
| "it eq\n" |
| "asreq r0, r1, #16\n"); |
| |
| COMPARE_T32(Asr(eq, r0, r1, 32), |
| "it eq\n" |
| "asreq r0, r1, #32\n"); |
| |
| COMPARE_T32(Asr(eq, r0, r1, 0), |
| "bne 0x0000000a\n" |
| "mov r0, #0\n" |
| "asr r0, r1, r0\n"); |
| |
| // ASR (register) T1 |
| COMPARE_T32(Asr(eq, r7, r7, r3), |
| "it eq\n" |
| "asreq r7, r3\n"); |
| |
| COMPARE_T32(Asr(eq, r8, r8, r3), |
| "bne 0x00000006\n" |
| "asr r8, r3\n"); |
| |
| // BIC (register) T1 |
| COMPARE_T32(Bic(eq, r7, r7, r6), |
| "it eq\n" |
| "biceq r7, r6\n"); |
| |
| COMPARE_T32(Bic(eq, r8, r8, r6), |
| "bne 0x00000006\n" |
| "bic r8, r6\n"); |
| |
| Label l; |
| __ Bind(&l); |
| |
| // BLX (register) T1 |
| COMPARE_T32(Blx(eq, lr), |
| "it eq\n" |
| "blxeq lr\n"); |
| COMPARE_T32(Blx(eq, &l), |
| "bne 0x00000006\n" |
| "blx 0x00000000\n"); |
| |
| // BX (register) T1 |
| COMPARE_T32(Bx(eq, lr), |
| "it eq\n" |
| "bxeq lr\n"); |
| |
| // CMN (register) T1 |
| COMPARE_T32(Cmn(eq, r0, r1), |
| "it eq\n" |
| "cmneq r0, r1\n"); |
| |
| COMPARE_T32(Cmn(eq, r0, r8), |
| "bne 0x00000006\n" |
| "cmn r0, r8\n"); |
| |
| // CMP (immediate) T1 |
| COMPARE_T32(Cmp(eq, r7, 0xff), |
| "it eq\n" |
| "cmpeq r7, #255\n"); |
| |
| // CMP (register) T1 |
| COMPARE_T32(Cmp(eq, r6, r7), |
| "it eq\n" |
| "cmpeq r6, r7\n"); |
| |
| // CMP (register) T2 |
| COMPARE_T32(Cmp(eq, r9, r10), |
| "it eq\n" |
| "cmpeq r9, r10\n"); |
| |
| COMPARE_T32(Cmp(eq, r0, 0x100), |
| "bne 0x00000006\n" |
| "cmp r0, #256\n"); |
| |
| // EOR (register) T1 |
| COMPARE_T32(Eor(eq, r0, r0, r7), |
| "it eq\n" |
| "eoreq r0, r7\n"); |
| |
| COMPARE_T32(Eor(eq, r0, r0, 0x1), |
| "bne 0x00000006\n" |
| "eor r0, #0x1\n"); |
| |
| // LDR (immediate) T1 |
| COMPARE_T32(Ldr(eq, r4, MemOperand(r7, 124)), |
| "it eq\n" |
| "ldreq r4, [r7, #124]\n"); |
| |
| COMPARE_T32(Ldr(eq, r4, MemOperand(r7, 1)), |
| "bne 0x00000006\n" |
| "ldr r4, [r7, #1]\n"); |
| |
| COMPARE_T32(Ldr(eq, r4, MemOperand(r7, 128)), |
| "bne 0x00000006\n" |
| "ldr r4, [r7, #128]\n"); |
| |
| // LDR (immediate) T2 |
| COMPARE_T32(Ldr(eq, r4, MemOperand(sp, 1020)), |
| "it eq\n" |
| "ldreq r4, [sp, #1020]\n"); |
| |
| COMPARE_T32(Ldr(eq, r4, MemOperand(sp, 1)), |
| "bne 0x00000006\n" |
| "ldr r4, [sp, #1]\n"); |
| |
| COMPARE_T32(Ldr(eq, r4, MemOperand(sp, 1024)), |
| "bne 0x00000006\n" |
| "ldr r4, [sp, #1024]\n"); |
| |
| // LDR (register) T1 |
| COMPARE_T32(Ldr(eq, r5, MemOperand(r6, r7)), |
| "it eq\n" |
| "ldreq r5, [r6, r7]\n"); |
| |
| COMPARE_T32(Ldr(eq, r5, MemOperand(r6, r8)), |
| "bne 0x00000006\n" |
| "ldr r5, [r6, r8]\n"); |
| |
| // LDRB (immediate) T1 |
| COMPARE_T32(Ldrb(eq, r6, MemOperand(r7, 31)), |
| "it eq\n" |
| "ldrbeq r6, [r7, #31]\n"); |
| |
| COMPARE_T32(Ldrb(eq, r6, MemOperand(r7, 32)), |
| "bne 0x00000006\n" |
| "ldrb r6, [r7, #32]\n"); |
| |
| // LDRB (register) T1 |
| COMPARE_T32(Ldrb(eq, r5, MemOperand(r6, r7)), |
| "it eq\n" |
| "ldrbeq r5, [r6, r7]\n"); |
| |
| COMPARE_T32(Ldrb(eq, r6, MemOperand(r9)), |
| "bne 0x00000006\n" |
| "ldrb r6, [r9]\n"); |
| |
| // LDRH (immediate) T1 |
| COMPARE_T32(Ldrh(eq, r6, MemOperand(r7, 62)), |
| "it eq\n" |
| "ldrheq r6, [r7, #62]\n"); |
| |
| COMPARE_T32(Ldrh(eq, r6, MemOperand(r7, 64)), |
| "bne 0x00000006\n" |
| "ldrh r6, [r7, #64]\n"); |
| |
| COMPARE_T32(Ldrh(eq, r6, MemOperand(r7, 1)), |
| "bne 0x00000006\n" |
| "ldrh r6, [r7, #1]\n"); |
| |
| // LDRH (register) T1 |
| COMPARE_T32(Ldrh(eq, r5, MemOperand(r6, r7)), |
| "it eq\n" |
| "ldrheq r5, [r6, r7]\n"); |
| |
| COMPARE_T32(Ldrh(eq, r6, MemOperand(r9)), |
| "bne 0x00000006\n" |
| "ldrh r6, [r9]\n"); |
| |
| // LDRSB (register) T1 |
| COMPARE_T32(Ldrsb(eq, r5, MemOperand(r6, r7)), |
| "it eq\n" |
| "ldrsbeq r5, [r6, r7]\n"); |
| |
| COMPARE_T32(Ldrsb(eq, r6, MemOperand(r9)), |
| "bne 0x00000006\n" |
| "ldrsb r6, [r9]\n"); |
| |
| // LDRSH (register) T1 |
| COMPARE_T32(Ldrsh(eq, r5, MemOperand(r6, r7)), |
| "it eq\n" |
| "ldrsheq r5, [r6, r7]\n"); |
| |
| COMPARE_T32(Ldrsh(eq, r6, MemOperand(r9)), |
| "bne 0x00000006\n" |
| "ldrsh r6, [r9]\n"); |
| |
| // LSL (immediate) T2 |
| COMPARE_T32(Lsl(eq, r0, r1, 16), |
| "it eq\n" |
| "lsleq r0, r1, #16\n"); |
| |
| COMPARE_T32(Lsl(eq, r0, r1, 0), |
| "bne 0x0000000a\n" |
| "mov r0, #0\n" |
| "lsl r0, r1, r0\n"); |
| |
| COMPARE_T32(Lsl(eq, r0, r1, 32), |
| "bne 0x0000000a\n" |
| "mov r0, #32\n" |
| "lsl r0, r1, r0\n"); |
| |
| // LSL (register) T1 |
| COMPARE_T32(Lsl(eq, r7, r7, r3), |
| "it eq\n" |
| "lsleq r7, r3\n"); |
| |
| COMPARE_T32(Lsl(eq, r8, r8, r3), |
| "bne 0x00000006\n" |
| "lsl r8, r3\n"); |
| |
| // LSR (immediate) T2 |
| COMPARE_T32(Lsr(eq, r0, r1, 16), |
| "it eq\n" |
| "lsreq r0, r1, #16\n"); |
| |
| COMPARE_T32(Lsr(eq, r0, r1, 32), |
| "it eq\n" |
| "lsreq r0, r1, #32\n"); |
| |
| COMPARE_T32(Lsr(eq, r0, r1, 0), |
| "bne 0x0000000a\n" |
| "mov r0, #0\n" |
| "lsr r0, r1, r0\n"); |
| |
| // LSR (register) T1 |
| COMPARE_T32(Lsr(eq, r7, r7, r3), |
| "it eq\n" |
| "lsreq r7, r3\n"); |
| |
| COMPARE_T32(Lsr(eq, r8, r8, r3), |
| "bne 0x00000006\n" |
| "lsr r8, r3\n"); |
| |
| // MOV (immediate) T1 |
| COMPARE_T32(Mov(eq, r7, 0xff), |
| "it eq\n" |
| "moveq r7, #255\n"); |
| |
| // MOV (register) T1 |
| COMPARE_T32(Mov(eq, r9, r8), |
| "it eq\n" |
| "moveq r9, r8\n"); |
| |
| // MOV (register) T2 |
| COMPARE_T32(Mov(eq, r0, Operand(r1, LSR, 16)), |
| "it eq\n" |
| "lsreq r0, r1, #16\n"); |
| |
| COMPARE_T32(Mov(eq, r0, Operand(r1, ROR, 16)), |
| "bne 0x00000006\n" |
| "ror r0, r1, #16\n"); |
| |
| // MOV (register-shifted register) T1 |
| COMPARE_T32(Mov(eq, r0, Operand(r0, LSR, r1)), |
| "it eq\n" |
| "lsreq r0, r1\n"); |
| |
| COMPARE_T32(Mov(eq, r0, Operand(r1, LSR, r2)), |
| "bne 0x00000006\n" |
| "lsr r0, r1, r2\n"); |
| |
| // MUL (T1) |
| COMPARE_T32(Mul(eq, r0, r1, r0), |
| "it eq\n" |
| "muleq r0, r1, r0\n"); |
| |
| COMPARE_T32(Mul(eq, r0, r1, r2), |
| "bne 0x00000006\n" |
| "mul r0, r1, r2\n"); |
| |
| // MVN (register) T1 |
| COMPARE_T32(Mvn(eq, r4, r6), |
| "it eq\n" |
| "mvneq r4, r6\n"); |
| |
| COMPARE_T32(Mvn(eq, r8, r6), |
| "bne 0x00000006\n" |
| "mvn r8, r6\n"); |
| |
| // ORR (register) T1 |
| COMPARE_T32(Orr(eq, r0, r0, r1), |
| "it eq\n" |
| "orreq r0, r1\n"); |
| |
| COMPARE_T32(Orr(eq, r0, r1, r2), |
| "bne 0x00000006\n" |
| "orr r0, r1, r2\n"); |
| |
| // ROR (register) T1 |
| COMPARE_T32(Ror(eq, r7, r7, r3), |
| "it eq\n" |
| "roreq r7, r3\n"); |
| |
| COMPARE_T32(Ror(eq, r8, r8, r3), |
| "bne 0x00000006\n" |
| "ror r8, r3\n"); |
| |
| COMPARE_T32(Ror(eq, r0, r1, 16), |
| "bne 0x00000006\n" |
| "ror r0, r1, #16\n"); |
| |
| // RSB (immediate) T1 |
| COMPARE_T32(Rsb(eq, r0, r1, 0), |
| "it eq\n" |
| "rsbeq r0, r1, #0\n"); |
| |
| COMPARE_T32(Rsb(eq, r0, r1, 1), |
| "bne 0x00000006\n" |
| "rsb r0, r1, #1\n"); |
| |
| // SBC (register) T1 |
| COMPARE_T32(Sbc(eq, r0, r0, r1), |
| "it eq\n" |
| "sbceq r0, r1\n"); |
| |
| COMPARE_T32(Sbc(eq, r0, r1, r2), |
| "bne 0x00000006\n" |
| "sbc r0, r1, r2\n"); |
| |
| // STR (immediate) T1 |
| COMPARE_T32(Str(eq, r4, MemOperand(r7, 124)), |
| "it eq\n" |
| "streq r4, [r7, #124]\n"); |
| |
| COMPARE_T32(Str(eq, r4, MemOperand(r7, 1)), |
| "bne 0x00000006\n" |
| "str r4, [r7, #1]\n"); |
| |
| COMPARE_T32(Str(eq, r4, MemOperand(r7, 128)), |
| "bne 0x00000006\n" |
| "str r4, [r7, #128]\n"); |
| |
| // STR (immediate) T2 |
| COMPARE_T32(Str(eq, r4, MemOperand(sp, 1020)), |
| "it eq\n" |
| "streq r4, [sp, #1020]\n"); |
| |
| COMPARE_T32(Str(eq, r4, MemOperand(sp, 1)), |
| "bne 0x00000006\n" |
| "str r4, [sp, #1]\n"); |
| |
| COMPARE_T32(Str(eq, r4, MemOperand(sp, 1024)), |
| "bne 0x00000006\n" |
| "str r4, [sp, #1024]\n"); |
| |
| // STR (register) T1 |
| COMPARE_T32(Str(eq, r5, MemOperand(r6, r7)), |
| "it eq\n" |
| "streq r5, [r6, r7]\n"); |
| |
| COMPARE_T32(Str(eq, r5, MemOperand(r6, r8)), |
| "bne 0x00000006\n" |
| "str r5, [r6, r8]\n"); |
| |
| // STRB (immediate) T1 |
| COMPARE_T32(Strb(eq, r6, MemOperand(r7, 31)), |
| "it eq\n" |
| "strbeq r6, [r7, #31]\n"); |
| |
| COMPARE_T32(Strb(eq, r6, MemOperand(r7, 32)), |
| "bne 0x00000006\n" |
| "strb r6, [r7, #32]\n"); |
| |
| // STRB (register) T1 |
| COMPARE_T32(Strb(eq, r5, MemOperand(r6, r7)), |
| "it eq\n" |
| "strbeq r5, [r6, r7]\n"); |
| |
| COMPARE_T32(Strb(eq, r6, MemOperand(r9)), |
| "bne 0x00000006\n" |
| "strb r6, [r9]\n"); |
| |
| // STRH (immediate) T1 |
| COMPARE_T32(Strh(eq, r6, MemOperand(r7, 62)), |
| "it eq\n" |
| "strheq r6, [r7, #62]\n"); |
| |
| COMPARE_T32(Strh(eq, r6, MemOperand(r7, 64)), |
| "bne 0x00000006\n" |
| "strh r6, [r7, #64]\n"); |
| |
| COMPARE_T32(Strh(eq, r6, MemOperand(r7, 1)), |
| "bne 0x00000006\n" |
| "strh r6, [r7, #1]\n"); |
| |
| // STRH (register) T1 |
| COMPARE_T32(Strh(eq, r5, MemOperand(r6, r7)), |
| "it eq\n" |
| "strheq r5, [r6, r7]\n"); |
| |
| COMPARE_T32(Strh(eq, r6, MemOperand(r9)), |
| "bne 0x00000006\n" |
| "strh r6, [r9]\n"); |
| |
| // SUB (immediate) T1 |
| COMPARE_T32(Sub(eq, r0, r1, 0x1), |
| "it eq\n" |
| "subeq r0, r1, #1\n"); |
| |
| COMPARE_T32(Sub(eq, r0, r1, 0x8), |
| "bne 0x00000006\n" |
| "sub r0, r1, #8\n"); |
| |
| // SUB (immediate) T2 |
| COMPARE_T32(Sub(eq, r0, r0, 0xff), |
| "it eq\n" |
| "subeq r0, #255\n"); |
| |
| // SUB (register) T1 |
| COMPARE_T32(Sub(eq, r0, r1, r7), |
| "it eq\n" |
| "subeq r0, r1, r7\n"); |
| |
| COMPARE_T32(Sub(eq, r5, r5, r8), |
| "bne 0x00000006\n" |
| "sub r5, r8\n"); |
| |
| COMPARE_T32(Sub(eq, r7, sp, 1), |
| "bne 0x00000006\n" |
| "sub r7, sp, #1\n"); |
| |
| COMPARE_T32(Sub(eq, pc, pc, 0), |
| "bne 0x00000006\n" |
| "sub pc, #0\n"); |
| |
| // TST (register) T1 |
| COMPARE_T32(Tst(eq, r0, r1), |
| "it eq\n" |
| "tsteq r0, r1\n"); |
| |
| COMPARE_T32(Tst(eq, r8, r9), |
| "bne 0x00000006\n" |
| "tst r8, r9\n"); |
| |
| CLEANUP(); |
| } |
| |
| |
| TEST(unbound_label) { |
| SETUP(); |
| |
| #ifdef VIXL_DEBUG |
| MUST_FAIL_TEST_BOTH_BLOCK( |
| { |
| Label label; |
| masm.B(&label); |
| }, |
| "Label used but not bound.\n") |
| |
| MUST_FAIL_TEST_BOTH_BLOCK( |
| { |
| Label label; |
| masm.B(eq, &label); |
| }, |
| "Label used but not bound.\n") |
| |
| MUST_FAIL_TEST_T32_BLOCK( |
| { |
| Label label; |
| masm.Cbz(r0, &label); |
| }, |
| "Label used but not bound.\n") |
| |
| MUST_FAIL_TEST_T32_BLOCK( |
| { |
| Label label; |
| masm.Cbnz(r1, &label); |
| }, |
| "Label used but not bound.\n") |
| #endif |
| |
| CLEANUP(); |
| } |
| |
| |
| |
| TEST(macro_assembler_AddressComputationHelper) { |
| SETUP(); |
| |
| // Simple cases: the address fits in the mask. |
| COMPARE_A32(Ldr(r0, masm.MemOperandComputationHelper(r1, r1, 0xfff, 0xfff)), |
| "ldr r0, [r1, #4095]\n"); |
| COMPARE_A32(Ldr(r0, masm.MemOperandComputationHelper(r1, r1, 1, 0xfff)), |
| "ldr r0, [r1, #1]\n"); |
| COMPARE_A32(Ldr(r0, masm.MemOperandComputationHelper(r1, r1, 0, 0xfff)), |
| "ldr r0, [r1]\n"); |
| |
| // Similar, but the base register must be preserved. (This has no effect for |
| // encodable cases.) |
| COMPARE_A32(Ldr(r0, masm.MemOperandComputationHelper(r2, r1, 0xfff, 0xfff)), |
| "ldr r0, [r1, #4095]\n"); |
| |
| // Cases where the extra offset has to be aligned. |
| COMPARE_A32(Vldr(d0, masm.MemOperandComputationHelper(r1, r1, 0x3fc, 0x3fc)), |
| "vldr d0, [r1, #1020]\n"); |
| |
| // Out-of-range offsets. |
| COMPARE_A32(Ldr(r0, masm.MemOperandComputationHelper(r1, r1, 0x1000, 0xfff)), |
| "add r1, #4096\n" |
| "ldr r0, [r1]\n"); |
| COMPARE_A32(Ldr(r0, masm.MemOperandComputationHelper(r2, r1, 0x1000, 0xfff)), |
| "add r2, r1, #4096\n" |
| "ldr r0, [r2]\n"); |
| COMPARE_A32(Ldr(r0, masm.MemOperandComputationHelper(r2, |
| r1, |
| 0xffffffff, |
| 0xfff)), |
| "sub r2, r1, #1\n" |
| "ldr r0, [r2]\n"); |
| |
| // TODO: Improve the code generation for these cases. |
| |
| COMPARE_A32(Ldr(r0, masm.MemOperandComputationHelper(r2, |
| r1, |
| 0x12345678, |
| 0xfff)), |
| "mov r2, #20480\n" |
| "movt r2, #4660\n" |
| "add r2, r1, r2\n" |
| "ldr r0, [r2, #1656]\n"); |
| COMPARE_A32(Ldr(r0, masm.MemOperandComputationHelper(r2, |
| r1, |
| 0x7fffffff, |
| 0xfff)), |
| "sub r2, r1, #1\n" |
| "sub r2, #2147483648\n" |
| "ldr r0, [r2]\n"); |
| COMPARE_A32(Ldr(r0, masm.MemOperandComputationHelper(r2, |
| r1, |
| 0xffcba000, |
| 0xfff)), |
| "sub r2, r1, #286720\n" |
| "sub r2, #3145728\n" |
| "ldr r0, [r2]\n"); |
| |
| CLEANUP(); |
| } |
| |
| |
| TEST(barriers) { |
| SETUP(); |
| |
| // DMB |
| COMPARE_BOTH(Dmb(SY), "dmb sy\n"); |
| COMPARE_BOTH(Dmb(ST), "dmb st\n"); |
| COMPARE_BOTH(Dmb(ISH), "dmb ish\n"); |
| COMPARE_BOTH(Dmb(ISHST), "dmb ishst\n"); |
| COMPARE_BOTH(Dmb(NSH), "dmb nsh\n"); |
| COMPARE_BOTH(Dmb(NSHST), "dmb nshst\n"); |
| COMPARE_BOTH(Dmb(OSH), "dmb osh\n"); |
| COMPARE_BOTH(Dmb(OSHST), "dmb oshst\n"); |
| |
| // DSB |
| COMPARE_BOTH(Dsb(SY), "dsb sy\n"); |
| COMPARE_BOTH(Dsb(ST), "dsb st\n"); |
| COMPARE_BOTH(Dsb(ISH), "dsb ish\n"); |
| COMPARE_BOTH(Dsb(ISHST), "dsb ishst\n"); |
| COMPARE_BOTH(Dsb(NSH), "dsb nsh\n"); |
| COMPARE_BOTH(Dsb(NSHST), "dsb nshst\n"); |
| COMPARE_BOTH(Dsb(OSH), "dsb osh\n"); |
| COMPARE_BOTH(Dsb(OSHST), "dsb oshst\n"); |
| |
| // ISB |
| COMPARE_BOTH(Isb(SY), "isb sy\n"); |
| |
| CLEANUP(); |
| } |
| |
| |
| TEST(preloads) { |
| // Smoke test for various pld/pli forms. |
| SETUP(); |
| |
| // PLD immediate |
| COMPARE_BOTH(Pld(MemOperand(r0, 0)), "pld [r0]\n"); |
| COMPARE_BOTH(Pld(MemOperand(r1, 123)), "pld [r1, #123]\n"); |
| COMPARE_BOTH(Pld(MemOperand(r4, -123)), "pld [r4, #-123]\n"); |
| |
| COMPARE_A32(Pld(MemOperand(r7, -4095)), "pld [r7, #-4095]\n"); |
| |
| // PLDW immediate |
| COMPARE_BOTH(Pldw(MemOperand(r0, 0)), "pldw [r0]\n"); |
| COMPARE_BOTH(Pldw(MemOperand(r1, 123)), "pldw [r1, #123]\n"); |
| COMPARE_BOTH(Pldw(MemOperand(r4, -123)), "pldw [r4, #-123]\n"); |
| |
| COMPARE_A32(Pldw(MemOperand(r7, -4095)), "pldw [r7, #-4095]\n"); |
| |
| // PLD register |
| COMPARE_BOTH(Pld(MemOperand(r0, r1)), "pld [r0, r1]\n"); |
| COMPARE_BOTH(Pld(MemOperand(r0, r1, LSL, 1)), "pld [r0, r1, lsl #1]\n"); |
| |
| COMPARE_A32(Pld(MemOperand(r0, r1, LSL, 20)), "pld [r0, r1, lsl #20]\n"); |
| |
| // PLDW register |
| COMPARE_BOTH(Pldw(MemOperand(r0, r1)), "pldw [r0, r1]\n"); |
| COMPARE_BOTH(Pldw(MemOperand(r0, r1, LSL, 1)), "pldw [r0, r1, lsl #1]\n"); |
| |
| COMPARE_A32(Pldw(MemOperand(r0, r1, LSL, 20)), "pldw [r0, r1, lsl #20]\n"); |
| |
| // PLD literal |
| Label pld_label; |
| COMPARE_A32(Pld(&pld_label);, "pld [pc, #-0]\n"); |
| COMPARE_T32(Pld(&pld_label);, "pld [pc, #-0]\n"); |
| __ Bind(&pld_label); |
| |
| // PLI immediate |
| COMPARE_BOTH(Pli(MemOperand(r0, 0)), "pli [r0]\n"); |
| COMPARE_BOTH(Pli(MemOperand(r1, 123)), "pli [r1, #123]\n"); |
| COMPARE_BOTH(Pli(MemOperand(r4, -123)), "pli [r4, #-123]\n"); |
| |
| COMPARE_A32(Pli(MemOperand(r7, -4095)), "pli [r7, #-4095]\n"); |
| |
| // PLI register |
| COMPARE_BOTH(Pli(MemOperand(r0, r1)), "pli [r0, r1]\n"); |
| COMPARE_BOTH(Pli(MemOperand(r0, r1, LSL, 1)), "pli [r0, r1, lsl #1]\n"); |
| |
| COMPARE_A32(Pli(MemOperand(r0, r1, LSL, 20)), "pli [r0, r1, lsl #20]\n"); |
| |
| // PLI literal |
| Label pli_label; |
| COMPARE_A32(Pli(&pli_label);, "pli [pc, #-0]\n"); |
| COMPARE_T32(Pli(&pli_label);, "pli [pc, #-0]\n"); |
| __ Bind(&pli_label); |
| |
| CLEANUP(); |
| } |
| |
| |
| TEST(vmrs_vmsr) { |
| SETUP(); |
| |
| COMPARE_BOTH(Vmsr(FPSCR, r0), |
| "vmsr FPSCR, r0\n"); |
| |
| COMPARE_BOTH(Vmrs(RegisterOrAPSR_nzcv(r1.GetCode()), FPSCR), |
| "vmrs r1, FPSCR\n"); |
| |
| COMPARE_BOTH(Vmrs(RegisterOrAPSR_nzcv(pc.GetCode()), FPSCR), |
| "vmrs APSR_nzcv, FPSCR\n"); |
| |
| CLEANUP(); |
| } |
| |
| |
| TEST(ldm_stm) { |
| SETUP(); |
| |
| // ldm/stm |
| COMPARE_BOTH(Ldm(r0, NO_WRITE_BACK, RegisterList(r1)), |
| "ldm r0, {r1}\n"); |
| |
| COMPARE_BOTH(Ldm(r1, NO_WRITE_BACK, RegisterList(r2, r5, r9, r10)), |
| "ldm r1, {r2,r5,r9,r10}\n"); |
| |
| COMPARE_BOTH(Ldm(r0, WRITE_BACK, RegisterList(r1, r2)), |
| "ldm r0!, {r1,r2}\n"); |
| |
| COMPARE_BOTH(Stm(r1, NO_WRITE_BACK, RegisterList(r2, r5, r9, r10)), |
| "stm r1, {r2,r5,r9,r10}\n"); |
| |
| COMPARE_BOTH(Stm(r0, WRITE_BACK, RegisterList(r1, r2)), |
| "stm r0!, {r1,r2}\n"); |
| |
| // ldmda/stmda |
| COMPARE_A32(Ldmda(r11, WRITE_BACK, RegisterList(r0, r1)), |
| "ldmda r11!, {r0,r1}\n"); |
| |
| COMPARE_A32(Ldmda(r11, NO_WRITE_BACK, RegisterList(r2, r3)), |
| "ldmda r11, {r2,r3}\n"); |
| |
| COMPARE_A32(Stmda(r11, WRITE_BACK, RegisterList(r0, r1)), |
| "stmda r11!, {r0,r1}\n"); |
| |
| COMPARE_A32(Stmda(r11, NO_WRITE_BACK, RegisterList(r2, r3)), |
| "stmda r11, {r2,r3}\n"); |
| |
| // ldmib/stmib |
| COMPARE_A32(Ldmib(r11, WRITE_BACK, RegisterList(r0, r1)), |
| "ldmib r11!, {r0,r1}\n"); |
| |
| COMPARE_A32(Ldmib(r11, NO_WRITE_BACK, RegisterList(r2, r3)), |
| "ldmib r11, {r2,r3}\n"); |
| |
| COMPARE_A32(Stmib(r11, WRITE_BACK, RegisterList(r0, r1)), |
| "stmib r11!, {r0,r1}\n"); |
| |
| COMPARE_A32(Stmib(r11, NO_WRITE_BACK, RegisterList(r2, r3)), |
| "stmib r11, {r2,r3}\n"); |
| |
| // ldmdb/stmdb |
| COMPARE_BOTH(Ldmdb(r11, WRITE_BACK, RegisterList(r0, r1)), |
| "ldmdb r11!, {r0,r1}\n"); |
| |
| COMPARE_BOTH(Ldmdb(r11, NO_WRITE_BACK, RegisterList(r2, r3)), |
| "ldmdb r11, {r2,r3}\n"); |
| |
| COMPARE_BOTH(Stmdb(r11, WRITE_BACK, RegisterList(r0, r1)), |
| "stmdb r11!, {r0,r1}\n"); |
| |
| COMPARE_BOTH(Stmdb(r11, NO_WRITE_BACK, RegisterList(r2, r3)), |
| "stmdb r11, {r2,r3}\n"); |
| |
| CLEANUP(); |
| } |
| |
| |
| #define CHECK_T32_16(ASM, EXP) COMPARE_T32_CHECK_SIZE(ASM, EXP, 2) |
| // For instructions inside an IT block, we need to account for the IT |
| // instruction as well (another 16 bits). |
| #define CHECK_T32_16_IT_BLOCK(ASM, EXP) COMPARE_T32_CHECK_SIZE(ASM, EXP, 4) |
| |
| TEST(macro_assembler_T32_16bit) { |
| SETUP(); |
| |
| // Allow the test to use all registers. |
| UseScratchRegisterScope temps(&masm); |
| temps.ExcludeAll(); |
| |
| CHECK_T32_16(Adc(DontCare, r7, r7, r6), |
| "adcs r7, r6\n"); |
| |
| CHECK_T32_16_IT_BLOCK(Adc(DontCare, eq, r7, r7, r6), |
| "it eq\n" |
| "adceq r7, r6\n"); |
| |
| CHECK_T32_16(Add(DontCare, r6, r7, 7), |
| "adds r6, r7, #7\n"); |
| |
| CHECK_T32_16_IT_BLOCK(Add(DontCare, lt, r6, r7, 7), |
| "it lt\n" |
| "addlt r6, r7, #7\n"); |
| |
| CHECK_T32_16(Add(DontCare, r5, r5, 255), |
| "adds r5, #255\n"); |
| |
| CHECK_T32_16_IT_BLOCK(Add(DontCare, lt, r5, r5, 255), |
| "it lt\n" |
| "addlt r5, #255\n"); |
| |
| // Make sure we select the non flag-setting version here, since |
| // this can have two potential encodings. |
| CHECK_T32_16(Add(DontCare, r1, r1, r2), |
| "add r1, r2\n"); |
| |
| CHECK_T32_16(Add(DontCare, r1, r2, r7), |
| "adds r1, r2, r7\n"); |
| |
| CHECK_T32_16_IT_BLOCK(Add(DontCare, lt, r1, r2, r7), |
| "it lt\n" |
| "addlt r1, r2, r7\n"); |
| |
| CHECK_T32_16(Add(DontCare, r4, r4, r12), |
| "add r4, ip\n"); |
| |
| CHECK_T32_16_IT_BLOCK(Add(DontCare, eq, r4, r4, r12), |
| "it eq\n" |
| "addeq r4, ip\n"); |
| |
| CHECK_T32_16(Add(DontCare, r0, sp, 1020), |
| "add r0, sp, #1020\n"); |
| |
| CHECK_T32_16_IT_BLOCK(Add(DontCare, ge, r0, sp, 1020), |
| "it ge\n" |
| "addge r0, sp, #1020\n"); |
| |
| // The equivalent inside an IT block is deprecated. |
| CHECK_T32_16(Add(DontCare, sp, sp, 508), |
| "add sp, #508\n"); |
| |
| CHECK_T32_16(Add(DontCare, r7, sp, r7), |
| "add r7, sp, r7\n"); |
| |
| CHECK_T32_16_IT_BLOCK(Add(DontCare, eq, r7, sp, r7), |
| "it eq\n" |
| "addeq r7, sp, r7\n"); |
| |
| CHECK_T32_16(Add(DontCare, sp, sp, r10), |
| "add sp, r10\n"); |
| |
| CHECK_T32_16_IT_BLOCK(Add(DontCare, eq, sp, sp, r10), |
| "it eq\n" |
| "addeq sp, r10\n"); |
| |
| CHECK_T32_16(And(DontCare, r7, r7, r6), |
| "ands r7, r6\n"); |
| |
| CHECK_T32_16_IT_BLOCK(And(DontCare, eq, r7, r7, r6), |
| "it eq\n" |
| "andeq r7, r6\n"); |
| |
| CHECK_T32_16(Asr(DontCare, r0, r1, 32), |
| "asrs r0, r1, #32\n"); |
| |
| CHECK_T32_16_IT_BLOCK(Asr(DontCare, eq, r0, r1, 32), |
| "it eq\n" |
| "asreq r0, r1, #32\n"); |
| |
| CHECK_T32_16(Asr(DontCare, r0, r0, r1), |
| "asrs r0, r1\n"); |
| |
| CHECK_T32_16_IT_BLOCK(Asr(DontCare, eq, r0, r0, r1), |
| "it eq\n" |
| "asreq r0, r1\n"); |
| |
| CHECK_T32_16(Bic(DontCare, r7, r7, r6), |
| "bics r7, r6\n"); |
| |
| CHECK_T32_16_IT_BLOCK(Bic(DontCare, eq, r7, r7, r6), |
| "it eq\n" |
| "biceq r7, r6\n"); |
| |
| CHECK_T32_16(Eor(DontCare, r7, r7, r6), |
| "eors r7, r6\n"); |
| |
| CHECK_T32_16_IT_BLOCK(Eor(DontCare, eq, r7, r7, r6), |
| "it eq\n" |
| "eoreq r7, r6\n"); |
| |
| CHECK_T32_16(Lsl(DontCare, r0, r1, 31), |
| "lsls r0, r1, #31\n"); |
| |
| CHECK_T32_16_IT_BLOCK(Lsl(DontCare, eq, r0, r1, 31), |
| "it eq\n" |
| "lsleq r0, r1, #31\n"); |
| |
| CHECK_T32_16(Lsl(DontCare, r0, r0, r1), |
| "lsls r0, r1\n"); |
| |
| CHECK_T32_16_IT_BLOCK(Lsl(DontCare, eq, r0, r0, r1), |
| "it eq\n" |
| "lsleq r0, r1\n"); |
| |
| CHECK_T32_16(Lsr(DontCare, r0, r1, 32), |
| "lsrs r0, r1, #32\n"); |
| |
| CHECK_T32_16_IT_BLOCK(Lsr(DontCare, eq, r0, r1, 32), |
| "it eq\n" |
| "lsreq r0, r1, #32\n"); |
| |
| CHECK_T32_16(Lsr(DontCare, r0, r0, r1), |
| "lsrs r0, r1\n"); |
| |
| CHECK_T32_16_IT_BLOCK(Lsr(DontCare, eq, r0, r0, r1), |
| "it eq\n" |
| "lsreq r0, r1\n"); |
| |
| CHECK_T32_16(Mov(DontCare, r7, 255), |
| "movs r7, #255\n"); |
| |
| CHECK_T32_16_IT_BLOCK(Mov(DontCare, eq, r7, 255), |
| "it eq\n" |
| "moveq r7, #255\n"); |
| |
| CHECK_T32_16(Mov(DontCare, r9, r8), |
| "mov r9, r8\n"); |
| |
| // Check that we don't try to pick the MOVS register-shifted register variant. |
| CHECK_T32_16(Mov(DontCare, r5, r6), |
| "mov r5, r6\n"); |
| |
| CHECK_T32_16_IT_BLOCK(Mov(DontCare, eq, r9, r8), |
| "it eq\n" |
| "moveq r9, r8\n"); |
| |
| CHECK_T32_16(Mov(DontCare, r5, Operand(r6, ASR, 1)), |
| "asrs r5, r6, #1\n"); |
| |
| CHECK_T32_16(Mov(DontCare, r5, Operand(r6, ASR, 32)), |
| "asrs r5, r6, #32\n"); |
| |
| CHECK_T32_16(Mov(DontCare, r5, Operand(r6, LSR, 1)), |
| "lsrs r5, r6, #1\n"); |
| |
| CHECK_T32_16(Mov(DontCare, r5, Operand(r6, LSR, 32)), |
| "lsrs r5, r6, #32\n"); |
| |
| CHECK_T32_16(Mov(DontCare, r5, Operand(r6, LSL, 1)), |
| "lsls r5, r6, #1\n"); |
| |
| CHECK_T32_16(Mov(DontCare, r5, Operand(r6, LSL, 31)), |
| "lsls r5, r6, #31\n"); |
| |
| CHECK_T32_16_IT_BLOCK(Mov(DontCare, eq, r5, Operand(r6, ASR, 1)), |
| "it eq\n" |
| "asreq r5, r6, #1\n"); |
| |
| CHECK_T32_16_IT_BLOCK(Mov(DontCare, eq, r5, Operand(r6, ASR, 32)), |
| "it eq\n" |
| "asreq r5, r6, #32\n"); |
| |
| CHECK_T32_16_IT_BLOCK(Mov(DontCare, eq, r5, Operand(r6, LSR, 1)), |
| "it eq\n" |
| "lsreq r5, r6, #1\n"); |
| |
| CHECK_T32_16_IT_BLOCK(Mov(DontCare, eq, r5, Operand(r6, LSR, 32)), |
| "it eq\n" |
| "lsreq r5, r6, #32\n"); |
| |
| CHECK_T32_16_IT_BLOCK(Mov(DontCare, eq, r5, Operand(r6, LSL, 1)), |
| "it eq\n" |
| "lsleq r5, r6, #1\n"); |
| |
| CHECK_T32_16_IT_BLOCK(Mov(DontCare, eq, r5, Operand(r6, LSL, 31)), |
| "it eq\n" |
| "lsleq r5, r6, #31\n"); |
| |
| CHECK_T32_16(Mov(DontCare, r7, Operand(r7, ASR, r6)), |
| "asrs r7, r6\n"); |
| |
| CHECK_T32_16_IT_BLOCK(Mov(DontCare, eq, r7, Operand(r7, ASR, r6)), |
| "it eq\n" |
| "asreq r7, r6\n"); |
| |
| CHECK_T32_16(Mov(DontCare, r7, Operand(r7, LSR, r6)), |
| "lsrs r7, r6\n"); |
| |
| CHECK_T32_16_IT_BLOCK(Mov(DontCare, eq, r7, Operand(r7, LSR, r6)), |
| "it eq\n" |
| "lsreq r7, r6\n"); |
| |
| CHECK_T32_16(Mov(DontCare, r7, Operand(r7, LSL, r6)), |
| "lsls r7, r6\n"); |
| |
| CHECK_T32_16_IT_BLOCK(Mov(DontCare, eq, r7, Operand(r7, LSL, r6)), |
| "it eq\n" |
| "lsleq r7, r6\n"); |
| |
| CHECK_T32_16(Mov(DontCare, r7, Operand(r7, ROR, r6)), |
| "rors r7, r6\n"); |
| |
| CHECK_T32_16_IT_BLOCK(Mov(DontCare, eq, r7, Operand(r7, ROR, r6)), |
| "it eq\n" |
| "roreq r7, r6\n"); |
| |
| CHECK_T32_16(Mul(DontCare, r0, r1, r0), |
| "muls r0, r1, r0\n"); |
| |
| CHECK_T32_16_IT_BLOCK(Mul(DontCare, eq, r0, r1, r0), |
| "it eq\n" |
| "muleq r0, r1, r0\n"); |
| |
| CHECK_T32_16(Mvn(DontCare, r6, r7), |
| "mvns r6, r7\n"); |
| |
| CHECK_T32_16_IT_BLOCK(Mvn(DontCare, eq, r6, r7), |
| "it eq\n" |
| "mvneq r6, r7\n"); |
| |
| CHECK_T32_16(Orr(DontCare, r7, r7, r6), |
| "orrs r7, r6\n"); |
| |
| CHECK_T32_16_IT_BLOCK(Orr(DontCare, eq, r7, r7, r6), |
| "it eq\n" |
| "orreq r7, r6\n"); |
| |
| CHECK_T32_16(Ror(DontCare, r0, r0, r1), |
| "rors r0, r1\n"); |
| |
| CHECK_T32_16_IT_BLOCK(Ror(DontCare, eq, r0, r0, r1), |
| "it eq\n" |
| "roreq r0, r1\n"); |
| |
| CHECK_T32_16(Rsb(DontCare, r7, r6, 0), |
| "rsbs r7, r6, #0\n"); |
| |
| CHECK_T32_16_IT_BLOCK(Rsb(DontCare, eq, r7, r6, 0), |
| "it eq\n" |
| "rsbeq r7, r6, #0\n"); |
| |
| CHECK_T32_16(Sbc(DontCare, r7, r7, r6), |
| "sbcs r7, r6\n"); |
| |
| CHECK_T32_16_IT_BLOCK(Sbc(DontCare, eq, r7, r7, r6), |
| "it eq\n" |
| "sbceq r7, r6\n"); |
| |
| CHECK_T32_16(Sub(DontCare, r6, r7, 7), |
| "subs r6, r7, #7\n"); |
| |
| CHECK_T32_16_IT_BLOCK(Sub(DontCare, lt, r6, r7, 7), |
| "it lt\n" |
| "sublt r6, r7, #7\n"); |
| |
| CHECK_T32_16(Sub(DontCare, r5, r5, 255), |
| "subs r5, #255\n"); |
| |
| CHECK_T32_16_IT_BLOCK(Sub(DontCare, lt, r5, r5, 255), |
| "it lt\n" |
| "sublt r5, #255\n"); |
| |
| CHECK_T32_16(Sub(DontCare, r1, r2, r7), |
| "subs r1, r2, r7\n"); |
| |
| CHECK_T32_16_IT_BLOCK(Sub(DontCare, lt, r1, r2, r7), |
| "it lt\n" |
| "sublt r1, r2, r7\n"); |
| |
| // The equivalent inside an IT block is deprecated. |
| CHECK_T32_16(Sub(DontCare, sp, sp, 508), |
| "sub sp, #508\n"); |
| |
| // Generate SUBS for ADD. |
| CHECK_T32_16(Add(DontCare, r0, r1, -1), |
| "subs r0, r1, #1\n"); |
| |
| CHECK_T32_16(Add(DontCare, r0, r1, -7), |
| "subs r0, r1, #7\n"); |
| |
| CHECK_T32_16(Add(DontCare, r6, r6, -1), |
| "subs r6, #1\n"); |
| |
| CHECK_T32_16(Add(DontCare, r6, r6, -255), |
| "subs r6, #255\n"); |
| |
| // Generate ADDS for SUB. |
| CHECK_T32_16(Sub(DontCare, r0, r1, -1), |
| "adds r0, r1, #1\n"); |
| |
| CHECK_T32_16(Sub(DontCare, r0, r1, -7), |
| "adds r0, r1, #7\n"); |
| |
| CHECK_T32_16(Sub(DontCare, r6, r6, -1), |
| "adds r6, #1\n"); |
| |
| CHECK_T32_16(Sub(DontCare, r6, r6, -255), |
| "adds r6, #255\n"); |
| |
| // Check that we don't change the opcode for INT_MIN. |
| COMPARE_T32(Add(DontCare, r6, r6, 0x80000000), |
| "add r6, #2147483648\n"); |
| |
| COMPARE_T32(Sub(DontCare, r6, r6, 0x80000000), |
| "sub r6, #2147483648\n"); |
| |
| CLEANUP(); |
| } |
| #undef CHECK_T32_16 |
| #undef CHECK_T32_16_IT_BLOCK |
| |
| TEST(nop_code) { |
| SETUP(); |
| |
| COMPARE_BOTH(Nop(), "nop\n"); |
| |
| COMPARE_BOTH(And(r0, r0, r0), ""); |
| COMPARE_BOTH(And(DontCare, r0, r0, r0), ""); |
| |
| COMPARE_BOTH(Mov(r0, r0), ""); |
| COMPARE_BOTH(Mov(DontCare, r0, r0), ""); |
| |
| COMPARE_BOTH(Orr(r0, r0, r0), ""); |
| COMPARE_BOTH(Orr(DontCare, r0, r0, r0), ""); |
| |
| CLEANUP(); |
| } |
| |
| |
| TEST(big_add_sub) { |
| SETUP(); |
| |
| COMPARE_A32(Add(r0, r1, 0x4321), |
| "add r0, r1, #33\n" |
| "add r0, #17152\n"); |
| COMPARE_T32(Add(r0, r1, 0x4321), |
| "add r0, r1, #801\n" |
| "add r0, #16384\n"); |
| COMPARE_BOTH(Add(r0, r1, 0x432100), |
| "add r0, r1, #8448\n" |
| "add r0, #4390912\n"); |
| COMPARE_BOTH(Add(r0, r1, 0x43000210), |
| "add r0, r1, #528\n" |
| "add r0, #1124073472\n"); |
| COMPARE_BOTH(Add(r0, r1, 0x30c00210), |
| "add r0, r1, #528\n" |
| "add r0, #817889280\n"); |
| COMPARE_BOTH(Add(r0, r1, 0x43000021), |
| "add r0, r1, #33\n" |
| "add r0, #1124073472\n"); |
| COMPARE_T32(Add(r0, r1, 0x54321), |
| "add r0, r1, #801\n" |
| "add r0, #344064\n"); |
| COMPARE_T32(Add(r0, r1, 0x54000321), |
| "add r0, r1, #801\n" |
| "add r0, #1409286144\n"); |
| |
| COMPARE_A32(Sub(r0, r1, 0x4321), |
| "sub r0, r1, #33\n" |
| "sub r0, #17152\n"); |
| COMPARE_T32(Sub(r0, r1, 0x4321), |
| "sub r0, r1, #801\n" |
| "sub r0, #16384\n"); |
| COMPARE_BOTH(Sub(r0, r1, 0x432100), |
| "sub r0, r1, #8448\n" |
| "sub r0, #4390912\n"); |
| COMPARE_BOTH(Sub(r0, r1, 0x43000210), |
| "sub r0, r1, #528\n" |
| "sub r0, #1124073472\n"); |
| COMPARE_BOTH(Sub(r0, r1, 0x30c00210), |
| "sub r0, r1, #528\n" |
| "sub r0, #817889280\n"); |
| COMPARE_BOTH(Sub(r0, r1, 0x43000021), |
| "sub r0, r1, #33\n" |
| "sub r0, #1124073472\n"); |
| COMPARE_T32(Sub(r0, r1, 0x54321), |
| "sub r0, r1, #801\n" |
| "sub r0, #344064\n"); |
| COMPARE_T32(Sub(r0, r1, 0x54000321), |
| "sub r0, r1, #801\n" |
| "sub r0, #1409286144\n"); |
| |
| CLEANUP(); |
| } |
| |
| } // namespace aarch32 |
| } // namespace vixl |