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 | |