Compare commits

...

114 Commits

Author SHA1 Message Date
Nia 18c97e9b55 Revert "compiler: saturate float-to-int conversions"
This reverts commit a867b56e5f.
2021-01-16 14:27:35 -05:00
Nia Weiss a867b56e5f compiler: saturate float-to-int conversions
This works around some UB in LLVM, where an out-of-bounds conversion would produce a poison value.
The selected behavior is saturating, except that NaN is mapped to the minimum value.
2021-01-16 19:12:36 +01:00
Nia Weiss f159429152 compiler: emit a nil check when slicing an array pointer 2021-01-15 21:35:49 +01:00
Ayke van Laethem a848d720db compiler: refactor and add tests
This commit finally introduces unit tests for the compiler, to check
whether input Go code is converted to the expected output IR.

To make this necessary, a few refactors were needed. Hopefully these
refactors (to compile a program package by package instead of all at
once) will eventually become standard, so that packages can all be
compiled separate from each other and be cached between compiles.
2021-01-15 14:43:43 +01:00
Ayke van Laethem dbc438b2ae loader: use name "main" for the main package
This should make exported names a bit more consistent.

I believe there was a bug report for this issue, but I can't easily find
it. In any case, I think it's an important improvement to match the
behavior of the Go toolchain.
2021-01-15 14:43:43 +01:00
Fauchon c4d642007e New tinygo -x option to print commands (#1572)
main: add '-x' runtime flag that displays the executed external commands when performing operations like flashing.
2021-01-14 21:35:01 +01:00
Ayke van Laethem da0161d6ab wasm: implement a growable heap
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.
2021-01-10 21:08:52 +01:00
Ayke van Laethem 5af4c073cd runtime: put metadata at the top end of the heap
This commit swaps the layout of the heap. Previously, the metadata was
at the start and the data blocks (the actual heap memory) followed
after. This commit swaps those, so that the heap area starts with the
data blocks followed by the heap metadata.

This arrangement is not very relevant for baremetal targets that always
have all RAM allocated, but it is an important improvement for other
targets such as WebAssembly where growing the heap is possible but
starting with a small heap is a good idea. Because the metadata lives at
the end, and because the metadata does not contain pointers, it can
easily be moved. The data itself cannot be moved as the conservative GC
does not know all the pointer locations, plus moving the data could be
very expensive.
2021-01-10 21:08:52 +01:00
Ayke van Laethem 154c7c691b stm32: use stm32-rs SVDs which are of much higher quality
This commit changes the number of wait states for the stm32f103 chip to
2 instead of 4. This gets it back in line with the datasheet, but it
also has the side effect of breaking I2C. Therefore, another (seemingly
unrelated) change is needed: the i2cTimeout constant must be increased
to a higher value to adjust to the lower flash wait states - presumably
because the lower number of wait states allows the chip to run code
faster.
2021-01-09 21:45:07 +01:00
Fauchon 65caf777dd Support for STM32L0 MCUs and Dragino LGT92 device (#1561)
machine/stm32l0: add support for stm32l0 family and Dragino LGT92 Board
2021-01-08 22:27:25 +01:00
Ayke van Laethem a4d0877cf0 stacksize: add support for DW_CFA_offset_extended
It should be possible to ignore this directive, but we still have to
consume the two operands.
2021-01-06 19:41:47 +01:00
deadprogram 6ec868710b machine/microbit-v2: add initial support based on work done by @alankrantas thank you!
Signed-off-by: deadprogram <ron@hybridgroup.com>
2021-01-06 18:50:46 +01:00
deadprogram 086645153e tools/gen: ignore cluster registers with no actual clusters in them, and handle parsing binary integer fields in versions of Go before 1.13
Signed-off-by: deadprogram <ron@hybridgroup.com>
2021-01-06 18:50:46 +01:00
deadprogram cf930f652e nrfx: updare submodule to latest commit
Signed-off-by: deadprogram <ron@hybridgroup.com>
2021-01-06 18:50:46 +01:00
Quentin Smith 0dcff35d3a machine/p1am-100: rename MISO/MOSI
This makes p1am-100 work again after commit d1c4ed6.
2021-01-06 13:35:30 +01:00
Quentin Smith 75f13491b6 Add support for the P1AM-100 (similar to Arduino MKR) 2021-01-06 00:36:57 +01:00
Wu Han 533d2c9ce0 README: fix bluepill link
STM32Duino wiki url has been updated.
2021-01-05 23:59:03 +01:00
Jacques Supcik 2f4200a01b Add support for additional openocd commands (#1492)
main: add ability to define specific OpenOCD commands to be executed for a target.
2020-12-30 09:19:41 +01:00
deadprogram 939b393325 machine/clue: correct volume name and add alias for release version of Adafruit Clue board
Signed-off-by: deadprogram <ron@hybridgroup.com>
2020-12-29 15:17:38 +01:00
deadprogram a5ee1ba4b3 machine/nrf52840: ensure that USB CDC interface is only initialized once
Signed-off-by: deadprogram <ron@hybridgroup.com>
2020-12-29 12:20:33 +01:00
Ayke van Laethem fb0bb69f62 compiler: fix non-int integer constants
Before this change, the compiler could panic with the following message:

    panic: 20 not an Int

That of course doesn't make much sense. But it apparently is expected
behavior, see https://github.com/golang/go/issues/43165 for details.

This commit fixes this issue by converting the constant to an integer if
needed.
2020-12-27 16:13:36 +01:00
Ayke van Laethem 5917b8baa2 interp: fix alignment of untyped globals
During a run of interp, some memory (for example, memory allocated
through runtime.alloc) may not have a known LLVM type. This memory is
alllocated by creating an i8 array.
This does not necessarily work, as i8 has no alignment requirements
while the allocated object may have allocation requirements. Therefore,
the resulting global may have an alignment that is too loose.
This works on some microcontrollers but notably does not work on a
Cortex-M0 or Cortex-M0+, as all load/store operations must be aligned.

This commit fixes this by setting the alignment of untyped memory to the
maximum alignment. The determination of "maximum alignment" is not
great but should get the job done on most architectures.
2020-12-27 11:21:35 +01:00
Ayke van Laethem 30df912565 interp: rewrite entire package
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.
2020-12-22 15:54:23 +01:00
Ayke van Laethem e9d549d211 compiler: fix incorrect "exported function" panic
Because the parentHandle parameter wasn't always set to the right value,
the coroutine lowering pass would sometimes panic with "trying to make
exported function async" even though there was no exported function
involved. Therefore, it should unconditionally be set to avoid this.

The parent function doesn't always have the parentHandle function
parameter set because it can only be set after defining a function, not
when it is only declared.
2020-12-22 15:54:23 +01:00
Ayke van Laethem 6ad631539d compiler: fix undefined behavior in wordpack
Previously, EmitPointerPack would generate an out-of-bounds read from an
alloca. This commit fixes that by creating an alloca of the appropriate
size instead of using the size of the to-be-packed data (which might be
smaller than a pointer).

I discovered this error while working on a rewrite of the interp
package, which checks for out-of-bounds reads and writes. There I
discovered this issue when the image package was compiled.
2020-12-22 15:54:23 +01:00
Ayke van Laethem cda5fffd98 nrf: use SPIM peripheral instead of the legacy SPI peripheral
This newer peripheral supports DMA (through EasyDMA) and should
generally be faster. Importantly for some operations: interrupts (within
255 byte buffers) will not interfere with the SPI transfer.
2020-12-22 14:41:06 +01:00
Ayke van Laethem ce539ce583 nrf: refactor code a bit to reduce duplication
The nrf52 series is all very similar and copying the code only makes it
harder to maintain the code or to add more chips in the nrf52 series
(for example, the nrf52833 as used in the micro:bit v2).

This commit also has a small improvement regarding pins: it now includes
chip-level pin names (P0.00, P0.01, etc) to the machine package.
2020-12-22 14:41:06 +01:00
kenbell 43a31467d3 Nucleo f722ze (#1526)
machine/nucleo-f722ze: Add support for ST Micro NUCLEO-F722ZE
2020-12-15 06:51:35 +01:00
Ayke van Laethem ae92ea149c esp32: enable the FPU
This allows working with float32 values, for example it allows
testdata/float.go to work correctly (assuming an Xtensa backend bug is
fixed, see https://github.com/espressif/llvm-project/issues/41).
2020-12-11 12:11:46 +01:00
Ayke van Laethem 4568de556e esp32: use the compiler-rt library for extra routines
These routines are necessary to compile testdata/float.go.
2020-12-11 12:11:46 +01:00
ardnew 7a4ccd916f matrixportal-m4: Add support for board Adafruit Matrix Portal M4 (#1529)
machine/matrixportal-m4: add Adafruit Matrix Portal M4 board definition
2020-12-11 10:00:41 +01:00
Ayke van Laethem 4cc1cdf672 main: support gdb debugging with AVR
Be able to run `tinygo gdb -target=arduino examples/serial` and debug a
program with the power of a real debugger.

Note that this only works on LLVM 11 because older versions have a bug
in the AVR backend that cause it to produce invalid debug information:
https://reviews.llvm.org/D74213.
2020-12-10 16:44:27 +01:00
Ayke van Laethem bb27bbcb41 all: switch to LLVM 11 for static builds
This commit switches to LLVM 11 for builds with LLVM linked statically
(e.g. `make`). It does not yet switch the default for builds dynamically
linked to LLVM, that should be done in a later change.

This commit also changes to use the default host toolchain (probably
GCC) instead of Clang as the default compiler in CI. There were some
issues with Clang 3.8 in CI and hopefully this will fix it.

Additionally it updates the way LLVM is built on Windows, with
-DLLVM_ENABLE_PIC=OFF (which should have been used all along). This
change makes it possible to revert a hack to build libclang manually and
instead uses the libclang static library like on all other operating
systems, simplifying the Makefile.
2020-12-10 07:01:32 +01:00
deadprogram 9c2d2b662b build: remove release build job for arch release until it can be debugged
Signed-off-by: deadprogram <ron@hybridgroup.com>
2020-12-07 17:12:19 +01:00
Ayke van Laethem 098f900363 esp8266: implement task based scheduler
I have chosed to call this implementation `esp8266` instead of `xtensa`
as it has been written specifically for the ESP8266 and there are no
other Xtensa chips with the CALL0 ABI (no windowing) that I know of. The
only other related chip is the ESP32, which does implement register
windowing and thus needs a very different implementation.
2020-12-05 11:09:46 +01:00
Ayke van Laethem caf35cfc41 esp32: implement task based scheduler
This has been a *lot* of work, trying to understand the Xtensa windowed
registers ABI. But in the end I managed to come up with a very simple
implementation that so far seems to work very well.

I tested this with both blinky examples (with blinky2 slightly edited)
and ./testdata/coroutines.go to verify that it actually works.
Most development happened on the ESP32 QEMU fork from Espressif
(https://github.com/espressif/qemu/wiki) but I also verified that it
works on a real ESP32.
2020-12-05 09:02:11 +01:00
Ayke van Laethem abb09e869e runtime, internal/task: refactor to simplify stack switching
The Cortex-M target isn't much changed, but much of the logic for the
AVR stack switcher that was previously in assembly has now been moved to
Go to make it more maintainable and in fact smaller in code size. Three
functions (tinygo_getCurrentStackPointer, tinygo_switchToTask,
tinygo_switchToScheduler) have been changed to one: tinygo_swapTask.

This reduction in assembly code should make the code more maintainable
and should make it easier to port stack switching to other
architectures.

I've also moved the assembly files to src/internal/task, which seems
like a more appropriate location to me.
2020-12-05 09:02:11 +01:00
Ayke van Laethem bb58783158 ci: switch to Go 1.15 for MacOS builds
Unfortunately, CircleCI doesn't seem to provide Debian stretch builds
with Go 1.15. We should be using Debian stretch (an older distro) to
make sure the tinygo binary runs on as many Linux systems as possible
(including older ones), and I think using Go 1.14 for these builds is
unfortunate but the better tradeoff.
2020-12-03 08:42:04 +01:00
fleshin 7abc67107d sam: add support for the MKR1000 board 2020-12-03 00:33:23 +01:00
sago35 2540172cc5 atsam: add a length check to findPinPadMapping 2020-12-02 01:21:38 +01:00
sago35 0c4f0b1ebf version: update TinyGo version to 0.17.0-dev 2020-11-27 18:55:02 +01:00
Ayke van Laethem 15361c829e main: release 0.16.0 2020-11-17 11:15:15 +01:00
Ayke van Laethem 3f680b75f3 sam: remove redundant build tags
Some build tags were duplicated. This commit removes them.
2020-11-15 16:19:51 +01:00
Ayke van Laethem 9a7e633997 teensy36: add to smoketest
This required some changes to the UART code to get it to compile on Go
1.11.
2020-11-15 13:08:36 +01:00
ardnew 39b1f8b6f5 teensy40: UART: add missing godocs, rename Flush to Sync 2020-11-15 12:34:15 +01:00
ardnew 9aa50853b8 teensy40: add UART support 2020-11-15 12:34:15 +01:00
Ayke van Laethem 9ca0e3f2d1 Makefile: fix issue with Go 1.15.5
For details, see https://github.com/golang/go/issues/42606
2020-11-14 15:04:11 +01:00
ardnew 3cdc110462 teensy40: use implicit const defs (PinMode/PinChange) 2020-11-13 07:53:16 +01:00
ardnew 7cc687d416 teensy40: Add GPIO external interrupt support 2020-11-13 07:53:16 +01:00
Ron Evans ce57a034c3 ci: update CircleCI, Azure, and Docker builds to Go 1.15
Signed-off-by: deadprogram <ron@hybridgroup.com>
2020-11-13 03:07:35 +01:00
ardnew 30bee3afef add better fault identification for Cortex-M3/M33/M4/M7 hardfault handlers, add fault description registers to SCB_Type 2020-11-11 18:34:47 +01:00
ardnew b1d24a72c1 teensy40: fix typo in target JSON 2020-11-11 18:34:47 +01:00
ardnew 6e24c86320 teensy40: remove FPU spec in target JSON list of cflags 2020-11-11 18:34:47 +01:00
ardnew 19a0270303 teensy40: refactor to remove unnecessary code and constants 2020-11-11 18:34:47 +01:00
ardnew 47410a4b54 teensy40: init RTC and use ARM cycle counter for improved SysTick accuracy 2020-11-11 18:34:47 +01:00
ardnew 0d9c46b59e teensy40: fix PIT clock, which actually uses 24 MHz OSC
see: https://forum.pjrc.com/threads/63979-What-peripherals-are-affected-by-the-undocumented-24-MHz-OSC-circuit-on-Teensy-4-0
2020-11-11 18:34:47 +01:00
ardnew f93b28057a mimxrt1062: move device-specific files to "device/nxp" package 2020-11-11 18:34:47 +01:00
ardnew 691185f5f4 teensy40: initial implementation 2020-11-11 18:34:47 +01:00
Ayke van Laethem 163df7670a main: update go-llvm to fix LLVM build tags for Linux
This should make TinyGo buildable again on Darwin with a Homebrew
installed LLVM.
2020-11-09 18:45:37 +01:00
deadprogram 77c70d2758 machine/qtpy: add board definition for Adafruit QTPy
Signed-off-by: deadprogram <ron@hybridgroup.com>
2020-11-08 22:56:01 +01:00
Ayke van Laethem 7c4e83f5c0 machine: clarify caller's responsibility in SetInterrupt 2020-11-08 15:11:50 +01:00
jypelle db27541b1a Fix #1483 2020-11-08 09:32:13 +01:00
tom-horn 3bb994da9f Allow interrupts in stm32f103xx (#1466)
machine/stm32f103xx: allow interrupts in stm32f103xx
2020-11-07 12:21:38 +01:00
Elliott Sales de Andrade b3bd891ee0 Make lib64 clang include path check more robust.
On Fedora 33+, there is a buggy package that installs to
`/usr/lib64/clang/{version}/lib`, even on 32-bit systems. The original
code sees the `/usr/lib64/clang/{version}` directory, checks for an
`include` subdirectory, and then gives up because it doesn't exist.

To be more robust, check both `/usr/lib64/clang/{version}/include` and
`/usr/lib/clang/{version}/include`, and only allow versions that match
the LLVM major version used to build tinygo.
2020-11-04 00:04:33 +01:00
Lucas Teske 387bca8e32 nintendoswitch: Add env parser and removed unused stuff
*	Heap allocation based on available ram
*	Added homebrew launcher parser (for overriden heap)
*	Removed unused stuff (moved to gonx)
*	Kept require code at minimum to work in a real device
*	Moved everything to a single file
2020-11-03 23:28:55 +01:00
Nia Weiss d424b3d7ea add missing return pointer restore for regular coroutine tail calls
This fixes an issue where a normal suspending call followed by a plain tail call would result in the tail return value being written to the return pointer of the normal suspending call.
This is fixed by saving the return pointer at the start of the function and restoring it before initiating a plain tail call.
2020-11-01 08:32:55 +01:00
deadprogram c20328472b make: fixes error detecting llvm-nm tool for wasi-libc build
Signed-off-by: deadprogram <ron@hybridgroup.com>
2020-11-01 01:12:39 +01:00
Ayke van Laethem 171f793c1e avr: properly support the .rodata section
Unfortunately, the .rodata section can't be stored in flash. Instead, an
explicit .progmem section should be used, which is supported in LLVM as
address space 1 but not exposed to normal programs.

Eventually a pass should be written that converts trivial const globals
of which all loads are visible to be in addrspace 1, to get the benefits
of storing those globals directly in ROM.
2020-10-31 21:06:26 +01:00
Nia Weiss 3364da6f25 improve autodetection of LLVM tooling
Previously, our autodetection would fail on LLVM 11.
This now checks all common names for LLVM 10 and 11.
2020-10-31 18:17:38 +01:00
Ayke van Laethem e99b8a24fe runtime: allow ranging over a nil map
This appears to be allowed by the specification, at least it is allowed
by the main Go implementation: https://play.golang.org/p/S8jxAMytKDB

Allow it in TinyGo too, for consistency.

Found because it is triggered with `tinygo test flags`. This doesn't
make the flags package pass all tests, but is a step closer.
2020-10-29 21:53:41 +01:00
Ayke van Laethem 69e1aa4878 testing: add Run method
This patch adds subtests via the Run function. This gets two more
packages to pass tests: encoding/base32 and hash/fnv.
2020-10-28 18:25:56 +01:00
Ayke van Laethem 7a78b2dc0e all: replace underscores with dashes in target names
This is the convention, so use it everywhere.
2020-10-28 12:40:54 +01:00
Ayke van Laethem 2b1d4ce96e main: update go.sum 2020-10-28 08:48:11 +01:00
Ayke van Laethem 3e40b08ba0 compiler: implement negate for complex numbers 2020-10-28 07:38:51 +01:00
Takeshi Yoneda ffeff55706 wasm: use the fixed length buffer for putchar
Signed-off-by: mathetake <takeshi@tetrate.io>
2020-10-23 22:04:32 +02:00
Takeshi Yoneda 1dec9dcbc4 implement reflect.Swapper
Signed-off-by: mathetake <takeshi@tetrate.io>
2020-10-23 21:37:35 +02:00
Martin Tournoij ff833ef998 Add os.LookupEnv() stub
os.Getenv() was already stubbed out, but os.LookupEnv() wasn't. This
will allow me to compile my program unmodified without using separate
files and build tags.
2020-10-23 14:39:15 +02:00
Connor 6eeebfeb5c WIP: Esp8266 Get Function (#1438)
machine/esp8266: add Pin Get() support
2020-10-22 20:58:44 +02:00
Ayke van Laethem 6ab0106af3 nrf: fix nrf52832 flash size
I've accidentally specified just half of the available flash in the
linker script. This change fixes that.

There is in fact a 256kB version of the nrf52832, but it also has 32kB
of RAM so if you had used that it wouldn't actually work right now.
Also, extending the available flash should not affect existing programs
(as I haven't seen any run into size limitations yet).
2020-10-20 19:13:43 +02:00
蒼時弦也 e690ff0d8c Add instanceof support for WebAssembly 2020-10-18 22:15:47 +02:00
Ayke van Laethem 06564cbdb2 Switch default frequency to 4MHz
Let's use the same default frequency everywhere, for consistency.
It could be any frequency, but 4MHz is already used for other chips and
it seems like a reasonable frequency to me (not too fast for most chips
but still reasonably fast). Oh, and 4MHz is slow enough that it can be
inspected by a Saleae Logic 4 (that sadly has been discontinued).
2020-10-18 22:14:21 +02:00
Ayke van Laethem 47dc76fc34 nrf: give more flexibility in picking SPI speeds
Instead of only allowing a limited number of speeds, use the provided
speed as an upper bound on the allowed speed. The reasoning is that
picking a higher speed than requrested will likely result in malfunction
while picking a lower speed will usually only result in slower
operation.
This behavior matches the ESP32 at least.
2020-10-18 22:11:03 +02:00
deadprogram d382f3a259 esp8266: add target for d1mini board and add pin mappings for SPI/I2C to help out implementers
Signed-off-by: deadprogram <ron@hybridgroup.com>
2020-10-17 19:57:53 +02:00
Ayke van Laethem 47dc1087e8 ci: work around bug in Go 1.15.3
This works around a bug in Go 1.15.3 by pinning an older version.
See: https://github.com/golang/go/issues/42032
2020-10-17 12:23:03 +02:00
deadprogram c7d8223ab7 machine/esp32, targets/esp32: correct board definitions for actual boards not processor variants, also define all labeled pins
Signed-off-by: deadprogram <ron@hybridgroup.com>
2020-10-14 21:48:48 +02:00
ardnew 3eb33dff5d feather-stm32f405: add I2C support (#1378)
* machine/stm32f405: add initial I2C support
2020-10-14 18:00:24 +02:00
deadprogram bb146edb47 wasi: remove --no-threads flag as no longer in LLVM 11 linker
Signed-off-by: deadprogram <ron@hybridgroup.com>
2020-10-14 13:58:01 +02:00
Nia Weiss ed9b97cc0d runtime: add cheap atomic condition variable 2020-10-14 13:35:00 +02:00
Ayke van Laethem b40f250530 main: add initial support for (in-development) LLVM 11
This can be useful to test improvements in LLVM master and to make it
possible to support LLVM 11 for the most part already before the next
release. That also allows catching LLVM bugs early to fix them upstream.

Note that tests do not yet pass for this LLVM version, but the TinyGo
compiler can be built with the binaries from apt.llvm.org (at the time
of making this commit).
2020-10-13 20:23:50 +02:00
ardnew 184175378f gen-device-svd: ensure enum bitfields are unique 2020-10-10 12:52:11 +02:00
Ayke van Laethem d8dbe5748a testing: implement some benchmark stubs
This allows the following packages to pass tests:

  * crypto/des
  * encoding/hex

I have not included crypto/rc4 as it doesn't pass tests on Go 1.11 (but
it works on later versions).
2020-10-09 15:22:19 +02:00
Lucas Teske c2bfe6bc8d arm64: Add support for system calls (SVC) 2020-10-03 20:07:51 +02:00
Takeshi Yoneda 9a015f4f64 add wasm-abi field in TargetSpec && set generic for WASI by default (#1421)
Signed-off-by: mathetake <takeshi@tetrate.io>
2020-10-03 19:52:01 +02:00
ardnew 9ad2315079 feather-stm32f405: add SPI support (#1377)
* machine/stm32f405: add SPI support
2020-10-02 20:05:58 +02:00
Ayke van Laethem 431e51b8a0 runtime: use dedicated printfloat32
It can be unexpected that printing a float32 involves 64-bit floating
point routines, see for example:
https://github.com/tinygo-org/tinygo/issues/1415

This commit adds a dedicated printfloat32 instead just for printing
float32 values. It comes with a possible code size increase, but only if
both float32 and float64 values are printed. Therefore, this should be
an improvement in almost all cases.

I also tried using printfloat32 for everything (and casting a float64 to
float32 to print) but the printed values are slightly different,
breaking the testdata/math.go test for example.
2020-10-02 11:26:22 +02:00
Ayke van Laethem 67de8b490d gc: use raw stack access whenever possible
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.
2020-10-02 08:54:43 +02:00
Ayke van Laethem bfa29f17da runtime: move/refactor some GC-related code
Instead of putting tinygo_scanCurrentStack in scheduler_*.S files, put
them in dedicated files. The function tinygo_scanCurrentStack has
nothing to do with scheduling and so doesn't belong there. Additionally,
while scheduling code is made specific for the Cortex-M, the
tinygo_scanCurrentStack is generic to all ARM targets so this move
removes some duplication there.

Specifically:

  * tinygo_scanCurrentStack is moved out of scheduler_cortexm.S as it
    isn't really part of the scheduler. It is now gc_arm.S.
  * Same for the AVR target.
  * Same for the RISCV target.
  * scheduler_gba.S is removed, using gc_arm.S instead as it only
    contains tinygo_scanCurrentStack.
2020-10-02 08:54:43 +02:00
Ayke van Laethem 7123941df0 main: add support for debugging qemu-user targets
This commit allows debugging like the following:

    GOARCH=arm tinygo gdb ./testdata/alias.go

This can be very useful to debug issues on a different instruction set
architecture but still on a host system.

I tested the following 7 configurations to make sure it works and I
didn't break anything:

    GOOS=amd64
    GOOS=386
    GOOS=arm
    GOOS=arm64
    tinygo gdb -target=hifive1-qemu
    tinygo gdb -target=cortex-m-qemu
    tinygo gdb -target=microbit
2020-10-02 08:54:43 +02:00
Ayke van Laethem 05d2f2c412 main: improve support for x86-32 and add tests
To avoid breaking this, make sure we actually test x86-32 (aka i386 aka
GOARCH=386) support in CI.

Also remove the now-unnecessary binutils-arm-none-eabi package to speed
up CI a bit.
2020-10-02 08:54:43 +02:00
Ayke van Laethem 9a12d129ab testing: implement dummy Helper method
This lets a few more packages pass tests: container/heap and
encoding/ascii85.
2020-10-01 02:14:59 +02:00
Ayke van Laethem 0ecfe9eade ci: fix make wasmtest
This fixes issue https://github.com/tinygo-org/tinygo/issues/1418. In
short, it appears there was a race condition that was only visible on
GOARCH=386 but not on GOARCH=amd64. Updating to a more recent chromedp
version fixes the issue.
2020-10-01 02:07:00 +02:00
Takeshi Yoneda f50ad3585d support WASI target (#1373)
* initial commit for WASI support

* merge "time" package with wasi build tag
* override syscall package with wasi build tag
* create runtime_wasm_{js,wasi}.go files
* create syscall_wasi.go file
* create time/zoneinfo_wasi.go file as the replacement of zoneinfo_js.go
* add targets/wasi.json target

* set visbility hidden for runtime extern variables

Accodring to the WASI docs (https://github.com/WebAssembly/WASI/blob/master/design/application-abi.md#current-unstable-abi),
none of exports of WASI executable(Command) should no be accessed.

v0.19.0 of bytecodealliance/wasmetime, which is often refered to as the reference implementation of WASI,
does not accept any exports except functions and the only limited variables like "table", "memory".

* merge syscall_{baremetal,wasi}.go

* fix js target build

* mv wasi functions to syscall/wasi && implement sleepTicks

* WASI: set visibility hidden for globals variables

* mv back syscall/wasi/* to runtime package

* WASI: add test

* unexport wasi types

* WASI test: fix wasmtime path

* stop changing visibility of runtime.alloc

* use GOOS=linux, GOARCH=arm for wasi target

Signed-off-by: mathetake <takeshi@tetrate.io>

* WASI: fix build tags for os/runtime packages

Signed-off-by: mathetake <takeshi@tetrate.io>

* run WASI test only on Linux

Signed-off-by: mathetake <takeshi@tetrate.io>

* set InternalLinkage instead of changing visibility

Signed-off-by: mathetake <takeshi@tetrate.io>
2020-09-29 21:58:03 +02:00
Ayke van Laethem d39c7abb4d nrf: fix double stop signal in I2C 2020-09-27 15:21:54 +02:00
Daniel M. Lambea 9e61e6fe4d nrf: add I2C error checking (#1392)
* machine/nrf: add I2C error checking
2020-09-26 09:20:43 +02:00
Lucas Teske d1f90ef59c nintendoswitch: fix crash when printing long lines (> 120) 2020-09-26 02:52:10 +02:00
Ayke van Laethem 54602fe0c3 main: add support for -c and -o flags to tinygo test 2020-09-25 16:18:10 +02:00
Ayke van Laethem c10dcd429c main: refactor -o flag 2020-09-25 16:18:10 +02:00
Ayke van Laethem b713001313 test: support non-host tests
For example, for running tests with -target=wasm or
-target=cortex-m-qemu. It looks at the output to determine whether tests
were successful in the absence of a status code.
2020-09-24 21:17:26 +02:00
Ayke van Laethem 1096596b69 compiler: fix floating point bugs
There were a few bugs related to floating point. After fixing these, the
math package started passing all tests.
2020-09-21 10:43:46 +02:00
Ayke van Laethem 7b601b3e3c loader: fix linkname in test binaries
This is an issue in particular in the math package, of which most
functions are defined in the runtime package.
2020-09-21 10:43:46 +02:00
Ayke van Laethem ec54e7763d runtime: fix UTF-8 decoding
The algorithm now checks for invalid UTF-8 sequences, which is required
by the Go spec.

This gets the tests of the unicode/utf8 package to pass.

Also add bytes.Equal for Go 1.11, which again is necessary for the
unicode/utf8 package.
2020-09-21 08:49:13 +02:00
Elliott Sales de Andrade 41afb77080 builder: also check lib64 for clang include path.
On 64-bit Fedora, `lib64` is where the clang headers are, not `lib`. For
multiarch systems, both will exist, but it's likely you want 64-bit, so
check that first.
2020-09-20 14:00:02 +02:00
Jacques Supcik 13fe668929 compileopts: simplify copyProperties using reflection 2020-09-20 13:49:16 +02:00
sago35 2a72262c33 version: update TinyGo version to 0.16.0-dev 2020-09-18 01:32:31 +02:00
269 changed files with 14262 additions and 4049 deletions
+31 -95
View File
@@ -28,6 +28,7 @@ commands:
qemu-user \
gcc-avr \
avr-libc
sudo apt-get install --no-install-recommends libc6-dev-i386 lib32gcc-8-dev
install-node:
steps:
- run:
@@ -44,6 +45,13 @@ commands:
command: |
wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
sudo apt install ./google-chrome-stable_current_amd64.deb
install-wasmtime:
steps:
- run:
name: "Install wasmtime"
command: |
curl https://wasmtime.dev/install.sh -sSf | bash
sudo ln -s ~/.wasmtime/bin/wasmtime /usr/local/bin/wasmtime
install-xtensa-toolchain:
parameters:
variant:
@@ -60,12 +68,12 @@ commands:
steps:
- restore_cache:
keys:
- llvm-source-10-v1
- llvm-source-11-v1
- run:
name: "Fetch LLVM source"
command: make llvm-source
- save_cache:
key: llvm-source-10-v1
key: llvm-source-11-v1
paths:
- llvm-project
build-wasi-libc:
@@ -91,6 +99,7 @@ commands:
llvm: "<<parameters.llvm>>"
- install-node
- install-chrome
- install-wasmtime
- restore_cache:
keys:
- go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
@@ -105,7 +114,7 @@ commands:
key: wasi-libc-sysroot-systemclang-v2
paths:
- lib/wasi-libc/sysroot
- run: go test -v -tags=llvm<<parameters.llvm>> ./cgo ./compileopts ./interp ./transform .
- run: go test -v -tags=llvm<<parameters.llvm>> ./cgo ./compileopts ./compiler ./interp ./transform .
- run: make gen-device -j4
- run: make smoketest XTENSA=0
- run: make tinygo-test
@@ -125,7 +134,6 @@ commands:
command: |
sudo apt-get install \
gcc-arm-linux-gnueabihf \
binutils-arm-none-eabi \
libc6-dev-armel-cross \
gcc-aarch64-linux-gnu \
libc6-dev-arm64-cross \
@@ -133,7 +141,9 @@ commands:
qemu-user \
gcc-avr \
avr-libc
sudo apt-get install --no-install-recommends libc6-dev-i386 lib32gcc-6-dev
- install-node
- install-wasmtime
- install-xtensa-toolchain:
variant: "linux-amd64"
- restore_cache:
@@ -143,17 +153,14 @@ commands:
- llvm-source-linux
- restore_cache:
keys:
- llvm-build-10-linux-v1-assert
- llvm-build-11-linux-v1-assert
- run:
name: "Build LLVM"
command: |
if [ ! -f llvm-build/lib/liblldELF.a ]
then
# install dependencies
sudo apt-get install cmake clang ninja-build
# make build faster
export CC=clang
export CXX=clang++
sudo apt-get install cmake ninja-build
# hack ninja to use less jobs
echo -e '#!/bin/sh\n/usr/bin/ninja -j3 "$@"' > /go/bin/ninja
chmod +x /go/bin/ninja
@@ -161,7 +168,7 @@ commands:
make ASSERT=1 llvm-build
fi
- save_cache:
key: llvm-build-10-linux-v1-assert
key: llvm-build-11-linux-v1-assert
paths:
llvm-build
- run: make ASSERT=1
@@ -185,7 +192,6 @@ commands:
command: |
sudo apt-get install \
gcc-arm-linux-gnueabihf \
binutils-arm-none-eabi \
libc6-dev-armel-cross \
gcc-aarch64-linux-gnu \
libc6-dev-arm64-cross \
@@ -193,7 +199,9 @@ commands:
qemu-user \
gcc-avr \
avr-libc
sudo apt-get install --no-install-recommends libc6-dev-i386 lib32gcc-6-dev
- install-node
- install-wasmtime
- install-xtensa-toolchain:
variant: "linux-amd64"
- restore_cache:
@@ -203,17 +211,14 @@ commands:
- llvm-source-linux
- restore_cache:
keys:
- llvm-build-10-linux-v1
- llvm-build-11-linux-v1-noassert
- run:
name: "Build LLVM"
command: |
if [ ! -f llvm-build/lib/liblldELF.a ]
then
# install dependencies
sudo apt-get install cmake clang ninja-build
# make build faster
export CC=clang
export CXX=clang++
sudo apt-get install cmake ninja-build
# hack ninja to use less jobs
echo -e '#!/bin/sh\n/usr/bin/ninja -j3 "$@"' > /go/bin/ninja
chmod +x /go/bin/ninja
@@ -221,7 +226,7 @@ commands:
make llvm-build
fi
- save_cache:
key: llvm-build-10-linux-v1
key: llvm-build-11-linux-v1-noassert
paths:
llvm-build
- build-wasi-libc
@@ -263,8 +268,8 @@ commands:
- run:
name: "Install dependencies"
command: |
curl https://dl.google.com/go/go1.14.darwin-amd64.tar.gz -o go1.14.darwin-amd64.tar.gz
sudo tar -C /usr/local -xzf go1.14.darwin-amd64.tar.gz
curl https://dl.google.com/go/go1.15.5.darwin-amd64.tar.gz -o go1.15.5.darwin-amd64.tar.gz
sudo tar -C /usr/local -xzf go1.15.5.darwin-amd64.tar.gz
ln -s /usr/local/go/bin/go /usr/local/bin/go
HOMEBREW_NO_AUTO_UPDATE=1 brew install qemu
- install-xtensa-toolchain:
@@ -275,17 +280,17 @@ commands:
- go-cache-macos-v2-{{ checksum "go.mod" }}
- restore_cache:
keys:
- llvm-source-10-macos-v1
- llvm-source-11-macos-v1
- run:
name: "Fetch LLVM source"
command: make llvm-source
- save_cache:
key: llvm-source-10-macos-v1
key: llvm-source-11-macos-v1
paths:
- llvm-project
- restore_cache:
keys:
- llvm-build-10-macos-v1
- llvm-build-11-macos-v1
- run:
name: "Build LLVM"
command: |
@@ -297,7 +302,7 @@ commands:
make llvm-build
fi
- save_cache:
key: llvm-build-10-macos-v1
key: llvm-build-11-macos-v1
paths:
llvm-build
- restore_cache:
@@ -333,60 +338,6 @@ commands:
paths:
- ~/.cache/go-build
- /go/pkg/mod
arch-release:
steps:
- run:
name: Install dependencies
command: pacman -Sy --noconfirm openssh git pacman-contrib binutils
- run:
name: Create TinyGo user
command: useradd -m tinygo
- run:
name: Start SSH Agent
user: tinygo
command: eval $(ssh-agent -s)
- run:
name: Add ARCH_RELEASE_SSH_PRIVATE_KEY identity
user: tinygo
command: echo "${ARCH_RELEASE_SSH_PRIVATE_KEY}" | tr -d '\r' | ssh-add -
- run:
name: Create SSH directory
user: tinygo
command: mkdir -p ~/.ssh && chmod 700 ~/.ssh
- run:
name: Add aur.archlinux.org to known hosts
user: tinygo
command: ssh-keyscan aur.archlinux.org >> ~/.ssh/known_hosts
- run:
name: Clone tinygo-bin repo
user: tinygo
command: git clone ssh://aur@aur.archlinux.org/tinygo-bin.git ~/tinygo-bin
- run:
name: Update package version
user: tinygo
command: sed -i -E "s/(pkgver=)(.*)$/\1${CIRCLE_TAG}/" ~/tinygo-bin/PKGBUILD
- run:
name: Update file checksums
user: tinygo
command: cd ~/tinygo-bin && updpkgsums
- run:
name: Update .SRCINFO
user: tinygo
command: cd ~/tinygo-bin && makepkg --printsrcinfo > .SRCINFO
# Commit the update
- run:
name: Set git commit config
user: tinygo
command: |
git config --global user.email "tinygo-bot@tinygo.org" &&
git config --global user.name "TinyGo Release Bot"
- run:
name: Commit and push changes
user: tinygo
command: |
cd ~/tinygo-bin &&
git commit -a -m "Update tinygo-bin to v${CIRCLE_TAG}" &&
git push origin master
jobs:
test-llvm9-go111:
@@ -413,12 +364,12 @@ jobs:
steps:
- test-linux:
llvm: "10"
test-llvm10-go115:
test-llvm11-go115:
docker:
- image: circleci/golang:1.15-buster
steps:
- test-linux:
llvm: "10"
llvm: "11"
assert-test-linux:
docker:
- image: circleci/golang:1.14-stretch
@@ -434,12 +385,6 @@ jobs:
xcode: "10.1.0"
steps:
- build-macos
arch-release:
docker:
- image: archlinux:latest
steps:
- arch-release
@@ -450,16 +395,7 @@ workflows:
- test-llvm10-go112
- test-llvm10-go113
- test-llvm10-go114
- test-llvm10-go115
- test-llvm11-go115
- build-linux
- build-macos
- assert-test-linux
release:
jobs:
- arch-release:
filters:
branches:
ignore: /.*/
tags:
# Runs on every semver release
only: /v[0-9]+(\.[0-9]+)*(-.*)*/
+3
View File
@@ -20,3 +20,6 @@
[submodule "lib/picolibc"]
path = lib/picolibc
url = https://github.com/keith-packard/picolibc.git
[submodule "lib/stm32-svd"]
path = lib/stm32-svd
url = https://github.com/tinygo-org/stm32-svd
+54
View File
@@ -1,3 +1,57 @@
0.16.0
---
* **command-line**
- add initial support for LLVM 11
- make lib64 clang include path check more robust
- `build`: improve support for GOARCH=386 and add tests
- `gdb`: add support for qemu-user targets
- `test`: support non-host tests
- `test`: add support for -c and -o flags
- `test`: implement some benchmark stubs
* **compiler**
- `builder`: improve detection of clang on Fedora
- `compiler`: fix floating point comparison bugs
- `compiler`: implement negate for complex numbers
- `loader`: fix linkname in test binaries
- `transform`: add missing return pointer restore for regular coroutine tail
calls
* **standard library**
- `machine`: switch default frequency to 4MHz
- `machine`: clarify caller's responsibility in `SetInterrupt`
- `os`: add `LookupEnv()` stub
- `reflect`: implement `Swapper`
- `runtime`: fix UTF-8 decoding
- `runtime`: gc: use raw stack access whenever possible
- `runtime`: use dedicated printfloat32
- `runtime`: allow ranging over a nil map
- `runtime`: avoid device/nxp dependency in HardFault handler
- `testing`: implement dummy Helper method
- `testing`: add Run method
* **targets**
- `arm64`: add support for SVCall intrinsic
- `atsamd51`: avoid panic when configuring SPI with SDI=NoPin
- `avr`: properly support the `.rodata` section
- `esp8266`: implement `Pin.Get` function
- `nintendoswitch`: fix crash when printing long lines (> 120)
- `nintendoswitch`: add env parser and removed unused stuff
- `nrf`: add I2C error checking
- `nrf`: give more flexibility in picking SPI speeds
- `nrf`: fix nrf52832 flash size
- `stm32f103`: support wakeups from interrupts
- `stm32f405`: add SPI support
- `stm32f405`: add I2C support
- `wasi`: add support for this target
- `wasi`: use 'generic' ABI by default
- `wasi`: remove --no-threads flag from wasm-ld
- `wasm`: add instanceof support for WebAssembly
- `wasm`: use fixed length buffer for putchar
* **boards**
- `d1mini`: add this ESP8266 based board
- `esp32`: use board definitions instead of chip names
- `qtpy`: add board definition for Adafruit QTPy
- `teensy40`: add this board
0.15.0
---
+2 -2
View File
@@ -1,5 +1,5 @@
# TinyGo base stage installs Go 1.14, LLVM 10 and the TinyGo compiler itself.
FROM golang:1.14 AS tinygo-base
# TinyGo base stage installs the most recent Go 1.15.x, LLVM 10 and the TinyGo compiler itself.
FROM golang:1.15 AS tinygo-base
RUN wget -O- https://apt.llvm.org/llvm-snapshot.gpg.key| apt-key add - && \
echo "deb http://apt.llvm.org/buster/ llvm-toolchain-buster-10 main" >> /etc/apt/sources.list && \
+42 -42
View File
@@ -9,25 +9,10 @@ CLANG_SRC ?= $(LLVM_PROJECTDIR)/clang
LLD_SRC ?= $(LLVM_PROJECTDIR)/lld
# Try to autodetect LLVM build tools.
ifneq (, $(shell command -v llvm-build/bin/clang 2> /dev/null))
CLANG ?= $(abspath llvm-build/bin/clang)
else
CLANG ?= clang-10
endif
ifneq (, $(shell command -v llvm-build/bin/llvm-ar 2> /dev/null))
LLVM_AR ?= $(abspath llvm-build/bin/llvm-ar)
else ifneq (, $(shell command -v llvm-ar-10 2> /dev/null))
LLVM_AR ?= llvm-ar-10
else
LLVM_AR ?= llvm-ar
endif
ifneq (, $(shell command -v llvm-build/bin/llvm-nm 2> /dev/null))
LLVM_NM ?= $(abspath llvm-build/bin/llvm-nm)
else ifneq (, $(shell command -v llvm-nm-10 2> /dev/null))
LLVM_NM ?= llvm-nm-10
else
LLVM_NM ?= llvm-nm
endif
detect = $(shell command -v $(1) 2> /dev/null && echo $(1))
CLANG ?= $(word 1,$(abspath $(call detect,llvm-build/bin/clang))$(call detect,clang-11)$(call detect,clang-10)$(call detect,clang))
LLVM_AR ?= $(word 1,$(abspath $(call detect,llvm-build/bin/llvm-ar))$(call detect,llvm-ar-11)$(call detect,llvm-ar-10)$(call detect,llvm-ar))
LLVM_NM ?= $(word 1,$(abspath $(call detect,llvm-build/bin/llvm-nm))$(call detect,llvm-nm-11)$(call detect,llvm-nm-10)$(call detect,llvm-nm))
# Go binary and GOROOT to select
GO ?= go
@@ -37,7 +22,7 @@ export GOROOT = $(shell $(GO) env GOROOT)
MD5SUM = md5sum
# tinygo binary for tests
TINYGO ?= tinygo
TINYGO ?= $(word 1,$(call detect,tinygo)$(call detect,build/tinygo))
# Use CCACHE for LLVM if possible
ifneq (, $(shell command -v ccache 2> /dev/null))
@@ -63,23 +48,13 @@ ifeq ($(OS),Windows_NT)
# LLVM compiled using MinGW on Windows appears to have problems with threads.
# Without this flag, linking results in errors like these:
# libLLVMSupport.a(Threading.cpp.obj):Threading.cpp:(.text+0x55): undefined reference to `std::thread::hardware_concurrency()'
LLVM_OPTION += -DLLVM_ENABLE_THREADS=OFF
LLVM_OPTION += -DLLVM_ENABLE_THREADS=OFF -DLLVM_ENABLE_PIC=OFF
CGO_CPPFLAGS += -DCINDEX_NO_EXPORTS
CGO_LDFLAGS += -static -static-libgcc -static-libstdc++
CGO_LDFLAGS_EXTRA += -lversion
# Build libclang manually because the CMake-based build system on Windows
# doesn't allow building libclang as a static library.
LIBCLANG_PATH = $(abspath build/libclang-custom.a)
LIBCLANG_FILES = $(abspath $(wildcard $(LLVM_BUILDDIR)/tools/clang/tools/libclang/CMakeFiles/libclang.dir/*.cpp.obj))
# Add the libclang dependency to the tinygo binary target.
tinygo: $(LIBCLANG_PATH)
test: $(LIBCLANG_PATH)
# Build libclang.
$(LIBCLANG_PATH): $(LIBCLANG_FILES)
@mkdir -p build
ar rcs $(LIBCLANG_PATH) $^
LIBCLANG_PATH = $(abspath $(LLVM_BUILDDIR))/lib/liblibclang.a
else ifeq ($(shell uname -s),Darwin)
MD5SUM = md5
@@ -102,9 +77,9 @@ LLD_LIBS = $(START_GROUP) -llldCOFF -llldCommon -llldCore -llldDriver -llldELF -
# For static linking.
ifneq ("$(wildcard $(LLVM_BUILDDIR)/bin/llvm-config*)","")
CGO_CPPFLAGS=$(shell $(LLVM_BUILDDIR)/bin/llvm-config --cppflags) -I$(abspath $(LLVM_BUILDDIR))/tools/clang/include -I$(abspath $(CLANG_SRC))/include -I$(abspath $(LLD_SRC))/include
CGO_CPPFLAGS+=$(shell $(LLVM_BUILDDIR)/bin/llvm-config --cppflags) -I$(abspath $(LLVM_BUILDDIR))/tools/clang/include -I$(abspath $(CLANG_SRC))/include -I$(abspath $(LLD_SRC))/include
CGO_CXXFLAGS=-std=c++14
CGO_LDFLAGS+=$(LIBCLANG_PATH) -std=c++14 -L$(abspath $(LLVM_BUILDDIR)/lib) $(CLANG_LIBS) $(LLD_LIBS) $(shell $(LLVM_BUILDDIR)/bin/llvm-config --ldflags --libs --system-libs $(LLVM_COMPONENTS)) -lstdc++ $(CGO_LDFLAGS_EXTRA)
CGO_LDFLAGS+=$(LIBCLANG_PATH) -L$(abspath $(LLVM_BUILDDIR)/lib) $(CLANG_LIBS) $(LLD_LIBS) $(shell $(LLVM_BUILDDIR)/bin/llvm-config --ldflags --libs --system-libs $(LLVM_COMPONENTS)) -lstdc++ $(CGO_LDFLAGS_EXTRA)
endif
@@ -155,13 +130,13 @@ gen-device-kendryte: build/gen-device-svd
GO111MODULE=off $(GO) fmt ./src/device/kendryte
gen-device-stm32: build/gen-device-svd
./build/gen-device-svd -source=https://github.com/posborne/cmsis-svd/tree/master/data/STMicro lib/cmsis-svd/data/STMicro/ src/device/stm32/
./build/gen-device-svd -source=https://github.com/tinygo-org/stm32-svd lib/stm32-svd/svd src/device/stm32/
GO111MODULE=off $(GO) fmt ./src/device/stm32
# Get LLVM sources.
$(LLVM_PROJECTDIR)/README.md:
git clone -b xtensa_release_10.0.1 --depth=1 https://github.com/tinygo-org/llvm-project $(LLVM_PROJECTDIR)
git clone -b xtensa_release_11.0.0 --depth=1 https://github.com/tinygo-org/llvm-project $(LLVM_PROJECTDIR)
llvm-source: $(LLVM_PROJECTDIR)/README.md
# Configure LLVM.
@@ -188,16 +163,27 @@ tinygo:
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) build -buildmode exe -o build/tinygo$(EXE) -tags byollvm -ldflags="-X main.gitSha1=`git rev-parse --short HEAD`" .
test: wasi-libc
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test -v -buildmode exe -tags byollvm ./cgo ./compileopts ./interp ./transform .
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test -v -buildmode exe -tags byollvm ./cgo ./compileopts ./compiler ./interp ./transform .
# Test known-working standard library packages.
# TODO: do this in one command, parallelize, and only show failing tests (no
# implied -v flag).
.PHONY: tinygo-test
tinygo-test:
$(TINYGO) test container/heap
$(TINYGO) test container/list
$(TINYGO) test container/ring
$(TINYGO) test crypto/des
$(TINYGO) test encoding/ascii85
$(TINYGO) test encoding/base32
$(TINYGO) test encoding/hex
$(TINYGO) test hash/adler32
$(TINYGO) test hash/fnv
$(TINYGO) test hash/crc64
$(TINYGO) test math
$(TINYGO) test math/cmplx
$(TINYGO) test text/scanner
$(TINYGO) test unicode/utf8
.PHONY: smoketest
smoketest:
@@ -223,6 +209,8 @@ smoketest:
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=microbit examples/microbit-blink
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=microbit-v2 examples/microbit-blink
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/pininterrupt
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/serial
@@ -234,8 +222,6 @@ smoketest:
# test simulated boards on play.tinygo.org
$(TINYGO) build -o test.wasm -tags=arduino examples/blinky1
@$(MD5SUM) test.wasm
$(TINYGO) build -o test.wasm -tags=hifive1-qemu examples/serial
@$(MD5SUM) test.wasm
$(TINYGO) build -o test.wasm -tags=hifive1b examples/blinky1
@$(MD5SUM) test.wasm
$(TINYGO) build -o test.wasm -tags=reelboard examples/blinky1
@@ -287,7 +273,7 @@ smoketest:
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=circuitplay-express examples/i2s
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=clue_alpha examples/blinky1
$(TINYGO) build -size short -o test.hex -target=clue-alpha examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.gba -target=gameboy-advance examples/gba-display
@$(MD5SUM) test.gba
@@ -311,6 +297,8 @@ smoketest:
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=pinetime-devkit0 examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=lgt92 examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=x9pro examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=pca10056-s140v7 examples/blinky1
@@ -331,6 +319,16 @@ smoketest:
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=itsybitsy-nrf52840 examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=qtpy examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=teensy40 examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=teensy36 examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=nucleo-f722ze examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=p1am-100 examples/blinky1
@$(MD5SUM) test.hex
# test pwm
$(TINYGO) build -size short -o test.hex -target=itsybitsy-m0 examples/pwm
@$(MD5SUM) test.hex
@@ -357,13 +355,15 @@ ifneq ($(AVR), 0)
@$(MD5SUM) test.hex
endif
ifneq ($(XTENSA), 0)
$(TINYGO) build -size short -o test.bin -target=esp32-wroom-32 examples/blinky1
$(TINYGO) build -size short -o test.bin -target=esp32-mini32 examples/blinky1
@$(MD5SUM) test.bin
$(TINYGO) build -size short -o test.bin -target=nodemcu examples/blinky1
@$(MD5SUM) test.bin
endif
$(TINYGO) build -size short -o test.hex -target=hifive1b examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=hifive1-qemu examples/serial
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=maixbit examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -o wasm.wasm -target=wasm examples/wasm/export
+2 -2
View File
@@ -86,7 +86,7 @@ The following 44 microcontroller boards are currently supported:
* [Seeed Sipeed MAix BiT](https://www.seeedstudio.com/Sipeed-MAix-BiT-for-RISC-V-AI-IoT-p-2872.html)
* [SiFIve HiFive1](https://www.sifive.com/boards/hifive1)
* [ST Micro "Nucleo F103RB"](https://www.st.com/en/evaluation-tools/nucleo-f103rb.html)
* [ST Micro STM32F103XX "Bluepill"](http://wiki.stm32duino.com/index.php?title=Blue_Pill)
* [ST Micro STM32F103XX "Bluepill"](https://stm32-base.org/boards/STM32F103C8T6-Blue-Pill)
* [ST Micro STM32F407 "Discovery"](https://www.st.com/en/evaluation-tools/stm32f4discovery.html)
* [X9 Pro smartwatch](https://github.com/curtpw/nRF5x-device-reverse-engineering/tree/master/X9-nrf52832-activity-tracker/)
@@ -148,6 +148,6 @@ The original reasoning was: if [Python](https://micropython.org/) can run on mic
This project is licensed under the BSD 3-clause license, just like the [Go project](https://golang.org/LICENSE) itself.
Some code has been copied from the LLVM project and is therefore licensed under [a variant of the Apache 2.0 license](http://releases.llvm.org/10.0.0/LICENSE.TXT). This has been clearly indicated in the header of these files.
Some code has been copied from the LLVM project and is therefore licensed under [a variant of the Apache 2.0 license](http://releases.llvm.org/11.0.0/LICENSE.TXT). This has been clearly indicated in the header of these files.
Some code has been copied and/or ported from Paul Stoffregen's Teensy libraries and is therefore licensed under PJRC's license. This has been clearly indicated in the header of these files.
+6 -2
View File
@@ -18,7 +18,7 @@ jobs:
- task: Cache@2
displayName: Cache LLVM source
inputs:
key: llvm-source-10-windows-v1
key: llvm-source-11-windows-v1
path: llvm-project
- task: Bash@3
displayName: Download LLVM source
@@ -32,7 +32,7 @@ jobs:
- task: CacheBeta@0
displayName: Cache LLVM build
inputs:
key: llvm-build-10-windows-v1
key: llvm-build-11-windows-v3
path: llvm-build
- task: Bash@3
displayName: Build LLVM
@@ -41,7 +41,11 @@ jobs:
script: |
if [ ! -f llvm-build/lib/liblldELF.a ]
then
# install dependencies
choco install ninja
# hack ninja to use fewer jobs
echo -e 'C:\\ProgramData\\Chocolatey\\bin\\ninja -j4 %*' > /usr/bin/ninja.bat
# build!
make llvm-build
fi
- task: Bash@3
+43 -23
View File
@@ -8,6 +8,7 @@ import (
"encoding/binary"
"errors"
"fmt"
"go/types"
"io/ioutil"
"os"
"path/filepath"
@@ -19,6 +20,7 @@ import (
"github.com/tinygo-org/tinygo/compiler"
"github.com/tinygo-org/tinygo/goenv"
"github.com/tinygo-org/tinygo/interp"
"github.com/tinygo-org/tinygo/loader"
"github.com/tinygo-org/tinygo/stacksize"
"github.com/tinygo-org/tinygo/transform"
"tinygo.org/x/go-llvm"
@@ -43,16 +45,31 @@ type BuildResult struct {
// The error value may be of type *MultiError. Callers will likely want to check
// for this case and print such errors individually.
func Build(pkgName, outpath string, config *compileopts.Config, action func(BuildResult) error) error {
// Compile Go code to IR.
// Load the target machine, which is the LLVM object that contains all
// details of a target (alignment restrictions, pointer size, default
// address spaces, etc).
machine, err := compiler.NewTargetMachine(config)
if err != nil {
return err
}
buildOutput, errs := compiler.Compile(pkgName, machine, config)
// Load entire program AST into memory.
lprogram, err := loader.Load(config, []string{pkgName}, config.ClangHeaders, types.Config{
Sizes: compiler.Sizes(machine),
})
if err != nil {
return err
}
err = lprogram.Parse()
if err != nil {
return err
}
// Compile AST to IR.
mod, errs := compiler.CompileProgram(pkgName, lprogram, machine, config)
if errs != nil {
return newMultiError(errs)
}
mod := buildOutput.Mod
if config.Options.PrintIR {
fmt.Println("; Generated LLVM IR:")
@@ -79,7 +96,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
// keep functions interoperable, pass int64 types as pointers to
// stack-allocated values.
// Use -wasm-abi=generic to disable this behaviour.
if config.Options.WasmAbi == "js" && strings.HasPrefix(config.Triple(), "wasm") {
if config.WasmAbi() == "js" {
err := transform.ExternalInt64AsPtr(mod)
if err != nil {
return err
@@ -117,16 +134,15 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
return errors.New("verification failure after LLVM optimization passes")
}
// On the AVR, pointers can point either to flash or to RAM, but we don't
// know. As a temporary fix, load all global variables in RAM.
// In the future, there should be a compiler pass that determines which
// pointers are flash and which are in RAM so that pointers can have a
// correct address space parameter (address space 1 is for flash).
if strings.HasPrefix(config.Triple(), "avr") {
transform.NonConstGlobals(mod)
if err := llvm.VerifyModule(mod, llvm.PrintMessageAction); err != nil {
return errors.New("verification error after making all globals non-constant on AVR")
}
// LLVM 11 by default tries to emit tail calls (even with the target feature
// disabled) unless it is explicitly disabled with a function attribute.
// This is a problem, as it tries to emit them and prints an error when it
// can't with this feature disabled.
// Because as of september 2020 tail calls are not yet widely supported,
// they need to be disabled until they are widely supported (at which point
// the +tail-call target feautre can be set).
if strings.HasPrefix(config.Triple(), "wasm") {
transform.DisableTailCalls(mod)
}
// Make sure stack sizes are loaded from a separate section so they can be
@@ -209,17 +225,21 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
}
// Compile C files in packages.
for i, file := range buildOutput.ExtraFiles {
outpath := filepath.Join(dir, "pkg"+strconv.Itoa(i)+"-"+filepath.Base(file)+".o")
err := runCCompiler(config.Target.Compiler, append(config.CFlags(), "-c", "-o", outpath, file)...)
if err != nil {
return &commandError{"failed to build", file, err}
// Gather the list of (C) file paths that should be included in the build.
for i, pkg := range lprogram.Sorted() {
for j, filename := range pkg.CFiles {
file := filepath.Join(pkg.Dir, filename)
outpath := filepath.Join(dir, "pkg"+strconv.Itoa(i)+"."+strconv.Itoa(j)+"-"+filepath.Base(file)+".o")
err := runCCompiler(config.Target.Compiler, append(config.CFlags(), "-c", "-o", outpath, file)...)
if err != nil {
return &commandError{"failed to build", file, err}
}
ldflags = append(ldflags, outpath)
}
ldflags = append(ldflags, outpath)
}
if len(buildOutput.ExtraLDFlags) > 0 {
ldflags = append(ldflags, buildOutput.ExtraLDFlags...)
if len(lprogram.LDFlags) > 0 {
ldflags = append(ldflags, lprogram.LDFlags...)
}
// Link the object files together.
@@ -305,7 +325,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
}
return action(BuildResult{
Binary: tmppath,
MainDir: buildOutput.MainDir,
MainDir: lprogram.MainPkg().Dir,
})
}
}
+21 -18
View File
@@ -101,7 +101,7 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
// Target Options
Opts.Triple = llvm::Triple::normalize(Args.getLastArgValue(OPT_triple));
Opts.CPU = Args.getLastArgValue(OPT_target_cpu);
Opts.CPU = std::string(Args.getLastArgValue(OPT_target_cpu));
Opts.Features = Args.getAllArgValues(OPT_target_feature);
// Use the default target triple if unspecified.
@@ -132,13 +132,19 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
Opts.RelaxELFRelocations = Args.hasArg(OPT_mrelax_relocations);
Opts.DwarfVersion = getLastArgIntValue(Args, OPT_dwarf_version_EQ, 2, Diags);
Opts.DwarfDebugFlags = Args.getLastArgValue(OPT_dwarf_debug_flags);
Opts.DwarfDebugProducer = Args.getLastArgValue(OPT_dwarf_debug_producer);
Opts.DebugCompilationDir = Args.getLastArgValue(OPT_fdebug_compilation_dir);
Opts.MainFileName = Args.getLastArgValue(OPT_main_file_name);
Opts.DwarfDebugFlags =
std::string(Args.getLastArgValue(OPT_dwarf_debug_flags));
Opts.DwarfDebugProducer =
std::string(Args.getLastArgValue(OPT_dwarf_debug_producer));
Opts.DebugCompilationDir =
std::string(Args.getLastArgValue(OPT_fdebug_compilation_dir));
Opts.MainFileName = std::string(Args.getLastArgValue(OPT_main_file_name));
for (const auto &Arg : Args.getAllArgValues(OPT_fdebug_prefix_map_EQ))
Opts.DebugPrefixMap.insert(StringRef(Arg).split('='));
for (const auto &Arg : Args.getAllArgValues(OPT_fdebug_prefix_map_EQ)) {
auto Split = StringRef(Arg).split('=');
Opts.DebugPrefixMap.insert(
{std::string(Split.first), std::string(Split.second)});
}
// Frontend Options
if (Args.hasArg(OPT_INPUT)) {
@@ -154,8 +160,9 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
}
}
Opts.LLVMArgs = Args.getAllArgValues(OPT_mllvm);
Opts.OutputPath = Args.getLastArgValue(OPT_o);
Opts.SplitDwarfOutput = Args.getLastArgValue(OPT_split_dwarf_output);
Opts.OutputPath = std::string(Args.getLastArgValue(OPT_o));
Opts.SplitDwarfOutput =
std::string(Args.getLastArgValue(OPT_split_dwarf_output));
if (Arg *A = Args.getLastArg(OPT_filetype)) {
StringRef Name = A->getValue();
unsigned OutputType = StringSwitch<unsigned>(Name)
@@ -183,8 +190,9 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
Opts.NoExecStack = Args.hasArg(OPT_mno_exec_stack);
Opts.FatalWarnings = Args.hasArg(OPT_massembler_fatal_warnings);
Opts.NoWarn = Args.hasArg(OPT_massembler_no_warn);
Opts.RelocationModel = Args.getLastArgValue(OPT_mrelocation_model, "pic");
Opts.TargetABI = Args.getLastArgValue(OPT_target_abi);
Opts.RelocationModel =
std::string(Args.getLastArgValue(OPT_mrelocation_model, "pic"));
Opts.TargetABI = std::string(Args.getLastArgValue(OPT_target_abi));
Opts.IncrementalLinkerCompatible =
Args.hasArg(OPT_mincremental_linker_compatible);
Opts.SymbolDefs = Args.getAllArgValues(OPT_defsym);
@@ -314,12 +322,7 @@ bool ExecuteAssembler(AssemblerInvocation &Opts, DiagnosticsEngine &Diags) {
SrcMgr.getMemoryBuffer(BufferIndex)->getBuffer());
// Build up the feature string from the target feature list.
std::string FS;
if (!Opts.Features.empty()) {
FS = Opts.Features[0];
for (unsigned i = 1, e = Opts.Features.size(); i != e; ++i)
FS += "," + Opts.Features[i];
}
std::string FS = llvm::join(Opts.Features, ",");
std::unique_ptr<MCStreamer> Str;
@@ -383,7 +386,7 @@ bool ExecuteAssembler(AssemblerInvocation &Opts, DiagnosticsEngine &Diags) {
MCSection *AsmLabel = Ctx.getMachOSection(
"__LLVM", "__asm", MachO::S_REGULAR, 4, SectionKind::getReadOnly());
Str.get()->SwitchSection(AsmLabel);
Str.get()->EmitZeros(1);
Str.get()->emitZeros(1);
}
// Assembly to object compilation should leverage assembly info.
+1
View File
@@ -11,6 +11,7 @@
#include <clang/FrontendTool/Utils.h>
#include <llvm/ADT/IntrusiveRefCntPtr.h>
#include <llvm/Option/Option.h>
#include <llvm/Support/Host.h>
using namespace llvm;
using namespace clang;
+29 -9
View File
@@ -6,6 +6,9 @@ import (
"os/exec"
"path/filepath"
"sort"
"strings"
"tinygo.org/x/go-llvm"
)
// getClangHeaderPath returns the path to the built-in Clang headers. It tries
@@ -26,6 +29,7 @@ func getClangHeaderPath(TINYGOROOT string) string {
// It looks like we are built with a system-installed LLVM. Do a last
// attempt: try to use Clang headers relative to the clang binary.
llvmMajor := strings.Split(llvm.Version, ".")[0]
for _, cmdName := range commands["clang"] {
binpath, err := exec.LookPath(cmdName)
if err == nil {
@@ -40,22 +44,38 @@ func getClangHeaderPath(TINYGOROOT string) string {
// Example executable:
// /usr/lib/llvm-9/bin/clang
// Example include path:
// /usr/lib/llvm-9/lib/clang/9.0.1/include/
// /usr/lib/llvm-9/lib64/clang/9.0.1/include/
llvmRoot := filepath.Dir(filepath.Dir(binpath))
clangVersionRoot := filepath.Join(llvmRoot, "lib", "clang")
dirs, err := ioutil.ReadDir(clangVersionRoot)
if err != nil {
clangVersionRoot := filepath.Join(llvmRoot, "lib64", "clang")
dirs64, err64 := ioutil.ReadDir(clangVersionRoot)
// Example include path:
// /usr/lib/llvm-9/lib/clang/9.0.1/include/
clangVersionRoot = filepath.Join(llvmRoot, "lib", "clang")
dirs32, err32 := ioutil.ReadDir(clangVersionRoot)
if err64 != nil && err32 != nil {
// Unexpected.
continue
}
dirnames := make([]string, len(dirs))
for i, d := range dirs {
dirnames[i] = d.Name()
dirnames := make([]string, len(dirs64)+len(dirs32))
dirCount := 0
for _, d := range dirs32 {
name := d.Name()
if name == llvmMajor || strings.HasPrefix(name, llvmMajor+".") {
dirnames[dirCount] = filepath.Join(llvmRoot, "lib", "clang", name)
dirCount++
}
}
for _, d := range dirs64 {
name := d.Name()
if name == llvmMajor || strings.HasPrefix(name, llvmMajor+".") {
dirnames[dirCount] = filepath.Join(llvmRoot, "lib64", "clang", name)
dirCount++
}
}
sort.Strings(dirnames)
// Check for the highest version first.
for i := len(dirnames) - 1; i >= 0; i-- {
path := filepath.Join(clangVersionRoot, dirnames[i], "include")
for i := dirCount - 1; i >= 0; i-- {
path := filepath.Join(dirnames[i], "include")
_, err := os.Stat(filepath.Join(path, "stdint.h"))
if err == nil {
return path
+7 -1
View File
@@ -61,7 +61,7 @@ func extractROM(path string) (uint64, []byte, error) {
progs := make(progSlice, 0, 2)
for _, prog := range f.Progs {
if prog.Type != elf.PT_LOAD || prog.Filesz == 0 {
if prog.Type != elf.PT_LOAD || prog.Filesz == 0 || prog.Off == 0 {
continue
}
progs = append(progs, prog)
@@ -73,6 +73,12 @@ func extractROM(path string) (uint64, []byte, error) {
var rom []byte
for _, prog := range progs {
romEnd := progs[0].Paddr + uint64(len(rom))
if prog.Paddr > romEnd && prog.Paddr < romEnd+16 {
// Sometimes, the linker seems to insert a bit of padding between
// segments. Simply zero-fill these parts.
rom = append(rom, make([]byte, prog.Paddr-romEnd)...)
}
if prog.Paddr != progs[0].Paddr+uint64(len(rom)) {
diff := prog.Paddr - (progs[0].Paddr + uint64(len(rom)))
if diff > maxPadBytes {
+1 -1
View File
@@ -1,5 +1,5 @@
// +build !byollvm
// +build !llvm9
// +build !llvm9,!llvm11
package cgo
+14
View File
@@ -0,0 +1,14 @@
// +build !byollvm
// +build llvm11
package cgo
/*
#cgo linux CFLAGS: -I/usr/lib/llvm-11/include
#cgo darwin CFLAGS: -I/usr/local/opt/llvm@11/include
#cgo freebsd CFLAGS: -I/usr/local/llvm11/include
#cgo linux LDFLAGS: -L/usr/lib/llvm-11/lib -lclang
#cgo darwin LDFLAGS: -L/usr/local/opt/llvm@11/lib -lclang -lffi
#cgo freebsd LDFLAGS: -L/usr/local/llvm11/lib -lclang
*/
import "C"
+15 -10
View File
@@ -7,7 +7,6 @@ import (
"fmt"
"path/filepath"
"regexp"
"strconv"
"strings"
"github.com/tinygo-org/tinygo/goenv"
@@ -118,12 +117,12 @@ func (c *Config) NeedsStackObjects() bool {
switch c.GC() {
case "conservative", "extalloc":
for _, tag := range c.BuildTags() {
if tag == "baremetal" {
return false
if tag == "wasm" {
return true
}
}
return true
return false
default:
return false
}
@@ -203,12 +202,6 @@ func (c *Config) LDFlags() []string {
ldflags = append(ldflags, strings.Replace(flag, "{root}", root, -1))
}
ldflags = append(ldflags, "-L", root)
if c.Target.GOARCH == "wasm" {
// Round heap size to next multiple of 65536 (the WebAssembly page
// size).
heapSize := (c.Options.HeapSize + (65536 - 1)) &^ (65536 - 1)
ldflags = append(ldflags, "--initial-memory="+strconv.FormatInt(heapSize, 10))
}
if c.Target.LinkerScript != "" {
ldflags = append(ldflags, "-T", c.Target.LinkerScript)
}
@@ -303,6 +296,9 @@ func (c *Config) OpenOCDConfiguration() (args []string, err error) {
return nil, fmt.Errorf("unknown OpenOCD transport: %#v", c.Target.OpenOCDTransport)
}
args = []string{"-f", "interface/" + openocdInterface + ".cfg"}
for _, cmd := range c.Target.OpenOCDCommands {
args = append(args, "-c", cmd)
}
if c.Target.OpenOCDTransport != "" {
args = append(args, "-c", "transport select "+c.Target.OpenOCDTransport)
}
@@ -329,6 +325,15 @@ func (c *Config) RelocationModel() string {
return "static"
}
// WasmAbi returns the WASM ABI which is specified in the target JSON file, and
// the value is overridden by `-wasm-abi` flag if it is provided
func (c *Config) WasmAbi() string {
if c.Options.WasmAbi != "" {
return c.Options.WasmAbi
}
return c.Target.WasmAbi
}
type TestConfig struct {
CompileTestBinary bool
// TODO: Filter the test functions to run, include verbose flag, etc
+1 -1
View File
@@ -23,6 +23,7 @@ type Options struct {
PrintIR bool
DumpSSA bool
VerifyIR bool
PrintCommands bool
Debug bool
PrintSizes string
PrintStacks bool
@@ -30,7 +31,6 @@ type Options struct {
LDFlags []string
Tags string
WasmAbi string
HeapSize int64
TestConfig TestConfig
Programmer string
}
+62 -111
View File
@@ -8,6 +8,7 @@ import (
"io"
"os"
"path/filepath"
"reflect"
"runtime"
"strings"
@@ -39,7 +40,7 @@ type TargetSpec struct {
LDFlags []string `json:"ldflags"`
LinkerScript string `json:"linkerscript"`
ExtraFiles []string `json:"extra-files"`
Emulator []string `json:"emulator"`
Emulator []string `json:"emulator" override:"copy"` // inherited Emulator must not be append
FlashCommand string `json:"flash-command"`
GDB string `json:"gdb"`
PortReset string `json:"flash-1200-bps-reset"`
@@ -51,105 +52,53 @@ type TargetSpec struct {
OpenOCDInterface string `json:"openocd-interface"`
OpenOCDTarget string `json:"openocd-target"`
OpenOCDTransport string `json:"openocd-transport"`
OpenOCDCommands []string `json:"openocd-commands"`
JLinkDevice string `json:"jlink-device"`
CodeModel string `json:"code-model"`
RelocationModel string `json:"relocation-model"`
WasmAbi string `json:"wasm-abi"`
}
// copyProperties copies all properties that are set in spec2 into itself.
func (spec *TargetSpec) copyProperties(spec2 *TargetSpec) {
// TODO: simplify this using reflection? Inherits and BuildTags are special
// cases, but the rest can simply be copied if set.
spec.Inherits = append(spec.Inherits, spec2.Inherits...)
if spec2.Triple != "" {
spec.Triple = spec2.Triple
}
if spec2.CPU != "" {
spec.CPU = spec2.CPU
}
spec.Features = append(spec.Features, spec2.Features...)
if spec2.GOOS != "" {
spec.GOOS = spec2.GOOS
}
if spec2.GOARCH != "" {
spec.GOARCH = spec2.GOARCH
}
spec.BuildTags = append(spec.BuildTags, spec2.BuildTags...)
if spec2.GC != "" {
spec.GC = spec2.GC
}
if spec2.Scheduler != "" {
spec.Scheduler = spec2.Scheduler
}
if spec2.Compiler != "" {
spec.Compiler = spec2.Compiler
}
if spec2.Linker != "" {
spec.Linker = spec2.Linker
}
if spec2.RTLib != "" {
spec.RTLib = spec2.RTLib
}
if spec2.Libc != "" {
spec.Libc = spec2.Libc
}
if spec2.AutoStackSize != nil {
spec.AutoStackSize = spec2.AutoStackSize
}
if spec2.DefaultStackSize != 0 {
spec.DefaultStackSize = spec2.DefaultStackSize
}
spec.CFlags = append(spec.CFlags, spec2.CFlags...)
spec.LDFlags = append(spec.LDFlags, spec2.LDFlags...)
if spec2.LinkerScript != "" {
spec.LinkerScript = spec2.LinkerScript
}
spec.ExtraFiles = append(spec.ExtraFiles, spec2.ExtraFiles...)
if len(spec2.Emulator) != 0 {
spec.Emulator = spec2.Emulator
}
if spec2.FlashCommand != "" {
spec.FlashCommand = spec2.FlashCommand
}
if spec2.GDB != "" {
spec.GDB = spec2.GDB
}
if spec2.PortReset != "" {
spec.PortReset = spec2.PortReset
}
if spec2.FlashMethod != "" {
spec.FlashMethod = spec2.FlashMethod
}
if spec2.FlashVolume != "" {
spec.FlashVolume = spec2.FlashVolume
}
if spec2.FlashFilename != "" {
spec.FlashFilename = spec2.FlashFilename
}
if spec2.UF2FamilyID != "" {
spec.UF2FamilyID = spec2.UF2FamilyID
}
if spec2.BinaryFormat != "" {
spec.BinaryFormat = spec2.BinaryFormat
}
if spec2.OpenOCDInterface != "" {
spec.OpenOCDInterface = spec2.OpenOCDInterface
}
if spec2.OpenOCDTarget != "" {
spec.OpenOCDTarget = spec2.OpenOCDTarget
}
if spec2.OpenOCDTransport != "" {
spec.OpenOCDTransport = spec2.OpenOCDTransport
}
if spec2.JLinkDevice != "" {
spec.JLinkDevice = spec2.JLinkDevice
}
if spec2.CodeModel != "" {
spec.CodeModel = spec2.CodeModel
}
// overrideProperties overrides all properties that are set in child into itself using reflection.
func (spec *TargetSpec) overrideProperties(child *TargetSpec) {
specType := reflect.TypeOf(spec).Elem()
specValue := reflect.ValueOf(spec).Elem()
childValue := reflect.ValueOf(child).Elem()
if spec2.RelocationModel != "" {
spec.RelocationModel = spec2.RelocationModel
for i := 0; i < specType.NumField(); i++ {
field := specType.Field(i)
src := childValue.Field(i)
dst := specValue.Field(i)
switch kind := field.Type.Kind(); kind {
case reflect.String: // for strings, just copy the field of child to spec if not empty
if src.Len() > 0 {
dst.Set(src)
}
case reflect.Uint, reflect.Uint32, reflect.Uint64: // for Uint, copy if not zero
if src.Uint() != 0 {
dst.Set(src)
}
case reflect.Ptr: // for pointers, copy if not nil
if !src.IsNil() {
dst.Set(src)
}
case reflect.Slice: // for slices...
if src.Len() > 0 { // ... if not empty ...
switch tag := field.Tag.Get("override"); tag {
case "copy":
// copy the field of child to spec
dst.Set(src)
case "append", "":
// or append the field of child to spec
dst.Set(reflect.AppendSlice(src, dst))
default:
panic("override mode must be 'copy' or 'append' (default). I don't know how to '" + tag + "'.")
}
}
default:
panic("unknown field type : " + kind.String())
}
}
}
@@ -198,11 +147,11 @@ func (spec *TargetSpec) resolveInherits() error {
if err != nil {
return err
}
newSpec.copyProperties(subtarget)
newSpec.overrideProperties(subtarget)
}
// When all properties are loaded, make sure they are properly inherited.
newSpec.copyProperties(spec)
newSpec.overrideProperties(spec)
*spec = *newSpec
return nil
@@ -268,6 +217,7 @@ func LoadTarget(target string) (*TargetSpec, error) {
}
goarch := map[string]string{ // map from LLVM arch to Go arch
"i386": "386",
"i686": "386",
"x86_64": "amd64",
"aarch64": "arm64",
"armv7": "arm",
@@ -283,39 +233,40 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
// No target spec available. Use the default one, useful on most systems
// with a regular OS.
spec := TargetSpec{
Triple: triple,
GOOS: goos,
GOARCH: goarch,
BuildTags: []string{goos, goarch},
Compiler: "clang",
Linker: "cc",
CFlags: []string{"--target=" + triple},
GDB: "gdb",
PortReset: "false",
FlashMethod: "native",
Triple: triple,
GOOS: goos,
GOARCH: goarch,
BuildTags: []string{goos, goarch},
Compiler: "clang",
Linker: "cc",
CFlags: []string{"--target=" + triple},
GDB: "gdb",
PortReset: "false",
}
if goos == "darwin" {
spec.LDFlags = append(spec.LDFlags, "-Wl,-dead_strip")
} else {
spec.LDFlags = append(spec.LDFlags, "-no-pie", "-Wl,--gc-sections") // WARNING: clang < 5.0 requires -nopie
}
if goarch != "wasm" {
spec.ExtraFiles = append(spec.ExtraFiles, "src/runtime/gc_"+goarch+".S")
}
if goarch != runtime.GOARCH {
// Some educated guesses as to how to invoke helper programs.
spec.GDB = "gdb-multiarch"
if goarch == "arm" && goos == "linux" {
spec.CFlags = append(spec.CFlags, "--sysroot=/usr/arm-linux-gnueabihf")
spec.Linker = "arm-linux-gnueabihf-gcc"
spec.GDB = "arm-linux-gnueabihf-gdb"
spec.Emulator = []string{"qemu-arm", "-L", "/usr/arm-linux-gnueabihf"}
}
if goarch == "arm64" && goos == "linux" {
spec.CFlags = append(spec.CFlags, "--sysroot=/usr/aarch64-linux-gnu")
spec.Linker = "aarch64-linux-gnu-gcc"
spec.GDB = "aarch64-linux-gnu-gdb"
spec.Emulator = []string{"qemu-aarch64", "-L", "/usr/aarch64-linux-gnu"}
}
if goarch == "386" {
spec.CFlags = []string{"-m32"}
spec.LDFlags = []string{"-m32"}
if goarch == "386" && runtime.GOARCH == "amd64" {
spec.CFlags = append(spec.CFlags, "-m32")
spec.LDFlags = append(spec.LDFlags, "-m32")
}
}
return &spec, nil
+68 -1
View File
@@ -1,6 +1,9 @@
package compileopts
import "testing"
import (
"reflect"
"testing"
)
func TestLoadTarget(t *testing.T) {
_, err := LoadTarget("arduino")
@@ -17,3 +20,67 @@ func TestLoadTarget(t *testing.T) {
t.Error("LoadTarget failed for wrong reason:", err)
}
}
func TestOverrideProperties(t *testing.T) {
baseAutoStackSize := true
base := &TargetSpec{
GOOS: "baseGoos",
CPU: "baseCpu",
Features: []string{"bf1", "bf2"},
BuildTags: []string{"bt1", "bt2"},
Emulator: []string{"be1", "be2"},
DefaultStackSize: 42,
AutoStackSize: &baseAutoStackSize,
}
childAutoStackSize := false
child := &TargetSpec{
GOOS: "",
CPU: "chlidCpu",
Features: []string{"cf1", "cf2"},
Emulator: []string{"ce1", "ce2"},
AutoStackSize: &childAutoStackSize,
DefaultStackSize: 64,
}
base.overrideProperties(child)
if base.GOOS != "baseGoos" {
t.Errorf("Overriding failed : got %v", base.GOOS)
}
if base.CPU != "chlidCpu" {
t.Errorf("Overriding failed : got %v", base.CPU)
}
if !reflect.DeepEqual(base.Features, []string{"cf1", "cf2", "bf1", "bf2"}) {
t.Errorf("Overriding failed : got %v", base.Features)
}
if !reflect.DeepEqual(base.BuildTags, []string{"bt1", "bt2"}) {
t.Errorf("Overriding failed : got %v", base.BuildTags)
}
if !reflect.DeepEqual(base.Emulator, []string{"ce1", "ce2"}) {
t.Errorf("Overriding failed : got %v", base.Emulator)
}
if *base.AutoStackSize != false {
t.Errorf("Overriding failed : got %v", base.AutoStackSize)
}
if base.DefaultStackSize != 64 {
t.Errorf("Overriding failed : got %v", base.DefaultStackSize)
}
baseAutoStackSize = true
base = &TargetSpec{
AutoStackSize: &baseAutoStackSize,
DefaultStackSize: 42,
}
child = &TargetSpec{
AutoStackSize: nil,
DefaultStackSize: 0,
}
base.overrideProperties(child)
if *base.AutoStackSize != true {
t.Errorf("Overriding failed : got %v", base.AutoStackSize)
}
if base.DefaultStackSize != 42 {
t.Errorf("Overriding failed : got %v", base.DefaultStackSize)
}
}
+157 -97
View File
@@ -9,6 +9,7 @@ import (
"go/token"
"go/types"
"path/filepath"
"sort"
"strconv"
"strings"
@@ -53,6 +54,45 @@ type compilerContext struct {
astComments map[string]*ast.CommentGroup
}
// newCompilerContext returns a new compiler context ready for use, most
// importantly with a newly created LLVM context and module.
func newCompilerContext(moduleName string, machine llvm.TargetMachine, config *compileopts.Config) *compilerContext {
c := &compilerContext{
Config: config,
difiles: make(map[string]llvm.Metadata),
ditypes: make(map[types.Type]llvm.Metadata),
machine: machine,
targetData: machine.CreateTargetData(),
}
c.ctx = llvm.NewContext()
c.mod = c.ctx.NewModule(moduleName)
c.mod.SetTarget(config.Triple())
c.mod.SetDataLayout(c.targetData.String())
if c.Debug() {
c.dibuilder = llvm.NewDIBuilder(c.mod)
}
c.uintptrType = c.ctx.IntType(c.targetData.PointerSize() * 8)
if c.targetData.PointerSize() <= 4 {
// 8, 16, 32 bits targets
c.intType = c.ctx.Int32Type()
} else if c.targetData.PointerSize() == 8 {
// 64 bits target
c.intType = c.ctx.Int64Type()
} else {
panic("unknown pointer size")
}
c.i8ptrType = llvm.PointerType(c.ctx.Int8Type(), 0)
dummyFuncType := llvm.FunctionType(c.ctx.VoidType(), nil, false)
dummyFunc := llvm.AddFunction(c.mod, "tinygo.dummy", dummyFuncType)
c.funcPtrAddrSpace = dummyFunc.Type().PointerAddressSpace()
dummyFunc.EraseFromParentAsFunction()
return c
}
// builder contains all information relevant to build a single function.
type builder struct {
*compilerContext
@@ -76,6 +116,18 @@ type builder struct {
deferBuiltinFuncs map[ssa.Value]deferBuiltin
}
func newBuilder(c *compilerContext, irbuilder llvm.Builder, f *ir.Function) *builder {
return &builder{
compilerContext: c,
Builder: irbuilder,
fn: f,
locals: make(map[ssa.Value]llvm.Value),
dilocals: make(map[*types.Var]llvm.Metadata),
blockEntries: make(map[*ssa.BasicBlock]llvm.BasicBlock),
blockExits: make(map[*ssa.BasicBlock]llvm.BasicBlock),
}
}
type deferBuiltin struct {
funcName string
callback int
@@ -127,94 +179,47 @@ func NewTargetMachine(config *compileopts.Config) (llvm.TargetMachine, error) {
return machine, nil
}
// CompilerOutput is returned from the Compile() call. It contains the compile
// output and information necessary to continue to compile and link the program.
type CompilerOutput struct {
// The LLVM module that contains the compiled but not optimized LLVM module
// for all the Go code in the program.
Mod llvm.Module
// Sizes returns a types.Sizes appropriate for the given target machine. It
// includes the correct int size and aligment as is necessary for the Go
// typechecker.
func Sizes(machine llvm.TargetMachine) types.Sizes {
targetData := machine.CreateTargetData()
defer targetData.Dispose()
// ExtraFiles is a list of C source files included in packages that should
// be built and linked together with the main executable to form one
// program. They can be used from CGo, for example.
ExtraFiles []string
// ExtraLDFlags are linker flags obtained during CGo processing. These flags
// must be passed to the linker which links the entire executable.
ExtraLDFlags []string
// MainDir is the absolute directory path to the directory of the main
// package. This is useful for testing: tests must be run in the package
// directory that is being tested.
MainDir string
}
// Compile the given package path or .go file path. Return an error when this
// fails (in any stage). If successful it returns the LLVM module and a list of
// extra C files to be compiled. If not, one or more errors will be returned.
//
// The fact that it returns a list of filenames to compile is a layering
// violation. Eventually, this Compile function should only compile a single
// package and not the whole program, and loading of the program (including CGo
// processing) should be moved outside the compiler package.
func Compile(pkgName string, machine llvm.TargetMachine, config *compileopts.Config) (output CompilerOutput, errors []error) {
c := &compilerContext{
Config: config,
difiles: make(map[string]llvm.Metadata),
ditypes: make(map[types.Type]llvm.Metadata),
machine: machine,
targetData: machine.CreateTargetData(),
intPtrType := targetData.IntPtrType()
if intPtrType.IntTypeWidth()/8 <= 32 {
}
c.ctx = llvm.NewContext()
c.mod = c.ctx.NewModule(pkgName)
c.mod.SetTarget(config.Triple())
c.mod.SetDataLayout(c.targetData.String())
if c.Debug() {
c.dibuilder = llvm.NewDIBuilder(c.mod)
}
output.Mod = c.mod
c.uintptrType = c.ctx.IntType(c.targetData.PointerSize() * 8)
if c.targetData.PointerSize() <= 4 {
var intWidth int
if targetData.PointerSize() <= 4 {
// 8, 16, 32 bits targets
c.intType = c.ctx.Int32Type()
} else if c.targetData.PointerSize() == 8 {
intWidth = 32
} else if targetData.PointerSize() == 8 {
// 64 bits target
c.intType = c.ctx.Int64Type()
intWidth = 64
} else {
panic("unknown pointer size")
}
c.i8ptrType = llvm.PointerType(c.ctx.Int8Type(), 0)
dummyFuncType := llvm.FunctionType(c.ctx.VoidType(), nil, false)
dummyFunc := llvm.AddFunction(c.mod, "tinygo.dummy", dummyFuncType)
c.funcPtrAddrSpace = dummyFunc.Type().PointerAddressSpace()
dummyFunc.EraseFromParentAsFunction()
lprogram, err := loader.Load(c.Config, []string{pkgName}, c.ClangHeaders, types.Config{
Sizes: &stdSizes{
IntSize: int64(c.targetData.TypeAllocSize(c.intType)),
PtrSize: int64(c.targetData.PointerSize()),
MaxAlign: int64(c.targetData.PrefTypeAlignment(c.i8ptrType)),
}})
if err != nil {
return output, []error{err}
return &stdSizes{
IntSize: int64(intWidth / 8),
PtrSize: int64(targetData.PointerSize()),
MaxAlign: int64(targetData.PrefTypeAlignment(intPtrType)),
}
}
err = lprogram.Parse()
if err != nil {
return output, []error{err}
}
output.ExtraLDFlags = lprogram.LDFlags
output.MainDir = lprogram.MainPkg().Dir
// CompileProgram compiles the given package path or .go file path. Return an
// error when this fails (in any stage). If successful it returns the LLVM
// module. If not, one or more errors will be returned.
func CompileProgram(pkgName string, lprogram *loader.Program, machine llvm.TargetMachine, config *compileopts.Config) (llvm.Module, []error) {
c := newCompilerContext(pkgName, machine, config)
c.ir = ir.NewProgram(lprogram)
// Run a simple dead code elimination pass.
err = c.ir.SimpleDCE()
err := c.ir.SimpleDCE()
if err != nil {
return output, []error{err}
return llvm.Module{}, []error{err}
}
// Initialize debug information.
@@ -265,15 +270,7 @@ func Compile(pkgName string, machine llvm.TargetMachine, config *compileopts.Con
}
// Create the function definition.
b := builder{
compilerContext: c,
Builder: irbuilder,
fn: f,
locals: make(map[ssa.Value]llvm.Value),
dilocals: make(map[*types.Var]llvm.Metadata),
blockEntries: make(map[*ssa.BasicBlock]llvm.BasicBlock),
blockExits: make(map[*ssa.BasicBlock]llvm.BasicBlock),
}
b := newBuilder(c, irbuilder, f)
b.createFunctionDefinition()
}
@@ -352,14 +349,64 @@ func Compile(pkgName string, machine llvm.TargetMachine, config *compileopts.Con
c.dibuilder.Finalize()
}
// Gather the list of (C) file paths that should be included in the build.
for _, pkg := range c.ir.LoaderProgram.Sorted() {
for _, filename := range pkg.CFiles {
output.ExtraFiles = append(output.ExtraFiles, filepath.Join(pkg.Dir, filename))
return c.mod, c.diagnostics
}
// CompilePackage compiles a single package to a LLVM module.
func CompilePackage(moduleName string, pkg *loader.Package, machine llvm.TargetMachine, config *compileopts.Config) (llvm.Module, []error) {
c := newCompilerContext(moduleName, machine, config)
// Build SSA from AST.
ssaPkg := pkg.LoadSSA()
ssaPkg.Build()
// Sort by position, so that the order of the functions in the IR matches
// the order of functions in the source file. This is useful for testing,
// for example.
var members []string
for name := range ssaPkg.Members {
members = append(members, name)
}
sort.Slice(members, func(i, j int) bool {
iPos := ssaPkg.Members[members[i]].Pos()
jPos := ssaPkg.Members[members[j]].Pos()
if i == j {
// Cannot sort by pos, so do it by name.
return members[i] < members[j]
}
return iPos < jPos
})
// Create *ir.Functions objects.
var functions []*ir.Function
for _, name := range members {
member := ssaPkg.Members[name]
switch member := member.(type) {
case *ssa.Function:
functions = append(functions, &ir.Function{
Function: member,
})
}
}
return output, c.diagnostics
// Declare all functions.
for _, fn := range functions {
c.createFunctionDeclaration(fn)
}
// Add definitions to declarations.
irbuilder := c.ctx.NewBuilder()
defer irbuilder.Dispose()
for _, f := range functions {
if f.Blocks == nil {
continue // external function
}
// Create the function definition.
b := newBuilder(c, irbuilder, f)
b.createFunctionDefinition()
}
return c.mod, nil
}
// getLLVMRuntimeType obtains a named type from the runtime package and returns
@@ -1336,12 +1383,14 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
return b.createMemoryCopyCall(fn, instr.Args)
case name == "runtime.memzero":
return b.createMemoryZeroCall(instr.Args)
case name == "device.Asm" || name == "device/arm.Asm" || name == "device/avr.Asm" || name == "device/riscv.Asm":
case name == "device.Asm" || name == "device/arm.Asm" || name == "device/arm64.Asm" || name == "device/avr.Asm" || name == "device/riscv.Asm":
return b.createInlineAsm(instr.Args)
case name == "device.AsmFull" || name == "device/arm.AsmFull" || name == "device/avr.AsmFull" || name == "device/riscv.AsmFull":
case name == "device.AsmFull" || name == "device/arm.AsmFull" || name == "device/arm64.AsmFull" || name == "device/avr.AsmFull" || name == "device/riscv.AsmFull":
return b.createInlineAsmFull(instr)
case strings.HasPrefix(name, "device/arm.SVCall"):
return b.emitSVCall(instr.Args)
case strings.HasPrefix(name, "device/arm64.SVCall"):
return b.emitSV64Call(instr.Args)
case strings.HasPrefix(name, "(device/riscv.CSR)."):
return b.emitCSROperation(instr)
case strings.HasPrefix(name, "syscall.Syscall"):
@@ -1803,6 +1852,7 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
low,
}
b.createNilCheck(expr.X, value, "slice")
b.createSliceBoundsCheck(llvmLen, low, high, max, lowType, highType, maxType)
// Truncate ints bigger than uintptr. This is after the bounds
@@ -2044,17 +2094,17 @@ func (b *builder) createBinOp(op token.Token, typ, ytyp types.Type, x, y llvm.Va
case token.QUO: // /
return b.CreateFDiv(x, y, ""), nil
case token.EQL: // ==
return b.CreateFCmp(llvm.FloatUEQ, x, y, ""), nil
return b.CreateFCmp(llvm.FloatOEQ, x, y, ""), nil
case token.NEQ: // !=
return b.CreateFCmp(llvm.FloatUNE, x, y, ""), nil
case token.LSS: // <
return b.CreateFCmp(llvm.FloatULT, x, y, ""), nil
return b.CreateFCmp(llvm.FloatOLT, x, y, ""), nil
case token.LEQ: // <=
return b.CreateFCmp(llvm.FloatULE, x, y, ""), nil
return b.CreateFCmp(llvm.FloatOLE, x, y, ""), nil
case token.GTR: // >
return b.CreateFCmp(llvm.FloatUGT, x, y, ""), nil
return b.CreateFCmp(llvm.FloatOGT, x, y, ""), nil
case token.GEQ: // >=
return b.CreateFCmp(llvm.FloatUGE, x, y, ""), nil
return b.CreateFCmp(llvm.FloatOGE, x, y, ""), nil
default:
panic("binop on float: " + op.String())
}
@@ -2318,15 +2368,15 @@ func (b *builder) createConst(prefix string, expr *ssa.Const) llvm.Value {
return strObj
} else if typ.Kind() == types.UnsafePointer {
if !expr.IsNil() {
value, _ := constant.Uint64Val(expr.Value)
value, _ := constant.Uint64Val(constant.ToInt(expr.Value))
return llvm.ConstIntToPtr(llvm.ConstInt(b.uintptrType, value, false), b.i8ptrType)
}
return llvm.ConstNull(b.i8ptrType)
} else if typ.Info()&types.IsUnsigned != 0 {
n, _ := constant.Uint64Val(expr.Value)
n, _ := constant.Uint64Val(constant.ToInt(expr.Value))
return llvm.ConstInt(llvmType, n, false)
} else if typ.Info()&types.IsInteger != 0 { // signed
n, _ := constant.Int64Val(expr.Value)
n, _ := constant.Int64Val(constant.ToInt(expr.Value))
return llvm.ConstInt(llvmType, uint64(n), true)
} else if typ.Info()&types.IsFloat != 0 {
n, _ := constant.Float64Val(expr.Value)
@@ -2578,7 +2628,17 @@ func (b *builder) createUnOp(unop *ssa.UnOp) (llvm.Value, error) {
if typ.Info()&types.IsInteger != 0 {
return b.CreateSub(llvm.ConstInt(x.Type(), 0, false), x, ""), nil
} else if typ.Info()&types.IsFloat != 0 {
return b.CreateFSub(llvm.ConstFloat(x.Type(), 0.0), x, ""), nil
return b.CreateFNeg(x, ""), nil
} else if typ.Info()&types.IsComplex != 0 {
// Negate both components of the complex number.
r := b.CreateExtractValue(x, 0, "r")
i := b.CreateExtractValue(x, 1, "i")
r = b.CreateFNeg(r, "")
i = b.CreateFNeg(i, "")
cplx := llvm.Undef(x.Type())
cplx = b.CreateInsertValue(cplx, r, 0, "")
cplx = b.CreateInsertValue(cplx, i, 1, "")
return cplx, nil
} else {
return llvm.Value{}, b.makeError(unop.Pos(), "todo: unknown basic type for negate: "+typ.String())
}
+161
View File
@@ -0,0 +1,161 @@
package compiler
import (
"flag"
"go/types"
"io/ioutil"
"regexp"
"strconv"
"strings"
"testing"
"github.com/tinygo-org/tinygo/compileopts"
"github.com/tinygo-org/tinygo/loader"
"tinygo.org/x/go-llvm"
)
// Pass -update to go test to update the output of the test files.
var flagUpdate = flag.Bool("update", false, "update tests based on test output")
// Basic tests for the compiler. Build some Go files and compare the output with
// the expected LLVM IR for regression testing.
func TestCompiler(t *testing.T) {
target, err := compileopts.LoadTarget("i686--linux")
if err != nil {
t.Fatal("failed to load target:", err)
}
config := &compileopts.Config{
Options: &compileopts.Options{},
Target: target,
}
machine, err := NewTargetMachine(config)
if err != nil {
t.Fatal("failed to create target machine:", err)
}
tests := []string{
"basic.go",
"pointer.go",
"slice.go",
}
for _, testCase := range tests {
t.Run(testCase, func(t *testing.T) {
// Load entire program AST into memory.
lprogram, err := loader.Load(config, []string{"./testdata/" + testCase}, config.ClangHeaders, types.Config{
Sizes: Sizes(machine),
})
if err != nil {
t.Fatal("failed to create target machine:", err)
}
err = lprogram.Parse()
if err != nil {
t.Fatalf("could not parse test case %s: %s", testCase, err)
}
// Compile AST to IR.
pkg := lprogram.MainPkg()
mod, errs := CompilePackage(testCase, pkg, machine, config)
if errs != nil {
for _, err := range errs {
t.Log("error:", err)
}
return
}
// Optimize IR a little.
funcPasses := llvm.NewFunctionPassManagerForModule(mod)
defer funcPasses.Dispose()
funcPasses.AddInstructionCombiningPass()
funcPasses.InitializeFunc()
for fn := mod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) {
funcPasses.RunFunc(fn)
}
funcPasses.FinalizeFunc()
outfile := "./testdata/" + testCase[:len(testCase)-3] + ".ll"
// Update test if needed. Do not check the result.
if *flagUpdate {
err := ioutil.WriteFile(outfile, []byte(mod.String()), 0666)
if err != nil {
t.Error("failed to write updated output file:", err)
}
return
}
expected, err := ioutil.ReadFile(outfile)
if err != nil {
t.Fatal("failed to read golden file:", err)
}
if !fuzzyEqualIR(mod.String(), string(expected)) {
t.Errorf("output does not match expected output:\n%s", mod.String())
}
})
}
}
var alignRegexp = regexp.MustCompile(", align [0-9]+$")
// fuzzyEqualIR returns true if the two LLVM IR strings passed in are roughly
// equal. That means, only relevant lines are compared (excluding comments
// etc.).
func fuzzyEqualIR(s1, s2 string) bool {
lines1 := filterIrrelevantIRLines(strings.Split(s1, "\n"))
lines2 := filterIrrelevantIRLines(strings.Split(s2, "\n"))
if len(lines1) != len(lines2) {
return false
}
for i, line1 := range lines1 {
line2 := lines2[i]
match1 := alignRegexp.MatchString(line1)
match2 := alignRegexp.MatchString(line2)
if match1 != match2 {
// Only one of the lines has the align keyword. Remove it.
// This is a change to make the test work in both LLVM 10 and LLVM
// 11 (LLVM 11 appears to automatically add alignment everywhere).
line1 = alignRegexp.ReplaceAllString(line1, "")
line2 = alignRegexp.ReplaceAllString(line2, "")
}
if line1 != line2 {
return false
}
}
return true
}
// filterIrrelevantIRLines removes lines from the input slice of strings that
// are not relevant in comparing IR. For example, empty lines and comments are
// stripped out.
func filterIrrelevantIRLines(lines []string) []string {
var out []string
llvmVersion, err := strconv.Atoi(strings.Split(llvm.Version, ".")[0])
if err != nil {
// Note: this should never happen and if it does, it will always happen
// for a particular build because llvm.Version is a constant.
panic(err)
}
for _, line := range lines {
line = strings.Split(line, ";")[0] // strip out comments/info
line = strings.TrimRight(line, "\r ") // drop '\r' on Windows and remove trailing spaces from comments
if line == "" {
continue
}
if strings.HasPrefix(line, "source_filename = ") {
continue
}
if llvmVersion < 10 && strings.HasPrefix(line, "attributes ") {
// Ignore attribute groups. These may change between LLVM versions.
// Right now test outputs are for LLVM 10.
continue
}
if llvmVersion < 10 && strings.HasPrefix(line, "target datalayout ") {
// Ignore the target layout. This may change between LLVM versions.
continue
}
out = append(out, line)
}
return out
}
+38
View File
@@ -163,6 +163,44 @@ func (b *builder) emitSVCall(args []ssa.Value) (llvm.Value, error) {
return b.CreateCall(target, llvmArgs, ""), nil
}
// This is a compiler builtin which emits an inline SVCall instruction. It can
// be one of:
//
// func SVCall0(num uintptr) uintptr
// func SVCall1(num uintptr, a1 interface{}) uintptr
// func SVCall2(num uintptr, a1, a2 interface{}) uintptr
// func SVCall3(num uintptr, a1, a2, a3 interface{}) uintptr
// func SVCall4(num uintptr, a1, a2, a3, a4 interface{}) uintptr
//
// The num parameter must be a constant. All other parameters may be any scalar
// value supported by LLVM inline assembly.
// Same as emitSVCall but for AArch64
func (b *builder) emitSV64Call(args []ssa.Value) (llvm.Value, error) {
num, _ := constant.Uint64Val(args[0].(*ssa.Const).Value)
llvmArgs := []llvm.Value{}
argTypes := []llvm.Type{}
asm := "svc #" + strconv.FormatUint(num, 10)
constraints := "={x0}"
for i, arg := range args[1:] {
arg = arg.(*ssa.MakeInterface).X
if i == 0 {
constraints += ",0"
} else {
constraints += ",{x" + strconv.Itoa(i) + "}"
}
llvmValue := b.getValue(arg)
llvmArgs = append(llvmArgs, llvmValue)
argTypes = append(argTypes, llvmValue.Type())
}
// Implement the ARM64 calling convention by marking x1-x7 as
// clobbered. x0 is used as an output register so doesn't have to be
// marked as clobbered.
constraints += ",~{x1},~{x2},~{x3},~{x4},~{x5},~{x6},~{x7}"
fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
target := llvm.InlineAsm(fnType, asm, constraints, true, false, 0)
return b.CreateCall(target, llvmArgs, ""), nil
}
// This is a compiler builtin which emits CSR instructions. It can be one of:
//
// func (csr CSR) Get() uintptr
+1 -3
View File
@@ -465,9 +465,7 @@ func (c *compilerContext) getInterfaceInvokeWrapper(f *ir.Function) llvm.Value {
paramTypes := append([]llvm.Type{c.i8ptrType}, fnType.ParamTypes()[len(expandedReceiverType):]...)
wrapFnType := llvm.FunctionType(fnType.ReturnType(), paramTypes, false)
wrapper = llvm.AddFunction(c.mod, wrapperName, wrapFnType)
if f.LLVMFn.LastParam().Name() == "parentHandle" {
wrapper.LastParam().SetName("parentHandle")
}
wrapper.LastParam().SetName("parentHandle")
wrapper.SetLinkage(llvm.InternalLinkage)
wrapper.SetUnnamedAddr(true)
+45 -25
View File
@@ -26,7 +26,6 @@ func EmitPointerPack(builder llvm.Builder, mod llvm.Module, config *compileopts.
packedType := ctx.StructType(valueTypes, false)
// Allocate memory for the packed data.
var packedAlloc, packedHeapAlloc llvm.Value
size := targetData.TypeAllocSize(packedType)
if size == 0 {
return llvm.ConstPointerNull(i8ptrType)
@@ -39,9 +38,39 @@ func EmitPointerPack(builder llvm.Builder, mod llvm.Module, config *compileopts.
// Try to keep this cast in SSA form.
return builder.CreateIntToPtr(values[0], i8ptrType, "pack.int")
}
// Because packedType is a struct and we have to cast it to a *i8, store
// it in an alloca first for bitcasting (store+bitcast+load).
packedAlloc, _, _ = CreateTemporaryAlloca(builder, mod, packedType, "")
// it in a *i8 alloca first and load the *i8 value from there. This is
// effectively a bitcast.
packedAlloc, _, _ := CreateTemporaryAlloca(builder, mod, i8ptrType, "")
if size < targetData.TypeAllocSize(i8ptrType) {
// The alloca is bigger than the value that will be stored in it.
// To avoid having some bits undefined, zero the alloca first.
// Hopefully this will get optimized away.
builder.CreateStore(llvm.ConstNull(i8ptrType), packedAlloc)
}
// Store all values in the alloca.
packedAllocCast := builder.CreateBitCast(packedAlloc, llvm.PointerType(packedType, 0), "")
for i, value := range values {
indices := []llvm.Value{
llvm.ConstInt(ctx.Int32Type(), 0, false),
llvm.ConstInt(ctx.Int32Type(), uint64(i), false),
}
gep := builder.CreateInBoundsGEP(packedAllocCast, indices, "")
builder.CreateStore(value, gep)
}
// Load value (the *i8) from the alloca.
result := builder.CreateLoad(packedAlloc, "")
// End the lifetime of the alloca, to help the optimizer.
packedPtr := builder.CreateBitCast(packedAlloc, i8ptrType, "")
packedSize := llvm.ConstInt(ctx.Int64Type(), targetData.TypeAllocSize(packedAlloc.Type()), false)
EmitLifetimeEnd(builder, mod, packedPtr, packedSize)
return result
} else {
// Check if the values are all constants.
constant := true
@@ -67,7 +96,7 @@ func EmitPointerPack(builder llvm.Builder, mod llvm.Module, config *compileopts.
// Packed data is bigger than a pointer, so allocate it on the heap.
sizeValue := llvm.ConstInt(uintptrType, size, false)
alloc := mod.NamedFunction("runtime.alloc")
packedHeapAlloc = builder.CreateCall(alloc, []llvm.Value{
packedHeapAlloc := builder.CreateCall(alloc, []llvm.Value{
sizeValue,
llvm.Undef(i8ptrType), // unused context parameter
llvm.ConstPointerNull(i8ptrType), // coroutine handle
@@ -80,28 +109,19 @@ func EmitPointerPack(builder llvm.Builder, mod llvm.Module, config *compileopts.
llvm.ConstPointerNull(i8ptrType), // coroutine handle
}, "")
}
packedAlloc = builder.CreateBitCast(packedHeapAlloc, llvm.PointerType(packedType, 0), "")
}
// Store all values in the alloca or heap pointer.
for i, value := range values {
indices := []llvm.Value{
llvm.ConstInt(ctx.Int32Type(), 0, false),
llvm.ConstInt(ctx.Int32Type(), uint64(i), false),
}
gep := builder.CreateInBoundsGEP(packedAlloc, indices, "")
builder.CreateStore(value, gep)
}
packedAlloc := builder.CreateBitCast(packedHeapAlloc, llvm.PointerType(packedType, 0), "")
if packedHeapAlloc.IsNil() {
// Load value (as *i8) from the alloca.
packedAlloc = builder.CreateBitCast(packedAlloc, llvm.PointerType(i8ptrType, 0), "")
result := builder.CreateLoad(packedAlloc, "")
packedPtr := builder.CreateBitCast(packedAlloc, i8ptrType, "")
packedSize := llvm.ConstInt(ctx.Int64Type(), targetData.TypeAllocSize(packedAlloc.Type()), false)
EmitLifetimeEnd(builder, mod, packedPtr, packedSize)
return result
} else {
// Get the original heap allocation pointer, which already is an *i8.
// Store all values in the heap pointer.
for i, value := range values {
indices := []llvm.Value{
llvm.ConstInt(ctx.Int32Type(), 0, false),
llvm.ConstInt(ctx.Int32Type(), uint64(i), false),
}
gep := builder.CreateInBoundsGEP(packedAlloc, indices, "")
builder.CreateStore(value, gep)
}
// Return the original heap allocation pointer, which already is an *i8.
return packedHeapAlloc
}
}
+57
View File
@@ -0,0 +1,57 @@
package main
// Basic tests that don't need to be split into a separate file.
func addInt(x, y int) int {
return x + y
}
func equalInt(x, y int) bool {
return x == y
}
func floatEQ(x, y float32) bool {
return x == y
}
func floatNE(x, y float32) bool {
return x != y
}
func floatLower(x, y float32) bool {
return x < y
}
func floatLowerEqual(x, y float32) bool {
return x <= y
}
func floatGreater(x, y float32) bool {
return x > y
}
func floatGreaterEqual(x, y float32) bool {
return x >= y
}
func complexReal(x complex64) float32 {
return real(x)
}
func complexImag(x complex64) float32 {
return imag(x)
}
func complexAdd(x, y complex64) complex64 {
return x + y
}
func complexSub(x, y complex64) complex64 {
return x - y
}
func complexMul(x, y complex64) complex64 {
return x * y
}
// TODO: complexDiv (requires runtime call)
+98
View File
@@ -0,0 +1,98 @@
; ModuleID = 'basic.go'
source_filename = "basic.go"
target datalayout = "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128"
target triple = "i686--linux"
define internal void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
entry:
ret void
}
define internal i32 @main.addInt(i32 %x, i32 %y, i8* %context, i8* %parentHandle) unnamed_addr {
entry:
%0 = add i32 %x, %y
ret i32 %0
}
define internal i1 @main.equalInt(i32 %x, i32 %y, i8* %context, i8* %parentHandle) unnamed_addr {
entry:
%0 = icmp eq i32 %x, %y
ret i1 %0
}
define internal i1 @main.floatEQ(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr {
entry:
%0 = fcmp oeq float %x, %y
ret i1 %0
}
define internal i1 @main.floatNE(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr {
entry:
%0 = fcmp une float %x, %y
ret i1 %0
}
define internal i1 @main.floatLower(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr {
entry:
%0 = fcmp olt float %x, %y
ret i1 %0
}
define internal i1 @main.floatLowerEqual(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr {
entry:
%0 = fcmp ole float %x, %y
ret i1 %0
}
define internal i1 @main.floatGreater(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr {
entry:
%0 = fcmp ogt float %x, %y
ret i1 %0
}
define internal i1 @main.floatGreaterEqual(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr {
entry:
%0 = fcmp oge float %x, %y
ret i1 %0
}
define internal float @main.complexReal(float %x.r, float %x.i, i8* %context, i8* %parentHandle) unnamed_addr {
entry:
ret float %x.r
}
define internal float @main.complexImag(float %x.r, float %x.i, i8* %context, i8* %parentHandle) unnamed_addr {
entry:
ret float %x.i
}
define internal { float, float } @main.complexAdd(float %x.r, float %x.i, float %y.r, float %y.i, i8* %context, i8* %parentHandle) unnamed_addr {
entry:
%0 = fadd float %x.r, %y.r
%1 = fadd float %x.i, %y.i
%2 = insertvalue { float, float } undef, float %0, 0
%3 = insertvalue { float, float } %2, float %1, 1
ret { float, float } %3
}
define internal { float, float } @main.complexSub(float %x.r, float %x.i, float %y.r, float %y.i, i8* %context, i8* %parentHandle) unnamed_addr {
entry:
%0 = fsub float %x.r, %y.r
%1 = fsub float %x.i, %y.i
%2 = insertvalue { float, float } undef, float %0, 0
%3 = insertvalue { float, float } %2, float %1, 1
ret { float, float } %3
}
define internal { float, float } @main.complexMul(float %x.r, float %x.i, float %y.r, float %y.i, i8* %context, i8* %parentHandle) unnamed_addr {
entry:
%0 = fmul float %x.r, %y.r
%1 = fmul float %x.i, %y.i
%2 = fsub float %0, %1
%3 = fmul float %x.r, %y.i
%4 = fmul float %x.i, %y.r
%5 = fadd float %3, %4
%6 = insertvalue { float, float } undef, float %2, 0
%7 = insertvalue { float, float } %6, float %5, 1
ret { float, float } %7
}
+41
View File
@@ -0,0 +1,41 @@
package main
// This file tests various operations on pointers, such as pointer arithmetic
// and dereferencing pointers.
import "unsafe"
// Dereference pointers.
func pointerDerefZero(x *[0]int) [0]int {
return *x // This is a no-op, there is nothing to load.
}
// Unsafe pointer casts, they are sometimes a no-op.
func pointerCastFromUnsafe(x unsafe.Pointer) *int {
return (*int)(x)
}
func pointerCastToUnsafe(x *int) unsafe.Pointer {
return unsafe.Pointer(x)
}
func pointerCastToUnsafeNoop(x *byte) unsafe.Pointer {
return unsafe.Pointer(x)
}
// The compiler has support for a few special cast+add patterns that are
// transformed into a single GEP.
func pointerUnsafeGEPFixedOffset(ptr *byte) *byte {
return (*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(ptr)) + 10))
}
func pointerUnsafeGEPByteOffset(ptr *byte, offset uintptr) *byte {
return (*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(ptr)) + offset))
}
func pointerUnsafeGEPIntOffset(ptr *int32, offset uintptr) *int32 {
return (*int32)(unsafe.Pointer(uintptr(unsafe.Pointer(ptr)) + offset*4))
}
+49
View File
@@ -0,0 +1,49 @@
; ModuleID = 'pointer.go'
source_filename = "pointer.go"
target datalayout = "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128"
target triple = "i686--linux"
define internal void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
entry:
ret void
}
define internal [0 x i32] @main.pointerDerefZero([0 x i32]* %x, i8* %context, i8* %parentHandle) unnamed_addr {
entry:
ret [0 x i32] zeroinitializer
}
define internal i32* @main.pointerCastFromUnsafe(i8* %x, i8* %context, i8* %parentHandle) unnamed_addr {
entry:
%0 = bitcast i8* %x to i32*
ret i32* %0
}
define internal i8* @main.pointerCastToUnsafe(i32* dereferenceable_or_null(4) %x, i8* %context, i8* %parentHandle) unnamed_addr {
entry:
%0 = bitcast i32* %x to i8*
ret i8* %0
}
define internal i8* @main.pointerCastToUnsafeNoop(i8* dereferenceable_or_null(1) %x, i8* %context, i8* %parentHandle) unnamed_addr {
entry:
ret i8* %x
}
define internal i8* @main.pointerUnsafeGEPFixedOffset(i8* dereferenceable_or_null(1) %ptr, i8* %context, i8* %parentHandle) unnamed_addr {
entry:
%0 = getelementptr inbounds i8, i8* %ptr, i32 10
ret i8* %0
}
define internal i8* @main.pointerUnsafeGEPByteOffset(i8* dereferenceable_or_null(1) %ptr, i32 %offset, i8* %context, i8* %parentHandle) unnamed_addr {
entry:
%0 = getelementptr inbounds i8, i8* %ptr, i32 %offset
ret i8* %0
}
define internal i32* @main.pointerUnsafeGEPIntOffset(i32* dereferenceable_or_null(4) %ptr, i32 %offset, i8* %context, i8* %parentHandle) unnamed_addr {
entry:
%0 = getelementptr i32, i32* %ptr, i32 %offset
ret i32* %0
}
+9
View File
@@ -0,0 +1,9 @@
package main
func sliceLen(ints []int) int {
return len(ints)
}
func sliceCap(ints []int) int {
return cap(ints)
}
+19
View File
@@ -0,0 +1,19 @@
; ModuleID = 'slice.go'
source_filename = "slice.go"
target datalayout = "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128"
target triple = "i686--linux"
define internal void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
entry:
ret void
}
define internal i32 @main.sliceLen(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i8* %context, i8* %parentHandle) unnamed_addr {
entry:
ret i32 %ints.len
}
define internal i32 @main.sliceCap(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i8* %context, i8* %parentHandle) unnamed_addr {
entry:
ret i32 %ints.cap
}
+3 -3
View File
@@ -4,11 +4,11 @@ go 1.11
require (
github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2
github.com/chromedp/cdproto v0.0.0-20200116234248-4da64dd111ac
github.com/chromedp/chromedp v0.5.3
github.com/chromedp/cdproto v0.0.0-20200709115526-d1f6fc58448b
github.com/chromedp/chromedp v0.5.4-0.20200303084119-2bb39134ab9e
github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf
github.com/marcinbor85/gohex v0.0.0-20200531091804-343a4b548892
go.bug.st/serial v1.0.0
golang.org/x/tools v0.0.0-20200216192241-b320d3a0f5a2
tinygo.org/x/go-llvm v0.0.0-20200503225853-345b2947b59d
tinygo.org/x/go-llvm v0.0.0-20201104183921-570e7a6841d9
)
+10 -8
View File
@@ -1,9 +1,10 @@
github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2 h1:oMCHnXa6CCCafdPDbMh/lWRhRByN0VFLvv+g+ayx1SI=
github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2/go.mod h1:PkYb9DJNAwrSvRx5DYA+gUcOIgTGVMNkfSCbZM8cWpI=
github.com/chromedp/cdproto v0.0.0-20200116234248-4da64dd111ac h1:T7V5BXqnYd55Hj/g5uhDYumg9Fp3rMTS6bykYtTIFX4=
github.com/chromedp/cdproto v0.0.0-20200116234248-4da64dd111ac/go.mod h1:PfAWWKJqjlGFYJEidUM6aVIWPr0EpobeyVWEEmplX7g=
github.com/chromedp/chromedp v0.5.3 h1:F9LafxmYpsQhWQBdCs+6Sret1zzeeFyHS5LkRF//Ffg=
github.com/chromedp/chromedp v0.5.3/go.mod h1:YLdPtndaHQ4rCpSpBG+IPpy9JvX0VD+7aaLxYgYj28w=
github.com/chromedp/cdproto v0.0.0-20200709115526-d1f6fc58448b h1:LF+GRwyzxrO3MUzPvejv+yBup0lNG+/QdIRrkxOPseA=
github.com/chromedp/cdproto v0.0.0-20200709115526-d1f6fc58448b/go.mod h1:E6LPWRdIJc11h/di5p0rwvRmUYbhGpBEH7ZbPfzDIOE=
github.com/chromedp/chromedp v0.5.4-0.20200303084119-2bb39134ab9e h1:Hv0JVyHhbIXb9NiYQe4NsrfgrSofAp0q2FnhhJOXgi8=
github.com/chromedp/chromedp v0.5.4-0.20200303084119-2bb39134ab9e/go.mod h1:vmQMRHFZrY3T+Jv51T0n87OK/i6bK+5P9a+Fg5jPwgQ=
github.com/creack/goselect v0.1.1 h1:tiSSgKE1eJtxs1h/VgGQWuXUP0YS4CDIFMp6vaI1ls0=
github.com/creack/goselect v0.1.1/go.mod h1:a/NhLweNvqIYMuxcMOuWY516Cimucms3DglDzQP3hKY=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -11,14 +12,15 @@ github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8=
github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo=
github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
github.com/gobwas/ws v1.0.3 h1:ZOigqf7iBxkA4jdQ3am7ATzdlOFp9YzA6NmuvEEZc9g=
github.com/gobwas/ws v1.0.3/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf h1:7+FW5aGwISbqUtkfmIpZJGRgNFg2ioYPvFaUxdqpDsg=
github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf/go.mod h1:RpwtwJQFrIEPstU94h88MWPXP2ektJZ8cZ0YntAmXiE=
github.com/knq/sysutil v0.0.0-20191005231841-15668db23d08 h1:V0an7KRw92wmJysvFvtqtKMAPmvS5O0jtB0nYo6t+gs=
github.com/knq/sysutil v0.0.0-20191005231841-15668db23d08/go.mod h1:dFWs1zEqDjFtnBXsd1vPOZaLsESovai349994nHx3e0=
github.com/mailru/easyjson v0.7.0 h1:aizVhC/NAAcKWb+5QsU1iNOZb4Yws5UO2I+aIprQITM=
github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
github.com/mailru/easyjson v0.7.1 h1:mdxE1MF9o53iCb2Ghj1VfWvh7ZOwHpnVG/xwXrV90U8=
github.com/mailru/easyjson v0.7.1/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
github.com/marcinbor85/gohex v0.0.0-20200531091804-343a4b548892 h1:6J+qramlHVLmiBOgRiBOnQkno8uprqG6YFFQTt6uYIw=
github.com/marcinbor85/gohex v0.0.0-20200531091804-343a4b548892/go.mod h1:Pb6XcsXyropB9LNHhnqaknG/vEwYztLkQzVCHv8sQ3M=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
@@ -46,5 +48,5 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbO
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
tinygo.org/x/go-llvm v0.0.0-20200503225853-345b2947b59d h1:hcX7vpB067GWM/EH4sGGOti0PMgIx+0bbZwUXctOIvE=
tinygo.org/x/go-llvm v0.0.0-20200503225853-345b2947b59d/go.mod h1:fv1F0BSNpxMfCL0zF3M4OPFbgYHnhtB6ST0HvUtu/LE=
tinygo.org/x/go-llvm v0.0.0-20201104183921-570e7a6841d9 h1:l2kTQOhqEoeDTK3ckUnwReOQwMPUmURMIdjJbeAuDT4=
tinygo.org/x/go-llvm v0.0.0-20201104183921-570e7a6841d9/go.mod h1:fv1F0BSNpxMfCL0zF3M4OPFbgYHnhtB6ST0HvUtu/LE=
+1 -1
View File
@@ -12,7 +12,7 @@ import (
// Version of TinyGo.
// Update this value before release of new version of software.
const Version = "0.15.0"
const Version = "0.17.0-dev"
// GetGorootVersion returns the major and minor version for a given GOROOT path.
// If the goroot cannot be determined, (0, 0) is returned.
+92 -37
View File
@@ -6,50 +6,81 @@ possible and only run unknown expressions (e.g. external calls) at runtime. This
is in practice a partial evaluator of the `runtime.initAll` function, which
calls each package initializer.
It works by directly interpreting LLVM IR:
This package is a rewrite of a previous partial evaluator that worked
directly on LLVM IR and used the module and LLVM constants as intermediate
values. This newer version instead uses a mostly Go intermediate form. It
compiles functions and extracts relevant data first (compiler.go), then
executes those functions (interpreter.go) in a memory space that can be
rolled back per function (memory.go). This means that it is not necessary to
scan functions to see whether they can be run at compile time, which was very
error prone. Instead it just tries to execute everything and if it hits
something it cannot interpret (such as a store to memory-mapped I/O) it rolls
back the execution of that function and runs the function at runtime instead.
All in all, this design provides several benefits:
* Almost all operations work directly on constants, and are implemented using
the llvm.Const* set of functions that are evaluated directly.
* External function calls and some other operations (inline assembly, volatile
load, volatile store) are seen as having limited side effects. Limited in
the sense that it is known at compile time which globals it affects, which
then are marked 'dirty' (meaning, further operations on it must be done at
runtime). These operations are emitted directly in the `runtime.initAll`
function. Return values are also considered 'dirty'.
* Such 'dirty' objects and local values must be executed at runtime instead of
at compile time. This dirtyness propagates further through the IR, for
example storing a dirty local value to a global also makes the global dirty,
meaning that the global may not be read or written at compile time as it's
contents at that point during interpretation is unknown.
* There are some heuristics in place to avoid doing too much with dirty
values. For example, a branch based on a dirty local marks the whole
function itself as having side effect (as if it is an external function).
However, all globals it touches are still taken into account and when a call
is inserted in `runtime.initAll`, all globals it references are also marked
dirty.
* Heap allocation (`runtime.alloc`) is emulated by creating new objects. The
value in the allocation is the initializer of the global, the zero value is
the zero initializer.
* Stack allocation (`alloca`) is often emulated using a fake alloca object,
until the address of the alloca is taken in which case it is also created as
a real `alloca` in `runtime.initAll` and marked dirty. This may be necessary
when calling an external function with the given alloca as paramter.
* Much better error handling. By being able to revert to runtime execution
without the need for scanning functions, this version is able to
automatically work around many bugs in the previous implementation.
* More correct memory model. This is not inherent to the new design, but the
new design also made the memory model easier to reason about.
* Faster execution of initialization code. While it is not much faster for
normal interpretation (maybe 25% or so) due to the compilation overhead,
it should be a whole lot faster for loops as it doesn't have to call into
LLVM (via CGo) for every operation.
As mentioned, this partial evaulator comes in three parts: a compiler, an
interpreter, and a memory manager.
## Compiler
The main task of the compiler is that it extracts all necessary data from
every instruction in a function so that when this instruction is interpreted,
no additional CGo calls are necessary. This is not currently done for all
instructions (`runtime.alloc` is a notable exception), but at least it does
so for the vast majority of instructions.
## Interpreter
The interpreter runs an instruction just like it would if it were executed
'for real'. The vast majority of instructions can be executed at compile
time. As indicated above, some instructions need to be executed at runtime
instead.
## Memory
Memory is represented as objects (the `object` type) that contains data that
will eventually be stored in a global and values (the `value` interface) that
can be worked with while running the interpreter. Values therefore are only
used locally and are always passed by value (just like most LLVM constants)
while objects represent the backing storage (like LLVM globals). Some values
are pointer values, and point to an object.
Importantly, this partial evaluator can roll back the execution of a
function. This is implemented by creating a new memory view per function
activation, which makes sure that any change to a global (such as a store
instruction) is stored in the memory view. It creates a copy of the object
and stores that in the memory view to be modified. Once the function has
executed successfully, all these modified objects are then copied into the
parent function, up to the root function invocation which (on successful
execution) writes the values back into the LLVM module. This way, function
invocations can be rolled back without leaving a trace.
Pointer values point to memory objects, but not to a particular memory
object. Every memory object is given an index, and pointers use that index to
look up the current active object for the pointer to load from or to copy
when storing to it.
Rolling back a function should roll back everyting, including the few
instructions emitted at runtime. This is done by treating instructions much
like memory objects and removing the created instructions when necessary.
## Why is this necessary?
A partial evaluator is hard to get right, so why go through all the trouble of
writing one?
The main reason is that the previous attempt wasn't complete and wasn't sound.
It simply tried to evaluate Go SSA directly, which was good but more difficult
than necessary. An IR based interpreter needs to understand fewer instructions
as the LLVM IR simply has less (complex) instructions than Go SSA. Also, LLVM
provides some useful tools like easily getting all uses of a function or global,
which Go SSA does not provide.
But why is it necessary at all? The answer is that globals with initializers are
much easier to optimize by LLVM than initialization code. Also, there are a few
other benefits:
The answer is that globals with initializers are much easier to optimize by
LLVM than initialization code. Also, there are a few other benefits:
* Dead globals are trivial to optimize away.
* Constant globals are easier to detect. Remember that Go does not have global
@@ -60,5 +91,29 @@ other benefits:
* Constants are much more efficent on microcontrollers, as they can be
allocated in flash instead of RAM.
The Go SSA package does not create constant initializers for globals.
Instead, it emits initialization functions, so if you write the following:
```go
var foo = []byte{1, 2, 3, 4}
```
It would generate something like this:
```go
var foo []byte
func init() {
foo = make([]byte, 4)
foo[0] = 1
foo[1] = 2
foo[2] = 3
foo[3] = 4
}
```
This is of course hugely wasteful, it's much better to create `foo` as a
global array instead of initializing it at runtime.
For more details, see [this section of the
documentation](https://tinygo.org/compiler-internals/differences-from-go/).
+410
View File
@@ -0,0 +1,410 @@
package interp
// This file compiles the LLVM IR to a form that's easy to efficiently
// interpret.
import (
"strings"
"tinygo.org/x/go-llvm"
)
// A function is a compiled LLVM function, which means that interpreting it
// avoids most CGo calls necessary. This is done in a separate step so the
// result can be cached.
// Functions are in SSA form, just like the LLVM version if it. The first block
// (blocks[0]) is the entry block.
type function struct {
llvmFn llvm.Value
name string // precalculated llvmFn.Name()
params []llvm.Value // precalculated llvmFn.Params()
blocks []*basicBlock
locals map[llvm.Value]int
}
// basicBlock represents a LLVM basic block and contains a slice of
// instructions. The last instruction must be a terminator instruction.
type basicBlock struct {
instructions []instruction
}
// instruction is a precompiled LLVM IR instruction. The operands can be either
// an already known value (such as literalValue or pointerValue) but can also be
// the special localValue, which means that the value is a function parameter or
// is produced by another instruction in the function. In that case, the
// interpreter will replace the operand with that local value.
type instruction struct {
opcode llvm.Opcode
localIndex int
operands []value
llvmInst llvm.Value
name string
}
// String returns a nice human-readable version of this instruction.
func (inst *instruction) String() string {
operands := make([]string, len(inst.operands))
for i, op := range inst.operands {
operands[i] = op.String()
}
name := instructionNameMap[inst.opcode]
if name == "" {
name = "<unknown op>"
}
return name + " " + strings.Join(operands, " ")
}
// compileFunction compiles a given LLVM function to an easier to interpret
// version of the function. As far as possible, all operands are preprocessed so
// that the interpreter doesn't have to call into LLVM.
func (r *runner) compileFunction(llvmFn llvm.Value) *function {
fn := &function{
llvmFn: llvmFn,
name: llvmFn.Name(),
params: llvmFn.Params(),
locals: make(map[llvm.Value]int),
}
if llvmFn.IsDeclaration() {
// Nothing to do.
return fn
}
for i, param := range fn.params {
fn.locals[param] = i
}
// Make a map of all the blocks, to quickly find the block number for a
// given branch instruction.
blockIndices := make(map[llvm.Value]int)
for llvmBB := llvmFn.FirstBasicBlock(); !llvmBB.IsNil(); llvmBB = llvm.NextBasicBlock(llvmBB) {
index := len(blockIndices)
blockIndices[llvmBB.AsValue()] = index
}
// Compile every block.
for llvmBB := llvmFn.FirstBasicBlock(); !llvmBB.IsNil(); llvmBB = llvm.NextBasicBlock(llvmBB) {
bb := &basicBlock{}
fn.blocks = append(fn.blocks, bb)
// Compile every instruction in the block.
for llvmInst := llvmBB.FirstInstruction(); !llvmInst.IsNil(); llvmInst = llvm.NextInstruction(llvmInst) {
// Create instruction skeleton.
opcode := llvmInst.InstructionOpcode()
inst := instruction{
opcode: opcode,
localIndex: len(fn.locals),
llvmInst: llvmInst,
}
fn.locals[llvmInst] = len(fn.locals)
// Add operands specific for this instruction.
switch opcode {
case llvm.Ret:
// Return instruction, which can either be a `ret void` (no
// return value) or return a value.
numOperands := llvmInst.OperandsCount()
if numOperands != 0 {
inst.operands = []value{
r.getValue(llvmInst.Operand(0)),
}
}
case llvm.Br:
// Branch instruction. Can be either a conditional branch (with
// 3 operands) or unconditional branch (with just one basic
// block operand).
numOperands := llvmInst.OperandsCount()
switch numOperands {
case 3:
// Conditional jump to one of two blocks. Comparable to an
// if/else in procedural languages.
thenBB := llvmInst.Operand(2)
elseBB := llvmInst.Operand(1)
inst.operands = []value{
r.getValue(llvmInst.Operand(0)),
literalValue{uint32(blockIndices[thenBB])},
literalValue{uint32(blockIndices[elseBB])},
}
case 1:
// Unconditional jump to a target basic block. Comparable to
// a jump in C and Go.
jumpBB := llvmInst.Operand(0)
inst.operands = []value{
literalValue{uint32(blockIndices[jumpBB])},
}
default:
panic("unknown number of operands")
}
case llvm.PHI:
inst.name = llvmInst.Name()
incomingCount := inst.llvmInst.IncomingCount()
for i := 0; i < incomingCount; i++ {
incomingBB := inst.llvmInst.IncomingBlock(i)
incomingValue := inst.llvmInst.IncomingValue(i)
inst.operands = append(inst.operands,
literalValue{uint32(blockIndices[incomingBB.AsValue()])},
r.getValue(incomingValue),
)
}
case llvm.Select:
// Select is a special instruction that is much like a ternary
// operator. It produces operand 1 or 2 based on the boolean
// that is operand 0.
inst.name = llvmInst.Name()
inst.operands = []value{
r.getValue(llvmInst.Operand(0)),
r.getValue(llvmInst.Operand(1)),
r.getValue(llvmInst.Operand(2)),
}
case llvm.Call:
// Call is a regular function call but could also be a runtime
// intrinsic. Some runtime intrinsics are treated specially by
// the interpreter, such as runtime.alloc. We don't
// differentiate between them here because these calls may also
// need to be run at runtime, in which case they should all be
// created in the same way.
llvmCalledValue := llvmInst.CalledValue()
if !llvmCalledValue.IsAFunction().IsNil() {
name := llvmCalledValue.Name()
if name == "llvm.dbg.value" || strings.HasPrefix(name, "llvm.lifetime.") {
// These intrinsics should not be interpreted, they are not
// relevant to the execution of this function.
continue
}
}
inst.name = llvmInst.Name()
numOperands := llvmInst.OperandsCount()
inst.operands = append(inst.operands, r.getValue(llvmCalledValue))
for i := 0; i < numOperands-1; i++ {
inst.operands = append(inst.operands, r.getValue(llvmInst.Operand(i)))
}
case llvm.Load:
// Load instruction. The interpreter will load from the
// appropriate memory view.
// Also provide the memory size to be loaded, which is necessary
// with a lack of type information.
inst.name = llvmInst.Name()
inst.operands = []value{
r.getValue(llvmInst.Operand(0)),
literalValue{r.targetData.TypeAllocSize(llvmInst.Type())},
}
case llvm.Store:
// Store instruction. The interpreter will create a new object
// in the memory view of the function invocation and store to
// that, to make it possible to roll back this store.
inst.operands = []value{
r.getValue(llvmInst.Operand(0)),
r.getValue(llvmInst.Operand(1)),
}
case llvm.Alloca:
// Alloca allocates stack space for local variables.
numElements := r.getValue(inst.llvmInst.Operand(0)).(literalValue).value.(uint32)
elementSize := r.targetData.TypeAllocSize(inst.llvmInst.Type().ElementType())
inst.operands = []value{
literalValue{elementSize * uint64(numElements)},
}
case llvm.GetElementPtr:
// GetElementPtr does pointer arithmetic.
inst.name = llvmInst.Name()
ptr := llvmInst.Operand(0)
n := llvmInst.OperandsCount()
elementType := ptr.Type().ElementType()
// gep: [source ptr, dest value size, pairs of indices...]
inst.operands = []value{
r.getValue(ptr),
literalValue{r.targetData.TypeAllocSize(llvmInst.Type().ElementType())},
r.getValue(llvmInst.Operand(1)),
literalValue{r.targetData.TypeAllocSize(elementType)},
}
for i := 2; i < n; i++ {
operand := r.getValue(llvmInst.Operand(i))
if elementType.TypeKind() == llvm.StructTypeKind {
index := operand.(literalValue).value.(uint32)
elementOffset := r.targetData.ElementOffset(elementType, int(index))
// Encode operands in a special way. The elementOffset
// is just the offset in bytes. The elementSize is a
// negative number (when cast to a int64) by flipping
// all the bits. This allows the interpreter to detect
// this is a struct field and that it should not
// multiply it with the elementOffset to get the offset.
// It is important for the interpreter to know the
// struct field index for when the GEP must be done at
// runtime.
inst.operands = append(inst.operands, literalValue{elementOffset}, literalValue{^uint64(index)})
elementType = elementType.StructElementTypes()[index]
} else {
elementType = elementType.ElementType()
elementSize := r.targetData.TypeAllocSize(elementType)
elementSizeOperand := literalValue{elementSize}
// Add operand * elementSizeOperand bytes to the pointer.
inst.operands = append(inst.operands, operand, elementSizeOperand)
}
}
case llvm.BitCast, llvm.IntToPtr, llvm.PtrToInt:
// Bitcasts are ususally used to cast a pointer from one type to
// another leaving the pointer itself intact.
inst.name = llvmInst.Name()
inst.operands = []value{
r.getValue(llvmInst.Operand(0)),
}
case llvm.ExtractValue:
inst.name = llvmInst.Name()
agg := llvmInst.Operand(0)
var offset uint64
indexingType := agg.Type()
for _, index := range inst.llvmInst.Indices() {
switch indexingType.TypeKind() {
case llvm.StructTypeKind:
offset += r.targetData.ElementOffset(indexingType, int(index))
indexingType = indexingType.StructElementTypes()[index]
default: // ArrayTypeKind
indexingType = indexingType.ElementType()
elementSize := r.targetData.TypeAllocSize(indexingType)
offset += elementSize * uint64(index)
}
}
size := r.targetData.TypeAllocSize(inst.llvmInst.Type())
// extractvalue [agg, byteOffset, byteSize]
inst.operands = []value{
r.getValue(agg),
literalValue{offset},
literalValue{size},
}
case llvm.InsertValue:
inst.name = llvmInst.Name()
agg := llvmInst.Operand(0)
var offset uint64
indexingType := agg.Type()
for _, index := range inst.llvmInst.Indices() {
switch indexingType.TypeKind() {
case llvm.StructTypeKind:
offset += r.targetData.ElementOffset(indexingType, int(index))
indexingType = indexingType.StructElementTypes()[index]
default: // ArrayTypeKind
indexingType = indexingType.ElementType()
elementSize := r.targetData.TypeAllocSize(indexingType)
offset += elementSize * uint64(index)
}
}
// insertvalue [agg, elt, byteOffset]
inst.operands = []value{
r.getValue(agg),
r.getValue(llvmInst.Operand(1)),
literalValue{offset},
}
case llvm.ICmp:
inst.name = llvmInst.Name()
inst.operands = []value{
r.getValue(llvmInst.Operand(0)),
r.getValue(llvmInst.Operand(1)),
literalValue{uint8(llvmInst.IntPredicate())},
}
case llvm.FCmp:
inst.name = llvmInst.Name()
inst.operands = []value{
r.getValue(llvmInst.Operand(0)),
r.getValue(llvmInst.Operand(1)),
literalValue{uint8(llvmInst.FloatPredicate())},
}
case llvm.Add, llvm.Sub, llvm.Mul, llvm.UDiv, llvm.SDiv, llvm.URem, llvm.SRem, llvm.Shl, llvm.LShr, llvm.AShr, llvm.And, llvm.Or, llvm.Xor:
// Integer binary operations.
inst.name = llvmInst.Name()
inst.operands = []value{
r.getValue(llvmInst.Operand(0)),
r.getValue(llvmInst.Operand(1)),
}
case llvm.SExt, llvm.ZExt, llvm.Trunc:
// Extend or shrink an integer size.
// No sign extension going on so easy to do.
// zext: [value, bitwidth]
// trunc: [value, bitwidth]
inst.name = llvmInst.Name()
inst.operands = []value{
r.getValue(llvmInst.Operand(0)),
literalValue{uint64(llvmInst.Type().IntTypeWidth())},
}
case llvm.SIToFP, llvm.UIToFP:
// Convert an integer to a floating point instruction.
// opcode: [value, bitwidth]
inst.name = llvmInst.Name()
inst.operands = []value{
r.getValue(llvmInst.Operand(0)),
literalValue{uint64(r.targetData.TypeAllocSize(llvmInst.Type()) * 8)},
}
default:
// Unknown instruction, which is already set in inst.opcode so
// is detectable.
// This error is handled when actually trying to interpret this
// instruction (to not trigger on code that won't be executed).
}
bb.instructions = append(bb.instructions, inst)
}
}
return fn
}
// instructionNameMap maps from instruction opcodes to instruction names. This
// can be useful for debug logging.
var instructionNameMap = [...]string{
llvm.Ret: "ret",
llvm.Br: "br",
llvm.Switch: "switch",
llvm.IndirectBr: "indirectbr",
llvm.Invoke: "invoke",
llvm.Unreachable: "unreachable",
// Standard Binary Operators
llvm.Add: "add",
llvm.FAdd: "fadd",
llvm.Sub: "sub",
llvm.FSub: "fsub",
llvm.Mul: "mul",
llvm.FMul: "fmul",
llvm.UDiv: "udiv",
llvm.SDiv: "sdiv",
llvm.FDiv: "fdiv",
llvm.URem: "urem",
llvm.SRem: "srem",
llvm.FRem: "frem",
// Logical Operators
llvm.Shl: "shl",
llvm.LShr: "lshr",
llvm.AShr: "ashr",
llvm.And: "and",
llvm.Or: "or",
llvm.Xor: "xor",
// Memory Operators
llvm.Alloca: "alloca",
llvm.Load: "load",
llvm.Store: "store",
llvm.GetElementPtr: "getelementptr",
// Cast Operators
llvm.Trunc: "trunc",
llvm.ZExt: "zext",
llvm.SExt: "sext",
llvm.FPToUI: "fptoui",
llvm.FPToSI: "fptosi",
llvm.UIToFP: "uitofp",
llvm.SIToFP: "sitofp",
llvm.FPTrunc: "fptrunc",
llvm.FPExt: "fpext",
llvm.PtrToInt: "ptrtoint",
llvm.IntToPtr: "inttoptr",
llvm.BitCast: "bitcast",
// Other Operators
llvm.ICmp: "icmp",
llvm.FCmp: "fcmp",
llvm.PHI: "phi",
llvm.Call: "call",
llvm.Select: "select",
llvm.VAArg: "vaarg",
llvm.ExtractElement: "extractelement",
llvm.InsertElement: "insertelement",
llvm.ShuffleVector: "shufflevector",
llvm.ExtractValue: "extractvalue",
llvm.InsertValue: "insertvalue",
}
+16 -12
View File
@@ -11,15 +11,19 @@ import (
"tinygo.org/x/go-llvm"
)
// errUnreachable is returned when an unreachable instruction is executed. This
// error should not be visible outside of the interp package.
var errUnreachable = &Error{Err: errors.New("interp: unreachable executed")}
var errLiteralToPointer = errors.New("interp: trying to convert literal value to pointer")
// unsupportedInstructionError returns a new "unsupported instruction" error for
// the given instruction. It includes source location information, when
// available.
func (e *evalPackage) unsupportedInstructionError(inst llvm.Value) *Error {
return e.errorAt(inst, errors.New("interp: unsupported instruction"))
// These errors are expected during normal execution and can be recovered from
// by running the affected function at runtime instead of compile time.
var (
errExpectedPointer = errors.New("interp: trying to use an integer as a pointer (memory-mapped I/O?)")
errUnsupportedInst = errors.New("interp: unsupported instruction")
errUnsupportedRuntimeInst = errors.New("interp: unsupported instruction (to be emitted at runtime)")
errMapAlreadyCreated = errors.New("interp: map already created")
)
func isRecoverableError(err error) bool {
return err == errExpectedPointer || err == errUnsupportedInst || err == errUnsupportedRuntimeInst || err == errMapAlreadyCreated
}
// ErrorLine is one line in a traceback. The position may be missing.
@@ -46,13 +50,13 @@ func (e *Error) Error() string {
// errorAt returns an error value for the currently interpreted package at the
// location of the instruction. The location information may not be complete as
// it depends on debug information in the IR.
func (e *evalPackage) errorAt(inst llvm.Value, err error) *Error {
pos := getPosition(inst)
func (r *runner) errorAt(inst instruction, err error) *Error {
pos := getPosition(inst.llvmInst)
return &Error{
ImportPath: e.packagePath,
ImportPath: r.pkgName,
Pos: pos,
Err: err,
Traceback: []ErrorLine{{pos, inst}},
Traceback: []ErrorLine{{pos, inst.llvmInst}},
}
}
-708
View File
@@ -1,708 +0,0 @@
package interp
// This file implements the core interpretation routines, interpreting single
// functions.
import (
"errors"
"strings"
"tinygo.org/x/go-llvm"
)
type frame struct {
*evalPackage
fn llvm.Value
locals map[llvm.Value]Value
}
// evalBasicBlock evaluates a single basic block, returning the return value (if
// ending with a ret instruction), a list of outgoing basic blocks (if not
// ending with a ret instruction), or an error on failure.
// Most of it works at compile time. Some calls get translated into calls to be
// executed at runtime: calls to functions with side effects, external calls,
// and operations on the result of such instructions.
func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (retval Value, outgoing []llvm.Value, err *Error) {
for inst := bb.FirstInstruction(); !inst.IsNil(); inst = llvm.NextInstruction(inst) {
if fr.Debug {
print(indent)
inst.Dump()
println()
}
switch {
case !inst.IsABinaryOperator().IsNil():
lhs := fr.getLocal(inst.Operand(0)).(*LocalValue).Underlying
rhs := fr.getLocal(inst.Operand(1)).(*LocalValue).Underlying
switch inst.InstructionOpcode() {
// Standard binary operators
case llvm.Add:
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateAdd(lhs, rhs, "")}
case llvm.FAdd:
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateFAdd(lhs, rhs, "")}
case llvm.Sub:
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateSub(lhs, rhs, "")}
case llvm.FSub:
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateFSub(lhs, rhs, "")}
case llvm.Mul:
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateMul(lhs, rhs, "")}
case llvm.FMul:
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateFMul(lhs, rhs, "")}
case llvm.UDiv:
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateUDiv(lhs, rhs, "")}
case llvm.SDiv:
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateSDiv(lhs, rhs, "")}
case llvm.FDiv:
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateFDiv(lhs, rhs, "")}
case llvm.URem:
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateURem(lhs, rhs, "")}
case llvm.SRem:
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateSRem(lhs, rhs, "")}
case llvm.FRem:
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateFRem(lhs, rhs, "")}
// Logical operators
case llvm.Shl:
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateShl(lhs, rhs, "")}
case llvm.LShr:
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateLShr(lhs, rhs, "")}
case llvm.AShr:
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateAShr(lhs, rhs, "")}
case llvm.And:
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateAnd(lhs, rhs, "")}
case llvm.Or:
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateOr(lhs, rhs, "")}
case llvm.Xor:
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateXor(lhs, rhs, "")}
default:
return nil, nil, fr.unsupportedInstructionError(inst)
}
// Memory operators
case !inst.IsAAllocaInst().IsNil():
allocType := inst.Type().ElementType()
alloca := llvm.AddGlobal(fr.Mod, allocType, fr.packagePath+"$alloca")
alloca.SetInitializer(llvm.ConstNull(allocType))
alloca.SetLinkage(llvm.InternalLinkage)
fr.locals[inst] = &LocalValue{
Underlying: alloca,
Eval: fr.Eval,
}
case !inst.IsALoadInst().IsNil():
operand := fr.getLocal(inst.Operand(0)).(*LocalValue)
var value llvm.Value
if !operand.IsConstant() || inst.IsVolatile() || (!operand.Underlying.IsAConstantExpr().IsNil() && operand.Underlying.Opcode() == llvm.BitCast) {
value = fr.builder.CreateLoad(operand.Value(), inst.Name())
} else {
var err error
value, err = operand.Load()
if err != nil {
return nil, nil, fr.errorAt(inst, err)
}
}
if value.Type() != inst.Type() {
return nil, nil, fr.errorAt(inst, errors.New("interp: load: type does not match"))
}
fr.locals[inst] = fr.getValue(value)
case !inst.IsAStoreInst().IsNil():
value := fr.getLocal(inst.Operand(0))
ptr := fr.getLocal(inst.Operand(1))
if inst.IsVolatile() {
fr.builder.CreateStore(value.Value(), ptr.Value())
} else {
err := ptr.Store(value.Value())
if err != nil {
return nil, nil, fr.errorAt(inst, err)
}
}
case !inst.IsAGetElementPtrInst().IsNil():
value := fr.getLocal(inst.Operand(0))
llvmIndices := make([]llvm.Value, inst.OperandsCount()-1)
for i := range llvmIndices {
llvmIndices[i] = inst.Operand(i + 1)
}
indices := make([]uint32, len(llvmIndices))
for i, llvmIndex := range llvmIndices {
operand := fr.getLocal(llvmIndex)
if !operand.IsConstant() {
// Not a constant operation.
// This should be detected by the scanner, but isn't at the
// moment.
return nil, nil, fr.errorAt(inst, errors.New("todo: non-const gep"))
}
indices[i] = uint32(operand.Value().ZExtValue())
}
result, err := value.GetElementPtr(indices)
if err != nil {
return nil, nil, fr.errorAt(inst, err)
}
if result.Type() != inst.Type() {
return nil, nil, fr.errorAt(inst, errors.New("interp: gep: type does not match"))
}
fr.locals[inst] = result
// Cast operators
case !inst.IsATruncInst().IsNil():
value := fr.getLocal(inst.Operand(0))
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateTrunc(value.(*LocalValue).Value(), inst.Type(), "")}
case !inst.IsAZExtInst().IsNil():
value := fr.getLocal(inst.Operand(0))
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateZExt(value.(*LocalValue).Value(), inst.Type(), "")}
case !inst.IsASExtInst().IsNil():
value := fr.getLocal(inst.Operand(0))
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateSExt(value.(*LocalValue).Value(), inst.Type(), "")}
case !inst.IsAFPToUIInst().IsNil():
value := fr.getLocal(inst.Operand(0))
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateFPToUI(value.(*LocalValue).Value(), inst.Type(), "")}
case !inst.IsAFPToSIInst().IsNil():
value := fr.getLocal(inst.Operand(0))
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateFPToSI(value.(*LocalValue).Value(), inst.Type(), "")}
case !inst.IsAUIToFPInst().IsNil():
value := fr.getLocal(inst.Operand(0))
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateUIToFP(value.(*LocalValue).Value(), inst.Type(), "")}
case !inst.IsASIToFPInst().IsNil():
value := fr.getLocal(inst.Operand(0))
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateSIToFP(value.(*LocalValue).Value(), inst.Type(), "")}
case !inst.IsAFPTruncInst().IsNil():
value := fr.getLocal(inst.Operand(0))
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateFPTrunc(value.(*LocalValue).Value(), inst.Type(), "")}
case !inst.IsAFPExtInst().IsNil():
value := fr.getLocal(inst.Operand(0))
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateFPExt(value.(*LocalValue).Value(), inst.Type(), "")}
case !inst.IsAPtrToIntInst().IsNil():
value := fr.getLocal(inst.Operand(0))
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreatePtrToInt(value.Value(), inst.Type(), "")}
case !inst.IsABitCastInst().IsNil() && inst.Type().TypeKind() == llvm.PointerTypeKind:
operand := inst.Operand(0)
if !operand.IsACallInst().IsNil() {
fn := operand.CalledValue()
if !fn.IsAFunction().IsNil() && fn.Name() == "runtime.alloc" {
continue // special case: bitcast of alloc
}
}
if _, ok := fr.getLocal(operand).(*MapValue); ok {
// Special case for runtime.trackPointer calls.
// Note: this might not be entirely sound in some rare cases
// where the map is stored in a dirty global.
uses := getUses(inst)
if len(uses) == 1 {
use := uses[0]
if !use.IsACallInst().IsNil() && !use.CalledValue().IsAFunction().IsNil() && use.CalledValue().Name() == "runtime.trackPointer" {
continue
}
}
// It is not possible in Go to bitcast a map value to a pointer.
return nil, nil, fr.errorAt(inst, errors.New("unimplemented: bitcast of map"))
}
value := fr.getLocal(operand).(*LocalValue)
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateBitCast(value.Value(), inst.Type(), "")}
// Other operators
case !inst.IsAICmpInst().IsNil():
lhs := fr.getLocal(inst.Operand(0)).(*LocalValue).Underlying
rhs := fr.getLocal(inst.Operand(1)).(*LocalValue).Underlying
predicate := inst.IntPredicate()
if predicate == llvm.IntEQ {
var lhsZero, rhsZero bool
var ok1, ok2 bool
if lhs.Type().TypeKind() == llvm.PointerTypeKind {
// Unfortunately, the const propagation in the IR builder
// doesn't handle pointer compares of inttoptr values. So we
// implement it manually here.
lhsZero, ok1 = isPointerNil(lhs)
rhsZero, ok2 = isPointerNil(rhs)
}
if lhs.Type().TypeKind() == llvm.IntegerTypeKind {
lhsZero, ok1 = isZero(lhs)
rhsZero, ok2 = isZero(rhs)
}
if ok1 && ok2 {
if lhsZero && rhsZero {
// Both are zero, so this icmp is always evaluated to true.
fr.locals[inst] = &LocalValue{fr.Eval, llvm.ConstInt(fr.Mod.Context().Int1Type(), 1, false)}
continue
}
if lhsZero != rhsZero {
// Only one of them is zero, so this comparison must return false.
fr.locals[inst] = &LocalValue{fr.Eval, llvm.ConstInt(fr.Mod.Context().Int1Type(), 0, false)}
continue
}
}
}
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateICmp(predicate, lhs, rhs, "")}
case !inst.IsAFCmpInst().IsNil():
lhs := fr.getLocal(inst.Operand(0)).(*LocalValue).Underlying
rhs := fr.getLocal(inst.Operand(1)).(*LocalValue).Underlying
predicate := inst.FloatPredicate()
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateFCmp(predicate, lhs, rhs, "")}
case !inst.IsAPHINode().IsNil():
for i := 0; i < inst.IncomingCount(); i++ {
if inst.IncomingBlock(i) == incoming {
fr.locals[inst] = fr.getLocal(inst.IncomingValue(i))
}
}
case !inst.IsACallInst().IsNil():
callee := inst.CalledValue()
switch {
case callee.Name() == "runtime.alloc":
// heap allocation
users := getUses(inst)
var resultInst = inst
if len(users) == 1 && !users[0].IsABitCastInst().IsNil() {
// happens when allocating something other than i8*
resultInst = users[0]
}
size := fr.getLocal(inst.Operand(0)).(*LocalValue).Underlying.ZExtValue()
allocType := resultInst.Type().ElementType()
typeSize := fr.TargetData.TypeAllocSize(allocType)
elementCount := 1
if size != typeSize {
// allocate an array
if size%typeSize != 0 {
return nil, nil, fr.unsupportedInstructionError(inst)
}
elementCount = int(size / typeSize)
allocType = llvm.ArrayType(allocType, elementCount)
}
alloc := llvm.AddGlobal(fr.Mod, allocType, fr.packagePath+"$alloc")
alloc.SetInitializer(llvm.ConstNull(allocType))
alloc.SetLinkage(llvm.InternalLinkage)
result := &LocalValue{
Underlying: alloc,
Eval: fr.Eval,
}
if elementCount == 1 {
fr.locals[resultInst] = result
} else {
result, err := result.GetElementPtr([]uint32{0, 0})
if err != nil {
return nil, nil, fr.errorAt(inst, err)
}
fr.locals[resultInst] = result
}
case callee.Name() == "runtime.hashmapMake":
// create a map
keySize := inst.Operand(0).ZExtValue()
valueSize := inst.Operand(1).ZExtValue()
fr.locals[inst] = &MapValue{
Eval: fr.Eval,
PkgName: fr.packagePath,
KeySize: int(keySize),
ValueSize: int(valueSize),
}
case callee.Name() == "runtime.hashmapStringSet":
// set a string key in the map
keyBuf := fr.getLocal(inst.Operand(1)).(*LocalValue)
keyLen := fr.getLocal(inst.Operand(2)).(*LocalValue)
valPtr := fr.getLocal(inst.Operand(3)).(*LocalValue)
m, ok := fr.getLocal(inst.Operand(0)).(*MapValue)
if !ok || !keyBuf.IsConstant() || !keyLen.IsConstant() || !valPtr.IsConstant() {
// The mapassign operation could not be done at compile
// time. Do it at runtime instead.
m := fr.getLocal(inst.Operand(0)).Value()
fr.markDirty(m)
llvmParams := []llvm.Value{
m, // *runtime.hashmap
fr.getLocal(inst.Operand(1)).Value(), // key.ptr
fr.getLocal(inst.Operand(2)).Value(), // key.len
fr.getLocal(inst.Operand(3)).Value(), // value (unsafe.Pointer)
fr.getLocal(inst.Operand(4)).Value(), // context
fr.getLocal(inst.Operand(5)).Value(), // parentHandle
}
fr.builder.CreateCall(callee, llvmParams, "")
continue
}
// "key" is a Go string value, which in the TinyGo calling convention is split up
// into separate pointer and length parameters.
err := m.PutString(keyBuf, keyLen, valPtr)
if err != nil {
return nil, nil, fr.errorAt(inst, err)
}
case callee.Name() == "runtime.hashmapBinarySet":
// set a binary (int etc.) key in the map
keyBuf := fr.getLocal(inst.Operand(1)).(*LocalValue)
valPtr := fr.getLocal(inst.Operand(2)).(*LocalValue)
m, ok := fr.getLocal(inst.Operand(0)).(*MapValue)
if !ok || !keyBuf.IsConstant() || !valPtr.IsConstant() {
// The mapassign operation could not be done at compile
// time. Do it at runtime instead.
m := fr.getLocal(inst.Operand(0)).Value()
fr.markDirty(m)
llvmParams := []llvm.Value{
m, // *runtime.hashmap
fr.getLocal(inst.Operand(1)).Value(), // key
fr.getLocal(inst.Operand(2)).Value(), // value
fr.getLocal(inst.Operand(3)).Value(), // context
fr.getLocal(inst.Operand(4)).Value(), // parentHandle
}
fr.builder.CreateCall(callee, llvmParams, "")
continue
}
err := m.PutBinary(keyBuf, valPtr)
if err != nil {
return nil, nil, fr.errorAt(inst, err)
}
case callee.Name() == "runtime.stringConcat":
// adding two strings together
buf1Ptr := fr.getLocal(inst.Operand(0))
buf1Len := fr.getLocal(inst.Operand(1))
buf2Ptr := fr.getLocal(inst.Operand(2))
buf2Len := fr.getLocal(inst.Operand(3))
buf1, err := getStringBytes(buf1Ptr, buf1Len.Value())
if err != nil {
return nil, nil, fr.errorAt(inst, err)
}
buf2, err := getStringBytes(buf2Ptr, buf2Len.Value())
if err != nil {
return nil, nil, fr.errorAt(inst, err)
}
result := []byte(string(buf1) + string(buf2))
vals := make([]llvm.Value, len(result))
for i := range vals {
vals[i] = llvm.ConstInt(fr.Mod.Context().Int8Type(), uint64(result[i]), false)
}
globalType := llvm.ArrayType(fr.Mod.Context().Int8Type(), len(result))
globalValue := llvm.ConstArray(fr.Mod.Context().Int8Type(), vals)
global := llvm.AddGlobal(fr.Mod, globalType, fr.packagePath+"$stringconcat")
global.SetInitializer(globalValue)
global.SetLinkage(llvm.InternalLinkage)
global.SetGlobalConstant(true)
global.SetUnnamedAddr(true)
stringType := fr.Mod.GetTypeByName("runtime._string")
retPtr := llvm.ConstGEP(global, getLLVMIndices(fr.Mod.Context().Int32Type(), []uint32{0, 0}))
retLen := llvm.ConstInt(stringType.StructElementTypes()[1], uint64(len(result)), false)
ret := llvm.ConstNull(stringType)
ret = llvm.ConstInsertValue(ret, retPtr, []uint32{0})
ret = llvm.ConstInsertValue(ret, retLen, []uint32{1})
fr.locals[inst] = &LocalValue{fr.Eval, ret}
case callee.Name() == "runtime.sliceCopy":
elementSize := fr.getLocal(inst.Operand(4)).(*LocalValue).Value().ZExtValue()
dstArray := fr.getLocal(inst.Operand(0)).(*LocalValue).stripPointerCasts()
srcArray := fr.getLocal(inst.Operand(1)).(*LocalValue).stripPointerCasts()
dstLen := fr.getLocal(inst.Operand(2)).(*LocalValue)
srcLen := fr.getLocal(inst.Operand(3)).(*LocalValue)
if elementSize != 1 && dstArray.Type().ElementType().TypeKind() == llvm.ArrayTypeKind && srcArray.Type().ElementType().TypeKind() == llvm.ArrayTypeKind {
// Slice data pointers are created by adding a global array
// and getting the address of the first element using a GEP.
// However, before the compiler can pass it to
// runtime.sliceCopy, it has to perform a bitcast to a *i8,
// to make it a unsafe.Pointer. Now, when the IR builder
// sees a bitcast of a GEP with zero indices, it will make
// a bitcast of the original array instead of the GEP,
// which breaks our assumptions.
// Re-add this GEP, in the hope that it it is then of the correct type...
dstArrayValue, err := dstArray.GetElementPtr([]uint32{0, 0})
if err != nil {
return nil, nil, fr.errorAt(inst, err)
}
dstArray = dstArrayValue.(*LocalValue)
srcArrayValue, err := srcArray.GetElementPtr([]uint32{0, 0})
if err != nil {
return nil, nil, fr.errorAt(inst, err)
}
srcArray = srcArrayValue.(*LocalValue)
}
if fr.Eval.TargetData.TypeAllocSize(dstArray.Type().ElementType()) != elementSize {
return nil, nil, fr.errorAt(inst, errors.New("interp: slice dst element size does not match pointer type"))
}
if fr.Eval.TargetData.TypeAllocSize(srcArray.Type().ElementType()) != elementSize {
return nil, nil, fr.errorAt(inst, errors.New("interp: slice src element size does not match pointer type"))
}
if dstArray.Type() != srcArray.Type() {
return nil, nil, fr.errorAt(inst, errors.New("interp: slice element types don't match"))
}
length := dstLen.Value().SExtValue()
if srcLength := srcLen.Value().SExtValue(); srcLength < length {
length = srcLength
}
if length < 0 {
return nil, nil, fr.errorAt(inst, errors.New("interp: trying to copy a slice with negative length?"))
}
for i := int64(0); i < length; i++ {
// *dst = *src
val, err := srcArray.Load()
if err != nil {
return nil, nil, fr.errorAt(inst, err)
}
err = dstArray.Store(val)
if err != nil {
return nil, nil, fr.errorAt(inst, err)
}
// dst++
dstArrayValue, err := dstArray.GetElementPtr([]uint32{1})
if err != nil {
return nil, nil, fr.errorAt(inst, err)
}
dstArray = dstArrayValue.(*LocalValue)
// src++
srcArrayValue, err := srcArray.GetElementPtr([]uint32{1})
if err != nil {
return nil, nil, fr.errorAt(inst, err)
}
srcArray = srcArrayValue.(*LocalValue)
}
case callee.Name() == "runtime.stringToBytes":
// convert a string to a []byte
bufPtr := fr.getLocal(inst.Operand(0))
bufLen := fr.getLocal(inst.Operand(1))
result, err := getStringBytes(bufPtr, bufLen.Value())
if err != nil {
return nil, nil, fr.errorAt(inst, err)
}
vals := make([]llvm.Value, len(result))
for i := range vals {
vals[i] = llvm.ConstInt(fr.Mod.Context().Int8Type(), uint64(result[i]), false)
}
globalType := llvm.ArrayType(fr.Mod.Context().Int8Type(), len(result))
globalValue := llvm.ConstArray(fr.Mod.Context().Int8Type(), vals)
global := llvm.AddGlobal(fr.Mod, globalType, fr.packagePath+"$bytes")
global.SetInitializer(globalValue)
global.SetLinkage(llvm.InternalLinkage)
global.SetGlobalConstant(true)
global.SetUnnamedAddr(true)
sliceType := inst.Type()
retPtr := llvm.ConstGEP(global, getLLVMIndices(fr.Mod.Context().Int32Type(), []uint32{0, 0}))
retLen := llvm.ConstInt(sliceType.StructElementTypes()[1], uint64(len(result)), false)
ret := llvm.ConstNull(sliceType)
ret = llvm.ConstInsertValue(ret, retPtr, []uint32{0}) // ptr
ret = llvm.ConstInsertValue(ret, retLen, []uint32{1}) // len
ret = llvm.ConstInsertValue(ret, retLen, []uint32{2}) // cap
fr.locals[inst] = &LocalValue{fr.Eval, ret}
case callee.Name() == "runtime.typeAssert":
actualTypeInt := fr.getLocal(inst.Operand(0)).(*LocalValue).Underlying
assertedType := fr.getLocal(inst.Operand(1)).(*LocalValue).Underlying
if actualTypeInt.IsAConstantExpr().IsNil() || actualTypeInt.Opcode() != llvm.PtrToInt {
return nil, nil, fr.errorAt(inst, errors.New("interp: expected typecode in runtime.typeAssert to be a ptrtoint"))
}
actualType := actualTypeInt.Operand(0)
if actualType.IsAConstant().IsNil() || assertedType.IsAConstant().IsNil() {
return nil, nil, fr.errorAt(inst, errors.New("interp: unimplemented: type assert with non-constant interface value"))
}
assertOk := uint64(0)
if llvm.ConstExtractValue(actualType.Initializer(), []uint32{0}) == assertedType {
assertOk = 1
}
fr.locals[inst] = &LocalValue{fr.Eval, llvm.ConstInt(fr.Mod.Context().Int1Type(), assertOk, false)}
case callee.Name() == "runtime.interfaceImplements":
typecode := fr.getLocal(inst.Operand(0)).(*LocalValue).Underlying
interfaceMethodSet := fr.getLocal(inst.Operand(1)).(*LocalValue).Underlying
if typecode.IsAConstantExpr().IsNil() || typecode.Opcode() != llvm.PtrToInt {
return nil, nil, fr.errorAt(inst, errors.New("interp: expected typecode to be a ptrtoint"))
}
typecode = typecode.Operand(0)
if interfaceMethodSet.IsAConstantExpr().IsNil() || interfaceMethodSet.Opcode() != llvm.GetElementPtr {
return nil, nil, fr.errorAt(inst, errors.New("interp: expected method set in runtime.interfaceImplements to be a constant gep"))
}
interfaceMethodSet = interfaceMethodSet.Operand(0).Initializer()
methodSet := llvm.ConstExtractValue(typecode.Initializer(), []uint32{1})
if methodSet.IsAConstantExpr().IsNil() || methodSet.Opcode() != llvm.GetElementPtr {
return nil, nil, fr.errorAt(inst, errors.New("interp: expected method set to be a constant gep"))
}
methodSet = methodSet.Operand(0).Initializer()
// Make a set of all the methods on the concrete type, for
// easier checking in the next step.
definedMethods := map[string]struct{}{}
for i := 0; i < methodSet.Type().ArrayLength(); i++ {
methodInfo := llvm.ConstExtractValue(methodSet, []uint32{uint32(i)})
name := llvm.ConstExtractValue(methodInfo, []uint32{0}).Name()
definedMethods[name] = struct{}{}
}
// Check whether all interface methods are also in the list
// of defined methods calculated above.
implements := uint64(1) // i1 true
for i := 0; i < interfaceMethodSet.Type().ArrayLength(); i++ {
name := llvm.ConstExtractValue(interfaceMethodSet, []uint32{uint32(i)}).Name()
if _, ok := definedMethods[name]; !ok {
// There is a method on the interface that is not
// implemented by the type.
implements = 0 // i1 false
break
}
}
fr.locals[inst] = &LocalValue{fr.Eval, llvm.ConstInt(fr.Mod.Context().Int1Type(), implements, false)}
case callee.Name() == "runtime.nanotime":
fr.locals[inst] = &LocalValue{fr.Eval, llvm.ConstInt(fr.Mod.Context().Int64Type(), 0, false)}
case callee.Name() == "llvm.dbg.value":
// do nothing
case strings.HasPrefix(callee.Name(), "llvm.lifetime."):
// do nothing
case callee.Name() == "runtime.trackPointer":
// do nothing
case strings.HasPrefix(callee.Name(), "runtime.print") || callee.Name() == "runtime._panic":
// This are all print instructions, which necessarily have side
// effects but no results.
// TODO: print an error when executing runtime._panic (with the
// exact error message it would print at runtime).
var params []llvm.Value
for i := 0; i < inst.OperandsCount()-1; i++ {
operand := fr.getLocal(inst.Operand(i)).Value()
fr.markDirty(operand)
params = append(params, operand)
}
// TODO: accurate debug info, including call chain
fr.builder.CreateCall(callee, params, inst.Name())
case !callee.IsAFunction().IsNil() && callee.IsDeclaration():
// external functions
var params []llvm.Value
for i := 0; i < inst.OperandsCount()-1; i++ {
operand := fr.getLocal(inst.Operand(i)).Value()
fr.markDirty(operand)
params = append(params, operand)
}
// TODO: accurate debug info, including call chain
result := fr.builder.CreateCall(callee, params, inst.Name())
if inst.Type().TypeKind() != llvm.VoidTypeKind {
fr.markDirty(result)
fr.locals[inst] = &LocalValue{fr.Eval, result}
}
case !callee.IsAFunction().IsNil():
// regular function
var params []Value
dirtyParams := false
for i := 0; i < inst.OperandsCount()-1; i++ {
local := fr.getLocal(inst.Operand(i))
if !local.IsConstant() {
dirtyParams = true
}
params = append(params, local)
}
var ret Value
scanResult, err := fr.hasSideEffects(callee)
if err != nil {
return nil, nil, err
}
if scanResult.severity == sideEffectLimited || dirtyParams && scanResult.severity != sideEffectAll {
// Side effect is bounded. This means the operation invokes
// side effects (like calling an external function) but it
// is known at compile time which side effects it invokes.
// This means the function can be called at runtime and the
// affected globals can be marked dirty at compile time.
llvmParams := make([]llvm.Value, len(params))
for i, param := range params {
llvmParams[i] = param.Value()
}
result := fr.builder.CreateCall(callee, llvmParams, inst.Name())
ret = &LocalValue{fr.Eval, result}
// mark all mentioned globals as dirty
for global := range scanResult.mentionsGlobals {
fr.markDirty(global)
}
} else {
// Side effect is one of:
// * None: no side effects, can be fully interpreted at
// compile time.
// * Unbounded: cannot call at runtime so we'll try to
// interpret anyway and hope for the best.
ret, err = fr.function(callee, params, indent+" ")
if err != nil {
// Record this function call in the backtrace.
err.Traceback = append(err.Traceback, ErrorLine{
Pos: getPosition(inst),
Inst: inst,
})
return nil, nil, err
}
}
if inst.Type().TypeKind() != llvm.VoidTypeKind {
fr.locals[inst] = ret
}
default:
// function pointers, etc.
return nil, nil, fr.unsupportedInstructionError(inst)
}
case !inst.IsAExtractValueInst().IsNil():
agg := fr.getLocal(inst.Operand(0)).(*LocalValue) // must be constant
indices := inst.Indices()
if agg.Underlying.IsConstant() {
newValue := llvm.ConstExtractValue(agg.Underlying, indices)
fr.locals[inst] = fr.getValue(newValue)
} else {
if len(indices) != 1 {
return nil, nil, fr.errorAt(inst, errors.New("interp: cannot handle extractvalue with not exactly 1 index"))
}
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateExtractValue(agg.Underlying, int(indices[0]), inst.Name())}
}
case !inst.IsAInsertValueInst().IsNil():
agg := fr.getLocal(inst.Operand(0)).(*LocalValue) // must be constant
val := fr.getLocal(inst.Operand(1))
indices := inst.Indices()
if agg.IsConstant() && val.IsConstant() {
newValue := llvm.ConstInsertValue(agg.Underlying, val.Value(), indices)
fr.locals[inst] = &LocalValue{fr.Eval, newValue}
} else {
if len(indices) != 1 {
return nil, nil, fr.errorAt(inst, errors.New("interp: cannot handle insertvalue with not exactly 1 index"))
}
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateInsertValue(agg.Underlying, val.Value(), int(indices[0]), inst.Name())}
}
case !inst.IsASelectInst().IsNil():
// var result T
// if cond {
// result = x
// } else {
// result = y
// }
// return result
cond := fr.getLocal(inst.Operand(0)).(*LocalValue).Underlying
x := fr.getLocal(inst.Operand(1)).(*LocalValue).Underlying
y := fr.getLocal(inst.Operand(2)).(*LocalValue).Underlying
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateSelect(cond, x, y, "")}
case !inst.IsAReturnInst().IsNil() && inst.OperandsCount() == 0:
return nil, nil, nil // ret void
case !inst.IsAReturnInst().IsNil() && inst.OperandsCount() == 1:
return fr.getLocal(inst.Operand(0)), nil, nil
case !inst.IsABranchInst().IsNil() && inst.OperandsCount() == 3:
// conditional branch (if/then/else)
cond := fr.getLocal(inst.Operand(0)).Value()
if cond.Type() != fr.Mod.Context().Int1Type() {
return nil, nil, fr.errorAt(inst, errors.New("expected an i1 in a branch instruction"))
}
thenBB := inst.Operand(1)
elseBB := inst.Operand(2)
if !cond.IsAInstruction().IsNil() {
return nil, nil, fr.errorAt(inst, errors.New("interp: branch on a non-constant"))
}
if !cond.IsAConstantExpr().IsNil() {
// This may happen when the instruction builder could not
// const-fold some instructions.
return nil, nil, fr.errorAt(inst, errors.New("interp: branch on a non-const-propagated constant expression"))
}
switch cond {
case llvm.ConstInt(fr.Mod.Context().Int1Type(), 0, false): // false
return nil, []llvm.Value{thenBB}, nil // then
case llvm.ConstInt(fr.Mod.Context().Int1Type(), 1, false): // true
return nil, []llvm.Value{elseBB}, nil // else
default:
return nil, nil, fr.errorAt(inst, errors.New("branch was not true or false"))
}
case !inst.IsABranchInst().IsNil() && inst.OperandsCount() == 1:
// unconditional branch (goto)
return nil, []llvm.Value{inst.Operand(0)}, nil
case !inst.IsAUnreachableInst().IsNil():
// Unreachable was reached (e.g. after a call to panic()).
// Report this as an error, as it is not supposed to happen.
// This is a sentinel error value.
return nil, nil, errUnreachable
default:
return nil, nil, fr.unsupportedInstructionError(inst)
}
}
panic("interp: reached end of basic block without terminator")
}
// Get the Value for an operand, which is a constant value of some sort.
func (fr *frame) getLocal(v llvm.Value) Value {
if ret, ok := fr.locals[v]; ok {
return ret
} else if value := fr.getValue(v); value != nil {
return value
} else {
// This should not happen under normal circumstances.
panic("cannot find value")
}
}
+100 -114
View File
@@ -1,59 +1,77 @@
// Package interp interprets Go package initializers as much as possible. This
// avoid running them at runtime, improving code size and making other
// optimizations possible.
// Package interp is a partial evaluator of code run at package init time. See
// the README in this package for details.
package interp
// This file provides the overarching Eval object with associated (utility)
// methods.
import (
"fmt"
"os"
"strings"
"time"
"tinygo.org/x/go-llvm"
)
type Eval struct {
Mod llvm.Module
TargetData llvm.TargetData
Debug bool
builder llvm.Builder
dirtyGlobals map[llvm.Value]struct{}
sideEffectFuncs map[llvm.Value]*sideEffectResult // cache of side effect scan results
// Enable extra checks, which should be disabled by default.
// This may help track down bugs by adding a few more sanity checks.
const checks = true
// runner contains all state related to one interp run.
type runner struct {
mod llvm.Module
targetData llvm.TargetData
builder llvm.Builder
pointerSize uint32 // cached pointer size from the TargetData
i8ptrType llvm.Type // often used type so created in advance
maxAlign int // maximum alignment of an object, alignment of runtime.alloc() result
debug bool // log debug messages
pkgName string // package name of the currently executing package
functionCache map[llvm.Value]*function // cache of compiled functions
objects []object // slice of objects in memory
globals map[llvm.Value]int // map from global to index in objects slice
start time.Time
callsExecuted uint64
}
// evalPackage encapsulates the Eval type for just a single package. The Eval
// type keeps state across the whole program, the evalPackage type keeps extra
// state for the currently interpreted package.
type evalPackage struct {
*Eval
packagePath string
}
// Run evaluates the function with the given name and then eliminates all
// callers.
// Run evaluates runtime.initAll function as much as possible at compile time.
// Set debug to true if it should print output while running.
func Run(mod llvm.Module, debug bool) error {
if debug {
println("\ncompile-time evaluation:")
r := runner{
mod: mod,
targetData: llvm.NewTargetData(mod.DataLayout()),
debug: debug,
functionCache: make(map[llvm.Value]*function),
objects: []object{{}},
globals: make(map[llvm.Value]int),
start: time.Now(),
}
r.pointerSize = uint32(r.targetData.PointerSize())
r.i8ptrType = llvm.PointerType(mod.Context().Int8Type(), 0)
r.maxAlign = r.targetData.PrefTypeAlignment(r.i8ptrType) // assume pointers are maximally aligned (this is not always the case)
name := "runtime.initAll"
e := &Eval{
Mod: mod,
TargetData: llvm.NewTargetData(mod.DataLayout()),
Debug: debug,
dirtyGlobals: map[llvm.Value]struct{}{},
}
e.builder = mod.Context().NewBuilder()
initAll := mod.NamedFunction(name)
initAll := mod.NamedFunction("runtime.initAll")
bb := initAll.EntryBasicBlock()
// Create a builder, to insert instructions that could not be evaluated at
// compile time.
r.builder = mod.Context().NewBuilder()
defer r.builder.Dispose()
// Create a dummy alloca in the entry block that we can set the insert point
// to. This is necessary because otherwise we might be removing the
// instruction (init call) that we are removing after successful
// interpretation.
e.builder.SetInsertPointBefore(bb.FirstInstruction())
dummy := e.builder.CreateAlloca(e.Mod.Context().Int8Type(), "dummy")
e.builder.SetInsertPointBefore(dummy)
r.builder.SetInsertPointBefore(bb.FirstInstruction())
dummy := r.builder.CreateAlloca(r.mod.Context().Int8Type(), "dummy")
r.builder.SetInsertPointBefore(dummy)
defer dummy.EraseFromParentAsInstruction()
// Get a list if init calls. A runtime.initAll might look something like this:
// func initAll() {
// unsafe.init()
// machine.init()
// runtime.init()
// }
// This function gets a list of these call instructions.
var initCalls []llvm.Value
for inst := bb.FirstInstruction(); !inst.IsNil(); inst = llvm.NextInstruction(inst) {
if inst == dummy {
@@ -63,99 +81,67 @@ func Run(mod llvm.Module, debug bool) error {
break // ret void
}
if inst.IsACallInst().IsNil() || inst.CalledValue().IsAFunction().IsNil() {
return errorAt(inst, "interp: expected all instructions in "+name+" to be direct calls")
return errorAt(inst, "interp: expected all instructions in "+initAll.Name()+" to be direct calls")
}
initCalls = append(initCalls, inst)
}
// Do this in a separate step to avoid corrupting the iterator above.
undefPtr := llvm.Undef(llvm.PointerType(mod.Context().Int8Type(), 0))
// Run initializers for each package. Once the package initializer is
// finished, the call to the package initializer can be removed.
for _, call := range initCalls {
initName := call.CalledValue().Name()
if !strings.HasSuffix(initName, ".init") {
return errorAt(call, "interp: expected all instructions in "+name+" to be *.init() calls")
return errorAt(call, "interp: expected all instructions in "+initAll.Name()+" to be *.init() calls")
}
pkgName := initName[:len(initName)-5]
r.pkgName = initName[:len(initName)-len(".init")]
fn := call.CalledValue()
if r.debug {
fmt.Fprintln(os.Stderr, "call:", fn.Name())
}
_, mem, callErr := r.run(r.getFunction(fn), nil, nil, " ")
if callErr != nil {
if isRecoverableError(callErr.Err) {
if r.debug {
fmt.Fprintln(os.Stderr, "not interpretring", r.pkgName, "because of error:", callErr.Err)
}
mem.revert()
continue
}
return callErr
}
call.EraseFromParentAsInstruction()
evalPkg := evalPackage{
Eval: e,
packagePath: pkgName,
for index, obj := range mem.objects {
r.objects[index] = obj
}
_, err := evalPkg.function(fn, []Value{&LocalValue{e, undefPtr}, &LocalValue{e, undefPtr}}, "")
if err == errUnreachable {
break
}
r.pkgName = ""
// Update all global variables in the LLVM module.
mem := memoryView{r: &r}
for _, obj := range r.objects {
if obj.llvmGlobal.IsNil() {
continue
}
if err != nil {
return err
if obj.buffer == nil {
continue
}
initializer := obj.buffer.toLLVMValue(obj.llvmGlobal.Type().ElementType(), &mem)
if checks && initializer.Type() != obj.llvmGlobal.Type().ElementType() {
panic("initializer type mismatch")
}
obj.llvmGlobal.SetInitializer(initializer)
}
return nil
}
// function interprets the given function. The params are the function params
// and the indent is the string indentation to use when dumping all interpreted
// instructions.
func (e *evalPackage) function(fn llvm.Value, params []Value, indent string) (Value, *Error) {
fr := frame{
evalPackage: e,
fn: fn,
locals: make(map[llvm.Value]Value),
}
for i, param := range fn.Params() {
fr.locals[param] = params[i]
}
bb := fn.EntryBasicBlock()
var lastBB llvm.BasicBlock
for {
retval, outgoing, err := fr.evalBasicBlock(bb, lastBB, indent)
if outgoing == nil {
// returned something (a value or void, or an error)
return retval, err
}
if len(outgoing) > 1 {
panic("unimplemented: multiple outgoing blocks")
}
next := outgoing[0]
if next.IsABasicBlock().IsNil() {
panic("did not switch to a basic block")
}
lastBB = bb
bb = next.AsBasicBlock()
}
}
// getValue determines what kind of LLVM value it gets and returns the
// appropriate Value type.
func (e *Eval) getValue(v llvm.Value) Value {
return &LocalValue{e, v}
}
// markDirty marks the passed-in LLVM value dirty, recursively. For example,
// when it encounters a constant GEP on a global, it marks the global dirty.
func (e *Eval) markDirty(v llvm.Value) {
if !v.IsAGlobalVariable().IsNil() {
if v.IsGlobalConstant() {
return
}
if _, ok := e.dirtyGlobals[v]; !ok {
e.dirtyGlobals[v] = struct{}{}
e.sideEffectFuncs = nil // re-calculate all side effects
}
} else if v.IsConstant() {
if v.OperandsCount() >= 2 && !v.Operand(0).IsAGlobalVariable().IsNil() {
// looks like a constant getelementptr of a global.
// TODO: find a way to make sure it really is: v.Opcode() returns 0.
e.markDirty(v.Operand(0))
return
}
return // nothing to mark
} else if !v.IsAGetElementPtrInst().IsNil() {
panic("interp: todo: GEP")
} else {
// Not constant and not a global or GEP so doesn't have to be marked
// non-constant.
// getFunction returns the compiled version of the given LLVM function. It
// compiles the function if necessary and caches the result.
func (r *runner) getFunction(llvmFn llvm.Value) *function {
if fn, ok := r.functionCache[llvmFn]; ok {
return fn
}
fn := r.compileFunction(llvmFn)
r.functionCache[llvmFn] = fn
return fn
}
+35 -2
View File
@@ -3,6 +3,7 @@ package interp
import (
"io/ioutil"
"os"
"regexp"
"strings"
"testing"
@@ -41,9 +42,29 @@ func runTest(t *testing.T, pathPrefix string) {
// Perform the transform.
err = Run(mod, false)
if err != nil {
if err, match := err.(*Error); match {
println(err.Error())
if !err.Inst.IsNil() {
err.Inst.Dump()
println()
}
if len(err.Traceback) > 0 {
println("\ntraceback:")
for _, line := range err.Traceback {
println(line.Pos.String() + ":")
line.Inst.Dump()
println()
}
}
}
t.Fatal(err)
}
// To be sure, verify that the module is still valid.
if llvm.VerifyModule(mod, llvm.PrintMessageAction) != nil {
t.FailNow()
}
// Run some cleanup passes to get easy-to-read outputs.
pm := llvm.NewPassManager()
defer pm.Dispose()
@@ -66,6 +87,8 @@ func runTest(t *testing.T, pathPrefix string) {
}
}
var alignRegexp = regexp.MustCompile(", align [0-9]+$")
// fuzzyEqualIR returns true if the two LLVM IR strings passed in are roughly
// equal. That means, only relevant lines are compared (excluding comments
// etc.).
@@ -75,8 +98,18 @@ func fuzzyEqualIR(s1, s2 string) bool {
if len(lines1) != len(lines2) {
return false
}
for i, line := range lines1 {
if line != lines2[i] {
for i, line1 := range lines1 {
line2 := lines2[i]
match1 := alignRegexp.MatchString(line1)
match2 := alignRegexp.MatchString(line2)
if match1 != match2 {
// Only one of the lines has the align keyword. Remove it.
// This is a change to make the test work in both LLVM 10 and LLVM
// 11 (LLVM 11 appears to automatically add alignment everywhere).
line1 = alignRegexp.ReplaceAllString(line1, "")
line2 = alignRegexp.ReplaceAllString(line2, "")
}
if line1 != line2 {
return false
}
}
+917
View File
@@ -0,0 +1,917 @@
package interp
import (
"errors"
"fmt"
"math"
"os"
"strings"
"time"
"tinygo.org/x/go-llvm"
)
func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent string) (value, memoryView, *Error) {
mem := memoryView{r: r, parent: parentMem}
locals := make([]value, len(fn.locals))
r.callsExecuted++
if time.Since(r.start) > time.Minute {
// Running for more than a minute. This should never happen.
return nil, mem, r.errorAt(fn.blocks[0].instructions[0], fmt.Errorf("interp: running for more than a minute, timing out (executed calls: %d)", r.callsExecuted))
}
// Parameters are considered a kind of local values.
for i, param := range params {
locals[i] = param
}
// Start with the first basic block and the first instruction.
// Branch instructions may modify both bb and instIndex when branching.
bb := fn.blocks[0]
currentBB := 0
lastBB := -1 // last basic block is undefined, only defined after a branch
var operands []value
for instIndex := 0; instIndex < len(bb.instructions); instIndex++ {
inst := bb.instructions[instIndex]
operands = operands[:0]
isRuntimeInst := false
if inst.opcode != llvm.PHI {
for _, v := range inst.operands {
if v, ok := v.(localValue); ok {
if localVal := locals[fn.locals[v.value]]; localVal == nil {
return nil, mem, r.errorAt(inst, errors.New("interp: local not defined"))
} else {
operands = append(operands, localVal)
if _, ok := localVal.(localValue); ok {
isRuntimeInst = true
}
continue
}
}
operands = append(operands, v)
}
}
if isRuntimeInst {
err := r.runAtRuntime(fn, inst, locals, &mem, indent)
if err != nil {
return nil, mem, err
}
continue
}
switch inst.opcode {
case llvm.Ret:
if len(operands) != 0 {
if r.debug {
fmt.Fprintln(os.Stderr, indent+"ret", operands[0])
}
// Return instruction has a value to return.
return operands[0], mem, nil
}
if r.debug {
fmt.Fprintln(os.Stderr, indent+"ret")
}
// Return instruction doesn't return anything, it's just 'ret void'.
return nil, mem, nil
case llvm.Br:
switch len(operands) {
case 1:
// Unconditional branch: [nextBB]
lastBB = currentBB
currentBB = int(operands[0].(literalValue).value.(uint32))
bb = fn.blocks[currentBB]
instIndex = -1 // start at 0 the next cycle
if r.debug {
fmt.Fprintln(os.Stderr, indent+"br", operands, "->", currentBB)
}
case 3:
// Conditional branch: [cond, thenBB, elseBB]
lastBB = currentBB
switch operands[0].Uint() {
case 1: // true -> thenBB
currentBB = int(operands[1].(literalValue).value.(uint32))
case 0: // false -> elseBB
currentBB = int(operands[2].(literalValue).value.(uint32))
default:
panic("bool should be 0 or 1")
}
if r.debug {
fmt.Fprintln(os.Stderr, indent+"br", operands, "->", currentBB)
}
bb = fn.blocks[currentBB]
instIndex = -1 // start at 0 the next cycle
default:
panic("unknown operands length")
}
break // continue with next block
case llvm.PHI:
var result value
for i := 0; i < len(inst.operands); i += 2 {
if int(inst.operands[i].(literalValue).value.(uint32)) == lastBB {
incoming := inst.operands[i+1]
if local, ok := incoming.(localValue); ok {
result = locals[fn.locals[local.value]]
} else {
result = incoming
}
break
}
}
if r.debug {
fmt.Fprintln(os.Stderr, indent+"phi", inst.operands, "->", result)
}
if result == nil {
panic("could not find PHI input")
}
locals[inst.localIndex] = result
case llvm.Select:
// Select is much like a ternary operator: it picks a result from
// the second and third operand based on the boolean first operand.
var result value
switch operands[0].Uint() {
case 1:
result = operands[1]
case 0:
result = operands[2]
default:
panic("boolean must be 0 or 1")
}
locals[inst.localIndex] = result
if r.debug {
fmt.Fprintln(os.Stderr, indent+"select", operands, "->", result)
}
case llvm.Call:
// A call instruction can either be a regular call or a runtime intrinsic.
fnPtr, err := operands[0].asPointer(r)
if err != nil {
return nil, mem, r.errorAt(inst, err)
}
callFn := r.getFunction(fnPtr.llvmValue(&mem))
switch {
case callFn.name == "runtime.trackPointer":
// Allocas and such are created as globals, so don't need a
// runtime.trackPointer.
// Unless the object is allocated at runtime for example, in
// which case this call won't even get to this point but will
// already be emitted in initAll.
continue
case callFn.name == "(reflect.Type).Elem" || strings.HasPrefix(callFn.name, "runtime.print") || callFn.name == "runtime._panic" || callFn.name == "runtime.hashmapGet":
// These functions should be run at runtime. Specifically:
// * (reflect.Type).Elem is a special function. It should
// eventually be interpreted, but fall back to a runtime call
// for now.
// * Print and panic functions are best emitted directly without
// interpreting them, otherwise we get a ton of putchar (etc.)
// calls.
// * runtime.hashmapGet tries to access the map value directly.
// This is not possible as the map value is treated as a special
// kind of object in this package.
err := r.runAtRuntime(fn, inst, locals, &mem, indent)
if err != nil {
return nil, mem, err
}
case callFn.name == "runtime.nanotime" && r.pkgName == "time":
// The time package contains a call to runtime.nanotime.
// This appears to be to work around a limitation in Windows
// Server 2008:
// > Monotonic times are reported as offsets from startNano.
// > We initialize startNano to runtimeNano() - 1 so that on systems where
// > monotonic time resolution is fairly low (e.g. Windows 2008
// > which appears to have a default resolution of 15ms),
// > we avoid ever reporting a monotonic time of 0.
// > (Callers may want to use 0 as "time not set".)
// Simply let runtime.nanotime return 0 in this case, which
// should be fine and avoids a call to runtime.nanotime. It
// means that monotonic time in the time package is counted from
// time.Time{}.Sub(1), which should be fine.
locals[inst.localIndex] = literalValue{uint64(0)}
case callFn.name == "runtime.alloc":
// Allocate heap memory. At compile time, this is instead done
// by creating a global variable.
// Get the requested memory size to be allocated.
size := operands[1].Uint()
// Create the object.
alloc := object{
globalName: r.pkgName + "$alloc",
buffer: newRawValue(uint32(size)),
size: uint32(size),
}
index := len(r.objects)
r.objects = append(r.objects, alloc)
// And create a pointer to this object, for working with it (so
// that stores to it copy it, etc).
ptr := newPointerValue(r, index, 0)
if r.debug {
fmt.Fprintln(os.Stderr, indent+"runtime.alloc:", size, "->", ptr)
}
locals[inst.localIndex] = ptr
case callFn.name == "runtime.sliceCopy":
// sliceCopy implements the built-in copy function for slices.
// It is implemented here so that it can be used even if the
// runtime implementation is not available. Doing it this way
// may also be faster.
// Code:
// func sliceCopy(dst, src unsafe.Pointer, dstLen, srcLen uintptr, elemSize uintptr) int {
// n := srcLen
// if n > dstLen {
// n = dstLen
// }
// memmove(dst, src, n*elemSize)
// return int(n)
// }
dstLen := operands[3].Uint()
srcLen := operands[4].Uint()
elemSize := operands[5].Uint()
n := srcLen
if n > dstLen {
n = dstLen
}
if r.debug {
fmt.Fprintln(os.Stderr, indent+"copy:", operands[1], operands[2], n)
}
if n != 0 {
// Only try to copy bytes when there are any bytes to copy.
// This is not just an optimization. If one of the slices
// (or both) are nil, the asPointer method call will fail
// even though copying a nil slice is allowed.
dst, err := operands[1].asPointer(r)
if err != nil {
return nil, mem, r.errorAt(inst, err)
}
src, err := operands[2].asPointer(r)
if err != nil {
return nil, mem, r.errorAt(inst, err)
}
nBytes := uint32(n * elemSize)
dstObj := mem.getWritable(dst.index())
dstBuf := dstObj.buffer.asRawValue(r)
srcBuf := mem.get(src.index()).buffer.asRawValue(r)
copy(dstBuf.buf[dst.offset():dst.offset()+nBytes], srcBuf.buf[src.offset():])
dstObj.buffer = dstBuf
mem.put(dst.index(), dstObj)
}
switch inst.llvmInst.Type().IntTypeWidth() {
case 16:
locals[inst.localIndex] = literalValue{uint16(n)}
case 32:
locals[inst.localIndex] = literalValue{uint32(n)}
case 64:
locals[inst.localIndex] = literalValue{uint64(n)}
default:
panic("unknown integer type width")
}
case strings.HasPrefix(callFn.name, "llvm.memcpy.p0i8.p0i8.") || strings.HasPrefix(callFn.name, "llvm.memmove.p0i8.p0i8."):
// Copy a block of memory from one pointer to another.
dst, err := operands[1].asPointer(r)
if err != nil {
return nil, mem, r.errorAt(inst, err)
}
src, err := operands[2].asPointer(r)
if err != nil {
return nil, mem, r.errorAt(inst, err)
}
nBytes := uint32(operands[3].Uint())
dstObj := mem.getWritable(dst.index())
dstBuf := dstObj.buffer.asRawValue(r)
srcBuf := mem.get(src.index()).buffer.asRawValue(r)
copy(dstBuf.buf[dst.offset():dst.offset()+nBytes], srcBuf.buf[src.offset():])
dstObj.buffer = dstBuf
mem.put(dst.index(), dstObj)
case callFn.name == "runtime.typeAssert":
// This function must be implemented manually as it is normally
// implemented by the interface lowering pass.
if r.debug {
fmt.Fprintln(os.Stderr, indent+"typeassert:", operands[1:])
}
typeInInterfacePtr, err := operands[1].asPointer(r)
if err != nil {
return nil, mem, r.errorAt(inst, err)
}
actualType, err := mem.load(typeInInterfacePtr, r.pointerSize).asPointer(r)
if err != nil {
return nil, mem, r.errorAt(inst, err)
}
assertedType, err := operands[2].asPointer(r)
if err != nil {
return nil, mem, r.errorAt(inst, err)
}
result := assertedType.asRawValue(r).equal(actualType.asRawValue(r))
if result {
locals[inst.localIndex] = literalValue{uint8(1)}
} else {
locals[inst.localIndex] = literalValue{uint8(0)}
}
case callFn.name == "runtime.interfaceImplements":
if r.debug {
fmt.Fprintln(os.Stderr, indent+"interface assert:", operands[1:])
}
// Load various values for the interface implements check below.
typeInInterfacePtr, err := operands[1].asPointer(r)
if err != nil {
return nil, mem, r.errorAt(inst, err)
}
methodSetPtr, err := mem.load(typeInInterfacePtr.addOffset(r.pointerSize), r.pointerSize).asPointer(r)
if err != nil {
return nil, mem, r.errorAt(inst, err)
}
methodSet := mem.get(methodSetPtr.index()).llvmGlobal.Initializer()
interfaceMethodSetPtr, err := operands[2].asPointer(r)
if err != nil {
return nil, mem, r.errorAt(inst, err)
}
interfaceMethodSet := mem.get(interfaceMethodSetPtr.index()).llvmGlobal.Initializer()
// Make a set of all the methods on the concrete type, for
// easier checking in the next step.
concreteTypeMethods := map[string]struct{}{}
for i := 0; i < methodSet.Type().ArrayLength(); i++ {
methodInfo := llvm.ConstExtractValue(methodSet, []uint32{uint32(i)})
name := llvm.ConstExtractValue(methodInfo, []uint32{0}).Name()
concreteTypeMethods[name] = struct{}{}
}
// Check whether all interface methods are also in the list
// of defined methods calculated above. This is the interface
// assert itself.
assertOk := uint8(1) // i1 true
for i := 0; i < interfaceMethodSet.Type().ArrayLength(); i++ {
name := llvm.ConstExtractValue(interfaceMethodSet, []uint32{uint32(i)}).Name()
if _, ok := concreteTypeMethods[name]; !ok {
// There is a method on the interface that is not
// implemented by the type. The assertion will fail.
assertOk = 0 // i1 false
break
}
}
// If assertOk is still 1, the assertion succeeded.
locals[inst.localIndex] = literalValue{assertOk}
case callFn.name == "runtime.hashmapMake":
// Create a new map.
hashmapPointerType := inst.llvmInst.Type()
keySize := uint32(operands[1].Uint())
valueSize := uint32(operands[2].Uint())
m := newMapValue(r, hashmapPointerType, keySize, valueSize)
alloc := object{
llvmType: hashmapPointerType,
globalName: r.pkgName + "$map",
buffer: m,
size: m.len(r),
}
index := len(r.objects)
r.objects = append(r.objects, alloc)
// Create a pointer to this map. Maps are reference types, so
// are implemented as pointers.
ptr := newPointerValue(r, index, 0)
if r.debug {
fmt.Fprintln(os.Stderr, indent+"runtime.hashmapMake:", keySize, valueSize, "->", ptr)
}
locals[inst.localIndex] = ptr
case callFn.name == "runtime.hashmapBinarySet":
// Do a mapassign operation with a binary key (that is, without
// a string key).
if r.debug {
fmt.Fprintln(os.Stderr, indent+"runtime.hashmapBinarySet:", operands[1:])
}
mapPtr, err := operands[1].asPointer(r)
if err != nil {
return nil, mem, r.errorAt(inst, err)
}
m := mem.getWritable(mapPtr.index()).buffer.(*mapValue)
keyPtr, err := operands[2].asPointer(r)
if err != nil {
return nil, mem, r.errorAt(inst, err)
}
valuePtr, err := operands[3].asPointer(r)
if err != nil {
return nil, mem, r.errorAt(inst, err)
}
err = m.putBinary(&mem, keyPtr, valuePtr)
if err != nil {
return nil, mem, r.errorAt(inst, err)
}
case callFn.name == "runtime.hashmapStringSet":
// Do a mapassign operation with a string key.
if r.debug {
fmt.Fprintln(os.Stderr, indent+"runtime.hashmapBinarySet:", operands[1:])
}
mapPtr, err := operands[1].asPointer(r)
if err != nil {
return nil, mem, r.errorAt(inst, err)
}
m := mem.getWritable(mapPtr.index()).buffer.(*mapValue)
stringPtr, err := operands[2].asPointer(r)
if err != nil {
return nil, mem, r.errorAt(inst, err)
}
stringLen := operands[3].Uint()
valuePtr, err := operands[4].asPointer(r)
if err != nil {
return nil, mem, r.errorAt(inst, err)
}
err = m.putString(&mem, stringPtr, stringLen, valuePtr)
if err != nil {
return nil, mem, r.errorAt(inst, err)
}
default:
if len(callFn.blocks) == 0 {
// Call to a function declaration without a definition
// available.
err := r.runAtRuntime(fn, inst, locals, &mem, indent)
if err != nil {
return nil, mem, err
}
continue
}
// Call a function with a definition available. Run it as usual,
// possibly trying to recover from it if it failed to execute.
if r.debug {
argStrings := make([]string, len(operands)-1)
for i := range argStrings {
argStrings[i] = operands[i+1].String()
}
fmt.Fprintln(os.Stderr, indent+"call:", callFn.name+"("+strings.Join(argStrings, ", ")+")")
}
retval, callMem, callErr := r.run(callFn, operands[1:], &mem, indent+" ")
if callErr != nil {
if isRecoverableError(callErr.Err) {
// This error can be recovered by doing the call at
// runtime instead of at compile time. But we need to
// revert any changes made by the call first.
if r.debug {
fmt.Fprintln(os.Stderr, indent+"!! revert because of error:", callErr.Err)
}
callMem.revert()
err := r.runAtRuntime(fn, inst, locals, &mem, indent)
if err != nil {
return nil, mem, err
}
continue
}
// Add to the traceback, so that error handling code can see
// how this function got called.
callErr.Traceback = append(callErr.Traceback, ErrorLine{
Pos: getPosition(inst.llvmInst),
Inst: inst.llvmInst,
})
return nil, mem, callErr
}
locals[inst.localIndex] = retval
mem.extend(callMem)
}
case llvm.Load:
// Load instruction, loading some data from the topmost memory view.
ptr, err := operands[0].asPointer(r)
if err != nil {
return nil, mem, r.errorAt(inst, err)
}
size := operands[1].(literalValue).value.(uint64)
if mem.hasExternalStore(ptr) {
// If there could be an external store (for example, because a
// pointer to the object was passed to a function that could not
// be interpreted at compile time) then the load must be done at
// runtime.
err := r.runAtRuntime(fn, inst, locals, &mem, indent)
if err != nil {
return nil, mem, err
}
continue
}
result := mem.load(ptr, uint32(size))
if r.debug {
fmt.Fprintln(os.Stderr, indent+"load:", ptr, "->", result)
}
locals[inst.localIndex] = result
case llvm.Store:
// Store instruction. Create a new object in the memory view and
// store to that, to make it possible to roll back this store.
ptr, err := operands[1].asPointer(r)
if err != nil {
return nil, mem, r.errorAt(inst, err)
}
if mem.hasExternalLoadOrStore(ptr) {
err := r.runAtRuntime(fn, inst, locals, &mem, indent)
if err != nil {
return nil, mem, err
}
continue
}
val := operands[0]
if r.debug {
fmt.Fprintln(os.Stderr, indent+"store:", val, ptr)
}
mem.store(val, ptr)
case llvm.Alloca:
// Alloca normally allocates some stack memory. In the interpreter,
// it allocates a global instead.
// This can likely be optimized, as all it really needs is an alloca
// in the initAll function and creating a global is wasteful for
// this purpose.
// Create the new object.
size := operands[0].(literalValue).value.(uint64)
alloca := object{
llvmType: inst.llvmInst.Type(),
globalName: r.pkgName + "$alloca",
buffer: newRawValue(uint32(size)),
size: uint32(size),
}
index := len(r.objects)
r.objects = append(r.objects, alloca)
// Create a pointer to this object (an alloca produces a pointer).
ptr := newPointerValue(r, index, 0)
if r.debug {
fmt.Fprintln(os.Stderr, indent+"alloca:", operands, "->", ptr)
}
locals[inst.localIndex] = ptr
case llvm.GetElementPtr:
// GetElementPtr does pointer arithmetic, changing the offset of the
// pointer into the underlying object.
var offset uint64
var gepOperands []uint64
for i := 2; i < len(operands); i += 2 {
index := operands[i].Uint()
elementSize := operands[i+1].Uint()
if int64(elementSize) < 0 {
// This is a struct field.
// The field number is encoded by flipping all the bits.
gepOperands = append(gepOperands, ^elementSize)
offset += index
} else {
// This is a normal GEP, probably an array index.
gepOperands = append(gepOperands, index)
offset += elementSize * index
}
}
ptr, err := operands[0].asPointer(r)
if err != nil {
return nil, mem, r.errorAt(inst, err)
}
ptr = ptr.addOffset(uint32(offset))
locals[inst.localIndex] = ptr
if r.debug {
fmt.Fprintln(os.Stderr, indent+"gep:", operands, "->", ptr)
}
case llvm.BitCast, llvm.IntToPtr, llvm.PtrToInt:
// Various bitcast-like instructions that all keep the same bits
// while changing the LLVM type.
// Because interp doesn't preserve the type, these operations are
// identity operations.
if r.debug {
fmt.Fprintln(os.Stderr, indent+instructionNameMap[inst.opcode]+":", operands[0])
}
locals[inst.localIndex] = operands[0]
case llvm.ExtractValue:
agg := operands[0].asRawValue(r)
offset := operands[1].(literalValue).value.(uint64)
size := operands[2].(literalValue).value.(uint64)
elt := rawValue{
buf: agg.buf[offset : offset+size],
}
if r.debug {
fmt.Fprintln(os.Stderr, indent+"extractvalue:", operands, "->", elt)
}
locals[inst.localIndex] = elt
case llvm.InsertValue:
agg := operands[0].asRawValue(r)
elt := operands[1].asRawValue(r)
offset := int(operands[2].(literalValue).value.(uint64))
newagg := newRawValue(uint32(len(agg.buf)))
copy(newagg.buf, agg.buf)
copy(newagg.buf[offset:], elt.buf)
if r.debug {
fmt.Fprintln(os.Stderr, indent+"insertvalue:", operands, "->", newagg)
}
locals[inst.localIndex] = newagg
case llvm.ICmp:
predicate := llvm.IntPredicate(operands[2].(literalValue).value.(uint8))
var result bool
lhs := operands[0]
rhs := operands[1]
switch predicate {
case llvm.IntEQ, llvm.IntNE:
lhsPointer, lhsErr := lhs.asPointer(r)
rhsPointer, rhsErr := rhs.asPointer(r)
if (lhsErr == nil) != (rhsErr == nil) {
// Fast path: only one is a pointer, so they can't be equal.
result = false
} else if lhsErr == nil {
// Both must be nil, so both are pointers.
// Compare them directly.
result = lhsPointer.equal(rhsPointer)
} else {
// Fall back to generic comparison.
result = lhs.asRawValue(r).equal(rhs.asRawValue(r))
}
if predicate == llvm.IntNE {
result = !result
}
case llvm.IntUGT:
result = lhs.Uint() > rhs.Uint()
case llvm.IntUGE:
result = lhs.Uint() >= rhs.Uint()
case llvm.IntULT:
result = lhs.Uint() < rhs.Uint()
case llvm.IntULE:
result = lhs.Uint() <= rhs.Uint()
case llvm.IntSGT:
result = lhs.Int() > rhs.Int()
case llvm.IntSGE:
result = lhs.Int() >= rhs.Int()
case llvm.IntSLT:
result = lhs.Int() < rhs.Int()
case llvm.IntSLE:
result = lhs.Int() <= rhs.Int()
default:
return nil, mem, r.errorAt(inst, errors.New("interp: unsupported icmp"))
}
if result {
locals[inst.localIndex] = literalValue{uint8(1)}
} else {
locals[inst.localIndex] = literalValue{uint8(0)}
}
if r.debug {
fmt.Fprintln(os.Stderr, indent+"icmp:", operands[0], intPredicateString(predicate), operands[1], "->", result)
}
case llvm.FCmp:
predicate := llvm.FloatPredicate(operands[2].(literalValue).value.(uint8))
var result bool
var lhs, rhs float64
switch operands[0].len(r) {
case 8:
lhs = math.Float64frombits(operands[0].Uint())
rhs = math.Float64frombits(operands[1].Uint())
case 4:
lhs = float64(math.Float32frombits(uint32(operands[0].Uint())))
rhs = float64(math.Float32frombits(uint32(operands[1].Uint())))
default:
panic("unknown float type")
}
switch predicate {
case llvm.FloatOEQ:
result = lhs == rhs
case llvm.FloatUNE:
result = lhs != rhs
case llvm.FloatOGT:
result = lhs > rhs
case llvm.FloatOGE:
result = lhs >= rhs
case llvm.FloatOLT:
result = lhs < rhs
case llvm.FloatOLE:
result = lhs <= rhs
default:
return nil, mem, r.errorAt(inst, errors.New("interp: unsupported fcmp"))
}
if result {
locals[inst.localIndex] = literalValue{uint8(1)}
} else {
locals[inst.localIndex] = literalValue{uint8(0)}
}
if r.debug {
fmt.Fprintln(os.Stderr, indent+"fcmp:", operands[0], predicate, operands[1], "->", result)
}
case llvm.Add, llvm.Sub, llvm.Mul, llvm.UDiv, llvm.SDiv, llvm.URem, llvm.SRem, llvm.Shl, llvm.LShr, llvm.AShr, llvm.And, llvm.Or, llvm.Xor:
// Integer binary operations.
lhs := operands[0]
rhs := operands[1]
lhsPtr, err := lhs.asPointer(r)
if err == nil {
// The lhs is a pointer. This sometimes happens for particular
// pointer tricks.
switch inst.opcode {
case llvm.Add:
// This likely means this is part of a
// unsafe.Pointer(uintptr(ptr) + offset) pattern.
lhsPtr = lhsPtr.addOffset(uint32(rhs.Uint()))
locals[inst.localIndex] = lhsPtr
continue
case llvm.Xor:
if rhs.Uint() == 0 {
// Special workaround for strings.noescape, see
// src/strings/builder.go in the Go source tree. This is
// the identity operator, so we can return the input.
locals[inst.localIndex] = lhs
continue
}
default:
// Catch-all for weird operations that should just be done
// at runtime.
err := r.runAtRuntime(fn, inst, locals, &mem, indent)
if err != nil {
return nil, mem, err
}
continue
}
}
var result uint64
switch inst.opcode {
case llvm.Add:
result = lhs.Uint() + rhs.Uint()
case llvm.Sub:
result = lhs.Uint() - rhs.Uint()
case llvm.Mul:
result = lhs.Uint() * rhs.Uint()
case llvm.UDiv:
result = lhs.Uint() / rhs.Uint()
case llvm.SDiv:
result = uint64(lhs.Int() / rhs.Int())
case llvm.URem:
result = lhs.Uint() % rhs.Uint()
case llvm.SRem:
result = uint64(lhs.Int() % rhs.Int())
case llvm.Shl:
result = lhs.Uint() << rhs.Uint()
case llvm.LShr:
result = lhs.Uint() >> rhs.Uint()
case llvm.AShr:
result = uint64(lhs.Int() >> rhs.Uint())
case llvm.And:
result = lhs.Uint() & rhs.Uint()
case llvm.Or:
result = lhs.Uint() | rhs.Uint()
case llvm.Xor:
result = lhs.Uint() ^ rhs.Uint()
default:
panic("unreachable")
}
switch lhs.len(r) {
case 8:
locals[inst.localIndex] = literalValue{result}
case 4:
locals[inst.localIndex] = literalValue{uint32(result)}
case 2:
locals[inst.localIndex] = literalValue{uint16(result)}
case 1:
locals[inst.localIndex] = literalValue{uint8(result)}
default:
panic("unknown integer size")
}
if r.debug {
fmt.Fprintln(os.Stderr, indent+instructionNameMap[inst.opcode]+":", lhs, rhs, "->", result)
}
case llvm.SExt, llvm.ZExt, llvm.Trunc:
// Change the size of an integer to a larger or smaller bit width.
// We make use of the fact that the Uint() function already
// zero-extends the value and that Int() already sign-extends the
// value, so we only need to truncate it to the appropriate bit
// width. This means we can implement sext, zext and trunc in the
// same way, by first {zero,sign}extending all the way up to uint64
// and then truncating it as necessary.
var value uint64
if inst.opcode == llvm.SExt {
value = uint64(operands[0].Int())
} else {
value = operands[0].Uint()
}
bitwidth := operands[1].Uint()
if r.debug {
fmt.Fprintln(os.Stderr, indent+instructionNameMap[inst.opcode]+":", value, bitwidth)
}
switch bitwidth {
case 64:
locals[inst.localIndex] = literalValue{value}
case 32:
locals[inst.localIndex] = literalValue{uint32(value)}
case 16:
locals[inst.localIndex] = literalValue{uint16(value)}
case 8:
locals[inst.localIndex] = literalValue{uint8(value)}
default:
panic("unknown integer size in sext/zext/trunc")
}
case llvm.SIToFP, llvm.UIToFP:
var value float64
switch inst.opcode {
case llvm.SIToFP:
value = float64(operands[0].Int())
case llvm.UIToFP:
value = float64(operands[0].Uint())
}
bitwidth := operands[1].Uint()
if r.debug {
fmt.Fprintln(os.Stderr, indent+instructionNameMap[inst.opcode]+":", value, bitwidth)
}
switch bitwidth {
case 64:
locals[inst.localIndex] = literalValue{math.Float64bits(value)}
case 32:
locals[inst.localIndex] = literalValue{math.Float32bits(float32(value))}
default:
panic("unknown integer size in sitofp/uitofp")
}
default:
if r.debug {
fmt.Fprintln(os.Stderr, indent+inst.String())
}
return nil, mem, r.errorAt(inst, errUnsupportedInst)
}
}
return nil, mem, r.errorAt(bb.instructions[len(bb.instructions)-1], errors.New("interp: reached end of basic block without terminator"))
}
func (r *runner) runAtRuntime(fn *function, inst instruction, locals []value, mem *memoryView, indent string) *Error {
numOperands := inst.llvmInst.OperandsCount()
operands := make([]llvm.Value, numOperands)
for i := 0; i < numOperands; i++ {
operand := inst.llvmInst.Operand(i)
if !operand.IsAInstruction().IsNil() || !operand.IsAArgument().IsNil() {
operand = locals[fn.locals[operand]].toLLVMValue(operand.Type(), mem)
}
operands[i] = operand
}
if r.debug {
fmt.Fprintln(os.Stderr, indent+inst.String())
}
var result llvm.Value
switch inst.opcode {
case llvm.Call:
llvmFn := operands[len(operands)-1]
args := operands[:len(operands)-1]
for _, arg := range args {
if arg.Type().TypeKind() == llvm.PointerTypeKind {
mem.markExternalStore(arg)
}
}
result = r.builder.CreateCall(llvmFn, args, inst.name)
case llvm.Load:
mem.markExternalLoad(operands[0])
result = r.builder.CreateLoad(operands[0], inst.name)
if inst.llvmInst.IsVolatile() {
result.SetVolatile(true)
}
case llvm.Store:
mem.markExternalStore(operands[1])
result = r.builder.CreateStore(operands[0], operands[1])
if inst.llvmInst.IsVolatile() {
result.SetVolatile(true)
}
case llvm.BitCast:
result = r.builder.CreateBitCast(operands[0], inst.llvmInst.Type(), inst.name)
case llvm.ExtractValue:
indices := inst.llvmInst.Indices()
if len(indices) != 1 {
panic("expected exactly one index")
}
result = r.builder.CreateExtractValue(operands[0], int(indices[0]), inst.name)
case llvm.InsertValue:
indices := inst.llvmInst.Indices()
if len(indices) != 1 {
panic("expected exactly one index")
}
result = r.builder.CreateInsertValue(operands[0], operands[1], int(indices[0]), inst.name)
case llvm.Add:
result = r.builder.CreateAdd(operands[0], operands[1], inst.name)
case llvm.Sub:
result = r.builder.CreateSub(operands[0], operands[1], inst.name)
case llvm.Mul:
result = r.builder.CreateMul(operands[0], operands[1], inst.name)
case llvm.UDiv:
result = r.builder.CreateUDiv(operands[0], operands[1], inst.name)
case llvm.SDiv:
result = r.builder.CreateSDiv(operands[0], operands[1], inst.name)
case llvm.URem:
result = r.builder.CreateURem(operands[0], operands[1], inst.name)
case llvm.SRem:
result = r.builder.CreateSRem(operands[0], operands[1], inst.name)
case llvm.ZExt:
result = r.builder.CreateZExt(operands[0], inst.llvmInst.Type(), inst.name)
default:
return r.errorAt(inst, errUnsupportedRuntimeInst)
}
locals[inst.localIndex] = localValue{result}
mem.instructions = append(mem.instructions, result)
return nil
}
func intPredicateString(predicate llvm.IntPredicate) string {
switch predicate {
case llvm.IntEQ:
return "eq"
case llvm.IntNE:
return "ne"
case llvm.IntUGT:
return "ugt"
case llvm.IntUGE:
return "uge"
case llvm.IntULT:
return "ult"
case llvm.IntULE:
return "ule"
case llvm.IntSGT:
return "sgt"
case llvm.IntSGE:
return "sge"
case llvm.IntSLT:
return "slt"
case llvm.IntSLE:
return "sle"
default:
return "cmp?"
}
}
+1430
View File
File diff suppressed because it is too large Load Diff
-259
View File
@@ -1,259 +0,0 @@
package interp
import (
"errors"
"strings"
"tinygo.org/x/go-llvm"
)
type sideEffectSeverity int
func (severity sideEffectSeverity) String() string {
switch severity {
case sideEffectInProgress:
return "in progress"
case sideEffectNone:
return "none"
case sideEffectLimited:
return "limited"
case sideEffectAll:
return "all"
default:
return "unknown"
}
}
const (
sideEffectInProgress sideEffectSeverity = iota // computing side effects is in progress (for recursive functions)
sideEffectNone // no side effects at all (pure)
sideEffectLimited // has side effects, but the effects are known
sideEffectAll // has unknown side effects
)
// sideEffectResult contains the scan results after scanning a function for side
// effects (recursively).
type sideEffectResult struct {
severity sideEffectSeverity
mentionsGlobals map[llvm.Value]struct{}
}
// hasSideEffects scans this function and all descendants, recursively. It
// returns whether this function has side effects and if it does, which globals
// it mentions anywhere in this function or any called functions.
func (e *evalPackage) hasSideEffects(fn llvm.Value) (*sideEffectResult, *Error) {
name := fn.Name()
switch {
case name == "runtime.alloc":
// Cannot be scanned but can be interpreted.
return &sideEffectResult{severity: sideEffectNone}, nil
case name == "runtime.nanotime":
// Fixed value at compile time.
return &sideEffectResult{severity: sideEffectNone}, nil
case name == "runtime._panic":
return &sideEffectResult{severity: sideEffectLimited}, nil
case name == "runtime.typeAssert":
return &sideEffectResult{severity: sideEffectNone}, nil
case name == "runtime.interfaceImplements":
return &sideEffectResult{severity: sideEffectNone}, nil
case name == "runtime.sliceCopy":
return &sideEffectResult{severity: sideEffectNone}, nil
case name == "runtime.trackPointer":
return &sideEffectResult{severity: sideEffectNone}, nil
case name == "llvm.dbg.value":
return &sideEffectResult{severity: sideEffectNone}, nil
case name == "(*sync/atomic.Value).Load" || name == "(*sync/atomic.Value).Store":
// These functions do some unsafe pointer loading/storing but are
// otherwise safe.
return &sideEffectResult{severity: sideEffectLimited}, nil
case strings.HasPrefix(name, "llvm.lifetime."):
return &sideEffectResult{severity: sideEffectNone}, nil
}
if fn.IsDeclaration() {
return &sideEffectResult{severity: sideEffectLimited}, nil
}
if e.sideEffectFuncs == nil {
e.sideEffectFuncs = make(map[llvm.Value]*sideEffectResult)
}
if se, ok := e.sideEffectFuncs[fn]; ok {
return se, nil
}
result := &sideEffectResult{
severity: sideEffectInProgress,
mentionsGlobals: map[llvm.Value]struct{}{},
}
e.sideEffectFuncs[fn] = result
dirtyLocals := map[llvm.Value]struct{}{}
for bb := fn.EntryBasicBlock(); !bb.IsNil(); bb = llvm.NextBasicBlock(bb) {
for inst := bb.FirstInstruction(); !inst.IsNil(); inst = llvm.NextInstruction(inst) {
if inst.IsAInstruction().IsNil() {
// Should not happen in valid IR.
panic("not an instruction")
}
// Check for any globals mentioned anywhere in the function. Assume
// any mentioned globals may be read from or written to when
// executed, thus must be marked dirty with a call.
for i := 0; i < inst.OperandsCount(); i++ {
operand := inst.Operand(i)
if !operand.IsAGlobalVariable().IsNil() {
result.mentionsGlobals[operand] = struct{}{}
}
}
switch inst.InstructionOpcode() {
case llvm.IndirectBr, llvm.Invoke:
// Not emitted by the compiler.
return nil, e.errorAt(inst, errors.New("unknown instructions"))
case llvm.Call:
child := inst.CalledValue()
if !child.IsAInlineAsm().IsNil() {
// Inline assembly. This most likely has side effects.
// Assume they're only limited side effects, similar to
// external function calls.
result.updateSeverity(sideEffectLimited)
continue
}
if child.IsAFunction().IsNil() {
// Indirect call?
// In any case, we can't know anything here about what it
// affects exactly so mark this function as invoking all
// possible side effects.
result.updateSeverity(sideEffectAll)
continue
}
if child.IsDeclaration() {
// External function call. Assume only limited side effects
// (no affected globals, etc.).
switch child.Name() {
case "runtime.alloc":
continue
case "runtime.typeAssert":
continue // implemented in interp
case "runtime.interfaceImplements":
continue // implemented in interp
}
if e.hasLocalSideEffects(dirtyLocals, inst) {
result.updateSeverity(sideEffectLimited)
}
continue
}
childSideEffects, err := e.hasSideEffects(child)
if err != nil {
return nil, err
}
switch childSideEffects.severity {
case sideEffectInProgress, sideEffectNone:
// no side effects or recursive function - continue scanning
case sideEffectLimited:
// The return value may be problematic.
if e.hasLocalSideEffects(dirtyLocals, inst) {
result.updateSeverity(sideEffectLimited)
}
case sideEffectAll:
result.updateSeverity(sideEffectAll)
default:
panic("unreachable")
}
case llvm.Load:
if inst.IsVolatile() {
result.updateSeverity(sideEffectLimited)
}
if _, ok := e.dirtyGlobals[inst.Operand(0)]; ok {
if e.hasLocalSideEffects(dirtyLocals, inst) {
result.updateSeverity(sideEffectLimited)
}
}
case llvm.Store:
if inst.IsVolatile() {
result.updateSeverity(sideEffectLimited)
}
case llvm.IntToPtr:
// Pointer casts are not yet supported.
result.updateSeverity(sideEffectLimited)
default:
// Ignore most instructions.
// Check this list for completeness:
// https://godoc.org/github.com/llvm-mirror/llvm/bindings/go/llvm#Opcode
}
}
}
if result.severity == sideEffectInProgress {
// No side effect was reported for this function.
result.severity = sideEffectNone
}
return result, nil
}
// hasLocalSideEffects checks whether the given instruction flows into a branch
// or return instruction, in which case the whole function must be marked as
// having side effects and be called at runtime.
func (e *Eval) hasLocalSideEffects(dirtyLocals map[llvm.Value]struct{}, inst llvm.Value) bool {
if _, ok := dirtyLocals[inst]; ok {
// It is already known that this local is dirty.
return true
}
for use := inst.FirstUse(); !use.IsNil(); use = use.NextUse() {
user := use.User()
if user.IsAInstruction().IsNil() {
// Should not happen in valid IR.
panic("user not an instruction")
}
switch user.InstructionOpcode() {
case llvm.Br, llvm.Switch:
// A branch on a dirty value makes this function dirty: it cannot be
// interpreted at compile time so has to be run at runtime. It is
// marked as having side effects for this reason.
return true
case llvm.Ret:
// This function returns a dirty value so it is itself marked as
// dirty to make sure it is called at runtime.
return true
case llvm.Store:
ptr := user.Operand(1)
if !ptr.IsAGlobalVariable().IsNil() {
// Store to a global variable.
// Already handled in (*Eval).hasSideEffects.
continue
}
// This store might affect all kinds of values. While it is
// certainly possible to traverse through all of them, the easiest
// option right now is to just assume the worst and say that this
// function has side effects.
// TODO: traverse through all stores and mark all relevant allocas /
// globals dirty.
return true
default:
// All instructions that take 0 or more operands (1 or more if it
// was a use) and produce a result.
// For a list:
// https://godoc.org/github.com/llvm-mirror/llvm/bindings/go/llvm#Opcode
dirtyLocals[user] = struct{}{}
if e.hasLocalSideEffects(dirtyLocals, user) {
return true
}
}
}
// No side effects found.
return false
}
// updateSeverity sets r.severity to the max of r.severity and severity,
// conservatively assuming the worst severity.
func (r *sideEffectResult) updateSeverity(severity sideEffectSeverity) {
if severity > r.severity {
r.severity = severity
}
}
// updateSeverity updates the severity with the severity of the child severity,
// like in a function call. This means it also copies the mentioned globals.
func (r *sideEffectResult) update(child *sideEffectResult) {
r.updateSeverity(child.severity)
for global := range child.mentionsGlobals {
r.mentionsGlobals[global] = struct{}{}
}
}
-95
View File
@@ -1,95 +0,0 @@
package interp
import (
"os"
"sort"
"testing"
"tinygo.org/x/go-llvm"
)
var scanTestTable = []struct {
name string
severity sideEffectSeverity
mentionsGlobals []string
}{
{"returnsConst", sideEffectNone, nil},
{"returnsArg", sideEffectNone, nil},
{"externalCallOnly", sideEffectNone, nil},
{"externalCallAndReturn", sideEffectLimited, nil},
{"externalCallBranch", sideEffectLimited, nil},
{"readCleanGlobal", sideEffectNone, []string{"cleanGlobalInt"}},
{"readDirtyGlobal", sideEffectLimited, []string{"dirtyGlobalInt"}},
{"callFunctionPointer", sideEffectAll, []string{"functionPointer"}},
{"getDirtyPointer", sideEffectLimited, nil},
{"storeToPointer", sideEffectLimited, nil},
{"callTypeAssert", sideEffectNone, nil},
{"callInterfaceImplements", sideEffectNone, nil},
}
func TestScan(t *testing.T) {
t.Parallel()
// Read the input IR.
path := "testdata/scan.ll"
ctx := llvm.NewContext()
buf, err := llvm.NewMemoryBufferFromFile(path)
os.Stat(path) // make sure this file is tracked by `go test` caching
if err != nil {
t.Fatalf("could not read file %s: %v", path, err)
}
mod, err := ctx.ParseIR(buf)
if err != nil {
t.Fatalf("could not load module:\n%v", err)
}
// Check all to-be-tested functions.
for _, tc := range scanTestTable {
// Create an eval object, for testing.
e := &Eval{
Mod: mod,
TargetData: llvm.NewTargetData(mod.DataLayout()),
dirtyGlobals: map[llvm.Value]struct{}{},
}
// Mark some globals dirty, for testing.
e.markDirty(mod.NamedGlobal("dirtyGlobalInt"))
// Scan for side effects.
fn := mod.NamedFunction(tc.name)
if fn.IsNil() {
t.Errorf("scan test: could not find tested function %s in the IR", tc.name)
continue
}
evalPkg := &evalPackage{e, "testdata"}
result, err := evalPkg.hasSideEffects(fn)
if err != nil {
t.Errorf("scan test: failed to scan %s for side effects: %v", fn.Name(), err)
}
// Check whether the result is what we expect.
if result.severity != tc.severity {
t.Errorf("scan test: function %s should have severity %s but it has %s", tc.name, tc.severity, result.severity)
}
// Check whether the mentioned globals match with what we'd expect.
mentionsGlobalNames := make([]string, 0, len(result.mentionsGlobals))
for global := range result.mentionsGlobals {
mentionsGlobalNames = append(mentionsGlobalNames, global.Name())
}
sort.Strings(mentionsGlobalNames)
globalsMismatch := false
if len(result.mentionsGlobals) != len(tc.mentionsGlobals) {
globalsMismatch = true
} else {
for i, globalName := range mentionsGlobalNames {
if tc.mentionsGlobals[i] != globalName {
globalsMismatch = true
}
}
}
if globalsMismatch {
t.Errorf("scan test: expected %s to mention globals %v, but it mentions globals %v", tc.name, tc.mentionsGlobals, mentionsGlobalNames)
}
}
}
+29
View File
@@ -4,6 +4,10 @@ target triple = "x86_64--linux"
@main.v1 = internal global i64 0
@main.nonConst1 = global [4 x i64] zeroinitializer
@main.nonConst2 = global i64 0
@main.someArray = global [8 x {i16, i32}] zeroinitializer
@main.exportedValue = global [1 x i16*] [i16* @main.exposedValue1]
@main.exposedValue1 = global i16 0
@main.exposedValue2 = global i16 0
declare void @runtime.printint64(i64) unnamed_addr
@@ -47,6 +51,20 @@ entry:
%value2 = load i64, i64* %gep2
store i64 %value2, i64* @main.nonConst2
; Test that the following GEP works:
; var someArray
; modifyExternal(&someArray[3].field1)
%gep3 = getelementptr [8 x {i16, i32}], [8 x {i16, i32}]* @main.someArray, i32 0, i32 3, i32 1
call void @modifyExternal(i32* %gep3)
; Test that marking a value as external also marks all referenced values.
call void @modifyExternal(i32* bitcast ([1 x i16*]* @main.exportedValue to i32*))
store i16 5, i16* @main.exposedValue1
; Test that this even propagates through functions.
call void @modifyExternal(i32* bitcast (void ()* @willModifyGlobal to i32*))
store i16 7, i16* @main.exposedValue2
ret void
}
@@ -58,3 +76,14 @@ entry:
}
declare i64 @someValue()
declare void @modifyExternal(i32*)
; This function will modify an external value. By passing this function as a
; function pointer to an external function, @main.exposedValue2 should be
; marked as external.
define void @willModifyGlobal() {
entry:
store i16 8, i16* @main.exposedValue2
ret void
}
+17
View File
@@ -3,6 +3,10 @@ target triple = "x86_64--linux"
@main.nonConst1 = local_unnamed_addr global [4 x i64] zeroinitializer
@main.nonConst2 = local_unnamed_addr global i64 0
@main.someArray = global [8 x { i16, i32 }] zeroinitializer
@main.exportedValue = global [1 x i16*] [i16* @main.exposedValue1]
@main.exposedValue1 = global i16 0
@main.exposedValue2 = local_unnamed_addr global i16 0
declare void @runtime.printint64(i64) unnamed_addr
@@ -16,6 +20,11 @@ entry:
store i64 %value1, i64* getelementptr inbounds ([4 x i64], [4 x i64]* @main.nonConst1, i32 0, i32 0)
%value2 = load i64, i64* getelementptr inbounds ([4 x i64], [4 x i64]* @main.nonConst1, i32 0, i32 0)
store i64 %value2, i64* @main.nonConst2
call void @modifyExternal(i32* getelementptr inbounds ([8 x { i16, i32 }], [8 x { i16, i32 }]* @main.someArray, i32 0, i32 3, i32 1))
call void @modifyExternal(i32* bitcast ([1 x i16*]* @main.exportedValue to i32*))
store i16 5, i16* @main.exposedValue1
call void @modifyExternal(i32* bitcast (void ()* @willModifyGlobal to i32*))
store i16 7, i16* @main.exposedValue2
ret void
}
@@ -27,3 +36,11 @@ entry:
}
declare i64 @someValue() local_unnamed_addr
declare void @modifyExternal(i32*) local_unnamed_addr
define void @willModifyGlobal() {
entry:
store i16 8, i16* @main.exposedValue2
ret void
}
+2 -4
View File
@@ -48,8 +48,7 @@ entry:
define internal void @main.testNonConstantBinarySet() {
%hashmap.key = alloca i8
%hashmap.value = alloca i8
; Create hashmap from global. This breaks the normal hashmapBinarySet
; optimization, to test the fallback.
; Create hashmap from global.
%map.new = call %runtime.hashmap* @runtime.hashmapMake(i8 1, i8 1, i32 1, i8* undef, i8* null)
store %runtime.hashmap* %map.new, %runtime.hashmap** @main.binaryMap
%map = load %runtime.hashmap*, %runtime.hashmap** @main.binaryMap
@@ -64,8 +63,7 @@ define internal void @main.testNonConstantBinarySet() {
; operations (with string keys).
define internal void @main.testNonConstantStringSet() {
%hashmap.value = alloca i8
; Create hashmap from global. This breaks the normal hashmapStringSet
; optimization, to test the fallback.
; Create hashmap from global.
%map.new = call %runtime.hashmap* @runtime.hashmapMake(i8 8, i8 1, i32 1, i8* undef, i8* null)
store %runtime.hashmap* %map.new, %runtime.hashmap** @main.stringMap
%map = load %runtime.hashmap*, %runtime.hashmap** @main.stringMap
+8 -16
View File
@@ -2,27 +2,19 @@ target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
target triple = "armv6m-none-eabi"
%runtime.hashmap = type { %runtime.hashmap*, i8*, i32, i8, i8, i8 }
%runtime._string = type { i8*, i32 }
@main.m = local_unnamed_addr global %runtime.hashmap* @"main$map"
@main.binaryMap = local_unnamed_addr global %runtime.hashmap* @"main$map.4"
@main.stringMap = local_unnamed_addr global %runtime.hashmap* @"main$map.6"
@main.binaryMap = local_unnamed_addr global %runtime.hashmap* @"main$map.1"
@main.stringMap = local_unnamed_addr global %runtime.hashmap* @"main$map.3"
@main.init.string = internal unnamed_addr constant [7 x i8] c"CONNECT"
@"main$mapbucket" = internal unnamed_addr global { [8 x i8], i8*, [8 x i8], [8 x %runtime._string] } { [8 x i8] c"\04\00\00\00\00\00\00\00", i8* null, [8 x i8] c"\01\00\00\00\00\00\00\00", [8 x %runtime._string] [%runtime._string { i8* getelementptr inbounds ([7 x i8], [7 x i8]* @main.init.string, i32 0, i32 0), i32 7 }, %runtime._string zeroinitializer, %runtime._string zeroinitializer, %runtime._string zeroinitializer, %runtime._string zeroinitializer, %runtime._string zeroinitializer, %runtime._string zeroinitializer, %runtime._string zeroinitializer] }
@"main$map" = internal unnamed_addr global %runtime.hashmap { %runtime.hashmap* null, i8* getelementptr inbounds ({ [8 x i8], i8*, [8 x i8], [8 x %runtime._string] }, { [8 x i8], i8*, [8 x i8], [8 x %runtime._string] }* @"main$mapbucket", i32 0, i32 0, i32 0), i32 1, i8 1, i8 8, i8 0 }
@"main$alloca.2" = internal global i8 1
@"main$alloca.3" = internal global i8 2
@"main$map.4" = internal unnamed_addr global %runtime.hashmap { %runtime.hashmap* null, i8* null, i32 0, i8 1, i8 1, i8 0 }
@"main$alloca.5" = internal global i8 2
@"main$map.6" = internal unnamed_addr global %runtime.hashmap { %runtime.hashmap* null, i8* null, i32 0, i8 8, i8 1, i8 0 }
declare void @runtime.hashmapBinarySet(%runtime.hashmap*, i8*, i8*, i8*, i8*) local_unnamed_addr
declare void @runtime.hashmapStringSet(%runtime.hashmap*, i8*, i32, i8*, i8*, i8*) local_unnamed_addr
@"main$map" = internal global %runtime.hashmap { %runtime.hashmap* null, i8* getelementptr inbounds ({ [8 x i8], i8*, { i8, [7 x i8] }, { { [7 x i8]*, [4 x i8] }, [56 x i8] } }, { [8 x i8], i8*, { i8, [7 x i8] }, { { [7 x i8]*, [4 x i8] }, [56 x i8] } }* @"main$mapbucket", i32 0, i32 0, i32 0), i32 1, i8 1, i8 8, i8 0 }
@"main$mapbucket" = internal unnamed_addr global { [8 x i8], i8*, { i8, [7 x i8] }, { { [7 x i8]*, [4 x i8] }, [56 x i8] } } { [8 x i8] c"\04\00\00\00\00\00\00\00", i8* null, { i8, [7 x i8] } { i8 1, [7 x i8] zeroinitializer }, { { [7 x i8]*, [4 x i8] }, [56 x i8] } { { [7 x i8]*, [4 x i8] } { [7 x i8]* @main.init.string, [4 x i8] c"\07\00\00\00" }, [56 x i8] zeroinitializer } }
@"main$map.1" = internal global %runtime.hashmap { %runtime.hashmap* null, i8* getelementptr inbounds ({ [8 x i8], i8*, { i8, [7 x i8] }, { i8, [7 x i8] } }, { [8 x i8], i8*, { i8, [7 x i8] }, { i8, [7 x i8] } }* @"main$mapbucket.2", i32 0, i32 0, i32 0), i32 1, i8 1, i8 1, i8 0 }
@"main$mapbucket.2" = internal unnamed_addr global { [8 x i8], i8*, { i8, [7 x i8] }, { i8, [7 x i8] } } { [8 x i8] c"\04\00\00\00\00\00\00\00", i8* null, { i8, [7 x i8] } { i8 1, [7 x i8] zeroinitializer }, { i8, [7 x i8] } { i8 2, [7 x i8] zeroinitializer } }
@"main$map.3" = internal global %runtime.hashmap { %runtime.hashmap* null, i8* getelementptr inbounds ({ [8 x i8], i8*, { { [7 x i8]*, [4 x i8] }, [56 x i8] }, { i8, [7 x i8] } }, { [8 x i8], i8*, { { [7 x i8]*, [4 x i8] }, [56 x i8] }, { i8, [7 x i8] } }* @"main$mapbucket.4", i32 0, i32 0, i32 0), i32 1, i8 8, i8 1, i8 0 }
@"main$mapbucket.4" = internal unnamed_addr global { [8 x i8], i8*, { { [7 x i8]*, [4 x i8] }, [56 x i8] }, { i8, [7 x i8] } } { [8 x i8] c"x\00\00\00\00\00\00\00", i8* null, { { [7 x i8]*, [4 x i8] }, [56 x i8] } { { [7 x i8]*, [4 x i8] } { [7 x i8]* @main.init.string, [4 x i8] c"\07\00\00\00" }, [56 x i8] zeroinitializer }, { i8, [7 x i8] } { i8 2, [7 x i8] zeroinitializer } }
define void @runtime.initAll() unnamed_addr {
entry:
call void @runtime.hashmapBinarySet(%runtime.hashmap* @"main$map.4", i8* @"main$alloca.2", i8* @"main$alloca.3", i8* undef, i8* null)
call void @runtime.hashmapStringSet(%runtime.hashmap* @"main$map.6", i8* getelementptr inbounds ([7 x i8], [7 x i8]* @main.init.string, i32 0, i32 0), i32 7, i8* @"main$alloca.5", i8* undef, i8* null)
ret void
}
-78
View File
@@ -1,78 +0,0 @@
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64--linux"
%runtime.typecodeID = type { %runtime.typecodeID*, i64 }
declare i1 @runtime.typeAssert(i64, %runtime.typecodeID*, i8*, i8*)
declare i1 @runtime.interfaceImplements(i64, i8**)
define i64 @returnsConst() {
ret i64 0
}
define i64 @returnsArg(i64 %arg) {
ret i64 %arg
}
declare i64 @externalCall()
define i64 @externalCallOnly() {
%result = call i64 @externalCall()
ret i64 0
}
define i64 @externalCallAndReturn() {
%result = call i64 @externalCall()
ret i64 %result
}
define i64 @externalCallBranch() {
%result = call i64 @externalCall()
%zero = icmp eq i64 %result, 0
br i1 %zero, label %if.then, label %if.done
if.then:
ret i64 2
if.done:
ret i64 4
}
@cleanGlobalInt = global i64 5
define i64 @readCleanGlobal() {
%global = load i64, i64* @cleanGlobalInt
ret i64 %global
}
@dirtyGlobalInt = global i64 5
define i64 @readDirtyGlobal() {
%global = load i64, i64* @dirtyGlobalInt
ret i64 %global
}
declare i64* @getDirtyPointer()
define void @storeToPointer() {
%ptr = call i64* @getDirtyPointer()
store i64 3, i64* %ptr
ret void
}
@functionPointer = global i64()* null
define i64 @callFunctionPointer() {
%fp = load i64()*, i64()** @functionPointer
%result = call i64 %fp()
ret i64 %result
}
define i1 @callTypeAssert() {
; Note: parameters are not realistic.
%ok = call i1 @runtime.typeAssert(i64 0, %runtime.typecodeID* null, i8* undef, i8* null)
ret i1 %ok
}
define i1 @callInterfaceImplements() {
; Note: parameters are not realistic.
%ok = call i1 @runtime.interfaceImplements(i64 0, i8** null)
ret i1 %ok
}
+4 -1
View File
@@ -1,6 +1,8 @@
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64--linux"
@"main$alloc.1" = internal unnamed_addr constant [6 x i8] c"\05\00{\00\00\04"
declare void @runtime.printuint8(i8) local_unnamed_addr
declare void @runtime.printint16(i16) local_unnamed_addr
@@ -15,6 +17,7 @@ entry:
call void @runtime.printuint8(i8 3)
call void @runtime.printuint8(i8 3)
call void @runtime.printint16(i16 5)
call void @runtime.printint16(i16 5)
%int16SliceDst.val = load i16, i16* bitcast ([6 x i8]* @"main$alloc.1" to i16*)
call void @runtime.printint16(i16 %int16SliceDst.val)
ret void
}
-126
View File
@@ -1,126 +0,0 @@
package interp
import (
"errors"
"tinygo.org/x/go-llvm"
)
// Return a list of values (actually, instructions) where this value is used as
// an operand.
func getUses(value llvm.Value) []llvm.Value {
var uses []llvm.Value
use := value.FirstUse()
for !use.IsNil() {
uses = append(uses, use.User())
use = use.NextUse()
}
return uses
}
// getStringBytes loads the byte slice of a Go string represented as a
// {ptr, len} pair.
func getStringBytes(strPtr Value, strLen llvm.Value) ([]byte, error) {
if !strLen.IsConstant() {
return nil, errors.New("getStringBytes with a non-constant length")
}
buf := make([]byte, strLen.ZExtValue())
for i := range buf {
gep, err := strPtr.GetElementPtr([]uint32{uint32(i)})
if err != nil {
return nil, err
}
c, err := gep.Load()
if err != nil {
return nil, err
}
buf[i] = byte(c.ZExtValue())
}
return buf, nil
}
// getLLVMIndices converts an []uint32 into an []llvm.Value, for use in
// llvm.ConstGEP.
func getLLVMIndices(int32Type llvm.Type, indices []uint32) []llvm.Value {
llvmIndices := make([]llvm.Value, len(indices))
for i, index := range indices {
llvmIndices[i] = llvm.ConstInt(int32Type, uint64(index), false)
}
return llvmIndices
}
// Return true if this type is a scalar value (integer or floating point), false
// otherwise.
func isScalar(t llvm.Type) bool {
switch t.TypeKind() {
case llvm.IntegerTypeKind, llvm.FloatTypeKind, llvm.DoubleTypeKind:
return true
default:
return false
}
}
// isPointerNil returns whether this is a nil pointer or not. The ok value
// indicates whether the result is certain: if it is false the result boolean is
// not valid.
func isPointerNil(v llvm.Value) (result bool, ok bool) {
if !v.IsAConstantExpr().IsNil() {
switch v.Opcode() {
case llvm.IntToPtr:
// Whether a constant inttoptr is nil is easy to
// determine.
result, ok = isZero(v.Operand(0))
if ok {
return
}
case llvm.BitCast, llvm.GetElementPtr:
// These const instructions are just a kind of wrappers for the
// underlying pointer.
return isPointerNil(v.Operand(0))
}
}
if !v.IsAConstantPointerNull().IsNil() {
// A constant pointer null is always null, of course.
return true, true
}
if !v.IsAGlobalValue().IsNil() {
// A global value is never null.
return false, true
}
return false, false // not valid
}
// isZero returns whether the value in v is the integer zero, and whether that
// can be known right now.
func isZero(v llvm.Value) (result bool, ok bool) {
if !v.IsAConstantExpr().IsNil() {
switch v.Opcode() {
case llvm.PtrToInt:
return isPointerNil(v.Operand(0))
}
}
if !v.IsAConstantInt().IsNil() {
val := v.ZExtValue()
return val == 0, true
}
return false, false // not valid
}
// unwrap returns the underlying value, with GEPs removed. This can be useful to
// get the underlying global of a GEP pointer.
func unwrap(value llvm.Value) llvm.Value {
for {
if !value.IsAConstantExpr().IsNil() {
switch value.Opcode() {
case llvm.GetElementPtr:
value = value.Operand(0)
continue
}
} else if !value.IsAGetElementPtrInst().IsNil() {
value = value.Operand(0)
continue
}
break
}
return value
}
-443
View File
@@ -1,443 +0,0 @@
package interp
// This file provides a litte bit of abstraction around LLVM values.
import (
"errors"
"strconv"
"tinygo.org/x/go-llvm"
)
// A Value is a LLVM value with some extra methods attached for easier
// interpretation.
type Value interface {
Value() llvm.Value // returns a LLVM value
Type() llvm.Type // equal to Value().Type()
IsConstant() bool // returns true if this value is a constant value
Load() (llvm.Value, error) // dereference a pointer
Store(llvm.Value) error // store to a pointer
GetElementPtr([]uint32) (Value, error) // returns an interior pointer
String() string // string representation, for debugging
}
// A type that simply wraps a LLVM constant value.
type LocalValue struct {
Eval *Eval
Underlying llvm.Value
}
// Value implements Value by returning the constant value itself.
func (v *LocalValue) Value() llvm.Value {
return v.Underlying
}
func (v *LocalValue) Type() llvm.Type {
return v.Underlying.Type()
}
func (v *LocalValue) IsConstant() bool {
if _, ok := v.Eval.dirtyGlobals[unwrap(v.Underlying)]; ok {
return false
}
return v.Underlying.IsConstant()
}
// Load loads a constant value if this is a constant pointer.
func (v *LocalValue) Load() (llvm.Value, error) {
if !v.Underlying.IsAGlobalVariable().IsNil() {
return v.Underlying.Initializer(), nil
}
switch v.Underlying.Opcode() {
case llvm.GetElementPtr:
indices := v.getConstGEPIndices()
if indices[0] != 0 {
return llvm.Value{}, errors.New("invalid GEP")
}
global := v.Eval.getValue(v.Underlying.Operand(0))
agg, err := global.Load()
if err != nil {
return llvm.Value{}, err
}
return llvm.ConstExtractValue(agg, indices[1:]), nil
case llvm.BitCast:
return llvm.Value{}, errors.New("interp: load from a bitcast")
default:
return llvm.Value{}, errors.New("interp: load from a constant")
}
}
// Store stores to the underlying value if the value type is a pointer type,
// otherwise it returns an error.
func (v *LocalValue) Store(value llvm.Value) error {
if !v.Underlying.IsAGlobalVariable().IsNil() {
if !value.IsConstant() {
v.MarkDirty()
v.Eval.builder.CreateStore(value, v.Underlying)
} else {
v.Underlying.SetInitializer(value)
}
return nil
}
if !value.IsConstant() {
v.MarkDirty()
v.Eval.builder.CreateStore(value, v.Underlying)
return nil
}
switch v.Underlying.Opcode() {
case llvm.GetElementPtr:
indices := v.getConstGEPIndices()
if indices[0] != 0 {
return errors.New("invalid GEP")
}
global := &LocalValue{v.Eval, v.Underlying.Operand(0)}
agg, err := global.Load()
if err != nil {
return err
}
agg = llvm.ConstInsertValue(agg, value, indices[1:])
return global.Store(agg)
default:
return errors.New("interp: store on a constant")
}
}
// GetElementPtr returns a GEP when the underlying value is of pointer type.
func (v *LocalValue) GetElementPtr(indices []uint32) (Value, error) {
if !v.Underlying.IsAGlobalVariable().IsNil() {
int32Type := v.Underlying.Type().Context().Int32Type()
gep := llvm.ConstGEP(v.Underlying, getLLVMIndices(int32Type, indices))
return &LocalValue{v.Eval, gep}, nil
}
if !v.Underlying.IsAConstantExpr().IsNil() {
switch v.Underlying.Opcode() {
case llvm.GetElementPtr, llvm.IntToPtr, llvm.BitCast:
int32Type := v.Underlying.Type().Context().Int32Type()
llvmIndices := getLLVMIndices(int32Type, indices)
return &LocalValue{v.Eval, llvm.ConstGEP(v.Underlying, llvmIndices)}, nil
}
}
return nil, errors.New("interp: unknown GEP")
}
// stripPointerCasts removes all const bitcasts from pointer values, if there
// are any.
func (v *LocalValue) stripPointerCasts() *LocalValue {
value := v.Underlying
for {
if !value.IsAConstantExpr().IsNil() {
switch value.Opcode() {
case llvm.BitCast:
value = value.Operand(0)
continue
}
}
return &LocalValue{
Eval: v.Eval,
Underlying: value,
}
}
}
func (v *LocalValue) String() string {
isConstant := "false"
if v.IsConstant() {
isConstant = "true"
}
return "&LocalValue{Type: " + v.Type().String() + ", IsConstant: " + isConstant + "}"
}
// getConstGEPIndices returns indices of this constant GEP, if this is a GEP
// instruction. If it is not, the behavior is undefined.
func (v *LocalValue) getConstGEPIndices() []uint32 {
indices := make([]uint32, v.Underlying.OperandsCount()-1)
for i := range indices {
operand := v.Underlying.Operand(i + 1)
indices[i] = uint32(operand.ZExtValue())
}
return indices
}
// MarkDirty marks this global as dirty, meaning that every load from and store
// to this global (from now on) must be performed at runtime.
func (v *LocalValue) MarkDirty() {
underlying := unwrap(v.Underlying)
if underlying.IsAGlobalVariable().IsNil() {
panic("trying to mark a non-global as dirty")
}
if !v.IsConstant() {
return // already dirty
}
v.Eval.dirtyGlobals[underlying] = struct{}{}
}
// MapValue implements a Go map which is created at compile time and stored as a
// global variable.
type MapValue struct {
Eval *Eval
PkgName string
Underlying llvm.Value
Keys []Value
Values []Value
KeySize int
ValueSize int
KeyType llvm.Type
ValueType llvm.Type
}
func (v *MapValue) newBucket() llvm.Value {
ctx := v.Eval.Mod.Context()
i8ptrType := llvm.PointerType(ctx.Int8Type(), 0)
bucketType := ctx.StructType([]llvm.Type{
llvm.ArrayType(ctx.Int8Type(), 8), // tophash
i8ptrType, // next bucket
llvm.ArrayType(v.KeyType, 8), // key type
llvm.ArrayType(v.ValueType, 8), // value type
}, false)
bucketValue := llvm.ConstNull(bucketType)
bucket := llvm.AddGlobal(v.Eval.Mod, bucketType, v.PkgName+"$mapbucket")
bucket.SetInitializer(bucketValue)
bucket.SetLinkage(llvm.InternalLinkage)
bucket.SetUnnamedAddr(true)
return bucket
}
// Value returns a global variable which is a pointer to the actual hashmap.
func (v *MapValue) Value() llvm.Value {
if !v.Underlying.IsNil() {
return v.Underlying
}
ctx := v.Eval.Mod.Context()
i8ptrType := llvm.PointerType(ctx.Int8Type(), 0)
var firstBucketGlobal llvm.Value
if len(v.Keys) == 0 {
// there are no buckets
firstBucketGlobal = llvm.ConstPointerNull(i8ptrType)
} else {
// create initial bucket
firstBucketGlobal = v.newBucket()
}
// Insert each key/value pair in the hashmap.
bucketGlobal := firstBucketGlobal
for i, key := range v.Keys {
var keyBuf []byte
llvmKey := key.Value()
llvmValue := v.Values[i].Value()
if key.Type().TypeKind() == llvm.StructTypeKind && key.Type().StructName() == "runtime._string" {
keyPtr := llvm.ConstExtractValue(llvmKey, []uint32{0})
keyLen := llvm.ConstExtractValue(llvmKey, []uint32{1})
keyPtrVal := v.Eval.getValue(keyPtr)
var err error
keyBuf, err = getStringBytes(keyPtrVal, keyLen)
if err != nil {
panic(err) // TODO
}
} else if key.Type().TypeKind() == llvm.IntegerTypeKind {
keyBuf = make([]byte, v.Eval.TargetData.TypeAllocSize(key.Type()))
n := key.Value().ZExtValue()
for i := range keyBuf {
keyBuf[i] = byte(n)
n >>= 8
}
} else if key.Type().TypeKind() == llvm.ArrayTypeKind &&
key.Type().ElementType().TypeKind() == llvm.IntegerTypeKind &&
key.Type().ElementType().IntTypeWidth() == 8 {
keyBuf = make([]byte, v.Eval.TargetData.TypeAllocSize(key.Type()))
for i := range keyBuf {
keyBuf[i] = byte(llvm.ConstExtractValue(llvmKey, []uint32{uint32(i)}).ZExtValue())
}
} else {
panic("interp: map key type not implemented: " + key.Type().String())
}
hash := v.hash(keyBuf)
if i%8 == 0 && i != 0 {
// Bucket is full, create a new one.
newBucketGlobal := v.newBucket()
zero := llvm.ConstInt(ctx.Int32Type(), 0, false)
newBucketPtr := llvm.ConstInBoundsGEP(newBucketGlobal, []llvm.Value{zero})
newBucketPtrCast := llvm.ConstBitCast(newBucketPtr, i8ptrType)
// insert pointer into old bucket
bucket := bucketGlobal.Initializer()
bucket = llvm.ConstInsertValue(bucket, newBucketPtrCast, []uint32{1})
bucketGlobal.SetInitializer(bucket)
// switch to next bucket
bucketGlobal = newBucketGlobal
}
tophashValue := llvm.ConstInt(ctx.Int8Type(), uint64(v.topHash(hash)), false)
bucket := bucketGlobal.Initializer()
bucket = llvm.ConstInsertValue(bucket, tophashValue, []uint32{0, uint32(i % 8)})
bucket = llvm.ConstInsertValue(bucket, llvmKey, []uint32{2, uint32(i % 8)})
bucket = llvm.ConstInsertValue(bucket, llvmValue, []uint32{3, uint32(i % 8)})
bucketGlobal.SetInitializer(bucket)
}
// Create the hashmap itself.
zero := llvm.ConstInt(ctx.Int32Type(), 0, false)
bucketPtr := llvm.ConstInBoundsGEP(firstBucketGlobal, []llvm.Value{zero})
hashmapType := v.Type()
hashmap := llvm.ConstNamedStruct(hashmapType, []llvm.Value{
llvm.ConstPointerNull(llvm.PointerType(hashmapType, 0)), // next
llvm.ConstBitCast(bucketPtr, i8ptrType), // buckets
llvm.ConstInt(hashmapType.StructElementTypes()[2], uint64(len(v.Keys)), false), // count
llvm.ConstInt(ctx.Int8Type(), uint64(v.KeySize), false), // keySize
llvm.ConstInt(ctx.Int8Type(), uint64(v.ValueSize), false), // valueSize
llvm.ConstInt(ctx.Int8Type(), 0, false), // bucketBits
})
// Create a pointer to this hashmap.
hashmapPtr := llvm.AddGlobal(v.Eval.Mod, hashmap.Type(), v.PkgName+"$map")
hashmapPtr.SetInitializer(hashmap)
hashmapPtr.SetLinkage(llvm.InternalLinkage)
hashmapPtr.SetUnnamedAddr(true)
v.Underlying = llvm.ConstInBoundsGEP(hashmapPtr, []llvm.Value{zero})
return v.Underlying
}
// Type returns type runtime.hashmap, which is the actual hashmap type.
func (v *MapValue) Type() llvm.Type {
return v.Eval.Mod.GetTypeByName("runtime.hashmap")
}
func (v *MapValue) IsConstant() bool {
return true // TODO: dirty maps
}
// Load panics: maps are of reference type so cannot be dereferenced.
func (v *MapValue) Load() (llvm.Value, error) {
panic("interp: load from a map")
}
// Store returns an error: maps are of reference type so cannot be stored to.
func (v *MapValue) Store(value llvm.Value) error {
// This must be a bug, but it might be helpful to indicate the location
// anyway.
return errors.New("interp: store on a map")
}
// GetElementPtr panics: maps are of reference type so their (interior)
// addresses cannot be calculated.
func (v *MapValue) GetElementPtr(indices []uint32) (Value, error) {
return nil, errors.New("interp: GEP on a map")
}
// PutString does a map assign operation, assuming that the map is of type
// map[string]T.
func (v *MapValue) PutString(keyBuf, keyLen, valPtr *LocalValue) error {
if !v.Underlying.IsNil() {
return errors.New("map already created")
}
if valPtr.Underlying.Opcode() == llvm.BitCast {
valPtr = &LocalValue{v.Eval, valPtr.Underlying.Operand(0)}
}
value, err := valPtr.Load()
if err != nil {
return err
}
if v.ValueType.IsNil() {
v.ValueType = value.Type()
if int(v.Eval.TargetData.TypeAllocSize(v.ValueType)) != v.ValueSize {
return errors.New("interp: map store value type has the wrong size")
}
} else {
if value.Type() != v.ValueType {
return errors.New("interp: map store value type is inconsistent")
}
}
keyType := v.Eval.Mod.GetTypeByName("runtime._string")
v.KeyType = keyType
key := llvm.ConstNull(keyType)
key = llvm.ConstInsertValue(key, keyBuf.Value(), []uint32{0})
key = llvm.ConstInsertValue(key, keyLen.Value(), []uint32{1})
// TODO: avoid duplicate keys
v.Keys = append(v.Keys, &LocalValue{v.Eval, key})
v.Values = append(v.Values, &LocalValue{v.Eval, value})
return nil
}
// PutBinary does a map assign operation.
func (v *MapValue) PutBinary(keyPtr, valPtr *LocalValue) error {
if !v.Underlying.IsNil() {
return errors.New("map already created")
}
if valPtr.Underlying.Opcode() == llvm.BitCast {
valPtr = &LocalValue{v.Eval, valPtr.Underlying.Operand(0)}
}
value, err := valPtr.Load()
if err != nil {
return err
}
if v.ValueType.IsNil() {
v.ValueType = value.Type()
if int(v.Eval.TargetData.TypeAllocSize(v.ValueType)) != v.ValueSize {
return errors.New("interp: map store value type has the wrong size")
}
} else {
if value.Type() != v.ValueType {
return errors.New("interp: map store value type is inconsistent")
}
}
if !keyPtr.Underlying.IsAConstantExpr().IsNil() {
if keyPtr.Underlying.Opcode() == llvm.BitCast {
keyPtr = &LocalValue{v.Eval, keyPtr.Underlying.Operand(0)}
} else if keyPtr.Underlying.Opcode() == llvm.GetElementPtr {
keyPtr = &LocalValue{v.Eval, keyPtr.Underlying.Operand(0)}
}
}
key, err := keyPtr.Load()
if err != nil {
return err
}
if v.KeyType.IsNil() {
v.KeyType = key.Type()
if int(v.Eval.TargetData.TypeAllocSize(v.KeyType)) != v.KeySize {
return errors.New("interp: map store key type has the wrong size")
}
} else {
if key.Type() != v.KeyType {
return errors.New("interp: map store key type is inconsistent")
}
}
// TODO: avoid duplicate keys
v.Keys = append(v.Keys, &LocalValue{v.Eval, key})
v.Values = append(v.Values, &LocalValue{v.Eval, value})
return nil
}
// Get FNV-1a hash of this string.
//
// https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function#FNV-1a_hash
func (v *MapValue) hash(data []byte) uint32 {
var result uint32 = 2166136261 // FNV offset basis
for _, c := range data {
result ^= uint32(c)
result *= 16777619 // FNV prime
}
return result
}
// Get the topmost 8 bits of the hash, without using a special value (like 0).
func (v *MapValue) topHash(hash uint32) uint8 {
tophash := uint8(hash >> 24)
if tophash < 1 {
// 0 means empty slot, so make it bigger.
tophash += 1
}
return tophash
}
func (v *MapValue) String() string {
return "&MapValue{KeySize: " + strconv.Itoa(v.KeySize) + ", ValueSize: " + strconv.Itoa(v.ValueSize) + "}"
}
+2 -2
View File
@@ -67,7 +67,7 @@ func NewProgram(lprogram *loader.Program) *Program {
program := lprogram.LoadSSA()
program.Build()
mainPkg := program.ImportedPackage(lprogram.MainPkg().ImportPath)
mainPkg := program.Package(lprogram.MainPkg().Pkg)
if mainPkg == nil {
panic("could not find main package")
}
@@ -79,7 +79,7 @@ func NewProgram(lprogram *loader.Program) *Program {
}
for _, pkg := range lprogram.Sorted() {
p.AddPackage(program.ImportedPackage(pkg.ImportPath))
p.AddPackage(program.Package(pkg.Pkg))
}
return p
Submodule
+1
Submodule lib/stm32-svd added at 9ec9982485
+1 -1
View File
@@ -189,7 +189,7 @@ func mergeDirectory(goroot, tinygoroot, tmpgoroot, importPath string, overrides
// with the TinyGo version. This is the case on some targets.
func needsSyscallPackage(buildTags []string) bool {
for _, tag := range buildTags {
if tag == "baremetal" || tag == "darwin" || tag == "nintendoswitch" {
if tag == "baremetal" || tag == "darwin" || tag == "nintendoswitch" || tag == "wasi" {
return true
}
}
+47 -8
View File
@@ -43,6 +43,7 @@ type Program struct {
type PackageJSON struct {
Dir string
ImportPath string
Name string
ForTest string
// Source files
@@ -51,8 +52,7 @@ type PackageJSON struct {
CFiles []string
// Dependency information
Imports []string
ImportMap map[string]string
Imports []string
// Error information
Error *struct {
@@ -169,6 +169,41 @@ func Load(config *compileopts.Config, inputPkgs []string, clangHeaders string, t
}
return nil, err
}
if config.TestConfig.CompileTestBinary {
// When creating a test binary, `go list` will list two or three
// packages used for testing the package. The first is the original
// package as if it were built normally, the second is the same
// package but with the *_test.go files included. A possible third
// may be included for _test packages (such as math_test), used to
// test the external API with no access to internal functions.
// All packages that are necessary for testing (including the to be
// tested package with *_test.go files, but excluding the original
// unmodified package) have a suffix added to the import path, for
// example the math package has import path "math [math.test]" and
// test dependencies such as fmt will have an import path of the
// form "fmt [math.test]".
// The code below removes this suffix, and if this results in a
// duplicate (which happens with the to-be-tested package without
// *.test.go files) the previous package is removed from the list of
// packages included in this build.
// This is necessary because the change in import paths results in
// breakage to //go:linkname. Additionally, the duplicated package
// slows down the build and so is best removed.
if pkg.ForTest != "" && strings.HasSuffix(pkg.ImportPath, " ["+pkg.ForTest+".test]") {
newImportPath := pkg.ImportPath[:len(pkg.ImportPath)-len(" ["+pkg.ForTest+".test]")]
if _, ok := p.Packages[newImportPath]; ok {
// Delete the previous package (that this package overrides).
delete(p.Packages, newImportPath)
for i, pkg := range p.sorted {
if pkg.ImportPath == newImportPath {
p.sorted = append(p.sorted[:i], p.sorted[i+1:]...) // remove element from slice
break
}
}
}
pkg.ImportPath = newImportPath
}
}
p.sorted = append(p.sorted, pkg)
p.Packages[pkg.ImportPath] = pkg
}
@@ -301,7 +336,16 @@ func (p *Package) Check() error {
// Do typechecking of the package.
checker.Importer = p
typesPkg, err := checker.Check(p.ImportPath, p.program.fset, p.Files, &p.info)
packageName := p.ImportPath
if p.Name == "main" {
// The main package normally has a different import path, such as
// "command-line-arguments" or "./testdata/cgo". Therefore, use the name
// "main" in such a case: this package isn't imported from anywhere.
// This is safe as it isn't possible to import a package with the name
// "main".
packageName = "main"
}
typesPkg, err := checker.Check(packageName, p.program.fset, p.Files, &p.info)
if err != nil {
if err, ok := err.(Errors); ok {
return err
@@ -367,11 +411,6 @@ func (p *Package) Import(to string) (*types.Package, error) {
if to == "unsafe" {
return types.Unsafe, nil
}
if replace, ok := p.ImportMap[to]; ok {
// This import path should be replaced by another import path, according
// to `go list`.
to = replace
}
if imported, ok := p.program.Packages[to]; ok {
return imported.Pkg, nil
} else {
+8
View File
@@ -16,3 +16,11 @@ func (p *Program) LoadSSA() *ssa.Program {
return prog
}
// LoadSSA constructs the SSA form of this package.
//
// The program must already be parsed and type-checked with the .Parse() method.
func (p *Package) LoadSSA() *ssa.Package {
prog := ssa.NewProgram(p.program.fset, ssa.SanityCheckFunctions|ssa.BareInits|ssa.GlobalDebug)
return prog.CreatePackage(p.Pkg, p.Files, &p.info, true)
}
+132 -85
View File
@@ -14,7 +14,6 @@ import (
"os/signal"
"path/filepath"
"runtime"
"strconv"
"strings"
"syscall"
"time"
@@ -74,7 +73,12 @@ func copyFile(src, dst string) error {
}
defer source.Close()
destination, err := os.Create(dst)
st, err := source.Stat()
if err != nil {
return err
}
destination, err := os.OpenFile(dst, os.O_RDWR|os.O_CREATE|os.O_TRUNC, st.Mode())
if err != nil {
return err
}
@@ -84,6 +88,14 @@ func copyFile(src, dst string) error {
return err
}
// executeCommand is a simple wrapper to exec.Cmd
func executeCommand(options *compileopts.Options, name string, arg ...string) *exec.Cmd {
if options.PrintCommands {
fmt.Printf("%s %s\n ", name, strings.Join(arg, " "))
}
return exec.Command(name, arg...)
}
// Build compiles and links the given package and writes it to outpath.
func Build(pkgName, outpath string, options *compileopts.Options) error {
config, err := builder.NewConfig(options)
@@ -120,30 +132,71 @@ func Build(pkgName, outpath string, options *compileopts.Options) error {
}
// Test runs the tests in the given package.
func Test(pkgName string, options *compileopts.Options) error {
func Test(pkgName string, options *compileopts.Options, testCompileOnly bool, outpath string) error {
options.TestConfig.CompileTestBinary = true
config, err := builder.NewConfig(options)
if err != nil {
return err
}
return builder.Build(pkgName, ".elf", config, func(result builder.BuildResult) error {
cmd := exec.Command(result.Binary)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Dir = result.MainDir
err := cmd.Run()
if err != nil {
// Propagate the exit code
if err, ok := err.(*exec.ExitError); ok {
if status, ok := err.Sys().(syscall.WaitStatus); ok {
os.Exit(status.ExitStatus())
}
os.Exit(1)
return builder.Build(pkgName, outpath, config, func(result builder.BuildResult) error {
if testCompileOnly || outpath != "" {
// Write test binary to the specified file name.
if outpath == "" {
// No -o path was given, so create one now.
// This matches the behavior of go test.
outpath = filepath.Base(result.MainDir) + ".test"
}
copyFile(result.Binary, outpath)
}
if testCompileOnly {
// Do not run the test.
return nil
}
if len(config.Target.Emulator) == 0 {
// Run directly.
cmd := executeCommand(config.Options, result.Binary)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Dir = result.MainDir
err := cmd.Run()
if err != nil {
// Propagate the exit code
if err, ok := err.(*exec.ExitError); ok {
if status, ok := err.Sys().(syscall.WaitStatus); ok {
os.Exit(status.ExitStatus())
}
os.Exit(1)
}
return &commandError{"failed to run compiled binary", result.Binary, err}
}
return nil
} else {
// Run in an emulator.
args := append(config.Target.Emulator[1:], result.Binary)
cmd := executeCommand(config.Options, config.Target.Emulator[0], args...)
buf := &bytes.Buffer{}
w := io.MultiWriter(os.Stdout, buf)
cmd.Stdout = w
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
if err, ok := err.(*exec.ExitError); !ok || !err.Exited() {
// Workaround for QEMU which always exits with an error.
return &commandError{"failed to run emulator with", result.Binary, err}
}
}
testOutput := string(buf.Bytes())
if testOutput == "PASS\n" || strings.HasSuffix(testOutput, "\nPASS\n") {
// Test passed.
return nil
} else {
// Test failed, either by ending with the word "FAIL" or with a
// panic of some sort.
os.Exit(1)
return nil // unreachable
}
return &commandError{"failed to run compiled binary", result.Binary, err}
}
return nil
})
}
@@ -230,9 +283,9 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
if len(command) < 2 {
return errors.New("invalid flash command")
}
cmd = exec.Command(command[0], command[1:]...)
cmd = executeCommand(config.Options, command[0], command[1:]...)
default:
cmd = exec.Command("/bin/sh", "-c", flashCmd)
cmd = executeCommand(config.Options, "/bin/sh", "-c", flashCmd)
}
cmd.Stdout = os.Stdout
@@ -246,13 +299,13 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
case "msd":
switch fileExt {
case ".uf2":
err := flashUF2UsingMSD(config.Target.FlashVolume, result.Binary)
err := flashUF2UsingMSD(config.Target.FlashVolume, result.Binary, config.Options)
if err != nil {
return &commandError{"failed to flash", result.Binary, err}
}
return nil
case ".hex":
err := flashHexUsingMSD(config.Target.FlashVolume, result.Binary)
err := flashHexUsingMSD(config.Target.FlashVolume, result.Binary, config.Options)
if err != nil {
return &commandError{"failed to flash", result.Binary, err}
}
@@ -266,7 +319,7 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
return err
}
args = append(args, "-c", "program "+filepath.ToSlash(result.Binary)+" reset exit")
cmd := exec.Command("openocd", args...)
cmd := executeCommand(config.Options, "openocd", args...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err = cmd.Run()
@@ -302,16 +355,22 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro
switch gdbInterface {
case "msd", "command", "":
if len(config.Target.Emulator) != 0 {
// Assume QEMU as an emulator.
if config.Target.Emulator[0] == "mgba" {
gdbInterface = "mgba"
} else {
} else if config.Target.Emulator[0] == "simavr" {
gdbInterface = "simavr"
} else if strings.HasPrefix(config.Target.Emulator[0], "qemu-system-") {
gdbInterface = "qemu"
} else {
// Assume QEMU as an emulator.
gdbInterface = "qemu-user"
}
} else if openocdInterface != "" && config.Target.OpenOCDTarget != "" {
gdbInterface = "openocd"
} else if config.Target.JLinkDevice != "" {
gdbInterface = "jlink"
} else {
gdbInterface = "native"
}
}
@@ -329,7 +388,7 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro
if err != nil {
return err
}
daemon = exec.Command("openocd", args...)
daemon = executeCommand(config.Options, "openocd", args...)
if ocdOutput {
// Make it clear which output is from the daemon.
w := &ColorWriter{
@@ -344,7 +403,7 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro
gdbCommands = append(gdbCommands, "target remote :2331", "load", "monitor reset halt")
// We need a separate debugging daemon for on-chip debugging.
daemon = exec.Command("JLinkGDBServer", "-device", config.Target.JLinkDevice)
daemon = executeCommand(config.Options, "JLinkGDBServer", "-device", config.Target.JLinkDevice)
if ocdOutput {
// Make it clear which output is from the daemon.
w := &ColorWriter{
@@ -360,7 +419,15 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro
// Run in an emulator.
args := append(config.Target.Emulator[1:], result.Binary, "-s", "-S")
daemon = exec.Command(config.Target.Emulator[0], args...)
daemon = executeCommand(config.Options, config.Target.Emulator[0], args...)
daemon.Stdout = os.Stdout
daemon.Stderr = os.Stderr
case "qemu-user":
gdbCommands = append(gdbCommands, "target remote :1234")
// Run in an emulator.
args := append(config.Target.Emulator[1:], "-g", "1234", result.Binary)
daemon = executeCommand(config.Options, config.Target.Emulator[0], args...)
daemon.Stdout = os.Stdout
daemon.Stderr = os.Stderr
case "mgba":
@@ -368,7 +435,15 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro
// Run in an emulator.
args := append(config.Target.Emulator[1:], result.Binary, "-g")
daemon = exec.Command(config.Target.Emulator[0], args...)
daemon = executeCommand(config.Options, config.Target.Emulator[0], args...)
daemon.Stdout = os.Stdout
daemon.Stderr = os.Stderr
case "simavr":
gdbCommands = append(gdbCommands, "target remote :1234")
// Run in an emulator.
args := append(config.Target.Emulator[1:], "-g", result.Binary)
daemon = executeCommand(config.Options, config.Target.Emulator[0], args...)
daemon.Stdout = os.Stdout
daemon.Stderr = os.Stderr
case "msd":
@@ -409,7 +484,7 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro
for _, cmd := range gdbCommands {
params = append(params, "-ex", cmd)
}
cmd := exec.Command(config.Target.GDB, params...)
cmd := executeCommand(config.Options, config.Target.GDB, params...)
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
@@ -434,7 +509,7 @@ func Run(pkgName string, options *compileopts.Options) error {
return builder.Build(pkgName, ".elf", config, func(result builder.BuildResult) error {
if len(config.Target.Emulator) == 0 {
// Run directly.
cmd := exec.Command(result.Binary)
cmd := executeCommand(config.Options, result.Binary)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Run()
@@ -449,7 +524,7 @@ func Run(pkgName string, options *compileopts.Options) error {
} else {
// Run in an emulator.
args := append(config.Target.Emulator[1:], result.Binary)
cmd := exec.Command(config.Target.Emulator[0], args...)
cmd := executeCommand(config.Options, config.Target.Emulator[0], args...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Run()
@@ -492,7 +567,7 @@ func touchSerialPortAt1200bps(port string) (err error) {
const maxMSDRetries = 10
func flashUF2UsingMSD(volume, tmppath string) error {
func flashUF2UsingMSD(volume, tmppath string, options *compileopts.Options) error {
// find standard UF2 info path
var infoPath string
switch runtime.GOOS {
@@ -501,7 +576,7 @@ func flashUF2UsingMSD(volume, tmppath string) error {
case "darwin":
infoPath = "/Volumes/" + volume + "/INFO_UF2.TXT"
case "windows":
path, err := windowsFindUSBDrive(volume)
path, err := windowsFindUSBDrive(volume, options)
if err != nil {
return err
}
@@ -516,7 +591,7 @@ func flashUF2UsingMSD(volume, tmppath string) error {
return moveFile(tmppath, filepath.Dir(d)+"/flash.uf2")
}
func flashHexUsingMSD(volume, tmppath string) error {
func flashHexUsingMSD(volume, tmppath string, options *compileopts.Options) error {
// find expected volume path
var destPath string
switch runtime.GOOS {
@@ -525,7 +600,7 @@ func flashHexUsingMSD(volume, tmppath string) error {
case "darwin":
destPath = "/Volumes/" + volume
case "windows":
path, err := windowsFindUSBDrive(volume)
path, err := windowsFindUSBDrive(volume, options)
if err != nil {
return err
}
@@ -559,8 +634,8 @@ func locateDevice(volume, path string) (string, error) {
return d[0], nil
}
func windowsFindUSBDrive(volume string) (string, error) {
cmd := exec.Command("wmic",
func windowsFindUSBDrive(volume string, options *compileopts.Options) (string, error) {
cmd := executeCommand(options, "wmic",
"PATH", "Win32_LogicalDisk", "WHERE", "VolumeName = '"+volume+"'",
"get", "DeviceID,VolumeName,FileSystem,DriveType")
@@ -582,30 +657,6 @@ func windowsFindUSBDrive(volume string) (string, error) {
return "", errors.New("unable to locate a USB device to be flashed")
}
// parseSize converts a human-readable size (with k/m/g suffix) into a plain
// number.
func parseSize(s string) (int64, error) {
s = strings.ToLower(strings.TrimSpace(s))
if len(s) == 0 {
return 0, errors.New("no size provided")
}
multiply := int64(1)
switch s[len(s)-1] {
case 'k':
multiply = 1 << 10
case 'm':
multiply = 1 << 20
case 'g':
multiply = 1 << 30
}
if multiply != 1 {
s = s[:len(s)-1]
}
n, err := strconv.ParseInt(s, 0, 64)
n *= multiply
return n, err
}
// getDefaultPort returns the default serial port depending on the operating system.
func getDefaultPort() (port string, err error) {
var portPath string
@@ -753,7 +804,6 @@ func main() {
}
command := os.Args[1]
outpath := flag.String("o", "", "output filename")
opt := flag.String("opt", "z", "optimization level: 0, 1, 2, s, z")
gc := flag.String("gc", "", "garbage collector to use (none, leaking, extalloc, conservative)")
panicStrategy := flag.String("panic", "print", "panic strategy (print, trap)")
@@ -765,20 +815,28 @@ func main() {
target := flag.String("target", "", "LLVM target | .json file with TargetSpec")
printSize := flag.String("size", "", "print sizes (none, short, full)")
printStacks := flag.Bool("print-stacks", false, "print stack sizes of goroutines")
printCommands := flag.Bool("x", false, "Print commands")
nodebug := flag.Bool("no-debug", false, "disable DWARF debug symbol generation")
ocdOutput := flag.Bool("ocd-output", false, "print OCD daemon output during debug")
port := flag.String("port", "", "flash port")
programmer := flag.String("programmer", "", "which hardware programmer to use")
cFlags := flag.String("cflags", "", "additional cflags for compiler")
ldFlags := flag.String("ldflags", "", "additional ldflags for linker")
wasmAbi := flag.String("wasm-abi", "js", "WebAssembly ABI conventions: js (no i64 params) or generic")
heapSize := flag.String("heap-size", "1M", "default heap size in bytes (only supported by WebAssembly)")
wasmAbi := flag.String("wasm-abi", "", "WebAssembly ABI conventions: js (no i64 params) or generic")
var flagJSON, flagDeps *bool
if command == "list" {
if command == "help" || command == "list" {
flagJSON = flag.Bool("json", false, "print data in JSON format")
flagDeps = flag.Bool("deps", false, "")
}
var outpath string
if command == "help" || command == "build" || command == "build-library" || command == "test" {
flag.StringVar(&outpath, "o", "", "output filename")
}
var testCompileOnlyFlag *bool
if command == "help" || command == "test" {
testCompileOnlyFlag = flag.Bool("c", false, "compile the test binary but do not run it")
}
// Early command processing, before commands are interpreted by the Go flag
// library.
@@ -805,6 +863,7 @@ func main() {
Debug: !*nodebug,
PrintSizes: *printSize,
PrintStacks: *printStacks,
PrintCommands: *printCommands,
Tags: *tags,
WasmAbi: *wasmAbi,
Programmer: *programmer,
@@ -818,16 +877,9 @@ func main() {
options.LDFlags = strings.Split(*ldFlags, " ")
}
var err error
if options.HeapSize, err = parseSize(*heapSize); err != nil {
fmt.Fprintln(os.Stderr, "Could not read heap size:", *heapSize)
usage()
os.Exit(1)
}
os.Setenv("CC", "clang -target="+*target)
err = options.Verify()
err := options.Verify()
if err != nil {
fmt.Fprintln(os.Stderr, err.Error())
usage()
@@ -836,7 +888,7 @@ func main() {
switch command {
case "build":
if *outpath == "" {
if outpath == "" {
fmt.Fprintln(os.Stderr, "No output filename supplied (-o).")
usage()
os.Exit(1)
@@ -849,15 +901,15 @@ func main() {
usage()
os.Exit(1)
}
if options.Target == "" && filepath.Ext(*outpath) == ".wasm" {
if options.Target == "" && filepath.Ext(outpath) == ".wasm" {
options.Target = "wasm"
}
err := Build(pkgName, *outpath, options)
err := Build(pkgName, outpath, options)
handleCompilerError(err)
case "build-library":
// Note: this command is only meant to be used while making a release!
if *outpath == "" {
if outpath == "" {
fmt.Fprintln(os.Stderr, "No output filename supplied (-o).")
usage()
os.Exit(1)
@@ -882,13 +934,8 @@ func main() {
}
path, err := lib.Load(*target)
handleCompilerError(err)
copyFile(path, *outpath)
copyFile(path, outpath)
case "flash", "gdb":
if *outpath != "" {
fmt.Fprintln(os.Stderr, "Output cannot be specified with the flash command.")
usage()
os.Exit(1)
}
pkgName := filepath.ToSlash(flag.Arg(0))
if command == "flash" {
err := Flash(pkgName, *port, options)
@@ -920,7 +967,7 @@ func main() {
usage()
os.Exit(1)
}
err := Test(pkgName, options)
err := Test(pkgName, options, *testCompileOnlyFlag, outpath)
handleCompilerError(err)
case "targets":
dir := filepath.Join(goenv.Get("TINYGOROOT"), "targets")
+13 -5
View File
@@ -78,6 +78,9 @@ func TestCompiler(t *testing.T) {
}
if runtime.GOOS == "linux" {
t.Run("X86Linux", func(t *testing.T) {
runPlatTests("i386--linux-gnu", matches, t)
})
t.Run("ARMLinux", func(t *testing.T) {
runPlatTests("arm--linux-gnueabihf", matches, t)
})
@@ -98,6 +101,10 @@ func TestCompiler(t *testing.T) {
runPlatTests("wasm", matches, t)
})
}
t.Run("WASI", func(t *testing.T) {
runPlatTests("wasi", matches, t)
})
}
}
@@ -109,7 +116,6 @@ func runPlatTests(target string, matches []string, t *testing.T) {
t.Run(filepath.Base(path), func(t *testing.T) {
t.Parallel()
runTest(path, target, t)
})
}
@@ -159,8 +165,9 @@ func runTest(path, target string, t *testing.T) {
VerifyIR: true,
Debug: true,
PrintSizes: "",
WasmAbi: "js",
WasmAbi: "",
}
binary := filepath.Join(tmpdir, "test")
err = runBuild("./"+path, binary, config)
if err != nil {
@@ -181,10 +188,11 @@ func runTest(path, target string, t *testing.T) {
t.Fatal("failed to load target spec:", err)
}
if len(spec.Emulator) == 0 {
t.Fatal("no emulator available for target:", target)
cmd = exec.Command(binary)
} else {
args := append(spec.Emulator[1:], binary)
cmd = exec.Command(spec.Emulator[0], args...)
}
args := append(spec.Emulator[1:], binary)
cmd = exec.Command(spec.Emulator[0], args...)
}
stdout := &bytes.Buffer{}
cmd.Stdout = stdout
-35
View File
@@ -76,32 +76,8 @@ const (
SCS_BASE = 0xE000E000
SYST_BASE = SCS_BASE + 0x0010
NVIC_BASE = SCS_BASE + 0x0100
SCB_BASE = SCS_BASE + 0x0D00
)
const (
SCB_AIRCR_VECTKEY_Pos = 16
SCB_AIRCR_SYSRESETREQ_Pos = 2
SCB_AIRCR_SYSRESETREQ_Msk = 1 << SCB_AIRCR_SYSRESETREQ_Pos
)
// System Control Block (SCB)
//
// SCB_Type provides the definitions for the System Control Block Registers.
type SCB_Type struct {
CPUID volatile.Register32 // CPUID Base Register
ICSR volatile.Register32 // Interrupt Control and State Register
VTOR volatile.Register32 // Vector Table Offset Register
AIRCR volatile.Register32 // Application Interrupt and Reset Control Register
SCR volatile.Register32 // System Control Register
CCR volatile.Register32 // Configuration Control Register
_ volatile.Register32 // RESERVED1;
SHP [2]volatile.Register32 // System Handlers Priority Registers. [0] is RESERVED
SHCSR volatile.Register32 // System Handler Control and State Register
}
var SCB = (*SCB_Type)(unsafe.Pointer(uintptr(SCB_BASE)))
// Nested Vectored Interrupt Controller (NVIC).
//
// Source:
@@ -213,17 +189,6 @@ func EnableInterrupts(mask uintptr) {
})
}
// SystemReset performs a hard system reset.
func SystemReset() {
// SCB->AIRCR = ((0x5FA << SCB_AIRCR_VECTKEY_Pos) |
// SCB_AIRCR_SYSRESETREQ_Msk);
SCB.AIRCR.Set((0x5FA << SCB_AIRCR_VECTKEY_Pos) | SCB_AIRCR_SYSRESETREQ_Msk)
for {
Asm("wfi")
}
}
// Set up the system timer to generate periodic tick events.
// This will cause SysTick_Handler to fire once per tick.
// The cyclecount parameter is a counter value which can range from 0 to
+431
View File
@@ -0,0 +1,431 @@
// Hand created file. DO NOT DELETE.
// Cortex-M System Control Block-related definitions.
// +build cortexm
package arm
import (
"runtime/volatile"
"unsafe"
)
const SCB_BASE = SCS_BASE + 0x0D00
// System Control Block (SCB)
//
// SCB_Type provides the definitions for the System Control Block Registers.
type SCB_Type struct {
CPUID volatile.Register32 // 0xD00: CPUID Base Register
ICSR volatile.Register32 // 0xD04: Interrupt Control and State Register
VTOR volatile.Register32 // 0xD08: Vector Table Offset Register
AIRCR volatile.Register32 // 0xD0C: Application Interrupt and Reset Control Register
SCR volatile.Register32 // 0xD10: System Control Register
CCR volatile.Register32 // 0xD14: Configuration and Control Register
SHPR1 volatile.Register32 // 0xD18: System Handler Priority Register 1 (Cortex-M3/M33/M4/M7 only)
SHPR2 volatile.Register32 // 0xD1C: System Handler Priority Register 2
SHPR3 volatile.Register32 // 0xD20: System Handler Priority Register 3
// the following are only applicable for Cortex-M3/M33/M4/M7
SHCSR volatile.Register32 // 0xD24: System Handler Control and State Register
CFSR volatile.Register32 // 0xD28: Configurable Fault Status Register
HFSR volatile.Register32 // 0xD2C: HardFault Status Register
DFSR volatile.Register32 // 0xD30: Debug Fault Status Register
MMFAR volatile.Register32 // 0xD34: MemManage Fault Address Register
BFAR volatile.Register32 // 0xD38: BusFault Address Register
}
var SCB = (*SCB_Type)(unsafe.Pointer(uintptr(SCB_BASE)))
// SystemReset performs a hard system reset.
func SystemReset() {
SCB.AIRCR.Set((0x5FA << SCB_AIRCR_VECTKEY_Pos) | SCB_AIRCR_SYSRESETREQ_Msk)
for {
Asm("wfi")
}
}
const (
// CPUID: CPUID Base Register
SCB_CPUID_REVISION_Pos = 0x0 // Position of REVISION field.
SCB_CPUID_REVISION_Msk = 0xf // Bit mask of REVISION field.
SCB_CPUID_PARTNO_Pos = 0x4 // Position of PARTNO field.
SCB_CPUID_PARTNO_Msk = 0xfff0 // Bit mask of PARTNO field.
SCB_CPUID_ARCHITECTURE_Pos = 0x10 // Position of ARCHITECTURE field.
SCB_CPUID_ARCHITECTURE_Msk = 0xf0000 // Bit mask of ARCHITECTURE field.
SCB_CPUID_VARIANT_Pos = 0x14 // Position of VARIANT field.
SCB_CPUID_VARIANT_Msk = 0xf00000 // Bit mask of VARIANT field.
SCB_CPUID_IMPLEMENTER_Pos = 0x18 // Position of IMPLEMENTER field.
SCB_CPUID_IMPLEMENTER_Msk = 0xff000000 // Bit mask of IMPLEMENTER field.
// ICSR: Interrupt Control and State Register
SCB_ICSR_VECTACTIVE_Pos = 0x0 // Position of VECTACTIVE field.
SCB_ICSR_VECTACTIVE_Msk = 0x1ff // Bit mask of VECTACTIVE field.
SCB_ICSR_RETTOBASE_Pos = 0xb // Position of RETTOBASE field.
SCB_ICSR_RETTOBASE_Msk = 0x800 // Bit mask of RETTOBASE field.
SCB_ICSR_RETTOBASE = 0x800 // Bit RETTOBASE.
SCB_ICSR_RETTOBASE_RETTOBASE_0 = 0x0 // there are preempted active exceptions to execute
SCB_ICSR_RETTOBASE_RETTOBASE_1 = 0x1 // there are no active exceptions, or the currently-executing exception is the only active exception
SCB_ICSR_VECTPENDING_Pos = 0xc // Position of VECTPENDING field.
SCB_ICSR_VECTPENDING_Msk = 0x1ff000 // Bit mask of VECTPENDING field.
SCB_ICSR_ISRPENDING_Pos = 0x16 // Position of ISRPENDING field.
SCB_ICSR_ISRPENDING_Msk = 0x400000 // Bit mask of ISRPENDING field.
SCB_ICSR_ISRPENDING = 0x400000 // Bit ISRPENDING.
SCB_ICSR_ISRPENDING_ISRPENDING_0 = 0x0 // No external interrupt pending.
SCB_ICSR_ISRPENDING_ISRPENDING_1 = 0x1 // External interrupt pending.
SCB_ICSR_PENDSTCLR_Pos = 0x19 // Position of PENDSTCLR field.
SCB_ICSR_PENDSTCLR_Msk = 0x2000000 // Bit mask of PENDSTCLR field.
SCB_ICSR_PENDSTCLR = 0x2000000 // Bit PENDSTCLR.
SCB_ICSR_PENDSTCLR_PENDSTCLR_0 = 0x0 // no effect
SCB_ICSR_PENDSTCLR_PENDSTCLR_1 = 0x1 // removes the pending state from the SysTick exception
SCB_ICSR_PENDSTSET_Pos = 0x1a // Position of PENDSTSET field.
SCB_ICSR_PENDSTSET_Msk = 0x4000000 // Bit mask of PENDSTSET field.
SCB_ICSR_PENDSTSET = 0x4000000 // Bit PENDSTSET.
SCB_ICSR_PENDSTSET_PENDSTSET_0 = 0x0 // write: no effect; read: SysTick exception is not pending
SCB_ICSR_PENDSTSET_PENDSTSET_1 = 0x1 // write: changes SysTick exception state to pending; read: SysTick exception is pending
SCB_ICSR_PENDSVCLR_Pos = 0x1b // Position of PENDSVCLR field.
SCB_ICSR_PENDSVCLR_Msk = 0x8000000 // Bit mask of PENDSVCLR field.
SCB_ICSR_PENDSVCLR = 0x8000000 // Bit PENDSVCLR.
SCB_ICSR_PENDSVCLR_PENDSVCLR_0 = 0x0 // no effect
SCB_ICSR_PENDSVCLR_PENDSVCLR_1 = 0x1 // removes the pending state from the PendSV exception
SCB_ICSR_PENDSVSET_Pos = 0x1c // Position of PENDSVSET field.
SCB_ICSR_PENDSVSET_Msk = 0x10000000 // Bit mask of PENDSVSET field.
SCB_ICSR_PENDSVSET = 0x10000000 // Bit PENDSVSET.
SCB_ICSR_PENDSVSET_PENDSVSET_0 = 0x0 // write: no effect; read: PendSV exception is not pending
SCB_ICSR_PENDSVSET_PENDSVSET_1 = 0x1 // write: changes PendSV exception state to pending; read: PendSV exception is pending
SCB_ICSR_NMIPENDSET_Pos = 0x1f // Position of NMIPENDSET field.
SCB_ICSR_NMIPENDSET_Msk = 0x80000000 // Bit mask of NMIPENDSET field.
SCB_ICSR_NMIPENDSET = 0x80000000 // Bit NMIPENDSET.
SCB_ICSR_NMIPENDSET_NMIPENDSET_0 = 0x0 // write: no effect; read: NMI exception is not pending
SCB_ICSR_NMIPENDSET_NMIPENDSET_1 = 0x1 // write: changes NMI exception state to pending; read: NMI exception is pending
// VTOR: Vector Table Offset Register
SCB_VTOR_TBLOFF_Pos = 0x7 // Position of TBLOFF field.
SCB_VTOR_TBLOFF_Msk = 0xffffff80 // Bit mask of TBLOFF field.
// AIRCR: Application Interrupt and Reset Control Register
SCB_AIRCR_VECTRESET_Pos = 0x0 // Position of VECTRESET field.
SCB_AIRCR_VECTRESET_Msk = 0x1 // Bit mask of VECTRESET field.
SCB_AIRCR_VECTRESET = 0x1 // Bit VECTRESET.
SCB_AIRCR_VECTRESET_VECTRESET_0 = 0x0 // No change
SCB_AIRCR_VECTRESET_VECTRESET_1 = 0x1 // Causes a local system reset
SCB_AIRCR_VECTCLRACTIVE_Pos = 0x1 // Position of VECTCLRACTIVE field.
SCB_AIRCR_VECTCLRACTIVE_Msk = 0x2 // Bit mask of VECTCLRACTIVE field.
SCB_AIRCR_VECTCLRACTIVE = 0x2 // Bit VECTCLRACTIVE.
SCB_AIRCR_VECTCLRACTIVE_VECTCLRACTIVE_0 = 0x0 // No change
SCB_AIRCR_VECTCLRACTIVE_VECTCLRACTIVE_1 = 0x1 // Clears all active state information for fixed and configurable exceptions
SCB_AIRCR_SYSRESETREQ_Pos = 0x2 // Position of SYSRESETREQ field.
SCB_AIRCR_SYSRESETREQ_Msk = 0x4 // Bit mask of SYSRESETREQ field.
SCB_AIRCR_SYSRESETREQ = 0x4 // Bit SYSRESETREQ.
SCB_AIRCR_SYSRESETREQ_SYSRESETREQ_0 = 0x0 // no system reset request
SCB_AIRCR_SYSRESETREQ_SYSRESETREQ_1 = 0x1 // asserts a signal to the outer system that requests a reset
SCB_AIRCR_PRIGROUP_Pos = 0x8 // Position of PRIGROUP field.
SCB_AIRCR_PRIGROUP_Msk = 0x700 // Bit mask of PRIGROUP field.
SCB_AIRCR_ENDIANNESS_Pos = 0xf // Position of ENDIANNESS field.
SCB_AIRCR_ENDIANNESS_Msk = 0x8000 // Bit mask of ENDIANNESS field.
SCB_AIRCR_ENDIANNESS = 0x8000 // Bit ENDIANNESS.
SCB_AIRCR_ENDIANNESS_ENDIANNESS_0 = 0x0 // Little-endian
SCB_AIRCR_ENDIANNESS_ENDIANNESS_1 = 0x1 // Big-endian
SCB_AIRCR_VECTKEY_Pos = 0x10 // Position of VECTKEY field.
SCB_AIRCR_VECTKEY_Msk = 0xffff0000 // Bit mask of VECTKEY field.
// SCR: System Control Register
SCB_SCR_SLEEPONEXIT_Pos = 0x1 // Position of SLEEPONEXIT field.
SCB_SCR_SLEEPONEXIT_Msk = 0x2 // Bit mask of SLEEPONEXIT field.
SCB_SCR_SLEEPONEXIT = 0x2 // Bit SLEEPONEXIT.
SCB_SCR_SLEEPONEXIT_SLEEPONEXIT_0 = 0x0 // o not sleep when returning to Thread mode
SCB_SCR_SLEEPONEXIT_SLEEPONEXIT_1 = 0x1 // enter sleep, or deep sleep, on return from an ISR
SCB_SCR_SLEEPDEEP_Pos = 0x2 // Position of SLEEPDEEP field.
SCB_SCR_SLEEPDEEP_Msk = 0x4 // Bit mask of SLEEPDEEP field.
SCB_SCR_SLEEPDEEP = 0x4 // Bit SLEEPDEEP.
SCB_SCR_SLEEPDEEP_SLEEPDEEP_0 = 0x0 // sleep
SCB_SCR_SLEEPDEEP_SLEEPDEEP_1 = 0x1 // deep sleep
SCB_SCR_SEVONPEND_Pos = 0x4 // Position of SEVONPEND field.
SCB_SCR_SEVONPEND_Msk = 0x10 // Bit mask of SEVONPEND field.
SCB_SCR_SEVONPEND = 0x10 // Bit SEVONPEND.
SCB_SCR_SEVONPEND_SEVONPEND_0 = 0x0 // only enabled interrupts or events can wakeup the processor, disabled interrupts are excluded
SCB_SCR_SEVONPEND_SEVONPEND_1 = 0x1 // enabled events and all interrupts, including disabled interrupts, can wakeup the processor
// CCR: Configuration and Control Register
SCB_CCR_NONBASETHRDENA_Pos = 0x0 // Position of NONBASETHRDENA field.
SCB_CCR_NONBASETHRDENA_Msk = 0x1 // Bit mask of NONBASETHRDENA field.
SCB_CCR_NONBASETHRDENA = 0x1 // Bit NONBASETHRDENA.
SCB_CCR_NONBASETHRDENA_NONBASETHRDENA_0 = 0x0 // processor can enter Thread mode only when no exception is active
SCB_CCR_NONBASETHRDENA_NONBASETHRDENA_1 = 0x1 // processor can enter Thread mode from any level under the control of an EXC_RETURN value
SCB_CCR_USERSETMPEND_Pos = 0x1 // Position of USERSETMPEND field.
SCB_CCR_USERSETMPEND_Msk = 0x2 // Bit mask of USERSETMPEND field.
SCB_CCR_USERSETMPEND = 0x2 // Bit USERSETMPEND.
SCB_CCR_USERSETMPEND_USERSETMPEND_0 = 0x0 // disable
SCB_CCR_USERSETMPEND_USERSETMPEND_1 = 0x1 // enable
SCB_CCR_UNALIGN_TRP_Pos = 0x3 // Position of UNALIGN_TRP field.
SCB_CCR_UNALIGN_TRP_Msk = 0x8 // Bit mask of UNALIGN_TRP field.
SCB_CCR_UNALIGN_TRP = 0x8 // Bit UNALIGN_TRP.
SCB_CCR_UNALIGN_TRP_UNALIGN_TRP_0 = 0x0 // do not trap unaligned halfword and word accesses
SCB_CCR_UNALIGN_TRP_UNALIGN_TRP_1 = 0x1 // trap unaligned halfword and word accesses
SCB_CCR_DIV_0_TRP_Pos = 0x4 // Position of DIV_0_TRP field.
SCB_CCR_DIV_0_TRP_Msk = 0x10 // Bit mask of DIV_0_TRP field.
SCB_CCR_DIV_0_TRP = 0x10 // Bit DIV_0_TRP.
SCB_CCR_DIV_0_TRP_DIV_0_TRP_0 = 0x0 // do not trap divide by 0
SCB_CCR_DIV_0_TRP_DIV_0_TRP_1 = 0x1 // trap divide by 0
SCB_CCR_BFHFNMIGN_Pos = 0x8 // Position of BFHFNMIGN field.
SCB_CCR_BFHFNMIGN_Msk = 0x100 // Bit mask of BFHFNMIGN field.
SCB_CCR_BFHFNMIGN = 0x100 // Bit BFHFNMIGN.
SCB_CCR_BFHFNMIGN_BFHFNMIGN_0 = 0x0 // data bus faults caused by load and store instructions cause a lock-up
SCB_CCR_BFHFNMIGN_BFHFNMIGN_1 = 0x1 // handlers running at priority -1 and -2 ignore data bus faults caused by load and store instructions
SCB_CCR_STKALIGN_Pos = 0x9 // Position of STKALIGN field.
SCB_CCR_STKALIGN_Msk = 0x200 // Bit mask of STKALIGN field.
SCB_CCR_STKALIGN = 0x200 // Bit STKALIGN.
SCB_CCR_STKALIGN_STKALIGN_0 = 0x0 // 4-byte aligned
SCB_CCR_STKALIGN_STKALIGN_1 = 0x1 // 8-byte aligned
SCB_CCR_DC_Pos = 0x10 // Position of DC field.
SCB_CCR_DC_Msk = 0x10000 // Bit mask of DC field.
SCB_CCR_DC = 0x10000 // Bit DC.
SCB_CCR_DC_DC_0 = 0x0 // L1 data cache disabled
SCB_CCR_DC_DC_1 = 0x1 // L1 data cache enabled
SCB_CCR_IC_Pos = 0x11 // Position of IC field.
SCB_CCR_IC_Msk = 0x20000 // Bit mask of IC field.
SCB_CCR_IC = 0x20000 // Bit IC.
SCB_CCR_IC_IC_0 = 0x0 // L1 instruction cache disabled
SCB_CCR_IC_IC_1 = 0x1 // L1 instruction cache enabled
SCB_CCR_BP_Pos = 0x12 // Position of BP field.
SCB_CCR_BP_Msk = 0x40000 // Bit mask of BP field.
SCB_CCR_BP = 0x40000 // Bit BP.
// SHPR1: System Handler Priority Register 1
SCB_SHPR1_PRI_4_Pos = 0x0 // Position of PRI_4 field.
SCB_SHPR1_PRI_4_Msk = 0xff // Bit mask of PRI_4 field.
SCB_SHPR1_PRI_5_Pos = 0x8 // Position of PRI_5 field.
SCB_SHPR1_PRI_5_Msk = 0xff00 // Bit mask of PRI_5 field.
SCB_SHPR1_PRI_6_Pos = 0x10 // Position of PRI_6 field.
SCB_SHPR1_PRI_6_Msk = 0xff0000 // Bit mask of PRI_6 field.
// SHPR2: System Handler Priority Register 2
SCB_SHPR2_PRI_11_Pos = 0x18 // Position of PRI_11 field.
SCB_SHPR2_PRI_11_Msk = 0xff000000 // Bit mask of PRI_11 field.
// SHPR3: System Handler Priority Register 3
SCB_SHPR3_PRI_14_Pos = 0x10 // Position of PRI_14 field.
SCB_SHPR3_PRI_14_Msk = 0xff0000 // Bit mask of PRI_14 field.
SCB_SHPR3_PRI_15_Pos = 0x18 // Position of PRI_15 field.
SCB_SHPR3_PRI_15_Msk = 0xff000000 // Bit mask of PRI_15 field.
// SHCSR: System Handler Control and State Register
SCB_SHCSR_MEMFAULTACT_Pos = 0x0 // Position of MEMFAULTACT field.
SCB_SHCSR_MEMFAULTACT_Msk = 0x1 // Bit mask of MEMFAULTACT field.
SCB_SHCSR_MEMFAULTACT = 0x1 // Bit MEMFAULTACT.
SCB_SHCSR_MEMFAULTACT_MEMFAULTACT_0 = 0x0 // exception is not active
SCB_SHCSR_MEMFAULTACT_MEMFAULTACT_1 = 0x1 // exception is active
SCB_SHCSR_BUSFAULTACT_Pos = 0x1 // Position of BUSFAULTACT field.
SCB_SHCSR_BUSFAULTACT_Msk = 0x2 // Bit mask of BUSFAULTACT field.
SCB_SHCSR_BUSFAULTACT = 0x2 // Bit BUSFAULTACT.
SCB_SHCSR_BUSFAULTACT_BUSFAULTACT_0 = 0x0 // exception is not active
SCB_SHCSR_BUSFAULTACT_BUSFAULTACT_1 = 0x1 // exception is active
SCB_SHCSR_USGFAULTACT_Pos = 0x3 // Position of USGFAULTACT field.
SCB_SHCSR_USGFAULTACT_Msk = 0x8 // Bit mask of USGFAULTACT field.
SCB_SHCSR_USGFAULTACT = 0x8 // Bit USGFAULTACT.
SCB_SHCSR_USGFAULTACT_USGFAULTACT_0 = 0x0 // exception is not active
SCB_SHCSR_USGFAULTACT_USGFAULTACT_1 = 0x1 // exception is active
SCB_SHCSR_SVCALLACT_Pos = 0x7 // Position of SVCALLACT field.
SCB_SHCSR_SVCALLACT_Msk = 0x80 // Bit mask of SVCALLACT field.
SCB_SHCSR_SVCALLACT = 0x80 // Bit SVCALLACT.
SCB_SHCSR_SVCALLACT_SVCALLACT_0 = 0x0 // exception is not active
SCB_SHCSR_SVCALLACT_SVCALLACT_1 = 0x1 // exception is active
SCB_SHCSR_MONITORACT_Pos = 0x8 // Position of MONITORACT field.
SCB_SHCSR_MONITORACT_Msk = 0x100 // Bit mask of MONITORACT field.
SCB_SHCSR_MONITORACT = 0x100 // Bit MONITORACT.
SCB_SHCSR_MONITORACT_MONITORACT_0 = 0x0 // exception is not active
SCB_SHCSR_MONITORACT_MONITORACT_1 = 0x1 // exception is active
SCB_SHCSR_PENDSVACT_Pos = 0xa // Position of PENDSVACT field.
SCB_SHCSR_PENDSVACT_Msk = 0x400 // Bit mask of PENDSVACT field.
SCB_SHCSR_PENDSVACT = 0x400 // Bit PENDSVACT.
SCB_SHCSR_PENDSVACT_PENDSVACT_0 = 0x0 // exception is not active
SCB_SHCSR_PENDSVACT_PENDSVACT_1 = 0x1 // exception is active
SCB_SHCSR_SYSTICKACT_Pos = 0xb // Position of SYSTICKACT field.
SCB_SHCSR_SYSTICKACT_Msk = 0x800 // Bit mask of SYSTICKACT field.
SCB_SHCSR_SYSTICKACT = 0x800 // Bit SYSTICKACT.
SCB_SHCSR_SYSTICKACT_SYSTICKACT_0 = 0x0 // exception is not active
SCB_SHCSR_SYSTICKACT_SYSTICKACT_1 = 0x1 // exception is active
SCB_SHCSR_USGFAULTPENDED_Pos = 0xc // Position of USGFAULTPENDED field.
SCB_SHCSR_USGFAULTPENDED_Msk = 0x1000 // Bit mask of USGFAULTPENDED field.
SCB_SHCSR_USGFAULTPENDED = 0x1000 // Bit USGFAULTPENDED.
SCB_SHCSR_USGFAULTPENDED_USGFAULTPENDED_0 = 0x0 // exception is not pending
SCB_SHCSR_USGFAULTPENDED_USGFAULTPENDED_1 = 0x1 // exception is pending
SCB_SHCSR_MEMFAULTPENDED_Pos = 0xd // Position of MEMFAULTPENDED field.
SCB_SHCSR_MEMFAULTPENDED_Msk = 0x2000 // Bit mask of MEMFAULTPENDED field.
SCB_SHCSR_MEMFAULTPENDED = 0x2000 // Bit MEMFAULTPENDED.
SCB_SHCSR_MEMFAULTPENDED_MEMFAULTPENDED_0 = 0x0 // exception is not pending
SCB_SHCSR_MEMFAULTPENDED_MEMFAULTPENDED_1 = 0x1 // exception is pending
SCB_SHCSR_BUSFAULTPENDED_Pos = 0xe // Position of BUSFAULTPENDED field.
SCB_SHCSR_BUSFAULTPENDED_Msk = 0x4000 // Bit mask of BUSFAULTPENDED field.
SCB_SHCSR_BUSFAULTPENDED = 0x4000 // Bit BUSFAULTPENDED.
SCB_SHCSR_BUSFAULTPENDED_BUSFAULTPENDED_0 = 0x0 // exception is not pending
SCB_SHCSR_BUSFAULTPENDED_BUSFAULTPENDED_1 = 0x1 // exception is pending
SCB_SHCSR_SVCALLPENDED_Pos = 0xf // Position of SVCALLPENDED field.
SCB_SHCSR_SVCALLPENDED_Msk = 0x8000 // Bit mask of SVCALLPENDED field.
SCB_SHCSR_SVCALLPENDED = 0x8000 // Bit SVCALLPENDED.
SCB_SHCSR_SVCALLPENDED_SVCALLPENDED_0 = 0x0 // exception is not pending
SCB_SHCSR_SVCALLPENDED_SVCALLPENDED_1 = 0x1 // exception is pending
SCB_SHCSR_MEMFAULTENA_Pos = 0x10 // Position of MEMFAULTENA field.
SCB_SHCSR_MEMFAULTENA_Msk = 0x10000 // Bit mask of MEMFAULTENA field.
SCB_SHCSR_MEMFAULTENA = 0x10000 // Bit MEMFAULTENA.
SCB_SHCSR_MEMFAULTENA_MEMFAULTENA_0 = 0x0 // disable the exception
SCB_SHCSR_MEMFAULTENA_MEMFAULTENA_1 = 0x1 // enable the exception
SCB_SHCSR_BUSFAULTENA_Pos = 0x11 // Position of BUSFAULTENA field.
SCB_SHCSR_BUSFAULTENA_Msk = 0x20000 // Bit mask of BUSFAULTENA field.
SCB_SHCSR_BUSFAULTENA = 0x20000 // Bit BUSFAULTENA.
SCB_SHCSR_BUSFAULTENA_BUSFAULTENA_0 = 0x0 // disable the exception
SCB_SHCSR_BUSFAULTENA_BUSFAULTENA_1 = 0x1 // enable the exception
SCB_SHCSR_USGFAULTENA_Pos = 0x12 // Position of USGFAULTENA field.
SCB_SHCSR_USGFAULTENA_Msk = 0x40000 // Bit mask of USGFAULTENA field.
SCB_SHCSR_USGFAULTENA = 0x40000 // Bit USGFAULTENA.
SCB_SHCSR_USGFAULTENA_USGFAULTENA_0 = 0x0 // disable the exception
SCB_SHCSR_USGFAULTENA_USGFAULTENA_1 = 0x1 // enable the exception
// CFSR: Configurable Fault Status Register
SCB_CFSR_IACCVIOL_Pos = 0x0 // Position of IACCVIOL field.
SCB_CFSR_IACCVIOL_Msk = 0x1 // Bit mask of IACCVIOL field.
SCB_CFSR_IACCVIOL = 0x1 // Bit IACCVIOL.
SCB_CFSR_IACCVIOL_IACCVIOL_0 = 0x0 // no instruction access violation fault
SCB_CFSR_IACCVIOL_IACCVIOL_1 = 0x1 // the processor attempted an instruction fetch from a location that does not permit execution
SCB_CFSR_DACCVIOL_Pos = 0x1 // Position of DACCVIOL field.
SCB_CFSR_DACCVIOL_Msk = 0x2 // Bit mask of DACCVIOL field.
SCB_CFSR_DACCVIOL = 0x2 // Bit DACCVIOL.
SCB_CFSR_DACCVIOL_DACCVIOL_0 = 0x0 // no data access violation fault
SCB_CFSR_DACCVIOL_DACCVIOL_1 = 0x1 // the processor attempted a load or store at a location that does not permit the operation
SCB_CFSR_MUNSTKERR_Pos = 0x3 // Position of MUNSTKERR field.
SCB_CFSR_MUNSTKERR_Msk = 0x8 // Bit mask of MUNSTKERR field.
SCB_CFSR_MUNSTKERR = 0x8 // Bit MUNSTKERR.
SCB_CFSR_MUNSTKERR_MUNSTKERR_0 = 0x0 // no unstacking fault
SCB_CFSR_MUNSTKERR_MUNSTKERR_1 = 0x1 // unstack for an exception return has caused one or more access violations
SCB_CFSR_MSTKERR_Pos = 0x4 // Position of MSTKERR field.
SCB_CFSR_MSTKERR_Msk = 0x10 // Bit mask of MSTKERR field.
SCB_CFSR_MSTKERR = 0x10 // Bit MSTKERR.
SCB_CFSR_MSTKERR_MSTKERR_0 = 0x0 // no stacking fault
SCB_CFSR_MSTKERR_MSTKERR_1 = 0x1 // stacking for an exception entry has caused one or more access violations
SCB_CFSR_MLSPERR_Pos = 0x5 // Position of MLSPERR field.
SCB_CFSR_MLSPERR_Msk = 0x20 // Bit mask of MLSPERR field.
SCB_CFSR_MLSPERR = 0x20 // Bit MLSPERR.
SCB_CFSR_MLSPERR_MLSPERR_0 = 0x0 // No MemManage fault occurred during floating-point lazy state preservation
SCB_CFSR_MLSPERR_MLSPERR_1 = 0x1 // A MemManage fault occurred during floating-point lazy state preservation
SCB_CFSR_MMARVALID_Pos = 0x7 // Position of MMARVALID field.
SCB_CFSR_MMARVALID_Msk = 0x80 // Bit mask of MMARVALID field.
SCB_CFSR_MMARVALID = 0x80 // Bit MMARVALID.
SCB_CFSR_MMARVALID_MMARVALID_0 = 0x0 // value in MMAR is not a valid fault address
SCB_CFSR_MMARVALID_MMARVALID_1 = 0x1 // MMAR holds a valid fault address
SCB_CFSR_IBUSERR_Pos = 0x8 // Position of IBUSERR field.
SCB_CFSR_IBUSERR_Msk = 0x100 // Bit mask of IBUSERR field.
SCB_CFSR_IBUSERR = 0x100 // Bit IBUSERR.
SCB_CFSR_IBUSERR_IBUSERR_0 = 0x0 // no instruction bus error
SCB_CFSR_IBUSERR_IBUSERR_1 = 0x1 // instruction bus error
SCB_CFSR_PRECISERR_Pos = 0x9 // Position of PRECISERR field.
SCB_CFSR_PRECISERR_Msk = 0x200 // Bit mask of PRECISERR field.
SCB_CFSR_PRECISERR = 0x200 // Bit PRECISERR.
SCB_CFSR_PRECISERR_PRECISERR_0 = 0x0 // no precise data bus error
SCB_CFSR_PRECISERR_PRECISERR_1 = 0x1 // a data bus error has occurred, and the PC value stacked for the exception return points to the instruction that caused the fault
SCB_CFSR_IMPRECISERR_Pos = 0xa // Position of IMPRECISERR field.
SCB_CFSR_IMPRECISERR_Msk = 0x400 // Bit mask of IMPRECISERR field.
SCB_CFSR_IMPRECISERR = 0x400 // Bit IMPRECISERR.
SCB_CFSR_IMPRECISERR_IMPRECISERR_0 = 0x0 // no imprecise data bus error
SCB_CFSR_IMPRECISERR_IMPRECISERR_1 = 0x1 // a data bus error has occurred, but the return address in the stack frame is not related to the instruction that caused the error
SCB_CFSR_UNSTKERR_Pos = 0xb // Position of UNSTKERR field.
SCB_CFSR_UNSTKERR_Msk = 0x800 // Bit mask of UNSTKERR field.
SCB_CFSR_UNSTKERR = 0x800 // Bit UNSTKERR.
SCB_CFSR_UNSTKERR_UNSTKERR_0 = 0x0 // no unstacking fault
SCB_CFSR_UNSTKERR_UNSTKERR_1 = 0x1 // unstack for an exception return has caused one or more BusFaults
SCB_CFSR_STKERR_Pos = 0xc // Position of STKERR field.
SCB_CFSR_STKERR_Msk = 0x1000 // Bit mask of STKERR field.
SCB_CFSR_STKERR = 0x1000 // Bit STKERR.
SCB_CFSR_STKERR_STKERR_0 = 0x0 // no stacking fault
SCB_CFSR_STKERR_STKERR_1 = 0x1 // stacking for an exception entry has caused one or more BusFaults
SCB_CFSR_LSPERR_Pos = 0xd // Position of LSPERR field.
SCB_CFSR_LSPERR_Msk = 0x2000 // Bit mask of LSPERR field.
SCB_CFSR_LSPERR = 0x2000 // Bit LSPERR.
SCB_CFSR_LSPERR_LSPERR_0 = 0x0 // No bus fault occurred during floating-point lazy state preservation
SCB_CFSR_LSPERR_LSPERR_1 = 0x1 // A bus fault occurred during floating-point lazy state preservation
SCB_CFSR_BFARVALID_Pos = 0xf // Position of BFARVALID field.
SCB_CFSR_BFARVALID_Msk = 0x8000 // Bit mask of BFARVALID field.
SCB_CFSR_BFARVALID = 0x8000 // Bit BFARVALID.
SCB_CFSR_BFARVALID_BFARVALID_0 = 0x0 // value in BFAR is not a valid fault address
SCB_CFSR_BFARVALID_BFARVALID_1 = 0x1 // BFAR holds a valid fault address
SCB_CFSR_UNDEFINSTR_Pos = 0x10 // Position of UNDEFINSTR field.
SCB_CFSR_UNDEFINSTR_Msk = 0x10000 // Bit mask of UNDEFINSTR field.
SCB_CFSR_UNDEFINSTR = 0x10000 // Bit UNDEFINSTR.
SCB_CFSR_UNDEFINSTR_UNDEFINSTR_0 = 0x0 // no undefined instruction UsageFault
SCB_CFSR_UNDEFINSTR_UNDEFINSTR_1 = 0x1 // the processor has attempted to execute an undefined instruction
SCB_CFSR_INVSTATE_Pos = 0x11 // Position of INVSTATE field.
SCB_CFSR_INVSTATE_Msk = 0x20000 // Bit mask of INVSTATE field.
SCB_CFSR_INVSTATE = 0x20000 // Bit INVSTATE.
SCB_CFSR_INVSTATE_INVSTATE_0 = 0x0 // no invalid state UsageFault
SCB_CFSR_INVSTATE_INVSTATE_1 = 0x1 // the processor has attempted to execute an instruction that makes illegal use of the EPSR
SCB_CFSR_INVPC_Pos = 0x12 // Position of INVPC field.
SCB_CFSR_INVPC_Msk = 0x40000 // Bit mask of INVPC field.
SCB_CFSR_INVPC = 0x40000 // Bit INVPC.
SCB_CFSR_INVPC_INVPC_0 = 0x0 // no invalid PC load UsageFault
SCB_CFSR_INVPC_INVPC_1 = 0x1 // the processor has attempted an illegal load of EXC_RETURN to the PC
SCB_CFSR_NOCP_Pos = 0x13 // Position of NOCP field.
SCB_CFSR_NOCP_Msk = 0x80000 // Bit mask of NOCP field.
SCB_CFSR_NOCP = 0x80000 // Bit NOCP.
SCB_CFSR_NOCP_NOCP_0 = 0x0 // no UsageFault caused by attempting to access a coprocessor
SCB_CFSR_NOCP_NOCP_1 = 0x1 // the processor has attempted to access a coprocessor
SCB_CFSR_UNALIGNED_Pos = 0x18 // Position of UNALIGNED field.
SCB_CFSR_UNALIGNED_Msk = 0x1000000 // Bit mask of UNALIGNED field.
SCB_CFSR_UNALIGNED = 0x1000000 // Bit UNALIGNED.
SCB_CFSR_UNALIGNED_UNALIGNED_0 = 0x0 // no unaligned access fault, or unaligned access trapping not enabled
SCB_CFSR_UNALIGNED_UNALIGNED_1 = 0x1 // the processor has made an unaligned memory access
SCB_CFSR_DIVBYZERO_Pos = 0x19 // Position of DIVBYZERO field.
SCB_CFSR_DIVBYZERO_Msk = 0x2000000 // Bit mask of DIVBYZERO field.
SCB_CFSR_DIVBYZERO = 0x2000000 // Bit DIVBYZERO.
SCB_CFSR_DIVBYZERO_DIVBYZERO_0 = 0x0 // no divide by zero fault, or divide by zero trapping not enabled
SCB_CFSR_DIVBYZERO_DIVBYZERO_1 = 0x1 // the processor has executed an SDIV or UDIV instruction with a divisor of 0
// HFSR: HardFault Status register
SCB_HFSR_VECTTBL_Pos = 0x1 // Position of VECTTBL field.
SCB_HFSR_VECTTBL_Msk = 0x2 // Bit mask of VECTTBL field.
SCB_HFSR_VECTTBL = 0x2 // Bit VECTTBL.
SCB_HFSR_VECTTBL_VECTTBL_0 = 0x0 // no BusFault on vector table read
SCB_HFSR_VECTTBL_VECTTBL_1 = 0x1 // BusFault on vector table read
SCB_HFSR_FORCED_Pos = 0x1e // Position of FORCED field.
SCB_HFSR_FORCED_Msk = 0x40000000 // Bit mask of FORCED field.
SCB_HFSR_FORCED = 0x40000000 // Bit FORCED.
SCB_HFSR_FORCED_FORCED_0 = 0x0 // no forced HardFault
SCB_HFSR_FORCED_FORCED_1 = 0x1 // forced HardFault
SCB_HFSR_DEBUGEVT_Pos = 0x1f // Position of DEBUGEVT field.
SCB_HFSR_DEBUGEVT_Msk = 0x80000000 // Bit mask of DEBUGEVT field.
SCB_HFSR_DEBUGEVT = 0x80000000 // Bit DEBUGEVT.
SCB_HFSR_DEBUGEVT_DEBUGEVT_0 = 0x0 // No Debug event has occurred.
SCB_HFSR_DEBUGEVT_DEBUGEVT_1 = 0x1 // Debug event has occurred. The Debug Fault Status Register has been updated.
// DFSR: Debug Fault Status Register
SCB_DFSR_HALTED_Pos = 0x0 // Position of HALTED field.
SCB_DFSR_HALTED_Msk = 0x1 // Bit mask of HALTED field.
SCB_DFSR_HALTED = 0x1 // Bit HALTED.
SCB_DFSR_HALTED_HALTED_0 = 0x0 // No active halt request debug event
SCB_DFSR_HALTED_HALTED_1 = 0x1 // Halt request debug event active
SCB_DFSR_BKPT_Pos = 0x1 // Position of BKPT field.
SCB_DFSR_BKPT_Msk = 0x2 // Bit mask of BKPT field.
SCB_DFSR_BKPT = 0x2 // Bit BKPT.
SCB_DFSR_BKPT_BKPT_0 = 0x0 // No current breakpoint debug event
SCB_DFSR_BKPT_BKPT_1 = 0x1 // At least one current breakpoint debug event
SCB_DFSR_DWTTRAP_Pos = 0x2 // Position of DWTTRAP field.
SCB_DFSR_DWTTRAP_Msk = 0x4 // Bit mask of DWTTRAP field.
SCB_DFSR_DWTTRAP = 0x4 // Bit DWTTRAP.
SCB_DFSR_DWTTRAP_DWTTRAP_0 = 0x0 // No current debug events generated by the DWT
SCB_DFSR_DWTTRAP_DWTTRAP_1 = 0x1 // At least one current debug event generated by the DWT
SCB_DFSR_VCATCH_Pos = 0x3 // Position of VCATCH field.
SCB_DFSR_VCATCH_Msk = 0x8 // Bit mask of VCATCH field.
SCB_DFSR_VCATCH = 0x8 // Bit VCATCH.
SCB_DFSR_VCATCH_VCATCH_0 = 0x0 // No Vector catch triggered
SCB_DFSR_VCATCH_VCATCH_1 = 0x1 // Vector catch triggered
SCB_DFSR_EXTERNAL_Pos = 0x4 // Position of EXTERNAL field.
SCB_DFSR_EXTERNAL_Msk = 0x10 // Bit mask of EXTERNAL field.
SCB_DFSR_EXTERNAL = 0x10 // Bit EXTERNAL.
SCB_DFSR_EXTERNAL_EXTERNAL_0 = 0x0 // No external debug request debug event
SCB_DFSR_EXTERNAL_EXTERNAL_1 = 0x1 // External debug request debug event
// MMFAR: MemManage Fault Address Register
SCB_MMFAR_ADDRESS_Pos = 0x0 // Position of ADDRESS field.
SCB_MMFAR_ADDRESS_Msk = 0xffffffff // Bit mask of ADDRESS field.
// BFAR: BusFault Address Register
SCB_BFAR_ADDRESS_Pos = 0x0 // Position of ADDRESS field.
SCB_BFAR_ADDRESS_Msk = 0xffffffff // Bit mask of ADDRESS field.
)
+36
View File
@@ -0,0 +1,36 @@
package arm64
// Run the given assembly code. The code will be marked as having side effects,
// as it doesn't produce output and thus would normally be eliminated by the
// optimizer.
func Asm(asm string)
// Run the given inline assembly. The code will be marked as having side
// effects, as it would otherwise be optimized away. The inline assembly string
// recognizes template values in the form {name}, like so:
//
// arm.AsmFull(
// "str {value}, {result}",
// map[string]interface{}{
// "value": 1
// "result": &dest,
// })
//
// You can use {} in the asm string (which expands to a register) to set the
// return value.
func AsmFull(asm string, regs map[string]interface{}) uintptr
// Run the following system call (SVCall) with 0 arguments.
func SVCall0(num uintptr) uintptr
// Run the following system call (SVCall) with 1 argument.
func SVCall1(num uintptr, a1 interface{}) uintptr
// Run the following system call (SVCall) with 2 arguments.
func SVCall2(num uintptr, a1, a2 interface{}) uintptr
// Run the following system call (SVCall) with 3 arguments.
func SVCall3(num uintptr, a1, a2, a3 interface{}) uintptr
// Run the following system call (SVCall) with 4 arguments.
func SVCall4(num uintptr, a1, a2, a3, a4 interface{}) uintptr
+7 -2
View File
@@ -24,7 +24,7 @@ call_start_cpu0:
wsr.ps a2
rsync
// Set WINDOWBASE to 1 << WINDOWSTART.
// Set WINDOWSTART to 1 << WINDOWBASE.
rsr.windowbase a2
ssl a2
movi a2, 1
@@ -42,8 +42,13 @@ call_start_cpu0:
wsr.ps a2
rsync
// Enable the FPU (coprocessor 0 so the lowest bit).
movi a2, 1
wsr.cpenable a2
rsync
// Jump to the runtime start function written in Go.
j main
call4 main
.section .text.tinygo_scanCurrentStack
.global tinygo_scanCurrentStack
+29
View File
@@ -0,0 +1,29 @@
// Hand created file. DO NOT DELETE.
// Hardfault aliases for definitions that have inconsistent naming (which are
// auto-generated by gen-device-svd.go) among devices in package nxp.
// +build nxp,!mimxrt1062
package nxp
const (
HardFault_CFSR_IACCVIOL = SystemControl_CFSR_IACCVIOL
HardFault_CFSR_DACCVIOL = SystemControl_CFSR_DACCVIOL
HardFault_CFSR_MUNSTKERR = SystemControl_CFSR_MUNSTKERR
HardFault_CFSR_MSTKERR = SystemControl_CFSR_MSTKERR
HardFault_CFSR_MLSPERR = SystemControl_CFSR_MLSPERR
HardFault_CFSR_IBUSERR = SystemControl_CFSR_IBUSERR
HardFault_CFSR_PRECISERR = SystemControl_CFSR_PRECISERR
HardFault_CFSR_IMPRECISERR = SystemControl_CFSR_IMPRECISERR
HardFault_CFSR_UNSTKERR = SystemControl_CFSR_UNSTKERR
HardFault_CFSR_STKERR = SystemControl_CFSR_STKERR
HardFault_CFSR_LSPERR = SystemControl_CFSR_LSPERR
HardFault_CFSR_UNDEFINSTR = SystemControl_CFSR_UNDEFINSTR
HardFault_CFSR_INVSTATE = SystemControl_CFSR_INVSTATE
HardFault_CFSR_INVPC = SystemControl_CFSR_INVPC
HardFault_CFSR_NOCP = SystemControl_CFSR_NOCP
HardFault_CFSR_UNALIGNED = SystemControl_CFSR_UNALIGNED
HardFault_CFSR_DIVBYZERO = SystemControl_CFSR_DIVBYZERO
HardFault_CFSR_MMARVALID = SystemControl_CFSR_MMARVALID
HardFault_CFSR_BFARVALID = SystemControl_CFSR_BFARVALID
)
+529
View File
@@ -0,0 +1,529 @@
// Hand created file. DO NOT DELETE.
// Type definitions, fields, and constants associated with various clocks and
// peripherals of the NXP MIMXRT1062.
// +build nxp,mimxrt1062
package nxp
import (
"runtime/volatile"
"unsafe"
)
// Clock represents an individual peripheral clock that may be enabled/disabled
// at runtime. Clocks also have a method `Mux` for selecting the clock source
// and a method `Div` for selecting the hardware divisor. Note that many
// peripherals have an independent prescalar configuration applied to the output
// of this divisor.
type (
Clock uint32
ClockMode uint8
)
// Enable activates or deactivates the clock gate of receiver Clock c.
func (c Clock) Enable(enable bool) {
if enable {
c.setGate(clockNeededRunWait)
} else {
c.setGate(clockNotNeeded)
}
}
// Mux selects a clock source for the mux of the receiver Clock c.
func (c Clock) Mux(mux uint32) { c.setCcm(mux) }
// Div configures the prescalar divisor of the receiver Clock c.
func (c Clock) Div(div uint32) { c.setCcm(div) }
const (
ClockModeRun ClockMode = 0 // Remain in run mode
ClockModeWait ClockMode = 1 // Transfer to wait mode
ClockModeStop ClockMode = 2 // Transfer to stop mode
)
// Set configures the run mode of the MCU.
func (m ClockMode) Set() {
CCM.CLPCR.Set((CCM.CLPCR.Get() & ^uint32(CCM_CLPCR_LPM_Msk)) |
((uint32(m) << CCM_CLPCR_LPM_Pos) & CCM_CLPCR_LPM_Msk))
}
// Named oscillators
const (
ClockCpu Clock = 0x0 // CPU clock
ClockAhb Clock = 0x1 // AHB clock
ClockSemc Clock = 0x2 // SEMC clock
ClockIpg Clock = 0x3 // IPG clock
ClockPer Clock = 0x4 // PER clock
ClockOsc Clock = 0x5 // OSC clock selected by PMU_LOWPWR_CTRL[OSC_SEL]
ClockRtc Clock = 0x6 // RTC clock (RTCCLK)
ClockArmPll Clock = 0x7 // ARMPLLCLK
ClockUsb1Pll Clock = 0x8 // USB1PLLCLK
ClockUsb1PllPfd0 Clock = 0x9 // USB1PLLPDF0CLK
ClockUsb1PllPfd1 Clock = 0xA // USB1PLLPFD1CLK
ClockUsb1PllPfd2 Clock = 0xB // USB1PLLPFD2CLK
ClockUsb1PllPfd3 Clock = 0xC // USB1PLLPFD3CLK
ClockUsb2Pll Clock = 0xD // USB2PLLCLK
ClockSysPll Clock = 0xE // SYSPLLCLK
ClockSysPllPfd0 Clock = 0xF // SYSPLLPDF0CLK
ClockSysPllPfd1 Clock = 0x10 // SYSPLLPFD1CLK
ClockSysPllPfd2 Clock = 0x11 // SYSPLLPFD2CLK
ClockSysPllPfd3 Clock = 0x12 // SYSPLLPFD3CLK
ClockEnetPll0 Clock = 0x13 // Enet PLLCLK ref_enetpll0
ClockEnetPll1 Clock = 0x14 // Enet PLLCLK ref_enetpll1
ClockEnetPll2 Clock = 0x15 // Enet PLLCLK ref_enetpll2
ClockAudioPll Clock = 0x16 // Audio PLLCLK
ClockVideoPll Clock = 0x17 // Video PLLCLK
)
// Named clocks of integrated peripherals
const (
ClockIpAipsTz1 Clock = (0 << 8) | CCM_CCGR0_CG0_Pos // CCGR0, CG0
ClockIpAipsTz2 Clock = (0 << 8) | CCM_CCGR0_CG1_Pos // CCGR0, CG1
ClockIpMqs Clock = (0 << 8) | CCM_CCGR0_CG2_Pos // CCGR0, CG2
ClockIpFlexSpiExsc Clock = (0 << 8) | CCM_CCGR0_CG3_Pos // CCGR0, CG3
ClockIpSimMMain Clock = (0 << 8) | CCM_CCGR0_CG4_Pos // CCGR0, CG4
ClockIpDcp Clock = (0 << 8) | CCM_CCGR0_CG5_Pos // CCGR0, CG5
ClockIpLpuart3 Clock = (0 << 8) | CCM_CCGR0_CG6_Pos // CCGR0, CG6
ClockIpCan1 Clock = (0 << 8) | CCM_CCGR0_CG7_Pos // CCGR0, CG7
ClockIpCan1S Clock = (0 << 8) | CCM_CCGR0_CG8_Pos // CCGR0, CG8
ClockIpCan2 Clock = (0 << 8) | CCM_CCGR0_CG9_Pos // CCGR0, CG9
ClockIpCan2S Clock = (0 << 8) | CCM_CCGR0_CG10_Pos // CCGR0, CG10
ClockIpTrace Clock = (0 << 8) | CCM_CCGR0_CG11_Pos // CCGR0, CG11
ClockIpGpt2 Clock = (0 << 8) | CCM_CCGR0_CG12_Pos // CCGR0, CG12
ClockIpGpt2S Clock = (0 << 8) | CCM_CCGR0_CG13_Pos // CCGR0, CG13
ClockIpLpuart2 Clock = (0 << 8) | CCM_CCGR0_CG14_Pos // CCGR0, CG14
ClockIpGpio2 Clock = (0 << 8) | CCM_CCGR0_CG15_Pos // CCGR0, CG15
ClockIpLpspi1 Clock = (1 << 8) | CCM_CCGR1_CG0_Pos // CCGR1, CG0
ClockIpLpspi2 Clock = (1 << 8) | CCM_CCGR1_CG1_Pos // CCGR1, CG1
ClockIpLpspi3 Clock = (1 << 8) | CCM_CCGR1_CG2_Pos // CCGR1, CG2
ClockIpLpspi4 Clock = (1 << 8) | CCM_CCGR1_CG3_Pos // CCGR1, CG3
ClockIpAdc2 Clock = (1 << 8) | CCM_CCGR1_CG4_Pos // CCGR1, CG4
ClockIpEnet Clock = (1 << 8) | CCM_CCGR1_CG5_Pos // CCGR1, CG5
ClockIpPit Clock = (1 << 8) | CCM_CCGR1_CG6_Pos // CCGR1, CG6
ClockIpAoi2 Clock = (1 << 8) | CCM_CCGR1_CG7_Pos // CCGR1, CG7
ClockIpAdc1 Clock = (1 << 8) | CCM_CCGR1_CG8_Pos // CCGR1, CG8
ClockIpSemcExsc Clock = (1 << 8) | CCM_CCGR1_CG9_Pos // CCGR1, CG9
ClockIpGpt1 Clock = (1 << 8) | CCM_CCGR1_CG10_Pos // CCGR1, CG10
ClockIpGpt1S Clock = (1 << 8) | CCM_CCGR1_CG11_Pos // CCGR1, CG11
ClockIpLpuart4 Clock = (1 << 8) | CCM_CCGR1_CG12_Pos // CCGR1, CG12
ClockIpGpio1 Clock = (1 << 8) | CCM_CCGR1_CG13_Pos // CCGR1, CG13
ClockIpCsu Clock = (1 << 8) | CCM_CCGR1_CG14_Pos // CCGR1, CG14
ClockIpGpio5 Clock = (1 << 8) | CCM_CCGR1_CG15_Pos // CCGR1, CG15
ClockIpOcramExsc Clock = (2 << 8) | CCM_CCGR2_CG0_Pos // CCGR2, CG0
ClockIpCsi Clock = (2 << 8) | CCM_CCGR2_CG1_Pos // CCGR2, CG1
ClockIpIomuxcSnvs Clock = (2 << 8) | CCM_CCGR2_CG2_Pos // CCGR2, CG2
ClockIpLpi2c1 Clock = (2 << 8) | CCM_CCGR2_CG3_Pos // CCGR2, CG3
ClockIpLpi2c2 Clock = (2 << 8) | CCM_CCGR2_CG4_Pos // CCGR2, CG4
ClockIpLpi2c3 Clock = (2 << 8) | CCM_CCGR2_CG5_Pos // CCGR2, CG5
ClockIpOcotp Clock = (2 << 8) | CCM_CCGR2_CG6_Pos // CCGR2, CG6
ClockIpXbar3 Clock = (2 << 8) | CCM_CCGR2_CG7_Pos // CCGR2, CG7
ClockIpIpmux1 Clock = (2 << 8) | CCM_CCGR2_CG8_Pos // CCGR2, CG8
ClockIpIpmux2 Clock = (2 << 8) | CCM_CCGR2_CG9_Pos // CCGR2, CG9
ClockIpIpmux3 Clock = (2 << 8) | CCM_CCGR2_CG10_Pos // CCGR2, CG10
ClockIpXbar1 Clock = (2 << 8) | CCM_CCGR2_CG11_Pos // CCGR2, CG11
ClockIpXbar2 Clock = (2 << 8) | CCM_CCGR2_CG12_Pos // CCGR2, CG12
ClockIpGpio3 Clock = (2 << 8) | CCM_CCGR2_CG13_Pos // CCGR2, CG13
ClockIpLcd Clock = (2 << 8) | CCM_CCGR2_CG14_Pos // CCGR2, CG14
ClockIpPxp Clock = (2 << 8) | CCM_CCGR2_CG15_Pos // CCGR2, CG15
ClockIpFlexio2 Clock = (3 << 8) | CCM_CCGR3_CG0_Pos // CCGR3, CG0
ClockIpLpuart5 Clock = (3 << 8) | CCM_CCGR3_CG1_Pos // CCGR3, CG1
ClockIpSemc Clock = (3 << 8) | CCM_CCGR3_CG2_Pos // CCGR3, CG2
ClockIpLpuart6 Clock = (3 << 8) | CCM_CCGR3_CG3_Pos // CCGR3, CG3
ClockIpAoi1 Clock = (3 << 8) | CCM_CCGR3_CG4_Pos // CCGR3, CG4
ClockIpLcdPixel Clock = (3 << 8) | CCM_CCGR3_CG5_Pos // CCGR3, CG5
ClockIpGpio4 Clock = (3 << 8) | CCM_CCGR3_CG6_Pos // CCGR3, CG6
ClockIpEwm0 Clock = (3 << 8) | CCM_CCGR3_CG7_Pos // CCGR3, CG7
ClockIpWdog1 Clock = (3 << 8) | CCM_CCGR3_CG8_Pos // CCGR3, CG8
ClockIpFlexRam Clock = (3 << 8) | CCM_CCGR3_CG9_Pos // CCGR3, CG9
ClockIpAcmp1 Clock = (3 << 8) | CCM_CCGR3_CG10_Pos // CCGR3, CG10
ClockIpAcmp2 Clock = (3 << 8) | CCM_CCGR3_CG11_Pos // CCGR3, CG11
ClockIpAcmp3 Clock = (3 << 8) | CCM_CCGR3_CG12_Pos // CCGR3, CG12
ClockIpAcmp4 Clock = (3 << 8) | CCM_CCGR3_CG13_Pos // CCGR3, CG13
ClockIpOcram Clock = (3 << 8) | CCM_CCGR3_CG14_Pos // CCGR3, CG14
ClockIpIomuxcSnvsGpr Clock = (3 << 8) | CCM_CCGR3_CG15_Pos // CCGR3, CG15
ClockIpIomuxc Clock = (4 << 8) | CCM_CCGR4_CG1_Pos // CCGR4, CG1
ClockIpIomuxcGpr Clock = (4 << 8) | CCM_CCGR4_CG2_Pos // CCGR4, CG2
ClockIpBee Clock = (4 << 8) | CCM_CCGR4_CG3_Pos // CCGR4, CG3
ClockIpSimM7 Clock = (4 << 8) | CCM_CCGR4_CG4_Pos // CCGR4, CG4
ClockIpTsc Clock = (4 << 8) | CCM_CCGR4_CG5_Pos // CCGR4, CG5
ClockIpSimM Clock = (4 << 8) | CCM_CCGR4_CG6_Pos // CCGR4, CG6
ClockIpSimEms Clock = (4 << 8) | CCM_CCGR4_CG7_Pos // CCGR4, CG7
ClockIpPwm1 Clock = (4 << 8) | CCM_CCGR4_CG8_Pos // CCGR4, CG8
ClockIpPwm2 Clock = (4 << 8) | CCM_CCGR4_CG9_Pos // CCGR4, CG9
ClockIpPwm3 Clock = (4 << 8) | CCM_CCGR4_CG10_Pos // CCGR4, CG10
ClockIpPwm4 Clock = (4 << 8) | CCM_CCGR4_CG11_Pos // CCGR4, CG11
ClockIpEnc1 Clock = (4 << 8) | CCM_CCGR4_CG12_Pos // CCGR4, CG12
ClockIpEnc2 Clock = (4 << 8) | CCM_CCGR4_CG13_Pos // CCGR4, CG13
ClockIpEnc3 Clock = (4 << 8) | CCM_CCGR4_CG14_Pos // CCGR4, CG14
ClockIpEnc4 Clock = (4 << 8) | CCM_CCGR4_CG15_Pos // CCGR4, CG15
ClockIpRom Clock = (5 << 8) | CCM_CCGR5_CG0_Pos // CCGR5, CG0
ClockIpFlexio1 Clock = (5 << 8) | CCM_CCGR5_CG1_Pos // CCGR5, CG1
ClockIpWdog3 Clock = (5 << 8) | CCM_CCGR5_CG2_Pos // CCGR5, CG2
ClockIpDma Clock = (5 << 8) | CCM_CCGR5_CG3_Pos // CCGR5, CG3
ClockIpKpp Clock = (5 << 8) | CCM_CCGR5_CG4_Pos // CCGR5, CG4
ClockIpWdog2 Clock = (5 << 8) | CCM_CCGR5_CG5_Pos // CCGR5, CG5
ClockIpAipsTz4 Clock = (5 << 8) | CCM_CCGR5_CG6_Pos // CCGR5, CG6
ClockIpSpdif Clock = (5 << 8) | CCM_CCGR5_CG7_Pos // CCGR5, CG7
ClockIpSimMain Clock = (5 << 8) | CCM_CCGR5_CG8_Pos // CCGR5, CG8
ClockIpSai1 Clock = (5 << 8) | CCM_CCGR5_CG9_Pos // CCGR5, CG9
ClockIpSai2 Clock = (5 << 8) | CCM_CCGR5_CG10_Pos // CCGR5, CG10
ClockIpSai3 Clock = (5 << 8) | CCM_CCGR5_CG11_Pos // CCGR5, CG11
ClockIpLpuart1 Clock = (5 << 8) | CCM_CCGR5_CG12_Pos // CCGR5, CG12
ClockIpLpuart7 Clock = (5 << 8) | CCM_CCGR5_CG13_Pos // CCGR5, CG13
ClockIpSnvsHp Clock = (5 << 8) | CCM_CCGR5_CG14_Pos // CCGR5, CG14
ClockIpSnvsLp Clock = (5 << 8) | CCM_CCGR5_CG15_Pos // CCGR5, CG15
ClockIpUsbOh3 Clock = (6 << 8) | CCM_CCGR6_CG0_Pos // CCGR6, CG0
ClockIpUsdhc1 Clock = (6 << 8) | CCM_CCGR6_CG1_Pos // CCGR6, CG1
ClockIpUsdhc2 Clock = (6 << 8) | CCM_CCGR6_CG2_Pos // CCGR6, CG2
ClockIpDcdc Clock = (6 << 8) | CCM_CCGR6_CG3_Pos // CCGR6, CG3
ClockIpIpmux4 Clock = (6 << 8) | CCM_CCGR6_CG4_Pos // CCGR6, CG4
ClockIpFlexSpi Clock = (6 << 8) | CCM_CCGR6_CG5_Pos // CCGR6, CG5
ClockIpTrng Clock = (6 << 8) | CCM_CCGR6_CG6_Pos // CCGR6, CG6
ClockIpLpuart8 Clock = (6 << 8) | CCM_CCGR6_CG7_Pos // CCGR6, CG7
ClockIpTimer4 Clock = (6 << 8) | CCM_CCGR6_CG8_Pos // CCGR6, CG8
ClockIpAipsTz3 Clock = (6 << 8) | CCM_CCGR6_CG9_Pos // CCGR6, CG9
ClockIpSimPer Clock = (6 << 8) | CCM_CCGR6_CG10_Pos // CCGR6, CG10
ClockIpAnadig Clock = (6 << 8) | CCM_CCGR6_CG11_Pos // CCGR6, CG11
ClockIpLpi2c4 Clock = (6 << 8) | CCM_CCGR6_CG12_Pos // CCGR6, CG12
ClockIpTimer1 Clock = (6 << 8) | CCM_CCGR6_CG13_Pos // CCGR6, CG13
ClockIpTimer2 Clock = (6 << 8) | CCM_CCGR6_CG14_Pos // CCGR6, CG14
ClockIpTimer3 Clock = (6 << 8) | CCM_CCGR6_CG15_Pos // CCGR6, CG15
ClockIpEnet2 Clock = (7 << 8) | CCM_CCGR7_CG0_Pos // CCGR7, CG0
ClockIpFlexSpi2 Clock = (7 << 8) | CCM_CCGR7_CG1_Pos // CCGR7, CG1
ClockIpAxbsL Clock = (7 << 8) | CCM_CCGR7_CG2_Pos // CCGR7, CG2
ClockIpCan3 Clock = (7 << 8) | CCM_CCGR7_CG3_Pos // CCGR7, CG3
ClockIpCan3S Clock = (7 << 8) | CCM_CCGR7_CG4_Pos // CCGR7, CG4
ClockIpAipsLite Clock = (7 << 8) | CCM_CCGR7_CG5_Pos // CCGR7, CG5
ClockIpFlexio3 Clock = (7 << 8) | CCM_CCGR7_CG6_Pos // CCGR7, CG6
)
// PLL name
const (
ClockPllArm Clock = ((offPllArm & 0xFFF) << 16) | CCM_ANALOG_PLL_ARM_ENABLE_Pos // PLL ARM
ClockPllSys Clock = ((offPllSys & 0xFFF) << 16) | CCM_ANALOG_PLL_SYS_ENABLE_Pos // PLL SYS
ClockPllUsb1 Clock = ((offPllUsb1 & 0xFFF) << 16) | CCM_ANALOG_PLL_USB1_ENABLE_Pos // PLL USB1
ClockPllAudio Clock = ((offPllAudio & 0xFFF) << 16) | CCM_ANALOG_PLL_AUDIO_ENABLE_Pos // PLL Audio
ClockPllVideo Clock = ((offPllVideo & 0xFFF) << 16) | CCM_ANALOG_PLL_VIDEO_ENABLE_Pos // PLL Video
ClockPllEnet Clock = ((offPllEnet & 0xFFF) << 16) | CCM_ANALOG_PLL_ENET_ENABLE_Pos // PLL Enet0
ClockPllEnet2 Clock = ((offPllEnet & 0xFFF) << 16) | CCM_ANALOG_PLL_ENET_ENET2_REF_EN_Pos // PLL Enet1
ClockPllEnet25M Clock = ((offPllEnet & 0xFFF) << 16) | CCM_ANALOG_PLL_ENET_ENET_25M_REF_EN_Pos // PLL Enet2
ClockPllUsb2 Clock = ((offPllUsb2 & 0xFFF) << 16) | CCM_ANALOG_PLL_USB2_ENABLE_Pos // PLL USB2
)
// PLL PFD name
const (
ClockPfd0 Clock = 0 // PLL PFD0
ClockPfd1 Clock = 1 // PLL PFD1
ClockPfd2 Clock = 2 // PLL PFD2
ClockPfd3 Clock = 3 // PLL PFD3
)
// Named clock muxes of integrated peripherals
const (
MuxIpPll3Sw Clock = (offCCSR & 0xFF) | (CCM_CCSR_PLL3_SW_CLK_SEL_Pos << 8) | (((CCM_CCSR_PLL3_SW_CLK_SEL_Msk >> CCM_CCSR_PLL3_SW_CLK_SEL_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // pll3_sw_clk mux name
MuxIpPeriph Clock = (offCBCDR & 0xFF) | (CCM_CBCDR_PERIPH_CLK_SEL_Pos << 8) | (((CCM_CBCDR_PERIPH_CLK_SEL_Msk >> CCM_CBCDR_PERIPH_CLK_SEL_Pos) & 0x1FFF) << 13) | (CCM_CDHIPR_PERIPH_CLK_SEL_BUSY_Pos << 26) // periph mux name
MuxIpSemcAlt Clock = (offCBCDR & 0xFF) | (CCM_CBCDR_SEMC_ALT_CLK_SEL_Pos << 8) | (((CCM_CBCDR_SEMC_ALT_CLK_SEL_Msk >> CCM_CBCDR_SEMC_ALT_CLK_SEL_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // semc mux name
MuxIpSemc Clock = (offCBCDR & 0xFF) | (CCM_CBCDR_SEMC_CLK_SEL_Pos << 8) | (((CCM_CBCDR_SEMC_CLK_SEL_Msk >> CCM_CBCDR_SEMC_CLK_SEL_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // semc mux name
MuxIpPrePeriph Clock = (offCBCMR & 0xFF) | (CCM_CBCMR_PRE_PERIPH_CLK_SEL_Pos << 8) | (((CCM_CBCMR_PRE_PERIPH_CLK_SEL_Msk >> CCM_CBCMR_PRE_PERIPH_CLK_SEL_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // pre-periph mux name
MuxIpTrace Clock = (offCBCMR & 0xFF) | (CCM_CBCMR_TRACE_CLK_SEL_Pos << 8) | (((CCM_CBCMR_TRACE_CLK_SEL_Msk >> CCM_CBCMR_TRACE_CLK_SEL_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // trace mux name
MuxIpPeriphClk2 Clock = (offCBCMR & 0xFF) | (CCM_CBCMR_PERIPH_CLK2_SEL_Pos << 8) | (((CCM_CBCMR_PERIPH_CLK2_SEL_Msk >> CCM_CBCMR_PERIPH_CLK2_SEL_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // periph clock2 mux name
MuxIpFlexSpi2 Clock = (offCBCMR & 0xFF) | (CCM_CBCMR_FLEXSPI2_CLK_SEL_Pos << 8) | (((CCM_CBCMR_FLEXSPI2_CLK_SEL_Msk >> CCM_CBCMR_FLEXSPI2_CLK_SEL_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // flexspi2 mux name
MuxIpLpspi Clock = (offCBCMR & 0xFF) | (CCM_CBCMR_LPSPI_CLK_SEL_Pos << 8) | (((CCM_CBCMR_LPSPI_CLK_SEL_Msk >> CCM_CBCMR_LPSPI_CLK_SEL_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // lpspi mux name
MuxIpFlexSpi Clock = (offCSCMR1 & 0xFF) | (CCM_CSCMR1_FLEXSPI_CLK_SEL_Pos << 8) | (((CCM_CSCMR1_FLEXSPI_CLK_SEL_Msk >> CCM_CSCMR1_FLEXSPI_CLK_SEL_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // flexspi mux name
MuxIpUsdhc2 Clock = (offCSCMR1 & 0xFF) | (CCM_CSCMR1_USDHC2_CLK_SEL_Pos << 8) | (((CCM_CSCMR1_USDHC2_CLK_SEL_Msk >> CCM_CSCMR1_USDHC2_CLK_SEL_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // usdhc2 mux name
MuxIpUsdhc1 Clock = (offCSCMR1 & 0xFF) | (CCM_CSCMR1_USDHC1_CLK_SEL_Pos << 8) | (((CCM_CSCMR1_USDHC1_CLK_SEL_Msk >> CCM_CSCMR1_USDHC1_CLK_SEL_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // usdhc1 mux name
MuxIpSai3 Clock = (offCSCMR1 & 0xFF) | (CCM_CSCMR1_SAI3_CLK_SEL_Pos << 8) | (((CCM_CSCMR1_SAI3_CLK_SEL_Msk >> CCM_CSCMR1_SAI3_CLK_SEL_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // sai3 mux name
MuxIpSai2 Clock = (offCSCMR1 & 0xFF) | (CCM_CSCMR1_SAI2_CLK_SEL_Pos << 8) | (((CCM_CSCMR1_SAI2_CLK_SEL_Msk >> CCM_CSCMR1_SAI2_CLK_SEL_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // sai2 mux name
MuxIpSai1 Clock = (offCSCMR1 & 0xFF) | (CCM_CSCMR1_SAI1_CLK_SEL_Pos << 8) | (((CCM_CSCMR1_SAI1_CLK_SEL_Msk >> CCM_CSCMR1_SAI1_CLK_SEL_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // sai1 mux name
MuxIpPerclk Clock = (offCSCMR1 & 0xFF) | (CCM_CSCMR1_PERCLK_CLK_SEL_Pos << 8) | (((CCM_CSCMR1_PERCLK_CLK_SEL_Msk >> CCM_CSCMR1_PERCLK_CLK_SEL_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // perclk mux name
MuxIpFlexio2 Clock = (offCSCMR2 & 0xFF) | (CCM_CSCMR2_FLEXIO2_CLK_SEL_Pos << 8) | (((CCM_CSCMR2_FLEXIO2_CLK_SEL_Msk >> CCM_CSCMR2_FLEXIO2_CLK_SEL_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // flexio2 mux name
MuxIpCan Clock = (offCSCMR2 & 0xFF) | (CCM_CSCMR2_CAN_CLK_SEL_Pos << 8) | (((CCM_CSCMR2_CAN_CLK_SEL_Msk >> CCM_CSCMR2_CAN_CLK_SEL_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // can mux name
MuxIpUart Clock = (offCSCDR1 & 0xFF) | (CCM_CSCDR1_UART_CLK_SEL_Pos << 8) | (((CCM_CSCDR1_UART_CLK_SEL_Msk >> CCM_CSCDR1_UART_CLK_SEL_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // uart mux name
MuxIpSpdif Clock = (offCDCDR & 0xFF) | (CCM_CDCDR_SPDIF0_CLK_SEL_Pos << 8) | (((CCM_CDCDR_SPDIF0_CLK_SEL_Msk >> CCM_CDCDR_SPDIF0_CLK_SEL_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // spdif mux name
MuxIpFlexio1 Clock = (offCDCDR & 0xFF) | (CCM_CDCDR_FLEXIO1_CLK_SEL_Pos << 8) | (((CCM_CDCDR_FLEXIO1_CLK_SEL_Msk >> CCM_CDCDR_FLEXIO1_CLK_SEL_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // flexio1 mux name
MuxIpLpi2c Clock = (offCSCDR2 & 0xFF) | (CCM_CSCDR2_LPI2C_CLK_SEL_Pos << 8) | (((CCM_CSCDR2_LPI2C_CLK_SEL_Msk >> CCM_CSCDR2_LPI2C_CLK_SEL_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // lpi2c mux name
MuxIpLcdifPre Clock = (offCSCDR2 & 0xFF) | (CCM_CSCDR2_LCDIF_PRE_CLK_SEL_Pos << 8) | (((CCM_CSCDR2_LCDIF_PRE_CLK_SEL_Msk >> CCM_CSCDR2_LCDIF_PRE_CLK_SEL_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // lcdif pre mux name
MuxIpCsi Clock = (offCSCDR3 & 0xFF) | (CCM_CSCDR3_CSI_CLK_SEL_Pos << 8) | (((CCM_CSCDR3_CSI_CLK_SEL_Msk >> CCM_CSCDR3_CSI_CLK_SEL_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // csi mux name
)
// Named hardware clock divisors of integrated peripherals
const (
DivIpArm Clock = (offCACRR & 0xFF) | (CCM_CACRR_ARM_PODF_Pos << 8) | (((CCM_CACRR_ARM_PODF_Msk >> CCM_CACRR_ARM_PODF_Pos) & 0x1FFF) << 13) | (CCM_CDHIPR_ARM_PODF_BUSY_Pos << 26) // core div name
DivIpPeriphClk2 Clock = (offCBCDR & 0xFF) | (CCM_CBCDR_PERIPH_CLK2_PODF_Pos << 8) | (((CCM_CBCDR_PERIPH_CLK2_PODF_Msk >> CCM_CBCDR_PERIPH_CLK2_PODF_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // periph clock2 div name
DivIpSemc Clock = (offCBCDR & 0xFF) | (CCM_CBCDR_SEMC_PODF_Pos << 8) | (((CCM_CBCDR_SEMC_PODF_Msk >> CCM_CBCDR_SEMC_PODF_Pos) & 0x1FFF) << 13) | (CCM_CDHIPR_SEMC_PODF_BUSY_Pos << 26) // semc div name
DivIpAhb Clock = (offCBCDR & 0xFF) | (CCM_CBCDR_AHB_PODF_Pos << 8) | (((CCM_CBCDR_AHB_PODF_Msk >> CCM_CBCDR_AHB_PODF_Pos) & 0x1FFF) << 13) | (CCM_CDHIPR_AHB_PODF_BUSY_Pos << 26) // ahb div name
DivIpIpg Clock = (offCBCDR & 0xFF) | (CCM_CBCDR_IPG_PODF_Pos << 8) | (((CCM_CBCDR_IPG_PODF_Msk >> CCM_CBCDR_IPG_PODF_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // ipg div name
DivIpFlexSpi2 Clock = (offCBCMR & 0xFF) | (CCM_CBCMR_FLEXSPI2_PODF_Pos << 8) | (((CCM_CBCMR_FLEXSPI2_PODF_Msk >> CCM_CBCMR_FLEXSPI2_PODF_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // flexspi2 div name
DivIpLpspi Clock = (offCBCMR & 0xFF) | (CCM_CBCMR_LPSPI_PODF_Pos << 8) | (((CCM_CBCMR_LPSPI_PODF_Msk >> CCM_CBCMR_LPSPI_PODF_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // lpspi div name
DivIpLcdif Clock = (offCBCMR & 0xFF) | (CCM_CBCMR_LCDIF_PODF_Pos << 8) | (((CCM_CBCMR_LCDIF_PODF_Msk >> CCM_CBCMR_LCDIF_PODF_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // lcdif div name
DivIpFlexSpi Clock = (offCSCMR1 & 0xFF) | (CCM_CSCMR1_FLEXSPI_PODF_Pos << 8) | (((CCM_CSCMR1_FLEXSPI_PODF_Msk >> CCM_CSCMR1_FLEXSPI_PODF_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // flexspi div name
DivIpPerclk Clock = (offCSCMR1 & 0xFF) | (CCM_CSCMR1_PERCLK_PODF_Pos << 8) | (((CCM_CSCMR1_PERCLK_PODF_Msk >> CCM_CSCMR1_PERCLK_PODF_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // perclk div name
DivIpCan Clock = (offCSCMR2 & 0xFF) | (CCM_CSCMR2_CAN_CLK_PODF_Pos << 8) | (((CCM_CSCMR2_CAN_CLK_PODF_Msk >> CCM_CSCMR2_CAN_CLK_PODF_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // can div name
DivIpTrace Clock = (offCSCDR1 & 0xFF) | (CCM_CSCDR1_TRACE_PODF_Pos << 8) | (((CCM_CSCDR1_TRACE_PODF_Msk >> CCM_CSCDR1_TRACE_PODF_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // trace div name
DivIpUsdhc2 Clock = (offCSCDR1 & 0xFF) | (CCM_CSCDR1_USDHC2_PODF_Pos << 8) | (((CCM_CSCDR1_USDHC2_PODF_Msk >> CCM_CSCDR1_USDHC2_PODF_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // usdhc2 div name
DivIpUsdhc1 Clock = (offCSCDR1 & 0xFF) | (CCM_CSCDR1_USDHC1_PODF_Pos << 8) | (((CCM_CSCDR1_USDHC1_PODF_Msk >> CCM_CSCDR1_USDHC1_PODF_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // usdhc1 div name
DivIpUart Clock = (offCSCDR1 & 0xFF) | (CCM_CSCDR1_UART_CLK_PODF_Pos << 8) | (((CCM_CSCDR1_UART_CLK_PODF_Msk >> CCM_CSCDR1_UART_CLK_PODF_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // uart div name
DivIpFlexio2 Clock = (offCS1CDR & 0xFF) | (CCM_CS1CDR_FLEXIO2_CLK_PODF_Pos << 8) | (((CCM_CS1CDR_FLEXIO2_CLK_PODF_Msk >> CCM_CS1CDR_FLEXIO2_CLK_PODF_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // flexio2 pre div name
DivIpSai3Pre Clock = (offCS1CDR & 0xFF) | (CCM_CS1CDR_SAI3_CLK_PRED_Pos << 8) | (((CCM_CS1CDR_SAI3_CLK_PRED_Msk >> CCM_CS1CDR_SAI3_CLK_PRED_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // sai3 pre div name
DivIpSai3 Clock = (offCS1CDR & 0xFF) | (CCM_CS1CDR_SAI3_CLK_PODF_Pos << 8) | (((CCM_CS1CDR_SAI3_CLK_PODF_Msk >> CCM_CS1CDR_SAI3_CLK_PODF_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // sai3 div name
DivIpFlexio2Pre Clock = (offCS1CDR & 0xFF) | (CCM_CS1CDR_FLEXIO2_CLK_PRED_Pos << 8) | (((CCM_CS1CDR_FLEXIO2_CLK_PRED_Msk >> CCM_CS1CDR_FLEXIO2_CLK_PRED_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // sai3 pre div name
DivIpSai1Pre Clock = (offCS1CDR & 0xFF) | (CCM_CS1CDR_SAI1_CLK_PRED_Pos << 8) | (((CCM_CS1CDR_SAI1_CLK_PRED_Msk >> CCM_CS1CDR_SAI1_CLK_PRED_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // sai1 pre div name
DivIpSai1 Clock = (offCS1CDR & 0xFF) | (CCM_CS1CDR_SAI1_CLK_PODF_Pos << 8) | (((CCM_CS1CDR_SAI1_CLK_PODF_Msk >> CCM_CS1CDR_SAI1_CLK_PODF_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // sai1 div name
DivIpSai2Pre Clock = (offCS2CDR & 0xFF) | (CCM_CS2CDR_SAI2_CLK_PRED_Pos << 8) | (((CCM_CS2CDR_SAI2_CLK_PRED_Msk >> CCM_CS2CDR_SAI2_CLK_PRED_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // sai2 pre div name
DivIpSai2 Clock = (offCS2CDR & 0xFF) | (CCM_CS2CDR_SAI2_CLK_PODF_Pos << 8) | (((CCM_CS2CDR_SAI2_CLK_PODF_Msk >> CCM_CS2CDR_SAI2_CLK_PODF_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // sai2 div name
DivIpSpdif0Pre Clock = (offCDCDR & 0xFF) | (CCM_CDCDR_SPDIF0_CLK_PRED_Pos << 8) | (((CCM_CDCDR_SPDIF0_CLK_PRED_Msk >> CCM_CDCDR_SPDIF0_CLK_PRED_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // spdif pre div name
DivIpSpdif0 Clock = (offCDCDR & 0xFF) | (CCM_CDCDR_SPDIF0_CLK_PODF_Pos << 8) | (((CCM_CDCDR_SPDIF0_CLK_PODF_Msk >> CCM_CDCDR_SPDIF0_CLK_PODF_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // spdif div name
DivIpFlexio1Pre Clock = (offCDCDR & 0xFF) | (CCM_CDCDR_FLEXIO1_CLK_PRED_Pos << 8) | (((CCM_CDCDR_FLEXIO1_CLK_PRED_Msk >> CCM_CDCDR_FLEXIO1_CLK_PRED_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // flexio1 pre div name
DivIpFlexio1 Clock = (offCDCDR & 0xFF) | (CCM_CDCDR_FLEXIO1_CLK_PODF_Pos << 8) | (((CCM_CDCDR_FLEXIO1_CLK_PODF_Msk >> CCM_CDCDR_FLEXIO1_CLK_PODF_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // flexio1 div name
DivIpLpi2c Clock = (offCSCDR2 & 0xFF) | (CCM_CSCDR2_LPI2C_CLK_PODF_Pos << 8) | (((CCM_CSCDR2_LPI2C_CLK_PODF_Msk >> CCM_CSCDR2_LPI2C_CLK_PODF_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // lpi2c div name
DivIpLcdifPre Clock = (offCSCDR2 & 0xFF) | (CCM_CSCDR2_LCDIF_PRED_Pos << 8) | (((CCM_CSCDR2_LCDIF_PRED_Msk >> CCM_CSCDR2_LCDIF_PRED_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // lcdif pre div name
DivIpCsi Clock = (offCSCDR3 & 0xFF) | (CCM_CSCDR3_CSI_PODF_Pos << 8) | (((CCM_CSCDR3_CSI_PODF_Msk >> CCM_CSCDR3_CSI_PODF_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // csi div name
)
// Selected clock offsets
const (
offCCSR = 0x0C
offCBCDR = 0x14
offCBCMR = 0x18
offCSCMR1 = 0x1C
offCSCMR2 = 0x20
offCSCDR1 = 0x24
offCDCDR = 0x30
offCSCDR2 = 0x38
offCSCDR3 = 0x3C
offCACRR = 0x10
offCS1CDR = 0x28
offCS2CDR = 0x2C
offPllArm = 0x00
offPllSys = 0x30
offPllUsb1 = 0x10
offPllAudio = 0x70
offPllVideo = 0xA0
offPllEnet = 0xE0
offPllUsb2 = 0x20
noBusyWait = 0x20
)
// analog PLL definition
const (
pllBypassPos = 16
pllBypassClkSrcMsk = 0xC000
pllBypassClkSrcPos = 14
)
// PLL clock source, bypass cloco source also
const (
pllSrc24M = 0 // Pll clock source 24M
pllSrcClkPN = 1 // Pll clock source CLK1_P and CLK1_N
)
const (
clockNotNeeded uint32 = 0 // Clock is off during all modes
clockNeededRun uint32 = 1 // Clock is on in run mode, but off in WAIT and STOP modes
clockNeededRunWait uint32 = 3 // Clock is on during all modes, except STOP mode
)
// getGate returns the CCM clock gating register for the receiver clk.
func (clk Clock) getGate() *volatile.Register32 {
switch clk >> 8 {
case 0:
return &CCM.CCGR0
case 1:
return &CCM.CCGR1
case 2:
return &CCM.CCGR2
case 3:
return &CCM.CCGR3
case 4:
return &CCM.CCGR4
case 5:
return &CCM.CCGR5
case 6:
return &CCM.CCGR6
case 7:
return &CCM.CCGR7
default:
panic("nxp: invalid clock")
}
}
// setGate enables or disables the receiver clk using its gating register.
func (clk Clock) setGate(value uint32) {
reg := clk.getGate()
shift := clk & 0x1F
reg.Set((reg.Get() & ^(3 << shift)) | (value << shift))
}
func (clk Clock) setCcm(value uint32) {
const ccmBase = 0x400fc000
reg := (*volatile.Register32)(unsafe.Pointer(uintptr(ccmBase + (uint32(clk) & 0xFF))))
msk := ((uint32(clk) >> 13) & 0x1FFF) << ((uint32(clk) >> 8) & 0x1F)
pos := (uint32(clk) >> 8) & 0x1F
bsy := (uint32(clk) >> 26) & 0x3F
reg.Set((reg.Get() & ^uint32(msk)) | ((value << pos) & msk))
if bsy < noBusyWait {
for CCM.CDHIPR.HasBits(1 << bsy) {
}
}
}
func setSysPfd(value ...uint32) {
for i, val := range value {
pfd528 := CCM_ANALOG.PFD_528.Get() &
^((CCM_ANALOG_PFD_528_PFD0_CLKGATE_Msk | CCM_ANALOG_PFD_528_PFD0_FRAC_Msk) << (8 * uint32(i)))
frac := (val << CCM_ANALOG_PFD_528_PFD0_FRAC_Pos) & CCM_ANALOG_PFD_528_PFD0_FRAC_Msk
// disable the clock output first
CCM_ANALOG.PFD_528.Set(pfd528 | (CCM_ANALOG_PFD_528_PFD0_CLKGATE_Msk << (8 * uint32(i))))
// set the new value and enable output
CCM_ANALOG.PFD_528.Set(pfd528 | (frac << (8 * uint32(i))))
}
}
func setUsb1Pfd(value ...uint32) {
for i, val := range value {
pfd480 := CCM_ANALOG.PFD_480.Get() &
^((CCM_ANALOG_PFD_480_PFD0_CLKGATE_Msk | CCM_ANALOG_PFD_480_PFD0_FRAC_Msk) << (8 * uint32(i)))
frac := (val << CCM_ANALOG_PFD_480_PFD0_FRAC_Pos) & CCM_ANALOG_PFD_480_PFD0_FRAC_Msk
// disable the clock output first
CCM_ANALOG.PFD_480.Set(pfd480 | (CCM_ANALOG_PFD_480_PFD0_CLKGATE_Msk << (8 * uint32(i))))
// set the new value and enable output
CCM_ANALOG.PFD_480.Set(pfd480 | (frac << (8 * uint32(i))))
}
}
// PLL configuration for ARM
type ClockConfigArmPll struct {
LoopDivider uint32 // PLL loop divider. Valid range for divider value: 54-108. Fout=Fin*LoopDivider/2.
Src uint8 // Pll clock source, reference _clock_pll_clk_src
}
func (cfg ClockConfigArmPll) Configure() {
// bypass PLL first
src := (uint32(cfg.Src) << CCM_ANALOG_PLL_ARM_BYPASS_CLK_SRC_Pos) & CCM_ANALOG_PLL_ARM_BYPASS_CLK_SRC_Msk
CCM_ANALOG.PLL_ARM.Set(
(CCM_ANALOG.PLL_ARM.Get() & ^uint32(CCM_ANALOG_PLL_ARM_BYPASS_CLK_SRC_Msk)) |
CCM_ANALOG_PLL_ARM_BYPASS_Msk | src)
sel := (cfg.LoopDivider << CCM_ANALOG_PLL_ARM_DIV_SELECT_Pos) & CCM_ANALOG_PLL_ARM_DIV_SELECT_Msk
CCM_ANALOG.PLL_ARM.Set(
(CCM_ANALOG.PLL_ARM.Get() & ^uint32(CCM_ANALOG_PLL_ARM_DIV_SELECT_Msk|CCM_ANALOG_PLL_ARM_POWERDOWN_Msk)) |
CCM_ANALOG_PLL_ARM_ENABLE_Msk | sel)
for !CCM_ANALOG.PLL_ARM.HasBits(CCM_ANALOG_PLL_ARM_LOCK_Msk) {
}
// disable bypass
CCM_ANALOG.PLL_ARM.ClearBits(CCM_ANALOG_PLL_ARM_BYPASS_Msk)
}
// PLL configuration for System
type ClockConfigSysPll struct {
LoopDivider uint8 // PLL loop divider. Intended to be 1 (528M): 0 - Fout=Fref*20, 1 - Fout=Fref*22
Numerator uint32 // 30 bit Numerator of fractional loop divider.
Denominator uint32 // 30 bit Denominator of fractional loop divider
Src uint8 // Pll clock source, reference _clock_pll_clk_src
SsStop uint16 // Stop value to get frequency change.
SsEnable uint8 // Enable spread spectrum modulation
SsStep uint16 // Step value to get frequency change step.
}
func (cfg ClockConfigSysPll) Configure(pfd ...uint32) {
// bypass PLL first
src := (uint32(cfg.Src) << CCM_ANALOG_PLL_SYS_BYPASS_CLK_SRC_Pos) & CCM_ANALOG_PLL_SYS_BYPASS_CLK_SRC_Msk
CCM_ANALOG.PLL_SYS.Set(
(CCM_ANALOG.PLL_SYS.Get() & ^uint32(CCM_ANALOG_PLL_SYS_BYPASS_CLK_SRC_Msk)) |
CCM_ANALOG_PLL_SYS_BYPASS_Msk | src)
sel := (uint32(cfg.LoopDivider) << CCM_ANALOG_PLL_SYS_DIV_SELECT_Pos) & CCM_ANALOG_PLL_SYS_DIV_SELECT_Msk
CCM_ANALOG.PLL_SYS.Set(
(CCM_ANALOG.PLL_SYS.Get() & ^uint32(CCM_ANALOG_PLL_SYS_DIV_SELECT_Msk|CCM_ANALOG_PLL_SYS_POWERDOWN_Msk)) |
CCM_ANALOG_PLL_SYS_ENABLE_Msk | sel)
// initialize the fractional mode
CCM_ANALOG.PLL_SYS_NUM.Set((cfg.Numerator << CCM_ANALOG_PLL_SYS_NUM_A_Pos) & CCM_ANALOG_PLL_SYS_NUM_A_Msk)
CCM_ANALOG.PLL_SYS_DENOM.Set((cfg.Denominator << CCM_ANALOG_PLL_SYS_DENOM_B_Pos) & CCM_ANALOG_PLL_SYS_DENOM_B_Msk)
// initialize the spread spectrum mode
inc := (uint32(cfg.SsStep) << CCM_ANALOG_PLL_SYS_SS_STEP_Pos) & CCM_ANALOG_PLL_SYS_SS_STEP_Msk
enb := (uint32(cfg.SsEnable) << CCM_ANALOG_PLL_SYS_SS_ENABLE_Pos) & CCM_ANALOG_PLL_SYS_SS_ENABLE_Msk
stp := (uint32(cfg.SsStop) << CCM_ANALOG_PLL_SYS_SS_STOP_Pos) & CCM_ANALOG_PLL_SYS_SS_STOP_Msk
CCM_ANALOG.PLL_SYS_SS.Set(inc | enb | stp)
for !CCM_ANALOG.PLL_SYS.HasBits(CCM_ANALOG_PLL_SYS_LOCK_Msk) {
}
// disable bypass
CCM_ANALOG.PLL_SYS.ClearBits(CCM_ANALOG_PLL_SYS_BYPASS_Msk)
// update PFDs after update
setSysPfd(pfd...)
}
// PLL configuration for USB
type ClockConfigUsbPll struct {
Instance uint8 // USB PLL number (1 or 2)
LoopDivider uint8 // PLL loop divider: 0 - Fout=Fref*20, 1 - Fout=Fref*22
Src uint8 // Pll clock source, reference _clock_pll_clk_src
}
func (cfg ClockConfigUsbPll) Configure(pfd ...uint32) {
switch cfg.Instance {
case 1:
// bypass PLL first
src := (uint32(cfg.Src) << CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_Pos) & CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_Msk
CCM_ANALOG.PLL_USB1.Set(
(CCM_ANALOG.PLL_USB1.Get() & ^uint32(CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_Msk)) |
CCM_ANALOG_PLL_USB1_BYPASS_Msk | src)
sel := uint32((cfg.LoopDivider << CCM_ANALOG_PLL_USB1_DIV_SELECT_Pos) & CCM_ANALOG_PLL_USB1_DIV_SELECT_Msk)
CCM_ANALOG.PLL_USB1_SET.Set(
(CCM_ANALOG.PLL_USB1.Get() & ^uint32(CCM_ANALOG_PLL_USB1_DIV_SELECT_Msk)) |
CCM_ANALOG_PLL_USB1_ENABLE_Msk | CCM_ANALOG_PLL_USB1_POWER_Msk |
CCM_ANALOG_PLL_USB1_EN_USB_CLKS_Msk | sel)
for !CCM_ANALOG.PLL_USB1.HasBits(CCM_ANALOG_PLL_USB1_LOCK_Msk) {
}
// disable bypass
CCM_ANALOG.PLL_USB1_CLR.Set(CCM_ANALOG_PLL_USB1_BYPASS_Msk)
// update PFDs after update
setUsb1Pfd(pfd...)
case 2:
// bypass PLL first
src := (uint32(cfg.Src) << CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_Pos) & CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_Msk
CCM_ANALOG.PLL_USB2.Set(
(CCM_ANALOG.PLL_USB2.Get() & ^uint32(CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_Msk)) |
CCM_ANALOG_PLL_USB2_BYPASS_Msk | src)
sel := uint32((cfg.LoopDivider << CCM_ANALOG_PLL_USB2_DIV_SELECT_Pos) & CCM_ANALOG_PLL_USB2_DIV_SELECT_Msk)
CCM_ANALOG.PLL_USB2.Set(
(CCM_ANALOG.PLL_USB2.Get() & ^uint32(CCM_ANALOG_PLL_USB2_DIV_SELECT_Msk)) |
CCM_ANALOG_PLL_USB2_ENABLE_Msk | CCM_ANALOG_PLL_USB2_POWER_Msk |
CCM_ANALOG_PLL_USB2_EN_USB_CLKS_Msk | sel)
for !CCM_ANALOG.PLL_USB2.HasBits(CCM_ANALOG_PLL_USB2_LOCK_Msk) {
}
// disable bypass
CCM_ANALOG.PLL_USB2.ClearBits(CCM_ANALOG_PLL_USB2_BYPASS_Msk)
default:
panic("nxp: invalid USB PLL")
}
}
+29
View File
@@ -0,0 +1,29 @@
// Hand created file. DO NOT DELETE.
// Hardfault aliases for definitions that have inconsistent naming (which are
// auto-generated by gen-device-svd.go) among devices in package nxp.
// +build nxp,mimxrt1062
package nxp
const (
HardFault_CFSR_IACCVIOL = SCB_CFSR_IACCVIOL
HardFault_CFSR_DACCVIOL = SCB_CFSR_DACCVIOL
HardFault_CFSR_MUNSTKERR = SCB_CFSR_MUNSTKERR
HardFault_CFSR_MSTKERR = SCB_CFSR_MSTKERR
HardFault_CFSR_MLSPERR = SCB_CFSR_MLSPERR
HardFault_CFSR_IBUSERR = SCB_CFSR_IBUSERR
HardFault_CFSR_PRECISERR = SCB_CFSR_PRECISERR
HardFault_CFSR_IMPRECISERR = SCB_CFSR_IMPRECISERR
HardFault_CFSR_UNSTKERR = SCB_CFSR_UNSTKERR
HardFault_CFSR_STKERR = SCB_CFSR_STKERR
HardFault_CFSR_LSPERR = SCB_CFSR_LSPERR
HardFault_CFSR_UNDEFINSTR = SCB_CFSR_UNDEFINSTR
HardFault_CFSR_INVSTATE = SCB_CFSR_INVSTATE
HardFault_CFSR_INVPC = SCB_CFSR_INVPC
HardFault_CFSR_NOCP = SCB_CFSR_NOCP
HardFault_CFSR_UNALIGNED = SCB_CFSR_UNALIGNED
HardFault_CFSR_DIVBYZERO = SCB_CFSR_DIVBYZERO
HardFault_CFSR_MMARVALID = SCB_CFSR_MMARVALID
HardFault_CFSR_BFARVALID = SCB_CFSR_BFARVALID
)
+278
View File
@@ -0,0 +1,278 @@
// Hand created file. DO NOT DELETE.
// Type definitions, fields, and constants associated with the MPU peripheral
// of the NXP MIMXRT1062.
// +build nxp,mimxrt1062
package nxp
import (
"device/arm"
"runtime/volatile"
"unsafe"
)
type MPU_Type struct {
TYPE volatile.Register32 // 0x000 (R/ ) - MPU Type Register
CTRL volatile.Register32 // 0x004 (R/W) - MPU Control Register
RNR volatile.Register32 // 0x008 (R/W) - MPU Region RNRber Register
RBAR volatile.Register32 // 0x00C (R/W) - MPU Region Base Address Register
RASR volatile.Register32 // 0x010 (R/W) - MPU Region Attribute and Size Register
RBAR_A1 volatile.Register32 // 0x014 (R/W) - MPU Alias 1 Region Base Address Register
RASR_A1 volatile.Register32 // 0x018 (R/W) - MPU Alias 1 Region Attribute and Size Register
RBAR_A2 volatile.Register32 // 0x01C (R/W) - MPU Alias 2 Region Base Address Register
RASR_A2 volatile.Register32 // 0x020 (R/W) - MPU Alias 2 Region Attribute and Size Register
RBAR_A3 volatile.Register32 // 0x024 (R/W) - MPU Alias 3 Region Base Address Register
RASR_A3 volatile.Register32 // 0x028 (R/W) - MPU Alias 3 Region Attribute and Size Register
}
var MPU = (*MPU_Type)(unsafe.Pointer(uintptr(0xe000ed90)))
type (
RegionSize uint32
AccessPerms uint32
Extension uint32
)
// MPU Control Register Definitions
const (
MPU_CTRL_PRIVDEFENA_Pos = 2 // MPU CTRL: PRIVDEFENA Position
MPU_CTRL_PRIVDEFENA_Msk = 1 << MPU_CTRL_PRIVDEFENA_Pos // MPU CTRL: PRIVDEFENA Mask
MPU_CTRL_HFNMIENA_Pos = 1 // MPU CTRL: HFNMIENA Position
MPU_CTRL_HFNMIENA_Msk = 1 << MPU_CTRL_HFNMIENA_Pos // MPU CTRL: HFNMIENA Mask
MPU_CTRL_ENABLE_Pos = 0 // MPU CTRL: ENABLE Position
MPU_CTRL_ENABLE_Msk = 1 // MPU CTRL: ENABLE Mask
)
// MPU Region Base Address Register Definitions
const (
MPU_RBAR_ADDR_Pos = 5 // MPU RBAR: ADDR Position
MPU_RBAR_ADDR_Msk = 0x7FFFFFF << MPU_RBAR_ADDR_Pos // MPU RBAR: ADDR Mask
MPU_RBAR_VALID_Pos = 4 // MPU RBAR: VALID Position
MPU_RBAR_VALID_Msk = 1 << MPU_RBAR_VALID_Pos // MPU RBAR: VALID Mask
MPU_RBAR_REGION_Pos = 0 // MPU RBAR: REGION Position
MPU_RBAR_REGION_Msk = 0xF // MPU RBAR: REGION Mask
)
// MPU Region Attribute and Size Register Definitions
const (
MPU_RASR_ATTRS_Pos = 16 // MPU RASR: MPU Region Attribute field Position
MPU_RASR_ATTRS_Msk = 0xFFFF << MPU_RASR_ATTRS_Pos // MPU RASR: MPU Region Attribute field Mask
MPU_RASR_XN_Pos = 28 // MPU RASR: ATTRS.XN Position
MPU_RASR_XN_Msk = 1 << MPU_RASR_XN_Pos // MPU RASR: ATTRS.XN Mask
MPU_RASR_AP_Pos = 24 // MPU RASR: ATTRS.AP Position
MPU_RASR_AP_Msk = 0x7 << MPU_RASR_AP_Pos // MPU RASR: ATTRS.AP Mask
MPU_RASR_TEX_Pos = 19 // MPU RASR: ATTRS.TEX Position
MPU_RASR_TEX_Msk = 0x7 << MPU_RASR_TEX_Pos // MPU RASR: ATTRS.TEX Mask
MPU_RASR_S_Pos = 18 // MPU RASR: ATTRS.S Position
MPU_RASR_S_Msk = 1 << MPU_RASR_S_Pos // MPU RASR: ATTRS.S Mask
MPU_RASR_C_Pos = 17 // MPU RASR: ATTRS.C Position
MPU_RASR_C_Msk = 1 << MPU_RASR_C_Pos // MPU RASR: ATTRS.C Mask
MPU_RASR_B_Pos = 16 // MPU RASR: ATTRS.B Position
MPU_RASR_B_Msk = 1 << MPU_RASR_B_Pos // MPU RASR: ATTRS.B Mask
MPU_RASR_SRD_Pos = 8 // MPU RASR: Sub-Region Disable Position
MPU_RASR_SRD_Msk = 0xFF << MPU_RASR_SRD_Pos // MPU RASR: Sub-Region Disable Mask
MPU_RASR_SIZE_Pos = 1 // MPU RASR: Region Size Field Position
MPU_RASR_SIZE_Msk = 0x1F << MPU_RASR_SIZE_Pos // MPU RASR: Region Size Field Mask
MPU_RASR_ENABLE_Pos = 0 // MPU RASR: Region enable bit Position
MPU_RASR_ENABLE_Msk = 1 // MPU RASR: Region enable bit Disable Mask
)
const (
SCB_DCISW_WAY_Pos = 30 // SCB DCISW: Way Position
SCB_DCISW_WAY_Msk = 3 << SCB_DCISW_WAY_Pos // SCB DCISW: Way Mask
SCB_DCISW_SET_Pos = 5 // SCB DCISW: Set Position
SCB_DCISW_SET_Msk = 0x1FF << SCB_DCISW_SET_Pos // SCB DCISW: Set Mask
)
const (
SCB_DCCISW_WAY_Pos = 30 // SCB DCCISW: Way Position
SCB_DCCISW_WAY_Msk = 3 << SCB_DCCISW_WAY_Pos // SCB DCCISW: Way Mask
SCB_DCCISW_SET_Pos = 5 // SCB DCCISW: Set Position
SCB_DCCISW_SET_Msk = 0x1FF << SCB_DCCISW_SET_Pos // SCB DCCISW: Set Mask
)
const (
RGNSZ_32B RegionSize = 0x04 // MPU Region Size 32 Bytes
RGNSZ_64B RegionSize = 0x05 // MPU Region Size 64 Bytes
RGNSZ_128B RegionSize = 0x06 // MPU Region Size 128 Bytes
RGNSZ_256B RegionSize = 0x07 // MPU Region Size 256 Bytes
RGNSZ_512B RegionSize = 0x08 // MPU Region Size 512 Bytes
RGNSZ_1KB RegionSize = 0x09 // MPU Region Size 1 KByte
RGNSZ_2KB RegionSize = 0x0A // MPU Region Size 2 KBytes
RGNSZ_4KB RegionSize = 0x0B // MPU Region Size 4 KBytes
RGNSZ_8KB RegionSize = 0x0C // MPU Region Size 8 KBytes
RGNSZ_16KB RegionSize = 0x0D // MPU Region Size 16 KBytes
RGNSZ_32KB RegionSize = 0x0E // MPU Region Size 32 KBytes
RGNSZ_64KB RegionSize = 0x0F // MPU Region Size 64 KBytes
RGNSZ_128KB RegionSize = 0x10 // MPU Region Size 128 KBytes
RGNSZ_256KB RegionSize = 0x11 // MPU Region Size 256 KBytes
RGNSZ_512KB RegionSize = 0x12 // MPU Region Size 512 KBytes
RGNSZ_1MB RegionSize = 0x13 // MPU Region Size 1 MByte
RGNSZ_2MB RegionSize = 0x14 // MPU Region Size 2 MBytes
RGNSZ_4MB RegionSize = 0x15 // MPU Region Size 4 MBytes
RGNSZ_8MB RegionSize = 0x16 // MPU Region Size 8 MBytes
RGNSZ_16MB RegionSize = 0x17 // MPU Region Size 16 MBytes
RGNSZ_32MB RegionSize = 0x18 // MPU Region Size 32 MBytes
RGNSZ_64MB RegionSize = 0x19 // MPU Region Size 64 MBytes
RGNSZ_128MB RegionSize = 0x1A // MPU Region Size 128 MBytes
RGNSZ_256MB RegionSize = 0x1B // MPU Region Size 256 MBytes
RGNSZ_512MB RegionSize = 0x1C // MPU Region Size 512 MBytes
RGNSZ_1GB RegionSize = 0x1D // MPU Region Size 1 GByte
RGNSZ_2GB RegionSize = 0x1E // MPU Region Size 2 GBytes
RGNSZ_4GB RegionSize = 0x1F // MPU Region Size 4 GBytes
)
const (
PERM_NONE AccessPerms = 0 // MPU Access Permission no access
PERM_PRIV AccessPerms = 1 // MPU Access Permission privileged access only
PERM_URO AccessPerms = 2 // MPU Access Permission unprivileged access read-only
PERM_FULL AccessPerms = 3 // MPU Access Permission full access
PERM_PRO AccessPerms = 5 // MPU Access Permission privileged access read-only
PERM_RO AccessPerms = 6 // MPU Access Permission read-only access
)
const (
EXTN_NORMAL Extension = 0
EXTN_DEVICE Extension = 2
)
func (mpu *MPU_Type) Enable(enable bool) {
if enable {
mpu.CTRL.Set(MPU_CTRL_PRIVDEFENA_Msk | MPU_CTRL_ENABLE_Msk)
SystemControl.SHCSR.SetBits(SCB_SHCSR_MEMFAULTENA_Msk)
arm.AsmFull(`
dsb 0xF
isb 0xF
`, nil)
enableDcache(true)
enableIcache(true)
} else {
enableIcache(false)
enableDcache(false)
arm.AsmFull(`
dmb 0xF
`, nil)
SystemControl.SHCSR.ClearBits(SCB_SHCSR_MEMFAULTENA_Msk)
mpu.CTRL.ClearBits(MPU_CTRL_ENABLE_Msk)
}
}
// MPU Region Base Address Register value
func (mpu *MPU_Type) SetRBAR(region uint32, baseAddress uint32) {
mpu.RBAR.Set((baseAddress & MPU_RBAR_ADDR_Msk) |
(region & MPU_RBAR_REGION_Msk) | MPU_RBAR_VALID_Msk)
}
// MPU Region Attribute and Size Register value
func (mpu *MPU_Type) SetRASR(size RegionSize, access AccessPerms, ext Extension, exec, share, cache, buffer, disable bool) {
boolBit := func(b bool) uint32 {
if b {
return 1
}
return 0
}
attr := ((uint32(ext) << MPU_RASR_TEX_Pos) & MPU_RASR_TEX_Msk) |
((boolBit(share) << MPU_RASR_S_Pos) & MPU_RASR_S_Msk) |
((boolBit(cache) << MPU_RASR_C_Pos) & MPU_RASR_C_Msk) |
((boolBit(buffer) << MPU_RASR_B_Pos) & MPU_RASR_B_Msk)
mpu.RASR.Set(((boolBit(!exec) << MPU_RASR_XN_Pos) & MPU_RASR_XN_Msk) |
((uint32(access) << MPU_RASR_AP_Pos) & MPU_RASR_AP_Msk) |
(attr & (MPU_RASR_TEX_Msk | MPU_RASR_S_Msk | MPU_RASR_C_Msk | MPU_RASR_B_Msk)) |
((boolBit(disable) << MPU_RASR_SRD_Pos) & MPU_RASR_SRD_Msk) |
((uint32(size) << MPU_RASR_SIZE_Pos) & MPU_RASR_SIZE_Msk) |
MPU_RASR_ENABLE_Msk)
}
func enableIcache(enable bool) {
if enable != SystemControl.CCR.HasBits(SCB_CCR_IC_Msk) {
if enable {
arm.AsmFull(`
dsb 0xF
isb 0xF
`, nil)
SystemControl.ICIALLU.Set(0)
arm.AsmFull(`
dsb 0xF
isb 0xF
`, nil)
SystemControl.CCR.SetBits(SCB_CCR_IC_Msk)
arm.AsmFull(`
dsb 0xF
isb 0xF
`, nil)
} else {
arm.AsmFull(`
dsb 0xF
isb 0xF
`, nil)
SystemControl.CCR.ClearBits(SCB_CCR_IC_Msk)
SystemControl.ICIALLU.Set(0)
arm.AsmFull(`
dsb 0xF
isb 0xF
`, nil)
}
}
}
func enableDcache(enable bool) {
if enable != SystemControl.CCR.HasBits(SCB_CCR_DC_Msk) {
if enable {
SystemControl.CSSELR.Set(0)
arm.AsmFull(`
dsb 0xF
`, nil)
ccsidr := SystemControl.CCSIDR.Get()
sets := (ccsidr & SCB_CCSIDR_NUMSETS_Msk) >> SCB_CCSIDR_NUMSETS_Pos
for sets != 0 {
ways := (ccsidr & SCB_CCSIDR_ASSOCIATIVITY_Msk) >> SCB_CCSIDR_ASSOCIATIVITY_Pos
for ways != 0 {
SystemControl.DCISW.Set(
((sets << SCB_DCISW_SET_Pos) & SCB_DCISW_SET_Msk) |
((ways << SCB_DCISW_WAY_Pos) & SCB_DCISW_WAY_Msk))
ways--
}
sets--
}
arm.AsmFull(`
dsb 0xF
`, nil)
SystemControl.CCR.SetBits(SCB_CCR_DC_Msk)
arm.AsmFull(`
dsb 0xF
isb 0xF
`, nil)
} else {
var (
ccsidr volatile.Register32
sets volatile.Register32
ways volatile.Register32
)
SystemControl.CSSELR.Set(0)
arm.AsmFull(`
dsb 0xF
`, nil)
SystemControl.CCR.ClearBits(SCB_CCR_DC_Msk)
arm.AsmFull(`
dsb 0xF
`, nil)
ccsidr.Set(SystemControl.CCSIDR.Get())
sets.Set((ccsidr.Get() & SCB_CCSIDR_NUMSETS_Msk) >> SCB_CCSIDR_NUMSETS_Pos)
for sets.Get() != 0 {
ways.Set((ccsidr.Get() & SCB_CCSIDR_ASSOCIATIVITY_Msk) >> SCB_CCSIDR_ASSOCIATIVITY_Pos)
for ways.Get() != 0 {
SystemControl.DCCISW.Set(
((sets.Get() << SCB_DCCISW_SET_Pos) & SCB_DCCISW_SET_Msk) |
((ways.Get() << SCB_DCCISW_WAY_Pos) & SCB_DCCISW_WAY_Msk))
ways.Set(ways.Get() - 1)
}
sets.Set(sets.Get() - 1)
}
arm.AsmFull(`
dsb 0xF
isb 0xF
`, nil)
}
}
}
-57
View File
@@ -1,57 +0,0 @@
// Hand created file. DO NOT DELETE.
// STM32FXXX (except stm32f1xx) bitfield definitions that are not
// auto-generated by gen-device-svd.go
// These apply to the stm32 families that use the MODER, OTYPE amd PUPDR
// registers for managing GPIO functionality.
// Add in other families that use the same settings, e.g. stm32f0xx, etc
// +build stm32,!stm32f103xx
package stm32
// AltFunc represents the alternate function peripherals that can be mapped to
// the GPIO ports. Since these differ by what is supported on the stm32 family
// they are defined in the more specific files
type AltFunc uint8
// Family-wide common post-reset AltFunc. This represents
// normal GPIO operation of the pins
const AF0_SYSTEM AltFunc = 0
const (
// Register values for the chip
// GPIOx_MODER
GPIOModeInput = 0
GPIOModeOutputGeneral = 1
GPIOModeOutputAltFunc = 2
GPIOModeAnalog = 3
// GPIOx_OTYPER
GPIOOutputTypePushPull = 0
GPIOOutputTypeOpenDrain = 1
// GPIOx_OSPEEDR
GPIOSpeedLow = 0
GPIOSpeedMid = 1
GPIOSpeedHigh = 2 // Note: this is also low speed on stm32f0, see RM0091
GPIOSpeedVeryHigh = 3
// GPIOx_PUPDR
GPIOPUPDRFloating = 0
GPIOPUPDRPullUp = 1
GPIOPUPDRPullDown = 2
)
// SPI prescaler values fPCLK / X
const (
SPI_PCLK_2 = 0
SPI_PCLK_4 = 1
SPI_PCLK_8 = 2
SPI_PCLK_16 = 3
SPI_PCLK_32 = 4
SPI_PCLK_64 = 5
SPI_PCLK_128 = 6
SPI_PCLK_256 = 7
)
-88
View File
@@ -1,88 +0,0 @@
// Hand created file. DO NOT DELETE.
// STM32F103XX bitfield definitions that are not auto-generated by gen-device-svd.go
// +build stm32,stm32f103xx
package stm32
const (
// Flash Access Control Register flag values.
FLASH_ACR_LATENCY_0 = 0x00000001
FLASH_ACR_LATENCY_1 = 0x00000002
FLASH_ACR_LATENCY_2 = 0x00000004
// Reset and Clock Control Control Register flag values.
// System Clock source
RCC_CFGR_SW_HSI = 0
RCC_CFGR_SW_HSE = 1
RCC_CFGR_SW_PLL = 2
// Flags for when System Clock source is set.
RCC_CFGR_SWS_HSI = 0x00000000
RCC_CFGR_SWS_HSE = 0x00000004
RCC_CFGR_SWS_PLL = 0x00000008
// Sets PCLK1
RCC_CFGR_PPRE1_DIV_NONE = 0x00000000
RCC_CFGR_PPRE1_DIV_2 = 0x00000400
RCC_CFGR_PPRE1_DIV_4 = 0x00000500
RCC_CFGR_PPRE1_DIV_8 = 0x00000600
RCC_CFGR_PPRE1_DIV_16 = 0x00000700
// Sets PCLK2
RCC_CFGR_PPRE2_DIV_NONE = 0x00000000
RCC_CFGR_PPRE2_DIV_2 = 0x00002000
RCC_CFGR_PPRE2_DIV_4 = 0x00002800
RCC_CFGR_PPRE2_DIV_8 = 0x00003000
RCC_CFGR_PPRE2_DIV_16 = 0x00003800
// Sets PLL multiplier
RCC_CFGR_PLLMUL_2 = 0x00000000
RCC_CFGR_PLLMUL_3 = 0x00040000
RCC_CFGR_PLLMUL_4 = 0x00080000
RCC_CFGR_PLLMUL_5 = 0x000C0000
RCC_CFGR_PLLMUL_6 = 0x00100000
RCC_CFGR_PLLMUL_7 = 0x00140000
RCC_CFGR_PLLMUL_8 = 0x00180000
RCC_CFGR_PLLMUL_9 = 0x001C0000
RCC_CFGR_PLLMUL_10 = 0x00200000
RCC_CFGR_PLLMUL_11 = 0x00240000
RCC_CFGR_PLLMUL_12 = 0x00280000
RCC_CFGR_PLLMUL_13 = 0x002C0000
RCC_CFGR_PLLMUL_14 = 0x00300000
RCC_CFGR_PLLMUL_15 = 0x00340000
RCC_CFGR_PLLMUL_16 = 0x00380000
// RTC clock source
RCC_RTCCLKSource_LSE = 0x00000100
RCC_RTCCLKSource_LSI = 0x00000200
RCC_RTCCLKSource_HSE_Div128 = 0x00000300
// SPI settings
SPI_FirstBit_MSB = 0x0000
SPI_FirstBit_LSB = 0x0080
SPI_BaudRatePrescaler_2 = 0x0000
SPI_BaudRatePrescaler_4 = 0x0008
SPI_BaudRatePrescaler_8 = 0x0010
SPI_BaudRatePrescaler_16 = 0x0018
SPI_BaudRatePrescaler_32 = 0x0020
SPI_BaudRatePrescaler_64 = 0x0028
SPI_BaudRatePrescaler_128 = 0x0030
SPI_BaudRatePrescaler_256 = 0x0038
SPI_Direction_2Lines_FullDuplex = 0x0000
SPI_Direction_2Lines_RxOnly = 0x0400
SPI_Direction_1Line_Rx = 0x8000
SPI_Direction_1Line_Tx = 0xC000
SPI_Mode_Master = 0x0104
SPI_Mode_Slave = 0x0000
SPI_NSS_Soft = 0x0200
SPI_NSS_Hard = 0x0000
SPI_NSSInternalSoft_Set = 0x0100
SPI_NSSInternalSoft_Reset = 0xFEFF
)
@@ -1,28 +0,0 @@
// Hand created file. DO NOT DELETE.
// STM32FXXX (except stm32f1xx) bitfield definitions that are not
// auto-generated by gen-device-svd.go
// +build stm32f4
// Alternate function settings on the stm32f4 series
package stm32
const (
// Alternative peripheral pin functions
// AF0_SYSTEM is defined im the common bitfields package
AF1_TIM1_2 AltFunc = 1
AF2_TIM3_4_5 = 2
AF3_TIM8_9_10_11 = 3
AF4_I2C1_2_3 = 4
AF5_SPI1_SPI2 = 5
AF6_SPI3 = 6
AF7_USART1_2_3 = 7
AF8_USART4_5_6 = 8
AF9_CAN1_CAN2_TIM12_13_14 = 9
AF10_OTG_FS_OTG_HS = 10
AF11_ETH = 11
AF12_FSMC_SDIO_OTG_HS_1 = 12
AF13_DCMI = 13
AF14 = 14
AF15_EVENTOUT = 15
)
+28 -1
View File
@@ -45,6 +45,11 @@ func Pause() {
currentTask.state.pause()
}
//export tinygo_pause
func pause() {
Pause()
}
// Resume the task until it pauses or completes.
// This may only be called from the scheduler.
func (t *Task) Resume() {
@@ -58,10 +63,32 @@ func (s *state) initialize(fn uintptr, args unsafe.Pointer, stackSize uintptr) {
// Create a stack.
stack := make([]uintptr, stackSize/unsafe.Sizeof(uintptr(0)))
// Set up the stack canary, a random number that should be checked when
// switching from the task back to the scheduler. The stack canary pointer
// points to the first word of the stack. If it has changed between now and
// the next stack switch, there was a stack overflow.
s.canaryPtr = &stack[0]
*s.canaryPtr = stackCanary
// Get a pointer to the top of the stack, where the initial register values
// are stored. They will be popped off the stack on the first stack switch
// to the goroutine, and will start running tinygo_startTask (this setup
// happens in archInit).
r := (*calleeSavedRegs)(unsafe.Pointer(&stack[uintptr(len(stack))-(unsafe.Sizeof(calleeSavedRegs{})/unsafe.Sizeof(uintptr(0)))]))
// Invoke architecture-specific initialization.
s.archInit(stack, fn, args)
s.archInit(r, fn, args)
}
//export tinygo_swapTask
func swapTask(oldStack uintptr, newStack *uintptr)
// startTask is a small wrapper function that sets up the first (and only)
// argument to the new goroutine and makes sure it is exited when the goroutine
// finishes.
//go:extern tinygo_startTask
var startTask [0]uint8
//go:linkname runqueuePushBack runtime.runqueuePushBack
func runqueuePushBack(*Task)
+99
View File
@@ -0,0 +1,99 @@
.section .bss.tinygo_systemStack
.global tinygo_systemStack
.type tinygo_systemStack, %object
tinygo_systemStack:
.short 0
.section .text.tinygo_startTask
.global tinygo_startTask
.type tinygo_startTask, %function
tinygo_startTask:
// Small assembly stub for starting a goroutine. This is already run on the
// new stack, with the callee-saved registers already loaded.
// Most importantly, r2r3 contain the pc of the to-be-started function and
// r4r5 contain the only argument it is given. Multiple arguments are packed
// into one by storing them in a new allocation.
// Set the first argument of the goroutine start wrapper, which contains all
// the arguments.
movw r24, r4
// Branch to the "goroutine start" function. Note that the Z register is
// call-clobbered, so does not need to be restored after use.
movw Z, r2
icall
// After return, exit this goroutine. This is a tail call.
#if __AVR_ARCH__ == 2 || __AVR_ARCH__ == 25
// Small memory devices (8kB flash) that do not have the long call
// instruction availble will need to use rcall instead.
// Note that they will probably not be able to run more than the main
// goroutine anyway, but this file is compiled for all AVRs so it needs to
// compile at least.
rcall tinygo_pause
#else
// Other devices can (and must) use the regular call instruction.
call tinygo_pause
#endif
.global tinygo_swapTask
.type tinygo_swapTask, %function
tinygo_swapTask:
// This function gets the following parameters:
// r24:r25 = newStack uintptr
// r22:r23 = oldStack *uintptr
// Save all call-saved registers:
// https://gcc.gnu.org/wiki/avr-gcc#Call-Saved_Registers
push r29 // Y
push r28 // Y
push r17
push r16
push r15
push r14
push r13
push r12
push r11
push r10
push r9
push r8
push r7
push r6
push r5
push r4
push r3
push r2
// Save the current stack pointer in oldStack.
in r2, 0x3d; SPL
in r3, 0x3e; SPH
movw Y, r22
std Y+0, r2
std Y+1, r3
// Switch to the new stack pointer.
out 0x3d, r24; SPL
out 0x3e, r25; SPH
// Load saved register from the new stack.
pop r2
pop r3
pop r4
pop r5
pop r6
pop r7
pop r8
pop r9
pop r10
pop r11
pop r12
pop r13
pop r14
pop r15
pop r16
pop r17
pop r28 // Y
pop r29 // Y
// Return into the new task, as if tinygo_swapTask was a regular call.
ret
+15 -34
View File
@@ -4,9 +4,12 @@ package task
import "unsafe"
//go:extern tinygo_systemStack
var systemStack uintptr
// calleeSavedRegs is the list of registers that must be saved and restored when
// switching between tasks. Also see scheduler_avr.S that relies on the
// exact layout of this struct.
// switching between tasks. Also see task_stack_avr.S that relies on the exact
// layout of this struct.
//
// https://gcc.gnu.org/wiki/avr-gcc#Call-Saved_Registers
type calleeSavedRegs struct {
@@ -23,34 +26,15 @@ type calleeSavedRegs struct {
pc uintptr
}
// registers gets a pointer to the registers stored at the top of the stack.
func (s *state) registers() *calleeSavedRegs {
return (*calleeSavedRegs)(unsafe.Pointer(s.sp + 1))
}
// startTask is a small wrapper function that sets up the first (and only)
// argument to the new goroutine and makes sure it is exited when the goroutine
// finishes.
//go:extern tinygo_startTask
var startTask [0]uint8
// archInit runs architecture-specific setup for the goroutine startup.
// Note: adding //go:noinline to work around an AVR backend bug.
//go:noinline
func (s *state) archInit(stack []uintptr, fn uintptr, args unsafe.Pointer) {
// Set up the stack canary, a random number that should be checked when
// switching from the task back to the scheduler. The stack canary pointer
// points to the first word of the stack. If it has changed between now and
// the next stack switch, there was a stack overflow.
s.canaryPtr = &stack[0]
*s.canaryPtr = stackCanary
func (s *state) archInit(r *calleeSavedRegs, fn uintptr, args unsafe.Pointer) {
// Store the initial sp for the startTask function (implemented in assembly).
s.sp = uintptr(unsafe.Pointer(&stack[uintptr(len(stack))-(unsafe.Sizeof(calleeSavedRegs{})/unsafe.Sizeof(uintptr(0)))])) - 1
s.sp = uintptr(unsafe.Pointer(r)) - 1
// Initialize the registers.
// These will be popped off of the stack on the first resume of the goroutine.
r := s.registers()
// Start the function at tinygo_startTask.
startTask := uintptr(unsafe.Pointer(&startTask))
@@ -67,20 +51,17 @@ func (s *state) archInit(stack []uintptr, fn uintptr, args unsafe.Pointer) {
}
func (s *state) resume() {
switchToTask(s.sp)
swapTask(s.sp, &systemStack)
}
//export tinygo_switchToTask
func switchToTask(uintptr)
//export tinygo_switchToScheduler
func switchToScheduler(*uintptr)
func (s *state) pause() {
switchToScheduler(&s.sp)
newStack := systemStack
systemStack = 0
swapTask(newStack, &s.sp)
}
//export tinygo_pause
func pause() {
Pause()
// SystemStack returns the system stack pointer when called from a task stack.
// When called from the system stack, it returns 0.
func SystemStack() uintptr {
return systemStack
}
@@ -30,17 +30,6 @@ tinygo_startTask:
.cfi_endproc
.size tinygo_startTask, .-tinygo_startTask
.section .text.tinygo_getSystemStackPointer
.global tinygo_getSystemStackPointer
.type tinygo_getSystemStackPointer, %function
tinygo_getSystemStackPointer:
.cfi_startproc
// The system stack pointer is always stored in the MSP register.
mrs r0, MSP
bx lr
.cfi_endproc
.size tinygo_getSystemStackPointer, .-tinygo_getSystemStackPointer
.section .text.tinygo_switchToScheduler
.global tinygo_switchToScheduler
.type tinygo_switchToScheduler, %function
@@ -136,34 +125,3 @@ tinygo_swapTask:
#endif
.cfi_endproc
.size tinygo_swapTask, .-tinygo_swapTask
.section .text.tinygo_scanCurrentStack
.global tinygo_scanCurrentStack
.type tinygo_scanCurrentStack, %function
tinygo_scanCurrentStack:
.cfi_startproc
// Save callee-saved registers onto the stack.
#if defined(__thumb2__)
push {r4-r11, lr}
.cfi_def_cfa_offset 9*4
#else
mov r0, r8
mov r1, r9
mov r2, r10
mov r3, r11
push {r0-r3, lr}
.cfi_def_cfa_offset 5*4
push {r4-r7}
.cfi_def_cfa_offset 4*4
#endif
// Scan the stack.
mov r0, sp
bl tinygo_scanstack
// Restore stack state and return.
add sp, #32
.cfi_def_cfa_offset 1*4
pop {pc}
.cfi_endproc
.size tinygo_scanCurrentStack, .-tinygo_scanCurrentStack
+12 -27
View File
@@ -2,10 +2,13 @@
package task
import "unsafe"
import (
"device/arm"
"unsafe"
)
// calleeSavedRegs is the list of registers that must be saved and restored when
// switching between tasks. Also see scheduler_cortexm.S that relies on the
// switching between tasks. Also see task_stack_cortexm.S that relies on the
// exact layout of this struct.
type calleeSavedRegs struct {
r4 uintptr
@@ -20,34 +23,15 @@ type calleeSavedRegs struct {
pc uintptr
}
// registers gets a pointer to the registers stored at the top of the stack.
func (s *state) registers() *calleeSavedRegs {
return (*calleeSavedRegs)(unsafe.Pointer(s.sp))
}
// startTask is a small wrapper function that sets up the first (and only)
// argument to the new goroutine and makes sure it is exited when the goroutine
// finishes.
//go:extern tinygo_startTask
var startTask [0]uint8
// archInit runs architecture-specific setup for the goroutine startup.
func (s *state) archInit(stack []uintptr, fn uintptr, args unsafe.Pointer) {
// Set up the stack canary, a random number that should be checked when
// switching from the task back to the scheduler. The stack canary pointer
// points to the first word of the stack. If it has changed between now and
// the next stack switch, there was a stack overflow.
s.canaryPtr = &stack[0]
*s.canaryPtr = stackCanary
func (s *state) archInit(r *calleeSavedRegs, fn uintptr, args unsafe.Pointer) {
// Store the initial sp for the startTask function (implemented in assembly).
s.sp = uintptr(unsafe.Pointer(&stack[uintptr(len(stack))-(unsafe.Sizeof(calleeSavedRegs{})/unsafe.Sizeof(uintptr(0)))]))
s.sp = uintptr(unsafe.Pointer(r))
// Initialize the registers.
// These will be popped off of the stack on the first resume of the goroutine.
r := s.registers()
// Start the function at tinygo_startTask (defined in src/runtime/scheduler_cortexm.S).
// Start the function at tinygo_startTask (defined in src/internal/task/task_stack_cortexm.S).
// This assembly code calls a function (passed in r4) with a single argument (passed in r5).
// After the function returns, it calls Pause().
r.pc = uintptr(unsafe.Pointer(&startTask))
@@ -75,7 +59,8 @@ func (s *state) pause() {
switchToScheduler(&s.sp)
}
//export tinygo_pause
func pause() {
Pause()
// SystemStack returns the system stack pointer. On Cortex-M, it is always
// available.
func SystemStack() uintptr {
return arm.AsmFull("mrs {}, MSP", nil)
}
+86
View File
@@ -0,0 +1,86 @@
.section .text.tinygo_startTask,"ax",@progbits
.global tinygo_startTask
.type tinygo_startTask, %function
tinygo_startTask:
// Small assembly stub for starting a goroutine. This already runs on the
// new stack, control reaches this function after returning from the initial
// tinygo_swapTask below (the retw.n instruction).
//
// The stack was set up in such a way that it looks as if this function was
// paused using tinygo_swapTask by setting up the parent register window and
// return pointer as a call4 instruction - except such a call never took
// place. Instead, the stack pointer is switched to the new stack after all
// live-but-invisible registers have been flushed to the stack. This means
// that all registers as present in tinygo_swapTask are moved four up (a2 in
// tinygo_swapTask is a6 in this function). We don't use any of those
// registers however. Instead, the retw.n instruction will load them through
// an underflow exception from the stack which means we get a0-a3 as defined
// in task_stack_esp32.go.
// Branch to the "goroutine start" function. The first (and only) parameter
// is stored in a2, but has to be moved to a6 to make it appear as a2 in the
// goroutine start function (due to changing the register window by four
// with callx4).
mov.n a6, a2
callx4 a3
// After return, exit this goroutine. This call never returns.
call4 tinygo_pause
.section .text.tinygo_swapTask,"ax",@progbits
.global tinygo_swapTask
.type tinygo_swapTask, %function
tinygo_swapTask:
// This function gets the following parameters:
// a2 = newStack uintptr
// a3 = oldStack *uintptr
// Reserve 32 bytes on the stack. It really needs to be 32 bytes, with 16
// extra at the bottom to adhere to the ABI.
entry sp, 32
// Disable interrupts while flushing registers. This is necessary because
// interrupts might want to use the stack pointer (at a2) which will be some
// arbitrary register while registers are flushed.
rsil a4, 3 // XCHAL_EXCM_LEVEL
// Flush all unsaved registers to the stack.
// This trick has been borrowed from the Zephyr project:
// https://github.com/zephyrproject-rtos/zephyr/blob/d79b003758/arch/xtensa/include/xtensa-asm2-s.h#L17
and a12, a12, a12
rotw 3
and a12, a12, a12
rotw 3
and a12, a12, a12
rotw 3
and a12, a12, a12
rotw 3
and a12, a12, a12
rotw 4
// Restore interrupts.
wsr.ps a4
// At this point, the following is true:
// WindowStart == 1 << WindowBase
// Therefore, we don't need to do this manually.
// It also means that the stack pointer can now be safely modified.
// Save a0, which stores the return address and the parent register window
// in the upper two bits.
s32i.n a0, sp, 0
// Save the current stack pointer in oldStack.
s32i.n sp, a3, 0
// Switch to the new stack pointer (newStack).
mov.n sp, a2
// Load a0, which is the previous return addres from before the previous
// switch or the constructed return address to tinygo_startTask. This
// register also stores the parent register window.
l32i.n a0, sp, 0
// Return into the new stack. This instruction will trigger a window
// underflow, reloading the saved registers from the stack.
retw.n
+76
View File
@@ -0,0 +1,76 @@
// +build scheduler.tasks,esp32
package task
// The windowed ABI (used on the ESP32) is as follows:
// a0: return address (link register)
// a1: stack pointer (must be 16-byte aligned)
// a2-a7: incoming arguments
// a7: stack frame pointer (optional, normally unused in TinyGo)
// Sources:
// http://cholla.mmto.org/esp8266/xtensa.html
// https://0x04.net/~mwk/doc/xtensa.pdf
import (
"unsafe"
)
var systemStack uintptr
// calleeSavedRegs is the list of registers that must be saved and restored when
// switching between tasks. Also see task_stack_esp8266.S that relies on the
// exact layout of this struct.
type calleeSavedRegs struct {
// Registers in the register window of tinygo_startTask.
a0 uintptr
a1 uintptr
a2 uintptr
a3 uintptr
// Locals that can be used by tinygo_swapTask.
// The first field is the a0 loaded in tinygo_swapTask, the rest is unused.
locals [4]uintptr
}
// archInit runs architecture-specific setup for the goroutine startup.
func (s *state) archInit(r *calleeSavedRegs, fn uintptr, args unsafe.Pointer) {
// Store the stack pointer for the tinygo_swapTask function (implemented in
// assembly). It needs to point to the locals field instead of a0 so that
// the retw.n at the end of tinygo_swapTask will return into
// tinygo_startTask with a0-a3 loaded (using the register window mechanism).
s.sp = uintptr(unsafe.Pointer(&r.locals[0]))
// Start the goroutine at tinygo_startTask (defined in
// src/internal/task/task_stack_esp32.S). The topmost two bits are not part
// of the address but instead store the register window of the caller.
// In this case there is no caller, instead we set up the return address as
// if tinygo_startTask called tinygo_swapTask with a call4 instruction.
r.locals[0] = uintptr(unsafe.Pointer(&startTask))&^(3<<30) | (1 << 30)
// Set up the stack pointer inside tinygo_startTask.
// Unlike most calling conventions, the windowed ABI actually saves the
// stack pointer on the stack to make register windowing work.
r.a1 = uintptr(unsafe.Pointer(r)) + 32
// Store the function pointer and the (only) parameter on the stack in a
// location that will be reloaded into registers when doing the
// pseudo-return to tinygo_startTask using the register window mechanism.
r.a3 = fn
r.a2 = uintptr(args)
}
func (s *state) resume() {
swapTask(s.sp, &systemStack)
}
func (s *state) pause() {
newStack := systemStack
systemStack = 0
swapTask(newStack, &s.sp)
}
// SystemStack returns the system stack pointer when called from a task stack.
// When called from the system stack, it returns 0.
func SystemStack() uintptr {
return systemStack
}
+54
View File
@@ -0,0 +1,54 @@
.section .text.tinygo_startTask,"ax",@progbits
.global tinygo_startTask
.type tinygo_startTask, %function
tinygo_startTask:
// Small assembly stub for starting a goroutine. This is already run on the
// new stack, with the callee-saved registers already loaded.
// Most importantly, r4 contains the pc of the to-be-started function and r5
// contains the only argument it is given. Multiple arguments are packed
// into one by storing them in a new allocation.
// Set the first argument of the goroutine start wrapper, which contains all
// the arguments.
mov.n a2, a13
// Branch to the "goroutine start" function.
callx0 a12
// After return, exit this goroutine. This is a tail call.
call0 tinygo_pause
.size tinygo_startTask, .-tinygo_startTask
.global tinygo_swapTask
.type tinygo_swapTask, %function
tinygo_swapTask:
// This function gets the following parameters:
// a2 = newStack uintptr
// a3 = oldStack *uintptr
// Note:
// a0 is the return address
// a1 is the stack pointer (sp)
// Save all callee-saved registers:
addi sp, sp, -20
s32i.n a12, sp, 0
s32i.n a13, sp, 4
s32i.n a14, sp, 8
s32i.n a15, sp, 12
s32i.n a0, sp, 16
// Save the current stack pointer in oldStack.
s32i.n sp, a3, 0
// Switch to the new stack pointer.
mov.n sp, a2
// Load state from new task and branch to the previous position in the
// program.
l32i.n a12, sp, 0
l32i.n a13, sp, 4
l32i.n a14, sp, 8
l32i.n a15, sp, 12
l32i.n a0, sp, 16
addi sp, sp, 20
ret.n
+70
View File
@@ -0,0 +1,70 @@
// +build scheduler.tasks,esp8266
package task
// Stack switch implementation for the ESP8266, which does not use the windowed
// ABI of Xtensa. Registers are assigned as follows:
// a0: return address (link register)
// a1: stack pointer (must be 16-byte aligned)
// a2-a7: incoming arguments
// a8: static chain (unused)
// a12-a15: callee-saved
// a15: stack frame pointer (optional, unused)
// Sources:
// http://cholla.mmto.org/esp8266/xtensa.html
// https://0x04.net/~mwk/doc/xtensa.pdf
import "unsafe"
var systemStack uintptr
// calleeSavedRegs is the list of registers that must be saved and restored when
// switching between tasks. Also see task_stack_esp8266.S that relies on the
// exact layout of this struct.
type calleeSavedRegs struct {
a12 uintptr
a13 uintptr
a14 uintptr
a15 uintptr
pc uintptr // also link register or r0
}
// archInit runs architecture-specific setup for the goroutine startup.
func (s *state) archInit(r *calleeSavedRegs, fn uintptr, args unsafe.Pointer) {
// Store the initial sp for the startTask function (implemented in assembly).
s.sp = uintptr(unsafe.Pointer(r))
// Initialize the registers.
// These will be popped off of the stack on the first resume of the goroutine.
// Start the function at tinygo_startTask (defined in
// src/internal/task/task_stack_esp8266.S).
// This assembly code calls a function (passed in a12) with a single argument
// (passed in a13). After the function returns, it calls Pause().
r.pc = uintptr(unsafe.Pointer(&startTask))
// Pass the function to call in a12.
// This function is a compiler-generated wrapper which loads arguments out of a struct pointer.
// See createGoroutineStartWrapper (defined in compiler/goroutine.go) for more information.
r.a12 = fn
// Pass the pointer to the arguments struct in a13.
r.a13 = uintptr(args)
}
func (s *state) resume() {
swapTask(s.sp, &systemStack)
}
func (s *state) pause() {
newStack := systemStack
systemStack = 0
swapTask(newStack, &s.sp)
}
// SystemStack returns the system stack pointer when called from a task stack.
// When called from the system stack, it returns 0.
func SystemStack() uintptr {
return systemStack
}
+91
View File
@@ -0,0 +1,91 @@
// +build arduino_mkr1000
// This contains the pin mappings for the Arduino MKR1000 board.
//
// For more information, see: https://store.arduino.cc/usa/arduino-mkr1000-with-headers-mounted
//
package machine
// used to reset into bootloader
const RESET_MAGIC_VALUE = 0x07738135
// GPIO Pins
const (
RX0 Pin = PB23 // UART2 RX
TX1 Pin = PB22 // UART2 TX
D0 Pin = PA22 // PWM available
D1 Pin = PA23 // PWM available
D2 Pin = PA10 // PWM available
D3 Pin = PA11 // PWM available
D4 Pin = PB10 // PWM available
D5 Pin = PB11 // PWM available
D6 Pin = PA20 // PWM available
D7 Pin = PA21 // PWM available
D8 Pin = PA16 // PWM available
D9 Pin = PA17
D10 Pin = PA19 // PWM available
D11 Pin = PA08 // SDA
D12 Pin = PA09 // PWM available, SCL
D13 Pin = PB23 // RX
D14 Pin = PB22 // TX
)
// Analog pins
const (
A0 Pin = PA02 // ADC0/AIN[0]
A1 Pin = PB02 // AIN[10]
A2 Pin = PB03 // AIN[11]
A3 Pin = PA04 // AIN[04]
A4 Pin = PA05 // AIN[05]
A5 Pin = PA06 // AIN[06]
A6 Pin = PA07 // AIN[07]
)
const (
LED = D6
)
// UART0 aka USBCDC pins
const (
USBCDC_DM_PIN Pin = PA24
USBCDC_DP_PIN Pin = PA25
)
// UART1 pins
const (
UART_TX_PIN Pin = PB22
UART_RX_PIN Pin = PB23
)
// I2C pins
const (
SDA_PIN Pin = D11 // SDA
SCL_PIN Pin = D12 // SCL
)
// SPI pins
const (
SPI0_SCK_PIN Pin = D9 // SCK: S1
SPI0_SDO_PIN Pin = D8 // SDO: S1
SPI0_SDI_PIN Pin = D10 // SDI: S1
)
// I2S pins
const (
I2S_SCK_PIN Pin = PA10
I2S_SD_PIN Pin = PA07
I2S_WS_PIN = NoPin // TODO: figure out what this is on Arduino Nano 33.
)
// USB CDC identifiers
const (
usb_STRING_PRODUCT = "Arduino MKR1000"
usb_STRING_MANUFACTURER = "Arduino"
)
var (
usb_VID uint16 = 0x2341
usb_PID uint16 = 0x804e
)
+1 -1
View File
@@ -58,7 +58,7 @@ const (
// UART0 is the USB device
var (
UART0 = USB
UART0 = &USB
)
// I2C pins
+1 -1
View File
@@ -105,7 +105,7 @@ const (
// UART0 is the USB device
var (
UART0 = USB
UART0 = &USB
)
// I2C pins
+88
View File
@@ -0,0 +1,88 @@
// +build esp32_coreboard_v2
package machine
// Built-in LED on some ESP32 boards.
const LED = IO2
const (
CLK Pin = 6
CMD Pin = 11
IO0 Pin = 0
IO1 Pin = 1
IO10 Pin = 10
IO16 Pin = 16
IO17 Pin = 17
IO18 Pin = 18
IO19 Pin = 19
IO2 Pin = 2
IO21 Pin = 21
IO22 Pin = 22
IO23 Pin = 23
IO25 Pin = 25
IO26 Pin = 26
IO27 Pin = 27
IO3 Pin = 3
IO32 Pin = 32
IO33 Pin = 33
IO34 Pin = 34
IO35 Pin = 35
IO36 Pin = 36
IO39 Pin = 39
IO4 Pin = 4
IO5 Pin = 5
IO9 Pin = 9
RXD Pin = 3
SD0 Pin = 7
SD1 Pin = 8
SD2 Pin = 9
SD3 Pin = 10
SVN Pin = 39
SVP Pin = 36
TCK Pin = 13
TD0 Pin = 15
TDI Pin = 12
TMS Pin = 14
TXD Pin = 1
)
// SPI pins
const (
SPI0_SCK_PIN = IO18
SPI0_SDO_PIN = IO23
SPI0_SDI_PIN = IO19
SPI0_CS0_PIN = IO5
)
// I2C pins
const (
SDA_PIN = IO21
SCL_PIN = IO22
)
// ADC pins
const (
ADC0 Pin = IO34
ADC1 Pin = IO35
ADC2 Pin = IO36
ADC3 Pin = IO39
)
// UART0 pins
const (
UART_TX_PIN = IO1
UART_RX_PIN = IO3
)
// UART1 pins
const (
UART1_TX_PIN = IO9
UART1_RX_PIN = IO10
)
// PWM pins
const (
PWM0_PIN Pin = IO2
PWM1_PIN Pin = IO0
PWM2_PIN Pin = IO4
)
-6
View File
@@ -1,6 +0,0 @@
// +build esp32_wroom_32
package machine
// Blue LED on the ESP32-WROOM-32 module.
const LED = Pin(2)
+1 -1
View File
@@ -77,7 +77,7 @@ const (
// UART0 is the USB device
var (
UART0 = USB
UART0 = &USB
)
// I2C pins
+39 -7
View File
@@ -122,17 +122,17 @@ var (
UART1 = UART{
Buffer: NewRingBuffer(),
Bus: stm32.USART3,
AltFuncSelector: stm32.AF7_USART1_2_3,
AltFuncSelector: AF7_USART1_2_3,
}
UART2 = UART{
Buffer: NewRingBuffer(),
Bus: stm32.USART6,
AltFuncSelector: stm32.AF8_USART4_5_6,
AltFuncSelector: AF8_USART4_5_6,
}
UART3 = UART{
Buffer: NewRingBuffer(),
Bus: stm32.USART1,
AltFuncSelector: stm32.AF7_USART1_2_3,
AltFuncSelector: AF7_USART1_2_3,
}
UART0 = UART1
)
@@ -178,6 +178,22 @@ const (
SPI_SDO_PIN = SPI0_SDO_PIN //
)
var (
SPI1 = SPI{
Bus: stm32.SPI2,
AltFuncSelector: AF5_SPI1_SPI2,
}
SPI2 = SPI{
Bus: stm32.SPI3,
AltFuncSelector: AF6_SPI3,
}
SPI3 = SPI{
Bus: stm32.SPI1,
AltFuncSelector: AF5_SPI1_SPI2,
}
SPI0 = SPI1
)
func initSPI() {}
// -- I2C ----------------------------------------------------------------------
@@ -186,11 +202,11 @@ const (
// #===========#==========#==============#==============#=======#=======#
// | Interface | Hardware | Bus(Freq) | SDA/SCL Pins | AltFn | Alias |
// #===========#==========#==============#==============#=======#=======#
// | I2C1 | I2C1 | | D14/D15 | | ~ |
// | I2C2 | I2C2 | | D0/D1 | | ~ |
// | I2C3 | I2C1 | | D9/D10 | | ~ |
// | I2C1 | I2C1 | APB1(42 MHz) | D14/D15 | 4 | ~ |
// | I2C2 | I2C2 | APB1(42 MHz) | D0/D1 | 4 | ~ |
// | I2C3 | I2C1 | APB1(42 MHz) | D9/D10 | 4 | ~ |
// | --------- | -------- | ------------ | ------------ | ----- | ----- |
// | I2C0 | I2C1 | | D14/D15 | | I2C1 |
// | I2C0 | I2C1 | APB1(42 MHz) | D14/D15 | 4 | I2C1 |
// #===========#==========#==============#==============#=======#=======#
NUM_I2C_INTERFACES = 3
@@ -210,4 +226,20 @@ const (
I2C_SCL_PIN = I2C0_SCL_PIN //
)
var (
I2C1 = I2C{
Bus: stm32.I2C1,
AltFuncSelector: AF4_I2C1_2_3,
}
I2C2 = I2C{
Bus: stm32.I2C2,
AltFuncSelector: AF4_I2C1_2_3,
}
I2C3 = I2C{
Bus: stm32.I2C1,
AltFuncSelector: AF4_I2C1_2_3,
}
I2C0 = I2C1
)
func initI2C() {}
+1 -1
View File
@@ -72,7 +72,7 @@ const (
// UART0 is the USB device
var (
UART0 = USB
UART0 = &USB
)
// I2C pins
+80
View File
@@ -0,0 +1,80 @@
// +build lgt92
package machine
import (
"device/stm32"
"runtime/interrupt"
)
const (
LED1 = PA12
LED2 = PA8
LED3 = PA11
LED_RED = LED1
LED_BLUE = LED2
LED_GREEN = LED3
// Default led
LED = LED1
BUTTON = PB14
// LG GPS module
GPS_STANDBY_PIN = PB3
GPS_RESET_PIN = PB4
GPS_POWER_PIN = PB5
MEMS_ACCEL_CS = PE3
MEMS_ACCEL_INT1 = PE0
MEMS_ACCEL_INT2 = PE1
// SPI
SPI1_SCK_PIN = PA5
SPI1_SDI_PIN = PA6
SPI1_SDO_PIN = PA7
SPI0_SCK_PIN = SPI1_SCK_PIN
SPI0_SDI_PIN = SPI1_SDI_PIN
SPI0_SDO_PIN = SPI1_SDO_PIN
// LORA RFM95 Radio
RFM95_DIO0_PIN = PC13
//TinyGo UART is MCU LPUSART1
UART_RX_PIN = PA13
UART_TX_PIN = PA14
//TinyGo UART1 is MCU USART1
UART1_RX_PIN = PB6
UART1_TX_PIN = PB7
)
var (
// Console UART (LPUSART1)
UART0 = UART{
Buffer: NewRingBuffer(),
Bus: stm32.LPUART1,
AltFuncSelector: 6,
}
// Gps UART
UART1 = UART{
Buffer: NewRingBuffer(),
Bus: stm32.USART1,
AltFuncSelector: 0,
}
// SPI
SPI0 = SPI{
Bus: stm32.SPI1,
}
SPI1 = &SPI0
)
func init() {
// Enable UARTs Interrupts
UART0.Interrupt = interrupt.New(stm32.IRQ_AES_RNG_LPUART1, UART0.handleInterrupt)
UART1.Interrupt = interrupt.New(stm32.IRQ_USART1, UART1.handleInterrupt)
}
+178
View File
@@ -0,0 +1,178 @@
// +build matrixportal_m4
package machine
// used to reset into bootloader
const RESET_MAGIC_VALUE = 0xF01669EF
// Digital pins
const (
// Pin // Function SERCOM PWM Interrupt
// ---- // ---------------- ------ --- ---------
D0 = PA01 // UART RX 1[1] PWM EXTI1
D1 = PA00 // UART TX 1[0] PWM EXTI0
D2 = PB22 // Button "Up" EXTI6
D3 = PB23 // Button "Down" EXTI7
D4 = PA23 // NeoPixel EXTI7
D5 = PB31 // I2C SDA 5[1] EXTI15
D6 = PB30 // I2C SCL 5[0] EXTI14
D7 = PB00 // HUB75 R1 EXTI0
D8 = PB01 // HUB75 G1 EXTI1
D9 = PB02 // HUB75 B1 EXTI2
D10 = PB03 // HUB75 R2 EXTI3
D11 = PB04 // HUB75 G2 EXTI4
D12 = PB05 // HUB75 B2 EXTI5
D13 = PA14 // LED PWM EXTI14
D14 = PB06 // HUB75 CLK EXTI6
D15 = PB14 // HUB75 LAT EXTI14
D16 = PB12 // HUB75 OE EXTI12
D17 = PB07 // HUB75 ADDR A EXTI7
D18 = PB08 // HUB75 ADDR B EXTI8
D19 = PB09 // HUB75 ADDR C EXTI9
D20 = PB15 // HUB75 ADDR D EXTI15
D21 = PB13 // HUB75 ADDR E EXTI13
D22 = PA02 // ADC (A0) EXTI2
D23 = PA05 // ADC (A1) EXTI5
D24 = PA04 // ADC (A2) PWM EXTI4
D25 = PA06 // ADC (A3) PWM EXTI6
D26 = PA07 // ADC (A4) EXTI7
D27 = PA12 // ESP32 UART RX 4[1] PWM EXTI12
D28 = PA13 // ESP32 UART TX 4[0] PWM EXTI13
D29 = PA20 // ESP32 GPIO0 PWM EXTI4
D30 = PA21 // ESP32 Reset PWM EXTI5
D31 = PA22 // ESP32 Busy PWM EXTI6
D32 = PA18 // ESP32 RTS PWM EXTI2
D33 = PB17 // ESP32 SPI CS PWM EXTI1
D34 = PA16 // ESP32 SPI SCK 3[1] PWM EXTI0
D35 = PA17 // ESP32 SPI SDI 3[0] PWM EXTI1
D36 = PA19 // ESP32 SPI SDO 1[3] PWM EXTI3
D37 = NoPin // USB Host enable
D38 = PA24 // USB DM
D39 = PA27 // USB DP
D40 = PA03 // DAC/VREFP
D41 = PB10 // Flash QSPI SCK
D42 = PB11 // Flash QSPI CS
D43 = PA08 // Flash QSPI I00
D44 = PA09 // Flash QSPI IO1
D45 = PA10 // Flash QSPI IO2
D46 = PA11 // Flash QSPI IO3
D47 = PA27 // LIS3DH IRQ EXTI11
D48 = PA05 // SPI SCK 0[1] EXTI5
D49 = PA04 // SPI SDO 0[0] PWM EXTI4
D50 = PA07 // SPI SDI 0[3] EXTI7
)
// Analog pins
const (
A0 = PA02 // ADC Channel 0
A1 = PA05 // ADC Channel 5
A2 = PA04 // ADC Channel 4
A3 = PA06 // ADC Channel 6
A4 = PA07 // ADC Channel 7
)
// LED pins
const (
LED = D13
NEOPIXEL = D4
)
// Button pins
const (
BUTTON_UP = D2
BUTTON_DOWN = D3
)
// UART pins
const (
UART1_RX_PIN = D0 // SERCOM1[1]
UART1_TX_PIN = D1 // SERCOM1[0]
UART2_RX_PIN = D27 // SERCOM4[1] (ESP32 RX)
UART2_TX_PIN = D28 // SERCOM4[0] (ESP32 TX)
UART_RX_PIN = UART1_RX_PIN
UART_TX_PIN = UART1_TX_PIN
)
// SPI pins
const (
SPI0_SCK_PIN = D34 // SERCOM3[1] (ESP32 SCK)
SPI0_SDO_PIN = D36 // SERCOM1[3] (ESP32 SDO)
SPI0_SDI_PIN = D35 // SERCOM3[0] (ESP32 SDI)
SPI1_SCK_PIN = D48 // SERCOM0[1]
SPI1_SDO_PIN = D49 // SERCOM0[0]
SPI1_SDI_PIN = D50 // SERCOM0[3]
SPI_SCK_PIN = SPI0_SCK_PIN
SPI_SDO_PIN = SPI0_SDO_PIN
SPI_SDI_PIN = SPI0_SDI_PIN
)
// I2C pins
const (
I2C0_SDA_PIN = D5 // SERCOM5[1]
I2C0_SCL_PIN = D6 // SERCOM5[0]
I2C_SDA_PIN = I2C0_SDA_PIN
I2C_SCL_PIN = I2C0_SCL_PIN
SDA_PIN = I2C_SDA_PIN // awkward naming required by machine_atsamd51.go
SCL_PIN = I2C_SCL_PIN //
)
// ESP32 pins
const (
NINA_ACK = D31
NINA_GPIO0 = D29
NINA_RESETN = D30
NINA_RX = UART2_RX_PIN
NINA_TX = UART2_TX_PIN
NINA_RTS = D32
NINA_CS = D33
NINA_SDO = SPI0_SDO_PIN
NINA_SDI = SPI0_SDI_PIN
NINA_SCK = SPI0_SCK_PIN
)
// HUB75 pins
const (
HUB75_R1 = D7
HUB75_G1 = D8
HUB75_B1 = D9
HUB75_R2 = D10
HUB75_G2 = D11
HUB75_B2 = D12
HUB75_CLK = D14
HUB75_LAT = D15
HUB75_OE = D16
HUB75_ADDR_A = D17
HUB75_ADDR_B = D18
HUB75_ADDR_C = D19
HUB75_ADDR_D = D20
HUB75_ADDR_E = D21
)
// USB CDC pins (UART0)
const (
USBCDC_DM_PIN = D38
USBCDC_DP_PIN = D39
UART0_RX_PIN = USBCDC_DM_PIN
UART0_TX_PIN = USBCDC_DP_PIN
)
// USB CDC identifiers
const (
usb_STRING_PRODUCT = "Matrix Portal M4"
usb_STRING_MANUFACTURER = "Adafruit Industries"
)
var (
usb_VID uint16 = 0x239A
usb_PID uint16 = 0x80C9
)
@@ -0,0 +1,50 @@
// +build sam,atsamd51,matrixportal_m4
package machine
import (
"device/sam"
"runtime/interrupt"
)
// UART on the MatrixPortal M4
var (
UART1 = UART{
Buffer: NewRingBuffer(),
Bus: sam.SERCOM1_USART_INT,
SERCOM: 1,
}
UART2 = UART{
Buffer: NewRingBuffer(),
Bus: sam.SERCOM4_USART_INT,
SERCOM: 4,
}
)
func init() {
UART1.Interrupt = interrupt.New(sam.IRQ_SERCOM1_1, UART1.handleInterrupt)
UART2.Interrupt = interrupt.New(sam.IRQ_SERCOM4_1, UART2.handleInterrupt)
}
// I2C on the MatrixPortal M4
var (
I2C0 = I2C{
Bus: sam.SERCOM5_I2CM,
SERCOM: 5,
}
)
// SPI on the MatrixPortal M4
var (
SPI0 = SPI{
Bus: sam.SERCOM3_SPIM,
SERCOM: 3, // BUG: SDO on SERCOM1!
}
NINA_SPI = SPI0
SPI1 = SPI{
Bus: sam.SERCOM0_SPIM,
SERCOM: 0,
}
)
+114
View File
@@ -0,0 +1,114 @@
// +build microbit_v2
package machine
// The micro:bit does not have a 32kHz crystal on board.
const HasLowFrequencyCrystal = false
const (
LED = P13
LED1 = LED
)
// Buttons on the micro:bit v2 (A and B)
const (
BUTTON Pin = BUTTONA
BUTTONA Pin = P5
BUTTONB Pin = P11
)
// UART pins
const (
UART_TX_PIN Pin = P34
UART_RX_PIN Pin = P33
)
// ADC pins
const (
ADC0 Pin = P0
ADC1 Pin = P1
ADC2 Pin = P2
)
// I2C0 (internal) pins
const (
SDA_PIN Pin = SDA0_PIN
SCL_PIN Pin = SCL0_PIN
SDA0_PIN Pin = P30
SCL0_PIN Pin = P31
)
// I2C1 (external) pins
const (
SDA1_PIN Pin = P20
SCL1_PIN Pin = P19
)
// SPI pins
const (
SPI0_SCK_PIN Pin = P13
SPI0_SDO_PIN Pin = P15
SPI0_SDI_PIN Pin = P14
)
// GPIO/Analog pins
const (
P0 Pin = 2
P1 Pin = 3
P2 Pin = 4
P3 Pin = 31
P4 Pin = 28
P5 Pin = 14
P6 Pin = 37
P7 Pin = 11
P8 Pin = 10
P9 Pin = 9
P10 Pin = 30
P11 Pin = 23
P12 Pin = 12
P13 Pin = 17
P14 Pin = 1
P15 Pin = 13
P16 Pin = 34
P19 Pin = 26
P20 Pin = 32
P21 Pin = 21
P22 Pin = 22
P23 Pin = 15
P24 Pin = 24
P25 Pin = 19
P26 Pin = 36
P27 Pin = 0
P28 Pin = 20
P29 Pin = 5
P30 Pin = 16
P31 Pin = 8
P32 Pin = 25
P33 Pin = 40
P34 Pin = 6
)
// LED matrix pins
const (
LED_COL_1 Pin = P4
LED_COL_2 Pin = P7
LED_COL_3 Pin = P3
LED_COL_4 Pin = P6
LED_COL_5 Pin = P10
LED_ROW_1 Pin = P21
LED_ROW_2 Pin = P22
LED_ROW_3 Pin = P23
LED_ROW_4 Pin = P24
LED_ROW_5 Pin = P25
)
// USB CDC identifiers
const (
usb_STRING_PRODUCT = "BBC micro:bit V2"
usb_STRING_MANUFACTURER = "BBC"
)
var (
usb_VID uint16 = 0x0d28
usb_PID uint16 = 0x0204
)
+14
View File
@@ -19,3 +19,17 @@ const (
// Onboard blue LED (on the AI-Thinker module).
const LED = D4
// SPI pins
const (
SPI0_SCK_PIN = D5
SPI0_SDO_PIN = D7
SPI0_SDI_PIN = D6
SPI0_CS0_PIN = D8
)
// I2C pins
const (
SDA_PIN = D2
SCL_PIN = D1
)
+58
View File
@@ -0,0 +1,58 @@
// +build nucleof722ze
package machine
import (
"device/stm32"
"runtime/interrupt"
)
const (
LED = LED_BUILTIN
LED_BUILTIN = LED_GREEN
LED_GREEN = PB0
LED_BLUE = PB7
LED_RED = PB14
)
const (
BUTTON = BUTTON_USER
BUTTON_USER = PC13
)
// UART pins
const (
// PD8 and PD9 are connected to the ST-Link Virtual Com Port (VCP)
UART_TX_PIN = PD8
UART_RX_PIN = PD9
UART_ALT_FN = 7 // GPIO_AF7_UART3
)
var (
// USART3 is the hardware serial port connected to the onboard ST-LINK
// debugger to be exposed as virtual COM port over USB on Nucleo boards.
// Both UART0 and UART1 refer to USART2.
UART0 = UART{
Buffer: NewRingBuffer(),
Bus: stm32.USART3,
AltFuncSelector: UART_ALT_FN,
}
UART1 = &UART0
)
func init() {
UART0.Interrupt = interrupt.New(stm32.IRQ_USART3, UART0.handleInterrupt)
}
// SPI pins
const (
SPI0_SCK_PIN = PA5
SPI0_SDI_PIN = PA6
SPI0_SDO_PIN = PA7
)
// I2C pins
const (
SCL_PIN = PB6
SDA_PIN = PB7
)
+120
View File
@@ -0,0 +1,120 @@
// +build p1am_100
// This contains the pin mappings for the ProductivityOpen P1AM-100 board.
//
// For more information, see: https://facts-engineering.github.io/
//
package machine
// used to reset into bootloader
const RESET_MAGIC_VALUE = 0x07738135
// Note: On the P1AM-100, pins D8, D9, D10, A3, and A4 are used for
// communication with the base controller.
// GPIO Pins
const (
D0 Pin = PA22 // PWM available
D1 Pin = PA23 // PWM available
D2 Pin = PA10 // PWM available
D3 Pin = PA11 // PWM available
D4 Pin = PB10 // PWM available
D5 Pin = PB11 // PWM available
D6 Pin = PA20 // PWM available
D7 Pin = PA21 // PWM available
D8 Pin = PA16 // PWM available
D9 Pin = PA17
D10 Pin = PA19 // PWM available
D11 Pin = PA08
D12 Pin = PA09
D13 Pin = PB23
D14 Pin = PB22
// Remaining pins are shared with analog pins
D15 Pin = PA02
D16 Pin = PB02
D17 Pin = PB03
D18 Pin = PA04 // PWM available
D19 Pin = PA05 // PWM available
D20 Pin = PA06
D21 Pin = PA07
)
// Analog pins
const (
A0 Pin = PA02 // ADC/AIN[0]
A1 Pin = PB02 // ADC/AIN[10]
A2 Pin = PB03 // ADC/AIN[11]
A3 Pin = PA04 // ADC/AIN[4]
A4 Pin = PA05 // ADC/AIN[5]
A5 Pin = PA06 // ADC/AIN[6]
A6 Pin = PA07 // ADC/AIN[7]
)
const (
SWITCH Pin = PA28
LED Pin = PB08
ADC_BATTERY Pin = PB09 // ADC/AIN[3]
)
// P1AM Base Controller
const (
BASE_SLAVE_SELECT_PIN Pin = A3
BASE_SLAVE_ACK_PIN Pin = A4
BASE_ENABLE_PIN Pin = PB09
)
// UART0 aka USBCDC pins
const (
USBCDC_DM_PIN Pin = PA24
USBCDC_DP_PIN Pin = PA25
USBCDC_HOST_ENABLE_PIN Pin = PA18
)
// UART1 pins
const (
UART_RX_PIN Pin = PB23 // RX: SERCOM5/PAD[3]
UART_TX_PIN Pin = PB22 // TX: SERCOM5/PAD[2]
)
// I2C pins
const (
SDA_PIN Pin = PA08 // SDA: SERCOM0/PAD[0]
SCL_PIN Pin = PA09 // SCL: SERCOM0/PAD[1]
)
// SPI pins
const (
SPI0_SCK_PIN Pin = D9 // SCK: SERCOM1/PAD[1]
SPI0_SDO_PIN Pin = D8 // SDO: SERCOM1/PAD[0]
SPI0_SDI_PIN Pin = D10 // SDI: SERCOM1/PAD[3]
)
// SD card pins
const (
SDCARD_SDI_PIN Pin = PA15 // SDI: SERCOM2/PAD[3]
SDCARD_SDO_PIN Pin = PA12 // SDO: SERCOM2/PAD[0]
SDCARD_SCK_PIN Pin = PA13 // SCK: SERCOM2/PAD[1]
SDCARD_SS_PIN Pin = PA14 // SS: as GPIO
SDCARD_CD_PIN Pin = PA27
)
// I2S pins
const (
I2S_SCK_PIN Pin = D2
I2S_SD_PIN Pin = A6
I2S_WS_PIN = D3
)
// USB CDC identifiers
const (
usb_STRING_PRODUCT = "P1AM-100"
usb_STRING_MANUFACTURER = "Facts Engineering"
)
var (
usb_VID uint16 = 0x1354
usb_PID uint16 = 0x4000
)
+47
View File
@@ -0,0 +1,47 @@
// +build sam,atsamd21,p1am_100
package machine
import (
"device/sam"
"runtime/interrupt"
)
// UART1 on the P1AM-100 connects to the normal TX/RX pins.
var (
UART1 = UART{
Buffer: NewRingBuffer(),
Bus: sam.SERCOM3_USART,
SERCOM: 5,
}
)
func init() {
UART1.Interrupt = interrupt.New(sam.IRQ_SERCOM5, UART1.handleInterrupt)
}
// I2C on the P1AM-100.
var (
I2C0 = I2C{
Bus: sam.SERCOM0_I2CM,
SERCOM: 0,
}
)
// SPI on the P1AM-100 is used for Base Controller.
var (
SPI0 = SPI{
Bus: sam.SERCOM1_SPI,
SERCOM: 1,
}
BASE_CONTROLLER_SPI = SPI0
)
// SPI1 is connected to the SD card slot on the P1AM-100
var (
SPI1 = SPI{
Bus: sam.SERCOM2_SPI,
SERCOM: 2,
}
SDCARD_SPI = SPI1
)
+1 -1
View File
@@ -41,7 +41,7 @@ const (
// UART
var (
Serial = USB
Serial = &USB
UART0 = NRF_UART0
)
+1 -1
View File
@@ -41,7 +41,7 @@ const (
// UART
var (
Serial = USB
Serial = &USB
UART0 = NRF_UART0
)
+1 -1
View File
@@ -41,7 +41,7 @@ const (
// UART
var (
Serial = USB
Serial = &USB
UART0 = NRF_UART0
)
+118
View File
@@ -0,0 +1,118 @@
// +build sam,atsamd21,qtpy
package machine
import (
"device/sam"
"runtime/interrupt"
)
// used to reset into bootloader
const RESET_MAGIC_VALUE = 0xf01669ef
// GPIO Pins
const (
D0 = PA02 // PWM available
D1 = PA03
D2 = PA04 // PWM available
D3 = PA05 // PWM available
D4 = PA16 // PWM available
D5 = PA17 // PWM available
D6 = PA06
D7 = PA07
D8 = PA11
D9 = PA09
D10 = PA10
D11 = PA18
D12 = PA15
D13 = PA27
D14 = PA23
D15 = PA19
D16 = PA22
D17 = PA08
)
// Analog pins
const (
A0 = D1
A1 = D1
A2 = D2
A3 = D3
A4 = D4
)
const (
LED = D13
)
// UART0 aka USBCDC pins
const (
USBCDC_DM_PIN = PA24
USBCDC_DP_PIN = PA25
)
// UART1 pins
const (
UART_TX_PIN = D6
UART_RX_PIN = D7
)
// UART1 on the QT Py M0.
var (
UART1 = UART{
Buffer: NewRingBuffer(),
Bus: sam.SERCOM0_USART,
SERCOM: 0,
}
)
func init() {
UART1.Interrupt = interrupt.New(sam.IRQ_SERCOM0, UART1.handleInterrupt)
}
// SPI pins
const (
SPI0_SCK_PIN = D8
SPI0_SDO_PIN = D10
SPI0_SDI_PIN = D9
)
// SPI on the QT Py M0.
var (
SPI0 = SPI{
Bus: sam.SERCOM0_SPI,
SERCOM: 0,
}
)
// I2C pins
const (
SDA_PIN = D4 // SDA
SCL_PIN = D5 // SCL
)
// I2C on the QT Py M0.
var (
I2C0 = I2C{
Bus: sam.SERCOM2_I2CM,
SERCOM: 2,
}
)
// I2S pins
const (
I2S_SCK_PIN = PA10
I2S_SD_PIN = PA08
I2S_WS_PIN = NoPin // TODO: figure out what this is on QT Py M0.
)
// USB CDC identifiers
const (
usb_STRING_PRODUCT = "Adafruit QTPy M0"
usb_STRING_MANUFACTURER = "Adafruit"
)
var (
usb_VID uint16 = 0x239A
usb_PID uint16 = 0x80CB
)
+2 -2
View File
@@ -30,7 +30,7 @@ var (
UART0 = UART{
Buffer: NewRingBuffer(),
Bus: stm32.USART2,
AltFuncSelector: stm32.AF7_USART1_2_3,
AltFuncSelector: AF7_USART1_2_3,
}
UART1 = &UART0
)
@@ -62,7 +62,7 @@ const (
var (
SPI0 = SPI{
Bus: stm32.SPI1,
AltFuncSelector: stm32.AF5_SPI1_SPI2,
AltFuncSelector: AF5_SPI1_SPI2,
}
SPI1 = &SPI0
)
+262
View File
@@ -0,0 +1,262 @@
// +build teensy40
package machine
import (
"device/nxp"
"runtime/interrupt"
)
// Digital pins
const (
// = Pin // [Pad]: Alt Func 0 Alt Func 1 Alt Func 2 Alt Func 3 Alt Func 4 Alt Func 5 Alt Func 6 Alt Func 7 Alt Func 8 Alt Func 9
// = ---- ----------- --------------- --------------- --------------- -------------- -------------------- ---------- -------------------- --------------------- --------------------- ----------------
D0 = PA3 // [AD_B0_03]: FLEXCAN2_RX XBAR1_INOUT17 LPUART6_RX USB_OTG1_OC FLEXPWM1_PWMX01 GPIO1_IO03 REF_CLK_24M LPSPI3_PCS0 ~ ~
D1 = PA2 // [AD_B0_02]: FLEXCAN2_TX XBAR1_INOUT16 LPUART6_TX USB_OTG1_PWR FLEXPWM1_PWMX00 GPIO1_IO02 LPI2C1_HREQ LPSPI3_SDI ~ ~
D2 = PD4 // [EMC_04]: SEMC_DATA04 FLEXPWM4_PWMA02 SAI2_TX_DATA XBAR1_INOUT06 FLEXIO1_FLEXIO04 GPIO4_IO04 ~ ~ ~ ~
D3 = PD5 // [EMC_05]: SEMC_DATA05 FLEXPWM4_PWMB02 SAI2_TX_SYNC XBAR1_INOUT07 FLEXIO1_FLEXIO05 GPIO4_IO05 ~ ~ ~ ~
D4 = PD6 // [EMC_06]: SEMC_DATA06 FLEXPWM2_PWMA00 SAI2_TX_BCLK XBAR1_INOUT08 FLEXIO1_FLEXIO06 GPIO4_IO06 ~ ~ ~ ~
D5 = PD8 // [EMC_08]: SEMC_DM00 FLEXPWM2_PWMA01 SAI2_RX_DATA XBAR1_INOUT17 FLEXIO1_FLEXIO08 GPIO4_IO08 ~ ~ ~ ~
D6 = PB10 // [B0_10]: LCD_DATA06 QTIMER4_TIMER1 FLEXPWM2_PWMA02 SAI1_TX_DATA03 FLEXIO2_FLEXIO10 GPIO2_IO10 SRC_BOOT_CFG06 ENET2_CRS ~ ~
D7 = PB17 // [B1_01]: LCD_DATA13 XBAR1_INOUT15 LPUART4_RX SAI1_TX_DATA00 FLEXIO2_FLEXIO17 GPIO2_IO17 FLEXPWM1_PWMB03 ENET2_RDATA00 FLEXIO3_FLEXIO17 ~
D8 = PB16 // [B1_00]: LCD_DATA12 XBAR1_INOUT14 LPUART4_TX SAI1_RX_DATA00 FLEXIO2_FLEXIO16 GPIO2_IO16 FLEXPWM1_PWMA03 ENET2_RX_ER FLEXIO3_FLEXIO16 ~
D9 = PB11 // [B0_11]: LCD_DATA07 QTIMER4_TIMER2 FLEXPWM2_PWMB02 SAI1_TX_DATA02 FLEXIO2_FLEXIO11 GPIO2_IO11 SRC_BOOT_CFG07 ENET2_COL ~ ~
D10 = PB0 // [B0_00]: LCD_CLK QTIMER1_TIMER0 MQS_RIGHT LPSPI4_PCS0 FLEXIO2_FLEXIO00 GPIO2_IO00 SEMC_CSX01 ENET2_MDC ~ ~
D11 = PB2 // [B0_02]: LCD_HSYNC QTIMER1_TIMER2 FLEXCAN1_TX LPSPI4_SDO FLEXIO2_FLEXIO02 GPIO2_IO02 SEMC_CSX03 ENET2_1588_EVENT0_OUT ~ ~
D12 = PB1 // [B0_01]: LCD_ENABLE QTIMER1_TIMER1 MQS_LEFT LPSPI4_SDI FLEXIO2_FLEXIO01 GPIO2_IO01 SEMC_CSX02 ENET2_MDIO ~ ~
D13 = PB3 // [B0_03]: LCD_VSYNC QTIMER2_TIMER0 FLEXCAN1_RX LPSPI4_SCK FLEXIO2_FLEXIO03 GPIO2_IO03 WDOG2_RESET_B_DEB ENET2_1588_EVENT0_IN ~ ~
D14 = PA18 // [AD_B1_02]: USB_OTG1_ID QTIMER3_TIMER2 LPUART2_TX SPDIF_OUT ENET_1588_EVENT2_OUT GPIO1_IO18 USDHC1_CD_B KPP_ROW06 GPT2_CLK FLEXIO3_FLEXIO02
D15 = PA19 // [AD_B1_03]: USB_OTG1_OC QTIMER3_TIMER3 LPUART2_RX SPDIF_IN ENET_1588_EVENT2_IN GPIO1_IO19 USDHC2_CD_B KPP_COL06 GPT2_CAPTURE1 FLEXIO3_FLEXIO03
D16 = PA23 // [AD_B1_07]: FLEXSPIB_DATA00 LPI2C3_SCL LPUART3_RX SPDIF_EXT_CLK CSI_HSYNC GPIO1_IO23 USDHC2_DATA3 KPP_COL04 GPT2_COMPARE3 FLEXIO3_FLEXIO07
D17 = PA22 // [AD_B1_06]: FLEXSPIB_DATA01 LPI2C3_SDA LPUART3_TX SPDIF_LOCK CSI_VSYNC GPIO1_IO22 USDHC2_DATA2 KPP_ROW04 GPT2_COMPARE2 FLEXIO3_FLEXIO06
D18 = PA17 // [AD_B1_01]: USB_OTG1_PWR QTIMER3_TIMER1 LPUART2_RTS_B LPI2C1_SDA CCM_PMIC_READY GPIO1_IO17 USDHC1_VSELECT KPP_COL07 ENET2_1588_EVENT0_IN FLEXIO3_FLEXIO01
D19 = PA16 // [AD_B1_00]: USB_OTG2_ID QTIMER3_TIMER0 LPUART2_CTS_B LPI2C1_SCL WDOG1_B GPIO1_IO16 USDHC1_WP KPP_ROW07 ENET2_1588_EVENT0_OUT FLEXIO3_FLEXIO00
D20 = PA26 // [AD_B1_10]: FLEXSPIA_DATA03 WDOG1_B LPUART8_TX SAI1_RX_SYNC CSI_DATA07 GPIO1_IO26 USDHC2_WP KPP_ROW02 ENET2_1588_EVENT1_OUT FLEXIO3_FLEXIO10
D21 = PA27 // [AD_B1_11]: FLEXSPIA_DATA02 EWM_OUT_B LPUART8_RX SAI1_RX_BCLK CSI_DATA06 GPIO1_IO27 USDHC2_RESET_B KPP_COL02 ENET2_1588_EVENT1_IN FLEXIO3_FLEXIO11
D22 = PA24 // [AD_B1_08]: FLEXSPIA_SS1_B FLEXPWM4_PWMA00 FLEXCAN1_TX CCM_PMIC_READY CSI_DATA09 GPIO1_IO24 USDHC2_CMD KPP_ROW03 FLEXIO3_FLEXIO08 ~
D23 = PA25 // [AD_B1_09]: FLEXSPIA_DQS FLEXPWM4_PWMA01 FLEXCAN1_RX SAI1_MCLK CSI_DATA08 GPIO1_IO25 USDHC2_CLK KPP_COL03 FLEXIO3_FLEXIO09 ~
D24 = PA12 // [AD_B0_12]: LPI2C4_SCL CCM_PMIC_READY LPUART1_TX WDOG2_WDOG_B FLEXPWM1_PWMX02 GPIO1_IO12 ENET_1588_EVENT1_OUT NMI_GLUE_NMI ~ ~
D25 = PA13 // [AD_B0_13]: LPI2C4_SDA GPT1_CLK LPUART1_RX EWM_OUT_B FLEXPWM1_PWMX03 GPIO1_IO13 ENET_1588_EVENT1_IN REF_CLK_24M ~ ~
D26 = PA30 // [AD_B1_14]: FLEXSPIA_SCLK ACMP_OUT02 LPSPI3_SDO SAI1_TX_BCLK CSI_DATA03 GPIO1_IO30 USDHC2_DATA6 KPP_ROW00 ENET2_1588_EVENT3_OUT FLEXIO3_FLEXIO14
D27 = PA31 // [AD_B1_15]: FLEXSPIA_SS0_B ACMP_OUT03 LPSPI3_SCK SAI1_TX_SYNC CSI_DATA02 GPIO1_IO31 USDHC2_DATA7 KPP_COL00 ENET2_1588_EVENT3_IN FLEXIO3_FLEXIO15
D28 = PC18 // [EMC_32]: SEMC_DATA10 FLEXPWM3_PWMB01 LPUART7_RX CCM_PMIC_RDY CSI_DATA21 GPIO3_IO18 ENET2_TX_EN ~ ~ ~
D29 = PD31 // [EMC_31]: SEMC_DATA09 FLEXPWM3_PWMA01 LPUART7_TX LPSPI1_PCS1 CSI_DATA22 GPIO4_IO31 ENET2_TDATA01 ~ ~ ~
D30 = PC23 // [EMC_37]: SEMC_DATA15 XBAR1_IN23 GPT1_COMPARE3 SAI3_MCLK CSI_DATA16 GPIO3_IO23 USDHC2_WP ENET2_RX_EN FLEXCAN3_RX ~
D31 = PC22 // [EMC_36]: SEMC_DATA14 XBAR1_IN22 GPT1_COMPARE2 SAI3_TX_DATA CSI_DATA17 GPIO3_IO22 USDHC1_WP ENET2_RDATA01 FLEXCAN3_TX ~
D32 = PB12 // [B0_12]: LCD_DATA08 XBAR1_INOUT10 ARM_TRACE_CLK SAI1_TX_DATA01 FLEXIO2_FLEXIO12 GPIO2_IO12 SRC_BOOT_CFG08 ENET2_TDATA00 ~ ~
D33 = PD7 // [EMC_07]: SEMC_DATA07 FLEXPWM2_PWMB00 SAI2_MCLK XBAR1_INOUT09 FLEXIO1_FLEXIO07 GPIO4_IO07 ~ ~ ~ ~
D34 = PC15 // [SD_B0_03]: USDHC1_DATA1 FLEXPWM1_PWMB01 LPUART8_RTS_B XBAR1_INOUT07 LPSPI1_SDI GPIO3_IO15 ENET2_RDATA00 SEMC_CLK6 ~ ~
D35 = PC14 // [SD_B0_02]: USDHC1_DATA0 FLEXPWM1_PWMA01 LPUART8_CTS_B XBAR1_INOUT06 LPSPI1_SDO GPIO3_IO14 ENET2_RX_ER SEMC_CLK5 ~ ~
D36 = PC13 // [SD_B0_01]: USDHC1_CLK FLEXPWM1_PWMB00 LPI2C3_SDA XBAR1_INOUT05 LPSPI1_PCS0 GPIO3_IO13 FLEXSPIB_SS1_B ENET2_TX_CLK ENET2_REF_CLK2 ~
D37 = PC12 // [SD_B0_00]: USDHC1_CMD FLEXPWM1_PWMA00 LPI2C3_SCL XBAR1_INOUT04 LPSPI1_SCK GPIO3_IO12 FLEXSPIA_SS1_B ENET2_TX_EN SEMC_DQS4 ~
D38 = PC17 // [SD_B0_05]: USDHC1_DATA3 FLEXPWM1_PWMB02 LPUART8_RX XBAR1_INOUT09 FLEXSPIB_DQS GPIO3_IO17 CCM_CLKO2 ENET2_RX_EN ~ ~
D39 = PC16 // [SD_B0_04]: USDHC1_DATA2 FLEXPWM1_PWMA02 LPUART8_TX XBAR1_INOUT08 FLEXSPIB_SS0_B GPIO3_IO16 CCM_CLKO1 ENET2_RDATA01 ~ ~
)
// Analog pins
const (
// = Pin // Dig | [Pad] {ADC1/ADC2}
A0 = PA18 // D14 | [AD_B1_02] { 7 / 7 }
A1 = PA19 // D15 | [AD_B1_03] { 8 / 8 }
A2 = PA23 // D16 | [AD_B1_07] { 12 / 12 }
A3 = PA22 // D17 | [AD_B1_06] { 11 / 11 }
A4 = PA17 // D18 | [AD_B1_01] { 6 / 6 }
A5 = PA16 // D19 | [AD_B1_00] { 5 / 5 }
A6 = PA26 // D20 | [AD_B1_10] { 15 / 15 }
A7 = PA27 // D21 | [AD_B1_11] { 0 / 0 }
A8 = PA24 // D22 | [AD_B1_08] { 13 / 13 }
A9 = PA25 // D23 | [AD_B1_09] { 14 / 14 }
A10 = PA12 // D24 | [AD_B0_12] { 1 / - }
A11 = PA13 // D25 | [AD_B0_13] { 2 / - }
A12 = PA30 // D26 | [AD_B1_14] { - / 3 }
A13 = PA31 // D27 | [AD_B1_15] { - / 4 }
)
// Default peripheral pins
const (
LED = D13
UART_RX_PIN = UART1_RX_PIN // D0
UART_TX_PIN = UART1_TX_PIN // D1
SPI_SDI_PIN = SPI1_SDI_PIN // D12
SPI_SDO_PIN = SPI1_SDO_PIN // D11
SPI_SCK_PIN = SPI1_SCK_PIN // D13
SPI_CS_PIN = SPI1_CS_PIN // D10
I2C_SDA_PIN = I2C1_SDA_PIN // D18/A4
I2C_SCL_PIN = I2C1_SCL_PIN // D19/A5
)
func init() {
// register any interrupt handlers for this board's peripherals
UART1.Interrupt = interrupt.New(nxp.IRQ_LPUART6, UART1.handleInterrupt)
UART2.Interrupt = interrupt.New(nxp.IRQ_LPUART4, UART2.handleInterrupt)
UART3.Interrupt = interrupt.New(nxp.IRQ_LPUART2, UART3.handleInterrupt)
UART4.Interrupt = interrupt.New(nxp.IRQ_LPUART3, UART4.handleInterrupt)
UART5.Interrupt = interrupt.New(nxp.IRQ_LPUART8, UART5.handleInterrupt)
UART6.Interrupt = interrupt.New(nxp.IRQ_LPUART1, UART6.handleInterrupt)
UART7.Interrupt = interrupt.New(nxp.IRQ_LPUART7, UART7.handleInterrupt)
}
// #=====================================================#
// | UART |
// #===========#===========#=============#===============#
// | Interface | Hardware | Clock(Freq) | RX/TX : Alt |
// #===========#===========#=============#=========-=====#
// | UART1 | LPUART6 | OSC(24 MHz) | D0/D1 : 2/2 |
// | UART2 | LPUART4 | OSC(24 MHz) | D7/D8 : 2/2 |
// | UART3 | LPUART2 | OSC(24 MHz) | D15/D14 : 2/2 |
// | UART4 | LPUART3 | OSC(24 MHz) | D16/D17 : 2/2 |
// | UART5 | LPUART8 | OSC(24 MHz) | D21/D20 : 2/2 |
// | UART6 | LPUART1 | OSC(24 MHz) | D25/D24 : 2/2 |
// | UART7 | LPUART7 | OSC(24 MHz) | D28/D29 : 2/2 |
// #===========#===========#=============#=========-=====#
const (
UART1_RX_PIN = D0
UART1_TX_PIN = D1
UART2_RX_PIN = D7
UART2_TX_PIN = D8
UART3_RX_PIN = D15
UART3_TX_PIN = D14
UART4_RX_PIN = D16
UART4_TX_PIN = D17
UART5_RX_PIN = D21
UART5_TX_PIN = D20
UART6_RX_PIN = D25
UART6_TX_PIN = D24
UART7_RX_PIN = D28
UART7_TX_PIN = D29
)
var (
UART1 = UART{
Buffer: NewRingBuffer(),
Bus: nxp.LPUART6,
muxRX: muxSelect{ // D0 (PA3 [AD_B0_03])
mux: nxp.IOMUXC_LPUART6_RX_SELECT_INPUT_DAISY_GPIO_AD_B0_03_ALT2,
sel: &nxp.IOMUXC.LPUART6_RX_SELECT_INPUT,
},
muxTX: muxSelect{ // D1 (PA2 [AD_B0_02])
mux: nxp.IOMUXC_LPUART6_TX_SELECT_INPUT_DAISY_GPIO_AD_B0_02_ALT2,
sel: &nxp.IOMUXC.LPUART6_TX_SELECT_INPUT,
},
}
UART2 = UART{
Buffer: NewRingBuffer(),
Bus: nxp.LPUART4,
muxRX: muxSelect{ // D7 (PB17 [B1_01])
mux: nxp.IOMUXC_LPUART4_RX_SELECT_INPUT_DAISY_GPIO_B1_01_ALT2,
sel: &nxp.IOMUXC.LPUART4_RX_SELECT_INPUT,
},
muxTX: muxSelect{ // D8 (PB16 [B1_00])
mux: nxp.IOMUXC_LPUART4_TX_SELECT_INPUT_DAISY_GPIO_B1_00_ALT2,
sel: &nxp.IOMUXC.LPUART4_TX_SELECT_INPUT,
},
}
UART3 = UART{
Buffer: NewRingBuffer(),
Bus: nxp.LPUART2,
muxRX: muxSelect{ // D15 (PA19 [AD_B1_03])
mux: nxp.IOMUXC_LPUART2_RX_SELECT_INPUT_DAISY_GPIO_AD_B1_03_ALT2,
sel: &nxp.IOMUXC.LPUART2_RX_SELECT_INPUT,
},
muxTX: muxSelect{ // D14 (PA18 [AD_B1_02])
mux: nxp.IOMUXC_LPUART2_TX_SELECT_INPUT_DAISY_GPIO_AD_B1_02_ALT2,
sel: &nxp.IOMUXC.LPUART2_TX_SELECT_INPUT,
},
}
UART4 = UART{
Buffer: NewRingBuffer(),
Bus: nxp.LPUART3,
muxRX: muxSelect{ // D16 (PA23 [AD_B1_07])
mux: nxp.IOMUXC_LPUART3_RX_SELECT_INPUT_DAISY_GPIO_AD_B1_07_ALT2,
sel: &nxp.IOMUXC.LPUART3_RX_SELECT_INPUT,
},
muxTX: muxSelect{ // D17 (PA22 [AD_B1_06])
mux: nxp.IOMUXC_LPUART3_TX_SELECT_INPUT_DAISY_GPIO_AD_B1_06_ALT2,
sel: &nxp.IOMUXC.LPUART3_TX_SELECT_INPUT,
},
}
UART5 = UART{
Buffer: NewRingBuffer(),
Bus: nxp.LPUART8,
muxRX: muxSelect{ // D21 (PA27 [AD_B1_11])
mux: nxp.IOMUXC_LPUART8_RX_SELECT_INPUT_DAISY_GPIO_AD_B1_11_ALT2,
sel: &nxp.IOMUXC.LPUART8_RX_SELECT_INPUT,
},
muxTX: muxSelect{ // D20 (PA26 [AD_B1_10])
mux: nxp.IOMUXC_LPUART8_TX_SELECT_INPUT_DAISY_GPIO_AD_B1_10_ALT2,
sel: &nxp.IOMUXC.LPUART8_TX_SELECT_INPUT,
},
}
UART6 = UART{
Buffer: NewRingBuffer(),
Bus: nxp.LPUART1,
// LPUART1 not connected via IOMUXC
// RX: D24 (PA12 [AD_B0_12])
// TX: D25 (PA13 [AD_B0_13])
}
UART7 = UART{
Buffer: NewRingBuffer(),
Bus: nxp.LPUART7,
muxRX: muxSelect{ // D28 (PC18 [EMC_32])
mux: nxp.IOMUXC_LPUART7_RX_SELECT_INPUT_DAISY_GPIO_EMC_32_ALT2,
sel: &nxp.IOMUXC.LPUART7_RX_SELECT_INPUT,
},
muxTX: muxSelect{ // D29 (PD31 [EMC_31])
mux: nxp.IOMUXC_LPUART7_TX_SELECT_INPUT_DAISY_GPIO_EMC_31_ALT2,
sel: &nxp.IOMUXC.LPUART7_TX_SELECT_INPUT,
},
}
)
// #===========#==========#===============#===========================#
// | Interface | Hardware | Clock(Freq) | SDI/SDO/SCK/CS : Alt |
// #===========#==========#===============#=================-=========#
// | SPI1 | LPSPI4 | PLL2(132 MHz) | D12/D11/D13/D10 : 3/3/3/3 |
// | SPI2 | LPSPI3 | PLL2(132 MHz) | D1/D26/D27/D0 : 7/2/2/7 |
// | SPI3 | LPSPI1 | PLL2(132 MHz) | D34/D35/D37/D36 : 4/4/4/4 |
// #===========#==========#===============#=================-=========#
const (
SPI1_SDI_PIN = D12
SPI1_SDO_PIN = D11
SPI1_SCK_PIN = D13
SPI1_CS_PIN = D10
SPI2_SDI_PIN = D1
SPI2_SDO_PIN = D26
SPI2_SCK_PIN = D27
SPI2_CS_PIN = D0
SPI3_SDI_PIN = D34
SPI3_SDO_PIN = D35
SPI3_SCK_PIN = D37
SPI3_CS_PIN = D36
)
// #====================================================#
// | I2C |
// #===========#==========#=============#===============#
// | Interface | Hardware | Clock(Freq) | SDA/SCL : Alt |
// #===========#==========#=============#=========-=====#
// | I2C1 | LPI2C1 | OSC(24 MHz) | D18/D19 : 3/3 |
// | I2C2 | LPI2C3 | OSC(24 MHz) | D17/D16 : 1/1 |
// | I2C3 | LPI2C4 | OSC(24 MHz) | D25/D24 : 0/0 |
// #===========#==========#=============#=========-=====#
const (
I2C1_SDA_PIN = D18
I2C1_SCL_PIN = D19
I2C2_SDA_PIN = D17
I2C2_SCL_PIN = D16
I2C3_SDA_PIN = D25
I2C3_SCL_PIN = D24
)

Some files were not shown because too many files have changed in this diff Show More