Introduction to the

Swift_Boost_Context is a foundational library that provides a sort of cooperative multitasking on a single thread. By providing an abstraction of the current execution state in the current thread, including the stack (with local variables) and stack pointer, all registers and CPU flags, and the instruction pointer, a execution context represents a specific point in the application’s execution path. This is useful for building higher-level abstractions, like coroutines, cooperative threads (userland threads) or an equivalent to C# keyword yield in C++.

Translation:

Swift_Boost_Context is a basic library that provides some kind of collaborative multitasking on a single thread. By providing an abstraction of the current execution state in the current thread, including the stack (with local variables) and the stack pointer, all register and CPU flags, and instruction Pointers, the execution context represents a specific point in the application’s execution path. This is useful for building higher-level abstractions, such as coroutines, collaborative threads (user threads), or the c# equivalent of yield.

In addition,

This library is a version of boostorg/ Context reimplemented with Swift.

The purpose of this library is that Swift has officially supported most of the world’s popular operating systems and platforms since version 5.3, including but not limited to Windows10, Android, and linux-based distributions (Ubuntu, centOS). WASM, the latest generation of front-end technology, and so on, to take advantage of Swift this system level language cross-platform characteristics of Corountine into the above operating system and platform to go.

There are several hidden messages:

  • It can be used in Android software developmentThe Swift version of CorountineRather thanKotlin's version of Corountine in the Jvm ecology.
  • Can be used in Windows10 software developmentThe Swift version of Corountine.
  • Can be used in Linux software developmentThe Swift version of Corountine.
  • Working on Apple’s operating system, to say nothing of that.

Interpretation of the

By providing an abstraction of the current state of execution in the current thread,

The BoostContext protocol (interface in other languages) is an abstraction representing the current state of execution in the current thread.

The abstraction of the current execution state in the current thread includes the stack (with local variables) and the stack pointer, all registers and CPU flags, and instruction Pointers, with the execution context representing a specific point in the application’s execution path.

That is, the BoostContext consists of the stack (with local variables) and the stack pointer, all registers and CPU flags, and instruction Pointers, and the execution context represents a specific point in the application’s execution path.

Imagine that each separate BoostContext instance contains the current execution state in a different thread, and then dynamically switch the new BoostContext instances purposefully during execution to achieve a flow jump (similar to a GOto statement).

The following code demonstrates how to use BoostContext to implement a program flow jump.

import Swift_Boost_Context

func f1(data: Int.yield: FN_YIELD<String.Int>) -> String {
        defer {
            print("f1 finish")}print("main ----> f1    data = \(data)")

        let data1: Int = yield("1234567")
        print("main ----> f1    data = \(data1)")

        let data2: Int = yield("7654321")
        print("main ----> f1    data = \(data2)")

        return "9876543"
}

func main(a) throws {
    let yield: FN_YIELD<Int.String> = makeBoostContext(f1)

    let data1: String = yield(123)
    print("main <---- f1    data = \(data1)")

    let data2: String = yield(765)
    print("main <---- f1    data = \(data2)")

    let data3: String = yield(987)
    print("main <---- f1    data = \(data3)")}do {
    try main()
} catch {
    print("main : \(error)")}Copy the code

Output: (Run on macOS x86_64)

main ----> f1    data = 123
main <---- f1    data = 1234567
main ----> f1    data = 765
main <---- f1    data = 7654321
main ----> f1    data = 987
f1 finish
main <---- f1    data = 9876543

Process finished with exit code 0
Copy the code

The output above shows the flow of the program very intuitively:

Main (start) - (start) -- - > > f1 f2 (start) - write repeatedly jump back and forth | left main (end) (end) < < -- f1 -Copy the code

The principle of

How is BoostContext implemented?

The BoostContext consists of the stack (with local variables) and the stack pointer, all registers and CPU flags, and instruction Pointers, and the execution context represents a specific point in the application’s execution path.

All registers and CPU flags and instruction Pointers (PC, Process Count) need to be stored in an instance of BoostContext to facilitate subsequent switches.

So we need to use assembly technology to manipulate these registers and CPU flags and instruction Pointers (PC, Process Count), here by showing macOS x86_64(the simplest is 😓, Android aARCH64 and Android AARCH64 to get a rough idea:

  • macOS x86_64

In the previous section of this article, BoostContext was created using makeBoostContext:

// ...
let yield: FN_YIELD<Int.String> = makeBoostContext(f1)
// ...
Copy the code

The function makeBoostContext written using Swift ends up calling the assembly implementation’s function make_fcontext.

Now let’s look at the code implementation for the assembly part of make_fcontext:

Function prototype:

fcontext_t make_fcontext(void *sp, size_t size, void (*fn)(transfer_t));
Copy the code

Concrete implementation:

/* Copyright Oliver Kowalke 2009. Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0. TXT or copy at http://www.boost.org/LICENSE_1_0.txt) * / / * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- * * | 0 | 1 | 2 | 3 | 4 5 6 7 | | | |  * * ---------------------------------------------------------------------------------- * * | 0x0 | 0x4 | 0x8 | 0xc | 0x10 | 0x14 | 0x18 | 0x1c | * * ---------------------------------------------------------------------------------- * * |  fc_mxcsr|fc_x87_cw| R12 | R13 | R14 | * * -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- * * -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- * * | | 8 9 10 11 12 13 | | | | | 14 | 15 | * * ---------------------------------------------------------------------------------- * * | 0x20 | 0x24 | 0x28 |  0x2c | 0x30 | 0x34 | 0x38 | 0x3c | * * ---------------------------------------------------------------------------------- * * | R15 | RBX | RBP | RIP | * * -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - * * * ****************************************************************************************/ .text .globl _make_fcontext .align 8 _make_fcontext: /* first arg of make_fcontext() == top of context-stack */ movq %rdi, %rax /* shift address in RAX to lower 16 byte boundary */ andq $-16, %rax /* reserve space for context-data on context-stack */ /* on context-function entry: (RSP -0x8) % 16 == 0 */ leaq -0x40(%rax), %rax /* third arg of make_fcontext() == address of context-function */ /* stored in RBX */ movq %rdx, 0x28(%rax) /* save MMX control- and status-word */ stmxcsr (%rax) /* save x87 control-word */ fnstcw 0x4(%rax) /* compute abs address of label trampoline */ leaq trampoline(%rip), %rcx /* save address of trampoline as return-address for context-function */ /* will be entered after calling jump_fcontext() first time */ movq %rcx, 0x38(%rax) /* compute abs address of label finish */ leaq finish(%rip), %rcx /* save address of finish as return-address for context-function */ /* will be entered after context-function returns */ movq %rcx, 0x30(%rax) ret /* return pointer to context-data */ trampoline: /* store return address on stack */ /* fix stack alignment */ push %rbp /* jump to context-function */ jmp *%rbx finish:  /* exit code is zero */ xorq %rdi, %rdi /* exit application */ call __exit hltCopy the code

The following need a little basic knowledge, I hope to be as simple and clear as possible 😓:

  • Basic Knowledge 1

Take a look at this chart first:

/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- * * | 0 | 1 | 2 | 3 | 4 5 6 7 | | | |  * * ---------------------------------------------------------------------------------- * * | 0x0 | 0x4 | 0x8 | 0xc | 0x10 | 0x14 | 0x18 | 0x1c | * * ---------------------------------------------------------------------------------- * * |  fc_mxcsr|fc_x87_cw| R12 | R13 | R14 | * * -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- * * -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- * * | | 8 9 10 11 12 13 | | | | | 14 | 15 | * * ---------------------------------------------------------------------------------- * * | 0x20 | 0x24 | 0x28 |  0x2c | 0x30 | 0x34 | 0x38 | 0x3c | * * ---------------------------------------------------------------------------------- * * | R15 | RBX | RBP | RIP | * * -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /Copy the code

The data corresponding to this table (in most cases, the memory address of the real data) is actually stored as part of it in a block of memory allocated on the heap.

Why part of it? Because functions (f1 in the previous section) have local variables, and f1 may call other functions that call other functions, other functions must also have their own local variables. The local variable contains the return value of the function.

The heap-allocated memory can be created using MALloc in C, and created and destroyed on Swift like this:

class BoostContextImpl<_IN> :BoostContext {

    /// destructor method
    deinit {
        / / destroy
        _sp.deallocate()
    }

    /// construct method
    init(_ fn: @escaping FN) {
        / / create
        let spSize: Int = .pageSize * 16
        let sp: FContextStack = .allocate(byteCount: spSize, alignment: .pageSize)
    }
}
Copy the code

Key points:

The memory size of the heap is let spSize: Int =.pagesize * 16, which varies depending on the system configuration of the particular platform, but is definitely greater than 64 bytes.

PS: pageSize is typically 4092 btye

  • Basic Knowledge 2
|... |... | R12 | R13 | R14 | | R15 | RBX | RBP | RIP |Copy the code

These RXX represent registers.

R is short for Register.

 *  |   0x20  |   0x24  |   0x28  |  0x2c   |   0x30   |   0x34  |   0x38  |   0x3c  |  *
 *  ----------------------------------------------------------------------------------  *
 *  |        R15        |        RBX        |         RBP        |        RIP        |  *
Copy the code

For example, the data in register R15 is stored in this byte with address interval [0x20 0x28].

PS: address 0 x20 corresponding in front of the vertical bar |, don’t wrong.

Key points:

The memory required to fill this table is 64 btye.

  • Basic Knowledge 3

The 64byte table is stored at the bottom of the heap with a size of.pagesize * 16. The address at the bottom of the stack is the high address.

This chunk of heap memory with size.pagesize * 16, commonly known as the stack, is of course relative to the context in which the library is used.

Why is memory allocated on a heap called a stack? This is because the essence of the library is to simulate function calls that normally occur on the stack by allocating a chunk of memory on the heap.

  • Basic Knowledge 4
| fc_mxcsr|fc_x87_cw|
Copy the code

I don’t know what to use it for.

  • Sort out the basic knowledge above with the aid of effect drawings

Function prototype (here is a Copy, convenient comparison):

fcontext_t make_fcontext(void *sp, size_t size, void (*fn)(transfer_t));
Copy the code
(sp - size) sp pointer â­£ â­£ -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - the heap size: .pageSize * 16 ------------------------------------------------ | | | | -------------------------- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - | |... | be invoked by f1 function and its local variable | | f1's bottom of stack (8 byte) | | 16 byte aligned address | | | -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - | | | (until the function call stack overflow) -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - | | | (low base address addr) Base addr +.pagesize * 16Copy the code
  • Finally, it’s time to explain the above piece of assembly code 😓 :
/* first arg of make_fcontext() == top of context-stack */
    movq  %rdi, %rax
    
/* shift address in RAX to lower 16 byte boundary */
andq  $-16, %rax
Copy the code

First, address alignment, which is an integer multiple of 16.

Copies the value of the SP pointer (an address) into the RAX register.

-16’s complement: 0xffffFFF0 (binary: 1… 1 0000),

Andq $-16, %rax will address the rax at the lower 4 position 0.

Effect schematic diagram:

0 x000000010015e000 0 x000000010014e000 â­£ 0 x000000010015e000 â­£ â­£ â­£ (sp - size) rax sp â­£ â­£ â­£ -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - The heap size: .pageSize * 16 ------------------------------------------------ | | | | -------------------------- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - | |... | be invoked by f1 function and its local variable | | f1's bottom of stack (8 byte) | | 16 byte aligned address | | | -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - | | | (until the function call stack overflow) -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - | | | (low base address addr) Base addr +.pagesize * 16Copy the code

/* reserve space for context-data on context-stack */
/* on context-function entry: (RSP -0x8) % 16 == 0 */
leaq  -0x40(%rax), %rax
Copy the code

Offset 0x40 to the lower address (64 bytes of storage allocated)

Effect schematic diagram:

0 x000000010015dfc0 â­£ (sp - size) rax 0 x000000010015e000 sp â­£ â­£ â­£ â­£ -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - the heap size: .pageSize * 16 ------------------------------------------------ | | | | -------------------------- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - | |... | be invoked by f1 function and its local variable | | f1's bottom of stack (64 byte) | | 16 byte aligned address | | | -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - | | | (until the function call stack overflow) -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - | | | (low base address addr) Base addr +.pagesize * 16Copy the code

/* third arg of make_fcontext() == address of context-function */
/* stored in RBX */
movq  %rdx, 0x28(%rax)
Copy the code

Move to the high address and store the address of F1 (in RDX) in the memory location corresponding to the RBX register.

/* compute abs address of label trampoline */ leaq trampoline(%rip), %rcx /* save address of trampoline as return-address for context-function */ /* will be entered after calling jump_fcontext() first time */ movq %rcx, 0x38(%rax) // <---- (2) // ... ret /* return pointer to context-data */ // <------ (3) trampoline: /* store return address on stack */ /* fix stack alignment */ push %rbp /* jump to context-function */ jmp *%rbx // < - (1)Copy the code

Trampoline is another assembly implementation (usually called label in assembly). RBX stores the address of F1 (remember). When trampoline is called, it is JMP *% RBX.

In addition, the address of the trampoline function is stored in the memory location corresponding to RIP.

When is the trampoline called? Directly on the code:

func main(a) throws {
    let bc1: BoostContext = makeBoostContext(f1)
   
    let resultF1ToMain: BoostTransfer<String> = try bc1.jump(data: 123)  // <-- where trampoline is called
    print("main <---- f1 resultF1ToMain = \(resultF1ToMain.data)")}Copy the code

The answer is when jump_fContext is called.

How do you switch to a BoostContext created with make_fContext in the next section? Is explained.

Key points:

Position (3) returns the value 0x000000010015Dfc0 in raX.

typedef void *fcontext_t;

fcontext_t fc1 = make_fcontext(sp1 + FCONTEXT_SIZE, FCONTEXT_SIZE, f1);
Copy the code

The return value fc1 is a pointer to 0x000000010015DFC0


  • How do I switch BoostContext?

Jump_fcontext requires assembly implementation.

Function prototype:

transfer_t jump_fcontext(fcontext_t const to, void *vp);
Copy the code

PS: the first argument, fcontext_t const to, is the return value 0x000000010015dfc0 from the previous part by calling make_fContext

Assembly implementation:

/* Copyright Oliver Kowalke 2009. Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0. TXT or copy at http://www.boost.org/LICENSE_1_0.txt) * / / * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- * * | 0 | 1 | 2 | 3 | 4 5 6 7 | | | |  * * ---------------------------------------------------------------------------------- * * | 0x0 | 0x4 | 0x8 | 0xc | 0x10 | 0x14 | 0x18 | 0x1c | * * ---------------------------------------------------------------------------------- * * |  fc_mxcsr|fc_x87_cw| R12 | R13 | R14 | * * -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- * * -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- * * | | 8 9 10 11 12 13 | | | | | 14 | 15 | * * ---------------------------------------------------------------------------------- * * | 0x20 | 0x24 | 0x28 |  0x2c | 0x30 | 0x34 | 0x38 | 0x3c | * * ---------------------------------------------------------------------------------- * * | R15 | RBX | RBP | RIP | * * -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - * * * ****************************************************************************************/ .text .globl _jump_fcontext .align 8 _jump_fcontext: leaq -0x38(%rsp), %rsp /* prepare stack */ #if ! defined(BOOST_USE_TSX) stmxcsr (%rsp) /* save MMX control- and status-word */ fnstcw 0x4(%rsp) /* save x87 control-word */ #endif movq %r12, 0x8(%rsp) /* save R12 */ movq %r13, 0x10(%rsp) /* save R13 */ movq %r14, 0x18(%rsp) /* save R14 */ movq %r15, 0x20(%rsp) /* save R15 */ movq %rbx, 0x28(%rsp) /* save RBX */ movq %rbp, 0x30(%rsp) /* save RBP */ /* store RSP (pointing to context-data) in RAX */ movq %rsp, %rax /* restore RSP (pointing to context-data) from RDI */ movq %rdi, %rsp movq 0x38(%rsp), %r8 /* restore return-address */ // <----- (1) #if ! defined(BOOST_USE_TSX) ldmxcsr (%rsp) /* restore MMX control- and status-word */ fldcw 0x4(%rsp) /* restore x87 control-word */ #endif movq 0x8(%rsp), %r12 /* restore R12 */ movq 0x10(%rsp), %r13 /* restore R13 */ movq 0x18(%rsp), %r14 /* restore R14 */ movq 0x20(%rsp), %r15 /* restore R15 */ movq 0x28(%rsp), %rbx /* restore RBX */ movq 0x30(%rsp), %rbp /* restore RBP */ leaq 0x40(%rsp), %rsp /* prepare stack */ /* return transfer_t from jump */ /* RAX == fctx, RDX == data */ movq %rsi, %rdx /* pass transfer_t as first arg in context function */ /* RDI == fctx, RSI == data */ movq %rax, %rdi /* indirect jump to context */ jmp *%r8 // <----- (2)Copy the code

Let’s start with the following two key lines of code:

// ...

movq  0x38(%rsp), %r8  /* restore return-address */   // <----- (1)

// ...

/* indirect jump to context */		
jmp  *%r8						// <----- (2)
Copy the code

Remember that the address of the trampoline function is stored at 0x38(% RSP), and the trampoline call calls f1,

That is, JMP *%r8 essentially calls F1 indirectly by calling trampoline.

The other code is also very simple:

JMP *%r8 restores the BoostContext context and sets the jump_fContext return value.

/* restore RSP (pointing to context-data) from RDI */ movq %rdi, %rsp // -----> start: Movq 0x38(% RSP), %r8 /* Restore return-address */ #if! defined(BOOST_USE_TSX) ldmxcsr (%rsp) /* restore MMX control- and status-word */ fldcw 0x4(%rsp) /* restore x87 control-word */ #endif movq 0x8(%rsp), %r12 /* restore R12 */ movq 0x10(%rsp), %r13 /* restore R13 */ movq 0x18(%rsp), %r14 /* restore R14 */ movq 0x20(%rsp), %r15 /* restore R15 */ movq 0x28(%rsp), %rbx /* restore RBX */ movq 0x30(%rsp), %rbp /* restore RBP */ leaq 0x40(%rsp), %rsp /* prepare stack */ // <----- end: /* return transfer_t from jump */ * RAX == FCTX, RDX == data */ movq %rsi, %rdx // -----> start: Transfer_t as first arg in context function */ * RDI == FCTX, RSI == data */ movq %rax, %rdi // -----> end: Set the return value of 'jump_fContext' /* Indirect jump to Context */ JMP *%r8 //Copy the code

Before restoring the environment where BoostContext was created, you need to save the current environment

#if ! defined(BOOST_USE_TSX) stmxcsr (%rsp) /* save MMX control- and status-word */ fnstcw 0x4(%rsp) /* save x87 control-word */ #endif movq %r12, 0x8(%rsp) /* save R12 */ movq %r13, 0x10(%rsp) /* save R13 */ movq %r14, 0x18(%rsp) /* save R14 */ movq %r15, 0x20(%rsp) /* save R15 */ movq %rbx, 0x28(%rsp) /* save RBX */ movq %rbp, 0x30(%rsp) /* save RBP */Copy the code

At this point, the assembly part of the code has been briefly explained. You need to know more and you have to learn to assemble.

Android demo

This part is intended to show you how it works on Android.

Show the fact that swift-Coroutine can run on Android.

In addition, Android real machine (AARCH64 architecture) and emulator (X86_64 architecture) can run.

Again: Swift-Coroutine, not Kotlin-Coroutine, which also runs on some Apple operating systems.

First of all to compileSwift_Boost_ContextProject:

Currently, only x86_64 executables are compiled.


# (1)

mkdir -P ~/dev_kit/sdk/swift_source
cd ~/dev_kit/sdk/swift_source
git clone https://github.com/Guang1234567/Swift_Boost_Context.git Swift_Boost_Context
cd Swift_Boost_Context

# (2) Download the latest Swift_Android-toolchain cross-compilation tool (macOS only)

git clone https://github.com/Guang1234567/swift-android-toolchain_5_3_1_release_ndk_20.git swift-android-toolchain_5_3_1_release_ndk_20

#export SWIFT_ANDROID_ARCH=aarch64
export SWIFT_ANDROID_ARCH=x86_64
export SWIFT_ANDROID_HOME=$HOME/ dev_kit/SDK/swift_source/swift - android - 5.3.1 - release# compiler
${HOME}/ dev_kit/SDK/swift_source/swift - android - 5.3.1 - release/build - the tools / 1.9.6 swift5 / swift - build - the configuration of the debug -Xswiftc -DDEBUG -Xswiftc -gThe following output is generated after successful compilation
ls -al .build/x86_64-none-linux-android/debug/Example

# (3) Copy it to the Android VIRTUAL machine

adb push .build/x86_64-none-linux-android/debug/Example /data/local/tmp
adb push ${HOME}/dev_kit/sdk/swift_source/swift-android-toolchain_5_3_1_release_ndk_20/usr/lib/swift/android/x86_64/*.so /data/local/tmp

# (4)

adb shell LD_LIBRARY_PATH=/data/local/tmp /data/local/tmp/Example
Copy the code

The output

$ adb shell LD_LIBRARY_PATH=/data/local/tmp /data/local/tmp/Example

bc1 = BoostContextImpl(_spSize: 65536, _sp: 0x00007c733dc4e000, _fctx: 0x00007c733dc5dfc0)
main ----> f1  fromCtx = BoostContextProxy(_fctx: 0x00007ffca9cacf80)  data = 123
resultF1ToMain = BoostTransfer(fromContext: BoostContextProxy(_fctx: 0x00007c733dc5d9d0))
main <---- f1 resultF1ToMain = 7654321

....
Copy the code

screenshots