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
50namespace dart {
51
52DEFINE_FLAG(bool, use_vfp, true, "Use vfp instructions if supported");
53DEFINE_FLAG(bool, use_neon, true, "Use neon instructions if supported");
54DEFINE_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)
61DEFINE_FLAG(bool, sim_use_hardfp, false, "Use the hardfp ABI.");
62#else
63DEFINE_FLAG(bool, sim_use_hardfp, true, "Use the hardfp ABI.");
64#endif
65#endif
66
67void 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
99const char* CPU::Id() {
100 return
101#if defined(TARGET_HOST_MISMATCH)
102 "sim"
103#endif // defined(TARGET_HOST_MISMATCH)
104 "arm";
105}
106
107bool HostCPUFeatures::integer_division_supported_ = false;
108bool HostCPUFeatures::vfp_supported_ = false;
109bool HostCPUFeatures::neon_supported_ = false;
110bool HostCPUFeatures::hardfp_supported_ = false;
111const char* HostCPUFeatures::hardware_ = NULL;
112intptr_t HostCPUFeatures::store_pc_read_offset_ = 8;
113#if defined(DEBUG)
114bool HostCPUFeatures::initialized_ = false;
115#endif
116
117#if !defined(TARGET_HOST_MISMATCH)
118#if HOST_OS_IOS
119void 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
134void 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
218void 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
231void 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
244void 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