Now that most of the utility compiler methods are ported over to the
builder or compilerContext, it is possible to avoid having to do the
wrapper creation in two steps. A new builder is created just to create
the wrapper.
This is a small reduction in line count (and a significant reduction in
complexity!), even though more documentation was added.
This is a fairly big commit, but it actually changes very little.
getValue should really be a property of the builder (or frame), where
the previously created instructions are kept.
This commit unfortunately introduces a significant amount of code
duplication. However, all that duplicate code should be removed once
this refactor is done.
This is the first commit in a series to refactor the compiler. The
intention is to make sure every function to be compiled eventually has
its own IR builder. This will make it much easier to do other
refactorings in the future:
* Most code won't depend (directly) on the central Compiler object,
perhaps making it possible to eliminate it in the future. Right now
it's embedded in the `builder` struct but individual fields from the
`Compiler` can easily be moved into the `builder` object.
* Some functions are not directly exposed in Go SSA, they are wrapper
functions for something. At the moment they are included in the list
of functions to be compiled with the reachability analysis
(SimpleDCE) in the ir package, but eventually this reachability
analys will be removed. At that point, it would be very convenient
to be able to simply build a function with a new IR builder.
The `compilerContext` struct makes sure that it is not possible for
`builder` methods to accidentally use global state such as the global IR
builder. It is a transitional mechanism and may be removed when
finished.
This marks the libc function abort as non-returning. This allows LLVM to optimize away code after panics. Also, this allows deadlocks to be properly propogated with the coroutines scheduler.
This commit lets the compiler know about interrupts and allows
optimizations to be performed based on that: interrupts are eliminated
when they appear to be unused in a program. This is done with a new
pseudo-call (runtime/interrupt.New) that is treated specially by the
compiler.
Add location information (whenever possible) to failed imports. This
helps in debugging where an incorrect import came from.
For example, show the following error message:
/home/ayke/src/github.com/tinygo-org/tinygo/src/machine/machine.go:5:8: cannot find package "foobar" in any of:
/usr/local/go/src/foobar (from $GOROOT)
/home/ayke/src/foobar (from $GOPATH)
Instead of the following:
error: cannot find package "foobar" in any of:
/usr/local/go/src/foobar (from $GOROOT)
/home/ayke/src/foobar (from $GOPATH)
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.
Instead of putting the magic in the AST, generate regular accessor
methods. This avoids a number of special cases in the compiler, and
avoids missing any of them.
The resulting union accesses are somewhat clunkier to use, but the
compiler implementation has far less coupling between the CGo
implementation and the IR generator.
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.
Previously, the cycle was broken by inserting an unsafe.Pointer type in
some places. This is of course incorrect, and makes debugging harder.
However, LLVM provides a way to make temporary nodes that are later
replaced, exactly for this purpose.
This commit uses those temporary metadata nodes to allow such recursive
types.
This commit allows starting a new goroutine directly from a func value,
not just when the static callee is known.
This is necessary to support the whole time package, not just the
commonly used subset that was compiled with the SimpleDCE pass enabled.
A bug was introduced in the previous commit that led to miscompilations
in the time.Sleep function when the scheduler was disabled, because
time.Sleep (implemented in the runtime) tried to switch to the scheduler
stack.
This commit restores the binary size of most examples to what it was
before, but still reduces static RAM consumption (.bss) slightly. This
gives me some confidence that it does indeed fix the introduced bug.
This scheduler is intended to live along the (stackless) coroutine based
scheduler which is needed for WebAssembly and unsupported platforms. The
stack based scheduler is somewhat simpler in implementation as it does
not require full program transform passes and supports things like
function pointers and interface methods out of the box with no changes.
Code size is reduced in most cases, even in the case where no scheduler
scheduler is used at all. I'm not exactly sure why but these changes
likely allowed some further optimizations somewhere. Even RAM is
slightly reduced, perhaps some global was elminated in the process as
well.
Previously it would use a bitcast, which cannot directly be used on AVR
because functions live in a different address space on AVR. To fix this,
use a ptrtoint/inttoptr pair.
This allows testdata/coroutines.go to be compiled, but due to what
appears to be an LLVM bug cannot be optimized and codegen'ed:
tinygo: /home/ayke/src/github.com/tinygo-org/tinygo/llvm-project/llvm/lib/IR/Constants.cpp:1776: static llvm::Constant *llvm::ConstantExpr::getBitCast(llvm::Constant *, llvm::Type *, bool): Assertion `CastInst::castIsValid(Instruction::BitCast, C, DstTy) && "Invalid constantexpr bitcast!"' failed.
This happens as one of the function passes after the TinyGo passes and
after the module has been verified so most likely it is a bug somewhere
in LLVM.