-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement the ARM64 Windows context switch (#15155)
This is essentially the existing AArch64 context switch, plus the Win32-specific Thread Information Block handling. It makes specs like `spec/std/channel_spec.cr` pass, and the compiler macro run also depends on it.
- Loading branch information
1 parent
a92a6c2
commit c322370
Showing
2 changed files
with
150 additions
and
1 deletion.
There are no files selected for viewing
2 changes: 1 addition & 1 deletion
2
src/fiber/context/aarch64.cr → src/fiber/context/aarch64-generic.cr
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
{% skip_file unless flag?(:aarch64) && flag?(:win32) %} | ||
|
||
class Fiber | ||
# :nodoc: | ||
def makecontext(stack_ptr, fiber_main) : Nil | ||
# ARM64 Windows also follows the AAPCS64 for the most part, except extra | ||
# bookkeeping information needs to be kept in the Thread Information Block, | ||
# referenceable from the x18 register | ||
|
||
# 12 general-purpose registers + 8 FPU registers + 1 parameter + 3 qwords for NT_TIB | ||
@context.stack_top = (stack_ptr - 24).as(Void*) | ||
@context.resumable = 1 | ||
|
||
# actual stack top, not including guard pages and reserved pages | ||
LibC.GetNativeSystemInfo(out system_info) | ||
stack_top = @stack_bottom - system_info.dwPageSize | ||
|
||
stack_ptr[-4] = self.as(Void*) # x0 (r0): puts `self` as first argument for `fiber_main` | ||
stack_ptr[-16] = fiber_main.pointer # x30 (lr): initial `resume` will `ret` to this address | ||
|
||
# The following three values are stored in the Thread Information Block (NT_TIB) | ||
# and are used by Windows to track the current stack limits | ||
stack_ptr[-3] = @stack # [x18, #0x1478]: Win32 DeallocationStack | ||
stack_ptr[-2] = stack_top # [x18, #16]: Stack Limit | ||
stack_ptr[-1] = @stack_bottom # [x18, #8]: Stack Base | ||
end | ||
|
||
# :nodoc: | ||
@[NoInline] | ||
@[Naked] | ||
def self.swapcontext(current_context, new_context) : Nil | ||
# x0 , x1 | ||
|
||
# see also `./aarch64-generic.cr` | ||
{% if compare_versions(Crystal::LLVM_VERSION, "9.0.0") >= 0 %} | ||
asm(" | ||
stp d15, d14, [sp, #-24*8]! | ||
stp d13, d12, [sp, #2*8] | ||
stp d11, d10, [sp, #4*8] | ||
stp d9, d8, [sp, #6*8] | ||
stp x30, x29, [sp, #8*8] // lr, fp | ||
stp x28, x27, [sp, #10*8] | ||
stp x26, x25, [sp, #12*8] | ||
stp x24, x23, [sp, #14*8] | ||
stp x22, x21, [sp, #16*8] | ||
stp x20, x19, [sp, #18*8] | ||
str x0, [sp, #20*8] // push 1st argument | ||
ldr x19, [x18, #0x1478] // Thread Information Block: Win32 DeallocationStack | ||
str x19, [sp, #21*8] | ||
ldr x19, [x18, #16] // Thread Information Block: Stack Limit | ||
str x19, [sp, #22*8] | ||
ldr x19, [x18, #8] // Thread Information Block: Stack Base | ||
str x19, [sp, #23*8] | ||
mov x19, sp // current_context.stack_top = sp | ||
str x19, [x0, #0] | ||
mov x19, #1 // current_context.resumable = 1 | ||
str x19, [x0, #8] | ||
mov x19, #0 // new_context.resumable = 0 | ||
str x19, [x1, #8] | ||
ldr x19, [x1, #0] // sp = new_context.stack_top (x19) | ||
mov sp, x19 | ||
ldr x19, [sp, #23*8] | ||
str x19, [x18, #8] | ||
ldr x19, [sp, #22*8] | ||
str x19, [x18, #16] | ||
ldr x19, [sp, #21*8] | ||
str x19, [x18, #0x1478] | ||
ldr x0, [sp, #20*8] // pop 1st argument (+ alignment) | ||
ldp x20, x19, [sp, #18*8] | ||
ldp x22, x21, [sp, #16*8] | ||
ldp x24, x23, [sp, #14*8] | ||
ldp x26, x25, [sp, #12*8] | ||
ldp x28, x27, [sp, #10*8] | ||
ldp x30, x29, [sp, #8*8] // lr, fp | ||
ldp d9, d8, [sp, #6*8] | ||
ldp d11, d10, [sp, #4*8] | ||
ldp d13, d12, [sp, #2*8] | ||
ldp d15, d14, [sp], #24*8 | ||
// avoid a stack corruption that will confuse the unwinder | ||
mov x16, x30 // save lr | ||
mov x30, #0 // reset lr | ||
br x16 // jump to new pc value | ||
") | ||
{% else %} | ||
# On LLVM < 9.0 using the previous code emits some additional | ||
# instructions that breaks the context switching. | ||
asm(" | ||
stp d15, d14, [sp, #-24*8]! | ||
stp d13, d12, [sp, #2*8] | ||
stp d11, d10, [sp, #4*8] | ||
stp d9, d8, [sp, #6*8] | ||
stp x30, x29, [sp, #8*8] // lr, fp | ||
stp x28, x27, [sp, #10*8] | ||
stp x26, x25, [sp, #12*8] | ||
stp x24, x23, [sp, #14*8] | ||
stp x22, x21, [sp, #16*8] | ||
stp x20, x19, [sp, #18*8] | ||
str x0, [sp, #20*8] // push 1st argument | ||
ldr x19, [x18, #0x1478] // Thread Information Block: Win32 DeallocationStack | ||
str x19, [sp, #21*8] | ||
ldr x19, [x18, #16] // Thread Information Block: Stack Limit | ||
str x19, [sp, #22*8] | ||
ldr x19, [x18, #8] // Thread Information Block: Stack Base | ||
str x19, [sp, #23*8] | ||
mov x19, sp // current_context.stack_top = sp | ||
str x19, [$0, #0] | ||
mov x19, #1 // current_context.resumable = 1 | ||
str x19, [$0, #8] | ||
mov x19, #0 // new_context.resumable = 0 | ||
str x19, [$1, #8] | ||
ldr x19, [$1, #0] // sp = new_context.stack_top (x19) | ||
mov sp, x19 | ||
ldr x19, [sp, #23*8] | ||
str x19, [x18, #8] | ||
ldr x19, [sp, #22*8] | ||
str x19, [x18, #16] | ||
ldr x19, [sp, #21*8] | ||
str x19, [x18, #0x1478] | ||
ldr x0, [sp, #20*8] // pop 1st argument (+ alignment) | ||
ldp x20, x19, [sp, #18*8] | ||
ldp x22, x21, [sp, #16*8] | ||
ldp x24, x23, [sp, #14*8] | ||
ldp x26, x25, [sp, #12*8] | ||
ldp x28, x27, [sp, #10*8] | ||
ldp x30, x29, [sp, #8*8] // lr, fp | ||
ldp d9, d8, [sp, #6*8] | ||
ldp d11, d10, [sp, #4*8] | ||
ldp d13, d12, [sp, #2*8] | ||
ldp d15, d14, [sp], #24*8 | ||
// avoid a stack corruption that will confuse the unwinder | ||
mov x16, x30 // save lr | ||
mov x30, #0 // reset lr | ||
br x16 // jump to new pc value | ||
" :: "r"(current_context), "r"(new_context)) | ||
{% end %} | ||
end | ||
end |