Go Assembly by Example
davidwong.fr
davidwong.fr
> Some others self-hosted native-targeted language implementations (like Golang, Free Pascal, SBCL) have their own assemblers with multiple targets. They may be used for inline assembly inside language, or even included as a library, but not always suitable for standalone application - no command-line tool exists, or only intermediate representation used as a source, or support for targets very limited.
Despite receive so much criticism, Go seems to be doing pretty well at carving out its own place. It's nice to see a language pick certain categories of problems and just focus on that. I haven't had any chance to write something serious in Go, but I've had to dig through a few projects and it's always refreshingly simple to read and understand. Really wish I could say the same thing about C++.
[0] https://en.wikipedia.org/wiki/Comparison_of_assemblers#As_pa...
That's just not a design goal of C++. Different languages have different priorities and appeal to different folks. Personally, while writing C++ can be frustrating at times and the language still has too many arbitrary restrictions for my taste, every time I go through a Go codebase I feel glad I haven't had to write any Go myself. The amount of mindless, repetitive boilerplate one has to write because the language tries to be "simple" at the expense of abstraction, genericity and expressiveness looks intolerable to me.
But, clearly, there are people that enjoy writing Go and that's great. More diversity in language design can only be a good thing.
Depending on the application, the utility of the ecosystem often outweighs the constraints of the language. But inevitably, Go will have to improve as a language before some other language's ecosystem improves and eats Go's lunch.
This is the one part where I generally tend to disagree. Basic language constraints are immutable, whereas ecosystem pain points can be fixed if people care enough. Everything you cited is true, the Go people seem to have done a great job at making the ecosystem convenient to use, but language issues are the one thing users can't fix on their own. The C++ ecosystem is fragmented, everyone has their own preferences and we'd all like to see it improved, but individual developers (as well as organizations, just look at Google's internal ecosystem) find a way to make it work well. If there's a dealbreaker in language design, on the other hand, there's just nothing you can do.
It's also worth noting that these "prohibitive language issues" can be solved with code generation in the worst case, and while this is often unjustifiably tedious, it's still far simpler than basic C++ project management (maintaining CMake, integrating an editor into your project, setting up testing, cross compiling, dealing with templates and compiler errors, etc). And I say all of this as a former C++ developer. If you can make C++ work in the basic cases, Go's worst case is still far nicer for all but the most performance-demanding applications.
Overall, I feel like the ecosystem utility / tooling situation has improved hugely over the past few years, to the point where it's comparable to any other language out there, though at the expense of requiring some user effort in choosing tools and getting a bit used to them. That's just my personal opinion, though, YMMV.
After wasting half a day staring at a totally nonsense generic class definition in Java spanning more than ten lines of code (after having worked in with Java for many years) I do not want any programmer to be clever any more, except he or she has so much social (and code) proof, that I will be delighted to learn something in such an exercise.
For the rest - I might add as a hyperbole - even Golang is too expressive.
I clicked through to the AES example, and found that it "makes use of Go Assembly to leverage Intel's hardware support for AES, calling the AES-NI CPU instructions." So does that only work on Intel CPUs?
I'm confused about why Go has its own assembly syntax if you're going to be writing platform-specific code anyway. Or can you write Go assembly that works well on both x86 and ARM, say?
EDIT to put the question a different way: what are some use cases where you'd use Go assembly? In C, you'd typically drop down to platform-specific assembly for two reasons: 1) to muck about with the stack, for example in a threading library, or 2) to use platform-specific instructions, like NEON or SSE. (In that second case you can often get away with intrinsics rather than assembly, though.) Does Go assembly have the same uses, or different ones?
I'm partly with the dtrace guy, but it is nice to see somebody not propagating that gcc inline asm shite... so I'm prepared to forgive it a certain amount.
But I can't quite figure out whether this Go assembly stuff is a real feature that makes sense and is well-designed, or if it's just a quirk of the implementation, stemming from its background in Plan 9.
The assembly section in the Go docs literally says "go read the Plan 9 docs first". That's kind of weird.
https://github.com/golang/go/tree/master/src/hash/crc32
That has different .s files for different architectures, and a .go file with the platform-independent version, so the assembly clearly isn't platform-independent.
It seems a strange decision to disguise e.g. arm64 assembly behind an x86-like syntax, so I'm going to go with "quirk of the implementation."
https://talks.golang.org/2016/asm.slide#39
The particular slide linked above answers the question "what are the advantages of having their own assembler." Turns out, they'll be able to abstract away the differences across the different manufacturers' assemblers to where they can just feed in the PDFs to be able to support that platform.
It's an interactive compiler allowing you fiddle around with the generated assembly.
Additionally, i see that the opcodes are not machine language (target) machine opcodes... It feels a bit ackward to have to manually program into an assembler that is not really targeting the actual CPU; kind of defeats one of the reasons to write in assembler in the first place.
As for Go, i always recall this article, which I found interesting:
Also, when is an appropriate time to use assembly in a Go program? After the failed trig experiment blog post that was featured here last week, the benefits aren't very clear to me.
EDIT: ok, I see it now. The empty function declaration. What an odd syntax. I get the tutorial now, but still wondering about the benefits.
After looking at the comments from people more knowledgeable than me, I realized that it had failed mostly because the assembly that I wrote was pretty bad. You have to know assembly language pretty well to beat the compiler.
I have been spending more time with this. And I understand my faults a bit better now. There are a lot of places which can be improved. And I have sent a CL to add some commands to the Go assembler (so that I don't have to write the ugly hex codes :P)
I plan to revisit the code once the CL is merged. And see how it goes.
To answer your question - You might get benefits if:
* You really understand what the compiler is doing.
* You know the current shortcomings of the Go assembler and the latest amd64 instruction set.
* You know assembly language well enough to write custom code for your problem and beat the compiler.
From what I have seen - the Go assembler attempts to generate binary code which supports old amd64 processors too. As a result of which you cannot take advantage of the latest instructions which come as part of AVX2 extensions. If you are doing high performance math stuff and know the AVX2 instruction set like the back of your hand, then you are sure to reap benefits.
Cheers.
There's a lower level `go tool` command, of course, but I don't know it off the top of my head.
[0]: https://github.com/golang/go/tree/master/src/hash/crc32