fix unintended R27 clobber on arm64 (#165)
For some reason the Go assembler uses R27 as a scratch register when loading in a variable's address. Since callbackasm1 is called from C and the arm64 ABI states that R27 is callee saved it was unexpectedly clobbering the value. This commit now saves R27 and restores it at the end of the function. I also cleaned up some of the other parts of this function. I store the floats in pairs which saved 4 instructions. I also use the zero register to put zero in callbackArgs.result directly Updates #124 Closes #162
- commit
- 202a0cad5c2ed0d79e85d2c4c6aea0c607d982ce
- author
- TotallyGamerJet · · 2023-09-22 11:48 UTC
- committer
- Hajime Hoshi · · 2023-09-22 14:46 UTC
- parent
- 0ff0887
- signature
- unsigned
- refs
- tag: v0.5.0-rc.2
1 file · +27 -21
M sys_unix_arm64.s +27 -21
| @@ -17,31 +17,37 @@ TEXT callbackasm1(SB), NOSPLIT|NOFRAME, $0 | |||
| 17 | 17 | // Save callback register arguments R0-R7 and F0-F7. | |
| 18 | 18 | // We do this at the top of the frame so they're contiguous with stack arguments. | |
| 19 | 19 | SUB $(16*8), RSP, R14 | |
| 20 | - | FMOVD F0, (0*8)(R14) | |
| 21 | - | FMOVD F1, (1*8)(R14) | |
| 22 | - | FMOVD F2, (2*8)(R14) | |
| 23 | - | FMOVD F3, (3*8)(R14) | |
| 24 | - | FMOVD F4, (4*8)(R14) | |
| 25 | - | FMOVD F5, (5*8)(R14) | |
| 26 | - | FMOVD F6, (6*8)(R14) | |
| 27 | - | FMOVD F7, (7*8)(R14) | |
| 20 | + | FSTPD (F0, F1), (0*8)(R14) | |
| 21 | + | FSTPD (F2, F3), (2*8)(R14) | |
| 22 | + | FSTPD (F4, F5), (4*8)(R14) | |
| 23 | + | FSTPD (F6, F7), (6*8)(R14) | |
| 28 | 24 | STP (R0, R1), (8*8)(R14) | |
| 29 | 25 | STP (R2, R3), (10*8)(R14) | |
| 30 | 26 | STP (R4, R5), (12*8)(R14) | |
| 31 | 27 | STP (R6, R7), (14*8)(R14) | |
| 32 | 28 | ||
| 33 | 29 | // Adjust SP by frame size. | |
| 34 | - | // crosscall2 clobbers FP in the frame record so only save/restore SP. | |
| 35 | - | SUB $(28*8), RSP | |
| 36 | - | MOVD R30, (RSP) | |
| 30 | + | SUB $(26*8), RSP | |
| 31 | + | ||
| 32 | + | // It is important to save R27 because the go assembler | |
| 33 | + | // uses it for move instructions for a variable. | |
| 34 | + | // This line: | |
| 35 | + | // MOVD ·callbackWrap_call(SB), R0 | |
| 36 | + | // Creates the instructions: | |
| 37 | + | // ADRP 14335(PC), R27 | |
| 38 | + | // MOVD 388(27), R0 | |
| 39 | + | // R27 is a callee saved register so we are responsible | |
| 40 | + | // for ensuring its value doesn't change. So save it and | |
| 41 | + | // restore it at the end of this function. | |
| 42 | + | // R30 is the link register. crosscall2 doesn't save it | |
| 43 | + | // so it's saved here. | |
| 44 | + | STP (R27, R30), 0(RSP) | |
| 37 | 45 | ||
| 38 | 46 | // Create a struct callbackArgs on our stack. | |
| 39 | - | ADD $(callbackArgs__size + 3*8), RSP, R13 | |
| 40 | - | MOVD R12, callbackArgs_index(R13) // callback index | |
| 41 | - | MOVD R14, R0 | |
| 42 | - | MOVD R0, callbackArgs_args(R13) // address of args vector | |
| 43 | - | MOVD $0, R0 | |
| 44 | - | MOVD R0, callbackArgs_result(R13) // result | |
| 47 | + | MOVD $(callbackArgs__size)(RSP), R13 | |
| 48 | + | MOVD R12, callbackArgs_index(R13) // callback index | |
| 49 | + | MOVD R14, callbackArgs_args(R13) // address of args vector | |
| 50 | + | MOVD ZR, callbackArgs_result(R13) // result | |
| 45 | 51 | ||
| 46 | 52 | // Move parameters into registers | |
| 47 | 53 | // Get the ABIInternal function pointer | |
| @@ -54,11 +60,11 @@ TEXT callbackasm1(SB), NOSPLIT|NOFRAME, $0 | |||
| 54 | 60 | BL crosscall2(SB) | |
| 55 | 61 | ||
| 56 | 62 | // Get callback result. | |
| 57 | - | ADD $(callbackArgs__size + 3*8), RSP, R13 | |
| 63 | + | MOVD $(callbackArgs__size)(RSP), R13 | |
| 58 | 64 | MOVD callbackArgs_result(R13), R0 | |
| 59 | 65 | ||
| 60 | - | // Restore SP | |
| 61 | - | MOVD (RSP), R30 | |
| 62 | - | ADD $(28*8), RSP | |
| 66 | + | // Restore LR and R27 | |
| 67 | + | LDP 0(RSP), (R27, R30) | |
| 68 | + | ADD $(26*8), RSP | |
| 63 | 69 | ||
| 64 | 70 | RET | |
| @@ -17,31 +17,37 @@ TEXT callbackasm1(SB), NOSPLIT|NOFRAME, $0 | |||||
| 17 | // Save callback register arguments R0-R7 and F0-F7. | 17 | // Save callback register arguments R0-R7 and F0-F7. | ||
| 18 | // We do this at the top of the frame so they're contiguous with stack arguments. | 18 | // We do this at the top of the frame so they're contiguous with stack arguments. | ||
| 19 | SUB $(16*8), RSP, R14 | 19 | SUB $(16*8), RSP, R14 | ||
| 20 | - | FMOVD F0, (0*8)(R14) | 20 | + | FSTPD (F0, F1), (0*8)(R14) |
| 21 | - | FMOVD F1, (1*8)(R14) | 21 | + | FSTPD (F2, F3), (2*8)(R14) |
| 22 | - | FMOVD F2, (2*8)(R14) | 22 | + | FSTPD (F4, F5), (4*8)(R14) |
| 23 | - | FMOVD F3, (3*8)(R14) | 23 | + | FSTPD (F6, F7), (6*8)(R14) |
| 24 | - | FMOVD F4, (4*8)(R14) | |||
| 25 | - | FMOVD F5, (5*8)(R14) | |||
| 26 | - | FMOVD F6, (6*8)(R14) | |||
| 27 | - | FMOVD F7, (7*8)(R14) | |||
| 28 | STP (R0, R1), (8*8)(R14) | 24 | STP (R0, R1), (8*8)(R14) | ||
| 29 | STP (R2, R3), (10*8)(R14) | 25 | STP (R2, R3), (10*8)(R14) | ||
| 30 | STP (R4, R5), (12*8)(R14) | 26 | STP (R4, R5), (12*8)(R14) | ||
| 31 | STP (R6, R7), (14*8)(R14) | 27 | STP (R6, R7), (14*8)(R14) | ||
| 32 | 28 | ||||
| 33 | // Adjust SP by frame size. | 29 | // Adjust SP by frame size. | ||
| 34 | - | // crosscall2 clobbers FP in the frame record so only save/restore SP. | 30 | + | SUB $(26*8), RSP |
| 35 | - | SUB $(28*8), RSP | 31 | + | |
| 36 | - | MOVD R30, (RSP) | 32 | + | // It is important to save R27 because the go assembler |
| 33 | + | // uses it for move instructions for a variable. | |||
| 34 | + | // This line: | |||
| 35 | + | // MOVD ·callbackWrap_call(SB), R0 | |||
| 36 | + | // Creates the instructions: | |||
| 37 | + | // ADRP 14335(PC), R27 | |||
| 38 | + | // MOVD 388(27), R0 | |||
| 39 | + | // R27 is a callee saved register so we are responsible | |||
| 40 | + | // for ensuring its value doesn't change. So save it and | |||
| 41 | + | // restore it at the end of this function. | |||
| 42 | + | // R30 is the link register. crosscall2 doesn't save it | |||
| 43 | + | // so it's saved here. | |||
| 44 | + | STP (R27, R30), 0(RSP) | |||
| 37 | 45 | ||||
| 38 | // Create a struct callbackArgs on our stack. | 46 | // Create a struct callbackArgs on our stack. | ||
| 39 | - | ADD $(callbackArgs__size + 3*8), RSP, R13 | 47 | + | MOVD $(callbackArgs__size)(RSP), R13 |
| 40 | - | MOVD R12, callbackArgs_index(R13) // callback index | 48 | + | MOVD R12, callbackArgs_index(R13) // callback index |
| 41 | - | MOVD R14, R0 | 49 | + | MOVD R14, callbackArgs_args(R13) // address of args vector |
| 42 | - | MOVD R0, callbackArgs_args(R13) // address of args vector | 50 | + | MOVD ZR, callbackArgs_result(R13) // result |
| 43 | - | MOVD $0, R0 | |||
| 44 | - | MOVD R0, callbackArgs_result(R13) // result | |||
| 45 | 51 | ||||
| 46 | // Move parameters into registers | 52 | // Move parameters into registers | ||
| 47 | // Get the ABIInternal function pointer | 53 | // Get the ABIInternal function pointer | ||
| @@ -54,11 +60,11 @@ TEXT callbackasm1(SB), NOSPLIT|NOFRAME, $0 | |||||
| 54 | BL crosscall2(SB) | 60 | BL crosscall2(SB) | ||
| 55 | 61 | ||||
| 56 | // Get callback result. | 62 | // Get callback result. | ||
| 57 | - | ADD $(callbackArgs__size + 3*8), RSP, R13 | 63 | + | MOVD $(callbackArgs__size)(RSP), R13 |
| 58 | MOVD callbackArgs_result(R13), R0 | 64 | MOVD callbackArgs_result(R13), R0 | ||
| 59 | 65 | ||||
| 60 | - | // Restore SP | 66 | + | // Restore LR and R27 |
| 61 | - | MOVD (RSP), R30 | 67 | + | LDP 0(RSP), (R27, R30) |
| 62 | - | ADD $(28*8), RSP | 68 | + | ADD $(26*8), RSP |
| 63 | 69 | ||||
| 64 | RET | 70 | RET | ||