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(®isters_); 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