[vm] Use AT_HWCAP instead of /proc/cpuinfo for most ARM feature detection.
Under user-space emulation or 32-bit ARM on a 64-bit ARM system, /proc/cpuinfo describes the host CPU instead of the emulated ARM CPU. Also fix the encoding of sdiv. Apparently QEMU is stricter than hardware. TEST=ci Bug: https://github.com/dart-lang/sdk/issues/29270 Bug: https://github.com/dart-lang/sdk/issues/54909 Change-Id: Icadf1a276e468dbb8142da49f961f33bca499d27 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/352680 Commit-Queue: Ryan Macnak <rmacnak@google.com> Reviewed-by: Slava Egorov <vegorov@google.com> Reviewed-by: Alexander Markov <alexmarkov@google.com>
This commit is contained in:
committed by
Commit Queue
parent
19aa44bd10
commit
688917045f
@@ -419,7 +419,8 @@ void Assembler::EmitDivOp(Condition cond,
|
|||||||
int32_t encoding = opcode | (static_cast<int32_t>(cond) << kConditionShift) |
|
int32_t encoding = opcode | (static_cast<int32_t>(cond) << kConditionShift) |
|
||||||
(static_cast<int32_t>(rn) << kDivRnShift) |
|
(static_cast<int32_t>(rn) << kDivRnShift) |
|
||||||
(static_cast<int32_t>(rd) << kDivRdShift) | B26 | B25 |
|
(static_cast<int32_t>(rd) << kDivRdShift) | B26 | B25 |
|
||||||
B24 | B20 | B4 | (static_cast<int32_t>(rm) << kDivRmShift);
|
B24 | B20 | B15 | B14 | B13 | B12 | B4 |
|
||||||
|
(static_cast<int32_t>(rm) << kDivRmShift);
|
||||||
Emit(encoding);
|
Emit(encoding);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -85,6 +85,9 @@ enum {
|
|||||||
B10 = 1 << 10,
|
B10 = 1 << 10,
|
||||||
B11 = 1 << 11,
|
B11 = 1 << 11,
|
||||||
B12 = 1 << 12,
|
B12 = 1 << 12,
|
||||||
|
B13 = 1 << 13,
|
||||||
|
B14 = 1 << 14,
|
||||||
|
B15 = 1 << 15,
|
||||||
B16 = 1 << 16,
|
B16 = 1 << 16,
|
||||||
B17 = 1 << 17,
|
B17 = 1 << 17,
|
||||||
B18 = 1 << 18,
|
B18 = 1 << 18,
|
||||||
|
|||||||
@@ -65,6 +65,13 @@ TEST_CASE(ReciprocalOps) {
|
|||||||
|
|
||||||
#define __ assembler->
|
#define __ assembler->
|
||||||
|
|
||||||
|
#if defined(PRODUCT)
|
||||||
|
#define EXPECT_DISASSEMBLY(expected)
|
||||||
|
#else
|
||||||
|
#define EXPECT_DISASSEMBLY(expected) \
|
||||||
|
EXPECT_STREQ(expected, test->RelativeDisassembly())
|
||||||
|
#endif
|
||||||
|
|
||||||
ASSEMBLER_TEST_GENERATE(Simple, assembler) {
|
ASSEMBLER_TEST_GENERATE(Simple, assembler) {
|
||||||
__ mov(R0, Operand(42));
|
__ mov(R0, Operand(42));
|
||||||
__ Ret();
|
__ Ret();
|
||||||
@@ -1746,6 +1753,12 @@ ASSEMBLER_TEST_RUN(Udiv, test) {
|
|||||||
if (TargetCPUFeatures::integer_division_supported()) {
|
if (TargetCPUFeatures::integer_division_supported()) {
|
||||||
typedef int (*Tst)() DART_UNUSED;
|
typedef int (*Tst)() DART_UNUSED;
|
||||||
EXPECT_EQ(3, EXECUTE_TEST_CODE_INT32(Tst, test->entry()));
|
EXPECT_EQ(3, EXECUTE_TEST_CODE_INT32(Tst, test->entry()));
|
||||||
|
EXPECT_DISASSEMBLY(
|
||||||
|
"e3a0001b mov r0, #27\n"
|
||||||
|
"e3a01009 mov r1, #9\n"
|
||||||
|
"e732f110 udiv r2, r0, r1\n"
|
||||||
|
"e1a00002 mov r0, r2\n"
|
||||||
|
"e12fff1e bx lr\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1764,6 +1777,12 @@ ASSEMBLER_TEST_RUN(Sdiv, test) {
|
|||||||
if (TargetCPUFeatures::integer_division_supported()) {
|
if (TargetCPUFeatures::integer_division_supported()) {
|
||||||
typedef int (*Tst)() DART_UNUSED;
|
typedef int (*Tst)() DART_UNUSED;
|
||||||
EXPECT_EQ(-3, EXECUTE_TEST_CODE_INT32(Tst, test->entry()));
|
EXPECT_EQ(-3, EXECUTE_TEST_CODE_INT32(Tst, test->entry()));
|
||||||
|
EXPECT_DISASSEMBLY(
|
||||||
|
"e3a0001b mov r0, #27\n"
|
||||||
|
"e3e01008 mvn r1, #8\n"
|
||||||
|
"e712f110 sdiv r2, r0, r1\n"
|
||||||
|
"e1a00002 mov r0, r2\n"
|
||||||
|
"e12fff1e bx lr\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3423,7 +3442,7 @@ ASSEMBLER_TEST_GENERATE(Vminqs_zero, assembler) {
|
|||||||
ASSEMBLER_TEST_RUN(Vminqs_zero, test) {
|
ASSEMBLER_TEST_RUN(Vminqs_zero, test) {
|
||||||
EXPECT(test != nullptr);
|
EXPECT(test != nullptr);
|
||||||
if (TargetCPUFeatures::neon_supported()) {
|
if (TargetCPUFeatures::neon_supported()) {
|
||||||
typedef int (*Tst)() DART_UNUSED;
|
typedef float (*Tst)() DART_UNUSED;
|
||||||
float res = EXECUTE_TEST_CODE_FLOAT(Tst, test->entry());
|
float res = EXECUTE_TEST_CODE_FLOAT(Tst, test->entry());
|
||||||
EXPECT_EQ(true, signbit(res) && (res == 0.0));
|
EXPECT_EQ(true, signbit(res) && (res == 0.0));
|
||||||
}
|
}
|
||||||
@@ -3474,7 +3493,7 @@ ASSEMBLER_TEST_GENERATE(Vmaxqs_zero, assembler) {
|
|||||||
ASSEMBLER_TEST_RUN(Vmaxqs_zero, test) {
|
ASSEMBLER_TEST_RUN(Vmaxqs_zero, test) {
|
||||||
EXPECT(test != nullptr);
|
EXPECT(test != nullptr);
|
||||||
if (TargetCPUFeatures::neon_supported()) {
|
if (TargetCPUFeatures::neon_supported()) {
|
||||||
typedef int (*Tst)() DART_UNUSED;
|
typedef float (*Tst)() DART_UNUSED;
|
||||||
float res = EXECUTE_TEST_CODE_FLOAT(Tst, test->entry());
|
float res = EXECUTE_TEST_CODE_FLOAT(Tst, test->entry());
|
||||||
EXPECT_EQ(true, !signbit(res) && (res == 0.0));
|
EXPECT_EQ(true, !signbit(res) && (res == 0.0));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,7 +75,8 @@ void DisassembleToMemory::ConsumeInstruction(char* hex_buffer,
|
|||||||
|
|
||||||
// TODO(compiler): Update assembler tests for other architectures so there is
|
// TODO(compiler): Update assembler tests for other architectures so there is
|
||||||
// coverage of encodings, not just mnemonics.
|
// coverage of encodings, not just mnemonics.
|
||||||
#if defined(TARGET_ARCH_RISCV32) || defined(TARGET_ARCH_RISCV64)
|
#if defined(TARGET_ARCH_RISCV32) || defined(TARGET_ARCH_RISCV64) || \
|
||||||
|
defined(TARGET_ARCH_ARM)
|
||||||
len = strlen(hex_buffer);
|
len = strlen(hex_buffer);
|
||||||
if (remaining_ < len + 100) {
|
if (remaining_ < len + 100) {
|
||||||
*buffer_++ = '.';
|
*buffer_++ = '.';
|
||||||
|
|||||||
@@ -947,9 +947,9 @@ void ARMDecoder::DecodeType3(Instr* instr) {
|
|||||||
static_assert(kDivRnShift == kRmShift,
|
static_assert(kDivRnShift == kRmShift,
|
||||||
"div 'rn does not correspond to 'rm");
|
"div 'rn does not correspond to 'rm");
|
||||||
if (instr->IsDivUnsigned()) {
|
if (instr->IsDivUnsigned()) {
|
||||||
Format(instr, "udiv'cond 'rn, 'rs, 'rm");
|
Format(instr, "udiv'cond 'rn, 'rm, 'rs");
|
||||||
} else {
|
} else {
|
||||||
Format(instr, "sdiv'cond 'rn, 'rs, 'rm");
|
Format(instr, "sdiv'cond 'rn, 'rm, 'rs");
|
||||||
}
|
}
|
||||||
} else if (instr->IsRbit()) {
|
} else if (instr->IsRbit()) {
|
||||||
Format(instr, "rbit'cond 'rd, 'rm");
|
Format(instr, "rbit'cond 'rd, 'rm");
|
||||||
|
|||||||
+27
-63
@@ -15,10 +15,9 @@
|
|||||||
#include <libkern/OSCacheControl.h>
|
#include <libkern/OSCacheControl.h>
|
||||||
#elif defined(DART_HOST_OS_WINDOWS)
|
#elif defined(DART_HOST_OS_WINDOWS)
|
||||||
#include <processthreadsapi.h>
|
#include <processthreadsapi.h>
|
||||||
#endif
|
#elif defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID)
|
||||||
#if !defined(DART_HOST_OS_WINDOWS)
|
#include <asm/hwcap.h>
|
||||||
#include <string.h> /* NOLINT */
|
#include <sys/auxv.h>
|
||||||
#include <sys/utsname.h> /* NOLINT */
|
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -140,72 +139,37 @@ void HostCPUFeatures::Init() {
|
|||||||
}
|
}
|
||||||
#else // DART_HOST_OS_IOS
|
#else // DART_HOST_OS_IOS
|
||||||
void HostCPUFeatures::Init() {
|
void HostCPUFeatures::Init() {
|
||||||
bool is_arm64 = false;
|
// Reading /proc/cpuinfo under QEMU can report the host CPU instead of the
|
||||||
|
// emulated CPU.
|
||||||
|
unsigned long hwcap = getauxval(AT_HWCAP); // NOLINT
|
||||||
|
integer_division_supported_ = (hwcap & HWCAP_IDIVA) != 0;
|
||||||
|
neon_supported_ = (hwcap & HWCAP_NEON) != 0;
|
||||||
|
|
||||||
CpuInfo::Init();
|
CpuInfo::Init();
|
||||||
hardware_ = CpuInfo::GetCpuModel();
|
hardware_ = CpuInfo::GetCpuModel();
|
||||||
|
|
||||||
// QEMU may report host cpuinfo instead of emulated cpuinfo, use uname as a
|
// Qualcomm Krait CPUs (QCT APQ8064) in Nexus 4 and 7 incorrectly report that
|
||||||
// fallback for checking if CPU is AArch64 or ARMv7.
|
// they lack integer division.
|
||||||
struct utsname uname_;
|
if (CpuInfo::FieldContains(kCpuInfoHardware, "QCT APQ8064")) {
|
||||||
int ret_ = uname(&uname_);
|
integer_division_supported_ = true;
|
||||||
|
}
|
||||||
// Check for ARMv7, or aarch64.
|
// Marvell Armada 370/XP incorrectly reports that it has integer division.
|
||||||
// It can be in either the Processor or Model information fields.
|
if (CpuInfo::FieldContains(kCpuInfoHardware, "Marvell Armada 370/XP")) {
|
||||||
if (CpuInfo::FieldContains(kCpuInfoProcessor, "aarch64") ||
|
integer_division_supported_ = false;
|
||||||
CpuInfo::FieldContains(kCpuInfoModel, "aarch64") ||
|
}
|
||||||
CpuInfo::FieldContains(kCpuInfoArchitecture, "8") ||
|
// Some Android ARM emulators claim support for integer division but do not
|
||||||
CpuInfo::FieldContains(kCpuInfoArchitecture, "AArch64") ||
|
// actually support it.
|
||||||
(ret_ == 0 && (strstr(uname_.machine, "aarch64") != nullptr ||
|
if (CpuInfo::FieldContains(kCpuInfoHardware, "Dummy Virtual Machine")) {
|
||||||
strstr(uname_.machine, "arm64") != nullptr ||
|
integer_division_supported_ = false;
|
||||||
strstr(uname_.machine, "armv8") != nullptr))) {
|
|
||||||
// pretend that this arm64 cpu is really an ARMv7
|
|
||||||
is_arm64 = true;
|
|
||||||
} else if (!CpuInfo::FieldContains(kCpuInfoProcessor, "ARMv7") &&
|
|
||||||
!CpuInfo::FieldContains(kCpuInfoModel, "ARMv7") &&
|
|
||||||
!CpuInfo::FieldContains(kCpuInfoArchitecture, "7") &&
|
|
||||||
!(ret_ == 0 && strstr(uname_.machine, "armv7") != nullptr)) {
|
|
||||||
FATAL("Unrecognized ARM CPU architecture.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Has integer division.
|
// Allow flags to override feature detection.
|
||||||
// Special cases:
|
if (!FLAG_use_integer_division) {
|
||||||
// - Qualcomm Krait CPUs (QCT APQ8064) in Nexus 4 and 7 incorrectly report
|
|
||||||
// that they lack integer division.
|
|
||||||
// - Marvell Armada 370/XP incorrectly reports that it has integer division.
|
|
||||||
bool is_krait = CpuInfo::FieldContains(kCpuInfoHardware, "QCT APQ8064");
|
|
||||||
bool is_armada_370xp =
|
|
||||||
CpuInfo::FieldContains(kCpuInfoHardware, "Marvell Armada 370/XP");
|
|
||||||
bool is_virtual_machine =
|
|
||||||
CpuInfo::FieldContains(kCpuInfoHardware, "Dummy Virtual Machine");
|
|
||||||
#if defined(DART_HOST_OS_ANDROID)
|
|
||||||
bool is_android = true;
|
|
||||||
#else
|
|
||||||
bool is_android = false;
|
|
||||||
#endif
|
|
||||||
if (is_krait) {
|
|
||||||
integer_division_supported_ = FLAG_use_integer_division;
|
|
||||||
} else if (is_android && is_arm64) {
|
|
||||||
// Various Android ARM64 devices, including the Qualcomm Snapdragon 820/821
|
|
||||||
// CPUs (MSM 8996 and MSM8996pro) in Xiaomi MI5 and Pixel lack integer
|
|
||||||
// division even though ARMv8 requires it in A32. Instead of attempting to
|
|
||||||
// track all of these devices, we conservatively disable use of integer
|
|
||||||
// division on Android ARM64 devices.
|
|
||||||
// TODO(29270): /proc/self/auxv might be more reliable here.
|
|
||||||
integer_division_supported_ = false;
|
integer_division_supported_ = false;
|
||||||
} else if (is_armada_370xp) {
|
|
||||||
integer_division_supported_ = false;
|
|
||||||
} else if (is_android && !is_arm64 && is_virtual_machine) {
|
|
||||||
// Some Android ARM emulators claim support for integer division in
|
|
||||||
// /proc/cpuinfo but do not actually support it.
|
|
||||||
integer_division_supported_ = false;
|
|
||||||
} else {
|
|
||||||
integer_division_supported_ =
|
|
||||||
(CpuInfo::FieldContains(kCpuInfoFeatures, "idiva") || is_arm64) &&
|
|
||||||
FLAG_use_integer_division;
|
|
||||||
}
|
}
|
||||||
neon_supported_ =
|
if (!FLAG_use_neon) {
|
||||||
(CpuInfo::FieldContains(kCpuInfoFeatures, "neon") || is_arm64) &&
|
neon_supported_ = false;
|
||||||
FLAG_use_neon;
|
}
|
||||||
|
|
||||||
// Use the cross-compiler's predefined macros to determine whether we should
|
// Use the cross-compiler's predefined macros to determine whether we should
|
||||||
// use the hard or soft float ABI.
|
// use the hard or soft float ABI.
|
||||||
|
|||||||
Reference in New Issue
Block a user