Previously, a package initializer that could not be reverted correctly
would be called at runtime. But the initializer would be called in the
wrong order: after later packages are initialized.
This commit fixes this oversight and adds a test to verify the new
behavior.
The markExternal function is used when a global (function or global
variable) is somehow run at runtime. All the other globals it refers to
are from then on no longer known at compile time, so can't be used by
the interp package anymore.
This can also include inline assembly. While it is possible to modify
globals that way, it is only possible to modify exported globals:
similar to calling an undefined function (in C for example).
The interp package is in many cases able to execute map functions in the
runtime directly. This is probably slower than adding special support
for them in the interp package and also doesn't cover all cases (most
importantly, map keys that contain pointers) but removing this code also
removes a large amount of code that needs to be maintained and is
susceptible to hard-to-find bugs.
As a side effect, this resulted in different output of the
testdata/map.go test because the test relied on the existing iteration
order of TinyGo maps. I've updated the test to not rely on this test,
making the output compatible with what the Go toolchain would output.
I've discovered a bug in the implementation of the PHI instruction in
the interp package. This commit fixes the bug.
I've found this issue while investigating an issue with maps after
running interp per package.
A switch statement is not normally emitted by the compiler package, but
LLVM function passes may convert a series of if/else pairs to a switch
statement. A future change will run function passes in the package
compile phase, so the interp package (which is also run after all
modules are merged together) will need to deal with these new switch
statements.
Sometimes, LLVM may rename named structs when merging modules.
Therefore, we can't rely on typecodeID structs to retain their struct
names.
This commit changes the interface lowering pass to not rely on these
names. The interp package does however still rely on this name, but I
hope to fix that in the future.
Previously there was code to avoid impossible type asserts but it wasn't
great and in fact was too aggressive when combined with reflection.
This commit improves this by checking all types that exist in the
program that may appear in an interface (even struct fields and the
like) but without creating runtime.typecodeID objects with the type
assert. This has two advantages:
* As mentioned, it optimizes impossible type asserts away.
* It allows methods on types that were only asserted on (in
runtime.typeAssert) but never used in an interface to be optimized
away using GlobalDCE. This may have a cascading effect so that other
parts of the code can be further optimized.
This sometimes massively improves code size and mostly negates the code
size regression of the previous commit.
This distinction was useful before when reflect wasn't properly
supported. Back then it made sense to only include method sets that were
actually used in an interface. But now that it is possible to get to
other values (for example, by extracting fields from structs) and it is
possible to turn them back into interfaces, it is necessary to preserve
all method sets that can possibly be used in the program in a type
assert, interface assert or interface method call.
In the future, this logic will need to be revisited again when
reflect.New or reflect.Zero gets implemented.
Code size increases a bit in some cases, but usually in a very limited
way (except for one outlier in the drivers smoke tests). The next commit
will improve the situation significantly.
For a full explanation, see interp/README.md. In short, this rewrite is
a redesign of the partial evaluator which improves it over the previous
partial evaluator. The main functional difference is that when
interpreting a function, the interpretation can be rolled back when an
unsupported instruction is encountered (for example, an actual unknown
instruction or a branch on a value that's only known at runtime). This
also means that it is no longer necessary to scan functions to see
whether they can be interpreted: instead, this package now just tries to
interpret it and reverts when it can't go further.
This new design has several benefits:
* Most errors coming from the interp package are avoided, as it can
simply skip the code it can't handle. This has long been an issue.
* The memory model has been improved, which means some packages now
pass all tests that previously didn't pass them.
* Because of a better design, it is in fact a bit faster than the
previous version.
This means the following packages now pass tests with `tinygo test`:
* hash/adler32: previously it would hang in an infinite loop
* math/cmplx: previously it resulted in errors
This also means that the math/big package can be imported. It would
previously fail with a "interp: branch on a non-constant" error.
This commit teaches the interp scanner that supported interface
operations (type assertions, interface assertions) are supported.
This fixes a problem with math/rand in Go 1.14.
Non-constant type asserts are not yet implemented, but should be
relatively easy to add at a later time. They should result in a clear
error message for now.
Some mapassign operations cannot (yet) be done by the interp package.
Implement a fallback mechanism so that these operations can still be
performed at runtime.
This kind of code might be generated by the switch implementation of
func values. The func value is represented as a ptrtoint, and before
calling it, it is compared against 0.
This is really just a simple workaround. When such an instruction is
encountered, it will just fall back to marking the entire function as
having side effects. Ideally it should trace all affected instructions
and check if they would have any side effects, but this at least fixes a
number of compile errors.
This commit gets the following packages to compile:
* context
* database/sql/driver
* image/jpeg
* image/png
Declarations would enter an infinite loop when trying to loop over basic
blocks. That was probably an undefined operation, but still somehow
didn't crash the compiler.
Make sure that scanning declarations works as expected.
This implements the copy() built-in function. It may not work in all
cases, but should work in most cases.
This commit gets the following 3 packages to compile, according to
tinygo-site/imports/main.go:
* encoding/base32
* encoding/base64
* encoding/pem (was blocked by encoding/base64)