diff --git a/semester3/spca/code-examples/01_asm/08_unorthodox-controlflow.c b/semester3/spca/code-examples/01_asm/08_unorthodox-controlflow.c new file mode 100644 index 0000000..7ec2b6e --- /dev/null +++ b/semester3/spca/code-examples/01_asm/08_unorthodox-controlflow.c @@ -0,0 +1,22 @@ +#include +#include + +static jmp_buf buf; + +void second( void ) { + printf( "second\n" ); + longjmp( buf, 1 ); +} + +void first( void ) { + second(); + printf( "first\n" ); // Never executed +} + +int main() { + if ( !setjmp( buf ) ) // returns 0 initially + first(); + else + printf( "main\n" ); // 1 is returned when longjmp is executed + return 0; +} diff --git a/semester3/spca/code-examples/01_asm/09_setjmp-longjmp.s b/semester3/spca/code-examples/01_asm/09_setjmp-longjmp.s new file mode 100644 index 0000000..ea3650a --- /dev/null +++ b/semester3/spca/code-examples/01_asm/09_setjmp-longjmp.s @@ -0,0 +1,27 @@ +# Copyright 2011-2012 Nicholas J. Kain, licensed under standard MIT license +setjmp: + mov %rbx, (%rdi) + mov %rbp, 8(%rdi) + mov %r12, 16(%rdi) + mov %r13, 24(%rdi) + mov %r14, 32(%rdi) + mov %r15, 40(%rdi) + lea 8(%rsp), %rdx + mov %rdx, 48(%rdi) + mov (%rsp), %rdx + mov %rdx, 56(%rdi) + xor %eax, %eax + ret + +longjmp: + xor %eax, %eax + cmp $1, %esi # CF = val ? 0 : 1 + adc %esi, %eax # eax = val + !val + mov (%rdi), %rbx # rdi is the jmp_buf, restore regs from it + mov 8(%rdi), %rbp + mov 16(%rdi), %r12 + mov 24(%rdi), %r13 + mov 32(%rdi), %r14 + mov 40(%rdi), %r15 + mov 48(%rdi), %rsp + jmp *56(%rdi) # goto saved address without altering rsp diff --git a/semester3/spca/code-examples/01_asm/10_coroutine.c b/semester3/spca/code-examples/01_asm/10_coroutine.c new file mode 100644 index 0000000..5c7e383 --- /dev/null +++ b/semester3/spca/code-examples/01_asm/10_coroutine.c @@ -0,0 +1,40 @@ +#include "10_coroutine.h" +#include +#include +#include +#define CORO_STACK_SIZE 128 +static struct coroutine *cur_co; +static struct coroutine *main_co; + +void co_init() { + main_co = (struct coroutine *) calloc( 1, sizeof( struct coroutine ) ); + cur_co = main_co; + co_switchto( main_co ); +} + +void *co_switchto( struct coroutine *next, void *arg ) { + if ( setjmp( cur_co->env ) == 0 ) { + cur_co = next; + cur_co->arg = arg; + longjmp( cur_co->env, 1 ); + } + return cur_co->arg; +} + +static void start_cl( void ) { + ( cur_co->start )( cur_co->arg ); + co_switchto( main_co ); + + printf( "Error: returned from coroutine start closure.\n" ); + exit( -1 ); +} + +struct coroutine *co_new( co_start_fn *start, void *ctxt ) { + struct coroutine *co = (struct coroutine *) calloc( 1, sizeof( struct coroutine ) ); + co->stack = calloc( 1, CORO_STACK_SIZE + 16 ); + co->start = start; + co->arg = ctxt; + setjmp( co->env ); + co->env[ 0 ].__jmpbuf[ 6 ] = ( (uint64_t) ( co->stack ) + CORO_STACK_SIZE ); // Machine specific + co->env[ 0 ].__jmpbuf[ 7 ] = ( (uint64_t) ( start_cl ) ); // Machine specific +} diff --git a/semester3/spca/code-examples/01_asm/10_coroutine.h b/semester3/spca/code-examples/01_asm/10_coroutine.h new file mode 100644 index 0000000..c71a6f5 --- /dev/null +++ b/semester3/spca/code-examples/01_asm/10_coroutine.h @@ -0,0 +1,13 @@ +#include +typedef void( co_start_fn )( void * ); + +struct coroutine { + void *stack; // The call stack + jmp_buf env; // The saved context + co_start_fn *start; // Function to call + void *arg; // Argument to the function +}; +struct coroutine *co_new( co_start_fn *start, void *ctxt ); +void co_free( struct coroutine *self ); +void *co_switchto( struct coroutine *next ); +void co_init( void ); diff --git a/semester3/spca/parts/00_asm/05_stack.tex b/semester3/spca/parts/00_asm/05_stack.tex index cf50ef1..521f870 100644 --- a/semester3/spca/parts/00_asm/05_stack.tex +++ b/semester3/spca/parts/00_asm/05_stack.tex @@ -75,4 +75,3 @@ A more complex example, passing addresses as arguments: \\ This function swaps 2 array elements (using a \texttt{swap} function) and adds the first value to an accumulator. \inputcodewithfilename{gas}{code-examples/01_asm/}{07_swap_and_sum.s} - diff --git a/semester3/spca/parts/00_asm/06_unorthodox-control-flow.tex b/semester3/spca/parts/00_asm/06_unorthodox-control-flow.tex index e69de29..047a97a 100644 --- a/semester3/spca/parts/00_asm/06_unorthodox-control-flow.tex +++ b/semester3/spca/parts/00_asm/06_unorthodox-control-flow.tex @@ -0,0 +1,19 @@ +\newpage +\subsection{Unorthodox Control Flow} +In \lC, the \texttt{setjmp.h} header file can be included, which gives us access to \texttt{setjmp} and \texttt{longjmp}. + +To use them, we first need to declare a \texttt{jmp\_buf} somewhere, usually as a static variable. + +The \texttt{setjmp( jmp\_buf env } function stores the current stack / environment in the \texttt{jmp\_buf} and returns 0. + +The \texttt{longjmp( jmp\_buf env, int val )} function causes a second return, which returns \texttt{val}, +to the \texttt{setjmp} invocation and jumps back to that place. + +\inputcodewithfilename{c}{code-examples/01_asm/}{08_unorthodox-controlflow.c} + +What the above code outputs is: \texttt{second} followed by \texttt{main}. + +\newpage +They are implemented in Assembly as follows. Nothing really surprising for the implementation there. +The assembly code is from the Musl \lC\ library +\inputcodewithfilename{gas}{code-examples/01_asm/}{09_setjmp-longjmp.s} diff --git a/semester3/spca/parts/00_asm/07_coroutines.tex b/semester3/spca/parts/00_asm/07_coroutines.tex new file mode 100644 index 0000000..d736990 --- /dev/null +++ b/semester3/spca/parts/00_asm/07_coroutines.tex @@ -0,0 +1,12 @@ +\newpage +\subsection{Coroutines} +Coroutines are functions that call each other when they are done or they need new data to work on. +An example is a decompresser that calls a parser when it has finished compressing parts of the file and that parser then again calls the decompresser when it has finished parsing. + +We can implement that either by rewriting the functions into a single function, which often is a bit clumsy. +A way around this is to use \bi{Continuations}, where the first function saves its state and the context is switched to the other function. +That function can then load its state and continue where it left off, runs until it finishes its task, then saves its state and the context switches back to the original function. +\inputcodewithfilename{c}{code-examples/01_asm/}{10_coroutine.h} +\inputcodewithfilename{c}{code-examples/01_asm/}{10_coroutine.c} + +As you can see, the \texttt{setjmp.h} functions are the foundation of all concurrent programming. diff --git a/semester3/spca/spca-summary.pdf b/semester3/spca/spca-summary.pdf index d9148f7..e333478 100644 Binary files a/semester3/spca/spca-summary.pdf and b/semester3/spca/spca-summary.pdf differ diff --git a/semester3/spca/spca-summary.tex b/semester3/spca/spca-summary.tex index ff1ade7..56a2404 100644 --- a/semester3/spca/spca-summary.tex +++ b/semester3/spca/spca-summary.tex @@ -102,6 +102,8 @@ If there are changes and you'd like to update this summary, please open a pull r \input{parts/00_asm/03_operations/03_jumping.tex} \input{parts/00_asm/04_control-flow.tex} \input{parts/00_asm/05_stack.tex} +\input{parts/00_asm/06_unorthodox-control-flow.tex} +\input{parts/00_asm/07_coroutines.tex} % ── Intro to C ──────────────────────────────────────────────────────