tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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