Skip to content

Commit bc02f9f

Browse files
committed
RISC-V Fiber context switching native support
1 parent c138058 commit bc02f9f

File tree

3 files changed

+224
-1
lines changed

3 files changed

+224
-1
lines changed

runtime/CMakeLists.txt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,11 @@ endif()
233233
# druntime ASM parts
234234
set(DRUNTIME_ASM)
235235
if("${TARGET_SYSTEM}" MATCHES "UNIX")
236-
list(APPEND DRUNTIME_ASM ${RUNTIME_DIR}/src/core/threadasm.S ${RUNTIME_DIR}/src/ldc/eh_asm.S)
236+
list(APPEND DRUNTIME_ASM
237+
${RUNTIME_DIR}/src/core/threadasm.S
238+
${RUNTIME_DIR}/src/core/thread/fiber/switch_context_riscv.S
239+
${RUNTIME_DIR}/src/ldc/eh_asm.S
240+
)
237241
endif()
238242

239243
if(PHOBOS2_DIR)

runtime/druntime/src/core/thread/fiber.d

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,16 @@ private
160160
version = AsmExternal;
161161
}
162162
}
163+
else version (RISCV32)
164+
{
165+
version = RISCV_Any;
166+
version = AsmExternal;
167+
}
168+
else version (RISCV64)
169+
{
170+
version = RISCV_Any;
171+
version = AsmExternal;
172+
}
163173
else version (SPARC)
164174
{
165175
// NOTE: The SPARC ABI specifies only doubleword alignment.
@@ -247,6 +257,13 @@ private
247257
extern (C) void fiber_trampoline() nothrow;
248258
version (LoongArch64)
249259
extern (C) void fiber_trampoline() nothrow;
260+
version (RISCV_Any)
261+
{
262+
// External asm stack initialization is used to support different register
263+
// storage sizes that the D compiler does not know about
264+
extern (C) static void* fiber_initStack(void* stack, void* entry) nothrow @nogc;
265+
extern (C) void fiber_trampoline() nothrow;
266+
}
250267
}
251268
else version (LDC_Windows)
252269
{
@@ -2055,6 +2072,13 @@ private:
20552072
*/
20562073
pstack += int.sizeof * 1;
20572074
}
2075+
else version (RISCV_Any)
2076+
{
2077+
version (StackGrowsDown) {}
2078+
else static assert(false, "RISC-V only supports decrementing stacks");
2079+
2080+
pstack = fiber_initStack(pstack, &fiber_trampoline);
2081+
}
20582082
else static if ( __traits( compiles, ucontext_t ) )
20592083
{
20602084
getcontext( &m_utxt );
Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
/**
2+
* Support code for RISC-V fibers.
3+
*
4+
* Copyright: Copyright Denis Feklushkin 2025.
5+
* License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
6+
* Authors: Denis Feklushkin
7+
*/
8+
9+
#if defined(__riscv)
10+
11+
// For saving a register in memory, regardless of the size of machine register bit size
12+
#if(__riscv_xlen == 32)
13+
#define save sw
14+
#define fsave fsw
15+
#else // rv64
16+
#define save sd
17+
#define fsave fsd
18+
#endif
19+
20+
// Ditto, but for loading
21+
#if(__riscv_xlen == 32)
22+
#define load lw
23+
#define fload flw
24+
#else // rv64
25+
#define load ld
26+
#define fload fld
27+
#endif
28+
29+
// Integer register size, bytes
30+
reg_s = __riscv_xlen / 8
31+
32+
// Floating register size, bytes
33+
#if defined(__riscv_flen)
34+
freg_s = __riscv_flen / 8
35+
#else
36+
freg_s = 0 // hard float is not supported
37+
#endif
38+
39+
ints_storage_size = reg_s * 12 // all callee-saved integer registers (embedded ABI isn't supported for now)
40+
floats_storage_size = freg_s * 12 // all callee-saved float registers
41+
42+
/**
43+
* Parameters:
44+
* a0 - void* - pointer to a new stack
45+
* a1 - void* - pointer to the entry point
46+
*
47+
* Returns:
48+
* a0 - void* - modified new stack pointer
49+
*/
50+
.text
51+
.globl fiber_initStack
52+
.type fiber_initStack, @function
53+
fiber_initStack:
54+
// At this point assumed that memory for the stack is already allocated (but not zeroed)
55+
56+
// adjust stack pointer
57+
addi a0, a0, -ints_storage_size
58+
59+
// store entry point start address as saved ra register below stack pointer
60+
save a1, -reg_s(a0)
61+
62+
ret
63+
64+
.text
65+
.globl fiber_trampoline
66+
.type fiber_trampoline, @function
67+
fiber_trampoline:
68+
.cfi_startproc // necessary for .eh_frame
69+
// discard ra value - fiber_entryPoint never returns
70+
.cfi_undefined ra
71+
72+
// non-returnable jump (i.e., a non-unwinding tail-call) to fiber_entryPoint
73+
tail fiber_entryPoint
74+
.cfi_endproc
75+
76+
/**
77+
* Parameters:
78+
* a0 - void** - ptr to old stack pointer
79+
* a1 - void* - new stack pointer
80+
*
81+
* RISCV ABI registers:
82+
* x0 zero : hardwired to zero
83+
* x1 ra : return address
84+
* x2 sp : stack pointer
85+
* x3 gp : global pointer (variables are ‘relaxed’ and accessed via a relative imm offset from the gp)
86+
* x4 tp : thread pointer
87+
* x5-x7 t0-t2 : temporary/scratch registers
88+
* x8 s0/fp : callee-saved register 0 AKA frame pointer
89+
* x9 s1 : callee-saved register 1
90+
* x10-x17 a0-a7 : function arguments
91+
* x18-x27 s2-s11 : callee-saved registers
92+
* x28-x31 t3-t6 : temporary/scratch registers
93+
*
94+
* (floating registers omitted)
95+
*/
96+
.text
97+
.globl fiber_switchContext
98+
.type fiber_switchContext, @function
99+
fiber_switchContext:
100+
101+
// Reserve space on the stack to store registers
102+
// Moving stack pointer so hardware stack size checker can make sure
103+
// that stack boundary are not violated
104+
addi sp, sp, -(ints_storage_size + floats_storage_size + reg_s /*additional space for ra register*/)
105+
106+
// Move stack pointer back a little and store ra and floats above of
107+
// the stack border to avoid GC scan them in the stack frame
108+
addi sp, sp, reg_s /*excluded ra*/ + floats_storage_size
109+
110+
// ra stored above of the current stack
111+
save ra, -(1 * reg_s)(sp)
112+
113+
#if defined(__riscv_flen)
114+
// Floats also stored above of the current stack.
115+
//
116+
// For the convenience of manual verification counting is shifted so
117+
// that in most cases register names match the offsets (except the last one).
118+
//
119+
// Shift (by ra register size) is added in addition to multiplication due
120+
// to the fact that the sizes of integer and float registers can differ.
121+
fsave fs1, -(1 * freg_s + reg_s)(sp)
122+
fsave fs2, -(2 * freg_s + reg_s)(sp)
123+
fsave fs3, -(3 * freg_s + reg_s)(sp)
124+
fsave fs4, -(4 * freg_s + reg_s)(sp)
125+
fsave fs5, -(5 * freg_s + reg_s)(sp)
126+
fsave fs6, -(6 * freg_s + reg_s)(sp)
127+
fsave fs7, -(7 * freg_s + reg_s)(sp)
128+
fsave fs8, -(8 * freg_s + reg_s)(sp)
129+
fsave fs9, -(9 * freg_s + reg_s)(sp)
130+
fsave fs10, -(10 * freg_s + reg_s)(sp)
131+
fsave fs11, -(11 * freg_s + reg_s)(sp)
132+
fsave fs0, -(12 * freg_s + reg_s)(sp)
133+
#endif
134+
135+
// Integer register data stored on the stack in the usual way
136+
save s0, (0 * reg_s)(sp)
137+
save s1, (1 * reg_s)(sp)
138+
save s2, (2 * reg_s)(sp)
139+
save s3, (3 * reg_s)(sp)
140+
save s4, (4 * reg_s)(sp)
141+
save s5, (5 * reg_s)(sp)
142+
save s6, (6 * reg_s)(sp)
143+
save s7, (7 * reg_s)(sp)
144+
save s8, (8 * reg_s)(sp)
145+
save s9, (9 * reg_s)(sp)
146+
save s10, (10 * reg_s)(sp)
147+
save s11, (11 * reg_s)(sp)
148+
149+
// Save current sp to oldp
150+
save sp, (a0)
151+
152+
// Load sp from newp
153+
addi sp, a1, 0
154+
155+
// Load ra from above of the stack border
156+
load ra, -(1 * reg_s)(sp)
157+
158+
#if defined(__riscv_flen)
159+
// Loading floats
160+
fload fs1, -(1 * freg_s + reg_s)(sp)
161+
fload fs2, -(2 * freg_s + reg_s)(sp)
162+
fload fs3, -(3 * freg_s + reg_s)(sp)
163+
fload fs4, -(4 * freg_s + reg_s)(sp)
164+
fload fs5, -(5 * freg_s + reg_s)(sp)
165+
fload fs6, -(6 * freg_s + reg_s)(sp)
166+
fload fs7, -(7 * freg_s + reg_s)(sp)
167+
fload fs8, -(8 * freg_s + reg_s)(sp)
168+
fload fs9, -(9 * freg_s + reg_s)(sp)
169+
fload fs10, -(10 * freg_s + reg_s)(sp)
170+
fload fs11, -(11 * freg_s + reg_s)(sp)
171+
fload fs0, -(12 * freg_s + reg_s)(sp)
172+
#endif
173+
174+
// Load registers from obtained stack
175+
load s0, (0 * reg_s)(sp)
176+
load s1, (1 * reg_s)(sp)
177+
load s2, (2 * reg_s)(sp)
178+
load s3, (3 * reg_s)(sp)
179+
load s4, (4 * reg_s)(sp)
180+
load s5, (5 * reg_s)(sp)
181+
load s6, (6 * reg_s)(sp)
182+
load s7, (7 * reg_s)(sp)
183+
load s8, (8 * reg_s)(sp)
184+
load s9, (9 * reg_s)(sp)
185+
load s10, (10 * reg_s)(sp)
186+
load s11, (11 * reg_s)(sp)
187+
188+
// Freeing stack
189+
// (Floats storage was "freed" before floats was actually stored)
190+
addi sp, sp, ints_storage_size
191+
192+
// Return
193+
jr ra
194+
195+
#endif

0 commit comments

Comments
 (0)