| // Copyright 2013 the V8 project 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 Google Inc. 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 AND 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 <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| |
| #include <cmath> |
| #include <limits> |
| #include <optional> |
| |
| #include "src/base/utils/random-number-generator.h" |
| #include "src/codegen/arm64/assembler-arm64-inl.h" |
| #include "src/codegen/arm64/decoder-arm64-inl.h" |
| #include "src/codegen/arm64/macro-assembler-arm64-inl.h" |
| #include "src/codegen/arm64/utils-arm64.h" |
| #include "src/codegen/macro-assembler.h" |
| #include "src/diagnostics/arm64/disasm-arm64.h" |
| #include "src/execution/arm64/simulator-arm64.h" |
| #include "src/heap/factory.h" |
| #include "test/cctest/cctest.h" |
| #include "test/cctest/test-utils-arm64.h" |
| #include "test/common/assembler-tester.h" |
| |
| namespace v8 { |
| namespace internal { |
| |
| // Test infrastructure. |
| // |
| // Tests are functions which accept no parameters and have no return values. |
| // The testing code should not perform an explicit return once completed. For |
| // example to test the mov immediate instruction a very simple test would be: |
| // |
| // TEST(mov_x0_one) { |
| // SETUP(); |
| // |
| // START(); |
| // __ mov(x0, Operand(1)); |
| // END(); |
| // |
| // RUN(); |
| // |
| // CHECK_EQUAL_64(1, x0); |
| // } |
| // |
| // Within a START ... END block all registers but sp can be modified. sp has to |
| // be explicitly saved/restored. The END() macro replaces the function return |
| // so it may appear multiple times in a test if the test has multiple exit |
| // points. |
| // |
| // Once the test has been run all integer and floating point registers as well |
| // as flags are accessible through a RegisterDump instance, see |
| // utils-arm64.cc for more info on RegisterDump. |
| // |
| // We provide some helper assert to handle common cases: |
| // |
| // CHECK_EQUAL_32(int32_t, int_32t) |
| // CHECK_EQUAL_FP32(float, float) |
| // CHECK_EQUAL_32(int32_t, W register) |
| // CHECK_EQUAL_FP32(float, S register) |
| // CHECK_EQUAL_64(int64_t, int_64t) |
| // CHECK_EQUAL_FP64(double, double) |
| // CHECK_EQUAL_64(int64_t, X register) |
| // CHECK_EQUAL_64(X register, X register) |
| // CHECK_EQUAL_FP64(double, D register) |
| // |
| // e.g. CHECK_EQUAL_64(0.5, d30); |
| // |
| // If more advance computation is required before the assert then access the |
| // RegisterDump named core directly: |
| // |
| // CHECK_EQUAL_64(0x1234, core.xreg(0) & 0xFFFF); |
| |
| #if 0 // TODO(all): enable. |
| static v8::Persistent<v8::Context> env; |
| |
| static void InitializeVM() { |
| if (env.IsEmpty()) { |
| env = v8::Context::New(); |
| } |
| } |
| #endif |
| |
| #define __ masm. |
| |
| #define BUF_SIZE 8192 |
| #define SETUP() SETUP_SIZE(BUF_SIZE) |
| |
| #define INIT_V8() CcTest::InitializeVM(); |
| |
| // Declare that a test will use an optional feature, which means execution needs |
| // to be behind CAN_RUN(). |
| #define SETUP_FEATURE(feature) \ |
| const bool can_run = CpuFeatures::IsSupported(feature); \ |
| USE(can_run); \ |
| CpuFeatureScope feature_scope(&masm, feature, \ |
| CpuFeatureScope::kDontCheckSupported) |
| |
| #ifdef USE_SIMULATOR |
| |
| // The simulator can always run the code even when IsSupported(f) is false. |
| #define CAN_RUN() true |
| |
| // Run tests with the simulator. |
| #define SETUP_SIZE(buf_size) \ |
| Isolate* isolate = CcTest::i_isolate(); \ |
| HandleScope scope(isolate); \ |
| CHECK_NOT_NULL(isolate); \ |
| auto owned_buf = \ |
| AllocateAssemblerBuffer(buf_size, nullptr, JitPermission::kNoJit); \ |
| MacroAssembler masm(isolate, v8::internal::CodeObjectRequired::kYes, \ |
| ExternalAssemblerBuffer(owned_buf->start(), buf_size)); \ |
| std::optional<AssemblerBufferWriteScope> rw_buffer_scope; \ |
| Decoder<DispatchingDecoderVisitor>* decoder = \ |
| new Decoder<DispatchingDecoderVisitor>(); \ |
| Simulator simulator(decoder); \ |
| std::unique_ptr<PrintDisassembler> pdis; \ |
| RegisterDump core; \ |
| HandleScope handle_scope(isolate); \ |
| Handle<Code> code; \ |
| if (i::v8_flags.trace_sim) { \ |
| pdis.reset(new PrintDisassembler(stdout)); \ |
| decoder->PrependVisitor(pdis.get()); \ |
| } |
| |
| // Reset the assembler and simulator, so that instructions can be generated, |
| // but don't actually emit any code. This can be used by tests that need to |
| // emit instructions at the start of the buffer. Note that START_AFTER_RESET |
| // must be called before any callee-saved register is modified, and before an |
| // END is encountered. |
| // |
| // Most tests should call START, rather than call RESET directly. |
| #define RESET() \ |
| __ Reset(); \ |
| simulator.ResetState(); |
| |
| #define START_AFTER_RESET() \ |
| __ PushCalleeSavedRegisters(); \ |
| __ Debug("Start test.", __LINE__, TRACE_ENABLE | LOG_ALL); |
| |
| #define START() \ |
| RESET(); \ |
| START_AFTER_RESET(); |
| |
| #define RUN() \ |
| simulator.RunFrom(reinterpret_cast<Instruction*>(code->instruction_start())) |
| |
| #define END() \ |
| __ Debug("End test.", __LINE__, TRACE_DISABLE | LOG_ALL); \ |
| core.Dump(&masm); \ |
| __ PopCalleeSavedRegisters(); \ |
| __ Ret(); \ |
| { \ |
| CodeDesc desc; \ |
| __ GetCode(masm.isolate(), &desc); \ |
| code = Factory::CodeBuilder(isolate, desc, CodeKind::FOR_TESTING).Build(); \ |
| if (v8_flags.print_code) code->Print(); \ |
| } |
| |
| #else // ifdef USE_SIMULATOR. |
| |
| #define CAN_RUN() can_run |
| |
| // Run the test on real hardware or models. |
| #define SETUP_SIZE(buf_size) \ |
| Isolate* isolate = CcTest::i_isolate(); \ |
| HandleScope scope(isolate); \ |
| CHECK_NOT_NULL(isolate); \ |
| auto owned_buf = AllocateAssemblerBuffer(buf_size); \ |
| std::optional<AssemblerBufferWriteScope> rw_buffer_scope; \ |
| MacroAssembler masm(isolate, v8::internal::CodeObjectRequired::kYes, \ |
| owned_buf->CreateView()); \ |
| HandleScope handle_scope(isolate); \ |
| Handle<Code> code; \ |
| RegisterDump core; |
| |
| #define RESET() \ |
| rw_buffer_scope.emplace(*owned_buf); \ |
| __ Reset(); \ |
| __ CodeEntry(); \ |
| /* Reset the machine state (like simulator.ResetState()). */ \ |
| __ Msr(NZCV, xzr); \ |
| __ Msr(FPCR, xzr); |
| |
| #define START_AFTER_RESET() \ |
| __ PushCalleeSavedRegisters(); |
| |
| #define START() \ |
| RESET(); \ |
| START_AFTER_RESET(); |
| |
| #define RUN() \ |
| { \ |
| /* Reset the scope and thus make the buffer executable. */ \ |
| rw_buffer_scope.reset(); \ |
| auto f = GeneratedCode<void>::FromCode(isolate, *code); \ |
| f.Call(); \ |
| } |
| |
| #define END() \ |
| core.Dump(&masm); \ |
| __ PopCalleeSavedRegisters(); \ |
| __ Ret(); \ |
| { \ |
| CodeDesc desc; \ |
| __ GetCode(masm.isolate(), &desc); \ |
| code = Factory::CodeBuilder(isolate, desc, CodeKind::FOR_TESTING).Build(); \ |
| if (v8_flags.print_code) code->Print(); \ |
| } |
| |
| #endif // ifdef USE_SIMULATOR. |
| |
| #define CHECK_EQUAL_NZCV(expected) \ |
| CHECK(EqualNzcv(expected, core.flags_nzcv())) |
| |
| #define CHECK_EQUAL_REGISTERS(expected) \ |
| CHECK(EqualV8Registers(&expected, &core)) |
| |
| #define CHECK_EQUAL_32(expected, result) \ |
| CHECK(Equal32(static_cast<uint32_t>(expected), &core, result)) |
| |
| #define CHECK_EQUAL_FP32(expected, result) \ |
| CHECK(EqualFP32(expected, &core, result)) |
| |
| #define CHECK_EQUAL_64(expected, result) \ |
| CHECK(Equal64(expected, &core, result)) |
| |
| #define CHECK_FULL_HEAP_OBJECT_IN_REGISTER(expected, result) \ |
| CHECK(Equal64(expected->ptr(), &core, result)) |
| |
| #define CHECK_NOT_ZERO_AND_NOT_EQUAL_64(reg0, reg1) \ |
| { \ |
| int64_t value0 = core.xreg(reg0.code()); \ |
| int64_t value1 = core.xreg(reg1.code()); \ |
| CHECK_NE(0, value0); \ |
| CHECK_NE(0, value1); \ |
| CHECK_NE(value0, value1); \ |
| } |
| |
| #define CHECK_EQUAL_FP64(expected, result) \ |
| CHECK(EqualFP64(expected, &core, result)) |
| |
| // Expected values for 128-bit comparisons are passed as two 64-bit values, |
| // where expected_h (high) is <127:64> and expected_l (low) is <63:0>. |
| #define CHECK_EQUAL_128(expected_h, expected_l, result) \ |
| CHECK(Equal128(expected_h, expected_l, &core, result)) |
| |
| #ifdef DEBUG |
| #define CHECK_CONSTANT_POOL_SIZE(expected) \ |
| CHECK_EQ(expected, __ GetConstantPoolEntriesSizeForTesting()) |
| #else |
| #define CHECK_CONSTANT_POOL_SIZE(expected) ((void)0) |
| #endif |
| |
| TEST(stack_ops) { |
| INIT_V8(); |
| SETUP(); |
| |
| START(); |
| // save sp. |
| __ Mov(x29, sp); |
| |
| // Set the sp to a known value. |
| __ Mov(x16, 0x1000); |
| __ Mov(sp, x16); |
| __ Mov(x0, sp); |
| |
| // Add immediate to the sp, and move the result to a normal register. |
| __ Add(sp, sp, Operand(0x50)); |
| __ Mov(x1, sp); |
| |
| // Add extended to the sp, and move the result to a normal register. |
| __ Mov(x17, 0xFFF); |
| __ Add(sp, sp, Operand(x17, SXTB)); |
| __ Mov(x2, sp); |
| |
| // Create an sp using a logical instruction, and move to normal register. |
| __ Orr(sp, xzr, Operand(0x1FFF)); |
| __ Mov(x3, sp); |
| |
| // Write wsp using a logical instruction. |
| __ Orr(wsp, wzr, Operand(0xFFFFFFF8L)); |
| __ Mov(x4, sp); |
| |
| // Write sp, and read back wsp. |
| __ Orr(sp, xzr, Operand(0xFFFFFFF8L)); |
| __ Mov(w5, wsp); |
| |
| // restore sp. |
| __ Mov(sp, x29); |
| END(); |
| |
| RUN(); |
| |
| CHECK_EQUAL_64(0x1000, x0); |
| CHECK_EQUAL_64(0x1050, x1); |
| CHECK_EQUAL_64(0x104F, x2); |
| CHECK_EQUAL_64(0x1FFF, x3); |
| CHECK_EQUAL_64(0xFFFFFFF8, x4); |
| CHECK_EQUAL_64(0xFFFFFFF8, x5); |
| } |
| |
| TEST(mvn) { |
| INIT_V8(); |
| SETUP(); |
| |
| START(); |
| __ Mvn(w0, 0xFFF); |
| __ Mvn(x1, 0xFFF); |
| __ Mvn(w2, Operand(w0, LSL, 1)); |
| __ Mvn(x3, Operand(x1, LSL, 2)); |
| __ Mvn(w4, Operand(w0, LSR, 3)); |
| __ Mvn(x5, Operand(x1, LSR, 4)); |
| __ Mvn(w6, Operand(w0, ASR, 11)); |
| __ Mvn(x7, Operand(x1, ASR, 12)); |
| __ Mvn(w8, Operand(w0, ROR, 13)); |
| __ Mvn(x9, Operand(x1, ROR, 14)); |
| __ Mvn(w10, Operand(w2, UXTB)); |
| __ Mvn(x11, Operand(x2, SXTB, 1)); |
| __ Mvn(w12, Operand(w2, UXTH, 2)); |
| __ Mvn(x13, Operand(x2, SXTH, 3)); |
| __ Mvn(x14, Operand(w2, UXTW, 4)); |
| __ Mvn(x15, Operand(w2, SXTW, 4)); |
| END(); |
| |
| RUN(); |
| |
| CHECK_EQUAL_64(0xFFFFF000, x0); |
| CHECK_EQUAL_64(0xFFFFFFFFFFFFF000UL, x1); |
| CHECK_EQUAL_64(0x00001FFF, x2); |
| CHECK_EQUAL_64(0x0000000000003FFFUL, x3); |
| CHECK_EQUAL_64(0xE00001FF, x4); |
| CHECK_EQUAL_64(0xF0000000000000FFUL, x5); |
| CHECK_EQUAL_64(0x00000001, x6); |
| CHECK_EQUAL_64(0x0, x7); |
| CHECK_EQUAL_64(0x7FF80000, x8); |
| CHECK_EQUAL_64(0x3FFC000000000000UL, x9); |
| CHECK_EQUAL_64(0xFFFFFF00, x10); |
| CHECK_EQUAL_64(0x0000000000000001UL, x11); |
| CHECK_EQUAL_64(0xFFFF8003, x12); |
| CHECK_EQUAL_64(0xFFFFFFFFFFFF0007UL, x13); |
| CHECK_EQUAL_64(0xFFFFFFFFFFFE000FUL, x14); |
| CHECK_EQUAL_64(0xFFFFFFFFFFFE000FUL, x15); |
| } |
| |
| TEST(mov) { |
| INIT_V8(); |
| SETUP(); |
| |
| START(); |
| __ Mov(x0, 0xFFFFFFFFFFFFFFFFL); |
| __ Mov(x1, 0xFFFFFFFFFFFFFFFFL); |
| __ Mov(x2, 0xFFFFFFFFFFFFFFFFL); |
| __ Mov(x3, 0xFFFFFFFFFFFFFFFFL); |
| |
| __ Mov(x0, 0x0123456789ABCDEFL); |
| |
| __ movz(x1, 0xABCDLL << 16); |
| __ movk(x2, 0xABCDLL << 32); |
| __ movn(x3, 0xABCDLL << 48); |
| |
| __ Mov(x4, 0x0123456789ABCDEFL); |
| __ Mov(x5, x4); |
| |
| __ Mov(w6, -1); |
| |
| // Test that moves back to the same register have the desired effect. This |
| // is a no-op for X registers, and a truncation for W registers. |
| __ Mov(x7, 0x0123456789ABCDEFL); |
| __ Mov(x7, x7); |
| __ Mov(x8, 0x0123456789ABCDEFL); |
| __ Mov(w8, w8); |
| __ Mov(x9, 0x0123456789ABCDEFL); |
| __ Mov(x9, Operand(x9)); |
| __ Mov(x10, 0x0123456789ABCDEFL); |
| __ Mov(w10, Operand(w10)); |
| |
| __ Mov(w11, 0xFFF); |
| __ Mov(x12, 0xFFF); |
| __ Mov(w13, Operand(w11, LSL, 1)); |
| __ Mov(x14, Operand(x12, LSL, 2)); |
| __ Mov(w15, Operand(w11, LSR, 3)); |
| __ Mov(x28, Operand(x12, LSR, 4)); |
| __ Mov(w19, Operand(w11, ASR, 11)); |
| __ Mov(x20, Operand(x12, ASR, 12)); |
| __ Mov(w21, Operand(w11, ROR, 13)); |
| __ Mov(x22, Operand(x12, ROR, 14)); |
| __ Mov(w23, Operand(w13, UXTB)); |
| __ Mov(x24, Operand(x13, SXTB, 1)); |
| __ Mov(w25, Operand(w13, UXTH, 2)); |
| __ Mov(x26, Operand(x13, SXTH, 3)); |
| __ Mov(x27, Operand(w13, UXTW, 4)); |
| END(); |
| |
| RUN(); |
| |
| CHECK_EQUAL_64(0x0123456789ABCDEFL, x0); |
| CHECK_EQUAL_64(0x00000000ABCD0000L, x1); |
| CHECK_EQUAL_64(0xFFFFABCDFFFFFFFFL, x2); |
| CHECK_EQUAL_64(0x5432FFFFFFFFFFFFL, x3); |
| CHECK_EQUAL_64(x4, x5); |
| CHECK_EQUAL_32(-1, w6); |
| CHECK_EQUAL_64(0x0123456789ABCDEFL, x7); |
| CHECK_EQUAL_32(0x89ABCDEFL, w8); |
| CHECK_EQUAL_64(0x0123456789ABCDEFL, x9); |
| CHECK_EQUAL_32(0x89ABCDEFL, w10); |
| CHECK_EQUAL_64(0x00000FFF, x11); |
| CHECK_EQUAL_64(0x0000000000000FFFUL, x12); |
| CHECK_EQUAL_64(0x00001FFE, x13); |
| CHECK_EQUAL_64(0x0000000000003FFCUL, x14); |
| CHECK_EQUAL_64(0x000001FF, x15); |
| CHECK_EQUAL_64(0x00000000000000FFUL, x28); |
| CHECK_EQUAL_64(0x00000001, x19); |
| CHECK_EQUAL_64(0x0, x20); |
| CHECK_EQUAL_64(0x7FF80000, x21); |
| CHECK_EQUAL_64(0x3FFC000000000000UL, x22); |
| CHECK_EQUAL_64(0x000000FE, x23); |
| CHECK_EQUAL_64(0xFFFFFFFFFFFFFFFCUL, x24); |
| CHECK_EQUAL_64(0x00007FF8, x25); |
| CHECK_EQUAL_64(0x000000000000FFF0UL, x26); |
| CHECK_EQUAL_64(0x000000000001FFE0UL, x27); |
| } |
| |
| TEST(move_pair) { |
| INIT_V8(); |
| SETUP(); |
| |
| START(); |
| __ Mov(x0, 0xabababab); |
| __ Mov(x1, 0xbabababa); |
| __ Mov(x2, 0x12341234); |
| __ Mov(x3, 0x43214321); |
| |
| // No overlap: |
| // x4 <- x0 |
| // x5 <- x1 |
| __ MovePair(x4, x0, x5, x1); |
| |
| // Overlap but we can swap moves: |
| // x2 <- x0 |
| // x6 <- x2 |
| __ MovePair(x2, x0, x6, x2); |
| |
| // Overlap but can be done: |
| // x7 <- x3 |
| // x3 <- x0 |
| __ MovePair(x7, x3, x3, x0); |
| |
| // Swap. |
| // x0 <- x1 |
| // x1 <- x0 |
| __ MovePair(x0, x1, x1, x0); |
| |
| END(); |
| |
| RUN(); |
| |
| // x4 <- x0 |
| // x5 <- x1 |
| CHECK_EQUAL_64(0xabababab, x4); |
| CHECK_EQUAL_64(0xbabababa, x5); |
| |
| // x2 <- x0 |
| // x6 <- x2 |
| CHECK_EQUAL_64(0xabababab, x2); |
| CHECK_EQUAL_64(0x12341234, x6); |
| |
| // x7 <- x3 |
| // x3 <- x0 |
| CHECK_EQUAL_64(0x43214321, x7); |
| CHECK_EQUAL_64(0xabababab, x3); |
| |
| // x0 and x1 should be swapped. |
| CHECK_EQUAL_64(0xbabababa, x0); |
| CHECK_EQUAL_64(0xabababab, x1); |
| } |
| |
| TEST(mov_imm_w) { |
| INIT_V8(); |
| SETUP(); |
| |
| START(); |
| __ Mov(w0, 0xFFFFFFFFL); |
| __ Mov(w1, 0xFFFF1234L); |
| __ Mov(w2, 0x1234FFFFL); |
| __ Mov(w3, 0x00000000L); |
| __ Mov(w4, 0x00001234L); |
| __ Mov(w5, 0x12340000L); |
| __ Mov(w6, 0x12345678L); |
| __ Mov(w7, (int32_t)0x80000000); |
| __ Mov(w8, (int32_t)0xFFFF0000); |
| __ Mov(w9, kWMinInt); |
| END(); |
| |
| RUN(); |
| |
| CHECK_EQUAL_64(0xFFFFFFFFL, x0); |
| CHECK_EQUAL_64(0xFFFF1234L, x1); |
| CHECK_EQUAL_64(0x1234FFFFL, x2); |
| CHECK_EQUAL_64(0x00000000L, x3); |
| CHECK_EQUAL_64(0x00001234L, x4); |
| CHECK_EQUAL_64(0x12340000L, x5); |
| CHECK_EQUAL_64(0x12345678L, x6); |
| CHECK_EQUAL_64(0x80000000L, x7); |
| CHECK_EQUAL_64(0xFFFF0000L, x8); |
| CHECK_EQUAL_32(kWMinInt, w9); |
| } |
| |
| TEST(mov_imm_x) { |
| INIT_V8(); |
| SETUP(); |
| |
| START(); |
| __ Mov(x0, 0xFFFFFFFFFFFFFFFFL); |
| __ Mov(x1, 0xFFFFFFFFFFFF1234L); |
| __ Mov(x2, 0xFFFFFFFF12345678L); |
| __ Mov(x3, 0xFFFF1234FFFF5678L); |
| __ Mov(x4, 0x1234FFFFFFFF5678L); |
| __ Mov(x5, 0x1234FFFF5678FFFFL); |
| __ Mov(x6, 0x12345678FFFFFFFFL); |
| __ Mov(x7, 0x1234FFFFFFFFFFFFL); |
| __ Mov(x8, 0x123456789ABCFFFFL); |
| __ Mov(x9, 0x12345678FFFF9ABCL); |
| __ Mov(x10, 0x1234FFFF56789ABCL); |
| __ Mov(x11, 0xFFFF123456789ABCL); |
| __ Mov(x12, 0x0000000000000000L); |
| __ Mov(x13, 0x0000000000001234L); |
| __ Mov(x14, 0x0000000012345678L); |
| __ Mov(x15, 0x0000123400005678L); |
| __ Mov(x30, 0x1234000000005678L); |
| __ Mov(x19, 0x1234000056780000L); |
| __ Mov(x20, 0x1234567800000000L); |
| __ Mov(x21, 0x1234000000000000L); |
| __ Mov(x22, 0x123456789ABC0000L); |
| __ Mov(x23, 0x1234567800009ABCL); |
| __ Mov(x24, 0x1234000056789ABCL); |
| __ Mov(x25, 0x0000123456789ABCL); |
| __ Mov(x26, 0x123456789ABCDEF0L); |
| __ Mov(x27, 0xFFFF000000000001L); |
| __ Mov(x28, 0x8000FFFF00000000L); |
| END(); |
| |
| RUN(); |
| |
| CHECK_EQUAL_64(0xFFFFFFFFFFFF1234L, x1); |
| CHECK_EQUAL_64(0xFFFFFFFF12345678L, x2); |
| CHECK_EQUAL_64(0xFFFF1234FFFF5678L, x3); |
| CHECK_EQUAL_64(0x1234FFFFFFFF5678L, x4); |
| CHECK_EQUAL_64(0x1234FFFF5678FFFFL, x5); |
| CHECK_EQUAL_64(0x12345678FFFFFFFFL, x6); |
| CHECK_EQUAL_64(0x1234FFFFFFFFFFFFL, x7); |
| CHECK_EQUAL_64(0x123456789ABCFFFFL, x8); |
| CHECK_EQUAL_64(0x12345678FFFF9ABCL, x9); |
| CHECK_EQUAL_64(0x1234FFFF56789ABCL, x10); |
| CHECK_EQUAL_64(0xFFFF123456789ABCL, x11); |
| CHECK_EQUAL_64(0x0000000000000000L, x12); |
| CHECK_EQUAL_64(0x0000000000001234L, x13); |
| CHECK_EQUAL_64(0x0000000012345678L, x14); |
| CHECK_EQUAL_64(0x0000123400005678L, x15); |
| CHECK_EQUAL_64(0x1234000000005678L, x30); |
| CHECK_EQUAL_64(0x1234000056780000L, x19); |
| CHECK_EQUAL_64(0x1234567800000000L, x20); |
| CHECK_EQUAL_64(0x1234000000000000L, x21); |
| CHECK_EQUAL_64(0x123456789ABC0000L, x22); |
| CHECK_EQUAL_64(0x1234567800009ABCL, x23); |
| CHECK_EQUAL_64(0x1234000056789ABCL, x24); |
| CHECK_EQUAL_64(0x0000123456789ABCL, x25); |
| CHECK_EQUAL_64(0x123456789ABCDEF0L, x26); |
| CHECK_EQUAL_64(0xFFFF000000000001L, x27); |
| CHECK_EQUAL_64(0x8000FFFF00000000L, x28); |
| } |
| |
| TEST(orr) { |
| INIT_V8(); |
| SETUP(); |
| |
| START(); |
| __ Mov(x0, 0xF0F0); |
| __ Mov(x1, 0xF00000FF); |
| |
| __ Orr(x2, x0, Operand(x1)); |
| __ Orr(w3, w0, Operand(w1, LSL, 28)); |
| __ Orr(x4, x0, Operand(x1, LSL, 32)); |
| __ Orr(x5, x0, Operand(x1, LSR, 4)); |
| __ Orr(w6, w0, Operand(w1, ASR, 4)); |
| __ Orr(x7, x0, Operand(x1, ASR, 4)); |
| __ Orr(w8, w0, Operand(w1, ROR, 12)); |
| __ Orr(x9, x0, Operand(x1, ROR, 12)); |
| __ Orr(w10, w0, Operand(0xF)); |
| __ Orr(x11, x0, Operand(0xF0000000F0000000L)); |
| END(); |
| |
| RUN(); |
| |
| CHECK_EQUAL_64(0xF000F0FF, x2); |
| CHECK_EQUAL_64(0xF000F0F0, x3); |
| CHECK_EQUAL_64(0xF00000FF0000F0F0L, x4); |
| CHECK_EQUAL_64(0x0F00F0FF, x5); |
| CHECK_EQUAL_64(0xFF00F0FF, x6); |
| CHECK_EQUAL_64(0x0F00F0FF, x7); |
| CHECK_EQUAL_64(0x0FFFF0F0, x8); |
| CHECK_EQUAL_64(0x0FF00000000FF0F0L, x9); |
| CHECK_EQUAL_64(0xF0FF, x10); |
| CHECK_EQUAL_64(0xF0000000F000F0F0L, x11); |
| } |
| |
| TEST(orr_extend) { |
| INIT_V8(); |
| SETUP(); |
| |
| START(); |
| __ Mov(x0, 1); |
| __ Mov(x1, 0x8000000080008080UL); |
| __ Orr(w6, w0, Operand(w1, UXTB)); |
| __ Orr(x7, x0, Operand(x1, UXTH, 1)); |
| __ Orr(w8, w0, Operand(w1, UXTW, 2)); |
| __ Orr(x9, x0, Operand(x1, UXTX, 3)); |
| __ Orr(w10, w0, Operand(w1, SXTB)); |
| __ Orr(x11, x0, Operand(x1, SXTH, 1)); |
| __ Orr(x12, x0, Operand(x1, SXTW, 2)); |
| __ Orr(x13, x0, Operand(x1, SXTX, 3)); |
| END(); |
| |
| RUN(); |
| |
| CHECK_EQUAL_64(0x00000081, x6); |
| CHECK_EQUAL_64(0x00010101, x7); |
| CHECK_EQUAL_64(0x00020201, x8); |
| CHECK_EQUAL_64(0x0000000400040401UL, x9); |
| CHECK_EQUAL_64(0x00000000FFFFFF81UL, x10); |
| CHECK_EQUAL_64(0xFFFFFFFFFFFF0101UL, x11); |
| CHECK_EQUAL_64(0xFFFFFFFE00020201UL, x12); |
| CHECK_EQUAL_64(0x0000000400040401UL, x13); |
| } |
| |
| TEST(bitwise_wide_imm) { |
| INIT_V8(); |
| SETUP(); |
| |
| START(); |
| __ Mov(x0, 0); |
| __ Mov(x1, 0xF0F0F0F0F0F0F0F0UL); |
| |
| __ Orr(x10, x0, Operand(0x1234567890ABCDEFUL)); |
| __ Orr(w11, w1, Operand(0x90ABCDEF)); |
| |
| __ Orr(w12, w0, kWMinInt); |
| __ Eor(w13, w0, kWMinInt); |
| END(); |
| |
| RUN(); |
| |
| CHECK_EQUAL_64(0, x0); |
| CHECK_EQUAL_64(0xF0F0F0F0F0F0F0F0UL, x1); |
| CHECK_EQUAL_64(0x1234567890ABCDEFUL, x10); |
| CHECK_EQUAL_64(0xF0FBFDFFUL, x11); |
| CHECK_EQUAL_32(kWMinInt, w12); |
| CHECK_EQUAL_32(kWMinInt, w13); |
| } |
| |
| TEST(orn) { |
| INIT_V8(); |
| SETUP(); |
| |
| START(); |
| __ Mov(x0, 0xF0F0); |
| __ Mov(x1, 0xF00000FF); |
| |
| __ Orn(x2, x0, Operand(x1)); |
| __ Orn(w3, w0, Operand(w1, LSL, 4)); |
| __ Orn(x4, x0, Operand(x1, LSL, 4)); |
| __ Orn(x5, x0, Operand(x1, LSR, 1)); |
| __ Orn(w6, w0, Operand(w1, ASR, 1)); |
| __ Orn(x7, x0, Operand(x1, ASR, 1)); |
| __ Orn(w8, w0, Operand(w1, ROR, 16)); |
| __ Orn(x9, x0, Operand(x1, ROR, 16)); |
| __ Orn(w10, w0, Operand(0xFFFF)); |
| __ Orn(x11, x0, Operand(0xFFFF0000FFFFL)); |
| END(); |
| |
| RUN(); |
| |
| CHECK_EQUAL_64(0xFFFFFFFF0FFFFFF0L, x2); |
| CHECK_EQUAL_64(0xFFFFF0FF, x3); |
| CHECK_EQUAL_64(0xFFFFFFF0FFFFF0FFL, x4); |
| CHECK_EQUAL_64(0xFFFFFFFF87FFFFF0L, x5); |
| CHECK_EQUAL_64(0x07FFFFF0, x6); |
| CHECK_EQUAL_64(0xFFFFFFFF87FFFFF0L, x7); |
| CHECK_EQUAL_64(0xFF00FFFF, x8); |
| CHECK_EQUAL_64(0xFF00FFFFFFFFFFFFL, x9); |
| CHECK_EQUAL_64(0xFFFFF0F0, x10); |
| CHECK_EQUAL_64(0xFFFF0000FFFFF0F0L, x11); |
| } |
| |
| TEST(orn_extend) { |
| INIT_V8(); |
| SETUP(); |
| |
| START(); |
| __ Mov(x0, 1); |
| __ Mov(x1, 0x8000000080008081UL); |
| __ Orn(w6, w0, Operand(w1, UXTB)); |
| __ Orn(x7, x0, Operand(x1, UXTH, 1)); |
| __ Orn(w8, w0, Operand(w1, UXTW, 2)); |
| __ Orn(x9, x0, Operand(x1, UXTX, 3)); |
| __ Orn(w10, w0, Operand(w1, SXTB)); |
| __ Orn(x11, x0, Operand(x1, SXTH, 1)); |
| __ Orn(x12, x0, Operand(x1, SXTW, 2)); |
| __ Orn(x13, x0, Operand(x1, SXTX, 3)); |
| END(); |
| |
| RUN(); |
| |
| CHECK_EQUAL_64(0xFFFFFF7F, x6); |
| CHECK_EQUAL_64(0xFFFFFFFFFFFEFEFDUL, x7); |
| CHECK_EQUAL_64(0xFFFDFDFB, x8); |
| CHECK_EQUAL_64(0xFFFFFFFBFFFBFBF7UL, x9); |
| CHECK_EQUAL_64(0x0000007F, x10); |
| CHECK_EQUAL_64(0x0000FEFD, x11); |
| CHECK_EQUAL_64(0x00000001FFFDFDFBUL, x12); |
| CHECK_EQUAL_64(0xFFFFFFFBFFFBFBF7UL, x13); |
| } |
| |
| TEST(and_) { |
| INIT_V8(); |
| SETUP(); |
| |
| START(); |
| __ Mov(x0, 0xFFF0); |
| __ Mov(x1, 0xF00000FF); |
| |
| __ And(x2, x0, Operand(x1)); |
| __ And(w3, w0, Operand(w1, LSL, 4)); |
| __ And(x4, x0, Operand(x1, LSL, 4)); |
| __ And(x5, x0, Operand(x1, LSR, 1)); |
| __ And(w6, w0, Operand(w1, ASR, 20)); |
| __ And(x7, x0, Operand(x1, ASR, 20)); |
| __ And(w8, w0, Operand(w1, ROR, 28)); |
| __ And(x9, x0, Operand(x1, ROR, 28)); |
| __ And(w10, w0, Operand(0xFF00)); |
| __ And(x11, x0, Operand(0xFF)); |
| END(); |
| |
| RUN(); |
| |
| CHECK_EQUAL_64(0x000000F0, x2); |
| CHECK_EQUAL_64(0x00000FF0, x3); |
| CHECK_EQUAL_64(0x00000FF0, x4); |
| CHECK_EQUAL_64(0x00000070, x5); |
| CHECK_EQUAL_64(0x0000FF00, x6); |
| CHECK_EQUAL_64(0x00000F00, x7); |
| CHECK_EQUAL_64(0x00000FF0, x8); |
| CHECK_EQUAL_64(0x00000000, x9); |
| CHECK_EQUAL_64(0x0000FF00, x10); |
| CHECK_EQUAL_64(0x000000F0, x11); |
| } |
| |
| TEST(and_extend) { |
| INIT_V8(); |
| SETUP(); |
| |
| START(); |
| __ Mov(x0, 0xFFFFFFFFFFFFFFFFUL); |
| __ Mov(x1, 0x8000000080008081UL); |
| __ And(w6, w0, Operand(w1, UXTB)); |
| __ And(x7, x0, Operand(x1, UXTH, 1)); |
| __ And(w8, w0, Operand(w1, UXTW, 2)); |
| __ And(x9, x0, Operand(x1, UXTX, 3)); |
| __ And(w10, w0, Operand(w1, SXTB)); |
| __ And(x11, x0, Operand(x1, SXTH, 1)); |
| __ And(x12, x0, Operand(x1, SXTW, 2)); |
| __ And(x13, x0, Operand(x1, SXTX, 3)); |
| END(); |
| |
| RUN(); |
| |
| CHECK_EQUAL_64(0x00000081, x6); |
| CHECK_EQUAL_64(0x00010102, x7); |
| CHECK_EQUAL_64(0x00020204, x8); |
| CHECK_EQUAL_64(0x0000000400040408UL, x9); |
| CHECK_EQUAL_64(0xFFFFFF81, x10); |
| CHECK_EQUAL_64(0xFFFFFFFFFFFF0102UL, x11); |
| CHECK_EQUAL_64(0xFFFFFFFE00020204UL, x12); |
| CHECK_EQUAL_64(0x0000000400040408UL, x13); |
| } |
| |
| TEST(ands) { |
| INIT_V8(); |
| SETUP(); |
| |
| START(); |
| __ Mov(x1, 0xF00000FF); |
| __ Ands(w0, w1, Operand(w1)); |
| END(); |
| |
| RUN(); |
| |
| CHECK_EQUAL_NZCV(NFlag); |
| CHECK_EQUAL_64(0xF00000FF, x0); |
| |
| START(); |
| __ Mov(x0, 0xFFF0); |
| __ Mov(x1, 0xF00000FF); |
| __ Ands(w0, w0, Operand(w1, LSR, 4)); |
| END(); |
| |
| RUN(); |
| |
| CHECK_EQUAL_NZCV(ZFlag); |
| CHECK_EQUAL_64(0x00000000, x0); |
| |
| START(); |
| __ Mov(x0, 0x8000000000000000L); |
| __ Mov(x1, 0x00000001); |
| __ Ands(x0, x0, Operand(x1, ROR, 1)); |
| END(); |
| |
| RUN(); |
| |
| CHECK_EQUAL_NZCV(NFlag); |
| CHECK_EQUAL_64(0x8000000000000000L, x0); |
| |
| START(); |
| __ Mov(x0, 0xFFF0); |
| __ Ands(w0, w0, Operand(0xF)); |
| END(); |
| |
| RUN(); |
| |
| CHECK_EQUAL_NZCV(ZFlag); |
| CHECK_EQUAL_64(0x00000000, x0); |
| |
| START(); |
| __ Mov(x0, 0xFF000000); |
| __ Ands(w0, w0, Operand(0x80000000)); |
| END(); |
| |
| RUN(); |
| |
| CHECK_EQUAL_NZCV(NFlag); |
| CHECK_EQUAL_64(0x80000000, x0); |
| } |
| |
| TEST(bic) { |
| INIT_V8(); |
| SETUP(); |
| |
| START(); |
| __ Mov(x0, 0xFFF0); |
| __ Mov(x1, 0xF00000FF); |
| |
| __ Bic(x2, x0, Operand(x1)); |
| __ Bic(w3, w0, Operand(w1, LSL, 4)); |
| __ Bic(x4, x0, Operand(x1, LSL, 4)); |
| __ Bic(x5, x0, Operand(x1, LSR, 1)); |
| __ Bic(w6, w0, Operand(w1, ASR, 20)); |
| __ Bic(x7, x0, Operand(x1, ASR, 20)); |
| __ Bic(w8, w0, Operand(w1, ROR, 28)); |
| __ Bic(x9, x0, Operand(x1, ROR, 24)); |
| __ Bic(x10, x0, Operand(0x1F)); |
| __ Bic(x11, x0, Operand(0x100)); |
| |
| // Test bic into sp when the constant cannot be encoded in the immediate |
| // field. |
| // Use x20 to preserve sp. We check for the result via x21 because the |
| // test infrastructure requires that sp be restored to its original value. |
| __ Mov(x20, sp); |
| __ Mov(x0, 0xFFFFFF); |
| __ Bic(sp, x0, Operand(0xABCDEF)); |
| __ Mov(x21, sp); |
| __ Mov(sp, x20); |
| END(); |
| |
| RUN(); |
| |
| CHECK_EQUAL_64(0x0000FF00, x2); |
| CHECK_EQUAL_64(0x0000F000, x3); |
| CHECK_EQUAL_64(0x0000F000, x4); |
| CHECK_EQUAL_64(0x0000FF80, x5); |
| CHECK_EQUAL_64(0x000000F0, x6); |
| CHECK_EQUAL_64(0x0000F0F0, x7); |
| CHECK_EQUAL_64(0x0000F000, x8); |
| CHECK_EQUAL_64(0x0000FF00, x9); |
| CHECK_EQUAL_64(0x0000FFE0, x10); |
| CHECK_EQUAL_64(0x0000FEF0, x11); |
| |
| CHECK_EQUAL_64(0x543210, x21); |
| } |
| |
| TEST(bic_extend) { |
| INIT_V8(); |
| SETUP(); |
| |
| START(); |
| __ Mov(x0, 0xFFFFFFFFFFFFFFFFUL); |
| __ Mov(x1, 0x8000000080008081UL); |
| __ Bic(w6, w0, Operand(w1, UXTB)); |
| __ Bic(x7, x0, Operand(x1, UXTH, 1)); |
| __ Bic(w8, w0, Operand(w1, UXTW, 2)); |
| __ Bic(x9, x0, Operand(x1, UXTX, 3)); |
| __ Bic(w10, w0, Operand(w1, SXTB)); |
| __ Bic(x11, x0, Operand(x1, SXTH, 1)); |
| __ Bic(x12, x0, Operand(x1, SXTW, 2)); |
| __ Bic(x13, x0, Operand(x1, SXTX, 3)); |
| END(); |
| |
| RUN(); |
| |
| CHECK_EQUAL_64(0xFFFFFF7E, x6); |
| CHECK_EQUAL_64(0xFFFFFFFFFFFEFEFDUL, x7); |
| CHECK_EQUAL_64(0xFFFDFDFB, x8); |
| CHECK_EQUAL_64(0xFFFFFFFBFFFBFBF7UL, x9); |
| CHECK_EQUAL_64(0x0000007E, x10); |
| CHECK_EQUAL_64(0x0000FEFD, x11); |
| CHECK_EQUAL_64(0x00000001FFFDFDFBUL, x12); |
| CHECK_EQUAL_64(0xFFFFFFFBFFFBFBF7UL, x13); |
| } |
| |
| TEST(bics) { |
| INIT_V8(); |
| SETUP(); |
| |
| START(); |
| __ Mov(x1, 0xFFFF); |
| __ Bics(w0, w1, Operand(w1)); |
| END(); |
| |
| RUN(); |
| |
| CHECK_EQUAL_NZCV(ZFlag); |
| CHECK_EQUAL_64(0x00000000, x0); |
| |
| START(); |
| __ Mov(x0, 0xFFFFFFFF); |
| __ Bics(w0, w0, Operand(w0, LSR, 1)); |
| END(); |
| |
| RUN(); |
| |
| CHECK_EQUAL_NZCV(NFlag); |
| CHECK_EQUAL_64(0x80000000, x0); |
| |
| START(); |
| __ Mov(x0, 0x8000000000000000L); |
| __ Mov(x1, 0x00000001); |
| __ Bics(x0, x0, Operand(x1, ROR, 1)); |
| END(); |
| |
| RUN(); |
| |
| CHECK_EQUAL_NZCV(ZFlag); |
| CHECK_EQUAL_64(0x00000000, x0); |
| |
| START(); |
| __ Mov(x0, 0xFFFFFFFFFFFFFFFFL); |
| __ Bics(x0, x0, Operand(0x7FFFFFFFFFFFFFFFL)); |
| END(); |
| |
| RUN(); |
| |
| CHECK_EQUAL_NZCV(NFlag); |
| CHECK_EQUAL_64(0x8000000000000000L, x0); |
| |
| START(); |
| __ Mov(w0, 0xFFFF0000); |
| __ Bics(w0, w0, Operand(0xFFFFFFF0)); |
| END(); |
| |
| RUN(); |
| |
| CHECK_EQUAL_NZCV(ZFlag); |
| CHECK_EQUAL_64(0x00000000, x0); |
| } |
| |
| TEST(eor) { |
| INIT_V8(); |
| SETUP(); |
| |
| START(); |
| __ Mov(x0, 0xFFF0); |
| __ Mov(x1, 0xF00000FF); |
| |
| __ Eor(x2, x0, Operand(x1)); |
| __ Eor(w3, w0, Operand(w1, LSL, 4)); |
| __ Eor(x4, x0, Operand(x1, LSL, 4)); |
| __ Eor(x5, x0, Operand(x1, LSR, 1)); |
| __ Eor(w6, w0, Operand(w1, ASR, 20)); |
| __ Eor(x7, x0, Operand(x1, ASR, 20)); |
| __ Eor(w8, w0, Operand(w1, ROR, 28)); |
| __ Eor(x9, x0, Operand(x1, ROR, 28)); |
| __ Eor(w10, w0, Operand(0xFF00FF00)); |
| __ Eor(x11, x0, Operand(0xFF00FF00FF00FF00L)); |
| END(); |
| |
| RUN(); |
| |
| CHECK_EQUAL_64(0xF000FF0F, x2); |
| CHECK_EQUAL_64(0x0000F000, x3); |
| CHECK_EQUAL_64(0x0000000F0000F000L, x4); |
| CHECK_EQUAL_64(0x7800FF8F, x5); |
| CHECK_EQUAL_64(0xFFFF00F0, x6); |
| CHECK_EQUAL_64(0x0000F0F0, x7); |
| CHECK_EQUAL_64(0x0000F00F, x8); |
| CHECK_EQUAL_64(0x00000FF00000FFFFL, x9); |
| CHECK_EQUAL_64(0xFF0000F0, x10); |
| CHECK_EQUAL_64(0xFF00FF00FF0000F0L, x11); |
| } |
| |
| TEST(eor_extend) { |
| INIT_V8(); |
| SETUP(); |
| |
| START(); |
| __ Mov(x0, 0x1111111111111111UL); |
| __ Mov(x1, 0x8000000080008081UL); |
| __ Eor(w6, w0, Operand(w1, UXTB)); |
| __ Eor(x7, x0, Operand(x1, UXTH, 1)); |
| __ Eor(w8, w0, Operand(w1, UXTW, 2)); |
| __ Eor(x9, x0, Operand(x1, UXTX, 3)); |
| __ Eor(w10, w0, Operand(w1, SXTB)); |
| __ Eor(x11, x0, Operand(x1, SXTH, 1)); |
| __ Eor(x12, x0, Operand(x1, SXTW, 2)); |
| __ Eor(x13, x0, Operand(x1, SXTX, 3)); |
| END(); |
| |
| RUN(); |
| |
| CHECK_EQUAL_64(0x11111190, x6); |
| CHECK_EQUAL_64(0x1111111111101013UL, x7); |
| CHECK_EQUAL_64(0x11131315, x8); |
| CHECK_EQUAL_64(0x1111111511151519UL, x9); |
| CHECK_EQUAL_64(0xEEEEEE90, x10); |
| CHECK_EQUAL_64(0xEEEEEEEEEEEE1013UL, x11); |
| CHECK_EQUAL_64(0xEEEEEEEF11131315UL, x12); |
| CHECK_EQUAL_64(0x1111111511151519UL, x13); |
| } |
| |
| TEST(eon) { |
| INIT_V8(); |
| SETUP(); |
| |
| START(); |
| __ Mov(x0, 0xFFF0); |
| __ Mov(x1, 0xF00000FF); |
| |
| __ Eon(x2, x0, Operand(x1)); |
| __ Eon(w3, w0, Operand(w1, LSL, 4)); |
| __ Eon(x4, x0, Operand(x1, LSL, 4)); |
| __ Eon(x5, x0, Operand(x1, LSR, 1)); |
| __ Eon(w6, w0, Operand(w1, ASR, 20)); |
| __ Eon(x7, x0, Operand(x1, ASR, 20)); |
| __ Eon(w8, w0, Operand(w1, ROR, 28)); |
| __ Eon(x9, x0, Operand(x1, ROR, 28)); |
| __ Eon(w10, w0, Operand(0x03C003C0)); |
| __ Eon(x11, x0, Operand(0x0000100000001000L)); |
| END(); |
| |
| RUN(); |
| |
| CHECK_EQUAL_64(0xFFFFFFFF0FFF00F0L, x2); |
| CHECK_EQUAL_64(0xFFFF0FFF, x3); |
| CHECK_EQUAL_64(0xFFFFFFF0FFFF0FFFL, x4); |
| CHECK_EQUAL_64(0xFFFFFFFF87FF0070L, x5); |
| CHECK_EQUAL_64(0x0000FF0F, x6); |
| CHECK_EQUAL_64(0xFFFFFFFFFFFF0F0FL, x7); |
| CHECK_EQUAL_64(0xFFFF0FF0, x8); |
| CHECK_EQUAL_64(0xFFFFF00FFFFF0000L, x9); |
| CHECK_EQUAL_64(0xFC3F03CF, x10); |
| CHECK_EQUAL_64(0xFFFFEFFFFFFF100FL, x11); |
| } |
| |
| TEST(eon_extend) { |
| INIT_V8(); |
| SETUP(); |
| |
| START(); |
| __ Mov(x0, 0x1111111111111111UL); |
| __ Mov(x1, 0x8000000080008081UL); |
| __ Eon(w6, w0, Operand(w1, UXTB)); |
| __ Eon(x7, x0, Operand(x1, UXTH, 1)); |
| __ Eon(w8, w0, Operand(w1, UXTW, 2)); |
| __ Eon(x9, x0, Operand(x1, UXTX, 3)); |
| __ Eon(w10, w0, Operand(w1, SXTB)); |
| __ Eon(x11, x0, Operand(x1, SXTH, 1)); |
| __ Eon(x12, x0, Operand(x1, SXTW, 2)); |
| __ Eon(x13, x0, Operand(x1, SXTX, 3)); |
| END(); |
| |
| RUN(); |
| |
| CHECK_EQUAL_64(0xEEEEEE6F, x6); |
| CHECK_EQUAL_64(0xEEEEEEEEEEEFEFECUL, x7); |
| CHECK_EQUAL_64(0xEEECECEA, x8); |
| CHECK_EQUAL_64(0xEEEEEEEAEEEAEAE6UL, x9); |
| CHECK_EQUAL_64(0x1111116F, x10); |
| CHECK_EQUAL_64(0x111111111111EFECUL, x11); |
| CHECK_EQUAL_64(0x11111110EEECECEAUL, x12); |
| CHECK_EQUAL_64(0xEEEEEEEAEEEAEAE6UL, x13); |
| } |
| |
| TEST(mul) { |
| INIT_V8(); |
| SETUP(); |
| |
| START(); |
| __ Mov(x16, 0); |
| __ Mov(x17, 1); |
| __ Mov(x15, 0xFFFFFFFF); |
| __ Mov(x19, 0xFFFFFFFFFFFFFFFFUL); |
| |
| __ Mul(w0, w16, w16); |
| __ Mul(w1, w16, w17); |
| __ Mul(w2, w17, w15); |
| __ Mul(w3, w15, w19); |
| __ Mul(x4, x16, x16); |
| __ Mul(x5, x17, x15); |
| __ Mul(x6, x15, x19); |
| __ Mul(x7, x19, x19); |
| __ Smull(x8, w17, w15); |
| __ Smull(x9, w15, w15); |
| __ Smull(x10, w19, w19); |
| __ Mneg(w11, w16, w16); |
| __ Mneg(w12, w16, w17); |
| __ Mneg(w13, w17, w15); |
| __ Mneg(w14, w15, w19); |
| __ Mneg(x20, x16, x16); |
| __ Mneg(x21, x17, x15); |
| __ Mneg(x22, x15, x19); |
| __ Mneg(x23, x19, x19); |
| END(); |
| |
| RUN(); |
| |
| CHECK_EQUAL_64(0, x0); |
| CHECK_EQUAL_64(0, x1); |
| CHECK_EQUAL_64(0xFFFFFFFF, x2); |
| CHECK_EQUAL_64(1, x3); |
| CHECK_EQUAL_64(0, x4); |
| CHECK_EQUAL_64(0xFFFFFFFF, x5); |
| CHECK_EQUAL_64(0xFFFFFFFF00000001UL, x6); |
| CHECK_EQUAL_64(1, x7); |
| CHECK_EQUAL_64(0xFFFFFFFFFFFFFFFFUL, x8); |
| CHECK_EQUAL_64(1, x9); |
| CHECK_EQUAL_64(1, x10); |
| CHECK_EQUAL_64(0, x11); |
| CHECK_EQUAL_64(0, x12); |
| CHECK_EQUAL_64(1, x13); |
| CHECK_EQUAL_64(0xFFFFFFFF, x14); |
| CHECK_EQUAL_64(0, x20); |
| CHECK_EQUAL_64(0xFFFFFFFF00000001UL, x21); |
| CHECK_EQUAL_64(0xFFFFFFFF, x22); |
| CHECK_EQUAL_64(0xFFFFFFFFFFFFFFFFUL, x23); |
| } |
| |
| static void SmullHelper(int64_t expected, int64_t a, int64_t b) { |
| SETUP(); |
| START(); |
| __ Mov(w0, a); |
| __ Mov(w1, b); |
| __ Smull(x2, w0, w1); |
| END(); |
| RUN(); |
| CHECK_EQUAL_64(expected, x2); |
| } |
| |
| TEST(smull) { |
| INIT_V8(); |
| SmullHelper(0, 0, 0); |
| SmullHelper(1, 1, 1); |
| SmullHelper(-1, -1, 1); |
| SmullHelper(1, -1, -1); |
| SmullHelper(0xFFFFFFFF80000000, 0x80000000, 1); |
| SmullHelper(0x0000000080000000, 0x00010000, 0x00008000); |
| } |
| |
| TEST(madd) { |
| INIT_V8(); |
| SETUP(); |
| |
| START(); |
| __ Mov(x16, 0); |
| __ Mov(x17, 1); |
| __ Mov(x28, 0xFFFFFFFF); |
| __ Mov(x19, 0xFFFFFFFFFFFFFFFFUL); |
| |
| __ Madd(w0, w16, w16, w16); |
| __ Madd(w1, w16, w16, w17); |
| __ Madd(w2, w16, w16, w28); |
| __ Madd(w3, w16, w16, w19); |
| __ Madd(w4, w16, w17, w17); |
| __ Madd(w5, w17, w17, w28); |
| __ Madd(w6, w17, w17, w19); |
| __ Madd(w7, w17, w28, w16); |
| __ Madd(w8, w17, w28, w28); |
| __ Madd(w9, w28, w28, w17); |
| __ Madd(w10, w28, w19, w28); |
| __ Madd(w11, w19, w19, w19); |
| |
| __ Madd(x12, x16, x16, x16); |
| __ Madd(x13, x16, x16, x17); |
| __ Madd(x14, x16, x16, x28); |
| __ Madd(x15, x16, x16, x19); |
| __ Madd(x20, x16, x17, x17); |
| __ Madd(x21, x17, x17, x28); |
| __ Madd(x22, x17, x17, x19); |
| __ Madd(x23, x17, x28, x16); |
| __ Madd(x24, x17, x28, x28); |
| __ Madd(x25, x28, x28, x17); |
| __ Madd(x26, x28, x19, x28); |
| __ Madd(x27, x19, x19, x19); |
| |
| END(); |
| |
| RUN(); |
| |
| CHECK_EQUAL_64(0, x0); |
| CHECK_EQUAL_64(1, x1); |
| CHECK_EQUAL_64(0xFFFFFFFF, x2); |
| CHECK_EQUAL_64(0xFFFFFFFF, x3); |
| CHECK_EQUAL_64(1, x4); |
| CHECK_EQUAL_64(0, x5); |
| CHECK_EQUAL_64(0, x6); |
| CHECK_EQUAL_64(0xFFFFFFFF, x7); |
| CHECK_EQUAL_64(0xFFFFFFFE, x8); |
| CHECK_EQUAL_64(2, x9); |
| CHECK_EQUAL_64(0, x10); |
| CHECK_EQUAL_64(0, x11); |
| |
| CHECK_EQUAL_64(0, x12); |
| CHECK_EQUAL_64(1, x13); |
| CHECK_EQUAL_64(0xFFFFFFFF, x14); |
| CHECK_EQUAL_64(0xFFFFFFFFFFFFFFFF, x15); |
| CHECK_EQUAL_64(1, x20); |
| CHECK_EQUAL_64(0x100000000UL, x21); |
| CHECK_EQUAL_64(0, x22); |
| CHECK_EQUAL_64(0xFFFFFFFF, x23); |
| CHECK_EQUAL_64(0x1FFFFFFFE, x24); |
| CHECK_EQUAL_64(0xFFFFFFFE00000002UL, x25); |
| CHECK_EQUAL_64(0, x26); |
| CHECK_EQUAL_64(0, x27); |
| } |
| |
| TEST(msub) { |
| INIT_V8(); |
| SETUP(); |
| |
| START(); |
| __ Mov(x16, 0); |
| __ Mov(x17, 1); |
| __ Mov(x28, 0xFFFFFFFF); |
| __ Mov(x19, 0xFFFFFFFFFFFFFFFFUL); |
| |
| __ Msub(w0, w16, w16, w16); |
| __ Msub(w1, w16, w16, w17); |
| __ Msub(w2, w16, w16, w28); |
| __ Msub(w3, w16, w16, w19); |
| __ Msub(w4, w16, w17, w17); |
| __ Msub(w5, w17, w17, w28); |
| __ Msub(w6, w17, w17, w19); |
| __ Msub(w7, w17, w28, w16); |
| __ Msub(w8, w17, w28, w28); |
| __ Msub(w9, w28, w28, w17); |
| __ Msub(w10, w28, w19, w28); |
| __ Msub(w11, w19, w19, w19); |
| |
| __ Msub(x12, x16, x16, x16); |
| __ Msub(x13, x16, x16, x17); |
| __ Msub(x14, x16, x16, x28); |
| __ Msub(x15, x16, x16, x19); |
| __ Msub(x20, x16, x17, x17); |
| __ Msub(x21, x17, x17, x28); |
| __ Msub(x22, x17, x17, x19); |
| __ Msub(x23, x17, x28, x16); |
| __ Msub(x24, x17, x28, x28); |
| __ Msub(x25, x28, x28, x17); |
| __ Msub(x26, x28, x19, x28); |
| __ Msub(x27, x19, x19, x19); |
| |
| END(); |
| |
| RUN(); |
| |
| CHECK_EQUAL_64(0, x0); |
| CHECK_EQUAL_64(1, x1); |
| CHECK_EQUAL_64(0xFFFFFFFF, x2); |
| CHECK_EQUAL_64(0xFFFFFFFF, x3); |
| CHECK_EQUAL_64(1, x4); |
| CHECK_EQUAL_64(0xFFFFFFFE, x5); |
| CHECK_EQUAL_64(0xFFFFFFFE, x6); |
| CHECK_EQUAL_64(1, x7); |
| CHECK_EQUAL_64(0, x8); |
| CHECK_EQUAL_64(0, x9); |
| CHECK_EQUAL_64(0xFFFFFFFE, x10); |
| CHECK_EQUAL_64(0xFFFFFFFE, x11); |
| |
| CHECK_EQUAL_64(0, x12); |
| CHECK_EQUAL_64(1, x13); |
| CHECK_EQUAL_64(0xFFFFFFFF, x14); |
| CHECK_EQUAL_64(0xFFFFFFFFFFFFFFFFUL, x15); |
| CHECK_EQUAL_64(1, x20); |
| CHECK_EQUAL_64(0xFFFFFFFEUL, x21); |
| CHECK_EQUAL_64(0xFFFFFFFFFFFFFFFEUL, x22); |
| CHECK_EQUAL_64(0xFFFFFFFF00000001UL, x23); |
| CHECK_EQUAL_64(0, x24); |
| CHECK_EQUAL_64(0x200000000UL, x25); |
| CHECK_EQUAL_64(0x1FFFFFFFEUL, x26); |
| CHECK_EQUAL_64(0xFFFFFFFFFFFFFFFEUL, x27); |
| } |
| |
| TEST(smulh) { |
| INIT_V8(); |
| SETUP(); |
| |
| START(); |
| __ Mov(x20, 0); |
| __ Mov(x21, 1); |
| __ Mov(x22, 0x0000000100000000L); |
| __ Mov(x23, 0x12345678); |
| __ Mov(x24, 0x0123456789ABCDEFL); |
| __ Mov(x25, 0x0000000200000000L); |
| __ Mov(x26, 0x8000000000000000UL); |
| __ Mov(x27, 0xFFFFFFFFFFFFFFFFUL); |
| __ Mov(x28, 0x5555555555555555UL); |
| __ Mov(x29, 0xAAAAAAAAAAAAAAAAUL); |
| |
| __ Smulh(x0, x20, x24); |
| __ Smulh(x1, x21, x24); |
| __ Smulh(x2, x22, x23); |
| __ Smulh(x3, x22, x24); |
| __ Smulh(x4, x24, x25); |
| __ Smulh(x5, x23, x27); |
| __ Smulh(x6, x26, x26); |
| __ Smulh(x7, x26, x27); |
| __ Smulh(x8, x27, x27); |
| __ Smulh(x9, x28, x28); |
| __ Smulh(x10, x28, x29); |
| __ Smulh(x11, x29, x29); |
| END(); |
| |
| RUN(); |
| |
| CHECK_EQUAL_64(0, x0); |
| CHECK_EQUAL_64(0, x1); |
| CHECK_EQUAL_64(0, x2); |
| CHECK_EQUAL_64(0x01234567, x3); |
| CHECK_EQUAL_64(0x02468ACF, x4); |
| CHECK_EQUAL_64(0xFFFFFFFFFFFFFFFFUL, x5); |
| CHECK_EQUAL_64(0x4000000000000000UL, x6); |
| CHECK_EQUAL_64(0, x7); |
| CHECK_EQUAL_64(0, x8); |
| CHECK_EQUAL_64(0x1C71C71C71C71C71UL, x9); |
| CHECK_EQUAL_64(0xE38E38E38E38E38EUL, x10); |
| CHECK_EQUAL_64(0x1C71C71C71C71C72UL, x11); |
| } |
| |
| TEST(smaddl_umaddl) { |
| INIT_V8(); |
| SETUP(); |
| |
| START(); |
| __ Mov(x17, 1); |
| __ Mov(x28, 0xFFFFFFFF); |
| __ Mov(x19, 0xFFFFFFFFFFFFFFFFUL); |
| __ Mov(x20, 4); |
| __ Mov(x21, 0x200000000UL); |
| |
| __ Smaddl(x9, w17, w28, x20); |
| __ Smaddl(x10, w28, w28, x20); |
| __ Smaddl(x11, w19, w19, x20); |
| __ Smaddl(x12, w19, w19, x21); |
| __ Umaddl(x13, w17, w28, x20); |
| __ Umaddl(x14, w28, w28, x20); |
| __ Umaddl(x15, w19, w19, x20); |
| __ Umaddl(x22, w19, w19, x21); |
| END(); |
| |
| RUN(); |
| |
| CHECK_EQUAL_64(3, x9); |
| CHECK_EQUAL_64(5, x10); |
| CHECK_EQUAL_64(5, x11); |
| CHECK_EQUAL_64(0x200000001UL, x12); |
| CHECK_EQUAL_64(0x100000003UL, x13); |
| CHECK_EQUAL_64(0xFFFFFFFE00000005UL, x14); |
| CHECK_EQUAL_64(0xFFFFFFFE00000005UL, x15); |
| CHECK_EQUAL_64(0x1, x22); |
| } |
| |
| TEST(smsubl_umsubl) { |
| INIT_V8(); |
| SETUP(); |
| |
| START(); |
| __ Mov(x17, 1); |
| __ Mov(x28, 0xFFFFFFFF); |
| __ Mov(x19, 0xFFFFFFFFFFFFFFFFUL); |
| __ Mov(x20, 4); |
| __ Mov(x21, 0x200000000UL); |
| |
| __ Smsubl(x9, w17, w28, x20); |
| __ Smsubl(x10, w28, w28, x20); |
| __ Smsubl(x11, w19, w19, x20); |
| __ Smsubl(x12, w19, w19, x21); |
| __ Umsubl(x13, w17, w28, x20); |
| __ Umsubl(x14, w28, w28, x20); |
| __ Umsubl(x15, w19, w19, x20); |
| __ Umsubl(x22, w19, w19, x21); |
| END(); |
| |
| RUN(); |
| |
| CHECK_EQUAL_64(5, x9); |
| CHECK_EQUAL_64(3, x10); |
| CHECK_EQUAL_64(3, x11); |
| CHECK_EQUAL_64(0x1FFFFFFFFUL, x12); |
| CHECK_EQUAL_64(0xFFFFFFFF00000005UL, x13); |
| CHECK_EQUAL_64(0x200000003UL, x14); |
| CHECK_EQUAL_64(0x200000003UL, x15); |
| CHECK_EQUAL_64(0x3FFFFFFFFUL, x22); |
| } |
| |
| TEST(div) { |
| INIT_V8(); |
| SETUP(); |
| |
| START(); |
| __ Mov(x16, 1); |
| __ Mov(x17, 0xFFFFFFFF); |
| __ Mov(x30, 0xFFFFFFFFFFFFFFFFUL); |
| __ Mov(x19, 0x80000000); |
| __ Mov(x20, 0x8000000000000000UL); |
| __ Mov(x21, 2); |
| |
| __ Udiv(w0, w16, w16); |
| __ Udiv(w1, w17, w16); |
| __ Sdiv(w2, w16, w16); |
| __ Sdiv(w3, w16, w17); |
| __ Sdiv(w4, w17, w30); |
| |
| __ Udiv(x5, x16, x16); |
| __ Udiv(x6, x17, x30); |
| __ Sdiv(x7, x16, x16); |
| __ Sdiv(x8, x16, x17); |
| __ Sdiv(x9, x17, x30); |
| |
| __ Udiv(w10, w19, w21); |
| __ Sdiv(w11, w19, w21); |
| __ Udiv(x12, x19, x21); |
| __ Sdiv(x13, x19, x21); |
| __ Udiv(x14, x20, x21); |
| __ Sdiv(x15, x20, x21); |
| |
| __ Udiv(w22, w19, w17); |
| __ Sdiv(w23, w19, w17); |
| __ Udiv(x24, x20, x30); |
| __ Sdiv(x25, x20, x30); |
| |
| __ Udiv(x26, x16, x21); |
| __ Sdiv(x27, x16, x21); |
| __ Udiv(x28, x30, x21); |
| __ Sdiv(x29, x30, x21); |
| |
| __ Mov(x17, 0); |
| __ Udiv(w30, w16, w17); |
| __ Sdiv(w19, w16, w17); |
| __ Udiv(x20, x16, x17); |
| __ Sdiv(x21, x16, x17); |
| END(); |
| |
| RUN(); |
| |
| CHECK_EQUAL_64(1, x0); |
| CHECK_EQUAL_64(0xFFFFFFFF, x1); |
| CHECK_EQUAL_64(1, x2); |
| CHECK_EQUAL_64(0xFFFFFFFF, x3); |
| CHECK_EQUAL_64(1, x4); |
| CHECK_EQUAL_64(1, x5); |
| CHECK_EQUAL_64(0, x6); |
| CHECK_EQUAL_64(1, x7); |
| CHECK_EQUAL_64(0, x8); |
| CHECK_EQUAL_64(0xFFFFFFFF00000001UL, x9); |
| CHECK_EQUAL_64(0x40000000, x10); |
| CHECK_EQUAL_64(0xC0000000, x11); |
| CHECK_EQUAL_64(0x40000000, x12); |
| CHECK_EQUAL_64(0x40000000, x13); |
| CHECK_EQUAL_64(0x4000000000000000UL, x14); |
| CHECK_EQUAL_64(0xC000000000000000UL, x15); |
| CHECK_EQUAL_64(0, x22); |
| CHECK_EQUAL_64(0x80000000, x23); |
| CHECK_EQUAL_64(0, x24); |
| CHECK_EQUAL_64(0x8000000000000000UL, x25); |
| CHECK_EQUAL_64(0, x26); |
| CHECK_EQUAL_64(0, x27); |
| CHECK_EQUAL_64(0x7FFFFFFFFFFFFFFFUL, x28); |
| CHECK_EQUAL_64(0, x29); |
| CHECK_EQUAL_64(0, x30); |
| CHECK_EQUAL_64(0, x19); |
| CHECK_EQUAL_64(0, x20); |
| CHECK_EQUAL_64(0, x21); |
| } |
| |
| TEST(rbit_rev) { |
| INIT_V8(); |
| SETUP(); |
| |
| START(); |
| __ Mov(x24, 0xFEDCBA9876543210UL); |
| __ Rbit(w0, w24); |
| __ Rbit(x1, x24); |
| __ Rev16(w2, w24); |
| __ Rev16(x3, x24); |
| __ Rev(w4, w24); |
| __ Rev32(x5, x24); |
| __ Rev(x6, x24); |
| END(); |
| |
| RUN(); |
| |
| CHECK_EQUAL_64(0x084C2A6E, x0); |
| CHECK_EQUAL_64(0x084C2A6E195D3B7FUL, x1); |
| CHECK_EQUAL_64(0x54761032, x2); |
| CHECK_EQUAL_64(0xDCFE98BA54761032UL, x3); |
| CHECK_EQUAL_64(0x10325476, x4); |
| CHECK_EQUAL_64(0x98BADCFE10325476UL, x5); |
| CHECK_EQUAL_64(0x1032547698BADCFEUL, x6); |
| } |
| |
| TEST(clz_cls) { |
| INIT_V8(); |
| SETUP(); |
| |
| START(); |
| __ Mov(x24, 0x0008000000800000UL); |
| __ Mov(x25, 0xFF800000FFF80000UL); |
| __ Mov(x26, 0); |
| __ Clz(w0, w24); |
| __ Clz(x1, x24); |
| __ Clz(w2, w25); |
| __ Clz(x3, x25); |
| __ Clz(w4, w26); |
| __ Clz(x5, x26); |
| __ Cls(w6, w24); |
| __ Cls(x7, x24); |
| __ Cls(w8, w25); |
| __ Cls(x9, x25); |
| __ Cls(w10, w26); |
| __ Cls(x11, x26); |
| END(); |
| |
| RUN(); |
| |
| CHECK_EQUAL_64(8, x0); |
| CHECK_EQUAL_64(12, x1); |
| CHECK_EQUAL_64(0, x2); |
| CHECK_EQUAL_64(0, x3); |
| CHECK_EQUAL_64(32, x4); |
| CHECK_EQUAL_64(64, x5); |
| CHECK_EQUAL_64(7, x6); |
| CHECK_EQUAL_64(11, x7); |
| CHECK_EQUAL_64(12, x8); |
| CHECK_EQUAL_64(8, x9); |
| CHECK_EQUAL_64(31, x10); |
| CHECK_EQUAL_64(63, x11); |
| } |
| |
| TEST(label) { |
| INIT_V8(); |
| SETUP(); |
| |
| Label label_1, label_2, label_3, label_4; |
| |
| START(); |
| __ Mov(x0, 0x1); |
| __ Mov(x1, 0x0); |
| __ Mov(x22, lr); // Save lr. |
| |
| __ B(&label_1); |
| __ B(&label_1); |
| __ B(&label_1); // Multiple branches to the same label. |
| __ Mov(x0, 0x0); |
| __ Bind(&label_2); |
| __ B(&label_3); // Forward branch. |
| __ Mov(x0, 0x0); |
| __ Bind(&label_1); |
| __ B(&label_2); // Backward branch. |
| __ Mov(x0, 0x0); |
| __ Bind(&label_3); |
| __ Bl(&label_4); |
| END(); |
| |
| __ Bind(&label_4); |
| __ Mov(x1, 0x1); |
| __ Mov(lr, x22); |
| END(); |
| |
| RUN(); |
| |
| CHECK_EQUAL_64(0x1, x0); |
| CHECK_EQUAL_64(0x1, x1); |
| } |
| |
| TEST(branch_at_start) { |
| INIT_V8(); |
| SETUP(); |
| |
| Label good, exit; |
| |
| // Test that branches can exist at the start of the buffer. (This is a |
| // boundary condition in the label-handling code.) To achieve this, we have |
| // to work around the code generated by START. |
| RESET(); |
| __ B(&good); |
| |
| START_AFTER_RESET(); |
| __ Mov(x0, 0x0); |
| END(); |
| |
| __ Bind(&exit); |
| START_AFTER_RESET(); |
| __ Mov(x0, 0x1); |
| END(); |
| |
| __ Bind(&good); |
| __ B(&exit); |
| END(); |
| |
| RUN(); |
| |
| CHECK_EQUAL_64(0x1, x0); |
| } |
| |
| TEST(adr) { |
| INIT_V8(); |
| SETUP(); |
| |
| Label label_1, label_2, label_3, label_4; |
| |
| START(); |
| __ Mov(x0, 0x0); // Set to non-zero to indicate failure. |
| __ Adr(x1, &label_3); // Set to zero to indicate success. |
| |
| __ Adr(x2, &label_1); // Multiple forward references to the same label. |
| __ Adr(x3, &label_1); |
| __ Adr(x4, &label_1); |
| |
| __ Bind(&label_2, BranchTargetIdentifier::kBtiJump); |
| __ Eor(x5, x2, Operand(x3)); // Ensure that x2,x3 and x4 are identical. |
| __ Eor(x6, x2, Operand(x4)); |
| __ Orr(x0, x0, Operand(x5)); |
| __ Orr(x0, x0, Operand(x6)); |
| __ Br(x2); // label_1, label_3 |
| |
| __ Bind(&label_3, BranchTargetIdentifier::kBtiJump); |
| __ Adr(x2, &label_3); // Self-reference (offset 0). |
| __ Eor(x1, x1, Operand(x2)); |
| __ Adr(x2, &label_4); // Simple forward reference. |
| __ Br(x2); // label_4 |
| |
| __ Bind(&label_1, BranchTargetIdentifier::kBtiJump); |
| __ Adr(x2, &label_3); // Multiple reverse references to the same label. |
| __ Adr(x3, &label_3); |
| __ Adr(x4, &label_3); |
| __ Adr(x5, &label_2); // Simple reverse reference. |
| __ Br(x5); // label_2 |
| |
| __ Bind(&label_4, BranchTargetIdentifier::kBtiJump); |
| END(); |
| |
| RUN(); |
| |
| CHECK_EQUAL_64(0x0, x0); |
| CHECK_EQUAL_64(0x0, x1); |
| } |
| |
| TEST(adr_far) { |
| INIT_V8(); |
| |
| int max_range = 1 << (Instruction::ImmPCRelRangeBitwidth - 1); |
| SETUP_SIZE(max_range + 1000 * kInstrSize); |
| |
| Label done, fail; |
| Label test_near, near_forward, near_backward; |
| Label test_far, far_forward, far_backward; |
| |
| START(); |
| __ Mov(x0, 0x0); |
| |
| __ Bind(&test_near); |
| __ Adr(x10, &near_forward, MacroAssembler::kAdrFar); |
| __ Br(x10); |
| __ B(&fail); |
| __ Bind(&near_backward, BranchTargetIdentifier::kBtiJump); |
| __ Orr(x0, x0, 1 << 1); |
| __ B(&test_far); |
| |
| __ Bind(&near_forward, BranchTargetIdentifier::kBtiJump); |
| __ Orr(x0, x0, 1 << 0); |
| __ Adr(x10, &near_backward, MacroAssembler::kAdrFar); |
| __ Br(x10); |
| |
| __ Bind(&test_far); |
| __ Adr(x10, &far_forward, MacroAssembler::kAdrFar); |
| __ Br(x10); |
| __ B(&fail); |
| __ Bind(&far_backward, BranchTargetIdentifier::kBtiJump); |
| __ Orr(x0, x0, 1 << 3); |
| __ B(&done); |
| |
| for (int i = 0; i < max_range / kInstrSize + 1; ++i) { |
| if (i % 100 == 0) { |
| // If we do land in this code, we do not want to execute so many nops |
| // before reaching the end of test (especially if tracing is activated). |
| __ b(&fail); |
| } else { |
| __ nop(); |
| } |
| } |
| |
| __ Bind(&far_forward, BranchTargetIdentifier::kBtiJump); |
| __ Orr(x0, x0, 1 << 2); |
| __ Adr(x10, &far_backward, MacroAssembler::kAdrFar); |
| __ Br(x10); |
| |
| __ B(&done); |
| __ Bind(&fail); |
| __ Orr(x0, x0, 1 << 4); |
| __ Bind(&done); |
| |
| END(); |
| |
| RUN(); |
| |
| CHECK_EQUAL_64(0xF, x0); |
| } |
| |
| TEST(branch_cond) { |
| INIT_V8(); |
| SETUP(); |
| |
| Label wrong; |
| |
| START(); |
| __ Mov(x0, 0x1); |
| __ Mov(x1, 0x1); |
| __ Mov(x2, 0x8000000000000000L); |
| |
| // For each 'cmp' instruction below, condition codes other than the ones |
| // following it would branch. |
| |
| __ Cmp(x1, 0); |
| __ B(&wrong, eq); |
| __ B(&wrong, lo); |
| __ B(&wrong, mi); |
| __ B(&wrong, vs); |
| __ B(&wrong, ls); |
| __ B(&wrong, lt); |
| __ B(&wrong, le); |
| Label ok_1; |
| __ B(&ok_1, ne); |
| __ Mov(x0, 0x0); |
| __ Bind(&ok_1); |
| |
| __ Cmp(x1, 1); |
| __ B(&wrong, ne); |
| __ B(&wrong, lo); |
| __ B(&wrong, mi); |
| __ B(&wrong, vs); |
| __ B(&wrong, hi); |
| __ B(&wrong, lt); |
| __ B(&wrong, gt); |
| Label ok_2; |
| __ B(&ok_2, pl); |
| __ Mov(x0, 0x0); |
| __ Bind(&ok_2); |
| |
| __ Cmp(x1, 2); |
| __ B(&wrong, eq); |
| __ B(&wrong, hs); |
| __ B(&wrong, pl); |
| __ B(&wrong, vs); |
| __ B(&wrong, hi); |
| __ B(&wrong, ge); |
| __ B(&wrong, gt); |
| Label ok_3; |
| __ B(&ok_3, vc); |
| __ Mov(x0, 0x0); |
| __ Bind(&ok_3); |
| |
| __ Cmp(x2, 1); |
| __ B(&wrong, eq); |
| __ B(&wrong, lo); |
| __ B(&wrong, mi); |
| __ B(&wrong, vc); |
| __ B(&wrong, ls); |
| __ B(&wrong, ge); |
| __ B(&wrong, gt); |
| Label ok_4; |
| __ B(&ok_4, le); |
| __ Mov(x0, 0x0); |
| __ Bind(&ok_4); |
| |
| Label ok_5; |
| __ b(&ok_5, al); |
| __ Mov(x0, 0x0); |
| __ Bind(&ok_5); |
| |
| Label ok_6; |
| __ b(&ok_6, nv); |
| __ Mov(x0, 0x0); |
| __ Bind(&ok_6); |
| |
| END(); |
| |
| __ Bind(&wrong); |
| __ Mov(x0, 0x0); |
| END(); |
| |
| RUN(); |
| |
| CHECK_EQUAL_64(0x1, x0); |
| } |
| |
| TEST(branch_to_reg) { |
| INIT_V8(); |
| SETUP(); |
| |
| // Test br. |
| Label fn1, after_fn1, after_bl1; |
| |
| START(); |
| __ Mov(x29, lr); |
| |
| __ Mov(x1, 0); |
| __ B(&after_fn1); |
| |
| __ Bind(&fn1); |
| __ Mov(x0, lr); |
| __ Mov(x1, 42); |
| __ Br(x0); |
| |
| __ Bind(&after_fn1); |
| __ Bl(&fn1); |
| __ Bind(&after_bl1, BranchTargetIdentifier::kBtiJump); // For Br(x0) in fn1. |
| |
| // Test blr. |
| Label fn2, after_fn2, after_bl2; |
| |
| __ Mov(x2, 0); |
| __ B(&after_fn2); |
| |
| __ Bind(&fn2); |
| __ Mov(x0, lr); |
| __ Mov(x2, 84); |
| __ Blr(x0); |
| |
| __ Bind(&after_fn2); |
| __ Bl(&fn2); |
| __ Bind(&after_bl2, BranchTargetIdentifier::kBtiCall); // For Blr(x0) in fn2. |
| __ Mov(x3, lr); |
| |
| __ Mov(lr, x29); |
| END(); |
| |
| RUN(); |
| |
| CHECK_EQUAL_64(core.xreg(3) + kInstrSize, x0); |
| CHECK_EQUAL_64(42, x1); |
| CHECK_EQUAL_64(84, x2); |
| } |
| |
| static void BtiHelper(Register ipreg) { |
| SETUP(); |
| |
| Label jump_target, jump_call_target, call_target, test_pacibsp, |
| pacibsp_target, done; |
| START(); |
| UseScratchRegisterScope temps(&masm); |
| temps.Exclude(ipreg); |
| |
| __ Adr(x0, &jump_target); |
| __ Br(x0); |
| __ Nop(); |
| |
| __ Bind(&jump_target, BranchTargetIdentifier::kBtiJump); |
| __ Adr(x0, &call_target); |
| __ Blr(x0); |
| |
| __ Adr(ipreg, &jump_call_target); |
| __ Blr(ipreg); |
| __ Adr(lr, &test_pacibsp); // Make Ret return to test_pacibsp. |
| __ Br(ipreg); |
| |
| __ Bind(&test_pacibsp, BranchTargetIdentifier::kNone); |
| __ Adr(ipreg, &pacibsp_target); |
| __ Blr(ipreg); |
| __ Adr(lr, &done); // Make Ret return to done label. |
| __ Br(ipreg); |
| |
| __ Bind(&call_target, BranchTargetIdentifier::kBtiCall); |
| __ Ret(); |
| |
| __ Bind(&jump_call_target, BranchTargetIdentifier::kBtiJumpCall); |
| __ Ret(); |
| |
| __ Bind(&pacibsp_target, BranchTargetIdentifier::kPacibsp); |
| __ Autibsp(); |
| __ Ret(); |
| |
| __ Bind(&done); |
| END(); |
| |
| #ifdef USE_SIMULATOR |
| simulator.SetGuardedPages(true); |
| RUN(); |
| #endif // USE_SIMULATOR |
| } |
| |
| TEST(bti) { |
| BtiHelper(x16); |
| BtiHelper(x17); |
| } |
| |
| TEST(unguarded_bti_is_nop) { |
| SETUP(); |
| |
| Label start, none, c, j, jc; |
| START(); |
| __ B(&start); |
| __ Bind(&none, BranchTargetIdentifier::kBti); |
| __ Bind(&c, BranchTargetIdentifier::kBtiCall); |
| __ Bind(&j, BranchTargetIdentifier::kBtiJump); |
| __ Bind(&jc, BranchTargetIdentifier::kBtiJumpCall); |
| CHECK(__ SizeOfCodeGeneratedSince(&none) == 4 * kInstrSize); |
| __ Ret(); |
| |
| Label jump_to_c, call_to_j; |
| __ Bind(&start); |
| __ Adr(x0, &none); |
| __ Adr(lr, &jump_to_c); |
| __ Br(x0); |
| |
| __ Bind(&jump_to_c); |
| __ Adr(x0, &c); |
| __ Adr(lr, &call_to_j); |
| __ Br(x0); |
| |
| __ Bind(&call_to_j); |
| __ Adr(x0, &j); |
| __ Blr(x0); |
| END(); |
| |
| #ifdef USE_SIMULATOR |
| simulator.SetGuardedPages(false); |
| RUN(); |
| #endif // USE_SIMULATOR |
| } |
| |
| TEST(compare_branch) { |
| INIT_V8(); |
| SETUP(); |
| |
| START(); |
| __ Mov(x0, 0); |
| __ Mov(x1, 0); |
| __ Mov(x2, 0); |
| __ Mov(x3, 0); |
| __ Mov(x4, 0); |
| __ Mov(x5, 0); |
| __ Mov(x16, 0); |
| __ Mov(x17, 42); |
| |
| Label zt, zt_end; |
| __ Cbz(w16, &zt); |
| __ B(&zt_end); |
| __ Bind(&zt); |
| __ Mov(x0, 1); |
| __ Bind(&zt_end); |
| |
| Label zf, zf_end; |
| __ Cbz(x17, &zf); |
| __ B(&zf_end); |
| __ Bind(&zf); |
| __ Mov(x1, 1); |
| __ Bind(&zf_end); |
| |
| Label nzt, nzt_end; |
| __ Cbnz(w17, &nzt); |
| __ B(&nzt_end); |
| __ Bind(&nzt); |
| __ Mov(x2, 1); |
| __ Bind(&nzt_end); |
| |
| Label nzf, nzf_end; |
| __ Cbnz(x16, &nzf); |
| __ B(&nzf_end); |
| __ Bind(&nzf); |
| __ Mov(x3, 1); |
| __ Bind(&nzf_end); |
| |
| __ Mov(x19, 0xFFFFFFFF00000000UL); |
| |
| Label a, a_end; |
| __ Cbz(w19, &a); |
| __ B(&a_end); |
| __ Bind(&a); |
| __ Mov(x4, 1); |
| __ Bind(&a_end); |
| |
| Label b, b_end; |
| __ Cbnz(w19, &b); |
| __ B(&b_end); |
| __ Bind(&b); |
| __ Mov(x5, 1); |
| __ Bind(&b_end); |
| |
| END(); |
| |
| RUN(); |
| |
| CHECK_EQUAL_64(1, x0); |
| CHECK_EQUAL_64(0, x1); |
| CHECK_EQUAL_64(1, x2); |
| CHECK_EQUAL_64(0, x3); |
| CHECK_EQUAL_64(1, x4); |
| CHECK_EQUAL_64(0, x5); |
| } |
| |
| TEST(test_branch) { |
| INIT_V8(); |
| SETUP(); |
| |
| START(); |
| __ Mov(x0, 0); |
| __ Mov(x1, 0); |
| __ Mov(x2, 0); |
| __ Mov(x3, 0); |
| __ Mov(x16, 0xAAAAAAAAAAAAAAAAUL); |
| |
| Label bz, bz_end; |
| __ Tbz(w16, 0, &bz); |
| __ B(&bz_end); |
| __ Bind(&bz); |
| __ Mov(x0, 1); |
| __ Bind(&bz_end); |
| |
| Label bo, bo_end; |
| __ Tbz(x16, 63, &bo); |
| __ B(&bo_end); |
| __ Bind(&bo); |
| __ Mov(x1, 1); |
| __ Bind(&bo_end); |
| |
| Label nbz, nbz_end; |
| __ Tbnz(x16, 61, &nbz); |
| __ B(&nbz_end); |
| __ Bind(&nbz); |
| __ Mov(x2, 1); |
| __ Bind(&nbz_end); |
| |
| Label nbo, nbo_end; |
| __ Tbnz(w16, 2, &nbo); |
| __ B(&nbo_end); |
| __ Bind(&nbo); |
| __ Mov(x3, 1); |
| __ Bind(&nbo_end); |
| END(); |
| |
| RUN(); |
| |
| CHECK_EQUAL_64(1, x0); |
| CHECK_EQUAL_64(0, x1); |
| CHECK_EQUAL_64(1, x2); |
| CHECK_EQUAL_64(0, x3); |
| } |
| |
| namespace { |
| // Generate a block of code that, when hit, always jumps to `landing_pad`. |
| void GenerateLandingNops(MacroAssembler* masm, int n, Label* landing_pad) { |
| for (int i = 0; i < (n - 1); i++) { |
| if (i % 100 == 0) { |
| masm->B(landing_pad); |
| } else { |
| masm->Nop(); |
| } |
| } |
| masm->B(landing_pad); |
| } |
| } // namespace |
| |
| TEST(far_branch_backward) { |
| INIT_V8(); |
| |
| ImmBranchType branch_types[] = {TestBranchType, CompareBranchType, |
| CondBranchType}; |
| |
| for (ImmBranchType type : branch_types) { |
| int range = Instruction::ImmBranchRange(type); |
| |
| SETUP_SIZE(range + 1000 * kInstrSize); |
| |
| START(); |
| |
| Label done, fail; |
| // Avoid using near and far as variable name because both are defined as |
| // macro in minwindef.h from Windows SDK. |
| Label near_label, far_label, in_range, out_of_range; |
| |
| __ Mov(x0, 0); |
| __ Mov(x1, 1); |
| __ Mov(x10, 0); |
| |
| __ B(&near_label); |
| __ Bind(&in_range); |
| __ Orr(x0, x0, 1 << 0); |
| |
| __ B(&far_label); |
| __ Bind(&out_of_range); |
| __ Orr(x0, x0, 1 << 1); |
| |
| __ B(&done); |
| |
| // We use a slack and an approximate budget instead of checking precisely |
| // when the branch limit is hit, since veneers and literal pool can mess |
| // with our calculation of where the limit is. |
| // In this test, we want to make sure we support backwards branches and the |
| // range is more-or-less correct. It's not a big deal if the macro-assembler |
| // got the range a little wrong, as long as it's not far off which could |
| // affect performance. |
| |
| int budget = |
| (range - static_cast<int>(__ SizeOfCodeGeneratedSince(&in_range))) / |
| kInstrSize; |
| |
| const int kSlack = 100; |
| |
| // Generate enough code so that the next branch will be in range but we are |
| // close to the limit. |
| GenerateLandingNops(&masm, budget - kSlack, &fail); |
| |
| __ Bind(&near_label); |
| switch (type) { |
| case TestBranchType: |
| __ Tbz(x10, 3, &in_range); |
| // This should be: |
| // TBZ <in_range> |
| CHECK_EQ(1 * kInstrSize, __ SizeOfCodeGeneratedSince(&near_label)); |
| break; |
| case CompareBranchType: |
| __ Cbz(x10, &in_range); |
| // This should be: |
| // CBZ <in_range> |
| CHECK_EQ(1 * kInstrSize, __ SizeOfCodeGeneratedSince(&near_label)); |
| break; |
| case CondBranchType: |
| __ Cmp(x10, 0); |
| __ B(eq, &in_range); |
| // This should be: |
| // CMP |
| // B.EQ <in_range> |
| CHECK_EQ(2 * kInstrSize, __ SizeOfCodeGeneratedSince(&near_label)); |
| break; |
| default: |
| UNREACHABLE(); |
| } |
| |
| // Now go past the limit so that branches are now out of range. |
| GenerateLandingNops(&masm, kSlack * 2, &fail); |
| |
| __ Bind(&far_label); |
| switch (type) { |
| case TestBranchType: |
| __ Tbz(x10, 5, &out_of_range); |
| // This should be: |
| // TBNZ <skip> |
| // B <out_of_range> |
| // skip: |
| CHECK_EQ(2 * kInstrSize, __ SizeOfCodeGeneratedSince(&far_label)); |
| break; |
| case CompareBranchType: |
| __ Cbz(x10, &out_of_range); |
| // This should be: |
| // CBNZ <skip> |
| // B <out_of_range> |
| // skip: |
| CHECK_EQ(2 * kInstrSize, __ SizeOfCodeGeneratedSince(&far_label)); |
| break; |
| case CondBranchType: |
| __ Cmp(x10, 0); |
| __ B(eq, &out_of_range); |
| // This should be: |
| // CMP |
| // B.NE <skip> |
| // B <out_of_range> |
| // skip: |
| CHECK_EQ(3 * kInstrSize, __ SizeOfCodeGeneratedSince(&far_label)); |
| break; |
| default: |
| UNREACHABLE(); |
| } |
| |
| __ Bind(&fail); |
| __ Mov(x1, 0); |
| __ Bind(&done); |
| |
| END(); |
| |
| RUN(); |
| |
| CHECK_EQUAL_64(0x3, x0); |
| CHECK_EQUAL_64(1, x1); |
| } |
| } |
| |
| TEST(far_branch_simple_veneer) { |
| INIT_V8(); |
| |
| // Test that the MacroAssembler correctly emits veneers for forward branches |
| // to labels that are outside the immediate range of branch instructions. |
| int max_range = |
| std::max(Instruction::ImmBranchRange(TestBranchType), |
| std::max(Instruction::ImmBranchRange(CompareBranchType), |
| Instruction::ImmBranchRange(CondBranchType))); |
| |
| SETUP_SIZE(max_range + 1000 * kInstrSize); |
| |
| START(); |
| |
| Label done, fail; |
| Label test_tbz, test_cbz, test_bcond; |
| Label success_tbz, success_cbz, success_bcond; |
| |
| __ Mov(x0, 0); |
| __ Mov(x1, 1); |
| __ Mov(x10, 0); |
| |
| __ Bind(&test_tbz); |
| __ Tbz(x10, 7, &success_tbz); |
| __ Bind(&test_cbz); |
| __ Cbz(x10, &success_cbz); |
| __ Bind(&test_bcond); |
| __ Cmp(x10, 0); |
| __ B(eq, &success_bcond); |
| |
| // Generate enough code to overflow the immediate range of the three types of |
| // branches below. |
| for (int i = 0; i < max_range / kInstrSize + 1; ++i) { |
| if (i % 100 == 0) { |
| // If we do land in this code, we do not want to execute so many nops |
| // before reaching the end of test (especially if tracing is activated). |
| // Also, the branches give the MacroAssembler the opportunity to emit the |
| // veneers. |
| __ B(&fail); |
| } else { |
| __ Nop(); |
| } |
| } |
| __ B(&fail); |
| |
| __ Bind(&success_tbz); |
| __ Orr(x0, x0, 1 << 0); |
| __ B(&test_cbz); |
| __ Bind(&success_cbz); |
| __ Orr(x0, x0, 1 << 1); |
| __ B(&test_bcond); |
| __ Bind(&success_bcond); |
| __ Orr(x0, x0, 1 << 2); |
| |
| __ B(&done); |
| __ Bind(&fail); |
| __ Mov(x1, 0); |
| __ Bind(&done); |
| |
| END(); |
| |
| RUN(); |
| |
| CHECK_EQUAL_64(0x7, x0); |
| CHECK_EQUAL_64(0x1, x1); |
| } |
| |
| TEST(far_branch_veneer_link_chain) { |
| INIT_V8(); |
| |
| // Test that the MacroAssembler correctly emits veneers for forward branches |
| // that target out-of-range labels and are part of multiple instructions |
| // jumping to that label. |
| // |
| // We test the three situations with the different types of instruction: |
| // (1)- When the branch is at the start of the chain with tbz. |
| // (2)- When the branch is in the middle of the chain with cbz. |
| // (3)- When the branch is at the end of the chain with bcond. |
| int max_range = |
| std::max(Instruction::ImmBranchRange(TestBranchType), |
| std::max(Instruction::ImmBranchRange(CompareBranchType), |
| Instruction::ImmBranchRange(CondBranchType))); |
| |
| SETUP_SIZE(max_range + 1000 * kInstrSize); |
| |
| START(); |
| |
| Label skip, fail, done; |
| Label test_tbz, test_cbz, test_bcond; |
| Label success_tbz, success_cbz, success_bcond; |
| |
| __ Mov(x0, 0); |
| __ Mov(x1, 1); |
| __ Mov(x10, 0); |
| |
| __ B(&skip); |
| // Branches at the start of the chain for situations (2) and (3). |
| __ B(&success_cbz); |
| __ B(&success_bcond); |
| __ Nop(); |
| __ B(&success_bcond); |
| __ B(&success_cbz); |
| __ Bind(&skip); |
| |
| __ Bind(&test_tbz); |
| __ Tbz(x10, 7, &success_tbz); |
| __ Bind(&test_cbz); |
| __ Cbz(x10, &success_cbz); |
| __ Bind(&test_bcond); |
| __ Cmp(x10, 0); |
| __ B(eq, &success_bcond); |
| |
| skip.Unuse(); |
| __ B(&skip); |
| // Branches at the end of the chain for situations (1) and (2). |
| __ B(&success_cbz); |
| __ B(&success_tbz); |
| __ Nop(); |
| __ B(&success_tbz); |
| __ B(&success_cbz); |
| __ Bind(&skip); |
| |
| // Generate enough code to overflow the immediate range of the three types of |
| // branches below. |
| GenerateLandingNops(&masm, (max_range / kInstrSize) + 1, &fail); |
| |
| __ Bind(&success_tbz); |
| __ Orr(x0, x0, 1 << 0); |
| __ B(&test_cbz); |
| __ Bind(&success_cbz); |
| __ Orr(x0, x0, 1 << 1); |
| __ B(&test_bcond); |
| __ Bind(&success_bcond); |
| __ Orr(x0, x0, 1 << 2); |
| |
| __ B(&done); |
| __ Bind(&fail); |
| __ Mov(x1, 0); |
| __ Bind(&done); |
| |
| END(); |
| |
| RUN(); |
| |
| CHECK_EQUAL_64(0x7, x0); |
| CHECK_EQUAL_64(0x1, x1); |
| } |
| |
| TEST(far_branch_veneer_broken_link_chain) { |
| INIT_V8(); |
| |
| // Check that the MacroAssembler correctly handles the situation when removing |
| // a branch from the link chain of a label and the two links on each side of |
| // the removed branch cannot be linked together (out of range). |
| // |
| // We want to generate the following code, we test with tbz because it has a |
| // small range: |
| // |
| // ~~~ |
| // 1: B <far> |
| // : |
| // : |
| // : |
| // 2: TBZ <far> -------. |
| // : | |
| // : | out of range |
| // : | |
| // 3: TBZ <far> | |
| // | | |
| // | in range | |
| // V | |
| // far: <-' |
| // ~~~ |
| // |
| // If we say that the range of TBZ is 3 lines on this graph, then we can get |
| // into a situation where the link chain gets broken. When emitting the two |
| // TBZ instructions, we are in range of the previous branch in the chain so |
| // we'll generate a TBZ and not a TBNZ+B sequence that can encode a bigger |
| // range. |
| // |
| // However, the first TBZ (2), is out of range of the far label so a veneer |
| // will be generated after the second TBZ (3). And this will result in a |
| // broken chain because we can no longer link from (3) back to (1). |
| // |
| // ~~~ |
| // 1: B <far> <-. |
| // : |
| // : out of range |
| // : |
| // 2: TBZ <veneer> : |
| // : |
| // : |
| // : |
| // 3: TBZ <far> ----' |
| // |
| // B <skip> |
| // veneer: |
| // B <far> |
| // skip: |
| // |
| // far: |
| // ~~~ |
| // |
| // This test makes sure the MacroAssembler is able to resolve this case by, |
| // for instance, resolving (1) early and making it jump to <veneer> instead of |
| // <far>. |
| |
| int max_range = Instruction::ImmBranchRange(TestBranchType); |
| int inter_range = max_range / 2 + max_range / 10; |
| |
| SETUP_SIZE(3 * inter_range + 1000 * kInstrSize); |
| |
| START(); |
| |
| Label fail, done; |
| Label test_1, test_2, test_3; |
| Label far_target; |
| |
| __ Mov(x0, 0); // Indicates the origin of the branch. |
| __ Mov(x1, 1); |
| __ Mov(x10, 0); |
| |
| // First instruction in the label chain. |
| __ Bind(&test_1); |
| __ Mov(x0, 1); |
| __ B(&far_target); |
| |
| GenerateLandingNops(&masm, inter_range / kInstrSize, &fail); |
| |
| // Will need a veneer to point to reach the target. |
| __ Bind(&test_2); |
| __ Mov(x0, 2); |
| { |
| Label tbz; |
| __ Bind(&tbz); |
| __ Tbz(x10, 7, &far_target); |
| // This should be a single TBZ since the previous link is in range at this |
| // point. |
| CHECK_EQ(1 * kInstrSize, __ SizeOfCodeGeneratedSince(&tbz)); |
| } |
| |
| GenerateLandingNops(&masm, inter_range / kInstrSize, &fail); |
| |
| // Does not need a veneer to reach the target, but the initial branch |
| // instruction is out of range. |
| __ Bind(&test_3); |
| __ Mov(x0, 3); |
| { |
| Label tbz; |
| __ Bind(&tbz); |
| __ Tbz(x10, 7, &far_target); |
| // This should be a single TBZ since the previous link is in range at this |
| // point. |
| CHECK_EQ(1 * kInstrSize, __ SizeOfCodeGeneratedSince(&tbz)); |
| } |
| |
| // A veneer will be generated for the first TBZ, which will then remove the |
| // label from the chain and break it because the second TBZ is out of range of |
| // the first branch. |
| // The MacroAssembler should be able to cope with this. |
| |
| GenerateLandingNops(&masm, inter_range / kInstrSize, &fail); |
| |
| __ B(&fail); |
| |
| __ Bind(&far_target); |
| __ Cmp(x0, 1); |
| __ B(eq, &test_2); |
| __ Cmp(x0, 2); |
| __ B(eq, &test_3); |
| |
| __ B(&done); |
| __ Bind(&fail); |
| __ Mov(x1, 0); |
| __ Bind(&done); |
| |
| END(); |
| |
| RUN(); |
| |
| CHECK_EQUAL_64(0x3, x0); |
| CHECK_EQUAL_64(0x1, x1); |
| } |
| |
| TEST(branch_type) { |
| INIT_V8(); |
| |
| SETUP(); |
| |
| Label fail, done; |
| |
| START(); |
| __ Mov(x0, 0x0); |
| __ Mov(x10, 0x7); |
| __ Mov(x11, 0x0); |
| |
| // Test non taken branches. |
| __ Cmp(x10, 0x7); |
| __ B(&fail, ne); |
| __ B(&fail, never); |
| __ B(&fail, reg_zero, x10); |
| __ B(&fail, reg_not_zero, x11); |
| __ B(&fail, reg_bit_clear, x10, 0); |
| __ B(&fail, reg_bit_set, x10, 3); |
| |
| // Test taken branches. |
| Label l1, l2, l3, l4, l5; |
| __ Cmp(x10, 0x7); |
| __ B(&l1, eq); |
| __ B(&fail); |
| __ Bind(&l1); |
| __ B(&l2, always); |
| __ B(&fail); |
| __ Bind(&l2); |
| __ B(&l3, reg_not_zero, x10); |
| __ B(&fail); |
| __ Bind(&l3); |
| __ B(&l4, reg_bit_clear, x10, 15); |
| __ B(&fail); |
| __ Bind(&l4); |
| __ B(&l5, reg_bit_set, x10, 1); |
| __ B(&fail); |
| __ Bind(&l5); |
| |
| __ B(&done); |
| |
| __ Bind(&fail); |
| __ Mov(x0, 0x1); |
| |
| __ Bind(&done); |
| |
| END(); |
| |
| RUN(); |
| |
| CHECK_EQUAL_64(0x0, x0); |
| } |
| |
| TEST(ldr_str_offset) { |
| INIT_V8(); |
| SETUP(); |
| |
| uint64_t src[2] = {0xFEDCBA9876543210UL, 0x0123456789ABCDEFUL}; |
| uint64_t dst[5] = {0, 0, 0, 0, 0}; |
| uintptr_t src_base = reinterpret_cast<uintptr_t>(src); |
| uintptr_t dst_base = reinterpret_cast<uintptr_t>(dst); |
| |
| START(); |
| __ Mov(x17, src_base); |
| __ Mov(x19, dst_base); |
| __ Ldr(w0, MemOperand(x17)); |
| __ Str(w0, MemOperand(x19)); |
| __ Ldr(w1, MemOperand(x17, 4)); |
| __ Str(w1, MemOperand(x19, 12)); |
| __ Ldr(x2, MemOperand(x17, 8)); |
| __ Str(x2, MemOperand(x19, 16)); |
| __ Ldrb(w3, MemOperand(x17, 1)); |
| __ Strb(w3, MemOperand(x19, 25)); |
| __ Ldrh(w4, MemOperand(x17, 2)); |
| __ Strh(w4, MemOperand(x19, 33)); |
| END(); |
| |
| RUN(); |
| |
| CHECK_EQUAL_64(0x76543210, x0); |
| CHECK_EQUAL_64(0x76543210, dst[0]); |
| CHECK_EQUAL_64(0xFEDCBA98, x1); |
| CHECK_EQUAL_64(0xFEDCBA9800000000UL, dst[1]); |
| CHECK_EQUAL_64(0x0123456789ABCDEFUL, x2); |
| CHECK_EQUAL_64(0x0123456789ABCDEFUL, dst[2]); |
| CHECK_EQUAL_64(0x32, x3); |
| CHECK_EQUAL_64(0x3200, dst[3]); |
| CHECK_EQUAL_64(0x7654, x4); |
| CHECK_EQUAL_64(0x765400, dst[4]); |
| CHECK_EQUAL_64(src_base, x17); |
| CHECK_EQUAL_64(dst_base, x19); |
| } |
| |
| TEST(ldr_str_wide) { |
| INIT_V8(); |
| SETUP(); |
| |
| uint32_t src[8192]; |
| uint32_t dst[8192]; |
| uintptr_t src_base = reinterpret_cast<uintptr_t>(src); |
| uintptr_t dst_base = reinterpret_cast<uintptr_t>(dst); |
| memset(src, 0xAA, 8192 * sizeof(src[0])); |
| memset(dst, 0xAA, 8192 * sizeof(dst[0])); |
| src[0] = 0; |
| src[6144] = 6144; |
| src[8191] = 8191; |
| |
| START(); |
| __ Mov(x22, src_base); |
| __ Mov(x23, dst_base); |
| __ Mov(x24, src_base); |
| __ Mov(x25, dst_base); |
| __ Mov(x26, src_base); |
| __ Mov(x27, dst_base); |
| |
| __ Ldr(w0, MemOperand(x22, 8191 * sizeof(src[0]))); |
| __ Str(w0, MemOperand(x23, 8191 * sizeof(dst[0]))); |
| __ Ldr(w1, MemOperand(x24, 4096 * sizeof(src[0]), PostIndex)); |
| __ Str(w1, MemOperand(x25, 4096 * sizeof(dst[0]), PostIndex)); |
| __ Ldr(w2, MemOperand(x26, 6144 * sizeof(src[0]), PreIndex)); |
| __ Str(w2, MemOperand(x27, 6144 * sizeof(dst[0]), PreIndex)); |
| END(); |
| |
| RUN(); |
| |
| CHECK_EQUAL_32(8191, w0); |
| CHECK_EQUAL_32(8191, dst[8191]); |
| CHECK_EQUAL_64(src_base, x22); |
| CHECK_EQUAL_64(dst_base, x23); |
| CHECK_EQUAL_32(0, w1); |
| CHECK_EQUAL_32(0, dst[0]); |
| CHECK_EQUAL_64(src_base + 4096 * sizeof(src[0]), x24); |
| CHECK_EQUAL_64(dst_base + 4096 * sizeof(dst[0]), x25); |
| CHECK_EQUAL_32(6144, w2); |
| CHECK_EQUAL_32(6144, dst[6144]); |
| CHECK_EQUAL_64(src_base + 6144 * sizeof(src[0]), x26); |
| CHECK_EQUAL_64(dst_base + 6144 * sizeof(dst[0]), x27); |
| } |
| |
| TEST(ldr_str_preindex) { |
| INIT_V8(); |
| SETUP(); |
| |
| uint64_t src[2] = {0xFEDCBA9876543210UL, 0x0123456789ABCDEFUL}; |
| uint64_t dst[6] = {0, 0, 0, 0, 0, 0}; |
| uintptr_t src_base = reinterpret_cast<uintptr_t>(src); |
| uintptr_t dst_base = reinterpret_cast<uintptr_t>(dst); |
| |
| START(); |
| __ Mov(x17, src_base); |
| __ Mov(x28, dst_base); |
| __ Mov(x19, src_base); |
| __ Mov(x20, dst_base); |
| __ Mov(x21, src_base + 16); |
| __ Mov(x22, dst_base + 40); |
| __ Mov(x23, src_base); |
| __ Mov(x24, dst_base); |
| __ Mov(x25, src_base); |
| __ Mov(x26, dst_base); |
| __ Ldr(w0, MemOperand(x17, 4, PreIndex)); |
| __ Str(w0, MemOperand(x28, 12, PreIndex)); |
| __ Ldr(x1, MemOperand(x19, 8, PreIndex)); |
| __ Str(x1, MemOperand(x20, 16, PreIndex)); |
| __ Ldr(w2, MemOperand(x21, -4, PreIndex)); |
| __ Str(w2, MemOperand(x22, -4, PreIndex)); |
| __ Ldrb(w3, MemOperand(x23, 1, PreIndex)); |
| __ Strb(w3, MemOperand(x24, 25, PreIndex)); |
| __ Ldrh(w4, MemOperand(x25, 3, PreIndex)); |
| __ Strh(w4, MemOperand(x26, 41, PreIndex)); |
| END(); |
| |
| RUN(); |
| |
| CHECK_EQUAL_64(0xFEDCBA98, x0); |
| CHECK_EQUAL_64(0xFEDCBA9800000000UL, dst[1]); |
| CHECK_EQUAL_64(0x0123456789ABCDEFUL, x1); |
| CHECK_EQUAL_64(0x0123456789ABCDEFUL, dst[2]); |
| CHECK_EQUAL_64(0x01234567, x2); |
| CHECK_EQUAL_64(0x0123456700000000UL, dst[4]); |
| CHECK_EQUAL_64(0x32, x3); |
| CHECK_EQUAL_64(0x3200, dst[3]); |
| CHECK_EQUAL_64(0x9876, x4); |
| CHECK_EQUAL_64(0x987600, dst[5]); |
| CHECK_EQUAL_64(src_base + 4, x17); |
| CHECK_EQUAL_64(dst_base + 12, x28); |
| CHECK_EQUAL_64(src_base + 8, x19); |
| CHECK_EQUAL_64(dst_base + 16, x20); |
| CHECK_EQUAL_64(src_base + 12, x21); |
| CHECK_EQUAL_64(dst_base + 36, x22); |
| CHECK_EQUAL_64(src_base + 1, x23); |
| CHECK_EQUAL_64(dst_base + 25, x24); |
| CHECK_EQUAL_64(src_base + 3, x25); |
| CHECK_EQUAL_64(dst_base + 41, x26); |
| } |
| |
| TEST(ldr_str_postindex) { |
| INIT_V8(); |
| SETUP(); |
| |
| uint64_t src[2] = {0xFEDCBA9876543210UL, 0x0123456789ABCDEFUL}; |
| uint64_t dst[6] = {0, 0, 0, 0, 0, 0}; |
| uintptr_t src_base = reinterpret_cast<uintptr_t>(src); |
| uintptr_t dst_base = reinterpret_cast<uintptr_t>(dst); |
| |
| START(); |
| __ Mov(x17, src_base + 4); |
| __ Mov(x28, dst_base + 12); |
| __ Mov(x19, src_base + 8); |
| __ Mov(x20, dst_base + 16); |
| __ Mov(x21, src_base + 8); |
| __ Mov(x22, dst_base + 32); |
| __ Mov(x23, src_base + 1); |
| __ Mov(x24, dst_base + 25); |
| __ Mov(x25, src_base + 3); |
| __ Mov(x26, dst_base + 41); |
| __ Ldr(w0, MemOperand(x17, 4, PostIndex)); |
| __ Str(w0, MemOperand(x28, 12, PostIndex)); |
| __ Ldr(x1, MemOperand(x19, 8, PostIndex)); |
| __ Str(x1, MemOperand(x20, 16, PostIndex)); |
| __ Ldr(x2, MemOperand(x21, -8, PostIndex)); |
| __ Str(x2, MemOperand(x22, -32, PostIndex)); |
| __ Ldrb(w3, MemOperand(x23, 1, PostIndex)); |
| __ Strb(w3, MemOperand(x24, 5, PostIndex)); |
| __ Ldrh(w4, MemOperand(x25, -3, PostIndex)); |
| __ Strh(w4, MemOperand(x26, -41, PostIndex)); |
| END(); |
| |
| RUN(); |
| |
| CHECK_EQUAL_64(0xFEDCBA98, x0); |
| CHECK_EQUAL_64(0xFEDCBA9800000000UL, dst[1]); |
| CHECK_EQUAL_64(0x0123456789ABCDEFUL, x1); |
| CHECK_EQUAL_64(0x0123456789ABCDEFUL, dst[2]); |
| CHECK_EQUAL_64(0x0123456789ABCDEFUL, x2); |
| CHECK_EQUAL_64(0x0123456789ABCDEFUL, dst[4]); |
| CHECK_EQUAL_64(0x32, x3); |
| CHECK_EQUAL_64(0x3200, dst[3]); |
| CHECK_EQUAL_64(0x9876, x4); |
| CHECK_EQUAL_64(0x987600, dst[5]); |
| CHECK_EQUAL_64(src_base + 8, x17); |
| CHECK_EQUAL_64(dst_base + 24, x28); |
| CHECK_EQUAL_64(src_base + 16, x19); |
| CHECK_EQUAL_64(dst_base + 32, x20); |
| CHECK_EQUAL_64(src_base, x21); |
| CHECK_EQUAL_64(dst_base, x22); |
| CHECK_EQUAL_64(src_base + 2, x23); |
| CHECK_EQUAL_64(dst_base + 30, x24); |
| CHECK_EQUAL_64(src_base, x25); |
| CHECK_EQUAL_64(dst_base, x26); |
| } |
| |
| TEST(load_signed) { |
| INIT_V8(); |
| SETUP(); |
| |
| uint32_t src[2] = {0x80008080, 0x7FFF7F7F}; |
| uintptr_t src_base = reinterpret_cast<uintptr_t>(src); |
| |
| START(); |
| __ Mov(x24, src_base); |
| __ Ldrsb(w0, MemOperand(x24)); |
| __ Ldrsb(w1, MemOperand(x24, 4)); |
| __ Ldrsh(w2, MemOperand(x24)); |
| __ Ldrsh(w3, MemOperand(x24, 4)); |
| __ Ldrsb(x4, MemOperand(x24)); |
| __ Ldrsb(x5, MemOperand(x24, 4)); |
| __ Ldrsh(x6, MemOperand(x24)); |
| __ Ldrsh(x7, MemOperand(x24, 4)); |
| __ Ldrsw(x8, MemOperand(x24)); |
| __ Ldrsw(x9, MemOperand(x24, 4)); |
| END(); |
| |
| RUN(); |
| |
| CHECK_EQUAL_64(0xFFFFFF80, x0); |
| CHECK_EQUAL_64(0x0000007F, x1); |
| CHECK_EQUAL_64(0xFFFF8080, x2); |
| CHECK_EQUAL_64(0x00007F7F, x3); |
| CHECK_EQUAL_64(0xFFFFFFFFFFFFFF80UL, x4); |
| CHECK_EQUAL_64(0x000000000000007FUL, x5); |
| CHECK_EQUAL_64(0xFFFFFFFFFFFF8080UL, x6); |
| CHECK_EQUAL_64(0x0000000000007F7FUL, x7); |
| CHECK_EQUAL_64(0xFFFFFFFF80008080UL, x8); |
| CHECK_EQUAL_64(0x000000007FFF7F7FUL, x9); |
| } |
| |
| TEST(load_store_regoffset) { |
| INIT_V8(); |
| SETUP(); |
| |
| uint32_t src[3] = {1, 2, 3}; |
| uint32_t dst[4] = {0, 0, 0, 0}; |
| uintptr_t src_base = reinterpret_cast<uintptr_t>(src); |
| uintptr_t dst_base = reinterpret_cast<uintptr_t>(dst); |
| |
| START(); |
| __ Mov(x16, src_base); |
| __ Mov(x17, dst_base); |
| __ Mov(x21, src_base + 3 * sizeof(src[0])); |
| __ Mov(x19, dst_base + 3 * sizeof(dst[0])); |
| __ Mov(x20, dst_base + 4 * sizeof(dst[0])); |
| __ Mov(x24, 0); |
| __ Mov(x25, 4); |
| __ Mov(x26, -4); |
| __ Mov(x27, 0xFFFFFFFC); // 32-bit -4. |
| __ Mov(x28, 0xFFFFFFFE); // 32-bit -2. |
| __ Mov(x29, 0xFFFFFFFF); // 32-bit -1. |
| |
| __ Ldr(w0, MemOperand(x16, x24)); |
| __ Ldr(x1, MemOperand(x16, x25)); |
| __ Ldr(w2, MemOperand(x21, x26)); |
| __ Ldr(w3, MemOperand(x21, x27, SXTW)); |
| __ Ldr(w4, MemOperand(x21, x28, SXTW, 2)); |
| __ Str(w0, MemOperand(x17, x24)); |
| __ Str(x1, MemOperand(x17, x25)); |
| __ Str(w2, MemOperand(x20, x29, SXTW, 2)); |
| END(); |
| |
| RUN(); |
| |
| CHECK_EQUAL_64(1, x0); |
| CHECK_EQUAL_64(0x0000000300000002UL, x1); |
| CHECK_EQUAL_64(3, x2); |
| CHECK_EQUAL_64(3, x3); |
| CHECK_EQUAL_64(2, x4); |
| CHECK_EQUAL_32(1, dst[0]); |
| CHECK_EQUAL_32(2, dst[1]); |
| CHECK_EQUAL_32(3, dst[2]); |
| CHECK_EQUAL_32(3, dst[3]); |
| } |
| |
| TEST(load_store_float) { |
| INIT_V8(); |
| SETUP(); |
| |
| float src[3] = {1.0, 2.0, 3.0}; |
| float dst[3] = {0.0, 0.0, 0.0}; |
| uintptr_t src_base = reinterpret_cast<uintptr_t>(src); |
| uintptr_t dst_base = reinterpret_cast<uintptr_t>(dst); |
| |
| START(); |
| __ Mov(x17, src_base); |
| __ Mov(x28, dst_base); |
| __ Mov(x19, src_base); |
| __ Mov(x20, dst_base); |
| __ Mov(x21, src_base); |
| __ Mov(x22, dst_base); |
| __ Ldr(s0, MemOperand(x17, sizeof(src[0]))); |
| __ Str(s0, MemOperand(x28, sizeof(dst[0]), PostIndex)); |
| __ Ldr(s1, MemOperand(x19, sizeof(src[0]), PostIndex)); |
| __ Str(s1, MemOperand(x20, 2 * sizeof(dst[0]), PreIndex)); |
| __ Ldr(s2, MemOperand(x21, 2 * sizeof(src[0]), PreIndex)); |
| __ Str(s2, MemOperand(x22, sizeof(dst[0]))); |
| END(); |
| |
| RUN(); |
| |
| CHECK_EQUAL_FP32(2.0, s0); |
| CHECK_EQUAL_FP32(2.0, dst[0]); |
| CHECK_EQUAL_FP32(1.0, s1); |
| CHECK_EQUAL_FP32(1.0, dst[2]); |
| CHECK_EQUAL_FP32(3.0, s2); |
| CHECK_EQUAL_FP32(3.0, dst[1]); |
| CHECK_EQUAL_64(src_base, x17); |
| CHECK_EQUAL_64(dst_base + sizeof(dst[0]), x28); |
| CHECK_EQUAL_64(src_base + sizeof(src[0]), x19); |
| CHECK_EQUAL_64(dst_base + 2 * sizeof(dst[0]), x20); |
| CHECK_EQUAL_64(src_base + 2 * sizeof(src[0]), x21); |
| CHECK_EQUAL_64(dst_base, x22); |
| } |
| |
| TEST(load_store_double) { |
| INIT_V8(); |
| SETUP(); |
| |
| double src[3] = {1.0, 2.0, 3.0}; |
| double dst[3] = {0.0, 0.0, 0.0}; |
| uintptr_t src_base = reinterpret_cast<uintptr_t>(src); |
| uintptr_t dst_base = reinterpret_cast<uintptr_t>(dst); |
| |
| START(); |
| __ Mov(x17, src_base); |
| __ Mov(x28, dst_base); |
| __ Mov(x19, src_base); |
| __ Mov(x20, dst_base); |
| __ Mov(x21, src_base); |
| __ Mov(x22, dst_base); |
| __ Ldr(d0, MemOperand(x17, sizeof(src[0]))); |
| __ Str(d0, MemOperand(x28, sizeof(dst[0]), PostIndex)); |
| __ Ldr(d1, MemOperand(x19, sizeof(src[0]), PostIndex)); |
| __ Str(d1, MemOperand(x20, 2 * sizeof(dst[0]), PreIndex)); |
| __ Ldr(d2, MemOperand(x21, 2 * sizeof(src[0]), PreIndex)); |
| __ Str(d2, MemOperand(x22, sizeof(dst[0]))); |
| END(); |
| |
| RUN(); |
| |
| CHECK_EQUAL_FP64(2.0, d0); |
| CHECK_EQUAL_FP64(2.0, dst[0]); |
| CHECK_EQUAL_FP64(1.0, d1); |
| CHECK_EQUAL_FP64(1.0, dst[2]); |
| CHECK_EQUAL_FP64(3.0, d2); |
| CHECK_EQUAL_FP64(3.0, dst[1]); |
| CHECK_EQUAL_64(src_base, x17); |
| CHECK_EQUAL_64(dst_base + sizeof(dst[0]), x28); |
| CHECK_EQUAL_64(src_base + sizeof(src[0]), x19); |
| CHECK_EQUAL_64(dst_base + 2 * sizeof(dst[0]), x20); |
| CHECK_EQUAL_64(src_base + 2 * sizeof(src[0]), x21); |
| CHECK_EQUAL_64(dst_base, x22); |
| } |
| |
| TEST(load_store_b) { |
| INIT_V8(); |
| SETUP(); |
| |
| uint8_t src[3] = {0x12, 0x23, 0x34}; |
| uint8_t dst[3] = {0, 0, 0}; |
| uintptr_t src_base = reinterpret_cast<uintptr_t>(src); |
| uintptr_t dst_base = reinterpret_cast<uintptr_t>(dst); |
| |
| START(); |
| __ Mov(x17, src_base); |
| __ Mov(x28, dst_base |