diff --git a/CMakeLists.txt b/CMakeLists.txt
index 15d7aad6..e4e68b7e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -49,6 +49,7 @@ set(PROCESSOR_IS_ARM FALSE)
set(PROCESSOR_IS_AARCH64 FALSE)
set(PROCESSOR_IS_X86 FALSE)
set(PROCESSOR_IS_POWER FALSE)
+set(PROCESSOR_IS_RISCV FALSE)
if(CMAKE_SYSTEM_PROCESSOR MATCHES "^mips")
set(PROCESSOR_IS_MIPS TRUE)
@@ -60,6 +61,8 @@ elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "(x86_64)|(AMD64|amd64)|(^i.86$)")
set(PROCESSOR_IS_X86 TRUE)
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(powerpc|ppc)")
set(PROCESSOR_IS_POWER TRUE)
+elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^riscv")
+ set(PROCESSOR_IS_RISCV TRUE)
endif()
macro(add_cpu_features_headers_and_sources HDRS_LIST_NAME SRCS_LIST_NAME)
@@ -79,6 +82,8 @@ macro(add_cpu_features_headers_and_sources HDRS_LIST_NAME SRCS_LIST_NAME)
list(APPEND ${SRCS_LIST_NAME} ${PROJECT_SOURCE_DIR}/include/internal/windows_utils.h)
elseif(PROCESSOR_IS_POWER)
list(APPEND ${HDRS_LIST_NAME} ${PROJECT_SOURCE_DIR}/include/cpuinfo_ppc.h)
+ elseif(PROCESSOR_IS_RISCV)
+ list(APPEND ${HDRS_LIST_NAME} ${PROJECT_SOURCE_DIR}/include/cpuinfo_riscv.h)
else()
message(FATAL_ERROR "Unsupported architectures ${CMAKE_SYSTEM_PROCESSOR}")
endif()
diff --git a/README.md b/README.md
index 3e6a16b4..644a0515 100644
--- a/README.md
+++ b/README.md
@@ -172,14 +172,14 @@ flags : aes,avx,cx16,smx,sse4_1,sse4_2,ssse3
## What's supported
-| | x86³ | ARM | AArch64 | MIPS⁴ | POWER |
-|---------|:----:|:-------:|:-------:|:-------:|:-------:|
-| Android | yes² | yes¹ | yes¹ | yes¹ | N/A |
-| iOS | N/A | not yet | not yet | N/A | N/A |
-| Linux | yes² | yes¹ | yes¹ | yes¹ | yes¹ |
-| MacOs | yes² | N/A | not yet | N/A | no |
-| Windows | yes² | not yet | not yet | N/A | N/A |
-| FreeBSD | yes² | not yet | not yet | not yet | not yet |
+| | x86³ | ARM | AArch64 | MIPS⁴ | POWER | RISC-V |
+|---------|:----:|:-------:|:-------:|:-------:|:-------:|:-------:|
+| Android | yes² | yes¹ | yes¹ | yes¹ | N/A | N/A |
+| iOS | N/A | not yet | not yet | N/A | N/A | N/A |
+| Linux | yes² | yes¹ | yes¹ | yes¹ | yes¹ | yes¹ |
+| MacOs | yes² | N/A | not yet | N/A | no | N/A |
+| Windows | yes² | not yet | not yet | N/A | N/A | N/A |
+| FreeBSD | yes² | not yet | not yet | not yet | not yet | N/A |
1. **Features revealed from Linux.** We gather data from several sources
depending on availability:
diff --git a/include/cpuinfo_riscv.h b/include/cpuinfo_riscv.h
new file mode 100644
index 00000000..fba88048
--- /dev/null
+++ b/include/cpuinfo_riscv.h
@@ -0,0 +1,72 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CPU_FEATURES_INCLUDE_CPUINFO_RISCV_H_
+#define CPU_FEATURES_INCLUDE_CPUINFO_RISCV_H_
+
+#include "cpu_features_cache_info.h"
+#include "cpu_features_macros.h"
+
+#if !defined(CPU_FEATURES_ARCH_RISCV)
+#error "Including cpuinfo_riscv.h from a non-riscv target."
+#endif
+
+CPU_FEATURES_START_CPP_NAMESPACE
+
+typedef struct {
+ int riscv32 : 1; // Is a 32 bit architecture
+ int riscv64 : 1; // Is a 64 bit architecture
+ int riscv128 : 1; // Is a 128 bit architecture
+ int a : 1; // Atomic Instructions
+ int c : 1; // Compressed Instructions
+ int d : 1; // Double Precision Floating Point
+ int e : 1; // Base Integer Reduced
+ int f : 1; // Single Precision Floating Point
+ int i : 1; // Base Integer
+ int m : 1; // Integer Multiplication and Division
+ int v : 1; // Vector Operations
+ int q : 1; // Quad Precision Floating Point
+} RiscvFeatures;
+
+
+typedef struct {
+ RiscvFeatures features;
+ char uarch[64]; // 0 terminated string
+ char vendor[64]; // 0 terminated string
+} RiscvInfo;
+
+typedef enum {
+ RISCV_32,
+ RISCV_64,
+ RISCV_128,
+ RISCV_A,
+ RISCV_C,
+ RISCV_D,
+ RISCV_E,
+ RISCV_F,
+ RISCV_I,
+ RISCV_M,
+ RISCV_V,
+ RISCV_Q,
+ RISCV_LAST_,
+} RiscvFeaturesEnum;
+
+RiscvInfo GetRiscvInfo(void);
+int GetRiscvFeaturesEnumValue(const RiscvFeatures* features, RiscvFeaturesEnum value);
+const char* GetRiscvFeaturesEnumName(RiscvFeaturesEnum);
+
+CPU_FEATURES_END_CPP_NAMESPACE
+
+#endif // CPU_FEATURES_INCLUDE_CPUINFO_RISCV_H_
+
diff --git a/include/internal/hwcaps.h b/include/internal/hwcaps.h
index f916b81c..ff7c16a5 100644
--- a/include/internal/hwcaps.h
+++ b/include/internal/hwcaps.h
@@ -177,6 +177,9 @@ CPU_FEATURES_START_CPP_NAMESPACE
#endif
// https://elixir.bootlin.com/linux/latest/source/arch/riscv/include/uapi/asm/hwcap.h
+#define RISCV_HWCAP_32 0x32
+#define RISCV_HWCAP_64 0x64
+#define RISCV_HWCAP_128 0x128
#define RISCV_HWCAP_A (1UL << ('A' - 'A'))
#define RISCV_HWCAP_C (1UL << ('C' - 'A'))
#define RISCV_HWCAP_D (1UL << ('D' - 'A'))
diff --git a/src/impl_riscv_linux.c b/src/impl_riscv_linux.c
new file mode 100644
index 00000000..0df4aa97
--- /dev/null
+++ b/src/impl_riscv_linux.c
@@ -0,0 +1,111 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cpu_features_macros.h"
+
+#ifdef CPU_FEATURES_ARCH_RISCV
+#if defined(CPU_FEATURES_OS_LINUX)
+
+#include "cpuinfo_riscv.h"
+
+////////////////////////////////////////////////////////////////////////////////
+// Definitions for introspection.
+////////////////////////////////////////////////////////////////////////////////
+#define INTROSPECTION_TABLE \
+ LINE(RISCV_32, riscv32, "32", RISCV_HWCAP_32, 0) \
+ LINE(RISCV_64, riscv64, "64", RISCV_HWCAP_64, 0) \
+ LINE(RISCV_128, riscv128, "128", RISCV_HWCAP_128, 0) \
+ LINE(RISCV_A, a, "a", RISCV_HWCAP_A, 0) \
+ LINE(RISCV_C, c, "c", RISCV_HWCAP_C, 0) \
+ LINE(RISCV_D, d, "d", RISCV_HWCAP_D, 0) \
+ LINE(RISCV_E, e, "e", RISCV_HWCAP_E, 0) \
+ LINE(RISCV_F, f, "f", RISCV_HWCAP_F, 0) \
+ LINE(RISCV_I, i, "i", RISCV_HWCAP_I, 0) \
+ LINE(RISCV_M, m, "m", RISCV_HWCAP_M, 0) \
+ LINE(RISCV_V, v, "v", RISCV_HWCAP_V, 0) \
+ LINE(RISCV_Q, q, "q", RISCV_HWCAP_Q, 0)
+#define INTROSPECTION_PREFIX Riscv
+#define INTROSPECTION_ENUM_PREFIX RISCV
+#include "define_introspection_and_hwcaps.inl"
+
+////////////////////////////////////////////////////////////////////////////////
+// Implementation.
+////////////////////////////////////////////////////////////////////////////////
+
+#include "internal/stack_line_reader.h"
+#include "internal/filesystem.h"
+
+#include
+#include
+
+static const RiscvInfo kEmptyRiscvInfo;
+
+static bool HandleRiscVLine(const LineResult result,
+ RiscvInfo* const info) {
+ StringView line = result.line;
+ StringView key, value;
+ if (CpuFeatures_StringView_GetAttributeKeyValue(line, &key, &value)) {
+ if (CpuFeatures_StringView_IsEquals(key, str("isa"))) {
+ StringView tmp_sv;
+ value.ptr+=2; // ignore 'rv' prefix at the beginning
+ value.size-=2;
+ for (size_t i = 0; i < RISCV_LAST_; ++i){
+ tmp_sv.ptr = kCpuInfoFlags[i];
+ tmp_sv.size = strlen(kCpuInfoFlags[i]);
+ kSetters[i](&info->features,
+ CpuFeatures_StringView_IndexOf(value, tmp_sv) != -1);
+
+ }
+ }
+ if (CpuFeatures_StringView_IsEquals(key, str("uarch"))){
+ int separatorIdx = CpuFeatures_StringView_IndexOfChar(value, ',');
+ StringView vendor = {.ptr = value.ptr, .size = separatorIdx};
+ StringView uarch = {.ptr = value.ptr + separatorIdx + 1, .size = value.size - separatorIdx - 1};
+
+ CpuFeatures_StringView_CopyString(vendor,info->vendor,sizeof(info->vendor));
+ CpuFeatures_StringView_CopyString(uarch,info->uarch,sizeof(info->uarch));
+ }
+ }
+ return !result.eof;
+}
+
+static void FillProcCpuInfoData(RiscvInfo* const info) {
+ const int fd = CpuFeatures_OpenFile("/proc/cpuinfo");
+ if (fd >= 0) {
+ StackLineReader reader;
+ StackLineReader_Initialize(&reader, fd);
+
+ for(;;){
+ if(!HandleRiscVLine(StackLineReader_NextLine(&reader), info))
+ break;
+ }
+
+ CpuFeatures_CloseFile(fd);
+ }
+}
+
+RiscvInfo GetRiscvInfo(void){
+ RiscvInfo info = kEmptyRiscvInfo;
+ FillProcCpuInfoData(&info);
+
+ if(!*info.vendor)
+ strcpy(info.vendor, "unknown_vendor");
+ if(!*info.uarch)
+ strcpy(info.uarch, "unknown_uarch");
+
+ return info;
+}
+
+#endif // defined(CPU_FEATURES_OS_LINUX) || defined(CPU_FEATURES_OS_ANDROID)
+#endif // CPU_FEATURES_ARCH_RISCV
\ No newline at end of file
diff --git a/src/utils/list_cpu_features.c b/src/utils/list_cpu_features.c
index 4d7fe147..e19decfd 100644
--- a/src/utils/list_cpu_features.c
+++ b/src/utils/list_cpu_features.c
@@ -35,6 +35,8 @@
#include "cpuinfo_mips.h"
#elif defined(CPU_FEATURES_ARCH_PPC)
#include "cpuinfo_ppc.h"
+#elif defined(CPU_FEATURES_ARCH_RISCV)
+#include "cpuinfo_riscv.h"
#endif
// Design principles
@@ -205,6 +207,9 @@ DEFINE_ADD_FLAGS(GetMipsFeaturesEnumValue, GetMipsFeaturesEnumName,
#elif defined(CPU_FEATURES_ARCH_PPC)
DEFINE_ADD_FLAGS(GetPPCFeaturesEnumValue, GetPPCFeaturesEnumName, PPCFeatures,
PPC_LAST_)
+#elif defined(CPU_FEATURES_ARCH_RISCV)
+DEFINE_ADD_FLAGS(GetRiscvFeaturesEnumValue, GetRiscvFeaturesEnumName, RiscvFeatures,
+ RISCV_LAST_)
#endif
// Prints a json string with characters escaping.
@@ -408,6 +413,12 @@ static Node* CreateTree(void) {
AddMapEntry(root, "microarchitecture",
CreateString(strings.type.base_platform));
AddFlags(root, &info.features);
+#elif defined(CPU_FEATURES_ARCH_RISCV)
+ const RiscvInfo info = GetRiscvInfo();
+ AddMapEntry(root, "arch", CreateString("risc-v"));
+ AddMapEntry(root, "vendor", CreateString(info.vendor));
+ AddMapEntry(root, "microarchitecture", CreateString(info.uarch));
+ AddFlags(root, &info.features);
#endif
return root;
}
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 8e8f72af..5e203ce9 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -89,3 +89,10 @@ if(PROCESSOR_IS_POWER)
target_link_libraries(cpuinfo_ppc_test all_libraries)
add_test(NAME cpuinfo_ppc_test COMMAND cpuinfo_ppc_test)
endif()
+##------------------------------------------------------------------------------
+## cpuinfo_riscv_test
+if(PROCESSOR_IS_RISCV)
+ add_executable(cpuinfo_riscv_test cpuinfo_riscv_test.cc ../src/impl_riscv_linux.c)
+ target_link_libraries(cpuinfo_riscv_test all_libraries)
+ add_test(NAME cpuinfo_riscv_test COMMAND cpuinfo_riscv_test)
+endif()
diff --git a/test/cpuinfo_riscv_test.cc b/test/cpuinfo_riscv_test.cc
new file mode 100644
index 00000000..20b3f2fe
--- /dev/null
+++ b/test/cpuinfo_riscv_test.cc
@@ -0,0 +1,123 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cpuinfo_riscv.h"
+
+#include "filesystem_for_testing.h"
+#include "gtest/gtest.h"
+#include "hwcaps_for_testing.h"
+
+namespace cpu_features {
+namespace {
+
+TEST(CpuinfoRiscvTest, LicheeFromCpuInfo) {
+ ResetHwcaps();
+ auto& fs = GetEmptyFilesystem();
+ fs.CreateFile("/proc/cpuinfo", R"(processor : 0
+hart : 0
+isa : rv64imafdc
+mmu : sv39
+uarch : thead,c906)");
+ const auto info = GetRiscvInfo();
+ EXPECT_STREQ(info.uarch, "c906");
+ EXPECT_STREQ(info.vendor, "thead");
+
+ EXPECT_FALSE(info.features.riscv32);
+ EXPECT_TRUE(info.features.riscv64);
+ EXPECT_FALSE(info.features.riscv128);
+ EXPECT_TRUE(info.features.a);
+ EXPECT_TRUE(info.features.c);
+ EXPECT_TRUE(info.features.d);
+ EXPECT_FALSE(info.features.e);
+ EXPECT_TRUE(info.features.f);
+ EXPECT_TRUE(info.features.i);
+ EXPECT_TRUE(info.features.m);
+ EXPECT_FALSE(info.features.q);
+ EXPECT_FALSE(info.features.v);
+}
+
+TEST(CpuinfoRiscvTest, KendryteFromCpuInfo) {
+ ResetHwcaps();
+ auto& fs = GetEmptyFilesystem();
+ fs.CreateFile("/proc/cpuinfo", R"(hart : 0
+isa : rv64i2p0m2p0a2p0f2p0d2p0c2p0xv5-0p0
+mmu : sv39
+
+hart : 1
+isa : rv64i2p0m2p0a2p0f2p0d2p0c2p0xv5-0p0
+mmu : sv39)");
+ const auto info = GetRiscvInfo();
+ EXPECT_STREQ(info.uarch, "unknown_uarch");
+ EXPECT_STREQ(info.vendor, "unknown_vendor");
+
+ EXPECT_FALSE(info.features.riscv32);
+ EXPECT_TRUE(info.features.riscv64);
+ EXPECT_FALSE(info.features.riscv128);
+ EXPECT_TRUE(info.features.a);
+ EXPECT_TRUE(info.features.c);
+ EXPECT_TRUE(info.features.d);
+ EXPECT_FALSE(info.features.e);
+ EXPECT_TRUE(info.features.f);
+ EXPECT_TRUE(info.features.i);
+ EXPECT_TRUE(info.features.m);
+ EXPECT_FALSE(info.features.q);
+ EXPECT_TRUE(info.features.v);
+}
+
+TEST(CpuinfoRiscvTest, UnknownFromCpuInfo) {
+ ResetHwcaps();
+ auto& fs = GetEmptyFilesystem();
+ fs.CreateFile("/proc/cpuinfo", R"(processor : 0
+hart : 2
+isa : rv64imafdc
+mmu : sv39
+uarch : sifive,bullet0
+
+processor : 1
+hart : 1
+isa : rv64imafdc
+mmu : sv39
+uarch : sifive,bullet0
+
+processor : 2
+hart : 3
+isa : rv64imafdc
+mmu : sv39
+uarch : sifive,bullet0
+
+processor : 3
+hart : 4
+isa : rv64imafdc
+mmu : sv39
+uarch : sifive,bullet0)");
+ const auto info = GetRiscvInfo();
+ EXPECT_STREQ(info.uarch, "bullet0");
+ EXPECT_STREQ(info.vendor, "sifive");
+
+ EXPECT_FALSE(info.features.riscv32);
+ EXPECT_TRUE(info.features.riscv64);
+ EXPECT_FALSE(info.features.riscv128);
+ EXPECT_TRUE(info.features.a);
+ EXPECT_TRUE(info.features.c);
+ EXPECT_TRUE(info.features.d);
+ EXPECT_FALSE(info.features.e);
+ EXPECT_TRUE(info.features.f);
+ EXPECT_TRUE(info.features.i);
+ EXPECT_TRUE(info.features.m);
+ EXPECT_FALSE(info.features.q);
+ EXPECT_FALSE(info.features.v);
+}
+
+} // namespace
+} // namespace cpu_features
\ No newline at end of file