Home | History | Annotate | Download | only in arm
      1 /*
      2  * Copyright (C) 2014 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include "assembler_thumb2.h"
     18 
     19 #include "base/stl_util.h"
     20 #include "utils/assembler_test.h"
     21 
     22 namespace art {
     23 
     24 class AssemblerThumb2Test : public AssemblerTest<arm::Thumb2Assembler,
     25                                                  arm::Register, arm::SRegister,
     26                                                  uint32_t> {
     27  protected:
     28   std::string GetArchitectureString() OVERRIDE {
     29     return "arm";
     30   }
     31 
     32   std::string GetAssemblerParameters() OVERRIDE {
     33     return " -march=armv7-a -mcpu=cortex-a15 -mfpu=neon -mthumb";
     34   }
     35 
     36   const char* GetAssemblyHeader() OVERRIDE {
     37     return kThumb2AssemblyHeader;
     38   }
     39 
     40   std::string GetDisassembleParameters() OVERRIDE {
     41     return " -D -bbinary -marm --disassembler-options=force-thumb --no-show-raw-insn";
     42   }
     43 
     44   void SetUpHelpers() OVERRIDE {
     45     if (registers_.size() == 0) {
     46       registers_.insert(end(registers_),
     47                         {  // NOLINT(whitespace/braces)
     48                           new arm::Register(arm::R0),
     49                           new arm::Register(arm::R1),
     50                           new arm::Register(arm::R2),
     51                           new arm::Register(arm::R3),
     52                           new arm::Register(arm::R4),
     53                           new arm::Register(arm::R5),
     54                           new arm::Register(arm::R6),
     55                           new arm::Register(arm::R7),
     56                           new arm::Register(arm::R8),
     57                           new arm::Register(arm::R9),
     58                           new arm::Register(arm::R10),
     59                           new arm::Register(arm::R11),
     60                           new arm::Register(arm::R12),
     61                           new arm::Register(arm::R13),
     62                           new arm::Register(arm::R14),
     63                           new arm::Register(arm::R15)
     64                         });
     65     }
     66   }
     67 
     68   void TearDown() OVERRIDE {
     69     AssemblerTest::TearDown();
     70     STLDeleteElements(&registers_);
     71   }
     72 
     73   std::vector<arm::Register*> GetRegisters() OVERRIDE {
     74     return registers_;
     75   }
     76 
     77   uint32_t CreateImmediate(int64_t imm_value) OVERRIDE {
     78     return imm_value;
     79   }
     80 
     81  private:
     82   std::vector<arm::Register*> registers_;
     83 
     84   static constexpr const char* kThumb2AssemblyHeader = ".syntax unified\n.thumb\n";
     85 };
     86 
     87 
     88 TEST_F(AssemblerThumb2Test, Toolchain) {
     89   EXPECT_TRUE(CheckTools());
     90 }
     91 
     92 
     93 TEST_F(AssemblerThumb2Test, Sbfx) {
     94   GetAssembler()->sbfx(arm::R0, arm::R1, 0, 1);
     95   GetAssembler()->sbfx(arm::R0, arm::R1, 0, 8);
     96   GetAssembler()->sbfx(arm::R0, arm::R1, 0, 16);
     97   GetAssembler()->sbfx(arm::R0, arm::R1, 0, 32);
     98 
     99   GetAssembler()->sbfx(arm::R0, arm::R1, 8, 1);
    100   GetAssembler()->sbfx(arm::R0, arm::R1, 8, 8);
    101   GetAssembler()->sbfx(arm::R0, arm::R1, 8, 16);
    102   GetAssembler()->sbfx(arm::R0, arm::R1, 8, 24);
    103 
    104   GetAssembler()->sbfx(arm::R0, arm::R1, 16, 1);
    105   GetAssembler()->sbfx(arm::R0, arm::R1, 16, 8);
    106   GetAssembler()->sbfx(arm::R0, arm::R1, 16, 16);
    107 
    108   GetAssembler()->sbfx(arm::R0, arm::R1, 31, 1);
    109 
    110   const char* expected =
    111       "sbfx r0, r1, #0, #1\n"
    112       "sbfx r0, r1, #0, #8\n"
    113       "sbfx r0, r1, #0, #16\n"
    114       "sbfx r0, r1, #0, #32\n"
    115 
    116       "sbfx r0, r1, #8, #1\n"
    117       "sbfx r0, r1, #8, #8\n"
    118       "sbfx r0, r1, #8, #16\n"
    119       "sbfx r0, r1, #8, #24\n"
    120 
    121       "sbfx r0, r1, #16, #1\n"
    122       "sbfx r0, r1, #16, #8\n"
    123       "sbfx r0, r1, #16, #16\n"
    124 
    125       "sbfx r0, r1, #31, #1\n";
    126   DriverStr(expected, "sbfx");
    127 }
    128 
    129 TEST_F(AssemblerThumb2Test, Ubfx) {
    130   GetAssembler()->ubfx(arm::R0, arm::R1, 0, 1);
    131   GetAssembler()->ubfx(arm::R0, arm::R1, 0, 8);
    132   GetAssembler()->ubfx(arm::R0, arm::R1, 0, 16);
    133   GetAssembler()->ubfx(arm::R0, arm::R1, 0, 32);
    134 
    135   GetAssembler()->ubfx(arm::R0, arm::R1, 8, 1);
    136   GetAssembler()->ubfx(arm::R0, arm::R1, 8, 8);
    137   GetAssembler()->ubfx(arm::R0, arm::R1, 8, 16);
    138   GetAssembler()->ubfx(arm::R0, arm::R1, 8, 24);
    139 
    140   GetAssembler()->ubfx(arm::R0, arm::R1, 16, 1);
    141   GetAssembler()->ubfx(arm::R0, arm::R1, 16, 8);
    142   GetAssembler()->ubfx(arm::R0, arm::R1, 16, 16);
    143 
    144   GetAssembler()->ubfx(arm::R0, arm::R1, 31, 1);
    145 
    146   const char* expected =
    147       "ubfx r0, r1, #0, #1\n"
    148       "ubfx r0, r1, #0, #8\n"
    149       "ubfx r0, r1, #0, #16\n"
    150       "ubfx r0, r1, #0, #32\n"
    151 
    152       "ubfx r0, r1, #8, #1\n"
    153       "ubfx r0, r1, #8, #8\n"
    154       "ubfx r0, r1, #8, #16\n"
    155       "ubfx r0, r1, #8, #24\n"
    156 
    157       "ubfx r0, r1, #16, #1\n"
    158       "ubfx r0, r1, #16, #8\n"
    159       "ubfx r0, r1, #16, #16\n"
    160 
    161       "ubfx r0, r1, #31, #1\n";
    162   DriverStr(expected, "ubfx");
    163 }
    164 
    165 TEST_F(AssemblerThumb2Test, Vmstat) {
    166   GetAssembler()->vmstat();
    167 
    168   const char* expected = "vmrs APSR_nzcv, FPSCR\n";
    169 
    170   DriverStr(expected, "vmrs");
    171 }
    172 
    173 TEST_F(AssemblerThumb2Test, ldrexd) {
    174   GetAssembler()->ldrexd(arm::R0, arm::R1, arm::R0);
    175   GetAssembler()->ldrexd(arm::R0, arm::R1, arm::R1);
    176   GetAssembler()->ldrexd(arm::R0, arm::R1, arm::R2);
    177   GetAssembler()->ldrexd(arm::R5, arm::R3, arm::R7);
    178 
    179   const char* expected =
    180       "ldrexd r0, r1, [r0]\n"
    181       "ldrexd r0, r1, [r1]\n"
    182       "ldrexd r0, r1, [r2]\n"
    183       "ldrexd r5, r3, [r7]\n";
    184   DriverStr(expected, "ldrexd");
    185 }
    186 
    187 TEST_F(AssemblerThumb2Test, strexd) {
    188   GetAssembler()->strexd(arm::R9, arm::R0, arm::R1, arm::R0);
    189   GetAssembler()->strexd(arm::R9, arm::R0, arm::R1, arm::R1);
    190   GetAssembler()->strexd(arm::R9, arm::R0, arm::R1, arm::R2);
    191   GetAssembler()->strexd(arm::R9, arm::R5, arm::R3, arm::R7);
    192 
    193   const char* expected =
    194       "strexd r9, r0, r1, [r0]\n"
    195       "strexd r9, r0, r1, [r1]\n"
    196       "strexd r9, r0, r1, [r2]\n"
    197       "strexd r9, r5, r3, [r7]\n";
    198   DriverStr(expected, "strexd");
    199 }
    200 
    201 TEST_F(AssemblerThumb2Test, LdrdStrd) {
    202   GetAssembler()->ldrd(arm::R0, arm::Address(arm::R2, 8));
    203   GetAssembler()->ldrd(arm::R0, arm::Address(arm::R12));
    204   GetAssembler()->strd(arm::R0, arm::Address(arm::R2, 8));
    205 
    206   const char* expected =
    207       "ldrd r0, r1, [r2, #8]\n"
    208       "ldrd r0, r1, [r12]\n"
    209       "strd r0, r1, [r2, #8]\n";
    210   DriverStr(expected, "ldrdstrd");
    211 }
    212 
    213 TEST_F(AssemblerThumb2Test, eor) {
    214 #define __ GetAssembler()->
    215   __ eor(arm::R1, arm::R1, arm::ShifterOperand(arm::R0));
    216   __ eor(arm::R1, arm::R0, arm::ShifterOperand(arm::R1));
    217   __ eor(arm::R1, arm::R8, arm::ShifterOperand(arm::R0));
    218   __ eor(arm::R8, arm::R1, arm::ShifterOperand(arm::R0));
    219   __ eor(arm::R1, arm::R0, arm::ShifterOperand(arm::R8));
    220 
    221   const char* expected =
    222       "eors r1, r0\n"
    223       "eor r1, r0, r1\n"
    224       "eor r1, r8, r0\n"
    225       "eor r8, r1, r0\n"
    226       "eor r1, r0, r8\n";
    227   DriverStr(expected, "abs");
    228 }
    229 
    230 TEST_F(AssemblerThumb2Test, sub) {
    231   __ subs(arm::R1, arm::R0, arm::ShifterOperand(42));
    232   __ sub(arm::R1, arm::R0, arm::ShifterOperand(42));
    233 
    234   const char* expected =
    235       "subs r1, r0, #42\n"
    236       "subw r1, r0, #42\n";
    237   DriverStr(expected, "sub");
    238 }
    239 
    240 TEST_F(AssemblerThumb2Test, add) {
    241   __ adds(arm::R1, arm::R0, arm::ShifterOperand(42));
    242   __ add(arm::R1, arm::R0, arm::ShifterOperand(42));
    243 
    244   const char* expected =
    245       "adds r1, r0, #42\n"
    246       "addw r1, r0, #42\n";
    247   DriverStr(expected, "add");
    248 }
    249 
    250 TEST_F(AssemblerThumb2Test, StoreWordToThumbOffset) {
    251   arm::StoreOperandType type = arm::kStoreWord;
    252   int32_t offset = 4092;
    253   ASSERT_TRUE(arm::Address::CanHoldStoreOffsetThumb(type, offset));
    254 
    255   __ StoreToOffset(type, arm::R0, arm::SP, offset);
    256   __ StoreToOffset(type, arm::IP, arm::SP, offset);
    257   __ StoreToOffset(type, arm::IP, arm::R5, offset);
    258 
    259   const char* expected =
    260       "str r0, [sp, #4092]\n"
    261       "str ip, [sp, #4092]\n"
    262       "str ip, [r5, #4092]\n";
    263   DriverStr(expected, "StoreWordToThumbOffset");
    264 }
    265 
    266 TEST_F(AssemblerThumb2Test, StoreWordToNonThumbOffset) {
    267   arm::StoreOperandType type = arm::kStoreWord;
    268   int32_t offset = 4096;
    269   ASSERT_FALSE(arm::Address::CanHoldStoreOffsetThumb(type, offset));
    270 
    271   __ StoreToOffset(type, arm::R0, arm::SP, offset);
    272   __ StoreToOffset(type, arm::IP, arm::SP, offset);
    273   __ StoreToOffset(type, arm::IP, arm::R5, offset);
    274 
    275   const char* expected =
    276       "mov ip, #4096\n"       // LoadImmediate(ip, 4096)
    277       "add ip, ip, sp\n"
    278       "str r0, [ip, #0]\n"
    279 
    280       "str r5, [sp, #-4]!\n"  // Push(r5)
    281       "movw r5, #4100\n"      // LoadImmediate(r5, 4096 + kRegisterSize)
    282       "add r5, r5, sp\n"
    283       "str ip, [r5, #0]\n"
    284       "ldr r5, [sp], #4\n"    // Pop(r5)
    285 
    286       "str r6, [sp, #-4]!\n"  // Push(r6)
    287       "mov r6, #4096\n"       // LoadImmediate(r6, 4096)
    288       "add r6, r6, r5\n"
    289       "str ip, [r6, #0]\n"
    290       "ldr r6, [sp], #4\n";   // Pop(r6)
    291   DriverStr(expected, "StoreWordToNonThumbOffset");
    292 }
    293 
    294 TEST_F(AssemblerThumb2Test, StoreWordPairToThumbOffset) {
    295   arm::StoreOperandType type = arm::kStoreWordPair;
    296   int32_t offset = 1020;
    297   ASSERT_TRUE(arm::Address::CanHoldStoreOffsetThumb(type, offset));
    298 
    299   __ StoreToOffset(type, arm::R0, arm::SP, offset);
    300   // We cannot use IP (i.e. R12) as first source register, as it would
    301   // force us to use SP (i.e. R13) as second source register, which
    302   // would have an "unpredictable" effect according to the ARMv7
    303   // specification (the T1 encoding describes the result as
    304   // UNPREDICTABLE when of the source registers is R13).
    305   //
    306   // So we use (R11, IP) (e.g. (R11, R12)) as source registers in the
    307   // following instructions.
    308   __ StoreToOffset(type, arm::R11, arm::SP, offset);
    309   __ StoreToOffset(type, arm::R11, arm::R5, offset);
    310 
    311   const char* expected =
    312       "strd r0, r1, [sp, #1020]\n"
    313       "strd r11, ip, [sp, #1020]\n"
    314       "strd r11, ip, [r5, #1020]\n";
    315   DriverStr(expected, "StoreWordPairToThumbOffset");
    316 }
    317 
    318 TEST_F(AssemblerThumb2Test, StoreWordPairToNonThumbOffset) {
    319   arm::StoreOperandType type = arm::kStoreWordPair;
    320   int32_t offset = 1024;
    321   ASSERT_FALSE(arm::Address::CanHoldStoreOffsetThumb(type, offset));
    322 
    323   __ StoreToOffset(type, arm::R0, arm::SP, offset);
    324   // Same comment as in AssemblerThumb2Test.StoreWordPairToThumbOffset
    325   // regarding the use of (R11, IP) (e.g. (R11, R12)) as source
    326   // registers in the following instructions.
    327   __ StoreToOffset(type, arm::R11, arm::SP, offset);
    328   __ StoreToOffset(type, arm::R11, arm::R5, offset);
    329 
    330   const char* expected =
    331       "mov ip, #1024\n"           // LoadImmediate(ip, 1024)
    332       "add ip, ip, sp\n"
    333       "strd r0, r1, [ip, #0]\n"
    334 
    335       "str r5, [sp, #-4]!\n"      // Push(r5)
    336       "movw r5, #1028\n"          // LoadImmediate(r5, 1024 + kRegisterSize)
    337       "add r5, r5, sp\n"
    338       "strd r11, ip, [r5, #0]\n"
    339       "ldr r5, [sp], #4\n"        // Pop(r5)
    340 
    341       "str r6, [sp, #-4]!\n"      // Push(r6)
    342       "mov r6, #1024\n"           // LoadImmediate(r6, 1024)
    343       "add r6, r6, r5\n"
    344       "strd r11, ip, [r6, #0]\n"
    345       "ldr r6, [sp], #4\n";       // Pop(r6)
    346   DriverStr(expected, "StoreWordPairToNonThumbOffset");
    347 }
    348 
    349 }  // namespace art
    350