Skip to content

Commit 636ec2e

Browse files
committed
RISC-V: Fiber native switch context implemented
1 parent 8812065 commit 636ec2e

File tree

3 files changed

+227
-1
lines changed

3 files changed

+227
-1
lines changed

druntime/Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -393,7 +393,7 @@ $(ROOT)/errno_c$(DOTOBJ) : src/core/stdc/errno.c $(DMD)
393393
@mkdir -p $(dir $@)
394394
$(DMD) -c $(DFLAGS) -I. -P=-I. $< -of$@
395395

396-
$(ROOT)/threadasm$(DOTOBJ) : src/core/thread/fiber/switch_context_asm.S
396+
$(ROOT)/threadasm$(DOTOBJ) : src/core/thread/fiber/switch_context_asm.S src/core/thread/fiber/switch_context_riscv.S
397397
@mkdir -p $(dir $@)
398398
$(CC) -c $(CFLAGS) $< -o$@
399399

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

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

0 commit comments

Comments
 (0)