| //===-- 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 ® : 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 ® : 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, ®.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. |
| } |