diff --git a/docs/environment_variables.md b/docs/environment_variables.md index dd5d59fbe..4eb096595 100644 --- a/docs/environment_variables.md +++ b/docs/environment_variables.md @@ -40,6 +40,9 @@ By default the cpu is detected and the most advanced form of SIMD is used. But LIBYUV_DISABLE_LSX LIBYUV_DISABLE_LASX +## RISCV CPUs + LIBYUV_DISABLE_RVV + # Test Width/Height/Repeat The unittests default to a small image (128x72) to run fast. This can be set by environment variable to test a specific resolutions. diff --git a/include/libyuv/cpu_id.h b/include/libyuv/cpu_id.h index fb90c6c73..203f7e0d6 100644 --- a/include/libyuv/cpu_id.h +++ b/include/libyuv/cpu_id.h @@ -55,6 +55,11 @@ static const int kCpuHasLOONGARCH = 0x2000000; static const int kCpuHasLSX = 0x4000000; static const int kCpuHasLASX = 0x8000000; +// These flags are only valid on RISCV processors. +static const int kCpuHasRISCV = 0x10000000; +static const int kCpuHasRVV = 0x20000000; +static const int kCpuHasRVVZVFH = 0x40000000; + // Optional init function. TestCpuFlag does an auto-init. // Returns cpu_info flags. LIBYUV_API @@ -78,6 +83,8 @@ LIBYUV_API int ArmCpuCaps(const char* cpuinfo_name); LIBYUV_API int MipsCpuCaps(const char* cpuinfo_name); +LIBYUV_API +int RiscvCpuCaps(const char* cpuinfo_name); // For testing, allow CPU flags to be disabled. // ie MaskCpuFlags(~kCpuHasSSSE3) to disable SSSE3. diff --git a/source/cpu_id.cc b/source/cpu_id.cc index 13e3da7bb..409456e8f 100644 --- a/source/cpu_id.cc +++ b/source/cpu_id.cc @@ -191,6 +191,63 @@ LIBYUV_API SAFEBUFFERS int MipsCpuCaps(const char* cpuinfo_name) { return flag; } +LIBYUV_API SAFEBUFFERS int RiscvCpuCaps(const char* cpuinfo_name) { + char cpuinfo_line[512]; + int flag = 0x0; + FILE* f = fopen(cpuinfo_name, "r"); + if (!f) { + // Assume nothing if /proc/cpuinfo is unavailable. + // This will occur for Chrome sandbox for Pepper or Render process. + return 0; + } + while (fgets(cpuinfo_line, sizeof(cpuinfo_line) - 1, f)) { + if (memcmp(cpuinfo_line, "isa", 3) == 0) { + // ISA string must begin with rv64{i,e,g} for a 64-bit processor. + char* isa = strstr(cpuinfo_line, "rv64"); + if (isa) { + const int isa_len = strlen(isa); + // 5 ISA characters + 1 new-line character + if (isa_len < 6) { + fclose(f); + return 0; + } + // Skip {i,e,g} canonical checking. + // Skip rvxxx + isa += 5; + + // Find the very first occurrence of 's', 'x' or 'z'. + // To detect multi-letter standard, non-standard, and + // supervisor-level extensions. + int otherExts_len = 0; + char* otherExts = strpbrk(isa, "zxs"); + if (otherExts) { + otherExts_len = strlen(otherExts); + // Multi-letter extensions are seperated by a single underscore + // as described in RISC-V User-Level ISA V2.2. + char* ext = strtok(otherExts, "_"); + while (ext) { + // Search for the ZVFH (Vector FP16) extension. + // The ZVFH implied the (Scalar FP16)ZFH extension. + if (!strcmp(ext, "zvfh") || !strcmp(ext, "zvfh\n")) { + flag |= kCpuHasRVVZVFH; + } + ext = strtok(NULL, "_"); + } + } + const int std_isa_len = isa_len - otherExts_len - 5 - 1; + // Detect the v in the standard single-letter extensions. + // Skip optional Zve* and Zvl* extensions detection at otherExts. + if (memchr(isa, 'v', std_isa_len)) { + // The RVV implied the F extension. + flag |= kCpuHasRVV; + } + } + } + } + fclose(f); + return flag; +} + // TODO(fbarchard): Consider read_loongarch_ir(). #define LOONGARCH_CFG2 0x2 #define LOONGARCH_CFG2_LSX (1 << 6) @@ -277,6 +334,10 @@ static SAFEBUFFERS int GetCpuFlags(void) { #endif cpu_info |= kCpuHasARM; #endif // __arm__ +#if defined(__riscv) && defined(__linux__) + cpu_info = RiscvCpuCaps("/proc/cpuinfo"); + cpu_info |= kCpuHasRISCV; +#endif // __riscv cpu_info |= kCpuInitialized; return cpu_info; } diff --git a/unit_test/cpu_test.cc b/unit_test/cpu_test.cc index 080778f5f..3bd159f78 100644 --- a/unit_test/cpu_test.cc +++ b/unit_test/cpu_test.cc @@ -26,6 +26,14 @@ TEST_F(LibYUVBaseTest, TestCpuHas) { printf("Has ARM %d\n", has_arm); int has_neon = TestCpuFlag(kCpuHasNEON); printf("Has NEON %d\n", has_neon); +#endif +#if defined(__riscv) && defined(__linux__) + int has_riscv = TestCpuFlag(kCpuHasRISCV); + printf("Has RISCV %d\n", has_riscv); + int has_rvv = TestCpuFlag(kCpuHasRVV); + printf("Has RVV %d\n", has_rvv); + int has_rvvzvfh = TestCpuFlag(kCpuHasRVVZVFH); + printf("Has RVVZVFH %d\n", has_rvvzvfh); #endif int has_x86 = TestCpuFlag(kCpuHasX86); int has_sse2 = TestCpuFlag(kCpuHasSSE2); @@ -164,6 +172,9 @@ TEST_F(LibYUVBaseTest, TestCompilerMacros) { #ifdef _WIN32 printf("_WIN32 %d\n", _WIN32); #endif +#ifdef __riscv + printf("__riscv %d\n", __riscv); +#endif #ifdef GG_LONGLONG printf("GG_LONGLONG %d\n", GG_LONGLONG); #endif @@ -264,6 +275,32 @@ TEST_F(LibYUVBaseTest, TestLinuxMipsMsa) { } } +TEST_F(LibYUVBaseTest, TestLinuxRVV) { + if (FileExists("../../unit_test/testdata/riscv64.txt")) { + printf("Note: testing to load \"../../unit_test/testdata/riscv64.txt\"\n"); + + EXPECT_EQ(0, RiscvCpuCaps("../../unit_test/testdata/riscv64.txt")); + EXPECT_EQ(kCpuHasRVV, + RiscvCpuCaps("../../unit_test/testdata/riscv64_rvv.txt")); + EXPECT_EQ(kCpuHasRVV | kCpuHasRVVZVFH, + RiscvCpuCaps("../../unit_test/testdata/riscv64_rvv_zvfh.txt")); + } else { + printf( + "WARNING: unable to load " + "\"../../unit_test/testdata/riscv64.txt\"\n"); + } +#if defined(__linux__) && defined(__riscv) + if (FileExists("/proc/cpuinfo")) { + if (!(kCpuHasRVV & RiscvCpuCaps("/proc/cpuinfo"))) { + // This can happen on RVV emulator but /proc/cpuinfo is from host. + printf("WARNING: RVV build enabled but CPU does not have RVV\n"); + } + } else { + printf("WARNING: unable to load \"/proc/cpuinfo\"\n"); + } +#endif +} + // TODO(fbarchard): Fix clangcl test of cpuflags. #ifdef _MSC_VER TEST_F(LibYUVBaseTest, DISABLED_TestSetCpuFlags) { diff --git a/unit_test/testdata/riscv64.txt b/unit_test/testdata/riscv64.txt new file mode 100644 index 000000000..fbb4200fc --- /dev/null +++ b/unit_test/testdata/riscv64.txt @@ -0,0 +1,4 @@ +processor : 0 +hart : 1 +isa : rv64imac +mmu : sv48 \ No newline at end of file diff --git a/unit_test/testdata/riscv64_rvv.txt b/unit_test/testdata/riscv64_rvv.txt new file mode 100644 index 000000000..af1b3f366 --- /dev/null +++ b/unit_test/testdata/riscv64_rvv.txt @@ -0,0 +1,4 @@ +processor : 0 +hart : 1 +isa : rv64imafdcv +mmu : sv48 \ No newline at end of file diff --git a/unit_test/testdata/riscv64_rvv_zvfh.txt b/unit_test/testdata/riscv64_rvv_zvfh.txt new file mode 100644 index 000000000..c416c1aff --- /dev/null +++ b/unit_test/testdata/riscv64_rvv_zvfh.txt @@ -0,0 +1,4 @@ +processor : 0 +hart : 1 +isa : rv64imafdcv_zfh_zvfh +mmu : sv48 \ No newline at end of file diff --git a/unit_test/unit_test.cc b/unit_test/unit_test.cc index 61145a462..b66ebfab4 100644 --- a/unit_test/unit_test.cc +++ b/unit_test/unit_test.cc @@ -88,6 +88,11 @@ int TestCpuEnv(int cpu_info) { cpu_info &= ~libyuv::kCpuHasLASX; } #endif +#if defined(__riscv) && defined(__linux__) + if (TestEnv("LIBYUV_DISABLE_RVV")) { + cpu_info &= ~libyuv::kCpuHasRVV; + } +#endif #if !defined(__pnacl__) && !defined(__CLR_VER) && \ (defined(__x86_64__) || defined(_M_X64) || defined(__i386__) || \ defined(_M_IX86))