Repository: go
Issue: cmd/compile: switch to a register-based calling convention for Go functions
Labels: Proposal,Proposal-Accepted,compiler/runtime

I propose that we switch the Go internal ABI (used between Go functions) from stack-based to register-based argument and result passing for Go ~1.16~ 1.17.

I lay out the details of our proposal and the work required in [this document](https://golang.org/design/40724-register-calling).

The ABI specification can be found [here](https://go.googlesource.com/go/+/refs/heads/dev.regabi/src/cmd/compile/internal-abi.md).

This was previously proposed in #18597, where @dr2chase did some excellent prototyping work that our new proposal builds on. With multiple ABI support, we’re now in a much better position to execute on this without breaking compatibility with the existing body of Go assembly code. I’ve opened this new issue to focus on discussion of our new proposal.

/cc @dr2chase @danscales @thanm @cherrymui @mknyszek @prattmic @randall77 

An incomplete and evolving list of tasks:

- [X] Define ABI specification ([CL](https://golang.org/cl/285292), @aclements)
- [X] Add GOEXPERIMENT=regabi and asm macro ([CL](https://golang.org/cl/252258))
- [x] cmd/compile: late call lowering ([CLs](https://go-review.googlesource.com/q/hashtag:%22drchase-late-call%22))
- [x] cmd/compile: implement register arguments (@dr2chase)
    - [x] spill around morestack call ([CL](https://golang.org/cl/279520))
    - [x] add temporary ABI pragma for testing ([CL](https://golang.org/cl/279521))
    - [x] add abstract argument registers ([WIP CL](https://golang.org/cl/260539))
    - [x] implement ABI rules ([CL](https://golang.org/cl/273011))
    - [x] use abstract argument registers
- [x] cmd/compile: implement register returns
    - [x] late return lowering ([CL](https://golang.org/cl/279519), @dr2chase)
    - [x] implement ABI rules ([CL](https://golang.org/cl/273011), @thanm)
- [x] cmd/compile: ABI wrappers ([CL](https://golang.org/cl/270863), [old CL](https://golang.org/cl/259445))
    - [x] cmd/link: mangle wrapper names for ELF ([CL](https://golang.org/cl/289289))
    - [x] cmd/link: mangle wrapper names for PE ([CL](https://golang.org/cl/304432))
    - [x] implement zeroing of X15 in ABI0->ABIInternal wrappers ([CL](https://golang.org/cl/288093))
- [X] cmd/asm: add syntax for runtime packages to reference ABIInternal symbols ([CL](https://golang.org/cl/260477))
    - [x] Mark runtime.{goexit,asyncPreempt}, reflect.{makeFuncStub,methodValueCall} definitions ABIInternal ([CL](https://golang.org/cl/262319))
    - [x] Mark reference to runtime.callbackWrap in sys_windows_amd64.s as ABIInternal ([CL](https://golang.org/cl/305669))
- [x] runtime: fix assembly closure calls (mcall) (@aclements, [CL](https://golang.org/cl/305669))
- [x] reflect: support calling ABIInternal ([CL](https://golang.org/cl/272568))
- [x] reflect: support MakeFunc and method value calls ([CL](https://golang.org/cl/298670))
- [x] go and defer statements
    - [x] cmd/compile: desugar go/defer to zero-argument (@thanm, [CL](https://golang.org/cl/298669))
    - [x] cmd/compile: fix `defer recover()` (@cherrymui, [CL](https://golang.org/cl/304771))
    - [x] cmd/compile: desugar go/defer with results (@cherrymui, [CL](https://golang.org/cl/305550))
- [X] cmd/cgo: decouple cgo callbacks from ABI ([CL](https://golang.org/cl/258938))
- [x] runtime: finalizer support ([CL](https://golang.org/cl/300113))
- [x] runtime: Windows callback support ([CL](https://golang.org/cl/300113))
- [x] cmd/internal/obj: introduce and use `RegEntryTmp` registers in prologue (@mknyszek, [CL](https://golang.org/cl/303314))
- [x] cmd/compile: generate correct ABI0 argument maps for body-less functions (see `WriteFuncMap`) (@dr2chase, [CL](https://golang.org/cl/302072))
- [x] cmd/compile: make `cgo_unsafe_args` generate an ABI0 function (or an ABIInternal-with-no-registers function) (@cherrymui, [CL](https://golang.org/cl/306609))
- [x] runtime: update debug call protocol ([CL](https://golang.org/cl/309169))
- [x] Fix ABI0/ABIInternal confusion in cgo ([CL](https://golang.org/cl/309341))
- [x] Run signature fuzzer and fix bugs (everyone)
- [x] Get all.bash to pass on Linux, Windows, and Darwin (everyone)

High-priority non-critical path
- [x] Export ABI information across package boundaries to eliminate more wrappers (@aclements, [CL](https://golang.org/cl/305274))
- [x] Make `funcPC` always return the "native" PC of a function (maybe also introduce `ABIOther`) (@cherrymui, [CL](https://golang.org/cl/304232))
- [x] Strip unnecessary ABIInternal annotations once `funcPC` is in
- [x] Tracebacks
    - [x] Define extended argument metadata format (in progress, @cherrymui)
    - [x] cmd/compile: produce extended argument metadata (@cherrymui, [CL](https://golang.org/cl/304470))
    - [x] runtime: consume extended argument metadata (@cherrymui, [CL](https://golang.org/cl/304470))
    - [x] cmd/compile,runtime: traceback metadata for spill slot liveness
- [x] DWARF
    - [x] cmd/compile: ensure arguments have appropriate DWARF locations
    - [x] Ensure GDB and Delve can find register arguments
    - ~[ ] cmd/compile: add a DWARF vendor attribute with function argument frame size ([context](https://golang.org/cl/309169))~
- [x] Wrapper mangling for non-critical arches
    - [x] cmd/link: mangle wrapper names for Mach-O ([CL](https://golang.org/cl/307229))
    - [x] cmd/link: mangle wrapper names for xcoff
    - [x] cmd/link: mangle wrapper names for Plan 9
- [x] runtime: simplify go/defer call paths
    - [x] assert that go/defer frames are zero sized ([CL](https://golang.org/cl/306009))
    - [x] replace defer reflectcall with a direct closure call ([CL](https://golang.org/cl/306010))
- [x] cmd/internal/obj: reject splittable ABIInternal functions without morestack spill info (e.g., asm functions) because we can't generate a correct morestack path ([CL](https://golang.org/cl/309789))
- [x] cmd/asm: don't reference `args_stackmap` in ABIInternal functions (because it's for ABI0) ([CL](https://golang.org/cl/309790))
- [x] Port performance-critical assembly to ABIInternal (see [CL 296372](https://golang.org/cl/296372) for valuable functions) ([CL 308931](https://golang.org/cl/308931), [CL 310184](https://golang.org/cl/310184))
- [x] math: fix overhead of assembly->Go calls [CL 310331](https://golang.org/cl/310331)
- [x] Release notes
  - Mention undocumented behavior we've observed applications depending on that may have changed.
    - Using `reflect.ValueOf(fn).Pointer()` to get the PC of an assembly function will now return the PC of the ABI wrapper
  - Performance surprise: calling from assembly to Go is slightly more expensive.
  - Performance surprise: calling an assembly function via a closure is slightly more expensive.
  - Traceback format is much improved

Enabling steps
- [x] Enable regabiwrappers by default on amd64 (Linux, Windows, and Darwin)
- [x] Enable regabidefer
- [x] Enable regabireflect
- [x] Enable regabig
- [x] Enable regabiargs

Testing
- [ ] [Function signature fuzzer](https://github.com/thanm/cabi-testgen) (ongoing)
    - [ ] Static call, closure call, method call, interface call, `reflect.{ValueOf(target),MakeFunc(x, target),Method(x)}.{Call,Interface}`, called from `defer`, called from `go`, called as a finalizer
    - [x] {Big,Small} {argument,result} in {memory,registers} and {not addressed,addressed and not leaked to heap,addressed and leaked to heap}
    - [x] Use `defer` in a test function (check arguments, modify results)
    - [x] Pass pointers-to-stack and pointers-to-heap
    - [x] Cause result to move to heap
    - [x] runtime: add `MoveStackOnNextCall`, assertions for pointer-to-stack/pointer-to-heap, assertions for live/dead (@aclements, [CL](https://golang.org/cl/305330))
    - [x] Integrate runtime testing hook
    - [ ] Clean up fuzzer, and get into tools repo.
- [x] cmd/compile: revive clobberdead ([CL](https://golang.org/cl/301791))
- [x] cmd/compile: add clobberdeadreg ([CL](https://golang.org/cl/302809))

Post-MVP
- [x] Move g from TLS to register ([CL](https://golang.org/cl/289196))
    - [x] Use g register on non-Linux OSes (see [CL](https://golang.org/cl/289718))
- [x] Reserve and use X15 as zero register ([CL](https://golang.org/cl/288093))
    - ~[ ] Should this be X0 for more compact instruction encoding?~
- [ ] Port to other platforms
   - [x] amd64 
   - [x] arm64
   - [ ] mips
   - [x] ppc
   - [ ] s390x
   - [x] risc-v
   - ~[ ] porting guide~
- [x] Eliminate legacy ABI paths for all platforms, even if they're using ABIInternal with 0 registers
- [ ] Eliminate spill slots?
- [ ] Pass pointer-shaped method receiver in context register (#44827)
- [x] runtime: simplify go/defer call paths
    - [x] simplify go/defer paths to assume zero args/results
    - [x] simplify special _defer allocator and remove special _defer cases from mallocgc (because _defer records will be fixed size).
- [ ] cmd/vet: add syntax and checks for register arguments and results in assembly like x+0(FP)
- [ ] cmd/vet: check argument size for ABIInternal functions and fix runtime asm declarations

Cleanup (can be done later)
- [ ] runtime: port all assembly -> Go calls to ABIInternal?
- [x] Eliminate ABIAlias support from cmd/compile, cmd/link, and object format
- [x] remove now-redundant fields from ssa.AuxCall
- [x] rationalize use of LocalsOffset/FrameOffset (for registers that use a link register, e.g. arm64, powerppc).
- [ ] cmd/compile: revisit abiutils.go and clean up the API (also reduce ABIConfig copying)
- [ ] cmd/internal/obj: generate stack maps in obj so we can better compact the morestack/body maps and reduce subtlety?
- [x] cmd/compile,runtime: re-enable passing arguments to `go` in runtime ([context](https://go-review.googlesource.com/c/go/+/298669/comment/68535db6_d8c8f271/))
- [ ] cmd/compile: always attach `ir.Func` to function `ir.Name`s ([context](https://golang.org/cl/305271))
- [x] cmd/compile: support ABIInternal for tail calls ([context](https://golang.org/cl/307234))
- [x] cmd/compile: once wrapping of defers is on for all arch/OS combinations, it should be possible to simplify or remove some of the openDefer processing in ssagen (notablly we could get rid of [openDeferSave()](https://go.googlesource.com/go/+/b56177a3037a035ee7f74e619838b6d853697100/src/cmd/compile/internal/ssagen/ssa.go#4697)
- ~[ ] runtime: think about an ABI-insensitive debugCall~ Delve uses DWARF information
- [ ] Fix up named slots & value association
- [ ] fix `firstpos` in `ssa.go` – sometimes it's late.