aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPierre Langlois <pierre.langlois@arm.com>2017-02-24 17:53:36 +0000
committerPierre Langlois <pierre.langlois@arm.com>2017-02-24 18:10:56 +0000
commit94a02bb2b5f7c85dc5a3852aac8c6692f9c6c446 (patch)
tree78e5223a954c25447f6abb78edf7d88b643e6ef7
parent94ce7a99e8fad024fd8e17ada567eebe32431d64 (diff)
Add accidently removed tests
I accidently removed this file when regenerating test traces. Change-Id: Ia7db9159e8c22bb436fdaff8f9ab89a1468c02f3
-rw-r--r--test/aarch32/test-assembler-aarch32.cc6115
1 files changed, 6115 insertions, 0 deletions
diff --git a/test/aarch32/test-assembler-aarch32.cc b/test/aarch32/test-assembler-aarch32.cc
new file mode 100644
index 00000000..b871fd6f
--- /dev/null
+++ b/test/aarch32/test-assembler-aarch32.cc
@@ -0,0 +1,6115 @@
+// 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 <cstdio>
+#include <string>
+#include <iostream>
+
+#include "test-runner.h"
+#include "test-utils.h"
+#include "aarch32/test-utils-aarch32.h"
+
+#include "aarch32/macro-assembler-aarch32.h"
+#include "aarch32/disasm-aarch32.h"
+
+namespace vixl {
+namespace aarch32 {
+
+#define STRINGIFY(x) #x
+
+#ifdef VIXL_INCLUDE_TARGET_A32_ONLY
+#define TEST_T32(Name) \
+ void Test##Name##Impl(InstructionSet isa __attribute__((unused)))
+#else
+// Tests declared with this macro will only target T32.
+#define TEST_T32(Name) \
+ void Test##Name##Impl(InstructionSet isa); \
+ void Test##Name() { Test##Name##Impl(T32); } \
+ Test test_##Name(STRINGIFY(AARCH32_T32_##Name), &Test##Name); \
+ void Test##Name##Impl(InstructionSet isa __attribute__((unused)))
+#endif
+
+#ifdef VIXL_INCLUDE_TARGET_T32_ONLY
+#define TEST_A32(Name) \
+ void Test##Name##Impl(InstructionSet isa __attribute__((unused)))
+#else
+// Test declared with this macro will only target A32.
+#define TEST_A32(Name) \
+ void Test##Name##Impl(InstructionSet isa); \
+ void Test##Name() { Test##Name##Impl(A32); } \
+ Test test_##Name(STRINGIFY(AARCH32_A32_##Name), &Test##Name); \
+ void Test##Name##Impl(InstructionSet isa __attribute__((unused)))
+#endif
+
+// Tests declared with this macro will be run twice: once targeting A32 and
+// once targeting T32.
+#if defined(VIXL_INCLUDE_TARGET_A32_ONLY)
+#define TEST(Name) TEST_A32(Name)
+#elif defined(VIXL_INCLUDE_TARGET_T32_ONLY)
+#define TEST(Name) TEST_T32(Name)
+#else
+#define TEST(Name) \
+ void Test##Name##Impl(InstructionSet isa); \
+ void Test##Name() { \
+ Test##Name##Impl(A32); \
+ printf(" > A32 done\n"); \
+ Test##Name##Impl(T32); \
+ printf(" > T32 done\n"); \
+ } \
+ Test test_##Name(STRINGIFY(AARCH32_ASM_##Name), &Test##Name); \
+ void Test##Name##Impl(InstructionSet isa __attribute__((unused)))
+#endif
+
+// Tests declared with this macro are not expected to use any provided test
+// helpers such as SETUP, RUN, etc.
+#define TEST_NOASM(Name) \
+ void Test##Name(); \
+ Test test_##Name(STRINGIFY(AARCH32_##Name), &Test##Name); \
+ void Test##Name()
+
+#define __ masm.
+#define BUF_SIZE (4096)
+
+#define ASSERT_LITERAL_POOL_SIZE(size) \
+ do { \
+ VIXL_CHECK(__ GetLiteralPoolSize() == size); \
+ } while (false)
+
+#ifdef VIXL_INCLUDE_SIMULATOR_AARCH32
+// No simulator yet.
+
+#define SETUP() MacroAssembler masm(BUF_SIZE, isa);
+
+#define START() masm.GetBuffer()->Reset();
+
+#define END() \
+ __ Hlt(0); \
+ __ FinalizeCode();
+
+#define RUN() DISASSEMBLE();
+
+#define TEARDOWN()
+
+#else // ifdef VIXL_INCLUDE_SIMULATOR_AARCH32.
+
+#define SETUP() \
+ RegisterDump core; \
+ MacroAssembler masm(BUF_SIZE, isa); \
+ UseScratchRegisterScope harness_scratch(&masm); \
+ harness_scratch.ExcludeAll();
+
+#define START() \
+ masm.GetBuffer()->Reset(); \
+ __ Push(r4); \
+ __ Push(r5); \
+ __ Push(r6); \
+ __ Push(r7); \
+ __ Push(r8); \
+ __ Push(r9); \
+ __ Push(r10); \
+ __ Push(r11); \
+ __ Push(ip); \
+ __ Push(lr); \
+ __ Mov(r0, 0); \
+ __ Msr(APSR_nzcvq, r0); \
+ harness_scratch.Include(ip);
+
+#define END() \
+ harness_scratch.Exclude(ip); \
+ core.Dump(&masm); \
+ __ Pop(lr); \
+ __ Pop(ip); \
+ __ Pop(r11); \
+ __ Pop(r10); \
+ __ Pop(r9); \
+ __ Pop(r8); \
+ __ Pop(r7); \
+ __ Pop(r6); \
+ __ Pop(r5); \
+ __ Pop(r4); \
+ __ Bx(lr); \
+ __ FinalizeCode();
+
+// Execute the generated code from the MacroAssembler's automatic code buffer.
+// Note the offset for ExecuteMemory since the PCS requires that
+// the address be odd in the case of branching to T32 code.
+#define RUN() \
+ DISASSEMBLE(); \
+ { \
+ int pcs_offset = masm.IsUsingT32() ? 1 : 0; \
+ masm.GetBuffer()->SetExecutable(); \
+ ExecuteMemory(masm.GetBuffer()->GetStartAddress<byte*>(), \
+ masm.GetSizeOfCodeGenerated(), \
+ pcs_offset); \
+ masm.GetBuffer()->SetWritable(); \
+ }
+
+#define TEARDOWN() harness_scratch.Close();
+
+#endif // ifdef VIXL_INCLUDE_SIMULATOR_AARCH32
+
+#ifdef VIXL_INCLUDE_SIMULATOR_AARCH32
+// No simulator yet. We can't test the results.
+
+#define ASSERT_EQUAL_32(expected, result)
+
+#define ASSERT_EQUAL_64(expected, result)
+
+#define ASSERT_EQUAL_128(expected_h, expected_l, result)
+
+#define ASSERT_EQUAL_FP32(expected, result)
+
+#define ASSERT_EQUAL_FP64(expected, result)
+
+#define ASSERT_EQUAL_NZCV(expected)
+
+#else
+
+#define ASSERT_EQUAL_32(expected, result) \
+ VIXL_CHECK(Equal32(expected, &core, result))
+
+#define ASSERT_EQUAL_64(expected, result) \
+ VIXL_CHECK(Equal64(expected, &core, result))
+
+#define ASSERT_EQUAL_128(expected_h, expected_l, result) \
+ VIXL_CHECK(Equal128(expected_h, expected_l, &core, result))
+
+#define ASSERT_EQUAL_FP32(expected, result) \
+ VIXL_CHECK(EqualFP32(expected, &core, result))
+
+#define ASSERT_EQUAL_FP64(expected, result) \
+ VIXL_CHECK(EqualFP64(expected, &core, result))
+
+#define ASSERT_EQUAL_NZCV(expected) \
+ VIXL_CHECK(EqualNzcv(expected, core.flags_nzcv()))
+
+#endif
+
+#define DISASSEMBLE() \
+ if (Test::disassemble()) { \
+ PrintDisassembler dis(std::cout, 0); \
+ if (masm.IsUsingT32()) { \
+ dis.DisassembleT32Buffer(masm.GetBuffer()->GetStartAddress<uint16_t*>(), \
+ masm.GetCursorOffset()); \
+ } else { \
+ dis.DisassembleA32Buffer(masm.GetBuffer()->GetStartAddress<uint32_t*>(), \
+ masm.GetCursorOffset()); \
+ } \
+ }
+
+// TODO: Add SBC to the ADC tests.
+
+
+TEST(adc_shift) {
+ SETUP();
+
+ START();
+ // Initialize registers.
+ __ Mov(r0, 0);
+ __ Mov(r1, 1);
+ __ Mov(r2, 0x01234567);
+ __ Mov(r3, 0xfedcba98);
+
+ // Clear the C flag.
+ __ Adds(r0, r0, 0);
+
+ __ Adc(r4, r2, r3);
+ __ Adc(r5, r0, Operand(r1, LSL, 30));
+ __ Adc(r6, r0, Operand(r2, LSR, 16));
+ __ Adc(r7, r2, Operand(r3, ASR, 4));
+ __ Adc(r8, r2, Operand(r3, ROR, 8));
+ __ Adc(r9, r2, Operand(r3, RRX));
+ END();
+
+ RUN();
+
+ ASSERT_EQUAL_32(0xffffffff, r4);
+ ASSERT_EQUAL_32(INT32_C(1) << 30, r5);
+ ASSERT_EQUAL_32(0x00000123, r6);
+ ASSERT_EQUAL_32(0x01111110, r7);
+ ASSERT_EQUAL_32(0x9a222221, r8);
+ ASSERT_EQUAL_32(0x8091a2b3, r9);
+
+ START();
+ // Initialize registers.
+ __ Mov(r0, 0);
+ __ Mov(r1, 1);
+ __ Mov(r2, 0x01234567);
+ __ Mov(r3, 0xfedcba98);
+ __ Mov(r4, 0xffffffff);
+
+ // Set the C flag.
+ __ Adds(r0, r4, r1);
+
+ __ Adc(r5, r2, r3);
+ __ Adc(r6, r0, Operand(r1, LSL, 30));
+ __ Adc(r7, r0, Operand(r2, LSR, 16));
+ __ Adc(r8, r2, Operand(r3, ASR, 4));
+ __ Adc(r9, r2, Operand(r3, ROR, 8));
+ __ Adc(r10, r2, Operand(r3, RRX));
+ END();
+
+ RUN();
+
+ ASSERT_EQUAL_32(0xffffffff + 1, r5);
+ ASSERT_EQUAL_32((INT32_C(1) << 30) + 1, r6);
+ ASSERT_EQUAL_32(0x00000123 + 1, r7);
+ ASSERT_EQUAL_32(0x01111110 + 1, r8);
+ ASSERT_EQUAL_32(0x9a222221 + 1, r9);
+ ASSERT_EQUAL_32(0x0091a2b3 + 1, r10);
+
+ // Check that adc correctly sets the condition flags.
+ START();
+ __ Mov(r0, 0);
+ __ Mov(r1, 0xffffffff);
+ __ Mov(r2, 1);
+
+ // Clear the C flag.
+ __ Adds(r0, r0, 0);
+ __ Adcs(r3, r2, r1);
+ END();
+
+ RUN();
+
+ ASSERT_EQUAL_NZCV(ZCFlag);
+ ASSERT_EQUAL_32(0, r3);
+
+ START();
+ __ Mov(r0, 0);
+ __ Mov(r1, 0x80000000);
+ __ Mov(r2, 1);
+
+ // Clear the C flag.
+ __ Adds(r0, r0, 0);
+ __ Adcs(r3, r2, Operand(r1, ASR, 31));
+ END();
+
+ RUN();
+
+ ASSERT_EQUAL_NZCV(ZCFlag);
+ ASSERT_EQUAL_32(0, r3);
+
+ START();
+ __ Mov(r0, 0);
+ __ Mov(r1, 0x80000000);
+ __ Mov(r2, 0xffffffff);
+
+ // Clear the C flag.
+ __ Adds(r0, r0, 0);
+ __ Adcs(r3, r2, Operand(r1, LSR, 31));
+ END();
+
+ RUN();
+
+ ASSERT_EQUAL_NZCV(ZCFlag);
+ ASSERT_EQUAL_32(0, r3);
+
+ START();
+ __ Mov(r0, 0);
+ __ Mov(r1, 0x07ffffff);
+ __ Mov(r2, 0x10);
+
+ // Clear the C flag.
+ __ Adds(r0, r0, 0);
+ __ Adcs(r3, r2, Operand(r1, LSL, 4));
+ END();
+
+ RUN();
+
+ ASSERT_EQUAL_NZCV(NVFlag);
+ ASSERT_EQUAL_32(0x080000000, r3);
+
+ START();
+ __ Mov(r0, 0);
+ __ Mov(r1, 0xffffff00);
+ __ Mov(r2, 0xff000001);
+
+ // Clear the C flag.
+ __ Adds(r0, r0, 0);
+ __ Adcs(r3, r2, Operand(r1, ROR, 8));
+ END();
+
+ RUN();
+
+ ASSERT_EQUAL_NZCV(ZCFlag);
+ ASSERT_EQUAL_32(0, r3);
+
+ START();
+ __ Mov(r0, 0);
+ __ Mov(r1, 0xffffffff);
+ __ Mov(r2, 0x1);
+
+ // Clear the C flag, forcing RRX to insert 0 in r1's most significant bit.
+ __ Adds(r0, r0, 0);
+ __ Adcs(r3, r2, Operand(r1, RRX));
+ END();
+
+ RUN();
+
+ ASSERT_EQUAL_NZCV(NVFlag);
+ ASSERT_EQUAL_32(0x80000000, r3);
+
+ START();
+ __ Mov(r0, 0);
+ __ Mov(r1, 0xffffffff);
+ __ Mov(r2, 0x1);
+
+ // Set the C flag, forcing RRX to insert 1 in r1's most significant bit.
+ __ Adds(r0, r1, r2);
+ __ Adcs(r3, r2, Operand(r1, RRX));
+ END();
+
+ RUN();
+
+ ASSERT_EQUAL_NZCV(CFlag);
+ ASSERT_EQUAL_32(1, r3);
+
+ TEARDOWN();
+}
+
+
+TEST(adc_wide_imm) {
+ SETUP();
+
+ START();
+ __ Mov(r0, 0);
+
+ // Clear the C flag.
+ __ Adds(r0, r0, 0);
+
+ __ Adc(r1, r0, 0x12345678);
+ __ Adc(r2, r0, 0xffffffff);
+
+ // Set the C flag.
+ __ Cmp(r0, r0);
+
+ __ Adc(r3, r0, 0x12345678);
+ __ Adc(r4, r0, 0xffffffff);
+ END();
+
+ RUN();
+
+ ASSERT_EQUAL_32(0x12345678, r1);
+ ASSERT_EQUAL_32(0xffffffff, r2);
+ ASSERT_EQUAL_32(0x12345678 + 1, r3);
+ ASSERT_EQUAL_32(0, r4);
+
+ TEARDOWN();
+}
+
+
+// TODO: Add SUB tests to the ADD tests.
+
+
+TEST(add_imm) {
+ SETUP();
+
+ START();
+ __ Mov(r0, 0);
+ __ Mov(r1, 0x1111);
+ __ Mov(r2, 0xffffffff);
+ __ Mov(r3, 0x80000000);
+
+ __ Add(r4, r0, 0x12);
+ __ Add(r5, r1, 0x120000);
+ __ Add(r6, r0, 0xab << 12);
+ __ Add(r7, r2, 1);
+
+ END();
+
+ RUN();
+
+ ASSERT_EQUAL_32(0x12, r4);
+ ASSERT_EQUAL_32(0x121111, r5);
+ ASSERT_EQUAL_32(0xab000, r6);
+ ASSERT_EQUAL_32(0x0, r7);
+
+ TEARDOWN();
+}
+
+
+TEST(add_wide_imm) {
+ SETUP();
+
+ START();
+ __ Mov(r0, 0);
+ __ Mov(r1, 1);
+
+ __ Add(r2, r0, 0x12345678);
+ __ Add(r3, r1, 0xffff);
+ END();
+
+ RUN();
+
+ ASSERT_EQUAL_32(0x12345678, r2);
+ ASSERT_EQUAL_32(0x00010000, r3);
+
+ TEARDOWN();
+}
+
+
+TEST(add_shifted) {
+ SETUP();
+
+ START();
+ __ Mov(r0, 0);
+ __ Mov(r1, 0x01234567);
+ __ Mov(r2, 0x76543210);
+ __ Mov(r3, 0xffffffff);
+
+ __ Add(r4, r1, r2);
+ __ Add(r5, r0, Operand(r1, LSL, 8));
+ __ Add(r6, r0, Operand(r1, LSR, 8));
+ __ Add(r7, r0, Operand(r1, ASR, 8));
+ __ Add(r8, r3, Operand(r1, ROR, 8));
+
+ // Set the C flag.
+ __ Adds(r0, r3, 1);
+ __ Add(r9, r3, Operand(r1, RRX));
+
+ // Clear the C flag.
+ __ Adds(r0, r0, 0);
+ __ Add(r10, r3, Operand(r1, RRX));
+
+ END();
+
+ RUN();
+
+ ASSERT_EQUAL_32(0x77777777, r4);
+ ASSERT_EQUAL_32(0x23456700, r5);
+ ASSERT_EQUAL_32(0x00012345, r6);
+ ASSERT_EQUAL_32(0x00012345, r7);
+ ASSERT_EQUAL_32(0x67012344, r8);
+ ASSERT_EQUAL_32(0x8091a2b2, r9);
+ ASSERT_EQUAL_32(0x0091a2b2, r10);
+
+ TEARDOWN();
+}
+
+
+TEST(and_) {
+ SETUP();
+
+ START();
+ __ Mov(r0, 0x0000fff0);
+ __ Mov(r1, 0xf00000ff);
+ __ Mov(r2, 0xffffffff);
+
+ __ And(r3, r0, r1);
+ __ And(r4, r0, Operand(r1, LSL, 4));
+ __ And(r5, r0, Operand(r1, LSR, 1));
+ __ And(r6, r0, Operand(r1, ASR, 20));
+ __ And(r7, r0, Operand(r1, ROR, 28));
+ __ And(r8, r0, 0xff);
+
+ // Set the C flag.
+ __ Adds(r9, r2, 1);
+ __ And(r9, r1, Operand(r1, RRX));
+
+ // Clear the C flag.
+ __ Adds(r10, r0, 0);
+ __ And(r10, r1, Operand(r1, RRX));
+ END();
+
+ RUN();
+
+ ASSERT_EQUAL_32(0x000000f0, r3);
+ ASSERT_EQUAL_32(0x00000ff0, r4);
+ ASSERT_EQUAL_32(0x00000070, r5);
+ ASSERT_EQUAL_32(0x0000ff00, r6);
+ ASSERT_EQUAL_32(0x00000ff0, r7);
+ ASSERT_EQUAL_32(0x000000f0, r8);
+ ASSERT_EQUAL_32(0xf000007f, r9);
+ ASSERT_EQUAL_32(0x7000007f, r10);
+
+ TEARDOWN();
+}
+
+
+TEST(ands) {
+ SETUP();
+
+ START();
+ __ Mov(r0, 0);
+ __ Mov(r1, 0xf00000ff);
+
+ __ Ands(r0, r1, r1);
+ END();
+
+ RUN();
+
+ ASSERT_EQUAL_NZCV(NFlag);
+ ASSERT_EQUAL_32(0xf00000ff, r0);
+
+ START();
+ __ Mov(r0, 0x00fff000);
+ __ Mov(r1, 0xf00000ff);
+
+ __ Ands(r0, r0, Operand(r1, LSL, 4));
+ END();
+
+ RUN();
+
+ ASSERT_EQUAL_NZCV(ZCFlag);
+ ASSERT_EQUAL_32(0x00000000, r0);
+
+ START();
+ __ Mov(r0, 0x0000fff0);
+ __ Mov(r1, 0xf00000ff);
+
+ __ Ands(r0, r0, Operand(r1, LSR, 4));
+ END();
+
+ RUN();
+
+ ASSERT_EQUAL_NZCV(ZCFlag);
+ ASSERT_EQUAL_32(0x00000000, r0);
+
+ START();
+ __ Mov(r0, 0xf000fff0);
+ __ Mov(r1, 0xf00000ff);
+
+ __ Ands(r0, r0, Operand(r1, ASR, 4));
+ END();
+
+ RUN();
+
+ ASSERT_EQUAL_NZCV(NCFlag);
+ ASSERT_EQUAL_32(0xf0000000, r0);
+
+ START();
+ __ Mov(r0, 0x80000000);
+ __ Mov(r1, 0x00000001);
+
+ __ Ands(r0, r0, Operand(r1, ROR, 1));
+ END();
+
+ RUN();
+
+ ASSERT_EQUAL_NZCV(NCFlag);
+ ASSERT_EQUAL_32(0x80000000, r0);
+
+ START();
+ __ Mov(r0, 0x80000000);
+ __ Mov(r1, 0x80000001);
+
+ // Clear the C flag, forcing RRX to insert 0 in r1's most significant bit.
+ __ Adds(r2, r0, 0);
+ __ Ands(r2, r0, Operand(r1, RRX));
+ END();
+
+ RUN();
+
+ ASSERT_EQUAL_NZCV(ZCFlag);
+ ASSERT_EQUAL_32(0, r2);
+
+ START();
+ __ Mov(r0, 0x80000000);
+ __ Mov(r1, 0x80000001);
+ __ Mov(r2, 0xffffffff);
+
+ // Set the C flag, forcing RRX to insert 1 in r1's most significant bit.
+ __ Adds(r2, r2, 1);
+ __ Ands(r2, r0, Operand(r1, RRX));
+ END();
+
+ RUN();
+
+ ASSERT_EQUAL_NZCV(NCFlag);
+ ASSERT_EQUAL_32(0x80000000, r2);
+
+ START();
+ __ Mov(r0, 0xfff0);
+
+ __ Ands(r0, r0, 0xf);
+ END();
+
+ RUN();
+
+ ASSERT_EQUAL_NZCV(ZFlag);
+ ASSERT_EQUAL_32(0x00000000, r0);
+
+ START();
+ __ Mov(r0, 0xff000000);
+
+ __ Ands(r0, r0, 0x80000000);
+ END();
+
+ RUN();
+
+ ASSERT_EQUAL_NZCV(NCFlag);
+ ASSERT_EQUAL_32(0x80000000, r0);
+
+ TEARDOWN();
+}
+
+
+TEST(adr_in_range) {
+ SETUP();
+
+ Label label_1, label_2, label_3, label_4;
+
+ START();
+ {
+ size_t size_of_generated_code;
+ if (masm.IsUsingA32()) {
+ size_of_generated_code = 18 * kA32InstructionSizeInBytes;
+ } else {
+ size_of_generated_code = 18 * k32BitT32InstructionSizeInBytes +
+ 3 * k16BitT32InstructionSizeInBytes;
+ }
+ ExactAssemblyScope scope(&masm,
+ size_of_generated_code,
+ ExactAssemblyScope::kExactSize);
+
+ __ mov(r0, 0x0); // Set to zero to indicate success.
+ __ adr(r1, &label_3);
+
+ __ adr(r2, &label_1); // Multiple forward references to the same label.
+ __ adr(r3, &label_1);
+ __ adr(r4, &label_1);
+
+ __ bind(&label_2);
+ __ eor(r5, r2, r3); // Ensure that r2, r3 and r4 are identical.
+ __ eor(r6, r2, r4);
+ __ orr(r0, r5, r6);
+ if (masm.IsUsingT32()) {
+ // The jump target needs to have its least significant bit set to indicate
+ // that we are jumping into thumb mode.
+ __ orr(r2, r2, 1);
+ }
+ __ bx(r2); // label_1, label_3
+
+ __ bind(&label_3);
+ __ adr(r2, &label_3); // Self-reference (offset 0).
+ __ eor(r1, r1, r2);
+ __ adr(r2, &label_4); // Simple forward reference.
+ if (masm.IsUsingT32()) {
+ // The jump target needs to have its least significant bit set to indicate
+ // that we are jumping into thumb mode.
+ __ orr(r2, r2, 1);
+ }
+ __ bx(r2); // label_4
+
+ __ bind(&label_1);
+ __ adr(r2, &label_3); // Multiple reverse references to the same label.
+ __ adr(r3, &label_3);
+ __ adr(r4, &label_3);
+ __ adr(r5, &label_2); // Simple reverse reference.
+ if (masm.IsUsingT32()) {
+ // The jump target needs to have its least significant bit set to indicate
+ // that we are jumping into thumb mode.
+ __ orr(r5, r5, 1);
+ }
+ __ bx(r5); // label_2
+
+ __ bind(&label_4);
+ }
+ END();
+
+ RUN();
+
+ ASSERT_EQUAL_32(0x0, r0);
+ ASSERT_EQUAL_32(0x0, r1);
+
+ TEARDOWN();
+}
+
+
+TEST(shift_imm) {
+ SETUP();
+
+ START();
+ __ Mov(r0, 0);
+ __ Mov(r1, 0xfedcba98);
+ __ Mov(r2, 0xffffffff);
+
+ __ Lsl(r3, r1, 4);
+ __ Lsr(r4, r1, 8);
+ __ Asr(r5, r1, 16);
+ __ Ror(r6, r1, 20);
+ END();
+
+ RUN();
+
+ ASSERT_EQUAL_32(0xedcba980, r3);
+ ASSERT_EQUAL_32(0x00fedcba, r4);
+ ASSERT_EQUAL_32(0xfffffedc, r5);
+ ASSERT_EQUAL_32(0xcba98fed, r6);
+
+ TEARDOWN();
+}
+
+
+TEST(shift_reg) {
+ SETUP();
+
+ START();
+ __ Mov(r0, 0);
+ __ Mov(r1, 0xfedcba98);
+ __ Mov(r2, 0xffffffff);
+
+ __ Add(r9, r0, 4);
+ __ Lsl(r3, r1, r9);
+
+ __ Add(r9, r0, 8);
+ __ Lsr(r4, r1, r9);
+
+ __ Add(r9, r0, 16);
+ __ Asr(r5, r1, r9);
+
+ __ Add(r9, r0, 20);
+ __ Ror(r6, r1, r9);
+
+ // Set the C flag.
+ __ Adds(r7, r2, 1);
+ __ Rrx(r7, r1);
+
+ // Clear the C flag.
+ __ Adds(r8, r0, 0);
+ __ Rrx(r8, r1);
+ END();
+
+ RUN();
+
+ ASSERT_EQUAL_32(0xedcba980, r3);
+ ASSERT_EQUAL_32(0x00fedcba, r4);
+ ASSERT_EQUAL_32(0xfffffedc, r5);
+ ASSERT_EQUAL_32(0xcba98fed, r6);
+ ASSERT_EQUAL_32(0xff6e5d4c, r7);
+ ASSERT_EQUAL_32(0x7f6e5d4c, r8);
+
+ TEARDOWN();
+}
+
+
+TEST(branch_cond) {
+ SETUP();
+
+ Label done, wrong;
+
+ START();
+ __ Mov(r0, 0x0);
+ __ Mov(r1, 0x1);
+ __ Mov(r2, 0x80000000);
+ // TODO: Use r0 instead of r3 when r0 becomes available.
+ __ Mov(r3, 0x1);
+
+ // For each 'cmp' instruction below, condition codes other than the ones
+ // following it would branch.
+
+ __ Cmp(r1, 0);
+ __ B(eq, &wrong);
+ __ B(lo, &wrong);
+ __ B(mi, &wrong);
+ __ B(vs, &wrong);
+ __ B(ls, &wrong);
+ __ B(lt, &wrong);
+ __ B(le, &wrong);
+ Label ok_1;
+ __ B(ne, &ok_1);
+ // TODO: Use __ Mov(r0, 0x0) instead.
+ __ Add(r3, r0, 0x0);
+ __ Bind(&ok_1);
+
+ __ Cmp(r1, 1);
+ __ B(ne, &wrong);
+ __ B(lo, &wrong);
+ __ B(mi, &wrong);
+ __ B(vs, &wrong);
+ __ B(hi, &wrong);
+ __ B(lt, &wrong);
+ __ B(gt, &wrong);
+ Label ok_2;
+ __ B(pl, &ok_2);
+ // TODO: Use __ Mov(r0, 0x0) instead.
+ __ Add(r3, r0, 0x0);
+ __ Bind(&ok_2);
+
+ __ Cmp(r1, 2);
+ __ B(eq, &wrong);
+ __ B(hs, &wrong);
+ __ B(pl, &wrong);
+ __ B(vs, &wrong);
+ __ B(hi, &wrong);
+ __ B(ge, &wrong);
+ __ B(gt, &wrong);
+ Label ok_3;
+ __ B(vc, &ok_3);
+ // TODO: Use __ Mov(r0, 0x0) instead.
+ __ Add(r3, r0, 0x0);
+ __ Bind(&ok_3);
+
+ __ Cmp(r2, 1);
+ __ B(eq, &wrong);
+ __ B(lo, &wrong);
+ __ B(mi, &wrong);
+ __ B(vc, &wrong);
+ __ B(ls, &wrong);
+ __ B(ge, &wrong);
+ __ B(gt, &wrong);
+ Label ok_4;
+ __ B(le, &ok_4);
+ // TODO: Use __ Mov(r0, 0x0) instead.
+ __ Add(r3, r0, 0x0);
+ __ Bind(&ok_4);
+
+ Label ok_5;
+ __ B(&ok_5);
+ // TODO: Use __ Mov(r0, 0x0) instead.
+ __ Add(r3, r0, 0x0);
+ __ Bind(&ok_5);
+
+ __ B(&done);
+
+ __ Bind(&wrong);
+ // TODO: Use __ Mov(r0, 0x0) instead.
+ __ Add(r3, r0, 0x0);
+
+ __ Bind(&done);
+ END();
+
+ RUN();
+
+ // TODO: Use r0.
+ ASSERT_EQUAL_32(0x1, r3);
+
+ TEARDOWN();
+}
+
+
+TEST(bfc_bfi) {
+ SETUP();
+
+ START();
+ __ Mov(r0, 0xffffffff);
+ __ Mov(r1, 0x01234567);
+ __ Mov(r2, 0x0);
+
+ __ Bfc(r0, 0, 3);
+ __ Bfc(r0, 16, 5);
+
+ __ Bfi(r2, r1, 0, 8);
+ __ Bfi(r2, r1, 16, 16);
+ END();
+
+ RUN();
+
+ ASSERT_EQUAL_32(0xffe0fff8, r0);
+ ASSERT_EQUAL_32(0x45670067, r2);
+
+ TEARDOWN();
+}
+
+
+TEST(bic) {
+ SETUP();
+
+ START();
+ __ Mov(r0, 0xfff0);
+ __ Mov(r1, 0xf00000ff);
+ __ Mov(r2, 0xffffffff);
+
+ __ Bic(r3, r0, r1);
+ __ Bic(r4, r0, Operand(r1, LSL, 4));
+ __ Bic(r5, r0, Operand(r1, LSR, 1));
+ __ Bic(r6, r0, Operand(r1, ASR, 20));
+ __ Bic(r7, r0, Operand(r1, ROR, 28));
+ __ Bic(r8, r0, 0x1f);
+
+ // Set the C flag.
+ __ Adds(r9, r2, 1);
+ __ Bic(r9, r1, Operand(r1, RRX));
+
+ // Clear the C flag.
+ __ Adds(r10, r0, 0);
+ __ Bic(r10, r1, Operand(r1, RRX));
+ END();
+
+ RUN();
+
+ ASSERT_EQUAL_32(0x0000ff00, r3);
+ ASSERT_EQUAL_32(0x0000f000, r4);
+ ASSERT_EQUAL_32(0x0000ff80, r5);
+ ASSERT_EQUAL_32(0x000000f0, r6);
+ ASSERT_EQUAL_32(0x0000f000, r7);
+ ASSERT_EQUAL_32(0x0000ffe0, r8);
+ ASSERT_EQUAL_32(0x00000080, r9);
+ ASSERT_EQUAL_32(0x80000080, r10);
+
+ TEARDOWN();
+}
+
+
+TEST(bics) {
+ SETUP();
+
+ START();
+ __ Mov(r0, 0);
+ __ Mov(r1, 0xf00000ff);
+
+ __ Bics(r0, r1, r1);
+ END();
+
+ RUN();
+
+ ASSERT_EQUAL_NZCV(ZFlag);
+ ASSERT_EQUAL_32(0, r0);
+
+ START();
+ __ Mov(r0, 0x00fff000);
+ __ Mov(r1, 0x0fffff00);
+
+ __ Bics(r0, r0, Operand(r1, LSL, 4));
+ END();
+
+ RUN();
+
+ ASSERT_EQUAL_NZCV(ZFlag);
+ ASSERT_EQUAL_32(0x00000000, r0);
+
+ START();
+ __ Mov(r0, 0x0000fff0);
+ __ Mov(r1, 0x0fffff00);
+
+ __ Bics(r0, r0, Operand(r1, LSR, 4));
+ END();
+
+ RUN();
+
+ ASSERT_EQUAL_NZCV(ZFlag);
+ ASSERT_EQUAL_32(0x00000000, r0);
+
+ START();
+ __ Mov(r0, 0xf000fff0);
+ __ Mov(r1, 0x0fffff00);
+
+ __ Bics(r0, r0, Operand(r1, ASR, 4));
+ END();
+
+ RUN();
+
+ ASSERT_EQUAL_NZCV(NFlag);
+ ASSERT_EQUAL_32(0xf0000000, r0);
+
+ START();
+ __ Mov(r0, 0x80000000);
+ __ Mov(r1, 0xfffffffe);
+
+ __ Bics(r0, r0, Operand(r1, ROR, 1));
+ END();
+
+ RUN();
+
+ ASSERT_EQUAL_NZCV(NFlag);
+ ASSERT_EQUAL_32(0x80000000, r0);
+
+ START();
+ __ Mov(r0, 0x80000000);
+ __ Mov(r1, 0x80000001);
+
+ // Clear the C flag, forcing RRX to insert 0 in r1's most significant bit.
+ __ Adds(r2, r0, 0);
+ __ Bics(r2, r0, Operand(r1, RRX));
+ END();
+
+ RUN();
+
+ ASSERT_EQUAL_NZCV(NCFlag);
+ ASSERT_EQUAL_32(0x80000000, r2);
+
+ START();
+ __ Mov(r0, 0x80000000);
+ __ Mov(r1, 0x80000001);
+ __ Mov(r2, 0xffffffff);
+
+ // Set the C flag, forcing RRX to insert 1 in r1's most significant bit.
+ __ Adds(r2, r2, 1);
+ __ Bics(r2, r0, Operand(r1, RRX));
+ END();
+
+ RUN();
+
+ ASSERT_EQUAL_NZCV(ZCFlag);
+ ASSERT_EQUAL_32(0, r2);
+
+ START();
+ __ Mov(r0, 0xf000);
+
+ __ Bics(r0, r0, 0xf000);
+ END();
+
+ RUN();
+
+ ASSERT_EQUAL_NZCV(ZFlag);
+ ASSERT_EQUAL_32(0x00000000, r0);
+
+ START();
+ __ Mov(r0, 0xff000000);
+
+ __ Bics(r0, r0, 0x7fffffff);
+ END();
+
+ RUN();
+
+ ASSERT_EQUAL_NZCV(NFlag);
+ ASSERT_EQUAL_32(0x80000000, r0);
+
+ TEARDOWN();
+}
+
+
+// Make sure calling a macro-assembler instruction will generate literal pools
+// if needed.
+TEST_T32(veneer_pool_generated_by_macro_instruction) {
+ SETUP();
+
+ START();
+
+ Label start, end;
+
+ VIXL_CHECK(masm.VeneerPoolIsEmpty());
+ VIXL_CHECK(masm.LiteralPoolIsEmpty());
+
+ __ Mov(r0, 1);
+
+ __ Bind(&start);
+ __ Cbz(r0, &end);
+
+ VIXL_CHECK(!masm.VeneerPoolIsEmpty());
+ VIXL_CHECK(masm.LiteralPoolIsEmpty());
+
+ // Generate enough code so that, after the loop, no instruction can be
+ // generated before we need to generate the veneer pool.
+ // Use `ExactAssemblyScope` and the assembler to generate the code.
+ int32_t space = masm.GetMarginBeforeVeneerEmission();
+ {
+ ExactAssemblyScope scope(&masm, space, ExactAssemblyScope::kExactSize);
+ while (space > 0) {
+ __ nop();
+ space -= k16BitT32InstructionSizeInBytes;
+ }
+ }
+
+ // We should not have emitted the veneer pool at this point.
+ VIXL_CHECK(!masm.VeneerPoolIsEmpty());
+ VIXL_CHECK(masm.LiteralPoolIsEmpty());
+ VIXL_CHECK(masm.GetMarginBeforeVeneerEmission() == 0);
+
+ // Now the pool will need to be generated before we can emit anything.
+ Label check;
+ __ Bind(&check);
+ __ Mov(r0, 0);
+ // We should have generated 3 wide instructions:
+ // b.w past_veneer_pool
+ // b.w end ;; veneer from CBZ to "end".
+ // past_veneer_pool:
+ // mov r0, #0
+ VIXL_CHECK(masm.GetSizeOfCodeGeneratedSince(&check) ==
+ (3 * k32BitT32InstructionSizeInBytes));
+
+ // Branch back to make sure the veneers work.
+ __ B(&start);
+ __ Bind(&end);
+
+ VIXL_CHECK(masm.VeneerPoolIsEmpty());
+ VIXL_CHECK(masm.LiteralPoolIsEmpty());
+
+ END();
+
+ RUN();
+
+ ASSERT_EQUAL_32(0, r0);
+
+ TEARDOWN();
+}
+
+
+TEST(emit_reused_load_literal_rewind) {
+ // This test generates an Ldrd that needs to be rewinded and loads a literal
+ // that already is in the pool (hence it will be part of the pool that gets
+ // emitted as part of the rewind).
+ SETUP();
+
+ START();
+
+ // Make sure the pool is empty.
+ masm.EmitLiteralPool(MacroAssembler::kBranchRequired);
+ ASSERT_LITERAL_POOL_SIZE(0);
+
+ const int ldrd_range = masm.IsUsingA32() ? 255 : 1020;
+ const int string_size = AlignUp(ldrd_range + kMaxInstructionSizeInBytes, 4);
+ std::string test_string(string_size, 'x');
+ StringLiteral big_literal(test_string.c_str());
+ __ Adr(r4, &big_literal);
+
+ // This load has a wider range than the Ldrd used below for the same
+ // literal.
+ Literal<uint64_t> l1(0xcafebeefdeadbaba);
+ __ Ldr(r0, &l1);
+
+ // This Ldrd will be emitted and then rewinded, forcing the pool to be
+ // emitted before we regenerate the instruction, so l1 will be bound and the
+ // literal pool empty afterwards.
+ __ Ldrd(r2, r3, &l1);
+ ASSERT_LITERAL_POOL_SIZE(0);
+
+ __ Ldr(r4, MemOperand(r4)); // Load the first 4 characters in r4.
+ END();
+
+ RUN();
+
+ // Check that the literals loaded correctly.
+ ASSERT_EQUAL_32(0xdeadbaba, r0);
+ ASSERT_EQUAL_32(0xdeadbaba, r2);
+ ASSERT_EQUAL_32(0xcafebeef, r3);
+ ASSERT_EQUAL_32(0x78787878, r4);
+
+ TEARDOWN();
+}
+
+
+TEST(emit_reused_load_literal_should_not_rewind) {
+ // This test checks that we are not conservative when rewinding a load of a
+ // literal that is already in the literal pool.
+ SETUP();
+
+ START();
+
+ // Make sure the pool is empty.
+ masm.EmitLiteralPool(MacroAssembler::kBranchRequired);
+ ASSERT_LITERAL_POOL_SIZE(0);
+
+ // This load has a wider range than the Ldrd used below for the same
+ // literal.
+ Literal<uint64_t> l1(0xcafebeefdeadbaba);
+ __ Ldr(r0, &l1);
+
+ // Add a large string to the literal pool, but only *after* l1, so the
+ // Ldrd below should not need to rewind.
+ const int ldrd_range = masm.IsUsingA32() ? 255 : 1020;
+ const int string_size = AlignUp(ldrd_range + kMaxInstructionSizeInBytes, 4);
+ std::string test_string(string_size, 'x');
+ StringLiteral big_literal(test_string.c_str());
+ __ Adr(r4, &big_literal);
+ __ Ldrd(r2, r3, &l1);
+
+ ASSERT_LITERAL_POOL_SIZE(AlignUp(string_size + 1, 4) + l1.GetSize());
+
+ // Make sure the pool is emitted.
+ masm.EmitLiteralPool(MacroAssembler::kBranchRequired);
+ ASSERT_LITERAL_POOL_SIZE(0);
+
+ __ Ldr(r4, MemOperand(r4)); // Load the first 4 characters in r4.
+ END();
+
+ RUN();
+
+ // Check that the literals loaded correctly.
+ ASSERT_EQUAL_32(0xdeadbaba, r0);
+ ASSERT_EQUAL_32(0xdeadbaba, r2);
+ ASSERT_EQUAL_32(0xcafebeef, r3);
+ ASSERT_EQUAL_32(0x78787878, r4);
+
+ TEARDOWN();
+}
+
+
+void EmitReusedLoadLiteralStressTest(InstructionSet isa, bool conditional) {
+ // This test stresses loading a literal that is already in the literal pool,
+ // for
+ // various positionings on the existing load from that literal. We try to
+ // exercise
+ // cases where the two loads result in similar checkpoints for the literal
+ // pool.
+ SETUP();
+
+ const int ldrd_range = masm.IsUsingA32() ? 255 : 1020;
+ const int ldr_range = 4095;
+ const int nop_size = masm.IsUsingA32() ? 4 : 2;
+ const int nops = (ldr_range - ldrd_range) / nop_size;
+
+ for (int n = nops - 10; n < nops + 10; ++n) {
+ START();
+
+ // Make sure the pool is empty.
+ masm.EmitLiteralPool(MacroAssembler::kBranchRequired);
+ ASSERT_LITERAL_POOL_SIZE(0);
+
+ if (conditional) {
+ __ Mov(r1, 0);
+ __ Cmp(r1, 0);
+ }
+
+ // Add a large string to the pool, which will force the Ldrd below to rewind
+ // (if the pool is not already emitted due to the Ldr).
+ const int string_size = AlignUp(ldrd_range + kMaxInstructionSizeInBytes, 4);
+ std::string test_string(string_size, 'x');
+ StringLiteral big_literal(test_string.c_str());
+ __ Ldr(r4, &big_literal);
+
+ // This load has a wider range than the Ldrd used below for the same
+ // literal.
+ Literal<uint64_t> l1(0xcafebeefdeadbaba);
+ __ Ldr(r0, &l1);
+
+ // Generate nops, in order to bring the checkpoints of the Ldr and Ldrd
+ // closer.
+ {
+ ExactAssemblyScope scope(&masm,
+ n * nop_size,
+ ExactAssemblyScope::kExactSize);
+ for (int i = 0; i < n; ++i) {
+ __ nop();
+ }
+ }
+
+ if (conditional) {
+ __ Ldrd(eq, r2, r3, &l1);
+ } else {
+ __ Ldrd(r2, r3, &l1);
+ }
+ // At this point, the pool will be emitted either because Ldrd needed to
+ // rewind, or because Ldr reached its range.
+ ASSERT_LITERAL_POOL_SIZE(0);
+
+ END();
+
+ RUN();
+
+ // Check that the literals loaded correctly.
+ ASSERT_EQUAL_32(0xdeadbaba, r0);
+ ASSERT_EQUAL_32(0xdeadbaba, r2);
+ ASSERT_EQUAL_32(0xcafebeef, r3);
+ ASSERT_EQUAL_32(0x78787878, r4);
+ }
+
+ TEARDOWN();
+}
+
+
+TEST(emit_reused_load_literal_stress) {
+ EmitReusedLoadLiteralStressTest(isa, false /*conditional*/);
+}
+
+
+TEST(emit_reused_conditional_load_literal_stress) {
+ EmitReusedLoadLiteralStressTest(isa, true /*conditional*/);
+}
+
+
+TEST(test_many_loads_from_same_literal) {
+ // This test generates multiple loads from the same literal in order to
+ // test that the delegate recursion limit is appropriate for Ldrd with
+ // large negative offsets.
+ SETUP();
+
+ START();
+
+ // Make sure the pool is empty.
+ masm.EmitLiteralPool(MacroAssembler::kBranchRequired);
+ ASSERT_LITERAL_POOL_SIZE(0);
+
+ Literal<uint64_t> l0(0xcafebeefdeadbaba);
+ __ Ldrd(r0, r1, &l0);
+ for (int i = 0; i < 10000; ++i) {
+ __ Add(r2, r2, i);
+ __ Ldrd(r4, r5, &l0);
+ }
+
+ __ Ldrd(r2, r3, &l0);
+
+ END();
+
+ RUN();
+
+ // Check that the literals loaded correctly.
+ ASSERT_EQUAL_32(0xdeadbaba, r0);
+ ASSERT_EQUAL_32(0xcafebeef, r1);
+ ASSERT_EQUAL_32(0xdeadbaba, r2);
+ ASSERT_EQUAL_32(0xcafebeef, r3);
+ ASSERT_EQUAL_32(0xdeadbaba, r4);
+ ASSERT_EQUAL_32(0xcafebeef, r5);
+
+ TEARDOWN();
+}
+
+
+// Make sure calling a macro-assembler instruction will generate literal pools
+// if needed.
+TEST_T32(literal_pool_generated_by_macro_instruction) {
+ SETUP();
+
+ START();
+
+ VIXL_CHECK(masm.VeneerPoolIsEmpty());
+ VIXL_CHECK(masm.LiteralPoolIsEmpty());
+
+ __ Ldrd(r0, r1, 0x1234567890abcdef);
+
+ VIXL_CHECK(masm.VeneerPoolIsEmpty());
+ VIXL_CHECK(!masm.LiteralPoolIsEmpty());
+
+ // Generate enough code so that, after the loop, no instruction can be
+ // generated before we need to generate the literal pool.
+ // Use `ExactAssemblyScope` and the assembler to generate the code.
+ int32_t space = masm.GetMarginBeforeLiteralEmission();
+ {
+ ExactAssemblyScope scope(&masm, space, ExactAssemblyScope::kExactSize);
+ while (space > 0) {
+ __ nop();
+ space -= k16BitT32InstructionSizeInBytes;
+ }
+ }
+
+ // We should not have emitted the literal pool at this point.
+ VIXL_CHECK(masm.VeneerPoolIsEmpty());
+ VIXL_CHECK(!masm.LiteralPoolIsEmpty());
+ VIXL_CHECK(masm.GetMarginBeforeLiteralEmission() == 0);
+
+ // Now the pool will need to be generated before we emit anything.
+ Label check;
+ __ Bind(&check);
+ __ Mov(r2, 0x12345678);
+ // We should have generated 3 wide instructions and 8 bytes of data:
+ // b.w past_literal_pool
+ // .bytes 0x1234567890abcdef
+ // past_literal_pool:
+ // mov r2, #22136
+ // movt r2, #4660
+ VIXL_CHECK(masm.GetSizeOfCodeGeneratedSince(&check) ==
+ (3 * k32BitT32InstructionSizeInBytes + 8));
+
+ VIXL_CHECK(masm.VeneerPoolIsEmpty());
+ VIXL_CHECK(masm.LiteralPoolIsEmpty());
+
+ END();
+
+ RUN();
+
+ ASSERT_EQUAL_32(0x90abcdef, r0);
+ ASSERT_EQUAL_32(0x12345678, r1);
+ ASSERT_EQUAL_32(0x12345678, r2);
+
+ TEARDOWN();
+}
+
+
+TEST(emit_single_literal) {
+ SETUP();
+
+ START();
+ // Make sure the pool is empty.
+ masm.EmitLiteralPool(MacroAssembler::kBranchRequired);
+ ASSERT_LITERAL_POOL_SIZE(0);
+
+ // Create one literal pool entry.
+ __ Ldrd(r0, r1, 0x1234567890abcdef);
+ ASSERT_LITERAL_POOL_SIZE(8);
+ __ Vldr(s0, 1.0);
+ __ Vldr(d1, 2.0);
+ __ Vmov(d2, 4.1);
+ __ Vmov(s8, 8.2);
+ ASSERT_LITERAL_POOL_SIZE(20);
+ END();
+
+ RUN();
+
+ // Check that the literals loaded correctly.
+ ASSERT_EQUAL_32(0x90abcdef, r0);
+ ASSERT_EQUAL_32(0x12345678, r1);
+ ASSERT_EQUAL_FP32(1.0f, s0);
+ ASSERT_EQUAL_FP64(2.0, d1);
+ ASSERT_EQUAL_FP64(4.1, d2);
+ ASSERT_EQUAL_FP32(8.2f, s8);
+
+ TEARDOWN();
+}
+
+
+#undef __
+#define __ masm->
+
+
+void EmitLdrdLiteralTest(MacroAssembler* masm) {
+ const int ldrd_range = masm->IsUsingA32() ? 255 : 1020;
+ // We want to emit code up to the maximum literal load range and ensure the
+ // pool has not been emitted. Compute the limit (end).
+ ptrdiff_t end = AlignDown(
+ // Align down the PC to 4 bytes as the instruction does when it's
+ // executed.
+ // The PC will be the cursor offset plus the architecture state PC
+ // offset.
+ AlignDown(masm->GetBuffer()->GetCursorOffset() +
+ masm->GetArchitectureStatePCOffset(),
+ 4) +
+ // Maximum range allowed to access the constant.
+ ldrd_range -
+ // The literal pool has a two instruction margin.
+ 2 * kMaxInstructionSizeInBytes,
+ // AlignDown to 4 byte as the literals will be 4 byte aligned.
+ 4);
+
+ // Create one literal pool entry.
+ __ Ldrd(r0, r1, 0x1234567890abcdef);
+ ASSERT_LITERAL_POOL_SIZE(8);
+
+ int32_t margin = masm->GetMarginBeforeLiteralEmission();
+ {
+ ExactAssemblyScope scope(masm, margin, ExactAssemblyScope::kExactSize);
+ // Opening the scope should not have triggered the emission of the literal
+ // pool.
+ VIXL_CHECK(!masm->LiteralPoolIsEmpty());
+ while (masm->GetCursorOffset() < end) {
+ __ nop();
+ }
+ VIXL_CHECK(masm->GetCursorOffset() == end);
+ }
+
+ // Check that the pool has not been emited along the way.
+ ASSERT_LITERAL_POOL_SIZE(8);
+ // This extra instruction should trigger an emit of the pool.
+ __ Nop();
+ // The pool should have been emitted.
+ ASSERT_LITERAL_POOL_SIZE(0);
+}
+
+
+#undef __
+#define __ masm.
+
+
+TEST(emit_literal_rewind) {
+ SETUP();
+
+ START();
+
+ // Make sure the pool is empty.
+ masm.EmitLiteralPool(MacroAssembler::kBranchRequired);
+ ASSERT_LITERAL_POOL_SIZE(0);
+
+ EmitLdrdLiteralTest(&masm);
+
+ const int ldrd_range = masm.IsUsingA32() ? 255 : 1020;
+ const int string_size = AlignUp(ldrd_range + kMaxInstructionSizeInBytes, 4);
+ std::string test_string(string_size, 'x');
+ StringLiteral big_literal(test_string.c_str());
+ __ Adr(r4, &big_literal);
+ // This adr will overflow the literal pool and force a rewind.
+ // That means that the string will be generated then, then Ldrd and the
+ // Ldrd's value will be alone in the pool.
+ __ Ldrd(r2, r3, 0xcafebeefdeadbaba);
+ ASSERT_LITERAL_POOL_SIZE(8);
+
+ masm.EmitLiteralPool(MacroAssembler::kBranchRequired);
+ ASSERT_LITERAL_POOL_SIZE(0);
+ __ Ldr(r4, MemOperand(r4)); // Load the first 4 characters in r4.
+ END();
+
+ RUN();
+
+ // Check that the literals loaded correctly.
+ ASSERT_EQUAL_32(0x90abcdef, r0);
+ ASSERT_EQUAL_32(0x12345678, r1);
+ ASSERT_EQUAL_32(0xdeadbaba, r2);
+ ASSERT_EQUAL_32(0xcafebeef, r3);
+ ASSERT_EQUAL_32(0x78787878, r4);
+
+ TEARDOWN();
+}
+
+TEST(emit_literal_conditional_rewind) {
+ SETUP();
+
+ START();
+
+ // This test is almost identical to the test above, but the Ldrd instruction
+ // is conditional and there is a second conditional Ldrd instruction that will
+ // not be executed. This is to check that reverting the emission of a load
+ // literal instruction, rewinding, emitting the literal pool and then emitting
+ // the instruction again works correctly when the load is conditional.
+
+ // Make sure the pool is empty.
+ masm.EmitLiteralPool(MacroAssembler::kBranchRequired);
+ ASSERT_LITERAL_POOL_SIZE(0);
+
+ const int ldrd_range = masm.IsUsingA32() ? 255 : 1020;
+ const int string_size = AlignUp(ldrd_range + kMaxInstructionSizeInBytes, 4);
+ std::string test_string(string_size, 'x');
+ StringLiteral big_literal(test_string.c_str());
+ __ Adr(r2, &big_literal);
+ // This adr will overflow the literal pool and force a rewind.
+ // That means that the string will be generated then, then Ldrd and the
+ // Ldrd's value will be alone in the pool.
+ __ Mov(r0, 0);
+ __ Mov(r1, 0);
+ __ Mov(r3, 1);
+ __ Cmp(r3, 1);
+ __ Ldrd(eq, r0, r1, 0xcafebeefdeadbaba);
+ __ Ldrd(ne, r0, r1, 0xdeadcafebeefbaba);
+ ASSERT_LITERAL_POOL_SIZE(16);
+
+ masm.EmitLiteralPool(MacroAssembler::kBranchRequired);
+ ASSERT_LITERAL_POOL_SIZE(0);
+ __ Ldr(r2, MemOperand(r2)); // Load the first 4 characters in r2.
+ END();
+
+ RUN();
+
+ // Check that the literals loaded correctly.
+ ASSERT_EQUAL_32(0xdeadbaba, r0);
+ ASSERT_EQUAL_32(0xcafebeef, r1);
+ ASSERT_EQUAL_32(0x78787878, r2);
+
+ TEARDOWN();
+}
+
+enum LiteralStressTestMode {
+ kUnconditional,
+ kConditionalTrue,
+ kConditionalFalse,
+ kConditionalBoth
+};
+
+// Test loading a literal when the size of the literal pool is close to the
+// maximum range of the load, with varying PC values (and alignment, for T32).
+// This test is similar to the tests above, with the difference that we allow
+// an extra offset to the string size in order to make sure that various pool
+// sizes close to the maximum supported offset will produce code that executes
+// correctly. As the Ldrd might or might not be rewinded, we do not assert on
+// the size of the literal pool in this test.
+void EmitLdrdLiteralStressTest(InstructionSet isa,
+ bool unaligned,
+ LiteralStressTestMode test_mode) {
+ SETUP();
+
+ for (int offset = -10; offset <= 10; ++offset) {
+ START();
+
+ if (unaligned) {
+ __ Nop();
+ VIXL_ASSERT((masm.GetBuffer()->GetCursorOffset() % 4) == 2);
+ }
+
+ // Make sure the pool is empty.
+ masm.EmitLiteralPool(MacroAssembler::kBranchRequired);
+ ASSERT_LITERAL_POOL_SIZE(0);
+
+ const int ldrd_range = masm.IsUsingA32() ? 255 : 1020;
+ const int string_size = ldrd_range + offset;
+ std::string test_string(string_size - 1, 'x');
+ StringLiteral big_literal(test_string.c_str());
+ __ Adr(r2, &big_literal);
+ __ Mov(r0, 0);
+ __ Mov(r1, 0);
+ switch (test_mode) {
+ case kUnconditional:
+ __ Ldrd(r0, r1, 0xcafebeefdeadbaba);
+ break;
+ case kConditionalTrue:
+ __ Mov(r0, 0xffffffff);
+ __ Mov(r1, r0);
+ __ Mov(r3, 1);
+ __ Cmp(r3, 1);
+ __ Ldrd(eq, r0, r1, 0xcafebeefdeadbaba);
+ break;
+ case kConditionalFalse:
+ __ Mov(r0, 0xdeadbaba);
+ __ Mov(r1, 0xcafebeef);
+ __ Mov(r3, 1);
+ __ Cmp(r3, 1);
+ __ Ldrd(ne, r0, r1, 0xdeadcafebeefbaba);
+ break;
+ case kConditionalBoth:
+ __ Mov(r3, 1);
+ __ Cmp(r3, 1);
+ __ Ldrd(eq, r0, r1, 0xcafebeefdeadbaba);
+ __ Ldrd(ne, r0, r1, 0xdeadcafebeefbaba);
+ break;
+ }
+
+ masm.EmitLiteralPool(MacroAssembler::kBranchRequired);
+ ASSERT_LITERAL_POOL_SIZE(0);
+ __ Ldr(r2, MemOperand(r2)); // Load the first 4 characters in r2.
+ END();
+
+ RUN();
+
+ // Check that the literals loaded correctly.
+ ASSERT_EQUAL_32(0xdeadbaba, r0);
+ ASSERT_EQUAL_32(0xcafebeef, r1);
+ ASSERT_EQUAL_32(0x78787878, r2);
+ }
+
+ TEARDOWN();
+}
+
+
+TEST(emit_literal_rewind_stress) {
+ EmitLdrdLiteralStressTest(isa, false /*unaligned*/, kUnconditional);
+}
+
+
+TEST_T32(emit_literal_rewind_stress_unaligned) {
+ EmitLdrdLiteralStressTest(isa, true /*unaligned*/, kUnconditional);
+}
+
+
+TEST(emit_literal_conditional_rewind_stress) {
+ EmitLdrdLiteralStressTest(isa, false /*unaligned*/, kConditionalTrue);
+ EmitLdrdLiteralStressTest(isa, false /*unaligned*/, kConditionalFalse);
+ EmitLdrdLiteralStressTest(isa, false /*unaligned*/, kConditionalBoth);
+}
+
+
+TEST_T32(emit_literal_conditional_rewind_stress_unaligned) {
+ EmitLdrdLiteralStressTest(isa, true /*unaligned*/, kConditionalTrue);
+ EmitLdrdLiteralStressTest(isa, true /*unaligned*/, kConditionalFalse);
+ EmitLdrdLiteralStressTest(isa, true /*unaligned*/, kConditionalBoth);
+}
+
+
+TEST_T32(emit_literal_unaligned) {
+ SETUP();
+
+ START();
+
+ // Make sure the pool is empty.
+ masm.EmitLiteralPool(MacroAssembler::kBranchRequired);
+ ASSERT_LITERAL_POOL_SIZE(0);
+
+ // Generate a nop to break the 4 bytes alignment.
+ __ Nop();
+
+ EmitLdrdLiteralTest(&masm);
+
+ END();
+
+ RUN();
+
+ // Check that the literals loaded correctly.
+ ASSERT_EQUAL_32(0x90abcdef, r0);
+ ASSERT_EQUAL_32(0x12345678, r1);
+
+ TEARDOWN();
+}
+
+
+TEST(literal_multiple_uses) {
+ SETUP();
+
+ START();
+ Literal<int32_t> lit(42);
+ __ Ldr(r0, &lit);
+ ASSERT_LITERAL_POOL_SIZE(4);
+
+ // Multiple uses of the same literal object should not make the
+ // pool grow.
+ __ Ldrb(r1, &lit);
+ __ Ldrsb(r2, &lit);
+ __ Ldrh(r3, &lit);
+ __ Ldrsh(r4, &lit);
+ ASSERT_LITERAL_POOL_SIZE(4);
+
+ END();
+
+ RUN();
+
+ ASSERT_EQUAL_32(42, r0);
+ ASSERT_EQUAL_32(42, r1);
+ ASSERT_EQUAL_32(42, r2);
+ ASSERT_EQUAL_32(42, r3);
+ ASSERT_EQUAL_32(42, r4);
+
+ TEARDOWN();
+}
+
+
+// A test with two loads literal which go out of range at the same time.
+TEST_A32(ldr_literal_range_same_time) {
+ SETUP();
+
+ START();
+ const int ldrd_range = 255;
+ // We need to take into account the jump over the pool.
+ const int ldrd_padding = ldrd_range - 2 * kA32InstructionSizeInBytes;
+ const int ldr_range = 4095;
+ // We need to take into account the ldrd padding and the ldrd instruction.
+ const int ldr_padding =
+ ldr_range - ldrd_padding - 2 * kA32InstructionSizeInBytes;
+
+ __ Ldr(r1, 0x12121212);
+ ASSERT_LITERAL_POOL_SIZE(4);
+
+ {
+ int space = AlignDown(ldr_padding, kA32InstructionSizeInBytes);
+ ExactAssemblyScope scope(&masm, space, ExactAssemblyScope::kExactSize);
+ int32_t end = masm.GetCursorOffset() + space;
+ while (masm.GetCursorOffset() < end) {
+ __ nop();
+ }
+ }
+
+ __ Ldrd(r2, r3, 0x1234567890abcdef);
+ ASSERT_LITERAL_POOL_SIZE(12);
+
+ {
+ int space = AlignDown(ldrd_padding, kA32InstructionSizeInBytes);
+ ExactAssemblyScope scope(&masm, space, ExactAssemblyScope::kExactSize);
+ for (int32_t end = masm.GetCursorOffset() + space;
+ masm.GetCursorOffset() < end;) {
+ __ nop();
+ }
+ }
+ ASSERT_LITERAL_POOL_SIZE(12);
+
+ // This mov will put the two loads literal out of range and will force
+ // the literal pool emission.
+ __ Mov(r0, 0);
+ ASSERT_LITERAL_POOL_SIZE(0);
+ END();
+
+ RUN();
+
+ ASSERT_EQUAL_32(0x12121212, r1);
+ ASSERT_EQUAL_32(0x90abcdef, r2);
+ ASSERT_EQUAL_32(0x12345678, r3);
+
+ TEARDOWN();
+}
+
+
+TEST(ldr_literal_mix_types) {
+ SETUP();
+
+ START();
+ Literal<uint64_t> l0(0x1234567890abcdef);
+ Literal<int32_t> l1(0x12345678);
+ Literal<uint16_t> l2(1234);
+ Literal<int16_t> l3(-678);
+ Literal<uint8_t> l4(42);
+ Literal<int8_t> l5(-12);
+
+ __ Ldrd(r0, r1, &l0);
+ __ Ldr(r2, &l1);
+ __ Ldrh(r3, &l2);
+ __ Ldrsh(r4, &l3);
+ __ Ldrb(r5, &l4);
+ __ Ldrsb(r6, &l5);
+ ASSERT_LITERAL_POOL_SIZE(28);
+
+ END();
+
+ RUN();
+
+ ASSERT_EQUAL_32(0x90abcdef, r0);
+ ASSERT_EQUAL_32(0x12345678, r1);
+ ASSERT_EQUAL_32(0x12345678, r2);
+ ASSERT_EQUAL_32(1234, r3);
+ ASSERT_EQUAL_32(-678, r4);
+ ASSERT_EQUAL_32(42, r5);
+ ASSERT_EQUAL_32(-12, r6);
+
+ TEARDOWN();
+}
+
+
+TEST(ldr_literal_conditional) {
+ SETUP();
+
+ START();
+ Literal<uint64_t> l0(0x1234567890abcdef);
+ Literal<uint64_t> l0_not_taken(0x90abcdef12345678);
+ Literal<int32_t> l1(0x12345678);
+ Literal<int32_t> l1_not_taken(0x56781234);
+ Literal<uint16_t> l2(1234);
+ Literal<uint16_t> l2_not_taken(3412);
+ Literal<int16_t> l3(-678);
+ Literal<int16_t> l3_not_taken(678);
+ Literal<uint8_t> l4(42);
+ Literal<uint8_t> l4_not_taken(-42);
+ Literal<int8_t> l5(-12);
+ Literal<int8_t> l5_not_taken(12);
+ Literal<float> l6(1.2345f);
+ Literal<float> l6_not_taken(0.0f);
+ Literal<double> l7(1.3333);
+ Literal<double> l7_not_taken(0.0);
+
+ // Check that conditionally loading literals of different types works
+ // correctly for both A32 and T32.
+ __ Mov(r7, 1);
+ __ Cmp(r7, 1);
+ __ Ldrd(eq, r0, r1, &l0);
+ __ Ldrd(ne, r0, r1, &l0_not_taken);
+ __ Cmp(r7, 0);
+ __ Ldr(gt, r2, &l1);
+ __ Ldr(le, r2, &l1_not_taken);
+ __ Cmp(r7, 2);
+ __ Ldrh(lt, r3, &l2);
+ __ Ldrh(ge, r3, &l2_not_taken);
+ __ Ldrsh(le, r4, &l3);
+ __ Ldrsh(gt, r4, &l3_not_taken);
+ __ Cmp(r7, 1);
+ __ Ldrb(ge, r5, &l4);
+ __ Ldrb(lt, r5, &l4_not_taken);
+ __ Ldrsb(eq, r6, &l5);
+ __ Ldrsb(ne, r6, &l5_not_taken);
+ __ Vldr(Condition(eq), s0, &l6);
+ __ Vldr(Condition(ne), s0, &l6_not_taken);
+ __ Vldr(Condition(eq), d1, &l7);
+ __ Vldr(Condition(ne), d1, &l7_not_taken);
+
+ END();
+
+ RUN();
+
+ ASSERT_EQUAL_32(0x90abcdef, r0);
+ ASSERT_EQUAL_32(0x12345678, r1);
+ ASSERT_EQUAL_32(0x12345678, r2);
+ ASSERT_EQUAL_32(1234, r3);
+ ASSERT_EQUAL_32(-678, r4);
+ ASSERT_EQUAL_32(42, r5);
+ ASSERT_EQUAL_32(-12, r6);
+ ASSERT_EQUAL_FP32(1.2345f, s0);
+ ASSERT_EQUAL_FP64(1.3333, d1);
+
+ TEARDOWN();
+}
+
+
+struct LdrLiteralRangeTest {
+ void (MacroAssembler::*instruction)(Register, RawLiteral*);
+ Register result_reg;
+ int a32_range;
+ int t32_range;
+ uint32_t literal_value;
+ uint32_t test_value;
+};
+
+
+const LdrLiteralRangeTest kLdrLiteralRangeTestData[] =
+ {{&MacroAssembler::Ldr, r1, 4095, 4095, 0x12345678, 0x12345678},
+ {&MacroAssembler::Ldrh, r2, 255, 4095, 0xabcdefff, 0x0000efff},
+ {&MacroAssembler::Ldrsh, r3, 255, 4095, 0x00008765, 0xffff8765},
+ {&MacroAssembler::Ldrb, r4, 4095, 4095, 0x12345678, 0x00000078},
+ {&MacroAssembler::Ldrsb, r5, 255, 4095, 0x00000087, 0xffffff87}};
+
+
+void GenerateLdrLiteralTriggerPoolEmission(InstructionSet isa,
+ bool unaligned_ldr) {
+ SETUP();
+
+ for (size_t i = 0; i < ARRAY_SIZE(kLdrLiteralRangeTestData); ++i) {
+ const LdrLiteralRangeTest& test = kLdrLiteralRangeTestData[i];
+
+ START();
+
+ if (unaligned_ldr) {
+ // Generate a nop to break the 4-byte alignment.
+ __ Nop();
+ VIXL_ASSERT((masm.GetBuffer()->GetCursorOffset() % 4) == 2);
+ }
+
+ __ Ldr(r6, 0x12345678);
+ ASSERT_LITERAL_POOL_SIZE(4);
+
+ // TODO: The MacroAssembler currently checks for more space than required
+ // when emitting macro instructions, triggering emission of the pool before
+ // absolutely required. For now we keep a buffer. Fix this test when the
+ // MacroAssembler becomes precise again.
+ int masm_check_margin = 10 * kMaxInstructionSizeInBytes;
+ size_t expected_pool_size = 4;
+ while ((masm.GetMarginBeforeLiteralEmission() - masm_check_margin) >=
+ static_cast<int32_t>(kMaxInstructionSizeInBytes)) {
+ __ Ldr(r7, 0x90abcdef);
+ // Each ldr instruction will force a new literal value to be added
+ // to the pool. Check that the literal pool grows accordingly.
+ expected_pool_size += 4;
+ ASSERT_LITERAL_POOL_SIZE(expected_pool_size);
+ }
+
+ int space = masm.GetMarginBeforeLiteralEmission();
+ int end = masm.GetCursorOffset() + space;
+ {
+ // Generate nops precisely to fill the buffer.
+ ExactAssemblyScope accurate_scope(&masm, space); // This should not
+ // trigger emission of
+ // the pool.
+ VIXL_CHECK(!masm.LiteralPoolIsEmpty());
+ while (masm.GetCursorOffset() < end) {
+ __ nop();
+ }
+ }
+
+ // This ldr will force the literal pool to be emitted before emitting
+ // the load and will create a new pool for the new literal used by this ldr.
+ VIXL_CHECK(!masm.LiteralPoolIsEmpty());
+ Literal<uint32_t> literal(test.literal_value);
+ (masm.*test.instruction)(test.result_reg, &literal);
+ ASSERT_LITERAL_POOL_SIZE(4);
+
+ END();
+
+ RUN();
+
+ ASSERT_EQUAL_32(0x12345678, r6);
+ ASSERT_EQUAL_32(0x90abcdef, r7);
+ ASSERT_EQUAL_32(test.test_value, test.result_reg);
+ }
+
+ TEARDOWN();
+}
+
+
+TEST(ldr_literal_trigger_pool_emission) {
+ GenerateLdrLiteralTriggerPoolEmission(isa, false);
+}
+
+
+TEST_T32(ldr_literal_trigger_pool_emission_unaligned) {
+ GenerateLdrLiteralTriggerPoolEmission(isa, true);
+}
+
+
+void GenerateLdrLiteralRangeTest(InstructionSet isa, bool unaligned_ldr) {
+ SETUP();
+
+ for (size_t i = 0; i < ARRAY_SIZE(kLdrLiteralRangeTestData); ++i) {
+ const LdrLiteralRangeTest& test = kLdrLiteralRangeTestData[i];
+
+ START();
+
+ // Make sure the pool is empty.
+ masm.EmitLiteralPool(MacroAssembler::kBranchRequired);
+ ASSERT_LITERAL_POOL_SIZE(0);
+
+ if (unaligned_ldr) {
+ // Generate a nop to break the 4-byte alignment.
+ __ Nop();
+ VIXL_ASSERT((masm.GetBuffer()->GetCursorOffset() % 4) == 2);
+ }
+
+ Literal<uint32_t> literal(test.literal_value);
+ (masm.*test.instruction)(test.result_reg, &literal);
+ ASSERT_LITERAL_POOL_SIZE(4);
+
+ // Generate enough instruction so that we go out of range for the load
+ // literal we just emitted.
+ ptrdiff_t end = masm.GetBuffer()->GetCursorOffset() +
+ ((masm.IsUsingA32()) ? test.a32_range : test.t32_range);
+ while (masm.GetBuffer()->GetCursorOffset() < end) {
+ __ Mov(r0, 0);
+ }
+
+ // The literal pool should have been emitted now.
+ VIXL_CHECK(literal.IsBound());
+ ASSERT_LITERAL_POOL_SIZE(0);
+
+ END();
+
+ RUN();
+
+ ASSERT_EQUAL_32(test.test_value, test.result_reg);
+ }
+
+ TEARDOWN();
+}
+
+
+TEST(ldr_literal_range) { GenerateLdrLiteralRangeTest(isa, false); }
+
+
+TEST_T32(ldr_literal_range_unaligned) {
+ GenerateLdrLiteralRangeTest(isa, true);
+}
+
+
+TEST(string_literal) {
+ SETUP();
+
+ START();
+ // Make sure the pool is empty.
+ masm.EmitLiteralPool(MacroAssembler::kBranchRequired);
+ ASSERT_LITERAL_POOL_SIZE(0);
+
+ StringLiteral hello_string("hello");
+
+ __ Ldrb(r1, &hello_string);
+
+ __ Adr(r0, &hello_string);
+ __ Ldrb(r2, MemOperand(r0));
+ END();
+
+ RUN();
+
+ ASSERT_EQUAL_32('h', r1);
+ ASSERT_EQUAL_32('h', r2);
+
+ TEARDOWN();
+}
+
+
+TEST(custom_literal_in_pool) {
+ SETUP();
+
+ START();
+ // Make sure the pool is empty.
+ masm.EmitLiteralPool(MacroAssembler::kBranchRequired);
+ ASSERT_LITERAL_POOL_SIZE(0);
+
+ Literal<uint32_t> l0(static_cast<uint32_t>(0x12345678));
+ __ Ldr(r0, &l0);
+ masm.EmitLiteralPool(MacroAssembler::kBranchRequired);
+ __ Ldr(r1, &l0);
+ ASSERT_LITERAL_POOL_SIZE(0);
+
+ Literal<uint64_t> cafebeefdeadbaba(0xcafebeefdeadbaba);
+ __ Ldrd(r8, r9, &cafebeefdeadbaba);
+ masm.EmitLiteralPool(MacroAssembler::kBranchRequired);
+ __ Ldrd(r2, r3, &cafebeefdeadbaba);
+ ASSERT_LITERAL_POOL_SIZE(0);
+
+ Literal<uint32_t> l1(0x09abcdef);
+ __ Adr(r4, &l1);
+ __ Ldr(r4, MemOperand(r4));
+ masm.EmitLiteralPool();
+ __ Adr(r5, &l1);
+ __ Ldr(r5, MemOperand(r5));
+ ASSERT_LITERAL_POOL_SIZE(0);
+
+ END();
+
+ RUN();
+
+ // Check that the literals loaded correctly.
+ ASSERT_EQUAL_32(0x12345678, r0);
+ ASSERT_EQUAL_32(0x12345678, r1);
+ ASSERT_EQUAL_32(0xdeadbaba, r2);
+ ASSERT_EQUAL_32(0xcafebeef, r3);
+ ASSERT_EQUAL_32(0xdeadbaba, r8);
+ ASSERT_EQUAL_32(0xcafebeef, r9);
+ ASSERT_EQUAL_32(0x09abcdef, r4);
+ ASSERT_EQUAL_32(0x09abcdef, r5);
+}
+
+
+TEST(custom_literal_place) {
+ SETUP();
+
+ START();
+ // Make sure the pool is empty.
+ masm.EmitLiteralPool(MacroAssembler::kBranchRequired);
+ ASSERT_LITERAL_POOL_SIZE(0);
+
+ Literal<uint64_t> l0(0xcafebeefdeadbaba, RawLiteral::kManuallyPlaced);
+ Literal<int32_t> l1(0x12345678, RawLiteral::kManuallyPlaced);
+ Literal<uint16_t> l2(4567, RawLiteral::kManuallyPlaced);
+ Literal<int16_t> l3(-4567, RawLiteral::kManuallyPlaced);
+ Literal<uint8_t> l4(123, RawLiteral::kManuallyPlaced);
+ Literal<int8_t> l5(-123, RawLiteral::kManuallyPlaced);
+
+ __ Ldrd(r0, r1, &l0);
+ __ Ldr(r2, &l1);
+ __ Ldrh(r3, &l2);
+ __ Ldrsh(r4, &l3);
+ __ Ldrb(r5, &l4);
+ __ Ldrsb(r6, &l5);
+
+ ASSERT_LITERAL_POOL_SIZE(0);
+
+ // Manually generate a literal pool.
+ Label after_pool;
+ __ B(&after_pool);
+ __ Place(&l0);
+ __ Place(&l1);
+ __ Place(&l2);
+ __ Place(&l3);
+ __ Place(&l4);
+ __ Place(&l5);
+ __ Bind(&after_pool);
+
+ {
+ UseScratchRegisterScope temps(&masm);
+ Register temp = temps.Acquire();
+ VIXL_CHECK(temp.Is(r12));
+
+ __ Ldrd(r8, r9, &l0);
+ __ Ldr(r7, &l1);
+ __ Ldrh(r10, &l2);
+ __ Ldrsh(r11, &l3);
+ __ Ldrb(temp, &l4);
+ // We don't use any function call so we can use lr as an extra register.
+ __ Ldrsb(lr, &l5);
+ }
+
+ ASSERT_LITERAL_POOL_SIZE(0);
+
+ END();
+
+ RUN();
+
+ // Check that the literals loaded correctly.
+ ASSERT_EQUAL_32(0xdeadbaba, r0);
+ ASSERT_EQUAL_32(0xcafebeef, r1);
+ ASSERT_EQUAL_32(0x12345678, r2);
+ ASSERT_EQUAL_32(4567, r3);
+ ASSERT_EQUAL_32(-4567, r4);
+ ASSERT_EQUAL_32(123, r5);
+ ASSERT_EQUAL_32(-123, r6);
+
+ ASSERT_EQUAL_32(0xdeadbaba, r8);
+ ASSERT_EQUAL_32(0xcafebeef, r9);
+ ASSERT_EQUAL_32(0x12345678, r7);
+ ASSERT_EQUAL_32(4567, r10);
+ ASSERT_EQUAL_32(-4567, r11);
+ ASSERT_EQUAL_32(123, r12);
+ ASSERT_EQUAL_32(-123, lr);
+
+ TEARDOWN();
+}
+
+
+TEST(custom_literal_place_shared) {
+ SETUP();
+
+ for (size_t i = 0; i < ARRAY_SIZE(kLdrLiteralRangeTestData); ++i) {
+ const LdrLiteralRangeTest& test = kLdrLiteralRangeTestData[i];
+
+ START();
+
+ // Make sure the pool is empty.
+ masm.EmitLiteralPool(MacroAssembler::kBranchRequired);
+ ASSERT_LITERAL_POOL_SIZE(0);
+
+ Literal<uint32_t> before(test.literal_value, RawLiteral::kManuallyPlaced);
+ Literal<uint32_t> after(test.literal_value, RawLiteral::kManuallyPlaced);
+
+ VIXL_CHECK(!before.IsBound());
+ VIXL_CHECK(!after.IsBound());
+
+ // Manually generate a pool.
+ Label end_of_pool_before;
+ __ B(&end_of_pool_before);
+ __ Place(&before);
+ __ Bind(&end_of_pool_before);
+
+ ASSERT_LITERAL_POOL_SIZE(0);
+ VIXL_CHECK(before.IsBound());
+ VIXL_CHECK(!after.IsBound());
+
+ // Load the entries several times to test that literals can be shared.
+ for (int i = 0; i < 20; i++) {
+ (masm.*test.instruction)(r0, &before);
+ (masm.*test.instruction)(r1, &after);
+ }
+
+ ASSERT_LITERAL_POOL_SIZE(0);
+ VIXL_CHECK(before.IsBound());
+ VIXL_CHECK(!after.IsBound());
+
+ // Manually generate a pool.
+ Label end_of_pool_after;
+ __ B(&end_of_pool_after);
+ __ Place(&after);
+ __ Bind(&end_of_pool_after);
+
+ ASSERT_LITERAL_POOL_SIZE(0);
+ VIXL_CHECK(before.IsBound());
+ VIXL_CHECK(after.IsBound());
+
+ END();
+
+ RUN();
+
+ ASSERT_EQUAL_32(test.test_value, r0);
+ ASSERT_EQUAL_32(test.test_value, r1);
+ }
+
+ TEARDOWN();
+}
+
+
+TEST(custom_literal_place_range) {
+ SETUP();
+
+ for (size_t i = 0; i < ARRAY_SIZE(kLdrLiteralRangeTestData); ++i) {
+ const LdrLiteralRangeTest& test = kLdrLiteralRangeTestData[i];
+ const int nop_size = masm.IsUsingA32() ? kA32InstructionSizeInBytes
+ : k16BitT32InstructionSizeInBytes;
+ const int range = masm.IsUsingA32() ? test.a32_range : test.t32_range;
+ // On T32 the PC will be 4-byte aligned to compute the range. The
+ // MacroAssembler might also need to align the code buffer before emitting
+ // the literal when placing it. We keep a margin to account for this.
+ const int margin = masm.IsUsingT32() ? 4 : 0;
+
+ // Take PC offset into account and make sure the literal is in the range.
+ const int padding_before =
+ range - masm.GetArchitectureStatePCOffset() - sizeof(uint32_t) - margin;
+
+ // The margin computation below is correct because the ranges are not
+ // 4-byte aligned. Otherwise this test would insert the exact number of
+ // instructions to cover the range and the literal would end up being
+ // placed outside the range.
+ VIXL_ASSERT((range % 4) != 0);
+
+ // The range is extended by the PC offset but we need to consider the ldr
+ // instruction itself and the branch over the pool.
+ const int padding_after = range + masm.GetArchitectureStatePCOffset() -
+ (2 * kMaxInstructionSizeInBytes) - margin;
+ START();
+
+ Literal<uint32_t> before(test.literal_value, RawLiteral::kManuallyPlaced);
+ Literal<uint32_t> after(test.literal_value, RawLiteral::kManuallyPlaced);
+
+ Label test_start;
+ __ B(&test_start);
+ __ Place(&before);
+
+ {
+ int space = AlignDown(padding_before, nop_size);
+ ExactAssemblyScope scope(&masm, space, ExactAssemblyScope::kExactSize);
+ for (int32_t end = masm.GetCursorOffset() + space;
+ masm.GetCursorOffset() < end;) {
+ __ nop();
+ }
+ }
+
+ __ Bind(&test_start);
+ (masm.*test.instruction)(r0, &before);
+ (masm.*test.instruction)(r1, &after);
+
+ {
+ int space = AlignDown(padding_after, nop_size);
+ ExactAssemblyScope scope(&masm, space, ExactAssemblyScope::kExactSize);
+ for (int32_t end = masm.GetCursorOffset() + space;
+ masm.GetCursorOffset() < end;) {
+ __ nop();
+ }
+ }
+
+ Label after_pool;
+ __ B(&after_pool);
+ __ Place(&after);
+ __ Bind(&after_pool);
+
+ END();
+
+ RUN();
+
+ ASSERT_EQUAL_32(test.test_value, r0);
+ ASSERT_EQUAL_32(test.test_value, r1);
+ }
+
+ TEARDOWN();
+}
+
+
+TEST(emit_big_pool) {
+ SETUP();
+
+ START();
+ // Make sure the pool is empty.
+ ASSERT_LITERAL_POOL_SIZE(0);
+
+ Label start;
+ __ Bind(&start);
+ for (int i = 1000; i > 0; --i) {
+ __ Ldr(r0, i);
+ }
+
+ VIXL_ASSERT(masm.GetSizeOfCodeGeneratedSince(&start) == 4000);
+
+ ASSERT_LITERAL_POOL_SIZE(4000);
+ END();
+
+ RUN();
+
+ // Check that the literals loaded correctly.
+ ASSERT_EQUAL_32(1, r0);
+
+ TEARDOWN();
+}
+
+
+TEST_T32(too_far_cbz) {
+ SETUP();
+
+ START();
+ Label start;
+ Label end;
+ Label exit;
+ __ Mov(r0, 0);
+ __ B(&start);
+ __ Bind(&end);
+ __ Mov(r0, 1);
+ __ B(&exit);
+ __ Bind(&start);
+ // Cbz is only defined for forward jump. Check that it will work (substituted
+ // by Cbnz/B).
+ __ Cbz(r0, &end);
+ __ Bind(&exit);
+ END();
+
+ RUN();
+
+ ASSERT_EQUAL_32(1, r0);
+}
+
+
+TEST_T32(close_cbz) {
+ SETUP();
+
+ START();
+ Label first;
+ Label second;
+ __ Mov(r0, 0);
+ __ Mov(r1, 0);
+ __ Mov(r2, 0);
+ __ Cbz(r0, &first);
+ __ Bind(&first);
+ __ Mov(r1, 1);
+ __ Cbnz(r0, &second);
+ __ Bind(&second);
+ __ Mov(r2, 2);
+ END();
+
+ RUN();
+
+ ASSERT_EQUAL_32(0, r0);
+ ASSERT_EQUAL_32(1, r1);
+ ASSERT_EQUAL_32(2, r2);
+}
+
+
+TEST_T32(close_cbz2) {
+ SETUP();
+
+ START();
+ Label first;
+ Label second;
+ __ Mov(r0, 0);
+ __ Mov(r1, 0);
+ __ Mov(r2, 0);
+ __ Cmp(r0, 0);
+ __ B(ne, &first);
+ __ B(gt, &second);
+ __ Cbz(r0, &first);
+ __ Bind(&first);
+ __ Mov(r1, 1);
+ __ Cbnz(r0, &second);
+ __ Bind(&second);
+ __ Mov(r2, 2);
+ END();
+
+ RUN();
+
+ ASSERT_EQUAL_32(0, r0);
+ ASSERT_EQUAL_32(1, r1);
+ ASSERT_EQUAL_32(2, r2);
+}
+
+
+TEST_T32(not_close_cbz) {
+ SETUP();
+
+ START();
+ Label first;
+ Label second;
+ __ Cbz(r0, &first);
+ __ B(ne, &first);
+ __ Bind(&first);
+ __ Cbnz(r0, &second);
+ __ B(gt, &second);
+ __ Bind(&second);
+ END();
+
+ RUN();
+}
+
+
+TEST_T32(veneers) {
+ SETUP();
+
+ START();
+ Label zero;
+ Label exit;
+ __ Mov(r0, 0);
+ // Create one literal pool entry.
+ __ Ldr(r1, 0x12345678);
+ ASSERT_LITERAL_POOL_SIZE(4);
+ __ Cbz(r0, &zero);
+ __ Mov(r0, 1);
+ __ B(&exit);
+ for (int i = 32; i > 0; i--) {
+ __ Mov(r1, 0);
+ }
+ // Assert that the literal pool has been generated with the veneers.
+ ASSERT_LITERAL_POOL_SIZE(0);
+ __ Bind(&zero);
+ __ Mov(r0, 2);
+ __ Bind(&exit);
+ END();
+
+ RUN();
+
+ ASSERT_EQUAL_32(2, r0);
+ ASSERT_EQUAL_32(0x12345678, r1);
+}
+
+
+// This test checks that veneers are sorted. If not, the test failed as the
+// veneer for "exit" is emitted before the veneer for "zero" and the "zero"
+// veneer is out of range for Cbz.
+TEST_T32(veneers_labels_sort) {
+ SETUP();
+
+ START();
+ Label start;
+ Label zero;
+ Label exit;
+ __ Movs(r0, 0);
+ __ B(ne, &exit);
+ __ B(&start);
+ for (int i = 1048400; i > 0; i -= 4) {
+ __ Mov(r1, 0);
+ }
+ __ Bind(&start);
+ __ Cbz(r0, &zero);
+ __ Mov(r0, 1);
+ __ B(&exit);
+ for (int i = 32; i > 0; i--) {
+ __ Mov(r1, 0);
+ }
+ __ Bind(&zero);
+ __ Mov(r0, 2);
+ __ Bind(&exit);
+ END();
+
+ RUN();
+
+ ASSERT_EQUAL_32(2, r0);
+}
+
+// Check that a label bound within the assembler is effectively removed from
+// the veneer pool.
+TEST_T32(veneer_bind) {
+ SETUP();
+ Label target;
+ __ Cbz(r0, &target);
+ __ Nop();
+
+ {
+ // Bind the target label using the `Assembler`.
+ ExactAssemblyScope scope(&masm,
+ kMaxInstructionSizeInBytes,
+ ExactAssemblyScope::kMaximumSize);
+ __ bind(&target);
+ __ nop();
+ }
+
+ VIXL_CHECK(target.IsBound());
+ VIXL_CHECK(masm.VeneerPoolIsEmpty());
+
+ END();
+}
+
+
+// Check that the veneer pool is correctly emitted even if we do enough narrow
+// branches before a cbz so that the cbz needs its veneer emitted first in the
+// pool in order to work.
+TEST_T32(b_narrow_and_cbz_sort) {
+ SETUP();
+ START();
+
+ const int kLabelsCount = 40;
+ const int kNops = 30;
+ Label b_labels[kLabelsCount];
+ Label cbz_label;
+
+ __ Nop();
+
+ __ Mov(r0, 0);
+ __ Cmp(r0, 0);
+
+ for (int i = 0; i < kLabelsCount; ++i) {
+ __ B(ne, &b_labels[i], kNear);
+ }
+
+ {
+ ExactAssemblyScope scope(&masm,
+ k16BitT32InstructionSizeInBytes * kNops,
+ ExactAssemblyScope::kExactSize);
+ for (int i = 0; i < kNops; i++) {
+ __ nop();
+ }
+ }
+
+ // The pool should not be emitted here.
+ __ Cbz(r0, &cbz_label);
+
+ // Force pool emission. If the labels are not sorted, the cbz will be out
+ // of range.
+ int32_t margin = masm.GetMarginBeforeVeneerEmission();
+ int32_t end = masm.GetCursorOffset() + margin;
+
+ {
+ ExactAssemblyScope scope(&masm, margin, ExactAssemblyScope::kExactSize);
+ while (masm.GetCursorOffset() < end) {
+ __ nop();
+ }
+ }
+
+ __ Mov(r0, 1);
+
+ for (int i = 0; i < kLabelsCount; ++i) {
+ __ Bind(&b_labels[i]);
+ }
+
+ __ Bind(&cbz_label);
+
+ END();
+
+ RUN();
+
+ ASSERT_EQUAL_32(0, r0);
+
+ TEARDOWN();
+}
+
+
+TEST_T32(b_narrow_and_cbz_sort_2) {
+ SETUP();
+ START();
+
+ const int kLabelsCount = 40;
+ const int kNops = 30;
+ Label b_labels[kLabelsCount];
+ Label cbz_label;
+
+ __ Mov(r0, 0);
+ __ Cmp(r0, 0);
+
+ for (int i = 0; i < kLabelsCount; ++i) {
+ __ B(ne, &b_labels[i], kNear);
+ }
+
+ {
+ ExactAssemblyScope scope(&masm,
+ k16BitT32InstructionSizeInBytes * kNops,
+ ExactAssemblyScope::kExactSize);
+ for (int i = 0; i < kNops; i++) {
+ __ nop();
+ }
+ }
+
+ // The pool should not be emitted here.
+ __ Cbz(r0, &cbz_label);
+
+ // Force pool emission. If the labels are not sorted, the cbz will be out
+ // of range.
+ int32_t margin = masm.GetMarginBeforeVeneerEmission();
+ int32_t end = masm.GetCursorOffset() + margin;
+
+ while (masm.GetCursorOffset() < end) __ Nop();
+
+ __ Mov(r0, 1);
+
+ for (int i = 0; i < kLabelsCount; ++i) {
+ __ Bind(&b_labels[i]);
+ }
+
+ __ Bind(&cbz_label);
+
+ END();
+
+ RUN();
+
+ ASSERT_EQUAL_32(0, r0);
+
+ TEARDOWN();
+}
+
+
+TEST_T32(long_branch) {
+ SETUP();
+ START();
+
+ for (int label_count = 128; label_count < 2048; label_count *= 2) {
+ Label* l = new Label[label_count];
+
+ for (int i = 0; i < label_count; i++) {
+ __ B(&l[i]);
+ }
+
+ for (int i = 0; i < label_count; i++) {
+ __ B(ne, &l[i]);
+ }
+
+ for (int i = 0; i < 261625; i++) {
+ __ Clz(r0, r0);
+ }
+
+ for (int i = label_count - 1; i >= 0; i--) {
+ __ Bind(&l[i]);
+ __ Nop();
+ }
+
+ delete[] l;
+ }
+
+ masm.FinalizeCode();
+
+ END();
+ RUN();
+ TEARDOWN();
+}
+
+
+TEST_T32(unaligned_branch_after_literal) {
+ SETUP();
+
+ START();
+
+ // This test manually places a 32-bit literal after a 16-bit branch
+ // which branches over the literal to an unaligned PC.
+ Literal<int32_t> l0(0x01234567, RawLiteral::kManuallyPlaced);
+
+ __ Ldr(r0, &l0);
+ ASSERT_LITERAL_POOL_SIZE(0);
+
+ masm.EmitLiteralPool(MacroAssembler::kBranchRequired);
+ ASSERT_LITERAL_POOL_SIZE(0);
+
+ // Manually generate a literal pool.
+ {
+ Label after_pool;
+ ExactAssemblyScope scope(&masm,
+ k16BitT32InstructionSizeInBytes + sizeof(int32_t),
+ CodeBufferCheckScope::kMaximumSize);
+ __ b(Narrow, &after_pool);
+ __ place(&l0);
+ VIXL_ASSERT((masm.GetBuffer()->GetCursorOffset() % 4) == 2);
+ __ bind(&after_pool);
+ }
+
+ ASSERT_LITERAL_POOL_SIZE(0);
+
+ END();
+
+ RUN();
+
+ // Check that the literal was loaded correctly.
+ ASSERT_EQUAL_32(0x01234567, r0);
+
+ TEARDOWN();
+}
+
+
+// This test check that we can update a Literal after usage.
+TEST(literal_update) {
+ SETUP();
+
+ START();
+ Label exit;
+ Literal<uint32_t>* a32 =
+ new Literal<uint32_t>(0xabcdef01, RawLiteral::kDeletedOnPoolDestruction);
+ Literal<uint64_t>* a64 =
+ new Literal<uint64_t>(UINT64_C(0xabcdef01abcdef01),
+ RawLiteral::kDeletedOnPoolDestruction);
+ __ Ldr(r0, a32);
+ __ Ldrd(r2, r3, a64);
+ __ EmitLiteralPool();
+ Literal<uint32_t>* b32 =
+ new Literal<uint32_t>(0x10fedcba, RawLiteral::kDeletedOnPoolDestruction);
+ Literal<uint64_t>* b64 =
+ new Literal<uint64_t>(UINT64_C(0x10fedcba10fedcba),
+ RawLiteral::kDeletedOnPoolDestruction);
+ __ Ldr(r1, b32);
+ __ Ldrd(r4, r5, b64);
+ // Update literals' values. "a32" and "a64" are already emitted. "b32" and
+ // "b64" will only be emitted when "END()" will be called.
+ a32->UpdateValue(0x12345678, masm.GetBuffer());
+ a64->UpdateValue(UINT64_C(0x13579bdf02468ace), masm.GetBuffer());
+ b32->UpdateValue(0x87654321, masm.GetBuffer());
+ b64->UpdateValue(UINT64_C(0x1032547698badcfe), masm.GetBuffer());
+ END();
+
+ RUN();
+
+ ASSERT_EQUAL_32(0x12345678, r0);
+ ASSERT_EQUAL_32(0x87654321, r1);
+ ASSERT_EQUAL_32(0x02468ace, r2);
+ ASSERT_EQUAL_32(0x13579bdf, r3);
+ ASSERT_EQUAL_32(0x98badcfe, r4);
+ ASSERT_EQUAL_32(0x10325476, r5);
+}
+
+
+TEST(claim_peek_poke) {
+ SETUP();
+
+ START();
+
+ Label start;
+ __ Bind(&start);
+ __ Claim(0);
+ __ Drop(0);
+ VIXL_CHECK((masm.GetCursorOffset() - start.GetLocation()) == 0);
+
+ __ Claim(32);
+ __ Ldr(r0, 0xcafe0000);
+ __ Ldr(r1, 0xcafe0001);
+ __ Ldr(r2, 0xcafe0002);
+ __ Poke(r0, 0);
+ __ Poke(r1, 4);
+ __ Poke(r2, 8);
+ __ Peek(r2, 0);
+ __ Peek(r0, 4);
+ __ Peek(r1, 8);
+ __ Drop(32);
+
+ END();
+
+ RUN();
+
+ ASSERT_EQUAL_32(0xcafe0001, r0);
+ ASSERT_EQUAL_32(0xcafe0002, r1);
+ ASSERT_EQUAL_32(0xcafe0000, r2);
+
+ TEARDOWN();
+}
+
+
+TEST(msr_i) {
+ SETUP();
+
+ START();
+ __ Mov(r0, 0xdead);
+ __ Mov(r1, 0xdead);
+ __ Mov(r2, 0xdead);
+ __ Mov(r3, 0xb);
+ __ Msr(APSR_nzcvqg, 0);
+ __ Mrs(r0, APSR);
+ __ Msr(APSR_nzcvqg, 0xffffffff);
+ __ Mrs(r1, APSR);
+ // Only modify nzcvq => keep previous g.
+ __ Lsl(r4, r3, 28);
+ __ Msr(APSR_nzcvq, r4);
+ __ Mrs(r2, APSR);
+ END();
+
+ RUN();
+
+ ASSERT_EQUAL_32(0x10, r0);
+ ASSERT_EQUAL_32(0xf80f0010, r1);
+ ASSERT_EQUAL_32(0xb00f0010, r2);
+
+ TEARDOWN();
+}
+
+
+TEST(vmrs_vmsr) {
+ SETUP();
+
+ START();
+ // Move some value to FPSCR and get them back to test vmsr/vmrs instructions.
+ __ Mov(r0, 0x2a000000);
+ __ Vmsr(FPSCR, r0);
+ __ Vmrs(RegisterOrAPSR_nzcv(r1.GetCode()), FPSCR);
+
+ __ Mov(r0, 0x5a000000);
+ __ Vmsr(FPSCR, r0);
+ __ Vmrs(RegisterOrAPSR_nzcv(r2.GetCode()), FPSCR);
+
+ // Move to APSR_nzcv.
+ __ Vmrs(RegisterOrAPSR_nzcv(pc.GetCode()), FPSCR);
+ __ Mrs(r3, APSR);
+ __ And(r3, r3, 0xf0000000);
+
+ END();
+
+ RUN();
+
+ ASSERT_EQUAL_32(0x2a000000, r1);
+ ASSERT_EQUAL_32(0x5a000000, r2);
+ ASSERT_EQUAL_32(0x50000000, r3);
+
+ TEARDOWN();
+}
+
+
+TEST(printf) {
+ SETUP();
+
+ START();
+ __ Mov(r0, 0xb00e0000);
+ __ Msr(APSR_nzcvqg, r0);
+ __ Mov(r0, sp);
+ __ Printf("sp=%x\n", r0);
+ // __ Printf("Hello world!\n");
+ __ Mov(r0, 0x1234);
+ __ Mov(r1, 0x5678);
+ StringLiteral literal("extra string");
+ __ Adr(r2, &literal);
+ __ Mov(r3, 5);
+ __ Mov(r4, 0xdead4444);
+ __ Mov(r5, 0xdead5555);
+ __ Mov(r6, 0xdead6666);
+ __ Mov(r7, 0xdead7777);
+ __ Mov(r8, 0xdead8888);
+ __ Mov(r9, 0xdead9999);
+ __ Mov(r10, 0xdeadaaaa);
+ __ Mov(r11, 0xdeadbbbb);
+ __ Vldr(d0, 1.2345);
+ __ Vldr(d1, 2.9876);
+ __ Vldr(s4, 1.3333);
+ __ Vldr(s5, 3.21);
+ __ Vldr(d3, 3.333);
+ __ Vldr(d4, 4.444);
+ __ Vldr(d5, 5.555);
+ __ Vldr(d6, 6.666);
+ __ Vldr(d7, 7.777);
+ __ Vldr(d8, 8.888);
+ __ Vldr(d9, 9.999);
+ __ Vldr(d10, 10.000);
+ __ Vldr(d11, 11.111);
+ __ Vldr(d12, 12.222);
+ __ Vldr(d13, 13.333);
+ __ Vldr(d14, 14.444);
+ __ Vldr(d15, 15.555);
+ __ Vldr(d16, 16.666);
+ __ Vldr(d17, 17.777);
+ __ Vldr(d18, 18.888);
+ __ Vldr(d19, 19.999);
+ __ Vldr(d20, 20.000);
+ __ Vldr(d21, 21.111);
+ __ Vldr(d22, 22.222);
+ __ Vldr(d23, 23.333);
+ __ Vldr(d24, 24.444);
+ __ Vldr(d25, 25.555);
+ __ Vldr(d26, 26.666);
+ __ Vldr(d27, 27.777);
+ __ Vldr(d28, 28.888);
+ __ Vldr(d29, 29.999);
+ __ Vldr(d30, 30.000);
+ __ Vldr(d31, 31.111);
+ {
+ UseScratchRegisterScope temps(&masm);
+ // For effective use as an inspection tool, Printf must work without any
+ // scratch registers.
+ VIXL_CHECK(r12.Is(temps.Acquire()));
+ __ Mov(r12, 0xdeadcccc);
+ VIXL_CHECK(masm.GetScratchRegisterList()->IsEmpty());
+
+ __ Printf("%% r0=%x r1=%x str=<%.*s>\n", r0, r1, r3, r2);
+ __ Printf("r0=%d r1=%d str=<%s>\n", r0, r1, r2);
+ __ Printf("d0=%g\n", d0);
+ __ Printf("s4=%g\n", s4);
+ __ Printf("d0=%g d1=%g s4=%g s5=%g\n", d0, d1, s4, s5);
+ __ Printf("d0=%g r0=%x s4=%g r1=%x\n", d0, r0, s4, r1);
+ __ Printf("r0=%x d0=%g r1=%x s4=%g\n", r0, d0, r1, s4);
+ __ Mov(r0, sp);
+ __ Printf("sp=%x\n", r0);
+ __ Mrs(r0, APSR);
+ // Only keep R/W fields.
+ __ Mov(r2, 0xf80f0200);
+ __ And(r0, r0, r2);
+ }
+ END();
+
+ RUN();
+
+ ASSERT_EQUAL_32(0xb00e0000, r0);
+ ASSERT_EQUAL_32(0x5678, r1);
+ ASSERT_EQUAL_32(5, r3);
+ ASSERT_EQUAL_32(0xdead4444, r4);
+ ASSERT_EQUAL_32(0xdead5555, r5);
+ ASSERT_EQUAL_32(0xdead6666, r6);
+ ASSERT_EQUAL_32(0xdead7777, r7);
+ ASSERT_EQUAL_32(0xdead8888, r8);
+ ASSERT_EQUAL_32(0xdead9999, r9);
+ ASSERT_EQUAL_32(0xdeadaaaa, r10);
+ ASSERT_EQUAL_32(0xdeadbbbb, r11);
+ ASSERT_EQUAL_32(0xdeadcccc, r12);
+ ASSERT_EQUAL_FP64(1.2345, d0);
+ ASSERT_EQUAL_FP64(2.9876, d1);
+ ASSERT_EQUAL_FP32(1.3333, s4);
+ ASSERT_EQUAL_FP32(3.21, s5);
+ ASSERT_EQUAL_FP64(4.444, d4);
+ ASSERT_EQUAL_FP64(5.555, d5);
+ ASSERT_EQUAL_FP64(6.666, d6);
+ ASSERT_EQUAL_FP64(7.777, d7);
+ ASSERT_EQUAL_FP64(8.888, d8);
+ ASSERT_EQUAL_FP64(9.999, d9);
+ ASSERT_EQUAL_FP64(10.000, d10);
+ ASSERT_EQUAL_FP64(11.111, d11);
+ ASSERT_EQUAL_FP64(12.222, d12);
+ ASSERT_EQUAL_FP64(13.333, d13);
+ ASSERT_EQUAL_FP64(14.444, d14);
+ ASSERT_EQUAL_FP64(15.555, d15);
+ ASSERT_EQUAL_FP64(16.666, d16);
+ ASSERT_EQUAL_FP64(17.777, d17);
+ ASSERT_EQUAL_FP64(18.888, d18);
+ ASSERT_EQUAL_FP64(19.999, d19);
+ ASSERT_EQUAL_FP64(20.000, d20);
+ ASSERT_EQUAL_FP64(21.111, d21);
+ ASSERT_EQUAL_FP64(22.222, d22);
+ ASSERT_EQUAL_FP64(23.333, d23);
+ ASSERT_EQUAL_FP64(24.444, d24);
+ ASSERT_EQUAL_FP64(25.555, d25);
+ ASSERT_EQUAL_FP64(26.666, d26);
+ ASSERT_EQUAL_FP64(27.777, d27);
+ ASSERT_EQUAL_FP64(28.888, d28);
+ ASSERT_EQUAL_FP64(29.999, d29);
+ ASSERT_EQUAL_FP64(30.000, d30);
+ ASSERT_EQUAL_FP64(31.111, d31);
+
+ TEARDOWN();
+}
+
+TEST(printf2) {
+ SETUP();
+
+ START();
+ __ Mov(r0, 0x1234);
+ __ Mov(r1, 0x5678);
+ __ Vldr(d0, 1.2345);
+ __ Vldr(s2, 2.9876);
+ __ Printf("d0=%g d1=%g r0=%x r1=%x\n", d0, s2, r0, r1);
+ END();
+
+ RUN();
+
+ TEARDOWN();
+}
+
+
+template <typename T>
+void CheckInstructionSetA32(const T& assm) {
+ VIXL_CHECK(assm.IsUsingA32());
+ VIXL_CHECK(!assm.IsUsingT32());
+ VIXL_CHECK(assm.GetInstructionSetInUse() == A32);
+}
+
+
+template <typename T>
+void CheckInstructionSetT32(const T& assm) {
+ VIXL_CHECK(assm.IsUsingT32());
+ VIXL_CHECK(!assm.IsUsingA32());
+ VIXL_CHECK(assm.GetInstructionSetInUse() == T32);
+}
+
+
+TEST_NOASM(set_isa_constructors) {
+ byte buffer[1024];
+
+#ifndef VIXL_INCLUDE_TARGET_T32_ONLY
+ // A32 by default.
+ CheckInstructionSetA32(Assembler());
+ CheckInstructionSetA32(Assembler(1024));
+ CheckInstructionSetA32(Assembler(buffer, sizeof(buffer)));
+
+ CheckInstructionSetA32(MacroAssembler());
+ CheckInstructionSetA32(MacroAssembler(1024));
+ CheckInstructionSetA32(MacroAssembler(buffer, sizeof(buffer)));
+#else
+ // T32 by default.
+ CheckInstructionSetT32(Assembler());
+ CheckInstructionSetT32(Assembler(1024));
+ CheckInstructionSetT32(Assembler(buffer, sizeof(buffer)));
+
+ CheckInstructionSetT32(MacroAssembler());
+ CheckInstructionSetT32(MacroAssembler(1024));
+ CheckInstructionSetT32(MacroAssembler(buffer, sizeof(buffer)));
+#endif
+
+#ifdef VIXL_INCLUDE_TARGET_A32
+ // Explicit A32.
+ CheckInstructionSetA32(Assembler(A32));
+ CheckInstructionSetA32(Assembler(1024, A32));
+ CheckInstructionSetA32(Assembler(buffer, sizeof(buffer), A32));
+
+ CheckInstructionSetA32(MacroAssembler(A32));
+ CheckInstructionSetA32(MacroAssembler(1024, A32));
+ CheckInstructionSetA32(MacroAssembler(buffer, sizeof(buffer), A32));
+#endif
+
+#ifdef VIXL_INCLUDE_TARGET_T32
+ // Explicit T32.
+ CheckInstructionSetT32(Assembler(T32));
+ CheckInstructionSetT32(Assembler(1024, T32));
+ CheckInstructionSetT32(Assembler(buffer, sizeof(buffer), T32));
+
+ CheckInstructionSetT32(MacroAssembler(T32));
+ CheckInstructionSetT32(MacroAssembler(1024, T32));
+ CheckInstructionSetT32(MacroAssembler(buffer, sizeof(buffer), T32));
+#endif
+}
+
+
+TEST_NOASM(set_isa_empty) {
+// It is possible to change the instruction set if no instructions have yet
+// been generated. This test only makes sense when both A32 and T32 are
+// supported.
+#ifdef VIXL_INCLUDE_TARGET_AARCH32
+ Assembler assm;
+ CheckInstructionSetA32(assm);
+ assm.UseT32();
+ CheckInstructionSetT32(assm);
+ assm.UseA32();
+ CheckInstructionSetA32(assm);
+ assm.UseInstructionSet(T32);
+ CheckInstructionSetT32(assm);
+ assm.UseInstructionSet(A32);
+ CheckInstructionSetA32(assm);
+
+ MacroAssembler masm;
+ CheckInstructionSetA32(masm);
+ masm.UseT32();
+ CheckInstructionSetT32(masm);
+ masm.UseA32();
+ CheckInstructionSetA32(masm);
+ masm.UseInstructionSet(T32);
+ CheckInstructionSetT32(masm);
+ masm.UseInstructionSet(A32);
+ CheckInstructionSetA32(masm);
+#endif
+}
+
+
+TEST_NOASM(set_isa_noop) {
+// It is possible to call a no-op UseA32/T32 or UseInstructionSet even if
+// one or more instructions have been generated.
+#ifdef VIXL_INCLUDE_TARGET_A32
+ {
+ Assembler assm(A32);
+ CheckInstructionSetA32(assm);
+ CodeBufferCheckScope scope(&assm, kMaxInstructionSizeInBytes);
+ assm.bx(lr);
+ VIXL_ASSERT(assm.GetCursorOffset() > 0);
+ CheckInstructionSetA32(assm);
+ assm.UseA32();
+ CheckInstructionSetA32(assm);
+ assm.UseInstructionSet(A32);
+ CheckInstructionSetA32(assm);
+ assm.FinalizeCode();
+ }
+ {
+ MacroAssembler masm(A32);
+ CheckInstructionSetA32(masm);
+ masm.Bx(lr);
+ VIXL_ASSERT(masm.GetCursorOffset() > 0);
+ CheckInstructionSetA32(masm);
+ masm.UseA32();
+ CheckInstructionSetA32(masm);
+ masm.UseInstructionSet(A32);
+ CheckInstructionSetA32(masm);
+ masm.FinalizeCode();
+ }
+#endif
+
+#ifdef VIXL_INCLUDE_TARGET_T32
+ {
+ Assembler assm(T32);
+ CheckInstructionSetT32(assm);
+ CodeBufferCheckScope scope(&assm, kMaxInstructionSizeInBytes);
+ assm.bx(lr);
+ VIXL_ASSERT(assm.GetCursorOffset() > 0);
+ CheckInstructionSetT32(assm);
+ assm.UseT32();
+ CheckInstructionSetT32(assm);
+ assm.UseInstructionSet(T32);
+ CheckInstructionSetT32(assm);
+ assm.FinalizeCode();
+ }
+ {
+ MacroAssembler masm(T32);
+ CheckInstructionSetT32(masm);
+ masm.Bx(lr);
+ VIXL_ASSERT(masm.GetCursorOffset() > 0);
+ CheckInstructionSetT32(masm);
+ masm.UseT32();
+ CheckInstructionSetT32(masm);
+ masm.UseInstructionSet(T32);
+ CheckInstructionSetT32(masm);
+ masm.FinalizeCode();
+ }
+#endif
+}
+
+
+TEST(logical_arithmetic_identities) {
+ SETUP();
+
+ START();
+
+ Label blob_1;
+ __ Bind(&blob_1);
+ __ Add(r0, r0, 0);
+ __ And(r0, r0, 0xffffffff);
+ __ Bic(r0, r0, 0);
+ __ Eor(r0, r0, 0);
+ __ Orn(r0, r0, 0xffffffff);
+ __ Orr(r0, r0, 0);
+ __ Sub(r0, r0, 0);
+ VIXL_ASSERT(masm.GetSizeOfCodeGeneratedSince(&blob_1) == 0);
+
+ Label blob_2;
+ __ Bind(&blob_2);
+ __ Adds(r0, r0, 0);
+ VIXL_ASSERT(masm.GetSizeOfCodeGeneratedSince(&blob_2) != 0);
+
+ Label blob_3;
+ __ Bind(&blob_3);
+ __ Ands(r0, r0, 0);
+ VIXL_ASSERT(masm.GetSizeOfCodeGeneratedSince(&blob_3) != 0);
+
+ Label blob_4;
+ __ Bind(&blob_4);
+ __ Bics(r0, r0, 0);
+ VIXL_ASSERT(masm.GetSizeOfCodeGeneratedSince(&blob_4) != 0);
+
+ Label blob_5;
+ __ Bind(&blob_5);
+ __ Eors(r0, r0, 0);
+ VIXL_ASSERT(masm.GetSizeOfCodeGeneratedSince(&blob_5) != 0);
+
+ Label blob_6;
+ __ Bind(&blob_6);
+ __ Orns(r0, r0, 0);
+ VIXL_ASSERT(masm.GetSizeOfCodeGeneratedSince(&blob_6) != 0);
+
+ Label blob_7;
+ __ Bind(&blob_7);
+ __ Orrs(r0, r0, 0);
+ VIXL_ASSERT(masm.GetSizeOfCodeGeneratedSince(&blob_7) != 0);
+
+ Label blob_8;
+ __ Bind(&blob_8);
+ __ Subs(r0, r0, 0);
+ VIXL_ASSERT(masm.GetSizeOfCodeGeneratedSince(&blob_8) != 0);
+
+ __ Mov(r0, 0xbad);
+ __ And(r1, r0, 0);
+ __ Bic(r2, r0, 0xffffffff);
+ __ Eor(r3, r0, 0xffffffff);
+ __ Orn(r4, r0, 0);
+ __ Orr(r5, r0, 0xffffffff);
+
+ END();
+
+ RUN();
+
+ ASSERT_EQUAL_32(0xbad, r0);
+ ASSERT_EQUAL_32(0, r1);
+ ASSERT_EQUAL_32(0, r2);
+ ASSERT_EQUAL_32(~0xbad, r3);
+ ASSERT_EQUAL_32(0xffffffff, r4);
+ ASSERT_EQUAL_32(0xffffffff, r5);
+
+ TEARDOWN();
+}
+
+
+TEST(scratch_register_checks) {
+ // It is unsafe for users to use registers that the MacroAssembler is also
+ // using as scratch registers. This test checks the MacroAssembler's checking
+ // mechanism itself.
+ SETUP();
+ START();
+ {
+ UseScratchRegisterScope temps(&masm);
+ // 'ip' is a scratch register by default.
+ VIXL_CHECK(masm.GetScratchRegisterList()->GetList() ==
+ (1u << ip.GetCode()));
+ VIXL_CHECK(temps.IsAvailable(ip));
+
+ // Integer registers have no complicated aliasing so
+ // masm.AliasesAvailableScratchRegister(reg) == temps.IsAvailable(reg).
+ for (unsigned i = 0; i < kNumberOfRegisters; i++) {
+ Register reg(i);
+ VIXL_CHECK(masm.AliasesAvailableScratchRegister(reg) ==
+ temps.IsAvailable(reg));
+ }
+ }
+ END();
+ TEARDOWN();
+}
+
+
+TEST(scratch_register_checks_v) {
+ // It is unsafe for users to use registers that the MacroAssembler is also
+ // using as scratch registers. This test checks the MacroAssembler's checking
+ // mechanism itself.
+ SETUP();
+ {
+ UseScratchRegisterScope temps(&masm);
+ // There is no default floating-point scratch register. Add temps of various
+ // sizes to check handling of aliased registers.
+ VIXL_CHECK(masm.GetScratchVRegisterList()->GetList() == 0);
+ temps.Include(q15);
+ temps.Include(d15);
+ temps.Include(s15);
+ temps.Include(d4);
+ temps.Include(d5);
+ temps.Include(s24);
+ temps.Include(s25);
+ temps.Include(s26);
+ temps.Include(s27);
+ temps.Include(q0);
+ // See VRegisterList for details of the list encoding.
+ VIXL_CHECK(masm.GetScratchVRegisterList()->GetList() ==
+ UINT64_C(0xf0000000cf008f0f));
+ // | || || |
+ // q15 d15| || q0
+ // s24-s27 |d4-d5
+ // s15
+
+ // Simple checks: Included registers are available.
+ VIXL_CHECK(temps.IsAvailable(q15));
+ VIXL_CHECK(temps.IsAvailable(d15));
+ VIXL_CHECK(temps.IsAvailable(s15));
+ VIXL_CHECK(temps.IsAvailable(d4));
+ VIXL_CHECK(temps.IsAvailable(d5));
+ VIXL_CHECK(temps.IsAvailable(s24));
+ VIXL_CHECK(temps.IsAvailable(s25));
+ VIXL_CHECK(temps.IsAvailable(s26));
+ VIXL_CHECK(temps.IsAvailable(s27));
+ VIXL_CHECK(temps.IsAvailable(q0));
+
+ // Each available S register should mark the corresponding D and Q registers
+ // as aliasing an available scratch register.
+ for (unsigned s = 0; s < kNumberOfSRegisters; s++) {
+ if (temps.IsAvailable(SRegister(s))) {
+ VIXL_CHECK(masm.AliasesAvailableScratchRegister(SRegister(s)));
+ VIXL_CHECK(masm.AliasesAvailableScratchRegister(DRegister(s / 2)));
+ VIXL_CHECK(masm.AliasesAvailableScratchRegister(QRegister(s / 4)));
+ } else {
+ // AliasesAvailableScratchRegiters == IsAvailable for S registers.
+ VIXL_CHECK(!masm.AliasesAvailableScratchRegister(SRegister(s)));
+ }
+ }
+
+ // Similar checks for high D registers.
+ unsigned first_high_d_register = kNumberOfSRegisters / 2;
+ for (unsigned d = first_high_d_register; d < kMaxNumberOfDRegisters; d++) {
+ if (temps.IsAvailable(DRegister(d))) {
+ VIXL_CHECK(masm.AliasesAvailableScratchRegister(DRegister(d)));
+ VIXL_CHECK(masm.AliasesAvailableScratchRegister(QRegister(d / 2)));
+ } else {
+ // AliasesAvailableScratchRegiters == IsAvailable for high D registers.
+ VIXL_CHECK(!masm.AliasesAvailableScratchRegister(DRegister(d)));
+ }
+ }
+ }
+ TEARDOWN();
+}
+
+
+TEST(nop) {
+ SETUP();
+
+ Label start;
+ __ Bind(&start);
+ __ Nop();
+ size_t nop_size = (isa == T32) ? k16BitT32InstructionSizeInBytes
+ : kA32InstructionSizeInBytes;
+ // `MacroAssembler::Nop` must generate at least one nop.
+ VIXL_CHECK(masm.GetSizeOfCodeGeneratedSince(&start) >= nop_size);
+
+ masm.FinalizeCode();
+
+ TEARDOWN();
+}
+
+
+// Check that `GetMarginBeforeLiteralEmission()` is precise.
+TEST(literal_pool_margin) {
+ SETUP();
+
+ START();
+
+ VIXL_CHECK(masm.VeneerPoolIsEmpty());
+ VIXL_CHECK(masm.LiteralPoolIsEmpty());
+
+ // Create a single literal.
+ __ Ldrd(r0, r1, 0x1234567890abcdef);
+
+ VIXL_CHECK(!masm.LiteralPoolIsEmpty());
+
+ // Generate code to fill all the margin we have before generating the literal
+ // pool.
+ int32_t margin = masm.GetMarginBeforeLiteralEmission();
+ int32_t end = masm.GetCursorOffset() + margin;
+ {
+ ExactAssemblyScope scope(&masm, margin, ExactAssemblyScope::kExactSize);
+ // Opening the scope should not have triggered the emission of the literal
+ // pool.
+ VIXL_CHECK(!masm.LiteralPoolIsEmpty());
+ while (masm.GetCursorOffset() < end) {
+ __ nop();
+ }
+ VIXL_CHECK(masm.GetCursorOffset() == end);
+ }
+
+ // There should be no margin left to emit the literal pool.
+ VIXL_CHECK(!masm.LiteralPoolIsEmpty());
+ VIXL_CHECK(masm.GetMarginBeforeLiteralEmission() == 0);
+
+ // So emitting a single instruction should force emission of the pool.
+ __ Nop();
+ VIXL_CHECK(masm.LiteralPoolIsEmpty());
+ END();
+
+ RUN();
+
+ // Check that the literals loaded correctly.
+ ASSERT_EQUAL_32(0x90abcdef, r0);
+ ASSERT_EQUAL_32(0x12345678, r1);
+
+ TEARDOWN();
+}
+
+
+// Check that `GetMarginBeforeVeneerEmission()` is precise.
+TEST(veneer_pool_margin) {
+ SETUP();
+
+ START();
+
+ VIXL_CHECK(masm.VeneerPoolIsEmpty());
+ VIXL_CHECK(masm.LiteralPoolIsEmpty());
+
+ // Create a single veneer.
+ Label target;
+ __ B(eq, &target);
+
+ VIXL_CHECK(!masm.VeneerPoolIsEmpty());
+
+ // Generate code to fill all the margin we have before generating the veneer
+ // pool.
+ int32_t margin = masm.GetMarginBeforeVeneerEmission();
+ int32_t end = masm.GetCursorOffset() + margin;
+ {
+ ExactAssemblyScope scope(&masm, margin, ExactAssemblyScope::kExactSize);
+ // Opening the scope should not have triggered the emission of the veneer
+ // pool.
+ VIXL_CHECK(!masm.VeneerPoolIsEmpty());
+ while (masm.GetCursorOffset() < end) {
+ __ nop();
+ }
+ VIXL_CHECK(masm.GetCursorOffset() == end);
+ }
+ // There should be no margin left to emit the veneer pool.
+ VIXL_CHECK(masm.GetMarginBeforeVeneerEmission() == 0);
+
+ // So emitting a single instruction should force emission of the pool.
+ // We cannot simply check that the veneer pool is empty, because the veneer
+ // emitted for the CBZ instruction above is itself tracked by the veneer
+ // mechanisms. Instead, check that some 'unexpected' code is generated.
+ Label check;
+ __ Bind(&check);
+ {
+ ExactAssemblyScope scope(&masm, 2, ExactAssemblyScope::kMaximumSize);
+ // Do not actually generate any code.
+ }
+ VIXL_CHECK(masm.GetSizeOfCodeGeneratedSince(&check) > 0);
+ __ Bind(&target);
+ VIXL_CHECK(masm.VeneerPoolIsEmpty());
+
+ END();
+
+ RUN();
+
+ TEARDOWN();
+}
+
+
+TEST_T32(near_branch_fuzz) {
+ SETUP();
+ START();
+
+ uint16_t seed[3] = {1, 2, 3};
+ seed48(seed);
+
+ const int label_count = 31;
+ bool allbound;
+ Label* l;
+
+ // Use multiple iterations, as each produces a different predictably random
+ // sequence.
+ const int iterations = 64;
+
+ int loop_count = 0;
+ __ Mov(r1, 0);
+
+ // Initialise the status flags to Z set.
+ __ Cmp(r1, r1);
+
+ // Gradually increasing the number of cases effectively increases the
+ // probability of nops being emitted in the sequence. The branch-to-bind
+ // ratio in the sequence is fixed at 4:1 by the ratio of cases.
+ for (int case_count = 6; case_count < 37; case_count++) {
+ for (int iter = 0; iter < iterations; iter++) {
+ // Reset local state.
+ allbound = false;
+ l = new Label[label_count];
+
+ // Set r0 != 0 to force no branches to be taken. Also acts as a marker
+ // between each iteration in the disassembly.
+ __ Mov(r0, 1);
+
+ for (;;) {
+ uint32_t inst_case = static_cast<uint32_t>(mrand48()) % case_count;
+ uint32_t label_index = static_cast<uint32_t>(mrand48()) % label_count;
+
+ switch (inst_case) {
+ case 0: // Bind.
+ if (!l[label_index].IsBound()) {
+ __ Bind(&l[label_index]);
+
+ // We should hit each label exactly once (because the branches are
+ // never taken). Keep a counter to verify this.
+ loop_count++;
+ __ Add(r1, r1, 1);
+ }
+ break;
+ case 1: // Compare and branch if zero (untaken as r0 == 1).
+ __ Cbz(r0, &l[label_index]);
+ break;
+ case 2: { // Compare and branch if not zero.
+ Label past_branch;
+ __ B(eq, &past_branch, kNear);
+ __ Cbnz(r0, &l[label_index]);
+ __ Bind(&past_branch);
+ break;
+ }
+ case 3: { // Unconditional branch preferred near.
+ Label past_branch;
+ __ B(eq, &past_branch, kNear);
+ __ B(&l[label_index], kNear);
+ __ Bind(&past_branch);
+ break;
+ }
+ case 4: // Conditional branch (untaken as Z set) preferred near.
+ __ B(ne, &l[label_index], kNear);
+ break;
+ default: // Nop.
+ __ Nop();
+ break;
+ }
+
+ // If all labels have been bound, exit the inner loop and finalise the
+ // code.
+ allbound = true;
+ for (int i = 0; i < label_count; i++) {
+ allbound = allbound && l[i].IsBound();
+ }
+ if (allbound) break;
+ }
+
+ // Ensure that the veneer pools are emitted, to keep each branch/bind test
+ // independent. We will generate more code following this.
+ masm.FinalizeCode(MacroAssembler::kFallThrough);
+ delete[] l;
+ }
+ }
+
+ END();
+ RUN();
+
+ ASSERT_EQUAL_32(loop_count, r1);
+
+ TEARDOWN();
+}
+
+
+TEST_T32(near_branch_and_literal_fuzz) {
+ SETUP();
+ START();
+
+ uint16_t seed[3] = {1, 2, 3};
+ seed48(seed);
+
+ const int label_count = 15;
+ const int literal_count = 31;
+ bool allbound;
+ Label* labels;
+ uint64_t* literal_values;
+ Literal<uint64_t>* literals[literal_count];
+
+ // Use multiple iterations, as each produces a different predictably random
+ // sequence.
+ const int iterations = 128;
+ const int n_cases = 20;
+
+ int loop_count = 0;
+ __ Mov(r1, 0);
+
+ // If the value of r4 changes then the test fails.
+ __ Mov(r4, 42);
+
+ // This test generates a mix of 20 different code sequences (see switch case
+ // below). The cases are split in 4 groups:
+ //
+ // - 0..3: Generate various amount of nops.
+ // - 4..7: Generate various load intstructions with literals.
+ // - 8..14: Generate various branch instructions.
+ // - 15..19: Generate various amount of nops.
+ //
+ // The idea behind this is that we can have a window of size N which we can
+ // slide across these cases. And as a result, randomly generate sequences with
+ // a different ratio of:
+ // - "nops vs literals"
+ // - "literal vs veneers"
+ // - "veneers vs nops"
+ //
+ // In this test, we grow a window from 5 to 14, and then slide this window
+ // across all cases each time. We call this sliding a "ratio", which is in
+ // fact an offset from the first case of the switch.
+
+ for (uint32_t window = 5; window < 14; window++) {
+ for (uint32_t ratio = 0; ratio < static_cast<uint32_t>(n_cases - window);
+ ratio++) {
+ for (int iter = 0; iter < iterations; iter++) {
+ Label fail;
+ Label end;
+
+ // Reset local state.
+ allbound = false;
+ labels = new Label[label_count];
+
+ // Create new literal values.
+ literal_values = new uint64_t[literal_count];
+ for (int lit = 0; lit < literal_count; lit++) {
+ // TODO: Generate pseudo-random data for literals. At the moment, the
+ // disassembler breaks if we do this.
+ literal_values[lit] = lit;
+ literals[lit] = new Literal<uint64_t>(literal_values[lit]);
+ }
+
+ for (;;) {
+ uint32_t inst_case =
+ (static_cast<uint32_t>(mrand48()) % window) + ratio;
+ uint32_t label_index = static_cast<uint32_t>(mrand48()) % label_count;
+ uint32_t literal_index =
+ static_cast<uint32_t>(mrand48()) % literal_count;
+
+ if (inst_case == ratio) {
+ if (!labels[label_index].IsBound()) {
+ __ Bind(&labels[label_index]);
+
+ // We should hit each label exactly once (because the branches are
+ // never taken). Keep a counter to verify this.
+ loop_count++;
+ __ Add(r1, r1, 1);
+ continue;
+ }
+ }
+
+ switch (inst_case) {
+ case 0:
+ __ Nop();
+ break;
+ case 1:
+ __ Nop();
+ __ Nop();
+ __ Nop();
+ __ Nop();
+ __ Nop();
+ __ Nop();
+ __ Nop();
+ __ Nop();
+ __ Nop();
+ break;
+ case 2:
+ __ Nop();
+ __ Nop();
+ __ Nop();
+ break;
+ case 3:
+ __ Nop();
+ __ Nop();
+ __ Nop();
+ __ Nop();
+ __ Nop();
+ __ Nop();
+ __ Nop();
+ break;
+ case 4:
+ __ Ldr(r2, literals[literal_index]);
+ __ Cmp(r2, static_cast<uint32_t>(literal_values[literal_index]));
+ __ B(ne, &fail);
+ __ Mov(r2, 0);
+ break;
+ case 5:
+ __ Ldrb(r2, literals[literal_index]);
+ __ Cmp(r2,
+ static_cast<uint32_t>(literal_values[literal_index]) &
+ 0xff);
+ __ B(ne, &fail);
+ __ Mov(r2, 0);
+ break;
+ case 6:
+ __ Ldrd(r2, r3, literals[literal_index]);
+ __ Cmp(r2, static_cast<uint32_t>(literal_values[literal_index]));
+ __ B(ne, &fail);
+ __ Mov(r2, 0);
+ __ Cmp(r3,
+ static_cast<uint32_t>(literal_values[literal_index] >>
+ 32));
+ __ B(ne, &fail);
+ __ Mov(r3, 0);
+ break;
+ case 7:
+ __ Vldr(s0, literals[literal_index]);
+ __ Vmov(s1, static_cast<uint32_t>(literal_values[literal_index]));
+ __ Vcmp(s0, s1);
+ __ B(ne, &fail);
+ __ Vmov(s0, 0);
+ break;
+ case 8: {
+ Label past_branch;
+ __ B(&past_branch, kNear);
+ __ Cbz(r0, &labels[label_index]);
+ __ Bind(&past_branch);
+ break;
+ }
+ case 9: {
+ Label past_branch;
+ __ B(&past_branch, kNear);
+ __ Cbnz(r0, &labels[label_index]);
+ __ Bind(&past_branch);
+ break;
+ }
+ case 10: {
+ Label past_branch;
+ __ B(&past_branch, kNear);
+ __ B(ne, &labels[label_index], kNear);
+ __ Bind(&past_branch);
+ break;
+ }
+ case 11: {
+ Label past_branch;
+ __ B(&past_branch, kNear);
+ __ B(&labels[label_index], kNear);
+ __ Bind(&past_branch);
+ break;
+ }
+ case 12: {
+ Label past_branch;
+ __ B(&past_branch, kNear);
+ __ B(ne, &labels[label_index]);
+ __ Bind(&past_branch);
+ break;
+ }
+ case 13: {
+ Label past_branch;
+ __ B(&past_branch, kNear);
+ __ B(&labels[label_index]);
+ __ Bind(&past_branch);
+ break;
+ }
+ case 14: {
+ Label past_branch;
+ __ B(&past_branch, kNear);
+ __ Bl(&labels[label_index]);
+ __ Bind(&past_branch);
+ break;
+ }
+ case 15:
+ __ Nop();
+ __ Nop();
+ __ Nop();
+ __ Nop();
+ __ Nop();
+ break;
+ case 16:
+ __ Nop();
+ __ Nop();
+ __ Nop();
+ __ Nop();
+ __ Nop();
+ __ Nop();
+ break;
+ case 17:
+ __ Nop();
+ __ Nop();
+ __ Nop();
+ __ Nop();
+ break;
+ case 18:
+ __ Nop();
+ __ Nop();
+ __ Nop();
+ __ Nop();
+ __ Nop();
+ __ Nop();
+ __ Nop();
+ __ Nop();
+ break;
+ case 19:
+ __ Nop();
+ __ Nop();
+ break;
+ default:
+ VIXL_UNREACHABLE();
+ break;
+ }
+
+ // If all labels have been bound, exit the inner loop and finalise the
+ // code.
+ allbound = true;
+ for (int i = 0; i < label_count; i++) {
+ allbound = allbound && labels[i].IsBound();
+ }
+ if (allbound) break;
+ }
+
+ __ B(&end);
+ __ Bind(&fail);
+ __ Mov(r4, 0);
+ __ Bind(&end);
+
+ // Ensure that the veneer pools are emitted, to keep each branch/bind
+ // test
+ // independent.
+ masm.FinalizeCode(MacroAssembler::kFallThrough);
+ delete[] labels;
+ for (int lit = 0; lit < literal_count; lit++) {
+ delete literals[lit];
+ }
+ }
+ }
+ }
+
+ END();
+ RUN();
+
+ ASSERT_EQUAL_32(loop_count, r1);
+ ASSERT_EQUAL_32(42, r4);
+
+ TEARDOWN();
+}
+
+
+#ifdef VIXL_INCLUDE_TARGET_T32
+TEST_NOASM(code_buffer_precise_growth) {
+ static const int kBaseBufferSize = 16;
+ MacroAssembler masm(kBaseBufferSize, T32);
+
+ VIXL_CHECK(masm.GetBuffer()->GetCapacity() == kBaseBufferSize);
+
+ {
+ // Fill the buffer with nops.
+ ExactAssemblyScope scope(&masm,
+ kBaseBufferSize,
+ ExactAssemblyScope::kExactSize);
+ for (int i = 0; i < kBaseBufferSize; i += k16BitT32InstructionSizeInBytes) {
+ __ nop();
+ }
+ }
+
+ // The buffer should not have grown yet.
+ VIXL_CHECK(masm.GetBuffer()->GetCapacity() == kBaseBufferSize);
+
+ // Generating a single instruction should force the buffer to grow.
+ __ Nop();
+
+ VIXL_CHECK(masm.GetBuffer()->GetCapacity() > kBaseBufferSize);
+
+ masm.FinalizeCode();
+}
+#endif
+
+
+#ifdef VIXL_INCLUDE_TARGET_T32
+TEST_NOASM(out_of_space_immediately_before_PerformEnsureEmit) {
+ static const int kBaseBufferSize = 64;
+ MacroAssembler masm(kBaseBufferSize, T32);
+
+ VIXL_CHECK(masm.GetBuffer()->GetCapacity() == kBaseBufferSize);
+
+ VIXL_CHECK(masm.VeneerPoolIsEmpty());
+ VIXL_CHECK(masm.LiteralPoolIsEmpty());
+
+ // Create a veneer.
+ Label target;
+ __ Cbz(r0, &target);
+
+ VIXL_CHECK(!masm.VeneerPoolIsEmpty());
+
+ VIXL_CHECK(IsUint32(masm.GetBuffer()->GetRemainingBytes()));
+ uint32_t space = static_cast<uint32_t>(masm.GetBuffer()->GetRemainingBytes());
+ {
+ // Fill the buffer with nops.
+ ExactAssemblyScope scope(&masm, space, ExactAssemblyScope::kExactSize);
+ for (uint32_t i = 0; i < space; i += k16BitT32InstructionSizeInBytes) {
+ __ nop();
+ }
+ }
+
+ VIXL_CHECK(!masm.VeneerPoolIsEmpty());
+
+ // The buffer should not have grown yet, and there should be no space left.
+ VIXL_CHECK(masm.GetBuffer()->GetCapacity() == kBaseBufferSize);
+ VIXL_CHECK(masm.GetBuffer()->GetRemainingBytes() == 0);
+
+ // Force emission of the veneer, at a point where there is no space available
+ // in the buffer.
+ int32_t past_cbz_range = masm.GetMarginBeforeVeneerEmission() + 1;
+ masm.EnsureEmitFor(past_cbz_range);
+
+ __ Bind(&target);
+
+ VIXL_CHECK(masm.VeneerPoolIsEmpty());
+
+ masm.FinalizeCode();
+}
+#endif
+
+
+TEST_T32(distant_literal_references) {
+ SETUP();
+ START();
+
+ Literal<uint64_t>* literal =
+ new Literal<uint64_t>(UINT64_C(0x0123456789abcdef),
+ RawLiteral::kPlacedWhenUsed,
+ RawLiteral::kDeletedOnPoolDestruction);
+ // Refer to the literal so that it is emitted early.
+ __ Ldr(r0, literal);
+
+ // Add enough nops to exceed the range of all loads.
+ int space = 5000;
+ {
+ ExactAssemblyScope scope(&masm, space, CodeBufferCheckScope::kExactSize);
+ VIXL_ASSERT(masm.IsUsingT32());
+ for (int i = 0; i < space; i += k16BitT32InstructionSizeInBytes) {
+ __ nop();
+ }
+ }
+
+#define ENSURE_ALIGNED() \
+ do { \
+ if (!IsMultiple<k32BitT32InstructionSizeInBytes>( \
+ masm.GetCursorOffset())) { \
+ ExactAssemblyScope scope(&masm, \
+ k16BitT32InstructionSizeInBytes, \
+ ExactAssemblyScope::kExactSize); \
+ __ nop(); \
+ } \
+ VIXL_ASSERT( \
+ IsMultiple<k32BitT32InstructionSizeInBytes>(masm.GetCursorOffset())); \
+ } while (0)
+
+ // The literal has already been emitted, and is out of range of all of these
+ // instructions. The delegates must generate fix-up code.
+ ENSURE_ALIGNED();
+ __ Ldr(r1, literal);
+ ENSURE_ALIGNED();
+ __ Ldrb(r2, literal);
+ ENSURE_ALIGNED();
+ __ Ldrsb(r3, literal);
+ ENSURE_ALIGNED();
+ __ Ldrh(r4, literal);
+ ENSURE_ALIGNED();
+ __ Ldrsh(r5, literal);
+ ENSURE_ALIGNED();
+ __ Ldrd(r6, r7, literal);
+ ENSURE_ALIGNED();
+ __ Vldr(d0, literal);
+ ENSURE_ALIGNED();
+ __ Vldr(s3, literal);
+
+#undef ENSURE_ALIGNED
+
+ END();
+ RUN();
+
+ // Check that the literals loaded correctly.
+ ASSERT_EQUAL_32(0x89abcdef, r0);
+ ASSERT_EQUAL_32(0x89abcdef, r1);
+ ASSERT_EQUAL_32(0xef, r2);
+ ASSERT_EQUAL_32(0xffffffef, r3);
+ ASSERT_EQUAL_32(0xcdef, r4);
+ ASSERT_EQUAL_32(0xffffcdef, r5);
+ ASSERT_EQUAL_32(0x89abcdef, r6);
+ ASSERT_EQUAL_32(0x01234567, r7);
+ ASSERT_EQUAL_FP64(RawbitsToDouble(0x0123456789abcdef), d0);
+ ASSERT_EQUAL_FP32(RawbitsToFloat(0x89abcdef), s3);
+
+ TEARDOWN();
+}
+
+
+TEST_T32(distant_literal_references_unaligned_pc) {
+ SETUP();
+ START();
+
+ Literal<uint64_t>* literal =
+ new Literal<uint64_t>(UINT64_C(0x0123456789abcdef),
+ RawLiteral::kPlacedWhenUsed,
+ RawLiteral::kDeletedOnPoolDestruction);
+ // Refer to the literal so that it is emitted early.
+ __ Ldr(r0, literal);
+
+ // Add enough nops to exceed the range of all loads, leaving the PC aligned
+ // to only a two-byte boundary.
+ int space = 5002;
+ {
+ ExactAssemblyScope scope(&masm, space, CodeBufferCheckScope::kExactSize);
+ VIXL_ASSERT(masm.IsUsingT32());
+ for (int i = 0; i < space; i += k16BitT32InstructionSizeInBytes) {
+ __ nop();
+ }
+ }
+
+#define ENSURE_NOT_ALIGNED() \
+ do { \
+ if (IsMultiple<k32BitT32InstructionSizeInBytes>(masm.GetCursorOffset())) { \
+ ExactAssemblyScope scope(&masm, \
+ k16BitT32InstructionSizeInBytes, \
+ ExactAssemblyScope::kExactSize); \
+ __ nop(); \
+ } \
+ VIXL_ASSERT( \
+ !IsMultiple<k32BitT32InstructionSizeInBytes>(masm.GetCursorOffset())); \
+ } while (0)
+
+ // The literal has already been emitted, and is out of range of all of these
+ // instructions. The delegates must generate fix-up code.
+ ENSURE_NOT_ALIGNED();
+ __ Ldr(r1, literal);
+ ENSURE_NOT_ALIGNED();
+ __ Ldrb(r2, literal);
+ ENSURE_NOT_ALIGNED();
+ __ Ldrsb(r3, literal);
+ ENSURE_NOT_ALIGNED();
+ __ Ldrh(r4, literal);
+ ENSURE_NOT_ALIGNED();
+ __ Ldrsh(r5, literal);
+ ENSURE_NOT_ALIGNED();
+ __ Ldrd(r6, r7, literal);
+ {
+ // TODO: We currently require an extra scratch register for these cases
+ // because MemOperandComputationHelper isn't able to fit add_sub_offset into
+ // a single 'sub' instruction, so 'pc' gets preserved first. The same
+ // problem technically exists for the other loads, but vldr is particularly
+ // badly affected because vldr cannot set the low bits in its offset mask,
+ // so the add/sub operand is likely to be difficult to encode.
+ //
+ // At the moment, we get this:
+ // mov r8, pc
+ // mov ip, #5118
+ // sub r8, pc
+ // vldr d0, [r8, #48]
+ //
+ // We should be able to generate something like this:
+ // sub ip, pc, #0x1300 // 5118 & 0xff00
+ // sub ip, #0xfe // 5118 & 0x00ff
+ // vldr d0, [ip, #48]
+ UseScratchRegisterScope temps(&masm);
+ temps.Include(r8);
+ ENSURE_NOT_ALIGNED();
+ __ Vldr(d0, literal);
+ ENSURE_NOT_ALIGNED();
+ __ Vldr(s3, literal);
+ }
+
+#undef ENSURE_NOT_ALIGNED
+
+ END();
+ RUN();
+
+ // Check that the literals loaded correctly.
+ ASSERT_EQUAL_32(0x89abcdef, r0);
+ ASSERT_EQUAL_32(0x89abcdef, r1);
+ ASSERT_EQUAL_32(0xef, r2);
+ ASSERT_EQUAL_32(0xffffffef, r3);
+ ASSERT_EQUAL_32(0xcdef, r4);
+ ASSERT_EQUAL_32(0xffffcdef, r5);
+ ASSERT_EQUAL_32(0x89abcdef, r6);
+ ASSERT_EQUAL_32(0x01234567, r7);
+ ASSERT_EQUAL_FP64(RawbitsToDouble(0x0123456789abcdef), d0);
+ ASSERT_EQUAL_FP32(RawbitsToFloat(0x89abcdef), s3);
+
+ TEARDOWN();
+}
+
+
+TEST_T32(distant_literal_references_short_range) {
+ SETUP();
+ START();
+
+ Literal<uint64_t>* literal =
+ new Literal<uint64_t>(UINT64_C(0x0123456789abcdef),
+ RawLiteral::kPlacedWhenUsed,
+ RawLiteral::kDeletedOnPoolDestruction);
+ // Refer to the literal so that it is emitted early.
+ __ Vldr(s4, literal);
+
+ // Add enough nops to exceed the range of the loads, but not the adr that will
+ // be generated to read the PC.
+ int space = 4000;
+ {
+ ExactAssemblyScope scope(&masm, space, CodeBufferCheckScope::kExactSize);
+ VIXL_ASSERT(masm.IsUsingT32());
+ for (int i = 0; i < space; i += k16BitT32InstructionSizeInBytes) {
+ __ nop();
+ }
+ }
+
+#define ENSURE_ALIGNED() \
+ do { \
+ if (!IsMultiple<k32BitT32InstructionSizeInBytes>( \
+ masm.GetCursorOffset())) { \
+ ExactAssemblyScope scope(&masm, \
+ k16BitT32InstructionSizeInBytes, \
+ ExactAssemblyScope::kExactSize); \
+ __ nop(); \
+ } \
+ VIXL_ASSERT( \
+ IsMultiple<k32BitT32InstructionSizeInBytes>(masm.GetCursorOffset())); \
+ } while (0)
+
+ // The literal has already been emitted, and is out of range of all of these
+ // instructions. The delegates must generate fix-up code.
+ ENSURE_ALIGNED();
+ __ Ldr(r1, literal);
+ ENSURE_ALIGNED();
+ __ Ldrb(r2, literal);
+ ENSURE_ALIGNED();
+ __ Ldrsb(r3, literal);
+ ENSURE_ALIGNED();
+ __ Ldrh(r4, literal);
+ ENSURE_ALIGNED();
+ __ Ldrsh(r5, literal);
+ ENSURE_ALIGNED();
+ __ Ldrd(r6, r7, literal);
+ ENSURE_ALIGNED();
+ __ Vldr(d0, literal);
+ ENSURE_ALIGNED();
+ __ Vldr(s3, literal);
+
+#undef ENSURE_ALIGNED
+
+ END();
+ RUN();
+
+ // Check that the literals loaded correctly.
+ ASSERT_EQUAL_FP32(RawbitsToFloat(0x89abcdef), s4);
+ ASSERT_EQUAL_32(0x89abcdef, r1);
+ ASSERT_EQUAL_32(0xef, r2);
+ ASSERT_EQUAL_32(0xffffffef, r3);
+ ASSERT_EQUAL_32(0xcdef, r4);
+ ASSERT_EQUAL_32(0xffffcdef, r5);
+ ASSERT_EQUAL_32(0x89abcdef, r6);
+ ASSERT_EQUAL_32(0x01234567, r7);
+ ASSERT_EQUAL_FP64(RawbitsToDouble(0x0123456789abcdef), d0);
+ ASSERT_EQUAL_FP32(RawbitsToFloat(0x89abcdef), s3);
+
+ TEARDOWN();
+}
+
+
+TEST_T32(distant_literal_references_short_range_unaligned_pc) {
+ SETUP();
+ START();
+
+ Literal<uint64_t>* literal =
+ new Literal<uint64_t>(UINT64_C(0x0123456789abcdef),
+ RawLiteral::kPlacedWhenUsed,
+ RawLiteral::kDeletedOnPoolDestruction);
+ // Refer to the literal so that it is emitted early.
+ __ Vldr(s4, literal);
+
+ // Add enough nops to exceed the range of the loads, but not the adr that will
+ // be generated to read the PC.
+ int space = 4000;
+ {
+ ExactAssemblyScope scope(&masm, space, CodeBufferCheckScope::kExactSize);
+ VIXL_ASSERT(masm.IsUsingT32());
+ for (int i = 0; i < space; i += k16BitT32InstructionSizeInBytes) {
+ __ nop();
+ }
+ }
+
+#define ENSURE_NOT_ALIGNED() \
+ do { \
+ if (IsMultiple<k32BitT32InstructionSizeInBytes>(masm.GetCursorOffset())) { \
+ ExactAssemblyScope scope(&masm, \
+ k16BitT32InstructionSizeInBytes, \
+ ExactAssemblyScope::kExactSize); \
+ __ nop(); \
+ } \
+ VIXL_ASSERT( \
+ !IsMultiple<k32BitT32InstructionSizeInBytes>(masm.GetCursorOffset())); \
+ } while (0)
+
+ // The literal has already been emitted, and is out of range of all of these
+ // instructions. The delegates must generate fix-up code.
+ ENSURE_NOT_ALIGNED();
+ __ Ldr(r1, literal);
+ ENSURE_NOT_ALIGNED();
+ __ Ldrb(r2, literal);
+ ENSURE_NOT_ALIGNED();
+ __ Ldrsb(r3, literal);
+ ENSURE_NOT_ALIGNED();
+ __ Ldrh(r4, literal);
+ ENSURE_NOT_ALIGNED();
+ __ Ldrsh(r5, literal);
+ ENSURE_NOT_ALIGNED();
+ __ Ldrd(r6, r7, literal);
+ ENSURE_NOT_ALIGNED();
+ __ Vldr(d0, literal);
+ ENSURE_NOT_ALIGNED();
+ __ Vldr(s3, literal);
+
+#undef ENSURE_NOT_ALIGNED
+
+ END();
+ RUN();
+
+ // Check that the literals loaded correctly.
+ ASSERT_EQUAL_FP32(RawbitsToFloat(0x89abcdef), s4);
+ ASSERT_EQUAL_32(0x89abcdef, r1);
+ ASSERT_EQUAL_32(0xef, r2);
+ ASSERT_EQUAL_32(0xffffffef, r3);
+ ASSERT_EQUAL_32(0xcdef, r4);
+ ASSERT_EQUAL_32(0xffffcdef, r5);
+ ASSERT_EQUAL_32(0x89abcdef, r6);
+ ASSERT_EQUAL_32(0x01234567, r7);
+ ASSERT_EQUAL_FP64(RawbitsToDouble(0x0123456789abcdef), d0);
+ ASSERT_EQUAL_FP32(RawbitsToFloat(0x89abcdef), s3);
+
+ TEARDOWN();
+}
+
+
+TEST_T32(distant_literal_references_long_range) {
+ SETUP();
+ START();
+
+ Literal<uint64_t>* literal =
+ new Literal<uint64_t>(UINT64_C(0x0123456789abcdef),
+ RawLiteral::kPlacedWhenUsed,
+ RawLiteral::kDeletedOnPoolDestruction);
+ // Refer to the literal so that it is emitted early.
+ __ Ldr(r0, literal);
+
+#define PAD_WITH_NOPS(space) \
+ do { \
+ { \
+ ExactAssemblyScope scope(&masm, \
+ space, \
+ CodeBufferCheckScope::kExactSize); \
+ VIXL_ASSERT(masm.IsUsingT32()); \
+ for (int i = 0; i < space; i += k16BitT32InstructionSizeInBytes) { \
+ __ nop(); \
+ } \
+ } \
+ } while (0)
+
+ // Add enough nops to exceed the range of all loads.
+ PAD_WITH_NOPS(5000);
+
+ // The literal has already been emitted, and is out of range of all of these
+ // instructions. The delegates must generate fix-up code.
+ __ Ldr(r1, literal);
+ __ Ldrb(r2, literal);
+ __ Ldrsb(r3, literal);
+ __ Ldrh(r4, literal);
+ __ Ldrsh(r5, literal);
+ __ Ldrd(r6, r7, literal);
+ __ Vldr(d0, literal);
+ __ Vldr(s3, literal);
+
+ // Add enough nops to exceed the range of the adr+sub sequence.
+ PAD_WITH_NOPS(0x421000);
+
+ __ Ldr(r1, literal);
+ __ Ldrb(r2, literal);
+ __ Ldrsb(r3, literal);
+ __ Ldrh(r4, literal);
+ __ Ldrsh(r5, literal);
+ __ Ldrd(r6, r7, literal);
+ {
+ // TODO: We currently require an extra scratch register for these cases. We
+ // should be able to optimise the code generation to avoid this requirement
+ // (and in many cases avoid a 32-bit instruction).
+ UseScratchRegisterScope temps(&masm);
+ temps.Include(r8);
+ __ Vldr(d0, literal);
+ __ Vldr(s3, literal);
+ }
+
+#undef PAD_WITH_NOPS
+
+ END();
+ RUN();
+
+ // Check that the literals loaded correctly.
+ ASSERT_EQUAL_32(0x89abcdef, r0);
+ ASSERT_EQUAL_32(0x89abcdef, r1);
+ ASSERT_EQUAL_32(0xef, r2);
+ ASSERT_EQUAL_32(0xffffffef, r3);
+ ASSERT_EQUAL_32(0xcdef, r4);
+ ASSERT_EQUAL_32(0xffffcdef, r5);
+ ASSERT_EQUAL_32(0x89abcdef, r6);
+ ASSERT_EQUAL_32(0x01234567, r7);
+ ASSERT_EQUAL_FP64(RawbitsToDouble(0x0123456789abcdef), d0);
+ ASSERT_EQUAL_FP32(RawbitsToFloat(0x89abcdef), s3);
+
+ TEARDOWN();
+}
+
+
+TEST(barriers) {
+ // Generate all supported barriers, this is just a smoke test
+ SETUP();
+
+ START();
+
+ // DMB
+ __ Dmb(SY);
+ __ Dmb(ST);
+ __ Dmb(ISH);
+ __ Dmb(ISHST);
+ __ Dmb(NSH);
+ __ Dmb(NSHST);
+ __ Dmb(OSH);
+ __ Dmb(OSHST);
+
+ // DSB
+ __ Dsb(SY);
+ __ Dsb(ST);
+ __ Dsb(ISH);
+ __ Dsb(ISHST);
+ __ Dsb(NSH);
+ __ Dsb(NSHST);
+ __ Dsb(OSH);
+ __ Dsb(OSHST);
+
+ // ISB
+ __ Isb(SY);
+
+ END();
+
+ TEARDOWN();
+}
+
+
+TEST(preloads) {
+ // Smoke test for various pld/pli forms.
+ SETUP();
+
+ START();
+
+ // PLD immediate
+ __ Pld(MemOperand(sp, 0));
+ __ Pld(MemOperand(r0, 0));
+ __ Pld(MemOperand(r1, 123));
+ __ Pld(MemOperand(r2, 1234));
+ __ Pld(MemOperand(r3, 4095));
+ __ Pld(MemOperand(r4, -123));
+ __ Pld(MemOperand(r5, -255));
+
+ if (masm.IsUsingA32()) {
+ __ Pld(MemOperand(r6, -1234));
+ __ Pld(MemOperand(r7, -4095));
+ }
+
+
+ // PLDW immediate
+ __ Pldw(MemOperand(sp, 0));
+ __ Pldw(MemOperand(r0, 0));
+ __ Pldw(MemOperand(r1, 123));
+ __ Pldw(MemOperand(r2, 1234));
+ __ Pldw(MemOperand(r3, 4095));
+ __ Pldw(MemOperand(r4, -123));
+ __ Pldw(MemOperand(r5, -255));
+
+ if (masm.IsUsingA32()) {
+ __ Pldw(MemOperand(r6, -1234));
+ __ Pldw(MemOperand(r7, -4095));
+ }
+
+ // PLD register
+ __ Pld(MemOperand(r0, r1));
+ __ Pld(MemOperand(r0, r1, LSL, 1));
+ __ Pld(MemOperand(r0, r1, LSL, 2));
+ __ Pld(MemOperand(r0, r1, LSL, 3));
+
+ if (masm.IsUsingA32()) {
+ __ Pld(MemOperand(r0, r1, LSL, 4));
+ __ Pld(MemOperand(r0, r1, LSL, 20));
+ }
+
+ // PLDW register
+ __ Pldw(MemOperand(r0, r1));
+ __ Pldw(MemOperand(r0, r1, LSL, 1));
+ __ Pldw(MemOperand(r0, r1, LSL, 2));
+ __ Pldw(MemOperand(r0, r1, LSL, 3));
+
+ if (masm.IsUsingA32()) {
+ __ Pldw(MemOperand(r0, r1, LSL, 4));
+ __ Pldw(MemOperand(r0, r1, LSL, 20));
+ }
+
+ // PLD literal
+ Label pld_label;
+ __ Pld(&pld_label);
+ __ Bind(&pld_label);
+
+ // PLI immediate
+ __ Pli(MemOperand(sp, 0));
+ __ Pli(MemOperand(r0, 0));
+ __ Pli(MemOperand(r1, 123));
+ __ Pli(MemOperand(r2, 1234));
+ __ Pli(MemOperand(r3, 4095));
+ __ Pli(MemOperand(r4, -123));
+ __ Pli(MemOperand(r5, -255));
+
+ if (masm.IsUsingA32()) {
+ __ Pli(MemOperand(r6, -1234));
+ __ Pli(MemOperand(r7, -4095));
+ }
+
+ // PLI register
+ __ Pli(MemOperand(r0, r1));
+ __ Pli(MemOperand(r0, r1, LSL, 1));
+ __ Pli(MemOperand(r0, r1, LSL, 2));
+ __ Pli(MemOperand(r0, r1, LSL, 3));
+
+ if (masm.IsUsingA32()) {
+ __ Pli(MemOperand(r0, r1, LSL, 4));
+ __ Pli(MemOperand(r0, r1, LSL, 20));
+ }
+
+ // PLI literal
+ Label pli_label;
+ __ Pli(&pli_label);
+ __ Bind(&pli_label);
+
+ END();
+
+ TEARDOWN();
+}
+
+
+TEST_T32(veneer_mirrored_branches) {
+ SETUP();
+
+ START();
+
+ const int kMaxBranchCount = 256;
+
+ for (int branch_count = 1; branch_count < kMaxBranchCount; branch_count++) {
+ Label* targets = new Label[branch_count];
+
+ for (int i = 0; i < branch_count; i++) {
+ __ Cbz(r0, &targets[i]);
+ }
+
+ for (int i = 0; i < branch_count; i++) {
+ __ Bind(&targets[branch_count - i - 1]);
+ __ Orr(r0, r0, r0);
+ }
+
+ delete[] targets;
+ }
+
+ END();
+
+ TEARDOWN();
+}
+
+
+TEST_T32(branch_fuzz_example) {
+ SETUP();
+
+ START();
+
+ Label l[64];
+ __ And(r0, r0, r0);
+ __ Cbz(r0, &l[30]);
+ __ And(r0, r0, r0);
+ __ Cbz(r0, &l[22]);
+ __ And(r0, r0, r0);
+ __ Cbz(r0, &l[1]);
+ __ Cbz(r0, &l[15]);
+ __ Cbz(r0, &l[9]);
+ __ Cbz(r0, &l[6]);
+ __ Bind(&l[26]);
+ __ Cbz(r0, &l[29]);
+ __ And(r0, r0, r0);
+ __ And(r0, r0, r0);
+ __ Cbz(r0, &l[22]);
+ __ Bind(&l[12]);
+ __ Bind(&l[22]);
+ __ Cbz(r0, &l[10]);
+ __ And(r0, r0, r0);
+ __ Cbz(r0, &l[30]);
+ __ Cbz(r0, &l[17]);
+ __ Cbz(r0, &l[27]);
+ __ Cbz(r0, &l[11]);
+ __ Bind(&l[7]);
+ __ Cbz(r0, &l[18]);
+ __ Bind(&l[14]);
+ __ Cbz(r0, &l[1]);
+ __ Bind(&l[18]);
+ __ Cbz(r0, &l[11]);
+ __ Cbz(r0, &l[6]);
+ __ Bind(&l[21]);
+ __ Cbz(r0, &l[28]);
+ __ And(r0, r0, r0);
+ __ Cbz(r0, &l[28]);
+ __ Cbz(r0, &l[22]);
+ __ Bind(&l[23]);
+ __ Cbz(r0, &l[21]);
+ __ Cbz(r0, &l[28]);
+ __ Cbz(r0, &l[9]);
+ __ Bind(&l[9]);
+ __ Cbz(r0, &l[4]);
+ __ And(r0, r0, r0);
+ __ Cbz(r0, &l[10]);
+ __ And(r0, r0, r0);
+ __ Bind(&l[8]);
+ __ And(r0, r0, r0);
+ __ Cbz(r0, &l[10]);
+ __ And(r0, r0, r0);
+ __ Cbz(r0, &l[17]);
+ __ Bind(&l[10]);
+ __ Cbz(r0, &l[8]);
+ __ Cbz(r0, &l[25]);
+ __ Cbz(r0, &l[4]);
+ __ Bind(&l[28]);
+ __ And(r0, r0, r0);
+ __ Cbz(r0, &l[16]);
+ __ Bind(&l[19]);
+ __ Cbz(r0, &l[14]);
+ __ Cbz(r0, &l[28]);
+ __ Cbz(r0, &l[26]);
+ __ Cbz(r0, &l[21]);
+ __ And(r0, r0, r0);
+ __ Bind(&l[24]);
+ __ And(r0, r0, r0);
+ __ Cbz(r0, &l[24]);
+ __ Cbz(r0, &l[24]);
+ __ Cbz(r0, &l[19]);
+ __ Cbz(r0, &l[26]);
+ __ Cbz(r0, &l[4]);
+ __ And(r0, r0, r0);
+ __ Cbz(r0, &l[27]);
+ __ Cbz(r0, &l[14]);
+ __ Cbz(r0, &l[5]);
+ __ Cbz(r0, &l[18]);
+ __ Cbz(r0, &l[5]);
+ __ Cbz(r0, &l[6]);
+ __ Cbz(r0, &l[28]);
+ __ Cbz(r0, &l[15]);
+ __ Cbz(r0, &l[0]);
+ __ Cbz(r0, &l[10]);
+ __ Cbz(r0, &l[16]);
+ __ Cbz(r0, &l[30]);
+ __ Cbz(r0, &l[8]);
+ __ Cbz(r0, &l[16]);
+ __ Cbz(r0, &l[22]);
+ __ Cbz(r0, &l[27]);
+ __ Cbz(r0, &l[12]);
+ __ Cbz(r0, &l[0]);
+ __ Cbz(r0, &l[23]);
+ __ Cbz(r0, &l[27]);
+ __ Cbz(r0, &l[16]);
+ __ Cbz(r0, &l[24]);
+ __ Cbz(r0, &l[17]);
+ __ Cbz(r0, &l[4]);
+ __ Cbz(r0, &l[11]);
+ __ Cbz(r0, &l[6]);
+ __ Cbz(r0, &l[23]);
+ __ Bind(&l[16]);
+ __ Cbz(r0, &l[10]);
+ __ Cbz(r0, &l[17]);
+ __ Cbz(r0, &l[12]);
+ __ And(r0, r0, r0);
+ __ Cbz(r0, &l[11]);
+ __ Cbz(r0, &l[17]);
+ __ Cbz(r0, &l[1]);
+ __ Cbz(r0, &l[3]);
+ __ Cbz(r0, &l[18]);
+ __ Bind(&l[4]);
+ __ Cbz(r0, &l[31]);
+ __ Cbz(r0, &l[25]);
+ __ Cbz(r0, &l[22]);
+ __ And(r0, r0, r0);
+ __ Cbz(r0, &l[19]);
+ __ Cbz(r0, &l[16]);
+ __ Cbz(r0, &l[21]);
+ __ Cbz(r0, &l[27]);
+ __ Bind(&l[1]);
+ __ Cbz(r0, &l[9]);
+ __ Cbz(r0, &l[13]);
+ __ Cbz(r0, &l[10]);
+ __ Cbz(r0, &l[6]);
+ __ Cbz(r0, &l[30]);
+ __ Cbz(r0, &l[28]);
+ __ Cbz(r0, &l[7]);
+ __ Cbz(r0, &l[17]);
+ __ Bind(&l[0]);
+ __ Cbz(r0, &l[13]);
+ __ Cbz(r0, &l[11]);
+ __ Cbz(r0, &l[19]);
+ __ Cbz(r0, &l[22]);
+ __ Cbz(r0, &l[9]);
+ __ And(r0, r0, r0);
+ __ Cbz(r0, &l[15]);
+ __ Cbz(r0, &l[31]);
+ __ Cbz(r0, &l[2]);
+ __ And(r0, r0, r0);
+ __ Cbz(r0, &l[6]);
+ __ Bind(&l[27]);
+ __ Bind(&l[13]);
+ __ Cbz(r0, &l[23]);
+ __ Cbz(r0, &l[7]);
+ __ Bind(&l[2]);
+ __ And(r0, r0, r0);
+ __ Cbz(r0, &l[1]);
+ __ Bind(&l[15]);
+ __ Cbz(r0, &l[13]);
+ __ Cbz(r0, &l[17]);
+ __ Cbz(r0, &l[8]);
+ __ Cbz(r0, &l[30]);
+ __ Cbz(r0, &l[8]);
+ __ Cbz(r0, &l[27]);
+ __ Cbz(r0, &l[2]);
+ __ Cbz(r0, &l[31]);
+ __ Cbz(r0, &l[4]);
+ __ Cbz(r0, &l[11]);
+ __ Bind(&l[29]);
+ __ Cbz(r0, &l[7]);
+ __ Cbz(r0, &l[5]);
+ __ Cbz(r0, &l[11]);
+ __ Cbz(r0, &l[24]);
+ __ Cbz(r0, &l[9]);
+ __ Cbz(r0, &l[3]);
+ __ Cbz(r0, &l[3]);
+ __ Cbz(r0, &l[22]);
+ __ Cbz(r0, &l[19]);
+ __ Cbz(r0, &l[4]);
+ __ Bind(&l[6]);
+ __ And(r0, r0, r0);
+ __ And(r0, r0, r0);
+ __ Cbz(r0, &l[9]);
+ __ Cbz(r0, &l[3]);
+ __ Cbz(r0, &l[23]);
+ __ Cbz(r0, &l[12]);
+ __ Cbz(r0, &l[1]);
+ __ Cbz(r0, &l[22]);
+ __ Cbz(r0, &l[24]);
+ __ And(r0, r0, r0);
+ __ Cbz(r0, &l[16]);
+ __ Cbz(r0, &l[19]);
+ __ Cbz(r0, &l[20]);
+ __ Cbz(r0, &l[1]);
+ __ Cbz(r0, &l[4]);
+ __ Cbz(r0, &l[1]);
+ __ Cbz(r0, &l[25]);
+ __ Cbz(r0, &l[21]);
+ __ Cbz(r0, &l[20]);
+ __ Cbz(r0, &l[29]);
+ __ And(r0, r0, r0);
+ __ Cbz(r0, &l[10]);
+ __ Cbz(r0, &l[5]);
+ __ And(r0, r0, r0);
+ __ Cbz(r0, &l[25]);
+ __ Cbz(r0, &l[26]);
+ __ Cbz(r0, &l[28]);
+ __ Cbz(r0, &l[19]);
+ __ And(r0, r0, r0);
+ __ Bind(&l[17]);
+ __ And(r0, r0, r0);
+ __ And(r0, r0, r0);
+ __ And(r0, r0, r0);
+ __ And(r0, r0, r0);
+ __ Cbz(r0, &l[6]);
+ __ And(r0, r0, r0);
+ __ Cbz(r0, &l[5]);
+ __ Cbz(r0, &l[26]);
+ __ Cbz(r0, &l[28]);
+ __ Cbz(r0, &l[24]);
+ __ Bind(&l[20]);
+ __ And(r0, r0, r0);
+ __ Cbz(r0, &l[10]);
+ __ Cbz(r0, &l[19]);
+ __ Cbz(r0, &l[6]);
+ __ And(r0, r0, r0);
+ __ Cbz(r0, &l[13]);
+ __ Cbz(r0, &l[15]);
+ __ Cbz(r0, &l[22]);
+ __ Cbz(r0, &l[8]);
+ __ Cbz(r0, &l[6]);
+ __ Cbz(r0, &l[23]);
+ __ Cbz(r0, &l[6]);
+ __ And(r0, r0, r0);
+ __ Cbz(r0, &l[13]);
+ __ Bind(&l[31]);
+ __ Cbz(r0, &l[14]);
+ __ Cbz(r0, &l[5]);
+ __ Cbz(r0, &l[1]);
+ __ Cbz(r0, &l[17]);
+ __ Cbz(r0, &l[27]);
+ __ Cbz(r0, &l[10]);
+ __ Cbz(r0, &l[30]);
+ __ Cbz(r0, &l[14]);
+ __ Cbz(r0, &l[24]);
+ __ Cbz(r0, &l[26]);
+ __ And(r0, r0, r0);
+ __ Cbz(r0, &l[2]);
+ __ Cbz(r0, &l[21]);
+ __ Cbz(r0, &l[5]);
+ __ Cbz(r0, &l[24]);
+ __ And(r0, r0, r0);
+ __ Cbz(r0, &l[24]);
+ __ Cbz(r0, &l[17]);
+ __ And(r0, r0, r0);
+ __ And(r0, r0, r0);
+ __ Cbz(r0, &l[24]);
+ __ And(r0, r0, r0);
+ __ Cbz(r0, &l[17]);
+ __ Cbz(r0, &l[12]);
+ __ And(r0, r0, r0);
+ __ Cbz(r0, &l[9]);
+ __ Cbz(r0, &l[9]);
+ __ Cbz(r0, &l[31]);
+ __ Cbz(r0, &l[25]);
+ __ And(r0, r0, r0);
+ __ And(r0, r0, r0);
+ __ Cbz(r0, &l[13]);
+ __ Cbz(r0, &l[14]);
+ __ Cbz(r0, &l[5]);
+ __ Cbz(r0, &l[5]);
+ __ Cbz(r0, &l[12]);
+ __ Cbz(r0, &l[3]);
+ __ Cbz(r0, &l[25]);
+ __ Bind(&l[11]);
+ __ Cbz(r0, &l[15]);
+ __ Cbz(r0, &l[20]);
+ __ Cbz(r0, &l[22]);
+ __ Cbz(r0, &l[19]);
+ __ And(r0, r0, r0);
+ __ Cbz(r0, &l[19]);
+ __ And(r0, r0, r0);
+ __ Cbz(r0, &l[21]);
+ __ Cbz(r0, &l[0]);
+ __ And(r0, r0, r0);
+ __ Cbz(r0, &l[16]);
+ __ Cbz(r0, &l[28]);
+ __ Cbz(r0, &l[18]);
+ __ Cbz(r0, &l[3]);
+ __ And(r0, r0, r0);
+ __ Cbz(r0, &l[15]);
+ __ Cbz(r0, &l[8]);
+ __ Cbz(r0, &l[25]);
+ __ Cbz(r0, &l[1]);
+ __ Cbz(r0, &l[21]);
+ __ Cbz(r0, &l[1]);
+ __ Cbz(r0, &l[29]);
+ __ Cbz(r0, &l[15]);
+ __ And(r0, r0, r0);
+ __ Cbz(r0, &l[24]);
+ __ Cbz(r0, &l[3]);
+ __ Cbz(r0, &l[9]);
+ __ Cbz(r0, &l[9]);
+ __ Cbz(r0, &l[24]);
+ __ And(r0, r0, r0);
+ __ Cbz(r0, &l[19]);
+ __ And(r0, r0, r0);
+ __ Cbz(r0, &l[30]);
+ __ Bind(&l[25]);
+ __ Bind(&l[3]);
+ __ Bind(&l[30]);
+ __ Bind(&l[5]);
+
+ END();
+
+ TEARDOWN();
+}
+
+
+// Generate a "B" and a "Cbz" which have the same checkpoint. Without proper
+// management (i.e. if the veneers were only generated at the shared
+// checkpoint), one one of the branches would be out of range.
+TEST_T32(veneer_simultaneous) {
+ SETUP();
+
+ START();
+
+ // `2046` max range - the size of the B.EQ itself.
+ static const int kMaxBCondRange = 1048574;
+
+ Label target_1;
+ Label target_2;
+
+ __ B(eq, &target_1);
+
+ int target_1_size_1 =
+ kMaxBCondRange - kCbzCbnzRange - k32BitT32InstructionSizeInBytes;
+ int end_1 = masm.GetCursorOffset() + target_1_size_1;
+ while (masm.GetCursorOffset() < end_1) {
+ __ Nop();
+ }
+
+ __ Cbz(r0, &target_2);
+
+ int target_1_size_2 = kCbzCbnzRange - k16BitT32InstructionSizeInBytes;
+ int end_2 = masm.GetCursorOffset() + target_1_size_2;
+ while (masm.GetCursorOffset() < end_2) {
+ __ Nop();
+ }
+
+ __ Nop();
+
+ __ Bind(&target_1);
+ __ Bind(&target_2);
+
+ END();
+
+ TEARDOWN();
+}
+
+
+// Generate a "B" and a "Cbz" which have the same checkpoint and the same label.
+TEST_T32(veneer_simultaneous_one_label) {
+ SETUP();
+
+ START();
+
+ // `2046` max range - the size of the B.EQ itself.
+ static const int kMaxBCondRange = 1048574;
+
+ Label target;
+
+ __ B(eq, &target);
+
+ int target_1_size_1 =
+ kMaxBCondRange - kCbzCbnzRange - k32BitT32InstructionSizeInBytes;
+ int end_1 = masm.GetCursorOffset() + target_1_size_1;
+ while (masm.GetCursorOffset() < end_1) {
+ __ Nop();
+ }
+
+ __ Cbz(r0, &target);
+
+ int target_1_size_2 = kCbzCbnzRange - k16BitT32InstructionSizeInBytes;
+ int end_2 = masm.GetCursorOffset() + target_1_size_2;
+ while (masm.GetCursorOffset() < end_2) {
+ __ Nop();
+ }
+
+ __ Nop();
+
+ __ Bind(&target);
+
+ END();
+
+ TEARDOWN();
+}
+
+
+// The literal pool will be emitted early because we keep a margin to always be
+// able to generate the veneers before the literal.
+TEST_T32(veneer_and_literal) {
+ SETUP();
+
+ START();
+
+ VIXL_CHECK(masm.VeneerPoolIsEmpty());
+ VIXL_CHECK(masm.LiteralPoolIsEmpty());
+
+ const uint32_t ldrd_range = 1020;
+ const uint32_t cbz_range = 126;
+ const uint32_t kLabelsCount = 20;
+ Label labels[kLabelsCount];
+
+ // Create one literal pool entry.
+ __ Ldrd(r0, r1, 0x1234567890abcdef);
+
+ // Generate some nops.
+ uint32_t i = 0;
+ for (; i < ldrd_range - cbz_range - 40;
+ i += k16BitT32InstructionSizeInBytes) {
+ __ Nop();
+ }
+
+ // At this point, it remains cbz_range + 40 => 166 bytes before ldrd becomes
+ // out of range.
+ // We generate kLabelsCount * 4 => 80 bytes. We shouldn't generate the
+ // literal pool.
+ for (uint32_t j = 0; j < kLabelsCount; j++) {
+ __ Cbz(r0, &labels[j]);
+ __ Nop();
+ i += 2 * k16BitT32InstructionSizeInBytes;
+ }
+
+ // However as we have pending veneer, the range is shrinken and the literal
+ // pool is generated.
+ VIXL_ASSERT(masm.LiteralPoolIsEmpty());
+ // However, we didn't generate the veneer pool.
+ VIXL_ASSERT(masm.GetMarginBeforeVeneerEmission() <
+ static_cast<int32_t>(cbz_range));
+
+ // We generate a few more instructions.
+ for (; i < ldrd_range - 4 * kA32InstructionSizeInBytes;
+ i += k16BitT32InstructionSizeInBytes) {
+ __ Nop();
+ }
+
+ // And a veneer pool has been generated.
+ VIXL_ASSERT(masm.GetMarginBeforeVeneerEmission() >
+ static_cast<int32_t>(cbz_range));
+
+ // Bind all the used labels.
+ for (uint32_t j = 0; j < kLabelsCount; j++) {
+ __ Bind(&labels[j]);
+ __ Nop();
+ }
+
+ // Now that all the labels have been bound, we have no more veneer.
+ VIXL_CHECK(masm.VeneerPoolIsEmpty());
+
+ END();
+
+ RUN();
+
+ // Check that the literals loaded correctly.
+ ASSERT_EQUAL_32(0x90abcdef, r0);
+ ASSERT_EQUAL_32(0x12345678, r1);
+
+ TEARDOWN();
+}
+
+
+// The literal pool will be emitted early and, as the emission of the literal
+// pool would have put veneer out of range, the veneers are emitted first.
+TEST_T32(veneer_and_literal2) {
+ SETUP();
+
+ START();
+
+ VIXL_CHECK(masm.VeneerPoolIsEmpty());
+ VIXL_CHECK(masm.LiteralPoolIsEmpty());
+
+ const uint32_t ldrd_range = 1020;
+ const uint32_t cbz_range = 126;
+ const uint32_t kLabelsCount = 20;
+ const int32_t kTypicalMacroInstructionMaxSize =
+ 8 * kMaxInstructionSizeInBytes;
+ Label labels[kLabelsCount];
+
+ // Create one literal pool entry.
+ __ Ldrd(r0, r1, 0x1234567890abcdef);
+
+ for (uint32_t i = 0; i < ldrd_range - cbz_range - 4 * kLabelsCount;
+ i += k16BitT32InstructionSizeInBytes) {
+ __ Nop();
+ }
+
+ // Add entries to the veneer pool.
+ for (uint32_t i = 0; i < kLabelsCount; i++) {
+ __ Cbz(r0, &labels[i]);
+ __ Nop();
+ }
+
+ // Generate nops up to the literal pool limit.
+ while (masm.GetMarginBeforeLiteralEmission() >=
+ kTypicalMacroInstructionMaxSize) {
+ __ Nop();
+ }
+
+ // At this point, no literals and no veneers have been generated.
+ VIXL_ASSERT(!masm.LiteralPoolIsEmpty());
+ VIXL_ASSERT(masm.GetMarginBeforeVeneerEmission() <
+ static_cast<int32_t>(cbz_range));
+ // The literal pool needs to be generated.
+ VIXL_ASSERT(masm.GetMarginBeforeLiteralEmission() <
+ kTypicalMacroInstructionMaxSize);
+ // But not the veneer pool.
+ VIXL_ASSERT(masm.GetMarginBeforeVeneerEmission() >=
+ kTypicalMacroInstructionMaxSize);
+ // However, as the literal emission would put veneers out of range.
+ VIXL_ASSERT(masm.GetMarginBeforeVeneerEmission() <
+ kTypicalMacroInstructionMaxSize +
+ static_cast<int32_t>(masm.GetLiteralPoolSize()));
+
+ // This extra Nop will generate the literal pool and before that the veneer
+ // pool.
+ __ Nop();
+ // Now the literal pool has been generated.
+ VIXL_ASSERT(masm.LiteralPoolIsEmpty());
+ // And also the veneer pool.
+ VIXL_ASSERT(masm.GetMarginBeforeVeneerEmission() > 1000);
+
+ // Bind all the used labels.
+ for (uint32_t j = 0; j < kLabelsCount; j++) {
+ __ Bind(&labels[j]);
+ __ Nop();
+ }
+
+ // Now that all the labels have been bound, we have no more veneer.
+ VIXL_CHECK(masm.VeneerPoolIsEmpty());
+
+ END();
+
+ RUN();
+
+ // Check that the literals loaded correctly.
+ ASSERT_EQUAL_32(0x90abcdef, r0);
+ ASSERT_EQUAL_32(0x12345678, r1);
+
+ TEARDOWN();
+}
+
+
+// Use a literal when we already have a veneer pool potential size greater than
+// the literal range => generate the literal immediately (not optimum but it
+// works).
+TEST_T32(veneer_and_literal3) {
+ SETUP();
+
+ START();
+
+ static const int kLabelsCount = 1000;
+
+ Label labels[kLabelsCount];
+
+ // Set the Z flag so that the following branches are not taken.
+ __ Movs(r0, 0);
+
+ for (int i = 0; i < kLabelsCount; i++) {
+ __ B(ne, &labels[i]);
+ }
+
+ // Create one literal pool entry.
+ __ Ldrd(r0, r1, 0x1234567890abcdef);
+
+ for (int i = 0; i < 10; i++) {
+ __ Nop();
+ }
+
+ for (int i = 0; i < kLabelsCount; i++) {
+ __ Bind(&labels[i]);
+ }
+
+ END();
+
+ RUN();
+
+ // Check that the literals loaded correctly.
+ ASSERT_EQUAL_32(0x90abcdef, r0);
+ ASSERT_EQUAL_32(0x12345678, r1);
+
+ TEARDOWN();
+}
+
+
+// Literal has to be generated sooner than veneers. However, as the literal
+// pool generation would make the veneers out of range, generate the veneers
+// first.
+TEST_T32(veneer_and_literal4) {
+ SETUP();
+
+ START();
+
+ Label end;
+
+ // Set the Z flag so that the following branch is not taken.
+ __ Movs(r0, 0);
+ __ B(ne, &end);
+
+ uint32_t value = 0x1234567;
+ Literal<uint32_t>* literal =
+ new Literal<uint32_t>(value,
+ RawLiteral::kPlacedWhenUsed,
+ RawLiteral::kDeletedOnPoolDestruction);
+
+ __ Ldr(r11, literal);
+
+ // The range for ldr is 4095, the range for cbz is 127. Generate nops
+ // to have the ldr becomming out of range just before the cbz.
+ const int NUM_NOPS = 2044;
+ const int NUM_RANGE = 58;
+
+ const int NUM1 = NUM_NOPS - NUM_RANGE;
+ const int NUM2 = NUM_RANGE;
+
+ {
+ ExactAssemblyScope aas(&masm, 2 * NUM1, CodeBufferCheckScope::kMaximumSize);
+ for (int i = 0; i < NUM1; i++) {
+ __ nop();
+ }
+ }
+
+ __ Cbz(r1, &end);
+
+ {
+ ExactAssemblyScope aas(&masm, 2 * NUM2, CodeBufferCheckScope::kMaximumSize);
+ for (int i = 0; i < NUM2; i++) {
+ __ nop();
+ }
+ }
+
+ {
+ ExactAssemblyScope aas(&masm, 4, CodeBufferCheckScope::kMaximumSize);
+ __ add(r1, r1, 3);
+ }
+ __ Bind(&end);
+
+ END();
+
+ RUN();
+
+ // Check that the literals loaded correctly.
+ ASSERT_EQUAL_32(0x1234567, r11);
+
+ TEARDOWN();
+}
+
+
+// Literal has to be generated sooner than veneers. However, as the literal
+// pool generation would make the veneers out of range, generate the veneers
+// first.
+TEST_T32(veneer_and_literal5) {
+ SETUP();
+
+ START();
+
+ static const int kTestCount = 100;
+ Label labels[kTestCount];
+
+ int first_test = 2000;
+ // Test on both sizes of the Adr range which is 4095.
+ for (int test = 0; test < kTestCount; test++) {
+ const int string_size = 1000; // A lot more than the cbz range.
+ std::string test_string(string_size, 'x');
+ StringLiteral big_literal(test_string.c_str());
+
+ __ Adr(r11, &big_literal);
+
+ {
+ int num_nops = first_test + test;
+ ExactAssemblyScope aas(&masm,
+ 2 * num_nops,
+ CodeBufferCheckScope::kMaximumSize);
+ for (int i = 0; i < num_nops; i++) {
+ __ nop();
+ }
+ }
+
+ __ Cbz(r1, &labels[test]);
+
+ {
+ ExactAssemblyScope aas(&masm, 4, CodeBufferCheckScope::kMaximumSize);
+ __ add(r1, r1, 3);
+ }
+ __ Bind(&labels[test]);
+ // Emit the literal pool if it has not beeen emitted (it's the case for
+ // the lower values of test).
+ __ EmitLiteralPool(MacroAssembler::kBranchRequired);
+ }
+
+ END();
+
+ TEARDOWN();
+}
+
+
+// Check that veneer and literals are well generated when they are out of
+// range at the same time.
+TEST_T32(veneer_and_literal6) {
+ SETUP();
+
+ START();
+
+ Label t1, t2, t3, t4, t5;
+ static const int kLdrdRange = 1020;
+ static const int kSizeForCbz = k16BitT32InstructionSizeInBytes;
+
+ __ Ldrd(r0, r1, 0x1111111111111111);
+ __ Ldrd(r2, r3, 0x2222222222222222);
+ __ Ldrd(r4, r5, 0x3333333333333333);
+ __ Ldrd(r6, r7, 0x4444444444444444);
+ __ Ldrd(r8, r9, 0x5555555555555555);
+ __ Ldrd(r10, r11, 0x6666666666666666);
+ __ Ldrd(r10, r11, 0x1234567890abcdef);
+
+ // Ldrd has a bigger range that cbz. Generate some nops before the cbzs in
+ // order to reach the maximum range of ldrd and cbz at the same time.
+ {
+ int nop_size = kLdrdRange - kCbzCbnzRange - 5 * kSizeForCbz;
+ ExactAssemblyScope scope(&masm, nop_size, CodeBufferCheckScope::kExactSize);
+ for (int i = 0; i < nop_size; i += k16BitT32InstructionSizeInBytes) {
+ __ nop();
+ }
+ }
+
+ __ Cbz(r2, &t1);
+ __ Cbz(r2, &t2);
+ __ Cbz(r2, &t3);
+ __ Cbz(r2, &t4);
+ __ Cbz(r2, &t5);
+
+ // At this point, the ldrds are not out of range. It remains a kCbzCbnzRange
+ // margin (minus the size of the veneers).
+
+ // At this point, the literal and the veneer pools are not emitted.
+ VIXL_CHECK(masm.GetLiteralPoolSize() > 0);
+ VIXL_CHECK(masm.GetMarginBeforeVeneerEmission() < kCbzCbnzRange);
+
+ // This scope will generate both veneers (they are both out of range).
+ {
+ int nop_size = kCbzCbnzRange;
+ ExactAssemblyScope scope(&masm, nop_size, CodeBufferCheckScope::kExactSize);
+ for (int i = 0; i < nop_size; i += k16BitT32InstructionSizeInBytes) {
+ __ nop();
+ }
+ }
+
+ // Check that both veneers have been emitted.
+ VIXL_CHECK(masm.GetLiteralPoolSize() == 0);
+ VIXL_CHECK(masm.GetMarginBeforeVeneerEmission() > kCbzCbnzRange);
+
+ __ Bind(&t1);
+ __ Bind(&t2);
+ __ Bind(&t3);
+ __ Bind(&t4);
+ __ Bind(&t5);
+
+ END();
+
+ RUN();
+
+ // Check that the literals loaded correctly.
+ ASSERT_EQUAL_32(0x11111111, r0);
+ ASSERT_EQUAL_32(0x11111111, r1);
+ ASSERT_EQUAL_32(0x22222222, r2);
+ ASSERT_EQUAL_32(0x22222222, r3);
+ ASSERT_EQUAL_32(0x33333333, r4);
+ ASSERT_EQUAL_32(0x33333333, r5);
+ ASSERT_EQUAL_32(0x44444444, r6);
+ ASSERT_EQUAL_32(0x44444444, r7);
+ ASSERT_EQUAL_32(0x55555555, r8);
+ ASSERT_EQUAL_32(0x55555555, r9);
+ ASSERT_EQUAL_32(0x90abcdef, r10);
+ ASSERT_EQUAL_32(0x12345678, r11);
+
+ TEARDOWN();
+}
+
+
+// Check that a label which is just bound during the MacroEmissionCheckScope
+// can be used.
+TEST(ldr_label_bound_during_scope) {
+ SETUP();
+ START();
+
+ const int32_t kTypicalMacroInstructionMaxSize =
+ 8 * kMaxInstructionSizeInBytes;
+
+ Literal<uint64_t>* literal =
+ new Literal<uint64_t>(UINT64_C(0x1234567890abcdef),
+ RawLiteral::kPlacedWhenUsed,
+ RawLiteral::kDeletedOnPoolDestruction);
+ __ Ldrd(r0, r1, literal);
+
+ while (masm.GetMarginBeforeLiteralEmission() >=
+ kTypicalMacroInstructionMaxSize) {
+ __ Nop();
+ }
+
+ VIXL_ASSERT(!masm.LiteralPoolIsEmpty());
+
+ // This Ldrd will first generate the pool and then use literal which has just
+ // been bound.
+ __ Ldrd(r2, r3, literal);
+
+ VIXL_ASSERT(masm.LiteralPoolIsEmpty());
+
+ END();
+
+ RUN();
+
+ // Check that the literals loaded correctly.
+ ASSERT_EQUAL_32(0x90abcdef, r0);
+ ASSERT_EQUAL_32(0x12345678, r1);
+ ASSERT_EQUAL_32(0x90abcdef, r2);
+ ASSERT_EQUAL_32(0x12345678, r3);
+
+ TEARDOWN();
+}
+
+
+TEST_T32(test_it_scope_and_literal_pool) {
+ // This test stresses the EnsureEmitFor check inside ITScope to make sure the
+ // number of bytes it tries to ensure we can emit is in sync with the
+ // MacroEmissionCheckScope that is usually around it.
+ SETUP();
+
+ START();
+
+ // Make sure the pool is empty.
+ masm.EmitLiteralPool(MacroAssembler::kBranchRequired);
+ ASSERT_LITERAL_POOL_SIZE(0);
+
+ Literal<uint64_t> l0(0xcafebeefdeadbaba);
+ __ Ldrd(r0, r1, &l0);
+ // Leave exactly as many bytes between cursor and pool emission checkpoint as
+ // the typical macro instruction needs (and MacroEmissionCheckScope allows
+ // for).
+ const int32_t kTypicalMacroInstructionMaxSize =
+ 8 * kMaxInstructionSizeInBytes;
+ int32_t margin =
+ masm.GetMarginBeforeLiteralEmission() - kTypicalMacroInstructionMaxSize;
+ int32_t end = masm.GetCursorOffset() + margin;
+
+ {
+ ExactAssemblyScope scope(&masm, margin, ExactAssemblyScope::kExactSize);
+ while (masm.GetCursorOffset() < end) {
+ __ nop();
+ }
+ }
+ VIXL_CHECK(masm.GetMarginBeforeLiteralEmission() ==
+ kTypicalMacroInstructionMaxSize);
+
+ // We cannot use an IT block for this instruction, hence ITScope will
+ // generate a branch over it.
+ __ Add(ne, r8, r9, 256);
+
+ END();
+
+ RUN();
+
+ // Check that the literals loaded correctly.
+ ASSERT_EQUAL_32(0xdeadbaba, r0);
+ ASSERT_EQUAL_32(0xcafebeef, r1);
+
+ TEARDOWN();
+}
+
+
+// TODO: Remove this limitation by having a sandboxing mechanism.
+#if defined(VIXL_HOST_POINTER_32)
+TEST(ldm_stm_no_writeback) {
+ SETUP();
+
+ START();
+
+ const uint32_t src[4] = {0x12345678, 0x09abcdef, 0xc001c0de, 0xdeadbeef};
+ uint32_t dst1[4] = {0x00000000, 0x00000000, 0x00000000, 0x00000000};
+ uint32_t dst2[4] = {0x00000000, 0x00000000, 0x00000000, 0x00000000};
+
+ __ Mov(r0, reinterpret_cast<uintptr_t>(src));
+ __ Ldm(r0, NO_WRITE_BACK, RegisterList(r1, r2, r3, r4));
+ ;
+ __ Ldm(r0, NO_WRITE_BACK, RegisterList(r5, r6, r9, r11));
+
+ __ Mov(r0, reinterpret_cast<uintptr_t>(dst1));
+ __ Stm(r0, NO_WRITE_BACK, RegisterList(r1, r2, r3, r4));
+ ;
+
+ __ Mov(r0, reinterpret_cast<uintptr_t>(dst2));
+ __ Stm(r0, NO_WRITE_BACK, RegisterList(r5, r6, r9, r11));
+
+ END();
+
+ RUN();
+
+ ASSERT_EQUAL_32(0x12345678, r1);
+ ASSERT_EQUAL_32(0x09abcdef, r2);
+ ASSERT_EQUAL_32(0xc001c0de, r3);
+ ASSERT_EQUAL_32(0xdeadbeef, r4);
+
+ ASSERT_EQUAL_32(0x12345678, r5);
+ ASSERT_EQUAL_32(0x09abcdef, r6);
+ ASSERT_EQUAL_32(0xc001c0de, r9);
+ ASSERT_EQUAL_32(0xdeadbeef, r11);
+
+ ASSERT_EQUAL_32(0x12345678, dst1[0]);
+ ASSERT_EQUAL_32(0x09abcdef, dst1[1]);
+ ASSERT_EQUAL_32(0xc001c0de, dst1[2]);
+ ASSERT_EQUAL_32(0xdeadbeef, dst1[3]);
+
+ ASSERT_EQUAL_32(0x12345678, dst2[0]);
+ ASSERT_EQUAL_32(0x09abcdef, dst2[1]);
+ ASSERT_EQUAL_32(0xc001c0de, dst2[2]);
+ ASSERT_EQUAL_32(0xdeadbeef, dst2[3]);
+
+ TEARDOWN();
+}
+
+
+TEST(ldm_stm_writeback) {
+ SETUP();
+
+ START();
+
+ const uint32_t src[4] = {0x12345678, 0x09abcdef, 0xc001c0de, 0xdeadbeef};
+ uint32_t dst[8] = {0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000};
+
+ __ Mov(r0, reinterpret_cast<uintptr_t>(src));
+ __ Ldm(r0, WRITE_BACK, RegisterList(r2, r3));
+ ;
+ __ Ldm(r0, WRITE_BACK, RegisterList(r4, r5));
+ ;
+
+ __ Mov(r1, reinterpret_cast<uintptr_t>(dst));
+ __ Stm(r1, WRITE_BACK, RegisterList(r2, r3, r4, r5));
+ __ Stm(r1, WRITE_BACK, RegisterList(r2, r3, r4, r5));
+
+ END();
+
+ RUN();
+
+ ASSERT_EQUAL_32(reinterpret_cast<uintptr_t>(src + 4), r0);
+ ASSERT_EQUAL_32(reinterpret_cast<uintptr_t>(dst + 8), r1);
+
+ ASSERT_EQUAL_32(0x12345678, r2);
+ ASSERT_EQUAL_32(0x09abcdef, r3);
+ ASSERT_EQUAL_32(0xc001c0de, r4);
+ ASSERT_EQUAL_32(0xdeadbeef, r5);
+
+ ASSERT_EQUAL_32(0x12345678, dst[0]);
+ ASSERT_EQUAL_32(0x09abcdef, dst[1]);
+ ASSERT_EQUAL_32(0xc001c0de, dst[2]);
+ ASSERT_EQUAL_32(0xdeadbeef, dst[3]);
+ ASSERT_EQUAL_32(0x12345678, dst[4]);
+ ASSERT_EQUAL_32(0x09abcdef, dst[5]);
+ ASSERT_EQUAL_32(0xc001c0de, dst[6]);
+ ASSERT_EQUAL_32(0xdeadbeef, dst[7]);
+
+ TEARDOWN();
+}
+
+
+TEST_A32(ldm_stm_da_ib) {
+ SETUP();
+
+ START();
+
+ const uint32_t src1[4] = {0x33333333, 0x44444444, 0x11111111, 0x22222222};
+ const uint32_t src2[4] = {0x11111111, 0x22222222, 0x33333333, 0x44444444};
+
+ uint32_t dst1[4] = {0x00000000, 0x00000000, 0x00000000, 0x00000000};
+ uint32_t dst2[4] = {0x00000000, 0x00000000, 0x00000000, 0x00000000};
+
+ __ Mov(r11, reinterpret_cast<uintptr_t>(src1 + 3));
+ __ Ldmda(r11, WRITE_BACK, RegisterList(r0, r1));
+ __ Ldmda(r11, NO_WRITE_BACK, RegisterList(r2, r3));
+
+ __ Mov(r10, reinterpret_cast<uintptr_t>(src2 - 1));
+ __ Ldmib(r10, WRITE_BACK, RegisterList(r4, r5));
+ __ Ldmib(r10, NO_WRITE_BACK, RegisterList(r6, r7));
+
+ __ Mov(r9, reinterpret_cast<uintptr_t>(dst1 + 3));
+ __ Stmda(r9, WRITE_BACK, RegisterList(r0, r1));
+ __ Stmda(r9, NO_WRITE_BACK, RegisterList(r2, r3));
+
+ __ Mov(r8, reinterpret_cast<uintptr_t>(dst2 - 1));
+ __ Stmib(r8, WRITE_BACK, RegisterList(r4, r5));
+ __ Stmib(r8, NO_WRITE_BACK, RegisterList(r6, r7));
+
+
+ END();
+
+ RUN();
+
+ ASSERT_EQUAL_32(reinterpret_cast<uintptr_t>(src1 + 1), r11);
+ ASSERT_EQUAL_32(reinterpret_cast<uintptr_t>(src2 + 1), r10);
+ ASSERT_EQUAL_32(reinterpret_cast<uintptr_t>(dst1 + 1), r9);
+ ASSERT_EQUAL_32(reinterpret_cast<uintptr_t>(dst2 + 1), r8);
+
+ ASSERT_EQUAL_32(0x11111111, r0);
+ ASSERT_EQUAL_32(0x22222222, r1);
+ ASSERT_EQUAL_32(0x33333333, r2);
+ ASSERT_EQUAL_32(0x44444444, r3);
+
+ ASSERT_EQUAL_32(0x11111111, r4);
+ ASSERT_EQUAL_32(0x22222222, r5);
+ ASSERT_EQUAL_32(0x33333333, r6);
+ ASSERT_EQUAL_32(0x44444444, r7);
+
+ ASSERT_EQUAL_32(0x33333333, dst1[0]);
+ ASSERT_EQUAL_32(0x44444444, dst1[1]);
+ ASSERT_EQUAL_32(0x11111111, dst1[2]);
+ ASSERT_EQUAL_32(0x22222222, dst1[3]);
+
+ ASSERT_EQUAL_32(0x11111111, dst2[0]);
+ ASSERT_EQUAL_32(0x22222222, dst2[1]);
+ ASSERT_EQUAL_32(0x33333333, dst2[2]);
+ ASSERT_EQUAL_32(0x44444444, dst2[3]);
+
+ TEARDOWN();
+}
+
+
+TEST(ldmdb_stmdb) {
+ SETUP();
+
+ START();
+
+ const uint32_t src[6] =
+ {0x55555555, 0x66666666, 0x33333333, 0x44444444, 0x11111111, 0x22222222};
+
+ uint32_t dst[6] =
+ {0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000};
+
+ __ Mov(r11, reinterpret_cast<uintptr_t>(src + 6));
+ __ Ldmdb(r11, WRITE_BACK, RegisterList(r1, r2));
+ __ Ldmdb(r11, WRITE_BACK, RegisterList(r3, r4));
+ __ Ldmdb(r11, NO_WRITE_BACK, RegisterList(r5, r6));
+
+ __ Mov(r10, reinterpret_cast<uintptr_t>(dst + 6));
+ __ Stmdb(r10, WRITE_BACK, RegisterList(r5, r6));
+ __ Stmdb(r10, WRITE_BACK, RegisterList(r3, r4));
+ __ Stmdb(r10, NO_WRITE_BACK, RegisterList(r1, r2));
+
+ END();
+
+ RUN();
+
+ ASSERT_EQUAL_32(reinterpret_cast<uintptr_t>(src + 2), r11);
+ ASSERT_EQUAL_32(reinterpret_cast<uintptr_t>(dst + 2), r10);
+
+ ASSERT_EQUAL_32(0x11111111, r1);
+ ASSERT_EQUAL_32(0x22222222, r2);
+ ASSERT_EQUAL_32(0x33333333, r3);
+ ASSERT_EQUAL_32(0x44444444, r4);
+ ASSERT_EQUAL_32(0x55555555, r5);
+ ASSERT_EQUAL_32(0x66666666, r6);
+
+ ASSERT_EQUAL_32(0x11111111, dst[0]);
+ ASSERT_EQUAL_32(0x22222222, dst[1]);
+ ASSERT_EQUAL_32(0x33333333, dst[2]);
+ ASSERT_EQUAL_32(0x44444444, dst[3]);
+ ASSERT_EQUAL_32(0x55555555, dst[4]);
+ ASSERT_EQUAL_32(0x66666666, dst[5]);
+
+ TEARDOWN();
+}
+#endif
+
+
+TEST(blx) {
+ SETUP();
+
+ START();
+
+ // TODO(all): Ideally this test should jump back and forth between ARM and
+ // Thumb mode and should also cover BLX immediate. Update this test if we
+ // allow VIXL assembler to change ISA anywhere in the code buffer.
+
+ Label test_start;
+ Label func1;
+ Label func2;
+
+ __ B(&test_start);
+
+ __ Bind(&func1);
+ __ Mov(r0, 0x11111111);
+ __ Push(lr);
+ {
+ size_t size_of_generated_code;
+ if (masm.IsUsingA32()) {
+ size_of_generated_code = 7 * kA32InstructionSizeInBytes;
+ } else {
+ size_of_generated_code = 5 * k32BitT32InstructionSizeInBytes +
+ 3 * k16BitT32InstructionSizeInBytes;
+ }
+ ExactAssemblyScope scope(&masm,
+ size_of_generated_code,
+ ExactAssemblyScope::kExactSize);
+ __ adr(r11, &func2);
+ if (masm.IsUsingT32()) {
+ // The jump target needs to have its least significant bit set to indicate
+ // that we are jumping into thumb mode.
+ __ orr(r11, r11, 1);
+ }
+ __ blx(r11);
+ __ pop(lr);
+ __ bx(lr);
+
+ __ bind(&func2);
+ __ movw(r1, 0x2222);
+ __ movt(r1, 0x2222);
+ __ bx(lr);
+ }
+
+ __ Bind(&test_start);
+ __ Mov(r0, 0xdeadc0de);
+ __ Mov(r1, 0xdeadc0de);
+ __ Bl(&func1);
+
+ END();
+
+ RUN();
+
+ // Really basic test to check that we reached the different parts of the test.
+ ASSERT_EQUAL_32(0x11111111, r0);
+ ASSERT_EQUAL_32(0x22222222, r1);
+
+ TEARDOWN();
+}
+
+
+// Check that B with a near hint use a narrow branch when it can.
+TEST_T32(b_near_hint) {
+ SETUP();
+ START();
+
+ Label start;
+ Label end;
+
+ __ Bind(&start);
+ __ Nop();
+
+ {
+ // Generate a branch which should be narrow.
+ EmissionCheckScope scope(&masm,
+ k16BitT32InstructionSizeInBytes,
+ EmissionCheckScope::kExactSize);
+ __ B(&start, kNear);
+ }
+ {
+ ExactAssemblyScope scope(&masm,
+ kBNarrowRange,
+ ExactAssemblyScope::kExactSize);
+ for (int32_t i = 0; i < kBNarrowRange;
+ i += k16BitT32InstructionSizeInBytes) {
+ __ nop();
+ }
+ }
+ {
+ // Generate a branch which should be wide.
+ EmissionCheckScope scope(&masm,
+ k32BitT32InstructionSizeInBytes,
+ EmissionCheckScope::kExactSize);
+ __ B(&start, kNear);
+ }
+ {
+ // Generate a forward branch which should be narrow.
+ EmissionCheckScope scope(&masm,
+ k16BitT32InstructionSizeInBytes,
+ EmissionCheckScope::kExactSize);
+ __ B(&end, kNear);
+ }
+
+ VIXL_CHECK(masm.GetMarginBeforeVeneerEmission() < kBNarrowRange);
+
+ {
+ ExactAssemblyScope scope(&masm,
+ kBNarrowRange,
+ ExactAssemblyScope::kExactSize);
+ for (int32_t i = 0; i < kBNarrowRange;
+ i += k16BitT32InstructionSizeInBytes) {
+ __ nop();
+ }
+ }
+
+ // A veneer should have been generated.
+ VIXL_CHECK(masm.GetMarginBeforeVeneerEmission() > kBNarrowRange);
+
+ __ Bind(&end);
+
+ END();
+
+ DISASSEMBLE();
+
+ TEARDOWN();
+}
+
+
+// Check that B with a far hint use a narrow branch only for a near backward
+// branch.
+TEST_T32(b_far_hint) {
+ SETUP();
+ START();
+
+ Label start;
+ Label end;
+
+ __ Bind(&start);
+ __ Nop();
+
+ {
+ // Generate a branch which should be narrow.
+ EmissionCheckScope scope(&masm,
+ k16BitT32InstructionSizeInBytes,
+ EmissionCheckScope::kExactSize);
+ __ B(&start, kFar);
+ }
+ {
+ ExactAssemblyScope scope(&masm,
+ kBNarrowRange,
+ ExactAssemblyScope::kExactSize);
+ for (int32_t i = 0; i < kBNarrowRange;
+ i += k16BitT32InstructionSizeInBytes) {
+ __ nop();
+ }
+ }
+ {
+ // Generate a branch which should be wide.
+ EmissionCheckScope scope(&masm,
+ k32BitT32InstructionSizeInBytes,
+ EmissionCheckScope::kExactSize);
+ __ B(&start, kFar);
+ }
+ {
+ // Generate a forward branch which should be wide.
+ EmissionCheckScope scope(&masm,
+ k32BitT32InstructionSizeInBytes,
+ EmissionCheckScope::kExactSize);
+ __ B(&end, kFar);
+ }
+
+ __ Bind(&end);
+
+ END();
+
+ DISASSEMBLE();
+
+ TEARDOWN();
+}
+
+
+// Check that conditional B with a near hint use a narrow branch when it can.
+TEST_T32(b_conditional_near_hint) {
+ SETUP();
+ START();
+
+ Label start;
+ Label end;
+
+ __ Bind(&start);
+ __ Nop();
+ {
+ // Generate a branch which should be narrow.
+ EmissionCheckScope scope(&masm,
+ k16BitT32InstructionSizeInBytes,
+ EmissionCheckScope::kExactSize);
+ __ B(eq, &start, kNear);
+ }
+ {
+ ExactAssemblyScope scope(&masm,
+ kBConditionalNarrowRange,
+ ExactAssemblyScope::kExactSize);
+ for (int32_t i = 0; i < kBConditionalNarrowRange;
+ i += k16BitT32InstructionSizeInBytes) {
+ __ nop();
+ }
+ }
+ {
+ // Generate a branch which should be wide.
+ EmissionCheckScope scope(&masm,
+ k32BitT32InstructionSizeInBytes,
+ EmissionCheckScope::kExactSize);
+ __ B(eq, &start, kNear);
+ }
+ {
+ // Generate a forward branch which should be narrow.
+ EmissionCheckScope scope(&masm,
+ k16BitT32InstructionSizeInBytes,
+ EmissionCheckScope::kExactSize);
+ __ B(eq, &end, kNear);
+ }
+
+ VIXL_CHECK(masm.GetMarginBeforeVeneerEmission() < kBConditionalNarrowRange);
+
+ {
+ ExactAssemblyScope scope(&masm,
+ kBConditionalNarrowRange,
+ ExactAssemblyScope::kExactSize);
+ for (int32_t i = 0; i < kBConditionalNarrowRange;
+ i += k16BitT32InstructionSizeInBytes) {
+ __ nop();
+ }
+ }
+
+ // A veneer should have been generated.
+ VIXL_CHECK(masm.GetMarginBeforeVeneerEmission() > kBConditionalNarrowRange);
+
+ __ Bind(&end);
+
+ END();
+
+ DISASSEMBLE();
+
+ TEARDOWN();
+}
+
+
+// Check that conditional B with a far hint use a narrow branch only for a
+// near backward branch.
+TEST_T32(b_conditional_far_hint) {
+ SETUP();
+ START();
+
+ Label start;
+ Label end;
+
+ __ Bind(&start);
+ __ Nop();
+
+ {
+ // Generate a branch which should be narrow.
+ EmissionCheckScope scope(&masm,
+ k16BitT32InstructionSizeInBytes,
+ EmissionCheckScope::kExactSize);
+ __ B(eq, &start, kFar);
+ }
+ {
+ ExactAssemblyScope scope(&masm,
+ kBConditionalNarrowRange,
+ ExactAssemblyScope::kExactSize);
+ for (int32_t i = 0; i < kBConditionalNarrowRange;
+ i += k16BitT32InstructionSizeInBytes) {
+ __ nop();
+ }
+ }
+ {
+ // Generate a branch which should be wide.
+ EmissionCheckScope scope(&masm,
+ k32BitT32InstructionSizeInBytes,
+ EmissionCheckScope::kExactSize);
+ __ B(eq, &start, kFar);
+ }
+ {
+ // Generate a forward branch which should be wide.
+ EmissionCheckScope scope(&masm,
+ k32BitT32InstructionSizeInBytes,
+ EmissionCheckScope::kExactSize);
+ __ B(eq, &end, kFar);
+ }
+
+ __ Bind(&end);
+
+ END();
+
+ DISASSEMBLE();
+
+ TEARDOWN();
+}
+
+
+// Check that the veneer pool is correctly emitted even if we do a lot of narrow
+// branches.
+TEST_T32(b_narrow_many) {
+ SETUP();
+ START();
+
+ static const int kLabelsCount = kBNarrowRange / 2;
+
+ Label labels[kLabelsCount];
+
+ __ Mov(r0, 0);
+
+ for (int i = 0; i < kLabelsCount; i++) {
+ __ B(&labels[i], kNear);
+ }
+
+ __ Mov(r0, 1);
+ for (int i = 0; i < kLabelsCount; i++) {
+ __ Bind(&labels[i]);
+ }
+ __ Nop();
+
+ END();
+
+ RUN();
+
+ ASSERT_EQUAL_32(0, r0);
+
+ TEARDOWN();
+}
+
+
+// Check that the veneer pool is correctly emitted even if we do a lot of narrow
+// branches and cbz.
+TEST_T32(b_narrow_and_cbz) {
+ SETUP();
+ START();
+
+ static const int kLabelsCount = kBNarrowRange / 4;
+
+ Label b_labels[kLabelsCount];
+ Label cbz_labels[kLabelsCount];
+
+ __ Mov(r0, 0);
+
+ for (int i = 0; i < kLabelsCount; i++) {
+ __ B(&b_labels[i], kNear);
+ __ Cbz(r0, &cbz_labels[i]);
+ }
+
+ __ Mov(r0, 1);
+ for (int i = 0; i < kLabelsCount; i++) {
+ __ Bind(&b_labels[i]);
+ }
+
+ __ Mov(r0, 2);
+ for (int i = 0; i < kLabelsCount; i++) {
+ __ Bind(&cbz_labels[i]);
+ }
+
+ __ Nop();
+
+ END();
+
+ RUN();
+
+ ASSERT_EQUAL_32(2, r0);
+
+ TEARDOWN();
+}
+
+
+#define CHECK_SIZE_MATCH(ASM1, ASM2) \
+ { \
+ MacroAssembler masm1(BUF_SIZE); \
+ masm1.UseInstructionSet(isa); \
+ VIXL_ASSERT(masm1.GetCursorOffset() == 0); \
+ masm1.ASM1; \
+ masm1.FinalizeCode(); \
+ int size1 = masm1.GetCursorOffset(); \
+ \
+ MacroAssembler masm2(BUF_SIZE); \
+ masm2.UseInstructionSet(isa); \
+ VIXL_ASSERT(masm2.GetCursorOffset() == 0); \
+ masm2.ASM2; \
+ masm2.FinalizeCode(); \
+ int size2 = masm2.GetCursorOffset(); \
+ \
+ bool disassemble = Test::disassemble(); \
+ if (size1 != size2) { \
+ printf("Sizes did not match:\n"); \
+ disassemble = true; \
+ } \
+ if (disassemble) { \
+ PrintDisassembler dis(std::cout, 0); \
+ printf("// " #ASM1 "\n"); \
+ if (masm1.IsUsingT32()) { \
+ dis.DisassembleT32Buffer(masm1.GetBuffer() \
+ ->GetStartAddress<uint16_t*>(), \
+ size1); \
+ } else { \
+ dis.DisassembleA32Buffer(masm1.GetBuffer() \
+ ->GetStartAddress<uint32_t*>(), \
+ size1); \
+ } \
+ printf("\n"); \
+ \
+ dis.SetCodeAddress(0); \
+ printf("// " #ASM2 "\n"); \
+ if (masm2.IsUsingT32()) { \
+ dis.DisassembleT32Buffer(masm2.GetBuffer() \
+ ->GetStartAddress<uint16_t*>(), \
+ size2); \
+ } else { \
+ dis.DisassembleA32Buffer(masm2.GetBuffer() \
+ ->GetStartAddress<uint32_t*>(), \
+ size2); \
+ } \
+ printf("\n"); \
+ } \
+ VIXL_CHECK(size1 == size2); \
+ }
+
+
+TEST_T32(macro_assembler_commute) {
+ // Test that the MacroAssembler will commute operands if it means it can use a
+ // 16-bit instruction with the same effect.
+
+ // TODO: The commented-out tests should pass, but don't. When they are fixed,
+ // we should update this test.
+
+ // CHECK_SIZE_MATCH(Adc(DontCare, r7, r6, r7),
+ // Adc(DontCare, r7, r7, r6));
+
+ // CHECK_SIZE_MATCH(Adc(DontCare, eq, r7, r6, r7),
+ // Adc(DontCare, eq, r7, r7, r6));
+
+ CHECK_SIZE_MATCH(Add(DontCare, r1, r2, r7), Add(DontCare, r1, r7, r2));
+
+ CHECK_SIZE_MATCH(Add(DontCare, lt, r1, r2, r7),
+ Add(DontCare, lt, r1, r7, r2));
+
+ // CHECK_SIZE_MATCH(Add(DontCare, r4, r4, r10),
+ // Add(DontCare, r4, r10, r4));
+
+ // CHECK_SIZE_MATCH(Add(DontCare, eq, r4, r4, r10),
+ // Add(DontCare, eq, r4, r10, r4));
+
+ // CHECK_SIZE_MATCH(Add(DontCare, r7, sp, r7),
+ // Add(DontCare, r7, r7, sp));
+
+ // CHECK_SIZE_MATCH(Add(DontCare, eq, r7, sp, r7),
+ // Add(DontCare, eq, r7, r7, sp));
+
+ // CHECK_SIZE_MATCH(Add(DontCare, sp, sp, r10),
+ // Add(DontCare, sp, r10, sp));
+
+ // CHECK_SIZE_MATCH(Add(DontCare, eq, sp, sp, r10),
+ // Add(DontCare, eq, sp, r10, sp));
+
+ // CHECK_SIZE_MATCH(And(DontCare, r7, r7, r6),
+ // And(DontCare, r7, r6, r7));
+
+ // CHECK_SIZE_MATCH(And(DontCare, eq, r7, r7, r6),
+ // And(DontCare, eq, r7, r6, r7));
+
+ // CHECK_SIZE_MATCH(Eor(DontCare, r7, r7, r6),
+ // Eor(DontCare, r7, r6, r7));
+
+ // CHECK_SIZE_MATCH(Eor(DontCare, eq, r7, r7, r6),
+ // Eor(DontCare, eq, r7, r6, r7));
+
+ // CHECK_SIZE_MATCH(Mul(DontCare, r0, r1, r0),
+ // Mul(DontCare, r0, r0, r1));
+
+ // CHECK_SIZE_MATCH(Mul(DontCare, eq, r0, r1, r0),
+ // Mul(DontCare, eq, r0, r0, r1));
+
+ // CHECK_SIZE_MATCH(Orr(DontCare, r7, r7, r6),
+ // Orr(DontCare, r7, r6, r7));
+
+ // CHECK_SIZE_MATCH(Orr(DontCare, eq, r7, r7, r6),
+ // Orr(DontCare, eq, r7, r6, r7));
+
+
+ CHECK_SIZE_MATCH(Adc(r7, r6, r7), Adc(r7, r7, r6));
+
+ // CHECK_SIZE_MATCH(Adc(eq, r7, r6, r7),
+ // Adc(eq, r7, r7, r6));
+
+ CHECK_SIZE_MATCH(Add(r1, r2, r7), Add(r1, r7, r2));
+
+ CHECK_SIZE_MATCH(Add(lt, r1, r2, r7), Add(lt, r1, r7, r2));
+
+ // CHECK_SIZE_MATCH(Add(r4, r4, r10),
+ // Add(r4, r10, r4));
+
+ // CHECK_SIZE_MATCH(Add(eq, r4, r4, r10),
+ // Add(eq, r4, r10, r4));
+
+ // CHECK_SIZE_MATCH(Add(r7, sp, r7),
+ // Add(r7, r7, sp));
+
+ // CHECK_SIZE_MATCH(Add(eq, r7, sp, r7),
+ // Add(eq, r7, r7, sp));
+
+ // CHECK_SIZE_MATCH(Add(sp, sp, r10),
+ // Add(sp, r10, sp));
+
+ // CHECK_SIZE_MATCH(Add(eq, sp, sp, r10),
+ // Add(eq, sp, r10, sp));
+
+ CHECK_SIZE_MATCH(And(r7, r7, r6), And(r7, r6, r7));
+
+ // CHECK_SIZE_MATCH(And(eq, r7, r7, r6),
+ // And(eq, r7, r6, r7));
+
+ CHECK_SIZE_MATCH(Eor(r7, r7, r6), Eor(r7, r6, r7));
+
+ // CHECK_SIZE_MATCH(Eor(eq, r7, r7, r6),
+ // Eor(eq, r7, r6, r7));
+
+ CHECK_SIZE_MATCH(Mul(r0, r1, r0), Mul(r0, r0, r1));
+
+ // CHECK_SIZE_MATCH(Mul(eq, r0, r1, r0),
+ // Mul(eq, r0, r0, r1));
+
+ CHECK_SIZE_MATCH(Orr(r7, r7, r6), Orr(r7, r6, r7));
+
+ // CHECK_SIZE_MATCH(Orr(eq, r7, r7, r6),
+ // Orr(eq, r7, r6, r7));
+
+
+ // CHECK_SIZE_MATCH(Adcs(r7, r6, r7),
+ // Adcs(r7, r7, r6));
+
+ // CHECK_SIZE_MATCH(Adcs(eq, r7, r6, r7),
+ // Adcs(eq, r7, r7, r6));
+
+ CHECK_SIZE_MATCH(Adds(r1, r2, r7), Adds(r1, r7, r2));
+
+ CHECK_SIZE_MATCH(Adds(lt, r1, r2, r7), Adds(lt, r1, r7, r2));
+
+ CHECK_SIZE_MATCH(Adds(r4, r4, r10), Adds(r4, r10, r4));
+
+ CHECK_SIZE_MATCH(Adds(eq, r4, r4, r10), Adds(eq, r4, r10, r4));
+
+ CHECK_SIZE_MATCH(Adds(r7, sp, r7), Adds(r7, r7, sp));
+
+ CHECK_SIZE_MATCH(Adds(eq, r7, sp, r7), Adds(eq, r7, r7, sp));
+
+ CHECK_SIZE_MATCH(Adds(sp, sp, r10), Adds(sp, r10, sp));
+
+ CHECK_SIZE_MATCH(Adds(eq, sp, sp, r10), Adds(eq, sp, r10, sp));
+
+ // CHECK_SIZE_MATCH(Ands(r7, r7, r6),
+ // Ands(r7, r6, r7));
+
+ // CHECK_SIZE_MATCH(Ands(eq, r7, r7, r6),
+ // Ands(eq, r7, r6, r7));
+
+ // CHECK_SIZE_MATCH(Eors(r7, r7, r6),
+ // Eors(r7, r6, r7));
+
+ // CHECK_SIZE_MATCH(Eors(eq, r7, r7, r6),
+ // Eors(eq, r7, r6, r7));
+
+ // CHECK_SIZE_MATCH(Muls(r0, r1, r0),
+ // Muls(r0, r0, r1));
+
+ // CHECK_SIZE_MATCH(Muls(eq, r0, r1, r0),
+ // Muls(eq, r0, r0, r1));
+
+ // CHECK_SIZE_MATCH(Orrs(r7, r7, r6),
+ // Orrs(r7, r6, r7));
+
+ // CHECK_SIZE_MATCH(Orrs(eq, r7, r7, r6),
+ // Orrs(eq, r7, r6, r7));
+}
+
+
+} // namespace aarch32
+} // namespace vixl