diff options
-rw-r--r-- | docs/faq.rst | 160 | ||||
-rw-r--r-- | docs/index.rst | 1 |
2 files changed, 161 insertions, 0 deletions
diff --git a/docs/faq.rst b/docs/faq.rst new file mode 100644 index 000000000..84cb63b98 --- /dev/null +++ b/docs/faq.rst @@ -0,0 +1,160 @@ +.. faq: + +Frequently Asked Questions +========================== + + +What is TinyGo exactly? +----------------------- + +A new compiler and a new runtime implementation. + +Specifically: + + * A new compiler using (mostly) the standard library to parse Go programs and + using LLVM to optimize the code and generate machine code for the target + architecture. + + * A new runtime library that implements some compiler intrinsics, like a + memory allocator, a scheduler, and operations on strings. Also, some + packages that are strongly connected to the runtime like the ``sync`` + package and the ``reflect`` package have been or will be re-implemented for + use with this new compiler. + + +Why a new compiler? +------------------- + +Why not modify the existing compiler to produce binaries for microcontrollers? + +There are several reasons for this: + + * The standard Go compiler (``gc``) does not support instruction sets as used + on microcontrollers: + + * The Thumb instruction set is unsupported, but it should be possible to + add support for it as it already has an ARM backend. + * The AVR instruction set (as used in the Arduino Uno) is unsupported and + unlikely to be ever supported. + + Of course, it is possible to use ``gccgo``, but that has different problems + (see below). + + * The runtime is really big. A standard 'hello world' on a desktop PC produces + a binary of about 1MB, even when using the builtin ``println`` function and + nothing else. All this overhead is due to the runtime. Of course, it may be + possible to use a different runtime with the same compiler but that will be + kind of painful as the exact ABI as used by the compiler has to be matched, + limiting optimization opportunities (see below). + + * The compiler is optimized for speed, not for code size or memory + consumption (which are usually far more important on MCUs). This results in + design choices like allocating memory on every value → interface conversion + while TinyGo sacrifices some performance for reduced GC pressure. + + * With the existing Go libraries for parsing Go code and the pretty awesome + LLVM optimizer/backend it is relatively easy to get simple Go programs + working with a very small binary size. Extra features can be added where + needed in a pay-as-you-go manner similar to C++ avoiding their cost when + unused. Most programs on microcontrollers are relatively small so a + not-complete compiler is still useful. + + * The standard Go compilers do not allocate global variables as static data, + but as zero-initialized data that is initialized during program startup. + This is not a big deal on desktop computers but prevents allocating these + values in flash on microcontrollers. Part of this is due to how the + `language specification defines package initialization + <https://golang.org/ref/spec#Package_initialization>`_, but this can be + worked around to a large extent. + + * The standard Go compilers do a few special things for CGo calls. This is + necessary because only Go code can use the (small) Go stack while C code + will need a much bigger stack. A new compiler can avoid this limitation if + it ensures stacks are big enough for C, greatly reducing the C ↔ Go calling + overhead. + +`At one point <https://github.com/aykevl/tinygo-gccgo>`_, a real Go compiler +had been used to produce binaries for various platforms, and the result was +painful enough to start writing a new compiler: + + * The ABI was fixed, so could not be optimized for speed. Also, the ABI + didn't seem to be documented anywhere. + + * Working arount limitations in the ``go`` toolchain was rather burdensome + and quite a big hack. + + * The binaries produced were quite bloated, for various reasons: + + * The Go calling convention places all arguments on the stack. Due to + this, stack usage was really bad and code size was bigger than it + needed to be. + + * Global initialization was very inefficient, see above. + + * There seemed to be no way to optimize across packages. + + +Why Go instead of Rust? +----------------------- + +Rust is another "new" and safer language that is now made ready for embedded +processors. There is `a fairly active community around it +<https://rust-embedded.github.io/blog/>`_. + +However, apart from personal language preference, Go has a few advantages: + + * Subjective, but in general Go is `easier to learn + <https://matthias-endler.de/2017/go-vs-rust/>`_. Rust is in general far more + complicated than Go, with difficult-to-grasp ownership rules, traits, + generics, etc. Go prides itself on being a simple and slightly dumb + language, sacrificing some expressiveness for readability. + + * Built-in support for concurrency with goroutines and channels that do not + rely on a particular implementation threads. This avoids the need for a + custom `RTOS-like framework <https://blog.japaric.io/rtfm-v2/>`_ or a + `full-blown RTOS <https://github.com/rust-embedded/wg/issues/45>`_ with the + associated API one has to learn. In Go, everything is handled by goroutines + which are built into the language itself. + + * A batteries-included standard library that consists of loosely-coupled + packages. Rust uses a monolithic standard library that is currently unusable + on bare-metal, while the Go standard library is much more loosely coupled so + is more likely to be (partially) supported. Also, non-standard packages in + Go do not have to be marked with something like ``#![no_std]`` to be usable + on bare metal. Note: most standard library packages cannot yet be compiled, + but this situation will hopefully improve in the future. + +At the same time, Rust has other advantages: + + * Unlike Go, Rust does not have a garbage collector by default and carefully + written Rust code can avoid most or all uses of the heap. Go relies heavily + on garbage collection and often implicitly allocates memory on the heap. + + * Rust has stronger memory-safety guarantees. + + * In general, Rust is more low-level and easier to support on a + microcontroller. Of course, this doesn't mean one shouldn't try to run Go on + a microcontroller, just that it is more difficult. When even dynamic + languages like `Python <https://micropython.org/>`_, `Lua + <https://nodemcu.readthedocs.io/en/master/>`_ and `JavaScript + <https://www.espruino.com/>`_ can run on a microcontroller, then certainly + Go can. + + +What about the ESP8266/ESP32? +----------------------------- + +These chips use the rather obscure Xtensa instruction set. While a port of GCC +exists and Espressif provides precompiled GNU toolchains, there is no support +yet in LLVM (although there have been `multiple attempts +<http://lists.llvm.org/pipermail/llvm-dev/2018-July/124789.html>`_). + +There are two ways these chips might be supported in the future, and both will +take a considerable amount of work: + + * The compiled LLVM IR can be converted into (ugly) C and then be compiled + with a supported C compiler (like GCC for Xtensa). This has been `done + before <https://github.com/JuliaComputing/llvm-cbe>`_ so should be doable. + + * One of the work-in-progress LLVM backends can be worked on to get it in a + usable state. If this is finished, a true TinyGo port is possible. diff --git a/docs/index.rst b/docs/index.rst index 4ce8a2adc..18ca4907a 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -13,4 +13,5 @@ Contents: installation microcontrollers + faq internals |