Repository: go
Issue: cmd/compile: register ABI: allow selecting ABI when Go code refers to assembly routine
Labels: NeedsInvestigation,umbrella,compiler/runtime


[Note: this is a tracking issue, not a regular bug.]

In [CL 260477](https://go-review.googlesource.com/c/go/+/260477) as part of the register ABI work (tracking issue [40724](https://golang.org/issue/40724)), the assembler was changed to add syntax for specifying the ABI (ABI0 vs ABIInternal) when defining a given assembler routine or referring to some other routine. This change made it possible to have finer control over "special" assembler routines that are defined and used in non-standard ways.

For example, a function such as `runtime.panicIndex` is called by the compiler using a custom ABI (not ABI0), hence in order to get the correct semantics, there had to be some way to insure that a call from Go code to `runtime.panicIndex` did not go through an ABIInternal->ABI0 wrapper.  Changing the assembly definition of `runtime.panicIndex` to have it be ABIInternal took care of this, but it is worth pointing out that declaring `runtime.panicIndex` as ABIInternal is something of a fiction, since the compiler currently doesn't follow the new ABIInternal rules exactly when emitting calls to the function.

The function `runtime.goexit` is another example: this function is never called, only used as a sentinel (e.g. the runtime takes its address, but never actually invokes it).  When the runtime takes its address, however, the actual PC of the function itself it needed, as opposed to taking the address of the ABIInternal->ABI0 wrapper for the function.  To resolve this problem, the assembly source for `runtime.goexit` was changed the to give it a definition as ABIInternal-- this meant that references from the runtime Go code would pick up the function directly, as opposed to the ABIInternal->ABI0 wrapper for it.  Again here things are a little misleading, since there will never be an ABIInternal call, or any other call, to `runtime.goexit`.

A third case comes up with syscall wrappers: for functions like the x509 assembly [wrappers](https://go.googlesource.com/go/+/26e29aa15a189b26d3b2400a594d329368e78e79/src/crypto/x509/internal/macos/corefoundation.s#9), if we take the address of the wrapper function from Go to pass it to a syscall, we have to insure that we get the function itself and not an ABIInternal->ABI0 wrapper.

To improve on this situation, it might make more sense to instead create some sort of mechanism at the Go level to allow control over which ABI is selected (this idea from @cherrymui). For example, something like

```
  //go:abi0
  func goexit() ...
```

would be a way to say, "Compiler, I know that this function is an assembly routine, but when I refer to it, I want to get the ABI0 symbol and not the ABIInternal->ABI0 wrapper for the symbol". The one potential drawback here is that once this pragma is used in the  package, we can no longer call the routine in question from Go within the package (if for some reason we need to have both direct calls and a reference that bypasses the ABI wrapper). 

Another possibility would be to develop a pragma that applies to the referring expression itself, e.g.

```
  // Take address of "runtime.someAssemblyFunc", insuring that we get
  // the func itself and not the ABIInternal->ABI0 wrapper for the symbol
  myFuncPtr := unsafe.SelectABI(runtime.someAssemblyFunc, "ABI0")
```

An advantage of this second option would be that we could communicate to the compiler that a function uses an unknown or non-standard ABI, and should not be calllable directly from Go. Example:

```
  // Here 'anotherAssemblyRoutine' uses the C++ ABI, so ti should not  be called directly from Go.
  myCppFuncPtr := unsafe.SelectABI(runtime.anotherAssemblyRoutine, "ABIUnspecified")

  // legal (we're just storing the address)
  C.somestruct.cfuncptr = myCppFuncPtr

  // illegal-- generates a compiler error
  myCppFuncPtr(101, nil)
```

Unsure as to how much work either of these options would be to implement.  Also probably might make sense to restrict the use of these constructs to "runtime" packages (as we already do with the assembler feature).