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