| 1 | // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 2 | // for details. All rights reserved. Use of this source code is governed by a |
| 3 | // BSD-style license that can be found in the LICENSE file. |
| 4 | |
| 5 | #include "vm/globals.h" |
| 6 | #if defined(TARGET_ARCH_ARM) |
| 7 | |
| 8 | #include "vm/cpu.h" |
| 9 | #include "vm/cpu_arm.h" |
| 10 | |
| 11 | #include "vm/cpuinfo.h" |
| 12 | #include "vm/heap/heap.h" |
| 13 | #include "vm/isolate.h" |
| 14 | #include "vm/object.h" |
| 15 | #include "vm/simulator.h" |
| 16 | |
| 17 | #if defined(HOST_OS_IOS) |
| 18 | #include <libkern/OSCacheControl.h> |
| 19 | #endif |
| 20 | |
| 21 | #if !defined(TARGET_HOST_MISMATCH) |
| 22 | #include <sys/syscall.h> /* NOLINT */ |
| 23 | #include <unistd.h> /* NOLINT */ |
| 24 | #endif |
| 25 | |
| 26 | // ARM version differences. |
| 27 | // We support only ARMv7 and variants. We detect the presence of vfp, |
| 28 | // neon, and integer division instructions. Considering ARMv5TE as the baseline, |
| 29 | // later versions add the following features/instructions that we use: |
| 30 | // |
| 31 | // ARMv6: |
| 32 | // - PC read offset in store instructions is 8 rather than 12, matching the |
| 33 | // offset in read instructions, |
| 34 | // - strex, ldrex, and clrex load/store/clear exclusive instructions, |
| 35 | // - umaal multiplication instruction, |
| 36 | // ARMv7: |
| 37 | // - movw, movt 16-bit immediate load instructions, |
| 38 | // - mls multiplication instruction, |
| 39 | // - vmovs, vmovd floating point immediate load instructions. |
| 40 | // |
| 41 | // If an aarch64 CPU is detected, we generate ARMv7 code. |
| 42 | // |
| 43 | // Where we are missing vfp, we do not unbox doubles, or generate intrinsics for |
| 44 | // floating point operations. Where we are missing neon, we do not unbox SIMD |
| 45 | // values, or inline operations on SIMD values. Where we are missing integer |
| 46 | // division, we do not inline division operations, and we do not generate |
| 47 | // intrinsics that do division. See the feature tests in flow_graph_optimizer.cc |
| 48 | // for details. |
| 49 | |
| 50 | namespace dart { |
| 51 | |
| 52 | DEFINE_FLAG(bool, use_vfp, true, "Use vfp instructions if supported" ); |
| 53 | DEFINE_FLAG(bool, use_neon, true, "Use neon instructions if supported" ); |
| 54 | DEFINE_FLAG(bool, |
| 55 | use_integer_division, |
| 56 | true, |
| 57 | "Use integer division instruction if supported" ); |
| 58 | |
| 59 | #if defined(TARGET_HOST_MISMATCH) |
| 60 | #if defined(TARGET_OS_ANDROID) || defined(TARGET_OS_IOS) |
| 61 | DEFINE_FLAG(bool, sim_use_hardfp, false, "Use the hardfp ABI." ); |
| 62 | #else |
| 63 | DEFINE_FLAG(bool, sim_use_hardfp, true, "Use the hardfp ABI." ); |
| 64 | #endif |
| 65 | #endif |
| 66 | |
| 67 | void CPU::FlushICache(uword start, uword size) { |
| 68 | #if defined(DART_PRECOMPILED_RUNTIME) |
| 69 | UNREACHABLE(); |
| 70 | #elif !defined(TARGET_HOST_MISMATCH) && HOST_ARCH_ARM |
| 71 | // Nothing to do. Flushing no instructions. |
| 72 | if (size == 0) { |
| 73 | return; |
| 74 | } |
| 75 | |
| 76 | // ARM recommends using the gcc intrinsic __clear_cache on Linux, and the |
| 77 | // library call cacheflush from unistd.h on Android: |
| 78 | // |
| 79 | // https://community.arm.com/developer/ip-products/processors/b/processors-ip-blog/posts/caches-and-self-modifying-code |
| 80 | // |
| 81 | // On iOS we use sys_icache_invalidate from Darwin. See: |
| 82 | // |
| 83 | // https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/sys_icache_invalidate.3.html |
| 84 | #if defined(HOST_OS_IOS) |
| 85 | sys_icache_invalidate(reinterpret_cast<void*>(start), size); |
| 86 | #elif defined(__linux__) && !defined(ANDROID) |
| 87 | extern void __clear_cache(char*, char*); |
| 88 | char* beg = reinterpret_cast<char*>(start); |
| 89 | char* end = reinterpret_cast<char*>(start + size); |
| 90 | ::__clear_cache(beg, end); |
| 91 | #elif defined(ANDROID) |
| 92 | cacheflush(start, start + size, 0); |
| 93 | #else |
| 94 | #error FlushICache only tested/supported on Linux, Android and iOS |
| 95 | #endif |
| 96 | #endif |
| 97 | } |
| 98 | |
| 99 | const char* CPU::Id() { |
| 100 | return |
| 101 | #if defined(TARGET_HOST_MISMATCH) |
| 102 | "sim" |
| 103 | #endif // defined(TARGET_HOST_MISMATCH) |
| 104 | "arm" ; |
| 105 | } |
| 106 | |
| 107 | bool HostCPUFeatures::integer_division_supported_ = false; |
| 108 | bool HostCPUFeatures::vfp_supported_ = false; |
| 109 | bool HostCPUFeatures::neon_supported_ = false; |
| 110 | bool HostCPUFeatures::hardfp_supported_ = false; |
| 111 | const char* HostCPUFeatures::hardware_ = NULL; |
| 112 | intptr_t HostCPUFeatures::store_pc_read_offset_ = 8; |
| 113 | #if defined(DEBUG) |
| 114 | bool HostCPUFeatures::initialized_ = false; |
| 115 | #endif |
| 116 | |
| 117 | #if !defined(TARGET_HOST_MISMATCH) |
| 118 | #if HOST_OS_IOS |
| 119 | void HostCPUFeatures::Init() { |
| 120 | // TODO(24743): Actually check the CPU features and fail if we're missing |
| 121 | // something assumed in a precompiled snapshot. |
| 122 | hardware_ = "" ; |
| 123 | // When the VM is targetted to ARMv7, pretend that the CPU is ARMv7 even if |
| 124 | // the CPU is actually AArch64. |
| 125 | vfp_supported_ = FLAG_use_vfp; |
| 126 | integer_division_supported_ = FLAG_use_integer_division; |
| 127 | neon_supported_ = FLAG_use_neon; |
| 128 | hardfp_supported_ = false; |
| 129 | #if defined(DEBUG) |
| 130 | initialized_ = true; |
| 131 | #endif |
| 132 | } |
| 133 | #else // HOST_OS_IOS |
| 134 | void HostCPUFeatures::Init() { |
| 135 | bool is_arm64 = false; |
| 136 | CpuInfo::Init(); |
| 137 | hardware_ = CpuInfo::GetCpuModel(); |
| 138 | |
| 139 | // Check for ARMv7, or aarch64. |
| 140 | // It can be in either the Processor or Model information fields. |
| 141 | if (CpuInfo::FieldContains(kCpuInfoProcessor, "aarch64" ) || |
| 142 | CpuInfo::FieldContains(kCpuInfoModel, "aarch64" ) || |
| 143 | CpuInfo::FieldContains(kCpuInfoArchitecture, "8" ) || |
| 144 | CpuInfo::FieldContains(kCpuInfoArchitecture, "AArch64" )) { |
| 145 | // pretend that this arm64 cpu is really an ARMv7 |
| 146 | is_arm64 = true; |
| 147 | } else if (!CpuInfo::FieldContains(kCpuInfoProcessor, "ARMv7" ) && |
| 148 | !CpuInfo::FieldContains(kCpuInfoModel, "ARMv7" ) && |
| 149 | !CpuInfo::FieldContains(kCpuInfoArchitecture, "7" )) { |
| 150 | #if !defined(DART_RUN_IN_QEMU_ARMv7) |
| 151 | FATAL("Unrecognized ARM CPU architecture." ); |
| 152 | #endif |
| 153 | } |
| 154 | |
| 155 | #if defined(DART_RUN_IN_QEMU_ARMv7) |
| 156 | vfp_supported_ = true; |
| 157 | #else |
| 158 | // Has floating point unit. |
| 159 | vfp_supported_ = |
| 160 | (CpuInfo::FieldContains(kCpuInfoFeatures, "vfp" ) || is_arm64) && |
| 161 | FLAG_use_vfp; |
| 162 | #endif |
| 163 | |
| 164 | // Has integer division. |
| 165 | // Special cases: |
| 166 | // - Qualcomm Krait CPUs (QCT APQ8064) in Nexus 4 and 7 incorrectly report |
| 167 | // that they lack integer division. |
| 168 | // - Marvell Armada 370/XP incorrectly reports that it has integer division. |
| 169 | bool is_krait = CpuInfo::FieldContains(kCpuInfoHardware, "QCT APQ8064" ); |
| 170 | bool is_armada_370xp = |
| 171 | CpuInfo::FieldContains(kCpuInfoHardware, "Marvell Armada 370/XP" ); |
| 172 | bool is_virtual_machine = |
| 173 | CpuInfo::FieldContains(kCpuInfoHardware, "Dummy Virtual Machine" ); |
| 174 | #if defined(HOST_OS_ANDROID) |
| 175 | bool is_android = true; |
| 176 | #else |
| 177 | bool is_android = false; |
| 178 | #endif |
| 179 | if (is_krait) { |
| 180 | integer_division_supported_ = FLAG_use_integer_division; |
| 181 | } else if (is_android && is_arm64) { |
| 182 | // Various Android ARM64 devices, including the Qualcomm Snapdragon 820/821 |
| 183 | // CPUs (MSM 8996 and MSM8996pro) in Xiaomi MI5 and Pixel lack integer |
| 184 | // division even though ARMv8 requires it in A32. Instead of attempting to |
| 185 | // track all of these devices, we conservatively disable use of integer |
| 186 | // division on Android ARM64 devices. |
| 187 | // TODO(29270): /proc/self/auxv might be more reliable here. |
| 188 | integer_division_supported_ = false; |
| 189 | } else if (is_armada_370xp) { |
| 190 | integer_division_supported_ = false; |
| 191 | } else if (is_android && !is_arm64 && is_virtual_machine) { |
| 192 | // Some Android ARM emulators claim support for integer division in |
| 193 | // /proc/cpuinfo but do not actually support it. |
| 194 | integer_division_supported_ = false; |
| 195 | } else { |
| 196 | integer_division_supported_ = |
| 197 | (CpuInfo::FieldContains(kCpuInfoFeatures, "idiva" ) || is_arm64) && |
| 198 | FLAG_use_integer_division; |
| 199 | } |
| 200 | neon_supported_ = |
| 201 | (CpuInfo::FieldContains(kCpuInfoFeatures, "neon" ) || is_arm64) && |
| 202 | FLAG_use_vfp && FLAG_use_neon; |
| 203 | |
| 204 | // Use the cross-compiler's predefined macros to determine whether we should |
| 205 | // use the hard or soft float ABI. |
| 206 | #if defined(__ARM_PCS_VFP) || defined(DART_RUN_IN_QEMU_ARMv7) |
| 207 | hardfp_supported_ = true; |
| 208 | #else |
| 209 | hardfp_supported_ = false; |
| 210 | #endif |
| 211 | |
| 212 | #if defined(DEBUG) |
| 213 | initialized_ = true; |
| 214 | #endif |
| 215 | } |
| 216 | #endif // HOST_OS_IOS |
| 217 | |
| 218 | void HostCPUFeatures::Cleanup() { |
| 219 | DEBUG_ASSERT(initialized_); |
| 220 | #if defined(DEBUG) |
| 221 | initialized_ = false; |
| 222 | #endif |
| 223 | ASSERT(hardware_ != NULL); |
| 224 | free(const_cast<char*>(hardware_)); |
| 225 | hardware_ = NULL; |
| 226 | CpuInfo::Cleanup(); |
| 227 | } |
| 228 | |
| 229 | #else |
| 230 | |
| 231 | void HostCPUFeatures::Init() { |
| 232 | CpuInfo::Init(); |
| 233 | hardware_ = CpuInfo::GetCpuModel(); |
| 234 | |
| 235 | integer_division_supported_ = FLAG_use_integer_division; |
| 236 | vfp_supported_ = FLAG_use_vfp; |
| 237 | neon_supported_ = FLAG_use_vfp && FLAG_use_neon; |
| 238 | hardfp_supported_ = FLAG_sim_use_hardfp; |
| 239 | #if defined(DEBUG) |
| 240 | initialized_ = true; |
| 241 | #endif |
| 242 | } |
| 243 | |
| 244 | void HostCPUFeatures::Cleanup() { |
| 245 | DEBUG_ASSERT(initialized_); |
| 246 | #if defined(DEBUG) |
| 247 | initialized_ = false; |
| 248 | #endif |
| 249 | ASSERT(hardware_ != NULL); |
| 250 | free(const_cast<char*>(hardware_)); |
| 251 | hardware_ = NULL; |
| 252 | CpuInfo::Cleanup(); |
| 253 | } |
| 254 | #endif // !defined(TARGET_HOST_MISMATCH) |
| 255 | |
| 256 | } // namespace dart |
| 257 | |
| 258 | #endif // defined TARGET_ARCH_ARM |
| 259 | |