1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sts=4 et sw=4 tw=99:
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_ARM_SIMULATOR) && !defined(__APPLE__)
10 #include <elf.h>
11 #endif
12
13 #include <fcntl.h>
14 #include <unistd.h>
15
16 #include "jit/arm/Assembler-arm.h"
17 #include "jit/RegisterSets.h"
18
19 #if !defined(__linux__) || defined(ANDROID) || defined(JS_SIMULATOR_ARM)
20 // The Android NDK and B2G do not include the hwcap.h kernel header, and it is not
21 // defined when building the simulator, so inline the header defines we need.
22 # define HWCAP_VFP (1 << 6)
23 # define HWCAP_NEON (1 << 12)
24 # define HWCAP_VFPv3 (1 << 13)
25 # define HWCAP_VFPv3D16 (1 << 14) /* also set for VFPv4-D16 */
26 # define HWCAP_VFPv4 (1 << 16)
27 # define HWCAP_IDIVA (1 << 17)
28 # define HWCAP_IDIVT (1 << 18)
29 # define HWCAP_VFPD32 (1 << 19) /* set if VFP has 32 regs (not 16) */
30 # define AT_HWCAP 16
31 #else
32 # include <asm/hwcap.h>
33 # if !defined(HWCAP_IDIVA)
34 # define HWCAP_IDIVA (1 << 17)
35 # endif
36 # if !defined(HWCAP_VFPD32)
37 # define HWCAP_VFPD32 (1 << 19) /* set if VFP has 32 regs (not 16) */
38 # endif
39 #endif
40
41 namespace js {
42 namespace jit {
43
44
45 // Parse the Linux kernel cpuinfo features. This is also used to parse the
46 // override features which has some extensions: 'armv7', 'align' and 'hardfp'.
47 static uint32_t
ParseARMCpuFeatures(const char * features,bool override=false)48 ParseARMCpuFeatures(const char* features, bool override = false)
49 {
50 uint32_t flags = 0;
51
52 for (;;) {
53 char ch = *features;
54 if (!ch) {
55 // End of string.
56 break;
57 }
58 if (ch == ' ' || ch == ',') {
59 // Skip separator characters.
60 features++;
61 continue;
62 }
63 // Find the end of the token.
64 const char* end = features + 1;
65 for (; ; end++) {
66 ch = *end;
67 if (!ch || ch == ' ' || ch == ',')
68 break;
69 }
70 size_t count = end - features;
71 if (count == 3 && strncmp(features, "vfp", 3) == 0)
72 flags |= HWCAP_VFP;
73 else if (count == 4 && strncmp(features, "neon", 4) == 0)
74 flags |= HWCAP_NEON;
75 else if (count == 5 && strncmp(features, "vfpv3", 5) == 0)
76 flags |= HWCAP_VFPv3;
77 else if (count == 8 && strncmp(features, "vfpv3d16", 8) == 0)
78 flags |= HWCAP_VFPv3D16;
79 else if (count == 5 && strncmp(features, "vfpv4", 5) == 0)
80 flags |= HWCAP_VFPv4;
81 else if (count == 5 && strncmp(features, "idiva", 5) == 0)
82 flags |= HWCAP_IDIVA;
83 else if (count == 5 && strncmp(features, "idivt", 5) == 0)
84 flags |= HWCAP_IDIVT;
85 else if (count == 6 && strncmp(features, "vfpd32", 6) == 0)
86 flags |= HWCAP_VFPD32;
87 else if (count == 5 && strncmp(features, "armv7", 5) == 0)
88 flags |= HWCAP_ARMv7;
89 else if (count == 5 && strncmp(features, "align", 5) == 0)
90 flags |= HWCAP_ALIGNMENT_FAULT;
91 #if defined(JS_SIMULATOR_ARM)
92 else if (count == 6 && strncmp(features, "hardfp", 6) == 0)
93 flags |= HWCAP_USE_HARDFP_ABI;
94 #endif
95 else if (override)
96 fprintf(stderr, "Warning: unexpected ARM feature at: %s\n", features);
97 features = end;
98 }
99 return flags;
100 }
101
102 static uint32_t
CanonicalizeARMHwCapFlags(uint32_t flags)103 CanonicalizeARMHwCapFlags(uint32_t flags)
104 {
105 // Canonicalize the flags. These rules are also applied to the features
106 // supplied for simulation.
107
108 // The VFPv3 feature is expected when the VFPv3D16 is reported, but add it
109 // just in case of a kernel difference in feature reporting.
110 if (flags & HWCAP_VFPv3D16)
111 flags |= HWCAP_VFPv3;
112
113 // If VFPv3 or Neon is supported then this must be an ARMv7.
114 if (flags & (HWCAP_VFPv3 | HWCAP_NEON))
115 flags |= HWCAP_ARMv7;
116
117 // Some old kernels report VFP and not VFPv3, but if ARMv7 then it must be
118 // VFPv3.
119 if (flags & HWCAP_VFP && flags & HWCAP_ARMv7)
120 flags |= HWCAP_VFPv3;
121
122 // Older kernels do not implement the HWCAP_VFPD32 flag.
123 if ((flags & HWCAP_VFPv3) && !(flags & HWCAP_VFPv3D16))
124 flags |= HWCAP_VFPD32;
125
126 return flags;
127 }
128
129 // The override flags parsed from the ARMHWCAP environment variable or from the
130 // --arm-hwcap js shell argument.
131 volatile uint32_t armHwCapFlags = HWCAP_UNINITIALIZED;
132
133 bool
ParseARMHwCapFlags(const char * armHwCap)134 ParseARMHwCapFlags(const char* armHwCap)
135 {
136 uint32_t flags = 0;
137
138 if (!armHwCap)
139 return false;
140
141 if (strstr(armHwCap, "help")) {
142 fflush(NULL);
143 printf(
144 "\n"
145 "usage: ARMHWCAP=option,option,option,... where options can be:\n"
146 "\n"
147 " vfp \n"
148 " neon \n"
149 " vfpv3 \n"
150 " vfpv3d16 \n"
151 " vfpv4 \n"
152 " idiva \n"
153 " idivt \n"
154 " vfpd32 \n"
155 " armv7 \n"
156 " align \n"
157 #ifdef JS_SIMULATOR_ARM
158 " hardfp \n"
159 #endif
160 "\n"
161 );
162 exit(0);
163 /*NOTREACHED*/
164 }
165
166 flags = ParseARMCpuFeatures(armHwCap, /* override = */ true);
167
168 #ifdef JS_CODEGEN_ARM_HARDFP
169 flags |= HWCAP_USE_HARDFP_ABI;
170 #endif
171
172 armHwCapFlags = CanonicalizeARMHwCapFlags(flags);
173 JitSpew(JitSpew_Codegen, "ARM HWCAP: 0x%x\n", armHwCapFlags);
174 return true;
175 }
176
177 void
InitARMFlags()178 InitARMFlags()
179 {
180 uint32_t flags = 0;
181
182 if (armHwCapFlags != HWCAP_UNINITIALIZED)
183 return;
184
185 const char* env = getenv("ARMHWCAP");
186 if (ParseARMHwCapFlags(env))
187 return;
188
189 #ifdef JS_SIMULATOR_ARM
190 flags = HWCAP_ARMv7 | HWCAP_VFP | HWCAP_VFPv3 | HWCAP_VFPv4 | HWCAP_NEON;
191 #else
192
193 #if defined(__linux__)
194 // This includes Android and B2G.
195 bool readAuxv = false;
196 int fd = open("/proc/self/auxv", O_RDONLY);
197 if (fd > 0) {
198 struct { uint32_t a_type; uint32_t a_val; } aux;
199 while (read(fd, &aux, sizeof(aux))) {
200 if (aux.a_type == AT_HWCAP) {
201 flags = aux.a_val;
202 readAuxv = true;
203 break;
204 }
205 }
206 close(fd);
207 }
208
209 if (!readAuxv) {
210 // Read the cpuinfo Features if the auxv is not available.
211 FILE* fp = fopen("/proc/cpuinfo", "r");
212 if (fp) {
213 char buf[1024];
214 memset(buf, 0, sizeof(buf));
215 size_t len = fread(buf, sizeof(char), sizeof(buf) - 1, fp);
216 fclose(fp);
217 buf[len] = '\0';
218 char* featureList = strstr(buf, "Features");
219 if (featureList) {
220 if (char* featuresEnd = strstr(featureList, "\n"))
221 *featuresEnd = '\0';
222 flags = ParseARMCpuFeatures(featureList + 8);
223 }
224 if (strstr(buf, "ARMv7"))
225 flags |= HWCAP_ARMv7;
226 }
227 }
228 #endif
229
230 // If compiled to use specialized features then these features can be
231 // assumed to be present otherwise the compiler would fail to run.
232
233 #ifdef JS_CODEGEN_ARM_HARDFP
234 // Compiled to use the hardfp ABI.
235 flags |= HWCAP_USE_HARDFP_ABI;
236 #endif
237
238 #if defined(__VFP_FP__) && !defined(__SOFTFP__)
239 // Compiled to use VFP instructions so assume VFP support.
240 flags |= HWCAP_VFP;
241 #endif
242
243 #if defined(__ARM_ARCH_7__) || defined (__ARM_ARCH_7A__)
244 // Compiled to use ARMv7 instructions so assume the ARMv7 arch.
245 flags |= HWCAP_ARMv7;
246 #endif
247
248 #if defined(__APPLE__)
249 #if defined(__ARM_NEON__)
250 flags |= HWCAP_NEON;
251 #endif
252 #if defined(__ARMVFPV3__)
253 flags |= HWCAP_VFPv3 | HWCAP_VFPD32
254 #endif
255 #endif
256
257 #endif // JS_SIMULATOR_ARM
258
259 armHwCapFlags = CanonicalizeARMHwCapFlags(flags);
260
261 JitSpew(JitSpew_Codegen, "ARM HWCAP: 0x%x\n", armHwCapFlags);
262 return;
263 }
264
265 uint32_t
GetARMFlags()266 GetARMFlags()
267 {
268 MOZ_ASSERT(armHwCapFlags != HWCAP_UNINITIALIZED);
269 return armHwCapFlags;
270 }
271
HasMOVWT()272 bool HasMOVWT()
273 {
274 MOZ_ASSERT(armHwCapFlags != HWCAP_UNINITIALIZED);
275 return armHwCapFlags & HWCAP_ARMv7;
276 }
277
HasLDSTREXBHD()278 bool HasLDSTREXBHD()
279 {
280 // These are really available from ARMv6K and later, but why bother?
281 MOZ_ASSERT(armHwCapFlags != HWCAP_UNINITIALIZED);
282 return armHwCapFlags & HWCAP_ARMv7;
283 }
284
HasDMBDSBISB()285 bool HasDMBDSBISB()
286 {
287 MOZ_ASSERT(armHwCapFlags != HWCAP_UNINITIALIZED);
288 return armHwCapFlags & HWCAP_ARMv7;
289 }
290
HasVFPv3()291 bool HasVFPv3()
292 {
293 MOZ_ASSERT(armHwCapFlags != HWCAP_UNINITIALIZED);
294 return armHwCapFlags & HWCAP_VFPv3;
295 }
296
HasVFP()297 bool HasVFP()
298 {
299 MOZ_ASSERT(armHwCapFlags != HWCAP_UNINITIALIZED);
300 return armHwCapFlags & HWCAP_VFP;
301 }
302
Has32DP()303 bool Has32DP()
304 {
305 MOZ_ASSERT(armHwCapFlags != HWCAP_UNINITIALIZED);
306 return armHwCapFlags & HWCAP_VFPD32;
307 }
308
HasIDIV()309 bool HasIDIV()
310 {
311 MOZ_ASSERT(armHwCapFlags != HWCAP_UNINITIALIZED);
312 return armHwCapFlags & HWCAP_IDIVA;
313 }
314
315 // This is defined in the header and inlined when not using the simulator.
316 #ifdef JS_SIMULATOR_ARM
UseHardFpABI()317 bool UseHardFpABI()
318 {
319 MOZ_ASSERT(armHwCapFlags != HWCAP_UNINITIALIZED);
320 return armHwCapFlags & HWCAP_USE_HARDFP_ABI;
321 }
322 #endif
323
324 Registers::Code
FromName(const char * name)325 Registers::FromName(const char* name)
326 {
327 // Check for some register aliases first.
328 if (strcmp(name, "ip") == 0)
329 return ip;
330 if (strcmp(name, "r13") == 0)
331 return r13;
332 if (strcmp(name, "lr") == 0)
333 return lr;
334 if (strcmp(name, "r15") == 0)
335 return r15;
336
337 for (size_t i = 0; i < Total; i++) {
338 if (strcmp(GetName(i), name) == 0)
339 return Code(i);
340 }
341
342 return Invalid;
343 }
344
345 FloatRegisters::Code
FromName(const char * name)346 FloatRegisters::FromName(const char* name)
347 {
348 for (size_t i = 0; i < Total; i++) {
349 if (strcmp(GetName(i), name) == 0)
350 return Code(i);
351 }
352
353 return Invalid;
354 }
355
356 FloatRegisterSet
ReduceSetForPush(const FloatRegisterSet & s)357 VFPRegister::ReduceSetForPush(const FloatRegisterSet& s)
358 {
359 LiveFloatRegisterSet mod;
360 for (FloatRegisterIterator iter(s); iter.more(); iter++) {
361 if ((*iter).isSingle()) {
362 // Add in just this float.
363 mod.addUnchecked(*iter);
364 } else if ((*iter).id() < 16) {
365 // A double with an overlay, add in both floats.
366 mod.addUnchecked((*iter).singleOverlay(0));
367 mod.addUnchecked((*iter).singleOverlay(1));
368 } else {
369 // Add in the lone double in the range 16-31.
370 mod.addUnchecked(*iter);
371 }
372 }
373 return mod.set();
374 }
375
376 uint32_t
GetPushSizeInBytes(const FloatRegisterSet & s)377 VFPRegister::GetPushSizeInBytes(const FloatRegisterSet& s)
378 {
379 FloatRegisterSet ss = s.reduceSetForPush();
380 uint64_t bits = ss.bits();
381 uint32_t ret = mozilla::CountPopulation32(bits&0xffffffff) * sizeof(float);
382 ret += mozilla::CountPopulation32(bits >> 32) * sizeof(double);
383 return ret;
384 }
385 uint32_t
getRegisterDumpOffsetInBytes()386 VFPRegister::getRegisterDumpOffsetInBytes()
387 {
388 if (isSingle())
389 return id() * sizeof(float);
390 if (isDouble())
391 return id() * sizeof(double);
392 MOZ_CRASH("not Single or Double");
393 }
394
395 uint32_t
ActualTotalPhys()396 FloatRegisters::ActualTotalPhys()
397 {
398 if (Has32DP())
399 return 32;
400 return 16;
401 }
402
403
404 } // namespace jit
405 } // namespace js
406
407