From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id xvjBGg4mDmlMwikAWB0awg (envelope-from ) for ; Fri, 07 Nov 2025 12:02:06 -0500 Authentication-Results: simark.ca; dkim=pass (2048-bit key; unprotected) header.d=syntacore.com header.i=@syntacore.com header.a=rsa-sha256 header.s=m header.b=M+fy8Jvs; dkim-atps=neutral Received: by simark.ca (Postfix, from userid 112) id 5A1B61E04C; Fri, 07 Nov 2025 12:02:06 -0500 (EST) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-25) on simark.ca X-Spam-Level: X-Spam-Status: No, score=-2.4 required=5.0 tests=ARC_SIGNED,ARC_VALID,BAYES_00, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI, RCVD_IN_DNSWL_MED,RCVD_IN_VALIDITY_CERTIFIED_BLOCKED, RCVD_IN_VALIDITY_RPBL_BLOCKED,RCVD_IN_VALIDITY_SAFE_BLOCKED autolearn=ham autolearn_force=no version=4.0.1 Received: from server2.sourceware.org (server2.sourceware.org [8.43.85.97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519 server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by simark.ca (Postfix) with ESMTPS id 7DCE01E04C for ; Fri, 07 Nov 2025 12:02:03 -0500 (EST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 1754A3858C52 for ; Fri, 7 Nov 2025 17:02:03 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 1754A3858C52 Authentication-Results: sourceware.org; dkim=pass (2048-bit key, unprotected) header.d=syntacore.com header.i=@syntacore.com header.a=rsa-sha256 header.s=m header.b=M+fy8Jvs Received: from m.syntacore.com (m.syntacore.com [178.249.69.228]) by sourceware.org (Postfix) with ESMTPS id 13CB03858C60 for ; Fri, 7 Nov 2025 16:55:51 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 13CB03858C60 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=syntacore.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=syntacore.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 13CB03858C60 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=178.249.69.228 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1762534551; cv=none; b=Sf3vRluYCacbdlls99sCbwsiorXQQwrW+7GNE+XC2QNRS3OMIPNsXdVdEMH3yZjlqKm/Hxbc06IaClBd4exWyjNXJP2IqHaBm3bWVQL5Jw+ELP9t16Af7Pzm5RSt0s4S9Uf8fLqbzqEI+LOTTXqBzlHiRbZbM+aYlePNBG6VJcU= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1762534551; c=relaxed/simple; bh=pYDOeitg4vr8m5aowG5sxKBnmiUkdObFChE6bSqCXGI=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=MommHoKQWhTTtD9qzSrQMZ+m0M5etZhgkTaJjRPJouOv3GHipEfihGH2vMmbLqeL69CzCL2Ll+AS4BBJNpoVw8iCes9vKHWeoB/I1VJbZlDncI2c7TcAYEg8awtS0ibOHPpC+17D1NDjVx7Q3BjAASmZM3RFJzh/tnaN6oWeOZY= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 13CB03858C60 Received: from MRN-SC-KSMG-01.corp.syntacore.com (localhost [127.0.0.1]) by m.syntacore.com (Postfix) with ESMTP id DA6221A0004 for ; Fri, 7 Nov 2025 16:55:48 +0000 (UTC) DKIM-Filter: OpenDKIM Filter v2.11.0 m.syntacore.com DA6221A0004 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=syntacore.com; s=m; t=1762534548; bh=wbKDGD/459QyrDmDTbShQtdcleoqdTji51aGW5X8BXw=; h=From:To:Subject:Date:Message-ID:MIME-Version:Content-Type:From; b=M+fy8JvsiyHsdFiOIr9s62Cy9wAyj6+RAEmsIr3rPclwH4oxn1xKz7AIM0wT+euTG 5GFq2BX1lOC6H+aFX9LvslPQL7wRGJnrPl574DjMRzNd6ERZfmRkiBG2BBsTVcVuZ7 SApm8soCBhPXXvY1X6yrk6cTO1dvpnKPePb7BtgxjqjZbHdqGmTjqrTVEMgSnrVq26 PsX+Q6hHT8y8JuCiDaPLjh2p/HV+HZ7NhKkvRUHr0t/F+33D0FjYJVlML2EPzi3OrN xZi1k0G4IQSZru+P744v2j9e11Y68nj5RxGL8AMTF7b//yceeU3IQ3Wly/VncZiMvG Ae9y1j/fp1lTw== Received: from S-SC-EXCH-01.corp.syntacore.com (exchange.syntacore.com [10.76.202.20]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by m.syntacore.com (Postfix) with ESMTPS for ; Fri, 7 Nov 2025 16:55:48 +0000 (UTC) Received: from ouran.high.school.host.club (10.178.157.72) by S-SC-EXCH-01.corp.syntacore.com (10.76.202.20) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.2562.29; Fri, 7 Nov 2025 19:55:44 +0300 From: Kirill Radkin To: CC: Kirill Radkin Subject: [PATCH 1/2] RISC-V Vector Extension Support Date: Fri, 7 Nov 2025 19:55:33 +0300 Message-ID: <20251107165534.1688124-1-kirill.radkin@syntacore.com> X-Mailer: git-send-email 2.43.0 MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Content-Type: text/plain X-Originating-IP: [10.178.157.72] X-ClientProxiedBy: S-SC-EXCH-01.corp.syntacore.com (10.76.202.20) To S-SC-EXCH-01.corp.syntacore.com (10.76.202.20) X-KSMG-AntiPhishing: not scanned, disabled by settings X-KSMG-AntiSpam-Interceptor-Info: not scanned X-KSMG-AntiSpam-Status: not scanned, disabled by settings X-KSMG-AntiVirus: Kaspersky Secure Mail Gateway, version 2.1.1.8310, bases: 2025/11/07 15:09:00 #27893311 X-KSMG-AntiVirus-Status: NotDetected, skipped X-KSMG-LinksScanning: NotDetected, bases: 2025/11/07 16:09:00 X-KSMG-Message-Action: skipped X-KSMG-Rule-ID: 5 X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gdb-patches-bounces~public-inbox=simark.ca@sourceware.org This patch adds support for the RISC-V Vector Extension (RVV) and the RISC-V Vector ABI to GDB, for both gdb and gdbserver (so, it's available for cross and native debugging). It is now possible to inspect and modify vector registers directly. Furthermore, GDB supports evaluating variables with RISC-V vector types (such as vint32m4_t) and calling functions that take vector arguments using the print and call GDB commands. Patch also includes tests targeting RVV support, tested on targets with different vlen (74, 128, 1024). Implementation is tested on system QEMU (Linux) and on OpenOCD + spike configuration. --- gdb/arch/riscv.c | 9 +- gdb/arch/riscv.h | 18 +- gdb/features/riscv/rvv.c | 113 ++++++ gdb/gdbtypes.c | 41 ++ gdb/nat/riscv-linux-ptrace.h | 31 ++ gdb/nat/riscv-linux-tdesc.c | 33 ++ gdb/riscv-linux-nat.c | 164 ++++++++ gdb/riscv-regs.h | 79 ++++ gdb/riscv-tdep.c | 758 ++++++++++++++++++++++++++++++----- gdb/riscv-tdep.h | 63 +-- gdbserver/linux-riscv-low.cc | 140 +++++-- gdbsupport/common-utils.h | 2 + include/elf/common.h | 1 + 13 files changed, 1255 insertions(+), 197 deletions(-) create mode 100644 gdb/features/riscv/rvv.c create mode 100644 gdb/nat/riscv-linux-ptrace.h create mode 100644 gdb/riscv-regs.h diff --git a/gdb/arch/riscv.c b/gdb/arch/riscv.c index c698fa2b62e..d5d41fe6382 100644 --- a/gdb/arch/riscv.c +++ b/gdb/arch/riscv.c @@ -22,6 +22,7 @@ #include "../features/riscv/64bit-cpu.c" #include "../features/riscv/32bit-fpu.c" #include "../features/riscv/64bit-fpu.c" +#include "../features/riscv/rvv.c" #include "../features/riscv/rv32e-xregs.c" #ifndef GDBSERVER @@ -82,11 +83,9 @@ riscv_create_target_description (const struct riscv_gdbarch_features features) else if (features.flen == 8) regnum = create_feature_riscv_64bit_fpu (tdesc.get (), regnum); - /* Currently GDB only supports vector features coming from remote - targets. We don't support creating vector features on native targets - (yet). */ - if (features.vlen != 0) - error (_("unable to create vector feature")); + if (features.vlenb != 0) + regnum = create_feature_riscv_rvv (tdesc.get (), features.vlenb, + features.xlen); return tdesc; } diff --git a/gdb/arch/riscv.h b/gdb/arch/riscv.h index bc95e72bb35..8b4e4b3c8cb 100644 --- a/gdb/arch/riscv.h +++ b/gdb/arch/riscv.h @@ -51,7 +51,7 @@ struct riscv_gdbarch_features target should be 16 and 4 for an embedded subset compliant target (with 'Zve32*' extension), but GDB doesn't currently mind, and will accept any vector size. */ - int vlen = 0; + int vlenb = 0; /* When true this target is RV32E. */ bool embedded = false; @@ -68,9 +68,8 @@ struct riscv_gdbarch_features /* Equality operator. */ bool operator== (const struct riscv_gdbarch_features &rhs) const { - return (xlen == rhs.xlen && flen == rhs.flen - && embedded == rhs.embedded && vlen == rhs.vlen - && has_fflags_reg == rhs.has_fflags_reg + return (xlen == rhs.xlen && flen == rhs.flen && embedded == rhs.embedded + && vlenb == rhs.vlenb && has_fflags_reg == rhs.has_fflags_reg && has_frm_reg == rhs.has_frm_reg && has_fcsr_reg == rhs.has_fcsr_reg); } @@ -84,13 +83,10 @@ struct riscv_gdbarch_features /* Used by std::unordered_map to hash feature sets. */ std::size_t hash () const noexcept { - std::size_t val = ((embedded ? 1 : 0) << 10 - | (has_fflags_reg ? 1 : 0) << 11 - | (has_frm_reg ? 1 : 0) << 12 - | (has_fcsr_reg ? 1 : 0) << 13 - | (xlen & 0x1f) << 5 - | (flen & 0x1f) << 0 - | (vlen & 0x3fff) << 14); + std::size_t val + = ((embedded ? 1 : 0) << 10 | (has_fflags_reg ? 1 : 0) << 11 + | (has_frm_reg ? 1 : 0) << 12 | (has_fcsr_reg ? 1 : 0) << 13 + | (xlen & 0x1f) << 5 | (flen & 0x1f) << 0 | (vlenb & 0x3fff) << 14); return val; } }; diff --git a/gdb/features/riscv/rvv.c b/gdb/features/riscv/rvv.c new file mode 100644 index 00000000000..a7a3adb5e65 --- /dev/null +++ b/gdb/features/riscv/rvv.c @@ -0,0 +1,113 @@ +/* Copyright (C) 2025 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#include "gdbsupport/gdb_assert.h" +#include "gdbsupport/tdesc.h" +#include "gdb/riscv-regs.h" +#include + +/* This file is NOT auto generated from xml. + 'create_feature_riscv_rvv' creates a RISCV Vector Extension feature. + 'vlenb' is a vector register size in bytes */ + +struct vector_field_type_info +{ + const char *element_type_name; + const char *vector_type_name; + int element_count; +}; + +static void +create_vector_register_type (tdesc_feature *feature, + std::vector &types_info, + const char *type_name) +{ + tdesc_type *element_type; + + for (auto &&type_info : types_info) + { + if (type_info.element_count == -1) + continue; + element_type = tdesc_named_type (feature, type_info.element_type_name); + tdesc_create_vector (feature, type_info.vector_type_name, element_type, + type_info.element_count); + } + + tdesc_type_with_fields *type_with_fields + = tdesc_create_union (feature, type_name); + + for (auto &&type_info : types_info) + { + if (type_info.element_count == -1) + continue; + element_type = tdesc_named_type (feature, type_info.vector_type_name); + tdesc_add_field (type_with_fields, type_info.vector_type_name, + element_type); + } +} + +static int +create_feature_riscv_rvv (target_desc *result, int vlenb, int xlen) +{ + gdb_assert (result); + gdb_assert (xlen == 4 || xlen == 8); + gdb_assert (vlenb >= 4 && ((vlenb & (vlenb - 1)) == 0)); + + int v_bitsize = 8 * vlenb; + int x_bitsize = 8 * xlen; + + tdesc_feature *csr_feature + = tdesc_create_feature (result, "org.gnu.gdb.riscv.csr"); + tdesc_create_reg (csr_feature, "vstart", RISCV_CSR_VSTART_REGNUM, 1, NULL, + x_bitsize, "int"); + tdesc_create_reg (csr_feature, "vcsr", RISCV_CSR_VCSR_REGNUM, 1, NULL, + x_bitsize, "int"); + tdesc_create_reg (csr_feature, "vl", RISCV_CSR_VL_REGNUM, 1, NULL, x_bitsize, + "int"); + tdesc_create_reg (csr_feature, "vtype", RISCV_CSR_VTYPE_REGNUM, 1, NULL, + x_bitsize, "int"); + tdesc_create_reg (csr_feature, "vlenb", RISCV_CSR_VLENB_REGNUM, 1, NULL, + x_bitsize, "int"); + + tdesc_feature *vector_feature + = tdesc_create_feature (result, "org.gnu.gdb.riscv.vector"); + + std::vector elements_types_info + = { { "int8", "i8", vlenb }, + { "int16", "i16", vlenb / 2 }, + { "int32", "i32", vlenb / 4 }, + { "int64", "i64", (vlenb >= 8) ? vlenb / 8 : -1 }, + { "ieee_half", "half", vlenb / 2 }, + { "ieee_single", "f32", vlenb / 4 }, + { "ieee_double", "f64", (vlenb >= 8) ? vlenb / 8 : -1 } }; + + create_vector_register_type (vector_feature, elements_types_info, "rvv"); + + int regnum = RISCV_V0_REGNUM; + + constexpr const char *vec_reg_names[] + = { "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7", + "v8", "v9", "v10", "v11", "v12", "v13", "v14", "v15", + "v16", "v17", "v18", "v19", "v20", "v21", "v22", "v23", + "v24", "v25", "v26", "v27", "v28", "v29", "v30", "v31" }; + + for (int i = 0; i < 32; ++i) + tdesc_create_reg (vector_feature, vec_reg_names[i], regnum++, 1, NULL, + v_bitsize, "rvv"); + + return regnum; +} diff --git a/gdb/gdbtypes.c b/gdb/gdbtypes.c index 63d6f4c0ceb..ba356fefdea 100644 --- a/gdb/gdbtypes.c +++ b/gdb/gdbtypes.c @@ -2151,6 +2151,21 @@ is_dynamic_type_internal (struct type *type, bool top_level) } } break; + + case TYPE_CODE_FUNC: + { + /* If the type of value returned by function is dynamic, we should mark + func as dynamic to later resolve this dynamic type */ + if (type->target_type () + && is_dynamic_type_internal (type->target_type (), false)) + return true; + + /* Same for function arguments */ + for (int i = 0; i < type->num_fields (); ++i) + if (is_dynamic_type_internal (type->field (i).type (), false)) + return true; + } + break; } return false; @@ -2873,6 +2888,25 @@ resolve_dynamic_struct (struct type *type, return resolved_type; } +/* Resolve dynamic function's arguments/returned value types */ +static struct type * +resolve_dynamic_func (struct type *type, const property_addr_info *addr_stack, + const frame_info_ptr &frame, bool top_level) +{ + gdb_assert (type->code () == TYPE_CODE_FUNC); + + struct type *resolved_type = copy_type (type); + + resolved_type->set_target_type (resolve_dynamic_type_internal ( + type->target_type (), addr_stack, frame, top_level)); + + for (int i = 0; i < resolved_type->num_fields (); i++) + resolved_type->field (i).set_type (resolve_dynamic_type_internal ( + resolved_type->field (i).type (), addr_stack, frame, top_level)); + + return resolved_type; +} + /* Worker for resolved_dynamic_type. */ static struct type * @@ -2971,6 +3005,13 @@ resolve_dynamic_type_internal (struct type *type, case TYPE_CODE_STRUCT: resolved_type = resolve_dynamic_struct (type, addr_stack, frame); break; + + case TYPE_CODE_FUNC: + /* If func was marked as dynamic, that means that it have dynamic + arguments or returned value*/ + resolved_type + = resolve_dynamic_func (type, addr_stack, frame, top_level); + break; } } diff --git a/gdb/nat/riscv-linux-ptrace.h b/gdb/nat/riscv-linux-ptrace.h new file mode 100644 index 00000000000..4ec67093936 --- /dev/null +++ b/gdb/nat/riscv-linux-ptrace.h @@ -0,0 +1,31 @@ +/* Copyright (C) 2025 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifndef NAT_RISCV_LINUX_HW_PTRACE_H +#define NAT_RISCV_LINUX_HW_PTRACE_H + +struct __riscv_v_regset_state +{ + unsigned long vstart; + unsigned long vl; + unsigned long vtype; + unsigned long vcsr; + unsigned long vlenb; + char vreg[]; +}; + +#endif // NAT_RISCV_LINUX_HW_PTRACE_H diff --git a/gdb/nat/riscv-linux-tdesc.c b/gdb/nat/riscv-linux-tdesc.c index 254a25ccefe..e7d6d29eea1 100644 --- a/gdb/nat/riscv-linux-tdesc.c +++ b/gdb/nat/riscv-linux-tdesc.c @@ -30,6 +30,27 @@ # define NFPREG 33 #endif +// RISC-V Hardware Probing Syscall Number +#ifndef NR_riscv_hwprobe +#ifndef NR_arch_specific_syscall +#define NR_arch_specific_syscall 244 +#endif +#define NR_riscv_hwprobe (NR_arch_specific_syscall + 14) +#endif + +#ifndef RISCV_HWPROBE_KEY_IMA_EXT_0 +// A bitmask containing the supported extensions +#define RISCV_HWPROBE_KEY_IMA_EXT_0 4 +// Bit that indicate RISC-V Vector extension support +#define RISCV_HWPROBE_IMA_V (1 << 2) +#endif + +struct riscv_hwprobe +{ + int64_t key; + uint64_t value; +}; + /* See nat/riscv-linux-tdesc.h. */ struct riscv_gdbarch_features @@ -78,5 +99,17 @@ riscv_linux_read_features (int tid) break; } + features.vlenb = 0; + + static struct riscv_hwprobe query[] = { { RISCV_HWPROBE_KEY_IMA_EXT_0, 0 } }; + + if ((syscall (NR_riscv_hwprobe, query, 1, 0, NULL, 0) == 0) + && (query[0].value & RISCV_HWPROBE_IMA_V)) + { + int reg = 0; + asm volatile ("csrr %[vlenb], vlenb" : [vlenb] "=r"(reg)); + features.vlenb = reg; + } + return features; } diff --git a/gdb/riscv-linux-nat.c b/gdb/riscv-linux-nat.c index 89f1ddc1b17..459654565a3 100644 --- a/gdb/riscv-linux-nat.c +++ b/gdb/riscv-linux-nat.c @@ -24,10 +24,13 @@ #include "elf/common.h" +#include "nat/riscv-linux-ptrace.h" #include "nat/riscv-linux-tdesc.h" #include +#include + /* Work around glibc header breakage causing ELF_NFPREG not to be usable. */ #ifndef NFPREG # define NFPREG 33 @@ -132,6 +135,63 @@ supply_fpregset (struct regcache *regcache, const prfpregset_t *fpregs) supply_fpregset_regnum (regcache, fpregs, -1); } +static void +supply_vecregset_regnum (regcache *regcache, + const __riscv_v_regset_state *vecregs, int regnum) +{ + gdb_assert (vecregs->vlenb > 0); + if ((regnum >= RISCV_V0_REGNUM && regnum <= RISCV_V31_REGNUM)) + { + regcache->raw_supply ( + regnum, vecregs->vreg + vecregs->vlenb * (regnum - RISCV_V0_REGNUM)); + return; + } + + if (regnum == RISCV_CSR_VSTART_REGNUM) + { + regcache->raw_supply (regnum, &vecregs->vstart); + return; + } + + if (regnum == RISCV_CSR_VCSR_REGNUM) + { + regcache->raw_supply (regnum, &vecregs->vcsr); + return; + } + + if (regnum == RISCV_CSR_VL_REGNUM) + { + regcache->raw_supply (regnum, &vecregs->vl); + return; + } + + if (regnum == RISCV_CSR_VTYPE_REGNUM) + { + regcache->raw_supply (regnum, &vecregs->vtype); + return; + } + + if (regnum == RISCV_CSR_VLENB_REGNUM) + { + regcache->raw_supply (regnum, &vecregs->vlenb); + return; + } + + if (regnum == -1) + { + regcache->raw_supply (RISCV_CSR_VSTART_REGNUM, &vecregs->vstart); + regcache->raw_supply (RISCV_CSR_VCSR_REGNUM, &vecregs->vcsr); + regcache->raw_supply (RISCV_CSR_VL_REGNUM, &vecregs->vl); + regcache->raw_supply (RISCV_CSR_VTYPE_REGNUM, &vecregs->vtype); + regcache->raw_supply (RISCV_CSR_VLENB_REGNUM, &vecregs->vlenb); + + for (int i = RISCV_V0_REGNUM; i <= RISCV_V31_REGNUM; i++) + regcache->raw_supply (i, vecregs->vreg + + vecregs->vlenb * (i - RISCV_V0_REGNUM)); + return; + } +} + /* Copy general purpose register REGNUM (or all gp regs if REGNUM == -1) from REGCACHE into regset GREGS. */ @@ -195,6 +255,63 @@ fill_fpregset (const struct regcache *regcache, prfpregset_t *fpregs, } } +static void +fill_vecregset_regnum (regcache *regcache, __riscv_v_regset_state *vecregs, + int regnum) +{ + if ((regnum >= RISCV_V0_REGNUM && regnum <= RISCV_V31_REGNUM)) + { + regcache->raw_collect ( + regnum, vecregs->vreg + vecregs->vlenb * (regnum - RISCV_V0_REGNUM)); + return; + } + + if (regnum == RISCV_CSR_VSTART_REGNUM) + { + regcache->raw_collect (regnum, &vecregs->vstart); + return; + } + + if (regnum == RISCV_CSR_VCSR_REGNUM) + { + regcache->raw_collect (regnum, &vecregs->vcsr); + return; + } + + if (regnum == RISCV_CSR_VL_REGNUM) + { + regcache->raw_collect (regnum, &vecregs->vl); + return; + } + + if (regnum == RISCV_CSR_VTYPE_REGNUM) + { + regcache->raw_collect (regnum, &vecregs->vtype); + return; + } + + if (regnum == RISCV_CSR_VLENB_REGNUM) + { + regcache->raw_collect (regnum, &vecregs->vlenb); + return; + } + + if (regnum == -1) + { + regcache->raw_collect (RISCV_CSR_VSTART_REGNUM, &vecregs->vstart); + regcache->raw_collect (RISCV_CSR_VCSR_REGNUM, &vecregs->vcsr); + regcache->raw_collect (RISCV_CSR_VL_REGNUM, &vecregs->vl); + regcache->raw_collect (RISCV_CSR_VTYPE_REGNUM, &vecregs->vtype); + regcache->raw_collect (RISCV_CSR_VLENB_REGNUM, &vecregs->vlenb); + + for (int i = RISCV_V0_REGNUM; i <= RISCV_V31_REGNUM; i++) + regcache->raw_collect ( + i, vecregs->vreg + vecregs->vlenb * (i - RISCV_V0_REGNUM)); + + return; + } +} + /* Return a target description for the current target. */ const struct target_desc * @@ -261,6 +378,29 @@ riscv_linux_nat_target::fetch_registers (struct regcache *regcache, int regnum) regcache->raw_supply_zeroed (RISCV_CSR_MISA_REGNUM); } + if (riscv_is_vpr_or_vcsr (regnum) || (regnum == -1)) + { + int vecreg_size = register_size (regcache->arch (), RISCV_V0_REGNUM); + std::vector vregs_buff (sizeof (__riscv_v_regset_state) + + vecreg_size * 32); + + __riscv_v_regset_state *vregs_state + = (__riscv_v_regset_state *)vregs_buff.data (); + + iovec iov; + iov.iov_base = vregs_state; + iov.iov_len = sizeof (struct __riscv_v_regset_state) + 32 * vecreg_size; + + if (ptrace (PTRACE_GETREGSET, tid, NT_RISCV_VECTOR, + (PTRACE_TYPE_ARG3)&iov) + == 0 + && vregs_state->vlenb > 0) + { + gdb_assert (vregs_state->vlenb == vecreg_size); + supply_vecregset_regnum (regcache, vregs_state, regnum); + } + } + /* Access to other CSRs has potential security issues, don't support them for now. */ } @@ -323,6 +463,30 @@ riscv_linux_nat_target::store_registers (struct regcache *regcache, int regnum) } } + if (riscv_is_vpr_or_vcsr (regnum) || (regnum == -1)) + { + int vecreg_size = register_size (regcache->arch (), RISCV_V0_REGNUM); + std::vector vregs_buff (sizeof (__riscv_v_regset_state) + + vecreg_size * 32); + + __riscv_v_regset_state *vregs_state + = (__riscv_v_regset_state *)vregs_buff.data (); + + iovec iov; + iov.iov_base = vregs_state; + iov.iov_len = sizeof (struct __riscv_v_regset_state) + 32 * vecreg_size; + + if (ptrace (PTRACE_GETREGSET, tid, NT_RISCV_VECTOR, + (PTRACE_TYPE_ARG3)&iov) + == 0) + { + fill_vecregset_regnum (regcache, vregs_state, regnum); + + ptrace (PTRACE_SETREGSET, tid, NT_RISCV_VECTOR, + (PTRACE_TYPE_ARG3)&iov); + } + } + /* Access to CSRs has potential security issues, don't support them for now. */ } diff --git a/gdb/riscv-regs.h b/gdb/riscv-regs.h new file mode 100644 index 00000000000..e6b06445e6e --- /dev/null +++ b/gdb/riscv-regs.h @@ -0,0 +1,79 @@ +/* Target-dependent header for the RISC-V architecture, for GDB, the + GNU Debugger. + + Copyright (C) 2025 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifndef GDB_RISCV_REGS_H +#define GDB_RISCV_REGS_H + +/* RiscV register numbers. */ +enum +{ + RISCV_ZERO_REGNUM = 0, /* Read-only register, always 0. */ + RISCV_RA_REGNUM = 1, /* Return Address. */ + RISCV_SP_REGNUM = 2, /* Stack Pointer. */ + RISCV_GP_REGNUM = 3, /* Global Pointer. */ + RISCV_TP_REGNUM = 4, /* Thread Pointer. */ + RISCV_FP_REGNUM = 8, /* Frame Pointer. */ + RISCV_A0_REGNUM = 10, /* First argument. */ + RISCV_A1_REGNUM = 11, /* Second argument. */ + RISCV_A2_REGNUM = 12, /* Third argument. */ + RISCV_A3_REGNUM = 13, /* Forth argument. */ + RISCV_A4_REGNUM = 14, /* Fifth argument. */ + RISCV_A5_REGNUM = 15, /* Sixth argument. */ + RISCV_A7_REGNUM = 17, /* Register to pass syscall number. */ + RISCV_PC_REGNUM = 32, /* Program Counter. */ + + RISCV_NUM_INTEGER_REGS = 32, + + RISCV_FIRST_FP_REGNUM = 33, /* First Floating Point Register */ + RISCV_FA0_REGNUM = 43, + RISCV_FA1_REGNUM = RISCV_FA0_REGNUM + 1, + RISCV_LAST_FP_REGNUM = 64, /* Last Floating Point Register */ + + RISCV_FIRST_CSR_REGNUM = 65, /* First CSR */ +#define DECLARE_CSR(name, num, class, define_version, abort_version) \ + RISCV_ ## num ## _REGNUM = RISCV_FIRST_CSR_REGNUM + num, +#include "opcode/riscv-opc.h" +#undef DECLARE_CSR + RISCV_LAST_CSR_REGNUM = 4160, + RISCV_CSR_LEGACY_MISA_REGNUM = 0xf10 + RISCV_FIRST_CSR_REGNUM, + + RISCV_PRIV_REGNUM = 4161, + + RISCV_V0_REGNUM, + + RISCV_V31_REGNUM = RISCV_V0_REGNUM + 31, + + RISCV_LAST_REGNUM = RISCV_V31_REGNUM +}; + +/* RiscV DWARF register numbers. */ +enum +{ + RISCV_DWARF_REGNUM_X0 = 0, + RISCV_DWARF_REGNUM_X31 = 31, + RISCV_DWARF_REGNUM_F0 = 32, + RISCV_DWARF_REGNUM_F31 = 63, + RISCV_DWARF_REGNUM_V0 = 96, + RISCV_DWARF_REGNUM_V31 = 127, + RISCV_DWARF_FIRST_CSR = 4096, + RISCV_DWARF_LAST_CSR = 8191, +}; + +#endif /* GDB_RISCV_REGS_H */ diff --git a/gdb/riscv-tdep.c b/gdb/riscv-tdep.c index 76d10a3b298..03587e14418 100644 --- a/gdb/riscv-tdep.c +++ b/gdb/riscv-tdep.c @@ -17,6 +17,8 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ +#include + #include "extract-store-integer.h" #include "frame.h" #include "inferior.h" @@ -57,6 +59,13 @@ #include "record-full.h" #include "riscv-ravenscar-thread.h" +#include +#include +#include +#include +#include +#include +#include #include /* The stack must be 16-byte aligned. */ @@ -619,39 +628,23 @@ struct riscv_vector_feature : public riscv_register_feature riscv_vector_feature () : riscv_register_feature (riscv_feature_name_vector) { - m_registers = { - { RISCV_V0_REGNUM + 0, { "v0" } }, - { RISCV_V0_REGNUM + 1, { "v1" } }, - { RISCV_V0_REGNUM + 2, { "v2" } }, - { RISCV_V0_REGNUM + 3, { "v3" } }, - { RISCV_V0_REGNUM + 4, { "v4" } }, - { RISCV_V0_REGNUM + 5, { "v5" } }, - { RISCV_V0_REGNUM + 6, { "v6" } }, - { RISCV_V0_REGNUM + 7, { "v7" } }, - { RISCV_V0_REGNUM + 8, { "v8" } }, - { RISCV_V0_REGNUM + 9, { "v9" } }, - { RISCV_V0_REGNUM + 10, { "v10" } }, - { RISCV_V0_REGNUM + 11, { "v11" } }, - { RISCV_V0_REGNUM + 12, { "v12" } }, - { RISCV_V0_REGNUM + 13, { "v13" } }, - { RISCV_V0_REGNUM + 14, { "v14" } }, - { RISCV_V0_REGNUM + 15, { "v15" } }, - { RISCV_V0_REGNUM + 16, { "v16" } }, - { RISCV_V0_REGNUM + 17, { "v17" } }, - { RISCV_V0_REGNUM + 18, { "v18" } }, - { RISCV_V0_REGNUM + 19, { "v19" } }, - { RISCV_V0_REGNUM + 20, { "v20" } }, - { RISCV_V0_REGNUM + 21, { "v21" } }, - { RISCV_V0_REGNUM + 22, { "v22" } }, - { RISCV_V0_REGNUM + 23, { "v23" } }, - { RISCV_V0_REGNUM + 24, { "v24" } }, - { RISCV_V0_REGNUM + 25, { "v25" } }, - { RISCV_V0_REGNUM + 26, { "v26" } }, - { RISCV_V0_REGNUM + 27, { "v27" } }, - { RISCV_V0_REGNUM + 28, { "v28" } }, - { RISCV_V0_REGNUM + 29, { "v29" } }, - { RISCV_V0_REGNUM + 30, { "v30" } }, - { RISCV_V0_REGNUM + 31, { "v31" } }, + m_registers = { + { RISCV_V0_REGNUM + 0, { "v0" } }, { RISCV_V0_REGNUM + 1, { "v1" } }, + { RISCV_V0_REGNUM + 2, { "v2" } }, { RISCV_V0_REGNUM + 3, { "v3" } }, + { RISCV_V0_REGNUM + 4, { "v4" } }, { RISCV_V0_REGNUM + 5, { "v5" } }, + { RISCV_V0_REGNUM + 6, { "v6" } }, { RISCV_V0_REGNUM + 7, { "v7" } }, + { RISCV_V0_REGNUM + 8, { "v8" } }, { RISCV_V0_REGNUM + 9, { "v9" } }, + { RISCV_V0_REGNUM + 10, { "v10" } }, { RISCV_V0_REGNUM + 11, { "v11" } }, + { RISCV_V0_REGNUM + 12, { "v12" } }, { RISCV_V0_REGNUM + 13, { "v13" } }, + { RISCV_V0_REGNUM + 14, { "v14" } }, { RISCV_V0_REGNUM + 15, { "v15" } }, + { RISCV_V0_REGNUM + 16, { "v16" } }, { RISCV_V0_REGNUM + 17, { "v17" } }, + { RISCV_V0_REGNUM + 18, { "v18" } }, { RISCV_V0_REGNUM + 19, { "v19" } }, + { RISCV_V0_REGNUM + 20, { "v20" } }, { RISCV_V0_REGNUM + 21, { "v21" } }, + { RISCV_V0_REGNUM + 22, { "v22" } }, { RISCV_V0_REGNUM + 23, { "v23" } }, + { RISCV_V0_REGNUM + 24, { "v24" } }, { RISCV_V0_REGNUM + 25, { "v25" } }, + { RISCV_V0_REGNUM + 26, { "v26" } }, { RISCV_V0_REGNUM + 27, { "v27" } }, + { RISCV_V0_REGNUM + 28, { "v28" } }, { RISCV_V0_REGNUM + 29, { "v29" } }, + { RISCV_V0_REGNUM + 30, { "v30" } }, { RISCV_V0_REGNUM + 31, { "v31" } }, }; } @@ -660,10 +653,14 @@ struct riscv_vector_feature : public riscv_register_feature RISCV_V0_REGNUM + 31. */ const char *register_name (int regnum) const { - gdb_assert (regnum >= RISCV_V0_REGNUM - && regnum <= RISCV_V0_REGNUM + 31); - regnum -= RISCV_V0_REGNUM; - return m_registers[regnum].names[0]; + gdb_assert (regnum >= RISCV_V0_REGNUM && regnum <= RISCV_V0_REGNUM + 31); + + const auto reg_info_it = std::find_if ( + m_registers.begin (), m_registers.end (), + [regnum] (const auto ®_info) { return reg_info.regnum == regnum; }); + if (reg_info_it == m_registers.end ()) + gdb_assert_not_reached ("Incorrect vector register number %d", regnum); + return reg_info_it->names.front (); } /* Check this feature within TDESC, record the registers from this @@ -679,7 +676,7 @@ struct riscv_vector_feature : public riscv_register_feature feature set and return. */ if (feature_vector == nullptr) { - features->vlen = 0; + features->vlenb = 0; return true; } @@ -696,6 +693,9 @@ struct riscv_vector_feature : public riscv_register_feature int vector_bitsize = -1; for (const auto ® : m_registers) { + if (reg.regnum < RISCV_V0_REGNUM || reg.regnum > RISCV_V31_REGNUM) + continue; + int reg_bitsize = -1; for (const char *name : reg.names) { @@ -712,7 +712,7 @@ struct riscv_vector_feature : public riscv_register_feature return false; } - features->vlen = (vector_bitsize / 8); + features->vlenb = (vector_bitsize / 8); return true; } }; @@ -793,6 +793,25 @@ riscv_abi_xlen (struct gdbarch *gdbarch) /* See riscv-tdep.h. */ +int +riscv_isa_vlenb (struct gdbarch *gdbarch) +{ + riscv_gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + return tdep->isa_features.vlenb; +} + +/* Return true if GDBARCH is using vector hardware ABI. */ + +static bool +riscv_has_vector_abi (struct gdbarch *gdbarch) +{ + gdb_assert (gdbarch); + riscv_gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + return tdep->abi_features.vlenb > 0; +} + +/* See riscv-tdep.h. */ + int riscv_isa_flen (struct gdbarch *gdbarch) { @@ -1211,7 +1230,7 @@ riscv_print_one_register_info (struct gdbarch *gdbarch, struct value_print_options opts; riscv_gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); - /* Print the register in hex. */ + /* Print the register in hex, exclude vector registers. */ get_formatted_print_options (&opts, 'x'); opts.deref_ref = true; common_val_print (val, file, 0, &opts, current_language); @@ -1339,17 +1358,45 @@ riscv_print_one_register_info (struct gdbarch *gdbarch, else gdb_printf (file, "\tprv:%d [INVALID]", priv); } - else + else if (regnum == RISCV_CSR_VTYPE_REGNUM) { - /* If not a vector register, print it also according to its - natural format. */ - if (regtype->is_vector () == 0) - { - get_user_print_options (&opts); - opts.deref_ref = true; - gdb_printf (file, "\t"); - common_val_print (val, file, 0, &opts, current_language); - } + LONGEST d = value_as_long (val); + // Values are decoded according to RISC-V Vector Specification + unsigned lmul_val = d & 0x7; + const char *decoded_lmul_variants[] + = { "1", "2", "4", "8", "Reserved", "1/8", "1/4", "1/2" }; + unsigned sew_val = (d >> 3) & 0x7; + const char *decoded_sew_variants[] + = { "e8", "e16", "e32", "e64", + "Reserved", "Reserved", "Reserved", "Reserved" }; + unsigned vta = (d >> 6) & 0x1; + const char *decoded_vta_variants[] = { "tu", "ta" }; + unsigned vma = (d >> 7) & 0x1; + const char *decoded_vma_variants[] = { "mu", "ma" }; + int size = register_size (gdbarch, regnum); + unsigned xlen = size * 8; + unsigned vill = (d >> (xlen - 1)) & 0x1; + gdb_printf (file, + "\tLMUL:%u (%s) SEW:%u (%s) vta:%u (%s) vma:%u " + "(%s) vill:%u", + lmul_val, decoded_lmul_variants[lmul_val], sew_val, + decoded_sew_variants[sew_val], vta, + decoded_vta_variants[vta], vma, + decoded_vma_variants[vma], vill); + } + else if (regnum == RISCV_CSR_VCSR_REGNUM) + { + LONGEST d = value_as_long (val); + unsigned vxsat = d & 1; + unsigned vxrm = (d >> 1) & 0b11; + gdb_printf (file, "\tVXSAT:%u VXRM:%u", vxsat, vxrm); + } + else if (regtype->is_vector () == 0) + { + get_user_print_options (&opts); + opts.deref_ref = true; + gdb_printf (file, "\t"); + common_val_print (val, file, 0, &opts, current_language); } } } @@ -1469,7 +1516,7 @@ riscv_register_reggroup_p (struct gdbarch *gdbarch, int regnum, return 0; } else if (reggroup == vector_reggroup) - return (regnum >= RISCV_V0_REGNUM && regnum <= RISCV_V31_REGNUM); + return riscv_is_vpr_or_vcsr (regnum); else return 0; } @@ -2654,18 +2701,25 @@ struct riscv_arg_info { /* What type of location this is. */ enum location_type - { - /* Argument passed in a register. */ - in_reg, + { + /* Argument passed in a register. */ + in_reg, - /* Argument passed as an on stack argument. */ - on_stack, + /* Argument passed in several regs. (For now, used for Vector registers.) + The first register in the sequence of registers (used for passing + arguments) is passed through the first location (see the comment above + struct location), and the last one is passed through the second + location. */ + in_several_regs, - /* Argument passed by reference. The second location is always - valid for a BY_REF argument, and describes where the address - of the BY_REF argument should be placed. */ - by_ref - } loc_type; + /* Argument passed as an on stack argument. */ + on_stack, + + /* Argument passed by reference. The second location is always + valid for a BY_REF argument, and describes where the address + of the BY_REF argument should be placed. */ + by_ref + } loc_type; /* Information that depends on the location type. */ union @@ -2714,13 +2768,98 @@ struct riscv_arg_reg /* Nothing. */ } - /* The GDB register number to use in this set. */ + /* The GDB register number to use in this set. */ int next_regnum; - /* The last GDB register number to use in this set. */ + /* The last GDB register number to use in this set. */ int last_regnum; }; +struct riscv_vector_arg_reg_interval +{ + int first; + int last; +}; + +struct riscv_vector_arg_reg +{ + riscv_vector_arg_reg (int first, int last) + : available_intervals{ { first, last } } + { + /* Nothing. */ + } + + int + get_interval_start (int count, int NFIELDS = 1) + { + gdb_assert (count % NFIELDS == 0); + + int LMUL = count / NFIELDS; + for (auto cur_interval = available_intervals.begin (); + cur_interval != available_intervals.end (); ++cur_interval) + { + int cur_first = cur_interval->first; + int cur_last = cur_interval->last; + if ((cur_first - RISCV_V0_REGNUM) % LMUL + != 0) /* According to RISC-V Vector ABI, + first register number should be + a multiple of LMUL */ + { + cur_first -= (cur_first - RISCV_V0_REGNUM) % LMUL; + cur_first += LMUL; + } + + if ((cur_last - cur_first + 1) >= count) + { + if (cur_first > cur_interval->first) + { + available_intervals.insert ( + cur_interval, { cur_interval->first, cur_first - 1 }); + } + + cur_interval->first = cur_first + count; + + return cur_first; + } + } + + return -1; + } + + int + try_use_v0 () + { + if (is_v0_used) + return get_interval_start (1); + else + { + is_v0_used = true; + return RISCV_V0_REGNUM; + } + } + + bool is_v0_used = false; + + std::list available_intervals; + + /* From RISCV-ABI, Standard Vector Calling Convention Variant: + The rules for passing vector arguments are as follows: + 1. For the first vector mask argument, use v0 to pass it. + 2. For vector data arguments or rest vector mask arguments, starting + from the v8 register, if a vector register group between v8-v23 that has + not been allocated can be found and the first register number is a + multiple of LMUL, then allocate this vector register group to the argument + and mark these registers as allocated. Otherwise, pass it by reference and + are replaced in the 12 argument list with the address. + 3. For tuple vector data arguments, starting from the v8 register, if + NFIELDS consecutive vector register groups between v8-v23 that have not + been allocated can be found and the first register number is a multiple of + LMUL, then allocate these vector register groups to the argument and mark + these registers as allocated. Otherwise, pass it by reference and are + replaced in the argument list with the address + */ +}; + /* Arguments can be passed as on stack arguments, or by reference. The on stack arguments must be in a continuous region starting from $sp, while the by reference arguments can be anywhere, but we'll put them @@ -2757,11 +2896,17 @@ struct riscv_memory_offsets struct riscv_call_info { riscv_call_info (struct gdbarch *gdbarch) - : int_regs (RISCV_A0_REGNUM, RISCV_A0_REGNUM + 7), - float_regs (RISCV_FA0_REGNUM, RISCV_FA0_REGNUM + 7) + : int_regs (RISCV_A0_REGNUM, RISCV_A0_REGNUM + 7), + float_regs (RISCV_FA0_REGNUM, RISCV_FA0_REGNUM + 7), + vector_regs (RISCV_V0_REGNUM + 8, RISCV_V0_REGNUM + 23) { xlen = riscv_abi_xlen (gdbarch); flen = riscv_abi_flen (gdbarch); + /* According to the RVV specification, a binary file does not require + any particular vlenb value. However, a target vlenb value is + required to place arguments correctly. For this purpose, we save + the riscv_isa_vlenb value here. */ + vlenb = riscv_isa_vlenb (gdbarch); /* Reduce the number of integer argument registers when using the embedded abi (i.e. rv32e). */ @@ -2771,6 +2916,10 @@ struct riscv_call_info /* Disable use of floating point registers if needed. */ if (!riscv_has_fp_abi (gdbarch)) float_regs.next_regnum = float_regs.last_regnum + 1; + + /* Disable use of vector registers if needed. */ + if (!riscv_has_vector_abi (gdbarch)) + vector_regs.available_intervals.clear (); } /* Track the memory areas used for holding in-memory arguments to a @@ -2785,10 +2934,15 @@ struct riscv_call_info passing an argument. */ struct riscv_arg_reg float_regs; + /* Holds information about the next vector register to use for + passing an argument. */ + struct riscv_vector_arg_reg vector_regs; + /* The XLEN and FLEN are copied in to this structure for convenience, and are just the results of calling RISCV_ABI_XLEN and RISCV_ABI_FLEN. */ int xlen; int flen; + int vlenb; }; /* Return the number of registers available for use as parameters in the @@ -2830,6 +2984,291 @@ riscv_assign_reg_location (struct riscv_arg_info::location *loc, return false; } +struct rvv_type_info +{ + /* Selected Element Width. For RVV_BOOL types, this field holds EEW/EMUL + value, encoded into the type. */ + unsigned sew = 0; + + /* Length Multiplier, amount of registers used for this type. */ + unsigned lmul = 0; + + /* For non-tuple types, nfield = 1. */ + unsigned nfield = 0; + + enum class rvv_elem_type : char + { + RVV_INT, + RVV_UINT, + RVV_FLOAT, + RVV_BOOL, + RVV_UNKNOWN, + } element_type + = rvv_elem_type::RVV_UNKNOWN; + + /* is_fractional_lmul means, that used only part of a vector + register. For example, if lmul = 2 and is_fractional_lmul = + true it means that used a half of a register. */ + bool is_fractional_lmul = false; +}; + +/* verify_rvv_type() only checks those unavailable configurations. + Unavailable configurations: + 1) lmul == 1 && is_fractional_lmul == true + 2) RVV_FLOAT && sew == 8 + 3) (sew == 1,2,4) && !RVV_BOOL + 4) (lmul != 1 || nfield != 1) && RVV_BOOL */ +static bool +verify_rvv_type (rvv_type_info type_info) +{ + if (type_info.lmul == 1 && type_info.is_fractional_lmul) + return false; + + switch (type_info.element_type) + { + case rvv_type_info::rvv_elem_type::RVV_FLOAT: + if (type_info.sew == 8) + return false; + [[fallthrough]]; + + case rvv_type_info::rvv_elem_type::RVV_INT: + case rvv_type_info::rvv_elem_type::RVV_UINT: + return (type_info.sew != 1 && type_info.sew != 2 && type_info.sew != 4); + + case rvv_type_info::rvv_elem_type::RVV_BOOL: + return (type_info.lmul == 1 && type_info.nfield == 1); + + default: + return false; + } + + gdb_assert_not_reached ("Uknown element type: %c", + static_cast (type_info.element_type)); +} + +static std::optional +get_rvv_type_info_unverified (struct type *type) +{ + if (!type) + return std::nullopt; + + type = check_typedef (type); + + if (!type->name ()) + { + riscv_infcall_debug_printf ( + "The type name is missing, unable to get RVV type info."); + return std::nullopt; + } + + if (type->code () != TYPE_CODE_ARRAY && type->code () != TYPE_CODE_STRUCT) + { + riscv_infcall_debug_printf ("Incorrect type_code: %d. Expected %d " + "(TYPE_CODE_ARRAY) or %d (TYPE_CODE_STRUCT)", + type->code (), TYPE_CODE_ARRAY, + TYPE_CODE_STRUCT); + return std::nullopt; + } + + struct rvv_type_name_mapping_t + { + const char *name; + rvv_type_info::rvv_elem_type type; + }; + constexpr std::array rvv_type_names = { + rvv_type_name_mapping_t{ "int", rvv_type_info::rvv_elem_type::RVV_INT }, + rvv_type_name_mapping_t{ "uint", rvv_type_info::rvv_elem_type::RVV_UINT }, + rvv_type_name_mapping_t{ "float", + rvv_type_info::rvv_elem_type::RVV_FLOAT }, + rvv_type_name_mapping_t{ "bool", rvv_type_info::rvv_elem_type::RVV_BOOL } + }; + std::string regex_string ("__rvv_("); + std::string separator = ""; + for (auto type_name : rvv_type_names) + { + regex_string += separator + type_name.name; + separator = "|"; + } + regex_string += ")(1|2|4|8|16|32|64)((m|mf)([1248]))?(x([2-8]))?_t"; + + std::regex rvv_type_regex (regex_string); + std::cmatch matches; + if (!std::regex_search (type->name (), matches, rvv_type_regex)) + { + riscv_infcall_debug_printf ("Failed to match RVV type name: %s", + type->name ()); + return std::nullopt; + } + /* 'matches' content example for name __rvv_int32mf2x4_t: + ["__rvv_vint32mf2x4_t", "int", "32", "mf2", "mf", "2", "x4", "4"] + 'matches' content example for name __rvv_bool8_t: + ["__rvv_bool8_t", "bool", "8", "", "", "", "", ""] */ + + std::string_view parsed_name (matches[1].first, matches[1].length ()); + std::string_view parsed_sew (matches[2].first, matches[2].length ()); + std::string_view parsed_lmul_str (matches[3].first, matches[3].length ()); + std::string_view parsed_lmul_type (matches[4].first, matches[4].length ()); + std::string_view parsed_lmul_val (matches[5].first, matches[5].length ()); + std::string_view parsed_nfield_str (matches[6].first, matches[6].length ()); + std::string_view parsed_nfield_val (matches[7].first, matches[7].length ()); + + rvv_type_info res; + + auto it = std::find_if ( + rvv_type_names.begin (), rvv_type_names.end (), + [parsed_name] (const rvv_type_name_mapping_t &type_name) { + return parsed_name.compare (type_name.name) == 0; + }); + if (it == rvv_type_names.end ()) + { + riscv_infcall_debug_printf ( + "Unable to find RVV element type in type name %s.", type->name ()); + return std::nullopt; + } + res.element_type = it->type; + + if (std::from_chars (parsed_sew.data (), + parsed_sew.data () + parsed_sew.size (), res.sew) + .ec + != std::errc{}) + { + riscv_infcall_debug_printf ( + "Failed to convert SEW string (%s) to unsigned", parsed_sew.data ()); + return std::nullopt; + } + + if (!parsed_lmul_str.empty ()) + { + if (parsed_lmul_type.compare ("mf") == 0) + res.is_fractional_lmul = true; + else + res.is_fractional_lmul = false; + + if (std::from_chars (parsed_lmul_val.data (), + parsed_lmul_val.data () + parsed_lmul_val.size (), + res.lmul) + .ec + != std::errc{}) + { + riscv_infcall_debug_printf ( + "Failed to convert LMUL string (%s) to unsigned", + parsed_lmul_val.data ()); + return std::nullopt; + } + } + else + res.lmul = 1; + + if (!parsed_nfield_str.empty ()) + { + if (std::from_chars (parsed_nfield_val.data (), + parsed_nfield_val.data () + + parsed_nfield_val.size (), + res.nfield) + .ec + != std::errc{}) + { + riscv_infcall_debug_printf ( + "Failed to convert NFIELD string (%s) to unsigned", + parsed_nfield_val.data ()); + return std::nullopt; + } + } + else + res.nfield = 1; + + return res; +} + +static std::optional +get_rvv_type_info (struct type *type) +{ + std::optional res = get_rvv_type_info_unverified (type); + + if (res.has_value () && verify_rvv_type (res.value ())) + return res; + + return std::nullopt; +} + +static bool +is_rvv_type (struct type *type) +{ + return get_rvv_type_info (type).has_value (); +} + +static bool +riscv_assign_vec_reg_location (struct riscv_arg_info *ainfo, + struct riscv_vector_arg_reg *reg, + struct type *func_arg_type, int vlenb) +{ + int data_length = vlenb; /* Amount of data that is stored on one register */ + + struct riscv_arg_info::location *loc0 = &ainfo->argloc[0]; + struct riscv_arg_info::location *loc1 = &ainfo->argloc[1]; + + rvv_type_info arg_type_info; + if (std::optional res = get_rvv_type_info (func_arg_type)) + arg_type_info = res.value (); + else + gdb_assert_not_reached ("Incorrect type %s", func_arg_type->name ()); + + riscv_infcall_debug_printf ( + "rvv_type_info of %s: element_type = %d, sew = %u, lmul = %u (%s " + "fractional), nfield = %u", + func_arg_type->name (), static_cast (arg_type_info.element_type), + arg_type_info.sew, arg_type_info.lmul, + (arg_type_info.is_fractional_lmul ? "is" : "is not"), + arg_type_info.nfield); + + int num_required_regs + = ((arg_type_info.is_fractional_lmul) ? 1 : arg_type_info.lmul) + * arg_type_info.nfield; + + if (arg_type_info.is_fractional_lmul) + { + num_required_regs = arg_type_info.nfield; + data_length = data_length / arg_type_info.lmul; + } + + int first_regnum = -1; + + /* Representation of vector segmented types, like vint32m4x2_t, + is different in Clang and GCC. + In Clang, all types are arrays. + In GCC, segmented types are struct's, other vector types are arrays. */ + if (ainfo->type->code () == TYPE_CODE_ARRAY) + { + if (ainfo->type->target_type ()->code () == TYPE_CODE_BOOL) + first_regnum = reg->try_use_v0 (); + else + first_regnum = reg->get_interval_start (num_required_regs, + arg_type_info.nfield); + } + else if (ainfo->type->code () == TYPE_CODE_STRUCT) + { + first_regnum + = reg->get_interval_start (num_required_regs, arg_type_info.nfield); + } + + if (first_regnum == -1) + return false; + + int last_regnum = first_regnum + num_required_regs - 1; + + loc0->loc_type = riscv_arg_info::location::in_several_regs; + loc0->loc_data.regno = first_regnum; + loc0->c_length = data_length; + loc0->c_offset = 0; + + loc1->loc_type = riscv_arg_info::location::in_several_regs; + loc1->loc_data.regno = last_regnum; + loc1->c_length = 0; + loc1->c_offset = 0; + + return true; +} + /* Assign LOC a location as the next stack parameter, and update MEMORY to record that an area of stack has been used to hold the parameter described by LOC. @@ -3238,21 +3677,44 @@ riscv_call_arg_struct (struct riscv_arg_info *ainfo, riscv_call_arg_scalar_int (ainfo, cinfo); } +static void +riscv_call_arg_vector (struct riscv_arg_info *ainfo, + struct riscv_call_info *cinfo, + struct type *func_arg_type) +{ + if (!riscv_assign_vec_reg_location (ainfo, &cinfo->vector_regs, + func_arg_type, cinfo->vlenb)) + { + // Try to pass value by reference + ainfo->argloc[0].loc_type = riscv_arg_info::location::by_ref; + cinfo->memory.ref_offset + = align_up (cinfo->memory.ref_offset, ainfo->align); + ainfo->argloc[0].loc_data.offset = cinfo->memory.ref_offset; + cinfo->memory.ref_offset += ainfo->length; + ainfo->argloc[0].c_length = ainfo->length; + + if (!riscv_assign_reg_location (&ainfo->argloc[1], &cinfo->int_regs, + cinfo->xlen, 0)) + riscv_assign_stack_location (&ainfo->argloc[1], &cinfo->memory, + cinfo->xlen, cinfo->xlen); + } +} + /* Assign a location to call (or return) argument AINFO, the location is selected from CINFO which holds information about what call argument locations are available for use next. The TYPE is the type of the argument being passed, this information is recorded into AINFO (along with some additional information derived from the type). IS_UNNAMED is true if this is an unnamed (stdarg) argument, this info is also - recorded into AINFO. + recorded into AINFO. FUNC_ARG_TYPE is the type of the function parameter in + its declaration. After assigning a location to AINFO, CINFO will have been updated. */ static void -riscv_arg_location (struct gdbarch *gdbarch, - struct riscv_arg_info *ainfo, - struct riscv_call_info *cinfo, - struct type *type, bool is_unnamed) +riscv_arg_location (struct gdbarch *gdbarch, struct riscv_arg_info *ainfo, + struct riscv_call_info *cinfo, struct type *type, + struct type *func_arg_type, bool is_unnamed) { ainfo->type = type; ainfo->length = ainfo->type->length (); @@ -3262,6 +3724,12 @@ riscv_arg_location (struct gdbarch *gdbarch, ainfo->argloc[0].c_length = 0; ainfo->argloc[1].c_length = 0; + if (is_rvv_type (func_arg_type)) /* for RISC_V vector registers */ + { + riscv_call_arg_vector (ainfo, cinfo, func_arg_type); + return; + } + switch (ainfo->type->code ()) { case TYPE_CODE_INT: @@ -3384,6 +3852,13 @@ riscv_print_arg_location (ui_file *stream, struct gdbarch *gdbarch, } break; + case riscv_arg_info::location::in_several_regs: + gdb_printf ( + stream, ", registers from %s to %s", + gdbarch_register_name (gdbarch, info->argloc[0].loc_data.regno), + gdbarch_register_name (gdbarch, info->argloc[1].loc_data.regno)); + break; + default: gdb_assert_not_reached ("unknown argument location type"); } @@ -3398,15 +3873,13 @@ static void riscv_regcache_cooked_write (int regnum, const gdb_byte *data, int len, struct regcache *regcache, int flen) { - gdb_byte tmp [sizeof (ULONGEST)]; - + int regsize = register_size (regcache->arch (), regnum); + std::vector tmp (regsize); /* FP values in FP registers must be NaN-boxed. */ if (riscv_is_fp_regno_p (regnum) && len < flen) - memset (tmp, -1, sizeof (tmp)); - else - memset (tmp, 0, sizeof (tmp)); - memcpy (tmp, data, len); - regcache->cooked_write (regnum, tmp); + memset (tmp.data (), -1, tmp.size ()); + memcpy (tmp.data (), data, len); + regcache->cooked_write (regnum, tmp.data ()); } /* Implement the push dummy call gdbarch callback. */ @@ -3446,16 +3919,21 @@ riscv_push_dummy_call (struct gdbarch *gdbarch, { struct value *arg_value; struct type *arg_type; + struct type *func_arg_type; struct riscv_arg_info *info = &arg_info[i]; arg_value = args[i]; arg_type = check_typedef (arg_value->type ()); - riscv_arg_location (gdbarch, info, &call_info, arg_type, + func_arg_type + = (i < ftype->num_fields ()) ? ftype->field (i).type () : nullptr; + + riscv_arg_location (gdbarch, info, &call_info, arg_type, func_arg_type, ftype->has_varargs () && i >= ftype->num_fields ()); if (info->type != arg_type) arg_value = value_cast (info->type, arg_value); + info->contents = arg_value->contents ().data (); } @@ -3466,14 +3944,18 @@ riscv_push_dummy_call (struct gdbarch *gdbarch, if (riscv_debug_infcall) { RISCV_INFCALL_SCOPED_DEBUG_START_END ("dummy call args"); - riscv_infcall_debug_printf ("floating point ABI %s in use", - (riscv_has_fp_abi (gdbarch) - ? "is" : "is not")); + riscv_infcall_debug_printf ( + "floating point ABI %s in use", + (riscv_has_fp_abi (gdbarch) ? "is" : "is not")); + riscv_infcall_debug_printf ( + "vector ABI %s in use", + (riscv_has_vector_abi (gdbarch) ? "is" : "is not")); riscv_infcall_debug_printf ("xlen: %d", call_info.xlen); riscv_infcall_debug_printf ("flen: %d", call_info.flen); + riscv_infcall_debug_printf ("vlenb: %d", call_info.vlenb); if (return_method == return_method_struct) - riscv_infcall_debug_printf - ("[**] struct return pointer in register $A0"); + riscv_infcall_debug_printf ( + "[**] struct return pointer in register $A0"); for (i = 0; i < nargs; ++i) { struct riscv_arg_info *info = &arg_info [i]; @@ -3547,6 +4029,22 @@ riscv_push_dummy_call (struct gdbarch *gdbarch, second_arg_data = (gdb_byte *) &dst; break; + case riscv_arg_info::location::in_several_regs: + { + const gdb_byte *cur_contents = info->contents; + int cur_c_length = info->argloc[0].c_length; + for (int cur_regno = info->argloc[0].loc_data.regno; + cur_regno <= info->argloc[1].loc_data.regno; cur_regno++) + { + riscv_regcache_cooked_write (cur_regno, cur_contents, + cur_c_length, regcache, + call_info.flen); + cur_contents += cur_c_length; + } + second_arg_length = 0; + } + break; + default: gdb_assert_not_reached ("unknown argument location type"); } @@ -3577,6 +4075,8 @@ riscv_push_dummy_call (struct gdbarch *gdbarch, } case riscv_arg_info::location::by_ref: + case riscv_arg_info::location:: + in_several_regs: /* We shouldn't get here for this case*/ default: /* The second location should never be a reference, any argument being passed by reference just places its address @@ -3615,9 +4115,14 @@ riscv_return_value (struct gdbarch *gdbarch, struct riscv_call_info call_info (gdbarch); struct riscv_arg_info info; struct type *arg_type; + struct type *func_retval_type; arg_type = check_typedef (type); - riscv_arg_location (gdbarch, &info, &call_info, arg_type, false); + func_retval_type = (function && function->type ()) + ? function->type ()->target_type () + : nullptr; + riscv_arg_location (gdbarch, &info, &call_info, arg_type, func_retval_type, + false); if (riscv_debug_infcall) { @@ -3770,9 +4275,41 @@ riscv_return_value (struct gdbarch *gdbarch, } break; + case riscv_arg_info::location::in_several_regs: + { + int first_regnum = info.argloc[0].loc_data.regno; + int last_regnum = info.argloc[1].loc_data.regno; + + gdb_byte *tmp_readbuf = readbuf; + const gdb_byte *tmp_writebuf = writebuf; + + for (int cur_regnum = first_regnum; cur_regnum <= last_regnum; + cur_regnum++) + { + if (readbuf) + { + gdb_byte *ptr = tmp_readbuf + info.argloc[0].c_offset; + regcache->cooked_read_part (cur_regnum, 0, + info.argloc[0].c_length, ptr); + tmp_readbuf += info.argloc[0].c_length; + } + + if (writebuf) + { + const gdb_byte *ptr + = tmp_writebuf + info.argloc[0].c_offset; + riscv_regcache_cooked_write (cur_regnum, ptr, + info.argloc[0].c_length, + regcache, call_info.flen); + tmp_writebuf += info.argloc[0].c_length; + } + } + } + break; + case riscv_arg_info::location::on_stack: default: - error (_("invalid argument location")); + error (_ ("invalid argument location")); break; } @@ -3804,6 +4341,7 @@ riscv_return_value (struct gdbarch *gdbarch, switch (info.argloc[0].loc_type) { case riscv_arg_info::location::in_reg: + case riscv_arg_info::location::in_several_regs: return RETURN_VALUE_REGISTER_CONVENTION; case riscv_arg_info::location::by_ref: return RETURN_VALUE_ABI_PRESERVES_ADDRESS; @@ -3973,6 +4511,28 @@ riscv_features_from_bfd (const bfd *abfd) } features.embedded = true; } + + obj_attribute *obj_attr = elf_known_obj_attributes_proc (abfd); + const char *march = obj_attr[Tag_RISCV_arch].s; + if (march) + { + /* According to the RVV specification, a binary file does not require + any particular vlenb value. Therefore, we used minimal vlenb value + to indicate that the vector ABI is in use. Additionally, a valid + vlenb value is required here, as it will be used later to create a + default target description. */ + std::string march_str (march); + if (march_str.find ("_v") != std::string::npos) + features.vlenb = 16; + else if (march_str.find ("_zve64") != std::string::npos) + features.vlenb = 8; + else if (march_str.find ("_zve32") != std::string::npos) + features.vlenb = 4; + else + features.vlenb = 0; + } + else + features.vlenb = 0; } return features; @@ -4266,18 +4826,24 @@ riscv_gdbarch_init (struct gdbarch_info info, however, this has not been tested in GDB yet, so for now we require that the requested xlen match the targets xlen. */ if (abi_features.xlen != features.xlen) - error (_("bfd requires xlen %d, but target has xlen %d"), - abi_features.xlen, features.xlen); + error (_ ("bfd requires xlen %d, but target has xlen %d"), + abi_features.xlen, features.xlen); /* We do support running binaries compiled for 32-bit float on targets with 64-bit float, so we only complain if the binary requires more than the target has available. */ if (abi_features.flen > features.flen) - error (_("bfd requires flen %d, but target has flen %d"), - abi_features.flen, features.flen); + error (_ ("bfd requires flen %d, but target has flen %d"), + abi_features.flen, features.flen); + + /* Look at riscv_features_from_bfd*/ + if ((abi_features.vlenb > 0) && (features.vlenb == 0)) + { + warning (_ ("bfd requires non-zero vlenb, but target has vlenb = 0, " + "vector registers unsupported")); + } /* Find a candidate among the list of pre-declared architectures. */ - for (arches = gdbarch_list_lookup_by_info (arches, &info); - arches != NULL; + for (arches = gdbarch_list_lookup_by_info (arches, &info); arches != NULL; arches = gdbarch_list_lookup_by_info (arches->next, &info)) { /* Check that the feature set of the ARCHES matches the feature set @@ -5388,3 +5954,13 @@ riscv_process_record (struct gdbarch *gdbarch, struct regcache *regcache, return 0; } + +bool +riscv_is_vpr_or_vcsr (unsigned regnum) +{ + return (regnum >= RISCV_V0_REGNUM && regnum <= RISCV_V0_REGNUM + 31) + || regnum == RISCV_CSR_VSTART_REGNUM + || regnum == RISCV_CSR_VCSR_REGNUM || regnum == RISCV_CSR_VL_REGNUM + || regnum == RISCV_CSR_VTYPE_REGNUM + || regnum == RISCV_CSR_VLENB_REGNUM; +} diff --git a/gdb/riscv-tdep.h b/gdb/riscv-tdep.h index 2903aefd007..91dd01dac62 100644 --- a/gdb/riscv-tdep.h +++ b/gdb/riscv-tdep.h @@ -23,61 +23,7 @@ #include "arch/riscv.h" #include "gdbarch.h" - -/* RiscV register numbers. */ -enum -{ - RISCV_ZERO_REGNUM = 0, /* Read-only register, always 0. */ - RISCV_RA_REGNUM = 1, /* Return Address. */ - RISCV_SP_REGNUM = 2, /* Stack Pointer. */ - RISCV_GP_REGNUM = 3, /* Global Pointer. */ - RISCV_TP_REGNUM = 4, /* Thread Pointer. */ - RISCV_FP_REGNUM = 8, /* Frame Pointer. */ - RISCV_A0_REGNUM = 10, /* First argument. */ - RISCV_A1_REGNUM = 11, /* Second argument. */ - RISCV_A2_REGNUM = 12, /* Third argument. */ - RISCV_A3_REGNUM = 13, /* Forth argument. */ - RISCV_A4_REGNUM = 14, /* Fifth argument. */ - RISCV_A5_REGNUM = 15, /* Sixth argument. */ - RISCV_A7_REGNUM = 17, /* Register to pass syscall number. */ - RISCV_PC_REGNUM = 32, /* Program Counter. */ - - RISCV_NUM_INTEGER_REGS = 32, - - RISCV_FIRST_FP_REGNUM = 33, /* First Floating Point Register */ - RISCV_FA0_REGNUM = 43, - RISCV_FA1_REGNUM = RISCV_FA0_REGNUM + 1, - RISCV_LAST_FP_REGNUM = 64, /* Last Floating Point Register */ - - RISCV_FIRST_CSR_REGNUM = 65, /* First CSR */ -#define DECLARE_CSR(name, num, class, define_version, abort_version) \ - RISCV_ ## num ## _REGNUM = RISCV_FIRST_CSR_REGNUM + num, -#include "opcode/riscv-opc.h" -#undef DECLARE_CSR - RISCV_LAST_CSR_REGNUM = 4160, - RISCV_CSR_LEGACY_MISA_REGNUM = 0xf10 + RISCV_FIRST_CSR_REGNUM, - - RISCV_PRIV_REGNUM = 4161, - - RISCV_V0_REGNUM, - - RISCV_V31_REGNUM = RISCV_V0_REGNUM + 31, - - RISCV_LAST_REGNUM = RISCV_V31_REGNUM -}; - -/* RiscV DWARF register numbers. */ -enum -{ - RISCV_DWARF_REGNUM_X0 = 0, - RISCV_DWARF_REGNUM_X31 = 31, - RISCV_DWARF_REGNUM_F0 = 32, - RISCV_DWARF_REGNUM_F31 = 63, - RISCV_DWARF_REGNUM_V0 = 96, - RISCV_DWARF_REGNUM_V31 = 127, - RISCV_DWARF_FIRST_CSR = 4096, - RISCV_DWARF_LAST_CSR = 8191, -}; +#include "riscv-regs.h" /* RISC-V specific per-architecture information. */ struct riscv_gdbarch_tdep : gdbarch_tdep_base @@ -135,6 +81,8 @@ extern int riscv_isa_xlen (struct gdbarch *gdbarch); single, double or quad floating point support is available. */ extern int riscv_isa_flen (struct gdbarch *gdbarch); +extern int riscv_isa_vlenb (struct gdbarch *gdbarch); + /* Return the width in bytes of the general purpose register abi for GDBARCH. This can be equal to, or less than RISCV_ISA_XLEN and reflects how the binary was compiled rather than the hardware that is available. @@ -158,6 +106,8 @@ extern int riscv_abi_flen (struct gdbarch *gdbarch); argument registers. */ extern bool riscv_abi_embedded (struct gdbarch *gdbarch); +extern int riscv_abi_vlenb (struct gdbarch *gdbarch); + /* Single step based on where the current instruction will take us. */ extern std::vector riscv_software_single_step (struct regcache *regcache); @@ -194,4 +144,7 @@ extern int riscv_process_record (struct gdbarch *gdbarch, /* The names of the RISC-V target description features. */ extern const char *riscv_feature_name_csr; +/* Determines if regnum corresponds to vector register vX or vector CSR. */ +extern bool riscv_is_vpr_or_vcsr (unsigned regnum); + #endif /* GDB_RISCV_TDEP_H */ diff --git a/gdbserver/linux-riscv-low.cc b/gdbserver/linux-riscv-low.cc index f70ed597051..82362542c19 100644 --- a/gdbserver/linux-riscv-low.cc +++ b/gdbserver/linux-riscv-low.cc @@ -21,6 +21,7 @@ #include "linux-low.h" #include "tdesc.h" #include "elf/common.h" +#include "nat/riscv-linux-ptrace.h" #include "nat/riscv-linux-tdesc.h" #include "opcode/riscv.h" @@ -102,26 +103,6 @@ riscv_target::low_get_syscall_trapinfo (regcache *regcache, int *sysno) *sysno = (int)l_sysno; } -/* Implementation of linux target ops method "low_arch_setup". */ - -void -riscv_target::low_arch_setup () -{ - static const char *expedite_regs[] = { "sp", "pc", NULL }; - - const riscv_gdbarch_features features - = riscv_linux_read_features (current_thread->id.lwp ()); - target_desc_up tdesc = riscv_create_target_description (features); - - if (tdesc->expedite_regs.empty ()) - { - init_target_desc (tdesc.get (), expedite_regs, GDB_OSABI_LINUX); - gdb_assert (!tdesc->expedite_regs.empty ()); - } - - current_process ()->tdesc = tdesc.release (); -} - /* Collect GPRs from REGCACHE into BUF. */ static void @@ -185,25 +166,76 @@ riscv_store_fpregset (struct regcache *regcache, const void *buf) supply_register_by_name (regcache, "fcsr", regbuf); } +/* Collect vector regs from REGCACHE into BUF. */ + +static void +riscv_fill_vecregset (struct regcache *regcache, void *buf) +{ + const struct target_desc *tdesc = regcache->tdesc; + int v0_regno = find_regno (tdesc, "v0"); + + struct __riscv_v_regset_state *vecregs + = (struct __riscv_v_regset_state *)buf; + unsigned long vlenb = vecregs->vlenb; + gdb_byte *regbuf = (gdb_byte *)vecregs->vreg; + + for (int i = 0; i < 32; i++, regbuf += vlenb) + collect_register (regcache, v0_regno + i, regbuf); + + collect_register_by_name (regcache, "vstart", &vecregs->vstart); + collect_register_by_name (regcache, "vcsr", &vecregs->vcsr); + collect_register_by_name (regcache, "vl", &vecregs->vl); + collect_register_by_name (regcache, "vtype", &vecregs->vtype); + collect_register_by_name (regcache, "vlenb", &vecregs->vlenb); +} + +/* Supply vector regs from BUF into REGCACHE. */ + +static void +riscv_store_vecregset (struct regcache *regcache, const void *buf) +{ + const struct target_desc *tdesc = regcache->tdesc; + int v0_regno = find_regno (tdesc, "v0"); + + const struct __riscv_v_regset_state *vecregs + = (const struct __riscv_v_regset_state *)buf; + if (vecregs->vlenb == 0) + return; + int v0_regsize = register_size (tdesc, v0_regno); + gdb_assert (vecregs->vlenb == v0_regsize); + unsigned long vlenb = vecregs->vlenb; + const gdb_byte *regbuf = (const gdb_byte *)vecregs->vreg; + + for (int i = 0; i < 32; i++, regbuf += vlenb) + supply_register (regcache, v0_regno + i, regbuf); + + supply_register_by_name (regcache, "vstart", &vecregs->vstart); + supply_register_by_name (regcache, "vcsr", &vecregs->vcsr); + supply_register_by_name (regcache, "vl", &vecregs->vl); + supply_register_by_name (regcache, "vtype", &vecregs->vtype); + supply_register_by_name (regcache, "vlenb", &vecregs->vlenb); +} + /* RISC-V/Linux regsets. FPRs are optional and come in different sizes, so define multiple regsets for them marking them all as OPTIONAL_REGS rather than FP_REGS, so that "regsets_fetch_inferior_registers" picks the right one according to size. */ -static struct regset_info riscv_regsets[] = { - { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_PRSTATUS, - sizeof (elf_gregset_t), GENERAL_REGS, - riscv_fill_gregset, riscv_store_gregset }, - { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_FPREGSET, - sizeof (struct __riscv_mc_q_ext_state), OPTIONAL_REGS, - riscv_fill_fpregset, riscv_store_fpregset }, - { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_FPREGSET, - sizeof (struct __riscv_mc_d_ext_state), OPTIONAL_REGS, - riscv_fill_fpregset, riscv_store_fpregset }, - { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_FPREGSET, - sizeof (struct __riscv_mc_f_ext_state), OPTIONAL_REGS, - riscv_fill_fpregset, riscv_store_fpregset }, - NULL_REGSET -}; +static struct regset_info riscv_regsets[] + = { { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_PRSTATUS, + sizeof (elf_gregset_t), GENERAL_REGS, riscv_fill_gregset, + riscv_store_gregset }, + { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_FPREGSET, + sizeof (struct __riscv_mc_q_ext_state), OPTIONAL_REGS, + riscv_fill_fpregset, riscv_store_fpregset }, + { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_FPREGSET, + sizeof (struct __riscv_mc_d_ext_state), OPTIONAL_REGS, + riscv_fill_fpregset, riscv_store_fpregset }, + { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_FPREGSET, + sizeof (struct __riscv_mc_f_ext_state), OPTIONAL_REGS, + riscv_fill_fpregset, riscv_store_fpregset }, + { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_RISCV_VECTOR, 0, + EXTENDED_REGS, riscv_fill_vecregset, riscv_store_vecregset }, + NULL_REGSET }; /* RISC-V/Linux regset information. */ static struct regsets_info riscv_regsets_info = @@ -229,6 +261,44 @@ riscv_target::get_regs_info () return &riscv_regs; } +/* Setup Vector Regset. */ +static void +setup_vector_regset (struct regset_info *vector_regset_info, + const riscv_gdbarch_features *features) +{ + int vecreg_size = features->vlenb * 8; + vector_regset_info->size + = sizeof (struct __riscv_v_regset_state) + 32 * vecreg_size; +} + +/* Implementation of linux target ops method "low_arch_setup". */ + +void +riscv_target::low_arch_setup () +{ + static const char *expedite_regs[] = { "sp", "pc", NULL }; + + const riscv_gdbarch_features features + = riscv_linux_read_features (current_thread->id.lwp ()); + target_desc_up tdesc = riscv_create_target_description (features); + + struct regset_info *regset; + for (regset = riscv_regsets; regset->size >= 0; regset++) + if (regset->nt_type == NT_RISCV_VECTOR) + { + setup_vector_regset (regset, &features); + break; + } + + if (tdesc->expedite_regs.empty ()) + { + init_target_desc (tdesc.get (), expedite_regs, GDB_OSABI_LINUX); + gdb_assert (!tdesc->expedite_regs.empty ()); + } + + current_process ()->tdesc = tdesc.release (); +} + /* Implementation of linux target ops method "low_fetch_register". */ bool diff --git a/gdbsupport/common-utils.h b/gdbsupport/common-utils.h index 10bf9f49e2a..a29aa8edfa4 100644 --- a/gdbsupport/common-utils.h +++ b/gdbsupport/common-utils.h @@ -20,12 +20,14 @@ #ifndef GDBSUPPORT_COMMON_UTILS_H #define GDBSUPPORT_COMMON_UTILS_H +#include #include #include #include "gdbsupport/byte-vector.h" #include "gdbsupport/gdb_unique_ptr.h" #include "gdbsupport/array-view.h" #include "poison.h" +#include #include #if defined HAVE_LIBXXHASH diff --git a/include/elf/common.h b/include/elf/common.h index 5d0f93ebf56..f3bc367b962 100644 --- a/include/elf/common.h +++ b/include/elf/common.h @@ -776,6 +776,7 @@ /* note name must be "LINUX". */ #define NT_RISCV_CSR 0x900 /* RISC-V Control and Status Registers */ /* note name must be "LINUX". */ +#define NT_RISCV_VECTOR 0x901 /* RISC-V Vector Registers. */ #define NT_SIGINFO 0x53494749 /* Fields of siginfo_t. */ #define NT_FILE 0x46494c45 /* Description of mapped files. */ -- 2.43.0