github.com/AESNooper/go/src@v0.0.0-20220218095104-b56a4ab1bbbb/runtime/sys_windows_arm64.s (about) 1 // Copyright 2018 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 #include "go_asm.h" 6 #include "go_tls.h" 7 #include "textflag.h" 8 #include "funcdata.h" 9 #include "time_windows.h" 10 11 // Offsets into Thread Environment Block (pointer in R18) 12 #define TEB_error 0x68 13 #define TEB_TlsSlots 0x1480 14 15 // Note: R0-R7 are args, R8 is indirect return value address, 16 // R9-R15 are caller-save, R19-R29 are callee-save. 17 // 18 // load_g and save_g (in tls_arm64.s) clobber R27 (REGTMP) and R0. 19 20 // void runtime·asmstdcall(void *c); 21 TEXT runtime·asmstdcall(SB),NOSPLIT|NOFRAME,$0 22 STP.W (R29, R30), -32(RSP) // allocate C ABI stack frame 23 STP (R19, R20), 16(RSP) // save old R19, R20 24 MOVD R0, R19 // save libcall pointer 25 MOVD RSP, R20 // save stack pointer 26 27 // SetLastError(0) 28 MOVD $0, TEB_error(R18_PLATFORM) 29 MOVD libcall_args(R19), R12 // libcall->args 30 31 // Do we have more than 8 arguments? 32 MOVD libcall_n(R19), R0 33 CMP $0, R0; BEQ _0args 34 CMP $1, R0; BEQ _1args 35 CMP $2, R0; BEQ _2args 36 CMP $3, R0; BEQ _3args 37 CMP $4, R0; BEQ _4args 38 CMP $5, R0; BEQ _5args 39 CMP $6, R0; BEQ _6args 40 CMP $7, R0; BEQ _7args 41 CMP $8, R0; BEQ _8args 42 43 // Reserve stack space for remaining args 44 SUB $8, R0, R2 45 ADD $1, R2, R3 // make even number of words for stack alignment 46 AND $~1, R3 47 LSL $3, R3 48 SUB R3, RSP 49 50 // R4: size of stack arguments (n-8)*8 51 // R5: &args[8] 52 // R6: loop counter, from 0 to (n-8)*8 53 // R7: scratch 54 // R8: copy of RSP - (R2)(RSP) assembles as (R2)(ZR) 55 SUB $8, R0, R4 56 LSL $3, R4 57 ADD $(8*8), R12, R5 58 MOVD $0, R6 59 MOVD RSP, R8 60 stackargs: 61 MOVD (R6)(R5), R7 62 MOVD R7, (R6)(R8) 63 ADD $8, R6 64 CMP R6, R4 65 BNE stackargs 66 67 _8args: 68 MOVD (7*8)(R12), R7 69 _7args: 70 MOVD (6*8)(R12), R6 71 _6args: 72 MOVD (5*8)(R12), R5 73 _5args: 74 MOVD (4*8)(R12), R4 75 _4args: 76 MOVD (3*8)(R12), R3 77 _3args: 78 MOVD (2*8)(R12), R2 79 _2args: 80 MOVD (1*8)(R12), R1 81 _1args: 82 MOVD (0*8)(R12), R0 83 _0args: 84 85 MOVD libcall_fn(R19), R12 // branch to libcall->fn 86 BL (R12) 87 88 MOVD R20, RSP // free stack space 89 MOVD R0, libcall_r1(R19) // save return value to libcall->r1 90 // TODO(rsc) floating point like amd64 in libcall->r2? 91 92 // GetLastError 93 MOVD TEB_error(R18_PLATFORM), R0 94 MOVD R0, libcall_err(R19) 95 96 // Restore callee-saved registers. 97 LDP 16(RSP), (R19, R20) 98 LDP.P 32(RSP), (R29, R30) 99 RET 100 101 TEXT runtime·badsignal2(SB),NOSPLIT,$16-0 102 NO_LOCAL_POINTERS 103 104 // stderr 105 MOVD runtime·_GetStdHandle(SB), R1 106 MOVD $-12, R0 107 SUB $16, RSP // skip over saved frame pointer below RSP 108 BL (R1) 109 ADD $16, RSP 110 111 // handle in R0 already 112 MOVD $runtime·badsignalmsg(SB), R1 // lpBuffer 113 MOVD $runtime·badsignallen(SB), R2 // lpNumberOfBytesToWrite 114 MOVD (R2), R2 115 MOVD R13, R3 // lpNumberOfBytesWritten 116 MOVD $0, R4 // lpOverlapped 117 MOVD runtime·_WriteFile(SB), R12 118 SUB $16, RSP // skip over saved frame pointer below RSP 119 BL (R12) 120 121 // Does not return. 122 B runtime·abort(SB) 123 124 RET 125 126 TEXT runtime·getlasterror(SB),NOSPLIT|NOFRAME,$0 127 MOVD TEB_error(R18_PLATFORM), R0 128 MOVD R0, ret+0(FP) 129 RET 130 131 #define SAVE_R19_TO_R28(offset) \ 132 MOVD R19, savedR19+((offset)+0*8)(SP); \ 133 MOVD R20, savedR20+((offset)+1*8)(SP); \ 134 MOVD R21, savedR21+((offset)+2*8)(SP); \ 135 MOVD R22, savedR22+((offset)+3*8)(SP); \ 136 MOVD R23, savedR23+((offset)+4*8)(SP); \ 137 MOVD R24, savedR24+((offset)+5*8)(SP); \ 138 MOVD R25, savedR25+((offset)+6*8)(SP); \ 139 MOVD R26, savedR26+((offset)+7*8)(SP); \ 140 MOVD R27, savedR27+((offset)+8*8)(SP); \ 141 MOVD g, savedR28+((offset)+9*8)(SP); 142 143 #define RESTORE_R19_TO_R28(offset) \ 144 MOVD savedR19+((offset)+0*8)(SP), R19; \ 145 MOVD savedR20+((offset)+1*8)(SP), R20; \ 146 MOVD savedR21+((offset)+2*8)(SP), R21; \ 147 MOVD savedR22+((offset)+3*8)(SP), R22; \ 148 MOVD savedR23+((offset)+4*8)(SP), R23; \ 149 MOVD savedR24+((offset)+5*8)(SP), R24; \ 150 MOVD savedR25+((offset)+6*8)(SP), R25; \ 151 MOVD savedR26+((offset)+7*8)(SP), R26; \ 152 MOVD savedR27+((offset)+8*8)(SP), R27; \ 153 MOVD savedR28+((offset)+9*8)(SP), g; /* R28 */ 154 155 // Called by Windows as a Vectored Exception Handler (VEH). 156 // First argument is pointer to struct containing 157 // exception record and context pointers. 158 // Handler function is stored in R1 159 // Return 0 for 'not handled', -1 for handled. 160 // int32_t sigtramp( 161 // PEXCEPTION_POINTERS ExceptionInfo, 162 // func *GoExceptionHandler); 163 TEXT sigtramp<>(SB),NOSPLIT|NOFRAME,$0 164 // Save R0, R1 (args) as well as LR, R27, R28 (callee-save). 165 MOVD R0, R5 166 MOVD R1, R6 167 MOVD LR, R7 168 MOVD R27, R16 // saved R27 (callee-save) 169 MOVD g, R17 // saved R28 (callee-save from Windows, not really g) 170 171 BL runtime·load_g(SB) // smashes R0, R27, R28 (g) 172 CMP $0, g // is there a current g? 173 BNE 2(PC) 174 BL runtime·badsignal2(SB) 175 176 // Do we need to switch to the g0 stack? 177 MOVD g, R3 // R3 = oldg (for sigtramp_g0) 178 MOVD g_m(g), R2 // R2 = m 179 MOVD m_g0(R2), R2 // R2 = g0 180 CMP g, R2 // if curg == g0 181 BNE switch 182 183 // No: on g0 stack already, tail call to sigtramp_g0. 184 // Restore all the callee-saves so sigtramp_g0 can return to our caller. 185 // We also pass R2 = g0, R3 = oldg, both set above. 186 MOVD R5, R0 187 MOVD R6, R1 188 MOVD R7, LR 189 MOVD R16, R27 // restore R27 190 MOVD R17, g // restore R28 191 B sigtramp_g0<>(SB) 192 193 switch: 194 // switch to g0 stack (but do not update g - that's sigtramp_g0's job) 195 MOVD RSP, R8 196 MOVD (g_sched+gobuf_sp)(R2), R4 // R4 = g->gobuf.sp 197 SUB $(6*8), R4 // alloc space for saves - 2 words below SP for frame pointer, 3 for us to use, 1 for alignment 198 MOVD R4, RSP // switch to g0 stack 199 200 MOVD $0, (0*8)(RSP) // fake saved LR 201 MOVD R7, (1*8)(RSP) // saved LR 202 MOVD R8, (2*8)(RSP) // saved SP 203 204 MOVD R5, R0 // original args 205 MOVD R6, R1 // original args 206 MOVD R16, R27 207 MOVD R17, g // R28 208 BL sigtramp_g0<>(SB) 209 210 // switch back to original stack; g already updated 211 MOVD (1*8)(RSP), R7 // saved LR 212 MOVD (2*8)(RSP), R8 // saved SP 213 MOVD R7, LR 214 MOVD R8, RSP 215 RET 216 217 // sigtramp_g0 is running on the g0 stack, with R2 = g0, R3 = oldg. 218 // But g itself is not set - that's R28, a callee-save register, 219 // and it still holds the value from the Windows DLL caller. 220 TEXT sigtramp_g0<>(SB),NOSPLIT,$128 221 NO_LOCAL_POINTERS 222 223 // Push C callee-save registers R19-R28. LR, FP already saved. 224 SAVE_R19_TO_R28(-10*8) 225 226 MOVD 0(R0), R5 // R5 = ExceptionPointers->ExceptionRecord 227 MOVD 8(R0), R6 // R6 = ExceptionPointers->ContextRecord 228 MOVD R6, context-(11*8)(SP) 229 230 MOVD R2, g // g0 231 BL runtime·save_g(SB) // smashes R0 232 233 MOVD R5, (1*8)(RSP) // arg0 (ExceptionRecord) 234 MOVD R6, (2*8)(RSP) // arg1 (ContextRecord) 235 MOVD R3, (3*8)(RSP) // arg2 (original g) 236 MOVD R3, oldg-(12*8)(SP) 237 BL (R1) 238 MOVD oldg-(12*8)(SP), g 239 BL runtime·save_g(SB) // smashes R0 240 MOVW (4*8)(RSP), R0 // return value (0 or -1) 241 242 // if return value is CONTINUE_SEARCH, do not set up control 243 // flow guard workaround 244 CMP $0, R0 245 BEQ return 246 247 // Check if we need to set up the control flow guard workaround. 248 // On Windows, the stack pointer in the context must lie within 249 // system stack limits when we resume from exception. 250 // Store the resume SP and PC in alternate registers 251 // and return to sigresume on the g0 stack. 252 // sigresume makes no use of the stack at all, 253 // loading SP from R0 and jumping to R1. 254 // Note that smashing R0 and R1 is only safe because we know sigpanic 255 // will not actually return to the original frame, so the registers 256 // are effectively dead. But this does mean we can't use the 257 // same mechanism for async preemption. 258 MOVD context-(11*8)(SP), R6 259 MOVD context_pc(R6), R2 // load PC from context record 260 MOVD $sigresume<>(SB), R1 261 262 CMP R1, R2 263 BEQ return // do not clobber saved SP/PC 264 265 // Save resume SP and PC into R0, R1. 266 MOVD context_xsp(R6), R2 267 MOVD R2, (context_x+0*8)(R6) 268 MOVD context_pc(R6), R2 269 MOVD R2, (context_x+1*8)(R6) 270 271 // Set up context record to return to sigresume on g0 stack 272 MOVD RSP, R2 273 MOVD R2, context_xsp(R6) 274 MOVD $sigresume<>(SB), R2 275 MOVD R2, context_pc(R6) 276 277 return: 278 RESTORE_R19_TO_R28(-10*8) // smashes g 279 RET 280 281 // Trampoline to resume execution from exception handler. 282 // This is part of the control flow guard workaround. 283 // It switches stacks and jumps to the continuation address. 284 // R0 and R1 are set above at the end of sigtramp<> 285 // in the context that starts executing at sigresume<>. 286 TEXT sigresume<>(SB),NOSPLIT|NOFRAME,$0 287 // Important: do not smash LR, 288 // which is set to a live value when handling 289 // a signal by pushing a call to sigpanic onto the stack. 290 MOVD R0, RSP 291 B (R1) 292 293 TEXT runtime·exceptiontramp(SB),NOSPLIT|NOFRAME,$0 294 MOVD $runtime·exceptionhandler(SB), R1 295 B sigtramp<>(SB) 296 297 TEXT runtime·firstcontinuetramp(SB),NOSPLIT|NOFRAME,$0 298 MOVD $runtime·firstcontinuehandler(SB), R1 299 B sigtramp<>(SB) 300 301 TEXT runtime·lastcontinuetramp(SB),NOSPLIT|NOFRAME,$0 302 MOVD $runtime·lastcontinuehandler(SB), R1 303 B sigtramp<>(SB) 304 305 GLOBL runtime·cbctxts(SB), NOPTR, $4 306 307 TEXT runtime·callbackasm1(SB),NOSPLIT,$208-0 308 NO_LOCAL_POINTERS 309 310 // On entry, the trampoline in zcallback_windows_arm64.s left 311 // the callback index in R12 (which is volatile in the C ABI). 312 313 // Save callback register arguments R0-R7. 314 // We do this at the top of the frame so they're contiguous with stack arguments. 315 // The 7*8 setting up R14 looks like a bug but is not: the eighth word 316 // is the space the assembler reserved for our caller's frame pointer, 317 // but we are not called from Go so that space is ours to use, 318 // and we must to be contiguous with the stack arguments. 319 MOVD $arg0-(7*8)(SP), R14 320 MOVD R0, (0*8)(R14) 321 MOVD R1, (1*8)(R14) 322 MOVD R2, (2*8)(R14) 323 MOVD R3, (3*8)(R14) 324 MOVD R4, (4*8)(R14) 325 MOVD R5, (5*8)(R14) 326 MOVD R6, (6*8)(R14) 327 MOVD R7, (7*8)(R14) 328 329 // Push C callee-save registers R19-R28. 330 // LR, FP already saved. 331 SAVE_R19_TO_R28(-18*8) 332 333 // Create a struct callbackArgs on our stack. 334 MOVD $cbargs-(18*8+callbackArgs__size)(SP), R13 335 MOVD R12, callbackArgs_index(R13) // callback index 336 MOVD R14, R0 337 MOVD R0, callbackArgs_args(R13) // address of args vector 338 MOVD $0, R0 339 MOVD R0, callbackArgs_result(R13) // result 340 341 // Call cgocallback, which will call callbackWrap(frame). 342 MOVD $·callbackWrap<ABIInternal>(SB), R0 // PC of function to call, cgocallback takes an ABIInternal entry-point 343 MOVD R13, R1 // frame (&callbackArgs{...}) 344 MOVD $0, R2 // context 345 MOVD R0, (1*8)(RSP) 346 MOVD R1, (2*8)(RSP) 347 MOVD R2, (3*8)(RSP) 348 BL runtime·cgocallback(SB) 349 350 // Get callback result. 351 MOVD $cbargs-(18*8+callbackArgs__size)(SP), R13 352 MOVD callbackArgs_result(R13), R0 353 354 RESTORE_R19_TO_R28(-18*8) 355 356 RET 357 358 // uint32 tstart_stdcall(M *newm); 359 TEXT runtime·tstart_stdcall(SB),NOSPLIT,$96-0 360 SAVE_R19_TO_R28(-10*8) 361 362 MOVD m_g0(R0), g 363 MOVD R0, g_m(g) 364 BL runtime·save_g(SB) 365 366 // Set up stack guards for OS stack. 367 MOVD RSP, R0 368 MOVD R0, g_stack+stack_hi(g) 369 SUB $(64*1024), R0 370 MOVD R0, (g_stack+stack_lo)(g) 371 MOVD R0, g_stackguard0(g) 372 MOVD R0, g_stackguard1(g) 373 374 BL runtime·emptyfunc(SB) // fault if stack check is wrong 375 BL runtime·mstart(SB) 376 377 RESTORE_R19_TO_R28(-10*8) 378 379 // Exit the thread. 380 MOVD $0, R0 381 RET 382 383 // Runs on OS stack. 384 // duration (in -100ns units) is in dt+0(FP). 385 // g may be nil. 386 TEXT runtime·usleep2(SB),NOSPLIT,$32-4 387 MOVW dt+0(FP), R0 388 MOVD $16(RSP), R2 // R2 = pTime 389 MOVD R0, 0(R2) // *pTime = -dt 390 MOVD $-1, R0 // R0 = handle 391 MOVD $0, R1 // R1 = FALSE (alertable) 392 MOVD runtime·_NtWaitForSingleObject(SB), R3 393 SUB $16, RSP // skip over saved frame pointer below RSP 394 BL (R3) 395 ADD $16, RSP 396 RET 397 398 // Runs on OS stack. 399 // duration (in -100ns units) is in dt+0(FP). 400 // g is valid. 401 // TODO: neeeds to be implemented properly. 402 TEXT runtime·usleep2HighRes(SB),NOSPLIT,$0-4 403 B runtime·abort(SB) 404 405 // Runs on OS stack. 406 TEXT runtime·switchtothread(SB),NOSPLIT,$16-0 407 MOVD runtime·_SwitchToThread(SB), R0 408 SUB $16, RSP // skip over saved frame pointer below RSP 409 BL (R0) 410 ADD $16, RSP 411 RET 412 413 TEXT runtime·nanotime1(SB),NOSPLIT|NOFRAME,$0-8 414 MOVB runtime·useQPCTime(SB), R0 415 CMP $0, R0 416 BNE useQPC 417 MOVD $_INTERRUPT_TIME, R3 418 MOVD time_lo(R3), R0 419 MOVD $100, R1 420 MUL R1, R0 421 MOVD R0, ret+0(FP) 422 RET 423 useQPC: 424 B runtime·nanotimeQPC(SB) // tail call 425 426 // This is called from rt0_go, which runs on the system stack 427 // using the initial stack allocated by the OS. 428 // It calls back into standard C using the BL below. 429 TEXT runtime·wintls(SB),NOSPLIT,$0 430 // Allocate a TLS slot to hold g across calls to external code 431 MOVD runtime·_TlsAlloc(SB), R0 432 SUB $16, RSP // skip over saved frame pointer below RSP 433 BL (R0) 434 ADD $16, RSP 435 436 // Assert that slot is less than 64 so we can use _TEB->TlsSlots 437 CMP $64, R0 438 BLT ok 439 MOVD $runtime·abort(SB), R1 440 BL (R1) 441 ok: 442 443 // Save offset from R18 into tls_g. 444 LSL $3, R0 445 ADD $TEB_TlsSlots, R0 446 MOVD R0, runtime·tls_g(SB) 447 RET