Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

proposal: improve CALL support #144

Open
dallbee opened this issue Apr 11, 2020 · 3 comments
Open

proposal: improve CALL support #144

dallbee opened this issue Apr 11, 2020 · 3 comments
Labels
proposal Ideas for discussion

Comments

@dallbee
Copy link

dallbee commented Apr 11, 2020

It seems like avo could do a lot more to support the CALL instruction, including helpers and documentation. I'll give a simplified example of a function I had hoped avo would help me write.

The goal I'll present is to do a pure asm implementation of runtime.Callers(), but instead with a callback signalling to continue unwinding the stack.

func Callers(cb func(pc uintptr) bool)

In my terrible psuedo-assembly, that would look something like this:

TEXT Callers
fill:
    MOV BP[1], STACK[1]    // Push PC to stack
    CALL PARAM(cb)
    CMP SP[0], false       // check return value
    MOV 0(BP), BP          // frame = frame.parent
    JNE fill
    RET

Writing this code in go asm is perilous for several reasons:

  1. It requires knowledge of the go calling conventions to pass the values to the callback and then use the return value.
  2. You're maintaining a local pointer, so it's necessary to work with the runtime.
  3. The callback has it's own stack associated with it, which you have to be careful to preserve.

It seems like this would be a perfect use case for avo: those are all considerations that can be handled via code generation.

What I found while attempting to implement this routine was that avo seems to lack any special magic around CALL. Some of the things I had hoped avo could help with:

  • Provide helpers for passing arguments to the callee and retrieving the return value.
  • Automatically inserting the appropriate FUNCDATA and PCDATA instructions to play nicely with the runtime.

Today, writing go asm functions which call into go is widely avoided. To quote Ian Lance Taylor:

You've run into a really hairy area of asm code. My first suggestion is not try to call from assembler into Go.

He explains how PCDATA calls are required, and then goes on to say:

But the details are so complex that they are undocumented

I think avo is in a unique position to open up this class of assembly routines.

@dallbee
Copy link
Author

dallbee commented Apr 11, 2020

FWIW here's the full text of the go asm implementation I have so far:

// func Trace(f func(frame Frame) bool)
TEXT ·Trace(SB), NOSPLIT, $16-0
    NO_LOCAL_POINTERS

    // TODO: notify runtime of these pointers
    MOVQ f+0(FP), SI
    MOVQ BP, DI

fill:
    MOVQ 8(DI), AX
    MOVQ DI, x-16(SP)   // FP -> frame.FP
    MOVQ AX, y-8(SP)    // PC -> frame.PC

    CALL (SI)
    CMPB 16(SP), $0
    MOVQ 0(DI), DI
    JNE fill            // for f(frame) == true
    RET

Which, of course, is completely broken. I'm still learning the details of when PCDATA needs to be called and how to handle the closure's stack frame.

@mmcloughlin
Copy link
Owner

Thanks for this detailed issue, and sorry for my slow reply.

I do think this would be a nice feature for avo to have, as it definitely falls into the category of simplifying the nasty details of Go assembly. However, given the apparent difficulty and I think relatively niche use case, I have to be honest that this is low on my priority list (right now the next big thing is AVX-512). I'll certainly leave the issue open, so if anyone else has this use case please reply here or emoji vote or something.

Just a quick note on the technical details, the argument passing part would be fairly easy since that part is already implemented in the gotypes package. For the stack map, we'd need to extend the API so that people could indicate that certain memory offsets contained pointers. Assuming we did that, the quote "But the details are so complex that they are undocumented" doesn't fill me with hope. Figuring out this complex undocumented format is no doubt possible from the source code, but you'd really want it to be a one-time thing. Do you happen to know if there is any stability promise about PCDATA and FUNCDATA?

@mmcloughlin mmcloughlin added the proposal Ideas for discussion label May 23, 2020
@mmcloughlin mmcloughlin changed the title Improve CALL support proposal: improve CALL support May 23, 2020
@dallbee
Copy link
Author

dallbee commented May 28, 2020

I totally understand this being low priority. Thanks for keeping it open as a proposal!

Unfortunately I have no idea about the stability of PCDATA/FUNCDATA. I know that the Go team has previously looked at using Avo for the generation of some of their own library code (crypto package I think?). I don't know where they landed on that, but perhaps there's some room for cooperation here - no one wants to manually insert those pseudo calls, and closures aren't the only place where it'd be useful to do so.

There's a somewhat infamous blog post where Filippo Valsorda explored fast FFI that bypasses cgo by using go assembly, but his approach shares this same problem as what I've run into: the resulting code isn't sound because it doesn't work with the runtime.

Avo could potentially unlock what he was digging at in a sound manner. I think there's a lot of potential utility in code-generated CALL functionality.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
proposal Ideas for discussion
Projects
None yet
Development

No branches or pull requests

2 participants