Add riscv half-precision floating point detection (#375)

Add cpuinfo_has_riscv_zfh() and cpuinfo_has_riscv_zvfh for fp16 detection.

The motivation here is to enable this runtime detection support in xnnpack for its rvv fp16 kernels. (xnnpack uses this library)

GitOrigin-RevId: d05fbcd57dc096718c4979e7c054e628f1f3520b
Change-Id: I6cbab6d7c0fb2eb1a605edfeb22a4df2684c8d20
diff --git a/include/cpuinfo.h b/include/cpuinfo.h
index a441bb1..321998c 100644
--- a/include/cpuinfo.h
+++ b/include/cpuinfo.h
@@ -2232,6 +2232,12 @@
 	bool c;
 	/* Vector Extension. */
 	bool v;
+
+	/* ISA Extensions */
+	/* Half-Precision Floating-Point Extension. */
+	bool zfh;
+	/* Half-Precision Floating-Point Vector Extension. */
+	bool zvfh;
 };
 
 extern struct cpuinfo_riscv_isa cpuinfo_isa;
@@ -2307,6 +2313,22 @@
 #endif
 }
 
+static inline bool cpuinfo_has_riscv_zfh(void) {
+#if CPUINFO_ARCH_RISCV32 || CPUINFO_ARCH_RISCV64
+	return cpuinfo_isa.zfh;
+#else
+	return false;
+#endif
+}
+
+static inline bool cpuinfo_has_riscv_zvfh(void) {
+#if CPUINFO_ARCH_RISCV32 || CPUINFO_ARCH_RISCV64
+	return cpuinfo_isa.zvfh;
+#else
+	return false;
+#endif
+}
+
 const struct cpuinfo_processor* CPUINFO_ABI cpuinfo_get_processors(void);
 const struct cpuinfo_core* CPUINFO_ABI cpuinfo_get_cores(void);
 const struct cpuinfo_cluster* CPUINFO_ABI cpuinfo_get_clusters(void);
diff --git a/src/riscv/linux/api.h b/src/riscv/linux/api.h
index 829de84..1c356bf 100644
--- a/src/riscv/linux/api.h
+++ b/src/riscv/linux/api.h
@@ -61,11 +61,13 @@
  * @param[processor] - The Linux ID of the target processor.
  * @param[vendor] - Reference to the cpuinfo_vendor to populate.
  * @param[uarch] - Reference to the cpuinfo_uarch to populate.
+ * @param[isa] - Reference to the cpuinfo_riscv_isa to populate isa extensions.
  */
 CPUINFO_INTERNAL void cpuinfo_riscv_linux_decode_vendor_uarch_from_hwprobe(
 	uint32_t processor,
 	enum cpuinfo_vendor vendor[restrict static 1],
-	enum cpuinfo_uarch uarch[restrict static 1]);
+	enum cpuinfo_uarch uarch[restrict static 1],
+	struct cpuinfo_riscv_isa isa[restrict static 1]);
 
 /* Used to determine which uarch is associated with the current thread. */
 extern CPUINFO_INTERNAL const uint32_t* cpuinfo_linux_cpu_to_uarch_index_map;
diff --git a/src/riscv/linux/init.c b/src/riscv/linux/init.c
index 9ab3d6e..45168c7 100644
--- a/src/riscv/linux/init.c
+++ b/src/riscv/linux/init.c
@@ -6,7 +6,7 @@
 #include <riscv/linux/api.h>
 
 /* ISA structure to hold supported extensions. */
-struct cpuinfo_riscv_isa cpuinfo_isa;
+struct cpuinfo_riscv_isa cpuinfo_isa = {0};
 
 /* Helper function to bitmask flags and ensure operator precedence. */
 static inline bool bitmask_all(uint32_t flags, uint32_t mask) {
@@ -320,7 +320,8 @@
 		cpuinfo_riscv_linux_decode_vendor_uarch_from_hwprobe(
 			processor,
 			&riscv_linux_processors[processor].core.vendor,
-			&riscv_linux_processors[processor].core.uarch);
+			&riscv_linux_processors[processor].core.uarch,
+			&cpuinfo_isa);
 
 		/* Populate frequency information of this core. */
 		uint32_t frequency = cpuinfo_linux_get_processor_cur_frequency(processor);
diff --git a/src/riscv/linux/riscv-hw.c b/src/riscv/linux/riscv-hw.c
index 63a92e2..f820086 100644
--- a/src/riscv/linux/riscv-hw.c
+++ b/src/riscv/linux/riscv-hw.c
@@ -53,6 +53,30 @@
 #define RISCV_HWPROBE_EXT_ZBB (1 << 4)
 #define RISCV_HWPROBE_EXT_ZBS (1 << 5)
 #define RISCV_HWPROBE_EXT_ZICBOZ (1 << 6)
+#define	RISCV_HWPROBE_EXT_ZBC (1 << 7)
+#define	RISCV_HWPROBE_EXT_ZBKB (1 << 8)
+#define	RISCV_HWPROBE_EXT_ZBKC (1 << 9)
+#define	RISCV_HWPROBE_EXT_ZBKX (1 << 10)
+#define	RISCV_HWPROBE_EXT_ZKND (1 << 11)
+#define	RISCV_HWPROBE_EXT_ZKNE (1 << 12)
+#define	RISCV_HWPROBE_EXT_ZKNH (1 << 13)
+#define	RISCV_HWPROBE_EXT_ZKSED (1 << 14)
+#define	RISCV_HWPROBE_EXT_ZKSH (1 << 15)
+#define	RISCV_HWPROBE_EXT_ZKT (1 << 16)
+#define	RISCV_HWPROBE_EXT_ZVBB (1 << 17)
+#define	RISCV_HWPROBE_EXT_ZVBC (1 << 18)
+#define	RISCV_HWPROBE_EXT_ZVKB (1 << 19)
+#define	RISCV_HWPROBE_EXT_ZVKG (1 << 20)
+#define	RISCV_HWPROBE_EXT_ZVKNED (1 << 21)
+#define	RISCV_HWPROBE_EXT_ZVKNHA (1 << 22)
+#define	RISCV_HWPROBE_EXT_ZVKNHB (1 << 23)
+#define	RISCV_HWPROBE_EXT_ZVKSED (1 << 24)
+#define	RISCV_HWPROBE_EXT_ZVKSH (1 << 25)
+#define	RISCV_HWPROBE_EXT_ZVKT (1 << 26)
+#define	RISCV_HWPROBE_EXT_ZFH (1 << 27)
+#define	RISCV_HWPROBE_EXT_ZFHMIN (1 << 28)
+#define	RISCV_HWPROBE_EXT_ZIHINTNTL (1 << 29)
+#define	RISCV_HWPROBE_EXT_ZVFH (1 << 30)
 #define RISCV_HWPROBE_KEY_CPUPERF_0 5
 #define RISCV_HWPROBE_MISALIGNED_UNKNOWN (0 << 0)
 #define RISCV_HWPROBE_MISALIGNED_EMULATED (1 << 0)
@@ -72,7 +96,8 @@
 void cpuinfo_riscv_linux_decode_vendor_uarch_from_hwprobe(
 	uint32_t processor,
 	enum cpuinfo_vendor vendor[restrict static 1],
-	enum cpuinfo_uarch uarch[restrict static 1]) {
+	enum cpuinfo_uarch uarch[restrict static 1],
+	struct cpuinfo_riscv_isa isa[restrict static 1]) {
 	struct riscv_hwprobe pairs[] = {
 		{
 			.key = RISCV_HWPROBE_KEY_MVENDORID,
@@ -83,6 +108,9 @@
 		{
 			.key = RISCV_HWPROBE_KEY_MIMPID,
 		},
+		{
+			.key = RISCV_HWPROBE_KEY_IMA_EXT_0,
+		},
 	};
 	const size_t pairs_count = sizeof(pairs) / sizeof(struct riscv_hwprobe);
 
@@ -128,6 +156,7 @@
 	uint32_t vendor_id = 0;
 	uint32_t arch_id = 0;
 	uint32_t imp_id = 0;
+	uint64_t ima_ext_0 = 0;
 	for (size_t pair = 0; pair < pairs_count; pair++) {
 		switch (pairs[pair].key) {
 			case RISCV_HWPROBE_KEY_MVENDORID:
@@ -139,6 +168,9 @@
 			case RISCV_HWPROBE_KEY_MIMPID:
 				imp_id = pairs[pair].value;
 				break;
+			case RISCV_HWPROBE_KEY_IMA_EXT_0:
+				ima_ext_0 = pairs[pair].value;
+				break;
 			default:
 				/* The key value may be -1 if unsupported. */
 				break;
@@ -146,6 +178,16 @@
 	}
 	cpuinfo_riscv_decode_vendor_uarch(vendor_id, arch_id, imp_id, vendor, uarch);
 
+	/* Parse ISA extensions retrieved. */
+	if (ima_ext_0 != 0) {
+		if (ima_ext_0 & RISCV_HWPROBE_EXT_ZFH) {
+			isa->zfh = true;
+		}
+		if (ima_ext_0 & RISCV_HWPROBE_EXT_ZVFH) {
+			isa->zvfh = true;
+		}
+	}
+
 cleanup:
 	CPU_FREE(cpu_set);
 }
diff --git a/tools/isa-info.c b/tools/isa-info.c
index a7e387a..9511e8b 100644
--- a/tools/isa-info.c
+++ b/tools/isa-info.c
@@ -199,7 +199,9 @@
 	printf("\tAtomics: %s\n", cpuinfo_has_riscv_a() ? "yes" : "no");
 	printf("\tSingle-Precision Floating-Point: %s\n", cpuinfo_has_riscv_f() ? "yes" : "no");
 	printf("\tDouble-Precision Floating-Point: %s\n", cpuinfo_has_riscv_d() ? "yes" : "no");
+	printf("\tHalf-Precision Floating-Point: %s\n", cpuinfo_has_riscv_zfh() ? "yes" : "no");
 	printf("\tCompressed: %s\n", cpuinfo_has_riscv_c() ? "yes" : "no");
 	printf("\tVector: %s\n", cpuinfo_has_riscv_v() ? "yes" : "no");
+	printf("\tVector Half-Precision Floating-Point: %s\n", cpuinfo_has_riscv_zvfh() ? "yes" : "no");
 #endif
 }