|
| 1 | + |
| 2 | +static void *slp_switch(void *(*save_state)(void*, void*), |
| 3 | + void *(*restore_state)(void*, void*), |
| 4 | + void *extra) __attribute__((noinline)); |
| 5 | + |
| 6 | +static void *slp_switch(void *(*save_state)(void*, void*), |
| 7 | + void *(*restore_state)(void*, void*), |
| 8 | + void *extra) |
| 9 | +{ |
| 10 | + void *result; |
| 11 | + /* |
| 12 | + registers to preserve: x18-x28, x29(fp), and v8-v15 |
| 13 | + registers marked as clobbered: x0-x18, x30 |
| 14 | +
|
| 15 | + Note that x18 appears in both lists; see below. We also save |
| 16 | + x30 although it's also marked as clobbered, which might not |
| 17 | + be necessary but doesn't hurt. |
| 18 | +
|
| 19 | + Don't assume gcc saves any register for us when generating |
| 20 | + code for slp_switch(). |
| 21 | +
|
| 22 | + The values 'save_state', 'restore_state' and 'extra' are first moved |
| 23 | + by gcc to some registers that are not marked as clobbered, so between |
| 24 | + x19 and x29. Similarly, gcc expects 'result' to be in a register |
| 25 | + between x19 and x29. We don't want x18 to be used here, because of |
| 26 | + some special meaning it might have. We don't want x30 to be used |
| 27 | + here, because it is clobbered by the first "blr". |
| 28 | +
|
| 29 | + This means that three of the values we happen to save and restore |
| 30 | + will, in fact, contain the three arguments, and one of these values |
| 31 | + will, in fact, not be restored at all but receive 'result'. |
| 32 | + */ |
| 33 | + |
| 34 | + __asm__ volatile ( |
| 35 | + |
| 36 | + /* The stack is supposed to be aligned as necessary already. |
| 37 | + Save 12 registers from x18 to x29, plus 8 from v8 to v15 */ |
| 38 | + |
| 39 | + "stp x18, x19, [sp, -160]!\n" |
| 40 | + "stp x20, x11, [sp, 16]\n" |
| 41 | + "stp x22, x23, [sp, 32]\n" |
| 42 | + "stp x24, x25, [sp, 48]\n" |
| 43 | + "stp x26, x27, [sp, 64]\n" |
| 44 | + "stp x28, x29, [sp, 80]\n" |
| 45 | + "str d8, [sp, 96]\n" |
| 46 | + "str d9, [sp, 104]\n" |
| 47 | + "str d10, [sp, 112]\n" |
| 48 | + "str d11, [sp, 120]\n" |
| 49 | + "str d12, [sp, 128]\n" |
| 50 | + "str d13, [sp, 136]\n" |
| 51 | + "str d14, [sp, 144]\n" |
| 52 | + "str d15, [sp, 152]\n" |
| 53 | + |
| 54 | + "mov x0, sp\n" /* arg 1: current (old) stack pointer */ |
| 55 | + "mov x1, %[extra]\n" /* arg 2: extra, from x19-x28 */ |
| 56 | + "blr %[save_state]\n" /* call save_state(), from x19-x28 */ |
| 57 | + |
| 58 | + /* skip the rest if the return value is null */ |
| 59 | + "cbz x0, 0f\n" |
| 60 | + |
| 61 | + "mov sp, x0\n" /* change the stack pointer */ |
| 62 | + |
| 63 | + /* From now on, the stack pointer is modified, but the content of the |
| 64 | + stack is not restored yet. It contains only garbage here. */ |
| 65 | + "mov x1, %[extra]\n" /* arg 2: extra, still from x19-x28 */ |
| 66 | + /* arg 1: current (new) stack pointer is already in x0*/ |
| 67 | + "blr %[restore_state]\n"/* call restore_state() */ |
| 68 | + |
| 69 | + /* The stack's content is now restored. */ |
| 70 | + "0:\n" |
| 71 | + |
| 72 | + /* Restore all saved registers */ |
| 73 | + "ldp x20, x11, [sp, 16]\n" |
| 74 | + "ldp x22, x23, [sp, 32]\n" |
| 75 | + "ldp x24, x25, [sp, 48]\n" |
| 76 | + "ldp x26, x27, [sp, 64]\n" |
| 77 | + "ldp x28, x29, [sp, 80]\n" |
| 78 | + "ldr d8, [sp, 96]\n" |
| 79 | + "ldr d9, [sp, 104]\n" |
| 80 | + "ldr d10, [sp, 112]\n" |
| 81 | + "ldr d11, [sp, 120]\n" |
| 82 | + "ldr d12, [sp, 128]\n" |
| 83 | + "ldr d13, [sp, 136]\n" |
| 84 | + "ldr d14, [sp, 144]\n" |
| 85 | + "ldr d15, [sp, 152]\n" |
| 86 | + "ldp x18, x19, [sp], 160\n" |
| 87 | + |
| 88 | + /* Move x0 into the final location of 'result' */ |
| 89 | + "mov %[result], x0\n" |
| 90 | + |
| 91 | + : [result]"=r"(result) /* output variables */ |
| 92 | + /* input variables */ |
| 93 | + : [restore_state]"r"(restore_state), |
| 94 | + [save_state]"r"(save_state), |
| 95 | + [extra]"r"(extra) |
| 96 | + : "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", |
| 97 | + "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", |
| 98 | + "memory", "cc", "x30" // x30==lr |
| 99 | + ); |
| 100 | + return result; |
| 101 | +} |
0 commit comments