This commit fixes the following issue:
https://github.com/tinygo-org/tinygo/issues/309
Also, it prepares for some other reflect-related changes that should
make it easier to add support for named types (etc.) in the future.
In LLVM 8, the AVR backend has moved all function pointers to address
space 1 by default. Much of the code still assumes function pointers
live in address space 0, leading to assertion failures.
This commit fixes this problem by autodetecting function pointers and
avoiding them in interface pseudo-calls.
This commit changes many things:
* Most interface-related operations are moved into an optimization
pass for more modularity. IR construction creates pseudo-calls which
are lowered in this pass.
* Type codes are assigned in this interface lowering pass, after DCE.
* Type codes are sorted by usage: types more often used in type
asserts are assigned lower numbers to ease jump table construction
during machine code generation.
* Interface assertions are optimized: they are replaced by constant
false, comparison against a constant, or a typeswitch with only
concrete types in the general case.
* Interface calls are replaced with unreachable, direct calls, or a
concrete type switch with direct calls depending on the number of
implementing types. This hopefully makes some interface patterns
zero-cost.
These changes lead to a ~0.5K reduction in code size on Cortex-M for
testdata/interface.go. It appears that a major cause for this is the
replacement of function pointers with direct calls, which are far more
susceptible to optimization. Also, not having a fixed global array of
function pointers greatly helps dead code elimination.
This change also makes future optimizations easier, like optimizations
on interface value comparisons.
Because a few things were left unimplemented it only happened to kind-of
work before in my test cases.
This commit should complete interface-to-interface type asserts.
Comparing an interface to nil is easy, as the dynamic type is also nil.
Comparing the dynamic values (when the dynamic types match) is much
harder and depends on reflection capabilities, so is not yet implemented.
TODO: do better at it by tracking min/max values of integers. The
following straightforward code doesn't have its bounds checks removed:
for _, n := range slice {
println(n)
}
It took Android some time to even hit the 64K limit for regular method
calls, so switching to 16-bit IDs should be fine for method IDs of
interfaces. At least for the time being. When this limit is ever hit,
I'll think of another way, probably involving some platform-dependent
interface code (e.g. microcontrollers won't need 64K of methods) or
detecting the limit at build time.
https://android-developers.googleblog.com/2014/12/google-play-services-and-dex-method.html
Code size isn't changed, probably because the compiler optimizes away
all method calls.
This commit moves the itfmethod call implemented directly in LLVM IR to
a Go implementation in the runtime. Additionally, it fixes variable
names to be more obvious and adds a lot of documentation to explain how
interfaces actually work in TinyGo.
Code size changes for src/examples/hello:
nrf: -144
unix: -93
I'm guessing this code size reduction is a result of removing the
'noinline' function attribute.