blob: 330a24af67c4ebb1b44c2f4181f9c19103736c0f [file] [log] [blame]
//===-- RegisterFlagsDetector_arm64.cpp -----------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "RegisterFlagsDetector_arm64.h"
#include "lldb/lldb-private-types.h"
// This file is built on all systems because it is used by native processes and
// core files, so we manually define the needed HWCAP values here.
// These values are the same for Linux and FreeBSD.
#define HWCAP_FPHP (1ULL << 9)
#define HWCAP_ASIMDHP (1ULL << 10)
#define HWCAP_DIT (1ULL << 24)
#define HWCAP_SSBS (1ULL << 28)
#define HWCAP_GCS (1ULL << 32)
#define HWCAP2_BTI (1ULL << 17)
#define HWCAP2_MTE (1ULL << 18)
#define HWCAP2_AFP (1ULL << 20)
#define HWCAP2_SME (1ULL << 23)
#define HWCAP2_EBF16 (1ULL << 32)
#define HWCAP2_FPMR (1ULL << 48)
#define HWCAP3_MTE_STORE_ONLY (1ULL << 1)
using namespace lldb_private;
Arm64RegisterFlagsDetector::Fields
Arm64RegisterFlagsDetector::DetectFPMRFields(uint64_t hwcap, uint64_t hwcap2,
uint64_t hwcap3) {
(void)hwcap;
(void)hwcap3;
if (!(hwcap2 & HWCAP2_FPMR))
return {};
static const FieldEnum fp8_format_enum("fp8_format_enum", {
{0, "FP8_E5M2"},
{1, "FP8_E4M3"},
});
return {
{"LSCALE2", 32, 37},
{"NSCALE", 24, 31},
{"LSCALE", 16, 22},
{"OSC", 15},
{"OSM", 14},
{"F8D", 6, 8, &fp8_format_enum},
{"F8S2", 3, 5, &fp8_format_enum},
{"F8S1", 0, 2, &fp8_format_enum},
};
}
Arm64RegisterFlagsDetector::Fields
Arm64RegisterFlagsDetector::DetectGCSFeatureFields(uint64_t hwcap,
uint64_t hwcap2,
uint64_t hwcap3) {
(void)hwcap2;
(void)hwcap3;
if (!(hwcap & HWCAP_GCS))
return {};
return {
{"PUSH", 2},
{"WRITE", 1},
{"ENABLE", 0},
};
}
Arm64RegisterFlagsDetector::Fields
Arm64RegisterFlagsDetector::DetectSVCRFields(uint64_t hwcap, uint64_t hwcap2,
uint64_t hwcap3) {
(void)hwcap;
(void)hwcap3;
if (!(hwcap2 & HWCAP2_SME))
return {};
// Represents the pseudo register that lldb-server builds, which itself
// matches the architectural register SCVR. The fields match SVCR in the Arm
// manual.
return {
{"ZA", 1},
{"SM", 0},
};
}
Arm64RegisterFlagsDetector::Fields
Arm64RegisterFlagsDetector::DetectMTECtrlFields(uint64_t hwcap, uint64_t hwcap2,
uint64_t hwcap3) {
(void)hwcap;
if (!(hwcap2 & HWCAP2_MTE))
return {};
// Represents the contents of NT_ARM_TAGGED_ADDR_CTRL and the value passed
// to prctl(PR_TAGGED_ADDR_CTRL...). Fields are derived from the defines
// used to build the value.
std::vector<RegisterFlags::Field> fields;
fields.reserve(4);
if (hwcap3 & HWCAP3_MTE_STORE_ONLY)
fields.push_back({"STORE_ONLY", 19});
static const FieldEnum tcf_enum(
"tcf_enum",
{{0, "TCF_NONE"}, {1, "TCF_SYNC"}, {2, "TCF_ASYNC"}, {3, "TCF_ASYMM"}});
fields.insert(
std::end(fields),
{{"TAGS", 3, 18}, // 16 bit bitfield shifted up by PR_MTE_TAG_SHIFT.
{"TCF", 1, 2, &tcf_enum},
{"TAGGED_ADDR_ENABLE", 0}});
return fields;
}
Arm64RegisterFlagsDetector::Fields
Arm64RegisterFlagsDetector::DetectFPCRFields(uint64_t hwcap, uint64_t hwcap2,
uint64_t hwcap3) {
(void)hwcap3;
static const FieldEnum rmode_enum(
"rmode_enum", {{0, "RN"}, {1, "RP"}, {2, "RM"}, {3, "RZ"}});
std::vector<RegisterFlags::Field> fpcr_fields{
{"AHP", 26}, {"DN", 25}, {"FZ", 24}, {"RMode", 22, 23, &rmode_enum},
// Bits 21-20 are "Stride" which is unused in AArch64 state.
};
// FEAT_FP16 is indicated by the presence of FPHP (floating point half
// precision) and ASIMDHP (Advanced SIMD half precision) features.
if ((hwcap & HWCAP_FPHP) && (hwcap & HWCAP_ASIMDHP))
fpcr_fields.push_back({"FZ16", 19});
// Bits 18-16 are "Len" which is unused in AArch64 state.
fpcr_fields.push_back({"IDE", 15});
// Bit 14 is unused.
if (hwcap2 & HWCAP2_EBF16)
fpcr_fields.push_back({"EBF", 13});
fpcr_fields.push_back({"IXE", 12});
fpcr_fields.push_back({"UFE", 11});
fpcr_fields.push_back({"OFE", 10});
fpcr_fields.push_back({"DZE", 9});
fpcr_fields.push_back({"IOE", 8});
// Bits 7-3 reserved.
if (hwcap2 & HWCAP2_AFP) {
fpcr_fields.push_back({"NEP", 2});
fpcr_fields.push_back({"AH", 1});
fpcr_fields.push_back({"FIZ", 0});
}
return fpcr_fields;
}
Arm64RegisterFlagsDetector::Fields
Arm64RegisterFlagsDetector::DetectFPSRFields(uint64_t hwcap, uint64_t hwcap2,
uint64_t hwcap3) {
// fpsr's contents are constant.
(void)hwcap;
(void)hwcap2;
(void)hwcap3;
return {
// Bits 31-28 are N/Z/C/V, only used by AArch32.
{"QC", 27},
// Bits 26-8 reserved.
{"IDC", 7},
// Bits 6-5 reserved.
{"IXC", 4},
{"UFC", 3},
{"OFC", 2},
{"DZC", 1},
{"IOC", 0},
};
}
Arm64RegisterFlagsDetector::Fields
Arm64RegisterFlagsDetector::DetectCPSRFields(uint64_t hwcap, uint64_t hwcap2,
uint64_t hwcap3) {
(void)hwcap3;
// The fields here are a combination of the Arm manual's SPSR_EL1,
// plus a few changes where Linux has decided not to make use of them at all,
// or at least not from userspace.
// Status bits that are always present.
std::vector<RegisterFlags::Field> cpsr_fields{
{"N", 31}, {"Z", 30}, {"C", 29}, {"V", 28},
// Bits 27-26 reserved.
};
if (hwcap2 & HWCAP2_MTE)
cpsr_fields.push_back({"TCO", 25});
if (hwcap & HWCAP_DIT)
cpsr_fields.push_back({"DIT", 24});
// UAO and PAN are bits 23 and 22 and have no meaning for userspace so
// are treated as reserved by the kernels.
cpsr_fields.push_back({"SS", 21});
cpsr_fields.push_back({"IL", 20});
// Bits 19-14 reserved.
// Bit 13, ALLINT, requires FEAT_NMI that isn't relevant to userspace, and we
// can't detect either, don't show this field.
if (hwcap & HWCAP_SSBS)
cpsr_fields.push_back({"SSBS", 12});
if (hwcap2 & HWCAP2_BTI)
cpsr_fields.push_back({"BTYPE", 10, 11});
cpsr_fields.push_back({"D", 9});
cpsr_fields.push_back({"A", 8});
cpsr_fields.push_back({"I", 7});
cpsr_fields.push_back({"F", 6});
// Bit 5 reserved
// Called "M" in the ARMARM.
cpsr_fields.push_back({"nRW", 4});
// This is a 4 bit field M[3:0] in the ARMARM, we split it into parts.
cpsr_fields.push_back({"EL", 2, 3});
// Bit 1 is unused and expected to be 0.
cpsr_fields.push_back({"SP", 0});
return cpsr_fields;
}
void Arm64RegisterFlagsDetector::DetectFields(uint64_t hwcap, uint64_t hwcap2,
uint64_t hwcap3) {
for (auto &reg : m_registers)
reg.m_flags.SetFields(reg.m_detector(hwcap, hwcap2, hwcap3));
m_has_detected = true;
}
void Arm64RegisterFlagsDetector::UpdateRegisterInfo(
const RegisterInfo *reg_info, uint32_t num_regs) {
assert(m_has_detected &&
"Must call DetectFields before updating register info.");
// Register names will not be duplicated, so we do not want to compare against
// one if it has already been found. Each time we find one, we erase it from
// this list.
std::vector<std::pair<llvm::StringRef, const RegisterFlags *>>
search_registers;
for (const auto &reg : m_registers) {
// It is possible that a register is all extension dependent fields, and
// none of them are present.
if (reg.m_flags.GetFields().size())
search_registers.push_back({reg.m_name, &reg.m_flags});
}
// Walk register information while there are registers we know need
// to be updated. Example:
// Register information: [a, b, c, d]
// To be patched: [b, c]
// * a != b, a != c, do nothing and move on.
// * b == b, patch b, new patch list is [c], move on.
// * c == c, patch c, patch list is empty, exit early without looking at d.
for (uint32_t idx = 0; idx < num_regs && search_registers.size();
++idx, ++reg_info) {
auto reg_it = std::find_if(
search_registers.cbegin(), search_registers.cend(),
[reg_info](auto reg) { return reg.first == reg_info->name; });
if (reg_it != search_registers.end()) {
// Attach the field information.
reg_info->flags_type = reg_it->second;
// We do not expect to see this name again so don't look for it again.
search_registers.erase(reg_it);
}
}
// We do not assert that search_registers is empty here, because it may
// contain registers from optional extensions that are not present on the
// current target.
}