github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/src/internal/task/task_stack_amd64_windows.go (about)

     1  //go:build scheduler.tasks && amd64 && windows
     2  
     3  package task
     4  
     5  // This is almost the same as task_stack_amd64.go, but with the extra rdi and
     6  // rsi registers saved: Windows has a slightly different calling convention.
     7  
     8  import "unsafe"
     9  
    10  var systemStack uintptr
    11  
    12  // calleeSavedRegs is the list of registers that must be saved and restored when
    13  // switching between tasks. Also see task_stack_amd64_windows.S that relies on
    14  // the exact layout of this struct.
    15  // The calling convention is described here:
    16  // https://docs.microsoft.com/en-us/cpp/build/x64-calling-convention?view=msvc-170
    17  // Most importantly, these are the registers we need to save/restore:
    18  //
    19  // > The x64 ABI considers registers RBX, RBP, RDI, RSI, RSP, R12, R13, R14,
    20  // > R15, and XMM6-XMM15 nonvolatile. They must be saved and restored by a
    21  // > function that uses them.
    22  type calleeSavedRegs struct {
    23  	// Note: rbx is placed here so that the stack is correctly aligned when
    24  	// loading/storing the xmm registers.
    25  	rbx   uintptr
    26  	xmm15 [2]uint64
    27  	xmm14 [2]uint64
    28  	xmm13 [2]uint64
    29  	xmm12 [2]uint64
    30  	xmm11 [2]uint64
    31  	xmm10 [2]uint64
    32  	xmm9  [2]uint64
    33  	xmm8  [2]uint64
    34  	xmm7  [2]uint64
    35  	xmm6  [2]uint64
    36  	rbp   uintptr
    37  	rdi   uintptr
    38  	rsi   uintptr
    39  	r12   uintptr
    40  	r13   uintptr
    41  	r14   uintptr
    42  	r15   uintptr
    43  
    44  	pc uintptr
    45  }
    46  
    47  // archInit runs architecture-specific setup for the goroutine startup.
    48  func (s *state) archInit(r *calleeSavedRegs, fn uintptr, args unsafe.Pointer) {
    49  	// Store the initial sp for the startTask function (implemented in assembly).
    50  	s.sp = uintptr(unsafe.Pointer(r))
    51  
    52  	// Initialize the registers.
    53  	// These will be popped off of the stack on the first resume of the goroutine.
    54  
    55  	// Start the function at tinygo_startTask (defined in
    56  	// src/internal/task/task_stack_amd64_windows.S). This assembly code calls a
    57  	// function (passed in r12) with a single argument (passed in r13). After
    58  	// the function returns, it calls Pause().
    59  	r.pc = uintptr(unsafe.Pointer(&startTask))
    60  
    61  	// Pass the function to call in r12.
    62  	// This function is a compiler-generated wrapper which loads arguments out
    63  	// of a struct pointer. See createGoroutineStartWrapper (defined in
    64  	// compiler/goroutine.go) for more information.
    65  	r.r12 = fn
    66  
    67  	// Pass the pointer to the arguments struct in r13.
    68  	r.r13 = uintptr(args)
    69  }
    70  
    71  func (s *state) resume() {
    72  	swapTask(s.sp, &systemStack)
    73  }
    74  
    75  func (s *state) pause() {
    76  	newStack := systemStack
    77  	systemStack = 0
    78  	swapTask(newStack, &s.sp)
    79  }
    80  
    81  // SystemStack returns the system stack pointer when called from a task stack.
    82  // When called from the system stack, it returns 0.
    83  func SystemStack() uintptr {
    84  	return systemStack
    85  }