| // Copyright 2015, 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 <sys/mman.h> |
| |
| #include <cfloat> |
| #include <cmath> |
| #include <cstdio> |
| #include <cstdlib> |
| #include <cstring> |
| |
| #include "test-runner.h" |
| #include "test-utils.h" |
| #include "aarch64/test-utils-aarch64.h" |
| |
| #include "aarch64/cpu-aarch64.h" |
| #include "aarch64/disasm-aarch64.h" |
| #include "aarch64/macro-assembler-aarch64.h" |
| #include "aarch64/simulator-aarch64.h" |
| #include "test-assembler-aarch64.h" |
| |
| namespace vixl { |
| namespace aarch64 { |
| |
| TEST(preshift_immediates) { |
| SETUP(); |
| |
| START(); |
| // Test operations involving immediates that could be generated using a |
| // pre-shifted encodable immediate followed by a post-shift applied to |
| // the arithmetic or logical operation. |
| |
| // Save sp. |
| __ Mov(x29, sp); |
| |
| // Set the registers to known values. |
| __ Mov(x0, 0x1000); |
| __ Mov(sp, 0x1004); |
| |
| // Arithmetic ops. |
| __ Add(x1, x0, 0x1f7de); |
| __ Add(w2, w0, 0xffffff1); |
| __ Adds(x3, x0, 0x18001); |
| __ Adds(w4, w0, 0xffffff1); |
| __ Sub(x5, x0, 0x1f7de); |
| __ Sub(w6, w0, 0xffffff1); |
| __ Subs(x7, x0, 0x18001); |
| __ Subs(w8, w0, 0xffffff1); |
| |
| // Logical ops. |
| __ And(x9, x0, 0x1f7de); |
| __ Orr(w10, w0, 0xffffff1); |
| __ Eor(x11, x0, 0x18001); |
| |
| // Ops using the stack pointer. |
| __ Add(sp, sp, 0x18001); |
| __ Mov(x12, sp); |
| __ Mov(sp, 0x1004); |
| |
| __ Add(sp, sp, 0x1f7de); |
| __ Mov(x13, sp); |
| __ Mov(sp, 0x1004); |
| |
| __ Adds(x14, sp, 0x1f7de); |
| |
| __ Orr(sp, x0, 0x1f7de); |
| __ Mov(x15, sp); |
| |
| // Restore sp. |
| __ Mov(sp, x29); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0x1000, x0); |
| ASSERT_EQUAL_64(0x207de, x1); |
| ASSERT_EQUAL_64(0x10000ff1, x2); |
| ASSERT_EQUAL_64(0x19001, x3); |
| ASSERT_EQUAL_64(0x10000ff1, x4); |
| ASSERT_EQUAL_64(0xfffffffffffe1822, x5); |
| ASSERT_EQUAL_64(0xf000100f, x6); |
| ASSERT_EQUAL_64(0xfffffffffffe8fff, x7); |
| ASSERT_EQUAL_64(0xf000100f, x8); |
| ASSERT_EQUAL_64(0x1000, x9); |
| ASSERT_EQUAL_64(0xffffff1, x10); |
| ASSERT_EQUAL_64(0x19001, x11); |
| ASSERT_EQUAL_64(0x19005, x12); |
| ASSERT_EQUAL_64(0x207e2, x13); |
| ASSERT_EQUAL_64(0x207e2, x14); |
| ASSERT_EQUAL_64(0x1f7de, x15); |
| } |
| } |
| |
| |
| TEST(stack_ops) { |
| SETUP(); |
| |
| START(); |
| // save sp. |
| __ Mov(x29, sp); |
| |
| // Set the sp to a known value. |
| __ Mov(sp, 0x1004); |
| __ Mov(x0, sp); |
| |
| // Add immediate to the sp, and move the result to a normal register. |
| __ Add(sp, sp, 0x50); |
| __ Mov(x1, sp); |
| |
| // Add extended to the sp, and move the result to a normal register. |
| __ Mov(x17, 0xfff); |
| __ Add(sp, sp, Operand(x17, SXTB)); |
| __ Mov(x2, sp); |
| |
| // Create an sp using a logical instruction, and move to normal register. |
| __ Orr(sp, xzr, 0x1fff); |
| __ Mov(x3, sp); |
| |
| // Write wsp using a logical instruction. |
| __ Orr(wsp, wzr, 0xfffffff8); |
| __ Mov(x4, sp); |
| |
| // Write sp, and read back wsp. |
| __ Orr(sp, xzr, 0xfffffff8); |
| __ Mov(w5, wsp); |
| |
| // Test writing into wsp in cases where the immediate isn't encodable. |
| VIXL_ASSERT(!Assembler::IsImmLogical(0x1234, kWRegSize)); |
| __ Orr(wsp, w5, 0x1234); |
| __ Mov(w6, wsp); |
| |
| // restore sp. |
| __ Mov(sp, x29); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0x1004, x0); |
| ASSERT_EQUAL_64(0x1054, x1); |
| ASSERT_EQUAL_64(0x1053, x2); |
| ASSERT_EQUAL_64(0x1fff, x3); |
| ASSERT_EQUAL_64(0xfffffff8, x4); |
| ASSERT_EQUAL_64(0xfffffff8, x5); |
| ASSERT_EQUAL_64(0xfffffffc, x6); |
| } |
| } |
| |
| |
| TEST(mvn) { |
| SETUP(); |
| |
| START(); |
| __ Mvn(w0, 0xfff); |
| __ Mvn(x1, 0xfff); |
| __ Mvn(w2, Operand(w0, LSL, 1)); |
| __ Mvn(x3, Operand(x1, LSL, 2)); |
| __ Mvn(w4, Operand(w0, LSR, 3)); |
| __ Mvn(x5, Operand(x1, LSR, 4)); |
| __ Mvn(w6, Operand(w0, ASR, 11)); |
| __ Mvn(x7, Operand(x1, ASR, 12)); |
| __ Mvn(w8, Operand(w0, ROR, 13)); |
| __ Mvn(x9, Operand(x1, ROR, 14)); |
| __ Mvn(w10, Operand(w2, UXTB)); |
| __ Mvn(x11, Operand(x2, SXTB, 1)); |
| __ Mvn(w12, Operand(w2, UXTH, 2)); |
| __ Mvn(x13, Operand(x2, SXTH, 3)); |
| __ Mvn(x14, Operand(w2, UXTW, 4)); |
| __ Mvn(x15, Operand(w2, SXTW, 4)); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0xfffff000, x0); |
| ASSERT_EQUAL_64(0xfffffffffffff000, x1); |
| ASSERT_EQUAL_64(0x00001fff, x2); |
| ASSERT_EQUAL_64(0x0000000000003fff, x3); |
| ASSERT_EQUAL_64(0xe00001ff, x4); |
| ASSERT_EQUAL_64(0xf0000000000000ff, x5); |
| ASSERT_EQUAL_64(0x00000001, x6); |
| ASSERT_EQUAL_64(0x0000000000000000, x7); |
| ASSERT_EQUAL_64(0x7ff80000, x8); |
| ASSERT_EQUAL_64(0x3ffc000000000000, x9); |
| ASSERT_EQUAL_64(0xffffff00, x10); |
| ASSERT_EQUAL_64(0x0000000000000001, x11); |
| ASSERT_EQUAL_64(0xffff8003, x12); |
| ASSERT_EQUAL_64(0xffffffffffff0007, x13); |
| ASSERT_EQUAL_64(0xfffffffffffe000f, x14); |
| ASSERT_EQUAL_64(0xfffffffffffe000f, x15); |
| } |
| } |
| |
| |
| TEST(mov_imm_w) { |
| SETUP(); |
| |
| START(); |
| __ Mov(w0, 0xffffffff); |
| __ Mov(w1, 0xffff1234); |
| __ Mov(w2, 0x1234ffff); |
| __ Mov(w3, 0x00000000); |
| __ Mov(w4, 0x00001234); |
| __ Mov(w5, 0x12340000); |
| __ Mov(w6, 0x12345678); |
| __ Mov(w7, (int32_t)0x80000000); |
| __ Mov(w8, (int32_t)0xffff0000); |
| __ Mov(w9, kWMinInt); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0xffffffff, x0); |
| ASSERT_EQUAL_64(0xffff1234, x1); |
| ASSERT_EQUAL_64(0x1234ffff, x2); |
| ASSERT_EQUAL_64(0x00000000, x3); |
| ASSERT_EQUAL_64(0x00001234, x4); |
| ASSERT_EQUAL_64(0x12340000, x5); |
| ASSERT_EQUAL_64(0x12345678, x6); |
| ASSERT_EQUAL_64(0x80000000, x7); |
| ASSERT_EQUAL_64(0xffff0000, x8); |
| ASSERT_EQUAL_32(kWMinInt, w9); |
| } |
| } |
| |
| |
| TEST(mov_imm_x) { |
| SETUP(); |
| |
| START(); |
| __ Mov(x0, 0xffffffffffffffff); |
| __ Mov(x1, 0xffffffffffff1234); |
| __ Mov(x2, 0xffffffff12345678); |
| __ Mov(x3, 0xffff1234ffff5678); |
| __ Mov(x4, 0x1234ffffffff5678); |
| __ Mov(x5, 0x1234ffff5678ffff); |
| __ Mov(x6, 0x12345678ffffffff); |
| __ Mov(x7, 0x1234ffffffffffff); |
| __ Mov(x8, 0x123456789abcffff); |
| __ Mov(x9, 0x12345678ffff9abc); |
| __ Mov(x10, 0x1234ffff56789abc); |
| __ Mov(x11, 0xffff123456789abc); |
| __ Mov(x12, 0x0000000000000000); |
| __ Mov(x13, 0x0000000000001234); |
| __ Mov(x14, 0x0000000012345678); |
| __ Mov(x15, 0x0000123400005678); |
| __ Mov(x18, 0x1234000000005678); |
| __ Mov(x19, 0x1234000056780000); |
| __ Mov(x20, 0x1234567800000000); |
| __ Mov(x21, 0x1234000000000000); |
| __ Mov(x22, 0x123456789abc0000); |
| __ Mov(x23, 0x1234567800009abc); |
| __ Mov(x24, 0x1234000056789abc); |
| __ Mov(x25, 0x0000123456789abc); |
| __ Mov(x26, 0x123456789abcdef0); |
| __ Mov(x27, 0xffff000000000001); |
| __ Mov(x28, 0x8000ffff00000000); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0xffffffffffff1234, x1); |
| ASSERT_EQUAL_64(0xffffffff12345678, x2); |
| ASSERT_EQUAL_64(0xffff1234ffff5678, x3); |
| ASSERT_EQUAL_64(0x1234ffffffff5678, x4); |
| ASSERT_EQUAL_64(0x1234ffff5678ffff, x5); |
| ASSERT_EQUAL_64(0x12345678ffffffff, x6); |
| ASSERT_EQUAL_64(0x1234ffffffffffff, x7); |
| ASSERT_EQUAL_64(0x123456789abcffff, x8); |
| ASSERT_EQUAL_64(0x12345678ffff9abc, x9); |
| ASSERT_EQUAL_64(0x1234ffff56789abc, x10); |
| ASSERT_EQUAL_64(0xffff123456789abc, x11); |
| ASSERT_EQUAL_64(0x0000000000000000, x12); |
| ASSERT_EQUAL_64(0x0000000000001234, x13); |
| ASSERT_EQUAL_64(0x0000000012345678, x14); |
| ASSERT_EQUAL_64(0x0000123400005678, x15); |
| ASSERT_EQUAL_64(0x1234000000005678, x18); |
| ASSERT_EQUAL_64(0x1234000056780000, x19); |
| ASSERT_EQUAL_64(0x1234567800000000, x20); |
| ASSERT_EQUAL_64(0x1234000000000000, x21); |
| ASSERT_EQUAL_64(0x123456789abc0000, x22); |
| ASSERT_EQUAL_64(0x1234567800009abc, x23); |
| ASSERT_EQUAL_64(0x1234000056789abc, x24); |
| ASSERT_EQUAL_64(0x0000123456789abc, x25); |
| ASSERT_EQUAL_64(0x123456789abcdef0, x26); |
| ASSERT_EQUAL_64(0xffff000000000001, x27); |
| ASSERT_EQUAL_64(0x8000ffff00000000, x28); |
| } |
| } |
| |
| |
| TEST(mov) { |
| SETUP(); |
| |
| START(); |
| __ Mov(x0, 0xffffffffffffffff); |
| __ Mov(x1, 0xffffffffffffffff); |
| __ Mov(x2, 0xffffffffffffffff); |
| __ Mov(x3, 0xffffffffffffffff); |
| |
| __ Mov(x0, 0x0123456789abcdef); |
| |
| { |
| ExactAssemblyScope scope(&masm, 3 * kInstructionSize); |
| __ movz(x1, UINT64_C(0xabcd) << 16); |
| __ movk(x2, UINT64_C(0xabcd) << 32); |
| __ movn(x3, UINT64_C(0xabcd) << 48); |
| } |
| |
| __ Mov(x4, 0x0123456789abcdef); |
| __ Mov(x5, x4); |
| |
| __ Mov(w6, -1); |
| |
| // Test that moves back to the same register have the desired effect. This |
| // is a no-op for X registers, and a truncation for W registers. |
| __ Mov(x7, 0x0123456789abcdef); |
| __ Mov(x7, x7); |
| __ Mov(x8, 0x0123456789abcdef); |
| __ Mov(w8, w8); |
| __ Mov(x9, 0x0123456789abcdef); |
| __ Mov(x9, Operand(x9)); |
| __ Mov(x10, 0x0123456789abcdef); |
| __ Mov(w10, Operand(w10)); |
| |
| __ Mov(w11, 0xfff); |
| __ Mov(x12, 0xfff); |
| __ Mov(w13, Operand(w11, LSL, 1)); |
| __ Mov(x14, Operand(x12, LSL, 2)); |
| __ Mov(w15, Operand(w11, LSR, 3)); |
| __ Mov(x18, Operand(x12, LSR, 4)); |
| __ Mov(w19, Operand(w11, ASR, 11)); |
| __ Mov(x20, Operand(x12, ASR, 12)); |
| __ Mov(w21, Operand(w11, ROR, 13)); |
| __ Mov(x22, Operand(x12, ROR, 14)); |
| __ Mov(w23, Operand(w13, UXTB)); |
| __ Mov(x24, Operand(x13, SXTB, 1)); |
| __ Mov(w25, Operand(w13, UXTH, 2)); |
| __ Mov(x26, Operand(x13, SXTH, 3)); |
| __ Mov(x27, Operand(w13, UXTW, 4)); |
| |
| __ Mov(x28, 0x0123456789abcdef); |
| __ Mov(w28, w28, kDiscardForSameWReg); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0x0123456789abcdef, x0); |
| ASSERT_EQUAL_64(0x00000000abcd0000, x1); |
| ASSERT_EQUAL_64(0xffffabcdffffffff, x2); |
| ASSERT_EQUAL_64(0x5432ffffffffffff, x3); |
| ASSERT_EQUAL_64(x4, x5); |
| ASSERT_EQUAL_32(-1, w6); |
| ASSERT_EQUAL_64(0x0123456789abcdef, x7); |
| ASSERT_EQUAL_32(0x89abcdef, w8); |
| ASSERT_EQUAL_64(0x0123456789abcdef, x9); |
| ASSERT_EQUAL_32(0x89abcdef, w10); |
| ASSERT_EQUAL_64(0x00000fff, x11); |
| ASSERT_EQUAL_64(0x0000000000000fff, x12); |
| ASSERT_EQUAL_64(0x00001ffe, x13); |
| ASSERT_EQUAL_64(0x0000000000003ffc, x14); |
| ASSERT_EQUAL_64(0x000001ff, x15); |
| ASSERT_EQUAL_64(0x00000000000000ff, x18); |
| ASSERT_EQUAL_64(0x00000001, x19); |
| ASSERT_EQUAL_64(0x0000000000000000, x20); |
| ASSERT_EQUAL_64(0x7ff80000, x21); |
| ASSERT_EQUAL_64(0x3ffc000000000000, x22); |
| ASSERT_EQUAL_64(0x000000fe, x23); |
| ASSERT_EQUAL_64(0xfffffffffffffffc, x24); |
| ASSERT_EQUAL_64(0x00007ff8, x25); |
| ASSERT_EQUAL_64(0x000000000000fff0, x26); |
| ASSERT_EQUAL_64(0x000000000001ffe0, x27); |
| ASSERT_EQUAL_64(0x0123456789abcdef, x28); |
| } |
| } |
| |
| |
| TEST(mov_negative) { |
| SETUP(); |
| |
| START(); |
| __ Mov(w11, 0xffffffff); |
| __ Mov(x12, 0xffffffffffffffff); |
| |
| __ Mov(w13, Operand(w11, LSL, 1)); |
| __ Mov(w14, Operand(w11, LSR, 1)); |
| __ Mov(w15, Operand(w11, ASR, 1)); |
| __ Mov(w18, Operand(w11, ROR, 1)); |
| __ Mov(w19, Operand(w11, UXTB, 1)); |
| __ Mov(w20, Operand(w11, SXTB, 1)); |
| __ Mov(w21, Operand(w11, UXTH, 1)); |
| __ Mov(w22, Operand(w11, SXTH, 1)); |
| |
| __ Mov(x23, Operand(x12, LSL, 1)); |
| __ Mov(x24, Operand(x12, LSR, 1)); |
| __ Mov(x25, Operand(x12, ASR, 1)); |
| __ Mov(x26, Operand(x12, ROR, 1)); |
| __ Mov(x27, Operand(x12, UXTH, 1)); |
| __ Mov(x28, Operand(x12, SXTH, 1)); |
| __ Mov(x29, Operand(x12, UXTW, 1)); |
| __ Mov(x30, Operand(x12, SXTW, 1)); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0xfffffffe, x13); |
| ASSERT_EQUAL_64(0x7fffffff, x14); |
| ASSERT_EQUAL_64(0xffffffff, x15); |
| ASSERT_EQUAL_64(0xffffffff, x18); |
| ASSERT_EQUAL_64(0x000001fe, x19); |
| ASSERT_EQUAL_64(0xfffffffe, x20); |
| ASSERT_EQUAL_64(0x0001fffe, x21); |
| ASSERT_EQUAL_64(0xfffffffe, x22); |
| |
| ASSERT_EQUAL_64(0xfffffffffffffffe, x23); |
| ASSERT_EQUAL_64(0x7fffffffffffffff, x24); |
| ASSERT_EQUAL_64(0xffffffffffffffff, x25); |
| ASSERT_EQUAL_64(0xffffffffffffffff, x26); |
| ASSERT_EQUAL_64(0x000000000001fffe, x27); |
| ASSERT_EQUAL_64(0xfffffffffffffffe, x28); |
| ASSERT_EQUAL_64(0x00000001fffffffe, x29); |
| ASSERT_EQUAL_64(0xfffffffffffffffe, x30); |
| } |
| } |
| |
| |
| TEST(orr) { |
| SETUP(); |
| |
| START(); |
| __ Mov(x0, 0xf0f0); |
| __ Mov(x1, 0xf00000ff); |
| |
| __ Orr(x2, x0, Operand(x1)); |
| __ Orr(w3, w0, Operand(w1, LSL, 28)); |
| __ Orr(x4, x0, Operand(x1, LSL, 32)); |
| __ Orr(x5, x0, Operand(x1, LSR, 4)); |
| __ Orr(w6, w0, Operand(w1, ASR, 4)); |
| __ Orr(x7, x0, Operand(x1, ASR, 4)); |
| __ Orr(w8, w0, Operand(w1, ROR, 12)); |
| __ Orr(x9, x0, Operand(x1, ROR, 12)); |
| __ Orr(w10, w0, 0xf); |
| __ Orr(x11, x0, 0xf0000000f0000000); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0x00000000f000f0ff, x2); |
| ASSERT_EQUAL_64(0xf000f0f0, x3); |
| ASSERT_EQUAL_64(0xf00000ff0000f0f0, x4); |
| ASSERT_EQUAL_64(0x000000000f00f0ff, x5); |
| ASSERT_EQUAL_64(0xff00f0ff, x6); |
| ASSERT_EQUAL_64(0x000000000f00f0ff, x7); |
| ASSERT_EQUAL_64(0x0ffff0f0, x8); |
| ASSERT_EQUAL_64(0x0ff00000000ff0f0, x9); |
| ASSERT_EQUAL_64(0x0000f0ff, x10); |
| ASSERT_EQUAL_64(0xf0000000f000f0f0, x11); |
| } |
| } |
| |
| |
| TEST(orr_extend) { |
| SETUP(); |
| |
| START(); |
| __ Mov(x0, 1); |
| __ Mov(x1, 0x8000000080008080); |
| __ Orr(w6, w0, Operand(w1, UXTB)); |
| __ Orr(x7, x0, Operand(x1, UXTH, 1)); |
| __ Orr(w8, w0, Operand(w1, UXTW, 2)); |
| __ Orr(x9, x0, Operand(x1, UXTX, 3)); |
| __ Orr(w10, w0, Operand(w1, SXTB)); |
| __ Orr(x11, x0, Operand(x1, SXTH, 1)); |
| __ Orr(x12, x0, Operand(x1, SXTW, 2)); |
| __ Orr(x13, x0, Operand(x1, SXTX, 3)); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0x00000081, x6); |
| ASSERT_EQUAL_64(0x0000000000010101, x7); |
| ASSERT_EQUAL_64(0x00020201, x8); |
| ASSERT_EQUAL_64(0x0000000400040401, x9); |
| ASSERT_EQUAL_64(0xffffff81, x10); |
| ASSERT_EQUAL_64(0xffffffffffff0101, x11); |
| ASSERT_EQUAL_64(0xfffffffe00020201, x12); |
| ASSERT_EQUAL_64(0x0000000400040401, x13); |
| } |
| } |
| |
| |
| TEST(bitwise_wide_imm) { |
| SETUP(); |
| |
| START(); |
| __ Mov(x0, 0); |
| __ Mov(x1, 0xf0f0f0f0f0f0f0f0); |
| |
| __ Orr(x10, x0, 0x1234567890abcdef); |
| __ Orr(w11, w1, 0x90abcdef); |
| |
| __ Orr(w12, w0, kWMinInt); |
| __ Eor(w13, w0, kWMinInt); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0, x0); |
| ASSERT_EQUAL_64(0xf0f0f0f0f0f0f0f0, x1); |
| ASSERT_EQUAL_64(0x1234567890abcdef, x10); |
| ASSERT_EQUAL_64(0x00000000f0fbfdff, x11); |
| ASSERT_EQUAL_32(kWMinInt, w12); |
| ASSERT_EQUAL_32(kWMinInt, w13); |
| } |
| } |
| |
| |
| TEST(orn) { |
| SETUP(); |
| |
| START(); |
| __ Mov(x0, 0xf0f0); |
| __ Mov(x1, 0xf00000ff); |
| |
| __ Orn(x2, x0, Operand(x1)); |
| __ Orn(w3, w0, Operand(w1, LSL, 4)); |
| __ Orn(x4, x0, Operand(x1, LSL, 4)); |
| __ Orn(x5, x0, Operand(x1, LSR, 1)); |
| __ Orn(w6, w0, Operand(w1, ASR, 1)); |
| __ Orn(x7, x0, Operand(x1, ASR, 1)); |
| __ Orn(w8, w0, Operand(w1, ROR, 16)); |
| __ Orn(x9, x0, Operand(x1, ROR, 16)); |
| __ Orn(w10, w0, 0x0000ffff); |
| __ Orn(x11, x0, 0x0000ffff0000ffff); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0xffffffff0ffffff0, x2); |
| ASSERT_EQUAL_64(0xfffff0ff, x3); |
| ASSERT_EQUAL_64(0xfffffff0fffff0ff, x4); |
| ASSERT_EQUAL_64(0xffffffff87fffff0, x5); |
| ASSERT_EQUAL_64(0x07fffff0, x6); |
| ASSERT_EQUAL_64(0xffffffff87fffff0, x7); |
| ASSERT_EQUAL_64(0xff00ffff, x8); |
| ASSERT_EQUAL_64(0xff00ffffffffffff, x9); |
| ASSERT_EQUAL_64(0xfffff0f0, x10); |
| ASSERT_EQUAL_64(0xffff0000fffff0f0, x11); |
| } |
| } |
| |
| |
| TEST(orn_extend) { |
| SETUP(); |
| |
| START(); |
| __ Mov(x0, 1); |
| __ Mov(x1, 0x8000000080008081); |
| __ Orn(w6, w0, Operand(w1, UXTB)); |
| __ Orn(x7, x0, Operand(x1, UXTH, 1)); |
| __ Orn(w8, w0, Operand(w1, UXTW, 2)); |
| __ Orn(x9, x0, Operand(x1, UXTX, 3)); |
| __ Orn(w10, w0, Operand(w1, SXTB)); |
| __ Orn(x11, x0, Operand(x1, SXTH, 1)); |
| __ Orn(x12, x0, Operand(x1, SXTW, 2)); |
| __ Orn(x13, x0, Operand(x1, SXTX, 3)); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0xffffff7f, x6); |
| ASSERT_EQUAL_64(0xfffffffffffefefd, x7); |
| ASSERT_EQUAL_64(0xfffdfdfb, x8); |
| ASSERT_EQUAL_64(0xfffffffbfffbfbf7, x9); |
| ASSERT_EQUAL_64(0x0000007f, x10); |
| ASSERT_EQUAL_64(0x000000000000fefd, x11); |
| ASSERT_EQUAL_64(0x00000001fffdfdfb, x12); |
| ASSERT_EQUAL_64(0xfffffffbfffbfbf7, x13); |
| } |
| } |
| |
| |
| TEST(and_) { |
| SETUP(); |
| |
| START(); |
| __ Mov(x0, 0xfff0); |
| __ Mov(x1, 0xf00000ff); |
| |
| __ And(x2, x0, Operand(x1)); |
| __ And(w3, w0, Operand(w1, LSL, 4)); |
| __ And(x4, x0, Operand(x1, LSL, 4)); |
| __ And(x5, x0, Operand(x1, LSR, 1)); |
| __ And(w6, w0, Operand(w1, ASR, 20)); |
| __ And(x7, x0, Operand(x1, ASR, 20)); |
| __ And(w8, w0, Operand(w1, ROR, 28)); |
| __ And(x9, x0, Operand(x1, ROR, 28)); |
| __ And(w10, w0, Operand(0xff00)); |
| __ And(x11, x0, Operand(0xff)); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0x000000f0, x2); |
| ASSERT_EQUAL_64(0x00000ff0, x3); |
| ASSERT_EQUAL_64(0x00000ff0, x4); |
| ASSERT_EQUAL_64(0x00000070, x5); |
| ASSERT_EQUAL_64(0x0000ff00, x6); |
| ASSERT_EQUAL_64(0x00000f00, x7); |
| ASSERT_EQUAL_64(0x00000ff0, x8); |
| ASSERT_EQUAL_64(0x00000000, x9); |
| ASSERT_EQUAL_64(0x0000ff00, x10); |
| ASSERT_EQUAL_64(0x000000f0, x11); |
| } |
| } |
| |
| |
| TEST(and_extend) { |
| SETUP(); |
| |
| START(); |
| __ Mov(x0, 0xffffffffffffffff); |
| __ Mov(x1, 0x8000000080008081); |
| __ And(w6, w0, Operand(w1, UXTB)); |
| __ And(x7, x0, Operand(x1, UXTH, 1)); |
| __ And(w8, w0, Operand(w1, UXTW, 2)); |
| __ And(x9, x0, Operand(x1, UXTX, 3)); |
| __ And(w10, w0, Operand(w1, SXTB)); |
| __ And(x11, x0, Operand(x1, SXTH, 1)); |
| __ And(x12, x0, Operand(x1, SXTW, 2)); |
| __ And(x13, x0, Operand(x1, SXTX, 3)); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0x00000081, x6); |
| ASSERT_EQUAL_64(0x0000000000010102, x7); |
| ASSERT_EQUAL_64(0x00020204, x8); |
| ASSERT_EQUAL_64(0x0000000400040408, x9); |
| ASSERT_EQUAL_64(0xffffff81, x10); |
| ASSERT_EQUAL_64(0xffffffffffff0102, x11); |
| ASSERT_EQUAL_64(0xfffffffe00020204, x12); |
| ASSERT_EQUAL_64(0x0000000400040408, x13); |
| } |
| } |
| |
| |
| TEST(ands) { |
| SETUP(); |
| |
| START(); |
| __ Mov(x1, 0xf00000ff); |
| __ Ands(w0, w1, Operand(w1)); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_NZCV(NFlag); |
| ASSERT_EQUAL_64(0xf00000ff, x0); |
| } |
| |
| START(); |
| __ Mov(x0, 0xfff0); |
| __ Mov(x1, 0xf00000ff); |
| __ Ands(w0, w0, Operand(w1, LSR, 4)); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_NZCV(ZFlag); |
| ASSERT_EQUAL_64(0x00000000, x0); |
| } |
| |
| START(); |
| __ Mov(x0, 0x8000000000000000); |
| __ Mov(x1, 0x00000001); |
| __ Ands(x0, x0, Operand(x1, ROR, 1)); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_NZCV(NFlag); |
| ASSERT_EQUAL_64(0x8000000000000000, x0); |
| } |
| |
| START(); |
| __ Mov(x0, 0xfff0); |
| __ Ands(w0, w0, Operand(0xf)); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_NZCV(ZFlag); |
| ASSERT_EQUAL_64(0x00000000, x0); |
| } |
| |
| START(); |
| __ Mov(x0, 0xff000000); |
| __ Ands(w0, w0, Operand(0x80000000)); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_NZCV(NFlag); |
| ASSERT_EQUAL_64(0x80000000, x0); |
| } |
| } |
| |
| |
| TEST(bic) { |
| SETUP(); |
| |
| START(); |
| __ Mov(x0, 0xfff0); |
| __ Mov(x1, 0xf00000ff); |
| |
| __ Bic(x2, x0, Operand(x1)); |
| __ Bic(w3, w0, Operand(w1, LSL, 4)); |
| __ Bic(x4, x0, Operand(x1, LSL, 4)); |
| __ Bic(x5, x0, Operand(x1, LSR, 1)); |
| __ Bic(w6, w0, Operand(w1, ASR, 20)); |
| __ Bic(x7, x0, Operand(x1, ASR, 20)); |
| __ Bic(w8, w0, Operand(w1, ROR, 28)); |
| __ Bic(x9, x0, Operand(x1, ROR, 24)); |
| __ Bic(x10, x0, Operand(0x1f)); |
| __ Bic(x11, x0, Operand(0x100)); |
| |
| // Test bic into sp when the constant cannot be encoded in the immediate |
| // field. |
| // Use x20 to preserve sp. We check for the result via x21 because the |
| // test infrastructure requires that sp be restored to its original value. |
| __ Mov(x20, sp); |
| __ Mov(x0, 0xffffff); |
| __ Bic(sp, x0, Operand(0xabcdef)); |
| __ Mov(x21, sp); |
| __ Mov(sp, x20); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0x0000ff00, x2); |
| ASSERT_EQUAL_64(0x0000f000, x3); |
| ASSERT_EQUAL_64(0x0000f000, x4); |
| ASSERT_EQUAL_64(0x0000ff80, x5); |
| ASSERT_EQUAL_64(0x000000f0, x6); |
| ASSERT_EQUAL_64(0x0000f0f0, x7); |
| ASSERT_EQUAL_64(0x0000f000, x8); |
| ASSERT_EQUAL_64(0x0000ff00, x9); |
| ASSERT_EQUAL_64(0x0000ffe0, x10); |
| ASSERT_EQUAL_64(0x0000fef0, x11); |
| |
| ASSERT_EQUAL_64(0x543210, x21); |
| } |
| } |
| |
| |
| TEST(bic_extend) { |
| SETUP(); |
| |
| START(); |
| __ Mov(x0, 0xffffffffffffffff); |
| __ Mov(x1, 0x8000000080008081); |
| __ Bic(w6, w0, Operand(w1, UXTB)); |
| __ Bic(x7, x0, Operand(x1, UXTH, 1)); |
| __ Bic(w8, w0, Operand(w1, UXTW, 2)); |
| __ Bic(x9, x0, Operand(x1, UXTX, 3)); |
| __ Bic(w10, w0, Operand(w1, SXTB)); |
| __ Bic(x11, x0, Operand(x1, SXTH, 1)); |
| __ Bic(x12, x0, Operand(x1, SXTW, 2)); |
| __ Bic(x13, x0, Operand(x1, SXTX, 3)); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0xffffff7e, x6); |
| ASSERT_EQUAL_64(0xfffffffffffefefd, x7); |
| ASSERT_EQUAL_64(0xfffdfdfb, x8); |
| ASSERT_EQUAL_64(0xfffffffbfffbfbf7, x9); |
| ASSERT_EQUAL_64(0x0000007e, x10); |
| ASSERT_EQUAL_64(0x000000000000fefd, x11); |
| ASSERT_EQUAL_64(0x00000001fffdfdfb, x12); |
| ASSERT_EQUAL_64(0xfffffffbfffbfbf7, x13); |
| } |
| } |
| |
| |
| TEST(bics) { |
| SETUP(); |
| |
| START(); |
| __ Mov(x1, 0xffff); |
| __ Bics(w0, w1, Operand(w1)); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_NZCV(ZFlag); |
| ASSERT_EQUAL_64(0x00000000, x0); |
| } |
| |
| START(); |
| __ Mov(x0, 0xffffffff); |
| __ Bics(w0, w0, Operand(w0, LSR, 1)); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_NZCV(NFlag); |
| ASSERT_EQUAL_64(0x80000000, x0); |
| } |
| |
| START(); |
| __ Mov(x0, 0x8000000000000000); |
| __ Mov(x1, 0x00000001); |
| __ Bics(x0, x0, Operand(x1, ROR, 1)); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_NZCV(ZFlag); |
| ASSERT_EQUAL_64(0x00000000, x0); |
| } |
| |
| START(); |
| __ Mov(x0, 0xffffffffffffffff); |
| __ Bics(x0, x0, 0x7fffffffffffffff); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_NZCV(NFlag); |
| ASSERT_EQUAL_64(0x8000000000000000, x0); |
| } |
| |
| START(); |
| __ Mov(w0, 0xffff0000); |
| __ Bics(w0, w0, 0xfffffff0); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_NZCV(ZFlag); |
| ASSERT_EQUAL_64(0x00000000, x0); |
| } |
| } |
| |
| |
| TEST(eor) { |
| SETUP(); |
| |
| START(); |
| __ Mov(x0, 0xfff0); |
| __ Mov(x1, 0xf00000ff); |
| |
| __ Eor(x2, x0, Operand(x1)); |
| __ Eor(w3, w0, Operand(w1, LSL, 4)); |
| __ Eor(x4, x0, Operand(x1, LSL, 4)); |
| __ Eor(x5, x0, Operand(x1, LSR, 1)); |
| __ Eor(w6, w0, Operand(w1, ASR, 20)); |
| __ Eor(x7, x0, Operand(x1, ASR, 20)); |
| __ Eor(w8, w0, Operand(w1, ROR, 28)); |
| __ Eor(x9, x0, Operand(x1, ROR, 28)); |
| __ Eor(w10, w0, 0xff00ff00); |
| __ Eor(x11, x0, 0xff00ff00ff00ff00); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0x00000000f000ff0f, x2); |
| ASSERT_EQUAL_64(0x0000f000, x3); |
| ASSERT_EQUAL_64(0x0000000f0000f000, x4); |
| ASSERT_EQUAL_64(0x000000007800ff8f, x5); |
| ASSERT_EQUAL_64(0xffff00f0, x6); |
| ASSERT_EQUAL_64(0x000000000000f0f0, x7); |
| ASSERT_EQUAL_64(0x0000f00f, x8); |
| ASSERT_EQUAL_64(0x00000ff00000ffff, x9); |
| ASSERT_EQUAL_64(0xff0000f0, x10); |
| ASSERT_EQUAL_64(0xff00ff00ff0000f0, x11); |
| } |
| } |
| |
| TEST(eor_extend) { |
| SETUP(); |
| |
| START(); |
| __ Mov(x0, 0x1111111111111111); |
| __ Mov(x1, 0x8000000080008081); |
| __ Eor(w6, w0, Operand(w1, UXTB)); |
| __ Eor(x7, x0, Operand(x1, UXTH, 1)); |
| __ Eor(w8, w0, Operand(w1, UXTW, 2)); |
| __ Eor(x9, x0, Operand(x1, UXTX, 3)); |
| __ Eor(w10, w0, Operand(w1, SXTB)); |
| __ Eor(x11, x0, Operand(x1, SXTH, 1)); |
| __ Eor(x12, x0, Operand(x1, SXTW, 2)); |
| __ Eor(x13, x0, Operand(x1, SXTX, 3)); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0x11111190, x6); |
| ASSERT_EQUAL_64(0x1111111111101013, x7); |
| ASSERT_EQUAL_64(0x11131315, x8); |
| ASSERT_EQUAL_64(0x1111111511151519, x9); |
| ASSERT_EQUAL_64(0xeeeeee90, x10); |
| ASSERT_EQUAL_64(0xeeeeeeeeeeee1013, x11); |
| ASSERT_EQUAL_64(0xeeeeeeef11131315, x12); |
| ASSERT_EQUAL_64(0x1111111511151519, x13); |
| } |
| } |
| |
| |
| TEST(eon) { |
| SETUP(); |
| |
| START(); |
| __ Mov(x0, 0xfff0); |
| __ Mov(x1, 0xf00000ff); |
| |
| __ Eon(x2, x0, Operand(x1)); |
| __ Eon(w3, w0, Operand(w1, LSL, 4)); |
| __ Eon(x4, x0, Operand(x1, LSL, 4)); |
| __ Eon(x5, x0, Operand(x1, LSR, 1)); |
| __ Eon(w6, w0, Operand(w1, ASR, 20)); |
| __ Eon(x7, x0, Operand(x1, ASR, 20)); |
| __ Eon(w8, w0, Operand(w1, ROR, 28)); |
| __ Eon(x9, x0, Operand(x1, ROR, 28)); |
| __ Eon(w10, w0, 0x03c003c0); |
| __ Eon(x11, x0, 0x0000100000001000); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0xffffffff0fff00f0, x2); |
| ASSERT_EQUAL_64(0xffff0fff, x3); |
| ASSERT_EQUAL_64(0xfffffff0ffff0fff, x4); |
| ASSERT_EQUAL_64(0xffffffff87ff0070, x5); |
| ASSERT_EQUAL_64(0x0000ff0f, x6); |
| ASSERT_EQUAL_64(0xffffffffffff0f0f, x7); |
| ASSERT_EQUAL_64(0xffff0ff0, x8); |
| ASSERT_EQUAL_64(0xfffff00fffff0000, x9); |
| ASSERT_EQUAL_64(0xfc3f03cf, x10); |
| ASSERT_EQUAL_64(0xffffefffffff100f, x11); |
| } |
| } |
| |
| |
| TEST(eon_extend) { |
| SETUP(); |
| |
| START(); |
| __ Mov(x0, 0x1111111111111111); |
| __ Mov(x1, 0x8000000080008081); |
| __ Eon(w6, w0, Operand(w1, UXTB)); |
| __ Eon(x7, x0, Operand(x1, UXTH, 1)); |
| __ Eon(w8, w0, Operand(w1, UXTW, 2)); |
| __ Eon(x9, x0, Operand(x1, UXTX, 3)); |
| __ Eon(w10, w0, Operand(w1, SXTB)); |
| __ Eon(x11, x0, Operand(x1, SXTH, 1)); |
| __ Eon(x12, x0, Operand(x1, SXTW, 2)); |
| __ Eon(x13, x0, Operand(x1, SXTX, 3)); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0xeeeeee6f, x6); |
| ASSERT_EQUAL_64(0xeeeeeeeeeeefefec, x7); |
| ASSERT_EQUAL_64(0xeeececea, x8); |
| ASSERT_EQUAL_64(0xeeeeeeeaeeeaeae6, x9); |
| ASSERT_EQUAL_64(0x1111116f, x10); |
| ASSERT_EQUAL_64(0x111111111111efec, x11); |
| ASSERT_EQUAL_64(0x11111110eeececea, x12); |
| ASSERT_EQUAL_64(0xeeeeeeeaeeeaeae6, x13); |
| } |
| } |
| |
| |
| TEST(mul) { |
| SETUP(); |
| |
| START(); |
| __ Mov(x25, 0); |
| __ Mov(x26, 1); |
| __ Mov(x18, 0xffffffff); |
| __ Mov(x19, 0xffffffffffffffff); |
| |
| __ Mul(w0, w25, w25); |
| __ Mul(w1, w25, w26); |
| __ Mul(w2, w26, w18); |
| __ Mul(w3, w18, w19); |
| __ Mul(x4, x25, x25); |
| __ Mul(x5, x26, x18); |
| __ Mul(x6, x18, x19); |
| __ Mul(x7, x19, x19); |
| __ Smull(x8, w26, w18); |
| __ Smull(x9, w18, w18); |
| __ Smull(x10, w19, w19); |
| __ Mneg(w11, w25, w25); |
| __ Mneg(w12, w25, w26); |
| __ Mneg(w13, w26, w18); |
| __ Mneg(w14, w18, w19); |
| __ Mneg(x20, x25, x25); |
| __ Mneg(x21, x26, x18); |
| __ Mneg(x22, x18, x19); |
| __ Mneg(x23, x19, x19); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0, x0); |
| ASSERT_EQUAL_64(0, x1); |
| ASSERT_EQUAL_64(0xffffffff, x2); |
| ASSERT_EQUAL_64(1, x3); |
| ASSERT_EQUAL_64(0, x4); |
| ASSERT_EQUAL_64(0xffffffff, x5); |
| ASSERT_EQUAL_64(0xffffffff00000001, x6); |
| ASSERT_EQUAL_64(1, x7); |
| ASSERT_EQUAL_64(0xffffffffffffffff, x8); |
| ASSERT_EQUAL_64(1, x9); |
| ASSERT_EQUAL_64(1, x10); |
| ASSERT_EQUAL_64(0, x11); |
| ASSERT_EQUAL_64(0, x12); |
| ASSERT_EQUAL_64(1, x13); |
| ASSERT_EQUAL_64(0xffffffff, x14); |
| ASSERT_EQUAL_64(0, x20); |
| ASSERT_EQUAL_64(0xffffffff00000001, x21); |
| ASSERT_EQUAL_64(0xffffffff, x22); |
| ASSERT_EQUAL_64(0xffffffffffffffff, x23); |
| } |
| } |
| |
| |
| static void SmullHelper(int64_t expected, int64_t a, int64_t b) { |
| SETUP(); |
| START(); |
| __ Mov(w0, a); |
| __ Mov(w1, b); |
| __ Smull(x2, w0, w1); |
| END(); |
| if (CAN_RUN()) { |
| RUN(); |
| ASSERT_EQUAL_64(expected, x2); |
| } |
| } |
| |
| |
| TEST(smull) { |
| SmullHelper(0, 0, 0); |
| SmullHelper(1, 1, 1); |
| SmullHelper(-1, -1, 1); |
| SmullHelper(1, -1, -1); |
| SmullHelper(0xffffffff80000000, 0x80000000, 1); |
| SmullHelper(0x0000000080000000, 0x00010000, 0x00008000); |
| } |
| |
| |
| TEST(madd) { |
| SETUP(); |
| |
| START(); |
| __ Mov(x16, 0); |
| __ Mov(x17, 1); |
| __ Mov(x18, 0xffffffff); |
| __ Mov(x19, 0xffffffffffffffff); |
| |
| __ Madd(w0, w16, w16, w16); |
| __ Madd(w1, w16, w16, w17); |
| __ Madd(w2, w16, w16, w18); |
| __ Madd(w3, w16, w16, w19); |
| __ Madd(w4, w16, w17, w17); |
| __ Madd(w5, w17, w17, w18); |
| __ Madd(w6, w17, w17, w19); |
| __ Madd(w7, w17, w18, w16); |
| __ Madd(w8, w17, w18, w18); |
| __ Madd(w9, w18, w18, w17); |
| __ Madd(w10, w18, w19, w18); |
| __ Madd(w11, w19, w19, w19); |
| |
| __ Madd(x12, x16, x16, x16); |
| __ Madd(x13, x16, x16, x17); |
| __ Madd(x14, x16, x16, x18); |
| __ Madd(x15, x16, x16, x19); |
| __ Madd(x20, x16, x17, x17); |
| __ Madd(x21, x17, x17, x18); |
| __ Madd(x22, x17, x17, x19); |
| __ Madd(x23, x17, x18, x16); |
| __ Madd(x24, x17, x18, x18); |
| __ Madd(x25, x18, x18, x17); |
| __ Madd(x26, x18, x19, x18); |
| __ Madd(x27, x19, x19, x19); |
| |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0, x0); |
| ASSERT_EQUAL_64(1, x1); |
| ASSERT_EQUAL_64(0xffffffff, x2); |
| ASSERT_EQUAL_64(0xffffffff, x3); |
| ASSERT_EQUAL_64(1, x4); |
| ASSERT_EQUAL_64(0, x5); |
| ASSERT_EQUAL_64(0, x6); |
| ASSERT_EQUAL_64(0xffffffff, x7); |
| ASSERT_EQUAL_64(0xfffffffe, x8); |
| ASSERT_EQUAL_64(2, x9); |
| ASSERT_EQUAL_64(0, x10); |
| ASSERT_EQUAL_64(0, x11); |
| |
| ASSERT_EQUAL_64(0, x12); |
| ASSERT_EQUAL_64(1, x13); |
| ASSERT_EQUAL_64(0x00000000ffffffff, x14); |
| ASSERT_EQUAL_64(0xffffffffffffffff, x15); |
| ASSERT_EQUAL_64(1, x20); |
| ASSERT_EQUAL_64(0x0000000100000000, x21); |
| ASSERT_EQUAL_64(0, x22); |
| ASSERT_EQUAL_64(0x00000000ffffffff, x23); |
| ASSERT_EQUAL_64(0x00000001fffffffe, x24); |
| ASSERT_EQUAL_64(0xfffffffe00000002, x25); |
| ASSERT_EQUAL_64(0, x26); |
| ASSERT_EQUAL_64(0, x27); |
| } |
| } |
| |
| |
| TEST(msub) { |
| SETUP(); |
| |
| START(); |
| __ Mov(x16, 0); |
| __ Mov(x17, 1); |
| __ Mov(x18, 0xffffffff); |
| __ Mov(x19, 0xffffffffffffffff); |
| |
| __ Msub(w0, w16, w16, w16); |
| __ Msub(w1, w16, w16, w17); |
| __ Msub(w2, w16, w16, w18); |
| __ Msub(w3, w16, w16, w19); |
| __ Msub(w4, w16, w17, w17); |
| __ Msub(w5, w17, w17, w18); |
| __ Msub(w6, w17, w17, w19); |
| __ Msub(w7, w17, w18, w16); |
| __ Msub(w8, w17, w18, w18); |
| __ Msub(w9, w18, w18, w17); |
| __ Msub(w10, w18, w19, w18); |
| __ Msub(w11, w19, w19, w19); |
| |
| __ Msub(x12, x16, x16, x16); |
| __ Msub(x13, x16, x16, x17); |
| __ Msub(x14, x16, x16, x18); |
| __ Msub(x15, x16, x16, x19); |
| __ Msub(x20, x16, x17, x17); |
| __ Msub(x21, x17, x17, x18); |
| __ Msub(x22, x17, x17, x19); |
| __ Msub(x23, x17, x18, x16); |
| __ Msub(x24, x17, x18, x18); |
| __ Msub(x25, x18, x18, x17); |
| __ Msub(x26, x18, x19, x18); |
| __ Msub(x27, x19, x19, x19); |
| |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0, x0); |
| ASSERT_EQUAL_64(1, x1); |
| ASSERT_EQUAL_64(0xffffffff, x2); |
| ASSERT_EQUAL_64(0xffffffff, x3); |
| ASSERT_EQUAL_64(1, x4); |
| ASSERT_EQUAL_64(0xfffffffe, x5); |
| ASSERT_EQUAL_64(0xfffffffe, x6); |
| ASSERT_EQUAL_64(1, x7); |
| ASSERT_EQUAL_64(0, x8); |
| ASSERT_EQUAL_64(0, x9); |
| ASSERT_EQUAL_64(0xfffffffe, x10); |
| ASSERT_EQUAL_64(0xfffffffe, x11); |
| |
| ASSERT_EQUAL_64(0, x12); |
| ASSERT_EQUAL_64(1, x13); |
| ASSERT_EQUAL_64(0x00000000ffffffff, x14); |
| ASSERT_EQUAL_64(0xffffffffffffffff, x15); |
| ASSERT_EQUAL_64(1, x20); |
| ASSERT_EQUAL_64(0x00000000fffffffe, x21); |
| ASSERT_EQUAL_64(0xfffffffffffffffe, x22); |
| ASSERT_EQUAL_64(0xffffffff00000001, x23); |
| ASSERT_EQUAL_64(0, x24); |
| ASSERT_EQUAL_64(0x0000000200000000, x25); |
| ASSERT_EQUAL_64(0x00000001fffffffe, x26); |
| ASSERT_EQUAL_64(0xfffffffffffffffe, x27); |
| } |
| } |
| |
| |
| TEST(smulh) { |
| SETUP(); |
| |
| START(); |
| __ Mov(x20, 0); |
| __ Mov(x21, 1); |
| __ Mov(x22, 0x0000000100000000); |
| __ Mov(x23, 0x0000000012345678); |
| __ Mov(x24, 0x0123456789abcdef); |
| __ Mov(x25, 0x0000000200000000); |
| __ Mov(x26, 0x8000000000000000); |
| __ Mov(x27, 0xffffffffffffffff); |
| __ Mov(x28, 0x5555555555555555); |
| __ Mov(x29, 0xaaaaaaaaaaaaaaaa); |
| |
| __ Smulh(x0, x20, x24); |
| __ Smulh(x1, x21, x24); |
| __ Smulh(x2, x22, x23); |
| __ Smulh(x3, x22, x24); |
| __ Smulh(x4, x24, x25); |
| __ Smulh(x5, x23, x27); |
| __ Smulh(x6, x26, x26); |
| __ Smulh(x7, x26, x27); |
| __ Smulh(x8, x27, x27); |
| __ Smulh(x9, x28, x28); |
| __ Smulh(x10, x28, x29); |
| __ Smulh(x11, x29, x29); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0, x0); |
| ASSERT_EQUAL_64(0, x1); |
| ASSERT_EQUAL_64(0, x2); |
| ASSERT_EQUAL_64(0x0000000001234567, x3); |
| ASSERT_EQUAL_64(0x0000000002468acf, x4); |
| ASSERT_EQUAL_64(0xffffffffffffffff, x5); |
| ASSERT_EQUAL_64(0x4000000000000000, x6); |
| ASSERT_EQUAL_64(0, x7); |
| ASSERT_EQUAL_64(0, x8); |
| ASSERT_EQUAL_64(0x1c71c71c71c71c71, x9); |
| ASSERT_EQUAL_64(0xe38e38e38e38e38e, x10); |
| ASSERT_EQUAL_64(0x1c71c71c71c71c72, x11); |
| } |
| } |
| |
| |
| TEST(umulh) { |
| SETUP(); |
| |
| START(); |
| __ Mov(x20, 0); |
| __ Mov(x21, 1); |
| __ Mov(x22, 0x0000000100000000); |
| __ Mov(x23, 0x0000000012345678); |
| __ Mov(x24, 0x0123456789abcdef); |
| __ Mov(x25, 0x0000000200000000); |
| __ Mov(x26, 0x8000000000000000); |
| __ Mov(x27, 0xffffffffffffffff); |
| __ Mov(x28, 0x5555555555555555); |
| __ Mov(x29, 0xaaaaaaaaaaaaaaaa); |
| |
| __ Umulh(x0, x20, x24); |
| __ Umulh(x1, x21, x24); |
| __ Umulh(x2, x22, x23); |
| __ Umulh(x3, x22, x24); |
| __ Umulh(x4, x24, x25); |
| __ Umulh(x5, x23, x27); |
| __ Umulh(x6, x26, x26); |
| __ Umulh(x7, x26, x27); |
| __ Umulh(x8, x27, x27); |
| __ Umulh(x9, x28, x28); |
| __ Umulh(x10, x28, x29); |
| __ Umulh(x11, x29, x29); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0, x0); |
| ASSERT_EQUAL_64(0, x1); |
| ASSERT_EQUAL_64(0, x2); |
| ASSERT_EQUAL_64(0x0000000001234567, x3); |
| ASSERT_EQUAL_64(0x0000000002468acf, x4); |
| ASSERT_EQUAL_64(0x0000000012345677, x5); |
| ASSERT_EQUAL_64(0x4000000000000000, x6); |
| ASSERT_EQUAL_64(0x7fffffffffffffff, x7); |
| ASSERT_EQUAL_64(0xfffffffffffffffe, x8); |
| ASSERT_EQUAL_64(0x1c71c71c71c71c71, x9); |
| ASSERT_EQUAL_64(0x38e38e38e38e38e3, x10); |
| ASSERT_EQUAL_64(0x71c71c71c71c71c6, x11); |
| } |
| } |
| |
| |
| TEST(smaddl_umaddl_umull) { |
| SETUP(); |
| |
| START(); |
| __ Mov(x17, 1); |
| __ Mov(x18, 0x00000000ffffffff); |
| __ Mov(x19, 0xffffffffffffffff); |
| __ Mov(x20, 4); |
| __ Mov(x21, 0x0000000200000000); |
| |
| __ Smaddl(x9, w17, w18, x20); |
| __ Smaddl(x10, w18, w18, x20); |
| __ Smaddl(x11, w19, w19, x20); |
| __ Smaddl(x12, w19, w19, x21); |
| __ Umaddl(x13, w17, w18, x20); |
| __ Umaddl(x14, w18, w18, x20); |
| __ Umaddl(x15, w19, w19, x20); |
| __ Umaddl(x22, w19, w19, x21); |
| __ Umull(x24, w19, w19); |
| __ Umull(x25, w17, w18); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(3, x9); |
| ASSERT_EQUAL_64(5, x10); |
| ASSERT_EQUAL_64(5, x11); |
| ASSERT_EQUAL_64(0x0000000200000001, x12); |
| ASSERT_EQUAL_64(0x0000000100000003, x13); |
| ASSERT_EQUAL_64(0xfffffffe00000005, x14); |
| ASSERT_EQUAL_64(0xfffffffe00000005, x15); |
| ASSERT_EQUAL_64(1, x22); |
| ASSERT_EQUAL_64(0xfffffffe00000001, x24); |
| ASSERT_EQUAL_64(0x00000000ffffffff, x25); |
| } |
| } |
| |
| |
| TEST(smsubl_umsubl) { |
| SETUP(); |
| |
| START(); |
| __ Mov(x17, 1); |
| __ Mov(x18, 0x00000000ffffffff); |
| __ Mov(x19, 0xffffffffffffffff); |
| __ Mov(x20, 4); |
| __ Mov(x21, 0x0000000200000000); |
| |
| __ Smsubl(x9, w17, w18, x20); |
| __ Smsubl(x10, w18, w18, x20); |
| __ Smsubl(x11, w19, w19, x20); |
| __ Smsubl(x12, w19, w19, x21); |
| __ Umsubl(x13, w17, w18, x20); |
| __ Umsubl(x14, w18, w18, x20); |
| __ Umsubl(x15, w19, w19, x20); |
| __ Umsubl(x22, w19, w19, x21); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(5, x9); |
| ASSERT_EQUAL_64(3, x10); |
| ASSERT_EQUAL_64(3, x11); |
| ASSERT_EQUAL_64(0x00000001ffffffff, x12); |
| ASSERT_EQUAL_64(0xffffffff00000005, x13); |
| ASSERT_EQUAL_64(0x0000000200000003, x14); |
| ASSERT_EQUAL_64(0x0000000200000003, x15); |
| ASSERT_EQUAL_64(0x00000003ffffffff, x22); |
| } |
| } |
| |
| |
| TEST(div) { |
| SETUP(); |
| |
| START(); |
| __ Mov(x16, 1); |
| __ Mov(x17, 0xffffffff); |
| __ Mov(x18, 0xffffffffffffffff); |
| __ Mov(x19, 0x80000000); |
| __ Mov(x20, 0x8000000000000000); |
| __ Mov(x21, 2); |
| |
| __ Udiv(w0, w16, w16); |
| __ Udiv(w1, w17, w16); |
| __ Sdiv(w2, w16, w16); |
| __ Sdiv(w3, w16, w17); |
| __ Sdiv(w4, w17, w18); |
| |
| __ Udiv(x5, x16, x16); |
| __ Udiv(x6, x17, x18); |
| __ Sdiv(x7, x16, x16); |
| __ Sdiv(x8, x16, x17); |
| __ Sdiv(x9, x17, x18); |
| |
| __ Udiv(w10, w19, w21); |
| __ Sdiv(w11, w19, w21); |
| __ Udiv(x12, x19, x21); |
| __ Sdiv(x13, x19, x21); |
| __ Udiv(x14, x20, x21); |
| __ Sdiv(x15, x20, x21); |
| |
| __ Udiv(w22, w19, w17); |
| __ Sdiv(w23, w19, w17); |
| __ Udiv(x24, x20, x18); |
| __ Sdiv(x25, x20, x18); |
| |
| __ Udiv(x26, x16, x21); |
| __ Sdiv(x27, x16, x21); |
| __ Udiv(x28, x18, x21); |
| __ Sdiv(x29, x18, x21); |
| |
| __ Mov(x17, 0); |
| __ Udiv(w18, w16, w17); |
| __ Sdiv(w19, w16, w17); |
| __ Udiv(x20, x16, x17); |
| __ Sdiv(x21, x16, x17); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(1, x0); |
| ASSERT_EQUAL_64(0xffffffff, x1); |
| ASSERT_EQUAL_64(1, x2); |
| ASSERT_EQUAL_64(0xffffffff, x3); |
| ASSERT_EQUAL_64(1, x4); |
| ASSERT_EQUAL_64(1, x5); |
| ASSERT_EQUAL_64(0, x6); |
| ASSERT_EQUAL_64(1, x7); |
| ASSERT_EQUAL_64(0, x8); |
| ASSERT_EQUAL_64(0xffffffff00000001, x9); |
| ASSERT_EQUAL_64(0x40000000, x10); |
| ASSERT_EQUAL_64(0xc0000000, x11); |
| ASSERT_EQUAL_64(0x0000000040000000, x12); |
| ASSERT_EQUAL_64(0x0000000040000000, x13); |
| ASSERT_EQUAL_64(0x4000000000000000, x14); |
| ASSERT_EQUAL_64(0xc000000000000000, x15); |
| ASSERT_EQUAL_64(0, x22); |
| ASSERT_EQUAL_64(0x80000000, x23); |
| ASSERT_EQUAL_64(0, x24); |
| ASSERT_EQUAL_64(0x8000000000000000, x25); |
| ASSERT_EQUAL_64(0, x26); |
| ASSERT_EQUAL_64(0, x27); |
| ASSERT_EQUAL_64(0x7fffffffffffffff, x28); |
| ASSERT_EQUAL_64(0, x29); |
| ASSERT_EQUAL_64(0, x18); |
| ASSERT_EQUAL_64(0, x19); |
| ASSERT_EQUAL_64(0, x20); |
| ASSERT_EQUAL_64(0, x21); |
| } |
| } |
| |
| |
| TEST(rbit_rev) { |
| SETUP(); |
| |
| START(); |
| __ Mov(x24, 0xfedcba9876543210); |
| __ Rbit(w0, w24); |
| __ Rbit(x1, x24); |
| __ Rev16(w2, w24); |
| __ Rev16(x3, x24); |
| __ Rev(w4, w24); |
| __ Rev32(x5, x24); |
| __ Rev64(x6, x24); |
| __ Rev(x7, x24); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0x084c2a6e, x0); |
| ASSERT_EQUAL_64(0x084c2a6e195d3b7f, x1); |
| ASSERT_EQUAL_64(0x54761032, x2); |
| ASSERT_EQUAL_64(0xdcfe98ba54761032, x3); |
| ASSERT_EQUAL_64(0x10325476, x4); |
| ASSERT_EQUAL_64(0x98badcfe10325476, x5); |
| ASSERT_EQUAL_64(0x1032547698badcfe, x6); |
| ASSERT_EQUAL_64(0x1032547698badcfe, x7); |
| } |
| } |
| |
| typedef void (MacroAssembler::*TestBranchSignature)(const Register& rt, |
| unsigned bit_pos, |
| Label* label); |
| |
| static void TbzRangePoolLimitHelper(TestBranchSignature test_branch) { |
| const int kTbzRange = 32768; |
| const int kNumLdrLiteral = kTbzRange / 4; |
| const int fuzzRange = 2; |
| for (int n = kNumLdrLiteral - fuzzRange; n <= kNumLdrLiteral + fuzzRange; |
| ++n) { |
| for (int margin = -32; margin < 32; margin += 4) { |
| SETUP(); |
| |
| START(); |
| |
| // Emit 32KB of literals (equal to the range of TBZ). |
| for (int i = 0; i < n; ++i) { |
| __ Ldr(w0, 0x12345678); |
| } |
| |
| const int kLiteralMargin = 128 * KBytes; |
| |
| // Emit enough NOPs to be just about to emit the literal pool. |
| ptrdiff_t end = |
| masm.GetCursorOffset() + (kLiteralMargin - n * 4 + margin); |
| while (masm.GetCursorOffset() < end) { |
| __ Nop(); |
| } |
| |
| // Add a TBZ instruction. |
| Label label; |
| |
| (masm.*test_branch)(x0, 2, &label); |
| |
| // Add enough NOPs to surpass its range, to make sure we can encode the |
| // veneer. |
| end = masm.GetCursorOffset() + (kTbzRange - 4); |
| { |
| ExactAssemblyScope scope(&masm, |
| kTbzRange, |
| ExactAssemblyScope::kMaximumSize); |
| while (masm.GetCursorOffset() < end) __ nop(); |
| } |
| |
| // Finally, bind the label. |
| __ Bind(&label); |
| |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| } |
| } |
| } |
| } |
| |
| TEST(test_branch_limits_literal_pool_size_tbz) { |
| TbzRangePoolLimitHelper(&MacroAssembler::Tbz); |
| } |
| |
| TEST(test_branch_limits_literal_pool_size_tbnz) { |
| TbzRangePoolLimitHelper(&MacroAssembler::Tbnz); |
| } |
| |
| TEST(clz_cls) { |
| SETUP(); |
| |
| START(); |
| __ Mov(x24, 0x0008000000800000); |
| __ Mov(x25, 0xff800000fff80000); |
| __ Mov(x26, 0); |
| __ Clz(w0, w24); |
| __ Clz(x1, x24); |
| __ Clz(w2, w25); |
| __ Clz(x3, x25); |
| __ Clz(w4, w26); |
| __ Clz(x5, x26); |
| __ Cls(w6, w24); |
| __ Cls(x7, x24); |
| __ Cls(w8, w25); |
| __ Cls(x9, x25); |
| __ Cls(w10, w26); |
| __ Cls(x11, x26); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(8, x0); |
| ASSERT_EQUAL_64(12, x1); |
| ASSERT_EQUAL_64(0, x2); |
| ASSERT_EQUAL_64(0, x3); |
| ASSERT_EQUAL_64(32, x4); |
| ASSERT_EQUAL_64(64, x5); |
| ASSERT_EQUAL_64(7, x6); |
| ASSERT_EQUAL_64(11, x7); |
| ASSERT_EQUAL_64(12, x8); |
| ASSERT_EQUAL_64(8, x9); |
| ASSERT_EQUAL_64(31, x10); |
| ASSERT_EQUAL_64(63, x11); |
| } |
| } |
| |
| |
| TEST(pacia_pacib_autia_autib) { |
| SETUP_WITH_FEATURES(CPUFeatures::kPAuth); |
| |
| START(); |
| |
| Register pointer = x24; |
| Register modifier = x25; |
| |
| __ Mov(pointer, 0x0000000012345678); |
| __ Mov(modifier, 0x477d469dec0b8760); |
| |
| // Generate PACs using keys A and B. |
| __ Mov(x0, pointer); |
| __ Pacia(x0, modifier); |
| |
| __ Mov(x1, pointer); |
| __ Pacib(x1, modifier); |
| |
| // Authenticate the pointers above. |
| __ Mov(x2, x0); |
| __ Autia(x2, modifier); |
| |
| __ Mov(x3, x1); |
| __ Autib(x3, modifier); |
| |
| // Attempt to authenticate incorrect pointers. |
| __ Mov(x4, x1); |
| __ Autia(x4, modifier); |
| |
| __ Mov(x5, x0); |
| __ Autib(x5, modifier); |
| |
| // Mask out just the PAC code bits. |
| // TODO: use Simulator::CalculatePACMask in a nice way. |
| __ And(x0, x0, 0x007f000000000000); |
| __ And(x1, x1, 0x007f000000000000); |
| |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| // Check PAC codes have been generated and aren't equal. |
| // NOTE: with a different ComputePAC implementation, there may be a |
| // collision. |
| ASSERT_NOT_EQUAL_64(0, x0); |
| ASSERT_NOT_EQUAL_64(0, x1); |
| ASSERT_NOT_EQUAL_64(x0, x1); |
| |
| // Pointers correctly authenticated. |
| ASSERT_EQUAL_64(pointer, x2); |
| ASSERT_EQUAL_64(pointer, x3); |
| |
| // Pointers corrupted after failing to authenticate. |
| ASSERT_EQUAL_64(0x0020000012345678, x4); |
| ASSERT_EQUAL_64(0x0040000012345678, x5); |
| } |
| } |
| |
| |
| TEST(paciza_pacizb_autiza_autizb) { |
| SETUP_WITH_FEATURES(CPUFeatures::kPAuth); |
| |
| START(); |
| |
| Register pointer = x24; |
| |
| __ Mov(pointer, 0x0000000012345678); |
| |
| // Generate PACs using keys A and B. |
| __ Mov(x0, pointer); |
| __ Paciza(x0); |
| |
| __ Mov(x1, pointer); |
| __ Pacizb(x1); |
| |
| // Authenticate the pointers above. |
| __ Mov(x2, x0); |
| __ Autiza(x2); |
| |
| __ Mov(x3, x1); |
| __ Autizb(x3); |
| |
| // Attempt to authenticate incorrect pointers. |
| __ Mov(x4, x1); |
| __ Autiza(x4); |
| |
| __ Mov(x5, x0); |
| __ Autizb(x5); |
| |
| // Mask out just the PAC code bits. |
| // TODO: use Simulator::CalculatePACMask in a nice way. |
| __ And(x0, x0, 0x007f000000000000); |
| __ And(x1, x1, 0x007f000000000000); |
| |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| // Check PAC codes have been generated and aren't equal. |
| // NOTE: with a different ComputePAC implementation, there may be a |
| // collision. |
| ASSERT_NOT_EQUAL_64(0, x0); |
| ASSERT_NOT_EQUAL_64(0, x1); |
| ASSERT_NOT_EQUAL_64(x0, x1); |
| |
| // Pointers correctly authenticated. |
| ASSERT_EQUAL_64(pointer, x2); |
| ASSERT_EQUAL_64(pointer, x3); |
| |
| // Pointers corrupted after failing to authenticate. |
| ASSERT_EQUAL_64(0x0020000012345678, x4); |
| ASSERT_EQUAL_64(0x0040000012345678, x5); |
| } |
| } |
| |
| |
| TEST(pacda_pacdb_autda_autdb) { |
| SETUP_WITH_FEATURES(CPUFeatures::kPAuth); |
| |
| START(); |
| |
| Register pointer = x24; |
| Register modifier = x25; |
| |
| __ Mov(pointer, 0x0000000012345678); |
| __ Mov(modifier, 0x477d469dec0b8760); |
| |
| // Generate PACs using keys A and B. |
| __ Mov(x0, pointer); |
| __ Pacda(x0, modifier); |
| |
| __ Mov(x1, pointer); |
| __ Pacdb(x1, modifier); |
| |
| // Authenticate the pointers above. |
| __ Mov(x2, x0); |
| __ Autda(x2, modifier); |
| |
| __ Mov(x3, x1); |
| __ Autdb(x3, modifier); |
| |
| // Attempt to authenticate incorrect pointers. |
| __ Mov(x4, x1); |
| __ Autda(x4, modifier); |
| |
| __ Mov(x5, x0); |
| __ Autdb(x5, modifier); |
| |
| // Mask out just the PAC code bits. |
| // TODO: use Simulator::CalculatePACMask in a nice way. |
| __ And(x0, x0, 0x007f000000000000); |
| __ And(x1, x1, 0x007f000000000000); |
| |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| // Check PAC codes have been generated and aren't equal. |
| // NOTE: with a different ComputePAC implementation, there may be a |
| // collision. |
| ASSERT_NOT_EQUAL_64(0, x0); |
| ASSERT_NOT_EQUAL_64(0, x1); |
| ASSERT_NOT_EQUAL_64(x0, x1); |
| |
| // Pointers correctly authenticated. |
| ASSERT_EQUAL_64(pointer, x2); |
| ASSERT_EQUAL_64(pointer, x3); |
| |
| // Pointers corrupted after failing to authenticate. |
| ASSERT_EQUAL_64(0x0020000012345678, x4); |
| ASSERT_EQUAL_64(0x0040000012345678, x5); |
| } |
| } |
| |
| |
| TEST(pacdza_pacdzb_autdza_autdzb) { |
| SETUP_WITH_FEATURES(CPUFeatures::kPAuth); |
| |
| START(); |
| |
| Register pointer = x24; |
| |
| __ Mov(pointer, 0x0000000012345678); |
| |
| // Generate PACs using keys A and B. |
| __ Mov(x0, pointer); |
| __ Pacdza(x0); |
| |
| __ Mov(x1, pointer); |
| __ Pacdzb(x1); |
| |
| // Authenticate the pointers above. |
| __ Mov(x2, x0); |
| __ Autdza(x2); |
| |
| __ Mov(x3, x1); |
| __ Autdzb(x3); |
| |
| // Attempt to authenticate incorrect pointers. |
| __ Mov(x4, x1); |
| __ Autdza(x4); |
| |
| __ Mov(x5, x0); |
| __ Autdzb(x5); |
| |
| // Mask out just the PAC code bits. |
| // TODO: use Simulator::CalculatePACMask in a nice way. |
| __ And(x0, x0, 0x007f000000000000); |
| __ And(x1, x1, 0x007f000000000000); |
| |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| // Check PAC codes have been generated and aren't equal. |
| // NOTE: with a different ComputePAC implementation, there may be a |
| // collision. |
| ASSERT_NOT_EQUAL_64(0, x0); |
| ASSERT_NOT_EQUAL_64(0, x1); |
| ASSERT_NOT_EQUAL_64(x0, x1); |
| |
| // Pointers correctly authenticated. |
| ASSERT_EQUAL_64(pointer, x2); |
| ASSERT_EQUAL_64(pointer, x3); |
| |
| // Pointers corrupted after failing to authenticate. |
| ASSERT_EQUAL_64(0x0020000012345678, x4); |
| ASSERT_EQUAL_64(0x0040000012345678, x5); |
| } |
| } |
| |
| |
| TEST(pacga_xpaci_xpacd) { |
| SETUP_WITH_FEATURES(CPUFeatures::kPAuth, CPUFeatures::kPAuthGeneric); |
| |
| START(); |
| |
| Register pointer = x24; |
| Register modifier = x25; |
| |
| __ Mov(pointer, 0x0000000012345678); |
| __ Mov(modifier, 0x477d469dec0b8760); |
| |
| // Generate generic PAC. |
| __ Pacga(x0, pointer, modifier); |
| |
| // Generate PACs using key A. |
| __ Mov(x1, pointer); |
| __ Mov(x2, pointer); |
| __ Pacia(x1, modifier); |
| __ Pacda(x2, modifier); |
| |
| // Strip PACs. |
| __ Mov(x3, x1); |
| __ Mov(x4, x2); |
| __ Xpaci(x3); |
| __ Xpacd(x4); |
| |
| // Mask out just the PAC code bits. |
| // TODO: use Simulator::CalculatePACMask in a nice way. |
| __ And(x0, x0, 0xffffffff00000000); |
| __ And(x1, x1, 0x007f000000000000); |
| __ And(x2, x2, 0x007f000000000000); |
| |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| |
| // Check PAC codes have been generated and aren't equal. |
| // NOTE: with a different ComputePAC implementation, there may be a |
| // collision. |
| ASSERT_NOT_EQUAL_64(0, x0); |
| |
| ASSERT_NOT_EQUAL_64(0, x1); |
| ASSERT_NOT_EQUAL_64(0, x2); |
| ASSERT_NOT_EQUAL_64(x1, x2); |
| |
| ASSERT_EQUAL_64(pointer, x3); |
| ASSERT_EQUAL_64(pointer, x4); |
| } |
| } |
| |
| |
| TEST(label) { |
| SETUP(); |
| |
| Label label_1, label_2, label_3, label_4; |
| |
| START(); |
| __ Mov(x0, 0x1); |
| __ Mov(x1, 0x0); |
| __ Mov(x22, lr); // Save lr. |
| |
| __ B(&label_1); |
| __ B(&label_1); |
| __ B(&label_1); // Multiple branches to the same label. |
| __ Mov(x0, 0x0); |
| __ Bind(&label_2); |
| __ B(&label_3); // Forward branch. |
| __ Mov(x0, 0x0); |
| __ Bind(&label_1); |
| __ B(&label_2); // Backward branch. |
| __ Mov(x0, 0x0); |
| __ Bind(&label_3); |
| __ Bl(&label_4); |
| END(); |
| |
| __ Bind(&label_4); |
| __ Mov(x1, 0x1); |
| __ Mov(lr, x22); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0x1, x0); |
| ASSERT_EQUAL_64(0x1, x1); |
| } |
| } |
| |
| |
| TEST(label_2) { |
| SETUP(); |
| |
| Label label_1, label_2, label_3; |
| Label first_jump_to_3; |
| |
| START(); |
| __ Mov(x0, 0x0); |
| |
| __ B(&label_1); |
| ptrdiff_t offset_2 = masm.GetCursorOffset(); |
| __ Orr(x0, x0, 1 << 1); |
| __ B(&label_3); |
| ptrdiff_t offset_1 = masm.GetCursorOffset(); |
| __ Orr(x0, x0, 1 << 0); |
| __ B(&label_2); |
| ptrdiff_t offset_3 = masm.GetCursorOffset(); |
| __ Tbz(x0, 2, &first_jump_to_3); |
| __ Orr(x0, x0, 1 << 3); |
| __ Bind(&first_jump_to_3); |
| __ Orr(x0, x0, 1 << 2); |
| __ Tbz(x0, 3, &label_3); |
| |
| // Labels 1, 2, and 3 are bound before the current buffer offset. Branches to |
| // label_1 and label_2 branch respectively forward and backward. Branches to |
| // label 3 include both forward and backward branches. |
| masm.BindToOffset(&label_1, offset_1); |
| masm.BindToOffset(&label_2, offset_2); |
| masm.BindToOffset(&label_3, offset_3); |
| |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0xf, x0); |
| } |
| } |
| |
| |
| TEST(adr) { |
| SETUP(); |
| |
| Label label_1, label_2, label_3, label_4; |
| |
| START(); |
| __ Mov(x0, 0x0); // Set to non-zero to indicate failure. |
| __ Adr(x1, &label_3); // Set to zero to indicate success. |
| |
| __ Adr(x2, &label_1); // Multiple forward references to the same label. |
| __ Adr(x3, &label_1); |
| __ Adr(x4, &label_1); |
| |
| __ Bind(&label_2); |
| __ Eor(x5, x2, Operand(x3)); // Ensure that x2,x3 and x4 are identical. |
| __ Eor(x6, x2, Operand(x4)); |
| __ Orr(x0, x0, Operand(x5)); |
| __ Orr(x0, x0, Operand(x6)); |
| __ Br(x2); // label_1, label_3 |
| |
| __ Bind(&label_3); |
| __ Adr(x2, &label_3); // Self-reference (offset 0). |
| __ Eor(x1, x1, Operand(x2)); |
| __ Adr(x2, &label_4); // Simple forward reference. |
| __ Br(x2); // label_4 |
| |
| __ Bind(&label_1); |
| __ Adr(x2, &label_3); // Multiple reverse references to the same label. |
| __ Adr(x3, &label_3); |
| __ Adr(x4, &label_3); |
| __ Adr(x5, &label_2); // Simple reverse reference. |
| __ Br(x5); // label_2 |
| |
| __ Bind(&label_4); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0x0, x0); |
| ASSERT_EQUAL_64(0x0, x1); |
| } |
| } |
| |
| |
| // Simple adrp tests: check that labels are linked and handled properly. |
| // This is similar to the adr test, but all the adrp instructions are put on the |
| // same page so that they return the same value. |
| TEST(adrp) { |
| Label start; |
| Label label_1, label_2, label_3; |
| |
| SETUP_CUSTOM(2 * kPageSize, PageOffsetDependentCode); |
| START(); |
| |
| // Waste space until the start of a page. |
| { |
| ExactAssemblyScope scope(&masm, |
| kPageSize, |
| ExactAssemblyScope::kMaximumSize); |
| const uintptr_t kPageOffsetMask = kPageSize - 1; |
| while ((masm.GetCursorAddress<uintptr_t>() & kPageOffsetMask) != 0) { |
| __ b(&start); |
| } |
| __ bind(&start); |
| } |
| |
| // Simple forward reference. |
| __ Adrp(x0, &label_2); |
| |
| __ Bind(&label_1); |
| |
| // Multiple forward references to the same label. |
| __ Adrp(x1, &label_3); |
| __ Adrp(x2, &label_3); |
| __ Adrp(x3, &label_3); |
| |
| __ Bind(&label_2); |
| |
| // Self-reference (offset 0). |
| __ Adrp(x4, &label_2); |
| |
| __ Bind(&label_3); |
| |
| // Simple reverse reference. |
| __ Adrp(x5, &label_1); |
| |
| // Multiple reverse references to the same label. |
| __ Adrp(x6, &label_2); |
| __ Adrp(x7, &label_2); |
| __ Adrp(x8, &label_2); |
| |
| VIXL_ASSERT(masm.GetSizeOfCodeGeneratedSince(&start) < kPageSize); |
| END(); |
| if (CAN_RUN()) { |
| RUN(); |
| |
| uint64_t expected = reinterpret_cast<uint64_t>( |
| AlignDown(masm.GetLabelAddress<uint64_t*>(&start), kPageSize)); |
| ASSERT_EQUAL_64(expected, x0); |
| ASSERT_EQUAL_64(expected, x1); |
| ASSERT_EQUAL_64(expected, x2); |
| ASSERT_EQUAL_64(expected, x3); |
| ASSERT_EQUAL_64(expected, x4); |
| ASSERT_EQUAL_64(expected, x5); |
| ASSERT_EQUAL_64(expected, x6); |
| ASSERT_EQUAL_64(expected, x7); |
| ASSERT_EQUAL_64(expected, x8); |
| } |
| } |
| |
| |
| static void AdrpPageBoundaryHelper(unsigned offset_into_page) { |
| VIXL_ASSERT(offset_into_page < kPageSize); |
| VIXL_ASSERT((offset_into_page % kInstructionSize) == 0); |
| |
| const uintptr_t kPageOffsetMask = kPageSize - 1; |
| |
| // The test label is always bound on page 0. Adrp instructions are generated |
| // on pages from kStartPage to kEndPage (inclusive). |
| const int kStartPage = -16; |
| const int kEndPage = 16; |
| const int kMaxCodeSize = (kEndPage - kStartPage + 2) * kPageSize; |
| |
| SETUP_CUSTOM(kMaxCodeSize, PageOffsetDependentCode); |
| START(); |
| |
| Label test; |
| Label start; |
| |
| { |
| ExactAssemblyScope scope(&masm, |
| kMaxCodeSize, |
| ExactAssemblyScope::kMaximumSize); |
| // Initialize NZCV with `eq` flags. |
| __ cmp(wzr, wzr); |
| // Waste space until the start of a page. |
| while ((masm.GetCursorAddress<uintptr_t>() & kPageOffsetMask) != 0) { |
| __ b(&start); |
| } |
| |
| // The first page. |
| VIXL_STATIC_ASSERT(kStartPage < 0); |
| { |
| ExactAssemblyScope scope_page(&masm, kPageSize); |
| __ bind(&start); |
| __ adrp(x0, &test); |
| __ adrp(x1, &test); |
| for (size_t i = 2; i < (kPageSize / kInstructionSize); i += 2) { |
| __ ccmp(x0, x1, NoFlag, eq); |
| __ adrp(x1, &test); |
| } |
| } |
| |
| // Subsequent pages. |
| VIXL_STATIC_ASSERT(kEndPage >= 0); |
| for (int page = (kStartPage + 1); page <= kEndPage; page++) { |
| ExactAssemblyScope scope_page(&masm, kPageSize); |
| if (page == 0) { |
| for (size_t i = 0; i < (kPageSize / kInstructionSize);) { |
| if (i++ == (offset_into_page / kInstructionSize)) __ bind(&test); |
| __ ccmp(x0, x1, NoFlag, eq); |
| if (i++ == (offset_into_page / kInstructionSize)) __ bind(&test); |
| __ adrp(x1, &test); |
| } |
| } else { |
| for (size_t i = 0; i < (kPageSize / kInstructionSize); i += 2) { |
| __ ccmp(x0, x1, NoFlag, eq); |
| __ adrp(x1, &test); |
| } |
| } |
| } |
| } |
| |
| // Every adrp instruction pointed to the same label (`test`), so they should |
| // all have produced the same result. |
| |
| END(); |
| if (CAN_RUN()) { |
| RUN(); |
| |
| uintptr_t expected = |
| AlignDown(masm.GetLabelAddress<uintptr_t>(&test), kPageSize); |
| ASSERT_EQUAL_64(expected, x0); |
| ASSERT_EQUAL_64(expected, x1); |
| ASSERT_EQUAL_NZCV(ZCFlag); |
| } |
| } |
| |
| |
| // Test that labels are correctly referenced by adrp across page boundaries. |
| TEST(adrp_page_boundaries) { |
| VIXL_STATIC_ASSERT(kPageSize == 4096); |
| AdrpPageBoundaryHelper(kInstructionSize * 0); |
| AdrpPageBoundaryHelper(kInstructionSize * 1); |
| AdrpPageBoundaryHelper(kInstructionSize * 512); |
| AdrpPageBoundaryHelper(kInstructionSize * 1022); |
| AdrpPageBoundaryHelper(kInstructionSize * 1023); |
| } |
| |
| |
| static void AdrpOffsetHelper(int64_t offset) { |
| const size_t kPageOffsetMask = kPageSize - 1; |
| const int kMaxCodeSize = 2 * kPageSize; |
| |
| SETUP_CUSTOM(kMaxCodeSize, PageOffsetDependentCode); |
| START(); |
| |
| Label page; |
| |
| { |
| ExactAssemblyScope scope(&masm, |
| kMaxCodeSize, |
| ExactAssemblyScope::kMaximumSize); |
| // Initialize NZCV with `eq` flags. |
| __ cmp(wzr, wzr); |
| // Waste space until the start of a page. |
| while ((masm.GetCursorAddress<uintptr_t>() & kPageOffsetMask) != 0) { |
| __ b(&page); |
| } |
| __ bind(&page); |
| |
| { |
| ExactAssemblyScope scope_page(&masm, kPageSize); |
| // Every adrp instruction on this page should return the same value. |
| __ adrp(x0, offset); |
| __ adrp(x1, offset); |
| for (size_t i = 2; i < kPageSize / kInstructionSize; i += 2) { |
| __ ccmp(x0, x1, NoFlag, eq); |
| __ adrp(x1, offset); |
| } |
| } |
| } |
| |
| END(); |
| if (CAN_RUN()) { |
| RUN(); |
| |
| uintptr_t expected = |
| masm.GetLabelAddress<uintptr_t>(&page) + (kPageSize * offset); |
| ASSERT_EQUAL_64(expected, x0); |
| ASSERT_EQUAL_64(expected, x1); |
| ASSERT_EQUAL_NZCV(ZCFlag); |
| } |
| } |
| |
| |
| // Check that adrp produces the correct result for a specific offset. |
| TEST(adrp_offset) { |
| AdrpOffsetHelper(0); |
| AdrpOffsetHelper(1); |
| AdrpOffsetHelper(-1); |
| AdrpOffsetHelper(4); |
| AdrpOffsetHelper(-4); |
| AdrpOffsetHelper(0x000fffff); |
| AdrpOffsetHelper(-0x000fffff); |
| AdrpOffsetHelper(-0x00100000); |
| } |
| |
| |
| TEST(branch_cond) { |
| SETUP(); |
| |
| Label done, wrong; |
| |
| START(); |
| __ Mov(x0, 0x1); |
| __ Mov(x1, 0x1); |
| __ Mov(x2, 0x8000000000000000); |
| |
| // For each 'cmp' instruction below, condition codes other than the ones |
| // following it would branch. |
| |
| __ Cmp(x1, 0); |
| __ B(&wrong, eq); |
| __ B(&wrong, lo); |
| __ B(&wrong, mi); |
| __ B(&wrong, vs); |
| __ B(&wrong, ls); |
| __ B(&wrong, lt); |
| __ B(&wrong, le); |
| Label ok_1; |
| __ B(&ok_1, ne); |
| __ Mov(x0, 0x0); |
| __ Bind(&ok_1); |
| |
| __ Cmp(x1, 1); |
| __ B(&wrong, ne); |
| __ B(&wrong, lo); |
| __ B(&wrong, mi); |
| __ B(&wrong, vs); |
| __ B(&wrong, hi); |
| __ B(&wrong, lt); |
| __ B(&wrong, gt); |
| Label ok_2; |
| __ B(&ok_2, pl); |
| __ Mov(x0, 0x0); |
| __ Bind(&ok_2); |
| |
| __ Cmp(x1, 2); |
| __ B(&wrong, eq); |
| __ B(&wrong, hs); |
| __ B(&wrong, pl); |
| __ B(&wrong, vs); |
| __ B(&wrong, hi); |
| __ B(&wrong, ge); |
| __ B(&wrong, gt); |
| Label ok_3; |
| __ B(&ok_3, vc); |
| __ Mov(x0, 0x0); |
| __ Bind(&ok_3); |
| |
| __ Cmp(x2, 1); |
| __ B(&wrong, eq); |
| __ B(&wrong, lo); |
| __ B(&wrong, mi); |
| __ B(&wrong, vc); |
| __ B(&wrong, ls); |
| __ B(&wrong, ge); |
| __ B(&wrong, gt); |
| Label ok_4; |
| __ B(&ok_4, le); |
| __ Mov(x0, 0x0); |
| __ Bind(&ok_4); |
| |
| // The MacroAssembler does not allow al as a branch condition. |
| Label ok_5; |
| { |
| ExactAssemblyScope scope(&masm, kInstructionSize); |
| __ b(&ok_5, al); |
| } |
| __ Mov(x0, 0x0); |
| __ Bind(&ok_5); |
| |
| // The MacroAssembler does not allow nv as a branch condition. |
| Label ok_6; |
| { |
| ExactAssemblyScope scope(&masm, kInstructionSize); |
| __ b(&ok_6, nv); |
| } |
| __ Mov(x0, 0x0); |
| __ Bind(&ok_6); |
| |
| __ B(&done); |
| |
| __ Bind(&wrong); |
| __ Mov(x0, 0x0); |
| |
| __ Bind(&done); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0x1, x0); |
| } |
| } |
| |
| |
| TEST(branch_to_reg) { |
| SETUP(); |
| |
| // Test br. |
| Label fn1, after_fn1; |
| |
| START(); |
| __ Mov(x29, lr); |
| |
| __ Mov(x1, 0); |
| __ B(&after_fn1); |
| |
| __ Bind(&fn1); |
| __ Mov(x0, lr); |
| __ Mov(x1, 42); |
| __ Br(x0); |
| |
| __ Bind(&after_fn1); |
| __ Bl(&fn1); |
| |
| // Test blr. |
| Label fn2, after_fn2, after_bl2; |
| |
| __ Mov(x2, 0); |
| __ B(&after_fn2); |
| |
| __ Bind(&fn2); |
| __ Mov(x0, lr); |
| __ Mov(x2, 84); |
| __ Blr(x0); |
| |
| __ Bind(&after_fn2); |
| __ Bl(&fn2); |
| __ Bind(&after_bl2); |
| __ Mov(x3, lr); |
| __ Adr(x4, &after_bl2); |
| __ Adr(x5, &after_fn2); |
| |
| __ Mov(lr, x29); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(x4, x0); |
| ASSERT_EQUAL_64(x5, x3); |
| ASSERT_EQUAL_64(42, x1); |
| ASSERT_EQUAL_64(84, x2); |
| } |
| } |
| |
| TEST(branch_to_reg_auth_a) { |
| SETUP_WITH_FEATURES(CPUFeatures::kPAuth); |
| |
| START(); |
| |
| Label fn1, after_fn1; |
| |
| __ Mov(x28, 0x477d469dec0b8760); |
| __ Mov(x29, lr); |
| |
| __ Mov(x1, 0); |
| __ B(&after_fn1); |
| |
| __ Bind(&fn1); |
| __ Mov(x0, lr); |
| __ Mov(x1, 42); |
| __ Pacia(x0, x28); |
| __ Braa(x0, x28); |
| |
| __ Bind(&after_fn1); |
| __ Bl(&fn1); |
| |
| Label fn2, after_fn2, after_bl2; |
| |
| __ Mov(x2, 0); |
| __ B(&after_fn2); |
| |
| __ Bind(&fn2); |
| __ Mov(x0, lr); |
| __ Mov(x2, 84); |
| __ Pacia(x0, x28); |
| __ Blraa(x0, x28); |
| |
| __ Bind(&after_fn2); |
| __ Bl(&fn2); |
| __ Bind(&after_bl2); |
| __ Mov(x3, lr); |
| __ Adr(x4, &after_bl2); |
| __ Adr(x5, &after_fn2); |
| |
| __ Xpaci(x0); |
| __ Mov(lr, x29); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(x4, x0); |
| ASSERT_EQUAL_64(x5, x3); |
| ASSERT_EQUAL_64(42, x1); |
| ASSERT_EQUAL_64(84, x2); |
| } |
| } |
| |
| TEST(return_to_reg_auth) { |
| SETUP_WITH_FEATURES(CPUFeatures::kPAuth); |
| |
| START(); |
| |
| Label fn1, after_fn1; |
| |
| __ Mov(x28, sp); |
| __ Mov(x29, lr); |
| __ Mov(sp, 0x477d469dec0b8760); |
| |
| __ Mov(x0, 0); |
| __ B(&after_fn1); |
| |
| __ Bind(&fn1); |
| __ Mov(x0, 42); |
| __ Paciasp(); |
| __ Retaa(); |
| |
| __ Bind(&after_fn1); |
| __ Bl(&fn1); |
| |
| Label fn2, after_fn2; |
| |
| __ Mov(x1, 0); |
| __ B(&after_fn2); |
| |
| __ Bind(&fn2); |
| __ Mov(x1, 84); |
| __ Pacibsp(); |
| __ Retab(); |
| |
| __ Bind(&after_fn2); |
| __ Bl(&fn2); |
| |
| __ Mov(sp, x28); |
| __ Mov(lr, x29); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(42, x0); |
| ASSERT_EQUAL_64(84, x1); |
| } |
| } |
| |
| TEST(return_to_reg_auth_guarded) { |
| SETUP_WITH_FEATURES(CPUFeatures::kPAuth); |
| |
| START(); |
| |
| Label fn1, after_fn1; |
| |
| __ Mov(x28, sp); |
| __ Mov(x29, lr); |
| __ Mov(sp, 0x477d469dec0b8760); |
| |
| __ Mov(x0, 0); |
| __ B(&after_fn1); |
| |
| __ Bind(&fn1, EmitPACIASP); |
| __ Mov(x0, 42); |
| __ Retaa(); |
| |
| __ Bind(&after_fn1); |
| __ Adr(x2, &fn1); |
| __ Blr(x2); |
| |
| Label fn2, after_fn2; |
| |
| __ Mov(x1, 0); |
| __ B(&after_fn2); |
| |
| __ Bind(&fn2, EmitPACIBSP); |
| __ Mov(x1, 84); |
| __ Retab(); |
| |
| __ Bind(&after_fn2); |
| __ Adr(x2, &fn2); |
| __ Blr(x2); |
| |
| __ Mov(sp, x28); |
| __ Mov(lr, x29); |
| END(); |
| |
| if (CAN_RUN()) { |
| #ifdef VIXL_INCLUDE_SIMULATOR_AARCH64 |
| simulator.SetGuardedPages(true); |
| #else |
| VIXL_UNIMPLEMENTED(); |
| #endif |
| RUN(); |
| |
| ASSERT_EQUAL_64(42, x0); |
| ASSERT_EQUAL_64(84, x1); |
| } |
| } |
| |
| #ifdef VIXL_NEGATIVE_TESTING |
| TEST(branch_to_reg_auth_fail) { |
| SETUP_WITH_FEATURES(CPUFeatures::kPAuth); |
| |
| START(); |
| |
| Label fn1, after_fn1; |
| |
| __ Mov(x29, lr); |
| |
| __ B(&after_fn1); |
| |
| __ Bind(&fn1); |
| __ Mov(x0, lr); |
| __ Pacizb(x0); |
| __ Blraaz(x0); |
| |
| __ Bind(&after_fn1); |
| // There is a small but not negligible chance (1 in 127 runs) that the PAC |
| // codes for keys A and B will collide and BLRAAZ won't abort. To mitigate |
| // this, we simply repeat the test a few more times. |
| for (unsigned i = 0; i < 32; i++) { |
| __ Bl(&fn1); |
| } |
| |
| __ Mov(lr, x29); |
| END(); |
| |
| if (CAN_RUN()) { |
| MUST_FAIL_WITH_MESSAGE(RUN(), "Failed to authenticate pointer."); |
| } |
| } |
| #endif // VIXL_NEGATIVE_TESTING |
| |
| #ifdef VIXL_NEGATIVE_TESTING |
| TEST(return_to_reg_auth_fail) { |
| SETUP_WITH_FEATURES(CPUFeatures::kPAuth); |
| |
| START(); |
| |
| Label fn1, after_fn1; |
| |
| __ Mov(x28, sp); |
| __ Mov(x29, lr); |
| __ Mov(sp, 0x477d469dec0b8760); |
| |
| __ B(&after_fn1); |
| |
| __ Bind(&fn1); |
| __ Paciasp(); |
| __ Retab(); |
| |
| __ Bind(&after_fn1); |
| // There is a small but not negligible chance (1 in 127 runs) that the PAC |
| // codes for keys A and B will collide and RETAB won't abort. To mitigate |
| // this, we simply repeat the test a few more times. |
| for (unsigned i = 0; i < 32; i++) { |
| __ Bl(&fn1); |
| } |
| |
| __ Mov(sp, x28); |
| __ Mov(lr, x29); |
| END(); |
| |
| if (CAN_RUN()) { |
| MUST_FAIL_WITH_MESSAGE(RUN(), "Failed to authenticate pointer."); |
| } |
| } |
| #endif // VIXL_NEGATIVE_TESTING |
| |
| TEST(branch_to_reg_auth_a_zero) { |
| SETUP_WITH_FEATURES(CPUFeatures::kPAuth); |
| |
| START(); |
| |
| Label fn1, after_fn1; |
| |
| __ Mov(x29, lr); |
| |
| __ Mov(x1, 0); |
| __ B(&after_fn1); |
| |
| __ Bind(&fn1); |
| __ Mov(x0, lr); |
| __ Mov(x1, 42); |
| __ Paciza(x0); |
| __ Braaz(x0); |
| |
| __ Bind(&after_fn1); |
| __ Bl(&fn1); |
| |
| Label fn2, after_fn2, after_bl2; |
| |
| __ Mov(x2, 0); |
| __ B(&after_fn2); |
| |
| __ Bind(&fn2); |
| __ Mov(x0, lr); |
| __ Mov(x2, 84); |
| __ Paciza(x0); |
| __ Blraaz(x0); |
| |
| __ Bind(&after_fn2); |
| __ Bl(&fn2); |
| __ Bind(&after_bl2); |
| __ Mov(x3, lr); |
| __ Adr(x4, &after_bl2); |
| __ Adr(x5, &after_fn2); |
| |
| __ Xpaci(x0); |
| __ Mov(lr, x29); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(x4, x0); |
| ASSERT_EQUAL_64(x5, x3); |
| ASSERT_EQUAL_64(42, x1); |
| ASSERT_EQUAL_64(84, x2); |
| } |
| } |
| |
| |
| TEST(compare_branch) { |
| SETUP(); |
| |
| START(); |
| __ Mov(x0, 0); |
| __ Mov(x1, 0); |
| __ Mov(x2, 0); |
| __ Mov(x3, 0); |
| __ Mov(x4, 0); |
| __ Mov(x5, 0); |
| __ Mov(x16, 0); |
| __ Mov(x17, 42); |
| |
| Label zt, zt_end; |
| __ Cbz(w16, &zt); |
| __ B(&zt_end); |
| __ Bind(&zt); |
| __ Mov(x0, 1); |
| __ Bind(&zt_end); |
| |
| Label zf, zf_end; |
| __ Cbz(x17, &zf); |
| __ B(&zf_end); |
| __ Bind(&zf); |
| __ Mov(x1, 1); |
| __ Bind(&zf_end); |
| |
| Label nzt, nzt_end; |
| __ Cbnz(w17, &nzt); |
| __ B(&nzt_end); |
| __ Bind(&nzt); |
| __ Mov(x2, 1); |
| __ Bind(&nzt_end); |
| |
| Label nzf, nzf_end; |
| __ Cbnz(x16, &nzf); |
| __ B(&nzf_end); |
| __ Bind(&nzf); |
| __ Mov(x3, 1); |
| __ Bind(&nzf_end); |
| |
| __ Mov(x18, 0xffffffff00000000); |
| |
| Label a, a_end; |
| __ Cbz(w18, &a); |
| __ B(&a_end); |
| __ Bind(&a); |
| __ Mov(x4, 1); |
| __ Bind(&a_end); |
| |
| Label b, b_end; |
| __ Cbnz(w18, &b); |
| __ B(&b_end); |
| __ Bind(&b); |
| __ Mov(x5, 1); |
| __ Bind(&b_end); |
| |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(1, x0); |
| ASSERT_EQUAL_64(0, x1); |
| ASSERT_EQUAL_64(1, x2); |
| ASSERT_EQUAL_64(0, x3); |
| ASSERT_EQUAL_64(1, x4); |
| ASSERT_EQUAL_64(0, x5); |
| } |
| } |
| |
| |
| TEST(test_branch) { |
| SETUP(); |
| |
| START(); |
| __ Mov(x0, 0); |
| __ Mov(x1, 0); |
| __ Mov(x2, 0); |
| __ Mov(x3, 0); |
| __ Mov(x16, 0xaaaaaaaaaaaaaaaa); |
| |
| Label bz, bz_end; |
| __ Tbz(w16, 0, &bz); |
| __ B(&bz_end); |
| __ Bind(&bz); |
| __ Mov(x0, 1); |
| __ Bind(&bz_end); |
| |
| Label bo, bo_end; |
| __ Tbz(x16, 63, &bo); |
| __ B(&bo_end); |
| __ Bind(&bo); |
| __ Mov(x1, 1); |
| __ Bind(&bo_end); |
| |
| Label nbz, nbz_end; |
| __ Tbnz(x16, 61, &nbz); |
| __ B(&nbz_end); |
| __ Bind(&nbz); |
| __ Mov(x2, 1); |
| __ Bind(&nbz_end); |
| |
| Label nbo, nbo_end; |
| __ Tbnz(w16, 2, &nbo); |
| __ B(&nbo_end); |
| __ Bind(&nbo); |
| __ Mov(x3, 1); |
| __ Bind(&nbo_end); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(1, x0); |
| ASSERT_EQUAL_64(0, x1); |
| ASSERT_EQUAL_64(1, x2); |
| ASSERT_EQUAL_64(0, x3); |
| } |
| } |
| |
| |
| TEST(branch_type) { |
| SETUP(); |
| |
| Label fail, done; |
| |
| START(); |
| __ Mov(x0, 0x0); |
| __ Mov(x10, 0x7); |
| __ Mov(x11, 0x0); |
| |
| // Test non taken branches. |
| __ Cmp(x10, 0x7); |
| __ B(&fail, ne); |
| __ B(&fail, never); |
| __ B(&fail, reg_zero, x10); |
| __ B(&fail, reg_not_zero, x11); |
| __ B(&fail, reg_bit_clear, x10, 0); |
| __ B(&fail, reg_bit_set, x10, 3); |
| |
| // Test taken branches. |
| Label l1, l2, l3, l4, l5; |
| __ Cmp(x10, 0x7); |
| __ B(&l1, eq); |
| __ B(&fail); |
| __ Bind(&l1); |
| __ B(&l2, always); |
| __ B(&fail); |
| __ Bind(&l2); |
| __ B(&l3, reg_not_zero, x10); |
| __ B(&fail); |
| __ Bind(&l3); |
| __ B(&l4, reg_bit_clear, x10, 15); |
| __ B(&fail); |
| __ Bind(&l4); |
| __ B(&l5, reg_bit_set, x10, 1); |
| __ B(&fail); |
| __ Bind(&l5); |
| |
| __ B(&done); |
| |
| __ Bind(&fail); |
| __ Mov(x0, 0x1); |
| |
| __ Bind(&done); |
| |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0x0, x0); |
| } |
| } |
| |
| |
| TEST(ldr_str_offset) { |
| SETUP(); |
| |
| uint64_t src[2] = {0xfedcba9876543210, 0x0123456789abcdef}; |
| uint64_t dst[5] = {0, 0, 0, 0, 0}; |
| uintptr_t src_base = reinterpret_cast<uintptr_t>(src); |
| uintptr_t dst_base = reinterpret_cast<uintptr_t>(dst); |
| |
| START(); |
| __ Mov(x17, src_base); |
| __ Mov(x18, dst_base); |
| __ Ldr(w0, MemOperand(x17)); |
| __ Str(w0, MemOperand(x18)); |
| __ Ldr(w1, MemOperand(x17, 4)); |
| __ Str(w1, MemOperand(x18, 12)); |
| __ Ldr(x2, MemOperand(x17, 8)); |
| __ Str(x2, MemOperand(x18, 16)); |
| __ Ldrb(w3, MemOperand(x17, 1)); |
| __ Strb(w3, MemOperand(x18, 25)); |
| __ Ldrh(w4, MemOperand(x17, 2)); |
| __ Strh(w4, MemOperand(x18, 33)); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0x76543210, x0); |
| ASSERT_EQUAL_64(0x76543210, dst[0]); |
| ASSERT_EQUAL_64(0xfedcba98, x1); |
| ASSERT_EQUAL_64(0xfedcba9800000000, dst[1]); |
| ASSERT_EQUAL_64(0x0123456789abcdef, x2); |
| ASSERT_EQUAL_64(0x0123456789abcdef, dst[2]); |
| ASSERT_EQUAL_64(0x32, x3); |
| ASSERT_EQUAL_64(0x3200, dst[3]); |
| ASSERT_EQUAL_64(0x7654, x4); |
| ASSERT_EQUAL_64(0x765400, dst[4]); |
| ASSERT_EQUAL_64(src_base, x17); |
| ASSERT_EQUAL_64(dst_base, x18); |
| } |
| } |
| |
| |
| TEST(ldr_str_wide) { |
| SETUP(); |
| |
| uint32_t src[8192]; |
| uint32_t dst[8192]; |
| uintptr_t src_base = reinterpret_cast<uintptr_t>(src); |
| uintptr_t dst_base = reinterpret_cast<uintptr_t>(dst); |
| memset(src, 0xaa, 8192 * sizeof(src[0])); |
| memset(dst, 0xaa, 8192 * sizeof(dst[0])); |
| src[0] = 0; |
| src[6144] = 6144; |
| src[8191] = 8191; |
| |
| START(); |
| __ Mov(x22, src_base); |
| __ Mov(x23, dst_base); |
| __ Mov(x24, src_base); |
| __ Mov(x25, dst_base); |
| __ Mov(x26, src_base); |
| __ Mov(x27, dst_base); |
| |
| __ Ldr(w0, MemOperand(x22, 8191 * sizeof(src[0]))); |
| __ Str(w0, MemOperand(x23, 8191 * sizeof(dst[0]))); |
| __ Ldr(w1, MemOperand(x24, 4096 * sizeof(src[0]), PostIndex)); |
| __ Str(w1, MemOperand(x25, 4096 * sizeof(dst[0]), PostIndex)); |
| __ Ldr(w2, MemOperand(x26, 6144 * sizeof(src[0]), PreIndex)); |
| __ Str(w2, MemOperand(x27, 6144 * sizeof(dst[0]), PreIndex)); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_32(8191, w0); |
| ASSERT_EQUAL_32(8191, dst[8191]); |
| ASSERT_EQUAL_64(src_base, x22); |
| ASSERT_EQUAL_64(dst_base, x23); |
| ASSERT_EQUAL_32(0, w1); |
| ASSERT_EQUAL_32(0, dst[0]); |
| ASSERT_EQUAL_64(src_base + 4096 * sizeof(src[0]), x24); |
| ASSERT_EQUAL_64(dst_base + 4096 * sizeof(dst[0]), x25); |
| ASSERT_EQUAL_32(6144, w2); |
| ASSERT_EQUAL_32(6144, dst[6144]); |
| ASSERT_EQUAL_64(src_base + 6144 * sizeof(src[0]), x26); |
| ASSERT_EQUAL_64(dst_base + 6144 * sizeof(dst[0]), x27); |
| } |
| } |
| |
| |
| TEST(ldr_str_preindex) { |
| SETUP(); |
| |
| uint64_t src[2] = {0xfedcba9876543210, 0x0123456789abcdef}; |
| uint64_t dst[6] = {0, 0, 0, 0, 0, 0}; |
| uintptr_t src_base = reinterpret_cast<uintptr_t>(src); |
| uintptr_t dst_base = reinterpret_cast<uintptr_t>(dst); |
| |
| START(); |
| __ Mov(x17, src_base); |
| __ Mov(x18, dst_base); |
| __ Mov(x19, src_base); |
| __ Mov(x20, dst_base); |
| __ Mov(x21, src_base + 16); |
| __ Mov(x22, dst_base + 40); |
| __ Mov(x23, src_base); |
| __ Mov(x24, dst_base); |
| __ Mov(x25, src_base); |
| __ Mov(x26, dst_base); |
| __ Ldr(w0, MemOperand(x17, 4, PreIndex)); |
| __ Str(w0, MemOperand(x18, 12, PreIndex)); |
| __ Ldr(x1, MemOperand(x19, 8, PreIndex)); |
| __ Str(x1, MemOperand(x20, 16, PreIndex)); |
| __ Ldr(w2, MemOperand(x21, -4, PreIndex)); |
| __ Str(w2, MemOperand(x22, -4, PreIndex)); |
| __ Ldrb(w3, MemOperand(x23, 1, PreIndex)); |
| __ Strb(w3, MemOperand(x24, 25, PreIndex)); |
| __ Ldrh(w4, MemOperand(x25, 3, PreIndex)); |
| __ Strh(w4, MemOperand(x26, 41, PreIndex)); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0xfedcba98, x0); |
| ASSERT_EQUAL_64(0xfedcba9800000000, dst[1]); |
| ASSERT_EQUAL_64(0x0123456789abcdef, x1); |
| ASSERT_EQUAL_64(0x0123456789abcdef, dst[2]); |
| ASSERT_EQUAL_64(0x01234567, x2); |
| ASSERT_EQUAL_64(0x0123456700000000, dst[4]); |
| ASSERT_EQUAL_64(0x32, x3); |
| ASSERT_EQUAL_64(0x3200, dst[3]); |
| ASSERT_EQUAL_64(0x9876, x4); |
| ASSERT_EQUAL_64(0x987600, dst[5]); |
| ASSERT_EQUAL_64(src_base + 4, x17); |
| ASSERT_EQUAL_64(dst_base + 12, x18); |
| ASSERT_EQUAL_64(src_base + 8, x19); |
| ASSERT_EQUAL_64(dst_base + 16, x20); |
| ASSERT_EQUAL_64(src_base + 12, x21); |
| ASSERT_EQUAL_64(dst_base + 36, x22); |
| ASSERT_EQUAL_64(src_base + 1, x23); |
| ASSERT_EQUAL_64(dst_base + 25, x24); |
| ASSERT_EQUAL_64(src_base + 3, x25); |
| ASSERT_EQUAL_64(dst_base + 41, x26); |
| } |
| } |
| |
| |
| TEST(ldr_str_postindex) { |
| SETUP(); |
| |
| uint64_t src[2] = {0xfedcba9876543210, 0x0123456789abcdef}; |
| uint64_t dst[6] = {0, 0, 0, 0, 0, 0}; |
| uintptr_t src_base = reinterpret_cast<uintptr_t>(src); |
| uintptr_t dst_base = reinterpret_cast<uintptr_t>(dst); |
| |
| START(); |
| __ Mov(x17, src_base + 4); |
| __ Mov(x18, dst_base + 12); |
| __ Mov(x19, src_base + 8); |
| __ Mov(x20, dst_base + 16); |
| __ Mov(x21, src_base + 8); |
| __ Mov(x22, dst_base + 32); |
| __ Mov(x23, src_base + 1); |
| __ Mov(x24, dst_base + 25); |
| __ Mov(x25, src_base + 3); |
| __ Mov(x26, dst_base + 41); |
| __ Ldr(w0, MemOperand(x17, 4, PostIndex)); |
| __ Str(w0, MemOperand(x18, 12, PostIndex)); |
| __ Ldr(x1, MemOperand(x19, 8, PostIndex)); |
| __ Str(x1, MemOperand(x20, 16, PostIndex)); |
| __ Ldr(x2, MemOperand(x21, -8, PostIndex)); |
| __ Str(x2, MemOperand(x22, -32, PostIndex)); |
| __ Ldrb(w3, MemOperand(x23, 1, PostIndex)); |
| __ Strb(w3, MemOperand(x24, 5, PostIndex)); |
| __ Ldrh(w4, MemOperand(x25, -3, PostIndex)); |
| __ Strh(w4, MemOperand(x26, -41, PostIndex)); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0xfedcba98, x0); |
| ASSERT_EQUAL_64(0xfedcba9800000000, dst[1]); |
| ASSERT_EQUAL_64(0x0123456789abcdef, x1); |
| ASSERT_EQUAL_64(0x0123456789abcdef, dst[2]); |
| ASSERT_EQUAL_64(0x0123456789abcdef, x2); |
| ASSERT_EQUAL_64(0x0123456789abcdef, dst[4]); |
| ASSERT_EQUAL_64(0x32, x3); |
| ASSERT_EQUAL_64(0x3200, dst[3]); |
| ASSERT_EQUAL_64(0x9876, x4); |
| ASSERT_EQUAL_64(0x987600, dst[5]); |
| ASSERT_EQUAL_64(src_base + 8, x17); |
| ASSERT_EQUAL_64(dst_base + 24, x18); |
| ASSERT_EQUAL_64(src_base + 16, x19); |
| ASSERT_EQUAL_64(dst_base + 32, x20); |
| ASSERT_EQUAL_64(src_base, x21); |
| ASSERT_EQUAL_64(dst_base, x22); |
| ASSERT_EQUAL_64(src_base + 2, x23); |
| ASSERT_EQUAL_64(dst_base + 30, x24); |
| ASSERT_EQUAL_64(src_base, x25); |
| ASSERT_EQUAL_64(dst_base, x26); |
| } |
| } |
| |
| |
| TEST(ldr_str_largeindex) { |
| SETUP(); |
| |
| // This value won't fit in the immediate offset field of ldr/str instructions. |
| int largeoffset = 0xabcdef; |
| |
| int64_t data[3] = {0x1122334455667788, 0, 0}; |
| uint64_t base_addr = reinterpret_cast<uintptr_t>(data); |
| uint64_t drifted_addr = base_addr - largeoffset; |
| |
| // This test checks that we we can use large immediate offsets when |
| // using PreIndex or PostIndex addressing mode of the MacroAssembler |
| // Ldr/Str instructions. |
| |
| START(); |
| __ Mov(x19, drifted_addr); |
| __ Ldr(x0, MemOperand(x19, largeoffset, PreIndex)); |
| |
| __ Mov(x20, base_addr); |
| __ Ldr(x1, MemOperand(x20, largeoffset, PostIndex)); |
| |
| __ Mov(x21, drifted_addr); |
| __ Str(x0, MemOperand(x21, largeoffset + 8, PreIndex)); |
| |
| __ Mov(x22, base_addr + 16); |
| __ Str(x0, MemOperand(x22, largeoffset, PostIndex)); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0x1122334455667788, data[0]); |
| ASSERT_EQUAL_64(0x1122334455667788, data[1]); |
| ASSERT_EQUAL_64(0x1122334455667788, data[2]); |
| ASSERT_EQUAL_64(0x1122334455667788, x0); |
| ASSERT_EQUAL_64(0x1122334455667788, x1); |
| |
| ASSERT_EQUAL_64(base_addr, x19); |
| ASSERT_EQUAL_64(base_addr + largeoffset, x20); |
| ASSERT_EQUAL_64(base_addr + 8, x21); |
| ASSERT_EQUAL_64(base_addr + 16 + largeoffset, x22); |
| } |
| } |
| |
| |
| TEST(load_signed) { |
| SETUP(); |
| |
| uint32_t src[2] = {0x80008080, 0x7fff7f7f}; |
| uintptr_t src_base = reinterpret_cast<uintptr_t>(src); |
| |
| START(); |
| __ Mov(x24, src_base); |
| __ Ldrsb(w0, MemOperand(x24)); |
| __ Ldrsb(w1, MemOperand(x24, 4)); |
| __ Ldrsh(w2, MemOperand(x24)); |
| __ Ldrsh(w3, MemOperand(x24, 4)); |
| __ Ldrsb(x4, MemOperand(x24)); |
| __ Ldrsb(x5, MemOperand(x24, 4)); |
| __ Ldrsh(x6, MemOperand(x24)); |
| __ Ldrsh(x7, MemOperand(x24, 4)); |
| __ Ldrsw(x8, MemOperand(x24)); |
| __ Ldrsw(x9, MemOperand(x24, 4)); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0xffffff80, x0); |
| ASSERT_EQUAL_64(0x0000007f, x1); |
| ASSERT_EQUAL_64(0xffff8080, x2); |
| ASSERT_EQUAL_64(0x00007f7f, x3); |
| ASSERT_EQUAL_64(0xffffffffffffff80, x4); |
| ASSERT_EQUAL_64(0x000000000000007f, x5); |
| ASSERT_EQUAL_64(0xffffffffffff8080, x6); |
| ASSERT_EQUAL_64(0x0000000000007f7f, x7); |
| ASSERT_EQUAL_64(0xffffffff80008080, x8); |
| ASSERT_EQUAL_64(0x000000007fff7f7f, x9); |
| } |
| } |
| |
| |
| TEST(load_store_regoffset) { |
| SETUP(); |
| |
| uint32_t src[3] = {1, 2, 3}; |
| uint32_t dst[4] = {0, 0, 0, 0}; |
| uintptr_t src_base = reinterpret_cast<uintptr_t>(src); |
| uintptr_t dst_base = reinterpret_cast<uintptr_t>(dst); |
| |
| START(); |
| __ Mov(x16, src_base); |
| __ Mov(x17, dst_base); |
| __ Mov(x18, src_base + 3 * sizeof(src[0])); |
| __ Mov(x19, dst_base + 3 * sizeof(dst[0])); |
| __ Mov(x20, dst_base + 4 * sizeof(dst[0])); |
| __ Mov(x24, 0); |
| __ Mov(x25, 4); |
| __ Mov(x26, -4); |
| __ Mov(x27, 0xfffffffc); // 32-bit -4. |
| __ Mov(x28, 0xfffffffe); // 32-bit -2. |
| __ Mov(x29, 0xffffffff); // 32-bit -1. |
| |
| __ Ldr(w0, MemOperand(x16, x24)); |
| __ Ldr(x1, MemOperand(x16, x25)); |
| __ Ldr(w2, MemOperand(x18, x26)); |
| __ Ldr(w3, MemOperand(x18, x27, SXTW)); |
| __ Ldr(w4, MemOperand(x18, x28, SXTW, 2)); |
| __ Str(w0, MemOperand(x17, x24)); |
| __ Str(x1, MemOperand(x17, x25)); |
| __ Str(w2, MemOperand(x20, x29, SXTW, 2)); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(1, x0); |
| ASSERT_EQUAL_64(0x0000000300000002, x1); |
| ASSERT_EQUAL_64(3, x2); |
| ASSERT_EQUAL_64(3, x3); |
| ASSERT_EQUAL_64(2, x4); |
| ASSERT_EQUAL_32(1, dst[0]); |
| ASSERT_EQUAL_32(2, dst[1]); |
| ASSERT_EQUAL_32(3, dst[2]); |
| ASSERT_EQUAL_32(3, dst[3]); |
| } |
| } |
| |
| |
| TEST(load_pauth) { |
| SETUP_WITH_FEATURES(CPUFeatures::kPAuth); |
| |
| uint64_t src[4] = {1, 2, 3, 4}; |
| uintptr_t src_base = reinterpret_cast<uintptr_t>(src); |
| |
| START(); |
| __ Mov(x16, src_base); |
| __ Mov(x17, src_base); |
| __ Mov(x18, src_base + 4 * sizeof(src[0])); |
| __ Mov(x19, src_base + 4 * sizeof(src[0])); |
| |
| // Add PAC codes to addresses |
| __ Pacdza(x16); |
| __ Pacdzb(x17); |
| __ Pacdza(x18); |
| __ Pacdzb(x19); |
| |
| __ Ldraa(x0, MemOperand(x16)); |
| __ Ldraa(x1, MemOperand(x16, sizeof(src[0]))); |
| __ Ldraa(x2, MemOperand(x16, 2 * sizeof(src[0]), PreIndex)); |
| __ Ldraa(x3, MemOperand(x18, -sizeof(src[0]))); |
| __ Ldrab(x4, MemOperand(x17)); |
| __ Ldrab(x5, MemOperand(x17, sizeof(src[0]))); |
| __ Ldrab(x6, MemOperand(x17, 2 * sizeof(src[0]), PreIndex)); |
| __ Ldrab(x7, MemOperand(x19, -sizeof(src[0]))); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(1, x0); |
| ASSERT_EQUAL_64(2, x1); |
| ASSERT_EQUAL_64(3, x2); |
| ASSERT_EQUAL_64(4, x3); |
| ASSERT_EQUAL_64(1, x4); |
| ASSERT_EQUAL_64(2, x5); |
| ASSERT_EQUAL_64(3, x6); |
| ASSERT_EQUAL_64(4, x7); |
| ASSERT_EQUAL_64(src_base + 2 * sizeof(src[0]), x16); |
| ASSERT_EQUAL_64(src_base + 2 * sizeof(src[0]), x17); |
| } |
| } |
| |
| |
| #ifdef VIXL_NEGATIVE_TESTING |
| TEST(load_pauth_negative_test) { |
| SETUP_WITH_FEATURES(CPUFeatures::kPAuth); |
| |
| uint64_t src[4] = {1, 2, 3, 4}; |
| uintptr_t src_base = reinterpret_cast<uintptr_t>(src); |
| |
| START(); |
| __ Mov(x16, src_base); |
| |
| // There is a small but not negligible chance (1 in 127 runs) that the PAC |
| // codes for keys A and B will collide and LDRAB won't abort. To mitigate |
| // this, we simply repeat the test a few more times. |
| for (unsigned i = 0; i < 32; i++) { |
| __ Add(x17, x16, i); |
| __ Pacdza(x17); |
| __ Ldrab(x0, MemOperand(x17)); |
| } |
| END(); |
| |
| if (CAN_RUN()) { |
| MUST_FAIL_WITH_MESSAGE(RUN(), "Failed to authenticate pointer."); |
| } |
| } |
| #endif // VIXL_NEGATIVE_TESTING |
| |
| |
| TEST(ldp_stp_offset) { |
| SETUP(); |
| |
| uint64_t src[3] = {0x0011223344556677, |
| 0x8899aabbccddeeff, |
| 0xffeeddccbbaa9988}; |
| uint64_t dst[7] = {0, 0, 0, 0, 0, 0, 0}; |
| uintptr_t src_base = reinterpret_cast<uintptr_t>(src); |
| uintptr_t dst_base = reinterpret_cast<uintptr_t>(dst); |
| |
| START(); |
| __ Mov(x16, src_base); |
| __ Mov(x17, dst_base); |
| __ Mov(x18, src_base + 24); |
| __ Mov(x19, dst_base + 56); |
| __ Ldp(w0, w1, MemOperand(x16)); |
| __ Ldp(w2, w3, MemOperand(x16, 4)); |
| __ Ldp(x4, x5, MemOperand(x16, 8)); |
| __ Ldp(w6, w7, MemOperand(x18, -12)); |
| __ Ldp(x8, x9, MemOperand(x18, -16)); |
| __ Stp(w0, w1, MemOperand(x17)); |
| __ Stp(w2, w3, MemOperand(x17, 8)); |
| __ Stp(x4, x5, MemOperand(x17, 16)); |
| __ Stp(w6, w7, MemOperand(x19, -24)); |
| __ Stp(x8, x9, MemOperand(x19, -16)); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0x44556677, x0); |
| ASSERT_EQUAL_64(0x00112233, x1); |
| ASSERT_EQUAL_64(0x0011223344556677, dst[0]); |
| ASSERT_EQUAL_64(0x00112233, x2); |
| ASSERT_EQUAL_64(0xccddeeff, x3); |
| ASSERT_EQUAL_64(0xccddeeff00112233, dst[1]); |
| ASSERT_EQUAL_64(0x8899aabbccddeeff, x4); |
| ASSERT_EQUAL_64(0x8899aabbccddeeff, dst[2]); |
| ASSERT_EQUAL_64(0xffeeddccbbaa9988, x5); |
| ASSERT_EQUAL_64(0xffeeddccbbaa9988, dst[3]); |
| ASSERT_EQUAL_64(0x8899aabb, x6); |
| ASSERT_EQUAL_64(0xbbaa9988, x7); |
| ASSERT_EQUAL_64(0xbbaa99888899aabb, dst[4]); |
| ASSERT_EQUAL_64(0x8899aabbccddeeff, x8); |
| ASSERT_EQUAL_64(0x8899aabbccddeeff, dst[5]); |
| ASSERT_EQUAL_64(0xffeeddccbbaa9988, x9); |
| ASSERT_EQUAL_64(0xffeeddccbbaa9988, dst[6]); |
| ASSERT_EQUAL_64(src_base, x16); |
| ASSERT_EQUAL_64(dst_base, x17); |
| ASSERT_EQUAL_64(src_base + 24, x18); |
| ASSERT_EQUAL_64(dst_base + 56, x19); |
| } |
| } |
| |
| |
| TEST(ldp_stp_offset_wide) { |
| SETUP(); |
| |
| uint64_t src[3] = {0x0011223344556677, |
| 0x8899aabbccddeeff, |
| 0xffeeddccbbaa9988}; |
| uint64_t dst[7] = {0, 0, 0, 0, 0, 0, 0}; |
| uintptr_t src_base = reinterpret_cast<uintptr_t>(src); |
| uintptr_t dst_base = reinterpret_cast<uintptr_t>(dst); |
| // Move base too far from the array to force multiple instructions |
| // to be emitted. |
| const int64_t base_offset = 1024; |
| |
| START(); |
| __ Mov(x20, src_base - base_offset); |
| __ Mov(x21, dst_base - base_offset); |
| __ Mov(x18, src_base + base_offset + 24); |
| __ Mov(x19, dst_base + base_offset + 56); |
| __ Ldp(w0, w1, MemOperand(x20, base_offset)); |
| __ Ldp(w2, w3, MemOperand(x20, base_offset + 4)); |
| __ Ldp(x4, x5, MemOperand(x20, base_offset + 8)); |
| __ Ldp(w6, w7, MemOperand(x18, -12 - base_offset)); |
| __ Ldp(x8, x9, MemOperand(x18, -16 - base_offset)); |
| __ Stp(w0, w1, MemOperand(x21, base_offset)); |
| __ Stp(w2, w3, MemOperand(x21, base_offset + 8)); |
| __ Stp(x4, x5, MemOperand(x21, base_offset + 16)); |
| __ Stp(w6, w7, MemOperand(x19, -24 - base_offset)); |
| __ Stp(x8, x9, MemOperand(x19, -16 - base_offset)); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0x44556677, x0); |
| ASSERT_EQUAL_64(0x00112233, x1); |
| ASSERT_EQUAL_64(0x0011223344556677, dst[0]); |
| ASSERT_EQUAL_64(0x00112233, x2); |
| ASSERT_EQUAL_64(0xccddeeff, x3); |
| ASSERT_EQUAL_64(0xccddeeff00112233, dst[1]); |
| ASSERT_EQUAL_64(0x8899aabbccddeeff, x4); |
| ASSERT_EQUAL_64(0x8899aabbccddeeff, dst[2]); |
| ASSERT_EQUAL_64(0xffeeddccbbaa9988, x5); |
| ASSERT_EQUAL_64(0xffeeddccbbaa9988, dst[3]); |
| ASSERT_EQUAL_64(0x8899aabb, x6); |
| ASSERT_EQUAL_64(0xbbaa9988, x7); |
| ASSERT_EQUAL_64(0xbbaa99888899aabb, dst[4]); |
| ASSERT_EQUAL_64(0x8899aabbccddeeff, x8); |
| ASSERT_EQUAL_64(0x8899aabbccddeeff, dst[5]); |
| ASSERT_EQUAL_64(0xffeeddccbbaa9988, x9); |
| ASSERT_EQUAL_64(0xffeeddccbbaa9988, dst[6]); |
| ASSERT_EQUAL_64(src_base - base_offset, x20); |
| ASSERT_EQUAL_64(dst_base - base_offset, x21); |
| ASSERT_EQUAL_64(src_base + base_offset + 24, x18); |
| ASSERT_EQUAL_64(dst_base + base_offset + 56, x19); |
| } |
| } |
| |
| |
| TEST(ldnp_stnp_offset) { |
| SETUP_WITH_FEATURES(CPUFeatures::kNEON); |
| |
| uint64_t src[4] = {0x0011223344556677, |
| 0x8899aabbccddeeff, |
| 0xffeeddccbbaa9988, |
| 0x7766554433221100}; |
| uint64_t dst[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; |
| uintptr_t src_base = reinterpret_cast<uintptr_t>(src); |
| uintptr_t dst_base = reinterpret_cast<uintptr_t>(dst); |
| |
| START(); |
| __ Mov(x16, src_base); |
| __ Mov(x17, dst_base); |
| __ Mov(x18, src_base + 24); |
| __ Mov(x19, dst_base + 64); |
| __ Mov(x20, src_base + 32); |
| |
| // Ensure address set up has happened before executing non-temporal ops. |
| __ Dmb(InnerShareable, BarrierAll); |
| |
| __ Ldnp(w0, w1, MemOperand(x16)); |
| __ Ldnp(w2, w3, MemOperand(x16, 4)); |
| __ Ldnp(x4, x5, MemOperand(x16, 8)); |
| __ Ldnp(w6, w7, MemOperand(x18, -12)); |
| __ Ldnp(x8, x9, MemOperand(x18, -16)); |
| __ Ldnp(q16, q17, MemOperand(x16)); |
| __ Ldnp(q19, q18, MemOperand(x20, -32)); |
| __ Stnp(w0, w1, MemOperand(x17)); |
| __ Stnp(w2, w3, MemOperand(x17, 8)); |
| __ Stnp(x4, x5, MemOperand(x17, 16)); |
| __ Stnp(w6, w7, MemOperand(x19, -32)); |
| __ Stnp(x8, x9, MemOperand(x19, -24)); |
| __ Stnp(q17, q16, MemOperand(x19)); |
| __ Stnp(q18, q19, MemOperand(x19, 32)); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0x44556677, x0); |
| ASSERT_EQUAL_64(0x00112233, x1); |
| ASSERT_EQUAL_64(0x0011223344556677, dst[0]); |
| ASSERT_EQUAL_64(0x00112233, x2); |
| ASSERT_EQUAL_64(0xccddeeff, x3); |
| ASSERT_EQUAL_64(0xccddeeff00112233, dst[1]); |
| ASSERT_EQUAL_64(0x8899aabbccddeeff, x4); |
| ASSERT_EQUAL_64(0x8899aabbccddeeff, dst[2]); |
| ASSERT_EQUAL_64(0xffeeddccbbaa9988, x5); |
| ASSERT_EQUAL_64(0xffeeddccbbaa9988, dst[3]); |
| ASSERT_EQUAL_64(0x8899aabb, x6); |
| ASSERT_EQUAL_64(0xbbaa9988, x7); |
| ASSERT_EQUAL_64(0xbbaa99888899aabb, dst[4]); |
| ASSERT_EQUAL_64(0x8899aabbccddeeff, x8); |
| ASSERT_EQUAL_64(0x8899aabbccddeeff, dst[5]); |
| ASSERT_EQUAL_64(0xffeeddccbbaa9988, x9); |
| ASSERT_EQUAL_64(0xffeeddccbbaa9988, dst[6]); |
| ASSERT_EQUAL_128(0x8899aabbccddeeff, 0x0011223344556677, q16); |
| ASSERT_EQUAL_128(0x7766554433221100, 0xffeeddccbbaa9988, q17); |
| ASSERT_EQUAL_128(0x7766554433221100, 0xffeeddccbbaa9988, q18); |
| ASSERT_EQUAL_128(0x8899aabbccddeeff, 0x0011223344556677, q19); |
| ASSERT_EQUAL_64(0xffeeddccbbaa9988, dst[8]); |
| ASSERT_EQUAL_64(0x7766554433221100, dst[9]); |
| ASSERT_EQUAL_64(0x0011223344556677, dst[10]); |
| ASSERT_EQUAL_64(0x8899aabbccddeeff, dst[11]); |
| ASSERT_EQUAL_64(0xffeeddccbbaa9988, dst[12]); |
| ASSERT_EQUAL_64(0x7766554433221100, dst[13]); |
| ASSERT_EQUAL_64(0x0011223344556677, dst[14]); |
| ASSERT_EQUAL_64(0x8899aabbccddeeff, dst[15]); |
| ASSERT_EQUAL_64(src_base, x16); |
| ASSERT_EQUAL_64(dst_base, x17); |
| ASSERT_EQUAL_64(src_base + 24, x18); |
| ASSERT_EQUAL_64(dst_base + 64, x19); |
| ASSERT_EQUAL_64(src_base + 32, x20); |
| } |
| } |
| |
| TEST(ldp_stp_preindex) { |
| SETUP(); |
| |
| uint64_t src[3] = {0x0011223344556677, |
| 0x8899aabbccddeeff, |
| 0xffeeddccbbaa9988}; |
| uint64_t dst[5] = {0, 0, 0, 0, 0}; |
| uintptr_t src_base = reinterpret_cast<uintptr_t>(src); |
| uintptr_t dst_base = reinterpret_cast<uintptr_t>(dst); |
| |
| START(); |
| __ Mov(x16, src_base); |
| __ Mov(x17, dst_base); |
| __ Mov(x18, dst_base + 16); |
| __ Ldp(w0, w1, MemOperand(x16, 4, PreIndex)); |
| __ Mov(x19, x16); |
| __ Ldp(w2, w3, MemOperand(x16, -4, PreIndex)); |
| __ Stp(w2, w3, MemOperand(x17, 4, PreIndex)); |
| __ Mov(x20, x17); |
| __ Stp(w0, w1, MemOperand(x17, -4, PreIndex)); |
| __ Ldp(x4, x5, MemOperand(x16, 8, PreIndex)); |
| __ Mov(x21, x16); |
| __ Ldp(x6, x7, MemOperand(x16, -8, PreIndex)); |
| __ Stp(x7, x6, MemOperand(x18, 8, PreIndex)); |
| __ Mov(x22, x18); |
| __ Stp(x5, x4, MemOperand(x18, -8, PreIndex)); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0x00112233, x0); |
| ASSERT_EQUAL_64(0xccddeeff, x1); |
| ASSERT_EQUAL_64(0x44556677, x2); |
| ASSERT_EQUAL_64(0x00112233, x3); |
| ASSERT_EQUAL_64(0xccddeeff00112233, dst[0]); |
| ASSERT_EQUAL_64(0x0000000000112233, dst[1]); |
| ASSERT_EQUAL_64(0x8899aabbccddeeff, x4); |
| ASSERT_EQUAL_64(0xffeeddccbbaa9988, x5); |
| ASSERT_EQUAL_64(0x0011223344556677, x6); |
| ASSERT_EQUAL_64(0x8899aabbccddeeff, x7); |
| ASSERT_EQUAL_64(0xffeeddccbbaa9988, dst[2]); |
| ASSERT_EQUAL_64(0x8899aabbccddeeff, dst[3]); |
| ASSERT_EQUAL_64(0x0011223344556677, dst[4]); |
| ASSERT_EQUAL_64(src_base, x16); |
| ASSERT_EQUAL_64(dst_base, x17); |
| ASSERT_EQUAL_64(dst_base + 16, x18); |
| ASSERT_EQUAL_64(src_base + 4, x19); |
| ASSERT_EQUAL_64(dst_base + 4, x20); |
| ASSERT_EQUAL_64(src_base + 8, x21); |
| ASSERT_EQUAL_64(dst_base + 24, x22); |
| } |
| } |
| |
| |
| TEST(ldp_stp_preindex_wide) { |
| SETUP(); |
| |
| uint64_t src[3] = {0x0011223344556677, |
| 0x8899aabbccddeeff, |
| 0xffeeddccbbaa9988}; |
| uint64_t dst[5] = {0, 0, 0, 0, 0}; |
| uintptr_t src_base = reinterpret_cast<uintptr_t>(src); |
| uintptr_t dst_base = reinterpret_cast<uintptr_t>(dst); |
| // Move base too far from the array to force multiple instructions |
| // to be emitted. |
| const int64_t base_offset = 1024; |
| |
| START(); |
| __ Mov(x24, src_base - base_offset); |
| __ Mov(x25, dst_base + base_offset); |
| __ Mov(x18, dst_base + base_offset + 16); |
| __ Ldp(w0, w1, MemOperand(x24, base_offset + 4, PreIndex)); |
| __ Mov(x19, x24); |
| __ Mov(x24, src_base - base_offset + 4); |
| __ Ldp(w2, w3, MemOperand(x24, base_offset - 4, PreIndex)); |
| __ Stp(w2, w3, MemOperand(x25, 4 - base_offset, PreIndex)); |
| __ Mov(x20, x25); |
| __ Mov(x25, dst_base + base_offset + 4); |
| __ Mov(x24, src_base - base_offset); |
| __ Stp(w0, w1, MemOperand(x25, -4 - base_offset, PreIndex)); |
| __ Ldp(x4, x5, MemOperand(x24, base_offset + 8, PreIndex)); |
| __ Mov(x21, x24); |
| __ Mov(x24, src_base - base_offset + 8); |
| __ Ldp(x6, x7, MemOperand(x24, base_offset - 8, PreIndex)); |
| __ Stp(x7, x6, MemOperand(x18, 8 - base_offset, PreIndex)); |
| __ Mov(x22, x18); |
| __ Mov(x18, dst_base + base_offset + 16 + 8); |
| __ Stp(x5, x4, MemOperand(x18, -8 - base_offset, PreIndex)); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0x00112233, x0); |
| ASSERT_EQUAL_64(0xccddeeff, x1); |
| ASSERT_EQUAL_64(0x44556677, x2); |
| ASSERT_EQUAL_64(0x00112233, x3); |
| ASSERT_EQUAL_64(0xccddeeff00112233, dst[0]); |
| ASSERT_EQUAL_64(0x0000000000112233, dst[1]); |
| ASSERT_EQUAL_64(0x8899aabbccddeeff, x4); |
| ASSERT_EQUAL_64(0xffeeddccbbaa9988, x5); |
| ASSERT_EQUAL_64(0x0011223344556677, x6); |
| ASSERT_EQUAL_64(0x8899aabbccddeeff, x7); |
| ASSERT_EQUAL_64(0xffeeddccbbaa9988, dst[2]); |
| ASSERT_EQUAL_64(0x8899aabbccddeeff, dst[3]); |
| ASSERT_EQUAL_64(0x0011223344556677, dst[4]); |
| ASSERT_EQUAL_64(src_base, x24); |
| ASSERT_EQUAL_64(dst_base, x25); |
| ASSERT_EQUAL_64(dst_base + 16, x18); |
| ASSERT_EQUAL_64(src_base + 4, x19); |
| ASSERT_EQUAL_64(dst_base + 4, x20); |
| ASSERT_EQUAL_64(src_base + 8, x21); |
| ASSERT_EQUAL_64(dst_base + 24, x22); |
| } |
| } |
| |
| |
| TEST(ldp_stp_postindex) { |
| SETUP(); |
| |
| uint64_t src[4] = {0x0011223344556677, |
| 0x8899aabbccddeeff, |
| 0xffeeddccbbaa9988, |
| 0x7766554433221100}; |
| uint64_t dst[5] = {0, 0, 0, 0, 0}; |
| uintptr_t src_base = reinterpret_cast<uintptr_t>(src); |
| uintptr_t dst_base = reinterpret_cast<uintptr_t>(dst); |
| |
| START(); |
| __ Mov(x16, src_base); |
| __ Mov(x17, dst_base); |
| __ Mov(x18, dst_base + 16); |
| __ Ldp(w0, w1, MemOperand(x16, 4, PostIndex)); |
| __ Mov(x19, x16); |
| __ Ldp(w2, w3, MemOperand(x16, -4, PostIndex)); |
| __ Stp(w2, w3, MemOperand(x17, 4, PostIndex)); |
| __ Mov(x20, x17); |
| __ Stp(w0, w1, MemOperand(x17, -4, PostIndex)); |
| __ Ldp(x4, x5, MemOperand(x16, 8, PostIndex)); |
| __ Mov(x21, x16); |
| __ Ldp(x6, x7, MemOperand(x16, -8, PostIndex)); |
| __ Stp(x7, x6, MemOperand(x18, 8, PostIndex)); |
| __ Mov(x22, x18); |
| __ Stp(x5, x4, MemOperand(x18, -8, PostIndex)); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0x44556677, x0); |
| ASSERT_EQUAL_64(0x00112233, x1); |
| ASSERT_EQUAL_64(0x00112233, x2); |
| ASSERT_EQUAL_64(0xccddeeff, x3); |
| ASSERT_EQUAL_64(0x4455667700112233, dst[0]); |
| ASSERT_EQUAL_64(0x0000000000112233, dst[1]); |
| ASSERT_EQUAL_64(0x0011223344556677, x4); |
| ASSERT_EQUAL_64(0x8899aabbccddeeff, x5); |
| ASSERT_EQUAL_64(0x8899aabbccddeeff, x6); |
| ASSERT_EQUAL_64(0xffeeddccbbaa9988, x7); |
| ASSERT_EQUAL_64(0xffeeddccbbaa9988, dst[2]); |
| ASSERT_EQUAL_64(0x8899aabbccddeeff, dst[3]); |
| ASSERT_EQUAL_64(0x0011223344556677, dst[4]); |
| ASSERT_EQUAL_64(src_base, x16); |
| ASSERT_EQUAL_64(dst_base, x17); |
| ASSERT_EQUAL_64(dst_base + 16, x18); |
| ASSERT_EQUAL_64(src_base + 4, x19); |
| ASSERT_EQUAL_64(dst_base + 4, x20); |
| ASSERT_EQUAL_64(src_base + 8, x21); |
| ASSERT_EQUAL_64(dst_base + 24, x22); |
| } |
| } |
| |
| |
| TEST(ldp_stp_postindex_wide) { |
| SETUP(); |
| |
| uint64_t src[4] = {0x0011223344556677, |
| 0x8899aabbccddeeff, |
| 0xffeeddccbbaa9988, |
| 0x7766554433221100}; |
| uint64_t dst[5] = {0, 0, 0, 0, 0}; |
| uintptr_t src_base = reinterpret_cast<uintptr_t>(src); |
| uintptr_t dst_base = reinterpret_cast<uintptr_t>(dst); |
| // Move base too far from the array to force multiple instructions |
| // to be emitted. |
| const int64_t base_offset = 1024; |
| |
| START(); |
| __ Mov(x24, src_base); |
| __ Mov(x25, dst_base); |
| __ Mov(x18, dst_base + 16); |
| __ Ldp(w0, w1, MemOperand(x24, base_offset + 4, PostIndex)); |
| __ Mov(x19, x24); |
| __ Sub(x24, x24, base_offset); |
| __ Ldp(w2, w3, MemOperand(x24, base_offset - 4, PostIndex)); |
| __ Stp(w2, w3, MemOperand(x25, 4 - base_offset, PostIndex)); |
| __ Mov(x20, x25); |
| __ Sub(x24, x24, base_offset); |
| __ Add(x25, x25, base_offset); |
| __ Stp(w0, w1, MemOperand(x25, -4 - base_offset, PostIndex)); |
| __ Ldp(x4, x5, MemOperand(x24, base_offset + 8, PostIndex)); |
| __ Mov(x21, x24); |
| __ Sub(x24, x24, base_offset); |
| __ Ldp(x6, x7, MemOperand(x24, base_offset - 8, PostIndex)); |
| __ Stp(x7, x6, MemOperand(x18, 8 - base_offset, PostIndex)); |
| __ Mov(x22, x18); |
| __ Add(x18, x18, base_offset); |
| __ Stp(x5, x4, MemOperand(x18, -8 - base_offset, PostIndex)); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0x44556677, x0); |
| ASSERT_EQUAL_64(0x00112233, x1); |
| ASSERT_EQUAL_64(0x00112233, x2); |
| ASSERT_EQUAL_64(0xccddeeff, x3); |
| ASSERT_EQUAL_64(0x4455667700112233, dst[0]); |
| ASSERT_EQUAL_64(0x0000000000112233, dst[1]); |
| ASSERT_EQUAL_64(0x0011223344556677, x4); |
| ASSERT_EQUAL_64(0x8899aabbccddeeff, x5); |
| ASSERT_EQUAL_64(0x8899aabbccddeeff, x6); |
| ASSERT_EQUAL_64(0xffeeddccbbaa9988, x7); |
| ASSERT_EQUAL_64(0xffeeddccbbaa9988, dst[2]); |
| ASSERT_EQUAL_64(0x8899aabbccddeeff, dst[3]); |
| ASSERT_EQUAL_64(0x0011223344556677, dst[4]); |
| ASSERT_EQUAL_64(src_base + base_offset, x24); |
| ASSERT_EQUAL_64(dst_base - base_offset, x25); |
| ASSERT_EQUAL_64(dst_base - base_offset + 16, x18); |
| ASSERT_EQUAL_64(src_base + base_offset + 4, x19); |
| ASSERT_EQUAL_64(dst_base - base_offset + 4, x20); |
| ASSERT_EQUAL_64(src_base + base_offset + 8, x21); |
| ASSERT_EQUAL_64(dst_base - base_offset + 24, x22); |
| } |
| } |
| |
| |
| TEST(ldp_sign_extend) { |
| SETUP(); |
| |
| uint32_t src[2] = {0x80000000, 0x7fffffff}; |
| uintptr_t src_base = reinterpret_cast<uintptr_t>(src); |
| |
| START(); |
| __ Mov(x24, src_base); |
| __ Ldpsw(x0, x1, MemOperand(x24)); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0xffffffff80000000, x0); |
| ASSERT_EQUAL_64(0x000000007fffffff, x1); |
| } |
| } |
| |
| |
| TEST(ldur_stur) { |
| SETUP(); |
| |
| int64_t src[2] = {0x0123456789abcdef, 0x0123456789abcdef}; |
| int64_t dst[5] = {0, 0, 0, 0, 0}; |
| uintptr_t src_base = reinterpret_cast<uintptr_t>(src); |
| uintptr_t dst_base = reinterpret_cast<uintptr_t>(dst); |
| |
| START(); |
| __ Mov(x17, src_base); |
| __ Mov(x18, dst_base); |
| __ Mov(x19, src_base + 16); |
| __ Mov(x20, dst_base + 32); |
| __ Mov(x21, dst_base + 40); |
| __ Ldr(w0, MemOperand(x17, 1)); |
| __ Str(w0, MemOperand(x18, 2)); |
| __ Ldr(x1, MemOperand(x17, 3)); |
| __ Str(x1, MemOperand(x18, 9)); |
| __ Ldr(w2, MemOperand(x19, -9)); |
| __ Str(w2, MemOperand(x20, -5)); |
| __ Ldrb(w3, MemOperand(x19, -1)); |
| __ Strb(w3, MemOperand(x21, -1)); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0x6789abcd, x0); |
| ASSERT_EQUAL_64(0x00006789abcd0000, dst[0]); |
| ASSERT_EQUAL_64(0xabcdef0123456789, x1); |
| ASSERT_EQUAL_64(0xcdef012345678900, dst[1]); |
| ASSERT_EQUAL_64(0x000000ab, dst[2]); |
| ASSERT_EQUAL_64(0xabcdef01, x2); |
| ASSERT_EQUAL_64(0x00abcdef01000000, dst[3]); |
| ASSERT_EQUAL_64(0x00000001, x3); |
| ASSERT_EQUAL_64(0x0100000000000000, dst[4]); |
| ASSERT_EQUAL_64(src_base, x17); |
| ASSERT_EQUAL_64(dst_base, x18); |
| ASSERT_EQUAL_64(src_base + 16, x19); |
| ASSERT_EQUAL_64(dst_base + 32, x20); |
| } |
| } |
| |
| |
| TEST(ldur_stur_neon) { |
| SETUP_WITH_FEATURES(CPUFeatures::kNEON); |
| |
| int64_t src[3] = {0x0123456789abcdef, 0x0123456789abcdef, 0x0123456789abcdef}; |
| int64_t dst[5] = {0, 0, 0, 0, 0}; |
| uintptr_t src_base = reinterpret_cast<uintptr_t>(src); |
| uintptr_t dst_base = reinterpret_cast<uintptr_t>(dst); |
| |
| START(); |
| __ Mov(x17, src_base); |
| __ Mov(x18, dst_base); |
| __ Ldr(b0, MemOperand(x17)); |
| __ Str(b0, MemOperand(x18)); |
| __ Ldr(h1, MemOperand(x17, 1)); |
| __ Str(h1, MemOperand(x18, 1)); |
| __ Ldr(s2, MemOperand(x17, 2)); |
| __ Str(s2, MemOperand(x18, 3)); |
| __ Ldr(d3, MemOperand(x17, 3)); |
| __ Str(d3, MemOperand(x18, 7)); |
| __ Ldr(q4, MemOperand(x17, 4)); |
| __ Str(q4, MemOperand(x18, 15)); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_128(0, 0xef, q0); |
| ASSERT_EQUAL_128(0, 0xabcd, q1); |
| ASSERT_EQUAL_128(0, 0x456789ab, q2); |
| ASSERT_EQUAL_128(0, 0xabcdef0123456789, q3); |
| ASSERT_EQUAL_128(0x89abcdef01234567, 0x89abcdef01234567, q4); |
| ASSERT_EQUAL_64(0x89456789ababcdef, dst[0]); |
| ASSERT_EQUAL_64(0x67abcdef01234567, dst[1]); |
| ASSERT_EQUAL_64(0x6789abcdef012345, dst[2]); |
| ASSERT_EQUAL_64(0x0089abcdef012345, dst[3]); |
| } |
| } |
| |
| |
| TEST(ldr_literal) { |
| SETUP_WITH_FEATURES(CPUFeatures::kNEON); |
| |
| START(); |
| __ Ldr(x2, 0x1234567890abcdef); |
| __ Ldr(w3, 0xfedcba09); |
| __ Ldrsw(x4, 0x7fffffff); |
| __ Ldrsw(x5, 0x80000000); |
| __ Ldr(q11, 0x1234000056780000, 0xabcd0000ef000000); |
| __ Ldr(d13, 1.234); |
| __ Ldr(s25, 2.5); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0x1234567890abcdef, x2); |
| ASSERT_EQUAL_64(0xfedcba09, x3); |
| ASSERT_EQUAL_64(0x7fffffff, x4); |
| ASSERT_EQUAL_64(0xffffffff80000000, x5); |
| ASSERT_EQUAL_128(0x1234000056780000, 0xabcd0000ef000000, q11); |
| ASSERT_EQUAL_FP64(1.234, d13); |
| ASSERT_EQUAL_FP32(2.5, s25); |
| } |
| } |
| |
| |
| TEST(ldr_literal_range) { |
| SETUP_WITH_FEATURES(CPUFeatures::kNEON); |
| |
| START(); |
| // Make sure the pool is empty; |
| masm.EmitLiteralPool(LiteralPool::kBranchRequired); |
| ASSERT_LITERAL_POOL_SIZE(0); |
| |
| // Create some literal pool entries. |
| __ Ldr(x0, 0x1234567890abcdef); |
| __ Ldr(w1, 0xfedcba09); |
| __ Ldrsw(x2, 0x7fffffff); |
| __ Ldrsw(x3, 0x80000000); |
| __ Ldr(q2, 0x1234000056780000, 0xabcd0000ef000000); |
| __ Ldr(d0, 1.234); |
| __ Ldr(s1, 2.5); |
| ASSERT_LITERAL_POOL_SIZE(48); |
| |
| // Emit more code than the maximum literal load range to ensure the pool |
| // should be emitted. |
| const ptrdiff_t end = masm.GetCursorOffset() + 2 * kMaxLoadLiteralRange; |
| while (masm.GetCursorOffset() < end) { |
| __ Nop(); |
| } |
| |
| // The pool should have been emitted. |
| ASSERT_LITERAL_POOL_SIZE(0); |
| |
| // These loads should be after the pool (and will require a new one). |
| __ Ldr(x4, 0x34567890abcdef12); |
| __ Ldr(w5, 0xdcba09fe); |
| __ Ldrsw(x6, 0x7fffffff); |
| __ Ldrsw(x7, 0x80000000); |
| __ Ldr(q6, 0x1234000056780000, 0xabcd0000ef000000); |
| __ Ldr(d4, 123.4); |
| __ Ldr(s5, 250.0); |
| ASSERT_LITERAL_POOL_SIZE(48); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| // Check that the literals loaded correctly. |
| ASSERT_EQUAL_64(0x1234567890abcdef, x0); |
| ASSERT_EQUAL_64(0xfedcba09, x1); |
| ASSERT_EQUAL_64(0x7fffffff, x2); |
| ASSERT_EQUAL_64(0xffffffff80000000, x3); |
| ASSERT_EQUAL_128(0x1234000056780000, 0xabcd0000ef000000, q2); |
| ASSERT_EQUAL_FP64(1.234, d0); |
| ASSERT_EQUAL_FP32(2.5, s1); |
| ASSERT_EQUAL_64(0x34567890abcdef12, x4); |
| ASSERT_EQUAL_64(0xdcba09fe, x5); |
| ASSERT_EQUAL_64(0x7fffffff, x6); |
| ASSERT_EQUAL_64(0xffffffff80000000, x7); |
| ASSERT_EQUAL_128(0x1234000056780000, 0xabcd0000ef000000, q6); |
| ASSERT_EQUAL_FP64(123.4, d4); |
| ASSERT_EQUAL_FP32(250.0, s5); |
| } |
| } |
| |
| |
| template <typename T> |
| void LoadIntValueHelper(T values[], int card) { |
| SETUP(); |
| |
| const bool is_32bit = (sizeof(T) == 4); |
| Register tgt1 = is_32bit ? Register(w1) : Register(x1); |
| Register tgt2 = is_32bit ? Register(w2) : Register(x2); |
| |
| START(); |
| __ Mov(x0, 0); |
| |
| // If one of the values differ then x0 will be one. |
| for (int i = 0; i < card; ++i) { |
| __ Mov(tgt1, values[i]); |
| __ Ldr(tgt2, values[i]); |
| __ Cmp(tgt1, tgt2); |
| __ Cset(x0, ne); |
| } |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| // If one of the values differs, the trace can be used to identify which |
| // one. |
| ASSERT_EQUAL_64(0, x0); |
| } |
| } |
| |
| |
| TEST(ldr_literal_values_x) { |
| static const uint64_t kValues[] = {0x8000000000000000, |
| 0x7fffffffffffffff, |
| 0x0000000000000000, |
| 0xffffffffffffffff, |
| 0x00ff00ff00ff00ff, |
| 0x1234567890abcdef}; |
| |
| LoadIntValueHelper(kValues, sizeof(kValues) / sizeof(kValues[0])); |
| } |
| |
| |
| TEST(ldr_literal_values_w) { |
| static const uint32_t kValues[] = {0x80000000, |
| 0x7fffffff, |
| 0x00000000, |
| 0xffffffff, |
| 0x00ff00ff, |
| 0x12345678, |
| 0x90abcdef}; |
| |
| LoadIntValueHelper(kValues, sizeof(kValues) / sizeof(kValues[0])); |
| } |
| |
| TEST(ldr_literal_custom) { |
| SETUP_WITH_FEATURES(CPUFeatures::kNEON); |
| |
| Label end_of_pool_before; |
| Label end_of_pool_after; |
| |
| const size_t kSizeOfPoolInBytes = 44; |
| |
| Literal<uint64_t> before_x(0x1234567890abcdef); |
| Literal<uint32_t> before_w(0xfedcba09); |
| Literal<uint32_t> before_sx(0x80000000); |
| Literal<uint64_t> before_q(0x1234000056780000, 0xabcd0000ef000000); |
| Literal<double> before_d(1.234); |
| Literal<float> before_s(2.5); |
| |
| Literal<uint64_t> after_x(0x1234567890abcdef); |
| Literal<uint32_t> after_w(0xfedcba09); |
| Literal<uint32_t> after_sx(0x80000000); |
| Literal<uint64_t> after_q(0x1234000056780000, 0xabcd0000ef000000); |
| Literal<double> after_d(1.234); |
| Literal<float> after_s(2.5); |
| |
| START(); |
| |
| // Manually generate a pool. |
| __ B(&end_of_pool_before); |
| { |
| ExactAssemblyScope scope(&masm, kSizeOfPoolInBytes); |
| __ place(&before_x); |
| __ place(&before_w); |
| __ place(&before_sx); |
| __ place(&before_q); |
| __ place(&before_d); |
| __ place(&before_s); |
| } |
| __ Bind(&end_of_pool_before); |
| |
| { |
| ExactAssemblyScope scope(&masm, 12 * kInstructionSize); |
| __ ldr(x2, &before_x); |
| __ ldr(w3, &before_w); |
| __ ldrsw(x5, &before_sx); |
| __ ldr(q11, &before_q); |
| __ ldr(d13, &before_d); |
| __ ldr(s25, &before_s); |
| |
| __ ldr(x6, &after_x); |
| __ ldr(w7, &after_w); |
| __ ldrsw(x8, &after_sx); |
| __ ldr(q18, &after_q); |
| __ ldr(d14, &after_d); |
| __ ldr(s26, &after_s); |
| } |
| |
| // Manually generate a pool. |
| __ B(&end_of_pool_after); |
| { |
| ExactAssemblyScope scope(&masm, kSizeOfPoolInBytes); |
| __ place(&after_x); |
| __ place(&after_w); |
| __ place(&after_sx); |
| __ place(&after_q); |
| __ place(&after_d); |
| __ place(&after_s); |
| } |
| __ Bind(&end_of_pool_after); |
| |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0x1234567890abcdef, x2); |
| ASSERT_EQUAL_64(0xfedcba09, x3); |
| ASSERT_EQUAL_64(0xffffffff80000000, x5); |
| ASSERT_EQUAL_128(0x1234000056780000, 0xabcd0000ef000000, q11); |
| ASSERT_EQUAL_FP64(1.234, d13); |
| ASSERT_EQUAL_FP32(2.5, s25); |
| |
| ASSERT_EQUAL_64(0x1234567890abcdef, x6); |
| ASSERT_EQUAL_64(0xfedcba09, x7); |
| ASSERT_EQUAL_64(0xffffffff80000000, x8); |
| ASSERT_EQUAL_128(0x1234000056780000, 0xabcd0000ef000000, q18); |
| ASSERT_EQUAL_FP64(1.234, d14); |
| ASSERT_EQUAL_FP32(2.5, s26); |
| } |
| } |
| |
| |
| TEST(ldr_literal_custom_shared) { |
| SETUP_WITH_FEATURES(CPUFeatures::kNEON); |
| |
| Label end_of_pool_before; |
| Label end_of_pool_after; |
| |
| const size_t kSizeOfPoolInBytes = 40; |
| |
| Literal<uint64_t> before_x(0x1234567890abcdef); |
| Literal<uint32_t> before_w(0xfedcba09); |
| Literal<uint64_t> before_q(0x1234000056780000, 0xabcd0000ef000000); |
| Literal<double> before_d(1.234); |
| Literal<float> before_s(2.5); |
| |
| Literal<uint64_t> after_x(0x1234567890abcdef); |
| Literal<uint32_t> after_w(0xfedcba09); |
| Literal<uint64_t> after_q(0x1234000056780000, 0xabcd0000ef000000); |
| Literal<double> after_d(1.234); |
| Literal<float> after_s(2.5); |
| |
| START(); |
| |
| // Manually generate a pool. |
| __ B(&end_of_pool_before); |
| { |
| ExactAssemblyScope scope(&masm, kSizeOfPoolInBytes); |
| __ place(&before_x); |
| __ place(&before_w); |
| __ place(&before_q); |
| __ place(&before_d); |
| __ place(&before_s); |
| } |
| __ Bind(&end_of_pool_before); |
| |
| // Load the entries several times to test that literals can be shared. |
| for (int i = 0; i < 50; i++) { |
| ExactAssemblyScope scope(&masm, 12 * kInstructionSize); |
| __ ldr(x2, &before_x); |
| __ ldr(w3, &before_w); |
| __ ldrsw(x5, &before_w); // Re-use before_w. |
| __ ldr(q11, &before_q); |
| __ ldr(d13, &before_d); |
| __ ldr(s25, &before_s); |
| |
| __ ldr(x6, &after_x); |
| __ ldr(w7, &after_w); |
| __ ldrsw(x8, &after_w); // Re-use after_w. |
| __ ldr(q18, &after_q); |
| __ ldr(d14, &after_d); |
| __ ldr(s26, &after_s); |
| } |
| |
| // Manually generate a pool. |
| __ B(&end_of_pool_after); |
| { |
| ExactAssemblyScope scope(&masm, kSizeOfPoolInBytes); |
| __ place(&after_x); |
| __ place(&after_w); |
| __ place(&after_q); |
| __ place(&after_d); |
| __ place(&after_s); |
| } |
| __ Bind(&end_of_pool_after); |
| |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0x1234567890abcdef, x2); |
| ASSERT_EQUAL_64(0xfedcba09, x3); |
| ASSERT_EQUAL_64(0xfffffffffedcba09, x5); |
| ASSERT_EQUAL_128(0x1234000056780000, 0xabcd0000ef000000, q11); |
| ASSERT_EQUAL_FP64(1.234, d13); |
| ASSERT_EQUAL_FP32(2.5, s25); |
| |
| ASSERT_EQUAL_64(0x1234567890abcdef, x6); |
| ASSERT_EQUAL_64(0xfedcba09, x7); |
| ASSERT_EQUAL_64(0xfffffffffedcba09, x8); |
| ASSERT_EQUAL_128(0x1234000056780000, 0xabcd0000ef000000, q18); |
| ASSERT_EQUAL_FP64(1.234, d14); |
| ASSERT_EQUAL_FP32(2.5, s26); |
| } |
| } |
| |
| |
| TEST(prfm_offset) { |
| SETUP(); |
| |
| START(); |
| // The address used in prfm doesn't have to be valid. |
| __ Mov(x0, 0x0123456789abcdef); |
| |
| for (int i = 0; i < (1 << ImmPrefetchOperation_width); i++) { |
| // Unallocated prefetch operations are ignored, so test all of them. |
| PrefetchOperation op = static_cast<PrefetchOperation>(i); |
| |
| __ Prfm(op, MemOperand(x0)); |
| __ Prfm(op, MemOperand(x0, 8)); |
| __ Prfm(op, MemOperand(x0, 32760)); |
| __ Prfm(op, MemOperand(x0, 32768)); |
| |
| __ Prfm(op, MemOperand(x0, 1)); |
| __ Prfm(op, MemOperand(x0, 9)); |
| __ Prfm(op, MemOperand(x0, 255)); |
| __ Prfm(op, MemOperand(x0, 257)); |
| __ Prfm(op, MemOperand(x0, -1)); |
| __ Prfm(op, MemOperand(x0, -9)); |
| __ Prfm(op, MemOperand(x0, -255)); |
| __ Prfm(op, MemOperand(x0, -257)); |
| |
| __ Prfm(op, MemOperand(x0, 0xfedcba9876543210)); |
| } |
| |
| END(); |
| if (CAN_RUN()) { |
| RUN(); |
| } |
| } |
| |
| |
| TEST(prfm_regoffset) { |
| SETUP(); |
| |
| START(); |
| // The address used in prfm doesn't have to be valid. |
| __ Mov(x0, 0x0123456789abcdef); |
| |
| CPURegList inputs(CPURegister::kRegister, kXRegSize, 10, 18); |
| __ Mov(x10, 0); |
| __ Mov(x11, 1); |
| __ Mov(x12, 8); |
| __ Mov(x13, 255); |
| __ Mov(x14, -0); |
| __ Mov(x15, -1); |
| __ Mov(x16, -8); |
| __ Mov(x17, -255); |
| __ Mov(x18, 0xfedcba9876543210); |
| |
| for (int i = 0; i < (1 << ImmPrefetchOperation_width); i++) { |
| // Unallocated prefetch operations are ignored, so test all of them. |
| PrefetchOperation op = static_cast<PrefetchOperation>(i); |
| |
| CPURegList loop = inputs; |
| while (!loop.IsEmpty()) { |
| Register input(loop.PopLowestIndex()); |
| __ Prfm(op, MemOperand(x0, input)); |
| __ Prfm(op, MemOperand(x0, input, UXTW)); |
| __ Prfm(op, MemOperand(x0, input, UXTW, 3)); |
| __ Prfm(op, MemOperand(x0, input, LSL)); |
| __ Prfm(op, MemOperand(x0, input, LSL, 3)); |
| __ Prfm(op, MemOperand(x0, input, SXTW)); |
| __ Prfm(op, MemOperand(x0, input, SXTW, 3)); |
| __ Prfm(op, MemOperand(x0, input, SXTX)); |
| __ Prfm(op, MemOperand(x0, input, SXTX, 3)); |
| } |
| } |
| |
| END(); |
| if (CAN_RUN()) { |
| RUN(); |
| } |
| } |
| |
| |
| TEST(prfm_literal_imm19) { |
| SETUP(); |
| START(); |
| |
| for (int i = 0; i < (1 << ImmPrefetchOperation_width); i++) { |
| // Unallocated prefetch operations are ignored, so test all of them. |
| PrefetchOperation op = static_cast<PrefetchOperation>(i); |
| |
| ExactAssemblyScope scope(&masm, 7 * kInstructionSize); |
| // The address used in prfm doesn't have to be valid. |
| __ prfm(op, INT64_C(0)); |
| __ prfm(op, 1); |
| __ prfm(op, -1); |
| __ prfm(op, 1000); |
| __ prfm(op, -1000); |
| __ prfm(op, 0x3ffff); |
| __ prfm(op, -0x40000); |
| } |
| |
| END(); |
| if (CAN_RUN()) { |
| RUN(); |
| } |
| } |
| |
| |
| TEST(prfm_literal) { |
| SETUP(); |
| |
| Label end_of_pool_before; |
| Label end_of_pool_after; |
| Literal<uint64_t> before(0); |
| Literal<uint64_t> after(0); |
| |
| START(); |
| |
| // Manually generate a pool. |
| __ B(&end_of_pool_before); |
| { |
| ExactAssemblyScope scope(&masm, before.GetSize()); |
| __ place(&before); |
| } |
| __ Bind(&end_of_pool_before); |
| |
| for (int i = 0; i < (1 << ImmPrefetchOperation_width); i++) { |
| // Unallocated prefetch operations are ignored, so test all of them. |
| PrefetchOperation op = static_cast<PrefetchOperation>(i); |
| |
| ExactAssemblyScope guard(&masm, 2 * kInstructionSize); |
| __ prfm(op, &before); |
| __ prfm(op, &after); |
| } |
| |
| // Manually generate a pool. |
| __ B(&end_of_pool_after); |
| { |
| ExactAssemblyScope scope(&masm, after.GetSize()); |
| __ place(&after); |
| } |
| __ Bind(&end_of_pool_after); |
| |
| END(); |
| if (CAN_RUN()) { |
| RUN(); |
| } |
| } |
| |
| |
| TEST(prfm_wide) { |
| SETUP(); |
| |
| START(); |
| // The address used in prfm doesn't have to be valid. |
| __ Mov(x0, 0x0123456789abcdef); |
| |
| for (int i = 0; i < (1 << ImmPrefetchOperation_width); i++) { |
| // Unallocated prefetch operations are ignored, so test all of them. |
| PrefetchOperation op = static_cast<PrefetchOperation>(i); |
| |
| __ Prfm(op, MemOperand(x0, 0x40000)); |
| __ Prfm(op, MemOperand(x0, -0x40001)); |
| __ Prfm(op, MemOperand(x0, UINT64_C(0x5555555555555555))); |
| __ Prfm(op, MemOperand(x0, UINT64_C(0xfedcba9876543210))); |
| } |
| |
| END(); |
| if (CAN_RUN()) { |
| RUN(); |
| } |
| } |
| |
| |
| TEST(load_prfm_literal) { |
| // Test literals shared between both prfm and ldr. |
| SETUP_WITH_FEATURES(CPUFeatures::kFP); |
| |
| Label end_of_pool_before; |
| Label end_of_pool_after; |
| |
| const size_t kSizeOfPoolInBytes = 28; |
| |
| Literal<uint64_t> before_x(0x1234567890abcdef); |
| Literal<uint32_t> before_w(0xfedcba09); |
| Literal<uint32_t> before_sx(0x80000000); |
| Literal<double> before_d(1.234); |
| Literal<float> before_s(2.5); |
| Literal<uint64_t> after_x(0x1234567890abcdef); |
| Literal<uint32_t> after_w(0xfedcba09); |
| Literal<uint32_t> after_sx(0x80000000); |
| Literal<double> after_d(1.234); |
| Literal<float> after_s(2.5); |
| |
| START(); |
| |
| // Manually generate a pool. |
| __ B(&end_of_pool_before); |
| { |
| ExactAssemblyScope scope(&masm, kSizeOfPoolInBytes); |
| __ place(&before_x); |
| __ place(&before_w); |
| __ place(&before_sx); |
| __ place(&before_d); |
| __ place(&before_s); |
| } |
| __ Bind(&end_of_pool_before); |
| |
| for (int i = 0; i < (1 << ImmPrefetchOperation_width); i++) { |
| // Unallocated prefetch operations are ignored, so test all of them. |
| PrefetchOperation op = static_cast<PrefetchOperation>(i); |
| ExactAssemblyScope scope(&masm, 10 * kInstructionSize); |
| |
| __ prfm(op, &before_x); |
| __ prfm(op, &before_w); |
| __ prfm(op, &before_sx); |
| __ prfm(op, &before_d); |
| __ prfm(op, &before_s); |
| |
| __ prfm(op, &after_x); |
| __ prfm(op, &after_w); |
| __ prfm(op, &after_sx); |
| __ prfm(op, &after_d); |
| __ prfm(op, &after_s); |
| } |
| |
| { |
| ExactAssemblyScope scope(&masm, 10 * kInstructionSize); |
| __ ldr(x2, &before_x); |
| __ ldr(w3, &before_w); |
| __ ldrsw(x5, &before_sx); |
| __ ldr(d13, &before_d); |
| __ ldr(s25, &before_s); |
| |
| __ ldr(x6, &after_x); |
| __ ldr(w7, &after_w); |
| __ ldrsw(x8, &after_sx); |
| __ ldr(d14, &after_d); |
| __ ldr(s26, &after_s); |
| } |
| |
| // Manually generate a pool. |
| __ B(&end_of_pool_after); |
| { |
| ExactAssemblyScope scope(&masm, kSizeOfPoolInBytes); |
| __ place(&after_x); |
| __ place(&after_w); |
| __ place(&after_sx); |
| __ place(&after_d); |
| __ place(&after_s); |
| } |
| __ Bind(&end_of_pool_after); |
| |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0x1234567890abcdef, x2); |
| ASSERT_EQUAL_64(0xfedcba09, x3); |
| ASSERT_EQUAL_64(0xffffffff80000000, x5); |
| ASSERT_EQUAL_FP64(1.234, d13); |
| ASSERT_EQUAL_FP32(2.5, s25); |
| |
| ASSERT_EQUAL_64(0x1234567890abcdef, x6); |
| ASSERT_EQUAL_64(0xfedcba09, x7); |
| ASSERT_EQUAL_64(0xffffffff80000000, x8); |
| ASSERT_EQUAL_FP64(1.234, d14); |
| ASSERT_EQUAL_FP32(2.5, s26); |
| } |
| } |
| |
| |
| TEST(add_sub_imm) { |
| SETUP(); |
| |
| START(); |
| __ Mov(x0, 0x0); |
| __ Mov(x1, 0x1111); |
| __ Mov(x2, 0xffffffffffffffff); |
| __ Mov(x3, 0x8000000000000000); |
| |
| __ Add(x10, x0, Operand(0x123)); |
| __ Add(x11, x1, Operand(0x122000)); |
| __ Add(x12, x0, Operand(0xabc << 12)); |
| __ Add(x13, x2, Operand(1)); |
| |
| __ Add(w14, w0, Operand(0x123)); |
| __ Add(w15, w1, Operand(0x122000)); |
| __ Add(w16, w0, Operand(0xabc << 12)); |
| __ Add(w17, w2, Operand(1)); |
| |
| __ Sub(x20, x0, Operand(0x1)); |
| __ Sub(x21, x1, Operand(0x111)); |
| __ Sub(x22, x1, Operand(0x1 << 12)); |
| __ Sub(x23, x3, Operand(1)); |
| |
| __ Sub(w24, w0, Operand(0x1)); |
| __ Sub(w25, w1, Operand(0x111)); |
| __ Sub(w26, w1, Operand(0x1 << 12)); |
| __ Sub(w27, w3, Operand(1)); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0x123, x10); |
| ASSERT_EQUAL_64(0x123111, x11); |
| ASSERT_EQUAL_64(0xabc000, x12); |
| ASSERT_EQUAL_64(0x0, x13); |
| |
| ASSERT_EQUAL_32(0x123, w14); |
| ASSERT_EQUAL_32(0x123111, w15); |
| ASSERT_EQUAL_32(0xabc000, w16); |
| ASSERT_EQUAL_32(0x0, w17); |
| |
| ASSERT_EQUAL_64(0xffffffffffffffff, x20); |
| ASSERT_EQUAL_64(0x1000, x21); |
| ASSERT_EQUAL_64(0x111, x22); |
| ASSERT_EQUAL_64(0x7fffffffffffffff, x23); |
| |
| ASSERT_EQUAL_32(0xffffffff, w24); |
| ASSERT_EQUAL_32(0x1000, w25); |
| ASSERT_EQUAL_32(0x111, w26); |
| ASSERT_EQUAL_32(0xffffffff, w27); |
| } |
| } |
| |
| |
| TEST(add_sub_wide_imm) { |
| SETUP(); |
| |
| START(); |
| __ Mov(x0, 0x0); |
| __ Mov(x1, 0x1); |
| |
| __ Add(x10, x0, Operand(0x1234567890abcdef)); |
| __ Add(x11, x1, Operand(0xffffffff)); |
| |
| __ Add(w12, w0, Operand(0x12345678)); |
| __ Add(w13, w1, Operand(0xffffffff)); |
| |
| __ Add(w18, w0, Operand(kWMinInt)); |
| __ Sub(w19, w0, Operand(kWMinInt)); |
| |
| __ Sub(x20, x0, Operand(0x1234567890abcdef)); |
| __ Sub(w21, w0, Operand(0x12345678)); |
| |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0x1234567890abcdef, x10); |
| ASSERT_EQUAL_64(0x100000000, x11); |
| |
| ASSERT_EQUAL_32(0x12345678, w12); |
| ASSERT_EQUAL_64(0x0, x13); |
| |
| ASSERT_EQUAL_32(kWMinInt, w18); |
| ASSERT_EQUAL_32(kWMinInt, w19); |
| |
| ASSERT_EQUAL_64(-0x1234567890abcdef, x20); |
| ASSERT_EQUAL_32(-0x12345678, w21); |
| } |
| } |
| |
| |
| TEST(add_sub_shifted) { |
| SETUP(); |
| |
| START(); |
| __ Mov(x0, 0); |
| __ Mov(x1, 0x0123456789abcdef); |
| __ Mov(x2, 0xfedcba9876543210); |
| __ Mov(x3, 0xffffffffffffffff); |
| |
| __ Add(x10, x1, Operand(x2)); |
| __ Add(x11, x0, Operand(x1, LSL, 8)); |
| __ Add(x12, x0, Operand(x1, LSR, 8)); |
| __ Add(x13, x0, Operand(x1, ASR, 8)); |
| __ Add(x14, x0, Operand(x2, ASR, 8)); |
| __ Add(w15, w0, Operand(w1, ASR, 8)); |
| __ Add(w18, w3, Operand(w1, ROR, 8)); |
| __ Add(x19, x3, Operand(x1, ROR, 8)); |
| |
| __ Sub(x20, x3, Operand(x2)); |
| __ Sub(x21, x3, Operand(x1, LSL, 8)); |
| __ Sub(x22, x3, Operand(x1, LSR, 8)); |
| __ Sub(x23, x3, Operand(x1, ASR, 8)); |
| __ Sub(x24, x3, Operand(x2, ASR, 8)); |
| __ Sub(w25, w3, Operand(w1, ASR, 8)); |
| __ Sub(w26, w3, Operand(w1, ROR, 8)); |
| __ Sub(x27, x3, Operand(x1, ROR, 8)); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0xffffffffffffffff, x10); |
| ASSERT_EQUAL_64(0x23456789abcdef00, x11); |
| ASSERT_EQUAL_64(0x000123456789abcd, x12); |
| ASSERT_EQUAL_64(0x000123456789abcd, x13); |
| ASSERT_EQUAL_64(0xfffedcba98765432, x14); |
| ASSERT_EQUAL_64(0xff89abcd, x15); |
| ASSERT_EQUAL_64(0xef89abcc, x18); |
| ASSERT_EQUAL_64(0xef0123456789abcc, x19); |
| |
| ASSERT_EQUAL_64(0x0123456789abcdef, x20); |
| ASSERT_EQUAL_64(0xdcba9876543210ff, x21); |
| ASSERT_EQUAL_64(0xfffedcba98765432, x22); |
| ASSERT_EQUAL_64(0xfffedcba98765432, x23); |
| ASSERT_EQUAL_64(0x000123456789abcd, x24); |
| ASSERT_EQUAL_64(0x00765432, x25); |
| ASSERT_EQUAL_64(0x10765432, x26); |
| ASSERT_EQUAL_64(0x10fedcba98765432, x27); |
| } |
| } |
| |
| |
| TEST(add_sub_extended) { |
| SETUP(); |
| |
| START(); |
| __ Mov(x0, 0); |
| __ Mov(x1, 0x0123456789abcdef); |
| __ Mov(x2, 0xfedcba9876543210); |
| __ Mov(w3, 0x80); |
| |
| __ Add(x10, x0, Operand(x1, UXTB, 0)); |
| __ Add(x11, x0, Operand(x1, UXTB, 1)); |
| __ Add(x12, x0, Operand(x1, UXTH, 2)); |
| __ Add(x13, x0, Operand(x1, UXTW, 4)); |
| |
| __ Add(x14, x0, Operand(x1, SXTB, 0)); |
| __ Add(x15, x0, Operand(x1, SXTB, 1)); |
| __ Add(x16, x0, Operand(x1, SXTH, 2)); |
| __ Add(x17, x0, Operand(x1, SXTW, 3)); |
| __ Add(x18, x0, Operand(x2, SXTB, 0)); |
| __ Add(x19, x0, Operand(x2, SXTB, 1)); |
| __ Add(x20, x0, Operand(x2, SXTH, 2)); |
| __ Add(x21, x0, Operand(x2, SXTW, 3)); |
| |
| __ Add(x22, x1, Operand(x2, SXTB, 1)); |
| __ Sub(x23, x1, Operand(x2, SXTB, 1)); |
| |
| __ Add(w24, w1, Operand(w2, UXTB, 2)); |
| __ Add(w25, w0, Operand(w1, SXTB, 0)); |
| __ Add(w26, w0, Operand(w1, SXTB, 1)); |
| __ Add(w27, w2, Operand(w1, SXTW, 3)); |
| |
| __ Add(w28, w0, Operand(w1, SXTW, 3)); |
| __ Add(x29, x0, Operand(w1, SXTW, 3)); |
| |
| __ Sub(x30, x0, Operand(w3, SXTB, 1)); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0xef, x10); |
| ASSERT_EQUAL_64(0x1de, x11); |
| ASSERT_EQUAL_64(0x337bc, x12); |
| ASSERT_EQUAL_64(0x89abcdef0, x13); |
| |
| ASSERT_EQUAL_64(0xffffffffffffffef, x14); |
| ASSERT_EQUAL_64(0xffffffffffffffde, x15); |
| ASSERT_EQUAL_64(0xffffffffffff37bc, x16); |
| ASSERT_EQUAL_64(0xfffffffc4d5e6f78, x17); |
| ASSERT_EQUAL_64(0x10, x18); |
| ASSERT_EQUAL_64(0x20, x19); |
| ASSERT_EQUAL_64(0xc840, x20); |
| ASSERT_EQUAL_64(0x3b2a19080, x21); |
| |
| ASSERT_EQUAL_64(0x0123456789abce0f, x22); |
| ASSERT_EQUAL_64(0x0123456789abcdcf, x23); |
| |
| ASSERT_EQUAL_32(0x89abce2f, w24); |
| ASSERT_EQUAL_32(0xffffffef, w25); |
| ASSERT_EQUAL_32(0xffffffde, w26); |
| ASSERT_EQUAL_32(0xc3b2a188, w27); |
| |
| ASSERT_EQUAL_32(0x4d5e6f78, w28); |
| ASSERT_EQUAL_64(0xfffffffc4d5e6f78, x29); |
| |
| ASSERT_EQUAL_64(256, x30); |
| } |
| } |
| |
| |
| TEST(add_sub_negative) { |
| SETUP(); |
| |
| START(); |
| __ Mov(x0, 0); |
| __ Mov(x1, 4687); |
| __ Mov(x2, 0x1122334455667788); |
| __ Mov(w3, 0x11223344); |
| __ Mov(w4, 400000); |
| |
| __ Add(x10, x0, -42); |
| __ Add(x11, x1, -687); |
| __ Add(x12, x2, -0x88); |
| |
| __ Sub(x13, x0, -600); |
| __ Sub(x14, x1, -313); |
| __ Sub(x15, x2, -0x555); |
| |
| __ Add(w19, w3, -0x344); |
| __ Add(w20, w4, -2000); |
| |
| __ Sub(w21, w3, -0xbc); |
| __ Sub(w22, w4, -2000); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(-42, x10); |
| ASSERT_EQUAL_64(4000, x11); |
| ASSERT_EQUAL_64(0x1122334455667700, x12); |
| |
| ASSERT_EQUAL_64(600, x13); |
| ASSERT_EQUAL_64(5000, x14); |
| ASSERT_EQUAL_64(0x1122334455667cdd, x15); |
| |
| ASSERT_EQUAL_32(0x11223000, w19); |
| ASSERT_EQUAL_32(398000, w20); |
| |
| ASSERT_EQUAL_32(0x11223400, w21); |
| ASSERT_EQUAL_32(402000, w22); |
| } |
| } |
| |
| |
| TEST(add_sub_zero) { |
| SETUP(); |
| |
| START(); |
| __ Mov(x0, 0); |
| __ Mov(x1, 0); |
| __ Mov(x2, 0); |
| |
| Label blob1; |
| __ Bind(&blob1); |
| __ Add(x0, x0, 0); |
| __ Sub(x1, x1, 0); |
| __ Sub(x2, x2, xzr); |
| VIXL_CHECK(__ GetSizeOfCodeGeneratedSince(&blob1) == 0); |
| |
| Label blob2; |
| __ Bind(&blob2); |
| __ Add(w3, w3, 0); |
| VIXL_CHECK(__ GetSizeOfCodeGeneratedSince(&blob2) != 0); |
| |
| Label blob3; |
| __ Bind(&blob3); |
| __ Sub(w3, w3, wzr); |
| VIXL_CHECK(__ GetSizeOfCodeGeneratedSince(&blob3) != 0); |
| |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0, x0); |
| ASSERT_EQUAL_64(0, x1); |
| ASSERT_EQUAL_64(0, x2); |
| } |
| } |
| |
| |
| TEST(claim_drop_zero) { |
| SETUP(); |
| |
| START(); |
| |
| Label start; |
| __ Bind(&start); |
| __ Claim(Operand(0)); |
| __ Drop(Operand(0)); |
| __ Claim(Operand(xzr)); |
| __ Drop(Operand(xzr)); |
| VIXL_CHECK(__ GetSizeOfCodeGeneratedSince(&start) == 0); |
| |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| } |
| } |
| |
| |
| TEST(neg) { |
| SETUP(); |
| |
| START(); |
| __ Mov(x0, 0xf123456789abcdef); |
| |
| // Immediate. |
| __ Neg(x1, 0x123); |
| __ Neg(w2, 0x123); |
| |
| // Shifted. |
| __ Neg(x3, Operand(x0, LSL, 1)); |
| __ Neg(w4, Operand(w0, LSL, 2)); |
| __ Neg(x5, Operand(x0, LSR, 3)); |
| __ Neg(w6, Operand(w0, LSR, 4)); |
| __ Neg(x7, Operand(x0, ASR, 5)); |
| __ Neg(w8, Operand(w0, ASR, 6)); |
| |
| // Extended. |
| __ Neg(w9, Operand(w0, UXTB)); |
| __ Neg(x10, Operand(x0, SXTB, 1)); |
| __ Neg(w11, Operand(w0, UXTH, 2)); |
| __ Neg(x12, Operand(x0, SXTH, 3)); |
| __ Neg(w13, Operand(w0, UXTW, 4)); |
| __ Neg(x14, Operand(x0, SXTW, 4)); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0xfffffffffffffedd, x1); |
| ASSERT_EQUAL_64(0xfffffedd, x2); |
| ASSERT_EQUAL_64(0x1db97530eca86422, x3); |
| ASSERT_EQUAL_64(0xd950c844, x4); |
| ASSERT_EQUAL_64(0xe1db97530eca8643, x5); |
| ASSERT_EQUAL_64(0xf7654322, x6); |
| ASSERT_EQUAL_64(0x0076e5d4c3b2a191, x7); |
| ASSERT_EQUAL_64(0x01d950c9, x8); |
| ASSERT_EQUAL_64(0xffffff11, x9); |
| ASSERT_EQUAL_64(0x0000000000000022, x10); |
| ASSERT_EQUAL_64(0xfffcc844, x11); |
| ASSERT_EQUAL_64(0x0000000000019088, x12); |
| ASSERT_EQUAL_64(0x65432110, x13); |
| ASSERT_EQUAL_64(0x0000000765432110, x14); |
| } |
| } |
| |
| |
| template <typename T, typename Op> |
| static void AdcsSbcsHelper( |
| Op op, T left, T right, int carry, T expected, StatusFlags expected_flags) { |
| int reg_size = sizeof(T) * 8; |
| Register left_reg(0, reg_size); |
| Register right_reg(1, reg_size); |
| Register result_reg(2, reg_size); |
| |
| SETUP(); |
| START(); |
| |
| __ Mov(left_reg, left); |
| __ Mov(right_reg, right); |
| __ Mov(x10, (carry ? CFlag : NoFlag)); |
| |
| __ Msr(NZCV, x10); |
| (masm.*op)(result_reg, left_reg, right_reg); |
| |
| END(); |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(left, left_reg.X()); |
| ASSERT_EQUAL_64(right, right_reg.X()); |
| ASSERT_EQUAL_64(expected, result_reg.X()); |
| ASSERT_EQUAL_NZCV(expected_flags); |
| } |
| } |
| |
| |
| TEST(adcs_sbcs_x) { |
| uint64_t inputs[] = { |
| 0x0000000000000000, |
| 0x0000000000000001, |
| 0x7ffffffffffffffe, |
| 0x7fffffffffffffff, |
| 0x8000000000000000, |
| 0x8000000000000001, |
| 0xfffffffffffffffe, |
| 0xffffffffffffffff, |
| }; |
| static const size_t input_count = sizeof(inputs) / sizeof(inputs[0]); |
| |
| struct Expected { |
| uint64_t carry0_result; |
| StatusFlags carry0_flags; |
| uint64_t carry1_result; |
| StatusFlags carry1_flags; |
| }; |
| |
| static const Expected expected_adcs_x[input_count][input_count] = |
| {{{0x0000000000000000, ZFlag, 0x0000000000000001, NoFlag}, |
| {0x0000000000000001, NoFlag, 0x0000000000000002, NoFlag}, |
| {0x7ffffffffffffffe, NoFlag, 0x7fffffffffffffff, NoFlag}, |
| {0x7fffffffffffffff, NoFlag, 0x8000000000000000, NVFlag}, |
| {0x8000000000000000, NFlag, 0x8000000000000001, NFlag}, |
| {0x8000000000000001, NFlag, 0x8000000000000002, NFlag}, |
| {0xfffffffffffffffe, NFlag, 0xffffffffffffffff, NFlag}, |
| {0xffffffffffffffff, NFlag, 0x0000000000000000, ZCFlag}}, |
| {{0x0000000000000001, NoFlag, 0x0000000000000002, NoFlag}, |
| {0x0000000000000002, NoFlag, 0x0000000000000003, NoFlag}, |
| {0x7fffffffffffffff, NoFlag, 0x8000000000000000, NVFlag}, |
| {0x8000000000000000, NVFlag, 0x8000000000000001, NVFlag}, |
| {0x8000000000000001, NFlag, 0x8000000000000002, NFlag}, |
| {0x8000000000000002, NFlag, 0x8000000000000003, NFlag}, |
| {0xffffffffffffffff, NFlag, 0x0000000000000000, ZCFlag}, |
| {0x0000000000000000, ZCFlag, 0x0000000000000001, CFlag}}, |
| {{0x7ffffffffffffffe, NoFlag, 0x7fffffffffffffff, NoFlag}, |
| {0x7fffffffffffffff, NoFlag, 0x8000000000000000, NVFlag}, |
| {0xfffffffffffffffc, NVFlag, 0xfffffffffffffffd, NVFlag}, |
| {0xfffffffffffffffd, NVFlag, 0xfffffffffffffffe, NVFlag}, |
| {0xfffffffffffffffe, NFlag, 0xffffffffffffffff, NFlag}, |
| {0xffffffffffffffff, NFlag, 0x0000000000000000, ZCFlag}, |
| {0x7ffffffffffffffc, CFlag, 0x7ffffffffffffffd, CFlag}, |
| {0x7ffffffffffffffd, CFlag, 0x7ffffffffffffffe, CFlag}}, |
| {{0x7fffffffffffffff, NoFlag, 0x8000000000000000, NVFlag}, |
| {0x8000000000000000, NVFlag, 0x8000000000000001, NVFlag}, |
| {0xfffffffffffffffd, NVFlag, 0xfffffffffffffffe, NVFlag}, |
| {0xfffffffffffffffe, NVFlag, 0xffffffffffffffff, NVFlag}, |
| {0xffffffffffffffff, NFlag, 0x0000000000000000, ZCFlag}, |
| {0x0000000000000000, ZCFlag, 0x0000000000000001, CFlag}, |
| {0x7ffffffffffffffd, CFlag, 0x7ffffffffffffffe, CFlag}, |
| {0x7ffffffffffffffe, CFlag, 0x7fffffffffffffff, CFlag}}, |
| {{0x8000000000000000, NFlag, 0x8000000000000001, NFlag}, |
| {0x8000000000000001, NFlag, 0x8000000000000002, NFlag}, |
| {0xfffffffffffffffe, NFlag, 0xffffffffffffffff, NFlag}, |
| {0xffffffffffffffff, NFlag, 0x0000000000000000, ZCFlag}, |
| {0x0000000000000000, ZCVFlag, 0x0000000000000001, CVFlag}, |
| {0x0000000000000001, CVFlag, 0x0000000000000002, CVFlag}, |
| {0x7ffffffffffffffe, CVFlag, 0x7fffffffffffffff, CVFlag}, |
| {0x7fffffffffffffff, CVFlag, 0x8000000000000000, NCFlag}}, |
| {{0x8000000000000001, NFlag, 0x8000000000000002, NFlag}, |
| {0x8000000000000002, NFlag, 0x8000000000000003, NFlag}, |
| {0xffffffffffffffff, NFlag, 0x0000000000000000, ZCFlag}, |
| {0x0000000000000000, ZCFlag, 0x0000000000000001, CFlag}, |
| {0x0000000000000001, CVFlag, 0x0000000000000002, CVFlag}, |
| {0x0000000000000002, CVFlag, 0x0000000000000003, CVFlag}, |
| {0x7fffffffffffffff, CVFlag, 0x8000000000000000, NCFlag}, |
| {0x8000000000000000, NCFlag, 0x8000000000000001, NCFlag}}, |
| {{0xfffffffffffffffe, NFlag, 0xffffffffffffffff, NFlag}, |
| {0xffffffffffffffff, NFlag, 0x0000000000000000, ZCFlag}, |
| {0x7ffffffffffffffc, CFlag, 0x7ffffffffffffffd, CFlag}, |
| {0x7ffffffffffffffd, CFlag, 0x7ffffffffffffffe, CFlag}, |
| {0x7ffffffffffffffe, CVFlag, 0x7fffffffffffffff, CVFlag}, |
| {0x7fffffffffffffff, CVFlag, 0x8000000000000000, NCFlag}, |
| {0xfffffffffffffffc, NCFlag, 0xfffffffffffffffd, NCFlag}, |
| {0xfffffffffffffffd, NCFlag, 0xfffffffffffffffe, NCFlag}}, |
| {{0xffffffffffffffff, NFlag, 0x0000000000000000, ZCFlag}, |
| {0x0000000000000000, ZCFlag, 0x0000000000000001, CFlag}, |
| {0x7ffffffffffffffd, CFlag, 0x7ffffffffffffffe, CFlag}, |
| {0x7ffffffffffffffe, CFlag, 0x7fffffffffffffff, CFlag}, |
| {0x7fffffffffffffff, CVFlag, 0x8000000000000000, NCFlag}, |
| {0x8000000000000000, NCFlag, 0x8000000000000001, NCFlag}, |
| {0xfffffffffffffffd, NCFlag, 0xfffffffffffffffe, NCFlag}, |
| {0xfffffffffffffffe, NCFlag, 0xffffffffffffffff, NCFlag}}}; |
| |
| static const Expected expected_sbcs_x[input_count][input_count] = |
| {{{0xffffffffffffffff, NFlag, 0x0000000000000000, ZCFlag}, |
| {0xfffffffffffffffe, NFlag, 0xffffffffffffffff, NFlag}, |
| {0x8000000000000001, NFlag, 0x8000000000000002, NFlag}, |
| {0x8000000000000000, NFlag, 0x8000000000000001, NFlag}, |
| {0x7fffffffffffffff, NoFlag, 0x8000000000000000, NVFlag}, |
| {0x7ffffffffffffffe, NoFlag, 0x7fffffffffffffff, NoFlag}, |
| {0x0000000000000001, NoFlag, 0x0000000000000002, NoFlag}, |
| {0x0000000000000000, ZFlag, 0x0000000000000001, NoFlag}}, |
| {{0x0000000000000000, ZCFlag, 0x0000000000000001, CFlag}, |
| {0xffffffffffffffff, NFlag, 0x0000000000000000, ZCFlag}, |
| {0x8000000000000002, NFlag, 0x8000000000000003, NFlag}, |
| {0x8000000000000001, NFlag, 0x8000000000000002, NFlag}, |
| {0x8000000000000000, NVFlag, 0x8000000000000001, NVFlag}, |
| {0x7fffffffffffffff, NoFlag, 0x8000000000000000, NVFlag}, |
| {0x0000000000000002, NoFlag, 0x0000000000000003, NoFlag}, |
| {0x0000000000000001, NoFlag, 0x0000000000000002, NoFlag}}, |
| {{0x7ffffffffffffffd, CFlag, 0x7ffffffffffffffe, CFlag}, |
| {0x7ffffffffffffffc, CFlag, 0x7ffffffffffffffd, CFlag}, |
| {0xffffffffffffffff, NFlag, 0x0000000000000000, ZCFlag}, |
| {0xfffffffffffffffe, NFlag, 0xffffffffffffffff, NFlag}, |
| {0xfffffffffffffffd, NVFlag, 0xfffffffffffffffe, NVFlag}, |
| {0xfffffffffffffffc, NVFlag, 0xfffffffffffffffd, NVFlag}, |
| {0x7fffffffffffffff, NoFlag, 0x8000000000000000, NVFlag}, |
| {0x7ffffffffffffffe, NoFlag, 0x7fffffffffffffff, NoFlag}}, |
| {{0x7ffffffffffffffe, CFlag, 0x7fffffffffffffff, CFlag}, |
| {0x7ffffffffffffffd, CFlag, 0x7ffffffffffffffe, CFlag}, |
| {0x0000000000000000, ZCFlag, 0x0000000000000001, CFlag}, |
| {0xffffffffffffffff, NFlag, 0x0000000000000000, ZCFlag}, |
| {0xfffffffffffffffe, NVFlag, 0xffffffffffffffff, NVFlag}, |
| {0xfffffffffffffffd, NVFlag, 0xfffffffffffffffe, NVFlag}, |
| {0x8000000000000000, NVFlag, 0x8000000000000001, NVFlag}, |
| {0x7fffffffffffffff, NoFlag, 0x8000000000000000, NVFlag}}, |
| {{0x7fffffffffffffff, CVFlag, 0x8000000000000000, NCFlag}, |
| {0x7ffffffffffffffe, CVFlag, 0x7fffffffffffffff, CVFlag}, |
| {0x0000000000000001, CVFlag, 0x0000000000000002, CVFlag}, |
| {0x0000000000000000, ZCVFlag, 0x0000000000000001, CVFlag}, |
| {0xffffffffffffffff, NFlag, 0x0000000000000000, ZCFlag}, |
| {0xfffffffffffffffe, NFlag, 0xffffffffffffffff, NFlag}, |
| {0x8000000000000001, NFlag, 0x8000000000000002, NFlag}, |
| {0x8000000000000000, NFlag, 0x8000000000000001, NFlag}}, |
| {{0x8000000000000000, NCFlag, 0x8000000000000001, NCFlag}, |
| {0x7fffffffffffffff, CVFlag, 0x8000000000000000, NCFlag}, |
| {0x0000000000000002, CVFlag, 0x0000000000000003, CVFlag}, |
| {0x0000000000000001, CVFlag, 0x0000000000000002, CVFlag}, |
| {0x0000000000000000, ZCFlag, 0x0000000000000001, CFlag}, |
| {0xffffffffffffffff, NFlag, 0x0000000000000000, ZCFlag}, |
| {0x8000000000000002, NFlag, 0x8000000000000003, NFlag}, |
| {0x8000000000000001, NFlag, 0x8000000000000002, NFlag}}, |
| {{0xfffffffffffffffd, NCFlag, 0xfffffffffffffffe, NCFlag}, |
| {0xfffffffffffffffc, NCFlag, 0xfffffffffffffffd, NCFlag}, |
| {0x7fffffffffffffff, CVFlag, 0x8000000000000000, NCFlag}, |
| {0x7ffffffffffffffe, CVFlag, 0x7fffffffffffffff, CVFlag}, |
| {0x7ffffffffffffffd, CFlag, 0x7ffffffffffffffe, CFlag}, |
| {0x7ffffffffffffffc, CFlag, 0x7ffffffffffffffd, CFlag}, |
| {0xffffffffffffffff, NFlag, 0x0000000000000000, ZCFlag}, |
| {0xfffffffffffffffe, NFlag, 0xffffffffffffffff, NFlag}}, |
| {{0xfffffffffffffffe, NCFlag, 0xffffffffffffffff, NCFlag}, |
| {0xfffffffffffffffd, NCFlag, 0xfffffffffffffffe, NCFlag}, |
| {0x8000000000000000, NCFlag, 0x8000000000000001, NCFlag}, |
| {0x7fffffffffffffff, CVFlag, 0x8000000000000000, NCFlag}, |
| {0x7ffffffffffffffe, CFlag, 0x7fffffffffffffff, CFlag}, |
| {0x7ffffffffffffffd, CFlag, 0x7ffffffffffffffe, CFlag}, |
| {0x0000000000000000, ZCFlag, 0x0000000000000001, CFlag}, |
| {0xffffffffffffffff, NFlag, 0x0000000000000000, ZCFlag}}}; |
| |
| for (size_t left = 0; left < input_count; left++) { |
| for (size_t right = 0; right < input_count; right++) { |
| const Expected& expected = expected_adcs_x[left][right]; |
| AdcsSbcsHelper(&MacroAssembler::Adcs, |
| inputs[left], |
| inputs[right], |
| 0, |
| expected.carry0_result, |
| expected.carry0_flags); |
| AdcsSbcsHelper(&MacroAssembler::Adcs, |
| inputs[left], |
| inputs[right], |
| 1, |
| expected.carry1_result, |
| expected.carry1_flags); |
| } |
| } |
| |
| for (size_t left = 0; left < input_count; left++) { |
| for (size_t right = 0; right < input_count; right++) { |
| const Expected& expected = expected_sbcs_x[left][right]; |
| AdcsSbcsHelper(&MacroAssembler::Sbcs, |
| inputs[left], |
| inputs[right], |
| 0, |
| expected.carry0_result, |
| expected.carry0_flags); |
| AdcsSbcsHelper(&MacroAssembler::Sbcs, |
| inputs[left], |
| inputs[right], |
| 1, |
| expected.carry1_result, |
| expected.carry1_flags); |
| } |
| } |
| } |
| |
| |
| TEST(adcs_sbcs_w) { |
| uint32_t inputs[] = { |
| 0x00000000, |
| 0x00000001, |
| 0x7ffffffe, |
| 0x7fffffff, |
| 0x80000000, |
| 0x80000001, |
| 0xfffffffe, |
| 0xffffffff, |
| }; |
| static const size_t input_count = sizeof(inputs) / sizeof(inputs[0]); |
| |
| struct Expected { |
| uint32_t carry0_result; |
| StatusFlags carry0_flags; |
| uint32_t carry1_result; |
| StatusFlags carry1_flags; |
| }; |
| |
| static const Expected expected_adcs_w[input_count][input_count] = |
| {{{0x00000000, ZFlag, 0x00000001, NoFlag}, |
| {0x00000001, NoFlag, 0x00000002, NoFlag}, |
| {0x7ffffffe, NoFlag, 0x7fffffff, NoFlag}, |
| {0x7fffffff, NoFlag, 0x80000000, NVFlag}, |
| {0x80000000, NFlag, 0x80000001, NFlag}, |
| {0x80000001, NFlag, 0x80000002, NFlag}, |
| {0xfffffffe, NFlag, 0xffffffff, NFlag}, |
| {0xffffffff, NFlag, 0x00000000, ZCFlag}}, |
| {{0x00000001, NoFlag, 0x00000002, NoFlag}, |
| {0x00000002, NoFlag, 0x00000003, NoFlag}, |
| {0x7fffffff, NoFlag, 0x80000000, NVFlag}, |
| {0x80000000, NVFlag, 0x80000001, NVFlag}, |
| {0x80000001, NFlag, 0x80000002, NFlag}, |
| {0x80000002, NFlag, 0x80000003, NFlag}, |
| {0xffffffff, NFlag, 0x00000000, ZCFlag}, |
| {0x00000000, ZCFlag, 0x00000001, CFlag}}, |
| {{0x7ffffffe, NoFlag, 0x7fffffff, NoFlag}, |
| {0x7fffffff, NoFlag, 0x80000000, NVFlag}, |
| {0xfffffffc, NVFlag, 0xfffffffd, NVFlag}, |
| {0xfffffffd, NVFlag, 0xfffffffe, NVFlag}, |
| {0xfffffffe, NFlag, 0xffffffff, NFlag}, |
| {0xffffffff, NFlag, 0x00000000, ZCFlag}, |
| {0x7ffffffc, CFlag, 0x7ffffffd, CFlag}, |
| {0x7ffffffd, CFlag, 0x7ffffffe, CFlag}}, |
| {{0x7fffffff, NoFlag, 0x80000000, NVFlag}, |
| {0x80000000, NVFlag, 0x80000001, NVFlag}, |
| {0xfffffffd, NVFlag, 0xfffffffe, NVFlag}, |
| {0xfffffffe, NVFlag, 0xffffffff, NVFlag}, |
| {0xffffffff, NFlag, 0x00000000, ZCFlag}, |
| {0x00000000, ZCFlag, 0x00000001, CFlag}, |
| {0x7ffffffd, CFlag, 0x7ffffffe, CFlag}, |
| {0x7ffffffe, CFlag, 0x7fffffff, CFlag}}, |
| {{0x80000000, NFlag, 0x80000001, NFlag}, |
| {0x80000001, NFlag, 0x80000002, NFlag}, |
| {0xfffffffe, NFlag, 0xffffffff, NFlag}, |
| {0xffffffff, NFlag, 0x00000000, ZCFlag}, |
| {0x00000000, ZCVFlag, 0x00000001, CVFlag}, |
| {0x00000001, CVFlag, 0x00000002, CVFlag}, |
| {0x7ffffffe, CVFlag, 0x7fffffff, CVFlag}, |
| {0x7fffffff, CVFlag, 0x80000000, NCFlag}}, |
| {{0x80000001, NFlag, 0x80000002, NFlag}, |
| {0x80000002, NFlag, 0x80000003, NFlag}, |
| {0xffffffff, NFlag, 0x00000000, ZCFlag}, |
| {0x00000000, ZCFlag, 0x00000001, CFlag}, |
| {0x00000001, CVFlag, 0x00000002, CVFlag}, |
| {0x00000002, CVFlag, 0x00000003, CVFlag}, |
| {0x7fffffff, CVFlag, 0x80000000, NCFlag}, |
| {0x80000000, NCFlag, 0x80000001, NCFlag}}, |
| {{0xfffffffe, NFlag, 0xffffffff, NFlag}, |
| {0xffffffff, NFlag, 0x00000000, ZCFlag}, |
| {0x7ffffffc, CFlag, 0x7ffffffd, CFlag}, |
| {0x7ffffffd, CFlag, 0x7ffffffe, CFlag}, |
| {0x7ffffffe, CVFlag, 0x7fffffff, CVFlag}, |
| {0x7fffffff, CVFlag, 0x80000000, NCFlag}, |
| {0xfffffffc, NCFlag, 0xfffffffd, NCFlag}, |
| {0xfffffffd, NCFlag, 0xfffffffe, NCFlag}}, |
| {{0xffffffff, NFlag, 0x00000000, ZCFlag}, |
| {0x00000000, ZCFlag, 0x00000001, CFlag}, |
| {0x7ffffffd, CFlag, 0x7ffffffe, CFlag}, |
| {0x7ffffffe, CFlag, 0x7fffffff, CFlag}, |
| {0x7fffffff, CVFlag, 0x80000000, NCFlag}, |
| {0x80000000, NCFlag, 0x80000001, NCFlag}, |
| {0xfffffffd, NCFlag, 0xfffffffe, NCFlag}, |
| {0xfffffffe, NCFlag, 0xffffffff, NCFlag}}}; |
| |
| static const Expected expected_sbcs_w[input_count][input_count] = |
| {{{0xffffffff, NFlag, 0x00000000, ZCFlag}, |
| {0xfffffffe, NFlag, 0xffffffff, NFlag}, |
| {0x80000001, NFlag, 0x80000002, NFlag}, |
| {0x80000000, NFlag, 0x80000001, NFlag}, |
| {0x7fffffff, NoFlag, 0x80000000, NVFlag}, |
| {0x7ffffffe, NoFlag, 0x7fffffff, NoFlag}, |
| {0x00000001, NoFlag, 0x00000002, NoFlag}, |
| {0x00000000, ZFlag, 0x00000001, NoFlag}}, |
| {{0x00000000, ZCFlag, 0x00000001, CFlag}, |
| {0xffffffff, NFlag, 0x00000000, ZCFlag}, |
| {0x80000002, NFlag, 0x80000003, NFlag}, |
| {0x80000001, NFlag, 0x80000002, NFlag}, |
| {0x80000000, NVFlag, 0x80000001, NVFlag}, |
| {0x7fffffff, NoFlag, 0x80000000, NVFlag}, |
| {0x00000002, NoFlag, 0x00000003, NoFlag}, |
| {0x00000001, NoFlag, 0x00000002, NoFlag}}, |
| {{0x7ffffffd, CFlag, 0x7ffffffe, CFlag}, |
| {0x7ffffffc, CFlag, 0x7ffffffd, CFlag}, |
| {0xffffffff, NFlag, 0x00000000, ZCFlag}, |
| {0xfffffffe, NFlag, 0xffffffff, NFlag}, |
| {0xfffffffd, NVFlag, 0xfffffffe, NVFlag}, |
| {0xfffffffc, NVFlag, 0xfffffffd, NVFlag}, |
| {0x7fffffff, NoFlag, 0x80000000, NVFlag}, |
| {0x7ffffffe, NoFlag, 0x7fffffff, NoFlag}}, |
| {{0x7ffffffe, CFlag, 0x7fffffff, CFlag}, |
| {0x7ffffffd, CFlag, 0x7ffffffe, CFlag}, |
| {0x00000000, ZCFlag, 0x00000001, CFlag}, |
| {0xffffffff, NFlag, 0x00000000, ZCFlag}, |
| {0xfffffffe, NVFlag, 0xffffffff, NVFlag}, |
| {0xfffffffd, NVFlag, 0xfffffffe, NVFlag}, |
| {0x80000000, NVFlag, 0x80000001, NVFlag}, |
| {0x7fffffff, NoFlag, 0x80000000, NVFlag}}, |
| {{0x7fffffff, CVFlag, 0x80000000, NCFlag}, |
| {0x7ffffffe, CVFlag, 0x7fffffff, CVFlag}, |
| {0x00000001, CVFlag, 0x00000002, CVFlag}, |
| {0x00000000, ZCVFlag, 0x00000001, CVFlag}, |
| {0xffffffff, NFlag, 0x00000000, ZCFlag}, |
| {0xfffffffe, NFlag, 0xffffffff, NFlag}, |
| {0x80000001, NFlag, 0x80000002, NFlag}, |
| {0x80000000, NFlag, 0x80000001, NFlag}}, |
| {{0x80000000, NCFlag, 0x80000001, NCFlag}, |
| {0x7fffffff, CVFlag, 0x80000000, NCFlag}, |
| {0x00000002, CVFlag, 0x00000003, CVFlag}, |
| {0x00000001, CVFlag, 0x00000002, CVFlag}, |
| {0x00000000, ZCFlag, 0x00000001, CFlag}, |
| {0xffffffff, NFlag, 0x00000000, ZCFlag}, |
| {0x80000002, NFlag, 0x80000003, NFlag}, |
| {0x80000001, NFlag, 0x80000002, NFlag}}, |
| {{0xfffffffd, NCFlag, 0xfffffffe, NCFlag}, |
| {0xfffffffc, NCFlag, 0xfffffffd, NCFlag}, |
| {0x7fffffff, CVFlag, 0x80000000, NCFlag}, |
| {0x7ffffffe, CVFlag, 0x7fffffff, CVFlag}, |
| {0x7ffffffd, CFlag, 0x7ffffffe, CFlag}, |
| {0x7ffffffc, CFlag, 0x7ffffffd, CFlag}, |
| {0xffffffff, NFlag, 0x00000000, ZCFlag}, |
| {0xfffffffe, NFlag, 0xffffffff, NFlag}}, |
| {{0xfffffffe, NCFlag, 0xffffffff, NCFlag}, |
| {0xfffffffd, NCFlag, 0xfffffffe, NCFlag}, |
| {0x80000000, NCFlag, 0x80000001, NCFlag}, |
| {0x7fffffff, CVFlag, 0x80000000, NCFlag}, |
| {0x7ffffffe, CFlag, 0x7fffffff, CFlag}, |
| {0x7ffffffd, CFlag, 0x7ffffffe, CFlag}, |
| {0x00000000, ZCFlag, 0x00000001, CFlag}, |
| {0xffffffff, NFlag, 0x00000000, ZCFlag}}}; |
| |
| for (size_t left = 0; left < input_count; left++) { |
| for (size_t right = 0; right < input_count; right++) { |
| const Expected& expected = expected_adcs_w[left][right]; |
| AdcsSbcsHelper(&MacroAssembler::Adcs, |
| inputs[left], |
| inputs[right], |
| 0, |
| expected.carry0_result, |
| expected.carry0_flags); |
| AdcsSbcsHelper(&MacroAssembler::Adcs, |
| inputs[left], |
| inputs[right], |
| 1, |
| expected.carry1_result, |
| expected.carry1_flags); |
| } |
| } |
| |
| for (size_t left = 0; left < input_count; left++) { |
| for (size_t right = 0; right < input_count; right++) { |
| const Expected& expected = expected_sbcs_w[left][right]; |
| AdcsSbcsHelper(&MacroAssembler::Sbcs, |
| inputs[left], |
| inputs[right], |
| 0, |
| expected.carry0_result, |
| expected.carry0_flags); |
| AdcsSbcsHelper(&MacroAssembler::Sbcs, |
| inputs[left], |
| inputs[right], |
| 1, |
| expected.carry1_result, |
| expected.carry1_flags); |
| } |
| } |
| } |
| |
| |
| TEST(adc_sbc_shift) { |
| SETUP(); |
| |
| START(); |
| __ Mov(x0, 0); |
| __ Mov(x1, 1); |
| __ Mov(x2, 0x0123456789abcdef); |
| __ Mov(x3, 0xfedcba9876543210); |
| __ Mov(x4, 0xffffffffffffffff); |
| |
| // Clear the C flag. |
| __ Adds(x0, x0, Operand(0)); |
| |
| __ Adc(x5, x2, Operand(x3)); |
| __ Adc(x6, x0, Operand(x1, LSL, 60)); |
| __ Sbc(x7, x4, Operand(x3, LSR, 4)); |
| __ Adc(x8, x2, Operand(x3, ASR, 4)); |
| __ Adc(x9, x2, Operand(x3, ROR, 8)); |
| |
| __ Adc(w10, w2, Operand(w3)); |
| __ Adc(w11, w0, Operand(w1, LSL, 30)); |
| __ Sbc(w12, w4, Operand(w3, LSR, 4)); |
| __ Adc(w13, w2, Operand(w3, ASR, 4)); |
| __ Adc(w14, w2, Operand(w3, ROR, 8)); |
| |
| // Set the C flag. |
| __ Cmp(w0, Operand(w0)); |
| |
| __ Adc(x18, x2, Operand(x3)); |
| __ Adc(x19, x0, Operand(x1, LSL, 60)); |
| __ Sbc(x20, x4, Operand(x3, LSR, 4)); |
| __ Adc(x21, x2, Operand(x3, ASR, 4)); |
| __ Adc(x22, x2, Operand(x3, ROR, 8)); |
| |
| __ Adc(w23, w2, Operand(w3)); |
| __ Adc(w24, w0, Operand(w1, LSL, 30)); |
| __ Sbc(w25, w4, Operand(w3, LSR, 4)); |
| __ Adc(w26, w2, Operand(w3, ASR, 4)); |
| __ Adc(w27, w2, Operand(w3, ROR, 8)); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0xffffffffffffffff, x5); |
| ASSERT_EQUAL_64(INT64_C(1) << 60, x6); |
| ASSERT_EQUAL_64(0xf0123456789abcdd, x7); |
| ASSERT_EQUAL_64(0x0111111111111110, x8); |
| ASSERT_EQUAL_64(0x1222222222222221, x9); |
| |
| ASSERT_EQUAL_32(0xffffffff, w10); |
| ASSERT_EQUAL_32(INT32_C(1) << 30, w11); |
| ASSERT_EQUAL_32(0xf89abcdd, w12); |
| ASSERT_EQUAL_32(0x91111110, w13); |
| ASSERT_EQUAL_32(0x9a222221, w14); |
| |
| ASSERT_EQUAL_64(0xffffffffffffffff + 1, x18); |
| ASSERT_EQUAL_64((INT64_C(1) << 60) + 1, x19); |
| ASSERT_EQUAL_64(0xf0123456789abcdd + 1, x20); |
| ASSERT_EQUAL_64(0x0111111111111110 + 1, x21); |
| ASSERT_EQUAL_64(0x1222222222222221 + 1, x22); |
| |
| ASSERT_EQUAL_32(0xffffffff + 1, w23); |
| ASSERT_EQUAL_32((INT32_C(1) << 30) + 1, w24); |
| ASSERT_EQUAL_32(0xf89abcdd + 1, w25); |
| ASSERT_EQUAL_32(0x91111110 + 1, w26); |
| ASSERT_EQUAL_32(0x9a222221 + 1, w27); |
| } |
| } |
| |
| |
| TEST(adc_sbc_extend) { |
| SETUP(); |
| |
| START(); |
| // Clear the C flag. |
| __ Adds(x0, x0, Operand(0)); |
| |
| __ Mov(x0, 0); |
| __ Mov(x1, 1); |
| __ Mov(x2, 0x0123456789abcdef); |
| |
| __ Adc(x10, x1, Operand(w2, UXTB, 1)); |
| __ Adc(x11, x1, Operand(x2, SXTH, 2)); |
| __ Sbc(x12, x1, Operand(w2, UXTW, 4)); |
| __ Adc(x13, x1, Operand(x2, UXTX, 4)); |
| |
| __ Adc(w14, w1, Operand(w2, UXTB, 1)); |
| __ Adc(w15, w1, Operand(w2, SXTH, 2)); |
| __ Adc(w9, w1, Operand(w2, UXTW, 4)); |
| |
| // Set the C flag. |
| __ Cmp(w0, Operand(w0)); |
| |
| __ Adc(x20, x1, Operand(w2, UXTB, 1)); |
| __ Adc(x21, x1, Operand(x2, SXTH, 2)); |
| __ Sbc(x22, x1, Operand(w2, UXTW, 4)); |
| __ Adc(x23, x1, Operand(x2, UXTX, 4)); |
| |
| __ Adc(w24, w1, Operand(w2, UXTB, 1)); |
| __ Adc(w25, w1, Operand(w2, SXTH, 2)); |
| __ Adc(w26, w1, Operand(w2, UXTW, 4)); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0x1df, x10); |
| ASSERT_EQUAL_64(0xffffffffffff37bd, x11); |
| ASSERT_EQUAL_64(0xfffffff765432110, x12); |
| ASSERT_EQUAL_64(0x123456789abcdef1, x13); |
| |
| ASSERT_EQUAL_32(0x1df, w14); |
| ASSERT_EQUAL_32(0xffff37bd, w15); |
| ASSERT_EQUAL_32(0x9abcdef1, w9); |
| |
| ASSERT_EQUAL_64(0x1df + 1, x20); |
| ASSERT_EQUAL_64(0xffffffffffff37bd + 1, x21); |
| ASSERT_EQUAL_64(0xfffffff765432110 + 1, x22); |
| ASSERT_EQUAL_64(0x123456789abcdef1 + 1, x23); |
| |
| ASSERT_EQUAL_32(0x1df + 1, w24); |
| ASSERT_EQUAL_32(0xffff37bd + 1, w25); |
| ASSERT_EQUAL_32(0x9abcdef1 + 1, w26); |
| } |
| |
| // Check that adc correctly sets the condition flags. |
| START(); |
| __ Mov(x0, 0xff); |
| __ Mov(x1, 0xffffffffffffffff); |
| // Clear the C flag. |
| __ Adds(x0, x0, Operand(0)); |
| __ Adcs(x10, x0, Operand(x1, SXTX, 1)); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_NZCV(CFlag); |
| } |
| |
| START(); |
| __ Mov(x0, 0x7fffffffffffffff); |
| __ Mov(x1, 1); |
| // Clear the C flag. |
| __ Adds(x0, x0, Operand(0)); |
| __ Adcs(x10, x0, Operand(x1, UXTB, 2)); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_NZCV(NVFlag); |
| } |
| |
| START(); |
| __ Mov(x0, 0x7fffffffffffffff); |
| // Clear the C flag. |
| __ Adds(x0, x0, Operand(0)); |
| __ Adcs(x10, x0, Operand(1)); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_NZCV(NVFlag); |
| } |
| } |
| |
| |
| TEST(adc_sbc_wide_imm) { |
| SETUP(); |
| |
| START(); |
| __ Mov(x0, 0); |
| |
| // Clear the C flag. |
| __ Adds(x0, x0, Operand(0)); |
| |
| __ Adc(x7, x0, Operand(0x1234567890abcdef)); |
| __ Adc(w8, w0, Operand(0xffffffff)); |
| __ Sbc(x9, x0, Operand(0x1234567890abcdef)); |
| __ Sbc(w10, w0, Operand(0xffffffff)); |
| __ Ngc(x11, Operand(0xffffffff00000000)); |
| __ Ngc(w12, Operand(0xffff0000)); |
| |
| // Set the C flag. |
| __ Cmp(w0, Operand(w0)); |
| |
| __ Adc(x18, x0, Operand(0x1234567890abcdef)); |
| __ Adc(w19, w0, Operand(0xffffffff)); |
| __ Sbc(x20, x0, Operand(0x1234567890abcdef)); |
| __ Sbc(w21, w0, Operand(0xffffffff)); |
| __ Ngc(x22, Operand(0xffffffff00000000)); |
| __ Ngc(w23, Operand(0xffff0000)); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0x1234567890abcdef, x7); |
| ASSERT_EQUAL_64(0xffffffff, x8); |
| ASSERT_EQUAL_64(0xedcba9876f543210, x9); |
| ASSERT_EQUAL_64(0, x10); |
| ASSERT_EQUAL_64(0xffffffff, x11); |
| ASSERT_EQUAL_64(0xffff, x12); |
| |
| ASSERT_EQUAL_64(0x1234567890abcdef + 1, x18); |
| ASSERT_EQUAL_64(0, x19); |
| ASSERT_EQUAL_64(0xedcba9876f543211, x20); |
| ASSERT_EQUAL_64(1, x21); |
| ASSERT_EQUAL_64(0x0000000100000000, x22); |
| ASSERT_EQUAL_64(0x0000000000010000, x23); |
| } |
| } |
| |
| |
| TEST(rmif) { |
| SETUP_WITH_FEATURES(CPUFeatures::kFlagM); |
| |
| START(); |
| __ Mov(x0, 0x0123456789abcdef); |
| |
| // Set NZCV to 0b1011 (0xb) |
| __ Rmif(x0, 0, NCVFlag); |
| __ Mrs(x1, NZCV); |
| |
| // Set NZCV to 0b0111 (0x7) |
| __ Rmif(x0, 6, NZCVFlag); |
| __ Mrs(x2, NZCV); |
| |
| // Set Z to 0, NZCV = 0b0011 (0x3) |
| __ Rmif(x0, 60, ZFlag); |
| __ Mrs(x3, NZCV); |
| |
| // Set N to 1 and C to 0, NZCV = 0b1001 (0x9) |
| __ Rmif(x0, 62, NCFlag); |
| __ Mrs(x4, NZCV); |
| |
| // No change to NZCV |
| __ Rmif(x0, 0, NoFlag); |
| __ Mrs(x5, NZCV); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| ASSERT_EQUAL_32(NCVFlag, w1); |
| ASSERT_EQUAL_32(ZCVFlag, w2); |
| ASSERT_EQUAL_32(CVFlag, w3); |
| ASSERT_EQUAL_32(NVFlag, w4); |
| ASSERT_EQUAL_32(NVFlag, w5); |
| } |
| } |
| |
| |
| TEST(setf8_setf16) { |
| SETUP_WITH_FEATURES(CPUFeatures::kFlagM); |
| |
| START(); |
| __ Mov(x0, 0x0); |
| __ Mov(x1, 0x1); |
| __ Mov(x2, 0xff); |
| __ Mov(x3, 0x100); |
| __ Mov(x4, 0x101); |
| __ Mov(x5, 0xffff); |
| __ Mov(x6, 0x10000); |
| __ Mov(x7, 0x10001); |
| __ Mov(x8, 0xfffffffff); |
| |
| __ Setf8(w0); |
| __ Mrs(x9, NZCV); |
| __ Setf8(w1); |
| __ Mrs(x10, NZCV); |
| __ Setf8(w2); |
| __ Mrs(x11, NZCV); |
| __ Setf8(w3); |
| __ Mrs(x12, NZCV); |
| __ Setf8(w4); |
| __ Mrs(x13, NZCV); |
| __ Setf8(w8); |
| __ Mrs(x14, NZCV); |
| |
| __ Setf16(w0); |
| __ Mrs(x15, NZCV); |
| __ Setf16(w1); |
| __ Mrs(x16, NZCV); |
| __ Setf16(w5); |
| __ Mrs(x17, NZCV); |
| __ Setf16(w6); |
| __ Mrs(x18, NZCV); |
| __ Setf16(w7); |
| __ Mrs(x19, NZCV); |
| __ Setf16(w8); |
| __ Mrs(x20, NZCV); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_32(ZFlag, w9); // Zero |
| ASSERT_EQUAL_32(NoFlag, w10); // Regular int8 |
| ASSERT_EQUAL_32(NVFlag, w11); // Negative but not sign-extended (overflow) |
| ASSERT_EQUAL_32(ZVFlag, w12); // Overflow with zero remainder |
| ASSERT_EQUAL_32(VFlag, w13); // Overflow with non-zero remainder |
| ASSERT_EQUAL_32(NFlag, w14); // Negative and sign-extended |
| |
| ASSERT_EQUAL_32(ZFlag, w15); // Zero |
| ASSERT_EQUAL_32(NoFlag, w16); // Regular int16 |
| ASSERT_EQUAL_32(NVFlag, w17); // Negative but not sign-extended (overflow) |
| ASSERT_EQUAL_32(ZVFlag, w18); // Overflow with zero remainder |
| ASSERT_EQUAL_32(VFlag, w19); // Overflow with non-zero remainder |
| ASSERT_EQUAL_32(NFlag, w20); // Negative and sign-extended |
| } |
| } |
| |
| |
| TEST(flags) { |
| SETUP(); |
| |
| START(); |
| __ Mov(x0, 0); |
| __ Mov(x1, 0x1111111111111111); |
| __ Neg(x10, Operand(x0)); |
| __ Neg(x11, Operand(x1)); |
| __ Neg(w12, Operand(w1)); |
| // Clear the C flag. |
| __ Adds(x0, x0, Operand(0)); |
| __ Ngc(x13, Operand(x0)); |
| // Set the C flag. |
| __ Cmp(x0, Operand(x0)); |
| __ Ngc(w14, Operand(w0)); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0, x10); |
| ASSERT_EQUAL_64(-0x1111111111111111, x11); |
| ASSERT_EQUAL_32(-0x11111111, w12); |
| ASSERT_EQUAL_64(-1, x13); |
| ASSERT_EQUAL_32(0, w14); |
| } |
| |
| START(); |
| __ Mov(x0, 0); |
| __ Cmp(x0, Operand(x0)); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_NZCV(ZCFlag); |
| } |
| |
| START(); |
| __ Mov(w0, 0); |
| __ Cmp(w0, Operand(w0)); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_NZCV(ZCFlag); |
| } |
| |
| START(); |
| __ Mov(x0, 0); |
| __ Mov(x1, 0x1111111111111111); |
| __ Cmp(x0, Operand(x1)); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_NZCV(NFlag); |
| } |
| |
| START(); |
| __ Mov(w0, 0); |
| __ Mov(w1, 0x11111111); |
| __ Cmp(w0, Operand(w1)); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_NZCV(NFlag); |
| } |
| |
| START(); |
| __ Mov(x1, 0x1111111111111111); |
| __ Cmp(x1, Operand(0)); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_NZCV(CFlag); |
| } |
| |
| START(); |
| __ Mov(w1, 0x11111111); |
| __ Cmp(w1, Operand(0)); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_NZCV(CFlag); |
| } |
| |
| START(); |
| __ Mov(x0, 1); |
| __ Mov(x1, 0x7fffffffffffffff); |
| __ Cmn(x1, Operand(x0)); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_NZCV(NVFlag); |
| } |
| |
| START(); |
| __ Mov(w0, 1); |
| __ Mov(w1, 0x7fffffff); |
| __ Cmn(w1, Operand(w0)); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_NZCV(NVFlag); |
| } |
| |
| START(); |
| __ Mov(x0, 1); |
| __ Mov(x1, 0xffffffffffffffff); |
| __ Cmn(x1, Operand(x0)); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_NZCV(ZCFlag); |
| } |
| |
| START(); |
| __ Mov(w0, 1); |
| __ Mov(w1, 0xffffffff); |
| __ Cmn(w1, Operand(w0)); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_NZCV(ZCFlag); |
| } |
| |
| START(); |
| __ Mov(w0, 0); |
| __ Mov(w1, 1); |
| // Clear the C flag. |
| __ Adds(w0, w0, Operand(0)); |
| __ Ngcs(w0, Operand(w1)); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_NZCV(NFlag); |
| } |
| |
| START(); |
| __ Mov(w0, 0); |
| __ Mov(w1, 0); |
| // Set the C flag. |
| __ Cmp(w0, Operand(w0)); |
| __ Ngcs(w0, Operand(w1)); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_NZCV(ZCFlag); |
| } |
| } |
| |
| |
| TEST(cmp_shift) { |
| SETUP(); |
| |
| START(); |
| __ Mov(x18, 0xf0000000); |
| __ Mov(x19, 0xf000000010000000); |
| __ Mov(x20, 0xf0000000f0000000); |
| __ Mov(x21, 0x7800000078000000); |
| __ Mov(x22, 0x3c0000003c000000); |
| __ Mov(x23, 0x8000000780000000); |
| __ Mov(x24, 0x0000000f00000000); |
| __ Mov(x25, 0x00000003c0000000); |
| __ Mov(x26, 0x8000000780000000); |
| __ Mov(x27, 0xc0000003); |
| |
| __ Cmp(w20, Operand(w21, LSL, 1)); |
| __ Mrs(x0, NZCV); |
| |
| __ Cmp(x20, Operand(x22, LSL, 2)); |
| __ Mrs(x1, NZCV); |
| |
| __ Cmp(w19, Operand(w23, LSR, 3)); |
| __ Mrs(x2, NZCV); |
| |
| __ Cmp(x18, Operand(x24, LSR, 4)); |
| __ Mrs(x3, NZCV); |
| |
| __ Cmp(w20, Operand(w25, ASR, 2)); |
| __ Mrs(x4, NZCV); |
| |
| __ Cmp(x20, Operand(x26, ASR, 3)); |
| __ Mrs(x5, NZCV); |
| |
| __ Cmp(w27, Operand(w22, ROR, 28)); |
| __ Mrs(x6, NZCV); |
| |
| __ Cmp(x20, Operand(x21, ROR, 31)); |
| __ Mrs(x7, NZCV); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_32(ZCFlag, w0); |
| ASSERT_EQUAL_32(ZCFlag, w1); |
| ASSERT_EQUAL_32(ZCFlag, w2); |
| ASSERT_EQUAL_32(ZCFlag, w3); |
| ASSERT_EQUAL_32(ZCFlag, w4); |
| ASSERT_EQUAL_32(ZCFlag, w5); |
| ASSERT_EQUAL_32(ZCFlag, w6); |
| ASSERT_EQUAL_32(ZCFlag, w7); |
| } |
| } |
| |
| |
| TEST(cmp_extend) { |
| SETUP(); |
| |
| START(); |
| __ Mov(w20, 0x2); |
| __ Mov(w21, 0x1); |
| __ Mov(x22, 0xffffffffffffffff); |
| __ Mov(x23, 0xff); |
| __ Mov(x24, 0xfffffffffffffffe); |
| __ Mov(x25, 0xffff); |
| __ Mov(x26, 0xffffffff); |
| |
| __ Cmp(w20, Operand(w21, LSL, 1)); |
| __ Mrs(x0, NZCV); |
| |
| __ Cmp(x22, Operand(x23, SXTB, 0)); |
| __ Mrs(x1, NZCV); |
| |
| __ Cmp(x24, Operand(x23, SXTB, 1)); |
| __ Mrs(x2, NZCV); |
| |
| __ Cmp(x24, Operand(x23, UXTB, 1)); |
| __ Mrs(x3, NZCV); |
| |
| __ Cmp(w22, Operand(w25, UXTH)); |
| __ Mrs(x4, NZCV); |
| |
| __ Cmp(x22, Operand(x25, SXTH)); |
| __ Mrs(x5, NZCV); |
| |
| __ Cmp(x22, Operand(x26, UXTW)); |
| __ Mrs(x6, NZCV); |
| |
| __ Cmp(x24, Operand(x26, SXTW, 1)); |
| __ Mrs(x7, NZCV); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_32(ZCFlag, w0); |
| ASSERT_EQUAL_32(ZCFlag, w1); |
| ASSERT_EQUAL_32(ZCFlag, w2); |
| ASSERT_EQUAL_32(NCFlag, w3); |
| ASSERT_EQUAL_32(NCFlag, w4); |
| ASSERT_EQUAL_32(ZCFlag, w5); |
| ASSERT_EQUAL_32(NCFlag, w6); |
| ASSERT_EQUAL_32(ZCFlag, w7); |
| } |
| } |
| |
| |
| TEST(ccmp) { |
| SETUP(); |
| |
| START(); |
| __ Mov(w16, 0); |
| __ Mov(w17, 1); |
| __ Cmp(w16, w16); |
| __ Ccmp(w16, w17, NCFlag, eq); |
| __ Mrs(x0, NZCV); |
| |
| __ Cmp(w16, w16); |
| __ Ccmp(w16, w17, NCFlag, ne); |
| __ Mrs(x1, NZCV); |
| |
| __ Cmp(x16, x16); |
| __ Ccmn(x16, 2, NZCVFlag, eq); |
| __ Mrs(x2, NZCV); |
| |
| __ Cmp(x16, x16); |
| __ Ccmn(x16, 2, NZCVFlag, ne); |
| __ Mrs(x3, NZCV); |
| |
| // The MacroAssembler does not allow al as a condition. |
| { |
| ExactAssemblyScope scope(&masm, kInstructionSize); |
| __ ccmp(x16, x16, NZCVFlag, al); |
| } |
| __ Mrs(x4, NZCV); |
| |
| // The MacroAssembler does not allow nv as a condition. |
| { |
| ExactAssemblyScope scope(&masm, kInstructionSize); |
| __ ccmp(x16, x16, NZCVFlag, nv); |
| } |
| __ Mrs(x5, NZCV); |
| |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_32(NFlag, w0); |
| ASSERT_EQUAL_32(NCFlag, w1); |
| ASSERT_EQUAL_32(NoFlag, w2); |
| ASSERT_EQUAL_32(NZCVFlag, w3); |
| ASSERT_EQUAL_32(ZCFlag, w4); |
| ASSERT_EQUAL_32(ZCFlag, w5); |
| } |
| } |
| |
| |
| TEST(ccmp_wide_imm) { |
| SETUP(); |
| |
| START(); |
| __ Mov(w20, 0); |
| |
| __ Cmp(w20, Operand(w20)); |
| __ Ccmp(w20, Operand(0x12345678), NZCVFlag, eq); |
| __ Mrs(x0, NZCV); |
| |
| __ Cmp(w20, Operand(w20)); |
| __ Ccmp(x20, Operand(0xffffffffffffffff), NZCVFlag, eq); |
| __ Mrs(x1, NZCV); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_32(NFlag, w0); |
| ASSERT_EQUAL_32(NoFlag, w1); |
| } |
| } |
| |
| |
| TEST(ccmp_shift_extend) { |
| SETUP(); |
| |
| START(); |
| __ Mov(w20, 0x2); |
| __ Mov(w21, 0x1); |
| __ Mov(x22, 0xffffffffffffffff); |
| __ Mov(x23, 0xff); |
| __ Mov(x24, 0xfffffffffffffffe); |
| |
| __ Cmp(w20, Operand(w20)); |
| __ Ccmp(w20, Operand(w21, LSL, 1), NZCVFlag, eq); |
| __ Mrs(x0, NZCV); |
| |
| __ Cmp(w20, Operand(w20)); |
| __ Ccmp(x22, Operand(x23, SXTB, 0), NZCVFlag, eq); |
| __ Mrs(x1, NZCV); |
| |
| __ Cmp(w20, Operand(w20)); |
| __ Ccmp(x24, Operand(x23, SXTB, 1), NZCVFlag, eq); |
| __ Mrs(x2, NZCV); |
| |
| __ Cmp(w20, Operand(w20)); |
| __ Ccmp(x24, Operand(x23, UXTB, 1), NZCVFlag, eq); |
| __ Mrs(x3, NZCV); |
| |
| __ Cmp(w20, Operand(w20)); |
| __ Ccmp(x24, Operand(x23, UXTB, 1), NZCVFlag, ne); |
| __ Mrs(x4, NZCV); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_32(ZCFlag, w0); |
| ASSERT_EQUAL_32(ZCFlag, w1); |
| ASSERT_EQUAL_32(ZCFlag, w2); |
| ASSERT_EQUAL_32(NCFlag, w3); |
| ASSERT_EQUAL_32(NZCVFlag, w4); |
| } |
| } |
| |
| |
| TEST(csel_reg) { |
| SETUP(); |
| |
| START(); |
| __ Mov(x16, 0); |
| __ Mov(x24, 0x0000000f0000000f); |
| __ Mov(x25, 0x0000001f0000001f); |
| |
| __ Cmp(w16, Operand(0)); |
| __ Csel(w0, w24, w25, eq); |
| __ Csel(w1, w24, w25, ne); |
| __ Csinc(w2, w24, w25, mi); |
| __ Csinc(w3, w24, w25, pl); |
| |
| // The MacroAssembler does not allow al or nv as a condition. |
| { |
| ExactAssemblyScope scope(&masm, 2 * kInstructionSize); |
| __ csel(w13, w24, w25, al); |
| __ csel(x14, x24, x25, nv); |
| } |
| |
| __ Cmp(x16, Operand(1)); |
| __ Csinv(x4, x24, x25, gt); |
| __ Csinv(x5, x24, x25, le); |
| __ Csneg(x6, x24, x25, hs); |
| __ Csneg(x7, x24, x25, lo); |
| |
| __ Cset(w8, ne); |
| __ Csetm(w9, ne); |
| __ Cinc(x10, x25, ne); |
| __ Cinv(x11, x24, ne); |
| __ Cneg(x12, x24, ne); |
| |
| // The MacroAssembler does not allow al or nv as a condition. |
| { |
| ExactAssemblyScope scope(&masm, 2 * kInstructionSize); |
| __ csel(w15, w24, w25, al); |
| __ csel(x17, x24, x25, nv); |
| } |
| |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0x0000000f, x0); |
| ASSERT_EQUAL_64(0x0000001f, x1); |
| ASSERT_EQUAL_64(0x00000020, x2); |
| ASSERT_EQUAL_64(0x0000000f, x3); |
| ASSERT_EQUAL_64(0xffffffe0ffffffe0, x4); |
| ASSERT_EQUAL_64(0x0000000f0000000f, x5); |
| ASSERT_EQUAL_64(0xffffffe0ffffffe1, x6); |
| ASSERT_EQUAL_64(0x0000000f0000000f, x7); |
| ASSERT_EQUAL_64(0x00000001, x8); |
| ASSERT_EQUAL_64(0xffffffff, x9); |
| ASSERT_EQUAL_64(0x0000001f00000020, x10); |
| ASSERT_EQUAL_64(0xfffffff0fffffff0, x11); |
| ASSERT_EQUAL_64(0xfffffff0fffffff1, x12); |
| ASSERT_EQUAL_64(0x0000000f, x13); |
| ASSERT_EQUAL_64(0x0000000f0000000f, x14); |
| ASSERT_EQUAL_64(0x0000000f, x15); |
| ASSERT_EQUAL_64(0x0000000f0000000f, x17); |
| } |
| } |
| |
| TEST(csel_zero) { |
| SETUP(); |
| |
| START(); |
| |
| __ Mov(x15, 0x0); |
| __ Mov(x16, 0x0000001f0000002f); |
| |
| // Check results when zero registers are used as inputs |
| // for Csinc, Csinv and Csneg for both true and false conditions. |
| __ Cmp(x15, 0); |
| __ Csinc(x0, x16, xzr, eq); |
| __ Csinc(x1, xzr, x16, eq); |
| __ Cmp(x15, 1); |
| __ Csinc(w2, w16, wzr, eq); |
| __ Csinc(w3, wzr, w16, eq); |
| |
| __ Csinc(x4, xzr, xzr, eq); |
| |
| __ Cmp(x15, 0); |
| __ Csinv(x5, x16, xzr, eq); |
| __ Csinv(x6, xzr, x16, eq); |
| __ Cmp(x15, 1); |
| __ Csinv(w7, w16, wzr, eq); |
| __ Csinv(w8, wzr, w16, eq); |
| |
| __ Csinv(x9, xzr, xzr, eq); |
| |
| __ Cmp(x15, 0); |
| __ Csneg(x10, x16, xzr, eq); |
| __ Csneg(x11, xzr, x16, eq); |
| __ Cmp(x15, 1); |
| __ Csneg(w12, w16, wzr, eq); |
| __ Csneg(w13, wzr, w16, eq); |
| |
| __ Csneg(x14, xzr, xzr, eq); |
| |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0x0000001f0000002f, x0); |
| ASSERT_EQUAL_64(0x0, x1); |
| ASSERT_EQUAL_32(0x1, w2); |
| ASSERT_EQUAL_32(0x30, w3); |
| ASSERT_EQUAL_64(0x1, x4); |
| ASSERT_EQUAL_64(0x0000001f0000002f, x5); |
| ASSERT_EQUAL_64(0x0, x6); |
| ASSERT_EQUAL_32(0xffffffff, w7); |
| ASSERT_EQUAL_32(0xffffffd0, w8); |
| ASSERT_EQUAL_64(0xffffffffffffffff, x9); |
| ASSERT_EQUAL_64(0x0000001f0000002f, x10); |
| ASSERT_EQUAL_64(0x0, x11); |
| ASSERT_EQUAL_32(0x0, w12); |
| ASSERT_EQUAL_32(0xffffffd1, w13); |
| ASSERT_EQUAL_64(0x0, x14); |
| } |
| } |
| |
| |
| TEST(csel_imm) { |
| SETUP(); |
| |
| int values[] = {-123, -2, -1, 0, 1, 2, 123}; |
| int n_values = sizeof(values) / sizeof(values[0]); |
| |
| for (int i = 0; i < n_values; i++) { |
| for (int j = 0; j < n_values; j++) { |
| int left = values[i]; |
| int right = values[j]; |
| |
| START(); |
| __ Mov(x10, 0); |
| __ Cmp(x10, 0); |
| __ Csel(w0, left, right, eq); |
| __ Csel(w1, left, right, ne); |
| __ Csel(x2, left, right, eq); |
| __ Csel(x3, left, right, ne); |
| |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_32(left, w0); |
| ASSERT_EQUAL_32(right, w1); |
| ASSERT_EQUAL_64(left, x2); |
| ASSERT_EQUAL_64(right, x3); |
| } |
| } |
| } |
| } |
| |
| |
| TEST(csel_mixed) { |
| SETUP(); |
| |
| START(); |
| __ Mov(x18, 0); |
| __ Mov(x19, 0x80000000); |
| __ Mov(x20, 0x8000000000000000); |
| |
| __ Cmp(x18, Operand(0)); |
| __ Csel(w0, w19, -2, ne); |
| __ Csel(w1, w19, -1, ne); |
| __ Csel(w2, w19, 0, ne); |
| __ Csel(w3, w19, 1, ne); |
| __ Csel(w4, w19, 2, ne); |
| __ Csel(w5, w19, Operand(w19, ASR, 31), ne); |
| __ Csel(w6, w19, Operand(w19, ROR, 1), ne); |
| __ Csel(w7, w19, 3, eq); |
| |
| __ Csel(x8, x20, -2, ne); |
| __ Csel(x9, x20, -1, ne); |
| __ Csel(x10, x20, 0, ne); |
| __ Csel(x11, x20, 1, ne); |
| __ Csel(x12, x20, 2, ne); |
| __ Csel(x13, x20, Operand(x20, ASR, 63), ne); |
| __ Csel(x14, x20, Operand(x20, ROR, 1), ne); |
| __ Csel(x15, x20, 3, eq); |
| |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_32(-2, w0); |
| ASSERT_EQUAL_32(-1, w1); |
| ASSERT_EQUAL_32(0, w2); |
| ASSERT_EQUAL_32(1, w3); |
| ASSERT_EQUAL_32(2, w4); |
| ASSERT_EQUAL_32(-1, w5); |
| ASSERT_EQUAL_32(0x40000000, w6); |
| ASSERT_EQUAL_32(0x80000000, w7); |
| |
| ASSERT_EQUAL_64(-2, x8); |
| ASSERT_EQUAL_64(-1, x9); |
| ASSERT_EQUAL_64(0, x10); |
| ASSERT_EQUAL_64(1, x11); |
| ASSERT_EQUAL_64(2, x12); |
| ASSERT_EQUAL_64(-1, x13); |
| ASSERT_EQUAL_64(0x4000000000000000, x14); |
| ASSERT_EQUAL_64(0x8000000000000000, x15); |
| } |
| } |
| |
| |
| TEST(lslv) { |
| SETUP(); |
| |
| uint64_t value = 0x0123456789abcdef; |
| int shift[] = {1, 3, 5, 9, 17, 33}; |
| |
| START(); |
| __ Mov(x0, value); |
| __ Mov(w1, shift[0]); |
| __ Mov(w2, shift[1]); |
| __ Mov(w3, shift[2]); |
| __ Mov(w4, shift[3]); |
| __ Mov(w5, shift[4]); |
| __ Mov(w6, shift[5]); |
| |
| // The MacroAssembler does not allow zr as an argument. |
| { |
| ExactAssemblyScope scope(&masm, kInstructionSize); |
| __ lslv(x0, x0, xzr); |
| } |
| |
| __ Lsl(x16, x0, x1); |
| __ Lsl(x17, x0, x2); |
| __ Lsl(x18, x0, x3); |
| __ Lsl(x19, x0, x4); |
| __ Lsl(x20, x0, x5); |
| __ Lsl(x21, x0, x6); |
| |
| __ Lsl(w22, w0, w1); |
| __ Lsl(w23, w0, w2); |
| __ Lsl(w24, w0, w3); |
| __ Lsl(w25, w0, w4); |
| __ Lsl(w26, w0, w5); |
| __ Lsl(w27, w0, w6); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(value, x0); |
| ASSERT_EQUAL_64(value << (shift[0] & 63), x16); |
| ASSERT_EQUAL_64(value << (shift[1] & 63), x17); |
| ASSERT_EQUAL_64(value << (shift[2] & 63), x18); |
| ASSERT_EQUAL_64(value << (shift[3] & 63), x19); |
| ASSERT_EQUAL_64(value << (shift[4] & 63), x20); |
| ASSERT_EQUAL_64(value << (shift[5] & 63), x21); |
| ASSERT_EQUAL_32(value << (shift[0] & 31), w22); |
| ASSERT_EQUAL_32(value << (shift[1] & 31), w23); |
| ASSERT_EQUAL_32(value << (shift[2] & 31), w24); |
| ASSERT_EQUAL_32(value << (shift[3] & 31), w25); |
| ASSERT_EQUAL_32(value << (shift[4] & 31), w26); |
| ASSERT_EQUAL_32(value << (shift[5] & 31), w27); |
| } |
| } |
| |
| |
| TEST(lsrv) { |
| SETUP(); |
| |
| uint64_t value = 0x0123456789abcdef; |
| int shift[] = {1, 3, 5, 9, 17, 33}; |
| |
| START(); |
| __ Mov(x0, value); |
| __ Mov(w1, shift[0]); |
| __ Mov(w2, shift[1]); |
| __ Mov(w3, shift[2]); |
| __ Mov(w4, shift[3]); |
| __ Mov(w5, shift[4]); |
| __ Mov(w6, shift[5]); |
| |
| // The MacroAssembler does not allow zr as an argument. |
| { |
| ExactAssemblyScope scope(&masm, kInstructionSize); |
| __ lsrv(x0, x0, xzr); |
| } |
| |
| __ Lsr(x16, x0, x1); |
| __ Lsr(x17, x0, x2); |
| __ Lsr(x18, x0, x3); |
| __ Lsr(x19, x0, x4); |
| __ Lsr(x20, x0, x5); |
| __ Lsr(x21, x0, x6); |
| |
| __ Lsr(w22, w0, w1); |
| __ Lsr(w23, w0, w2); |
| __ Lsr(w24, w0, w3); |
| __ Lsr(w25, w0, w4); |
| __ Lsr(w26, w0, w5); |
| __ Lsr(w27, w0, w6); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(value, x0); |
| ASSERT_EQUAL_64(value >> (shift[0] & 63), x16); |
| ASSERT_EQUAL_64(value >> (shift[1] & 63), x17); |
| ASSERT_EQUAL_64(value >> (shift[2] & 63), x18); |
| ASSERT_EQUAL_64(value >> (shift[3] & 63), x19); |
| ASSERT_EQUAL_64(value >> (shift[4] & 63), x20); |
| ASSERT_EQUAL_64(value >> (shift[5] & 63), x21); |
| |
| value &= 0xffffffff; |
| ASSERT_EQUAL_32(value >> (shift[0] & 31), w22); |
| ASSERT_EQUAL_32(value >> (shift[1] & 31), w23); |
| ASSERT_EQUAL_32(value >> (shift[2] & 31), w24); |
| ASSERT_EQUAL_32(value >> (shift[3] & 31), w25); |
| ASSERT_EQUAL_32(value >> (shift[4] & 31), w26); |
| ASSERT_EQUAL_32(value >> (shift[5] & 31), w27); |
| } |
| } |
| |
| |
| TEST(asrv) { |
| SETUP(); |
| |
| int64_t value = 0xfedcba98fedcba98; |
| int shift[] = {1, 3, 5, 9, 17, 33}; |
| |
| START(); |
| __ Mov(x0, value); |
| __ Mov(w1, shift[0]); |
| __ Mov(w2, shift[1]); |
| __ Mov(w3, shift[2]); |
| __ Mov(w4, shift[3]); |
| __ Mov(w5, shift[4]); |
| __ Mov(w6, shift[5]); |
| |
| // The MacroAssembler does not allow zr as an argument. |
| { |
| ExactAssemblyScope scope(&masm, kInstructionSize); |
| __ asrv(x0, x0, xzr); |
| } |
| |
| __ Asr(x16, x0, x1); |
| __ Asr(x17, x0, x2); |
| __ Asr(x18, x0, x3); |
| __ Asr(x19, x0, x4); |
| __ Asr(x20, x0, x5); |
| __ Asr(x21, x0, x6); |
| |
| __ Asr(w22, w0, w1); |
| __ Asr(w23, w0, w2); |
| __ Asr(w24, w0, w3); |
| __ Asr(w25, w0, w4); |
| __ Asr(w26, w0, w5); |
| __ Asr(w27, w0, w6); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(value, x0); |
| ASSERT_EQUAL_64(value >> (shift[0] & 63), x16); |
| ASSERT_EQUAL_64(value >> (shift[1] & 63), x17); |
| ASSERT_EQUAL_64(value >> (shift[2] & 63), x18); |
| ASSERT_EQUAL_64(value >> (shift[3] & 63), x19); |
| ASSERT_EQUAL_64(value >> (shift[4] & 63), x20); |
| ASSERT_EQUAL_64(value >> (shift[5] & 63), x21); |
| |
| int32_t value32 = static_cast<int32_t>(value & 0xffffffff); |
| ASSERT_EQUAL_32(value32 >> (shift[0] & 31), w22); |
| ASSERT_EQUAL_32(value32 >> (shift[1] & 31), w23); |
| ASSERT_EQUAL_32(value32 >> (shift[2] & 31), w24); |
| ASSERT_EQUAL_32(value32 >> (shift[3] & 31), w25); |
| ASSERT_EQUAL_32(value32 >> (shift[4] & 31), w26); |
| ASSERT_EQUAL_32(value32 >> (shift[5] & 31), w27); |
| } |
| } |
| |
| |
| TEST(rorv) { |
| SETUP(); |
| |
| uint64_t value = 0x0123456789abcdef; |
| int shift[] = {4, 8, 12, 16, 24, 36}; |
| |
| START(); |
| __ Mov(x0, value); |
| __ Mov(w1, shift[0]); |
| __ Mov(w2, shift[1]); |
| __ Mov(w3, shift[2]); |
| __ Mov(w4, shift[3]); |
| __ Mov(w5, shift[4]); |
| __ Mov(w6, shift[5]); |
| |
| // The MacroAssembler does not allow zr as an argument. |
| { |
| ExactAssemblyScope scope(&masm, kInstructionSize); |
| __ rorv(x0, x0, xzr); |
| } |
| |
| __ Ror(x16, x0, x1); |
| __ Ror(x17, x0, x2); |
| __ Ror(x18, x0, x3); |
| __ Ror(x19, x0, x4); |
| __ Ror(x20, x0, x5); |
| __ Ror(x21, x0, x6); |
| |
| __ Ror(w22, w0, w1); |
| __ Ror(w23, w0, w2); |
| __ Ror(w24, w0, w3); |
| __ Ror(w25, w0, w4); |
| __ Ror(w26, w0, w5); |
| __ Ror(w27, w0, w6); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(value, x0); |
| ASSERT_EQUAL_64(0xf0123456789abcde, x16); |
| ASSERT_EQUAL_64(0xef0123456789abcd, x17); |
| ASSERT_EQUAL_64(0xdef0123456789abc, x18); |
| ASSERT_EQUAL_64(0xcdef0123456789ab, x19); |
| ASSERT_EQUAL_64(0xabcdef0123456789, x20); |
| ASSERT_EQUAL_64(0x789abcdef0123456, x21); |
| ASSERT_EQUAL_32(0xf89abcde, w22); |
| ASSERT_EQUAL_32(0xef89abcd, w23); |
| ASSERT_EQUAL_32(0xdef89abc, w24); |
| ASSERT_EQUAL_32(0xcdef89ab, w25); |
| ASSERT_EQUAL_32(0xabcdef89, w26); |
| ASSERT_EQUAL_32(0xf89abcde, w27); |
| } |
| } |
| |
| |
| TEST(bfm) { |
| SETUP(); |
| |
| START(); |
| __ Mov(x1, 0x0123456789abcdef); |
| |
| __ Mov(x10, 0x8888888888888888); |
| __ Mov(x11, 0x8888888888888888); |
| __ Mov(x12, 0x8888888888888888); |
| __ Mov(x13, 0x8888888888888888); |
| __ Mov(x14, 0xffffffffffffffff); |
| __ Mov(w20, 0x88888888); |
| __ Mov(w21, 0x88888888); |
| |
| __ Bfm(x10, x1, 16, 31); |
| __ Bfm(x11, x1, 32, 15); |
| |
| __ Bfm(w20, w1, 16, 23); |
| __ Bfm(w21, w1, 24, 15); |
| |
| // Aliases. |
| __ Bfi(x12, x1, 16, 8); |
| __ Bfxil(x13, x1, 16, 8); |
| __ Bfc(x14, 16, 8); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| |
| ASSERT_EQUAL_64(0x88888888888889ab, x10); |
| ASSERT_EQUAL_64(0x8888cdef88888888, x11); |
| |
| ASSERT_EQUAL_32(0x888888ab, w20); |
| ASSERT_EQUAL_32(0x88cdef88, w21); |
| |
| ASSERT_EQUAL_64(0x8888888888ef8888, x12); |
| ASSERT_EQUAL_64(0x88888888888888ab, x13); |
| ASSERT_EQUAL_64(0xffffffffff00ffff, x14); |
| } |
| } |
| |
| |
| TEST(sbfm) { |
| SETUP(); |
| |
| START(); |
| __ Mov(x1, 0x0123456789abcdef); |
| __ Mov(x2, 0xfedcba9876543210); |
| |
| __ Sbfm(x10, x1, 16, 31); |
| __ Sbfm(x11, x1, 32, 15); |
| __ Sbfm(x12, x1, 32, 47); |
| __ Sbfm(x13, x1, 48, 35); |
| |
| __ Sbfm(w14, w1, 16, 23); |
| __ Sbfm(w15, w1, 24, 15); |
| __ Sbfm(w16, w2, 16, 23); |
| __ Sbfm(w17, w2, 24, 15); |
| |
| // Aliases. |
| __ Asr(x18, x1, 32); |
| __ Asr(x19, x2, 32); |
| __ Sbfiz(x20, x1, 8, 16); |
| __ Sbfiz(x21, x2, 8, 16); |
| __ Sbfx(x22, x1, 8, 16); |
| __ Sbfx(x23, x2, 8, 16); |
| __ Sxtb(x24, w1); |
| __ Sxtb(x25, x2); |
| __ Sxth(x26, w1); |
| __ Sxth(x27, x2); |
| __ Sxtw(x28, w1); |
| __ Sxtw(x29, x2); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| |
| ASSERT_EQUAL_64(0xffffffffffff89ab, x10); |
| ASSERT_EQUAL_64(0xffffcdef00000000, x11); |
| ASSERT_EQUAL_64(0x0000000000004567, x12); |
| ASSERT_EQUAL_64(0x000789abcdef0000, x13); |
| |
| ASSERT_EQUAL_32(0xffffffab, w14); |
| ASSERT_EQUAL_32(0xffcdef00, w15); |
| ASSERT_EQUAL_32(0x00000054, w16); |
| ASSERT_EQUAL_32(0x00321000, w17); |
| |
| ASSERT_EQUAL_64(0x0000000001234567, x18); |
| ASSERT_EQUAL_64(0xfffffffffedcba98, x19); |
| ASSERT_EQUAL_64(0xffffffffffcdef00, x20); |
| ASSERT_EQUAL_64(0x0000000000321000, x21); |
| ASSERT_EQUAL_64(0xffffffffffffabcd, x22); |
| ASSERT_EQUAL_64(0x0000000000005432, x23); |
| ASSERT_EQUAL_64(0xffffffffffffffef, x24); |
| ASSERT_EQUAL_64(0x0000000000000010, x25); |
| ASSERT_EQUAL_64(0xffffffffffffcdef, x26); |
| ASSERT_EQUAL_64(0x0000000000003210, x27); |
| ASSERT_EQUAL_64(0xffffffff89abcdef, x28); |
| ASSERT_EQUAL_64(0x0000000076543210, x29); |
| } |
| } |
| |
| |
| TEST(ubfm) { |
| SETUP(); |
| |
| START(); |
| __ Mov(x1, 0x0123456789abcdef); |
| __ Mov(x2, 0xfedcba9876543210); |
| |
| __ Mov(x10, 0x8888888888888888); |
| __ Mov(x11, 0x8888888888888888); |
| |
| __ Ubfm(x10, x1, 16, 31); |
| __ Ubfm(x11, x1, 32, 15); |
| __ Ubfm(x12, x1, 32, 47); |
| __ Ubfm(x13, x1, 48, 35); |
| |
| __ Ubfm(w25, w1, 16, 23); |
| __ Ubfm(w26, w1, 24, 15); |
| __ Ubfm(w27, w2, 16, 23); |
| __ Ubfm(w28, w2, 24, 15); |
| |
| // Aliases |
| __ Lsl(x15, x1, 63); |
| __ Lsl(x16, x1, 0); |
| __ Lsr(x17, x1, 32); |
| __ Ubfiz(x18, x1, 8, 16); |
| __ Ubfx(x19, x1, 8, 16); |
| __ Uxtb(x20, x1); |
| __ Uxth(x21, x1); |
| __ Uxtw(x22, x1); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0x00000000000089ab, x10); |
| ASSERT_EQUAL_64(0x0000cdef00000000, x11); |
| ASSERT_EQUAL_64(0x0000000000004567, x12); |
| ASSERT_EQUAL_64(0x000789abcdef0000, x13); |
| |
| ASSERT_EQUAL_32(0x000000ab, w25); |
| ASSERT_EQUAL_32(0x00cdef00, w26); |
| ASSERT_EQUAL_32(0x00000054, w27); |
| ASSERT_EQUAL_32(0x00321000, w28); |
| |
| ASSERT_EQUAL_64(0x8000000000000000, x15); |
| ASSERT_EQUAL_64(0x0123456789abcdef, x16); |
| ASSERT_EQUAL_64(0x0000000001234567, x17); |
| ASSERT_EQUAL_64(0x0000000000cdef00, x18); |
| ASSERT_EQUAL_64(0x000000000000abcd, x19); |
| ASSERT_EQUAL_64(0x00000000000000ef, x20); |
| ASSERT_EQUAL_64(0x000000000000cdef, x21); |
| ASSERT_EQUAL_64(0x0000000089abcdef, x22); |
| } |
| } |
| |
| |
| TEST(extr) { |
| SETUP(); |
| |
| START(); |
| __ Mov(x1, 0x0123456789abcdef); |
| __ Mov(x2, 0xfedcba9876543210); |
| |
| __ Extr(w10, w1, w2, 0); |
| __ Extr(w11, w1, w2, 1); |
| __ Extr(x12, x2, x1, 2); |
| |
| __ Ror(w13, w1, 0); |
| __ Ror(w14, w2, 17); |
| __ Ror(w15, w1, 31); |
| __ Ror(x18, x2, 0); |
| __ Ror(x19, x2, 1); |
| __ Ror(x20, x1, 63); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0x76543210, x10); |
| ASSERT_EQUAL_64(0xbb2a1908, x11); |
| ASSERT_EQUAL_64(0x0048d159e26af37b, x12); |
| ASSERT_EQUAL_64(0x89abcdef, x13); |
| ASSERT_EQUAL_64(0x19083b2a, x14); |
| ASSERT_EQUAL_64(0x13579bdf, x15); |
| ASSERT_EQUAL_64(0xfedcba9876543210, x18); |
| ASSERT_EQUAL_64(0x7f6e5d4c3b2a1908, x19); |
| ASSERT_EQUAL_64(0x02468acf13579bde, x20); |
| } |
| } |
| |
| |
| TEST(system_mrs) { |
| SETUP(); |
| |
| START(); |
| __ Mov(w0, 0); |
| __ Mov(w1, 1); |
| __ Mov(w2, 0x80000000); |
| |
| // Set the Z and C flags. |
| __ Cmp(w0, w0); |
| __ Mrs(x3, NZCV); |
| |
| // Set the N flag. |
| __ Cmp(w0, w1); |
| __ Mrs(x4, NZCV); |
| |
| // Set the Z, C and V flags. |
| __ Adds(w0, w2, w2); |
| __ Mrs(x5, NZCV); |
| |
| // Read the default FPCR. |
| __ Mrs(x6, FPCR); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| // NZCV |
| ASSERT_EQUAL_32(ZCFlag, w3); |
| ASSERT_EQUAL_32(NFlag, w4); |
| ASSERT_EQUAL_32(ZCVFlag, w5); |
| |
| // FPCR |
| // The default FPCR on Linux-based platforms is 0. |
| ASSERT_EQUAL_32(0, w6); |
| } |
| } |
| |
| TEST(system_rng) { |
| SETUP_WITH_FEATURES(CPUFeatures::kRNG); |
| |
| START(); |
| // Random number. |
| __ Mrs(x1, RNDR); |
| // Assume that each generation is successful now. |
| // TODO: Return failure occasionally. |
| __ Mrs(x2, NZCV); |
| __ Mrs(x3, RNDR); |
| __ Mrs(x4, NZCV); |
| |
| // Reseeded random number. |
| __ Mrs(x5, RNDRRS); |
| // Assume that each generation is successful now. |
| // TODO: Return failure occasionally. |
| __ Mrs(x6, NZCV); |
| __ Mrs(x7, RNDRRS); |
| __ Mrs(x8, NZCV); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| // Random number generation series. |
| // Check random numbers have been generated and aren't equal when reseed has |
| // happened. |
| // NOTE: With a different architectural implementation, there may be a |
| // collison. |
| // TODO: Return failure occasionally. Set ZFlag and return UNKNOWN value. |
| ASSERT_NOT_EQUAL_64(x1, x3); |
| ASSERT_EQUAL_64(NoFlag, x2); |
| ASSERT_EQUAL_64(NoFlag, x4); |
| ASSERT_NOT_EQUAL_64(x5, x7); |
| ASSERT_EQUAL_64(NoFlag, x6); |
| ASSERT_EQUAL_64(NoFlag, x8); |
| } |
| } |
| |
| TEST(cfinv) { |
| SETUP_WITH_FEATURES(CPUFeatures::kFlagM); |
| |
| START(); |
| __ Mov(w0, 1); |
| |
| // Set the C flag. |
| __ Cmp(w0, 0); |
| __ Mrs(x1, NZCV); |
| |
| // Invert the C flag. |
| __ Cfinv(); |
| __ Mrs(x2, NZCV); |
| |
| // Invert the C flag again. |
| __ Cfinv(); |
| __ Mrs(x3, NZCV); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_32(CFlag, w1); |
| ASSERT_EQUAL_32(NoFlag, w2); |
| ASSERT_EQUAL_32(CFlag, w3); |
| } |
| } |
| |
| |
| TEST(axflag_xaflag) { |
| // The AXFLAG and XAFLAG instructions are designed for converting the FP |
| // conditional flags from Arm format to an alternate format efficiently. |
| // There are only 4 cases which are relevant for this conversion but we test |
| // the behaviour for all 16 cases anyway. The 4 important cases are labelled |
| // below. |
| StatusFlags expected_x[16] = {NoFlag, |
| ZFlag, |
| CFlag, // Greater than |
| ZFlag, // Unordered |
| ZFlag, |
| ZFlag, |
| ZCFlag, // Equal to |
| ZFlag, |
| NoFlag, // Less than |
| ZFlag, |
| CFlag, |
| ZFlag, |
| ZFlag, |
| ZFlag, |
| ZCFlag, |
| ZFlag}; |
| StatusFlags expected_a[16] = {NFlag, // Less than |
| NFlag, |
| CFlag, // Greater than |
| CFlag, |
| CVFlag, // Unordered |
| CVFlag, |
| ZCFlag, // Equal to |
| ZCFlag, |
| NFlag, |
| NFlag, |
| CFlag, |
| CFlag, |
| CVFlag, |
| CVFlag, |
| ZCFlag, |
| ZCFlag}; |
| |
| for (unsigned i = 0; i < 16; i++) { |
| SETUP_WITH_FEATURES(CPUFeatures::kAXFlag); |
| |
| START(); |
| __ Mov(x0, i << Flags_offset); |
| __ Msr(NZCV, x0); |
| __ Axflag(); |
| __ Mrs(x1, NZCV); |
| __ Msr(NZCV, x0); |
| __ Xaflag(); |
| __ Mrs(x2, NZCV); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| ASSERT_EQUAL_32(expected_x[i], w1); |
| ASSERT_EQUAL_32(expected_a[i], w2); |
| } |
| } |
| } |
| |
| |
| TEST(system_msr) { |
| // All FPCR fields that must be implemented: AHP, DN, FZ, RMode |
| const uint64_t fpcr_core = 0x07c00000; |
| |
| // All FPCR fields (including fields which may be read-as-zero): |
| // Stride, Len |
| // IDE, IXE, UFE, OFE, DZE, IOE |
| const uint64_t fpcr_all = fpcr_core | 0x00379f00; |
| |
| SETUP(); |
| |
| START(); |
| __ Mov(w0, 0); |
| __ Mov(w1, 0x7fffffff); |
| |
| __ Mov(x7, 0); |
| |
| __ Mov(x10, NVFlag); |
| __ Cmp(w0, w0); // Set Z and C. |
| __ Msr(NZCV, x10); // Set N and V. |
| // The Msr should have overwritten every flag set by the Cmp. |
| __ Cinc(x7, x7, mi); // N |
| __ Cinc(x7, x7, ne); // !Z |
| __ Cinc(x7, x7, lo); // !C |
| __ Cinc(x7, x7, vs); // V |
| |
| __ Mov(x10, ZCFlag); |
| __ Cmn(w1, w1); // Set N and V. |
| __ Msr(NZCV, x10); // Set Z and C. |
| // The Msr should have overwritten every flag set by the Cmn. |
| __ Cinc(x7, x7, pl); // !N |
| __ Cinc(x7, x7, eq); // Z |
| __ Cinc(x7, x7, hs); // C |
| __ Cinc(x7, x7, vc); // !V |
| |
| // All core FPCR fields must be writable. |
| __ Mov(x8, fpcr_core); |
| __ Msr(FPCR, x8); |
| __ Mrs(x8, FPCR); |
| |
| // All FPCR fields, including optional ones. This part of the test doesn't |
| // achieve much other than ensuring that supported fields can be cleared by |
| // the next test. |
| __ Mov(x9, fpcr_all); |
| __ Msr(FPCR, x9); |
| __ Mrs(x9, FPCR); |
| __ And(x9, x9, fpcr_core); |
| |
| // The undefined bits must ignore writes. |
| // It's conceivable that a future version of the architecture could use these |
| // fields (making this test fail), but in the meantime this is a useful test |
| // for the simulator. |
| __ Mov(x10, ~fpcr_all); |
| __ Msr(FPCR, x10); |
| __ Mrs(x10, FPCR); |
| |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| // We should have incremented x7 (from 0) exactly 8 times. |
| ASSERT_EQUAL_64(8, x7); |
| |
| ASSERT_EQUAL_64(fpcr_core, x8); |
| ASSERT_EQUAL_64(fpcr_core, x9); |
| ASSERT_EQUAL_64(0, x10); |
| } |
| } |
| |
| |
| TEST(system_pauth_a) { |
| SETUP_WITH_FEATURES(CPUFeatures::kPAuth); |
| START(); |
| |
| // Exclude x16 and x17 from the scratch register list so we can use |
| // Pac/Autia1716 safely. |
| UseScratchRegisterScope temps(&masm); |
| temps.Exclude(x16, x17); |
| temps.Include(x10, x11); |
| |
| // Backup stack pointer. |
| __ Mov(x20, sp); |
| |
| // Modifiers |
| __ Mov(x16, 0x477d469dec0b8760); |
| __ Mov(sp, 0x477d469dec0b8760); |
| |
| // Generate PACs using the 3 system instructions. |
| __ Mov(x17, 0x0000000012345678); |
| __ Pacia1716(); |
| __ Mov(x0, x17); |
| |
| __ Mov(lr, 0x0000000012345678); |
| __ Paciaz(); |
| __ Mov(x1, lr); |
| |
| __ Mov(lr, 0x0000000012345678); |
| __ Paciasp(); |
| __ Mov(x2, lr); |
| |
| // Authenticate the pointers above. |
| __ Mov(x17, x0); |
| __ Autia1716(); |
| __ Mov(x3, x17); |
| |
| __ Mov(lr, x1); |
| __ Autiaz(); |
| __ Mov(x4, lr); |
| |
| __ Mov(lr, x2); |
| __ Autiasp(); |
| __ Mov(x5, lr); |
| |
| // Attempt to authenticate incorrect pointers. |
| __ Mov(x17, x1); |
| __ Autia1716(); |
| __ Mov(x6, x17); |
| |
| __ Mov(lr, x0); |
| __ Autiaz(); |
| __ Mov(x7, lr); |
| |
| __ Mov(lr, x1); |
| __ Autiasp(); |
| __ Mov(x8, lr); |
| |
| // Strip the pac code from the pointer in x0. |
| __ Mov(lr, x0); |
| __ Xpaclri(); |
| __ Mov(x9, lr); |
| |
| // Restore stack pointer. |
| __ Mov(sp, x20); |
| |
| // Mask out just the PAC code bits. |
| // TODO: use Simulator::CalculatePACMask in a nice way. |
| __ And(x0, x0, 0x007f000000000000); |
| __ And(x1, x1, 0x007f000000000000); |
| __ And(x2, x2, 0x007f000000000000); |
| |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| // Check PAC codes have been generated and aren't equal. |
| // NOTE: with a different ComputePAC implementation, there may be a |
| // collision. |
| ASSERT_NOT_EQUAL_64(0, x0); |
| ASSERT_NOT_EQUAL_64(0, x1); |
| ASSERT_NOT_EQUAL_64(0, x2); |
| ASSERT_NOT_EQUAL_64(x0, x1); |
| ASSERT_EQUAL_64(x0, x2); |
| |
| // Pointers correctly authenticated. |
| ASSERT_EQUAL_64(0x0000000012345678, x3); |
| ASSERT_EQUAL_64(0x0000000012345678, x4); |
| ASSERT_EQUAL_64(0x0000000012345678, x5); |
| |
| // Pointers corrupted after failing to authenticate. |
| ASSERT_EQUAL_64(0x0020000012345678, x6); |
| ASSERT_EQUAL_64(0x0020000012345678, x7); |
| ASSERT_EQUAL_64(0x0020000012345678, x8); |
| |
| // Pointer with code stripped. |
| ASSERT_EQUAL_64(0x0000000012345678, x9); |
| } |
| } |
| |
| |
| TEST(system_pauth_b) { |
| SETUP_WITH_FEATURES(CPUFeatures::kPAuth); |
| START(); |
| |
| // Exclude x16 and x17 from the scratch register list so we can use |
| // Pac/Autia1716 safely. |
| UseScratchRegisterScope temps(&masm); |
| temps.Exclude(x16, x17); |
| temps.Include(x10, x11); |
| |
| // Backup stack pointer. |
| __ Mov(x20, sp); |
| |
| // Modifiers |
| __ Mov(x16, 0x477d469dec0b8760); |
| __ Mov(sp, 0x477d469dec0b8760); |
| |
| // Generate PACs using the 3 system instructions. |
| __ Mov(x17, 0x0000000012345678); |
| __ Pacib1716(); |
| __ Mov(x0, x17); |
| |
| __ Mov(lr, 0x0000000012345678); |
| __ Pacibz(); |
| __ Mov(x1, lr); |
| |
| __ Mov(lr, 0x0000000012345678); |
| __ Pacibsp(); |
| __ Mov(x2, lr); |
| |
| // Authenticate the pointers above. |
| __ Mov(x17, x0); |
| __ Autib1716(); |
| __ Mov(x3, x17); |
| |
| __ Mov(lr, x1); |
| __ Autibz(); |
| __ Mov(x4, lr); |
| |
| __ Mov(lr, x2); |
| __ Autibsp(); |
| __ Mov(x5, lr); |
| |
| // Attempt to authenticate incorrect pointers. |
| __ Mov(x17, x1); |
| __ Autib1716(); |
| __ Mov(x6, x17); |
| |
| __ Mov(lr, x0); |
| __ Autibz(); |
| __ Mov(x7, lr); |
| |
| __ Mov(lr, x1); |
| __ Autibsp(); |
| __ Mov(x8, lr); |
| |
| // Strip the pac code from the pointer in x0. |
| __ Mov(lr, x0); |
| __ Xpaclri(); |
| __ Mov(x9, lr); |
| |
| // Restore stack pointer. |
| __ Mov(sp, x20); |
| |
| // Mask out just the PAC code bits. |
| // TODO: use Simulator::CalculatePACMask in a nice way. |
| __ And(x0, x0, 0x007f000000000000); |
| __ And(x1, x1, 0x007f000000000000); |
| __ And(x2, x2, 0x007f000000000000); |
| |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| // Check PAC codes have been generated and aren't equal. |
| // NOTE: with a different ComputePAC implementation, there may be a |
| // collision. |
| ASSERT_NOT_EQUAL_64(0, x0); |
| ASSERT_NOT_EQUAL_64(0, x1); |
| ASSERT_NOT_EQUAL_64(0, x2); |
| ASSERT_NOT_EQUAL_64(x0, x1); |
| ASSERT_EQUAL_64(x0, x2); |
| |
| // Pointers correctly authenticated. |
| ASSERT_EQUAL_64(0x0000000012345678, x3); |
| ASSERT_EQUAL_64(0x0000000012345678, x4); |
| ASSERT_EQUAL_64(0x0000000012345678, x5); |
| |
| // Pointers corrupted after failing to authenticate. |
| ASSERT_EQUAL_64(0x0040000012345678, x6); |
| ASSERT_EQUAL_64(0x0040000012345678, x7); |
| ASSERT_EQUAL_64(0x0040000012345678, x8); |
| |
| // Pointer with code stripped. |
| ASSERT_EQUAL_64(0x0000000012345678, x9); |
| } |
| } |
| |
| #ifdef VIXL_NEGATIVE_TESTING |
| TEST(system_pauth_negative_test) { |
| SETUP_WITH_FEATURES(CPUFeatures::kPAuth); |
| START(); |
| |
| // Test for an assert (independent of order). |
| MUST_FAIL_WITH_MESSAGE(__ Pacia1716(), |
| "Assertion failed " |
| "(!GetScratchRegisterList()->IncludesAliasOf("); |
| |
| // Test for x16 assert. |
| { |
| UseScratchRegisterScope temps(&masm); |
| temps.Exclude(x17); |
| temps.Include(x16); |
| MUST_FAIL_WITH_MESSAGE(__ Pacia1716(), |
| "Assertion failed " |
| "(!GetScratchRegisterList()->IncludesAliasOf(x16))"); |
| } |
| |
| // Test for x17 assert. |
| { |
| UseScratchRegisterScope temps(&masm); |
| temps.Exclude(x16); |
| temps.Include(x17); |
| MUST_FAIL_WITH_MESSAGE(__ Pacia1716(), |
| "Assertion failed " |
| "(!GetScratchRegisterList()->IncludesAliasOf(x17))"); |
| } |
| |
| // Repeat first test for other 1716 instructions. |
| MUST_FAIL_WITH_MESSAGE(__ Pacib1716(), |
| "Assertion failed " |
| "(!GetScratchRegisterList()->IncludesAliasOf("); |
| MUST_FAIL_WITH_MESSAGE(__ Autia1716(), |
| "Assertion failed " |
| "(!GetScratchRegisterList()->IncludesAliasOf("); |
| MUST_FAIL_WITH_MESSAGE(__ Autib1716(), |
| "Assertion failed " |
| "(!GetScratchRegisterList()->IncludesAliasOf("); |
| |
| END(); |
| } |
| #endif // VIXL_NEGATIVE_TESTING |
| |
| |
| TEST(system) { |
| // RegisterDump::Dump uses NEON. |
| SETUP_WITH_FEATURES(CPUFeatures::kNEON, CPUFeatures::kRAS); |
| RegisterDump before; |
| |
| START(); |
| before.Dump(&masm); |
| __ Nop(); |
| __ Esb(); |
| __ Csdb(); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_REGISTERS(before); |
| ASSERT_EQUAL_NZCV(before.flags_nzcv()); |
| } |
| } |
| |
| static void BtiHelper(Register ipreg) { |
| SETUP_WITH_FEATURES(CPUFeatures::kBTI); |
| |
| Label jump_target, jump_call_target, call_target, done; |
| START(); |
| UseScratchRegisterScope temps(&masm); |
| temps.Exclude(ipreg); |
| __ Adr(x0, &jump_target); |
| __ Br(x0); |
| __ Nop(); |
| __ Bind(&jump_target, EmitBTI_j); |
| __ Adr(x0, &call_target); |
| __ Blr(x0); |
| __ Adr(ipreg, &jump_call_target); |
| __ Blr(ipreg); |
| __ Adr(lr, &done); // Make Ret return to done label. |
| __ Br(ipreg); |
| __ Bind(&call_target, EmitBTI_c); |
| __ Ret(); |
| __ Bind(&jump_call_target, EmitBTI_jc); |
| __ Ret(); |
| __ Bind(&done); |
| END(); |
| |
| if (CAN_RUN()) { |
| #ifdef VIXL_INCLUDE_SIMULATOR_AARCH64 |
| simulator.SetGuardedPages(true); |
| #else |
| VIXL_UNIMPLEMENTED(); |
| #endif |
| RUN(); |
| } |
| } |
| |
| TEST(bti) { |
| BtiHelper(x16); |
| BtiHelper(x17); |
| } |
| |
| TEST(unguarded_bti_is_nop) { |
| SETUP_WITH_FEATURES(CPUFeatures::kBTI); |
| |
| Label start, none, c, j, jc; |
| START(); |
| __ B(&start); |
| __ Bind(&none, EmitBTI); |
| __ Bind(&c, EmitBTI_c); |
| __ Bind(&j, EmitBTI_j); |
| __ Bind(&jc, EmitBTI_jc); |
| VIXL_CHECK(__ GetSizeOfCodeGeneratedSince(&none) == 4 * kInstructionSize); |
| __ Ret(); |
| |
| Label jump_to_c, call_to_j; |
| __ Bind(&start); |
| __ Adr(x0, &none); |
| __ Adr(lr, &jump_to_c); |
| __ Br(x0); |
| |
| __ Bind(&jump_to_c); |
| __ Adr(x0, &c); |
| __ Adr(lr, &call_to_j); |
| __ Br(x0); |
| |
| __ Bind(&call_to_j); |
| __ Adr(x0, &j); |
| __ Blr(x0); |
| END(); |
| |
| if (CAN_RUN()) { |
| #ifdef VIXL_INCLUDE_SIMULATOR_AARCH64 |
| simulator.SetGuardedPages(false); |
| #else |
| VIXL_UNIMPLEMENTED(); |
| #endif |
| RUN(); |
| } |
| } |
| |
| #ifdef VIXL_NEGATIVE_TESTING |
| TEST(bti_jump_to_ip_unidentified) { |
| SETUP_WITH_FEATURES(CPUFeatures::kBTI); |
| |
| START(); |
| UseScratchRegisterScope temps(&masm); |
| temps.Exclude(x17); |
| Label l; |
| __ Adr(x17, &l); |
| __ Br(x17); |
| __ Nop(); |
| __ Bind(&l); |
| __ Nop(); |
| END(); |
| |
| if (CAN_RUN()) { |
| #ifdef VIXL_INCLUDE_SIMULATOR_AARCH64 |
| simulator.SetGuardedPages(true); |
| #else |
| VIXL_UNIMPLEMENTED(); |
| #endif |
| MUST_FAIL_WITH_MESSAGE(RUN(), |
| "Executing non-BTI instruction with wrong " |
| "BType."); |
| } |
| } |
| |
| TEST(bti_jump_to_unidentified) { |
| SETUP_WITH_FEATURES(CPUFeatures::kBTI); |
| |
| START(); |
| Label l; |
| __ Adr(x0, &l); |
| __ Br(x0); |
| __ Nop(); |
| __ Bind(&l); |
| __ Nop(); |
| END(); |
| |
| if (CAN_RUN()) { |
| #ifdef VIXL_INCLUDE_SIMULATOR_AARCH64 |
| simulator.SetGuardedPages(true); |
| #else |
| VIXL_UNIMPLEMENTED(); |
| #endif |
| MUST_FAIL_WITH_MESSAGE(RUN(), |
| "Executing non-BTI instruction with wrong " |
| "BType."); |
| } |
| } |
| |
| TEST(bti_call_to_unidentified) { |
| SETUP_WITH_FEATURES(CPUFeatures::kBTI); |
| |
| START(); |
| Label l; |
| __ Adr(x0, &l); |
| __ Blr(x0); |
| __ Nop(); |
| __ Bind(&l); |
| __ Nop(); |
| END(); |
| |
| if (CAN_RUN()) { |
| #ifdef VIXL_INCLUDE_SIMULATOR_AARCH64 |
| simulator.SetGuardedPages(true); |
| #else |
| VIXL_UNIMPLEMENTED(); |
| #endif |
| MUST_FAIL_WITH_MESSAGE(RUN(), |
| "Executing non-BTI instruction with wrong " |
| "BType."); |
| } |
| } |
| |
| TEST(bti_jump_to_c) { |
| SETUP_WITH_FEATURES(CPUFeatures::kBTI); |
| |
| START(); |
| // Jumping to a "BTI c" target must fail. |
| Label jump_target; |
| __ Adr(x0, &jump_target); |
| __ Br(x0); |
| __ Nop(); |
| __ Bind(&jump_target, EmitBTI_c); |
| __ Nop(); |
| END(); |
| |
| if (CAN_RUN()) { |
| #ifdef VIXL_INCLUDE_SIMULATOR_AARCH64 |
| simulator.SetGuardedPages(true); |
| #else |
| VIXL_UNIMPLEMENTED(); |
| #endif |
| MUST_FAIL_WITH_MESSAGE(RUN(), "Executing BTI c with wrong BType."); |
| } |
| } |
| |
| TEST(bti_call_to_j) { |
| SETUP_WITH_FEATURES(CPUFeatures::kBTI); |
| |
| START(); |
| // Calling a "BTI j" target must fail. |
| Label call_target; |
| __ Adr(x0, &call_target); |
| __ Blr(x0); |
| __ Nop(); |
| __ Bind(&call_target, EmitBTI_j); |
| __ Nop(); |
| END(); |
| |
| if (CAN_RUN()) { |
| #ifdef VIXL_INCLUDE_SIMULATOR_AARCH64 |
| simulator.SetGuardedPages(true); |
| #else |
| VIXL_UNIMPLEMENTED(); |
| #endif |
| MUST_FAIL_WITH_MESSAGE(RUN(), "Executing BTI j with wrong BType."); |
| } |
| } |
| #endif // VIXL_NEGATIVE_TESTING |
| |
| TEST(fall_through_bti) { |
| SETUP_WITH_FEATURES(CPUFeatures::kBTI, CPUFeatures::kPAuth); |
| |
| START(); |
| Label target, target_j, target_c, target_jc; |
| __ Mov(x0, 0); // 'Normal' instruction sets BTYPE to zero. |
| __ Bind(&target, EmitBTI); |
| __ Add(x0, x0, 1); |
| __ Bind(&target_j, EmitBTI_j); |
| __ Add(x0, x0, 1); |
| __ Bind(&target_c, EmitBTI_c); |
| __ Add(x0, x0, 1); |
| __ Bind(&target_jc, EmitBTI_jc); |
| __ Add(x0, x0, 1); |
| __ Paciasp(); |
| END(); |
| |
| if (CAN_RUN()) { |
| #ifdef VIXL_INCLUDE_SIMULATOR_AARCH64 |
| simulator.SetGuardedPages(true); |
| #else |
| VIXL_UNIMPLEMENTED(); |
| #endif |
| RUN(); |
| |
| ASSERT_EQUAL_64(4, x0); |
| } |
| } |
| |
| TEST(zero_dest) { |
| // RegisterDump::Dump uses NEON. |
| SETUP_WITH_FEATURES(CPUFeatures::kNEON); |
| RegisterDump before; |
| |
| START(); |
| // Preserve the stack pointer, in case we clobber it. |
| __ Mov(x30, sp); |
| // Initialize the other registers used in this test. |
| uint64_t literal_base = 0x0100001000100101; |
| __ Mov(x0, 0); |
| __ Mov(x1, literal_base); |
| for (unsigned i = 2; i < x30.GetCode(); i++) { |
| __ Add(Register::GetXRegFromCode(i), Register::GetXRegFromCode(i - 1), x1); |
| } |
| before.Dump(&masm); |
| |
| // All of these instructions should be NOPs in these forms, but have |
| // alternate forms which can write into the stack pointer. |
| { |
| ExactAssemblyScope scope(&masm, 3 * 7 * kInstructionSize); |
| __ add(xzr, x0, x1); |
| __ add(xzr, x1, xzr); |
| __ add(xzr, xzr, x1); |
| |
| __ and_(xzr, x0, x2); |
| __ and_(xzr, x2, xzr); |
| __ and_(xzr, xzr, x2); |
| |
| __ bic(xzr, x0, x3); |
| __ bic(xzr, x3, xzr); |
| __ bic(xzr, xzr, x3); |
| |
| __ eon(xzr, x0, x4); |
| __ eon(xzr, x4, xzr); |
| __ eon(xzr, xzr, x4); |
| |
| __ eor(xzr, x0, x5); |
| __ eor(xzr, x5, xzr); |
| __ eor(xzr, xzr, x5); |
| |
| __ orr(xzr, x0, x6); |
| __ orr(xzr, x6, xzr); |
| __ orr(xzr, xzr, x6); |
| |
| __ sub(xzr, x0, x7); |
| __ sub(xzr, x7, xzr); |
| __ sub(xzr, xzr, x7); |
| } |
| |
| // Swap the saved stack pointer with the real one. If sp was written |
| // during the test, it will show up in x30. This is done because the test |
| // framework assumes that sp will be valid at the end of the test. |
| __ Mov(x29, x30); |
| __ Mov(x30, sp); |
| __ Mov(sp, x29); |
| // We used x29 as a scratch register, so reset it to make sure it doesn't |
| // trigger a test failure. |
| __ Add(x29, x28, x1); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_REGISTERS(before); |
| ASSERT_EQUAL_NZCV(before.flags_nzcv()); |
| } |
| } |
| |
| |
| TEST(zero_dest_setflags) { |
| // RegisterDump::Dump uses NEON. |
| SETUP_WITH_FEATURES(CPUFeatures::kNEON); |
| RegisterDump before; |
| |
| START(); |
| // Preserve the stack pointer, in case we clobber it. |
| __ Mov(x30, sp); |
| // Initialize the other registers used in this test. |
| uint64_t literal_base = 0x0100001000100101; |
| __ Mov(x0, 0); |
| __ Mov(x1, literal_base); |
| for (int i = 2; i < 30; i++) { |
| __ Add(Register::GetXRegFromCode(i), Register::GetXRegFromCode(i - 1), x1); |
| } |
| before.Dump(&masm); |
| |
| // All of these instructions should only write to the flags in these forms, |
| // but have alternate forms which can write into the stack pointer. |
| { |
| ExactAssemblyScope scope(&masm, 6 * kInstructionSize); |
| __ adds(xzr, x0, Operand(x1, UXTX)); |
| __ adds(xzr, x1, Operand(xzr, UXTX)); |
| __ adds(xzr, x1, 1234); |
| __ adds(xzr, x0, x1); |
| __ adds(xzr, x1, xzr); |
| __ adds(xzr, xzr, x1); |
| } |
| |
| { |
| ExactAssemblyScope scope(&masm, 5 * kInstructionSize); |
| __ ands(xzr, x2, ~0xf); |
| __ ands(xzr, xzr, ~0xf); |
| __ ands(xzr, x0, x2); |
| __ ands(xzr, x2, xzr); |
| __ ands(xzr, xzr, x2); |
| } |
| |
| { |
| ExactAssemblyScope scope(&masm, 5 * kInstructionSize); |
| __ bics(xzr, x3, ~0xf); |
| __ bics(xzr, xzr, ~0xf); |
| __ bics(xzr, x0, x3); |
| __ bics(xzr, x3, xzr); |
| __ bics(xzr, xzr, x3); |
| } |
| |
| { |
| ExactAssemblyScope scope(&masm, 6 * kInstructionSize); |
| __ subs(xzr, x0, Operand(x3, UXTX)); |
| __ subs(xzr, x3, Operand(xzr, UXTX)); |
| __ subs(xzr, x3, 1234); |
| __ subs(xzr, x0, x3); |
| __ subs(xzr, x3, xzr); |
| __ subs(xzr, xzr, x3); |
| } |
| |
| // Swap the saved stack pointer with the real one. If sp was written |
| // during the test, it will show up in x30. This is done because the test |
| // framework assumes that sp will be valid at the end of the test. |
| __ Mov(x29, x30); |
| __ Mov(x30, sp); |
| __ Mov(sp, x29); |
| // We used x29 as a scratch register, so reset it to make sure it doesn't |
| // trigger a test failure. |
| __ Add(x29, x28, x1); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_REGISTERS(before); |
| } |
| } |
| |
| |
| TEST(stack_pointer_override) { |
| // This test generates some stack maintenance code, but the test only checks |
| // the reported state. |
| SETUP(); |
| START(); |
| |
| // The default stack pointer in VIXL is sp. |
| VIXL_CHECK(sp.Is(__ StackPointer())); |
| __ SetStackPointer(x0); |
| VIXL_CHECK(x0.Is(__ StackPointer())); |
| __ SetStackPointer(x28); |
| VIXL_CHECK(x28.Is(__ StackPointer())); |
| __ SetStackPointer(sp); |
| VIXL_CHECK(sp.Is(__ StackPointer())); |
| |
| END(); |
| if (CAN_RUN()) { |
| RUN(); |
| } |
| } |
| |
| |
| TEST(peek_poke_simple) { |
| SETUP(); |
| START(); |
| |
| static const RegList x0_to_x3 = |
| x0.GetBit() | x1.GetBit() | x2.GetBit() | x3.GetBit(); |
| static const RegList x10_to_x13 = |
| x10.GetBit() | x11.GetBit() | x12.GetBit() | x13.GetBit(); |
| |
| // The literal base is chosen to have two useful properties: |
| // * When multiplied by small values (such as a register index), this value |
| // is clearly readable in the result. |
| // * The value is not formed from repeating fixed-size smaller values, so it |
| // can be used to detect endianness-related errors. |
| uint64_t literal_base = 0x0100001000100101; |
| |
| // Initialize the registers. |
| __ Mov(x0, literal_base); |
| __ Add(x1, x0, x0); |
| __ Add(x2, x1, x0); |
| __ Add(x3, x2, x0); |
| |
| __ Claim(32); |
| |
| // Simple exchange. |
| // After this test: |
| // x0-x3 should be unchanged. |
| // w10-w13 should contain the lower words of x0-x3. |
| __ Poke(x0, 0); |
| __ Poke(x1, 8); |
| __ Poke(x2, 16); |
| __ Poke(x3, 24); |
| Clobber(&masm, x0_to_x3); |
| __ Peek(x0, 0); |
| __ Peek(x1, 8); |
| __ Peek(x2, 16); |
| __ Peek(x3, 24); |
| |
| __ Poke(w0, 0); |
| __ Poke(w1, 4); |
| __ Poke(w2, 8); |
| __ Poke(w3, 12); |
| Clobber(&masm, x10_to_x13); |
| __ Peek(w10, 0); |
| __ Peek(w11, 4); |
| __ Peek(w12, 8); |
| __ Peek(w13, 12); |
| |
| __ Drop(32); |
| |
| END(); |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(literal_base * 1, x0); |
| ASSERT_EQUAL_64(literal_base * 2, x1); |
| ASSERT_EQUAL_64(literal_base * 3, x2); |
| ASSERT_EQUAL_64(literal_base * 4, x3); |
| |
| ASSERT_EQUAL_64((literal_base * 1) & 0xffffffff, x10); |
| ASSERT_EQUAL_64((literal_base * 2) & 0xffffffff, x11); |
| ASSERT_EQUAL_64((literal_base * 3) & 0xffffffff, x12); |
| ASSERT_EQUAL_64((literal_base * 4) & 0xffffffff, x13); |
| } |
| } |
| |
| |
| TEST(peek_poke_unaligned) { |
| SETUP(); |
| START(); |
| |
| // The literal base is chosen to have two useful properties: |
| // * When multiplied by small values (such as a register index), this value |
| // is clearly readable in the result. |
| // * The value is not formed from repeating fixed-size smaller values, so it |
| // can be used to detect endianness-related errors. |
| uint64_t literal_base = 0x0100001000100101; |
| |
| // Initialize the registers. |
| __ Mov(x0, literal_base); |
| __ Add(x1, x0, x0); |
| __ Add(x2, x1, x0); |
| __ Add(x3, x2, x0); |
| __ Add(x4, x3, x0); |
| __ Add(x5, x4, x0); |
| __ Add(x6, x5, x0); |
| |
| __ Claim(32); |
| |
| // Unaligned exchanges. |
| // After this test: |
| // x0-x6 should be unchanged. |
| // w10-w12 should contain the lower words of x0-x2. |
| __ Poke(x0, 1); |
| Clobber(&masm, x0.GetBit()); |
| __ Peek(x0, 1); |
| __ Poke(x1, 2); |
| Clobber(&masm, x1.GetBit()); |
| __ Peek(x1, 2); |
| __ Poke(x2, 3); |
| Clobber(&masm, x2.GetBit()); |
| __ Peek(x2, 3); |
| __ Poke(x3, 4); |
| Clobber(&masm, x3.GetBit()); |
| __ Peek(x3, 4); |
| __ Poke(x4, 5); |
| Clobber(&masm, x4.GetBit()); |
| __ Peek(x4, 5); |
| __ Poke(x5, 6); |
| Clobber(&masm, x5.GetBit()); |
| __ Peek(x5, 6); |
| __ Poke(x6, 7); |
| Clobber(&masm, x6.GetBit()); |
| __ Peek(x6, 7); |
| |
| __ Poke(w0, 1); |
| Clobber(&masm, w10.GetBit()); |
| __ Peek(w10, 1); |
| __ Poke(w1, 2); |
| Clobber(&masm, w11.GetBit()); |
| __ Peek(w11, 2); |
| __ Poke(w2, 3); |
| Clobber(&masm, w12.GetBit()); |
| __ Peek(w12, 3); |
| |
| __ Drop(32); |
| |
| END(); |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(literal_base * 1, x0); |
| ASSERT_EQUAL_64(literal_base * 2, x1); |
| ASSERT_EQUAL_64(literal_base * 3, x2); |
| ASSERT_EQUAL_64(literal_base * 4, x3); |
| ASSERT_EQUAL_64(literal_base * 5, x4); |
| ASSERT_EQUAL_64(literal_base * 6, x5); |
| ASSERT_EQUAL_64(literal_base * 7, x6); |
| |
| ASSERT_EQUAL_64((literal_base * 1) & 0xffffffff, x10); |
| ASSERT_EQUAL_64((literal_base * 2) & 0xffffffff, x11); |
| ASSERT_EQUAL_64((literal_base * 3) & 0xffffffff, x12); |
| } |
| } |
| |
| |
| TEST(peek_poke_endianness) { |
| SETUP(); |
| START(); |
| |
| // The literal base is chosen to have two useful properties: |
| // * When multiplied by small values (such as a register index), this value |
| // is clearly readable in the result. |
| // * The value is not formed from repeating fixed-size smaller values, so it |
| // can be used to detect endianness-related errors. |
| uint64_t literal_base = 0x0100001000100101; |
| |
| // Initialize the registers. |
| __ Mov(x0, literal_base); |
| __ Add(x1, x0, x0); |
| |
| __ Claim(32); |
| |
| // Endianness tests. |
| // After this section: |
| // x4 should match x0[31:0]:x0[63:32] |
| // w5 should match w1[15:0]:w1[31:16] |
| __ Poke(x0, 0); |
| __ Poke(x0, 8); |
| __ Peek(x4, 4); |
| |
| __ Poke(w1, 0); |
| __ Poke(w1, 4); |
| __ Peek(w5, 2); |
| |
| __ Drop(32); |
| |
| END(); |
| if (CAN_RUN()) { |
| RUN(); |
| |
| uint64_t x0_expected = literal_base * 1; |
| uint64_t x1_expected = literal_base * 2; |
| uint64_t x4_expected = (x0_expected << 32) | (x0_expected >> 32); |
| uint64_t x5_expected = |
| ((x1_expected << 16) & 0xffff0000) | ((x1_expected >> 16) & 0x0000ffff); |
| |
| ASSERT_EQUAL_64(x0_expected, x0); |
| ASSERT_EQUAL_64(x1_expected, x1); |
| ASSERT_EQUAL_64(x4_expected, x4); |
| ASSERT_EQUAL_64(x5_expected, x5); |
| } |
| } |
| |
| |
| TEST(peek_poke_mixed) { |
| SETUP(); |
| START(); |
| |
| // Acquire all temps from the MacroAssembler. They are used arbitrarily below. |
| UseScratchRegisterScope temps(&masm); |
| temps.ExcludeAll(); |
| |
| // The literal base is chosen to have two useful properties: |
| // * When multiplied by small values (such as a register index), this value |
| // is clearly readable in the result. |
| // * The value is not formed from repeating fixed-size smaller values, so it |
| // can be used to detect endianness-related errors. |
| uint64_t literal_base = 0x0100001000100101; |
| |
| // Initialize the registers. |
| __ Mov(x0, literal_base); |
| __ Add(x1, x0, x0); |
| __ Add(x2, x1, x0); |
| __ Add(x3, x2, x0); |
| |
| __ Claim(32); |
| |
| // Mix with other stack operations. |
| // After this section: |
| // x0-x3 should be unchanged. |
| // x6 should match x1[31:0]:x0[63:32] |
| // w7 should match x1[15:0]:x0[63:48] |
| __ Poke(x1, 8); |
| __ Poke(x0, 0); |
| { |
| VIXL_ASSERT(__ StackPointer().Is(sp)); |
| __ Mov(x4, __ StackPointer()); |
| __ SetStackPointer(x4); |
| |
| __ Poke(wzr, 0); // Clobber the space we're about to drop. |
| __ Drop(4); |
| __ Peek(x6, 0); |
| __ Claim(8); |
| __ Peek(w7, 10); |
| __ Poke(x3, 28); |
| __ Poke(xzr, 0); // Clobber the space we're about to drop. |
| __ Drop(8); |
| __ Poke(x2, 12); |
| __ Push(w0); |
| |
| __ Mov(sp, __ StackPointer()); |
| __ SetStackPointer(sp); |
| } |
| |
| __ Pop(x0, x1, x2, x3); |
| |
| END(); |
| if (CAN_RUN()) { |
| RUN(); |
| |
| uint64_t x0_expected = literal_base * 1; |
| uint64_t x1_expected = literal_base * 2; |
| uint64_t x2_expected = literal_base * 3; |
| uint64_t x3_expected = literal_base * 4; |
| uint64_t x6_expected = (x1_expected << 32) | (x0_expected >> 32); |
| uint64_t x7_expected = |
| ((x1_expected << 16) & 0xffff0000) | ((x0_expected >> 48) & 0x0000ffff); |
| |
| ASSERT_EQUAL_64(x0_expected, x0); |
| ASSERT_EQUAL_64(x1_expected, x1); |
| ASSERT_EQUAL_64(x2_expected, x2); |
| ASSERT_EQUAL_64(x3_expected, x3); |
| ASSERT_EQUAL_64(x6_expected, x6); |
| ASSERT_EQUAL_64(x7_expected, x7); |
| } |
| } |
| |
| |
| TEST(peek_poke_reglist) { |
| SETUP_WITH_FEATURES(CPUFeatures::kFP); |
| |
| START(); |
| |
| // Acquire all temps from the MacroAssembler. They are used arbitrarily below. |
| UseScratchRegisterScope temps(&masm); |
| temps.ExcludeAll(); |
| |
| // The literal base is chosen to have two useful properties: |
| // * When multiplied by small values (such as a register index), this value |
| // is clearly readable in the result. |
| // * The value is not formed from repeating fixed-size smaller values, so it |
| // can be used to detect endianness-related errors. |
| uint64_t base = 0x0100001000100101; |
| |
| // Initialize the registers. |
| __ Mov(x1, base); |
| __ Add(x2, x1, x1); |
| __ Add(x3, x2, x1); |
| __ Add(x4, x3, x1); |
| |
| CPURegList list_1(x1, x2, x3, x4); |
| CPURegList list_2(x11, x12, x13, x14); |
| int list_1_size = list_1.GetTotalSizeInBytes(); |
| |
| __ Claim(2 * list_1_size); |
| |
| __ PokeCPURegList(list_1, 0); |
| __ PokeXRegList(list_1.GetList(), list_1_size); |
| __ PeekCPURegList(list_2, 2 * kXRegSizeInBytes); |
| __ PeekXRegList(x15.GetBit(), kWRegSizeInBytes); |
| __ PeekWRegList(w16.GetBit() | w17.GetBit(), 3 * kXRegSizeInBytes); |
| |
| __ Drop(2 * list_1_size); |
| |
| |
| uint64_t base_d = 0x1010010001000010; |
| |
| // Initialize the registers. |
| __ Mov(x1, base_d); |
| __ Add(x2, x1, x1); |
| __ Add(x3, x2, x1); |
| __ Add(x4, x3, x1); |
| __ Fmov(d1, x1); |
| __ Fmov(d2, x2); |
| __ Fmov(d3, x3); |
| __ Fmov(d4, x4); |
| |
| CPURegList list_d_1(d1, d2, d3, d4); |
| CPURegList list_d_2(d11, d12, d13, d14); |
| int list_d_1_size = list_d_1.GetTotalSizeInBytes(); |
| |
| __ Claim(2 * list_d_1_size); |
| |
| __ PokeCPURegList(list_d_1, 0); |
| __ PokeDRegList(list_d_1.GetList(), list_d_1_size); |
| __ PeekCPURegList(list_d_2, 2 * kDRegSizeInBytes); |
| __ PeekDRegList(d15.GetBit(), kSRegSizeInBytes); |
| __ PeekSRegList(s16.GetBit() | s17.GetBit(), 3 * kDRegSizeInBytes); |
| |
| __ Drop(2 * list_d_1_size); |
| |
| |
| END(); |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(3 * base, x11); |
| ASSERT_EQUAL_64(4 * base, x12); |
| ASSERT_EQUAL_64(1 * base, x13); |
| ASSERT_EQUAL_64(2 * base, x14); |
| ASSERT_EQUAL_64(((1 * base) >> kWRegSize) | ((2 * base) << kWRegSize), x15); |
| ASSERT_EQUAL_64(2 * base, x14); |
| ASSERT_EQUAL_32((4 * base) & kWRegMask, w16); |
| ASSERT_EQUAL_32((4 * base) >> kWRegSize, w17); |
| |
| ASSERT_EQUAL_FP64(RawbitsToDouble(3 * base_d), d11); |
| ASSERT_EQUAL_FP64(RawbitsToDouble(4 * base_d), d12); |
| ASSERT_EQUAL_FP64(RawbitsToDouble(1 * base_d), d13); |
| ASSERT_EQUAL_FP64(RawbitsToDouble(2 * base_d), d14); |
| ASSERT_EQUAL_FP64(RawbitsToDouble((base_d >> kSRegSize) | |
| ((2 * base_d) << kSRegSize)), |
| d15); |
| ASSERT_EQUAL_FP64(RawbitsToDouble(2 * base_d), d14); |
| ASSERT_EQUAL_FP32(RawbitsToFloat((4 * base_d) & kSRegMask), s16); |
| ASSERT_EQUAL_FP32(RawbitsToFloat((4 * base_d) >> kSRegSize), s17); |
| } |
| } |
| |
| |
| TEST(load_store_reglist) { |
| SETUP_WITH_FEATURES(CPUFeatures::kFP); |
| |
| START(); |
| |
| // The literal base is chosen to have two useful properties: |
| // * When multiplied by small values (such as a register index), this value |
| // is clearly readable in the result. |
| // * The value is not formed from repeating fixed-size smaller values, so it |
| // can be used to detect endianness-related errors. |
| uint64_t high_base = UINT32_C(0x01000010); |
| uint64_t low_base = UINT32_C(0x00100101); |
| uint64_t base = (high_base << 32) | low_base; |
| uint64_t array[21]; |
| memset(array, 0, sizeof(array)); |
| |
| // Initialize the registers. |
| __ Mov(x1, base); |
| __ Add(x2, x1, x1); |
| __ Add(x3, x2, x1); |
| __ Add(x4, x3, x1); |
| __ Fmov(d1, x1); |
| __ Fmov(d2, x2); |
| __ Fmov(d3, x3); |
| __ Fmov(d4, x4); |
| __ Fmov(d5, x1); |
| __ Fmov(d6, x2); |
| __ Fmov(d7, x3); |
| __ Fmov(d8, x4); |
| |
| Register reg_base = x20; |
| Register reg_index = x21; |
| int size_stored = 0; |
| |
| __ Mov(reg_base, reinterpret_cast<uintptr_t>(&array)); |
| |
| // Test aligned accesses. |
| CPURegList list_src(w1, w2, w3, w4); |
| CPURegList list_dst(w11, w12, w13, w14); |
| CPURegList list_fp_src_1(d1, d2, d3, d4); |
| CPURegList list_fp_dst_1(d11, d12, d13, d14); |
| |
| __ StoreCPURegList(list_src, MemOperand(reg_base, 0 * sizeof(uint64_t))); |
| __ LoadCPURegList(list_dst, MemOperand(reg_base, 0 * sizeof(uint64_t))); |
| size_stored += 4 * kWRegSizeInBytes; |
| |
| __ Mov(reg_index, size_stored); |
| __ StoreCPURegList(list_src, MemOperand(reg_base, reg_index)); |
| __ LoadCPURegList(list_dst, MemOperand(reg_base, reg_index)); |
| size_stored += 4 * kWRegSizeInBytes; |
| |
| __ StoreCPURegList(list_fp_src_1, MemOperand(reg_base, size_stored)); |
| __ LoadCPURegList(list_fp_dst_1, MemOperand(reg_base, size_stored)); |
| size_stored += 4 * kDRegSizeInBytes; |
| |
| __ Mov(reg_index, size_stored); |
| __ StoreCPURegList(list_fp_src_1, MemOperand(reg_base, reg_index)); |
| __ LoadCPURegList(list_fp_dst_1, MemOperand(reg_base, reg_index)); |
| size_stored += 4 * kDRegSizeInBytes; |
| |
| // Test unaligned accesses. |
| CPURegList list_fp_src_2(d5, d6, d7, d8); |
| CPURegList list_fp_dst_2(d15, d16, d17, d18); |
| |
| __ Str(wzr, MemOperand(reg_base, size_stored)); |
| size_stored += 1 * kWRegSizeInBytes; |
| __ StoreCPURegList(list_fp_src_2, MemOperand(reg_base, size_stored)); |
| __ LoadCPURegList(list_fp_dst_2, MemOperand(reg_base, size_stored)); |
| size_stored += 4 * kDRegSizeInBytes; |
| |
| __ Mov(reg_index, size_stored); |
| __ StoreCPURegList(list_fp_src_2, MemOperand(reg_base, reg_index)); |
| __ LoadCPURegList(list_fp_dst_2, MemOperand(reg_base, reg_index)); |
| |
| END(); |
| if (CAN_RUN()) { |
| RUN(); |
| |
| VIXL_CHECK(array[0] == (1 * low_base) + (2 * low_base << kWRegSize)); |
| VIXL_CHECK(array[1] == (3 * low_base) + (4 * low_base << kWRegSize)); |
| VIXL_CHECK(array[2] == (1 * low_base) + (2 * low_base << kWRegSize)); |
| VIXL_CHECK(array[3] == (3 * low_base) + (4 * low_base << kWRegSize)); |
| VIXL_CHECK(array[4] == 1 * base); |
| VIXL_CHECK(array[5] == 2 * base); |
| VIXL_CHECK(array[6] == 3 * base); |
| VIXL_CHECK(array[7] == 4 * base); |
| VIXL_CHECK(array[8] == 1 * base); |
| VIXL_CHECK(array[9] == 2 * base); |
| VIXL_CHECK(array[10] == 3 * base); |
| VIXL_CHECK(array[11] == 4 * base); |
| VIXL_CHECK(array[12] == ((1 * low_base) << kSRegSize)); |
| VIXL_CHECK(array[13] == (((2 * low_base) << kSRegSize) | (1 * high_base))); |
| VIXL_CHECK(array[14] == (((3 * low_base) << kSRegSize) | (2 * high_base))); |
| VIXL_CHECK(array[15] == (((4 * low_base) << kSRegSize) | (3 * high_base))); |
| VIXL_CHECK(array[16] == (((1 * low_base) << kSRegSize) | (4 * high_base))); |
| VIXL_CHECK(array[17] == (((2 * low_base) << kSRegSize) | (1 * high_base))); |
| VIXL_CHECK(array[18] == (((3 * low_base) << kSRegSize) | (2 * high_base))); |
| VIXL_CHECK(array[19] == (((4 * low_base) << kSRegSize) | (3 * high_base))); |
| VIXL_CHECK(array[20] == (4 * high_base)); |
| |
| ASSERT_EQUAL_64(1 * low_base, x11); |
| ASSERT_EQUAL_64(2 * low_base, x12); |
| ASSERT_EQUAL_64(3 * low_base, x13); |
| ASSERT_EQUAL_64(4 * low_base, x14); |
| ASSERT_EQUAL_FP64(RawbitsToDouble(1 * base), d11); |
| ASSERT_EQUAL_FP64(RawbitsToDouble(2 * base), d12); |
| ASSERT_EQUAL_FP64(RawbitsToDouble(3 * base), d13); |
| ASSERT_EQUAL_FP64(RawbitsToDouble(4 * base), d14); |
| ASSERT_EQUAL_FP64(RawbitsToDouble(1 * base), d15); |
| ASSERT_EQUAL_FP64(RawbitsToDouble(2 * base), d16); |
| ASSERT_EQUAL_FP64(RawbitsToDouble(3 * base), d17); |
| ASSERT_EQUAL_FP64(RawbitsToDouble(4 * base), d18); |
| } |
| } |
| |
| |
| // This enum is used only as an argument to the push-pop test helpers. |
| enum PushPopMethod { |
| // Push or Pop using the Push and Pop methods, with blocks of up to four |
| // registers. (Smaller blocks will be used if necessary.) |
| PushPopByFour, |
| |
| // Use Push<Size>RegList and Pop<Size>RegList to transfer the registers. |
| PushPopRegList |
| }; |
| |
| |
| // For the PushPop* tests, use the maximum number of registers that the test |
| // supports (where a reg_count argument would otherwise be provided). |
| static int const kPushPopUseMaxRegCount = -1; |
| |
| // Test a simple push-pop pattern: |
| // * Claim <claim> bytes to set the stack alignment. |
| // * Push <reg_count> registers with size <reg_size>. |
| // * Clobber the register contents. |
| // * Pop <reg_count> registers to restore the original contents. |
| // * Drop <claim> bytes to restore the original stack pointer. |
| // |
| // Different push and pop methods can be specified independently to test for |
| // proper word-endian behaviour. |
| static void PushPopSimpleHelper(int reg_count, |
| int claim, |
| int reg_size, |
| PushPopMethod push_method, |
| PushPopMethod pop_method) { |
| SETUP(); |
| |
| START(); |
| |
| // Arbitrarily pick a register to use as a stack pointer. |
| const Register& stack_pointer = x20; |
| const RegList allowed = ~stack_pointer.GetBit(); |
| if (reg_count == kPushPopUseMaxRegCount) { |
| reg_count = CountSetBits(allowed, kNumberOfRegisters); |
| } |
| // Work out which registers to use, based on reg_size. |
| Register r[kNumberOfRegisters]; |
| Register x[kNumberOfRegisters]; |
| RegList list = |
| PopulateRegisterArray(NULL, x, r, reg_size, reg_count, allowed); |
| |
| // Acquire all temps from the MacroAssembler. They are used arbitrarily below. |
| UseScratchRegisterScope temps(&masm); |
| temps.ExcludeAll(); |
| |
| // The literal base is chosen to have two useful properties: |
| // * When multiplied by small values (such as a register index), this value |
| // is clearly readable in the result. |
| // * The value is not formed from repeating fixed-size smaller values, so it |
| // can be used to detect endianness-related errors. |
| uint64_t literal_base = 0x0100001000100101; |
| |
| { |
| VIXL_ASSERT(__ StackPointer().Is(sp)); |
| __ Mov(stack_pointer, __ StackPointer()); |
| __ SetStackPointer(stack_pointer); |
| |
| int i; |
| |
| // Initialize the registers. |
| for (i = 0; i < reg_count; i++) { |
| // Always write into the X register, to ensure that the upper word is |
| // properly ignored by Push when testing W registers. |
| __ Mov(x[i], literal_base * i); |
| } |
| |
| // Claim memory first, as requested. |
| __ Claim(claim); |
| |
| switch (push_method) { |
| case PushPopByFour: |
| // Push high-numbered registers first (to the highest addresses). |
| for (i = reg_count; i >= 4; i -= 4) { |
| __ Push(r[i - 1], r[i - 2], r[i - 3], r[i - 4]); |
| } |
| // Finish off the leftovers. |
| switch (i) { |
| case 3: |
| __ Push(r[2], r[1], r[0]); |
| break; |
| case 2: |
| __ Push(r[1], r[0]); |
| break; |
| case 1: |
| __ Push(r[0]); |
| break; |
| default: |
| VIXL_ASSERT(i == 0); |
| break; |
| } |
| break; |
| case PushPopRegList: |
| __ PushSizeRegList(list, reg_size); |
| break; |
| } |
| |
| // Clobber all the registers, to ensure that they get repopulated by Pop. |
| Clobber(&masm, list); |
| |
| switch (pop_method) { |
| case PushPopByFour: |
| // Pop low-numbered registers first (from the lowest addresses). |
| for (i = 0; i <= (reg_count - 4); i += 4) { |
| __ Pop(r[i], r[i + 1], r[i + 2], r[i + 3]); |
| } |
| // Finish off the leftovers. |
| switch (reg_count - i) { |
| case 3: |
| __ Pop(r[i], r[i + 1], r[i + 2]); |
| break; |
| case 2: |
| __ Pop(r[i], r[i + 1]); |
| break; |
| case 1: |
| __ Pop(r[i]); |
| break; |
| default: |
| VIXL_ASSERT(i == reg_count); |
| break; |
| } |
| break; |
| case PushPopRegList: |
| __ PopSizeRegList(list, reg_size); |
| break; |
| } |
| |
| // Drop memory to restore stack_pointer. |
| __ Drop(claim); |
| |
| __ Mov(sp, __ StackPointer()); |
| __ SetStackPointer(sp); |
| } |
| |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| // Check that the register contents were preserved. |
| // Always use ASSERT_EQUAL_64, even when testing W registers, so we can test |
| // that the upper word was properly cleared by Pop. |
| literal_base &= (0xffffffffffffffff >> (64 - reg_size)); |
| for (int i = 0; i < reg_count; i++) { |
| if (x[i].Is(xzr)) { |
| ASSERT_EQUAL_64(0, x[i]); |
| } else { |
| ASSERT_EQUAL_64(literal_base * i, x[i]); |
| } |
| } |
| } |
| } |
| |
| |
| TEST(push_pop_xreg_simple_32) { |
| for (int claim = 0; claim <= 8; claim++) { |
| for (int count = 0; count <= 8; count++) { |
| PushPopSimpleHelper(count, |
| claim, |
| kWRegSize, |
| PushPopByFour, |
| PushPopByFour); |
| PushPopSimpleHelper(count, |
| claim, |
| kWRegSize, |
| PushPopByFour, |
| PushPopRegList); |
| PushPopSimpleHelper(count, |
| claim, |
| kWRegSize, |
| PushPopRegList, |
| PushPopByFour); |
| PushPopSimpleHelper(count, |
| claim, |
| kWRegSize, |
| PushPopRegList, |
| PushPopRegList); |
| } |
| // Test with the maximum number of registers. |
| PushPopSimpleHelper(kPushPopUseMaxRegCount, |
| claim, |
| kWRegSize, |
| PushPopByFour, |
| PushPopByFour); |
| PushPopSimpleHelper(kPushPopUseMaxRegCount, |
| claim, |
| kWRegSize, |
| PushPopByFour, |
| PushPopRegList); |
| PushPopSimpleHelper(kPushPopUseMaxRegCount, |
| claim, |
| kWRegSize, |
| PushPopRegList, |
| PushPopByFour); |
| PushPopSimpleHelper(kPushPopUseMaxRegCount, |
| claim, |
| kWRegSize, |
| PushPopRegList, |
| PushPopRegList); |
| } |
| } |
| |
| |
| TEST(push_pop_xreg_simple_64) { |
| for (int claim = 0; claim <= 8; claim++) { |
| for (int count = 0; count <= 8; count++) { |
| PushPopSimpleHelper(count, |
| claim, |
| kXRegSize, |
| PushPopByFour, |
| PushPopByFour); |
| PushPopSimpleHelper(count, |
| claim, |
| kXRegSize, |
| PushPopByFour, |
| PushPopRegList); |
| PushPopSimpleHelper(count, |
| claim, |
| kXRegSize, |
| PushPopRegList, |
| PushPopByFour); |
| PushPopSimpleHelper(count, |
| claim, |
| kXRegSize, |
| PushPopRegList, |
| PushPopRegList); |
| } |
| // Test with the maximum number of registers. |
| PushPopSimpleHelper(kPushPopUseMaxRegCount, |
| claim, |
| kXRegSize, |
| PushPopByFour, |
| PushPopByFour); |
| PushPopSimpleHelper(kPushPopUseMaxRegCount, |
| claim, |
| kXRegSize, |
| PushPopByFour, |
| PushPopRegList); |
| PushPopSimpleHelper(kPushPopUseMaxRegCount, |
| claim, |
| kXRegSize, |
| PushPopRegList, |
| PushPopByFour); |
| PushPopSimpleHelper(kPushPopUseMaxRegCount, |
| claim, |
| kXRegSize, |
| PushPopRegList, |
| PushPopRegList); |
| } |
| } |
| |
| // For the PushPopFP* tests, use the maximum number of registers that the test |
| // supports (where a reg_count argument would otherwise be provided). |
| static int const kPushPopFPUseMaxRegCount = -1; |
| |
| // Test a simple push-pop pattern: |
| // * Claim <claim> bytes to set the stack alignment. |
| // * Push <reg_count> FP registers with size <reg_size>. |
| // * Clobber the register contents. |
| // * Pop <reg_count> FP registers to restore the original contents. |
| // * Drop <claim> bytes to restore the original stack pointer. |
| // |
| // Different push and pop methods can be specified independently to test for |
| // proper word-endian behaviour. |
| static void PushPopFPSimpleHelper(int reg_count, |
| int claim, |
| int reg_size, |
| PushPopMethod push_method, |
| PushPopMethod pop_method) { |
| SETUP_WITH_FEATURES((reg_count == 0) ? CPUFeatures::kNone : CPUFeatures::kFP); |
| |
| START(); |
| |
| // We can use any floating-point register. None of them are reserved for |
| // debug code, for example. |
| static RegList const allowed = ~0; |
| if (reg_count == kPushPopFPUseMaxRegCount) { |
| reg_count = CountSetBits(allowed, kNumberOfVRegisters); |
| } |
| // Work out which registers to use, based on reg_size. |
| VRegister v[kNumberOfRegisters]; |
| VRegister d[kNumberOfRegisters]; |
| RegList list = |
| PopulateVRegisterArray(NULL, d, v, reg_size, reg_count, allowed); |
| |
| // Arbitrarily pick a register to use as a stack pointer. |
| const Register& stack_pointer = x10; |
| |
| // Acquire all temps from the MacroAssembler. They are used arbitrarily below. |
| UseScratchRegisterScope temps(&masm); |
| temps.ExcludeAll(); |
| |
| // The literal base is chosen to have two useful properties: |
| // * When multiplied (using an integer) by small values (such as a register |
| // index), this value is clearly readable in the result. |
| // * The value is not formed from repeating fixed-size smaller values, so it |
| // can be used to detect endianness-related errors. |
| // * It is never a floating-point NaN, and will therefore always compare |
| // equal to itself. |
| uint64_t literal_base = 0x0100001000100101; |
| |
| { |
| VIXL_ASSERT(__ StackPointer().Is(sp)); |
| __ Mov(stack_pointer, __ StackPointer()); |
| __ SetStackPointer(stack_pointer); |
| |
| int i; |
| |
| // Initialize the registers, using X registers to load the literal. |
| __ Mov(x0, 0); |
| __ Mov(x1, literal_base); |
| for (i = 0; i < reg_count; i++) { |
| // Always write into the D register, to ensure that the upper word is |
| // properly ignored by Push when testing S registers. |
| __ Fmov(d[i], x0); |
| // Calculate the next literal. |
| __ Add(x0, x0, x1); |
| } |
| |
| // Claim memory first, as requested. |
| __ Claim(claim); |
| |
| switch (push_method) { |
| case PushPopByFour: |
| // Push high-numbered registers first (to the highest addresses). |
| for (i = reg_count; i >= 4; i -= 4) { |
| __ Push(v[i - 1], v[i - 2], v[i - 3], v[i - 4]); |
| } |
| // Finish off the leftovers. |
| switch (i) { |
| case 3: |
| __ Push(v[2], v[1], v[0]); |
| break; |
| case 2: |
| __ Push(v[1], v[0]); |
| break; |
| case 1: |
| __ Push(v[0]); |
| break; |
| default: |
| VIXL_ASSERT(i == 0); |
| break; |
| } |
| break; |
| case PushPopRegList: |
| __ PushSizeRegList(list, reg_size, CPURegister::kVRegister); |
| break; |
| } |
| |
| // Clobber all the registers, to ensure that they get repopulated by Pop. |
| ClobberFP(&masm, list); |
| |
| switch (pop_method) { |
| case PushPopByFour: |
| // Pop low-numbered registers first (from the lowest addresses). |
| for (i = 0; i <= (reg_count - 4); i += 4) { |
| __ Pop(v[i], v[i + 1], v[i + 2], v[i + 3]); |
| } |
| // Finish off the leftovers. |
| switch (reg_count - i) { |
| case 3: |
| __ Pop(v[i], v[i + 1], v[i + 2]); |
| break; |
| case 2: |
| __ Pop(v[i], v[i + 1]); |
| break; |
| case 1: |
| __ Pop(v[i]); |
| break; |
| default: |
| VIXL_ASSERT(i == reg_count); |
| break; |
| } |
| break; |
| case PushPopRegList: |
| __ PopSizeRegList(list, reg_size, CPURegister::kVRegister); |
| break; |
| } |
| |
| // Drop memory to restore the stack pointer. |
| __ Drop(claim); |
| |
| __ Mov(sp, __ StackPointer()); |
| __ SetStackPointer(sp); |
| } |
| |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| // Check that the register contents were preserved. |
| // Always use ASSERT_EQUAL_FP64, even when testing S registers, so we can |
| // test that the upper word was properly cleared by Pop. |
| literal_base &= (0xffffffffffffffff >> (64 - reg_size)); |
| for (int i = 0; i < reg_count; i++) { |
| uint64_t literal = literal_base * i; |
| double expected; |
| memcpy(&expected, &literal, sizeof(expected)); |
| ASSERT_EQUAL_FP64(expected, d[i]); |
| } |
| } |
| } |
| |
| |
| TEST(push_pop_fp_xreg_simple_32) { |
| for (int claim = 0; claim <= 8; claim++) { |
| for (int count = 0; count <= 8; count++) { |
| PushPopFPSimpleHelper(count, |
| claim, |
| kSRegSize, |
| PushPopByFour, |
| PushPopByFour); |
| PushPopFPSimpleHelper(count, |
| claim, |
| kSRegSize, |
| PushPopByFour, |
| PushPopRegList); |
| PushPopFPSimpleHelper(count, |
| claim, |
| kSRegSize, |
| PushPopRegList, |
| PushPopByFour); |
| PushPopFPSimpleHelper(count, |
| claim, |
| kSRegSize, |
| PushPopRegList, |
| PushPopRegList); |
| } |
| // Test with the maximum number of registers. |
| PushPopFPSimpleHelper(kPushPopFPUseMaxRegCount, |
| claim, |
| kSRegSize, |
| PushPopByFour, |
| PushPopByFour); |
| PushPopFPSimpleHelper(kPushPopFPUseMaxRegCount, |
| claim, |
| kSRegSize, |
| PushPopByFour, |
| PushPopRegList); |
| PushPopFPSimpleHelper(kPushPopFPUseMaxRegCount, |
| claim, |
| kSRegSize, |
| PushPopRegList, |
| PushPopByFour); |
| PushPopFPSimpleHelper(kPushPopFPUseMaxRegCount, |
| claim, |
| kSRegSize, |
| PushPopRegList, |
| PushPopRegList); |
| } |
| } |
| |
| |
| TEST(push_pop_fp_xreg_simple_64) { |
| for (int claim = 0; claim <= 8; claim++) { |
| for (int count = 0; count <= 8; count++) { |
| PushPopFPSimpleHelper(count, |
| claim, |
| kDRegSize, |
| PushPopByFour, |
| PushPopByFour); |
| PushPopFPSimpleHelper(count, |
| claim, |
| kDRegSize, |
| PushPopByFour, |
| PushPopRegList); |
| PushPopFPSimpleHelper(count, |
| claim, |
| kDRegSize, |
| PushPopRegList, |
| PushPopByFour); |
| PushPopFPSimpleHelper(count, |
| claim, |
| kDRegSize, |
| PushPopRegList, |
| PushPopRegList); |
| } |
| // Test with the maximum number of registers. |
| PushPopFPSimpleHelper(kPushPopFPUseMaxRegCount, |
| claim, |
| kDRegSize, |
| PushPopByFour, |
| PushPopByFour); |
| PushPopFPSimpleHelper(kPushPopFPUseMaxRegCount, |
| claim, |
| kDRegSize, |
| PushPopByFour, |
| PushPopRegList); |
| PushPopFPSimpleHelper(kPushPopFPUseMaxRegCount, |
| claim, |
| kDRegSize, |
| PushPopRegList, |
| PushPopByFour); |
| PushPopFPSimpleHelper(kPushPopFPUseMaxRegCount, |
| claim, |
| kDRegSize, |
| PushPopRegList, |
| PushPopRegList); |
| } |
| } |
| |
| |
| // Push and pop data using an overlapping combination of Push/Pop and |
| // RegList-based methods. |
| static void PushPopMixedMethodsHelper(int claim, int reg_size) { |
| SETUP(); |
| |
| // Arbitrarily pick a register to use as a stack pointer. |
| const Register& stack_pointer = x5; |
| const RegList allowed = ~stack_pointer.GetBit(); |
| // Work out which registers to use, based on reg_size. |
| Register r[10]; |
| Register x[10]; |
| PopulateRegisterArray(NULL, x, r, reg_size, 10, allowed); |
| |
| // Calculate some handy register lists. |
| RegList r0_to_r3 = 0; |
| for (int i = 0; i <= 3; i++) { |
| r0_to_r3 |= x[i].GetBit(); |
| } |
| RegList r4_to_r5 = 0; |
| for (int i = 4; i <= 5; i++) { |
| r4_to_r5 |= x[i].GetBit(); |
| } |
| RegList r6_to_r9 = 0; |
| for (int i = 6; i <= 9; i++) { |
| r6_to_r9 |= x[i].GetBit(); |
| } |
| |
| // Acquire all temps from the MacroAssembler. They are used arbitrarily below. |
| UseScratchRegisterScope temps(&masm); |
| temps.ExcludeAll(); |
| |
| // The literal base is chosen to have two useful properties: |
| // * When multiplied by small values (such as a register index), this value |
| // is clearly readable in the result. |
| // * The value is not formed from repeating fixed-size smaller values, so it |
| // can be used to detect endianness-related errors. |
| uint64_t literal_base = 0x0100001000100101; |
| |
| START(); |
| { |
| VIXL_ASSERT(__ StackPointer().Is(sp)); |
| __ Mov(stack_pointer, __ StackPointer()); |
| __ SetStackPointer(stack_pointer); |
| |
| // Claim memory first, as requested. |
| __ Claim(claim); |
| |
| __ Mov(x[3], literal_base * 3); |
| __ Mov(x[2], literal_base * 2); |
| __ Mov(x[1], literal_base * 1); |
| __ Mov(x[0], literal_base * 0); |
| |
| __ PushSizeRegList(r0_to_r3, reg_size); |
| __ Push(r[3], r[2]); |
| |
| Clobber(&masm, r0_to_r3); |
| __ PopSizeRegList(r0_to_r3, reg_size); |
| |
| __ Push(r[2], r[1], r[3], r[0]); |
| |
| Clobber(&masm, r4_to_r5); |
| __ Pop(r[4], r[5]); |
| Clobber(&masm, r6_to_r9); |
| __ Pop(r[6], r[7], r[8], r[9]); |
| |
| // Drop memory to restore stack_pointer. |
| __ Drop(claim); |
| |
| __ Mov(sp, __ StackPointer()); |
| __ SetStackPointer(sp); |
| } |
| |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| // Always use ASSERT_EQUAL_64, even when testing W registers, so we can test |
| // that the upper word was properly cleared by Pop. |
| literal_base &= (0xffffffffffffffff >> (64 - reg_size)); |
| |
| ASSERT_EQUAL_64(literal_base * 3, x[9]); |
| ASSERT_EQUAL_64(literal_base * 2, x[8]); |
| ASSERT_EQUAL_64(literal_base * 0, x[7]); |
| ASSERT_EQUAL_64(literal_base * 3, x[6]); |
| ASSERT_EQUAL_64(literal_base * 1, x[5]); |
| ASSERT_EQUAL_64(literal_base * 2, x[4]); |
| } |
| } |
| |
| |
| TEST(push_pop_xreg_mixed_methods_64) { |
| for (int claim = 0; claim <= 8; claim++) { |
| PushPopMixedMethodsHelper(claim, kXRegSize); |
| } |
| } |
| |
| |
| TEST(push_pop_xreg_mixed_methods_32) { |
| for (int claim = 0; claim <= 8; claim++) { |
| PushPopMixedMethodsHelper(claim, kWRegSize); |
| } |
| } |
| |
| |
| // Push and pop data using overlapping X- and W-sized quantities. |
| static void PushPopWXOverlapHelper(int reg_count, int claim) { |
| SETUP(); |
| |
| // Arbitrarily pick a register to use as a stack pointer. |
| const Register& stack_pointer = x10; |
| const RegList allowed = ~stack_pointer.GetBit(); |
| if (reg_count == kPushPopUseMaxRegCount) { |
| reg_count = CountSetBits(allowed, kNumberOfRegisters); |
| } |
| // Work out which registers to use, based on reg_size. |
| Register w[kNumberOfRegisters]; |
| Register x[kNumberOfRegisters]; |
| RegList list = PopulateRegisterArray(w, x, NULL, 0, reg_count, allowed); |
| |
| // The number of W-sized slots we expect to pop. When we pop, we alternate |
| // between W and X registers, so we need reg_count*1.5 W-sized slots. |
| int const requested_w_slots = reg_count + reg_count / 2; |
| |
| // Track what _should_ be on the stack, using W-sized slots. |
| static int const kMaxWSlots = kNumberOfRegisters + kNumberOfRegisters / 2; |
| uint32_t stack[kMaxWSlots]; |
| for (int i = 0; i < kMaxWSlots; i++) { |
| stack[i] = 0xdeadbeef; |
| } |
| |
| // Acquire all temps from the MacroAssembler. They are used arbitrarily below. |
| UseScratchRegisterScope temps(&masm); |
| temps.ExcludeAll(); |
| |
| // The literal base is chosen to have two useful properties: |
| // * When multiplied by small values (such as a register index), this value |
| // is clearly readable in the result. |
| // * The value is not formed from repeating fixed-size smaller values, so it |
| // can be used to detect endianness-related errors. |
| static uint64_t const literal_base = 0x0100001000100101; |
| static uint64_t const literal_base_hi = literal_base >> 32; |
| static uint64_t const literal_base_lo = literal_base & 0xffffffff; |
| static uint64_t const literal_base_w = literal_base & 0xffffffff; |
| |
| START(); |
| { |
| VIXL_ASSERT(__ StackPointer().Is(sp)); |
| __ Mov(stack_pointer, __ StackPointer()); |
| __ SetStackPointer(stack_pointer); |
| |
| // Initialize the registers. |
| for (int i = 0; i < reg_count; i++) { |
| // Always write into the X register, to ensure that the upper word is |
| // properly ignored by Push when testing W registers. |
| __ Mov(x[i], literal_base * i); |
| } |
| |
| // Claim memory first, as requested. |
| __ Claim(claim); |
| |
| // The push-pop pattern is as follows: |
| // Push: Pop: |
| // x[0](hi) -> w[0] |
| // x[0](lo) -> x[1](hi) |
| // w[1] -> x[1](lo) |
| // w[1] -> w[2] |
| // x[2](hi) -> x[2](hi) |
| // x[2](lo) -> x[2](lo) |
| // x[2](hi) -> w[3] |
| // x[2](lo) -> x[4](hi) |
| // x[2](hi) -> x[4](lo) |
| // x[2](lo) -> w[5] |
| // w[3] -> x[5](hi) |
| // w[3] -> x[6](lo) |
| // w[3] -> w[7] |
| // w[3] -> x[8](hi) |
| // x[4](hi) -> x[8](lo) |
| // x[4](lo) -> w[9] |
| // ... pattern continues ... |
| // |
| // That is, registers are pushed starting with the lower numbers, |
| // alternating between x and w registers, and pushing i%4+1 copies of each, |
| // where i is the register number. |
| // Registers are popped starting with the higher numbers one-by-one, |
| // alternating between x and w registers, but only popping one at a time. |
| // |
| // This pattern provides a wide variety of alignment effects and overlaps. |
| |
| // ---- Push ---- |
| |
| int active_w_slots = 0; |
| for (int i = 0; active_w_slots < requested_w_slots; i++) { |
| VIXL_ASSERT(i < reg_count); |
| // In order to test various arguments to PushMultipleTimes, and to try to |
| // exercise different alignment and overlap effects, we push each |
| // register a different number of times. |
| int times = i % 4 + 1; |
| if (i & 1) { |
| // Push odd-numbered registers as W registers. |
| __ PushMultipleTimes(times, w[i]); |
| // Fill in the expected stack slots. |
| for (int j = 0; j < times; j++) { |
| if (w[i].Is(wzr)) { |
| // The zero register always writes zeroes. |
| stack[active_w_slots++] = 0; |
| } else { |
| stack[active_w_slots++] = literal_base_w * i; |
| } |
| } |
| } else { |
| // Push even-numbered registers as X registers. |
| __ PushMultipleTimes(times, x[i]); |
| // Fill in the expected stack slots. |
| for (int j = 0; j < times; j++) { |
| if (x[i].Is(xzr)) { |
| // The zero register always writes zeroes. |
| stack[active_w_slots++] = 0; |
| stack[active_w_slots++] = 0; |
| } else { |
| stack[active_w_slots++] = literal_base_hi * i; |
| stack[active_w_slots++] = literal_base_lo * i; |
| } |
| } |
| } |
| } |
| // Because we were pushing several registers at a time, we probably pushed |
| // more than we needed to. |
| if (active_w_slots > requested_w_slots) { |
| __ Drop((active_w_slots - requested_w_slots) * kWRegSizeInBytes); |
| // Bump the number of active W-sized slots back to where it should be, |
| // and fill the empty space with a dummy value. |
| do { |
| stack[active_w_slots--] = 0xdeadbeef; |
| } while (active_w_slots > requested_w_slots); |
| } |
| |
| // ---- Pop ---- |
| |
| Clobber(&masm, list); |
| |
| // If popping an even number of registers, the first one will be X-sized. |
| // Otherwise, the first one will be W-sized. |
| bool next_is_64 = !(reg_count & 1); |
| for (int i = reg_count - 1; i >= 0; i--) { |
| if (next_is_64) { |
| __ Pop(x[i]); |
| active_w_slots -= 2; |
| } else { |
| __ Pop(w[i]); |
| active_w_slots -= 1; |
| } |
| next_is_64 = !next_is_64; |
| } |
| VIXL_ASSERT(active_w_slots == 0); |
| |
| // Drop memory to restore stack_pointer. |
| __ Drop(claim); |
| |
| __ Mov(sp, __ StackPointer()); |
| __ SetStackPointer(sp); |
| } |
| |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| int slot = 0; |
| for (int i = 0; i < reg_count; i++) { |
| // Even-numbered registers were written as W registers. |
| // Odd-numbered registers were written as X registers. |
| bool expect_64 = (i & 1); |
| uint64_t expected; |
| |
| if (expect_64) { |
| uint64_t hi = stack[slot++]; |
| uint64_t lo = stack[slot++]; |
| expected = (hi << 32) | lo; |
| } else { |
| expected = stack[slot++]; |
| } |
| |
| // Always use ASSERT_EQUAL_64, even when testing W registers, so we can |
| // test that the upper word was properly cleared by Pop. |
| if (x[i].Is(xzr)) { |
| ASSERT_EQUAL_64(0, x[i]); |
| } else { |
| ASSERT_EQUAL_64(expected, x[i]); |
| } |
| } |
| VIXL_ASSERT(slot == requested_w_slots); |
| } |
| } |
| |
| |
| TEST(push_pop_xreg_wx_overlap) { |
| for (int claim = 0; claim <= 8; claim++) { |
| for (int count = 1; count <= 8; count++) { |
| PushPopWXOverlapHelper(count, claim); |
| } |
| // Test with the maximum number of registers. |
| PushPopWXOverlapHelper(kPushPopUseMaxRegCount, claim); |
| } |
| } |
| |
| |
| TEST(push_pop_sp) { |
| SETUP(); |
| |
| START(); |
| |
| VIXL_ASSERT(sp.Is(__ StackPointer())); |
| |
| // Acquire all temps from the MacroAssembler. They are used arbitrarily below. |
| UseScratchRegisterScope temps(&masm); |
| temps.ExcludeAll(); |
| |
| __ Mov(x3, 0x3333333333333333); |
| __ Mov(x2, 0x2222222222222222); |
| __ Mov(x1, 0x1111111111111111); |
| __ Mov(x0, 0x0000000000000000); |
| __ Claim(2 * kXRegSizeInBytes); |
| __ PushXRegList(x0.GetBit() | x1.GetBit() | x2.GetBit() | x3.GetBit()); |
| __ Push(x3, x2); |
| __ PopXRegList(x0.GetBit() | x1.GetBit() | x2.GetBit() | x3.GetBit()); |
| __ Push(x2, x1, x3, x0); |
| __ Pop(x4, x5); |
| __ Pop(x6, x7, x8, x9); |
| |
| __ Claim(2 * kXRegSizeInBytes); |
| __ PushWRegList(w0.GetBit() | w1.GetBit() | w2.GetBit() | w3.GetBit()); |
| __ Push(w3, w1, w2, w0); |
| __ PopWRegList(w10.GetBit() | w11.GetBit() | w12.GetBit() | w13.GetBit()); |
| __ Pop(w14, w15, w16, w17); |
| |
| __ Claim(2 * kXRegSizeInBytes); |
| __ Push(w2, w2, w1, w1); |
| __ Push(x3, x3); |
| __ Pop(w18, w19, w20, w21); |
| __ Pop(x22, x23); |
| |
| __ Claim(2 * kXRegSizeInBytes); |
| __ PushXRegList(x1.GetBit() | x22.GetBit()); |
| __ PopXRegList(x24.GetBit() | x26.GetBit()); |
| |
| __ Claim(2 * kXRegSizeInBytes); |
| __ PushWRegList(w1.GetBit() | w2.GetBit() | w4.GetBit() | w22.GetBit()); |
| __ PopWRegList(w25.GetBit() | w27.GetBit() | w28.GetBit() | w29.GetBit()); |
| |
| __ Claim(2 * kXRegSizeInBytes); |
| __ PushXRegList(0); |
| __ PopXRegList(0); |
| __ PushXRegList(0xffffffff); |
| __ PopXRegList(0xffffffff); |
| __ Drop(12 * kXRegSizeInBytes); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0x1111111111111111, x3); |
| ASSERT_EQUAL_64(0x0000000000000000, x2); |
| ASSERT_EQUAL_64(0x3333333333333333, x1); |
| ASSERT_EQUAL_64(0x2222222222222222, x0); |
| ASSERT_EQUAL_64(0x3333333333333333, x9); |
| ASSERT_EQUAL_64(0x2222222222222222, x8); |
| ASSERT_EQUAL_64(0x0000000000000000, x7); |
| ASSERT_EQUAL_64(0x3333333333333333, x6); |
| ASSERT_EQUAL_64(0x1111111111111111, x5); |
| ASSERT_EQUAL_64(0x2222222222222222, x4); |
| |
| ASSERT_EQUAL_32(0x11111111U, w13); |
| ASSERT_EQUAL_32(0x33333333U, w12); |
| ASSERT_EQUAL_32(0x00000000U, w11); |
| ASSERT_EQUAL_32(0x22222222U, w10); |
| ASSERT_EQUAL_32(0x11111111U, w17); |
| ASSERT_EQUAL_32(0x00000000U, w16); |
| ASSERT_EQUAL_32(0x33333333U, w15); |
| ASSERT_EQUAL_32(0x22222222U, w14); |
| |
| ASSERT_EQUAL_32(0x11111111U, w18); |
| ASSERT_EQUAL_32(0x11111111U, w19); |
| ASSERT_EQUAL_32(0x11111111U, w20); |
| ASSERT_EQUAL_32(0x11111111U, w21); |
| ASSERT_EQUAL_64(0x3333333333333333, x22); |
| ASSERT_EQUAL_64(0x0000000000000000, x23); |
| |
| ASSERT_EQUAL_64(0x3333333333333333, x24); |
| ASSERT_EQUAL_64(0x3333333333333333, x26); |
| |
| ASSERT_EQUAL_32(0x33333333U, w25); |
| ASSERT_EQUAL_32(0x00000000U, w27); |
| ASSERT_EQUAL_32(0x22222222U, w28); |
| ASSERT_EQUAL_32(0x33333333U, w29); |
| } |
| } |
| |
| |
| TEST(printf) { |
| // RegisterDump::Dump uses NEON. |
| // Printf uses FP to cast FP arguments to doubles. |
| SETUP_WITH_FEATURES(CPUFeatures::kNEON, CPUFeatures::kFP); |
| |
| START(); |
| |
| char const* test_plain_string = "Printf with no arguments.\n"; |
| char const* test_substring = "'This is a substring.'"; |
| RegisterDump before; |
| |
| // Initialize x29 to the value of the stack pointer. We will use x29 as a |
| // temporary stack pointer later, and initializing it in this way allows the |
| // RegisterDump check to pass. |
| __ Mov(x29, __ StackPointer()); |
| |
| // Test simple integer arguments. |
| __ Mov(x0, 1234); |
| __ Mov(x1, 0x1234); |
| |
| // Test simple floating-point arguments. |
| __ Fmov(d0, 1.234); |
| |
| // Test pointer (string) arguments. |
| __ Mov(x2, reinterpret_cast<uintptr_t>(test_substring)); |
| |
| // Test the maximum number of arguments, and sign extension. |
| __ Mov(w3, 0xffffffff); |
| __ Mov(w4, 0xffffffff); |
| __ Mov(x5, 0xffffffffffffffff); |
| __ Mov(x6, 0xffffffffffffffff); |
| __ Fmov(s1, 1.234); |
| __ Fmov(s2, 2.345); |
| __ Fmov(d3, 3.456); |
| __ Fmov(d4, 4.567); |
| |
| // Test printing callee-saved registers. |
| __ Mov(x28, 0x123456789abcdef); |
| __ Fmov(d10, 42.0); |
| |
| // Test with three arguments. |
| __ Mov(x10, 3); |
| __ Mov(x11, 40); |
| __ Mov(x12, 500); |
| |
| // A single character. |
| __ Mov(w13, 'x'); |
| |
| // Check that we don't clobber any registers. |
| before.Dump(&masm); |
| |
| __ Printf(test_plain_string); // NOLINT(runtime/printf) |
| __ Printf("x0: %" PRId64 ", x1: 0x%08" PRIx64 "\n", x0, x1); |
| __ Printf("w5: %" PRId32 ", x5: %" PRId64 "\n", w5, x5); |
| __ Printf("d0: %f\n", d0); |
| __ Printf("Test %%s: %s\n", x2); |
| __ Printf("w3(uint32): %" PRIu32 "\nw4(int32): %" PRId32 |
| "\n" |
| "x5(uint64): %" PRIu64 "\nx6(int64): %" PRId64 "\n", |
| w3, |
| w4, |
| x5, |
| x6); |
| __ Printf("%%f: %f\n%%g: %g\n%%e: %e\n%%E: %E\n", s1, s2, d3, d4); |
| __ Printf("0x%" PRIx32 ", 0x%" PRIx64 "\n", w28, x28); |
| __ Printf("%g\n", d10); |
| __ Printf("%%%%%s%%%c%%\n", x2, w13); |
| |
| // Print the stack pointer (sp). |
| __ Printf("StackPointer(sp): 0x%016" PRIx64 ", 0x%08" PRIx32 "\n", |
| __ StackPointer(), |
| __ StackPointer().W()); |
| |
| // Test with a different stack pointer. |
| const Register old_stack_pointer = __ StackPointer(); |
| __ Mov(x29, old_stack_pointer); |
| __ SetStackPointer(x29); |
| // Print the stack pointer (not sp). |
| __ Printf("StackPointer(not sp): 0x%016" PRIx64 ", 0x%08" PRIx32 "\n", |
| __ StackPointer(), |
| __ StackPointer().W()); |
| __ Mov(old_stack_pointer, __ StackPointer()); |
| __ SetStackPointer(old_stack_pointer); |
| |
| // Test with three arguments. |
| __ Printf("3=%u, 4=%u, 5=%u\n", x10, x11, x12); |
| |
| // Mixed argument types. |
| __ Printf("w3: %" PRIu32 ", s1: %f, x5: %" PRIu64 ", d3: %f\n", |
| w3, |
| s1, |
| x5, |
| d3); |
| __ Printf("s1: %f, d3: %f, w3: %" PRId32 ", x5: %" PRId64 "\n", |
| s1, |
| d3, |
| w3, |
| x5); |
| |
| END(); |
| if (CAN_RUN()) { |
| RUN(); |
| |
| // We cannot easily test the output of the Printf sequences, and because |
| // Printf preserves all registers by default, we can't look at the number of |
| // bytes that were printed. However, the printf_no_preserve test should |
| // check |
| // that, and here we just test that we didn't clobber any registers. |
| ASSERT_EQUAL_REGISTERS(before); |
| } |
| } |
| |
| |
| TEST(printf_no_preserve) { |
| // PrintfNoPreserve uses FP to cast FP arguments to doubles. |
| SETUP_WITH_FEATURES(CPUFeatures::kFP); |
| |
| START(); |
| |
| char const* test_plain_string = "Printf with no arguments.\n"; |
| char const* test_substring = "'This is a substring.'"; |
| |
| __ PrintfNoPreserve(test_plain_string); |
| __ Mov(x19, x0); |
| |
| // Test simple integer arguments. |
| __ Mov(x0, 1234); |
| __ Mov(x1, 0x1234); |
| __ PrintfNoPreserve("x0: %" PRId64 ", x1: 0x%08" PRIx64 "\n", x0, x1); |
| __ Mov(x20, x0); |
| |
| // Test simple floating-point arguments. |
| __ Fmov(d0, 1.234); |
| __ PrintfNoPreserve("d0: %f\n", d0); |
| __ Mov(x21, x0); |
| |
| // Test pointer (string) arguments. |
| __ Mov(x2, reinterpret_cast<uintptr_t>(test_substring)); |
| __ PrintfNoPreserve("Test %%s: %s\n", x2); |
| __ Mov(x22, x0); |
| |
| // Test the maximum number of arguments, and sign extension. |
| __ Mov(w3, 0xffffffff); |
| __ Mov(w4, 0xffffffff); |
| __ Mov(x5, 0xffffffffffffffff); |
| __ Mov(x6, 0xffffffffffffffff); |
| __ PrintfNoPreserve("w3(uint32): %" PRIu32 "\nw4(int32): %" PRId32 |
| "\n" |
| "x5(uint64): %" PRIu64 "\nx6(int64): %" PRId64 "\n", |
| w3, |
| w4, |
| x5, |
| x6); |
| __ Mov(x23, x0); |
| |
| __ Fmov(s1, 1.234); |
| __ Fmov(s2, 2.345); |
| __ Fmov(d3, 3.456); |
| __ Fmov(d4, 4.567); |
| __ PrintfNoPreserve("%%f: %f\n%%g: %g\n%%e: %e\n%%E: %E\n", s1, s2, d3, d4); |
| __ Mov(x24, x0); |
| |
| // Test printing callee-saved registers. |
| __ Mov(x28, 0x123456789abcdef); |
| __ PrintfNoPreserve("0x%" PRIx32 ", 0x%" PRIx64 "\n", w28, x28); |
| __ Mov(x25, x0); |
| |
| __ Fmov(d10, 42.0); |
| __ PrintfNoPreserve("%g\n", d10); |
| __ Mov(x26, x0); |
| |
| // Test with a different stack pointer. |
| const Register old_stack_pointer = __ StackPointer(); |
| __ Mov(x29, old_stack_pointer); |
| __ SetStackPointer(x29); |
| // Print the stack pointer (not sp). |
| __ PrintfNoPreserve("StackPointer(not sp): 0x%016" PRIx64 ", 0x%08" PRIx32 |
| "\n", |
| __ StackPointer(), |
| __ StackPointer().W()); |
| __ Mov(x27, x0); |
| __ Mov(old_stack_pointer, __ StackPointer()); |
| __ SetStackPointer(old_stack_pointer); |
| |
| // Test with three arguments. |
| __ Mov(x3, 3); |
| __ Mov(x4, 40); |
| __ Mov(x5, 500); |
| __ PrintfNoPreserve("3=%u, 4=%u, 5=%u\n", x3, x4, x5); |
| __ Mov(x28, x0); |
| |
| // Mixed argument types. |
| __ Mov(w3, 0xffffffff); |
| __ Fmov(s1, 1.234); |
| __ Mov(x5, 0xffffffffffffffff); |
| __ Fmov(d3, 3.456); |
| __ PrintfNoPreserve("w3: %" PRIu32 ", s1: %f, x5: %" PRIu64 ", d3: %f\n", |
| w3, |
| s1, |
| x5, |
| d3); |
| __ Mov(x29, x0); |
| |
| END(); |
| if (CAN_RUN()) { |
| RUN(); |
| |
| // We cannot easily test the exact output of the Printf sequences, but we |
| // can |
| // use the return code to check that the string length was correct. |
| |
| // Printf with no arguments. |
| ASSERT_EQUAL_64(strlen(test_plain_string), x19); |
| // x0: 1234, x1: 0x00001234 |
| ASSERT_EQUAL_64(25, x20); |
| // d0: 1.234000 |
| ASSERT_EQUAL_64(13, x21); |
| // Test %s: 'This is a substring.' |
| ASSERT_EQUAL_64(32, x22); |
| // w3(uint32): 4294967295 |
| // w4(int32): -1 |
| // x5(uint64): 18446744073709551615 |
| // x6(int64): -1 |
| ASSERT_EQUAL_64(23 + 14 + 33 + 14, x23); |
| // %f: 1.234000 |
| // %g: 2.345 |
| // %e: 3.456000e+00 |
| // %E: 4.567000E+00 |
| ASSERT_EQUAL_64(13 + 10 + 17 + 17, x24); |
| // 0x89abcdef, 0x123456789abcdef |
| ASSERT_EQUAL_64(30, x25); |
| // 42 |
| ASSERT_EQUAL_64(3, x26); |
| // StackPointer(not sp): 0x00007fb037ae2370, 0x37ae2370 |
| // Note: This is an example value, but the field width is fixed here so the |
| // string length is still predictable. |
| ASSERT_EQUAL_64(53, x27); |
| // 3=3, 4=40, 5=500 |
| ASSERT_EQUAL_64(17, x28); |
| // w3: 4294967295, s1: 1.234000, x5: 18446744073709551615, d3: 3.456000 |
| ASSERT_EQUAL_64(69, x29); |
| } |
| } |
| |
| |
| TEST(trace) { |
| // The Trace helper should not generate any code unless the simulator is being |
| // used. |
| SETUP(); |
| START(); |
| |
| Label start; |
| __ Bind(&start); |
| __ Trace(LOG_ALL, TRACE_ENABLE); |
| __ Trace(LOG_ALL, TRACE_DISABLE); |
| if (masm.GenerateSimulatorCode()) { |
| VIXL_CHECK(__ GetSizeOfCodeGeneratedSince(&start) > 0); |
| } else { |
| VIXL_CHECK(__ GetSizeOfCodeGeneratedSince(&start) == 0); |
| } |
| |
| END(); |
| } |
| |
| |
| TEST(log) { |
| // The Log helper should not generate any code unless the simulator is being |
| // used. |
| SETUP(); |
| START(); |
| |
| Label start; |
| __ Bind(&start); |
| __ Log(LOG_ALL); |
| if (masm.GenerateSimulatorCode()) { |
| VIXL_CHECK(__ GetSizeOfCodeGeneratedSince(&start) > 0); |
| } else { |
| VIXL_CHECK(__ GetSizeOfCodeGeneratedSince(&start) == 0); |
| } |
| |
| END(); |
| } |
| |
| |
| TEST(blr_lr) { |
| // A simple test to check that the simulator correcty handle "blr lr". |
| SETUP(); |
| |
| START(); |
| Label target; |
| Label end; |
| |
| __ Mov(x0, 0x0); |
| __ Adr(lr, &target); |
| |
| __ Blr(lr); |
| __ Mov(x0, 0xdeadbeef); |
| __ B(&end); |
| |
| __ Bind(&target); |
| __ Mov(x0, 0xc001c0de); |
| |
| __ Bind(&end); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0xc001c0de, x0); |
| } |
| } |
| |
| |
| TEST(barriers) { |
| // Generate all supported barriers, this is just a smoke test |
| SETUP(); |
| |
| START(); |
| |
| // DMB |
| __ Dmb(FullSystem, BarrierAll); |
| __ Dmb(FullSystem, BarrierReads); |
| __ Dmb(FullSystem, BarrierWrites); |
| __ Dmb(FullSystem, BarrierOther); |
| |
| __ Dmb(InnerShareable, BarrierAll); |
| __ Dmb(InnerShareable, BarrierReads); |
| __ Dmb(InnerShareable, BarrierWrites); |
| __ Dmb(InnerShareable, BarrierOther); |
| |
| __ Dmb(NonShareable, BarrierAll); |
| __ Dmb(NonShareable, BarrierReads); |
| __ Dmb(NonShareable, BarrierWrites); |
| __ Dmb(NonShareable, BarrierOther); |
| |
| __ Dmb(OuterShareable, BarrierAll); |
| __ Dmb(OuterShareable, BarrierReads); |
| __ Dmb(OuterShareable, BarrierWrites); |
| __ Dmb(OuterShareable, BarrierOther); |
| |
| // DSB |
| __ Dsb(FullSystem, BarrierAll); |
| __ Dsb(FullSystem, BarrierReads); |
| __ Dsb(FullSystem, BarrierWrites); |
| __ Dsb(FullSystem, BarrierOther); |
| |
| __ Dsb(InnerShareable, BarrierAll); |
| __ Dsb(InnerShareable, BarrierReads); |
| __ Dsb(InnerShareable, BarrierWrites); |
| __ Dsb(InnerShareable, BarrierOther); |
| |
| __ Dsb(NonShareable, BarrierAll); |
| __ Dsb(NonShareable, BarrierReads); |
| __ Dsb(NonShareable, BarrierWrites); |
| __ Dsb(NonShareable, BarrierOther); |
| |
| __ Dsb(OuterShareable, BarrierAll); |
| __ Dsb(OuterShareable, BarrierReads); |
| __ Dsb(OuterShareable, BarrierWrites); |
| __ Dsb(OuterShareable, BarrierOther); |
| |
| // ISB |
| __ Isb(); |
| |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| } |
| } |
| |
| |
| TEST(ldar_stlr) { |
| // The middle value is read, modified, and written. The padding exists only to |
| // check for over-write. |
| uint8_t b[] = {0, 0x12, 0}; |
| uint16_t h[] = {0, 0x1234, 0}; |
| uint32_t w[] = {0, 0x12345678, 0}; |
| uint64_t x[] = {0, 0x123456789abcdef0, 0}; |
| |
| SETUP(); |
| START(); |
| |
| __ Mov(x10, reinterpret_cast<uintptr_t>(&b[1])); |
| __ Ldarb(w0, MemOperand(x10)); |
| __ Add(w0, w0, 1); |
| __ Stlrb(w0, MemOperand(x10)); |
| |
| __ Mov(x10, reinterpret_cast<uintptr_t>(&h[1])); |
| __ Ldarh(w0, MemOperand(x10)); |
| __ Add(w0, w0, 1); |
| __ Stlrh(w0, MemOperand(x10)); |
| |
| __ Mov(x10, reinterpret_cast<uintptr_t>(&w[1])); |
| __ Ldar(w0, MemOperand(x10)); |
| __ Add(w0, w0, 1); |
| __ Stlr(w0, MemOperand(x10)); |
| |
| __ Mov(x10, reinterpret_cast<uintptr_t>(&x[1])); |
| __ Ldar(x0, MemOperand(x10)); |
| __ Add(x0, x0, 1); |
| __ Stlr(x0, MemOperand(x10)); |
| |
| END(); |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_32(0x13, b[1]); |
| ASSERT_EQUAL_32(0x1235, h[1]); |
| ASSERT_EQUAL_32(0x12345679, w[1]); |
| ASSERT_EQUAL_64(0x123456789abcdef1, x[1]); |
| |
| // Check for over-write. |
| ASSERT_EQUAL_32(0, b[0]); |
| ASSERT_EQUAL_32(0, b[2]); |
| ASSERT_EQUAL_32(0, h[0]); |
| ASSERT_EQUAL_32(0, h[2]); |
| ASSERT_EQUAL_32(0, w[0]); |
| ASSERT_EQUAL_32(0, w[2]); |
| ASSERT_EQUAL_64(0, x[0]); |
| ASSERT_EQUAL_64(0, x[2]); |
| } |
| } |
| |
| |
| TEST(ldlar_stllr) { |
| // The middle value is read, modified, and written. The padding exists only to |
| // check for over-write. |
| uint8_t b[] = {0, 0x12, 0}; |
| uint16_t h[] = {0, 0x1234, 0}; |
| uint32_t w[] = {0, 0x12345678, 0}; |
| uint64_t x[] = {0, 0x123456789abcdef0, 0}; |
| |
| SETUP_WITH_FEATURES(CPUFeatures::kLORegions); |
| |
| START(); |
| |
| __ Mov(x10, reinterpret_cast<uintptr_t>(&b[1])); |
| __ Ldlarb(w0, MemOperand(x10)); |
| __ Add(w0, w0, 1); |
| __ Stllrb(w0, MemOperand(x10)); |
| |
| __ Mov(x10, reinterpret_cast<uintptr_t>(&h[1])); |
| __ Ldlarh(w0, MemOperand(x10)); |
| __ Add(w0, w0, 1); |
| __ Stllrh(w0, MemOperand(x10)); |
| |
| __ Mov(x10, reinterpret_cast<uintptr_t>(&w[1])); |
| __ Ldlar(w0, MemOperand(x10)); |
| __ Add(w0, w0, 1); |
| __ Stllr(w0, MemOperand(x10)); |
| |
| __ Mov(x10, reinterpret_cast<uintptr_t>(&x[1])); |
| __ Ldlar(x0, MemOperand(x10)); |
| __ Add(x0, x0, 1); |
| __ Stllr(x0, MemOperand(x10)); |
| |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_32(0x13, b[1]); |
| ASSERT_EQUAL_32(0x1235, h[1]); |
| ASSERT_EQUAL_32(0x12345679, w[1]); |
| ASSERT_EQUAL_64(0x123456789abcdef1, x[1]); |
| |
| // Check for over-write. |
| ASSERT_EQUAL_32(0, b[0]); |
| ASSERT_EQUAL_32(0, b[2]); |
| ASSERT_EQUAL_32(0, h[0]); |
| ASSERT_EQUAL_32(0, h[2]); |
| ASSERT_EQUAL_32(0, w[0]); |
| ASSERT_EQUAL_32(0, w[2]); |
| ASSERT_EQUAL_64(0, x[0]); |
| ASSERT_EQUAL_64(0, x[2]); |
| } |
| } |
| |
| |
| TEST(ldxr_stxr) { |
| // The middle value is read, modified, and written. The padding exists only to |
| // check for over-write. |
| uint8_t b[] = {0, 0x12, 0}; |
| uint16_t h[] = {0, 0x1234, 0}; |
| uint32_t w[] = {0, 0x12345678, 0}; |
| uint64_t x[] = {0, 0x123456789abcdef0, 0}; |
| |
| // As above, but get suitably-aligned values for ldxp and stxp. |
| uint32_t wp_data[] = {0, 0, 0, 0, 0}; |
| uint32_t* wp = AlignUp(wp_data + 1, kWRegSizeInBytes * 2) - 1; |
| wp[1] = 0x12345678; // wp[1] is 64-bit-aligned. |
| wp[2] = 0x87654321; |
| uint64_t xp_data[] = {0, 0, 0, 0, 0}; |
| uint64_t* xp = AlignUp(xp_data + 1, kXRegSizeInBytes * 2) - 1; |
| xp[1] = 0x123456789abcdef0; // xp[1] is 128-bit-aligned. |
| xp[2] = 0x0fedcba987654321; |
| |
| SETUP(); |
| START(); |
| |
| __ Mov(x10, reinterpret_cast<uintptr_t>(&b[1])); |
| Label try_b; |
| __ Bind(&try_b); |
| __ Ldxrb(w0, MemOperand(x10)); |
| __ Add(w0, w0, 1); |
| __ Stxrb(w5, w0, MemOperand(x10)); |
| __ Cbnz(w5, &try_b); |
| |
| __ Mov(x10, reinterpret_cast<uintptr_t>(&h[1])); |
| Label try_h; |
| __ Bind(&try_h); |
| __ Ldxrh(w0, MemOperand(x10)); |
| __ Add(w0, w0, 1); |
| __ Stxrh(w5, w0, MemOperand(x10)); |
| __ Cbnz(w5, &try_h); |
| |
| __ Mov(x10, reinterpret_cast<uintptr_t>(&w[1])); |
| Label try_w; |
| __ Bind(&try_w); |
| __ Ldxr(w0, MemOperand(x10)); |
| __ Add(w0, w0, 1); |
| __ Stxr(w5, w0, MemOperand(x10)); |
| __ Cbnz(w5, &try_w); |
| |
| __ Mov(x10, reinterpret_cast<uintptr_t>(&x[1])); |
| Label try_x; |
| __ Bind(&try_x); |
| __ Ldxr(x0, MemOperand(x10)); |
| __ Add(x0, x0, 1); |
| __ Stxr(w5, x0, MemOperand(x10)); |
| __ Cbnz(w5, &try_x); |
| |
| __ Mov(x10, reinterpret_cast<uintptr_t>(&wp[1])); |
| Label try_wp; |
| __ Bind(&try_wp); |
| __ Ldxp(w0, w1, MemOperand(x10)); |
| __ Add(w0, w0, 1); |
| __ Add(w1, w1, 1); |
| __ Stxp(w5, w0, w1, MemOperand(x10)); |
| __ Cbnz(w5, &try_wp); |
| |
| __ Mov(x10, reinterpret_cast<uintptr_t>(&xp[1])); |
| Label try_xp; |
| __ Bind(&try_xp); |
| __ Ldxp(x0, x1, MemOperand(x10)); |
| __ Add(x0, x0, 1); |
| __ Add(x1, x1, 1); |
| __ Stxp(w5, x0, x1, MemOperand(x10)); |
| __ Cbnz(w5, &try_xp); |
| |
| END(); |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_32(0x13, b[1]); |
| ASSERT_EQUAL_32(0x1235, h[1]); |
| ASSERT_EQUAL_32(0x12345679, w[1]); |
| ASSERT_EQUAL_64(0x123456789abcdef1, x[1]); |
| ASSERT_EQUAL_32(0x12345679, wp[1]); |
| ASSERT_EQUAL_32(0x87654322, wp[2]); |
| ASSERT_EQUAL_64(0x123456789abcdef1, xp[1]); |
| ASSERT_EQUAL_64(0x0fedcba987654322, xp[2]); |
| |
| // Check for over-write. |
| ASSERT_EQUAL_32(0, b[0]); |
| ASSERT_EQUAL_32(0, b[2]); |
| ASSERT_EQUAL_32(0, h[0]); |
| ASSERT_EQUAL_32(0, h[2]); |
| ASSERT_EQUAL_32(0, w[0]); |
| ASSERT_EQUAL_32(0, w[2]); |
| ASSERT_EQUAL_64(0, x[0]); |
| ASSERT_EQUAL_64(0, x[2]); |
| ASSERT_EQUAL_32(0, wp[0]); |
| ASSERT_EQUAL_32(0, wp[3]); |
| ASSERT_EQUAL_64(0, xp[0]); |
| ASSERT_EQUAL_64(0, xp[3]); |
| } |
| } |
| |
| |
| TEST(ldaxr_stlxr) { |
| // The middle value is read, modified, and written. The padding exists only to |
| // check for over-write. |
| uint8_t b[] = {0, 0x12, 0}; |
| uint16_t h[] = {0, 0x1234, 0}; |
| uint32_t w[] = {0, 0x12345678, 0}; |
| uint64_t x[] = {0, 0x123456789abcdef0, 0}; |
| |
| // As above, but get suitably-aligned values for ldxp and stxp. |
| uint32_t wp_data[] = {0, 0, 0, 0, 0}; |
| uint32_t* wp = AlignUp(wp_data + 1, kWRegSizeInBytes * 2) - 1; |
| wp[1] = 0x12345678; // wp[1] is 64-bit-aligned. |
| wp[2] = 0x87654321; |
| uint64_t xp_data[] = {0, 0, 0, 0, 0}; |
| uint64_t* xp = AlignUp(xp_data + 1, kXRegSizeInBytes * 2) - 1; |
| xp[1] = 0x123456789abcdef0; // xp[1] is 128-bit-aligned. |
| xp[2] = 0x0fedcba987654321; |
| |
| SETUP(); |
| START(); |
| |
| __ Mov(x10, reinterpret_cast<uintptr_t>(&b[1])); |
| Label try_b; |
| __ Bind(&try_b); |
| __ Ldaxrb(w0, MemOperand(x10)); |
| __ Add(w0, w0, 1); |
| __ Stlxrb(w5, w0, MemOperand(x10)); |
| __ Cbnz(w5, &try_b); |
| |
| __ Mov(x10, reinterpret_cast<uintptr_t>(&h[1])); |
| Label try_h; |
| __ Bind(&try_h); |
| __ Ldaxrh(w0, MemOperand(x10)); |
| __ Add(w0, w0, 1); |
| __ Stlxrh(w5, w0, MemOperand(x10)); |
| __ Cbnz(w5, &try_h); |
| |
| __ Mov(x10, reinterpret_cast<uintptr_t>(&w[1])); |
| Label try_w; |
| __ Bind(&try_w); |
| __ Ldaxr(w0, MemOperand(x10)); |
| __ Add(w0, w0, 1); |
| __ Stlxr(w5, w0, MemOperand(x10)); |
| __ Cbnz(w5, &try_w); |
| |
| __ Mov(x10, reinterpret_cast<uintptr_t>(&x[1])); |
| Label try_x; |
| __ Bind(&try_x); |
| __ Ldaxr(x0, MemOperand(x10)); |
| __ Add(x0, x0, 1); |
| __ Stlxr(w5, x0, MemOperand(x10)); |
| __ Cbnz(w5, &try_x); |
| |
| __ Mov(x10, reinterpret_cast<uintptr_t>(&wp[1])); |
| Label try_wp; |
| __ Bind(&try_wp); |
| __ Ldaxp(w0, w1, MemOperand(x10)); |
| __ Add(w0, w0, 1); |
| __ Add(w1, w1, 1); |
| __ Stlxp(w5, w0, w1, MemOperand(x10)); |
| __ Cbnz(w5, &try_wp); |
| |
| __ Mov(x10, reinterpret_cast<uintptr_t>(&xp[1])); |
| Label try_xp; |
| __ Bind(&try_xp); |
| __ Ldaxp(x0, x1, MemOperand(x10)); |
| __ Add(x0, x0, 1); |
| __ Add(x1, x1, 1); |
| __ Stlxp(w5, x0, x1, MemOperand(x10)); |
| __ Cbnz(w5, &try_xp); |
| |
| END(); |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_32(0x13, b[1]); |
| ASSERT_EQUAL_32(0x1235, h[1]); |
| ASSERT_EQUAL_32(0x12345679, w[1]); |
| ASSERT_EQUAL_64(0x123456789abcdef1, x[1]); |
| ASSERT_EQUAL_32(0x12345679, wp[1]); |
| ASSERT_EQUAL_32(0x87654322, wp[2]); |
| ASSERT_EQUAL_64(0x123456789abcdef1, xp[1]); |
| ASSERT_EQUAL_64(0x0fedcba987654322, xp[2]); |
| |
| // Check for over-write. |
| ASSERT_EQUAL_32(0, b[0]); |
| ASSERT_EQUAL_32(0, b[2]); |
| ASSERT_EQUAL_32(0, h[0]); |
| ASSERT_EQUAL_32(0, h[2]); |
| ASSERT_EQUAL_32(0, w[0]); |
| ASSERT_EQUAL_32(0, w[2]); |
| ASSERT_EQUAL_64(0, x[0]); |
| ASSERT_EQUAL_64(0, x[2]); |
| ASSERT_EQUAL_32(0, wp[0]); |
| ASSERT_EQUAL_32(0, wp[3]); |
| ASSERT_EQUAL_64(0, xp[0]); |
| ASSERT_EQUAL_64(0, xp[3]); |
| } |
| } |
| |
| |
| TEST(clrex) { |
| // This data should never be written. |
| uint64_t data[] = {0, 0, 0}; |
| uint64_t* data_aligned = AlignUp(data, kXRegSizeInBytes * 2); |
| |
| SETUP(); |
| START(); |
| |
| __ Mov(x10, reinterpret_cast<uintptr_t>(data_aligned)); |
| __ Mov(w6, 0); |
| |
| __ Ldxrb(w0, MemOperand(x10)); |
| __ Clrex(); |
| __ Add(w0, w0, 1); |
| __ Stxrb(w5, w0, MemOperand(x10)); |
| __ Add(w6, w6, w5); |
| |
| __ Ldxrh(w0, MemOperand(x10)); |
| __ Clrex(); |
| __ Add(w0, w0, 1); |
| __ Stxrh(w5, w0, MemOperand(x10)); |
| __ Add(w6, w6, w5); |
| |
| __ Ldxr(w0, MemOperand(x10)); |
| __ Clrex(); |
| __ Add(w0, w0, 1); |
| __ Stxr(w5, w0, MemOperand(x10)); |
| __ Add(w6, w6, w5); |
| |
| __ Ldxr(x0, MemOperand(x10)); |
| __ Clrex(); |
| __ Add(x0, x0, 1); |
| __ Stxr(w5, x0, MemOperand(x10)); |
| __ Add(w6, w6, w5); |
| |
| __ Ldxp(w0, w1, MemOperand(x10)); |
| __ Clrex(); |
| __ Add(w0, w0, 1); |
| __ Add(w1, w1, 1); |
| __ Stxp(w5, w0, w1, MemOperand(x10)); |
| __ Add(w6, w6, w5); |
| |
| __ Ldxp(x0, x1, MemOperand(x10)); |
| __ Clrex(); |
| __ Add(x0, x0, 1); |
| __ Add(x1, x1, 1); |
| __ Stxp(w5, x0, x1, MemOperand(x10)); |
| __ Add(w6, w6, w5); |
| |
| // Acquire-release variants. |
| |
| __ Ldaxrb(w0, MemOperand(x10)); |
| __ Clrex(); |
| __ Add(w0, w0, 1); |
| __ Stlxrb(w5, w0, MemOperand(x10)); |
| __ Add(w6, w6, w5); |
| |
| __ Ldaxrh(w0, MemOperand(x10)); |
| __ Clrex(); |
| __ Add(w0, w0, 1); |
| __ Stlxrh(w5, w0, MemOperand(x10)); |
| __ Add(w6, w6, w5); |
| |
| __ Ldaxr(w0, MemOperand(x10)); |
| __ Clrex(); |
| __ Add(w0, w0, 1); |
| __ Stlxr(w5, w0, MemOperand(x10)); |
| __ Add(w6, w6, w5); |
| |
| __ Ldaxr(x0, MemOperand(x10)); |
| __ Clrex(); |
| __ Add(x0, x0, 1); |
| __ Stlxr(w5, x0, MemOperand(x10)); |
| __ Add(w6, w6, w5); |
| |
| __ Ldaxp(w0, w1, MemOperand(x10)); |
| __ Clrex(); |
| __ Add(w0, w0, 1); |
| __ Add(w1, w1, 1); |
| __ Stlxp(w5, w0, w1, MemOperand(x10)); |
| __ Add(w6, w6, w5); |
| |
| __ Ldaxp(x0, x1, MemOperand(x10)); |
| __ Clrex(); |
| __ Add(x0, x0, 1); |
| __ Add(x1, x1, 1); |
| __ Stlxp(w5, x0, x1, MemOperand(x10)); |
| __ Add(w6, w6, w5); |
| |
| END(); |
| if (CAN_RUN()) { |
| RUN(); |
| |
| // None of the 12 store-exclusives should have succeeded. |
| ASSERT_EQUAL_32(12, w6); |
| |
| ASSERT_EQUAL_64(0, data[0]); |
| ASSERT_EQUAL_64(0, data[1]); |
| ASSERT_EQUAL_64(0, data[2]); |
| } |
| } |
| |
| |
| #ifdef VIXL_INCLUDE_SIMULATOR_AARCH64 |
| // Check that the simulator occasionally makes store-exclusive fail. |
| TEST(ldxr_stxr_fail) { |
| uint64_t data[] = {0, 0, 0}; |
| uint64_t* data_aligned = AlignUp(data, kXRegSizeInBytes * 2); |
| |
| // Impose a hard limit on the number of attempts, so the test cannot hang. |
| static const uint64_t kWatchdog = 10000; |
| Label done; |
| |
| SETUP(); |
| START(); |
| |
| __ Mov(x10, reinterpret_cast<uintptr_t>(data_aligned)); |
| __ Mov(x11, kWatchdog); |
| |
| // This loop is the opposite of what we normally do with ldxr and stxr; we |
| // keep trying until we fail (or the watchdog counter runs out). |
| Label try_b; |
| __ Bind(&try_b); |
| __ Ldxrb(w0, MemOperand(x10)); |
| __ Stxrb(w5, w0, MemOperand(x10)); |
| // Check the watchdog counter. |
| __ Sub(x11, x11, 1); |
| __ Cbz(x11, &done); |
| // Check the exclusive-store result. |
| __ Cbz(w5, &try_b); |
| |
| Label try_h; |
| __ Bind(&try_h); |
| __ Ldxrh(w0, MemOperand(x10)); |
| __ Stxrh(w5, w0, MemOperand(x10)); |
| __ Sub(x11, x11, 1); |
| __ Cbz(x11, &done); |
| __ Cbz(w5, &try_h); |
| |
| Label try_w; |
| __ Bind(&try_w); |
| __ Ldxr(w0, MemOperand(x10)); |
| __ Stxr(w5, w0, MemOperand(x10)); |
| __ Sub(x11, x11, 1); |
| __ Cbz(x11, &done); |
| __ Cbz(w5, &try_w); |
| |
| Label try_x; |
| __ Bind(&try_x); |
| __ Ldxr(x0, MemOperand(x10)); |
| __ Stxr(w5, x0, MemOperand(x10)); |
| __ Sub(x11, x11, 1); |
| __ Cbz(x11, &done); |
| __ Cbz(w5, &try_x); |
| |
| Label try_wp; |
| __ Bind(&try_wp); |
| __ Ldxp(w0, w1, MemOperand(x10)); |
| __ Stxp(w5, w0, w1, MemOperand(x10)); |
| __ Sub(x11, x11, 1); |
| __ Cbz(x11, &done); |
| __ Cbz(w5, &try_wp); |
| |
| Label try_xp; |
| __ Bind(&try_xp); |
| __ Ldxp(x0, x1, MemOperand(x10)); |
| __ Stxp(w5, x0, x1, MemOperand(x10)); |
| __ Sub(x11, x11, 1); |
| __ Cbz(x11, &done); |
| __ Cbz(w5, &try_xp); |
| |
| __ Bind(&done); |
| // Trigger an error if x11 (watchdog) is zero. |
| __ Cmp(x11, 0); |
| __ Cset(x12, eq); |
| |
| END(); |
| if (CAN_RUN()) { |
| RUN(); |
| |
| // Check that the watchdog counter didn't run out. |
| ASSERT_EQUAL_64(0, x12); |
| } |
| } |
| #endif |
| |
| |
| #ifdef VIXL_INCLUDE_SIMULATOR_AARCH64 |
| // Check that the simulator occasionally makes store-exclusive fail. |
| TEST(ldaxr_stlxr_fail) { |
| uint64_t data[] = {0, 0, 0}; |
| uint64_t* data_aligned = AlignUp(data, kXRegSizeInBytes * 2); |
| |
| // Impose a hard limit on the number of attempts, so the test cannot hang. |
| static const uint64_t kWatchdog = 10000; |
| Label done; |
| |
| SETUP(); |
| START(); |
| |
| __ Mov(x10, reinterpret_cast<uintptr_t>(data_aligned)); |
| __ Mov(x11, kWatchdog); |
| |
| // This loop is the opposite of what we normally do with ldxr and stxr; we |
| // keep trying until we fail (or the watchdog counter runs out). |
| Label try_b; |
| __ Bind(&try_b); |
| __ Ldxrb(w0, MemOperand(x10)); |
| __ Stxrb(w5, w0, MemOperand(x10)); |
| // Check the watchdog counter. |
| __ Sub(x11, x11, 1); |
| __ Cbz(x11, &done); |
| // Check the exclusive-store result. |
| __ Cbz(w5, &try_b); |
| |
| Label try_h; |
| __ Bind(&try_h); |
| __ Ldaxrh(w0, MemOperand(x10)); |
| __ Stlxrh(w5, w0, MemOperand(x10)); |
| __ Sub(x11, x11, 1); |
| __ Cbz(x11, &done); |
| __ Cbz(w5, &try_h); |
| |
| Label try_w; |
| __ Bind(&try_w); |
| __ Ldaxr(w0, MemOperand(x10)); |
| __ Stlxr(w5, w0, MemOperand(x10)); |
| __ Sub(x11, x11, 1); |
| __ Cbz(x11, &done); |
| __ Cbz(w5, &try_w); |
| |
| Label try_x; |
| __ Bind(&try_x); |
| __ Ldaxr(x0, MemOperand(x10)); |
| __ Stlxr(w5, x0, MemOperand(x10)); |
| __ Sub(x11, x11, 1); |
| __ Cbz(x11, &done); |
| __ Cbz(w5, &try_x); |
| |
| Label try_wp; |
| __ Bind(&try_wp); |
| __ Ldaxp(w0, w1, MemOperand(x10)); |
| __ Stlxp(w5, w0, w1, MemOperand(x10)); |
| __ Sub(x11, x11, 1); |
| __ Cbz(x11, &done); |
| __ Cbz(w5, &try_wp); |
| |
| Label try_xp; |
| __ Bind(&try_xp); |
| __ Ldaxp(x0, x1, MemOperand(x10)); |
| __ Stlxp(w5, x0, x1, MemOperand(x10)); |
| __ Sub(x11, x11, 1); |
| __ Cbz(x11, &done); |
| __ Cbz(w5, &try_xp); |
| |
| __ Bind(&done); |
| // Trigger an error if x11 (watchdog) is zero. |
| __ Cmp(x11, 0); |
| __ Cset(x12, eq); |
| |
| END(); |
| if (CAN_RUN()) { |
| RUN(); |
| |
| // Check that the watchdog counter didn't run out. |
| ASSERT_EQUAL_64(0, x12); |
| } |
| } |
| #endif |
| |
| TEST(cas_casa_casl_casal_w) { |
| uint64_t data1[] = {0x01234567, 0}; |
| uint64_t data2[] = {0x01234567, 0}; |
| uint64_t data3[] = {0x01234567, 0}; |
| uint64_t data4[] = {0x01234567, 0}; |
| uint64_t data5[] = {0x01234567, 0}; |
| uint64_t data6[] = {0x01234567, 0}; |
| uint64_t data7[] = {0x01234567, 0}; |
| uint64_t data8[] = {0x01234567, 0}; |
| |
| uint64_t* data1_aligned = AlignUp(data1, kXRegSizeInBytes * 2); |
| uint64_t* data2_aligned = AlignUp(data2, kXRegSizeInBytes * 2); |
| uint64_t* data3_aligned = AlignUp(data3, kXRegSizeInBytes * 2); |
| uint64_t* data4_aligned = AlignUp(data4, kXRegSizeInBytes * 2); |
| uint64_t* data5_aligned = AlignUp(data5, kXRegSizeInBytes * 2); |
| uint64_t* data6_aligned = AlignUp(data6, kXRegSizeInBytes * 2); |
| uint64_t* data7_aligned = AlignUp(data7, kXRegSizeInBytes * 2); |
| uint64_t* data8_aligned = AlignUp(data8, kXRegSizeInBytes * 2); |
| |
| SETUP_WITH_FEATURES(CPUFeatures::kAtomics); |
| |
| START(); |
| |
| __ Mov(x21, reinterpret_cast<uintptr_t>(data1_aligned)); |
| __ Mov(x22, reinterpret_cast<uintptr_t>(data2_aligned)); |
| __ Mov(x23, reinterpret_cast<uintptr_t>(data3_aligned)); |
| __ Mov(x24, reinterpret_cast<uintptr_t>(data4_aligned)); |
| __ Mov(x25, reinterpret_cast<uintptr_t>(data5_aligned)); |
| __ Mov(x26, reinterpret_cast<uintptr_t>(data6_aligned)); |
| __ Mov(x27, reinterpret_cast<uintptr_t>(data7_aligned)); |
| __ Mov(x28, reinterpret_cast<uintptr_t>(data8_aligned)); |
| |
| __ Mov(x0, 0xffffffff); |
| |
| __ Mov(x1, 0x76543210); |
| __ Mov(x2, 0x01234567); |
| __ Mov(x3, 0x76543210); |
| __ Mov(x4, 0x01234567); |
| __ Mov(x5, 0x76543210); |
| __ Mov(x6, 0x01234567); |
| __ Mov(x7, 0x76543210); |
| __ Mov(x8, 0x01234567); |
| |
| __ Cas(w1, w0, MemOperand(x21)); |
| __ Cas(w2, w0, MemOperand(x22)); |
| __ Casa(w3, w0, MemOperand(x23)); |
| __ Casa(w4, w0, MemOperand(x24)); |
| __ Casl(w5, w0, MemOperand(x25)); |
| __ Casl(w6, w0, MemOperand(x26)); |
| __ Casal(w7, w0, MemOperand(x27)); |
| __ Casal(w8, w0, MemOperand(x28)); |
| |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0x01234567, x1); |
| ASSERT_EQUAL_64(0x01234567, x2); |
| ASSERT_EQUAL_64(0x01234567, x3); |
| ASSERT_EQUAL_64(0x01234567, x4); |
| ASSERT_EQUAL_64(0x01234567, x5); |
| ASSERT_EQUAL_64(0x01234567, x6); |
| ASSERT_EQUAL_64(0x01234567, x7); |
| ASSERT_EQUAL_64(0x01234567, x8); |
| |
| ASSERT_EQUAL_64(0x01234567, data1[0]); |
| ASSERT_EQUAL_64(0xffffffff, data2[0]); |
| ASSERT_EQUAL_64(0x01234567, data3[0]); |
| ASSERT_EQUAL_64(0xffffffff, data4[0]); |
| ASSERT_EQUAL_64(0x01234567, data5[0]); |
| ASSERT_EQUAL_64(0xffffffff, data6[0]); |
| ASSERT_EQUAL_64(0x01234567, data7[0]); |
| ASSERT_EQUAL_64(0xffffffff, data8[0]); |
| } |
| } |
| |
| TEST(cas_casa_casl_casal_x) { |
| uint64_t data1[] = {0x0123456789abcdef, 0}; |
| uint64_t data2[] = {0x0123456789abcdef, 0}; |
| uint64_t data3[] = {0x0123456789abcdef, 0}; |
| uint64_t data4[] = {0x0123456789abcdef, 0}; |
| uint64_t data5[] = {0x0123456789abcdef, 0}; |
| uint64_t data6[] = {0x0123456789abcdef, 0}; |
| uint64_t data7[] = {0x0123456789abcdef, 0}; |
| uint64_t data8[] = {0x0123456789abcdef, 0}; |
| |
| uint64_t* data1_aligned = AlignUp(data1, kXRegSizeInBytes * 2); |
| uint64_t* data2_aligned = AlignUp(data2, kXRegSizeInBytes * 2); |
| uint64_t* data3_aligned = AlignUp(data3, kXRegSizeInBytes * 2); |
| uint64_t* data4_aligned = AlignUp(data4, kXRegSizeInBytes * 2); |
| uint64_t* data5_aligned = AlignUp(data5, kXRegSizeInBytes * 2); |
| uint64_t* data6_aligned = AlignUp(data6, kXRegSizeInBytes * 2); |
| uint64_t* data7_aligned = AlignUp(data7, kXRegSizeInBytes * 2); |
| uint64_t* data8_aligned = AlignUp(data8, kXRegSizeInBytes * 2); |
| |
| SETUP_WITH_FEATURES(CPUFeatures::kAtomics); |
| |
| START(); |
| |
| __ Mov(x21, reinterpret_cast<uintptr_t>(data1_aligned)); |
| __ Mov(x22, reinterpret_cast<uintptr_t>(data2_aligned)); |
| __ Mov(x23, reinterpret_cast<uintptr_t>(data3_aligned)); |
| __ Mov(x24, reinterpret_cast<uintptr_t>(data4_aligned)); |
| __ Mov(x25, reinterpret_cast<uintptr_t>(data5_aligned)); |
| __ Mov(x26, reinterpret_cast<uintptr_t>(data6_aligned)); |
| __ Mov(x27, reinterpret_cast<uintptr_t>(data7_aligned)); |
| __ Mov(x28, reinterpret_cast<uintptr_t>(data8_aligned)); |
| |
| __ Mov(x0, 0xffffffffffffffff); |
| |
| __ Mov(x1, 0xfedcba9876543210); |
| __ Mov(x2, 0x0123456789abcdef); |
| __ Mov(x3, 0xfedcba9876543210); |
| __ Mov(x4, 0x0123456789abcdef); |
| __ Mov(x5, 0xfedcba9876543210); |
| __ Mov(x6, 0x0123456789abcdef); |
| __ Mov(x7, 0xfedcba9876543210); |
| __ Mov(x8, 0x0123456789abcdef); |
| |
| __ Cas(x1, x0, MemOperand(x21)); |
| __ Cas(x2, x0, MemOperand(x22)); |
| __ Casa(x3, x0, MemOperand(x23)); |
| __ Casa(x4, x0, MemOperand(x24)); |
| __ Casl(x5, x0, MemOperand(x25)); |
| __ Casl(x6, x0, MemOperand(x26)); |
| __ Casal(x7, x0, MemOperand(x27)); |
| __ Casal(x8, x0, MemOperand(x28)); |
| |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0x0123456789abcdef, x1); |
| ASSERT_EQUAL_64(0x0123456789abcdef, x2); |
| ASSERT_EQUAL_64(0x0123456789abcdef, x3); |
| ASSERT_EQUAL_64(0x0123456789abcdef, x4); |
| ASSERT_EQUAL_64(0x0123456789abcdef, x5); |
| ASSERT_EQUAL_64(0x0123456789abcdef, x6); |
| ASSERT_EQUAL_64(0x0123456789abcdef, x7); |
| ASSERT_EQUAL_64(0x0123456789abcdef, x8); |
| |
| ASSERT_EQUAL_64(0x0123456789abcdef, data1[0]); |
| ASSERT_EQUAL_64(0xffffffffffffffff, data2[0]); |
| ASSERT_EQUAL_64(0x0123456789abcdef, data3[0]); |
| ASSERT_EQUAL_64(0xffffffffffffffff, data4[0]); |
| ASSERT_EQUAL_64(0x0123456789abcdef, data5[0]); |
| ASSERT_EQUAL_64(0xffffffffffffffff, data6[0]); |
| ASSERT_EQUAL_64(0x0123456789abcdef, data7[0]); |
| ASSERT_EQUAL_64(0xffffffffffffffff, data8[0]); |
| } |
| } |
| |
| TEST(casb_casab_caslb_casalb) { |
| uint64_t data1[] = {0x01234567, 0}; |
| uint64_t data2[] = {0x01234567, 0}; |
| uint64_t data3[] = {0x01234567, 0}; |
| uint64_t data4[] = {0x01234567, 0}; |
| uint64_t data5[] = {0x01234567, 0}; |
| uint64_t data6[] = {0x01234567, 0}; |
| uint64_t data7[] = {0x01234567, 0}; |
| uint64_t data8[] = {0x01234567, 0}; |
| |
| uint64_t* data1_aligned = AlignUp(data1, kXRegSizeInBytes * 2); |
| uint64_t* data2_aligned = AlignUp(data2, kXRegSizeInBytes * 2); |
| uint64_t* data3_aligned = AlignUp(data3, kXRegSizeInBytes * 2); |
| uint64_t* data4_aligned = AlignUp(data4, kXRegSizeInBytes * 2); |
| uint64_t* data5_aligned = AlignUp(data5, kXRegSizeInBytes * 2); |
| uint64_t* data6_aligned = AlignUp(data6, kXRegSizeInBytes * 2); |
| uint64_t* data7_aligned = AlignUp(data7, kXRegSizeInBytes * 2); |
| uint64_t* data8_aligned = AlignUp(data8, kXRegSizeInBytes * 2); |
| |
| SETUP_WITH_FEATURES(CPUFeatures::kAtomics); |
| |
| START(); |
| |
| __ Mov(x21, reinterpret_cast<uintptr_t>(data1_aligned)); |
| __ Mov(x22, reinterpret_cast<uintptr_t>(data2_aligned)); |
| __ Mov(x23, reinterpret_cast<uintptr_t>(data3_aligned)); |
| __ Mov(x24, reinterpret_cast<uintptr_t>(data4_aligned)); |
| __ Mov(x25, reinterpret_cast<uintptr_t>(data5_aligned)); |
| __ Mov(x26, reinterpret_cast<uintptr_t>(data6_aligned)); |
| __ Mov(x27, reinterpret_cast<uintptr_t>(data7_aligned)); |
| __ Mov(x28, reinterpret_cast<uintptr_t>(data8_aligned)); |
| |
| __ Mov(x0, 0xffffffff); |
| |
| __ Mov(x1, 0x76543210); |
| __ Mov(x2, 0x01234567); |
| __ Mov(x3, 0x76543210); |
| __ Mov(x4, 0x01234567); |
| __ Mov(x5, 0x76543210); |
| __ Mov(x6, 0x01234567); |
| __ Mov(x7, 0x76543210); |
| __ Mov(x8, 0x01234567); |
| |
| __ Casb(w1, w0, MemOperand(x21)); |
| __ Casb(w2, w0, MemOperand(x22)); |
| __ Casab(w3, w0, MemOperand(x23)); |
| __ Casab(w4, w0, MemOperand(x24)); |
| __ Caslb(w5, w0, MemOperand(x25)); |
| __ Caslb(w6, w0, MemOperand(x26)); |
| __ Casalb(w7, w0, MemOperand(x27)); |
| __ Casalb(w8, w0, MemOperand(x28)); |
| |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0x00000067, x1); |
| ASSERT_EQUAL_64(0x00000067, x2); |
| ASSERT_EQUAL_64(0x00000067, x3); |
| ASSERT_EQUAL_64(0x00000067, x4); |
| ASSERT_EQUAL_64(0x00000067, x5); |
| ASSERT_EQUAL_64(0x00000067, x6); |
| ASSERT_EQUAL_64(0x00000067, x7); |
| ASSERT_EQUAL_64(0x00000067, x8); |
| |
| ASSERT_EQUAL_64(0x01234567, data1[0]); |
| ASSERT_EQUAL_64(0x012345ff, data2[0]); |
| ASSERT_EQUAL_64(0x01234567, data3[0]); |
| ASSERT_EQUAL_64(0x012345ff, data4[0]); |
| ASSERT_EQUAL_64(0x01234567, data5[0]); |
| ASSERT_EQUAL_64(0x012345ff, data6[0]); |
| ASSERT_EQUAL_64(0x01234567, data7[0]); |
| ASSERT_EQUAL_64(0x012345ff, data8[0]); |
| } |
| } |
| |
| TEST(cash_casah_caslh_casalh) { |
| uint64_t data1[] = {0x01234567, 0}; |
| uint64_t data2[] = {0x01234567, 0}; |
| uint64_t data3[] = {0x01234567, 0}; |
| uint64_t data4[] = {0x01234567, 0}; |
| uint64_t data5[] = {0x01234567, 0}; |
| uint64_t data6[] = {0x01234567, 0}; |
| uint64_t data7[] = {0x01234567, 0}; |
| uint64_t data8[] = {0x01234567, 0}; |
| |
| uint64_t* data1_aligned = AlignUp(data1, kXRegSizeInBytes * 2); |
| uint64_t* data2_aligned = AlignUp(data2, kXRegSizeInBytes * 2); |
| uint64_t* data3_aligned = AlignUp(data3, kXRegSizeInBytes * 2); |
| uint64_t* data4_aligned = AlignUp(data4, kXRegSizeInBytes * 2); |
| uint64_t* data5_aligned = AlignUp(data5, kXRegSizeInBytes * 2); |
| uint64_t* data6_aligned = AlignUp(data6, kXRegSizeInBytes * 2); |
| uint64_t* data7_aligned = AlignUp(data7, kXRegSizeInBytes * 2); |
| uint64_t* data8_aligned = AlignUp(data8, kXRegSizeInBytes * 2); |
| |
| SETUP_WITH_FEATURES(CPUFeatures::kAtomics); |
| |
| START(); |
| |
| __ Mov(x21, reinterpret_cast<uintptr_t>(data1_aligned)); |
| __ Mov(x22, reinterpret_cast<uintptr_t>(data2_aligned)); |
| __ Mov(x23, reinterpret_cast<uintptr_t>(data3_aligned)); |
| __ Mov(x24, reinterpret_cast<uintptr_t>(data4_aligned)); |
| __ Mov(x25, reinterpret_cast<uintptr_t>(data5_aligned)); |
| __ Mov(x26, reinterpret_cast<uintptr_t>(data6_aligned)); |
| __ Mov(x27, reinterpret_cast<uintptr_t>(data7_aligned)); |
| __ Mov(x28, reinterpret_cast<uintptr_t>(data8_aligned)); |
| |
| __ Mov(x0, 0xffffffff); |
| |
| __ Mov(x1, 0x76543210); |
| __ Mov(x2, 0x01234567); |
| __ Mov(x3, 0x76543210); |
| __ Mov(x4, 0x01234567); |
| __ Mov(x5, 0x76543210); |
| __ Mov(x6, 0x01234567); |
| __ Mov(x7, 0x76543210); |
| __ Mov(x8, 0x01234567); |
| |
| __ Cash(w1, w0, MemOperand(x21)); |
| __ Cash(w2, w0, MemOperand(x22)); |
| __ Casah(w3, w0, MemOperand(x23)); |
| __ Casah(w4, w0, MemOperand(x24)); |
| __ Caslh(w5, w0, MemOperand(x25)); |
| __ Caslh(w6, w0, MemOperand(x26)); |
| __ Casalh(w7, w0, MemOperand(x27)); |
| __ Casalh(w8, w0, MemOperand(x28)); |
| |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0x00004567, x1); |
| ASSERT_EQUAL_64(0x00004567, x2); |
| ASSERT_EQUAL_64(0x00004567, x3); |
| ASSERT_EQUAL_64(0x00004567, x4); |
| ASSERT_EQUAL_64(0x00004567, x5); |
| ASSERT_EQUAL_64(0x00004567, x6); |
| ASSERT_EQUAL_64(0x00004567, x7); |
| ASSERT_EQUAL_64(0x00004567, x8); |
| |
| ASSERT_EQUAL_64(0x01234567, data1[0]); |
| ASSERT_EQUAL_64(0x0123ffff, data2[0]); |
| ASSERT_EQUAL_64(0x01234567, data3[0]); |
| ASSERT_EQUAL_64(0x0123ffff, data4[0]); |
| ASSERT_EQUAL_64(0x01234567, data5[0]); |
| ASSERT_EQUAL_64(0x0123ffff, data6[0]); |
| ASSERT_EQUAL_64(0x01234567, data7[0]); |
| ASSERT_EQUAL_64(0x0123ffff, data8[0]); |
| } |
| } |
| |
| TEST(casp_caspa_caspl_caspal) { |
| uint64_t data1[] = {0x89abcdef01234567, 0}; |
| uint64_t data2[] = {0x89abcdef01234567, 0}; |
| uint64_t data3[] = {0x89abcdef01234567, 0}; |
| uint64_t data4[] = {0x89abcdef01234567, 0}; |
| uint64_t data5[] = {0x89abcdef01234567, 0}; |
| uint64_t data6[] = {0x89abcdef01234567, 0}; |
| uint64_t data7[] = {0x89abcdef01234567, 0}; |
| uint64_t data8[] = {0x89abcdef01234567, 0}; |
| |
| uint64_t* data1_aligned = AlignUp(data1, kXRegSizeInBytes * 2); |
| uint64_t* data2_aligned = AlignUp(data2, kXRegSizeInBytes * 2); |
| uint64_t* data3_aligned = AlignUp(data3, kXRegSizeInBytes * 2); |
| uint64_t* data4_aligned = AlignUp(data4, kXRegSizeInBytes * 2); |
| uint64_t* data5_aligned = AlignUp(data5, kXRegSizeInBytes * 2); |
| uint64_t* data6_aligned = AlignUp(data6, kXRegSizeInBytes * 2); |
| uint64_t* data7_aligned = AlignUp(data7, kXRegSizeInBytes * 2); |
| uint64_t* data8_aligned = AlignUp(data8, kXRegSizeInBytes * 2); |
| |
| SETUP_WITH_FEATURES(CPUFeatures::kAtomics); |
| |
| START(); |
| |
| __ Mov(x21, reinterpret_cast<uintptr_t>(data1_aligned)); |
| __ Mov(x22, reinterpret_cast<uintptr_t>(data2_aligned)); |
| __ Mov(x23, reinterpret_cast<uintptr_t>(data3_aligned)); |
| __ Mov(x24, reinterpret_cast<uintptr_t>(data4_aligned)); |
| __ Mov(x25, reinterpret_cast<uintptr_t>(data5_aligned)); |
| __ Mov(x26, reinterpret_cast<uintptr_t>(data6_aligned)); |
| __ Mov(x27, reinterpret_cast<uintptr_t>(data7_aligned)); |
| __ Mov(x28, reinterpret_cast<uintptr_t>(data8_aligned)); |
| |
| __ Mov(x0, 0xffffffff); |
| __ Mov(x1, 0xffffffff); |
| |
| __ Mov(x2, 0x76543210); |
| __ Mov(x3, 0xfedcba98); |
| __ Mov(x4, 0x89abcdef); |
| __ Mov(x5, 0x01234567); |
| |
| __ Mov(x6, 0x76543210); |
| __ Mov(x7, 0xfedcba98); |
| __ Mov(x8, 0x89abcdef); |
| __ Mov(x9, 0x01234567); |
| |
| __ Mov(x10, 0x76543210); |
| __ Mov(x11, 0xfedcba98); |
| __ Mov(x12, 0x89abcdef); |
| __ Mov(x13, 0x01234567); |
| |
| __ Mov(x14, 0x76543210); |
| __ Mov(x15, 0xfedcba98); |
| __ Mov(x16, 0x89abcdef); |
| __ Mov(x17, 0x01234567); |
| |
| __ Casp(w2, w3, w0, w1, MemOperand(x21)); |
| __ Casp(w4, w5, w0, w1, MemOperand(x22)); |
| __ Caspa(w6, w7, w0, w1, MemOperand(x23)); |
| __ Caspa(w8, w9, w0, w1, MemOperand(x24)); |
| __ Caspl(w10, w11, w0, w1, MemOperand(x25)); |
| __ Caspl(w12, w13, w0, w1, MemOperand(x26)); |
| __ Caspal(w14, w15, w0, w1, MemOperand(x27)); |
| __ Caspal(w16, w17, w0, w1, MemOperand(x28)); |
| |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0x89abcdef, x2); |
| ASSERT_EQUAL_64(0x01234567, x3); |
| ASSERT_EQUAL_64(0x89abcdef, x4); |
| ASSERT_EQUAL_64(0x01234567, x5); |
| ASSERT_EQUAL_64(0x89abcdef, x6); |
| ASSERT_EQUAL_64(0x01234567, x7); |
| ASSERT_EQUAL_64(0x89abcdef, x8); |
| ASSERT_EQUAL_64(0x01234567, x9); |
| ASSERT_EQUAL_64(0x89abcdef, x10); |
| ASSERT_EQUAL_64(0x01234567, x11); |
| ASSERT_EQUAL_64(0x89abcdef, x12); |
| ASSERT_EQUAL_64(0x01234567, x13); |
| ASSERT_EQUAL_64(0x89abcdef, x14); |
| ASSERT_EQUAL_64(0x01234567, x15); |
| ASSERT_EQUAL_64(0x89abcdef, x16); |
| ASSERT_EQUAL_64(0x01234567, x17); |
| |
| ASSERT_EQUAL_64(0x89abcdef01234567, data1[0]); |
| ASSERT_EQUAL_64(0xffffffffffffffff, data2[0]); |
| ASSERT_EQUAL_64(0x89abcdef01234567, data3[0]); |
| ASSERT_EQUAL_64(0xffffffffffffffff, data4[0]); |
| ASSERT_EQUAL_64(0x89abcdef01234567, data5[0]); |
| ASSERT_EQUAL_64(0xffffffffffffffff, data6[0]); |
| ASSERT_EQUAL_64(0x89abcdef01234567, data7[0]); |
| ASSERT_EQUAL_64(0xffffffffffffffff, data8[0]); |
| } |
| } |
| |
| |
| typedef void (MacroAssembler::*AtomicMemoryLoadSignature)( |
| const Register& rs, const Register& rt, const MemOperand& src); |
| typedef void (MacroAssembler::*AtomicMemoryStoreSignature)( |
| const Register& rs, const MemOperand& src); |
| |
| void AtomicMemoryWHelper(AtomicMemoryLoadSignature* load_funcs, |
| AtomicMemoryStoreSignature* store_funcs, |
| uint64_t arg1, |
| uint64_t arg2, |
| uint64_t expected, |
| uint64_t result_mask) { |
| uint64_t data0[] __attribute__((aligned(kXRegSizeInBytes * 2))) = {arg2, 0}; |
| uint64_t data1[] __attribute__((aligned(kXRegSizeInBytes * 2))) = {arg2, 0}; |
| uint64_t data2[] __attribute__((aligned(kXRegSizeInBytes * 2))) = {arg2, 0}; |
| uint64_t data3[] __attribute__((aligned(kXRegSizeInBytes * 2))) = {arg2, 0}; |
| uint64_t data4[] __attribute__((aligned(kXRegSizeInBytes * 2))) = {arg2, 0}; |
| uint64_t data5[] __attribute__((aligned(kXRegSizeInBytes * 2))) = {arg2, 0}; |
| |
| SETUP_WITH_FEATURES(CPUFeatures::kAtomics); |
| START(); |
| |
| __ Mov(x20, reinterpret_cast<uintptr_t>(data0)); |
| __ Mov(x21, reinterpret_cast<uintptr_t>(data1)); |
| __ Mov(x22, reinterpret_cast<uintptr_t>(data2)); |
| __ Mov(x23, reinterpret_cast<uintptr_t>(data3)); |
| |
| __ Mov(x0, arg1); |
| __ Mov(x1, arg1); |
| __ Mov(x2, arg1); |
| __ Mov(x3, arg1); |
| |
| (masm.*(load_funcs[0]))(w0, w10, MemOperand(x20)); |
| (masm.*(load_funcs[1]))(w1, w11, MemOperand(x21)); |
| (masm.*(load_funcs[2]))(w2, w12, MemOperand(x22)); |
| (masm.*(load_funcs[3]))(w3, w13, MemOperand(x23)); |
| |
| if (store_funcs != NULL) { |
| __ Mov(x24, reinterpret_cast<uintptr_t>(data4)); |
| __ Mov(x25, reinterpret_cast<uintptr_t>(data5)); |
| __ Mov(x4, arg1); |
| __ Mov(x5, arg1); |
| |
| (masm.*(store_funcs[0]))(w4, MemOperand(x24)); |
| (masm.*(store_funcs[1]))(w5, MemOperand(x25)); |
| } |
| |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| uint64_t stored_value = arg2 & result_mask; |
| ASSERT_EQUAL_64(stored_value, x10); |
| ASSERT_EQUAL_64(stored_value, x11); |
| ASSERT_EQUAL_64(stored_value, x12); |
| ASSERT_EQUAL_64(stored_value, x13); |
| |
| // The data fields contain arg2 already then only the bits masked by |
| // result_mask are overwritten. |
| uint64_t final_expected = (arg2 & ~result_mask) | (expected & result_mask); |
| ASSERT_EQUAL_64(final_expected, data0[0]); |
| ASSERT_EQUAL_64(final_expected, data1[0]); |
| ASSERT_EQUAL_64(final_expected, data2[0]); |
| ASSERT_EQUAL_64(final_expected, data3[0]); |
| |
| if (store_funcs != NULL) { |
| ASSERT_EQUAL_64(final_expected, data4[0]); |
| ASSERT_EQUAL_64(final_expected, data5[0]); |
| } |
| } |
| } |
| |
| void AtomicMemoryXHelper(AtomicMemoryLoadSignature* load_funcs, |
| AtomicMemoryStoreSignature* store_funcs, |
| uint64_t arg1, |
| uint64_t arg2, |
| uint64_t expected) { |
| uint64_t data0[] __attribute__((aligned(kXRegSizeInBytes * 2))) = {arg2, 0}; |
| uint64_t data1[] __attribute__((aligned(kXRegSizeInBytes * 2))) = {arg2, 0}; |
| uint64_t data2[] __attribute__((aligned(kXRegSizeInBytes * 2))) = {arg2, 0}; |
| uint64_t data3[] __attribute__((aligned(kXRegSizeInBytes * 2))) = {arg2, 0}; |
| uint64_t data4[] __attribute__((aligned(kXRegSizeInBytes * 2))) = {arg2, 0}; |
| uint64_t data5[] __attribute__((aligned(kXRegSizeInBytes * 2))) = {arg2, 0}; |
| |
| SETUP_WITH_FEATURES(CPUFeatures::kAtomics); |
| START(); |
| |
| __ Mov(x20, reinterpret_cast<uintptr_t>(data0)); |
| __ Mov(x21, reinterpret_cast<uintptr_t>(data1)); |
| __ Mov(x22, reinterpret_cast<uintptr_t>(data2)); |
| __ Mov(x23, reinterpret_cast<uintptr_t>(data3)); |
| |
| __ Mov(x0, arg1); |
| __ Mov(x1, arg1); |
| __ Mov(x2, arg1); |
| __ Mov(x3, arg1); |
| |
| (masm.*(load_funcs[0]))(x0, x10, MemOperand(x20)); |
| (masm.*(load_funcs[1]))(x1, x11, MemOperand(x21)); |
| (masm.*(load_funcs[2]))(x2, x12, MemOperand(x22)); |
| (masm.*(load_funcs[3]))(x3, x13, MemOperand(x23)); |
| |
| if (store_funcs != NULL) { |
| __ Mov(x24, reinterpret_cast<uintptr_t>(data4)); |
| __ Mov(x25, reinterpret_cast<uintptr_t>(data5)); |
| __ Mov(x4, arg1); |
| __ Mov(x5, arg1); |
| |
| (masm.*(store_funcs[0]))(x4, MemOperand(x24)); |
| (masm.*(store_funcs[1]))(x5, MemOperand(x25)); |
| } |
| |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(arg2, x10); |
| ASSERT_EQUAL_64(arg2, x11); |
| ASSERT_EQUAL_64(arg2, x12); |
| ASSERT_EQUAL_64(arg2, x13); |
| |
| ASSERT_EQUAL_64(expected, data0[0]); |
| ASSERT_EQUAL_64(expected, data1[0]); |
| ASSERT_EQUAL_64(expected, data2[0]); |
| ASSERT_EQUAL_64(expected, data3[0]); |
| |
| if (store_funcs != NULL) { |
| ASSERT_EQUAL_64(expected, data4[0]); |
| ASSERT_EQUAL_64(expected, data5[0]); |
| } |
| } |
| } |
| |
| // clang-format off |
| #define MAKE_LOADS(NAME) \ |
| {&MacroAssembler::Ld##NAME, \ |
| &MacroAssembler::Ld##NAME##a, \ |
| &MacroAssembler::Ld##NAME##l, \ |
| &MacroAssembler::Ld##NAME##al} |
| #define MAKE_STORES(NAME) \ |
| {&MacroAssembler::St##NAME, &MacroAssembler::St##NAME##l} |
| |
| #define MAKE_B_LOADS(NAME) \ |
| {&MacroAssembler::Ld##NAME##b, \ |
| &MacroAssembler::Ld##NAME##ab, \ |
| &MacroAssembler::Ld##NAME##lb, \ |
| &MacroAssembler::Ld##NAME##alb} |
| #define MAKE_B_STORES(NAME) \ |
| {&MacroAssembler::St##NAME##b, &MacroAssembler::St##NAME##lb} |
| |
| #define MAKE_H_LOADS(NAME) \ |
| {&MacroAssembler::Ld##NAME##h, \ |
| &MacroAssembler::Ld##NAME##ah, \ |
| &MacroAssembler::Ld##NAME##lh, \ |
| &MacroAssembler::Ld##NAME##alh} |
| #define MAKE_H_STORES(NAME) \ |
| {&MacroAssembler::St##NAME##h, &MacroAssembler::St##NAME##lh} |
| // clang-format on |
| |
| TEST(atomic_memory_add) { |
| AtomicMemoryLoadSignature loads[] = MAKE_LOADS(add); |
| AtomicMemoryStoreSignature stores[] = MAKE_STORES(add); |
| AtomicMemoryLoadSignature b_loads[] = MAKE_B_LOADS(add); |
| AtomicMemoryStoreSignature b_stores[] = MAKE_B_STORES(add); |
| AtomicMemoryLoadSignature h_loads[] = MAKE_H_LOADS(add); |
| AtomicMemoryStoreSignature h_stores[] = MAKE_H_STORES(add); |
| |
| // The arguments are chosen to have two useful properties: |
| // * When multiplied by small values (such as a register index), this value |
| // is clearly readable in the result. |
| // * The value is not formed from repeating fixed-size smaller values, so it |
| // can be used to detect endianness-related errors. |
| uint64_t arg1 = 0x0100001000100101; |
| uint64_t arg2 = 0x0200002000200202; |
| uint64_t expected = arg1 + arg2; |
| |
| AtomicMemoryWHelper(b_loads, b_stores, arg1, arg2, expected, kByteMask); |
| AtomicMemoryWHelper(h_loads, h_stores, arg1, arg2, expected, kHalfWordMask); |
| AtomicMemoryWHelper(loads, stores, arg1, arg2, expected, kWordMask); |
| AtomicMemoryXHelper(loads, stores, arg1, arg2, expected); |
| } |
| |
| TEST(atomic_memory_clr) { |
| AtomicMemoryLoadSignature loads[] = MAKE_LOADS(clr); |
| AtomicMemoryStoreSignature stores[] = MAKE_STORES(clr); |
| AtomicMemoryLoadSignature b_loads[] = MAKE_B_LOADS(clr); |
| AtomicMemoryStoreSignature b_stores[] = MAKE_B_STORES(clr); |
| AtomicMemoryLoadSignature h_loads[] = MAKE_H_LOADS(clr); |
| AtomicMemoryStoreSignature h_stores[] = MAKE_H_STORES(clr); |
| |
| uint64_t arg1 = 0x0300003000300303; |
| uint64_t arg2 = 0x0500005000500505; |
| uint64_t expected = arg2 & ~arg1; |
| |
| AtomicMemoryWHelper(b_loads, b_stores, arg1, arg2, expected, kByteMask); |
| AtomicMemoryWHelper(h_loads, h_stores, arg1, arg2, expected, kHalfWordMask); |
| AtomicMemoryWHelper(loads, stores, arg1, arg2, expected, kWordMask); |
| AtomicMemoryXHelper(loads, stores, arg1, arg2, expected); |
| } |
| |
| TEST(atomic_memory_eor) { |
| AtomicMemoryLoadSignature loads[] = MAKE_LOADS(eor); |
| AtomicMemoryStoreSignature stores[] = MAKE_STORES(eor); |
| AtomicMemoryLoadSignature b_loads[] = MAKE_B_LOADS(eor); |
| AtomicMemoryStoreSignature b_stores[] = MAKE_B_STORES(eor); |
| AtomicMemoryLoadSignature h_loads[] = MAKE_H_LOADS(eor); |
| AtomicMemoryStoreSignature h_stores[] = MAKE_H_STORES(eor); |
| |
| uint64_t arg1 = 0x0300003000300303; |
| uint64_t arg2 = 0x0500005000500505; |
| uint64_t expected = arg1 ^ arg2; |
| |
| AtomicMemoryWHelper(b_loads, b_stores, arg1, arg2, expected, kByteMask); |
| AtomicMemoryWHelper(h_loads, h_stores, arg1, arg2, expected, kHalfWordMask); |
| AtomicMemoryWHelper(loads, stores, arg1, arg2, expected, kWordMask); |
| AtomicMemoryXHelper(loads, stores, arg1, arg2, expected); |
| } |
| |
| TEST(atomic_memory_set) { |
| AtomicMemoryLoadSignature loads[] = MAKE_LOADS(set); |
| AtomicMemoryStoreSignature stores[] = MAKE_STORES(set); |
| AtomicMemoryLoadSignature b_loads[] = MAKE_B_LOADS(set); |
| AtomicMemoryStoreSignature b_stores[] = MAKE_B_STORES(set); |
| AtomicMemoryLoadSignature h_loads[] = MAKE_H_LOADS(set); |
| AtomicMemoryStoreSignature h_stores[] = MAKE_H_STORES(set); |
| |
| uint64_t arg1 = 0x0300003000300303; |
| uint64_t arg2 = 0x0500005000500505; |
| uint64_t expected = arg1 | arg2; |
| |
| AtomicMemoryWHelper(b_loads, b_stores, arg1, arg2, expected, kByteMask); |
| AtomicMemoryWHelper(h_loads, h_stores, arg1, arg2, expected, kHalfWordMask); |
| AtomicMemoryWHelper(loads, stores, arg1, arg2, expected, kWordMask); |
| AtomicMemoryXHelper(loads, stores, arg1, arg2, expected); |
| } |
| |
| TEST(atomic_memory_smax) { |
| AtomicMemoryLoadSignature loads[] = MAKE_LOADS(smax); |
| AtomicMemoryStoreSignature stores[] = MAKE_STORES(smax); |
| AtomicMemoryLoadSignature b_loads[] = MAKE_B_LOADS(smax); |
| AtomicMemoryStoreSignature b_stores[] = MAKE_B_STORES(smax); |
| AtomicMemoryLoadSignature h_loads[] = MAKE_H_LOADS(smax); |
| AtomicMemoryStoreSignature h_stores[] = MAKE_H_STORES(smax); |
| |
| uint64_t arg1 = 0x8100000080108181; |
| uint64_t arg2 = 0x0100001000100101; |
| uint64_t expected = 0x0100001000100101; |
| |
| AtomicMemoryWHelper(b_loads, b_stores, arg1, arg2, expected, kByteMask); |
| AtomicMemoryWHelper(h_loads, h_stores, arg1, arg2, expected, kHalfWordMask); |
| AtomicMemoryWHelper(loads, stores, arg1, arg2, expected, kWordMask); |
| AtomicMemoryXHelper(loads, stores, arg1, arg2, expected); |
| } |
| |
| TEST(atomic_memory_smin) { |
| AtomicMemoryLoadSignature loads[] = MAKE_LOADS(smin); |
| AtomicMemoryStoreSignature stores[] = MAKE_STORES(smin); |
| AtomicMemoryLoadSignature b_loads[] = MAKE_B_LOADS(smin); |
| AtomicMemoryStoreSignature b_stores[] = MAKE_B_STORES(smin); |
| AtomicMemoryLoadSignature h_loads[] = MAKE_H_LOADS(smin); |
| AtomicMemoryStoreSignature h_stores[] = MAKE_H_STORES(smin); |
| |
| uint64_t arg1 = 0x8100000080108181; |
| uint64_t arg2 = 0x0100001000100101; |
| uint64_t expected = 0x8100000080108181; |
| |
| AtomicMemoryWHelper(b_loads, b_stores, arg1, arg2, expected, kByteMask); |
| AtomicMemoryWHelper(h_loads, h_stores, arg1, arg2, expected, kHalfWordMask); |
| AtomicMemoryWHelper(loads, stores, arg1, arg2, expected, kWordMask); |
| AtomicMemoryXHelper(loads, stores, arg1, arg2, expected); |
| } |
| |
| TEST(atomic_memory_umax) { |
| AtomicMemoryLoadSignature loads[] = MAKE_LOADS(umax); |
| AtomicMemoryStoreSignature stores[] = MAKE_STORES(umax); |
| AtomicMemoryLoadSignature b_loads[] = MAKE_B_LOADS(umax); |
| AtomicMemoryStoreSignature b_stores[] = MAKE_B_STORES(umax); |
| AtomicMemoryLoadSignature h_loads[] = MAKE_H_LOADS(umax); |
| AtomicMemoryStoreSignature h_stores[] = MAKE_H_STORES(umax); |
| |
| uint64_t arg1 = 0x8100000080108181; |
| uint64_t arg2 = 0x0100001000100101; |
| uint64_t expected = 0x8100000080108181; |
| |
| AtomicMemoryWHelper(b_loads, b_stores, arg1, arg2, expected, kByteMask); |
| AtomicMemoryWHelper(h_loads, h_stores, arg1, arg2, expected, kHalfWordMask); |
| AtomicMemoryWHelper(loads, stores, arg1, arg2, expected, kWordMask); |
| AtomicMemoryXHelper(loads, stores, arg1, arg2, expected); |
| } |
| |
| TEST(atomic_memory_umin) { |
| AtomicMemoryLoadSignature loads[] = MAKE_LOADS(umin); |
| AtomicMemoryStoreSignature stores[] = MAKE_STORES(umin); |
| AtomicMemoryLoadSignature b_loads[] = MAKE_B_LOADS(umin); |
| AtomicMemoryStoreSignature b_stores[] = MAKE_B_STORES(umin); |
| AtomicMemoryLoadSignature h_loads[] = MAKE_H_LOADS(umin); |
| AtomicMemoryStoreSignature h_stores[] = MAKE_H_STORES(umin); |
| |
| uint64_t arg1 = 0x8100000080108181; |
| uint64_t arg2 = 0x0100001000100101; |
| uint64_t expected = 0x0100001000100101; |
| |
| AtomicMemoryWHelper(b_loads, b_stores, arg1, arg2, expected, kByteMask); |
| AtomicMemoryWHelper(h_loads, h_stores, arg1, arg2, expected, kHalfWordMask); |
| AtomicMemoryWHelper(loads, stores, arg1, arg2, expected, kWordMask); |
| AtomicMemoryXHelper(loads, stores, arg1, arg2, expected); |
| } |
| |
| TEST(atomic_memory_swp) { |
| AtomicMemoryLoadSignature loads[] = {&MacroAssembler::Swp, |
| &MacroAssembler::Swpa, |
| &MacroAssembler::Swpl, |
| &MacroAssembler::Swpal}; |
| AtomicMemoryLoadSignature b_loads[] = {&MacroAssembler::Swpb, |
| &MacroAssembler::Swpab, |
| &MacroAssembler::Swplb, |
| &MacroAssembler::Swpalb}; |
| AtomicMemoryLoadSignature h_loads[] = {&MacroAssembler::Swph, |
| &MacroAssembler::Swpah, |
| &MacroAssembler::Swplh, |
| &MacroAssembler::Swpalh}; |
| |
| uint64_t arg1 = 0x0100001000100101; |
| uint64_t arg2 = 0x0200002000200202; |
| uint64_t expected = 0x0100001000100101; |
| |
| // SWP functions have equivalent signatures to the Atomic Memory LD functions |
| // so we can use the same helper but without the ST aliases. |
| AtomicMemoryWHelper(b_loads, NULL, arg1, arg2, expected, kByteMask); |
| AtomicMemoryWHelper(h_loads, NULL, arg1, arg2, expected, kHalfWordMask); |
| AtomicMemoryWHelper(loads, NULL, arg1, arg2, expected, kWordMask); |
| AtomicMemoryXHelper(loads, NULL, arg1, arg2, expected); |
| } |
| |
| |
| TEST(ldaprb_ldaprh_ldapr) { |
| uint64_t data0[] = {0x1010101010101010, 0}; |
| uint64_t data1[] = {0x1010101010101010, 0}; |
| uint64_t data2[] = {0x1010101010101010, 0}; |
| uint64_t data3[] = {0x1010101010101010, 0}; |
| |
| uint64_t* data0_aligned = AlignUp(data0, kXRegSizeInBytes * 2); |
| uint64_t* data1_aligned = AlignUp(data1, kXRegSizeInBytes * 2); |
| uint64_t* data2_aligned = AlignUp(data2, kXRegSizeInBytes * 2); |
| uint64_t* data3_aligned = AlignUp(data3, kXRegSizeInBytes * 2); |
| |
| SETUP_WITH_FEATURES(CPUFeatures::kRCpc); |
| START(); |
| |
| __ Mov(x20, reinterpret_cast<uintptr_t>(data0_aligned)); |
| __ Mov(x21, reinterpret_cast<uintptr_t>(data1_aligned)); |
| __ Mov(x22, reinterpret_cast<uintptr_t>(data2_aligned)); |
| __ Mov(x23, reinterpret_cast<uintptr_t>(data3_aligned)); |
| |
| __ Ldaprb(w0, MemOperand(x20)); |
| __ Ldaprh(w1, MemOperand(x21)); |
| __ Ldapr(w2, MemOperand(x22)); |
| __ Ldapr(x3, MemOperand(x23)); |
| |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| ASSERT_EQUAL_64(0x10, x0); |
| ASSERT_EQUAL_64(0x1010, x1); |
| ASSERT_EQUAL_64(0x10101010, x2); |
| ASSERT_EQUAL_64(0x1010101010101010, x3); |
| } |
| } |
| |
| |
| TEST(ldapurb_ldapurh_ldapur) { |
| uint64_t data[] |
| __attribute__((aligned(kXRegSizeInBytes * 2))) = {0x0123456789abcdef, |
| 0xfedcba9876543210}; |
| |
| uintptr_t data_base = reinterpret_cast<uintptr_t>(data); |
| |
| SETUP_WITH_FEATURES(CPUFeatures::kRCpc, CPUFeatures::kRCpcImm); |
| START(); |
| |
| __ Mov(x20, data_base); |
| __ Mov(x21, data_base + 2 * sizeof(data[0])); |
| |
| __ Ldaprb(w0, MemOperand(x20)); |
| __ Ldaprh(w1, MemOperand(x20)); |
| __ Ldapr(w2, MemOperand(x20)); |
| __ Ldapr(x3, MemOperand(x20)); |
| __ Ldaprb(w4, MemOperand(x20, 12)); |
| __ Ldaprh(w5, MemOperand(x20, 8)); |
| __ Ldapr(w6, MemOperand(x20, 10)); |
| __ Ldapr(x7, MemOperand(x20, 7)); |
| __ Ldaprb(w8, MemOperand(x21, -1)); |
| __ Ldaprh(w9, MemOperand(x21, -3)); |
| __ Ldapr(w10, MemOperand(x21, -9)); |
| __ Ldapr(x11, MemOperand(x21, -12)); |
| |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| ASSERT_EQUAL_64(0xef, x0); |
| ASSERT_EQUAL_64(0xcdef, x1); |
| ASSERT_EQUAL_64(0x89abcdef, x2); |
| ASSERT_EQUAL_64(0x0123456789abcdef, x3); |
| ASSERT_EQUAL_64(0x98, x4); |
| ASSERT_EQUAL_64(0x3210, x5); |
| ASSERT_EQUAL_64(0xba987654, x6); |
| ASSERT_EQUAL_64(0xdcba987654321001, x7); |
| ASSERT_EQUAL_64(0xfe, x8); |
| ASSERT_EQUAL_64(0xdcba, x9); |
| ASSERT_EQUAL_64(0x54321001, x10); |
| ASSERT_EQUAL_64(0x7654321001234567, x11); |
| } |
| } |
| |
| |
| TEST(ldapursb_ldapursh_ldapursw) { |
| uint64_t data[] |
| __attribute__((aligned(kXRegSizeInBytes * 2))) = {0x0123456789abcdef, |
| 0xfedcba9876543210}; |
| |
| uintptr_t data_base = reinterpret_cast<uintptr_t>(data); |
| |
| SETUP_WITH_FEATURES(CPUFeatures::kRCpc, CPUFeatures::kRCpcImm); |
| START(); |
| |
| __ Mov(x20, data_base); |
| __ Mov(x21, data_base + 2 * sizeof(data[0])); |
| |
| __ Ldapursb(w0, MemOperand(x20)); |
| __ Ldapursb(x1, MemOperand(x20)); |
| __ Ldapursh(w2, MemOperand(x20)); |
| __ Ldapursh(x3, MemOperand(x20)); |
| __ Ldapursw(x4, MemOperand(x20)); |
| __ Ldapursb(w5, MemOperand(x20, 12)); |
| __ Ldapursb(x6, MemOperand(x20, 12)); |
| __ Ldapursh(w7, MemOperand(x20, 13)); |
| __ Ldapursh(x8, MemOperand(x20, 13)); |
| __ Ldapursw(x9, MemOperand(x20, 10)); |
| __ Ldapursb(w10, MemOperand(x21, -1)); |
| __ Ldapursb(x11, MemOperand(x21, -1)); |
| __ Ldapursh(w12, MemOperand(x21, -4)); |
| __ Ldapursh(x13, MemOperand(x21, -4)); |
| __ Ldapursw(x14, MemOperand(x21, -5)); |
| |
| __ Ldapursb(x15, MemOperand(x20, 8)); |
| __ Ldapursh(x16, MemOperand(x20, 8)); |
| __ Ldapursw(x17, MemOperand(x20, 8)); |
| |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| ASSERT_EQUAL_64(0xffffffef, x0); |
| ASSERT_EQUAL_64(0xffffffffffffffef, x1); |
| ASSERT_EQUAL_64(0xffffcdef, x2); |
| ASSERT_EQUAL_64(0xffffffffffffcdef, x3); |
| ASSERT_EQUAL_64(0xffffffff89abcdef, x4); |
| ASSERT_EQUAL_64(0xffffff98, x5); |
| ASSERT_EQUAL_64(0xffffffffffffff98, x6); |
| ASSERT_EQUAL_64(0xffffdcba, x7); |
| ASSERT_EQUAL_64(0xffffffffffffdcba, x8); |
| ASSERT_EQUAL_64(0xffffffffba987654, x9); |
| ASSERT_EQUAL_64(0xfffffffe, x10); |
| ASSERT_EQUAL_64(0xfffffffffffffffe, x11); |
| ASSERT_EQUAL_64(0xffffba98, x12); |
| ASSERT_EQUAL_64(0xffffffffffffba98, x13); |
| ASSERT_EQUAL_64(0xffffffffdcba9876, x14); |
| |
| ASSERT_EQUAL_64(0x0000000000000010, x15); |
| ASSERT_EQUAL_64(0x0000000000003210, x16); |
| ASSERT_EQUAL_64(0x0000000076543210, x17); |
| } |
| } |
| |
| |
| TEST(stlurb_stlurh_strlur) { |
| uint64_t data[] __attribute__((aligned(kXRegSizeInBytes * 2))) = {0x0, 0x0}; |
| |
| uintptr_t data_base = reinterpret_cast<uintptr_t>(data); |
| |
| SETUP_WITH_FEATURES(CPUFeatures::kRCpc, CPUFeatures::kRCpcImm); |
| START(); |
| |
| __ Mov(x0, 0x0011223344556677); |
| __ Mov(x20, data_base); |
| __ Mov(x21, data_base + 2 * sizeof(data[0])); |
| |
| __ Stlrb(w0, MemOperand(x20)); |
| __ Stlrh(w0, MemOperand(x20, 1)); |
| __ Stlr(w0, MemOperand(x20, 3)); |
| __ Stlr(x0, MemOperand(x21, -8)); |
| |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| ASSERT_EQUAL_64(0x0044556677667777, data[0]); |
| ASSERT_EQUAL_64(0x0011223344556677, data[1]); |
| } |
| } |
| |
| |
| #define SIMPLE_ATOMIC_OPS(V, DEF) \ |
| V(DEF, add) \ |
| V(DEF, clr) \ |
| V(DEF, eor) \ |
| V(DEF, set) \ |
| V(DEF, smax) \ |
| V(DEF, smin) \ |
| V(DEF, umax) \ |
| V(DEF, umin) |
| |
| #define SIMPLE_ATOMIC_STORE_MODES(V, NAME) \ |
| V(NAME) \ |
| V(NAME##l) |
| |
| #define SIMPLE_ATOMIC_LOAD_MODES(V, NAME) \ |
| SIMPLE_ATOMIC_STORE_MODES(V, NAME) \ |
| V(NAME##a) \ |
| V(NAME##al) |
| |
| |
| TEST(unaligned_single_copy_atomicity) { |
| uint64_t data0[] = {0x1010101010101010, 0x1010101010101010}; |
| uint64_t dst[] = {0x0000000000000000, 0x0000000000000000}; |
| |
| uint64_t* data0_aligned = AlignUp(data0, kAtomicAccessGranule); |
| uint64_t* dst_aligned = AlignUp(dst, kAtomicAccessGranule); |
| |
| CPUFeatures features(CPUFeatures::kAtomics, |
| CPUFeatures::kLORegions, |
| CPUFeatures::kRCpc, |
| CPUFeatures::kRCpcImm); |
| features.Combine(CPUFeatures::kUSCAT); |
| SETUP_WITH_FEATURES(features); |
| START(); |
| |
| __ Mov(x0, 0x0123456789abcdef); |
| __ Mov(x1, 0x456789abcdef0123); |
| __ Mov(x2, 0x89abcdef01234567); |
| __ Mov(x3, 0xcdef0123456789ab); |
| __ Mov(x18, reinterpret_cast<uintptr_t>(data0_aligned)); |
| __ Mov(x19, reinterpret_cast<uintptr_t>(dst_aligned)); |
| __ Mov(x20, x18); |
| __ Mov(x21, x19); |
| |
| for (unsigned i = 0; i < kAtomicAccessGranule; i++) { |
| __ Stxrb(w0, w1, MemOperand(x20)); |
| __ Stlxrb(w0, w1, MemOperand(x20)); |
| __ Ldxrb(w0, MemOperand(x20)); |
| __ Ldaxrb(w0, MemOperand(x20)); |
| __ Stllrb(w0, MemOperand(x20)); |
| __ Stlrb(w0, MemOperand(x20)); |
| __ Casb(w0, w1, MemOperand(x20)); |
| __ Caslb(w0, w1, MemOperand(x20)); |
| __ Ldlarb(w0, MemOperand(x20)); |
| __ Ldarb(w0, MemOperand(x20)); |
| __ Casab(w0, w1, MemOperand(x20)); |
| __ Casalb(w0, w1, MemOperand(x20)); |
| |
| __ Swpb(w0, w1, MemOperand(x20)); |
| __ Swplb(w0, w1, MemOperand(x20)); |
| __ Swpab(w0, w1, MemOperand(x20)); |
| __ Swpalb(w0, w1, MemOperand(x20)); |
| __ Ldaprb(w0, MemOperand(x20)); |
| // Use offset instead of Add to test Stlurb and Ldapurb. |
| __ Stlrb(w0, MemOperand(x19, i)); |
| __ Ldaprb(w0, MemOperand(x19, i)); |
| __ Ldapursb(w0, MemOperand(x20)); |
| __ Ldapursb(x0, MemOperand(x20)); |
| |
| #define ATOMIC_LOAD_B(NAME) __ Ld##NAME##b(w0, w1, MemOperand(x20)); |
| #define ATOMIC_STORE_B(NAME) __ St##NAME##b(w0, MemOperand(x20)); |
| SIMPLE_ATOMIC_OPS(SIMPLE_ATOMIC_LOAD_MODES, ATOMIC_LOAD_B) |
| SIMPLE_ATOMIC_OPS(SIMPLE_ATOMIC_STORE_MODES, ATOMIC_STORE_B) |
| #undef ATOMIC_LOAD_B |
| #undef ATOMIC_STORE_B |
| |
| if (i <= (kAtomicAccessGranule - kHRegSizeInBytes)) { |
| __ Stxrh(w0, w1, MemOperand(x20)); |
| __ Stlxrh(w0, w1, MemOperand(x20)); |
| __ Ldxrh(w0, MemOperand(x20)); |
| __ Ldaxrh(w0, MemOperand(x20)); |
| __ Stllrh(w0, MemOperand(x20)); |
| __ Stlrh(w0, MemOperand(x20)); |
| __ Cash(w0, w1, MemOperand(x20)); |
| __ Caslh(w0, w1, MemOperand(x20)); |
| __ Ldlarh(w0, MemOperand(x20)); |
| __ Ldarh(w0, MemOperand(x20)); |
| __ Casah(w0, w1, MemOperand(x20)); |
| __ Casalh(w0, w1, MemOperand(x20)); |
| |
| __ Swph(w0, w1, MemOperand(x20)); |
| __ Swplh(w0, w1, MemOperand(x20)); |
| __ Swpah(w0, w1, MemOperand(x20)); |
| __ Swpalh(w0, w1, MemOperand(x20)); |
| __ Ldaprh(w0, MemOperand(x20)); |
| // Use offset instead of Add to test Stlurh and Ldapurh. |
| __ Stlrh(w0, MemOperand(x19, i)); |
| __ Ldaprh(w0, MemOperand(x19, i)); |
| __ Ldapursh(w0, MemOperand(x20)); |
| __ Ldapursh(x0, MemOperand(x20)); |
| |
| #define ATOMIC_LOAD_H(NAME) __ Ld##NAME##h(w0, w1, MemOperand(x20)); |
| #define ATOMIC_STORE_H(NAME) __ St##NAME##h(w0, MemOperand(x20)); |
| SIMPLE_ATOMIC_OPS(SIMPLE_ATOMIC_LOAD_MODES, ATOMIC_LOAD_H) |
| SIMPLE_ATOMIC_OPS(SIMPLE_ATOMIC_STORE_MODES, ATOMIC_STORE_H) |
| #undef ATOMIC_LOAD_H |
| #undef ATOMIC_STORE_H |
| } |
| |
| if (i <= (kAtomicAccessGranule - kWRegSizeInBytes)) { |
| __ Stxr(w0, w1, MemOperand(x20)); |
| __ Stlxr(w0, w1, MemOperand(x20)); |
| __ Ldxr(w0, MemOperand(x20)); |
| __ Ldaxr(w0, MemOperand(x20)); |
| __ Stllr(w0, MemOperand(x20)); |
| __ Stlr(w0, MemOperand(x20)); |
| __ Cas(w0, w1, MemOperand(x20)); |
| __ Casl(w0, w1, MemOperand(x20)); |
| __ Ldlar(w0, MemOperand(x20)); |
| __ Ldar(w0, MemOperand(x20)); |
| __ Casa(w0, w1, MemOperand(x20)); |
| __ Casal(w0, w1, MemOperand(x20)); |
| |
| __ Swp(w0, w1, MemOperand(x20)); |
| __ Swpl(w0, w1, MemOperand(x20)); |
| __ Swpa(w0, w1, MemOperand(x20)); |
| __ Swpal(w0, w1, MemOperand(x20)); |
| __ Ldapr(w0, MemOperand(x20)); |
| // Use offset instead of Add to test Stlur and Ldapur. |
| __ Stlr(w0, MemOperand(x19, i)); |
| __ Ldapr(w0, MemOperand(x19, i)); |
| __ Ldapursw(x0, MemOperand(x20)); |
| |
| #define ATOMIC_LOAD_W(NAME) __ Ld##NAME(w0, w1, MemOperand(x20)); |
| #define ATOMIC_STORE_W(NAME) __ St##NAME(w0, MemOperand(x20)); |
| SIMPLE_ATOMIC_OPS(SIMPLE_ATOMIC_LOAD_MODES, ATOMIC_LOAD_W) |
| SIMPLE_ATOMIC_OPS(SIMPLE_ATOMIC_STORE_MODES, ATOMIC_STORE_W) |
| #undef ATOMIC_LOAD_W |
| #undef ATOMIC_STORE_W |
| } |
| |
| if (i <= (kAtomicAccessGranule - (kWRegSizeInBytes * 2))) { |
| __ Casp(w0, w1, w2, w3, MemOperand(x20)); |
| __ Caspl(w0, w1, w2, w3, MemOperand(x20)); |
| __ Caspa(w0, w1, w2, w3, MemOperand(x20)); |
| __ Caspal(w0, w1, w2, w3, MemOperand(x20)); |
| __ Stxp(w0, w1, w2, MemOperand(x20)); |
| __ Stlxp(w0, w1, w2, MemOperand(x20)); |
| __ Ldxp(w0, w1, MemOperand(x20)); |
| __ Ldaxp(w0, w1, MemOperand(x20)); |
| } |
| |
| if (i <= (kAtomicAccessGranule - kXRegSizeInBytes)) { |
| __ Stxr(x0, x1, MemOperand(x20)); |
| __ Stlxr(x0, x1, MemOperand(x20)); |
| __ Ldxr(x0, MemOperand(x20)); |
| __ Ldaxr(x0, MemOperand(x20)); |
| __ Stllr(x0, MemOperand(x20)); |
| __ Stlr(x0, MemOperand(x20)); |
| __ Cas(x0, x1, MemOperand(x20)); |
| __ Casl(x0, x1, MemOperand(x20)); |
| __ Ldlar(x0, MemOperand(x20)); |
| __ Ldar(x0, MemOperand(x20)); |
| __ Casa(x0, x1, MemOperand(x20)); |
| __ Casal(x0, x1, MemOperand(x20)); |
| |
| __ Swp(x0, x1, MemOperand(x20)); |
| __ Swpl(x0, x1, MemOperand(x20)); |
| __ Swpa(x0, x1, MemOperand(x20)); |
| __ Swpal(x0, x1, MemOperand(x20)); |
| __ Ldapr(x0, MemOperand(x20)); |
| // Use offset instead of Add to test Stlur and Ldapur. |
| __ Stlr(x0, MemOperand(x19, i)); |
| __ Ldapr(x0, MemOperand(x19, i)); |
| |
| #define ATOMIC_LOAD_X(NAME) __ Ld##NAME(x0, x1, MemOperand(x20)); |
| #define ATOMIC_STORE_X(NAME) __ St##NAME(x0, MemOperand(x20)); |
| SIMPLE_ATOMIC_OPS(SIMPLE_ATOMIC_LOAD_MODES, ATOMIC_LOAD_X) |
| SIMPLE_ATOMIC_OPS(SIMPLE_ATOMIC_STORE_MODES, ATOMIC_STORE_X) |
| #undef ATOMIC_LOAD_X |
| #undef ATOMIC_STORE_X |
| } |
| |
| if (i <= (kAtomicAccessGranule - (kXRegSizeInBytes * 2))) { |
| __ Casp(x0, x1, x2, x3, MemOperand(x20)); |
| __ Caspl(x0, x1, x2, x3, MemOperand(x20)); |
| __ Caspa(x0, x1, x2, x3, MemOperand(x20)); |
| __ Caspal(x0, x1, x2, x3, MemOperand(x20)); |
| __ Stxp(x0, x1, x2, MemOperand(x20)); |
| __ Stlxp(x0, x1, x2, MemOperand(x20)); |
| __ Ldxp(x0, x1, MemOperand(x20)); |
| __ Ldaxp(x0, x1, MemOperand(x20)); |
| } |
| |
| __ Add(x20, x20, 1); |
| __ Add(x21, x21, 1); |
| } |
| END(); |
| |
| if (CAN_RUN()) { |
| // We can't detect kUSCAT with the CPUFeaturesAuditor so it fails the seen |
| // check. |
| RUN_WITHOUT_SEEN_FEATURE_CHECK(); |
| } |
| } |
| |
| |
| #if defined(VIXL_NEGATIVE_TESTING) && defined(VIXL_INCLUDE_SIMULATOR_AARCH64) |
| |
| #define CHECK_ALIGN_FAIL(i, expr) \ |
| { \ |
| CPUFeatures features(CPUFeatures::kAtomics, \ |
| CPUFeatures::kLORegions, \ |
| CPUFeatures::kRCpc, \ |
| CPUFeatures::kRCpcImm); \ |
| features.Combine(CPUFeatures::kUSCAT); \ |
| SETUP_WITH_FEATURES(features); \ |
| START(); \ |
| __ Mov(x0, 0x0123456789abcdef); \ |
| __ Mov(x1, 0x456789abcdef0123); \ |
| __ Mov(x2, 0x89abcdef01234567); \ |
| __ Mov(x3, 0xcdef0123456789ab); \ |
| __ Mov(x20, reinterpret_cast<uintptr_t>(data0_aligned)); \ |
| __ Mov(x21, reinterpret_cast<uintptr_t>(dst_aligned)); \ |
| __ Add(x20, x20, i); \ |
| __ Add(x21, x21, i); \ |
| expr; \ |
| END(); \ |
| if (CAN_RUN()) { \ |
| /* We can't detect kUSCAT with the CPUFeaturesAuditor so it fails the */ \ |
| /* seen check. */ \ |
| MUST_FAIL_WITH_MESSAGE(RUN_WITHOUT_SEEN_FEATURE_CHECK(), \ |
| "ALIGNMENT EXCEPTION"); \ |
| } \ |
| } |
| |
| TEST(unaligned_single_copy_atomicity_negative_test) { |
| uint64_t data0[] = {0x1010101010101010, 0x1010101010101010}; |
| uint64_t dst[] = {0x0000000000000000, 0x0000000000000000}; |
| |
| uint64_t* data0_aligned = AlignUp(data0, kAtomicAccessGranule); |
| uint64_t* dst_aligned = AlignUp(dst, kAtomicAccessGranule); |
| |
| for (unsigned i = 0; i < kAtomicAccessGranule; i++) { |
| if (i > (kAtomicAccessGranule - kHRegSizeInBytes)) { |
| CHECK_ALIGN_FAIL(i, __ Stxrh(w0, w1, MemOperand(x20))); |
| CHECK_ALIGN_FAIL(i, __ Stlxrh(w0, w1, MemOperand(x20))); |
| CHECK_ALIGN_FAIL(i, __ Ldxrh(w0, MemOperand(x20))); |
| CHECK_ALIGN_FAIL(i, __ Ldaxrh(w0, MemOperand(x20))); |
| CHECK_ALIGN_FAIL(i, __ Stllrh(w0, MemOperand(x20))); |
| CHECK_ALIGN_FAIL(i, __ Stlrh(w0, MemOperand(x20))); |
| CHECK_ALIGN_FAIL(i, __ Cash(w0, w1, MemOperand(x20))); |
| CHECK_ALIGN_FAIL(i, __ Caslh(w0, w1, MemOperand(x20))); |
| CHECK_ALIGN_FAIL(i, __ Ldlarh(w0, MemOperand(x20))); |
| CHECK_ALIGN_FAIL(i, __ Ldarh(w0, MemOperand(x20))); |
| CHECK_ALIGN_FAIL(i, __ Casah(w0, w1, MemOperand(x20))); |
| CHECK_ALIGN_FAIL(i, __ Casalh(w0, w1, MemOperand(x20))); |
| |
| CHECK_ALIGN_FAIL(i, __ Swph(w0, w1, MemOperand(x20))); |
| CHECK_ALIGN_FAIL(i, __ Swplh(w0, w1, MemOperand(x20))); |
| CHECK_ALIGN_FAIL(i, __ Swpah(w0, w1, MemOperand(x20))); |
| CHECK_ALIGN_FAIL(i, __ Swpalh(w0, w1, MemOperand(x20))); |
| CHECK_ALIGN_FAIL(i, __ Ldaprh(w0, MemOperand(x20))); |
| // Use offset instead of Add to test Stlurh and Ldapurh. |
| CHECK_ALIGN_FAIL(0, __ Stlrh(w0, MemOperand(x20, i))); |
| CHECK_ALIGN_FAIL(0, __ Ldaprh(w0, MemOperand(x20, i))); |
| CHECK_ALIGN_FAIL(i, __ Ldapursh(w0, MemOperand(x20))); |
| CHECK_ALIGN_FAIL(i, __ Ldapursh(x0, MemOperand(x20))); |
| |
| #define ATOMIC_LOAD_H(NAME) \ |
| CHECK_ALIGN_FAIL(i, __ Ld##NAME##h(w0, w1, MemOperand(x20))); |
| #define ATOMIC_STORE_H(NAME) \ |
| CHECK_ALIGN_FAIL(i, __ St##NAME##h(w0, MemOperand(x20))); |
| SIMPLE_ATOMIC_OPS(SIMPLE_ATOMIC_LOAD_MODES, ATOMIC_LOAD_H) |
| SIMPLE_ATOMIC_OPS(SIMPLE_ATOMIC_STORE_MODES, ATOMIC_STORE_H) |
| #undef ATOMIC_LOAD_H |
| #undef ATOMIC_STORE_H |
| } |
| |
| if (i > (kAtomicAccessGranule - kWRegSizeInBytes)) { |
| CHECK_ALIGN_FAIL(i, __ Stxr(w0, w1, MemOperand(x20))); |
| CHECK_ALIGN_FAIL(i, __ Stlxr(w0, w1, MemOperand(x20))); |
| CHECK_ALIGN_FAIL(i, __ Ldxr(w0, MemOperand(x20))); |
| CHECK_ALIGN_FAIL(i, __ Ldaxr(w0, MemOperand(x20))); |
| CHECK_ALIGN_FAIL(i, __ Stllr(w0, MemOperand(x20))); |
| CHECK_ALIGN_FAIL(i, __ Stlr(w0, MemOperand(x20))); |
| CHECK_ALIGN_FAIL(i, __ Cas(w0, w1, MemOperand(x20))); |
| CHECK_ALIGN_FAIL(i, __ Casl(w0, w1, MemOperand(x20))); |
| CHECK_ALIGN_FAIL(i, __ Ldlar(w0, MemOperand(x20))); |
| CHECK_ALIGN_FAIL(i, __ Ldar(w0, MemOperand(x20))); |
| CHECK_ALIGN_FAIL(i, __ Casa(w0, w1, MemOperand(x20))); |
| CHECK_ALIGN_FAIL(i, __ Casal(w0, w1, MemOperand(x20))); |
| |
| CHECK_ALIGN_FAIL(i, __ Swp(w0, w1, MemOperand(x20))); |
| CHECK_ALIGN_FAIL(i, __ Swpl(w0, w1, MemOperand(x20))); |
| CHECK_ALIGN_FAIL(i, __ Swpa(w0, w1, MemOperand(x20))); |
| CHECK_ALIGN_FAIL(i, __ Swpal(w0, w1, MemOperand(x20))); |
| CHECK_ALIGN_FAIL(i, __ Ldapr(w0, MemOperand(x20))); |
| // Use offset instead of add to test Stlur and Ldapur. |
| CHECK_ALIGN_FAIL(0, __ Stlr(w0, MemOperand(x20, i))); |
| CHECK_ALIGN_FAIL(0, __ Ldapr(w0, MemOperand(x20, i))); |
| CHECK_ALIGN_FAIL(i, __ Ldapursw(x0, MemOperand(x20))); |
| |
| #define ATOMIC_LOAD_W(NAME) \ |
| CHECK_ALIGN_FAIL(i, __ Ld##NAME(w0, w1, MemOperand(x20))); |
| #define ATOMIC_STORE_W(NAME) \ |
| CHECK_ALIGN_FAIL(i, __ St##NAME(w0, MemOperand(x20))); |
| SIMPLE_ATOMIC_OPS(SIMPLE_ATOMIC_LOAD_MODES, ATOMIC_LOAD_W) |
| SIMPLE_ATOMIC_OPS(SIMPLE_ATOMIC_STORE_MODES, ATOMIC_STORE_W) |
| #undef ATOMIC_LOAD_W |
| #undef ATOMIC_STORE_W |
| } |
| |
| if (i > (kAtomicAccessGranule - (kWRegSizeInBytes * 2))) { |
| CHECK_ALIGN_FAIL(i, __ Casp(w0, w1, w2, w3, MemOperand(x20))); |
| CHECK_ALIGN_FAIL(i, __ Caspl(w0, w1, w2, w3, MemOperand(x20))); |
| CHECK_ALIGN_FAIL(i, __ Caspa(w0, w1, w2, w3, MemOperand(x20))); |
| CHECK_ALIGN_FAIL(i, __ Caspal(w0, w1, w2, w3, MemOperand(x20))); |
| CHECK_ALIGN_FAIL(i, __ Stxp(w0, w1, w2, MemOperand(x20))); |
| CHECK_ALIGN_FAIL(i, __ Stlxp(w0, w1, w2, MemOperand(x20))); |
| CHECK_ALIGN_FAIL(i, __ Ldxp(w0, w1, MemOperand(x20))); |
| CHECK_ALIGN_FAIL(i, __ Ldaxp(w0, w1, MemOperand(x20))); |
| } |
| |
| if (i > (kAtomicAccessGranule - kXRegSizeInBytes)) { |
| CHECK_ALIGN_FAIL(i, __ Stxr(x0, x1, MemOperand(x20))); |
| CHECK_ALIGN_FAIL(i, __ Stlxr(x0, x1, MemOperand(x20))); |
| CHECK_ALIGN_FAIL(i, __ Ldxr(x0, MemOperand(x20))); |
| CHECK_ALIGN_FAIL(i, __ Ldaxr(x0, MemOperand(x20))); |
| CHECK_ALIGN_FAIL(i, __ Stllr(x0, MemOperand(x20))); |
| CHECK_ALIGN_FAIL(i, __ Stlr(x0, MemOperand(x20))); |
| CHECK_ALIGN_FAIL(i, __ Cas(x0, x1, MemOperand(x20))); |
| CHECK_ALIGN_FAIL(i, __ Casl(x0, x1, MemOperand(x20))); |
| CHECK_ALIGN_FAIL(i, __ Ldlar(x0, MemOperand(x20))); |
| CHECK_ALIGN_FAIL(i, __ Ldar(x0, MemOperand(x20))); |
| CHECK_ALIGN_FAIL(i, __ Casa(x0, x1, MemOperand(x20))); |
| CHECK_ALIGN_FAIL(i, __ Casal(x0, x1, MemOperand(x20))); |
| |
| CHECK_ALIGN_FAIL(i, __ Swp(x0, x1, MemOperand(x20))); |
| CHECK_ALIGN_FAIL(i, __ Swpl(x0, x1, MemOperand(x20))); |
| CHECK_ALIGN_FAIL(i, __ Swpa(x0, x1, MemOperand(x20))); |
| CHECK_ALIGN_FAIL(i, __ Swpal(x0, x1, MemOperand(x20))); |
| CHECK_ALIGN_FAIL(i, __ Ldapr(x0, MemOperand(x20))); |
| // Use offset instead of add to test Stlur and Ldapur. |
| CHECK_ALIGN_FAIL(0, __ Stlr(x0, MemOperand(x20, i))); |
| CHECK_ALIGN_FAIL(0, __ Ldapr(x0, MemOperand(x20, i))); |
| |
| #define ATOMIC_LOAD_X(NAME) \ |
| CHECK_ALIGN_FAIL(i, __ Ld##NAME(x0, x1, MemOperand(x20))); |
| #define ATOMIC_STORE_X(NAME) \ |
| CHECK_ALIGN_FAIL(i, __ St##NAME(x0, MemOperand(x20))); |
| SIMPLE_ATOMIC_OPS(SIMPLE_ATOMIC_LOAD_MODES, ATOMIC_LOAD_X) |
| SIMPLE_ATOMIC_OPS(SIMPLE_ATOMIC_STORE_MODES, ATOMIC_STORE_X) |
| #undef ATOMIC_LOAD_X |
| #undef ATOMIC_STORE_X |
| } |
| |
| if (i > (kAtomicAccessGranule - (kXRegSizeInBytes * 2))) { |
| CHECK_ALIGN_FAIL(i, __ Casp(x0, x1, x2, x3, MemOperand(x20))); |
| CHECK_ALIGN_FAIL(i, __ Caspl(x0, x1, x2, x3, MemOperand(x20))); |
| CHECK_ALIGN_FAIL(i, __ Caspa(x0, x1, x2, x3, MemOperand(x20))); |
| CHECK_ALIGN_FAIL(i, __ Caspal(x0, x1, x2, x3, MemOperand(x20))); |
| CHECK_ALIGN_FAIL(i, __ Stxp(x0, x1, x2, MemOperand(x20))); |
| CHECK_ALIGN_FAIL(i, __ Stlxp(x0, x1, x2, MemOperand(x20))); |
| CHECK_ALIGN_FAIL(i, __ Ldxp(x0, x1, MemOperand(x20))); |
| CHECK_ALIGN_FAIL(i, __ Ldaxp(x0, x1, MemOperand(x20))); |
| } |
| } |
| } |
| |
| TEST(unaligned_single_copy_atomicity_negative_test_2) { |
| uint64_t data[] = {0x1010101010101010, 0x1010101010101010}; |
| |
| uint64_t* data_aligned = AlignUp(data, kAtomicAccessGranule); |
| |
| // Check that the same code doesn't fail with USCAT enabled but does |
| // fail when not enabled. |
| { |
| SETUP_WITH_FEATURES(CPUFeatures::kUSCAT); |
| START(); |
| __ Mov(x0, reinterpret_cast<uintptr_t>(data_aligned)); |
| __ Add(x0, x0, 1); |
| __ Ldxrh(w1, MemOperand(x0)); |
| END(); |
| if (CAN_RUN()) { |
| RUN_WITHOUT_SEEN_FEATURE_CHECK(); |
| } |
| } |
| { |
| SETUP(); |
| START(); |
| __ Mov(x0, reinterpret_cast<uintptr_t>(data_aligned)); |
| __ Add(x0, x0, 1); |
| __ Ldxrh(w1, MemOperand(x0)); |
| END(); |
| if (CAN_RUN()) { |
| MUST_FAIL_WITH_MESSAGE(RUN(), "ALIGNMENT EXCEPTION"); |
| } |
| } |
| } |
| #endif // VIXL_NEGATIVE_TESTING && VIXL_INCLUDE_SIMULATOR_AARCH64 |
| |
| |
| TEST(load_store_tagged_immediate_offset) { |
| uint64_t tags[] = {0x00, 0x1, 0x55, 0xff}; |
| int tag_count = sizeof(tags) / sizeof(tags[0]); |
| |
| const int kMaxDataLength = 160; |
| |
| for (int i = 0; i < tag_count; i++) { |
| unsigned char src[kMaxDataLength]; |
| uint64_t src_raw = reinterpret_cast<uint64_t>(src); |
| uint64_t src_tag = tags[i]; |
| uint64_t src_tagged = CPU::SetPointerTag(src_raw, src_tag); |
| |
| for (int k = 0; k < kMaxDataLength; k++) { |
| src[k] = k + 1; |
| } |
| |
| for (int j = 0; j < tag_count; j++) { |
| unsigned char dst[kMaxDataLength]; |
| uint64_t dst_raw = reinterpret_cast<uint64_t>(dst); |
| uint64_t dst_tag = tags[j]; |
| uint64_t dst_tagged = CPU::SetPointerTag(dst_raw, dst_tag); |
| |
| memset(dst, 0, kMaxDataLength); |
| |
| SETUP_WITH_FEATURES(CPUFeatures::kNEON); |
| START(); |
| |
| __ Mov(x0, src_tagged); |
| __ Mov(x1, dst_tagged); |
| |
| int offset = 0; |
| |
| // Scaled-immediate offsets. |
| { |
| ExactAssemblyScope scope(&masm, 2 * kInstructionSize); |
| __ ldp(q0, q1, MemOperand(x0, offset)); |
| __ stp(q0, q1, MemOperand(x1, offset)); |
| } |
| offset += 2 * kQRegSizeInBytes; |
| |
| { |
| ExactAssemblyScope scope(&masm, 2 * kInstructionSize); |
| __ ldp(x2, x3, MemOperand(x0, offset)); |
| __ stp(x2, x3, MemOperand(x1, offset)); |
| } |
| offset += 2 * kXRegSizeInBytes; |
| |
| { |
| ExactAssemblyScope scope(&masm, 2 * kInstructionSize); |
| __ ldpsw(x2, x3, MemOperand(x0, offset)); |
| __ stp(w2, w3, MemOperand(x1, offset)); |
| } |
| offset += 2 * kWRegSizeInBytes; |
| |
| { |
| ExactAssemblyScope scope(&masm, 2 * kInstructionSize); |
| __ ldp(d0, d1, MemOperand(x0, offset)); |
| __ stp(d0, d1, MemOperand(x1, offset)); |
| } |
| offset += 2 * kDRegSizeInBytes; |
| |
| { |
| ExactAssemblyScope scope(&masm, 2 * kInstructionSize); |
| __ ldp(w2, w3, MemOperand(x0, offset)); |
| __ stp(w2, w3, MemOperand(x1, offset)); |
| } |
| offset += 2 * kWRegSizeInBytes; |
| |
| { |
| ExactAssemblyScope scope(&masm, 2 * kInstructionSize); |
| __ ldp(s0, s1, MemOperand(x0, offset)); |
| __ stp(s0, s1, MemOperand(x1, offset)); |
| } |
| offset += 2 * kSRegSizeInBytes; |
| |
| { |
| ExactAssemblyScope scope(&masm, 2 * kInstructionSize); |
| __ ldr(x2, MemOperand(x0, offset), RequireScaledOffset); |
| __ str(x2, MemOperand(x1, offset), RequireScaledOffset); |
| } |
| offset += kXRegSizeInBytes; |
| |
| { |
| ExactAssemblyScope scope(&masm, 2 * kInstructionSize); |
| __ ldr(d0, MemOperand(x0, offset), RequireScaledOffset); |
| __ str(d0, MemOperand(x1, offset), RequireScaledOffset); |
| } |
| offset += kDRegSizeInBytes; |
| |
| { |
| ExactAssemblyScope scope(&masm, 2 * kInstructionSize); |
| __ ldr(w2, MemOperand(x0, offset), RequireScaledOffset); |
| __ str(w2, MemOperand(x1, offset), RequireScaledOffset); |
| } |
| offset += kWRegSizeInBytes; |
| |
| { |
| ExactAssemblyScope scope(&masm, 2 * kInstructionSize); |
| __ ldr(s0, MemOperand(x0, offset), RequireScaledOffset); |
| __ str(s0, MemOperand(x1, offset), RequireScaledOffset); |
| } |
| offset += kSRegSizeInBytes; |
| |
| { |
| ExactAssemblyScope scope(&masm, 2 * kInstructionSize); |
| __ ldrh(w2, MemOperand(x0, offset), RequireScaledOffset); |
| __ strh(w2, MemOperand(x1, offset), RequireScaledOffset); |
| } |
| offset += 2; |
| |
| { |
| ExactAssemblyScope scope(&masm, 2 * kInstructionSize); |
| __ ldrsh(w2, MemOperand(x0, offset), RequireScaledOffset); |
| __ strh(w2, MemOperand(x1, offset), RequireScaledOffset); |
| } |
| offset += 2; |
| |
| { |
| ExactAssemblyScope scope(&masm, 2 * kInstructionSize); |
| __ ldrb(w2, MemOperand(x0, offset), RequireScaledOffset); |
| __ strb(w2, MemOperand(x1, offset), RequireScaledOffset); |
| } |
| offset += 1; |
| |
| { |
| ExactAssemblyScope scope(&masm, 2 * kInstructionSize); |
| __ ldrsb(w2, MemOperand(x0, offset), RequireScaledOffset); |
| __ strb(w2, MemOperand(x1, offset), RequireScaledOffset); |
| } |
| offset += 1; |
| |
| // Unscaled-immediate offsets. |
| |
| { |
| ExactAssemblyScope scope(&masm, 2 * kInstructionSize); |
| __ ldur(x2, MemOperand(x0, offset), RequireUnscaledOffset); |
| __ stur(x2, MemOperand(x1, offset), RequireUnscaledOffset); |
| } |
| offset += kXRegSizeInBytes; |
| |
| { |
| ExactAssemblyScope scope(&masm, 2 * kInstructionSize); |
| __ ldur(d0, MemOperand(x0, offset), RequireUnscaledOffset); |
| __ stur(d0, MemOperand(x1, offset), RequireUnscaledOffset); |
| } |
| offset += kDRegSizeInBytes; |
| |
| { |
| ExactAssemblyScope scope(&masm, 2 * kInstructionSize); |
| __ ldur(w2, MemOperand(x0, offset), RequireUnscaledOffset); |
| __ stur(w2, MemOperand(x1, offset), RequireUnscaledOffset); |
| } |
| offset += kWRegSizeInBytes; |
| |
| { |
| ExactAssemblyScope scope(&masm, 2 * kInstructionSize); |
| __ ldur(s0, MemOperand(x0, offset), RequireUnscaledOffset); |
| __ stur(s0, MemOperand(x1, offset), RequireUnscaledOffset); |
| } |
| offset += kSRegSizeInBytes; |
| |
| { |
| ExactAssemblyScope scope(&masm, 2 * kInstructionSize); |
| __ ldurh(w2, MemOperand(x0, offset), RequireUnscaledOffset); |
| __ sturh(w2, MemOperand(x1, offset), RequireUnscaledOffset); |
| } |
| offset += 2; |
| |
| { |
| ExactAssemblyScope scope(&masm, 2 * kInstructionSize); |
| __ ldursh(w2, MemOperand(x0, offset), RequireUnscaledOffset); |
| __ sturh(w2, MemOperand(x1, offset), RequireUnscaledOffset); |
| } |
| offset += 2; |
| |
| { |
| ExactAssemblyScope scope(&masm, 2 * kInstructionSize); |
| __ ldurb(w2, MemOperand(x0, offset), RequireUnscaledOffset); |
| __ sturb(w2, MemOperand(x1, offset), RequireUnscaledOffset); |
| } |
| offset += 1; |
| |
| { |
| ExactAssemblyScope scope(&masm, 2 * kInstructionSize); |
| __ ldursb(w2, MemOperand(x0, offset), RequireUnscaledOffset); |
| __ sturb(w2, MemOperand(x1, offset), RequireUnscaledOffset); |
| } |
| offset += 1; |
| |
| // Extract the tag (so we can test that it was preserved correctly). |
| __ Ubfx(x0, x0, kAddressTagOffset, kAddressTagWidth); |
| __ Ubfx(x1, x1, kAddressTagOffset, kAddressTagWidth); |
| |
| VIXL_ASSERT(kMaxDataLength >= offset); |
| |
| END(); |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(src_tag, x0); |
| ASSERT_EQUAL_64(dst_tag, x1); |
| |
| for (int k = 0; k < offset; k++) { |
| VIXL_CHECK(src[k] == dst[k]); |
| } |
| } |
| } |
| } |
| } |
| |
| |
| TEST(load_store_tagged_immediate_preindex) { |
| uint64_t tags[] = {0x00, 0x1, 0x55, 0xff}; |
| int tag_count = sizeof(tags) / sizeof(tags[0]); |
| |
| const int kMaxDataLength = 128; |
| |
| for (int i = 0; i < tag_count; i++) { |
| unsigned char src[kMaxDataLength]; |
| uint64_t src_raw = reinterpret_cast<uint64_t>(src); |
| uint64_t src_tag = tags[i]; |
| uint64_t src_tagged = CPU::SetPointerTag(src_raw, src_tag); |
| |
| for (int k = 0; k < kMaxDataLength; k++) { |
| src[k] = k + 1; |
| } |
| |
| for (int j = 0; j < tag_count; j++) { |
| unsigned char dst[kMaxDataLength]; |
| uint64_t dst_raw = reinterpret_cast<uint64_t>(dst); |
| uint64_t dst_tag = tags[j]; |
| uint64_t dst_tagged = CPU::SetPointerTag(dst_raw, dst_tag); |
| |
| for (int k = 0; k < kMaxDataLength; k++) { |
| dst[k] = 0; |
| } |
| |
| SETUP_WITH_FEATURES(CPUFeatures::kNEON); |
| START(); |
| |
| // Each MemOperand must apply a pre-index equal to the size of the |
| // previous access. |
| |
| // Start with a non-zero preindex. |
| int preindex = 62 * kXRegSizeInBytes; |
| int data_length = 0; |
| |
| __ Mov(x0, src_tagged - preindex); |
| __ Mov(x1, dst_tagged - preindex); |
| |
| { |
| ExactAssemblyScope scope(&masm, 2 * kInstructionSize); |
| __ ldp(q0, q1, MemOperand(x0, preindex, PreIndex)); |
| __ stp(q0, q1, MemOperand(x1, preindex, PreIndex)); |
| } |
| preindex = 2 * kQRegSizeInBytes; |
| data_length = preindex; |
| |
| { |
| ExactAssemblyScope scope(&masm, 2 * kInstructionSize); |
| __ ldp(x2, x3, MemOperand(x0, preindex, PreIndex)); |
| __ stp(x2, x3, MemOperand(x1, preindex, PreIndex)); |
| } |
| preindex = 2 * kXRegSizeInBytes; |
| data_length += preindex; |
| |
| { |
| ExactAssemblyScope scope(&masm, 2 * kInstructionSize); |
| __ ldpsw(x2, x3, MemOperand(x0, preindex, PreIndex)); |
| __ stp(w2, w3, MemOperand(x1, preindex, PreIndex)); |
| } |
| preindex = 2 * kWRegSizeInBytes; |
| data_length += preindex; |
| |
| { |
| ExactAssemblyScope scope(&masm, 2 * kInstructionSize); |
| __ ldp(d0, d1, MemOperand(x0, preindex, PreIndex)); |
| __ stp(d0, d1, MemOperand(x1, preindex, PreIndex)); |
| } |
| preindex = 2 * kDRegSizeInBytes; |
| data_length += preindex; |
| |
| { |
| ExactAssemblyScope scope(&masm, 2 * kInstructionSize); |
| __ ldp(w2, w3, MemOperand(x0, preindex, PreIndex)); |
| __ stp(w2, w3, MemOperand(x1, preindex, PreIndex)); |
| } |
| preindex = 2 * kWRegSizeInBytes; |
| data_length += preindex; |
| |
| { |
| ExactAssemblyScope scope(&masm, 2 * kInstructionSize); |
| __ ldp(s0, s1, MemOperand(x0, preindex, PreIndex)); |
| __ stp(s0, s1, MemOperand(x1, preindex, PreIndex)); |
| } |
| preindex = 2 * kSRegSizeInBytes; |
| data_length += preindex; |
| |
| { |
| ExactAssemblyScope scope(&masm, 2 * kInstructionSize); |
| __ ldr(x2, MemOperand(x0, preindex, PreIndex)); |
| __ str(x2, MemOperand(x1, preindex, PreIndex)); |
| } |
| preindex = kXRegSizeInBytes; |
| data_length += preindex; |
| |
| { |
| ExactAssemblyScope scope(&masm, 2 * kInstructionSize); |
| __ ldr(d0, MemOperand(x0, preindex, PreIndex)); |
| __ str(d0, MemOperand(x1, preindex, PreIndex)); |
| } |
| preindex = kDRegSizeInBytes; |
| data_length += preindex; |
| |
| { |
| ExactAssemblyScope scope(&masm, 2 * kInstructionSize); |
| __ ldr(w2, MemOperand(x0, preindex, PreIndex)); |
| __ str(w2, MemOperand(x1, preindex, PreIndex)); |
| } |
| preindex = kWRegSizeInBytes; |
| data_length += preindex; |
| |
| { |
| ExactAssemblyScope scope(&masm, 2 * kInstructionSize); |
| __ ldr(s0, MemOperand(x0, preindex, PreIndex)); |
| __ str(s0, MemOperand(x1, preindex, PreIndex)); |
| } |
| preindex = kSRegSizeInBytes; |
| data_length += preindex; |
| |
| { |
| ExactAssemblyScope scope(&masm, 2 * kInstructionSize); |
| __ ldrh(w2, MemOperand(x0, preindex, PreIndex)); |
| __ strh(w2, MemOperand(x1, preindex, PreIndex)); |
| } |
| preindex = 2; |
| data_length += preindex; |
| |
| { |
| ExactAssemblyScope scope(&masm, 2 * kInstructionSize); |
| __ ldrsh(w2, MemOperand(x0, preindex, PreIndex)); |
| __ strh(w2, MemOperand(x1, preindex, PreIndex)); |
| } |
| preindex = 2; |
| data_length += preindex; |
| |
| { |
| ExactAssemblyScope scope(&masm, 2 * kInstructionSize); |
| __ ldrb(w2, MemOperand(x0, preindex, PreIndex)); |
| __ strb(w2, MemOperand(x1, preindex, PreIndex)); |
| } |
| preindex = 1; |
| data_length += preindex; |
| |
| { |
| ExactAssemblyScope scope(&masm, 2 * kInstructionSize); |
| __ ldrsb(w2, MemOperand(x0, preindex, PreIndex)); |
| __ strb(w2, MemOperand(x1, preindex, PreIndex)); |
| } |
| preindex = 1; |
| data_length += preindex; |
| |
| VIXL_ASSERT(kMaxDataLength >= data_length); |
| |
| END(); |
| if (CAN_RUN()) { |
| RUN(); |
| |
| // Check that the preindex was correctly applied in each operation, and |
| // that the tag was preserved. |
| ASSERT_EQUAL_64(src_tagged + data_length - preindex, x0); |
| ASSERT_EQUAL_64(dst_tagged + data_length - preindex, x1); |
| |
| for (int k = 0; k < data_length; k++) { |
| VIXL_CHECK(src[k] == dst[k]); |
| } |
| } |
| } |
| } |
| } |
| |
| |
| TEST(load_store_tagged_immediate_postindex) { |
| uint64_t tags[] = {0x00, 0x1, 0x55, 0xff}; |
| int tag_count = sizeof(tags) / sizeof(tags[0]); |
| |
| const int kMaxDataLength = 128; |
| |
| for (int i = 0; i < tag_count; i++) { |
| unsigned char src[kMaxDataLength]; |
| uint64_t src_raw = reinterpret_cast<uint64_t>(src); |
| uint64_t src_tag = tags[i]; |
| uint64_t src_tagged = CPU::SetPointerTag(src_raw, src_tag); |
| |
| for (int k = 0; k < kMaxDataLength; k++) { |
| src[k] = k + 1; |
| } |
| |
| for (int j = 0; j < tag_count; j++) { |
| unsigned char dst[kMaxDataLength]; |
| uint64_t dst_raw = reinterpret_cast<uint64_t>(dst); |
| uint64_t dst_tag = tags[j]; |
| uint64_t dst_tagged = CPU::SetPointerTag(dst_raw, dst_tag); |
| |
| for (int k = 0; k < kMaxDataLength; k++) { |
| dst[k] = 0; |
| } |
| |
| SETUP_WITH_FEATURES(CPUFeatures::kNEON); |
| START(); |
| |
| int postindex = 2 * kXRegSizeInBytes; |
| int data_length = 0; |
| |
| __ Mov(x0, src_tagged); |
| __ Mov(x1, dst_tagged); |
| |
| { |
| ExactAssemblyScope scope(&masm, 2 * kInstructionSize); |
| __ ldp(x2, x3, MemOperand(x0, postindex, PostIndex)); |
| __ stp(x2, x3, MemOperand(x1, postindex, PostIndex)); |
| } |
| data_length = postindex; |
| |
| postindex = 2 * kQRegSizeInBytes; |
| { |
| ExactAssemblyScope scope(&masm, 2 * kInstructionSize); |
| __ ldp(q0, q1, MemOperand(x0, postindex, PostIndex)); |
| __ stp(q0, q1, MemOperand(x1, postindex, PostIndex)); |
| } |
| data_length += postindex; |
| |
| postindex = 2 * kWRegSizeInBytes; |
| { |
| ExactAssemblyScope scope(&masm, 2 * kInstructionSize); |
| __ ldpsw(x2, x3, MemOperand(x0, postindex, PostIndex)); |
| __ stp(w2, w3, MemOperand(x1, postindex, PostIndex)); |
| } |
| data_length += postindex; |
| |
| postindex = 2 * kDRegSizeInBytes; |
| { |
| ExactAssemblyScope scope(&masm, 2 * kInstructionSize); |
| __ ldp(d0, d1, MemOperand(x0, postindex, PostIndex)); |
| __ stp(d0, d1, MemOperand(x1, postindex, PostIndex)); |
| } |
| data_length += postindex; |
| |
| postindex = 2 * kWRegSizeInBytes; |
| { |
| ExactAssemblyScope scope(&masm, 2 * kInstructionSize); |
| __ ldp(w2, w3, MemOperand(x0, postindex, PostIndex)); |
| __ stp(w2, w3, MemOperand(x1, postindex, PostIndex)); |
| } |
| data_length += postindex; |
| |
| postindex = 2 * kSRegSizeInBytes; |
| { |
| ExactAssemblyScope scope(&masm, 2 * kInstructionSize); |
| __ ldp(s0, s1, MemOperand(x0, postindex, PostIndex)); |
| __ stp(s0, s1, MemOperand(x1, postindex, PostIndex)); |
| } |
| data_length += postindex; |
| |
| postindex = kXRegSizeInBytes; |
| { |
| ExactAssemblyScope scope(&masm, 2 * kInstructionSize); |
| __ ldr(x2, MemOperand(x0, postindex, PostIndex)); |
| __ str(x2, MemOperand(x1, postindex, PostIndex)); |
| } |
| data_length += postindex; |
| |
| postindex = kDRegSizeInBytes; |
| { |
| ExactAssemblyScope scope(&masm, 2 * kInstructionSize); |
| __ ldr(d0, MemOperand(x0, postindex, PostIndex)); |
| __ str(d0, MemOperand(x1, postindex, PostIndex)); |
| } |
| data_length += postindex; |
| |
| postindex = kWRegSizeInBytes; |
| { |
| ExactAssemblyScope scope(&masm, 2 * kInstructionSize); |
| __ ldr(w2, MemOperand(x0, postindex, PostIndex)); |
| __ str(w2, MemOperand(x1, postindex, PostIndex)); |
| } |
| data_length += postindex; |
| |
| postindex = kSRegSizeInBytes; |
| { |
| ExactAssemblyScope scope(&masm, 2 * kInstructionSize); |
| __ ldr(s0, MemOperand(x0, postindex, PostIndex)); |
| __ str(s0, MemOperand(x1, postindex, PostIndex)); |
| } |
| data_length += postindex; |
| |
| postindex = 2; |
| { |
| ExactAssemblyScope scope(&masm, 2 * kInstructionSize); |
| __ ldrh(w2, MemOperand(x0, postindex, PostIndex)); |
| __ strh(w2, MemOperand(x1, postindex, PostIndex)); |
| } |
| data_length += postindex; |
| |
| postindex = 2; |
| { |
| ExactAssemblyScope scope(&masm, 2 * kInstructionSize); |
| __ ldrsh(w2, MemOperand(x0, postindex, PostIndex)); |
| __ strh(w2, MemOperand(x1, postindex, PostIndex)); |
| } |
| data_length += postindex; |
| |
| postindex = 1; |
| { |
| ExactAssemblyScope scope(&masm, 2 * kInstructionSize); |
| __ ldrb(w2, MemOperand(x0, postindex, PostIndex)); |
| __ strb(w2, MemOperand(x1, postindex, PostIndex)); |
| } |
| data_length += postindex; |
| |
| postindex = 1; |
| { |
| ExactAssemblyScope scope(&masm, 2 * kInstructionSize); |
| __ ldrsb(w2, MemOperand(x0, postindex, PostIndex)); |
| __ strb(w2, MemOperand(x1, postindex, PostIndex)); |
| } |
| data_length += postindex; |
| |
| VIXL_ASSERT(kMaxDataLength >= data_length); |
| |
| END(); |
| if (CAN_RUN()) { |
| RUN(); |
| |
| // Check that the postindex was correctly applied in each operation, and |
| // that the tag was preserved. |
| ASSERT_EQUAL_64(src_tagged + data_length, x0); |
| ASSERT_EQUAL_64(dst_tagged + data_length, x1); |
| |
| for (int k = 0; k < data_length; k++) { |
| VIXL_CHECK(src[k] == dst[k]); |
| } |
| } |
| } |
| } |
| } |
| |
| |
| TEST(load_store_tagged_register_offset) { |
| uint64_t tags[] = {0x00, 0x1, 0x55, 0xff}; |
| int tag_count = sizeof(tags) / sizeof(tags[0]); |
| |
| const int kMaxDataLength = 128; |
| |
| for (int i = 0; i < tag_count; i++) { |
| unsigned char src[kMaxDataLength]; |
| uint64_t src_raw = reinterpret_cast<uint64_t>(src); |
| uint64_t src_tag = tags[i]; |
| uint64_t src_tagged = CPU::SetPointerTag(src_raw, src_tag); |
| |
| for (int k = 0; k < kMaxDataLength; k++) { |
| src[k] = k + 1; |
| } |
| |
| for (int j = 0; j < tag_count; j++) { |
| unsigned char dst[kMaxDataLength]; |
| uint64_t dst_raw = reinterpret_cast<uint64_t>(dst); |
| uint64_t dst_tag = tags[j]; |
| uint64_t dst_tagged = CPU::SetPointerTag(dst_raw, dst_tag); |
| |
| // Also tag the offset register; the operation should still succeed. |
| for (int o = 0; o < tag_count; o++) { |
| uint64_t offset_base = CPU::SetPointerTag(UINT64_C(0), tags[o]); |
| int data_length = 0; |
| |
| for (int k = 0; k < kMaxDataLength; k++) { |
| dst[k] = 0; |
| } |
| |
| SETUP_WITH_FEATURES(CPUFeatures::kNEON); |
| START(); |
| |
| __ Mov(x0, src_tagged); |
| __ Mov(x1, dst_tagged); |
| |
| __ Mov(x10, offset_base + data_length); |
| { |
| ExactAssemblyScope scope(&masm, 2 * kInstructionSize); |
| __ ldr(x2, MemOperand(x0, x10)); |
| __ str(x2, MemOperand(x1, x10)); |
| } |
| data_length += kXRegSizeInBytes; |
| |
| __ Mov(x10, offset_base + data_length); |
| { |
| ExactAssemblyScope scope(&masm, 2 * kInstructionSize); |
| __ ldr(d0, MemOperand(x0, x10)); |
| __ str(d0, MemOperand(x1, x10)); |
| } |
| data_length += kDRegSizeInBytes; |
| |
| __ Mov(x10, offset_base + data_length); |
| { |
| ExactAssemblyScope scope(&masm, 2 * kInstructionSize); |
| __ ldr(w2, MemOperand(x0, x10)); |
| __ str(w2, MemOperand(x1, x10)); |
| } |
| data_length += kWRegSizeInBytes; |
| |
| __ Mov(x10, offset_base + data_length); |
| { |
| ExactAssemblyScope scope(&masm, 2 * kInstructionSize); |
| __ ldr(s0, MemOperand(x0, x10)); |
| __ str(s0, MemOperand(x1, x10)); |
| } |
| data_length += kSRegSizeInBytes; |
| |
| __ Mov(x10, offset_base + data_length); |
| { |
| ExactAssemblyScope scope(&masm, 2 * kInstructionSize); |
| __ ldrh(w2, MemOperand(x0, x10)); |
| __ strh(w2, MemOperand(x1, x10)); |
| } |
| data_length += 2; |
| |
| __ Mov(x10, offset_base + data_length); |
| { |
| ExactAssemblyScope scope(&masm, 2 * kInstructionSize); |
| __ ldrsh(w2, MemOperand(x0, x10)); |
| __ strh(w2, MemOperand(x1, x10)); |
| } |
| data_length += 2; |
| |
| __ Mov(x10, offset_base + data_length); |
| { |
| ExactAssemblyScope scope(&masm, 2 * kInstructionSize); |
| __ ldrb(w2, MemOperand(x0, x10)); |
| __ strb(w2, MemOperand(x1, x10)); |
| } |
| data_length += 1; |
| |
| __ Mov(x10, offset_base + data_length); |
| { |
| ExactAssemblyScope scope(&masm, 2 * kInstructionSize); |
| __ ldrsb(w2, MemOperand(x0, x10)); |
| __ strb(w2, MemOperand(x1, x10)); |
| } |
| data_length += 1; |
| |
| VIXL_ASSERT(kMaxDataLength >= data_length); |
| |
| END(); |
| if (CAN_RUN()) { |
| RUN(); |
| |
| // Check that the postindex was correctly applied in each operation, |
| // and that the tag was preserved. |
| ASSERT_EQUAL_64(src_tagged, x0); |
| ASSERT_EQUAL_64(dst_tagged, x1); |
| ASSERT_EQUAL_64(offset_base + data_length - 1, x10); |
| |
| for (int k = 0; k < data_length; k++) { |
| VIXL_CHECK(src[k] == dst[k]); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| |
| TEST(load_store_tagged_register_postindex) { |
| uint64_t src[] = {0x0706050403020100, 0x0f0e0d0c0b0a0908}; |
| uint64_t tags[] = {0x00, 0x1, 0x55, 0xff}; |
| int tag_count = sizeof(tags) / sizeof(tags[0]); |
| |
| for (int j = 0; j < tag_count; j++) { |
| for (int i = 0; i < tag_count; i++) { |
| SETUP_WITH_FEATURES(CPUFeatures::kNEON); |
| |
| uint64_t src_base = reinterpret_cast<uint64_t>(src); |
| uint64_t src_tagged = CPU::SetPointerTag(src_base, tags[i]); |
| uint64_t offset_tagged = CPU::SetPointerTag(UINT64_C(0), tags[j]); |
| |
| START(); |
| __ Mov(x10, src_tagged); |
| __ Mov(x11, offset_tagged); |
| __ Ld1(v0.V16B(), MemOperand(x10, x11, PostIndex)); |
| // TODO: add other instructions (ld2-4, st1-4) as they become available. |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_128(0x0f0e0d0c0b0a0908, 0x0706050403020100, q0); |
| ASSERT_EQUAL_64(src_tagged + offset_tagged, x10); |
| } |
| } |
| } |
| } |
| |
| |
| TEST(branch_tagged) { |
| SETUP(); |
| START(); |
| |
| Label loop, loop_entry, done; |
| __ Adr(x0, &loop); |
| __ Mov(x1, 0); |
| __ B(&loop_entry); |
| |
| __ Bind(&loop); |
| __ Add(x1, x1, 1); // Count successful jumps. |
| |
| // Advance to the next tag, then bail out if we've come back around to tag 0. |
| __ Add(x0, x0, UINT64_C(1) << kAddressTagOffset); |
| __ Tst(x0, kAddressTagMask); |
| __ B(eq, &done); |
| |
| __ Bind(&loop_entry); |
| __ Br(x0); |
| |
| __ Bind(&done); |
| |
| END(); |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(1 << kAddressTagWidth, x1); |
| } |
| } |
| |
| |
| TEST(branch_and_link_tagged) { |
| SETUP(); |
| START(); |
| |
| Label loop, loop_entry, done; |
| __ Adr(x0, &loop); |
| __ Mov(x1, 0); |
| __ B(&loop_entry); |
| |
| __ Bind(&loop); |
| |
| // Bail out (before counting a successful jump) if lr appears to be tagged. |
| __ Tst(lr, kAddressTagMask); |
| __ B(ne, &done); |
| |
| __ Add(x1, x1, 1); // Count successful jumps. |
| |
| // Advance to the next tag, then bail out if we've come back around to tag 0. |
| __ Add(x0, x0, UINT64_C(1) << kAddressTagOffset); |
| __ Tst(x0, kAddressTagMask); |
| __ B(eq, &done); |
| |
| __ Bind(&loop_entry); |
| __ Blr(x0); |
| |
| __ Bind(&done); |
| |
| END(); |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(1 << kAddressTagWidth, x1); |
| } |
| } |
| |
| |
| TEST(branch_tagged_and_adr_adrp) { |
| SETUP_CUSTOM(kPageSize, PageOffsetDependentCode); |
| START(); |
| |
| Label loop, loop_entry, done; |
| __ Adr(x0, &loop); |
| __ Mov(x1, 0); |
| __ B(&loop_entry); |
| |
| __ Bind(&loop); |
| |
| // Bail out (before counting a successful jump) if `adr x10, ...` is tagged. |
| __ Adr(x10, &done); |
| __ Tst(x10, kAddressTagMask); |
| __ B(ne, &done); |
| |
| // Bail out (before counting a successful jump) if `adrp x11, ...` is tagged. |
| __ Adrp(x11, &done); |
| __ Tst(x11, kAddressTagMask); |
| __ B(ne, &done); |
| |
| __ Add(x1, x1, 1); // Count successful iterations. |
| |
| // Advance to the next tag, then bail out if we've come back around to tag 0. |
| __ Add(x0, x0, UINT64_C(1) << kAddressTagOffset); |
| __ Tst(x0, kAddressTagMask); |
| __ B(eq, &done); |
| |
| __ Bind(&loop_entry); |
| __ Br(x0); |
| |
| __ Bind(&done); |
| |
| END(); |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(1 << kAddressTagWidth, x1); |
| } |
| } |
| |
| TEST(system_sys) { |
| SETUP(); |
| const char* msg = "SYS test!"; |
| uintptr_t msg_addr = reinterpret_cast<uintptr_t>(msg); |
| |
| START(); |
| __ Mov(x4, msg_addr); |
| __ Sys(3, 0x7, 0x5, 1, x4); |
| __ Mov(x3, x4); |
| __ Sys(3, 0x7, 0xa, 1, x3); |
| __ Mov(x2, x3); |
| __ Sys(3, 0x7, 0xb, 1, x2); |
| __ Mov(x1, x2); |
| __ Sys(3, 0x7, 0xe, 1, x1); |
| // TODO: Add tests to check ZVA equivalent. |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| } |
| } |
| |
| |
| TEST(system_ic) { |
| SETUP(); |
| const char* msg = "IC test!"; |
| uintptr_t msg_addr = reinterpret_cast<uintptr_t>(msg); |
| |
| START(); |
| __ Mov(x11, msg_addr); |
| __ Ic(IVAU, x11); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| } |
| } |
| |
| |
| TEST(system_dc) { |
| SETUP(); |
| const char* msg = "DC test!"; |
| uintptr_t msg_addr = reinterpret_cast<uintptr_t>(msg); |
| |
| START(); |
| __ Mov(x20, msg_addr); |
| __ Dc(CVAC, x20); |
| __ Mov(x21, msg_addr); |
| __ Dc(CVAU, x21); |
| __ Mov(x22, msg_addr); |
| __ Dc(CIVAC, x22); |
| // TODO: Add tests to check ZVA. |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| ASSERT_EQUAL_64(msg_addr, x20); |
| ASSERT_EQUAL_64(msg_addr, x21); |
| ASSERT_EQUAL_64(msg_addr, x22); |
| } |
| } |
| |
| |
| TEST(system_dcpop) { |
| SETUP_WITH_FEATURES(CPUFeatures::kDCPoP); |
| const char* msg = "DCPoP test!"; |
| uintptr_t msg_addr = reinterpret_cast<uintptr_t>(msg); |
| |
| START(); |
| __ Mov(x20, msg_addr); |
| __ Dc(CVAP, x20); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| ASSERT_EQUAL_64(msg_addr, x20); |
| } |
| } |
| |
| TEST(system_dccvadp) { |
| SETUP_WITH_FEATURES(CPUFeatures::kDCCVADP); |
| const char* msg = "DCCVADP test!"; |
| uintptr_t msg_addr = reinterpret_cast<uintptr_t>(msg); |
| |
| START(); |
| __ Mov(x20, msg_addr); |
| __ Dc(CVADP, x20); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| ASSERT_EQUAL_64(msg_addr, x20); |
| } |
| } |
| |
| |
| // We currently disable tests for CRC32 instructions when running natively. |
| // Support for this family of instruction is optional, and so native platforms |
| // may simply fail to execute the test. |
| TEST(crc32b) { |
| SETUP_WITH_FEATURES(CPUFeatures::kCRC32); |
| |
| START(); |
| |
| __ Mov(w0, 0); |
| __ Mov(w1, 0); |
| __ Crc32b(w10, w0, w1); |
| |
| __ Mov(w0, 0x1); |
| __ Mov(w1, 0x138); |
| __ Crc32b(w11, w0, w1); |
| |
| __ Mov(w0, 0x1); |
| __ Mov(w1, 0x38); |
| __ Crc32b(w12, w0, w1); |
| |
| __ Mov(w0, 0); |
| __ Mov(w1, 128); |
| __ Crc32b(w13, w0, w1); |
| |
| __ Mov(w0, UINT32_MAX); |
| __ Mov(w1, 255); |
| __ Crc32b(w14, w0, w1); |
| |
| __ Mov(w0, 0x00010001); |
| __ Mov(w1, 0x10001000); |
| __ Crc32b(w15, w0, w1); |
| |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0x0, x10); |
| ASSERT_EQUAL_64(0x5f058808, x11); |
| ASSERT_EQUAL_64(0x5f058808, x12); |
| ASSERT_EQUAL_64(0xedb88320, x13); |
| ASSERT_EQUAL_64(0x00ffffff, x14); |
| ASSERT_EQUAL_64(0x77073196, x15); |
| } |
| } |
| |
| |
| TEST(crc32h) { |
| SETUP_WITH_FEATURES(CPUFeatures::kCRC32); |
| |
| START(); |
| |
| __ Mov(w0, 0); |
| __ Mov(w1, 0); |
| __ Crc32h(w10, w0, w1); |
| |
| __ Mov(w0, 0x1); |
| __ Mov(w1, 0x10038); |
| __ Crc32h(w11, w0, w1); |
| |
| __ Mov(w0, 0x1); |
| __ Mov(w1, 0x38); |
| __ Crc32h(w12, w0, w1); |
| |
| __ Mov(w0, 0); |
| __ Mov(w1, 128); |
| __ Crc32h(w13, w0, w1); |
| |
| __ Mov(w0, UINT32_MAX); |
| __ Mov(w1, 255); |
| __ Crc32h(w14, w0, w1); |
| |
| __ Mov(w0, 0x00010001); |
| __ Mov(w1, 0x10001000); |
| __ Crc32h(w15, w0, w1); |
| |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0x0, x10); |
| ASSERT_EQUAL_64(0x0e848dba, x11); |
| ASSERT_EQUAL_64(0x0e848dba, x12); |
| ASSERT_EQUAL_64(0x3b83984b, x13); |
| ASSERT_EQUAL_64(0x2d021072, x14); |
| ASSERT_EQUAL_64(0x04ac2124, x15); |
| } |
| } |
| |
| |
| TEST(crc32w) { |
| SETUP_WITH_FEATURES(CPUFeatures::kCRC32); |
| |
| START(); |
| |
| __ Mov(w0, 0); |
| __ Mov(w1, 0); |
| __ Crc32w(w10, w0, w1); |
| |
| __ Mov(w0, 0x1); |
| __ Mov(w1, 0x80000031); |
| __ Crc32w(w11, w0, w1); |
| |
| __ Mov(w0, 0); |
| __ Mov(w1, 128); |
| __ Crc32w(w13, w0, w1); |
| |
| __ Mov(w0, UINT32_MAX); |
| __ Mov(w1, 255); |
| __ Crc32w(w14, w0, w1); |
| |
| __ Mov(w0, 0x00010001); |
| __ Mov(w1, 0x10001000); |
| __ Crc32w(w15, w0, w1); |
| |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0x0, x10); |
| ASSERT_EQUAL_64(0x1d937b81, x11); |
| ASSERT_EQUAL_64(0xed59b63b, x13); |
| ASSERT_EQUAL_64(0x00be2612, x14); |
| ASSERT_EQUAL_64(0xa036e530, x15); |
| } |
| } |
| |
| |
| TEST(crc32x) { |
| SETUP_WITH_FEATURES(CPUFeatures::kCRC32); |
| |
| START(); |
| |
| __ Mov(w0, 0); |
| __ Mov(x1, 0); |
| __ Crc32x(w10, w0, x1); |
| |
| __ Mov(w0, 0x1); |
| __ Mov(x1, UINT64_C(0x0000000800000031)); |
| __ Crc32x(w11, w0, x1); |
| |
| __ Mov(w0, 0); |
| __ Mov(x1, 128); |
| __ Crc32x(w13, w0, x1); |
| |
| __ Mov(w0, UINT32_MAX); |
| __ Mov(x1, 255); |
| __ Crc32x(w14, w0, x1); |
| |
| __ Mov(w0, 0x00010001); |
| __ Mov(x1, UINT64_C(0x1000100000000000)); |
| __ Crc32x(w15, w0, x1); |
| |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0x0, x10); |
| ASSERT_EQUAL_64(0x40797b92, x11); |
| ASSERT_EQUAL_64(0x533b85da, x13); |
| ASSERT_EQUAL_64(0xbc962670, x14); |
| ASSERT_EQUAL_64(0x0667602f, x15); |
| } |
| } |
| |
| |
| TEST(crc32cb) { |
| SETUP_WITH_FEATURES(CPUFeatures::kCRC32); |
| |
| START(); |
| |
| __ Mov(w0, 0); |
| __ Mov(w1, 0); |
| __ Crc32cb(w10, w0, w1); |
| |
| __ Mov(w0, 0x1); |
| __ Mov(w1, 0x138); |
| __ Crc32cb(w11, w0, w1); |
| |
| __ Mov(w0, 0x1); |
| __ Mov(w1, 0x38); |
| __ Crc32cb(w12, w0, w1); |
| |
| __ Mov(w0, 0); |
| __ Mov(w1, 128); |
| __ Crc32cb(w13, w0, w1); |
| |
| __ Mov(w0, UINT32_MAX); |
| __ Mov(w1, 255); |
| __ Crc32cb(w14, w0, w1); |
| |
| __ Mov(w0, 0x00010001); |
| __ Mov(w1, 0x10001000); |
| __ Crc32cb(w15, w0, w1); |
| |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0x0, x10); |
| ASSERT_EQUAL_64(0x4851927d, x11); |
| ASSERT_EQUAL_64(0x4851927d, x12); |
| ASSERT_EQUAL_64(0x82f63b78, x13); |
| ASSERT_EQUAL_64(0x00ffffff, x14); |
| ASSERT_EQUAL_64(0xf26b8203, x15); |
| } |
| } |
| |
| |
| TEST(crc32ch) { |
| SETUP_WITH_FEATURES(CPUFeatures::kCRC32); |
| |
| START(); |
| |
| __ Mov(w0, 0); |
| __ Mov(w1, 0); |
| __ Crc32ch(w10, w0, w1); |
| |
| __ Mov(w0, 0x1); |
| __ Mov(w1, 0x10038); |
| __ Crc32ch(w11, w0, w1); |
| |
| __ Mov(w0, 0x1); |
| __ Mov(w1, 0x38); |
| __ Crc32ch(w12, w0, w1); |
| |
| __ Mov(w0, 0); |
| __ Mov(w1, 128); |
| __ Crc32ch(w13, w0, w1); |
| |
| __ Mov(w0, UINT32_MAX); |
| __ Mov(w1, 255); |
| __ Crc32ch(w14, w0, w1); |
| |
| __ Mov(w0, 0x00010001); |
| __ Mov(w1, 0x10001000); |
| __ Crc32ch(w15, w0, w1); |
| |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0x0, x10); |
| ASSERT_EQUAL_64(0xcef8494c, x11); |
| ASSERT_EQUAL_64(0xcef8494c, x12); |
| ASSERT_EQUAL_64(0xfbc3faf9, x13); |
| ASSERT_EQUAL_64(0xad7dacae, x14); |
| ASSERT_EQUAL_64(0x03fc5f19, x15); |
| } |
| } |
| |
| |
| TEST(crc32cw) { |
| SETUP_WITH_FEATURES(CPUFeatures::kCRC32); |
| |
| START(); |
| |
| __ Mov(w0, 0); |
| __ Mov(w1, 0); |
| __ Crc32cw(w10, w0, w1); |
| |
| __ Mov(w0, 0x1); |
| __ Mov(w1, 0x80000031); |
| __ Crc32cw(w11, w0, w1); |
| |
| __ Mov(w0, 0); |
| __ Mov(w1, 128); |
| __ Crc32cw(w13, w0, w1); |
| |
| __ Mov(w0, UINT32_MAX); |
| __ Mov(w1, 255); |
| __ Crc32cw(w14, w0, w1); |
| |
| __ Mov(w0, 0x00010001); |
| __ Mov(w1, 0x10001000); |
| __ Crc32cw(w15, w0, w1); |
| |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0x0, x10); |
| ASSERT_EQUAL_64(0xbcb79ece, x11); |
| ASSERT_EQUAL_64(0x52a0c93f, x13); |
| ASSERT_EQUAL_64(0x9f9b5c7a, x14); |
| ASSERT_EQUAL_64(0xae1b882a, x15); |
| } |
| } |
| |
| |
| TEST(crc32cx) { |
| SETUP_WITH_FEATURES(CPUFeatures::kCRC32); |
| |
| START(); |
| |
| __ Mov(w0, 0); |
| __ Mov(x1, 0); |
| __ Crc32cx(w10, w0, x1); |
| |
| __ Mov(w0, 0x1); |
| __ Mov(x1, UINT64_C(0x0000000800000031)); |
| __ Crc32cx(w11, w0, x1); |
| |
| __ Mov(w0, 0); |
| __ Mov(x1, 128); |
| __ Crc32cx(w13, w0, x1); |
| |
| __ Mov(w0, UINT32_MAX); |
| __ Mov(x1, 255); |
| __ Crc32cx(w14, w0, x1); |
| |
| __ Mov(w0, 0x00010001); |
| __ Mov(x1, UINT64_C(0x1000100000000000)); |
| __ Crc32cx(w15, w0, x1); |
| |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0x0, x10); |
| ASSERT_EQUAL_64(0x7f320fcb, x11); |
| ASSERT_EQUAL_64(0x34019664, x13); |
| ASSERT_EQUAL_64(0x6cc27dd0, x14); |
| ASSERT_EQUAL_64(0xc6f0acdb, x15); |
| } |
| } |
| |
| TEST(regress_cmp_shift_imm) { |
| SETUP(); |
| |
| START(); |
| |
| __ Mov(x0, 0x3d720c8d); |
| __ Cmp(x0, Operand(0x3d720c8d)); |
| |
| END(); |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_NZCV(ZCFlag); |
| } |
| } |
| |
| |
| TEST(compute_address) { |
| SETUP(); |
| |
| START(); |
| int64_t base_address = INT64_C(0x123000000abc); |
| int64_t reg_offset = INT64_C(0x1087654321); |
| Register base = x0; |
| Register offset = x1; |
| |
| __ Mov(base, base_address); |
| __ Mov(offset, reg_offset); |
| |
| |
| __ ComputeAddress(x2, MemOperand(base, 0)); |
| __ ComputeAddress(x3, MemOperand(base, 8)); |
| __ ComputeAddress(x4, MemOperand(base, -100)); |
| |
| __ ComputeAddress(x5, MemOperand(base, offset)); |
| __ ComputeAddress(x6, MemOperand(base, offset, LSL, 2)); |
| __ ComputeAddress(x7, MemOperand(base, offset, LSL, 4)); |
| __ ComputeAddress(x8, MemOperand(base, offset, LSL, 8)); |
| |
| __ ComputeAddress(x9, MemOperand(base, offset, SXTW)); |
| __ ComputeAddress(x10, MemOperand(base, offset, UXTW, 1)); |
| __ ComputeAddress(x11, MemOperand(base, offset, SXTW, 2)); |
| __ ComputeAddress(x12, MemOperand(base, offset, UXTW, 3)); |
| |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(base_address, base); |
| |
| ASSERT_EQUAL_64(INT64_C(0x123000000abc), x2); |
| ASSERT_EQUAL_64(INT64_C(0x123000000ac4), x3); |
| ASSERT_EQUAL_64(INT64_C(0x123000000a58), x4); |
| |
| ASSERT_EQUAL_64(INT64_C(0x124087654ddd), x5); |
| ASSERT_EQUAL_64(INT64_C(0x12721d951740), x6); |
| ASSERT_EQUAL_64(INT64_C(0x133876543ccc), x7); |
| ASSERT_EQUAL_64(INT64_C(0x22b765432bbc), x8); |
| |
| ASSERT_EQUAL_64(INT64_C(0x122f87654ddd), x9); |
| ASSERT_EQUAL_64(INT64_C(0x12310eca90fe), x10); |
| ASSERT_EQUAL_64(INT64_C(0x122e1d951740), x11); |
| ASSERT_EQUAL_64(INT64_C(0x12343b2a23c4), x12); |
| } |
| } |
| |
| |
| TEST(far_branch_backward) { |
| // Test that the MacroAssembler correctly resolves backward branches to labels |
| // that are outside the immediate range of branch instructions. |
| // Take into account that backward branches can reach one instruction further |
| // than forward branches. |
| const int overflow_size = |
| kInstructionSize + |
| std::max(Instruction::GetImmBranchForwardRange(TestBranchType), |
| std::max(Instruction::GetImmBranchForwardRange( |
| CompareBranchType), |
| Instruction::GetImmBranchForwardRange(CondBranchType))); |
| |
| SETUP(); |
| START(); |
| |
| Label done, fail; |
| Label test_tbz, test_cbz, test_bcond; |
| Label success_tbz, success_cbz, success_bcond; |
| |
| __ Mov(x0, 0); |
| __ Mov(x1, 1); |
| __ Mov(x10, 0); |
| |
| __ B(&test_tbz); |
| __ Bind(&success_tbz); |
| __ Orr(x0, x0, 1 << 0); |
| __ B(&test_cbz); |
| __ Bind(&success_cbz); |
| __ Orr(x0, x0, 1 << 1); |
| __ B(&test_bcond); |
| __ Bind(&success_bcond); |
| __ Orr(x0, x0, 1 << 2); |
| |
| __ B(&done); |
| |
| // Generate enough code to overflow the immediate range of the three types of |
| // branches below. |
| for (unsigned i = 0; i < overflow_size / kInstructionSize; ++i) { |
| if (i % 100 == 0) { |
| // If we do land in this code, we do not want to execute so many nops |
| // before reaching the end of test (especially if tracing is activated). |
| __ B(&fail); |
| } else { |
| __ Nop(); |
| } |
| } |
| __ B(&fail); |
| |
| __ Bind(&test_tbz); |
| __ Tbz(x10, 7, &success_tbz); |
| __ Bind(&test_cbz); |
| __ Cbz(x10, &success_cbz); |
| __ Bind(&test_bcond); |
| __ Cmp(x10, 0); |
| __ B(eq, &success_bcond); |
| |
| // For each out-of-range branch instructions, at least two instructions should |
| // have been generated. |
| VIXL_CHECK(masm.GetSizeOfCodeGeneratedSince(&test_tbz) >= |
| 7 * kInstructionSize); |
| |
| __ Bind(&fail); |
| __ Mov(x1, 0); |
| __ Bind(&done); |
| |
| END(); |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0x7, x0); |
| ASSERT_EQUAL_64(0x1, x1); |
| } |
| } |
| |
| |
| TEST(single_veneer) { |
| SETUP(); |
| START(); |
| |
| const int max_range = Instruction::GetImmBranchForwardRange(TestBranchType); |
| |
| Label success, fail, done; |
| |
| __ Mov(x0, 0); |
| __ Mov(x1, 1); |
| __ Mov(x10, 0); |
| |
| __ Tbz(x10, 7, &success); |
| |
| // Generate enough code to overflow the immediate range of the `tbz`. |
| for (unsigned i = 0; i < max_range / kInstructionSize + 1; ++i) { |
| if (i % 100 == 0) { |
| // If we do land in this code, we do not want to execute so many nops |
| // before reaching the end of test (especially if tracing is activated). |
| __ B(&fail); |
| } else { |
| __ Nop(); |
| } |
| } |
| __ B(&fail); |
| |
| __ Bind(&success); |
| __ Mov(x0, 1); |
| |
| __ B(&done); |
| __ Bind(&fail); |
| __ Mov(x1, 0); |
| __ Bind(&done); |
| |
| END(); |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(1, x0); |
| ASSERT_EQUAL_64(1, x1); |
| } |
| } |
| |
| |
| TEST(simple_veneers) { |
| // Test that the MacroAssembler correctly emits veneers for forward branches |
| // to labels that are outside the immediate range of branch instructions. |
| const int max_range = |
| std::max(Instruction::GetImmBranchForwardRange(TestBranchType), |
| std::max(Instruction::GetImmBranchForwardRange( |
| CompareBranchType), |
| Instruction::GetImmBranchForwardRange(CondBranchType))); |
| |
| SETUP(); |
| START(); |
| |
| Label done, fail; |
| Label test_tbz, test_cbz, test_bcond; |
| Label success_tbz, success_cbz, success_bcond; |
| |
| __ Mov(x0, 0); |
| __ Mov(x1, 1); |
| __ Mov(x10, 0); |
| |
| __ Bind(&test_tbz); |
| __ Tbz(x10, 7, &success_tbz); |
| __ Bind(&test_cbz); |
| __ Cbz(x10, &success_cbz); |
| __ Bind(&test_bcond); |
| __ Cmp(x10, 0); |
| __ B(eq, &success_bcond); |
| |
| // Generate enough code to overflow the immediate range of the three types of |
| // branches below. |
| for (unsigned i = 0; i < max_range / kInstructionSize + 1; ++i) { |
| if (i % 100 == 0) { |
| // If we do land in this code, we do not want to execute so many nops |
| // before reaching the end of test (especially if tracing is activated). |
| __ B(&fail); |
| } else { |
| __ Nop(); |
| } |
| } |
| __ B(&fail); |
| |
| __ Bind(&success_tbz); |
| __ Orr(x0, x0, 1 << 0); |
| __ B(&test_cbz); |
| __ Bind(&success_cbz); |
| __ Orr(x0, x0, 1 << 1); |
| __ B(&test_bcond); |
| __ Bind(&success_bcond); |
| __ Orr(x0, x0, 1 << 2); |
| |
| __ B(&done); |
| __ Bind(&fail); |
| __ Mov(x1, 0); |
| __ Bind(&done); |
| |
| END(); |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(0x7, x0); |
| ASSERT_EQUAL_64(0x1, x1); |
| } |
| } |
| |
| |
| TEST(veneers_stress) { |
| SETUP(); |
| START(); |
| |
| // This is a code generation test stressing the emission of veneers. The code |
| // generated is not executed. |
| |
| Label target; |
| const unsigned max_range = |
| Instruction::GetImmBranchForwardRange(CondBranchType); |
| const unsigned iterations = |
| (max_range + max_range / 4) / (4 * kInstructionSize); |
| for (unsigned i = 0; i < iterations; i++) { |
| __ B(&target); |
| __ B(eq, &target); |
| __ Cbz(x0, &target); |
| __ Tbz(x0, 0, &target); |
| } |
| __ Bind(&target); |
| |
| END(); |
| } |
| |
| |
| TEST(veneers_two_out_of_range) { |
| SETUP(); |
| START(); |
| |
| // This is a code generation test. The code generated is not executed. |
| // Ensure that the MacroAssembler considers unresolved branches to chose when |
| // a veneer pool should be emitted. We generate two branches that go out of |
| // range at the same offset. When the MacroAssembler decides to emit the |
| // veneer pool, the emission of a first veneer should not cause the other |
| // branch to go out of range. |
| |
| int range_cbz = Instruction::GetImmBranchForwardRange(CompareBranchType); |
| int range_tbz = Instruction::GetImmBranchForwardRange(TestBranchType); |
| int max_target = static_cast<int>(masm.GetCursorOffset()) + range_cbz; |
| |
| Label done; |
| |
| // We use different labels to prevent the MacroAssembler from sharing veneers. |
| Label target_cbz, target_tbz; |
| |
| __ Cbz(x0, &target_cbz); |
| while (masm.GetCursorOffset() < max_target - range_tbz) { |
| __ Nop(); |
| } |
| __ Tbz(x0, 0, &target_tbz); |
| while (masm.GetCursorOffset() < max_target) { |
| __ Nop(); |
| } |
| |
| // This additional nop makes the branches go out of range. |
| __ Nop(); |
| |
| __ Bind(&target_cbz); |
| __ Bind(&target_tbz); |
| |
| END(); |
| } |
| |
| |
| TEST(veneers_hanging) { |
| SETUP(); |
| START(); |
| |
| // This is a code generation test. The code generated is not executed. |
| // Ensure that the MacroAssembler considers unresolved branches to chose when |
| // a veneer pool should be emitted. This is similar to the |
| // 'veneers_two_out_of_range' test. We try to trigger the following situation: |
| // b.eq label |
| // b.eq label |
| // ... |
| // nop |
| // ... |
| // cbz x0, label |
| // cbz x0, label |
| // ... |
| // tbz x0, 0 label |
| // nop |
| // ... |
| // nop <- From here the `b.eq` and `cbz` instructions run out of range, |
| // so a literal pool is required. |
| // veneer |
| // veneer |
| // veneer <- The `tbz` runs out of range somewhere in the middle of the |
| // veneer veneer pool. |
| // veneer |
| |
| const int range_bcond = Instruction::GetImmBranchForwardRange(CondBranchType); |
| const int range_cbz = |
| Instruction::GetImmBranchForwardRange(CompareBranchType); |
| const int range_tbz = Instruction::GetImmBranchForwardRange(TestBranchType); |
| const int max_target = static_cast<int>(masm.GetCursorOffset()) + range_bcond; |
| |
| Label done; |
| const int n_bcond = 100; |
| const int n_cbz = 100; |
| const int n_tbz = 1; |
| const int kNTotalBranches = n_bcond + n_cbz + n_tbz; |
| |
| // We use different labels to prevent the MacroAssembler from sharing veneers. |
| Label labels[kNTotalBranches]; |
| for (int i = 0; i < kNTotalBranches; i++) { |
| new (&labels[i]) Label(); |
| } |
| |
| for (int i = 0; i < n_bcond; i++) { |
| __ B(eq, &labels[i]); |
| } |
| |
| while (masm.GetCursorOffset() < max_target - range_cbz) { |
| __ Nop(); |
| } |
| |
| for (int i = 0; i < n_cbz; i++) { |
| __ Cbz(x0, &labels[n_bcond + i]); |
| } |
| |
| // Ensure the 'tbz' will go out of range after some of the previously |
| // generated branches. |
| int margin = (n_bcond / 2) * kInstructionSize; |
| while (masm.GetCursorOffset() < max_target - range_tbz + margin) { |
| __ Nop(); |
| } |
| |
| __ Tbz(x0, 0, &labels[n_bcond + n_cbz]); |
| |
| while (masm.GetCursorOffset() < max_target) { |
| __ Nop(); |
| } |
| |
| // This additional nop makes the 'b.eq' and 'cbz' instructions go out of range |
| // and forces the emission of a veneer pool. The 'tbz' is not yet out of |
| // range, but will go out of range while veneers are emitted for the other |
| // branches. |
| // The MacroAssembler should ensure that veneers are correctly emitted for all |
| // the branches, including the 'tbz'. Checks will fail if the target of a |
| // branch is out of range. |
| __ Nop(); |
| |
| for (int i = 0; i < kNTotalBranches; i++) { |
| __ Bind(&labels[i]); |
| } |
| |
| END(); |
| } |
| |
| |
| TEST(collision_literal_veneer_pools) { |
| SETUP_WITH_FEATURES(CPUFeatures::kFP); |
| START(); |
| |
| // This is a code generation test. The code generated is not executed. |
| |
| // Make sure the literal pool is empty; |
| masm.EmitLiteralPool(LiteralPool::kBranchRequired); |
| ASSERT_LITERAL_POOL_SIZE(0); |
| |
| // We chose the offsets below to (try to) trigger the following situation: |
| // buffer offset |
| // 0: tbz x0, 0, target_tbz ----------------------------------. |
| // 4: nop | |
| // ... | |
| // nop | |
| // literal gen: ldr s0, [pc + ...] ; load from `pool start + 0` | |
| // ldr s0, [pc + ...] ; load from `pool start + 4` | |
| // ... | |
| // ldr s0, [pc + ...] | |
| // pool start: floating-point literal (0.1) | |
| // floating-point literal (1.1) | |
| // ... | |
| // floating-point literal (<n>.1) <-----tbz-max-range--' |
| // floating-point literal (<n+1>.1) |
| // ... |
| |
| const int range_tbz = Instruction::GetImmBranchForwardRange(TestBranchType); |
| const int max_target = static_cast<int>(masm.GetCursorOffset()) + range_tbz; |
| |
| const size_t target_literal_pool_size = 100 * kInstructionSize; |
| const int offset_start_literal_gen = |
| target_literal_pool_size + target_literal_pool_size / 2; |
| |
| |
| Label target_tbz; |
| |
| __ Tbz(x0, 0, &target_tbz); |
| VIXL_CHECK(masm.GetNumberOfPotentialVeneers() == 1); |
| while (masm.GetCursorOffset() < max_target - offset_start_literal_gen) { |
| __ Nop(); |
| } |
| VIXL_CHECK(masm.GetNumberOfPotentialVeneers() == 1); |
| |
| for (int i = 0; i < 100; i++) { |
| // Use a different value to force one literal pool entry per iteration. |
| __ Ldr(s0, i + 0.1); |
| } |
| VIXL_CHECK(masm.GetLiteralPoolSize() >= target_literal_pool_size); |
| |
| // Force emission of a literal pool. |
| masm.EmitLiteralPool(LiteralPool::kBranchRequired); |
| ASSERT_LITERAL_POOL_SIZE(0); |
| |
| // The branch should not have gone out of range during the emission of the |
| // literal pool. |
| __ Bind(&target_tbz); |
| |
| VIXL_CHECK(masm.GetNumberOfPotentialVeneers() == 0); |
| |
| END(); |
| } |
| |
| |
| TEST(ldr_literal_explicit) { |
| SETUP(); |
| |
| START(); |
| Literal<int64_t> automatically_placed_literal(1, masm.GetLiteralPool()); |
| Literal<int64_t> manually_placed_literal(2); |
| { |
| ExactAssemblyScope scope(&masm, kInstructionSize + sizeof(int64_t)); |
| Label over_literal; |
| __ b(&over_literal); |
| __ place(&manually_placed_literal); |
| __ bind(&over_literal); |
| } |
| __ Ldr(x1, &manually_placed_literal); |
| __ Ldr(x2, &automatically_placed_literal); |
| __ Add(x0, x1, x2); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(3, x0); |
| } |
| } |
| |
| |
| TEST(ldr_literal_automatically_placed) { |
| SETUP_WITH_FEATURES(CPUFeatures::kFP); |
| |
| START(); |
| |
| // We start with an empty literal pool. |
| ASSERT_LITERAL_POOL_SIZE(0); |
| |
| // Create a literal that should be placed by the literal pool. |
| Literal<int64_t> explicit_literal(2, masm.GetLiteralPool()); |
| // It should not appear in the literal pool until its first use. |
| ASSERT_LITERAL_POOL_SIZE(0); |
| |
| // Check that using standard literals does not break the use of explicitly |
| // created literals. |
| __ Ldr(d1, 1.1); |
| ASSERT_LITERAL_POOL_SIZE(8); |
| masm.EmitLiteralPool(LiteralPool::kBranchRequired); |
| ASSERT_LITERAL_POOL_SIZE(0); |
| |
| __ Ldr(x2, &explicit_literal); |
| ASSERT_LITERAL_POOL_SIZE(8); |
| masm.EmitLiteralPool(LiteralPool::kBranchRequired); |
| ASSERT_LITERAL_POOL_SIZE(0); |
| |
| __ Ldr(d3, 3.3); |
| ASSERT_LITERAL_POOL_SIZE(8); |
| masm.EmitLiteralPool(LiteralPool::kBranchRequired); |
| ASSERT_LITERAL_POOL_SIZE(0); |
| |
| // Re-use our explicitly created literal. It has already been placed, so it |
| // should not impact the literal pool. |
| __ Ldr(x4, &explicit_literal); |
| ASSERT_LITERAL_POOL_SIZE(0); |
| |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_FP64(1.1, d1); |
| ASSERT_EQUAL_64(2, x2); |
| ASSERT_EQUAL_FP64(3.3, d3); |
| ASSERT_EQUAL_64(2, x4); |
| } |
| } |
| |
| |
| TEST(literal_update_overwrite) { |
| SETUP(); |
| |
| START(); |
| |
| ASSERT_LITERAL_POOL_SIZE(0); |
| LiteralPool* literal_pool = masm.GetLiteralPool(); |
| |
| Literal<int32_t> lit_32_update_before_pool(0xbad, literal_pool); |
| Literal<int32_t> lit_32_update_after_pool(0xbad, literal_pool); |
| Literal<int64_t> lit_64_update_before_pool(0xbad, literal_pool); |
| Literal<int64_t> lit_64_update_after_pool(0xbad, literal_pool); |
| |
| ASSERT_LITERAL_POOL_SIZE(0); |
| |
| lit_32_update_before_pool.UpdateValue(32); |
| lit_64_update_before_pool.UpdateValue(64); |
| |
| __ Ldr(w1, &lit_32_update_before_pool); |
| __ Ldr(x2, &lit_64_update_before_pool); |
| __ Ldr(w3, &lit_32_update_after_pool); |
| __ Ldr(x4, &lit_64_update_after_pool); |
| |
| masm.EmitLiteralPool(LiteralPool::kBranchRequired); |
| |
| VIXL_ASSERT(lit_32_update_after_pool.IsPlaced()); |
| VIXL_ASSERT(lit_64_update_after_pool.IsPlaced()); |
| lit_32_update_after_pool.UpdateValue(128, &masm); |
| lit_64_update_after_pool.UpdateValue(256, &masm); |
| |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(32, x1); |
| ASSERT_EQUAL_64(64, x2); |
| ASSERT_EQUAL_64(128, x3); |
| ASSERT_EQUAL_64(256, x4); |
| } |
| } |
| |
| |
| TEST(literal_deletion_policies) { |
| SETUP(); |
| |
| START(); |
| |
| // We cannot check exactly when the deletion of the literals occur, but we |
| // check that usage of the deletion policies is not broken. |
| |
| ASSERT_LITERAL_POOL_SIZE(0); |
| LiteralPool* literal_pool = masm.GetLiteralPool(); |
| |
| Literal<int32_t> lit_manual(0xbad, literal_pool); |
| Literal<int32_t>* lit_deleted_on_placement = |
| new Literal<int32_t>(0xbad, |
| literal_pool, |
| RawLiteral::kDeletedOnPlacementByPool); |
| Literal<int32_t>* lit_deleted_on_pool_destruction = |
| new Literal<int32_t>(0xbad, |
| literal_pool, |
| RawLiteral::kDeletedOnPoolDestruction); |
| |
| ASSERT_LITERAL_POOL_SIZE(0); |
| |
| lit_manual.UpdateValue(32); |
| lit_deleted_on_placement->UpdateValue(64); |
| |
| __ Ldr(w1, &lit_manual); |
| __ Ldr(w2, lit_deleted_on_placement); |
| __ Ldr(w3, lit_deleted_on_pool_destruction); |
| |
| masm.EmitLiteralPool(LiteralPool::kBranchRequired); |
| |
| VIXL_ASSERT(lit_manual.IsPlaced()); |
| VIXL_ASSERT(lit_deleted_on_pool_destruction->IsPlaced()); |
| lit_deleted_on_pool_destruction->UpdateValue(128, &masm); |
| |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(32, x1); |
| ASSERT_EQUAL_64(64, x2); |
| ASSERT_EQUAL_64(128, x3); |
| } |
| } |
| |
| |
| TEST(generic_operand) { |
| SETUP_WITH_FEATURES(CPUFeatures::kFP); |
| |
| int32_t data_32_array[5] = {0xbadbeef, |
| 0x11111111, |
| 0xbadbeef, |
| 0x33333333, |
| 0xbadbeef}; |
| int64_t data_64_array[5] = {INT64_C(0xbadbadbadbeef), |
| INT64_C(0x1111111111111111), |
| INT64_C(0xbadbadbadbeef), |
| INT64_C(0x3333333333333333), |
| INT64_C(0xbadbadbadbeef)}; |
| size_t size_32 = sizeof(data_32_array[0]); |
| size_t size_64 = sizeof(data_64_array[0]); |
| |
| START(); |
| |
| intptr_t data_32_address = reinterpret_cast<intptr_t>(&data_32_array[0]); |
| intptr_t data_64_address = reinterpret_cast<intptr_t>(&data_64_array[0]); |
| Register data_32 = x27; |
| Register data_64 = x28; |
| __ Mov(data_32, data_32_address); |
| __ Mov(data_64, data_64_address); |
| |
| __ Move(GenericOperand(w0), |
| GenericOperand(MemOperand(data_32, 1 * size_32), size_32)); |
| __ Move(GenericOperand(s0), |
| GenericOperand(MemOperand(data_32, 3 * size_32), size_32)); |
| __ Move(GenericOperand(x10), |
| GenericOperand(MemOperand(data_64, 1 * size_64), size_64)); |
| __ Move(GenericOperand(d10), |
| GenericOperand(MemOperand(data_64, 3 * size_64), size_64)); |
| |
| __ Move(GenericOperand(w1), GenericOperand(w0)); |
| __ Move(GenericOperand(s1), GenericOperand(s0)); |
| __ Move(GenericOperand(x11), GenericOperand(x10)); |
| __ Move(GenericOperand(d11), GenericOperand(d10)); |
| |
| __ Move(GenericOperand(MemOperand(data_32, 0 * size_32), size_32), |
| GenericOperand(w1)); |
| __ Move(GenericOperand(MemOperand(data_32, 2 * size_32), size_32), |
| GenericOperand(s1)); |
| __ Move(GenericOperand(MemOperand(data_64, 0 * size_64), size_64), |
| GenericOperand(x11)); |
| __ Move(GenericOperand(MemOperand(data_64, 2 * size_64), size_64), |
| GenericOperand(d11)); |
| |
| __ Move(GenericOperand(MemOperand(data_32, 4 * size_32), size_32), |
| GenericOperand(MemOperand(data_32, 0 * size_32), size_32)); |
| __ Move(GenericOperand(MemOperand(data_64, 4 * size_64), size_64), |
| GenericOperand(MemOperand(data_64, 0 * size_64), size_64)); |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_64(data_32_address, data_32); |
| ASSERT_EQUAL_64(data_64_address, data_64); |
| |
| ASSERT_EQUAL_32(0x11111111, w0); |
| ASSERT_EQUAL_32(0x33333333, core.sreg_bits(0)); |
| ASSERT_EQUAL_64(INT64_C(0x1111111111111111), x10); |
| ASSERT_EQUAL_64(INT64_C(0x3333333333333333), core.dreg_bits(10)); |
| |
| ASSERT_EQUAL_32(0x11111111, w1); |
| ASSERT_EQUAL_32(0x33333333, core.sreg_bits(1)); |
| ASSERT_EQUAL_64(INT64_C(0x1111111111111111), x11); |
| ASSERT_EQUAL_64(INT64_C(0x3333333333333333), core.dreg_bits(11)); |
| |
| VIXL_CHECK(data_32_array[0] == 0x11111111); |
| VIXL_CHECK(data_32_array[1] == 0x11111111); |
| VIXL_CHECK(data_32_array[2] == 0x33333333); |
| VIXL_CHECK(data_32_array[3] == 0x33333333); |
| VIXL_CHECK(data_32_array[4] == 0x11111111); |
| |
| VIXL_CHECK(data_64_array[0] == INT64_C(0x1111111111111111)); |
| VIXL_CHECK(data_64_array[1] == INT64_C(0x1111111111111111)); |
| VIXL_CHECK(data_64_array[2] == INT64_C(0x3333333333333333)); |
| VIXL_CHECK(data_64_array[3] == INT64_C(0x3333333333333333)); |
| VIXL_CHECK(data_64_array[4] == INT64_C(0x1111111111111111)); |
| } |
| } |
| |
| |
| // Test feature detection of calls to runtime functions. |
| |
| // C++11 should be sufficient to provide simulated runtime calls, except for a |
| // GCC bug before 4.9.1. |
| #if defined(VIXL_INCLUDE_SIMULATOR_AARCH64) && (__cplusplus >= 201103L) && \ |
| (defined(__clang__) || GCC_VERSION_OR_NEWER(4, 9, 1)) && \ |
| !defined(VIXL_HAS_SIMULATED_RUNTIME_CALL_SUPPORT) |
| #error \ |
| "C++11 should be sufficient to provide support for simulated runtime calls." |
| #endif // #if defined(VIXL_INCLUDE_SIMULATOR_AARCH64) && ... |
| |
| #if (__cplusplus >= 201103L) && \ |
| !defined(VIXL_HAS_MACROASSEMBLER_RUNTIME_CALL_SUPPORT) |
| #error \ |
| "C++11 should be sufficient to provide support for `MacroAssembler::CallRuntime()`." |
| #endif // #if (__cplusplus >= 201103L) && ... |
| |
| #ifdef VIXL_HAS_MACROASSEMBLER_RUNTIME_CALL_SUPPORT |
| int32_t runtime_call_add_one(int32_t a) { return a + 1; } |
| |
| double runtime_call_add_doubles(double a, double b, double c) { |
| return a + b + c; |
| } |
| |
| int64_t runtime_call_one_argument_on_stack(int64_t arg1 __attribute__((unused)), |
| int64_t arg2 __attribute__((unused)), |
| int64_t arg3 __attribute__((unused)), |
| int64_t arg4 __attribute__((unused)), |
| int64_t arg5 __attribute__((unused)), |
| int64_t arg6 __attribute__((unused)), |
| int64_t arg7 __attribute__((unused)), |
| int64_t arg8 __attribute__((unused)), |
| int64_t arg9) { |
| return arg9; |
| } |
| |
| double runtime_call_two_arguments_on_stack(int64_t arg1 __attribute__((unused)), |
| int64_t arg2 __attribute__((unused)), |
| int64_t arg3 __attribute__((unused)), |
| int64_t arg4 __attribute__((unused)), |
| int64_t arg5 __attribute__((unused)), |
| int64_t arg6 __attribute__((unused)), |
| int64_t arg7 __attribute__((unused)), |
| int64_t arg8 __attribute__((unused)), |
| double arg9, |
| double arg10) { |
| return arg9 - arg10; |
| } |
| |
| void runtime_call_store_at_address(int64_t* address) { *address = 0xf00d; } |
| |
| enum RuntimeCallTestEnum { Enum0 }; |
| |
| RuntimeCallTestEnum runtime_call_enum(RuntimeCallTestEnum e) { return e; } |
| |
| enum class RuntimeCallTestEnumClass { Enum0 }; |
| |
| RuntimeCallTestEnumClass runtime_call_enum_class(RuntimeCallTestEnumClass e) { |
| return e; |
| } |
| |
| int8_t test_int8_t(int8_t x) { return x; } |
| uint8_t test_uint8_t(uint8_t x) { return x; } |
| int16_t test_int16_t(int16_t x) { return x; } |
| uint16_t test_uint16_t(uint16_t x) { return x; } |
| |
| TEST(runtime_calls) { |
| SETUP_WITH_FEATURES(CPUFeatures::kFP); |
| |
| #ifndef VIXL_HAS_SIMULATED_RUNTIME_CALL_SUPPORT |
| if (masm.GenerateSimulatorCode()) { |
| // This configuration is unsupported and a `VIXL_UNREACHABLE()` would fire |
| // while trying to generate `CallRuntime`. This configuration should only be |
| // reachable with C++11 and a (buggy) version of GCC pre-4.9.1. |
| return; |
| } |
| #endif |
| |
| START(); |
| |
| // Test `CallRuntime`. |
| |
| __ Mov(w0, 0); |
| __ CallRuntime(runtime_call_add_one); |
| __ Mov(w20, w0); |
| |
| __ Fmov(d0, 0.0); |
| __ Fmov(d1, 1.5); |
| __ Fmov(d2, 2.5); |
| __ CallRuntime(runtime_call_add_doubles); |
| __ Fmov(d20, d0); |
| |
| __ Mov(x0, 0x123); |
| __ Push(x0, x0); |
| __ CallRuntime(runtime_call_one_argument_on_stack); |
| __ Mov(x21, x0); |
| __ Pop(x0, x1); |
| |
| __ Fmov(d0, 314.0); |
| __ Fmov(d1, 4.0); |
| __ Push(d1, d0); |
| __ CallRuntime(runtime_call_two_arguments_on_stack); |
| __ Fmov(d21, d0); |
| __ Pop(d1, d0); |
| |
| // Test that the template mechanisms don't break with enums. |
| __ Mov(w0, 0); |
| __ CallRuntime(runtime_call_enum); |
| __ Mov(w0, 0); |
| __ CallRuntime(runtime_call_enum_class); |
| |
| // Test `TailCallRuntime`. |
| |
| Label function, after_function; |
| __ B(&after_function); |
| __ Bind(&function); |
| __ Mov(x22, 0); |
| __ Mov(w0, 123); |
| __ TailCallRuntime(runtime_call_add_one); |
| // Control should not fall through. |
| __ Mov(x22, 0xbad); |
| __ Ret(); |
| __ Bind(&after_function); |
| |
| // Call our dummy function, taking care to preserve the link register. |
| __ Push(ip0, lr); |
| __ Bl(&function); |
| __ Pop(lr, ip0); |
| // Save the result. |
| __ Mov(w23, w0); |
| |
| __ Mov(x24, 0); |
| int test_values[] = {static_cast<int8_t>(-1), |
| static_cast<uint8_t>(-1), |
| static_cast<int16_t>(-1), |
| static_cast<uint16_t>(-1), |
| -256, |
| -1, |
| 0, |
| 1, |
| 256}; |
| for (size_t i = 0; i < sizeof(test_values) / sizeof(test_values[0]); ++i) { |
| Label pass_int8, pass_uint8, pass_int16, pass_uint16; |
| int x = test_values[i]; |
| __ Mov(w0, x); |
| __ CallRuntime(test_int8_t); |
| __ Sxtb(w0, w0); |
| __ Cmp(w0, ExtractSignedBitfield32(7, 0, x)); |
| __ Cinc(x24, x24, ne); |
| __ Mov(w0, x); |
| __ CallRuntime(test_uint8_t); |
| __ Uxtb(w0, w0); |
| __ Cmp(w0, ExtractUnsignedBitfield32(7, 0, x)); |
| __ Cinc(x24, x24, ne); |
| __ Mov(w0, x); |
| __ CallRuntime(test_int16_t); |
| __ Sxth(w0, w0); |
| __ Cmp(w0, ExtractSignedBitfield32(15, 0, x)); |
| __ Cinc(x24, x24, ne); |
| __ Mov(w0, x); |
| __ CallRuntime(test_uint16_t); |
| __ Uxth(w0, w0); |
| __ Cmp(w0, ExtractUnsignedBitfield32(15, 0, x)); |
| __ Cinc(x24, x24, ne); |
| } |
| |
| |
| int64_t value = 0xbadbeef; |
| __ Mov(x0, reinterpret_cast<uint64_t>(&value)); |
| __ CallRuntime(runtime_call_store_at_address); |
| |
| END(); |
| |
| #if defined(VIXL_HAS_SIMULATED_RUNTIME_CALL_SUPPORT) || \ |
| !defined(VIXL_INCLUDE_SIMULATOR_AARCH64) |
| if (CAN_RUN()) { |
| RUN(); |
| |
| ASSERT_EQUAL_32(1, w20); |
| ASSERT_EQUAL_FP64(4.0, d20); |
| ASSERT_EQUAL_64(0x123, x21); |
| ASSERT_EQUAL_FP64(310.0, d21); |
| VIXL_CHECK(value == 0xf00d); |
| ASSERT_EQUAL_64(0, x22); |
| ASSERT_EQUAL_32(124, w23); |
| ASSERT_EQUAL_64(0, x24); |
| } |
| #endif // #if defined(VIXL_HAS_SIMULATED_RUNTIME_CALL_SUPPORT) || ... |
| } |
| #endif // #ifdef VIXL_HAS_MACROASSEMBLER_RUNTIME_CALL_SUPPORT |
| |
| |
| TEST(optimised_mov_register) { |
| SETUP(); |
| |
| START(); |
| Label start; |
| __ Bind(&start); |
| __ Mov(x0, x0); |
| VIXL_CHECK(masm.GetSizeOfCodeGeneratedSince(&start) == 0); |
| __ Mov(w0, w0, kDiscardForSameWReg); |
| VIXL_CHECK(masm.GetSizeOfCodeGeneratedSince(&start) == 0); |
| __ Mov(w0, w0); |
| VIXL_CHECK(masm.GetSizeOfCodeGeneratedSince(&start) == kInstructionSize); |
| |
| END(); |
| |
| if (CAN_RUN()) { |
| RUN(); |
| } |
| } |
| |
| |
| TEST(nop) { |
| MacroAssembler masm; |
| |
| Label start; |
| __ Bind(&start); |
| __ Nop(); |
| // `MacroAssembler::Nop` must generate at least one nop. |
| VIXL_CHECK(masm.GetSizeOfCodeGeneratedSince(&start) >= kInstructionSize); |
| |
| masm.FinalizeCode(); |
| } |
| |
| TEST(scratch_scope_basic_v) { |
| MacroAssembler masm; |
| |
| { |
| UseScratchRegisterScope temps(&masm); |
| VRegister temp = temps.AcquireVRegisterOfSize(kQRegSize); |
| VIXL_CHECK(temp.Aliases(v31)); |
| } |
| { |
| UseScratchRegisterScope temps(&masm); |
| VRegister temp = temps.AcquireVRegisterOfSize(kDRegSize); |
| VIXL_CHECK(temp.Aliases(v31)); |
| } |
| { |
| UseScratchRegisterScope temps(&masm); |
| VRegister temp = temps.AcquireVRegisterOfSize(kSRegSize); |
| VIXL_CHECK(temp.Aliases(v31)); |
| } |
| } |
| |
| #ifdef VIXL_INCLUDE_SIMULATOR_AARCH64 |
| // Test the pseudo-instructions that control CPUFeatures dynamically in the |
| // Simulator. These are used by the test infrastructure itself, but in a fairly |
| // limited way. |
| |
| static void RunHelperWithFeatureCombinations( |
| void (*helper)(const CPUFeatures& base, const CPUFeatures& f)) { |
| // Iterate, testing the first n features in this list. |
| CPUFeatures::Feature features[] = { |
| // Put kNone first, so that the first iteration uses an empty feature set. |
| CPUFeatures::kNone, |
| // The remaining features used are arbitrary. |
| CPUFeatures::kIDRegisterEmulation, |
| CPUFeatures::kDCPoP, |
| CPUFeatures::kPAuth, |
| CPUFeatures::kFcma, |
| CPUFeatures::kAES, |
| CPUFeatures::kNEON, |
| CPUFeatures::kCRC32, |
| CPUFeatures::kFP, |
| CPUFeatures::kPmull1Q, |
| CPUFeatures::kSM4, |
| CPUFeatures::kSM3, |
| CPUFeatures::kDotProduct, |
| }; |
| VIXL_ASSERT(CPUFeatures(CPUFeatures::kNone) == CPUFeatures::None()); |
| // The features are not necessarily encoded in kInstructionSize-sized slots, |
| // so the MacroAssembler must pad the list to align the following instruction. |
| // Ensure that we have enough features in the list to cover all interesting |
| // alignment cases, even if the highest common factor of kInstructionSize and |
| // an encoded feature is one. |
| VIXL_STATIC_ASSERT(ARRAY_SIZE(features) > kInstructionSize); |
| |
| CPUFeatures base = CPUFeatures::None(); |
| for (size_t i = 0; i < ARRAY_SIZE(features); i++) { |
| base.Combine(features[i]); |
| CPUFeatures f = CPUFeatures::None(); |
| for (size_t j = 0; j < ARRAY_SIZE(features); j++) { |
| f.Combine(features[j]); |
| helper(base, f); |
| } |
| } |
| } |
| |
| static void SetSimulatorCPUFeaturesHelper(const CPUFeatures& base, |
| const CPUFeatures& f) { |
| SETUP_WITH_FEATURES(base); |
| START(); |
| |
| __ SetSimulatorCPUFeatures(f); |
| |
| END(); |
| if (CAN_RUN()) { |
| RUN_WITHOUT_SEEN_FEATURE_CHECK(); |
| VIXL_CHECK(*(simulator.GetCPUFeatures()) == f); |
| } |
| } |
| |
| TEST(configure_cpu_features_set) { |
| RunHelperWithFeatureCombinations(SetSimulatorCPUFeaturesHelper); |
| } |
| |
| static void EnableSimulatorCPUFeaturesHelper(const CPUFeatures& base, |
| const CPUFeatures& f) { |
| SETUP_WITH_FEATURES(base); |
| START(); |
| |
| __ EnableSimulatorCPUFeatures(f); |
| |
| END(); |
| if (CAN_RUN()) { |
| RUN_WITHOUT_SEEN_FEATURE_CHECK(); |
| VIXL_CHECK(*(simulator.GetCPUFeatures()) == base.With(f)); |
| } |
| } |
| |
| TEST(configure_cpu_features_enable) { |
| RunHelperWithFeatureCombinations(EnableSimulatorCPUFeaturesHelper); |
| } |
| |
| static void DisableSimulatorCPUFeaturesHelper(const CPUFeatures& base, |
| const CPUFeatures& f) { |
| SETUP_WITH_FEATURES(base); |
| START(); |
| |
| __ DisableSimulatorCPUFeatures(f); |
| |
| END(); |
| if (CAN_RUN()) { |
| RUN_WITHOUT_SEEN_FEATURE_CHECK(); |
| VIXL_CHECK(*(simulator.GetCPUFeatures()) == base.Without(f)); |
| } |
| } |
| |
| TEST(configure_cpu_features_disable) { |
| RunHelperWithFeatureCombinations(DisableSimulatorCPUFeaturesHelper); |
| } |
| |
| static void SaveRestoreSimulatorCPUFeaturesHelper(const CPUFeatures& base, |
| const CPUFeatures& f) { |
| SETUP_WITH_FEATURES(base); |
| START(); |
| |
| { |
| __ SaveSimulatorCPUFeatures(); |
| __ SetSimulatorCPUFeatures(f); |
| { |
| __ SaveSimulatorCPUFeatures(); |
| __ SetSimulatorCPUFeatures(CPUFeatures::All()); |
| __ RestoreSimulatorCPUFeatures(); |
| } |
| __ RestoreSimulatorCPUFeatures(); |
| } |
| |
| END(); |
| if (CAN_RUN()) { |
| RUN_WITHOUT_SEEN_FEATURE_CHECK(); |
| VIXL_CHECK(*(simulator.GetCPUFeatures()) == base); |
| } |
| } |
| |
| TEST(configure_cpu_features_save_restore) { |
| RunHelperWithFeatureCombinations(SaveRestoreSimulatorCPUFeaturesHelper); |
| } |
| |
| static void SimulationCPUFeaturesScopeHelper(const CPUFeatures& base, |
| const CPUFeatures& f) { |
| SETUP_WITH_FEATURES(base); |
| START(); |
| |
| { |
| SimulationCPUFeaturesScope scope_a(&masm, f); |
| { |
| SimulationCPUFeaturesScope scope_b(&masm, CPUFeatures::All()); |
| { |
| SimulationCPUFeaturesScope scope_c(&masm, CPUFeatures::None()); |
| // The scope arguments should combine with 'Enable', so we should be |
| // able to use any CPUFeatures here. |
| __ Fadd(v0.V4S(), v1.V4S(), v2.V4S()); // Requires {FP, NEON}. |
| } |
| } |
| } |
| |
| END(); |
| if (CAN_RUN()) { |
| RUN_WITHOUT_SEEN_FEATURE_CHECK(); |
| VIXL_CHECK(*(simulator.GetCPUFeatures()) == base); |
| } |
| } |
| |
| TEST(configure_cpu_features_scope) { |
| RunHelperWithFeatureCombinations(SimulationCPUFeaturesScopeHelper); |
| } |
| |
| #endif |
| |
| } // namespace aarch64 |
| } // namespace vixl |