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.
This interpreter currently complements the Go SSA level interpreter. It
may stay complementary or may be the only interpreter in the future.
This interpreter is experimental and not yet finished (there are known
bugs!) so it is disabled by default. It can be enabled by passing the
-initinterp flag.
The goal is to be able to run all initializations at compile time except
for the ones having side effects. This mostly works except perhaps for a
few edge cases.
In the future, this interpeter may be used to actually run regular Go
code, perhaps in a shell.
A single *ssa.BasicBlock may be split in multiple LLVM basic blocks due
to typeassert instructions. This means the incoming block and outgoing
block are different. PHI nodes need to get the result from the outgoing
block, which was fixed before, but incoming branches need to branch to
the incoming block, not the outgoing block.
Branching to the outgoing block led to a LLVM verification error when
compiling the fmt package.
Originally found in (*fmt.pp).handleMethods.
This is a common operation:
freevar := ...
defer func() {
println("I am deferred:", freevar)
}()
The function is thus an immediately applied closure. Only this form is
currently supported, support for regular (fat) function pointers should
be trivial to add but is not currently implemented as it wasn't
necessary to get fmt to compile.
When the underlying value of an interface does not fit in a pointer, a
pointer to the value was correctly inserted in the heap. However, the
receiving method still assumed it got the underlying value instead of a
pointer to it leading to a crash.
This commit inserts wrapper functions for method calls on interfaces.
The bug wasn't obvious as on a 64-bit system, the underlying value was
almost always put directly in the interface. However, it led to a crash
on the AVR platform where pointer are (usually) just 16 bits making it
far more likely that underlying values cannot be directly stored in an
interface.