[HN Gopher] How to store Go pointers from assembly
       ___________________________________________________________________
        
       How to store Go pointers from assembly
        
       Author : pdziepak
       Score  : 67 points
       Date   : 2025-06-23 12:40 UTC (10 hours ago)
        
 (HTM) web link (mazzo.li)
 (TXT) w3m dump (mazzo.li)
        
       | jasonthorsness wrote:
       | Go assembler is such a strange inclusion - I guess the idea is it
       | lets you do low-level routines without requiring CGO?
        
         | nu11ptr wrote:
         | That is my understanding. It lets you bypass CGo overhead, but
         | I'd be lying if I said I fully understood it.
        
           | MangoToupe wrote:
           | Yes, to a certain extent if you have knowledge about the code
           | you're calling into, you can "cheat" and stay on the go stack
           | rather than switching to the C one. This take pretty solid
           | knowledge of both runtimes to manage.
        
         | pjmlp wrote:
         | Not really, having an Assembler around has been quite common in
         | compiled languages, before the rise of scripting languages in
         | the 2000's.
         | 
         | For all my complaints about Go's design, that is certainly one
         | that I appreciate.
        
         | Thaxll wrote:
         | It's heavily used where you need performance, for example
         | crypto.
         | 
         | https://go.dev/wiki/AssemblyPolicy
         | 
         | https://github.com/golang/go/tree/master/src/crypto/internal...
        
           | nasretdinov wrote:
           | My understanding is that for crypto specifically it constant-
           | time algorithms matter due to security implications, and
           | those are only available when you use specific branchless
           | assembly instructions, so it's not just performance
        
             | charcircuit wrote:
             | CPUs do not guarantee that branchless instructions always
             | take the same amount of time.
        
               | Arnavion wrote:
               | CPUs guarantee what they guarantee, and if they guarantee
               | that a certain instruction takes an operand-independent
               | time then it does.
               | 
               | For example a RISC-V CPU implementing the Zkt extension
               | is required to implement a whole bunch of logical and
               | arithmetic integer operations with operand-independent
               | timing. This includes the two "branchless move"
               | instructions from the Zicond extension, czero.eqz and
               | czero.nez.
        
             | foobiekr wrote:
             | Just to pick nits, the important thing is basically no
             | secret-dependent { branches, loop bounds checks, memory
             | accesses }. This is a lot more complex than simple
             | "constant time."
        
         | cyberax wrote:
         | Remember that Go actually compiles the code to machine code
         | directly, so it needs to have an assembler for its compiler.
         | And if you have it, then why not make it available?
        
           | Stratoscope wrote:
           | > _Go actually compiles the code to machine code directly_
           | 
           | True.
           | 
           | > _so it needs to have an assembler for its compiler._
           | 
           | No, it doesn't need an assembler for this. As you said
           | correctly, it compiles to machine code _directly_.
           | 
           | While it was once fairly common to use assembly as an
           | intermediate step, very few or any modern compilers do that.
           | They just compile directly to binary machine code.
           | 
           | Go does have a -S flag to generate assembly language so you
           | can review the generated code more easily. But that assembly
           | code isn't part of the compilation pipeline, it's just
           | optional output for human review.
        
             | kbolino wrote:
             | I think this is just half true.
             | 
             | When dealing with purely high-level code (including C
             | without inline assembly), the compiler doesn't need a
             | discrete assembler. This much is true, and most modern
             | compilers will not spit out assembly unless requested.
             | 
             | However, there's usually still a stage of the compilation
             | process where an assembly-like internal representation is
             | used. Once the compiler has chosen which registers to use
             | and where, and which instructions to use and in what order,
             | etc., it's close to machine code but not fully there yet.
             | At this point, jump targets will still be labels without
             | concrete addresses, instructions will be still be
             | referenced by mnemonic, etc.
             | 
             | Serializing this internal representation as assembly will
             | generally produce more readable code than disassembling the
             | final binary. So it's not assembly exactly, but it's pretty
             | close.
        
               | Stratoscope wrote:
               | Yes, very true! And more accurate than what I wrote. The
               | point I meant to make was that assembly language in the
               | text form that we would use is not part of the
               | compilation process. But I oversimplified my description
               | rather badly.
               | 
               | Interestingly, Go also has a -d=ssa/all switch that
               | outputs not just an assembly representation of the final
               | code, but also the results of each optimization pass.
               | 
               | Here is a discussion I had with ChatGPT about this:
               | 
               | https://chatgpt.com/share/6859aea5-df1c-8012-be70-f236106
               | 0fb...
        
             | MangoToupe wrote:
             | The division between 'assembler' and 'generating machine
             | code directly' can be pretty blurry depending on the
             | interface. I imagine a compiler would basically interleave
             | what the assembler would normally do with the code
             | generation. I don't think this really yields deep insight,
             | though, you're effectively doing the same thing either way.
        
         | foobiekr wrote:
         | Sometimes you need assembly. There's nothing shocking here
         | other than Go doing this well when it's a gap for most
         | languages.
        
       | rsc wrote:
       | This is a strange and dangerous thing to try to do from assembly.
       | In particular, all these details about write barriers being hand-
       | coded in the assembly are subject to change from release to
       | release.
       | 
       | Better to structure your code so that you do the pointer
       | manipulation (and allocation) in Go code instead, and leave
       | assembly only for what is absolutely necessary for performance
       | (usually things like bulk operations, special instructions, and
       | so on).
        
         | corsix wrote:
         | In this case, "atomic 128-bit store" is the special
         | instruction, with the twist that half of those 128 bits contain
         | a pointer.
        
         | titzer wrote:
         | > Better to structure your code so that you do the pointer
         | manipulation (and allocation) in Go code instead, and leave
         | assembly only for what is absolutely necessary for performance
         | (usually things like bulk operations, special instructions, and
         | so on).
         | 
         | While I generally agree with this, one way to mitigate the
         | maintenance issue is to offer a macro assembler instruction
         | that performs a write barrier that is kept up to date with what
         | the Go compiler emits. If it the compiler itself uses that
         | macro assembler method, it's already pretty easy.
         | 
         | After many years of racking my brain on how to make each of the
         | things "blessed and bulletproof", I've realized that systems
         | languages will inevitably be used by people building _high
         | performance_ systems. People doing this will go to crazy
         | lengths and low-level hackery that you can 't anticipate, e.g.
         | generating their own custom machine code[1], doing funky memory
         | mapping tricks, etc. The boundaries between that machine code
         | and your language are always going to run into representational
         | issues like write barriers and object layouts.
         | 
         | [1] Pretty much all I do is language VMs, and there is no way
         | around this. It will have a JIT compiler.
        
       | hsnice16 wrote:
       | I am reading a book on Go, and interestingly I had also tweeted
       | about the Go GC recently.
       | https://x.com/hsnice16/status/1936005685214433437
        
       ___________________________________________________________________
       (page generated 2025-06-23 23:01 UTC)