This is an automated email from the ASF dual-hosted git repository.

wwbmmm pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/brpc.git


The following commit(s) were added to refs/heads/master by this push:
     new 2635ef63 Add RISC-V architecture support (#3125)
2635ef63 is described below

commit 2635ef63225ab5b26ed0300b92e8edaaa409d628
Author: Dayuxiaoshui <[email protected]>
AuthorDate: Mon Dec 8 10:55:28 2025 +0800

    Add RISC-V architecture support (#3125)
    
    This commit adds full support for RISC-V 64-bit architecture to brpc.
    
    Changes include:
    - Add RISC-V atomic operations implementation
    - Add RISC-V architecture detection in build system
    - Add RISC-V context switching (bthread support)
    - Add RISC-V clock cycle counter support (rdcycle)
    - Update CMake and Makefile for RISC-V compilation
    
    All core functionalities have been tested and verified in QEMU RISC-V
    environment, including:
    - Atomic operations (32-bit and 64-bit)
    - Memory barriers
    - Context switching
    - Clock cycle counting
    
    Co-authored-by: gong-flying <[email protected]>
---
 CMakeLists.txt                            |   3 +
 Makefile                                  |   4 +
 src/bthread/context.cpp                   |  88 ++++++++++++++
 src/bthread/context.h                     |   3 +
 src/bthread/processor.h                   |   2 +
 src/bthread/task_group.cpp                |   6 +-
 src/bthread/task_group.h                  |   2 +-
 src/butil/atomicops.h                     |   2 +
 src/butil/atomicops_internals_riscv_gcc.h | 192 ++++++++++++++++++++++++++++++
 src/butil/build_config.h                  |  10 ++
 src/butil/time.h                          |   7 ++
 11 files changed, 316 insertions(+), 3 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 9b5db489..6c6f985f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -170,6 +170,9 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
     elseif((CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64"))
         # segmentation fault in libcontext
         set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-gcse")
+    elseif((CMAKE_SYSTEM_PROCESSOR MATCHES "riscv64"))
+        # RISC-V specific optimizations
+        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=rv64gc")
     endif()
     if(NOT (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 7.0))
         set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-aligned-new")
diff --git a/Makefile b/Makefile
index 16a10ae2..abe029e3 100644
--- a/Makefile
+++ b/Makefile
@@ -44,6 +44,10 @@ ifeq ($(shell test $(GCC_VERSION) -ge 40400; echo $$?),0)
     CXXFLAGS+=-msse4 -msse4.2
   endif
 endif
+# RISC-V specific optimizations
+ifeq ($(shell uname -m),riscv64)
+  CXXFLAGS+=-march=rv64gc
+endif
 #not solved yet
 ifeq ($(CC),gcc)
   ifeq ($(shell test $(GCC_VERSION) -ge 70000; echo $$?),0)
diff --git a/src/bthread/context.cpp b/src/bthread/context.cpp
index bafa927d..b7be731e 100644
--- a/src/bthread/context.cpp
+++ b/src/bthread/context.cpp
@@ -900,3 +900,91 @@ __asm (
 );
 
 #endif
+
+#if defined(BTHREAD_CONTEXT_PLATFORM_linux_riscv64) && 
defined(BTHREAD_CONTEXT_COMPILER_gcc)
+__asm (
+".text\n"
+".align  3\n"
+".global bthread_jump_fcontext\n"
+".type   bthread_jump_fcontext, %function\n"
+"bthread_jump_fcontext:\n"
+"    addi    sp, sp, -160\n"
+"    # save callee-saved registers\n"
+"    sd      s0, 64(sp)\n"
+"    sd      s1, 72(sp)\n"
+"    sd      s2, 80(sp)\n"
+"    sd      s3, 88(sp)\n"
+"    sd      s4, 96(sp)\n"
+"    sd      s5, 104(sp)\n"
+"    sd      s6, 112(sp)\n"
+"    sd      s7, 120(sp)\n"
+"    sd      s8, 128(sp)\n"
+"    sd      s9, 136(sp)\n"
+"    sd      s10, 144(sp)\n"
+"    sd      s11, 152(sp)\n"
+"    sd      ra, 0(sp)\n"
+"    sd      fp, 8(sp)\n"
+"    # save floating point registers\n"
+"    fsd     fs0, 16(sp)\n"
+"    fsd     fs1, 24(sp)\n"
+"    fsd     fs2, 32(sp)\n"
+"    fsd     fs3, 40(sp)\n"
+"    fsd     fs4, 48(sp)\n"
+"    fsd     fs5, 56(sp)\n"
+"    # store current stack pointer\n"
+"    sd      sp, 0(a0)\n"
+"    # load new stack pointer\n"
+"    mv      sp, a1\n"
+"    # restore floating point registers\n"
+"    fld     fs0, 16(sp)\n"
+"    fld     fs1, 24(sp)\n"
+"    fld     fs2, 32(sp)\n"
+"    fld     fs3, 40(sp)\n"
+"    fld     fs4, 48(sp)\n"
+"    fld     fs5, 56(sp)\n"
+"    # restore callee-saved registers\n"
+"    ld      s0, 64(sp)\n"
+"    ld      s1, 72(sp)\n"
+"    ld      s2, 80(sp)\n"
+"    ld      s3, 88(sp)\n"
+"    ld      s4, 96(sp)\n"
+"    ld      s5, 104(sp)\n"
+"    ld      s6, 112(sp)\n"
+"    ld      s7, 120(sp)\n"
+"    ld      s8, 128(sp)\n"
+"    ld      s9, 136(sp)\n"
+"    ld      s10, 144(sp)\n"
+"    ld      s11, 152(sp)\n"
+"    ld      ra, 0(sp)\n"
+"    ld      fp, 8(sp)\n"
+"    # restore stack pointer\n"
+"    addi    sp, sp, 160\n"
+"    # return value in a0\n"
+"    mv      a0, a2\n"
+"    # jump to new context\n"
+"    ret\n"
+);
+
+__asm (
+".text\n"
+".align  3\n"
+".global bthread_make_fcontext\n"
+".type   bthread_make_fcontext, %function\n"
+"bthread_make_fcontext:\n"
+"    # align stack to 16-byte boundary\n"
+"    andi    a0, a0, -16\n"
+"    addi    a0, a0, -160\n"
+"    # store function pointer at the top of stack\n"
+"    sd      a2, 0(a0)\n"
+"    # store finish function address\n"
+"    la      t0, finish\n"
+"    sd      t0, 8(a0)\n"
+"    # return pointer to context data\n"
+"    ret\n"
+"finish:\n"
+"    # exit with code 0\n"
+"    li      a0, 0\n"
+"    # call exit\n"
+"    call    _exit\n"
+);
+#endif
diff --git a/src/bthread/context.h b/src/bthread/context.h
index 8de85af6..149c7672 100644
--- a/src/bthread/context.h
+++ b/src/bthread/context.h
@@ -42,6 +42,9 @@
         #elif __loongarch64
             #define BTHREAD_CONTEXT_PLATFORM_linux_loongarch64
             #define BTHREAD_CONTEXT_CALL_CONVENTION
+       #elif __riscv
+           #define BTHREAD_CONTEXT_PLATFORM_linux_riscv64
+           #define BTHREAD_CONTEXT_CALL_CONVENTION
        #endif
 
   #elif defined(__MINGW32__) || defined (__MINGW64__)
diff --git a/src/bthread/processor.h b/src/bthread/processor.h
index f8939234..246c8b93 100644
--- a/src/bthread/processor.h
+++ b/src/bthread/processor.h
@@ -28,6 +28,8 @@
 # ifndef cpu_relax
 #if defined(ARCH_CPU_ARM_FAMILY)
 # define cpu_relax() asm volatile("yield\n": : :"memory")
+#elif defined(ARCH_CPU_RISCV_FAMILY)
+# define cpu_relax() asm volatile("fence.i\n": : :"memory")
 #elif defined(ARCH_CPU_LOONGARCH64_FAMILY)
 # define cpu_relax() asm volatile("nop\n": : :"memory");
 #else
diff --git a/src/bthread/task_group.cpp b/src/bthread/task_group.cpp
index c577b64b..877a5d40 100644
--- a/src/bthread/task_group.cpp
+++ b/src/bthread/task_group.cpp
@@ -101,7 +101,8 @@ AtomicInteger128::Value AtomicInteger128::load() const {
 #endif // __x86_64__
     return {value[0], value[1]};
 #else // __x86_64__ || __ARM_NEON
-    BAIDU_SCOPED_LOCK(_mutex);
+    // RISC-V and other architectures use mutex fallback
+    BAIDU_SCOPED_LOCK(const_cast<FastPthreadMutex&>(_mutex));
     return _value;
 #endif // __x86_64__ || __ARM_NEON
 }
@@ -114,7 +115,8 @@ void AtomicInteger128::store(Value value) {
     int64x2_t v = vld1q_s64(reinterpret_cast<int64_t*>(&value));
     vst1q_s64(reinterpret_cast<int64_t*>(&_value), v);
 #else
-    BAIDU_SCOPED_LOCK(_mutex);
+    // RISC-V and other architectures use mutex fallback
+    BAIDU_SCOPED_LOCK(const_cast<FastPthreadMutex&>(_mutex));
     _value = value;
 #endif // __x86_64__ || __ARM_NEON
 }
diff --git a/src/bthread/task_group.h b/src/bthread/task_group.h
index f3e1d402..54140c0d 100644
--- a/src/bthread/task_group.h
+++ b/src/bthread/task_group.h
@@ -73,7 +73,7 @@ public:
 
 private:
     Value _value{};
-    // Used to protect `_cpu_time_stat' when __x86_64__ and __ARM_NEON is not 
defined.
+    // Used to protect `_cpu_time_stat' when __x86_64__, __ARM_NEON, and 
__riscv is not defined.
     FastPthreadMutex _mutex;
 };
 
diff --git a/src/butil/atomicops.h b/src/butil/atomicops.h
index cda1529f..7ee38377 100644
--- a/src/butil/atomicops.h
+++ b/src/butil/atomicops.h
@@ -157,6 +157,8 @@ Atomic64 Release_Load(volatile const Atomic64* ptr);
 #include "butil/atomicops_internals_mips_gcc.h"
 #elif defined(COMPILER_GCC) && defined(ARCH_CPU_LOONGARCH64_FAMILY)
 #include "butil/atomicops_internals_loongarch64_gcc.h"
+#elif defined(COMPILER_GCC) && defined(ARCH_CPU_RISCV_FAMILY)
+#include "butil/atomicops_internals_riscv_gcc.h"
 #else
 #error "Atomic operations are not supported on your platform"
 #endif
diff --git a/src/butil/atomicops_internals_riscv_gcc.h 
b/src/butil/atomicops_internals_riscv_gcc.h
new file mode 100644
index 00000000..e7bd78bb
--- /dev/null
+++ b/src/butil/atomicops_internals_riscv_gcc.h
@@ -0,0 +1,192 @@
+// Copyright 2024 The Apache Software Foundation. All rights reserved.
+// Use of this source code is governed by the Apache License, Version 2.0
+// that can be found in the LICENSE file.
+
+// This file is an internal atomic implementation, use butil/atomicops.h 
instead.
+// RISC-V architecture specific atomic operations implementation using GCC 
intrinsics.
+
+#ifndef BUTIL_ATOMICOPS_INTERNALS_RISCV_GCC_H_
+#define BUTIL_ATOMICOPS_INTERNALS_RISCV_GCC_H_
+
+namespace butil {
+namespace subtle {
+
+inline void MemoryBarrier() {
+  __asm__ __volatile__ ("fence" ::: "memory");  // NOLINT
+}
+
+// RISC-V atomic operations using GCC built-in functions
+// These are implemented using the standard GCC atomic built-ins which
+// are supported on RISC-V since GCC 7.1+
+
+inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr,
+                                         Atomic32 old_value,
+                                         Atomic32 new_value) {
+  Atomic32 prev_value;
+  do {
+    if (__sync_bool_compare_and_swap(ptr, old_value, new_value))
+      return old_value;
+    prev_value = *ptr;
+  } while (prev_value == old_value);
+  return prev_value;
+}
+
+inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr,
+                                         Atomic32 new_value) {
+  Atomic32 old_value;
+  do {
+    old_value = *ptr;
+  } while (!__sync_bool_compare_and_swap(ptr, old_value, new_value));
+  return old_value;
+}
+
+inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr,
+                                          Atomic32 increment) {
+  return Barrier_AtomicIncrement(ptr, increment);
+}
+
+inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr,
+                                        Atomic32 increment) {
+  for (;;) {
+    // Atomic exchange the old value with an incremented one.
+    Atomic32 old_value = *ptr;
+    Atomic32 new_value = old_value + increment;
+    if (__sync_bool_compare_and_swap(ptr, old_value, new_value)) {
+      // The exchange took place as expected.
+      return new_value;
+    }
+    // Otherwise, *ptr changed mid-loop and we need to retry.
+  }
+}
+
+inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr,
+                                       Atomic32 old_value,
+                                       Atomic32 new_value) {
+  // Since NoBarrier_CompareAndSwap uses __sync_bool_compare_and_swap, which
+  // is a full memory barrier, none is needed here or below in Release.
+  return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
+}
+
+inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr,
+                                       Atomic32 old_value,
+                                       Atomic32 new_value) {
+  return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
+}
+
+inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) {
+  *ptr = value;
+}
+
+inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) {
+  *ptr = value;
+  MemoryBarrier();
+}
+
+inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) {
+  MemoryBarrier();
+  *ptr = value;
+}
+
+inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) {
+  return *ptr;
+}
+
+inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) {
+  Atomic32 value = *ptr;
+  MemoryBarrier();
+  return value;
+}
+
+inline Atomic32 Release_Load(volatile const Atomic32* ptr) {
+  MemoryBarrier();
+  return *ptr;
+}
+
+// 64-bit versions of the operations.
+// See the 32-bit versions for comments.
+
+inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr,
+                                         Atomic64 old_value,
+                                         Atomic64 new_value) {
+  Atomic64 prev_value;
+  do {
+    if (__sync_bool_compare_and_swap(ptr, old_value, new_value))
+      return old_value;
+    prev_value = *ptr;
+  } while (prev_value == old_value);
+  return prev_value;
+}
+
+inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr,
+                                         Atomic64 new_value) {
+  Atomic64 old_value;
+  do {
+    old_value = *ptr;
+  } while (!__sync_bool_compare_and_swap(ptr, old_value, new_value));
+  return old_value;
+}
+
+inline Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64* ptr,
+                                          Atomic64 increment) {
+  return Barrier_AtomicIncrement(ptr, increment);
+}
+
+inline Atomic64 Barrier_AtomicIncrement(volatile Atomic64* ptr,
+                                        Atomic64 increment) {
+  for (;;) {
+    // Atomic exchange the old value with an incremented one.
+    Atomic64 old_value = *ptr;
+    Atomic64 new_value = old_value + increment;
+    if (__sync_bool_compare_and_swap(ptr, old_value, new_value)) {
+      // The exchange took place as expected.
+      return new_value;
+    }
+    // Otherwise, *ptr changed mid-loop and we need to retry.
+  }
+}
+
+inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr,
+                                       Atomic64 old_value,
+                                       Atomic64 new_value) {
+  return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
+}
+
+inline Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr,
+                                       Atomic64 old_value,
+                                       Atomic64 new_value) {
+  return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
+}
+
+inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) {
+  *ptr = value;
+}
+
+inline void Acquire_Store(volatile Atomic64* ptr, Atomic64 value) {
+  *ptr = value;
+  MemoryBarrier();
+}
+
+inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) {
+  MemoryBarrier();
+  *ptr = value;
+}
+
+inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) {
+  return *ptr;
+}
+
+inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) {
+  Atomic64 value = *ptr;
+  MemoryBarrier();
+  return value;
+}
+
+inline Atomic64 Release_Load(volatile const Atomic64* ptr) {
+  MemoryBarrier();
+  return *ptr;
+}
+
+}  // namespace butil::subtle
+}  // namespace butil
+
+#endif  // BUTIL_ATOMICOPS_INTERNALS_RISCV_GCC_H_
diff --git a/src/butil/build_config.h b/src/butil/build_config.h
index 5ddf3821..18d449b4 100644
--- a/src/butil/build_config.h
+++ b/src/butil/build_config.h
@@ -138,6 +138,16 @@
 #define ARCH_CPU_LOONGARCH64 1
 #define ARCH_CPU_64_BITS 1
 #define ARCH_CPU_LITTLE_ENDIAN 1
+#elif defined(__riscv)
+#define ARCH_CPU_RISCV_FAMILY 1
+#if defined(__riscv_xlen) && (__riscv_xlen == 64)
+#define ARCH_CPU_RISCV64 1
+#define ARCH_CPU_64_BITS 1
+#else
+#define ARCH_CPU_RISCV32 1
+#define ARCH_CPU_32_BITS 1
+#endif
+#define ARCH_CPU_LITTLE_ENDIAN 1
 #else
 #error Please add support for your architecture in butil/build_config.h
 #endif
diff --git a/src/butil/time.h b/src/butil/time.h
index 8b856998..c57000ea 100644
--- a/src/butil/time.h
+++ b/src/butil/time.h
@@ -254,6 +254,13 @@ inline uint64_t clock_cycles() {
         : "=r" (stable_counter), "=r" (counter_id)
         );
     return stable_counter;
+#elif defined(__riscv)
+    uint64_t cycles;
+    __asm__ __volatile__ (
+        "rdcycle %0"
+        : "=r" (cycles)
+        );
+    return cycles;
 #else
   #error "unsupported arch"
 #endif


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to