Architecture-arm.cpp (14708B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "jit/arm/Architecture-arm.h" 8 9 #if !defined(JS_SIMULATOR_ARM) && !defined(__APPLE__) 10 # include <elf.h> 11 #endif 12 13 #include <fcntl.h> 14 #include <string_view> 15 #ifdef XP_UNIX 16 # include <unistd.h> 17 #endif 18 19 #if defined(XP_IOS) 20 # include <libkern/OSCacheControl.h> 21 #endif 22 23 #include "jit/arm/Assembler-arm.h" 24 #include "jit/arm/Simulator-arm.h" 25 #include "jit/FlushICache.h" // js::jit::FlushICache 26 #include "jit/RegisterSets.h" 27 28 #if !defined(__linux__) || defined(ANDROID) || defined(JS_SIMULATOR_ARM) 29 // The Android NDK and B2G do not include the hwcap.h kernel header, and it is 30 // not defined when building the simulator, so inline the header defines we 31 // need. 32 # define HWCAP_VFP (1 << 6) 33 # define HWCAP_NEON (1 << 12) 34 # define HWCAP_VFPv3 (1 << 13) 35 # define HWCAP_VFPv3D16 (1 << 14) /* also set for VFPv4-D16 */ 36 # define HWCAP_VFPv4 (1 << 16) 37 # define HWCAP_IDIVA (1 << 17) 38 # define HWCAP_IDIVT (1 << 18) 39 # define HWCAP_VFPD32 (1 << 19) /* set if VFP has 32 regs (not 16) */ 40 # define HWCAP_FPHP (1 << 22) 41 # define AT_HWCAP 16 42 #else 43 # include <asm/hwcap.h> 44 # if !defined(HWCAP_IDIVA) 45 # define HWCAP_IDIVA (1 << 17) 46 # endif 47 # if !defined(HWCAP_VFPD32) 48 # define HWCAP_VFPD32 (1 << 19) /* set if VFP has 32 regs (not 16) */ 49 # endif 50 # if !defined(HWCAP_FPHP) 51 # define HWCAP_FPHP (1 << 22) 52 # endif 53 #endif 54 55 namespace js { 56 namespace jit { 57 58 // Parse the Linux kernel cpuinfo features. This is also used to parse the 59 // override features which has some extensions: 'armv7', 'align' and 'hardfp'. 60 static auto ParseARMCpuFeatures(const char* features, bool override = false) { 61 ARMCapabilities capabilities{}; 62 63 // For ease of running tests we want it to be the default to fixup faults. 64 bool fixupAlignmentFault = true; 65 66 for (;;) { 67 char ch = *features; 68 if (!ch) { 69 // End of string. 70 break; 71 } 72 if (ch == ' ' || ch == ',') { 73 // Skip separator characters. 74 features++; 75 continue; 76 } 77 // Find the end of the token. 78 const char* end = features + 1; 79 for (;; end++) { 80 ch = *end; 81 if (!ch || ch == ' ' || ch == ',') { 82 break; 83 } 84 } 85 size_t count = end - features; 86 std::string_view name{features, count}; 87 if (name == "vfp") { 88 capabilities += ARMCapability::VFP; 89 } else if (name == "vfpv2") { 90 capabilities += ARMCapability::VFP; // vfpv2 is the same as vfp 91 } else if (name == "neon") { 92 capabilities += ARMCapability::Neon; 93 } else if (name == "vfpv3") { 94 capabilities += ARMCapability::VFPv3; 95 } else if (name == "vfpv3d16") { 96 capabilities += ARMCapability::VFPv3D16; 97 } else if (name == "vfpv4") { 98 capabilities += ARMCapability::VFPv4; 99 } else if (name == "idiva") { 100 capabilities += ARMCapability::IDivA; 101 } else if (name == "vfpd32") { 102 capabilities += ARMCapability::VFPD32; 103 } else if (name == "fphp") { 104 capabilities += ARMCapability::FPHP; 105 } else if (name == "armv7") { 106 capabilities += ARMCapability::ARMv7; 107 } else if (name == "align") { 108 capabilities += 109 {ARMCapability::AlignmentFault, ARMCapability::FixupFault}; 110 #if defined(JS_SIMULATOR_ARM) 111 } else if (name == "nofixup") { 112 fixupAlignmentFault = false; 113 } else if (name == "hardfp") { 114 capabilities += ARMCapability::UseHardFpABI; 115 #endif 116 } else if (override) { 117 fprintf(stderr, "Warning: unexpected ARM feature at: %s\n", features); 118 } 119 features = end; 120 } 121 122 if (!fixupAlignmentFault) { 123 capabilities -= ARMCapability::FixupFault; 124 } 125 126 return capabilities; 127 } 128 129 static auto CanonicalizeARMHwCapabilities(ARMCapabilities capabilities) { 130 // Canonicalize the capabilities. These rules are also applied to the features 131 // supplied for simulation. 132 133 // VFPv3 is a subset of VFPv4, force this if the input string omits it. 134 if (capabilities.contains(ARMCapability::VFPv4)) { 135 capabilities += ARMCapability::VFPv3; 136 } 137 138 // The VFPv3 feature is expected when the VFPv3D16 is reported, but add it 139 // just in case of a kernel difference in feature reporting. 140 if (capabilities.contains(ARMCapability::VFPv3D16)) { 141 capabilities += ARMCapability::VFPv3; 142 } 143 144 // VFPv2 is a subset of VFPv3, force this if the input string omits it. VFPv2 145 // is just an alias for VFP. 146 if (capabilities.contains(ARMCapability::VFPv3)) { 147 capabilities += ARMCapability::VFP; 148 } 149 150 // If we have Neon we have floating point. 151 if (capabilities.contains(ARMCapability::Neon)) { 152 capabilities += ARMCapability::VFP; 153 } 154 155 // If VFPv3 or Neon is supported then this must be an ARMv7. 156 if (capabilities.contains(ARMCapability::VFPv3) || 157 capabilities.contains(ARMCapability::Neon)) { 158 capabilities += ARMCapability::ARMv7; 159 } 160 161 // Some old kernels report VFP and not VFPv3, but if ARMv7 then it must be 162 // VFPv3. 163 if (capabilities.contains(ARMCapability::VFP) && 164 capabilities.contains(ARMCapability::ARMv7)) { 165 capabilities += ARMCapability::VFPv3; 166 } 167 168 // Older kernels do not implement the HWCAP_VFPD32 flag. 169 if (capabilities.contains(ARMCapability::VFPv3) && 170 !capabilities.contains(ARMCapability::VFPv3D16)) { 171 capabilities += ARMCapability::VFPD32; 172 } 173 174 // If VFPv4 is supported, then half-precision floating point is supported. 175 if (capabilities.contains(ARMCapability::VFPv4)) { 176 capabilities += ARMCapability::FPHP; 177 } 178 179 return capabilities; 180 } 181 182 #if !defined(JS_SIMULATOR_ARM) && (defined(__linux__) || defined(ANDROID)) 183 static bool forceDoubleCacheFlush = false; 184 #endif 185 186 bool CPUFlagsHaveBeenComputed() { return ARMFlags::IsInitialized(); } 187 188 static const char* gArmHwCapString = nullptr; 189 190 void SetARMHwCapFlagsString(const char* armHwCap) { 191 MOZ_ASSERT(!CPUFlagsHaveBeenComputed()); 192 gArmHwCapString = armHwCap; 193 } 194 195 static auto ParseARMHwCapFlags(const char* armHwCap) { 196 MOZ_ASSERT(armHwCap); 197 198 if (strstr(armHwCap, "help")) { 199 fflush(NULL); 200 printf( 201 "\n" 202 "usage: ARMHWCAP=option,option,option,... where options can be:\n" 203 "\n" 204 " vfp \n" 205 " neon \n" 206 " vfpv3 \n" 207 " vfpv3d16 \n" 208 " vfpv4 \n" 209 " idiva \n" 210 " idivt \n" 211 " vfpd32 \n" 212 " fphp \n" 213 " armv7 \n" 214 " align - unaligned accesses will trap and be emulated\n" 215 #ifdef JS_SIMULATOR_ARM 216 " nofixup - disable emulation of unaligned accesses\n" 217 " hardfp \n" 218 #endif 219 "\n"); 220 exit(0); 221 /*NOTREACHED*/ 222 } 223 224 return ParseARMCpuFeatures(armHwCap, /* override = */ true); 225 } 226 227 #ifndef JS_SIMULATOR_ARM 228 # if defined(__linux__) || defined(ANDROID) 229 static auto FlagsToARMCapabilities(uint32_t flags) { 230 ARMCapabilities capabilities{}; 231 232 if (flags & HWCAP_VFP) { 233 capabilities += ARMCapability::VFP; 234 } 235 if (flags & HWCAP_VFPD32) { 236 capabilities += ARMCapability::VFPD32; 237 } 238 if (flags & HWCAP_VFPv3) { 239 capabilities += ARMCapability::VFPv3; 240 } 241 if (flags & HWCAP_VFPv3D16) { 242 capabilities += ARMCapability::VFPv3D16; 243 } 244 if (flags & HWCAP_VFPv4) { 245 capabilities += ARMCapability::VFPv4; 246 } 247 if (flags & HWCAP_NEON) { 248 capabilities += ARMCapability::Neon; 249 } 250 if (flags & HWCAP_IDIVA) { 251 capabilities += ARMCapability::IDivA; 252 } 253 if (flags & HWCAP_FPHP) { 254 capabilities += ARMCapability::FPHP; 255 } 256 257 return capabilities; 258 } 259 # endif 260 #endif 261 262 static auto ReadARMHwCapFlags() { 263 ARMCapabilities capabilities{}; 264 265 #ifdef JS_SIMULATOR_ARM 266 // ARMCapability::FixupFault is on by default even if 267 // ARMCapability::AlignmentFault is not on by default, because some memory 268 // access instructions always fault. Notably, this is true for floating point 269 // accesses. 270 capabilities += { 271 ARMCapability::ARMv7, ARMCapability::VFP, ARMCapability::VFPv3, 272 ARMCapability::VFPv4, ARMCapability::Neon, ARMCapability::IDivA, 273 ARMCapability::FixupFault, 274 }; 275 #else 276 277 # if defined(__linux__) || defined(ANDROID) 278 // This includes Android and B2G. 279 bool readAuxv = false; 280 int fd = open("/proc/self/auxv", O_RDONLY); 281 if (fd > 0) { 282 struct { 283 uint32_t a_type; 284 uint32_t a_val; 285 } aux; 286 while (read(fd, &aux, sizeof(aux))) { 287 if (aux.a_type == AT_HWCAP) { 288 uint32_t flags = aux.a_val; 289 capabilities += FlagsToARMCapabilities(flags); 290 readAuxv = true; 291 break; 292 } 293 } 294 close(fd); 295 } 296 297 FILE* fp = fopen("/proc/cpuinfo", "r"); 298 if (fp) { 299 char buf[1024] = {}; 300 size_t len = fread(buf, sizeof(char), sizeof(buf) - 1, fp); 301 fclose(fp); 302 buf[len] = '\0'; 303 304 // Read the cpuinfo Features if the auxv is not available. 305 if (!readAuxv) { 306 char* featureList = strstr(buf, "Features"); 307 if (featureList) { 308 if (char* featuresEnd = strstr(featureList, "\n")) { 309 *featuresEnd = '\0'; 310 } 311 capabilities += ParseARMCpuFeatures(featureList + 8); 312 } 313 if (strstr(buf, "ARMv7")) { 314 capabilities += ARMCapability::ARMv7; 315 } 316 } 317 318 // The exynos7420 cpu (EU galaxy S6 (Note)) has a bug where sometimes 319 // flushing doesn't invalidate the instruction cache. As a result we force 320 // it by calling the cacheFlush twice on different start addresses. 321 char* exynos7420 = strstr(buf, "Exynos7420"); 322 if (exynos7420) { 323 forceDoubleCacheFlush = true; 324 } 325 } 326 # endif 327 328 // If compiled to use specialized features then these features can be 329 // assumed to be present otherwise the compiler would fail to run. 330 331 # if defined(__VFP_FP__) && !defined(__SOFTFP__) 332 // Compiled to use VFP instructions so assume VFP support. 333 capabilities += ARMCapability::VFP; 334 # endif 335 336 # if defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) 337 // Compiled to use ARMv7 instructions so assume the ARMv7 arch. 338 capabilities += ARMCapability::ARMv7; 339 # endif 340 341 # if defined(__APPLE__) 342 # if defined(__ARM_NEON__) 343 capabilities += ARMCapability::Neon; 344 # endif 345 # if defined(__ARMVFPV3__) 346 capabilities += {ARMCapability::VFPv3, ARMCapability::VFPD32}; 347 # endif 348 # endif 349 350 #endif // JS_SIMULATOR_ARM 351 352 return capabilities; 353 } 354 355 void ARMFlags::Init() { 356 MOZ_RELEASE_ASSERT(!IsInitialized()); 357 358 ARMCapabilities capFlags; 359 if (const char* env = getenv("ARMHWCAP")) { 360 capFlags = ParseARMHwCapFlags(env); 361 } else if (gArmHwCapString) { 362 capFlags = ParseARMHwCapFlags(gArmHwCapString); 363 } else { 364 capFlags = ReadARMHwCapFlags(); 365 } 366 367 capFlags = CanonicalizeARMHwCapabilities(capFlags); 368 369 MOZ_ASSERT(!capFlags.contains(ARMCapability::Initialized)); 370 capFlags += ARMCapability::Initialized; 371 372 capabilities = capFlags; 373 374 JitSpew(JitSpew_Codegen, "ARM HWCAP: 0x%x\n", capFlags.serialize()); 375 } 376 377 Registers::Code Registers::FromName(const char* name) { 378 // Check for some register aliases first. 379 if (strcmp(name, "ip") == 0) { 380 return ip; 381 } 382 if (strcmp(name, "r13") == 0) { 383 return r13; 384 } 385 if (strcmp(name, "lr") == 0) { 386 return lr; 387 } 388 if (strcmp(name, "r15") == 0) { 389 return r15; 390 } 391 392 for (size_t i = 0; i < Total; i++) { 393 if (strcmp(GetName(i), name) == 0) { 394 return Code(i); 395 } 396 } 397 398 return Invalid; 399 } 400 401 FloatRegisters::Code FloatRegisters::FromName(const char* name) { 402 for (size_t i = 0; i < TotalSingle; ++i) { 403 if (strcmp(GetSingleName(Encoding(i)), name) == 0) { 404 return VFPRegister(i, VFPRegister::Single).code(); 405 } 406 } 407 for (size_t i = 0; i < TotalDouble; ++i) { 408 if (strcmp(GetDoubleName(Encoding(i)), name) == 0) { 409 return VFPRegister(i, VFPRegister::Double).code(); 410 } 411 } 412 413 return Invalid; 414 } 415 416 FloatRegisterSet VFPRegister::ReduceSetForPush(const FloatRegisterSet& s) { 417 #ifdef ENABLE_WASM_SIMD 418 # error "Needs more careful logic if SIMD is enabled" 419 #endif 420 421 LiveFloatRegisterSet mod; 422 for (FloatRegisterIterator iter(s); iter.more(); ++iter) { 423 FloatRegister reg = *iter; 424 if (reg.isSingle() && s.hasRegisterIndex(reg.doubleOverlay())) { 425 // If a single register overlays a double, we don't have to push the 426 // single register separately. 427 continue; 428 } 429 mod.addUnchecked(*iter); 430 } 431 return mod.set(); 432 } 433 434 uint32_t VFPRegister::GetPushSizeInBytes(const FloatRegisterSet& s) { 435 #ifdef ENABLE_WASM_SIMD 436 # error "Needs more careful logic if SIMD is enabled" 437 #endif 438 439 FloatRegisterSet ss = s.reduceSetForPush(); 440 uint64_t bits = ss.bits(); 441 uint32_t ret = mozilla::CountPopulation32(bits & 0xffffffff) * sizeof(float); 442 ret += mozilla::CountPopulation32(bits >> 32) * sizeof(double); 443 return ret; 444 } 445 uint32_t VFPRegister::getRegisterDumpOffsetInBytes() { 446 #ifdef ENABLE_WASM_SIMD 447 # error "Needs more careful logic if SIMD is enabled" 448 #endif 449 450 if (isSingle()) { 451 return id() * sizeof(float); 452 } 453 if (isDouble()) { 454 return id() * sizeof(double); 455 } 456 MOZ_CRASH("not Single or Double"); 457 } 458 459 uint32_t FloatRegisters::ActualTotalPhys() { 460 if (ARMFlags::Has32DP()) { 461 return 32; 462 } 463 return 16; 464 } 465 466 void FlushICache(void* code, size_t size) { 467 #if defined(JS_SIMULATOR_ARM) 468 js::jit::SimulatorProcess::FlushICache(code, size); 469 470 #elif (defined(__linux__) || defined(ANDROID)) && defined(__GNUC__) 471 void* end = (void*)(reinterpret_cast<char*>(code) + size); 472 asm volatile( 473 "push {r7}\n" 474 "mov r0, %0\n" 475 "mov r1, %1\n" 476 "mov r7, #0xf0000\n" 477 "add r7, r7, #0x2\n" 478 "mov r2, #0x0\n" 479 "svc 0x0\n" 480 "pop {r7}\n" 481 : 482 : "r"(code), "r"(end) 483 : "r0", "r1", "r2"); 484 485 if (forceDoubleCacheFlush) { 486 void* start = (void*)((uintptr_t)code + 1); 487 asm volatile( 488 "push {r7}\n" 489 "mov r0, %0\n" 490 "mov r1, %1\n" 491 "mov r7, #0xf0000\n" 492 "add r7, r7, #0x2\n" 493 "mov r2, #0x0\n" 494 "svc 0x0\n" 495 "pop {r7}\n" 496 : 497 : "r"(start), "r"(end) 498 : "r0", "r1", "r2"); 499 } 500 501 #elif defined(__FreeBSD__) || defined(__NetBSD__) 502 __clear_cache(code, reinterpret_cast<char*>(code) + size); 503 504 #elif defined(XP_IOS) 505 sys_icache_invalidate(code, size); 506 507 #else 508 # error "Unexpected platform" 509 #endif 510 } 511 512 void FlushExecutionContext() { 513 #ifndef JS_SIMULATOR_ARM 514 // Ensure that any instructions already in the pipeline are discarded and 515 // reloaded from the icache. 516 asm volatile("isb\n" : : : "memory"); 517 #else 518 // We assume the icache flushing routines on other platforms take care of this 519 #endif 520 } 521 522 } // namespace jit 523 } // namespace js