tinygo-org / tinygo

Go compiler for small places. Microcontrollers, WebAssembly (WASM/WASI), and command-line tools. Based on LLVM.
https://tinygo.org
Other
14.72k stars 858 forks source link

compiler: add inline assembly for i386 and amd64 #4311

Closed leongross closed 1 week ago

leongross commented 1 week ago

Currently tinygo does not support inline assembly for x86 (neither 32-bit nor 64-bit). This PR adds this functionality.

leongross commented 1 week ago

apparently this can also be done using device.Asm, no need for these wrappers for each architecture?

aykevl commented 1 week ago

The best way to use inline assembly is actually by using CGo. See this file for an example:

https://github.com/tinygo-org/drivers/blob/release/ws2812/ws2812_avr.go

I've wanted to remove inline assembly from TinyGo entirely for a while but never actually got around to doing it. The reason is that it's incredibly complex so needs a complex API. But there's one already: in C. So no need to reinvent it.

If performance is a concern: a static inline CGo function will typically be inlined directly into Go (unlike the main Go implementation which has a big CGo overheid).

leongross commented 5 days ago

I see your point with CGO but there are use cases where CGO cannot/should not be used and these projects would not have any custom assembly support. In u-root for example afaik we do not use CGO so this would make things way more complicated.

aykevl commented 5 days ago

Why do you not use CGo? In the case of TinyGo, it's not even possible to disable CGo support - it's always enabled.

leongross commented 18 hours ago

Could you please elaborate on that? Why is CGO impossible to disable?

Maybe I can give you a rundown of what I am trying to achieve here. On the u-root project, we want to keep the size of the binaries small, so CGO is disabled to prohibit any use of C libraries, so the entire project disables it. Currently, this is policy and can't/shouldn't be changed. There won't be any C libraries in the binary, which renders CGO inline assembly unusable for us.

Now, some libraries like github.com/intel-go/ need to use inline assembly for instructions like cpuid. Often, libraries use assembly files for that, which afaik tinygo does not support except for the target definitions. That leaves us with using the tinygo inline assembly approach that you want to deprecate.

Could you think of any other way how to drop-in and replace these assembly files in external packages we depend on? And what speaks against enabling support for assembly files as upstream go has?

leongross commented 14 hours ago

Also, I can observe, that the CGO_ENABLED variable in fact does make a difference in building applications; When I let cgo enabled I tend to get the following error:

# os/user
../../../../../usr/lib/golang/src/os/user/cgo_lookup_cgo.go:14:6: not implemented: build constraints in #cgo line

When setting CGO_ENABLED=0 this error disappears. Can you tell me what's going on there?

aykevl commented 13 hours ago

Could you please elaborate on that? Why is CGO impossible to disable?

There are a few systems where CGo is required for TinyGo to work at all, such as MacOS and the rp2040 chip. Previously it was possible to disable CGo but that only led to confused users. See: https://github.com/tinygo-org/tinygo/pull/4136

On the u-root project, we want to keep the size of the binaries small, so CGO is disabled to prohibit any use of C libraries, so the entire project disables it.

TinyGo works differently. It does not include external C libraries. On Linux, there are two that are always included (and statically linked): compiler-rt and musl.

I did a quick check and musl currently takes up around 5kB of binary size. You can check this yourself using the -size=full parameter. But, again, this is for pretty critical stuff that can't just be removed.

If we were to make it possible to disable CGo, you wouldn't actually gain anything from this. The compiler-rt and musl libraries would still be included. The only thing you would disable is the import "C" construct.

And what speaks against enabling support for assembly files as upstream go has?

This is actually much more difficult than it may appear to be. In fact, I tried this (see https://github.com/tinygo-org/tinygo/pull/3103) and while it worked, it was one giant hack that was pretty brittle. So no, unless the Go project makes it a lot easier to reuse those assembly files we're not going to do that.

Also, I can observe, that the CGO_ENABLED variable in fact does make a difference in building applications; When I let cgo enabled I tend to get the following error:

Odd, I think we're not passing the CGO_ENABLED environment variable to our internal go list call. That's a bug in TinyGo. In any case, this specific build issue (with os/user) has been fixed in our latest release (by replacing the os/user package with a stub).