This makes sure that the LLVM target features match the one generated by
Clang:
- This fixes a bug introduced when setting the target CPU for all
targets: Cortex-M4 would now start using floating point operations
while they were disabled in C.
- This will make it possible in the future to inline C functions in Go
and vice versa. This will need some more work though.
There is a code size impact. Cortex-M4 targets are increased slightly in
binary size while Cortex-M0 targets tend to be reduced a little bit.
Other than that, there is little impact.
This commit adds support for musl-libc and uses it by default on Linux.
The main benefit of it is that binaries are always statically linked
instead of depending on the host libc, even when using CGo.
Advantages:
- The resulting binaries are always statically linked.
- No need for any tools on the host OS, like a compiler, linker, or
libc in a release build of TinyGo.
- This also simplifies cross compilation as no cross compiler is
needed (it's all built into the TinyGo release build).
Disadvantages:
- Binary size increases by 5-6 kilobytes if -no-debug is used. Binary
size increases by a much larger margin when debugging symbols are
included (the default behavior) because musl is built with debugging
symbols enabled.
- Musl does things a bit differently than glibc, and some CGo code
might rely on the glibc behavior.
- The first build takes a bit longer because musl needs to be built.
As an additional bonus, time is now obtained from the system in a way
that fixes the Y2038 problem because musl has been a bit more agressive
in switching to 64-bit time_t.
This is really just a preparatory commit for musl support. The idea is
to store not just the archive file (.a) but also an include directory.
This is optional for picolibc but required for musl, so the main purpose
of this commit is the refactor needed for this change.
This brings a bit more consistency to libc configuration. It seems
better to me to set the header flags all in the same place, instead of
some in Go code and some in JSON target files (depending on the target).
This is for consistency with Clang, which always adds a CPU flag even if
it's not specified in CFLAGS.
This commit also adds some tests to make sure the Clang target-cpu
matches the CPU property in the JSON files.
This does have an effect on the generated binaries. The effect is very
small though: on average just 0.2% increase in binary size, apparently
because Cortex-M3 and Cortex-M4 are compiled a bit differently. However,
when rebased on top of https://github.com/tinygo-org/tinygo/pull/2218
(minsize), the difference drops to -0.1% (a slight decrease on average).
This adds support for stdio in picolibc and fixes wasm_exec.js so that
it can also support C puts. With this, C stdout works on all supported
platforms.
This commit changes a target triple like "armv6m-none-eabi" to
"armv6m-unknown-unknow-eabi". The reason is that while the former is
correctly parsed in Clang (due to normalization), it wasn't parsed
correctly in LLVM meaning that the environment wasn't set to EABI.
This change normalizes all target triples and uses the EABI environment
(-eabi in the triple) for Cortex-M targets.
This change also drops the `--target=` flag in the target JSON files,
the flag is now added implicitly in `(*compileopts.Config).CFlags()`.
This removes some duplication in target JSON files.
Unfortunately, this change also increases code size for Cortex-M
targets. It looks like LLVM now emits calls like __aeabi_memmove instead
of memmove, which pull in slightly more code (they basically just call
the regular C functions) and the calls themself don't seem to be as
efficient as they could be. Perhaps this is a LLVM bug that will be
fixed in the future, as this is a very common occurrence.
This brings some consistency to the CFlags and fixes the issue that on
some platforms (Linux, MacOS), no optimization level was set and
therefore C files in packages were not optimized at all.
Stripping debug information at link time also allows relocation
compression (aka linker relaxations). Keeping debug information at
compile time and optionally stripping it at link time has some
advantages:
* Automatic stack sizes on Cortex-M rely on the presence of debug
information.
* Some parts of the compiler now rely on the presence of debug
information for proper diagnostics.
* It works better with the cache: there is no distinction between
debug and no-debug builds.
* It makes it easier (or possible at all) to enable debug information
in the wasi-libc library without big downsides.
This can be very useful for some purposes:
* It makes it possible to disable the UART in cases where it is not
needed or needs to be disabled to conserve power.
* It makes it possible to disable the serial output to reduce code
size, which may be important for some chips. Sometimes, a few kB can
be saved this way.
* It makes it possible to override the default, for example you might
want to use an actual UART to debug the USB-CDC implementation.
It also lowers the dependency on having machine.Serial defined, which is
often not defined when targeting a chip. Eventually, we might want to
make it possible to write `-target=nrf52` or `-target=atmega328p` for
example to target the chip itself with no board specific assumptions.
The defaults don't change. I checked this by running `make smoketest`
before and after and comparing the results.
The wasm build tag together with GOARCH=arm was causing problems in the
internal/cpu package. In general, I think having two architecture build
tag will only cause problems (in this case, wasm and arm) so I've
removed the wasm build tag and replaced it with tinygo.wasm.
This is similar to the tinygo.riscv build tag, which is used for older
Go versions that don't yet have RISC-V support in the standard library
(and therefore pretend to be GOARCH=arm instead).
With this is possible to enable e.g., SIMD in WASM using -llvm-features
+simd128. Multiple features can be specified separated by comma,
e.g., -llvm-features +simd128,+tail-call
With help from @deadprogram and @aykevl.
This commit does two things:
1. It makes it possible to grow the heap on Linux and MacOS by
allocating 1GB of virtual memory on startup and then slowly using it
as necessary, when running out of available heap space.
2. It switches the default GC to be the conservative GC (previously
extalloc). This is good for consistency with other platforms that
all use this same GC.
This makes the extalloc GC unused by default.
This simplifies future changes. While the move itself is very simple, it
required some other changes to a few transforms that create new
functions to add the optsize attribute manually. It also required
abstracting away the optimization level flags (based on the -opt flag)
so that it can easily be retrieved from the config object.
This commit does not impact binary size on baremetal and WebAssembly.
I've seen a few tests on linux/amd64 grow slightly in size, but I'm not
too worried about those.
Moving settings to a separate config struct has two benefits:
- It decouples the compiler a bit from other packages, most
importantly the compileopts package. Decoupling is generally a good
thing.
- Perhaps more importantly, it precisely specifies which settings are
used while compiling and affect the resulting LLVM module. This will
be necessary for caching the LLVM module.
While it would have been possible to cache without this refactor, it
would have been very easy to miss a setting and thus let the
compiler work with invalid/stale data.
On WebAssembly it is possible to grow the heap with the memory.grow
instruction. This commit implements this feature and with that also
removes the -heap-size flag that was reportedly broken (I haven't
verified that). This should make it easier to use TinyGo for
WebAssembly, where there was no good reason to use a fixed heap size.
This commit has no effect on baremetal targets with optimizations
enabled.
The only architecture that actually needs special support for scanning
the stack is WebAssembly. All others allow raw access to the stack with
a small bit of assembly. Therefore, don't manually keep track of all
these objects on the stack manually and instead just use conservative
stack scanning.
This results in a massive code size decrease in the affected targets
(only tested linux/amd64 for code size) - sometimes around 33%. It also
allows for future improvements such as using proper stackful goroutines.
By modifying the linker script a bit and adding the NRO0 header directly
in the assembly, it's possible to craft an ELF file that can be
converted straight to a binary (using objcopy or similar) that is a NRO
file. This avoids custom code for NRO files or an extra build step.
With another change, .nro files are recognized by TinyGo so that this
will create a ready-to-run NRO file:
tinygo build -o test.nro -target=nintendoswitch examples/serial
This is a big change that will determine the stack size for many
goroutines automatically. Functions that aren't recursive and don't call
function pointers can in many cases have an automatically determined
worst case stack size. This is useful, as the stack size is usually much
lower than the previous hardcoded default of 1024 bytes: somewhere
around 200-500 bytes is common.
A side effect of this change is that the default stack sizes (including
the stack size for other architectures such as AVR) can now be changed
in the config JSON file, making it tunable per application.
Debug information is often useful and there is no reason to include it
for Go code but not for C code. Also, disabling debug information should
disable it entirely, not just for Go code.
Previously we used --sysroot to set the sysroot explicitly.
Unfortunately, this flag is not used directly by Clang to set the
include path (<sysroot>/include) but is instead interpreted by the
toolchain code. This means that even when the toolchain is explicitly
set (using the --sysroot parameter), it may still decide to use a
different include path such as <sysroot>/usr/include (such as on
baremetal aarch64).
This commit uses the Clang-internal -internal-isystem flag which sets
the include directory directly (as a system include path). This should
be more robust.
The reason the --sysroot parameter has so far worked is that all
existing targets happened to add <sysroot>/include as an include path.
The relevant Clang code is here:
https://github.com/llvm/llvm-project/blob/release/9.x/clang/lib/Driver/Driver.cpp#L4693-L4739
So far, RISC-V is handled by RISCVToolchain, Cortex-M targets by
BareMetal (which seems to be specific to ARM unlike what the name says)
and aarch64 fell back to Generic_ELF.
This is necessary for better CGo support on bare metal. Existing
libraries expect to be able to include parts of libc and expect to be
able to link to those symbols.
Because with this all targets have a working libc, it is now possible to
add tests to check that a libc in fact works basically.
Not all parts of picolibc are included, such as the math or stdio parts.
These should be added later, when needed.
This commit also avoids the need for the custom memcpy/memset/memcmp
symbols that are sometimes emitted by LLVM. The C library will take care
of that.
This allows packages other than the compiler to know (from a single
source of truth) which implemenation is used for Go func values.
This refactor is necessary to be able to move the Optimize function to
the transform package.
This code is required by transformation passes which are being moved
into a separate package, but is too complicated to simply copy.
Therefore, I decided to move them into a new package.
This flag is overloaded. It can be used in two ways:
* Choosing the flash method to use (openocd, msd, command).
* Choosing the OpenOCD programmer name.
For example, you can use one of these to use OpenOCD instead of the
mass-storage device programmer:
tinygo flash -target=microbit -programmer=openocd
tinygo flash -target=microbit -programmer=cmsis-dap
This is a large commit that moves all code directly related to
compiling/linking into a new builder package. This has a number of
advantages:
* It cleanly separates the API between the command line and the full
compilation (with a very small API surface).
* When the compiler finally compiles one package at a time (instead of
everything at once as it does now), something will have to invoke it
once per package. This builder package will be the natural place to
do that, and also be the place where the whole process can be
parallelized.
* It allows the TinyGo compiler to be used as a package. A client can
simply import the builder package and compile code using it.
As part of this refactor, the following additional things changed:
* Exported symbols have been made unexported when they weren't needed.
* The compilation target has been moved into the compileopts.Options
struct. This is done because the target really is just another
compiler option, and the API is simplified by moving it in there.
* The moveFile function has been duplicated. It does not really belong
in the builder API but is used both by the builder and the command
line. Moving it into a separate package didn't seem useful either
for what is essentially an utility function.
* Some doc strings have been improved.
Some future changes/refactors I'd like to make after this commit:
* Clean up the API between the builder and the compiler package.
* Perhaps move the test files (in testdata/) into the builder package.
* Perhaps move the loader package into the builder package.
Setting the linker script as one property (instead of as part of the
generic ldflags property) allows it to be overriden.
This is important for the SoftDevice on Nordic chips, because the
SoftDevice takes up a fixed part of the flash/RAM and the application
must be flashed at a different position. With this linkerscript option,
it is possible to create (for example) a pca10040-s132v6 that overrides
the default linker script.
Move most of the logic of determining which compiler configuration to
use (such as GOOS/GOARCH, build tags, whether to include debug symbols,
panic strategy, etc.) into the compileopts package. This makes it a
single source of truth for anything related to compiler configuration.
It has a few advantages:
* The compile configuration is independent of the compiler package.
This makes it possible to move optimization passes out of the
compiler, as they don't rely on compiler.Config anymore.
* There is only one place to look if an incorrect compile option is
used.
* The compileopts provides some resistance against unintentionally
picking the wrong option, such as with c.selectGC() vs c.GC() in the
compiler.
* It is now a lot easier to change compile options, as most options
are getters now.