Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
135 changes: 54 additions & 81 deletions src/fiber/context/arm.cr
Original file line number Diff line number Diff line change
Expand Up @@ -21,100 +21,73 @@ class Fiber
@[NoInline]
@[Naked]
def self.swapcontext(current_context, new_context) : Nil
# r0 , r1

# ARM assembly requires integer literals to be moved to a register before
# being stored at an address; we use r4 as a scratch register that will be
# overwritten by the new context.
#
# Eventually reset LR to zero to avoid the ARM unwinder to mistake the
# context switch as a regular call.

{% if flag?(:armhf) %}
# r0 , r1
{% if compare_versions(Crystal::LLVM_VERSION, "9.0.0") >= 0 %}
asm("
// declare the presence of a conservative FPU to the ASM compiler
.fpu vfp

stmdb sp!, {r0, r4-r11, lr} // push 1st argument + callee-saved registers
vstmdb sp!, {d8-d15} // push FPU registers
str sp, [r0, #0] // current_context.stack_top = sp
mov r4, #1 // current_context.resumable = 1
str r4, [r0, #4]

mov r4, #0 // new_context.resumable = 0
str r4, [r1, #4]
ldr sp, [r1, #0] // sp = new_context.stack_top
vldmia sp!, {d8-d15} // pop FPU registers
ldmia sp!, {r0, r4-r11, lr} // pop 1st argument + callee-saved registers
{% if compare_versions(Crystal::LLVM_VERSION, "9.0.0") >= 0 %}
asm("
{% if flag?(:armhf) %}
// declare the presence of a conservative FPU to the ASM compiler
.fpu vfp
{% end %}

// avoid a stack corruption that will confuse the unwinder
mov r1, lr
mov lr, #0
mov pc, r1
")
{% else %}
# On LLVM < 9.0 using the previous code emits some additional
# instructions that breaks the context switching.
asm("
// declare the presence of a conservative FPU to the ASM compiler
.fpu vfp
stmdb sp!, {r0, r4-r11, lr} // push 1st argument + callee-saved registers
{% if flag?(:armhf) %}
vstmdb sp!, {d8-d15} // push FPU registers
{% end %}
str sp, [r0, #0] // current_context.stack_top = sp
mov r4, #1 // current_context.resumable = 1
str r4, [r0, #4]

stmdb sp!, {r0, r4-r11, lr} // push 1st argument + callee-saved registers
vstmdb sp!, {d8-d15} // push FPU registers
str sp, [$0, #0] // current_context.stack_top = sp
mov r4, #1 // current_context.resumable = 1
str r4, [$0, #4]
mov r4, #0 // new_context.resumable = 0
str r4, [r1, #4]
ldr sp, [r1, #0] // sp = new_context.stack_top
{% if flag?(:armhf) %}
vldmia sp!, {d8-d15} // pop FPU registers
{% end %}
ldmia sp!, {r0, r4-r11, lr} // pop 1st argument + callee-saved registers

mov r4, #0 // new_context.resumable = 0
str r4, [$1, #4]
ldr sp, [$1, #0] // sp = new_context.stack_top
vldmia sp!, {d8-d15} // pop FPU registers
ldmia sp!, {r0, r4-r11, lr} // pop 1st argument + callee-saved registers

// avoid a stack corruption that will confuse the unwinder
mov r1, lr
mov lr, #0
mov pc, r1
" :: "r"(current_context), "r"(new_context))
{% end %}
{% elsif flag?(:arm) %}
{% if compare_versions(Crystal::LLVM_VERSION, "9.0.0") >= 0 %}
asm("
stmdb sp!, {r0, r4-r11, lr} // push 1st argument + callee-saved registers
str sp, [r0, #0] // current_context.stack_top = sp
mov r4, #1 // current_context.resumable = 1
str r4, [r0, #4]

mov r4, #0 // new_context.resumable = 0
str r4, [r1, #4]
ldr sp, [r1, #0] // sp = new_context.stack_top
ldmia sp!, {r0, r4-r11, lr} // pop 1st argument + callee-saved registers
// avoid a stack corruption that will confuse the unwinder
mov r1, lr
mov lr, #0
mov pc, r1
")
{% else %}
# On LLVM < 9.0 using the previous code emits some additional
# instructions that breaks the context switching.
asm("
{% if flag?(:armhf) %}
// declare the presence of a conservative FPU to the ASM compiler
.fpu vfp
{% end %}

// avoid a stack corruption that will confuse the unwinder
mov r1, lr
mov lr, #0
mov pc, r1
")
{% else %}
# On LLVM < 9.0 using the previous code emits some additional
# instructions that breaks the context switching.
asm("
stmdb sp!, {r0, r4-r11, lr} // push 1st argument + callee-saved registers
str sp, [$0, #0] // current_context.stack_top = sp
mov r4, #1 // current_context.resumable = 1
str r4, [$0, #4]
stmdb sp!, {r0, r4-r11, lr} // push 1st argument + callee-saved registers
{% if flag?(:armhf) %}
vstmdb sp!, {d8-d15} // push FPU registers
{% end %}
str sp, [$0, #0] // current_context.stack_top = sp
mov r4, #1 // current_context.resumable = 1
str r4, [$0, #4]

mov r4, #0 // new_context.resumable = 0
str r4, [$1, #4]
ldr sp, [$1, #0] // sp = new_context.stack_top
ldmia sp!, {r0, r4-r11, lr} // pop 1st argument + callee-saved registers
mov r4, #0 // new_context.resumable = 0
str r4, [$1, #4]
ldr sp, [$1, #0] // sp = new_context.stack_top
{% if flag?(:armhf) %}
vldmia sp!, {d8-d15} // pop FPU registers
{% end %}
ldmia sp!, {r0, r4-r11, lr} // pop 1st argument + callee-saved registers

// avoid a stack corruption that will confuse the unwinder
mov r1, lr
mov lr, #0
mov pc, r1
" :: "r"(current_context), "r"(new_context))
{% end %}
// avoid a stack corruption that will confuse the unwinder
mov r1, lr
mov lr, #0
mov pc, r1
" :: "r"(current_context), "r"(new_context))
{% end %}
end
end
Loading