[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:
Ryan Macnak 2024-02-15 23:08:48 +00:00 committed by Commit Queue
parent 19aa44bd10
commit 688917045f
6 changed files with 57 additions and 69 deletions

View file

@ -419,7 +419,8 @@ void Assembler::EmitDivOp(Condition cond,
int32_t encoding = opcode | (static_cast<int32_t>(cond) << kConditionShift) |
(static_cast<int32_t>(rn) << kDivRnShift) |
(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);
}

View file

@ -85,6 +85,9 @@ enum {
B10 = 1 << 10,
B11 = 1 << 11,
B12 = 1 << 12,
B13 = 1 << 13,
B14 = 1 << 14,
B15 = 1 << 15,
B16 = 1 << 16,
B17 = 1 << 17,
B18 = 1 << 18,

View file

@ -65,6 +65,13 @@ TEST_CASE(ReciprocalOps) {
#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) {
__ mov(R0, Operand(42));
__ Ret();
@ -1746,6 +1753,12 @@ ASSEMBLER_TEST_RUN(Udiv, test) {
if (TargetCPUFeatures::integer_division_supported()) {
typedef int (*Tst)() DART_UNUSED;
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()) {
typedef int (*Tst)() DART_UNUSED;
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) {
EXPECT(test != nullptr);
if (TargetCPUFeatures::neon_supported()) {
typedef int (*Tst)() DART_UNUSED;
typedef float (*Tst)() DART_UNUSED;
float res = EXECUTE_TEST_CODE_FLOAT(Tst, test->entry());
EXPECT_EQ(true, signbit(res) && (res == 0.0));
}
@ -3474,7 +3493,7 @@ ASSEMBLER_TEST_GENERATE(Vmaxqs_zero, assembler) {
ASSEMBLER_TEST_RUN(Vmaxqs_zero, test) {
EXPECT(test != nullptr);
if (TargetCPUFeatures::neon_supported()) {
typedef int (*Tst)() DART_UNUSED;
typedef float (*Tst)() DART_UNUSED;
float res = EXECUTE_TEST_CODE_FLOAT(Tst, test->entry());
EXPECT_EQ(true, !signbit(res) && (res == 0.0));
}

View file

@ -75,7 +75,8 @@ void DisassembleToMemory::ConsumeInstruction(char* hex_buffer,
// TODO(compiler): Update assembler tests for other architectures so there is
// 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);
if (remaining_ < len + 100) {
*buffer_++ = '.';

View file

@ -947,9 +947,9 @@ void ARMDecoder::DecodeType3(Instr* instr) {
static_assert(kDivRnShift == kRmShift,
"div 'rn does not correspond to 'rm");
if (instr->IsDivUnsigned()) {
Format(instr, "udiv'cond 'rn, 'rs, 'rm");
Format(instr, "udiv'cond 'rn, 'rm, 'rs");
} else {
Format(instr, "sdiv'cond 'rn, 'rs, 'rm");
Format(instr, "sdiv'cond 'rn, 'rm, 'rs");
}
} else if (instr->IsRbit()) {
Format(instr, "rbit'cond 'rd, 'rm");

View file

@ -15,10 +15,9 @@
#include <libkern/OSCacheControl.h>
#elif defined(DART_HOST_OS_WINDOWS)
#include <processthreadsapi.h>
#endif
#if !defined(DART_HOST_OS_WINDOWS)
#include <string.h> /* NOLINT */
#include <sys/utsname.h> /* NOLINT */
#elif defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID)
#include <asm/hwcap.h>
#include <sys/auxv.h>
#endif
#endif
@ -140,72 +139,37 @@ void HostCPUFeatures::Init() {
}
#else // DART_HOST_OS_IOS
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();
hardware_ = CpuInfo::GetCpuModel();
// QEMU may report host cpuinfo instead of emulated cpuinfo, use uname as a
// fallback for checking if CPU is AArch64 or ARMv7.
struct utsname uname_;
int ret_ = uname(&uname_);
// Check for ARMv7, or aarch64.
// It can be in either the Processor or Model information fields.
if (CpuInfo::FieldContains(kCpuInfoProcessor, "aarch64") ||
CpuInfo::FieldContains(kCpuInfoModel, "aarch64") ||
CpuInfo::FieldContains(kCpuInfoArchitecture, "8") ||
CpuInfo::FieldContains(kCpuInfoArchitecture, "AArch64") ||
(ret_ == 0 && (strstr(uname_.machine, "aarch64") != nullptr ||
strstr(uname_.machine, "arm64") != nullptr ||
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.");
// Qualcomm Krait CPUs (QCT APQ8064) in Nexus 4 and 7 incorrectly report that
// they lack integer division.
if (CpuInfo::FieldContains(kCpuInfoHardware, "QCT APQ8064")) {
integer_division_supported_ = true;
}
// Marvell Armada 370/XP incorrectly reports that it has integer division.
if (CpuInfo::FieldContains(kCpuInfoHardware, "Marvell Armada 370/XP")) {
integer_division_supported_ = false;
}
// Some Android ARM emulators claim support for integer division but do not
// actually support it.
if (CpuInfo::FieldContains(kCpuInfoHardware, "Dummy Virtual Machine")) {
integer_division_supported_ = false;
}
// Has integer division.
// Special cases:
// - 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.
// Allow flags to override feature detection.
if (!FLAG_use_integer_division) {
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_ =
(CpuInfo::FieldContains(kCpuInfoFeatures, "neon") || is_arm64) &&
FLAG_use_neon;
if (!FLAG_use_neon) {
neon_supported_ = false;
}
// Use the cross-compiler's predefined macros to determine whether we should
// use the hard or soft float ABI.